[
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n### Ⅰ. Issue Description\n\n\n### Ⅱ. Describe what happened\n\n\n### Ⅲ. Describe what you expected to happen\n\n\n### Ⅳ. How to reproduce it (as minimally and precisely as possible)\n\n1.\n2.\n3.\n\n\n\n### Ⅴ. Anything else we need to know?\n\n1. If applicable, add nginx  [debug log doc](http://nginx.org/en/docs/debugging_log.html).\n2.\n3.\n\n### Ⅵ. Environment:\n\n- Tengine version (use `sbin/nginx -V`):\n- OS (e.g. from /etc/os-release):\n- Kernel (e.g. `uname -a`):\n- Others:\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n## Why you need it?\n<!-- Is your feature request related to a problem? Please describe in details  -->\n\n\n## How it could be?\n<!--A clear and concise description of what you want to happen. For a computer fan,  you can explain more about input of the feature, and output of it.-->\n\n\n## Other related information\n<!-- Add any other context or screenshots about the feature request here.-->\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/question-about-tengine.md",
    "content": "---\nname: Question about Tengine\nabout: Ask whatever you want to know or confusion about Tengine\ntitle: ''\nlabels: ''\nassignees: chobits\n\n---\n\n## Question\n<!-- You can ask any question about this project -->\n"
  },
  {
    "path": ".github/workflows/ci-arm64.yml",
    "content": "name: build tengine\n\non:\n  push:\n    branches: [ master ]\n  pull_request:\n\njobs:\n  build-arm64:\n    runs-on: \"ubuntu-24.04\"\n    strategy:\n      fail-fast: false\n      matrix:\n        compiler:\n         - { compiler: GNU,  CC: gcc,  CXX: g++}\n         - { compiler: LLVM, CC: clang, CXX: clang++}\n    steps:\n      - name: Checkout Tengine\n        uses: actions/checkout@v3\n\n      - name: 'checkout luajit2'\n        uses: actions/checkout@v3\n        with:\n          repository: openresty/luajit2\n          path: luajit2\n\n      - name: Compile with ${{ matrix.compiler.compiler }}\n        uses: uraimo/run-on-arch-action@v3\n        with:\n          arch: aarch64\n          distro: ubuntu24.04\n          githubToken: ${{ github.token }}\n          dockerRunArgs: |\n            --volume \"${PWD}:/tengine\"\n          install: |\n            set -x\n            apt-get update -q -y\n            apt-get install -q -y make gcc g++ clang libgd-dev libgeoip-dev libxslt1-dev libpcre3 libpcre3-dev liblua5.1-0-dev lua5.1 libperl-dev cpanminus libssl-dev file\n          run: |\n            set -x\n            cd /tengine\n            echo \"Build luajit2\"\n            cd luajit2\n            make -j4\n            make install\n            cd ..\n            echo \"Build tengine\"\n            export CC=${{ matrix.compiler.CC }}\n            export CXX=${{ matrix.compiler.CXX }}\n            export LUAJIT_LIB=/usr/local/lib\n            export LUAJIT_INC=/usr/local/include/luajit-2.1\n            ./configure \\\n              --with-ld-opt=\"-Wl,-lpcre,-rpath,/usr/local/lib\" \\\n              --with-ipv6 \\\n              --with-http_ssl_module \\\n              --with-http_v2_module \\\n              --with-http_addition_module \\\n              --with-stream \\\n              --with-stream_ssl_module \\\n              --with-stream_realip_module \\\n              --with-stream_geoip_module \\\n              --with-stream_ssl_preread_module \\\n              --with-stream_sni \\\n              --add-module=./modules/ngx_backtrace_module \\\n              --add-module=./modules/ngx_debug_pool \\\n              --add-module=./modules/ngx_debug_timer \\\n              --add-module=./modules/ngx_debug_conn \\\n              --add-module=./modules/ngx_http_concat_module \\\n              --add-module=./modules/ngx_http_footer_filter_module \\\n              --add-module=./modules/ngx_http_lua_module \\\n              --add-module=./modules/ngx_http_proxy_connect_module \\\n              --add-module=./modules/ngx_http_reqstat_module \\\n              --add-module=./modules/ngx_http_slice_module \\\n              --add-module=./modules/ngx_http_sysguard_module \\\n              --add-module=./modules/ngx_http_trim_filter_module \\\n              --add-module=./modules/ngx_http_upstream_check_module \\\n              --add-module=./modules/ngx_http_upstream_consistent_hash_module \\\n              --add-module=./modules/ngx_http_upstream_dynamic_module \\\n              --add-module=./modules/ngx_http_upstream_dyups_module \\\n              --add-module=./modules/ngx_http_upstream_iwrr_module \\\n              --add-module=./modules/ngx_http_upstream_keepalive_module \\\n              --add-module=./modules/ngx_http_upstream_session_sticky_module \\\n              --add-module=./modules/ngx_http_upstream_vnswrr_module \\\n              --add-module=./modules/ngx_http_user_agent_module \\\n              --add-module=./modules/ngx_multi_upstream_module \\\n              --add-module=./modules/ngx_slab_stat \\\n              --without-http_upstream_keepalive_module\n            make -j4\n            make install\n            file /usr/local/nginx/sbin/nginx | grep aarch64\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: test tengine\n\non:\n  push:\n    branches: [ master ]\n  pull_request:\n    branches: [ master ]\n\njobs:\n  build-and-test:\n   runs-on: \"ubuntu-24.04\"\n   strategy:\n     fail-fast: false\n     matrix:\n       compiler:\n         - { compiler: GNU,  CC: gcc,  CXX: g++}\n         - { compiler: LLVM, CC: clang, CXX: clang++}\n   steps:\n     - uses: actions/checkout@v3\n     - name: get dependencies\n       run: |\n         sudo apt update\n         sudo apt remove nginx libgd3\n         sudo apt install -y libgd-dev libgeoip-dev libxslt1-dev libpcre3 libpcre3-dev liblua5.1-0-dev lua5.1 libperl-dev cpanminus libssl-dev\n     - name: 'checkout luajit2'\n       uses: actions/checkout@v3\n       with:\n         repository: openresty/luajit2\n         path: luajit2\n     - name: 'build luajit2'\n       working-directory: luajit2\n       run: |\n         make\n         sudo make install\n     - name: 'checkout lua-resty-lrucache'\n       uses: actions/checkout@v3\n       with:\n         repository: openresty/lua-resty-lrucache\n         path: lua-resty-lrucache\n     - name: 'build lua-resty-lrucache'\n       working-directory: lua-resty-lrucache\n       run: |\n         sudo make install\n     - name: 'checkout lua-resty-core'\n       uses: actions/checkout@v3\n       with:\n         repository: openresty/lua-resty-core\n         ref: v0.1.27\n         path: lua-resty-core\n     - name: 'build lua-resty-core'\n       working-directory: lua-resty-core\n       run: |\n         sudo make install\n\n# TODO: fix tests so they don't depend on /usr/local/nginx/logs/\n#       so we can run `make`, `make test`, `make install`.\n\n     - name: build\n       env:\n         CC: ${{ matrix.compiler.CC }}\n         CXX: ${{ matrix.compiler.CXX }}\n         LUAJIT_LIB: /usr/local/lib\n         LUAJIT_INC: /usr/local/include/luajit-2.1\n       run: |\n         ./configure \\\n            --with-debug \\\n            --with-ld-opt=\"-Wl,-lpcre,-rpath,/usr/local/lib\" \\\n            --with-ipv6 \\\n            --with-openssl-async \\\n            --with-http_ssl_module \\\n            --with-http_v2_module \\\n            --with-http_addition_module \\\n            --with-stream \\\n            --with-stream_ssl_module \\\n            --with-stream_realip_module \\\n            --with-stream_geoip_module \\\n            --with-stream_ssl_preread_module \\\n            --with-stream_sni \\\n            --add-module=./modules/ngx_backtrace_module \\\n            --add-module=./modules/ngx_debug_pool \\\n            --add-module=./modules/ngx_debug_timer \\\n            --add-module=./modules/ngx_debug_conn \\\n            --add-module=./modules/ngx_http_concat_module \\\n            --add-module=./modules/ngx_http_footer_filter_module \\\n            --add-module=./modules/ngx_http_lua_module \\\n            --add-module=./modules/ngx_http_proxy_connect_module \\\n            --add-module=./modules/ngx_http_reqstat_module \\\n            --add-module=./modules/ngx_http_slice_module \\\n            --add-module=./modules/ngx_http_sysguard_module \\\n            --add-module=./modules/ngx_http_trim_filter_module \\\n            --add-module=./modules/ngx_http_upstream_check_module \\\n            --add-module=./modules/ngx_http_upstream_consistent_hash_module \\\n            --add-module=./modules/ngx_http_upstream_dynamic_module \\\n            --add-module=./modules/ngx_http_upstream_dyups_module \\\n            --add-module=./modules/ngx_http_upstream_iwrr_module \\\n            --add-module=./modules/ngx_http_upstream_keepalive_module \\\n            --add-module=./modules/ngx_http_upstream_session_sticky_module \\\n            --add-module=./modules/ngx_http_upstream_vnswrr_module \\\n            --add-module=./modules/ngx_http_user_agent_module \\\n            --add-module=./modules/ngx_multi_upstream_module \\\n            --add-module=./modules/ngx_slab_stat \\\n            --without-http_upstream_keepalive_module\n         make -j2\n         sudo make install\n     - name: tengine test cases using nginx-tests lib\n       working-directory: tests/nginx-tests\n       env:\n         TEST_NGINX_BINARY: /usr/local/nginx/sbin/nginx\n       run: |\n         sudo cpanm --notest Net::DNS::Nameserver > build.log 2>&1 || (cat build.log && exit 1)\n         prove -v -Inginx-tests/lib tengine-tests/\n         prove -v -Inginx-tests/lib ../../modules/ngx_http_proxy_connect_module/t\n         prove -v -Inginx-tests/lib ../../modules/ngx_debug_timer/t\n         prove -v -Inginx-tests/lib ../../modules/ngx_debug_conn/t\n         prove -v -Inginx-tests/lib ../../modules/ngx_slab_stat/t\n     - name: tengine test cases using test-nginx lib\n       working-directory: tests/test-nginx\n       run: |\n         sudo cpanm --notest Shell Test::Base Test::LongString List::MoreUtils LWP::UserAgent HTTP::Response  > build.log 2>&1 || (cat build.log && exit 1)\n         mkdir t\n         PATH=/usr/local/nginx/sbin:$PATH \\\n         prove -v -Itest-nginx/lib cases/\n"
  },
  {
    "path": ".github/workflows/test-nginx-core.yml",
    "content": "name: test nginx core\n\non:\n  push:\n    branches: [ master ]\n  pull_request:\n    branches: [ master ]\n\njobs:\n  build-and-test:\n   runs-on: \"ubuntu-24.04\"\n   strategy:\n     fail-fast: false\n     matrix:\n       compiler:\n         - { compiler: GNU,  CC: gcc,  CXX: g++}\n         - { compiler: LLVM, CC: clang, CXX: clang++}\n   steps:\n     - uses: actions/checkout@v3\n     - name: get dependencies\n       run: |\n         sudo apt update\n         sudo apt remove nginx libgd3\n         sudo apt install -y libgd-dev libgeoip-dev libxslt1-dev libpcre3 libpcre3-dev liblua5.1-0-dev lua5.1 libperl-dev cpanminus libssl-dev\n         # for building nginx core\n         sudo apt install -y libgoogle-perftools-dev\n         # for running cases in nginx-tests\n         sudo apt install -y uwsgi-plugin-python3 uwsgi ffmpeg memcached libsofthsm2-dev\n     - name: 'checkout luajit2'\n       uses: actions/checkout@v3\n       with:\n         repository: openresty/luajit2\n         path: luajit2\n     - name: 'build luajit2'\n       working-directory: luajit2\n       run: |\n         make\n         sudo make install\n     - name: 'checkout lua-resty-lrucache'\n       uses: actions/checkout@v3\n       with:\n         repository: openresty/lua-resty-lrucache\n         path: lua-resty-lrucache\n     - name: 'build lua-resty-lrucache'\n       working-directory: lua-resty-lrucache\n       run: |\n         sudo make install\n     - name: 'checkout lua-resty-core'\n       uses: actions/checkout@v3\n       with:\n         repository: openresty/lua-resty-core\n         ref: v0.1.27\n         path: lua-resty-core\n     - name: 'build lua-resty-core'\n       working-directory: lua-resty-core\n       run: |\n         sudo make install\n     - name: build\n       env:\n         CC: ${{ matrix.compiler.CC }}\n         CXX: ${{ matrix.compiler.CXX }}\n         LUAJIT_LIB: /usr/local/lib\n         LUAJIT_INC: /usr/local/include/luajit-2.1\n       run: |\n         # TODO: fix https://github.com/alibaba/tengine/issues/1720, then remove \"-D T_NGX_HTTP_IMAGE_FILTER=0\"\n\n         # NOTE:\n         # For \"-D T_NGX_MODIFY_DEFAULT_VALUE=0\", we dont compile the source included in this macro, otherwise some nginx-tests cases tests will fail.\n         # For \"-D T_NGX_SERVER_INFO=0\", it makes some cases pass, such as userid.t.\n         # For \"-D T_NGX_HTTP_UPSTREAM_RANDOM=0\", it makes some cases pass, such as image_filter_finalize.t.\n         ./configure  \\\n            --with-cc-opt=\"-D T_NGX_MODIFY_DEFAULT_VALUE=0 -D T_NGX_HTTP_IMAGE_FILTER=0 -D T_NGX_SERVER_INFO=0 -D T_NGX_HTTP_UPSTREAM_RANDOM=0\" \\\n            --with-ld-opt=\"-Wl,-lpcre,-rpath,/usr/local/lib\" \\\n            --with-openssl-async \\\n            --with-pcre \\\n            --with-http_ssl_module \\\n            --with-http_image_filter_module \\\n            --with-http_v2_module \\\n            --with-http_addition_module \\\n            --with-http_mp4_module \\\n            --with-http_realip_module \\\n            --with-http_xslt_module \\\n            --with-http_geoip_module \\\n            --with-http_sub_module \\\n            --with-http_dav_module \\\n            --with-http_flv_module \\\n            --with-http_gunzip_module \\\n            --with-http_gzip_static_module \\\n            --with-http_auth_request_module \\\n            --with-http_random_index_module \\\n            --with-http_secure_link_module \\\n            --with-http_degradation_module \\\n            --with-http_slice_module \\\n            --with-http_stub_status_module \\\n            --with-mail \\\n            --with-mail_ssl_module \\\n            --with-stream \\\n            --with-stream_ssl_module \\\n            --with-stream_realip_module \\\n            --with-stream_geoip_module \\\n            --with-stream_ssl_preread_module \\\n            --with-google_perftools_module \\\n            --with-cpp_test_module \\\n            --with-compat \\\n            --add-module=modules/mod_config \\\n            --add-module=modules/mod_dubbo \\\n            --add-module=modules/ngx_backtrace_module \\\n            --add-module=modules/ngx_debug_timer \\\n            --add-module=modules/ngx_http_concat_module \\\n            --add-module=modules/ngx_http_footer_filter_module \\\n            --add-module=modules/ngx_http_lua_module \\\n            --add-module=modules/ngx_http_proxy_connect_module \\\n            --add-module=modules/ngx_http_reqstat_module \\\n            --add-module=modules/ngx_http_sysguard_module \\\n            --add-module=modules/ngx_http_trim_filter_module \\\n            --add-module=modules/ngx_http_upstream_check_module \\\n            --add-module=modules/ngx_http_upstream_consistent_hash_module \\\n            --add-module=modules/ngx_http_upstream_dynamic_module \\\n            --add-module=modules/ngx_http_upstream_session_sticky_module \\\n            --add-module=modules/ngx_http_upstream_vnswrr_module \\\n            --add-module=modules/ngx_http_user_agent_module \\\n            --add-module=modules/ngx_multi_upstream_module \\\n            --add-module=modules/ngx_slab_stat \\\n            --add-module=modules/ngx_http_upstream_dyups_module \\\n            --with-http_perl_module \\\n            --with-stream_sni \\\n            --with-openssl-async \\\n            --with-debug\n            # skip ngx_debug_pool, it modified NGX_MIN_POOL_SIZE, which made some test case failed (http_header_buffers.t)\n            # skip tengine upstream keepalive module\n            #--without-http_upstream_keepalive_module \\\n            #--add-module=modules/ngx_http_upstream_keepalive_module \\\n            # skip tengine slice module\n            #--add-module=modules/ngx_http_slice_module \\\n         make -j2\n         sudo make install\n     - name: run cases in nginx-tests\n       working-directory: tests/nginx-tests\n       env:\n         TEST_NGINX_BINARY: /usr/local/nginx/sbin/nginx\n         TEST_NGINX_UNSAFE: yes\n       run: |\n         # prepare perl library for test case\n         sudo cpanm --notest SCGI Protocol::WebSocket Net::SSLeay IO::Socket::SSL Cache::Memcached Cache::Memcached::Fast Net::DNS::Nameserver GD > build.log 2>&1 || (cat build.log && exit 1)\n         # fixed http_method.t for tengine proxy_connect module\n         sed -i -e \"s+405 Not Allowed(?!.*200 OK)/s, 'connect'+400 Bad Request(?!.*200 OK)/s, 'connect'+\" nginx-tests/http_method.t\n         # run cases in nginx-tests\n         prove -I nginx-tests/lib nginx-tests/\n         # It must be root for some cases.\n         sudo groupadd wheel    # for proxy_bind_transparent.t\n         sudo TEST_NGINX_BINARY=/usr/local/nginx/sbin/nginx TEST_NGINX_UNSAFE=yes prove -I nginx-tests/lib nginx-tests/proxy_bind_transparent.t nginx-tests/proxy_bind_transparent_capability.t\n\n"
  },
  {
    "path": ".github/workflows/test-ntls.yml",
    "content": "name: test tengine ntls\n\non:\n  push:\n    branches: [ master ]\n  pull_request:\n    branches: [ master ]\n\njobs:\n  build-and-test:\n    runs-on: \"ubuntu-24.04\"\n    strategy:\n      fail-fast: false\n      matrix:\n        compiler:\n          - { compiler: GNU,  CC: gcc,  CXX: g++}\n          - { compiler: LLVM, CC: clang, CXX: clang++}\n    steps:\n      - uses: actions/checkout@v3\n        with:\n          path: tengine\n      - name: get dependencies\n        run: |\n          sudo apt install -y libpcre3 libpcre3-dev\n      - name: 'checkout luajit2'\n        uses: actions/checkout@v3\n        with:\n          repository: openresty/luajit2\n          path: luajit2\n      - name: 'build luajit2'\n        working-directory: luajit2\n        run: |\n          make\n          sudo make install\n      - name: 'checkout lua-resty-lrucache'\n        uses: actions/checkout@v3\n        with:\n          repository: openresty/lua-resty-lrucache\n          path: lua-resty-lrucache\n      - name: 'build lua-resty-lrucache'\n        working-directory: lua-resty-lrucache\n        run: |\n          sudo make install\n      - name: 'checkout lua-resty-core'\n        uses: actions/checkout@v3\n        with:\n          repository: openresty/lua-resty-core\n          ref: v0.1.27\n          path: lua-resty-core\n      - name: 'build lua-resty-core'\n        working-directory: lua-resty-core\n        run: |\n          sudo make install\n      - name: checkout Tongsuo\n        uses: actions/checkout@v3\n        with:\n          repository: Tongsuo-Project/Tongsuo\n          path: Tongsuo\n      - name: build Tongsuo\n        working-directory: Tongsuo\n        env:\n          CC: ${{ matrix.compiler.CC }}\n        run: |\n          ./config --prefix=${RUNNER_TEMP}/tongsuo enable-ntls no-shared\n          make -s -j4\n          make install_sw\n          make clean\n      - name: build Tengine\n        working-directory: tengine\n        env:\n          CC: ${{ matrix.compiler.CC }}\n          CXX: ${{ matrix.compiler.CXX }}\n          LUAJIT_LIB: /usr/local/lib\n          LUAJIT_INC: /usr/local/include/luajit-2.1\n        run: |\n          ./configure \\\n            --with-ld-opt=\"-Wl,-lpcre,-rpath,/usr/local/lib\" \\\n            --with-pcre \\\n            --add-module=modules/ngx_tongsuo_ntls \\\n            --add-module=modules/ngx_http_lua_module \\\n            --with-openssl=../Tongsuo \\\n            --with-openssl-opt=\"--api=1.1.1 enable-ntls\" \\\n            --with-http_ssl_module \\\n            --with-http_v2_module \\\n            --with-stream \\\n            --with-stream_ssl_module \\\n            --with-stream_sni\n          make -j2\n          sudo make install\n      - name: run test cases\n        working-directory: tengine\n        env:\n          TEST_OPENSSL_BINARY: ${{ runner.temp }}/tongsuo/bin/tongsuo\n          TEST_NGINX_BINARY: /usr/local/nginx/sbin/nginx\n          TEST_NGINX_LEAVE: 1\n        run: |\n          prove -Itests/nginx-tests/nginx-tests/lib/ modules/ngx_tongsuo_ntls/t\n      - name: debug\n        if: ${{ failure() }}\n        run: |\n          for file in `ls /tmp/nginx-test-*/error.log`; do cat $file; done\n"
  },
  {
    "path": ".gitignore",
    "content": "Makefile\nobjs/\ntags\n"
  },
  {
    "path": "AUTHORS.te",
    "content": "Names are in alphabetical order:\n\nAntónio P. P. Almeida (appa [at] perusio [dot] net)\nCharles Chen (weiyue [at] taobao [dot] com)\nXiaojiang Chen (zhongsheng.cxj [at] taobao [dot] com)\nYunxing Chen (yunxing.cyx [at] taobao [dot] com)\nZhen Chen (gongyuan.cz [at] taobao [dot] com)\nShanyuan Gao (kangbo [at] taobao [dot] com)\nSteve Peng (jinglong.pq [at] taobao [dot] com)\nFeibo Li (lizi [at] taobao [dot] com)\nSimon Liu (diaoliang [at] taobao [dot] com)\nYang Tian (lieyuan [at] taobao [dot] com)\nXiaozhe \"chaoslawful\" Wang (chaoslawful [at] gmail [dot] com)\nXiaowei Wu (yixiao.wxw [at] taobao [dot] com)\nJunmin Xiong (xiongjunmin.pt [at] taobao [dot] com)\nZhuo Yuan (yuanzhuo.pt [at] taobao [dot] com)\nWeibin Yao (wenjing.ywb [at] taobao [dot] com)\nZhang \"agentzh\" Yichun (agentzh [at] gmail [dot] com)\nLanshun Zhou (zls.sogou [at] gmail [dot] com)\nJoshua Zhu (shudu [at] taobao [dot] com)\nLiang Li (lianglli [at] taobao [dot] com)\n"
  },
  {
    "path": "CHANGES",
    "content": "Changes with nginx 1.24.0                                        11 Apr 2023\n\n    *) 1.24.x stable branch.\n\n\nChanges with nginx 1.23.4                                        28 Mar 2023\n\n    *) Change: now TLSv1.3 protocol is enabled by default.\n\n    *) Change: now nginx issues a warning if protocol parameters of a\n       listening socket are redefined.\n\n    *) Change: now nginx closes connections with lingering if pipelining was\n       used by the client.\n\n    *) Feature: byte ranges support in the ngx_http_gzip_static_module.\n\n    *) Bugfix: port ranges in the \"listen\" directive did not work; the bug\n       had appeared in 1.23.3.\n       Thanks to Valentin Bartenev.\n\n    *) Bugfix: incorrect location might be chosen to process a request if a\n       prefix location longer than 255 characters was used in the\n       configuration.\n\n    *) Bugfix: non-ASCII characters in file names on Windows were not\n       supported by the ngx_http_autoindex_module, the ngx_http_dav_module,\n       and the \"include\" directive.\n\n    *) Change: the logging level of the \"data length too long\", \"length too\n       short\", \"bad legacy version\", \"no shared signature algorithms\", \"bad\n       digest length\", \"missing sigalgs extension\", \"encrypted length too\n       long\", \"bad length\", \"bad key update\", \"mixed handshake and non\n       handshake data\", \"ccs received early\", \"data between ccs and\n       finished\", \"packet length too long\", \"too many warn alerts\", \"record\n       too small\", and \"got a fin before a ccs\" SSL errors has been lowered\n       from \"crit\" to \"info\".\n\n    *) Bugfix: a socket leak might occur when using HTTP/2 and the\n       \"error_page\" directive to redirect errors with code 400.\n\n    *) Bugfix: messages about logging to syslog errors did not contain\n       information that the errors happened while logging to syslog.\n       Thanks to Safar Safarly.\n\n    *) Workaround: \"gzip filter failed to use preallocated memory\" alerts\n       appeared in logs when using zlib-ng.\n\n    *) Bugfix: in the mail proxy server.\n\n\nChanges with nginx 1.23.3                                        13 Dec 2022\n\n    *) Bugfix: an error might occur when reading PROXY protocol version 2\n       header with large number of TLVs.\n\n    *) Bugfix: a segmentation fault might occur in a worker process if SSI\n       was used to process subrequests created by other modules.\n       Thanks to Ciel Zhao.\n\n    *) Workaround: when a hostname used in the \"listen\" directive resolves\n       to multiple addresses, nginx now ignores duplicates within these\n       addresses.\n\n    *) Bugfix: nginx might hog CPU during unbuffered proxying if SSL\n       connections to backends were used.\n\n\nChanges with nginx 1.23.2                                        19 Oct 2022\n\n    *) Security: processing of a specially crafted mp4 file by the\n       ngx_http_mp4_module might cause a worker process crash, worker\n       process memory disclosure, or might have potential other impact\n       (CVE-2022-41741, CVE-2022-41742).\n\n    *) Feature: the \"$proxy_protocol_tlv_...\" variables.\n\n    *) Feature: TLS session tickets encryption keys are now automatically\n       rotated when using shared memory in the \"ssl_session_cache\"\n       directive.\n\n    *) Change: the logging level of the \"bad record type\" SSL errors has\n       been lowered from \"crit\" to \"info\".\n       Thanks to Murilo Andrade.\n\n    *) Change: now when using shared memory in the \"ssl_session_cache\"\n       directive the \"could not allocate new session\" errors are logged at\n       the \"warn\" level instead of \"alert\" and not more often than once per\n       second.\n\n    *) Bugfix: nginx/Windows could not be built with OpenSSL 3.0.x.\n\n    *) Bugfix: in logging of the PROXY protocol errors.\n       Thanks to Sergey Brester.\n\n    *) Workaround: shared memory from the \"ssl_session_cache\" directive was\n       spent on sessions using TLS session tickets when using TLSv1.3 with\n       OpenSSL.\n\n    *) Workaround: timeout specified with the \"ssl_session_timeout\"\n       directive did not work when using TLSv1.3 with OpenSSL or BoringSSL.\n\n\nChanges with nginx 1.23.1                                        19 Jul 2022\n\n    *) Feature: memory usage optimization in configurations with SSL\n       proxying.\n\n    *) Feature: looking up of IPv4 addresses while resolving now can be\n       disabled with the \"ipv4=off\" parameter of the \"resolver\" directive.\n\n    *) Change: the logging level of the \"bad key share\", \"bad extension\",\n       \"bad cipher\", and \"bad ecpoint\" SSL errors has been lowered from\n       \"crit\" to \"info\".\n\n    *) Bugfix: while returning byte ranges nginx did not remove the\n       \"Content-Range\" header line if it was present in the original backend\n       response.\n\n    *) Bugfix: a proxied response might be truncated during reconfiguration\n       on Linux; the bug had appeared in 1.17.5.\n\n\nChanges with nginx 1.23.0                                        21 Jun 2022\n\n    *) Change in internal API: now header lines are represented as linked\n       lists.\n\n    *) Change: now nginx combines arbitrary header lines with identical\n       names when sending to FastCGI, SCGI, and uwsgi backends, in the\n       $r->header_in() method of the ngx_http_perl_module, and during lookup\n       of the \"$http_...\", \"$sent_http_...\", \"$sent_trailer_...\",\n       \"$upstream_http_...\", and \"$upstream_trailer_...\" variables.\n\n    *) Bugfix: if there were multiple \"Vary\" header lines in the backend\n       response, nginx only used the last of them when caching.\n\n    *) Bugfix: if there were multiple \"WWW-Authenticate\" header lines in the\n       backend response and errors with code 401 were intercepted or the\n       \"auth_request\" directive was used, nginx only sent the first of the\n       header lines to the client.\n\n    *) Change: the logging level of the \"application data after close\n       notify\" SSL errors has been lowered from \"crit\" to \"info\".\n\n    *) Bugfix: connections might hang if nginx was built on Linux 2.6.17 or\n       newer, but was used on systems without EPOLLRDHUP support, notably\n       with epoll emulation layers; the bug had appeared in 1.17.5.\n       Thanks to Marcus Ball.\n\n    *) Bugfix: nginx did not cache the response if the \"Expires\" response\n       header line disabled caching, but following \"Cache-Control\" header\n       line enabled caching.\n\n\nChanges with nginx 1.21.6                                        25 Jan 2022\n\n    *) Bugfix: when using EPOLLEXCLUSIVE on Linux client connections were\n       unevenly distributed among worker processes.\n\n    *) Bugfix: nginx returned the \"Connection: keep-alive\" header line in\n       responses during graceful shutdown of old worker processes.\n\n    *) Bugfix: in the \"ssl_session_ticket_key\" when using TLSv1.3.\n\n\nChanges with nginx 1.21.5                                        28 Dec 2021\n\n    *) Change: now nginx is built with the PCRE2 library by default.\n\n    *) Change: now nginx always uses sendfile(SF_NODISKIO) on FreeBSD.\n\n    *) Feature: support for sendfile(SF_NOCACHE) on FreeBSD.\n\n    *) Feature: the $ssl_curve variable.\n\n    *) Bugfix: connections might hang when using HTTP/2 without SSL with the\n       \"sendfile\" and \"aio\" directives.\n\n\nChanges with nginx 1.21.4                                        02 Nov 2021\n\n    *) Change: support for NPN instead of ALPN to establish HTTP/2\n       connections has been removed.\n\n    *) Change: now nginx rejects SSL connections if ALPN is used by the\n       client, but no supported protocols can be negotiated.\n\n    *) Change: the default value of the \"sendfile_max_chunk\" directive was\n       changed to 2 megabytes.\n\n    *) Feature: the \"proxy_half_close\" directive in the stream module.\n\n    *) Feature: the \"ssl_alpn\" directive in the stream module.\n\n    *) Feature: the $ssl_alpn_protocol variable.\n\n    *) Feature: support for SSL_sendfile() when using OpenSSL 3.0.\n\n    *) Feature: the \"mp4_start_key_frame\" directive in the\n       ngx_http_mp4_module.\n       Thanks to Tracey Jaquith.\n\n    *) Bugfix: in the $content_length variable when using chunked transfer\n       encoding.\n\n    *) Bugfix: after receiving a response with incorrect length from a\n       proxied backend nginx might nevertheless cache the connection.\n       Thanks to Awdhesh Mathpal.\n\n    *) Bugfix: invalid headers from backends were logged at the \"info\" level\n       instead of \"error\"; the bug had appeared in 1.21.1.\n\n    *) Bugfix: requests might hang when using HTTP/2 and the \"aio_write\"\n       directive.\n\n\nChanges with nginx 1.21.3                                        07 Sep 2021\n\n    *) Change: optimization of client request body reading when using\n       HTTP/2.\n\n    *) Bugfix: in request body filters internal API when using HTTP/2 and\n       buffering of the data being processed.\n\n\nChanges with nginx 1.21.2                                        31 Aug 2021\n\n    *) Change: now nginx rejects HTTP/1.0 requests with the\n       \"Transfer-Encoding\" header line.\n\n    *) Change: export ciphers are no longer supported.\n\n    *) Feature: OpenSSL 3.0 compatibility.\n\n    *) Feature: the \"Auth-SSL-Protocol\" and \"Auth-SSL-Cipher\" header lines\n       are now passed to the mail proxy authentication server.\n       Thanks to Rob Mueller.\n\n    *) Feature: request body filters API now permits buffering of the data\n       being processed.\n\n    *) Bugfix: backend SSL connections in the stream module might hang after\n       an SSL handshake.\n\n    *) Bugfix: the security level, which is available in OpenSSL 1.1.0 or\n       newer, did not affect loading of the server certificates when set\n       with \"@SECLEVEL=N\" in the \"ssl_ciphers\" directive.\n\n    *) Bugfix: SSL connections with gRPC backends might hang if select,\n       poll, or /dev/poll methods were used.\n\n    *) Bugfix: when using HTTP/2 client request body was always written to\n       disk if the \"Content-Length\" header line was not present in the\n       request.\n\n\nChanges with nginx 1.21.1                                        06 Jul 2021\n\n    *) Change: now nginx always returns an error for the CONNECT method.\n\n    *) Change: now nginx always returns an error if both \"Content-Length\"\n       and \"Transfer-Encoding\" header lines are present in the request.\n\n    *) Change: now nginx always returns an error if spaces or control\n       characters are used in the request line.\n\n    *) Change: now nginx always returns an error if spaces or control\n       characters are used in a header name.\n\n    *) Change: now nginx always returns an error if spaces or control\n       characters are used in the \"Host\" request header line.\n\n    *) Change: optimization of configuration testing when using many\n       listening sockets.\n\n    *) Bugfix: nginx did not escape \"\"\", \"<\", \">\", \"\\\", \"^\", \"`\", \"{\", \"|\",\n       and \"}\" characters when proxying with changed URI.\n\n    *) Bugfix: SSL variables might be empty when used in logs; the bug had\n       appeared in 1.19.5.\n\n    *) Bugfix: keepalive connections with gRPC backends might not be closed\n       after receiving a GOAWAY frame.\n\n    *) Bugfix: reduced memory consumption for long-lived requests when\n       proxying with more than 64 buffers.\n\n\nChanges with nginx 1.21.0                                        25 May 2021\n\n    *) Security: 1-byte memory overwrite might occur during DNS server\n       response processing if the \"resolver\" directive was used, allowing an\n       attacker who is able to forge UDP packets from the DNS server to\n       cause worker process crash or, potentially, arbitrary code execution\n       (CVE-2021-23017).\n\n    *) Feature: variables support in the \"proxy_ssl_certificate\",\n       \"proxy_ssl_certificate_key\" \"grpc_ssl_certificate\",\n       \"grpc_ssl_certificate_key\", \"uwsgi_ssl_certificate\", and\n       \"uwsgi_ssl_certificate_key\" directives.\n\n    *) Feature: the \"max_errors\" directive in the mail proxy module.\n\n    *) Feature: the mail proxy module supports POP3 and IMAP pipelining.\n\n    *) Feature: the \"fastopen\" parameter of the \"listen\" directive in the\n       stream module.\n       Thanks to Anbang Wen.\n\n    *) Bugfix: special characters were not escaped during automatic redirect\n       with appended trailing slash.\n\n    *) Bugfix: connections with clients in the mail proxy module might be\n       closed unexpectedly when using SMTP pipelining.\n\n\nChanges with nginx 1.19.10                                       13 Apr 2021\n\n    *) Change: the default value of the \"keepalive_requests\" directive was\n       changed to 1000.\n\n    *) Feature: the \"keepalive_time\" directive.\n\n    *) Feature: the $connection_time variable.\n\n    *) Workaround: \"gzip filter failed to use preallocated memory\" alerts\n       appeared in logs when using zlib-ng.\n\n\nChanges with nginx 1.19.9                                        30 Mar 2021\n\n    *) Bugfix: nginx could not be built with the mail proxy module, but\n       without the ngx_mail_ssl_module; the bug had appeared in 1.19.8.\n\n    *) Bugfix: \"upstream sent response body larger than indicated content\n       length\" errors might occur when working with gRPC backends; the bug\n       had appeared in 1.19.1.\n\n    *) Bugfix: nginx might not close a connection till keepalive timeout\n       expiration if the connection was closed by the client while\n       discarding the request body.\n\n    *) Bugfix: nginx might not detect that a connection was already closed\n       by the client when waiting for auth_delay or limit_req delay, or when\n       working with backends.\n\n    *) Bugfix: in the eventport method.\n\n\nChanges with nginx 1.19.8                                        09 Mar 2021\n\n    *) Feature: flags in the \"proxy_cookie_flags\" directive can now contain\n       variables.\n\n    *) Feature: the \"proxy_protocol\" parameter of the \"listen\" directive,\n       the \"proxy_protocol\" and \"set_real_ip_from\" directives in mail proxy.\n\n    *) Bugfix: HTTP/2 connections were immediately closed when using\n       \"keepalive_timeout 0\"; the bug had appeared in 1.19.7.\n\n    *) Bugfix: some errors were logged as unknown if nginx was built with\n       glibc 2.32.\n\n    *) Bugfix: in the eventport method.\n\n\nChanges with nginx 1.19.7                                        16 Feb 2021\n\n    *) Change: connections handling in HTTP/2 has been changed to better\n       match HTTP/1.x; the \"http2_recv_timeout\", \"http2_idle_timeout\", and\n       \"http2_max_requests\" directives have been removed, the\n       \"keepalive_timeout\" and \"keepalive_requests\" directives should be\n       used instead.\n\n    *) Change: the \"http2_max_field_size\" and \"http2_max_header_size\"\n       directives have been removed, the \"large_client_header_buffers\"\n       directive should be used instead.\n\n    *) Feature: now, if free worker connections are exhausted, nginx starts\n       closing not only keepalive connections, but also connections in\n       lingering close.\n\n    *) Bugfix: \"zero size buf in output\" alerts might appear in logs if an\n       upstream server returned an incorrect response during unbuffered\n       proxying; the bug had appeared in 1.19.1.\n\n    *) Bugfix: HEAD requests were handled incorrectly if the \"return\"\n       directive was used with the \"image_filter\" or \"xslt_stylesheet\"\n       directives.\n\n    *) Bugfix: in the \"add_trailer\" directive.\n\n\nChanges with nginx 1.19.6                                        15 Dec 2020\n\n    *) Bugfix: \"no live upstreams\" errors if a \"server\" inside \"upstream\"\n       block was marked as \"down\".\n\n    *) Bugfix: a segmentation fault might occur in a worker process if HTTPS\n       was used; the bug had appeared in 1.19.5.\n\n    *) Bugfix: nginx returned the 400 response on requests like\n       \"GET http://example.com?args HTTP/1.0\".\n\n    *) Bugfix: in the ngx_http_flv_module and ngx_http_mp4_module.\n       Thanks to Chris Newton.\n\n\nChanges with nginx 1.19.5                                        24 Nov 2020\n\n    *) Feature: the -e switch.\n\n    *) Feature: the same source files can now be specified in different\n       modules while building addon modules.\n\n    *) Bugfix: SSL shutdown did not work when lingering close was used.\n\n    *) Bugfix: \"upstream sent frame for closed stream\" errors might occur\n       when working with gRPC backends.\n\n    *) Bugfix: in request body filters internal API.\n\n\nChanges with nginx 1.19.4                                        27 Oct 2020\n\n    *) Feature: the \"ssl_conf_command\", \"proxy_ssl_conf_command\",\n       \"grpc_ssl_conf_command\", and \"uwsgi_ssl_conf_command\" directives.\n\n    *) Feature: the \"ssl_reject_handshake\" directive.\n\n    *) Feature: the \"proxy_smtp_auth\" directive in mail proxy.\n\n\nChanges with nginx 1.19.3                                        29 Sep 2020\n\n    *) Feature: the ngx_stream_set_module.\n\n    *) Feature: the \"proxy_cookie_flags\" directive.\n\n    *) Feature: the \"userid_flags\" directive.\n\n    *) Bugfix: the \"stale-if-error\" cache control extension was erroneously\n       applied if backend returned a response with status code 500, 502,\n       503, 504, 403, 404, or 429.\n\n    *) Bugfix: \"[crit] cache file ... has too long header\" messages might\n       appear in logs if caching was used and the backend returned responses\n       with the \"Vary\" header line.\n\n    *) Workaround: \"[crit] SSL_write() failed\" messages might appear in logs\n       when using OpenSSL 1.1.1.\n\n    *) Bugfix: \"SSL_shutdown() failed (SSL: ... bad write retry)\" messages\n       might appear in logs; the bug had appeared in 1.19.2.\n\n    *) Bugfix: a segmentation fault might occur in a worker process when\n       using HTTP/2 if errors with code 400 were redirected to a proxied\n       location using the \"error_page\" directive.\n\n    *) Bugfix: socket leak when using HTTP/2 and subrequests in the njs\n       module.\n\n\nChanges with nginx 1.19.2                                        11 Aug 2020\n\n    *) Change: now nginx starts closing keepalive connections before all\n       free worker connections are exhausted, and logs a warning about this\n       to the error log.\n\n    *) Change: optimization of client request body reading when using\n       chunked transfer encoding.\n\n    *) Bugfix: memory leak if the \"ssl_ocsp\" directive was used.\n\n    *) Bugfix: \"zero size buf in output\" alerts might appear in logs if a\n       FastCGI server returned an incorrect response; the bug had appeared\n       in 1.19.1.\n\n    *) Bugfix: a segmentation fault might occur in a worker process if\n       different large_client_header_buffers sizes were used in different\n       virtual servers.\n\n    *) Bugfix: SSL shutdown might not work.\n\n    *) Bugfix: \"SSL_shutdown() failed (SSL: ... bad write retry)\" messages\n       might appear in logs.\n\n    *) Bugfix: in the ngx_http_slice_module.\n\n    *) Bugfix: in the ngx_http_xslt_filter_module.\n\n\nChanges with nginx 1.19.1                                        07 Jul 2020\n\n    *) Change: the \"lingering_close\", \"lingering_time\", and\n       \"lingering_timeout\" directives now work when using HTTP/2.\n\n    *) Change: now extra data sent by a backend are always discarded.\n\n    *) Change: now after receiving a too short response from a FastCGI\n       server nginx tries to send the available part of the response to the\n       client, and then closes the client connection.\n\n    *) Change: now after receiving a response with incorrect length from a\n       gRPC backend nginx stops response processing with an error.\n\n    *) Feature: the \"min_free\" parameter of the \"proxy_cache_path\",\n       \"fastcgi_cache_path\", \"scgi_cache_path\", and \"uwsgi_cache_path\"\n       directives.\n       Thanks to Adam Bambuch.\n\n    *) Bugfix: nginx did not delete unix domain listen sockets during\n       graceful shutdown on the SIGQUIT signal.\n\n    *) Bugfix: zero length UDP datagrams were not proxied.\n\n    *) Bugfix: proxying to uwsgi backends using SSL might not work.\n       Thanks to Guanzhong Chen.\n\n    *) Bugfix: in error handling when using the \"ssl_ocsp\" directive.\n\n    *) Bugfix: on XFS and NFS file systems disk cache size might be\n       calculated incorrectly.\n\n    *) Bugfix: \"negative size buf in writer\" alerts might appear in logs if\n       a memcached server returned a malformed response.\n\n\nChanges with nginx 1.19.0                                        26 May 2020\n\n    *) Feature: client certificate validation with OCSP.\n\n    *) Bugfix: \"upstream sent frame for closed stream\" errors might occur\n       when working with gRPC backends.\n\n    *) Bugfix: OCSP stapling might not work if the \"resolver\" directive was\n       not specified.\n\n    *) Bugfix: connections with incorrect HTTP/2 preface were not logged.\n\n\nChanges with nginx 1.17.10                                       14 Apr 2020\n\n    *) Feature: the \"auth_delay\" directive.\n\n\nChanges with nginx 1.17.9                                        03 Mar 2020\n\n    *) Change: now nginx does not allow several \"Host\" request header lines.\n\n    *) Bugfix: nginx ignored additional \"Transfer-Encoding\" request header\n       lines.\n\n    *) Bugfix: socket leak when using HTTP/2.\n\n    *) Bugfix: a segmentation fault might occur in a worker process if OCSP\n       stapling was used.\n\n    *) Bugfix: in the ngx_http_mp4_module.\n\n    *) Bugfix: nginx used status code 494 instead of 400 if errors with code\n       494 were redirected with the \"error_page\" directive.\n\n    *) Bugfix: socket leak when using subrequests in the njs module and the\n       \"aio\" directive.\n\n\nChanges with nginx 1.17.8                                        21 Jan 2020\n\n    *) Feature: variables support in the \"grpc_pass\" directive.\n\n    *) Bugfix: a timeout might occur while handling pipelined requests in an\n       SSL connection; the bug had appeared in 1.17.5.\n\n    *) Bugfix: in the \"debug_points\" directive when using HTTP/2.\n       Thanks to Daniil Bondarev.\n\n\nChanges with nginx 1.17.7                                        24 Dec 2019\n\n    *) Bugfix: a segmentation fault might occur on start or during\n       reconfiguration if the \"rewrite\" directive with an empty replacement\n       string was used in the configuration.\n\n    *) Bugfix: a segmentation fault might occur in a worker process if the\n       \"break\" directive was used with the \"alias\" directive or with the\n       \"proxy_pass\" directive with a URI.\n\n    *) Bugfix: the \"Location\" response header line might contain garbage if\n       the request URI was rewritten to the one containing a null character.\n\n    *) Bugfix: requests with bodies were handled incorrectly when returning\n       redirections with the \"error_page\" directive; the bug had appeared in\n       0.7.12.\n\n    *) Bugfix: socket leak when using HTTP/2.\n\n    *) Bugfix: a timeout might occur while handling pipelined requests in an\n       SSL connection; the bug had appeared in 1.17.5.\n\n    *) Bugfix: in the ngx_http_dav_module.\n\n\nChanges with nginx 1.17.6                                        19 Nov 2019\n\n    *) Feature: the $proxy_protocol_server_addr and\n       $proxy_protocol_server_port variables.\n\n    *) Feature: the \"limit_conn_dry_run\" directive.\n\n    *) Feature: the $limit_req_status and $limit_conn_status variables.\n\n\nChanges with nginx 1.17.5                                        22 Oct 2019\n\n    *) Feature: now nginx uses ioctl(FIONREAD), if available, to avoid\n       reading from a fast connection for a long time.\n\n    *) Bugfix: incomplete escaped characters at the end of the request URI\n       were ignored.\n\n    *) Bugfix: \"/.\" and \"/..\" at the end of the request URI were not\n       normalized.\n\n    *) Bugfix: in the \"merge_slashes\" directive.\n\n    *) Bugfix: in the \"ignore_invalid_headers\" directive.\n       Thanks to Alan Kemp.\n\n    *) Bugfix: nginx could not be built with MinGW-w64 gcc 8.1 or newer.\n\n\nChanges with nginx 1.17.4                                        24 Sep 2019\n\n    *) Change: better detection of incorrect client behavior in HTTP/2.\n\n    *) Change: in handling of not fully read client request body when\n       returning errors in HTTP/2.\n\n    *) Bugfix: the \"worker_shutdown_timeout\" directive might not work when\n       using HTTP/2.\n\n    *) Bugfix: a segmentation fault might occur in a worker process when\n       using HTTP/2 and the \"proxy_request_buffering\" directive.\n\n    *) Bugfix: the ECONNABORTED error log level was \"crit\" instead of\n       \"error\" on Windows when using SSL.\n\n    *) Bugfix: nginx ignored extra data when using chunked transfer\n       encoding.\n\n    *) Bugfix: nginx always returned the 500 error if the \"return\" directive\n       was used and an error occurred during reading client request body.\n\n    *) Bugfix: in memory allocation error handling.\n\n\nChanges with nginx 1.17.3                                        13 Aug 2019\n\n    *) Security: when using HTTP/2 a client might cause excessive memory\n       consumption and CPU usage (CVE-2019-9511, CVE-2019-9513,\n       CVE-2019-9516).\n\n    *) Bugfix: \"zero size buf\" alerts might appear in logs when using\n       gzipping; the bug had appeared in 1.17.2.\n\n    *) Bugfix: a segmentation fault might occur in a worker process if the\n       \"resolver\" directive was used in SMTP proxy.\n\n\nChanges with nginx 1.17.2                                        23 Jul 2019\n\n    *) Change: minimum supported zlib version is 1.2.0.4.\n       Thanks to Ilya Leoshkevich.\n\n    *) Change: the $r->internal_redirect() embedded perl method now expects\n       escaped URIs.\n\n    *) Feature: it is now possible to switch to a named location using the\n       $r->internal_redirect() embedded perl method.\n\n    *) Bugfix: in error handling in embedded perl.\n\n    *) Bugfix: a segmentation fault might occur on start or during\n       reconfiguration if hash bucket size larger than 64 kilobytes was used\n       in the configuration.\n\n    *) Bugfix: nginx might hog CPU during unbuffered proxying and when\n       proxying WebSocket connections if the select, poll, or /dev/poll\n       methods were used.\n\n    *) Bugfix: in the ngx_http_xslt_filter_module.\n\n    *) Bugfix: in the ngx_http_ssi_filter_module.\n\n\nChanges with nginx 1.17.1                                        25 Jun 2019\n\n    *) Feature: the \"limit_req_dry_run\" directive.\n\n    *) Feature: when using the \"hash\" directive inside the \"upstream\" block\n       an empty hash key now triggers round-robin balancing.\n       Thanks to Niklas Keller.\n\n    *) Bugfix: a segmentation fault might occur in a worker process if\n       caching was used along with the \"image_filter\" directive, and errors\n       with code 415 were redirected with the \"error_page\" directive; the\n       bug had appeared in 1.11.10.\n\n    *) Bugfix: a segmentation fault might occur in a worker process if\n       embedded perl was used; the bug had appeared in 1.7.3.\n\n\nChanges with nginx 1.17.0                                        21 May 2019\n\n    *) Feature: variables support in the \"limit_rate\" and \"limit_rate_after\"\n       directives.\n\n    *) Feature: variables support in the \"proxy_upload_rate\" and\n       \"proxy_download_rate\" directives in the stream module.\n\n    *) Change: minimum supported OpenSSL version is 0.9.8.\n\n    *) Change: now the postpone filter is always built.\n\n    *) Bugfix: the \"include\" directive did not work inside the \"if\" and\n       \"limit_except\" blocks.\n\n    *) Bugfix: in byte ranges processing.\n\n\nChanges with nginx 1.15.12                                       16 Apr 2019\n\n    *) Bugfix: a segmentation fault might occur in a worker process if\n       variables were used in the \"ssl_certificate\" or \"ssl_certificate_key\"\n       directives and OCSP stapling was enabled.\n\n\nChanges with nginx 1.15.11                                       09 Apr 2019\n\n    *) Bugfix: in the \"ssl_stapling_file\" directive on Windows.\n\n\nChanges with nginx 1.15.10                                       26 Mar 2019\n\n    *) Change: when using a hostname in the \"listen\" directive nginx now\n       creates listening sockets for all addresses the hostname resolves to\n       (previously, only the first address was used).\n\n    *) Feature: port ranges in the \"listen\" directive.\n\n    *) Feature: loading of SSL certificates and secret keys from variables.\n\n    *) Workaround: the $ssl_server_name variable might be empty when using\n       OpenSSL 1.1.1.\n\n    *) Bugfix: nginx/Windows could not be built with Visual Studio 2015 or\n       newer; the bug had appeared in 1.15.9.\n\n\nChanges with nginx 1.15.9                                        26 Feb 2019\n\n    *) Feature: variables support in the \"ssl_certificate\" and\n       \"ssl_certificate_key\" directives.\n\n    *) Feature: the \"poll\" method is now available on Windows when using\n       Windows Vista or newer.\n\n    *) Bugfix: if the \"select\" method was used on Windows and an error\n       occurred while establishing a backend connection, nginx waited for\n       the connection establishment timeout to expire.\n\n    *) Bugfix: the \"proxy_upload_rate\" and \"proxy_download_rate\" directives\n       in the stream module worked incorrectly when proxying UDP datagrams.\n\n\nChanges with nginx 1.15.8                                        25 Dec 2018\n\n    *) Feature: the $upstream_bytes_sent variable.\n       Thanks to Piotr Sikora.\n\n    *) Feature: new directives in vim syntax highlighting scripts.\n       Thanks to Gena Makhomed.\n\n    *) Bugfix: in the \"proxy_cache_background_update\" directive.\n\n    *) Bugfix: in the \"geo\" directive when using unix domain listen sockets.\n\n    *) Workaround: the \"ignoring stale global SSL error ... bad length\"\n       alerts might appear in logs when using the \"ssl_early_data\" directive\n       with OpenSSL.\n\n    *) Bugfix: in nginx/Windows.\n\n    *) Bugfix: in the ngx_http_autoindex_module on 32-bit platforms.\n\n\nChanges with nginx 1.15.7                                        27 Nov 2018\n\n    *) Feature: the \"proxy_requests\" directive in the stream module.\n\n    *) Feature: the \"delay\" parameter of the \"limit_req\" directive.\n       Thanks to Vladislav Shabanov and Peter Shchuchkin.\n\n    *) Bugfix: memory leak on errors during reconfiguration.\n\n    *) Bugfix: in the $upstream_response_time, $upstream_connect_time, and\n       $upstream_header_time variables.\n\n    *) Bugfix: a segmentation fault might occur in a worker process if the\n       ngx_http_mp4_module was used on 32-bit platforms.\n\n\nChanges with nginx 1.15.6                                        06 Nov 2018\n\n    *) Security: when using HTTP/2 a client might cause excessive memory\n       consumption (CVE-2018-16843) and CPU usage (CVE-2018-16844).\n\n    *) Security: processing of a specially crafted mp4 file with the\n       ngx_http_mp4_module might result in worker process memory disclosure\n       (CVE-2018-16845).\n\n    *) Feature: the \"proxy_socket_keepalive\", \"fastcgi_socket_keepalive\",\n       \"grpc_socket_keepalive\", \"memcached_socket_keepalive\",\n       \"scgi_socket_keepalive\", and \"uwsgi_socket_keepalive\" directives.\n\n    *) Bugfix: if nginx was built with OpenSSL 1.1.0 and used with OpenSSL\n       1.1.1, the TLS 1.3 protocol was always enabled.\n\n    *) Bugfix: working with gRPC backends might result in excessive memory\n       consumption.\n\n\nChanges with nginx 1.15.5                                        02 Oct 2018\n\n    *) Bugfix: a segmentation fault might occur in a worker process when\n       using OpenSSL 1.1.0h or newer; the bug had appeared in 1.15.4.\n\n    *) Bugfix: of minor potential bugs.\n\n\nChanges with nginx 1.15.4                                        25 Sep 2018\n\n    *) Feature: now the \"ssl_early_data\" directive can be used with OpenSSL.\n\n    *) Bugfix: in the ngx_http_uwsgi_module.\n       Thanks to Chris Caputo.\n\n    *) Bugfix: connections with some gRPC backends might not be cached when\n       using the \"keepalive\" directive.\n\n    *) Bugfix: a socket leak might occur when using the \"error_page\"\n       directive to redirect early request processing errors, notably errors\n       with code 400.\n\n    *) Bugfix: the \"return\" directive did not change the response code when\n       returning errors if the request was redirected by the \"error_page\"\n       directive.\n\n    *) Bugfix: standard error pages and responses of the\n       ngx_http_autoindex_module module used the \"bgcolor\" attribute, and\n       might be displayed incorrectly when using custom color settings in\n       browsers.\n       Thanks to Nova DasSarma.\n\n    *) Change: the logging level of the \"no suitable key share\" and \"no\n       suitable signature algorithm\" SSL errors has been lowered from \"crit\"\n       to \"info\".\n\n\nChanges with nginx 1.15.3                                        28 Aug 2018\n\n    *) Feature: now TLSv1.3 can be used with BoringSSL.\n\n    *) Feature: the \"ssl_early_data\" directive, currently available with\n       BoringSSL.\n\n    *) Feature: the \"keepalive_timeout\" and \"keepalive_requests\" directives\n       in the \"upstream\" block.\n\n    *) Bugfix: the ngx_http_dav_module did not truncate destination file\n       when copying a file over an existing one with the COPY method.\n\n    *) Bugfix: the ngx_http_dav_module used zero access rights on the\n       destination file and did not preserve file modification time when\n       moving a file between different file systems with the MOVE method.\n\n    *) Bugfix: the ngx_http_dav_module used default access rights when\n       copying a file with the COPY method.\n\n    *) Workaround: some clients might not work when using HTTP/2; the bug\n       had appeared in 1.13.5.\n\n    *) Bugfix: nginx could not be built with LibreSSL 2.8.0.\n\n\nChanges with nginx 1.15.2                                        24 Jul 2018\n\n    *) Feature: the $ssl_preread_protocol variable in the\n       ngx_stream_ssl_preread_module.\n\n    *) Feature: now when using the \"reset_timedout_connection\" directive\n       nginx will reset connections being closed with the 444 code.\n\n    *) Change: a logging level of the \"http request\", \"https proxy request\",\n       \"unsupported protocol\", and \"version too low\" SSL errors has been\n       lowered from \"crit\" to \"info\".\n\n    *) Bugfix: DNS requests were not resent if initial sending of a request\n       failed.\n\n    *) Bugfix: the \"reuseport\" parameter of the \"listen\" directive was\n       ignored if the number of worker processes was specified after the\n       \"listen\" directive.\n\n    *) Bugfix: when using OpenSSL 1.1.0 or newer it was not possible to\n       switch off \"ssl_prefer_server_ciphers\" in a virtual server if it was\n       switched on in the default server.\n\n    *) Bugfix: SSL session reuse with upstream servers did not work with the\n       TLS 1.3 protocol.\n\n\nChanges with nginx 1.15.1                                        03 Jul 2018\n\n    *) Feature: the \"random\" directive inside the \"upstream\" block.\n\n    *) Feature: improved performance when using the \"hash\" and \"ip_hash\"\n       directives with the \"zone\" directive.\n\n    *) Feature: the \"reuseport\" parameter of the \"listen\" directive now uses\n       SO_REUSEPORT_LB on FreeBSD 12.\n\n    *) Bugfix: HTTP/2 server push did not work if SSL was terminated by a\n       proxy server in front of nginx.\n\n    *) Bugfix: the \"tcp_nopush\" directive was always used on backend\n       connections.\n\n    *) Bugfix: sending a disk-buffered request body to a gRPC backend might\n       fail.\n\n\nChanges with nginx 1.15.0                                        05 Jun 2018\n\n    *) Change: the \"ssl\" directive is deprecated; the \"ssl\" parameter of the\n       \"listen\" directive should be used instead.\n\n    *) Change: now nginx detects missing SSL certificates during\n       configuration testing when using the \"ssl\" parameter of the \"listen\"\n       directive.\n\n    *) Feature: now the stream module can handle multiple incoming UDP\n       datagrams from a client within a single session.\n\n    *) Bugfix: it was possible to specify an incorrect response code in the\n       \"proxy_cache_valid\" directive.\n\n    *) Bugfix: nginx could not be built by gcc 8.1.\n\n    *) Bugfix: logging to syslog stopped on local IP address changes.\n\n    *) Bugfix: nginx could not be built by clang with CUDA SDK installed;\n       the bug had appeared in 1.13.8.\n\n    *) Bugfix: \"getsockopt(TCP_FASTOPEN) ... failed\" messages might appear\n       in logs during binary upgrade when using unix domain listen sockets\n       on FreeBSD.\n\n    *) Bugfix: nginx could not be built on Fedora 28 Linux.\n\n    *) Bugfix: request processing rate might exceed configured rate when\n       using the \"limit_req\" directive.\n\n    *) Bugfix: in handling of client addresses when using unix domain listen\n       sockets to work with datagrams on Linux.\n\n    *) Bugfix: in memory allocation error handling.\n\n\nChanges with nginx 1.13.12                                       10 Apr 2018\n\n    *) Bugfix: connections with gRPC backends might be closed unexpectedly\n       when returning a large response.\n\n\nChanges with nginx 1.13.11                                       03 Apr 2018\n\n    *) Feature: the \"proxy_protocol\" parameter of the \"listen\" directive now\n       supports the PROXY protocol version 2.\n\n    *) Bugfix: nginx could not be built with OpenSSL 1.1.1 statically on\n       Linux.\n\n    *) Bugfix: in the \"http_404\", \"http_500\", etc. parameters of the\n       \"proxy_next_upstream\" directive.\n\n\nChanges with nginx 1.13.10                                       20 Mar 2018\n\n    *) Feature: the \"set\" parameter of the \"include\" SSI directive now\n       allows writing arbitrary responses to a variable; the\n       \"subrequest_output_buffer_size\" directive defines maximum response\n       size.\n\n    *) Feature: now nginx uses clock_gettime(CLOCK_MONOTONIC) if available,\n       to avoid timeouts being incorrectly triggered on system time changes.\n\n    *) Feature: the \"escape=none\" parameter of the \"log_format\" directive.\n       Thanks to Johannes Baiter and Calin Don.\n\n    *) Feature: the $ssl_preread_alpn_protocols variable in the\n       ngx_stream_ssl_preread_module.\n\n    *) Feature: the ngx_http_grpc_module.\n\n    *) Bugfix: in memory allocation error handling in the \"geo\" directive.\n\n    *) Bugfix: when using variables in the \"auth_basic_user_file\" directive\n       a null character might appear in logs.\n       Thanks to Vadim Filimonov.\n\n\nChanges with nginx 1.13.9                                        20 Feb 2018\n\n    *) Feature: HTTP/2 server push support; the \"http2_push\" and\n       \"http2_push_preload\" directives.\n\n    *) Bugfix: \"header already sent\" alerts might appear in logs when using\n       cache; the bug had appeared in 1.9.13.\n\n    *) Bugfix: a segmentation fault might occur in a worker process if the\n       \"ssl_verify_client\" directive was used and no SSL certificate was\n       specified in a virtual server.\n\n    *) Bugfix: in the ngx_http_v2_module.\n\n    *) Bugfix: in the ngx_http_dav_module.\n\n\nChanges with nginx 1.13.8                                        26 Dec 2017\n\n    *) Feature: now nginx automatically preserves the CAP_NET_RAW capability\n       in worker processes when using the \"transparent\" parameter of the\n       \"proxy_bind\", \"fastcgi_bind\", \"memcached_bind\", \"scgi_bind\", and\n       \"uwsgi_bind\" directives.\n\n    *) Feature: improved CPU cache line size detection.\n       Thanks to Debayan Ghosh.\n\n    *) Feature: new directives in vim syntax highlighting scripts.\n       Thanks to Gena Makhomed.\n\n    *) Bugfix: binary upgrade refused to work if nginx was re-parented to a\n       process with PID different from 1 after its parent process has\n       finished.\n\n    *) Bugfix: the ngx_http_autoindex_module incorrectly handled requests\n       with bodies.\n\n    *) Bugfix: in the \"proxy_limit_rate\" directive when used with the\n       \"keepalive\" directive.\n\n    *) Bugfix: some parts of a response might be buffered when using\n       \"proxy_buffering off\" if the client connection used SSL.\n       Thanks to Patryk Lesiewicz.\n\n    *) Bugfix: in the \"proxy_cache_background_update\" directive.\n\n    *) Bugfix: it was not possible to start a parameter with a variable in\n       the \"${name}\" form with the name in curly brackets without enclosing\n       the parameter into single or double quotes.\n\n\nChanges with nginx 1.13.7                                        21 Nov 2017\n\n    *) Bugfix: in the $upstream_status variable.\n\n    *) Bugfix: a segmentation fault might occur in a worker process if a\n       backend returned a \"101 Switching Protocols\" response to a\n       subrequest.\n\n    *) Bugfix: a segmentation fault occurred in a master process if a shared\n       memory zone size was changed during a reconfiguration and the\n       reconfiguration failed.\n\n    *) Bugfix: in the ngx_http_fastcgi_module.\n\n    *) Bugfix: nginx returned the 500 error if parameters without variables\n       were specified in the \"xslt_stylesheet\" directive.\n\n    *) Workaround: \"gzip filter failed to use preallocated memory\" alerts\n       appeared in logs when using a zlib library variant from Intel.\n\n    *) Bugfix: the \"worker_shutdown_timeout\" directive did not work when\n       using mail proxy and when proxying WebSocket connections.\n\n\nChanges with nginx 1.13.6                                        10 Oct 2017\n\n    *) Bugfix: switching to the next upstream server in the stream module\n       did not work when using the \"ssl_preread\" directive.\n\n    *) Bugfix: in the ngx_http_v2_module.\n       Thanks to Piotr Sikora.\n\n    *) Bugfix: nginx did not support dates after the year 2038 on 32-bit\n       platforms with 64-bit time_t.\n\n    *) Bugfix: in handling of dates prior to the year 1970 and after the\n       year 10000.\n\n    *) Bugfix: in the stream module timeouts waiting for UDP datagrams from\n       upstream servers were not logged or logged at the \"info\" level\n       instead of \"error\".\n\n    *) Bugfix: when using HTTP/2 nginx might return the 400 response without\n       logging the reason.\n\n    *) Bugfix: in processing of corrupted cache files.\n\n    *) Bugfix: cache control headers were ignored when caching errors\n       intercepted by error_page.\n\n    *) Bugfix: when using HTTP/2 client request body might be corrupted.\n\n    *) Bugfix: in handling of client addresses when using unix domain\n       sockets.\n\n    *) Bugfix: nginx hogged CPU when using the \"hash ... consistent\"\n       directive in the upstream block if large weights were used and all or\n       most of the servers were unavailable.\n\n\nChanges with nginx 1.13.5                                        05 Sep 2017\n\n    *) Feature: the $ssl_client_escaped_cert variable.\n\n    *) Bugfix: the \"ssl_session_ticket_key\" directive and the \"include\"\n       parameter of the \"geo\" directive did not work on Windows.\n\n    *) Bugfix: incorrect response length was returned on 32-bit platforms\n       when requesting more than 4 gigabytes with multiple ranges.\n\n    *) Bugfix: the \"expires modified\" directive and processing of the\n       \"If-Range\" request header line did not use the response last\n       modification time if proxying without caching was used.\n\n\nChanges with nginx 1.13.4                                        08 Aug 2017\n\n    *) Feature: the ngx_http_mirror_module.\n\n    *) Bugfix: client connections might be dropped during configuration\n       testing when using the \"reuseport\" parameter of the \"listen\"\n       directive on Linux.\n\n    *) Bugfix: request body might not be available in subrequests if it was\n       saved to a file and proxying was used.\n\n    *) Bugfix: cleaning cache based on the \"max_size\" parameter did not work\n       on Windows.\n\n    *) Bugfix: any shared memory allocation required 4096 bytes on Windows.\n\n    *) Bugfix: nginx worker might be terminated abnormally when using the\n       \"zone\" directive inside the \"upstream\" block on Windows.\n\n\nChanges with nginx 1.13.3                                        11 Jul 2017\n\n    *) Security: a specially crafted request might result in an integer\n       overflow and incorrect processing of ranges in the range filter,\n       potentially resulting in sensitive information leak (CVE-2017-7529).\n\n\nChanges with nginx 1.13.2                                        27 Jun 2017\n\n    *) Change: nginx now returns 200 instead of 416 when a range starting\n       with 0 is requested from an empty file.\n\n    *) Feature: the \"add_trailer\" directive.\n       Thanks to Piotr Sikora.\n\n    *) Bugfix: nginx could not be built on Cygwin and NetBSD; the bug had\n       appeared in 1.13.0.\n\n    *) Bugfix: nginx could not be built under MSYS2 / MinGW 64-bit.\n       Thanks to Orgad Shaneh.\n\n    *) Bugfix: a segmentation fault might occur in a worker process when\n       using SSI with many includes and proxy_pass with variables.\n\n    *) Bugfix: in the ngx_http_v2_module.\n       Thanks to Piotr Sikora.\n\n\nChanges with nginx 1.13.1                                        30 May 2017\n\n    *) Feature: now a hostname can be used as the \"set_real_ip_from\"\n       directive parameter.\n\n    *) Feature: vim syntax highlighting scripts improvements.\n\n    *) Feature: the \"worker_cpu_affinity\" directive now works on DragonFly\n       BSD.\n       Thanks to Sepherosa Ziehau.\n\n    *) Bugfix: SSL renegotiation on backend connections did not work when\n       using OpenSSL before 1.1.0.\n\n    *) Workaround: nginx could not be built with Oracle Developer Studio\n       12.5.\n\n    *) Workaround: now cache manager ignores long locked cache entries when\n       cleaning cache based on the \"max_size\" parameter.\n\n    *) Bugfix: client SSL connections were immediately closed if deferred\n       accept and the \"proxy_protocol\" parameter of the \"listen\" directive\n       were used.\n\n    *) Bugfix: in the \"proxy_cache_background_update\" directive.\n\n    *) Workaround: now the \"tcp_nodelay\" directive sets the TCP_NODELAY\n       option before an SSL handshake.\n\n\nChanges with nginx 1.13.0                                        25 Apr 2017\n\n    *) Change: SSL renegotiation is now allowed on backend connections.\n\n    *) Feature: the \"rcvbuf\" and \"sndbuf\" parameters of the \"listen\"\n       directives of the mail proxy and stream modules.\n\n    *) Feature: the \"return\" and \"error_page\" directives can now be used to\n       return 308 redirections.\n       Thanks to Simon Leblanc.\n\n    *) Feature: the \"TLSv1.3\" parameter of the \"ssl_protocols\" directive.\n\n    *) Feature: when logging signals nginx now logs PID of the process which\n       sent the signal.\n\n    *) Bugfix: in memory allocation error handling.\n\n    *) Bugfix: if a server in the stream module listened on a wildcard\n       address, the source address of a response UDP datagram could differ\n       from the original datagram destination address.\n\n\nChanges with nginx 1.11.13                                       04 Apr 2017\n\n    *) Feature: the \"http_429\" parameter of the \"proxy_next_upstream\",\n       \"fastcgi_next_upstream\", \"scgi_next_upstream\", and\n       \"uwsgi_next_upstream\" directives.\n       Thanks to Piotr Sikora.\n\n    *) Bugfix: in memory allocation error handling.\n\n    *) Bugfix: requests might hang when using the \"sendfile\" and\n       \"timer_resolution\" directives on Linux.\n\n    *) Bugfix: requests might hang when using the \"sendfile\" and \"aio_write\"\n       directives with subrequests.\n\n    *) Bugfix: in the ngx_http_v2_module.\n       Thanks to Piotr Sikora.\n\n    *) Bugfix: a segmentation fault might occur in a worker process when\n       using HTTP/2.\n\n    *) Bugfix: requests might hang when using the \"limit_rate\",\n       \"sendfile_max_chunk\", \"limit_req\" directives, or the $r->sleep()\n       embedded perl method with subrequests.\n\n    *) Bugfix: in the ngx_http_slice_module.\n\n\nChanges with nginx 1.11.12                                       24 Mar 2017\n\n    *) Bugfix: nginx might hog CPU; the bug had appeared in 1.11.11.\n\n\nChanges with nginx 1.11.11                                       21 Mar 2017\n\n    *) Feature: the \"worker_shutdown_timeout\" directive.\n\n    *) Feature: vim syntax highlighting scripts improvements.\n       Thanks to Wei-Ko Kao.\n\n    *) Bugfix: a segmentation fault might occur in a worker process if the\n       $limit_rate variable was set to an empty string.\n\n    *) Bugfix: the \"proxy_cache_background_update\",\n       \"fastcgi_cache_background_update\", \"scgi_cache_background_update\",\n       and \"uwsgi_cache_background_update\" directives might work incorrectly\n       if the \"if\" directive was used.\n\n    *) Bugfix: a segmentation fault might occur in a worker process if\n       number of large_client_header_buffers in a virtual server was\n       different from the one in the default server.\n\n    *) Bugfix: in the mail proxy server.\n\n\nChanges with nginx 1.11.10                                       14 Feb 2017\n\n    *) Change: cache header format has been changed, previously cached\n       responses will be invalidated.\n\n    *) Feature: support of \"stale-while-revalidate\" and \"stale-if-error\"\n       extensions in the \"Cache-Control\" backend response header line.\n\n    *) Feature: the \"proxy_cache_background_update\",\n       \"fastcgi_cache_background_update\", \"scgi_cache_background_update\",\n       and \"uwsgi_cache_background_update\" directives.\n\n    *) Feature: nginx is now able to cache responses with the \"Vary\" header\n       line up to 128 characters long (instead of 42 characters in previous\n       versions).\n\n    *) Feature: the \"build\" parameter of the \"server_tokens\" directive.\n       Thanks to Tom Thorogood.\n\n    *) Bugfix: \"[crit] SSL_write() failed\" messages might appear in logs\n       when handling requests with the \"Expect: 100-continue\" request header\n       line.\n\n    *) Bugfix: the ngx_http_slice_module did not work in named locations.\n\n    *) Bugfix: a segmentation fault might occur in a worker process when\n       using AIO after an \"X-Accel-Redirect\" redirection.\n\n    *) Bugfix: reduced memory consumption for long-lived requests using\n       gzipping.\n\n\nChanges with nginx 1.11.9                                        24 Jan 2017\n\n    *) Bugfix: nginx might hog CPU when using the stream module; the bug had\n       appeared in 1.11.5.\n\n    *) Bugfix: EXTERNAL authentication mechanism in mail proxy was accepted\n       even if it was not enabled in the configuration.\n\n    *) Bugfix: a segmentation fault might occur in a worker process if the\n       \"ssl_verify_client\" directive of the stream module was used.\n\n    *) Bugfix: the \"ssl_verify_client\" directive of the stream module might\n       not work.\n\n    *) Bugfix: closing keepalive connections due to no free worker\n       connections might be too aggressive.\n       Thanks to Joel Cunningham.\n\n    *) Bugfix: an incorrect response might be returned when using the\n       \"sendfile\" directive on FreeBSD and macOS; the bug had appeared in\n       1.7.8.\n\n    *) Bugfix: a truncated response might be stored in cache when using the\n       \"aio_write\" directive.\n\n    *) Bugfix: a socket leak might occur when using the \"aio_write\"\n       directive.\n\n\nChanges with nginx 1.11.8                                        27 Dec 2016\n\n    *) Feature: the \"absolute_redirect\" directive.\n\n    *) Feature: the \"escape\" parameter of the \"log_format\" directive.\n\n    *) Feature: client SSL certificates verification in the stream module.\n\n    *) Feature: the \"ssl_session_ticket_key\" directive supports AES256\n       encryption of TLS session tickets when used with 80-byte keys.\n\n    *) Feature: vim-commentary support in vim scripts.\n       Thanks to Armin Grodon.\n\n    *) Bugfix: recursion when evaluating variables was not limited.\n\n    *) Bugfix: in the ngx_stream_ssl_preread_module.\n\n    *) Bugfix: if a server in an upstream in the stream module failed, it\n       was considered alive only when a test connection sent to it after\n       fail_timeout was closed; now a successfully established connection is\n       enough.\n\n    *) Bugfix: nginx/Windows could not be built with 64-bit Visual Studio.\n\n    *) Bugfix: nginx/Windows could not be built with OpenSSL 1.1.0.\n\n\nChanges with nginx 1.11.7                                        13 Dec 2016\n\n    *) Change: now in case of a client certificate verification error the\n       $ssl_client_verify variable contains a string with the failure\n       reason, for example, \"FAILED:certificate has expired\".\n\n    *) Feature: the $ssl_ciphers, $ssl_curves, $ssl_client_v_start,\n       $ssl_client_v_end, and $ssl_client_v_remain variables.\n\n    *) Feature: the \"volatile\" parameter of the \"map\" directive.\n\n    *) Bugfix: dependencies specified for a module were ignored while\n       building dynamic modules.\n\n    *) Bugfix: when using HTTP/2 and the \"limit_req\" or \"auth_request\"\n       directives client request body might be corrupted; the bug had\n       appeared in 1.11.0.\n\n    *) Bugfix: a segmentation fault might occur in a worker process when\n       using HTTP/2; the bug had appeared in 1.11.3.\n\n    *) Bugfix: in the ngx_http_mp4_module.\n       Thanks to Congcong Hu.\n\n    *) Bugfix: in the ngx_http_perl_module.\n\n\nChanges with nginx 1.11.6                                        15 Nov 2016\n\n    *) Change: format of the $ssl_client_s_dn and $ssl_client_i_dn variables\n       has been changed to follow RFC 2253 (RFC 4514); values in the old\n       format are available in the $ssl_client_s_dn_legacy and\n       $ssl_client_i_dn_legacy variables.\n\n    *) Change: when storing temporary files in a cache directory they will\n       be stored in the same subdirectories as corresponding cache files\n       instead of a separate subdirectory for temporary files.\n\n    *) Feature: EXTERNAL authentication mechanism support in mail proxy.\n       Thanks to Robert Norris.\n\n    *) Feature: WebP support in the ngx_http_image_filter_module.\n\n    *) Feature: variables support in the \"proxy_method\" directive.\n       Thanks to Dmitry Lazurkin.\n\n    *) Feature: the \"http2_max_requests\" directive in the\n       ngx_http_v2_module.\n\n    *) Feature: the \"proxy_cache_max_range_offset\",\n       \"fastcgi_cache_max_range_offset\", \"scgi_cache_max_range_offset\", and\n       \"uwsgi_cache_max_range_offset\" directives.\n\n    *) Bugfix: graceful shutdown of old worker processes might require\n       infinite time when using HTTP/2.\n\n    *) Bugfix: in the ngx_http_mp4_module.\n\n    *) Bugfix: \"ignore long locked inactive cache entry\" alerts might appear\n       in logs when proxying WebSocket connections with caching enabled.\n\n    *) Bugfix: nginx did not write anything to log and returned a response\n       with code 502 instead of 504 when a timeout occurred during an SSL\n       handshake to a backend.\n\n\nChanges with nginx 1.11.5                                        11 Oct 2016\n\n    *) Change: the --with-ipv6 configure option was removed, now IPv6\n       support is configured automatically.\n\n    *) Change: now if there are no available servers in an upstream, nginx\n       will not reset number of failures of all servers as it previously\n       did, but will wait for fail_timeout to expire.\n\n    *) Feature: the ngx_stream_ssl_preread_module.\n\n    *) Feature: the \"server\" directive in the \"upstream\" context supports\n       the \"max_conns\" parameter.\n\n    *) Feature: the --with-compat configure option.\n\n    *) Feature: \"manager_files\", \"manager_threshold\", and \"manager_sleep\"\n       parameters of the \"proxy_cache_path\", \"fastcgi_cache_path\",\n       \"scgi_cache_path\", and \"uwsgi_cache_path\" directives.\n\n    *) Bugfix: flags passed by the --with-ld-opt configure option were not\n       used while building perl module.\n\n    *) Bugfix: in the \"add_after_body\" directive when used with the\n       \"sub_filter\" directive.\n\n    *) Bugfix: in the $realip_remote_addr variable.\n\n    *) Bugfix: the \"dav_access\", \"proxy_store_access\",\n       \"fastcgi_store_access\", \"scgi_store_access\", and \"uwsgi_store_access\"\n       directives ignored permissions specified for user.\n\n    *) Bugfix: unix domain listen sockets might not be inherited during\n       binary upgrade on Linux.\n\n    *) Bugfix: nginx returned the 400 response on requests with the \"-\"\n       character in the HTTP method.\n\n\nChanges with nginx 1.11.4                                        13 Sep 2016\n\n    *) Feature: the $upstream_bytes_received variable.\n\n    *) Feature: the $bytes_received, $session_time, $protocol, $status,\n       $upstream_addr, $upstream_bytes_sent, $upstream_bytes_received,\n       $upstream_connect_time, $upstream_first_byte_time, and\n       $upstream_session_time variables in the stream module.\n\n    *) Feature: the ngx_stream_log_module.\n\n    *) Feature: the \"proxy_protocol\" parameter of the \"listen\" directive,\n       the $proxy_protocol_addr and $proxy_protocol_port variables in the\n       stream module.\n\n    *) Feature: the ngx_stream_realip_module.\n\n    *) Bugfix: nginx could not be built with the stream module and the\n       ngx_http_ssl_module, but without ngx_stream_ssl_module; the bug had\n       appeared in 1.11.3.\n\n    *) Feature: the IP_BIND_ADDRESS_NO_PORT socket option was not used; the\n       bug had appeared in 1.11.2.\n\n    *) Bugfix: in the \"ranges\" parameter of the \"geo\" directive.\n\n    *) Bugfix: an incorrect response might be returned when using the \"aio\n       threads\" and \"sendfile\" directives; the bug had appeared in 1.9.13.\n\n\nChanges with nginx 1.11.3                                        26 Jul 2016\n\n    *) Change: now the \"accept_mutex\" directive is turned off by default.\n\n    *) Feature: now nginx uses EPOLLEXCLUSIVE on Linux.\n\n    *) Feature: the ngx_stream_geo_module.\n\n    *) Feature: the ngx_stream_geoip_module.\n\n    *) Feature: the ngx_stream_split_clients_module.\n\n    *) Feature: variables support in the \"proxy_pass\" and \"proxy_ssl_name\"\n       directives in the stream module.\n\n    *) Bugfix: socket leak when using HTTP/2.\n\n    *) Bugfix: in configure tests.\n       Thanks to Piotr Sikora.\n\n\nChanges with nginx 1.11.2                                        05 Jul 2016\n\n    *) Change: now nginx always uses internal MD5 and SHA1 implementations;\n       the --with-md5 and --with-sha1 configure options were canceled.\n\n    *) Feature: variables support in the stream module.\n\n    *) Feature: the ngx_stream_map_module.\n\n    *) Feature: the ngx_stream_return_module.\n\n    *) Feature: a port can be specified in the \"proxy_bind\", \"fastcgi_bind\",\n       \"memcached_bind\", \"scgi_bind\", and \"uwsgi_bind\" directives.\n\n    *) Feature: now nginx uses the IP_BIND_ADDRESS_NO_PORT socket option\n       when available.\n\n    *) Bugfix: a segmentation fault might occur in a worker process when\n       using HTTP/2 and the \"proxy_request_buffering\" directive.\n\n    *) Bugfix: the \"Content-Length\" request header line was always added to\n       requests passed to backends, including requests without body, when\n       using HTTP/2.\n\n    *) Bugfix: \"http request count is zero\" alerts might appear in logs when\n       using HTTP/2.\n\n    *) Bugfix: unnecessary buffering might occur when using the \"sub_filter\"\n       directive; the issue had appeared in 1.9.4.\n\n\nChanges with nginx 1.11.1                                        31 May 2016\n\n    *) Security: a segmentation fault might occur in a worker process while\n       writing a specially crafted request body to a temporary file\n       (CVE-2016-4450); the bug had appeared in 1.3.9.\n\n\nChanges with nginx 1.11.0                                        24 May 2016\n\n    *) Feature: the \"transparent\" parameter of the \"proxy_bind\",\n       \"fastcgi_bind\", \"memcached_bind\", \"scgi_bind\", and \"uwsgi_bind\"\n       directives.\n\n    *) Feature: the $request_id variable.\n\n    *) Feature: the \"map\" directive supports combinations of multiple\n       variables as resulting values.\n\n    *) Feature: now nginx checks if EPOLLRDHUP events are supported by\n       kernel, and optimizes connection handling accordingly if the \"epoll\"\n       method is used.\n\n    *) Feature: the \"ssl_certificate\" and \"ssl_certificate_key\" directives\n       can be specified multiple times to load certificates of different\n       types (for example, RSA and ECDSA).\n\n    *) Feature: the \"ssl_ecdh_curve\" directive now allows specifying a list\n       of curves when using OpenSSL 1.0.2 or newer; by default a list built\n       into OpenSSL is used.\n\n    *) Change: to use DHE ciphers it is now required to specify parameters\n       using the \"ssl_dhparam\" directive.\n\n    *) Feature: the $proxy_protocol_port variable.\n\n    *) Feature: the $realip_remote_port variable in the\n       ngx_http_realip_module.\n\n    *) Feature: the ngx_http_realip_module is now able to set the client\n       port in addition to the address.\n\n    *) Change: the \"421 Misdirected Request\" response now used when\n       rejecting requests to a virtual server different from one negotiated\n       during an SSL handshake; this improves interoperability with some\n       HTTP/2 clients when using client certificates.\n\n    *) Change: HTTP/2 clients can now start sending request body\n       immediately; the \"http2_body_preread_size\" directive controls size of\n       the buffer used before nginx will start reading client request body.\n\n    *) Bugfix: cached error responses were not updated when using the\n       \"proxy_cache_bypass\" directive.\n\n\nChanges with nginx 1.9.15                                        19 Apr 2016\n\n    *) Bugfix: \"recv() failed\" errors might occur when using HHVM as a\n       FastCGI server.\n\n    *) Bugfix: when using HTTP/2 and the \"limit_req\" or \"auth_request\"\n       directives a timeout or a \"client violated flow control\" error might\n       occur while reading client request body; the bug had appeared in\n       1.9.14.\n\n    *) Workaround: a response might not be shown by some browsers if HTTP/2\n       was used and client request body was not fully read; the bug had\n       appeared in 1.9.14.\n\n    *) Bugfix: connections might hang when using the \"aio threads\"\n       directive.\n       Thanks to Mindaugas Rasiukevicius.\n\n\nChanges with nginx 1.9.14                                        05 Apr 2016\n\n    *) Feature: OpenSSL 1.1.0 compatibility.\n\n    *) Feature: the \"proxy_request_buffering\", \"fastcgi_request_buffering\",\n       \"scgi_request_buffering\", and \"uwsgi_request_buffering\" directives\n       now work with HTTP/2.\n\n    *) Bugfix: \"zero size buf in output\" alerts might appear in logs when\n       using HTTP/2.\n\n    *) Bugfix: the \"client_max_body_size\" directive might work incorrectly\n       when using HTTP/2.\n\n    *) Bugfix: of minor bugs in logging.\n\n\nChanges with nginx 1.9.13                                        29 Mar 2016\n\n    *) Change: non-idempotent requests (POST, LOCK, PATCH) are no longer\n       passed to the next server by default if a request has been sent to a\n       backend; the \"non_idempotent\" parameter of the \"proxy_next_upstream\"\n       directive explicitly allows retrying such requests.\n\n    *) Feature: the ngx_http_perl_module can be built dynamically.\n\n    *) Feature: UDP support in the stream module.\n\n    *) Feature: the \"aio_write\" directive.\n\n    *) Feature: now cache manager monitors number of elements in caches and\n       tries to avoid cache keys zone overflows.\n\n    *) Bugfix: \"task already active\" and \"second aio post\" alerts might\n       appear in logs when using the \"sendfile\" and \"aio\" directives with\n       subrequests.\n\n    *) Bugfix: \"zero size buf in output\" alerts might appear in logs if\n       caching was used and a client closed a connection prematurely.\n\n    *) Bugfix: connections with clients might be closed needlessly if\n       caching was used.\n       Thanks to Justin Li.\n\n    *) Bugfix: nginx might hog CPU if the \"sendfile\" directive was used on\n       Linux or Solaris and a file being sent was changed during sending.\n\n    *) Bugfix: connections might hang when using the \"sendfile\" and \"aio\n       threads\" directives.\n\n    *) Bugfix: in the \"proxy_pass\", \"fastcgi_pass\", \"scgi_pass\", and\n       \"uwsgi_pass\" directives when using variables.\n       Thanks to Piotr Sikora.\n\n    *) Bugfix: in the ngx_http_sub_filter_module.\n\n    *) Bugfix: if an error occurred in a cached backend connection, the\n       request was passed to the next server regardless of the\n       proxy_next_upstream directive.\n\n    *) Bugfix: \"CreateFile() failed\" errors when creating temporary files on\n       Windows.\n\n\nChanges with nginx 1.9.12                                        24 Feb 2016\n\n    *) Feature: Huffman encoding of response headers in HTTP/2.\n       Thanks to Vlad Krasnov.\n\n    *) Feature: the \"worker_cpu_affinity\" directive now supports more than\n       64 CPUs.\n\n    *) Bugfix: compatibility with 3rd party C++ modules; the bug had\n       appeared in 1.9.11.\n       Thanks to Piotr Sikora.\n\n    *) Bugfix: nginx could not be built statically with OpenSSL on Linux;\n       the bug had appeared in 1.9.11.\n\n    *) Bugfix: the \"add_header ... always\" directive with an empty value did\n       not delete \"Last-Modified\" and \"ETag\" header lines from error\n       responses.\n\n    *) Workaround: \"called a function you should not call\" and \"shutdown\n       while in init\" messages might appear in logs when using OpenSSL\n       1.0.2f.\n\n    *) Bugfix: invalid headers might be logged incorrectly.\n\n    *) Bugfix: socket leak when using HTTP/2.\n\n    *) Bugfix: in the ngx_http_v2_module.\n\n\nChanges with nginx 1.9.11                                        09 Feb 2016\n\n    *) Feature: TCP support in resolver.\n\n    *) Feature: dynamic modules.\n\n    *) Bugfix: the $request_length variable did not include size of request\n       headers when using HTTP/2.\n\n    *) Bugfix: in the ngx_http_v2_module.\n\n\nChanges with nginx 1.9.10                                        26 Jan 2016\n\n    *) Security: invalid pointer dereference might occur during DNS server\n       response processing if the \"resolver\" directive was used, allowing an\n       attacker who is able to forge UDP packets from the DNS server to\n       cause segmentation fault in a worker process (CVE-2016-0742).\n\n    *) Security: use-after-free condition might occur during CNAME response\n       processing if the \"resolver\" directive was used, allowing an attacker\n       who is able to trigger name resolution to cause segmentation fault in\n       a worker process, or might have potential other impact\n       (CVE-2016-0746).\n\n    *) Security: CNAME resolution was insufficiently limited if the\n       \"resolver\" directive was used, allowing an attacker who is able to\n       trigger arbitrary name resolution to cause excessive resource\n       consumption in worker processes (CVE-2016-0747).\n\n    *) Feature: the \"auto\" parameter of the \"worker_cpu_affinity\" directive.\n\n    *) Bugfix: the \"proxy_protocol\" parameter of the \"listen\" directive did\n       not work with IPv6 listen sockets.\n\n    *) Bugfix: connections to upstream servers might be cached incorrectly\n       when using the \"keepalive\" directive.\n\n    *) Bugfix: proxying used the HTTP method of the original request after\n       an \"X-Accel-Redirect\" redirection.\n\n\nChanges with nginx 1.9.9                                         09 Dec 2015\n\n    *) Bugfix: proxying to unix domain sockets did not work when using\n       variables; the bug had appeared in 1.9.8.\n\n\nChanges with nginx 1.9.8                                         08 Dec 2015\n\n    *) Feature: pwritev() support.\n\n    *) Feature: the \"include\" directive inside the \"upstream\" block.\n\n    *) Feature: the ngx_http_slice_module.\n\n    *) Bugfix: a segmentation fault might occur in a worker process when\n       using LibreSSL; the bug had appeared in 1.9.6.\n\n    *) Bugfix: nginx could not be built on OS X in some cases.\n\n\nChanges with nginx 1.9.7                                         17 Nov 2015\n\n    *) Feature: the \"nohostname\" parameter of logging to syslog.\n\n    *) Feature: the \"proxy_cache_convert_head\" directive.\n\n    *) Feature: the $realip_remote_addr variable in the\n       ngx_http_realip_module.\n\n    *) Bugfix: the \"expires\" directive might not work when using variables.\n\n    *) Bugfix: a segmentation fault might occur in a worker process when\n       using HTTP/2; the bug had appeared in 1.9.6.\n\n    *) Bugfix: if nginx was built with the ngx_http_v2_module it was\n       possible to use the HTTP/2 protocol even if the \"http2\" parameter of\n       the \"listen\" directive was not specified.\n\n    *) Bugfix: in the ngx_http_v2_module.\n\n\nChanges with nginx 1.9.6                                         27 Oct 2015\n\n    *) Bugfix: a segmentation fault might occur in a worker process when\n       using HTTP/2.\n       Thanks to Piotr Sikora and Denis Andzakovic.\n\n    *) Bugfix: the $server_protocol variable was empty when using HTTP/2.\n\n    *) Bugfix: backend SSL connections in the stream module might be timed\n       out unexpectedly.\n\n    *) Bugfix: a segmentation fault might occur in a worker process if\n       different ssl_session_cache settings were used in different virtual\n       servers.\n\n    *) Bugfix: nginx/Windows could not be built with MinGW gcc; the bug had\n       appeared in 1.9.4.\n       Thanks to Kouhei Sutou.\n\n    *) Bugfix: time was not updated when the timer_resolution directive was\n       used on Windows.\n\n    *) Miscellaneous minor fixes and improvements.\n       Thanks to Markus Linnala, Kurtis Nusbaum and Piotr Sikora.\n\n\nChanges with nginx 1.9.5                                         22 Sep 2015\n\n    *) Feature: the ngx_http_v2_module (replaces ngx_http_spdy_module).\n       Thanks to Dropbox and Automattic for sponsoring this work.\n\n    *) Change: now the \"output_buffers\" directive uses two buffers by\n       default.\n\n    *) Change: now nginx limits subrequests recursion, not simultaneous\n       subrequests.\n\n    *) Change: now nginx checks the whole cache key when returning a\n       response from cache.\n       Thanks to Gena Makhomed and Sergey Brester.\n\n    *) Bugfix: \"header already sent\" alerts might appear in logs when using\n       cache; the bug had appeared in 1.7.5.\n\n    *) Bugfix: \"writev() failed (4: Interrupted system call)\" errors might\n       appear in logs when using CephFS and the \"timer_resolution\" directive\n       on Linux.\n\n    *) Bugfix: in invalid configurations handling.\n       Thanks to Markus Linnala.\n\n    *) Bugfix: a segmentation fault occurred in a worker process if the\n       \"sub_filter\" directive was used at http level; the bug had appeared\n       in 1.9.4.\n\n\nChanges with nginx 1.9.4                                         18 Aug 2015\n\n    *) Change: the \"proxy_downstream_buffer\" and \"proxy_upstream_buffer\"\n       directives of the stream module are replaced with the\n       \"proxy_buffer_size\" directive.\n\n    *) Feature: the \"tcp_nodelay\" directive in the stream module.\n\n    *) Feature: multiple \"sub_filter\" directives can be used simultaneously.\n\n    *) Feature: variables support in the search string of the \"sub_filter\"\n       directive.\n\n    *) Workaround: configuration testing might fail under Linux OpenVZ.\n       Thanks to Gena Makhomed.\n\n    *) Bugfix: old worker processes might hog CPU after reconfiguration with\n       a large number of worker_connections.\n\n    *) Bugfix: a segmentation fault might occur in a worker process if the\n       \"try_files\" and \"alias\" directives were used inside a location given\n       by a regular expression; the bug had appeared in 1.7.1.\n\n    *) Bugfix: the \"try_files\" directive inside a nested location given by a\n       regular expression worked incorrectly if the \"alias\" directive was\n       used in the outer location.\n\n    *) Bugfix: in hash table initialization error handling.\n\n    *) Bugfix: nginx could not be built with Visual Studio 2015.\n\n\nChanges with nginx 1.9.3                                         14 Jul 2015\n\n    *) Change: duplicate \"http\", \"mail\", and \"stream\" blocks are now\n       disallowed.\n\n    *) Feature: connection limiting in the stream module.\n\n    *) Feature: data rate limiting in the stream module.\n\n    *) Bugfix: the \"zone\" directive inside the \"upstream\" block did not work\n       on Windows.\n\n    *) Bugfix: compatibility with LibreSSL in the stream module.\n       Thanks to Piotr Sikora.\n\n    *) Bugfix: in the \"--builddir\" configure parameter.\n       Thanks to Piotr Sikora.\n\n    *) Bugfix: the \"ssl_stapling_file\" directive did not work; the bug had\n       appeared in 1.9.2.\n       Thanks to Faidon Liambotis and Brandon Black.\n\n    *) Bugfix: a segmentation fault might occur in a worker process if the\n       \"ssl_stapling\" directive was used; the bug had appeared in 1.9.2.\n       Thanks to Matthew Baldwin.\n\n\nChanges with nginx 1.9.2                                         16 Jun 2015\n\n    *) Feature: the \"backlog\" parameter of the \"listen\" directives of the\n       mail proxy and stream modules.\n\n    *) Feature: the \"allow\" and \"deny\" directives in the stream module.\n\n    *) Feature: the \"proxy_bind\" directive in the stream module.\n\n    *) Feature: the \"proxy_protocol\" directive in the stream module.\n\n    *) Feature: the -T switch.\n\n    *) Feature: the REQUEST_SCHEME parameter added to the fastcgi.conf,\n       fastcgi_params, scgi_params, and uwsgi_params standard configuration\n       files.\n\n    *) Bugfix: the \"reuseport\" parameter of the \"listen\" directive of the\n       stream module did not work.\n\n    *) Bugfix: OCSP stapling might return an expired OCSP response in some\n       cases.\n\n\nChanges with nginx 1.9.1                                         26 May 2015\n\n    *) Change: now SSLv3 protocol is disabled by default.\n\n    *) Change: some long deprecated directives are not supported anymore.\n\n    *) Feature: the \"reuseport\" parameter of the \"listen\" directive.\n       Thanks to Yingqi Lu at Intel and Sepherosa Ziehau.\n\n    *) Feature: the $upstream_connect_time variable.\n\n    *) Bugfix: in the \"hash\" directive on big-endian platforms.\n\n    *) Bugfix: nginx might fail to start on some old Linux variants; the bug\n       had appeared in 1.7.11.\n\n    *) Bugfix: in IP address parsing.\n       Thanks to Sergey Polovko.\n\n\nChanges with nginx 1.9.0                                         28 Apr 2015\n\n    *) Change: obsolete aio and rtsig event methods have been removed.\n\n    *) Feature: the \"zone\" directive inside the \"upstream\" block.\n\n    *) Feature: the stream module.\n\n    *) Feature: byte ranges support in the ngx_http_memcached_module.\n       Thanks to Martin Mlynář.\n\n    *) Feature: shared memory can now be used on Windows versions with\n       address space layout randomization.\n       Thanks to Sergey Brester.\n\n    *) Feature: the \"error_log\" directive can now be used on mail and server\n       levels in mail proxy.\n\n    *) Bugfix: the \"proxy_protocol\" parameter of the \"listen\" directive did\n       not work if not specified in the first \"listen\" directive for a\n       listen socket.\n\n\nChanges with nginx 1.7.12                                        07 Apr 2015\n\n    *) Feature: now the \"tcp_nodelay\" directive works with backend SSL\n       connections.\n\n    *) Feature: now thread pools can be used to read cache file headers.\n\n    *) Bugfix: in the \"proxy_request_buffering\" directive.\n\n    *) Bugfix: a segmentation fault might occur in a worker process when\n       using thread pools on Linux.\n\n    *) Bugfix: in error handling when using the \"ssl_stapling\" directive.\n       Thanks to Filipe da Silva.\n\n    *) Bugfix: in the ngx_http_spdy_module.\n\n\nChanges with nginx 1.7.11                                        24 Mar 2015\n\n    *) Change: the \"sendfile\" parameter of the \"aio\" directive is\n       deprecated; now nginx automatically uses AIO to pre-load data for\n       sendfile if both \"aio\" and \"sendfile\" directives are used.\n\n    *) Feature: experimental thread pools support.\n\n    *) Feature: the \"proxy_request_buffering\", \"fastcgi_request_buffering\",\n       \"scgi_request_buffering\", and \"uwsgi_request_buffering\" directives.\n\n    *) Feature: request body filters experimental API.\n\n    *) Feature: client SSL certificates support in mail proxy.\n       Thanks to Sven Peter, Franck Levionnois, and Filipe Da Silva.\n\n    *) Feature: startup speedup when using the \"hash ... consistent\"\n       directive in the upstream block.\n       Thanks to Wai Keen Woon.\n\n    *) Feature: debug logging into a cyclic memory buffer.\n\n    *) Bugfix: in hash table handling.\n       Thanks to Chris West.\n\n    *) Bugfix: in the \"proxy_cache_revalidate\" directive.\n\n    *) Bugfix: SSL connections might hang if deferred accept or the\n       \"proxy_protocol\" parameter of the \"listen\" directive were used.\n       Thanks to James Hamlin.\n\n    *) Bugfix: the $upstream_response_time variable might contain a wrong\n       value if the \"image_filter\" directive was used.\n\n    *) Bugfix: in integer overflow handling.\n       Thanks to Régis Leroy.\n\n    *) Bugfix: it was not possible to enable SSLv3 with LibreSSL.\n\n    *) Bugfix: the \"ignoring stale global SSL error ... called a function\n       you should not call\" alerts appeared in logs when using LibreSSL.\n\n    *) Bugfix: certificates specified by the \"ssl_client_certificate\" and\n       \"ssl_trusted_certificate\" directives were inadvertently used to\n       automatically construct certificate chains.\n\n\nChanges with nginx 1.7.10                                        10 Feb 2015\n\n    *) Feature: the \"use_temp_path\" parameter of the \"proxy_cache_path\",\n       \"fastcgi_cache_path\", \"scgi_cache_path\", and \"uwsgi_cache_path\"\n       directives.\n\n    *) Feature: the $upstream_header_time variable.\n\n    *) Workaround: now on disk overflow nginx tries to write error logs once\n       a second only.\n\n    *) Bugfix: the \"try_files\" directive did not ignore normal files while\n       testing directories.\n       Thanks to Damien Tournoud.\n\n    *) Bugfix: alerts \"sendfile() failed\" if the \"sendfile\" directive was\n       used on OS X; the bug had appeared in 1.7.8.\n\n    *) Bugfix: alerts \"sem_post() failed\" might appear in logs.\n\n    *) Bugfix: nginx could not be built with musl libc.\n       Thanks to James Taylor.\n\n    *) Bugfix: nginx could not be built on Tru64 UNIX.\n       Thanks to Goetz T. Fischer.\n\n\nChanges with nginx 1.7.9                                         23 Dec 2014\n\n    *) Feature: variables support in the \"proxy_cache\", \"fastcgi_cache\",\n       \"scgi_cache\", and \"uwsgi_cache\" directives.\n\n    *) Feature: variables support in the \"expires\" directive.\n\n    *) Feature: loading of secret keys from hardware tokens with OpenSSL\n       engines.\n       Thanks to Dmitrii Pichulin.\n\n    *) Feature: the \"autoindex_format\" directive.\n\n    *) Bugfix: cache revalidation is now only used for responses with 200\n       and 206 status codes.\n       Thanks to Piotr Sikora.\n\n    *) Bugfix: the \"TE\" client request header line was passed to backends\n       while proxying.\n\n    *) Bugfix: the \"proxy_pass\", \"fastcgi_pass\", \"scgi_pass\", and\n       \"uwsgi_pass\" directives might not work correctly inside the \"if\" and\n       \"limit_except\" blocks.\n\n    *) Bugfix: the \"proxy_store\" directive with the \"on\" parameter was\n       ignored if the \"proxy_store\" directive with an explicitly specified\n       file path was used on a previous level.\n\n    *) Bugfix: nginx could not be built with BoringSSL.\n       Thanks to Lukas Tribus.\n\n\nChanges with nginx 1.7.8                                         02 Dec 2014\n\n    *) Change: now the \"If-Modified-Since\", \"If-Range\", etc. client request\n       header lines are passed to a backend while caching if nginx knows in\n       advance that the response will not be cached (e.g., when using\n       proxy_cache_min_uses).\n\n    *) Change: now after proxy_cache_lock_timeout nginx sends a request to a\n       backend with caching disabled; the new directives\n       \"proxy_cache_lock_age\", \"fastcgi_cache_lock_age\",\n       \"scgi_cache_lock_age\", and \"uwsgi_cache_lock_age\" specify a time\n       after which the lock will be released and another attempt to cache a\n       response will be made.\n\n    *) Change: the \"log_format\" directive can now be used only at http\n       level.\n\n    *) Feature: the \"proxy_ssl_certificate\", \"proxy_ssl_certificate_key\",\n       \"proxy_ssl_password_file\", \"uwsgi_ssl_certificate\",\n       \"uwsgi_ssl_certificate_key\", and \"uwsgi_ssl_password_file\"\n       directives.\n       Thanks to Piotr Sikora.\n\n    *) Feature: it is now possible to switch to a named location using\n       \"X-Accel-Redirect\".\n       Thanks to Toshikuni Fukaya.\n\n    *) Feature: now the \"tcp_nodelay\" directive works with SPDY connections.\n\n    *) Feature: new directives in vim syntax highliting scripts.\n       Thanks to Peter Wu.\n\n    *) Bugfix: nginx ignored the \"s-maxage\" value in the \"Cache-Control\"\n       backend response header line.\n       Thanks to Piotr Sikora.\n\n    *) Bugfix: in the ngx_http_spdy_module.\n       Thanks to Piotr Sikora.\n\n    *) Bugfix: in the \"ssl_password_file\" directive when using OpenSSL\n       0.9.8zc, 1.0.0o, 1.0.1j.\n\n    *) Bugfix: alerts \"header already sent\" appeared in logs if the\n       \"post_action\" directive was used; the bug had appeared in 1.5.4.\n\n    *) Bugfix: alerts \"the http output chain is empty\" might appear in logs\n       if the \"postpone_output 0\" directive was used with SSI includes.\n\n    *) Bugfix: in the \"proxy_cache_lock\" directive with SSI subrequests.\n       Thanks to Yichun Zhang.\n\n\nChanges with nginx 1.7.7                                         28 Oct 2014\n\n    *) Change: now nginx takes into account the \"Vary\" header line in a\n       backend response while caching.\n\n    *) Feature: the \"proxy_force_ranges\", \"fastcgi_force_ranges\",\n       \"scgi_force_ranges\", and \"uwsgi_force_ranges\" directives.\n\n    *) Feature: the \"proxy_limit_rate\", \"fastcgi_limit_rate\",\n       \"scgi_limit_rate\", and \"uwsgi_limit_rate\" directives.\n\n    *) Feature: the \"Vary\" parameter of the \"proxy_ignore_headers\",\n       \"fastcgi_ignore_headers\", \"scgi_ignore_headers\", and\n       \"uwsgi_ignore_headers\" directives.\n\n    *) Bugfix: the last part of a response received from a backend with\n       unbufferred proxy might not be sent to a client if \"gzip\" or \"gunzip\"\n       directives were used.\n\n    *) Bugfix: in the \"proxy_cache_revalidate\" directive.\n       Thanks to Piotr Sikora.\n\n    *) Bugfix: in error handling.\n       Thanks to Yichun Zhang and Daniil Bondarev.\n\n    *) Bugfix: in the \"proxy_next_upstream_tries\" and\n       \"proxy_next_upstream_timeout\" directives.\n       Thanks to Feng Gu.\n\n    *) Bugfix: nginx/Windows could not be built with MinGW-w64 gcc.\n       Thanks to Kouhei Sutou.\n\n\nChanges with nginx 1.7.6                                         30 Sep 2014\n\n    *) Change: the deprecated \"limit_zone\" directive is not supported\n       anymore.\n\n    *) Feature: the \"limit_conn_zone\" and \"limit_req_zone\" directives now\n       can be used with combinations of multiple variables.\n\n    *) Bugfix: request body might be transmitted incorrectly when retrying a\n       FastCGI request to the next upstream server.\n\n    *) Bugfix: in logging to syslog.\n\n\nChanges with nginx 1.7.5                                         16 Sep 2014\n\n    *) Security: it was possible to reuse SSL sessions in unrelated contexts\n       if a shared SSL session cache or the same TLS session ticket key was\n       used for multiple \"server\" blocks (CVE-2014-3616).\n       Thanks to Antoine Delignat-Lavaud.\n\n    *) Change: now the \"stub_status\" directive does not require a parameter.\n\n    *) Feature: the \"always\" parameter of the \"add_header\" directive.\n\n    *) Feature: the \"proxy_next_upstream_tries\",\n       \"proxy_next_upstream_timeout\", \"fastcgi_next_upstream_tries\",\n       \"fastcgi_next_upstream_timeout\", \"memcached_next_upstream_tries\",\n       \"memcached_next_upstream_timeout\", \"scgi_next_upstream_tries\",\n       \"scgi_next_upstream_timeout\", \"uwsgi_next_upstream_tries\", and\n       \"uwsgi_next_upstream_timeout\" directives.\n\n    *) Bugfix: in the \"if\" parameter of the \"access_log\" directive.\n\n    *) Bugfix: in the ngx_http_perl_module.\n       Thanks to Piotr Sikora.\n\n    *) Bugfix: the \"listen\" directive of the mail proxy module did not allow\n       to specify more than two parameters.\n\n    *) Bugfix: the \"sub_filter\" directive did not work with a string to\n       replace consisting of a single character.\n\n    *) Bugfix: requests might hang if resolver was used and a timeout\n       occurred during a DNS request.\n\n    *) Bugfix: in the ngx_http_spdy_module when using with AIO.\n\n    *) Bugfix: a segmentation fault might occur in a worker process if the\n       \"set\" directive was used to change the \"$http_...\", \"$sent_http_...\",\n       or \"$upstream_http_...\" variables.\n\n    *) Bugfix: in memory allocation error handling.\n       Thanks to Markus Linnala and Feng Gu.\n\n\nChanges with nginx 1.7.4                                         05 Aug 2014\n\n    *) Security: pipelined commands were not discarded after STARTTLS\n       command in SMTP proxy (CVE-2014-3556); the bug had appeared in 1.5.6.\n       Thanks to Chris Boulton.\n\n    *) Change: URI escaping now uses uppercase hexadecimal digits.\n       Thanks to Piotr Sikora.\n\n    *) Feature: now nginx can be build with BoringSSL and LibreSSL.\n       Thanks to Piotr Sikora.\n\n    *) Bugfix: requests might hang if resolver was used and a DNS server\n       returned a malformed response; the bug had appeared in 1.5.8.\n\n    *) Bugfix: in the ngx_http_spdy_module.\n       Thanks to Piotr Sikora.\n\n    *) Bugfix: the $uri variable might contain garbage when returning errors\n       with code 400.\n       Thanks to Sergey Bobrov.\n\n    *) Bugfix: in error handling in the \"proxy_store\" directive and the\n       ngx_http_dav_module.\n       Thanks to Feng Gu.\n\n    *) Bugfix: a segmentation fault might occur if logging of errors to\n       syslog was used; the bug had appeared in 1.7.1.\n\n    *) Bugfix: the $geoip_latitude, $geoip_longitude, $geoip_dma_code, and\n       $geoip_area_code variables might not work.\n       Thanks to Yichun Zhang.\n\n    *) Bugfix: in memory allocation error handling.\n       Thanks to Tatsuhiko Kubo and Piotr Sikora.\n\n\nChanges with nginx 1.7.3                                         08 Jul 2014\n\n    *) Feature: weak entity tags are now preserved on response\n       modifications, and strong ones are changed to weak.\n\n    *) Feature: cache revalidation now uses If-None-Match header if\n       possible.\n\n    *) Feature: the \"ssl_password_file\" directive.\n\n    *) Bugfix: the If-None-Match request header line was ignored if there\n       was no Last-Modified header in a response returned from cache.\n\n    *) Bugfix: \"peer closed connection in SSL handshake\" messages were\n       logged at \"info\" level instead of \"error\" while connecting to\n       backends.\n\n    *) Bugfix: in the ngx_http_dav_module module in nginx/Windows.\n\n    *) Bugfix: SPDY connections might be closed prematurely if caching was\n       used.\n\n\nChanges with nginx 1.7.2                                         17 Jun 2014\n\n    *) Feature: the \"hash\" directive inside the \"upstream\" block.\n\n    *) Feature: defragmentation of free shared memory blocks.\n       Thanks to Wandenberg Peixoto and Yichun Zhang.\n\n    *) Bugfix: a segmentation fault might occur in a worker process if the\n       default value of the \"access_log\" directive was used; the bug had\n       appeared in 1.7.0.\n       Thanks to Piotr Sikora.\n\n    *) Bugfix: trailing slash was mistakenly removed from the last parameter\n       of the \"try_files\" directive.\n\n    *) Bugfix: nginx could not be built on OS X in some cases.\n\n    *) Bugfix: in the ngx_http_spdy_module.\n\n\nChanges with nginx 1.7.1                                         27 May 2014\n\n    *) Feature: the \"$upstream_cookie_...\" variables.\n\n    *) Feature: the $ssl_client_fingerprint variable.\n\n    *) Feature: the \"error_log\" and \"access_log\" directives now support\n       logging to syslog.\n\n    *) Feature: the mail proxy now logs client port on connect.\n\n    *) Bugfix: memory leak if the \"ssl_stapling\" directive was used.\n       Thanks to Filipe da Silva.\n\n    *) Bugfix: the \"alias\" directive used inside a location given by a\n       regular expression worked incorrectly if the \"if\" or \"limit_except\"\n       directives were used.\n\n    *) Bugfix: the \"charset\" directive did not set a charset to encoded\n       backend responses.\n\n    *) Bugfix: a \"proxy_pass\" directive without URI part might use original\n       request after the $args variable was set.\n       Thanks to Yichun Zhang.\n\n    *) Bugfix: in the \"none\" parameter in the \"smtp_auth\" directive; the bug\n       had appeared in 1.5.6.\n       Thanks to Svyatoslav Nikolsky.\n\n    *) Bugfix: if sub_filter and SSI were used together, then responses\n       might be transferred incorrectly.\n\n    *) Bugfix: nginx could not be built with the --with-file-aio option on\n       Linux/aarch64.\n\n\nChanges with nginx 1.7.0                                         24 Apr 2014\n\n    *) Feature: backend SSL certificate verification.\n\n    *) Feature: support for SNI while working with SSL backends.\n\n    *) Feature: the $ssl_server_name variable.\n\n    *) Feature: the \"if\" parameter of the \"access_log\" directive.\n\n\nChanges with nginx 1.5.13                                        08 Apr 2014\n\n    *) Change: improved hash table handling; the default values of the\n       \"variables_hash_max_size\" and \"types_hash_bucket_size\" were changed\n       to 1024 and 64 respectively.\n\n    *) Feature: the ngx_http_mp4_module now supports the \"end\" argument.\n\n    *) Feature: byte ranges support in the ngx_http_mp4_module and while\n       saving responses to cache.\n\n    *) Bugfix: alerts \"ngx_slab_alloc() failed: no memory\" no longer logged\n       when using shared memory in the \"ssl_session_cache\" directive and in\n       the ngx_http_limit_req_module.\n\n    *) Bugfix: the \"underscores_in_headers\" directive did not allow\n       underscore as a first character of a header.\n       Thanks to Piotr Sikora.\n\n    *) Bugfix: cache manager might hog CPU on exit in nginx/Windows.\n\n    *) Bugfix: nginx/Windows terminated abnormally if the\n       \"ssl_session_cache\" directive was used with the \"shared\" parameter.\n\n    *) Bugfix: in the ngx_http_spdy_module.\n\n\nChanges with nginx 1.5.12                                        18 Mar 2014\n\n    *) Security: a heap memory buffer overflow might occur in a worker\n       process while handling a specially crafted request by\n       ngx_http_spdy_module, potentially resulting in arbitrary code\n       execution (CVE-2014-0133).\n       Thanks to Lucas Molas, researcher at Programa STIC, Fundación Dr.\n       Manuel Sadosky, Buenos Aires, Argentina.\n\n    *) Feature: the \"proxy_protocol\" parameters of the \"listen\" and\n       \"real_ip_header\" directives, the $proxy_protocol_addr variable.\n\n    *) Bugfix: in the \"fastcgi_next_upstream\" directive.\n       Thanks to Lucas Molas.\n\n\nChanges with nginx 1.5.11                                        04 Mar 2014\n\n    *) Security: memory corruption might occur in a worker process on 32-bit\n       platforms while handling a specially crafted request by\n       ngx_http_spdy_module, potentially resulting in arbitrary code\n       execution (CVE-2014-0088); the bug had appeared in 1.5.10.\n       Thanks to Lucas Molas, researcher at Programa STIC, Fundación Dr.\n       Manuel Sadosky, Buenos Aires, Argentina.\n\n    *) Feature: the $ssl_session_reused variable.\n\n    *) Bugfix: the \"client_max_body_size\" directive might not work when\n       reading a request body using chunked transfer encoding; the bug had\n       appeared in 1.3.9.\n       Thanks to Lucas Molas.\n\n    *) Bugfix: a segmentation fault might occur in a worker process when\n       proxying WebSocket connections.\n\n    *) Bugfix: a segmentation fault might occur in a worker process if the\n       ngx_http_spdy_module was used on 32-bit platforms; the bug had\n       appeared in 1.5.10.\n\n    *) Bugfix: the $upstream_status variable might contain wrong data if the\n       \"proxy_cache_use_stale\" or \"proxy_cache_revalidate\" directives were\n       used.\n       Thanks to Piotr Sikora.\n\n    *) Bugfix: a segmentation fault might occur in a worker process if\n       errors with code 400 were redirected to a named location using the\n       \"error_page\" directive.\n\n    *) Bugfix: nginx/Windows could not be built with Visual Studio 2013.\n\n\nChanges with nginx 1.5.10                                        04 Feb 2014\n\n    *) Feature: the ngx_http_spdy_module now uses SPDY 3.1 protocol.\n       Thanks to Automattic and MaxCDN for sponsoring this work.\n\n    *) Feature: the ngx_http_mp4_module now skips tracks too short for a\n       seek requested.\n\n    *) Bugfix: a segmentation fault might occur in a worker process if the\n       $ssl_session_id variable was used in logs; the bug had appeared in\n       1.5.9.\n\n    *) Bugfix: the $date_local and $date_gmt variables used wrong format\n       outside of the ngx_http_ssi_filter_module.\n\n    *) Bugfix: client connections might be immediately closed if deferred\n       accept was used; the bug had appeared in 1.3.15.\n\n    *) Bugfix: alerts \"getsockopt(TCP_FASTOPEN) ... failed\" appeared in logs\n       during binary upgrade on Linux; the bug had appeared in 1.5.8.\n       Thanks to Piotr Sikora.\n\n\nChanges with nginx 1.5.9                                         22 Jan 2014\n\n    *) Change: now nginx expects escaped URIs in \"X-Accel-Redirect\" headers.\n\n    *) Feature: the \"ssl_buffer_size\" directive.\n\n    *) Feature: the \"limit_rate\" directive can now be used to rate limit\n       responses sent in SPDY connections.\n\n    *) Feature: the \"spdy_chunk_size\" directive.\n\n    *) Feature: the \"ssl_session_tickets\" directive.\n       Thanks to Dirkjan Bussink.\n\n    *) Bugfix: the $ssl_session_id variable contained full session\n       serialized instead of just a session id.\n       Thanks to Ivan Ristić.\n\n    *) Bugfix: nginx incorrectly handled escaped \"?\" character in the\n       \"include\" SSI command.\n\n    *) Bugfix: the ngx_http_dav_module did not unescape destination URI of\n       the COPY and MOVE methods.\n\n    *) Bugfix: resolver did not understand domain names with a trailing dot.\n       Thanks to Yichun Zhang.\n\n    *) Bugfix: alerts \"zero size buf in output\" might appear in logs while\n       proxying; the bug had appeared in 1.3.9.\n\n    *) Bugfix: a segmentation fault might occur in a worker process if the\n       ngx_http_spdy_module was used.\n\n    *) Bugfix: proxied WebSocket connections might hang right after\n       handshake if the select, poll, or /dev/poll methods were used.\n\n    *) Bugfix: the \"xclient\" directive of the mail proxy module incorrectly\n       handled IPv6 client addresses.\n\n\nChanges with nginx 1.5.8                                         17 Dec 2013\n\n    *) Feature: IPv6 support in resolver.\n\n    *) Feature: the \"listen\" directive supports the \"fastopen\" parameter.\n       Thanks to Mathew Rodley.\n\n    *) Feature: SSL support in the ngx_http_uwsgi_module.\n       Thanks to Roberto De Ioris.\n\n    *) Feature: vim syntax highlighting scripts were added to contrib.\n       Thanks to Evan Miller.\n\n    *) Bugfix: a timeout might occur while reading client request body in an\n       SSL connection using chunked transfer encoding.\n\n    *) Bugfix: the \"master_process\" directive did not work correctly in\n       nginx/Windows.\n\n    *) Bugfix: the \"setfib\" parameter of the \"listen\" directive might not\n       work.\n\n    *) Bugfix: in the ngx_http_spdy_module.\n\n\nChanges with nginx 1.5.7                                         19 Nov 2013\n\n    *) Security: a character following an unescaped space in a request line\n       was handled incorrectly (CVE-2013-4547); the bug had appeared in\n       0.8.41.\n       Thanks to Ivan Fratric of the Google Security Team.\n\n    *) Change: a logging level of auth_basic errors about no user/password\n       provided has been lowered from \"error\" to \"info\".\n\n    *) Feature: the \"proxy_cache_revalidate\", \"fastcgi_cache_revalidate\",\n       \"scgi_cache_revalidate\", and \"uwsgi_cache_revalidate\" directives.\n\n    *) Feature: the \"ssl_session_ticket_key\" directive.\n       Thanks to Piotr Sikora.\n\n    *) Bugfix: the directive \"add_header Cache-Control ''\" added a\n       \"Cache-Control\" response header line with an empty value.\n\n    *) Bugfix: the \"satisfy any\" directive might return 403 error instead of\n       401 if auth_request and auth_basic directives were used.\n       Thanks to Jan Marc Hoffmann.\n\n    *) Bugfix: the \"accept_filter\" and \"deferred\" parameters of the \"listen\"\n       directive were ignored for listen sockets created during binary\n       upgrade.\n       Thanks to Piotr Sikora.\n\n    *) Bugfix: some data received from a backend with unbufferred proxy\n       might not be sent to a client immediately if \"gzip\" or \"gunzip\"\n       directives were used.\n       Thanks to Yichun Zhang.\n\n    *) Bugfix: in error handling in ngx_http_gunzip_filter_module.\n\n    *) Bugfix: responses might hang if the ngx_http_spdy_module was used\n       with the \"auth_request\" directive.\n\n    *) Bugfix: memory leak in nginx/Windows.\n\n\nChanges with nginx 1.5.6                                         01 Oct 2013\n\n    *) Feature: the \"fastcgi_buffering\" directive.\n\n    *) Feature: the \"proxy_ssl_protocols\" and \"proxy_ssl_ciphers\"\n       directives.\n       Thanks to Piotr Sikora.\n\n    *) Feature: optimization of SSL handshakes when using long certificate\n       chains.\n\n    *) Feature: the mail proxy supports SMTP pipelining.\n\n    *) Bugfix: in the ngx_http_auth_basic_module when using \"$apr1$\"\n       password encryption method.\n       Thanks to Markus Linnala.\n\n    *) Bugfix: in MacOSX, Cygwin, and nginx/Windows incorrect location might\n       be used to process a request if locations were given using characters\n       in different cases.\n\n    *) Bugfix: automatic redirect with appended trailing slash for proxied\n       locations might not work.\n\n    *) Bugfix: in the mail proxy server.\n\n    *) Bugfix: in the ngx_http_spdy_module.\n\n\nChanges with nginx 1.5.5                                         17 Sep 2013\n\n    *) Change: now nginx assumes HTTP/1.0 by default if it is not able to\n       detect protocol reliably.\n\n    *) Feature: the \"disable_symlinks\" directive now uses O_PATH on Linux.\n\n    *) Feature: now nginx uses EPOLLRDHUP events to detect premature\n       connection close by clients if the \"epoll\" method is used.\n\n    *) Bugfix: in the \"valid_referers\" directive if the \"server_names\"\n       parameter was used.\n\n    *) Bugfix: the $request_time variable did not work in nginx/Windows.\n\n    *) Bugfix: in the \"image_filter\" directive.\n       Thanks to Lanshun Zhou.\n\n    *) Bugfix: OpenSSL 1.0.1f compatibility.\n       Thanks to Piotr Sikora.\n\n\nChanges with nginx 1.5.4                                         27 Aug 2013\n\n    *) Change: the \"js\" extension MIME type has been changed to\n       \"application/javascript\"; default value of the \"charset_types\"\n       directive was changed accordingly.\n\n    *) Change: now the \"image_filter\" directive with the \"size\" parameter\n       returns responses with the \"application/json\" MIME type.\n\n    *) Feature: the ngx_http_auth_request_module.\n\n    *) Bugfix: a segmentation fault might occur on start or during\n       reconfiguration if the \"try_files\" directive was used with an empty\n       parameter.\n\n    *) Bugfix: memory leak if relative paths were specified using variables\n       in the \"root\" or \"auth_basic_user_file\" directives.\n\n    *) Bugfix: the \"valid_referers\" directive incorrectly executed regular\n       expressions if a \"Referer\" header started with \"https://\".\n       Thanks to Liangbin Li.\n\n    *) Bugfix: responses might hang if subrequests were used and an SSL\n       handshake error happened during subrequest processing.\n       Thanks to Aviram Cohen.\n\n    *) Bugfix: in the ngx_http_autoindex_module.\n\n    *) Bugfix: in the ngx_http_spdy_module.\n\n\nChanges with nginx 1.5.3                                         30 Jul 2013\n\n    *) Change in internal API: now u->length defaults to -1 if working with\n       backends in unbuffered mode.\n\n    *) Change: now after receiving an incomplete response from a backend\n       server nginx tries to send an available part of the response to a\n       client, and then closes client connection.\n\n    *) Bugfix: a segmentation fault might occur in a worker process if the\n       ngx_http_spdy_module was used with the \"client_body_in_file_only\"\n       directive.\n\n    *) Bugfix: the \"so_keepalive\" parameter of the \"listen\" directive might\n       be handled incorrectly on DragonFlyBSD.\n       Thanks to Sepherosa Ziehau.\n\n    *) Bugfix: in the ngx_http_xslt_filter_module.\n\n    *) Bugfix: in the ngx_http_sub_filter_module.\n\n\nChanges with nginx 1.5.2                                         02 Jul 2013\n\n    *) Feature: now several \"error_log\" directives can be used.\n\n    *) Bugfix: the $r->header_in() embedded perl method did not return value\n       of the \"Cookie\" and \"X-Forwarded-For\" request header lines; the bug\n       had appeared in 1.3.14.\n\n    *) Bugfix: in the ngx_http_spdy_module.\n       Thanks to Jim Radford.\n\n    *) Bugfix: nginx could not be built on Linux with x32 ABI.\n       Thanks to Serguei Ivantsov.\n\n\nChanges with nginx 1.5.1                                         04 Jun 2013\n\n    *) Feature: the \"ssi_last_modified\", \"sub_filter_last_modified\", and\n       \"xslt_last_modified\" directives.\n       Thanks to Alexey Kolpakov.\n\n    *) Feature: the \"http_403\" parameter of the \"proxy_next_upstream\",\n       \"fastcgi_next_upstream\", \"scgi_next_upstream\", and\n       \"uwsgi_next_upstream\" directives.\n\n    *) Feature: the \"allow\" and \"deny\" directives now support unix domain\n       sockets.\n\n    *) Bugfix: nginx could not be built with the ngx_mail_ssl_module, but\n       without ngx_http_ssl_module; the bug had appeared in 1.3.14.\n\n    *) Bugfix: in the \"proxy_set_body\" directive.\n       Thanks to Lanshun Zhou.\n\n    *) Bugfix: in the \"lingering_time\" directive.\n       Thanks to Lanshun Zhou.\n\n    *) Bugfix: the \"fail_timeout\" parameter of the \"server\" directive in the\n       \"upstream\" context might not work if \"max_fails\" parameter was used;\n       the bug had appeared in 1.3.0.\n\n    *) Bugfix: a segmentation fault might occur in a worker process if the\n       \"ssl_stapling\" directive was used.\n       Thanks to Piotr Sikora.\n\n    *) Bugfix: in the mail proxy server.\n       Thanks to Filipe Da Silva.\n\n    *) Bugfix: nginx/Windows might stop accepting connections if several\n       worker processes were used.\n\n\nChanges with nginx 1.5.0                                         07 May 2013\n\n    *) Security: a stack-based buffer overflow might occur in a worker\n       process while handling a specially crafted request, potentially\n       resulting in arbitrary code execution (CVE-2013-2028); the bug had\n       appeared in 1.3.9.\n       Thanks to Greg MacManus, iSIGHT Partners Labs.\n\n\nChanges with nginx 1.4.0                                         24 Apr 2013\n\n    *) Bugfix: nginx could not be built with the ngx_http_perl_module if the\n       --with-openssl option was used; the bug had appeared in 1.3.16.\n\n    *) Bugfix: in a request body handling in the ngx_http_perl_module; the\n       bug had appeared in 1.3.9.\n\n\nChanges with nginx 1.3.16                                        16 Apr 2013\n\n    *) Bugfix: a segmentation fault might occur in a worker process if\n       subrequests were used; the bug had appeared in 1.3.9.\n\n    *) Bugfix: the \"tcp_nodelay\" directive caused an error if a WebSocket\n       connection was proxied into a unix domain socket.\n\n    *) Bugfix: the $upstream_response_length variable has an incorrect value\n       \"0\" if buffering was not used.\n       Thanks to Piotr Sikora.\n\n    *) Bugfix: in the eventport and /dev/poll methods.\n\n\nChanges with nginx 1.3.15                                        26 Mar 2013\n\n    *) Change: opening and closing a connection without sending any data in\n       it is no longer logged to access_log with error code 400.\n\n    *) Feature: the ngx_http_spdy_module.\n       Thanks to Automattic for sponsoring this work.\n\n    *) Feature: the \"limit_req_status\" and \"limit_conn_status\" directives.\n       Thanks to Nick Marden.\n\n    *) Feature: the \"image_filter_interlace\" directive.\n       Thanks to Ian Babrou.\n\n    *) Feature: $connections_waiting variable in the\n       ngx_http_stub_status_module.\n\n    *) Feature: the mail proxy module now supports IPv6 backends.\n\n    *) Bugfix: request body might be transmitted incorrectly when retrying a\n       request to the next upstream server; the bug had appeared in 1.3.9.\n       Thanks to Piotr Sikora.\n\n    *) Bugfix: in the \"client_body_in_file_only\" directive; the bug had\n       appeared in 1.3.9.\n\n    *) Bugfix: responses might hang if subrequests were used and a DNS error\n       happened during subrequest processing.\n       Thanks to Lanshun Zhou.\n\n    *) Bugfix: in backend usage accounting.\n\n\nChanges with nginx 1.3.14                                        05 Mar 2013\n\n    *) Feature: $connections_active, $connections_reading, and\n       $connections_writing variables in the ngx_http_stub_status_module.\n\n    *) Feature: support of WebSocket connections in the\n       ngx_http_uwsgi_module and ngx_http_scgi_module.\n\n    *) Bugfix: in virtual servers handling with SNI.\n\n    *) Bugfix: new sessions were not always stored if the \"ssl_session_cache\n       shared\" directive was used and there was no free space in shared\n       memory.\n       Thanks to Piotr Sikora.\n\n    *) Bugfix: multiple X-Forwarded-For headers were handled incorrectly.\n       Thanks to Neal Poole for sponsoring this work.\n\n    *) Bugfix: in the ngx_http_mp4_module.\n       Thanks to Gernot Vormayr.\n\n\nChanges with nginx 1.3.13                                        19 Feb 2013\n\n    *) Change: a compiler with name \"cc\" is now used by default.\n\n    *) Feature: support for proxying of WebSocket connections.\n       Thanks to Apcera and CloudBees for sponsoring this work.\n\n    *) Feature: the \"auth_basic_user_file\" directive supports \"{SHA}\"\n       password encryption method.\n       Thanks to Louis Opter.\n\n\nChanges with nginx 1.3.12                                        05 Feb 2013\n\n    *) Feature: variables support in the \"proxy_bind\", \"fastcgi_bind\",\n       \"memcached_bind\", \"scgi_bind\", and \"uwsgi_bind\" directives.\n\n    *) Feature: the $pipe, $request_length, $time_iso8601, and $time_local\n       variables can now be used not only in the \"log_format\" directive.\n       Thanks to Kiril Kalchev.\n\n    *) Feature: IPv6 support in the ngx_http_geoip_module.\n       Thanks to Gregor Kališnik.\n\n    *) Bugfix: in the \"proxy_method\" directive.\n\n    *) Bugfix: a segmentation fault might occur in a worker process if\n       resolver was used with the poll method.\n\n    *) Bugfix: nginx might hog CPU during SSL handshake with a backend if\n       the select, poll, or /dev/poll methods were used.\n\n    *) Bugfix: the \"[crit] SSL_write() failed (SSL:)\" error.\n\n    *) Bugfix: in the \"client_body_in_file_only\" directive; the bug had\n       appeared in 1.3.9.\n\n    *) Bugfix: in the \"fastcgi_keep_conn\" directive.\n\n\nChanges with nginx 1.3.11                                        10 Jan 2013\n\n    *) Bugfix: a segmentation fault might occur if logging was used; the bug\n       had appeared in 1.3.10.\n\n    *) Bugfix: the \"proxy_pass\" directive did not work with IP addresses\n       without port specified; the bug had appeared in 1.3.10.\n\n    *) Bugfix: a segmentation fault occurred on start or during\n       reconfiguration if the \"keepalive\" directive was specified more than\n       once in a single upstream block.\n\n    *) Bugfix: parameter \"default\" of the \"geo\" directive did not set\n       default value for IPv6 addresses.\n\n\nChanges with nginx 1.3.10                                        25 Dec 2012\n\n    *) Change: domain names specified in configuration file are now resolved\n       to IPv6 addresses as well as IPv4 ones.\n\n    *) Change: now if the \"include\" directive with mask is used on Unix\n       systems, included files are sorted in alphabetical order.\n\n    *) Change: the \"add_header\" directive adds headers to 201 responses.\n\n    *) Feature: the \"geo\" directive now supports IPv6 addresses in CIDR\n       notation.\n\n    *) Feature: the \"flush\" and \"gzip\" parameters of the \"access_log\"\n       directive.\n\n    *) Feature: variables support in the \"auth_basic\" directive.\n\n    *) Bugfix: nginx could not be built with the ngx_http_perl_module in\n       some cases.\n\n    *) Bugfix: a segmentation fault might occur in a worker process if the\n       ngx_http_xslt_module was used.\n\n    *) Bugfix: nginx could not be built on MacOSX in some cases.\n       Thanks to Piotr Sikora.\n\n    *) Bugfix: the \"limit_rate\" directive with high rates might result in\n       truncated responses on 32-bit platforms.\n       Thanks to Alexey Antropov.\n\n    *) Bugfix: a segmentation fault might occur in a worker process if the\n       \"if\" directive was used.\n       Thanks to Piotr Sikora.\n\n    *) Bugfix: a \"100 Continue\" response was issued with \"413 Request Entity\n       Too Large\" responses.\n\n    *) Bugfix: the \"image_filter\", \"image_filter_jpeg_quality\" and\n       \"image_filter_sharpen\" directives might be inherited incorrectly.\n       Thanks to Ian Babrou.\n\n    *) Bugfix: \"crypt_r() failed\" errors might appear if the \"auth_basic\"\n       directive was used on Linux.\n\n    *) Bugfix: in backup servers handling.\n       Thanks to Thomas Chen.\n\n    *) Bugfix: proxied HEAD requests might return incorrect response if the\n       \"gzip\" directive was used.\n\n\nChanges with nginx 1.3.9                                         27 Nov 2012\n\n    *) Feature: support for chunked transfer encoding while reading client\n       request body.\n\n    *) Feature: the $request_time and $msec variables can now be used not\n       only in the \"log_format\" directive.\n\n    *) Bugfix: cache manager and cache loader processes might not be able to\n       start if more than 512 listen sockets were used.\n\n    *) Bugfix: in the ngx_http_dav_module.\n\n\nChanges with nginx 1.3.8                                         30 Oct 2012\n\n    *) Feature: the \"optional_no_ca\" parameter of the \"ssl_verify_client\"\n       directive.\n       Thanks to Mike Kazantsev and Eric O'Connor.\n\n    *) Feature: the $bytes_sent, $connection, and $connection_requests\n       variables can now be used not only in the \"log_format\" directive.\n       Thanks to Benjamin Grössing.\n\n    *) Feature: the \"auto\" parameter of the \"worker_processes\" directive.\n\n    *) Bugfix: \"cache file ... has md5 collision\" alert.\n\n    *) Bugfix: in the ngx_http_gunzip_filter_module.\n\n    *) Bugfix: in the \"ssl_stapling\" directive.\n\n\nChanges with nginx 1.3.7                                         02 Oct 2012\n\n    *) Feature: OCSP stapling support.\n       Thanks to Comodo, DigiCert and GlobalSign for sponsoring this work.\n\n    *) Feature: the \"ssl_trusted_certificate\" directive.\n\n    *) Feature: resolver now randomly rotates addresses returned from cache.\n       Thanks to Anton Jouline.\n\n    *) Bugfix: OpenSSL 0.9.7 compatibility.\n\n\nChanges with nginx 1.3.6                                         12 Sep 2012\n\n    *) Feature: the ngx_http_gunzip_filter_module.\n\n    *) Feature: the \"memcached_gzip_flag\" directive.\n\n    *) Feature: the \"always\" parameter of the \"gzip_static\" directive.\n\n    *) Bugfix: in the \"limit_req\" directive; the bug had appeared in 1.1.14.\n       Thanks to Charles Chen.\n\n    *) Bugfix: nginx could not be built by gcc 4.7 with -O2 optimization if\n       the --with-ipv6 option was used.\n\n\nChanges with nginx 1.3.5                                         21 Aug 2012\n\n    *) Change: the ngx_http_mp4_module module no longer skips tracks in\n       formats other than H.264 and AAC.\n\n    *) Bugfix: a segmentation fault might occur in a worker process if the\n       \"map\" directive was used with variables as values.\n\n    *) Bugfix: a segmentation fault might occur in a worker process if the\n       \"geo\" directive was used with the \"ranges\" parameter but without the\n       \"default\" parameter; the bug had appeared in 0.8.43.\n       Thanks to Zhen Chen and Weibin Yao.\n\n    *) Bugfix: in the -p command-line parameter handling.\n\n    *) Bugfix: in the mail proxy server.\n\n    *) Bugfix: of minor potential bugs.\n       Thanks to Coverity.\n\n    *) Bugfix: nginx/Windows could not be built with Visual Studio 2005\n       Express.\n       Thanks to HAYASHI Kentaro.\n\n\nChanges with nginx 1.3.4                                         31 Jul 2012\n\n    *) Change: the \"ipv6only\" parameter is now turned on by default for\n       listening IPv6 sockets.\n\n    *) Feature: the Clang compiler support.\n\n    *) Bugfix: extra listening sockets might be created.\n       Thanks to Roman Odaisky.\n\n    *) Bugfix: nginx/Windows might hog CPU if a worker process failed to\n       start.\n       Thanks to Ricardo Villalobos Guevara.\n\n    *) Bugfix: the \"proxy_pass_header\", \"fastcgi_pass_header\",\n       \"scgi_pass_header\", \"uwsgi_pass_header\", \"proxy_hide_header\",\n       \"fastcgi_hide_header\", \"scgi_hide_header\", and \"uwsgi_hide_header\"\n       directives might be inherited incorrectly.\n\n\nChanges with nginx 1.3.3                                         10 Jul 2012\n\n    *) Feature: entity tags support and the \"etag\" directive.\n\n    *) Bugfix: trailing dot in a source value was not ignored if the \"map\"\n       directive was used with the \"hostnames\" parameter.\n\n    *) Bugfix: incorrect location might be used to process a request if a\n       URI was changed via a \"rewrite\" directive before an internal redirect\n       to a named location.\n\n\nChanges with nginx 1.3.2                                         26 Jun 2012\n\n    *) Change: the \"single\" parameter of the \"keepalive\" directive is now\n       ignored.\n\n    *) Change: SSL compression is now disabled when using all versions of\n       OpenSSL, including ones prior to 1.0.0.\n\n    *) Feature: it is now possible to use the \"ip_hash\" directive to balance\n       IPv6 clients.\n\n    *) Feature: the $status variable can now be used not only in the\n       \"log_format\" directive.\n\n    *) Bugfix: a segmentation fault might occur in a worker process on\n       shutdown if the \"resolver\" directive was used.\n\n    *) Bugfix: a segmentation fault might occur in a worker process if the\n       ngx_http_mp4_module was used.\n\n    *) Bugfix: in the ngx_http_mp4_module.\n\n    *) Bugfix: a segmentation fault might occur in a worker process if\n       conflicting wildcard server names were used.\n\n    *) Bugfix: nginx might be terminated abnormally on a SIGBUS signal on\n       ARM platform.\n\n    *) Bugfix: an alert \"sendmsg() failed (9: Bad file number)\" on HP-UX\n       while reconfiguration.\n\n\nChanges with nginx 1.3.1                                         05 Jun 2012\n\n    *) Security: now nginx/Windows ignores trailing dot in URI path\n       component, and does not allow URIs with \":$\" in it.\n       Thanks to Vladimir Kochetkov, Positive Research Center.\n\n    *) Feature: the \"proxy_pass\", \"fastcgi_pass\", \"scgi_pass\", \"uwsgi_pass\"\n       directives, and the \"server\" directive inside the \"upstream\" block,\n       now support IPv6 addresses.\n\n    *) Feature: the \"resolver\" directive now supports IPv6 addresses and an\n       optional port specification.\n\n    *) Feature: the \"least_conn\" directive inside the \"upstream\" block.\n\n    *) Feature: it is now possible to specify a weight for servers while\n       using the \"ip_hash\" directive.\n\n    *) Bugfix: a segmentation fault might occur in a worker process if the\n       \"image_filter\" directive was used; the bug had appeared in 1.3.0.\n\n    *) Bugfix: nginx could not be built with ngx_cpp_test_module; the bug\n       had appeared in 1.1.12.\n\n    *) Bugfix: access to variables from SSI and embedded perl module might\n       not work after reconfiguration.\n       Thanks to Yichun Zhang.\n\n    *) Bugfix: in the ngx_http_xslt_filter_module.\n       Thanks to Kuramoto Eiji.\n\n    *) Bugfix: memory leak if $geoip_org variable was used.\n       Thanks to Denis F. Latypoff.\n\n    *) Bugfix: in the \"proxy_cookie_domain\" and \"proxy_cookie_path\"\n       directives.\n\n\nChanges with nginx 1.3.0                                         15 May 2012\n\n    *) Feature: the \"debug_connection\" directive now supports IPv6 addresses\n       and the \"unix:\" parameter.\n\n    *) Feature: the \"set_real_ip_from\" directive and the \"proxy\" parameter\n       of the \"geo\" directive now support IPv6 addresses.\n\n    *) Feature: the \"real_ip_recursive\", \"geoip_proxy\", and\n       \"geoip_proxy_recursive\" directives.\n\n    *) Feature: the \"proxy_recursive\" parameter of the \"geo\" directive.\n\n    *) Bugfix: a segmentation fault might occur in a worker process if the\n       \"resolver\" directive was used.\n\n    *) Bugfix: a segmentation fault might occur in a worker process if the\n       \"fastcgi_pass\", \"scgi_pass\", or \"uwsgi_pass\" directives were used and\n       backend returned incorrect response.\n\n    *) Bugfix: a segmentation fault might occur in a worker process if the\n       \"rewrite\" directive was used and new request arguments in a\n       replacement used variables.\n\n    *) Bugfix: nginx might hog CPU if the open file resource limit was\n       reached.\n\n    *) Bugfix: nginx might loop infinitely over backends if the\n       \"proxy_next_upstream\" directive with the \"http_404\" parameter was\n       used and there were backup servers specified in an upstream block.\n\n    *) Bugfix: adding the \"down\" parameter of the \"server\" directive might\n       cause unneeded client redistribution among backend servers if the\n       \"ip_hash\" directive was used.\n\n    *) Bugfix: socket leak.\n       Thanks to Yichun Zhang.\n\n    *) Bugfix: in the ngx_http_fastcgi_module.\n\n\nChanges with nginx 1.2.0                                         23 Apr 2012\n\n    *) Bugfix: a segmentation fault might occur in a worker process if the\n       \"try_files\" directive was used; the bug had appeared in 1.1.19.\n\n    *) Bugfix: response might be truncated if there were more than IOV_MAX\n       buffers used.\n\n    *) Bugfix: in the \"crop\" parameter of the \"image_filter\" directive.\n       Thanks to Maxim Bublis.\n\n\nChanges with nginx 1.1.19                                        12 Apr 2012\n\n    *) Security: specially crafted mp4 file might allow to overwrite memory\n       locations in a worker process if the ngx_http_mp4_module was used,\n       potentially resulting in arbitrary code execution (CVE-2012-2089).\n       Thanks to Matthew Daley.\n\n    *) Bugfix: nginx/Windows might be terminated abnormally.\n       Thanks to Vincent Lee.\n\n    *) Bugfix: nginx hogged CPU if all servers in an upstream were marked as\n       \"backup\".\n\n    *) Bugfix: the \"allow\" and \"deny\" directives might be inherited\n       incorrectly if they were used with IPv6 addresses.\n\n    *) Bugfix: the \"modern_browser\" and \"ancient_browser\" directives might\n       be inherited incorrectly.\n\n    *) Bugfix: timeouts might be handled incorrectly on Solaris/SPARC.\n\n    *) Bugfix: in the ngx_http_mp4_module.\n\n\nChanges with nginx 1.1.18                                        28 Mar 2012\n\n    *) Change: keepalive connections are no longer disabled for Safari by\n       default.\n\n    *) Feature: the $connection_requests variable.\n\n    *) Feature: $tcpinfo_rtt, $tcpinfo_rttvar, $tcpinfo_snd_cwnd and\n       $tcpinfo_rcv_space variables.\n\n    *) Feature: the \"worker_cpu_affinity\" directive now works on FreeBSD.\n\n    *) Feature: the \"xslt_param\" and \"xslt_string_param\" directives.\n       Thanks to Samuel Behan.\n\n    *) Bugfix: in configure tests.\n       Thanks to Piotr Sikora.\n\n    *) Bugfix: in the ngx_http_xslt_filter_module.\n\n    *) Bugfix: nginx could not be built on Debian GNU/Hurd.\n\n\nChanges with nginx 1.1.17                                        15 Mar 2012\n\n    *) Security: content of previously freed memory might be sent to a\n       client if backend returned specially crafted response.\n       Thanks to Matthew Daley.\n\n    *) Bugfix: in the embedded perl module if used from SSI.\n       Thanks to Matthew Daley.\n\n    *) Bugfix: in the ngx_http_uwsgi_module.\n\n\nChanges with nginx 1.1.16                                        29 Feb 2012\n\n    *) Change: the simultaneous subrequest limit has been raised to 200.\n\n    *) Feature: the \"from\" parameter of the \"disable_symlinks\" directive.\n\n    *) Feature: the \"return\" and \"error_page\" directives can now be used to\n       return 307 redirections.\n\n    *) Bugfix: a segmentation fault might occur in a worker process if the\n       \"resolver\" directive was used and there was no \"error_log\" directive\n       specified at global level.\n       Thanks to Roman Arutyunyan.\n\n    *) Bugfix: a segmentation fault might occur in a worker process if the\n       \"proxy_http_version 1.1\" or \"fastcgi_keep_conn on\" directives were\n       used.\n\n    *) Bugfix: memory leaks.\n       Thanks to Lanshun Zhou.\n\n    *) Bugfix: in the \"disable_symlinks\" directive.\n\n    *) Bugfix: on ZFS filesystem disk cache size might be calculated\n       incorrectly; the bug had appeared in 1.0.1.\n\n    *) Bugfix: nginx could not be built by the icc 12.1 compiler.\n\n    *) Bugfix: nginx could not be built by gcc on Solaris; the bug had\n       appeared in 1.1.15.\n\n\nChanges with nginx 1.1.15                                        15 Feb 2012\n\n    *) Feature: the \"disable_symlinks\" directive.\n\n    *) Feature: the \"proxy_cookie_domain\" and \"proxy_cookie_path\"\n       directives.\n\n    *) Bugfix: nginx might log incorrect error \"upstream prematurely closed\n       connection\" instead of correct \"upstream sent too big header\" one.\n       Thanks to Feibo Li.\n\n    *) Bugfix: nginx could not be built with the ngx_http_perl_module if the\n       --with-openssl option was used.\n\n    *) Bugfix: the number of internal redirects to named locations was not\n       limited.\n\n    *) Bugfix: calling $r->flush() multiple times might cause errors in the\n       ngx_http_gzip_filter_module.\n\n    *) Bugfix: temporary files might be not removed if the \"proxy_store\"\n       directive was used with SSI includes.\n\n    *) Bugfix: in some cases non-cacheable variables (such as the $args\n       variable) returned old empty cached value.\n\n    *) Bugfix: a segmentation fault might occur in a worker process if too\n       many SSI subrequests were issued simultaneously; the bug had appeared\n       in 0.7.25.\n\n\nChanges with nginx 1.1.14                                        30 Jan 2012\n\n    *) Feature: multiple \"limit_req\" limits may be used simultaneously.\n\n    *) Bugfix: in error handling while connecting to a backend.\n       Thanks to Piotr Sikora.\n\n    *) Bugfix: in AIO error handling on FreeBSD.\n\n    *) Bugfix: in the OpenSSL library initialization.\n\n    *) Bugfix: the \"proxy_redirect\" directives might be inherited\n       incorrectly.\n\n    *) Bugfix: memory leak during reconfiguration if the \"pcre_jit\"\n       directive was used.\n\n\nChanges with nginx 1.1.13                                        16 Jan 2012\n\n    *) Feature: the \"TLSv1.1\" and \"TLSv1.2\" parameters of the\n       \"ssl_protocols\" directive.\n\n    *) Bugfix: the \"limit_req\" directive parameters were not inherited\n       correctly; the bug had appeared in 1.1.12.\n\n    *) Bugfix: the \"proxy_redirect\" directive incorrectly processed\n       \"Refresh\" header if regular expression were used.\n\n    *) Bugfix: the \"proxy_cache_use_stale\" directive with \"error\" parameter\n       did not return answer from cache if there were no live upstreams.\n\n    *) Bugfix: the \"worker_cpu_affinity\" directive might not work.\n\n    *) Bugfix: nginx could not be built on Solaris; the bug had appeared in\n       1.1.12.\n\n    *) Bugfix: in the ngx_http_mp4_module.\n\n\nChanges with nginx 1.1.12                                        26 Dec 2011\n\n    *) Change: a \"proxy_pass\" directive without URI part now uses changed\n       URI after redirection with the \"error_page\" directive.\n       Thanks to Lanshun Zhou.\n\n    *) Feature: the \"proxy/fastcgi/scgi/uwsgi_cache_lock\",\n       \"proxy/fastcgi/scgi/uwsgi_cache_lock_timeout\" directives.\n\n    *) Feature: the \"pcre_jit\" directive.\n\n    *) Feature: the \"if\" SSI command supports captures in regular\n       expressions.\n\n    *) Bugfix: the \"if\" SSI command did not work inside the \"block\" command.\n\n    *) Bugfix: the \"limit_conn_log_level\" and \"limit_req_log_level\"\n       directives might not work.\n\n    *) Bugfix: the \"limit_rate\" directive did not allow to use full\n       throughput, even if limit value was very high.\n\n    *) Bugfix: the \"sendfile_max_chunk\" directive did not work, if the\n       \"limit_rate\" directive was used.\n\n    *) Bugfix: a \"proxy_pass\" directive without URI part always used\n       original request URI if variables were used.\n\n    *) Bugfix: a \"proxy_pass\" directive without URI part might use original\n       request after redirection with the \"try_files\" directive.\n       Thanks to Lanshun Zhou.\n\n    *) Bugfix: in the ngx_http_scgi_module.\n\n    *) Bugfix: in the ngx_http_mp4_module.\n\n    *) Bugfix: nginx could not be built on Solaris; the bug had appeared in\n       1.1.9.\n\n\nChanges with nginx 1.1.11                                        12 Dec 2011\n\n    *) Feature: the \"so_keepalive\" parameter of the \"listen\" directive.\n       Thanks to Vsevolod Stakhov.\n\n    *) Feature: the \"if_not_empty\" parameter of the\n       \"fastcgi/scgi/uwsgi_param\" directives.\n\n    *) Feature: the $https variable.\n\n    *) Feature: the \"proxy_redirect\" directive supports variables in the\n       first parameter.\n\n    *) Feature: the \"proxy_redirect\" directive supports regular expressions.\n\n    *) Bugfix: the $sent_http_cache_control variable might contain a wrong\n       value if the \"expires\" directive was used.\n       Thanks to Yichun Zhang.\n\n    *) Bugfix: the \"read_ahead\" directive might not work combined with\n       \"try_files\" and \"open_file_cache\".\n\n    *) Bugfix: a segmentation fault might occur in a worker process if small\n       time was used in the \"inactive\" parameter of the \"proxy_cache_path\"\n       directive.\n\n    *) Bugfix: responses from cache might hang.\n\n\nChanges with nginx 1.1.10                                        30 Nov 2011\n\n    *) Bugfix: a segmentation fault occurred in a worker process if AIO was\n       used on Linux; the bug had appeared in 1.1.9.\n\n\nChanges with nginx 1.1.9                                         28 Nov 2011\n\n    *) Change: now double quotes are encoded in an \"echo\" SSI-command\n       output.\n       Thanks to Zaur Abasmirzoev.\n\n    *) Feature: the \"valid\" parameter of the \"resolver\" directive. By\n       default TTL returned by a DNS server is used.\n       Thanks to Kirill A. Korinskiy.\n\n    *) Bugfix: nginx might hang after a worker process abnormal termination.\n\n    *) Bugfix: a segmentation fault might occur in a worker process if SNI\n       was used; the bug had appeared in 1.1.2.\n\n    *) Bugfix: in the \"keepalive_disable\" directive; the bug had appeared in\n       1.1.8.\n       Thanks to Alexander Usov.\n\n    *) Bugfix: SIGWINCH signal did not work after first binary upgrade; the\n       bug had appeared in 1.1.1.\n\n    *) Bugfix: backend responses with length not matching \"Content-Length\"\n       header line are no longer cached.\n\n    *) Bugfix: in the \"scgi_param\" directive, if complex parameters were\n       used.\n\n    *) Bugfix: in the \"epoll\" event method.\n       Thanks to Yichun Zhang.\n\n    *) Bugfix: in the ngx_http_flv_module.\n       Thanks to Piotr Sikora.\n\n    *) Bugfix: in the ngx_http_mp4_module.\n\n    *) Bugfix: IPv6 addresses are now handled properly in a request line and\n       in a \"Host\" request header line.\n\n    *) Bugfix: \"add_header\" and \"expires\" directives did not work if a\n       request was proxied and response status code was 206.\n\n    *) Bugfix: nginx could not be built on FreeBSD 10.\n\n    *) Bugfix: nginx could not be built on AIX.\n\n\nChanges with nginx 1.1.8                                         14 Nov 2011\n\n    *) Change: the ngx_http_limit_zone_module was renamed to the\n       ngx_http_limit_conn_module.\n\n    *) Change: the \"limit_zone\" directive was superseded by the\n       \"limit_conn_zone\" directive with a new syntax.\n\n    *) Feature: support for multiple \"limit_conn\" limits on the same level.\n\n    *) Feature: the \"image_filter_sharpen\" directive.\n\n    *) Bugfix: a segmentation fault might occur in a worker process if\n       resolver got a big DNS response.\n       Thanks to Ben Hawkes.\n\n    *) Bugfix: in cache key calculation if internal MD5 implementation was\n       used; the bug had appeared in 1.0.4.\n\n    *) Bugfix: the \"If-Modified-Since\", \"If-Range\", etc. client request\n       header lines might be passed to backend while caching; or not passed\n       without caching if caching was enabled in another part of the\n       configuration.\n\n    *) Bugfix: the module ngx_http_mp4_module sent incorrect\n       \"Content-Length\" response header line if the \"start\" argument was\n       used.\n       Thanks to Piotr Sikora.\n\n\nChanges with nginx 1.1.7                                         31 Oct 2011\n\n    *) Feature: support of several DNS servers in the \"resolver\" directive.\n       Thanks to Kirill A. Korinskiy.\n\n    *) Bugfix: a segmentation fault occurred on start or during\n       reconfiguration if the \"ssl\" directive was used at http level and\n       there was no \"ssl_certificate\" defined.\n\n    *) Bugfix: reduced memory consumption while proxying big files if they\n       were buffered to disk.\n\n    *) Bugfix: a segmentation fault might occur in a worker process if\n       \"proxy_http_version 1.1\" directive was used.\n\n    *) Bugfix: in the \"expires @time\" directive.\n\n\nChanges with nginx 1.1.6                                         17 Oct 2011\n\n    *) Change in internal API: now module context data are cleared while\n       internal redirect to named location.\n       Requested by Yichun Zhang.\n\n    *) Change: if a server in an upstream failed, only one request will be\n       sent to it after fail_timeout; the server will be considered alive if\n       it will successfully respond to the request.\n\n    *) Change: now the 0x7F-0xFF characters are escaped as \\xXX in an\n       access_log.\n\n    *) Feature: \"proxy/fastcgi/scgi/uwsgi_ignore_headers\" directives support\n       the following additional values: X-Accel-Limit-Rate,\n       X-Accel-Buffering, X-Accel-Charset.\n\n    *) Feature: decrease of memory consumption if SSL is used.\n\n    *) Bugfix: some UTF-8 characters were processed incorrectly.\n       Thanks to Alexey Kuts.\n\n    *) Bugfix: the ngx_http_rewrite_module directives specified at \"server\"\n       level were executed twice if no matching locations were defined.\n\n    *) Bugfix: a socket leak might occurred if \"aio sendfile\" was used.\n\n    *) Bugfix: connections with fast clients might be closed after\n       send_timeout if file AIO was used.\n\n    *) Bugfix: in the ngx_http_autoindex_module.\n\n    *) Bugfix: the module ngx_http_mp4_module did not support seeking on\n       32-bit platforms.\n\n\nChanges with nginx 1.1.5                                         05 Oct 2011\n\n    *) Feature: the \"uwsgi_buffering\" and \"scgi_buffering\" directives.\n       Thanks to Peter Smit.\n\n    *) Bugfix: non-cacheable responses might be cached if\n       \"proxy_cache_bypass\" directive was used.\n       Thanks to John Ferlito.\n\n    *) Bugfix: in HTTP/1.1 support in the ngx_http_proxy_module.\n\n    *) Bugfix: cached responses with an empty body were returned\n       incorrectly; the bug had appeared in 0.8.31.\n\n    *) Bugfix: 201 responses of the ngx_http_dav_module were incorrect; the\n       bug had appeared in 0.8.32.\n\n    *) Bugfix: in the \"return\" directive.\n\n    *) Bugfix: the \"ssl_session_cache builtin\" directive caused segmentation\n       fault; the bug had appeared in 1.1.1.\n\n\nChanges with nginx 1.1.4                                         20 Sep 2011\n\n    *) Feature: the ngx_http_upstream_keepalive module.\n\n    *) Feature: the \"proxy_http_version\" directive.\n\n    *) Feature: the \"fastcgi_keep_conn\" directive.\n\n    *) Feature: the \"worker_aio_requests\" directive.\n\n    *) Bugfix: if nginx was built --with-file-aio it could not be run on\n       Linux kernel which did not support AIO.\n\n    *) Bugfix: in Linux AIO error processing.\n       Thanks to Hagai Avrahami.\n\n    *) Bugfix: reduced memory consumption for long-lived requests.\n\n    *) Bugfix: the module ngx_http_mp4_module did not support 64-bit MP4\n       \"co64\" atom.\n\n\nChanges with nginx 1.1.3                                         14 Sep 2011\n\n    *) Feature: the module ngx_http_mp4_module.\n\n    *) Bugfix: in Linux AIO combined with open_file_cache.\n\n    *) Bugfix: open_file_cache did not update file info on retest if file\n       was not atomically changed.\n\n    *) Bugfix: nginx could not be built on MacOSX 10.7.\n\n\nChanges with nginx 1.1.2                                         05 Sep 2011\n\n    *) Change: now if total size of all ranges is greater than source\n       response size, then nginx disables ranges and returns just the source\n       response.\n\n    *) Feature: the \"max_ranges\" directive.\n\n    *) Bugfix: the \"ssl_verify_client\", \"ssl_verify_depth\", and\n       \"ssl_prefer_server_ciphers\" directives might work incorrectly if SNI\n       was used.\n\n    *) Bugfix: in the \"proxy/fastcgi/scgi/uwsgi_ignore_client_abort\"\n       directives.\n\n\nChanges with nginx 1.1.1                                         22 Aug 2011\n\n    *) Change: now cache loader processes either as many files as specified\n       by \"loader_files\" parameter or works no longer than time specified by\n       the \"loader_threshold\" parameter during each iteration.\n\n    *) Change: now SIGWINCH signal works only in daemon mode.\n\n    *) Feature: now shared zones and caches use POSIX semaphores on Solaris.\n       Thanks to Den Ivanov.\n\n    *) Feature: accept filters are now supported on NetBSD.\n\n    *) Bugfix: nginx could not be built on Linux 3.0.\n\n    *) Bugfix: nginx did not use gzipping in some cases; the bug had\n       appeared in 1.1.0.\n\n    *) Bugfix: request body might be processed incorrectly if client used\n       pipelining.\n\n    *) Bugfix: in the \"request_body_in_single_buf\" directive.\n\n    *) Bugfix: in \"proxy_set_body\" and \"proxy_pass_request_body\" directives\n       if SSL connection to backend was used.\n\n    *) Bugfix: nginx hogged CPU if all servers in an upstream were marked as\n       \"down\".\n\n    *) Bugfix: a segmentation fault might occur during reconfiguration if\n       ssl_session_cache was defined but not used in previous configuration.\n\n    *) Bugfix: a segmentation fault might occur in a worker process if many\n       backup servers were used in an upstream.\n\n    *) Bugfix: a segmentation fault might occur in a worker process if\n       \"fastcgi/scgi/uwsgi_param\" directives were used with values starting\n       with \"HTTP_\"; the bug had appeared in 0.8.40.\n\n\nChanges with nginx 1.1.0                                         01 Aug 2011\n\n    *) Feature: cache loader run time decrease.\n\n    *) Feature: \"loader_files\", \"loader_sleep\", and \"loader_threshold\"\n       options of the \"proxy/fastcgi/scgi/uwsgi_cache_path\" directives.\n\n    *) Feature: loading time decrease of configuration with large number of\n       HTTPS sites.\n\n    *) Feature: now nginx supports ECDHE key exchange ciphers.\n       Thanks to Adrian Kotelba.\n\n    *) Feature: the \"lingering_close\" directive.\n       Thanks to Maxim Dounin.\n\n    *) Bugfix: in closing connection for pipelined requests.\n       Thanks to Maxim Dounin.\n\n    *) Bugfix: nginx did not disable gzipping if client sent \"gzip;q=0\" in\n       \"Accept-Encoding\" request header line.\n\n    *) Bugfix: in timeout in unbuffered proxied mode.\n       Thanks to Maxim Dounin.\n\n    *) Bugfix: memory leaks when a \"proxy_pass\" directive contains variables\n       and proxies to an HTTPS backend.\n       Thanks to Maxim Dounin.\n\n    *) Bugfix: in parameter validation of a \"proxy_pass\" directive with\n       variables.\n       Thanks to Lanshun Zhou.\n\n    *) Bugfix: SSL did not work on QNX.\n       Thanks to Maxim Dounin.\n\n    *) Bugfix: SSL modules could not be built by gcc 4.6 without\n       --with-debug option.\n\n\nChanges with nginx 1.0.5                                         19 Jul 2011\n\n    *) Change: now default SSL ciphers are \"HIGH:!aNULL:!MD5\".\n       Thanks to Rob Stradling.\n\n    *) Feature: the \"referer_hash_max_size\" and \"referer_hash_bucket_size\"\n       directives.\n       Thanks to Witold Filipczyk.\n\n    *) Feature: $uid_reset variable.\n\n    *) Bugfix: a segmentation fault might occur in a worker process, if a\n       caching was used.\n       Thanks to Lanshun Zhou.\n\n    *) Bugfix: worker processes may got caught in an endless loop during\n       reconfiguration, if a caching was used; the bug had appeared in\n       0.8.48.\n       Thanks to Maxim Dounin.\n\n    *) Bugfix: \"stalled cache updating\" alert.\n       Thanks to Maxim Dounin.\n\n\nChanges with nginx 1.0.4                                         01 Jun 2011\n\n    *) Change: now regular expressions case sensitivity in the \"map\"\n       directive is given by prefixes \"~\" or \"~*\".\n\n    *) Feature: now shared zones and caches use POSIX semaphores on Linux.\n       Thanks to Denis F. Latypoff.\n\n    *) Bugfix: \"stalled cache updating\" alert.\n\n    *) Bugfix: nginx could not be built --without-http_auth_basic_module;\n       the bug had appeared in 1.0.3.\n\n\nChanges with nginx 1.0.3                                         25 May 2011\n\n    *) Feature: the \"auth_basic_user_file\" directive supports \"$apr1\",\n       \"{PLAIN}\", and \"{SSHA}\" password encryption methods.\n       Thanks to Maxim Dounin.\n\n    *) Feature: the \"geoip_org\" directive and $geoip_org variable.\n       Thanks to Alexander Uskov, Arnaud Granal, and Denis F. Latypoff.\n\n    *) Feature: ngx_http_geo_module and ngx_http_geoip_module support IPv4\n       addresses mapped to IPv6 addresses.\n\n    *) Bugfix: a segmentation fault occurred in a worker process during\n       testing IPv4 address mapped to IPv6 address, if access or deny rules\n       were defined only for IPv6; the bug had appeared in 0.8.22.\n\n    *) Bugfix: a cached response may be broken if \"proxy/fastcgi/scgi/\n       uwsgi_cache_bypass\" and \"proxy/fastcgi/scgi/uwsgi_no_cache\" directive\n       values were different; the bug had appeared in 0.8.46.\n\n\nChanges with nginx 1.0.2                                         10 May 2011\n\n    *) Feature: now shared zones and caches use POSIX semaphores.\n\n    *) Bugfix: in the \"rotate\" parameter of the \"image_filter\" directive.\n       Thanks to Adam Bocim.\n\n    *) Bugfix: nginx could not be built on Solaris; the bug had appeared in\n       1.0.1.\n\n\nChanges with nginx 1.0.1                                         03 May 2011\n\n    *) Change: now the \"split_clients\" directive uses MurmurHash2 algorithm\n       because of better distribution.\n       Thanks to Oleg Mamontov.\n\n    *) Change: now long strings starting with zero are not considered as\n       false values.\n       Thanks to Maxim Dounin.\n\n    *) Change: now nginx uses a default listen backlog value 511 on Linux.\n\n    *) Feature: the $upstream_... variables may be used in the SSI and perl\n       modules.\n\n    *) Bugfix: now nginx limits better disk cache size.\n       Thanks to Oleg Mamontov.\n\n    *) Bugfix: a segmentation fault might occur while parsing incorrect IPv4\n       address; the bug had appeared in 0.9.3.\n       Thanks to Maxim Dounin.\n\n    *) Bugfix: nginx could not be built by gcc 4.6 without --with-debug\n       option.\n\n    *) Bugfix: nginx could not be built on Solaris 9 and earlier; the bug\n       had appeared in 0.9.3.\n       Thanks to Dagobert Michelsen.\n\n    *) Bugfix: $request_time variable had invalid values if subrequests were\n       used; the bug had appeared in 0.8.47.\n       Thanks to Igor A. Valcov.\n\n\nChanges with nginx 1.0.0                                         12 Apr 2011\n\n    *) Bugfix: a cache manager might hog CPU after reload.\n       Thanks to Maxim Dounin.\n\n    *) Bugfix: an \"image_filter crop\" directive worked incorrectly coupled\n       with an \"image_filter rotate 180\" directive.\n\n    *) Bugfix: a \"satisfy any\" directive disabled custom 401 error page.\n\n\nChanges with nginx 0.9.7                                         04 Apr 2011\n\n    *) Feature: now keepalive connections may be closed premature, if there\n       are no free worker connections.\n       Thanks to Maxim Dounin.\n\n    *) Feature: the \"rotate\" parameter of the \"image_filter\" directive.\n       Thanks to Adam Bocim.\n\n    *) Bugfix: a case when a backend in \"fastcgi_pass\", \"scgi_pass\", or\n       \"uwsgi_pass\" directives is given by expression and refers to a\n       defined upstream.\n\n\nChanges with nginx 0.9.6                                         21 Mar 2011\n\n    *) Feature: the \"map\" directive supports regular expressions as value of\n       the first parameter.\n\n    *) Feature: $time_iso8601 access_log variable.\n       Thanks to Michael Lustfield.\n\n\nChanges with nginx 0.9.5                                         21 Feb 2011\n\n    *) Change: now nginx uses a default listen backlog value -1 on Linux.\n       Thanks to Andrei Nigmatulin.\n\n    *) Feature: the \"utf8\" parameter of \"geoip_country\" and \"geoip_city\"\n       directives.\n       Thanks to Denis F. Latypoff.\n\n    *) Bugfix: in a default \"proxy_redirect\" directive if \"proxy_pass\"\n       directive has no URI part.\n       Thanks to Maxim Dounin.\n\n    *) Bugfix: an \"error_page\" directive did not work with nonstandard error\n       codes; the bug had appeared in 0.8.53.\n       Thanks to Maxim Dounin.\n\n\nChanges with nginx 0.9.4                                         21 Jan 2011\n\n    *) Feature: the \"server_name\" directive supports the $hostname variable.\n\n    *) Feature: 494 code for \"Request Header Too Large\" error.\n\n\nChanges with nginx 0.9.3                                         13 Dec 2010\n\n    *) Bugfix: if there was a single server for given IPv6 address:port\n       pair, then captures in regular expressions in a \"server_name\"\n       directive did not work.\n\n    *) Bugfix: nginx could not be built on Solaris; the bug had appeared in\n       0.9.0.\n\n\nChanges with nginx 0.9.2                                         06 Dec 2010\n\n    *) Feature: the \"If-Unmodified-Since\" client request header line\n       support.\n\n    *) Workaround: fallback to accept() syscall if accept4() was not\n       implemented; the issue had appeared in 0.9.0.\n\n    *) Bugfix: nginx could not be built on Cygwin; the bug had appeared in\n       0.9.0.\n\n    *) Bugfix: for OpenSSL vulnerability CVE-2010-4180.\n       Thanks to Maxim Dounin.\n\n\nChanges with nginx 0.9.1                                         30 Nov 2010\n\n    *) Bugfix: \"return CODE message\" directives did not work; the bug had\n       appeared in 0.9.0.\n\n\nChanges with nginx 0.9.0                                         29 Nov 2010\n\n    *) Feature: the \"keepalive_disable\" directive.\n\n    *) Feature: the \"map\" directive supports variables as value of a defined\n       variable.\n\n    *) Feature: the \"map\" directive supports empty strings as value of the\n       first parameter.\n\n    *) Feature: the \"map\" directive supports expressions as the first\n       parameter.\n\n    *) Feature: nginx(8) manual page.\n       Thanks to Sergey Osokin.\n\n    *) Feature: Linux accept4() support.\n       Thanks to Simon Liu.\n\n    *) Workaround: elimination of Linux linker warning about \"sys_errlist\"\n       and \"sys_nerr\"; the warning had appeared in 0.8.35.\n\n    *) Bugfix: a segmentation fault might occur in a worker process, if the\n       \"auth_basic\" directive was used.\n       Thanks to Michail Laletin.\n\n    *) Bugfix: compatibility with ngx_http_eval_module; the bug had appeared\n       in 0.8.42.\n\n\nChanges with nginx 0.8.53                                        18 Oct 2010\n\n    *) Feature: now the \"error_page\" directive allows to change a status\n       code in a redirect.\n\n    *) Feature: the \"gzip_disable\" directive supports special \"degradation\"\n       mask.\n\n    *) Bugfix: a socket leak might occurred if file AIO was used.\n       Thanks to Maxim Dounin.\n\n    *) Bugfix: if the first server had no \"listen\" directive and there was\n       no explicit default server, then a next server with a \"listen\"\n       directive became the default server; the bug had appeared in 0.8.21.\n\n\nChanges with nginx 0.8.52                                        28 Sep 2010\n\n    *) Bugfix: nginx used SSL mode for a listen socket if any listen option\n       was set; the bug had appeared in 0.8.51.\n\n\nChanges with nginx 0.8.51                                        27 Sep 2010\n\n    *) Change: the \"secure_link_expires\" directive has been canceled.\n\n    *) Change: a logging level of resolver errors has been lowered from\n       \"alert\" to \"error\".\n\n    *) Feature: now a listen socket \"ssl\" parameter may be set several\n       times.\n\n\nChanges with nginx 0.8.50                                        02 Sep 2010\n\n    *) Feature: the \"secure_link\", \"secure_link_md5\", and\n       \"secure_link_expires\" directives of the ngx_http_secure_link_module.\n\n    *) Feature: the -q switch.\n       Thanks to Gena Makhomed.\n\n    *) Bugfix: worker processes may got caught in an endless loop during\n       reconfiguration, if a caching was used; the bug had appeared in\n       0.8.48.\n\n    *) Bugfix: in the \"gzip_disable\" directive.\n       Thanks to Derrick Petzold.\n\n    *) Bugfix: nginx/Windows could not send stop, quit, reopen, and reload\n       signals to a process run in other session.\n\n\nChanges with nginx 0.8.49                                        09 Aug 2010\n\n    *) Feature: the \"image_filter_jpeg_quality\" directive supports\n       variables.\n\n    *) Bugfix: a segmentation fault might occur in a worker process, if the\n       $geoip_region_name variables was used; the bug had appeared in\n       0.8.48.\n\n    *) Bugfix: errors intercepted by error_page were cached only for next\n       request; the bug had appeared in 0.8.48.\n\n\nChanges with nginx 0.8.48                                        03 Aug 2010\n\n    *) Change: now the \"server_name\" directive default value is an empty\n       name \"\".\n       Thanks to Gena Makhomed.\n\n    *) Change: now the \"server_name_in_redirect\" directive default value is\n       \"off\".\n\n    *) Feature: the $geoip_dma_code, $geoip_area_code, and\n       $geoip_region_name variables.\n       Thanks to Christine McGonagle.\n\n    *) Bugfix: the \"proxy_pass\", \"fastcgi_pass\", \"uwsgi_pass\", and\n       \"scgi_pass\" directives were not inherited inside \"limit_except\"\n       blocks.\n\n    *) Bugfix: the \"proxy_cache_min_uses\", \"fastcgi_cache_min_uses\"\n       \"uwsgi_cache_min_uses\", and \"scgi_cache_min_uses\" directives did not\n       work; the bug had appeared in 0.8.46.\n\n    *) Bugfix: the \"fastcgi_split_path_info\" directive used incorrectly\n       captures, if only parts of an URI were captured.\n       Thanks to Yuriy Taraday and Frank Enderle.\n\n    *) Bugfix: the \"rewrite\" directive did not escape a \";\" character during\n       copying from URI to query string.\n       Thanks to Daisuke Murase.\n\n    *) Bugfix: the ngx_http_image_filter_module closed a connection, if an\n       image was larger than \"image_filter_buffer\" size.\n\n\nChanges with nginx 0.8.47                                        28 Jul 2010\n\n    *) Bugfix: $request_time variable had invalid values for subrequests.\n\n    *) Bugfix: errors intercepted by error_page could not be cached.\n\n    *) Bugfix: a cache manager process may got caught in an endless loop, if\n       max_size parameter was used; the bug had appeared in 0.8.46.\n\n\nChanges with nginx 0.8.46                                        19 Jul 2010\n\n    *) Change: now the \"proxy_no_cache\", \"fastcgi_no_cache\",\n       \"uwsgi_no_cache\", and \"scgi_no_cache\" directives affect on a cached\n       response saving only.\n\n    *) Feature: the \"proxy_cache_bypass\", \"fastcgi_cache_bypass\",\n       \"uwsgi_cache_bypass\", and \"scgi_cache_bypass\" directives.\n\n    *) Bugfix: nginx did not free memory in cache keys zones if there was an\n       error during working with backend: the memory was freed only after\n       inactivity time or on memory low condition.\n\n\nChanges with nginx 0.8.45                                        13 Jul 2010\n\n    *) Feature: ngx_http_xslt_filter improvements.\n       Thanks to Laurence Rowe.\n\n    *) Bugfix: SSI response might be truncated after include with\n       wait=\"yes\"; the bug had appeared in 0.7.25.\n       Thanks to Maxim Dounin.\n\n    *) Bugfix: the \"listen\" directive did not support the \"setfib=0\"\n       parameter.\n\n\nChanges with nginx 0.8.44                                        05 Jul 2010\n\n    *) Change: now nginx does not cache by default backend responses, if\n       they have a \"Set-Cookie\" header line.\n\n    *) Feature: the \"listen\" directive supports the \"setfib\" parameter.\n       Thanks to Andrew Filonov.\n\n    *) Bugfix: the \"sub_filter\" directive might change character case on\n       partial match.\n\n    *) Bugfix: compatibility with HP/UX.\n\n    *) Bugfix: compatibility with AIX xlC_r compiler.\n\n    *) Bugfix: nginx treated large SSLv2 packets as plain requests.\n       Thanks to Miroslaw Jaworski.\n\n\nChanges with nginx 0.8.43                                        30 Jun 2010\n\n    *) Feature: large geo ranges base loading speed-up.\n\n    *) Bugfix: an error_page redirection to \"location /zero {return 204;}\"\n       without changing status code kept the error body; the bug had\n       appeared in 0.8.42.\n\n    *) Bugfix: nginx might close IPv6 listen socket during reconfiguration.\n       Thanks to Maxim Dounin.\n\n    *) Bugfix: the $uid_set variable may be used at any request processing\n       stage.\n\n\nChanges with nginx 0.8.42                                        21 Jun 2010\n\n    *) Change: now nginx tests locations given by regular expressions, if\n       request was matched exactly by a location given by a prefix string.\n       The previous behavior has been introduced in 0.7.1.\n\n    *) Feature: the ngx_http_scgi_module.\n       Thanks to Manlio Perillo.\n\n    *) Feature: a text answer may be added to a \"return\" directive.\n\n\nChanges with nginx 0.8.41                                        15 Jun 2010\n\n    *) Security: nginx/Windows worker might be terminated abnormally if a\n       requested file name has invalid UTF-8 encoding.\n\n    *) Change: now nginx allows to use spaces in a request line.\n\n    *) Bugfix: the \"proxy_redirect\" directive changed incorrectly a backend\n       \"Refresh\" response header line.\n       Thanks to Andrey Andreew and Max Sogin.\n\n    *) Bugfix: nginx did not support path without host name in \"Destination\"\n       request header line.\n\n\nChanges with nginx 0.8.40                                        07 Jun 2010\n\n    *) Security: now nginx/Windows ignores default file stream name.\n       Thanks to Jose Antonio Vazquez Gonzalez.\n\n    *) Feature: the ngx_http_uwsgi_module.\n       Thanks to Roberto De Ioris.\n\n    *) Feature: a \"fastcgi_param\" directive with value starting with \"HTTP_\"\n       overrides a client request header line.\n\n    *) Bugfix: the \"If-Modified-Since\", \"If-Range\", etc. client request\n       header lines were passed to FastCGI-server while caching.\n\n    *) Bugfix: listen unix domain socket could not be changed during\n       reconfiguration.\n       Thanks to Maxim Dounin.\n\n\nChanges with nginx 0.8.39                                        31 May 2010\n\n    *) Bugfix: an inherited \"alias\" directive worked incorrectly in\n       inclusive location.\n\n    *) Bugfix: in \"alias\" with variables and \"try_files\" directives\n       combination.\n\n    *) Bugfix: listen unix domain and IPv6 sockets did not inherit while\n       online upgrade.\n       Thanks to Maxim Dounin.\n\n\nChanges with nginx 0.8.38                                        24 May 2010\n\n    *) Feature: the \"proxy_no_cache\" and \"fastcgi_no_cache\" directives.\n\n    *) Feature: now the \"rewrite\" directive does a redirect automatically if\n       the $scheme variable is used.\n       Thanks to Piotr Sikora.\n\n    *) Bugfix: now \"limit_req\" delay directive conforms to the described\n       algorithm.\n       Thanks to Maxim Dounin.\n\n    *) Bugfix: the $uid_got variable might not be used in the SSI and perl\n       modules.\n\n\nChanges with nginx 0.8.37                                        17 May 2010\n\n    *) Feature: the ngx_http_split_clients_module.\n\n    *) Feature: the \"map\" directive supports keys more than 255 characters.\n\n    *) Bugfix: nginx ignored the \"private\" and \"no-store\" values in the\n       \"Cache-Control\" backend response header line.\n\n    *) Bugfix: a \"stub\" parameter of an \"include\" SSI directive was not\n       used, if empty response has 200 status code.\n\n    *) Bugfix: if a proxied or FastCGI request was internally redirected to\n       another proxied or FastCGI location, then a segmentation fault might\n       occur in a worker process; the bug had appeared in 0.8.33.\n       Thanks to Yichun Zhang.\n\n    *) Bugfix: IMAP connections may hang until they timed out while talking\n       to Zimbra server.\n       Thanks to Alan Batie.\n\n\nChanges with nginx 0.8.36                                        22 Apr 2010\n\n    *) Bugfix: the ngx_http_dav_module handled incorrectly the DELETE, COPY,\n       and MOVE methods for symlinks.\n\n    *) Bugfix: values of the $query_string, $arg_..., etc. variables cached\n       in main request were used by the SSI module in subrequests.\n\n    *) Bugfix: a variable value was repeatedly encoded after each an \"echo\"\n       SSI-command output; the bug had appeared in 0.6.14.\n\n    *) Bugfix: a worker process hung if a FIFO file was requested.\n       Thanks to Vicente Aguilar and Maxim Dounin.\n\n    *) Bugfix: OpenSSL-1.0.0 compatibility on 64-bit Linux.\n       Thanks to Maxim Dounin.\n\n    *) Bugfix: nginx could not be built --without-http-cache; the bug had\n       appeared in 0.8.35.\n\n\nChanges with nginx 0.8.35                                        01 Apr 2010\n\n    *) Change: now the charset filter runs before the SSI filter.\n\n    *) Feature: the \"chunked_transfer_encoding\" directive.\n\n    *) Bugfix: an \"&\" character was not escaped when it was copied in\n       arguments part in a rewrite rule.\n\n    *) Bugfix: nginx might be terminated abnormally while a signal\n       processing or if the directive \"timer_resolution\" was used on\n       platforms which do not support kqueue or eventport notification\n       methods.\n       Thanks to George Xie and Maxim Dounin.\n\n    *) Bugfix: if temporary files and permanent storage area resided at\n       different file systems, then permanent file modification times were\n       incorrect.\n       Thanks to Maxim Dounin.\n\n    *) Bugfix: ngx_http_memcached_module might issue the error message\n       \"memcached sent invalid trailer\".\n       Thanks to Maxim Dounin.\n\n    *) Bugfix: nginx could not built zlib-1.2.4 library using the library\n       sources.\n       Thanks to Maxim Dounin.\n\n    *) Bugfix: a segmentation fault occurred in a worker process, if there\n       was large stderr output before FastCGI response; the bug had appeared\n       in 0.8.34.\n       Thanks to Maxim Dounin.\n\n\nChanges with nginx 0.8.34                                        03 Mar 2010\n\n    *) Bugfix: nginx did not support all ciphers and digests used in client\n       certificates.\n       Thanks to Innocenty Enikeew.\n\n    *) Bugfix: nginx cached incorrectly FastCGI responses if there was large\n       stderr output before response.\n\n    *) Bugfix: nginx did not support HTTPS referrers.\n\n    *) Bugfix: nginx/Windows might not find file if path in configuration\n       was given in other character case; the bug had appeared in 0.8.33.\n\n    *) Bugfix: the $date_local variable has an incorrect value, if the \"%s\"\n       format was used.\n       Thanks to Maxim Dounin.\n\n    *) Bugfix: if ssl_session_cache was not set or was set to \"none\", then\n       during client certificate verify the error \"session id context\n       uninitialized\" might occur; the bug had appeared in 0.7.1.\n\n    *) Bugfix: a geo range returned default value if the range included two\n       or more /16 networks and did not begin at /16 network boundary.\n\n    *) Bugfix: a block used in a \"stub\" parameter of an \"include\" SSI\n       directive was output with \"text/plain\" MIME type.\n\n    *) Bugfix: $r->sleep() did not work; the bug had appeared in 0.8.11.\n\n\nChanges with nginx 0.8.33                                        01 Feb 2010\n\n    *) Security: now nginx/Windows ignores trailing spaces in URI.\n       Thanks to Dan Crowley, Core Security Technologies.\n\n    *) Security: now nginx/Windows ignores short files names.\n       Thanks to Dan Crowley, Core Security Technologies.\n\n    *) Change: now keepalive connections after POST requests are not\n       disabled for MSIE 7.0+.\n       Thanks to Adam Lounds.\n\n    *) Workaround: now keepalive connections are disabled for Safari.\n       Thanks to Joshua Sierles.\n\n    *) Bugfix: if a proxied or FastCGI request was internally redirected to\n       another proxied or FastCGI location, then $upstream_response_time\n       variable may have abnormally large value; the bug had appeared in\n       0.8.7.\n\n    *) Bugfix: a segmentation fault might occur in a worker process, while\n       discarding a request body; the bug had appeared in 0.8.11.\n\n\nChanges with nginx 0.8.32                                        11 Jan 2010\n\n    *) Bugfix: UTF-8 encoding usage in the ngx_http_autoindex_module.\n       Thanks to Maxim Dounin.\n\n    *) Bugfix: regular expression named captures worked for two names only.\n       Thanks to Maxim Dounin.\n\n    *) Bugfix: now the \"localhost\" name is used in the \"Host\" request header\n       line, if an unix domain socket is defined in the \"auth_http\"\n       directive.\n       Thanks to Maxim Dounin.\n\n    *) Bugfix: nginx did not support chunked transfer encoding for 201\n       responses.\n       Thanks to Julian Reich.\n\n    *) Bugfix: if the \"expires modified\" set date in the past, then a\n       negative number was set in the \"Cache-Control\" response header line.\n       Thanks to Alex Kapranoff.\n\n\nChanges with nginx 0.8.31                                        23 Dec 2009\n\n    *) Feature: now the \"error_page\" directive may redirect the 301 and 302\n       responses.\n\n    *) Feature: the $geoip_city_continent_code, $geoip_latitude, and\n       $geoip_longitude variables.\n       Thanks to Arvind Sundararajan.\n\n    *) Feature: now the ngx_http_image_filter_module deletes always EXIF and\n       other application specific data if the data consume more than 5% of a\n       JPEG file.\n\n    *) Bugfix: nginx closed a connection if a cached response had an empty\n       body.\n       Thanks to Piotr Sikora.\n\n    *) Bugfix: nginx might not be built by gcc 4.x if the -O2 or higher\n       optimization option was used.\n       Thanks to Maxim Dounin and Denis F. Latypoff.\n\n    *) Bugfix: regular expressions in location were always tested in\n       case-sensitive mode; the bug had appeared in 0.8.25.\n\n    *) Bugfix: nginx cached a 304 response if there was the \"If-None-Match\"\n       header line in a proxied request.\n       Thanks to Tim Dettrick and David Kostal.\n\n    *) Bugfix: nginx/Windows tried to delete a temporary file twice if the\n       file should replace an already existent file.\n\n\nChanges with nginx 0.8.30                                        15 Dec 2009\n\n    *) Change: now the default buffer size of the\n       \"large_client_header_buffers\" directive is 8K.\n       Thanks to Andrew Cholakian.\n\n    *) Feature: the conf/fastcgi.conf for simple FastCGI configurations.\n\n    *) Bugfix: nginx/Windows tried to rename a temporary file twice if the\n       file should replace an already existent file.\n\n    *) Bugfix: of \"double free or corruption\" error issued if host could not\n       be resolved; the bug had appeared in 0.8.22.\n       Thanks to Konstantin Svist.\n\n    *) Bugfix: in libatomic usage on some platforms.\n       Thanks to W-Mark Kubacki.\n\n\nChanges with nginx 0.8.29                                        30 Nov 2009\n\n    *) Change: now the \"009\" status code is written to an access log for\n       proxied HTTP/0.9 responses.\n\n    *) Feature: the \"addition_types\", \"charset_types\", \"gzip_types\",\n       \"ssi_types\", \"sub_filter_types\", and \"xslt_types\" directives support\n       an \"*\" parameter.\n\n    *) Feature: GCC 4.1+ built-in atomic operations usage.\n       Thanks to W-Mark Kubacki.\n\n    *) Feature: the --with-libatomic[=DIR] option in the configure.\n       Thanks to W-Mark Kubacki.\n\n    *) Bugfix: listen unix domain socket had limited access rights.\n\n    *) Bugfix: cached HTTP/0.9 responses were handled incorrectly.\n\n    *) Bugfix: regular expression named captures given by \"?P<...>\" did not\n       work in a \"server_name\" directive.\n       Thanks to Maxim Dounin.\n\n\nChanges with nginx 0.8.28                                        23 Nov 2009\n\n    *) Bugfix: nginx could not be built with the --without-pcre parameter;\n       the bug had appeared in 0.8.25.\n\n\nChanges with nginx 0.8.27                                        17 Nov 2009\n\n    *) Bugfix: regular expressions did not work in nginx/Windows; the bug\n       had appeared in 0.8.25.\n\n\nChanges with nginx 0.8.26                                        16 Nov 2009\n\n    *) Bugfix: in captures usage in \"rewrite\" directive; the bug had\n       appeared in 0.8.25.\n\n    *) Bugfix: nginx could not be built without the --with-debug option; the\n       bug had appeared in 0.8.25.\n\n\nChanges with nginx 0.8.25                                        16 Nov 2009\n\n    *) Change: now no message is written in an error log if a variable is\n       not found by $r->variable() method.\n\n    *) Feature: the ngx_http_degradation_module.\n\n    *) Feature: regular expression named captures.\n\n    *) Feature: now URI part is not required a \"proxy_pass\" directive if\n       variables are used.\n\n    *) Feature: now the \"msie_padding\" directive works for Chrome too.\n\n    *) Bugfix: a segmentation fault occurred in a worker process on low\n       memory condition; the bug had appeared in 0.8.18.\n\n    *) Bugfix: nginx sent gzipped responses to clients those do not support\n       gzip, if \"gzip_static on\" and \"gzip_vary off\"; the bug had appeared\n       in 0.8.16.\n\n\nChanges with nginx 0.8.24                                        11 Nov 2009\n\n    *) Bugfix: nginx always added \"Content-Encoding: gzip\" response header\n       line in 304 responses sent by ngx_http_gzip_static_module.\n\n    *) Bugfix: nginx could not be built without the --with-debug option; the\n       bug had appeared in 0.8.23.\n\n    *) Bugfix: the \"unix:\" parameter of the \"set_real_ip_from\" directive\n       inherited incorrectly from previous level.\n\n    *) Bugfix: in resolving empty name.\n\n\nChanges with nginx 0.8.23                                        11 Nov 2009\n\n    *) Security: now SSL/TLS renegotiation is disabled.\n       Thanks to Maxim Dounin.\n\n    *) Bugfix: listen unix domain socket did not inherit while online\n       upgrade.\n\n    *) Bugfix: the \"unix:\" parameter of the \"set_real_ip_from\" directive did\n       not without yet another directive with any IP address.\n\n    *) Bugfix: segmentation fault and infinite looping in resolver.\n\n    *) Bugfix: in resolver.\n       Thanks to Artem Bokhan.\n\n\nChanges with nginx 0.8.22                                        03 Nov 2009\n\n    *) Feature: the \"proxy_bind\", \"fastcgi_bind\", and \"memcached_bind\"\n       directives.\n\n    *) Feature: the \"access\" and the \"deny\" directives support IPv6.\n\n    *) Feature: the \"set_real_ip_from\" directive supports IPv6 addresses in\n       request headers.\n\n    *) Feature: the \"unix:\" parameter of the \"set_real_ip_from\" directive.\n\n    *) Bugfix: nginx did not delete unix domain socket after configuration\n       testing.\n\n    *) Bugfix: nginx deleted unix domain socket while online upgrade.\n\n    *) Bugfix: the \"!-x\" operator did not work.\n       Thanks to Maxim Dounin.\n\n    *) Bugfix: a segmentation fault might occur in a worker process, if\n       limit_rate was used in HTTPS server.\n       Thanks to Maxim Dounin.\n\n    *) Bugfix: a segmentation fault might occur in a worker process while\n       $limit_rate logging.\n       Thanks to Maxim Dounin.\n\n    *) Bugfix: a segmentation fault might occur in a worker process, if\n       there was no \"listen\" directive in \"server\" block; the bug had\n       appeared in 0.8.21.\n\n\nChanges with nginx 0.8.21                                        26 Oct 2009\n\n    *) Feature: now the \"-V\" switch shows TLS SNI support.\n\n    *) Feature: the \"listen\" directive of the HTTP module supports unix\n       domain sockets.\n       Thanks to Hongli Lai.\n\n    *) Feature: the \"default_server\" parameter of the \"listen\" directive.\n\n    *) Feature: now a \"default\" parameter is not required to set listen\n       socket options.\n\n    *) Bugfix: nginx did not support dates in 2038 year on 32-bit platforms;\n\n    *) Bugfix: socket leak; the bug had appeared in 0.8.11.\n\n\nChanges with nginx 0.8.20                                        14 Oct 2009\n\n    *) Change: now default SSL ciphers are \"HIGH:!ADH:!MD5\".\n\n    *) Bugfix: the ngx_http_autoindex_module did not show the trailing slash\n       in links to a directory; the bug had appeared in 0.7.15.\n\n    *) Bugfix: nginx did not close a log file set by the --error-log-path\n       configuration option; the bug had appeared in 0.7.53.\n\n    *) Bugfix: nginx did not treat a comma as separator in the\n       \"Cache-Control\" backend response header line.\n\n    *) Bugfix: nginx/Windows might not create temporary file, a cache file,\n       or \"proxy/fastcgi_store\"d file if a worker had no enough access\n       rights for top level directories.\n\n    *) Bugfix: the \"Set-Cookie\" and \"P3P\" FastCGI response header lines were\n       not hidden while caching if no \"fastcgi_hide_header\" directives were\n       used with any parameters.\n\n    *) Bugfix: nginx counted incorrectly disk cache size.\n\n\nChanges with nginx 0.8.19                                        06 Oct 2009\n\n    *) Change: now SSLv2 protocol is disabled by default.\n\n    *) Change: now default SSL ciphers are \"ALL:!ADH:RC4+RSA:+HIGH:+MEDIUM\".\n\n    *) Bugfix: a \"limit_req\" directive did not work; the bug had appeared in\n       0.8.18.\n\n\nChanges with nginx 0.8.18                                        06 Oct 2009\n\n    *) Feature: the \"read_ahead\" directive.\n\n    *) Feature: now several \"perl_modules\" directives may be used.\n\n    *) Feature: the \"limit_req_log_level\" and \"limit_conn_log_level\"\n       directives.\n\n    *) Bugfix: now \"limit_req\" directive conforms to the leaky bucket\n       algorithm.\n       Thanks to Maxim Dounin.\n\n    *) Bugfix: nginx did not work on Linux/sparc.\n       Thanks to Marcus Ramberg.\n\n    *) Bugfix: nginx sent '\\0' in a \"Location\" response header line on MKCOL\n       request.\n       Thanks to Xie Zhenye.\n\n    *) Bugfix: zero status code was logged instead of 499 status code; the\n       bug had appeared in 0.8.11.\n\n    *) Bugfix: socket leak; the bug had appeared in 0.8.11.\n\n\nChanges with nginx 0.8.17                                        28 Sep 2009\n\n    *) Security: now \"/../\" are disabled in \"Destination\" request header\n       line.\n\n    *) Change: now $host variable value is always low case.\n\n    *) Feature: the $ssl_session_id variable.\n\n    *) Bugfix: socket leak; the bug had appeared in 0.8.11.\n\n\nChanges with nginx 0.8.16                                        22 Sep 2009\n\n    *) Feature: the \"image_filter_transparency\" directive.\n\n    *) Bugfix: \"addition_types\" directive was incorrectly named\n       \"addtion_types\".\n\n    *) Bugfix: resolver cache poisoning.\n       Thanks to Matthew Dempsky.\n\n    *) Bugfix: memory leak in resolver.\n       Thanks to Matthew Dempsky.\n\n    *) Bugfix: invalid request line in $request variable was written in\n       access_log only if error_log was set to \"info\" or \"debug\" level.\n\n    *) Bugfix: in PNG alpha-channel support in the\n       ngx_http_image_filter_module.\n\n    *) Bugfix: nginx always added \"Vary: Accept-Encoding\" response header\n       line, if both \"gzip_static\" and \"gzip_vary\" were on.\n\n    *) Bugfix: in UTF-8 encoding support by \"try_files\" directive in\n       nginx/Windows.\n\n    *) Bugfix: in \"post_action\" directive usage; the bug had appeared in\n       0.8.11.\n       Thanks to Igor Artemiev.\n\n\nChanges with nginx 0.8.15                                        14 Sep 2009\n\n    *) Security: a segmentation fault might occur in worker process while\n       specially crafted request handling.\n       Thanks to Chris Ries.\n\n    *) Bugfix: if names .domain.tld, .sub.domain.tld, and .domain-some.tld\n       were defined, then the name .sub.domain.tld was matched by\n       .domain.tld.\n\n    *) Bugfix: in transparency support in the ngx_http_image_filter_module.\n\n    *) Bugfix: in file AIO.\n\n    *) Bugfix: in X-Accel-Redirect usage; the bug had appeared in 0.8.11.\n\n    *) Bugfix: in embedded perl module; the bug had appeared in 0.8.11.\n\n\nChanges with nginx 0.8.14                                        07 Sep 2009\n\n    *) Bugfix: an expired cached response might stick in the \"UPDATING\"\n       state.\n\n    *) Bugfix: a segmentation fault might occur in worker process, if\n       error_log was set to info or debug level.\n       Thanks to Sergey Bochenkov.\n\n    *) Bugfix: in embedded perl module; the bug had appeared in 0.8.11.\n\n    *) Bugfix: an \"error_page\" directive did not redirect a 413 error; the\n       bug had appeared in 0.6.10.\n\n\nChanges with nginx 0.8.13                                        31 Aug 2009\n\n    *) Bugfix: in the \"aio sendfile\" directive; the bug had appeared in\n       0.8.12.\n\n    *) Bugfix: nginx could not be built without the --with-file-aio option\n       on FreeBSD; the bug had appeared in 0.8.12.\n\n\nChanges with nginx 0.8.12                                        31 Aug 2009\n\n    *) Feature: the \"sendfile\" parameter in the \"aio\" directive on FreeBSD.\n\n    *) Bugfix: in try_files; the bug had appeared in 0.8.11.\n\n    *) Bugfix: in memcached; the bug had appeared in 0.8.11.\n\n\nChanges with nginx 0.8.11                                        28 Aug 2009\n\n    *) Change: now directive \"gzip_disable msie6\" does not disable gzipping\n       for MSIE 6.0 SV1.\n\n    *) Feature: file AIO support on FreeBSD and Linux.\n\n    *) Feature: the \"directio_alignment\" directive.\n\n\nChanges with nginx 0.8.10                                        24 Aug 2009\n\n    *) Bugfix: memory leaks if GeoIP City database was used.\n\n    *) Bugfix: in copying temporary files to permanent storage area; the bug\n       had appeared in 0.8.9.\n\n\nChanges with nginx 0.8.9                                         17 Aug 2009\n\n    *) Feature: now the start cache loader runs in a separate process; this\n       should improve large caches handling.\n\n    *) Feature: now temporary files and permanent storage area may reside at\n       different file systems.\n\n\nChanges with nginx 0.8.8                                         10 Aug 2009\n\n    *) Bugfix: in handling FastCGI headers split in records.\n\n    *) Bugfix: a segmentation fault occurred in worker process, if a request\n       was handled in two proxied or FastCGIed locations and a caching was\n       enabled in the first location; the bug had appeared in 0.8.7.\n\n\nChanges with nginx 0.8.7                                         27 Jul 2009\n\n    *) Change: minimum supported OpenSSL version is 0.9.7.\n\n    *) Change: the \"ask\" parameter of the \"ssl_verify_client\" directive was\n       changed to the \"optional\" parameter and now it checks a client\n       certificate if it was offered.\n       Thanks to Brice Figureau.\n\n    *) Feature: the $ssl_client_verify variable.\n       Thanks to Brice Figureau.\n\n    *) Feature: the \"ssl_crl\" directive.\n       Thanks to Brice Figureau.\n\n    *) Feature: the \"proxy\" parameter of the \"geo\" directive.\n\n    *) Feature: the \"image_filter\" directive supports variables for setting\n       size.\n\n    *) Bugfix: the $ssl_client_cert variable usage corrupted memory; the bug\n       had appeared in 0.7.7.\n       Thanks to Sergey Zhuravlev.\n\n    *) Bugfix: \"proxy_pass_header\" and \"fastcgi_pass_header\" directives did\n       not pass to a client the \"X-Accel-Redirect\", \"X-Accel-Limit-Rate\",\n       \"X-Accel-Buffering\", and \"X-Accel-Charset\" lines from backend\n       response header.\n       Thanks to Maxim Dounin.\n\n    *) Bugfix: in handling \"Last-Modified\" and \"Accept-Ranges\" backend\n       response header lines; the bug had appeared in 0.7.44.\n       Thanks to Maxim Dounin.\n\n    *) Bugfix: the \"[alert] zero size buf\" error if subrequest returns an\n       empty response; the bug had appeared in 0.8.5.\n\n\nChanges with nginx 0.8.6                                         20 Jul 2009\n\n    *) Feature: the ngx_http_geoip_module.\n\n    *) Bugfix: XSLT filter may fail with message \"not well formed XML\n       document\" for valid XML document.\n       Thanks to Kuramoto Eiji.\n\n    *) Bugfix: now in MacOSX, Cygwin, and nginx/Windows locations given by a\n       regular expression are always tested in case insensitive mode.\n\n    *) Bugfix: now nginx/Windows ignores trailing dots in URI.\n       Thanks to Hugo Leisink.\n\n    *) Bugfix: name of file specified in --conf-path was not honored during\n       installation; the bug had appeared in 0.6.6.\n       Thanks to Maxim Dounin.\n\n\nChanges with nginx 0.8.5                                         13 Jul 2009\n\n    *) Bugfix: now nginx allows underscores in a request method.\n\n    *) Bugfix: a 500 error code was returned for invalid login/password\n       while HTTP Basic authentication on Windows.\n\n    *) Bugfix: ngx_http_perl_module responses did not work in subrequests.\n\n    *) Bugfix: in ngx_http_limit_req_module.\n       Thanks to Maxim Dounin.\n\n\nChanges with nginx 0.8.4                                         22 Jun 2009\n\n    *) Bugfix: nginx could not be built --without-http-cache; the bug had\n       appeared in 0.8.3.\n\n\nChanges with nginx 0.8.3                                         19 Jun 2009\n\n    *) Feature: the $upstream_cache_status variable.\n\n    *) Bugfix: nginx could not be built on MacOSX 10.6.\n\n    *) Bugfix: nginx could not be built --without-http-cache; the bug had\n       appeared in 0.8.2.\n\n    *) Bugfix: a segmentation fault occurred in worker process, if a backend\n       401 error was intercepted and the backend did not set the\n       \"WWW-Authenticate\" response header line.\n       Thanks to Eugene Mychlo.\n\n\nChanges with nginx 0.8.2                                         15 Jun 2009\n\n    *) Bugfix: in open_file_cache and proxy/fastcgi cache interaction on\n       start up.\n\n    *) Bugfix: open_file_cache might cache open file descriptors too long;\n       the bug had appeared in 0.7.4.\n\n\nChanges with nginx 0.8.1                                         08 Jun 2009\n\n    *) Feature: the \"updating\" parameter in \"proxy_cache_use_stale\" and\n       \"fastcgi_cache_use_stale\" directives.\n\n    *) Bugfix: the \"If-Modified-Since\", \"If-Range\", etc. client request\n       header lines were passed to backend while caching if no\n       \"proxy_set_header\" directive was used with any parameters.\n\n    *) Bugfix: the \"Set-Cookie\" and \"P3P\" response header lines were not\n       hidden while caching if no \"proxy_hide_header/fastcgi_hide_header\"\n       directives were used with any parameters.\n\n    *) Bugfix: the ngx_http_image_filter_module did not support GIF87a\n       format.\n       Thanks to Denis Ilyinyh.\n\n    *) Bugfix: nginx could not be built modules on Solaris 10 and early; the\n       bug had appeared in 0.7.56.\n\n\nChanges with nginx 0.8.0                                         02 Jun 2009\n\n    *) Feature: the \"keepalive_requests\" directive.\n\n    *) Feature: the \"limit_rate_after\" directive.\n       Thanks to Ivan Debnar.\n\n    *) Bugfix: XLST filter did not work in subrequests.\n\n    *) Bugfix: in relative paths handling in nginx/Windows.\n\n    *) Bugfix: in proxy_store, fastcgi_store, proxy_cache, and fastcgi_cache\n       in nginx/Windows.\n\n    *) Bugfix: in memory allocation error handling.\n       Thanks to Maxim Dounin and Kirill A. Korinskiy.\n\n\nChanges with nginx 0.7.59                                        25 May 2009\n\n    *) Feature: the \"proxy_cache_methods\" and \"fastcgi_cache_methods\"\n       directives.\n\n    *) Bugfix: socket leak; the bug had appeared in 0.7.25.\n       Thanks to Maxim Dounin.\n\n    *) Bugfix: a segmentation fault occurred in worker process, if a request\n       had no body and the $request_body variable was used;\n       the bug had appeared in 0.7.58.\n\n    *) Bugfix: the SSL modules might not built on Solaris and Linux;\n       the bug had appeared in 0.7.56.\n\n    *) Bugfix: ngx_http_xslt_filter_module responses were not handled by\n       SSI, charset, and gzip filters.\n\n    *) Bugfix: a \"charset\" directive did not set a charset to\n       ngx_http_gzip_static_module responses.\n\n\nChanges with nginx 0.7.58                                        18 May 2009\n\n    *) Feature: a \"listen\" directive of the mail proxy module supports IPv6.\n\n    *) Feature: the \"image_filter_jpeg_quality\" directive.\n\n    *) Feature: the \"client_body_in_single_buffer\" directive.\n\n    *) Feature: the $request_body variable.\n\n    *) Bugfix: in ngx_http_autoindex_module in file name links having a \":\"\n       symbol in the name.\n\n    *) Bugfix: \"make upgrade\" procedure did not work; the bug had appeared\n       in 0.7.53.\n       Thanks to Denis F. Latypoff.\n\n\nChanges with nginx 0.7.57                                        12 May 2009\n\n    *) Bugfix: a floating-point fault occurred in worker process, if the\n       ngx_http_image_filter_module errors were redirected to named\n       location; the bug had appeared in 0.7.56.\n\n\nChanges with nginx 0.7.56                                        11 May 2009\n\n    *) Feature: nginx/Windows supports IPv6 in a \"listen\" directive of the\n       HTTP module.\n\n    *) Bugfix: in ngx_http_image_filter_module.\n\n\nChanges with nginx 0.7.55                                        06 May 2009\n\n    *) Bugfix: the http_XXX parameters in \"proxy_cache_use_stale\" and\n       \"fastcgi_cache_use_stale\" directives did not work.\n\n    *) Bugfix: fastcgi cache did not cache header only responses.\n\n    *) Bugfix: of \"select() failed (9: Bad file descriptor)\" error in\n       nginx/Unix and \"select() failed (10038: ...)\" error in nginx/Windows.\n\n    *) Bugfix: a segmentation fault might occur in worker process, if an\n       \"debug_connection\" directive was used; the bug had appeared in\n       0.7.54.\n\n    *) Bugfix: fix ngx_http_image_filter_module building errors.\n\n    *) Bugfix: the files bigger than 2G could not be transferred using\n       $r->sendfile.\n       Thanks to Maxim Dounin.\n\n\nChanges with nginx 0.7.54                                        01 May 2009\n\n    *) Feature: the ngx_http_image_filter_module.\n\n    *) Feature: the \"proxy_ignore_headers\" and \"fastcgi_ignore_headers\"\n       directives.\n\n    *) Bugfix: a segmentation fault might occur in worker process, if an\n       \"open_file_cache_errors off\" directive was used; the bug had appeared\n       in 0.7.53.\n\n    *) Bugfix: the \"port_in_redirect off\" directive did not work; the bug\n       had appeared in 0.7.39.\n\n    *) Bugfix: improve handling of \"select\" method errors.\n\n    *) Bugfix: of \"select() failed (10022: ...)\" error in nginx/Windows.\n\n    *) Bugfix: in error text descriptions in nginx/Windows; the bug had\n       appeared in 0.7.53.\n\n\nChanges with nginx 0.7.53                                        27 Apr 2009\n\n    *) Change: now a log set by --error-log-path is created from the very\n       start-up.\n\n    *) Feature: now the start up errors and warnings are outputted to an\n       error_log and stderr.\n\n    *) Feature: the empty --prefix= configure parameter forces nginx to use\n       a directory where it was run as prefix.\n\n    *) Feature: the -p switch.\n\n    *) Feature: the -s switch on Unix platforms.\n\n    *) Feature: the -? and -h switches.\n       Thanks to Jerome Loyet.\n\n    *) Feature: now switches may be set in condensed form.\n\n    *) Bugfix: nginx/Windows did not work if configuration file was given by\n       the -c switch.\n\n    *) Bugfix: temporary files might be not removed if the \"proxy_store\",\n       \"fastcgi_store\", \"proxy_cache\", or \"fastcgi_cache\" were used.\n       Thanks to Maxim Dounin.\n\n    *) Bugfix: an incorrect value was passed to mail proxy authentication\n       server in \"Auth-Method\" header line; the bug had appeared\n       in 0.7.34.\n       Thanks to Simon Lecaille.\n\n    *) Bugfix: system error text descriptions were not logged on Linux;\n       the bug had appeared in 0.7.45.\n\n    *) Bugfix: the \"fastcgi_cache_min_uses\" directive did not work.\n       Thanks to Andrew Vorobyoff.\n\n\nChanges with nginx 0.7.52                                        20 Apr 2009\n\n    *) Feature: the first native Windows binary release.\n\n    *) Bugfix: in processing HEAD method while caching.\n\n    *) Bugfix: in processing the \"If-Modified-Since\", \"If-Range\", etc.\n       client request header lines while caching.\n\n    *) Bugfix: now the \"Set-Cookie\" and \"P3P\" header lines are hidden in\n       cacheable responses.\n\n    *) Bugfix: if nginx was built with the ngx_http_perl_module and with a\n       perl which supports threads, then during a master process exit the\n       message \"panic: MUTEX_LOCK\" might be issued.\n\n    *) Bugfix: nginx could not be built --without-http-cache; the bug had\n       appeared in 0.7.48.\n\n    *) Bugfix: nginx could not be built on platforms different from i386,\n       amd64, sparc, and ppc; the bug had appeared in 0.7.42.\n\n\nChanges with nginx 0.7.51                                        12 Apr 2009\n\n    *) Feature: the \"try_files\" directive supports a response code in the\n       fallback parameter.\n\n    *) Feature: now any response code can be used in the \"return\" directive.\n\n    *) Bugfix: the \"error_page\" directive made an external redirect without\n       query string; the bug had appeared in 0.7.44.\n\n    *) Bugfix: if servers listened on several defined explicitly addresses,\n       then virtual servers might not work; the bug had appeared in 0.7.39.\n\n\nChanges with nginx 0.7.50                                        06 Apr 2009\n\n    *) Bugfix: the $arg_... variables did not work; the bug had appeared in\n       0.7.49.\n\n\nChanges with nginx 0.7.49                                        06 Apr 2009\n\n    *) Bugfix: a segmentation fault might occur in worker process, if the\n       $arg_... variables were used; the bug had appeared in 0.7.48.\n\n\nChanges with nginx 0.7.48                                        06 Apr 2009\n\n    *) Feature: the \"proxy_cache_key\" directive.\n\n    *) Bugfix: now nginx takes into account the \"X-Accel-Expires\",\n       \"Expires\", and \"Cache-Control\" header lines in a backend response.\n\n    *) Bugfix: now nginx caches responses for the GET requests only.\n\n    *) Bugfix: the \"fastcgi_cache_key\" directive was not inherited.\n\n    *) Bugfix: the $arg_... variables did not work with SSI subrequests.\n       Thanks to Maxim Dounin.\n\n    *) Bugfix: nginx could not be built with uclibc library.\n       Thanks to Timothy Redaelli.\n\n    *) Bugfix: nginx could not be built on OpenBSD; the bug had\n       appeared in 0.7.46.\n\n\nChanges with nginx 0.7.47                                        01 Apr 2009\n\n    *) Bugfix: nginx could not be built on FreeBSD 6 and early versions; the\n       bug had appeared in 0.7.46.\n\n    *) Bugfix: nginx could not be built on MacOSX; the bug had\n       appeared in 0.7.46.\n\n    *) Bugfix: if the \"max_size\" parameter was set, then the cache manager\n       might purge a whole cache; the bug had appeared in 0.7.46.\n\n    *) Change: a segmentation fault might occur in worker process, if the\n       \"proxy_cache\"/\"fastcgi_cache\" and the \"proxy_cache_valid\"/\n       \"fastcgi_cache_valid\" were set on different levels; the bug had\n       appeared in 0.7.46.\n\n    *) Bugfix: a segmentation fault might occur in worker process, if a\n       request was redirected to a proxied or FastCGI server via error_page\n       or try_files; the bug had appeared in 0.7.44.\n\n\nChanges with nginx 0.7.46                                        30 Mar 2009\n\n    *) Bugfix: the previous release tarball was incorrect.\n\n\nChanges with nginx 0.7.45                                        30 Mar 2009\n\n    *) Change: now the \"proxy_cache\" and the \"proxy_cache_valid\" directives\n       can be set on different levels.\n\n    *) Change: the \"clean_time\" parameter of the \"proxy_cache_path\"\n       directive is canceled.\n\n    *) Feature: the \"max_size\" parameter of the \"proxy_cache_path\"\n       directive.\n\n    *) Feature: the ngx_http_fastcgi_module preliminary cache support.\n\n    *) Feature: now on shared memory allocation errors directive and zone\n       names are logged.\n\n    *) Bugfix: the directive \"add_header last-modified ''\" did not delete a\n       \"Last-Modified\" response header line; the bug had appeared in 0.7.44.\n\n    *) Bugfix: a relative path in the \"auth_basic_user_file\" directive given\n       without variables did not work; the bug had appeared in 0.7.44.\n       Thanks to Jerome Loyet.\n\n    *) Bugfix: in an \"alias\" directive given using variables without\n       references to captures of regular expressions; the bug had appeared\n       in 0.7.42.\n\n\nChanges with nginx 0.7.44                                        23 Mar 2009\n\n    *) Feature: the ngx_http_proxy_module preliminary cache support.\n\n    *) Feature: the --with-pcre option in the configure.\n\n    *) Feature: the \"try_files\" directive is now allowed on the server block\n       level.\n\n    *) Bugfix: the \"try_files\" directive handled incorrectly a query string\n       in a fallback parameter.\n\n    *) Bugfix: the \"try_files\" directive might test incorrectly directories.\n\n    *) Bugfix: if there was a single server for given address:port pair,\n       then captures in regular expressions in a \"server_name\" directive did\n       not work.\n\n\nChanges with nginx 0.7.43                                        18 Mar 2009\n\n    *) Bugfix: a request was handled incorrectly, if a \"root\" directive used\n       variables; the bug had appeared in 0.7.42.\n\n    *) Bugfix: if a server listened on wildcard address, then the\n       $server_addr variable value was \"0.0.0.0\"; the bug had appeared in\n       0.7.36.\n\n\nChanges with nginx 0.7.42                                        16 Mar 2009\n\n    *) Change: now the \"Invalid argument\" error returned by\n       setsockopt(TCP_NODELAY) on Solaris, is ignored.\n\n    *) Change: now if a file specified in a \"auth_basic_user_file\" directive\n       is absent, then the 403 error is returned instead of the 500 one.\n\n    *) Feature: the \"auth_basic_user_file\" directive supports variables.\n       Thanks to Kirill A. Korinskiy.\n\n    *) Feature: the \"listen\" directive supports the \"ipv6only\" parameter.\n       Thanks to Zhang Hua.\n\n    *) Bugfix: in an \"alias\" directive with references to captures of\n       regular expressions; the bug had appeared in 0.7.40.\n\n    *) Bugfix: compatibility with Tru64 UNIX.\n       Thanks to Dustin Marquess.\n\n    *) Bugfix: nginx could not be built without PCRE library; the bug had\n       appeared in 0.7.41.\n\n\nChanges with nginx 0.7.41                                        11 Mar 2009\n\n    *) Bugfix: a segmentation fault might occur in worker process, if a\n       \"server_name\" or a \"location\" directives had captures in regular\n       expressions; the issue had appeared in 0.7.40.\n       Thanks to Vladimir Sopot.\n\n\nChanges with nginx 0.7.40                                        09 Mar 2009\n\n    *) Feature: the \"location\" directive supports captures in regular\n       expressions.\n\n    *) Feature: an \"alias\" directive with capture references may be used\n       inside a location given by a regular expression with captures.\n\n    *) Feature: the \"server_name\" directive supports captures in regular\n       expressions.\n\n    *) Workaround: the ngx_http_autoindex_module did not show the trailing\n       slash in directories on XFS filesystem; the issue had appeared in\n       0.7.15.\n       Thanks to Dmitry Kuzmenko.\n\n\nChanges with nginx 0.7.39                                        02 Mar 2009\n\n    *) Bugfix: large response with SSI might hang, if gzipping was enabled;\n       the bug had appeared in 0.7.28.\n       Thanks to Artem Bokhan.\n\n    *) Bugfix: a segmentation fault might occur in worker process, if short\n       static variants are used in a \"try_files\" directive.\n\n\nChanges with nginx 0.7.38                                        23 Feb 2009\n\n    *) Feature: authentication failures logging.\n\n    *) Bugfix: name/password in auth_basic_user_file were ignored after odd\n       number of empty lines.\n       Thanks to Alexander Zagrebin.\n\n    *) Bugfix: a segmentation fault occurred in a master process, if long\n       path was used in unix domain socket; the bug had appeared in 0.7.36.\n\n\nChanges with nginx 0.7.37                                        21 Feb 2009\n\n    *) Bugfix: directives using upstreams did not work; the bug had appeared\n       in 0.7.36.\n\n\nChanges with nginx 0.7.36                                        21 Feb 2009\n\n    *) Feature: a preliminary IPv6 support; the \"listen\" directive of the\n       HTTP module supports IPv6.\n\n    *) Bugfix: the $ancient_browser variable did not work for browsers\n       preset by a \"modern_browser\" directives.\n\n\nChanges with nginx 0.7.35                                        16 Feb 2009\n\n    *) Bugfix: a \"ssl_engine\" directive did not use a SSL-accelerator for\n       asymmetric ciphers.\n       Thanks to Marcin Gozdalik.\n\n    *) Bugfix: a \"try_files\" directive set MIME type depending on an\n       original request extension.\n\n    *) Bugfix: \"*domain.tld\" names were handled incorrectly in\n       \"server_name\", \"valid_referers\", and \"map\" directives, if\n       \".domain.tld\" and \".subdomain.domain.tld\" wildcards were used;\n       the bug had appeared in 0.7.9.\n\n\nChanges with nginx 0.7.34                                        10 Feb 2009\n\n    *) Feature: the \"off\" parameter of the \"if_modified_since\" directive.\n\n    *) Feature: now nginx sends an HELO/EHLO command after a XCLIENT\n       command.\n       Thanks to Maxim Dounin.\n\n    *) Feature: Microsoft specific \"AUTH LOGIN with User Name\" mode support\n       in mail proxy server.\n       Thanks to Maxim Dounin.\n\n    *) Bugfix: in a redirect rewrite directive original arguments were\n       concatenated with new arguments by a \"?\" rather than an \"&\";\n       the bug had appeared in 0.1.18.\n       Thanks to Maxim Dounin.\n\n    *) Bugfix: nginx could not be built on AIX.\n\n\nChanges with nginx 0.7.33                                        02 Feb 2009\n\n    *) Bugfix: a double response might be returned if the epoll or rtsig\n       methods are used and a redirect was returned to a request with body.\n       Thanks to Eden Li.\n\n    *) Bugfix: the $sent_http_location variable was empty for some redirects\n       types.\n\n    *) Bugfix: a segmentation fault might occur in worker process if\n       \"resolver\" directive was used in SMTP proxy.\n\n\nChanges with nginx 0.7.32                                        26 Jan 2009\n\n    *) Feature: now a directory existence testing can be set explicitly in\n       the \"try_files\" directive.\n\n    *) Bugfix: fastcgi_store stored files not always.\n\n    *) Bugfix: in geo ranges.\n\n    *) Bugfix: in shared memory allocations if nginx was built without\n       debugging.\n       Thanks to Andrey Kvasov.\n\n\nChanges with nginx 0.7.31                                        19 Jan 2009\n\n    *) Change: now the \"try_files\" directive tests files only and ignores\n       directories.\n\n    *) Feature: the \"fastcgi_split_path_info\" directive.\n\n    *) Bugfixes in an \"Expect\" request header line support.\n\n    *) Bugfixes in geo ranges.\n\n    *) Bugfix: in a miss case ngx_http_memcached_module returned the \"END\"\n       line as response body instead of default 404 page body; the bug had\n       appeared in 0.7.18.\n       Thanks to Maxim Dounin.\n\n    *) Bugfix: while SMTP proxying nginx issued message \"250 2.0.0 OK\"\n       instead of \"235 2.0.0 OK\"; the bug had appeared in 0.7.22.\n       Thanks to Maxim Dounin.\n\n\nChanges with nginx 0.7.30                                        24 Dec 2008\n\n    *) Bugfix: a segmentation fault occurred in worker process, if variables\n       were used in the \"fastcgi_pass\" or \"proxy_pass\" directives and host\n       name must be resolved; the bug had appeared in 0.7.29.\n\n\nChanges with nginx 0.7.29                                        24 Dec 2008\n\n    *) Bugfix: the \"fastcgi_pass\" and \"proxy_pass\" directives did not\n       support variables if unix domain sockets were used.\n\n    *) Bugfixes in subrequest processing; the bugs had appeared in 0.7.25.\n\n    *) Bugfix: a \"100 Continue\" response was issued for HTTP/1.0 requests;\n       Thanks to Maxim Dounin.\n\n    *) Bugfix: in memory allocation in the ngx_http_gzip_filter_module on\n       Cygwin.\n\n\nChanges with nginx 0.7.28                                        22 Dec 2008\n\n    *) Change: in memory allocation in the ngx_http_gzip_filter_module.\n\n    *) Change: the default \"gzip_buffers\" directive values have been changed\n       to 32 4k or 16 8k from 4 4k/8k.\n\n\nChanges with nginx 0.7.27                                        15 Dec 2008\n\n    *) Feature: the \"try_files\" directive.\n\n    *) Feature: variables support in the \"fastcgi_pass\" directive.\n\n    *) Feature: now the $geo variable may get an address from a variable.\n       Thanks to Andrei Nigmatulin.\n\n    *) Feature: now a location's modifier may be used without space before\n       name.\n\n    *) Feature: the $upstream_response_length variable.\n\n    *) Bugfix: now a \"add_header\" directive does not add an empty value.\n\n    *) Bugfix: if zero length static file was requested, then nginx just\n       closed connection; the bug had appeared in 0.7.25.\n\n    *) Bugfix: a MOVE method could not move file in non-existent directory.\n\n    *) Bugfix: a segmentation fault occurred in worker process, if no one\n       named location was defined in server, but some one was used in an\n       error_page directive.\n       Thanks to Sergey Bochenkov.\n\n\nChanges with nginx 0.7.26                                        08 Dec 2008\n\n    *) Bugfix: in subrequest processing; the bug had appeared in 0.7.25.\n\n\nChanges with nginx 0.7.25                                        08 Dec 2008\n\n    *) Change: in subrequest processing.\n\n    *) Change: now POSTs without \"Content-Length\" header line are allowed.\n\n    *) Bugfix: now the \"limit_req\" and \"limit_conn\" directives log a\n       prohibition reason.\n\n    *) Bugfix: in the \"delete\" parameter of the \"geo\" directive.\n\n\nChanges with nginx 0.7.24                                        01 Dec 2008\n\n    *) Feature: the \"if_modified_since\" directive.\n\n    *) Bugfix: nginx did not process a FastCGI server response, if the\n       server send too many messages to stderr before response.\n\n    *) Bugfix: the \"$cookie_...\" variables did not work in the SSI and the\n       perl module.\n\n\nChanges with nginx 0.7.23                                        27 Nov 2008\n\n    *) Feature: the \"delete\" and \"ranges\" parameters in the \"geo\" directive.\n\n    *) Feature: speeding up loading of geo base with large number of values.\n\n    *) Feature: decrease of memory required for geo base load.\n\n\nChanges with nginx 0.7.22                                        20 Nov 2008\n\n    *) Feature: the \"none\" parameter in the \"smtp_auth\" directive.\n       Thanks to Maxim Dounin.\n\n    *) Feature: the \"$cookie_...\" variables.\n\n    *) Bugfix: the \"directio\" directive did not work in XFS filesystem.\n\n    *) Bugfix: the resolver did not understand big DNS responses.\n       Thanks to Zyb.\n\n\nChanges with nginx 0.7.21                                        11 Nov 2008\n\n    *) Changes in the ngx_http_limit_req_module.\n\n    *) Feature: the EXSLT support in the ngx_http_xslt_module.\n       Thanks to Denis F. Latypoff.\n\n    *) Workaround: compatibility with glibc 2.3.\n       Thanks to Eric Benson and Maxim Dounin.\n\n    *) Bugfix: nginx could not run on MacOSX 10.4 and earlier; the bug had\n       appeared in 0.7.6.\n\n\nChanges with nginx 0.7.20                                        10 Nov 2008\n\n    *) Changes in the ngx_http_gzip_filter_module.\n\n    *) Feature: the ngx_http_limit_req_module.\n\n    *) Bugfix: worker processes might exit on a SIGBUS signal on sparc and\n       ppc platforms; the bug had appeared in 0.7.3.\n       Thanks to Maxim Dounin.\n\n    *) Bugfix: the \"proxy_pass http://host/some:uri\" directives did not\n       work; the bug had appeared in 0.7.12.\n\n    *) Bugfix: in HTTPS mode requests might fail with the \"bad write retry\"\n       error.\n\n    *) Bugfix: the ngx_http_secure_link_module did not work inside\n       locations, whose names are less than 3 characters.\n\n    *) Bugfix: $server_addr variable might have no value.\n\n\nChanges with nginx 0.7.19                                        13 Oct 2008\n\n    *) Bugfix: version number update.\n\n\nChanges with nginx 0.7.18                                        13 Oct 2008\n\n    *) Change: the \"underscores_in_headers\" directive; now nginx does not\n       allows underscores in a client request header line names.\n\n    *) Feature: the ngx_http_secure_link_module.\n\n    *) Feature: the \"real_ip_header\" directive supports any header.\n\n    *) Feature: the \"log_subrequest\" directive.\n\n    *) Feature: the $realpath_root variable.\n\n    *) Feature: the \"http_502\" and \"http_504\" parameters of the\n       \"proxy_next_upstream\" directive.\n\n    *) Bugfix: the \"http_503\" parameter of the \"proxy_next_upstream\" or\n       \"fastcgi_next_upstream\" directives did not work.\n\n    *) Bugfix: nginx might send a \"Transfer-Encoding: chunked\" header line\n       for HEAD requests.\n\n    *) Bugfix: now accept threshold depends on worker_connections.\n\n\nChanges with nginx 0.7.17                                        15 Sep 2008\n\n    *) Feature: now the \"directio\" directive works on Linux.\n\n    *) Feature: the $pid variable.\n\n    *) Bugfix: the \"directio\" optimization that had appeared in 0.7.15 did\n       not work with open_file_cache.\n\n    *) Bugfix: the \"access_log\" with variables did not work on Linux; the\n       bug had appeared in 0.7.7.\n\n    *) Bugfix: the ngx_http_charset_module did not understand quoted charset\n       name received from backend.\n\n\nChanges with nginx 0.7.16                                        08 Sep 2008\n\n    *) Bugfix: nginx could not be built on 64-bit platforms; the bug had\n       appeared in 0.7.15.\n\n\nChanges with nginx 0.7.15                                        08 Sep 2008\n\n    *) Feature: the ngx_http_random_index_module.\n\n    *) Feature: the \"directio\" directive has been optimized for file\n       requests starting from arbitrary position.\n\n    *) Feature: the \"directio\" directive turns off sendfile if it is\n       necessary.\n\n    *) Feature: now nginx allows underscores in a client request header line\n       names.\n\n\nChanges with nginx 0.7.14                                        01 Sep 2008\n\n    *) Change: now the ssl_certificate and ssl_certificate_key directives\n       have no default values.\n\n    *) Feature: the \"listen\" directive supports the \"ssl\" parameter.\n\n    *) Feature: now nginx takes into account a time zone change while\n       reconfiguration on FreeBSD and Linux.\n\n    *) Bugfix: the \"listen\" directive parameters such as \"backlog\",\n       \"rcvbuf\", etc. were not set, if a default server was not the first\n       one.\n\n    *) Bugfix: if URI part captured by a \"rewrite\" directive was used as a\n       query string, then the query string was not escaped.\n\n    *) Bugfix: configuration file validity test improvements.\n\n\nChanges with nginx 0.7.13                                        26 Aug 2008\n\n    *) Bugfix: nginx could not be built on Linux and Solaris; the bug had\n       appeared in 0.7.12.\n\n\nChanges with nginx 0.7.12                                        26 Aug 2008\n\n    *) Feature: the \"server_name\" directive supports empty name \"\".\n\n    *) Feature: the \"gzip_disable\" directive supports special \"msie6\" mask.\n\n    *) Bugfix: if the \"max_fails=0\" parameter was used in upstream with\n       several servers, then a worker process exited on a SIGFPE signal.\n       Thanks to Maxim Dounin.\n\n    *) Bugfix: a request body was dropped while redirection via an\n       \"error_page\" directive.\n\n    *) Bugfix: a full response was returned for request method HEAD while\n       redirection via an \"error_page\" directive.\n\n    *) Bugfix: the $r->header_in() method did not return value of the\n       \"Host\", \"User-Agent\", and \"Connection\" request header lines; the bug\n       had appeared in 0.7.0.\n\n\nChanges with nginx 0.7.11                                        18 Aug 2008\n\n    *) Change: now ngx_http_charset_module does not work by default with\n       text/css MIME type.\n\n    *) Feature: now nginx returns the 405 status code for POST method\n       requesting a static file only if the file exists.\n\n    *) Feature: the \"proxy_ssl_session_reuse\" directive.\n\n    *) Bugfix: a \"proxy_pass\" directive without URI part might use original\n       request after the \"X-Accel-Redirect\" redirection was used.\n\n    *) Bugfix: if a directory has search only rights and the first index\n       file was absent, then nginx returned the 500 status code.\n\n    *) Bugfix: in inclusive locations; the bugs had appeared in 0.7.1.\n\n\nChanges with nginx 0.7.10                                        13 Aug 2008\n\n    *) Bugfix: in the \"addition_types\", \"charset_types\", \"gzip_types\",\n       \"ssi_types\", \"sub_filter_types\", and \"xslt_types\" directives; the\n       bugs had appeared in 0.7.9.\n\n    *) Bugfix: of recursive error_page for 500 status code.\n\n    *) Bugfix: now the ngx_http_realip_module sets address not for whole\n       keepalive connection, but for each request passed via the connection.\n\n\nChanges with nginx 0.7.9                                         12 Aug 2008\n\n    *) Change: now ngx_http_charset_module works by default with following\n       MIME types: text/html, text/css, text/xml, text/plain,\n       text/vnd.wap.wml, application/x-javascript, and application/rss+xml.\n\n    *) Feature: the \"charset_types\" and \"addition_types\" directives.\n\n    *) Feature: now the \"gzip_types\", \"ssi_types\", and \"sub_filter_types\"\n       directives use hash.\n\n    *) Feature: the ngx_cpp_test_module.\n\n    *) Feature: the \"expires\" directive supports daily time.\n\n    *) Feature: the ngx_http_xslt_module improvements and bug fixing.\n       Thanks to Denis F. Latypoff and Maxim Dounin.\n\n    *) Bugfix: the \"log_not_found\" directive did not work for index files\n       tests.\n\n    *) Bugfix: HTTPS connections might hang, if kqueue, epoll, rtsig, or\n       eventport methods were used; the bug had appeared in 0.7.7.\n\n    *) Bugfix: if the \"server_name\", \"valid_referers\", and \"map\" directives\n       used an \"*.domain.tld\" wildcard and exact name \"domain.tld\" was not\n       set, then the exact name was matched by the wildcard; the bug had\n       appeared in 0.3.18.\n\n\nChanges with nginx 0.7.8                                         04 Aug 2008\n\n    *) Feature: the ngx_http_xslt_module.\n\n    *) Feature: the \"$arg_...\" variables.\n\n    *) Feature: Solaris directio support.\n       Thanks to Ivan Debnar.\n\n    *) Bugfix: now if FastCGI server sends a \"Location\" header line without\n       status line, then nginx uses 302 status code.\n       Thanks to Maxim Dounin.\n\n\nChanges with nginx 0.7.7                                         30 Jul 2008\n\n    *) Change: now the EAGAIN error returned by connect() is not considered\n       as temporary error.\n\n    *) Change: now the $ssl_client_cert variable value is a certificate with\n       TAB character intended before each line except first one; an\n       unchanged certificate is available in the $ssl_client_raw_cert\n       variable.\n\n    *) Feature: the \"ask\" parameter in the \"ssl_verify_client\" directive.\n\n    *) Feature: byte-range processing improvements.\n       Thanks to Maxim Dounin.\n\n    *) Feature: the \"directio\" directive.\n       Thanks to Jiang Hong.\n\n    *) Feature: MacOSX 10.5 sendfile() support.\n\n    *) Bugfix: now in MacOSX and Cygwin locations are tested in case\n       insensitive mode; however, the compare is provided by single-byte\n       locales only.\n\n    *) Bugfix: mail proxy SSL connections hanged, if select, poll, or\n       /dev/poll methods were used.\n\n    *) Bugfix: UTF-8 encoding usage in the ngx_http_autoindex_module.\n\n\nChanges with nginx 0.7.6                                         07 Jul 2008\n\n    *) Bugfix: now if variables are used in the \"access_log\" directive a\n       request root existence is always tested.\n\n    *) Bugfix: the ngx_http_flv_module did not support several values in a\n       query string.\n\n\nChanges with nginx 0.7.5                                         01 Jul 2008\n\n    *) Bugfixes in variables support in the \"access_log\" directive; the bugs\n       had appeared in 0.7.4.\n\n    *) Bugfix: nginx could not be built --without-http_gzip_module; the bug\n       had appeared in 0.7.3.\n       Thanks to Kirill A. Korinskiy.\n\n    *) Bugfix: if sub_filter and SSI were used together, then responses\n       might were transferred incorrectly.\n\n\nChanges with nginx 0.7.4                                         30 Jun 2008\n\n    *) Feature: variables support in the \"access_log\" directive.\n\n    *) Feature: the \"open_log_file_cache\" directive.\n\n    *) Feature: the -g switch.\n\n    *) Feature: the \"Expect\" request header line support.\n\n    *) Bugfix: large SSI inclusions might be truncated.\n\n\nChanges with nginx 0.7.3                                         23 Jun 2008\n\n    *) Change: the \"rss\" extension MIME type has been changed to\n       \"application/rss+xml\".\n\n    *) Change: now the \"gzip_vary\" directive turned on issues a\n       \"Vary: Accept-Encoding\" header line for uncompressed responses too.\n\n    *) Feature: now the \"rewrite\" directive does a redirect automatically if\n       the \"https://\" protocol is used.\n\n    *) Bugfix: the \"proxy_pass\" directive did not work with the HTTPS\n       protocol; the bug had appeared in 0.6.9.\n\n\nChanges with nginx 0.7.2                                         16 Jun 2008\n\n    *) Feature: now nginx supports EDH key exchange ciphers.\n\n    *) Feature: the \"ssl_dhparam\" directive.\n\n    *) Feature: the $ssl_client_cert variable.\n       Thanks to Manlio Perillo.\n\n    *) Bugfix: after changing URI via a \"rewrite\" directive nginx did not\n       search a new location; the bug had appeared in 0.7.1.\n       Thanks to Maxim Dounin.\n\n    *) Bugfix: nginx could not be built without PCRE library; the bug had\n       appeared in 0.7.1.\n\n    *) Bugfix: when a request to a directory was redirected with the slash\n       added, nginx dropped a query string from the original request.\n\n\nChanges with nginx 0.7.1                                         26 May 2008\n\n    *) Change: now locations are searched in a tree.\n\n    *) Change: the \"optimize_server_names\" directive was canceled due to the\n       \"server_name_in_redirect\" directive introduction.\n\n    *) Change: some long deprecated directives are not supported anymore.\n\n    *) Change: the \"none\" parameter in the \"ssl_session_cache\" directive;\n       now this is default parameter.\n       Thanks to Rob Mueller.\n\n    *) Bugfix: worker processes might not catch reconfiguration and log\n       rotation signals.\n\n    *) Bugfix: nginx could not be built on latest Fedora 9 Linux.\n       Thanks to Roxis.\n\n\nChanges with nginx 0.7.0                                         19 May 2008\n\n    *) Change: now the 0x00-0x1F, '\"' and '\\' characters are escaped as \\xXX\n       in an access_log.\n       Thanks to Maxim Dounin.\n\n    *) Change: now nginx allows several \"Host\" request header line.\n\n    *) Feature: the \"modified\" flag in the \"expires\" directive.\n\n    *) Feature: the $uid_got and $uid_set variables may be used at any\n       request processing stage.\n\n    *) Feature: the $hostname variable.\n       Thanks to Andrei Nigmatulin.\n\n    *) Feature: DESTDIR support.\n       Thanks to Todd A. Fisher and Andras Voroskoi.\n\n    *) Bugfix: a segmentation fault might occur in worker process on Linux,\n       if keepalive was enabled.\n\n\nChanges with nginx 0.6.31                                        12 May 2008\n\n    *) Bugfix: nginx did not process FastCGI response if header was at the\n       end of FastCGI record; the bug had appeared in 0.6.2.\n       Thanks to Sergey Serov.\n\n    *) Bugfix: a segmentation fault might occur in worker process if a file\n       was deleted and the \"open_file_cache_errors\" directive was off.\n\n\nChanges with nginx 0.6.30                                        29 Apr 2008\n\n    *) Change: now if an \"include\" directive pattern does not match any\n       file, then nginx does not issue an error.\n\n    *) Feature: now the time in directives may be specified without spaces,\n       for example, \"1h50m\".\n\n    *) Bugfix: memory leaks if the \"ssl_verify_client\" directive was on.\n       Thanks to Chavelle Vincent.\n\n    *) Bugfix: the \"sub_filter\" directive might set text to change into\n       output.\n\n    *) Bugfix: the \"error_page\" directive did not take into account\n       arguments in redirected URI.\n\n    *) Bugfix: now nginx always opens files in binary mode under Cygwin.\n\n    *) Bugfix: nginx could not be built on OpenBSD; the bug had appeared in\n       0.6.15.\n\n\nChanges with nginx 0.6.29                                        18 Mar 2008\n\n    *) Feature: the ngx_google_perftools_module.\n\n    *) Bugfix: the ngx_http_perl_module could not be built on 64-bit\n       platforms; the bug had appeared in 0.6.27.\n\n\nChanges with nginx 0.6.28                                        13 Mar 2008\n\n    *) Bugfix: the rtsig method could not be built; the bug had appeared in\n       0.6.27.\n\n\nChanges with nginx 0.6.27                                        12 Mar 2008\n\n    *) Change: now by default the rtsig method is not built on\n       Linux 2.6.18+.\n\n    *) Change: now a request method is not changed while redirection to a\n       named location via an \"error_page\" directive.\n\n    *) Feature: the \"resolver\" and \"resolver_timeout\" directives in SMTP\n       proxy.\n\n    *) Feature: the \"post_action\" directive supports named locations.\n\n    *) Bugfix: a segmentation fault occurred in worker process, if a request\n       was redirected from proxy, FastCGI, or memcached location to static\n       named locations.\n\n    *) Bugfix: browsers did not repeat SSL handshake if there is no valid\n       client certificate in first handshake.\n       Thanks to Alexander V. Inyukhin.\n\n    *) Bugfix: if response code 495-497 was redirected via an \"error_page\"\n       directive without code change, then nginx tried to allocate too many\n       memory.\n\n    *) Bugfix: memory leak in long-lived non buffered connections.\n\n    *) Bugfix: memory leak in resolver.\n\n    *) Bugfix: a segmentation fault occurred in worker process, if a request\n       was redirected from proxy, FastCGI, or memcached location to static\n       named locations.\n\n    *) Bugfix: in the $proxy_host and $proxy_port variables caching.\n       Thanks to Sergey Bochenkov.\n\n    *) Bugfix: a \"proxy_pass\" directive with variables used incorrectly the\n       same port as in another \"proxy_pass\" directive with the same host\n       name and without variables.\n       Thanks to Sergey Bochenkov.\n\n    *) Bugfix: an alert \"sendmsg() failed (9: Bad file descriptor)\" on some\n       64-bit platforms while reconfiguration.\n\n    *) Bugfix: a segmentation fault occurred in worker process, if empty\n       stub block was used second time in SSI.\n\n    *) Bugfix: in copying URI part contained escaped symbols into arguments.\n\n\nChanges with nginx 0.6.26                                        11 Feb 2008\n\n    *) Bugfix: the \"proxy_store\" and \"fastcgi_store\" directives did not\n       check a response length.\n\n    *) Bugfix: a segmentation fault occurred in worker process, if big value\n       was used in a \"expires\" directive.\n       Thanks to Joaquin Cuenca Abela.\n\n    *) Bugfix: nginx incorrectly detected cache line size on Pentium 4.\n       Thanks to Gena Makhomed.\n\n    *) Bugfix: in proxied or FastCGI subrequests a client original method\n       was used instead of the GET method.\n\n    *) Bugfix: socket leak in HTTPS mode if deferred accept was used.\n       Thanks to Ben Maurer.\n\n    *) Bugfix: nginx issued the bogus error message \"SSL_shutdown() failed\n       (SSL: )\"; the bug had appeared in 0.6.23.\n\n    *) Bugfix: in HTTPS mode requests might fail with the \"bad write retry\"\n       error; the bug had appeared in 0.6.23.\n\n\nChanges with nginx 0.6.25                                        08 Jan 2008\n\n    *) Change: now the \"server_name_in_redirect\" directive is used instead\n       of the \"server_name\" directive's special \"*\" parameter.\n\n    *) Change: now wildcard and regex names can be used as main name in a\n       \"server_name\" directive.\n\n    *) Change: the \"satisfy_any\" directive was replaced by the \"satisfy\"\n       directive.\n\n    *) Workaround: old worker processes might hog CPU after reconfiguration\n       if they was run under Linux OpenVZ.\n\n    *) Feature: the \"min_delete_depth\" directive.\n\n    *) Bugfix: the COPY and MOVE methods did not work with single files.\n\n    *) Bugfix: the ngx_http_gzip_static_module did not allow the\n       ngx_http_dav_module to work; the bug had appeared in 0.6.23.\n\n    *) Bugfix: socket leak in HTTPS mode if deferred accept was used.\n       Thanks to Ben Maurer.\n\n    *) Bugfix: nginx could not be built without PCRE library; the bug had\n       appeared in 0.6.23.\n\n\nChanges with nginx 0.6.24                                        27 Dec 2007\n\n    *) Bugfix: a segmentation fault might occur in worker process if HTTPS\n       was used; the bug had appeared in 0.6.23.\n\n\nChanges with nginx 0.6.23                                        27 Dec 2007\n\n    *) Change: the \"off\" parameter in the \"ssl_session_cache\" directive; now\n       this is default parameter.\n\n    *) Change: the \"open_file_cache_retest\" directive was renamed to the\n       \"open_file_cache_valid\".\n\n    *) Feature: the \"open_file_cache_min_uses\" directive.\n\n    *) Feature: the ngx_http_gzip_static_module.\n\n    *) Feature: the \"gzip_disable\" directive.\n\n    *) Feature: the \"memcached_pass\" directive may be used inside the \"if\"\n       block.\n\n    *) Bugfix: a segmentation fault occurred in worker process, if the\n       \"memcached_pass\" and \"if\" directives were used in the same location.\n\n    *) Bugfix: if a \"satisfy_any on\" directive was used and not all access\n       and auth modules directives were set, then other given access and\n       auth directives were not tested;\n\n    *) Bugfix: regex parameters in a \"valid_referers\" directive were not\n       inherited from previous level.\n\n    *) Bugfix: a \"post_action\" directive did run if a request was completed\n       with 499 status code.\n\n    *) Bugfix: optimization of 16K buffer usage in a SSL connection.\n       Thanks to Ben Maurer.\n\n    *) Bugfix: the STARTTLS in SMTP mode did not work.\n       Thanks to Oleg Motienko.\n\n    *) Bugfix: in HTTPS mode requests might fail with the \"bad write retry\"\n       error; the bug had appeared in 0.5.13.\n\n\nChanges with nginx 0.6.22                                        19 Dec 2007\n\n    *) Change: now all ngx_http_perl_module methods return values copied to\n       perl's allocated memory.\n\n    *) Bugfix: if nginx was built with ngx_http_perl_module, the perl before\n       5.8.6 was used, and perl supported threads, then during\n       reconfiguration the master process aborted; the bug had appeared in\n       0.5.9.\n       Thanks to Boris Zhmurov.\n\n    *) Bugfix: the ngx_http_perl_module methods may get invalid values of\n       the regex captures.\n\n    *) Bugfix: a segmentation fault occurred in worker process, if the\n       $r->has_request_body() method was called for a request whose small\n       request body was already received.\n\n    *) Bugfix: large_client_header_buffers did not freed before going to\n       keep-alive state.\n       Thanks to Olexander Shtepa.\n\n    *) Bugfix: the last address was missed in the $upstream_addr variable;\n       the bug had appeared in 0.6.18.\n\n    *) Bugfix: the \"fastcgi_catch_stderr\" directive did return error code;\n       now it returns 502 code, that can be rerouted to a next server using\n       the \"fastcgi_next_upstream invalid_header\" directive.\n\n    *) Bugfix: a segmentation fault occurred in master process if the\n       \"fastcgi_catch_stderr\" directive was used; the bug had appeared in\n       0.6.10.\n       Thanks to Manlio Perillo.\n\n\nChanges with nginx 0.6.21                                        03 Dec 2007\n\n    *) Change: if variable values used in a \"proxy_pass\" directive contain\n       IP-addresses only, then a \"resolver\" directive is not mandatory.\n\n    *) Bugfix: a segmentation fault might occur in worker process if a\n       \"proxy_pass\" directive with URI-part was used; the bug had appeared\n       in 0.6.19.\n\n    *) Bugfix: if resolver was used on platform that does not support\n       kqueue, then nginx issued an alert \"name is out of response\".\n       Thanks to Andrei Nigmatulin.\n\n    *) Bugfix: if the $server_protocol was used in FastCGI parameters and a\n       request line length was near to the \"client_header_buffer_size\"\n       directive value, then nginx issued an alert \"fastcgi: the request\n       record is too big\".\n\n    *) Bugfix: if a plain text HTTP/0.9 version request was made to HTTPS\n       server, then nginx returned usual response.\n\n\nChanges with nginx 0.6.20                                        28 Nov 2007\n\n    *) Bugfix: a segmentation fault might occur in worker process if a\n       \"proxy_pass\" directive with URI-part was used; the bug had appeared\n       in 0.6.19.\n\n\nChanges with nginx 0.6.19                                        27 Nov 2007\n\n    *) Bugfix: the 0.6.18 version could not be built.\n\n\nChanges with nginx 0.6.18                                        27 Nov 2007\n\n    *) Change: now the ngx_http_userid_module adds start time microseconds\n       to the cookie field contains a pid value.\n\n    *) Change: now the full request line instead of URI only is written to\n       error_log.\n\n    *) Feature: variables support in the \"proxy_pass\" directive.\n\n    *) Feature: the \"resolver\" and \"resolver_timeout\" directives.\n\n    *) Feature: now the directive \"add_header last-modified ''\" deletes a\n       \"Last-Modified\" response header line.\n\n    *) Bugfix: the \"limit_rate\" directive did not allow to use full\n       throughput, even if limit value was very high.\n\n\nChanges with nginx 0.6.17                                        15 Nov 2007\n\n    *) Feature: the \"If-Range\" request header line support.\n       Thanks to Alexander V. Inyukhin.\n\n    *) Bugfix: URL double escaping in a redirect of the \"msie_refresh\"\n       directive; the bug had appeared in 0.6.4.\n\n    *) Bugfix: the \"autoindex\" directive did not work with the \"alias /\"\n       directive.\n\n    *) Bugfix: a segmentation fault might occur in worker process if\n       subrequests were used.\n\n    *) Bugfix: the big responses may be transferred truncated if SSL and\n       gzip were used.\n\n    *) Bugfix: the $status variable was equal to 0 if a proxied server\n       returned response in HTTP/0.9 version.\n\n\nChanges with nginx 0.6.16                                        29 Oct 2007\n\n    *) Change: now the uname(2) is used on Linux instead of procfs.\n       Thanks to Ilya Novikov.\n\n    *) Bugfix: if the \"?\" character was in a \"error_page\" directive, then it\n       was escaped in a proxied request; the bug had appeared in 0.6.11.\n\n    *) Bugfix: compatibility with mget.\n\n\nChanges with nginx 0.6.15                                        22 Oct 2007\n\n    *) Feature: Cygwin compatibility.\n       Thanks to Vladimir Kutakov.\n\n    *) Feature: the \"merge_slashes\" directive.\n\n    *) Feature: the \"gzip_vary\" directive.\n\n    *) Feature: the \"server_tokens\" directive.\n\n    *) Bugfix: nginx did not unescape URI in the \"include\" SSI command.\n\n    *) Bugfix: the segmentation fault was occurred on start or while\n       reconfiguration if variable was used in the \"charset\" or\n       \"source_charset\" directives.\n\n    *) Bugfix: nginx returned the 400 response on requests like\n       \"GET http://www.domain.com HTTP/1.0\".\n       Thanks to James Oakley.\n\n    *) Bugfix: if request with request body was redirected using the\n       \"error_page\" directive, then nginx tried to read the request body\n       again; the bug had appeared in 0.6.7.\n\n    *) Bugfix: a segmentation fault occurred in worker process if no\n       server_name was explicitly defined for server processing request; the\n       bug had appeared in 0.6.7.\n\n\nChanges with nginx 0.6.14                                        15 Oct 2007\n\n    *) Change: now by default the \"echo\" SSI command uses entity encoding.\n\n    *) Feature: the \"encoding\" parameter in the \"echo\" SSI command.\n\n    *) Feature: the \"access_log\" directive may be used inside the\n       \"limit_except\" block.\n\n    *) Bugfix: if all upstream servers were failed, then all servers had got\n       weight the was equal one until servers became alive; the bug had\n       appeared in 0.6.6.\n\n    *) Bugfix: a segmentation fault occurred in worker process if\n       $date_local and $date_gmt were used outside the\n       ngx_http_ssi_filter_module.\n\n    *) Bugfix: a segmentation fault might occur in worker process if debug\n       log was enabled.\n       Thanks to Andrei Nigmatulin.\n\n    *) Bugfix: ngx_http_memcached_module did not set\n       $upstream_response_time.\n       Thanks to Maxim Dounin.\n\n    *) Bugfix: a worker process may got caught in an endless loop, if the\n       memcached was used.\n\n    *) Bugfix: nginx supported low case only \"close\" and \"keep-alive\" values\n       in the \"Connection\" request header line; the bug had appeared in\n       0.6.11.\n\n    *) Bugfix: sub_filter did not work with empty substitution.\n\n    *) Bugfix: in sub_filter parsing.\n\n\nChanges with nginx 0.6.13                                        24 Sep 2007\n\n    *) Bugfix: nginx did not close directory file on HEAD request if\n       autoindex was used.\n       Thanks to Arkadiusz Patyk.\n\n\nChanges with nginx 0.6.12                                        21 Sep 2007\n\n    *) Change: mail proxy was split on three modules: pop3, imap and smtp.\n\n    *) Feature: the --without-mail_pop3_module, --without-mail_imap_module,\n       and --without-mail_smtp_module configuration parameters.\n\n    *) Feature: the \"smtp_greeting_delay\" and \"smtp_client_buffer\"\n       directives of the ngx_mail_smtp_module.\n\n    *) Bugfix: the trailing wildcards did not work; the bug had appeared in\n       0.6.9.\n\n    *) Bugfix: nginx could not start on Solaris if the shared PCRE library\n       located in non-standard place was used.\n\n    *) Bugfix: the \"proxy_hide_header\" and \"fastcgi_hide_header\" directives\n       did not hide response header lines whose name was longer than 32\n       characters.\n       Thanks to Manlio Perillo.\n\n\nChanges with nginx 0.6.11                                        11 Sep 2007\n\n    *) Bugfix: active connection counter always increased if mail proxy was\n       used.\n\n    *) Bugfix: if backend returned response header only using non-buffered\n       proxy, then nginx closed backend connection on timeout.\n\n    *) Bugfix: nginx did not support several \"Connection\" request header\n       lines.\n\n    *) Bugfix: if the \"max_fails\" was set for upstream server, then after\n       first failure server weight was always one; the bug had appeared in\n       0.6.6.\n\n\nChanges with nginx 0.6.10                                        03 Sep 2007\n\n    *) Feature: the \"open_file_cache\", \"open_file_cache_retest\", and\n       \"open_file_cache_errors\" directives.\n\n    *) Bugfix: socket leak; the bug had appeared in 0.6.7.\n\n    *) Bugfix: a charset set by the \"charset\" directive was not appended to\n       the \"Content-Type\" header set by $r->send_http_header().\n\n    *) Bugfix: a segmentation fault might occur in worker process if\n       /dev/poll method was used.\n\n\nChanges with nginx 0.6.9                                         28 Aug 2007\n\n    *) Bugfix: a worker process may got caught in an endless loop, if the\n       HTTPS protocol was used; the bug had appeared in 0.6.7.\n\n    *) Bugfix: if server listened on two addresses or ports and trailing\n       wildcard was used, then nginx did not run.\n\n    *) Bugfix: the \"ip_hash\" directive might incorrectly mark servers as\n       down.\n\n    *) Bugfix: nginx could not be built on amd64; the bug had appeared in\n       0.6.8.\n\n\nChanges with nginx 0.6.8                                         20 Aug 2007\n\n    *) Change: now nginx tries to set the \"worker_priority\",\n       \"worker_rlimit_nofile\", \"worker_rlimit_core\", and\n       \"worker_rlimit_sigpending\" without super-user privileges.\n\n    *) Change: now nginx escapes space and \"%\" in request to a mail proxy\n       authentication server.\n\n    *) Change: now nginx escapes \"%\" in $memcached_key variable.\n\n    *) Bugfix: nginx used path relative to configuration prefix for\n       non-absolute configuration file path specified in the \"-c\" key; the\n       bug had appeared in 0.6.6.\n\n    *) Bugfix: nginx did not work on FreeBSD/sparc64.\n\n\nChanges with nginx 0.6.7                                         15 Aug 2007\n\n    *) Change: now the paths specified in the \"include\",\n       \"auth_basic_user_file\", \"perl_modules\", \"ssl_certificate\",\n       \"ssl_certificate_key\", and \"ssl_client_certificate\" directives are\n       relative to directory of nginx configuration file nginx.conf, but not\n       to nginx prefix directory.\n\n    *) Change: the --sysconfdir=PATH option in configure was canceled.\n\n    *) Change: the special make target \"upgrade1\" was defined for online\n       upgrade of 0.1.x versions.\n\n    *) Feature: the \"server_name\" and \"valid_referers\" directives support\n       regular expressions.\n\n    *) Feature: the \"server\" directive in the \"upstream\" context supports\n       the \"backup\" parameter.\n\n    *) Feature: the ngx_http_perl_module supports the\n       $r->discard_request_body.\n\n    *) Feature: the \"add_header Last-Modified ...\" directive changes the\n       \"Last-Modified\" response header line.\n\n    *) Bugfix: if a response different than 200 was returned to a request\n       with body and connection went to the keep-alive state after the\n       request, then nginx returned 400 for the next request.\n\n    *) Bugfix: a segmentation fault occurred in worker process if invalid\n       address was set in the \"auth_http\" directive.\n\n    *) Bugfix: now nginx uses default listen backlog value 511 on all\n       platforms except FreeBSD.\n       Thanks to Jiang Hong.\n\n    *) Bugfix: a worker process may got caught in an endless loop, if a\n       \"server\" inside \"upstream\" block was marked as \"down\"; the bug had\n       appeared in 0.6.6.\n\n    *) Bugfix: now Solaris sendfilev() is not used to transfer the client\n       request body to FastCGI-server via the unix domain socket.\n\n\nChanges with nginx 0.6.6                                         30 Jul 2007\n\n    *) Feature: the --sysconfdir=PATH option in configure.\n\n    *) Feature: named locations.\n\n    *) Feature: the $args variable can be set with the \"set\" directive.\n\n    *) Feature: the $is_args variable.\n\n    *) Bugfix: fair big weight upstream balancer.\n\n    *) Bugfix: if a client has closed connection to mail proxy then nginx\n       might not close connection to backend.\n\n    *) Bugfix: if the same host without specified port was used as backend\n       for HTTP and HTTPS, then nginx used only one port - 80 or 443.\n\n    *) Bugfix: fix building on Solaris/amd64 by Sun Studio 11 and early\n       versions; the bug had appeared in 0.6.4.\n\n\nChanges with nginx 0.6.5                                         23 Jul 2007\n\n    *) Feature: $nginx_version variable.\n       Thanks to Nick S. Grechukh.\n\n    *) Feature: the mail proxy supports AUTHENTICATE in IMAP mode.\n       Thanks to Maxim Dounin.\n\n    *) Feature: the mail proxy supports STARTTLS in SMTP mode.\n       Thanks to Maxim Dounin.\n\n    *) Bugfix: now nginx escapes space in $memcached_key variable.\n\n    *) Bugfix: nginx was incorrectly built by Sun Studio on Solaris/amd64.\n       Thanks to Jiang Hong.\n\n    *) Bugfix: of minor potential bugs.\n       Thanks to Coverity's Scan.\n\n\nChanges with nginx 0.6.4                                         17 Jul 2007\n\n    *) Security: the \"msie_refresh\" directive allowed XSS.\n       Thanks to Maxim Boguk.\n\n    *) Change: the \"proxy_store\" and \"fastcgi_store\" directives were\n       changed.\n\n    *) Feature: the \"proxy_store_access\" and \"fastcgi_store_access\"\n       directives.\n\n    *) Bugfix: nginx did not work on Solaris/sparc64 if it was built by Sun\n       Studio.\n       Thanks to Andrei Nigmatulin.\n\n    *) Workaround: for Sun Studio 12.\n       Thanks to Jiang Hong.\n\n\nChanges with nginx 0.6.3                                         12 Jul 2007\n\n    *) Feature: the \"proxy_store\" and \"fastcgi_store\" directives.\n\n    *) Bugfix: a segmentation fault might occur in worker process if the\n       \"auth_http_header\" directive was used.\n       Thanks to Maxim Dounin.\n\n    *) Bugfix: a segmentation fault occurred in worker process if the\n       CRAM-MD5 authentication method was used, but it was not enabled.\n\n    *) Bugfix: a segmentation fault might occur in worker process when the\n       HTTPS protocol was used in the \"proxy_pass\" directive.\n\n    *) Bugfix: a segmentation fault might occur in worker process if the\n       eventport method was used.\n\n    *) Bugfix: the \"proxy_ignore_client_abort\" and\n       \"fastcgi_ignore_client_abort\" directives did not work; the bug had\n       appeared in 0.5.13.\n\n\nChanges with nginx 0.6.2                                         09 Jul 2007\n\n    *) Bugfix: if the FastCGI header was split in records, then nginx passed\n       garbage in the header to a client.\n\n\nChanges with nginx 0.6.1                                         17 Jun 2007\n\n    *) Bugfix: in SSI parsing.\n\n    *) Bugfix: if remote SSI subrequest was used, then posterior local file\n       subrequest might transferred to client in wrong order.\n\n    *) Bugfix: large SSI inclusions buffered in temporary files were\n       truncated.\n\n    *) Bugfix: the perl $$ variable value in ngx_http_perl_module was equal\n       to the master process identification number.\n\n\nChanges with nginx 0.6.0                                         14 Jun 2007\n\n    *) Feature: the \"server_name\", \"map\", and \"valid_referers\" directives\n       support the \"www.example.*\" wildcards.\n\n\nChanges with nginx 0.5.25                                        11 Jun 2007\n\n    *) Bugfix: nginx could not be built with the\n       --without-http_rewrite_module parameter; the bug had appeared in\n       0.5.24.\n\n\nChanges with nginx 0.5.24                                        06 Jun 2007\n\n    *) Security: the \"ssl_verify_client\" directive did not work if request\n       was made using HTTP/0.9.\n\n    *) Bugfix: a part of response body might be passed uncompressed if gzip\n       was used; the bug had appeared in 0.5.23.\n\n\nChanges with nginx 0.5.23                                        04 Jun 2007\n\n    *) Feature: the ngx_http_ssl_module supports Server Name Indication TLS\n       extension.\n\n    *) Feature: the \"fastcgi_catch_stderr\" directive.\n       Thanks to Nick S. Grechukh, OWOX project.\n\n    *) Bugfix: a segmentation fault occurred in master process if two\n       virtual servers should bind() to the overlapping ports.\n\n    *) Bugfix: if nginx was built with ngx_http_perl_module and perl\n       supported threads, then during second reconfiguration the error\n       messages \"panic: MUTEX_LOCK\" and \"perl_parse() failed\" were issued.\n\n    *) Bugfix: in the HTTPS protocol in the \"proxy_pass\" directive.\n\n\nChanges with nginx 0.5.22                                        29 May 2007\n\n    *) Bugfix: a big request body might not be passed to backend; the bug\n       had appeared in 0.5.21.\n\n\nChanges with nginx 0.5.21                                        28 May 2007\n\n    *) Bugfix: if server has more than about ten locations, then regex\n       locations might be chosen not in that order as they were specified.\n\n    *) Bugfix: a worker process may got caught in an endless loop on 64-bit\n       platform, if the 33-rd or next in succession backend has failed.\n       Thanks to Anton Povarov.\n\n    *) Bugfix: a bus error might occur on Solaris/sparc64 if the PCRE\n       library was used.\n       Thanks to Andrei Nigmatulin.\n\n    *) Bugfix: in the HTTPS protocol in the \"proxy_pass\" directive.\n\n\nChanges with nginx 0.5.20                                        07 May 2007\n\n    *) Feature: the \"sendfile_max_chunk\" directive.\n\n    *) Feature: the \"$http_...\", \"$sent_http_...\", and \"$upstream_http_...\"\n       variables may be changed using the \"set\" directive.\n\n    *) Bugfix: a segmentation fault might occur in worker process if the SSI\n       command 'if expr=\"$var = /\"' was used.\n\n    *) Bugfix: trailing boundary of multipart range response was transferred\n       incorrectly.\n       Thanks to Evan Miller.\n\n    *) Bugfix: nginx did not work on Solaris/sparc64 if it was built by Sun\n       Studio.\n       Thanks to Andrei Nigmatulin.\n\n    *) Bugfix: the ngx_http_perl_module could not be built by Solaris make.\n       Thanks to Andrei Nigmatulin.\n\n\nChanges with nginx 0.5.19                                        24 Apr 2007\n\n    *) Change: now the $request_time variable has millisecond precision.\n\n    *) Change: the method $r->rflush of ngx_http_perl_module was renamed to\n       the $r->flush.\n\n    *) Feature: the $upstream_addr variable.\n\n    *) Feature: the \"proxy_headers_hash_max_size\" and\n       \"proxy_headers_hash_bucket_size\" directives.\n       Thanks to Volodymyr Kostyrko.\n\n    *) Bugfix: the files more than 2G could not be transferred using\n       sendfile and limit_rate on 64-bit platforms.\n\n    *) Bugfix: the files more than 2G could not be transferred using\n       sendfile on 64-bit Linux.\n\n\nChanges with nginx 0.5.18                                        19 Apr 2007\n\n    *) Feature: the ngx_http_sub_filter_module.\n\n    *) Feature: the \"$upstream_http_...\" variables.\n\n    *) Feature: now the $upstream_status and $upstream_response_time\n       variables keep data about all upstreams before X-Accel-Redirect.\n\n    *) Bugfix: a segmentation fault occurred in master process after first\n       reconfiguration and receiving any signal if nginx was built with\n       ngx_http_perl_module and perl did not support multiplicity; the bug\n       had appeared in 0.5.9.\n\n    *) Bugfix: if perl did not support multiplicity, then after\n       reconfiguration perl code did not work; the bug had appeared in\n       0.3.38.\n\n\nChanges with nginx 0.5.17                                        02 Apr 2007\n\n    *) Change: now nginx always returns the 405 status for the TRACE method.\n\n    *) Feature: now nginx supports the \"include\" directive inside the\n       \"types\" block.\n\n    *) Bugfix: the $document_root variable usage in the \"root\" and \"alias\"\n       directives is disabled: this caused recursive stack overflow.\n\n    *) Bugfix: in the HTTPS protocol in the \"proxy_pass\" directive.\n\n    *) Bugfix: in some cases non-cacheable variables (such as $uri variable)\n       returned old cached value.\n\n\nChanges with nginx 0.5.16                                        26 Mar 2007\n\n    *) Bugfix: the C-class network was not used as hash key in the \"ip_hash\"\n       directive.\n       Thanks to Pavel Yarkovoy.\n\n    *) Bugfix: a segmentation fault might occur in worker process if a\n       charset was set in the \"Content-Type\" header line and the line has\n       trailing \";\"; the bug had appeared in 0.3.50.\n\n    *) Bugfix: the \"[alert] zero size buf\" error when FastCGI server was\n       used and a request body written in a temporary file was multiple of\n       32K.\n\n    *) Bugfix: nginx could not be built on Solaris without the --with-debug\n       option; the bug had appeared in 0.5.15.\n\n\nChanges with nginx 0.5.15                                        19 Mar 2007\n\n    *) Feature: the mail proxy supports authenticated SMTP proxying and the\n       \"smtp_auth\", \"smtp_capabilities\", and \"xclient\" directives.\n       Thanks to Anton Yuzhaninov and Maxim Dounin.\n\n    *) Feature: now the keep-alive connections are closed just after\n       receiving the reconfiguration signal.\n\n    *) Change: the \"imap\" and \"auth\" directives were renamed to the \"mail\"\n       and \"pop3_auth\" directives.\n\n    *) Bugfix: a segmentation fault occurred in worker process if the\n       CRAM-MD5 authentication method was used and the APOP method was\n       disabled.\n\n    *) Bugfix: if the \"starttls only\" directive was used in POP3 protocol,\n       then nginx allowed authentication without switching to the SSL mode.\n\n    *) Bugfix: worker processes did not exit after reconfiguration and did\n       not rotate logs if the eventport method was used.\n\n    *) Bugfix: a worker process may got caught in an endless loop, if the\n       \"ip_hash\" directive was used.\n\n    *) Bugfix: now nginx does not log some alerts if eventport or /dev/poll\n       methods are used.\n\n\nChanges with nginx 0.5.14                                        23 Feb 2007\n\n    *) Bugfix: nginx ignored superfluous closing \"}\" in the end of\n       configuration file.\n\n\nChanges with nginx 0.5.13                                        19 Feb 2007\n\n    *) Feature: the COPY and MOVE methods.\n\n    *) Bugfix: the ngx_http_realip_module set garbage for requests passed\n       via keep-alive connection.\n\n    *) Bugfix: nginx did not work on big-endian 64-bit Linux.\n       Thanks to Andrei Nigmatulin.\n\n    *) Bugfix: now when IMAP/POP3 proxy receives too long command it closes\n       the connection right away, but not after timeout.\n\n    *) Bugfix: if the \"epoll\" method was used and a client closed a\n       connection prematurely, then nginx closed the connection after a send\n       timeout only.\n\n    *) Bugfix: nginx could not be built on platforms different from i386,\n       amd64, sparc, and ppc; the bug had appeared in 0.5.8.\n\n\nChanges with nginx 0.5.12                                        12 Feb 2007\n\n    *) Bugfix: nginx could not be built on platforms different from i386,\n       amd64, sparc, and ppc; the bug had appeared in 0.5.8.\n\n    *) Bugfix: a segmentation fault might occur in worker process if the\n       temporary files were used while working with FastCGI server; the bug\n       had appeared in 0.5.8.\n\n    *) Bugfix: a segmentation fault might occur in worker process if the\n       $fastcgi_script_name variable was logged.\n\n    *) Bugfix: ngx_http_perl_module could not be built on Solaris.\n\n\nChanges with nginx 0.5.11                                        05 Feb 2007\n\n    *) Feature: now configure detects system PCRE library in MacPorts.\n       Thanks to Chris McGrath.\n\n    *) Bugfix: the response was incorrect if several ranges were requested;\n       the bug had appeared in 0.5.6.\n\n    *) Bugfix: the \"create_full_put_path\" directive could not create the\n       intermediate directories if no \"dav_access\" directive was set.\n       Thanks to Evan Miller.\n\n    *) Bugfix: the \"0\" response code might be logged in the access_log\n       instead of the \"400\" and \"408\" error codes.\n\n    *) Bugfix: a segmentation fault might occur in worker process if nginx\n       was built with -O2 optimization.\n\n\nChanges with nginx 0.5.10                                        26 Jan 2007\n\n    *) Bugfix: while online executable file upgrade the new master process\n       did not inherit the listening sockets; the bug had appeared in 0.5.9.\n\n    *) Bugfix: a segmentation fault might occur in worker process if nginx\n       was built with -O2 optimization; the bug had appeared in 0.5.1.\n\n\nChanges with nginx 0.5.9                                         25 Jan 2007\n\n    *) Change: now the ngx_http_memcached_module uses the $memcached_key\n       variable value as a key.\n\n    *) Feature: the $memcached_key variable.\n\n    *) Feature: the \"clean\" parameter in the \"client_body_in_file_only\"\n       directive.\n\n    *) Feature: the \"env\" directive.\n\n    *) Feature: the \"sendfile\" directive is available inside the \"if\" block.\n\n    *) Feature: now on failure of the writing to access nginx logs a message\n       to error_log, but not more often than once a minute.\n\n    *) Bugfix: the \"access_log off\" directive did not always turn off the\n       logging.\n\n\nChanges with nginx 0.5.8                                         19 Jan 2007\n\n    *) Bugfix: a segmentation fault might occur if\n       \"client_body_in_file_only on\" was used and a request body was small.\n\n    *) Bugfix: a segmentation fault occurred if\n       \"client_body_in_file_only on\" and \"proxy_pass_request_body off\" or\n       \"fastcgi_pass_request_body off\" directives were used, and nginx\n       switched to a next upstream.\n\n    *) Bugfix: if the \"proxy_buffering off\" directive was used and a client\n       connection was non-active, then the connection was closed after send\n       timeout; the bug had appeared in 0.4.7.\n\n    *) Bugfix: if the \"epoll\" method was used and a client closed a\n       connection prematurely, then nginx closed the connection after a send\n       timeout only.\n\n    *) Bugfix: the \"[alert] zero size buf\" error when FastCGI server was\n       used.\n\n    *) Bugfixes in the \"limit_zone\" directive.\n\n\nChanges with nginx 0.5.7                                         15 Jan 2007\n\n    *) Feature: the ssl_session_cache storage optimization.\n\n    *) Bugfixes in the \"ssl_session_cache\" and \"limit_zone\" directives.\n\n    *) Bugfix: the segmentation fault was occurred on start or while\n       reconfiguration if the \"ssl_session_cache\" or \"limit_zone\" directives\n       were used on 64-bit platforms.\n\n    *) Bugfix: a segmentation fault occurred if the \"add_before_body\" or\n       \"add_after_body\" directives were used and there was no \"Content-Type\"\n       header line in response.\n\n    *) Bugfix: the OpenSSL library was always built with the threads\n       support.\n       Thanks to Den Ivanov.\n\n    *) Bugfix: the PCRE-6.5+ library and the icc compiler compatibility.\n\n\nChanges with nginx 0.5.6                                         09 Jan 2007\n\n    *) Change: now the ngx_http_index_module ignores all methods except the\n       GET, HEAD, and POST methods.\n\n    *) Feature: the ngx_http_limit_zone_module.\n\n    *) Feature: the $binary_remote_addr variable.\n\n    *) Feature: the \"ssl_session_cache\" directives of the\n       ngx_http_ssl_module and ngx_imap_ssl_module.\n\n    *) Feature: the DELETE method supports recursive removal.\n\n    *) Bugfix: the byte-ranges were transferred incorrectly if the\n       $r->sendfile() was used.\n\n\nChanges with nginx 0.5.5                                         24 Dec 2006\n\n    *) Change: the -v switch does not show compiler information any more.\n\n    *) Feature: the -V switch.\n\n    *) Feature: the \"worker_rlimit_core\" directive supports size in K, M,\n       and G.\n\n    *) Bugfix: the nginx.pm module now could be installed by an unprivileged\n       user.\n\n    *) Bugfix: a segmentation fault might occur if the $r->request_body or\n       $r->request_body_file methods were used.\n\n    *) Bugfix: the ppc platform specific bugs.\n\n\nChanges with nginx 0.5.4                                         15 Dec 2006\n\n    *) Feature: the \"perl\" directive may be used inside the \"limit_except\"\n       block.\n\n    *) Bugfix: the ngx_http_dav_module required the \"Date\" request header\n       line for the DELETE method.\n\n    *) Bugfix: if one only parameter was used in the \"dav_access\" directive,\n       then nginx might report about configuration error.\n\n    *) Bugfix: a segmentation fault might occur if the $host variable was\n       used; the bug had appeared in 0.4.14.\n\n\nChanges with nginx 0.5.3                                         13 Dec 2006\n\n    *) Feature: the ngx_http_perl_module supports the $r->status,\n       $r->log_error, and $r->sleep methods.\n\n    *) Feature: the $r->variable method supports variables that do not exist\n       in nginx configuration.\n\n    *) Bugfix: the $r->has_request_body method did not work.\n\n\nChanges with nginx 0.5.2                                         11 Dec 2006\n\n    *) Bugfix: if the \"proxy_pass\" directive used the name of the \"upstream\"\n       block, then nginx tried to resolve the name; the bug had appeared in\n       0.5.1.\n\n\nChanges with nginx 0.5.1                                         11 Dec 2006\n\n    *) Bugfix: the \"post_action\" directive might not run after a\n       unsuccessful completion of a request.\n\n    *) Workaround: for Eudora for Mac; the bug had appeared in 0.4.11.\n       Thanks to Bron Gondwana.\n\n    *) Bugfix: if the \"upstream\" name was used in the \"fastcgi_pass\", then\n       the message \"no port in upstream\" was issued; the bug had appeared in\n       0.5.0.\n\n    *) Bugfix: if the \"proxy_pass\" and \"fastcgi_pass\" directives used the\n       same servers but different ports, then these directives uses the\n       first described port; the bug had appeared in 0.5.0.\n\n    *) Bugfix: if the \"proxy_pass\" and \"fastcgi_pass\" directives used the\n       unix domain sockets, then these directives used first described\n       socket; the bug had appeared in 0.5.0.\n\n    *) Bugfix: ngx_http_auth_basic_module ignored the user if it was in the\n       last line in the password file and there was no the carriage return,\n       the line feed, or the \":\" symbol after the password.\n\n    *) Bugfix: the $upstream_response_time variable might be equal to\n       \"0.000\", although response time was more than 1 millisecond.\n\n\nChanges with nginx 0.5.0                                         04 Dec 2006\n\n    *) Change: the parameters in the \"%name\" form in the \"log_format\"\n       directive are not supported anymore.\n\n    *) Change: the \"proxy_upstream_max_fails\",\n       \"proxy_upstream_fail_timeout\", \"fastcgi_upstream_max_fails\",\n       \"fastcgi_upstream_fail_timeout\", \"memcached_upstream_max_fails\", and\n       \"memcached_upstream_fail_timeout\" directives are not supported\n       anymore.\n\n    *) Feature: the \"server\" directive in the \"upstream\" context supports\n       the \"max_fails\", \"fail_timeout\", and \"down\" parameters.\n\n    *) Feature: the \"ip_hash\" directive inside the \"upstream\" block.\n\n    *) Feature: the WAIT status in the \"Auth-Status\" header line of the\n       IMAP/POP3 proxy authentication server response.\n\n    *) Bugfix: nginx could not be built on 64-bit platforms; the bug had\n       appeared in 0.4.14.\n\n\nChanges with nginx 0.4.14                                        27 Nov 2006\n\n    *) Feature: the \"proxy_pass_error_message\" directive in IMAP/POP3 proxy.\n\n    *) Feature: now configure detects system PCRE library on FreeBSD, Linux,\n       and NetBSD.\n\n    *) Bugfix: ngx_http_perl_module did not work with perl built with the\n       threads support; the bug had appeared in 0.3.38.\n\n    *) Bugfix: ngx_http_perl_module did not work if perl was called\n       recursively.\n\n    *) Bugfix: nginx ignored a host name in a request line.\n\n    *) Bugfix: a worker process may got caught in an endless loop, if a\n       FastCGI server sent too many data to the stderr.\n\n    *) Bugfix: the $upstream_response_time variable may be negative if the\n       system time was changed backward.\n\n    *) Bugfix: the \"Auth-Login-Attempt\" parameter was not sent to IMAP/POP3\n       proxy authentication server when POP3 was used.\n\n    *) Bugfix: a segmentation fault might occur if connect to IMAP/POP3\n       proxy authentication server failed.\n\n\nChanges with nginx 0.4.13                                        15 Nov 2006\n\n    *) Feature: the \"proxy_pass\" directive may be used inside the\n       \"limit_except\" block.\n\n    *) Feature: the \"limit_except\" directive supports all WebDAV methods.\n\n    *) Bugfix: if the \"add_before_body\" directive was used without the\n       \"add_after_body\" directive, then a response did not transferred\n       complete.\n\n    *) Bugfix: a large request body did not receive if the epoll method and\n       the deferred accept() were used.\n\n    *) Bugfix: a charset could not be set for ngx_http_autoindex_module\n       responses; the bug had appeared in 0.3.50.\n\n    *) Bugfix: the \"[alert] zero size buf\" error when FastCGI server was\n       used;\n\n    *) Bugfix: the --group= configuration parameter was ignored.\n       Thanks to Thomas Moschny.\n\n    *) Bugfix: the 50th subrequest in SSI response did not work; the bug had\n       appeared in 0.3.50.\n\n\nChanges with nginx 0.4.12                                        31 Oct 2006\n\n    *) Feature: the ngx_http_perl_module supports the $r->variable method.\n\n    *) Bugfix: if a big static file was included using SSI in a response,\n       then the response may be transferred incomplete.\n\n    *) Bugfix: nginx did not omit the \"#fragment\" part in URI.\n\n\nChanges with nginx 0.4.11                                        25 Oct 2006\n\n    *) Feature: the POP3 proxy supports the AUTH LOGIN PLAIN and CRAM-MD5.\n\n    *) Feature: the ngx_http_perl_module supports the $r->allow_ranges\n       method.\n\n    *) Bugfix: if the APOP was enabled in the POP3 proxy, then the USER/PASS\n       commands might not work; the bug had appeared in 0.4.10.\n\n\nChanges with nginx 0.4.10                                        23 Oct 2006\n\n    *) Feature: the POP3 proxy supports the APOP command.\n\n    *) Bugfix: if the select, poll or /dev/poll methods were used, then\n       while waiting authentication server response the IMAP/POP3 proxy\n       hogged CPU.\n\n    *) Bugfix: a segmentation fault might occur if the $server_addr variable\n       was used in the \"map\" directive.\n\n    *) Bugfix: the ngx_http_flv_module did not support the byte ranges for\n       full responses; the bug had appeared in 0.4.7.\n\n    *) Bugfix: nginx could not be built on Debian amd64; the bug had\n       appeared in 0.4.9.\n\n\nChanges with nginx 0.4.9                                         13 Oct 2006\n\n    *) Feature: the \"set\" parameter in the \"include\" SSI command.\n\n    *) Feature: the ngx_http_perl_module now tests the nginx.pm module\n       version.\n\n\nChanges with nginx 0.4.8                                         11 Oct 2006\n\n    *) Bugfix: if an \"include\" SSI command were before another \"include\" SSI\n       command with a \"wait\" parameter, then the \"wait\" parameter might not\n       work.\n\n    *) Bugfix: the ngx_http_flv_module added the FLV header to the full\n       responses.\n       Thanks to Alexey Kovyrin.\n\n\nChanges with nginx 0.4.7                                         10 Oct 2006\n\n    *) Feature: the ngx_http_flv_module.\n\n    *) Feature: the $request_body_file variable.\n\n    *) Feature: the \"charset\" and \"source_charset\" directives support the\n       variables.\n\n    *) Bugfix: if an \"include\" SSI command were before another \"include\" SSI\n       command with a \"wait\" parameter, then the \"wait\" parameter might not\n       work.\n\n    *) Bugfix: if the \"proxy_buffering off\" directive was used or while\n       working with memcached the connections might not be closed on\n       timeout.\n\n    *) Bugfix: nginx did not run on 64-bit platforms except amd64, sparc64,\n       and ppc64.\n\n\nChanges with nginx 0.4.6                                         06 Oct 2006\n\n    *) Bugfix: nginx did not run on 64-bit platforms except amd64, sparc64,\n       and ppc64.\n\n    *) Bugfix: nginx sent the chunked response for HTTP/1.1 request,\n       if its length was set by text string in the\n       $r->headers_out(\"Content-Length\", ...) method.\n\n    *) Bugfix: after redirecting error by an \"error_page\" directive any\n       ngx_http_rewrite_module directive returned this error code; the bug\n       had appeared in 0.4.4.\n\n\nChanges with nginx 0.4.5                                         02 Oct 2006\n\n    *) Bugfix: nginx could not be built on Linux and Solaris; the bug had\n       appeared in 0.4.4.\n\n\nChanges with nginx 0.4.4                                         02 Oct 2006\n\n    *) Feature: the $scheme variable.\n\n    *) Feature: the \"expires\" directive supports the \"max\" parameter.\n\n    *) Feature: the \"include\" directive supports the \"*\" mask.\n       Thanks to Jonathan Dance.\n\n    *) Bugfix: the \"return\" directive always overrode the \"error_page\"\n       response code redirected by the \"error_page\" directive.\n\n    *) Bugfix: a segmentation fault occurred if zero-length body was in PUT\n       method.\n\n    *) Bugfix: the redirect was changed incorrectly if the variables were\n       used in the \"proxy_redirect\" directive.\n\n\nChanges with nginx 0.4.3                                         26 Sep 2006\n\n    *) Change: now the 499 error could not be redirected using an\n       \"error_page\" directive.\n\n    *) Feature: the Solaris 10 event ports support.\n\n    *) Feature: the ngx_http_browser_module.\n\n    *) Bugfix: a segmentation fault may occur while redirecting the 400\n       error to the proxied server using a \"proxy_pass\" directive.\n\n    *) Bugfix: a segmentation fault occurred if an unix domain socket was\n       used in a \"proxy_pass\" directive; the bug had appeared in 0.3.47.\n\n    *) Bugfix: SSI did work with memcached and nonbuffered responses.\n\n    *) Workaround: of the Sun Studio PAUSE hardware capability bug.\n\n\nChanges with nginx 0.4.2                                         14 Sep 2006\n\n    *) Bugfix: the O_NOATIME flag support on Linux was canceled; the bug had\n       appeared in 0.4.1.\n\n\nChanges with nginx 0.4.1                                         14 Sep 2006\n\n    *) Bugfix: the DragonFlyBSD compatibility.\n       Thanks to Pavel Nazarov.\n\n    *) Workaround: of bug in 64-bit Linux sendfile(), when file is more than\n       2G.\n\n    *) Feature: now on Linux nginx uses O_NOATIME flag for static requests.\n       Thanks to Yusuf Goolamabbas.\n\n\nChanges with nginx 0.4.0                                         30 Aug 2006\n\n    *) Change in internal API: the HTTP modules initialization was moved\n       from the init module phase to the HTTP postconfiguration phase.\n\n    *) Change: now the request body is not read beforehand for the\n       ngx_http_perl_module: it's required to start the reading using the\n       $r->has_request_body method.\n\n    *) Feature: the ngx_http_perl_module supports the DECLINED return code.\n\n    *) Feature: the ngx_http_dav_module supports the incoming \"Date\" header\n       line for the PUT method.\n\n    *) Feature: the \"ssi\" directive is available inside the \"if\" block.\n\n    *) Bugfix: a segmentation fault occurred if there was an \"index\"\n       directive with variables and the first index name was without\n       variables; the bug had appeared in 0.1.29.\n\n\nChanges with nginx 0.3.61                                        28 Aug 2006\n\n    *) Change: now the \"tcp_nodelay\" directive is turned on by default.\n\n    *) Feature: the \"msie_refresh\" directive.\n\n    *) Feature: the \"recursive_error_pages\" directive.\n\n    *) Bugfix: the \"rewrite\" directive returned incorrect redirect, if the\n       redirect had the captured escaped symbols from original URI.\n\n\nChanges with nginx 0.3.60                                        18 Aug 2006\n\n    *) Bugfix: a worker process may got caught in an endless loop while an\n       error redirection; the bug had appeared in 0.3.59.\n\n\nChanges with nginx 0.3.59                                        16 Aug 2006\n\n    *) Feature: now is possible to do several redirection using the\n       \"error_page\" directive.\n\n    *) Bugfix: the \"dav_access\" directive did not support three parameters.\n\n    *) Bugfix: the \"error_page\" directive did not changes the \"Content-Type\"\n       header line after the \"X-Accel-Redirect\" was used; the bug had\n       appeared in 0.3.58.\n\n\nChanges with nginx 0.3.58                                        14 Aug 2006\n\n    *) Feature: the \"error_page\" directive supports the variables.\n\n    *) Change: now the procfs interface instead of sysctl is used on Linux.\n\n    *) Change: now the \"Content-Type\" header line is inherited from first\n       response when the \"X-Accel-Redirect\" was used.\n\n    *) Bugfix: the \"error_page\" directive did not redirect the 413 error.\n\n    *) Bugfix: the trailing \"?\" did not remove old arguments if no new\n       arguments were added to a rewritten URI.\n\n    *) Bugfix: nginx could not run on 64-bit FreeBSD 7.0-CURRENT.\n\n\nChanges with nginx 0.3.57                                        09 Aug 2006\n\n    *) Feature: the $ssl_client_serial variable.\n\n    *) Bugfix: in the \"!-e\" operator of the \"if\" directive.\n       Thanks to Andrian Budanstov.\n\n    *) Bugfix: while a client certificate verification nginx did not send to\n       a client the required certificates information.\n\n    *) Bugfix: the $document_root variable did not support the variables in\n       the \"root\" directive.\n\n\nChanges with nginx 0.3.56                                        04 Aug 2006\n\n    *) Feature: the \"dav_access\" directive.\n\n    *) Feature: the \"if\" directive supports the \"-d\", \"!-d\", \"-e\", \"!-e\",\n       \"-x\", and \"!-x\" operators.\n\n    *) Bugfix: a segmentation fault occurred if a request returned a\n       redirect and some sent to client header lines were logged in the\n       access log.\n\n\nChanges with nginx 0.3.55                                        28 Jul 2006\n\n    *) Feature: the \"stub\" parameter in the \"include\" SSI command.\n\n    *) Feature: the \"block\" SSI command.\n\n    *) Feature: the unicode2nginx script was added to contrib.\n\n    *) Bugfix: if a \"root\" was specified by variable only, then the root was\n       relative to a server prefix.\n\n    *) Bugfix: if the request contained \"//\" or \"/./\" and escaped symbols\n       after them, then the proxied request was sent unescaped.\n\n    *) Bugfix: the $r->header_in(\"Cookie\") of the ngx_http_perl_module now\n       returns all \"Cookie\" header lines.\n\n    *) Bugfix: a segmentation fault occurred if\n       \"client_body_in_file_only on\" was used and nginx switched to a next\n       upstream.\n\n    *) Bugfix: on some condition while reconfiguration character codes\n       inside the \"charset_map\" may be treated invalid; the bug had appeared\n       in 0.3.50.\n\n\nChanges with nginx 0.3.54                                        11 Jul 2006\n\n    *) Feature: nginx now logs the subrequest information to the error log.\n\n    *) Feature: the \"proxy_next_upstream\", \"fastcgi_next_upstream\", and\n       \"memcached_next_upstream\" directives support the \"off\" parameter.\n\n    *) Feature: the \"debug_connection\" directive supports the CIDR address\n       form.\n\n    *) Bugfix: if a response of proxied server or FastCGI server was\n       converted from UTF-8 or back, then it may be transferred incomplete.\n\n    *) Bugfix: the $upstream_response_time variable had the time of the\n       first request to a backend only.\n\n    *) Bugfix: nginx could not be built on amd64 platform; the bug had\n       appeared in 0.3.53.\n\n\nChanges with nginx 0.3.53                                        07 Jul 2006\n\n    *) Change: the \"add_header\" directive adds the string to 204, 301, and\n       302 responses.\n\n    *) Feature: the \"server\" directive in the \"upstream\" context supports\n       the \"weight\" parameter.\n\n    *) Feature: the \"server_name\" directive supports the \"*\" wildcard.\n\n    *) Feature: nginx supports the request body size more than 2G.\n\n    *) Bugfix: if a client was successfully authorized using \"satisfy_any\n       on\", then anyway the message \"access forbidden by rule\" was written\n       in the log.\n\n    *) Bugfix: the \"PUT\" method may erroneously not create a file and return\n       the 409 code.\n\n    *) Bugfix: if the IMAP/POP3 backend returned an error, then nginx\n       continued proxying anyway.\n\n\nChanges with nginx 0.3.52                                        03 Jul 2006\n\n    *) Change: the ngx_http_index_module behavior for the \"POST /\" requests\n       is reverted to the 0.3.40 version state: the module now does not\n       return the 405 error.\n\n    *) Bugfix: the worker process may got caught in an endless loop if the\n       limit rate was used; the bug had appeared in 0.3.37.\n\n    *) Bugfix: ngx_http_charset_module logged \"unknown charset\" alert, even\n       if the recoding was not needed; the bug had appeared in 0.3.50.\n\n    *) Bugfix: if a code response of the PUT request was 409, then a\n       temporary file was not removed.\n\n\nChanges with nginx 0.3.51                                        30 Jun 2006\n\n    *) Bugfix: the \"<\" symbols might disappeared some conditions in the SSI;\n       the bug had appeared in 0.3.50.\n\n\nChanges with nginx 0.3.50                                        28 Jun 2006\n\n    *) Change: the \"proxy_redirect_errors\" and \"fastcgi_redirect_errors\"\n       directives was renamed to the \"proxy_intercept_errors\" and\n       \"fastcgi_intercept_errors\" directives.\n\n    *) Feature: the ngx_http_charset_module supports the recoding from the\n       single byte encodings to the UTF-8 encoding and back.\n\n    *) Feature: the \"X-Accel-Charset\" response header line is supported in\n       proxy and FastCGI mode.\n\n    *) Bugfix: the \"\\\" escape symbol in the \"\\\"\" and \"\\'\" pairs in the SSI\n       command was removed only if the command also has the \"$\" symbol.\n\n    *) Bugfix: the \"<!--\" string might be added on some conditions in the\n       SSI after inclusion.\n\n    *) Bugfix: if the \"Content-Length: 0\" header line was in response, then\n       in nonbuffered proxying mode the client connection was not closed.\n\n\nChanges with nginx 0.3.49                                        31 May 2006\n\n    *) Bugfix: in the \"set\" directive.\n\n    *) Bugfix: if two or more FastCGI subrequests was in SSI, then first\n       subrequest output was included instead of second and following\n       subrequests.\n\n\nChanges with nginx 0.3.48                                        29 May 2006\n\n    *) Change: now the ngx_http_charset_module works for subrequests, if the\n       response has no \"Content-Type\" header line.\n\n    *) Bugfix: if the \"proxy_pass\" directive has no URI part, then the\n       \"proxy_redirect default\" directive add the unnecessary slash in start\n       of the rewritten redirect.\n\n    *) Bugfix: the internal redirect always transform client's HTTP method\n       to GET, now the transformation is made for the \"X-Accel-Redirect\"\n       redirects only and if the method is not HEAD; the bug had appeared in\n       0.3.42.\n\n    *) Bugfix: the ngx_http_perl_module could not be built, if the perl was\n       built with the threads support; the bug had appeared in 0.3.46.\n\n\nChanges with nginx 0.3.47                                        23 May 2006\n\n    *) Feature: the \"upstream\" directive.\n\n    *) Change: now the \"\\\" escape symbol in the \"\\\"\" and \"\\'\" pairs in the\n       SSI command is always removed.\n\n\nChanges with nginx 0.3.46                                        11 May 2006\n\n    *) Feature: the \"proxy_hide_header\", \"proxy_pass_header\",\n       \"fastcgi_hide_header\", and \"fastcgi_pass_header\" directives.\n\n    *) Change: the \"proxy_pass_x_powered_by\", \"fastcgi_x_powered_by\", and\n       \"proxy_pass_server\" directives were canceled.\n\n    *) Feature: the \"X-Accel-Buffering\" response header line is supported in\n       proxy mode.\n\n    *) Bugfix: the reconfiguration bug and memory leaks in the\n       ngx_http_perl_module.\n\n\nChanges with nginx 0.3.45                                        06 May 2006\n\n    *) Feature: the \"ssl_verify_client\", \"ssl_verify_depth\", and\n       \"ssl_client_certificate\" directives.\n\n    *) Change: the $request_method variable now returns the main request\n       method.\n\n    *) Change: the &deg; symbol codes were changed in koi-win conversion\n       table.\n\n    *) Feature: the euro and N symbols were added to koi-win conversion\n       table.\n\n    *) Bugfix: if nginx distributed the requests among several backends and\n       some backend failed, then requests intended for this backend was\n       directed to one live backend only instead of being distributed among\n       the rest.\n\n\nChanges with nginx 0.3.44                                        04 May 2006\n\n    *) Feature: the \"wait\" parameter in the \"include\" SSI command.\n\n    *) Feature: the Ukrainian and Byelorussian characters were added to\n       koi-win conversion table.\n\n    *) Bugfix: in the SSI.\n\n\nChanges with nginx 0.3.43                                        26 Apr 2006\n\n    *) Bugfix: in the SSI.\n\n\nChanges with nginx 0.3.42                                        26 Apr 2006\n\n    *) Feature: the \"bind\" option of the \"listen\" directive in IMAP/POP3\n       proxy.\n\n    *) Bugfix: if the same capture in the \"rewrite\" directive was used more\n       then once.\n\n    *) Bugfix: the $sent_http_content_type, $sent_http_content_length,\n       $sent_http_last_modified, $sent_http_connection,\n       $sent_http_keep_alive, and $sent_http_transfer_encoding variables\n       were not written to access log.\n\n    *) Bugfix: the $sent_http_cache_control returned value of the single\n       \"Cache-Control\" response header line.\n\n\nChanges with nginx 0.3.41                                        21 Apr 2006\n\n    *) Feature: the -v switch.\n\n    *) Bugfix: the segmentation fault may occurred if the SSI page has\n       remote subrequests.\n\n    *) Bugfix: in FastCGI handling.\n\n    *) Bugfix: if the perl modules path was not set using\n       --with-perl_modules_path=PATH or the \"perl_modules\", then the\n       segmentation fault was occurred.\n\n\nChanges with nginx 0.3.40                                        19 Apr 2006\n\n    *) Feature: the ngx_http_dav_module supports the MKCOL method.\n\n    *) Feature: the \"create_full_put_path\" directive.\n\n    *) Feature: the \"$limit_rate\" variable.\n\n\nChanges with nginx 0.3.39                                        17 Apr 2006\n\n    *) Feature: the \"uninitialized_variable_warn\" directive; the logging\n       level of the \"uninitialized variable\" message was lowered from\n       \"alert\" to \"warn\".\n\n    *) Feature: the \"override_charset\" directive.\n\n    *) Change: now if the unknown variable is used in the \"echo\" and \"if\n       expr='$name'\" SSI-commands, then the \"unknown variable\" message is\n       not logged.\n\n    *) Bugfix: the active connection counter increased on the exceeding of\n       the connection limit specified by the \"worker_connections\" directive;\n       the bug had appeared in 0.2.0.\n\n    *) Bugfix: the limit rate might not work on some condition; the bug had\n       appeared in 0.3.38.\n\n\nChanges with nginx 0.3.38                                        14 Apr 2006\n\n    *) Feature: the ngx_http_dav_module.\n\n    *) Change: the ngx_http_perl_module optimizations.\n       Thanks to Sergey Skvortsov.\n\n    *) Feature: the ngx_http_perl_module supports the $r->request_body_file\n       method.\n\n    *) Feature: the \"client_body_in_file_only\" directive.\n\n    *) Workaround: now on disk overflow nginx tries to write access logs\n       once a second only.\n       Thanks to Anton Yuzhaninov and Maxim Dounin.\n\n    *) Bugfix: now the \"limit_rate\" directive more precisely limits rate if\n       rate is more than 100 Kbyte/s.\n       Thanks to ForJest.\n\n    *) Bugfix: now the IMAP/POP3 proxy escapes the \"\\r\" and \"\\n\" symbols in\n       login and password to pass authorization server.\n       Thanks to Maxim Dounin.\n\n\nChanges with nginx 0.3.37                                        07 Apr 2006\n\n    *) Feature: the \"limit_except\" directive.\n\n    *) Feature: the \"if\" directive supports the \"!~\", \"!~*\", \"-f\", and \"!-f\"\n       operators.\n\n    *) Feature: the ngx_http_perl_module supports the $r->request_body\n       method.\n\n    *) Bugfix: in the ngx_http_addition_filter_module.\n\n\nChanges with nginx 0.3.36                                        05 Apr 2006\n\n    *) Feature: the ngx_http_addition_filter_module.\n\n    *) Feature: the \"proxy_pass\" and \"fastcgi_pass\" directives may be used\n       inside the \"if\" block.\n\n    *) Feature: the \"proxy_ignore_client_abort\" and\n       \"fastcgi_ignore_client_abort\" directives.\n\n    *) Feature: the \"$request_completion\" variable.\n\n    *) Feature: the ngx_http_perl_module supports the $r->request_method and\n       $r->remote_addr.\n\n    *) Feature: the ngx_http_ssi_module supports the \"elif\" command.\n\n    *) Bugfix: the \"\\/\" string in the expression of the \"if\" command of the\n       ngx_http_ssi_module was treated incorrectly.\n\n    *) Bugfix: in the regular expressions in the \"if\" command of the\n       ngx_http_ssi_module.\n\n    *) Bugfix: if the relative path was specified in the\n       \"client_body_temp_path\", \"proxy_temp_path\", \"fastcgi_temp_path\", and\n       \"perl_modules\" directives, then the directory was used relatively to\n       a current path but not to a server prefix.\n\n\nChanges with nginx 0.3.35                                        22 Mar 2006\n\n    *) Bugfix: the accept-filter and the TCP_DEFER_ACCEPT option were set\n       for first \"listen\" directive only; the bug had appeared in 0.3.31.\n\n    *) Bugfix: in the \"proxy_pass\" directive without the URI part in a\n       subrequest.\n\n\nChanges with nginx 0.3.34                                        21 Mar 2006\n\n    *) Feature: the \"add_header\" directive supports the variables.\n\n\nChanges with nginx 0.3.33                                        15 Mar 2006\n\n    *) Feature: the \"http_503\" parameter of the \"proxy_next_upstream\" or\n       \"fastcgi_next_upstream\" directives.\n\n    *) Bugfix: ngx_http_perl_module did not work with inlined in the\n       configuration code, if it was not started with the \"sub\" word.\n\n    *) Bugfix: in the \"post_action\" directive.\n\n\nChanges with nginx 0.3.32                                        11 Mar 2006\n\n    *) Bugfix: the debug logging on startup and reconfiguration time was\n       removed; the bug had appeared in 0.3.31.\n\n\nChanges with nginx 0.3.31                                        10 Mar 2006\n\n    *) Change: now nginx passes the malformed proxied backend responses.\n\n    *) Feature: the \"listen\" directives support the address in the \"*:port\"\n       form.\n\n    *) Feature: the EVFILER_TIMER support in MacOSX 10.4.\n\n    *) Workaround: for MacOSX 64-bit kernel kqueue millisecond timeout bug.\n       Thanks to Andrei Nigmatulin.\n\n    *) Bugfix: if there were several \"listen\" directives listening one\n       various addresses inside one server, then server names like\n       \"*.domain.tld\" worked for first address only; the bug had appeared in\n       0.3.18.\n\n    *) Bugfix: if the HTTPS protocol was used in the \"proxy_pass\" directive\n       and the request body was in temporary file then the request was not\n       transferred.\n\n    *) Bugfix: perl 5.8.8 compatibility.\n\n\nChanges with nginx 0.3.30                                        22 Feb 2006\n\n    *) Change: the ECONNABORTED error log level was changed to \"error\" from\n       \"crit\".\n\n    *) Bugfix: the ngx_http_perl_module could not be build without the\n       ngx_http_ssi_filter_module.\n\n    *) Bugfix: nginx could not be built on i386 platform, if the PIC was\n       used; the bug had appeared in 0.3.27.\n\n\nChanges with nginx 0.3.29                                        20 Feb 2006\n\n    *) Feature: now nginx uses less memory, if PHP in FastCGI mode sends\n       many warnings before the response.\n\n    *) Bugfix: the \"Transfer-Encoding: chunked\" header line was issued in\n       the 204 responses for the HTTP/1.1 requests.\n\n    *) Bugfix: nginx returned the 502 response, if the complete response\n       header lines were transferred in a separate FastCGI records.\n\n    *) Bugfix: if the proxied URI was specified in the \"post_action\"\n       directive, then it ran only after a successful completion of a\n       request.\n\n\nChanges with nginx 0.3.28                                        16 Feb 2006\n\n    *) Feature: the \"restrict_host_names\" directive was canceled.\n\n    *) Feature: the --with-cpu-opt=ppc64 configuration parameter.\n\n    *) Bugfix: on some condition the proxied connection with a client was\n       terminated prematurely.\n       Thanks to Vladimir Shutoff.\n\n    *) Bugfix: the \"X-Accel-Limit-Rate\" header line was not taken into\n       account if the request was redirected using the \"X-Accel-Redirect\"\n       header line.\n\n    *) Bugfix: the \"post_action\" directive ran only after a successful\n       completion of a request.\n\n    *) Bugfix: the proxied response body generated by the \"post_action\"\n       directive was transferred to a client.\n\n\nChanges with nginx 0.3.27                                        08 Feb 2006\n\n    *) Change: the \"variables_hash_max_size\" and\n       \"variables_hash_bucket_size\" directives.\n\n    *) Feature: the $body_bytes_sent variable can be used not only in the\n       \"log_format\" directive.\n\n    *) Feature: the $ssl_protocol and $ssl_cipher variables.\n\n    *) Feature: the cache line size detection for widespread CPUs at start\n       time.\n\n    *) Feature: now the \"accept_mutex\" directive is supported using fcntl(2)\n       on platforms different from i386, amd64, sparc64, and ppc.\n\n    *) Feature: the \"lock_file\" directive and the --with-lock-path=PATH\n       autoconfiguration directive.\n\n    *) Bugfix: if the HTTPS protocol was used in the \"proxy_pass\" directive\n       then the requests with the body was not transferred.\n\n\nChanges with nginx 0.3.26                                        03 Feb 2006\n\n    *) Change: the \"optimize_host_names\" directive was renamed to the\n       \"optimize_server_names\".\n\n    *) Bugfix: if in the \"proxy_pass\" directive was no the URI part, then\n       the main request URI was transferred to a backend while proxying the\n       SSI subrequest.\n\n\nChanges with nginx 0.3.25                                        01 Feb 2006\n\n    *) Bugfix: the segmentation fault was occurred on start or while\n       reconfiguration if there was invalid configuration; the bug had\n       appeared in 0.3.24.\n\n\nChanges with nginx 0.3.24                                        01 Feb 2006\n\n    *) Workaround: for bug in FreeBSD kqueue.\n\n    *) Bugfix: now a response generated by the \"post_action\" directive is\n       not transferred to a client.\n\n    *) Bugfix: the memory leaks were occurring if many log files were used.\n\n    *) Bugfix: the first \"proxy_redirect\" directive was working inside one\n       location.\n\n    *) Bugfix: on 64-bit platforms segmentation fault may occurred on start\n       if the many names were used in the \"server_name\" directives; the bug\n       had appeared in 0.3.18.\n\n\nChanges with nginx 0.3.23                                        24 Jan 2006\n\n    *) Feature: the \"optimize_host_names\" directive.\n\n    *) Bugfix: in using of the variables in the \"path\" and \"alias\"\n       directives.\n\n    *) Bugfix: the ngx_http_perl_module was incorrectly built on Linux and\n       Solaris.\n\n\nChanges with nginx 0.3.22                                        17 Jan 2006\n\n    *) Feature: the ngx_http_perl_module supports the $r->args and\n       $r->unescape methods.\n\n    *) Feature: the method $r->query_string of ngx_http_perl_module was\n       canceled.\n\n    *) Bugfix: segmentation fault was occurred if the \"none\" or \"blocked\"\n       values was specified in the \"valid_referers\" directive; the bug had\n       appeared in 0.3.18.\n\n\nChanges with nginx 0.3.21                                        16 Jan 2006\n\n    *) Feature: the ngx_http_perl_module.\n\n    *) Change: the \"valid_referers\" directive allows the referrers without\n       URI part.\n\n\nChanges with nginx 0.3.20                                        11 Jan 2006\n\n    *) Bugfix: in SSI handling.\n\n    *) Bugfix: the ngx_http_memcached_module did not support the keys in the\n       \"/usr?args\" form.\n\n\nChanges with nginx 0.3.19                                        28 Dec 2005\n\n    *) Feature: the \"path\" and \"alias\" directives support the variables.\n\n    *) Change: now the \"valid_referers\" directive again checks the URI part.\n\n    *) Bugfix: in SSI handling.\n\n\nChanges with nginx 0.3.18                                        26 Dec 2005\n\n    *) Feature: the \"server_names\" directive supports the \".domain.tld\"\n       names.\n\n    *) Feature: the \"server_names\" directive uses the hash for the\n       \"*.domain.tld\" names and more effective hash for usual names.\n\n    *) Change: the \"server_names_hash_max_size\" and\n       \"server_names_hash_bucket_size\" directives.\n\n    *) Change: the \"server_names_hash\" and \"server_names_hash_threshold\"\n       directives were canceled.\n\n    *) Feature: the \"valid_referers\" directive uses the hash site names.\n\n    *) Change: now the \"valid_referers\" directive checks the site names only\n       without the URI part.\n\n    *) Bugfix: some \".domain.tld\" names incorrectly processed by the\n       ngx_http_map_module.\n\n    *) Bugfix: segmentation fault was occurred if configuration file did not\n       exist; the bug had appeared in 0.3.12.\n\n    *) Bugfix: on 64-bit platforms segmentation fault may occurred on start;\n       the bug had appeared in 0.3.16.\n\n\nChanges with nginx 0.3.17                                        18 Dec 2005\n\n    *) Change: now on Linux configure checks the presence of epoll and\n       sendfile64() in kernel.\n\n    *) Feature: the \"map\" directive supports domain names in the\n       \".domain.tld\" form.\n\n    *) Bugfix: the timeouts were not used in SSL handshake; the bug had\n       appeared in 0.2.4.\n\n    *) Bugfix: in the HTTPS protocol in the \"proxy_pass\" directive.\n\n    *) Bugfix: when the HTTPS protocol was used in the \"proxy_pass\"\n       directive the port 80 was used by default.\n\n\nChanges with nginx 0.3.16                                        16 Dec 2005\n\n    *) Feature: the ngx_http_map_module.\n\n    *) Feature: the \"types_hash_max_size\" and \"types_hash_bucket_size\"\n       directives.\n\n    *) Feature: the \"ssi_value_length\" directive.\n\n    *) Feature: the \"worker_rlimit_core\" directive.\n\n    *) Workaround: the connection number in logs was always 1 if nginx was\n       built by the icc 8.1 or 9.0 compilers with optimization for\n       Pentium 4.\n\n    *) Bugfix: the \"config timefmt\" SSI command set incorrect time format.\n\n    *) Bugfix: nginx did not close connection to IMAP/POP3 backend for the\n       SSL connections; the bug had appeared in 0.3.13.\n       Thanks to Rob Mueller.\n\n    *) Bugfix: segmentation fault may occurred in at SSL shutdown; the bug\n       had appeared in 0.3.13.\n\n\nChanges with nginx 0.3.15                                        07 Dec 2005\n\n    *) Feature: the new 444 code of the \"return\" directive to close\n       connection.\n\n    *) Feature: the \"so_keepalive\" directive in IMAP/POP3 proxy.\n\n    *) Bugfix: if there are unclosed connection nginx now calls abort() only\n       on graceful quit and active \"debug_points\" directive.\n\n\nChanges with nginx 0.3.14                                        05 Dec 2005\n\n    *) Bugfix: in the 304 response the body was transferred; the bug had\n       appeared in 0.3.13.\n\n\nChanges with nginx 0.3.13                                        05 Dec 2005\n\n    *) Feature: the IMAP/POP3 proxy supports STARTTLS and STLS.\n\n    *) Bugfix: the IMAP/POP3 proxy did not work with the select, poll, and\n       /dev/poll methods.\n\n    *) Bugfix: in SSI handling.\n\n    *) Bugfix: now Solaris sendfilev() is not used to transfer the client\n       request body to FastCGI-server via the unix domain socket.\n\n    *) Bugfix: the \"auth_basic\" directive did not disable the authorization;\n       the bug had appeared in 0.3.11.\n\n\nChanges with nginx 0.3.12                                        26 Nov 2005\n\n    *) Security: if nginx was built with the ngx_http_realip_module and the\n       \"satisfy_any on\" directive was used, then access and authorization\n       directives did not work. The ngx_http_realip_module was not built and\n       is not built by default.\n\n    *) Change: the \"$time_gmt\" variable name was changed to \"$time_local\".\n\n    *) Change: the \"proxy_header_buffer_size\" and\n       \"fastcgi_header_buffer_size\" directives was renamed to the\n       \"proxy_buffer_size\" and \"fastcgi_buffer_size\" directives.\n\n    *) Feature: the ngx_http_memcached_module.\n\n    *) Feature: the \"proxy_buffering\" directive.\n\n    *) Bugfix: the changes in accept mutex handling when the \"rtsig\" method\n       was used; the bug had appeared in 0.3.0.\n\n    *) Bugfix: if the client sent the \"Transfer-Encoding: chunked\" header\n       line, then nginx returns the 411 error.\n\n    *) Bugfix: if the \"auth_basic\" directive was inherited from the http\n       level, then the realm in the \"WWW-Authenticate\" header line was\n       without the \"Basic realm\" text.\n\n    *) Bugfix: if the \"combined\" format was explicitly specified in the\n       \"access_log\" directive, then the empty lines was written to the log;\n       the bug had appeared in 0.3.8.\n\n    *) Bugfix: nginx did not run on the sparc platform under any OS except\n       Solaris.\n\n    *) Bugfix: now it is not necessary to place space between the quoted\n       string and closing bracket in the \"if\" directive.\n\n\nChanges with nginx 0.3.11                                        15 Nov 2005\n\n    *) Bugfix: nginx did not pass the client request headers and body while\n       proxying; the bug had appeared in 0.3.10.\n\n\nChanges with nginx 0.3.10                                        15 Nov 2005\n\n    *) Change: the \"valid_referers\" directive and the \"$invalid_referer\"\n       variable were moved to the new ngx_http_referer_module from the\n       ngx_http_rewrite_module.\n\n    *) Change: the \"$apache_bytes_sent\" variable name was changed to\n       \"$body_bytes_sent\".\n\n    *) Feature: the \"$sent_http_...\" variables.\n\n    *) Feature: the \"if\" directive supports the \"=\" and \"!=\" operations.\n\n    *) Feature: the \"proxy_pass\" directive supports the HTTPS protocol.\n\n    *) Feature: the \"proxy_set_body\" directive.\n\n    *) Feature: the \"post_action\" directive.\n\n    *) Feature: the ngx_http_empty_gif_module.\n\n    *) Feature: the \"worker_cpu_affinity\" directive for Linux.\n\n    *) Bugfix: the \"rewrite\" directive did not unescape URI part in\n       redirect, now it is unescaped except the %00-%25 and %7F-%FF\n       characters.\n\n    *) Bugfix: nginx could not be built by the icc 9.0 compiler.\n\n    *) Bugfix: if the SSI was enabled for zero size static file, then the\n       chunked response was encoded incorrectly.\n\n\nChanges with nginx 0.3.9                                         10 Nov 2005\n\n    *) Bugfix: nginx considered URI as unsafe if two any symbols was between\n       two slashes; the bug had appeared in 0.3.8.\n\n\nChanges with nginx 0.3.8                                         09 Nov 2005\n\n    *) Security: nginx now checks URI got from a backend in\n       \"X-Accel-Redirect\" header line or in SSI file for the \"/../\" paths\n       and zeroes.\n\n    *) Change: nginx now does not treat the empty user name in the\n       \"Authorization\" header line as valid one.\n\n    *) Feature: the \"ssl_session_timeout\" directives of the\n       ngx_http_ssl_module and ngx_imap_ssl_module.\n\n    *) Feature: the \"auth_http_header\" directive of the\n       ngx_imap_auth_http_module.\n\n    *) Feature: the \"add_header\" directive.\n\n    *) Feature: the ngx_http_realip_module.\n\n    *) Feature: the new variables to use in the \"log_format\" directive:\n       $bytes_sent, $apache_bytes_sent, $status, $time_gmt, $uri,\n       $request_time, $request_length, $upstream_status,\n       $upstream_response_time, $gzip_ratio, $uid_got, $uid_set,\n       $connection, $pipe, and $msec. The parameters in the \"%name\" form\n       will be canceled soon.\n\n    *) Change: now the false variable values in the \"if\" directive are the\n       empty string \"\" and string starting with \"0\".\n\n    *) Bugfix: while using proxied or FastCGI-server nginx may leave\n       connections and temporary files with client requests in open state.\n\n    *) Bugfix: the worker processes did not flush the buffered logs on\n       graceful exit.\n\n    *) Bugfix: if the request URI was changes by the \"rewrite\" directive and\n       the request was proxied in location given by regular expression, then\n       the incorrect request was transferred to backend; the bug had\n       appeared in 0.2.6.\n\n    *) Bugfix: the \"expires\" directive did not remove the previous \"Expires\"\n       header.\n\n    *) Bugfix: nginx may stop to accept requests if the \"rtsig\" method and\n       several worker processes were used.\n\n    *) Bugfix: the \"\\\"\" and \"\\'\" escape symbols were incorrectly handled in\n       SSI commands.\n\n    *) Bugfix: if the response was ended just after the SSI command and\n       gzipping was used, then the response did not transferred complete or\n       did not transferred at all.\n\n\nChanges with nginx 0.3.7                                         27 Oct 2005\n\n    *) Feature: the \"access_log\" supports the \"buffer=\" parameter.\n\n    *) Bugfix: nginx could not be built on platforms different from i386,\n       amd64, sparc, and ppc; the bug had appeared in 0.3.2.\n\n\nChanges with nginx 0.3.6                                         24 Oct 2005\n\n    *) Change: now the IMAP/POP3 proxy do not send the empty login to\n       authorization server.\n\n    *) Feature: the \"log_format\" supports the variables in the $name form.\n\n    *) Bugfix: if at least in one server was no the \"listen\" directive, then\n       nginx did not listen on the 80 port; the bug had appeared in 0.3.3.\n\n    *) Bugfix: if the URI part is omitted in \"proxy_pass\" directive, the 80\n       port was always used.\n\n\nChanges with nginx 0.3.5                                         21 Oct 2005\n\n    *) Bugfix: the segmentation fault may occurred if the IMAP/POP3 login\n       was changed by authorization server; the bug had appeared in 0.2.2.\n\n    *) Bugfix: the accept mutex did not work and all connections were\n       handled by one process; the bug had appeared in 0.3.3.\n\n    *) Bugfix: the timeout did not work if the \"rtsig\" method and the\n       \"timer_resolution\" directive were used.\n\n\nChanges with nginx 0.3.4                                         19 Oct 2005\n\n    *) Bugfix: nginx could not be built on Linux 2.4+ and MacOS X; the bug\n       had appeared in 0.3.3.\n\n\nChanges with nginx 0.3.3                                         19 Oct 2005\n\n    *) Change: the \"bl\" and \"af\" parameters of the \"listen\" directive was\n       renamed to the \"backlog\" and \"accept_filter\".\n\n    *) Feature: the \"rcvbuf\" and \"sndbuf\" parameters of the \"listen\"\n       directive.\n\n    *) Change: the \"$msec\" log parameter does not require now the additional\n       the gettimeofday() system call.\n\n    *) Feature: the -t switch now tests the \"listen\" directives.\n\n    *) Bugfix: if the invalid address was specified in the \"listen\"\n       directive, then after the -HUP signal nginx left an open socket in\n       the CLOSED state.\n\n    *) Bugfix: the mime type may be incorrectly set to default value for\n       index file with variable in the name; the bug had appeared in 0.3.0.\n\n    *) Feature: the \"timer_resolution\" directive.\n\n    *) Feature: the millisecond \"$upstream_response_time\" log parameter.\n\n    *) Bugfix: a temporary file with client request body now is removed just\n       after the response header was transferred to a client.\n\n    *) Bugfix: OpenSSL 0.9.6 compatibility.\n\n    *) Bugfix: the SSL certificate and key file paths could not be relative.\n\n    *) Bugfix: the \"ssl_prefer_server_ciphers\" directive did not work in the\n       ngx_imap_ssl_module.\n\n    *) Bugfix: the \"ssl_protocols\" directive allowed to specify the single\n       protocol only.\n\n\nChanges with nginx 0.3.2                                         12 Oct 2005\n\n    *) Feature: the Sun Studio 10 C compiler support.\n\n    *) Feature: the \"proxy_upstream_max_fails\",\n       \"proxy_upstream_fail_timeout\", \"fastcgi_upstream_max_fails\", and\n       \"fastcgi_upstream_fail_timeout\" directives.\n\n\nChanges with nginx 0.3.1                                         10 Oct 2005\n\n    *) Bugfix: the segmentation fault occurred when the signal queue\n       overflowed if the \"rtsig\" method was used; the bug had appeared in\n       0.2.0.\n\n    *) Change: correct handling of the \"\\\\\", \"\\\"\", \"\\'\", and \"\\$\" pairs in\n       SSI.\n\n\nChanges with nginx 0.3.0                                         07 Oct 2005\n\n    *) Change: the 10-days live time limit of worker process was eliminated.\n       The limit was introduced because of millisecond timers overflow.\n\n\nChanges with nginx 0.2.6                                         05 Oct 2005\n\n    *) Change: while using load-balancing the time before the failed backend\n       retry was decreased from 60 to 10 seconds.\n\n    *) Change: the \"proxy_pass_unparsed_uri\" was canceled, the original URI\n       now passed, if the URI part is omitted in \"proxy_pass\" directive.\n\n    *) Feature: the \"error_page\" directive supports redirects and allows\n       more flexible to change an error code.\n\n    *) Change: the charset in the \"Content-Type\" header line now is ignored\n       in proxied subrequests.\n\n    *) Bugfix: if the URI was changed in the \"if\" block and request did not\n       found new configuration, then the ngx_http_rewrite_module rules ran\n       again.\n\n    *) Bugfix: if the \"set\" directive set the ngx_http_geo_module variable\n       in some configuration part, the this variable was not available in\n       other configuration parts and the \"using uninitialized variable\"\n       error was occurred; the bug had appeared in 0.2.2.\n\n\nChanges with nginx 0.2.5                                         04 Oct 2005\n\n    *) Change: the duplicate value of the ngx_http_geo_module variable now\n       causes the warning and changes old value.\n\n    *) Feature: the ngx_http_ssi_module supports the \"set\" command.\n\n    *) Feature: the ngx_http_ssi_module supports the \"file\" parameter in the\n       \"include\" command.\n\n    *) Feature: the ngx_http_ssi_module supports the variable value\n       substitutions in expressions of the \"if\" command.\n\n\nChanges with nginx 0.2.4                                         03 Oct 2005\n\n    *) Feature: the ngx_http_ssi_module supports \"$var=text\", \"$var!=text\",\n       \"$var=/text/\", and \"$var!=/text/\" expressions in the \"if\" command.\n\n    *) Bugfix: in proxying location without trailing slash; the bug had\n       appeared in 0.1.44.\n\n    *) Bugfix: the segmentation fault may occurred if the \"rtsig\" method was\n       used; the bug had appeared in 0.2.0.\n\n\nChanges with nginx 0.2.3                                         30 Sep 2005\n\n    *) Bugfix: nginx could not be built without the --with-debug option; the\n       bug had appeared in 0.2.2.\n\n\nChanges with nginx 0.2.2                                         30 Sep 2005\n\n    *) Feature: the \"config errmsg\" command of the ngx_http_ssi_module.\n\n    *) Change: the ngx_http_geo_module variables can be overridden by the\n       \"set\" directive.\n\n    *) Feature: the \"ssl_protocols\" and \"ssl_prefer_server_ciphers\"\n       directives of the ngx_http_ssl_module and ngx_imap_ssl_module.\n\n    *) Bugfix: the ngx_http_autoindex_module did not show correctly the long\n       file names;\n\n    *) Bugfix: the ngx_http_autoindex_module now do not show the files\n       starting by dot.\n\n    *) Bugfix: if the SSL handshake failed then another connection may be\n       closed too.\n       Thanks to Rob Mueller.\n\n    *) Bugfix: the export versions of MSIE 5.x could not connect via HTTPS.\n\n\nChanges with nginx 0.2.1                                         23 Sep 2005\n\n    *) Bugfix: if all backend using in load-balancing failed after one\n       error, then nginx may got caught in an endless loop; the bug had\n       appeared in 0.2.0.\n\n\nChanges with nginx 0.2.0                                         23 Sep 2005\n\n    *) The pid-file names used during online upgrade was changed and now is\n       not required a manual rename operation. The old master process adds\n       the \".oldbin\" suffix to its pid-file and executes a new binary file.\n       The new master process creates usual pid-file without the \".newbin\"\n       suffix. If the master process exits, then old master process renames\n       back its pid-file with the \".oldbin\" suffix to the pid-file without\n       suffix.\n\n    *) Change: the \"worker_connections\" directive, new name of the\n       \"connections\" directive; now the directive specifies maximum number\n       of connections, but not maximum socket descriptor number.\n\n    *) Feature: SSL supports the session cache inside one worker process.\n\n    *) Feature: the \"satisfy_any\" directive.\n\n    *) Change: the ngx_http_access_module and ngx_http_auth_basic_module do\n       not run for subrequests.\n\n    *) Feature: the \"worker_rlimit_nofile\" and \"worker_rlimit_sigpending\"\n       directives.\n\n    *) Bugfix: if all backend using in load-balancing failed after one\n       error, then nginx did not try do connect to them during 60 seconds.\n\n    *) Bugfix: in IMAP/POP3 command argument parsing.\n       Thanks to Rob Mueller.\n\n    *) Bugfix: errors while using SSL in IMAP/POP3 proxy.\n\n    *) Bugfix: errors while using SSI and gzipping.\n\n    *) Bugfix: the \"Expires\" and \"Cache-Control\" header lines were omitted\n       from the 304 responses.\n       Thanks to Alexandr Kukushkin.\n\n\nChanges with nginx 0.1.45                                        08 Sep 2005\n\n    *) Change: the \"ssl_engine\" directive was canceled in the\n       ngx_http_ssl_module and now is introduced at global level.\n\n    *) Bugfix: the responses with SSI subrequests did not transferred via\n       SSL connection.\n\n    *) Various bug fixes in the IMAP/POP3 proxy.\n\n\nChanges with nginx 0.1.44                                        06 Sep 2005\n\n    *) Feature: the IMAP/POP3 proxy supports SSL.\n\n    *) Feature: the \"proxy_timeout\" directive of the ngx_imap_proxy_module.\n\n    *) Feature: the \"userid_mark\" directive.\n\n    *) Feature: the $remote_user variable value is determined independently\n       of authorization use.\n\n\nChanges with nginx 0.1.43                                        30 Aug 2005\n\n    *) Feature: the listen(2) backlog in the \"listen\" directive can be\n       changed using the -HUP signal.\n\n    *) Feature: the geo2nginx.pl script was added to contrib.\n\n    *) Change: the FastCGI parameters with the empty values now are passed\n       to a server.\n\n    *) Bugfix: the segmentation fault occurred or the worker process may got\n       caught in an endless loop if the proxied or FastCGI server sent the\n       \"Cache-Control\" header line and the \"expires\" directive was used; in\n       the proxied mode the bug had appeared in 0.1.29.\n\n\nChanges with nginx 0.1.42                                        23 Aug 2005\n\n    *) Bugfix: if the request URI had a zero length after the processing in\n       the ngx_http_proxy_module, then the segmentation fault or bus error\n       occurred in the ngx_http_proxy_module.\n\n    *) Bugfix: the \"limit_rate\" directive did not work inside the \"if\"\n       block; the bug had appeared in 0.1.38.\n\n\nChanges with nginx 0.1.41                                        25 Jul 2005\n\n    *) Bugfix: if the variable was used in the configuration file, then it\n       can not be used in SSI.\n\n\nChanges with nginx 0.1.40                                        22 Jul 2005\n\n    *) Bugfix: if a client sent too long header line, then the request\n       information did not logged in the error log.\n\n    *) Bugfix: the \"Set-Cookie\" header line was not transferred when the\n       \"X-Accel-Redirect\" was used; the bug had appeared in 0.1.39.\n\n    *) Bugfix: the \"Content-Disposition\" header line was not transferred\n       when the \"X-Accel-Redirect\" was used.\n\n    *) Bugfix: the master process did not close the listen socket on the\n       SIGQUIT signal.\n\n    *) Bugfix: after on-line upgrade on Linux and Solaris the process name\n       became shorter in the \"ps\" command.\n\n\nChanges with nginx 0.1.39                                        14 Jul 2005\n\n    *) The changes in the ngx_http_charset_module: the \"default_charset\"\n       directive was canceled; the \"charset\" directive sets the response\n       charset; the \"source_charset\" directive sets the source charset only.\n\n    *) Bugfix: the backend \"WWW-Authenticate\" header line did not\n       transferred while the 401 response code redirecting.\n\n    *) Bugfix: the ngx_http_proxy_module and ngx_http_fastcgi_module may\n       close a connection before anything was transferred to a client; the\n       bug had appeared in 0.1.38.\n\n    *) Workaround: the Linux glibc crypt_r() initialization bug.\n\n    *) Bugfix: the ngx_http_ssi_module did not support the relative URI in\n       the \"include virtual\" command.\n\n    *) Bugfix: if the backend response had the \"Location\" header line and\n       nginx should not rewrite this line, then the 500 code response body\n       was transferred; the bug had appeared in 0.1.29.\n\n    *) Bugfix: some directives of the ngx_http_proxy_module and\n       ngx_http_fastcgi_module were not inherited from the server to the\n       location level; the bug had appeared in 0.1.29.\n\n    *) Bugfix: the ngx_http_ssl_module did not support the certificate\n       chain.\n\n    *) Bugfix: the ngx_http_autoindex_module did not show correctly the long\n       file names; the bug had appeared in 0.1.38.\n\n    *) Bugfixes in IMAP/POP3 proxy in interaction with a backend at the\n       login state.\n\n\nChanges with nginx 0.1.38                                        08 Jul 2005\n\n    *) Feature: the \"limit_rate\" directive is supported in proxy and FastCGI\n       mode.\n\n    *) Feature: the \"X-Accel-Limit-Rate\" response header line is supported\n       in proxy and FastCGI mode.\n\n    *) Feature: the \"break\" directive.\n\n    *) Feature: the \"log_not_found\" directive.\n\n    *) Bugfix: the response status code was not changed when request was\n       redirected by the \"\"X-Accel-Redirect\" header line.\n\n    *) Bugfix: the variables set by the \"set\" directive could not be used in\n       SSI.\n\n    *) Bugfix: the segmentation fault may occurred if the SSI page has more\n       than one remote subrequest.\n\n    *) Bugfix: nginx treated the backend response as invalid if the status\n       line in the header was transferred in two packets; the bug had\n       appeared in 0.1.29.\n\n    *) Feature: the \"ssi_types\" directive.\n\n    *) Feature: the \"autoindex_exact_size\" directive.\n\n    *) Bugfix: the ngx_http_autoindex_module did not support the long file\n       names in UTF-8.\n\n    *) Feature: the IMAP/POP3 proxy.\n\n\nChanges with nginx 0.1.37                                        23 Jun 2005\n\n    *) Change: now the \"\\n\" is added to the end of the \"nginx.pid\" file.\n\n    *) Bugfix: the responses may be transferred not completely, if many\n       parts or the big parts were included by SSI.\n\n    *) Bugfix: if all backends had returned the 404 response and the\n       \"http_404\" parameter of the \"proxy_next_upstream\" or\n       \"fastcgi_next_upstream\" directives was used, then nginx started to\n       request all backends again.\n\n\nChanges with nginx 0.1.36                                        15 Jun 2005\n\n    *) Change: if the request header has duplicate the \"Host\", \"Connection\",\n       \"Content-Length\", or \"Authorization\" lines, then nginx now returns\n       the 400 error.\n\n    *) Change: the \"post_accept_timeout\" directive was canceled.\n\n    *) Feature: the \"default\", \"af=\", \"bl=\", \"deferred\", and \"bind\"\n       parameters of the \"listen\" directive.\n\n    *) Feature: the FreeBSD accept filters support.\n\n    *) Feature: the Linux TCP_DEFER_ACCEPT support.\n\n    *) Bugfix: the ngx_http_autoindex_module did not support the file names\n       in UTF-8.\n\n    *) Bugfix: the new log file can be rotated by the -USR1 signal only if\n       the reconfiguration by the -HUP signal was made twice.\n\n\nChanges with nginx 0.1.35                                        07 Jun 2005\n\n    *) Feature: the \"working_directory\" directive.\n\n    *) Feature: the \"port_in_redirect\" directive.\n\n    *) Bugfix: the segmentation fault was occurred if the backend response\n       header was in several packets; the bug had appeared in 0.1.29.\n\n    *) Bugfix: if more than 10 servers were configured or some server did\n       not use the \"listen\" directive, then the segmentation fault was\n       occurred on the start.\n\n    *) Bugfix: the segmentation fault might occur if the response was bigger\n       than the temporary file.\n\n    *) Bugfix: nginx returned the 400 response on requests like\n       \"GET http://www.domain.com/uri HTTP/1.0\"; the bug had appeared in\n       0.1.28.\n\n\nChanges with nginx 0.1.34                                        26 May 2005\n\n    *) Bugfix: the worker process may got caught in an endless loop if the\n       big response part were include by SSI.\n\n    *) Bugfix: the variables set by the \"set\" directive were not available\n       in SSI.\n\n    *) Feature: the \"autoindex_localtime\" directive.\n\n    *) Bugfix: the empty value of the \"proxy_set_header\" directive forbids\n       the client request header line passing.\n\n\nChanges with nginx 0.1.33                                        23 May 2005\n\n    *) Bugfix: nginx could not be built with the --without-pcre parameter;\n       the bug had appeared in 0.1.29.\n\n    *) Bugfix: 3, 4, 7, and 8 the \"proxy_set_header\" directives in one level\n       cause the bus fault on start up.\n\n    *) Bugfix: the HTTP protocol was specified in the HTTPS redirects.\n\n    *) Bugfix: if the \"rewrite\" directive used the captures inside the \"if\"\n       directive, then the 500 error code was returned.\n\n\nChanges with nginx 0.1.32                                        19 May 2005\n\n    *) Bugfix: the arguments were omitted in the redirects, issued by the\n       \"rewrite\" directive; the bug had appeared in 0.1.29.\n\n    *) Feature: the \"if\" directive supports the captures in regular\n       expressions.\n\n    *) Feature: the \"set\" directive supports the variables and the captures\n       of regular expressions.\n\n    *) Feature: the \"X-Accel-Redirect\" response header line is supported in\n       proxy and FastCGI mode.\n\n\nChanges with nginx 0.1.31                                        16 May 2005\n\n    *) Bugfix: the response encrypted by SSL may not transferred complete.\n\n    *) Bugfix: errors while processing FastCGI response by SSI.\n\n    *) Bugfix: errors while using SSI and gzipping.\n\n    *) Bugfix: the redirect with the 301 code was transferred without\n       response body; the bug had appeared in 0.1.30.\n\n\nChanges with nginx 0.1.30                                        14 May 2005\n\n    *) Bugfix: the worker process may got caught in an endless loop if the\n       SSI was used.\n\n    *) Bugfix: the response encrypted by SSL may not transferred complete.\n\n    *) Bugfix: if the length of the response part received at once from\n       proxied or FastCGI server was equal to 500, then nginx returns the\n       500 response code; in proxy mode the bug had appeared in 0.1.29 only.\n\n    *) Bugfix: nginx did not consider the directives with 8 or 9 parameters\n       as invalid.\n\n    *) Feature: the \"return\" directive can return the 204 response code.\n\n    *) Feature: the \"ignore_invalid_headers\" directive.\n\n\nChanges with nginx 0.1.29                                        12 May 2005\n\n    *) Feature: the ngx_http_ssi_module supports \"include virtual\" command.\n\n    *) Feature: the ngx_http_ssi_module supports the condition command like\n       'if expr=\"$NAME\"' and \"else\" and \"endif\" commands. Only one nested\n       level is supported.\n\n    *) Feature: the ngx_http_ssi_module supports the DATE_LOCAL and DATE_GMT\n       variables and \"config timefmt\" command.\n\n    *) Feature: the \"ssi_ignore_recycled_buffers\" directive.\n\n    *) Bugfix: the \"echo\" command did not show the default value for the\n       empty QUERY_STRING variable.\n\n    *) Change: the ngx_http_proxy_module was rewritten.\n\n    *) Feature: the \"proxy_redirect\", \"proxy_pass_request_headers\",\n       \"proxy_pass_request_body\", and \"proxy_method\" directives.\n\n    *) Feature: the \"proxy_set_header\" directive. The \"proxy_x_var\" was\n       canceled and must be replaced with the proxy_set_header directive.\n\n    *) Change: the \"proxy_preserve_host\" is canceled and must be replaced\n       with the \"proxy_set_header Host $host\" and the \"proxy_redirect off\"\n       directives, the \"proxy_set_header Host $host:$proxy_port\" directive\n       and the appropriate proxy_redirect directives.\n\n    *) Change: the \"proxy_set_x_real_ip\" is canceled and must be replaced\n       with the \"proxy_set_header X-Real-IP $remote_addr\" directive.\n\n    *) Change: the \"proxy_add_x_forwarded_for\" is canceled and must be\n       replaced with\n       the \"proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for\"\n       directive.\n\n    *) Change: the \"proxy_set_x_url\" is canceled and must be replaced with\n       the \"proxy_set_header X-URL http://$host:$server_port$request_uri\"\n       directive.\n\n    *) Feature: the \"fastcgi_param\" directive.\n\n    *) Change: the \"fastcgi_root\", \"fastcgi_set_var\" and \"fastcgi_params\"\n       directive are canceled and must be replaced with the fastcgi_param\n       directives.\n\n    *) Feature: the \"index\" directive can use the variables.\n\n    *) Feature: the \"index\" directive can be used at http and server levels.\n\n    *) Change: the last index only in the \"index\" directive can be absolute.\n\n    *) Feature: the \"rewrite\" directive can use the variables.\n\n    *) Feature: the \"internal\" directive.\n\n    *) Feature: the CONTENT_LENGTH, CONTENT_TYPE, REMOTE_PORT, SERVER_ADDR,\n       SERVER_PORT, SERVER_PROTOCOL, DOCUMENT_ROOT, SERVER_NAME,\n       REQUEST_METHOD, REQUEST_URI, and REMOTE_USER variables.\n\n    *) Change: nginx now passes the invalid lines in a client request\n       headers or a backend response header.\n\n    *) Bugfix: if the backend did not transfer response for a long time and\n       the \"send_timeout\" was less than \"proxy_read_timeout\", then nginx\n       returned the 408 response.\n\n    *) Bugfix: the segmentation fault was occurred if the backend sent an\n       invalid line in response header; the bug had appeared in 0.1.26.\n\n    *) Bugfix: the segmentation fault may occurred in FastCGI fault\n       tolerance configuration.\n\n    *) Bugfix: the \"expires\" directive did not remove the previous \"Expires\"\n       and \"Cache-Control\" headers.\n\n    *) Bugfix: nginx did not take into account trailing dot in \"Host\" header\n       line.\n\n    *) Bugfix: the ngx_http_auth_module did not work under Linux.\n\n    *) Bugfix: the rewrite directive worked incorrectly, if the arguments\n       were in a request.\n\n    *) Bugfix: nginx could not be built on MacOS X.\n\n\nChanges with nginx 0.1.28                                        08 Apr 2005\n\n    *) Bugfix: nginx hogs CPU while proxying the huge files.\n\n    *) Bugfix: nginx could not be built by gcc 4.0 on Linux.\n\n\nChanges with nginx 0.1.27                                        28 Mar 2005\n\n    *) Feature: the \"blocked\" parameter of the \"valid_referers\" directive.\n\n    *) Change: the errors while handling the request header now logged at\n       \"info\" level. The server name and the \"Host\" and \"Referer\" header\n       lines also logged.\n\n    *) Change: the \"Host\" header line is also logged in error log.\n\n    *) Feature: the proxy_pass_unparsed_uri directive. The special handling\n       of the \"://\" symbols in URI, appeared in 0.1.11 version, now is\n       canceled.\n\n    *) Bugfix: nginx could not be built on FreeBSD and Linux, if the\n       --without-ngx_http_auth_basic_module configuration parameter was\n       used.\n\n\nChanges with nginx 0.1.26                                        22 Mar 2005\n\n    *) Change: the invalid client header lines are now ignored and logged at\n       the info level.\n\n    *) Change: the server name is also logged in error log.\n\n    *) Feature: the ngx_http_auth_basic_module module and the auth_basic and\n       auth_basic_user_file directives.\n\n\nChanges with nginx 0.1.25                                        19 Mar 2005\n\n    *) Bugfix: nginx did run on Linux parisc.\n\n    *) Feature: nginx now does not start under FreeBSD if the sysctl\n       kern.ipc.somaxconn value is too big.\n\n    *) Bugfix: if a request was internally redirected by the\n       ngx_http_index_module module to the ngx_http_proxy_module or\n       ngx_http_fastcgi_module modules, then the index file was not closed\n       after request completion.\n\n    *) Feature: the \"proxy_pass\" can be used in location with regular\n       expression.\n\n    *) Feature: the ngx_http_rewrite_filter_module module supports the\n       condition like \"if ($HTTP_USER_AGENT ~ MSIE)\".\n\n    *) Bugfix: nginx started too slow if the large number of addresses and\n       text values were used in the \"geo\" directive.\n\n    *) Change: a variable name must be declared as \"$name\" in the \"geo\"\n       directive. The previous variant without \"$\" is still supported, but\n       will be removed soon.\n\n    *) Feature: the \"%{VARIABLE}v\" logging parameter.\n\n    *) Feature: the \"set $name value\" directive.\n\n    *) Bugfix: gcc 4.0 compatibility.\n\n    *) Feature: the --with-openssl-opt=OPTIONS autoconfiguration directive.\n\n\nChanges with nginx 0.1.24                                        04 Mar 2005\n\n    *) Feature: the ngx_http_ssi_filter_module supports the QUERY_STRING and\n       DOCUMENT_URI variables.\n\n    *) Bugfix: the ngx_http_autoindex_module may some times return the 404\n       response for existent directory, if this directory was used in\n       \"alias\" directive.\n\n    *) Bugfix: the ngx_http_ssi_filter_module ran incorrectly for large\n       responses.\n\n    *) Bugfix: the lack of the \"Referer\" header line was always accounted as\n       valid referrer.\n\n\nChanges with nginx 0.1.23                                        01 Mar 2005\n\n    *) Feature: the ngx_http_ssi_filter_module and the ssi,\n       ssi_silent_errors, and ssi_min_file_chunk directives. The 'echo\n       var=\"HTTP_...\" default=\"\"' and 'echo var=\"REMOTE_ADDR\"' commands are\n       supported.\n\n    *) Feature: the %request_time log parameter.\n\n    *) Feature: if the request has no the \"Host\" header line, then the\n       \"proxy_preserve_host\" directive set this header line to the first\n       server name of the \"server_name\" directive.\n\n    *) Bugfix: nginx could not be built on platforms different from i386,\n       amd64, sparc, and ppc; the bug had appeared in 0.1.22.\n\n    *) Bugfix: the ngx_http_autoindex_module now shows the information not\n       about the symlink, but about file or directory it points to.\n\n    *) Bugfix: the %apache_length parameter logged the negative length of\n       the response header if the no response was transferred to a client.\n\n\nChanges with nginx 0.1.22                                        22 Feb 2005\n\n    *) Bugfix: the ngx_http_stub_status_module showed incorrect handled\n       connections statistics if the proxying or FastCGI server were used.\n\n    *) Bugfix: the installation paths were incorrectly quoted on Linux and\n       Solaris; the bug had appeared in 0.1.21.\n\n\nChanges with nginx 0.1.21                                        22 Feb 2005\n\n    *) Bugfix: the ngx_http_stub_status_module showed incorrect statistics\n       if \"rtsig\" method was used or if several worker process ran on SMP.\n\n    *) Bugfix: nginx could not be built by the icc compiler on Linux or if\n       the zlib-1.2.x library was building from sources.\n\n    *) Bugfix: nginx could not be built on NetBSD 2.0.\n\n\nChanges with nginx 0.1.20                                        17 Feb 2005\n\n    *) Feature: the new \"script_filename\" and \"remote_port\" parameters of\n       the fastcgi_params directive.\n\n    *) Bugfix: the FastCGI stderr stream was handled incorrectly.\n\n\nChanges with nginx 0.1.19                                        16 Feb 2005\n\n    *) Bugfix: now, if request contains the zero, then the 404 error is\n       returned for the local requests.\n\n    *) Bugfix: nginx could not be built on NetBSD 2.0.\n\n    *) Bugfix: the timeout may occur while reading of the client request\n       body via SSL connections.\n\n\nChanges with nginx 0.1.18                                        09 Feb 2005\n\n    *) Workaround: the default values of the devpoll_events and the\n       devpoll_changes directives changed from 512 to 32 to be compatible\n       with Solaris 10.\n\n    *) Bugfix: the proxy_set_x_var and fastcgi_set_var directives were not\n       inherited.\n\n    *) Bugfix: in a redirect rewrite directive arguments were concatenated\n       with URI by an \"&\" rather than a \"?\".\n\n    *) Bugfix: the lines without trailing \";\" in the file being included by\n       the ngx_http_geo_module were silently ignored.\n\n    *) Feature: the ngx_http_stub_status_module.\n\n    *) Bugfix: the unknown log format in the access_log directive caused the\n       segmentation fault.\n\n    *) Feature: the new \"document_root\" parameter of the fastcgi_params\n       directive.\n\n    *) Feature: the fastcgi_redirect_errors directive.\n\n    *) Feature: the new \"break\" modifier of the \"rewrite\" directive allows\n       to stop the rewrite/location cycle and sets the current configuration\n       to the request.\n\n\nChanges with nginx 0.1.17                                        03 Feb 2005\n\n    *) Change: the ngx_http_rewrite_module was rewritten from the scratch.\n       Now it is possible to redirect, to return the error codes, to check\n       the variables and referrers. The directives can be used inside\n       locations. The redirect directive was canceled.\n\n    *) Feature: the ngx_http_geo_module.\n\n    *) Feature: the proxy_set_x_var and fastcgi_set_var directives.\n\n    *) Bugfix: the location configuration with \"=\" modifier may be used in\n       another location.\n\n    *) Bugfix: the correct content type was set only for requests that use\n       small caps letters in extension.\n\n    *) Bugfix: if the proxy_pass or fastcgi_pass directives were set in the\n       location, and access was denied, and the error was redirected to a\n       static page, then the segmentation fault occurred.\n\n    *) Bugfix: if in a proxied \"Location\" header was a relative URL, then a\n       host name and a slash were added to them; the bug had appeared in\n       0.1.14.\n\n    *) Bugfix: the system error message was not logged on Linux.\n\n\nChanges with nginx 0.1.16                                        25 Jan 2005\n\n    *) Bugfix: if the response were transferred by chunks, then on the HEAD\n       request the final chunk was issued.\n\n    *) Bugfix: the \"Connection: keep-alive\" header were issued, even if the\n       keepalive_timeout directive forbade the keep-alive use.\n\n    *) Bugfix: the errors in the ngx_http_fastcgi_module caused the\n       segmentation faults.\n\n    *) Bugfix: the compressed response encrypted by SSL may not transferred\n       complete.\n\n    *) Bugfix: the TCP-specific TCP_NODELAY, TCP_NOPUSH, and TCP_CORK\n       options, are not used for the unix domain sockets.\n\n    *) Feature: the rewrite directive supports the arguments rewriting.\n\n    *) Bugfix: the response code 400 was returned for the POST request with\n       the \"Content-Length: 0\" header; the bug had appeared in 0.1.14.\n\n\nChanges with nginx 0.1.15                                        19 Jan 2005\n\n    *) Bugfix: the error while the connecting to the FastCGI server caused\n       segmentation fault.\n\n    *) Bugfix: the correct handling of the regular expression, that has\n       different number of the captures and substitutions.\n\n    *) Feature: the location, that is passed to the FastCGI server, can be\n       regular expression.\n\n    *) Bugfix: the FastCGI's parameter REQUEST_URI is now passed with the\n       arguments and in the original state.\n\n    *) Bugfix: the ngx_http_rewrite_module module was required to be built\n       to use the regular expressions in locations.\n\n    *) Bugfix: the directive \"proxy_preserve_host on\" adds port 80 to the\n       \"Host\" headers, if upstream listen on port 80; the bug had appeared\n       in 0.1.14.\n\n    *) Bugfix: the same paths in autoconfiguration parameters\n       --http-client-body-temp-path=PATH and --http-proxy-temp-path=PATH, or\n       --http-client-body-temp-path=PATH and --http-fastcgi-temp-path=PATH\n       caused segmentation fault.\n\n\nChanges with nginx 0.1.14                                        18 Jan 2005\n\n    *) Feature: the autoconfiguration directives:\n       --http-client-body-temp-path=PATH, --http-proxy-temp-path=PATH, and\n       --http-fastcgi-temp-path=PATH\n\n    *) Change: the directory name for the temporary files with the client\n       request body is specified by directive client_body_temp_path, by\n       default it is <prefix>/client_body_temp.\n\n    *) Feature: the ngx_http_fastcgi_module and the directives:\n       fastcgi_pass, fastcgi_root, fastcgi_index, fastcgi_params,\n       fastcgi_connect_timeout, fastcgi_send_timeout, fastcgi_read_timeout,\n       fastcgi_send_lowat, fastcgi_header_buffer_size, fastcgi_buffers,\n       fastcgi_busy_buffers_size, fastcgi_temp_path,\n       fastcgi_max_temp_file_size, fastcgi_temp_file_write_size,\n       fastcgi_next_upstream, and fastcgi_x_powered_by.\n\n    *) Bugfix: the \"[alert] zero size buf\" error; the bug had appeared in\n       0.1.3.\n\n    *) Change: the URI must be specified after the host name in the\n       proxy_pass directive.\n\n    *) Change: the %3F symbol in the URI was considered as the argument\n       string start.\n\n    *) Feature: the unix domain sockets support in the\n       ngx_http_proxy_module.\n\n    *) Feature: the ssl_engine and ssl_ciphers directives.\n       Thanks to Sergey Skvortsov for SSL-accelerator.\n\n\nChanges with nginx 0.1.13                                        21 Dec 2004\n\n    *) Feature: the server_names_hash and server_names_hash_threshold\n       directives.\n\n    *) Bugfix: the *.domain.tld names in the \"server_name\" directive did not\n       work.\n\n    *) Bugfix: the %request_length log parameter logged the incorrect\n       length.\n\n\nChanges with nginx 0.1.12                                        06 Dec 2004\n\n    *) Feature: the %request_length log parameter.\n\n    *) Bugfix: when using the /dev/poll, select and poll on the platforms,\n       where these methods may do the false reports, there may be the long\n       delay when the request was passed via the keep-alive connection. It\n       may be at least on Solaris when using the /dev/poll.\n\n    *) Bugfix: the send_lowat directive is ignored on Linux because Linux\n       does not support the SO_SNDLOWAT option.\n\n\nChanges with nginx 0.1.11                                        02 Dec 2004\n\n    *) Feature: the worker_priority directive.\n\n    *) Change: both tcp_nopush and tcp_nodelay directives affect the\n       transferred response.\n\n    *) Bugfix: nginx did not call initgroups().\n       Thanks to Andrew Sitnikov and Andrei Nigmatulin.\n\n    *) Change: now the ngx_http_autoindex_module shows the file size in the\n       bytes.\n\n    *) Bugfix: the ngx_http_autoindex_module returned the 500 error if the\n       broken symlink was in a directory.\n\n    *) Bugfix: the files bigger than 4G could not be transferred using\n       sendfile.\n\n    *) Bugfix: if the backend was resolved to several backends and there was\n       an error while the response waiting then process may got caught in an\n       endless loop.\n\n    *) Bugfix: the worker process may exit with the \"unknown cycle\" message\n       when the /dev/poll method was used.\n\n    *) Bugfix: \"close() channel failed\" errors.\n\n    *) Bugfix: the autodetection of the \"nobody\" and \"nogroup\" groups.\n\n    *) Bugfix: the send_lowat directive did not work on Linux.\n\n    *) Bugfix: the segmentation fault occurred if there was no events\n       section in configuration.\n\n    *) Bugfix: nginx could not be built on OpenBSD.\n\n    *) Bugfix: the double slashes in \"://\" in the URI were converted to\n       \":/\".\n\n\nChanges with nginx 0.1.10                                        26 Nov 2004\n\n    *) Bugfix: if the request without arguments contains \"//\", \"/./\", \"/../\"\n       or \"%XX\" then the last character in the request line was lost; the\n       bug had appeared in 0.1.9.\n\n    *) Bugfix: the fix in 0.1.9 for the files bigger than 2G on Linux did\n       not work.\n\n\nChanges with nginx 0.1.9                                         25 Nov 2004\n\n    *) Bugfix: the proxied request was sent without arguments if the request\n       contains \"//\", \"/./\", \"/../\" or \"%XX\".\n\n    *) Bugfix: the large compressed responses may be transferred not\n       completely.\n\n    *) Bugfix: the files bigger than 2G was not transferred on Linux that\n       does not support sendfile64().\n\n    *) Bugfix: while the build configuration on Linux the --with-poll_module\n       parameter was required; the bug had appeared in 0.1.8.\n\n\nChanges with nginx 0.1.8                                         20 Nov 2004\n\n    *) Bugfix: in the ngx_http_autoindex_module if the long file names were\n       in the listing.\n\n    *) Feature: the \"^~\" modifier in the location directive.\n\n    *) Feature: the proxy_max_temp_file_size directive.\n\n\nChanges with nginx 0.1.7                                         12 Nov 2004\n\n    *) Bugfix: on FreeBSD the segmentation fault may occur if the size of\n       the transferred file was changed; the bug had appeared in 0.1.5.\n\n\nChanges with nginx 0.1.6                                         11 Nov 2004\n\n    *) Bugfix: some location directive combinations with the regular\n       expressions caused the wrong configuration choose.\n\n\nChanges with nginx 0.1.5                                         11 Nov 2004\n\n    *) Bugfix: on Solaris and Linux there may be too many \"recvmsg()\n       returned not enough data\" alerts.\n\n    *) Bugfix: there were the \"writev() failed (22: Invalid argument)\"\n       errors on Solaris in proxy mode without sendfile. On other platforms\n       that do not support sendfile at all the process got caught in an\n       endless loop.\n\n    *) Bugfix: segmentation fault on Solaris in proxy mode and using\n       sendfile.\n\n    *) Bugfix: segmentation fault on Solaris.\n\n    *) Bugfix: on-line upgrade did not work on Linux.\n\n    *) Bugfix: the ngx_http_autoindex_module module did not escape the\n       spaces, the quotes, and the percent signs in the directory listing.\n\n    *) Change: the decrease of the copy operations.\n\n    *) Feature: the userid_p3p directive.\n\n\nChanges with nginx 0.1.4                                         26 Oct 2004\n\n    *) Bugfix: in the ngx_http_autoindex_module.\n\n\nChanges with nginx 0.1.3                                         25 Oct 2004\n\n    *) Feature: the ngx_http_autoindex_module and the autoindex directive.\n\n    *) Feature: the proxy_set_x_url directive.\n\n    *) Bugfix: proxy module may get caught in an endless loop when sendfile\n       is not used.\n\n\nChanges with nginx 0.1.2                                         21 Oct 2004\n\n    *) Feature: the --user=USER, --group=GROUP, and --with-ld-opt=OPTIONS\n       options in configure.\n\n    *) Feature: the server_name directive supports *.domain.tld.\n\n    *) Bugfix: the portability improvements.\n\n    *) Bugfix: if configuration file was set in command line, the\n       reconfiguration was impossible; the bug had appeared in 0.1.1.\n\n    *) Bugfix: proxy module may get caught in an endless loop when sendfile\n       is not used.\n\n    *) Bugfix: with sendfile the response was not recoded according to the\n       charset module directives; the bug had appeared in 0.1.1.\n\n    *) Bugfix: very seldom bug in the kqueue processing.\n\n    *) Bugfix: the gzip module compressed the proxied responses that was\n       already compressed.\n\n\nChanges with nginx 0.1.1                                         11 Oct 2004\n\n    *) Feature: the gzip_types directive.\n\n    *) Feature: the tcp_nodelay directive.\n\n    *) Feature: the send_lowat directive is working not only on OSes that\n       support kqueue NOTE_LOWAT, but also on OSes that support SO_SNDLOWAT.\n\n    *) Feature: the setproctitle() emulation for Linux and Solaris.\n\n    *) Bugfix: the \"Location\" header rewrite bug fixed while the proxying.\n\n    *) Bugfix: the ngx_http_chunked_module module may get caught in an\n       endless loop.\n\n    *) Bugfix: the /dev/poll module bugs fixed.\n\n    *) Bugfix: the responses were corrupted when the temporary files were\n       used while the proxying.\n\n    *) Bugfix: the unescaped requests were passed to the backend.\n\n    *) Bugfix: while the build configuration on Linux 2.4 the\n       --with-poll_module parameter was required.\n\n\nChanges with nginx 0.1.0                                         04 Oct 2004\n\n    *) The first public version.\n"
  },
  {
    "path": "CHANGES.cn",
    "content": "Tengine 3.1.0 [2023-10-27]\n* Security: 修复HTTP2安全漏洞CVE-2023-44487 (lianglli) \n* Feature: 基于tengine-ingress，允许动态配置不同域名的多个TLS协议版本 (lianglli)\n* Feature: 基于tengine-ingress，允许动态配置基于多个Header值的高级路由 (lianglli, dreamwind1985)\n* Feature: 基于tengine-ingress，允许动态配置基于多个Cookie值的高级路由 (lianglli, dreamwind1985)\n* Feature: 基于tengine-ingress，允许动态配置基于多个Query参数值的高级路由 (lianglli, dreamwind1985)\n* Feature: 基于tengine-ingress，允许动态配置基于Header取模的高级路由 (lianglli, dreamwind1985)\n* Feature: 基于tengine-ingress，允许动态配置基于Cookie取模的高级路由 (lianglli, dreamwind1985)\n* Feature: 基于tengine-ingress，允许动态配置基于Query取模的高级路由 (lianglli, dreamwind1985)\n* Feature: 基于tengine-ingress，允许高级路由动态配置流量染色，请求消息增加Header (lianglli, dreamwind1985)\n* Feature: 基于tengine-ingress，允许高级路由动态配置流量染色，请求消息追加Header (lianglli, dreamwind1985)\n* Feature: 基于tengine-ingress，允许高级路由动态配置流量染色，请求消息增加Query参数 (lianglli, dreamwind1985)\n* Feature: 基于tengine-ingress，允许高级路由动态配置流量染色，响应消息增加Header (lianglli, dreamwind1985)\n* Feature: 新增listen选项https_allow_http，允许TLS端口处理HTTP请求 (drawing)\n* Feature: 新增ngx_http_debug_conn_module模块，调试连接信息 (hongxiaolong)\n* Bugfix: 修复HTTP/3 Lua模块证书回调问题 (drawing)\n* Bugfix: 修复HTTP/3 XQUIC模块连接句柄泄漏问题 (dreamwind1985)\n* Bugfix: 修复HTTP/3 XQUIC模块静态证书匹配问题 (lurker-Chen)\n* Bugfix: 修复HTTP/3 XQUIC模块调用ngx_http_find_virtual_server的crash问题 (morf)\n* Bugfix: 修复异步SSL模块使用thread_pool的crash问题 (zhsnew)\n* Bugfix: 修复ngx_http_grpc_module模块使用ngx_tongsuo_ntls的编译问题 (dongbeiouba)\n* Bugfix: 修复ngx_http_debug_conn_module模块的单元测试问题 (chobits)\n* Bugfix: 修复ngx_getcpuinfo的日志问题 (fuchencong)\n\n\nTengine 3.0.0 [2023-07-06]\n* Feature: 支持域名，证书，路由的动态无损生效 [tengine-ingress] (drawing, lianglli)\n* Feature: 支持HTTP/3 (QUIC v1和draft-29)[XQUIC] (lurker-Chen, Kulsk, lianglli)\n* Feature: 支持bypass内核的用户态高性能UDP转发 [XUDP] (D-Wythe, fengidri)\n* Feature: 支持基于header，header值和服务权重的高级路由动态无损生效 [tengine-ingress] (drawing, lianglli)\n* Feature: 支持配置timeout，强制HTTPS，CORS和robots的动态无损生效 [tengine-ingress] (drawing, lianglli)\n* Change: 升级core代码到Nginx-1.24.0稳定版本 (lianglli)\n* Bugfix: 修复ssl_async开启时ngx_ssl_shutdown导致的coredump问题 (foxriver1025)\n\n\nTengine 2.4.1 [2023-06-08]\n* Change: 更新ngx_http_proxy_connect_module模块到v0.0.4 (chobits)\n* Bugfix: 修复stream_set模块编译错误 (chobits)\n* Bugfix: 修复NTLS证书检测，将sign/enc证书移到upstream，增加ngx_tongsuo_ntls模块 (dongbeiouba)\n* Bugfix: 修复当开启ssl_async功能后，客户端无法接受到正确数据包的问题 (oyaya)\n\n\nTengine 2.4.0 [2023-02-08]\n* Feature: 更新到nginx官方1.22.1版本 (jiuzhoucui)\n\n\nTengine 2.3.4 [2022-10-18]\n* Feature: 增加新模块ngx_openssl_ntls，以支持NTLS协议 (dongbeiouba)\n* Change: 将ngx_openssl_ntls模块中依赖的SSL库从BabaSSL更新为Tongsuo (wa5i)\n* Bugfix: 修复CVE-2021-23017 (chobits)\n* Bugfix: 修复upstream check模块在upstream配置块中使用zone指令时导致的死锁 (zjd87)\n* Bugfix：修复upstream check编译错误 (RocFang)\n* Bugfix: 修复dubbo编译报错 (MengqiWu)\n\n\nTengine 2.3.3 [2021-03-25]\n* Feature: 支持dtlsv1和dtlsv1.2 (mrpre)\n* Feature: ngx_http_upstream_check_module模块增加prometheus格式和json属性 (dkrutsko)\n* Feature: mod_dubbo模块支持dubbo_pass中使用变量 (spacewander)\n* Change: 升级core代码版本到Nginx-1.18.0 (lianglli)\n* Change: 文档README增加Tengine钉钉群号 (cnmade)\n* Change: 更新mod_dubbo模块的说明文档 (spacewander)\n* Bugfix: 修复mod_dubbo模块解码int32错误的问题 (spacewander)\n* Bugfix: 修复mod_dubbo模块解码整型payload出现core dump的问题(spacewander)\n* Bugfix: 修复ngx_http_lua_module模块在debug日志级别下的内存泄漏问题 (hawkxiang)\n* Bugfix: 修复ngx_multi_upstream_module模块未释放fake请求的问题 (spacewander)\n* Bugfix: 修复ngx_http_upstream_check_module模块中的共享内存互斥锁问题 (scriptkids)\n* Bugfix: 去除ngx_http_upstream_check_module模块中冗余的健康检查 (scriptkids)\n* Bugfix: 删除ngx_multi_upstream_module模块日志相关的重复代码 (spacewander)\n* Bugfix: 修复ngx_http_upstream_dyups_module读消息且打开健康检查导致CPU负载过高的问题 (zjd87)\n* Bugfix: 修复ngx_http_upstream_vnswrr_module不支持dynamic_resolve指令的问题 (wangfakang)\n* Bugfix: 修复limit_req_zone指令在多个变量中使用的问题 (wangfakang)\n* Bugfix: 修复master进程偶现core dump问题 (wangfakang)\n* Bugfix: 修复rewrite字符串含'\\0'引起的内存泄漏问题 (hongxiaolong)\n* Bugfix: 删除mod_dubbo模块未使用的变量hex_str (Weiliang-Li)\n* Bugfix: 修复keep-alive请求未发送完整导致400错误响应的问题 (fishgege)\n\n\nTengine 2.3.2 [2019-08-20]\n* Security: 修复HTTP2安全漏洞CVE-2019-9511, CVE-2019-9513和CVE-2019-9516 (wangfakang)\n* Feature: 支持后端Dubbo协议 (MenqqiWu)\n* Feature: upstream支持VNSWRR高效负载均衡算法 (wangfakang)\n* Feature: dynamic_resolve模块支持IPv6 (wangfakang)\n* Change: proxy_connect模块支持动态编译并增加调试日志 (chobits)\n* Change: 升级core代码版本到Nginx-1.17.3 (wangfakang)\n* Change: 更新健康检查模块使用文档 (zhangqx2010)\n* Change: 更新README文档(Lin-Buo-Ren)\n* Bugfix: 修复健康检查模块的JSON输出格式问题 (IYism)\n* Bugfix: 修复ngx-lua模块在init_worker_by_lua*指令下的问题 (wangfakang)\n* Bugfix: 修复dyups模块与OpenSSL兼容性问题 (wangfakang)\n\n\nTengine 2.3.1 [2019-06-18]\n* Feature: stream ssl模块新增$ssl_handshakd_time变量 [mrpre]\n* Feature: upstream check模块支持websocket健康检查 (mrpre)\n* Change: round robin负载均衡算法采用随机起点 (wangfakang)\n* Change: 升级http lua模块到v0.10.14 (mrpre)\n* Change: 升级dyups模块到yzprofile/dyups的最新主干版本 (chobits)\n* Change: 升级Nginx版本到Nginx-1.16.0(MenqqiWu)\n* Change: 支持reqstatus模块的dso编译 (chobits)\n* Change: 支持upstream dynamic模块的dso编译 (wangfakang)\n* Change: 支持trim模块的dso编译 (wangfakang)\n* Change: 支持footer模块的dso编译 (wangfakang)\n* Change: 支持user_agent模块的dso编译 (wangfakang)\n* Change: 支持concat模块的dso编译 (mathieu-aubin)\n* Bugfix: 修复部分服务端返回的HTTP头部服务端版本信息  (AstroProfundis)\n* Bugfix: 修复\"-m\"参数显示dynamic module的问题 (wangfakang)\n* Bugfix: 修复limit_req指令的参数检查 (wangfakang)\n* Bugfix: 修复reqstatus在macOS下的兼容性问题 (chobits)\n\n\nTengine 2.3.0 [2019-03-25]\n* Feature: 新增proxy_connect模块，可以支持HTTP的CONNECT方法 [chobits]\n* Feature: Stream模块server配置块支持server_name指令 [mrpre]\n* Feature: reqstat模块新增req_status_lazy指令 [taoyuanyuan]\n* Feature: 新增http2指令，可以在server配置块中控制http2开关 [jinjiu]\n* Feature: 新增ssl_handshake_time变量，统计SSL握手时间 [jinjiu]\n* Feature: limit_req_zone指令的rate参数支持变量模式 [Alaaask]\n* Change: 更新debug_pool模块，适配Nginx官方1.15.9 [chobits]\n* Change: 更新reuse_port、dso、limit_req指令使用文档 [chobits, wangfakang]\n* Change: 合并官方limit_req逻辑，当对应key所有的变量值为空才跳过统计 [chobits]\n* Change: 删除reuse_port、dso、slice指令，统一使用Nginx官方对应的功能 [wangfakang]\n* Change: 适配并合并官方1.15.9测试用列 [chobits, wangfakang]\n* Change: 对Tengine自身模块全部剥离到modules目录下，减少对core的入侵 [chobits, wangfakang]\n* Change: 升级合并官方1.15.9代码，并同步官方新功能如Stream、gRPC等 [chobits, wangfakang]\n* Change: 更新Lua模块至v0.10.14rc4 [wangfakang]\n* Change: 更新dyups文档 [lf1029698952]\n* Change: 对core代码的修改全部使用宏区分开 [chobits, wangfakang, fankeke, hongxiaolong, imkeeper]\n* Change: 恢复accpte_filter功能 [wangfakang]\n* Bugfix: 修复dyups模块在和高版本OpenSSL一起编译的报错问题 [wangfakang]\n* Bugfix: 修复dyups模块init_number初始化逻辑 [FengXingYuXin]\n* Bugfix: 修复rollback日志进程在reload时可能导致日志写到滚动后的文件里面 [MengqiWu]\n* Bugfix: 修复ssl_verify_client_exception指令导致的coredump问题 [chobits]\n* Bugfix: 修复dyups、session_sticky模块在升级core代码后导致的coredump问题 [wangfakang]\n* Bugfix: 修复limit_req、http2模块的编译错误 [hongxiaolong]\n* Bugfix: 修复日志pipe进程在关闭监听socket时误删除监听Unix domain文件 [wangfakang]\n* Bugfix: 修复静态加载OpenSSL库时导致async openssl无法检测 [mrpre]\n* Bugfix: 修复ngx_http_top_input_body_filter被删除的问题 [chobits]\n* Bugfix: 修复reuse_port和accept_mutex指令冲突问题 [innomentats]\n* Bugfix: 修复在高版本GCC下的编译报错问题 [wangfakang]\n\n\nTengine 2.2.3 [2018-11-11]\n* Security: 修复安全漏洞CVE-2018-16843，CVE-2018-16844和CVE-2018-16845 [chobits]\n\n\nTengine 2.2.2 [2018-01-26]\n* Feature: 支持异步OpenSSL，可使用相关硬件(如：QAT)进行SSL的卸载 [mrpre]\n* Feature: 支持TLS1.3，TLS握手0-RTT [jlijian3] \n* Feature: upstream配置块里面支持include指令 [wangfakang] \n* Change：更新并修复测试用例 [wangfakang]\n* Change: 恢复ssl_verify_client_exception指令 [jinjiu]  \n* Change: 更新Debian相关软件包版本号 [PeterDaveHello]\n* Change: 更新README文档 [taoyuanyuan]\n* Bugfix: 修复upstream_check模块在reload时可能触发段错误 [yongjianchn]\n* Bugfix: 修复pipe模块在FreeBSD系统中编译报错问题 [MengqiWu]\n\n\nTengine 2.2.1 [2017-09-27]\n* Security: Nginx range过滤器整形溢出漏洞(CVE–2017–7529) [hongxiaolong]\n* Feature: 新增ngx_slab_stat模块，可获取NGINX/Tengine共享内存池的slab和空闲页状态信 [hongxiaolong]\n* Feature: pipe指令新增rollback参数，可基于时间、文件大小等维度进行日志回滚功能 [MengqiWu]\n* Feature: 新增dns cache功能，可避免dns服务不可用时导致Tengine无法正常启停 [wangfakang]\n* Feature: 新增sysguard_cpu指令，基于cpu保护维度来限制用户的请求 [wangfakang]\n* Change: 更新range filter模块，避免启始位置为负数 [jinjiu]\n* Change: 同步nginx官方1.13.4版本的ngx_slab，为了启用slab相关统计信息 [hongxiaolong]\n* Change: 更新http2相关测试文件：Nginx.pm和HTTP2.pm [chobits]\n* Change: 更新http2模块，同步nginx官方1.11.6版本http2 [PeterDaveHello]\n* Change: 更新tengine core文档 [chobits]\n* Change: 去掉在OS X系统中废弃使用OpenSSL library时的警告信息 [chobits]\n* Change: 更新travis.yml配置文件 [PeterDaveHello]\n* Bugfix: 修复proxy_upstream_tries，避免lua子请求会多重试一次后端 [wangfakang]\n* Bugfix：修复reuse_port和accept_mutex的冲突 [gwtony]\n* Bugfix：修复reuse_port在worker_processes设置为auto时不生效 [wangfakang]\n* Bugfix: 修复开启reuse_port后执行nginx -t失败问题 [chobits]\n* Bugfix: 修复pipe模块返回值异常情况 [chobits]\n* Bugfix: 修复configure检测报错 [chobits]\n* Bugfix：修复aio选项在freebsd系统中缺少检查的问题 [chobits]\n\n\nTengine 2.2.0 [2016-11-29]\n* Security: 进程将特殊构造的请求体写到临时文件时会触发段错误 (CVE-2016-4450) [0x7E]\n* Feature:  增加force_exit指令 [aholic, chobits]\n* Feature:  debug pool模块，该模块可以获取nginx内存池的内存使用情况 [chobits]\n* Change:   合并HTTP/2模块，删除SPDY模块 [PeterDaveHello]\n* Change:   支持nginx官方syslog，删除tengine原先支持的syslog功能\n* Change:   合并nginx-1.8.1版本的修改 [lhanjian, magicbear, chobits]\n* Change:   支持EPOLL_EXCLUSIVE [cfsego]\n* Change:   导出API: ngx_http_upstream_check_upstream_down [detailyang]\n* Change:   在TCP健康检查功能中关闭check_keepalive_requests特性 [cynron]\n* Change:   更新reqstatus模块 [cfsego]\n* Bugfix:   删除ngx_http_named_location中重复代码 [innomentats]\n* Bugfix:   修复session-sticky模块的bug [detailyang]\n* Bugfix:   修复rsolve.conf文件解析器的bug [zuopucuen]\n* Bugfix:   修复tfs模块编译警告 [monadbobo]\n* Bugfix:   dynamic_resolver特性在proxy_pass指令使用变量时会触发段错误 [chobits]\n* Bugfix:   修复session-sticky模块设置无效的Set-Cookie值的bug [YanagiEiichi]\n* Bugfix:   修复dyups模块中cf变量未初始化的bug [wangfakang]\n* Bugfix:   修复健康检查模块中含有重复peers的bug [FqqCS, taoyuanyuan]\n* Bugfix:   修复concat模块中错误的javascript内容类型 [IYism]\n\n\nTengine 2.1.1 [2015-08-12]\n* Feature: 支持动态upstream更新 [yzprofile]\n* Feature: 增强ngx_http_reqstat_module模块 [cfsego]\n* Feature: 增加ssl_verify_client_exception指令 [InfoHunter]\n* Change:  降低解析配置的内存消耗 [ilexshen]\n* Change:  trim模块增加$trim_bytes和$trim_original_bytes [taoyuanyuan]\n* Change:  升级debian二进制包的版本到2.1.0 [PeterDaveHello]\n* Change:  将ngx_http_spdy_module模块集成到travis-ci自动编译 [chobits]\n* Change:  更新SPDY/3.1 [chobits]\n* Change:  SPDY时关闭proxy_request_buffering指令 [chobits]\n* Change:  增加编译选项支持设置linker [tanguofu]\n* Bugfix:  修复SPDY的Backport bug [nginx official, ym]\n* Bugfix:  SPDY和SSL模块一起编译时报错 [ym]\n* Bugfix:  修复打开reuseport的bug [monadbobo]\n\n\nTengine 2.1.0 [2014-12-19]\n* Feature: 支持SO_REUSEPORT选项，以提升CPU负载均衡性和性能 [monadbobo]\n* Feature: 支持动态解析upstream中出现的域名 [InfoHunter]\n* Feature: rewrite指令支持重定向到命名location [yzprofile]\n* Feature: image_filter指令支持crop_keepx和crop_keepy参数 [Lax]\n* Feature: consistent_hash模块和session_sticky模块支持SSL会话保持 [dinic]\n* Feature: 支持travis-ci.org自动编译 [Jamyn]\n* Feature: 健康检查模块支持FASTCGI检查 [yzprofile]\n* Feature: 增强sysguard模块的功能 [InfoHunter]\n* Feature: 新增变量$normalized_request得到规范化的请求 [yunkai]\n* Feature: dso的include指令支持通配符 [monadbobo]\n* Feature: 新增gzip_clear_etag指令 [taoyuanyuan]\n* Feature: 为log_escape指令添加unprintable参数 [skoo87]\n* Change: 合并nginx-1.6.2版本的修改 [cfsego, taoyuanyuan, chobits]\n* Change: round robin负载均衡算法随机选择某个源站作为循环起点 [taoyuanyuan]\n* Change: 对共享内存中的碎片进行优化 [chobits]\n* Bugfix: SPDY/3关闭连接时去掉drop标志 [chobits]\n* Bugfix: 修复SPDY/3出现连接泄漏的问题 [chobits]\n* Bugfix: 修复limit_req模块将长度超过255个字符的key截断的问题 [chobits]\n* Bugfix: 解析/etc/resolv.conf中的IPv6地址出错 [lifeibo]\n* Bugfix: 通过红黑树查找到错误的upstream [taoyuanyuan]\n\n\nTengine 2.0.3 [2014-05-30]\n* Feature: 支持按指定维度(域名，url等)收集Tengine运行状态 [cfsego]\n* Feature: 支持debian、ubuntu打包 [betetrpm, szepeviktor]\n* Change: 合并nginx-1.4.7的修改 [chobits]\n* Change: 使用红黑树优化upstream配置解析和查找 [SarahWang]\n* Change: 更新版权信息\n* Bugfix: 修复session-sticky模块相关问题 [dinic]\n* Bugfix: 修复DSO编译和安装模块的问题 [cfsego]\n* Bugfix: 修复spdy相关问题 [chobits]\n\n\nTengine 2.0.2 [2014-03-28]\n* Bugfix: 在读事件处理完后继续发送SPDY数据 [chobits]\n* Bugfix: CVE-2014-0133以及CVE-2014-0088 [chobits]\n\n\nTengine 2.0.1 [2014-03-06]\n* Feature: 请求体不缓存的机制支持chunked输入 [yaoweibin]\n* Feature: trim模块支持更多规则，支持根据变量启用 [taoyuanyuan]\n* Feature: 利用/etc/resolv.conf自动配置resolver [lifeibo, yaoweibin]\n* Feature: 增加$ascii_变量前缀，可以生成任意ASCII字符 [yzprofile]\n* Feature: 增加\"image_filter_crop_offset\"指令 [lax]\n* Change: 合并截至nginx-1.4.6版本的所有修改 [chobits, cfsego]\n* Bugfix: 修正使用长连接进行健康检查时报错的问题 [lilbedwin]\n* Bugfix: 修正使用WebSocket时nginx崩溃的问题\n          http://trac.nginx.org/nginx/ticket/503 [Hao Chen]\n* Bugfix: 减少nginx处理大文件时的内存消耗 [cfsego]\n* Bugfix: 在未设置URI时，禁用跳转到named locations的重定向\n\n\nTengine 2.0.0 [2014-01-08]\n* Feature: 增强DSO模块，编译动态模块不再依赖原始编译环境 [monadbobo]\n* Feature: 支持SPDY v3协议，自动检测同一端口的SPDY请求和HTTP请求 [lilbedwin, chobits]\n* Feature: 支持设置proxy、memcached、fastcgi、scgi、uwsgi在后端失败时的重试次数 [supertcy]\n* Feature: tfs模块在RcServer心跳时汇报访问统计 [zhcn381]\n* Feature: if指令支持比较数值大小：'>'、'<'、'>='、'<=' [flygoast]\n* Feature: 健康检查模块支持长连接检查，增加\"check_keepalive_requests\"指令 [lilbedwin]\n* Feature: trim模块支持SSI和ESI的注释 [taoyuanyuan]\n* Feature: expires_by_types指令支持使用通配符，例如'text/*'匹配子类型 [zhcn381]\n* Feature: 增加$base64_decode_变量前缀，支持计算指定变量的base64解压结果 [yzprofile]\n* Feature: 增加$md5_encode_变量前缀，支持计算指定变量的md5哈希 [yzprofile]\n* Feature: 增加$time_http变量，支持按http格式输出当前时间 [flygoast]\n* Feature: 增加$full_request变量，取得原始的请求url，包括协议类型和域名 [yzprofile]\n* Feature: 增加$escape_uri_变量前缀，支持对指定变量进行url转义 [yzprofile]\n* Feature: 增加$raw_uri变量，支持取得不含参数的原始uri [flygoast]\n* Feature: 支持按微秒记录子请求的请求时间 [jinglong]\n* Feature: 增加API，支持对url进行base64编码 [lilbedwin]\n* Change: 合并nginx-1.4.4版本的修改 [cfsego]\n* Change: 修改stub_status模块，不对子请求进行统计 [jinglong]\n* Bugfix: 修正footer模块，不处理含有Content-Encoding头的响应 [yaoweibin]\n* Bugfix: 修正client_body_postpone_size指令设置为0时出现的问题 [yaoweibin]\n* Bugfix: 修正Lua模块编译时出现警告 [diwayou]\n\n\nTengine-1.5.2 [2013-11-22]\n* Security: 修复CVE-2013-4547安全漏洞\n* Bugfix: 修复limit_req模块中nodelay无效的问题 [cfsego]\n* Bugfix: 修复trim模块在替换javascript异常的问题 [taoyuanyuan]\n\n\nTengine-1.5.1 [2013-08-29]\n* Feature: 增加retry_cached_connection指令，可以关闭对后端长连接的无条件重试 [yaoweibin]\n* Feature: sysguard模块的sysguard_load指令中加入ncpu参数 [yzprofile]\n* Bugfix：修复referer模块在https协议时正则匹配失效的问题 [lilbedwin]\n* Bugfix：修复trim模块可能产生0长度块的问题 [taoyuanyuan]\n* Bugfix：修复在使用--without-dso选项时出现的编译错误 [zhuzhaoyuan]\n* Bugfix：修复两个编译警告 [zzjin, diwayou]\n\n\nTengine-1.5.0 [2013-07-31]\n* Feature: 增加DSO（动态模块加载）兼容性校验机制 [monadbobo]\n* Feature: 增加了请求体不缓存到磁盘的机制，HTTP代理和FastCGI模块收到部分请求体即可以转发给后端服务器 [yaoweibin]\n* Feature: 增加了trim模块，该模块可以自动删除HTML页面中无意义的空白符和注释，减小页面的大小 [taoyuanyuan]\n* Feature: 加入accept filter机制，支持在连接接收以后进行过滤处理 [yzprofile]\n* Feature: 现在server_tag指令可以改变默认错误页面的服务器标识 [zhuzhaoyuan]\n* Bugfix：修复access_log指令中buffer参数失效的问题 [cfsego]\n* Bugfix：修复session_sticky模块在某些情况下没有发出session cookie的问题 [dinic]\n\n\nTengine-1.4.6 [2013-05-14]\n* Bugfix：合并nginx-1.2.9的更新，修正CVE-2013-2070带来的安全问题。该安全问题在1.4.0以后开始出现 [yaoweibin]\n\n\nTengine-1.4.5 [2013-05-01]\n* Feature：增加一致性hash模块，可以为后端服务器提供一致性hash的负载均衡方法 [dinic]\n* Feature：通过keepalive_timeout指令可以设置后端keepalive连接的超时时间 [jinglong]\n* Feature：加入所有模块静态编译或者所有模块动态编译的编译选项 [monadbobo]\n* Change：更新Lua模块至0.7.19 [jinglong]\n* Change：合并Nginx-1.2.8的更新 [yaoweibin]\n* Bugfix：修正syslog和upstream_check模块在GCC-4.4.5上的编译警告 [magicbear]\n\n\nTengine-1.4.4 [2013-03-21]\n* Feature：增加session_sticky模块，可以为客户端和后端服务器提供会话保持功能 [dinic]\n* Feature：sysguard模块增加空闲内存监控功能 [lifeibo]\n* Feature：geoip模块增加对地区数据库的支持 [jasonlfunk]\n* Feature：log_empty_request指令增加对408响应的空请求支持 [yaoweibin]\n* Change：合并Nginx-1.2.5至Nginx-1.2.7的更新 [cfsego]\n* Change：默认关闭CPU亲缘性 [cfsego]\n* Bugfix：修正在Solaris 11上sysguard和upstream_check模块编译出错的问题 [lifeibo, yaoweibin]\n* Bugfix：修正TFS模块返回值可能错误的问题 [zhcn381]\n* Bugfix: 修正TFS模块上传大文件可能出错的问题 [zhcn381]\n\n\nTengine-1.4.3 [2013-1-21]\n* Feature：增加TFS模块，可以通过RESTful接口与TFS分布式文件系统通信 [zhcn381, monadbobo]\n* Feature：增加$sent_cookie_XXX系列变量，可以获取响应中Set-Cookie头的cookie值 [skoo87]\n* Feature：syslog指令的发送地址支持域名 [cfsego]\n* Change：upstream块中的server指令增加id属性 [yaoweibin]\n* Bugfix：DSO模块修正reload时可能失败的问题 [monadbobo]\n* Bugfix：修复upstream_check模块当超时时间长于检查时间可能导致段错误的问题 [yaoweibin]\n* Bugfix：修复user_agent模块在请求缺少User-Agent头会段错误的问题 [dinic]\n* Bugfix：修复sysguard模块在Mac OS下面不能工作的问题 [lizi]\n\n\nTengine-1.4.2 [2012-11-22]\n* Feature：增加--dso-tool-path配置选项，可以选择dso_tool脚本的安装目录 [monadbobo]\n* Feature：增加$unix_time变量，表示当前的时间戳秒数 [yaoweibin]\n* Feature：Makefile中增加test命令以便运行测试用例 [yaoweibin]\n* Feature：sysguard模块可在location里面配置 [lifeibo]\n* Change：合并Nginx-1.2.4和Nginx-1.2.5的更新 [zhuzhaoyuan]\n* Change：增加对input_filter函数返回值的检查，防止第三方模块调用出错 [cfsego]\n* Bugfix：修复limit_req指令不能使用4个参数的问题，感谢LazyZhu的报告 [monadbobo]\n* Bugfix：修复在cygwin下面编译sysinfo文件出错的问题，感谢Cao Peiran的报告 [lifeibo]\n* Bugfix：修复user-agent模块安装时需要拷贝browsers配置文件的问题，感谢Jianbin Xiao的报告 [monadbobo]\n* Bugfix：修复DSO模块RPM打包安装目录出错的问题，感谢Jianbin Xiao和Ren Xiaolei的报告 [monadbobo]\n\n\nTengine-1.4.1 [2012-10-10]\n* Feature： 添加jemalloc库的支持 [fanjizhao]\n* Feature： 加入$dollar变量, 它的值就是美元符号 [zhuzhaoyuan]\n* Feature： 为worker_cpu_affinity指令加入off选项 [cfsego]\n* Change： 当工作进程异常退出以后，新进程不会绑定CPU亲缘性 [cfsego]\n* Bugfix:  修正在Mac OS操作系统下，Lua模块与LuaJIT动态编译时出现的错误 [monadbobo]\n* Bugfix:  修正动态编译第三方filter模块时出现的模块执行顺序错误 [monadbobo]\n\n\nTengine-1.4.0 [2012-09-05]\n* Feature： 增加动态模块加载支持（DSO），要添加一个模块不再需要重新编译tengine了 [monadbobo]\n* Feature： 更新Lua模块到最新的稳定版本 [chaoslawful, agentzh, jinglong]\n* Feature： 为健康检查模块增加json和csv格式的输出 [yaoweibin]\n* Feature： 增加log_empty_request指令，可以用来关掉空请求日志——那些连接了但没发数据的连接 [zhuzhaoyuan]\n* Feature： 给concat模块增加concat_delimiter指令，设置文件间的间隔内容 [dinic]\n* Feature： 增加concat_ignore_file_error指令并允许concat的语法更宽松 [dinic] \n* Feature： 给error_page指令增加default选项，以恢复所有的错误页面为默认值 [jinglong]\n* Feature： 给proc模块增加priority指令，可设置proc进程的优先级 [yzprofile]\n* Feature： 给proc模块增加delay_start指令，可设置延迟加载时间 [yzprofile]\n* Change： 集成最新nginx稳定版本1.2.3的内容 [zhuzhaoyuan]\n* Bugfix： 修正一个geo模块设了range但是没有默认值时产生的段错误问题 [yzprofile]\n* Bugfix： 修正一个proc模块的空指针问题 [yzprofile]\n* Bugfix： 修正一个健康检查模块的socket泄漏问题 [yaoweibin]\n* Bugfix： 修正limit_req模块的若干问题 [monadbobo]\n* Bugfix： 修正若干日志输出类型错误的问题 [yaoweibin]\n* Bugfix： 修正perl模块和proc一起打开的一个编译错误 [yzprofile]\n\n\nTengine-1.3.0 [2012-05-25]\n* Feature：加入Lua模块，可以在配置中使用Lua语言 [chaoslawful, agentzh]\n* Feature：加入procs模块，可以更方便的开启独立进程 [yzprofile]\n* Change：user_agent模块中参数nongreedy改名为greedy [dinic]\n* Bugfix：修复syslog指令中因为指针未初始化引起的段错误 [cfsego]\n* Bugfix：修复syslog指令打开--with-ipv6选项引起的编译错误 [cfsego]\n\n\nTengine-1.2.5 [2012-05-09]\n* Feature：增加upstream_check模块，对后端服务器做主动健康检查，以自动的下线失效的服务器 [yaoweibin]\n* Feature：允许syslog输出日志时指定程序的标识（program identifier） [cfsego]\n* Change：合并nginx-1.0.14至nginx-1.0.15之间的修改 [zhuzhaoyuan]\n* Change：将accept_mutex_delay的默认值从500毫秒更改为100毫秒以提高性能 [zhuzhaoyuan]\n* Bugfix：修复syslog的一个在后端服务器连接不上导致端错误的bug [cfsego]\n* Bugfix：修复access_log可能和buffer参数冲突的bug [cfsego]\n\n\nTengine-1.2.4 [2012-03-30]\n* Feature：增加user_agent模块 [dinic]\n* Feature：增加log_escape指令 [agentzh, skoo87]\n* Change：合并nginx-1.0.12至nginx-1.0.14之间的修改 [zhuzhaoyuan]\n* Bugfix：修复limit_req模块的一个bug [liseen.wan]\n* Bugfix：修复subrequest的一个bug [lifeibo]\n\n\nTengine-1.2.3 [2012-02-27]\n* Feature：增加request_time_cache指令，用来控制是否启用精确的响应时间 [yzprofile]\n* Feature：增加slice模块，获得一个文件的一个片段，可以添加头和尾 [zhuzhaoyuan]\n* Change：合并nginx-1.0.11至nginx-1.0.12之间的修改 [zhuzhaoyuan]\n* Change：去掉无用的user-agent判断 [zhuzhaoyuan]\n* Bugfix：修复upstream中的一个process_header的bug [lifeibo]\n* Bugfix：修复expires_by_types的一个bug [lifeibo]\n\n\nTengine-1.2.2 [2012-01-11]\n* Feature：增加input body filter机制 [cfsego]\n* Feature：对mail部分支持ssl的dialog [cfsego]\n* Change：合并进nginx-1.0.10至nginx-1.0.11之间的修改 [zhuzhaoyuan]\n* Change：默认关掉lingering_close [zhuzhaoyuan]\n* Bugfix：修正日志管道时的bug [cfsego]\n* Bugfix：修正limit_req的forbid_action无效的bug [monadbobo]\n* Bugfix： 修正backtrace模块backtrace_max_stack_size的问题 [monadbobo]\n* Bugfix：修正内容为空footer模块输出不正确的问题 [dinic]\n* Bugfix：修正syslog时hostname最后一个字母丢失的问题 [cfsego]\n\n\nTengine-1.2.1 [2011-12-06]\n* Bugfix：修正默认错误日志和访问日志不存在时启动报错的bug [yzprofile]\n\n\nTengine-1.2.0 [2011-11-29]\n* Feature：错误日志和访问日志支持输出到syslog [cfsego]\n* Feature：错误日志和访问日志支持输出到管道的方式 [cfsego]\n* Feature：增加realloc相关的API，包括内存池 [gongyuan]\n* Feature：HTTP日志支持更多的有关时间的变量 [skoo87]\n* Feature：增加backtrace模块，在coredump时输出调用栈 [monadbobo]\n* Feature：limit_req功能增强，增加白名单，可以有多个条件 [monadbobo]\n* Feature：sysguard模块，load和内存占用偏高时进行保护 [lifeibo]\n* Feature：增加API（ngx_http_header_in/ngx_http_header_out），用来取输入和输出的HTTP头信息 [lifeibo]\n* Feature：增加两个变量$request_time_msec和$request_time_usec，分别是相应时间的毫秒表示和微秒表示 [jinglong]\n* Feature：增加footer模块，可以在HTML末尾添加内容（支持变量） [yunxing]\n* Feature：增加变量$conn_requests，记录当前request是连接上的第几个 [lieyuan]\n* Feature：增加变量$host_comment，插入注释功能，以说明哪台机器产生的请求 [yunxing]\n* Feature：访问日志增加ratio参数，抽样功能，可以减少日志的记录量 [cfsego]\n* Feature：增加server_admin、server_info指令，出错信息提示，更友好的错误页面 [lieyuan]\n* Feature：增加命令行参数-d，把配置文件的内容全部打印出来 [piaoling]\n* Feature：增加指令expires_by_types，可以根据types来设置超时 [lifeibo]\n* Feature：增加命令行参数-l，可以列出所有的directives [dinic]\n* Feature：增加ngx_atoll的api，可以将字符串转换成64位整数，支持32位与64位系统 [lifeibo]\n* Feature：status line（302，405）现在采用RFC 2616的标准 [zhuzhaoyuan]\n* Feature：ngx_escape_uri/ngx_unescape_uri现在支持PHP/Java的编解码格式 [zhuzhaoyuan]\n* Feature：配置文件include多个文件时按照字母顺序进行包含 [zhuzhaoyuan]\n* Feature：error_page指令增强，支持default，可以把把上一级设置的error_page重新设定 [zhuzhaoyuan]\n* Feature：增加对每请求的响应时间的统计（cacti，tsar） [jinglong]\n* Feature：增加指令server_tag，更强大的控制HTTP服务器的Server头是否显示以及内容是什么 [jinglong]\n* Feature：自动调整worker进程的数目和绑定CPU亲缘性 [cfsego]\n* Feature：增加指令ssl_pass_phrase_dialog，SSL对key进行加密功能 [cfsego]\n* Feature：增加-s选项的start参数 [zhuzhaoyuan]\n* Feature：增加-m选项，可以把已编译的模块列出来 [zhuzhaoyuan]\n* Change：更改msie_padding的默认值为关掉 [zhuzhaoyuan]\n* Bugfix：修复open_file_cache在已经检测到缓存文件发生变化后，仍然返回过时的文件状态信息的问题 [cfsego]\n* Bugfix：修复upsteam在subrequest in memory且keepalive时会导致timeout的bug [lifeibo]\n* Bugfix：修复$sent_http_connection和$sent_http_keep_alive记录不正确的问题 [zhongsheng]\n* Bugfix：修正error_page不能发现重复的code的问题，不能正常继承上一级设置的问题 [zhuzhaoyuan]\n* Bugfix：修正Nginx在处理FastCGI时有重复HTTP头会core dump的bug [monadbobo]\n* Bugfix：修正Nginx对CPU亲缘性设置不正确的bug [cfsego]\n"
  },
  {
    "path": "CHANGES.te",
    "content": "Changes with Tengine 3.1.0                                         27 Oct 2023\n\n    *) Security: fixed HTTP2 CVE-2023-44487 [lianglli]\n\n    *) Feature: dynamically configure different TLS protocols for different server names without tengine reload based on tengine-ingress [lianglli]\n\n    *) Feature: dynamically configure HTTP routes based on multiple values of a specific header without tengine reload based on tengine-ingress [lianglli, dreamwind1985]\n\n    *) Feature: dynamically configure HTTP routes based on multiple values of a specific cookie without tengine reload based on tengine-ingress [lianglli, dreamwind1985]\n    \n    *) Feature: dynamically configure HTTP routes based on multiple values of a specific query parameter without tengine reload based on tengine-ingress [lianglli, dreamwind1985]\n\n    *) Feature: dynamically configure HTTP routes based on the modulo operation for a specific header without tengine reload based on tengine-ingress [lianglli, dreamwind1985]\n\n    *) Feature: dynamically configure HTTP routes based on the modulo operation for a specific cookie without tengine reload based on tengine-ingress [lianglli, dreamwind1985]\n\n    *) Feature: dynamically configure HTTP routes based on the modulo operation for a specific query parameter without tengine reload based on tengine-ingress [lianglli, dreamwind1985]\n\n    *) Feature: dynamically configure HTTP routes to add custom headers to the HTTP request without tengine reload based on tengine-ingress [lianglli, dreamwind1985]\n\n    *) Feature: dynamically configure HTTP routes to append custom headers to the HTTP request without tengine reload based on tengine-ingress [lianglli, dreamwind1985]\n\n    *) Feature: dynamically configure HTTP routes to add query parameter to the HTTP request without tengine reload based on tengine-ingress [lianglli, dreamwind1985]\n\n    *) Feature: dynamically configure HTTP routes to add custom headers to the HTTP response without tengine reload based on tengine-ingress [lianglli, dreamwind1985]\n\n    *) Feature: add new option https_allow_http of listen for receiving HTTP traffic on the TLS listener (drawing)\n\n    *) Feature: add new module ngx_http_debug_conn_module for debugging connection info (hongxiaolong)\n\n    *) Bugfix: fixed HTTP/3 Lua callback issue (drawing)\n\n    *) Bugfix: fixed FD leak issue in HTTP/3 XQUIC module (dreamwind1985)\n\n    *) Bugfix: fixed certificate matching issue with static configuration file in HTTP/3 XQUIC module (lurker-Chen)\n\n    *) Bugfix: fixed crash for calling ngx_http_find_virtual_server in the HTTP/3 XQUIC module (morf)\n\n    *) Bugfix: fixed crash for using thread_pool in SSL asynchronous mode (zhsnew)\n\n    *) Bugfix: fixed compilation error of ngx_http_grpc_module with ngx_tongsuo_ntls (dongbeiouba)\n\n    *) Bugfix: fixed test case issues of new module ngx_http_debug_conn_module (chobits)\n\n    *) Bugfix: fixed log error of ngx_getcpuinfo (fuchencong)\n\n\nChanges with Tengine 3.0.0                                         06 Jul 2023\n\n    *) Feature: dynamically reconfigure the servers, locations and upstreams without reloading or restarting worker processes [tengine-ingress] (drawing, lianglli)\n\n    *) Feature: HTTP/3 support (QUIC v1 and draft-29)[XQUIC] (lurker-Chen, Kulsk, lianglli)\n\n    *) Feature: high-speed UDP transmission with kernel-bypass [XUDP] (D-Wythe, fengidri)\n\n    *) Feature: dynamically reconfigure canary routing based on standard and custom HTTP headers, header value, and weights [tengine-ingress] (drawing, lianglli)\n    \n    *) Feature: dynamically reconfigure timeout setting, SSL Redirects, CORS and enabling/disabling robots for the ingress/path [tengine-ingress] (drawing, lianglli)\n\n    *) Change: update core to stable version Nginx-1.24.0 (lianglli)\n\n    *) Bugfix: fixed coredump caused by ngx_ssl_shutdown() with ssl_async enabled (foxriver1025)\n\n\nChanges with Tengine 2.4.1                                         08 Jun 2023\n\n    *) Change: updated ngx_http_proxy_connect_module to v0.0.4 (chobits)\n\n    *) Bugfix: fixed compilation error in stream_set module (chobits)\n\n    *) Bugfix: fixed NTLS cert check, move sign/enc certficate to upstream,\n       add ngx_tongsuo_ntls module (dongbeiouba)\n\n    *) Bugfix: fixed bug that client cannot receive right packages with\n       ssl_async enabled (oyaya)\n\n\nChanges with Tengine 2.4.0                                         08 Feb 2023\n\n    *) Feature: Update to nginx 1.22.1  (jiuzhoucui)\n\n\nChanges with Tengine 2.3.4                                         18 Oct 2022\n\n    *) Feature: added new module ngx_openssl_ntls to support NTLS protocol\n       (dongbeiouba)\n\n    *) Change: updated SSL library from BabaSSL to Tongsuo in the\n       ngx_openssl_ntls module (wa5i)\n\n    *) Bugfix: fixed CVE-2021-23017 (chobits)\n\n    *) Bugfix: fixed deadlock in the upstream check module with \"zone\"\n       directive configured in upstream block (zjd87)\n\n    *) Bugfix：fixed compilation in the upstream check module (RocFang)\n\n    *) Bugfix: fixed compilation error in the dubbo module (MengqiWu)\n\n\nChanges with Tengine 2.3.3                                         25 Mar 2021\n\n    *) Feature: tengine supports dtlsv1 and dtlsv1.2. (mrpre)\n\n    *) Feature: prometheus format and additional json properties \n       was added to ngx_http_upstream_check_module. (dkrutsko)\n\n    *) Feature: the \"dubbo_pass\" directive can use the variables. (spacewander)\n\n    *) Change: all features of nginx-1.18.0 are inherited, i.e., \n       it is 100% compatible with nginx. (lianglli)\n\n    *) Change: dingtalk user group was added to README. (cnmade)\n\n    *) Change: format document of the mod_dubbo. (spacewander)\n\n    *) Bugfix: int32 values are not decoded properly in the mod_dubbo. (spacewander)\n\n    *) Bugfix: a segmentation fault might occur in a worker process \n       when decoding a dubbo payload with integer value in the mod_dubbo. (spacewander) \n\n    *) Bugfix: memory leak in ngx_http_lua_module with debug log. (hawkxiang)\n\n    *) Bugfix: fake request was not freed in the ngx_multi_upstream_module. (spacewander)\n\n    *) Bugfix: shared memory mutex in the ngx_http_upstream_check_module. (scriptkids)\n\n    *) Bugfix: redundant upstream health check was removed \n       in the ngx_http_upstream_check_module. (scriptkids)\n\n    *) Bugfix: duplicate log_ctx was deleted in the ngx_multi_upstream_module. (spacewander)\n\n    *) Bugfix: tengine hogged CPU during reading message in the ngx_http_upstream_dyups_module \n       and when upstream check was used. (zjd87)\n\n    *) Bugfix: ngx_http_upstream_vnswrr_module did not support \"dynamic_resolve\" directive. (wangfakang)\n\n    *) Bugfix: \"limit_req_zone\" directive were used in multiple variables. (wangfakang)\n\n    *) Bugfix: a segmentation fault might occur in a master process. (wangfakang)\n\n    *) Bugfix: memory leak when rewrite string contains ASCII 0 character. (hongxiaolong)\n\n    *) Bugfix: variable hex_str was not used in the mod_dubbo. (Weiliang-Li)\n\n    *) Bugfix: keep-alive request did not transferred complete caused the 400 response. (fishgege)\n\n\nChanges with Tengine 2.3.2                                         20 Aug 2019\n\n    *) Security: fixed CVE-2019-9511, CVE-2019-9513 and CVE-2019-9516. (wangfakang)\n\n    *) Feature: added dubbo_pass directive to\n       support the back-end HTTP to Dubbo protocol. (MenqqiWu)\n\n    *) Feature: added VNSWRR algorithm for upstream module. (wangfakang)\n\n    *) Feature: support IPv6 for dynamic_resolve module. (wangfakang)\n\n    *) Change: support dynamic build and add some debug log\n       for proxy_connect module. (chobits)\n\n    *) Change: updated the code from Nginx-1.17.3 version. (wangfakang)\n\n    *) Change: updated the health_check module document. (zhangqx2010)\n\n    *) Change: updated README document. (Lin-Buo-Ren)\n\n    *) Bugfix: fixed JSON format for health_check module. (IYism)\n\n    *) Bugfix: ensured 'init_worker_by_lua*' does not\n       mutate another Nginx module's main_conf. (wangfakang)\n\n    *) Bugfix: fixed compilation error of dyups module compiled\n       with a higher version of OpenSSL. (wangfakang)\n\n\nChanges with Tengine 2.3.1                                         18 Jun 2019\n\n    *) Feature: add $ssl_handshakd_time variable for stream ssl module (mrpre)\n\n    *) Feature: support websocket check of upstream check module (mrpre)\n\n    *) Change: random index logical for round robin (wangfakang)\n\n    *) Change: update http lua module to v0.10.14 (mrpre)\n\n    *) Change: update dyups to master branch of yzprofile/dyups (chobits)\n\n    *) Change: update core to Nginx-1.16.0 (MenqqiWu)\n\n    *) Change: support dynamic module for reqstatus (chobits)\n\n    *) Change: support dynamic build for upstream dynamic module (wangfakang)\n\n    *) Change: support dynamic build for trim module (wangfakang)\n\n    *) Change: support dynamic build for footer module (wangfakang)\n\n    *) Change: support dynamic build for user_agent module (wangfakang)\n\n    *) Change: support dynamic build for concat module (mathieu-aubin)\n\n    *) Bugfix: server version strings in http2 and stream response headers (AstroProfundis)\n\n    *) Bugfix: \"-m\" option to show dynamic module (wangfakang)\n\n    *) Bugfix: parameter number check for limit_req directive (wangfakang)\n\n    *) Bugfix: fixed compilation error on macOS for reqstatus (chobits)\n\n\nChanges with Tengine 2.3.0                                         25 Mar 2019\n\n    *) Feature: added proxy_connect module support for the CONNECT\n       HTTP method. (chobits)\n\n    *) Feature: added server_name directive for Stream module. (mrpre)\n\n    *) Feature: added req_status_lazy directive for reqstat module. (taoyuanyuan)\n\n    *) Feature: added http2 directive to enable or disable http2\n       in the server block. (jinjiu)\n\n    *) Feature: added $ssl_handshake_time variable used for monitoring\n       SSL handshake time. (jinjiu)\n\n    *) Feature: added support of variable of limit_req_zone\n       parameter rate. (Alaaask)\n\n    *) Change: updated debug_pool module for Nginx 1.15.9. (chobits)\n\n    *) Change: updated documents for reuse_port, dso, limit_req\n       directive changes. (chobits, wangfakang)\n\n    *) Change: merged the official limit_req logic. Now will ignore statistics\n       when all variable values are empty. (chobits)\n\n    *) Change: the reuse_port, dso, slice directive has been removed and\n       use the official features of Nginx. (wangfakang)\n\n    *) Change: updated and modify the official 1.15.9 test cases.\n       (chobits, wangfakang)\n\n    *) Change: put all Tengine's modules into the modules directory\n       which reduces the intrusion of Nginx's core module. (chobits, wangfakang)\n\n    *) Change: updated the code from Nginx-1.15.9 version,\n       Stream, gRPC etc. (chobits, wangfakang)\n\n    *) Change: updated the Lua module to v0.10.14rc4. (wangfakang)\n\n    *) Change: updated the dyups document. (lf1029698952)\n\n    *) Change: changes of the core code are all guarded by macros.\n       (chobits, wangfakang, fankeke, hongxiaolong, imkeeper)\n\n    *) Change: rollback  accpte_filter feature. (wangfakang)\n\n    *) Bugfix: fixed compilation error of dyups module compiled\n       with a higher version of OpenSSL. (wangfakang)\n\n    *) Bugfix: fixed init_number initialization for dyups. (FengXingYuXin)\n\n    *) Bugfix: fixed the rollback log process that may cause logs\n       to be written to a rolled-up file when reloaded. (MengqiWu)\n\n    *) Bugfix: fixed coredump of referring null pointer\n       for ssl_verify_client_exception. (chobits)\n\n    *) Bugfix: fixed coredump caused by upgrading core code\n       in dyups and session_sticky modules. (wangfakang)\n\n    *) Bugfix: fixed compilation error of limit_req, http2 module. (hongxiaolong)\n\n    *) Bugfix: fixed removes the Unix domain socket file\n       when pipe proc close listen socket. (wangfakang)\n\n    *) Bugfix: fixed compatibility for --with-openssl\n       and --with-openssl-async. (mrpre)\n\n    *) Bugfix: fixed bug that function ngx_http_top_intput_body_filter\n       is removed mistakenly. (chobits)\n\n    *) Bugfix: fixed reuse_port and accept_mutex conflict. (innomentats)\n\n    *) Bugfix: fixed tengine build failure when compiled with\n       gcc7 compiler. (wangfakang)\n\n\nChanges with Tengine 2.2.3                                         11 Nov 2018\n\n    *) Security: fixed CVE-2018-16843, CVE-2018-16844 and\n       CVE-2018-16845. (chobits)\n\n\nChanges with Tengine 2.2.2                                         26 Jan 2018\n\n    *) Feature: support asynchronous SSL/TLS mode, Could use QAT to\n       offload and accelerated SSL. (mrpre)\n\n    *) Feature: support TLS1.3 and 0-RTT data. (jlijian3) \n\n    *) Feature: enabled \"include\" inside http upstreams. (wangfakang)\n\n    *) Change: update and fixed the test cases. (wangfakang)\n\n    *) Change: restore the functionality of the ssl_verify_client_exception\n       directive. (jinjiu)\n\n    *) Change: updated the Debian package version number. (PeterDaveHello)\n\n    *) Change: updated README document. (taoyuanyuan)\n\n    *) Bugfix: fixed bug that the upstream_check module could trigger a segment\n       while reload. (yongjianchn)\n\n    *) Bugfix: fixed compiler error for pipe module on FreeBSD. (MengqiWu)\n\n\nChanges with Tengine 2.2.1                                         27 Sep 2017\n\n    *) Security: range filter protect from total size overflows.\n       (CVE–2017–7529) (hongxiaolong)\n\n    *) Feature: added ngx_slab_stat module to show shared memory statistics.\n       (hongxiaolong)\n\n    *) Feature: added rollback parameter for pipe to support roll back log file\n       by time or file size. (MengqiWu)\n\n    *) Feature: added dns cache to avoid tengine reload, start or stop etc\n       failed when dns server is unavailable. (wangfakang)\n\n    *) Feature: added sysguard_cpu to protect the system by monitoring\n       the CPU usage. (wangfakang)\n\n    *) Change: Range filter: avoid negative range start. (jinjiu)\n\n    *) Change: updated ngx_slab from nginx-1.13.4 to enable slab slots and\n       free pages statistics. (hongxiaolong)\n\n    *) Change: update test cases: Nginx.pm and HTTP2.pm. (chobits)\n\n    *) Change: HTTP/2 updates from nginx v1.9.8~1.11.6. (PeterDaveHello)\n\n    *) Change: updated core document. (chobits)\n\n    *) Change: remove the warning message when using deprecated system OpenSSL\n       library on OS X. (chobits)\n\n    *) Change: updated travis.yml configure. (PeterDaveHello)\n\n    *) Bugfix: fixed \"proxy_upstream_tries\" number of retry backend\n       is incorrect for lua subrequest. (wangfakang)\n\n    *) Bugfix: fixed bug reuse_port and accept_mutex conflict. (gwtony)\n\n    *) Bugfix: fixed bug when the worker_processes is set to auto caused\n       reuse_port did not work. (wangfakang)\n\n    *) Bugfix: fixed bug of used reuse_port caused nginx -t failed. (chobits)\n\n    *) Bugfix: fixed bug of check return value of pipe module. (chobits)\n\n    *) Bugfix: fixed check configure error. (chobits)\n\n    *) Bugfix: fixed check aio configure option in auto/os/freebsd. (chobits)\n\n\nChanges with Tengine 2.2.0                                         29 Nov 2016\n\n    *) Security: a segmentation fault might occur in a worker process\n       while writing a specially crafted request body to a temporary file\n       (CVE-2016-4450) (0x7E)\n\n    *) Feature: the \"force_exit\" directive. (aholic, chobits)\n\n    *) Feature: debug pool module which can get memory usage of nginx memory\n       pool. (chobits)\n\n    *) Change:  merged HTTP/2 module, SPDY module is removed. (PeterDaveHello)\n\n    *) Change:  official nginx syslog support, tengine syslog support is removed.\n\n    *) Change:  merged changes from nginx-1.8.1. (lhanjian, magicbear, chobits)\n\n    *) Change:  support for EPOLL_EXCLUSIVE. (cfsego)\n\n    *) Change:  export api: ngx_http_upstream_check_upstream_down. (detailyang)\n\n    *) Change:  disable \"check_keepalive_requests\" feature for TCP health check.\n       (cynron)\n\n    *) Change:  updated reqstatus module. (cfsego)\n\n    *) Bugfix:  remove duplicate code in ngx_http_named_location (innomentats)\n\n    *) Bugfix:  fixed bug of session-sticky module. (detailyang)\n\n    *) Bugfix:  fixed bug of resolve.conf parser. (zuopucuen)\n\n    *) Bugfix:  fixed the compile warning of tfs module. (monadbobo)\n\n    *) Bugfix:  fixed a segmentation fault of dynamic_resolver feature when\n       variable is used in proxy_pass directive. (chobits)\n\n    *) Bugfix:  fixed bug of invalid Set-Cookie value in session-sticky module.\n       (YanagiEiichi)\n\n    *) Bugfix:  fixed bug of uninitialized 'cf' variable in dyups module.\n       (wangfakang)\n\n    *) Bugfix:  fixed bug of duplicate peers in health check module.\n       (FqqCS, taoyuanyuan)\n\n    *) Bugfix:  fixed bug of wrong javascript content-type in concat module.\n       (IYism)\n\n\nChanges with Tengine 2.1.1                                         12 Aug 2015\n\n    *) Feature: support for dynamic upstream update. (yzprofile)\n\n    *) Feature: enchanced ngx_http_reqstat_module. (cfsego)\n\n    *) Feature: added ssl_verify_client_exception directive. (InfoHunter)\n\n    *) Change:  Reduced memory usage while parsing configuration. (ilexshen)\n\n    *) Change:  added $trim_bytes and $trim_original_bytes. (taoyuanyuan)\n\n    *) Change:  upgrade debian package to 2.1.0 (PeterDaveHello)\n\n    *) Change:  support for auto compile for ngx_http_spdy_module. (chobits)\n\n    *) Change:  updated SPDY/3.1. (chobits)\n\n    *) Change:  disabled 'proxy_request_buffering' for SPDY. (chobits)\n\n    *) Change:  added configue options to support set linker. (tanguofu)\n\n    *) Bugfix:  fixed Backport bug of SPDY (nginx official, ym)\n\n    *) Bugfix:  fixed compile error with SSL. (ym)\n\n    *) Bugfix:  fixed bug of reuseport. (monadbobo)\n\n\nChanges with Tengine 2.1.0                                         19 Dec 2014\n\n    *) Feature: support the SO_REUSEPORT option, to improve performance on\n       multicore systems. (monadbobo)\n\n    *) Feature: support for resolving upstream domain names on the fly.\n       (InfoHunter)\n\n    *) Feature: support for rewriting to named locations. (yzprofile)\n\n    *) Feature: added two parameters 'crop_keepx' and 'crop_keepy' to the\n       directive 'image_filter'. (Lax)\n\n    *) Feature: support for saving SSL sessions in consistent_hash module and\n       session_sticky module. (dinic)\n\n    *) Feature: support for compiling Tengine automatically in travis-ci.org.\n       (Jamyn)\n\n    *) Feature: support for FastCGI health check. (yzprofile)\n\n    *) Feature: enhanced sysguard module. (InfoHunter)\n\n    *) Feature: added a variable '$normalized_request', to get normalized\n       request URIs. (yunkai)\n\n    *) Feature: added wildcard support for 'include' directive in 'dso' block.\n       (monadbobo)\n\n    *) Feature: added the 'gzip_clear_etag' directive. (taoyuanyuan)\n\n    *) Feature: added the 'unprintable' parameter to the 'log_escape' directive.\n       (skoo87)\n\n    *) Change: merged changes from nginx-1.6.2. (cfsego, taoyuanyuan, chobits)\n\n    *) Change: now the order of servers in an upstream are random when\n       initialized. (taoyuanyuan)\n\n    *) Change: slab allocator free pages defragmentation. (chobits)\n\n    *) Bugfix: SPDY/3 dropped the \"delayed\" flag when finalizing connection.\n       (chobits)\n\n    *) Bugfix: fixed SPDY/3 connection leak. (chobits)\n\n    *) Bugfix: now don't truncate value of key to 255 bytes in limit_req module.\n       (chobits)\n\n    *) Bugfix: failed to parse /etc/resolv.conf with IPv6 addresses. (lifeibo)\n\n    *) Bugfix: upstream rbtree bugfix. (taoyuanyuan)\n\n\nChanges with Tengine 2.0.3                                         30 May 2014\n\n    *) Feature: added support for collecting the running status of Tengine\n       according to specific key (domain, url, etc). (cfsego)\n\n    *) Feature: added support for generating package of debian/ubuntu\n       format(*.deb). (betetrpm, szepeviktmr)\n\n    *) Change: merged changes between nginx-1.4.6 and nginx-1.4.7. (chobits)\n\n    *) Change: optimized the parsing and searching strategy of upstream by\n       using rbtree. (SarahWang)\n\n    *) Change: updated the copyright.\n\n    *) Bugfix: fixed bugs of session-sticky module. (dinic)\n\n    *) Bugfix: fixed compiling and installing issues of DSO modules. (cfsego)\n\n    *) Bugfix: fixed bugs of SPDY protocol. (chobits)\n\n\nChanges with Tengine 2.0.2                                       28 March 2014\n\n    *) Bugfix: send output queue after processing of read event in SPDY. (chobits)\n\n    *) Bugfix: CVE-2014-0133 and CVE-2014-0088. (chobits)\n\n\nChanges with Tengine 2.0.1                                       06 March 2014\n\n    *) Feature: now non-buffering request body mechanism supports chunked input.\n       (yaoweibin)\n\n    *) Feature: trim module added more rules, and now can be enabled according\n       to variables. (taoyuanyuan)\n\n    *) Feature: resolver can be configured automatically from /etc/resolv.conf.\n       (lifeibo, yaoweibin)\n\n    *) Feature: added variables starting with \"$ascii_\", which can represent\n       arbitrary ASCII characters. (yzprofile)\n\n    *) Feature: added a new directive \"image_filter_crop_offset\". (lax)\n\n    *) Change: merged changes between nginx-1.4.4 and nginx-1.4.6. (chobits, cfsego)\n\n    *) Bugfix: upstream health check module failed occasionally when using\n       keep-alive connections. (lilbedwin)\n\n    *) Bugfix: nginx crashed when upstream rejected nginx WebSocket connection.\n       http://trac.nginx.org/nginx/ticket/503 (Hao Chen)\n\n    *) Bugfix: reduce nginx memory consumption when processing large files.\n       (cfsego)\n\n    *) Bugfix: disabled redirects to named locations if URI is not set.\n\n\nChanges with Tengine 2.0.0                                       08 Jan 2014\n\n    *) Feature: now DSO module does not need the original source code or\n       compiler options when compiling a new module. (monadbobo)\n\n    *) Feature: added support for SPDY v3, and SPDY/HTTP servers can listen on\n       the same port. (lilbedwin, chobits)\n\n    *) Feature: added support for setting retries for upstream servers (proxy,\n       memcached, fastcgi, scgi, uwsgi). (supertcy)\n\n    *) Feature: now tfs module can report access status to rcs while keepalive.\n       (zhcn381)\n\n    *) Feature: now the directive \"if\" supports \">\", \"<\", \">=\", \"<=\" operators\n       for numeric comparison. (flygoast)\n\n    *) Feature: now upstream health check module uses keep-alive connections.\n       added a new directive \"check_keepalive_requests\". (lilbedwin)\n\n    *) Feature: now trim module can handle SSI and ESI comments properly.\n       (taoyuanyuan)\n\n    *) Feature: now directive \"expires_by_types\" supports wildcard such as\n       \"text/*\". (zhcn381)\n\n    *) Feature: added variables starting with \"$base64_decode_\" to encode\n       variables in base64. (yzprofile)\n\n    *) Feature: added variables starting with \"$md5_encode_\" to encode variables\n       in md5. (yzprofile)\n\n    *) Feature: added a variable \"$time_http\" to get the current HTTP time.\n       (flygoast)\n\n    *) Feature: added a variable \"$full_request\" to get the original request\n       URL with scheme and host. (yzprofile)\n\n    *) Feature: added variables starting with \"$escape_uri_\" to escape variables\n       into formal URL syntax. (yzprofile)\n\n    *) Feature: added a variable \"$raw_uri\" to get the original URI without\n       arguments. (flygoast)\n\n    *) Feature: added support for logging subrequests in nanoseconds. (jinglong)\n\n    *) Feature: added a new API function to encode URL into base64. (lilbedwin)\n\n    *) Change: merged changes between nginx-1.2.9 and nginx-1.4.4. (cfsego)\n\n    *) Change: now stub_status module does not log subrequests. (jinglong)\n\n    *) Bugfix: fixed a bug in footer module when reading a response with\n       a \"Content-Encoding\" header. (yaoweibin)\n\n    *) Bugfix: fixed a bug when \"client_body_postpone_size\" is set to 0.\n       (yaoweibin)\n\n    *) Bugfix: fixed a compilation warning of Lua module. (diwayou)\n\n\nChanges with Tengine 1.5.2                                       22 Nov 2013\n\n    *) Security: a character following an unescaped space in a request line\n       was handled incorrectly (CVE-2013-4547); the bug had appeared in\n       0.8.41.\n       Thanks to Ivan Fratric of the Google Security Team.\n\n    *) Bugfix: fix a bug that 'nodelay' might be ignored in limit_req module.\n       (cfsego)\n\n    *) Bugfix: fix a bug in trim module when processing JavaScript comment.\n       (taoyuanyuan)\n\n\nChanges with Tengine 1.5.1                                       29 Aug 2013\n\n    *) Feature: added the directive 'retry_cached_connection' which could\n       disable unconditional retries with a cached backend connection.\n       (yaoweibin)\n\n    *) Feature: added the argument of 'ncpu' to 'sysguard_load' directive.\n       (yzprofile)\n\n    *) Bugfix: fixed a bug in referer module that regex rules might be\n       invalid with https requests. (lilbedwin)\n\n    *) Bugfix: fixed a bug that the trim module might send a zero-size\n       buffer. (taoyuanyuan)\n\n    *) Bugfix: fixed a compile error when using the configure option\n       '--without-dso'. (zhuzhaoyuan)\n\n    *) Bugfix: fixed two compile warnings. (zzjin, diwayou)\n\n\nChanges with Tengine 1.5.0                                       31 Jul 2013\n\n    *) Feature: added ABI compatibility verification for DSO modules.\n       (monadbobo)\n\n    *) Feature: added non-buffering request body mechanism. Now the http proxy\n       and fastcgi module can send requests to backend servers when it receives\n       part of a request body. (yaoweibin)\n\n    *) Feature: added trim module which can remove unnecessary white spaces and\n       comments to reduce the size of a page. (taoyuanyuan)\n\n    *) Feature: added the accept filter mechanism which supports to do some\n       filter processing after accepting a new connection. (yzprofile)\n\n    *) Feature: Now the server banner in a default error page can be replaced\n       by the string specified in server_tag. (zhuzhaoyuan)\n\n    *) Bugfix: fixed the bug of the 'buffer' argument might be ignored in the\n       'access_log' directive. (cfsego)\n\n    *) Bugfix: fixed the session_sticky module didn't issue the session cookie\n       in the direct mode. (dinic)\n\n\nChanges with Tengine 1.4.6                                       14 May 2013\n\n    *) Bugfix: merged the changes of Nginx-1.2.9 and fixed the security problem\n       CVE-2013-2070. This bug had appeared in 1.4.0. (yaoweibin)\n\n\nChanges with Tengine 1.4.5                                       1 May 2013\n\n    *) Feature: added the consistent_hash module which dispatches requests\n       to upstream servers based on consistent hashing algorithm of a\n       variable specified. (dinic)\n\n    *) Feature: added the \"keepalive_timeout\" directive to set timeout for\n       the upstream keepalive connections. (jinglong)\n\n    *) Feature: now the configure script supports compilation of all modules\n       to be shared or static. (monadbobo)\n\n    *) Change: updated the Lua module to 0.7.19. (jinglong)\n\n    *) Change: merged the changes of Nginx-1.2.8. (yaoweibin)\n\n    *) Bugfix: fixed the compile warnings of syslog and upstream_check\n       modules in GCC-4.4.5. (magicbear)\n\n\nChanges with Tengine 1.4.4                                       21 Mar 2013\n\n    *) Feature: added the session_sticky module by using which one client\n       can be always served by the same upstream server. (dinic)\n\n    *) Feature: now the sysguard module can protect the server based on\n       the amount of free memory. (lifeibo)\n\n    *) Feature: added support for geoip regional database in geoip module.\n       (jasonlfunk)\n\n    *) Feature: log_empty_request can also disable the logs for timeout (408)\n       empty request. (yaoweibin)\n\n    *) Change: merged changes between Nginx-1.2.5 and Nginx-1.2.7. (cfsego)\n\n    *) Change: CPU affinity is off by default now. (cfsego)\n\n    *) Bugfix: fixed a bug that sysguard and upstream_check module didn't\n       compile on Solaris 11. (lifeibo, yaoweibin)\n\n    *) Bugfix: fixed a bug with TFS module that it might return bad values.\n       (zhcn381)\n\n    *) Bugfix: fixed a bug with TFS module that it might corrupt large files.\n       (zhcn381)\n\n\nChanges with Tengine 1.4.3                                       21 Jan 2013\n\n    *) Feature: added the TFS module which provides a RESTful API to Taobao\n       File System. (zhcn381, monadbobo)\n\n    *) Feature: added a $sent_cookie_XXX variable which could be used to get\n       the value of cookie XXX from the Set-Cookie headers. (skoo87)\n\n    *) Feature: now the syslog logging supports host name and domain name as\n       its destination address. (cfsego)\n\n    *) Change: added an attribute 'id' for the server directive in the upstream\n       block. (yaoweibin)\n\n    *) Bugfix: fixed a bug of DSO module which might stop Tengine from\n       reloading. (monadbobo)\n\n    *) Bugfix: fixed a segmentation fault bug of upstream_check module when\n       the check timeout was larger than the check interval. (yaoweibin)\n\n    *) Bugfix: fixed a segmentation fault bug of user_agent module when there\n       was no User-Agent header existed in a request. (dinic)\n\n    *) Bugfix: fixed the bug that sysguard module didn't work on Mac OS. (lizi)\n\n\nChanges with Tengine 1.4.2                                         22 Nov 2012\n\n    *) Feature: added the option '--dso-tool-path' to configure script, which\n       can specify the installation path for the dso_tool script. (monadbobo)\n\n    *) Feature: added a new variable '$unix_time', whose value is the current\n       number of seconds since unix epoch time. (yaoweibin)\n\n    *) Feature: added the 'make test' target to run test cases. (yaoweibin)\n\n    *) Feature: now the sysguard module can be used in a location block.\n       (lifeibo)\n\n    *) Change: merged the changes from Nginx-1.2.4 and Nginx-1.2.5.\n       (zhuzhaoyuan)\n\n    *) Change: now checks the error codes of input body filters more carefully\n       to avoid socket leaks. (cfsego)\n\n    *) Bugfix: fixed the problem with directive limit_req can't handle 4\n       arguments. (monadbobo)\n       Thanks to LazyZhu.\n\n    *) Bugfix: fixed a compilation error with the file of sysinfo in Cygwin.\n       (lifeibo)\n       Thanks to Cao Peiran.\n\n    *) Bugfix: now the installation script will copy the user_agent module's\n       configuration. (monadbobo)\n       Thanks to Jianbin Xiao.\n\n    *) Bugfix: fixed the installation directory error with the DSO module\n       when creating the RPM package. (monadbobo)\n       Thanks to Jianbin Xiao and Ren Xiaolei.\n\n\nChanges with Tengine 1.4.1                                         10 Oct 2012\n\n    *) Feature: added jemalloc library support. (fanjizhao)\n\n    *) Feature: added a new variable '$dollar', whose value is the dollar\n       sign ('$'). (zhuzhaoyuan)\n\n    *) Feature: added the option 'off' to 'worker_cpu_affinity' directive.\n       (cfsego)\n\n    *) Change: disable CPU affinity when a new worker process is forked as\n       an old one exits abnormally. (cfsego)\n\n    *) Bugfix: fixed compile error with shared Lua module when using LuaJIT\n       in Mac OS. (monadbobo)\n\n    *) Bugfix: fixed the wrong module execution order with the third party\n       shared filter module. (monadbobo)\n\n\nChanges with Tengine 1.4.0                                         05 Sep 2012\n\n    *) Feature: added the dynamic module loading support (a.k.a. DSO), so we\n       don't have to recompile tengine when we want to add a new module.\n       (monadbobo)\n\n    *) Feature: updated the Lua module to the latest stable version.\n       (chaoslawful, agentzh, jinglong)\n\n    *) Feature: added json and csv format output for the upstream_check\n       module. (yaoweibin)\n\n    *) Feature: added the 'log_empty_request' directive which could be used\n       to turn off logs from a connection without HTTP data. (zhuzhaoyuan)\n\n    *) Feature: added the 'concat_delimiter' directive to the concat module\n       to allow adding delimiter between each file. (dinic)\n\n    *) Feature: added the 'concat_ignore_file_error' directive to ignore file\n       errors and the syntax of concat is less strict now. (dinic)\n\n    *) Feature: added the 'default' option to the 'error_page' directive to\n       set all error pages to default values. (jinglong)\n\n    *) Feature: added the 'priority' directive of the procs module.\n       (yzprofile)\n\n    *) Feature: added the 'delay_start' directive of the procs module.\n       (yzprofile)\n\n    *) Change: merged changes from nginx-1.2.3. (zhuzhaoyuan)\n\n    *) Bugfix: fixed a segmentation fault bug of the geo module when 'range'\n       was set without default value. (yzprofile)\n\n    *) Bugfix: fixed a segmentation fault bug with the procs module.\n       (yzprofile)\n\n    *) Bugfix: fixed a socket leak bug when upstream_check was enabled.\n       (yaoweibin)\n\n    *) Bugfix: fixed some bugs of the limit_req module. (monadbobo)\n\n    *) Bugfix: fixed wrong format types with error logs. (yaoweibin)\n\n    *) Bugfix: fixed a compile error if the perl module was used with procs.\n       (yzprofile)\n\n\nChanges with Tengine 1.3.0                                         25 May 2012\n\n    *) Feature: added the Lua module which embeds the power of Lua into Tengine.\n       (chaoslawful, agentzh)\n\n    *) Feature: added the procs module which provides a mechanism to support\n       standalone processes. (yzprofile)\n\n    *) Change: renamed the parameter from 'nongreedy' to 'greedy' in the\n       user_agent module. (dinic)\n\n    *) Bugfix: fixed a segmentation fault bug in syslog with uninitialized\n       pointer problem. (cfsego)\n\n    *) Bugfix: fixed a compile error in syslog with '--with-ipv6' configuration\n       parameter. (cfsego)\n\n\nChanges with Tengine 1.2.5                                         09 May 2012\n\n    *) Feature: added the upstream_check module which could be used to do\n       proactive health check of upstream servers. (yaoweibin)\n\n    *) Feature: now allow to specify program identifiers with syslogs. (cfsego)\n\n    *) Change: merged changes between nginx-1.0.14 and nginx-1.0.15.\n       (zhuzhaoyuan)\n\n    *) Change: the default value of 'accept_mutex_delay' was changed from\n       500ms to 100ms to gain better performance. (zhuzhaoyuan)\n\n    *) Bugfix: fixed a segmentation fault bug in syslog when failed to connect\n       to an upstream server. (cfsego)\n\n    *) Bugfix: fixed the bug of 'access_log' might not be compatible with the\n       'buffer' parameter. (cfsego)\n\n\nChanges with Tengine 1.2.4                                         30 Mar 2012\n\n    *) Feature: added the user_agent module. (dinic)\n\n    *) Feature: added the 'log_escape' directive. (agentzh, skoo87)\n\n    *) Change: merged changes between nginx-1.0.12 and nginx-1.0.14.\n       (zhuzhaoyuan)\n\n    *) Bugfix: fixed a bug in the limit_req module. (liseen.wan)\n\n    *) Bugfix: fixed a bug in subrequest. (lifeibo)\n\n\nChanges with Tengine 1.2.3                                         27 Feb 2012\n\n    *) Feature: added the 'request_time_cache' directive to get more precise\n       $request_time/$request_time_msec/$request_time_usec. (yzprofile)\n\n    *) Feature: added the slice module. (zhuzhaoyuan)\n\n    *) Change: merged changes between nginx-1.0.11 and nginx-1.0.12.\n       (zhuzhaoyuan)\n\n    *) Change: deleted unused browsers detection. (zhuzhaoyuan)\n\n    *) Bugfix: fixed a bug in upstream when reading header. (lifeibo)\n\n    *) Bugfix: fixed a bug in 'expires_by_types'. (lifeibo)\n\n\nChanges with Tengine 1.2.2                                         11 Jan 2012\n\n    *) Feature: added the input body filter mechanism. (cfsego)\n\n    *) Feature: added SSL dialog support to the mail module. (cfsego)\n\n    *) Change: merged changes between nginx-1.0.10 and nginx-1.0.11.\n       (zhuzhaoyuan)\n\n    *) Change: turned 'lingering_close' off by default. (zhuzhaoyuan)\n\n    *) Bugfix: fixed a bug in pipe logs. (cfsego)\n\n    *) Bugfix: fixed a 'forbid_action' bug in the limit_req module.\n       (monadbobo)\n\n    *) Bugfix: fixed a bug in 'backtrace_max_stack_size'. (monadbobo)\n\n    *) Bugfix: fixed a bug in the footer module when output body is empty.\n       (dinic)\n\n    *) Bugfix: fixed the last hostname letter omitted bug in syslog.\n       (cfsego)\n\n\nChanges with Tengine 1.2.1                                         06 Dev 2011\n\n    *) Bugfix: fixed a segmentation fault bug when using default error log\n       or access log. (yzprofile)\n\n\nChanges with Tengine 1.2.0                                         29 Nov 2011\n\n    *) This is the first public release.\n\n    *) Feature: added syslog support to error_log and access_log. (cfsego)\n\n    *) Feature: added pipe support to error_log and access_log. (cfsego)\n\n    *) Feature: added realloc() related APIs. (gongyuan)\n\n    *) Feature: added time specific variables. (skoo87)\n\n    *) Feature: added the backtrace module. (monadbobo)\n\n    *) Feature: added whitelist support to the limit_req module. (monadbobo)\n\n    *) Feature: now more limit_req directives are allowed in a single location.\n       (monadbobo)\n\n    *) Feature: added the sysguard module. (lifeibo)\n\n    *) Feature: added two APIs, ngx_http_header_in and ngx_http_header_out.\n       (lifeibo)\n\n    *) Feature: added two variables, $request_time_msec and $request_time_usec.\n       (jinglong)\n\n    *) Feature: added the footer module. (yunxing)\n\n    *) Feature: added the $conn_requests variable which is similar to Apache's\n       '%K'. (lieyuan)\n\n    *) Feature: added the $host_comment variable. (yunxing)\n\n    *) Feature: added a 'ratio' parameter to access_log so now access log can\n       be sampled. (cfsego)\n\n    *) Feature: added the 'server_info' and 'server_admin' directives to show\n       more information when 4xx/5xx errors encountered. (lieyuan)\n\n    *) Feature: added the '-d' command line option to dump contents of\n       the configuration files. (piaoling)\n\n    *) Feature: added response time statistics to the stub_status module.\n       (jinglong)\n\n    *) Feature: added the 'server_tag' directive. (zhuzhaoyuan)\n\n    *) Feature: now the 'worker_processes' supports the 'auto' parameter, which\n       sets the worker process numbers to the cores automatically. (cfsego)\n\n    *) Feature: now the 'worker_cpu_affinity' directive supports the 'auto'\n       parameter, which binds the worker processes to the cores automatically.\n       (cfsego)\n\n    *) Feature: added the 'ssl_pass_phrase_dialog' directive. (cfsego)\n\n    *) Feature: added the '-s start' command line option. (zhuzhaoyuan)\n\n    *) Feature: added '-m' command line option to list all compiled-in modules.\n       (zhuzhaoyuan)\n\n    *) Feature: added the 'expires_by_types' directive. (lifeibo)\n\n    *) Feature: added the '-l' command line option to list all supported\n       directives. (dinic)\n\n    *) Feature: added the ngx_atoll() API, which can convert a string to a long\n       long integer (64 bits). (lifeibo)\n\n    *) Feature: now status lines (302, 405) are RFC-2616 compatibale. (zhuzhaoyuan)\n\n    *) Feature: now ngx_escape_uri/ngx_unescape_uri supports encoding/decoding\n       style of Java and PHP. (zhuzhaoyuan)\n\n    *) Feature: now configuration files included are sorted. (zhuzhaoyuan)\n\n    *) Feature: now 'error_page' can be reset to 'default' (zhuzhaoyuan)\n\n    *) Change: turned 'msie_padding' off by default. (zhuzhaoyuan)\n\n    *) Bugfix: fixed a bug when subrequest_in_memory and upstream keepalive\n       being used. (lifeibo)\n\n    *) Bugfix: fixed a bug in $sent_http_connection and $sent_http_keep_alive.\n       (zhongsheng)\n\n    *) Bugfix: fixed a bug that error_page directive can't detect duplicate\n       codes and inherited correctly. (zhuzhaoyuan)\n\n    *) Bugfix: fixed a segmentation fault bug in the FastCGI module, while\n       processing duplicated HTTP headers. (monadbobo)\n\n    *) Bugfix: fixed a bug that file in open_file_cache can't be updated.\n       (cfsego)\n\n    *) Bugfix: fixed a bug in 'worker_cpu_affinity'. (cfsego)\n"
  },
  {
    "path": "LICENSE",
    "content": "/* \n * Copyright (C) 2002-2022 Igor Sysoev\n * Copyright (C) 2011,2022 Nginx, Inc.\n * Copyright (C) 2010-2022 Alibaba Group Holding Limited\n * Copyright (C) 2011-2013 Xiaozhe \"chaoslawful\" Wang\n * Copyright (C) 2011-2013 Zhang \"agentzh\" Yichun\n * Copyright (C) 2011-2013 Weibin Yao\n * Copyright (C) 2012-2013 Sogou, Inc.\n * Copyright (C) 2012-2013 NetEase, Inc.\n * Copyright (C) 2014-2017 Intel, Inc.\n * All rights reserved.\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n"
  },
  {
    "path": "README.markdown",
    "content": "<h1 align=\"center\" style=\"border-bottom: none\">\n    <br>Tengine\n</h1>\n\n<p align=\"center\">Visit <a href=\"https://tengine.taobao.org\" target=\"_blank\">tengine.taobao.org</a> for the full documentation,\nexamples and guides.</p>\n\n<div align=\"center\">\n\n[![GitHub license](https://img.shields.io/github/license/alibaba/tengine.svg)](https://github.com/alibaba/tengine/blob/main/LICENSE)\n[![GitHub stars](https://img.shields.io/github/stars/alibaba/tengine.svg)](https://github.com/alibaba/tengine/stargazers)\n[![GitHub stars](https://img.shields.io/badge/contributions-welcome-orange.svg)](https://github.com/alibaba/tengine/blob/main/CONTRIBUTING.md)\n[![Build Status](https://github.com/alibaba/tengine/actions/workflows/ci.yml/badge.svg)](https://github.com/alibaba/tengine/actions/workflows/ci.yml)\n\n</div>\n\n\n## Introduction\nTengine is a web server originated by [Taobao](http://en.wikipedia.org/wiki/Taobao), the largest e-commerce website in Asia. It is based on the [Nginx](http://nginx.org) HTTP server and has many advanced features. Tengine has proven to be very stable and efficient on some of the top 100 websites in the world, including [taobao.com](http://www.taobao.com) and [tmall.com](http://www.tmall.com).\n\nTengine has been an open source project since December 2011. It is being actively developed by the Tengine team, whose core members are from Taobao, Sogou and other Internet companies. Tengine is a community effort and everyone is encouraged to [get involved](https://github.com/alibaba/tengine).\n\n## Features\n* All features of nginx-1.24.0 are inherited, i.e., it is 100% compatible with nginx.\n* Dynamically configure the servers, locations and upstreams without reloading or restarting worker processes with [tengine-ingress](https://github.com/alibaba/tengine-ingress).\n* HTTP/3 support (QUIC v1 and draft-29) with [xquic](https://github.com/alibaba/xquic).\n* High-speed UDP transmission with kernel-bypass.\n* Dynamically configure different TLS protocols for different server names with [tengine-ingress](https://github.com/alibaba/tengine-ingress).\n* Dynamically configure timeout setting, SSL Redirects, CORS and enabling/disabling robots for the server and location with [tengine-ingress](https://github.com/alibaba/tengine-ingress).\n* Dynamically configure HTTP routing based on multiple values of a specific header, cookie or query parameter with [tengine-ingress](https://github.com/alibaba/tengine-ingress).\n* Dynamically configure HTTP routing based on multiple upstream according to weight with [tengine-ingress](https://github.com/alibaba/tengine-ingress).\n* Dynamically configure HTTP routing based on modulo operation for a specific header, cookie or query parameter with [tengine-ingress](https://github.com/alibaba/tengine-ingress).\n* Dynamically configure HTTP routing to add/append custom header or add query parameter in the HTTP request to the upstream with [tengine-ingress](https://github.com/alibaba/tengine-ingress).\n* Dynamically configure HTTP routing to add custom header in the HTTP response to the client with [tengine-ingress](https://github.com/alibaba/tengine-ingress).\n* Support the CONNECT HTTP method for forward proxy.\n* Support asynchronous OpenSSL, using hardware such as QAT for HTTPS acceleration.\n* Enhanced operations monitoring, such as asynchronous log & rollback, DNS caching, memory usage, etc.\n* Support server_name in Stream module.\n* More load balancing methods, e.g., consistent hashing, and session persistence.\n* Input body filter support. It's quite handy to write Web Application Firewalls using this mechanism.\n* Dynamic scripting language (Lua) support, which is very efficient and makes it easy to extend core functionalities.\n* Limits retries for upstream servers (proxy, memcached, fastcgi, scgi, uwsgi).\n* Includes a mechanism to support standalone processes.\n* Protects the server in case system load or memory use goes too high.\n* Multiple CSS or JavaScript requests can be combined into one request to reduce download time.\n* Removes unnecessary white spaces and comments to reduce the size of a page.\n* Proactive health checks of upstream servers can be performed.\n* The number of worker processes and CPU affinities can be set automatically.\n* The limit_req module is enhanced with whitelist support and more conditions are allowed in a single location.\n* Enhanced diagnostic information makes it easier to troubleshoot errors.\n* More user-friendly command lines, e.g., showing all compiled-in modules and supported directives.\n* Expiration times can be specified for certain MIME types.\n* Receives HTTP traffic on the TLS listener with option.\n* Debugging HTTP connection usage.\n* ...\n\n## Installation\nTengine can be downloaded at [http://tengine.taobao.org/download/tengine.tar.gz](http://tengine.taobao.org/download/tengine.tar.gz). You can also checkout the latest source code from GitHub at [https://github.com/alibaba/tengine](https://github.com/alibaba/tengine)\n\nTo install Tengine, just follow these three steps:\n```bash\n./configure\nmake\nsudo make install\n```\n\nBy default, it will be installed to _/usr/local/nginx_. You can use the __'--prefix'__ option to specify the root directory.\nIf you want to know all the _'configure'_ options, you should run __'./configure --help'__ for help.\n\n## Documentation\nThe homepage of Tengine is at [http://tengine.taobao.org/](http://tengine.taobao.org/)\nYou can access [http://tengine.taobao.org/documentation.html](http://tengine.taobao.org/documentation.html) for more information.\n\n## Contact\n[https://github.com/alibaba/tengine/issues](https://github.com/alibaba/tengine/issues)\n\nDingtalk user group: 23394285\n\n## License\n\n[BSD-2-Clause License](https://github.com/alibaba/tengine/blob/master/LICENSE)\n\n<h1 align=\"center\" style=\"border-bottom: none\">\n    <a href=\"https://tengine.taobao.org\" target=\"_blank\"><img alt=\"Tengine\" src=\"/docs/image/tengine-logo.png\"></a>\n</h1>\n"
  },
  {
    "path": "THANKS.te",
    "content": "perusio (Antnio P. P. Almeida)\nliseen.wan\n"
  },
  {
    "path": "auto/cc/acc",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\n# aCC: HP ANSI C++ B3910B A.03.55.02\n\n# C89 mode\n\nCFLAGS=\"$CFLAGS -Ae\"\nCC_TEST_FLAGS=\"-Ae\"\n\nPCRE_OPT=\"$PCRE_OPT -Ae\"\nZLIB_OPT=\"$ZLIB_OPT -Ae\"\n"
  },
  {
    "path": "auto/cc/bcc",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\n# Borland C++ 5.5\n\n# optimizations\n\n# maximize speed\nCFLAGS=\"$CFLAGS -O2\"\n\ncase $CPU in\n    pentium)\n        # optimize for Pentium and Athlon\n        CPU_OPT=\"-5\"\n    ;;\n\n    pentiumpro)\n        # optimize for Pentium Pro, Pentium II and Pentium III\n        CPU_OPT=\"-6\"\n    ;;\nesac\n\n# __stdcall\n#CPU_OPT=\"$CPU_OPT -ps\"\n# __fastcall\n#CPU_OPT=\"$CPU_OPT -pr\"\n\nCFLAGS=\"$CFLAGS $CPU_OPT\"\n\n# multithreaded\nCFLAGS=\"$CFLAGS -tWM\"\n\n# stop on warning\nCFLAGS=\"$CFLAGS -w!\"\n\n# disable logo\nCFLAGS=\"$CFLAGS -q\"\n\n\n# precompiled headers\nCORE_DEPS=\"$CORE_DEPS $NGX_OBJS/ngx_config.csm\"\nNGX_PCH=\"$NGX_OBJS/ngx_config.csm\"\nNGX_BUILD_PCH=\"-H=$NGX_OBJS/ngx_config.csm\"\nNGX_USE_PCH=\"-Hu -H=$NGX_OBJS/ngx_config.csm\"\n\n\n# Win32 GUI mode application\n#LINK=\"\\$(CC) -laa\"\n\n\n# the resource file\nNGX_RES=\"$NGX_OBJS/nginx.res\"\nNGX_RCC=\"brcc32 -fo$NGX_OBJS/nginx.res \\$(CORE_INCS) $NGX_WIN32_RC\"\n# the pragma allows to link the resource file using bcc32 and\n# to avoid the direct ilink32 calling and the c0w32.obj's WinMain/main problem\nNGX_PRAGMA=\"#pragma resource \\\"$NGX_OBJS/nginx.res\\\"\"\n\n\nngx_include_opt=\"-I\"\nngx_objout=\"-o\"\nngx_binout=\"-e\"\nngx_objext=\"obj\"\n\nngx_long_start='@&&|\n\t'\nngx_long_end='|'\n\nngx_regex_dirsep='\\\\'\nngx_dirsep=\"\\\\\"\n"
  },
  {
    "path": "auto/cc/ccc",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\n# Compaq C V6.5-207\n\nngx_include_opt=\"-I\"\n\n# warnings\n\nCFLAGS=\"$CFLAGS -msg_enable level6 -msg_fatal level6\"\n\nCFLAGS=\"$CFLAGS -msg_disable unknownmacro\"\nCFLAGS=\"$CFLAGS -msg_disable unusedincl\"\nCFLAGS=\"$CFLAGS -msg_disable unnecincl\"\nCFLAGS=\"$CFLAGS -msg_disable nestincl\"\nCFLAGS=\"$CFLAGS -msg_disable strctpadding\"\nCFLAGS=\"$CFLAGS -msg_disable ansialiascast\"\nCFLAGS=\"$CFLAGS -msg_disable inlinestoclsmod\"\nCFLAGS=\"$CFLAGS -msg_disable cxxkeyword\"\nCFLAGS=\"$CFLAGS -msg_disable longlongsufx\"\nCFLAGS=\"$CFLAGS -msg_disable valuepres\"\n\n# STUB\nCFLAGS=\"$CFLAGS -msg_disable truncintcast\"\nCFLAGS=\"$CFLAGS -msg_disable trunclongcast\"\n\nCFLAGS=\"$CFLAGS -msg_disable truncintasn\"\nCFLAGS=\"$CFLAGS -msg_disable trunclongint\"\nCFLAGS=\"$CFLAGS -msg_disable intconcastsgn\"\nCFLAGS=\"$CFLAGS -msg_disable intconstsign\"\nCFLAGS=\"$CFLAGS -msg_disable switchlong\"\nCFLAGS=\"$CFLAGS -msg_disable subscrbounds2\"\n\nCFLAGS=\"$CFLAGS -msg_disable hexoctunsign\"\n\nCFLAGS=\"$CFLAGS -msg_disable ignorecallval\"\nCFLAGS=\"$CFLAGS -msg_disable nonstandcast\"\nCFLAGS=\"$CFLAGS -msg_disable embedcomment\"\nCFLAGS=\"$CFLAGS -msg_disable unreachcode\"\nCFLAGS=\"$CFLAGS -msg_disable questcompare2\"\nCFLAGS=\"$CFLAGS -msg_disable unusedtop\"\nCFLAGS=\"$CFLAGS -msg_disable unrefdecl\"\n\nCFLAGS=\"$CFLAGS -msg_disable bitnotint\"\n"
  },
  {
    "path": "auto/cc/clang",
    "content": "\n# Copyright (C) Nginx, Inc.\n\n\n# clang\n\n\nNGX_CLANG_VER=`$CC -v 2>&1 | grep 'version' 2>&1 \\\n                           | sed -n -e 's/^.*clang version \\(.*\\)/\\1/p' \\\n                                    -e 's/^.*LLVM version \\(.*\\)/\\1/p'`\n\necho \" + clang version: $NGX_CLANG_VER\"\n\nhave=NGX_COMPILER value=\"\\\"clang $NGX_CLANG_VER\\\"\" . auto/define\n\n\nCC_TEST_FLAGS=\"-pipe\"\n\n\n# optimizations\n\n#NGX_CLANG_OPT=\"-O2\"\n#NGX_CLANG_OPT=\"-Oz\"\nNGX_CLANG_OPT=\"-O\"\n\ncase $CPU in\n    pentium)\n        # optimize for Pentium\n        CPU_OPT=\"-march=pentium\"\n        NGX_CPU_CACHE_LINE=32\n    ;;\n\n    pentiumpro | pentium3)\n        # optimize for Pentium Pro, Pentium II and Pentium III\n        CPU_OPT=\"-march=pentiumpro\"\n        NGX_CPU_CACHE_LINE=32\n    ;;\n\n    pentium4)\n        # optimize for Pentium 4\n        CPU_OPT=\"-march=pentium4\"\n        NGX_CPU_CACHE_LINE=128\n    ;;\n\n    athlon)\n        # optimize for Athlon\n        CPU_OPT=\"-march=athlon\"\n        NGX_CPU_CACHE_LINE=64\n    ;;\n\n    opteron)\n        # optimize for Opteron\n        CPU_OPT=\"-march=opteron\"\n        NGX_CPU_CACHE_LINE=64\n    ;;\n\nesac\n\nCC_AUX_FLAGS=\"$CC_AUX_FLAGS $CPU_OPT\"\n\n\nCFLAGS=\"$CFLAGS -pipe $CPU_OPT\"\n\nif [ \".$PCRE_OPT\" = \".\" ]; then\n    PCRE_OPT=\"-O2 -pipe $CPU_OPT\"\nelse\n    PCRE_OPT=\"$PCRE_OPT -pipe\"\nfi\n\nif [ \".$ZLIB_OPT\" = \".\" ]; then\n    ZLIB_OPT=\"-O2 -pipe $CPU_OPT\"\nelse\n    ZLIB_OPT=\"$ZLIB_OPT -pipe\"\nfi\n\n\n# warnings\n\nCFLAGS=\"$CFLAGS $NGX_CLANG_OPT -Wall -Wextra -Wpointer-arith\"\nCFLAGS=\"$CFLAGS -Wconditional-uninitialized\"\n#CFLAGS=\"$CFLAGS -Wmissing-prototypes\"\n\n# we have a lot of unused function arguments\nCFLAGS=\"$CFLAGS -Wno-unused-parameter\"\n\n# deprecated system OpenSSL library on OS X\nif [ \"$NGX_SYSTEM\" = \"Darwin\" ]; then\n    CFLAGS=\"$CFLAGS -Wno-deprecated-declarations\"\nfi\n\n# stop on warning\nCFLAGS=\"$CFLAGS -Werror\"\n\n# debug\nCFLAGS=\"$CFLAGS -g\"\n\nif [ \".$CPP\" = \".\" ]; then\n    CPP=\"$CC -E\"\nfi\n"
  },
  {
    "path": "auto/cc/conf",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\nLINK=\"\\$(CC)\"\n\nMAIN_LINK=\nMODULE_LINK=\"-shared\"\n\nngx_include_opt=\"-I \"\nngx_compile_opt=\"-c\"\nngx_pic_opt=\"-fPIC\"\nngx_objout=\"-o \"\nngx_binout=\"-o \"\nngx_objext=\"o\"\nngx_binext=\nngx_modext=\".so\"\n\nngx_long_start=\nngx_long_end=\n\nngx_regex_dirsep=\"\\/\"\nngx_dirsep='/'\n\nngx_regex_cont=' \\\\\\\n\t'\nngx_cont=' \\\n\t'\nngx_tab=' \\\n\t\t'\nngx_spacer=\n\nngx_long_regex_cont=$ngx_regex_cont\nngx_long_cont=$ngx_cont\n\n. auto/cc/name\n\nif test -n \"$CFLAGS\"; then\n\n    CC_TEST_FLAGS=\"$CFLAGS $NGX_CC_OPT\"\n\n    case $NGX_CC_NAME in\n\n        ccc)\n            # Compaq C V6.5-207\n\n            ngx_include_opt=\"-I\"\n        ;;\n\n        sunc)\n\n            MAIN_LINK=\n            MODULE_LINK=\"-G\"\n\n            case \"$NGX_MACHINE\" in\n\n                i86pc)\n                    NGX_AUX=\" src/os/unix/ngx_sunpro_x86.il\"\n                ;;\n\n                sun4u | sun4v)\n                    NGX_AUX=\" src/os/unix/ngx_sunpro_sparc64.il\"\n                ;;\n\n            esac\n\n            case $CPU in\n\n                amd64)\n                    NGX_AUX=\" src/os/unix/ngx_sunpro_amd64.il\"\n                ;;\n\n            esac\n        ;;\n\n    esac\n\nelse\n\n    case $NGX_CC_NAME in\n        gcc)\n            # gcc 2.7.2.3, 2.8.1, 2.95.4, egcs-1.1.2\n            #     3.0.4, 3.1.1, 3.2.3, 3.3.2, 3.3.3, 3.3.4, 3.4.0, 3.4.2\n            #     4.0.0, 4.0.1, 4.1.0\n\n            . auto/cc/gcc\n        ;;\n\n        clang)\n            # Clang C compiler\n\n            . auto/cc/clang\n        ;;\n\n        icc)\n            # Intel C++ compiler 7.1, 8.0, 8.1\n\n            . auto/cc/icc\n        ;;\n\n        sunc)\n            # Sun C 5.7 Patch 117837-04 2005/05/11\n\n            . auto/cc/sunc\n        ;;\n\n        ccc)\n            # Compaq C V6.5-207\n\n            . auto/cc/ccc\n        ;;\n\n        acc)\n            # aCC: HP ANSI C++ B3910B A.03.55.02\n\n            . auto/cc/acc\n        ;;\n\n        msvc)\n            # MSVC++ 6.0 SP2, MSVC++ Toolkit 2003\n\n            . auto/cc/msvc\n        ;;\n\n        owc)\n            # Open Watcom C 1.0, 1.2\n\n            . auto/cc/owc\n        ;;\n\n        bcc)\n            # Borland C++ 5.5\n\n            . auto/cc/bcc\n        ;;\n\n    esac\n\n    CC_TEST_FLAGS=\"$CC_TEST_FLAGS $NGX_CC_OPT\"\n\nfi\n\nCFLAGS=\"$CFLAGS $NGX_CC_OPT\"\nNGX_TEST_LD_OPT=\"$NGX_LD_OPT\"\n\nif [ \"$NGX_PLATFORM\" != win32 ]; then\n\n    if test -n \"$NGX_LD_OPT\"; then\n        ngx_feature=--with-ld-opt=\\\"$NGX_LD_OPT\\\"\n        ngx_feature_name=\n        ngx_feature_run=no\n        ngx_feature_incs=\n        ngx_feature_path=\n        ngx_feature_libs=\n        ngx_feature_test=\n        . auto/feature\n\n        if [ $ngx_found = no ]; then\n            echo $0: error: the invalid value in --with-ld-opt=\\\"$NGX_LD_OPT\\\"\n            echo\n            exit 1\n        fi\n    fi\n\n\n    ngx_feature=\"-Wl,-E switch\"\n    ngx_feature_name=\n    ngx_feature_run=no\n    ngx_feature_incs=\n    ngx_feature_path=\n    ngx_feature_libs=-Wl,-E\n    ngx_feature_test=\n    . auto/feature\n\n    if [ $ngx_found = yes ]; then\n        MAIN_LINK=\"-Wl,-E\"\n    fi\n\n\n    if [ \"$NGX_CC_NAME\" = \"sunc\" ]; then\n        echo \"checking for gcc builtin atomic operations ... disabled\"\n    else\n        ngx_feature=\"gcc builtin atomic operations\"\n        ngx_feature_name=NGX_HAVE_GCC_ATOMIC\n        ngx_feature_run=yes\n        ngx_feature_incs=\n        ngx_feature_path=\n        ngx_feature_libs=\n        ngx_feature_test=\"long  n = 0;\n                          if (!__sync_bool_compare_and_swap(&n, 0, 1))\n                              return 1;\n                          if (__sync_fetch_and_add(&n, 1) != 1)\n                              return 1;\n                          if (n != 2)\n                              return 1;\n                          __sync_synchronize();\"\n        . auto/feature\n    fi\n\n\n    if [ \"$NGX_CC_NAME\" = \"ccc\" ]; then\n        echo \"checking for C99 variadic macros ... disabled\"\n    else\n        ngx_feature=\"C99 variadic macros\"\n        ngx_feature_name=\"NGX_HAVE_C99_VARIADIC_MACROS\"\n        ngx_feature_run=yes\n        ngx_feature_incs=\"#include <stdio.h>\n#define var(dummy, ...)  sprintf(__VA_ARGS__)\"\n        ngx_feature_path=\n        ngx_feature_libs=\n        ngx_feature_test=\"char  buf[30]; buf[0] = '0';\n                          var(0, buf, \\\"%d\\\", 1);\n                          if (buf[0] != '1') return 1\"\n        . auto/feature\n    fi\n\n\n    ngx_feature=\"gcc variadic macros\"\n    ngx_feature_name=\"NGX_HAVE_GCC_VARIADIC_MACROS\"\n    ngx_feature_run=yes\n    ngx_feature_incs=\"#include <stdio.h>\n#define var(dummy, args...)  sprintf(args)\"\n    ngx_feature_path=\n    ngx_feature_libs=\n    ngx_feature_test=\"char  buf[30]; buf[0] = '0';\n                      var(0, buf, \\\"%d\\\", 1);\n                      if (buf[0] != '1') return 1\"\n    . auto/feature\n\n\n    ngx_feature=\"gcc builtin 64 bit byteswap\"\n    ngx_feature_name=\"NGX_HAVE_GCC_BSWAP64\"\n    ngx_feature_run=no\n    ngx_feature_incs=\n    ngx_feature_path=\n    ngx_feature_libs=\n    ngx_feature_test=\"if (__builtin_bswap64(0)) return 1\"\n    . auto/feature\n\n\n#    ngx_feature=\"inline\"\n#    ngx_feature_name=\n#    ngx_feature_run=no\n#    ngx_feature_incs=\"int inline f(void) { return 1 }\"\n#    ngx_feature_path=\n#    ngx_feature_libs=\n#    ngx_feature_test=\n#    . auto/feature\n#\n#    if [ $ngx_found = yes ]; then\n#    fi\n\nfi\n"
  },
  {
    "path": "auto/cc/gcc",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\n# gcc 2.7.2.3, 2.8.1, 2.95.4, egcs-1.1.2\n#     3.0.4, 3.1.1, 3.2.3, 3.3.2, 3.3.3, 3.3.4, 3.4.0, 3.4.2\n#     4.0.0, 4.0.1, 4.1.0\n\n\nNGX_GCC_VER=`$CC -v 2>&1 | grep 'gcc version' 2>&1 \\\n                         | sed -e 's/^.* version \\(.*\\)/\\1/'`\n\necho \" + gcc version: $NGX_GCC_VER\"\n\nhave=NGX_COMPILER value=\"\\\"gcc $NGX_GCC_VER\\\"\" . auto/define\n\n\n# Solaris 7's /usr/ccs/bin/as does not support \"-pipe\"\n\nCC_TEST_FLAGS=\"-pipe\"\n\nngx_feature=\"gcc -pipe switch\"\nngx_feature_name=\nngx_feature_run=no\nngx_feature_incs=\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\n. auto/feature\n\nCC_TEST_FLAGS=\n\nif [ $ngx_found = yes ]; then\n    PIPE=\"-pipe\"\nfi\n\n\ncase \"$NGX_MACHINE\" in\n\n    sun4u | sun4v | sparc | sparc64 )\n        # \"-mcpu=v9\" enables the \"casa\" assembler instruction\n        CFLAGS=\"$CFLAGS -mcpu=v9\"\n    ;;\n\nesac\n\n\n# optimizations\n\n#NGX_GCC_OPT=\"-O2\"\n#NGX_GCC_OPT=\"-Os\"\nNGX_GCC_OPT=\"-O\"\n\n#CFLAGS=\"$CFLAGS -fomit-frame-pointer\"\n\ncase $CPU in\n    pentium)\n        # optimize for Pentium and Athlon\n        CPU_OPT=\"-march=pentium\"\n        NGX_CPU_CACHE_LINE=32\n    ;;\n\n    pentiumpro | pentium3)\n        # optimize for Pentium Pro, Pentium II and Pentium III\n        CPU_OPT=\"-march=pentiumpro\"\n        NGX_CPU_CACHE_LINE=32\n    ;;\n\n    pentium4)\n        # optimize for Pentium 4, gcc 3.x\n        CPU_OPT=\"-march=pentium4\"\n        NGX_CPU_CACHE_LINE=128\n    ;;\n\n    athlon)\n        # optimize for Athlon, gcc 3.x\n        CPU_OPT=\"-march=athlon\"\n        NGX_CPU_CACHE_LINE=64\n    ;;\n\n    opteron)\n        # optimize for Opteron, gcc 3.x\n        CPU_OPT=\"-march=opteron\"\n        NGX_CPU_CACHE_LINE=64\n    ;;\n\n    sparc32)\n        # build 32-bit UltraSparc binary\n        CPU_OPT=\"-m32\"\n        CORE_LINK=\"$CORE_LINK -m32\"\n        NGX_CPU_CACHE_LINE=64\n    ;;\n\n    sparc64)\n        # build 64-bit UltraSparc binary\n        CPU_OPT=\"-m64\"\n        CORE_LINK=\"$CORE_LINK -m64\"\n        NGX_CPU_CACHE_LINE=64\n    ;;\n\n    ppc64)\n        # build 64-bit PowerPC binary\n        CPU_OPT=\"-m64\"\n        CPU_OPT=\"$CPU_OPT -falign-functions=32 -falign-labels=32\"\n        CPU_OPT=\"$CPU_OPT -falign-loops=32 -falign-jumps=32\"\n        CORE_LINK=\"$CORE_LINK -m64\"\n        NGX_CPU_CACHE_LINE=128\n    ;;\n\nesac\n\nCC_AUX_FLAGS=\"$CC_AUX_FLAGS $CPU_OPT\"\n\ncase \"$NGX_GCC_VER\" in\n    2.7*)\n        # batch build\n        CPU_OPT=\n    ;;\nesac\n\n\nCFLAGS=\"$CFLAGS $PIPE $CPU_OPT\"\n\nif [ \".$PCRE_OPT\" = \".\" ]; then\n    PCRE_OPT=\"-O2 -fomit-frame-pointer $PIPE $CPU_OPT\"\nelse\n    PCRE_OPT=\"$PCRE_OPT $PIPE\"\nfi\n\nif [ \".$ZLIB_OPT\" = \".\" ]; then\n    ZLIB_OPT=\"-O2 -fomit-frame-pointer $PIPE $CPU_OPT\"\nelse\n    ZLIB_OPT=\"$ZLIB_OPT $PIPE\"\nfi\n\n\n# warnings\n\n# -W requires at least -O\nCFLAGS=\"$CFLAGS ${NGX_GCC_OPT:--O} -W\"\n\nCFLAGS=\"$CFLAGS -Wall -Wpointer-arith\"\n#CFLAGS=\"$CFLAGS -Wconversion\"\n#CFLAGS=\"$CFLAGS -Winline\"\n#CFLAGS=\"$CFLAGS -Wmissing-prototypes\"\n\ncase \"$NGX_GCC_VER\" in\n    2.*)\n        # we have a lot of the unused function arguments\n        CFLAGS=\"$CFLAGS -Wno-unused\"\n    ;;\n\n    *)\n        # we have a lot of the unused function arguments\n        CFLAGS=\"$CFLAGS -Wno-unused-parameter\"\n        # 4.2.1 shows the warning in wrong places\n        #CFLAGS=\"$CFLAGS -Wunreachable-code\"\n\n        # deprecated system OpenSSL library on OS X\n        if [ \"$NGX_SYSTEM\" = \"Darwin\" ]; then\n            CFLAGS=\"$CFLAGS -Wno-deprecated-declarations\"\n        fi\n    ;;\nesac\n\n\n# stop on warning\nCFLAGS=\"$CFLAGS -Werror\"\n\n# debug\nCFLAGS=\"$CFLAGS -g\"\n\n# DragonFly's gcc3 generates DWARF\n#CFLAGS=\"$CFLAGS -g -gstabs\"\n\nif [ \".$CPP\" = \".\" ]; then\n    CPP=\"$CC -E\"\nfi\n"
  },
  {
    "path": "auto/cc/icc",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\n# Intel C++ compiler 7.1, 8.0, 8.1, 9.0, 11.1\n\nNGX_ICC_VER=`$CC -V 2>&1 | grep 'Version' 2>&1 \\\n                         | sed -e 's/^.* Version \\([^ ]*\\) *Build.*$/\\1/'`\n\necho \" + icc version: $NGX_ICC_VER\"\n\nhave=NGX_COMPILER value=\"\\\"Intel C Compiler $NGX_ICC_VER\\\"\" . auto/define\n\n\n# optimizations\n\nCFLAGS=\"$CFLAGS -O\"\n\nCORE_LINK=\"$CORE_LINK -opt_report_file=$NGX_OBJS/opt_report_file\"\n\n\ncase $CPU in\n    pentium)\n        # optimize for Pentium and Athlon\n        CPU_OPT=\"-march=pentium\"\n    ;;\n\n    pentiumpro)\n        # optimize for Pentium Pro, Pentium II and Pentium III\n        CPU_OPT=\"-mcpu=pentiumpro -march=pentiumpro\"\n    ;;\n\n    pentium4)\n        # optimize for Pentium 4, default\n        CPU_OPT=\"-march=pentium4\"\n    ;;\nesac\n\nCFLAGS=\"$CFLAGS $CPU_OPT\"\n\nif [ \".$PCRE_OPT\" = \".\" ]; then\n    PCRE_OPT=\"-O $CPU_OPT\"\nfi\n\nif [ \".$ZLIB_OPT\" = \".\" ]; then\n    ZLIB_OPT=\"-O $CPU_OPT\"\nfi\n\n\n# warnings\n\nCFLAGS=\"$CFLAGS -w2\"\n\n# disable some warnings\n\n# invalid type conversion: \"int\" to \"char *\"\nCFLAGS=\"$CFLAGS -wd171\"\n# argument is incompatible with corresponding format string conversion\nCFLAGS=\"$CFLAGS -wd181\"\n# zero used for undefined preprocessing identifier\nCFLAGS=\"$CFLAGS -wd193\"\n# the format string ends before this argument\nCFLAGS=\"$CFLAGS -wd268\"\n# invalid format string conversion\nCFLAGS=\"$CFLAGS -wd269\"\n# conversion from \"long long\" to \"size_t\" may lose significant bits\nCFLAGS=\"$CFLAGS -wd810\"\n# parameter was never referenced\nCFLAGS=\"$CFLAGS -wd869\"\n# attribute \"unused\" is only allowed in a function definition, warning on pTHX_\nCFLAGS=\"$CFLAGS -wd1301\"\n\n# STUB\n# enumerated type mixed with another type\nCFLAGS=\"$CFLAGS -wd188\"\n# controlling expression is constant\nCFLAGS=\"$CFLAGS -wd279\"\n# operands are evaluated in unspecified order\nCFLAGS=\"$CFLAGS -wd981\"\n# external definition with no prior declaration\nCFLAGS=\"$CFLAGS -wd1418\"\n# external declaration in primary source file\nCFLAGS=\"$CFLAGS -wd1419\"\n\ncase \"$NGX_ICC_VER\" in\n    9.*)\n        # \"cc\" clobber ignored, warnings for Linux's htonl()/htons()\n        CFLAGS=\"$CFLAGS -wd1469\"\n        # explicit conversion of a 64-bit integral type to a smaller\n        # integral type\n        CFLAGS=\"$CFLAGS -wd1683\"\n        # conversion from pointer to same-sized integral type,\n        # warning on offsetof()\n        CFLAGS=\"$CFLAGS -wd1684\"\n        # floating-point equality and inequality comparisons are unreliable,\n        # warning on SvTRUE()\n        CFLAGS=\"$CFLAGS -wd1572\"\n    ;;\n\n    8.*)\n        # \"cc\" clobber ignored, warnings for Linux's htonl()/htons()\n        CFLAGS=\"$CFLAGS -wd1469\"\n        # floating-point equality and inequality comparisons are unreliable,\n        # warning on SvTRUE()\n        CFLAGS=\"$CFLAGS -wd1572\"\n    ;;\n\n    *)\n    ;;\nesac\n\n# stop on warning\nCFLAGS=\"$CFLAGS -Werror\"\n\n# debug\nCFLAGS=\"$CFLAGS -g\"\n"
  },
  {
    "path": "auto/cc/msvc",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\n# MSVC 6.0 SP2                            cl 12.00\n# MSVC Toolkit 2003 (7.1)                 cl 13.10\n# MSVC 2005 Express Edition SP1 (8.0)     cl 14.00\n# MSVC 2008 Express Edition (9.0)         cl 15.00\n# MSVC 2010 (10.0)                        cl 16.00\n# MSVC 2015 (14.0)                        cl 19.00\n\n\nNGX_MSVC_VER=`$NGX_WINE $CC 2>&1 | grep 'C/C++.* [0-9][0-9]*\\.[0-9]' 2>&1 \\\n                                 | sed -e 's/^.* \\([0-9][0-9]*\\.[0-9].*\\)/\\1/'`\n\necho \" + cl version: $NGX_MSVC_VER\"\n\nhave=NGX_COMPILER value=\"\\\"cl $NGX_MSVC_VER\\\"\" . auto/define\n\n\nngx_msvc_ver=`echo $NGX_MSVC_VER | sed -e 's/^\\([0-9]*\\).*/\\1/'`\n\n\n# detect x64 builds\n\ncase \"$NGX_MSVC_VER\" in\n\n    *x64)\n        NGX_MACHINE=amd64\n    ;;\n\n    *)\n        NGX_MACHINE=i386\n    ;;\n\nesac\n\n\n# optimizations\n\n# maximize speed, equivalent to -Og -Oi -Ot -Oy -Ob2 -Gs -GF -Gy\nCFLAGS=\"$CFLAGS -O2\"\n\n# enable global optimization\n#CFLAGS=\"$CFLAGS -Og\"\n# enable intrinsic functions\n#CFLAGS=\"$CFLAGS -Oi\"\n\n# disable inline expansion\n#CFLAGS=\"$CFLAGS -Ob0\"\n# explicit inline expansion\n#CFLAGS=\"$CFLAGS -Ob1\"\n# explicit and implicit inline expansion\n#CFLAGS=\"$CFLAGS -Ob2\"\n\n# enable frame pointer omission\n#CFLAGS=\"$CFLAGS -Oy\"\n# disable stack checking calls\n#CFLAGS=\"$CFLAGS -Gs\"\n\n# pools strings as read/write\n#CFLAGS=\"$CFLAGS -Gf\"\n# pools strings as read-only\n#CFLAGS=\"$CFLAGS -GF\"\n\n\ncase $CPU in\n    pentium)\n        # optimize for Pentium and Athlon\n        CPU_OPT=\"-G5\"\n    ;;\n\n    pentiumpro)\n        # optimize for Pentium Pro, Pentium II and Pentium III\n        CPU_OPT=\"-G6\"\n    ;;\n\n    pentium4)\n        # optimize for Pentium 4, MSVC 7\n        CPU_OPT=\"-G7\"\n    ;;\nesac\n\n# __cdecl, default, must be used with OpenSSL, md5 asm, and sha1 asm\n#CPU_OPT=\"$CPU_OPT -Gd\"\n# __stdcall\n#CPU_OPT=\"$CPU_OPT -Gz\"\n# __fastcall\n#CPU_OPT=\"$CPU_OPT -Gr\"\n\n\nCFLAGS=\"$CFLAGS $CPU_OPT\"\n\n\n# warnings\n\nCFLAGS=\"$CFLAGS -W4\"\n\n# stop on warning\nCFLAGS=\"$CFLAGS -WX\"\n\n# disable logo\nCFLAGS=\"$CFLAGS -nologo\"\n\n# the link flags\nCORE_LINK=\"$CORE_LINK -link -verbose:lib\"\n\n# link with libcmt.lib, multithreaded\nLIBC=\"-MT\"\n# link with msvcrt.dll\n# however, MSVC Toolkit 2003 has no MSVCRT.LIB\n#LIBC=\"-MD\"\n\nCFLAGS=\"$CFLAGS $LIBC\"\n\nCORE_LIBS=\"$CORE_LIBS kernel32.lib user32.lib\"\n\n# Win32 GUI mode application\n#CORE_LINK=\"$CORE_LINK -subsystem:windows -entry:mainCRTStartup\"\n\n# debug\n# msvc under Wine issues\n# C1902: Program database manager mismatch; please check your installation\nif [ -z \"$NGX_WINE\" ]; then\n   CFLAGS=\"$CFLAGS -Zi -Fd$NGX_OBJS/nginx.pdb\"\n   CORE_LINK=\"$CORE_LINK -debug\"\nfi\n\n\n# MSVC 2005 supports C99 variadic macros\nif [ \"$ngx_msvc_ver\" -ge 14 ]; then\n    have=NGX_HAVE_C99_VARIADIC_MACROS . auto/have\nfi\n\n\n# precompiled headers\nCORE_DEPS=\"$CORE_DEPS $NGX_OBJS/ngx_config.pch\"\nCORE_LINK=\"$CORE_LINK $NGX_OBJS/ngx_pch.obj\"\nNGX_PCH=\"$NGX_OBJS/ngx_config.pch\"\nNGX_BUILD_PCH=\"-Ycngx_config.h -Fp$NGX_OBJS/ngx_config.pch\"\nNGX_USE_PCH=\"-Yungx_config.h -Fp$NGX_OBJS/ngx_config.pch\"\n\n\n# the resource file\nNGX_RES=\"$NGX_OBJS/nginx.res\"\nNGX_RCC=\"rc -fo$NGX_RES \\$(CORE_INCS) $NGX_WIN32_RC\"\nCORE_LINK=\"$NGX_RES $CORE_LINK\"\n\n\n# dynamic modules\n#MAIN_LINK=\"-link -def:$NGX_OBJS/nginx.def\"\n#MODULE_LINK=\"-LD $NGX_OBJS/nginx.lib\"\n\n\nngx_pic_opt=\nngx_objout=\"-Fo\"\nngx_binout=\"-Fe\"\nngx_objext=\"obj\"\n\nngx_long_start='@<<\n\t'\nngx_long_end='<<'\nngx_long_regex_cont=' \\\n\t'\nngx_long_cont='\n\t'\n\n# MSVC understand / in path\n#ngx_regex_dirsep='\\\\'\n#ngx_dirsep=\"\\\\\"\n"
  },
  {
    "path": "auto/cc/name",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\nif [ \"$NGX_PLATFORM\" != win32 ]; then\n\n    ngx_feature=\"C compiler\"\n    ngx_feature_name=\n    ngx_feature_run=yes\n    ngx_feature_incs=\n    ngx_feature_path=\n    ngx_feature_libs=\n    ngx_feature_test=\n    . auto/feature\n\n    if [ $ngx_found = no ]; then\n        echo\n        echo $0: error: C compiler $CC is not found\n        echo\n        exit 1\n    fi\n\nfi\n\n\nif [ \"$CC\" = cl ]; then\n    NGX_CC_NAME=msvc\n    echo \" + using Microsoft Visual C++ compiler\"\n\nelif [ \"$CC\" = wcl386 ]; then\n    NGX_CC_NAME=owc\n    echo \" + using Open Watcom C compiler\"\n\nelif [ \"$CC\" = bcc32 ]; then\n    NGX_CC_NAME=bcc\n    echo \" + using Borland C++ compiler\"\n\nelif `$CC -V 2>&1 | grep '^Intel(R) C' >/dev/null 2>&1`; then\n    NGX_CC_NAME=icc\n    echo \" + using Intel C++ compiler\"\n\nelif `$CC -v 2>&1 | grep 'gcc version' >/dev/null 2>&1`; then\n    NGX_CC_NAME=gcc\n    echo \" + using GNU C compiler\"\n\nelif `$CC -v 2>&1 | grep 'clang version' >/dev/null 2>&1`; then\n    NGX_CC_NAME=clang\n    echo \" + using Clang C compiler\"\n\nelif `$CC -v 2>&1 | grep 'LLVM version' >/dev/null 2>&1`; then\n    NGX_CC_NAME=clang\n    echo \" + using Clang C compiler\"\n\nelif `$CC -V 2>&1 | grep 'Sun C' >/dev/null 2>&1`; then\n    NGX_CC_NAME=sunc\n    echo \" + using Sun C compiler\"\n\nelif `$CC -V 2>&1 | grep '^Compaq C' >/dev/null 2>&1`; then\n    NGX_CC_NAME=ccc\n    echo \" + using Compaq C compiler\"\n\nelif `$CC -V 2>&1 | grep '^aCC: ' >/dev/null 2>&1`; then\n    NGX_CC_NAME=acc\n    echo \" + using HP aC++ compiler\"\n\nelse\n    NGX_CC_NAME=unknown\n\nfi\n"
  },
  {
    "path": "auto/cc/owc",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\n# Open Watcom C 1.0, 1.2, 1.3\n\n# optimizations\n\n# maximize speed\nCFLAGS=\"$CFLAGS -ot\"\n# reorder instructions for best pipeline usage\nCFLAGS=\"$CFLAGS -op\"\n# inline intrinsic functions\nCFLAGS=\"$CFLAGS -oi\"\n# inline expansion\nCFLAGS=\"$CFLAGS -oe\"\n# disable stack checking calls\nCFLAGS=\"$CFLAGS -s\"\n\ncase $CPU in\n    pentium)\n        # optimize for Pentium and Athlon\n        # register-based arguments passing conventions\n        CPU_OPT=\"-5r\"\n        # stack-based arguments passing conventions\n        #CPU_OPT=\"-5s\"\n    ;;\n\n    pentiumpro)\n        # optimize for Pentium Pro, Pentium II and Pentium III\n        # register-based arguments passing conventions\n        CPU_OPT=\"-6r\"\n        # stack-based arguments passing conventions\n        #CPU_OPT=\"-6s\"\n    ;;\nesac\n\nCFLAGS=\"$CFLAGS $CPU_OPT\"\n\n\n# warnings\n\n# maximum level\nCFLAGS=\"$CFLAGS -wx\"\n#CFLAGS=\"$CFLAGS -w3\"\n\n# stop on warning\nCFLAGS=\"$CFLAGS -we\"\n\n# built target is NT\nCFLAGS=\"$CFLAGS -bt=nt\"\n\n# multithreaded\nCFLAGS=\"$CFLAGS -bm\"\n\n# debug\nCFLAGS=\"$CFLAGS -d2\"\n\n# quiet\nCFLAGS=\"$CFLAGS -zq\"\n\n# Open Watcom C 1.2\nhave=NGX_HAVE_C99_VARIADIC_MACROS . auto/have\n\n\n# the precompiled headers\n#CORE_DEPS=\"$CORE_DEPS $NGX_OBJS/ngx_config.pch\"\n#NGX_PCH=\"$NGX_OBJS/ngx_config.pch\"\n#NGX_BUILD_PCH=\"-fhq=$NGX_OBJS/ngx_config.pch\"\n#NGX_USE_PCH=\"-fh=$NGX_OBJS/ngx_config.pch\"\n\n\n# the link flags, built target is NT GUI mode application\n#CORE_LINK=\"$CORE_LINK -l=nt_win\"\n\n\n# the resource file\nNGX_RCC=\"wrc \\$(CORE_INCS) -fo=$NGX_OBJS/nginx.res \"\nNGX_RCC=\"$NGX_RCC $NGX_WIN32_RC $NGX_OBJS/nginx.exe\"\n\n\nngx_include_opt=\"-i=\"\nngx_objout=\"-fo\"\nngx_binout=\"-fe=\"\nngx_objext=\"obj\"\n\nngx_regex_dirsep='\\\\'\nngx_dirsep=\"\\\\\"\n\nngx_long_start=' '\nngx_long_end=' '\nngx_long_regex_cont=' \\&\\\n\t'\nngx_long_cont=' &\n\t'\n\nngx_regex_cont=' \\&\\\n\t'\nngx_cont=' &\n\t'\nngx_tab=' &\n\t\t'\n"
  },
  {
    "path": "auto/cc/sunc",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\n# Sun C 5.7 Patch 117837-04 2005/05/11    Sun Studio 10\n# Sun C 5.8 2005/10/13                    Sun Studio 11\n# Sun C 5.9 SunOS_i386 2007/05/03         Sun Studio 12\n# Sun C 5.9 SunOS_sparc 2007/05/03\n# Sun C 5.10 SunOS_i386 2009/06/03        Sun Studio 12.1\n# Sun C 5.11 SunOS_i386 2010/08/13        Oracle Solaris Studio 12.2\n# Sun C 5.12 SunOS_i386 2011/11/16        Oracle Solaris Studio 12.3\n# Sun C 5.13 SunOS_i386 2014/10/20        Oracle Solaris Studio 12.4\n# Sun C 5.14 SunOS_i386 2016/05/31        Oracle Developer Studio 12.5\n\nNGX_SUNC_VER=`$CC -V 2>&1 | grep 'Sun C' 2>&1 \\\n                          | sed -e 's/^.* Sun C \\(.*\\)/\\1/'`\n\necho \" + Sun C version: $NGX_SUNC_VER\"\n\nhave=NGX_COMPILER value=\"\\\"Sun C $NGX_SUNC_VER\\\"\" . auto/define\n\n\ncat << END > $NGX_AUTOTEST.c\n\nint main(void) {\n    printf(\"%d\", __SUNPRO_C);\n    return 0;\n}\n\nEND\n\neval \"$CC -o $NGX_AUTOTEST $NGX_AUTOTEST.c >> $NGX_ERR 2>&1\"\n\nif [ -x $NGX_AUTOTEST ]; then\n    ngx_sunc_ver=`$NGX_AUTOTEST`\nfi\n\nrm -rf $NGX_AUTOTEST*\n\n# 1424 == 0x590, Sun Studio 12\n\nif [ \"$ngx_sunc_ver\" -ge 1424 ]; then\n    ngx_sparc32=\"-m32\"\n    ngx_sparc64=\"-m64\"\n    ngx_amd64=\"-m64\"\n\nelse\n    ngx_sparc32=\"-xarch=v8plus\"\n    ngx_sparc64=\"-xarch=v9\"\n    ngx_amd64=\"-xarch=amd64\"\nfi\n\ncase \"$NGX_MACHINE\" in\n\n    i86pc)\n        NGX_AUX=\" src/os/unix/ngx_sunpro_x86.il\"\n    ;;\n\n    sun4u | sun4v)\n        NGX_AUX=\" src/os/unix/ngx_sunpro_sparc64.il\"\n    ;;\n\nesac\n\nMAIN_LINK=\nMODULE_LINK=\"-G\"\n\n\n# optimizations\n\n# 20736 == 0x5100, Sun Studio 12.1\n\nif [ \"$ngx_sunc_ver\" -ge 20736 ]; then\n    ngx_fast=\"-fast\"\n\nelse\n    # older versions had problems with bit-fields\n    ngx_fast=\"-fast -xalias_level=any\"\nfi\n\nIPO=-xipo\nCFLAGS=\"$CFLAGS $ngx_fast $IPO\"\nCORE_LINK=\"$CORE_LINK $ngx_fast $IPO\"\n\n\ncase $CPU in\n    pentium)\n        # optimize for Pentium and Athlon\n        CPU_OPT=\"-xchip=pentium\"\n    ;;\n\n    pentiumpro)\n        # optimize for Pentium Pro, Pentium II\n        CPU_OPT=\"-xchip=pentium_pro\"\n    ;;\n\n    pentium3)\n        # optimize for Pentium III\n        CPU_OPT=\"-xchip=pentium3\"\n        #CPU_OPT=\"$CPU_OPT -xarch=sse\"\n        CPU_OPT=\"$CPU_OPT -xcache=16/32/4:256/32/4\"\n    ;;\n\n    pentium4)\n        # optimize for Pentium 4\n        CPU_OPT=\"-xchip=pentium4\"\n        #CPU_OPT=\"$CPU_OPT -xarch=sse2\"\n        CPU_OPT=\"$CPU_OPT -xcache=8/64/4:256/128/8\"\n    ;;\n\n    opteron)\n        # optimize for Opteron\n        CPU_OPT=\"-xchip=opteron\"\n        #CPU_OPT=\"$CPU_OPT -xarch=sse2\"\n        CPU_OPT=\"$CPU_OPT -xcache=64/64/2:1024/64/16\"\n    ;;\n\n    sparc32)\n        # build 32-bit UltraSparc binary\n        CPU_OPT=\"$ngx_sparc32\"\n        CORE_LINK=\"$CORE_LINK $ngx_sparc32\"\n        CC_AUX_FLAGS=\"$CC_AUX_FLAGS $ngx_sparc32\"\n        NGX_CPU_CACHE_LINE=64\n    ;;\n\n    sparc64)\n        # build 64-bit UltraSparc binary\n        CPU_OPT=\"$ngx_sparc64\"\n        CORE_LINK=\"$CORE_LINK $ngx_sparc64\"\n        CC_AUX_FLAGS=\"$CC_AUX_FLAGS $ngx_sparc64\"\n        NGX_CPU_CACHE_LINE=64\n    ;;\n\n    amd64)\n        # build 64-bit amd64 binary\n        CPU_OPT=\"$ngx_amd64\"\n        CORE_LINK=\"$CORE_LINK $ngx_amd64\"\n        CC_AUX_FLAGS=\"$CC_AUX_FLAGS $ngx_amd64\"\n        NGX_AUX=\" src/os/unix/ngx_sunpro_amd64.il\"\n        NGX_CPU_CACHE_LINE=64\n    ;;\n\nesac\n\n\nCFLAGS=\"$CFLAGS $CPU_OPT\"\n\n\nif [ \".$PCRE_OPT\" = \".\" ]; then\n    PCRE_OPT=\"$ngx_fast $IPO $CPU_OPT\"\nfi\n\nif [ \".$ZLIB_OPT\" = \".\" ]; then\n    ZLIB_OPT=\"$ngx_fast $IPO $CPU_OPT\"\nfi\n\n\n# stop on warning\nCFLAGS=\"$CFLAGS -errwarn=%all\"\n\n# debug\nCFLAGS=\"$CFLAGS -g\"\n"
  },
  {
    "path": "auto/configure",
    "content": "#!/bin/sh\n\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\nLC_ALL=C\nexport LC_ALL\n\n. auto/options\n. auto/init\n. auto/sources\n\ntest -d $NGX_OBJS || mkdir -p $NGX_OBJS\n\necho > $NGX_AUTO_HEADERS_H\necho > $NGX_AUTOCONF_ERR\n\necho \"#define NGX_CONFIGURE \\\"$NGX_CONFIGURE\\\"\" > $NGX_AUTO_CONFIG_H\n\n\nif [ $NGX_DEBUG = YES ]; then\n    have=NGX_DEBUG . auto/have\nfi\n\n\nif test -z \"$NGX_PLATFORM\"; then\n    echo \"checking for OS\"\n\n    NGX_SYSTEM=`uname -s 2>/dev/null`\n    NGX_RELEASE=`uname -r 2>/dev/null`\n    NGX_MACHINE=`uname -m 2>/dev/null`\n\n    echo \" + $NGX_SYSTEM $NGX_RELEASE $NGX_MACHINE\"\n\n    NGX_PLATFORM=\"$NGX_SYSTEM:$NGX_RELEASE:$NGX_MACHINE\";\n\n    case \"$NGX_SYSTEM\" in\n        MINGW32_* | MINGW64_* | MSYS_*)\n            NGX_PLATFORM=win32\n        ;;\n    esac\n\nelse\n    echo \"building for $NGX_PLATFORM\"\n    NGX_SYSTEM=$NGX_PLATFORM\n    NGX_MACHINE=i386\nfi\n\n. auto/cc/conf\n\nif [ \"$NGX_PLATFORM\" != win32 ]; then\n    . auto/headers\nfi\n\n. auto/os/conf\n\nif [ \"$NGX_PLATFORM\" != win32 ]; then\n    . auto/unix\nfi\n\n. auto/threads\n. auto/modules\n. auto/lib/conf\n\ncase \".$NGX_PREFIX\" in\n    .)\n        NGX_PREFIX=${NGX_PREFIX:-/usr/local/nginx}\n        have=NGX_PREFIX value=\"\\\"$NGX_PREFIX/\\\"\" . auto/define\n    ;;\n\n    .!)\n        NGX_PREFIX=\n    ;;\n\n    *)\n        have=NGX_PREFIX value=\"\\\"$NGX_PREFIX/\\\"\" . auto/define\n    ;;\nesac\n\nif [ \".$NGX_CONF_PREFIX\" != \".\" ]; then\n    have=NGX_CONF_PREFIX value=\"\\\"$NGX_CONF_PREFIX/\\\"\" . auto/define\nfi\n\nhave=NGX_SBIN_PATH value=\"\\\"$NGX_SBIN_PATH\\\"\" . auto/define\nhave=NGX_CONF_PATH value=\"\\\"$NGX_CONF_PATH\\\"\" . auto/define\nhave=NGX_PID_PATH value=\"\\\"$NGX_PID_PATH\\\"\" . auto/define\nhave=NGX_LOCK_PATH value=\"\\\"$NGX_LOCK_PATH\\\"\" . auto/define\nhave=NGX_ERROR_LOG_PATH value=\"\\\"$NGX_ERROR_LOG_PATH\\\"\" . auto/define\n\nif [ \".$NGX_ERROR_LOG_PATH\" = \".\" ]; then\n    have=NGX_ERROR_LOG_STDERR . auto/have\nfi\n\nhave=NGX_HTTP_LOG_PATH value=\"\\\"$NGX_HTTP_LOG_PATH\\\"\" . auto/define\nhave=NGX_HTTP_CLIENT_TEMP_PATH value=\"\\\"$NGX_HTTP_CLIENT_TEMP_PATH\\\"\"\n. auto/define\nhave=NGX_HTTP_PROXY_TEMP_PATH value=\"\\\"$NGX_HTTP_PROXY_TEMP_PATH\\\"\"\n. auto/define\nhave=NGX_HTTP_FASTCGI_TEMP_PATH value=\"\\\"$NGX_HTTP_FASTCGI_TEMP_PATH\\\"\"\n. auto/define\nhave=NGX_HTTP_UWSGI_TEMP_PATH value=\"\\\"$NGX_HTTP_UWSGI_TEMP_PATH\\\"\"\n. auto/define\nhave=NGX_HTTP_SCGI_TEMP_PATH value=\"\\\"$NGX_HTTP_SCGI_TEMP_PATH\\\"\"\n. auto/define\n\n. auto/make\n. auto/lib/make\n. auto/install\n\n# STUB\n. auto/stubs\n\nhave=NGX_USER value=\"\\\"$NGX_USER\\\"\" . auto/define\nhave=NGX_GROUP value=\"\\\"$NGX_GROUP\\\"\" . auto/define\n\nif [ \".$NGX_BUILD\" != \".\" ]; then\n    have=NGX_BUILD value=\"\\\"$NGX_BUILD\\\"\" . auto/define\nfi\n\n. auto/summary\n"
  },
  {
    "path": "auto/define",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\ncat << END >> $NGX_AUTO_CONFIG_H\n\n#ifndef $have\n#define $have  $value\n#endif\n\nEND\n"
  },
  {
    "path": "auto/endianness",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\necho $ngx_n \"checking for system byte ordering ...$ngx_c\"\n\ncat << END >> $NGX_AUTOCONF_ERR\n\n----------------------------------------\nchecking for system byte ordering\n\nEND\n\n\ncat << END > $NGX_AUTOTEST.c\n\nint main(void) {\n    int i = 0x11223344;\n    char *p;\n\n    p = (char *) &i;\n    if (*p == 0x44) return 0;\n    return 1;\n}\n\nEND\n\nngx_test=\"$CC $CC_TEST_FLAGS $CC_AUX_FLAGS \\\n          -o $NGX_AUTOTEST $NGX_AUTOTEST.c $NGX_LD_OPT $ngx_feature_libs\"\n\neval \"$ngx_test >> $NGX_AUTOCONF_ERR 2>&1\"\n\nif [ -x $NGX_AUTOTEST ]; then\n    if $NGX_AUTOTEST >/dev/null 2>&1; then\n        echo \" little endian\"\n        have=NGX_HAVE_LITTLE_ENDIAN . auto/have\n    else\n        echo \" big endian\"\n    fi\n\n    rm -rf $NGX_AUTOTEST*\n\nelse\n    rm -rf $NGX_AUTOTEST*\n\n    echo\n    echo \"$0: error: cannot detect system byte ordering\"\n    exit 1\nfi\n"
  },
  {
    "path": "auto/feature",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\necho $ngx_n \"checking for $ngx_feature ...$ngx_c\"\n\ncat << END >> $NGX_AUTOCONF_ERR\n\n----------------------------------------\nchecking for $ngx_feature\n\nEND\n\nngx_found=no\n\nif test -n \"$ngx_feature_name\"; then\n    ngx_have_feature=`echo $ngx_feature_name \\\n                   | tr abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ`\nfi\n\nif test -n \"$ngx_feature_path\"; then\n    for ngx_temp in $ngx_feature_path; do\n        ngx_feature_inc_path=\"$ngx_feature_inc_path -I $ngx_temp\"\n    done\nfi\n\ncat << END > $NGX_AUTOTEST.c\n\n#include <sys/types.h>\n$NGX_INCLUDE_UNISTD_H\n$ngx_feature_incs\n\nint main(void) {\n    $ngx_feature_test;\n    return 0;\n}\n\nEND\n\n\nngx_test=\"$CC $CC_TEST_FLAGS $CC_AUX_FLAGS $ngx_feature_inc_path \\\n          -o $NGX_AUTOTEST $NGX_AUTOTEST.c $NGX_TEST_LD_OPT $ngx_feature_libs\"\n\nngx_feature_inc_path=\n\neval \"/bin/sh -c \\\"$ngx_test\\\" >> $NGX_AUTOCONF_ERR 2>&1\"\n\n\nif [ -x $NGX_AUTOTEST ]; then\n\n    case \"$ngx_feature_run\" in\n\n        yes)\n            # /bin/sh is used to intercept \"Killed\" or \"Abort trap\" messages\n            if /bin/sh -c $NGX_AUTOTEST >> $NGX_AUTOCONF_ERR 2>&1; then\n                echo \" found\"\n                ngx_found=yes\n\n                if test -n \"$ngx_feature_name\"; then\n                    have=$ngx_have_feature . auto/have\n                fi\n\n            else\n                echo \" found but is not working\"\n            fi\n        ;;\n\n        value)\n            # /bin/sh is used to intercept \"Killed\" or \"Abort trap\" messages\n            if /bin/sh -c $NGX_AUTOTEST >> $NGX_AUTOCONF_ERR 2>&1; then\n                echo \" found\"\n                ngx_found=yes\n\n                cat << END >> $NGX_AUTO_CONFIG_H\n\n#ifndef $ngx_feature_name\n#define $ngx_feature_name  `$NGX_AUTOTEST`\n#endif\n\nEND\n            else\n                echo \" found but is not working\"\n            fi\n        ;;\n\n        bug)\n            # /bin/sh is used to intercept \"Killed\" or \"Abort trap\" messages\n            if /bin/sh -c $NGX_AUTOTEST >> $NGX_AUTOCONF_ERR 2>&1; then\n                echo \" not found\"\n\n            else\n                echo \" found\"\n                ngx_found=yes\n\n                if test -n \"$ngx_feature_name\"; then\n                    have=$ngx_have_feature . auto/have\n                fi\n            fi\n        ;;\n\n        *)\n            echo \" found\"\n            ngx_found=yes\n\n            if test -n \"$ngx_feature_name\"; then\n                have=$ngx_have_feature . auto/have\n            fi\n        ;;\n\n    esac\n\nelse\n    echo \" not found\"\n\n    echo \"----------\"    >> $NGX_AUTOCONF_ERR\n    cat $NGX_AUTOTEST.c  >> $NGX_AUTOCONF_ERR\n    echo \"----------\"    >> $NGX_AUTOCONF_ERR\n    echo $ngx_test       >> $NGX_AUTOCONF_ERR\n    echo \"----------\"    >> $NGX_AUTOCONF_ERR\nfi\n\nrm -rf $NGX_AUTOTEST*\n"
  },
  {
    "path": "auto/have",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\ncat << END >> $NGX_AUTO_CONFIG_H\n\n#ifndef $have\n#define $have  1\n#endif\n\nEND\n"
  },
  {
    "path": "auto/have_headers",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\ncat << END >> $NGX_AUTO_HEADERS_H\n\n#ifndef $have\n#define $have  1\n#endif\n\nEND\n"
  },
  {
    "path": "auto/headers",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\nngx_include=\"unistd.h\";      . auto/include\nngx_include=\"inttypes.h\";    . auto/include\nngx_include=\"limits.h\";      . auto/include\nngx_include=\"sys/filio.h\";   . auto/include\nngx_include=\"sys/param.h\";   . auto/include\nngx_include=\"sys/mount.h\";   . auto/include\nngx_include=\"sys/statvfs.h\"; . auto/include\nngx_include=\"crypt.h\";       . auto/include\n"
  },
  {
    "path": "auto/include",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\necho $ngx_n \"checking for $ngx_include ...$ngx_c\"\n\ncat << END >> $NGX_AUTOCONF_ERR\n\n----------------------------------------\nchecking for $ngx_include\n\nEND\n\n\nngx_found=no\n\ncat << END > $NGX_AUTOTEST.c\n\n$NGX_INCLUDE_SYS_PARAM_H\n#include <$ngx_include>\n\nint main(void) {\n    return 0;\n}\n\nEND\n\n\nngx_test=\"$CC -o $NGX_AUTOTEST $NGX_AUTOTEST.c\"\n\neval \"$ngx_test >> $NGX_AUTOCONF_ERR 2>&1\"\n\nif [ -x $NGX_AUTOTEST ]; then\n\n    ngx_found=yes\n\n    echo \" found\"\n\n    ngx_name=`echo $ngx_include \\\n              | tr abcdefghijklmnopqrstuvwxyz/. ABCDEFGHIJKLMNOPQRSTUVWXYZ__`\n\n\n    have=NGX_HAVE_$ngx_name . auto/have_headers\n\n    eval \"NGX_INCLUDE_$ngx_name='#include <$ngx_include>'\"\n\nelse\n    echo \" not found\"\n\n    echo \"----------\"    >> $NGX_AUTOCONF_ERR\n    cat $NGX_AUTOTEST.c  >> $NGX_AUTOCONF_ERR\n    echo \"----------\"    >> $NGX_AUTOCONF_ERR\n    echo $ngx_test       >> $NGX_AUTOCONF_ERR\n    echo \"----------\"    >> $NGX_AUTOCONF_ERR\nfi\n\nrm -rf $NGX_AUTOTEST*\n"
  },
  {
    "path": "auto/init",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\nNGX_MAKEFILE=$NGX_OBJS/Makefile\nNGX_MODULES_C=$NGX_OBJS/ngx_modules.c\n\nNGX_AUTO_HEADERS_H=$NGX_OBJS/ngx_auto_headers.h\nNGX_AUTO_CONFIG_H=$NGX_OBJS/ngx_auto_config.h\n\nNGX_AUTOTEST=$NGX_OBJS/autotest\nNGX_AUTOCONF_ERR=$NGX_OBJS/autoconf.err\n\n# STUBs\nNGX_ERR=$NGX_OBJS/autoconf.err\nMAKEFILE=$NGX_OBJS/Makefile\n\n\nNGX_PCH=\nNGX_USE_PCH=\n\n\n# check the echo's \"-n\" option and \"\\c\" capability\n\nif echo \"test\\c\" | grep c >/dev/null; then\n\n    if echo -n test | grep n >/dev/null; then\n        ngx_n=\n        ngx_c=\n\n    else\n        ngx_n=-n\n        ngx_c=\n    fi\n\nelse\n    ngx_n=\n    ngx_c='\\c'\nfi\n\n\n# create Makefile\n\ncat << END > Makefile\n\ndefault:\tbuild\n\nclean:\n\trm -rf Makefile $NGX_OBJS\n\n.PHONY:\tdefault clean\nEND\n"
  },
  {
    "path": "auto/install",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\nif [ $USE_PERL != NO ]; then\n\n    cat << END                                                >> $NGX_MAKEFILE\n\ninstall_perl_modules:\n\tcd $NGX_OBJS/src/http/modules/perl && \\$(MAKE) install\nEND\n\n    NGX_INSTALL_PERL_MODULES=install_perl_modules\n\nfi\n\n\ncase \".$NGX_SBIN_PATH\" in\n    ./*)\n    ;;\n\n    *)\n        NGX_SBIN_PATH=$NGX_PREFIX/$NGX_SBIN_PATH\n    ;;\nesac\n\n\ncase \".$NGX_MODULES_PATH\" in\n    ./*)\n    ;;\n\n    *)\n        NGX_MODULES_PATH=$NGX_PREFIX/$NGX_MODULES_PATH\n    ;;\nesac\n\nNGX_MODULES_PATH=`dirname $NGX_MODULES_PATH/.`\n\n\ncase \".$NGX_CONF_PATH\" in\n    ./*)\n    ;;\n\n    *)\n        NGX_CONF_PATH=$NGX_PREFIX/$NGX_CONF_PATH\n    ;;\nesac\n\n\nNGX_CONF_PREFIX=`dirname $NGX_CONF_PATH`\n\n\ncase \".$NGX_PID_PATH\" in\n    ./*)\n    ;;\n\n    *)\n        NGX_PID_PATH=$NGX_PREFIX/$NGX_PID_PATH\n    ;;\nesac\n\n\ncase \".$NGX_ERROR_LOG_PATH\" in\n    ./* | .)\n    ;;\n\n    *)\n        NGX_ERROR_LOG_PATH=$NGX_PREFIX/$NGX_ERROR_LOG_PATH\n    ;;\nesac\n\n\ncase \".$NGX_HTTP_LOG_PATH\" in\n    ./*)\n    ;;\n\n    *)\n        NGX_HTTP_LOG_PATH=$NGX_PREFIX/$NGX_HTTP_LOG_PATH\n    ;;\nesac\n\n\nif test -f man/nginx.8 ; then\n    NGX_MAN=man/nginx.8\nelse\n    NGX_MAN=docs/man/nginx.8\nfi\n\nif test -d html ; then\n    NGX_HTML=html\nelse\n    NGX_HTML=docs/html\nfi\n\ncat << END                                                    >> $NGX_MAKEFILE\n\nmanpage:\t$NGX_OBJS/nginx.8\n\n$NGX_OBJS/nginx.8:\t$NGX_MAN $NGX_AUTO_CONFIG_H\n\tsed -e \"s|%%PREFIX%%|$NGX_PREFIX|\" \\\\\n\t\t-e \"s|%%PID_PATH%%|$NGX_PID_PATH|\" \\\\\n\t\t-e \"s|%%CONF_PATH%%|$NGX_CONF_PATH|\" \\\\\n\t\t-e \"s|%%ERROR_LOG_PATH%%|${NGX_ERROR_LOG_PATH:-stderr}|\" \\\\\n\t\t< $NGX_MAN > \\$@\n\ninstall:\tbuild $NGX_INSTALL_PERL_MODULES\n\ttest -d '\\$(DESTDIR)$NGX_PREFIX' || mkdir -p '\\$(DESTDIR)$NGX_PREFIX'\n\n\ttest -d '\\$(DESTDIR)`dirname \"$NGX_SBIN_PATH\"`' \\\\\n\t\t|| mkdir -p '\\$(DESTDIR)`dirname \"$NGX_SBIN_PATH\"`'\n\ttest ! -f '\\$(DESTDIR)$NGX_SBIN_PATH' \\\\\n\t\t|| mv '\\$(DESTDIR)$NGX_SBIN_PATH' \\\\\n\t\t\t'\\$(DESTDIR)$NGX_SBIN_PATH.old'\n\tcp $NGX_OBJS/nginx '\\$(DESTDIR)$NGX_SBIN_PATH'\n\n\ttest -d '\\$(DESTDIR)$NGX_CONF_PREFIX' \\\\\n\t\t|| mkdir -p '\\$(DESTDIR)$NGX_CONF_PREFIX'\n\n\tcp conf/koi-win '\\$(DESTDIR)$NGX_CONF_PREFIX'\n\tcp conf/koi-utf '\\$(DESTDIR)$NGX_CONF_PREFIX'\n\tcp conf/win-utf '\\$(DESTDIR)$NGX_CONF_PREFIX'\n\n\ttest -f '\\$(DESTDIR)$NGX_CONF_PREFIX/mime.types' \\\\\n\t\t|| cp conf/mime.types '\\$(DESTDIR)$NGX_CONF_PREFIX'\n\tcp conf/mime.types '\\$(DESTDIR)$NGX_CONF_PREFIX/mime.types.default'\n\n\ttest -f '\\$(DESTDIR)$NGX_CONF_PREFIX/fastcgi_params' \\\\\n\t\t|| cp conf/fastcgi_params '\\$(DESTDIR)$NGX_CONF_PREFIX'\n\tcp conf/fastcgi_params \\\\\n\t\t'\\$(DESTDIR)$NGX_CONF_PREFIX/fastcgi_params.default'\n\n\ttest -f '\\$(DESTDIR)$NGX_CONF_PREFIX/fastcgi.conf' \\\\\n\t\t|| cp conf/fastcgi.conf '\\$(DESTDIR)$NGX_CONF_PREFIX'\n\tcp conf/fastcgi.conf '\\$(DESTDIR)$NGX_CONF_PREFIX/fastcgi.conf.default'\n\n\ttest -f '\\$(DESTDIR)$NGX_CONF_PREFIX/uwsgi_params' \\\\\n\t\t|| cp conf/uwsgi_params '\\$(DESTDIR)$NGX_CONF_PREFIX'\n\tcp conf/uwsgi_params \\\\\n\t\t'\\$(DESTDIR)$NGX_CONF_PREFIX/uwsgi_params.default'\n\n\ttest -f '\\$(DESTDIR)$NGX_CONF_PREFIX/scgi_params' \\\\\n\t\t|| cp conf/scgi_params '\\$(DESTDIR)$NGX_CONF_PREFIX'\n\tcp conf/scgi_params \\\\\n\t\t'\\$(DESTDIR)$NGX_CONF_PREFIX/scgi_params.default'\n\n\ttest -f '\\$(DESTDIR)$NGX_CONF_PATH' \\\\\n\t\t|| cp conf/nginx.conf '\\$(DESTDIR)$NGX_CONF_PATH'\n\tcp conf/nginx.conf '\\$(DESTDIR)$NGX_CONF_PREFIX/nginx.conf.default'\n\n\ttest -d '\\$(DESTDIR)`dirname \"$NGX_PID_PATH\"`' \\\\\n\t\t|| mkdir -p '\\$(DESTDIR)`dirname \"$NGX_PID_PATH\"`'\n\n\ttest -d '\\$(DESTDIR)`dirname \"$NGX_HTTP_LOG_PATH\"`' \\\\\n\t\t|| mkdir -p '\\$(DESTDIR)`dirname \"$NGX_HTTP_LOG_PATH\"`'\n\n\ttest -d '\\$(DESTDIR)$NGX_PREFIX/html' \\\\\n\t\t|| cp -R $NGX_HTML '\\$(DESTDIR)$NGX_PREFIX'\nEND\n\n\nif test -n \"$NGX_ERROR_LOG_PATH\"; then\n    cat << END                                                >> $NGX_MAKEFILE\n\n\ttest -d '\\$(DESTDIR)`dirname \"$NGX_ERROR_LOG_PATH\"`' \\\\\n\t\t|| mkdir -p '\\$(DESTDIR)`dirname \"$NGX_ERROR_LOG_PATH\"`'\nEND\n\nfi\n\n\nif test -n \"$DYNAMIC_MODULES\"; then\n    cat << END                                                >> $NGX_MAKEFILE\n\n\ttest -d '\\$(DESTDIR)$NGX_MODULES_PATH' \\\\\n\t\t|| mkdir -p '\\$(DESTDIR)$NGX_MODULES_PATH'\nEND\n\nfi\n\n\nfor ngx_module in $DYNAMIC_MODULES\ndo\n    ngx_module=$ngx_module$ngx_modext\n\n    cat << END                                                >> $NGX_MAKEFILE\n\n\ttest ! -f '\\$(DESTDIR)$NGX_MODULES_PATH/$ngx_module' \\\\\n\t\t|| mv '\\$(DESTDIR)$NGX_MODULES_PATH/$ngx_module' \\\\\n\t\t\t'\\$(DESTDIR)$NGX_MODULES_PATH/$ngx_module.old'\n\tcp $NGX_OBJS/$ngx_module '\\$(DESTDIR)$NGX_MODULES_PATH/$ngx_module'\nEND\n\ndone\n\n\n# create Makefile\n\ncat << END >> Makefile\n\nbuild:\n\t\\$(MAKE) -f $NGX_MAKEFILE\n\ninstall:\n\t\\$(MAKE) -f $NGX_MAKEFILE install\n\nmodules:\n\t\\$(MAKE) -f $NGX_MAKEFILE modules\n\nupgrade:\n\t$NGX_SBIN_PATH -t\n\n\tkill -USR2 \\`cat $NGX_PID_PATH\\`\n\tsleep 1\n\ttest -f $NGX_PID_PATH.oldbin\n\n\tkill -QUIT \\`cat $NGX_PID_PATH.oldbin\\`\n\n.PHONY:\tbuild install modules upgrade\nEND\n"
  },
  {
    "path": "auto/lib/conf",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\nif [ $USE_PCRE = YES -o $PCRE != NONE ]; then\n    . auto/lib/pcre/conf\n\nelse\n    if [ $USE_PCRE = DISABLED -a $HTTP = YES -a $HTTP_REWRITE = YES ]; then\n\ncat << END\n\n$0: error: the HTTP rewrite module requires the PCRE library.\nYou can either disable the module by using --without-http_rewrite_module\noption or you have to enable the PCRE support.\n\nEND\n        exit 1\n    fi\nfi\n\n\nif [ $USE_OPENSSL = YES ]; then\n    . auto/lib/openssl/conf\nfi\n\nif [ $USE_ZLIB = YES ]; then\n    . auto/lib/zlib/conf\nfi\n\nif [ $USE_LIBXSLT != NO ]; then\n    . auto/lib/libxslt/conf\nfi\n\nif [ $USE_LIBGD != NO ]; then\n    . auto/lib/libgd/conf\nfi\n\nif [ $USE_PERL != NO ]; then\n    . auto/lib/perl/conf\nfi\n\nif [ $USE_GEOIP != NO ]; then\n    . auto/lib/geoip/conf\nfi\n\nif [ $NGX_GOOGLE_PERFTOOLS = YES ]; then\n    . auto/lib/google-perftools/conf\nfi\n\nif [ $NGX_LIBATOMIC != NO ]; then\n    . auto/lib/libatomic/conf\nfi\n\nif [ $JEMALLOC != NO ]; then\n    . auto/lib/jemalloc/conf\nfi\n\n"
  },
  {
    "path": "auto/lib/geoip/conf",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\n    ngx_feature=\"GeoIP library\"\n    ngx_feature_name=\n    ngx_feature_run=no\n    ngx_feature_incs=\"#include <GeoIP.h>\"\n    ngx_feature_path=\n    ngx_feature_libs=\"-lGeoIP\"\n    ngx_feature_test=\"GeoIP_open(NULL, 0)\"\n    . auto/feature\n\n\nif [ $ngx_found = no ]; then\n\n    # FreeBSD port\n\n    ngx_feature=\"GeoIP library in /usr/local/\"\n    ngx_feature_path=\"/usr/local/include\"\n\n    if [ $NGX_RPATH = YES ]; then\n        ngx_feature_libs=\"-R/usr/local/lib -L/usr/local/lib -lGeoIP\"\n    else\n        ngx_feature_libs=\"-L/usr/local/lib -lGeoIP\"\n    fi\n\n    . auto/feature\nfi\n\n\nif [ $ngx_found = no ]; then\n\n    # NetBSD port\n\n    ngx_feature=\"GeoIP library in /usr/pkg/\"\n    ngx_feature_path=\"/usr/pkg/include\"\n\n    if [ $NGX_RPATH = YES ]; then\n        ngx_feature_libs=\"-R/usr/pkg/lib -L/usr/pkg/lib -lGeoIP\"\n    else\n        ngx_feature_libs=\"-L/usr/pkg/lib -lGeoIP\"\n    fi\n\n    . auto/feature\nfi\n\n\nif [ $ngx_found = no ]; then\n\n    # MacPorts\n\n    ngx_feature=\"GeoIP library in /opt/local/\"\n    ngx_feature_path=\"/opt/local/include\"\n\n    if [ $NGX_RPATH = YES ]; then\n        ngx_feature_libs=\"-R/opt/local/lib -L/opt/local/lib -lGeoIP\"\n    else\n        ngx_feature_libs=\"-L/opt/local/lib -lGeoIP\"\n    fi\n\n    . auto/feature\nfi\n\n\nif [ $ngx_found = yes ]; then\n\n    CORE_INCS=\"$CORE_INCS $ngx_feature_path\"\n\n    if [ $USE_GEOIP = YES ]; then\n        CORE_LIBS=\"$CORE_LIBS $ngx_feature_libs\"\n    fi\n\n    NGX_LIB_GEOIP=$ngx_feature_libs\n\n    ngx_feature=\"GeoIP IPv6 support\"\n    ngx_feature_name=\"NGX_HAVE_GEOIP_V6\"\n    ngx_feature_run=no\n    ngx_feature_incs=\"#include <stdio.h>\n                      #include <GeoIP.h>\"\n    #ngx_feature_path=\n    #ngx_feature_libs=\n    ngx_feature_test=\"printf(\\\"%d\\\", GEOIP_CITY_EDITION_REV0_V6);\"\n    . auto/feature\n\nelse\n\ncat << END\n\n$0: error: the GeoIP module requires the GeoIP library.\nYou can either do not enable the module or install the library.\n\nEND\n\n    exit 1\nfi\n"
  },
  {
    "path": "auto/lib/google-perftools/conf",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\n    ngx_feature=\"Google perftools\"\n    ngx_feature_name=\n    ngx_feature_run=no\n    ngx_feature_incs=\n    ngx_feature_path=\n    ngx_feature_libs=\"-lprofiler\"\n    ngx_feature_test=\"void ProfilerStop(void);\n                      ProfilerStop()\"\n    . auto/feature\n\n\nif [ $ngx_found = no ]; then\n\n    # FreeBSD port\n\n    ngx_feature=\"Google perftools in /usr/local/\"\n\n    if [ $NGX_RPATH = YES ]; then\n        ngx_feature_libs=\"-R/usr/local/lib -L/usr/local/lib -lprofiler\"\n    else\n        ngx_feature_libs=\"-L/usr/local/lib -lprofiler\"\n    fi\n\n    . auto/feature\nfi\n\n\nif [ $ngx_found = no ]; then\n\n    # MacPorts\n\n    ngx_feature=\"Google perftools in /opt/local/\"\n\n    if [ $NGX_RPATH = YES ]; then\n        ngx_feature_libs=\"-R/opt/local/lib -L/opt/local/lib -lprofiler\"\n    else\n        ngx_feature_libs=\"-L/opt/local/lib -lprofiler\"\n    fi\n\n    . auto/feature\nfi\n\n\nif [ $ngx_found = yes ]; then\n    CORE_LIBS=\"$CORE_LIBS $ngx_feature_libs\"\n\nelse\n\ncat << END\n\n$0: error: the Google perftools module requires the Google perftools\nlibrary. You can either do not enable the module or install the library.\n\nEND\n\n    exit 1\nfi\n"
  },
  {
    "path": "auto/lib/jemalloc/conf",
    "content": "\n# Copyright (C) 2010-2015 Alibaba Group Holding Limited\n\n\nif [ $JEMALLOC != NONE ]; then\n\n    case \"$NGX_CC_NAME\" in\n\n        *)\n            have=NGX_JEMALLOC . auto/have\n            LINK_DEPS=\"$LINK_DEPS $JEMALLOC/lib/libjemalloc.a\"\n            CORE_LIBS=\"$CORE_LIBS $JEMALLOC/lib/libjemalloc.a -lpthread\"\n            CFLAGS=\"$CFLAGS -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-free\"\n        ;;\n\n    esac\n\nelif [ $USE_JEMALLOC = YES ]; then\n    \n    if [ \"$NGX_PLATFORM\" != win32 ]; then\n        JEMALLOC=NO\n\n        # FreeBSD, Mac OS X, Linux\n\n        ngx_feature=\"jemalloc library\"\n        ngx_feature_name=\"NGX_JEMALLOC\"\n        ngx_feature_run=no\n        ngx_feature_incs=\n        ngx_feature_path=\n        ngx_feature_libs=\"-ljemalloc\"\n        ngx_feature_test=\n        . auto/feature\n\n\n        if [ $ngx_found = yes ]; then\n            if [ $CC = gcc ]; then\n                CFLAGS=\"$CFLAGS -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-free\"\n            fi        \n            CORE_LIBS=\"$CORE_LIBS $ngx_feature_libs\"\n            JEMALLOC=YES\n        fi\n    fi\n\n    if [ $JEMALLOC != YES ]; then\ncat << END\n\n$0: error: the --with-jemalloc requires the jemalloc library.\nYou can either do not enable this feature, or install the jemalloc \nlibrary into the system, or build the jemalloc library statically from \nthe source with nginx by using --with-jemalloc=<path> option.\n\nEND\n        exit 1\n    fi\n\nfi\n"
  },
  {
    "path": "auto/lib/jemalloc/make",
    "content": "\n# Copyright (C) 2010-2015 Alibaba Group Holding Limited\n\n\ndone=NO\n\n\ncase \"$NGX_PLATFORM\" in\n\n    *)\n        cat << END                                            >> $NGX_MAKEFILE\n\n$JEMALLOC/Makefile:\t$NGX_MAKEFILE\n\tcd $JEMALLOC \\\\\n\t&& if [ -f Makefile ]; then \\$(MAKE) distclean; fi \\\\\n\t&& CC=\"\\$(CC)\" \\\\\n\t./configure\n\n$JEMALLOC/lib/libjemalloc.a:\t$JEMALLOC/Makefile\n\tcd $JEMALLOC \\\\\n\t&& \\$(MAKE)\n\nEND\n\n    ;;\n\nesac\n"
  },
  {
    "path": "auto/lib/libatomic/conf",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\nif [ $NGX_LIBATOMIC != YES ]; then\n\n    have=NGX_HAVE_LIBATOMIC . auto/have\n    CORE_INCS=\"$CORE_INCS $NGX_LIBATOMIC/src\"\n    LINK_DEPS=\"$LINK_DEPS $NGX_LIBATOMIC/src/libatomic_ops.a\"\n    CORE_LIBS=\"$CORE_LIBS $NGX_LIBATOMIC/src/libatomic_ops.a\"\n\nelse\n\n    ngx_feature=\"atomic_ops library\"\n    ngx_feature_name=NGX_HAVE_LIBATOMIC\n    ngx_feature_run=yes\n    ngx_feature_incs=\"#define AO_REQUIRE_CAS\n                      #include <atomic_ops.h>\"\n    ngx_feature_path=\n    ngx_feature_libs=\"-latomic_ops\"\n    ngx_feature_test=\"long  n = 0;\n                      if (!AO_compare_and_swap(&n, 0, 1))\n                          return 1;\n                      if (AO_fetch_and_add(&n, 1) != 1)\n                          return 1;\n                      if (n != 2)\n                          return 1;\n                      AO_nop();\"\n    . auto/feature\n\n    if [ $ngx_found = yes ]; then\n        CORE_LIBS=\"$CORE_LIBS $ngx_feature_libs\"\n    else\n\ncat << END\n\n$0: error: libatomic_ops library was not found.\n\nEND\n        exit 1\n    fi\nfi\n"
  },
  {
    "path": "auto/lib/libatomic/make",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\n    cat << END                                            >> $NGX_MAKEFILE\n\n$NGX_LIBATOMIC/src/libatomic_ops.a:\t$NGX_LIBATOMIC/Makefile\n\tcd $NGX_LIBATOMIC && \\$(MAKE)\n\n$NGX_LIBATOMIC/Makefile:\t$NGX_MAKEFILE\n\tcd $NGX_LIBATOMIC \\\\\n\t&& if [ -f Makefile ]; then \\$(MAKE) distclean; fi \\\\\n\t&& ./configure\n\nEND\n"
  },
  {
    "path": "auto/lib/libgd/conf",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\n    ngx_feature=\"GD library\"\n    ngx_feature_name=\n    ngx_feature_run=no\n    ngx_feature_incs=\"#include <gd.h>\"\n    ngx_feature_path=\n    ngx_feature_libs=\"-lgd\"\n    ngx_feature_test=\"gdImagePtr img = gdImageCreateFromGifPtr(1, NULL);\n                      (void) img\"\n    . auto/feature\n\n\nif [ $ngx_found = no ]; then\n\n    # FreeBSD port\n\n    ngx_feature=\"GD library in /usr/local/\"\n    ngx_feature_path=\"/usr/local/include\"\n\n    if [ $NGX_RPATH = YES ]; then\n        ngx_feature_libs=\"-R/usr/local/lib -L/usr/local/lib -lgd\"\n    else\n        ngx_feature_libs=\"-L/usr/local/lib -lgd\"\n    fi\n\n    . auto/feature\nfi\n\n\nif [ $ngx_found = no ]; then\n\n    # NetBSD port\n\n    ngx_feature=\"GD library in /usr/pkg/\"\n    ngx_feature_path=\"/usr/pkg/include\"\n\n    if [ $NGX_RPATH = YES ]; then\n        ngx_feature_libs=\"-R/usr/pkg/lib -L/usr/pkg/lib -lgd\"\n    else\n        ngx_feature_libs=\"-L/usr/pkg/lib -lgd\"\n    fi\n\n    . auto/feature\nfi\n\n\nif [ $ngx_found = no ]; then\n\n    # MacPorts\n\n    ngx_feature=\"GD library in /opt/local/\"\n    ngx_feature_path=\"/opt/local/include\"\n\n    if [ $NGX_RPATH = YES ]; then\n        ngx_feature_libs=\"-R/opt/local/lib -L/opt/local/lib -lgd\"\n    else\n        ngx_feature_libs=\"-L/opt/local/lib -lgd\"\n    fi\n\n    . auto/feature\nfi\n\n\nif [ $ngx_found = yes ]; then\n\n    CORE_INCS=\"$CORE_INCS $ngx_feature_path\"\n\n    if [ $USE_LIBGD = YES ]; then\n        CORE_LIBS=\"$CORE_LIBS $ngx_feature_libs\"\n    fi\n\n    NGX_LIB_LIBGD=$ngx_feature_libs\n\n    ngx_feature=\"GD WebP support\"\n    ngx_feature_name=\"NGX_HAVE_GD_WEBP\"\n    ngx_feature_test=\"gdImagePtr img = gdImageCreateFromWebpPtr(1, NULL);\n                      (void) img\"\n    . auto/feature\n\nelse\n\ncat << END\n\n$0: error: the HTTP image filter module requires the GD library.\nYou can either do not enable the module or install the libraries.\n\nEND\n\n    exit 1\n\nfi\n"
  },
  {
    "path": "auto/lib/libxslt/conf",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\n    ngx_feature=\"libxslt\"\n    ngx_feature_name=\n    ngx_feature_run=no\n    ngx_feature_incs=\"#include <libxml/parser.h>\n                      #include <libxml/tree.h>\n                      #include <libxslt/xslt.h>\n                      #include <libxslt/xsltInternals.h>\n                      #include <libxslt/transform.h>\n                      #include <libxslt/xsltutils.h>\"\n    ngx_feature_path=\"/usr/include/libxml2\"\n    ngx_feature_libs=\"-lxml2 -lxslt\"\n    ngx_feature_test=\"xmlParserCtxtPtr    ctxt = NULL;\n                      xsltStylesheetPtr   sheet = NULL;\n                      xmlDocPtr           doc = NULL;\n                      xmlParseChunk(ctxt, NULL, 0, 0);\n                      xsltApplyStylesheet(sheet, doc, NULL);\"\n    . auto/feature\n\n\nif [ $ngx_found = no ]; then\n\n    # FreeBSD port\n\n    ngx_feature=\"libxslt in /usr/local/\"\n    ngx_feature_path=\"/usr/local/include/libxml2 /usr/local/include\"\n\n    if [ $NGX_RPATH = YES ]; then\n        ngx_feature_libs=\"-R/usr/local/lib -L/usr/local/lib -lxml2 -lxslt\"\n    else\n        ngx_feature_libs=\"-L/usr/local/lib -lxml2 -lxslt\"\n    fi\n\n    . auto/feature\nfi\n\n\nif [ $ngx_found = no ]; then\n\n    # NetBSD port\n\n    ngx_feature=\"libxslt in /usr/pkg/\"\n    ngx_feature_path=\"/usr/pkg/include/libxml2 /usr/pkg/include\"\n\n    if [ $NGX_RPATH = YES ]; then\n        ngx_feature_libs=\"-R/usr/pkg/lib -L/usr/pkg/lib -lxml2 -lxslt\"\n    else\n        ngx_feature_libs=\"-L/usr/pkg/lib -lxml2 -lxslt\"\n    fi\n\n    . auto/feature\nfi\n\n\nif [ $ngx_found = no ]; then\n\n    # MacPorts\n\n    ngx_feature=\"libxslt in /opt/local/\"\n    ngx_feature_path=\"/opt/local/include/libxml2 /opt/local/include\"\n\n    if [ $NGX_RPATH = YES ]; then\n        ngx_feature_libs=\"-R/opt/local/lib -L/opt/local/lib -lxml2 -lxslt\"\n    else\n        ngx_feature_libs=\"-L/opt/local/lib -lxml2 -lxslt\"\n    fi\n\n    . auto/feature\nfi\n\n\nif [ $ngx_found = yes ]; then\n\n    CORE_INCS=\"$CORE_INCS $ngx_feature_path\"\n\n    if [ $USE_LIBXSLT = YES ]; then\n        CORE_LIBS=\"$CORE_LIBS $ngx_feature_libs\"\n    fi\n\n    NGX_LIB_LIBXSLT=$ngx_feature_libs\n\nelse\n\ncat << END\n\n$0: error: the HTTP XSLT module requires the libxml2/libxslt\nlibraries. You can either do not enable the module or install the libraries.\n\nEND\n\n    exit 1\nfi\n\n\n    ngx_feature=\"libexslt\"\n    ngx_feature_name=NGX_HAVE_EXSLT\n    ngx_feature_run=no\n    ngx_feature_incs=\"#include <libexslt/exslt.h>\"\n    ngx_feature_path=\"/usr/include/libxml2\"\n    ngx_feature_libs=\"-lexslt\"\n    ngx_feature_test=\"exsltRegisterAll();\"\n    . auto/feature\n\nif [ $ngx_found = no ]; then\n\n    # FreeBSD port\n\n    ngx_feature=\"libexslt in /usr/local/\"\n    ngx_feature_path=\"/usr/local/include/libxml2 /usr/local/include\"\n\n    if [ $NGX_RPATH = YES ]; then\n        ngx_feature_libs=\"-R/usr/local/lib -L/usr/local/lib -lexslt\"\n    else\n        ngx_feature_libs=\"-L/usr/local/lib -lexslt\"\n    fi\n\n    . auto/feature\nfi\n\n\nif [ $ngx_found = no ]; then\n\n    # NetBSD port\n\n    ngx_feature=\"libexslt in /usr/pkg/\"\n    ngx_feature_path=\"/usr/pkg/include/libxml2 /usr/local/include\"\n\n    if [ $NGX_RPATH = YES ]; then\n        ngx_feature_libs=\"-R/usr/pkg/lib -L/usr/pkg/lib -lexslt\"\n    else\n        ngx_feature_libs=\"-L/usr/pkg/lib -lexslt\"\n    fi\n\n    . auto/feature\nfi\n\n\nif [ $ngx_found = no ]; then\n\n    # MacPorts\n\n    ngx_feature=\"libexslt in /opt/local/\"\n    ngx_feature_path=\"/opt/local/include/libxml2 /opt/local/include\"\n\n    if [ $NGX_RPATH = YES ]; then\n        ngx_feature_libs=\"-R/opt/local/lib -L/opt/local/lib -lexslt\"\n    else\n        ngx_feature_libs=\"-L/opt/local/lib -lexslt\"\n    fi\n\n    . auto/feature\nfi\n\n\nif [ $ngx_found = yes ]; then\n    if [ $USE_LIBXSLT = YES ]; then\n        CORE_LIBS=\"$CORE_LIBS -lexslt\"\n    fi\n\n    NGX_LIB_LIBXSLT=\"$NGX_LIB_LIBXSLT -lexslt\"\nfi\n"
  },
  {
    "path": "auto/lib/make",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\nif [ $PCRE != NONE -a $PCRE != NO -a $PCRE != YES ]; then\n    . auto/lib/pcre/make\nfi\n\nif [ $OPENSSL != NONE -a $OPENSSL != NO -a $OPENSSL != YES ]; then\n    . auto/lib/openssl/make\nfi\n\nif [ $ZLIB != NONE -a $ZLIB != NO -a $ZLIB != YES ]; then\n    . auto/lib/zlib/make\nfi\n\nif [ $NGX_LIBATOMIC != NO -a $NGX_LIBATOMIC != YES ]; then\n    . auto/lib/libatomic/make\nfi\n\nif [ $USE_PERL != NO ]; then\n    . auto/lib/perl/make\nfi\n\nif [ $JEMALLOC != NONE ]; then\n    . auto/lib/jemalloc/make\nfi\n\n"
  },
  {
    "path": "auto/lib/openssl/conf",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\nif [ $OPENSSL != NONE ]; then\n\n    case \"$CC\" in\n\n        cl | bcc32)\n            have=NGX_OPENSSL . auto/have\n            have=NGX_SSL . auto/have\n\n            CFLAGS=\"$CFLAGS -DNO_SYS_TYPES_H\"\n\n            CORE_INCS=\"$CORE_INCS $OPENSSL/openssl/include\"\n            CORE_DEPS=\"$CORE_DEPS $OPENSSL/openssl/include/openssl/ssl.h\"\n\n            if [ -f $OPENSSL/ms/do_ms.bat ]; then\n                # before OpenSSL 1.1.0\n                CORE_LIBS=\"$CORE_LIBS $OPENSSL/openssl/lib/ssleay32.lib\"\n                CORE_LIBS=\"$CORE_LIBS $OPENSSL/openssl/lib/libeay32.lib\"\n            else\n                # OpenSSL 1.1.0+\n                CORE_LIBS=\"$CORE_LIBS $OPENSSL/openssl/lib/libssl.lib\"\n                CORE_LIBS=\"$CORE_LIBS $OPENSSL/openssl/lib/libcrypto.lib\"\n            fi\n\n            # libeay32.lib requires gdi32.lib\n            CORE_LIBS=\"$CORE_LIBS gdi32.lib\"\n            # OpenSSL 1.0.0 requires crypt32.lib\n            CORE_LIBS=\"$CORE_LIBS crypt32.lib\"\n        ;;\n\n        *)\n            have=NGX_OPENSSL . auto/have\n            have=NGX_SSL . auto/have\n\n            CORE_INCS=\"$CORE_INCS $OPENSSL/.openssl/include\"\n            CORE_DEPS=\"$CORE_DEPS $OPENSSL/.openssl/include/openssl/ssl.h\"\n            CORE_LIBS=\"$CORE_LIBS $OPENSSL/.openssl/lib/libssl.a\"\n            CORE_LIBS=\"$CORE_LIBS $OPENSSL/.openssl/lib/libcrypto.a\"\n            CORE_LIBS=\"$CORE_LIBS $NGX_LIBDL\"\n            CORE_LIBS=\"$CORE_LIBS $NGX_LIBPTHREAD\"\n\n            if [ \"$NGX_PLATFORM\" = win32 ]; then\n                CORE_LIBS=\"$CORE_LIBS -lgdi32 -lcrypt32 -lws2_32\"\n            fi\n        ;;\n    esac\n\nelse\n\n    if [ \"$NGX_PLATFORM\" != win32 ]; then\n\n        OPENSSL=NO\n\n        ngx_feature=\"OpenSSL library\"\n        ngx_feature_name=\"NGX_OPENSSL\"\n        ngx_feature_run=no\n        ngx_feature_incs=\"#include <openssl/ssl.h>\"\n        ngx_feature_path=\n        ngx_feature_libs=\"-lssl -lcrypto $NGX_LIBDL $NGX_LIBPTHREAD\"\n        ngx_feature_test=\"SSL_CTX_set_options(NULL, 0)\"\n        . auto/feature\n\n        if [ $ngx_found = no ]; then\n\n            # FreeBSD port\n\n            ngx_feature=\"OpenSSL library in /usr/local/\"\n            ngx_feature_path=\"/usr/local/include\"\n\n            if [ $NGX_RPATH = YES ]; then\n                ngx_feature_libs=\"-R/usr/local/lib -L/usr/local/lib -lssl -lcrypto\"\n            else\n                ngx_feature_libs=\"-L/usr/local/lib -lssl -lcrypto\"\n            fi\n\n            ngx_feature_libs=\"$ngx_feature_libs $NGX_LIBDL $NGX_LIBPTHREAD\"\n\n            . auto/feature\n        fi\n\n        if [ $ngx_found = no ]; then\n\n            # NetBSD port\n\n            ngx_feature=\"OpenSSL library in /usr/pkg/\"\n            ngx_feature_path=\"/usr/pkg/include\"\n\n            if [ $NGX_RPATH = YES ]; then\n                ngx_feature_libs=\"-R/usr/pkg/lib -L/usr/pkg/lib -lssl -lcrypto\"\n            else\n                ngx_feature_libs=\"-L/usr/pkg/lib -lssl -lcrypto\"\n            fi\n\n            ngx_feature_libs=\"$ngx_feature_libs $NGX_LIBDL $NGX_LIBPTHREAD\"\n\n            . auto/feature\n        fi\n\n        if [ $ngx_found = no ]; then\n\n            # MacPorts\n\n            ngx_feature=\"OpenSSL library in /opt/local/\"\n            ngx_feature_path=\"/opt/local/include\"\n\n            if [ $NGX_RPATH = YES ]; then\n                ngx_feature_libs=\"-R/opt/local/lib -L/opt/local/lib -lssl -lcrypto\"\n            else\n                ngx_feature_libs=\"-L/opt/local/lib -lssl -lcrypto\"\n            fi\n\n            ngx_feature_libs=\"$ngx_feature_libs $NGX_LIBDL $NGX_LIBPTHREAD\"\n\n            . auto/feature\n        fi\n\n        if [ $ngx_found = yes ]; then\n            have=NGX_SSL . auto/have\n            CORE_INCS=\"$CORE_INCS $ngx_feature_path\"\n            CORE_LIBS=\"$CORE_LIBS $ngx_feature_libs\"\n            OPENSSL=YES\n        fi\n    fi\n\n    if [ $OPENSSL != YES ]; then\n\ncat << END\n\n$0: error: SSL modules require the OpenSSL library.\nYou can either do not enable the modules, or install the OpenSSL library\ninto the system, or build the OpenSSL library statically from the source\nwith nginx by using --with-openssl=<path> option.\n\nEND\n        exit 1\n    fi\n\nfi\n\n\nOPENSSL_ASYNC=\nif [ \"$NGX_SSL_ASYNC\" != NO ]; then\n    OPENSSL_ASYNC=NO\n\n    ngx_feature=\"OpenSSL Async Library\"\n    ngx_feature_name=\n    ngx_feature_run=no\n    ngx_feature_incs=\"#include <openssl/ssl.h>\"\n\n    if [ $OPENSSL = NONE ]; then\n         ngx_feature_path=\n    else\n         ngx_feature_path=$CORE_INCS\n    fi\n\n    ngx_feature_libs=\"-lssl -lcrypto\"\n    ngx_feature_test=\"#ifndef SSL_MODE_ASYNC\n    error: not define async\n    #endif\n    \"\n    . auto/feature\n    if [ $ngx_found = yes ]; then\n        have=NGX_SSL_ASYNC . auto/have\n        OPENSSL_ASYNC=YES\n    fi\nfi\n\nif [ -n \"$OPENSSL_ASYNC\"  -a  \"$OPENSSL_ASYNC\" != YES  ]; then\ncat << END\n$1: error: For using asynchronous mode, The OpenSSL must be version 1.1.0 or greater.\n\nEND\n    exit 1\nfi\n\n\nngx_feature=\"OpenSSL DTLS support\"\nngx_feature_name=\nngx_feature_run=no\nngx_feature_incs=\"#include <openssl/ssl.h>\"\nif [ $OPENSSL = NONE ]; then\n    ngx_feature_path=\nelse\n    ngx_feature_path=$CORE_INCS\nfi\nngx_feature_libs=\"-lssl -lcrypto\"\nngx_feature_test=\"SSL_CTX_set_cookie_generate_cb(NULL, NULL)\"\n\n. auto/feature\n\nif [ $ngx_found = yes ]; then\n    have=T_NGX_HAVE_DTLS . auto/have\nfi"
  },
  {
    "path": "auto/lib/openssl/make",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\ncase \"$CC\" in\n\n    cl)\n\n        case \"$NGX_MACHINE\" in\n\n            amd64)\n                OPENSSL_TARGET=VC-WIN64A\n            ;;\n\n            *)\n                OPENSSL_TARGET=VC-WIN32\n            ;;\n\n        esac\n\n        cat << END                                            >> $NGX_MAKEFILE\n\n$OPENSSL/openssl/include/openssl/ssl.h:\t$NGX_MAKEFILE\n\t\\$(MAKE) -f auto/lib/openssl/makefile.msvc\t\t\t\\\n\t\tOPENSSL=\"$OPENSSL\" OPENSSL_OPT=\"$OPENSSL_OPT\"\t\t\\\n\t\tOPENSSL_TARGET=\"$OPENSSL_TARGET\"\n\nEND\n\n    ;;\n\n    bcc32)\n\n        ngx_opt=`echo \"-DOPENSSL=\\\"$OPENSSL\\\" -DOPENSSL_OPT=\\\"$OPENSSL_OPT\\\"\" \\\n            | sed -e \"s/\\//$ngx_regex_dirsep/g\"`\n\n        cat << END                                            >> $NGX_MAKEFILE\n\n`echo \"$OPENSSL\\\\openssl\\\\lib\\\\libeay32.lib:\t\t\t\t\\\n\t$OPENSSL\\\\openssl\\\\include\\\\openssl\\\\ssl.h\"\t\t\t\\\n\t| sed -e \"s/\\//$ngx_regex_dirsep/g\"`\n\n`echo \"$OPENSSL\\\\openssl\\\\lib\\\\ssleay32.lib:\t\t\t\t\\\n\t$OPENSSL\\\\openssl\\\\include\\\\openssl\\\\ssl.h\"\t\t\t\\\n\t| sed -e \"s/\\//$ngx_regex_dirsep/g\"`\n\n`echo \"$OPENSSL\\\\openssl\\\\include\\\\openssl\\\\ssl.h:\t$NGX_MAKEFILE\"\t\\\n\t| sed -e \"s/\\//$ngx_regex_dirsep/g\"`\n\t\\$(MAKE) -f auto/lib/openssl/makefile.bcc $ngx_opt\n\nEND\n\n    ;;\n\n    *)\n        case $OPENSSL in\n            /*) ngx_prefix=\"$OPENSSL/.openssl\" ;;\n            *)  ngx_prefix=\"$PWD/$OPENSSL/.openssl\" ;;\n        esac\n\n        cat << END                                            >> $NGX_MAKEFILE\n\n$OPENSSL/.openssl/include/openssl/ssl.h:\t$NGX_MAKEFILE\n\tcd $OPENSSL \\\\\n\t&& if [ -f Makefile ]; then \\$(MAKE) clean; fi \\\\\n\t&& ./config --prefix=$ngx_prefix no-shared no-threads $OPENSSL_OPT \\\\\n\t&& \\$(MAKE) \\\\\n\t&& \\$(MAKE) install_sw LIBDIR=lib\n\nEND\n\n    ;;\n\nesac\n"
  },
  {
    "path": "auto/lib/openssl/makefile.bcc",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\nall:\n\tcd $(OPENSSL)\n\n\tperl Configure BC-32 no-shared --prefix=openssl $(OPENSSL_OPT)\n\n\tms\\do_nasm\n\n\t$(MAKE) -f ms\\bcb.mak\n\t$(MAKE) -f ms\\bcb.mak install\n\n\t# Borland's make does not expand \"[ch]\" in\n\t#    copy \"inc32\\openssl\\*.[ch]\" \"openssl\\include\\openssl\"\n\tcopy inc32\\openssl\\*.h openssl\\include\\openssl\n"
  },
  {
    "path": "auto/lib/openssl/makefile.msvc",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\nall:\n\tcd $(OPENSSL)\n\n\tperl Configure $(OPENSSL_TARGET) no-shared no-threads\t\t\\\n\t\t--prefix=\"%cd%/openssl\" \t\t\t\t\\\n\t\t--openssldir=\"%cd%/openssl/ssl\" \t\t\t\\\n\t\t$(OPENSSL_OPT)\n\n\tif exist ms\\do_ms.bat (\t\t\t\t\t\t\\\n\t\tms\\do_ms\t\t\t\t\t\t\\\n\t\t&& $(MAKE) -f ms\\nt.mak\t\t\t\t\t\\\n\t\t&& $(MAKE) -f ms\\nt.mak install\t\t\t\t\\\n\t) else (\t\t\t\t\t\t\t\\\n\t\t$(MAKE)\t\t\t\t\t\t\t\\\n\t\t&& $(MAKE) install_sw\t\t\t\t\t\\\n\t)\n"
  },
  {
    "path": "auto/lib/pcre/conf",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\nif [ $PCRE != NONE ]; then\n\n    if [ -f $PCRE/src/pcre2.h.generic ]; then\n\n        PCRE_LIBRARY=PCRE2\n\n        have=NGX_PCRE . auto/have\n        have=NGX_PCRE2 . auto/have\n\n        if [ \"$NGX_PLATFORM\" = win32 ]; then\n            have=PCRE2_STATIC . auto/have\n        fi\n\n        CORE_INCS=\"$CORE_INCS $PCRE/src/\"\n        CORE_DEPS=\"$CORE_DEPS $PCRE/src/pcre2.h\"\n\n        case \"$NGX_CC_NAME\" in\n\n            msvc)\n                LINK_DEPS=\"$LINK_DEPS $PCRE/src/pcre2-8.lib\"\n                CORE_LIBS=\"$CORE_LIBS $PCRE/src/pcre2-8.lib\"\n            ;;\n\n            *)\n                LINK_DEPS=\"$LINK_DEPS $PCRE/.libs/libpcre2-8.a\"\n                CORE_LIBS=\"$CORE_LIBS $PCRE/.libs/libpcre2-8.a\"\n            ;;\n\n        esac\n\n    else\n\n        PCRE_LIBRARY=PCRE\n\n        have=NGX_PCRE . auto/have\n\n        if [ \"$NGX_PLATFORM\" = win32 ]; then\n            have=PCRE_STATIC . auto/have\n        fi\n\n        CORE_INCS=\"$CORE_INCS $PCRE\"\n        CORE_DEPS=\"$CORE_DEPS $PCRE/pcre.h\"\n\n        case \"$NGX_CC_NAME\" in\n\n            msvc | owc | bcc)\n                LINK_DEPS=\"$LINK_DEPS $PCRE/pcre.lib\"\n                CORE_LIBS=\"$CORE_LIBS $PCRE/pcre.lib\"\n            ;;\n\n            *)\n                LINK_DEPS=\"$LINK_DEPS $PCRE/.libs/libpcre.a\"\n                CORE_LIBS=\"$CORE_LIBS $PCRE/.libs/libpcre.a\"\n            ;;\n\n        esac\n    fi\n\n    if [ $PCRE_JIT = YES ]; then\n        have=NGX_HAVE_PCRE_JIT . auto/have\n        PCRE_CONF_OPT=\"$PCRE_CONF_OPT --enable-jit\"\n    fi\n\nelse\n\n    if [ \"$NGX_PLATFORM\" != win32 ]; then\n        PCRE=NO\n    fi\n\n    if [ $PCRE = NO -a $PCRE2 != DISABLED ]; then\n\n        ngx_feature=\"PCRE2 library\"\n        ngx_feature_name=\"NGX_PCRE2\"\n        ngx_feature_run=no\n        ngx_feature_incs=\"#define PCRE2_CODE_UNIT_WIDTH 8\n                          #include <pcre2.h>\"\n        ngx_feature_path=\n        ngx_feature_libs=\"-lpcre2-8\"\n        ngx_feature_test=\"pcre2_code *re;\n                          re = pcre2_compile(NULL, 0, 0, NULL, NULL, NULL);\n                          if (re == NULL) return 1\"\n        . auto/feature\n\n        if [ $ngx_found = no ]; then\n\n            # pcre2-config\n\n            ngx_pcre2_prefix=`pcre2-config --prefix 2>/dev/null`\n\n            if [ -n \"$ngx_pcre2_prefix\" ]; then\n                ngx_feature=\"PCRE2 library in $ngx_pcre2_prefix\"\n                ngx_feature_path=`pcre2-config --cflags \\\n                                  | sed -n -e 's/.*-I *\\([^ ][^ ]*\\).*/\\1/p'`\n                ngx_feature_libs=`pcre2-config --libs8`\n                . auto/feature\n            fi\n        fi\n\n        if [ $ngx_found = yes ]; then\n            have=NGX_PCRE . auto/have\n            CORE_INCS=\"$CORE_INCS $ngx_feature_path\"\n            CORE_LIBS=\"$CORE_LIBS $ngx_feature_libs\"\n            PCRE=YES\n            PCRE_LIBRARY=PCRE2\n        fi\n    fi\n\n    if [ $PCRE = NO ]; then\n\n        ngx_feature=\"PCRE library\"\n        ngx_feature_name=\"NGX_PCRE\"\n        ngx_feature_run=no\n        ngx_feature_incs=\"#include <pcre.h>\"\n        ngx_feature_path=\n        ngx_feature_libs=\"-lpcre\"\n        ngx_feature_test=\"pcre *re;\n                          re = pcre_compile(NULL, 0, NULL, 0, NULL);\n                          if (re == NULL) return 1\"\n        . auto/feature\n\n        if [ $ngx_found = no ]; then\n\n            # FreeBSD port\n\n            ngx_feature=\"PCRE library in /usr/local/\"\n            ngx_feature_path=\"/usr/local/include\"\n\n            if [ $NGX_RPATH = YES ]; then\n                ngx_feature_libs=\"-R/usr/local/lib -L/usr/local/lib -lpcre\"\n            else\n                ngx_feature_libs=\"-L/usr/local/lib -lpcre\"\n            fi\n\n            . auto/feature\n        fi\n\n        if [ $ngx_found = no ]; then\n\n            # RedHat RPM, Solaris package\n\n            ngx_feature=\"PCRE library in /usr/include/pcre/\"\n            ngx_feature_path=\"/usr/include/pcre\"\n            ngx_feature_libs=\"-lpcre\"\n\n            . auto/feature\n        fi\n\n        if [ $ngx_found = no ]; then\n\n            # NetBSD port\n\n            ngx_feature=\"PCRE library in /usr/pkg/\"\n            ngx_feature_path=\"/usr/pkg/include\"\n\n            if [ $NGX_RPATH = YES ]; then\n                ngx_feature_libs=\"-R/usr/pkg/lib -L/usr/pkg/lib -lpcre\"\n            else\n                ngx_feature_libs=\"-L/usr/pkg/lib -lpcre\"\n            fi\n\n            . auto/feature\n        fi\n\n        if [ $ngx_found = no ]; then\n\n            # MacPorts\n\n            ngx_feature=\"PCRE library in /opt/local/\"\n            ngx_feature_path=\"/opt/local/include\"\n\n            if [ $NGX_RPATH = YES ]; then\n                ngx_feature_libs=\"-R/opt/local/lib -L/opt/local/lib -lpcre\"\n            else\n                ngx_feature_libs=\"-L/opt/local/lib -lpcre\"\n            fi\n\n            . auto/feature\n        fi\n\n        if [ $ngx_found = yes ]; then\n            CORE_INCS=\"$CORE_INCS $ngx_feature_path\"\n            CORE_LIBS=\"$CORE_LIBS $ngx_feature_libs\"\n            PCRE=YES\n            PCRE_LIBRARY=PCRE\n        fi\n\n        if [ $PCRE = YES ]; then\n            ngx_feature=\"PCRE JIT support\"\n            ngx_feature_name=\"NGX_HAVE_PCRE_JIT\"\n            ngx_feature_test=\"int jit = 0;\n                              pcre_free_study(NULL);\n                              pcre_config(PCRE_CONFIG_JIT, &jit);\n                              if (jit != 1) return 1;\"\n            . auto/feature\n\n            if [ $ngx_found = yes ]; then\n                PCRE_JIT=YES\n            fi\n        fi\n    fi\n\n    if [ $PCRE != YES ]; then\ncat << END\n\n$0: error: the HTTP rewrite module requires the PCRE library.\nYou can either disable the module by using --without-http_rewrite_module\noption, or install the PCRE library into the system, or build the PCRE library\nstatically from the source with nginx by using --with-pcre=<path> option.\n\nEND\n        exit 1\n    fi\n\nfi\n"
  },
  {
    "path": "auto/lib/pcre/make",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\nif [ $PCRE_LIBRARY = PCRE2 ]; then\n\n    # PCRE2\n\n    if [ $NGX_CC_NAME = msvc ]; then\n\n        # With PCRE2, it is not possible to compile all sources.\n        # Since list of source files changes between versions, we\n        # test files which might not be present.\n\n        ngx_pcre_srcs=\"pcre2_auto_possess.c \\\n                       pcre2_chartables.c \\\n                       pcre2_compile.c \\\n                       pcre2_config.c \\\n                       pcre2_context.c \\\n                       pcre2_dfa_match.c \\\n                       pcre2_error.c \\\n                       pcre2_jit_compile.c \\\n                       pcre2_maketables.c \\\n                       pcre2_match.c \\\n                       pcre2_match_data.c \\\n                       pcre2_newline.c \\\n                       pcre2_ord2utf.c \\\n                       pcre2_pattern_info.c \\\n                       pcre2_string_utils.c \\\n                       pcre2_study.c \\\n                       pcre2_substitute.c \\\n                       pcre2_substring.c \\\n                       pcre2_tables.c \\\n                       pcre2_ucd.c \\\n                       pcre2_valid_utf.c \\\n                       pcre2_xclass.c\"\n\n        ngx_pcre_test=\"pcre2_convert.c \\\n                       pcre2_extuni.c \\\n                       pcre2_find_bracket.c \\\n                       pcre2_script_run.c \\\n                       pcre2_serialize.c\"\n\n        for ngx_src in $ngx_pcre_test\n        do\n            if [ -f $PCRE/src/$ngx_src ]; then\n                ngx_pcre_srcs=\"$ngx_pcre_srcs $ngx_src\"\n            fi\n        done\n\n        ngx_pcre_objs=`echo $ngx_pcre_srcs \\\n            | sed -e \"s#\\([^ ]*\\.\\)c#\\1$ngx_objext#g\"`\n\n        ngx_pcre_srcs=`echo $ngx_pcre_srcs \\\n            | sed -e \"s/  *\\([^ ][^ ]*\\)/$ngx_regex_cont\\1/g\"`\n        ngx_pcre_objs=`echo $ngx_pcre_objs \\\n            | sed -e \"s/  *\\([^ ][^ ]*\\)/$ngx_regex_cont\\1/g\"`\n\n        cat << END                                            >> $NGX_MAKEFILE\n\nPCRE_CFLAGS =\t-O2 -Ob1 -Oi -Gs $LIBC $CPU_OPT\nPCRE_FLAGS =\t-DHAVE_CONFIG_H -DPCRE2_STATIC -DPCRE2_CODE_UNIT_WIDTH=8 \\\\\n\t\t-DHAVE_MEMMOVE\n\nPCRE_SRCS =\t $ngx_pcre_srcs\nPCRE_OBJS =\t $ngx_pcre_objs\n\n$PCRE/src/pcre2.h:\n\tcd $PCRE/src \\\\\n\t&& copy /y config.h.generic config.h \\\\\n\t&& copy /y pcre2.h.generic pcre2.h \\\\\n\t&& copy /y pcre2_chartables.c.dist pcre2_chartables.c\n\n$PCRE/src/pcre2-8.lib:\t$PCRE/src/pcre2.h $NGX_MAKEFILE\n\tcd $PCRE/src \\\\\n\t&& cl -nologo -c \\$(PCRE_CFLAGS) -I . \\$(PCRE_FLAGS) \\$(PCRE_SRCS) \\\\\n\t&& link -lib -out:pcre2-8.lib -verbose:lib \\$(PCRE_OBJS)\n\nEND\n\n    else\n\n        cat << END                                            >> $NGX_MAKEFILE\n\n$PCRE/src/pcre2.h:\t$PCRE/Makefile\n\n$PCRE/Makefile:\t$NGX_MAKEFILE\n\tcd $PCRE \\\\\n\t&& if [ -f Makefile ]; then \\$(MAKE) distclean; fi \\\\\n\t&& CC=\"\\$(CC)\" CFLAGS=\"$PCRE_OPT\" \\\\\n\t./configure --disable-shared $PCRE_CONF_OPT\n\n$PCRE/.libs/libpcre2-8.a:\t$PCRE/Makefile\n\tcd $PCRE \\\\\n\t&& \\$(MAKE) libpcre2-8.la\n\nEND\n\n    fi\n\n\nelse\n\n    # PCRE\n\n    case \"$NGX_CC_NAME\" in\n\n        msvc)\n            ngx_makefile=makefile.msvc\n            ngx_opt=\"CPU_OPT=\\\"$CPU_OPT\\\" LIBC=$LIBC\"\n            ngx_pcre=\"PCRE=\\\"$PCRE\\\"\"\n        ;;\n\n        owc)\n            ngx_makefile=makefile.owc\n            ngx_opt=\"CPU_OPT=\\\"$CPU_OPT\\\"\"\n            ngx_pcre=`echo PCRE=\\\"$PCRE\\\" | sed -e \"s/\\//$ngx_regex_dirsep/g\"`\n        ;;\n\n        bcc)\n            ngx_makefile=makefile.bcc\n            ngx_opt=\"-DCPU_OPT=\\\"$CPU_OPT\\\"\"\n            ngx_pcre=`echo \\-DPCRE=\\\"$PCRE\\\" \\\n                | sed -e \"s/\\//$ngx_regex_dirsep/g\"`\n        ;;\n\n        *)\n            ngx_makefile=\n        ;;\n\n    esac\n\n\n    if [ -n \"$ngx_makefile\" ]; then\n\n        cat << END                                            >> $NGX_MAKEFILE\n\n`echo \"$PCRE/pcre.lib:\t$PCRE/pcre.h $NGX_MAKEFILE\"\t\t\t\\\n\t| sed -e \"s/\\//$ngx_regex_dirsep/g\"`\n\t\\$(MAKE) -f auto/lib/pcre/$ngx_makefile $ngx_pcre $ngx_opt\n\n`echo \"$PCRE/pcre.h:\" | sed -e \"s/\\//$ngx_regex_dirsep/g\"`\n\t\\$(MAKE) -f auto/lib/pcre/$ngx_makefile $ngx_pcre pcre.h\n\nEND\n\n    else\n\n        cat << END                                            >> $NGX_MAKEFILE\n\n$PCRE/pcre.h:\t$PCRE/Makefile\n\n$PCRE/Makefile:\t$NGX_MAKEFILE\n\tcd $PCRE \\\\\n\t&& if [ -f Makefile ]; then \\$(MAKE) distclean; fi \\\\\n\t&& CC=\"\\$(CC)\" CFLAGS=\"$PCRE_OPT\" \\\\\n\t./configure --disable-shared $PCRE_CONF_OPT\n\n$PCRE/.libs/libpcre.a:\t$PCRE/Makefile\n\tcd $PCRE \\\\\n\t&& \\$(MAKE) libpcre.la\n\nEND\n\n    fi\n\nfi\n"
  },
  {
    "path": "auto/lib/pcre/makefile.bcc",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\nCFLAGS =\t-q -O2 -tWM -w-8004 $(CPU_OPT)\nPCREFLAGS =\t-DHAVE_CONFIG_H -DPCRE_STATIC -DPOSIX_MALLOC_THRESHOLD=10 \\\n\t\t-DSUPPORT_PCRE8 -DHAVE_MEMMOVE\n\n\npcre.lib:\n\tcd $(PCRE)\n\n\tbcc32 -c $(CFLAGS) -I. $(PCREFLAGS) pcre_*.c\n\n\tcopy /y nul pcre.lst\n\tfor %n in (*.obj) do @echo +%n ^^& >> pcre.lst\n\techo + >> pcre.lst\n\n\ttlib pcre.lib @pcre.lst\n\npcre.h:\n\tcd $(PCRE)\n\n\tcopy /y pcre.h.generic pcre.h\n\tcopy /y config.h.generic config.h\n\tcopy /y pcre_chartables.c.dist pcre_chartables.c\n"
  },
  {
    "path": "auto/lib/pcre/makefile.msvc",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\nCFLAGS =\t-O2 -Ob1 -Oi -Gs $(LIBC) $(CPU_OPT)\nPCREFLAGS =\t-DHAVE_CONFIG_H -DPCRE_STATIC -DPOSIX_MALLOC_THRESHOLD=10 \\\n\t\t-DSUPPORT_PCRE8 -DHAVE_MEMMOVE\n\n\npcre.lib:\n\tcd $(PCRE)\n\n\tcl -nologo -c $(CFLAGS) -I . $(PCREFLAGS) pcre_*.c\n\n\tlink -lib -out:pcre.lib -verbose:lib pcre_*.obj\n\npcre.h:\n\tcd $(PCRE)\n\n\tcopy /y pcre.h.generic pcre.h\n\tcopy /y config.h.generic config.h\n\tcopy /y pcre_chartables.c.dist pcre_chartables.c\n"
  },
  {
    "path": "auto/lib/pcre/makefile.owc",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\nCFLAGS =\t-c -zq -bt=nt -ot -op -oi -oe -s -bm $(CPU_OPT)\nPCREFLAGS =\t-DHAVE_CONFIG_H -DPCRE_STATIC -DPOSIX_MALLOC_THRESHOLD=10 &\n\t\t-DSUPPORT_PCRE8 -DHAVE_MEMMOVE\n\n\npcre.lib:\n\tcd $(PCRE)\n\n\twcl386 $(CFLAGS) -i=. $(PCREFLAGS) pcre_*.c\n\n\tdir /b *.obj > pcre.lst\n\n\twlib -n pcre.lib @pcre.lst\n\npcre.h:\n\tcd $(PCRE)\n\n\tcopy /y pcre.h.generic pcre.h\n\tcopy /y config.h.generic config.h\n\tcopy /y pcre_chartables.c.dist pcre_chartables.c\n"
  },
  {
    "path": "auto/lib/perl/conf",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\necho \"checking for perl\"\n\n\nNGX_PERL_VER=`$NGX_PERL -v 2>&1 | grep '^This is perl' 2>&1 \\\n                                | sed -e 's/^This is perl, \\(.*\\)/\\1/'`\n\nif test -n \"$NGX_PERL_VER\"; then\n    echo \" + perl version: $NGX_PERL_VER\"\n\n    if [ \"`$NGX_PERL -e 'use 5.008006; print \"OK\"'`\" != \"OK\" ]; then\n        echo\n        echo \"$0: error: perl 5.8.6 or higher is required\"\n        echo\n\n        exit 1;\n    fi\n\n    if [ \"`$NGX_PERL -MExtUtils::Embed -e 'print \"OK\"'`\" != \"OK\" ]; then\n        echo\n        echo \"$0: error: perl module ExtUtils::Embed is required\"\n        echo\n\n        exit 1;\n    fi\n\n    NGX_PM_CFLAGS=`$NGX_PERL -MExtUtils::Embed -e ccopts`\n    NGX_PM_LDFLAGS=`$NGX_PERL -MConfig -e 'print $Config{lddlflags}'`\n\n    NGX_PERL_CFLAGS=\"$CFLAGS `$NGX_PERL -MExtUtils::Embed -e ccopts`\"\n\n    # gcc 4.1/4.2 warn about unused values in pTHX_\n    NGX_PERL_CFLAGS=`echo $NGX_PERL_CFLAGS \\\n                     | sed -e 's/-Wunused-value/-Wno-unused-value/'`\n    # icc8 warns 'declaration hides parameter \"my_perl\"' in ENTER and LEAVE\n    NGX_PERL_CFLAGS=`echo $NGX_PERL_CFLAGS \\\n                     | sed -e 's/-wd171/-wd171 -wd1599/'`\n\n    ngx_perl_ldopts=`$NGX_PERL -MExtUtils::Embed -e ldopts`\n\n    ngx_perl_dlext=`$NGX_PERL -MConfig -e 'print $Config{dlext}'`\n    ngx_perl_libdir=\"src/http/modules/perl/blib/arch/auto\"\n    ngx_perl_module=\"$ngx_perl_libdir/nginx/nginx.$ngx_perl_dlext\"\n\n    if $NGX_PERL -V:usemultiplicity | grep define > /dev/null; then\n        have=NGX_HAVE_PERL_MULTIPLICITY . auto/have\n        echo \" + perl interpreter multiplicity found\"\n    fi\n\n    if $NGX_PERL -V:useithreads | grep undef > /dev/null; then\n        # FreeBSD port wants to link with -pthread non-threaded perl\n        ngx_perl_ldopts=`echo $ngx_perl_ldopts | sed 's/ -pthread//'`\n    fi\n\n    if [ \"$NGX_SYSTEM\" = \"Darwin\" ]; then\n        # OS X system perl wants to link universal binaries\n        ngx_perl_ldopts=`echo $ngx_perl_ldopts \\\n                         | sed -e 's/-arch i386//' -e 's/-arch x86_64//'`\n    fi\n\n    if [ $USE_PERL = YES ]; then\n        CORE_LINK=\"$CORE_LINK $ngx_perl_ldopts\"\n    fi\n\n    NGX_LIB_PERL=\"$ngx_perl_ldopts\"\n\n    if test -n \"$NGX_PERL_MODULES\"; then\n        have=NGX_PERL_MODULES value=\"(u_char *) \\\"$NGX_PERL_MODULES\\\"\"\n        . auto/define\n        NGX_PERL_MODULES_MAN=$NGX_PERL_MODULES/man3\n    fi\n\nelse\n    echo\n    echo \"$0: error: perl 5.8.6 or higher is required\"\n    echo\n\n    exit 1;\nfi\n"
  },
  {
    "path": "auto/lib/perl/make",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\ncat << END                                                    >> $NGX_MAKEFILE\n\n$NGX_OBJS/src/http/modules/perl/ngx_http_perl_module.o: \\\\\n\t\t$NGX_OBJS/$ngx_perl_module\n\n$NGX_OBJS/$ngx_perl_module: \\\\\n\t\t\\$(CORE_DEPS) \\$(HTTP_DEPS) \\\\\n\t\tsrc/http/modules/perl/ngx_http_perl_module.h \\\\\n\t\t$NGX_OBJS/src/http/modules/perl/Makefile\n\tcd $NGX_OBJS/src/http/modules/perl && \\$(MAKE)\n\n\trm -rf $NGX_OBJS/install_perl\n\n\n$NGX_OBJS/src/http/modules/perl/Makefile: \\\\\n\t\t$NGX_AUTO_CONFIG_H \\\\\n\t\tsrc/core/nginx.h \\\\\n\t\tsrc/http/modules/perl/Makefile.PL \\\\\n\t\tsrc/http/modules/perl/nginx.pm \\\\\n\t\tsrc/http/modules/perl/nginx.xs \\\\\n\t\tsrc/http/modules/perl/typemap\n\tgrep 'define NGINX_VERSION' src/core/nginx.h \\\\\n\t\t| sed -e 's/^.*\"\\(.*\\)\".*/\\1/' > \\\\\n\t\t$NGX_OBJS/src/http/modules/perl/version\n\tsed \"s/%%VERSION%%/\\`cat $NGX_OBJS/src/http/modules/perl/version\\`/\" \\\\\n\t\tsrc/http/modules/perl/nginx.pm > \\\\\n\t\t$NGX_OBJS/src/http/modules/perl/nginx.pm\n\tcp -p src/http/modules/perl/nginx.xs $NGX_OBJS/src/http/modules/perl/\n\tcp -p src/http/modules/perl/typemap $NGX_OBJS/src/http/modules/perl/\n\tcp -p src/http/modules/perl/Makefile.PL $NGX_OBJS/src/http/modules/perl/\n\n\tcd $NGX_OBJS/src/http/modules/perl \\\\\n\t\t&& NGX_PM_CFLAGS=\"\\$(NGX_PM_CFLAGS) -g $NGX_CC_OPT\" \\\\\n\t\t\tNGX_PM_LDFLAGS=\"$NGX_LD_OPT \\$(NGX_PM_LDFLAGS)\" \\\\\n\t\t\tNGX_INCS=\"$CORE_INCS $NGX_OBJS $HTTP_INCS\" \\\\\n\t\t\tNGX_DEPS=\"\\$(CORE_DEPS) \\$(HTTP_DEPS)\" \\\\\n\t\t$NGX_PERL Makefile.PL \\\\\n\t\t\tLIB=$NGX_PERL_MODULES \\\\\n\t\t\tINSTALLSITEMAN3DIR=$NGX_PERL_MODULES_MAN\n\nEND\n"
  },
  {
    "path": "auto/lib/zlib/conf",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\nif [ $ZLIB != NONE ]; then\n    CORE_INCS=\"$CORE_INCS $ZLIB\"\n\n    case \"$NGX_CC_NAME\" in\n\n        msvc | owc | bcc)\n            have=NGX_ZLIB . auto/have\n            LINK_DEPS=\"$LINK_DEPS $ZLIB/zlib.lib\"\n            CORE_LIBS=\"$CORE_LIBS $ZLIB/zlib.lib\"\n        ;;\n\n        icc)\n            have=NGX_ZLIB . auto/have\n            LINK_DEPS=\"$LINK_DEPS $ZLIB/libz.a\"\n\n            # to allow -ipo optimization we link with the *.o but not library\n            CORE_LIBS=\"$CORE_LIBS $ZLIB/adler32.o\"\n            CORE_LIBS=\"$CORE_LIBS $ZLIB/crc32.o\"\n            CORE_LIBS=\"$CORE_LIBS $ZLIB/deflate.o\"\n            CORE_LIBS=\"$CORE_LIBS $ZLIB/trees.o\"\n            CORE_LIBS=\"$CORE_LIBS $ZLIB/zutil.o\"\n            CORE_LIBS=\"$CORE_LIBS $ZLIB/compress.o\"\n\n            if [ $ZLIB_ASM != NO ]; then\n                CORE_LIBS=\"$CORE_LIBS $ZLIB/match.o\"\n            fi\n        ;;\n\n        *)\n            have=NGX_ZLIB . auto/have\n            LINK_DEPS=\"$LINK_DEPS $ZLIB/libz.a\"\n            CORE_LIBS=\"$CORE_LIBS $ZLIB/libz.a\"\n            #CORE_LIBS=\"$CORE_LIBS -L $ZLIB -lz\"\n        ;;\n\n    esac\n\nelse\n\n    if [ \"$NGX_PLATFORM\" != win32 ]; then\n        ZLIB=NO\n\n        # FreeBSD, Solaris, Linux\n\n        ngx_feature=\"zlib library\"\n        ngx_feature_name=\"NGX_ZLIB\"\n        ngx_feature_run=no\n        ngx_feature_incs=\"#include <zlib.h>\"\n        ngx_feature_path=\n        ngx_feature_libs=\"-lz\"\n        ngx_feature_test=\"z_stream z; deflate(&z, Z_NO_FLUSH)\"\n        . auto/feature\n\n\n        if [ $ngx_found = yes ]; then\n            CORE_LIBS=\"$CORE_LIBS $ngx_feature_libs\"\n            ZLIB=YES\n            ngx_found=no\n        fi\n    fi\n\n    if [ $ZLIB != YES ]; then\ncat << END\n\n$0: error: the HTTP gzip module requires the zlib library.\nYou can either disable the module by using --without-http_gzip_module\noption, or install the zlib library into the system, or build the zlib library\nstatically from the source with nginx by using --with-zlib=<path> option.\n\nEND\n        exit 1\n    fi\n\nfi\n"
  },
  {
    "path": "auto/lib/zlib/make",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\ncase \"$NGX_CC_NAME\" in\n\n    msvc)\n        ngx_makefile=makefile.msvc\n        ngx_opt=\"CPU_OPT=\\\"$CPU_OPT\\\" LIBC=$LIBC\"\n        ngx_zlib=\"ZLIB=\\\"$ZLIB\\\"\"\n\n    ;;\n\n    owc)\n        ngx_makefile=makefile.owc\n        ngx_opt=\"CPU_OPT=\\\"$CPU_OPT\\\"\"\n        ngx_zlib=`echo ZLIB=\\\"$ZLIB\\\" | sed -e \"s/\\//$ngx_regex_dirsep/g\"`\n    ;;\n\n    bcc)\n        ngx_makefile=makefile.bcc\n        ngx_opt=\"-DCPU_OPT=\\\"$CPU_OPT\\\"\"\n        ngx_zlib=`echo \\-DZLIB=\\\"$ZLIB\\\" | sed -e \"s/\\//$ngx_regex_dirsep/g\"`\n    ;;\n\n    *)\n        ngx_makefile=\n    ;;\n\nesac\n\n\ndone=NO\n\n\ncase \"$NGX_PLATFORM\" in\n\n    win32)\n\n        if [ -n \"$ngx_makefile\" ]; then\n            cat << END                                        >> $NGX_MAKEFILE\n\n`echo \"$ZLIB/zlib.lib:\t$NGX_MAKEFILE\" | sed -e \"s/\\//$ngx_regex_dirsep/g\"`\n\t\\$(MAKE) -f auto/lib/zlib/$ngx_makefile $ngx_opt $ngx_zlib\n\nEND\n\n        else\n\n            cat << END                                        >> $NGX_MAKEFILE\n\n$ZLIB/libz.a:\t$NGX_MAKEFILE\n\tcd $ZLIB \\\\\n\t&& \\$(MAKE) distclean \\\\\n\t&& \\$(MAKE) -f win32/Makefile.gcc \\\\\n\t\tCFLAGS=\"$ZLIB_OPT\" CC=\"\\$(CC)\" \\\\\n\t\tlibz.a\n\nEND\n\n        fi\n\n        done=YES\n    ;;\n\n    # FreeBSD: i386\n    # Linux: i686\n\n    *:i386 | *:i686)\n        case $ZLIB_ASM in\n            pentium)\n\n                cat << END                                    >> $NGX_MAKEFILE\n\n$ZLIB/libz.a:\t$NGX_MAKEFILE\n\tcd $ZLIB \\\\\n\t&& \\$(MAKE) distclean \\\\\n\t&& cp contrib/asm586/match.S . \\\\\n\t&& CFLAGS=\"$ZLIB_OPT -DASMV\" CC=\"\\$(CC)\" \\\\\n\t\t./configure \\\\\n\t&& \\$(MAKE) OBJA=match.o libz.a\n\nEND\n\n                done=YES\n            ;;\n\n            pentiumpro)\n\n                cat << END                                    >> $NGX_MAKEFILE\n\n$ZLIB/libz.a:\t$NGX_MAKEFILE\n\tcd $ZLIB \\\\\n\t&& \\$(MAKE) distclean \\\\\n\t&& cp contrib/asm686/match.S . \\\\\n\t&& CFLAGS=\"$ZLIB_OPT -DASMV\" CC=\"\\$(CC)\" \\\\\n\t\t./configure \\\\\n\t&& \\$(MAKE) OBJA=match.o libz.a\n\nEND\n\n                done=YES\n            ;;\n\n            NO)\n            ;;\n\n            *)\n                echo \"$0: error: invalid --with-zlib-asm=$ZLIB_ASM option.\"\n                echo \"The valid values are \\\"pentium\\\" and \\\"pentiumpro\\\" only\".\n                echo\n\n                exit 1;\n            ;;\n        esac\n    ;;\n\nesac\n\n\nif [ $done = NO ]; then\n\n    cat << END                                                >> $NGX_MAKEFILE\n\n$ZLIB/libz.a:\t$NGX_MAKEFILE\n\tcd $ZLIB \\\\\n\t&& \\$(MAKE) distclean \\\\\n\t&& CFLAGS=\"$ZLIB_OPT\" CC=\"\\$(CC)\" \\\\\n\t\t./configure \\\\\n\t&& \\$(MAKE) libz.a\n\nEND\n\nfi\n"
  },
  {
    "path": "auto/lib/zlib/makefile.bcc",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\nCFLAGS = -q -O2 -tWM -w-8004 -w-8012 $(CPU_OPT)\n\nzlib.lib:\n\tcd $(ZLIB)\n\n\tbcc32 -c $(CFLAGS) adler32.c crc32.c deflate.c \\\n\t\ttrees.c zutil.c compress.c \\\n\t\tinflate.c inffast.c inftrees.c\n\n\ttlib zlib.lib +adler32.obj +crc32.obj +deflate.obj \\\n\t\t+trees.obj +zutil.obj +compress.obj \\\n\t\t+inflate.obj +inffast.obj +inftrees.obj\n"
  },
  {
    "path": "auto/lib/zlib/makefile.msvc",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\nCFLAGS = -nologo -O2 -Ob1 -Oi -Gs $(LIBC) $(CPU_OPT)\n\nzlib.lib:\n\tcd $(ZLIB)\n\n\tcl -c $(CFLAGS) adler32.c crc32.c deflate.c \\\n\t\ttrees.c zutil.c compress.c \\\n\t\tinflate.c inffast.c inftrees.c\n\n\tlink -lib -out:zlib.lib adler32.obj crc32.obj deflate.obj \\\n\t\ttrees.obj zutil.obj compress.obj \\\n\t\tinflate.obj inffast.obj inftrees.obj\n"
  },
  {
    "path": "auto/lib/zlib/makefile.owc",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\nCFLAGS = -zq -bt=nt -ot -op -oi -oe -s -bm $(CPU_OPT)\n\nzlib.lib:\n\tcd $(ZLIB)\n\n\twcl386 -c $(CFLAGS) adler32.c crc32.c deflate.c trees.c zutil.c &\n\t\tcompress.c inflate.c inffast.c inftrees.c\n\twlib -n zlib.lib adler32.obj crc32.obj deflate.obj trees.obj &\n\t\tzutil.obj compress.obj inflate.obj inffast.obj inftrees.obj\n"
  },
  {
    "path": "auto/make",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\necho \"creating $NGX_MAKEFILE\"\n\nmkdir -p $NGX_OBJS/src/core $NGX_OBJS/src/event $NGX_OBJS/src/event/modules \\\n         $NGX_OBJS/src/os/unix $NGX_OBJS/src/os/win32 \\\n         $NGX_OBJS/src/http $NGX_OBJS/src/http/v2 $NGX_OBJS/src/http/modules \\\n         $NGX_OBJS/src/http/modules/perl \\\n         $NGX_OBJS/modules/ngx_http_lua_module/src \\\n         $NGX_OBJS/src/mail \\\n         $NGX_OBJS/src/stream \\\n         $NGX_OBJS/src/misc \\\n         $NGX_OBJS/modules/ngx_http_lua_module/src \\\n         $NGX_OBJS/src/proc \\\n         $NGX_OBJS/modules\n\n\nngx_objs_dir=$NGX_OBJS$ngx_regex_dirsep\nngx_use_pch=`echo $NGX_USE_PCH | sed -e \"s/\\//$ngx_regex_dirsep/g\"`\n\n\ncat << END                                                     > $NGX_MAKEFILE\n\nCC =\t$CC\nCFLAGS = $CFLAGS\nCPP =\t$CPP\nLINK =\t$LINK\n\nEND\n\n\nif test -n \"$NGX_PERL_CFLAGS\"; then\n    echo NGX_PERL_CFLAGS = $NGX_PERL_CFLAGS                   >> $NGX_MAKEFILE\n    echo NGX_PM_CFLAGS = $NGX_PM_CFLAGS                       >> $NGX_MAKEFILE\n    echo NGX_PM_LDFLAGS = $NGX_PM_LDFLAGS                     >> $NGX_MAKEFILE\nfi\n\n\n# ALL_INCS, required by the addons and by OpenWatcom C precompiled headers\n\nngx_incs=`echo $CORE_INCS $NGX_OBJS $HTTP_INCS $MAIL_INCS $STREAM_INCS\\\n    | sed -e \"s/  *\\([^ ][^ ]*\\)/$ngx_regex_cont$ngx_include_opt\\1/g\" \\\n          -e \"s/\\//$ngx_regex_dirsep/g\"`\n\ncat << END                                                    >> $NGX_MAKEFILE\n\nALL_INCS = $ngx_include_opt$ngx_incs\n\nEND\n\n\nngx_all_srcs=\"$CORE_SRCS\"\n\n\n# the core dependencies and include paths\n\nngx_deps=`echo $CORE_DEPS $NGX_AUTO_CONFIG_H $NGX_PCH \\\n    | sed -e \"s/  *\\([^ ][^ ]*\\)/$ngx_regex_cont\\1/g\" \\\n          -e \"s/\\//$ngx_regex_dirsep/g\"`\n\nngx_incs=`echo $CORE_INCS $NGX_OBJS \\\n    | sed -e \"s/  *\\([^ ][^ ]*\\)/$ngx_regex_cont$ngx_include_opt\\1/g\" \\\n          -e \"s/\\//$ngx_regex_dirsep/g\"`\n\ncat << END                                                    >> $NGX_MAKEFILE\n\nCORE_DEPS = $ngx_deps\n\n\nCORE_INCS = $ngx_include_opt$ngx_incs\n\nEND\n\n\n# the http dependencies and include paths\n\nif [ $HTTP = YES ]; then\n\n    ngx_all_srcs=\"$ngx_all_srcs $HTTP_SRCS\"\n\n    ngx_deps=`echo $HTTP_DEPS \\\n        | sed -e \"s/  *\\([^ ][^ ]*\\)/$ngx_regex_cont\\1/g\" \\\n              -e \"s/\\//$ngx_regex_dirsep/g\"`\n\n    ngx_incs=`echo $HTTP_INCS \\\n        | sed -e \"s/  *\\([^ ][^ ]*\\)/$ngx_regex_cont$ngx_include_opt\\1/g\" \\\n              -e \"s/\\//$ngx_regex_dirsep/g\"`\n\n    cat << END                                                >> $NGX_MAKEFILE\n\nHTTP_DEPS = $ngx_deps\n\n\nHTTP_INCS = $ngx_include_opt$ngx_incs\n\nEND\n\nfi\n\n\n# the mail dependencies and include paths\n\nif [ $MAIL != NO ]; then\n\n    if [ $MAIL = YES ]; then\n        ngx_all_srcs=\"$ngx_all_srcs $MAIL_SRCS\"\n    fi\n\n    ngx_deps=`echo $MAIL_DEPS \\\n        | sed -e \"s/  *\\([^ ][^ ]*\\)/$ngx_regex_cont\\1/g\" \\\n              -e \"s/\\//$ngx_regex_dirsep/g\"`\n\n    ngx_incs=`echo $MAIL_INCS \\\n        | sed -e \"s/  *\\([^ ][^ ]*\\)/$ngx_regex_cont$ngx_include_opt\\1/g\" \\\n              -e \"s/\\//$ngx_regex_dirsep/g\"`\n\n    cat << END                                                >> $NGX_MAKEFILE\n\nMAIL_DEPS = $ngx_deps\n\n\nMAIL_INCS = $ngx_include_opt$ngx_incs\n\nEND\n\nfi\n\n\n# the stream dependencies and include paths\n\nif [ $STREAM != NO ]; then\n\n    if [ $STREAM = YES ]; then\n        ngx_all_srcs=\"$ngx_all_srcs $STREAM_SRCS\"\n    fi\n\n    ngx_deps=`echo $STREAM_DEPS \\\n        | sed -e \"s/  *\\([^ ][^ ]*\\)/$ngx_regex_cont\\1/g\" \\\n              -e \"s/\\//$ngx_regex_dirsep/g\"`\n\n    ngx_incs=`echo $STREAM_INCS \\\n        | sed -e \"s/  *\\([^ ][^ ]*\\)/$ngx_regex_cont$ngx_include_opt\\1/g\" \\\n              -e \"s/\\//$ngx_regex_dirsep/g\"`\n\n    cat << END                                                >> $NGX_MAKEFILE\n\nSTREAM_DEPS = $ngx_deps\n\n\nSTREAM_INCS = $ngx_include_opt$ngx_incs\n\nEND\n\nfi\n\n\nngx_all_srcs=\"$ngx_all_srcs $MISC_SRCS\"\n\n\nif test -n \"$NGX_ADDON_SRCS$DYNAMIC_MODULES\"; then\n\ncat << END                                                >> $NGX_MAKEFILE\n\nADDON_DEPS = \\$(CORE_DEPS) $NGX_ADDON_DEPS\n\nEND\n\nfi\n\n\n# nginx\n\nngx_all_srcs=`echo $ngx_all_srcs | sed -e \"s/\\//$ngx_regex_dirsep/g\"`\n\nfor ngx_src in $NGX_ADDON_SRCS\ndo\n    ngx_obj=\"addon/`basename \\`dirname $ngx_src\\``\"\n\n    test -d $NGX_OBJS/$ngx_obj || mkdir -p $NGX_OBJS/$ngx_obj\n\n    ngx_obj=`echo $ngx_obj/\\`basename $ngx_src\\` \\\n        | sed -e \"s/\\//$ngx_regex_dirsep/g\"`\n\n    ngx_all_srcs=\"$ngx_all_srcs $ngx_obj\"\ndone\n\nngx_all_objs=`echo $ngx_all_srcs \\\n    | sed -e \"s#\\([^ ]*\\.\\)cpp#$NGX_OBJS\\/\\1$ngx_objext#g\" \\\n          -e \"s#\\([^ ]*\\.\\)cc#$NGX_OBJS\\/\\1$ngx_objext#g\" \\\n          -e \"s#\\([^ ]*\\.\\)c#$NGX_OBJS\\/\\1$ngx_objext#g\" \\\n          -e \"s#\\([^ ]*\\.\\)S#$NGX_OBJS\\/\\1$ngx_objext#g\"`\n\nngx_modules_c=`echo $NGX_MODULES_C | sed -e \"s/\\//$ngx_regex_dirsep/g\"`\n\nngx_modules_obj=`echo $ngx_modules_c | sed -e \"s/\\(.*\\.\\)c/\\1$ngx_objext/\"`\n\n\nif test -n \"$NGX_RES\"; then\n   ngx_res=$NGX_RES\nelse\n   ngx_res=\"$NGX_RC $NGX_ICONS\"\n   ngx_rcc=`echo $NGX_RCC | sed -e \"s/\\//$ngx_regex_dirsep/g\"`\nfi\n\nngx_deps=`echo $ngx_all_objs $ngx_modules_obj $ngx_res $LINK_DEPS \\\n    | sed -e \"s/  *\\([^ ][^ ]*\\)/$ngx_regex_cont\\1/g\" \\\n          -e \"s/\\//$ngx_regex_dirsep/g\"`\n\nngx_objs=`echo $ngx_all_objs $ngx_modules_obj \\\n    | sed -e \"s/  *\\([^ ][^ ]*\\)/$ngx_long_regex_cont\\1/g\" \\\n          -e \"s/\\//$ngx_regex_dirsep/g\"`\n\nngx_libs=\nif test -n \"$NGX_LD_OPT$CORE_LIBS\"; then\n    ngx_libs=`echo $NGX_LD_OPT $CORE_LIBS \\\n        | sed -e \"s/\\//$ngx_regex_dirsep/g\" -e \"s/^/$ngx_long_regex_cont/\"`\nfi\n\nngx_link=${CORE_LINK:+`echo $CORE_LINK \\\n    | sed -e \"s/\\//$ngx_regex_dirsep/g\" -e \"s/^/$ngx_long_regex_cont/\"`}\n\nngx_main_link=${MAIN_LINK:+`echo $MAIN_LINK \\\n    | sed -e \"s/\\//$ngx_regex_dirsep/g\" -e \"s/^/$ngx_long_regex_cont/\"`}\n\n\ncat << END                                                    >> $NGX_MAKEFILE\n\nbuild:\tbinary modules manpage\n\nbinary:\t$NGX_OBJS${ngx_dirsep}nginx$ngx_binext\n\n$NGX_OBJS${ngx_dirsep}nginx$ngx_binext:\t$ngx_deps$ngx_spacer\n\t\\$(LINK) $ngx_long_start$ngx_binout$NGX_OBJS${ngx_dirsep}nginx$ngx_binext$ngx_long_cont$ngx_objs$ngx_libs$ngx_link$ngx_main_link\n\t$ngx_rcc\n$ngx_long_end\n\nmodules:\nEND\n\n\n# ngx_modules.c\n\nif test -n \"$NGX_PCH\"; then\n    ngx_cc=\"\\$(CC) $ngx_compile_opt \\$(CFLAGS) $ngx_use_pch \\$(ALL_INCS)\"\nelse\n    ngx_cc=\"\\$(CC) $ngx_compile_opt \\$(CFLAGS) \\$(CORE_INCS)\"\nfi\n\ncat << END                                                    >> $NGX_MAKEFILE\n\n$ngx_modules_obj:\t\\$(CORE_DEPS)$ngx_cont$ngx_modules_c\n\t$ngx_cc$ngx_tab$ngx_objout$ngx_modules_obj$ngx_tab$ngx_modules_c$NGX_AUX\n\nEND\n\n\n# the core sources\n\nfor ngx_src in $CORE_SRCS\ndo\n    ngx_src=`echo $ngx_src | sed -e \"s/\\//$ngx_regex_dirsep/g\"`\n    ngx_obj=`echo $ngx_src \\\n        | sed -e \"s#^\\(.*\\.\\)cpp\\\\$#$ngx_objs_dir\\1$ngx_objext#g\" \\\n              -e \"s#^\\(.*\\.\\)cc\\\\$#$ngx_objs_dir\\1$ngx_objext#g\" \\\n              -e \"s#^\\(.*\\.\\)c\\\\$#$ngx_objs_dir\\1$ngx_objext#g\" \\\n              -e \"s#^\\(.*\\.\\)S\\\\$#$ngx_objs_dir\\1$ngx_objext#g\"`\n\n    cat << END                                                >> $NGX_MAKEFILE\n\n$ngx_obj:\t\\$(CORE_DEPS)$ngx_cont$ngx_src\n\t$ngx_cc$ngx_tab$ngx_objout$ngx_obj$ngx_tab$ngx_src$NGX_AUX\n\nEND\n\ndone\n\n\n# the http sources\n\nif [ $HTTP = YES ]; then\n\n    if test -n \"$NGX_PCH\"; then\n        ngx_cc=\"\\$(CC) $ngx_compile_opt \\$(CFLAGS) $ngx_use_pch \\$(ALL_INCS)\"\n    else\n        ngx_cc=\"\\$(CC) $ngx_compile_opt \\$(CFLAGS) \\$(CORE_INCS) \\$(HTTP_INCS)\"\n        ngx_perl_cc=\"\\$(CC) $ngx_compile_opt \\$(NGX_PERL_CFLAGS)\"\n        ngx_perl_cc=\"$ngx_perl_cc \\$(CORE_INCS) \\$(HTTP_INCS)\"\n    fi\n\n    for ngx_source in $HTTP_SRCS\n    do\n        ngx_src=`echo $ngx_source | sed -e \"s/\\//$ngx_regex_dirsep/g\"`\n        ngx_obj=`echo $ngx_src \\\n            | sed -e \"s#^\\(.*\\.\\)cpp\\\\$#$ngx_objs_dir\\1$ngx_objext#g\" \\\n                  -e \"s#^\\(.*\\.\\)cc\\\\$#$ngx_objs_dir\\1$ngx_objext#g\" \\\n                  -e \"s#^\\(.*\\.\\)c\\\\$#$ngx_objs_dir\\1$ngx_objext#g\" \\\n                  -e \"s#^\\(.*\\.\\)S\\\\$#$ngx_objs_dir\\1$ngx_objext#g\"`\n\n        if [ $ngx_source = src/http/modules/perl/ngx_http_perl_module.c ]; then\n\n            cat << END                                        >> $NGX_MAKEFILE\n\n$ngx_obj:\t\\$(CORE_DEPS) \\$(HTTP_DEPS)$ngx_cont$ngx_src\n\t$ngx_perl_cc$ngx_tab$ngx_objout$ngx_obj$ngx_tab$ngx_src$NGX_AUX\n\nEND\n        else\n\n            cat << END                                        >> $NGX_MAKEFILE\n\n$ngx_obj:\t\\$(CORE_DEPS) \\$(HTTP_DEPS)$ngx_cont$ngx_src\n\t$ngx_cc$ngx_tab$ngx_objout$ngx_obj$ngx_tab$ngx_src$NGX_AUX\n\nEND\n\n        fi\n    done\n\nfi\n\n\n# the mail sources\n\nif [ $MAIL = YES ]; then\n\n    if test -n \"$NGX_PCH\"; then\n        ngx_cc=\"\\$(CC) $ngx_compile_opt \\$(CFLAGS) $ngx_use_pch \\$(ALL_INCS)\"\n    else\n        ngx_cc=\"\\$(CC) $ngx_compile_opt \\$(CFLAGS) \\$(CORE_INCS) \\$(MAIL_INCS)\"\n    fi\n\n    for ngx_src in $MAIL_SRCS\n    do\n        ngx_src=`echo $ngx_src | sed -e \"s/\\//$ngx_regex_dirsep/g\"`\n        ngx_obj=`echo $ngx_src \\\n            | sed -e \"s#^\\(.*\\.\\)cpp\\\\$#$ngx_objs_dir\\1$ngx_objext#g\" \\\n                  -e \"s#^\\(.*\\.\\)cc\\\\$#$ngx_objs_dir\\1$ngx_objext#g\" \\\n                  -e \"s#^\\(.*\\.\\)c\\\\$#$ngx_objs_dir\\1$ngx_objext#g\" \\\n                  -e \"s#^\\(.*\\.\\)S\\\\$#$ngx_objs_dir\\1$ngx_objext#g\"`\n\n        cat << END                                            >> $NGX_MAKEFILE\n\n$ngx_obj:\t\\$(CORE_DEPS) \\$(MAIL_DEPS)$ngx_cont$ngx_src\n\t$ngx_cc$ngx_tab$ngx_objout$ngx_obj$ngx_tab$ngx_src$NGX_AUX\n\nEND\n    done\n\nfi\n\n\n# the stream sources\n\nif [ $STREAM = YES ]; then\n\n    if test -n \"$NGX_PCH\"; then\n        ngx_cc=\"\\$(CC) $ngx_compile_opt \\$(CFLAGS) $ngx_use_pch \\$(ALL_INCS)\"\n    else\n        ngx_cc=\"\\$(CC) $ngx_compile_opt \\$(CFLAGS) \\$(CORE_INCS) \\$(STREAM_INCS)\"\n    fi\n\n    for ngx_src in $STREAM_SRCS\n    do\n        ngx_src=`echo $ngx_src | sed -e \"s/\\//$ngx_regex_dirsep/g\"`\n        ngx_obj=`echo $ngx_src \\\n            | sed -e \"s#^\\(.*\\.\\)cpp\\\\$#$ngx_objs_dir\\1$ngx_objext#g\" \\\n                  -e \"s#^\\(.*\\.\\)cc\\\\$#$ngx_objs_dir\\1$ngx_objext#g\" \\\n                  -e \"s#^\\(.*\\.\\)c\\\\$#$ngx_objs_dir\\1$ngx_objext#g\" \\\n                  -e \"s#^\\(.*\\.\\)S\\\\$#$ngx_objs_dir\\1$ngx_objext#g\"`\n\n        cat << END                                            >> $NGX_MAKEFILE\n\n$ngx_obj:\t\\$(CORE_DEPS) \\$(STREAM_DEPS)$ngx_cont$ngx_src\n\t$ngx_cc$ngx_tab$ngx_objout$ngx_obj$ngx_tab$ngx_src$NGX_AUX\n\nEND\n    done\n\nfi\n\n\n# the misc sources\n\nif test -n \"$MISC_SRCS\"; then\n\n    ngx_cc=\"\\$(CC) $ngx_compile_opt \\$(CFLAGS) $ngx_use_pch \\$(ALL_INCS)\"\n\n    for ngx_src in $MISC_SRCS\n    do\n        ngx_src=`echo $ngx_src | sed -e \"s/\\//$ngx_regex_dirsep/g\"`\n        ngx_obj=`echo $ngx_src \\\n            | sed -e \"s#^\\(.*\\.\\)cpp\\\\$#$ngx_objs_dir\\1$ngx_objext#g\" \\\n                  -e \"s#^\\(.*\\.\\)cc\\\\$#$ngx_objs_dir\\1$ngx_objext#g\" \\\n                  -e \"s#^\\(.*\\.\\)c\\\\$#$ngx_objs_dir\\1$ngx_objext#g\" \\\n                  -e \"s#^\\(.*\\.\\)S\\\\$#$ngx_objs_dir\\1$ngx_objext#g\"`\n\n        cat << END                                            >> $NGX_MAKEFILE\n\n$ngx_obj:\t\\$(CORE_DEPS) $ngx_cont$ngx_src\n\t$ngx_cc$ngx_tab$ngx_objout$ngx_obj$ngx_tab$ngx_src$NGX_AUX\n\nEND\n    done\n\nfi\n\n\n# the addons sources\n\nif test -n \"$NGX_ADDON_SRCS\"; then\n\n    ngx_cc=\"\\$(CC) $ngx_compile_opt \\$(CFLAGS) $ngx_use_pch \\$(ALL_INCS)\"\n\n    for ngx_src in $NGX_ADDON_SRCS\n    do\n        ngx_obj=\"addon/`basename \\`dirname $ngx_src\\``\"\n\n        ngx_obj=`echo $ngx_obj/\\`basename $ngx_src\\` \\\n            | sed -e \"s/\\//$ngx_regex_dirsep/g\"`\n\n        ngx_obj=`echo $ngx_obj \\\n            | sed -e \"s#^\\(.*\\.\\)cpp\\\\$#$ngx_objs_dir\\1$ngx_objext#g\" \\\n                  -e \"s#^\\(.*\\.\\)cc\\\\$#$ngx_objs_dir\\1$ngx_objext#g\" \\\n                  -e \"s#^\\(.*\\.\\)c\\\\$#$ngx_objs_dir\\1$ngx_objext#g\" \\\n                  -e \"s#^\\(.*\\.\\)S\\\\$#$ngx_objs_dir\\1$ngx_objext#g\"`\n\n        ngx_src=`echo $ngx_src | sed -e \"s/\\//$ngx_regex_dirsep/g\"`\n\n        cat << END                                            >> $NGX_MAKEFILE\n\n$ngx_obj:\t\\$(ADDON_DEPS)$ngx_cont$ngx_src\n\t$ngx_cc$ngx_tab$ngx_objout$ngx_obj$ngx_tab$ngx_src$NGX_AUX\n\nEND\n    done\n\nfi\n\n\n# the addons config.make\n\nif test -n \"$NGX_ADDONS$DYNAMIC_ADDONS\"; then\n\n    for ngx_addon_dir in $NGX_ADDONS $DYNAMIC_ADDONS\n    do\n        if test -f $ngx_addon_dir/config.make; then\n            . $ngx_addon_dir/config.make\n        fi\n    done\nfi\n\n\n# Win32 resource file\n\nif test -n \"$NGX_RES\"; then\n\n    ngx_res=`echo \"$NGX_RES:\t$NGX_RC $NGX_ICONS\" \\\n                 | sed -e \"s/\\//$ngx_regex_dirsep/g\"`\n    ngx_rcc=`echo $NGX_RCC | sed -e \"s/\\//$ngx_regex_dirsep/g\"`\n\n    cat << END                                                >> $NGX_MAKEFILE\n\n$ngx_res\n\t$ngx_rcc\n\nEND\n\nfi\n\n\n# the precompiled headers\n\nif test -n \"$NGX_PCH\"; then\n    echo \"#include <ngx_config.h>\" > $NGX_OBJS/ngx_pch.c\n\n    ngx_pch=\"src/core/ngx_config.h $OS_CONFIG $NGX_OBJS/ngx_auto_config.h\"\n    ngx_pch=`echo \"$NGX_PCH:\t$ngx_pch\" | sed -e \"s/\\//$ngx_regex_dirsep/g\"`\n\n    ngx_src=\"\\$(CC) \\$(CFLAGS) $NGX_BUILD_PCH $ngx_compile_opt \\$(ALL_INCS)\"\n    ngx_src=\"$ngx_src $ngx_objout$NGX_OBJS/ngx_pch.obj $NGX_OBJS/ngx_pch.c\"\n    ngx_src=`echo $ngx_src | sed -e \"s/\\//$ngx_regex_dirsep/g\"`\n\n    cat << END                                                >> $NGX_MAKEFILE\n\n$ngx_pch\n\t$ngx_src\n\nEND\n\nfi\n\n\n# dynamic modules\n\nif test -n \"$NGX_PCH\"; then\n    ngx_cc=\"\\$(CC) $ngx_compile_opt $ngx_pic_opt \\$(CFLAGS) $ngx_use_pch \\$(ALL_INCS)\"\nelse\n    ngx_cc=\"\\$(CC) $ngx_compile_opt $ngx_pic_opt \\$(CFLAGS) \\$(ALL_INCS)\"\n    ngx_perl_cc=\"\\$(CC) $ngx_compile_opt $ngx_pic_opt \\$(NGX_PERL_CFLAGS)\"\n    ngx_perl_cc=\"$ngx_perl_cc \\$(ALL_INCS)\"\nfi\n\nfor ngx_module in $DYNAMIC_MODULES\ndo\n    eval ngx_module_srcs=\"\\$${ngx_module}_SRCS\"\n    eval ngx_module_shrd=\"\\$${ngx_module}_SHRD\"\n    eval eval ngx_module_libs=\"\\\\\\\"\\$${ngx_module}_LIBS\\\\\\\"\"\n\n    eval ngx_module_modules=\"\\$${ngx_module}_MODULES\"\n    eval ngx_module_order=\"\\$${ngx_module}_ORDER\"\n\n    ngx_modules_c=$NGX_OBJS/${ngx_module}_modules.c\n\n    cat << END                                    > $ngx_modules_c\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\nEND\n\n    for mod in $ngx_module_modules\n    do\n        echo \"extern ngx_module_t  $mod;\"         >> $ngx_modules_c\n    done\n\n    echo                                          >> $ngx_modules_c\n    echo 'ngx_module_t *ngx_modules[] = {'        >> $ngx_modules_c\n\n    for mod in $ngx_module_modules\n    do\n        echo \"    &$mod,\"                         >> $ngx_modules_c\n    done\n\n    cat << END                                    >> $ngx_modules_c\n    NULL\n};\n\nEND\n\n    echo 'char *ngx_module_names[] = {'           >> $ngx_modules_c\n\n    for mod in $ngx_module_modules\n    do\n        echo \"    \\\"$mod\\\",\"                      >> $ngx_modules_c\n    done\n\n    cat << END                                    >> $ngx_modules_c\n    NULL\n};\n\nEND\n\n    echo 'char *ngx_module_order[] = {'           >> $ngx_modules_c\n\n    for mod in $ngx_module_order\n    do\n        echo \"    \\\"$mod\\\",\"                      >> $ngx_modules_c\n    done\n\n    cat << END                                    >> $ngx_modules_c\n    NULL\n};\n\nEND\n\n    ngx_modules_c=`echo $ngx_modules_c | sed -e \"s/\\//$ngx_regex_dirsep/g\"`\n\n    ngx_modules_obj=`echo $ngx_modules_c \\\n        | sed -e \"s/\\(.*\\.\\)c/\\1$ngx_objext/\"`\n\n    ngx_module_objs=\n    for ngx_src in $ngx_module_srcs $ngx_module_shrd\n    do\n        case \"$ngx_src\" in\n            src/*)\n                ngx_obj=$ngx_src\n                ;;\n            *)\n                ngx_obj=\"addon/`basename \\`dirname $ngx_src\\``\"\n                mkdir -p $NGX_OBJS/$ngx_obj\n                ngx_obj=\"$ngx_obj/`basename $ngx_src`\"\n                ;;\n        esac\n\n        ngx_module_objs=\"$ngx_module_objs $ngx_obj\"\n    done\n\n    ngx_module_objs=`echo $ngx_module_objs \\\n        | sed -e \"s#\\([^ ]*\\.\\)cpp#$NGX_OBJS\\/\\1$ngx_objext#g\" \\\n              -e \"s#\\([^ ]*\\.\\)cc#$NGX_OBJS\\/\\1$ngx_objext#g\" \\\n              -e \"s#\\([^ ]*\\.\\)c#$NGX_OBJS\\/\\1$ngx_objext#g\" \\\n              -e \"s#\\([^ ]*\\.\\)S#$NGX_OBJS\\/\\1$ngx_objext#g\"`\n\n    ngx_deps=`echo $ngx_module_objs $ngx_modules_obj $LINK_DEPS \\\n        | sed -e \"s/  *\\([^ ][^ ]*\\)/$ngx_regex_cont\\1/g\" \\\n              -e \"s/\\//$ngx_regex_dirsep/g\"`\n\n    ngx_objs=`echo $ngx_module_objs $ngx_modules_obj \\\n        | sed -e \"s/  *\\([^ ][^ ]*\\)/$ngx_long_regex_cont\\1/g\" \\\n              -e \"s/\\//$ngx_regex_dirsep/g\"`\n\n    ngx_obj=$NGX_OBJS$ngx_dirsep$ngx_module$ngx_modext\n\n    if [ \"$NGX_PLATFORM\" = win32 ]; then\n        ngx_module_libs=\"$CORE_LIBS $ngx_module_libs\"\n    fi\n\n    ngx_libs=\n    if test -n \"$NGX_LD_OPT$ngx_module_libs\"; then\n        ngx_libs=`echo $NGX_LD_OPT $ngx_module_libs \\\n            | sed -e \"s/\\//$ngx_regex_dirsep/g\" -e \"s/^/$ngx_long_regex_cont/\"`\n    fi\n\n    ngx_link=${CORE_LINK:+`echo $CORE_LINK \\\n        | sed -e \"s/\\//$ngx_regex_dirsep/g\" -e \"s/^/$ngx_long_regex_cont/\"`}\n\n    ngx_module_link=${MODULE_LINK:+`echo $MODULE_LINK \\\n        | sed -e \"s/\\//$ngx_regex_dirsep/g\" -e \"s/^/$ngx_long_regex_cont/\"`}\n\n\n    cat << END                                            >> $NGX_MAKEFILE\n\nmodules:\t$ngx_obj\n\n$ngx_obj:\t$ngx_deps$ngx_spacer\n\t\\$(LINK) $ngx_long_start$ngx_binout$ngx_obj$ngx_long_cont$ngx_objs$ngx_libs$ngx_link$ngx_module_link\n$ngx_long_end\n\n$ngx_modules_obj:\t\\$(CORE_DEPS)$ngx_cont$ngx_modules_c\n\t$ngx_cc$ngx_tab$ngx_objout$ngx_modules_obj$ngx_tab$ngx_modules_c$NGX_AUX\n\nEND\n\n    for ngx_source in $ngx_module_srcs\n    do\n        case \"$ngx_source\" in\n            src/*)\n                ngx_obj=`echo $ngx_source | sed -e \"s/\\//$ngx_regex_dirsep/g\"`\n                ;;\n            *)\n                ngx_obj=\"addon/`basename \\`dirname $ngx_source\\``\"\n                ngx_obj=`echo $ngx_obj/\\`basename $ngx_source\\` \\\n                    | sed -e \"s/\\//$ngx_regex_dirsep/g\"`\n                ;;\n        esac\n\n        ngx_obj=`echo $ngx_obj \\\n            | sed -e \"s#^\\(.*\\.\\)cpp\\\\$#$ngx_objs_dir\\1$ngx_objext#g\" \\\n                  -e \"s#^\\(.*\\.\\)cc\\\\$#$ngx_objs_dir\\1$ngx_objext#g\" \\\n                  -e \"s#^\\(.*\\.\\)c\\\\$#$ngx_objs_dir\\1$ngx_objext#g\" \\\n                  -e \"s#^\\(.*\\.\\)S\\\\$#$ngx_objs_dir\\1$ngx_objext#g\"`\n\n        ngx_src=`echo $ngx_source | sed -e \"s/\\//$ngx_regex_dirsep/g\"`\n\n        if [ $ngx_source = src/http/modules/perl/ngx_http_perl_module.c ]; then\n\n            cat << END                                        >> $NGX_MAKEFILE\n\n$ngx_obj:\t\\$(ADDON_DEPS)$ngx_cont$ngx_src\n\t$ngx_perl_cc$ngx_tab$ngx_objout$ngx_obj$ngx_tab$ngx_src$NGX_AUX\n\nEND\n        else\n\n            cat << END                                        >> $NGX_MAKEFILE\n\n$ngx_obj:\t\\$(ADDON_DEPS)$ngx_cont$ngx_src\n\t$ngx_cc$ngx_tab$ngx_objout$ngx_obj$ngx_tab$ngx_src$NGX_AUX\n\nEND\n\n        fi\n    done\ndone\n"
  },
  {
    "path": "auto/module",
    "content": "\n# Copyright (C) Ruslan Ermilov\n# Copyright (C) Nginx, Inc.\n\n\ncase $ngx_module_type in\n    HTTP_*) ngx_var=HTTP ;;\n    *)      ngx_var=$ngx_module_type ;;\nesac\n\n\nif [ \"$ngx_module_link\" = DYNAMIC ]; then\n\n    for ngx_module in $ngx_module_name; do\n        # extract the first name\n        break\n    done\n\n    DYNAMIC_MODULES=\"$DYNAMIC_MODULES $ngx_module\"\n\n    eval ${ngx_module}_MODULES=\\\"$ngx_module_name\\\"\n\n    if [ -z \"$ngx_module_order\" -a \\\n         \\( \"$ngx_module_type\" = \"HTTP_FILTER\" \\\n         -o \"$ngx_module_type\" = \"HTTP_AUX_FILTER\" \\) ]\n    then\n        eval ${ngx_module}_ORDER=\\\"$ngx_module_name \\\n                                   ngx_http_copy_filter_module\\\"\n    else\n        eval ${ngx_module}_ORDER=\\\"$ngx_module_order\\\"\n    fi\n\n    srcs=\n    shrd=\n    for src in $ngx_module_srcs\n    do\n        found=no\n        for old in $DYNAMIC_MODULES_SRCS\n        do\n            if [ $src = $old ]; then\n                found=yes\n                break\n            fi\n        done\n\n        if [ $found = no ]; then\n            srcs=\"$srcs $src\"\n        else\n            shrd=\"$shrd $src\"\n        fi\n    done\n    eval ${ngx_module}_SRCS=\\\"$srcs\\\"\n    eval ${ngx_module}_SHRD=\\\"$shrd\\\"\n\n    DYNAMIC_MODULES_SRCS=\"$DYNAMIC_MODULES_SRCS $srcs\"\n\n    if test -n \"$ngx_module_incs\"; then\n        CORE_INCS=\"$CORE_INCS $ngx_module_incs\"\n    fi\n\n    if test -n \"$ngx_module_deps\"; then\n        NGX_ADDON_DEPS=\"$NGX_ADDON_DEPS $ngx_module_deps\"\n    fi\n\n    libs=\n    for lib in $ngx_module_libs\n    do\n        case $lib in\n\n            LIBXSLT | LIBGD | GEOIP | PERL)\n                libs=\"$libs \\$NGX_LIB_$lib\"\n\n                if eval [ \"\\$USE_${lib}\" = NO ] ; then\n                    eval USE_${lib}=DYNAMIC\n                fi\n            ;;\n\n            PCRE | OPENSSL | ZLIB)\n                eval USE_${lib}=YES\n            ;;\n\n            MD5 | SHA1)\n                # obsolete\n            ;;\n\n            *)\n                libs=\"$libs $lib\"\n            ;;\n\n        esac\n    done\n    eval ${ngx_module}_LIBS=\\'$libs\\'\n\nelif [ \"$ngx_module_link\" = YES ]; then\n\n    eval ${ngx_module_type}_MODULES=\\\"\\$${ngx_module_type}_MODULES \\\n                                      $ngx_module_name\\\"\n\n    eval ${ngx_var}_SRCS=\\\"\\$${ngx_var}_SRCS $ngx_module_srcs\\\"\n\n    if test -n \"$ngx_module_incs\"; then\n        eval ${ngx_var}_INCS=\\\"\\$${ngx_var}_INCS $ngx_module_incs\\\"\n    fi\n\n    if test -n \"$ngx_module_deps\"; then\n        eval ${ngx_var}_DEPS=\\\"\\$${ngx_var}_DEPS $ngx_module_deps\\\"\n    fi\n\n    for lib in $ngx_module_libs\n    do\n        case $lib in\n\n            PCRE | OPENSSL | ZLIB | LIBXSLT | LIBGD | PERL | GEOIP)\n                eval USE_${lib}=YES\n            ;;\n\n            MD5 | SHA1)\n                # obsolete\n            ;;\n\n            *)\n                CORE_LIBS=\"$CORE_LIBS $lib\"\n            ;;\n\n        esac\n    done\n\nelif [ \"$ngx_module_link\" = ADDON ]; then\n\n    eval ${ngx_module_type}_MODULES=\\\"\\$${ngx_module_type}_MODULES \\\n                                      $ngx_module_name\\\"\n\n    srcs=\n    for src in $ngx_module_srcs\n    do\n        found=no\n        for old in $NGX_ADDON_SRCS\n        do\n            if [ $src = $old ]; then\n                found=yes\n                break\n            fi\n        done\n\n        if [ $found = no ]; then\n            srcs=\"$srcs $src\"\n        fi\n    done\n\n    NGX_ADDON_SRCS=\"$NGX_ADDON_SRCS $srcs\"\n\n    if test -n \"$ngx_module_incs\"; then\n        eval ${ngx_var}_INCS=\\\"\\$${ngx_var}_INCS $ngx_module_incs\\\"\n    fi\n\n    if test -n \"$ngx_module_deps\"; then\n        NGX_ADDON_DEPS=\"$NGX_ADDON_DEPS $ngx_module_deps\"\n    fi\n\n    for lib in $ngx_module_libs\n    do\n        case $lib in\n\n            PCRE | OPENSSL | ZLIB | LIBXSLT | LIBGD | PERL | GEOIP)\n                eval USE_${lib}=YES\n            ;;\n\n            MD5 | SHA1)\n                # obsolete\n            ;;\n\n            *)\n                CORE_LIBS=\"$CORE_LIBS $lib\"\n            ;;\n\n        esac\n    done\nfi\n"
  },
  {
    "path": "auto/modules",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\nif [ $EVENT_SELECT = NO -a $EVENT_FOUND = NO ]; then\n    EVENT_SELECT=YES\nfi\n\nif [ $EVENT_SELECT = YES ]; then\n    have=NGX_HAVE_SELECT . auto/have\n    CORE_SRCS=\"$CORE_SRCS $SELECT_SRCS\"\n    EVENT_MODULES=\"$EVENT_MODULES $SELECT_MODULE\"\nfi\n\n\nif [ $EVENT_POLL = NO -a $EVENT_FOUND = NO ]; then\n    EVENT_POLL=YES\nfi\n\nif [ $EVENT_POLL = YES ]; then\n    have=NGX_HAVE_POLL . auto/have\n    CORE_SRCS=\"$CORE_SRCS $POLL_SRCS\"\n    EVENT_MODULES=\"$EVENT_MODULES $POLL_MODULE\"\nfi\n\n\nif [ $NGX_TEST_BUILD_DEVPOLL = YES ]; then\n    have=NGX_HAVE_DEVPOLL . auto/have\n    have=NGX_TEST_BUILD_DEVPOLL . auto/have\n    EVENT_MODULES=\"$EVENT_MODULES $DEVPOLL_MODULE\"\n    CORE_SRCS=\"$CORE_SRCS $DEVPOLL_SRCS\"\nfi\n\n\nif [ $NGX_TEST_BUILD_EVENTPORT = YES ]; then\n    have=NGX_HAVE_EVENTPORT . auto/have\n    have=NGX_TEST_BUILD_EVENTPORT . auto/have\n    EVENT_MODULES=\"$EVENT_MODULES $EVENTPORT_MODULE\"\n    CORE_SRCS=\"$CORE_SRCS $EVENTPORT_SRCS\"\nfi\n\nif [ $NGX_TEST_BUILD_EPOLL = YES ]; then\n    have=NGX_HAVE_EPOLL . auto/have\n    have=NGX_HAVE_EPOLLRDHUP . auto/have\n    have=NGX_HAVE_EPOLLEXCLUSIVE . auto/have\n    have=NGX_HAVE_EVENTFD . auto/have\n    have=NGX_TEST_BUILD_EPOLL . auto/have\n    EVENT_MODULES=\"$EVENT_MODULES $EPOLL_MODULE\"\n    CORE_SRCS=\"$CORE_SRCS $EPOLL_SRCS\"\nfi\n\nif [ $NGX_TEST_BUILD_SOLARIS_SENDFILEV = YES ]; then\n    have=NGX_TEST_BUILD_SOLARIS_SENDFILEV . auto/have\n    CORE_SRCS=\"$CORE_SRCS $SOLARIS_SENDFILEV_SRCS\"\nfi\n\n\n# add proc\nif [ $PROCS = YES ]; then\n    have=NGX_PROCS . auto/have\n    CORE_SRCS=\"$CORE_SRCS $PROCS_SRCS\"\n    CORE_DEPS=\"$CORE_DEPS $PROCS_DEPS\"\n    CORE_INCS=\"$CORE_INCS $PROCS_INCS\"\nelse\n    PROCS_MODULES=\nfi\n\n\nif [ $HTTP = YES ]; then\n    HTTP_MODULES=\n    HTTP_DEPS=\n    HTTP_INCS=\n\n    ngx_module_type=HTTP\n\n    if :; then\n        ngx_module_name=\"ngx_http_module \\\n                         ngx_http_core_module \\\n                         ngx_http_log_module \\\n                         ngx_http_upstream_module\"\n        ngx_module_incs=\"src/http src/http/modules\"\n        ngx_module_deps=\"src/http/ngx_http.h \\\n                         src/http/ngx_http_request.h \\\n                         src/http/ngx_http_config.h \\\n                         src/http/ngx_http_core_module.h \\\n                         src/http/ngx_http_cache.h \\\n                         src/http/ngx_http_variables.h \\\n                         src/http/ngx_http_script.h \\\n                         src/http/ngx_http_upstream.h \\\n                         src/http/ngx_http_upstream_round_robin.h\"\n        ngx_module_srcs=\"src/http/ngx_http.c \\\n                         src/http/ngx_http_core_module.c \\\n                         src/http/ngx_http_special_response.c \\\n                         src/http/ngx_http_request.c \\\n                         src/http/ngx_http_parse.c \\\n                         src/http/modules/ngx_http_log_module.c \\\n                         src/http/ngx_http_request_body.c \\\n                         src/http/ngx_http_variables.c \\\n                         src/http/ngx_http_script.c \\\n                         src/http/ngx_http_upstream.c \\\n                         src/http/ngx_http_upstream_round_robin.c\"\n        ngx_module_libs=\n        ngx_module_link=YES\n\n        . auto/module\n    fi\n\n\n    if [ $HTTP_CACHE = YES ]; then\n        have=NGX_HTTP_CACHE . auto/have\n        HTTP_SRCS=\"$HTTP_SRCS $HTTP_FILE_CACHE_SRCS\"\n    fi\n\n\n    if [ $HTTP_V2 = YES ]; then\n        HTTP_SRCS=\"$HTTP_SRCS $HTTP_HUFF_SRCS\"\n    fi\n\n\n    # the module order is important\n    #     ngx_http_static_module\n    #     ngx_http_gzip_static_module\n    #     ngx_http_dav_module\n    #     ngx_http_autoindex_module\n    #     ngx_http_index_module\n    #     ngx_http_random_index_module\n    #\n    #     ngx_http_access_module\n    #     ngx_http_realip_module\n    #\n    #\n    # the filter order is important\n    #     ngx_http_write_filter\n    #     ngx_http_header_filter\n    #     ngx_http_chunked_filter\n    #     ngx_http_v2_filter\n    #     ngx_http_range_header_filter\n    #     ngx_http_gzip_filter\n    #     ngx_http_postpone_filter\n    #     ngx_http_ssi_filter\n    #     ngx_http_charset_filter\n    #         ngx_http_xslt_filter\n    #         ngx_http_image_filter\n    #         ngx_http_sub_filter\n    #         ngx_http_addition_filter\n    #         ngx_http_gunzip_filter\n    #         ngx_http_userid_filter\n    #         ngx_http_headers_filter\n    #     ngx_http_copy_filter\n    #     ngx_http_range_body_filter\n    #     ngx_http_not_modified_filter\n    #     ngx_http_slice_filter\n\n    ngx_module_type=HTTP_FILTER\n    HTTP_FILTER_MODULES=\n\n    ngx_module_order=\"ngx_http_static_module \\\n                      ngx_http_gzip_static_module \\\n                      ngx_http_dav_module \\\n                      ngx_http_autoindex_module \\\n                      ngx_http_index_module \\\n                      ngx_http_random_index_module \\\n                      ngx_http_access_module \\\n                      ngx_http_realip_module \\\n                      ngx_http_write_filter_module \\\n                      ngx_http_header_filter_module \\\n                      ngx_http_chunked_filter_module \\\n                      ngx_http_v2_filter_module \\\n                      ngx_http_range_header_filter_module \\\n                      ngx_http_gzip_filter_module \\\n                      ngx_http_postpone_filter_module \\\n                      ngx_http_ssi_filter_module \\\n                      ngx_http_charset_filter_module \\\n                      ngx_http_xslt_filter_module \\\n                      ngx_http_image_filter_module \\\n                      ngx_http_sub_filter_module \\\n                      ngx_http_addition_filter_module \\\n                      ngx_http_gunzip_filter_module \\\n                      ngx_http_userid_filter_module \\\n                      ngx_http_headers_filter_module \\\n                      ngx_http_copy_filter_module \\\n                      ngx_http_range_body_filter_module \\\n                      ngx_http_not_modified_filter_module \\\n                      ngx_http_slice_filter_module\"\n\n    if :; then\n        ngx_module_name=ngx_http_write_filter_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/ngx_http_write_filter_module.c\n        ngx_module_libs=\n        ngx_module_link=YES\n\n        . auto/module\n    fi\n\n    if :; then\n        ngx_module_name=ngx_http_header_filter_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/ngx_http_header_filter_module.c\n        ngx_module_libs=\n        ngx_module_link=YES\n\n        . auto/module\n    fi\n\n    if :; then\n        ngx_module_name=ngx_http_chunked_filter_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/modules/ngx_http_chunked_filter_module.c\n        ngx_module_libs=\n        ngx_module_link=YES\n\n        . auto/module\n    fi\n\n    if [ $HTTP_V2 = YES ]; then\n        ngx_module_name=ngx_http_v2_filter_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/v2/ngx_http_v2_filter_module.c\n        ngx_module_libs=\n        ngx_module_link=$HTTP_V2\n\n        . auto/module\n    fi\n\n    if :; then\n        ngx_module_name=ngx_http_range_header_filter_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/modules/ngx_http_range_filter_module.c\n        ngx_module_libs=\n        ngx_module_link=YES\n\n        . auto/module\n    fi\n\n    if [ $HTTP_GZIP = YES ]; then\n        have=NGX_HTTP_GZIP . auto/have\n        USE_ZLIB=YES\n\n        ngx_module_name=ngx_http_gzip_filter_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/modules/ngx_http_gzip_filter_module.c\n        ngx_module_libs=\n        ngx_module_link=$HTTP_GZIP\n\n        . auto/module\n    fi\n\n    if :; then\n        ngx_module_name=ngx_http_postpone_filter_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/ngx_http_postpone_filter_module.c\n        ngx_module_libs=\n        ngx_module_link=YES\n\n        . auto/module\n    fi\n\n    if [ $HTTP_SSI = YES ]; then\n        have=NGX_HTTP_SSI . auto/have\n\n        ngx_module_name=ngx_http_ssi_filter_module\n        ngx_module_incs=\n        ngx_module_deps=src/http/modules/ngx_http_ssi_filter_module.h\n        ngx_module_srcs=src/http/modules/ngx_http_ssi_filter_module.c\n        ngx_module_libs=\n        ngx_module_link=$HTTP_SSI\n\n        . auto/module\n    fi\n\n    if [ $HTTP_CHARSET = YES ]; then\n        ngx_module_name=ngx_http_charset_filter_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/modules/ngx_http_charset_filter_module.c\n        ngx_module_libs=\n        ngx_module_link=$HTTP_CHARSET\n\n        . auto/module\n    fi\n\n    if [ $HTTP_XSLT != NO ]; then\n        ngx_module_name=ngx_http_xslt_filter_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/modules/ngx_http_xslt_filter_module.c\n        ngx_module_libs=LIBXSLT\n        ngx_module_link=$HTTP_XSLT\n\n        . auto/module\n    fi\n\n    if [ $HTTP_IMAGE_FILTER != NO ]; then\n        ngx_module_name=ngx_http_image_filter_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/modules/ngx_http_image_filter_module.c\n        ngx_module_libs=LIBGD\n        ngx_module_link=$HTTP_IMAGE_FILTER\n\n        . auto/module\n    fi\n\n    if [ $HTTP_SUB = YES ]; then\n        ngx_module_name=ngx_http_sub_filter_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/modules/ngx_http_sub_filter_module.c\n        ngx_module_libs=\n        ngx_module_link=$HTTP_SUB\n\n        . auto/module\n    fi\n\n    if [ $HTTP_ADDITION = YES ]; then\n        ngx_module_name=ngx_http_addition_filter_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/modules/ngx_http_addition_filter_module.c\n        ngx_module_libs=\n        ngx_module_link=$HTTP_ADDITION\n\n        . auto/module\n    fi\n\n    if [ $HTTP_GUNZIP = YES ]; then\n        have=NGX_HTTP_GZIP . auto/have\n        USE_ZLIB=YES\n\n        ngx_module_name=ngx_http_gunzip_filter_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/modules/ngx_http_gunzip_filter_module.c\n        ngx_module_libs=\n        ngx_module_link=$HTTP_GUNZIP\n\n        . auto/module\n    fi\n\n    if [ $HTTP_USERID = YES ]; then\n        ngx_module_name=ngx_http_userid_filter_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/modules/ngx_http_userid_filter_module.c\n        ngx_module_libs=\n        ngx_module_link=$HTTP_USERID\n\n        . auto/module\n    fi\n\n    if :; then\n        ngx_module_name=ngx_http_headers_filter_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/modules/ngx_http_headers_filter_module.c\n        ngx_module_libs=\n        ngx_module_link=YES\n\n        . auto/module\n    fi\n\n\n    ngx_module_type=HTTP_INIT_FILTER\n    HTTP_INIT_FILTER_MODULES=\n\n    if :; then\n        ngx_module_name=ngx_http_copy_filter_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/ngx_http_copy_filter_module.c\n        ngx_module_libs=\n        ngx_module_link=YES\n\n        . auto/module\n    fi\n\n    if :; then\n        ngx_module_name=ngx_http_range_body_filter_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=\n        ngx_module_libs=\n        ngx_module_link=YES\n\n        . auto/module\n    fi\n\n    if :; then\n        ngx_module_name=ngx_http_not_modified_filter_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/modules/ngx_http_not_modified_filter_module.c\n        ngx_module_libs=\n        ngx_module_link=YES\n\n        . auto/module\n    fi\n\n    if [ $HTTP_SLICE = YES ]; then\n        ngx_module_name=ngx_http_slice_filter_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/modules/ngx_http_slice_filter_module.c\n        ngx_module_libs=\n        ngx_module_link=$HTTP_SLICE\n\n        . auto/module\n    fi\n\n\n    ngx_module_type=HTTP\n\n    if [ $HTTP_V2 = YES ]; then\n        have=NGX_HTTP_V2 . auto/have\n        have=NGX_HTTP_HEADERS . auto/have\n\n        ngx_module_name=ngx_http_v2_module\n        ngx_module_incs=src/http/v2\n        ngx_module_deps=\"src/http/v2/ngx_http_v2.h \\\n                         src/http/v2/ngx_http_v2_module.h\"\n        ngx_module_srcs=\"src/http/v2/ngx_http_v2.c \\\n                         src/http/v2/ngx_http_v2_table.c \\\n                         src/http/v2/ngx_http_v2_encode.c \\\n                         src/http/v2/ngx_http_v2_module.c\"\n        ngx_module_libs=\n        ngx_module_link=$HTTP_V2\n\n        . auto/module\n    fi\n\n    if :; then\n        ngx_module_name=ngx_http_static_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/modules/ngx_http_static_module.c\n        ngx_module_libs=\n        ngx_module_link=YES\n\n        . auto/module\n    fi\n\n    if [ $HTTP_GZIP_STATIC = YES ]; then\n        have=NGX_HTTP_GZIP . auto/have\n\n        ngx_module_name=ngx_http_gzip_static_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/modules/ngx_http_gzip_static_module.c\n        ngx_module_libs=\n        ngx_module_link=$HTTP_GZIP_STATIC\n\n        . auto/module\n    fi\n\n    if [ $HTTP_DAV = YES ]; then\n        have=NGX_HTTP_DAV . auto/have\n\n        ngx_module_name=ngx_http_dav_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/modules/ngx_http_dav_module.c\n        ngx_module_libs=\n        ngx_module_link=$HTTP_DAV\n\n        . auto/module\n    fi\n\n    if [ $HTTP_AUTOINDEX = YES ]; then\n        ngx_module_name=ngx_http_autoindex_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/modules/ngx_http_autoindex_module.c\n        ngx_module_libs=\n        ngx_module_link=$HTTP_AUTOINDEX\n\n        . auto/module\n    fi\n\n    if :; then\n        ngx_module_name=ngx_http_index_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/modules/ngx_http_index_module.c\n        ngx_module_libs=\n        ngx_module_link=YES\n\n        . auto/module\n    fi\n\n    if [ $HTTP_RANDOM_INDEX = YES ]; then\n        ngx_module_name=ngx_http_random_index_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/modules/ngx_http_random_index_module.c\n        ngx_module_libs=\n        ngx_module_link=$HTTP_RANDOM_INDEX\n\n        . auto/module\n    fi\n\n    if [ $HTTP_MIRROR = YES ]; then\n        ngx_module_name=ngx_http_mirror_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/modules/ngx_http_mirror_module.c\n        ngx_module_libs=\n        ngx_module_link=$HTTP_MIRROR\n\n        . auto/module\n    fi\n\n    if :; then\n        ngx_module_name=ngx_http_try_files_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/modules/ngx_http_try_files_module.c\n        ngx_module_libs=\n        ngx_module_link=YES\n\n        . auto/module\n    fi\n\n    if [ $HTTP_AUTH_REQUEST = YES ]; then\n        ngx_module_name=ngx_http_auth_request_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/modules/ngx_http_auth_request_module.c\n        ngx_module_libs=\n        ngx_module_link=$HTTP_AUTH_REQUEST\n\n        . auto/module\n    fi\n\n    if [ $HTTP_AUTH_BASIC = YES ]; then\n        have=NGX_CRYPT . auto/have\n\n        ngx_module_name=ngx_http_auth_basic_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/modules/ngx_http_auth_basic_module.c\n        ngx_module_libs=$CRYPT_LIB\n        ngx_module_link=$HTTP_AUTH_BASIC\n\n        . auto/module\n    fi\n\n    if [ $HTTP_ACCESS = YES ]; then\n        ngx_module_name=ngx_http_access_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/modules/ngx_http_access_module.c\n        ngx_module_libs=\n        ngx_module_link=$HTTP_ACCESS\n\n        . auto/module\n    fi\n\n    if [ $HTTP_LIMIT_CONN = YES ]; then\n        ngx_module_name=ngx_http_limit_conn_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/modules/ngx_http_limit_conn_module.c\n        ngx_module_libs=\n        ngx_module_link=$HTTP_LIMIT_CONN\n\n        . auto/module\n    fi\n\n    if [ $HTTP_LIMIT_REQ = YES ]; then\n        ngx_module_name=ngx_http_limit_req_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/modules/ngx_http_limit_req_module.c\n        ngx_module_libs=\n        ngx_module_link=$HTTP_LIMIT_REQ\n\n        . auto/module\n    fi\n\n    if [ $HTTP_REALIP = YES ]; then\n        have=NGX_HTTP_REALIP . auto/have\n        have=NGX_HTTP_X_FORWARDED_FOR . auto/have\n\n        ngx_module_name=ngx_http_realip_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/modules/ngx_http_realip_module.c\n        ngx_module_libs=\n        ngx_module_link=$HTTP_REALIP\n\n        . auto/module\n    fi\n\n    if [ $HTTP_STATUS = YES ]; then\n        ngx_module_name=ngx_http_status_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/modules/ngx_http_status_module.c\n        ngx_module_libs=\n        ngx_module_link=$HTTP_STATUS\n\n        . auto/module\n    fi\n\n    if [ $HTTP_GEO = YES ]; then\n        have=NGX_HTTP_X_FORWARDED_FOR . auto/have\n\n        ngx_module_name=ngx_http_geo_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/modules/ngx_http_geo_module.c\n        ngx_module_libs=\n        ngx_module_link=$HTTP_GEO\n\n        . auto/module\n    fi\n\n    if [ $HTTP_GEOIP != NO ]; then\n        have=NGX_HTTP_X_FORWARDED_FOR . auto/have\n\n        ngx_module_name=ngx_http_geoip_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/modules/ngx_http_geoip_module.c\n        ngx_module_libs=GEOIP\n        ngx_module_link=$HTTP_GEOIP\n\n        . auto/module\n    fi\n\n    if [ $HTTP_MAP = YES ]; then\n        ngx_module_name=ngx_http_map_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/modules/ngx_http_map_module.c\n        ngx_module_libs=\n        ngx_module_link=$HTTP_MAP\n\n        . auto/module\n    fi\n\n    if [ $HTTP_SPLIT_CLIENTS = YES ]; then\n        ngx_module_name=ngx_http_split_clients_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/modules/ngx_http_split_clients_module.c\n        ngx_module_libs=\n        ngx_module_link=$HTTP_SPLIT_CLIENTS\n\n        . auto/module\n    fi\n\n    if [ $HTTP_REFERER = YES ]; then\n        ngx_module_name=ngx_http_referer_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/modules/ngx_http_referer_module.c\n        ngx_module_libs=\n        ngx_module_link=$HTTP_REFERER\n\n        . auto/module\n    fi\n\n    if [ $HTTP_REWRITE = YES -a $USE_PCRE != DISABLED ]; then\n        USE_PCRE=YES\n\n        ngx_module_name=ngx_http_rewrite_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/modules/ngx_http_rewrite_module.c\n        ngx_module_libs=\n        ngx_module_link=$HTTP_REWRITE\n\n        . auto/module\n    fi\n\n    if [ $HTTP_SSL = YES ]; then\n        USE_OPENSSL=YES\n        have=NGX_HTTP_SSL . auto/have\n\n        ngx_module_name=ngx_http_ssl_module\n        ngx_module_incs=\n        ngx_module_deps=src/http/modules/ngx_http_ssl_module.h\n        ngx_module_srcs=src/http/modules/ngx_http_ssl_module.c\n        ngx_module_libs=\n        ngx_module_link=$HTTP_SSL\n\n        . auto/module\n    fi\n\n    if [ $HTTP_PROXY = YES ]; then\n        have=NGX_HTTP_X_FORWARDED_FOR . auto/have\n\n        ngx_module_name=ngx_http_proxy_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/modules/ngx_http_proxy_module.c\n        ngx_module_libs=\n        ngx_module_link=$HTTP_PROXY\n\n        . auto/module\n    fi\n\n    if [ $HTTP_FASTCGI = YES ]; then\n        ngx_module_name=ngx_http_fastcgi_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/modules/ngx_http_fastcgi_module.c\n        ngx_module_libs=\n        ngx_module_link=$HTTP_FASTCGI\n\n        . auto/module\n    fi\n\n    if [ $HTTP_UWSGI = YES ]; then\n        ngx_module_name=ngx_http_uwsgi_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/modules/ngx_http_uwsgi_module.c\n        ngx_module_libs=\n        ngx_module_link=$HTTP_UWSGI\n\n        . auto/module\n    fi\n\n    if [ $HTTP_SCGI = YES ]; then\n        ngx_module_name=ngx_http_scgi_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/modules/ngx_http_scgi_module.c\n        ngx_module_libs=\n        ngx_module_link=$HTTP_SCGI\n\n        . auto/module\n    fi\n\n    if [ $HTTP_GRPC = YES -a $HTTP_V2 = YES ]; then\n        ngx_module_name=ngx_http_grpc_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/modules/ngx_http_grpc_module.c\n        ngx_module_libs=\n        ngx_module_link=$HTTP_GRPC\n\n        . auto/module\n    fi\n\n    if [ $HTTP_PERL != NO ]; then\n        ngx_module_name=ngx_http_perl_module\n        ngx_module_incs=src/http/modules/perl\n        ngx_module_deps=src/http/modules/perl/ngx_http_perl_module.h\n        ngx_module_srcs=src/http/modules/perl/ngx_http_perl_module.c\n        ngx_module_libs=PERL\n        ngx_module_link=$HTTP_PERL\n\n        . auto/module\n    fi\n\n    if [ $HTTP_LUA = YES ]; then\n         NGX_ADDONS=\"$NGX_ADDONS modules/ngx_http_lua_module\"\n    fi\n\n    if [ $HTTP_MEMCACHED = YES ]; then\n        ngx_module_name=ngx_http_memcached_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/modules/ngx_http_memcached_module.c\n        ngx_module_libs=\n        ngx_module_link=$HTTP_MEMCACHED\n\n        . auto/module\n    fi\n\n    if [ $HTTP_EMPTY_GIF = YES ]; then\n        ngx_module_name=ngx_http_empty_gif_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/modules/ngx_http_empty_gif_module.c\n        ngx_module_libs=\n        ngx_module_link=$HTTP_EMPTY_GIF\n\n        . auto/module\n    fi\n\n    if [ $HTTP_BROWSER = YES ]; then\n        ngx_module_name=ngx_http_browser_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/modules/ngx_http_browser_module.c\n        ngx_module_libs=\n        ngx_module_link=$HTTP_BROWSER\n\n        . auto/module\n    fi\n\n    if [ $HTTP_SECURE_LINK = YES ]; then\n        ngx_module_name=ngx_http_secure_link_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/modules/ngx_http_secure_link_module.c\n        ngx_module_libs=\n        ngx_module_link=$HTTP_SECURE_LINK\n\n        . auto/module\n    fi\n\n    if [ $HTTP_DEGRADATION = YES ]; then\n        have=NGX_HTTP_DEGRADATION . auto/have\n\n        ngx_module_name=ngx_http_degradation_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/modules/ngx_http_degradation_module.c\n        ngx_module_libs=\n        ngx_module_link=$HTTP_DEGRADATION\n\n        . auto/module\n    fi\n\n    if [ $HTTP_FLV = YES ]; then\n        ngx_module_name=ngx_http_flv_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/modules/ngx_http_flv_module.c\n        ngx_module_libs=\n        ngx_module_link=$HTTP_FLV\n\n        . auto/module\n    fi\n\n    if [ $HTTP_MP4 = YES ]; then\n        ngx_module_name=ngx_http_mp4_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/modules/ngx_http_mp4_module.c\n        ngx_module_libs=\n        ngx_module_link=$HTTP_MP4\n\n        . auto/module\n    fi\n\n    if [ $HTTP_UPSTREAM_HASH = YES ]; then\n        ngx_module_name=ngx_http_upstream_hash_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/modules/ngx_http_upstream_hash_module.c\n        ngx_module_libs=\n        ngx_module_link=$HTTP_UPSTREAM_HASH\n\n        . auto/module\n    fi\n\n    if [ $HTTP_UPSTREAM_IP_HASH = YES ]; then\n        ngx_module_name=ngx_http_upstream_ip_hash_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/modules/ngx_http_upstream_ip_hash_module.c\n        ngx_module_libs=\n        ngx_module_link=$HTTP_UPSTREAM_IP_HASH\n\n        . auto/module\n    fi\n\n    if [ $HTTP_UPSTREAM_LEAST_CONN = YES ]; then\n        ngx_module_name=ngx_http_upstream_least_conn_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/modules/ngx_http_upstream_least_conn_module.c\n        ngx_module_libs=\n        ngx_module_link=$HTTP_UPSTREAM_LEAST_CONN\n\n        . auto/module\n    fi\n\n    if [ $HTTP_UPSTREAM_RANDOM = YES ]; then\n        ngx_module_name=ngx_http_upstream_random_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/modules/ngx_http_upstream_random_module.c\n        ngx_module_libs=\n        ngx_module_link=$HTTP_UPSTREAM_RANDOM\n\n        . auto/module\n    fi\n\n    if [ $HTTP_UPSTREAM_KEEPALIVE = YES ]; then\n        ngx_module_name=ngx_http_upstream_keepalive_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/modules/ngx_http_upstream_keepalive_module.c\n        ngx_module_libs=\n        ngx_module_link=$HTTP_UPSTREAM_KEEPALIVE\n\n        . auto/module\n    fi\n\n    if [ $HTTP_UPSTREAM_ZONE = YES ]; then\n        have=NGX_HTTP_UPSTREAM_ZONE . auto/have\n\n        ngx_module_name=ngx_http_upstream_zone_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/modules/ngx_http_upstream_zone_module.c\n        ngx_module_libs=\n        ngx_module_link=$HTTP_UPSTREAM_ZONE\n\n        . auto/module\n    fi\n\n    if [ $HTTP_STUB_STATUS = YES ]; then\n        have=NGX_STAT_STUB . auto/have\n\n        ngx_module_name=ngx_http_stub_status_module\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=src/http/modules/ngx_http_stub_status_module.c\n        ngx_module_libs=\n        ngx_module_link=$HTTP_STUB_STATUS\n\n        . auto/module\n    fi\n\n    if [ $HTTP_UPSTREAM_RBTREE = YES ]; then\n        have=NGX_HTTP_UPSTREAM_RBTREE . auto/have\n    fi\nfi\n\n\nif [ $MAIL != NO ]; then\n    MAIL_MODULES=\n    MAIL_DEPS=\n    MAIL_INCS=\n\n    ngx_module_type=MAIL\n    ngx_module_libs=\n    ngx_module_link=YES\n\n    ngx_module_order=\n\n    ngx_module_name=\"ngx_mail_module ngx_mail_core_module\"\n    ngx_module_incs=\"src/mail\"\n    ngx_module_deps=\"src/mail/ngx_mail.h\"\n    ngx_module_srcs=\"src/mail/ngx_mail.c \\\n                     src/mail/ngx_mail_core_module.c \\\n                     src/mail/ngx_mail_handler.c \\\n                     src/mail/ngx_mail_parse.c\"\n\n    . auto/module\n\n    ngx_module_incs=\n\n    if [ $MAIL_SSL = YES ]; then\n        USE_OPENSSL=YES\n        have=NGX_MAIL_SSL . auto/have\n\n        ngx_module_name=ngx_mail_ssl_module\n        ngx_module_deps=src/mail/ngx_mail_ssl_module.h\n        ngx_module_srcs=src/mail/ngx_mail_ssl_module.c\n\n        . auto/module\n    fi\n\n    if [ $MAIL_POP3 = YES ]; then\n        ngx_module_name=ngx_mail_pop3_module\n        ngx_module_deps=src/mail/ngx_mail_pop3_module.h\n        ngx_module_srcs=\"src/mail/ngx_mail_pop3_module.c \\\n                         src/mail/ngx_mail_pop3_handler.c\"\n\n        . auto/module\n    fi\n\n    if [ $MAIL_IMAP = YES ]; then\n        ngx_module_name=ngx_mail_imap_module\n        ngx_module_deps=src/mail/ngx_mail_imap_module.h\n        ngx_module_srcs=\"src/mail/ngx_mail_imap_module.c \\\n                         src/mail/ngx_mail_imap_handler.c\"\n\n        . auto/module\n    fi\n\n    if [ $MAIL_SMTP = YES ]; then\n        ngx_module_name=ngx_mail_smtp_module\n        ngx_module_deps=src/mail/ngx_mail_smtp_module.h\n        ngx_module_srcs=\"src/mail/ngx_mail_smtp_module.c \\\n                         src/mail/ngx_mail_smtp_handler.c\"\n\n        . auto/module\n    fi\n\n    ngx_module_name=ngx_mail_auth_http_module\n    ngx_module_deps=\n    ngx_module_srcs=src/mail/ngx_mail_auth_http_module.c\n\n    . auto/module\n\n    ngx_module_name=ngx_mail_proxy_module\n    ngx_module_deps=\n    ngx_module_srcs=src/mail/ngx_mail_proxy_module.c\n\n    . auto/module\n\n    ngx_module_name=ngx_mail_realip_module\n    ngx_module_deps=\n    ngx_module_srcs=src/mail/ngx_mail_realip_module.c\n\n    . auto/module\nfi\n\n\nif [ $STREAM != NO ]; then\n    STREAM_MODULES=\n    STREAM_DEPS=\n    STREAM_INCS=\n\n    ngx_module_type=STREAM\n    ngx_module_libs=\n    ngx_module_link=YES\n\n    ngx_module_order=\n\n    ngx_module_name=\"ngx_stream_module \\\n                     ngx_stream_core_module \\\n                     ngx_stream_log_module \\\n                     ngx_stream_proxy_module \\\n                     ngx_stream_upstream_module \\\n                     ngx_stream_write_filter_module\"\n    ngx_module_incs=\"src/stream\"\n    ngx_module_deps=\"src/stream/ngx_stream.h \\\n                     src/stream/ngx_stream_variables.h \\\n                     src/stream/ngx_stream_script.h \\\n                     src/stream/ngx_stream_upstream.h \\\n                     src/stream/ngx_stream_upstream_round_robin.h\"\n    ngx_module_srcs=\"src/stream/ngx_stream.c \\\n                     src/stream/ngx_stream_variables.c \\\n                     src/stream/ngx_stream_script.c \\\n                     src/stream/ngx_stream_handler.c \\\n                     src/stream/ngx_stream_core_module.c \\\n                     src/stream/ngx_stream_log_module.c \\\n                     src/stream/ngx_stream_proxy_module.c \\\n                     src/stream/ngx_stream_upstream.c \\\n                     src/stream/ngx_stream_upstream_round_robin.c \\\n                     src/stream/ngx_stream_write_filter_module.c\"\n\n    . auto/module\n\n    ngx_module_incs=\n\n    if [ $STREAM_SSL = YES ]; then\n        USE_OPENSSL=YES\n        have=NGX_STREAM_SSL . auto/have\n\n        ngx_module_name=ngx_stream_ssl_module\n        ngx_module_deps=src/stream/ngx_stream_ssl_module.h\n        ngx_module_srcs=src/stream/ngx_stream_ssl_module.c\n        ngx_module_libs=\n        ngx_module_link=$STREAM_SSL\n\n        . auto/module\n    fi\n\n    if [ $STREAM_REALIP = YES ]; then\n        ngx_module_name=ngx_stream_realip_module\n        ngx_module_deps=\n        ngx_module_srcs=src/stream/ngx_stream_realip_module.c\n        ngx_module_libs=\n        ngx_module_link=$STREAM_REALIP\n\n        . auto/module\n    fi\n\n    if [ $STREAM_LIMIT_CONN = YES ]; then\n        ngx_module_name=ngx_stream_limit_conn_module\n        ngx_module_deps=\n        ngx_module_srcs=src/stream/ngx_stream_limit_conn_module.c\n        ngx_module_libs=\n        ngx_module_link=$STREAM_LIMIT_CONN\n\n        . auto/module\n    fi\n\n    if [ $STREAM_ACCESS = YES ]; then\n        ngx_module_name=ngx_stream_access_module\n        ngx_module_deps=\n        ngx_module_srcs=src/stream/ngx_stream_access_module.c\n        ngx_module_libs=\n        ngx_module_link=$STREAM_ACCESS\n\n        . auto/module\n    fi\n\n    if [ $STREAM_GEO = YES ]; then\n        ngx_module_name=ngx_stream_geo_module\n        ngx_module_deps=\n        ngx_module_srcs=src/stream/ngx_stream_geo_module.c\n        ngx_module_libs=\n        ngx_module_link=$STREAM_GEO\n\n        . auto/module\n    fi\n\n    if [ $STREAM_GEOIP != NO ]; then\n        ngx_module_name=ngx_stream_geoip_module\n        ngx_module_deps=\n        ngx_module_srcs=src/stream/ngx_stream_geoip_module.c\n        ngx_module_libs=GEOIP\n        ngx_module_link=$STREAM_GEOIP\n\n        . auto/module\n    fi\n\n    if [ $STREAM_MAP = YES ]; then\n        ngx_module_name=ngx_stream_map_module\n        ngx_module_deps=\n        ngx_module_srcs=src/stream/ngx_stream_map_module.c\n        ngx_module_libs=\n        ngx_module_link=$STREAM_MAP\n\n        . auto/module\n    fi\n\n    if [ $STREAM_SPLIT_CLIENTS = YES ]; then\n        ngx_module_name=ngx_stream_split_clients_module\n        ngx_module_deps=\n        ngx_module_srcs=src/stream/ngx_stream_split_clients_module.c\n        ngx_module_libs=\n        ngx_module_link=$STREAM_SPLIT_CLIENTS\n\n        . auto/module\n    fi\n\n    if [ $STREAM_RETURN = YES ]; then\n        ngx_module_name=ngx_stream_return_module\n        ngx_module_deps=\n        ngx_module_srcs=src/stream/ngx_stream_return_module.c\n        ngx_module_libs=\n        ngx_module_link=$STREAM_RETURN\n\n        . auto/module\n    fi\n\n    if [ $STREAM_SET = YES ]; then\n        ngx_module_name=ngx_stream_set_module\n        ngx_module_deps=\n        ngx_module_srcs=src/stream/ngx_stream_set_module.c\n        ngx_module_libs=\n        ngx_module_link=$STREAM_SET\n\n        . auto/module\n    fi\n\n    if [ $STREAM_UPSTREAM_HASH = YES ]; then\n        ngx_module_name=ngx_stream_upstream_hash_module\n        ngx_module_deps=\n        ngx_module_srcs=src/stream/ngx_stream_upstream_hash_module.c\n        ngx_module_libs=\n        ngx_module_link=$STREAM_UPSTREAM_HASH\n\n        . auto/module\n    fi\n\n    if [ $STREAM_UPSTREAM_LEAST_CONN = YES ]; then\n        ngx_module_name=ngx_stream_upstream_least_conn_module\n        ngx_module_deps=\n        ngx_module_srcs=src/stream/ngx_stream_upstream_least_conn_module.c\n        ngx_module_libs=\n        ngx_module_link=$STREAM_UPSTREAM_LEAST_CONN\n\n        . auto/module\n    fi\n\n    if [ $STREAM_UPSTREAM_RANDOM = YES ]; then\n        ngx_module_name=ngx_stream_upstream_random_module\n        ngx_module_deps=\n        ngx_module_srcs=src/stream/ngx_stream_upstream_random_module.c\n        ngx_module_libs=\n        ngx_module_link=$STREAM_UPSTREAM_RANDOM\n\n        . auto/module\n    fi\n\n    if [ $STREAM_UPSTREAM_ZONE = YES ]; then\n        have=NGX_STREAM_UPSTREAM_ZONE . auto/have\n\n        ngx_module_name=ngx_stream_upstream_zone_module\n        ngx_module_deps=\n        ngx_module_srcs=src/stream/ngx_stream_upstream_zone_module.c\n        ngx_module_libs=\n        ngx_module_link=$STREAM_UPSTREAM_ZONE\n\n        . auto/module\n    fi\n\n    if [ $STREAM_SSL_PREREAD = YES ]; then\n        ngx_module_name=ngx_stream_ssl_preread_module\n        ngx_module_deps=\n        ngx_module_srcs=src/stream/ngx_stream_ssl_preread_module.c\n        ngx_module_libs=\n        ngx_module_link=$STREAM_SSL_PREREAD\n\n        . auto/module\n    fi\n\n    if [ $STREAM_SNI = YES ]; then\n        if [ $STREAM_SSL != YES ]; then\n            echo \"--with-stream_ssl_module is needed by --with-stream_sni\"\n            exit 1;\n        fi\n        have=T_NGX_STREAM_SNI . auto/have\n    fi\nfi\n\n\n#if [ -r $NGX_OBJS/auto ]; then\n#    . $NGX_OBJS/auto\n#fi\n\n\nif test -n \"$NGX_ADDONS\"; then\n\n    echo configuring additional modules\n\n    for ngx_addon_dir in $NGX_ADDONS\n    do\n        echo \"adding module in $ngx_addon_dir\"\n\n        ngx_module_type=\n        ngx_module_name=\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=\n        ngx_module_libs=\n        ngx_module_order=\n        ngx_module_link=ADDON\n\n        if test -f $ngx_addon_dir/config; then\n            . $ngx_addon_dir/config\n\n            echo \" + $ngx_addon_name was configured\"\n\n        else\n            echo \"$0: error: no $ngx_addon_dir/config was found\"\n            exit 1\n        fi\n    done\nfi\n\n\nif test -n \"$DYNAMIC_ADDONS\"; then\n\n    echo configuring additional dynamic modules\n\n    for ngx_addon_dir in $DYNAMIC_ADDONS\n    do\n        echo \"adding module in $ngx_addon_dir\"\n\n        ngx_module_type=\n        ngx_module_name=\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=\n        ngx_module_libs=\n        ngx_module_order=\n        ngx_module_link=DYNAMIC\n\n        if test -f $ngx_addon_dir/config; then\n            . $ngx_addon_dir/config\n\n            echo \" + $ngx_addon_name was configured\"\n\n        else\n            echo \"$0: error: no $ngx_addon_dir/config was found\"\n            exit 1\n        fi\n    done\nfi\n\n\nif [ $USE_OPENSSL = YES ]; then\n    ngx_module_type=CORE\n    ngx_module_name=ngx_openssl_module\n    ngx_module_incs=\n    ngx_module_deps=src/event/ngx_event_openssl.h\n    ngx_module_srcs=\"src/event/ngx_event_openssl.c\n                     src/event/ngx_event_openssl_stapling.c\"\n    ngx_module_libs=\n    ngx_module_link=YES\n    ngx_module_order=\n\n    . auto/module\nfi\n\n\nif [ $USE_PCRE = YES ]; then\n    ngx_module_type=CORE\n    ngx_module_name=ngx_regex_module\n    ngx_module_incs=\n    ngx_module_deps=src/core/ngx_regex.h\n    ngx_module_srcs=src/core/ngx_regex.c\n    ngx_module_libs=\n    ngx_module_link=YES\n    ngx_module_order=\n\n    . auto/module\nfi\n\n# add proc\nmodules=\"$CORE_MODULES $EVENT_MODULES $PROCS_MODULES\"\n\n\n# thread pool module should be initialized after events\nif [ $USE_THREADS = YES ]; then\n    modules=\"$modules $THREAD_POOL_MODULE\"\nfi\n\n\nif [ $HTTP = YES ]; then\n    modules=\"$modules $HTTP_MODULES $HTTP_FILTER_MODULES \\\n             $HTTP_AUX_FILTER_MODULES $HTTP_INIT_FILTER_MODULES\"\n\n    NGX_ADDON_DEPS=\"$NGX_ADDON_DEPS \\$(HTTP_DEPS)\"\nfi\n\n\nif [ $MAIL != NO ]; then\n\n    if [ $MAIL = YES ]; then\n        modules=\"$modules $MAIL_MODULES\"\n\n    elif [ $MAIL = DYNAMIC ]; then\n        ngx_module_name=$MAIL_MODULES\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=$MAIL_SRCS\n        ngx_module_libs=\n        ngx_module_link=DYNAMIC\n\n        . auto/module\n    fi\n\n    NGX_ADDON_DEPS=\"$NGX_ADDON_DEPS \\$(MAIL_DEPS)\"\nfi\n\n\nif [ $STREAM != NO ]; then\n\n    if [ $STREAM = YES ]; then\n        modules=\"$modules $STREAM_MODULES\"\n\n    elif [ $STREAM = DYNAMIC ]; then\n        ngx_module_name=$STREAM_MODULES\n        ngx_module_incs=\n        ngx_module_deps=\n        ngx_module_srcs=$STREAM_SRCS\n        ngx_module_libs=\n        ngx_module_link=DYNAMIC\n\n        . auto/module\n    fi\n\n    NGX_ADDON_DEPS=\"$NGX_ADDON_DEPS \\$(STREAM_DEPS)\"\nfi\n\n\nngx_module_type=MISC\nMISC_MODULES=\n\nif [ $NGX_GOOGLE_PERFTOOLS = YES ]; then\n    ngx_module_name=ngx_google_perftools_module\n    ngx_module_incs=\n    ngx_module_deps=\n    ngx_module_srcs=src/misc/ngx_google_perftools_module.c\n    ngx_module_libs=\n    ngx_module_link=$NGX_GOOGLE_PERFTOOLS\n\n    . auto/module\nfi\n\nif [ $NGX_CPP_TEST = YES ]; then\n    ngx_module_name=\n    ngx_module_incs=\n    ngx_module_deps=\n    ngx_module_srcs=src/misc/ngx_cpp_test_module.cpp\n    ngx_module_libs=-lstdc++\n    ngx_module_link=$NGX_CPP_TEST\n\n    . auto/module\nfi\n\nmodules=\"$modules $MISC_MODULES\"\n\n\nif [ $NGX_COMPAT = YES ]; then\n    have=NGX_COMPAT . auto/have\n    have=NGX_HTTP_GZIP . auto/have\n    have=NGX_HTTP_DAV . auto/have\n    have=NGX_HTTP_REALIP . auto/have\n    have=NGX_HTTP_X_FORWARDED_FOR . auto/have\n    have=NGX_HTTP_HEADERS . auto/have\n    have=NGX_HTTP_UPSTREAM_ZONE . auto/have\n    have=NGX_STREAM_UPSTREAM_ZONE . auto/have\nfi\n\n\ncat << END                                    > $NGX_MODULES_C\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n$NGX_PRAGMA\n\nEND\n\nfor mod in $modules\ndo\n    echo \"extern ngx_module_t  $mod;\"         >> $NGX_MODULES_C\ndone\n\necho                                          >> $NGX_MODULES_C\necho 'ngx_module_t *ngx_modules[] = {'        >> $NGX_MODULES_C\n\nfor mod in $modules\ndo\n    echo \"    &$mod,\"                         >> $NGX_MODULES_C\ndone\n\ncat << END                                    >> $NGX_MODULES_C\n    NULL\n};\n\nEND\n\necho 'char *ngx_module_names[] = {'           >> $NGX_MODULES_C\n\nfor mod in $modules\ndo\n    echo \"    \\\"$mod\\\",\"                      >> $NGX_MODULES_C\ndone\n\ncat << END                                    >> $NGX_MODULES_C\n    NULL\n};\n\nEND\n\ncase \"$NGX_PLATFORM\" in\n    Linux:*)\n        have=T_PIPE_SET_SIZE . auto/have\n    ;;\nesac\n\nhave=T_NGX_DNS_RESOLVE_BACKUP . auto/have\nhave=T_NGX_MASTER_ENV . auto/have\nhave=T_PIPES . auto/have\nhave=T_NGX_INPUT_BODY_FILTER . auto/have\nhave=T_NGX_GZIP_CLEAR_ETAG . auto/have\nhave=T_NGX_RESOLVER_FILE . auto/have\nhave=T_DEPRECATED . auto/have\nhave=T_NGX_VARS . auto/have\nhave=T_NGX_HTTP_STUB_STATUS . auto/have\nhave=T_UPSTREAM_TRIES  . auto/have\nhave=T_GEO . auto/have\nhave=T_NGX_RET_CACHE  . auto/have\nhave=T_LIMIT_REQ  . auto/have\nhave=T_LIMIT_REQ_RATE_VAR  . auto/have\nif [ $HTTP_V2 = YES ]; then\n    have=T_NGX_HTTP2_SRV_ENABLE . auto/have\nfi\nhave=T_NGX_SSL_HANDSHAKE_TIME . auto/have\nhave=T_NGX_HTTP_IMPROVED_IF . auto/have\nhave=T_NGX_HTTP_UPSTREAM_RETRY_CC . auto/have\nhave=T_NGX_HTTP_SSL_VCE . auto/have\nhave=T_NGX_HTTP_UPSTREAM_RANDOM . auto/have\nhave=T_NGX_IMPROVED_LIST . auto/have\nhave=T_NGX_SERVER_INFO . auto/have\nhave=T_NGX_ACCEPT_FILTER . auto/have\nhave=T_NGX_MODIFY_DEFAULT_VALUE . auto/have\nhave=T_NGX_HTTP_UPSTREAM_ID . auto/have\nhave=T_NGX_HTTP_IMPROVED_REWRITE . auto/have\nhave=T_NGX_SHOW_INFO . auto/have\nhave=T_NGX_HTTP_IMAGE_FILTER . auto/have\nhave=T_HTTP_HEADER . auto/have\nhave=T_HTTP_UPSTREAM_TIMEOUT_VAR . auto/have\nhave=T_NGX_HTTPS_ALLOW_HTTP . auto/have\nhave=T_NGX_REQUEST_START_TIME . auto/have\nhave=T_NGX_SOCKET_BUFFER . auto/have\n"
  },
  {
    "path": "auto/nohave",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\ncat << END >> $NGX_AUTO_CONFIG_H\n\n#ifndef $have\n#define $have  0\n#endif\n\nEND\n"
  },
  {
    "path": "auto/options",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\nhelp=no\n\nNGX_PREFIX=\nNGX_SBIN_PATH=\nNGX_MODULES_PATH=\nNGX_CONF_PREFIX=\nNGX_CONF_PATH=\nNGX_ERROR_LOG_PATH=\nNGX_PID_PATH=\nNGX_LOCK_PATH=\nNGX_USER=\nNGX_GROUP=\nNGX_BUILD=\n\nCC=${CC:-cc}\nCPP=\nNGX_OBJS=objs\n\nNGX_DEBUG=NO\nNGX_SSL_ASYNC=NO\nNGX_CC_OPT=\nNGX_LD_OPT=\nCPU=NO\n\nNGX_RPATH=NO\n\nNGX_TEST_BUILD_DEVPOLL=NO\nNGX_TEST_BUILD_EVENTPORT=NO\nNGX_TEST_BUILD_EPOLL=NO\nNGX_TEST_BUILD_SOLARIS_SENDFILEV=NO\n\nNGX_PLATFORM=\nNGX_WINE=\n\nEVENT_FOUND=NO\n\nEVENT_SELECT=NO\nEVENT_POLL=NO\n\nUSE_THREADS=NO\n\nNGX_FILE_AIO=NO\n\nHTTP=YES\n\nNGX_HTTP_LOG_PATH=\nNGX_HTTP_CLIENT_TEMP_PATH=\nNGX_HTTP_PROXY_TEMP_PATH=\nNGX_HTTP_FASTCGI_TEMP_PATH=\nNGX_HTTP_UWSGI_TEMP_PATH=\nNGX_HTTP_SCGI_TEMP_PATH=\n\nHTTP_CACHE=YES\nHTTP_CHARSET=YES\nHTTP_GZIP=YES\nHTTP_SSL=YES\nHTTP_V2=NO\nHTTP_SSI=YES\nHTTP_REALIP=NO\nHTTP_XSLT=NO\nHTTP_IMAGE_FILTER=NO\nHTTP_SUB=NO\nHTTP_ADDITION=NO\nHTTP_DAV=NO\nHTTP_ACCESS=YES\nHTTP_AUTH_BASIC=YES\nHTTP_AUTH_REQUEST=YES\nHTTP_MIRROR=YES\nHTTP_USERID=YES\nHTTP_SLICE=NO\nHTTP_AUTOINDEX=YES\nHTTP_RANDOM_INDEX=NO\nHTTP_STATUS=NO\nHTTP_GEO=YES\nHTTP_GEOIP=NO\nHTTP_MAP=YES\nHTTP_SPLIT_CLIENTS=YES\nHTTP_REFERER=YES\nHTTP_REWRITE=YES\nHTTP_PROXY=YES\nHTTP_FASTCGI=YES\nHTTP_UWSGI=YES\nHTTP_SCGI=YES\nHTTP_GRPC=YES\nHTTP_PERL=NO\nHTTP_LUA=NO\nHTTP_MEMCACHED=YES\nHTTP_LIMIT_CONN=YES\nHTTP_LIMIT_REQ=YES\nHTTP_EMPTY_GIF=YES\nHTTP_BROWSER=YES\nHTTP_SECURE_LINK=NO\nHTTP_DEGRADATION=NO\nHTTP_FLV=NO\nHTTP_MP4=NO\nHTTP_GUNZIP=NO\nHTTP_GZIP_STATIC=NO\nHTTP_UPSTREAM_HASH=YES\nHTTP_UPSTREAM_IP_HASH=YES\nHTTP_UPSTREAM_LEAST_CONN=YES\nHTTP_UPSTREAM_RANDOM=YES\nHTTP_UPSTREAM_KEEPALIVE=YES\nHTTP_UPSTREAM_RBTREE=YES\nHTTP_UPSTREAM_ZONE=YES\n\n# STUB\nHTTP_STUB_STATUS=YES\n\nMAIL=NO\nMAIL_SSL=NO\nMAIL_POP3=YES\nMAIL_IMAP=YES\nMAIL_SMTP=YES\n\n# procs module\nPROCS=YES\n\nSTREAM=NO\nSTREAM_SSL=NO\nSTREAM_REALIP=NO\nSTREAM_LIMIT_CONN=YES\nSTREAM_ACCESS=YES\nSTREAM_GEO=YES\nSTREAM_GEOIP=NO\nSTREAM_MAP=YES\nSTREAM_SPLIT_CLIENTS=YES\nSTREAM_RETURN=YES\nSTREAM_SET=YES\nSTREAM_UPSTREAM_HASH=YES\nSTREAM_UPSTREAM_LEAST_CONN=YES\nSTREAM_UPSTREAM_RANDOM=YES\nSTREAM_UPSTREAM_ZONE=YES\nSTREAM_SSL_PREREAD=NO\nSTREAM_SNI=NO\n\nDYNAMIC_MODULES=\nDYNAMIC_MODULES_SRCS=\n\nNGX_ADDONS=\nNGX_ADDON_SRCS=\nNGX_ADDON_DEPS=\nDYNAMIC_ADDONS=\n\nNGX_COMPAT=NO\n\nUSE_PCRE=NO\nPCRE=NONE\nPCRE_OPT=\nPCRE_CONF_OPT=\nPCRE_JIT=NO\nPCRE2=YES\n\nUSE_OPENSSL=NO\nOPENSSL=NONE\n\nUSE_ZLIB=NO\nZLIB=NONE\nZLIB_OPT=\nZLIB_ASM=NO\n\nUSE_PERL=NO\nNGX_PERL=perl\n\nLUAJIT_INC=$LUAJIT_INC\nLUAJIT_LIB=$LUAJIT_LIB\nLUA_INC=$LUA_INC\nLUA_LIB=$LUA_LIB\n\nXUDP_INC=$XUDP_INC\nXUDP_LIB=$XUDP_LIB\nXQUIC_XDP_INC=$XQUIC_XDP_INC\n\nXQUIC_INC=$XQUIC_INC\nXQUIC_LIB=$XQUIC_LIB\n\nUSE_LIBXSLT=NO\nUSE_LIBGD=NO\nUSE_GEOIP=NO\n\nNGX_GOOGLE_PERFTOOLS=NO\nNGX_CPP_TEST=NO\n\nNGX_LIBATOMIC=NO\n\nUSE_JEMALLOC=NO\nJEMALLOC=NONE\n\nNGX_CPU_CACHE_LINE=\n\nNGX_POST_CONF_MSG=\n\nopt=\n\nfor option\ndo\n    opt=\"$opt `echo $option | sed -e \\\"s/\\(--[^=]*=\\)\\(.* .*\\)/\\1'\\2'/\\\"`\"\n\n    case \"$option\" in\n        -*=*) value=`echo \"$option\" | sed -e 's/[-_a-zA-Z0-9]*=//'` ;;\n           *) value=\"\" ;;\n    esac\n\n    case \"$option\" in\n        --help)                          help=yes                   ;;\n\n        --prefix=)                       NGX_PREFIX=\"!\"             ;;\n        --prefix=*)                      NGX_PREFIX=\"$value\"        ;;\n        --sbin-path=*)                   NGX_SBIN_PATH=\"$value\"     ;;\n        --modules-path=*)                NGX_MODULES_PATH=\"$value\"  ;;\n        --conf-path=*)                   NGX_CONF_PATH=\"$value\"     ;;\n        --error-log-path=*)              NGX_ERROR_LOG_PATH=\"$value\";;\n        --pid-path=*)                    NGX_PID_PATH=\"$value\"      ;;\n        --lock-path=*)                   NGX_LOCK_PATH=\"$value\"     ;;\n        --user=*)                        NGX_USER=\"$value\"          ;;\n        --group=*)                       NGX_GROUP=\"$value\"         ;;\n\n        --crossbuild=*)                  NGX_PLATFORM=\"$value\"      ;;\n\n        --build=*)                       NGX_BUILD=\"$value\"         ;;\n        --builddir=*)                    NGX_OBJS=\"$value\"          ;;\n\n        --with-select_module)            EVENT_SELECT=YES           ;;\n        --without-select_module)         EVENT_SELECT=NONE          ;;\n        --with-poll_module)              EVENT_POLL=YES             ;;\n        --without-poll_module)           EVENT_POLL=NONE            ;;\n\n        --with-threads)                  USE_THREADS=YES            ;;\n\n        # PROCS\n        --without-procs)                 PROCS=NO                   ;;\n\n        --with-file-aio)                 NGX_FILE_AIO=YES           ;;\n\n        --with-ipv6)\n            NGX_POST_CONF_MSG=\"$NGX_POST_CONF_MSG\n$0: warning: the \\\"--with-ipv6\\\" option is deprecated\"\n        ;;\n\n        --without-http)                  HTTP=NO                    ;;\n        --without-http-cache)            HTTP_CACHE=NO              ;;\n\n        --http-log-path=*)               NGX_HTTP_LOG_PATH=\"$value\" ;;\n        --http-client-body-temp-path=*)  NGX_HTTP_CLIENT_TEMP_PATH=\"$value\" ;;\n        --http-proxy-temp-path=*)        NGX_HTTP_PROXY_TEMP_PATH=\"$value\" ;;\n        --http-fastcgi-temp-path=*)      NGX_HTTP_FASTCGI_TEMP_PATH=\"$value\" ;;\n        --http-uwsgi-temp-path=*)        NGX_HTTP_UWSGI_TEMP_PATH=\"$value\" ;;\n        --http-scgi-temp-path=*)         NGX_HTTP_SCGI_TEMP_PATH=\"$value\" ;;\n\n        --with-http_ssl_module)          HTTP_SSL=YES               ;;\n        --with-http_v2_module)           HTTP_V2=YES                ;;\n        --with-http_realip_module)       HTTP_REALIP=YES            ;;\n        --with-http_addition_module)     HTTP_ADDITION=YES          ;;\n        --with-http_xslt_module)         HTTP_XSLT=YES              ;;\n        --with-http_xslt_module=dynamic) HTTP_XSLT=DYNAMIC          ;;\n        --with-http_image_filter_module) HTTP_IMAGE_FILTER=YES      ;;\n        --with-http_image_filter_module=dynamic)\n                                         HTTP_IMAGE_FILTER=DYNAMIC  ;;\n        --with-http_geoip_module)        HTTP_GEOIP=YES             ;;\n        --with-http_geoip_module=dynamic)\n                                         HTTP_GEOIP=DYNAMIC         ;;\n        --with-http_sub_module)          HTTP_SUB=YES               ;;\n        --with-http_dav_module)          HTTP_DAV=YES               ;;\n        --with-http_flv_module)          HTTP_FLV=YES               ;;\n        --with-http_mp4_module)          HTTP_MP4=YES               ;;\n        --with-http_gunzip_module)       HTTP_GUNZIP=YES            ;;\n        --with-http_gzip_static_module)  HTTP_GZIP_STATIC=YES       ;;\n        --with-http_auth_request_module) HTTP_AUTH_REQUEST=YES      ;;\n        --with-http_random_index_module) HTTP_RANDOM_INDEX=YES      ;;\n        --with-http_secure_link_module)  HTTP_SECURE_LINK=YES       ;;\n        --with-http_degradation_module)  HTTP_DEGRADATION=YES       ;;\n        --with-http_slice_module)        HTTP_SLICE=YES             ;;\n\n        --without-http_charset_module)   HTTP_CHARSET=NO            ;;\n        --without-http_gzip_module)      HTTP_GZIP=NO               ;;\n        --without-http_ssi_module)       HTTP_SSI=NO                ;;\n        --without-http_userid_module)    HTTP_USERID=NO             ;;\n        --without-http_access_module)    HTTP_ACCESS=NO             ;;\n        --without-http_auth_basic_module) HTTP_AUTH_BASIC=NO        ;;\n        --without-http_mirror_module)    HTTP_MIRROR=NO             ;;\n        --without-http_autoindex_module) HTTP_AUTOINDEX=NO          ;;\n        --without-http_status_module)    HTTP_STATUS=NO             ;;\n        --without-http_geo_module)       HTTP_GEO=NO                ;;\n        --without-http_map_module)       HTTP_MAP=NO                ;;\n        --without-http_split_clients_module) HTTP_SPLIT_CLIENTS=NO  ;;\n        --without-http_referer_module)   HTTP_REFERER=NO            ;;\n        --without-http_rewrite_module)   HTTP_REWRITE=NO            ;;\n        --without-http_proxy_module)     HTTP_PROXY=NO              ;;\n        --without-http_fastcgi_module)   HTTP_FASTCGI=NO            ;;\n        --without-http_uwsgi_module)     HTTP_UWSGI=NO              ;;\n        --without-http_scgi_module)      HTTP_SCGI=NO               ;;\n        --without-http_grpc_module)      HTTP_GRPC=NO               ;;\n        --without-http_memcached_module) HTTP_MEMCACHED=NO          ;;\n        --without-http_limit_conn_module) HTTP_LIMIT_CONN=NO        ;;\n        --without-http_limit_req_module) HTTP_LIMIT_REQ=NO         ;;\n        --without-http_empty_gif_module) HTTP_EMPTY_GIF=NO          ;;\n        --without-http_browser_module)   HTTP_BROWSER=NO            ;;\n        --without-http_upstream_hash_module) HTTP_UPSTREAM_HASH=NO  ;;\n        --without-http_upstream_ip_hash_module) HTTP_UPSTREAM_IP_HASH=NO ;;\n        --without-http_upstream_least_conn_module)\n                                         HTTP_UPSTREAM_LEAST_CONN=NO ;;\n        --without-http_upstream_random_module)\n                                         HTTP_UPSTREAM_RANDOM=NO    ;;\n        --without-http_upstream_keepalive_module) HTTP_UPSTREAM_KEEPALIVE=NO ;;\n        --without-http_upstream_zone_module) HTTP_UPSTREAM_ZONE=NO  ;;\n\n        --with-http_perl_module)         HTTP_PERL=YES              ;;\n        --with-http_perl_module=dynamic) HTTP_PERL=DYNAMIC          ;;\n        --with-perl_modules_path=*)      NGX_PERL_MODULES=\"$value\"  ;;\n        --with-perl=*)                   NGX_PERL=\"$value\"          ;;\n\n        --with-xudp-inc=*)               XUDP_INC=\"$value\"          ;;\n        --with-xudp-lib=*)               XUDP_LIB=\"$value\"          ;;\n        --with-xquic_xdp-inc=*)          XQUIC_XDP_INC=\"$value\"     ;;\n\n        --with-xquic-inc=*)              XQUIC_INC=\"$value\"         ;;\n        --with-xquic-lib=*)              XQUIC_LIB=\"$value\"         ;;\n\n        # LUA\n        --with-http_lua_module)          HTTP_LUA=YES               ;;\n        --with-luajit-inc=*)             LUAJIT_INC=\"$value\"        ;;\n        --with-luajit-lib=*)             LUAJIT_LIB=\"$value\"        ;;\n        --with-lua-inc=*)                LUA_INC=\"$value\"           ;;\n        --with-lua-lib=*)                LUA_LIB=\"$value\"           ;;\n\n        # STUB\n        --with-http_stub_status_module)  HTTP_STUB_STATUS=YES       ;;\n\n        --with-mail)                     MAIL=YES                   ;;\n        --with-mail=dynamic)             MAIL=DYNAMIC               ;;\n        --with-mail_ssl_module)          MAIL_SSL=YES               ;;\n        # STUB\n        --with-imap)\n            MAIL=YES\n            NGX_POST_CONF_MSG=\"$NGX_POST_CONF_MSG\n$0: warning: the \\\"--with-imap\\\" option is deprecated, \\\nuse the \\\"--with-mail\\\" option instead\"\n        ;;\n        --with-imap_ssl_module)\n            MAIL_SSL=YES\n            NGX_POST_CONF_MSG=\"$NGX_POST_CONF_MSG\n$0: warning: the \\\"--with-imap_ssl_module\\\" option is deprecated, \\\nuse the \\\"--with-mail_ssl_module\\\" option instead\"\n        ;;\n        --without-mail_pop3_module)      MAIL_POP3=NO               ;;\n        --without-mail_imap_module)      MAIL_IMAP=NO               ;;\n        --without-mail_smtp_module)      MAIL_SMTP=NO               ;;\n\n        --with-stream)                   STREAM=YES                 ;;\n        --with-stream=dynamic)           STREAM=DYNAMIC             ;;\n        --with-stream_ssl_module)        STREAM_SSL=YES             ;;\n        --with-stream_realip_module)     STREAM_REALIP=YES          ;;\n        --with-stream_geoip_module)      STREAM_GEOIP=YES           ;;\n        --with-stream_geoip_module=dynamic)\n                                         STREAM_GEOIP=DYNAMIC       ;;\n        --with-stream_ssl_preread_module)\n                                         STREAM_SSL_PREREAD=YES     ;;\n        --with-stream_sni)               STREAM_SNI=YES             ;;\n        --without-stream_limit_conn_module)\n                                         STREAM_LIMIT_CONN=NO       ;;\n        --without-stream_access_module)  STREAM_ACCESS=NO           ;;\n        --without-stream_geo_module)     STREAM_GEO=NO              ;;\n        --without-stream_map_module)     STREAM_MAP=NO              ;;\n        --without-stream_split_clients_module)\n                                         STREAM_SPLIT_CLIENTS=NO    ;;\n        --without-stream_return_module)  STREAM_RETURN=NO           ;;\n        --without-stream_set_module)     STREAM_SET=NO              ;;\n        --without-stream_upstream_hash_module)\n                                         STREAM_UPSTREAM_HASH=NO    ;;\n        --without-stream_upstream_least_conn_module)\n                                         STREAM_UPSTREAM_LEAST_CONN=NO ;;\n        --without-stream_upstream_random_module)\n                                         STREAM_UPSTREAM_RANDOM=NO  ;;\n        --without-stream_upstream_zone_module)\n                                         STREAM_UPSTREAM_ZONE=NO    ;;\n\n        --with-google_perftools_module)  NGX_GOOGLE_PERFTOOLS=YES   ;;\n        --with-cpp_test_module)          NGX_CPP_TEST=YES           ;;\n\n        --add-module=*)                  NGX_ADDONS=\"$NGX_ADDONS $value\" ;;\n        --add-dynamic-module=*)          DYNAMIC_ADDONS=\"$DYNAMIC_ADDONS $value\" ;;\n\n        --with-compat)                   NGX_COMPAT=YES             ;;\n\n        --with-cc=*)                     CC=\"$value\"                ;;\n        --with-cpp=*)                    CPP=\"$value\"               ;;\n        --with-cc-opt=*)                 NGX_CC_OPT=\"$value\"        ;;\n        --with-ld-opt=*)                 NGX_LD_OPT=\"$value\"        ;;\n        --with-cpu-opt=*)                CPU=\"$value\"               ;;\n        --with-debug)                    NGX_DEBUG=YES              ;;\n        --with-openssl-async)            NGX_SSL_ASYNC=YES          ;;\n\n        --without-pcre)                  USE_PCRE=DISABLED          ;;\n        --with-pcre)                     USE_PCRE=YES               ;;\n        --with-pcre=*)                   PCRE=\"$value\"              ;;\n        --with-pcre-opt=*)               PCRE_OPT=\"$value\"          ;;\n        --with-pcre-jit)                 PCRE_JIT=YES               ;;\n        --without-pcre2)                 PCRE2=DISABLED             ;;\n\n        --with-openssl=*)                OPENSSL=\"$value\"           ;;\n        --with-openssl-opt=*)            OPENSSL_OPT=\"$value\"       ;;\n\n        --with-md5=*)\n            NGX_POST_CONF_MSG=\"$NGX_POST_CONF_MSG\n$0: warning: the \\\"--with-md5\\\" option is deprecated\"\n        ;;\n        --with-md5-opt=*)\n            NGX_POST_CONF_MSG=\"$NGX_POST_CONF_MSG\n$0: warning: the \\\"--with-md5-opt\\\" option is deprecated\"\n        ;;\n        --with-md5-asm)\n            NGX_POST_CONF_MSG=\"$NGX_POST_CONF_MSG\n$0: warning: the \\\"--with-md5-asm\\\" option is deprecated\"\n        ;;\n\n        --with-sha1=*)\n            NGX_POST_CONF_MSG=\"$NGX_POST_CONF_MSG\n$0: warning: the \\\"--with-sha1\\\" option is deprecated\"\n        ;;\n        --with-sha1-opt=*)\n            NGX_POST_CONF_MSG=\"$NGX_POST_CONF_MSG\n$0: warning: the \\\"--with-sha1-opt\\\" option is deprecated\"\n        ;;\n        --with-sha1-asm)\n            NGX_POST_CONF_MSG=\"$NGX_POST_CONF_MSG\n$0: warning: the \\\"--with-sha1-asm\\\" option is deprecated\"\n        ;;\n\n        --with-zlib=*)                   ZLIB=\"$value\"              ;;\n        --with-zlib-opt=*)               ZLIB_OPT=\"$value\"          ;;\n        --with-zlib-asm=*)               ZLIB_ASM=\"$value\"          ;;\n\n        --with-libatomic)                NGX_LIBATOMIC=YES          ;;\n        --with-libatomic=*)              NGX_LIBATOMIC=\"$value\"     ;;\n\n        --with-jemalloc)                 USE_JEMALLOC=YES           ;;\n        --with-jemalloc=*)               JEMALLOC=\"$value\"          ;;\n\n        --without-http-upstream-rbtree)  HTTP_UPSTREAM_RBTREE=NO    ;;\n\n        --test-build-devpoll)            NGX_TEST_BUILD_DEVPOLL=YES ;;\n        --test-build-eventport)          NGX_TEST_BUILD_EVENTPORT=YES ;;\n        --test-build-epoll)              NGX_TEST_BUILD_EPOLL=YES   ;;\n        --test-build-solaris-sendfilev)  NGX_TEST_BUILD_SOLARIS_SENDFILEV=YES ;;\n\n        *)\n            echo \"$0: error: invalid option \\\"$option\\\"\"\n            exit 1\n        ;;\n    esac\ndone\n\n\nNGX_CONFIGURE=\"$opt\"\n\n\nif [ $help = yes ]; then\n\ncat << END\n\n  --help                             print this message\n\n  --prefix=PATH                      set installation prefix\n  --sbin-path=PATH                   set nginx binary pathname\n  --modules-path=PATH                set modules path\n  --conf-path=PATH                   set nginx.conf pathname\n  --error-log-path=PATH              set error log pathname\n  --pid-path=PATH                    set nginx.pid pathname\n  --lock-path=PATH                   set nginx.lock pathname\n\n  --user=USER                        set non-privileged user for\n                                     worker processes\n  --group=GROUP                      set non-privileged group for\n                                     worker processes\n\n  --build=NAME                       set build name\n  --builddir=DIR                     set build directory\n\n  --with-select_module               enable select module\n  --without-select_module            disable select module\n  --with-poll_module                 enable poll module\n  --without-poll_module              disable poll module\n\n  --without-procs                    disable procs module\n\n  --with-threads                     enable thread pool support\n\n  --with-file-aio                    enable file AIO support\n\n  --with-http_ssl_module             enable ngx_http_ssl_module\n  --with-http_v2_module              enable ngx_http_v2_module\n  --with-http_realip_module          enable ngx_http_realip_module\n  --with-http_addition_module        enable ngx_http_addition_module\n  --with-http_xslt_module            enable ngx_http_xslt_module\n  --with-http_xslt_module=dynamic    enable dynamic ngx_http_xslt_module\n  --with-http_image_filter_module    enable ngx_http_image_filter_module\n  --with-http_image_filter_module=dynamic\n                                     enable dynamic ngx_http_image_filter_module\n  --with-http_geoip_module           enable ngx_http_geoip_module\n  --with-http_geoip_module=dynamic   enable dynamic ngx_http_geoip_module\n  --with-http_sub_module             enable ngx_http_sub_module\n  --with-http_dav_module             enable ngx_http_dav_module\n  --with-http_flv_module             enable ngx_http_flv_module\n  --with-http_mp4_module             enable ngx_http_mp4_module\n  --with-http_gunzip_module          enable ngx_http_gunzip_module\n  --with-http_gzip_static_module     enable ngx_http_gzip_static_module\n  --with-http_auth_request_module    enable ngx_http_auth_request_module\n  --with-http_random_index_module    enable ngx_http_random_index_module\n  --with-http_secure_link_module     enable ngx_http_secure_link_module\n  --with-http_degradation_module     enable ngx_http_degradation_module\n  --with-http_slice_module           enable ngx_http_slice_module\n  --with-http_stub_status_module     enable ngx_http_stub_status_module\n\n  --without-http_charset_module      disable ngx_http_charset_module\n  --without-http_gzip_module         disable ngx_http_gzip_module\n  --without-http_ssi_module          disable ngx_http_ssi_module\n  --without-http_ssl_module          disable ngx_http_ssl_module\n  --without-http_userid_module       disable ngx_http_userid_module\n  --without-http_access_module       disable ngx_http_access_module\n  --without-http_auth_basic_module   disable ngx_http_auth_basic_module\n  --without-http_mirror_module       disable ngx_http_mirror_module\n  --without-http_autoindex_module    disable ngx_http_autoindex_module\n  --without-http_geo_module          disable ngx_http_geo_module\n  --without-http_map_module          disable ngx_http_map_module\n  --without-http_split_clients_module disable ngx_http_split_clients_module\n  --without-http_referer_module      disable ngx_http_referer_module\n  --without-http_rewrite_module      disable ngx_http_rewrite_module\n  --without-http_proxy_module        disable ngx_http_proxy_module\n  --without-http_fastcgi_module      disable ngx_http_fastcgi_module\n  --without-http_uwsgi_module        disable ngx_http_uwsgi_module\n  --without-http_scgi_module         disable ngx_http_scgi_module\n  --without-http_grpc_module         disable ngx_http_grpc_module\n  --without-http_memcached_module    disable ngx_http_memcached_module\n  --without-http_limit_conn_module   disable ngx_http_limit_conn_module\n  --without-http_limit_req_module    disable ngx_http_limit_req_module\n  --without-http_empty_gif_module    disable ngx_http_empty_gif_module\n  --without-http_browser_module      disable ngx_http_browser_module\n  --without-http_stub_status_module  disable ngx_http_stub_status_module\n  --without-http_upstream_hash_module\n                                     disable ngx_http_upstream_hash_module\n  --without-http_upstream_ip_hash_module\n                                     disable ngx_http_upstream_ip_hash_module\n  --without-http_upstream_least_conn_module\n                                     disable ngx_http_upstream_least_conn_module\n  --without-http_upstream_random_module\n                                     disable ngx_http_upstream_random_module\n  --without-http_upstream_keepalive_module\n                                     disable ngx_http_upstream_keepalive_module\n  --without-http_upstream_zone_module\n                                     disable ngx_http_upstream_zone_module\n\n  --with-http_perl_module            enable ngx_http_perl_module\n  --with-http_perl_module=dynamic    enable dynamic ngx_http_perl_module\n  --with-perl_modules_path=PATH      set Perl modules path\n  --with-perl=PATH                   set perl binary pathname\n\n  --without-http-upstream-rbtree     disable using rbtree for upstream lookup\n\n  --with-http_lua_module             enable ngx_http_lua_module (will also enable --with-md5 and --with-sha1)\n  --with-luajit-inc=PATH             set LuaJIT headers path (where lua.h/lauxlib.h/... are located)\n  --with-luajit-lib=PATH             set LuaJIT library path (where libluajit-5.1.{a,so} are located)\n  --with-lua-inc=PATH                set Lua headers path (where lua.h/lauxlib.h/... are located)\n  --with-lua-lib=PATH                set Lua library path (where liblua.{a,so} are located, only support Lua-5.1.x)\n  --http-log-path=PATH               set http access log pathname\n  --http-client-body-temp-path=PATH  set path to store\n                                     http client request body temporary files\n  --http-proxy-temp-path=PATH        set path to store\n                                     http proxy temporary files\n  --http-fastcgi-temp-path=PATH      set path to store\n                                     http fastcgi temporary files\n  --http-uwsgi-temp-path=PATH        set path to store\n                                     http uwsgi temporary files\n  --http-scgi-temp-path=PATH         set path to store\n                                     http scgi temporary files\n\n  --without-http                     disable HTTP server\n  --without-http-cache               disable HTTP cache\n\n  --with-mail                        enable POP3/IMAP4/SMTP proxy module\n  --with-mail=dynamic                enable dynamic POP3/IMAP4/SMTP proxy module\n  --with-mail_ssl_module             enable ngx_mail_ssl_module\n  --without-mail_pop3_module         disable ngx_mail_pop3_module\n  --without-mail_imap_module         disable ngx_mail_imap_module\n  --without-mail_smtp_module         disable ngx_mail_smtp_module\n\n  --with-xquic-inc=PATH              set XQUIC headers path\n  --with-xquic-lib=PATH              set XQUIC library path (where libxquic.so is located)\n  --with-xquic-link=PATH             set XQUIC headers link path\n\n  --with-stream                      enable TCP/UDP proxy module\n  --with-stream=dynamic              enable dynamic TCP/UDP proxy module\n  --with-stream_ssl_module           enable ngx_stream_ssl_module\n  --with-stream_realip_module        enable ngx_stream_realip_module\n  --with-stream_geoip_module         enable ngx_stream_geoip_module\n  --with-stream_geoip_module=dynamic enable dynamic ngx_stream_geoip_module\n  --with-stream_ssl_preread_module   enable ngx_stream_ssl_preread_module\n  --with-stream_sni                  enable dynamic server block\n  --without-stream_limit_conn_module disable ngx_stream_limit_conn_module\n  --without-stream_access_module     disable ngx_stream_access_module\n  --without-stream_geo_module        disable ngx_stream_geo_module\n  --without-stream_map_module        disable ngx_stream_map_module\n  --without-stream_split_clients_module\n                                     disable ngx_stream_split_clients_module\n  --without-stream_return_module     disable ngx_stream_return_module\n  --without-stream_set_module        disable ngx_stream_set_module\n  --without-stream_upstream_hash_module\n                                     disable ngx_stream_upstream_hash_module\n  --without-stream_upstream_least_conn_module\n                                     disable ngx_stream_upstream_least_conn_module\n  --without-stream_upstream_random_module\n                                     disable ngx_stream_upstream_random_module\n  --without-stream_upstream_zone_module\n                                     disable ngx_stream_upstream_zone_module\n\n  --with-google_perftools_module     enable ngx_google_perftools_module\n  --with-cpp_test_module             enable ngx_cpp_test_module\n\n  --add-module=PATH                  enable external module\n  --add-dynamic-module=PATH          enable dynamic external module\n\n  --with-compat                      dynamic modules compatibility\n\n  --with-cc=PATH                     set C compiler pathname\n  --with-cpp=PATH                    set C preprocessor pathname\n  --with-cc-opt=OPTIONS              set additional C compiler options\n  --with-ld-opt=OPTIONS              set additional linker options\n  --with-cpu-opt=CPU                 build for the specified CPU, valid values:\n                                     pentium, pentiumpro, pentium3, pentium4,\n                                     athlon, opteron, sparc32, sparc64, ppc64\n\n  --without-pcre                     disable PCRE library usage\n  --with-pcre                        force PCRE library usage\n  --with-pcre=DIR                    set path to PCRE library sources\n  --with-pcre-opt=OPTIONS            set additional build options for PCRE\n  --with-pcre-jit                    build PCRE with JIT compilation support\n  --without-pcre2                    do not use PCRE2 library\n\n  --with-zlib=DIR                    set path to zlib library sources\n  --with-zlib-opt=OPTIONS            set additional build options for zlib\n  --with-zlib-asm=CPU                use zlib assembler sources optimized\n                                     for the specified CPU, valid values:\n                                     pentium, pentiumpro\n\n  --with-libatomic                   force libatomic_ops library usage\n  --with-libatomic=DIR               set path to libatomic_ops library sources\n\n  --with-jemalloc                    force jemalloc library usage\n  --with-jemalloc=DIR                set path to jemalloc library files\n\n  --with-openssl=DIR                 set path to OpenSSL library sources\n  --with-openssl-opt=OPTIONS         set additional build options for OpenSSL\n  --with-openssl-async               enable asynchronous SSL/TLS mode for OpenSSL library\n  --with-debug                       enable debug logging\n\nEND\n\n    exit 1\nfi\n\n\nif [ \".$NGX_PLATFORM\" = \".win32\" ]; then\n    NGX_WINE=$WINE\nfi\n\n\nNGX_SBIN_PATH=${NGX_SBIN_PATH:-sbin/nginx}\nNGX_MODULES_PATH=${NGX_MODULES_PATH:-modules}\nNGX_CONF_PATH=${NGX_CONF_PATH:-conf/nginx.conf}\nNGX_CONF_PREFIX=`dirname $NGX_CONF_PATH`\nNGX_PID_PATH=${NGX_PID_PATH:-logs/nginx.pid}\nNGX_LOCK_PATH=${NGX_LOCK_PATH:-logs/nginx.lock}\n\nif [ \".$NGX_ERROR_LOG_PATH\" = \".stderr\" ]; then\n    NGX_ERROR_LOG_PATH=\nelse\n    NGX_ERROR_LOG_PATH=${NGX_ERROR_LOG_PATH:-logs/error.log}\nfi\n\nNGX_HTTP_LOG_PATH=${NGX_HTTP_LOG_PATH:-logs/access.log}\nNGX_HTTP_CLIENT_TEMP_PATH=${NGX_HTTP_CLIENT_TEMP_PATH:-client_body_temp}\nNGX_HTTP_PROXY_TEMP_PATH=${NGX_HTTP_PROXY_TEMP_PATH:-proxy_temp}\nNGX_HTTP_FASTCGI_TEMP_PATH=${NGX_HTTP_FASTCGI_TEMP_PATH:-fastcgi_temp}\nNGX_HTTP_UWSGI_TEMP_PATH=${NGX_HTTP_UWSGI_TEMP_PATH:-uwsgi_temp}\nNGX_HTTP_SCGI_TEMP_PATH=${NGX_HTTP_SCGI_TEMP_PATH:-scgi_temp}\n\ncase \".$NGX_PERL_MODULES\" in\n    ./*)\n    ;;\n\n    .)\n    ;;\n\n    *)\n        NGX_PERL_MODULES=$NGX_PREFIX/$NGX_PERL_MODULES\n    ;;\nesac\n"
  },
  {
    "path": "auto/os/conf",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\necho \"checking for $NGX_SYSTEM specific features\"\n\ncase \"$NGX_PLATFORM\" in\n\n    FreeBSD:*)\n        . auto/os/freebsd\n    ;;\n\n    Linux:*)\n        . auto/os/linux\n    ;;\n\n    SunOS:*)\n        . auto/os/solaris\n    ;;\n\n    Darwin:*)\n        . auto/os/darwin\n    ;;\n\n    win32)\n        . auto/os/win32\n    ;;\n\n    DragonFly:*)\n        have=NGX_FREEBSD . auto/have_headers\n        CORE_INCS=\"$UNIX_INCS\"\n        CORE_DEPS=\"$UNIX_DEPS $FREEBSD_DEPS\"\n        CORE_SRCS=\"$UNIX_SRCS $FREEBSD_SRCS\"\n\n        echo \" + sendfile() found\"\n        have=NGX_HAVE_SENDFILE . auto/have\n        CORE_SRCS=\"$CORE_SRCS $FREEBSD_SENDFILE_SRCS\"\n\n        ngx_spacer='\n'\n    ;;\n\n    NetBSD:*)\n        CORE_INCS=\"$UNIX_INCS\"\n        CORE_DEPS=\"$UNIX_DEPS $POSIX_DEPS\"\n        CORE_SRCS=\"$UNIX_SRCS\"\n\n        NGX_RPATH=YES\n    ;;\n\n    HP-UX:*)\n        # HP/UX\n        have=NGX_HPUX . auto/have_headers\n        CORE_INCS=\"$UNIX_INCS\"\n        CORE_DEPS=\"$UNIX_DEPS $POSIX_DEPS\"\n        CORE_SRCS=\"$UNIX_SRCS\"\n        CC_AUX_FLAGS=\"$CC_AUX_FLAGS -D_XOPEN_SOURCE -D_XOPEN_SOURCE_EXTENDED=1\"\n        CC_AUX_FLAGS=\"$CC_AUX_FLAGS -D_HPUX_ALT_XOPEN_SOCKET_API\"\n    ;;\n\n    OSF1:*)\n        # Tru64 UNIX\n        have=NGX_TRU64 . auto/have_headers\n        have=NGX_HAVE_STRERROR_R . auto/nohave\n        CORE_INCS=\"$UNIX_INCS\"\n        CORE_DEPS=\"$UNIX_DEPS $POSIX_DEPS\"\n        CORE_SRCS=\"$UNIX_SRCS\"\n    ;;\n\n    GNU:*)\n        # GNU Hurd\n        have=NGX_GNU_HURD . auto/have_headers\n        CORE_INCS=\"$UNIX_INCS\"\n        CORE_DEPS=\"$UNIX_DEPS $POSIX_DEPS\"\n        CORE_SRCS=\"$UNIX_SRCS\"\n        CC_AUX_FLAGS=\"$CC_AUX_FLAGS -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64\"\n    ;;\n\n    *)\n        CORE_INCS=\"$UNIX_INCS\"\n        CORE_DEPS=\"$UNIX_DEPS $POSIX_DEPS\"\n        CORE_SRCS=\"$UNIX_SRCS\"\n    ;;\n\nesac\n\n\ncase \"$NGX_MACHINE\" in\n\n    i386 | i686 | i86pc)\n        have=NGX_HAVE_NONALIGNED . auto/have\n        NGX_MACH_CACHE_LINE=32\n    ;;\n\n    amd64 | x86_64)\n        have=NGX_HAVE_NONALIGNED . auto/have\n        NGX_MACH_CACHE_LINE=64\n    ;;\n\n    sun4u | sun4v | sparc | sparc64)\n        have=NGX_ALIGNMENT value=16 . auto/define\n        # TODO\n        NGX_MACH_CACHE_LINE=64\n    ;;\n\n    ia64 )\n        have=NGX_ALIGNMENT value=16 . auto/define\n        # TODO\n        NGX_MACH_CACHE_LINE=64\n    ;;\n\n    aarch64 | arm64)\n        have=NGX_ALIGNMENT value=16 . auto/define\n        NGX_MACH_CACHE_LINE=64\n    ;;\n\n    *)\n        have=NGX_ALIGNMENT value=16 . auto/define\n        NGX_MACH_CACHE_LINE=32\n    ;;\n\nesac\n\nif test -z \"$NGX_CPU_CACHE_LINE\"; then\n    NGX_CPU_CACHE_LINE=$NGX_MACH_CACHE_LINE\nfi\n\nhave=NGX_CPU_CACHE_LINE value=$NGX_CPU_CACHE_LINE . auto/define\n"
  },
  {
    "path": "auto/os/darwin",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\nhave=NGX_DARWIN . auto/have_headers\n\nCORE_INCS=\"$UNIX_INCS\"\nCORE_DEPS=\"$UNIX_DEPS $DARWIN_DEPS\"\nCORE_SRCS=\"$UNIX_SRCS $DARWIN_SRCS\"\n\n\n\nngx_spacer='\n'\n\nMAIN_LINK=\nMODULE_LINK=\"-shared -Wl,-undefined,dynamic_lookup\"\n\nCC_AUX_FLAGS=\"$CC_AUX_FLAGS -D__APPLE_USE_RFC_3542\"\n\n\n# kqueue\n\necho \" + kqueue found\"\nhave=NGX_HAVE_KQUEUE . auto/have\nhave=NGX_HAVE_CLEAR_EVENT . auto/have\nEVENT_MODULES=\"$EVENT_MODULES $KQUEUE_MODULE\"\nCORE_SRCS=\"$CORE_SRCS $KQUEUE_SRCS\"\nEVENT_FOUND=YES\nNGX_KQUEUE_CHECKED=YES\n\nngx_feature=\"kqueue's EVFILT_TIMER\"\nngx_feature_name=\"NGX_HAVE_TIMER_EVENT\"\nngx_feature_run=yes\nngx_feature_incs=\"#include <sys/event.h>\n                  #include <sys/time.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"int      kq;\n                  struct kevent    kev;\n                  struct timespec  ts;\n\n                  if ((kq = kqueue()) == -1) return 1;\n\n                  kev.ident = 0;\n                  kev.filter = EVFILT_TIMER;\n                  kev.flags = EV_ADD|EV_ENABLE;\n                  kev.fflags = 0;\n                  kev.data = 1000;\n                  kev.udata = 0;\n\n                  ts.tv_sec = 0;\n                  ts.tv_nsec = 0;\n\n                  if (kevent(kq, &kev, 1, &kev, 1, &ts) == -1) return 1;\n\n                  if (kev.flags & EV_ERROR) return 1;\"\n\n. auto/feature\n\n\nngx_feature=\"Darwin 64-bit kqueue millisecond timeout bug\"\nngx_feature_name=NGX_DARWIN_KEVENT_BUG\nngx_feature_run=bug\nngx_feature_incs=\"#include <sys/event.h>\n                  #include <sys/time.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"int  kq;\n                  struct kevent    kev;\n                  struct timespec  ts;\n                  struct timeval   tv, tv0;\n\n                  kq = kqueue();\n\n                  ts.tv_sec = 0;\n                  ts.tv_nsec = 999000000;\n\n                  gettimeofday(&tv, 0);\n                  kevent(kq, NULL, 0, &kev, 1, &ts);\n                  gettimeofday(&tv0, 0);\n                  timersub(&tv0, &tv, &tv);\n\n                  if (tv.tv_sec * 1000000 + tv.tv_usec < 900000) return 1;\"\n\n. auto/feature\n\n\n# sendfile()\n\nngx_feature=\"sendfile()\"\nngx_feature_name=\"NGX_HAVE_SENDFILE\"\nngx_feature_run=yes\nngx_feature_incs=\"#include <sys/types.h>\n                  #include <sys/socket.h>\n                  #include <sys/uio.h>\n                  #include <sys/errno.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"int s = 0, fd = 1;\n                  off_t n; off_t off = 0;\n                  n = sendfile(s, fd, off, &n, NULL, 0);\n                  if (n == -1 && errno == ENOSYS) return 1\"\n. auto/feature\n\nif [ $ngx_found = yes ]; then\n    CORE_SRCS=\"$CORE_SRCS $DARWIN_SENDFILE_SRCS\"\nfi\n\n\nngx_feature=\"atomic(3)\"\nngx_feature_name=NGX_DARWIN_ATOMIC\nngx_feature_run=no\nngx_feature_incs=\"#include <libkern/OSAtomic.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"int32_t  lock = 0;\n                  if (!OSAtomicCompareAndSwap32Barrier(0, 1, &lock)) return 1\"\n. auto/feature\n"
  },
  {
    "path": "auto/os/freebsd",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\nhave=NGX_FREEBSD . auto/have_headers\n\nCORE_INCS=\"$UNIX_INCS\"\nCORE_DEPS=\"$UNIX_DEPS $FREEBSD_DEPS\"\nCORE_SRCS=\"$UNIX_SRCS $FREEBSD_SRCS\"\n\nngx_spacer='\n'\n\n\n# __FreeBSD_version and sysctl kern.osreldate are the best ways\n# to determine whether some capability exists and is safe to use.\n# __FreeBSD_version is used for the testing of the build environment.\n# sysctl kern.osreldate is used for the testing of the kernel capabilities.\n\nversion=`grep \"#define __FreeBSD_version\" /usr/include/osreldate.h \\\n         | sed -e 's/^.* \\(.*\\)$/\\1/'`\n\nosreldate=`/sbin/sysctl -n kern.osreldate`\n\n\n# setproctitle() in libutil\n\nif [ \\( $version -ge 500000 -a $version -lt 500012 \\) \\\n     -o $version -lt 410002 ]\nthen\n    echo \" + setproctitle() in libutil\"\n\n    CORE_LIBS=\"$CORE_LIBS -lutil\"\n    NGX_SETPROCTITLE_LIB=\"-lutil\"\nfi\n\n# sendfile\n\nif [ $osreldate -gt 300007 ]; then\n    echo \" + sendfile() found\"\n\n    have=NGX_HAVE_SENDFILE . auto/have\n    CORE_SRCS=\"$CORE_SRCS $FREEBSD_SENDFILE_SRCS\"\nfi\n\nif [ $osreldate -gt 1100093 ]; then\n    echo \" + sendfile()'s SF_NODISKIO found\"\n\n    have=NGX_HAVE_SENDFILE_NODISKIO . auto/have\nfi\n\n# POSIX semaphores\n# http://www.freebsd.org/cgi/query-pr.cgi?pr=kern/127545\n\nif [ $osreldate -ge 701106 ]; then\n    echo \" + POSIX semaphores should work\"\nelse\n    have=NGX_HAVE_POSIX_SEM . auto/nohave\nfi\n\n\n# kqueue\n\nif [ \\( $osreldate -lt 500000 -a $osreldate -ge 410000 \\) \\\n     -o $osreldate -ge 500011 ]\nthen\n    echo \" + kqueue found\"\n\n    have=NGX_HAVE_KQUEUE . auto/have\n    have=NGX_HAVE_CLEAR_EVENT . auto/have\n    EVENT_MODULES=\"$EVENT_MODULES $KQUEUE_MODULE\"\n    CORE_SRCS=\"$CORE_SRCS $KQUEUE_SRCS\"\n    EVENT_FOUND=YES\nfi\n\n\nNGX_KQUEUE_CHECKED=YES\n\n\n# kqueue's NOTE_LOWAT\n\nif [ \\( $version -lt 500000 -a $version -ge 430000 \\) \\\n     -o $version -ge 500018 ]\nthen\n    echo \" + kqueue's NOTE_LOWAT found\"\n    have=NGX_HAVE_LOWAT_EVENT . auto/have\nfi\n\n# kqueue's EVFILT_TIMER\n\nif [ \\( $version -lt 500000 -a $version -ge 440001 \\) \\\n     -o $version -ge 500023 ]\nthen\n    echo \" + kqueue's EVFILT_TIMER found\"\n    have=NGX_HAVE_TIMER_EVENT . auto/have\nfi\n\n\n# cpuset_setaffinity()\n\nif [ $version -ge 701000 ]; then\n    echo \" + cpuset_setaffinity() found\"\n    have=NGX_HAVE_CPUSET_SETAFFINITY . auto/have\nfi\n"
  },
  {
    "path": "auto/os/linux",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\nhave=NGX_LINUX . auto/have_headers\n\nCORE_INCS=\"$UNIX_INCS\"\nCORE_DEPS=\"$UNIX_DEPS $LINUX_DEPS\"\nCORE_SRCS=\"$UNIX_SRCS $LINUX_SRCS\"\n\nngx_spacer='\n'\n\ncc_aux_flags=\"$CC_AUX_FLAGS\"\nCC_AUX_FLAGS=\"$cc_aux_flags -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64\"\n\n\n# Linux kernel version\n\nversion=$((`uname -r \\\n    | sed -n -e 's/^\\([0-9][0-9]*\\)\\.\\([0-9][0-9]*\\)\\.\\([0-9][0-9]*\\).*/ \\\n                                                 \\1*256*256+\\2*256+\\3/p' \\\n             -e 's/^\\([0-9][0-9]*\\)\\.\\([0-9][0-9]*\\).*/\\1*256*256+\\2*256/p'`))\n\nversion=${version:-0}\n\n\n# posix_fadvise64() had been implemented in 2.5.60\n\nif [ $version -lt 132412 ]; then\n    have=NGX_HAVE_POSIX_FADVISE . auto/nohave\nfi\n\n# epoll, EPOLLET version\n\nngx_feature=\"epoll\"\nngx_feature_name=\"NGX_HAVE_EPOLL\"\nngx_feature_run=yes\nngx_feature_incs=\"#include <sys/epoll.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"int efd = 0;\n                  struct epoll_event ee;\n                  ee.events = EPOLLIN|EPOLLOUT|EPOLLET;\n                  ee.data.ptr = NULL;\n                  (void) ee;\n                  efd = epoll_create(100);\n                  if (efd == -1) return 1;\"\n. auto/feature\n\nif [ $ngx_found = yes ]; then\n    have=NGX_HAVE_CLEAR_EVENT . auto/have\n    CORE_SRCS=\"$CORE_SRCS $EPOLL_SRCS\"\n    EVENT_MODULES=\"$EVENT_MODULES $EPOLL_MODULE\"\n    EVENT_FOUND=YES\n\n\n    # EPOLLRDHUP appeared in Linux 2.6.17, glibc 2.8\n\n    ngx_feature=\"EPOLLRDHUP\"\n    ngx_feature_name=\"NGX_HAVE_EPOLLRDHUP\"\n    ngx_feature_run=no\n    ngx_feature_incs=\"#include <sys/epoll.h>\"\n    ngx_feature_path=\n    ngx_feature_libs=\n    ngx_feature_test=\"int efd = 0, fd = 0;\n                      struct epoll_event ee;\n                      ee.events = EPOLLIN|EPOLLRDHUP|EPOLLET;\n                      ee.data.ptr = NULL;\n                      epoll_ctl(efd, EPOLL_CTL_ADD, fd, &ee)\"\n    . auto/feature\n\n\n    # EPOLLEXCLUSIVE appeared in Linux 4.5, glibc 2.24\n\n    ngx_feature=\"EPOLLEXCLUSIVE\"\n    ngx_feature_name=\"NGX_HAVE_EPOLLEXCLUSIVE\"\n    ngx_feature_run=no\n    ngx_feature_incs=\"#include <sys/epoll.h>\"\n    ngx_feature_path=\n    ngx_feature_libs=\n    ngx_feature_test=\"int efd = 0, fd = 0;\n                      struct epoll_event ee;\n                      ee.events = EPOLLIN|EPOLLEXCLUSIVE;\n                      ee.data.ptr = NULL;\n                      epoll_ctl(efd, EPOLL_CTL_ADD, fd, &ee)\"\n    . auto/feature\n\n\n    # eventfd()\n\n    ngx_feature=\"eventfd()\"\n    ngx_feature_name=\"NGX_HAVE_EVENTFD\"\n    ngx_feature_run=no\n    ngx_feature_incs=\"#include <sys/eventfd.h>\"\n    ngx_feature_path=\n    ngx_feature_libs=\n    ngx_feature_test=\"(void) eventfd(0, 0)\"\n    . auto/feature\n\n    if [ $ngx_found = yes ]; then\n        have=NGX_HAVE_SYS_EVENTFD_H . auto/have\n    fi\n\n\n    if [ $ngx_found = no ]; then\n\n        ngx_feature=\"eventfd() (SYS_eventfd)\"\n        ngx_feature_incs=\"#include <sys/syscall.h>\"\n        ngx_feature_test=\"(void) SYS_eventfd\"\n        . auto/feature\n    fi\nfi\n\n\n# O_PATH and AT_EMPTY_PATH were introduced in 2.6.39, glibc 2.14\n\nngx_feature=\"O_PATH\"\nngx_feature_name=\"NGX_HAVE_O_PATH\"\nngx_feature_run=no\nngx_feature_incs=\"#include <sys/types.h>\n                  #include <sys/stat.h>\n                  #include <fcntl.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"int fd; struct stat sb;\n                  fd = openat(AT_FDCWD, \\\".\\\", O_PATH|O_DIRECTORY|O_NOFOLLOW);\n                  if (fstatat(fd, \\\"\\\", &sb, AT_EMPTY_PATH) != 0) return 1\"\n. auto/feature\n\n\n# sendfile()\n\nCC_AUX_FLAGS=\"$cc_aux_flags -D_GNU_SOURCE\"\nngx_feature=\"sendfile()\"\nngx_feature_name=\"NGX_HAVE_SENDFILE\"\nngx_feature_run=yes\nngx_feature_incs=\"#include <sys/sendfile.h>\n                  #include <errno.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"int s = 0, fd = 1;\n                  ssize_t n; off_t off = 0;\n                  n = sendfile(s, fd, &off, 1);\n                  if (n == -1 && errno == ENOSYS) return 1\"\n. auto/feature\n\nif [ $ngx_found = yes ]; then\n    CORE_SRCS=\"$CORE_SRCS $LINUX_SENDFILE_SRCS\"\nfi\n\n\n# sendfile64()\n\nCC_AUX_FLAGS=\"$cc_aux_flags -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64\"\nngx_feature=\"sendfile64()\"\nngx_feature_name=\"NGX_HAVE_SENDFILE64\"\nngx_feature_run=yes\nngx_feature_incs=\"#include <sys/sendfile.h>\n                  #include <errno.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"int s = 0, fd = 1;\n                  ssize_t n; off_t off = 0;\n                  n = sendfile(s, fd, &off, 1);\n                  if (n == -1 && errno == ENOSYS) return 1\"\n. auto/feature\n\n\nngx_include=\"sys/prctl.h\"; . auto/include\n\n# prctl(PR_SET_DUMPABLE)\n\nngx_feature=\"prctl(PR_SET_DUMPABLE)\"\nngx_feature_name=\"NGX_HAVE_PR_SET_DUMPABLE\"\nngx_feature_run=yes\nngx_feature_incs=\"#include <sys/prctl.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) == -1) return 1\"\n. auto/feature\n\n\n# prctl(PR_SET_KEEPCAPS)\n\nngx_feature=\"prctl(PR_SET_KEEPCAPS)\"\nngx_feature_name=\"NGX_HAVE_PR_SET_KEEPCAPS\"\nngx_feature_run=yes\nngx_feature_incs=\"#include <sys/prctl.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1) return 1\"\n. auto/feature\n\n\n# capabilities\n\nngx_feature=\"capabilities\"\nngx_feature_name=\"NGX_HAVE_CAPABILITIES\"\nngx_feature_run=no\nngx_feature_incs=\"#include <linux/capability.h>\n                  #include <sys/syscall.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"struct __user_cap_data_struct    data;\n                  struct __user_cap_header_struct  header;\n\n                  header.version = _LINUX_CAPABILITY_VERSION_1;\n                  data.effective = CAP_TO_MASK(CAP_NET_RAW);\n                  data.permitted = 0;\n\n                  (void) header;\n                  (void) data;\n                  (void) SYS_capset\"\n. auto/feature\n\n\n# crypt_r()\n\nngx_feature=\"crypt_r()\"\nngx_feature_name=\"NGX_HAVE_GNU_CRYPT_R\"\nngx_feature_run=no\nngx_feature_incs=\"#include <crypt.h>\"\nngx_feature_path=\nngx_feature_libs=-lcrypt\nngx_feature_test=\"struct crypt_data  cd;\n                  crypt_r(\\\"key\\\", \\\"salt\\\", &cd);\"\n. auto/feature\n\n\nngx_include=\"sys/vfs.h\";     . auto/include\n\n\n# UDP segmentation offloading\n\nngx_feature=\"UDP_SEGMENT\"\nngx_feature_name=\"NGX_HAVE_UDP_SEGMENT\"\nngx_feature_run=no\nngx_feature_incs=\"#include <sys/socket.h>\n                  #include <netinet/udp.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"socklen_t optlen = sizeof(int);\n                  int val;\n                  getsockopt(0, SOL_UDP, UDP_SEGMENT, &val, &optlen)\"\n. auto/feature\n\n\nCC_AUX_FLAGS=\"$cc_aux_flags -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64\"\n"
  },
  {
    "path": "auto/os/solaris",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\nhave=NGX_SOLARIS . auto/have_headers\n\nCORE_INCS=\"$UNIX_INCS\"\nCORE_DEPS=\"$UNIX_DEPS $SOLARIS_DEPS\"\nCORE_SRCS=\"$UNIX_SRCS $SOLARIS_SRCS \"\nCORE_LIBS=\"$CORE_LIBS -lsocket -lnsl\"\n\nNGX_RPATH=YES\n\n# Solaris's make does not support a blank line between target and rules\nngx_spacer=\n\nCC_AUX_FLAGS=\"$CC_AUX_FLAGS -D_FILE_OFFSET_BITS=64 -lsocket -lnsl\"\n\n\nif [ $ZLIB_ASM != NO ]; then\n    echo \"$0: error: the --with-zlib-asm=CPU option is not supported\"\n    echo \"on that platform\"\n    echo\n\n    exit 1\nfi\n\n\nngx_feature=\"sendfilev()\"\nngx_feature_name=\"NGX_HAVE_SENDFILE\"\nngx_feature_run=no\nngx_feature_incs=\"#include <sys/sendfile.h>\"\nngx_feature_path=\nngx_feature_libs=\"-lsendfile\"\nngx_feature_test=\"int fd = 1; sendfilevec_t vec[1];\n                  size_t sent; ssize_t n;\n                  n = sendfilev(fd, vec, 1, &sent);\n                  if (n == -1) return 1\"\n. auto/feature\n\n\nif [ $ngx_found = yes ]; then\n    CORE_SRCS=\"$CORE_SRCS $SOLARIS_SENDFILEV_SRCS\"\n    CORE_LIBS=\"$CORE_LIBS -lsendfile\"\nfi\n\n\nngx_feature=\"event ports\"\nngx_feature_name=\"NGX_HAVE_EVENTPORT\"\nngx_feature_run=no\nngx_feature_incs=\"#include <port.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"(void) port_create()\"\n. auto/feature\n\nif [ $ngx_found = yes ]; then\n    CORE_SRCS=\"$CORE_SRCS $EVENTPORT_SRCS\"\n    EVENT_MODULES=\"$EVENT_MODULES $EVENTPORT_MODULE\"\nfi\n"
  },
  {
    "path": "auto/os/win32",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\nhave=NGX_WIN32 . auto/have_headers\n\nCORE_INCS=\"$WIN32_INCS\"\nCORE_DEPS=\"$WIN32_DEPS\"\nCORE_SRCS=\"$WIN32_SRCS $IOCP_SRCS\"\nOS_CONFIG=\"$WIN32_CONFIG\"\nNGX_ICONS=\"$NGX_WIN32_ICONS\"\nSELECT_SRCS=$WIN32_SELECT_SRCS\nPOLL_SRCS=$WIN32_POLL_SRCS\n\nngx_pic_opt=\nngx_binext=\".exe\"\n\ncase \"$NGX_CC_NAME\" in\n\n    gcc)\n        CORE_LIBS=\"$CORE_LIBS -ladvapi32 -lws2_32\"\n        MAIN_LINK=\"$MAIN_LINK -Wl,--export-all-symbols\"\n        MAIN_LINK=\"$MAIN_LINK -Wl,--out-implib=$NGX_OBJS/libnginx.a\"\n        MODULE_LINK=\"-shared -L $NGX_OBJS -lnginx\"\n    ;;\n\n    *)\n        CORE_LIBS=\"$CORE_LIBS advapi32.lib ws2_32.lib\"\n    ;;\n\nesac\n\nEVENT_MODULES=\"$EVENT_MODULES $IOCP_MODULE\"\n#EVENT_FOUND=YES\n\nhave=NGX_HAVE_INET6 . auto/have\n\nhave=NGX_HAVE_IOCP . auto/have\n"
  },
  {
    "path": "auto/sources",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\nCORE_MODULES=\"ngx_core_module ngx_errlog_module ngx_conf_module\"\n\nCORE_INCS=\"src/core\"\n\nCORE_DEPS=\"src/core/nginx.h \\\n           src/core/ngx_config.h \\\n           src/core/ngx_core.h \\\n           src/core/ngx_log.h \\\n           src/core/ngx_palloc.h \\\n           src/core/ngx_array.h \\\n           src/core/ngx_list.h \\\n           src/core/ngx_hash.h \\\n           src/core/ngx_buf.h \\\n           src/core/ngx_queue.h \\\n           src/core/ngx_string.h \\\n           src/core/ngx_parse.h \\\n           src/core/ngx_parse_time.h \\\n           src/core/ngx_inet.h \\\n           src/core/ngx_file.h \\\n           src/core/ngx_crc.h \\\n           src/core/ngx_crc32.h \\\n           src/core/ngx_murmurhash.h \\\n           src/core/ngx_md5.h \\\n           src/core/ngx_sha1.h \\\n           src/core/ngx_rbtree.h \\\n           src/core/ngx_trie.h \\\n           src/core/ngx_segment_tree.h \\\n           src/core/ngx_radix_tree.h \\\n           src/core/ngx_rwlock.h \\\n           src/core/ngx_slab.h \\\n           src/core/ngx_times.h \\\n           src/core/ngx_shmtx.h \\\n           src/core/ngx_connection.h \\\n           src/core/ngx_cycle.h \\\n           src/core/ngx_conf_file.h \\\n           src/core/ngx_module.h \\\n           src/core/ngx_resolver.h \\\n           src/core/ngx_open_file_cache.h \\\n           src/core/ngx_crypt.h \\\n           src/core/ngx_proxy_protocol.h \\\n           src/core/ngx_syslog.h\"\n\n\nCORE_SRCS=\"src/core/nginx.c \\\n           src/core/ngx_log.c \\\n           src/core/ngx_palloc.c \\\n           src/core/ngx_array.c \\\n           src/core/ngx_list.c \\\n           src/core/ngx_hash.c \\\n           src/core/ngx_buf.c \\\n           src/core/ngx_queue.c \\\n           src/core/ngx_output_chain.c \\\n           src/core/ngx_string.c \\\n           src/core/ngx_parse.c \\\n           src/core/ngx_parse_time.c \\\n           src/core/ngx_inet.c \\\n           src/core/ngx_file.c \\\n           src/core/ngx_crc32.c \\\n           src/core/ngx_murmurhash.c \\\n           src/core/ngx_md5.c \\\n           src/core/ngx_sha1.c \\\n           src/core/ngx_rbtree.c \\\n           src/core/ngx_trie.c\n           src/core/ngx_segment_tree.c \\\n           src/core/ngx_radix_tree.c \\\n           src/core/ngx_slab.c \\\n           src/core/ngx_times.c \\\n           src/core/ngx_shmtx.c \\\n           src/core/ngx_connection.c \\\n           src/core/ngx_cycle.c \\\n           src/core/ngx_spinlock.c \\\n           src/core/ngx_rwlock.c \\\n           src/core/ngx_cpuinfo.c \\\n           src/core/ngx_conf_file.c \\\n           src/core/ngx_module.c \\\n           src/core/ngx_resolver.c \\\n           src/core/ngx_open_file_cache.c \\\n           src/core/ngx_crypt.c \\\n           src/core/ngx_proxy_protocol.c \\\n           src/core/ngx_syslog.c\"\n\n\nEVENT_MODULES=\"ngx_events_module ngx_event_core_module\"\n\nEVENT_INCS=\"src/event src/event/modules\"\n\nEVENT_DEPS=\"src/event/ngx_event.h \\\n            src/event/ngx_event_timer.h \\\n            src/event/ngx_event_posted.h \\\n            src/event/ngx_event_connect.h \\\n            src/event/ngx_event_pipe.h \\\n\t    src/event/ngx_event_udp.h \\\n            src/event/ngx_event_udpv2.h\"\n\nEVENT_SRCS=\"src/event/ngx_event.c \\\n            src/event/ngx_event_timer.c \\\n            src/event/ngx_event_posted.c \\\n            src/event/ngx_event_accept.c \\\n            src/event/ngx_event_udp.c \\\n            src/event/ngx_event_connect.c \\\n            src/event/ngx_event_pipe.c  \\\n            src/event/ngx_event_udpv2.c\"\n\n\nSELECT_MODULE=ngx_select_module\nSELECT_SRCS=src/event/modules/ngx_select_module.c\nWIN32_SELECT_SRCS=src/event/modules/ngx_win32_select_module.c\n\nPOLL_MODULE=ngx_poll_module\nPOLL_SRCS=src/event/modules/ngx_poll_module.c\nWIN32_POLL_SRCS=src/event/modules/ngx_win32_poll_module.c\n\nKQUEUE_MODULE=ngx_kqueue_module\nKQUEUE_SRCS=src/event/modules/ngx_kqueue_module.c\n\nDEVPOLL_MODULE=ngx_devpoll_module\nDEVPOLL_SRCS=src/event/modules/ngx_devpoll_module.c\n\nEVENTPORT_MODULE=ngx_eventport_module\nEVENTPORT_SRCS=src/event/modules/ngx_eventport_module.c\n\nEPOLL_MODULE=ngx_epoll_module\nEPOLL_SRCS=src/event/modules/ngx_epoll_module.c\n\nIOCP_MODULE=ngx_iocp_module\nIOCP_SRCS=src/event/modules/ngx_iocp_module.c\n\n\nPROCS_MODULES=\"ngx_procs_module ngx_proc_core_module\"\nPROCS_DEPS=\"src/proc/ngx_proc.h\"\nPROCS_INCS=\"src/proc\"\nPROCS_SRCS=\"src/proc/ngx_proc.c\"\nFILE_AIO_SRCS=\"src/os/unix/ngx_file_aio_read.c\"\nLINUX_AIO_SRCS=\"src/os/unix/ngx_linux_aio_read.c\"\n\nPIPE_DEPS=\"src/os/unix/ngx_pipe.h\"\nPIPE_SRCS=\"src/os/unix/ngx_pipe.c\"\n\nUNIX_INCS=\"$CORE_INCS $EVENT_INCS src/os/unix\"\n\nUNIX_DEPS=\"$CORE_DEPS $EVENT_DEPS \\\n            src/os/unix/ngx_time.h \\\n            src/os/unix/ngx_errno.h \\\n            src/os/unix/ngx_alloc.h \\\n            src/os/unix/ngx_files.h \\\n            src/os/unix/ngx_channel.h \\\n            src/os/unix/ngx_shmem.h \\\n            src/os/unix/ngx_process.h \\\n            src/os/unix/ngx_setaffinity.h \\\n            src/os/unix/ngx_setproctitle.h \\\n            src/os/unix/ngx_atomic.h \\\n            src/os/unix/ngx_gcc_atomic_x86.h \\\n            src/os/unix/ngx_thread.h \\\n            src/os/unix/ngx_socket.h \\\n            src/os/unix/ngx_os.h \\\n            src/os/unix/ngx_user.h \\\n            src/os/unix/ngx_pipe.h \\\n            src/os/unix/ngx_sysinfo.h \\\n            src/os/unix/ngx_dlopen.h \\\n            src/os/unix/ngx_process_cycle.h\"\n\n# add to UNIX_DEPS\n#            src/os/unix/ngx_gcc_atomic_amd64.h \\\n#            src/os/unix/ngx_gcc_atomic_sparc64.h \\\n#            src/os/unix/ngx_gcc_atomic_ppc.h \\\n#            src/os/unix/ngx_sunpro_atomic_sparc64.h \\\n#            src/os/unix/ngx_sunpro_x86.il \\\n#            src/os/unix/ngx_sunpro_amd64.il \\\n#            src/os/unix/ngx_sunpro_sparc64.il \\\n\n\nUNIX_SRCS=\"$CORE_SRCS $EVENT_SRCS \\\n            src/os/unix/ngx_time.c \\\n            src/os/unix/ngx_errno.c \\\n            src/os/unix/ngx_alloc.c \\\n            src/os/unix/ngx_files.c \\\n            src/os/unix/ngx_socket.c \\\n            src/os/unix/ngx_recv.c \\\n            src/os/unix/ngx_readv_chain.c \\\n            src/os/unix/ngx_udp_recv.c \\\n            src/os/unix/ngx_send.c \\\n            src/os/unix/ngx_writev_chain.c \\\n            src/os/unix/ngx_udp_send.c \\\n            src/os/unix/ngx_udp_sendmsg_chain.c \\\n            src/os/unix/ngx_channel.c \\\n            src/os/unix/ngx_shmem.c \\\n            src/os/unix/ngx_process.c \\\n            src/os/unix/ngx_daemon.c \\\n            src/os/unix/ngx_setaffinity.c \\\n            src/os/unix/ngx_setproctitle.c \\\n            src/os/unix/ngx_posix_init.c \\\n            src/os/unix/ngx_user.c \\\n            src/os/unix/ngx_dlopen.c \\\n            src/os/unix/ngx_pipe.c \\\n            src/os/unix/ngx_sysinfo.c \\\n            src/os/unix/ngx_process_cycle.c\"\n\nPOSIX_DEPS=src/os/unix/ngx_posix_config.h\n\nTHREAD_POOL_MODULE=ngx_thread_pool_module\nTHREAD_POOL_DEPS=src/core/ngx_thread_pool.h\nTHREAD_POOL_SRCS=\"src/core/ngx_thread_pool.c\n                  src/os/unix/ngx_thread_cond.c\n                  src/os/unix/ngx_thread_mutex.c\n                  src/os/unix/ngx_thread_id.c\"\n\nFREEBSD_DEPS=\"src/os/unix/ngx_freebsd_config.h src/os/unix/ngx_freebsd.h\"\nFREEBSD_SRCS=src/os/unix/ngx_freebsd_init.c\nFREEBSD_SENDFILE_SRCS=src/os/unix/ngx_freebsd_sendfile_chain.c\n\nLINUX_DEPS=\"src/os/unix/ngx_linux_config.h src/os/unix/ngx_linux.h\"\nLINUX_SRCS=src/os/unix/ngx_linux_init.c\nLINUX_SENDFILE_SRCS=src/os/unix/ngx_linux_sendfile_chain.c\n\n\nSOLARIS_DEPS=\"src/os/unix/ngx_solaris_config.h src/os/unix/ngx_solaris.h\"\nSOLARIS_SRCS=src/os/unix/ngx_solaris_init.c\nSOLARIS_SENDFILEV_SRCS=src/os/unix/ngx_solaris_sendfilev_chain.c\n\n\nDARWIN_DEPS=\"src/os/unix/ngx_darwin_config.h src/os/unix/ngx_darwin.h\"\nDARWIN_SRCS=src/os/unix/ngx_darwin_init.c\nDARWIN_SENDFILE_SRCS=src/os/unix/ngx_darwin_sendfile_chain.c\n\n\nWIN32_INCS=\"$CORE_INCS $EVENT_INCS src/os/win32\"\n\nWIN32_DEPS=\"$CORE_DEPS $EVENT_DEPS \\\n            src/os/win32/ngx_win32_config.h \\\n            src/os/win32/ngx_time.h \\\n            src/os/win32/ngx_errno.h \\\n            src/os/win32/ngx_alloc.h \\\n            src/os/win32/ngx_files.h \\\n            src/os/win32/ngx_shmem.h \\\n            src/os/win32/ngx_process.h \\\n            src/os/win32/ngx_atomic.h \\\n            src/os/win32/ngx_thread.h \\\n            src/os/win32/ngx_socket.h \\\n            src/os/win32/ngx_os.h \\\n            src/os/win32/ngx_user.h \\\n            src/os/win32/ngx_dlopen.h \\\n            src/os/win32/ngx_process_cycle.h\"\n\nWIN32_CONFIG=src/os/win32/ngx_win32_config.h\n\nWIN32_SRCS=\"$CORE_SRCS $EVENT_SRCS \\\n            src/os/win32/ngx_errno.c \\\n            src/os/win32/ngx_alloc.c \\\n            src/os/win32/ngx_files.c \\\n            src/os/win32/ngx_shmem.c \\\n            src/os/win32/ngx_time.c \\\n            src/os/win32/ngx_process.c \\\n            src/os/win32/ngx_thread.c \\\n            src/os/win32/ngx_socket.c \\\n            src/os/win32/ngx_wsarecv.c \\\n            src/os/win32/ngx_wsarecv_chain.c \\\n            src/os/win32/ngx_udp_wsarecv.c \\\n            src/os/win32/ngx_wsasend.c \\\n            src/os/win32/ngx_wsasend_chain.c \\\n            src/os/win32/ngx_win32_init.c \\\n            src/os/win32/ngx_user.c \\\n            src/os/win32/ngx_dlopen.c \\\n            src/os/win32/ngx_event_log.c \\\n            src/os/win32/ngx_process_cycle.c \\\n            src/event/ngx_event_acceptex.c\"\n\nNGX_WIN32_ICONS=\"src/os/win32/nginx.ico\"\nNGX_WIN32_RC=\"src/os/win32/nginx.rc\"\n\n\nHTTP_FILE_CACHE_SRCS=src/http/ngx_http_file_cache.c\n\nHTTP_HUFF_SRCS=\"src/http/ngx_http_huff_decode.c\n                src/http/ngx_http_huff_encode.c\"\n\nNGX_HTTP_LUA_MODULE=ngx_http_lua_module\nNGX_HTTP_LUA_MODULE_SRCS=\"modules/ngx_http_lua_module/src/ngx_http_lua_script.c \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_log.c \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_subrequest.c \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_ndk.c \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_control.c \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_time.c \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_misc.c \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_variable.c \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_string.c \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_output.c \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_headers.c \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_req_body.c \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_uri.c \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_args.c \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_ctx.c \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_regex.c \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_module.c \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_headers_out.c \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_headers_in.c \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_directive.c \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_consts.c \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_exception.c \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_util.c \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_cache.c \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_contentby.c \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_rewriteby.c \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_accessby.c \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_setby.c \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_capturefilter.c \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_clfactory.c \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_pcrefix.c \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_headerfilterby.c \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_shdict.c \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_socket_tcp.c \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_api.c \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_logby.c \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_sleep.c \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_coroutine.c \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_bodyfilterby.c \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_initby.c \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_initworkerby.c \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_socket_udp.c \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_req_method.c \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_phase.c \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_uthread.c \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_timer.c \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_config.c \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_worker.c\"\n\nNGX_HTTP_LUA_MODULE_DEPS=\"modules/ngx_http_lua_module/src/ddebug.h \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_script.h \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_log.h \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_subrequest.h \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_ndk.h \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_control.h \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_time.h \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_string.h \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_misc.h \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_variable.h \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_output.h \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_headers.h \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_uri.h \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_req_body.h \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_args.h \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_ctx.h \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_regex.h \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_common.h \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_directive.h \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_headers_out.h \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_headers_in.h \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_consts.h \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_exception.h \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_util.h \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_cache.h \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_contentby.h \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_rewriteby.h \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_accessby.h \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_setby.h \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_capturefilter.h \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_clfactory.h \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_pcrefix.h \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_headerfilterby.h \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_shdict.h \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_socket_tcp.h \\\n                          modules/ngx_http_lua_module/src/api/ngx_http_lua_api.h \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_logby.h \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_sleep.h \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_coroutine.h \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_bodyfilterby.h \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_initby.h \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_initworkerby.h \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_socket_udp.h \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_req_method.h \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_phase.h \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_probe.h \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_uthread.h \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_timer.h \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_config.h \\\n                          modules/ngx_http_lua_module/src/ngx_http_lua_worker.h\"\n"
  },
  {
    "path": "auto/stubs",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\nhave=NGX_SUPPRESS_WARN . auto/have\n\nhave=NGX_SMP . auto/have\n"
  },
  {
    "path": "auto/summary",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\necho\necho \"Configuration summary\"\n\n\nif [ $USE_THREADS = YES ]; then\n    echo \"  + using threads\"\nfi\n\nif [ $USE_PCRE = DISABLED ]; then\n    echo \"  + PCRE library is disabled\"\n\nelse\n    case $PCRE in\n        YES)   echo \"  + using system $PCRE_LIBRARY library\" ;;\n        NONE)  echo \"  + PCRE library is not used\" ;;\n        *)     echo \"  + using $PCRE_LIBRARY library: $PCRE\" ;;\n    esac\nfi\n\ncase $OPENSSL in\n    YES)   echo \"  + using system OpenSSL library\" ;;\n    NONE)  echo \"  + OpenSSL library is not used\" ;;\n    *)     echo \"  + using OpenSSL library: $OPENSSL\" ;;\nesac\n\ncase $ZLIB in\n    YES)   echo \"  + using system zlib library\" ;;\n    NONE)  echo \"  + zlib library is not used\" ;;\n    *)     echo \"  + using zlib library: $ZLIB\" ;;\nesac\n\ncase $NGX_LIBATOMIC in\n    YES)   echo \"  + using system libatomic_ops library\" ;;\n    NO)    ;; # not used\n    *)     echo \"  + using libatomic_ops library: $NGX_LIBATOMIC\" ;;\nesac\n\nif [ $USE_JEMALLOC = NO ]; then\n    \n    case $JEMALLOC in\n        NONE)  echo \"  + jemalloc library is disabled\" ;;\n        *)     echo \"  + using jemalloc library: $JEMALLOC\" ;;\n    esac\nelse\n    echo \"  + using system jemalloc library\"\nfi\n\necho\n\n\ncat << END\n  nginx path prefix: \"$NGX_PREFIX\"\n  nginx binary file: \"$NGX_SBIN_PATH\"\n  nginx modules path: \"$NGX_MODULES_PATH\"\n  nginx configuration prefix: \"$NGX_CONF_PREFIX\"\n  nginx configuration file: \"$NGX_CONF_PATH\"\n  nginx pid file: \"$NGX_PID_PATH\"\nEND\n\nif test -n \"$NGX_ERROR_LOG_PATH\"; then\n    echo \"  nginx error log file: \\\"$NGX_ERROR_LOG_PATH\\\"\"\nelse\n    echo \"  nginx logs errors to stderr\"\nfi\n\ncat << END\n  nginx http access log file: \"$NGX_HTTP_LOG_PATH\"\n  nginx http client request body temporary files: \"$NGX_HTTP_CLIENT_TEMP_PATH\"\nEND\n\nif [ $HTTP_PROXY = YES ]; then\n    echo \"  nginx http proxy temporary files: \\\"$NGX_HTTP_PROXY_TEMP_PATH\\\"\"\nfi\n\nif [ $HTTP_FASTCGI = YES ]; then\n    echo \"  nginx http fastcgi temporary files: \\\"$NGX_HTTP_FASTCGI_TEMP_PATH\\\"\"\nfi\n\nif [ $HTTP_UWSGI = YES ]; then\n    echo \"  nginx http uwsgi temporary files: \\\"$NGX_HTTP_UWSGI_TEMP_PATH\\\"\"\nfi\n\nif [ $HTTP_SCGI = YES ]; then\n    echo \"  nginx http scgi temporary files: \\\"$NGX_HTTP_SCGI_TEMP_PATH\\\"\"\nfi\n\necho \"$NGX_POST_CONF_MSG\"\n"
  },
  {
    "path": "auto/threads",
    "content": "\n# Copyright (C) Nginx, Inc.\n\n\nif [ $USE_THREADS = YES ]; then\n\n    if [ \"$NGX_PLATFORM\" = win32 ]; then\n        cat << END\n\n$0: --with-threads is not supported on Windows\n\nEND\n        exit 1\n    fi\n\n    have=NGX_THREADS . auto/have\n    CORE_DEPS=\"$CORE_DEPS $THREAD_POOL_DEPS\"\n    CORE_SRCS=\"$CORE_SRCS $THREAD_POOL_SRCS\"\n    CORE_LIBS=\"$CORE_LIBS -lpthread\"\n    NGX_LIBPTHREAD=\"-lpthread\"\nfi\n"
  },
  {
    "path": "auto/types/sizeof",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\necho $ngx_n \"checking for $ngx_type size ...$ngx_c\"\n\ncat << END >> $NGX_AUTOCONF_ERR\n\n----------------------------------------\nchecking for $ngx_type size\n\nEND\n\nngx_size=\n\ncat << END > $NGX_AUTOTEST.c\n\n#include <sys/types.h>\n#include <sys/time.h>\n$NGX_INCLUDE_UNISTD_H\n#include <signal.h>\n#include <stdio.h>\n#include <sys/resource.h>\n$NGX_INCLUDE_INTTYPES_H\n$NGX_INCLUDE_AUTO_CONFIG_H\n\nint main(void) {\n    printf(\"%d\", (int) sizeof($ngx_type));\n    return 0;\n}\n\nEND\n\n\nngx_test=\"$CC $CC_TEST_FLAGS $CC_AUX_FLAGS \\\n          -o $NGX_AUTOTEST $NGX_AUTOTEST.c $NGX_LD_OPT $ngx_feature_libs\"\n\neval \"$ngx_test >> $NGX_AUTOCONF_ERR 2>&1\"\n\n\nif [ -x $NGX_AUTOTEST ]; then\n    ngx_size=`$NGX_AUTOTEST`\n    echo \" $ngx_size bytes\"\nfi\n\n\ncase $ngx_size in\n    4)\n        ngx_max_value=2147483647\n        ngx_max_len='(sizeof(\"-2147483648\") - 1)'\n    ;;\n\n    8)\n        ngx_max_value=9223372036854775807LL\n        ngx_max_len='(sizeof(\"-9223372036854775808\") - 1)'\n    ;;\n\n    *)\n        echo\n        echo \"$0: error: can not detect $ngx_type size\"\n\n        echo \"----------\"    >> $NGX_AUTOCONF_ERR\n        cat $NGX_AUTOTEST.c  >> $NGX_AUTOCONF_ERR\n        echo \"----------\"    >> $NGX_AUTOCONF_ERR\n        echo $ngx_test       >> $NGX_AUTOCONF_ERR\n        echo \"----------\"    >> $NGX_AUTOCONF_ERR\n\n        rm -rf $NGX_AUTOTEST*\n\n        exit 1\nesac\n\n\nrm -rf $NGX_AUTOTEST*\n\n"
  },
  {
    "path": "auto/types/typedef",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\necho $ngx_n \"checking for $ngx_type ...$ngx_c\"\n\ncat << END >> $NGX_AUTOCONF_ERR\n\n----------------------------------------\nchecking for $ngx_type\n\nEND\n\nngx_found=no\n\nfor ngx_try in $ngx_type $ngx_types\ndo\n\n    cat << END > $NGX_AUTOTEST.c\n\n#include <sys/types.h>\n#include <signal.h>\n#include <sys/socket.h>\n#include <sys/time.h>\n#include <sys/resource.h>\n#include <netinet/in.h>\n$NGX_INCLUDE_INTTYPES_H\n\nint main(void) {\n    $ngx_try i = 0;\n    return (int) i;\n}\n\nEND\n\n    ngx_test=\"$CC $CC_TEST_FLAGS $CC_AUX_FLAGS \\\n              -o $NGX_AUTOTEST $NGX_AUTOTEST.c $NGX_LD_OPT $ngx_feature_libs\"\n\n    eval \"$ngx_test >> $NGX_AUTOCONF_ERR 2>&1\"\n\n    if [ -x $NGX_AUTOTEST ]; then\n        if [ $ngx_try = $ngx_type ]; then\n            echo \" found\"\n            ngx_found=yes\n        else\n            echo \", $ngx_try used\"\n            ngx_found=$ngx_try\n        fi\n    fi\n\n    if [ $ngx_found = no ]; then\n        if [ $ngx_try = $ngx_type ]; then\n            echo $ngx_n \" $ngx_try not found$ngx_c\"\n        else\n            echo $ngx_n \", $ngx_try not found$ngx_c\"\n        fi\n\n        echo \"----------\"    >> $NGX_AUTOCONF_ERR\n        cat $NGX_AUTOTEST.c  >> $NGX_AUTOCONF_ERR\n        echo \"----------\"    >> $NGX_AUTOCONF_ERR\n        echo $ngx_test       >> $NGX_AUTOCONF_ERR\n        echo \"----------\"    >> $NGX_AUTOCONF_ERR\n    fi\n\n    rm -rf $NGX_AUTOTEST*\n\n    if [ $ngx_found != no ]; then\n        break\n    fi\ndone\n\nif [ $ngx_found = no ]; then\n    echo\n    echo \"$0: error: can not define $ngx_type\"\n\n    exit 1\nfi\n\nif [ $ngx_found != yes ]; then\n    echo \"typedef $ngx_found  $ngx_type;\"   >> $NGX_AUTO_CONFIG_H\nfi\n"
  },
  {
    "path": "auto/types/uintptr_t",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\necho $ngx_n \"checking for uintptr_t ...$ngx_c\"\n\ncat << END >> $NGX_AUTOCONF_ERR\n\n----------------------------------------\nchecking for uintptr_t\n\nEND\n\nfound=no\n\ncat << END > $NGX_AUTOTEST.c\n\n#include <sys/types.h>\n$NGX_INCLUDE_INTTYPES_H\n\nint main(void) {\n    uintptr_t i = 0;\n    return (int) i;\n}\n\nEND\n\nngx_test=\"$CC $CC_TEST_FLAGS $CC_AUX_FLAGS \\\n          -o $NGX_AUTOTEST $NGX_AUTOTEST.c $NGX_LD_OPT\"\n\neval \"$ngx_test >> $NGX_AUTOCONF_ERR 2>&1\"\n\nif [ -x $NGX_AUTOTEST ]; then\n    echo \" uintptr_t found\"\n    found=yes\nelse\n    echo $ngx_n \" uintptr_t not found\" $ngx_c\nfi\n\nrm -rf $NGX_AUTOTEST*\n\n\nif [ $found = no ]; then\n    found=\"uint`expr 8 \\* $ngx_ptr_size`_t\"\n    echo \", $found used\"\n\n    echo \"typedef $found  uintptr_t;\"                   >> $NGX_AUTO_CONFIG_H\n    echo \"typedef $found  intptr_t;\" | sed -e 's/u//g'  >> $NGX_AUTO_CONFIG_H\nfi\n"
  },
  {
    "path": "auto/types/value",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\ncat << END >> $NGX_AUTO_CONFIG_H\n\n#ifndef $ngx_param\n#define $ngx_param  $ngx_value\n#endif\n\nEND\n"
  },
  {
    "path": "auto/unix",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\nNGX_USER=${NGX_USER:-nobody}\n\nif [ -z \"$NGX_GROUP\" ]; then\n    if [ $NGX_USER = nobody ]; then\n        if grep nobody /etc/group 2>&1 >/dev/null; then\n            echo \"checking for nobody group ... found\"\n            NGX_GROUP=nobody\n        else\n            echo \"checking for nobody group ... not found\"\n\n            if grep nogroup /etc/group 2>&1 >/dev/null; then\n                echo \"checking for nogroup group ... found\"\n                NGX_GROUP=nogroup\n            else\n                echo \"checking for nogroup group ... not found\"\n                NGX_GROUP=nobody\n            fi\n        fi\n    else\n        NGX_GROUP=$NGX_USER\n    fi\nfi\n\n\nngx_feature=\"poll()\"\nngx_feature_name=\nngx_feature_run=no\nngx_feature_incs=\"#include <poll.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"int  n; struct pollfd  pl;\n                  pl.fd = 0;\n                  pl.events = 0;\n                  pl.revents = 0;\n                  n = poll(&pl, 1, 0);\n                  if (n == -1) return 1\"\n. auto/feature\n\nif [ $ngx_found = no ]; then\n    EVENT_POLL=NONE\nfi\n\n\nngx_feature=\"/dev/poll\"\nngx_feature_name=\"NGX_HAVE_DEVPOLL\"\nngx_feature_run=no\nngx_feature_incs=\"#include <sys/devpoll.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"int  n, dp; struct dvpoll  dvp;\n                  dp = 0;\n                  dvp.dp_fds = NULL;\n                  dvp.dp_nfds = 0;\n                  dvp.dp_timeout = 0;\n                  n = ioctl(dp, DP_POLL, &dvp);\n                  if (n == -1) return 1\"\n. auto/feature\n\nif [ $ngx_found = yes ]; then\n    CORE_SRCS=\"$CORE_SRCS $DEVPOLL_SRCS\"\n    EVENT_MODULES=\"$EVENT_MODULES $DEVPOLL_MODULE\"\n    EVENT_FOUND=YES\nfi\n\n\nif test -z \"$NGX_KQUEUE_CHECKED\"; then\n    ngx_feature=\"kqueue\"\n    ngx_feature_name=\"NGX_HAVE_KQUEUE\"\n    ngx_feature_run=no\n    ngx_feature_incs=\"#include <sys/event.h>\"\n    ngx_feature_path=\n    ngx_feature_libs=\n    ngx_feature_test=\"(void) kqueue()\"\n    . auto/feature\n\n    if [ $ngx_found = yes ]; then\n\n        have=NGX_HAVE_CLEAR_EVENT . auto/have\n        EVENT_MODULES=\"$EVENT_MODULES $KQUEUE_MODULE\"\n        CORE_SRCS=\"$CORE_SRCS $KQUEUE_SRCS\"\n        EVENT_FOUND=YES\n\n        ngx_feature=\"kqueue's NOTE_LOWAT\"\n        ngx_feature_name=\"NGX_HAVE_LOWAT_EVENT\"\n        ngx_feature_run=no\n        ngx_feature_incs=\"#include <sys/event.h>\"\n        ngx_feature_path=\n        ngx_feature_libs=\n        ngx_feature_test=\"struct kevent  kev;\n                          kev.fflags = NOTE_LOWAT;\n                          (void) kev\"\n        . auto/feature\n\n\n        ngx_feature=\"kqueue's EVFILT_TIMER\"\n        ngx_feature_name=\"NGX_HAVE_TIMER_EVENT\"\n        ngx_feature_run=yes\n        ngx_feature_incs=\"#include <sys/event.h>\n                          #include <sys/time.h>\"\n        ngx_feature_path=\n        ngx_feature_libs=\n        ngx_feature_test=\"int      kq;\n                  struct kevent    kev;\n                  struct timespec  ts;\n\n                  if ((kq = kqueue()) == -1) return 1;\n\n                  kev.ident = 0;\n                  kev.filter = EVFILT_TIMER;\n                  kev.flags = EV_ADD|EV_ENABLE;\n                  kev.fflags = 0;\n                  kev.data = 1000;\n                  kev.udata = 0;\n\n                  ts.tv_sec = 0;\n                  ts.tv_nsec = 0;\n\n                  if (kevent(kq, &kev, 1, &kev, 1, &ts) == -1) return 1;\n\n                  if (kev.flags & EV_ERROR) return 1;\"\n\n        . auto/feature\n    fi\nfi\n\n\nif [ \"$NGX_SYSTEM\" = \"NetBSD\" ]; then\n\n    # NetBSD 2.0 incompatibly defines kevent.udata as \"intptr_t\"\n\n    cat << END >> $NGX_AUTO_CONFIG_H\n\n#define NGX_KQUEUE_UDATA_T\n\nEND\n\nelse\n    cat << END >> $NGX_AUTO_CONFIG_H\n\n#define NGX_KQUEUE_UDATA_T  (void *)\n\nEND\n\nfi\n\n\nngx_feature=\"crypt()\"\nngx_feature_name=\nngx_feature_run=no\nngx_feature_incs=\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"crypt(\\\"test\\\", \\\"salt\\\");\"\n. auto/feature\n\n\nif [ $ngx_found = no ]; then\n\n    ngx_feature=\"crypt() in libcrypt\"\n    ngx_feature_name=\n    ngx_feature_run=no\n    ngx_feature_incs=\n    ngx_feature_path=\n    ngx_feature_libs=-lcrypt\n    . auto/feature\n\n    if [ $ngx_found = yes ]; then\n        CRYPT_LIB=\"-lcrypt\"\n    fi\nfi\n\n\nngx_feature=\"F_READAHEAD\"\nngx_feature_name=\"NGX_HAVE_F_READAHEAD\"\nngx_feature_run=no\nngx_feature_incs=\"#include <fcntl.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"fcntl(0, F_READAHEAD, 1);\"\n. auto/feature\n\n\nngx_feature=\"posix_fadvise()\"\nngx_feature_name=\"NGX_HAVE_POSIX_FADVISE\"\nngx_feature_run=no\nngx_feature_incs=\"#include <fcntl.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"posix_fadvise(0, 0, 0, POSIX_FADV_SEQUENTIAL);\"\n. auto/feature\n\n\nngx_feature=\"O_DIRECT\"\nngx_feature_name=\"NGX_HAVE_O_DIRECT\"\nngx_feature_run=no\nngx_feature_incs=\"#include <fcntl.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"fcntl(0, F_SETFL, O_DIRECT);\"\n. auto/feature\n\n\nif [ $ngx_found = yes -a \"$NGX_SYSTEM\" = \"Linux\" ]; then\n    have=NGX_HAVE_ALIGNED_DIRECTIO . auto/have\nfi\n\nngx_feature=\"F_NOCACHE\"\nngx_feature_name=\"NGX_HAVE_F_NOCACHE\"\nngx_feature_run=no\nngx_feature_incs=\"#include <fcntl.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"fcntl(0, F_NOCACHE, 1);\"\n. auto/feature\n\n\nngx_feature=\"directio()\"\nngx_feature_name=\"NGX_HAVE_DIRECTIO\"\nngx_feature_run=no\nngx_feature_incs=\"#include <sys/types.h>\n                  #include <sys/fcntl.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"directio(0, DIRECTIO_ON);\"\n. auto/feature\n\n\nngx_feature=\"statfs()\"\nngx_feature_name=\"NGX_HAVE_STATFS\"\nngx_feature_run=no\nngx_feature_incs=\"$NGX_INCLUDE_SYS_PARAM_H\n                  $NGX_INCLUDE_SYS_MOUNT_H\n                  $NGX_INCLUDE_SYS_VFS_H\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"struct statfs  fs;\n                  statfs(\\\".\\\", &fs);\"\n. auto/feature\n\n\nngx_feature=\"statvfs()\"\nngx_feature_name=\"NGX_HAVE_STATVFS\"\nngx_feature_run=no\nngx_feature_incs=\"#include <sys/types.h>\n                  #include <sys/statvfs.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"struct statvfs  fs;\n                  statvfs(\\\".\\\", &fs);\"\n. auto/feature\n\n\nngx_feature=\"dlopen()\"\nngx_feature_name=\"NGX_HAVE_DLOPEN\"\nngx_feature_run=no\nngx_feature_incs=\"#include <dlfcn.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"dlopen(NULL, RTLD_NOW | RTLD_GLOBAL); dlsym(NULL, \\\"\\\")\"\n. auto/feature\n\n\nif [ $ngx_found = no ]; then\n\n    ngx_feature=\"dlopen() in libdl\"\n    ngx_feature_libs=\"-ldl\"\n    . auto/feature\n\n    if [ $ngx_found = yes ]; then\n        CORE_LIBS=\"$CORE_LIBS -ldl\"\n        NGX_LIBDL=\"-ldl\"\n    fi\nfi\n\n\nngx_feature=\"sched_yield()\"\nngx_feature_name=\"NGX_HAVE_SCHED_YIELD\"\nngx_feature_run=no\nngx_feature_incs=\"#include <sched.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"sched_yield()\"\n. auto/feature\n\n\nif [ $ngx_found = no ]; then\n\n    ngx_feature=\"sched_yield() in librt\"\n    ngx_feature_libs=\"-lrt\"\n    . auto/feature\n\n    if [ $ngx_found = yes ]; then\n        CORE_LIBS=\"$CORE_LIBS -lrt\"\n    fi\nfi\n\n\nngx_feature=\"sched_setaffinity()\"\nngx_feature_name=\"NGX_HAVE_SCHED_SETAFFINITY\"\nngx_feature_run=no\nngx_feature_incs=\"#include <sched.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"cpu_set_t mask;\n                  CPU_ZERO(&mask);\n                  sched_setaffinity(0, sizeof(cpu_set_t), &mask)\"\n. auto/feature\n\n\nngx_feature=\"SO_SETFIB\"\nngx_feature_name=\"NGX_HAVE_SETFIB\"\nngx_feature_run=no\nngx_feature_incs=\"#include <sys/socket.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"setsockopt(0, SOL_SOCKET, SO_SETFIB, NULL, 0)\"\n. auto/feature\n\n\nngx_feature=\"SO_REUSEPORT\"\nngx_feature_name=\"NGX_HAVE_REUSEPORT\"\nngx_feature_run=no\nngx_feature_incs=\"#include <sys/socket.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"setsockopt(0, SOL_SOCKET, SO_REUSEPORT, NULL, 0)\"\n. auto/feature\n\n\nngx_feature=\"SO_ACCEPTFILTER\"\nngx_feature_name=\"NGX_HAVE_DEFERRED_ACCEPT\"\nngx_feature_run=no\nngx_feature_incs=\"#include <sys/socket.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"setsockopt(0, SOL_SOCKET, SO_ACCEPTFILTER, NULL, 0)\"\n. auto/feature\n\n\n# OpenBSD bind to any address for transparent proxying\n\nngx_feature=\"SO_BINDANY\"\nngx_feature_name=\"NGX_HAVE_TRANSPARENT_PROXY\"\nngx_feature_run=no\nngx_feature_incs=\"#include <sys/socket.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"setsockopt(0, SOL_SOCKET, SO_BINDANY, NULL, 0)\"\n. auto/feature\n\n\n# Linux transparent proxying\n\nngx_feature=\"IP_TRANSPARENT\"\nngx_feature_name=\"NGX_HAVE_TRANSPARENT_PROXY\"\nngx_feature_run=no\nngx_feature_incs=\"#include <sys/socket.h>\n                  #include <netinet/in.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"setsockopt(0, IPPROTO_IP, IP_TRANSPARENT, NULL, 0)\"\n. auto/feature\n\n\n# FreeBSD bind to any address for transparent proxying\n\nngx_feature=\"IP_BINDANY\"\nngx_feature_name=\"NGX_HAVE_TRANSPARENT_PROXY\"\nngx_feature_run=no\nngx_feature_incs=\"#include <sys/socket.h>\n                  #include <netinet/in.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"setsockopt(0, IPPROTO_IP, IP_BINDANY, NULL, 0)\"\n. auto/feature\n\n\n# Linux IP_BIND_ADDRESS_NO_PORT\n\nngx_feature=\"IP_BIND_ADDRESS_NO_PORT\"\nngx_feature_name=\"NGX_HAVE_IP_BIND_ADDRESS_NO_PORT\"\nngx_feature_run=no\nngx_feature_incs=\"#include <sys/socket.h>\n                  #include <netinet/in.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"setsockopt(0, IPPROTO_IP, IP_BIND_ADDRESS_NO_PORT, NULL, 0)\"\n. auto/feature\n\n\n# BSD way to get IPv4 datagram destination address\n\nngx_feature=\"IP_RECVDSTADDR\"\nngx_feature_name=\"NGX_HAVE_IP_RECVDSTADDR\"\nngx_feature_run=no\nngx_feature_incs=\"#include <sys/socket.h>\n                  #include <netinet/in.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"setsockopt(0, IPPROTO_IP, IP_RECVDSTADDR, NULL, 0)\"\n. auto/feature\n\n\n# BSD way to set IPv4 datagram source address\n\nngx_feature=\"IP_SENDSRCADDR\"\nngx_feature_name=\"NGX_HAVE_IP_SENDSRCADDR\"\nngx_feature_run=no\nngx_feature_incs=\"#include <sys/socket.h>\n                  #include <netinet/in.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"setsockopt(0, IPPROTO_IP, IP_SENDSRCADDR, NULL, 0)\"\n. auto/feature\n\n\n# Linux way to get IPv4 datagram destination address\n\nngx_feature=\"IP_PKTINFO\"\nngx_feature_name=\"NGX_HAVE_IP_PKTINFO\"\nngx_feature_run=no\nngx_feature_incs=\"#include <sys/socket.h>\n                  #include <netinet/in.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"struct in_pktinfo  pkt;\n                  pkt.ipi_spec_dst.s_addr = INADDR_ANY;\n                  (void) pkt;\n                  setsockopt(0, IPPROTO_IP, IP_PKTINFO, NULL, 0)\"\n. auto/feature\n\n\n# RFC 3542 way to get IPv6 datagram destination address\n\nngx_feature=\"IPV6_RECVPKTINFO\"\nngx_feature_name=\"NGX_HAVE_IPV6_RECVPKTINFO\"\nngx_feature_run=no\nngx_feature_incs=\"#include <sys/socket.h>\n                  #include <netinet/in.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"setsockopt(0, IPPROTO_IPV6, IPV6_RECVPKTINFO, NULL, 0)\"\n. auto/feature\n\n\nngx_feature=\"TCP_DEFER_ACCEPT\"\nngx_feature_name=\"NGX_HAVE_DEFERRED_ACCEPT\"\nngx_feature_run=no\nngx_feature_incs=\"#include <sys/socket.h>\n                  #include <netinet/in.h>\n                  #include <netinet/tcp.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"setsockopt(0, IPPROTO_TCP, TCP_DEFER_ACCEPT, NULL, 0)\"\n. auto/feature\n\n\nngx_feature=\"TCP_KEEPIDLE\"\nngx_feature_name=\"NGX_HAVE_KEEPALIVE_TUNABLE\"\nngx_feature_run=no\nngx_feature_incs=\"#include <sys/socket.h>\n                  #include <netinet/in.h>\n                  #include <netinet/tcp.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"setsockopt(0, IPPROTO_TCP, TCP_KEEPIDLE, NULL, 0);\n                  setsockopt(0, IPPROTO_TCP, TCP_KEEPINTVL, NULL, 0);\n                  setsockopt(0, IPPROTO_TCP, TCP_KEEPCNT, NULL, 0)\"\n. auto/feature\n\n\nngx_feature=\"TCP_FASTOPEN\"\nngx_feature_name=\"NGX_HAVE_TCP_FASTOPEN\"\nngx_feature_run=no\nngx_feature_incs=\"#include <sys/socket.h>\n                  #include <netinet/in.h>\n                  #include <netinet/tcp.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"setsockopt(0, IPPROTO_TCP, TCP_FASTOPEN, NULL, 0)\"\n. auto/feature\n\n\nngx_feature=\"TCP_INFO\"\nngx_feature_name=\"NGX_HAVE_TCP_INFO\"\nngx_feature_run=no\nngx_feature_incs=\"#include <sys/socket.h>\n                  #include <netinet/in.h>\n                  #include <netinet/tcp.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"socklen_t optlen = sizeof(struct tcp_info);\n                  struct tcp_info ti;\n                  ti.tcpi_rtt = 0;\n                  ti.tcpi_rttvar = 0;\n                  ti.tcpi_snd_cwnd = 0;\n                  ti.tcpi_rcv_space = 0;\n                  getsockopt(0, IPPROTO_TCP, TCP_INFO, &ti, &optlen)\"\n. auto/feature\n\n\nngx_feature=\"accept4()\"\nngx_feature_name=\"NGX_HAVE_ACCEPT4\"\nngx_feature_run=no\nngx_feature_incs=\"#include <sys/socket.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"accept4(0, NULL, NULL, SOCK_NONBLOCK)\"\n. auto/feature\n\nif [ $NGX_FILE_AIO = YES ]; then\n\n    ngx_feature=\"kqueue AIO support\"\n    ngx_feature_name=\"NGX_HAVE_FILE_AIO\"\n    ngx_feature_run=no\n    ngx_feature_incs=\"#include <aio.h>\"\n    ngx_feature_path=\n    ngx_feature_libs=\n    ngx_feature_test=\"struct aiocb  iocb;\n                      iocb.aio_sigevent.sigev_notify = SIGEV_KEVENT;\n                      (void) aio_read(&iocb)\"\n    . auto/feature\n\n    if [ $ngx_found = yes ]; then\n        CORE_SRCS=\"$CORE_SRCS $FILE_AIO_SRCS\"\n    fi\n\n    if [ $ngx_found = no ]; then\n\n        ngx_feature=\"Linux AIO support\"\n        ngx_feature_name=\"NGX_HAVE_FILE_AIO\"\n        ngx_feature_run=no\n        ngx_feature_incs=\"#include <linux/aio_abi.h>\n                          #include <sys/eventfd.h>\"\n        ngx_feature_path=\n        ngx_feature_libs=\n        ngx_feature_test=\"struct iocb  iocb;\n                          iocb.aio_lio_opcode = IOCB_CMD_PREAD;\n                          iocb.aio_flags = IOCB_FLAG_RESFD;\n                          iocb.aio_resfd = -1;\n                          (void) iocb;\n                          (void) eventfd(0, 0)\"\n        . auto/feature\n\n        if [ $ngx_found = yes ]; then\n            have=NGX_HAVE_EVENTFD . auto/have\n            have=NGX_HAVE_SYS_EVENTFD_H . auto/have\n            CORE_SRCS=\"$CORE_SRCS $LINUX_AIO_SRCS\"\n        fi\n    fi\n\n    if [ $ngx_found = no ]; then\n\n        ngx_feature=\"Linux AIO support (SYS_eventfd)\"\n        ngx_feature_incs=\"#include <linux/aio_abi.h>\n                          #include <sys/syscall.h>\"\n        ngx_feature_test=\"struct iocb  iocb;\n                          iocb.aio_lio_opcode = IOCB_CMD_PREAD;\n                          iocb.aio_flags = IOCB_FLAG_RESFD;\n                          iocb.aio_resfd = -1;\n                          (void) iocb;\n                          (void) SYS_eventfd\"\n        . auto/feature\n\n        if [ $ngx_found = yes ]; then\n            have=NGX_HAVE_EVENTFD . auto/have\n            CORE_SRCS=\"$CORE_SRCS $LINUX_AIO_SRCS\"\n        fi\n    fi\n\n    if [ $ngx_found = no ]; then\n        cat << END\n\n$0: no supported file AIO was found\nCurrently file AIO is supported on FreeBSD 4.3+ and Linux 2.6.22+ only\n\nEND\n        exit 1\n    fi\nfi\n\n\nhave=NGX_HAVE_UNIX_DOMAIN . auto/have\n\nngx_feature_libs=\n\n\n# C types\n\nngx_type=\"int\"; . auto/types/sizeof\n\nngx_type=\"long\"; . auto/types/sizeof\n\nngx_type=\"long long\"; . auto/types/sizeof\n\nngx_type=\"void *\"; . auto/types/sizeof; ngx_ptr_size=$ngx_size\nngx_param=NGX_PTR_SIZE; ngx_value=$ngx_size; . auto/types/value\n\n\n# POSIX types\n\nNGX_INCLUDE_AUTO_CONFIG_H=\"#include \\\"ngx_auto_config.h\\\"\"\n\nngx_type=\"uint32_t\"; ngx_types=\"u_int32_t\"; . auto/types/typedef\nngx_type=\"uint64_t\"; ngx_types=\"u_int64_t\"; . auto/types/typedef\n\nngx_type=\"sig_atomic_t\"; ngx_types=\"int\"; . auto/types/typedef\n. auto/types/sizeof\nngx_param=NGX_SIG_ATOMIC_T_SIZE; ngx_value=$ngx_size; . auto/types/value\n\nngx_type=\"socklen_t\"; ngx_types=\"int\"; . auto/types/typedef\n\nngx_type=\"in_addr_t\"; ngx_types=\"uint32_t u_int32_t\"; . auto/types/typedef\n\nngx_type=\"in_port_t\"; ngx_types=\"u_short\"; . auto/types/typedef\n\nngx_type=\"rlim_t\"; ngx_types=\"int\"; . auto/types/typedef\n\n. auto/types/uintptr_t\n\n. auto/endianness\n\nngx_type=\"size_t\"; . auto/types/sizeof\nngx_param=NGX_MAX_SIZE_T_VALUE; ngx_value=$ngx_max_value; . auto/types/value\nngx_param=NGX_SIZE_T_LEN; ngx_value=$ngx_max_len; . auto/types/value\n\nngx_type=\"off_t\"; . auto/types/sizeof\nngx_param=NGX_MAX_OFF_T_VALUE; ngx_value=$ngx_max_value; . auto/types/value\nngx_param=NGX_OFF_T_LEN; ngx_value=$ngx_max_len; . auto/types/value\n\nngx_type=\"time_t\"; . auto/types/sizeof\nngx_param=NGX_TIME_T_SIZE; ngx_value=$ngx_size; . auto/types/value\nngx_param=NGX_TIME_T_LEN; ngx_value=$ngx_max_len; . auto/types/value\nngx_param=NGX_MAX_TIME_T_VALUE; ngx_value=$ngx_max_value; . auto/types/value\n\n\n# syscalls, libc calls and some features\n\n\nngx_feature=\"AF_INET6\"\nngx_feature_name=\"NGX_HAVE_INET6\"\nngx_feature_run=no\nngx_feature_incs=\"#include <sys/socket.h>\n                  #include <netinet/in.h>\n                  #include <arpa/inet.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"struct sockaddr_in6  sin6;\n                  sin6.sin6_family = AF_INET6;\n                  (void) sin6\"\n. auto/feature\n\n\nngx_feature=\"setproctitle()\"\nngx_feature_name=\"NGX_HAVE_SETPROCTITLE\"\nngx_feature_run=no\nngx_feature_incs=\"#include <stdlib.h>\"\nngx_feature_path=\nngx_feature_libs=$NGX_SETPROCTITLE_LIB\nngx_feature_test=\"setproctitle(\\\"test\\\");\"\n. auto/feature\n\n\nngx_feature=\"pread()\"\nngx_feature_name=\"NGX_HAVE_PREAD\"\nngx_feature_run=no\nngx_feature_incs=\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"char buf[1]; ssize_t n; n = pread(0, buf, 1, 0);\n                  if (n == -1) return 1\"\n. auto/feature\n\n\nngx_feature=\"pwrite()\"\nngx_feature_name=\"NGX_HAVE_PWRITE\"\nngx_feature_run=no\nngx_feature_incs=\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"char buf[1]; ssize_t n; n = pwrite(1, buf, 1, 0);\n                  if (n == -1) return 1\"\n. auto/feature\n\n\n# pwritev() was introduced in FreeBSD 6 and Linux 2.6.30, glibc 2.10\n\nngx_feature=\"pwritev()\"\nngx_feature_name=\"NGX_HAVE_PWRITEV\"\nngx_feature_run=no\nngx_feature_incs='#include <sys/uio.h>'\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"char buf[1]; struct iovec vec[1]; ssize_t n;\n                  vec[0].iov_base = buf;\n                  vec[0].iov_len = 1;\n                  n = pwritev(1, vec, 1, 0);\n                  if (n == -1) return 1\"\n. auto/feature\n\n\n# strerrordesc_np(), introduced in glibc 2.32\n\nngx_feature=\"strerrordesc_np()\"\nngx_feature_name=\"NGX_HAVE_STRERRORDESC_NP\"\nngx_feature_run=no\nngx_feature_incs='#include <string.h>'\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"char *p; p = strerrordesc_np(0);\n                  if (p == NULL) return 1\"\n. auto/feature\n\n\nif [ $ngx_found = no ]; then\n\n    ngx_feature=\"sys_nerr\"\n    ngx_feature_name=\"NGX_SYS_NERR\"\n    ngx_feature_run=value\n    ngx_feature_incs='#include <errno.h>\n                      #include <stdio.h>'\n    ngx_feature_path=\n    ngx_feature_libs=\n    ngx_feature_test='printf(\"%d\", sys_nerr);'\n    . auto/feature\nfi\n\n\nif [ $ngx_found = no ]; then\n\n    # Cygiwn defines _sys_nerr\n    ngx_feature=\"_sys_nerr\"\n    ngx_feature_name=\"NGX_SYS_NERR\"\n    ngx_feature_run=value\n    ngx_feature_incs='#include <errno.h>\n                      #include <stdio.h>'\n    ngx_feature_path=\n    ngx_feature_libs=\n    ngx_feature_test='printf(\"%d\", _sys_nerr);'\n    . auto/feature\nfi\n\n\nngx_feature=\"localtime_r()\"\nngx_feature_name=\"NGX_HAVE_LOCALTIME_R\"\nngx_feature_run=no\nngx_feature_incs=\"#include <time.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"struct tm t; time_t c=0; localtime_r(&c, &t)\"\n. auto/feature\n\n\nngx_feature=\"clock_gettime(CLOCK_MONOTONIC)\"\nngx_feature_name=\"NGX_HAVE_CLOCK_MONOTONIC\"\nngx_feature_run=no\nngx_feature_incs=\"#include <time.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts)\"\n. auto/feature\n\n\nif [ $ngx_found = no ]; then\n\n    # Linux before glibc 2.17, notably CentOS 6\n\n    ngx_feature=\"clock_gettime(CLOCK_MONOTONIC) in librt\"\n    ngx_feature_libs=\"-lrt\"\n    . auto/feature\n\n    if [ $ngx_found = yes ]; then\n        CORE_LIBS=\"$CORE_LIBS -lrt\"\n    fi\nfi\n\n\nngx_feature=\"posix_memalign()\"\nngx_feature_name=\"NGX_HAVE_POSIX_MEMALIGN\"\nngx_feature_run=no\nngx_feature_incs=\"#include <stdlib.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"void *p; int n; n = posix_memalign(&p, 4096, 4096);\n                  if (n != 0) return 1\"\n. auto/feature\n\n\nngx_feature=\"memalign()\"\nngx_feature_name=\"NGX_HAVE_MEMALIGN\"\nngx_feature_run=no\nngx_feature_incs=\"#include <stdlib.h>\n                  #include <malloc.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"void *p; p = memalign(4096, 4096);\n                  if (p == NULL) return 1\"\n. auto/feature\n\n\nngx_feature=\"mmap(MAP_ANON|MAP_SHARED)\"\nngx_feature_name=\"NGX_HAVE_MAP_ANON\"\nngx_feature_run=yes\nngx_feature_incs=\"#include <sys/mman.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"void *p;\n                  p = mmap(NULL, 4096, PROT_READ|PROT_WRITE,\n                           MAP_ANON|MAP_SHARED, -1, 0);\n                  if (p == MAP_FAILED) return 1;\"\n. auto/feature\n\n\nngx_feature='mmap(\"/dev/zero\", MAP_SHARED)'\nngx_feature_name=\"NGX_HAVE_MAP_DEVZERO\"\nngx_feature_run=yes\nngx_feature_incs=\"#include <sys/mman.h>\n                  #include <sys/stat.h>\n                  #include <fcntl.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test='void *p; int  fd;\n                  fd = open(\"/dev/zero\", O_RDWR);\n                  p = mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);\n                  if (p == MAP_FAILED) return 1;'\n. auto/feature\n\n\nngx_feature=\"System V shared memory\"\nngx_feature_name=\"NGX_HAVE_SYSVSHM\"\nngx_feature_run=yes\nngx_feature_incs=\"#include <sys/ipc.h>\n                  #include <sys/shm.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"int  id;\n                  id = shmget(IPC_PRIVATE, 4096, (SHM_R|SHM_W|IPC_CREAT));\n                  if (id == -1) return 1;\n                  shmctl(id, IPC_RMID, NULL);\"\n. auto/feature\n\n\nngx_feature=\"POSIX semaphores\"\nngx_feature_name=\"NGX_HAVE_POSIX_SEM\"\nngx_feature_run=yes\nngx_feature_incs=\"#include <semaphore.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"sem_t  sem;\n                  if (sem_init(&sem, 1, 0) == -1) return 1;\n                  sem_destroy(&sem);\"\n. auto/feature\n\n\nif [ $ngx_found = no ]; then\n\n    # Linux has POSIX semaphores in libpthread\n    ngx_feature=\"POSIX semaphores in libpthread\"\n    ngx_feature_libs=-lpthread\n    . auto/feature\n\n    if [ $ngx_found = yes ]; then\n        CORE_LIBS=\"$CORE_LIBS -lpthread\"\n        NGX_LIBPTHREAD=\"-lpthread\"\n    fi\nfi\n\n\nif [ $ngx_found = no ]; then\n\n    # Solaris has POSIX semaphores in librt\n    ngx_feature=\"POSIX semaphores in librt\"\n    ngx_feature_libs=-lrt\n    . auto/feature\n\n    if [ $ngx_found = yes ]; then\n        CORE_LIBS=\"$CORE_LIBS -lrt\"\n    fi\nfi\n\n\nngx_feature=\"struct msghdr.msg_control\"\nngx_feature_name=\"NGX_HAVE_MSGHDR_MSG_CONTROL\"\nngx_feature_run=no\nngx_feature_incs=\"#include <sys/socket.h>\n                  #include <stdio.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"struct msghdr  msg;\n                  printf(\\\"%d\\\", (int) sizeof(msg.msg_control))\"\n. auto/feature\n\n\nngx_feature=\"ioctl(FIONBIO)\"\nngx_feature_name=\"NGX_HAVE_FIONBIO\"\nngx_feature_run=no\nngx_feature_incs=\"#include <sys/ioctl.h>\n                  #include <stdio.h>\n                  $NGX_INCLUDE_SYS_FILIO_H\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"int i = FIONBIO; printf(\\\"%d\\\", i)\"\n. auto/feature\n\n\nngx_feature=\"ioctl(FIONREAD)\"\nngx_feature_name=\"NGX_HAVE_FIONREAD\"\nngx_feature_run=no\nngx_feature_incs=\"#include <sys/ioctl.h>\n                  #include <stdio.h>\n                  $NGX_INCLUDE_SYS_FILIO_H\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"int i = FIONREAD; printf(\\\"%d\\\", i)\"\n. auto/feature\n\n\nngx_feature=\"struct tm.tm_gmtoff\"\nngx_feature_name=\"NGX_HAVE_GMTOFF\"\nngx_feature_run=no\nngx_feature_incs=\"#include <time.h>\n                  #include <stdio.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"struct tm  tm; tm.tm_gmtoff = 0;\n                  printf(\\\"%d\\\", (int) tm.tm_gmtoff)\"\n. auto/feature\n\n\nngx_feature=\"struct dirent.d_namlen\"\nngx_feature_name=\"NGX_HAVE_D_NAMLEN\"\nngx_feature_run=no\nngx_feature_incs=\"#include <dirent.h>\n                  #include <stdio.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"struct dirent  dir; dir.d_namlen = 0;\n                  printf(\\\"%d\\\", (int) dir.d_namlen)\"\n. auto/feature\n\n\nngx_feature=\"struct dirent.d_type\"\nngx_feature_name=\"NGX_HAVE_D_TYPE\"\nngx_feature_run=no\nngx_feature_incs=\"#include <dirent.h>\n                  #include <stdio.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"struct dirent  dir; dir.d_type = DT_REG;\n                  printf(\\\"%d\\\", (int) dir.d_type)\"\n. auto/feature\n\n\nngx_feature=\"sysconf(_SC_NPROCESSORS_ONLN)\"\nngx_feature_name=\"NGX_HAVE_SC_NPROCESSORS_ONLN\"\nngx_feature_run=no\nngx_feature_incs=\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"sysconf(_SC_NPROCESSORS_ONLN)\"\n. auto/feature\n\n\nngx_feature=\"sysconf(_SC_LEVEL1_DCACHE_LINESIZE)\"\nngx_feature_name=\"NGX_HAVE_LEVEL1_DCACHE_LINESIZE\"\nngx_feature_run=no\nngx_feature_incs=\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"sysconf(_SC_LEVEL1_DCACHE_LINESIZE)\"\n. auto/feature\n\n\nngx_feature=\"openat(), fstatat()\"\nngx_feature_name=\"NGX_HAVE_OPENAT\"\nngx_feature_run=no\nngx_feature_incs=\"#include <sys/types.h>\n                  #include <sys/stat.h>\n                  #include <fcntl.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"struct stat sb;\n                  openat(AT_FDCWD, \\\".\\\", O_RDONLY|O_NOFOLLOW);\n                  fstatat(AT_FDCWD, \\\".\\\", &sb, AT_SYMLINK_NOFOLLOW);\"\n. auto/feature\n\n\nngx_feature=\"getaddrinfo()\"\nngx_feature_name=\"NGX_HAVE_GETADDRINFO\"\nngx_feature_run=no\nngx_feature_incs=\"#include <sys/types.h>\n                  #include <sys/socket.h>\n                  #include <netdb.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test='struct addrinfo *res;\n                  if (getaddrinfo(\"localhost\", NULL, NULL, &res) != 0) return 1;\n                  freeaddrinfo(res)'\n. auto/feature\n\n\n# Tengine:\nngx_feature=\"sysinfo()\"\nngx_feature_name=\"NGX_HAVE_SYSINFO\"\nngx_feature_run=no\nngx_feature_incs=\"#include <sys/sysinfo.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"struct sysinfo s;\n                  sysinfo(&s);\"\n. auto/feature\n\n\nngx_feature=\"getloadavg()\"\nngx_feature_name=\"NGX_HAVE_GETLOADAVG\"\nngx_feature_run=no\nngx_feature_incs=\"#include <stdlib.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"double loadavg[1];\n                  getloadavg(loadavg, 1);\"\n. auto/feature\n\n\nngx_feature=\"/proc/meminfo\"\nngx_feature_name=\"NGX_HAVE_PROC_MEMINFO\"\nngx_feature_run=yes\nngx_feature_incs=\"#include <fcntl.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test='int fd;\n                  if (open(\"/proc/meminfo\", O_RDONLY) == -1) return 1;'\n. auto/feature\n\n\nngx_feature=\"/proc/stat\"\nngx_feature_name=\"NGX_HAVE_PROC_STAT\"\nngx_feature_run=yes\nngx_feature_incs=\"#include <fcntl.h>\"\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test='int fd;\n                  if (open(\"/proc/stat\", O_RDONLY) == -1) return 1;'\n. auto/feature\n\n\n# Tengine: auto read nameserver from /etc/resolv.conf.\n if [ -f \"/etc/resolv.conf\" ]; then\n    have=NGX_RESOLVER_FILE value=\"\\\"/etc/resolv.conf\\\"\" . auto/define\nfi\n\n"
  },
  {
    "path": "conf/browsers",
    "content": "# syntax:\n#\n# user_agent $variable_name {\n#     default         value;\n#     greedy        name;\n#\n#     name [([+|-]version) | (version1~version2)]  value;\n# }\n\nuser_agent $browser {\n\n    # default\n    default                                             unknown;\n\n    # greedy\n    greedy                                              Chrome;\n    greedy                                              Safari;\n    greedy                                              Firefox;\n\n    # name                   version                     value\n\n    # match version greater than 18.0[18.0,+OO]\n    Chrome                  18.0+                       chrome18;\n    # match version in[16.0,16.9999]\n    Chrome                  17.0~17.9999                chrome17;\n    Chrome                  16.0~16.9999                chrome16;\n    Chrome                  15.0~15.9999                chrome15;\n    Chrome                  14.0~14.9999                chrome14;\n    Chrome                  13.0~13.9999                chrome13;\n    Chrome                  12.0~12.9999                chrome12;\n    Chrome                  11.0~11.9999                chrome11;\n    Chrome                  10.0~10.9999                chrome10;\n    Chrome                  0~0.9999                    chrome_low;\n\n    # match version 4.0 exactly\n    ChromePlus              4.0                         chromeplus4;\n    ChromePlus              0~3.9999                    chromeplus;\n\n    Firebird                3.0~3.9999                  firebird3;\n    Firebird                0.7~0.7.9999                firebird07;\n    Firebird                0.6~0.6.9999                firebird06;\n\n    Firefox                 9.0~9.9999                  firefox9;\n    Firefox                 7.0~7.9999                  firefox7;\n    Firefox                 8.0~8.9999                  firefox8;\n    Firefox                 6.0~6.9999                  firefox6;\n    Firefox                 5.0~5.9999                  firefox5;\n    Firefox                 4.0~4.9999                  firefox4;\n    Firefox                 0~3.9999                    firefox_low;\n\n    MSIE                    10.0~10.9999                msie10;\n    MSIE                    9.0~9.9999                  msie9;\n    MSIE                    8.0~8.9999                  msie8;\n    MSIE                    7.0~7.9999                  msie7;\n    MSIE                    6.0~6.9999                  msie6;\n    # match version less than 5.9999[-OO,5.9999]\n    MSIE                    5.9999-                     msie_low;\n\n    Maxthon                 3.0~3.9999                  maxthon3;\n    Maxthon                 2.9999-                     maxthon_low;\n\n    Safari                  419.9999-                   safari_low;\n    Safari                  522.0~525.29.9999           safari3;\n    Safari                  526.8~526.11.999            safari_dp1;\n    Safari                  530.18.0~533.15.999         safari4;\n    Safari                  533.16.0~533.17.999         safari501;\n    Safari                  533.18.0~533.18.6           safari502;\n    Safari                  6533.18.0~6533.18.5.99      safari502;\n    Safari                  533.19.4~533.19.9           safari503;\n    Safari                  533.20.0~533.20.27          safari504;\n    Safari                  533.21~533.22               safari505;\n    Safari                  7534.48~7534.48.9          safari51;\n    Safari                  534.53~534.57               safari51;\n    Safari                  8536.25~8536.9999           safari6;\n\n    # match all versions\n    Opera                                               opera;\n\n    # mobile browsers\n    BlackBerry                                          blackberry;\n\n    Blazer                                              blazer;\n\n    'Maemo Browser'                                     maemobrowser;\n\n    'Opera Mini'                                        operamini;\n    'Opera Mobile'                                      operamobile;\n\n    Skyfire                                             skyfire;\n\n    UCWEB                                               UCWEB;\n}\n\nuser_agent $os {\n    default                                             unknown;\n\n    iPad                                                ipad;\n    iPhone                                              iphone;\n    Symbian                                             symbian;\n    MeeGo                                               meego;\n    'Windows Phone'                                     windowsphone;\n    Android                                             android;\n    BlackBerry                                          blackberry;\n    MIUI                                                miui;\n    'Windows NT'            5.1                         windowsxp;\n    'Windows NT'            6.1                         windows7;\n    'Windows NT'            6.0                         windowsvista;\n    linux                                               linux;\n    unix                                                unix;\n    FreeBSD                                             freebsd;\n    Macintosh                                           macos;\n}\n"
  },
  {
    "path": "conf/fastcgi.conf",
    "content": "\nfastcgi_param  SCRIPT_FILENAME    $document_root$fastcgi_script_name;\nfastcgi_param  QUERY_STRING       $query_string;\nfastcgi_param  REQUEST_METHOD     $request_method;\nfastcgi_param  CONTENT_TYPE       $content_type;\nfastcgi_param  CONTENT_LENGTH     $content_length;\n\nfastcgi_param  SCRIPT_NAME        $fastcgi_script_name;\nfastcgi_param  REQUEST_URI        $request_uri;\nfastcgi_param  DOCUMENT_URI       $document_uri;\nfastcgi_param  DOCUMENT_ROOT      $document_root;\nfastcgi_param  SERVER_PROTOCOL    $server_protocol;\nfastcgi_param  REQUEST_SCHEME     $scheme;\nfastcgi_param  HTTPS              $https if_not_empty;\n\nfastcgi_param  GATEWAY_INTERFACE  CGI/1.1;\nfastcgi_param  SERVER_SOFTWARE    nginx/$nginx_version;\n\nfastcgi_param  REMOTE_ADDR        $remote_addr;\nfastcgi_param  REMOTE_PORT        $remote_port;\nfastcgi_param  SERVER_ADDR        $server_addr;\nfastcgi_param  SERVER_PORT        $server_port;\nfastcgi_param  SERVER_NAME        $server_name;\n\n# PHP only, required if PHP was built with --enable-force-cgi-redirect\nfastcgi_param  REDIRECT_STATUS    200;\n"
  },
  {
    "path": "conf/fastcgi_params",
    "content": "\nfastcgi_param  QUERY_STRING       $query_string;\nfastcgi_param  REQUEST_METHOD     $request_method;\nfastcgi_param  CONTENT_TYPE       $content_type;\nfastcgi_param  CONTENT_LENGTH     $content_length;\n\nfastcgi_param  SCRIPT_NAME        $fastcgi_script_name;\nfastcgi_param  REQUEST_URI        $request_uri;\nfastcgi_param  DOCUMENT_URI       $document_uri;\nfastcgi_param  DOCUMENT_ROOT      $document_root;\nfastcgi_param  SERVER_PROTOCOL    $server_protocol;\nfastcgi_param  REQUEST_SCHEME     $scheme;\nfastcgi_param  HTTPS              $https if_not_empty;\n\nfastcgi_param  GATEWAY_INTERFACE  CGI/1.1;\nfastcgi_param  SERVER_SOFTWARE    nginx/$nginx_version;\n\nfastcgi_param  REMOTE_ADDR        $remote_addr;\nfastcgi_param  REMOTE_PORT        $remote_port;\nfastcgi_param  SERVER_ADDR        $server_addr;\nfastcgi_param  SERVER_PORT        $server_port;\nfastcgi_param  SERVER_NAME        $server_name;\n\n# PHP only, required if PHP was built with --enable-force-cgi-redirect\nfastcgi_param  REDIRECT_STATUS    200;\n"
  },
  {
    "path": "conf/koi-utf",
    "content": "\n# This map is not a full koi8-r <> utf8 map: it does not contain\n# box-drawing and some other characters.  Besides this map contains\n# several koi8-u and Byelorussian letters which are not in koi8-r.\n# If you need a full and standard map, use contrib/unicode2nginx/koi-utf\n# map instead.\n\ncharset_map  koi8-r  utf-8 {\n\n    80  E282AC ; # euro\n\n    95  E280A2 ; # bullet\n\n    9A  C2A0 ;   # &nbsp;\n\n    9E  C2B7 ;   # &middot;\n\n    A3  D191 ;   # small yo\n    A4  D194 ;   # small Ukrainian ye\n\n    A6  D196 ;   # small Ukrainian i\n    A7  D197 ;   # small Ukrainian yi\n\n    AD  D291 ;   # small Ukrainian soft g\n    AE  D19E ;   # small Byelorussian short u\n\n    B0  C2B0 ;   # &deg;\n\n    B3  D081 ;   # capital YO\n    B4  D084 ;   # capital Ukrainian YE\n\n    B6  D086 ;   # capital Ukrainian I\n    B7  D087 ;   # capital Ukrainian YI\n\n    B9  E28496 ; # numero sign\n\n    BD  D290 ;   # capital Ukrainian soft G\n    BE  D18E ;   # capital Byelorussian short U\n\n    BF  C2A9 ;   # (C)\n\n    C0  D18E ;   # small yu\n    C1  D0B0 ;   # small a\n    C2  D0B1 ;   # small b\n    C3  D186 ;   # small ts\n    C4  D0B4 ;   # small d\n    C5  D0B5 ;   # small ye\n    C6  D184 ;   # small f\n    C7  D0B3 ;   # small g\n    C8  D185 ;   # small kh\n    C9  D0B8 ;   # small i\n    CA  D0B9 ;   # small j\n    CB  D0BA ;   # small k\n    CC  D0BB ;   # small l\n    CD  D0BC ;   # small m\n    CE  D0BD ;   # small n\n    CF  D0BE ;   # small o\n\n    D0  D0BF ;   # small p\n    D1  D18F ;   # small ya\n    D2  D180 ;   # small r\n    D3  D181 ;   # small s\n    D4  D182 ;   # small t\n    D5  D183 ;   # small u\n    D6  D0B6 ;   # small zh\n    D7  D0B2 ;   # small v\n    D8  D18C ;   # small soft sign\n    D9  D18B ;   # small y\n    DA  D0B7 ;   # small z\n    DB  D188 ;   # small sh\n    DC  D18D ;   # small e\n    DD  D189 ;   # small shch\n    DE  D187 ;   # small ch\n    DF  D18A ;   # small hard sign\n\n    E0  D0AE ;   # capital YU\n    E1  D090 ;   # capital A\n    E2  D091 ;   # capital B\n    E3  D0A6 ;   # capital TS\n    E4  D094 ;   # capital D\n    E5  D095 ;   # capital YE\n    E6  D0A4 ;   # capital F\n    E7  D093 ;   # capital G\n    E8  D0A5 ;   # capital KH\n    E9  D098 ;   # capital I\n    EA  D099 ;   # capital J\n    EB  D09A ;   # capital K\n    EC  D09B ;   # capital L\n    ED  D09C ;   # capital M\n    EE  D09D ;   # capital N\n    EF  D09E ;   # capital O\n\n    F0  D09F ;   # capital P\n    F1  D0AF ;   # capital YA\n    F2  D0A0 ;   # capital R\n    F3  D0A1 ;   # capital S\n    F4  D0A2 ;   # capital T\n    F5  D0A3 ;   # capital U\n    F6  D096 ;   # capital ZH\n    F7  D092 ;   # capital V\n    F8  D0AC ;   # capital soft sign\n    F9  D0AB ;   # capital Y\n    FA  D097 ;   # capital Z\n    FB  D0A8 ;   # capital SH\n    FC  D0AD ;   # capital E\n    FD  D0A9 ;   # capital SHCH\n    FE  D0A7 ;   # capital CH\n    FF  D0AA ;   # capital hard sign\n}\n"
  },
  {
    "path": "conf/koi-win",
    "content": "\ncharset_map  koi8-r  windows-1251 {\n\n    80  88 ; # euro\n\n    95  95 ; # bullet\n\n    9A  A0 ; # &nbsp;\n\n    9E  B7 ; # &middot;\n\n    A3  B8 ; # small yo\n    A4  BA ; # small Ukrainian ye\n\n    A6  B3 ; # small Ukrainian i\n    A7  BF ; # small Ukrainian yi\n\n    AD  B4 ; # small Ukrainian soft g\n    AE  A2 ; # small Byelorussian short u\n\n    B0  B0 ; # &deg;\n\n    B3  A8 ; # capital YO\n    B4  AA ; # capital Ukrainian YE\n\n    B6  B2 ; # capital Ukrainian I\n    B7  AF ; # capital Ukrainian YI\n\n    B9  B9 ; # numero sign\n\n    BD  A5 ; # capital Ukrainian soft G\n    BE  A1 ; # capital Byelorussian short U\n\n    BF  A9 ; # (C)\n\n    C0  FE ; # small yu\n    C1  E0 ; # small a\n    C2  E1 ; # small b\n    C3  F6 ; # small ts\n    C4  E4 ; # small d\n    C5  E5 ; # small ye\n    C6  F4 ; # small f\n    C7  E3 ; # small g\n    C8  F5 ; # small kh\n    C9  E8 ; # small i\n    CA  E9 ; # small j\n    CB  EA ; # small k\n    CC  EB ; # small l\n    CD  EC ; # small m\n    CE  ED ; # small n\n    CF  EE ; # small o\n\n    D0  EF ; # small p\n    D1  FF ; # small ya\n    D2  F0 ; # small r\n    D3  F1 ; # small s\n    D4  F2 ; # small t\n    D5  F3 ; # small u\n    D6  E6 ; # small zh\n    D7  E2 ; # small v\n    D8  FC ; # small soft sign\n    D9  FB ; # small y\n    DA  E7 ; # small z\n    DB  F8 ; # small sh\n    DC  FD ; # small e\n    DD  F9 ; # small shch\n    DE  F7 ; # small ch\n    DF  FA ; # small hard sign\n\n    E0  DE ; # capital YU\n    E1  C0 ; # capital A\n    E2  C1 ; # capital B\n    E3  D6 ; # capital TS\n    E4  C4 ; # capital D\n    E5  C5 ; # capital YE\n    E6  D4 ; # capital F\n    E7  C3 ; # capital G\n    E8  D5 ; # capital KH\n    E9  C8 ; # capital I\n    EA  C9 ; # capital J\n    EB  CA ; # capital K\n    EC  CB ; # capital L\n    ED  CC ; # capital M\n    EE  CD ; # capital N\n    EF  CE ; # capital O\n\n    F0  CF ; # capital P\n    F1  DF ; # capital YA\n    F2  D0 ; # capital R\n    F3  D1 ; # capital S\n    F4  D2 ; # capital T\n    F5  D3 ; # capital U\n    F6  C6 ; # capital ZH\n    F7  C2 ; # capital V\n    F8  DC ; # capital soft sign\n    F9  DB ; # capital Y\n    FA  C7 ; # capital Z\n    FB  D8 ; # capital SH\n    FC  DD ; # capital E\n    FD  D9 ; # capital SHCH\n    FE  D7 ; # capital CH\n    FF  DA ; # capital hard sign\n}\n"
  },
  {
    "path": "conf/mime.types",
    "content": "\ntypes {\n    text/html                                        html htm shtml;\n    text/css                                         css;\n    text/xml                                         xml;\n    image/gif                                        gif;\n    image/jpeg                                       jpeg jpg;\n    application/javascript                           js;\n    application/atom+xml                             atom;\n    application/rss+xml                              rss;\n\n    text/mathml                                      mml;\n    text/plain                                       txt;\n    text/vnd.sun.j2me.app-descriptor                 jad;\n    text/vnd.wap.wml                                 wml;\n    text/x-component                                 htc;\n\n    image/avif                                       avif;\n    image/png                                        png;\n    image/svg+xml                                    svg svgz;\n    image/tiff                                       tif tiff;\n    image/vnd.wap.wbmp                               wbmp;\n    image/webp                                       webp;\n    image/x-icon                                     ico;\n    image/x-jng                                      jng;\n    image/x-ms-bmp                                   bmp;\n\n    font/woff                                        woff;\n    font/woff2                                       woff2;\n\n    application/java-archive                         jar war ear;\n    application/json                                 json;\n    application/mac-binhex40                         hqx;\n    application/msword                               doc;\n    application/pdf                                  pdf;\n    application/postscript                           ps eps ai;\n    application/rtf                                  rtf;\n    application/vnd.apple.mpegurl                    m3u8;\n    application/vnd.google-earth.kml+xml             kml;\n    application/vnd.google-earth.kmz                 kmz;\n    application/vnd.ms-excel                         xls;\n    application/vnd.ms-fontobject                    eot;\n    application/vnd.ms-powerpoint                    ppt;\n    application/vnd.oasis.opendocument.graphics      odg;\n    application/vnd.oasis.opendocument.presentation  odp;\n    application/vnd.oasis.opendocument.spreadsheet   ods;\n    application/vnd.oasis.opendocument.text          odt;\n    application/vnd.openxmlformats-officedocument.presentationml.presentation\n                                                     pptx;\n    application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\n                                                     xlsx;\n    application/vnd.openxmlformats-officedocument.wordprocessingml.document\n                                                     docx;\n    application/vnd.wap.wmlc                         wmlc;\n    application/wasm                                 wasm;\n    application/x-7z-compressed                      7z;\n    application/x-cocoa                              cco;\n    application/x-java-archive-diff                  jardiff;\n    application/x-java-jnlp-file                     jnlp;\n    application/x-makeself                           run;\n    application/x-perl                               pl pm;\n    application/x-pilot                              prc pdb;\n    application/x-rar-compressed                     rar;\n    application/x-redhat-package-manager             rpm;\n    application/x-sea                                sea;\n    application/x-shockwave-flash                    swf;\n    application/x-stuffit                            sit;\n    application/x-tcl                                tcl tk;\n    application/x-x509-ca-cert                       der pem crt;\n    application/x-xpinstall                          xpi;\n    application/xhtml+xml                            xhtml;\n    application/xspf+xml                             xspf;\n    application/zip                                  zip;\n\n    application/octet-stream                         bin exe dll;\n    application/octet-stream                         deb;\n    application/octet-stream                         dmg;\n    application/octet-stream                         iso img;\n    application/octet-stream                         msi msp msm;\n\n    audio/midi                                       mid midi kar;\n    audio/mpeg                                       mp3;\n    audio/ogg                                        ogg;\n    audio/x-m4a                                      m4a;\n    audio/x-realaudio                                ra;\n\n    video/3gpp                                       3gpp 3gp;\n    video/mp2t                                       ts;\n    video/mp4                                        mp4;\n    video/mpeg                                       mpeg mpg;\n    video/quicktime                                  mov;\n    video/webm                                       webm;\n    video/x-flv                                      flv;\n    video/x-m4v                                      m4v;\n    video/x-mng                                      mng;\n    video/x-ms-asf                                   asx asf;\n    video/x-ms-wmv                                   wmv;\n    video/x-msvideo                                  avi;\n}\n"
  },
  {
    "path": "conf/nginx.conf",
    "content": "\n#user  nobody;\nworker_processes  1;\n\n#error_log  logs/error.log;\n#error_log  logs/error.log  notice;\n#error_log  logs/error.log  info;\n#error_log  \"pipe:rollback logs/error_log interval=1d baknum=7 maxsize=2G\";\n\n#pid        logs/nginx.pid;\n\n\nevents {\n    worker_connections  1024;\n}\n\n\nhttp {\n    include       mime.types;\n    default_type  application/octet-stream;\n\n    #log_format  main  '$remote_addr - $remote_user [$time_local] \"$request\" '\n    #                  '$status $body_bytes_sent \"$http_referer\" '\n    #                  '\"$http_user_agent\" \"$http_x_forwarded_for\"';\n\n    #access_log  logs/access.log  main;\n    #access_log  \"pipe:rollback logs/access_log interval=1d baknum=7 maxsize=2G\"  main;\n\n    sendfile        on;\n    #tcp_nopush     on;\n\n    #keepalive_timeout  0;\n    keepalive_timeout  65;\n\n    #gzip  on;\n\n    server {\n        listen       80;\n        server_name  localhost;\n\n        #charset koi8-r;\n\n        #access_log  logs/host.access.log  main;\n        #access_log  \"pipe:rollback logs/host.access_log interval=1d baknum=7 maxsize=2G\"  main;\n\n        location / {\n            root   html;\n            index  index.html index.htm;\n        }\n\n        #error_page  404              /404.html;\n\n        # redirect server error pages to the static page /50x.html\n        #\n        error_page   500 502 503 504  /50x.html;\n        location = /50x.html {\n            root   html;\n        }\n\n        # proxy the PHP scripts to Apache listening on 127.0.0.1:80\n        #\n        #location ~ \\.php$ {\n        #    proxy_pass   http://127.0.0.1;\n        #}\n\n        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000\n        #\n        #location ~ \\.php$ {\n        #    root           html;\n        #    fastcgi_pass   127.0.0.1:9000;\n        #    fastcgi_index  index.php;\n        #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;\n        #    include        fastcgi_params;\n        #}\n\n        # pass the Dubbo rpc to Dubbo provider server listening on 127.0.0.1:20880\n        #\n        #location /dubbo {\n        #    dubbo_pass_all_headers on;\n        #    dubbo_pass_set args $args;\n        #    dubbo_pass_set uri $uri;\n        #    dubbo_pass_set method $request_method;\n        #\n        #    dubbo_pass org.apache.dubbo.samples.tengine.DemoService 0.0.0 tengineDubbo dubbo_backend;\n        #}\n\n        # deny access to .htaccess files, if Apache's document root\n        # concurs with nginx's one\n        #\n        #location ~ /\\.ht {\n        #    deny  all;\n        #}\n    }\n\n    # upstream for Dubbo rpc to Dubbo provider server listening on 127.0.0.1:20880\n    #\n    #upstream dubbo_backend {\n    #    multi 1;\n    #    server 127.0.0.1:20880;\n    #}\n\n    # another virtual host using mix of IP-, name-, and port-based configuration\n    #\n    #server {\n    #    listen       8000;\n    #    listen       somename:8080;\n    #    server_name  somename  alias  another.alias;\n\n    #    location / {\n    #        root   html;\n    #        index  index.html index.htm;\n    #    }\n    #}\n\n\n    # HTTPS server\n    #\n    #server {\n    #    listen       443 ssl;\n    #    server_name  localhost;\n\n    #    ssl_certificate      cert.pem;\n    #    ssl_certificate_key  cert.key;\n\n    #    ssl_session_cache    shared:SSL:1m;\n    #    ssl_session_timeout  5m;\n\n    #    ssl_ciphers  HIGH:!aNULL:!MD5;\n    #    ssl_prefer_server_ciphers  on;\n\n    #    location / {\n    #        root   html;\n    #        index  index.html index.htm;\n    #    }\n    #}\n\n}\n"
  },
  {
    "path": "conf/scgi_params",
    "content": "\nscgi_param  REQUEST_METHOD     $request_method;\nscgi_param  REQUEST_URI        $request_uri;\nscgi_param  QUERY_STRING       $query_string;\nscgi_param  CONTENT_TYPE       $content_type;\n\nscgi_param  DOCUMENT_URI       $document_uri;\nscgi_param  DOCUMENT_ROOT      $document_root;\nscgi_param  SCGI               1;\nscgi_param  SERVER_PROTOCOL    $server_protocol;\nscgi_param  REQUEST_SCHEME     $scheme;\nscgi_param  HTTPS              $https if_not_empty;\n\nscgi_param  REMOTE_ADDR        $remote_addr;\nscgi_param  REMOTE_PORT        $remote_port;\nscgi_param  SERVER_PORT        $server_port;\nscgi_param  SERVER_NAME        $server_name;\n"
  },
  {
    "path": "conf/uwsgi_params",
    "content": "\nuwsgi_param  QUERY_STRING       $query_string;\nuwsgi_param  REQUEST_METHOD     $request_method;\nuwsgi_param  CONTENT_TYPE       $content_type;\nuwsgi_param  CONTENT_LENGTH     $content_length;\n\nuwsgi_param  REQUEST_URI        $request_uri;\nuwsgi_param  PATH_INFO          $document_uri;\nuwsgi_param  DOCUMENT_ROOT      $document_root;\nuwsgi_param  SERVER_PROTOCOL    $server_protocol;\nuwsgi_param  REQUEST_SCHEME     $scheme;\nuwsgi_param  HTTPS              $https if_not_empty;\n\nuwsgi_param  REMOTE_ADDR        $remote_addr;\nuwsgi_param  REMOTE_PORT        $remote_port;\nuwsgi_param  SERVER_PORT        $server_port;\nuwsgi_param  SERVER_NAME        $server_name;\n"
  },
  {
    "path": "conf/win-utf",
    "content": "\n# This map is not a full windows-1251 <> utf8 map: it does not\n# contain Serbian and Macedonian letters.  If you need a full map,\n# use contrib/unicode2nginx/win-utf map instead.\n\ncharset_map  windows-1251  utf-8 {\n\n    82  E2809A ; # single low-9 quotation mark\n\n    84  E2809E ; # double low-9 quotation mark\n    85  E280A6 ; # ellipsis\n    86  E280A0 ; # dagger\n    87  E280A1 ; # double dagger\n    88  E282AC ; # euro\n    89  E280B0 ; # per mille\n\n    91  E28098 ; # left single quotation mark\n    92  E28099 ; # right single quotation mark\n    93  E2809C ; # left double quotation mark\n    94  E2809D ; # right double quotation mark\n    95  E280A2 ; # bullet\n    96  E28093 ; # en dash\n    97  E28094 ; # em dash\n\n    99  E284A2 ; # trade mark sign\n\n    A0  C2A0 ;   # &nbsp;\n    A1  D18E ;   # capital Byelorussian short U\n    A2  D19E ;   # small Byelorussian short u\n\n    A4  C2A4 ;   # currency sign\n    A5  D290 ;   # capital Ukrainian soft G\n    A6  C2A6 ;   # borken bar\n    A7  C2A7 ;   # section sign\n    A8  D081 ;   # capital YO\n    A9  C2A9 ;   # (C)\n    AA  D084 ;   # capital Ukrainian YE\n    AB  C2AB ;   # left-pointing double angle quotation mark\n    AC  C2AC ;   # not sign\n    AD  C2AD ;   # soft hypen\n    AE  C2AE ;   # (R)\n    AF  D087 ;   # capital Ukrainian YI\n\n    B0  C2B0 ;   # &deg;\n    B1  C2B1 ;   # plus-minus sign\n    B2  D086 ;   # capital Ukrainian I\n    B3  D196 ;   # small Ukrainian i\n    B4  D291 ;   # small Ukrainian soft g\n    B5  C2B5 ;   # micro sign\n    B6  C2B6 ;   # pilcrow sign\n    B7  C2B7 ;   # &middot;\n    B8  D191 ;   # small yo\n    B9  E28496 ; # numero sign\n    BA  D194 ;   # small Ukrainian ye\n    BB  C2BB ;   # right-pointing double angle quotation mark\n\n    BF  D197 ;   # small Ukrainian yi\n\n    C0  D090 ;   # capital A\n    C1  D091 ;   # capital B\n    C2  D092 ;   # capital V\n    C3  D093 ;   # capital G\n    C4  D094 ;   # capital D\n    C5  D095 ;   # capital YE\n    C6  D096 ;   # capital ZH\n    C7  D097 ;   # capital Z\n    C8  D098 ;   # capital I\n    C9  D099 ;   # capital J\n    CA  D09A ;   # capital K\n    CB  D09B ;   # capital L\n    CC  D09C ;   # capital M\n    CD  D09D ;   # capital N\n    CE  D09E ;   # capital O\n    CF  D09F ;   # capital P\n\n    D0  D0A0 ;   # capital R\n    D1  D0A1 ;   # capital S\n    D2  D0A2 ;   # capital T\n    D3  D0A3 ;   # capital U\n    D4  D0A4 ;   # capital F\n    D5  D0A5 ;   # capital KH\n    D6  D0A6 ;   # capital TS\n    D7  D0A7 ;   # capital CH\n    D8  D0A8 ;   # capital SH\n    D9  D0A9 ;   # capital SHCH\n    DA  D0AA ;   # capital hard sign\n    DB  D0AB ;   # capital Y\n    DC  D0AC ;   # capital soft sign\n    DD  D0AD ;   # capital E\n    DE  D0AE ;   # capital YU\n    DF  D0AF ;   # capital YA\n\n    E0  D0B0 ;   # small a\n    E1  D0B1 ;   # small b\n    E2  D0B2 ;   # small v\n    E3  D0B3 ;   # small g\n    E4  D0B4 ;   # small d\n    E5  D0B5 ;   # small ye\n    E6  D0B6 ;   # small zh\n    E7  D0B7 ;   # small z\n    E8  D0B8 ;   # small i\n    E9  D0B9 ;   # small j\n    EA  D0BA ;   # small k\n    EB  D0BB ;   # small l\n    EC  D0BC ;   # small m\n    ED  D0BD ;   # small n\n    EE  D0BE ;   # small o\n    EF  D0BF ;   # small p\n\n    F0  D180 ;   # small r\n    F1  D181 ;   # small s\n    F2  D182 ;   # small t\n    F3  D183 ;   # small u\n    F4  D184 ;   # small f\n    F5  D185 ;   # small kh\n    F6  D186 ;   # small ts\n    F7  D187 ;   # small ch\n    F8  D188 ;   # small sh\n    F9  D189 ;   # small shch\n    FA  D18A ;   # small hard sign\n    FB  D18B ;   # small y\n    FC  D18C ;   # small soft sign\n    FD  D18D ;   # small e\n    FE  D18E ;   # small yu\n    FF  D18F ;   # small ya\n}\n"
  },
  {
    "path": "configure",
    "content": "#!/bin/sh\n\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\n\nLC_ALL=C\nexport LC_ALL\n\n. auto/options\n. auto/init\n. auto/sources\n\ntest -d $NGX_OBJS || mkdir -p $NGX_OBJS\n\necho > $NGX_AUTO_HEADERS_H\necho > $NGX_AUTOCONF_ERR\n\necho \"#define NGX_CONFIGURE \\\"$NGX_CONFIGURE\\\"\" > $NGX_AUTO_CONFIG_H\n\n\nif [ $NGX_DEBUG = YES ]; then\n    have=NGX_DEBUG . auto/have\nfi\n\n\nif test -z \"$NGX_PLATFORM\"; then\n    echo \"checking for OS\"\n\n    NGX_SYSTEM=`uname -s 2>/dev/null`\n    NGX_RELEASE=`uname -r 2>/dev/null`\n    NGX_MACHINE=`uname -m 2>/dev/null`\n\n    echo \" + $NGX_SYSTEM $NGX_RELEASE $NGX_MACHINE\"\n\n    NGX_PLATFORM=\"$NGX_SYSTEM:$NGX_RELEASE:$NGX_MACHINE\";\n\n    case \"$NGX_SYSTEM\" in\n        MINGW32_* | MINGW64_* | MSYS_*)\n            NGX_PLATFORM=win32\n        ;;\n    esac\n\nelse\n    echo \"building for $NGX_PLATFORM\"\n    NGX_SYSTEM=$NGX_PLATFORM\nfi\n\n. auto/cc/conf\n\nif [ \"$NGX_PLATFORM\" != win32 ]; then\n    . auto/headers\nfi\n\n. auto/os/conf\n\nif [ \"$NGX_PLATFORM\" != win32 ]; then\n    . auto/unix\nfi\n\n. auto/threads\n. auto/modules\n. auto/lib/conf\n\ncase \".$NGX_PREFIX\" in\n    .)\n        NGX_PREFIX=${NGX_PREFIX:-/usr/local/nginx}\n        have=NGX_PREFIX value=\"\\\"$NGX_PREFIX/\\\"\" . auto/define\n    ;;\n\n    .!)\n        NGX_PREFIX=\n    ;;\n\n    *)\n        have=NGX_PREFIX value=\"\\\"$NGX_PREFIX/\\\"\" . auto/define\n    ;;\nesac\n\nif [ \".$NGX_CONF_PREFIX\" != \".\" ]; then\n    have=NGX_CONF_PREFIX value=\"\\\"$NGX_CONF_PREFIX/\\\"\" . auto/define\nfi\n\nhave=NGX_SBIN_PATH value=\"\\\"$NGX_SBIN_PATH\\\"\" . auto/define\nhave=NGX_CONF_PATH value=\"\\\"$NGX_CONF_PATH\\\"\" . auto/define\nhave=NGX_PID_PATH value=\"\\\"$NGX_PID_PATH\\\"\" . auto/define\nhave=NGX_LOCK_PATH value=\"\\\"$NGX_LOCK_PATH\\\"\" . auto/define\nhave=NGX_ERROR_LOG_PATH value=\"\\\"$NGX_ERROR_LOG_PATH\\\"\" . auto/define\n\nhave=NGX_HTTP_LOG_PATH value=\"\\\"$NGX_HTTP_LOG_PATH\\\"\" . auto/define\nhave=NGX_HTTP_CLIENT_TEMP_PATH value=\"\\\"$NGX_HTTP_CLIENT_TEMP_PATH\\\"\"\n. auto/define\nhave=NGX_HTTP_PROXY_TEMP_PATH value=\"\\\"$NGX_HTTP_PROXY_TEMP_PATH\\\"\"\n. auto/define\nhave=NGX_HTTP_FASTCGI_TEMP_PATH value=\"\\\"$NGX_HTTP_FASTCGI_TEMP_PATH\\\"\"\n. auto/define\nhave=NGX_HTTP_UWSGI_TEMP_PATH value=\"\\\"$NGX_HTTP_UWSGI_TEMP_PATH\\\"\"\n. auto/define\nhave=NGX_HTTP_SCGI_TEMP_PATH value=\"\\\"$NGX_HTTP_SCGI_TEMP_PATH\\\"\"\n. auto/define\n\n. auto/make\n. auto/lib/make\n. auto/install\n\n# STUB\n. auto/stubs\n\nhave=NGX_USER value=\"\\\"$NGX_USER\\\"\" . auto/define\nhave=NGX_GROUP value=\"\\\"$NGX_GROUP\\\"\" . auto/define\n\nif [ \".$NGX_BUILD\" != \".\" ]; then\n    have=NGX_BUILD value=\"\\\"$NGX_BUILD\\\"\" . auto/define\nfi\n\n. auto/summary\n"
  },
  {
    "path": "contrib/README",
    "content": "\ngeo2nginx.pl \t\tby Andrei Nigmatulin\n\n\tThe perl script to convert CSV geoip database ( free download\n\tat http://www.maxmind.com/app/geoip_country ) to format, suitable\n\tfor use by the ngx_http_geo_module.\n\n\nunicode2nginx\t\tby Maxim Dounin\n\n\tThe perl script to convert unicode mappings ( available\n\tat http://www.unicode.org/Public/MAPPINGS/ ) to the nginx\n\tconfiguration file format.\n\tTwo generated full maps for windows-1251 and koi8-r.\n\n\nvim\t\t\tby Evan Miller\n\n\tSyntax highlighting of nginx configuration for vim, to be\n\tplaced into ~/.vim/.\n\n"
  },
  {
    "path": "contrib/dso.in",
    "content": "DSO_CORE_DEPS=%%CORE_DEPS%%\nDSO_HTTP_DEPS=%%HTTP_DEPS%%\nDSO_ALL_INCS=%%ALL_INCS%%\n\n\nALL_INCS=\nCORE_INCS=\nHTTP_INCS=\nCORE_DEPS=\nHTTP_DEPS=\n\nNGX_OBJS=objs\n\nopt=\nhelp=no\n\nfor option\ndo\n    opt=\"$opt `echo $option | sed -e \\\"s/\\(--[^=]*=\\)\\(.* .*\\)/\\1'\\2'/\\\"`\"\n\n    case \"$option\" in\n        -*=*) value=`echo \"$option\" | sed -e 's/[-_a-zA-Z0-9]*=//'` ;;\n           *) value=\"\" ;;\n    esac\n\n    case \"$option\" in\n        --help)                          help=yes                       ;;\n        -h)                              help=yes                       ;;\n\n        --dst=)                          NGX_DSO_PREFIX=\"!\"             ;;\n        --dst=*)                         NGX_DSO_PREFIX=\"$value\"        ;;\n        -d=*)                            NGX_DSO_PREFIX=\"$value\"        ;;\n\n        --add-module=*)                  NGX_DSO_ADDONS=\"$NGX_DSO_ADDONS $value\" ;;\n        -a=*)                            NGX_DSO_ADDONS=\"$NGX_DSO_ADDONS $value\" ;;\n\n        --nginx-include=*)               NGX_INCLUDE_PATH=\"$value\"       ;;\n        -s=*)                            NGX_INCLUDE_PATH=\"$value\"       ;;\n\n        *)\n            echo \"$0: error: invalid option \\\"$option\\\"\"\n            exit 1\n        ;;\n    esac\ndone\n\n\nif ! test -n \"$option\" ; then\n    help=yes\nfi\n\n\nif [ $help = yes ]; then\n\ncat << END\n\n    -h, --help                 display this help and exit\n    -d, --dst=PATH             set module installation path\n    -a, --add-module=PATH      external module which will be compiled(absolute path)\n    -s, --nginx-include=SOURCE set nginx include path(absolute path)\n\nEND\n\n    exit 1\nfi\n\n\n\n# arg1 is addon dir\n# arg2 is addon deps\n# arg3 is addon src\n\ngenerate_make () {\n    # mkdir build temp\n    cd $1\n    dso_binout=$ngx_addon_name\n    NGX_DSO_MAKEFILE=objs/Makefile\n    NGX_ADDON_DEPS=$2\n\n    cat << END                                                     > $NGX_DSO_MAKEFILE\n\nCC = $CC\nCFLAGS = $CFLAGS\nCPP = $CPP\nLINK = $LINK\n\n\nCORE_LIBS = $CORE_LIBS\nCORE_INCS = $CORE_INCS\nHTTP_INCS = $HTTP_INCS\n\nCORE_LINK = $CORE_LINK\nNGX_LD_OPT = $NGX_LD_OPT\n\nEND\n\n\n    cat << END                                                     >> $NGX_DSO_MAKEFILE\n\nALL_INCS = $ALL_INCS\n\nCORE_DEPS = $CORE_DEPS\n\nHTTP_DEPS = $HTTP_DEPS\n\nADDON_DEPS = \\$(CORE_DEPS) $NGX_ADDON_DEPS\n\nEND\n\n\n    if test -n \"$NGX_LD_OPT$CORE_LIBS\"; then\n        ngx_libs=`echo $NGX_LD_OPT $CORE_LIBS \\\n            | sed -e \"s/\\//$ngx_regex_dirsep/g\" -e \"s/^/$ngx_long_regex_cont/\"`\n    fi\n\n    for ngx_src in $NGX_ADDON_SRCS\n    do\n        ngx_obj=\"objs/src/`basename \\`dirname $ngx_src\\``\"\n\n        test -d $ngx_obj || mkdir -p $ngx_obj\n\n        ngx_obj=`echo $ngx_obj/\\`basename $ngx_src\\` | sed -e \"s/\\//$ngx_regex_dirsep/g\"`\n\n        ngx_obj=`echo $ngx_obj \\\n            | sed -e \"s#^\\(.*\\.\\)cpp\\\\$#$ngx_objs_dir\\1$ngx_objext#g\" \\\n                  -e \"s#^\\(.*\\.\\)cc\\\\$#$ngx_objs_dir\\1$ngx_objext#g\" \\\n                  -e \"s#^\\(.*\\.\\)c\\\\$#$ngx_objs_dir\\1$ngx_objext#g\" \\\n                  -e \"s#^\\(.*\\.\\)S\\\\$#$ngx_objs_dir\\1$ngx_objext#g\"`\n\n        ngx_dso_all_objs=\"$ngx_dso_all_objs $ngx_obj\"\n\n    done\n\n    ngx_deps=`echo $ngx_dso_all_objs $ngx_res $LINK_DEPS \\\n        | sed -e \"s/  *\\([^ ][^ ]*\\)/$ngx_regex_cont\\1/g\" \\\n              -e \"s/\\//$ngx_regex_dirsep/g\"`\n\n    ngx_objs=`echo $ngx_dso_all_objs \\\n        | sed -e \"s/  *\\([^ ][^ ]*\\)/$ngx_long_regex_cont\\1/g\" \\\n              -e \"s/\\//$ngx_regex_dirsep/g\"`\n\n    ngx_link=${CORE_LINK:+`echo $CORE_LINK \\\n        | sed -e \"s/\\//$ngx_regex_dirsep%%/g\" -e \"s/^/$ngx_long_regex_cont/\"`}\n\n    cat << END                                                    >> $NGX_DSO_MAKEFILE\n\n$NGX_OBJS${ngx_dirsep}${dso_binout}${ngx_soext}:\t$ngx_deps$ngx_spacer\n\t\\$(LINK) ${ngx_long_start}${ngx_binout} $NGX_OBJS${ngx_dirsep}${dso_binout}${ngx_soext}$ngx_long_cont$ngx_objs$ngx_libs$ngx_link\n\t$ngx_rcc\n${ngx_long_end}\nEND\n\n    for ngx_src in $NGX_ADDON_SRCS\n    do\n        ngx_obj=\"objs/src/`basename \\`dirname $ngx_src\\``\"\n\n        test -d $ngx_obj || mkdir -p $ngx_obj\n\n        ngx_obj=`echo $ngx_obj/\\`basename $ngx_src\\` | sed -e \"s#/#$ngx_regex_dirsep#g\"`\n\n        ngx_obj=`echo $ngx_obj \\\n            | sed -e \"s#^\\(.*\\.\\)cpp\\\\$#$ngx_objs_dir\\1$ngx_objext#g\" \\\n                  -e \"s#^\\(.*\\.\\)cc\\\\$#$ngx_objs_dir\\1$ngx_objext#g\" \\\n                  -e \"s#^\\(.*\\.\\)c\\\\$#$ngx_objs_dir\\1$ngx_objext#g\" \\\n                  -e \"s#^\\(.*\\.\\)S\\\\$#$ngx_objs_dir\\1$ngx_objext#g\"`\n\n        ngx_src=`echo $ngx_src | sed -e \"s#/#$ngx_regex_dirsep#g\"`\n\n\n        cat << END                                            >> $NGX_DSO_MAKEFILE\n\n$ngx_obj:\t\\$(ADDON_DEPS)$ngx_cont$ngx_src\n\t$ngx_cc$ngx_tab\\$(ALL_INCS)$ngx_tab$ngx_objout$ngx_obj$ngx_tab$ngx_src$NGX_AUX\n\nEND\n\n     done\n\n    make -f $NGX_DSO_MAKEFILE\n\n    if [ \"$?\" = '0' ]; then\n\tif test -n \"$NGX_DSO_PREFIX\"; then\n            test ! -f $NGX_DSO_PREFIX${dso_binout}${ngx_soext} \\\n\t\t|| unlink $NGX_DSO_PREFIX${dso_binout}${ngx_soext}\n            echo copying $NGX_OBJS${ngx_dirsep}${dso_binout}${ngx_soext} to $NGX_DSO_PREFIX\n            cp $NGX_OBJS${ngx_dirsep}${dso_binout}${ngx_soext} $NGX_DSO_PREFIX\n\telse\n            NGX_DSO_DEST=$NGX_PREFIX${ngx_dirsep}$NGX_DSO_PATH\n            [[ $NGX_DSO_PATH == /* ]] && NGX_DSO_DEST=$NGX_DSO_PATH\n            test ! -f $NGX_DSO_DEST${ngx_dirsep}${dso_binout}${ngx_soext} \\\n\t\t|| unlink $NGX_DSO_DEST${ngx_dirsep}${dso_binout}${ngx_soext}\n            echo copying $NGX_OBJS${ngx_dirsep}${dso_binout}${ngx_soext} to $NGX_DSO_DEST\n            cp $NGX_OBJS${ngx_dirsep}${dso_binout}${ngx_soext} $NGX_DSO_DEST\n\tfi\n    fi\n}\n\n\nif test -n \"$NGX_DSO_ADDONS\"; then\n\n    echo configuring additional modules\n\n    for dai in $DSO_ALL_INCS\n    do\n        ALL_INCS=\"$ALL_INCS $dai\"\n    done\n\n    ALL_INCS=\"$ALL_INCS -I$NGX_INCLUDE_PATH\"\n\n    for dcd in $DSO_CORE_DEPS\n    do\n        if test -n \"$dcd\"; then\n            case \".$dcd\" in\n                ./*)\n                    CORE_DEPS=\"$CORE_DEPS $dcd\"\n                    ;;\n\n                *)\n                    CORE_DEPS=\"$CORE_DEPS $NGX_INCLUDE_PATH/$dcd\"\n                    ;;\n            esac\n        fi\n    done\n\n    for dhd in $DSO_HTTP_DEPS\n    do\n        if test -n \"$dhd\"; then\n            case \".$dhd\" in\n                ./*)\n                    HTTP_DEPS=\"$HTTP_DEPS $dhd\"\n                    ;;\n\n                *)\n                    HTTP_DEPS=\"$HTTP_DEPS $NGX_INCLUDE_PATH/$dhd\"\n                    ;;\n            esac\n        fi\n    done\n\n    for ngx_addon_dir in $NGX_DSO_ADDONS\n    do\n        echo \"adding module in $ngx_addon_dir\"\n        if test -f $ngx_addon_dir/config; then\n            NGX_ADDON_SRCS=\n            NGX_ADDON_DEPS=\n\n            CORE_LIBS_TEMP=$CORE_LIBS\n            CORE_LIBS=\n\n            cd $ngx_addon_dir\n\n\t    CORE_INCS_TEMP=$CORE_INCS\n            HTTP_INCS_TEMP=$HTTP_INCS\n            CORE_LIBS_TEMP=$CORE_LIBS\n            CORE_INCS=\n            HTTP_INCS=\n            CORE_LIBS=\n\n            if test -d $NGX_OBJS; then\n                rm -rf $NGX_OBJS\n            fi\n\n            mkdir -p $NGX_OBJS/auto\n            touch $NGX_AUTOCONF_ERR\n            touch $NGX_AUTOTEST\n            touch $NGX_AUTO_CONFIG_H\n\n            cat << END                                            > $NGX_OBJS/auto/feature\n            $ngx_feature_file\nEND\n\n            cat << END                                            > $NGX_OBJS/auto/have\n            $ngx_have_file\nEND\n\t    cd $NGX_OBJS\n            . $ngx_addon_dir/config\n\n\t    dso_core_incs=`echo $CORE_INCS \\\n                | sed -e \"s/  *\\([^ ][^ ]*\\)/$ngx_regex_cont$ngx_include_opt\\1/g\" \\\n                -e \"s/\\//$ngx_regex_dirsep/g\"`\n\n            dso_http_incs=`echo $HTTP_INCS \\\n                | sed -e \"s/  *\\([^ ][^ ]*\\)/$ngx_regex_cont$ngx_include_opt\\1/g\" \\\n                -e \"s/\\//$ngx_regex_dirsep/g\"`\n\n            dso_core_libs=`echo $CORE_LIBS \\\n                | sed -e \"s/\\//$ngx_regex_dirsep/g\"`\n\n            if test -n \"$dso_core_incs\"; then\n                CORE_INCS=\"$CORE_INCS_TEMP -I $dso_core_incs\"\n            else\n                CORE_INCS=$CORE_INCS_TEMP\n            fi\n\n            if test -n \"$dso_http_incs\"; then\n                HTTP_INCS=\"$HTTP_INCS_TEMP -I $dso_http_incs\"\n            else\n                HTTP_INCS=$HTTP_INCS_TEMP\n            fi\n\n            if test -n \"$dso_core_libs\"; then\n                CORE_LIBS=\"$CORE_LIBS_TEMP $dso_core_libs\"\n            else\n                CORE_LIBS=$CORE_LIBS_TEMP\n            fi\n\n            cd -\n\n            cd $ngx_addon_dir\n            echo \" + $ngx_addon_name will be compiled\"\n            generate_make $ngx_addon_dir $NGX_ADDON_DEPS $NGX_ADDON_SRCS $ngx_addon_name\n        else\n            echo \"$0: error: no $ngx_addon_dir/config was found\"\n            exit 1\n        fi\n    done\nelse\n    echo \"please specify the module path\"\n    exit 1\nfi\n"
  },
  {
    "path": "contrib/geo2nginx.pl",
    "content": "#!/usr/bin/perl -w\r\n\r\n# (c) Andrei Nigmatulin, 2005\r\n#\r\n# this script provided \"as is\", without any warranties. use it at your own risk.\r\n#\r\n# special thanx to Andrew Sitnikov for perl port\r\n#\r\n# this script converts CSV geoip database (free download at http://www.maxmind.com/app/geoip_country)\r\n# to format, suitable for use with nginx_http_geo module (http://sysoev.ru/nginx)\r\n#\r\n# for example, line with ip range\r\n#\r\n#   \"62.16.68.0\",\"62.16.127.255\",\"1041253376\",\"1041268735\",\"RU\",\"Russian Federation\"\r\n#\r\n# will be converted to four subnetworks:\r\n#\r\n#   62.16.68.0/22 RU;\r\n#   62.16.72.0/21 RU;\r\n#   62.16.80.0/20 RU;\r\n#   62.16.96.0/19 RU;\r\n\r\n\r\nuse warnings;\r\nuse strict;\r\n\r\nwhile( <STDIN> ){\r\n\tif (/\"[^\"]+\",\"[^\"]+\",\"([^\"]+)\",\"([^\"]+)\",\"([^\"]+)\"/){\r\n\t\tprint_subnets($1, $2, $3);\r\n\t}\r\n}\r\n\r\nsub  print_subnets {\r\n\tmy ($a1, $a2, $c) = @_;\r\n\tmy $l;\r\n    while ($a1 <= $a2) {\r\n\t\tfor ($l = 0; ($a1 & (1 << $l)) == 0 && ($a1 + ((1 << ($l + 1)) - 1)) <= $a2; $l++){};\r\n\t\tprint long2ip($a1) . \"/\" . (32 - $l) . \" \" . $c . \";\\n\";\r\n    \t$a1 += (1 << $l);\r\n\t}\r\n}\r\n\r\nsub long2ip {\r\n\tmy $ip = shift;\r\n\r\n\tmy $str = 0;\r\n\r\n\t$str = ($ip & 255);\r\n\r\n\t$ip >>= 8;\r\n\t$str = ($ip & 255).\".$str\";\r\n\r\n\t$ip >>= 8;\r\n\t$str = ($ip & 255).\".$str\";\r\n\r\n\t$ip >>= 8;\r\n\t$str = ($ip & 255).\".$str\";\r\n}\r\n"
  },
  {
    "path": "contrib/stylechecker.py",
    "content": "#!/usr/bin/python\n\n\"\"\"usage: python stylechecker.py /path/to/the/c/code\"\"\"\n\nimport os\nimport sys\nimport string\nimport re\n\nWHITE = '\\033[97m'\nCYAN = '\\033[96m'\nBLUE = '\\033[94m'\nGREEN = '\\033[92m'\nYELLOW = '\\033[93m'\nRED = '\\033[91m'\nENDC = '\\033[0m'\n\ndef check_file(file):\n    if re.search('\\.[c|h]$', file) == None:\n        return\n\n    f = open(file)\n    i = 1\n    file_name_printed = False\n\n    for line in f:\n        line = line.replace('\\n', '')\n\n        # check the number of columns greater than 80\n        if len(line) > 80:\n            if not file_name_printed:\n                print RED + file + ':' + ENDC\n                file_name_printed = True\n            print (GREEN + '    [>80]:' + BLUE + ' #%d(%d)' + WHITE + ':%s') % (i, len(line), line) + ENDC\n\n        # check the last space in the end of line\n        if re.match(r'.*\\s$', line):\n            if not file_name_printed:\n                print RED + file + ':' + ENDC\n                file_name_printed = True\n            print (GREEN + '    [SPACE]:' + BLUE + ' #%d(%d)' + WHITE + ':%s') % (i, len(line), line) + ENDC\n\n        # check the TAB key\n        if string.find(line, '\\t') >= 0:\n            if not file_name_printed:\n                print RED + file + ':' + ENDC\n                file_name_printed = True\n            print (YELLOW + '    [TAB]:' + BLUE + ' #%d(%d)' + WHITE + ':%s') % (i, len(line), line) + ENDC\n\n        # check blank lines\n        if line.isspace():\n            if not file_name_printed:\n                print RED + file + ':' + ENDC\n                file_name_printed = True\n            print (CYAN + '    [BLK]:' + BLUE + ' #%d(%d)' + WHITE + ':%s') % (i, len(line), line) + ENDC\n\n        i = i + 1\n\n    f.close()\n\ndef walk_dir(dir):\n    for root, dirs, files in os.walk(dir):\n        for f in files:\n            s = root + '/' + f\n            check_file(s)\n\n    for d in dirs:\n        walk_dir(d)\n\ndef usage():\n    print \"\"\"\nUsage: stylechecker.py file or dir\n\n    python stylechecker.py /path/to/the/c/code\n        or\n    python stylechecker.py /file/of/c/code \"\"\"\n\n    sys.exit(1)\n\n### main\nif len(sys.argv) == 2:\n    PATH = sys.argv[1]\n\n    if os.path.isfile(PATH):\n        check_file(PATH)\n    elif os.path.isdir(PATH):\n        walk_dir(PATH)\n    else:\n        print RED + \"Check the %s is file or dir\" % PATH + ENDC\nelse:\n    usage()\n"
  },
  {
    "path": "contrib/unicode2nginx/koi-utf",
    "content": "charset_map  koi8-r  utf-8 {\n\n    80  E29480 ; #\tBOX DRAWINGS LIGHT HORIZONTAL\n    81  E29482 ; #\tBOX DRAWINGS LIGHT VERTICAL\n    82  E2948C ; #\tBOX DRAWINGS LIGHT DOWN AND RIGHT\n    83  E29490 ; #\tBOX DRAWINGS LIGHT DOWN AND LEFT\n    84  E29494 ; #\tBOX DRAWINGS LIGHT UP AND RIGHT\n    85  E29498 ; #\tBOX DRAWINGS LIGHT UP AND LEFT\n    86  E2949C ; #\tBOX DRAWINGS LIGHT VERTICAL AND RIGHT\n    87  E294A4 ; #\tBOX DRAWINGS LIGHT VERTICAL AND LEFT\n    88  E294AC ; #\tBOX DRAWINGS LIGHT DOWN AND HORIZONTAL\n    89  E294B4 ; #\tBOX DRAWINGS LIGHT UP AND HORIZONTAL\n    8A  E294BC ; #\tBOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL\n    8B  E29680 ; #\tUPPER HALF BLOCK\n    8C  E29684 ; #\tLOWER HALF BLOCK\n    8D  E29688 ; #\tFULL BLOCK\n    8E  E2968C ; #\tLEFT HALF BLOCK\n    8F  E29690 ; #\tRIGHT HALF BLOCK\n    90  E29691 ; #\tLIGHT SHADE\n    91  E29692 ; #\tMEDIUM SHADE\n    92  E29693 ; #\tDARK SHADE\n    93  E28CA0 ; #\tTOP HALF INTEGRAL\n    94  E296A0 ; #\tBLACK SQUARE\n    95  E28899 ; #\tBULLET OPERATOR\n    96  E2889A ; #\tSQUARE ROOT\n    97  E28988 ; #\tALMOST EQUAL TO\n    98  E289A4 ; #\tLESS-THAN OR EQUAL TO\n    99  E289A5 ; #\tGREATER-THAN OR EQUAL TO\n    9A  C2A0 ; #\tNO-BREAK SPACE\n    9B  E28CA1 ; #\tBOTTOM HALF INTEGRAL\n    9C  C2B0 ; #\tDEGREE SIGN\n    9D  C2B2 ; #\tSUPERSCRIPT TWO\n    9E  C2B7 ; #\tMIDDLE DOT\n    9F  C3B7 ; #\tDIVISION SIGN\n    A0  E29590 ; #\tBOX DRAWINGS DOUBLE HORIZONTAL\n    A1  E29591 ; #\tBOX DRAWINGS DOUBLE VERTICAL\n    A2  E29592 ; #\tBOX DRAWINGS DOWN SINGLE AND RIGHT DOUBLE\n    A3  D191 ; #\tCYRILLIC SMALL LETTER IO\n    A4  E29593 ; #\tBOX DRAWINGS DOWN DOUBLE AND RIGHT SINGLE\n    A5  E29594 ; #\tBOX DRAWINGS DOUBLE DOWN AND RIGHT\n    A6  E29595 ; #\tBOX DRAWINGS DOWN SINGLE AND LEFT DOUBLE\n    A7  E29596 ; #\tBOX DRAWINGS DOWN DOUBLE AND LEFT SINGLE\n    A8  E29597 ; #\tBOX DRAWINGS DOUBLE DOWN AND LEFT\n    A9  E29598 ; #\tBOX DRAWINGS UP SINGLE AND RIGHT DOUBLE\n    AA  E29599 ; #\tBOX DRAWINGS UP DOUBLE AND RIGHT SINGLE\n    AB  E2959A ; #\tBOX DRAWINGS DOUBLE UP AND RIGHT\n    AC  E2959B ; #\tBOX DRAWINGS UP SINGLE AND LEFT DOUBLE\n    AD  E2959C ; #\tBOX DRAWINGS UP DOUBLE AND LEFT SINGLE\n    AE  E2959D ; #\tBOX DRAWINGS DOUBLE UP AND LEFT\n    AF  E2959E ; #\tBOX DRAWINGS VERTICAL SINGLE AND RIGHT DOUBLE\n    B0  E2959F ; #\tBOX DRAWINGS VERTICAL DOUBLE AND RIGHT SINGLE\n    B1  E295A0 ; #\tBOX DRAWINGS DOUBLE VERTICAL AND RIGHT\n    B2  E295A1 ; #\tBOX DRAWINGS VERTICAL SINGLE AND LEFT DOUBLE\n    B3  D081 ; #\tCYRILLIC CAPITAL LETTER IO\n    B4  E295A2 ; #\tBOX DRAWINGS VERTICAL DOUBLE AND LEFT SINGLE\n    B5  E295A3 ; #\tBOX DRAWINGS DOUBLE VERTICAL AND LEFT\n    B6  E295A4 ; #\tBOX DRAWINGS DOWN SINGLE AND HORIZONTAL DOUBLE\n    B7  E295A5 ; #\tBOX DRAWINGS DOWN DOUBLE AND HORIZONTAL SINGLE\n    B8  E295A6 ; #\tBOX DRAWINGS DOUBLE DOWN AND HORIZONTAL\n    B9  E295A7 ; #\tBOX DRAWINGS UP SINGLE AND HORIZONTAL DOUBLE\n    BA  E295A8 ; #\tBOX DRAWINGS UP DOUBLE AND HORIZONTAL SINGLE\n    BB  E295A9 ; #\tBOX DRAWINGS DOUBLE UP AND HORIZONTAL\n    BC  E295AA ; #\tBOX DRAWINGS VERTICAL SINGLE AND HORIZONTAL DOUBLE\n    BD  E295AB ; #\tBOX DRAWINGS VERTICAL DOUBLE AND HORIZONTAL SINGLE\n    BE  E295AC ; #\tBOX DRAWINGS DOUBLE VERTICAL AND HORIZONTAL\n    BF  C2A9 ; #\tCOPYRIGHT SIGN\n    C0  D18E ; #\tCYRILLIC SMALL LETTER YU\n    C1  D0B0 ; #\tCYRILLIC SMALL LETTER A\n    C2  D0B1 ; #\tCYRILLIC SMALL LETTER BE\n    C3  D186 ; #\tCYRILLIC SMALL LETTER TSE\n    C4  D0B4 ; #\tCYRILLIC SMALL LETTER DE\n    C5  D0B5 ; #\tCYRILLIC SMALL LETTER IE\n    C6  D184 ; #\tCYRILLIC SMALL LETTER EF\n    C7  D0B3 ; #\tCYRILLIC SMALL LETTER GHE\n    C8  D185 ; #\tCYRILLIC SMALL LETTER HA\n    C9  D0B8 ; #\tCYRILLIC SMALL LETTER I\n    CA  D0B9 ; #\tCYRILLIC SMALL LETTER SHORT I\n    CB  D0BA ; #\tCYRILLIC SMALL LETTER KA\n    CC  D0BB ; #\tCYRILLIC SMALL LETTER EL\n    CD  D0BC ; #\tCYRILLIC SMALL LETTER EM\n    CE  D0BD ; #\tCYRILLIC SMALL LETTER EN\n    CF  D0BE ; #\tCYRILLIC SMALL LETTER O\n    D0  D0BF ; #\tCYRILLIC SMALL LETTER PE\n    D1  D18F ; #\tCYRILLIC SMALL LETTER YA\n    D2  D180 ; #\tCYRILLIC SMALL LETTER ER\n    D3  D181 ; #\tCYRILLIC SMALL LETTER ES\n    D4  D182 ; #\tCYRILLIC SMALL LETTER TE\n    D5  D183 ; #\tCYRILLIC SMALL LETTER U\n    D6  D0B6 ; #\tCYRILLIC SMALL LETTER ZHE\n    D7  D0B2 ; #\tCYRILLIC SMALL LETTER VE\n    D8  D18C ; #\tCYRILLIC SMALL LETTER SOFT SIGN\n    D9  D18B ; #\tCYRILLIC SMALL LETTER YERU\n    DA  D0B7 ; #\tCYRILLIC SMALL LETTER ZE\n    DB  D188 ; #\tCYRILLIC SMALL LETTER SHA\n    DC  D18D ; #\tCYRILLIC SMALL LETTER E\n    DD  D189 ; #\tCYRILLIC SMALL LETTER SHCHA\n    DE  D187 ; #\tCYRILLIC SMALL LETTER CHE\n    DF  D18A ; #\tCYRILLIC SMALL LETTER HARD SIGN\n    E0  D0AE ; #\tCYRILLIC CAPITAL LETTER YU\n    E1  D090 ; #\tCYRILLIC CAPITAL LETTER A\n    E2  D091 ; #\tCYRILLIC CAPITAL LETTER BE\n    E3  D0A6 ; #\tCYRILLIC CAPITAL LETTER TSE\n    E4  D094 ; #\tCYRILLIC CAPITAL LETTER DE\n    E5  D095 ; #\tCYRILLIC CAPITAL LETTER IE\n    E6  D0A4 ; #\tCYRILLIC CAPITAL LETTER EF\n    E7  D093 ; #\tCYRILLIC CAPITAL LETTER GHE\n    E8  D0A5 ; #\tCYRILLIC CAPITAL LETTER HA\n    E9  D098 ; #\tCYRILLIC CAPITAL LETTER I\n    EA  D099 ; #\tCYRILLIC CAPITAL LETTER SHORT I\n    EB  D09A ; #\tCYRILLIC CAPITAL LETTER KA\n    EC  D09B ; #\tCYRILLIC CAPITAL LETTER EL\n    ED  D09C ; #\tCYRILLIC CAPITAL LETTER EM\n    EE  D09D ; #\tCYRILLIC CAPITAL LETTER EN\n    EF  D09E ; #\tCYRILLIC CAPITAL LETTER O\n    F0  D09F ; #\tCYRILLIC CAPITAL LETTER PE\n    F1  D0AF ; #\tCYRILLIC CAPITAL LETTER YA\n    F2  D0A0 ; #\tCYRILLIC CAPITAL LETTER ER\n    F3  D0A1 ; #\tCYRILLIC CAPITAL LETTER ES\n    F4  D0A2 ; #\tCYRILLIC CAPITAL LETTER TE\n    F5  D0A3 ; #\tCYRILLIC CAPITAL LETTER U\n    F6  D096 ; #\tCYRILLIC CAPITAL LETTER ZHE\n    F7  D092 ; #\tCYRILLIC CAPITAL LETTER VE\n    F8  D0AC ; #\tCYRILLIC CAPITAL LETTER SOFT SIGN\n    F9  D0AB ; #\tCYRILLIC CAPITAL LETTER YERU\n    FA  D097 ; #\tCYRILLIC CAPITAL LETTER ZE\n    FB  D0A8 ; #\tCYRILLIC CAPITAL LETTER SHA\n    FC  D0AD ; #\tCYRILLIC CAPITAL LETTER E\n    FD  D0A9 ; #\tCYRILLIC CAPITAL LETTER SHCHA\n    FE  D0A7 ; #\tCYRILLIC CAPITAL LETTER CHE\n    FF  D0AA ; #\tCYRILLIC CAPITAL LETTER HARD SIGN\n}\n"
  },
  {
    "path": "contrib/unicode2nginx/unicode-to-nginx.pl",
    "content": "#!/usr/bin/perl -w\n\n# Convert unicode mappings to nginx configuration file format.\n\n# You may find useful mappings in various places, including\n# unicode.org official site:\n#\n# http://www.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP1251.TXT\n# http://www.unicode.org/Public/MAPPINGS/VENDORS/MISC/KOI8-R.TXT\n\n# Needs perl 5.6 or later.\n\n# Written by Maxim Dounin, mdounin@mdounin.ru\n\n###############################################################################\n\nrequire 5.006;\n\nwhile (<>) {\n\t# Skip comments and empty lines\n\n\tnext if /^#/;\n\tnext if /^\\s*$/;\n\tchomp;\n\n\t# Convert mappings\n\n\tif (/^\\s*0x(..)\\s*0x(....)\\s*(#.*)/) {\n\t\t# Mapping <from-code> <unicode-code> \"#\" <unicode-name>\n\t\tmy $cs_code = $1;\n\t\tmy $un_code = $2;\n\t\tmy $un_name = $3;\n\n\t\t# Produce UTF-8 sequence from character code;\n\n\t\tmy $un_utf8 = join('',\n\t\t\tmap { sprintf(\"%02X\", $_) }\n\t\t\tunpack(\"U0C*\", pack(\"U\", hex($un_code)))\n\t\t);\n\n\t\tprint \"    $cs_code  $un_utf8 ; $un_name\\n\";\n\n\t} else {\n\t\twarn \"Unrecognized line: '$_'\";\n\t}\n}\n\n###############################################################################\n"
  },
  {
    "path": "contrib/unicode2nginx/win-utf",
    "content": "charset_map  windows-1251  utf-8 {\n\n    80  D082 ; #CYRILLIC CAPITAL LETTER DJE\n    81  D083 ; #CYRILLIC CAPITAL LETTER GJE\n    82  E2809A ; #SINGLE LOW-9 QUOTATION MARK\n    83  D193 ; #CYRILLIC SMALL LETTER GJE\n    84  E2809E ; #DOUBLE LOW-9 QUOTATION MARK\n    85  E280A6 ; #HORIZONTAL ELLIPSIS\n    86  E280A0 ; #DAGGER\n    87  E280A1 ; #DOUBLE DAGGER\n    88  E282AC ; #EURO SIGN\n    89  E280B0 ; #PER MILLE SIGN\n    8A  D089 ; #CYRILLIC CAPITAL LETTER LJE\n    8B  E280B9 ; #SINGLE LEFT-POINTING ANGLE QUOTATION MARK\n    8C  D08A ; #CYRILLIC CAPITAL LETTER NJE\n    8D  D08C ; #CYRILLIC CAPITAL LETTER KJE\n    8E  D08B ; #CYRILLIC CAPITAL LETTER TSHE\n    8F  D08F ; #CYRILLIC CAPITAL LETTER DZHE\n    90  D192 ; #CYRILLIC SMALL LETTER DJE\n    91  E28098 ; #LEFT SINGLE QUOTATION MARK\n    92  E28099 ; #RIGHT SINGLE QUOTATION MARK\n    93  E2809C ; #LEFT DOUBLE QUOTATION MARK\n    94  E2809D ; #RIGHT DOUBLE QUOTATION MARK\n    95  E280A2 ; #BULLET\n    96  E28093 ; #EN DASH\n    97  E28094 ; #EM DASH\n    99  E284A2 ; #TRADE MARK SIGN\n    9A  D199 ; #CYRILLIC SMALL LETTER LJE\n    9B  E280BA ; #SINGLE RIGHT-POINTING ANGLE QUOTATION MARK\n    9C  D19A ; #CYRILLIC SMALL LETTER NJE\n    9D  D19C ; #CYRILLIC SMALL LETTER KJE\n    9E  D19B ; #CYRILLIC SMALL LETTER TSHE\n    9F  D19F ; #CYRILLIC SMALL LETTER DZHE\n    A0  C2A0 ; #NO-BREAK SPACE\n    A1  D08E ; #CYRILLIC CAPITAL LETTER SHORT U\n    A2  D19E ; #CYRILLIC SMALL LETTER SHORT U\n    A3  D088 ; #CYRILLIC CAPITAL LETTER JE\n    A4  C2A4 ; #CURRENCY SIGN\n    A5  D290 ; #CYRILLIC CAPITAL LETTER GHE WITH UPTURN\n    A6  C2A6 ; #BROKEN BAR\n    A7  C2A7 ; #SECTION SIGN\n    A8  D081 ; #CYRILLIC CAPITAL LETTER IO\n    A9  C2A9 ; #COPYRIGHT SIGN\n    AA  D084 ; #CYRILLIC CAPITAL LETTER UKRAINIAN IE\n    AB  C2AB ; #LEFT-POINTING DOUBLE ANGLE QUOTATION MARK\n    AC  C2AC ; #NOT SIGN\n    AD  C2AD ; #SOFT HYPHEN\n    AE  C2AE ; #REGISTERED SIGN\n    AF  D087 ; #CYRILLIC CAPITAL LETTER YI\n    B0  C2B0 ; #DEGREE SIGN\n    B1  C2B1 ; #PLUS-MINUS SIGN\n    B2  D086 ; #CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I\n    B3  D196 ; #CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I\n    B4  D291 ; #CYRILLIC SMALL LETTER GHE WITH UPTURN\n    B5  C2B5 ; #MICRO SIGN\n    B6  C2B6 ; #PILCROW SIGN\n    B7  C2B7 ; #MIDDLE DOT\n    B8  D191 ; #CYRILLIC SMALL LETTER IO\n    B9  E28496 ; #NUMERO SIGN\n    BA  D194 ; #CYRILLIC SMALL LETTER UKRAINIAN IE\n    BB  C2BB ; #RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK\n    BC  D198 ; #CYRILLIC SMALL LETTER JE\n    BD  D085 ; #CYRILLIC CAPITAL LETTER DZE\n    BE  D195 ; #CYRILLIC SMALL LETTER DZE\n    BF  D197 ; #CYRILLIC SMALL LETTER YI\n    C0  D090 ; #CYRILLIC CAPITAL LETTER A\n    C1  D091 ; #CYRILLIC CAPITAL LETTER BE\n    C2  D092 ; #CYRILLIC CAPITAL LETTER VE\n    C3  D093 ; #CYRILLIC CAPITAL LETTER GHE\n    C4  D094 ; #CYRILLIC CAPITAL LETTER DE\n    C5  D095 ; #CYRILLIC CAPITAL LETTER IE\n    C6  D096 ; #CYRILLIC CAPITAL LETTER ZHE\n    C7  D097 ; #CYRILLIC CAPITAL LETTER ZE\n    C8  D098 ; #CYRILLIC CAPITAL LETTER I\n    C9  D099 ; #CYRILLIC CAPITAL LETTER SHORT I\n    CA  D09A ; #CYRILLIC CAPITAL LETTER KA\n    CB  D09B ; #CYRILLIC CAPITAL LETTER EL\n    CC  D09C ; #CYRILLIC CAPITAL LETTER EM\n    CD  D09D ; #CYRILLIC CAPITAL LETTER EN\n    CE  D09E ; #CYRILLIC CAPITAL LETTER O\n    CF  D09F ; #CYRILLIC CAPITAL LETTER PE\n    D0  D0A0 ; #CYRILLIC CAPITAL LETTER ER\n    D1  D0A1 ; #CYRILLIC CAPITAL LETTER ES\n    D2  D0A2 ; #CYRILLIC CAPITAL LETTER TE\n    D3  D0A3 ; #CYRILLIC CAPITAL LETTER U\n    D4  D0A4 ; #CYRILLIC CAPITAL LETTER EF\n    D5  D0A5 ; #CYRILLIC CAPITAL LETTER HA\n    D6  D0A6 ; #CYRILLIC CAPITAL LETTER TSE\n    D7  D0A7 ; #CYRILLIC CAPITAL LETTER CHE\n    D8  D0A8 ; #CYRILLIC CAPITAL LETTER SHA\n    D9  D0A9 ; #CYRILLIC CAPITAL LETTER SHCHA\n    DA  D0AA ; #CYRILLIC CAPITAL LETTER HARD SIGN\n    DB  D0AB ; #CYRILLIC CAPITAL LETTER YERU\n    DC  D0AC ; #CYRILLIC CAPITAL LETTER SOFT SIGN\n    DD  D0AD ; #CYRILLIC CAPITAL LETTER E\n    DE  D0AE ; #CYRILLIC CAPITAL LETTER YU\n    DF  D0AF ; #CYRILLIC CAPITAL LETTER YA\n    E0  D0B0 ; #CYRILLIC SMALL LETTER A\n    E1  D0B1 ; #CYRILLIC SMALL LETTER BE\n    E2  D0B2 ; #CYRILLIC SMALL LETTER VE\n    E3  D0B3 ; #CYRILLIC SMALL LETTER GHE\n    E4  D0B4 ; #CYRILLIC SMALL LETTER DE\n    E5  D0B5 ; #CYRILLIC SMALL LETTER IE\n    E6  D0B6 ; #CYRILLIC SMALL LETTER ZHE\n    E7  D0B7 ; #CYRILLIC SMALL LETTER ZE\n    E8  D0B8 ; #CYRILLIC SMALL LETTER I\n    E9  D0B9 ; #CYRILLIC SMALL LETTER SHORT I\n    EA  D0BA ; #CYRILLIC SMALL LETTER KA\n    EB  D0BB ; #CYRILLIC SMALL LETTER EL\n    EC  D0BC ; #CYRILLIC SMALL LETTER EM\n    ED  D0BD ; #CYRILLIC SMALL LETTER EN\n    EE  D0BE ; #CYRILLIC SMALL LETTER O\n    EF  D0BF ; #CYRILLIC SMALL LETTER PE\n    F0  D180 ; #CYRILLIC SMALL LETTER ER\n    F1  D181 ; #CYRILLIC SMALL LETTER ES\n    F2  D182 ; #CYRILLIC SMALL LETTER TE\n    F3  D183 ; #CYRILLIC SMALL LETTER U\n    F4  D184 ; #CYRILLIC SMALL LETTER EF\n    F5  D185 ; #CYRILLIC SMALL LETTER HA\n    F6  D186 ; #CYRILLIC SMALL LETTER TSE\n    F7  D187 ; #CYRILLIC SMALL LETTER CHE\n    F8  D188 ; #CYRILLIC SMALL LETTER SHA\n    F9  D189 ; #CYRILLIC SMALL LETTER SHCHA\n    FA  D18A ; #CYRILLIC SMALL LETTER HARD SIGN\n    FB  D18B ; #CYRILLIC SMALL LETTER YERU\n    FC  D18C ; #CYRILLIC SMALL LETTER SOFT SIGN\n    FD  D18D ; #CYRILLIC SMALL LETTER E\n    FE  D18E ; #CYRILLIC SMALL LETTER YU\n    FF  D18F ; #CYRILLIC SMALL LETTER YA\n}\n"
  },
  {
    "path": "contrib/vim/ftdetect/nginx.vim",
    "content": "au BufRead,BufNewFile *.nginx set ft=nginx\nau BufRead,BufNewFile */etc/nginx/* set ft=nginx\nau BufRead,BufNewFile */usr/local/nginx/conf/* set ft=nginx\nau BufRead,BufNewFile nginx.conf set ft=nginx\n"
  },
  {
    "path": "contrib/vim/ftplugin/nginx.vim",
    "content": "setlocal commentstring=#\\ %s\n"
  },
  {
    "path": "contrib/vim/indent/nginx.vim",
    "content": "if exists(\"b:did_indent\")\n    finish\nendif\nlet b:did_indent = 1\n\nsetlocal indentexpr=\n\n\" cindent actually works for nginx' simple file structure\nsetlocal cindent\n\" Just make sure that the comments are not reset as defs would be.\nsetlocal cinkeys-=0#\n"
  },
  {
    "path": "contrib/vim/syntax/nginx.vim",
    "content": "\" Vim syntax file\n\" Language: nginx.conf\n\nif exists(\"b:current_syntax\")\n  finish\nend\n\n\" general syntax\n\nif has(\"patch-7.4.1142\")\n    \" except control characters, \";\", \"{\", and \"}\"\n    syn iskeyword 33-58,60-122,124,126-255\nendif\n\nsyn match ngxName '\\([^;{} \\t\\\\]\\|\\\\.\\)\\+'\n    \\ contains=@ngxDirectives\n    \\ nextgroup=@ngxParams skipwhite skipempty\nsyn match ngxParam '\\(\\${\\|[^;{ \\t\\\\]\\|\\\\.\\)\\+'\n    \\ contained\n    \\ contains=ngxVariable\n    \\ nextgroup=@ngxParams skipwhite skipempty\nsyn region ngxString start=+\\z([\"']\\)+ end=+\\z1+ skip=+\\\\\\\\\\|\\\\\\z1+\n    \\ contains=ngxVariableString\n    \\ nextgroup=@ngxParams skipwhite skipempty\nsyn match ngxParamComment '#.*$'\n    \\ nextgroup=@ngxParams skipwhite skipempty\nsyn match ngxSemicolon ';' contained\nsyn region ngxBlock start=+{+ end=+}+ contained\n    \\ contains=@ngxTopLevel\nsyn match ngxComment '#.*$'\n\nsyn match ngxVariable '\\$\\(\\w\\+\\|{\\w\\+}\\)' contained\nsyn match ngxVariableString '\\$\\(\\w\\+\\|{\\w\\+}\\)' contained\n\nsyn cluster ngxTopLevel\n    \\ contains=ngxName,ngxString,ngxComment\nsyn cluster ngxDirectives\n    \\ contains=ngxDirective,ngxDirectiveBlock,ngxDirectiveImportant\n    \\ add=ngxDirectiveControl,ngxDirectiveError,ngxDirectiveDeprecated\n    \\ add=ngxDirectiveThirdParty,ngxDirectiveThirdPartyDeprecated\nsyn cluster ngxParams\n    \\ contains=ngxParam,ngxString,ngxParamComment,ngxSemicolon,ngxBlock\n\n\" boolean parameters\n\nsyn keyword ngxBoolean contained on off\n    \\ nextgroup=@ngxParams skipwhite skipempty\nsyn cluster ngxParams add=ngxBoolean\n\n\" listen directive\n\nsyn cluster ngxTopLevel add=ngxDirectiveListen\nsyn keyword ngxDirectiveListen listen\n    \\ nextgroup=@ngxListenParams skipwhite skipempty\nsyn match ngxListenParam '\\(\\${\\|[^;{ \\t\\\\]\\|\\\\.\\)\\+'\n    \\ contained\n    \\ nextgroup=@ngxListenParams skipwhite skipempty\nsyn region ngxListenString start=+\\z([\"']\\)+ end=+\\z1+ skip=+\\\\\\\\\\|\\\\\\z1+\n    \\ contained\n    \\ nextgroup=@ngxListenParams skipwhite skipempty\nsyn match ngxListenComment '#.*$'\n    \\ contained\n    \\ nextgroup=@ngxListenParams skipwhite skipempty\nsyn keyword ngxListenOptions contained\n    \\ default_server ssl http2 proxy_protocol\n    \\ setfib fastopen backlog rcvbuf sndbuf accept_filter deferred bind\n    \\ ipv6only reuseport so_keepalive\n    \\ nextgroup=@ngxListenParams skipwhite skipempty\nsyn keyword ngxListenOptionsDeprecated contained\n    \\ spdy\n    \\ nextgroup=@ngxListenParams skipwhite skipempty\nsyn cluster ngxListenParams\n    \\ contains=ngxListenParam,ngxListenString,ngxListenComment\n    \\ add=ngxListenOptions,ngxListenOptionsDeprecated\n\nsyn keyword ngxDirectiveBlock contained http\nsyn keyword ngxDirectiveBlock contained stream\nsyn keyword ngxDirectiveBlock contained mail\nsyn keyword ngxDirectiveBlock contained events\nsyn keyword ngxDirectiveBlock contained server\nsyn keyword ngxDirectiveBlock contained types\nsyn keyword ngxDirectiveBlock contained location\nsyn keyword ngxDirectiveBlock contained upstream\nsyn keyword ngxDirectiveBlock contained charset_map\nsyn keyword ngxDirectiveBlock contained limit_except\nsyn keyword ngxDirectiveBlock contained if\nsyn keyword ngxDirectiveBlock contained geo\nsyn keyword ngxDirectiveBlock contained map\nsyn keyword ngxDirectiveBlock contained split_clients\nsyn keyword ngxDirectiveBlock contained match\n\nsyn keyword ngxDirectiveImportant contained include\nsyn keyword ngxDirectiveImportant contained root\nsyn keyword ngxDirectiveImportant contained server_name\nsyn keyword ngxDirectiveImportant contained internal\nsyn keyword ngxDirectiveImportant contained proxy_pass\nsyn keyword ngxDirectiveImportant contained memcached_pass\nsyn keyword ngxDirectiveImportant contained fastcgi_pass\nsyn keyword ngxDirectiveImportant contained scgi_pass\nsyn keyword ngxDirectiveImportant contained uwsgi_pass\nsyn keyword ngxDirectiveImportant contained try_files\n\nsyn keyword ngxDirectiveControl contained break\nsyn keyword ngxDirectiveControl contained return\nsyn keyword ngxDirectiveControl contained rewrite\nsyn keyword ngxDirectiveControl contained set\n\nsyn keyword ngxDirectiveError contained error_page\nsyn keyword ngxDirectiveError contained post_action\n\nsyn keyword ngxDirectiveDeprecated contained limit_zone\nsyn keyword ngxDirectiveDeprecated contained proxy_downstream_buffer\nsyn keyword ngxDirectiveDeprecated contained proxy_upstream_buffer\nsyn keyword ngxDirectiveDeprecated contained spdy_chunk_size\nsyn keyword ngxDirectiveDeprecated contained spdy_headers_comp\nsyn keyword ngxDirectiveDeprecated contained spdy_keepalive_timeout\nsyn keyword ngxDirectiveDeprecated contained spdy_max_concurrent_streams\nsyn keyword ngxDirectiveDeprecated contained spdy_pool_size\nsyn keyword ngxDirectiveDeprecated contained spdy_recv_buffer_size\nsyn keyword ngxDirectiveDeprecated contained spdy_recv_timeout\nsyn keyword ngxDirectiveDeprecated contained spdy_streams_index_size\nsyn keyword ngxDirectiveDeprecated contained ssl\nsyn keyword ngxDirectiveDeprecated contained upstream_conf\n\nsyn keyword ngxDirective contained absolute_redirect\nsyn keyword ngxDirective contained accept_mutex\nsyn keyword ngxDirective contained accept_mutex_delay\nsyn keyword ngxDirective contained acceptex_read\nsyn keyword ngxDirective contained access_log\nsyn keyword ngxDirective contained add_after_body\nsyn keyword ngxDirective contained add_before_body\nsyn keyword ngxDirective contained add_header\nsyn keyword ngxDirective contained add_trailer\nsyn keyword ngxDirective contained addition_types\nsyn keyword ngxDirective contained aio\nsyn keyword ngxDirective contained aio_write\nsyn keyword ngxDirective contained alias\nsyn keyword ngxDirective contained allow\nsyn keyword ngxDirective contained ancient_browser\nsyn keyword ngxDirective contained ancient_browser_value\nsyn keyword ngxDirective contained api\nsyn keyword ngxDirective contained auth_basic\nsyn keyword ngxDirective contained auth_basic_user_file\nsyn keyword ngxDirective contained auth_http\nsyn keyword ngxDirective contained auth_http_header\nsyn keyword ngxDirective contained auth_http_pass_client_cert\nsyn keyword ngxDirective contained auth_http_timeout\nsyn keyword ngxDirective contained auth_jwt\nsyn keyword ngxDirective contained auth_jwt_claim_set\nsyn keyword ngxDirective contained auth_jwt_header_set\nsyn keyword ngxDirective contained auth_jwt_key_file\nsyn keyword ngxDirective contained auth_jwt_key_request\nsyn keyword ngxDirective contained auth_jwt_leeway\nsyn keyword ngxDirective contained auth_request\nsyn keyword ngxDirective contained auth_request_set\nsyn keyword ngxDirective contained autoindex\nsyn keyword ngxDirective contained autoindex_exact_size\nsyn keyword ngxDirective contained autoindex_format\nsyn keyword ngxDirective contained autoindex_localtime\nsyn keyword ngxDirective contained charset\nsyn keyword ngxDirective contained charset_types\nsyn keyword ngxDirective contained chunked_transfer_encoding\nsyn keyword ngxDirective contained client_body_buffer_size\nsyn keyword ngxDirective contained client_body_in_file_only\nsyn keyword ngxDirective contained client_body_in_single_buffer\nsyn keyword ngxDirective contained client_body_temp_path\nsyn keyword ngxDirective contained client_body_timeout\nsyn keyword ngxDirective contained client_header_buffer_size\nsyn keyword ngxDirective contained client_header_timeout\nsyn keyword ngxDirective contained client_max_body_size\nsyn keyword ngxDirective contained connection_pool_size\nsyn keyword ngxDirective contained create_full_put_path\nsyn keyword ngxDirective contained daemon\nsyn keyword ngxDirective contained dav_access\nsyn keyword ngxDirective contained dav_methods\nsyn keyword ngxDirective contained debug_connection\nsyn keyword ngxDirective contained debug_points\nsyn keyword ngxDirective contained default_type\nsyn keyword ngxDirective contained degradation\nsyn keyword ngxDirective contained degrade\nsyn keyword ngxDirective contained deny\nsyn keyword ngxDirective contained devpoll_changes\nsyn keyword ngxDirective contained devpoll_events\nsyn keyword ngxDirective contained directio\nsyn keyword ngxDirective contained directio_alignment\nsyn keyword ngxDirective contained disable_symlinks\nsyn keyword ngxDirective contained empty_gif\nsyn keyword ngxDirective contained env\nsyn keyword ngxDirective contained epoll_events\nsyn keyword ngxDirective contained error_log\nsyn keyword ngxDirective contained etag\nsyn keyword ngxDirective contained eventport_events\nsyn keyword ngxDirective contained expires\nsyn keyword ngxDirective contained f4f\nsyn keyword ngxDirective contained f4f_buffer_size\nsyn keyword ngxDirective contained fastcgi_bind\nsyn keyword ngxDirective contained fastcgi_buffer_size\nsyn keyword ngxDirective contained fastcgi_buffering\nsyn keyword ngxDirective contained fastcgi_buffers\nsyn keyword ngxDirective contained fastcgi_busy_buffers_size\nsyn keyword ngxDirective contained fastcgi_cache\nsyn keyword ngxDirective contained fastcgi_cache_background_update\nsyn keyword ngxDirective contained fastcgi_cache_bypass\nsyn keyword ngxDirective contained fastcgi_cache_key\nsyn keyword ngxDirective contained fastcgi_cache_lock\nsyn keyword ngxDirective contained fastcgi_cache_lock_age\nsyn keyword ngxDirective contained fastcgi_cache_lock_timeout\nsyn keyword ngxDirective contained fastcgi_cache_max_range_offset\nsyn keyword ngxDirective contained fastcgi_cache_methods\nsyn keyword ngxDirective contained fastcgi_cache_min_uses\nsyn keyword ngxDirective contained fastcgi_cache_path\nsyn keyword ngxDirective contained fastcgi_cache_purge\nsyn keyword ngxDirective contained fastcgi_cache_revalidate\nsyn keyword ngxDirective contained fastcgi_cache_use_stale\nsyn keyword ngxDirective contained fastcgi_cache_valid\nsyn keyword ngxDirective contained fastcgi_catch_stderr\nsyn keyword ngxDirective contained fastcgi_connect_timeout\nsyn keyword ngxDirective contained fastcgi_force_ranges\nsyn keyword ngxDirective contained fastcgi_hide_header\nsyn keyword ngxDirective contained fastcgi_ignore_client_abort\nsyn keyword ngxDirective contained fastcgi_ignore_headers\nsyn keyword ngxDirective contained fastcgi_index\nsyn keyword ngxDirective contained fastcgi_intercept_errors\nsyn keyword ngxDirective contained fastcgi_keep_conn\nsyn keyword ngxDirective contained fastcgi_limit_rate\nsyn keyword ngxDirective contained fastcgi_max_temp_file_size\nsyn keyword ngxDirective contained fastcgi_next_upstream\nsyn keyword ngxDirective contained fastcgi_next_upstream_timeout\nsyn keyword ngxDirective contained fastcgi_next_upstream_tries\nsyn keyword ngxDirective contained fastcgi_no_cache\nsyn keyword ngxDirective contained fastcgi_param\nsyn keyword ngxDirective contained fastcgi_pass_header\nsyn keyword ngxDirective contained fastcgi_pass_request_body\nsyn keyword ngxDirective contained fastcgi_pass_request_headers\nsyn keyword ngxDirective contained fastcgi_read_timeout\nsyn keyword ngxDirective contained fastcgi_request_buffering\nsyn keyword ngxDirective contained fastcgi_send_lowat\nsyn keyword ngxDirective contained fastcgi_send_timeout\nsyn keyword ngxDirective contained fastcgi_socket_keepalive\nsyn keyword ngxDirective contained fastcgi_split_path_info\nsyn keyword ngxDirective contained fastcgi_store\nsyn keyword ngxDirective contained fastcgi_store_access\nsyn keyword ngxDirective contained fastcgi_temp_file_write_size\nsyn keyword ngxDirective contained fastcgi_temp_path\nsyn keyword ngxDirective contained flv\nsyn keyword ngxDirective contained geoip_city\nsyn keyword ngxDirective contained geoip_country\nsyn keyword ngxDirective contained geoip_org\nsyn keyword ngxDirective contained geoip_proxy\nsyn keyword ngxDirective contained geoip_proxy_recursive\nsyn keyword ngxDirective contained google_perftools_profiles\nsyn keyword ngxDirective contained grpc_bind\nsyn keyword ngxDirective contained grpc_buffer_size\nsyn keyword ngxDirective contained grpc_connect_timeout\nsyn keyword ngxDirective contained grpc_hide_header\nsyn keyword ngxDirective contained grpc_ignore_headers\nsyn keyword ngxDirective contained grpc_intercept_errors\nsyn keyword ngxDirective contained grpc_next_upstream\nsyn keyword ngxDirective contained grpc_next_upstream_timeout\nsyn keyword ngxDirective contained grpc_next_upstream_tries\nsyn keyword ngxDirective contained grpc_pass\nsyn keyword ngxDirective contained grpc_pass_header\nsyn keyword ngxDirective contained grpc_read_timeout\nsyn keyword ngxDirective contained grpc_send_timeout\nsyn keyword ngxDirective contained grpc_set_header\nsyn keyword ngxDirective contained grpc_socket_keepalive\nsyn keyword ngxDirective contained grpc_ssl_certificate\nsyn keyword ngxDirective contained grpc_ssl_certificate_key\nsyn keyword ngxDirective contained grpc_ssl_ciphers\nsyn keyword ngxDirective contained grpc_ssl_crl\nsyn keyword ngxDirective contained grpc_ssl_name\nsyn keyword ngxDirective contained grpc_ssl_password_file\nsyn keyword ngxDirective contained grpc_ssl_protocols\nsyn keyword ngxDirective contained grpc_ssl_server_name\nsyn keyword ngxDirective contained grpc_ssl_session_reuse\nsyn keyword ngxDirective contained grpc_ssl_trusted_certificate\nsyn keyword ngxDirective contained grpc_ssl_verify\nsyn keyword ngxDirective contained grpc_ssl_verify_depth\nsyn keyword ngxDirective contained gunzip\nsyn keyword ngxDirective contained gunzip_buffers\nsyn keyword ngxDirective contained gzip\nsyn keyword ngxDirective contained gzip_buffers\nsyn keyword ngxDirective contained gzip_comp_level\nsyn keyword ngxDirective contained gzip_disable\nsyn keyword ngxDirective contained gzip_hash\nsyn keyword ngxDirective contained gzip_http_version\nsyn keyword ngxDirective contained gzip_min_length\nsyn keyword ngxDirective contained gzip_no_buffer\nsyn keyword ngxDirective contained gzip_proxied\nsyn keyword ngxDirective contained gzip_static\nsyn keyword ngxDirective contained gzip_types\nsyn keyword ngxDirective contained gzip_vary\nsyn keyword ngxDirective contained gzip_window\nsyn keyword ngxDirective contained hash\nsyn keyword ngxDirective contained health_check\nsyn keyword ngxDirective contained health_check_timeout\nsyn keyword ngxDirective contained hls\nsyn keyword ngxDirective contained hls_buffers\nsyn keyword ngxDirective contained hls_forward_args\nsyn keyword ngxDirective contained hls_fragment\nsyn keyword ngxDirective contained hls_mp4_buffer_size\nsyn keyword ngxDirective contained hls_mp4_max_buffer_size\nsyn keyword ngxDirective contained http2_body_preread_size\nsyn keyword ngxDirective contained http2_chunk_size\nsyn keyword ngxDirective contained http2_idle_timeout\nsyn keyword ngxDirective contained http2_max_concurrent_pushes\nsyn keyword ngxDirective contained http2_max_concurrent_streams\nsyn keyword ngxDirective contained http2_max_field_size\nsyn keyword ngxDirective contained http2_max_header_size\nsyn keyword ngxDirective contained http2_max_requests\nsyn keyword ngxDirective contained http2_pool_size\nsyn keyword ngxDirective contained http2_push\nsyn keyword ngxDirective contained http2_push_preload\nsyn keyword ngxDirective contained http2_recv_buffer_size\nsyn keyword ngxDirective contained http2_recv_timeout\nsyn keyword ngxDirective contained http2_streams_index_size\nsyn keyword ngxDirective contained if_modified_since\nsyn keyword ngxDirective contained ignore_invalid_headers\nsyn keyword ngxDirective contained image_filter\nsyn keyword ngxDirective contained image_filter_buffer\nsyn keyword ngxDirective contained image_filter_interlace\nsyn keyword ngxDirective contained image_filter_jpeg_quality\nsyn keyword ngxDirective contained image_filter_sharpen\nsyn keyword ngxDirective contained image_filter_transparency\nsyn keyword ngxDirective contained image_filter_webp_quality\nsyn keyword ngxDirective contained imap_auth\nsyn keyword ngxDirective contained imap_capabilities\nsyn keyword ngxDirective contained imap_client_buffer\nsyn keyword ngxDirective contained index\nsyn keyword ngxDirective contained iocp_threads\nsyn keyword ngxDirective contained ip_hash\nsyn keyword ngxDirective contained js_access\nsyn keyword ngxDirective contained js_content\nsyn keyword ngxDirective contained js_filter\nsyn keyword ngxDirective contained js_include\nsyn keyword ngxDirective contained js_path\nsyn keyword ngxDirective contained js_preread\nsyn keyword ngxDirective contained js_set\nsyn keyword ngxDirective contained keepalive\nsyn keyword ngxDirective contained keepalive_disable\nsyn keyword ngxDirective contained keepalive_requests\nsyn keyword ngxDirective contained keepalive_timeout\nsyn keyword ngxDirective contained keyval\nsyn keyword ngxDirective contained keyval_zone\nsyn keyword ngxDirective contained kqueue_changes\nsyn keyword ngxDirective contained kqueue_events\nsyn keyword ngxDirective contained large_client_header_buffers\nsyn keyword ngxDirective contained least_conn\nsyn keyword ngxDirective contained least_time\nsyn keyword ngxDirective contained limit_conn\nsyn keyword ngxDirective contained limit_conn_log_level\nsyn keyword ngxDirective contained limit_conn_status\nsyn keyword ngxDirective contained limit_conn_zone\nsyn keyword ngxDirective contained limit_rate\nsyn keyword ngxDirective contained limit_rate_after\nsyn keyword ngxDirective contained limit_req\nsyn keyword ngxDirective contained limit_req_dry_run\nsyn keyword ngxDirective contained limit_req_log_level\nsyn keyword ngxDirective contained limit_req_status\nsyn keyword ngxDirective contained limit_req_zone\nsyn keyword ngxDirective contained lingering_close\nsyn keyword ngxDirective contained lingering_time\nsyn keyword ngxDirective contained lingering_timeout\nsyn keyword ngxDirective contained load_module\nsyn keyword ngxDirective contained lock_file\nsyn keyword ngxDirective contained log_format\nsyn keyword ngxDirective contained log_not_found\nsyn keyword ngxDirective contained log_subrequest\nsyn keyword ngxDirective contained map_hash_bucket_size\nsyn keyword ngxDirective contained map_hash_max_size\nsyn keyword ngxDirective contained master_process\nsyn keyword ngxDirective contained max_ranges\nsyn keyword ngxDirective contained memcached_bind\nsyn keyword ngxDirective contained memcached_buffer_size\nsyn keyword ngxDirective contained memcached_connect_timeout\nsyn keyword ngxDirective contained memcached_force_ranges\nsyn keyword ngxDirective contained memcached_gzip_flag\nsyn keyword ngxDirective contained memcached_next_upstream\nsyn keyword ngxDirective contained memcached_next_upstream_timeout\nsyn keyword ngxDirective contained memcached_next_upstream_tries\nsyn keyword ngxDirective contained memcached_read_timeout\nsyn keyword ngxDirective contained memcached_send_timeout\nsyn keyword ngxDirective contained memcached_socket_keepalive\nsyn keyword ngxDirective contained merge_slashes\nsyn keyword ngxDirective contained min_delete_depth\nsyn keyword ngxDirective contained mirror\nsyn keyword ngxDirective contained mirror_request_body\nsyn keyword ngxDirective contained modern_browser\nsyn keyword ngxDirective contained modern_browser_value\nsyn keyword ngxDirective contained mp4\nsyn keyword ngxDirective contained mp4_buffer_size\nsyn keyword ngxDirective contained mp4_limit_rate\nsyn keyword ngxDirective contained mp4_limit_rate_after\nsyn keyword ngxDirective contained mp4_max_buffer_size\nsyn keyword ngxDirective contained msie_padding\nsyn keyword ngxDirective contained msie_refresh\nsyn keyword ngxDirective contained multi_accept\nsyn keyword ngxDirective contained ntlm\nsyn keyword ngxDirective contained open_file_cache\nsyn keyword ngxDirective contained open_file_cache_errors\nsyn keyword ngxDirective contained open_file_cache_events\nsyn keyword ngxDirective contained open_file_cache_min_uses\nsyn keyword ngxDirective contained open_file_cache_valid\nsyn keyword ngxDirective contained open_log_file_cache\nsyn keyword ngxDirective contained output_buffers\nsyn keyword ngxDirective contained override_charset\nsyn keyword ngxDirective contained pcre_jit\nsyn keyword ngxDirective contained perl\nsyn keyword ngxDirective contained perl_modules\nsyn keyword ngxDirective contained perl_require\nsyn keyword ngxDirective contained perl_set\nsyn keyword ngxDirective contained pid\nsyn keyword ngxDirective contained pop3_auth\nsyn keyword ngxDirective contained pop3_capabilities\nsyn keyword ngxDirective contained port_in_redirect\nsyn keyword ngxDirective contained post_acceptex\nsyn keyword ngxDirective contained postpone_gzipping\nsyn keyword ngxDirective contained postpone_output\nsyn keyword ngxDirective contained preread_buffer_size\nsyn keyword ngxDirective contained preread_timeout\nsyn keyword ngxDirective contained protocol\nsyn keyword ngxDirective contained proxy\nsyn keyword ngxDirective contained proxy_bind\nsyn keyword ngxDirective contained proxy_buffer\nsyn keyword ngxDirective contained proxy_buffer_size\nsyn keyword ngxDirective contained proxy_buffering\nsyn keyword ngxDirective contained proxy_buffers\nsyn keyword ngxDirective contained proxy_busy_buffers_size\nsyn keyword ngxDirective contained proxy_cache\nsyn keyword ngxDirective contained proxy_cache_background_update\nsyn keyword ngxDirective contained proxy_cache_bypass\nsyn keyword ngxDirective contained proxy_cache_convert_head\nsyn keyword ngxDirective contained proxy_cache_key\nsyn keyword ngxDirective contained proxy_cache_lock\nsyn keyword ngxDirective contained proxy_cache_lock_age\nsyn keyword ngxDirective contained proxy_cache_lock_timeout\nsyn keyword ngxDirective contained proxy_cache_max_range_offset\nsyn keyword ngxDirective contained proxy_cache_methods\nsyn keyword ngxDirective contained proxy_cache_min_uses\nsyn keyword ngxDirective contained proxy_cache_path\nsyn keyword ngxDirective contained proxy_cache_purge\nsyn keyword ngxDirective contained proxy_cache_revalidate\nsyn keyword ngxDirective contained proxy_cache_use_stale\nsyn keyword ngxDirective contained proxy_cache_valid\nsyn keyword ngxDirective contained proxy_connect_timeout\nsyn keyword ngxDirective contained proxy_cookie_domain\nsyn keyword ngxDirective contained proxy_cookie_path\nsyn keyword ngxDirective contained proxy_download_rate\nsyn keyword ngxDirective contained proxy_force_ranges\nsyn keyword ngxDirective contained proxy_headers_hash_bucket_size\nsyn keyword ngxDirective contained proxy_headers_hash_max_size\nsyn keyword ngxDirective contained proxy_hide_header\nsyn keyword ngxDirective contained proxy_http_version\nsyn keyword ngxDirective contained proxy_ignore_client_abort\nsyn keyword ngxDirective contained proxy_ignore_headers\nsyn keyword ngxDirective contained proxy_intercept_errors\nsyn keyword ngxDirective contained proxy_limit_rate\nsyn keyword ngxDirective contained proxy_max_temp_file_size\nsyn keyword ngxDirective contained proxy_method\nsyn keyword ngxDirective contained proxy_next_upstream\nsyn keyword ngxDirective contained proxy_next_upstream_timeout\nsyn keyword ngxDirective contained proxy_next_upstream_tries\nsyn keyword ngxDirective contained proxy_no_cache\nsyn keyword ngxDirective contained proxy_pass_error_message\nsyn keyword ngxDirective contained proxy_pass_header\nsyn keyword ngxDirective contained proxy_pass_request_body\nsyn keyword ngxDirective contained proxy_pass_request_headers\nsyn keyword ngxDirective contained proxy_protocol\nsyn keyword ngxDirective contained proxy_protocol_timeout\nsyn keyword ngxDirective contained proxy_read_timeout\nsyn keyword ngxDirective contained proxy_redirect\nsyn keyword ngxDirective contained proxy_request_buffering\nsyn keyword ngxDirective contained proxy_requests\nsyn keyword ngxDirective contained proxy_responses\nsyn keyword ngxDirective contained proxy_send_lowat\nsyn keyword ngxDirective contained proxy_send_timeout\nsyn keyword ngxDirective contained proxy_session_drop\nsyn keyword ngxDirective contained proxy_set_body\nsyn keyword ngxDirective contained proxy_set_header\nsyn keyword ngxDirective contained proxy_socket_keepalive\nsyn keyword ngxDirective contained proxy_ssl\nsyn keyword ngxDirective contained proxy_ssl_certificate\nsyn keyword ngxDirective contained proxy_ssl_certificate_key\nsyn keyword ngxDirective contained proxy_ssl_ciphers\nsyn keyword ngxDirective contained proxy_ssl_crl\nsyn keyword ngxDirective contained proxy_ssl_name\nsyn keyword ngxDirective contained proxy_ssl_password_file\nsyn keyword ngxDirective contained proxy_ssl_protocols\nsyn keyword ngxDirective contained proxy_ssl_server_name\nsyn keyword ngxDirective contained proxy_ssl_session_reuse\nsyn keyword ngxDirective contained proxy_ssl_trusted_certificate\nsyn keyword ngxDirective contained proxy_ssl_verify\nsyn keyword ngxDirective contained proxy_ssl_verify_depth\nsyn keyword ngxDirective contained proxy_store\nsyn keyword ngxDirective contained proxy_store_access\nsyn keyword ngxDirective contained proxy_temp_file_write_size\nsyn keyword ngxDirective contained proxy_temp_path\nsyn keyword ngxDirective contained proxy_timeout\nsyn keyword ngxDirective contained proxy_upload_rate\nsyn keyword ngxDirective contained queue\nsyn keyword ngxDirective contained random\nsyn keyword ngxDirective contained random_index\nsyn keyword ngxDirective contained read_ahead\nsyn keyword ngxDirective contained real_ip_header\nsyn keyword ngxDirective contained real_ip_recursive\nsyn keyword ngxDirective contained recursive_error_pages\nsyn keyword ngxDirective contained referer_hash_bucket_size\nsyn keyword ngxDirective contained referer_hash_max_size\nsyn keyword ngxDirective contained request_pool_size\nsyn keyword ngxDirective contained reset_timedout_connection\nsyn keyword ngxDirective contained resolver\nsyn keyword ngxDirective contained resolver_timeout\nsyn keyword ngxDirective contained rewrite_log\nsyn keyword ngxDirective contained satisfy\nsyn keyword ngxDirective contained scgi_bind\nsyn keyword ngxDirective contained scgi_buffer_size\nsyn keyword ngxDirective contained scgi_buffering\nsyn keyword ngxDirective contained scgi_buffers\nsyn keyword ngxDirective contained scgi_busy_buffers_size\nsyn keyword ngxDirective contained scgi_cache\nsyn keyword ngxDirective contained scgi_cache_background_update\nsyn keyword ngxDirective contained scgi_cache_bypass\nsyn keyword ngxDirective contained scgi_cache_key\nsyn keyword ngxDirective contained scgi_cache_lock\nsyn keyword ngxDirective contained scgi_cache_lock_age\nsyn keyword ngxDirective contained scgi_cache_lock_timeout\nsyn keyword ngxDirective contained scgi_cache_max_range_offset\nsyn keyword ngxDirective contained scgi_cache_methods\nsyn keyword ngxDirective contained scgi_cache_min_uses\nsyn keyword ngxDirective contained scgi_cache_path\nsyn keyword ngxDirective contained scgi_cache_purge\nsyn keyword ngxDirective contained scgi_cache_revalidate\nsyn keyword ngxDirective contained scgi_cache_use_stale\nsyn keyword ngxDirective contained scgi_cache_valid\nsyn keyword ngxDirective contained scgi_connect_timeout\nsyn keyword ngxDirective contained scgi_force_ranges\nsyn keyword ngxDirective contained scgi_hide_header\nsyn keyword ngxDirective contained scgi_ignore_client_abort\nsyn keyword ngxDirective contained scgi_ignore_headers\nsyn keyword ngxDirective contained scgi_intercept_errors\nsyn keyword ngxDirective contained scgi_limit_rate\nsyn keyword ngxDirective contained scgi_max_temp_file_size\nsyn keyword ngxDirective contained scgi_next_upstream\nsyn keyword ngxDirective contained scgi_next_upstream_timeout\nsyn keyword ngxDirective contained scgi_next_upstream_tries\nsyn keyword ngxDirective contained scgi_no_cache\nsyn keyword ngxDirective contained scgi_param\nsyn keyword ngxDirective contained scgi_pass_header\nsyn keyword ngxDirective contained scgi_pass_request_body\nsyn keyword ngxDirective contained scgi_pass_request_headers\nsyn keyword ngxDirective contained scgi_read_timeout\nsyn keyword ngxDirective contained scgi_request_buffering\nsyn keyword ngxDirective contained scgi_send_timeout\nsyn keyword ngxDirective contained scgi_socket_keepalive\nsyn keyword ngxDirective contained scgi_store\nsyn keyword ngxDirective contained scgi_store_access\nsyn keyword ngxDirective contained scgi_temp_file_write_size\nsyn keyword ngxDirective contained scgi_temp_path\nsyn keyword ngxDirective contained secure_link\nsyn keyword ngxDirective contained secure_link_md5\nsyn keyword ngxDirective contained secure_link_secret\nsyn keyword ngxDirective contained send_lowat\nsyn keyword ngxDirective contained send_timeout\nsyn keyword ngxDirective contained sendfile\nsyn keyword ngxDirective contained sendfile_max_chunk\nsyn keyword ngxDirective contained server_name_in_redirect\nsyn keyword ngxDirective contained server_names_hash_bucket_size\nsyn keyword ngxDirective contained server_names_hash_max_size\nsyn keyword ngxDirective contained server_tokens\nsyn keyword ngxDirective contained session_log\nsyn keyword ngxDirective contained session_log_format\nsyn keyword ngxDirective contained session_log_zone\nsyn keyword ngxDirective contained set_real_ip_from\nsyn keyword ngxDirective contained slice\nsyn keyword ngxDirective contained smtp_auth\nsyn keyword ngxDirective contained smtp_capabilities\nsyn keyword ngxDirective contained smtp_client_buffer\nsyn keyword ngxDirective contained smtp_greeting_delay\nsyn keyword ngxDirective contained source_charset\nsyn keyword ngxDirective contained ssi\nsyn keyword ngxDirective contained ssi_ignore_recycled_buffers\nsyn keyword ngxDirective contained ssi_last_modified\nsyn keyword ngxDirective contained ssi_min_file_chunk\nsyn keyword ngxDirective contained ssi_silent_errors\nsyn keyword ngxDirective contained ssi_types\nsyn keyword ngxDirective contained ssi_value_length\nsyn keyword ngxDirective contained ssl_buffer_size\nsyn keyword ngxDirective contained ssl_certificate\nsyn keyword ngxDirective contained ssl_certificate_key\nsyn keyword ngxDirective contained ssl_ciphers\nsyn keyword ngxDirective contained ssl_client_certificate\nsyn keyword ngxDirective contained ssl_crl\nsyn keyword ngxDirective contained ssl_dhparam\nsyn keyword ngxDirective contained ssl_early_data\nsyn keyword ngxDirective contained ssl_ecdh_curve\nsyn keyword ngxDirective contained ssl_engine\nsyn keyword ngxDirective contained ssl_handshake_timeout\nsyn keyword ngxDirective contained ssl_password_file\nsyn keyword ngxDirective contained ssl_prefer_server_ciphers\nsyn keyword ngxDirective contained ssl_preread\nsyn keyword ngxDirective contained ssl_protocols\nsyn keyword ngxDirective contained ssl_session_cache\nsyn keyword ngxDirective contained ssl_session_ticket_key\nsyn keyword ngxDirective contained ssl_session_tickets\nsyn keyword ngxDirective contained ssl_session_timeout\nsyn keyword ngxDirective contained ssl_stapling\nsyn keyword ngxDirective contained ssl_stapling_file\nsyn keyword ngxDirective contained ssl_stapling_responder\nsyn keyword ngxDirective contained ssl_stapling_verify\nsyn keyword ngxDirective contained ssl_trusted_certificate\nsyn keyword ngxDirective contained ssl_verify_client\nsyn keyword ngxDirective contained ssl_verify_depth\nsyn keyword ngxDirective contained starttls\nsyn keyword ngxDirective contained state\nsyn keyword ngxDirective contained status\nsyn keyword ngxDirective contained status_format\nsyn keyword ngxDirective contained status_zone\nsyn keyword ngxDirective contained sticky\nsyn keyword ngxDirective contained sticky_cookie_insert\nsyn keyword ngxDirective contained stub_status\nsyn keyword ngxDirective contained sub_filter\nsyn keyword ngxDirective contained sub_filter_last_modified\nsyn keyword ngxDirective contained sub_filter_once\nsyn keyword ngxDirective contained sub_filter_types\nsyn keyword ngxDirective contained subrequest_output_buffer_size\nsyn keyword ngxDirective contained tcp_nodelay\nsyn keyword ngxDirective contained tcp_nopush\nsyn keyword ngxDirective contained thread_pool\nsyn keyword ngxDirective contained timeout\nsyn keyword ngxDirective contained timer_resolution\nsyn keyword ngxDirective contained types_hash_bucket_size\nsyn keyword ngxDirective contained types_hash_max_size\nsyn keyword ngxDirective contained underscores_in_headers\nsyn keyword ngxDirective contained uninitialized_variable_warn\nsyn keyword ngxDirective contained use\nsyn keyword ngxDirective contained user\nsyn keyword ngxDirective contained userid\nsyn keyword ngxDirective contained userid_domain\nsyn keyword ngxDirective contained userid_expires\nsyn keyword ngxDirective contained userid_mark\nsyn keyword ngxDirective contained userid_name\nsyn keyword ngxDirective contained userid_p3p\nsyn keyword ngxDirective contained userid_path\nsyn keyword ngxDirective contained userid_service\nsyn keyword ngxDirective contained uwsgi_bind\nsyn keyword ngxDirective contained uwsgi_buffer_size\nsyn keyword ngxDirective contained uwsgi_buffering\nsyn keyword ngxDirective contained uwsgi_buffers\nsyn keyword ngxDirective contained uwsgi_busy_buffers_size\nsyn keyword ngxDirective contained uwsgi_cache\nsyn keyword ngxDirective contained uwsgi_cache_background_update\nsyn keyword ngxDirective contained uwsgi_cache_bypass\nsyn keyword ngxDirective contained uwsgi_cache_key\nsyn keyword ngxDirective contained uwsgi_cache_lock\nsyn keyword ngxDirective contained uwsgi_cache_lock_age\nsyn keyword ngxDirective contained uwsgi_cache_lock_timeout\nsyn keyword ngxDirective contained uwsgi_cache_max_range_offset\nsyn keyword ngxDirective contained uwsgi_cache_methods\nsyn keyword ngxDirective contained uwsgi_cache_min_uses\nsyn keyword ngxDirective contained uwsgi_cache_path\nsyn keyword ngxDirective contained uwsgi_cache_purge\nsyn keyword ngxDirective contained uwsgi_cache_revalidate\nsyn keyword ngxDirective contained uwsgi_cache_use_stale\nsyn keyword ngxDirective contained uwsgi_cache_valid\nsyn keyword ngxDirective contained uwsgi_connect_timeout\nsyn keyword ngxDirective contained uwsgi_force_ranges\nsyn keyword ngxDirective contained uwsgi_hide_header\nsyn keyword ngxDirective contained uwsgi_ignore_client_abort\nsyn keyword ngxDirective contained uwsgi_ignore_headers\nsyn keyword ngxDirective contained uwsgi_intercept_errors\nsyn keyword ngxDirective contained uwsgi_limit_rate\nsyn keyword ngxDirective contained uwsgi_max_temp_file_size\nsyn keyword ngxDirective contained uwsgi_modifier1\nsyn keyword ngxDirective contained uwsgi_modifier2\nsyn keyword ngxDirective contained uwsgi_next_upstream\nsyn keyword ngxDirective contained uwsgi_next_upstream_timeout\nsyn keyword ngxDirective contained uwsgi_next_upstream_tries\nsyn keyword ngxDirective contained uwsgi_no_cache\nsyn keyword ngxDirective contained uwsgi_param\nsyn keyword ngxDirective contained uwsgi_pass_header\nsyn keyword ngxDirective contained uwsgi_pass_request_body\nsyn keyword ngxDirective contained uwsgi_pass_request_headers\nsyn keyword ngxDirective contained uwsgi_read_timeout\nsyn keyword ngxDirective contained uwsgi_request_buffering\nsyn keyword ngxDirective contained uwsgi_send_timeout\nsyn keyword ngxDirective contained uwsgi_socket_keepalive\nsyn keyword ngxDirective contained uwsgi_ssl_certificate\nsyn keyword ngxDirective contained uwsgi_ssl_certificate_key\nsyn keyword ngxDirective contained uwsgi_ssl_ciphers\nsyn keyword ngxDirective contained uwsgi_ssl_crl\nsyn keyword ngxDirective contained uwsgi_ssl_name\nsyn keyword ngxDirective contained uwsgi_ssl_password_file\nsyn keyword ngxDirective contained uwsgi_ssl_protocols\nsyn keyword ngxDirective contained uwsgi_ssl_server_name\nsyn keyword ngxDirective contained uwsgi_ssl_session_reuse\nsyn keyword ngxDirective contained uwsgi_ssl_trusted_certificate\nsyn keyword ngxDirective contained uwsgi_ssl_verify\nsyn keyword ngxDirective contained uwsgi_ssl_verify_depth\nsyn keyword ngxDirective contained uwsgi_store\nsyn keyword ngxDirective contained uwsgi_store_access\nsyn keyword ngxDirective contained uwsgi_string\nsyn keyword ngxDirective contained uwsgi_temp_file_write_size\nsyn keyword ngxDirective contained uwsgi_temp_path\nsyn keyword ngxDirective contained valid_referers\nsyn keyword ngxDirective contained variables_hash_bucket_size\nsyn keyword ngxDirective contained variables_hash_max_size\nsyn keyword ngxDirective contained worker_aio_requests\nsyn keyword ngxDirective contained worker_connections\nsyn keyword ngxDirective contained worker_cpu_affinity\nsyn keyword ngxDirective contained worker_priority\nsyn keyword ngxDirective contained worker_processes\nsyn keyword ngxDirective contained worker_rlimit_core\nsyn keyword ngxDirective contained worker_rlimit_nofile\nsyn keyword ngxDirective contained worker_shutdown_timeout\nsyn keyword ngxDirective contained working_directory\nsyn keyword ngxDirective contained xclient\nsyn keyword ngxDirective contained xml_entities\nsyn keyword ngxDirective contained xslt_last_modified\nsyn keyword ngxDirective contained xslt_param\nsyn keyword ngxDirective contained xslt_string_param\nsyn keyword ngxDirective contained xslt_stylesheet\nsyn keyword ngxDirective contained xslt_types\nsyn keyword ngxDirective contained zone\nsyn keyword ngxDirective contained zone_sync\nsyn keyword ngxDirective contained zone_sync_buffers\nsyn keyword ngxDirective contained zone_sync_connect_retry_interval\nsyn keyword ngxDirective contained zone_sync_connect_timeout\nsyn keyword ngxDirective contained zone_sync_interval\nsyn keyword ngxDirective contained zone_sync_recv_buffer_size\nsyn keyword ngxDirective contained zone_sync_server\nsyn keyword ngxDirective contained zone_sync_ssl\nsyn keyword ngxDirective contained zone_sync_ssl_certificate\nsyn keyword ngxDirective contained zone_sync_ssl_certificate_key\nsyn keyword ngxDirective contained zone_sync_ssl_ciphers\nsyn keyword ngxDirective contained zone_sync_ssl_crl\nsyn keyword ngxDirective contained zone_sync_ssl_name\nsyn keyword ngxDirective contained zone_sync_ssl_password_file\nsyn keyword ngxDirective contained zone_sync_ssl_protocols\nsyn keyword ngxDirective contained zone_sync_ssl_server_name\nsyn keyword ngxDirective contained zone_sync_ssl_trusted_certificate\nsyn keyword ngxDirective contained zone_sync_ssl_verify\nsyn keyword ngxDirective contained zone_sync_ssl_verify_depth\nsyn keyword ngxDirective contained zone_sync_timeout\n\n\" 3rd party modules list taken from\n\" https://github.com/freebsd/freebsd-ports/blob/master/www/nginx-devel/Makefile\n\" -----------------------------------------------------------------------------\n\n\" Accept Language\n\" https://github.com/giom/nginx_accept_language_module\nsyn keyword ngxDirectiveThirdParty contained set_from_accept_language\n\n\" Digest Authentication\n\" https://github.com/atomx/nginx-http-auth-digest\nsyn keyword ngxDirectiveThirdParty contained auth_digest\nsyn keyword ngxDirectiveThirdParty contained auth_digest_drop_time\nsyn keyword ngxDirectiveThirdParty contained auth_digest_evasion_time\nsyn keyword ngxDirectiveThirdParty contained auth_digest_expires\nsyn keyword ngxDirectiveThirdParty contained auth_digest_maxtries\nsyn keyword ngxDirectiveThirdParty contained auth_digest_replays\nsyn keyword ngxDirectiveThirdParty contained auth_digest_shm_size\nsyn keyword ngxDirectiveThirdParty contained auth_digest_timeout\nsyn keyword ngxDirectiveThirdParty contained auth_digest_user_file\n\n\" SPNEGO Authentication\n\" https://github.com/stnoonan/spnego-http-auth-nginx-module\nsyn keyword ngxDirectiveThirdParty contained auth_gss\nsyn keyword ngxDirectiveThirdParty contained auth_gss_allow_basic_fallback\nsyn keyword ngxDirectiveThirdParty contained auth_gss_authorized_principal\nsyn keyword ngxDirectiveThirdParty contained auth_gss_force_realm\nsyn keyword ngxDirectiveThirdParty contained auth_gss_format_full\nsyn keyword ngxDirectiveThirdParty contained auth_gss_keytab\nsyn keyword ngxDirectiveThirdParty contained auth_gss_realm\nsyn keyword ngxDirectiveThirdParty contained auth_gss_service_name\n\n\" LDAP Authentication\n\" https://github.com/kvspb/nginx-auth-ldap\nsyn keyword ngxDirectiveThirdParty contained auth_ldap\nsyn keyword ngxDirectiveThirdParty contained auth_ldap_cache_enabled\nsyn keyword ngxDirectiveThirdParty contained auth_ldap_cache_expiration_time\nsyn keyword ngxDirectiveThirdParty contained auth_ldap_cache_size\nsyn keyword ngxDirectiveThirdParty contained auth_ldap_servers\nsyn keyword ngxDirectiveThirdParty contained auth_ldap_servers_size\nsyn keyword ngxDirectiveThirdParty contained ldap_server\n\n\" PAM Authentication\n\" https://github.com/sto/ngx_http_auth_pam_module\nsyn keyword ngxDirectiveThirdParty contained auth_pam\nsyn keyword ngxDirectiveThirdParty contained auth_pam_service_name\nsyn keyword ngxDirectiveThirdParty contained auth_pam_set_pam_env\n\n\" AJP protocol proxy\n\" https://github.com/yaoweibin/nginx_ajp_module\nsyn keyword ngxDirectiveThirdParty contained ajp_buffer_size\nsyn keyword ngxDirectiveThirdParty contained ajp_buffers\nsyn keyword ngxDirectiveThirdParty contained ajp_busy_buffers_size\nsyn keyword ngxDirectiveThirdParty contained ajp_cache\nsyn keyword ngxDirectiveThirdParty contained ajp_cache_key\nsyn keyword ngxDirectiveThirdParty contained ajp_cache_lock\nsyn keyword ngxDirectiveThirdParty contained ajp_cache_lock_timeout\nsyn keyword ngxDirectiveThirdParty contained ajp_cache_methods\nsyn keyword ngxDirectiveThirdParty contained ajp_cache_min_uses\nsyn keyword ngxDirectiveThirdParty contained ajp_cache_path\nsyn keyword ngxDirectiveThirdParty contained ajp_cache_use_stale\nsyn keyword ngxDirectiveThirdParty contained ajp_cache_valid\nsyn keyword ngxDirectiveThirdParty contained ajp_connect_timeout\nsyn keyword ngxDirectiveThirdParty contained ajp_header_packet_buffer_size\nsyn keyword ngxDirectiveThirdParty contained ajp_hide_header\nsyn keyword ngxDirectiveThirdParty contained ajp_ignore_client_abort\nsyn keyword ngxDirectiveThirdParty contained ajp_ignore_headers\nsyn keyword ngxDirectiveThirdParty contained ajp_intercept_errors\nsyn keyword ngxDirectiveThirdParty contained ajp_keep_conn\nsyn keyword ngxDirectiveThirdParty contained ajp_max_data_packet_size\nsyn keyword ngxDirectiveThirdParty contained ajp_max_temp_file_size\nsyn keyword ngxDirectiveThirdParty contained ajp_next_upstream\nsyn keyword ngxDirectiveThirdParty contained ajp_pass\nsyn keyword ngxDirectiveThirdParty contained ajp_pass_header\nsyn keyword ngxDirectiveThirdParty contained ajp_pass_request_body\nsyn keyword ngxDirectiveThirdParty contained ajp_pass_request_headers\nsyn keyword ngxDirectiveThirdParty contained ajp_read_timeout\nsyn keyword ngxDirectiveThirdParty contained ajp_send_lowat\nsyn keyword ngxDirectiveThirdParty contained ajp_send_timeout\nsyn keyword ngxDirectiveThirdParty contained ajp_store\nsyn keyword ngxDirectiveThirdParty contained ajp_store_access\nsyn keyword ngxDirectiveThirdParty contained ajp_temp_file_write_size\nsyn keyword ngxDirectiveThirdParty contained ajp_temp_path\nsyn keyword ngxDirectiveThirdParty contained ajp_upstream_fail_timeout\nsyn keyword ngxDirectiveThirdParty contained ajp_upstream_max_fails\n\n\" AWS proxy\n\" https://github.com/anomalizer/ngx_aws_auth\nsyn keyword ngxDirectiveThirdParty contained aws_access_key\nsyn keyword ngxDirectiveThirdParty contained aws_endpoint\nsyn keyword ngxDirectiveThirdParty contained aws_key_scope\nsyn keyword ngxDirectiveThirdParty contained aws_s3_bucket\nsyn keyword ngxDirectiveThirdParty contained aws_sign\nsyn keyword ngxDirectiveThirdParty contained aws_signing_key\n\n\" embedding Clojure or Java or Groovy programs\n\" https://github.com/nginx-clojure/nginx-clojure\nsyn keyword ngxDirectiveThirdParty contained access_handler_code\nsyn keyword ngxDirectiveThirdParty contained access_handler_name\nsyn keyword ngxDirectiveThirdParty contained access_handler_property\nsyn keyword ngxDirectiveThirdParty contained access_handler_type\nsyn keyword ngxDirectiveThirdParty contained always_read_body\nsyn keyword ngxDirectiveThirdParty contained auto_upgrade_ws\nsyn keyword ngxDirectiveThirdParty contained body_filter_code\nsyn keyword ngxDirectiveThirdParty contained body_filter_name\nsyn keyword ngxDirectiveThirdParty contained body_filter_property\nsyn keyword ngxDirectiveThirdParty contained body_filter_type\nsyn keyword ngxDirectiveThirdParty contained content_handler_code\nsyn keyword ngxDirectiveThirdParty contained content_handler_name\nsyn keyword ngxDirectiveThirdParty contained content_handler_property\nsyn keyword ngxDirectiveThirdParty contained content_handler_type\nsyn keyword ngxDirectiveThirdParty contained handler_code\nsyn keyword ngxDirectiveThirdParty contained handler_name\nsyn keyword ngxDirectiveThirdParty contained handler_type\nsyn keyword ngxDirectiveThirdParty contained handlers_lazy_init\nsyn keyword ngxDirectiveThirdParty contained header_filter_code\nsyn keyword ngxDirectiveThirdParty contained header_filter_name\nsyn keyword ngxDirectiveThirdParty contained header_filter_property\nsyn keyword ngxDirectiveThirdParty contained header_filter_type\nsyn keyword ngxDirectiveThirdParty contained jvm_classpath\nsyn keyword ngxDirectiveThirdParty contained jvm_classpath_check\nsyn keyword ngxDirectiveThirdParty contained jvm_exit_handler_code\nsyn keyword ngxDirectiveThirdParty contained jvm_exit_handler_name\nsyn keyword ngxDirectiveThirdParty contained jvm_handler_type\nsyn keyword ngxDirectiveThirdParty contained jvm_init_handler_code\nsyn keyword ngxDirectiveThirdParty contained jvm_init_handler_name\nsyn keyword ngxDirectiveThirdParty contained jvm_options\nsyn keyword ngxDirectiveThirdParty contained jvm_path\nsyn keyword ngxDirectiveThirdParty contained jvm_var\nsyn keyword ngxDirectiveThirdParty contained jvm_workers\nsyn keyword ngxDirectiveThirdParty contained max_balanced_tcp_connections\nsyn keyword ngxDirectiveThirdParty contained rewrite_handler_code\nsyn keyword ngxDirectiveThirdParty contained rewrite_handler_name\nsyn keyword ngxDirectiveThirdParty contained rewrite_handler_property\nsyn keyword ngxDirectiveThirdParty contained rewrite_handler_type\nsyn keyword ngxDirectiveThirdParty contained shared_map\nsyn keyword ngxDirectiveThirdParty contained write_page_size\n\n\" Certificate Transparency\n\" https://github.com/grahamedgecombe/nginx-ct\nsyn keyword ngxDirectiveThirdParty contained ssl_ct\nsyn keyword ngxDirectiveThirdParty contained ssl_ct_static_scts\n\n\" ngx_echo\n\" https://github.com/openresty/echo-nginx-module\nsyn keyword ngxDirectiveThirdParty contained echo_abort_parent\nsyn keyword ngxDirectiveThirdParty contained echo_after_body\nsyn keyword ngxDirectiveThirdParty contained echo_before_body\nsyn keyword ngxDirectiveThirdParty contained echo_blocking_sleep\nsyn keyword ngxDirectiveThirdParty contained echo_end\nsyn keyword ngxDirectiveThirdParty contained echo_exec\nsyn keyword ngxDirectiveThirdParty contained echo_flush\nsyn keyword ngxDirectiveThirdParty contained echo_foreach_split\nsyn keyword ngxDirectiveThirdParty contained echo_location\nsyn keyword ngxDirectiveThirdParty contained echo_location_async\nsyn keyword ngxDirectiveThirdParty contained echo_read_request_body\nsyn keyword ngxDirectiveThirdParty contained echo_request_body\nsyn keyword ngxDirectiveThirdParty contained echo_reset_timer\nsyn keyword ngxDirectiveThirdParty contained echo_status\nsyn keyword ngxDirectiveThirdParty contained echo_subrequest\nsyn keyword ngxDirectiveThirdParty contained echo_subrequest_async\n\n\" FastDFS\n\" https://github.com/happyfish100/fastdfs-nginx-module\nsyn keyword ngxDirectiveThirdParty contained ngx_fastdfs_module\n\n\" ngx_headers_more\n\" https://github.com/openresty/headers-more-nginx-module\nsyn keyword ngxDirectiveThirdParty contained more_clear_headers\nsyn keyword ngxDirectiveThirdParty contained more_clear_input_headers\nsyn keyword ngxDirectiveThirdParty contained more_set_headers\nsyn keyword ngxDirectiveThirdParty contained more_set_input_headers\n\n\" NGINX WebDAV missing commands support (PROPFIND & OPTIONS)\n\" https://github.com/arut/nginx-dav-ext-module\nsyn keyword ngxDirectiveThirdParty contained dav_ext_lock\nsyn keyword ngxDirectiveThirdParty contained dav_ext_lock_zone\nsyn keyword ngxDirectiveThirdParty contained dav_ext_methods\n\n\" ngx_eval\n\" https://github.com/openresty/nginx-eval-module\nsyn keyword ngxDirectiveThirdParty contained eval\nsyn keyword ngxDirectiveThirdParty contained eval_buffer_size\nsyn keyword ngxDirectiveThirdParty contained eval_escalate\nsyn keyword ngxDirectiveThirdParty contained eval_override_content_type\nsyn keyword ngxDirectiveThirdParty contained eval_subrequest_in_memory\n\n\" Fancy Index\n\" https://github.com/aperezdc/ngx-fancyindex\nsyn keyword ngxDirectiveThirdParty contained fancyindex\nsyn keyword ngxDirectiveThirdParty contained fancyindex_css_href\nsyn keyword ngxDirectiveThirdParty contained fancyindex_default_sort\nsyn keyword ngxDirectiveThirdParty contained fancyindex_directories_first\nsyn keyword ngxDirectiveThirdParty contained fancyindex_exact_size\nsyn keyword ngxDirectiveThirdParty contained fancyindex_footer\nsyn keyword ngxDirectiveThirdParty contained fancyindex_header\nsyn keyword ngxDirectiveThirdParty contained fancyindex_hide_parent_dir\nsyn keyword ngxDirectiveThirdParty contained fancyindex_hide_symlinks\nsyn keyword ngxDirectiveThirdParty contained fancyindex_ignore\nsyn keyword ngxDirectiveThirdParty contained fancyindex_localtime\nsyn keyword ngxDirectiveThirdParty contained fancyindex_name_length\nsyn keyword ngxDirectiveThirdParty contained fancyindex_show_path\nsyn keyword ngxDirectiveThirdParty contained fancyindex_time_format\n\n\" Footer filter\n\" https://github.com/alibaba/nginx-http-footer-filter\nsyn keyword ngxDirectiveThirdParty contained footer\nsyn keyword ngxDirectiveThirdParty contained footer_types\n\n\" ngx_http_geoip2_module\n\" https://github.com/leev/ngx_http_geoip2_module\nsyn keyword ngxDirectiveThirdParty contained geoip2\nsyn keyword ngxDirectiveThirdParty contained geoip2_proxy\nsyn keyword ngxDirectiveThirdParty contained geoip2_proxy_recursive\n\n\" A version of the Nginx HTTP stub status module that outputs in JSON format\n\" https://github.com/nginx-modules/nginx-json-status-module\nsyn keyword ngxDirectiveThirdParty contained json_status\nsyn keyword ngxDirectiveThirdParty contained json_status_type\n\n\" MogileFS client for nginx\n\" https://github.com/vkholodkov/nginx-mogilefs-module\nsyn keyword ngxDirectiveThirdParty contained mogilefs_class\nsyn keyword ngxDirectiveThirdParty contained mogilefs_connect_timeout\nsyn keyword ngxDirectiveThirdParty contained mogilefs_domain\nsyn keyword ngxDirectiveThirdParty contained mogilefs_methods\nsyn keyword ngxDirectiveThirdParty contained mogilefs_noverify\nsyn keyword ngxDirectiveThirdParty contained mogilefs_pass\nsyn keyword ngxDirectiveThirdParty contained mogilefs_read_timeout\nsyn keyword ngxDirectiveThirdParty contained mogilefs_send_timeout\nsyn keyword ngxDirectiveThirdParty contained mogilefs_tracker\n\n\" Ancient nginx plugin; probably not useful to anyone\n\" https://github.com/kr/nginx-notice\nsyn keyword ngxDirectiveThirdParty contained notice\nsyn keyword ngxDirectiveThirdParty contained notice_type\n\n\" nchan\n\" https://github.com/slact/nchan\nsyn keyword ngxDirectiveThirdParty contained nchan_access_control_allow_credentials\nsyn keyword ngxDirectiveThirdParty contained nchan_access_control_allow_origin\nsyn keyword ngxDirectiveThirdParty contained nchan_authorize_request\nsyn keyword ngxDirectiveThirdParty contained nchan_benchmark\nsyn keyword ngxDirectiveThirdParty contained nchan_benchmark_channels\nsyn keyword ngxDirectiveThirdParty contained nchan_benchmark_message_padding_bytes\nsyn keyword ngxDirectiveThirdParty contained nchan_benchmark_messages_per_channel_per_minute\nsyn keyword ngxDirectiveThirdParty contained nchan_benchmark_publisher_distribution\nsyn keyword ngxDirectiveThirdParty contained nchan_benchmark_subscriber_distribution\nsyn keyword ngxDirectiveThirdParty contained nchan_benchmark_subscribers_per_channel\nsyn keyword ngxDirectiveThirdParty contained nchan_benchmark_time\nsyn keyword ngxDirectiveThirdParty contained nchan_channel_event_string\nsyn keyword ngxDirectiveThirdParty contained nchan_channel_events_channel_id\nsyn keyword ngxDirectiveThirdParty contained nchan_channel_group\nsyn keyword ngxDirectiveThirdParty contained nchan_channel_group_accounting\nsyn keyword ngxDirectiveThirdParty contained nchan_channel_id\nsyn keyword ngxDirectiveThirdParty contained nchan_channel_id_split_delimiter\nsyn keyword ngxDirectiveThirdParty contained nchan_channel_timeout\nsyn keyword ngxDirectiveThirdParty contained nchan_deflate_message_for_websocket\nsyn keyword ngxDirectiveThirdParty contained nchan_eventsource_event\nsyn keyword ngxDirectiveThirdParty contained nchan_group_location\nsyn keyword ngxDirectiveThirdParty contained nchan_group_max_channels\nsyn keyword ngxDirectiveThirdParty contained nchan_group_max_messages\nsyn keyword ngxDirectiveThirdParty contained nchan_group_max_messages_disk\nsyn keyword ngxDirectiveThirdParty contained nchan_group_max_messages_memory\nsyn keyword ngxDirectiveThirdParty contained nchan_group_max_subscribers\nsyn keyword ngxDirectiveThirdParty contained nchan_longpoll_multipart_response\nsyn keyword ngxDirectiveThirdParty contained nchan_max_channel_id_length\nsyn keyword ngxDirectiveThirdParty contained nchan_max_channel_subscribers\nsyn keyword ngxDirectiveThirdParty contained nchan_max_reserved_memory\nsyn keyword ngxDirectiveThirdParty contained nchan_message_buffer_length\nsyn keyword ngxDirectiveThirdParty contained nchan_message_max_buffer_length\nsyn keyword ngxDirectiveThirdParty contained nchan_message_temp_path\nsyn keyword ngxDirectiveThirdParty contained nchan_message_timeout\nsyn keyword ngxDirectiveThirdParty contained nchan_permessage_deflate_compression_level\nsyn keyword ngxDirectiveThirdParty contained nchan_permessage_deflate_compression_memlevel\nsyn keyword ngxDirectiveThirdParty contained nchan_permessage_deflate_compression_strategy\nsyn keyword ngxDirectiveThirdParty contained nchan_permessage_deflate_compression_window\nsyn keyword ngxDirectiveThirdParty contained nchan_pub_channel_id\nsyn keyword ngxDirectiveThirdParty contained nchan_publisher\nsyn keyword ngxDirectiveThirdParty contained nchan_publisher_channel_id\nsyn keyword ngxDirectiveThirdParty contained nchan_publisher_location\nsyn keyword ngxDirectiveThirdParty contained nchan_publisher_upstream_request\nsyn keyword ngxDirectiveThirdParty contained nchan_pubsub\nsyn keyword ngxDirectiveThirdParty contained nchan_pubsub_channel_id\nsyn keyword ngxDirectiveThirdParty contained nchan_pubsub_location\nsyn keyword ngxDirectiveThirdParty contained nchan_redis_connect_timeout\nsyn keyword ngxDirectiveThirdParty contained nchan_redis_fakesub_timer_interval\nsyn keyword ngxDirectiveThirdParty contained nchan_redis_idle_channel_cache_timeout\nsyn keyword ngxDirectiveThirdParty contained nchan_redis_namespace\nsyn keyword ngxDirectiveThirdParty contained nchan_redis_nostore_fastpublish\nsyn keyword ngxDirectiveThirdParty contained nchan_redis_optimize_target\nsyn keyword ngxDirectiveThirdParty contained nchan_redis_pass\nsyn keyword ngxDirectiveThirdParty contained nchan_redis_pass_inheritable\nsyn keyword ngxDirectiveThirdParty contained nchan_redis_ping_interval\nsyn keyword ngxDirectiveThirdParty contained nchan_redis_publish_msgpacked_max_size\nsyn keyword ngxDirectiveThirdParty contained nchan_redis_server\nsyn keyword ngxDirectiveThirdParty contained nchan_redis_storage_mode\nsyn keyword ngxDirectiveThirdParty contained nchan_redis_subscribe_weights\nsyn keyword ngxDirectiveThirdParty contained nchan_redis_url\nsyn keyword ngxDirectiveThirdParty contained nchan_redis_wait_after_connecting\nsyn keyword ngxDirectiveThirdParty contained nchan_shared_memory_size\nsyn keyword ngxDirectiveThirdParty contained nchan_storage_engine\nsyn keyword ngxDirectiveThirdParty contained nchan_store_messages\nsyn keyword ngxDirectiveThirdParty contained nchan_stub_status\nsyn keyword ngxDirectiveThirdParty contained nchan_sub_channel_id\nsyn keyword ngxDirectiveThirdParty contained nchan_subscribe_existing_channels_only\nsyn keyword ngxDirectiveThirdParty contained nchan_subscribe_request\nsyn keyword ngxDirectiveThirdParty contained nchan_subscriber\nsyn keyword ngxDirectiveThirdParty contained nchan_subscriber_channel_id\nsyn keyword ngxDirectiveThirdParty contained nchan_subscriber_compound_etag_message_id\nsyn keyword ngxDirectiveThirdParty contained nchan_subscriber_first_message\nsyn keyword ngxDirectiveThirdParty contained nchan_subscriber_http_raw_stream_separator\nsyn keyword ngxDirectiveThirdParty contained nchan_subscriber_last_message_id\nsyn keyword ngxDirectiveThirdParty contained nchan_subscriber_location\nsyn keyword ngxDirectiveThirdParty contained nchan_subscriber_message_id_custom_etag_header\nsyn keyword ngxDirectiveThirdParty contained nchan_subscriber_timeout\nsyn keyword ngxDirectiveThirdParty contained nchan_unsubscribe_request\nsyn keyword ngxDirectiveThirdParty contained nchan_use_redis\nsyn keyword ngxDirectiveThirdParty contained nchan_websocket_client_heartbeat\nsyn keyword ngxDirectiveThirdParty contained nchan_websocket_ping_interval\nsyn keyword ngxDirectiveThirdParty contained push_authorized_channels_only\nsyn keyword ngxDirectiveThirdParty contained push_channel_group\nsyn keyword ngxDirectiveThirdParty contained push_channel_timeout\nsyn keyword ngxDirectiveThirdParty contained push_max_channel_id_length\nsyn keyword ngxDirectiveThirdParty contained push_max_channel_subscribers\nsyn keyword ngxDirectiveThirdParty contained push_max_message_buffer_length\nsyn keyword ngxDirectiveThirdParty contained push_max_reserved_memory\nsyn keyword ngxDirectiveThirdParty contained push_message_buffer_length\nsyn keyword ngxDirectiveThirdParty contained push_message_timeout\nsyn keyword ngxDirectiveThirdParty contained push_min_message_buffer_length\nsyn keyword ngxDirectiveThirdParty contained push_publisher\nsyn keyword ngxDirectiveThirdParty contained push_store_messages\nsyn keyword ngxDirectiveThirdParty contained push_subscriber\nsyn keyword ngxDirectiveThirdParty contained push_subscriber_concurrency\nsyn keyword ngxDirectiveThirdParty contained push_subscriber_timeout\n\n\" Push Stream\n\" https://github.com/wandenberg/nginx-push-stream-module\nsyn keyword ngxDirectiveThirdParty contained push_stream_allow_connections_to_events_channel\nsyn keyword ngxDirectiveThirdParty contained push_stream_allowed_origins\nsyn keyword ngxDirectiveThirdParty contained push_stream_authorized_channels_only\nsyn keyword ngxDirectiveThirdParty contained push_stream_channel_deleted_message_text\nsyn keyword ngxDirectiveThirdParty contained push_stream_channel_inactivity_time\nsyn keyword ngxDirectiveThirdParty contained push_stream_channel_info_on_publish\nsyn keyword ngxDirectiveThirdParty contained push_stream_channels_path\nsyn keyword ngxDirectiveThirdParty contained push_stream_channels_statistics\nsyn keyword ngxDirectiveThirdParty contained push_stream_events_channel_id\nsyn keyword ngxDirectiveThirdParty contained push_stream_footer_template\nsyn keyword ngxDirectiveThirdParty contained push_stream_header_template\nsyn keyword ngxDirectiveThirdParty contained push_stream_header_template_file\nsyn keyword ngxDirectiveThirdParty contained push_stream_last_event_id\nsyn keyword ngxDirectiveThirdParty contained push_stream_last_received_message_tag\nsyn keyword ngxDirectiveThirdParty contained push_stream_last_received_message_time\nsyn keyword ngxDirectiveThirdParty contained push_stream_longpolling_connection_ttl\nsyn keyword ngxDirectiveThirdParty contained push_stream_max_channel_id_length\nsyn keyword ngxDirectiveThirdParty contained push_stream_max_messages_stored_per_channel\nsyn keyword ngxDirectiveThirdParty contained push_stream_max_number_of_channels\nsyn keyword ngxDirectiveThirdParty contained push_stream_max_number_of_wildcard_channels\nsyn keyword ngxDirectiveThirdParty contained push_stream_max_subscribers_per_channel\nsyn keyword ngxDirectiveThirdParty contained push_stream_message_template\nsyn keyword ngxDirectiveThirdParty contained push_stream_message_ttl\nsyn keyword ngxDirectiveThirdParty contained push_stream_padding_by_user_agent\nsyn keyword ngxDirectiveThirdParty contained push_stream_ping_message_interval\nsyn keyword ngxDirectiveThirdParty contained push_stream_ping_message_text\nsyn keyword ngxDirectiveThirdParty contained push_stream_publisher\nsyn keyword ngxDirectiveThirdParty contained push_stream_shared_memory_size\nsyn keyword ngxDirectiveThirdParty contained push_stream_store_messages\nsyn keyword ngxDirectiveThirdParty contained push_stream_subscriber\nsyn keyword ngxDirectiveThirdParty contained push_stream_subscriber_connection_ttl\nsyn keyword ngxDirectiveThirdParty contained push_stream_timeout_with_body\nsyn keyword ngxDirectiveThirdParty contained push_stream_user_agent\nsyn keyword ngxDirectiveThirdParty contained push_stream_websocket_allow_publish\nsyn keyword ngxDirectiveThirdParty contained push_stream_wildcard_channel_max_qtd\nsyn keyword ngxDirectiveThirdParty contained push_stream_wildcard_channel_prefix\n\n\" redis module\n\" https://www.nginx.com/resources/wiki/modules/redis/\nsyn keyword ngxDirectiveThirdParty contained redis_bind\nsyn keyword ngxDirectiveThirdParty contained redis_buffer_size\nsyn keyword ngxDirectiveThirdParty contained redis_connect_timeout\nsyn keyword ngxDirectiveThirdParty contained redis_gzip_flag\nsyn keyword ngxDirectiveThirdParty contained redis_next_upstream\nsyn keyword ngxDirectiveThirdParty contained redis_pass\nsyn keyword ngxDirectiveThirdParty contained redis_read_timeout\nsyn keyword ngxDirectiveThirdParty contained redis_send_timeout\n\n\" ngx_http_response\n\" http://catap.ru/downloads/nginx/\nsyn keyword ngxDirectiveThirdParty contained response\nsyn keyword ngxDirectiveThirdParty contained response_type\n\n\" nginx_substitutions_filter\n\" https://github.com/yaoweibin/ngx_http_substitutions_filter_module\nsyn keyword ngxDirectiveThirdParty contained subs_buffers\nsyn keyword ngxDirectiveThirdParty contained subs_filter\nsyn keyword ngxDirectiveThirdParty contained subs_filter_bypass\nsyn keyword ngxDirectiveThirdParty contained subs_filter_types\nsyn keyword ngxDirectiveThirdParty contained subs_line_buffer_size\n\n\" Tarantool nginx upstream module\n\" https://github.com/tarantool/nginx_upstream_module\nsyn keyword ngxDirectiveThirdParty contained tnt_allowed_indexes\nsyn keyword ngxDirectiveThirdParty contained tnt_allowed_spaces\nsyn keyword ngxDirectiveThirdParty contained tnt_buffer_size\nsyn keyword ngxDirectiveThirdParty contained tnt_connect_timeout\nsyn keyword ngxDirectiveThirdParty contained tnt_delete\nsyn keyword ngxDirectiveThirdParty contained tnt_http_methods\nsyn keyword ngxDirectiveThirdParty contained tnt_http_rest_methods\nsyn keyword ngxDirectiveThirdParty contained tnt_in_multiplier\nsyn keyword ngxDirectiveThirdParty contained tnt_insert\nsyn keyword ngxDirectiveThirdParty contained tnt_method\nsyn keyword ngxDirectiveThirdParty contained tnt_multireturn_skip_count\nsyn keyword ngxDirectiveThirdParty contained tnt_next_upstream\nsyn keyword ngxDirectiveThirdParty contained tnt_next_upstream_timeout\nsyn keyword ngxDirectiveThirdParty contained tnt_next_upstream_tries\nsyn keyword ngxDirectiveThirdParty contained tnt_out_multiplier\nsyn keyword ngxDirectiveThirdParty contained tnt_pass\nsyn keyword ngxDirectiveThirdParty contained tnt_pass_http_request\nsyn keyword ngxDirectiveThirdParty contained tnt_pass_http_request_buffer_size\nsyn keyword ngxDirectiveThirdParty contained tnt_pure_result\nsyn keyword ngxDirectiveThirdParty contained tnt_read_timeout\nsyn keyword ngxDirectiveThirdParty contained tnt_replace\nsyn keyword ngxDirectiveThirdParty contained tnt_select\nsyn keyword ngxDirectiveThirdParty contained tnt_select_limit_max\nsyn keyword ngxDirectiveThirdParty contained tnt_send_timeout\nsyn keyword ngxDirectiveThirdParty contained tnt_set_header\nsyn keyword ngxDirectiveThirdParty contained tnt_update\nsyn keyword ngxDirectiveThirdParty contained tnt_upsert\n\n\" A module for nginx web server for handling file uploads using multipart/form-data encoding (RFC 1867)\n\" https://github.com/Austinb/nginx-upload-module\nsyn keyword ngxDirectiveThirdParty contained upload_aggregate_form_field\nsyn keyword ngxDirectiveThirdParty contained upload_archive_elm\nsyn keyword ngxDirectiveThirdParty contained upload_archive_elm_separator\nsyn keyword ngxDirectiveThirdParty contained upload_archive_path\nsyn keyword ngxDirectiveThirdParty contained upload_archive_path_separator\nsyn keyword ngxDirectiveThirdParty contained upload_buffer_size\nsyn keyword ngxDirectiveThirdParty contained upload_cleanup\nsyn keyword ngxDirectiveThirdParty contained upload_content_type\nsyn keyword ngxDirectiveThirdParty contained upload_discard\nsyn keyword ngxDirectiveThirdParty contained upload_field_name\nsyn keyword ngxDirectiveThirdParty contained upload_file_crc32\nsyn keyword ngxDirectiveThirdParty contained upload_file_md5\nsyn keyword ngxDirectiveThirdParty contained upload_file_md5_uc\nsyn keyword ngxDirectiveThirdParty contained upload_file_name\nsyn keyword ngxDirectiveThirdParty contained upload_file_sha1\nsyn keyword ngxDirectiveThirdParty contained upload_file_sha1_uc\nsyn keyword ngxDirectiveThirdParty contained upload_file_size\nsyn keyword ngxDirectiveThirdParty contained upload_filter\nsyn keyword ngxDirectiveThirdParty contained upload_max_file_size\nsyn keyword ngxDirectiveThirdParty contained upload_max_output_body_len\nsyn keyword ngxDirectiveThirdParty contained upload_max_part_header_len\nsyn keyword ngxDirectiveThirdParty contained upload_pass\nsyn keyword ngxDirectiveThirdParty contained upload_pass_args\nsyn keyword ngxDirectiveThirdParty contained upload_pass_form_field\nsyn keyword ngxDirectiveThirdParty contained upload_set_form_field\nsyn keyword ngxDirectiveThirdParty contained upload_store\nsyn keyword ngxDirectiveThirdParty contained upload_store_access\nsyn keyword ngxDirectiveThirdParty contained upload_tmp_path\nsyn keyword ngxDirectiveThirdParty contained upload_unzip\nsyn keyword ngxDirectiveThirdParty contained upload_unzip_buffers\nsyn keyword ngxDirectiveThirdParty contained upload_unzip_hash\nsyn keyword ngxDirectiveThirdParty contained upload_unzip_max_file_name_len\nsyn keyword ngxDirectiveThirdParty contained upload_unzip_window\nsyn keyword ngxDirectiveThirdParty contained upload_void_content_type\n\n\" nginx-upload-progress-module\n\" https://github.com/masterzen/nginx-upload-progress-module\nsyn keyword ngxDirectiveThirdParty contained report_uploads\nsyn keyword ngxDirectiveThirdParty contained track_uploads\nsyn keyword ngxDirectiveThirdParty contained upload_progress\nsyn keyword ngxDirectiveThirdParty contained upload_progress_content_type\nsyn keyword ngxDirectiveThirdParty contained upload_progress_header\nsyn keyword ngxDirectiveThirdParty contained upload_progress_java_output\nsyn keyword ngxDirectiveThirdParty contained upload_progress_json_output\nsyn keyword ngxDirectiveThirdParty contained upload_progress_jsonp_output\nsyn keyword ngxDirectiveThirdParty contained upload_progress_jsonp_parameter\nsyn keyword ngxDirectiveThirdParty contained upload_progress_template\n\n\" Health checks upstreams for nginx\n\" https://github.com/yaoweibin/nginx_upstream_check_module\nsyn keyword ngxDirectiveThirdParty contained check\nsyn keyword ngxDirectiveThirdParty contained check_fastcgi_param\nsyn keyword ngxDirectiveThirdParty contained check_http_expect_alive\nsyn keyword ngxDirectiveThirdParty contained check_http_send\nsyn keyword ngxDirectiveThirdParty contained check_keepalive_requests\nsyn keyword ngxDirectiveThirdParty contained check_shm_size\nsyn keyword ngxDirectiveThirdParty contained check_status\n\n\" The fair load balancer module for nginx\n\" https://github.com/cryptofuture/nginx-upstream-fair\nsyn keyword ngxDirectiveThirdParty contained fair\nsyn keyword ngxDirectiveThirdParty contained upstream_fair_shm_size\n\n\" Nginx Video Thumb Extractor Module\n\" https://github.com/wandenberg/nginx-video-thumbextractor-module\nsyn keyword ngxDirectiveThirdParty contained video_thumbextractor\nsyn keyword ngxDirectiveThirdParty contained video_thumbextractor_image_height\nsyn keyword ngxDirectiveThirdParty contained video_thumbextractor_image_width\nsyn keyword ngxDirectiveThirdParty contained video_thumbextractor_jpeg_baseline\nsyn keyword ngxDirectiveThirdParty contained video_thumbextractor_jpeg_dpi\nsyn keyword ngxDirectiveThirdParty contained video_thumbextractor_jpeg_optimize\nsyn keyword ngxDirectiveThirdParty contained video_thumbextractor_jpeg_progressive_mode\nsyn keyword ngxDirectiveThirdParty contained video_thumbextractor_jpeg_quality\nsyn keyword ngxDirectiveThirdParty contained video_thumbextractor_jpeg_smooth\nsyn keyword ngxDirectiveThirdParty contained video_thumbextractor_next_time\nsyn keyword ngxDirectiveThirdParty contained video_thumbextractor_only_keyframe\nsyn keyword ngxDirectiveThirdParty contained video_thumbextractor_processes_per_worker\nsyn keyword ngxDirectiveThirdParty contained video_thumbextractor_threads\nsyn keyword ngxDirectiveThirdParty contained video_thumbextractor_tile_color\nsyn keyword ngxDirectiveThirdParty contained video_thumbextractor_tile_cols\nsyn keyword ngxDirectiveThirdParty contained video_thumbextractor_tile_margin\nsyn keyword ngxDirectiveThirdParty contained video_thumbextractor_tile_max_cols\nsyn keyword ngxDirectiveThirdParty contained video_thumbextractor_tile_max_rows\nsyn keyword ngxDirectiveThirdParty contained video_thumbextractor_tile_padding\nsyn keyword ngxDirectiveThirdParty contained video_thumbextractor_tile_rows\nsyn keyword ngxDirectiveThirdParty contained video_thumbextractor_tile_sample_interval\nsyn keyword ngxDirectiveThirdParty contained video_thumbextractor_video_filename\nsyn keyword ngxDirectiveThirdParty contained video_thumbextractor_video_second\n\n\" drizzle-nginx-module - Upstream module for talking to MySQL and Drizzle directly\n\" https://github.com/openresty/drizzle-nginx-module\nsyn keyword ngxDirectiveThirdParty contained drizzle_buffer_size\nsyn keyword ngxDirectiveThirdParty contained drizzle_connect_timeout\nsyn keyword ngxDirectiveThirdParty contained drizzle_dbname\nsyn keyword ngxDirectiveThirdParty contained drizzle_keepalive\nsyn keyword ngxDirectiveThirdParty contained drizzle_module_header\nsyn keyword ngxDirectiveThirdParty contained drizzle_pass\nsyn keyword ngxDirectiveThirdParty contained drizzle_query\nsyn keyword ngxDirectiveThirdParty contained drizzle_recv_cols_timeout\nsyn keyword ngxDirectiveThirdParty contained drizzle_recv_rows_timeout\nsyn keyword ngxDirectiveThirdParty contained drizzle_send_query_timeout\nsyn keyword ngxDirectiveThirdParty contained drizzle_server\nsyn keyword ngxDirectiveThirdParty contained drizzle_status\n\n\" ngx_dynamic_upstream\n\" https://github.com/cubicdaiya/ngx_dynamic_upstream\nsyn keyword ngxDirectiveThirdParty contained dynamic_upstream\n\n\" encrypt and decrypt nginx variable values\n\" https://github.com/openresty/encrypted-session-nginx-module\nsyn keyword ngxDirectiveThirdParty contained encrypted_session_expires\nsyn keyword ngxDirectiveThirdParty contained encrypted_session_iv\nsyn keyword ngxDirectiveThirdParty contained encrypted_session_key\nsyn keyword ngxDirectiveThirdParty contained set_decrypt_session\nsyn keyword ngxDirectiveThirdParty contained set_encrypt_session\n\n\" serve content directly from MongoDB's GridFS\n\" https://github.com/mdirolf/nginx-gridfs\nsyn keyword ngxDirectiveThirdParty contained gridfs\nsyn keyword ngxDirectiveThirdParty contained mongo\n\n\" Adds support for arithmetic operations to NGINX config\n\" https://github.com/arut/nginx-let-module\nsyn keyword ngxDirectiveThirdParty contained let\n\n\" ngx_http_lua_module - Embed the power of Lua into Nginx HTTP Servers\n\" https://github.com/openresty/lua-nginx-module\nsyn keyword ngxDirectiveThirdParty contained access_by_lua\nsyn keyword ngxDirectiveThirdParty contained access_by_lua_block\nsyn keyword ngxDirectiveThirdParty contained access_by_lua_file\nsyn keyword ngxDirectiveThirdParty contained access_by_lua_no_postpone\nsyn keyword ngxDirectiveThirdParty contained balancer_by_lua_block\nsyn keyword ngxDirectiveThirdParty contained balancer_by_lua_file\nsyn keyword ngxDirectiveThirdParty contained body_filter_by_lua\nsyn keyword ngxDirectiveThirdParty contained body_filter_by_lua_block\nsyn keyword ngxDirectiveThirdParty contained body_filter_by_lua_file\nsyn keyword ngxDirectiveThirdParty contained content_by_lua\nsyn keyword ngxDirectiveThirdParty contained content_by_lua_block\nsyn keyword ngxDirectiveThirdParty contained content_by_lua_file\nsyn keyword ngxDirectiveThirdParty contained header_filter_by_lua\nsyn keyword ngxDirectiveThirdParty contained header_filter_by_lua_block\nsyn keyword ngxDirectiveThirdParty contained header_filter_by_lua_file\nsyn keyword ngxDirectiveThirdParty contained init_by_lua\nsyn keyword ngxDirectiveThirdParty contained init_by_lua_block\nsyn keyword ngxDirectiveThirdParty contained init_by_lua_file\nsyn keyword ngxDirectiveThirdParty contained init_worker_by_lua\nsyn keyword ngxDirectiveThirdParty contained init_worker_by_lua_block\nsyn keyword ngxDirectiveThirdParty contained init_worker_by_lua_file\nsyn keyword ngxDirectiveThirdParty contained log_by_lua\nsyn keyword ngxDirectiveThirdParty contained log_by_lua_block\nsyn keyword ngxDirectiveThirdParty contained log_by_lua_file\nsyn keyword ngxDirectiveThirdParty contained lua_capture_error_log\nsyn keyword ngxDirectiveThirdParty contained lua_check_client_abort\nsyn keyword ngxDirectiveThirdParty contained lua_code_cache\nsyn keyword ngxDirectiveThirdParty contained lua_fake_shm\nsyn keyword ngxDirectiveThirdParty contained lua_http10_buffering\nsyn keyword ngxDirectiveThirdParty contained lua_load_resty_core\nsyn keyword ngxDirectiveThirdParty contained lua_malloc_trim\nsyn keyword ngxDirectiveThirdParty contained lua_max_pending_timers\nsyn keyword ngxDirectiveThirdParty contained lua_max_running_timers\nsyn keyword ngxDirectiveThirdParty contained lua_need_request_body\nsyn keyword ngxDirectiveThirdParty contained lua_package_cpath\nsyn keyword ngxDirectiveThirdParty contained lua_package_path\nsyn keyword ngxDirectiveThirdParty contained lua_regex_cache_max_entries\nsyn keyword ngxDirectiveThirdParty contained lua_regex_match_limit\nsyn keyword ngxDirectiveThirdParty contained lua_sa_restart\nsyn keyword ngxDirectiveThirdParty contained lua_shared_dict\nsyn keyword ngxDirectiveThirdParty contained lua_socket_buffer_size\nsyn keyword ngxDirectiveThirdParty contained lua_socket_connect_timeout\nsyn keyword ngxDirectiveThirdParty contained lua_socket_keepalive_timeout\nsyn keyword ngxDirectiveThirdParty contained lua_socket_log_errors\nsyn keyword ngxDirectiveThirdParty contained lua_socket_pool_size\nsyn keyword ngxDirectiveThirdParty contained lua_socket_read_timeout\nsyn keyword ngxDirectiveThirdParty contained lua_socket_send_lowat\nsyn keyword ngxDirectiveThirdParty contained lua_socket_send_timeout\nsyn keyword ngxDirectiveThirdParty contained lua_ssl_ciphers\nsyn keyword ngxDirectiveThirdParty contained lua_ssl_crl\nsyn keyword ngxDirectiveThirdParty contained lua_ssl_protocols\nsyn keyword ngxDirectiveThirdParty contained lua_ssl_trusted_certificate\nsyn keyword ngxDirectiveThirdParty contained lua_ssl_verify_depth\nsyn keyword ngxDirectiveThirdParty contained lua_transform_underscores_in_response_headers\nsyn keyword ngxDirectiveThirdParty contained lua_use_default_type\nsyn keyword ngxDirectiveThirdParty contained rewrite_by_lua\nsyn keyword ngxDirectiveThirdParty contained rewrite_by_lua_block\nsyn keyword ngxDirectiveThirdParty contained rewrite_by_lua_file\nsyn keyword ngxDirectiveThirdParty contained rewrite_by_lua_no_postpone\nsyn keyword ngxDirectiveThirdParty contained set_by_lua\nsyn keyword ngxDirectiveThirdParty contained set_by_lua_block\nsyn keyword ngxDirectiveThirdParty contained set_by_lua_file\nsyn keyword ngxDirectiveThirdParty contained ssl_certificate_by_lua_block\nsyn keyword ngxDirectiveThirdParty contained ssl_certificate_by_lua_file\nsyn keyword ngxDirectiveThirdParty contained ssl_session_fetch_by_lua_block\nsyn keyword ngxDirectiveThirdParty contained ssl_session_fetch_by_lua_file\nsyn keyword ngxDirectiveThirdParty contained ssl_session_store_by_lua_block\nsyn keyword ngxDirectiveThirdParty contained ssl_session_store_by_lua_file\n\n\" ngx_memc - An extended version of the standard memcached module\n\" https://github.com/openresty/memc-nginx-module\nsyn keyword ngxDirectiveThirdParty contained memc_buffer_size\nsyn keyword ngxDirectiveThirdParty contained memc_cmds_allowed\nsyn keyword ngxDirectiveThirdParty contained memc_connect_timeout\nsyn keyword ngxDirectiveThirdParty contained memc_flags_to_last_modified\nsyn keyword ngxDirectiveThirdParty contained memc_ignore_client_abort\nsyn keyword ngxDirectiveThirdParty contained memc_next_upstream\nsyn keyword ngxDirectiveThirdParty contained memc_pass\nsyn keyword ngxDirectiveThirdParty contained memc_read_timeout\nsyn keyword ngxDirectiveThirdParty contained memc_send_timeout\nsyn keyword ngxDirectiveThirdParty contained memc_upstream_fail_timeout\nsyn keyword ngxDirectiveThirdParty contained memc_upstream_max_fails\n\n\" ModSecurity web application firewall\n\" https://github.com/SpiderLabs/ModSecurity/tree/master\nsyn keyword ngxDirectiveThirdParty contained ModSecurityConfig\nsyn keyword ngxDirectiveThirdParty contained ModSecurityEnabled\nsyn keyword ngxDirectiveThirdParty contained pool_context_hash_size\n\n\" NAXSI is an open-source, high performance, low rules maintenance WAF for NGINX\n\" https://github.com/nbs-system/naxsi\nsyn keyword ngxDirectiveThirdParty contained BasicRule\nsyn keyword ngxDirectiveThirdParty contained CheckRule\nsyn keyword ngxDirectiveThirdParty contained DeniedUrl\nsyn keyword ngxDirectiveThirdParty contained LearningMode\nsyn keyword ngxDirectiveThirdParty contained LibInjectionSql\nsyn keyword ngxDirectiveThirdParty contained LibInjectionXss\nsyn keyword ngxDirectiveThirdParty contained MainRule\nsyn keyword ngxDirectiveThirdParty contained SecRulesDisabled\nsyn keyword ngxDirectiveThirdParty contained SecRulesEnabled\nsyn keyword ngxDirectiveThirdParty contained basic_rule\nsyn keyword ngxDirectiveThirdParty contained check_rule\nsyn keyword ngxDirectiveThirdParty contained denied_url\nsyn keyword ngxDirectiveThirdParty contained learning_mode\nsyn keyword ngxDirectiveThirdParty contained libinjection_sql\nsyn keyword ngxDirectiveThirdParty contained libinjection_xss\nsyn keyword ngxDirectiveThirdParty contained main_rule\nsyn keyword ngxDirectiveThirdParty contained rules_disabled\nsyn keyword ngxDirectiveThirdParty contained rules_enabled\n\n\" Phusion Passenger\n\" https://www.phusionpassenger.com/library/config/nginx/reference/\nsyn keyword ngxDirectiveThirdParty contained passenger_abort_on_startup_error\nsyn keyword ngxDirectiveThirdParty contained passenger_abort_websockets_on_process_shutdown\nsyn keyword ngxDirectiveThirdParty contained passenger_admin_panel_auth_type\nsyn keyword ngxDirectiveThirdParty contained passenger_admin_panel_password\nsyn keyword ngxDirectiveThirdParty contained passenger_admin_panel_url\nsyn keyword ngxDirectiveThirdParty contained passenger_admin_panel_username\nsyn keyword ngxDirectiveThirdParty contained passenger_anonymous_telemetry_proxy\nsyn keyword ngxDirectiveThirdParty contained passenger_app_env\nsyn keyword ngxDirectiveThirdParty contained passenger_app_file_descriptor_ulimit\nsyn keyword ngxDirectiveThirdParty contained passenger_app_group_name\nsyn keyword ngxDirectiveThirdParty contained passenger_app_log_file\nsyn keyword ngxDirectiveThirdParty contained passenger_app_rights\nsyn keyword ngxDirectiveThirdParty contained passenger_app_root\nsyn keyword ngxDirectiveThirdParty contained passenger_app_type\nsyn keyword ngxDirectiveThirdParty contained passenger_base_uri\nsyn keyword ngxDirectiveThirdParty contained passenger_buffer_response\nsyn keyword ngxDirectiveThirdParty contained passenger_buffer_size\nsyn keyword ngxDirectiveThirdParty contained passenger_buffers\nsyn keyword ngxDirectiveThirdParty contained passenger_busy_buffers_size\nsyn keyword ngxDirectiveThirdParty contained passenger_concurrency_model\nsyn keyword ngxDirectiveThirdParty contained passenger_core_file_descriptor_ulimit\nsyn keyword ngxDirectiveThirdParty contained passenger_ctl\nsyn keyword ngxDirectiveThirdParty contained passenger_data_buffer_dir\nsyn keyword ngxDirectiveThirdParty contained passenger_debugger\nsyn keyword ngxDirectiveThirdParty contained passenger_default_group\nsyn keyword ngxDirectiveThirdParty contained passenger_default_user\nsyn keyword ngxDirectiveThirdParty contained passenger_disable_anonymous_telemetry\nsyn keyword ngxDirectiveThirdParty contained passenger_disable_security_update_check\nsyn keyword ngxDirectiveThirdParty contained passenger_document_root\nsyn keyword ngxDirectiveThirdParty contained passenger_dump_config_manifest\nsyn keyword ngxDirectiveThirdParty contained passenger_enabled\nsyn keyword ngxDirectiveThirdParty contained passenger_env_var\nsyn keyword ngxDirectiveThirdParty contained passenger_file_descriptor_log_file\nsyn keyword ngxDirectiveThirdParty contained passenger_fly_with\nsyn keyword ngxDirectiveThirdParty contained passenger_force_max_concurrent_requests_per_process\nsyn keyword ngxDirectiveThirdParty contained passenger_friendly_error_pages\nsyn keyword ngxDirectiveThirdParty contained passenger_group\nsyn keyword ngxDirectiveThirdParty contained passenger_headers_hash_bucket_size\nsyn keyword ngxDirectiveThirdParty contained passenger_headers_hash_max_size\nsyn keyword ngxDirectiveThirdParty contained passenger_ignore_client_abort\nsyn keyword ngxDirectiveThirdParty contained passenger_ignore_headers\nsyn keyword ngxDirectiveThirdParty contained passenger_instance_registry_dir\nsyn keyword ngxDirectiveThirdParty contained passenger_intercept_errors\nsyn keyword ngxDirectiveThirdParty contained passenger_load_shell_envvars\nsyn keyword ngxDirectiveThirdParty contained passenger_log_file\nsyn keyword ngxDirectiveThirdParty contained passenger_log_level\nsyn keyword ngxDirectiveThirdParty contained passenger_max_instances\nsyn keyword ngxDirectiveThirdParty contained passenger_max_instances_per_app\nsyn keyword ngxDirectiveThirdParty contained passenger_max_pool_size\nsyn keyword ngxDirectiveThirdParty contained passenger_max_preloader_idle_time\nsyn keyword ngxDirectiveThirdParty contained passenger_max_request_queue_size\nsyn keyword ngxDirectiveThirdParty contained passenger_max_request_queue_time\nsyn keyword ngxDirectiveThirdParty contained passenger_max_request_time\nsyn keyword ngxDirectiveThirdParty contained passenger_max_requests\nsyn keyword ngxDirectiveThirdParty contained passenger_memory_limit\nsyn keyword ngxDirectiveThirdParty contained passenger_meteor_app_settings\nsyn keyword ngxDirectiveThirdParty contained passenger_min_instances\nsyn keyword ngxDirectiveThirdParty contained passenger_monitor_log_file\nsyn keyword ngxDirectiveThirdParty contained passenger_nodejs\nsyn keyword ngxDirectiveThirdParty contained passenger_pass_header\nsyn keyword ngxDirectiveThirdParty contained passenger_pool_idle_time\nsyn keyword ngxDirectiveThirdParty contained passenger_pre_start\nsyn keyword ngxDirectiveThirdParty contained passenger_python\nsyn keyword ngxDirectiveThirdParty contained passenger_read_timeout\nsyn keyword ngxDirectiveThirdParty contained passenger_request_queue_overflow_status_code\nsyn keyword ngxDirectiveThirdParty contained passenger_resist_deployment_errors\nsyn keyword ngxDirectiveThirdParty contained passenger_response_buffer_high_watermark\nsyn keyword ngxDirectiveThirdParty contained passenger_restart_dir\nsyn keyword ngxDirectiveThirdParty contained passenger_rolling_restarts\nsyn keyword ngxDirectiveThirdParty contained passenger_root\nsyn keyword ngxDirectiveThirdParty contained passenger_ruby\nsyn keyword ngxDirectiveThirdParty contained passenger_security_update_check_proxy\nsyn keyword ngxDirectiveThirdParty contained passenger_set_header\nsyn keyword ngxDirectiveThirdParty contained passenger_show_version_in_header\nsyn keyword ngxDirectiveThirdParty contained passenger_socket_backlog\nsyn keyword ngxDirectiveThirdParty contained passenger_spawn_method\nsyn keyword ngxDirectiveThirdParty contained passenger_start_timeout\nsyn keyword ngxDirectiveThirdParty contained passenger_startup_file\nsyn keyword ngxDirectiveThirdParty contained passenger_stat_throttle_rate\nsyn keyword ngxDirectiveThirdParty contained passenger_sticky_sessions\nsyn keyword ngxDirectiveThirdParty contained passenger_sticky_sessions_cookie_name\nsyn keyword ngxDirectiveThirdParty contained passenger_thread_count\nsyn keyword ngxDirectiveThirdParty contained passenger_turbocaching\nsyn keyword ngxDirectiveThirdParty contained passenger_user\nsyn keyword ngxDirectiveThirdParty contained passenger_user_switching\nsyn keyword ngxDirectiveThirdParty contained passenger_vary_turbocache_by_cookie\nsyn keyword ngxDirectiveThirdPartyDeprecated contained passenger_analytics_log_group\nsyn keyword ngxDirectiveThirdPartyDeprecated contained passenger_analytics_log_user\nsyn keyword ngxDirectiveThirdPartyDeprecated contained passenger_debug_log_file\nsyn keyword ngxDirectiveThirdPartyDeprecated contained passenger_use_global_queue\nsyn keyword ngxDirectiveThirdPartyDeprecated contained rack_env\nsyn keyword ngxDirectiveThirdPartyDeprecated contained rails_app_spawner_idle_time\nsyn keyword ngxDirectiveThirdPartyDeprecated contained rails_env\nsyn keyword ngxDirectiveThirdPartyDeprecated contained rails_framework_spawner_idle_time\nsyn keyword ngxDirectiveThirdPartyDeprecated contained rails_spawn_method\nsyn keyword ngxDirectiveThirdPartyDeprecated contained union_station_filter\nsyn keyword ngxDirectiveThirdPartyDeprecated contained union_station_gateway_address\nsyn keyword ngxDirectiveThirdPartyDeprecated contained union_station_gateway_cert\nsyn keyword ngxDirectiveThirdPartyDeprecated contained union_station_gateway_port\nsyn keyword ngxDirectiveThirdPartyDeprecated contained union_station_key\nsyn keyword ngxDirectiveThirdPartyDeprecated contained union_station_proxy_address\nsyn keyword ngxDirectiveThirdPartyDeprecated contained union_station_support\n\n\" ngx_postgres is an upstream module that allows nginx to communicate directly with PostgreSQL database\n\" https://github.com/FRiCKLE/ngx_postgres\nsyn keyword ngxDirectiveThirdParty contained postgres_connect_timeout\nsyn keyword ngxDirectiveThirdParty contained postgres_escape\nsyn keyword ngxDirectiveThirdParty contained postgres_keepalive\nsyn keyword ngxDirectiveThirdParty contained postgres_output\nsyn keyword ngxDirectiveThirdParty contained postgres_pass\nsyn keyword ngxDirectiveThirdParty contained postgres_query\nsyn keyword ngxDirectiveThirdParty contained postgres_result_timeout\nsyn keyword ngxDirectiveThirdParty contained postgres_rewrite\nsyn keyword ngxDirectiveThirdParty contained postgres_server\nsyn keyword ngxDirectiveThirdParty contained postgres_set\n\n\" ngx_rds_csv - Nginx output filter module to convert Resty-DBD-Streams (RDS) to Comma-Separated Values (CSV)\n\" https://github.com/openresty/rds-csv-nginx-module\nsyn keyword ngxDirectiveThirdParty contained rds_csv\nsyn keyword ngxDirectiveThirdParty contained rds_csv_buffer_size\nsyn keyword ngxDirectiveThirdParty contained rds_csv_content_type\nsyn keyword ngxDirectiveThirdParty contained rds_csv_field_name_header\nsyn keyword ngxDirectiveThirdParty contained rds_csv_field_separator\nsyn keyword ngxDirectiveThirdParty contained rds_csv_row_terminator\n\n\" ngx_rds_json - an output filter that formats Resty DBD Streams generated by ngx_drizzle and others to JSON\n\" https://github.com/openresty/rds-json-nginx-module\nsyn keyword ngxDirectiveThirdParty contained rds_json\nsyn keyword ngxDirectiveThirdParty contained rds_json_buffer_size\nsyn keyword ngxDirectiveThirdParty contained rds_json_content_type\nsyn keyword ngxDirectiveThirdParty contained rds_json_errcode_key\nsyn keyword ngxDirectiveThirdParty contained rds_json_errstr_key\nsyn keyword ngxDirectiveThirdParty contained rds_json_format\nsyn keyword ngxDirectiveThirdParty contained rds_json_ret\nsyn keyword ngxDirectiveThirdParty contained rds_json_root\nsyn keyword ngxDirectiveThirdParty contained rds_json_success_property\nsyn keyword ngxDirectiveThirdParty contained rds_json_user_property\n\n\" ngx_redis2 - Nginx upstream module for the Redis 2.0 protocol\n\" https://github.com/openresty/redis2-nginx-module\nsyn keyword ngxDirectiveThirdParty contained redis2_bind\nsyn keyword ngxDirectiveThirdParty contained redis2_buffer_size\nsyn keyword ngxDirectiveThirdParty contained redis2_connect_timeout\nsyn keyword ngxDirectiveThirdParty contained redis2_literal_raw_query\nsyn keyword ngxDirectiveThirdParty contained redis2_next_upstream\nsyn keyword ngxDirectiveThirdParty contained redis2_pass\nsyn keyword ngxDirectiveThirdParty contained redis2_query\nsyn keyword ngxDirectiveThirdParty contained redis2_raw_queries\nsyn keyword ngxDirectiveThirdParty contained redis2_raw_query\nsyn keyword ngxDirectiveThirdParty contained redis2_read_timeout\nsyn keyword ngxDirectiveThirdParty contained redis2_send_timeout\n\n\" NGINX-based Media Streaming Server\n\" https://github.com/arut/nginx-rtmp-module\nsyn keyword ngxDirectiveThirdParty contained ack_window\nsyn keyword ngxDirectiveThirdParty contained application\nsyn keyword ngxDirectiveThirdParty contained buffer\nsyn keyword ngxDirectiveThirdParty contained buflen\nsyn keyword ngxDirectiveThirdParty contained busy\nsyn keyword ngxDirectiveThirdParty contained chunk_size\nsyn keyword ngxDirectiveThirdParty contained dash\nsyn keyword ngxDirectiveThirdParty contained dash_cleanup\nsyn keyword ngxDirectiveThirdParty contained dash_fragment\nsyn keyword ngxDirectiveThirdParty contained dash_nested\nsyn keyword ngxDirectiveThirdParty contained dash_path\nsyn keyword ngxDirectiveThirdParty contained dash_playlist_length\nsyn keyword ngxDirectiveThirdParty contained drop_idle_publisher\nsyn keyword ngxDirectiveThirdParty contained exec\nsyn keyword ngxDirectiveThirdParty contained exec_block\nsyn keyword ngxDirectiveThirdParty contained exec_kill_signal\nsyn keyword ngxDirectiveThirdParty contained exec_options\nsyn keyword ngxDirectiveThirdParty contained exec_play\nsyn keyword ngxDirectiveThirdParty contained exec_play_done\nsyn keyword ngxDirectiveThirdParty contained exec_publish\nsyn keyword ngxDirectiveThirdParty contained exec_publish_done\nsyn keyword ngxDirectiveThirdParty contained exec_pull\nsyn keyword ngxDirectiveThirdParty contained exec_push\nsyn keyword ngxDirectiveThirdParty contained exec_record_done\nsyn keyword ngxDirectiveThirdParty contained exec_static\nsyn keyword ngxDirectiveThirdParty contained hls_audio_buffer_size\nsyn keyword ngxDirectiveThirdParty contained hls_base_url\nsyn keyword ngxDirectiveThirdParty contained hls_cleanup\nsyn keyword ngxDirectiveThirdParty contained hls_continuous\nsyn keyword ngxDirectiveThirdParty contained hls_fragment_naming\nsyn keyword ngxDirectiveThirdParty contained hls_fragment_naming_granularity\nsyn keyword ngxDirectiveThirdParty contained hls_fragment_slicing\nsyn keyword ngxDirectiveThirdParty contained hls_fragments_per_key\nsyn keyword ngxDirectiveThirdParty contained hls_key_path\nsyn keyword ngxDirectiveThirdParty contained hls_key_url\nsyn keyword ngxDirectiveThirdParty contained hls_keys\nsyn keyword ngxDirectiveThirdParty contained hls_max_audio_delay\nsyn keyword ngxDirectiveThirdParty contained hls_max_fragment\nsyn keyword ngxDirectiveThirdParty contained hls_muxdelay\nsyn keyword ngxDirectiveThirdParty contained hls_nested\nsyn keyword ngxDirectiveThirdParty contained hls_path\nsyn keyword ngxDirectiveThirdParty contained hls_playlist_length\nsyn keyword ngxDirectiveThirdParty contained hls_sync\nsyn keyword ngxDirectiveThirdParty contained hls_type\nsyn keyword ngxDirectiveThirdParty contained hls_variant\nsyn keyword ngxDirectiveThirdParty contained idle_streams\nsyn keyword ngxDirectiveThirdParty contained interleave\nsyn keyword ngxDirectiveThirdParty contained live\nsyn keyword ngxDirectiveThirdParty contained max_connections\nsyn keyword ngxDirectiveThirdParty contained max_message\nsyn keyword ngxDirectiveThirdParty contained max_streams\nsyn keyword ngxDirectiveThirdParty contained meta\nsyn keyword ngxDirectiveThirdParty contained netcall_buffer\nsyn keyword ngxDirectiveThirdParty contained netcall_timeout\nsyn keyword ngxDirectiveThirdParty contained notify_method\nsyn keyword ngxDirectiveThirdParty contained notify_relay_redirect\nsyn keyword ngxDirectiveThirdParty contained notify_update_strict\nsyn keyword ngxDirectiveThirdParty contained notify_update_timeout\nsyn keyword ngxDirectiveThirdParty contained on_connect\nsyn keyword ngxDirectiveThirdParty contained on_disconnect\nsyn keyword ngxDirectiveThirdParty contained on_done\nsyn keyword ngxDirectiveThirdParty contained on_play\nsyn keyword ngxDirectiveThirdParty contained on_play_done\nsyn keyword ngxDirectiveThirdParty contained on_publish\nsyn keyword ngxDirectiveThirdParty contained on_publish_done\nsyn keyword ngxDirectiveThirdParty contained on_record_done\nsyn keyword ngxDirectiveThirdParty contained on_update\nsyn keyword ngxDirectiveThirdParty contained out_cork\nsyn keyword ngxDirectiveThirdParty contained out_queue\nsyn keyword ngxDirectiveThirdParty contained ping\nsyn keyword ngxDirectiveThirdParty contained ping_timeout\nsyn keyword ngxDirectiveThirdParty contained play\nsyn keyword ngxDirectiveThirdParty contained play_local_path\nsyn keyword ngxDirectiveThirdParty contained play_restart\nsyn keyword ngxDirectiveThirdParty contained play_temp_path\nsyn keyword ngxDirectiveThirdParty contained play_time_fix\nsyn keyword ngxDirectiveThirdParty contained publish_notify\nsyn keyword ngxDirectiveThirdParty contained publish_time_fix\nsyn keyword ngxDirectiveThirdParty contained pull\nsyn keyword ngxDirectiveThirdParty contained pull_reconnect\nsyn keyword ngxDirectiveThirdParty contained push\nsyn keyword ngxDirectiveThirdParty contained push_reconnect\nsyn keyword ngxDirectiveThirdParty contained record\nsyn keyword ngxDirectiveThirdParty contained record_append\nsyn keyword ngxDirectiveThirdParty contained record_interval\nsyn keyword ngxDirectiveThirdParty contained record_lock\nsyn keyword ngxDirectiveThirdParty contained record_max_frames\nsyn keyword ngxDirectiveThirdParty contained record_max_size\nsyn keyword ngxDirectiveThirdParty contained record_notify\nsyn keyword ngxDirectiveThirdParty contained record_path\nsyn keyword ngxDirectiveThirdParty contained record_suffix\nsyn keyword ngxDirectiveThirdParty contained record_unique\nsyn keyword ngxDirectiveThirdParty contained recorder\nsyn keyword ngxDirectiveThirdParty contained relay_buffer\nsyn keyword ngxDirectiveThirdParty contained respawn\nsyn keyword ngxDirectiveThirdParty contained respawn_timeout\nsyn keyword ngxDirectiveThirdParty contained rtmp\nsyn keyword ngxDirectiveThirdParty contained rtmp_auto_push\nsyn keyword ngxDirectiveThirdParty contained rtmp_auto_push_reconnect\nsyn keyword ngxDirectiveThirdParty contained rtmp_control\nsyn keyword ngxDirectiveThirdParty contained rtmp_socket_dir\nsyn keyword ngxDirectiveThirdParty contained rtmp_stat\nsyn keyword ngxDirectiveThirdParty contained rtmp_stat_stylesheet\nsyn keyword ngxDirectiveThirdParty contained session_relay\nsyn keyword ngxDirectiveThirdParty contained so_keepalive\nsyn keyword ngxDirectiveThirdParty contained stream_buckets\nsyn keyword ngxDirectiveThirdParty contained sync\nsyn keyword ngxDirectiveThirdParty contained wait_key\nsyn keyword ngxDirectiveThirdParty contained wait_video\n\n\" ngx_set_misc - Various set_xxx directives added to nginx's rewrite module (md5/sha1, sql/json quoting, and many more)\n\" https://github.com/openresty/set-misc-nginx-module\nsyn keyword ngxDirectiveThirdParty contained set_base32_alphabet\nsyn keyword ngxDirectiveThirdParty contained set_base32_padding\nsyn keyword ngxDirectiveThirdParty contained set_decode_base32\nsyn keyword ngxDirectiveThirdParty contained set_decode_base64\nsyn keyword ngxDirectiveThirdParty contained set_decode_hex\nsyn keyword ngxDirectiveThirdParty contained set_encode_base32\nsyn keyword ngxDirectiveThirdParty contained set_encode_base64\nsyn keyword ngxDirectiveThirdParty contained set_encode_hex\nsyn keyword ngxDirectiveThirdParty contained set_escape_uri\nsyn keyword ngxDirectiveThirdParty contained set_formatted_gmt_time\nsyn keyword ngxDirectiveThirdParty contained set_formatted_local_time\nsyn keyword ngxDirectiveThirdParty contained set_hashed_upstream\nsyn keyword ngxDirectiveThirdParty contained set_hmac_sha1\nsyn keyword ngxDirectiveThirdParty contained set_if_empty\nsyn keyword ngxDirectiveThirdParty contained set_local_today\nsyn keyword ngxDirectiveThirdParty contained set_misc_base32_padding\nsyn keyword ngxDirectiveThirdParty contained set_quote_json_str\nsyn keyword ngxDirectiveThirdParty contained set_quote_pgsql_str\nsyn keyword ngxDirectiveThirdParty contained set_quote_sql_str\nsyn keyword ngxDirectiveThirdParty contained set_random\nsyn keyword ngxDirectiveThirdParty contained set_rotate\nsyn keyword ngxDirectiveThirdParty contained set_secure_random_alphanum\nsyn keyword ngxDirectiveThirdParty contained set_secure_random_lcalpha\nsyn keyword ngxDirectiveThirdParty contained set_unescape_uri\n\n\" nginx-sflow-module\n\" https://github.com/sflow/nginx-sflow-module\nsyn keyword ngxDirectiveThirdParty contained sflow\n\n\" Shibboleth auth request module for Nginx\n\" https://github.com/nginx-shib/nginx-http-shibboleth\nsyn keyword ngxDirectiveThirdParty contained shib_request\nsyn keyword ngxDirectiveThirdParty contained shib_request_set\nsyn keyword ngxDirectiveThirdParty contained shib_request_use_headers\n\n\" nginx module which adds ability to cache static files\n\" https://github.com/FRiCKLE/ngx_slowfs_cache\nsyn keyword ngxDirectiveThirdParty contained slowfs_big_file_size\nsyn keyword ngxDirectiveThirdParty contained slowfs_cache\nsyn keyword ngxDirectiveThirdParty contained slowfs_cache_key\nsyn keyword ngxDirectiveThirdParty contained slowfs_cache_min_uses\nsyn keyword ngxDirectiveThirdParty contained slowfs_cache_path\nsyn keyword ngxDirectiveThirdParty contained slowfs_cache_purge\nsyn keyword ngxDirectiveThirdParty contained slowfs_cache_valid\nsyn keyword ngxDirectiveThirdParty contained slowfs_temp_path\n\n\" Dynamic Image Transformation Module For nginx\n\" https://github.com/cubicdaiya/ngx_small_light\nsyn keyword ngxDirectiveThirdParty contained small_light\nsyn keyword ngxDirectiveThirdParty contained small_light_buffer\nsyn keyword ngxDirectiveThirdParty contained small_light_getparam_mode\nsyn keyword ngxDirectiveThirdParty contained small_light_imlib2_temp_dir\nsyn keyword ngxDirectiveThirdParty contained small_light_material_dir\nsyn keyword ngxDirectiveThirdParty contained small_light_pattern_define\nsyn keyword ngxDirectiveThirdParty contained small_light_radius_max\nsyn keyword ngxDirectiveThirdParty contained small_light_sigma_max\n\n\" ngx_srcache - Transparent subrequest-based caching layout for arbitrary nginx locations\n\" https://github.com/openresty/srcache-nginx-module\nsyn keyword ngxDirectiveThirdParty contained srcache_buffer\nsyn keyword ngxDirectiveThirdParty contained srcache_default_expire\nsyn keyword ngxDirectiveThirdParty contained srcache_fetch\nsyn keyword ngxDirectiveThirdParty contained srcache_fetch_skip\nsyn keyword ngxDirectiveThirdParty contained srcache_header_buffer_size\nsyn keyword ngxDirectiveThirdParty contained srcache_ignore_content_encoding\nsyn keyword ngxDirectiveThirdParty contained srcache_max_expire\nsyn keyword ngxDirectiveThirdParty contained srcache_methods\nsyn keyword ngxDirectiveThirdParty contained srcache_request_cache_control\nsyn keyword ngxDirectiveThirdParty contained srcache_response_cache_control\nsyn keyword ngxDirectiveThirdParty contained srcache_store\nsyn keyword ngxDirectiveThirdParty contained srcache_store_hide_header\nsyn keyword ngxDirectiveThirdParty contained srcache_store_max_size\nsyn keyword ngxDirectiveThirdParty contained srcache_store_no_cache\nsyn keyword ngxDirectiveThirdParty contained srcache_store_no_store\nsyn keyword ngxDirectiveThirdParty contained srcache_store_pass_header\nsyn keyword ngxDirectiveThirdParty contained srcache_store_private\nsyn keyword ngxDirectiveThirdParty contained srcache_store_ranges\nsyn keyword ngxDirectiveThirdParty contained srcache_store_skip\nsyn keyword ngxDirectiveThirdParty contained srcache_store_statuses\n\n\" NGINX-based VOD Packager\n\" https://github.com/kaltura/nginx-vod-module\nsyn keyword ngxDirectiveThirdParty contained vod\nsyn keyword ngxDirectiveThirdParty contained vod_align_segments_to_key_frames\nsyn keyword ngxDirectiveThirdParty contained vod_apply_dynamic_mapping\nsyn keyword ngxDirectiveThirdParty contained vod_base_url\nsyn keyword ngxDirectiveThirdParty contained vod_bootstrap_segment_durations\nsyn keyword ngxDirectiveThirdParty contained vod_cache_buffer_size\nsyn keyword ngxDirectiveThirdParty contained vod_clip_from_param_name\nsyn keyword ngxDirectiveThirdParty contained vod_clip_to_param_name\nsyn keyword ngxDirectiveThirdParty contained vod_drm_clear_lead_segment_count\nsyn keyword ngxDirectiveThirdParty contained vod_drm_enabled\nsyn keyword ngxDirectiveThirdParty contained vod_drm_info_cache\nsyn keyword ngxDirectiveThirdParty contained vod_drm_max_info_length\nsyn keyword ngxDirectiveThirdParty contained vod_drm_request_uri\nsyn keyword ngxDirectiveThirdParty contained vod_drm_single_key\nsyn keyword ngxDirectiveThirdParty contained vod_drm_upstream_location\nsyn keyword ngxDirectiveThirdParty contained vod_dynamic_clip_map_uri\nsyn keyword ngxDirectiveThirdParty contained vod_dynamic_mapping_cache\nsyn keyword ngxDirectiveThirdParty contained vod_encryption_iv_seed\nsyn keyword ngxDirectiveThirdParty contained vod_expires\nsyn keyword ngxDirectiveThirdParty contained vod_expires_live\nsyn keyword ngxDirectiveThirdParty contained vod_expires_live_time_dependent\nsyn keyword ngxDirectiveThirdParty contained vod_fallback_upstream_location\nsyn keyword ngxDirectiveThirdParty contained vod_force_continuous_timestamps\nsyn keyword ngxDirectiveThirdParty contained vod_force_playlist_type_vod\nsyn keyword ngxDirectiveThirdParty contained vod_force_sequence_index\nsyn keyword ngxDirectiveThirdParty contained vod_gop_look_ahead\nsyn keyword ngxDirectiveThirdParty contained vod_gop_look_behind\nsyn keyword ngxDirectiveThirdParty contained vod_ignore_edit_list\nsyn keyword ngxDirectiveThirdParty contained vod_initial_read_size\nsyn keyword ngxDirectiveThirdParty contained vod_lang_param_name\nsyn keyword ngxDirectiveThirdParty contained vod_last_modified\nsyn keyword ngxDirectiveThirdParty contained vod_last_modified_types\nsyn keyword ngxDirectiveThirdParty contained vod_live_mapping_cache\nsyn keyword ngxDirectiveThirdParty contained vod_live_response_cache\nsyn keyword ngxDirectiveThirdParty contained vod_live_window_duration\nsyn keyword ngxDirectiveThirdParty contained vod_manifest_duration_policy\nsyn keyword ngxDirectiveThirdParty contained vod_manifest_segment_durations_mode\nsyn keyword ngxDirectiveThirdParty contained vod_mapping_cache\nsyn keyword ngxDirectiveThirdParty contained vod_max_frames_size\nsyn keyword ngxDirectiveThirdParty contained vod_max_mapping_response_size\nsyn keyword ngxDirectiveThirdParty contained vod_max_metadata_size\nsyn keyword ngxDirectiveThirdParty contained vod_max_upstream_headers_size\nsyn keyword ngxDirectiveThirdParty contained vod_media_set_map_uri\nsyn keyword ngxDirectiveThirdParty contained vod_media_set_override_json\nsyn keyword ngxDirectiveThirdParty contained vod_metadata_cache\nsyn keyword ngxDirectiveThirdParty contained vod_min_single_nalu_per_frame_segment\nsyn keyword ngxDirectiveThirdParty contained vod_mode\nsyn keyword ngxDirectiveThirdParty contained vod_multi_uri_suffix\nsyn keyword ngxDirectiveThirdParty contained vod_notification_uri\nsyn keyword ngxDirectiveThirdParty contained vod_open_file_thread_pool\nsyn keyword ngxDirectiveThirdParty contained vod_output_buffer_pool\nsyn keyword ngxDirectiveThirdParty contained vod_parse_hdlr_name\nsyn keyword ngxDirectiveThirdParty contained vod_path_response_postfix\nsyn keyword ngxDirectiveThirdParty contained vod_path_response_prefix\nsyn keyword ngxDirectiveThirdParty contained vod_performance_counters\nsyn keyword ngxDirectiveThirdParty contained vod_proxy_header_name\nsyn keyword ngxDirectiveThirdParty contained vod_proxy_header_value\nsyn keyword ngxDirectiveThirdParty contained vod_redirect_segments_url\nsyn keyword ngxDirectiveThirdParty contained vod_remote_upstream_location\nsyn keyword ngxDirectiveThirdParty contained vod_response_cache\nsyn keyword ngxDirectiveThirdParty contained vod_secret_key\nsyn keyword ngxDirectiveThirdParty contained vod_segment_count_policy\nsyn keyword ngxDirectiveThirdParty contained vod_segment_duration\nsyn keyword ngxDirectiveThirdParty contained vod_segments_base_url\nsyn keyword ngxDirectiveThirdParty contained vod_source_clip_map_uri\nsyn keyword ngxDirectiveThirdParty contained vod_speed_param_name\nsyn keyword ngxDirectiveThirdParty contained vod_status\nsyn keyword ngxDirectiveThirdParty contained vod_time_shift_param_name\nsyn keyword ngxDirectiveThirdParty contained vod_tracks_param_name\nsyn keyword ngxDirectiveThirdParty contained vod_upstream_extra_args\nsyn keyword ngxDirectiveThirdParty contained vod_upstream_location\n\n\" Nginx virtual host traffic status module\n\" https://github.com/vozlt/nginx-module-vts\nsyn keyword ngxDirectiveThirdParty contained vhost_traffic_status\nsyn keyword ngxDirectiveThirdParty contained vhost_traffic_status_average_method\nsyn keyword ngxDirectiveThirdParty contained vhost_traffic_status_bypass_limit\nsyn keyword ngxDirectiveThirdParty contained vhost_traffic_status_bypass_stats\nsyn keyword ngxDirectiveThirdParty contained vhost_traffic_status_display\nsyn keyword ngxDirectiveThirdParty contained vhost_traffic_status_display_format\nsyn keyword ngxDirectiveThirdParty contained vhost_traffic_status_display_jsonp\nsyn keyword ngxDirectiveThirdParty contained vhost_traffic_status_display_sum_key\nsyn keyword ngxDirectiveThirdParty contained vhost_traffic_status_dump\nsyn keyword ngxDirectiveThirdParty contained vhost_traffic_status_filter\nsyn keyword ngxDirectiveThirdParty contained vhost_traffic_status_filter_by_host\nsyn keyword ngxDirectiveThirdParty contained vhost_traffic_status_filter_by_set_key\nsyn keyword ngxDirectiveThirdParty contained vhost_traffic_status_filter_check_duplicate\nsyn keyword ngxDirectiveThirdParty contained vhost_traffic_status_filter_max_node\nsyn keyword ngxDirectiveThirdParty contained vhost_traffic_status_histogram_buckets\nsyn keyword ngxDirectiveThirdParty contained vhost_traffic_status_limit\nsyn keyword ngxDirectiveThirdParty contained vhost_traffic_status_limit_check_duplicate\nsyn keyword ngxDirectiveThirdParty contained vhost_traffic_status_limit_traffic\nsyn keyword ngxDirectiveThirdParty contained vhost_traffic_status_limit_traffic_by_set_key\nsyn keyword ngxDirectiveThirdParty contained vhost_traffic_status_set_by_filter\nsyn keyword ngxDirectiveThirdParty contained vhost_traffic_status_zone\n\n\" xss-nginx-module - Native cross-site scripting support in nginx\n\" https://github.com/openresty/xss-nginx-module\nsyn keyword ngxDirectiveThirdParty contained xss_callback_arg\nsyn keyword ngxDirectiveThirdParty contained xss_check_status\nsyn keyword ngxDirectiveThirdParty contained xss_get\nsyn keyword ngxDirectiveThirdParty contained xss_input_types\nsyn keyword ngxDirectiveThirdParty contained xss_output_type\nsyn keyword ngxDirectiveThirdParty contained xss_override_status\n\n\" Add support for array-typed variables to nginx config files\n\" https://github.com/openresty/array-var-nginx-module\nsyn keyword ngxDirectiveThirdParty contained array_join\nsyn keyword ngxDirectiveThirdParty contained array_map\nsyn keyword ngxDirectiveThirdParty contained array_map_op\nsyn keyword ngxDirectiveThirdParty contained array_split\n\n\" NGINX module for Brotli compression\n\" https://github.com/eustas/ngx_brotli\nsyn keyword ngxDirectiveThirdParty contained brotli\nsyn keyword ngxDirectiveThirdParty contained brotli_buffers\nsyn keyword ngxDirectiveThirdParty contained brotli_comp_level\nsyn keyword ngxDirectiveThirdParty contained brotli_min_length\nsyn keyword ngxDirectiveThirdParty contained brotli_static\nsyn keyword ngxDirectiveThirdParty contained brotli_types\nsyn keyword ngxDirectiveThirdParty contained brotli_window\n\n\" form-input-nginx-module\n\" https://github.com/calio/form-input-nginx-module\nsyn keyword ngxDirectiveThirdParty contained set_form_input\nsyn keyword ngxDirectiveThirdParty contained set_form_input_multi\n\n\" character conversion nginx module using libiconv\n\" https://github.com/calio/iconv-nginx-module\nsyn keyword ngxDirectiveThirdParty contained iconv_buffer_size\nsyn keyword ngxDirectiveThirdParty contained iconv_filter\nsyn keyword ngxDirectiveThirdParty contained set_iconv\n\n\" 3rd party modules list taken from\n\" https://www.nginx.com/resources/wiki/modules/\n\" ---------------------------------------------\n\n\" Nginx Module for Authenticating Akamai G2O requests\n\" https://github.com/kaltura/nginx_mod_akamai_g2o\nsyn keyword ngxDirectiveThirdParty contained g2o\nsyn keyword ngxDirectiveThirdParty contained g2o_data_header\nsyn keyword ngxDirectiveThirdParty contained g2o_hash_function\nsyn keyword ngxDirectiveThirdParty contained g2o_key\nsyn keyword ngxDirectiveThirdParty contained g2o_log_level\nsyn keyword ngxDirectiveThirdParty contained g2o_nonce\nsyn keyword ngxDirectiveThirdParty contained g2o_sign_header\nsyn keyword ngxDirectiveThirdParty contained g2o_time_window\nsyn keyword ngxDirectiveThirdParty contained g2o_version\n\n\" nginx_lua_module\n\" https://github.com/alacner/nginx_lua_module\nsyn keyword ngxDirectiveThirdParty contained lua_file\n\n\" Nginx Audio Track for HTTP Live Streaming\n\" https://github.com/flavioribeiro/nginx-audio-track-for-hls-module\nsyn keyword ngxDirectiveThirdParty contained ngx_hls_audio_track\nsyn keyword ngxDirectiveThirdParty contained ngx_hls_audio_track_output_format\nsyn keyword ngxDirectiveThirdParty contained ngx_hls_audio_track_output_header\nsyn keyword ngxDirectiveThirdParty contained ngx_hls_audio_track_rootpath\n\n\" A Nginx module to dump backtrace when a worker process exits abnormally\n\" https://github.com/alibaba/nginx-backtrace\nsyn keyword ngxDirectiveThirdParty contained backtrace_log\nsyn keyword ngxDirectiveThirdParty contained backtrace_max_stack_size\n\n\" circle_gif module\n\" https://github.com/evanmiller/nginx_circle_gif\nsyn keyword ngxDirectiveThirdParty contained circle_gif\nsyn keyword ngxDirectiveThirdParty contained circle_gif_max_radius\nsyn keyword ngxDirectiveThirdParty contained circle_gif_min_radius\nsyn keyword ngxDirectiveThirdParty contained circle_gif_step_radius\n\n\" Upstream Consistent Hash\n\" https://github.com/replay/ngx_http_consistent_hash\nsyn keyword ngxDirectiveThirdParty contained consistent_hash\n\n\" Nginx module for etags on dynamic content\n\" https://github.com/kali/nginx-dynamic-etags\nsyn keyword ngxDirectiveThirdParty contained dynamic_etags\n\n\" Enhanced Nginx Memcached Module\n\" https://github.com/bpaquet/ngx_http_enhanced_memcached_module\nsyn keyword ngxDirectiveThirdParty contained enhanced_memcached_allow_delete\nsyn keyword ngxDirectiveThirdParty contained enhanced_memcached_allow_put\nsyn keyword ngxDirectiveThirdParty contained enhanced_memcached_bind\nsyn keyword ngxDirectiveThirdParty contained enhanced_memcached_buffer_size\nsyn keyword ngxDirectiveThirdParty contained enhanced_memcached_connect_timeout\nsyn keyword ngxDirectiveThirdParty contained enhanced_memcached_flush\nsyn keyword ngxDirectiveThirdParty contained enhanced_memcached_flush_namespace\nsyn keyword ngxDirectiveThirdParty contained enhanced_memcached_hash_keys_with_md5\nsyn keyword ngxDirectiveThirdParty contained enhanced_memcached_pass\nsyn keyword ngxDirectiveThirdParty contained enhanced_memcached_read_timeout\nsyn keyword ngxDirectiveThirdParty contained enhanced_memcached_send_timeout\nsyn keyword ngxDirectiveThirdParty contained enhanced_memcached_stats\n\n\" nginx max connections queue\n\" https://github.com/ezmobius/nginx-ey-balancer\nsyn keyword ngxDirectiveThirdParty contained max_connections_max_queue_length\nsyn keyword ngxDirectiveThirdParty contained max_connections_queue_timeout\n\n\" Nginx module for POST authentication and authorization\n\" https://github.com/veruu/ngx_form_auth\nsyn keyword ngxDirectiveThirdParty contained form_auth\nsyn keyword ngxDirectiveThirdParty contained form_auth_login\nsyn keyword ngxDirectiveThirdParty contained form_auth_pam_service\nsyn keyword ngxDirectiveThirdParty contained form_auth_password\nsyn keyword ngxDirectiveThirdParty contained form_auth_remote_user\n\n\" ngx_http_accounting_module\n\" https://github.com/Lax/ngx_http_accounting_module\nsyn keyword ngxDirectiveThirdParty contained accounting\nsyn keyword ngxDirectiveThirdParty contained accounting_id\nsyn keyword ngxDirectiveThirdParty contained accounting_interval\nsyn keyword ngxDirectiveThirdParty contained accounting_log\nsyn keyword ngxDirectiveThirdParty contained accounting_perturb\n\n\" concatenating files in a given context: CSS and JS files usually\n\" https://github.com/alibaba/nginx-http-concat\nsyn keyword ngxDirectiveThirdParty contained concat\nsyn keyword ngxDirectiveThirdParty contained concat_delimiter\nsyn keyword ngxDirectiveThirdParty contained concat_ignore_file_error\nsyn keyword ngxDirectiveThirdParty contained concat_max_files\nsyn keyword ngxDirectiveThirdParty contained concat_types\nsyn keyword ngxDirectiveThirdParty contained concat_unique\n\n\" update upstreams' config by restful interface\n\" https://github.com/yzprofile/ngx_http_dyups_module\nsyn keyword ngxDirectiveThirdParty contained dyups_interface\nsyn keyword ngxDirectiveThirdParty contained dyups_read_msg_log\nsyn keyword ngxDirectiveThirdParty contained dyups_read_msg_timeout\nsyn keyword ngxDirectiveThirdParty contained dyups_shm_zone_size\nsyn keyword ngxDirectiveThirdParty contained dyups_trylock\nsyn keyword ngxDirectiveThirdParty contained dyups_upstream_conf\n\n\" add given content to the end of the response according to the condition specified\n\" https://github.com/flygoast/ngx_http_footer_if_filter\nsyn keyword ngxDirectiveThirdParty contained footer_if\n\n\" NGINX HTTP Internal Redirect Module\n\" https://github.com/flygoast/ngx_http_internal_redirect\nsyn keyword ngxDirectiveThirdParty contained internal_redirect_if\nsyn keyword ngxDirectiveThirdParty contained internal_redirect_if_no_postpone\n\n\" nginx-ip-blocker\n\" https://github.com/tmthrgd/nginx-ip-blocker\nsyn keyword ngxDirectiveThirdParty contained ip_blocker\n\n\" IP2Location Nginx\n\" https://github.com/chrislim2888/ip2location-nginx\nsyn keyword ngxDirectiveThirdParty contained ip2location_database\n\n\" Limit upload rate\n\" https://github.com/cfsego/limit_upload_rate\nsyn keyword ngxDirectiveThirdParty contained limit_upload_rate\nsyn keyword ngxDirectiveThirdParty contained limit_upload_rate_after\nsyn keyword ngxDirectiveThirdParty contained limit_upload_rate_log_level\n\n\" limit the number of connections to upstream\n\" https://github.com/cfsego/nginx-limit-upstream\nsyn keyword ngxDirectiveThirdParty contained limit_upstream_conn\nsyn keyword ngxDirectiveThirdParty contained limit_upstream_log_level\nsyn keyword ngxDirectiveThirdParty contained limit_upstream_zone\n\n\" conditional accesslog for nginx\n\" https://github.com/cfsego/ngx_log_if\nsyn keyword ngxDirectiveThirdParty contained access_log_bypass_if\n\n\" log messages over ZeroMQ\n\" https://github.com/alticelabs/nginx-log-zmq\nsyn keyword ngxDirectiveThirdParty contained log_zmq_endpoint\nsyn keyword ngxDirectiveThirdParty contained log_zmq_format\nsyn keyword ngxDirectiveThirdParty contained log_zmq_off\nsyn keyword ngxDirectiveThirdParty contained log_zmq_server\n\n\" simple module to uppercase/lowercase strings in the nginx config\n\" https://github.com/replay/ngx_http_lower_upper_case\nsyn keyword ngxDirectiveThirdParty contained lower\nsyn keyword ngxDirectiveThirdParty contained upper\n\n\" content filter for nginx, which returns the md5 hash of the content otherwise returned\n\" https://github.com/kainswor/nginx_md5_filter\nsyn keyword ngxDirectiveThirdParty contained md5_filter\n\n\" Non-blocking upstream module for Nginx to connect to MongoDB\n\" https://github.com/simpl/ngx_mongo\nsyn keyword ngxDirectiveThirdParty contained mongo_auth\nsyn keyword ngxDirectiveThirdParty contained mongo_bind\nsyn keyword ngxDirectiveThirdParty contained mongo_buffer_size\nsyn keyword ngxDirectiveThirdParty contained mongo_buffering\nsyn keyword ngxDirectiveThirdParty contained mongo_buffers\nsyn keyword ngxDirectiveThirdParty contained mongo_busy_buffers_size\nsyn keyword ngxDirectiveThirdParty contained mongo_connect_timeout\nsyn keyword ngxDirectiveThirdParty contained mongo_json\nsyn keyword ngxDirectiveThirdParty contained mongo_next_upstream\nsyn keyword ngxDirectiveThirdParty contained mongo_pass\nsyn keyword ngxDirectiveThirdParty contained mongo_query\nsyn keyword ngxDirectiveThirdParty contained mongo_read_timeout\nsyn keyword ngxDirectiveThirdParty contained mongo_send_timeout\n\n\" Nginx OCSP processing module designed for response caching\n\" https://github.com/kyprizel/nginx_ocsp_proxy-module\nsyn keyword ngxDirectiveThirdParty contained ocsp_cache_timeout\nsyn keyword ngxDirectiveThirdParty contained ocsp_proxy\n\n\" Nginx OpenSSL version check at startup\n\" https://github.com/apcera/nginx-openssl-version\nsyn keyword ngxDirectiveThirdParty contained openssl_builddate_minimum\nsyn keyword ngxDirectiveThirdParty contained openssl_version_minimum\n\n\" Automatic PageSpeed optimization module for Nginx\n\" https://github.com/pagespeed/ngx_pagespeed\nsyn keyword ngxDirectiveThirdParty contained pagespeed\n\n\" PECL Memcache standard hashing compatible loadbalancer for Nginx\n\" https://github.com/replay/ngx_http_php_memcache_standard_balancer\nsyn keyword ngxDirectiveThirdParty contained hash_key\n\n\" nginx module to parse php sessions\n\" https://github.com/replay/ngx_http_php_session\nsyn keyword ngxDirectiveThirdParty contained php_session_parse\nsyn keyword ngxDirectiveThirdParty contained php_session_strip_formatting\n\n\" Nginx HTTP rDNS module\n\" https://github.com/flant/nginx-http-rdns\nsyn keyword ngxDirectiveThirdParty contained rdns\nsyn keyword ngxDirectiveThirdParty contained rdns_allow\nsyn keyword ngxDirectiveThirdParty contained rdns_deny\n\n\" Streaming regular expression replacement in response bodies\n\" https://github.com/openresty/replace-filter-nginx-module\nsyn keyword ngxDirectiveThirdParty contained replace_filter\nsyn keyword ngxDirectiveThirdParty contained replace_filter_last_modified\nsyn keyword ngxDirectiveThirdParty contained replace_filter_max_buffered_size\nsyn keyword ngxDirectiveThirdParty contained replace_filter_skip\nsyn keyword ngxDirectiveThirdParty contained replace_filter_types\n\n\" Link RRDtool's graphing facilities directly into nginx\n\" https://github.com/evanmiller/mod_rrd_graph\nsyn keyword ngxDirectiveThirdParty contained rrd_graph\nsyn keyword ngxDirectiveThirdParty contained rrd_graph_root\n\n\" Module for nginx to proxy rtmp using http protocol\n\" https://github.com/kwojtek/nginx-rtmpt-proxy-module\nsyn keyword ngxDirectiveThirdParty contained rtmpt_proxy\nsyn keyword ngxDirectiveThirdParty contained rtmpt_proxy_http_timeout\nsyn keyword ngxDirectiveThirdParty contained rtmpt_proxy_rtmp_timeout\nsyn keyword ngxDirectiveThirdParty contained rtmpt_proxy_stat\nsyn keyword ngxDirectiveThirdParty contained rtmpt_proxy_stylesheet\nsyn keyword ngxDirectiveThirdParty contained rtmpt_proxy_target\n\n\" Syntactically Awesome NGINX Module\n\" https://github.com/mneudert/sass-nginx-module\nsyn keyword ngxDirectiveThirdParty contained sass_compile\nsyn keyword ngxDirectiveThirdParty contained sass_error_log\nsyn keyword ngxDirectiveThirdParty contained sass_include_path\nsyn keyword ngxDirectiveThirdParty contained sass_indent\nsyn keyword ngxDirectiveThirdParty contained sass_is_indented_syntax\nsyn keyword ngxDirectiveThirdParty contained sass_linefeed\nsyn keyword ngxDirectiveThirdParty contained sass_output_style\nsyn keyword ngxDirectiveThirdParty contained sass_precision\nsyn keyword ngxDirectiveThirdParty contained sass_source_comments\nsyn keyword ngxDirectiveThirdParty contained sass_source_map_embed\n\n\" Nginx Selective Cache Purge Module\n\" https://github.com/wandenberg/nginx-selective-cache-purge-module\nsyn keyword ngxDirectiveThirdParty contained selective_cache_purge_query\nsyn keyword ngxDirectiveThirdParty contained selective_cache_purge_redis_database\nsyn keyword ngxDirectiveThirdParty contained selective_cache_purge_redis_host\nsyn keyword ngxDirectiveThirdParty contained selective_cache_purge_redis_password\nsyn keyword ngxDirectiveThirdParty contained selective_cache_purge_redis_port\nsyn keyword ngxDirectiveThirdParty contained selective_cache_purge_redis_unix_socket\n\n\" cconv nginx module\n\" https://github.com/liseen/set-cconv-nginx-module\nsyn keyword ngxDirectiveThirdParty contained set_cconv_to_simp\nsyn keyword ngxDirectiveThirdParty contained set_cconv_to_trad\nsyn keyword ngxDirectiveThirdParty contained set_pinyin_to_normal\n\n\" Nginx module that allows the setting of variables to the value of a variety of hashes\n\" https://github.com/simpl/ngx_http_set_hash\nsyn keyword ngxDirectiveThirdParty contained set_md5\nsyn keyword ngxDirectiveThirdParty contained set_md5_upper\nsyn keyword ngxDirectiveThirdParty contained set_murmur2\nsyn keyword ngxDirectiveThirdParty contained set_murmur2_upper\nsyn keyword ngxDirectiveThirdParty contained set_sha1\nsyn keyword ngxDirectiveThirdParty contained set_sha1_upper\n\n\" Nginx module to set the language of a request based on a number of options\n\" https://github.com/simpl/ngx_http_set_lang\nsyn keyword ngxDirectiveThirdParty contained lang_cookie\nsyn keyword ngxDirectiveThirdParty contained lang_get_var\nsyn keyword ngxDirectiveThirdParty contained lang_host\nsyn keyword ngxDirectiveThirdParty contained lang_list\nsyn keyword ngxDirectiveThirdParty contained lang_post_var\nsyn keyword ngxDirectiveThirdParty contained lang_referer\nsyn keyword ngxDirectiveThirdParty contained set_lang\nsyn keyword ngxDirectiveThirdParty contained set_lang_method\n\n\" Nginx Sorted Querystring Module\n\" https://github.com/wandenberg/nginx-sorted-querystring-module\nsyn keyword ngxDirectiveThirdParty contained sorted_querysting_filter_parameter\n\n\" Nginx upstream module for Sphinx 2.x search daemon\n\" https://github.com/reeteshranjan/sphinx2-nginx-module\nsyn keyword ngxDirectiveThirdParty contained sphinx2_bind\nsyn keyword ngxDirectiveThirdParty contained sphinx2_buffer_size\nsyn keyword ngxDirectiveThirdParty contained sphinx2_connect_timeout\nsyn keyword ngxDirectiveThirdParty contained sphinx2_next_upstream\nsyn keyword ngxDirectiveThirdParty contained sphinx2_pass\nsyn keyword ngxDirectiveThirdParty contained sphinx2_read_timeout\nsyn keyword ngxDirectiveThirdParty contained sphinx2_send_timeout\n\n\" Nginx module for retrieving user attributes and groups from SSSD\n\" https://github.com/veruu/ngx_sssd_info\nsyn keyword ngxDirectiveThirdParty contained sssd_info\nsyn keyword ngxDirectiveThirdParty contained sssd_info_attribute\nsyn keyword ngxDirectiveThirdParty contained sssd_info_attribute_separator\nsyn keyword ngxDirectiveThirdParty contained sssd_info_attributes\nsyn keyword ngxDirectiveThirdParty contained sssd_info_group\nsyn keyword ngxDirectiveThirdParty contained sssd_info_group_separator\nsyn keyword ngxDirectiveThirdParty contained sssd_info_groups\nsyn keyword ngxDirectiveThirdParty contained sssd_info_output_to\n\n\" An nginx module for sending statistics to statsd\n\" https://github.com/zebrafishlabs/nginx-statsd\nsyn keyword ngxDirectiveThirdParty contained statsd_count\nsyn keyword ngxDirectiveThirdParty contained statsd_sample_rate\nsyn keyword ngxDirectiveThirdParty contained statsd_server\nsyn keyword ngxDirectiveThirdParty contained statsd_timing\n\n\" ngx_stream_echo - TCP/stream echo module for NGINX (a port of the ngx_http_echo module)\n\" https://github.com/openresty/stream-echo-nginx-module\nsyn keyword ngxDirectiveThirdParty contained echo\nsyn keyword ngxDirectiveThirdParty contained echo_client_error_log_level\nsyn keyword ngxDirectiveThirdParty contained echo_discard_request\nsyn keyword ngxDirectiveThirdParty contained echo_duplicate\nsyn keyword ngxDirectiveThirdParty contained echo_flush_wait\nsyn keyword ngxDirectiveThirdParty contained echo_lingering_close\nsyn keyword ngxDirectiveThirdParty contained echo_lingering_time\nsyn keyword ngxDirectiveThirdParty contained echo_lingering_timeout\nsyn keyword ngxDirectiveThirdParty contained echo_read_buffer_size\nsyn keyword ngxDirectiveThirdParty contained echo_read_bytes\nsyn keyword ngxDirectiveThirdParty contained echo_read_line\nsyn keyword ngxDirectiveThirdParty contained echo_read_timeout\nsyn keyword ngxDirectiveThirdParty contained echo_request_data\nsyn keyword ngxDirectiveThirdParty contained echo_send_timeout\nsyn keyword ngxDirectiveThirdParty contained echo_sleep\n\n\" Embed the power of Lua into NGINX TCP/UDP servers\n\" https://github.com/openresty/stream-lua-nginx-module\nsyn keyword ngxDirectiveThirdParty contained lua_add_variable\nsyn keyword ngxDirectiveThirdParty contained preread_by_lua_block\nsyn keyword ngxDirectiveThirdParty contained preread_by_lua_file\nsyn keyword ngxDirectiveThirdParty contained preread_by_lua_no_postpone\n\n\" nginx-upsync-module\n\" https://github.com/weibocom/nginx-upsync-module\nsyn keyword ngxDirectiveThirdParty contained upstream_show\nsyn keyword ngxDirectiveThirdParty contained upsync\nsyn keyword ngxDirectiveThirdParty contained upsync_dump_path\nsyn keyword ngxDirectiveThirdParty contained upsync_lb\n\n\" Whitespace stripper for nginx\n\" https://github.com/evanmiller/mod_strip\nsyn keyword ngxDirectiveThirdParty contained strip\n\n\" Split one big HTTP/Range request to multiple subrange requesets\n\" https://github.com/Qihoo360/ngx_http_subrange_module\nsyn keyword ngxDirectiveThirdParty contained subrange\n\n\" summarizer-nginx-module\n\" https://github.com/reeteshranjan/summarizer-nginx-module\nsyn keyword ngxDirectiveThirdParty contained summarizer_bind\nsyn keyword ngxDirectiveThirdParty contained summarizer_buffer_size\nsyn keyword ngxDirectiveThirdParty contained summarizer_connect_timeout\nsyn keyword ngxDirectiveThirdParty contained summarizer_next_upstream\nsyn keyword ngxDirectiveThirdParty contained summarizer_pass\nsyn keyword ngxDirectiveThirdParty contained summarizer_read_timeout\nsyn keyword ngxDirectiveThirdParty contained summarizer_send_timeout\n\n\" nginx module providing API to communicate with supervisord and manage (start/stop) backends on-demand\n\" https://github.com/FRiCKLE/ngx_supervisord\nsyn keyword ngxDirectiveThirdParty contained supervisord\nsyn keyword ngxDirectiveThirdParty contained supervisord_inherit_backend_status\nsyn keyword ngxDirectiveThirdParty contained supervisord_name\nsyn keyword ngxDirectiveThirdParty contained supervisord_start\nsyn keyword ngxDirectiveThirdParty contained supervisord_stop\n\n\" simple robot mitigation module using cookie based challenge/response technique. Not supported any more.\n\" https://github.com/kyprizel/testcookie-nginx-module\nsyn keyword ngxDirectiveThirdParty contained testcookie\nsyn keyword ngxDirectiveThirdParty contained testcookie_arg\nsyn keyword ngxDirectiveThirdParty contained testcookie_deny_keepalive\nsyn keyword ngxDirectiveThirdParty contained testcookie_domain\nsyn keyword ngxDirectiveThirdParty contained testcookie_expires\nsyn keyword ngxDirectiveThirdParty contained testcookie_fallback\nsyn keyword ngxDirectiveThirdParty contained testcookie_get_only\nsyn keyword ngxDirectiveThirdParty contained testcookie_httponly_flag\nsyn keyword ngxDirectiveThirdParty contained testcookie_https_location\nsyn keyword ngxDirectiveThirdParty contained testcookie_internal\nsyn keyword ngxDirectiveThirdParty contained testcookie_max_attempts\nsyn keyword ngxDirectiveThirdParty contained testcookie_name\nsyn keyword ngxDirectiveThirdParty contained testcookie_p3p\nsyn keyword ngxDirectiveThirdParty contained testcookie_pass\nsyn keyword ngxDirectiveThirdParty contained testcookie_path\nsyn keyword ngxDirectiveThirdParty contained testcookie_port_in_redirect\nsyn keyword ngxDirectiveThirdParty contained testcookie_redirect_via_refresh\nsyn keyword ngxDirectiveThirdParty contained testcookie_refresh_encrypt_cookie\nsyn keyword ngxDirectiveThirdParty contained testcookie_refresh_encrypt_cookie_iv\nsyn keyword ngxDirectiveThirdParty contained testcookie_refresh_encrypt_cookie_key\nsyn keyword ngxDirectiveThirdParty contained testcookie_refresh_status\nsyn keyword ngxDirectiveThirdParty contained testcookie_refresh_template\nsyn keyword ngxDirectiveThirdParty contained testcookie_secret\nsyn keyword ngxDirectiveThirdParty contained testcookie_secure_flag\nsyn keyword ngxDirectiveThirdParty contained testcookie_session\nsyn keyword ngxDirectiveThirdParty contained testcookie_whitelist\n\n\" ngx_http_types_filter_module\n\" https://github.com/flygoast/ngx_http_types_filter\nsyn keyword ngxDirectiveThirdParty contained types_filter\nsyn keyword ngxDirectiveThirdParty contained types_filter_use_default\n\n\" A module allowing the nginx to use files embedded in a zip file\n\" https://github.com/youzee/nginx-unzip-module\nsyn keyword ngxDirectiveThirdParty contained file_in_unzip\nsyn keyword ngxDirectiveThirdParty contained file_in_unzip_archivefile\nsyn keyword ngxDirectiveThirdParty contained file_in_unzip_extract\n\n\" An asynchronous domain name resolve module for nginx upstream\n\" https://github.com/wdaike/ngx_upstream_jdomain\nsyn keyword ngxDirectiveThirdParty contained jdomain\n\n\" Nginx url encoding converting module\n\" https://github.com/vozlt/nginx-module-url\nsyn keyword ngxDirectiveThirdParty contained url_encoding_convert\nsyn keyword ngxDirectiveThirdParty contained url_encoding_convert_alloc_size\nsyn keyword ngxDirectiveThirdParty contained url_encoding_convert_alloc_size_x\nsyn keyword ngxDirectiveThirdParty contained url_encoding_convert_from\nsyn keyword ngxDirectiveThirdParty contained url_encoding_convert_phase\nsyn keyword ngxDirectiveThirdParty contained url_encoding_convert_to\n\n\" A nginx module to match browsers and crawlers\n\" https://github.com/alibaba/nginx-http-user-agent\nsyn keyword ngxDirectiveThirdParty contained user_agent\n\n\" nginx load-balancer module implementing ketama consistent hashing\n\" https://github.com/flygoast/ngx_http_upstream_ketama_chash\nsyn keyword ngxDirectiveThirdParty contained ketama_chash\n\n\n\n\n\" highlight\n\nhi link ngxComment Comment\nhi link ngxParamComment Comment\nhi link ngxListenComment Comment\nhi link ngxVariable Identifier\nhi link ngxVariableString PreProc\nhi link ngxString String\nhi link ngxListenString String\n\nhi link ngxBoolean Boolean\nhi link ngxDirectiveBlock Statement\nhi link ngxDirectiveImportant Type\nhi link ngxDirectiveListen Type\nhi link ngxDirectiveControl Keyword\nhi link ngxDirectiveError Constant\nhi link ngxDirectiveDeprecated Error\nhi link ngxDirective Identifier\nhi link ngxDirectiveThirdParty Special\nhi link ngxDirectiveThirdPartyDeprecated Error\n\nhi link ngxListenOptions Keyword\nhi link ngxListenOptionsDeprecated Error\n\nlet b:current_syntax = \"nginx\"\n"
  },
  {
    "path": "docs/core.md",
    "content": "# Core functionality\n\n\n## Directives\n\n### force_exit\n\nSyntax: **force_exit** exit_time;\n\nDefault: —\n\nContext: main\n\nforce worker processes to exit after exit_time.\n\nThe force_exit support is not enabled by default. You should compile it explicitly:\n\n```\n ./configure --with-force-exit\n```\n\nNote: Removed force_exit directive after the Tengine-2.3.0 version and use Nginx official `worker_shutdown_timeout` , detailed reference [worker_shutdown_timeout](http://nginx.org/en/docs/ngx_core_module.html#worker_shutdown_timeout)\n\n\n### worker_processes\n\nSyntax: **worker_processes** [num | auto]\n\nDefault: worker_processes auto\n\nContext: main\n\nSet the number of worker processes.\nWhen set to 'auto', which is also the default behavior, Tengine will create the same number of worker processes as your CPUs.\n\n\n### master_env\n\nSyntax: **master_env** variable[=value];\n\nDefault: -\n\nContext: main\n\nIf use `master_env` directive to set `NGX_DNS_RESOLVE_BACKUP_PATH` environment variable and dns cache will be enabled.\nWhen the dns server is unavailable, it's will use the last dns cache.\n\nFor example `master_env NGX_DNS_RESOLVE_BACKUP_PATH=/home/tengine/worker/dnscache/path`, the domain A record results will be saved to the file and path depends on  `NGX_DNS_RESOLVE_BACKUP_PATH`.\n\n### worker_cpu_affinity\n\nSyntax: **worker_cpu_affinity** [mask1 mask2 mask3 ... | auto | off]\nDefault: worker_cpu_affinity off\nContext: main\n\nBind worker processes to the sets of CPUs.\nWhen set to 'auto', Tengine will automatically bind each worker process to a specific CPU. If the number of worker processes is larger than the number of your CPUs, then the rest of worker processes will be bond in descendant order. For example, if there are 8 CPUs in your system: \n\n*   When the process number set to 4, the binding bitmap will be:\n\n    10000000 01000000 00100000 00010000\n*   When the process number set to 8, the binding bitmap will be:\n\n    10000000 01000000 00100000 00010000 00001000 00000100 00000010 00000001\n*   When the process number set to 10, the binding bitmap will be:\n\n    10000000 01000000 00100000 00010000 00001000 00000100 00000010 00000001 10000000 01000000\n\nWhen set to 'off', Tengine will disable the CPU affinity.\n\n\n### request_time_cache\n\nSyntax: **request_time_cache** [on | off]\n\nDefault: request_time_cache on\n\nContext: http, server, location\n\nWhen set to 'off', Tengine will get a more precise time on $request_time, $request_time_msec, $request_time_usec because it does not use time cache.\n\n\n### log_empty_request\n\nSyntax: **log_empty_request** [on | off]\n\nDefault: log_empty_request on\n\nContext: http, server, location\n\nWhen you specify it 'off', Tengine will not record any access log when a client issues a connection without any data being sent.\nBy default, it's on. In the above case, it will print a 400 Bad Request message into the access log.\n\n\n### server_admin\n\nSyntax: **server_admin** admin\n\nDefault: none\n\nContext: http, server, location\n\nSpecify the administrator's information, which will appear in a default 4xx/5xx error response when 'server_info' is turned on.\n\n\n### server_info\n\nSyntax: **server_info** on | off \n\nDefault: server_info on\n\nContext: http, server, location\n\nShow up the server information in a default 4xx/5xx error response. The URL accessed by the user, the hostname serving the request, and the time are included.\n\n\n### server_tag\n\nSyntax: **server_tag** off | customized_tag \n\nDefault: none\n\nContext: http, server, location\n\nSpecify the customized 'Server' header in the HTTP responses, for example, 'Apache/2.2.22', 'IIS 6.0', 'Lighttpd', etc. You could also suppress the 'Server' header by setting it to 'off'.\n\n\n### reuse_port\n\nSyntax: **reuse_port** on |  off\n\nDefault: reuse_port off\n\nContext: events\n\nturn on support for SO_REUSEPORT socket option. This option is supported since Linux 3.9.\n\nNote:\nRemoved reuse_port directive after the Tengine-2.3.0 version and use the official reuseport of Nginx, detailed reference [document](https://www.nginx.com/blog/socket-sharding-nginx-release-1-9-1/).\n\n### server_name\n\nSyntax: **server_name** name;\n\nDefault: —\n\nContext: server\n\n`server_name` used in Stream module makes Tengine have the ability to listen same ip:port in multiply server blocks and. The connection will be attached to a certain server block by SNI extension in TLS. That means `server_name` should be used with SSL offloading(using `ssl` after `listen`).\nThe `server_name` support in Stream module is not enabled by default. You should compile it explicitly:\n\n```\n ./configure --with-stream_sni\n```\nNote:\nThis feature is experimental. We will deprecate this feature if there is any conflict with similar feature of nginx official.\n\n### ssl_sni_force\n\nSyntax: **ssl_sni_force** on | off\n\nDefault: ssl_sni_force off\n\nContext: stream, server\n\n`ssl_sni_force` will determine whether the TLS handsheke is rejected or not if SNI is not matched with server name which we configure by `server_name` in Stream module.\n\nNote:\nSame note in `server_name` above.\n"
  },
  {
    "path": "docs/core_cn.md",
    "content": "# Core functionality\n\n## 指令\n\n### force_exit\n\nSyntax: **force_exit** exit_time;\n\nDefault: —\n\nContext: main\n\n强制worker进程在接受到QUIT信号后 exit_time 时间退出。\n\nforce_exit功能默认没有编译开启。需要编译时开启:\n\n```\n ./configure --with-force-exit\n```\n注意：Tengine-2.3.0 版本后废弃force_exit指令,使用Nginx官方`worker_shutdown_timeout`指令替代，详细[文档](http://nginx.org/en/docs/ngx_core_module.html#worker_shutdown_timeout)\n\n\n### worker_processes\n\nSyntax: **worker_processes** [num | auto]\n\nDefault: worker_processes auto\n\nContext: core\n\n为worker_processes增加参数auto。当设置成auto，tengine将自动启动与cpu数量相同的worker进程。\n\n\n\n### master_env\n\nSyntax: **master_env** variable[=value];\n\nDefault: -\n\nContext: core\n\n当使用`master_env`指令设置`NGX_DNS_RESOLVE_BACKUP_PATH`环境变量后将会开启dns缓存容灾逻辑。即当dns服务器不可用时，使用上次dns缓存的A记录。\n比如设置`master_env NGX_DNS_RESOLVE_BACKUP_PATH=/home/tengine/worker/dnscache/path;`将会把配置中的域名解析结果缓存到`NGX_DNS_RESOLVE_BACKUP_PATH`所设置的路径下。\n\n\n### worker_cpu_affinity\n\nSyntax: **worker_cpu_affinity** [mask1 mask2 mask3 ... | auto | off ]\n\nDefault: worker_cpu_affinity off\n\nContext: core\n\n为worker_cpu_affinity增加参数auto和off。当设置成auto时，tengine将根据worker的数量自动配置cpu绑定位图。绑定的顺序是按CPU编号从大到小。\n如果worker数量大于cpu数量，则剩余的worker进程将按照CPU编号从大到小的顺序从编号最大的CPU开始再次绑定。例如：某CPU有8核，\n\n*   worker数量是4，则自动配置的绑定位图是10000000, 01000000, 00100000, 00010000\n*   worker数量是8，则自动配置的绑定位图是10000000, 01000000, 00100000, 00010000, 00001000, 00000100, 00000010, 00000001\n*   worker数量是10，则自动配置的绑定位图是10000000, 01000000, 00100000, 00010000, 00001000, 00000100, 00000010, 00000001, 10000000, 01000000\n\n当设置成off时，tengine不会进行cpu绑定。\n\nworker_cpu_affinity的error log最多显示64个CPU的绑定情况。\n\n\n### msie_padding\n\nSyntax: **msie_padding** [on | off]\n\nDefault: msie_padding off\n\nContext: http, server, location\n\n此指令关闭或开启MSIE浏览器的msie_padding特性，若启用选项，nginx会为response头部填满512字节，这样就阻止了相关浏览器会激活友好错误界面，因此不会隐藏更多的错误信息。Tengine中默认关闭此功能。\n\n\n### request_time_cache\n\nSyntax: **request_time_cache** [on | off]\n\nDefault: request_time_cache on\n\nContext: http, server, location\n\n设置成'off'时，Tengine将不使用时间缓存，$request_time、$request_time_msec和$request_time_usec将会得到更精确的时间。\n\n\n### log_empty_request\n\nSyntax: **log_empty_request** [on | off]\n\nDefault: log_empty_request on\n\nContext: http, server, location\n\n设置成'off'时，Tengine将不会记录没有发送任何数据的访问日志。默认情况下，Tengine会在访问日志里面记录一条400状态的日志。\n\n\n### server_admin\n\nSyntax: **server_admin** admin\n\nDefault: none\n\nContext: http, server, location\n\n设置网站管理员信息，当打开server_info的时候，显示错误页面时会显示该信息。\n\n\n### server_info\n\nSyntax: **server_info** on | off \n\nDefault: server_info on\n\nContext: http, server, location\n\n当打开server_info的时候，显示错误页面时会显示URL、服务器名称和出错时间。\n\n\n### server_tag\n\nSyntax: **server_tag** off | customized_tag \n\nDefault: none\n\nContext: http, server, location\n\n自定义设置HTTP响应的server头，‘off’可以禁止返回server头。如果什么都不设置，就是返回默认Nginx的标识。\n\n\n### reuse_port\n\nSyntax: **reuse_port** on |  off\n\nDefault: reuse_port off\n\nContext: events\n\n当打开reuse_port的时候，支持SO_REUSEPORT套接字参数，Linux从3.9开始支持。\n\n注意：Tengine-2.3.0 版本后废弃reuse_port指令，使用Nginx官方的reuseport。升级方法：将events配置块里面的reuse_port on|off 释掉，在对应的监听端口后面加reuseport参数、详细参考[文档](https://www.nginx.com/blog/socket-sharding-nginx-release-1-9-1/) 。\n\n### server_name\n\nSyntax: **server_name** name;\n\nDefault: —\n\nContext: server\n\n在Stream模块中，`server_name` 可以用来允许多个server块监听同一个ip:port。Tengine会根据TLS的SNI来决定请求连接匹配到哪个server块。这意味着，Stream模块的`server_name`必须用在SSL卸载的情况下（即`listen`指令后面有`ssl`这个参数）。\n\nStream模块中的`server_name` 默认是不开启的. 你需要这么显示的编译:\n\n```\n ./configure --with-stream_sni\n```\n注意:\n这个特性是实验性的。如果Nginx官方有类似的功能和该功能有冲突，那么改功能将被废弃。\n\n### ssl_sni_force\n\nSyntax: **ssl_sni_force** on | off\n\nDefault: ssl_sni_force off\n\nContext: stream, server\n\n在Stream模块中，`ssl_sni_force`决定了如果TLS的SNI和配置的`server_name`不匹配，TLS握手是否被拒绝。\n\n注意:\n详见`server_name`的注意点.\n"
  },
  {
    "path": "docs/modules/TFS_RESTful_API.md",
    "content": "#TFS RESTful API\n\nNOTE: <b>bold</b> words are fixed and reserved for API, <i>italics</i> words are input arguments.\n\n##Raw TFS\n\n###WRITE\n\n####Description\n\nThe implementation of WRITE operation stores data as a TFS file. File name is returned in JSON format.\n\n####Syntax\n\n>POST /<b>v1</b>/<i>appkey</i> HTTP/1.1\n\n>Host: <i>10.0.0.1:7500</i>\n\n>Content-Length: <i>length</i>\n\n>Date: <i>date</i>\n\n<i>appkey</i> is an id of an application configured in RcServer database. If you are not using RcServer, use <b>tfs</b> as appkey.\n\n####Request Parameters\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>parameter</th>\n\t\t<th>description</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>suffix</td>\n\t\t<td>file suffix</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>simple_name</td>\n\t\t<td>whether require the right suffix to access the file<br>1: require the right suffix to access<br>0: no such restrict</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>large_file</td>\n\t\t<td>whether save as large file(file name will start with 'L')<br>1: save as large file<br>0: do not save as large file</td>\n\t</tr>\n</table>\n\n####Response\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>name</th>\n\t\t<th>description</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>TFS_FILE_NAME</td>\n\t\t<td>TFS file name returned</td>\n\t</tr>\n</table>\n\n####Status code\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>HTTP status code</th>\n\t\t<th>description</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>200 OK</td>\n\t\t<td>operation success</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>400 Bad Request</td>\n\t\t<td>bad request</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>500 Internal Server Error</td>\n\t\t<td>internal server error in tfs or nginx server</td>\n\t</tr>\n</table>\n\n####Examples\n\nThe following request will use tfs as appkey to write a TFS file without suffix:\n<pre>\nPOST /v1/tfs HTTP/1.1\nHost: 10.0.0.1:7500\nContent-Length: 22\nDate: Fri, 30 Nov 2012 03:05:00 GMT\n\n[data]\n</pre>\n\nThe corresponding response will be:\n\n<pre>\nHTTP/1.1 200 OK\nServer: Tengine/1.3.0\nDate: Fri, 30 Nov 2012 03:05:00 GMT\nContent-Type: application/json\nTransfer-Encoding: chunked\nConnection: keep-alive\n\n{\n\t\"TFS_FILE_NAME\": \"T1FOZHB4ET1RCvBVdK\"\n}\n</pre>\n\nThe following request will use tfs as appkey to write a TFS file with \".jpg\" as its suffix. Access with this suffix is required.\n\n<pre>\nPOST /v1/tfs?suffix=.jpg&simple_name=1 HTTP/1.1\nHost: 10.0.0.1:7500\nContent-Length: 22\nDate: Fri, 30 Nov 2012 03:05:00 GMT\n\n[data]\n</pre>\n\nThe corresponding response will be:\n\n<pre>\nHTTP/1.1 200 OK\nServer: Tengine/1.3.0\nDate: Fri, 30 Nov 2012 03:05:00 GMT\nContent-Type: application/json\nTransfer-Encoding: chunked\nConnection: keep-alive\n\n{\n\t\"TFS_FILE_NAME\": \"T1FOZHB4ET1RCvBVdK.jpg\"\n}\n</pre>\n\n###UPDATE\n\n####Description\n\nThe implementation of UPDATE updates a existing TFS file. Also a TFS file name will returned in JSON format.\n\n####Syntax\n\n>PUT /<b>v1</b>/<i>appkey</i>/<i>TfsFileName</i> HTTP/1.1\n\n>Host: <i>10.0.0.1:7500</i>\n\n>Content-Length: <i>length</i>\n\n>Date: <i>date</i>\n\n<i>appkey</i> is an id of an application configured in RcServer database. If you are not using RcServer, use <b>tfs</b> as appkey.\n\n####Request Parameters\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>parameter</th>\n\t\t<th>description</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>suffix</td>\n\t\t<td>file suffix</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>simple_name</td>\n\t\t<td>whether require the right suffix to access the file<br>1: require the right suffix to access<br>0: no such restrict</td>\n\t</tr>\n</table>\n\n####Response\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>name</th>\n\t\t<th>description</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>TFS_FILE_NAME</td>\n\t\t<td>TFS file name returned</td>\n\t</tr>\n</table>\n\n####Status code\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>HTTP status code</th>\n\t\t<th>description</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>200 OK</td>\n\t\t<td>operation success</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>400 Bad Request</td>\n\t\t<td>bad request</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>500 Internal Server Error</td>\n\t\t<td>internal server error in tfs or nginx server</td>\n\t</tr>\n</table>\n\n####Examples\n\nThe following request will use tfs as appkey to update the TFS file T1FOZHB4ET1RCvBVdK:\n<pre>\nPUT /v1/tfs/T1FOZHB4ET1RCvBVdK HTTP/1.1\nHost: 10.0.0.1:7500\nContent-Length: 22\nDate: Fri, 30 Nov 2012 03:05:00 GMT\n\n[data]\n</pre>\n\nThe corresponding response will be:\n\n<pre>\nHTTP/1.1 200 OK\nServer: Tengine/1.3.0\nDate: Fri, 30 Nov 2012 03:05:00 GMT\nContent-Type: application/json\nTransfer-Encoding: chunked\nConnection: keep-alive\n\n{\n\t\"TFS_FILE_NAME\": \"T1FOZHB4ET1RCvBVdK\"\n}\n</pre>\n\n###READ\n\n####Description\n\nThe implementation of READ reads data from a TFS file.\n\n####Syntax\n>GET /<b>v1</b>/<i>appkey</i>/<i>TfsFileName</i> HTTP/1.1\n\n>Host: <i>10.0.0.1:7500</i>\n\n>Date: <i>date</i>\n\n<i>appkey</i> is an id of an application configured in RcServer database. If you are not using RcServer, use <b>tfs</b> as appkey.\n\n####Request Parameters\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>parameter</th>\n\t\t<th>description</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>suffix</td>\n\t\t<td>file suffix<br>NOTE: If this parameter is given, and there is another different suffix in TfsFileName, then the access will fail.</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>offset</td>\n\t\t<td>offset to read in the file</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>size</td>\n\t\t<td>size to read</td>\n\t</tr>\n</table>\n\n\n####Response\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>name</th>\n\t\t<th>description</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>data</td>\n\t\t<td>data readed</td>\n\t</tr>\n</table>\n\n####Status code\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>HTTP status code</th>\n\t\t<th>description</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>200 OK</td>\n\t\t<td>operation success</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>400 Bad Request</td>\n\t\t<td>bad request</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>404 Not Found</td>\n\t\t<td>file not found</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>500 Internal Server Error</td>\n\t\t<td>internal server error in tfs or nginx server</td>\n\t</tr>\n</table>\n\n####Examples\n\nThe following request will use tfs as appkey to read file T1FOZHB4ET1RCvBVdK:\n\n<pre>\nGET /v1/tfs/T1FOZHB4ET1RCvBVdK HTTP/1.1\nHost: 10.0.0.1:7500\nDate: Fri, 30 Nov 2012 03:05:00 GMT\n</pre>\n\nThe corresponding response will be:\n\n<pre>\nHTTP/1.1 200 OK\nServer: Tengine/1.3.0\nDate: Fri, 30 Nov 2012 03:05:00 GMT\nLast-Modified: Thu, 29 Nov 2012 03:05:00 GMT\nTransfer-Encoding: chunked\nConnection: keep-alive\n\n[data]\n</pre>\n\nThe following request will use tfs as appkey to read file T1FOZHB4ET1RCvBVdK.jpg:\n\n<pre>\nGET /v1/tfs/T1FOZHB4ET1RCvBVdK.jpg HTTP/1.1\nHost: 10.0.0.1:7500\nDate: Fri, 30 Nov 2012 03:05:00 GMT\n</pre>\n\nThe corresponding response will be:\n\n<pre>\nHTTP/1.1 200 OK\nServer: Tengine/1.3.0\nDate: Fri, 30 Nov 2012 03:05:00 GMT\nLast-Modified: Thu, 29 Nov 2012 03:05:00 GMT\nTransfer-Encoding: chunked\nConnection: keep-alive\n\n[data]\n</pre>\n\n###DELETE\n\n####Description\n\nThe implementation of DELETE deletes or conceal a TFS file.\n\n####Syntax\n\n>DELETE /<b>v1</b>/<i>appkey</i>/<i>TfsFileName</i> HTTP/1.1\n\n>Host: <i>10.0.0.1:7500</i>\n\n>Date: <i>date</i>\n\n<i>appkey</i> is an id of an application configured in RcServer database. If you are not using RcServer, use <b>tfs</b> as appkey.\n\n####Request Parameters\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>parameter</th>\n\t\t<th>description</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>suffix</td>\n\t\t<td>file suffix<br>NOTE: If this parameter is given, and there is another different suffix in TfsFileName, then the access will fail.</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>hide</td>\n\t\t<td>specify the operation of conceal:<br>1: conceal<br>0: reveal</td>\n\t</tr>\n</table>\n\n####Response\n\nNo\n\n####Status code\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>HTTP status code</th>\n\t\t<th>description</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>200 OK</td>\n\t\t<td>operation success</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>400 Bad Request</td>\n\t\t<td>bad request</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>404 Not Found</td>\n\t\t<td>file not found</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>500 Internal Server Error</td>\n\t\t<td>internal server error in tfs or nginx server</td>\n\t</tr>\n</table>\n\n####Examples\n\nThe following request will use tfs as appkey to delete file T1FOZHB4ET1RCvBVdK:\n\n<pre>\nDELETE /v1/tfs/T1FOZHB4ET1RCvBVdK HTTP/1.1\nHost: 10.0.0.1:7500\nDate: Fri, 30 Nov 2012 03:05:00 GMT\n</pre>\n\nThe corresponding response will be:\n\n<pre>\nHTTP/1.1 200 OK\nServer: Tengine/1.3.0\nDate: Fri, 30 Nov 2012 03:05:00 GMT\nContent-Length: 0\nConnection: keep-alive\n</pre>\n\nThe following request will use tfs as appkey to conceal file T1FOZHB4ET1RCvBVdK.jpg:\n\n<pre>\nDELETE /v1/tfs/T1FOZHB4ET1RCvBVdK.jpg?hide=1 HTTP/1.1\nHost: 10.0.0.1:7500\nDate: Fri, 30 Nov 2012 03:05:00 GMT\n</pre>\n\nThe corresponding response will be:\n\n<pre>\nHTTP/1.1 200 OK\nServer: Tengine/1.3.0\nDate: Fri, 30 Nov 2012 03:05:00 GMT\nContent-Length: 0\nConnection: keep-alive\n</pre>\n\n###STAT\n\n####Description\n\nThe implementation of STAT achieve the meta data of a TFS file. The meta data will be returned in JSON format.\n\n####Syntax\n\n>GET /<b>v1</b>/<i>appkey</i>/<b>metadata</b>/<i>TfsFileName</i> HTTP/1.1\n\n>Host: <i>10.0.0.1:7500</i>\n\n>Date: <i>date</i>\n\n<i>appkey</i> is an id of an application configured in RcServer database. If you are not using RcServer, use <b>tfs</b> as appkey.\n\n####Request Parameters\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>parameter</th>\n\t\t<th>description</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>suffix</td>\n\t\t<td>file suffix<br>NOTE: If this parameter is given, and there is another different suffix in TfsFileName, then the access will fail.</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>type</td>\n\t\t<td>achieve type:<br>0: normal, will fail if file is deleted or concealed.<br>1: force, will success even if file is deleted or concealed.</td>\n\t</tr>\n</table>\n\n####Response\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>name</th>\n\t\t<th>description</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>FILE_NAME</td>\n\t\t<td>file name</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>BLOCK_ID</td>\n\t\t<td>id of the block that contains this file</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>FILE_ID</td>\n\t\t<td>file id</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>OFFSET</td>\n\t\t<td>file offset inside the block</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>SIZE</td>\n\t\t<td>file size</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>OCCUPY_SIZE</td>\n\t\t<td>occupy size of file</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>MODIFY_TIME</td>\n\t\t<td>modify time</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>CREATE_TIME</td>\n\t\t<td>create time</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>STATUS</td>\n\t\t<td>status of file<br>0: normal<br>1: deleted<br>4: concealed<br>5: concealed and deleted</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>CRC</td>\n\t\t<td>crc of file data</td>\n\t</tr>\n</table>\n\n####Status code\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>HTTP status code</th>\n\t\t<th>description</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>200 OK</td>\n\t\t<td>operation success</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>400 Bad Request</td>\n\t\t<td>bad request</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>404 Not Found</td>\n\t\t<td>file not found</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>500 Internal Server Error</td>\n\t\t<td>internal server error in tfs or nginx server</td>\n\t</tr>\n</table>\n\n####Examples\n\nThe following request will use tfs as appkey to stat file T1FOZHB4ET1RCvBVdK:\n\n<pre>\nGET /v1/tfs/metadata/T1FOZHB4ET1RCvBVdK HTTP/1.1\nHost: 10.0.0.1:7500\nDate: Fri, 30 Nov 2012 03:05:00 GMT\n</pre>\n\nThe corresponding response will be:\n\n<pre>\nHTTP/1.1 200 OK\nServer: Tengine/1.3.0\nDate: Fri, 30 Nov 2012 03:05:00 GMT\nContent-Type: application/json\nTransfer-Encoding: chunked\nConnection: keep-alive\n\n{\n    \"FILE_NAME\": \"T1FOZHB4ET1RCvBVdK\",\n    \"BLOCK_ID\": 101,\n    \"FILE_ID\": 9223190836479524436,\n    \"OFFSET\": 69563585,\n    \"SIZE\": 103578,\n    \"OCCUPY_SIZE\": 103614,\n    \"MODIFY_TIME\": \"Fri, 09 Mar 2012 13:40:32 UTC+0800\",\n    \"CREATE_TIME\": \"Fri, 09 Mar 2012 13:40:32 UTC+0800\",\n    \"STATUS\": 0,\n    \"CRC\": 3208008078\n}\n</pre>\n\n##Custom TFS\n\n###GET_APPID\n\n####Description\n\nThe implementation of GET_APPID gets appid of the specified TFS application. Appid is returned in JSON format. Each application can get a unique appid corresponding to its appkdy. This appid is a required paramter in each Custom TFS operation. You can consider this as a namespace in TFS.\n\n####Syntax\n\n>GET /<b>v2</b>/<i>appkey</i>/<i>appid</i>  HTTP/1.1\n\n>Host: <i>10.0.0.1:7500</i>\n\n>Date: <i>date</i>\n\n<i>appkey</i> is an id of an application configured in RcServer database.\n\n####Request Parameters\n\nNo\n\n####Response\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>name</th>\n\t\t<th>description</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>APP_ID</td>\n\t\t<td>appid of application</td>\n\t</tr>\n</table>\n\n####Status code\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>HTTP status code</th>\n\t\t<th>description</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>200 OK</td>\n\t\t<td>operation success</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>400 Bad Request</td>\n\t\t<td>bad request</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>500 Internal Server Error</td>\n\t\t<td>login failed(maybe this appkey is not configured in RcServer database) or internal server error in tfs or nginx server</td>\n\t</tr>\n</table>\n\n####Examples\n\nThe following request will query the appid of appkey tfs:\n\n<pre>\nGET /v2/tfs/appid HTTP/1.1\nHost: 10.0.0.1:7500\nDate: Thu, 28 Jun 2012 08:00:26 GMT\n\n</pre>\n\nThe corresponding response will be:\n\n<pre>\nHTTP/1.1 200 OK\nServer: Tengine/1.3.0\nDate: Thu, 28 Jun 2012 08:00:26 GMT\nContent-Type: application/json\nTransfer-Encoding: chunked\nConnection: keep-alive\n\n{\n    \"APP_ID\": \"1\"\n}\n</pre>\n\n###CREATE_DIR\n\n####Description\n\nThe implementation of CREATE_DIR creates a dir.\n\n####Syntax\n\n>POST /<b>v2</b>/<i>appkey</i>/<i>appid</i>/<i>uid</i>/<b>dir</b>/<i>dir_name</i>  HTTP/1.1\n\n>Host: <i>10.0.0.1:7500</i>\n\n>Date: <i>date</i>\n\n<i>appkey</i> is an id of an application configured in RcServer database.\n\nAppid can be achieved by the API <b>GET_APPID</b>.\n\nUid is short for user id, each appid and uid makes a unique namespace in TFS.\n\n####Request Parameters\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>parameter</th>\n\t\t<th>description</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>recursive</td>\n\t\t<td>1: recursively create parent dirs<br>0: do not recursively create parent dirs</td>\n\t</tr>\n</table>\n\n####Response\n\nNo\n\n####Status code\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>HTTP status code</th>\n\t\t<th>description</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>201 Created</td>\n\t\t<td>operation success</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>400 Bad Request</td>\n\t\t<td>invalid dir name</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>401 Unauthorized</td>\n\t\t<td>do not have permission(each application is only allowed to modify its namespace(under its appid))</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>403 Forbidden</td>\n\t\t<td>subdir count/subfile count/dir depth exceeds the restrict</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>409 Conflict</td>\n\t\t<td>dir already exists</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>500 Internal Server Error</td>\n\t\t<td>internal server error in tfs or nginx server</td>\n\t</tr>\n</table>\n\n####Examples\n\nThe following request will use tfs as appkey, create \"/dir_1\" under the namespace of appid 1 and uid 1234:\n\n<pre>\nPOST /v2/tfs/1/1234/dir/dir_1 HTTP/1.1\nHost: 10.0.0.1:7500\nDate: Fri, 30 Nov 2012 03:05:00 GMT\n</pre>\n\nThe corresponding response will be:\n\n<pre>\nHTTP/1.1 201 Created\nServer: Tengine/1.3.0\nDate: Wed, 27 Jun 2012 14:59:27 GMT\nContent-Length: 0\nConnection: keep-alive\n</pre>\n\n###RM_DIR\n\n####Description\n\nThe implementation of RM_DIR removes a dir.\n\n####Syntax\n\n>DELETE /<b>v2</b>/<i>appkey</i>/<i>appid</i>/<i>uid</i>/<b>dir</b>/<i>dir_name</i> HTTP/1.1\n\n>Host: <i>10.0.0.1:7500</i>\n\n>Date: <i>date</i>\n\n<i>appkey</i> is an id of an application configured in RcServer database.\n\nAppid can be achieved by the API <b>GET_APPID</b>.\n\nUid is short for user id, each appid and uid makes a unique namespace in TFS.\n\n####Request Parameters\n\nNo\n\n####Response\n\nNo\n\n####Status code\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>HTTP status code</th>\n\t\t<th>description</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>200 Ok</td>\n\t\t<td>operation success</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>400 Bad Request</td>\n\t\t<td>invalid dir name</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>401 Unauthorized</td>\n\t\t<td>do not have permission(each application is only allowed to modify its namespace(under its appid))</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>403 Forbidden</td>\n\t\t<td>dir is not empty</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>404 Not Found</td>\n\t\t<td>dir or parent dir not exist</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>500 Internal Server Error</td>\n\t\t<td>internal server error in tfs or nginx server</td>\n\t</tr>\n</table>\n\n####Examples\n\nThe following request will use tfs appkey, remove \"/dir_1\" under the namespace of appid 1 uid 1234:\n\n<pre>\nDELETE /v2/tfs/1/1234/dir/dir_1 HTTP/1.1\nHost: 10.0.0.1:7500\nDate: Thu, 28 Jun 2012 08:12:13 GMT\n</pre>\n\nThe corresponding response will be:\n\n<pre>\nHTTP/1.1 200 OK\nServer: Tengine/1.3.0\nDate: Thu, 28 Jun 2012 08:12:13 GMT\nContent-Length: 0\nConnection: keep-alive\n</pre>\n\n###MV_DIR\n\n####Description\n\nThe implementation of MV_DIR moves or renames a dir.\n\n####Syntax\n\n>POST /<b>v2</b>/<i>appkey</i>/<i>appid</i>/<i>uid</i>/<b>dir</b>/<i>dest_dir_name</i> HTTP/1.1\n\n>Host: <i>10.0.0.1:7500</i>\n\n>Date: <i>date</i>\n\n>x-ali-move-source: /<i>src_dir_name</i>\n\n<i>appkey</i> is an id of an application configured in RcServer database.\n\nAppid can be achieved by the API <b>GET_APPID</b>.\n\nUid is short for user id, each appid and uid makes a unique namespace in TFS.\n\nA custom HTTP request header is needed here to specify the src dir.\n\n####Request Parameters\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>parameter</th>\n\t\t<th>description</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>recursive</td>\n\t\t<td>1: recursively create parent dirs of the dest dir<br>0: do not recursively create parent dirs of the dest dir</td>\n\t</tr>\n</table>\n\n####Response\n\nNo\n\n####Status code\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>HTTP status code</th>\n\t\t<th>description</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>200 Ok</td>\n\t\t<td>operation success</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>400 Bad Request</td>\n\t\t<td>invalid dir name</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>401 Unauthorized</td>\n\t\t<td>do not have permission(each application is only allowed to modify its namespace(under its appid))</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>403 Forbidden</td>\n\t\t<td>move to subdir</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>404 Not Found</td>\n\t\t<td>src dir or parent dir not exist, or dest dir already exists, or parent dir of dest dir not exist</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>500 Internal Server Error</td>\n\t\t<td>internal server error in tfs or nginx server</td>\n\t</tr>\n</table>\n\n####Examples\n\nThe following request will use tfs as appkey, rename \"/dir_src\" as \"/dir_dest\" under the namespace of appid 1 uid 1234:\n\n<pre>\nPOST /v2/tfs/1/1234/dir/dir_dest HTTP/1.1\nHost: 10.0.0.1:7500\nDate: Sat, 30 Jun 2012 05:33:05 GMT\nx-ali-move-source: /dir_src\n</pre>\n\nThe corresponding response will be:\n\n<pre>\nHTTP/1.1 200 OK\nServer: Tengine/1.3.0\nDate: Sat, 30 Jun 2012 05:33:05 GMT\nContent-Length: 0\nConnection: keep-alive\n</pre>\n\n###LS_DIR\n\n####Description\n\nThe implementation of LS_DIR lists all subdirs and subfiles. Subdirs and subfiles are returned in JSON format.\n\n####Syntax\n\n>GET /<b>v2</b>/<i>appkey</i>/<b>metadata</b>/<i>appid</i>/<i>uid</i>/<b>dir</b>/<i>dir_name</i>  HTTP/1.1\n\n>Host: <i>10.0.0.1:7500</i>\n\n>Date: <i>date</i>\n\n<i>appkey</i> is an id of an application configured in RcServer database.\n\nAppid can be achieved by the API <b>GET_APPID</b>.\n\nUid is short for user id, each appid and uid makes a unique namespace in TFS.\n\n####Request Parameters\n\nNo\n\n####Response\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>name</th>\n\t\t<th>description</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>NAME</td>\n\t\t<td>file/dir name/td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>PID</td>\n\t\t<td>if of the parent dir of file/dir</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>ID</td>\n\t\t<td>if of file/dir</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>SIZE</td>\n\t\t<td>file size</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>IS_FILE</td>\n\t\t<td>is file or not</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>CREATE_TIME</td>\n\t\t<td>create time</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>MODIFY_TIME</td>\n\t\t<td>modify time/td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>VER_NO</td>\n\t\t<td>version no of file/dir</td>\n\t</tr>\n</table>\n\n####Status code\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>HTTP status code</th>\n\t\t<th>description</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>200 Ok</td>\n\t\t<td>operation success</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>400 Bad Request</td>\n\t\t<td>invalid dir name</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>404 Not Found</td>\n\t\t<td>dir or parent dir not exist</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>500 Internal Server Error</td>\n\t\t<td>internal server error in tfs or nginx server</td>\n\t</tr>\n</table>\n\n####Examples\n\nThe following request will use tfs as appkey, list all subdirs and subfiles of the dir \"/\" under the namespace of appid 1 and uid 1234:\n\n<pre>\nGET /v2/tfs/metadata/1/1234/dir/ HTTP/1.1\nHost: 10.0.0.1:7500\nDate: Sat, 30 Jun 2012 05:42:25 GMT\n</pre>\n\nThe corresponding response will be:\n\n<pre>\nHTTP/1.1 200 OK\nServer: Tengine/1.3.0\nDate: Sat, 30 Jun 2012 05:42:25 GMT\nContent-Type: application/json\nTransfer-Encoding: chunked\nConnection: keep-alive\n\n[\n    {\n        \"NAME\": \"d_0\",\n        \"PID\": 635213,\n        \"ID\": 635218,\n        \"SIZE\": 0,\n        \"IS_FILE\": false,\n        \"CREATE_TIME\": \"Wed, 27 Jun 2012 10:32:04 UTC+0800\",\n        \"MODIFY_TIME\": \"Wed, 27 Jun 2012 11:29:30 UTC+0800\",\n        \"VER_NO\": 0\n    },\n    {\n        \"NAME\": \"d_3\",\n        \"PID\": 635213,\n        \"ID\": 635219,\n        \"SIZE\": 0,\n        \"IS_FILE\": false,\n        \"CREATE_TIME\": \"Wed, 27 Jun 2012 10:36:17 UTC+0800\",\n        \"MODIFY_TIME\": \"Sat, 30 Jun 2012 13:32:53 UTC+0800\",\n        \"VER_NO\": 0\n    },\n    {\n        \"NAME\": \"file_1\",\n        \"PID\": -9223372036854140595,\n        \"ID\": 0,\n        \"SIZE\": 222,\n        \"IS_FILE\": true,\n        \"CREATE_TIME\": \"Wed, 27 Jun 2012 17:13:23 UTC+0800\",\n        \"MODIFY_TIME\": \"Wed, 27 Jun 2012 17:25:03 UTC+0800\",\n        \"VER_NO\": 1\n    }\n]\n</pre>\n\n###IS_DIR_EXIST\n\n####Description\n\nThe implementation of IS_DIR_EXIST checks if the specified dir exists.\n\n####Syntax\n\n>HEAD /<b>v2</b>/<i>appkey</i>/<i>appid</i>/<i>uid</i>/<b>dir</b>/<i>dir_name</i>  HTTP/1.1\n\n>Host: <i>10.0.0.1:7500</i>\n\n>Date: <i>date</i>\n\n<i>appkey</i> is an id of an application configured in RcServer database.\n\nAppid can be achieved by the API <b>GET_APPID</b>.\n\nUid is short for user id, each appid and uid makes a unique namespace in TFS.\n\n####Request Parameters\n\nNo\n\n####Response\n\nNo\n\n####Status code\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>HTTP status code</th>\n\t\t<th>description</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>200 Ok</td>\n\t\t<td>dir exists</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>400 Bad Request</td>\n\t\t<td>invalid dir name</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>404 Not Found</td>\n\t\t<td>dir not exist</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>500 Internal Server Error</td>\n\t\t<td>internal server error in tfs or nginx server</td>\n\t</tr>\n</table>\n\n####Examples\n\nThe following request will use tfs as appkey, check whether dir \"/dir_1\" exists under the namespace of appid 1 uid 1234:\n\n<pre>\nHEAD /v2/tfs/1/1234/dir/dir_1 HTTP/1.1\nHost: 10.0.0.1:7500\nDate: Sat, 30 Jun 2012 05:42:25 GMT\n</pre>\n\nIf dir exists, the corresponding response will be:\n\n<pre>\nHTTP/1.1 200 OK\nServer: Tengine/1.3.0\nDate: Sat, 30 Jun 2012 05:42:25 GMT\nContent-Length: 0\nConnection: keep-alive\n</pre>\n\n###CREATE_FILE\n\n####Description\n\nThe implementation of CREATE_FILE creates a file.\n\n####Syntax\n>POST /<b>v2</b>/<i>appkey</i>/<i>appid</i>/<i>uid</i>/<b>file</b>/<i>file_name</i>  HTTP/1.1\n\n>Host: <i>10.0.0.1:7500</i>\n\n>Date: <i>date</i>\n\n<i>appkey</i> is an id of an application configured in RcServer database.\n\nAppid can be achieved by the API <b>GET_APPID</b>.\n\nUid is short for user id, each appid and uid makes a unique namespace in TFS.\n\n####Request Parameters\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>parameter</th>\n\t\t<th>description</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>recursive</td>\n\t\t<td>1: recursively create parent dirs<br>0: do not recursively create parent dirs</td>\n\t</tr>\n</table>\n\n####Response\n\nNo\n\n####Status code\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>HTTP status code</th>\n\t\t<th>description</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>201 Created</td>\n\t\t<td>operation success</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>400 Bad Request</td>\n\t\t<td>invalid file name</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>401 Unauthorized</td>\n\t\t<td>do not have permission(each application is only allowed to modify its namespace(under its appid))</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>403 Forbidden</td>\n\t\t<td>subdir count/subfile count/dir depth exceeds the restrict</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>404 Not Found</td>\n\t\t<td>parent dir not exist</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>409 Conflict</td>\n\t\t<td>file already exists</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>500 Internal Server Error</td>\n\t\t<td>internal server error in tfs or nginx server</td>\n\t</tr>\n</table>\n\n####Examples\n\nThe following request will use tfs as appkey, create \"/file_1\" under the namespace of appid 1 uid 1234:\n\n<pre>\nPOST /v2/tfs/1/1234/file/file_1 HTTP/1.1\nHost: 10.0.0.1:7500\nDate: Fri, 30 Nov 2012 03:05:00 GMT\n</pre>\n\nThe corresponding response will be:\n\n<pre>\nHTTP/1.1 201 Created\nServer: Tengine/1.3.0\nDate: Wed, 27 Jun 2012 14:59:27 GMT\nContent-Length: 0\nConnection: keep-alive\n</pre>\n\n###WRITE_FILE\n\n####Description\n\nThe implementation of WRITE_FILE write data to a file. PWRITE is supported. File hole is also supported. Append is used by default.\n\nNOTE: in-place update is not supported.\n\n####Syntax\n\n>PUT /<b>v2</b>/<i>appkey</i>/<i>appid</i>/<i>uid</i>/<b>file</b>/<i>file_name</i>  HTTP/1.1\n\n>Host: <i>10.0.0.1:7500</i>\n\n>Content-Length: <i>length</i>\n\n>Date: <i>date</i>\n\n<i>appkey</i> is an id of an application configured in RcServer database.\n\nAppid can be achieved by the API <b>GET_APPID</b>.\n\nUid is short for user id, each appid and uid makes a unique namespace in TFS.\n\n####Request Parameters\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>parameter</th>\n\t\t<th>description</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>offset</td>\n\t\t<td>offset to write</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>size</td>\n\t\t<td>data size to write</td>\n\t</tr>\n</table>\n\n####Response\n\nNo\n\n####Status code\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>HTTP status code</th>\n\t\t<th>description</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>200 Ok</td>\n\t\t<td>operation success</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>400 Bad Request</td>\n\t\t<td>invalid file name or parameter</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>401 Unauthorized</td>\n\t\t<td>do not have permission(each application is only allowed to modify its namespace(under its appid))</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>404 Not Found</td>\n\t\t<td>file not found</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>409 Conflict</td>\n\t\t<td>data already exists in write offset(in-place update)</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>500 Internal Server Error</td>\n\t\t<td>internal server error in tfs or nginx server</td>\n\t</tr>\n</table>\n\n####Examples\n\nThe following request will use tfs as appkey, write data to \"/file_1\" under the namespace of appid 1 uid 1234:\n\n<pre>\nPUT /v2/tfs/1/1234/file/file_1 HTTP/1.1\nHost: 10.0.0.1:7500\nContent-Length: 222\nDate: Wed, 27 Jun 2012 14:59:27 GMT\n\n[Data]\n</pre>\n\nThe corresponding response will be:\n\n<pre>\nHTTP/1.1 200 OK\nServer: Tengine/1.3.0\nDate: Wed, 27 Jun 2012 14:59:27 GMT\nContent-Length: 0\nConnection: keep-alive\n</pre>\n\n###READ_FILE\n\n####Description\n\nThe implementation of READ_FILE reads data from a file.\n\n####Syntax\n\n>GET /<b>v2</b>/<i>appkey</i>/<i>appid</i>/<i>uid</i>/<b>file</b>/<i>file_name</i>  HTTP/1.1\n\n>Host: <i>10.0.0.1:7500</i>\n\n>Date: <i>date</i>\n\n<i>appkey</i> is an id of an application configured in RcServer database.\n\nAppid can be achieved by the API <b>GET_APPID</b>.\n\nUid is short for user id, each appid and uid makes a unique namespace in TFS.\n\n####Request Parameters\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>parameter</th>\n\t\t<th>description</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>offset</td>\n\t\t<td>offset to read</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>size</td>\n\t\t<td>data size to read</td>\n\t</tr>\n</table>\n\n####Response\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>name</th>\n\t\t<th>description</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>data</td>\n\t\t<td>data readed</td>\n\t</tr>\n</table>\n\n####Status code\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>HTTP status code</th>\n\t\t<th>description</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>200 Ok</td>\n\t\t<td>operation success</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>400 Bad Request</td>\n\t\t<td>invalid file name or parameter</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>404 Not Found</td>\n\t\t<td>file not found</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>500 Internal Server Error</td>\n\t\t<td>internal server error in tfs or nginx server</td>\n\t</tr>\n</table>\n\n####Examples\n\nThe following request will use tfs as appkey, read data from \"/file_1\"(whole file) under the namespace of appid 1 uid 1234:\n\n<pre>\nGET /v2/tfs/1/1234/file/file_1 HTTP/1.1\nHost: 10.0.0.1:7500\nDate: Wed, 27 Jun 2012 14:59:27 GMT\n</pre>\n\nThe corresponding response will be:\n\n<pre>\nHTTP/1.1 200 OK\nServer: Tengine/1.3.0\nDate: Wed, 27 Jun 2012 14:59:27 GMT\nContent-Length: 222\nConnection: keep-alive\n\n[data]\n</pre>\n\n###RM_FILE\n\n####Description\n\nThe implementation of RM_FILE removes a file.\n\n####Syntax\n\n>DELETE /<b>v2</b>/<i>appkey</i>/<i>appid</i>/<i>uid</i>/<b>file</b>/<i>file_name</i>  HTTP/1.1\n\n>Host: <i>10.0.0.1:7500</i>\n\n>Date: <i>date</i>\n\n<i>appkey</i> is an id of an application configured in RcServer database.\n\nAppid can be achieved by the API <b>GET_APPID</b>.\n\nUid is short for user id, each appid and uid makes a unique namespace in TFS.\n\n####Request Parameters\n\nNo\n\n####Response\n\nNo\n\n####Status code\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>HTTP status code</th>\n\t\t<th>description</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>200 Ok</td>\n\t\t<td>operation success</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>400 Bad Request</td>\n\t\t<td>invalid file name</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>401 Unauthorized</td>\n\t\t<td>do not have permission(each application is only allowed to modify its namespace(under its appid))</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>404 Not Found</td>\n\t\t<td>file not found</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>500 Internal Server Error</td>\n\t\t<td>internal server error in tfs or nginx server</td>\n\t</tr>\n</table>\n\n####Examples\n\nThe following request will use tfs as appkey, remove \"/file_1\" under the namespace of appid 1 uid 1234:\n\n<pre>\nDELETE /v2/tfs/1/1234/file/file_1 HTTP/1.1\nHost: 10.0.0.1:7500\nDate: Thu, 28 Jun 2012 08:12:13 GMT\n</pre>\n\nThe corresponding response will be:\n\n<pre>\nHTTP/1.1 200 OK\nServer: Tengine/1.3.0\nDate: Thu, 28 Jun 2012 08:12:13 GMT\nContent-Length: 0\nConnection: keep-alive\n</pre>\n\n###MV_FILE\n\n####Description\n\nThe implementation of MV_FILE move or rename a file.\n\n####Syntax\n\n>POST /<b>v2</b>/<i>appkey</i>/<i>appid</i>/<i>uid</i>/<b>file</b>/<i>dest_file_name</i>  HTTP/1.1\n\n>Host: <i>10.0.0.1:7500</i>\n\n>Date: <i>date</i>\n\n>x-ali-move-source: /<i>src_file_name</i>\n\n<i>appkey</i> is an id of an application configured in RcServer database.\n\nAppid can be achieved by the API <b>GET_APPID</b>.\n\nUid is short for user id, each appid and uid makes a unique namespace in TFS.\n\nA custom HTTP request header is needed here to specify the src file.\n\n####Request Parameters\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>parameter</th>\n\t\t<th>description</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>recursive</td>\n\t\t<td>1: recursively create parent dirs of the dest file<br>0: do not recursively create parent dirs of the dest file</td>\n\t</tr>\n</table>\n\n####Response\n\nNo\n\n####Status code\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>HTTP status code</th>\n\t\t<th>description</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>200 Ok</td>\n\t\t<td>operation success</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>400 Bad Request</td>\n\t\t<td>invalid file name or src file and dest file are the same file</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>401 Unauthorized</td>\n\t\t<td>do not have permission(each application is only allowed to modify its namespace(under its appid))</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>404 Not Found</td>\n\t\t<td>src file not exist, or dest file already exists, or parent dir of dest file not exist</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>500 Internal Server Error</td>\n\t\t<td>internal server error in tfs or nginx server</td>\n\t</tr>\n</table>\n\n####Examples\n\nThe following request will use tfs as appkey, rename \"/file_src\" to \"/file_dest\" under the namespace of appid 1 uid 1234:\n\n<pre>\nPOST /v2/tfs/1/1234/file/file_dest HTTP/1.1\nHost: 10.0.0.1:7500\nDate: Sat, 30 Jun 2012 05:33:05 GMT\nx-ali-move-source: /file_src\n</pre>\n\nThe corresponding response will be:\n\n<pre>\nHTTP/1.1 200 OK\nServer: Tengine/1.3.0\nDate: Sat, 30 Jun 2012 05:33:05 GMT\nContent-Length: 0\nConnection: keep-alive\n</pre>\n\n###LS_FILE\n\n####Description\n\nThe implementation of LS_FILE achieves the meta data of the file.\n\n####Syntax\n\n>GET /<b>v2</b>/<i>appkey</i>/<b>metadata</b>/<i>appid</i>/<i>uid</i>/<b>file</b>/<i>file_name</i>  HTTP/1.1\n\n>Host: <i>10.0.0.1:7500</i>\n\n>Date: <i>date</i>\n\n<i>appkey</i> is an id of an application configured in RcServer database.\n\nAppid can be achieved by the API <b>GET_APPID</b>.\n\nUid is short for user id, each appid and uid makes a unique namespace in TFS.\n\n####Request Parameters\n\nNo\n\n####Response\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>name</th>\n\t\t<th>description</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>NAME</td>\n\t\t<td>file name(absolute path)</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>PID</td>\n\t\t<td>id of the parent dir of the file</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>ID</td>\n\t\t<td>id of the file</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>SIZE</td>\n\t\t<td>file size</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>IS_FILE</td>\n\t\t<td>is file or not</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>CREATE_TIME</td>\n\t\t<td>create time</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>MODIFY_TIME</td>\n\t\t<td>modify time</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>VER_NO</td>\n\t\t<td>version no of file</td>\n\t</tr>\n</table>\n\n####Status code\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>HTTP status code</th>\n\t\t<th>description</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>200 Ok</td>\n\t\t<td>operation success</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>400 Bad Request</td>\n\t\t<td>invalid file name</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>404 Not Found</td>\n\t\t<td>file not found</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>500 Internal Server Error</td>\n\t\t<td>internal server error in tfs or nginx server</td>\n\t</tr>\n</table>\n\n####Examples\n\nThe following request will use tfs as appkey, achieve the meta data of \"/file_1\" under the namespace of appid 1 uid 1234:\n\n<pre>\nGET /v2/tfs/metadata/1/1234/file/file_1 HTTP/1.1\nHost: 10.0.0.1:7500\nDate: Sat, 30 Jun 2012 05:42:25 GMT\n</pre>\n\nThe corresponding response will be:\n\n<pre>\nHTTP/1.1 200 OK\nServer: Tengine/1.3.0\nDate: Sat, 30 Jun 2012 05:42:25 GMT\nContent-Type: application/json\nTransfer-Encoding: chunked\nConnection: keep-alive\n\n{\n    \"NAME\": \"/file_1\",\n    \"PID\": 635213,\n    \"ID\": 0,\n    \"SIZE\": 298481,\n    \"IS_FILE\": true,\n    \"CREATE_TIME\": \"Fri, 15 Jun 2012 09:37:39 UTC+0800\",\n    \"MODIFY_TIME\": \"Sat, 30 Jun 2012 23:57:38 UTC+0800\",\n    \"VER_NO\": 0\n}\n</pre>\n\n###IS_FILE_EXIST\n\n####Description\n\nThe implementation of IS_FILE_EXIST checks if file exists.\n\n####Syntax\n\n>HEAD /<b>v2</b>/<i>appkey</i>/<i>appid</i>/<i>uid</i>/<b>file</b>/<i>file_name</i>  HTTP/1.1\n\n>Host: <i>10.0.0.1:7500</i>\n\n>Date: <i>date</i>\n\n<i>appkey</i> is an id of an application configured in RcServer database.\n\nAppid can be achieved by the API <b>GET_APPID</b>.\n\nUid is short for user id, each appid and uid makes a unique namespace in TFS.\n\n####Request Parameters\n\nNo\n\n####Response\n\nNo\n\n####Status code\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>HTTP status code</th>\n\t\t<th>description</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>200 Ok</td>\n\t\t<td>file exist</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>400 Bad Request</td>\n\t\t<td>invalid file name</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>404 Not Found</td>\n\t\t<td>file not found</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>500 Internal Server Error</td>\n\t\t<td>internal server error in tfs or nginx server</td>\n\t</tr>\n</table>\n\n####Examples\n\nThe following request will use tfs as appkey, check if \"/file_1\" exists under the namespace of appid 1 uid 1234:\n\n<pre>\nHEAD /v2/tfs/1/1234/file/file_1 HTTP/1.1\nHost: 10.0.0.1:7500\nDate: Sat, 30 Jun 2012 05:42:25 GMT\n</pre>\n\nIf file exists, the corresponding response will be:\n\n<pre>\nHTTP/1.1 200 OK\nServer: Tengine/1.3.0\nDate: Sat, 30 Jun 2012 05:42:25 GMT\nContent-Length: 0\nConnection: keep-alive\n</pre>\n"
  },
  {
    "path": "docs/modules/TFS_RESTful_API_cn.md",
    "content": "#TFS RESTful API\n\n注：API语法描述中的<b>粗体</b>为API语法规定的保留关键字部分，<i>斜体</i>为用户输入部分\n\n##原生TFS\n\n###写文件\n\n####描述\n\n此API用于将数据保存成一个TFS文件并以JSON格式返回TFS的文件名\n\n####语法\n\n>POST /<b>v1</b>/<i>appkey</i> HTTP/1.1\n\n>Host: <i>10.0.0.1:7500</i>\n\n>Content-Length: <i>length</i>\n\n>Date: <i>date</i>\n\n其中<i>appkey</i>是在RcServer的数据库中配置的应用的标识符，若无使用RcServer，使用<b>tfs</b>即可\n\n####请求参数\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>参数名</th>\n\t\t<th>描述</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>suffix</td>\n\t\t<td>文件后缀</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>simple_name</td>\n\t\t<td>是否要求必须带正确后缀才可访问存入的TFS文件<br>1：要求必须带正确后缀才可访问<br>0：不带后缀也可访问</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>large_file</td>\n\t\t<td>是否存成大文件（文件名以L开头）<br>1：存成大文件<br>0：不存成大文件</td>\n\t</tr>\n</table>\n\n####应答\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>名称</th>\n\t\t<th>描述</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>TFS_FILE_NAME</td>\n\t\t<td>返回的TFS文件名</td>\n\t</tr>\n</table>\n\n####返回的状态码\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>HTTP状态码</th>\n\t\t<th>描述</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>200 OK</td>\n\t\t<td>操作成功</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>400 Bad Request</td>\n\t\t<td>错误的请求</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>500 Internal Server Error</td>\n\t\t<td>TFS或Nginx服务器内部错误</td>\n\t</tr>\n</table>\n\n####例子\n\n下面这个请求，将会使用tfs这个appkey写一个不带后缀的TFS文件：\n<pre>\nPOST /v1/tfs HTTP/1.1\nHost: 10.0.0.1:7500\nContent-Length: 22\nDate: Fri, 30 Nov 2012 03:05:00 GMT\n\n[data]\n</pre>\n\n对应的应答将会是：\n\n<pre>\nHTTP/1.1 200 OK\nServer: Tengine/1.3.0\nDate: Fri, 30 Nov 2012 03:05:00 GMT\nContent-Type: application/json\nTransfer-Encoding: chunked\nConnection: keep-alive\n\n{\n\t\"TFS_FILE_NAME\": \"T1FOZHB4ET1RCvBVdK\"\n}\n</pre>\n\n下面这个请求，将会使用tfs这个appkey写一个带“.jpg”后缀的TFS文件，并且要求必须带该后缀才能访问该文件：\n\n<pre>\nPOST /v1/tfs?suffix=.jpg&simple_name=1 HTTP/1.1\nHost: 10.0.0.1:7500\nContent-Length: 22\nDate: Fri, 30 Nov 2012 03:05:00 GMT\n\n[data]\n</pre>\n\n对应的应答将会是：\n\n<pre>\nHTTP/1.1 200 OK\nServer: Tengine/1.3.0\nDate: Fri, 30 Nov 2012 03:05:00 GMT\nContent-Type: application/json\nTransfer-Encoding: chunked\nConnection: keep-alive\n\n{\n\t\"TFS_FILE_NAME\": \"T1FOZHB4ET1RCvBVdK.jpg\"\n}\n</pre>\n\n###更新文件\n\n####描述\n\n此API用于更新一个已有的TFS文件并以JSON格式返回文件名\n\n####语法\n\n>PUT /<b>v1</b>/<i>appkey</i>/<i>TfsFileName</i> HTTP/1.1\n\n>Host: <i>10.0.0.1:7500</i>\n\n>Content-Length: <i>length</i>\n\n>Date: <i>date</i>\n\n其中<i>appkey</i>是在RcServer的数据库中配置的应用的标识符，若无使用RcServer，使用<b>tfs</b>即可\n\n####请求参数\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>参数名</th>\n\t\t<th>描述</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>suffix</td>\n\t\t<td>文件后缀</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>simple_name</td>\n\t\t<td>是否要求必须带正确后缀才可访问存入的TFS文件<br>1：要求必须带正确后缀才可访问<br>0：不带后缀也可访问</td>\n\t</tr>\n</table>\n\n####应答\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>名称</th>\n\t\t<th>描述</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>TFS_FILE_NAME</td>\n\t\t<td>返回的TFS文件名</td>\n\t</tr>\n</table>\n\n####返回的状态码\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>HTTP状态码</th>\n\t\t<th>描述</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>200 OK</td>\n\t\t<td>操作成功</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>400 Bad Request</td>\n\t\t<td>错误的请求</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>500 Internal Server Error</td>\n\t\t<td>TFS或Nginx服务器内部错误</td>\n\t</tr>\n</table>\n\n####例子\n\n下面这个请求，将会使用tfs这个appkey更新TFS文件T1FOZHB4ET1RCvBVdK：\n<pre>\nPUT /v1/tfs/T1FOZHB4ET1RCvBVdK HTTP/1.1\nHost: 10.0.0.1:7500\nContent-Length: 22\nDate: Fri, 30 Nov 2012 03:05:00 GMT\n\n[data]\n</pre>\n\n对应的应答将会是：\n\n<pre>\nHTTP/1.1 200 OK\nServer: Tengine/1.3.0\nDate: Fri, 30 Nov 2012 03:05:00 GMT\nContent-Type: application/json\nTransfer-Encoding: chunked\nConnection: keep-alive\n\n{\n\t\"TFS_FILE_NAME\": \"T1FOZHB4ET1RCvBVdK\"\n}\n</pre>\n\n###读文件\n\n####描述\n\n此API用于从一个TFS文件中读取数据\n\n####语法\n>GET /<b>v1</b>/<i>appkey</i>/<i>TfsFileName</i> HTTP/1.1\n\n>Host: <i>10.0.0.1:7500</i>\n\n>Date: <i>date</i>\n\n其中<i>appkey</i>是在RcServer的数据库中配置的应用的标识符，若无使用RcServer，使用<b>tfs</b>即可\n\nTfsFileName是要读取的TFS文件的文件名，可带后缀\n\n####请求参数\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>参数名</th>\n\t\t<th>描述</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>suffix</td>\n\t\t<td>文件后缀<br>注：若指定了此参数，并且TfsFileName中也带了后缀，两者不一致将无法访问文件</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>offset</td>\n\t\t<td>要读取数据在文件在的偏移</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>size</td>\n\t\t<td>要读取数据的长度</td>\n\t</tr>\n</table>\n\n\n####应答\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>名称</th>\n\t\t<th>描述</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>data</td>\n\t\t<td>数据</td>\n\t</tr>\n</table>\n\n####返回的状态码\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>HTTP状态码</th>\n\t\t<th>描述</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>200 OK</td>\n\t\t<td>操作成功</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>400 Bad Request</td>\n\t\t<td>错误的请求</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>404 Not Found</td>\n\t\t<td>文件不存在</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>500 Internal Server Error</td>\n\t\t<td>TFS或Nginx服务器内部错误</td>\n\t</tr>\n</table>\n\n####例子\n\n下面这个请求，将会使用tfs这个appkey读文件T1FOZHB4ET1RCvBVdK：\n\n<pre>\nGET /v1/tfs/T1FOZHB4ET1RCvBVdK HTTP/1.1\nHost: 10.0.0.1:7500\nDate: Fri, 30 Nov 2012 03:05:00 GMT\n</pre>\n\n对应的应答将会是：\n\n<pre>\nHTTP/1.1 200 OK\nServer: Tengine/1.3.0\nDate: Fri, 30 Nov 2012 03:05:00 GMT\nLast-Modified: Thu, 29 Nov 2012 03:05:00 GMT\nTransfer-Encoding: chunked\nConnection: keep-alive\n\n[data]\n</pre>\n\n下面这个请求，将会使用tfs这个appkey读文件T1FOZHB4ET1RCvBVdK.jpg：\n\n<pre>\nGET /v1/tfs/T1FOZHB4ET1RCvBVdK.jpg HTTP/1.1\nHost: 10.0.0.1:7500\nDate: Fri, 30 Nov 2012 03:05:00 GMT\n</pre>\n\n对应的应答将会是：\n\n<pre>\nHTTP/1.1 200 OK\nServer: Tengine/1.3.0\nDate: Fri, 30 Nov 2012 03:05:00 GMT\nLast-Modified: Thu, 29 Nov 2012 03:05:00 GMT\nTransfer-Encoding: chunked\nConnection: keep-alive\n\n[data]\n</pre>\n\n###删除文件\n\n####描述\n\n此API用于将一个TFS文件删除或隐藏\n\n####语法\n\n>DELETE /<b>v1</b>/<i>appkey</i>/<i>TfsFileName</i> HTTP/1.1\n\n>Host: <i>10.0.0.1:7500</i>\n\n>Date: <i>date</i>\n\n其中<i>appkey</i>是在RcServer的数据库中配置的应用的标识符，若无使用RcServer，使用<b>tfs</b>即可\n\nTfsFileName是要读取的TFS文件的文件名，可带后缀\n\n####请求参数\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>参数名</th>\n\t\t<th>描述</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>suffix</td>\n\t\t<td>文件后缀<br>注：若指定了此参数，并且TfsFileName中也带了后缀，两者不一致将无法访问文件</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>hide</td>\n\t\t<td>指定隐藏操作类型：<br>1：隐藏<br>0：反隐藏</td>\n\t</tr>\n</table>\n\n####应答\n\n无\n\n####返回的状态码\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>HTTP状态码</th>\n\t\t<th>描述</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>200 OK</td>\n\t\t<td>操作成功</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>400 Bad Request</td>\n\t\t<td>错误的请求</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>404 Not Found</td>\n\t\t<td>文件不存在</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>500 Internal Server Error</td>\n\t\t<td>TFS或Nginx服务器内部错误</td>\n\t</tr>\n</table>\n\n####例子\n\n下面这个请求，将会使用tfs这个appkey删除文件T1FOZHB4ET1RCvBVdK：\n\n<pre>\nDELETE /v1/tfs/T1FOZHB4ET1RCvBVdK HTTP/1.1\nHost: 10.0.0.1:7500\nDate: Fri, 30 Nov 2012 03:05:00 GMT\n</pre>\n\n对应的应答将会是：\n\n<pre>\nHTTP/1.1 200 OK\nServer: Tengine/1.3.0\nDate: Fri, 30 Nov 2012 03:05:00 GMT\nContent-Length: 0\nConnection: keep-alive\n</pre>\n\n下面这个请求，将会使用tfs这个appkey隐藏TFS文件T1FOZHB4ET1RCvBVdK.jpg：\n\n<pre>\nDELETE /v1/tfs/T1FOZHB4ET1RCvBVdK.jpg?hide=1 HTTP/1.1\nHost: 10.0.0.1:7500\nDate: Fri, 30 Nov 2012 03:05:00 GMT\n</pre>\n\n对应的应答将会是：\n\n<pre>\nHTTP/1.1 200 OK\nServer: Tengine/1.3.0\nDate: Fri, 30 Nov 2012 03:05:00 GMT\nContent-Length: 0\nConnection: keep-alive\n</pre>\n\n###获取文件元信息(stat)\n\n####描述\n\n此API用于获取一个TFS文件的元信息，将以JSON格式返回\n\n####语法\n\n>GET /<b>v1</b>/<i>appkey</i>/<b>metadata</b>/<i>TfsFileName</i> HTTP/1.1\n\n>Host: <i>10.0.0.1:7500</i>\n\n>Date: <i>date</i>\n\n其中<i>appkey</i>是在RcServer的数据库中配置的应用的标识符，若无使用RcServer，使用<b>tfs</b>即可\n\nTfsFileName是要读取的TFS文件的文件名，可带后缀\n\n####请求参数\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>参数名</th>\n\t\t<th>描述</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>suffix</td>\n\t\t<td>文件后缀<br>注：若指定了此参数，并且TfsFileName中也带了后缀，两者不一致将无法访问文件</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>type</td>\n\t\t<td>是否强制获取：<br>0：正常获取，若文件被删除或隐藏则无法获取<br>1：强制获取，即使文件被删除或隐藏也可获取</td>\n\t</tr>\n</table>\n\n####应答\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>名称</th>\n\t\t<th>描述</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>FILE_NAME</td>\n\t\t<td>文件名</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>BLOCK_ID</td>\n\t\t<td>文件所在block的id</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>FILE_ID</td>\n\t\t<td>文件的file id</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>OFFSET</td>\n\t\t<td>文件在其所在block中的偏移</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>SIZE</td>\n\t\t<td>文件大小</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>OCCUPY_SIZE</td>\n\t\t<td>文件真正占用空间</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>MODIFY_TIME</td>\n\t\t<td>文件的最后修改时间</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>CREATE_TIME</td>\n\t\t<td>文件的创建时间</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>STATUS</td>\n\t\t<td>文件的状态<br>0：正常<br>1：删除<br>4：隐藏<br>5: 隐藏且删除</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>CRC</td>\n\t\t<td>文件的CRC校验码</td>\n\t</tr>\n</table>\n\n####返回的状态码\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>HTTP状态码</th>\n\t\t<th>描述</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>200 OK</td>\n\t\t<td>操作成功</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>400 Bad Request</td>\n\t\t<td>错误的请求</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>404 Not Found</td>\n\t\t<td>文件不存在</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>500 Internal Server Error</td>\n\t\t<td>TFS或Nginx服务器内部错误</td>\n\t</tr>\n</table>\n\n####例子\n\n下面这个请求，将会使用tfs这个appkey获取文件T1FOZHB4ET1RCvBVdK的元信息：\n\n<pre>\nGET /v1/tfs/metadata/T1FOZHB4ET1RCvBVdK HTTP/1.1\nHost: 10.0.0.1:7500\nDate: Fri, 30 Nov 2012 03:05:00 GMT\n</pre>\n\n对应的应答将会是：\n\n<pre>\nHTTP/1.1 200 OK\nServer: Tengine/1.3.0\nDate: Fri, 30 Nov 2012 03:05:00 GMT\nContent-Type: application/json\nTransfer-Encoding: chunked\nConnection: keep-alive\n\n{\n    \"FILE_NAME\": \"T1FOZHB4ET1RCvBVdK\",\n    \"BLOCK_ID\": 101,\n    \"FILE_ID\": 9223190836479524436,\n    \"OFFSET\": 69563585,\n    \"SIZE\": 103578,\n    \"OCCUPY_SIZE\": 103614,\n    \"MODIFY_TIME\": \"Fri, 09 Mar 2012 13:40:32 UTC+0800\",\n    \"CREATE_TIME\": \"Fri, 09 Mar 2012 13:40:32 UTC+0800\",\n    \"STATUS\": 0,\n    \"CRC\": 3208008078\n}\n</pre>\n\n##自定义文件名\n\n###获取APPID\n\n####描述\n\n此API用于获取每个TFS应用的appid，以JSON格式返回。每个应用根据自己的appkey，可以获取到一个唯一的appid，这个appid是所有自定义文件名操作所必需的参数。它代表一个应用在TFS中的一个独立的名字空间。\n\n####语法\n\n>GET /<b>v2</b>/<i>appkey</i>/<i>appid</i>  HTTP/1.1\n\n>Host: <i>10.0.0.1:7500</i>\n\n>Date: <i>date</i>\n\n其中<i>appkey</i>是在RcServer的数据库中配置的应用的标识符\n\n####请求参数\n\n无\n\n####应答\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>名称</th>\n\t\t<th>描述</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>APP_ID</td>\n\t\t<td>应用的appid</td>\n\t</tr>\n</table>\n\n####返回的状态码\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>HTTP状态码</th>\n\t\t<th>描述</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>200 OK</td>\n\t\t<td>操作成功</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>400 Bad Request</td>\n\t\t<td>错误的请求</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>500 Internal Server Error</td>\n\t\t<td>登录失败（未在RcServer数据库中配置应用），或TFS或Nginx服务器内部错误</td>\n\t</tr>\n</table>\n\n####例子\n\n下面这个请求，将会查询appkey为tfs的应用的appid：\n\n<pre>\nGET /v2/tfs/appid HTTP/1.1\nHost: 10.0.0.1:7500\nDate: Thu, 28 Jun 2012 08:00:26 GMT\n\n</pre>\n\n对应的应答将会是：\n\n<pre>\nHTTP/1.1 200 OK\nServer: Tengine/1.3.0\nDate: Thu, 28 Jun 2012 08:00:26 GMT\nContent-Type: application/json\nTransfer-Encoding: chunked\nConnection: keep-alive\n\n{\n    \"APP_ID\": \"1\"\n}\n</pre>\n\n###创建目录(create_dir)\n\n####描述\n\n此API用于创建一个目录\n\n####语法\n\n>POST /<b>v2</b>/<i>appkey</i>/<i>appid</i>/<i>uid</i>/<b>dir</b>/<i>dir_name</i>  HTTP/1.1\n\n>Host: <i>10.0.0.1:7500</i>\n\n>Date: <i>date</i>\n\n其中<i>appkey</i>是在RcServer的数据库中配置的应用的标识符\n\nappid是应用的appkey对应的appid，可通过<b>获取APPID</b>的API获得\n\nuid是用户id，每个appid和uid的组合对应一个独立的名字空间，可以认为是appid对应的名字空间下的子空间\n\n####请求参数\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>参数名</th>\n\t\t<th>描述</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>recursive</td>\n\t\t<td>1：递归创建父目录<br>0：不递归创建父目录</td>\n\t</tr>\n</table>\n\n####应答\n\n无\n\n####返回的状态码\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>HTTP状态码</th>\n\t\t<th>描述</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>201 Created</td>\n\t\t<td>创建成功</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>400 Bad Request</td>\n\t\t<td>目录名不合规范</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>401 Unauthorized</td>\n\t\t<td>无权限（目前每个应用只对自己的appid下的空间拥有修改权限）</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>403 Forbidden</td>\n\t\t<td>超过最大子目录数、子文件数或目录深度</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>409 Conflict</td>\n\t\t<td>目录已存在</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>500 Internal Server Error</td>\n\t\t<td>TFS或Nginx服务器内部错误</td>\n\t</tr>\n</table>\n\n####例子\n\n下面这个请求，将会使用tfs这个appkey在appid为1，uid为1234的名字空间下创建一个/dir_1的目录：\n\n<pre>\nPOST /v2/tfs/1/1234/dir/dir_1 HTTP/1.1\nHost: 10.0.0.1:7500\nDate: Fri, 30 Nov 2012 03:05:00 GMT\n</pre>\n\n对应的应答将会是：\n\n<pre>\nHTTP/1.1 201 Created\nServer: Tengine/1.3.0\nDate: Wed, 27 Jun 2012 14:59:27 GMT\nContent-Length: 0\nConnection: keep-alive\n</pre>\n\n###删除目录(rm_dir)\n\n####描述\n\n此API用于删除一个目录\n\n####语法\n\n>DELETE /<b>v2</b>/<i>appkey</i>/<i>appid</i>/<i>uid</i>/<b>dir</b>/<i>dir_name</i> HTTP/1.1\n\n>Host: <i>10.0.0.1:7500</i>\n\n>Date: <i>date</i>\n\n其中<i>appkey</i>是在RcServer的数据库中配置的应用的标识符\n\nappid是应用的appkey对应的appid，可通过<b>获取APPID</b>的API获得\n\nuid是用户id，每个appid和uid的组合对应一个独立的名字空间，可以认为是appid对应的名字空间下的子空间\n\n####请求参数\n\n无\n\n####应答\n\n无\n\n####返回的状态码\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>HTTP状态码</th>\n\t\t<th>描述</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>200 Ok</td>\n\t\t<td>删除成功</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>400 Bad Request</td>\n\t\t<td>目录/文件名不合规范</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>401 Unauthorized</td>\n\t\t<td>无权限（目前每个应用只对自己的appid下的空间拥有修改权限）</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>403 Forbidden</td>\n\t\t<td>目录非空</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>404 Not Found</td>\n\t\t<td>目录或父目录不存在</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>500 Internal Server Error</td>\n\t\t<td>TFS或Nginx服务器内部错误</td>\n\t</tr>\n</table>\n\n####例子\n\n下面这个请求，将会使用tfs这个appkey在appid为1，uid为1234的名字空间下删除/dir_1目录：\n\n<pre>\nDELETE /v2/tfs/1/1234/dir/dir_1 HTTP/1.1\nHost: 10.0.0.1:7500\nDate: Thu, 28 Jun 2012 08:12:13 GMT\n</pre>\n\n对应的应答将会是：\n\n<pre>\nHTTP/1.1 200 OK\nServer: Tengine/1.3.0\nDate: Thu, 28 Jun 2012 08:12:13 GMT\nContent-Length: 0\nConnection: keep-alive\n</pre>\n\n###移动/重命名目录（mv_dir）\n\n####描述\n\n此API用于移动或重命名一个目录\n\n####语法\n\n>POST /<b>v2</b>/<i>appkey</i>/<i>appid</i>/<i>uid</i>/<b>dir</b>/<i>dest_dir_name</i> HTTP/1.1\n\n>Host: <i>10.0.0.1:7500</i>\n\n>Date: <i>date</i>\n\n>x-ali-move-source: /<i>src_dir_name</i>\n\n其中<i>appkey</i>是在RcServer的数据库中配置的应用的标识符\n\nappid是应用的appkey对应的appid，可通过<b>获取APPID</b>的API获得\n\nuid是用户id，每个appid和uid的组合对应一个独立的名字空间，可以认为是appid对应的名字空间下的子空间\n\n此API需要一个特定的header，指定源目录路径\n\n####请求参数\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>参数名</th>\n\t\t<th>描述</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>recursive</td>\n\t\t<td>1：递归创建目的目录的父目录<br>0：不递归创建目的目录的父目录</td>\n\t</tr>\n</table>\n\n####应答\n\n无\n\n####返回的状态码\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>HTTP状态码</th>\n\t\t<th>描述</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>200 Ok</td>\n\t\t<td>操作成功</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>400 Bad Request</td>\n\t\t<td>目录/文件名不合规范</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>401 Unauthorized</td>\n\t\t<td>无权限（目前每个应用只对自己的appid下的空间拥有修改权限）</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>403 Forbidden</td>\n\t\t<td>移动到子目录中</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>404 Not Found</td>\n\t\t<td>源目录或父目录不存在，或目的目录已存在，或目的父目录不存在</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>500 Internal Server Error</td>\n\t\t<td>TFS或Nginx服务器内部错误</td>\n\t</tr>\n</table>\n\n####例子\n\n下面这个请求，将会使用tfs这个appkey在appid为1，uid为1234的名字空间下将目录/dir_src重命名为/dir_dest：\n\n<pre>\nPOST /v2/tfs/1/1234/dir/dir_dest HTTP/1.1\nHost: 10.0.0.1:7500\nDate: Sat, 30 Jun 2012 05:33:05 GMT\nx-ali-move-source: /dir_src\n</pre>\n\n对应的应答将会是：\n\n<pre>\nHTTP/1.1 200 OK\nServer: Tengine/1.3.0\nDate: Sat, 30 Jun 2012 05:33:05 GMT\nContent-Length: 0\nConnection: keep-alive\n</pre>\n\n###列目录（ls_dir）\n\n####描述\n\n此API用于列出目录下所有子目录和文件，并以json数组的格式返回。\n\n####语法\n\n>GET /<b>v2</b>/<i>appkey</i>/<b>metadata</b>/<i>appid</i>/<i>uid</i>/<b>dir</b>/<i>dir_name</i>  HTTP/1.1\n\n>Host: <i>10.0.0.1:7500</i>\n\n>Date: <i>date</i>\n\n其中<i>appkey</i>是在RcServer的数据库中配置的应用的标识符\n\nappid是应用的appkey对应的appid，可通过<b>获取APPID</b>的API获得\n\nuid是用户id，每个appid和uid的组合对应一个独立的名字空间，可以认为是appid对应的名字空间下的子空间\n\n####请求参数\n\n无\n\n####应答\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>名称</th>\n\t\t<th>描述</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>NAME</td>\n\t\t<td>文件/目录名</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>PID</td>\n\t\t<td>文件/目录的父目录的id</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>ID</td>\n\t\t<td>文件/目录的id</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>SIZE</td>\n\t\t<td>文件的大小</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>IS_FILE</td>\n\t\t<td>是否是文件</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>CREATE_TIME</td>\n\t\t<td>文件/目录的创建时间</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>MODIFY_TIME</td>\n\t\t<td>文件/目录的最后修改时间</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>VER_NO</td>\n\t\t<td>文件/目录版本号</td>\n\t</tr>\n</table>\n\n####返回的状态码\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>HTTP状态码</th>\n\t\t<th>描述</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>200 Ok</td>\n\t\t<td>操作成功</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>400 Bad Request</td>\n\t\t<td>目录/文件名不合规范</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>404 Not Found</td>\n\t\t<td>目录或父目录不存在</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>500 Internal Server Error</td>\n\t\t<td>TFS或Nginx服务器内部错误</td>\n\t</tr>\n</table>\n\n####例子\n\n下面这个请求，将会使用tfs这个appkey在appid为1，uid为1234的名字空间下列出目录“/”下的所有子目录和文件：\n\n<pre>\nGET /v2/tfs/metadata/1/1234/dir/ HTTP/1.1\nHost: 10.0.0.1:7500\nDate: Sat, 30 Jun 2012 05:42:25 GMT\n</pre>\n\n对应的应答将会是：\n\n<pre>\nHTTP/1.1 200 OK\nServer: Tengine/1.3.0\nDate: Sat, 30 Jun 2012 05:42:25 GMT\nContent-Type: application/json\nTransfer-Encoding: chunked\nConnection: keep-alive\n\n[\n    {\n        \"NAME\": \"d_0\",\n        \"PID\": 635213,\n        \"ID\": 635218,\n        \"SIZE\": 0,\n        \"IS_FILE\": false,\n        \"CREATE_TIME\": \"Wed, 27 Jun 2012 10:32:04 UTC+0800\",\n        \"MODIFY_TIME\": \"Wed, 27 Jun 2012 11:29:30 UTC+0800\",\n        \"VER_NO\": 0\n    },\n    {\n        \"NAME\": \"d_3\",\n        \"PID\": 635213,\n        \"ID\": 635219,\n        \"SIZE\": 0,\n        \"IS_FILE\": false,\n        \"CREATE_TIME\": \"Wed, 27 Jun 2012 10:36:17 UTC+0800\",\n        \"MODIFY_TIME\": \"Sat, 30 Jun 2012 13:32:53 UTC+0800\",\n        \"VER_NO\": 0\n    },\n    {\n        \"NAME\": \"file_1\",\n        \"PID\": -9223372036854140595,\n        \"ID\": 0,\n        \"SIZE\": 222,\n        \"IS_FILE\": true,\n        \"CREATE_TIME\": \"Wed, 27 Jun 2012 17:13:23 UTC+0800\",\n        \"MODIFY_TIME\": \"Wed, 27 Jun 2012 17:25:03 UTC+0800\",\n        \"VER_NO\": 1\n    }\n]\n</pre>\n\n###查看目录是否存在\n\n####描述\n\n此API用于查看指定目录是否存在\n\n####语法\n\n>HEAD /<b>v2</b>/<i>appkey</i>/<i>appid</i>/<i>uid</i>/<b>dir</b>/<i>dir_name</i>  HTTP/1.1\n\n>Host: <i>10.0.0.1:7500</i>\n\n>Date: <i>date</i>\n\n其中<i>appkey</i>是在RcServer的数据库中配置的应用的标识符\n\nappid是应用的appkey对应的appid，可通过<b>获取APPID</b>的API获得\n\nuid是用户id，每个appid和uid的组合对应一个独立的名字空间，可以认为是appid对应的名字空间下的子空间\n\n####请求参数\n\n无\n\n####应答\n\n无\n\n####返回的状态码\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>HTTP状态码</th>\n\t\t<th>描述</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>200 Ok</td>\n\t\t<td>目录存在</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>400 Bad Request</td>\n\t\t<td>目录名不合规范</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>404 Not Found</td>\n\t\t<td>目录不存在</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>500 Internal Server Error</td>\n\t\t<td>TFS或Nginx服务器内部错误</td>\n\t</tr>\n</table>\n\n####例子\n\n下面这个请求，将会使用tfs这个appkey在appid为1，uid为1234的名字空间下查看目录“/dir_1”是否存在：\n\n<pre>\nHEAD /v2/tfs/1/1234/dir/dir_1 HTTP/1.1\nHost: 10.0.0.1:7500\nDate: Sat, 30 Jun 2012 05:42:25 GMT\n</pre>\n\n假如目录存在，对应的应答将会是：\n\n<pre>\nHTTP/1.1 200 OK\nServer: Tengine/1.3.0\nDate: Sat, 30 Jun 2012 05:42:25 GMT\nContent-Length: 0\nConnection: keep-alive\n</pre>\n\n###创建文件(create_file)\n\n####描述\n\n此API用于创建一个文件\n\n####语法\n>POST /<b>v2</b>/<i>appkey</i>/<i>appid</i>/<i>uid</i>/<b>file</b>/<i>file_name</i>  HTTP/1.1\n\n>Host: <i>10.0.0.1:7500</i>\n\n>Date: <i>date</i>\n\n其中<i>appkey</i>是在RcServer的数据库中配置的应用的标识符\n\nappid是应用的appkey对应的appid，可通过<b>获取APPID</b>的API获得\n\nuid是用户id，每个appid和uid的组合对应一个独立的名字空间，可以认为是appid对应的名字空间下的子空间\n\n####请求参数\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>参数名</th>\n\t\t<th>描述</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>recursive</td>\n\t\t<td>1：递归创建父目录<br>0：不递归创建父目录</td>\n\t</tr>\n</table>\n\n####应答\n\n无\n\n####返回的状态码\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>HTTP状态码</th>\n\t\t<th>描述</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>201 Created</td>\n\t\t<td>创建成功</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>400 Bad Request</td>\n\t\t<td>文件名不合规范</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>401 Unauthorized</td>\n\t\t<td>无权限（目前每个应用只对自己的appid下的空间拥有修改权限）</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>403 Forbidden</td>\n\t\t<td>超过最大子目录数、子文件数或目录深度</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>404 Not Found</td>\n\t\t<td>父目录不存在</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>409 Conflict</td>\n\t\t<td>文件已存在</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>500 Internal Server Error</td>\n\t\t<td>TFS或Nginx服务器内部错误</td>\n\t</tr>\n</table>\n\n####例子\n\n下面这个请求，将会使用tfs这个appkey在appid为1，uid为1234的名字空间下创建一个/file_1的文件：\n\n<pre>\nPOST /v2/tfs/1/1234/file/file_1 HTTP/1.1\nHost: 10.0.0.1:7500\nDate: Fri, 30 Nov 2012 03:05:00 GMT\n</pre>\n\n对应的应答将会是：\n\n<pre>\nHTTP/1.1 201 Created\nServer: Tengine/1.3.0\nDate: Wed, 27 Jun 2012 14:59:27 GMT\nContent-Length: 0\nConnection: keep-alive\n</pre>\n\n###写文件(write_file)\n\n####描述\n\n此API用于向一个文件写数据。自定义文件名支持多次写入，支持pwrite（指定偏移写，可通过offset参数指定偏移量，支持文件空洞），默认为追加写。\n\n注：不支持更新已有数据\n\n####语法\n\n>PUT /<b>v2</b>/<i>appkey</i>/<i>appid</i>/<i>uid</i>/<b>file</b>/<i>file_name</i>  HTTP/1.1\n\n>Host: <i>10.0.0.1:7500</i>\n\n>Content-Length: <i>length</i>\n\n>Date: <i>date</i>\n\n其中<i>appkey</i>是在RcServer的数据库中配置的应用的标识符\n\nappid是应用的appkey对应的appid，可通过<b>获取APPID</b>的API获得\n\nuid是用户id，每个appid和uid的组合对应一个独立的名字空间，可以认为是appid对应的名字空间下的子空间\n\n####请求参数\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>参数名</th>\n\t\t<th>描述</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>offset</td>\n\t\t<td>要写入的偏移</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>size</td>\n\t\t<td>要写入的数据长度</td>\n\t</tr>\n</table>\n\n####应答\n\n无\n\n####返回的状态码\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>HTTP状态码</th>\n\t\t<th>描述</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>200 Ok</td>\n\t\t<td>操作成功</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>400 Bad Request</td>\n\t\t<td>文件名不合规范或参数不合法</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>401 Unauthorized</td>\n\t\t<td>无权限（目前每个应用只对自己的appid下的空间拥有修改权限）</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>404 Not Found</td>\n\t\t<td>文件不存在</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>409 Conflict</td>\n\t\t<td>写偏移处已存在数据</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>500 Internal Server Error</td>\n\t\t<td>TFS或Nginx服务器内部错误</td>\n\t</tr>\n</table>\n\n####例子\n\n下面这个请求，将会使用tfs这个appkey向appid为1，uid为1234的名字空间下的文件/file_1写入数据：\n\n<pre>\nPUT /v2/tfs/1/1234/file/file_1 HTTP/1.1\nHost: 10.0.0.1:7500\nContent-Length: 222\nDate: Wed, 27 Jun 2012 14:59:27 GMT\n\n[Data]\n</pre>\n\n对应的应答将会是：\n\n<pre>\nHTTP/1.1 200 OK\nServer: Tengine/1.3.0\nDate: Wed, 27 Jun 2012 14:59:27 GMT\nContent-Length: 0\nConnection: keep-alive\n</pre>\n\n###读文件(read_file)\n\n####描述\n\n此API用于从一个文件中读数据。\n\n####语法\n\n>GET /<b>v2</b>/<i>appkey</i>/<i>appid</i>/<i>uid</i>/<b>file</b>/<i>file_name</i>  HTTP/1.1\n\n>Host: <i>10.0.0.1:7500</i>\n\n>Date: <i>date</i>\n\n其中<i>appkey</i>是在RcServer的数据库中配置的应用的标识符\n\nappid是应用的appkey对应的appid，可通过<b>获取APPID</b>的API获得\n\nuid是用户id，每个appid和uid的组合对应一个独立的名字空间，可以认为是appid对应的名字空间下的子空间\n\n####请求参数\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>参数名</th>\n\t\t<th>描述</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>offset</td>\n\t\t<td>要读取数据在文件中的偏移</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>size</td>\n\t\t<td>要读取的数据长度</td>\n\t</tr>\n</table>\n\n####应答\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>名称</th>\n\t\t<th>描述</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>data</td>\n\t\t<td>数据</td>\n\t</tr>\n</table>\n\n####返回的状态码\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>HTTP状态码</th>\n\t\t<th>描述</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>200 Ok</td>\n\t\t<td>操作成功</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>400 Bad Request</td>\n\t\t<td>文件名不合规范或参数不合法</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>404 Not Found</td>\n\t\t<td>文件不存在</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>500 Internal Server Error</td>\n\t\t<td>TFS或Nginx服务器内部错误</td>\n\t</tr>\n</table>\n\n####例子\n\n下面这个请求，将会使用tfs这个appkey从appid为1，uid为1234的名字空间下读取文件/file_1（整个文件）：\n\n<pre>\nGET /v2/tfs/1/1234/file/file_1 HTTP/1.1\nHost: 10.0.0.1:7500\nDate: Wed, 27 Jun 2012 14:59:27 GMT\n</pre>\n\n对应的应答将会是：\n\n<pre>\nHTTP/1.1 200 OK\nServer: Tengine/1.3.0\nDate: Wed, 27 Jun 2012 14:59:27 GMT\nContent-Length: 222\nConnection: keep-alive\n\n[data]\n</pre>\n\n###删除文件(rm_file)\n\n####描述\n\n此API用于删除一个文件\n\n####语法\n\n>DELETE /<b>v2</b>/<i>appkey</i>/<i>appid</i>/<i>uid</i>/<b>file</b>/<i>file_name</i>  HTTP/1.1\n\n>Host: <i>10.0.0.1:7500</i>\n\n>Date: <i>date</i>\n\n其中<i>appkey</i>是在RcServer的数据库中配置的应用的标识符\n\nappid是应用的appkey对应的appid，可通过<b>获取APPID</b>的API获得\n\nuid是用户id，每个appid和uid的组合对应一个独立的名字空间，可以认为是appid对应的名字空间下的子空间\n\n####请求参数\n\n无\n\n####应答\n\n无\n\n####返回的状态码\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>HTTP状态码</th>\n\t\t<th>描述</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>200 Ok</td>\n\t\t<td>操作成功</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>400 Bad Request</td>\n\t\t<td>文件名不合规范</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>401 Unauthorized</td>\n\t\t<td>无权限（目前每个应用只对自己的appid下的空间拥有修改权限）</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>404 Not Found</td>\n\t\t<td>文件不存在</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>500 Internal Server Error</td>\n\t\t<td>TFS或Nginx服务器内部错误</td>\n\t</tr>\n</table>\n\n####例子\n\n下面这个请求，将会使用tfs这个appkey在appid为1，uid为1234的名字空间下删除/file_1文件：\n\n<pre>\nDELETE /v2/tfs/1/1234/file/file_1 HTTP/1.1\nHost: 10.0.0.1:7500\nDate: Thu, 28 Jun 2012 08:12:13 GMT\n</pre>\n\n对应的应答将会是：\n\n<pre>\nHTTP/1.1 200 OK\nServer: Tengine/1.3.0\nDate: Thu, 28 Jun 2012 08:12:13 GMT\nContent-Length: 0\nConnection: keep-alive\n</pre>\n\n###移动/重命名文件（mv_file）\n\n####描述\n\n此API用于移动或重命名一个文件\n\n####语法\n\n>POST /<b>v2</b>/<i>appkey</i>/<i>appid</i>/<i>uid</i>/<b>file</b>/<i>dest_file_name</i>  HTTP/1.1\n\n>Host: <i>10.0.0.1:7500</i>\n\n>Date: <i>date</i>\n\n>x-ali-move-source: /<i>src_file_name</i>\n\n其中<i>appkey</i>是在RcServer的数据库中配置的应用的标识符\n\nappid是应用的appkey对应的appid，可通过<b>获取APPID</b>的API获得\n\nuid是用户id，每个appid和uid的组合对应一个独立的名字空间，可以认为是appid对应的名字空间下的子空间\n\n此API需要一个特定的header，指定要移动或重命名的源文件路径\n\n####请求参数\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>参数名</th>\n\t\t<th>描述</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>recursive</td>\n\t\t<td>1：递归创建目的文件的父目录<br>0：不递归创建目的文件的父目录</td>\n\t</tr>\n</table>\n\n####应答\n\n无\n\n####返回的状态码\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>HTTP状态码</th>\n\t\t<th>描述</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>200 Ok</td>\n\t\t<td>操作成功</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>400 Bad Request</td>\n\t\t<td>文件名不合规范或源文件和目的文件相同</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>401 Unauthorized</td>\n\t\t<td>无权限（目前每个应用只对自己的appid下的空间拥有修改权限）</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>404 Not Found</td>\n\t\t<td>源文件不存在，或目的文件已存在，或目的文件父目录不存在</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>500 Internal Server Error</td>\n\t\t<td>TFS或Nginx服务器内部错误</td>\n\t</tr>\n</table>\n\n####例子\n\n下面这个请求，将会使用tfs这个appkey在appid为1，uid为1234的名字空间下将文件/file_src重命名为/file\\_dest：\n\n<pre>\nPOST /v2/tfs/1/1234/file/file_dest HTTP/1.1\nHost: 10.0.0.1:7500\nDate: Sat, 30 Jun 2012 05:33:05 GMT\nx-ali-move-source: /file_src\n</pre>\n\n对应的应答将会是：\n\n<pre>\nHTTP/1.1 200 OK\nServer: Tengine/1.3.0\nDate: Sat, 30 Jun 2012 05:33:05 GMT\nContent-Length: 0\nConnection: keep-alive\n</pre>\n\n###列文件元信息（ls_file）\n\n####描述\n\n此API用于列出文件的元信息\n\n####语法\n\n>GET /<b>v2</b>/<i>appkey</i>/<b>metadata</b>/<i>appid</i>/<i>uid</i>/<b>file</b>/<i>file_name</i>  HTTP/1.1\n\n>Host: <i>10.0.0.1:7500</i>\n\n>Date: <i>date</i>\n\n其中<i>appkey</i>是在RcServer的数据库中配置的应用的标识符\n\nappid是应用的appkey对应的appid，可通过<b>获取APPID</b>的API获得\n\nuid是用户id，每个appid和uid的组合对应一个独立的名字空间，可以认为是appid对应的名字空间下的子空间\n\n####请求参数\n\n无\n\n####应答\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>名称</th>\n\t\t<th>描述</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>NAME</td>\n\t\t<td>文件名(绝对路径）</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>PID</td>\n\t\t<td>文件的父目录的id</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>ID</td>\n\t\t<td>文件的id</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>SIZE</td>\n\t\t<td>文件的大小</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>IS_FILE</td>\n\t\t<td>是否是文件</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>CREATE_TIME</td>\n\t\t<td>文件的创建时间</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>MODIFY_TIME</td>\n\t\t<td>文件的最后修改时间</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>VER_NO</td>\n\t\t<td>文件的版本号</td>\n\t</tr>\n</table>\n\n####返回的状态码\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>HTTP状态码</th>\n\t\t<th>描述</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>200 Ok</td>\n\t\t<td>操作成功</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>400 Bad Request</td>\n\t\t<td>文件名不合规范</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>404 Not Found</td>\n\t\t<td>文件不存在</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>500 Internal Server Error</td>\n\t\t<td>TFS或Nginx服务器内部错误</td>\n\t</tr>\n</table>\n\n####例子\n\n下面这个请求，将会使用tfs这个appkey在appid为1，uid为1234的名字空间下列出文件“/file_1”的元信息：\n\n<pre>\nGET /v2/tfs/metadata/1/1234/file/file_1 HTTP/1.1\nHost: 10.0.0.1:7500\nDate: Sat, 30 Jun 2012 05:42:25 GMT\n</pre>\n\n对应的应答将会是：\n\n<pre>\nHTTP/1.1 200 OK\nServer: Tengine/1.3.0\nDate: Sat, 30 Jun 2012 05:42:25 GMT\nContent-Type: application/json\nTransfer-Encoding: chunked\nConnection: keep-alive\n\n{\n    \"NAME\": \"/file_1\",\n    \"PID\": 635213,\n    \"ID\": 0,\n    \"SIZE\": 298481,\n    \"IS_FILE\": true,\n    \"CREATE_TIME\": \"Fri, 15 Jun 2012 09:37:39 UTC+0800\",\n    \"MODIFY_TIME\": \"Sat, 30 Jun 2012 23:57:38 UTC+0800\",\n    \"VER_NO\": 0\n}\n</pre>\n\n###查看文件是否存在\n\n####描述\n\n此API用于查看指定文件是否存在\n\n####语法\n\n>HEAD /<b>v2</b>/<i>appkey</i>/<i>appid</i>/<i>uid</i>/<b>file</b>/<i>file_name</i>  HTTP/1.1\n\n>Host: <i>10.0.0.1:7500</i>\n\n>Date: <i>date</i>\n\n其中<i>appkey</i>是在RcServer的数据库中配置的应用的标识符\n\nappid是应用的appkey对应的appid，可通过<b>获取APPID</b>的API获得\n\nuid是用户id，每个appid和uid的组合对应一个独立的名字空间，可以认为是appid对应的名字空间下的子空间\n\n####请求参数\n\n无\n\n####应答\n\n无\n\n####返回的状态码\n\n<table>\n\t<tr align=\"left\">\n\t\t<th>HTTP状态码</th>\n\t\t<th>描述</th>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>200 Ok</td>\n\t\t<td>文件存在</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>400 Bad Request</td>\n\t\t<td>文件名不合规范</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>404 Not Found</td>\n\t\t<td>文件不存在</td>\n\t</tr>\n\t<tr align=\"left\">\n\t\t<td>500 Internal Server Error</td>\n\t\t<td>TFS或Nginx服务器内部错误</td>\n\t</tr>\n</table>\n\n####例子\n\n下面这个请求，将会使用tfs这个appkey在appid为1，uid为1234的名字空间下查看文件“/file_1”是否存在：\n\n<pre>\nHEAD /v2/tfs/1/1234/file/file_1 HTTP/1.1\nHost: 10.0.0.1:7500\nDate: Sat, 30 Jun 2012 05:42:25 GMT\n</pre>\n\n假如文件存在，对应的应答将会是：\n\n<pre>\nHTTP/1.1 200 OK\nServer: Tengine/1.3.0\nDate: Sat, 30 Jun 2012 05:42:25 GMT\nContent-Length: 0\nConnection: keep-alive\n</pre>\n"
  },
  {
    "path": "docs/modules/ngx_backtrace_module.md",
    "content": "Name\n====\n\n* backtrace module\n\nDescription\n===========\n\n* It can be used to dump backtrace of nginx in case a worker process exits abnormally, e.g. when some signal is received (SIGABR, SIGBUS, SIGFPE, SIGILL, SIGIOT, SIGSEGV). It's quite handy for debugging purpose.\n* This module requires the backtrace(3) function in glibc. You can't enable it on systems lack of this function (FreeBSD, Darwin).\n\nDirectives\n==========\n\nbacktrace_log\n-------------\n\n**Syntax**: *backtrace_log log_path*\n\n**Default**: *backtrace_log error.log*\n\n**Context**: *main*\n\nSpecify the log file name of backtrace.\nbacktrace_log /path/to/backtrace.log\n\nbacktrace_max_stack_size\n------------------------\n\n**Syntax**: *backtrace_max_stack_size size*\n\n**Default**: *backtrace_max_stack_size 30*\n\n**Context**: *main*\n\nSpecify the maximum stack depth for backtrace\n"
  },
  {
    "path": "docs/modules/ngx_debug_pool.md",
    "content": "ngx_debug_pool\n==============\n\nThis module provides access to information of memory usage for nginx/tengine memory pool.\n\nExample\n=======\n\nget information of worker process\n---------------------------------\n\n```\n http {\n    server {\n        listen 80;\n\n        location = /debug_pool {\n            debug_pool;\n        }\n    }\n }\n```\n\nRequesting URI /debug_pool, you will get information of memory usage for worker process which gets this request.  \nThe output page may look like as follows:\n\n```\n$ curl http://localhost:80/debug_pool\npid:18821\nsize:      223784 num:           2 cnum:           1 lnum:          10 ngx_init_cycle\nsize:        1536 num:           4 cnum:           1 lnum:          10 ngx_event_accept\nsize:           0 num:           1 cnum:           0 lnum:           0 ngx_http_lua_create_fake_request\nsize:           0 num:           1 cnum:           0 lnum:           0 main\nsize:           0 num:           1 cnum:           0 lnum:           0 ngx_http_lua_create_fake_connection\nsize:           0 num:           1 cnum:           0 lnum:           6 ngx_http_server_names\nsize:        8192 num:           4 cnum:           1 lnum:           0 ngx_http_create_request\nsize:           0 num:           1 cnum:           0 lnum:           0 ngx_http_lua_init_worker\nsize:       228KB num:          15 cnum:           3 lnum:          26 [SUMMARY]\n```\n\n\nget information of specific process\n-----------------------------------\n\nAlso you can use gdb script [debug_pool.gdb](https://github.com/alibaba/tengine/blob/master/modules/ngx_debug_pool/debug_pool.gdb) to get information of specific process.  \nSome process cannot handle HTTP request, such as master process or [tengine Proc process](https://github.com/alibaba/tengine/blob/master/docs/modules/ngx_procs_module.md).  \nThe following example shows how to get information of master process.\n\n```\n$ gdb -q -x debug_pool.gdb -p <pid of master process>\n(gdb) debug_pool\nsize:       16384 num:           1 cnum:           1 lnum:           0 ngx_http_user_agent_create_main_conf:24\nsize:           0 num:           1 cnum:           0 lnum:           0 main:224\nsize:      150312 num:           2 cnum:           1 lnum:          13 ngx_init_cycle:824\nsize:      166696 num:           4 cnum:           2 lnum:          13 [SUMMARY]\n```\n\nData\n====\n\nEvery line except the last one of output content has the same format, as follows:\n\n\"__size__: %12u __num__: %12u __cnum__: %12u __lnum__: %12u __\\<function name\\>__\"\n\n* __size__: size of current used memory of this pool\n* __num__:  number of created pool (including current used pool and destroyed pool)\n* __cnum__: number of current used pool\n* __lnum__: number of calling ngx_palloc_large()\n* __funcion name__: which nginx/tengine C function creates this pool\n  * With function name of pool creator, we can know memory usage of every module, for example:\n  * pool created by `ngx_http_create_request` is used for one HTTP request.\n    * Because most modules allocates memory from this pool directly, it's hard to distinguish between them.\n  * pool created by `ngx_event_accept` is used for one TCP connection.\n  * pool created by `ngx_init_cycle` is used for parsing nginx/tengine configuration and keeping other global data structures.\n  * pool created by `ngx_http_lua_init_worker` is used for conf.temp_pool of directive [init_worker_by_lua](https://github.com/openresty/lua-nginx-module#init_worker_by_lua).\n  * ...\n\nLast line of output content summarizes the information of all memory pools.\n\nInstall\n=======\n\n```\n$ ./configure --add-module=./modules/ngx_debug_pool\n$ make && make install\n```\n\nDirective\n=========\n\nSyntax: **debug_pool**\n\nDefault: `none`\n\nContext: `server, location`\n\nThe information of nginx/tengine memory pool usage will be accessible from the surrounding location.\n\nException\n=========\n\nMemory allocated without using memory pool does not get taken into account with this module.  \nFor example, ngx_http_spdy_module allocates a temporary buffer via malloc(ngx_alloc) for raw data of SYN_REPLY frame. After being compressed, this buffer will be freed immediately.\n"
  },
  {
    "path": "docs/modules/ngx_debug_pool_cn.md",
    "content": "ngx_debug_pool\n==============\n\n该模块可以提供nginx/tengine内存池占用内存的状态信息。\n\n示例\n====\n\n获取worker进程的信息\n--------------------\n\n```\n http {\n    server {\n        listen 80;\n\n        location = /debug_pool {\n            debug_pool;\n        }\n    }\n }\n```\n\n请求URI /debug_pool，可以获取到接受该请求的worker进程的内存使用情况。  \n页面输出如下：\n\n```\n$ curl http://localhost:80/debug_pool\npid:18821\nsize:      223784 num:           2 cnum:           1 lnum:          10 ngx_init_cycle\nsize:        1536 num:           4 cnum:           1 lnum:          10 ngx_event_accept\nsize:           0 num:           1 cnum:           0 lnum:           0 ngx_http_lua_create_fake_request\nsize:           0 num:           1 cnum:           0 lnum:           0 main\nsize:           0 num:           1 cnum:           0 lnum:           0 ngx_http_lua_create_fake_connection\nsize:           0 num:           1 cnum:           0 lnum:           6 ngx_http_server_names\nsize:        8192 num:           4 cnum:           1 lnum:           0 ngx_http_create_request\nsize:           0 num:           1 cnum:           0 lnum:           0 ngx_http_lua_init_worker\nsize:       228KB num:          15 cnum:           3 lnum:          26 [SUMMARY]\n```\n\n\n获取指定进程的信息\n------------------\n\n你可以使用gdb脚本[debug_pool.gdb](https://github.com/alibaba/tengine/blob/master/modules/ngx_debug_pool/debug_pool.gdb)来获取指定进程的内存使用情况。  \n某些进程无法处理HTTP请求，列如master进程和[tengine Proc 进程](https://github.com/alibaba/tengine/blob/master/docs/modules/ngx_procs_module.md)。  \n下面的示例展示如何获取master进程的内存使用情况。\n\n```\n$ gdb -q -x debug_pool.gdb -p <pid of master process>\n(gdb) debug_pool\nsize:       16384 num:           1 cnum:           1 lnum:           0 ngx_http_user_agent_create_main_conf:24\nsize:           0 num:           1 cnum:           0 lnum:           0 main:224\nsize:      150312 num:           2 cnum:           1 lnum:          13 ngx_init_cycle:824\nsize:      166696 num:           4 cnum:           2 lnum:          13 [SUMMARY]\n```\n\n数据\n====\n\n除了最后一行的每一行的输出内容都有相同的格式，如下：\n\n\"__size__: %12u __num__: %12u __cnum__: %12u __lnum__: %12u __\\<function name\\>__\"\n\n* __size__: 当前内存池占用的内存\n* __num__:  内存池创建的个数（包括当前正在使用的内存池数量和已经被释放的内存池数量）\n* __cnum__: 当前正在使用的内存池数量\n* __lnum__: 该类内存池调用ngx_palloc_large()次数\n* __funcion name__: 创建该内存池的nginx/tengine C函数的函数名\n  * 通过创建该内存池的函数的函数名，我们可以知道各个模块的内存使用情况，列如：\n  * `ngx_http_create_request`创建的内存池用于HTTP请求。\n    * 因为大多数模块直接从该内存池上分配内存，所以很难区分具体哪个模块使用了内存。\n  * `ngx_event_accept`创建的内存池用于TCP连接。\n  * `ngx_init_cycle`创建的内存池用于解析nginx/tengine的配置和保存其他全局数据结构。\n  * `ngx_http_lua_init_worker`用于指令[init_worker_by_lua](https://github.com/openresty/lua-nginx-module#init_worker_by_lua)。\n  * ...\n\n最后一行的输出内容汇总了所有内存池的信息。\n\n安装\n====\n\n```\n$ ./configure --add-module=./modules/ngx_debug_pool\n$ make && make install\n```\n\n指令\n====\n\nSyntax: **debug_pool**\n\nDefault: `none`\n\nContext: `server, location`\n\nnginx/tengine的内存池使用信息可以通过该location访问到。\n\n例外\n====\n\n不通过内存池分配的内存不会被该模块统计到。  \n例如，ngx_http_spdy_module模块会通过malloc(ngx_alloc)为SYN_REPLY帧的生数据分配一块临时缓冲区，该缓冲区在此数据被用于压缩后会被立即释放。\n"
  },
  {
    "path": "docs/modules/ngx_dso_module.md",
    "content": "Name\n====\n\n* Dynamic Module Loading Support (**DSO**)\n\nDescription\n===========\n\n* You can choose which functionalities to include by selecting a set of modules. A module will be compiled as a Dynamic Shared Object (**DSO**) that exists from the main tengine binary. So you don't have to recompile tengine when you want to add or enable a functionality to it.\n\n* If you want to enable a standard module, you can enable it via configure's option while compiling tengine, for instance, --with-http\\_example_module or --with-http\\_example\\_module=shared. Run *./configure --help* for more details.\n\n* The maximum of dynamically loaded modules is limited to 128.\n\n* For now, only HTTP modules can be dynamically loaded.\n\n* This feature is tested only on Linux/FreeBSD/MacOS.\n\n\nExample\n===========\n\n    worker_processes  1;\n    \n    dso {\n         load ngx_http_lua_module.so;\n         load ngx_http_memcached_module.so;\n    }\n\n    events {\n       worker_connections  1024;\n    }\n\nDirectives\n==========\n\n\npath\n------------------------\n\n**Syntax**: *path path*\n\n**Default**: *NGX\\_PREFIX/modules*\n\n**Context**: *dso*\n\nThis directive specifies the default path (prefix) of DSO modules.\n\nExample:\n\n    path /home/dso/module;\n\nSets the default path to */home/dso/module*.\n\n\nload\n------------------------\n\n**Syntax**: *load [module_name] \\[module_path]*\n\n**Default**: *none*\n\n**Context**: *dso*\n\nThe **load** directive loads the shared object file and enables the module. *module\\_name* is the name of the DSO module, and *module\\_path* is the path of the DSO module.\n\nThe order in which the module is searched is as follows:\n\n* the absolute path.\n* relative path to the prefix specified by the 'path' directive.\n* relative path to the default path (NGX\\_PREFIX/modules or path which is specified by the '--dso-path' configure option).\n\n\nExample:\n\n    load ngx_http_empty_gif_module  ngx_http_empty_gif_module.so;\n    load ngx_http_test_module;\n    load ngx_http_test2_module.so;\n\nIt will load the ngx\\_http\\_empty\\_gif\\_module from ngx\\_http\\_empty\\_gif\\_module.so, ngx\\_http\\_test_module and ngx\\_http\\_test2\\_module from ngx\\_http\\_test\\_module.so and ngx\\_http\\_test2\\_module.so.\n\n\nmodule_stub\n-------------\n\n**Syntax**: *module_stub module_name*\n\n**Default**: *none*\n\n**Context**: *dso*\n\n\nThis directive can insert a module into nginx's module array in order (see conf/module\\_stubs for more details). Note it will change the module runtime order. This directive does not need to be used in most cases. Don't use it or edit the *conf/module\\_stubs* file unless you know what you are doing.\n\nExample:\n\n        module_stub ngx_core_module;\n        module_stub ngx_errlog_module;\n        module_stub ngx_conf_module;\n        module_stub ngx_events_module;\n        module_stub ngx_event_core_module;\n        module_stub ngx_epoll_module;\n        module_stub ngx_openssl_module;\n        module_stub ngx_http_module;\n        module_stub ngx_http_core_module;\n        .......................\n        module_stub ngx_http_addition_filter_module;\n        module_stub ngx_http_my_filter_module;\n\nIt will place ngx\\_http\\_my\\_filter\\_module before ngx\\_http\\_addition\\_filter\\_module.\n\n\ninclude\n-------------\n\n**Syntax**: *include file_name*\n\n**Default**: *none*\n\n**Context**: *dso*\n\nSpecifies a file which contains the module stubs (via the **module_stub** directive).\n\nExample:\n    \n    include module_stubs;\n\nIt will load conf/module_stubs and define the loading order of the modules (via the **module\\_stub** directive).\n\n\nHow to compile a module\n===========\n\nStandard module\n------------------------\nIf you want to enable a standard module after you compiled and installed tengine, you can take these steps as following.\n\n* enable the standard module you wanted in shared mode, for example:\n\n    $ ./configure --with-http_sub_module=shared\n\n* compile it:\n\n    $ make\n\n* install the shared object (*.so):\n\n    $ make dso_install\n\nIt will copy the *.so files to the destination, or you can copy the files you want (in objs/modules) manually to the modules directory.\n\nThird party module\n------------------------\n\nYou can use the __dso_tool__ located in the directory of nginx binary to compile a third party module.\n\nExample:\n\n    ./dso_tool --add-module=/home/dso/lua-nginx-module\n\nIt will compile the ngx_lua module into a shared object, and install it to the default module path. You can specify the destination directory you want install to by the **--dst** option.\n\n\nNote\n===========\n\nRemoved dso_tool tool and dso directive after the Tengine-2.3.0 version, If before using Tengine dso feature, you could switch to the Nginx official load_module directive, detailed reference [document1](http://nginx.org/en/docs/ngx_core_module.html#load_module) 、[document2](https://www.nginx.com/resources/wiki/extending/converting/#compiling-dynamic).\n"
  },
  {
    "path": "docs/modules/ngx_dso_module_cn.md",
    "content": "模块名\n====\n\n*  动态加载模块\n\n描述\n===========\n\n* 这个模块主要是用来运行时动态加载模块，而不用每次都要重新编译Tengine.\n\n* 如果你想要编译官方模块为动态模块，你需要在configure的时候加上类似这样的指令(--with-http\\_xxx_module),./configure --help可以看到更多的细节.\n\n* 如果只想要安装官方模块为动态模块(不安装Nginx)，那么就只需要configure之后，执行 make dso_install命令.\n\n* 动态加载模块的个数限制为128个.\n\n* 如果已经加载的动态模块有修改，那么必须重起Tengine才会生效.\n\n* 只支持HTTP模块.\n\n* 模块 在Linux/FreeeBSD/MacOS下测试成功.\n\n\n例子\n===========\n\n    worker_processes  1;\n    \n    dso {\n         load ngx_http_lua_module.so;\n         load ngx_http_memcached_module.so;\n    }\n\n    events {\n       worker_connections  1024;\n    }\n\n\n指令\n==========\n\npath\n------------------------\n\n**Syntax**: *path path*\n\n**Default**: *none*\n\n**Context**: *dso*\n\npath 主要是设置默认的动态模块加载路径。\n\n例子:\n\n    path /home/dso/module/;\n\n设置默认的动态模块加载路径为/home/dso/module/.\n\n\nload\n------------------------\n\n**Syntax**: *load [module_name] \\[module_path]*\n\n**Default**: *none*\n\n**Context**: *dso*\n\nload命令用于在指定的路径(module\\_path),将指定的模块(module\\_name)动态加载到Nginx中。其中module\\_path和module\\_name可以只写一个,如果没有module\\_path参数，那么默认path是 $(modulename).so.如果没有module\\_name参数，那么默认name就是module\\_path删除掉\".so\"后缀.\n\n对于module\\_path的路径查找，这里是严格按照下面的顺序的\n\n1 module\\_path指定的是绝对路径。\n2 相对于dso\\_path设置的相对路径.\n3 相对于默认的加载路径的相对路径(NGX\\_PREFIX/modules或者说configure时通过--dso-path设置的路径).\n\n例子:\n\n    load ngx_http_empty_gif_module  ngx_http_empty_gif_module.so;\n    load ngx_http_test_module;\n    load ngx_http_test2_module.so;\n\n将会从ngx\\_http\\_empty\\_gif\\_module.so.加载empty\\_gif模块。以及从ngx\\_http\\_test\\_module.so加载ngx\\_http\\_test\\_module模块.第三条指令是从ngx\\_http\\_test2\\_module.so加载ngx\\_http\\_test2\\_module模块.\n\nmodule_stub\n-------------\n\n**Syntax**: *module_stub module_name*\n\n**Default**: *none*\n\n**Context**: *dso*\n\n\n这个指令主要是将你需要的动态模块插入到你所需要的位置(可以看conf/module\\_stubs这个文件),这个命令要很小心使用，因为它将会改变你的模块的运行时顺序(在Nginx中模块都是有严格顺序的).而大多数时候这个命令都是不需要设置的。\n\n例子:\n \n        module_stub ngx_core_module;\n        module_stub ngx_errlog_module;\n        module_stub ngx_conf_module;\n        module_stub ngx_events_module;\n        module_stub ngx_event_core_module;\n        module_stub ngx_epoll_module;\n        module_stub ngx_openssl_module;\n        module_stub ngx_http_module;\n        module_stub ngx_http_core_module;\n        .......................\n        module_stub ngx_http_addition_filter_module;\n        module_stub ngx_http_my_filter_module;\n\n上面这个例子将会插入my\\_filter模块到addition\\_filter之前执行。\n\n\ninclude\n-------------\n\n**Syntax**: *include file_name*\n\n**Default**: *none*\n\n**Context**: *dso*\n\ninclude命令主要用于指定一个文件，这个文件里面包含了对应模块顺序(module_stub指令),有关于module\\_stub指令可以看下面的module\\_stubs部分.\n\n例子:\n\n    include module_stubs\n    \n将会加载conf/module_stubs这个文件，这个文件主要是由(module_stub指令组成).\n\n\n如何编译动态模块\n===========\n\n官方模块\n------------------------\n\n如果你想要在安装完Tengine之后，编译官方模块为动态模块，那么你需要按照如下的步骤:\n\n* 在configure的时候打开你想要编译的模块.\n\n      $ ./configure --with-http_sub_module=shared\n      \n* 编译它.\n\n    $ make\n    \n* 安装动态模块.\n\n    $ make dso_install\n    \n它将会复制动态库文件到你的动态模块目录，或者你也可以手工拷贝动态模块文件(文件是在objs/modules)到你想要加载的目录.\n\n第三方模块\n------------------------\n\n你能够使用dso_tool(在Nginx安装目录的sbin下)这个工具来编译第三方模块.\n\n例子:\n\n    ./dso_tool --add-module=/home/dso/lua-nginx-module\n\n将会编译ngx\\_lua模块为动态库，然后安装到默认的模块路径.如果你想要安装到指定位置，那么需要指定--dst选项(更多的选项请使用dso_tool -h查看).\n\n\n注意事项\n===========\n\n在Tengine-2.3.0版本后废弃Tengine的dso_tool工具以及dso配置指令，若之前有使用Tengine的dso功能、则可以切换到Nginx官方的load_module指令,详细文档[参考1](http://nginx.org/en/docs/ngx_core_module.html#load_module) 和 [参考2](https://www.nginx.com/resources/wiki/extending/converting/#compiling-dynamic)。\n"
  },
  {
    "path": "docs/modules/ngx_http_concat_cn.md",
    "content": "# concat 模块\n\n## 介绍\n\n该模块类似于apache中的mod_concat模块，用于合并多个文件在一个响应报文中。\n\n请求参数需要用两个问号（'??'）例如：\n\n    http://example.com/??style1.css,style2.css,foo/style3.css\n    \n参数中某位置只包含一个‘?’，则'?'后表示文件的版本，例如：\n\n    http://example.com/??style1.css,style2.css,foo/style3.css?v=102234\n\n## 配置\n\n    location /static/css/ {\n        concat on;\n        concat_max_files 20;\n    }\n    \n    location /static/js/ {\n        concat on;\n        concat_max_files 30;\n    }\n\n## 指令\n\n**concat** `on` | `off`\n\n**默认:** `concat off`\n\n**上下文:** `http, server, location` \n     \n在配置的地方使模块有效（失效）\n\n<br/>\n<br/>\n\n**concat_types** `MIME types`\n\n**默认:** `concat_types: application/x-javascript text/css`\n\n**上下文:** `http, server, location`\n\n定义配置的[MIME types](http://en.wikipedia.org/wiki/MIME_type)以及 \"application/x-javascript\" 是可以被接受\n\n<br/>\n<br/>\n\n**concat_unique** `on` | `off`\n\n**默认:** `concat_unique on`\n\n**上下文:** `http, server, location`\n\n定义是否只接受在[MIME types]中的相同类型的文件，例如：\n\n    http://example.com/static/??foo.css,bar/foobaz.js\n如果配置为 'concat_unique on' 那么将返回400，如果配置为'concat_unique off'\n那么将合并两个文件。\n\n<br/>\n<br/>\n\n**concat\\_max\\_files** `number`\n\n**默认:** `concat_max_files 10`\n                \n**上下文:** `http, server, location`\n\n定义最大能接受的文件数量。\n\n<br/>\n<br/>\n\n**concat_delimiter** string\n\n**默认:**  无 \n\n**上下文** 'http, server, location'\n\n定义在文件之间添加分隔符，例如\n\n    http://example.com/??1.js,2.js； \n    如果配置为**concat_delimiter \"\\n\"**响应会在1.js和2.js两个文件之间插入一个换行符('\\n')；\n\n<br/>\n<br/>\n\n**concat_ignore_file_error** 'on | off'\n       \n**默认** 'concat_ignore_file_error off'\n         \n**上下文** 'http, server, location'\n       \n定义模块是否忽略文件不存在（404）或者没有权限（403）错误\n\n## 安装\n\n 1. 编译concat模块\n         \n    configure  [--add-module=modules/ngx_http_concat_module | --add-dynamic-module=modules/ngx_http_concat_module]\n\n    --add-module=modules/ngx_http_concat_module选项，concat模块将被静态编译到tengine中\n\n    --add-dynamic-module=modules/ngx_http_concat_module,concat模块将被编译成动态文件，采用动态模块的方式添加到tengine中\n\n 2. 编译,安装\n\n    make&make install\n \n 3. 配置concat的配置项\n \n 4. 运行\n"
  },
  {
    "path": "docs/modules/ngx_http_core_module.md",
    "content": "# Name #\n\n**ngx\\_http\\_core\\_module**\n\nTengine added some enhancements to this module. The new directives are listed below.\n\n\n# Directives #\n\n## client\\_body\\_buffers ##\n\nSyntax: **client\\_body\\_buffers** `number size`\n\nDefault: 16 4k/8k\n\nContext: `http, server, location`\n                                 \nSpecify the number and size of buffers used when reading non buffered client request body, all the buffers are stored in the memory. Buffers are allocated only on demand. By default, the buffer size is equal to your OS's pagesize. The total buffer size should be larger than `client_body_postpone_size`, otherwise, it will be enlarged by force.\n\n## client\\_body\\_postpone\\_size ##\n\nSyntax: **client\\_body\\_postpone\\_size** `size`\n\nDefault: 64k\n\nContext: `http, server, location`\n\nWhen you turn off the `proxy_request_buffering` or `fastcgi_request_buffering`, Tengine will send the body to backend either it receives more than `size` data or the whole request body has been received. It can save the connection and reduce the network system call number with backend. \n                                 \n## proxy\\_request\\_buffering ##\n\nSyntax: **proxy\\_request\\_buffering** `on | off`\n\nDefault: `on`\n\nContext: `http, server, location`\n\nSpecify the request body will be buffered to the disk or not. If it's off, the request body will be stored in the memory and sent to backend after Tengine receives more than `client_body_postpone_size` data. It can avoid the disk IO with large request body.\n\nBy default in the buffered mode, the whole request body larger than the `client_body_buffer_size` will always be saved into the disk. This behavior may increase the server load greatly with heavy upload application.\n\nNote that, if you turn it off, the nginx retry mechanism with unsuccessful response will be broken after you sent part of the request to backend. It just returns 500 directly when it encounters an unsuccessful response. This directive also breaks these variables: $request_body, $request_body_file. You should not use them any more while their values are incomplete.\n\nAlso note that, enabling spdy will prevent `proxy_request_buffering off` from taking effect.\n\n## fastcgi\\_request\\_buffering ##\n\nSyntax: **fastcgi\\_request\\_buffering** `on | off`\n\nDefault: `on`\n\nContext: `http, server, location`\n\nThe same as `proxy_request_buffering`.\n\n## gzip\\_clear\\_etag ##\n\nSyntax: **gzip\\_clear\\_etag** `on | off`\n\nDefault: `on`\n\nContext: `http, server, location`\n\nDetermines whether gzip module should clear the “ETag” response header field.\n"
  },
  {
    "path": "docs/modules/ngx_http_core_module_cn.md",
    "content": "# 模块名 #\n\n**ngx\\_http\\_core\\_module**\n\nTengine针对此模块进行了增强，下面列出了一些增加的指令。\n\n\n# 指令 #\n\n## client\\_body\\_buffers ##\n\nSyntax: **client\\_body\\_buffers** `number size`\n\nDefault: 16 4k/8k\n\nContext: `http, server, location`\n                                 \n当不缓存上传的请求body到磁盘时，指定每块缓存块大小和数量。所有的缓存块都保存在内存中，并且是按需分配的。默认情况下，缓存块等于系统页的大小。总缓存大小必须大于`client_body_postpone_size`指令的大小。\n\n## client\\_body\\_postpone\\_size ##\n\nSyntax: **client\\_body\\_postpone\\_size** `size`\n\nDefault: 64k\n\nContext: `http, server, location`\n\n当打开`proxy_request_buffering`或`fastcgi_request_buffering`指令，设置不缓存请求body到磁盘时，tengine每当接受到大于`client_body_postpone_size`大小的数据或者整个请求都发送完毕，才会往后端发送数据。这可以减少与后端服务器建立的连接数，并减少网络IO的次数。\n                                 \n## proxy\\_request\\_buffering ##\n\nSyntax: **proxy\\_request\\_buffering** `on | off`\n\nDefault: `on`\n\nContext: `http, server, location`\n\n指定当上传请求body时是否要将body缓存到磁盘。如果设成off，请求body只会被保存到内存，每当tengine接收到大于`client_body_postpone_size`的数据时，就发送这部分数据到后端服务器。\n\n默认情况下，当请求body大于`client_body_buffer_size`时，就会被保存到磁盘。这会增加磁盘IO，对于上传应用来说，服务器的负载会明显增加。\n\n需要注意的是，如果你配置成off且已经发出部分数据，tengine的重试机制就会失效。如果后端返回异常响应，tengine就会直接返回500。此时$request_body，$request_body_file也会不可用，他们保存的可能是不完整的内容。\n\n额外注意的是，当tengine开启了spdy时，`proxy_request_buffering off`不会起效。\n\n## fastcgi\\_request\\_buffering ##\n\nSyntax: **fastcgi\\_request\\_buffering** `on | off`\n\nDefault: `on`\n\nContext: `http, server, location`\n\n用法跟`proxy_request_buffering`指令一样。\n\n## gzip\\_clear\\_etag ##\n\nSyntax: **gzip\\_clear\\_etag** `on | off`\n\nDefault: `on`\n\nContext: `http, server, location`\n\n压缩的时候是否删除\"ETag\"响应头。\n\n"
  },
  {
    "path": "docs/modules/ngx_http_dubbo_module.md",
    "content": "ngx_http_dubbo_module\n====\n\nThis module provides support for the backend Dubbo support after Tengine version 2.3.2.\n[Apache Dubbo™](http://dubbo.apache.org)  is a high-performance, java based open source RPC framework.It is open source by Alibaba, in years of development, it is one of the most popular microservice framework.\n\nThere are two roles Consumer(client) and Provider(Server) in Dubbo. This module is used to make Tengine as a proxy gateway which receives HTTP/HTTPS/HTTP2 requests at the front then as a Dubbo Consumer passes the requests to backend Dubbo Provider service.\n\n\n\n\n```\n  User                 tengine (dubbo_pass)                         Dubbo Service Provider\n    |                          |                                              |\n    |--- GET github.com:443 -->|                                              |\n    |                          |--- Dubbo Multiplexing Binary RPC Request  -->|\n    |                          |                                              |\n    |                          |<-- Dubbo Multiplexing Binary RPC Response ---|\n    |<--    HTTP/1.1 200    ---|                                              |\n```\n\nExample\n=======\n\nTengine Configuration Example\n---------------------\n\n```\nupstream dubbo_backend {\n    multi 1;\n    server 127.0.0.1:20880;\n}\n\nserver {\n    listen 8080;\n    \n    location / {\n        dubbo_pass org.apache.dubbo.demo.DemoService 0.0.0 http_dubbo_tengine dubbo_backend;\n    }\n}\n\n```\n\nDubbo Demo Service Example\n----------------\n### Standard\n\nDubbo Provider need implement this interface, then configure the service name, Service version and service method to ```dubbo_pass``` like this. Tengine will convert HTTP/HTTPS/HTTP2 request to Dubbo interface invoke.\n\n```\nMap<String, Object> dubbo_method(Map<String, Object> context);\n\n```\n\nInput param ```Map<String, Object> context``` with a number of key and value， you can use ```dubbo_pass_set```,```dubbo_pass_all_headers```,```dubbo_pass_body``` directives to div them, last key is the retained field:\n```\nbody: HTTP request Body, value Object type is byte[]\n\n```\n\nFor output param ```Map<String, Object> context```, last key is the retained field:\n```\nbody: HTTP response Body, value Object type is byte[]\nstatue: HTTP response Status, value type is String\n```\n\n\n\n### Extend(Stay tuned for updates)\n\nSupport configure param mapping on Tengine, support invoke any Dubbo Provider method not need any change (Stay tuned for updates).\n\n\nQuickStart\n=======\nThis is a [QuickStart for Tengine Dubbo](https://github.com/apache/dubbo-samples/tree/master/dubbo-samples-tengine)\n\n\nInstall\n=======\n\nBuild Tengine with this module from source:\n\n```\n$ ./configure --add-module=./modules/mod_dubbo --add-module=./modules/ngx_multi_upstream_module --add-module=./modules/mod_config\n$ make && make install\n```\n\nDynamic module support\n\n* mod_dubbo: ```support``` build as a dynamic module\n* ngx_multi_upstream_module: ```no support``` build as a dynamic module\n* mod_config: ```support but no need``` build as a dynamic module\n\n\nDirective\n=========\n\ndubbo_pass\n-------------\nSyntax: **dubbo_pass** *service_name* *service_version* *method* *upstream_name*  \nDefault: `none`  \nContext: `location, if in location` \n\nconfigure use Dubbo protocol proxy to upstream\n\n* *service_name*: Dubbo provider service name\n* *service_version*: Dubbo provider service version\n* *method*: Dubbo provider service method\n* *upstream_name*: backend upstream name\n\nNginx variables can be used as `service_name`, `service_version` and `method`.\n\n```\n# proxy to upstream dubbo_backend\nupstream dubbo_backend {\n    multi 1;\n    server 127.0.0.1:20880;\n}\n\nset $dubbo_service_name \"org.apache.dubbo.demo.DemoService\";\nset $dubbo_service_name \"0.0.0\";\nset $dubbo_service_name \"http_dubbo_nginx\";\n\ndubbo_pass $dubbo_service_name $dubbo_service_version $dubbo_method dubbo_backend;\n```\n\nNotice:\n\n`dubbo_pass` only support multi upstream, must use `multi` configure in upstream, multi param is number of multiplexing connection.\n\n\ndubbo_pass_set\n-------------------\n\nSyntax: **dubbo_pass_set** *key* *value*;\nDefault: `none`\nContext: `location, if in location`\n\nWhen proxy request to backend, need pass this key-value, key and value can contain variables.\n\n```\ndubbo_pass_set username $cookie_user;\n```\n\ndubbo_pass_all_headers\n-----------------------------\n\nSyntax: **dubbo_pass_all_headers** on | off;\nDefault: `off`\nContext: `location, if in location`\n\nEnables or disables passing all http header to backend as key-value.\n\ndubbo_pass_body\n--------------------------\n\nSyntax: **dubbo_pass_body** on | off;\nDefault: `on`\nContext: `location, if in location`\n\nEnables or disables passing request body to backend.\n\ndubbo_heartbeat_interval\n--------------------------\n\nSyntax: **dubbo_heartbeat_interval** *time*;\nDefault: `60s`\nContext: `http, server, location`\n\nDefines a interval for auto sending ping frame to backend.\n\n\ndubbo_bind\n--------------------------\n\nSyntax:\t  **dubbo_bind**  *address* [transparent ] | off;\nDefault: `off`\nContext: `http, server, location`\n\nLike ```proxy_bind```. makes outgoing connections to a Dubbo provider originate from the specified local IP address with an optional port. Parameter value can contain variables. The special value off cancels the effect of the dubbo_bind directive inherited from the previous configuration level, which allows the system to auto-assign the local IP address and port.\n\nThe transparent parameter allows outgoing connections to a Dubbo provider originate from a non-local IP address, for example, from a real IP address of a client:\n```\ndubbo_bind $remote_addr transparent;\n```\nIn order for this parameter to work, it is usually necessary to run nginx worker processes with the superuser privileges. On Linux it is not required as if the transparent parameter is specified, worker processes inherit the CAP_NET_RAW capability from the master process. It is also necessary to configure kernel routing table to intercept network traffic from the Dubbo provider.\n\n\n\ndubbo_socket_keepalive\n--------------------------\n\nSyntax:\t  **dubbo_socket_keepalive**  on | off;\nDefault: `off`\nContext: `http, server, location`\n\nLike ```proxy_socket_keepalive```, configures the \"TCP keepalive\" behavior for outgoing connections to a Dubbo provider. By default, the operating system's settings are in effect for the socket. If the directive is set to the value \"on\", the SO_KEEPALIVE socket option is turned on for the socket.\n\ndubbo_connect_timeout\n--------------------------\n\nSyntax:\t  **dubbo_connect_timeout**  *time*;\nDefault: `60s`\nContext: `http, server, location`\n\nLike ```proxy_connect_timeout```, defines a timeout for establishing a connection with a Dubbo provider. It should be noted that this timeout cannot usually exceed 75 seconds.\n\ndubbo_send_timeout\n--------------------------\n\nSyntax:\t  **dubbo_send_timeout**  *time*;\nDefault: `60s`\nContext: `http, server, location`\n\nLike ```proxy_send_timeout```, sets a timeout for transmitting a request to the Dubbo provider. The timeout is set only between two successive write operations, not for the transmission of the whole request. If the Dubbo provider does not receive anything within this time, the connection is closed.\n\n\ndubbo_read_timeout\n--------------------------\n\nSyntax:\t  **dubbo_read_timeout**  *time*;\nDefault: `60s`\nContext: `http, server, location`\n\nLike ```proxy_read_timeout```, defines a timeout for reading a response from the Dubbo provider. The timeout is set only between two successive read operations, not for the transmission of the whole response. If the Dubbo provider does not transmit anything within this time, the connection is closed.\n\n\ndubbo_intercept_errors\n--------------------------\n\nSyntax:\t  **dubbo_intercept_errors**  on | off;\nDefault: `off`\nContext: `http, server, location`\n\nLike ```proxy_intercept_errors```, determines whether Dubbo provider responses with codes greater than or equal to 300 should be passed to a client or be intercepted and redirected to nginx for processing with the `error_page` directive.\n\n\ndubbo_buffer_size\n--------------------------\n\nSyntax:\t  **dubbo_buffer_size**  *size*;\nDefault: `4k|8k`\nContext: `http, server, location`\n\nLike ```proxy_buffer_size```, sets the size of the buffer used for reading the response received from the Dubbo provider. The response is passed to the client synchronously, as soon as it is received.\n\n\ndubbo_next_upstream\n--------------------------\n\nSyntax:\t  **dubbo_next_upstream**  error | timeout | invalid_header | http_500 | http_502 | http_503 | http_504 | http_403 | http_404 | http_429 | non_idempotent | off ...;\nDefault: `error timeout`\nContext: `http, server, location`\n\nLike ```proxy_next_upstream```, specifies in which cases a request should be passed to the next server.\n\ndubbo_next_upstream_tries\n--------------------------\n\nSyntax:\t  **dubbo_next_upstream_tries**  *number*;\nDefault: `0`\nContext: `http, server, location`\n\nLike ```proxy_next_upstream_tries```, limits the number of possible tries for passing a request to the next server. The 0 value turns off this limitation.\n\n\ndubbo_next_upstream_timeout\n--------------------------\n\nSyntax:\t  **dubbo_next_upstream_timeout**  *timer*;\nDefault: `0`\nContext: `http, server, location`\n\nLike ```proxy_next_upstream_tries```, limits the time during which a request can be passed to the next server. The 0 value turns off this limitation.\n\n\ndubbo_pass_header\n--------------------------\n\nSyntax:\t  **dubbo_pass_header**  *field*;\nDefault: `none`\nContext: `http, server, location`\n\nLike ```proxy_pass_header```, permits passing otherwise disabled header fields from a Dubbo provider to a client.\n\n\ndubbo_hide_header\n--------------------------\n\nSyntax:\t  **dubbo_hide_header**  *field*;\nDefault: `none`\nContext: `http, server, location`\n\nLike ```proxy_hide_header```, by default, tengine does not pass the header fields \"Date\", \"Server\", and \"X-Accel-...\" from the response of a Dubbo provider to a client. The dubbo_hide_header directive sets additional fields that will not be passed. If, on the contrary, the passing of fields needs to be permitted, the dubbo_pass_header directive can be used.\n\n\nVariables\n=========\n\n"
  },
  {
    "path": "docs/modules/ngx_http_dubbo_module_cn.md",
    "content": "ngx_http_dubbo_module\n=================\n\n该模块提供对后端Dubbo服务体系对接的支持。（Tengine 2.3.2版本之后）\n[Apache Dubbo™](http://dubbo.apache.org) 是一款高性能Java RPC框架。最初由Alibaba开源，经过长期发展和演进，目前已经成为业界主流微服务框架之一。\n\n在Dubbo服务框架中包含Consumer（client）和Provider（Server）两个角色。该模块支持Tengine作为网关代理，前端接收HTTP/HTTPS/HTTP2等请求，后端作为Dubbo的Consumer调用Dubbo的Provider服务（业务）。\n\n```\n  User                 tengine (dubbo_pass)                         Dubbo Service Provider\n    |                          |                                              |\n    |--- GET github.com:443 -->|                                              |\n    |                          |--- Dubbo Multiplexing Binary RPC Request  -->|\n    |                          |                                              |\n    |                          |<-- Dubbo Multiplexing Binary RPC Response ---|\n    |<--    HTTP/1.1 200    ---|                                              |\n```\n\nExample\n===================\n\nTengine Configuration Example\n---------------------\n\n```\nupstream dubbo_backend {\n    multi 1;\n    server 127.0.0.1:20880;\n}\n\nserver {\n    listen 8080;\n    \n    location / {\n        dubbo_pass org.apache.dubbo.demo.DemoService 0.0.0 http_dubbo_tengine dubbo_backend;\n    }\n}\n\n```\n\nDubbo Demo Service Example\n----------------\n### 标准方式下\n\nDubbo Provider侧需要实现如下接口，然后将服务名、服务版本号、方法名配置到```dubbo_pass```中。Tengine将前端HTTP/HTTPS/HTTP2请求，转换为对如下Dubbo接口的调用。\n\n```\nMap<String, Object> dubbo_method(Map<String, Object> context);\n\n```\n\n其中，方法入参Map<String, Object> context中包含若干键值对，可以通过```dubbo_pass_set```、```dubbo_pass_all_headers```、```dubbo_pass_body```等指令进行调整，如下Key为有特殊含义的规定：\n```\nbody： HTTP请求的Body，value的Object类型为byte[]\n\n```\n\n方法返回值中，如下Key为有特殊含义的规定：\n```\nbody： HTTP响应的Body，value的Object类型为byte[]\nstatue: HTTP响应的状态码，value的类型为String\n```\n\n\n\n### 扩展方式（持续更新中，敬请期待）\n\n支持在Tengine侧配置参数映射，动态生成对后端任意Dubbo Provider方法的调用（持续更新中，敬请期待）。\n\n\nQuickStart\n=======\n这里有一个[Tengine Dubbo功能的QuickStart](https://github.com/apache/dubbo-samples/tree/master/dubbo-samples-tengine)\n\n\nInstall\n=======\n\n源码安装此模块：\n\n```\n$ ./configure --add-module=./modules/mod_dubbo --add-module=./modules/ngx_multi_upstream_module --add-module=./modules/mod_config\n$ make && make install\n```\n\nDynamic module 支持\n* mod_dubbo: ```支持```编译成 dynamic module\n* ngx_multi_upstream_module: ```不支持```编译成 dynamic module\n* mod_config: ```支持但无需```编译成 dynamic module\n\n\nDirective\n=========\n\ndubbo_pass\n-------------\nSyntax: **dubbo_pass** *service_name* *service_version* *method* *upstream_name*  \nDefault: `none`  \nContext: `location, if in location` \n\n该指令用于配置使用Dubbo协议，代理到后端upstream \n\n* *service_name*: Dubbo provider发布的服务名\n* *service_version*: Dubbo provider发布的服务版本号\n* *method*: Dubbo provider发布的服务方法\n* *upstream_name*: 后端upstream名称\n\n`service_name`、`service_version`、`method` 支持使用变量。\n\n```\n# 代理到dubbo_backend这个upstream\nupstream dubbo_backend {\n    multi 1;\n    server 127.0.0.1:20880;\n}\n\nset $dubbo_service_name \"org.apache.dubbo.demo.DemoService\";\nset $dubbo_service_name \"0.0.0\";\nset $dubbo_service_name \"http_dubbo_nginx\";\n\ndubbo_pass $dubbo_service_name $dubbo_service_version $dubbo_method dubbo_backend;\n```\n\n注意：\n\n`dubbo_pass`只支持multi模式的upstream，相关upstream，必须通过`multi`指令，配置为多路复用模式，multi指令的参数为，多路复用连接的个数。\n\n\ndubbo_pass_set\n-------------------\n\nSyntax: **dubbo_pass_set** *key* *value*;  \nDefault: `none`  \nContext: `location, if in location`  \n\n该指令用于设置，代理到后端时，需要携带哪些key、value，支持变量。\n\n```\ndubbo_pass_set username $cookie_user;\n```\n\ndubbo_pass_all_headers\n-----------------------------\n\nSyntax: **dubbo_pass_all_headers** on | off;\nDefault: `off`\nContext: `location, if in location`\n\n指定是否向后端自动携带所有http头的key、value对。\n\ndubbo_pass_body\n--------------------------\n\nSyntax: **dubbo_pass_body** on | off;\nDefault: `on`\nContext: `location, if in location`\n\n指定是否向后端携带请求Body。\n\ndubbo_heartbeat_interval\n--------------------------\n\nSyntax: **dubbo_heartbeat_interval** *time*;\nDefault: `60s`\nContext: `http, server, location`\n\n指定后端Dubbo连接，自动发送ping帧的间隔。\n\ndubbo_bind\n--------------------------\n\nSyntax:\t  **dubbo_bind**  *address* [transparent ] | off;\nDefault: `off`\nContext: `http, server, location`\n\n类似```proxy_bind```指令，提供Dubbo连接时指定本地IP Port。当设置为off时，使用操作系统自动分配的本地IP地址和Port。当设置为transparent时，使用指定的非本地IP地址连接后端。\n\n```\ndubbo_bind $remote_addr transparent;\n```\n\ndubbo_socket_keepalive\n--------------------------\n\nSyntax:\t  **dubbo_socket_keepalive**  on | off;\nDefault: `off`\nContext: `http, server, location`\n\n类似```proxy_socket_keepalive```指令，配置 “TCP keepalive” 选项，当设置为on是，后端Dubbo连接将设置 SO_KEEPALIVE socket选项。\n\ndubbo_connect_timeout\n--------------------------\n\nSyntax:\t  **dubbo_connect_timeout**  *time*;\nDefault: `60s`\nContext: `http, server, location`\n\n类似```proxy_connect_timeout```指令，配置后端Dubbo建立TCP连接的超时，注意，这个时间通常不超过75s。\n\ndubbo_send_timeout\n--------------------------\n\nSyntax:\t  **dubbo_send_timeout**  *time*;\nDefault: `60s`\nContext: `http, server, location`\n\n类似```proxy_send_timeout```指令，配置后端Dubbo连接发送超时，这个超时仅代表两次相邻的成功write操作，而不是整个请求的处理时间。\n\ndubbo_read_timeout\n--------------------------\n\nSyntax:\t  **dubbo_read_timeout**  *time*;\nDefault: `60s`\nContext: `http, server, location`\n\n类似```proxy_read_timeout```指令，配置后端Dubbo连接读取超时，这个超时仅代表两次相邻的成功read操作，而不是整个请求的处理时间。\n\n\ndubbo_intercept_errors\n--------------------------\n\nSyntax:\t  **dubbo_intercept_errors**  on | off;\nDefault: `off`\nContext: `http, server, location`\n\n类似```proxy_intercept_errors```指令，指定后端返回的状态码大于300时，使用```error_page```处理还是直接返回给客户端。\n\n\ndubbo_buffer_size\n--------------------------\n\nSyntax:\t  **dubbo_buffer_size**  *size*;\nDefault: `4k|8k`\nContext: `http, server, location`\n\n类似```proxy_buffer_size```指令，指定读取后端Dubbo response时buffer的大小。\n\ndubbo_next_upstream\n--------------------------\n\nSyntax:\t  **dubbo_next_upstream**  error | timeout | invalid_header | http_500 | http_502 | http_503 | http_504 | http_403 | http_404 | http_429 | non_idempotent | off ...;\nDefault: `error timeout`\nContext: `http, server, location`\n\n类似```proxy_next_upstream```指令，指定对请求进行next server的条件。\n\ndubbo_next_upstream_tries\n--------------------------\n\nSyntax:\t  **dubbo_next_upstream_tries**  *number*;\nDefault: `0`\nContext: `http, server, location`\n\n类似```proxy_next_upstream_tries```指令，指定可以对请求进行next server的次数，0的话，代表不进行next server。\n\n\ndubbo_next_upstream_timeout\n--------------------------\n\nSyntax:\t  **dubbo_next_upstream_timeout**  *timer*;\nDefault: `0`\nContext: `http, server, location`\n\n类似```proxy_next_upstream_tries```指令，指定可以对请求进行next server的次数，0的话，代表不进行next server。\n\n\ndubbo_pass_header\n--------------------------\n\nSyntax:\t  **dubbo_pass_header**  *field*;\nDefault: `none`\nContext: `http, server, location`\n\n类似```proxy_pass_header```指令，默认情况下，Tengine不向客户端传递，后端Server返回的 “Date”, “Server”, and “X-Accel-...”，该指令用于允许向客户端传递指定的头。\n\n\ndubbo_hide_header\n--------------------------\n\nSyntax:\t  **dubbo_hide_header**  *field*;\nDefault: `none`\nContext: `http, server, location`\n\n类似```proxy_hide_header```指令，默认情况下，Tengine不向客户端传递，后端Server返回的 “Date”, “Server”, and “X-Accel-...”，该指令用于增加不向客户端传递的头。\n\n\nVariables\n=========\n\n\n\n"
  },
  {
    "path": "docs/modules/ngx_http_limit_req_module.md",
    "content": "Name\n====\n\n* limit_req module\n\nDescription\n===========\n\n* This is the enhanced version of nginx's limit_req module with white list support, and more limit conditions are allowed in a single location.\n\n\nDirectives\n==========\n\nlimit_req_zone\n-------------\n\n**Syntax**: *limit_req_zone $session_variable1 $session_variable2 ... zone=name_of_zone:size rate=rate | rate=$limit_variable*\n\n**Default**: *none*\n\n**Context**: *http*\n\nSupport more than one limit variables. For example:\n\n    limit_req_zone $binary_remote_addr $uri zone=one:3m rate=1r/s;\n    limit_req_zone $binary_remote_addr $request_uri zone=two:3m rate=1r/s;\n    \nThe last line of the above example indicates a client can access a specific URI only once in a second.\n\nSupport variable for rate. For example:\n\n    limit_req_zone $binary_remote_addr zone=three:3m rate=$limit_count;\n\n\nPrior to tengine version 2.3.0, requests with any empty variable are not accounted.\n\nFrom tengine version 2.3.0, requests with all empty variables are not accounted.\nThe variable can contain text, variables, and their combination. For example:\n\n    limit_req_zone $binary_remote_addr$request_uri zone=two:3m rate=1r/s;\n\n\nlimit_req\n------------------------\n\n**Syntax**: *limit_req [off] | zone=zone_name [burst=burst] \\[forbid_action=action\\] \\[nodelay\\]*\n\n**Default**: *none*\n\n**Context**: *http, server, location*\n\nMultiple limit conditions are allowed in a single block. And all the conditions are examined in order.\nYou can turn this directive on or off (default is on).\n'forbid_action' specifies the action URL to redirect. It can be a named location. By default, tengine will return 503.\n\nFor example:\n\n    limit_req_zone $binary_remote_addr zone=one:3m rate=1r/s;\n    limit_req_zone $binary_remote_addr $uri zone=two:3m rate=1r/s;\n    limit_req_zone $binary_remote_addr $request_uri zone=three:3m rate=1r/s;\n    limit_req_zone $binary_remote_addr $request_uri zone=four:3m rate=$limit_count;\n\n    location / {\n        limit_req zone=one burst=5;\n        limit_req zone=two forbid_action=@test1;\n        limit_req zone=three burst=3 forbid_action=@test2;\n        set $limit_count \"10r/s\";\n        if ($http_user_agent ~* \"Android\") {\n            set $limit_count \"1r/s\";\n        }\n        if ($http_user_agent ~* \"Iphone\") {\n            set $limit_count \"100r/s\";\n        }\n        limit_req zone=four burst=3 forbid_action=@test2;\n    }\n\n    location /off {\n        limit_req off;\n    }\n\n    location @test1 {\n        rewrite ^ /test1.html;\n    }\n\n    location @test2 {\n        rewrite ^  /test2.html;\n    }\n\n\nlimit_req_whitelist\n------------------------\n\n**Syntax**: *limit_req_whitelist geo_var_name=var_name geo_var_value=var_value*\n\n**Default**: *none*\n\n**Context**: *http, server, location*\n\nSet the whitelist.\nThis directive needs work with the geo module. The 'geo_var_name' is the variable name declared in the geo module, 'geo_var_value' is its value. For example:\n\n    geo $white_ip {\n        ranges;\n        default 0;\n        127.0.0.1-127.0.0.255 1;\n    }\n\n    limit_req_whitelist geo_var_name=white_ip geo_var_value=1;\n    \nIt means requests from IP (127.0.0.1 to 127.0.0.255) will be considered safe and let pass.\n"
  },
  {
    "path": "docs/modules/ngx_http_proxy_connect_module.md",
    "content": "name\n====\n\nThis module provides support for the CONNECT HTTP method after Tengine version 2.3.0.  \nThis method is mainly used to [tunnel SSL requests](https://en.wikipedia.org/wiki/HTTP_tunnel#HTTP_CONNECT_tunneling) through proxy servers.\n\nTable of Contents\n=================\n\n   * [name](#name)\n   * [Example](#example)\n      * [configuration example](#configuration-example)\n      * [example for curl](#example-for-curl)\n   * [Install](#install)\n   * [Error Log](#error-log)\n   * [Directive](#directive)\n      * [proxy_connect](#proxy_connect)\n      * [proxy_connect_allow](#proxy_connect_allow)\n      * [proxy_connect_connect_timeout](#proxy_connect_connect_timeout)\n      * [proxy_connect_data_timeout](#proxy_connect_data_timeout)\n      * [proxy_connect_read_timeout(deprecated)](#proxy_connect_read_timeout)\n      * [proxy_connect_send_timeout(deprecated)](#proxy_connect_send_timeout)\n      * [proxy_connect_address](#proxy_connect_address)\n      * [proxy_connect_bind](#proxy_connect_bind)\n      * [proxy_connect_response](#proxy_connect_response)\n   * [Variables](#variables)\n      * [$connect_host](#connect_host)\n      * [$connect_port](#connect_port)\n      * [$connect_addr](#connect_addr)\n      * [$proxy_connect_connect_timeout](#proxy_connect_connect_timeout-1)\n      * [$proxy_connect_read_timeout](#proxy_connect_read_timeout-1)\n      * [$proxy_connect_send_timeout(deprecated)](#proxy_connect_send_timeout-1)\n      * [$proxy_connect_resolve_time](#proxy_connect_resolve_time)\n      * [$proxy_connect_connect_time](#proxy_connect_connect_time)\n      * [$proxy_connect_first_byte_time](#proxy_connect_first_byte_time)\n      * [$proxy_connect_response](#proxy_connect_response-1)\n   * [Known Issues](#known-issues)\n\nExample\n=======\n\nConfiguration Example\n---------------------\n\n```nginx\n server {\n     listen                         3128;\n\n     # dns resolver used by forward proxying\n     resolver                       8.8.8.8;\n\n     # forward proxy for CONNECT request\n     proxy_connect;\n     proxy_connect_allow            443 563;\n     proxy_connect_connect_timeout  10s;\n     proxy_connect_data_timeout     10s;\n\n     # forward proxy for non-CONNECT request\n     location / {\n         proxy_pass http://$host;\n         proxy_set_header Host $host;\n     }\n }\n```\n\nExample for curl\n----------------\n\nWith above configuration, you can get any https website via HTTP CONNECT tunnel.\nA simple test with command `curl` is as following:\n\n```\n$ curl https://github.com/ -v -x 127.0.0.1:3128\n*   Trying 127.0.0.1...                                           -.\n* Connected to 127.0.0.1 (127.0.0.1) port 3128 (#0)                | curl creates TCP connection with nginx (with proxy_connect module).\n* Establish HTTP proxy tunnel to github.com:443                   -'\n> CONNECT github.com:443 HTTP/1.1                                 -.\n> Host: github.com:443                                         (1) | curl sends CONNECT request to create tunnel.\n> User-Agent: curl/7.43.0                                          |\n> Proxy-Connection: Keep-Alive                                    -'\n>\n< HTTP/1.0 200 Connection Established                             .- nginx replies 200 that tunnel is established.\n< Proxy-agent: nginx                                           (2)|  (The client is now being proxied to the remote host. Any data sent\n<                                                                 '-  to nginx is now forwarded, unmodified, to the remote host)\n\n* Proxy replied OK to CONNECT request\n* TLS 1.2 connection using TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256  -.\n* Server certificate: github.com                                   |\n* Server certificate: DigiCert SHA2 Extended Validation Server CA  | curl sends \"https://github.com\" request via tunnel,\n* Server certificate: DigiCert High Assurance EV Root CA           | proxy_connect module will proxy data to remote host (github.com).\n> GET / HTTP/1.1                                                   |\n> Host: github.com                                             (3) |\n> User-Agent: curl/7.43.0                                          |\n> Accept: */*                                                     -'\n>\n< HTTP/1.1 200 OK                                                 .-\n< Date: Fri, 11 Aug 2017 04:13:57 GMT                             |\n< Content-Type: text/html; charset=utf-8                          |  Any data received from remote host will be sent to client\n< Transfer-Encoding: chunked                                      |  by proxy_connect module.\n< Server: GitHub.com                                           (4)|\n< Status: 200 OK                                                  |\n< Cache-Control: no-cache                                         |\n< Vary: X-PJAX                                                    |\n...                                                               |\n... <other response headers & response body> ...                  |\n...                                                               '-\n```\n\nThe sequence diagram of above example is as following:\n\n```\n  curl                     nginx (proxy_connect)            github.com\n    |                             |                          |\n(1) |-- CONNECT github.com:443 -->|                          |\n    |                             |                          |\n    |                             |----[ TCP connection ]--->|\n    |                             |                          |\n(2) |<- HTTP/1.1 200           ---|                          |\n    |   Connection Established    |                          |\n    |                             |                          |\n    |                                                        |\n    ========= CONNECT tunnel has been established. ===========\n    |                                                        |\n    |                             |                          |\n    |                             |                          |\n    |   [ SSL stream       ]      |                          |\n(3) |---[ GET / HTTP/1.1   ]----->|   [ SSL stream       ]   |\n    |   [ Host: github.com ]      |---[ GET / HTTP/1.1   ]-->.\n    |                             |   [ Host: github.com ]   |\n    |                             |                          |\n    |                             |                          |\n    |                             |                          |\n    |                             |   [ SSL stream       ]   |\n    |   [ SSL stream       ]      |<--[ HTTP/1.1 200 OK  ]---'\n(4) |<--[ HTTP/1.1 200 OK  ]------|   [ < html page >    ]   |\n    |   [ < html page >    ]      |                          |\n    |                             |                          |\n```\n\nInstall\n=======\n\n* Build Tengine with this module from source:\n\n```\n$ ./configure --add-module=./modules/ngx_http_proxy_connect_module\n$ make && make install\n```\n\nError Log\n=========\n\nThis module logs its own error message beginning with `\"proxy_connect:\"` string.  \nSome typical error logs are shown as following:\n\n* The proxy_connect module tries to establish tunnel connection with backend server, but the TCP connection timeout occurs.\n\n```\n2019/08/07 17:27:20 [error] 19257#0: *1 proxy_connect: upstream connect timed out (peer:216.58.200.4:443) while connecting to upstream, client: 127.0.0.1, server: , request: \"CONNECT www.google.com:443 HTTP/1.1\", host: \"www.google.com:443\"\n```\n\nDirective\n=========\n\nproxy_connect\n-------------\n\nSyntax: **proxy_connect**  \nDefault: `none`  \nContext: `server`  \n\nEnable \"CONNECT\" HTTP method support.\n\nproxy_connect_allow\n-------------------\n\nSyntax: **proxy_connect_allow `all | [port ...] | [port-range ...]`**  \nDefault: `443 563`  \nContext: `server`  \n\nThis directive specifies a list of port numbers or ranges to which the proxy CONNECT method may connect.  \nBy default, only the default https port (443) and the default snews port (563) are enabled.  \nUsing this directive will override this default and allow connections to the listed ports only.\n\nThe value `all` will allow all ports to proxy.\n\nThe value `port` will allow specified port to proxy.\n\nThe value `port-range` will allow specified range of port to proxy, for example:\n\n```\nproxy_connect_allow 1000-2000 3000-4000; # allow range of port from 1000 to 2000, from 3000 to 4000.\n```\n\nproxy_connect_connect_timeout\n-----------------------------\n\nSyntax: **proxy_connect_connect_timeout `time`**  \nDefault: `none`  \nContext: `server`  \n\nDefines a timeout for establishing a connection with a proxied server.\n\nproxy_connect_data_timeout\n--------------------------\n\nSyntax: **proxy_connect_data_timeout `time`**  \nDefault: `60s`  \nContext: `server`  \n\nSets the timeout between two successive read or write operations on client or proxied server connections. If no data is transmitted within this time, the connection is closed.\n\nproxy_connect_read_timeout\n--------------------------\n\nSyntax: **proxy_connect_read_timeout `time`**  \nDefault: `60s`  \nContext: `server`  \n\nDeprecated.\n\nIt has the same function as the directive `proxy_connect_data_timeout` for compatibility. You can configure only one of the directives (`proxy_connect_data_timeout` or `proxy_connect_read_timeout`).\n\nproxy_connect_send_timeout\n--------------------------\n\nSyntax: **proxy_connect_send_timeout `time`**  \nDefault: `60s`  \nContext: `server`  \n\nDeprecated.\n\nIt has no function.\n\nproxy_connect_address\n---------------------\n\nSyntax: **proxy_connect_address `address | off`**  \nDefault: `none`  \nContext: `server`  \n\nSpecifiy an IP address of the proxied server. The address can contain variables.  \nThe special value off is equal to none, which uses the IP address resolved from host name of CONNECT request line.  \n\nproxy_connect_bind\n------------------\n\nSyntax: **proxy_connect_bind `address [transparent] | off`**  \nDefault: `none`  \nContext: `server`  \n\nMakes outgoing connections to a proxied server originate from the specified local IP address with an optional port.  \nParameter value can contain variables. The special value off is equal to none, which allows the system to auto-assign the local IP address and port.\n\nThe transparent parameter allows outgoing connections to a proxied server originate from a non-local IP address, for example, from a real IP address of a client:\n\n```\nproxy_connect_bind $remote_addr transparent;\n\n```\n\nIn order for this parameter to work, it is usually necessary to run nginx worker processes with the [superuser](http://nginx.org/en/docs/ngx_core_module.html#user) privileges. On Linux it is not required (1.13.8) as if the transparent parameter is specified, worker processes inherit the CAP_NET_RAW capability from the master process. It is also necessary to configure kernel routing table to intercept network traffic from the proxied server.\n\nproxy_connect_response\n----------------------\n\nSyntax: **proxy_connect_response `CONNECT response`**  \nDefault: `HTTP/1.1 200 Connection Established\\r\\nProxy-agent: nginx\\r\\n\\r\\n`  \nContext: `server`\n\nSet the response of CONNECT request.\n\nNote that it is only used for CONNECT request, it cannot modify the data flow over CONNECT tunnel.\n\nFor example:\n\n```\nproxy_connect_response \"HTTP/1.1 200 Connection Established\\r\\nProxy-agent: nginx\\r\\nX-Proxy-Connected-Addr: $connect_addr\\r\\n\\r\\n\";\n\n```\n\nThe `curl` command test case with above config is as following:\n\n```\n$ curl https://github.com -sv -x localhost:3128\n* Connected to localhost (127.0.0.1) port 3128 (#0)\n* allocate connect buffer!\n* Establish HTTP proxy tunnel to github.com:443\n> CONNECT github.com:443 HTTP/1.1\n> Host: github.com:443\n> User-Agent: curl/7.64.1\n> Proxy-Connection: Keep-Alive\n>\n< HTTP/1.1 200 Connection Established            --.\n< Proxy-agent: nginx                               | custom CONNECT response\n< X-Proxy-Connected-Addr: 13.229.188.59:443      --'\n...\n\n```\n\n\nVariables\n=========\n\n$connect_host\n-------------\n\nhost name from CONNECT request line.\n\n$connect_port\n-------------\n\nport from CONNECT request line.\n\n$connect_addr\n-------------\n\nIP address and port of the remote host, e.g. \"192.168.1.5:12345\".\nIP address is resolved from host name of CONNECT request line.\n\n$proxy_connect_connect_timeout\n------------------------------\n\nGet or set timeout of [`proxy_connect_connect_timeout` directive](#proxy_connect_connect_timeout).\n\nFor example:\n\n```nginx\n# Set default value\n\nproxy_connect_connect_timeout   10s;\nproxy_connect_data_timeout      10s;\n\n# Overlap default value\n\nif ($host = \"test.com\") {\n    set $proxy_connect_connect_timeout  \"10ms\";\n    set $proxy_connect_data_timeout     \"10ms\";\n}\n```\n\n$proxy_connect_data_timeout\n---------------------------\n\nGet or set a timeout of [`proxy_connect_data_timeout` directive](#proxy_connect_data_timeout).\n\n$proxy_connect_read_timeout\n---------------------------\n\nDeprecated. \nIt still can get or set a timeout of [`proxy_connect_data_timeout` directive](#proxy_connect_data_timeout) for compatibility.\n\n$proxy_connect_send_timeout\n---------------------------\n\nDeprecated.\nIt has no function.\n\n$proxy_connect_resolve_time\n---------------------------\n\nKeeps time spent on name resolving; the time is kept in seconds with millisecond resolution.\n\n* Value of \"\" means this module does not work on this request.\n* Value of \"-\" means name resolving failed.\n\n\n$proxy_connect_connect_time\n---------------------------\n\nKeeps time spent on establishing a connection with the upstream server; the time is kept in seconds with millisecond resolution.\n\n* Value of \"\" means this module does not work on this request.\n* Value of \"-\" means name resolving or connecting failed.\n\n\n$proxy_connect_first_byte_time\n---------------------------\n\nKeeps time to receive the first byte of data from the upstream server; the time is kept in seconds with millisecond resolution.\n\n* Value of \"\" means this module does not work on this request.\n* Value of \"-\" means name resolving, connecting or receving failed.\n\n\n$proxy_connect_response\n---------------------------\n\nGet or set the response of CONNECT request.  \nThe default response of CONNECT request is \"HTTP/1.1 200 Connection Established\\r\\nProxy-agent: nginx\\r\\n\\r\\n\".\n\nNote that it is only used for CONNECT request, it cannot modify the data flow over CONNECT tunnel.\n\nFor example:\n\n```nginx\n\n# modify default Proxy-agent header\nset $proxy_connect_response \"HTTP/1.1 200\\r\\nProxy-agent: nginx/1.19\\r\\n\\r\\n\";\n```\n\n\nKnown Issues\n============\n\n* In HTTP/2, the CONNECT method is not supported. It only supports the CONNECT method request in HTTP/1.x and HTTPS.\n\nSee Also\n========\n\n* [HTTP tunnel - Wikipedia](https://en.wikipedia.org/wiki/HTTP_tunnel)\n* [CONNECT method in HTTP/1.1](https://tools.ietf.org/html/rfc7231#section-4.3.6)\n* [CONNECT method in HTTP/2](https://httpwg.org/specs/rfc7540.html#CONNECT)\n\n"
  },
  {
    "path": "docs/modules/ngx_http_proxy_connect_module_cn.md",
    "content": "name\n====\n\n该模块提供对HTTP方法CONNECT的支持。（Tengine 2.3.0版本之后）  \n该方法主要用于[SSL请求隧道](https://en.wikipedia.org/wiki/HTTP_tunnel#HTTP_CONNECT_tunneling)。\n\nTable of Contents\n=================\n\n   * [name](#name)\n   * [Example](#example)\n      * [configuration example](#configuration-example)\n      * [example for curl](#example-for-curl)\n   * [Install](#install)\n   * [Error Log](#error-log)\n   * [Directive](#directive)\n      * [proxy_connect](#proxy_connect)\n      * [proxy_connect_allow](#proxy_connect_allow)\n      * [proxy_connect_connect_timeout](#proxy_connect_connect_timeout)\n      * [proxy_connect_data_timeout](#proxy_connect_data_timeout)\n      * [proxy_connect_read_timeout(deprecated)](#proxy_connect_read_timeout)\n      * [proxy_connect_send_timeout(deprecated)](#proxy_connect_send_timeout)\n      * [proxy_connect_address](#proxy_connect_address)\n      * [proxy_connect_bind](#proxy_connect_bind)\n      * [proxy_connect_response](#proxy_connect_response)\n   * [Variables](#variables)\n      * [$connect_host](#connect_host)\n      * [$connect_port](#connect_port)\n      * [$connect_addr](#connect_addr)\n      * [$proxy_connect_connect_timeout](#proxy_connect_connect_timeout-1)\n      * [$proxy_connect_read_timeout](#proxy_connect_read_timeout-1)\n      * [$proxy_connect_send_timeout(deprecated)](#proxy_connect_send_timeout-1)\n      * [$proxy_connect_resolve_time](#proxy_connect_resolve_time)\n      * [$proxy_connect_connect_time](#proxy_connect_connect_time)\n      * [$proxy_connect_first_byte_time](#proxy_connect_first_byte_time)\n      * [$proxy_connect_response](#proxy_connect_response-1)\n   * [Known Issues](#known-issues)\n\nExample\n=======\n\nConfiguration Example\n---------------------\n\n```nginx\n server {\n     listen                         3128;\n\n     # dns resolver used by forward proxying\n     resolver                       8.8.8.8;\n\n     # forward proxy for CONNECT request\n     proxy_connect;\n     proxy_connect_allow            443 563;\n     proxy_connect_connect_timeout  10s;\n     proxy_connect_data_timeout     10s;\n\n     # forward proxy for non-CONNECT request\n     location / {\n         proxy_pass http://$host;\n         proxy_set_header Host $host;\n     }\n }\n```\n\nExample for curl\n----------------\n\n你可以通过HTTP CONNECT隧道访问任意HTTPS网站。  \n使用命令`curl`的简单示例如下：\n\n```\n$ curl https://github.com/ -v -x 127.0.0.1:3128\n*   Trying 127.0.0.1...                                           -.\n* Connected to 127.0.0.1 (127.0.0.1) port 3128 (#0)                | curl与Tengine（proxy_connect模块）创建TCP连接。\n* Establish HTTP proxy tunnel to github.com:443                   -'\n> CONNECT github.com:443 HTTP/1.1                                 -.\n> Host: github.com:443                                         (1) | curl发送CONNECT请求以创建隧道。\n> User-Agent: curl/7.43.0                                          |\n> Proxy-Connection: Keep-Alive                                    -'\n>\n< HTTP/1.0 200 Connection Established                             .- Tengine返回200说明隧道建立成功。\n< Proxy-agent: nginx                                           (2)|  (后续客户端发送的任何数据都会被代理到对端，Tengine不会修改任何被代理的数据）\n<                                                                 '-\n\n* Proxy replied OK to CONNECT request\n* TLS 1.2 connection using TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256  -.\n* Server certificate: github.com                                   |\n* Server certificate: DigiCert SHA2 Extended Validation Server CA  | curl通过隧道发送\"https://github.com\"请求，\n* Server certificate: DigiCert High Assurance EV Root CA           | proxy_connect模块将把数据代理到对端（github.com）。\n> GET / HTTP/1.1                                                   |\n> Host: github.com                                             (3) |\n> User-Agent: curl/7.43.0                                          |\n> Accept: */*                                                     -'\n>\n< HTTP/1.1 200 OK                                                 .-\n< Date: Fri, 11 Aug 2017 04:13:57 GMT                             |\n< Content-Type: text/html; charset=utf-8                          |  任何来自对端的数据都会被proxy_connect模块发送给客户端curl。\n< Transfer-Encoding: chunked                                      |\n< Server: GitHub.com                                           (4)|\n< Status: 200 OK                                                  |\n< Cache-Control: no-cache                                         |\n< Vary: X-PJAX                                                    |\n...                                                               |\n... <other response headers & response body> ...                  |\n...                                                               '-\n```\n\n以上示例的流程图示例如下：\n\n```\n  curl                     nginx (proxy_connect)            github.com\n    |                             |                          |\n(1) |-- CONNECT github.com:443 -->|                          |\n    |                             |                          |\n    |                             |----[ TCP connection ]--->|\n    |                             |                          |\n(2) |<- HTTP/1.1 200           ---|                          |\n    |   Connection Established    |                          |\n    |                             |                          |\n    |                                                        |\n    ========= CONNECT 隧道已经被建立。========================\n    |                                                        |\n    |                             |                          |\n    |                             |                          |\n    |   [ SSL stream       ]      |                          |\n(3) |---[ GET / HTTP/1.1   ]----->|   [ SSL stream       ]   |\n    |   [ Host: github.com ]      |---[ GET / HTTP/1.1   ]-->.\n    |                             |   [ Host: github.com ]   |\n    |                             |                          |\n    |                             |                          |\n    |                             |                          |\n    |                             |   [ SSL stream       ]   |\n    |   [ SSL stream       ]      |<--[ HTTP/1.1 200 OK  ]---'\n(4) |<--[ HTTP/1.1 200 OK  ]------|   [ < html page >    ]   |\n    |   [ < html page >    ]      |                          |\n    |                             |                          |\n```\n\nInstall\n=======\n\n* 源码安装此模块：\n\n```\n$ ./configure --add-module=./modules/ngx_http_proxy_connect_module\n$ make && make install\n```\n\nError Log\n=========\n\n该模块记录的错误日志以`\"proxy_connect:\"`字符串为开头。  \n典型的错误日志如下：\n\n* proxy_connect模块尝试与后端服务器建立隧道连接，但发生了连接超时。\n\n```\n2019/08/07 17:27:20 [error] 19257#0: *1 proxy_connect: upstream connect timed out (peer:216.58.200.4:443) while connecting to upstream, client: 127.0.0.1, server: , request: \"CONNECT www.google.com:443 HTTP/1.1\", host: \"www.google.com:443\"\n```\n\nDirective\n=========\n\nproxy_connect\n-------------\n\nSyntax: **proxy_connect**  \nDefault: `none`  \nContext: `server`  \n\n开启对HTTP方法\"CONNECT\"的支持。\n\nproxy_connect_allow\n-------------------\n\nSyntax: **proxy_connect_allow `all | [port ...] | [port-range ...]`**  \nDefault: `443 563`  \nContext: `server`  \n\n该指令指定允许开启CONNECT方法的端口。  \n默认情况下，只有443和563端口被允许。  \n\n使用如下参数来修改默认行为：\n\n`all`值允许所有端口。\n\n`port`指定允许的特定端口。\n\n`port-range`指定允许的指定端口范围，示例：\n\n\n```\nproxy_connect_allow 1000-2000 3000-4000; # 允许端口范围1000-2000 和 3000-4000\n```\n\nproxy_connect_connect_timeout\n-----------------------------\n\nSyntax: **proxy_connect_connect_timeout `time`**  \nDefault: `none`  \nContext: `server`  \n\n指定与对端服务器建联的超时时间。\n\nproxy_connect_data_timeout\n--------------------------\n\nSyntax: **proxy_connect_data_timeout `time`**  \nDefault: `60s`  \nContext: `server`  \n\n指定与客户端（或后端服务器）数据传输的等待时间。  \n如果在此时间内没有数据传输（读或写操作），连接将被关闭。  \n\nproxy_connect_read_timeout\n--------------------------\n\nSyntax: **proxy_connect_read_timeout `time`**  \nDefault: `60s`  \nContext: `server`  \n\n已弃用。\n\n为了兼容性，它与指令`proxy_connect_data_timeout`具有相同的功能。只能配置其中一个指令（`proxy_connect_data_timeout`或`proxy_connect_read_timeout`）。\n\nproxy_connect_send_timeout\n--------------------------\n\nSyntax: **proxy_connect_send_timeout `time`**  \nDefault: `60s`  \nContext: `server`  \n\n已弃用。\n\n为了兼容性，此指令可以配置但没有任何作用。\n\nproxy_connect_address\n---------------------\n\nSyntax: **proxy_connect_address `address | off`**  \nDefault: `none`  \nContext: `server`  \n\n指定对端服务器的地址。该值可以包含变量。  \n值`off`或者不设置该指令，则对端服务器的地址将CONNECT请求行的host字段提取并解析（如查询DNS）。  \n\nproxy_connect_bind\n------------------\n\nSyntax: **proxy_connect_bind `address [transparent] | off`**  \nDefault: `none`  \nContext: `server`  \n\n指定与对端服务器的连接的来源地址。  \n该值可以包含变量。值`off`或者不设置该指令将由系统自动分配来源地址和端口。  \n\n`transparent`参数值使与对端服务器的连接的来源地址为非本地地址。示例如下（使用客户端地址作为来源地址）：  \n\n```\nproxy_connect_bind $remote_addr transparent;\n\n```\n\n为了使`transparent`参数生效，需要配置内核路由表去截获来自对端服务器的网络流量。\n\nproxy_connect_response\n----------------------\n\nSyntax: **proxy_connect_response `CONNECT response`**  \nDefault: `HTTP/1.1 200 Connection Established\\r\\nProxy-agent: nginx\\r\\n\\r\\n`  \nContext: `server`\n\n指定CONNECT请求的应答内容。\n\n注意该指令只作用于CONNECT请求，它无法修改CONNECT隧道中的数据流。\n\n示例：\n\n```\nproxy_connect_response \"HTTP/1.1 200 Connection Established\\r\\nProxy-agent: nginx\\r\\nX-Proxy-Connected-Addr: $connect_addr\\r\\n\\r\\n\";\n\n```\n\n使用`curl`指令测试上述配置的结果如下：\n\n```\n$ curl https://github.com -sv -x localhost:3128\n* Connected to localhost (127.0.0.1) port 3128 (#0)\n* allocate connect buffer!\n* Establish HTTP proxy tunnel to github.com:443\n> CONNECT github.com:443 HTTP/1.1\n> Host: github.com:443\n> User-Agent: curl/7.64.1\n> Proxy-Connection: Keep-Alive\n>\n< HTTP/1.1 200 Connection Established            --.\n< Proxy-agent: nginx                               | 自定义的CONNECT应答\n< X-Proxy-Connected-Addr: 13.229.188.59:443      --'\n...\n\n```\n\n\nVariables\n=========\n\n$connect_host\n-------------\n\nCONNECT请求行的主机名(host)字段。\n\n$connect_port\n-------------\n\nCONNECT请求行的端口(port)字段。\n\n$connect_addr\n-------------\n\n对端服务器的IP地址和端口，如\"192.168.1.5:12345\"。\n\n$proxy_connect_connect_timeout\n------------------------------\n\n获取和设置[`proxy_connect_connect_timeout`指令](#proxy_connect_connect_timeout)的超时时间。\n\n示例如下：\n\n```nginx\n# 设置默认值\n\nproxy_connect_connect_timeout   10s;\nproxy_connect_data_timeout      10s;\n\n# 覆盖默认值\n\nif ($host = \"test.com\") {\n    set $proxy_connect_connect_timeout  \"10ms\";\n    set $proxy_connect_data_timeout     \"10ms\";\n}\n```\n\n$proxy_connect_data_timeout\n---------------------------\n\n获取和设置[`proxy_connect_data_timeout`指令](#proxy_connect_data_timeout)的超时时间。\n\n$proxy_connect_read_timeout\n---------------------------\n\n弃用。 \n为了兼容性，仍然能够获取和设置[`proxy_connect_read_timeout`指令](#proxy_connect_read_timeout)的超时时间。\n\n$proxy_connect_send_timeout\n---------------------------\n\n弃用。 \n为了兼容性，仍然能够配置此变量，但没有任何作用。\n\n$proxy_connect_resolve_time\n---------------------------\n\n记录域名解析耗时；以秒为单位，以毫秒为精度。\n\n* \"\"值意味着此模块未做用于该请求。\n* \"-\"值意味着域名解析失败。\n\n\n$proxy_connect_connect_time\n---------------------------\n\n记录与后端建立连接的耗时；以秒为单位，以毫秒为精度。\n\n* \"\"值意味着此模块未做用于该请求。\n* \"-\"值意味着域名解析或者连接后端失败。\n\n\n$proxy_connect_first_byte_time\n---------------------------\n\n记录收到来自后端的首个字节的耗时；以秒为单位，以毫秒为精度。\n\n* \"\"值意味着此模块未做用于该请求。\n* \"-\"值意味着域名解析、连接后端或者接收后端数据失败。\n\n\n$proxy_connect_response\n---------------------------\n\n获取和设置CONNECT请求的应答内容。 \nCONNECT请求的应答内容的默认值是\"HTTP/1.1 200 Connection Established\\r\\nProxy-agent: nginx\\r\\n\\r\\n\".\n\n注意该变量只作用于CONNECT请求，它无法修改CONNECT隧道中的数据流。\n\n示例：\n\n```nginx\n\n# 修改Proxy-agent头的默认值\nset $proxy_connect_response \"HTTP/1.1 200\\r\\nProxy-agent: Tengine/2.4.0\\r\\n\\r\\n\";\n```\n\n\nKnown Issues\n============\n\n* 不支持HTTP/2的CONNECT方法。CONNECT方法仅支持HTTP/1.x和HTTPS。\n\nSee Also\n========\n\n* [维基百科：HTTP隧道](https://en.wikipedia.org/wiki/HTTP_tunnel)\n* [HTTP/1.1协议中CONNECT方法](https://tools.ietf.org/html/rfc7231#section-4.3.6)\n* [HTTP/2协议中CONNECT方法](https://httpwg.org/specs/rfc7540.html#CONNECT)\n\n"
  },
  {
    "path": "docs/modules/ngx_http_reqstat_module.md",
    "content": "Name\n====\n\n* ngx_http_reqstat_module\n\nDescription\n===========\n\nThis module will help monitor running status of Tengine.\n\n* It can provide running status information of Tengine.\n\n* The information is divided into different zones, and each zone is independent.\n\n* The status information is about connections, requests, response status codes, input and output flows,\n  rt, upstreams, and so on.\n\n* It shows all the results by default, and can be set to show part of them by specifying zones.\n\n* It supports for user-defined status by using nginx variables. The maximum of all the status is 50.\n\n* It recycles out-of-date running status information.\n\n* It supports for defining output format.\n\n* It follows the request processing flow, so internal redirect will not affect monitoring.\n\n* Do not use variables of response as a condition, eg., $status.\n\nCompilation\n===========\n\nThe module is compiled into Tengine by default. It can be disabled with '--without-http_reqstat_module'\nconfiguration parameter, or it can be compiled as a '.so' with '--with-http_reqstat_module=shared'.\n\nIf you use this module as a '.so', please make sure it is after 'ngx_http_lua_module'. Please refer to\n'nginx -m'.\n\n\nExample\n===========\n\n    http {\n\n        req_status_zone server \"$host,$server_addr:$server_port\" 10M;\n        req_status_zone_add_indicator server $limit;\n\n        server {\n            location /us {\n                req_status_show;\n                req_status_show_field req_total $limit;\n            }\n\n            set $limit 0;\n\n            if ($arg_limit = '1') {\n                set $limit 1;\n            }\n\n            req_status server;\n        }\n    }\n\n* when you call '/us', you will get the results like this:\n\n            www.example.com,127.0.0.1:80,162,6242,1,1,1,0,0,0,0,10,1,10,1....\n\n    * Each line shows the status infomation of a \"$host,$server_addr:$server_port\".\n\n    * Default line format:\n\n            kv,bytes_in,bytes_out,conn_total,req_total,http_2xx,http_3xx,http_4xx,http_5xx,http_other_status,rt,ups_req,ups_rt,ups_tries,http_200,http_206,http_302,http_304,http_403,http_404,http_416,http_499,http_500,http_502,http_503,http_504,http_508,http_other_detail_status,http_ups_4xx,http_ups_5xx\n\n        * **kv**                value of the variable defined by the directive 'req_status_zone'. The maximun key length is configurable, 152B by default, and overlength will be cut off\n        * **bytes_in**          total number of bytes received from client\n        * **bytes_out**         total number of bytes sent to client\n        * **conn_total**        total number of accepted connections\n        * **req_total**         total number of processed requests\n        * **http_2xx**          total number of 2xx requests\n        * **http_3xx**          total number of 3xx requests\n        * **http_4xx**          total number of 4xx requests\n        * **http_5xx**          total number of 5xx requests\n        * **http_other_status** total number of other requests\n        * **rt**                accumulation or rt\n        * **ups_req**           total number of requests calling for upstream\n        * **ups_rt**            accumulation or upstream rt\n        * **ups_tries**         total number of times calling for upstream\n        * **http_200**          total number of 200 requests\n        * **http_206**          total number of 206 requests\n        * **http_302**          total number of 302 requests\n        * **http_304**          total number of 304 requests\n        * **http_403**          total number of 403 requests\n        * **http_404**          total number of 404 requests\n        * **http_416**          total number of 416 requests\n        * **http_499**          total number of 499 requests\n        * **http_500**          total number of 500 requests\n        * **http_502**          total number of 502 requests\n        * **http_503**          total number of 503 requests\n        * **http_504**          total number of 504 requests\n        * **http_508**          total number of 508 requests\n        * **http_other_detail_status**      total number of requests of other status codes \n        * **http_ups_4xx**      total number of requests of upstream 4xx\n        * **http_ups_5xx**      total number of requests of upstream 5xx\n\n    * You can use names in the left column to define output format, with directive 'req_status_show_field'\n\n    * Some fields will be removed in future, because user-defined status has been supported.\n\n* tsar can parse the result and monitor, see also https://github.com/alibaba/tsar\n\nDirectives\n==========\n\nreq_status_zone\n-------------------------\n\n**Syntax**: *req_status_zone zone_name value size*\n\n**Default**: *none*\n\n**Context**: *http*\n\ncreate shared memory for this module. 'zone_name' is the name of memory block.\n'value' defines the key, in which variables can be used.\n'size' defines the size of shared memory.\n\nExample:\n\n    req_status_zone server \"$host,$server_addr:$server_port\" 10M;\n\n    the memory is 10MB, the key is \"$host,$server_addr:$server_port\", and the name is \"server\".\n\n* Notice, if you want to use tsar to monitor, you should not use comma in the key.\n\n\nreq_status\n-------------------------\n\n**Syntax**: *req_status zone_name1 [zone_name2 [zone_name3 [...]]]*\n\n**Default**: *none*\n\n**Context**: *http、srv、loc*\n\nEnable monitoring. You can specify multiple zones to monitor.\n\nreq_status_show\n-------------------------\n\n**Syntax**: *req_status_show [zone_name1 [zone_name2 [...]]]*\n\n**Default**: *all the targets defined by 'req_status_zone'*\n\n**Context**: *loc*\n\nDisplay the status information. You can specify zones to display.\n\n\nreq_status_show_field\n-------------------------------\n**Syntax**: *req_status_show_field field_name1 [field_name2 [field_name3 [...]]]*\n\n**Default**: *all the fields, including user defined fields*\n\n**Context**: *loc*\n\nDefine output format, used with the directive 'req_status_show'. You can use names\nto define internal supported fields, see it above. And also you can use variables\nto define user defined fields. 'kv' is always the first field in a line.\n\n\nreq_status_zone_add_indicator\n--------------------------------\n\n**Syntax**: *req_status_zone_add_indecator zone_name $var1 [$var2 [...]]*\n\n**Default**: *none*\n\n**Context**: *http*\n\nAdd user-defined status by using nginx variables. The status will be appended at the end of each line on display.\n\n\nreq_status_zone_key_length\n-------------------------------\n\n**Syntax**: *req_status_zone_key_length zone_name length*\n\n**Default**: *none*\n\n**Context**: *http*\n\nDefine the maximun length of key for a zone. The default is 104.\n\n\nreq_status_zone_recycle\n-------------------------------\n\n**Syntax**: *req_status_zone_recycle zone_name times seconds*\n\n**Default**: *none*\n\n**Context**: *http*\n\nDefine the recycle threshold for a zone. Recycle will be switched on when the shared memory is exhausted,\nand will only take effect on imformation whose visit frequency is lower than the setting.\nThe setting frequency is defined by 'times' and 'seconds', and it is 10r/min by default.\n     req_status_zone_recycle demo_zone 10 60;\n\nreq_status_lazy\n-------------------------------\n\n**Syntax**: *req_status_lazy on|off*\n\n**Default**: *off*\n\n**Context**: *http、srv、loc*\n\nreq_status_lazy directive is used to control whether the variable in the req_status_zone directive is recalculate during the log phasei. In order to solve some variables (such as the upstream_xxx related variable) as a key scene to get empty value.\n"
  },
  {
    "path": "docs/modules/ngx_http_reqstat_module_cn.md",
    "content": "模块名\n====\n\n* ngx_http_reqstat_module，监控模块\n\n描述\n===========\n\n* 这个模块计算定义的变量，根据变量值分别统计Tengine的运行状况。\n\n* 可以监视的运行状况有：连接数、请求数、各种响应码范围的请求数、输入输出流量、rt、upstream访问等。\n\n* 可以指定获取所有监控结果或者一部分监控结果。\n\n* 利用变量添加自定义监控状态。总的监控状态最大个数为50个。\n\n* 回收过期的监控数据。\n\n* 设置输出格式\n\n* 跟踪请求，不受内部跳转的影响\n\n* 不要使用与响应相关的变量作为条件，比如\"$status\"\n\n编译\n===========\n\n默认编入Tengine，可通过--without-http_reqstat_module不编译此模块，或通过--with-http_reqstat_module=shared编译为so模块。\n使用so模块加载的话，请确保其顺序在\"ngx_http_lua_module\"之后。可以借助\"nginx -m\"来确认。\n\n例子\n===========\n\n    http {\n\n        req_status_zone server \"$host,$server_addr:$server_port\" 10M;\n        req_status_zone_add_indicator server $limit;\n\n        server {\n            location /us {\n                req_status_show;\n                req_status_show_field req_total $limit;\n            }\n\n            set $limit 0;\n\n            if ($arg_limit = '1') {\n                set $limit 1;\n            }\n\n            req_status server;\n        }\n    }\n\n* 以上例，通过访问/us得到统计结果\n\n    * 每行对应一个server\n\n    * 每行的默认格式\n\n            kv,bytes_in,bytes_out,conn_total,req_total,http_2xx,http_3xx,http_4xx,http_5xx,http_other_status,rt,ups_req,ups_rt,ups_tries,http_200,http_206,http_302,http_304,http_403,http_404,http_416,http_499,http_500,http_502,http_503,http_504,http_508,http_other_detail_status,http_ups_4xx,http_ups_5xx\n\n        * kv                计算得到的req_status_zone指令定义变量的值，最大长度可配置，默认104B，超长的部分截断\n        * bytes_in          从客户端接收流量总和\n        * bytes_out         发送到客户端流量总和\n        * conn_total        处理过的连接总数\n        * req_total         处理过的总请求数\n        * http_2xx          2xx请求的总数\n        * http_3xx          3xx请求的总数\n        * http_4xx          4xx请求的总数\n        * http_5xx          5xx请求的总数\n        * http_other_status 其他请求的总数\n        * rt                rt的总数\n        * ups_req           需要访问upstream的请求总数\n        * ups_rt            访问upstream的总rt\n        * ups_tries         upstram总访问次数\n        * http_200          200请求的总数\n        * http_206          206请求的总数\n        * http_302          302请求的总数\n        * http_304          304请求的总数\n        * http_403          403请求的总数\n        * http_404          404请求的总数\n        * http_416          416请求的总数\n        * http_499          499请求的总数\n        * http_500          500请求的总数\n        * http_502          502请求的总数\n        * http_503          503请求的总数\n        * http_504          504请求的总数\n        * http_508          508请求的总数\n        * http_other_detail_status    非以上13种status code的请求总数\n        * http_ups_4xx      upstream返回4xx响应的请求总数\n        * http_ups_5xx      upstream返回5xx响应的请求总数\n\n    * 可以用\"req_status_show_field\"指令定义输出格式。左侧栏是字段的名字。\n\n    * 注，后续会清理这些状态，因为已经支持了自定义状态。\n\n* tsar可解析输出结果，具体见https://github.com/alibaba/tsar\n\n指令\n==========\n\nreq_status_zone\n-------------------------\n\n**Syntax**: *req_status_zone zone_name value size*\n\n**Default**: *none*\n\n**Context**: *main*\n\n创建统计使用的共享内存。zone_name是共享内存的名称，value用于定义key，支持变量。size是共享内存的大小。\n\n例子：\n\n    req_status_zone server \"$host,$server_addr:$server_port\" 10M;\n\n    创建名为“server”的共享内存，大小10M，使用“$host,$server_addr:$server_port”计算key。\n\n* 注意，如果希望用tsar来监控的话，key的定义中请不要使用逗号。\n\n\nreq_status\n-------------------------\n\n**Syntax**: *req_status zone_name1 [zone_name2 [zone_name3 [...]]]*\n\n**Default**: *none*\n\n**Context**: *http、srv、loc*\n\n开启统计，可以指定同时统计多个目标，每一个zone_name对应一个目标。\n\n\nreq_status_show\n-------------------------\n\n**Syntax**: *req_status_show [zone_name1 [zone_name2 [...]]]*\n\n**Default**: *所有建立的共享内存目标*\n\n**Context**: *loc*\n\n按格式返回统计结果。可指定返回部分目标的统计结果。\n\n\nreq_status_show_field\n-------------------------------\n**Syntax**: *req_status_show_field field_name1 [field_name2 [field_name3 [...]]]*\n\n**Default**: *all the fields, including user defined fields*\n\n**Context**: *loc*\n\n定义输出格式。可以使用的字段：内置字段，以上面的名字来表示；自定义字段，用变量表示。\n'kv'总是每行的第一个字段。\n\n\nreq_status_zone_add_indicator\n--------------------------------\n\n**Syntax**: *req_status_zone_add_indecator zone_name $var1 [$var2 [...]]*\n\n**Default**: *none*\n\n**Context**: *http*\n\n通过变量增加自定义字段，新增加的字段目前会展现在每行的末尾。\n\n\nreq_status_zone_key_length\n-------------------------------\n\n**Syntax**: *req_status_zone_key_length zone_name length*\n\n**Default**: *none*\n\n**Context**: *http*\n\n定义某个共享内存块中key的最大长度，默认值104。key中超出的部分会被截断。\n\n\nreq_status_zone_recycle\n-------------------------------\n\n**Syntax**: *req_status_zone_recycle zone_name times seconds*\n\n**Default**: *none*\n\n**Context**: *http*\n\n定义某个共享内存块过期数据的回收。回收在共享内存耗尽时自动开启。只会回收访问频率低于设置值的监控数据。\n频率定义为 times / seconds，默认值为10r/min，即\n     req_status_zone_recycle demo_zone 10 60;\n\n\nreq_status_lazy\n-------------------------------\n\n**Syntax**: *req_status_lazy on|off*\n\n**Default**: *off*\n\n**Context**: *http、srv、loc*\n\nreq_status_lazy指令用于控制req_status_zone指令中配置的变量是否在log阶段重新取值，用来解决部分变量（如upstream_xxx相关变量）作为key场景下获取是空的问题。\n"
  },
  {
    "path": "docs/modules/ngx_http_spdy_module.md",
    "content": "# Name #\n\n**NOTE**\n1. This module has been updated to official nginx SPDY/3.1 module, the document is available here:\n   http://nginx.org/en/docs/http/ngx_http_spdy_module.html\n2. This document only applies to Tengine-2.1.0 and its old version.  But the listen option `spdy_detect` can still be used with SPDY/3.1.\n3. Tengine-2.2.0 or later provides support for HTTP/2 and supersedes the SPDY module.\n\n**ngx\\_http\\_spdy\\_module**\n\nTengine added SPDY/3 support to this module. The new directives are listed below.\n\n\n# Directives #\n\n## spdy\\_version ##\n\nSyntax: **spdy\\_version** [2|3]\n\nDefault: 3\n\nContext: http, server\n\nSpecify the version of current SPDY protocol.\n\n## spdy\\_flow\\_control ##\n\nSyntax: **spdy\\_flow\\_control** on|off\n\nDefault: on\n\nContext: http, server\n\nTurn on or off with SPDY flow control.\n\n## spdy\\_init\\_recv\\_window\\_size ##\n\nSyntax: **spdy\\_init\\_recv\\_window\\_size** size\n\nDefault: 64k\n\nContext: http, server\n\nSpecify the receiving window size for SPDY. By default, it's 64K. It will send a WINDOW UPDATE frame when it receives half of the window size data every time.\n\n## spdy\\_detect ##\n\nSyntax: listen address[:port] [spdy_detect] [ssl]\n\nDefault:\n\nContext: listen directive\n\nServer can work for SPDY and HTTP on the same port with this directive. Note that the server will examine the first byte of one connection and determine whether the connection is SPDY or HTTP based on what it looks like (0x80 or 0x00 for SPDY).\n\nServer will listen on port 80 for SPDY and HTTP, for example:\n\n    listen 80 spdy_detect;\n\nServer will detect whether SPDY or HTTP is used without using a TLS extension (NPN), for example:\n\n    listen 443 ssl spdy_detect;\n\nServer can detect whether SPDY or HTTP is used directly, and also it can negotiate with client via NPN, for example:\n\n    listen 443 ssl spdy_detect spdy;\n\n\n"
  },
  {
    "path": "docs/modules/ngx_http_spdy_module_cn.md",
    "content": "# Name #\n\n**注意**\n1. Tengine-2.1.0以上版本的SPDY模块已经与官方nginx同步，只支持SPDY/3.1。\n   文档参考: http://nginx.org/en/docs/http/ngx_http_SPDY_module.html\n2. 以下文档只适用于Tengine-2.1.0及以下版本，只支持SPDY/2和SPDY/3。listen参数`spdy_detect`在SPDY/3.1下任然可以使用。\n3. Tengine-2.2.0以上版本将该模块删除，改为支持HTTP/2模块。\n\n**ngx\\_http\\_spdy\\_module**\n\nTengine对SPDY模块增加SPDY/3协议的支持。以下是新增的指令。\n\n\n# Directives #\n\n## spdy\\_version ##\n\nSyntax: **spdy\\_version** [2|3]\n\nDefault: 3\n\nContext: http, server\n\n指定SPDY协议使用的版本。默认是SPDY/3。\n\n## spdy\\_flow\\_control ##\n\nSyntax: **spdy\\_flow\\_control** on|off\n\nDefault: on\n\nContext: http, server\n\n打开或关闭SPDY/3的流控功能。\n\n## spdy\\_init\\_recv\\_window\\_size ##\n\nSyntax: **spdy\\_init\\_recv\\_window\\_size** size\n\nDefault: 64k\n\nContext: http, server\n\n指定SPDY/3服务器的接收窗口大小。接收窗口大小默认值是64K。服务器每次会在接收窗口使用超过一半时给客户端发送窗口更新帧(WINDOW UPDATE frame)。\n\n## spdy\\_detect ##\n\nSyntax: listen address[:port] [spdy_detect] [ssl]\n\nDefault:\n\nContext: listen directive\n\n启用这个指令时，SPDY协议和HTTP协议可以工作在同一个端口上。注意：服务器通过探测每个TCP连接上的首字节来判断此连接上是SPDY协议还是HTTP协议(如果首字节是0x80或者0x00，则认为是SPDY协议)。\n\n服务器在80端口上同时监听SPDY连接和HTTP连接，配置如下：\n\n    listen 80 spdy_detect;\n\n服务器在443端口上自动探测SSL层下是SPDY协议还是HTTP协议。注意服务器不会通过TLS扩展(NPN)来协商是SPDY协议还是HTTP协议，配置如下：\n\n    listen 443 ssl spdy_detect;\n\n服务器在443端口上既可以自动探测SSL层下是SPDY协议还是HTTP协议，也可以通过TLS扩展(NPN)来协商是SPDY协议还是HTTP协议，配置如下：\n\n    listen 443 ssl spdy_detect spdy;\n\n\n"
  },
  {
    "path": "docs/modules/ngx_http_ssl_asynchronous_mode.md",
    "content": "Name\n====\n\n* Nginx SSL/TLS asynchronous mode\n\nDescription\n===========\n\nProvide information about how to enable SSL/TLS asynchronous in Nginx.\n* SSL/TLS asynchronous mode is provided by OpenSSL 1.1.0+ version\n\nCompilation\n===========\n\nBuild Nginx with configuration item '--with-openssl-async'\n\nDirectives\n===========\n\n**Syntax**:     ssl_async on | off;\n\n**Default**:  ssl_async off;\n\n**Context**:    http, server\n\nEnables SSL/TLS asynchronous mode for the given virtual server.\n\nExample\n==========\n\nfile: conf/nginx.conf\n'''\n    http {\n        ssl_async  on;\n        server {\n            ...\n            }\n        }\n    }\n'''\nOR\n'''\n    http {\n        server {\n            ssl_async  on;\n            }\n        }\n    }\n'''\n\nNote\n========================\nTo demostrate the asynchronous mode of SSL/TLS, it needs an asynchronous enabled\nengine support. As a reference implementation, OpenSSL 1.1.0+ version provides\nan 'dasync' engine which support the asynchronous working flow.\n'dasync' engine will be built as a shared library 'dasync.so' in engines/\nPlease use below reference openssl.cnf file to enable it for RSA offloading.\n\n    openssl_conf = openssl_def\n    [openssl_def]\n    engines = engine_section\n    [engine_section]\n    dasync = dasync_section\n    [dasync_section]\n    engine_id = dasync\n    dynamic_path = /path/to/openssl/source/engines/dasync.so\n    default_algorithms = RSA\n\nFor more details information, please refer to https://www.openssl.org\n"
  },
  {
    "path": "docs/modules/ngx_http_ssl_asynchronous_mode_cn.md",
    "content": "名称\n====\n\n* Nginx SSL/TLS 异步模式\n\nDescription\n===========\n\n本文档提供关于如何在Nginx开启异步SSL/TLS支持的说明.\n* 异步SSL/TLS模式是OpenSSL 1.1.0版本之后引入的新的模式\n\n编译支持\n===========\n\n启用--with-openssl-async编译选项\n\n配置项\n===========\n\n**语法**:     ssl_async on | off;\n\n**默认值**:  ssl_async off;\n\n**作用域**:    http, server\n\n在给定的http块或者虚拟server块中配置启用异步SSL/TLS模式\n\n配置示例\n==========\n\n配置文件: conf/nginx.conf\n'''\n    http {\n        ssl_async  on;\n        server {\n            ...\n            }\n        }\n    }\n'''\n或\n'''\n    http {\n        server {\n            ssl_async  on;\n            }\n        }\n    }\n'''\n\n说明\n========================\n为了展示Nginx启用异步SSL/TLS的效果，需要OpenSSL在算法层提供支持异步的引擎模块\n在OpenSSL 1.1.0之后的版本中，默认提供了名为'dasync'的参考异步引擎\n在完成OpenSSL编译后,异步引擎'dasync'会以共享库'dasync.so'的形式出现在engines/\n目录下,使用如下openssl.cnf配置文件中的配置可以使能'dasync'异步引擎用于RSA算法\n\n    openssl_conf = openssl_def\n    [openssl_def]\n    engines = engine_section\n    [engine_section]\n    dasync = dasync_section\n    [dasync_section]\n    engine_id = dasync\n    dynamic_path = /path/to/openssl/source/engines/dasync.so\n    default_algorithms = RSA\n\n更多详细信息请参考https://www.openssl.org\n"
  },
  {
    "path": "docs/modules/ngx_http_sysguard.md",
    "content": "# ngx_http_sysguard_module\n\n## Description\n\nThis module monitors memory usage (including the swap partition), load of CPUs and average response time of requests of the system and cpu usage. If any guideline that is monitored exceeds the threshold set by user, the current request will be redirected to a specific url. To be clarified, this module can only be full functional when the system supports sysinfo function and loadavg function. The sysguard module also need to read memory information from /proc file system.\n\n## Configuration\n\n    server {\n        sysguard on;\n        sysguard_mode or;\n\n        sysguard_load load=10.5 action=/loadlimit;\n        sysguard_mem swapratio=20% action=/swaplimit;\n        sysguard_mem free=100M action=/freelimit;\n        sysguard_rt rt=0.01 period=5s action=/rtlimit;\n\n        location /loadlimit {\n            return 503;\n        }\n\n        location /swaplimit {\n            return 503;\n        }\n\n        location /freelimit {\n            return 503;\n        }\n\n        location /rtlimit {\n            return 503;\n        }\n        \n        location /cpulimit {\n            return 503;\n        }        \n    }\n\n## Directives\n\n**sysguard** `on` | `off`\n\n**Default:** `sysguard off`\n\n**Context:** `http, server, location` \n     \nEnable or disable the sysguard module.\n\n<br/>\n<br/>\n\n**sysguard_load** `load=[ncpu*]number [action=/url]`\n\n**Default:** `none`\n\n**Context:** `http, server, location`\n\nThis directive tells the module to protect the system by monitoring the load of CPUs. If the system's loads reach the value that is specified by 'number' in one minute, the incoming request will be redirected to the url specified by 'action' parameter. If 'action' is not specified, tengine will respond with 503 error directly. It's also possible to use ncpu\\* to make the configuration, in which case, ncpu stands for the number of the CPU cores. For instance, load = ncpu*1.5.\n\n<br/>\n<br/>\n\n**sysguard_cpu** `usage=num [period=time] [action=/url]`\n\n**Default:** `period=3s`\n\n**Context:** `http, server, location`\n\nThis directive tells the module to protect the system by monitoring the CPU usage. When the system in the `period` (` units: s `) within the CPU reach to num (` note: ` the num is a integer. such as protection CPU is 50% and could be set the usage = 50), the incoming request will be redirected to the url specified by 'action' parameter. If the action is not configured, tengine will return 503 directly.\n\n\n```\ncpu usage formula：\n    cpu_usage = [(cur.usr + cur.nice + cur.sys) - (pre.usr + pre.nice + pre.sys)]/\n                [(cur.usr + cur.nice + cur.sys + cur.iowait + cur.irq \n                 + cur.softirq + cur.idle)\n                 - (pre.usr + pre.nice + pre.sys + pre.iowait + pre.irq \n                 + pre.softirq + pre.idle)] * 100\n```\n\n<br/>\n<br/>\n\n\n**sysguard_mem** `[swapratio=ratio%] [free=size] [action=/url]`\n\n**Default:** `-`\n\n**Context:** `http, server, location`\n\nThis directive is used to tell the module to protect the system by monitoring memroy usage. 'swapratio' is used to specify how many percent of the swap partition of the system, and 'free' is used to specify the miminum size of current memory. If any condition is fulfilled, the incoming request will be redirected to specified url, which is defined by parameter 'action'. If 'action' is not specified, the request will be responded with 503 error directly. Besides, if the user disables the swap partition in the system, this directive will not be functional. 'free' is calculated by /proc/meminfo, the algorithm is 'memfree = free + buffered + cached'. \n\n<br/>\n<br/>\n\n**sysguard_rt** `[rt=seconds] [period=time] [action=/url]`\n\n**Default:** `-`\n                \n**Context:** `http, server, location`\n\nThis directive is used to tell the module to protect the system by monitoring average response time of requests in a specified period. Parameter rt is used to set a threshold of the average response time, in second. Parameter period is used to specifiy the period of the statistics cycle. If the average response time of the system exceeds the threshold specified by the user, the incoming request will be redirected to a specified url which is defined by parameter 'action'. If no 'action' is presented, the request will be responded with 503 error directly.\n\n<br/>\n<br/>\n\n**sysguard_mode** `and` | `or`\n\n**Default:**  `sysguard_mode or` \n\n**Context** `http, server, location`\n\nIf there are more than one type of monitor, this directive is used to specified the relations among all the monitors which are: 'and' for all matching and 'or' for any matching.\n\n<br/>\n<br/>\n\n**sysguard_interval** `time`\n       \n**Default** `sysguard_interval 1s`\n         \n**Context** `http, server, location`\n       \nSpecify the time interval to update your system information.\n\n<br/>\n<br/>\n\n**sysguard_log_level** `[info | notice | warn | error]`\n       \n**Default** `sysguard_log_level error`\n         \n**Context** `http, server, location`\n       \nSpecify the log level of sysguard.\n\n## Installation\n\n 1. Compile sysguard module\n         \n    configure  [--with-http_sysguard_module | --with-http_sysguard_module=shared]\n\n    --with-http_sysguard_module, sysguard module will be compiled statically into tengine.\n\n    --with-http_sysguard_module=shared, sysguard module will be compiled dynamically into tengine.\n\n 2. Build and install\n\n    make&make install\n \n 3. Make sysguard configuration\n \n 4. Run\n"
  },
  {
    "path": "docs/modules/ngx_http_sysguard_cn.md",
    "content": "# sysguard 模块\n\n## 介绍\n\n该模块监控内存（含swap分区）、CPU和请求的响应时间，当某些监控指标达到设定的阈值时，跳转到指定的url。注意，目前该模块仅对系统支持sysinfo函数时，才支持基于load与内存信息的保护，以及系统支持loadavg函数时支持基于load进行保护。模块需要从/proc文件系统中读取内存信息。\n\n## 配置\n\n    server {\n        sysguard on;\n        sysguard_mode or;\n\n        sysguard_load load=10.5 action=/loadlimit;\n        sysguard_cpu usage=20 period=3s action=/cpulimit;\n        sysguard_mem swapratio=20% action=/swaplimit;\n        sysguard_mem free=100M action=/freelimit;\n        sysguard_rt rt=0.01 period=5s action=/rtlimit;\n\n        location /loadlimit {\n            return 503;\n        }\n\n        location /swaplimit {\n            return 503;\n        }\n\n        location /freelimit {\n            return 503;\n        }\n\n        location /rtlimit {\n            return 503;\n        }\n\n        location /cpulimit {\n            return 503;\n        }\n    }\n\n## 指令\n\n**sysguard** `on` | `off`\n\n**默认:** `sysguard off`\n\n**上下文:** `http, server, location` \n     \n打开或者关闭这个模块\n\n<br/>\n<br/>\n\n**sysguard_load** `load=[ncpu*]number [action=/url]`\n\n**默认:** `none`\n\n**上下文:** `http, server, location`\n\n该指令用于配置根据系统的load来限制用户的请求，以保护系统。当系统在一分钟内的load达到number时，将进来的请求转到action所指定的url。如果action没有配置，则直接返回503错误。load的数值还支持使用ncpu\\*系数的方式来配置，ncpu表示cpu核数，乘以固定的系数得出期望限制的load值，如: load=ncpu\\*1.5。\n\n<br/>\n<br/>\n\n**sysguard_cpu** `usage=num [period=time] [action=/url]`\n\n**默认:** `period=3s`\n\n**上下文:** `http, server, location`\n\n该指令用于配置根据系统的cpu来限制用户的请求，以保护系统。当系统在period(`单位：s`)内的cpu达到num(`注意：` 取值是整数：如cpu保护阀值想设置为50%则可设置usage=50)时，将进来的请求转到action所指定的url。如果action没有配置，则直接返回503错误。\n\n```\n计算公式：\n    cpu_usage = [(cur.usr + cur.nice + cur.sys) - (pre.usr + pre.nice + pre.sys)]/\n                [(cur.usr + cur.nice + cur.sys + cur.iowait + cur.irq \n                 + cur.softirq + cur.idle)\n                 - (pre.usr + pre.nice + pre.sys + pre.iowait + pre.irq \n                 + pre.softirq + pre.idle)] * 100\n```\n\n<br/>\n<br/>\n\n**sysguard_mem** `[swapratio=ratio%] [free=size] [action=/url]`\n\n**默认:** `-`\n\n**上下文:** `http, server, location`\n\n该指令用于配置根据系统的内存使用状态来限制用户请求，以保护系统。swapratio用于配置当当前交换空间的已使用ratio%时，或者剩下的内存少于size时，就将进来的请求跳转到指定的url。如果action没有配置，则直接返回503错误。另外，如果用户自己禁用了交换区间，则配置该指定是不起作用的。free是根据/proc/meminfo的内容来计算的，计算公式是\"memfree= free + buffered + cached\"\n\n<br/>\n<br/>\n\n**sysguard_rt** `[rt=seconds] [period=time] [action=/url]`\n\n**默认:** `-`\n                \n**上下文:** `http, server, location`\n\n该指令用于配置根据系统的请求平均响应时间来限制用户请求，以保护系统。rt参数用于设置请求的平均响应时间的阈值，单位为秒，平均响应时间的统计周期使用period参数设置。当系统的请求平均响应时间大于阈值时，将当前请求跳转到action参数配置的url，如果action没有配置，则直接返回503。\n\n<br/>\n<br/>\n\n**sysguard_mode** `and` | `or`\n\n**默认:**  `sysguard_mode or` \n\n**上下文** `http, server, location`\n\n如果设置了多个监控指标，此参数用于指定指标间的判断关系，and为全部满足，or为任一满足。\n\n<br/>\n<br/>\n\n**sysguard_interval** `time`\n       \n**默认** `sysguard_interval 1s`\n         \n**上下文** `http, server, location`\n       \n该指定用于配置获取系统信息时的缓存时间。默认为1s，则表示在这1s内，只调用一次系统函数来获取系统的当前状况。\n\n<br/>\n<br/>\n\n**sysguard_log_level** `[info | notice | warn | error]`\n       \n**默认** `sysguard_log_level error`\n         \n**上下文** `http, server, location`\n       \n该指令用于配置，当保护系统的操作执行时，记录日志时的日志级别。\n\n## 安装\n\n 1. 编译sysguard模块\n         \n    configure  [--with-http_sysguard_module | --with-http_sysguard_module=shared]\n\n    --with-http_sysguard_module选项，sysguard模块将被静态编译到tengine中\n\n    --with-http_sysguard_module=shared, sysguard模块将被编译成动态文件，采用动态模块的方式添加到tengine中\n\n 2. 编译,安装\n\n    make&make install\n \n 3. 配置sysguard的配置项\n \n 4. 运行\n"
  },
  {
    "path": "docs/modules/ngx_http_tfs_module.md",
    "content": "Name\n====\n\n* TFS module\n\nDescription\n===========\n\n* This module implements an asynchronous client of TFS(Taobao File System), providing RESTful API to it. [TFS](http://tfs.taobao.org) is a distributed file system developed by Taobao Inc.\n\nThis module is not built by default, it should be enabled with the `--with-http_tfs_module` configuration parameter.\n\nExample\n=======\n\n    http {\n        #tfs_upstream tfs_rc {\n        #    server 127.0.0.1:6100;\n        #    type rcs;\n        #    rcs_zone name=tfs1 size=128M;\n        #    rcs_interface eth0;\n        #    rcs_heartbeat lock_file=/logs/lk.file interval=10s;\n        #}\n\n        tfs_upstream tfs_ns {\n            server 127.0.0.1:8108;\n            type ns;\n        }\n\n        server {\n              listen       7500;\n              server_name  localhost;\n\n              tfs_keepalive max_cached=100 bucket_count=10;\n              tfs_log \"pipe:/usr/sbin/cronolog -p 30min /path/to/nginx/logs/cronolog/%Y/%m/%Y-%m-%d-%H-%M-tfs_access.log\";\n\n              location / {\n                  tfs_pass tfs://tfs_ns;\n              }\n        }\n    }\n\nDirectives\n==========\n\ntfs\\_upstream\n----------------\n\n**Syntax**： *tfs\\_upstream name {...}*\n\n**Default**： *none*\n\n**Context**： *http*\n\nDefines information of upstream TFS server.\n\nExample:\n\n    tfs_upstream tfs_rc {\n        server 127.0.0.1:6100;\n        type rcs;\n        rcs_zone name=tfs1 size=128M;\n        rcs_interface eth0;\n        rcs_heartbeat lock_file=/logs/lk.file interval=10s;\n    }\n\n    tfs_upstream tfs_ns {\n        server 127.0.0.1:8100;\n        type ns;\n    }\n\nserver\n------------\n\n**Syntax**： *server address*\n\n**Default**： *none*\n\n**Context**： *tfs_upstream*\n\nDefines the address of upstream TFS server. An address can be specified as a domain name or IP address.\n\nExample :\n\n\tserver 10.0.0.1:8108;\n\ntype\n----------------\n\n**Syntax**： *type [ns | rcs]*\n\n**Default**： *ns*\n\n**Context**： *tfs_upstream*\n\nSpecify the type of upstream TFS server. It could be ns(NameServer) or rcs(RcServer), default is ns.\n\nrcs\\_zone\n--------------\n\n**Syntax**： *rcs_zone name=n size=num*\n\n**Default**： *none*\n\n**Context**： *tfs_upstream*\n\nDefines the shared memory zone used to store application configuration information registerd in RcServer. This directive is mandatory when upstream is RcServer. Application configuration information can be updated through heartbeat with RcServer(directive <i>rcs_heartbeat</i>).\n\nExample:\n\n\trcs_zone name=tfs1 size=128M;\n\nrcs\\_heartbeat\n--------------\n\n**Syntax**： *rcs_heartbeat lock_file=/path/to/file interval=time*\n\n**Default**： *none*\n\n**Context**： *tfs_upstream*\n\nEnable heartbeat with RcServer so that application configuration information can be updated in time. It is mandatory when upstream is RcServer.\n\nThe following parameters must be defined:\n\nlock_file=<i>/path/to/file</i>\n    use to create a mutex so that only one Nginx worker can do heartbeat at one time.\ninterval=<i>time</i>\n    set heartbeat interval.\n\nExample:\n\n\trcs_heartbeat lock_file=/path/to/nginx/logs/lk.file interval=10s;\n\nrcs\\_interface\n----------------\n\n**Syntax**： *rcs\\_interface interface*\n\n**Default**： *none*\n\n**Context**： *tfs_upstream*\n\nSpecify net interface used by TFS module. It is used to get local IP address. It is only mandatory when upstream is RCServer.\n\nExample:\n\n\trcs_interface eth0;\n\ntfs_pass\n--------\n\n**Syntax**： *tfs_pass name*\n\n**Default**： *none*\n\n**Context**： *location*\n\nSpecify TFS upstream. Remember that protocol used here must be \"tfs\".\n\nExample:\n\n\ttfs_upstream tfs_rc {\n    };\n\n\tlocation / {\n\t\ttfs_pass tfs://tfs_rc;\n\t}\n\ntfs_keepalive\n-------------\n\n**Syntax**： *tfs_keepalive max_cached=num bucket_count=num*\n\n**Default**： *none*\n\n**Context**： *http, server*\n\nDefines connection pool used by TFS module. This connection pool caches upstream connections.\n\nThe following parameters must be defined:\n\nmax_cached=<i>num</i>\n    set the capacity of one hash bucket.\nbucket_count=<i>num</i>\n    set the count of hash buckets.\n\nExample:\n\n\ttfs_keepalive max_cached=100 bucket_count=15;\n\ntfs\\_block\\_cache\\_zone\n-----------------------\n\n**Syntax**： *tfs_block_cache_zone size=num*\n\n**Default**： *none*\n\n**Context**： *http*\n\nDefines the shared memory zone used for BlockCache.\n\nExample:\n\n\ttfs_block_cache_zone size=256M;\n\ntfs\\_log\n----------------\n\n**Syntax**： *tfs_log path*\n\n**Default**： *none*\n\n**Context**： *http, server*\n\nSets the TFS access log.\n\nExample:\n\n\ttfs_log \"pipe:/usr/sbin/cronolog -p 30min /path/to/nginx/logs/cronolog/%Y/%m/%Y-%m-%d-%H-%M-tfs_access.log\";\n\ntfs\\_body\\_buffer\\_size\n-----------------------\n\n**Syntax**： *tfs_body_buffer_size size*\n\n**Default**： *2m*\n\n**Context**： *http, server, location*\n\nSets the buffer size for reading upstream response. By default, buffer size is 2m.\n\nExample:\n\n\ttfs_body_buffer_size 2m;\n\ntfs\\_connect\\_timeout\n---------------------\n\n**Syntax**： *tfs_connect_timeout time*\n\n**Default**： *3s*\n\n**Context**： *http, server, location*\n\nSets a timeout for connecting upstream servers.\n\ntfs\\_send\\_timeout\n------------------\n\n**Syntax**： *tfs_send_timeout time*\n\n**Default**： *3s*\n\n**Context**： *http, server, location*\n\nSets a timeout for transmitting data to upstream servers.\n\ntfs\\_read\\_timeout\n------------------\n\n**Syntax**： *tfs_read_timeout time*\n\n**Default**： *3s*\n\n**Context**： *http, server, location*\n\nSets a timeout for reading data from upstream servers.\n\nOthers\n------\nUploading file size supported depends on the directive <i>client_max_body_size</i>.\n"
  },
  {
    "path": "docs/modules/ngx_http_tfs_module_cn.md",
    "content": "模块名\n====\n\n* nginx-tfs\n\n描述\n====\n\n* 这个模块实现了TFS的客户端，为TFS提供了RESTful API。TFS的全称是Taobao File System，是淘宝开源的一个分布式文件系统。\n\n编译安装\n=======\n\n1. TFS模块使用了一个开源的JSON库来支持JSON，请先安装[yajl](http://lloyd.github.com/yajl/)-2.0.1。\n\n2. 下载[nginx](http://www.nginx.org/)或[tengine](http://tengine.taobao.org/)。\n\n3. ./configure --add-module=/path/to/nginx-tfs\n\n4. make && make install\n\n配置\n====\n\n    http {\n        #tfs_upstream tfs_rc {\n        #    server 127.0.0.1:6100;\n        #    type rcs;\n        #    rcs_zone name=tfs1 size=128M;\n        #    rcs_interface eth0;\n        #    rcs_heartbeat lock_file=/logs/lk.file interval=10s;\n        #}\n\n        tfs_upstream tfs_ns {\n            server 127.0.0.1:6100;\n            type ns;\n        }\n\n        server {\n              listen       7500;\n              server_name  localhost;\n\n              tfs_keepalive max_cached=100 bucket_count=10;\n              tfs_log \"pipe:/usr/sbin/cronolog -p 30min /path/to/nginx/logs/cronolog/%Y/%m/%Y-%m-%d-%H-%M-tfs_access.log\";\n\n              location / {\n                  tfs_pass tfs://tfs_ns;\n              }\n        }\n    }\n\n指令\n====\n\nserver\n------------\n\n**Syntax**： *server address*\n\n**Default**： *none*\n\n**Context**： *tfs_upstream*\n\n指定后端TFS服务器的地址，当指令<i>type</i>为<i>rcs</i>时为RcServer的地址，如果为为<i>ns</i>时为NameServer的地址。此指令必须配置。例如:\n\n\tserver 10.0.0.1:8108;\n\ntype\n----------------\n\n**Syntax**： *type [ns | rcs]*\n\n**Default**： *ns*\n\n**Context**： *tfs_upstream*\n\n设置server类型，类型只能为ns或者rcs，如果为ns,则指令<i>server</i>指定的地址为NameServer的地址，如果为rcs,则为RcServer的地址。如需使用自定义文件名功能请设置类型为rcs，使用自定义文件名功能需额外配置MetaServer和RootServer。\n\nrcs\\_zone\n--------------\n\n**Syntax**： *rcs_zone name=n size=num*\n\n**Default**： *none*\n\n**Context**： *tfs_upstream*\n\n配置TFS应用在RcServer的配置信息。若开启RcServer（配置了<i>type rcs</i>），则必须配置此指令。配置此指令会在共享内存中缓存TFS应用在RcServer的配置信息，并可以通过指令<i>rcs_heartbeat</i>来和RcServer进行keepalive以保证应用的配置信息的及时更新。例如：\n\n\trcs_zone name=tfs1 size=128M;\n\nrcs\\_heartbeat\n--------------\n\n**Syntax**： *rcs_heartbeat lock_file=/path/to/file interval=time*\n\n**Default**： *none*\n\n**Context**： *tfs_upstream*\n\n配置TFS应用和RcServer的keepalive，应用可通过此功能来和RcServer定期交互，以及时更新其配置信息。若开启RcServer功能（配置了<i>type rcs</i>），则必须配置此指令。例如：\n\n\trcs_heartbeat lock_file=/path/to/nginx/logs/lk.file interval=10s;\n\nrcs\\_interface\n----------------\n\n**Syntax**： *rcs\\_interface interface*\n\n**Default**： *none*\n\n**Context**： *tfs_upstream*\n\n配置TFS模块使用的网卡。若开启RcServer功能（配置了<i>type rcs</i>），则必须配置此指令。例如：\n\n\trcs_interface eth0;\n\ntfs\\_upstream\n----------------\n\n**Syntax**： *tfs\\_upstream name {...}*\n\n**Default**： *none*\n\n**Context**： *http*\n\n配置TFS模块的server信息,这个块包括上面几个命令。例如：\n\n    tfs_upstream tfs_rc {\n        server 127.0.0.1:6100;\n        type rcs;\n        rcs_zone name=tfs1 size=128M;\n        rcs_interface eth0;\n        rcs_heartbeat lock_file=/logs/lk.file interval=10s;\n    }\n\n\ntfs_pass\n--------\n\n**Syntax**： *tfs_pass name*\n\n**Default**： *none*\n\n**Context**： *location*\n\n是否打开TFS模块功能，此指令为关键指令，决定请求是否由TFS模块处理，必须配置。需要注意，这里不支持直接写ip地址或者域名，这里只支持指令<i>tfs_upstream name {...} </i>配置的upstream,并且必须以 tfs:// 开头。例如：\n\n\n\ttfs_upstream tfs_rc {\n    };\n\n\tlocation / {\n\t\ttfs_pass tfs://tfs_rc;\n\t}\n\ntfs_keepalive\n-------------\n\n**Syntax**： *tfs_keepalive max_cached=num bucket_count=num*\n\n**Default**： *none*\n\n**Context**： *http, server*\n\n配置TFS模块使用的连接池的大小，TFS模块的连接池会缓存TFS模块和后端TFS服务器的连接。可以把这个连接池看作由多个hash桶构成的hash表，其中bucket\\_count是桶的个数，max\\_cached是桶的容量。此指令必须配置。注意，应该根据机器的内存情况来合理配置连接池的大小。例如：\n\n\ttfs_keepalive max_cached=100 bucket_count=15;\n\ntfs\\_block\\_cache\\_zone\n-----------------------\n\n**Syntax**： *tfs_block_cache_zone size=num*\n\n**Default**： *none*\n\n**Context**： *http*\n\n配置TFS模块的本地BlockCache。配置此指令会在共享内存中缓存TFS中的Block和DataServer的映射关系。注意，应根据机器的内存情况来合理配置BlockCache大小。例如：\n\n\ttfs_block_cache_zone size=256M;\n\ntfs\\_log\n----------------\n\n**Syntax**： *tfs_log path*\n\n**Default**： *none*\n\n**Context**： *http, server*\n\n是否进行TFS访问记录。配置此指令会以固定格式将访问TFS的请求记录到指定log中，以便进行分析。具体格式参见代码。例如：\n\n\ttfs_log \"pipe:/usr/sbin/cronolog -p 30min /path/to/nginx/logs/cronolog/%Y/%m/%Y-%m-%d-%H-%M-tfs_access.log\";\n\n注：cronolog支持依赖于tengine提供的扩展的日志模块。\n\ntfs\\_body\\_buffer\\_size\n-----------------------\n\n**Syntax**： *tfs_body_buffer_size size*\n\n**Default**： *2m*\n\n**Context**： *http, server, location*\n\n配置用于和后端TFS服务器交互时使用的的body_buffer的大小。默认为2m。例如：\n\n\ttfs_body_buffer_size 2m;\n\ntfs\\_connect\\_timeout\n---------------------\n\n**Syntax**： *tfs_connect_timeout time*\n\n**Default**： *3s*\n\n**Context**： *http, server, location*\n\n配置连接后端TFS服务器的超时时间。\n\ntfs\\_send\\_timeout\n------------------\n\n**Syntax**： *tfs_send_timeout time*\n\n**Default**： *3s*\n\n**Context**： *http, server, location*\n\n配置向后端TFS服务器发送数据的超时时间。\n\ntfs\\_read\\_timeout\n------------------\n\n**Syntax**： *tfs_read_timeout time*\n\n**Default**： *3s*\n\n**Context**： *http, server, location*\n\n配置从后端TFS服务器接收数据的超时时间。\n\n其他\n----\n能支持上传文件大小决定于<i>client_max_body_size</i>指令配置的大小。\n"
  },
  {
    "path": "docs/modules/ngx_http_trim_filter_module.md",
    "content": "# Ngx_http_trim_filter module\n\nThe ngx_http_trim_filter module is a filter that modifies a response by removing unnecessary whitespaces \n(spaces, tabs, newlines) and comments from HTML (including inline javascript and css). Trim module parses \nHTML with a state machine.\n\n\n## Example Configuration\n\n    location / {\n        trim on;\n        trim_js on;\n        trim_css on;\n    }\n\n## Directives\n\n**trim** `on` | `off`\n\n**Default:** `trim off`\n\n**Context:** `http, server, location` \n     \nEnable or disable trim module for pure HTML.  \nThis module will retain some contents unchanged, in case that they are enclosed by the tag `pre`,`textarea`,`script` and `style`,as well as IE/SSI/ESI comments.  \nParameter value can contain variables.  \nExample:  \n\n    set $flag \"off\";\n    if ($condition) {\n        set $flag \"on\";\n    }\n    trim $flag;\n<br/>\n\n\n**trim_js** `on` | `off`\n\n**Default:** `trim_js off`\n\n**Context:** `http, server, location` \n     \nEnable or disable trim module for inline javascript.  \nParameter value can contain variables too.  \n<br/>\n\n\n**trim_css** `on` | `off`\n\n**Default:** `trim_css off`\n\n**Context:** `http, server, location` \n     \nEnable or disable trim module for inline css.  \nParameter value can contain variables too.  \n<br/>\n\n\n**trim_types** `MIME types`\n\n**Default:** `trim_types: text/html`\n\n**Context:** `http, server, location`\n\nEnable trim module for the specified MIME types in addition to \"text/html\". Responses with the “text/html” type are always processed.  \n<br/>\n\n\n## Debug\n\nTrim module will be disabled if incoming request has `http_trim=off` parameter in url.   \ne.g.  `http://www.xxx.com/index.html?http_trim=off`  \n\n## Sample\noriginal:\n\n    <!DOCTYPE html>\n    <textarea  >\n       trim\n            module\n    </textarea  >\n    <!--remove all-->\n    <!--[if IE]> trim module <![endif]-->\n    <!--[if !IE ]>--> trim module  <!--<![endif]-->\n    <!--# ssi-->\n    <!--esi-->\n    <pre    style  =\n        \"color:   blue\"  >Welcome    to    nginx!</pre  >\n    <script type=\"text/javascript\">\n    /***  muitl comment \n                       ***/\n    //// single comment\n    str.replace(/     /,\"hello\");\n    </script>\n    <style   type=\"text/css\"  >\n    /*** css comment\n                     ! ***/\n    body\n    {\n      font-size:  20px ;\n      line-height: 150% ;\n    }\n    </style>\n    \nresult:\n\n    <!DOCTYPE html>\n    <textarea>\n       trim  \n            module\n    </textarea>\n    <!--[if IE]> trim module <![endif]-->\n    <!--[if !IE ]>--> trim module  <!--<![endif]-->\n    <!--# ssi-->\n    <!--esi-->\n    <pre style=\"color:   blue\">Welcome    to    nginx!</pre>\n    <script type=\"text/javascript\">str.replace(/     /,\"hello\");</script>\n    <style type=\"text/css\">body{font-size:20px;line-height:150%;}</style>\n\n\n## Trim Rule\n\n### Html\n##### Whitespace\n+ Remove '\\r'.\n+ Replace '\\t' with space.\n+ Replace multiple spaces with a single space.\n+ Replace multiple '\\n' with a single '\\n'.\n+ Replace multiple '\\n' and '\\t' in tag with a single space.\n+ Do not trim quoted strings in tag.\n+ Do not trim the contents enclosed by the tag `pre`,`textarea`,`script` and `style`.\n\n##### Comment\n+ Remove html comment(`<!-- -->`).\n+ Do not trim IE/SSI/ESI comments.  \n  IE comment: `<!--[if  <![endif]-->`  \n  SSI comment: `<!--#  -->`  \n  ESI comment: `<!--esi  -->`  \n\n\n### Javascript\nContents enclosed by `<script type=\"text/javascript\">` or `<script>` will be identified as javascript.\n\n##### Whitespace\n+ Remove '\\r'.\n+ Remove '\\t','\\n' and space that behind '(',',','=',':','[','!','&','|','?',';','>','~','*','{'.\n+ Replace multiple spaces with a single space.\n+ Do not trim quoted strings and regular expression literals.\n\n##### Comment\n+ Remove single comment. `//`\n+ Remove multi comment. `/*  */`\n\n\n### Css\nContents enclosed by `<style type=\"text/css\">` or `<style>` will be identified as css.\n\n##### Whiltespace\n+ Remove '\\r'.\n+ Remove '\\t','\\n' and space that around ';','>','{','}',':',','.\n+ Replace multiple '\\n' and spaces with a single space.\n+ Do not trim quoted strings.\n\n##### Comment\n+ Remove css comment(`/* */`).\n+ Do not remove child seletor and IE5 /Mac hack comments.  \n  Child seletor hack: `html>/**/body p{color:blue}`  \n  IE5 /Mac hack: `/*\\*/.selector{color:khaki}/**/`  \n"
  },
  {
    "path": "docs/modules/ngx_http_trim_filter_module_cn.md",
    "content": "# trim 模块\n\n## 介绍\n\n该模块用于删除 html ， 内嵌 javascript 和 css 中的注释以及重复的空白符。\n\n\n## 配置\n\n    location / {\n        trim on;\n        trim_js on;\n        trim_css on;\n    }\n\n## 指令\n\n**trim** `on` | `off`\n\n**默认:** `trim off`\n\n**上下文:** `http, server, location` \n     \n使模块有效（失效），删除 html 的注释以及重复的空白符（\\n，\\r，\\t，' '）。   \n例外：对于 `pre`，`textarea`，`script`，`style` 和 ie/ssi/esi注释 等标签内的内容不作删除操作。  \n参数值可以包含变量。  \n例如：\n\n    set $flag \"off\";\n    if ($condition) {\n        set $flag \"on\";\n    }\n    trim $flag;\n<br/>\n\n**trim_js** `on` | `off`\n\n**默认:** `trim_js off`\n\n**上下文:** `http, server, location` \n     \n使模块有效（失效），删除 html 内嵌 javascript 的注释以及重复的空白符（\\n，\\r，\\t，' '）。   \n例外：对于非javascript代码的 `script` 标签内容不作删除操作。  \n参数值可以包含变量。 \n<br/>\n\n**trim_css** `on` | `off`\n\n**默认:** `trim_css off`\n\n**上下文:** `http, server, location` \n     \n使模块有效（失效），删除 html 内嵌 css 的注释以及重复的空白符（\\n，\\r，\\t，' ')。   \n例外：对于非css代码的 `style` 标签内容不作删除操作。  \n参数值可以包含变量。 \n<br/>\n\n**trim_types** `MIME types`\n\n**默认:** `trim_types: text/html`\n\n**上下文:** `http, server, location`\n\n定义哪些[MIME types](http://en.wikipedia.org/wiki/MIME_type)类型的响应可以被处理。  \n目前只能处理html格式的页面，js和css只针对于html内嵌的代码，不支持处理单独的js和css页面。  \n如果这样配置 `trim_type text/javascript;`，js代码将被作为html代码来处理而出错。\n<br/>\n\n## 调试\n\n添加请求参数http_trim=off，将关闭trim功能，返回原始代码，方便对照调试。   \n格式如下:  \n`http://www.xxx.com/index.html?http_trim=off`\n\n\n## 例子\n原始:\n\n    <!DOCTYPE html>\n    <textarea  >\n       trim\n            module\n    </textarea  >\n    <!--remove all-->\n    <!--[if IE]> trim module <![endif]-->\n    <!--[if !IE ]>--> trim module  <!--<![endif]-->\n    <!--# ssi-->\n    <!--esi-->\n    <pre    style  =\n        \"color:   blue\"  >Welcome    to    nginx!</pre  >\n    <script type=\"text/javascript\">\n    /***  muitl comment \n                       ***/\n    //// single comment\n    str.replace(/     /,\"hello\");\n    </script>\n    <style   type=\"text/css\"  >\n    /*** css comment\n                     ! ***/\n    body\n    {\n      font-size:  20px ;\n      line-height: 150% ;\n    }\n    </style>\n\n\n结果:\n\n\n    <!DOCTYPE html>\n    <textarea>\n       trim  \n            module\n    </textarea>\n    <!--[if IE]> trim module <![endif]-->\n    <!--[if !IE ]>--> trim module  <!--<![endif]-->\n    <!--# ssi-->\n    <!--esi-->\n    <pre style=\"color:   blue\">Welcome    to    nginx!</pre>\n    <script type=\"text/javascript\">str.replace(/     /,\"hello\");</script>\n    <style type=\"text/css\">body{font-size:20px;line-height:150%;}</style>\n    \n\n## trim规则\n\n### html\n#####  空白符\n\n+ 正文中的 '\\r' 直接删除。  \n+ 正文中的 '\\t' 替换为空格，然后重复的空格保留一个。 \n+ 正文中重复的 '\\n' 保留一个。  \n+ 标签中的 '\\t'，'\\n' 替换为空格，重复的空格保留一个，'=' 前后的空格直接删除，'>' 前面的空格直接删除。  \n+ 标签的双引号和单引号内的空白符不做删除。 \n\\<div class=\"no &nbsp; &nbsp; &nbsp;  trim\"\\>\n+ `pre` 和 `texterea` 标签的内容不做删除。  \n+ 支持 `pre` 嵌套使用。   \n+ `script` 和 `style` 标签的内容不做删除。  \n+ ie条件注释的内容不做删除。 \n+ ssi/esi注释的内容不做删除。  \n\n##### 注释\n+ 如果是ie条件注释不做删除。  \n   判断规则：`<!--[if <![endif]-->`  之间的内容判断为ie条件注释。\n+ 如果是ssi/esi注释的内容不做删除。  \n   判断规则：`<!--# -->`  `<!--esi -->`  之间的内容分别判断为ssi和esi注释。\n+ 其他正常html注释直接删除.  `<!--  -->`\n    \n### javascript  \n借鉴 jsmin 的处理规则 (http://www.crockford.com/javascript/jsmin.html)  \n`<script type=\"text/javascript\">` 或者 `<script>` 标签认为是javascript。  \n##### 空白符  \n+ '('，'['，'{'，';'，','，'>'，'=' 后的 '\\n'，'\\t'，空格 直接删除。\n+ '\\r' 直接删除。 \n+ 其他情况 重复的 '\\n'，'\\t'，空格 保留第一个。  \n+ 单引号和双引号内不删除。  \n     如下不做操作：  \n     \"hello   &nbsp;   \\\\\\\\\"  &nbsp;   world\"   \n     'hello  &nbsp;       \\'  &nbsp;   world'  \n+ 正则表达式的内容不删除。  \n     判断规则：'/' 前的非空字符是 ','，'('，'=' 三种的即认为是正则表达式。( 同jsmin的判断)   \n     如下不做操作：   \n     var re=/1 &nbsp; &nbsp; &nbsp;2/;     \n     data.match(/1  &nbsp;  &nbsp; 2/);  \n\n##### 注释  \n+ 删除单行注释。  `//`  \n+ 删除多行注释。  `/*   */`  \n注意：javascript也有一种条件注释，不过貌似用得很少，jsmin直接删除的，trim也是直接删除。  \nhttp://en.wikipedia.org/wiki/Conditional_comment  \n\n### css  \n借鉴 YUI Compressor 的处理规则 (http://yui.github.io/yuicompressor/css.html)   \n`<style type=\"text/css\">` 或者 `<style>` 标签认为是css。  \n##### 空白符  \n+ ';'，'>'，'{'，'}'，':'，',' 前后的 '\\n'，'\\t'，空格 直接删除。  \n+ '\\r' 直接删除。 \n+ 其他情况 连续的 '\\n'， '\\t' 和 空格 保留为一个空格。  \n+ 单引号和双引号内不删除。  \n     如下不做操作：  \n     \"hello   &nbsp;  \\\\\\\\\\\"  &nbsp;    world\"  \n      'hello  &nbsp;   \\'   &nbsp;  &nbsp;   world' \n\n##### 注释   \n+  child seletor hack的注释不删除。  \n      `html>/**/body p{color:blue}`  \n+  IE5 /Mac hack 的注释不删除。  \n     `/*\\*/.selector{color:khaki}/**/`  \n+  其他情况删除注释。  `/*    */`  \n\n"
  },
  {
    "path": "docs/modules/ngx_http_upstream_check_module.md",
    "content": "# Name #\n\n**ngx\\_http\\_upstream\\_check\\_module**\n\nAdd proactive health check for the upstream servers.\n\nThis module is not built by default, it should be enabled with the `--add-module=modules/ngx_http_upstream_check_module` configuration parameter.\n\n# Examples #\n\n\thttp {\n\t\tupstream cluster1 {\n\t\t\t# simple round-robin\n\t\t\tserver 192.168.0.1:80;\n\t\t\tserver 192.168.0.2:80;\n\n\t\t\tcheck interval=3000 rise=2 fall=5 timeout=1000 type=http;\n\t\t\tcheck_http_send \"HEAD / HTTP/1.0\\r\\n\\r\\n\";\n\t\t\tcheck_http_expect_alive http_2xx http_3xx;\n\t\t}\n\n\t\tupstream cluster2 {\n\t\t\t# simple round-robin\n\t\t\tserver 192.168.0.3:80;\n\t\t\tserver 192.168.0.4:80;\n\n\t\t\tcheck interval=3000 rise=2 fall=5 timeout=1000 type=http;\n\t\t\tcheck_keepalive_requests 100;\n\t\t\tcheck_http_send \"HEAD / HTTP/1.1\\r\\nConnection: keep-alive\\r\\nHost: foo.bar.com\\r\\n\\r\\n\";\n\t\t\tcheck_http_expect_alive http_2xx http_3xx;\n\t\t}\n\n\t\tserver {\n\t\t\tlisten 80;\n\n\t\t\tlocation /1 {\n\t\t\t\tproxy_pass http://cluster1;\n\t\t\t}\n\n\t\t\tlocation /2 {\n\t\t\t\tproxy_pass http://cluster2;\n\t\t\t}\n\n\t\t\tlocation /status {\n\t\t\t\tcheck_status;\n\n\t\t\t\taccess_log   off;\n\t\t\t\tallow SOME.IP.ADD.RESS;\n\t\t\t\tdeny all;\n\t\t\t}\n\t\t}\n\t}\n\n# Directives #\n\n## check ##\n\nSyntax: **check** `interval=milliseconds [fall=count] [rise=count] [timeout=milliseconds] [default_down=true|false] [type=tcp|http|ssl_hello|mysql|ajp] [port=check_port]`\n\nDefault: If the parameters are omitted, default values are: `interval=30000 fall=5 rise=2 timeout=1000 default_down=true type=tcp`\n\nContext: `upstream`\n\nAdd health check for the upstream servers.\n\nPassive health checking should not be enabled, as they may interfere. So do not use fail_timeout for the servers in the upstream context for which proactive health checking is enabled.\n\nThe parameters' meanings are:\n\n* `interval`: the check request's interval time.\n* `fall`(fall\\_count): After fall\\_count failure checks, the server is marked down.\n* `rise`(rise\\_count): After rise\\_count successful checks, the server is marked up.\n* `timeout`: the check request's timeout.\n* `default_down`: specify initial state of backend server, default is down.\n* `type`: the check protocol type:\n - `tcp`: a simple TCP socket connect and peek one byte.\n - `ssl_hello`: send a client SSL hello packet and receive the server SSL hello packet.\n - `http`: send a http request packet, receive and parse the http response to diagnose if the upstream server is alive.\n - `mysql`: connect to the mysql server, receive the greeting response to diagnose if the upstream server is alive.\n - `ajp`: send an AJP Cping packet, receive and parse the AJP Cpong response to diagnose if the upstream server is alive.\n* `port`: specify the check port in the backend servers. It can be different with the original servers port. Default the port is 0 and it means the same as the original backend server. This option is added after tengine-1.4.0.\n\n## check\\_keepalive\\_requests ##\n\nSyntax: **check\\_keepalive\\_requests** `request_num`\n\nDefault: `1`\n\nContext: `upstream`\n\nThe directive specifies the number of requests sent on a connection, the default vaule 1 indicates that tengine will certainly close the connection after a request.\n\nThis directive was first introduced in Tengine-2.0.0.\n\n## check\\_http\\_send ##\n\nSyntax: **check\\_http\\_send** `http_packet`\n\nDefault: `\"GET / HTTP/1.0\\r\\n\\r\\n\"`\n\nContext: `upstream`\n\nIf the check type is http, the check function will send this http packet to the upstream server. Method \"HEAD\" is recommended for reducing traffic.\n\nWhen persistant connection is used, a keep-alive request header should be added to the value of the directive, e.g. `\"HEAD / HTTP/1.1\\r\\nConnection: keep-alive\\r\\n\\r\\n\"`.\nIn addition, in the case of \"GET\" method, size of the request uri should not be too large, make sure the transmission can be finished within an `interval`, otherwise the health check will deduce a conclusion that there is something wrong with the servers or the net. \n\n## check\\_http\\_expect\\_alive ##\n\nSyntax: **check\\_http\\_expect\\_alive** `[ http_2xx | http_3xx | http_4xx | http_5xx ]`\n\nDefault: `http_2xx http_3xx`\n\nContext: `upstream`\n\nThese status codes indicate the upstream server's http response is OK and the check response is successful.\n\n## check\\_shm\\_size ##\n\nSyntax: **check\\_shm\\_size** `size`\n\nDefault: `1M`\n\nContext: `http`\n\nDefault size is one megabytes. If you want to check thousands of servers, the shared memory may be not enough, you can enlarge it with this directive.\n\n## check\\_status ##\n\nSyntax: **check\\_status** `[html|csv|json]`\n\nDefault: `check_status html`\n\nContext: `location`\n\nDisplay the status of checking servers. This directive should be used in the http block.\n\nYou can specify the default display format after Tengine-1.4.0. The formats can be `html`, `csv` or `json`. The default type is `html`. It also supports to specify the format by the request argument. Suppose your `check_status` location is '/status', the argument of `format` can change the display page's format. You can do like this:\n\n    /status?format=html\n    /status?format=csv\n    /status?format=json\n\nAt present, you can fetch the list of servers with the same status by the argument of `status`. For example:\n\n    /status?format=html&status=down\n    /status?format=csv&status=up\n\n\nBelow it's the sample html page:\n\n    <!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\n    \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n    <html xmlns=\"http://www.w3.org/1999/xhtml\">\n    <head>\n    <title>Nginx http upstream check status</title>\n    </head>\n    <body>\n        <h1>Nginx http upstream check status</h1>\n        <h2>Check upstream server number: 1, generation: 3</h2>\n        <table style=\"background-color:white\" cellspacing=\"0\"        cellpadding=\"3\" border=\"1\">\n            <tr bgcolor=\"#C0C0C0\">\n                <th>Index</th>\n                <th>Upstream</th>\n                <th>Name</th>\n                <th>Status</th>\n                <th>Rise counts</th>\n                <th>Fall counts</th>\n                <th>Check type</th>\n                <th>Check port</th>\n            </tr>\n            <tr>\n                <td>0</td>\n                <td>backend</td>\n                <td>192.168.0.1:80</td>\n                <td>up</td>\n                <td>39</td>\n                <td>0</td>\n                <td>http</td>\n                <td>80</td>\n            </tr>\n        </table>\n    </body>\n    </html>\n\nBelow it's the sample of csv page:\n\n    0,backend,192.168.0.1:80,up,46,0,http,80\n\nBelow it's the sample of json page:\n\n    {\"servers\": {\n      \"total\": 1,\n      \"generation\": 3,\n      \"server\": [\n       {\"index\": 0, \"upstream\": \"backend\", \"name\": \"192.168.0.1:80\", \"status\": \"up\", \"rise\": 58, \"fall\": 0, \"type\": \"http\", \"port\": 80}\n      ]\n     }}\n\n"
  },
  {
    "path": "docs/modules/ngx_http_upstream_check_module_cn.md",
    "content": "# Name #\n\n**ngx\\_http\\_upstream\\_check\\_module**\n\n该模块可以为Tengine提供主动式后端服务器健康检查的功能。\n\n该模块没有默认开启，它可以在配置编译选项的时候开启：`./configure --add-module=modules/ngx_http_upstream_check_module`\n\n# Examples #\n\n    http {\n\t\tupstream cluster1 {\n\t\t\t# simple round-robin\n\t\t\tserver 192.168.0.1:80;\n\t\t\tserver 192.168.0.2:80;\n\n\t\t\tcheck interval=3000 rise=2 fall=5 timeout=1000 type=http;\n\t\t\tcheck_http_send \"HEAD / HTTP/1.0\\r\\n\\r\\n\";\n\t\t\tcheck_http_expect_alive http_2xx http_3xx;\n\t\t}\n\n\t\tupstream cluster2 {\n\t\t\t# simple round-robin\n\t\t\tserver 192.168.0.3:80;\n\t\t\tserver 192.168.0.4:80;\n\n\t\t\tcheck interval=3000 rise=2 fall=5 timeout=1000 type=http;\n\t\t\tcheck_keepalive_requests 100;\n\t\t\tcheck_http_send \"HEAD / HTTP/1.1\\r\\nConnection: keep-alive\\r\\nHost: foo.bar.com\\r\\n\\r\\n\";\n\t\t\tcheck_http_expect_alive http_2xx http_3xx;\n\t\t}\n\n\t\tserver {\n\t\t\tlisten 80;\n\n\t\t\tlocation /1 {\n\t\t\t\tproxy_pass http://cluster1;\n\t\t\t}\n\n\t\t\tlocation /2 {\n\t\t\t\tproxy_pass http://cluster2;\n\t\t\t}\n\n\t\t\tlocation /status {\n\t\t\t\tcheck_status;\n\n\t\t\t\taccess_log   off;\n\t\t\t\tallow SOME.IP.ADD.RESS;\n\t\t\t\tdeny all;\n\t\t\t}\n\t\t}\n\t}\n\n# 指令 #\n\n## check ##\n\nSyntax: **check** `interval=milliseconds [fall=count] [rise=count] [timeout=milliseconds] [default_down=true|false] [type=tcp|http|ssl_hello|mysql|ajp] [port=check_port]`\n\nDefault: 如果没有配置参数，默认值是：`interval=30000 fall=5 rise=2 timeout=1000 default_down=true type=tcp`\n\nContext: `upstream`\n\n该指令可以打开后端服务器的健康检查功能。\n\n指令后面的参数意义是：\n\n* `interval`：向后端发送的健康检查包的间隔。\n* `fall`(fall\\_count): 如果连续失败次数达到fall\\_count，服务器就被认为是down。\n* `rise`(rise\\_count): 如果连续成功次数达到rise\\_count，服务器就被认为是up。\n* `timeout`: 后端健康请求的超时时间。\n* `default_down`: 设定初始时服务器的状态，如果是true，就说明默认是down的，如果是false，就是up的。默认值是true，也就是一开始服务器认为是不可用，要等健康检查包达到一定成功次数以后才会被认为是健康的。\n* `type`：健康检查包的类型，现在支持以下多种类型\n - `tcp`：简单的tcp连接，如果连接成功，就说明后端正常。\n - `ssl_hello`：发送一个初始的SSL hello包并接受服务器的SSL hello包。\n - `http`：发送HTTP请求，通过后端的回复包的状态来判断后端是否存活。\n - `fastcgi`：发送fsatcgi请求，通过后端的回复包的状态来判断后端是否存活。\n - `mysql`: 向mysql服务器连接，通过接收服务器的greeting包来判断后端是否存活。\n - `ajp`：向后端发送AJP协议的Cping包，通过接收Cpong包来判断后端是否存活。\n* `port`: 指定后端服务器的检查端口。你可以指定不同于真实服务的后端服务器的端口，比如后端提供的是443端口的应用，你可以去检查80端口的状态来判断后端健康状况。默认是0，表示跟后端server提供真实服务的端口一样。该选项出现于Tengine-1.4.0。\n\n\n## check\\_keepalive\\_requests ##\n\nSyntax: **check\\_keepalive\\_requests** `request_num`\n\nDefault: `1`\n\nContext: `upstream`\n\n该指令可以配置一个连接发送的请求数，其默认值为1，表示Tengine完成1次请求后即关闭连接。\n\n该指令在Tengine-2.0.0首次被引入。\n\n## check\\_http\\_send ##\n\nSyntax: **check\\_http\\_send** `http_packet`\n\nDefault: `\"GET / HTTP/1.0\\r\\n\\r\\n\"`\n\nContext: `upstream`\n\n该指令可以配置http健康检查包发送的请求内容。为了减少传输数据量，推荐采用`\"HEAD\"`方法。\n\n当采用长连接进行健康检查时，需在该指令中添加keep-alive请求头，如：`\"HEAD / HTTP/1.1\\r\\nConnection: keep-alive\\r\\n\\r\\n\"`。\n同时，在采用`\"GET\"`方法的情况下，请求uri的size不宜过大，确保可以在1个`interval`内传输完成，否则会被健康检查模块视为后端服务器或网络异常。\n\n## check\\_fastcgi\\_param ##\n\nSyntax: **check\\_fastcgi\\_params** `parameter`:`value`\n\nDefault: `REQUEST_METHOD: GET`\n         `REQUEST_URI: /`\n         `SCRIPT_FILENAME: index.php'\n\nContext: `upstream`\n\n该指令可以配置fastcgi健康检查包发送的请求的header项。\n\n## check\\_http\\_expect\\_alive ##\n\nSyntax: **check\\_http\\_expect\\_alive** `[ http_2xx | http_3xx | http_4xx | http_5xx ]`\n\nDefault: `http_2xx | http_3xx`\n\nContext: `upstream`\n\n该指令指定HTTP回复的成功状态，默认认为2XX和3XX的状态是健康的。\n\n## check\\_shm\\_size ##\n\nSyntax: **check\\_shm\\_size** `size`\n\nDefault: `1M`\n\nContext: `http`\n\n所有的后端服务器健康检查状态都存于共享内存中，该指令可以设置共享内存的大小。默认是1M，如果你有1千台以上的服务器并在配置的时候出现了错误，就可能需要扩大该内存的大小。\n\n## check\\_status ##\n\nSyntax: **check\\_status** `[html|csv|json]`\n\nDefault: `check_status html`\n\nContext: `location`\n\n显示服务器的健康状态页面。该指令需要在http块中配置。\n\n在Tengine-1.4.0以后，你可以配置显示页面的格式。支持的格式有: `html`、`csv`、 `json`。默认类型是`html`。\n\n你也可以通过请求的参数来指定格式，假设‘/status’是你状态页面的URL， `format`参数改变页面的格式，比如：\n\n    /status?format=html\n    /status?format=csv\n    /status?format=json\n\n同时你也可以通过status参数来获取相同服务器状态的列表，比如：\n\n    /status?format=html&status=down\n    /status?format=csv&status=up\n\n\n下面是一个HTML状态页面的例子（server number是后端服务器的数量，generation是Nginx reload的次数。Index是服务器的索引，Upstream是在配置中upstream的名称，Name是服务器IP，Status是服务器的状态，Rise是服务器连续检查成功的次数，Fall是连续检查失败的次数，Check type是检查的方式，Check port是后端专门为健康检查设置的端口）：\n\n    <!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\n    \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n    <html xmlns=\"http://www.w3.org/1999/xhtml\">\n    <head>\n    <title>Nginx http upstream check status</title>\n    </head>\n    <body>\n        <h1>Nginx http upstream check status</h1>\n        <h2>Check upstream server number: 1, generation: 3</h2>\n        <table style=\"background-color:white\" cellspacing=\"0\"        cellpadding=\"3\" border=\"1\">\n            <tr bgcolor=\"#C0C0C0\">\n                <th>Index</th>\n                <th>Upstream</th>\n                <th>Name</th>\n                <th>Status</th>\n                <th>Rise counts</th>\n                <th>Fall counts</th>\n                <th>Check type</th>\n                <th>Check port</th>\n            </tr>\n            <tr>\n                <td>0</td>\n                <td>backend</td>\n                <td>192.168.0.1:80</td>\n                <td>up</td>\n                <td>39</td>\n                <td>0</td>\n                <td>http</td>\n                <td>80</td>\n            </tr>\n        </table>\n    </body>\n    </html>\n\n下面是csv格式页面的例子：\n\n    0,backend,192.168.0.1:80,up,46,0,http,80\n\n下面是json格式页面的例子：\n\n    {\"servers\": {\n      \"total\": 1,\n      \"generation\": 3,\n      \"server\": [\n       {\"index\": 0, \"upstream\": \"backend\", \"name\": \"106.187.48.116:80\", \"status\": \"up\", \"rise\": 58, \"fall\": 0, \"type\": \"http\", \"port\": 80}\n      ]\n     }}\n"
  },
  {
    "path": "docs/modules/ngx_http_upstream_consistent_hash_module.md",
    "content": "Name\n====\n\n*  consistent hash module\n\nDescription\n===========\n\n* This module provides consistent hashing algorithm for upstream load-balancing.\n\n* If one of backend servers is down, the request of this client will be transferred to another server.\n\n* `server` *id* field: Id field can be used as server flag. If id field is not set, ip address and port are used to identify server. You can use id field to set server flag mannually. In that case, although ip address or port of a server is changed, id can still identify the server. BTW, it can reduce remapping keys effectively to use id field.\n\n* `server` *weight* field: server weight, the number of virtual peers\n\n* Algorithm: It supposes that 1 server is mapped to m virtual peers, so n servers correspond to n*m virtual peers. All these peers will be mapped to hash ring on average. Every time request comes, it calculates a hash key via configuration parameter, and finds a peer on the hash ring nearest to the location specified by the hash key.\n\n* It can dispatch requests to backend servers on average according to nginx configuration parameter.\n\n    `consistent_hash $remote_addr`: mapping via client ip address\n\n    `consistent_hash $request_uri`: mapping via request-uri\n\n    `consistent_hash $args`: mapping via url query string\n\n\nExample\n===========\n\n    worker_processes  1;\n\n    http {\n        upstream test {\n            consistent_hash $request_uri;\n\n            server 127.0.0.1:9001 id=1001 weight=3;\n            server 127.0.0.1:9002 id=1002 weight=10;\n            server 127.0.0.1:9003 id=1003 weight=20;\n        }\n    }\n\n\nDirectives\n==========\n\nconsistent_hash\n------------------------\n\n**Syntax**: *consistent_hash variable_name*\n\n**Default**: *none*\n\n**Context**: *upstream*\n\nThis directive causes requests to be distributed between upstreams based on consistent hashing alogrithm. And it uses nginx variables, specified by variable_name, as input data of hash function.\n\n\nInstallation\n===========\n\n* This module is built by default, it can be disabled with the `--without-http_upstream_consistent_hash_module` configuration parameter.\n\n    $ ./configure\n\n* compile\n\n    $ make\n\n* install\n\n    $ make install\n"
  },
  {
    "path": "docs/modules/ngx_http_upstream_consistent_hash_module_cn.md",
    "content": "模块名\n====\n\n*  一致性hash模块\n\n描述\n===========\n\n* 这个模块提供一致性hash作为负载均衡算法。\n\n* 该模块通过使用客户端信息(如：$ip, $uri, $args等变量)作为参数，使用一致性hash算法将客户端映射到后端机器\n\n* 如果后端机器宕机，这请求会被迁移到其他机器\n\n* `server` *id* 字段，如果配置id字段，则使用id字段作为server标识，否则使用server ip和端口作为server标识，\n\n    使用id字段可以手动设置server的标识，比如一台机器的ip或者端口变化，id仍然可以表示这台机器。使用id字段\n\n    可以减低增减服务器时hash的波动。\n\n* `server` *wegiht* 字段，作为server权重，对应虚拟节点数目\n\n* 具体算法，假设每个server对应n个虚拟节点，那m个server就对应n×m个虚拟节点，这些节点被均匀分布到hash环上。\n\n    每次请求进入时，模块根据配置的参数计算出一个hash值，在hash环上查找离这个hash值最近的虚拟节点，并将此\n\n    节点对应的server作为该次请求的后端机器。\n\n* 该模块可以根据配置参数采取不同的方式将请求均匀映射到后端机器，比如：\n\n    `consistent_hash $remote_addr`：可以根据客户端ip映射\n\n    `consistent_hash $request_uri`： 根据客户端请求的uri映射\n\n    `consistent_hash $args`：根据客户端携带的参数进行映射\n\n\n例子\n===========\n\n    worker_processes  1;\n\n    http {\n        upstream test {\n            consistent_hash $request_uri;\n\n            server 127.0.0.1:9001 id=1001 weight=3;\n            server 127.0.0.1:9002 id=1002 weight=10;\n            server 127.0.0.1:9003 id=1003 weight=20;\n        }\n    }\n\n\n指令\n==========\n\nconsistent_hash\n------------------------\n\n**Syntax**: *consistent_hash variable_name*\n\n**Default**: *none*\n\n**Context**: *upstream*\n\n配置upstream采用一致性hash作为负载均衡算法，variable_name作为hash输入，可以使用nginx变量。\n\n编译安装\n===========\n\n* configure默认打开一致性hash模块，若要关闭请使用选项`--without-http_upstream_consistent_hash_module`。\n\n      $ ./configure\n\n* 编译\n\n    $ make\n\n* 安装模块\n\n    $ make install\n"
  },
  {
    "path": "docs/modules/ngx_http_upstream_dynamic.md",
    "content": "Name\n====\n\n* ngx_http_upstream_dynamic_module\n\nDescription\n===========\n\n* This module provides the functionality to resolve domain names into IP addresses in an upstream at run-time.\n\nExamples\n========\n\n    upstream backend {\n        dynamic_resolve fallback=stale fail_timeout=30s;\n\n        server a.com;\n        server b.com;\n    }\n\n    server {\n        ...\n\n        proxy_pass http://backend;\n    }\n\nDirectives\n==========\n\ndynamic_resolve\n---------------\n\n**Syntax**: *dynamic_resolve [fallback=stale|next|shutdown] [fail_timeout=time]*\n\n**Default**: *-*\n\n**Context**: *upstream*\n\nEnable dynamic DNS resolving functionality in an upstream.\n\nThe 'fallback' parameter specifies what action to take if a domain name can not be resolved into an IP address:\n\n* stale, use the original IP addresses resolved when tengine starts.\n* next, go to next availiable server in the upstream.\n* shutdown, finalize current request.\n\nThe 'fail_timeout' parameter specifies how long time tengine considers the DNS server as unavailiable if a DNS query fails for a server in the upstream. In this period of time, all requests comming will follow what 'fallback' specifies.\n"
  },
  {
    "path": "docs/modules/ngx_http_upstream_dynamic_cn.md",
    "content": "模块名\n=====\n\n* ngx_http_upstream_dynamic_module\n\n介绍\n===\n\n* 此模块提供了在运行时动态解析upstream中server域名的功能\n\n配置示例\n=======\n\n    upstream backend {\n        dynamic_resolve fallback=stale fail_timeout=30s;\n\n        server a.com;\n        server b.com;\n    }\n\n    server {\n        ...\n\n        proxy_pass http://backend;\n    }\n\n指令\n===\n\ndynamic_resolve\n---------------\n\n**语法**: *dynamic_resolve [fallback=stale|next|shutdown] [fail_timeout=time]*\n\n**默认值**: *-*\n\n**上下文**: *upstream*\n\n指定在某个upstream中启用动态域名解析功能。\n\nfallback参数指定了当域名无法解析时采取的动作：\n\n* stale, 使用tengine启动的时候获取的旧地址\n* next, 选择upstream中的下一个server\n* shutdown, 结束当前请求\n\nfail_timeout参数指定了一个时间，在这个时间范围内，DNS服务将被当作无法使用。具体来说，就是当某次DNS请求失败后，假定后续多长的时间内DNS服务依然不可用，以减少对无效DNS的查询。\n"
  },
  {
    "path": "docs/modules/ngx_http_upstream_dyups_module.md",
    "content": "## Description\n\nThis module can be used to update your upstream-list without reloadding Nginx.\n\n## Example\n\nfile: conf/nginx.conf\n\n`ATTENTION`: You MUST use nginx variable to do proxy_pass\n\n    daemon off;\n    error_log logs/error.log debug;\n\n    events {\n    }\n\n    http {\n\n        include conf/upstream.conf;\n\n        server {\n            listen   8080;\n\n            location / {\n                # The upstream here must be a nginx variable\n                proxy_pass http://$host; \n            }\n        }\n\n        server {\n            listen 8088;\n            location / {\n                return 200 \"8088\";\n            }\n        }\n\n        server {\n            listen 8089;\n            location / {\n                return 200 \"8089\";\n            }\n        }\n\n        server {\n            listen 8081;\n            location / {\n                dyups_interface;\n            }\n        }\n    }\n\nIf your original config looks like this:\n\n    proxy_pass http://upstream_name;\n\nplease replace it with:\n\n    set $ups upstream_name;\n    proxy_pass http://$ups;\n\n`$ups` can be any valid nginx variable.\n\nfile: conf/upstream.conf\n\n    upstream host1 {\n        server 127.0.0.1:8088;\n    }\n\n    upstream host2 {\n        server 127.0.0.1:8089;\n    }\n\n\n## Installation\n\n* Only install dyups module\n\n```bash\n# to compile as a static module\n$ ./configure --add-module=./modules/ngx_http_upstream_dyups_module\n\n# to compile as a dynamic module\n$ ./configure --add-dynamic-module=./modules/ngx_http_upstream_dyups_module\n```\n\n* Install dyups module with lua-nginx-module and upstream check module\n    * upstream check module: To make upstream check module work well with dyups module, you should use `./modules/ngx_http_upstream_check_module`.\n    * lua-nginx-module: To enable [dyups LUA API](#lua-api-example), you MUST put `--add-module=./modules/ngx_http_lua_module` in front of `--add-module=./modules/ngx_http_upstream_dyups_module` in the `./configure` command.\n\n```bash\n# to compile as a static module\n$ ./configure --add-module=./modules/ngx_http_upstream_check_module --add-module=./modules/ngx_http_lua_module --add-module=./modules/ngx_http_upstream_dyups_module\n```\n\n## Directives\n\n### dyups_interface\n\nSyntax: **dyups_interface**\n\nDefault: `none`\n\nContext: `loc`\n\nThis directive set the interface location where you can add or delete the upstream list. See the section of Interface for detail.\n\n\n### dyups_read_msg_timeout\n\nSyntax: **dyups_read_msg_timeout** `time`\n\nDefault: `1s`\n\nContext: `main`\n\nThis directive set the interval of workers readding the commands from share memory.\n\n\n### dyups_shm_zone_size\n\nSyntax: **dyups_shm_zone_size** `size`\n\nDefault: `2MB`\n\nContext: `main`\n\nThis directive set the size of share memory which used to store the commands.\n\n\n### dyups_upstream_conf\n\nSyntax: **dyups_upstream_conf** `path`\n\nDefault: `none`\n\nContext: `main`\n\nThis directive has been deprecated\n\n\n### dyups_trylock\n\nSyntax: **dyups_trylock** `on | off`\n\nDefault: `off`\n\nContext: `main`\n\nYou will get a better prefomance but it maybe not stable, and you will get a '409' when the update request conflicts with others.\n\n\n### dyups_read_msg_log\n\nSyntax: **dyups_read_msg_log** `on | off`\n\nDefault: `off`\n\nContext: `main`\n\nYou can enable / disable log of workers readding the commands from share memory. The log looks like:\n\n```\n2017/02/28 15:37:53 [info] 56806#0: [dyups] has 0 upstreams, 1 static, 0 deleted, all 1\n```\n\n## restful interface\n\n### GET\n- `/detail`         get all upstreams and their servers\n- `/list`           get the list of upstreams\n- `/upstream/name`  find the upstream by it's name\n\n### POST\n- `/upstream/name`  update one upstream\n- `body` commands;\n- `body` server ip:port;\n\n### DELETE\n- `/upstream/name`  delete one upstream\n\nCall the interface, when you get the return code is `HTTP_INTERNAL_SERVER_ERROR 500`, you need to reload nginx to make the Nginx work at a good state.\n\nIf you got `HTTP_CONFLICT 409`, you need resend the same commands again latter.\n\nThe /list and /detail interface will return `HTTP_NO_CONTENT 204` when there is no upstream.\n\nOther code means you should modify your commands and call the interface again.\n\n`ATTENTION`: You also need a `third-party` to generate the new config and dump it to Nginx'conf directory.\n\n### Sample\n\n```bash\n» curl -H \"host: dyhost\" 127.0.0.1:8080\n<html>\n<head><title>502 Bad Gateway</title></head>\n<body bgcolor=\"white\">\n<center><h1>502 Bad Gateway</h1></center>\n<hr><center>nginx/1.3.13</center>\n</body>\n</html>\n\n» curl -d \"server 127.0.0.1:8089;server 127.0.0.1:8088;\" 127.0.0.1:8081/upstream/dyhost\nsuccess\n\n» curl -H \"host: dyhost\" 127.0.0.1:8080\n8089\n\n» curl -H \"host: dyhost\" 127.0.0.1:8080\n8088\n\n» curl 127.0.0.1:8081/detail\nhost1\nserver 127.0.0.1:8088 weight=1 max_conns=0 max_fails=1 fail_timeout=10 backup=0 down=0\n\nhost2\nserver 127.0.0.1:8089 weight=1 max_conns=0 max_fails=1 fail_timeout=10 backup=0 down=0\n\ndyhost\nserver 127.0.0.1:8089 weight=1 max_conns=0 max_fails=1 fail_timeout=10 backup=0 down=0\nserver 127.0.0.1:8088 weight=1 max_conns=0 max_fails=1 fail_timeout=10 backup=0 down=0\n\n» curl -i -X DELETE 127.0.0.1:8081/upstream/dyhost\nsuccess\n\n» curl 127.0.0.1:8081/detail\nhost1\nserver 127.0.0.1:8088 weight=1 max_conns=0 max_fails=1 fail_timeout=10 backup=0 down=0\n\nhost2\nserver 127.0.0.1:8089 weight=1 max_conns=0 max_fails=1 fail_timeout=10 backup=0 down=0\n```\n\n## C API\n\n```c\nextern ngx_flag_t ngx_http_dyups_api_enable;\nngx_int_t ngx_dyups_update_upstream(ngx_str_t *name, ngx_buf_t *buf,\n    ngx_str_t *rv);\nngx_int_t ngx_dyups_delete_upstream(ngx_str_t *name, ngx_str_t *rv);\n\nextern ngx_dyups_add_upstream_filter_pt ngx_dyups_add_upstream_top_filter;\nextern ngx_dyups_del_upstream_filter_pt ngx_dyups_del_upstream_top_filter;\n\n```\n\n## Lua API Example\n\nNOTICE:\n    you should add the directive `dyups_interface` into your config file to active this feature\n\n```lua\ncontent_by_lua '\n    local dyups = require \"ngx.dyups\"\n\n    local status, rv = dyups.update(\"test\", [[server 127.0.0.1:8088;]]);\n    ngx.print(status, rv)\n    if status ~= ngx.HTTP_OK then\n        ngx.print(status, rv)\n        return\n    end\n    ngx.print(\"update success\")\n\n    status, rv = dyups.delete(\"test\")\n    if status ~= ngx.HTTP_OK then\n        ngx.print(status, rv)\n        return\n    end\n    ngx.print(\"delete success\")\n';\n\n```\n\n## Compatibility\n### Module Compatibility\n\n* [lua-upstream-nginx-module](https://github.com/agentzh/lua-upstream-nginx-module): You can use `lua-upstream-nginx-module` to get more detail infomation of upstream.\n* [upstream check module](http://tengine.taobao.org/document/http_upstream_check.html): To make upstream check module work well with dyups module, you should use `./modules/ngx_http_upstream_check_module`.\n\n"
  },
  {
    "path": "docs/modules/ngx_http_upstream_iwrr_module.md",
    "content": "\n## Name\n\nngx_http_upstream_iwrr_module.\n\n\n## Introduction\n\nThe `IWRR` module is an efficient load balancing algorithm with `O(1)` time complexity, but `IWRR` is no need to incremental initialization.\n\nCompared with Nginx's official `SWRR` algorithm and `VNSWRR`, `IWRR` abandons smoothness on the premise of ensuring the correctness of the weighted load balancing algorithm, ensuring that no matter how the total weight of the cluster changes, `IWRR` space The complexity is always `O(n)`. \n\n## Example\n\n```\nhttp {\n\n    upstream backend {\n        iwrr; # enable IWRR load balancing algorithm.\n        127.0.0.1 port=81;\n        127.0.0.1 port=82 weight=2;\n        127.0.0.1 port=83;\n        127.0.0.1 port=84 backup;\n        127.0.0.1 port=85 down;\n    }\n    \n    server {\n        server_name localhost;\n        \n        location / {\n            proxy_pass http://backend;\n        }\n    }\n}\n\n```\n    \n## Installation\n\nBuild Tengine with this module from source:\n\n```\n\n./configure --add-module=./modules/ngx_http_upstream_iwrr_module/\nmake\nmake install\n\n```\n    \n\n## Directive\n\niwrr\n=======\n```\nSyntax: iwrr [max_init=number]\nDefault: none\nContext: upstream\n```\n\nEnable `iwrr` load balancing algorithm. \n"
  },
  {
    "path": "docs/modules/ngx_http_upstream_iwrr_module_cn.md",
    "content": "\n## 名称 \n\nngx_http_upstream_iwrr_module.\n\n\n## 介绍\n\n`IWRR`模块是一个高效的负载均衡算法，与`VNSWRR`相同，它具有`O(1)`的时间复杂度，但是`IWRR`不需要执行渐进式初始化操作。\n\n同Nginx官方的加权轮询负载均衡算法及`VNSWRR`相比，`IWRR`在保证加权负载均衡算法正确性的前提下，牺牲了平滑的特点，保证无论集群总权重如何变化，`IWRR`空间复杂度总是`O(n)`的。\n\n## 配置列子\n\n```\nhttp {\n\n    upstream backend {\n        iwrr; # enable IWRR load balancing algorithm.\n        127.0.0.1 port=81;\n        127.0.0.1 port=82 weight=2;\n        127.0.0.1 port=83;\n        127.0.0.1 port=84 backup;\n        127.0.0.1 port=85 down;\n    }\n    \n    server {\n        server_name localhost;\n        \n        location / {\n            proxy_pass http://backend;\n        }\n    }\n}\n\n```\n    \n## 安装方法\n\n在Tengine中，通过源码安装此模块：\n\n\n```\n\n./configure --add-module=./modules/ngx_http_upstream_iwrr_module\nmake\nmake install\n\n```\n    \n\n## 指令描述\n\niwrr\n=======\n```\nSyntax: iwrr\nDefault: none\nContext: upstream\n```\n\n在upstream里面启用 `iwrr` 加权轮询算法。\n"
  },
  {
    "path": "docs/modules/ngx_http_upstream_session_sticky_module.md",
    "content": "# Name\n**ngx\\_http\\_upstream\\_session\\_sticky\\_module**\n\nThis module is a load balancing module. It sticks the session between client and backend server via cookie. In such case, it guarantees that requests from the same client are distributed to the same server.\n\n# Example 1#\n\n    # default: cookie=route mode=insert fallback=on\n    upstream foo {\n       server 192.168.0.1;\n       server 192.168.0.2;\n       session_sticky;\n    }\n\n    server {\n        location / {\n            proxy_pass http://foo;\n        }\n    }\n\n# Example 2#\n\n    # insert + indirect mode:\n    upstream test {\n      session_sticky session_sticky cookie=uid domain=www.xxx.com fallback=on path=/ mode=insert option=indirect;\n      server  127.0.0.1:8080;\n    }\n\n    server {\n      location / {\n        # You need configure session_sticky_hide_cookie in insert + indirect mode or prefix mode.\n        # It removes the cookie before sending to backend server, and the backend server will not\n\t# receive and process this extra cookie.\n        session_sticky_hide_cookie upstream=test;\n        proxy_pass http://test;\n      }\n    }\n\n# Directive #\n\n## session_sticky ##\n\nSyntax: **session_sticky** `[cookie=name] [domain=your_domain] [path=your_path] [maxage=time] [mode=insert|rewrite|prefix] [option=indirect] [maxidle=time] [maxlife=time] [fallback=on|off] [hash=plain|md5]`\n\nDefault: `session_sticky cookie=route mode=insert fallback=on`\n\nContext: `upstream`\n\nDescription:\n\nThis directive will turn on the session sticky module. Specific parameters are as follows:\n\n+ `cookie` sets name of session cookie.\n+ `domain` sets domain of cookie. It is not set by default.\n+ `path` sets url path of cookie. The default value is '/'.\n+ `maxage` set lifetime of cookie (cookie max-age attribute). If not set, it's a session cookie. It expires when the browser is closed.\n+ `mode` sets mode of cookie:\n    - **insert**: This mode inserts cookie into http response via Set-Cookie header.\n    - **prefix**: This mode doesn't generate new cookie, but it inserts specific prefix ahead of cookie value of http response (e.g. \"Cookie: NAME=SRV~VALUE\"). When client(browser) requests next time with this specific cookie, it will delete inserted prefix before passing request to backend server. The operation is transparent to backend server which will get origin cookie .\n    - **rewrite**: In this mode, backend server can set cookie of session sticky itself. If backend server doesn't set this cookie in response, it disables session sticky for this request. In this mode, backend server manages which request needs sesstion sticky.\n\n+ `option` sets option value(indirect and direct) for cookie of session sticky. If setting indirect, it hides cookie of session sticky from backend server, otherwise the opposite.\n+ `maxidle` sets max idle time of session.\n+ `maxlife` sets max lifetime of session.\n+ `fallback` sets whether it can retry others when current backend server is down.\n+ `hash` sets whether server flag in cookie is passed through plaintext or md5. By default, md5 is used.\n\n## session\\_sticky\\_hide\\_cookie ##\n\nSyntax: **session\\_sticky\\_hide\\_cookie** upstream=name;\n\nDefault: none\n\nContext: server, location\n\nDescription:\n\nThis directive works with proxy_pass directive. It deletes cookie used as session sticky in insert+indirect and prefix mode, in which case cookie will be hidden from backend server. Upstream name specifies which upstream this directive takes effect in.\n"
  },
  {
    "path": "docs/modules/ngx_http_upstream_session_sticky_module_cn.md",
    "content": "# Name 模块\n**ngx\\_http\\_upstream\\_session\\_sticky\\_module**\n\n该模块是一个负载均衡模块，通过cookie实现客户端与后端服务器的会话保持, 在一定条件下可以保证同一个客户端访问的都是同一个后端服务器。\n\n# Example 1#\n\n    # 默认配置：cookie=route mode=insert fallback=on\n    upstream foo {\n       server 192.168.0.1;\n       server 192.168.0.2;\n       session_sticky;\n    }\n\n    server {\n        location / {\n            proxy_pass http://foo;\n        }\n    }\n\n# Example 2#\n\n    #insert + indirect模式：\n    upstream test {\n      session_sticky session_sticky cookie=uid domain=www.xxx.com fallback=on path=/ mode=insert option=indirect;\n      server  127.0.0.1:8080;\n    }\n\n    server {\n      location / {\n        #在insert + indirect模式或者prefix模式下需要配置session_sticky_hide_cookie\n        #这种模式不会将保持会话使用的cookie传给后端服务，让保持会话的cookie对后端透明\n        session_sticky_hide_cookie upstream=test;\n        proxy_pass http://test;\n      }\n    }\n\n# 指令 #\n\n## session_sticky ##\n\n语法：**session_sticky** `[cookie=name] [domain=your_domain] [path=your_path] [maxage=time] [mode=insert|rewrite|prefix] [option=indirect] [maxidle=time] [maxlife=time] [fallback=on|off] [hash=plain|md5]`\n\n默认值：`session_sticky cookie=route mode=insert fallback=on`\n\n上下文：`upstream`\n\n说明:\n\n本指令可以打开会话保持的功能，下面是具体的参数：\n\n+ `cookie`设置用来记录会话的cookie名称\n+ `domain`设置cookie作用的域名，默认不设置\n+ `path`设置cookie作用的URL路径，默认不设置\n+ `maxage`设置cookie的生存期，默认不设置，即为session cookie，浏览器关闭即失效\n+ `mode`设置cookie的模式:\n    - **insert**: 在回复中本模块通过Set-Cookie头直接插入相应名称的cookie。\n    - **prefix**: 不会生成新的cookie，但会在响应的cookie值前面加上特定的前缀，当浏览器带着这个有特定标识的cookie再次请求时，模块在传给后端服务前先删除加入的前缀，后端服务拿到的还是原来的cookie值，这些动作对后端透明。如：\"Cookie: NAME=SRV~VALUE\"。\n    - **rewrite**: 使用服务端标识覆盖后端设置的用于session sticky的cookie。如果后端服务在响应头中没有设置该cookie，则认为该请求不需要进行session sticky，使用这种模式，后端服务可以控制哪些请求需要sesstion sticky，哪些请求不需要。\n\n+ `option` 设置用于session sticky的cookie的选项，可设置成indirect或direct。indirect不会将session sticky的cookie传送给后端服务，该cookie对后端应用完全透明。direct则与indirect相反。\n+ `maxidle`设置session cookie的最长空闲的超时时间\n+ `maxlife`设置session cookie的最长生存期\n+ `fallback`设置是否重试其他机器，当sticky的后端机器挂了以后，是否需要尝试其他机器\n+ `hash` 设置cookie中server标识是用明文还是使用md5值，默认使用md5\n\n## session\\_sticky\\_hide\\_cookie ##\n\n语法: **session\\_sticky\\_hide\\_cookie** upstream=name;\n\n默认值: none\n\n上下文： server, location\n\n说明：\n\n配合proxy_pass指令使用。用于在insert+indirect模式和prefix模式下删除请求用于session sticky的cookie，这样就不会将该cookie传递给后端服务。upstream表示需要进行操作的upstream名称。\n"
  },
  {
    "path": "docs/modules/ngx_http_upstream_vnswrr_module.md",
    "content": "\n## Name\n\nngx_http_upstream_vnswrr_module.\n\n\n## Introduction\n\nThe `VNSWRR` module is an efficient load balancing algorithm that is smooth, decentralized, and high-performance compared to Nginx's official `SWRR` algorithm.\n\n\n## Example\n\n```\nhttp {\n\n    upstream backend {\n        vnswrr; # enable VNSWRR load balancing algorithm.\n        127.0.0.1 port=81;\n        127.0.0.1 port=82 weight=2;\n        127.0.0.1 port=83;\n        127.0.0.1 port=84 backup;\n        127.0.0.1 port=85 down;\n    }\n    \n    server {\n        server_name localhost;\n        \n        location / {\n            proxy_pass http://backend;\n        }\n    }\n}\n\n```\n    \n## Installation\n\nBuild Tengine with this module from source:\n\n```\n\n./configure --add-module=./modules/ngx_http_upstream_vnswrr_module/\nmake\nmake install\n\n```\n    \n\n## Directive\n\nvnswrr\n=======\n```\nSyntax: vnswrr [max_init=number]\nDefault: none\nContext: upstream\n```\n\nEnable `vnswrr` load balancing algorithm. \n\n- max_init\n\n    The max number of virtual node per initialization, to avoid initializing too many virtual nodes in one request.\n\n    In very large clusters, setting this argument can significantly reduce the time overhead of a single request.\n\n    There are two additional rules of `max_init`:\n    1. If `max_init` is not set or `0`, it will be adjusted to the number of peers.\n    2. If `max_init` is greater than total weight of peers, it will be adjusted to the total weight of peers.\n\n```\nhttp {\n\n    upstream backend {\n        # at most 3 virtual node per initialization\n        vnswrr max_init=3;\n        127.0.0.1 port=81 weight=101;\n        127.0.0.1 port=82 weight=102;\n        127.0.0.1 port=83 weight=103;\n        127.0.0.1 port=84 weight=104;\n        127.0.0.1 port=85 weight=105;\n    }\n    \n    server {\n        server_name localhost;\n        \n        location / {\n            proxy_pass http://backend;\n        }\n    }\n}\n```\n\n## Performance\n\n\nUnder the same pressure (wrk, 500 concurrency, keepalive, 2000 endpoint), the CPU consumption of `VNSWRR` algorithm accounts for `0.27%` ( `ngx_http_upstream_get_vnswrr`).\nCompared with `VNSWRR` algorithm, the CPU consumption of `SWRR` (`ngx_http_upstream_get_peer` `39%`) is an order of magnitude higher than `VNSWRR`.\n\n\n![image](/docs/image/vnswrr_vs_swrr_fhot.png)\n\nIn the above environment, the QPS of `VNSWRR` increased by `60%` compared with `SWRR` algorithm.\n\n\n![image](/docs/image/vnswrr_vs_swrr_2000.png)\n\n\nObserving the changes of QPS and RT in the different back-end number scenarios. \nUnder SWRR algorithm, with every 500 back-end servers added, the QPS of Nginx decreases by about 10% and RT increases by about 1 ms. But under the VNSWRR algorithm, QPS and RT do not change much.\n\n\n![image](/docs/image/vnswrr_vs_swrr_qps.png)\n\n![image](/docs/image/vnswrr_vs_swrr_rt.png)\n\n"
  },
  {
    "path": "docs/modules/ngx_http_upstream_vnswrr_module_cn.md",
    "content": "\n## 名称 \n\nngx_http_upstream_vnswrr_module.\n\n\n## 介绍\n\n`VNSWRR`模块是一个高效的负载均衡算法，同Nginx官方的加权轮询算法`SWRR`相比，`VNSWRR` 具备 平滑、散列和高性能特征。\n\n## 配置列子\n\n```\nhttp {\n\n    upstream backend {\n        vnswrr; # enable VNSWRR load balancing algorithm.\n        127.0.0.1 port=81;\n        127.0.0.1 port=82 weight=2;\n        127.0.0.1 port=83;\n        127.0.0.1 port=84 backup;\n        127.0.0.1 port=85 down;\n    }\n    \n    server {\n        server_name localhost;\n        \n        location / {\n            proxy_pass http://backend;\n        }\n    }\n}\n\n```\n    \n## 安装方法\n\n在Tengine中，通过源码安装此模块：\n\n\n```\n\n./configure --add-module=./modules/ngx_http_upstream_vnswrr_module\nmake\nmake install\n\n```\n    \n\n## 指令描述\n\nvnswrr\n=======\n```\nSyntax: vnswrr [max_init=number]\nDefault: none\nContext: upstream\n```\n\n在upstream里面启用 `vnswrr` 加权轮询算法。\n    \n- max_init\n\n    每次初始化虚拟结点的最大数量，避免再一次请求中初始化过多的虚拟节点。\n\n    在超大集群中，设置这个参数可以显著减少单次请求的开销。\n\n    关于`max_init`有两条额外的规则\n    1. 如果`max_init`没有设置或值为`0`，那么它将会被调整为节点数。\n    2. 如果`max_init`大于所有节点的权重之和，那么它将会被调整为所有节点的权重之和。\n\n```\nhttp {\n\n    upstream backend {\n        # at most 3 virtual node per initialization\n        vnswrr max_init=3;\n        127.0.0.1 port=81 weight=101;\n        127.0.0.1 port=82 weight=102;\n        127.0.0.1 port=83 weight=103;\n        127.0.0.1 port=84 weight=104;\n        127.0.0.1 port=85 weight=105;\n    }\n    \n    server {\n        server_name localhost;\n        \n        location / {\n            proxy_pass http://backend;\n        }\n    }\n}\n```\n\n## 性能数据\n\n\n在相同的压测环境下，`VNSWRR` 算法核心函数(`ngx_http_upstream_get_vnswrr`)CPU消耗占比仅有 `0.27%`，而在`SWRR`算法下其核心处理函数（`ngx_http_upstream_get_peer`）CPU消耗占比高至 `39%`。 其CPU消耗比`VNSWRR`算法要高出一个数量级。\n\n* 压测环境\n\n```\nCPU型号： Intel(R) Xeon(R) CPU E5-2682 v4 @ 2.50GHz\n\n压测工具：./wrk -t25  -d5m -c500  'http://ip/t2000'\n\nTengine核心配置：配置2个worker进程、2000 endpoint，压力源 --长连接--> Tengine/Nginx --短连接--> 后端\n```\n\n![image](/docs/image/vnswrr_vs_swrr_fhot.png)\n\n\n在上述的压测环境下，`VNSWRR`算法的QPS处理能力相比`SWRR`提升 `60%`左右，如下图所示。\n\n![image](/docs/image/vnswrr_vs_swrr_2000.png)\n\n\n通过试验，控制变量是upstream里面配置的server数量，观察不同场景下Nginx的QPS处理能力以及响应时间RT变化情况。从图中可以发现当后端upstream里面的server数量每增加500台则Nginx的QPS处理能力下降 10% 左右，响应RT增长 1ms 左右。\n\n![image](/docs/image/vnswrr_vs_swrr_qps.png)\n\n![image](/docs/image/vnswrr_vs_swrr_rt.png)\n\n"
  },
  {
    "path": "docs/modules/ngx_http_user_agent.md",
    "content": "# Name #\n\n**ngx\\_http\\_user\\_agent\\_module**\n\nThis module can analyse the header of User-Agent.\n\nThis module is enabled by default. It can be disabled with the --without-http_user_agent_module configuration parameter.\n\n# Examples #\n\n\thttp {\n\t\tuser_agent $ngx_browser {\n\t\t\tdefault                     unknown;\n\n\n\t\t\tgreedy                      Firefox;\n\n\t\t\tChrome      18.0+           chrome18;\n\t\t\tChrome      17.0~17.9999    chrome17;\n\t\t\tChrome      5.0-            chrome_low;\n\t\t}\n\t}\n\n# Directives #\n\n## user_agent ##\n\nSyntax: **user_agent** $variable_name   \nDefault: none   \nContext: http   \nSet a variable whose value depends on the value of user_agent string.This block contains three parts, **default**, **greedy** and **analysis items**.\n\n### default:         \n *Syntax*: **default**   value           \n *Default*: none           \n *Context*: user_agent block  \n The default variable value if the user_agent string can't match any of the item.     \n\n### greedy:   \n *Syntax*: **greedy   keyword**   \n *Default*: none  \n *Context*: user_agent block  \n If the keyword is greedy, it will continue to scan the user-agent string until it can find other item which is not greedy. If it can't find any other item, this keyword will be returned at last.    \ne.g.: \"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/13.0.782.112 Safari/535.1\",this user_agent string will return Chrome13,if configuration file like this:    \n\n    greedy                  Safari;     \n    Chrome  13.0~13.9999    chrome13;   \n\n### analysis items:     \n *Syntax*: **keyword version value**    \n *Default*: none    \n *Context*: user_agent block   \n\n* *keyword*: This is the word we want to match from the user_agent string.    \n* *version*: the version of keyword.  \n       - version\\+:greater or equal should be matched;    \n\t   - version\\-:less or equal should be matched;   \n\t   - version:equal should be matched;     \n\t   - version1~version2:matched in [version1,version2];    \n* *value*:If this item is matched, the value will be filled to the defined variable.  \n"
  },
  {
    "path": "docs/modules/ngx_limit_upstream.md",
    "content": "# Name #\n\n**limit upstream retries**\n\nLimits retries for upstream servers (proxy, memcached, fastcgi, scgi, uwsgi).\nUsing one of the directives below will enable this feature.\n\n# Example #\n\n    http {\n        upstream test {\n            server 127.0.0.1:8081;\n            server 127.0.0.2:8081;\n            server 127.0.0.3:8081;\n            server 127.0.0.4:8081;\n        }\n    \n        server {\n            proxy_upstream_tries 2;\n            proxy_set_header Host $host;\n    \n            location / {\n                proxy_pass test;\n            }\n        }\n    }\n\n# Directives #\n\n## fastcgi\\_upstream\\_tries ##\n\nSyntax: **fastcgi\\_upstream\\_tries** num\n\nDefault: -\n\nContext: http, server, locatioon\n\nLimit the maximum number of tries for fastcgi proxy. Nginx tries to connect different server each time.\n\n## proxy\\_upstream\\_tries ##\n\nSyntax: **proxy\\_upstream\\_tries** num\n\nDefault: -\n\nContext: http, server, locatioon\n\nLimit the maximum number of tries for http proxy. Nginx tries to connect different server each time.\n\n## memcached\\_upstream\\_tries ##\n\nSyntax: **memcached\\_upstream\\_tries** num\n\nDefault: -\n\nContext: http, server, locatioon\n\nLimit the maximum number of tries for memcached proxy. Nginx tries to connect different server each time.\n\n## scgi\\_upstream\\_tries ##\n\nSyntax: **scgi\\_upstream\\_tries** num\n\nDefault: -\n\nContext: http, server, locatioon\n\nLimit the maximum number of tries for scgi proxy. Nginx tries to connect different server each time.\n\n## uwsgi\\_upstream\\_tries ##\n\nSyntax: **uwsgi\\_upstream\\_tries** num\n\nDefault: -\n\nContext: http, server, locatioon\n\nLimit the maximum number of tries for uwsgi proxy. Nginx tries to connect different server each time.\n"
  },
  {
    "path": "docs/modules/ngx_limit_upstream_cn.md",
    "content": "# Name #\n\n**limit upstream retries**\n\n限制每个请求对后端服务器访问的最大尝试次数，支持proxy、memcached、fastcgi、scgi和uwsgi模块。\n可以使用下面的指令开启访问次数进行限制。\n\n# Example #\n\n    http {\n        upstream test {\n            server 127.0.0.1:8081;\n            server 127.0.0.2:8081;\n            server 127.0.0.3:8081;\n            server 127.0.0.4:8081;\n        }\n    \n        server {\n            proxy_upstream_tries 2;\n            proxy_set_header Host $host;\n    \n            location / {\n                proxy_pass test;\n            }\n        }\n    }\n\n# 指令 #\n\n## fastcgi\\_upstream\\_tries ##\n\nSyntax: **fastcgi\\_upstream\\_tries** num\n\nDefault: -\n\nContext: http, server, locatioon\n\n限制fastcgi代理的后端尝试次数。\n\n## proxy\\_upstream\\_tries ##\n\nSyntax: **proxy\\_upstream\\_tries** num\n\nDefault: -\n\nContext: http, server, locatioon\n\n限制proxy代理的后端尝试次数。\n\n## memcached\\_upstream\\_tries ##\n\nSyntax: **memcached\\_upstream\\_tries** num\n\nDefault: -\n\nContext: http, server, locatioon\n\n限制memcached代理的后端尝试次数。\n\n## scgi\\_upstream\\_tries ##\n\nSyntax: **scgi\\_upstream\\_tries** num\n\nDefault: -\n\nContext: http, server, locatioon\n\n限制scgi代理的后端尝试次数。\n\n## uwsgi\\_upstream\\_tries ##\n\nSyntax: **uwsgi\\_upstream\\_tries** num\n\nDefault: -\n\nContext: http, server, locatioon\n\n限制uwsgi代理的后端尝试次数。\n"
  },
  {
    "path": "docs/modules/ngx_log_pipe.md",
    "content": "# log pipe\nSyntax: **pipe:rollback** [logpath] **interval=**[interval] **baknum=**[baknum] **maxsize=**[maxsize] **adjust=**[adjust]\nDefault: none\nContext: http, server, location\n\nlog pipe module write log use special log proccess, it may not block worker, worker communicate with log proccess use pipe, rollback depend on log pipe module, it support log file auto rollback by tengine self. it support rollback by time and file size, also can configure backup file number. log rollback module will rename log file to backup filename, then reopen the log file and write again\n\nrollback configurge is built-in access_log and error_log：\n```\naccess_log \"pipe:rollback [logpath] interval=[interval] baknum=[baknum] maxsize=[maxsize] adjust=[adjust]\" proxyformat;\n\nerror_log  \"pipe:rollback [logpath] interval=[interval] baknum=[baknum] maxsize=[maxsize] adjust=[adjust]\" info;\n```\n\nlogpath: log output file path and name\n\ninterval：log rollback interval, default 0 (never)\n\nbaknum：backup file number, default 1 (keep 1 backup file)\n\nmaxsize：log file max size, default 0 (never)\n\nadjust: delay random rollback time when rollback by interval, to avoid all server focus on rollback default 60 (60s)\n\nexample：\n```\nerror_log  \"pipe:rollback logs/error_log interval=60m baknum=5 maxsize=2048M\" info;\n\nhttp {\n\tlog_format  main  '$remote_addr - $remote_user [$time_local] \"$request\" '\n                      '$status $body_bytes_sent \"$http_referer\" '\n                      '\"$http_user_agent\" \"$http_x_forwarded_for\"';\n\taccess_log  \"pipe:rollback logs/access_log interval=1h baknum=5 maxsize=2G\"  main;\n}\n```\n"
  },
  {
    "path": "docs/modules/ngx_log_pipe_cn.md",
    "content": "# log pipe\nSyntax: **pipe:rollback** [logpath] **interval=**[interval] **baknum=**[baknum] **maxsize=**[maxsize] **adjust=**[adjust]\nDefault: none\nContext: http, server, location\n\n日志pipe功能使用独立进程打印日志，不会阻塞worker进程，worker进程与独立日志进程间通过pipe进行通讯，rollback功能依赖日志pipe功能，提供基于tengine自身的日志回滚功能，支持，按照时间间隔、文件大小进行回滚，并支持配置，backup文件的个数。日志回滚模块会按照配置的条件将log文件rename成backup文件，然后重新写新日志文件\n\n该功能配置集成在access_log和error_log指令中：类似如下配置\n```\naccess_log \"pipe:rollback [logpath] interval=[interval] baknum=[baknum] maxsize=[maxsize] adjust=[adjust]\" proxyformat;\n\nerror_log  \"pipe:rollback [logpath] interval=[interval] baknum=[baknum] maxsize=[maxsize] adjust=[adjust]\" info;\n```\n\nlogpath: 日志输出路径\n\ninterval：日志回滚间隔，默认0（永不回滚）\n\nbaknum：backup文件保留个数，默认1（保留1个）\n\nmaxsize：log文件最大size，默认0（永不回滚）\n\nadjust: 按时间回滚时，回滚时间随机延后，用于规避集群同时触发回滚动作，默认60 （60s）\n\n使用示例：\n```\nerror_log  \"pipe:rollback logs/error_log interval=60m baknum=5 maxsize=2048M\" info;\n\nhttp {\n\tlog_format  main  '$remote_addr - $remote_user [$time_local] \"$request\" '\n                      '$status $body_bytes_sent \"$http_referer\" '\n                      '\"$http_user_agent\" \"$http_x_forwarded_for\"';\n\taccess_log  \"pipe:rollback logs/access_log interval=1h baknum=5 maxsize=2G\"  main;\n}\n```\n"
  },
  {
    "path": "docs/modules/ngx_procs_module.md",
    "content": "# Name #\n## Proc Module ##\n\nprovides a mechanism to support standalone processes\n\n# Code Examples #\n\na daytime server module, run in a standalone process.\n\nhttp://tengine.taobao.org/examples/ngx_proc_daytime_module\n\n# Examples #\n\n    processes {\n        process echo {\n            eho_str \"hello, world\";\n            echo on;\n            listen 8888;\n            count 1;\n            priority 1;\n            delay_start 10s;\n            respawn off;\n        }\n\n        process example {\n            count 1;\n            priority 0;\n            delay_start 0s;\n            respawn on;\n        }\n    }\n\n\n# Directives #\n\n## process ##\n\nSyntax: **process** `name { }`\n\nDefault: `none`\n\nContext: `processes`\n\n\n## count ##\n\nSyntax: **count** `num`\n\nDefault: `1`\n\nContext: `process`\n\nSpecify the number of processes which will be forked.\n\n\n## priority ##\n\nSyntax: **priority** `num`\n\nDefault: `0`\n\nContext: `process`\n\nPriority is a value in the range -20 to 20. Lower priorities cause more favorable scheduling.\n\n\n## delay\\_start ##\n\nSyntax: **delay\\_start** `time`\n\nDefault: `300ms`\n\nContext: `process`\n\nThe directive specifies the time to wait before process starts.\n\n\n## respawn ##\n\nSyntax: **respawn** `on | off`\n\nDefault: `on`\n\nContext: `process`\n\nThe directive specifies whether the process will be restarted by nginx when it encounters some errors and exits.\n"
  },
  {
    "path": "docs/modules/ngx_procs_module_cn.md",
    "content": "# 模块名 #\n## Proc 模块 ##\n\n提供一个让Tengine可以通过写不同模块启动独立进程的机制。\n\n# 代码实例 #\n\n一个时间回送服务器模块，它运行在一个独立的进程里。\n\nhttp://tengine.taobao.org/examples/ngx_proc_daytime_module\n\n\n# 例子 #\n\n    processes {\n        process echo {\n            eho_str \"hello, world\";\n            echo on;\n            listen 8888;\n            count 1;\n            priority 1;\n            delay_start 10s;\n            respawn off;\n        }\n\n        process example {\n            count 1;\n            priority 0;\n            delay_start 0s;\n            respawn on;\n        }\n    }\n\n\n# 指令 #\n\n## process ##\n\nSyntax: **process** `name { }`\n\nDefault: `none`\n\nContext: `processes`\n\n\n## count ##\n\nSyntax: **count** `num`\n\nDefault: `1`\n\nContext: `process`\n\n指定启动这个进程的数量。\n\n\n## priority ##\n\nSyntax: **priority** `num`\n\nDefault: `0`\n\nContext: `process`\n\n指定进程的优先级(-20 到 20 之间)，越低的数值调度优先级越高。\n\n\n## delay\\_start ##\n\nSyntax: **delay\\_start** `time`\n\nDefault: `300ms`\n\nContext: `process`\n\n指定延迟启动的时间。\n\n\n## respawn ##\n\nSyntax: **respawn** `on | off`\n\nDefault: `on`\n\nContext: `process`\n\n设置为`on`时，如果进程因为错误意外退出会被Tengine重新启动。\n"
  },
  {
    "path": "html/50x.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n<title>Error</title>\n<style>\n    body {\n        width: 35em;\n        margin: 0 auto;\n        font-family: Tahoma, Verdana, Arial, sans-serif;\n    }\n</style>\n</head>\n<body>\n<h1>An error occurred.</h1>\n<p>Sorry, the page you are looking for is currently unavailable.<br/>\nPlease try again later.</p>\n<p>If you are the system administrator of this resource then you should check\nthe error log for details.</p>\n<p><em>Faithfully yours, tengine.</em></p>\n</body>\n</html>\n"
  },
  {
    "path": "html/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n<title>Welcome to tengine!</title>\n<style>\n    body {\n        width: 35em;\n        margin: 0 auto;\n        font-family: Tahoma, Verdana, Arial, sans-serif;\n    }\n</style>\n</head>\n<body>\n<h1>Welcome to tengine!</h1>\n<p>If you see this page, the tengine web server is successfully installed and\nworking. Further configuration is required.</p>\n\n<p>For online documentation and support please refer to\n<a href=\"http://tengine.taobao.org/\">tengine.taobao.org</a>.</p>\n\n<p><em>Thank you for using tengine.</em></p>\n</body>\n</html>\n"
  },
  {
    "path": "man/nginx.8",
    "content": ".\\\"\n.\\\" Copyright (C) 2010 Sergey A. Osokin\n.\\\" Copyright (C) Nginx, Inc.\n.\\\" All rights reserved.\n.\\\"\n.\\\" Redistribution and use in source and binary forms, with or without\n.\\\" modification, are permitted provided that the following conditions\n.\\\" are met:\n.\\\" 1. Redistributions of source code must retain the above copyright\n.\\\"    notice, this list of conditions and the following disclaimer.\n.\\\" 2. Redistributions in binary form must reproduce the above copyright\n.\\\"    notice, this list of conditions and the following disclaimer in the\n.\\\"    documentation and/or other materials provided with the distribution.\n.\\\"\n.\\\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n.\\\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n.\\\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n.\\\" ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE\n.\\\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n.\\\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n.\\\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n.\\\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n.\\\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n.\\\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n.\\\" SUCH DAMAGE.\n.\\\"\n.\\\"\n.Dd June 16, 2015\n.Dt NGINX 8\n.Os\n.Sh NAME\n.Nm nginx\n.Nd \"HTTP and reverse proxy server, mail proxy server\"\n.Sh SYNOPSIS\n.Nm\n.Op Fl ?hqTtVv\n.Op Fl c Ar file\n.Op Fl g Ar directives\n.Op Fl p Ar prefix\n.Op Fl s Ar signal\n.Sh DESCRIPTION\n.Nm\n(pronounced\n.Dq engine x )\nis an HTTP and reverse proxy server, as well as a mail proxy server.\nIt is known for its high performance, stability, rich feature set, simple\nconfiguration, and low resource consumption.\n.Pp\nThe options are as follows:\n.Bl -tag -width \".Fl d Ar directives\"\n.It Fl ?\\& , h\nPrint help.\n.It Fl c Ar file\nUse an alternative configuration\n.Ar file .\n.It Fl g Ar directives\nSet global configuration directives.\nSee\n.Sx EXAMPLES\nfor details.\n.It Fl p Ar prefix\nSet the prefix path.\nThe default value is\n.Pa %%PREFIX%% .\n.It Fl q\nSuppress non-error messages during configuration testing.\n.It Fl s Ar signal\nSend a signal to the master process.\nThe argument\n.Ar signal\ncan be one of:\n.Cm stop , quit , reopen , reload .\nThe following table shows the corresponding system signals:\n.Pp\n.Bl -tag -width \".Cm reopen\" -compact\n.It Cm stop\n.Dv SIGTERM\n.It Cm quit\n.Dv SIGQUIT\n.It Cm reopen\n.Dv SIGUSR1\n.It Cm reload\n.Dv SIGHUP\n.El\n.It Fl t\nDo not run, just test the configuration file.\n.Nm\nchecks the configuration file syntax and then tries to open files\nreferenced in the configuration file.\n.It Fl T\nSame as\n.Fl t ,\nbut additionally dump configuration files to standard output.\n.It Fl V\nPrint the\n.Nm\nversion, compiler version, and\n.Pa configure\nscript parameters.\n.It Fl v\nPrint the\n.Nm\nversion.\n.El\n.Sh SIGNALS\nThe master process of\n.Nm\ncan handle the following signals:\n.Pp\n.Bl -tag -width \".Dv SIGINT , SIGTERM\" -compact\n.It Dv SIGINT , SIGTERM\nShut down quickly.\n.It Dv SIGHUP\nReload configuration, start the new worker process with a new\nconfiguration, and gracefully shut down old worker processes.\n.It Dv SIGQUIT\nShut down gracefully.\n.It Dv SIGUSR1\nReopen log files.\n.It Dv SIGUSR2\nUpgrade the\n.Nm\nexecutable on the fly.\n.It Dv SIGWINCH\nShut down worker processes gracefully.\n.El\n.Pp\nWhile there is no need to explicitly control worker processes normally,\nthey support some signals too:\n.Pp\n.Bl -tag -width \".Dv SIGINT , SIGTERM\" -compact\n.It Dv SIGTERM\nShut down quickly.\n.It Dv SIGQUIT\nShut down gracefully.\n.It Dv SIGUSR1\nReopen log files.\n.El\n.Sh DEBUGGING LOG\nTo enable a debugging log, reconfigure\n.Nm\nto build with debugging:\n.Pp\n.Dl \"./configure --with-debug ...\"\n.Pp\nand then set the\n.Cm debug\nlevel of the\n.Va error_log :\n.Pp\n.Dl \"error_log /path/to/log debug;\"\n.Pp\nIt is also possible to enable the debugging for a particular IP address:\n.Bd -literal -offset indent\nevents {\n\tdebug_connection 127.0.0.1;\n}\n.Ed\n.Sh ENVIRONMENT\nThe\n.Ev NGINX\nenvironment variable is used internally by\n.Nm\nand should not be set directly by the user.\n.Sh FILES\n.Bl -tag -width indent\n.It Pa %%PID_PATH%%\nContains the process ID of\n.Nm .\nThe contents of this file are not sensitive, so it can be world-readable.\n.It Pa %%CONF_PATH%%\nThe main configuration file.\n.It Pa %%ERROR_LOG_PATH%%\nError log file.\n.El\n.Sh EXIT STATUS\nExit status is 0 on success, or 1 if the command fails.\n.Sh EXAMPLES\nTest configuration file\n.Pa ~/mynginx.conf\nwith global directives for PID and quantity of worker processes:\n.Bd -literal -offset indent\nnginx -t -c ~/mynginx.conf \\e\n\t-g \"pid /var/run/mynginx.pid; worker_processes 2;\"\n.Ed\n.Sh SEE ALSO\n.\\\"Xr nginx.conf 5\n.\\\"Pp\nDocumentation at\n.Pa http://nginx.org/en/docs/ .\n.Pp\nFor questions and technical support, please refer to\n.Pa http://nginx.org/en/support.html .\n.Sh HISTORY\nDevelopment of\n.Nm\nstarted in 2002, with the first public release on October 4, 2004.\n.Sh AUTHORS\n.An -nosplit\n.An Igor Sysoev Aq igor@sysoev.ru .\n.Pp\nThis manual page was originally written by\n.An Sergey A. Osokin Aq osa@FreeBSD.org.ru\nas a result of compiling many\n.Nm\ndocuments from all over the world.\n"
  },
  {
    "path": "modules/mod_common/config",
    "content": "\n\nNGX_ADDON_SRCS=\"$NGX_ADDON_SRCS \\\n    $ngx_addon_dir/ngx_comm_string.c \\\n    $ngx_addon_dir/ngx_comm_shm.c \\\n    $ngx_addon_dir/ngx_comm_encrypt.c \\\n    $ngx_addon_dir/ngx_comm_serialize.c\"\n\nHTTP_INCS=\"$HTTP_INCS $ngx_addon_dir\"\n\n\nNGX_ADDON_DEPS=\"$NGX_ADDON_DEPS \\\n    $ngx_addon_dir/ngx_comm_string.h               \\\n    $ngx_addon_dir/ngx_comm_shm.h                  \\\n    $ngx_addon_dir/ngx_comm_encrypt.h             \\\n    $ngx_addon_dir/ngx_comm_serialize.h\"\n\n# for math comm api #\nCORE_LIBS=\"$CORE_LIBS -lm\"\n"
  },
  {
    "path": "modules/mod_common/ngx_comm_encrypt.c",
    "content": "\n/*\n * Copyright (C) 2020-2023 Alibaba Group Holding Limited\n */\n\n#include \"ngx_comm_encrypt.h\"\n\n#include <ngx_md5.h>\n\nvoid\nngx_comm_md5_string(ngx_str_t *src, u_char md5_hex[])\n{\n    u_char          md5_binary[NGX_COMM_MD5_BIN_LEN];\n    ngx_md5_t       md5;\n\n    ngx_md5_init(&md5);\n    ngx_md5_update(&md5, src->data, src->len);\n    ngx_md5_final(md5_binary, &md5);\n\n    ngx_hex_dump(md5_hex, md5_binary, NGX_COMM_MD5_BIN_LEN);\n}\n"
  },
  {
    "path": "modules/mod_common/ngx_comm_encrypt.h",
    "content": "/*\n * Copyright (C) 2020-2023 Alibaba Group Holding Limited\n */\n\n#ifndef NGX_COMM_ENCRYPT_H\n#define NGX_COMM_ENCRYPT_H\n\n#include <ngx_core.h>\n\n#define NGX_COMM_MD5_HEX_LEN         32\n#define NGX_COMM_MD5_BIN_LEN         16\n\n/**\n * @brief Calculate the md5 value of ngx_str_t\n * \n * @param src_data source string\n * @param md5_hex Output md5, size must be NGX_COMM_MD5_HEX_LEN\n */\nvoid\nngx_comm_md5_string(ngx_str_t *src, u_char md5_hex[]);\n\n#endif // NGX_COMM_ENCRYPT_H\n"
  },
  {
    "path": "modules/mod_common/ngx_comm_serialize.c",
    "content": "/*\n * Copyright (C) 2020-2023 Alibaba Group Holding Limited\n */\n\n#include <ngx_comm_serialize.h>\n\nngx_inline ngx_int_t\nngx_serialize_write_uint8(u_char **pos, uint32_t * left, uint8_t value)\n{\n    if (*left < sizeof(value)) {\n        return NGX_ERROR;\n    }\n\n    *left -= sizeof(value);\n    *(uint8_t*)(*pos) = value;\n\n    *pos += sizeof(value);\n\n    return NGX_OK;\n}\n\nngx_inline ngx_int_t\nngx_serialize_write_uint16(u_char **pos, uint32_t * left, uint16_t value)\n{\n    if (*left < sizeof(value)) {\n        return NGX_ERROR;\n    }\n\n    *left -= sizeof(value);\n    *(uint16_t*)(*pos) = htons(value);\n\n    *pos += sizeof(value);\n\n    return NGX_OK;\n}\n\nngx_inline ngx_int_t\nngx_serialize_write_uint32(u_char **pos, uint32_t * left, uint32_t value)\n{\n    if (*left < sizeof(value)) {\n        return NGX_ERROR;\n    }\n\n    *left -= sizeof(value);\n    *(uint32_t*)(*pos) = htonl(value);\n\n    *pos += sizeof(value);\n\n    return NGX_OK;\n}\n\nngx_inline ngx_int_t\nngx_serialize_write_data(u_char **pos, uint32_t * left, void* value, uint32_t len)\n{\n    if (*left < len) {\n        return NGX_ERROR;\n    }\n\n    *left -= len;\n\n    *pos = ngx_cpymem(*pos, value, len);\n\n    return NGX_OK;\n}\n\nngx_inline ngx_int_t\nngx_serialize_write_uint8_data(u_char **pos, uint32_t * left, void* value, uint8_t len)\n{\n    ngx_int_t rc;\n    \n    rc = ngx_serialize_write_uint8(pos, left, len);\n    if (rc == NGX_ERROR) {\n        return rc;\n    }\n\n    return ngx_serialize_write_data(pos, left, value, len);\n}\n\nngx_inline ngx_int_t \nngx_serialize_write_uint16_string(u_char **pos, uint32_t * left, ngx_str_t * str)\n{\n    ngx_int_t rc;\n\n    rc = ngx_serialize_write_uint16(pos, left, str->len);\n    if (rc == NGX_ERROR) {\n        return rc;\n    }\n\n    return ngx_serialize_write_data(pos, left, str->data, str->len);\n}\n\nngx_inline ngx_int_t \nngx_serialize_read_uint8(u_char **pos, uint32_t *left, uint8_t *value)\n{\n    if (*left < sizeof(*value)) {\n        return NGX_ERROR;\n    }\n\n    *value = **pos;\n\n    *pos += sizeof(*value);\n    *left -= sizeof(*value);\n\n    return NGX_OK;\n}\n\nngx_inline ngx_int_t \nngx_serialize_read_uint16(u_char **pos, uint32_t * left, uint16_t * value)\n{\n    if (*left < sizeof(*value)) {\n        return NGX_ERROR;\n    }\n\n    *value = ntohs(*(uint16_t*)(*pos));\n\n    *pos += sizeof(*value);\n    *left -= sizeof(*value);\n\n    return NGX_OK;\n}\n\nngx_inline ngx_int_t \nngx_serialize_read_uint32(u_char **pos, uint32_t * left, uint32_t * value)\n{\n    if (*left < sizeof(*value)) {\n        return NGX_ERROR;\n    }\n\n    *value = ntohl(*(uint32_t*)(*pos));\n\n    *pos += sizeof(*value);\n    *left -= sizeof(*value);\n\n    return NGX_OK;\n}\n\nngx_inline ngx_int_t \nngx_serialize_read_uint8_string(u_char **pos, uint32_t * left, ngx_str_t * str)\n{\n    ngx_int_t rc;\n    uint8_t len;\n\n    rc = ngx_serialize_read_uint8(pos, left, &len);\n    if (rc == NGX_ERROR) {\n        return rc;\n    }\n\n    if (*left < len) {\n        return NGX_ERROR;\n    }\n\n    str->data = *pos;\n    str->len = len;\n\n    *pos += len;\n    *left -= len;\n    \n    return NGX_OK;\n}\n\nngx_inline ngx_int_t \nngx_serialize_read_uint16_string(u_char **pos, uint32_t * left, ngx_str_t * str)\n{\n    ngx_int_t rc;\n    uint16_t len;\n\n    rc = ngx_serialize_read_uint16(pos, left, &len);\n    if (rc == NGX_ERROR) {\n        return rc;\n    }\n\n    if (*left < len) {\n        return NGX_ERROR;\n    }\n\n    str->data = *pos;\n    str->len = len;\n\n    *pos += len;\n    *left -= len;\n    \n    return NGX_OK;\n}\n\nngx_inline ngx_int_t\nngx_serialize_read_data(u_char **pos, uint32_t * left, void* value, uint32_t len)\n{\n    if (*left < len) {\n        return NGX_ERROR;\n    }\n\n    (void)ngx_cpymem(value, *pos, len);\n    \n    *pos += len;\n    *left -= len;\n\n    return NGX_OK;\n}\n\n\nngx_inline ngx_int_t \nngx_serialize_read_uint64(u_char **pos, uint32_t * left, uint64_t * value)\n{\n    if (*left < sizeof(*value)) {\n        return NGX_ERROR;\n    }\n\n    if (__BYTE_ORDER == __LITTLE_ENDIAN)\n    {\n        uint64_t val = *(uint64_t*)(*pos);\n        *value = (((uint64_t)htonl((uint32_t)((val << 32) >> 32))) << 32) | (unsigned int)htonl((int)(val >> 32));\n    }\n    else if (__BYTE_ORDER == __BIG_ENDIAN)\n    {\n        *value = *(uint64_t*)(*pos);\n    }\n    else {\n        return NGX_ERROR;\n    }\n\n    *pos += sizeof(*value);\n    *left -= sizeof(*value);\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "modules/mod_common/ngx_comm_serialize.h",
    "content": "/*\n * Copyright (C) 2020-2023 Alibaba Group Holding Limited\n */\n\n#ifndef NGX_COMM_SERIALIZE_H\n#define NGX_COMM_SERIALIZE_H\n\n#include <ngx_core.h>\n#include <ngx_string.h>\n\n/**\n * @brief Serialize 1-byte integer to target address\n * \n * @param pos the current position of the write\n * @param left remaining bytes\n * @param value value to write\n * @return ngx_inline\n *                  NGX_OK Success\n *                  NGX_ERROR failed\n */\nngx_int_t ngx_serialize_write_uint8(u_char **pos, uint32_t * left, uint8_t value);\n\n/**\n * @brief Serialize 2-byte integer to target address, network byte order\n * \n * @param pos the current position of the write\n * @param left remaining bytes\n * @param value value to write\n * @return ngx_inline\n *                  NGX_OK Success\n *                  NGX_ERROR failed\n */\nngx_int_t ngx_serialize_write_uint16(u_char **pos, uint32_t * left, uint16_t value);\n\n/**\n * @brief Serialize 4-byte integer to target address, network byte order\n * \n * @param pos the current position of the write\n * @param left remaining bytes\n * @param value value to write\n * @return ngx_inline\n *                  NGX_OK Success\n *                  NGX_ERROR failed\n */\nngx_int_t ngx_serialize_write_uint32(u_char **pos, uint32_t * left, uint32_t value);\n\n/**\n * @brief Serialize a buffer to the target address\n * \n * @param pos the current position of the write\n * @param left remaining bytes\n * @param value value to write\n * @param len value to write\n * @return \n *                  NGX_OK Success\n *                  NGX_ERROR failed\n */\nngx_int_t ngx_serialize_write_data(u_char **pos, uint32_t * left, void* value, uint32_t len);\n\n/**\n * @brief Serialize a section of 1 byte length + buffer to the target address\n * \n * @param pos the current position of the write\n * @param left remaining bytes\n * @param value value to write\n * @param len value to write\n * @return \n *                  NGX_OK Success\n *                  NGX_ERROR failed\n */\nngx_int_t ngx_serialize_write_uint8_data(u_char **pos, uint32_t * left, void* value, uint8_t len);\n\n/**\n * @brief Serialize a 2-byte length + string to the target address\n * \n * @param pos the current position of the write\n * @param left remaining bytes\n * @param str the string to write\n * @return \n *                  NGX_OK Success\n *                  NGX_ERROR failed\n */\nngx_int_t ngx_serialize_write_uint16_string(u_char **pos, uint32_t * left, ngx_str_t * str);\n\n/**\n * @brief Deserialize 1-byte from source memory\n * \n * @param pos Read the current position of the data\n * @param left remaining bytes\n * @param value deserialized value\n * @return \n *                  NGX_OK Success\n *                  NGX_ERROR failed\n */\nngx_int_t  ngx_serialize_read_uint8(u_char **pos, uint32_t *left, uint8_t *value);\n\n/**\n * @brief Deserialize 2-bytes from source memory\n * \n * @param pos Read the current position of the data\n * @param left remaining bytes\n * @param value deserialized value\n * @return \n *                  NGX_OK Success\n *                  NGX_ERROR failed\n */\nngx_int_t ngx_serialize_read_uint16(u_char **pos, uint32_t * left, uint16_t * value);\n\n/**\n * @brief Deserialize 4-bytes from source memory\n * \n * @param pos Read the current position of the data\n * @param left remaining bytes\n * @param value deserialized value\n * @return \n *                  NGX_OK Success\n *                  NGX_ERROR failed\n */\nngx_int_t ngx_serialize_read_uint32(u_char **pos, uint32_t * left, uint32_t * value);\n\n/**\n * @brief Deserialize string from source memory (including 1 byte length + content)\n * \n * @param pos Read the current position of the data\n * @param left remaining bytes\n * @param str deserialized value\n * @return \n *                  NGX_OK Success\n *                  NGX_ERROR failed\n */\nngx_int_t  ngx_serialize_read_uint8_string(u_char **pos, uint32_t * left, ngx_str_t * str);\n\n/**\n * @brief Deserialize string from source memory (including 2 bytes length + content)\n * \n * @param pos Read the current position of the data\n * @param left remaining bytes\n * @param str deserialized value\n * @return \n *                  NGX_OK Success\n *                  NGX_ERROR failed\n */\nngx_int_t ngx_serialize_read_uint16_string(u_char **pos, uint32_t * left, ngx_str_t * str);\n\n/**\n * @brief Deserialize a buffer from the source memory\n * \n * @param pos Read the current position of the data\n * @param left remaining bytes\n * @param value deserialized value\n * @param len The length to be deserialized\n * @return \n *                  NGX_OK Success\n *                  NGX_ERROR failed\n */\nngx_int_t ngx_serialize_read_data(u_char **pos, uint32_t * left, void* value, uint32_t len);\n\n/**\n * @brief Deserialize 8-bytes from source memory\n * \n * @param pos Read the current position of the data\n * @param left remaining bytes\n * @param value deserialized value\n * @return \n *                  NGX_OK Success\n *                  NGX_ERROR failed\n */\nngx_int_t ngx_serialize_read_uint64(u_char **pos, uint32_t * left, uint64_t * value);\n\n#endif // NGX_COMM_SERIALIZE_H\n"
  },
  {
    "path": "modules/mod_common/ngx_comm_shm.c",
    "content": "/*\n * Copyright (C) 2020-2023 Alibaba Group Holding Limited\n */\n\n#include \"ngx_comm_shm.h\"\n\n\nngx_shm_pool_t * ngx_shm_create_pool(u_char * addr, size_t size)\n{\n    ngx_shm_pool_t * pool = (ngx_shm_pool_t *)addr;\n\n    if (size < sizeof(ngx_shm_pool_t)) {\n        return NULL;\n    }\n\n    pool->base = addr + sizeof(ngx_shm_pool_t);\n    pool->pos = pool->base;\n    pool->last = addr + size;\n\n    pool->out_of_memory = 0;\n\n    return pool;\n}\n\nvoid ngx_shm_pool_reset(ngx_shm_pool_t * pool)\n{\n    pool->pos = pool->base;\n    pool->out_of_memory = 0;\n}\n\nngx_int_t ngx_shm_pool_size(ngx_shm_pool_t * pool)\n{\n    return pool->last - pool->base;\n}\n\nngx_int_t ngx_shm_pool_free_size(ngx_shm_pool_t * pool)\n{\n    return pool->last - pool->pos;\n}\n\nngx_int_t ngx_shm_pool_used_rate(ngx_shm_pool_t * pool)\n{\n    ngx_int_t used = pool->pos - pool->base;\n    ngx_int_t total = pool->last - pool->base;\n\n    return used * 100 / total;\n}\n\nvoid *ngx_shm_pool_calloc(ngx_shm_pool_t * pool, size_t size)\n{\n    void * p = NULL;\n\n    if (pool->last - pool->pos >= (ngx_int_t)size) {\n        p = pool->pos;\n        pool->pos += size;\n        memset(p, 0, size);\n    } else {\n        pool->out_of_memory = 1;\n    }\n\n    return p;\n}\n\nngx_int_t ngx_shm_pool_out_of_memory(ngx_shm_pool_t * pool)\n{\n    return pool->out_of_memory;\n}\n\nngx_str_t *ngx_shm_pool_calloc_str(ngx_shm_pool_t * pool, size_t str_size)\n{\n    ngx_str_t * str;\n    ngx_int_t buf_len = sizeof(ngx_str_t) + str_size;\n\n    u_char * p = ngx_shm_pool_calloc(pool, buf_len);\n    if (p == NULL) {\n        return NULL;\n    }\n\n    str = (ngx_str_t*)p;\n    str->data = p + sizeof(ngx_str_t);\n    str->len = 0;\n\n    return str;\n}\n\n\nngx_shm_array_t* ngx_shm_array_create(ngx_shm_pool_t * pool,\n    ngx_int_t max_n,\n    ngx_int_t size)\n{\n    u_char * addr;\n    \n    addr = ngx_shm_pool_calloc(pool, sizeof(ngx_shm_array_t) + max_n * size);\n    if (addr == NULL) {\n        return NULL;\n    }\n\n    ngx_shm_array_t * a = (ngx_shm_array_t *)addr;\n\n    a->elts = addr + sizeof(ngx_shm_array_t);\n    a->size = size;\n    a->nelts = 0;\n    a->nalloc = max_n;\n\n    return a;\n}\n\nvoid *ngx_shm_array_push(ngx_shm_array_t *a)\n{\n    void * p = NULL;\n\n    if (a == NULL) {\n        return NULL;\n    }\n    if (a->nelts == a->nalloc) {\n        return NULL;\n    }\n\n    p = (u_char*)a->elts + a->nelts * a->size;\n\n    a->nelts ++;\n\n    return p;\n}\n\nvoid *ngx_shm_array_push_n(ngx_shm_array_t *a, ngx_uint_t n)\n{\n    void * p = NULL;\n\n    if (a == NULL) {\n        return NULL;\n    }\n    if (a->nelts + n > a->nalloc) {\n        return NULL;\n    }\n\n    p = (u_char*)a->elts + a->nelts * a->size;\n\n    a->nelts += n;\n\n    return p;\n}\n\nvoid ngx_shm_sort_array(ngx_shm_array_t *a, ngx_shm_compar_func c)\n{\n    if (a == NULL) {\n        return;\n    }\n    qsort(a->elts, a->nelts, a->size, c);\n}\n\nvoid * ngx_shm_search_array(ngx_shm_array_t *a, const void * key, ngx_shm_compar_func c)\n{\n    void * res = NULL;\n    if (a == NULL || key == NULL) {\n        return NULL;\n    }\n    res = bsearch(key, a->elts, a->nelts, a->size, c);\n    return res;\n}\n\ntypedef struct {\n    ngx_queue_t  hash_node;\n    void        *data;\n} ngx_shm_hash_node_t;\n\n\nngx_shm_hash_t *ngx_shm_hash_create(ngx_shm_pool_t * pool,\n    ngx_int_t bucket_size,\n    ngx_shm_hash_calc_func hash_func,\n    ngx_shm_compar_func compar_func)\n{\n    ngx_shm_hash_t      *table = NULL;\n    u_char              *addr;\n    ngx_int_t            table_size;\n    ngx_int_t            i;\n\n    if (hash_func == NULL || compar_func == NULL) {\n        return NULL;\n    }\n\n    table_size = sizeof(ngx_shm_hash_t) + bucket_size * sizeof(ngx_queue_t);\n\n    addr = ngx_shm_pool_calloc(pool, table_size);\n    if (addr == NULL) {\n        return NULL;\n    }\n\n    table = (ngx_shm_hash_t*)addr;\n\n    table->bucket_size = bucket_size;\n    table->hash_func = hash_func;\n    table->compar_func = compar_func;\n    table->pool = pool;\n\n    for (i = 0; i < bucket_size; i++) {\n        ngx_queue_init(&table->buckets[i]);\n    }\n\n    return table;\n}\n\nngx_int_t ngx_shm_hash_add(ngx_shm_hash_t * table, void * elem)\n{\n    ngx_shm_hash_node_t * node = NULL;\n    ngx_uint_t hash = 0;\n\n    node = ngx_shm_pool_calloc(table->pool, sizeof(ngx_shm_hash_node_t));\n    if (node == NULL) {\n        return NGX_ERROR;\n    }\n\n    node->data = elem;\n    hash = table->hash_func(elem);\n\n    ngx_queue_insert_head(&table->buckets[hash % table->bucket_size], &node->hash_node);\n\n    return NGX_OK;\n}\n\nngx_int_t\nngx_shm_hash_del(ngx_shm_hash_t * table, void * elem)\n{\n    ngx_uint_t             hash;\n    ngx_queue_t           *slot;\n    ngx_queue_t           *q;\n    ngx_shm_hash_node_t   *node;\n\n    if (table == NULL) {\n        return NGX_ERROR;\n    }\n    hash = table->hash_func(elem);\n\n    slot = &table->buckets[hash % table->bucket_size];\n\n    for (q = ngx_queue_head(slot);\n         q != ngx_queue_sentinel(slot);\n         q = ngx_queue_next(q))\n    {\n        node = ngx_queue_data(q, ngx_shm_hash_node_t, hash_node);\n        \n        if (table->compar_func(node->data, elem) == 0) {\n            ngx_queue_remove(&node->hash_node);\n            break;\n        }\n    }\n\n    return NGX_OK;\n}\n\nvoid * ngx_shm_hash_get(ngx_shm_hash_t * table, void * elem)\n{\n    ngx_uint_t               hash;\n    ngx_queue_t             *slot;\n    ngx_queue_t             *q;\n    ngx_shm_hash_node_t     *node;\n\n    if (table == NULL) {\n        return NULL;\n    }\n    hash = table->hash_func(elem);\n\n    slot = &table->buckets[hash % table->bucket_size];\n\n    if (ngx_queue_empty(slot)) {\n        return NULL;\n    }\n\n    for (q = ngx_queue_head(slot);\n         q != ngx_queue_sentinel(slot);\n         q = ngx_queue_next(q))\n    {\n        node = ngx_queue_data(q, ngx_shm_hash_node_t, hash_node);\n        \n        if (table->compar_func(node->data, elem) == 0) {\n            return node->data;\n        }\n    }\n\n    return NULL;\n}\n\n\nngx_int_t ngx_shm_str_copy(ngx_shm_pool_t * pool, ngx_str_t * dst, ngx_str_t * src)\n{\n    dst->data = ngx_shm_pool_calloc(pool, src->len);\n    if (dst->data == NULL) {\n        return NGX_ERROR;\n    }\n\n    dst->len = src->len;\n    memcpy(dst->data, src->data, dst->len);\n\n    return NGX_OK;\n}\n\n\n"
  },
  {
    "path": "modules/mod_common/ngx_comm_shm.h",
    "content": "/*\n * Copyright (C) 2020-2023 Alibaba Group Holding Limited\n */\n\n#ifndef NGX_COMM_SHM_H\n#define NGX_COMM_SHM_H\n\n#include <ngx_core.h>\n#include <ngx_buf.h>\n#include <ngx_queue.h>\n\n/**\n * @brief shared memory allocator\n * @note Continuous allocation of memory, does not provide partial memory release, only all allocated memory can be released\n */\ntypedef struct {\n    u_char * base;\n    u_char * pos;\n    u_char * last;\n\n    ngx_int_t out_of_memory;\n} ngx_shm_pool_t;\n\n/**\n * @brief Create a shared memory pool, which has a fixed size and allocates memory continuously\n * \n * @param addr shared memory address\n * @param size shared memory size\n * @return ngx_shm_pool_t* Returns the memory pool address\n * @retval NULL Creation failed\n * @warning The size of the memory pool that can be allocated is size - sizeof(ngx_shm_pool_t)\n */\nngx_shm_pool_t * ngx_shm_create_pool(u_char * addr, size_t size);\n\n/**\n * @brief allocate memory from memory pool\n * \n * @param pool memory pool\n * @param size Required memory allocation size\n * @return void* \n * @warning Must be used in the process of creating the memory pool\n */\nvoid *ngx_shm_pool_calloc(ngx_shm_pool_t * pool, size_t size);\n\n/**\n * @brief Check for insufficient memory\n * \n * @param pool pool memory pool\n * @return ngx_int_t 1 Not enough storage\n *                   0 sufficient memory\n */\nngx_int_t ngx_shm_pool_out_of_memory(ngx_shm_pool_t * pool);\n\n/**\n * @brief allocate ngx_str_t\n * \n * @param pool memory pool\n * @param str_size String data required size\n * @return ngx_str_t* Returns the allocated string\n * @note data data is the memory space of str_size size, len is 0\n * @warning Must be used in the process of creating the memory pool\n */\nngx_str_t *ngx_shm_pool_calloc_str(ngx_shm_pool_t * pool, size_t str_size);\n\n/**\n * @brief Reset the pool, freeing all allocated memory\n * \n * @param pool shared memory pool\n * @warning Must be used in the process of creating the memory pool\n */\nvoid ngx_shm_pool_reset(ngx_shm_pool_t * pool);\n\n/**\n * @brief Get the memory space size of the memory pool\n * \n * @param pool shared memory pool\n * @return ngx_int_t 共享内存总大小\n * @note contains allocated space\n */\nngx_int_t ngx_shm_pool_size(ngx_shm_pool_t * pool);\n\n/**\n * @brief Get the memory size that can be allocated by the memory pool\n * \n * @param pool shared memory pool\n * @return ngx_int_t Allocatable memory size\n */\nngx_int_t ngx_shm_pool_free_size(ngx_shm_pool_t * pool);\n\n/**\n * @brief Get shared memory usage percentage\n * \n * @param pool shared memory size\n * @return ngx_int_t memory usage percentage [0-100]\n */\nngx_int_t ngx_shm_pool_used_rate(ngx_shm_pool_t * pool);\n\n\n/**\n * @brief shared memory array\n * @note Fixed element size and fixed number\n */\ntypedef struct {\n    void        *elts;\n    ngx_uint_t   nelts;\n    size_t       size;\n    ngx_uint_t   nalloc;\n} ngx_shm_array_t;\n\n/**\n * @brief Create shared memory array\n * \n * @param pool shared memory pool\n * @param max_n Maximum number of elements\n * @param size Each element size\n * @return ngx_shm_array_t* shared memory array\n * @retval NULL creation failed\n */\nngx_shm_array_t* ngx_shm_array_create(ngx_shm_pool_t * pool, ngx_int_t max_n, ngx_int_t size);\n\n/**\n * @brief add array element\n * \n * @param a shared memory array\n * @return void* return element address\n * @retval NULL failed to add element\n * @warning Does not support process safety, must be used in the process that creates the array\n */\nvoid *ngx_shm_array_push(ngx_shm_array_t *a);\n\n/**\n * @brief add n array elements\n * \n * @param a shared memory array\n * @param n number of elements added\n * @return void* return element address\n * @retval NULL failed to add element\n * @warning Does not support process safety, must be used in the process that creates the array\n */\nvoid *ngx_shm_array_push_n(ngx_shm_array_t *a, ngx_uint_t n);\n\n/**\n * @brief element comparison function\n */\ntypedef int (*ngx_shm_compar_func)(const void *, const void*);\n\n/**\n * @brief Sort array elements\n * \n * @param a array\n * @param c comparison function\n */\nvoid ngx_shm_sort_array(ngx_shm_array_t *a, ngx_shm_compar_func c);\n\n/**\n * @brief Retrieve an element in an ordered array\n * \n * @param a ordered array\n * @param key the key to retrieve\n * @param c comparison function\n * @return void* retrieved elements\n */\nvoid * ngx_shm_search_array(ngx_shm_array_t *a, const void * key, ngx_shm_compar_func c);\n\n\n/**\n * @brief Hash function\n */\ntypedef ngx_uint_t (*ngx_shm_hash_calc_func)(const void *);\n\n\n/**\n * @brief Shared Memory Hash Table\n * @code\n    typedef struct {\n        char key[255];\n        char data[1024];\n    } node;\n\n    int compare(const void * p1, const void* p2) {\n        node * n1 = p1;\n        node * n2 = p2;\n        return strcmp(n1.key, n2.key);\n    }\n    int hash(const void * p) {\n        node * n = p;\n        return ngx_hash_key(n->key, strlen(n->key));\n    }\n\n    ngx_shm_pool_t * pool = ngx_shm_create_pool(shm(size), size);\n\n    ngx_shm_hash_t * table = ngx_shm_hash_create(pool, 11701, hash, compare);\n\n    node * node1 = ngx_shm_pool_calloc(pool, sizeof(node));\n    strcpy(node1->key, \"testkey1\");\n    \n    ngx_shm_hash_add(table, node1);\n\n    node * node2 = ngx_shm_hash_get(table, node1);\n * @endcode\n */\n\ntypedef struct {\n    ngx_int_t                bucket_size;\n    ngx_shm_hash_calc_func   hash_func;\n    ngx_shm_compar_func      compar_func;\n    ngx_shm_pool_t          *pool;\n    ngx_queue_t              buckets[0];\n} ngx_shm_hash_t;\n\n/**\n * @brief Create a shared memory hash table\n * \n * @param pool memory pool\n * @param bucket_size Hash bucket size\n * @param hash_func Hash function\n * @param compar_func comparison function\n * @return ngx_shm_hash_t* Hash table address\n * @retval NULL Creation failed\n */\nngx_shm_hash_t *ngx_shm_hash_create(ngx_shm_pool_t * pool,\n    ngx_int_t bucket_size,\n    ngx_shm_hash_calc_func hash_func,\n    ngx_shm_compar_func compar_func);\n\n/**\n * @brief Add Hash element\n * \n * @param table Hash table\n * @param elem element pointer\n * @return ngx_int_t result\n * @retval NGX_OK success\n * @retval NGX_ERROR fail\n * @warning The element memory space must be created in advance using shared memory\n */\nngx_int_t ngx_shm_hash_add(ngx_shm_hash_t * table, void * elem);\n\n/**\n * @brief Delete the Hash element\n * \n * @param table Hash table\n * @param elem pointer to the element to be deleted\n * @return ngx_int_t result\n * @retval NGX_OK Success (deletion is successful if it does not exist)\n * @retval NGX_ERROR exception error, such as table is NULL\n */\nngx_int_t\nngx_shm_hash_del(ngx_shm_hash_t * table, void * elem);\n\n/**\n * @brief Get the Hash element\n * \n * @param table Hash table\n * @param elem The retrieved target Key\n * @return void* Element address in Hash table\n */\nvoid * ngx_shm_hash_get(ngx_shm_hash_t * table, void * elem);\n\n\n/**\n * @brief copy string\n * @param pool  memory pool\n * @param dst   destination string\n * @param src   source string\n * @return  result\n * @retval NGX_OK success\n * @retval NGX_ERROR fail\n * @note dst must exist, applicable to the ngx_str_t structure exists but the data memory space does not exist\n */\nngx_int_t ngx_shm_str_copy(ngx_shm_pool_t * pool, ngx_str_t * dst, ngx_str_t * src);\n\n\n#endif // NGX_COMM_SHM_H\n"
  },
  {
    "path": "modules/mod_common/ngx_comm_string.c",
    "content": "/*\n * Copyright (C) 2020-2023 Alibaba Group Holding Limited\n */\n\n#include \"ngx_comm_string.h\"\n\n\nngx_int_t\ncomm_split_string(ngx_str_t * out, ngx_int_t out_len, ngx_str_t * in, u_char terminate)\n{\n    u_char * pos = in->data;\n    u_char * last = in->data + in->len;\n\n    ngx_int_t index = 0;\n    ngx_int_t last_emtpy = 0;\n\n    while (index < out_len && pos < last) {\n        out[index].data = pos;\n        out[index].len = 0;\n        while (pos < last) {\n            if (*pos == terminate) {\n                pos ++;\n                last_emtpy = 1;\n                break;\n            }\n            pos ++;\n            out[index].len ++;\n            last_emtpy = 0;\n        }\n        index ++;\n    }\n    if (last_emtpy == 1 && index < out_len - 1) {\n        out[index].data = pos;\n        out[index].len = 0;\n        index ++;\n    }\n    return index;\n}\n\n\n#define UNIT_MAX_LONGLONG_T_VALUE  9223372036854775807\n\nlong long int\ncomm_atoll(u_char *line, size_t n)\n{\n    long long int  value, cutoff, cutlim;\n\n    if (n == 0) {\n        return NGX_ERROR;\n    }\n\n    cutoff = UNIT_MAX_LONGLONG_T_VALUE / 10;\n    cutlim = UNIT_MAX_LONGLONG_T_VALUE % 10;\n\n    for (value = 0; n--; line++) {\n        if (*line < '0' || *line > '9') {\n            return NGX_ERROR;\n        }\n\n        if (value >= cutoff && (value > cutoff || *line - '0' > cutlim)) {\n            return NGX_ERROR;\n        }\n\n        value = value * 10 + (*line - '0');\n    }\n\n    return value;\n}\n\nlong long int\ncomm_atoll_with_trim(u_char *line, size_t n)\n{\n    while (n > 0 && isspace(*line)) {\n        line++;\n        n--;\n    }\n    while (n > 0 && isspace(line[n-1])) {\n        n--;\n    }\n    return comm_atoll(line, n);\n}\n\n\nngx_int_t ngx_comm_strcasecmp(ngx_str_t * src, ngx_str_t * dst) {\n    if (src->len != dst->len) {\n        return src->len - dst->len;\n    }\n\n    return ngx_strncasecmp(src->data, dst->data, src->len);\n}\n\n\nvoid\nngx_strupper(u_char *dst, u_char *src, size_t n)\n{\n    while (n) {\n        *dst = ngx_toupper(*src);\n        dst++;\n        src++;\n        n--;\n    }\n}\n\nint ngx_comm_strcmp(const ngx_str_t * v1, const ngx_str_t * v2)\n{\n    if (v1->len != v2->len) {\n        return v1->len - v2->len;\n    }\n    return ngx_strncmp(v1->data, v2->data, v1->len);\n}\n\n\nngx_str_t*\nngx_comm_str_dup(ngx_pool_t * pool, ngx_str_t * src)\n{\n    ngx_str_t * dst = NULL;\n    u_char * p;\n\n    p = ngx_palloc(pool, sizeof(ngx_str_t) + src->len);\n    if (p == NULL) {\n        return dst;\n    }\n\n    dst = (ngx_str_t*)p;\n\n    dst->len = src->len;\n    dst->data = p + sizeof(ngx_str_t);\n    memcpy(dst->data, src->data, src->len);\n\n    return dst;\n}\n\nint\nngx_comm_cstr_casecmp(const char * src, size_t src_len, ngx_str_t * dst)\n{\n    if (src_len != dst->len) {\n        return src_len - dst->len;\n    }\n    return ngx_strncasecmp((u_char *)src, dst->data, src_len);\n}\n\n\nint\nngx_comm_count_character(u_char * pos, u_char * last, char c)\n{\n    int cnt;\n    for (cnt = 0; pos < last; pos++) {\n        if (*pos == c) {\n            cnt ++;\n        }\n    }\n    return cnt;\n}\n\nu_char *\nngx_comm_strchr(u_char * pos, u_char * last, char c)\n{\n    for (; pos < last; pos++) {\n        if (*pos == c) {\n            return pos;\n        }\n    }\n    return NULL;\n}\n\nngx_int_t\nngx_comm_split_string(ngx_str_t * arr, ngx_int_t n,\n    u_char * pos, u_char * last, u_char terminate)\n{\n    ngx_str_t input = {last - pos, pos};\n    return comm_split_string(arr, n , &input, terminate);\n}\n\nngx_int_t\nngx_comm_trim_string(ngx_str_t * source)\n{\n    ngx_int_t cnt = 0;\n    ngx_uint_t i;\n    for (i = 0; i < source->len; i++) {\n        if (!isspace(source->data[i])) {\n            break;\n        }\n    }\n    source->data += i;\n    source->len -= i;\n    cnt += i;\n    for (i = 0; i < source->len; i++) {\n        if (!isspace(source->data[source->len - i - 1])) {\n            break;\n        }\n    }\n    source->len -= i;\n    cnt += i;\n    return cnt;\n}\n\nngx_int_t ngx_comm_strcpy(ngx_pool_t * pool, ngx_str_t * dst, ngx_str_t * src)\n{\n    dst->data = ngx_palloc(pool, src->len);\n    if (dst->data == NULL) {\n        return NGX_ERROR;\n    }\n\n    dst->len = src->len;\n    memcpy(dst->data, src->data, src->len);\n\n    return NGX_OK;\n}\n\nngx_int_t ngx_comm_parse_string_value(ngx_str_t *line, ngx_str_t *key, ngx_str_t *value, u_char terminate)\n{\n#define MAX_LINE_KEY_NUM    128\n    ngx_str_t arr[MAX_LINE_KEY_NUM];\n    ngx_int_t i;\n\n    ngx_int_t num = comm_split_string(arr, MAX_LINE_KEY_NUM, line, terminate);\n    if (num == MAX_LINE_KEY_NUM) {\n        return NGX_ERROR;\n    }\n\n    for (i = 0; i < num; i++) {\n        ngx_str_t kv[3];\n        ngx_int_t kv_num = comm_split_string(kv, 3, &arr[i], '=');\n        if (kv_num != 2) {\n            return NGX_ERROR;\n        }\n        if (ngx_comm_strcmp(&kv[0], key) == 0) {\n            *value = kv[1];\n            return NGX_OK;\n        }\n    }\n\n    return NGX_ERROR;\n}\n\nngx_int_t ngx_comm_parse_int_value(ngx_str_t *line, ngx_str_t *key, u_char terminate)\n{\n    ngx_int_t rc;\n    ngx_str_t value;\n\n    rc = ngx_comm_parse_string_value(line, key, &value, terminate);\n    if (rc == NGX_ERROR) {\n        return rc;\n    }\n\n    rc = ngx_atoi(value.data, value.len);\n\n    return rc;\n}\n\n\nngx_int_t\nngx_comm_suffix_casecmp(ngx_str_t * src, ngx_str_t * suffix)\n{\n    ngx_int_t diff = src->len - suffix->len;\n    if (diff < 0) {\n        return NGX_ERROR;\n    }\n\n    if (ngx_strncasecmp((u_char *)src->data + diff, suffix->data, suffix->len) == 0) {\n        return NGX_OK;\n    }\n\n    return NGX_ERROR;\n}\n\n\nngx_int_t\nngx_comm_prefix_casecmp(ngx_str_t * src, ngx_str_t * prefix)\n{\n    if (src->len < prefix->len) {\n        return NGX_ERROR;\n    }\n\n    if (ngx_strncasecmp(src->data, prefix->data, prefix->len) == 0) {\n        return NGX_OK;\n    }\n\n    return NGX_ERROR;\n}\n\nngx_int_t\nngx_comm_prefix_cmp(ngx_str_t * src, ngx_str_t * prefix)\n{\n    if (src->len < prefix->len) {\n        return NGX_ERROR;\n    }\n\n    if (ngx_strncmp(src->data, prefix->data, prefix->len) == 0) {\n        return NGX_OK;\n    }\n\n    return NGX_ERROR;\n}\n\n\nint\nngx_comm_str_compare(const void *c1, const void *c2)\n{\n    return ngx_comm_strcmp(c1, c2);\n}\n"
  },
  {
    "path": "modules/mod_common/ngx_comm_string.h",
    "content": "/*\n * Copyright (C) 2020-2023 Alibaba Group Holding Limited\n */\n\n#ifndef NGX_COMM_STRING_H\n#define NGX_COMM_STRING_H\n\n#include <ngx_core.h>\n#include <ngx_buf.h>\n\n\n/**\n * @brief split string\n * \n * @param out       split string array\n * @param out_len   The size of the split string array\n * @param in        The character string to be split, the original value will not be changed after splitting\n * @param terminate delimiter\n * @return Return the size of the array, if out_len is too small, it will be truncated to return the maximum size that can be accommodated\n */\nngx_int_t\ncomm_split_string(ngx_str_t * out, ngx_int_t out_len, ngx_str_t * in, u_char terminate);\n\n/**\n * @brief String to integer (negative numbers are not supported)\n * \n * @param line  input string\n * @param n     input string size\n * @return  Returns an integer, or NGX_ERROR if the rule is not met\n */\nlong long int\ncomm_atoll(u_char *line, size_t n);\n\n/**\n * @brief String to integer (negative numbers are not supported, and blank characters are removed from input)\n * \n * @param line  input string\n * @param n     input string size\n * @return  Returns an integer, or NGX_ERROR if the rule is not met\n */\nlong long int\ncomm_atoll_with_trim(u_char *line, size_t n);\n\n/**\n * @brief compare ngx_str_t content\n * \n * @param src  source string\n * @param dst  destination string\n * @return  Returns an integer, equal returns 0, src<dst returns a negative number, src>dst returns a positive number\n */\nngx_int_t ngx_comm_strcasecmp(ngx_str_t * src, ngx_str_t * dst);\n\n/**\n * @brief String copy and convert to uppercase\n * \n * @param dst destination string (make sure there is enough space)\n * @param src source string\n * @param n   the number of bytes to copy\n */\nvoid ngx_strupper(u_char *dst, u_char *src, size_t n);\n\n/**\n * @brief compare ngx_str_t content\n * \n * @param src  input string\n * @param dst  output string\n * @return  Returns an integer, equal returns 0, src<dst returns a negative number, src>dst returns a positive number\n */\nint ngx_comm_strcmp(const ngx_str_t * v1, const ngx_str_t * v2);\n\n/**\n * @brief copy string\n * @param pool  memory pool\n * @param src   source string\n * @return  Returns the copied string, or NULL on failure\n */\nngx_str_t *ngx_comm_str_dup(ngx_pool_t * pool, ngx_str_t * src);\n\n\n/**\n * @brief compare c string and ngx_str content\n * \n * @param src       source c string\n * @param src_len   source c string length\n * @param dst       destination string\n * @return  Returns an integer, equal returns 0, src<dst returns a negative number, src>dst returns a positive number\n */\nint ngx_comm_cstr_casecmp(const char * src, size_t src_len, ngx_str_t * dst);\n\n/**\n * @brief Count character occurrences\n * \n * @param pos  starting point\n * @param last end position (not included)\n * @param c The target character to find\n * @return int The number of occurrences\n */\nint ngx_comm_count_character(u_char * pos, u_char * last, char c);\n\n/**\n * @brief Find a single character in a string\n * \n * @param pos   starting point\n * @param last  end position (not included)\n * @param c     The target character to find\n * @return u_char*  The first occurrence of the character, if not found, returns NULL\n */\nu_char *ngx_comm_strchr(u_char * pos, u_char * last, char c);\n\n/**\n * @brief split string\n * \n * @param arr           result array\n * @param n             The length of the result array (need to ensure that the length is sufficient)\n * @param pos           string starting position\n * @param last          end of string (exclusive)\n * @param terminate     split character\n * @return ngx_int_t    Return the size of the result array (if the length of the array is not enough, it will be truncated and only the maximum size that can be carried will be returned)\n */\nngx_int_t ngx_comm_split_string(ngx_str_t * arr, ngx_int_t n,\n    u_char * pos, u_char * last, u_char terminate);\n\n/**\n * @brief Remove leading and trailing whitespace characters\n * \n * @param source [in,out] destination string\n * @return ngx_int_t source string\n */\nngx_int_t ngx_comm_trim_string(ngx_str_t * source);\n\n/**\n * @brief deep copy string\n * @param pool  pool for alloc memory\n * @param dst   destination string\n * @param src   source string\n * @return  Returns NGX_OK on success, NGX_ERROR on failure\n */\nngx_int_t ngx_comm_strcpy(ngx_pool_t * pool, ngx_str_t * dst, ngx_str_t * src);\n\n/**\n * @brief Parse the key-value in a string\n * @param line      input string\n * @param key       key\n * @param value     return value\n * @param terminate Delimiter, such as key1=value1,key2=value2 delimiter is,\n * @return Returns NGX_OK on success, NGX_ERROR on failure\n */\nngx_int_t ngx_comm_parse_string_value(ngx_str_t *line, ngx_str_t *key, ngx_str_t *value, u_char terminate);\n\n/**\n * @brief Parse the key-value in a row\n * @param line      input line\n * @param key       key\n * @param value     return value\n * @param terminate Delimiter, such as key1=value1,key2=value2 delimiter is,\n * @return Successfully returns the parsed value successfully, fails to return NGX_ERROR\n */\nngx_int_t ngx_comm_parse_int_value(ngx_str_t *line, ngx_str_t *key, u_char terminate);\n\n\n/**\n * @brief Suffix comparison, case insensitive\n * \n * @param src  source string\n * @param suffix  matching suffix\n * @return  Returns an integer, returns NGX_OK if the match is successful, and NGX_ERROR indicates no match\n */\nngx_int_t ngx_comm_suffix_casecmp(ngx_str_t * src, ngx_str_t * suffix);\n\n/**\n * @brief Prefix comparison, case insensitive\n * \n * @param src  source string\n * @param prefix  matching prefix\n * @return  Returns an integer, returns NGX_OK if the match is successful, and NGX_ERROR indicates no match\n */\nngx_int_t ngx_comm_prefix_casecmp(ngx_str_t * src, ngx_str_t * prefix);\n\n\n/**\n * @brief Prefix comparison, case sensitive\n * \n * @param src  source string\n * @param prefix  matching prefix\n * @return  Returns an integer, returns NGX_OK if the match is successful, and NGX_ERROR indicates no match\n */\nngx_int_t ngx_comm_prefix_cmp(ngx_str_t * src, ngx_str_t * prefix);\n\n/**\n * @brief String comparison, for use with qsort\n */\nint\nngx_comm_str_compare(const void *c1, const void *c2);\n\n#endif // NGX_COMM_STRING_H\n\n\n"
  },
  {
    "path": "modules/mod_config/config",
    "content": "if [ \"$NGX_PLATFORM\" != win32 ]; then\n    ngx_feature=\"compiler structure-packing pragma\"\n    ngx_feature_name=\"NGX_HAVE_PACK_PRAGMA\"\n    ngx_feature_run=yes\n    ngx_feature_incs=\n    ngx_feature_path=\n    ngx_feature_libs=\n    ngx_feature_test=\"#pragma pack(push, 1)\n                      struct test_s {\n                          char foo;\n                          int  bar;\n                      };\n                      #pragma pack(pop)\n                      \n                      if (sizeof(struct test_s) != (sizeof(char) + sizeof(int)))\n                          return 1;\"\n        \n    . auto/feature\nfi\n"
  },
  {
    "path": "modules/mod_dubbo/README.md",
    "content": "# Quick Start\r\n\r\n## Install Tengine\r\n\r\n### Get Tengine\r\n```\r\ngit clone https://github.com/alibaba/tengine.git\r\n```\r\n### Get some other vendor\r\n```\r\ncd ./tengine\r\n\r\nwget https://ftp.pcre.org/pub/pcre/pcre-8.43.tar.gz\r\ntar xvf pcre-8.43.tar.gz\r\n\r\nwget https://www.openssl.org/source/openssl-1.0.2s.tar.gz\r\ntar xvf openssl-1.0.2s.tar.gz\r\n\r\nwget http://www.zlib.net/zlib-1.2.11.tar.gz\r\ntar xvf zlib-1.2.11.tar.gz\r\n```\r\n\r\n### Build Tengine\r\n```\r\n./configure --add-module=./modules/mod_dubbo --add-module=./modules/ngx_multi_upstream_module --add-module=./modules/mod_config --with-pcre=./pcre-8.43/ --with-openssl=./openssl-1.0.2s/ --with-zlib=./zlib-1.2.11\r\nmake\r\nsudo make install\r\n```\r\n\r\nCentOS maybe need\r\n```\r\nsudo yum install gcc\r\nsudo yum install gcc-c++\r\n```\r\n\r\n### Run Tengine\r\n\r\nmodify tengine config file ```/usr/local/nginx/conf/nginx.conf``` to \r\n\r\n```\r\nworker_processes  1;\r\n\r\nevents {\r\n    worker_connections  1024;\r\n}\r\n\r\n\r\nhttp {\r\n    include       mime.types;\r\n    default_type  application/octet-stream;\r\n\r\n    sendfile        on;\r\n\r\n    server {\r\n        listen       8080;\r\n        server_name  localhost;\r\n        \r\n        #pass the Dubbo to Dubbo Provider server listening on 127.0.0.1:20880\r\n        location / {\r\n            set $dubbo_service_name \"org.apache.dubbo.samples.tengine.DemoService\";\r\n            set $dubbo_service_name \"0.0.0\";\r\n            set $dubbo_service_name \"tengineDubbo\";\r\n\r\n            dubbo_pass_all_headers on;\r\n            dubbo_pass_set args $args;\r\n            dubbo_pass_set uri $uri;\r\n            dubbo_pass_set method $request_method;\r\n        \r\n            dubbo_pass $dubbo_service_name $dubbo_service_version $dubbo_method dubbo_backend;\r\n        }\r\n    }\r\n\r\n    #pass the Dubbo to Dubbo Provider server listening on 127.0.0.1:20880\r\n    upstream dubbo_backend {\r\n        multi 1;\r\n        server 127.0.0.1:20880;\r\n    }\r\n}\r\n```\r\n\r\n### Start Tengine\r\n\r\n```\r\n/usr/local/nginx/sbin/nginx\r\n```\r\n\r\nOther Commond (no need execute usual)\r\n```\r\n#restart\r\n/usr/local/nginx/sbin/nginx -s reload\r\n#stop\r\n/usr/local/nginx/sbin/nginx -s stop\r\n```\r\n\r\n### More document\r\n```\r\nhttps://github.com/alibaba/tengine/blob/master/docs/modules/ngx_http_dubbo_module.md\r\nhttps://github.com/alibaba/tengine/blob/master/docs/modules/ngx_http_dubbo_module_cn.md\r\n```\r\n\r\n## Install Dubbo\r\n### Get Dubbo Samples\r\n\r\n```\r\ngit clone https://github.com/apache/dubbo-samples.git\r\n```\r\n\r\n### Build Dubbo Tengine Sample\r\ndepend on ```maven``` and ```jdk8```\r\n\r\n```\r\ncd ./dubbo-samples/dubbo-samples-tengine\r\nmvn package\r\n```\r\n\r\nCentOS maybe need\r\n```\r\nsudo yum install maven\r\n\r\n#or\r\n\r\nwget http://repos.fedorapeople.org/repos/dchen/apache-maven/epel-apache-maven.repo -O /etc/yum.repos.d/epel-apache-maven.repo\r\nsudo yum -y install apache-maven\r\n\r\n\r\nsudo yum install java-1.8.0-openjdk-devel\r\n```\r\n\r\nUbuntu maybe need\r\n```\r\nsudo apt install maven\r\nsudo apt install openjdk-8-jdk-devel\r\n\r\n#some times\r\nsudo apt-get install software-properties-common\r\nsudo add-apt-repository ppa:openjdk-r/ppa\r\nsudo apt-get update\r\nsudo apt-get install openjdk-8-jdk\r\nsudo update-alternatives --config java\r\n```\r\n\r\n### Run Dubbo Demo\r\n```\r\ncd dubbo-samples-tengine-provider/target/\r\njava -Djava.net.preferIPv4Stack=true -jar dubbo-demo-provider.one-jar.jar\r\n```\r\n\r\n\r\n## Do Test\r\n\r\n```\r\ncurl http://127.0.0.1:8080/dubbo -i\r\n```\r\n\r\nLike this\r\n\r\n```\r\ncurl http://127.0.0.1:8080/dubbo -i\r\n\r\nHTTP/1.1 200 OK\r\nServer: Tengine/2.3.1\r\nDate: Thu, 15 Aug 2019 05:42:15 GMT\r\nContent-Type: application/octet-stream\r\nContent-Length: 13\r\nConnection: keep-alive\r\ntest: 123\r\n\r\ndubbo success\r\n```\r\n\r\nThis doc Verified on\r\n```\r\nUbuntu 14.04\r\nUbuntu 16.04\r\nUbuntu 18.04\r\n\r\nCentos 7\r\nCentos 6\r\n```"
  },
  {
    "path": "modules/mod_dubbo/config",
    "content": "ngx_addon_name=ngx_http_dubbo_module\n\nHTTP_DUBBO_DEPS=\" \\\n    $ngx_addon_dir/ngx_http_dubbo_module.h \\\n    $ngx_addon_dir/ngx_dubbo.h\"\n\nHTTP_DUBBO_SRCS=\" \\\n    $ngx_addon_dir/ngx_dubbo_util.cpp \\\n    $ngx_addon_dir/utils/objects.cc \\\n    $ngx_addon_dir/utils/utils.cc \\\n    $ngx_addon_dir/hessian2/hessian2_ext.cc \\\n    $ngx_addon_dir/hessian2/hessian2_input.cc \\\n    $ngx_addon_dir/hessian2/hessian2_output.cc \\\n    $ngx_addon_dir/ngx_dubbo.c \\\n    $ngx_addon_dir/ngx_http_dubbo_module.c\"\n\nngx_module_incs=\" \\\n    $ngx_addon_dir \\\n    $ngx_addon_dir/utils \\\n    $ngx_addon_dir/hessian2\"\n\nCORE_LIBS=\"$CORE_LIBS -lstdc++\"\n\nif test -n \"$ngx_module_link\"; then\n    ngx_module_type=HTTP\n    ngx_module_name=$ngx_addon_name\n    ngx_module_deps=\"$HTTP_DUBBO_DEPS\"\n    ngx_module_srcs=\"$HTTP_DUBBO_SRCS\"\n\n    . auto/module\nelse\n    HTTP_MODULES=\"$HTTP_MODULES $ngx_addon_name\"\n    NGX_ADDON_DEPS=\"$NGX_ADDON_DEPS $HTTP_DUBBO_DEPS\"\n    NGX_ADDON_SRCS=\"$NGX_ADDON_SRCS $HTTP_DUBBO_SRCS\"\n\n    CORE_INCS=\"$CORE_INCS $ngx_module_incs\"\nfi\n\nhave=T_NGX_DUBBO . auto/have\n\n"
  },
  {
    "path": "modules/mod_dubbo/hessian2/hessian2_ext.cc",
    "content": "#include \"hessian2_ext.h\"\n#include \"utils.h\"\n#include \"hessian2_input.h\"\n#include \"hessian2_output.h\"\n#include <memory>\n#include <ostream>\n#include <vector>\n\nnamespace hessian {\n\nusing namespace std;\n\nstatic vector<hessian2_serialize_pt> serializer_registry_hessian2;\n\nvoid hessian2_regist_serializer(uint32_t type_id, hessian2_serialize_pt serializer) {\n    if (type_id >= serializer_registry_hessian2.size()) {\n        serializer_registry_hessian2.resize(type_id + 1);\n    }\n    serializer_registry_hessian2[type_id] = serializer;\n}\n\nhessian2_serialize_pt hessian2_get_serializer(const Object* obj) {\n    return serializer_registry_hessian2[obj ? obj->type_id() : 0];\n}\n\nstatic void null_serialize_hessian2(const Object* obj, hessian2_output& hout) {\n    hout.write_null();\n}\n\nstatic void boolean_serialize_hessian2(const Object* obj, hessian2_output& hout) {\n    hout.write_bool(static_cast<const Boolean*>(obj)->data());\n}\n\nstatic void integer_serialize_hessian2(const Object* obj, hessian2_output& hout) {\n    hout.write_int32(static_cast<const Integer*>(obj)->data());\n}\n\nstatic void long_serialize_hessian2(const Object* obj, hessian2_output& hout) {\n    hout.write_int64(static_cast<const Long*>(obj)->data());\n}\n\nstatic void double_serialize_hessian2(const Object* obj, hessian2_output& hout) {\n    hout.write_double(static_cast<const Double*>(obj)->data());\n}\n\nstatic void date_serialize_hessian2(const Object* obj, hessian2_output& hout) {\n    hout.write_utc_date(static_cast<const Date*>(obj)->data());\n}\n\nstatic void string_serialize_hessian2(const Object* obj, hessian2_output& hout) {\n    const String* str = static_cast<const String*>(obj);\n    hout.write_utf8_string(str->data().c_str(), str->data().size());\n}\n\nstatic void bytearray_serialize_hessian2(const Object* obj, hessian2_output& hout) {\n    const ByteArray* bs = static_cast<const ByteArray*>(obj);\n    hout.write_bytes(bs->data().c_str(), bs->data().size());\n}\n\nstatic void reference_serialize_hessian2(const Object* obj, hessian2_output& hout) {\n    hout.write_object(static_cast<const Reference*>(obj)->data());\n}\n\nstatic void list_serialize_hessian2(const Object* obj, hessian2_output& hout) {\n    const List* list = static_cast<const List*>(obj);\n\n    hout.print_8bit('V');\n    hout.write_type(list->classname() == List::DEFAULT_CLASSNAME ? \"\" : list->classname());\n    hout.write_length(list->size());\n\n    for (List::data_type::const_iterator it = list->data().begin(); it != list->data().end(); ++it) {\n        hout.write_object(*it);\n    }\n\n    hout.print_8bit('z');\n}\n\nstatic void map_serialize_hessian2(const Object* obj, hessian2_output& hout) {\n    const Map* map = static_cast<const Map*>(obj);\n\n    hout.print_8bit('H');\n\n    for (Map::data_type::const_iterator it = map->data().begin(); it != map->data().end(); ++it) {\n        hout.write_object((*it).first);\n        hout.write_object((*it).second);\n    }\n\n    hout.print_8bit('Z');\n}\n\n/* ==================================================================\n * deserializer\n * ================================================================ */\ntypedef vector<pair<string, hessian2_deserialize_pt> > deserializer_registry_hessian2_t;\nstatic deserializer_registry_hessian2_t deserializer_registry_hessian2_strict;\nstatic deserializer_registry_hessian2_t deserializer_registry_hessian2_suffix;\n\nvoid hessian2_regist_deserializer(Object::ObjectType ext_type,\n        const string& classname,\n        hessian2_deserialize_pt deserializer) {\n    if (ext_type != Object::EXT_MAP) {\n        throw io_exception(\"illegal ext_type when registering hessian deserializer of \" + classname);\n    }\n\n    size_t asterisk = classname.find_first_of('*');\n    if (asterisk == string::npos) {\n        deserializer_registry_hessian2_strict.push_back(\n                deserializer_registry_hessian2_t::value_type(classname, deserializer));\n    } else if (asterisk == 0 && classname.size() > 1) {\n        deserializer_registry_hessian2_suffix.push_back(\n                deserializer_registry_hessian2_t::value_type(classname.substr(1), deserializer));\n    } else {\n        throw io_exception(\"illegal classname when registering hessian deserializer of \" + classname);\n    }\n}\n\nhessian2_deserialize_pt hessian2_get_deserializer(Object::ObjectType ext_type, const string& classname) {\n    if (ext_type != Object::EXT_MAP) {\n        return NULL;\n    }\n\n    for (deserializer_registry_hessian2_t::const_iterator it = deserializer_registry_hessian2_strict.begin();\n            it != deserializer_registry_hessian2_strict.end(); ++it) {\n        if (it->first == classname) {\n            return it->second;\n        }\n    }\n    for (deserializer_registry_hessian2_t::const_iterator it = deserializer_registry_hessian2_suffix.begin();\n            it != deserializer_registry_hessian2_suffix.end(); ++it) {\n        if (string_ends_with(classname, it->first)) {\n            return it->second;\n        }\n    }\n\n    return NULL;\n}\n\n/* ==================================================================\n * Java object ext\n * ================================================================ */\nstatic void exception_serialize_hessian2(const Object* obj, hessian2_output& hout) {\n    const Exception* ex = static_cast<const Exception*>(obj);\n\n    hout.print_8bit('M');\n    hout.write_type(ex->classname());\n\n    hout.write_utf8_string(\"detailMessage\");\n    hout.write_utf8_string(ex->detail_message());\n    hout.write_utf8_string(\"cause\");\n    hout.write_object(ex->cause());\n\n    // stack_trace different between C++/Java, not support\n\n    //serialize other\n    for (Map::data_type::const_iterator it = ex->data().begin(); it != ex->data().end(); ++it) {\n        hout.write_object((*it).first);\n        hout.write_object((*it).second);\n    }\n\n    hout.print_8bit('z');\n}\n\nstatic Object* exception_deserialize(const string& type, hessian2_input& hin) {\n    Exception* ex = new Exception(\"\", type);\n    Safeguard<Exception> safeguard(ex);\n    hin.add_ref(ex);\n\n    int tag;\n    while ((tag = hin.peek()) != 'z') {\n        if (tag == 'S' || tag == 's') {\n            string key;\n            hin.read_utf8_string(&key);\n\n            if (key == \"detailMessage\") {\n                // deal with \"String\"\n                hin.read_chunked_utf8_string(ex->mutable_detail_message());\n            } else if (key == \"cause\") {\n                // deal with \"Exception\"\n                pair<Object*, bool> ret = hin.read_object();\n                Object* cause = ret.first;\n                if (pointer_of<Exception>(cause)) {\n                    ex->set_cause((Exception*) cause, ret.second);\n                } else {\n                    throw io_exception(\"can not cast cause to Exception: \" + ex->classname());\n                }\n            } else if (key == \"stackTrace\") {\n                // deal with \"List\"\n                tag = hin.parse_8bit();\n                if (tag == 'V') {\n                    List* stack_trace = new List(hin.read_type());\n                    ex->set_stack_trace(stack_trace, true);\n                    hin.add_ref(stack_trace);\n                    uint32_t st_size = hin.read_length();\n                    if (st_size != 0xFFFFFFFF) {\n                        stack_trace->reserve(st_size);\n                    }\n                    for (uint32_t st_index = 0; st_index < st_size; ++st_index) {\n                        // parse \"StackTraceElement\"\n                        tag = hin.parse_8bit();\n                        if (tag == 'R') {\n                            stack_trace->push_back_ptr(hin.get_ref_object(hin.parse_32bit()), false);\n                            continue;\n                        } else if (tag != 'M') {\n                            throw io_exception(string(\n                                        \"fail to parse field 'stackTrace', expecting 'M' but actually: \")\n                                    .append(1, tag));\n                        }\n                        string* st_element = new string();\n                        String* element = new String(st_element, true, hin.read_type());\n                        stack_trace->push_back_ptr(element, true);\n                        hin.add_ref(element);\n\n                        string declaringClass;\n                        string fileName;\n                        int    lineNumber = -1;\n                        string methodName;\n                        while (hin.peek() != 'z') {\n                            string st_key;\n                            hin.read_utf8_string(&st_key);\n                            if (st_key == \"declaringClass\") {\n                                hin.read_chunked_utf8_string(&declaringClass);\n                            } else if (st_key == \"fileName\") {\n                                hin.read_chunked_utf8_string(&fileName);\n                            } else if (st_key == \"lineNumber\") {\n                                lineNumber = hin.read_int32();\n                            } else if (st_key == \"methodName\") {\n                                hin.read_chunked_utf8_string(&methodName);\n                            } else {\n                                pair<Object*, bool> ret = hin.read_object();\n                                if (ret.second) {\n                                    delete ret.first; // skip other fields\n                                }\n                            }\n                        }\n                        hin.seek(1); // skip 'z' for \"StackTraceElement\" end\n\n                        st_element->append(declaringClass).append(1, '.').append(methodName);\n                        if (lineNumber == -2) {\n                            st_element->append(\"(Native Method)\");\n                        } else if (fileName.empty()) {\n                            st_element->append(\"(Unknown Source)\");\n                        } else {\n                            st_element->append(1, '(').append(fileName);\n                            if (lineNumber >= 0) {\n                                st_element->append(1, ':').append(int32_to_string(lineNumber));\n                            }\n                            st_element->append(1, ')');\n                        }\n                    }\n                    hin.seek(1); // skip 'z' for \"[StackTraceElement\" end\n                } else if (tag != 'N') {\n                    throw io_exception(string(\n                                \"fail to parse field 'stackTrace', encounter value tag: \").append(1, tag));\n                }\n            } else {\n                // for other unknown fields\n                pair<Object*, bool> ret = hin.read_object();\n                ex->put(new String(key), ret.first, true, ret.second);\n            }\n\n        } else {\n            throw io_exception(\n                    \"fail to parse field of class '\" + type +\n                    \"', expecting key tag 'S' but actually met: \" + (char) tag);\n        }\n    }\n\n    hin.seek(1); // skip 'z' for end\n    safeguard.release();\n    return ex;\n}\n\n/* ==================================================================\n *  init\n * ================================================================ */\nstatic struct hessian2_extension_initializer {\n    hessian2_extension_initializer() {\n        serializer_registry_hessian2.resize(300);\n        serializer_registry_hessian2[NullObject::TYPE_ID] = &null_serialize_hessian2;\n        serializer_registry_hessian2[Boolean::TYPE_ID] = &boolean_serialize_hessian2;\n        serializer_registry_hessian2[Integer::TYPE_ID] = &integer_serialize_hessian2;\n        serializer_registry_hessian2[Long::TYPE_ID] = &long_serialize_hessian2;\n        serializer_registry_hessian2[Double::TYPE_ID] = &double_serialize_hessian2;\n        serializer_registry_hessian2[Date::TYPE_ID] = &date_serialize_hessian2;\n        serializer_registry_hessian2[String::TYPE_ID] = &string_serialize_hessian2;\n        serializer_registry_hessian2[ByteArray::TYPE_ID] = &bytearray_serialize_hessian2;\n        serializer_registry_hessian2[Reference::TYPE_ID] = &reference_serialize_hessian2;\n        serializer_registry_hessian2[List::TYPE_ID] = &list_serialize_hessian2;\n        serializer_registry_hessian2[Map::TYPE_ID] = &map_serialize_hessian2;\n        serializer_registry_hessian2[Exception::TYPE_ID] = &exception_serialize_hessian2;\n        hessian2_regist_deserializer(Object::EXT_MAP, \"*Exception\", &exception_deserialize);\n        hessian2_regist_deserializer(Object::EXT_MAP, \"*Error\", &exception_deserialize);\n    }\n} hessian2_extension_init;\n\n}\n"
  },
  {
    "path": "modules/mod_dubbo/hessian2/hessian2_ext.h",
    "content": "#ifndef _HESSIAN2_EXTENSION_H_\n#define _HESSIAN2_EXTENSION_H_\n\n#include \"objects.h\"\n#include <string>\n\nnamespace hessian {\n\nclass hessian2_input;\nclass hessian2_output;\n\ntypedef void (*hessian2_serialize_pt)(const Object* obj, hessian2_output& hout);\n\ntypedef Object* (*hessian2_deserialize_pt)(const std::string& type, hessian2_input& hin);\n\nvoid hessian2_regist_serializer(uint32_t type_id, hessian2_serialize_pt serializer);\n\nvoid hessian2_regist_deserializer(Object::ObjectType ext_type, const std::string& type, hessian2_deserialize_pt deserializer);\n\nhessian2_serialize_pt hessian2_get_serializer(const Object* obj);\n\nhessian2_deserialize_pt hessian2_get_deserializer(Object::ObjectType ext_type, const std::string& type);\n\n}\n#endif\n"
  },
  {
    "path": "modules/mod_dubbo/hessian2/hessian2_input.cc",
    "content": "#include \"hessian2_input.h\"\n#include \"objects.h\"\n#include \"hessian2_ext.h\"\n#include \"utils.h\"\n#include <map>\n#include <sstream>\n#include <memory>\n\nnamespace hessian {\n\nusing namespace std;\n\nhessian2_input::hessian2_input(const std::string* data)\n  : _begin(data->c_str()), _curr(_begin), _end(_begin + data->length()) {}\n\nhessian2_input::hessian2_input(const char* data, uint32_t size)\n  : _begin(data), _curr(data), _end(data + size) {}\n\nvoid hessian2_input::read_null() {\n  uint8_t tag = parse_8bit();\n  if (tag != 'N') {\n    throw expect(\"null\", tag);\n  }\n}\n\nbool hessian2_input::read_bool() {\n    uint8_t tag = parse_8bit();\n    switch(tag) {\n        case 56:\n        case 57:\n        case 58:\n        case 59:\n        case 61:\n        case 62:\n        case 63:\n            parse_8bit();\n            parse_8bit();\n            return true;\n        case 60:\n            return 256 * parse_8bit() + parse_8bit() != 0;\n        case 64:\n        case 65:\n        case 66:\n        case 67:\n        case 69:\n        case 71:\n        case 72:\n        case 74:\n        case 75:\n        case 77:\n        case 79:\n        case 80:\n        case 81:\n        case 82:\n        case 83:\n        case 85:\n        case 86:\n        case 87:\n        case 88:\n        case 90:\n        case 96:\n        case 97:\n        case 98:\n        case 99:\n        case 100:\n        case 101:\n        case 102:\n        case 103:\n        case 104:\n        case 105:\n        case 106:\n        case 107:\n        case 108:\n        case 109:\n        case 110:\n        case 111:\n        case 112:\n        case 113:\n        case 114:\n        case 115:\n        case 116:\n        case 117:\n        case 118:\n        case 119:\n        case 120:\n        case 121:\n        case 122:\n        case 123:\n        case 124:\n        case 125:\n        case 126:\n        case 127:\n        default:\n            throw expect(\"boolean\", tag);\n        case 68:\n            return parse_double() != 0.0;\n        case 70:\n            return false;\n        case 73:\n            return parse_32bit() != 0;\n        case 76:\n            return parse_64bit() != 0;\n        case 78:\n            return false;\n        case 84:\n            return true;\n        case 89:\n            return 16777216 * (long)parse_8bit() + 65536 * (long)parse_8bit() + (long)(256 * parse_8bit()) + (long)parse_8bit() != 0;\n        case 91:\n            return false;\n        case 92:\n            return true;\n        case 93:\n            return parse_8bit() != 0;\n        case 94:\n            return 256 * parse_8bit() + parse_8bit() != 0;\n        case 95:\n            {\n                int mills = parse_32bit();\n                return mills != 0;\n            }\n        case 128:\n        case 129:\n        case 130:\n        case 131:\n        case 132:\n        case 133:\n        case 134:\n        case 135:\n        case 136:\n        case 137:\n        case 138:\n        case 139:\n        case 140:\n        case 141:\n        case 142:\n        case 143:\n        case 144:\n        case 145:\n        case 146:\n        case 147:\n        case 148:\n        case 149:\n        case 150:\n        case 151:\n        case 152:\n        case 153:\n        case 154:\n        case 155:\n        case 156:\n        case 157:\n        case 158:\n        case 159:\n        case 160:\n        case 161:\n        case 162:\n        case 163:\n        case 164:\n        case 165:\n        case 166:\n        case 167:\n        case 168:\n        case 169:\n        case 170:\n        case 171:\n        case 172:\n        case 173:\n        case 174:\n        case 175:\n        case 176:\n        case 177:\n        case 178:\n        case 179:\n        case 180:\n        case 181:\n        case 182:\n        case 183:\n        case 184:\n        case 185:\n        case 186:\n        case 187:\n        case 188:\n        case 189:\n        case 190:\n        case 191:\n            return tag != 144;\n        case 192:\n        case 193:\n        case 194:\n        case 195:\n        case 196:\n        case 197:\n        case 198:\n        case 199:\n        case 201:\n        case 202:\n        case 203:\n        case 204:\n        case 205:\n        case 206:\n        case 207:\n            parse_8bit();\n            return true;\n        case 200:\n            return parse_8bit() != 0;\n        case 208:\n        case 209:\n        case 210:\n        case 211:\n        case 213:\n        case 214:\n        case 215:\n            parse_8bit();\n            parse_8bit();\n            return true;\n        case 212:\n            return 256 * parse_8bit() + parse_8bit() != 0;\n        case 216:\n        case 217:\n        case 218:\n        case 219:\n        case 220:\n        case 221:\n        case 222:\n        case 223:\n        case 224:\n        case 225:\n        case 226:\n        case 227:\n        case 228:\n        case 229:\n        case 230:\n        case 231:\n        case 232:\n        case 233:\n        case 234:\n        case 235:\n        case 236:\n        case 237:\n        case 238:\n        case 239:\n            return tag != 224;\n        case 240:\n        case 241:\n        case 242:\n        case 243:\n        case 244:\n        case 245:\n        case 246:\n        case 247:\n        case 249:\n        case 250:\n        case 251:\n        case 252:\n        case 253:\n        case 254:\n        case 255:\n            parse_8bit();\n            return true;\n        case 248:\n            return parse_8bit() != 0;\n    }\n}\n\nint32_t hessian2_input::read_int32() {\n    uint8_t tag = parse_8bit();\n    switch(tag) {\n        case 56:\n        case 57:\n        case 58:\n        case 59:\n        case 60:\n        case 61:\n        case 62:\n        case 63:\n            return ((tag - 60) << 16) + 256 * parse_8bit() + parse_8bit();\n        case 64:\n        case 65:\n        case 66:\n        case 67:\n        case 69:\n        case 71:\n        case 72:\n        case 74:\n        case 75:\n        case 77:\n        case 79:\n        case 80:\n        case 81:\n        case 82:\n        case 83:\n        case 85:\n        case 86:\n        case 87:\n        case 88:\n        case 90:\n        case 96:\n        case 97:\n        case 98:\n        case 99:\n        case 100:\n        case 101:\n        case 102:\n        case 103:\n        case 104:\n        case 105:\n        case 106:\n        case 107:\n        case 108:\n        case 109:\n        case 110:\n        case 111:\n        case 112:\n        case 113:\n        case 114:\n        case 115:\n        case 116:\n        case 117:\n        case 118:\n        case 119:\n        case 120:\n        case 121:\n        case 122:\n        case 123:\n        case 124:\n        case 125:\n        case 126:\n        case 127:\n        default:\n            throw expect(\"integer\", tag);\n        case 68:\n            return (int)parse_double();\n        case 70:\n            return 0;\n        case 73:\n        case 89:\n            return (parse_8bit() << 24) + (parse_8bit() << 16) + (parse_8bit() << 8) + parse_8bit();\n        case 76:\n            return (int)parse_64bit();\n        case 78:\n            return 0;\n        case 84:\n            return 1;\n        case 91:\n            return 0;\n        case 92:\n            return 1;\n        case 93:\n            return parse_8bit();\n        case 94:\n            return (short)(256 * parse_8bit() + parse_8bit());\n        case 95:\n            {\n                int mills = parse_32bit();\n                return (int)(0.001 * (double)mills);\n            }\n        case 128:\n        case 129:\n        case 130:\n        case 131:\n        case 132:\n        case 133:\n        case 134:\n        case 135:\n        case 136:\n        case 137:\n        case 138:\n        case 139:\n        case 140:\n        case 141:\n        case 142:\n        case 143:\n        case 144:\n        case 145:\n        case 146:\n        case 147:\n        case 148:\n        case 149:\n        case 150:\n        case 151:\n        case 152:\n        case 153:\n        case 154:\n        case 155:\n        case 156:\n        case 157:\n        case 158:\n        case 159:\n        case 160:\n        case 161:\n        case 162:\n        case 163:\n        case 164:\n        case 165:\n        case 166:\n        case 167:\n        case 168:\n        case 169:\n        case 170:\n        case 171:\n        case 172:\n        case 173:\n        case 174:\n        case 175:\n        case 176:\n        case 177:\n        case 178:\n        case 179:\n        case 180:\n        case 181:\n        case 182:\n        case 183:\n        case 184:\n        case 185:\n        case 186:\n        case 187:\n        case 188:\n        case 189:\n        case 190:\n        case 191:\n            return tag - 144;\n        case 192:\n        case 193:\n        case 194:\n        case 195:\n        case 196:\n        case 197:\n        case 198:\n        case 199:\n        case 200:\n        case 201:\n        case 202:\n        case 203:\n        case 204:\n        case 205:\n        case 206:\n        case 207:\n            return ((tag - 200) << 8) + parse_8bit();\n        case 208:\n        case 209:\n        case 210:\n        case 211:\n        case 212:\n        case 213:\n        case 214:\n        case 215:\n            return ((tag - 212) << 16) + 256 * parse_8bit() + parse_8bit();\n        case 216:\n        case 217:\n        case 218:\n        case 219:\n        case 220:\n        case 221:\n        case 222:\n        case 223:\n        case 224:\n        case 225:\n        case 226:\n        case 227:\n        case 228:\n        case 229:\n        case 230:\n        case 231:\n        case 232:\n        case 233:\n        case 234:\n        case 235:\n        case 236:\n        case 237:\n        case 238:\n        case 239:\n            return tag - 224;\n        case 240:\n        case 241:\n        case 242:\n        case 243:\n        case 244:\n        case 245:\n        case 246:\n        case 247:\n        case 248:\n        case 249:\n        case 250:\n        case 251:\n        case 252:\n        case 253:\n        case 254:\n        case 255:\n            return ((tag - 248) << 8) + parse_8bit();\n    }\n}\n\nint64_t hessian2_input::read_int64() {\n    uint8_t tag = parse_8bit();\n    switch(tag) {\n        case 56:\n        case 57:\n        case 58:\n        case 59:\n        case 60:\n        case 61:\n        case 62:\n        case 63:\n            return (int64_t)(((tag - 60) << 16) + 256 * parse_8bit() + parse_8bit());\n        case 64:\n        case 65:\n        case 66:\n        case 67:\n        case 69:\n        case 71:\n        case 72:\n        case 74:\n        case 75:\n        case 77:\n        case 79:\n        case 80:\n        case 81:\n        case 82:\n        case 83:\n        case 85:\n        case 86:\n        case 87:\n        case 88:\n        case 90:\n        case 96:\n        case 97:\n        case 98:\n        case 99:\n        case 100:\n        case 101:\n        case 102:\n        case 103:\n        case 104:\n        case 105:\n        case 106:\n        case 107:\n        case 108:\n        case 109:\n        case 110:\n        case 111:\n        case 112:\n        case 113:\n        case 114:\n        case 115:\n        case 116:\n        case 117:\n        case 118:\n        case 119:\n        case 120:\n        case 121:\n        case 122:\n        case 123:\n        case 124:\n        case 125:\n        case 126:\n        case 127:\n        default:\n            throw expect(\"long\", tag);\n        case 68:\n            return (int64_t)parse_double();\n        case 70:\n            return 0;\n        case 73:\n        case 89:\n            return (int64_t)parse_32bit();\n        case 76:\n            return parse_64bit();\n        case 78:\n            return 0;\n        case 84:\n            return 1;\n        case 91:\n            return 0;\n        case 92:\n            return 1;\n        case 93:\n            return (int64_t)parse_8bit();\n        case 94:\n            return (int64_t)(256 * parse_8bit() + parse_8bit());\n        case 95:\n            {\n                int mills = parse_32bit();\n                return (int64_t)(0.001 * (double)mills);\n            }\n        case 128:\n        case 129:\n        case 130:\n        case 131:\n        case 132:\n        case 133:\n        case 134:\n        case 135:\n        case 136:\n        case 137:\n        case 138:\n        case 139:\n        case 140:\n        case 141:\n        case 142:\n        case 143:\n        case 144:\n        case 145:\n        case 146:\n        case 147:\n        case 148:\n        case 149:\n        case 150:\n        case 151:\n        case 152:\n        case 153:\n        case 154:\n        case 155:\n        case 156:\n        case 157:\n        case 158:\n        case 159:\n        case 160:\n        case 161:\n        case 162:\n        case 163:\n        case 164:\n        case 165:\n        case 166:\n        case 167:\n        case 168:\n        case 169:\n        case 170:\n        case 171:\n        case 172:\n        case 173:\n        case 174:\n        case 175:\n        case 176:\n        case 177:\n        case 178:\n        case 179:\n        case 180:\n        case 181:\n        case 182:\n        case 183:\n        case 184:\n        case 185:\n        case 186:\n        case 187:\n        case 188:\n        case 189:\n        case 190:\n        case 191:\n            return (int64_t)(tag - 144);\n        case 192:\n        case 193:\n        case 194:\n        case 195:\n        case 196:\n        case 197:\n        case 198:\n        case 199:\n        case 200:\n        case 201:\n        case 202:\n        case 203:\n        case 204:\n        case 205:\n        case 206:\n        case 207:\n            return (int64_t)(((tag - 200) << 8) + parse_8bit());\n        case 208:\n        case 209:\n        case 210:\n        case 211:\n        case 212:\n        case 213:\n        case 214:\n        case 215:\n            return (int64_t)(((tag - 212) << 16) + 256 * parse_8bit() + parse_8bit());\n        case 216:\n        case 217:\n        case 218:\n        case 219:\n        case 220:\n        case 221:\n        case 222:\n        case 223:\n        case 224:\n        case 225:\n        case 226:\n        case 227:\n        case 228:\n        case 229:\n        case 230:\n        case 231:\n        case 232:\n        case 233:\n        case 234:\n        case 235:\n        case 236:\n        case 237:\n        case 238:\n        case 239:\n            return (int64_t)(tag - 224);\n        case 240:\n        case 241:\n        case 242:\n        case 243:\n        case 244:\n        case 245:\n        case 246:\n        case 247:\n        case 248:\n        case 249:\n        case 250:\n        case 251:\n        case 252:\n        case 253:\n        case 254:\n        case 255:\n            return (int64_t)(((tag - 248) << 8) + parse_8bit());\n    }\n}\n\ndouble hessian2_input::read_double() {\n    uint8_t tag = parse_8bit();\n    switch(tag) {\n        case 56:\n        case 57:\n        case 58:\n        case 59:\n        case 60:\n        case 61:\n        case 62:\n        case 63:\n            return (double)(((tag - 60) << 16) + 256 * parse_8bit() + parse_8bit());\n        case 64:\n        case 65:\n        case 66:\n        case 67:\n        case 69:\n        case 71:\n        case 72:\n        case 74:\n        case 75:\n        case 77:\n        case 79:\n        case 80:\n        case 81:\n        case 82:\n        case 83:\n        case 85:\n        case 86:\n        case 87:\n        case 88:\n        case 90:\n        case 96:\n        case 97:\n        case 98:\n        case 99:\n        case 100:\n        case 101:\n        case 102:\n        case 103:\n        case 104:\n        case 105:\n        case 106:\n        case 107:\n        case 108:\n        case 109:\n        case 110:\n        case 111:\n        case 112:\n        case 113:\n        case 114:\n        case 115:\n        case 116:\n        case 117:\n        case 118:\n        case 119:\n        case 120:\n        case 121:\n        case 122:\n        case 123:\n        case 124:\n        case 125:\n        case 126:\n        case 127:\n        default:\n            throw expect(\"double\", tag);\n        case 68:\n            return parse_double();\n        case 70:\n            return 0.0;\n        case 73:\n        case 89:\n            return (double)parse_double();\n        case 76:\n            return (double)parse_64bit();\n        case 78:\n            return 0.0;\n        case 84:\n            return 1.0;\n        case 91:\n            return 0.0;\n        case 92:\n            return 1.0;\n        case 93:\n            return (double)parse_8bit();\n        case 94:\n            return (double)(256 * parse_8bit() + parse_8bit());\n        case 95:\n            {\n                int mills = parse_32bit();\n                return 0.001 * (double)mills;\n            }\n        case 128:\n        case 129:\n        case 130:\n        case 131:\n        case 132:\n        case 133:\n        case 134:\n        case 135:\n        case 136:\n        case 137:\n        case 138:\n        case 139:\n        case 140:\n        case 141:\n        case 142:\n        case 143:\n        case 144:\n        case 145:\n        case 146:\n        case 147:\n        case 148:\n        case 149:\n        case 150:\n        case 151:\n        case 152:\n        case 153:\n        case 154:\n        case 155:\n        case 156:\n        case 157:\n        case 158:\n        case 159:\n        case 160:\n        case 161:\n        case 162:\n        case 163:\n        case 164:\n        case 165:\n        case 166:\n        case 167:\n        case 168:\n        case 169:\n        case 170:\n        case 171:\n        case 172:\n        case 173:\n        case 174:\n        case 175:\n        case 176:\n        case 177:\n        case 178:\n        case 179:\n        case 180:\n        case 181:\n        case 182:\n        case 183:\n        case 184:\n        case 185:\n        case 186:\n        case 187:\n        case 188:\n        case 189:\n        case 190:\n        case 191:\n            return (double)(tag - 144);\n        case 192:\n        case 193:\n        case 194:\n        case 195:\n        case 196:\n        case 197:\n        case 198:\n        case 199:\n        case 200:\n        case 201:\n        case 202:\n        case 203:\n        case 204:\n        case 205:\n        case 206:\n        case 207:\n            return (double)(((tag - 200) << 8) + parse_8bit());\n        case 208:\n        case 209:\n        case 210:\n        case 211:\n        case 212:\n        case 213:\n        case 214:\n        case 215:\n            return (double)(((tag - 212) << 16) + 256 * parse_8bit() + parse_8bit());\n        case 216:\n        case 217:\n        case 218:\n        case 219:\n        case 220:\n        case 221:\n        case 222:\n        case 223:\n        case 224:\n        case 225:\n        case 226:\n        case 227:\n        case 228:\n        case 229:\n        case 230:\n        case 231:\n        case 232:\n        case 233:\n        case 234:\n        case 235:\n        case 236:\n        case 237:\n        case 238:\n        case 239:\n            return (double)(tag - 224);\n        case 240:\n        case 241:\n        case 242:\n        case 243:\n        case 244:\n        case 245:\n        case 246:\n        case 247:\n        case 248:\n        case 249:\n        case 250:\n        case 251:\n        case 252:\n        case 253:\n        case 254:\n        case 255:\n            return (double)(((tag - 248) << 8) + parse_8bit());\n    }\n}\n\nint64_t hessian2_input::read_utc_date() {\n    uint8_t tag = parse_8bit();\n\n    if(tag == 'J') {\n        return parse_64bit();\n    } else if(tag == 'K') {\n        return (int64_t)parse_32bit() * 60000;\n    } else {\n        throw expect(\"date\", tag);\n    }\n}\n\nstd::string* hessian2_input::read_utf8_string(std::string *dest)\n{\n    Safeguard<string> safeguard(dest);\n    if (dest == NULL) {\n        dest = new string();\n        safeguard.reset(dest);\n    }\n    uint8_t tag = parse_8bit();\n    switch(tag) {\n        case 0:\n        case 1:\n        case 2:\n        case 3:\n        case 4:\n        case 5:\n        case 6:\n        case 7:\n        case 8:\n        case 9:\n        case 10:\n        case 11:\n        case 12:\n        case 13:\n        case 14:\n        case 15:\n        case 16:\n        case 17:\n        case 18:\n        case 19:\n        case 20:\n        case 21:\n        case 22:\n        case 23:\n        case 24:\n        case 25:\n        case 26:\n        case 27:\n        case 28:\n        case 29:\n        case 30:\n        case 31:\n            {\n                uint16_t len = tag - 0;\n                parse_utf8_string(len, dest);\n                safeguard.release();\n                return dest;\n            }\n        case 32:\n        case 33:\n        case 34:\n        case 35:\n        case 36:\n        case 37:\n        case 38:\n        case 39:\n        case 40:\n        case 41:\n        case 42:\n        case 43:\n        case 44:\n        case 45:\n        case 46:\n        case 47:\n        case 52:\n        case 53:\n        case 54:\n        case 55:\n        case 64:\n        case 65:\n        case 66:\n        case 67:\n        case 69:\n        case 71:\n        case 72:\n        case 74:\n        case 75:\n        case 77:\n        case 79:\n        case 80:\n        case 81:\n        case 85:\n        case 86:\n        case 87:\n        case 88:\n        case 90:\n        case 96:\n        case 97:\n        case 98:\n        case 99:\n        case 100:\n        case 101:\n        case 102:\n        case 103:\n        case 104:\n        case 105:\n        case 106:\n        case 107:\n        case 108:\n        case 109:\n        case 110:\n        case 111:\n        case 112:\n        case 113:\n        case 114:\n        case 115:\n        case 116:\n        case 117:\n        case 118:\n        case 119:\n        case 120:\n        case 121:\n        case 122:\n        case 123:\n        case 124:\n        case 125:\n        case 126:\n        case 127:\n        default:\n            throw expect(\"string\", tag);\n        case 48:\n        case 49:\n        case 50:\n        case 51:\n            {\n                uint16_t len = (tag - 48) * 256 + parse_8bit();\n                parse_utf8_string(len, dest);\n                safeguard.release();\n                return dest;\n            }\n        case 56:\n        case 57:\n        case 58:\n        case 59:\n        case 60:\n        case 61:\n        case 62:\n        case 63:\n            return new string(int32_to_string(((tag - 60) << 16) + 256 * parse_8bit() + parse_8bit()));\n        case 68:\n            return new string(double_to_string(parse_double()));\n        case 70:\n            return new string(\"false\");\n        case 73:\n        case 89:\n            return new string(int32_to_string(parse_32bit()));\n        case 76:\n            return new string(int32_to_string(parse_64bit()));\n        case 78:\n            return NULL;\n        case 82:\n            --_curr;\n            return read_chunked_utf8_string();\n        case 83:\n            {\n                uint16_t len = parse_16bit();\n                parse_utf8_string(len, dest);\n                safeguard.release();\n                return dest;\n\n            }\n        case 84:\n            return new string(\"true\");\n        case 91:\n            return new string(\"0.0\");\n        case 92:\n            return new string(\"1.0\");\n        case 93:\n            return new string(int32_to_string(parse_8bit()));\n        case 94:\n            return new string(int32_to_string(256 * parse_8bit() + parse_8bit()));\n        case 95:\n            {\n                int ch = parse_32bit();\n                return new string(double_to_string(0.001 * (double)ch));\n            }\n        case 128:\n        case 129:\n        case 130:\n        case 131:\n        case 132:\n        case 133:\n        case 134:\n        case 135:\n        case 136:\n        case 137:\n        case 138:\n        case 139:\n        case 140:\n        case 141:\n        case 142:\n        case 143:\n        case 144:\n        case 145:\n        case 146:\n        case 147:\n        case 148:\n        case 149:\n        case 150:\n        case 151:\n        case 152:\n        case 153:\n        case 154:\n        case 155:\n        case 156:\n        case 157:\n        case 158:\n        case 159:\n        case 160:\n        case 161:\n        case 162:\n        case 163:\n        case 164:\n        case 165:\n        case 166:\n        case 167:\n        case 168:\n        case 169:\n        case 170:\n        case 171:\n        case 172:\n        case 173:\n        case 174:\n        case 175:\n        case 176:\n        case 177:\n        case 178:\n        case 179:\n        case 180:\n        case 181:\n        case 182:\n        case 183:\n        case 184:\n        case 185:\n        case 186:\n        case 187:\n        case 188:\n        case 189:\n        case 190:\n        case 191:\n            return new string(int32_to_string(tag - 144));\n        case 192:\n        case 193:\n        case 194:\n        case 195:\n        case 196:\n        case 197:\n        case 198:\n        case 199:\n        case 200:\n        case 201:\n        case 202:\n        case 203:\n        case 204:\n        case 205:\n        case 206:\n        case 207:\n            return new string(int32_to_string(((tag - 200) << 8) + parse_8bit()));\n        case 208:\n        case 209:\n        case 210:\n        case 211:\n        case 212:\n        case 213:\n        case 214:\n        case 215:\n            return new string(int32_to_string(((tag - 212) << 16) + 256 * parse_8bit() + parse_8bit()));\n        case 216:\n        case 217:\n        case 218:\n        case 219:\n        case 220:\n        case 221:\n        case 222:\n        case 223:\n        case 224:\n        case 225:\n        case 226:\n        case 227:\n        case 228:\n        case 229:\n        case 230:\n        case 231:\n        case 232:\n        case 233:\n        case 234:\n        case 235:\n        case 236:\n        case 237:\n        case 238:\n        case 239:\n            return new string(int32_to_string(tag - 224));\n        case 240:\n        case 241:\n        case 242:\n        case 243:\n        case 244:\n        case 245:\n        case 246:\n        case 247:\n        case 248:\n        case 249:\n        case 250:\n        case 251:\n        case 252:\n        case 253:\n        case 254:\n        case 255:\n            return new string(int32_to_string(((tag - 248) << 8) + parse_8bit()));\n    }\n}\n\nstring* hessian2_input::read_chunked_utf8_string(string* dest) {\n    uint8_t tag = parse_8bit();\n\n    if (tag == 'N') {\n        return dest;\n    }\n\n    if (dest == NULL) {\n        dest = new string();\n    }\n\n    Safeguard<string> safeguard(dest);\n\n    while (tag == 'R') {\n        uint16_t char_size = parse_16bit();\n        parse_utf8_string(char_size, dest);\n        tag = parse_8bit();\n    }\n\n    switch(tag) {\n        case 0:\n        case 1:\n        case 2:\n        case 3:\n        case 4:\n        case 5:\n        case 6:\n        case 7:\n        case 8:\n        case 9:\n        case 10:\n        case 11:\n        case 12:\n        case 13:\n        case 14:\n        case 15:\n        case 16:\n        case 17:\n        case 18:\n        case 19:\n        case 20:\n        case 21:\n        case 22:\n        case 23:\n        case 24:\n        case 25:\n        case 26:\n        case 27:\n        case 28:\n        case 29:\n        case 30:\n        case 31:\n            {\n                uint16_t char_size = tag - 0;\n                parse_utf8_string(char_size, dest);\n                safeguard.release();\n                return dest;\n            }\n        case 32:\n        case 33:\n        case 34:\n        case 35:\n        case 36:\n        case 37:\n        case 38:\n        case 39:\n        case 40:\n        case 41:\n        case 42:\n        case 43:\n        case 44:\n        case 45:\n        case 46:\n        case 47:\n        case 52:\n        case 53:\n        case 54:\n        case 55:\n        case 56:\n        case 57:\n        case 58:\n        case 59:\n        case 60:\n        case 61:\n        case 62:\n        case 63:\n        case 64:\n        case 65:\n        case 66:\n        case 67:\n        case 68:\n        case 69:\n        case 70:\n        case 71:\n        case 72:\n        case 73:\n        case 74:\n        case 75:\n        case 76:\n        case 77:\n        case 78:\n        case 79:\n        case 80:\n        case 81:\n        default:\n            throw expect(\"string\", tag);\n        case 48:\n        case 49:\n        case 50:\n        case 51:\n            {\n                uint16_t char_size = ((tag - 48) << 8) + parse_8bit();\n                parse_utf8_string(char_size, dest);\n                safeguard.release();\n                return dest;\n            }\n        case 83:\n            {\n                uint16_t char_size = parse_16bit();\n                parse_utf8_string(char_size, dest);\n                safeguard.release();\n                return dest;\n            }\n    }\n}\n\nstring* hessian2_input::read_bytes() {\n    string *dest = new string();\n\n    Safeguard<string> safeguard(dest);\n\n    uint8_t tag = parse_8bit();\n    int len;\n    switch(tag) {\n        case 32:\n        case 33:\n        case 34:\n        case 35:\n        case 36:\n        case 37:\n        case 38:\n        case 39:\n        case 40:\n        case 41:\n        case 42:\n        case 43:\n        case 44:\n        case 45:\n        case 46:\n        case 47:\n            len = tag - 32;\n            parse_raw_bytes(len, dest);\n            safeguard.release();\n            return dest;\n        case 48:\n        case 49:\n        case 50:\n        case 51:\n        case 56:\n        case 57:\n        case 58:\n        case 59:\n        case 60:\n        case 61:\n        case 62:\n        case 63:\n        case 64:\n        case 67:\n        case 68:\n        case 69:\n        case 70:\n        case 71:\n        case 72:\n        case 73:\n        case 74:\n        case 75:\n        case 76:\n        case 77:\n        default:\n            throw expect(\"bytes\", tag);\n        case 52:\n        case 53:\n        case 54:\n        case 55:\n            len = (tag - 52) * 256 + parse_8bit();\n            parse_raw_bytes(len, dest);\n            safeguard.release();\n            return dest;\n        case 65:\n            --_curr;\n            safeguard.release();\n            return read_chunked_bytes(dest);\n        case 66:\n            {\n                uint16_t size = parse_16bit();\n                parse_raw_bytes(size, dest);\n                safeguard.release();\n                return dest;\n            }\n        case 78:\n            return NULL;\n    }\n    return NULL;\n}\n\nstring* hessian2_input::read_chunked_bytes(string* dest) {\n    uint8_t tag = parse_8bit();\n\n    if (tag == 'N') {\n        return dest;\n    }\n\n    if (dest == NULL) {\n        dest = new string();\n    }\n    Safeguard<string> safeguard(dest);\n\n    while (tag == 'A') {\n        uint16_t byte_size = parse_16bit();\n        parse_raw_bytes(byte_size, dest);\n        tag = parse_8bit();\n    }\n\n    switch(tag) {\n        case 32:\n        case 33:\n        case 34:\n        case 35:\n        case 36:\n        case 37:\n        case 38:\n        case 39:\n        case 40:\n        case 41:\n        case 42:\n        case 43:\n        case 44:\n        case 45:\n        case 46:\n        case 47:\n            {\n                uint16_t size = tag - 32;\n                parse_raw_bytes(size, dest);\n                safeguard.release();\n                return dest;\n            }\n        case 48:\n        case 49:\n        case 50:\n        case 51:\n        case 56:\n        case 57:\n        case 58:\n        case 59:\n        case 60:\n        case 61:\n        case 62:\n        case 63:\n        case 64:\n        default:\n            throw expect(\"byte[]\", tag);\n        case 52:\n        case 53:\n        case 54:\n        case 55:\n            {\n                uint16_t size = (tag - 52) * 256 + parse_8bit();\n                parse_raw_bytes(size, dest);\n                safeguard.release();\n                return dest;\n            }\n        case 66:\n            {\n                uint16_t size = parse_16bit();\n                parse_raw_bytes(size, dest);\n                safeguard.release();\n                return dest;\n            }\n    }\n\n    return NULL;\n}\n\nuint32_t hessian2_input::read_length() {\n    throw io_exception(\"type is unsupported for the moment\");\n}\n\nstring hessian2_input::read_type() {\n    throw io_exception(\"type is unsupported for the moment\");\n}\n\nObject* hessian2_input::read_list(const string& classname) {\n    throw io_exception(\"list is unsupported for the moment\");\n}\n\nObject* hessian2_input::read_map(const string& classname) {\n    uint8_t tag = parse_8bit();\n    string type;\n\n    switch (tag) {\n        case 'H':\n            break;\n        case 'M':\n            {\n                string *tmp = read_utf8_string();\n                type = *tmp;\n                delete tmp;\n                if (type.empty()) {\n                    type = classname.empty() ? Map::DEFAULT_CLASSNAME : classname;\n                }\n            }\n            break;\n        case 'N':\n            return NULL;\n        //case 'R': return get_ref_object(parse_32bit());\n        default:\n            throw expect(\"map\", tag);\n    }\n\n    hessian2_deserialize_pt ext = hessian2_get_deserializer(Object::EXT_MAP, type);\n\n    if (ext) {\n        return ext(type, *this);\n    } else {\n        Map* map = new Map(type);\n        Safeguard<Map> safeguard(map);\n        add_ref(map);\n\n        while ((tag = peek()) != 'Z') {\n            pair<Object*, bool> ret_key = read_object();\n            pair<Object*, bool> ret_val = read_object();\n            map->put(ret_key.first, ret_val.first, ret_key.second, ret_val.second);\n        }\n\n        ++_curr;\n        safeguard.release();\n        return map;\n    }\n}\n\npair<Object*, bool> hessian2_input::read_object() {\n    uint8_t tag = peek();\n\n    switch(tag) {\n        case 0:\n        case 1:\n        case 2:\n        case 3:\n        case 4:\n        case 5:\n        case 6:\n        case 7:\n        case 8:\n        case 9:\n        case 10:\n        case 11:\n        case 12:\n        case 13:\n        case 14:\n        case 15:\n        case 16:\n        case 17:\n        case 18:\n        case 19:\n        case 20:\n        case 21:\n        case 22:\n        case 23:\n        case 24:\n        case 25:\n        case 26:\n        case 27:\n        case 28:\n        case 29:\n        case 30:\n        case 31:\n            return pair<Object*, bool>(new String(read_utf8_string(), true), true);\n        case 32:\n        case 33:\n        case 34:\n        case 35:\n        case 36:\n        case 37:\n        case 38:\n        case 39:\n        case 40:\n        case 41:\n        case 42:\n        case 43:\n        case 44:\n        case 45:\n        case 46:\n        case 47:\n            return pair<Object*, bool>(new ByteArray(read_bytes(), true), true);\n        case 48:\n        case 49:\n        case 50:\n        case 51:\n            return pair<Object*, bool>(new String(read_utf8_string(), true), true);\n        case 52:\n        case 53:\n        case 54:\n        case 55:\n            return pair<Object*, bool>(new ByteArray(read_bytes(), true), true);\n        case 56:\n        case 57:\n        case 58:\n        case 59:\n        case 60:\n        case 61:\n        case 62:\n        case 63:\n            return pair<Object*, bool>(new Long(read_int64()), true);\n        case 64:\n        case 69:\n        case 71:\n        case 80:\n        case 90:\n        default:\n            throw expect(\"readObject: unknown code \", tag);\n        case 65:\n        case 66:\n            return pair<Object*, bool>(new ByteArray(read_bytes(), true), true);\n        case 67:\n            throw io_exception(\"unsupported type readObjectDefinition\");\n        case 68:\n            return pair<Object*, bool>(new Double(read_double()), true);\n        case 70:\n            return pair<Object*, bool>(new Boolean(read_bool()), true);\n        case 72:\n            return pair<Object*, bool>(read_map(), true);\n        case 73:\n            return pair<Object*, bool>(new Integer(read_int32()), true);\n        case 74:\n        case 75:\n            return pair<Object*, bool>(new Date(read_utc_date()), true);\n        case 76:\n            return pair<Object*, bool>(new Long(read_int64()), true);\n        case 77:\n            return pair<Object*, bool>(read_map(), true);\n        case 78:\n            return pair<Object*, bool>(NULL, true);\n        case 79:\n            throw io_exception(\"unsupported readObjectInstance\");\n        case 81:\n            return pair<Object*, bool>(read_ref(), true);\n        case 82:\n        case 83:\n            return pair<Object*, bool>(new String(read_utf8_string(), true), true);\n        case 84:\n            return pair<Object*, bool>(new Boolean(read_bool()), true);\n        case 85:\n            return pair<Object*, bool>(read_list(), true);\n        case 86:\n            return pair<Object*, bool>(read_list(), true);\n        case 87:\n            return pair<Object*, bool>(read_list(), true);\n        case 88:\n            return pair<Object*, bool>(read_list(), true);\n        case 89:\n            return pair<Object*, bool>(new Long(read_int64()), true);\n        case 91:\n            return pair<Object*, bool>(new Double(read_double()), true);\n        case 92:\n            return pair<Object*, bool>(new Double(read_double()), true);\n        case 93:\n            return pair<Object*, bool>(new Double(read_double()), true);\n        case 94:\n            return pair<Object*, bool>(new Double(read_double()), true);\n        case 95:\n            return pair<Object*, bool>(new Double(read_double()), true);\n        case 96:\n        case 97:\n        case 98:\n        case 99:\n        case 100:\n        case 101:\n        case 102:\n        case 103:\n        case 104:\n        case 105:\n        case 106:\n        case 107:\n        case 108:\n        case 109:\n        case 110:\n        case 111:\n            throw io_exception(\"unsupported readObjectInstance\");\n        case 112:\n        case 113:\n        case 114:\n        case 115:\n        case 116:\n        case 117:\n        case 118:\n        case 119:\n            return pair<Object*, bool>(read_list(), true);\n        case 120:\n        case 121:\n        case 122:\n        case 123:\n        case 124:\n        case 125:\n        case 126:\n        case 127:\n            return pair<Object*, bool>(read_list(), true);\n        case 128:\n        case 129:\n        case 130:\n        case 131:\n        case 132:\n        case 133:\n        case 134:\n        case 135:\n        case 136:\n        case 137:\n        case 138:\n        case 139:\n        case 140:\n        case 141:\n        case 142:\n        case 143:\n        case 144:\n        case 145:\n        case 146:\n        case 147:\n        case 148:\n        case 149:\n        case 150:\n        case 151:\n        case 152:\n        case 153:\n        case 154:\n        case 155:\n        case 156:\n        case 157:\n        case 158:\n        case 159:\n        case 160:\n        case 161:\n        case 162:\n        case 163:\n        case 164:\n        case 165:\n        case 166:\n        case 167:\n        case 168:\n        case 169:\n        case 170:\n        case 171:\n        case 172:\n        case 173:\n        case 174:\n        case 175:\n        case 176:\n        case 177:\n        case 178:\n        case 179:\n        case 180:\n        case 181:\n        case 182:\n        case 183:\n        case 184:\n        case 185:\n        case 186:\n        case 187:\n        case 188:\n        case 189:\n        case 190:\n        case 191:\n            return pair<Object*, bool>(new Integer(read_int32()), true);\n        case 192:\n        case 193:\n        case 194:\n        case 195:\n        case 196:\n        case 197:\n        case 198:\n        case 199:\n        case 200:\n        case 201:\n        case 202:\n        case 203:\n        case 204:\n        case 205:\n        case 206:\n        case 207:\n            return pair<Object*, bool>(new Integer(read_int32()), true);\n        case 208:\n        case 209:\n        case 210:\n        case 211:\n        case 212:\n        case 213:\n        case 214:\n        case 215:\n            return pair<Object*, bool>(new Integer(read_int32()), true);\n        case 216:\n        case 217:\n        case 218:\n        case 219:\n        case 220:\n        case 221:\n        case 222:\n        case 223:\n        case 224:\n        case 225:\n        case 226:\n        case 227:\n        case 228:\n        case 229:\n        case 230:\n        case 231:\n        case 232:\n        case 233:\n        case 234:\n        case 235:\n        case 236:\n        case 237:\n        case 238:\n        case 239:\n            return pair<Object*, bool>(new Long(read_int64()), true);\n        case 240:\n        case 241:\n        case 242:\n        case 243:\n        case 244:\n        case 245:\n        case 246:\n        case 247:\n        case 248:\n        case 249:\n        case 250:\n        case 251:\n        case 252:\n        case 253:\n        case 254:\n        case 255:\n            return pair<Object*, bool>(new Long(read_int64()), true);\n    }\n\n}\n\npair<Object*, bool> hessian2_input::read_object(const string& classname) {\n    throw io_exception(\"object is unsupported for the moment\");\n}\n\nint32_t hessian2_input::add_ref(Object* object) {\n    _refs_list.push_back(object);\n    return _refs_list.size();\n}\n\nObject* hessian2_input::read_ref() {\n    return get_ref_object(parse_32bit());\n}\n\nObject* hessian2_input::get_ref_object(uint32_t ref_id) {\n    if (ref_id >= _refs_list.size()) {\n        ostringstream oss;\n        oss << \"the given reference (ref_id=\" << ref_id\n            << \") is not in the _refs_list (size=\" << _refs_list.size() << \")\";\n        throw error(oss.str());\n    }\n    return _refs_list[ref_id];\n}\n\ndouble hessian2_input::parse_double() {\n    return long_to_double(parse_64bit());\n}\n\nvoid hessian2_input::parse_utf8_string(uint32_t char_size, string* dest) {\n    if (_curr + char_size - 1 >= _end) {\n        throw error(\"hessian2_input::parse_utf8_string(): will reach EOF\");\n    }\n\n    dest->reserve(dest->size() + char_size);\n\n    while (char_size--) {\n        if (eof()) {\n            throw error(\"hessian2_input::parse_utf8_string(): reached EOF\");\n        }\n\n        int ch = (uint8_t) *_curr++;\n\n        if (ch < 0x80) {\n            dest->push_back(ch);\n        } else if ((ch & 0xe0) == 0xc0) {\n            dest->push_back(ch);\n            dest->push_back(*_curr++);\n        } else if ((ch & 0xf0) == 0xe0) {\n            dest->push_back(ch);\n            dest->push_back(*_curr++);\n            dest->push_back(*_curr++);\n        } else {\n            throw error(\"bad utf-8 encoding\");\n        }\n    }\n}\n\nvoid hessian2_input::skip_object() {\n    pair<Object*, bool> ret = read_object();\n    if (ret.second) {\n        delete ret.first;\n    }\n}\n\nio_exception hessian2_input::expect(const string& expect, int ch) {\n    ostringstream oss;\n    oss << \"expected \" << expect;\n    if (ch < 0) {\n        oss << \" but reached end of file\";\n    } else {\n        oss << \" but actually met \" << hex << showbase << ch;\n    }\n    oss << \" near position \" << (uintptr_t)(_curr - _begin);\n    return io_exception(oss.str());\n}\n\nio_exception hessian2_input::error(const string& expect) {\n    ostringstream oss;\n    oss << \"error: \" << expect << \" near position \" << (uintptr_t)(_curr - _begin);\n    return io_exception(oss.str());\n}\n\n}\n"
  },
  {
    "path": "modules/mod_dubbo/hessian2/hessian2_input.h",
    "content": "#ifndef _HESSIAN2_INPUT_H_\n#define _HESSIAN2_INPUT_H_\n\n#include \"exceptions.h\"\n#include \"utils.h\"\n#include <stdint.h>\n#include <string>\n#include <vector>\n\nnamespace hessian {\n\nclass Object;\n\n/**\n * Hessian2 deserializater\n * Decode hessian from input data\n */\nclass hessian2_input {\n    public:\n        hessian2_input(const std::string* data);\n        hessian2_input(const char* data, uint32_t size);\n        ~hessian2_input() {}\n\n        bool eof() const { return _curr >= _end; }\n\n        //Clear refs, but not clear data\n        void clear() { _refs_list.clear(); }\n\n        // ---------------------------------------------------------\n\n        void read_null();\n\n        bool read_bool();\n\n        int32_t read_int32();\n        int64_t read_int64();\n\n        double read_double();\n\n        int64_t read_utc_date();\n\n        std::string* read_utf8_string(std::string* dest = NULL);\n        std::string* read_chunked_utf8_string(std::string* dest = NULL);\n        std::string* read_string();\n\n        std::string* read_bytes();\n        std::string* read_chunked_bytes(std::string* dest = NULL);\n\n        uint32_t read_length();\n        std::string read_type();\n\n        Object* read_list(const std::string& classname = \"\");\n        Object* read_map(const std::string& classname = \"\");\n\n        // Need do delete when the second param return is true !\n        std::pair<Object*, bool> read_object();\n        std::pair<Object*, bool> read_object(const std::string& classname);\n\n        int32_t add_ref(Object* object = NULL);\n        Object* read_ref();\n        Object* get_ref_object(uint32_t ref_id);\n\n        /* --------------------------------------------------------- *\n         * Low level functions\n         * --------------------------------------------------------- */\n        uint32_t current_position() const { return _curr - _begin; }\n        const char* current_ptr() const { return _curr; }\n        void seek(uint32_t offset) { _curr += offset; }\n\n        uint8_t peek();\n        uint8_t parse_8bit();\n        uint16_t parse_16bit();\n        uint32_t parse_32bit();\n        uint64_t parse_64bit();\n\n        double parse_double();\n\n        void parse_raw_bytes(uint32_t byte_size, std::string* dest);\n\n        void parse_utf8_string(uint32_t char_size, std::string* dest);\n\n        void skip_object();\n\n    private:\n        io_exception expect(const std::string& expect, int ch);\n        io_exception error(const std::string& expect);\n\n    private:\n        const char* _begin;\n        const char* _curr;\n        const char* _end;\n\n        std::vector<Object*> _refs_list;\n};\n\ninline uint8_t hessian2_input::peek() {\n    if (_curr >= _end) {\n        throw io_exception(\"hessian2_input::peek(): will reach EOF\");\n    }\n    return *_curr;\n}\n\ninline uint8_t hessian2_input::parse_8bit() {\n    if (_curr >= _end) {\n        throw io_exception(\"hessian2_input::read_8bit(): will reach EOF\");\n    }\n    return *_curr++;\n}\n\ninline uint16_t hessian2_input::parse_16bit() {\n    if (_curr + 1 >= _end) {\n        throw io_exception(\"hessian2_input::read_16bit(): will reach EOF\");\n    }\n    uint16_t ret = ntohs(*((uint16_t *)(_curr)));\n    _curr += 2;\n    return ret;\n}\n\ninline uint32_t hessian2_input::parse_32bit() {\n    if (_curr + 3 >= _end) {\n        throw io_exception(\"hessian2_input::read_32bit(): will reach EOF\");\n    }\n    uint32_t ret = ntohl(*((uint32_t *)(_curr)));\n    _curr += 4;\n    return ret;\n}\n\ninline uint64_t hessian2_input::parse_64bit() {\n    if (_curr + 7 >= _end) {\n        throw io_exception(\"hessian2_input::read_64bit(): will reach EOF\");\n    }\n    uint64_t ret = ngx_hessian_ntoh64(*((uint64_t *)(_curr)));\n    _curr += 8;\n    return ret;\n}\n\ninline void hessian2_input::parse_raw_bytes(uint32_t byte_size, std::string* dest) {\n    if (_curr + byte_size - 1 >= _end) {\n        throw io_exception(\"hessian2_input::read_raw_bytes(): will reach EOF\");\n    }\n    dest->append(_curr, byte_size);\n    _curr += byte_size;\n}\n\n}\n\n#endif\n"
  },
  {
    "path": "modules/mod_dubbo/hessian2/hessian2_output.cc",
    "content": "#include \"hessian2_output.h\"\n#include \"hessian2_ext.h\"\n#include \"objects.h\"\n#include \"utils.h\"\n\nnamespace hessian {\n\nusing namespace std;\n\nvoid hessian2_output::clear() {\n    _refs_map.clear();\n    _ref_idx = 0;\n}\n\n/*\n *            # null value\n * null       ::= 'N'\n */\nvoid hessian2_output::write_null() {\n    print_8bit('N');\n}\n\n/*\n *            # boolean true/false\n * boolean    ::= 'T'\n *            ::= 'F'\n */\nvoid hessian2_output::write_bool(bool b) {\n    print_8bit(b ? 'T' : 'F');\n}\n\n\n/*\n *           # 32-bit signed integer\n * int       ::= 'I' b3 b2 b1 b0\n *           ::= [x80-xbf]             # -x10 to x3f\n *           ::= [xc0-xcf] b0          # -x800 to x7ff\n *           ::= [xd0-xd7] b1 b0       # -x40000 to x3ffff\n */\nvoid hessian2_output::write_int32(int32_t value) {\n    //just use ::= 'I' b3 b2 b1 b0\n    print_8bit('I');\n    print_32bit(value);\n}\n\n/*\n *            # 64-bit signed long integer\n * long       ::= 'L' b7 b6 b5 b4 b3 b2 b1 b0\n *            ::= [xd8-xef]             # -x08 to x0f\n *            ::= [xf0-xff] b0          # -x800 to x7ff\n *            ::= [x38-x3f] b1 b0       # -x40000 to x3ffff\n *            ::= x59 b3 b2 b1 b0       # 32-bit integer cast to long\n */\nvoid hessian2_output::write_int64(int64_t value) {\n    //just use ::= 'L' b7 b6 b5 b4 b3 b2 b1 b0\n    print_8bit('L');\n    print_64bit(value);\n}\n\n/*\n *            # 64-bit IEEE double\n * double     ::= 'D' b7 b6 b5 b4 b3 b2 b1 b0\n *            ::= x5b                   # 0.0\n *            ::= x5c                   # 1.0\n *            ::= x5d b0                # byte cast to double\n *                                      #  (-128.0 to 127.0)\n *            ::= x5e b1 b0             # short cast to double\n *            ::= x5f b3 b2 b1 b0       # 32-bit float cast to double\n */\nvoid hessian2_output::write_double(double d_val) {\n    //just use ::= 'D' b7 b6 b5 b4 b3 b2 b1 b0\n    print_8bit('D');\n    print_64bit(double_to_long(d_val));\n}\n\n/*\n *            # time in UTC encoded as 64-bit long milliseconds since\n *            #  epoch\n * date       ::= x4a b7 b6 b5 b4 b3 b2 b1 b0\n *            ::= x4b b3 b2 b1 b0       # minutes since epoch\n */\nvoid hessian2_output::write_utc_date(int64_t milli_epoch) {\n    //just use ::= x4a b7 b6 b5 b4 b3 b2 b1 b0\n    print_8bit('J');\n    print_64bit(milli_epoch);\n}\n\n\n/*\n *            # UTF-8 encoded character string split into 64k chunks\n * string     ::= x52 b1 b0 <utf8-data> string  # non-final chunk\n *            ::= 'S' b1 b0 <utf8-data>         # string of length\n *                                              #  0-65535\n *            ::= [x00-x1f] <utf8-data>         # string of length\n *                                              #  0-31\n *            ::= [x30-x34] <utf8-data>         # string of length\n */\nvoid hessian2_output::write_utf8_string(const char* str, uint32_t byte_size) {\n    //hessian2 use utf8 charset standard\n\n    if (str == NULL || byte_size == 0) {\n        write_null();\n        return;\n    }\n\n    // begin new chunk\n    uint32_t patch_pos = _data->length();\n    _data->reserve(patch_pos + byte_size + byte_size / 10240 + 3);\n\n    uint32_t len = 0, last = 0;\n    const char *cur = str;\n    size_t i;\n    const uint32_t max_chunk_byte_size = 0x8000;\n\n    for (i = 0; i< byte_size; ) {\n        len++;\n        if (str[i] & 0x80) {\n            // more than one byte for this char\n            if ((str[i] & 0xe0) == 0xc0) {\n                i += 2;\n            } else if ((str[i] & 0xf0) == 0xe0) {\n                i += 3;\n            } else {\n                i += 4;\n            }\n        } else {\n            i++;\n        }\n\n        if (len >= max_chunk_byte_size) {\n            print_8bit('R');\n            print_8bit(len >> 8);\n            print_8bit(len);\n\n            _data->append(cur, i - last);\n\n            len = 0;\n            cur = str + i;\n            last = i;\n        }\n    }\n\n    if(len <= 0) {\n        return;\n    } else if (len <= 31) {\n        print_8bit(len);\n    } else if(len <= 1023) {\n        print_8bit((char)(48 + (len >> 8)));\n        print_8bit((char)len);\n    } else {\n        print_8bit('S');\n        print_8bit(len >> 8);\n        print_8bit(len);\n    }\n\n    _data->append(cur, i - last);\n}\n\n/*\n *\n *            # 8-bit binary data split into 64k chunks\n * binary     ::= x41 b1 b0 <binary-data> binary # non-final chunk\n *            ::= 'B' b1 b0 <binary-data>        # final chunk\n *            ::= [x20-x2f] <binary-data>        # binary data of\n *                                                 #  length 0-15\n *            ::= [x34-x37] <binary-data>        # binary data of\n *                                                 #  length 0-1023\n */\nvoid hessian2_output::write_bytes(const char* bytes, uint32_t byte_size) {\n\n    const uint32_t max_chunk_byte_size = 0x8000;\n\n    if (bytes == NULL || byte_size == 0) {\n        write_null();\n        return;\n    }\n\n\n    _data->reserve(_data->size() + byte_size / 10240 + 3);\n\n    for ( ; ; ) {\n        if (byte_size > max_chunk_byte_size) {\n            print_8bit('A');\n            print_8bit(max_chunk_byte_size >> 8);\n            print_8bit((uint8_t)max_chunk_byte_size);\n            print_raw_bytes(bytes, max_chunk_byte_size);\n            bytes += max_chunk_byte_size;\n\n            byte_size -= max_chunk_byte_size;\n        } else {\n            break;\n        }\n    }\n\n    if (byte_size <= 15 ) {\n        print_8bit(32 + byte_size);\n    } else if (byte_size <= 1023) {\n        print_8bit(52 + (byte_size >> 8));\n        print_8bit(byte_size);\n    } else {\n        print_8bit('B');\n        print_8bit(byte_size >> 8);\n        print_8bit(byte_size);\n    }\n\n    print_raw_bytes(bytes, byte_size);\n}\n\nvoid hessian2_output::write_utf8_string(const string& str) {\n    write_utf8_string(str.c_str(), str.size());\n}\n\nbool hessian2_output::write_ref(const Object* object) {\n    if (object == NULL || object->type_id() == Object::NULL_OBJECT) {\n        hessian2_output::write_null();\n        return true;\n    }\n\n    pair<map<uintptr_t, int32_t>::iterator, bool> ret = _refs_map.insert\n        (pair<uintptr_t, int32_t>((uintptr_t) object, _ref_idx));\n\n    if (ret.second == false) {\n        // ref already existed, write as a reference\n        write_ref(ret.first->second);\n        return true;\n    } else {\n        ++_ref_idx;\n        return false;\n    }\n}\n\nvoid hessian2_output::write_length(uint32_t length) {\n    print_8bit('l');\n    print_32bit(length);\n}\n\nvoid hessian2_output::write_type(const std::string& type) {\n    print_8bit('t');\n    if (type.empty()) {\n        print_raw_len_bytes(\"\", 0);\n    } else {\n        print_raw_len_bytes(type.c_str(), type.size());\n    }\n}\n\n/*\n *            # value reference (e.g. circular trees and graphs)\n * ref        ::= x51 int            # reference to nth map/list/object\n */\nvoid hessian2_output::write_ref(int32_t ref_id) {\n    print_8bit('Q');\n    print_32bit(ref_id);\n}\n\nvoid hessian2_output::write_object(const Object* object) {\n    if (object == NULL) {\n        hessian2_output::write_null();\n    } else if (object->type_id() <= Object::WEAK_REF || !write_ref(object)) {\n        hessian2_serialize_pt hs = hessian2_get_serializer(object);\n        if (hs) {\n            hs(object, *this);\n        } else {\n            throw io_exception(\"serializer not found for object \" + object->classname());\n        }\n    }\n}\n\n}\n\n"
  },
  {
    "path": "modules/mod_dubbo/hessian2/hessian2_output.h",
    "content": "#ifndef _HESSIAN2_OUTPUT_H_\n#define _HESSIAN2_OUTPUT_H_\n\n#include \"utils.h\"\n#include <stdint.h>\n#include <string>\n#include <map>\n\nnamespace hessian {\n\nclass Object;\n\nclass hessian2_output {\n    public:\n        hessian2_output(std::string* data) : _data(data), _ref_idx(0) {}\n        ~hessian2_output() {}\n\n        std::string* data() { return _data; }\n        uint32_t size() const { return _data->size(); }\n\n        void clear();\n\n        // ---------------------------------------------------------\n\n        void write_null();\n\n        void write_bool(bool b);\n\n        void write_int32(int32_t value);\n        void write_int64(int64_t value);\n\n        void write_double(double d_val);\n\n        void write_utc_date(int64_t milli_epoch);\n\n        void write_utf8_string(const char* str, uint32_t byte_size);\n        void write_utf8_string(const std::string& str);\n\n        void write_bytes(const char* bytes, uint32_t byte_size);\n\n        void write_length(uint32_t length);\n        void write_type(const std::string& type);\n\n        void write_ref(int32_t ref_id);\n        bool write_ref(const Object* object);\n        void write_object(const Object* object);\n\n        /* --------------------------------------------------------- *\n         * Low level functions\n         * --------------------------------------------------------- */\n        int32_t add_ref() { return _ref_idx++; }\n\n        void print_8bit(int8_t value) {\n            _data->push_back((char) value);\n        }\n        void print_16bit(int16_t value) {\n            _data->append((const char*)&(value = htons((uint16_t)value)), 2);\n        }\n        void print_32bit(int32_t value) {\n            _data->append((const char*)&(value = htonl((uint32_t)value)), 4);\n        }\n        void print_64bit(int64_t value) {\n            _data->append((const char*)&(value = ngx_hessian_hton64((uint64_t)value)), 8);\n        }\n\n        void fill_chars(uint32_t size, char c) {\n            _data->append(size, c);\n        }\n\n        void print_raw_bytes(const char* bytes, uint32_t byte_size) {\n            _data->append(bytes, byte_size);\n        }\n\n        void print_raw_len_bytes(const char* bytes, uint16_t byte_size) {\n            print_16bit(byte_size);\n            print_raw_bytes(bytes, byte_size);\n        }\n\n        uint32_t current_position() const { return _data->length(); }\n        void print_8bit_at_position(uint32_t pos, char value);\n        void print_16bit_at_position(uint32_t pos, int16_t value);\n        void print_32bit_at_position(uint32_t pos, int32_t value);\n        void print_64bit_at_position(uint32_t pos, int64_t value);\n\n    private:\n        std::string* _data;\n        std::map<uintptr_t, int32_t> _refs_map;\n        int32_t _ref_idx;\n};\n\ninline void hessian2_output::print_8bit_at_position(uint32_t pos, char value) {\n    (*_data)[pos] = value;\n}\ninline void hessian2_output::print_16bit_at_position(uint32_t pos, int16_t value) {\n    (*_data)[  pos] = (char) (value >> 8);\n    (*_data)[++pos] = (char) value;\n}\ninline void hessian2_output::print_32bit_at_position(uint32_t pos, int32_t value) {\n    (*_data)[  pos] = (char) (value >> 24);\n    (*_data)[++pos] = (char) (value >> 16);\n    (*_data)[++pos] = (char) (value >> 8);\n    (*_data)[++pos] = (char) value;\n}\ninline void hessian2_output::print_64bit_at_position(uint32_t pos, int64_t value) {\n    (*_data)[  pos] = (char) (value >> 56);\n    (*_data)[++pos] = (char) (value >> 48);\n    (*_data)[++pos] = (char) (value >> 40);\n    (*_data)[++pos] = (char) (value >> 32);\n    (*_data)[++pos] = (char) (value >> 24);\n    (*_data)[++pos] = (char) (value >> 16);\n    (*_data)[++pos] = (char) (value >> 8);\n    (*_data)[++pos] = (char) value;\n}\n\n}\n\n#endif\n"
  },
  {
    "path": "modules/mod_dubbo/ngx_dubbo.c",
    "content": "\n/*\n * Copyright (C) Mengqi Wu (Pull)\n * Copyright (C) 2018-2019 Alibaba Group Holding Limited\n */\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n#include <ngx_dubbo.h>\n\ntypedef struct {\n    ngx_dubbo_arg_type_t    type;\n    ngx_str_t               name;\n} ngx_dubbo_arg_type_value_t;\n\nngx_dubbo_arg_type_value_t ngx_dubbo_arg_type_map[] = {\n    {\n        DUBBO_ARG_STR,\n        ngx_string(\"Ljava/lang/String;\")\n    },\n    {\n        DUBBO_ARG_INT,\n        ngx_string(\"I\")\n    },\n    {\n        DUBBO_ARG_LSTR,\n        ngx_string(\"Ljava/util/List;\")\n    },\n    {\n        DUBBO_ARG_MAP,\n        ngx_string(\"Ljava/util/Map;\")\n    }\n};\n\nint ngx_dubbo_is_big_endian() {\n    const int n = 1;\n    if(*(char *)&n) {\n        return 0;\n    }\n    return 1;\n}\n\nstatic const u_char DUBBO_VERSION_ENCODE[] = { 0x05, 0x32, 0x2e, 0x30, 0x2e, 0x32 };            //0x05\"2.0.2\"\nstatic const u_char DUBBO_SERVICE_VERSION_ENCODE[] = { 0x05, 0x30, 0x2e, 0x30, 0x2e, 0x30 };   //0x05\"0.0.0\"\n\nstatic const u_char DUBBO_NULL[] = { 0x4e };                                                    //\"N\" null\nstatic const u_char DUBBO_FLAG_REQ_HESSIAN2 = 0xc2;                                             // 0b11000010  req & hessian2\nstatic const u_char DUBBO_FLAG_REQ_PING_HESSIAN2 = 0xe2;                                        // 0b11000010  req & ping & hessian2\n\nstatic ngx_int_t ngx_dubbo_get_request_props(ngx_pool_t *pool, ngx_str_t *props);\n\nngx_int_t \nngx_dubbo_encode_request(ngx_dubbo_connection_t *dubbo_c, ngx_str_t *service_name, ngx_str_t *service_version, ngx_str_t *method_name, ngx_array_t *args, ngx_multi_request_t *multi_r)\n{\n    size_t                   len, i, arg_len = 0;\n    ngx_str_t               *args_encode;\n    ngx_dubbo_arg_t         *arg;\n    uint32_t                 tmp32;\n    uint64_t                 tmp64;\n    ngx_buf_t               *b;\n    u_char                  *p;\n\n    ngx_str_t                service_name_encode;\n    ngx_str_t                service_version_encode;\n    ngx_str_t                method_name_encode;\n    ngx_str_t                arg_types;\n    ngx_str_t                arg_types_encode;\n\n    ngx_str_t                props;\n\n    //calc buf len\n    len = sizeof(ngx_dubbo_header_t);\n\n    len += sizeof(DUBBO_VERSION_ENCODE);\n\n    //service_name\n    if (NGX_OK != ngx_dubbo_hessian2_encode_str(dubbo_c->temp_pool, service_name, &service_name_encode)) {\n        ngx_log_error(NGX_LOG_ERR, dubbo_c->log, 0, \"dubbo: encode hessian2 str failed %V\", service_name);\n        return NGX_ERROR;\n    }\n\n    len += service_name_encode.len;\n\n    //service version\n    if (NGX_OK != ngx_dubbo_hessian2_encode_str(dubbo_c->temp_pool, service_version, &service_version_encode)) {\n        ngx_log_error(NGX_LOG_ERR, dubbo_c->log, 0, \"dubbo: encode hessian2 str failed %V\", service_version);\n        return NGX_ERROR;\n    }\n\n    len += service_version_encode.len;\n\n    //method_name\n    if (NGX_OK != ngx_dubbo_hessian2_encode_str(dubbo_c->temp_pool, method_name, &method_name_encode)) {\n        ngx_log_error(NGX_LOG_ERR, dubbo_c->log, 0, \"dubbo: encode hessian2 str failed %V\", method_name);\n        return NGX_ERROR;\n    }\n\n    len += method_name_encode.len;\n\n    arg = args->elts;\n    args_encode = ngx_pcalloc(dubbo_c->temp_pool, sizeof(ngx_str_t) * args->nelts);\n    for (i=0; i<args->nelts; i++) {\n        arg_len += ngx_dubbo_arg_type_map[arg[i].type].name.len;\n\n        switch(arg[i].type) {\n        case DUBBO_ARG_STR:\n            if (NGX_OK != ngx_dubbo_hessian2_encode_str(dubbo_c->temp_pool, &arg[i].value.str, &args_encode[i])) {\n                ngx_log_error(NGX_LOG_ERR, dubbo_c->log, 0, \"dubbo: encode hessian2 str failed\");\n                return NGX_ERROR;\n            }\n            break;\n#if 0\n        case DUBBO_ARG_INT:\n            if (NGX_OK != ngx_dubbo_hessian2_encode_int(dubbo_c->temp_pool, arg[i].value.n, &args_encode[i])) {\n                ngx_log_error(NGX_LOG_ERR, dubbo_c->log, 0, \"dubbo: encode hessian2 str int failed\");\n                return NGX_ERROR;\n            }\n            break;\n\n        case DUBBO_ARG_LSTR:\n            if (NGX_OK != ngx_dubbo_hessian2encode_lstr(dubbo_c->temp_pool, arg[i].value.pstr, &args_encode[i])) {\n                ngx_log_error(NGX_LOG_ERR, dubbo_c->log, 0, \"dubbo: encode hessian2 str list failed\");\n                return NGX_ERROR;\n            }\n            break;\n#endif\n        case DUBBO_ARG_MAP:\n            if (NGX_OK != ngx_dubbo_hessian2_encode_payload_map(dubbo_c->temp_pool, arg[i].value.m, &args_encode[i])) {\n                ngx_log_error(NGX_LOG_ERR, dubbo_c->log, 0, \"dubbo: encode hessian2 map failed\");\n                return NGX_ERROR;\n            }\n            break;\n        default:\n            ngx_log_error(NGX_LOG_ERR, dubbo_c->log, 0, \"dubbo: args param type unknown %d\", arg[i].type);\n            return NGX_ERROR;\n        }\n\n        len += args_encode[i].len;\n    }\n\n    arg_types.data = ngx_pcalloc(dubbo_c->temp_pool, arg_len);\n    if (arg_types.data == NULL) {\n        return NGX_ERROR;\n    }\n    p = arg_types.data;\n    arg_types.len = arg_len;\n    for (i=0; i<args->nelts; i++) {\n        ngx_memcpy(p, ngx_dubbo_arg_type_map[arg[i].type].name.data, ngx_dubbo_arg_type_map[arg[i].type].name.len);\n        p += ngx_dubbo_arg_type_map[arg[i].type].name.len;\n    }\n\n    if (NGX_OK != ngx_dubbo_hessian2_encode_str(dubbo_c->temp_pool, &arg_types, &arg_types_encode)) {\n        ngx_log_error(NGX_LOG_ERR, dubbo_c->log, 0, \"dubbo: encode hessian2 str failed\");\n        return NGX_ERROR;\n    }\n\n    len += arg_types_encode.len;\n\n    if (NGX_OK != ngx_dubbo_get_request_props(dubbo_c->temp_pool, &props)) {\n        ngx_log_error(NGX_LOG_ERR, dubbo_c->log, 0, \"encode props failed\");\n        return NGX_ERROR;\n    }\n\n    len += props.len;\n\n    multi_r->out = ngx_alloc_chain_link(multi_r->pool);\n    if (multi_r->out == NULL) {\n        return NGX_ERROR;\n    }\n    \n    multi_r->out->buf = ngx_create_temp_buf(multi_r->pool, len);\n    multi_r->out->next = NULL;\n    b = multi_r->out->buf;\n    if (b == NULL) {\n        return NGX_ERROR;\n    }\n\n    //fixed header\n    b->pos = b->start;\n\n    b->pos[0] = MAGIC_VALUE_0;              //magic\n    b->pos[1] = MAGIC_VALUE_1;              //version\n    b->pos[2] = DUBBO_FLAG_REQ_HESSIAN2;    //req & hessian2\n    b->pos[3] = 0;\n    b->last += 4;\n\n    //request id\n    dubbo_c->last_request_id++;\n    tmp64 = ngx_dubbo_hton64(dubbo_c->last_request_id);\n    multi_r->id = dubbo_c->last_request_id;\n    memcpy(b->last, &tmp64, 8);\n    b->last += 8;\n\n    //payload len\n    tmp32 = htonl(len - sizeof(ngx_dubbo_header_t));\n    memcpy(b->last, &tmp32, 4);\n    b->last += 4;\n\n    //dubbo version\n    memcpy(b->last, DUBBO_VERSION_ENCODE, sizeof(DUBBO_VERSION_ENCODE));\n    b->last += sizeof(DUBBO_VERSION_ENCODE);\n\n    //service name\n    memcpy(b->last, service_name_encode.data, service_name_encode.len);\n    b->last += service_name_encode.len;\n\n    //service version\n    memcpy(b->last, DUBBO_SERVICE_VERSION_ENCODE, sizeof(DUBBO_VERSION_ENCODE));\n    b->last += sizeof(DUBBO_SERVICE_VERSION_ENCODE);\n\n    //method name\n    memcpy(b->last, method_name_encode.data, method_name_encode.len);\n    b->last += method_name_encode.len;\n\n    //arg types\n    memcpy(b->last, arg_types_encode.data, arg_types_encode.len);\n    b->last += arg_types_encode.len;\n\n    for (i=0; i<args->nelts; i++) {\n        memcpy(b->last, args_encode[i].data, args_encode[i].len);\n        b->last += args_encode[i].len;\n    }\n\n    //props\n    memcpy(b->last, props.data, props.len);\n    b->last += props.len;\n\n    ngx_reset_pool(dubbo_c->temp_pool);\n    return NGX_OK;\n}\n\nstatic ngx_int_t\nngx_dubbo_get_request_props(ngx_pool_t *pool, ngx_str_t *props) \n{\n    //no need attachments, just use null\n    props->data = (u_char*)DUBBO_NULL;\n    props->len = sizeof(DUBBO_NULL);\n\n    return NGX_OK;\n}\n\nngx_int_t\nngx_dubbo_encode_ping_request(ngx_dubbo_connection_t *dubbo_c, ngx_multi_request_t *multi_r)\n{\n    size_t                   len;\n    uint32_t                 tmp32;\n    uint64_t                 tmp64;\n    ngx_buf_t               *b;\n\n    //calc buf len\n    len = sizeof(ngx_dubbo_header_t);\n\n    len += sizeof(DUBBO_NULL);\n\n    multi_r->out = ngx_alloc_chain_link(multi_r->pool);\n    if (multi_r->out == NULL) {\n        return NGX_ERROR;\n    }\n\n    multi_r->out->buf = ngx_create_temp_buf(multi_r->pool, len);\n    multi_r->out->next = NULL;\n    b = multi_r->out->buf;\n    if (b == NULL) {\n        return NGX_ERROR;\n    }\n\n    //fixed header\n    b->pos = b->start;\n\n    b->pos[0] = MAGIC_VALUE_0;                    //magic\n    b->pos[1] = MAGIC_VALUE_1;                    //version\n    b->pos[2] = DUBBO_FLAG_REQ_PING_HESSIAN2;     //req & ping & hessian2\n    b->pos[3] = 0;\n    b->last += 4;\n\n    //request id\n    dubbo_c->last_request_id++;\n    tmp64 = ngx_dubbo_hton64(dubbo_c->last_request_id);\n    multi_r->id = dubbo_c->last_request_id;\n    memcpy(b->last, &tmp64, 8);\n    b->last += 8;\n\n    //payload len\n    tmp32 = htonl(len - sizeof(ngx_dubbo_header_t));\n    memcpy(b->last, &tmp32, 4);\n    b->last += 4;\n\n    //null\n    memcpy(b->last, DUBBO_NULL, sizeof(DUBBO_NULL));\n    b->last += sizeof(DUBBO_NULL);\n\n    return NGX_OK;\n}\n\nstatic ngx_int_t\nngx_dubbo_copy_chain(void *dst, ngx_chain_t *src, size_t len)\n{\n    ngx_chain_t         *cl;\n\n    for (cl = src; cl; cl = cl->next) {\n        if (len <= (size_t)ngx_buf_size(cl->buf)) {\n            ngx_memcpy(dst, cl->buf->pos, len);\n            cl->buf->pos += len;\n\n            return NGX_OK;\n        } else {\n            ngx_memcpy(dst, cl->buf->pos, ngx_buf_size(cl->buf));\n            len -= ngx_buf_size(cl->buf);\n            cl->buf->last = cl->buf->pos;\n        }\n    }\n\n    return NGX_AGAIN;\n}\n\nngx_int_t\nngx_dubbo_decode_response(ngx_dubbo_connection_t *dubbo_c, ngx_chain_t *in)\n{\n    ngx_chain_t             *cl;\n    size_t                   len = 0;\n    ngx_dubbo_resp_t        *resp = &dubbo_c->resp;\n    u_char                  *dst;\n\n    //get size first\n    for (cl = in; cl; cl = cl->next) {\n        len += ngx_buf_size(cl->buf);\n    }\n\n    if (len == 0) {\n        return NGX_AGAIN;\n    }\n\n    for ( ; ; ) {\n        switch (dubbo_c->parse_state) {\n            case DUBBO_PARSE_READ_HEADER:\n                dst = ((u_char*)&resp->header) + dubbo_c->remain;\n                if ((len + dubbo_c->remain) >= sizeof(ngx_dubbo_header_t)) {\n                    ngx_dubbo_copy_chain(dst, in, sizeof(ngx_dubbo_header_t) - dubbo_c->remain);\n                    len -= sizeof(ngx_dubbo_header_t) - dubbo_c->remain;\n\n                    dubbo_c->remain = 0;\n\n                    resp->header.payloadlen = htonl(resp->header.payloadlen);\n                    resp->header.reqid = ngx_dubbo_hton64(resp->header.reqid);\n\n                    dubbo_c->parse_state = DUBBO_PARSE_READ_PAYLOAD;\n                } else {\n                    if (len) {\n                        ngx_dubbo_copy_chain(dst, in, len);\n                        dubbo_c->remain += len;\n                    }\n\n                    return NGX_AGAIN;\n                }\n\n                break;\n            case DUBBO_PARSE_READ_PAYLOAD:\n                if (resp->header.payloadlen > resp->payload_alloc || resp->payload == NULL) {\n                    if (resp->payload != NULL) {\n                        ngx_free(resp->payload);\n                    }\n\n                    resp->payload = ngx_alloc(resp->header.payloadlen, dubbo_c->log);\n                    if (resp->payload == NULL) {\n                        return NGX_ERROR;\n                    }\n                    resp->payload_alloc = resp->header.payloadlen;\n                }\n\n                dst = ((u_char*)resp->payload) + dubbo_c->remain;\n                if ((len + dubbo_c->remain) >= resp->header.payloadlen) {\n                    ngx_dubbo_copy_chain(dst, in, resp->header.payloadlen - dubbo_c->remain);\n                    len -= resp->header.payloadlen - dubbo_c->remain;\n\n                    dubbo_c->remain = 0;\n\n                    dubbo_c->parse_state = DUBBO_PARSE_READ_HEADER;\n\n                    return NGX_DONE;\n                } else {\n                    if (len) {\n                        ngx_dubbo_copy_chain(dst, in, len);\n                        dubbo_c->remain += len;\n                    }\n\n                    return NGX_AGAIN;\n                }\n\n                break;\n            default:\n                return NGX_ERROR;\n        }\n    }\n\n    return NGX_ERROR;\n}\n\nngx_dubbo_connection_t*\nngx_dubbo_create_connection(ngx_connection_t *c, ngx_event_handler_pt ping_handler)\n{\n    ngx_dubbo_connection_t        *dubbo_c;\n\n    dubbo_c = ngx_palloc(c->pool, sizeof(ngx_dubbo_connection_t));\n    if (dubbo_c == NULL) {\n        return NULL;\n    }\n\n    if (NGX_OK == ngx_dubbo_init_connection(dubbo_c, c, ping_handler)) {\n        return dubbo_c;\n    }\n\n    return NULL;\n}\n\nstatic void\nngx_dubbo_cleanup(void *data)\n{\n    ngx_dubbo_connection_t      *dubbo_c = data;\n\n    if (dubbo_c->resp.payload != NULL) {\n        ngx_free(dubbo_c->resp.payload);\n        dubbo_c->resp.payload = NULL;\n        dubbo_c->resp.payload_alloc = 0;\n    }\n\n    ngx_destroy_pool(dubbo_c->temp_pool);\n\n    if (dubbo_c->ping_event.timer_set) {\n        ngx_del_timer(&dubbo_c->ping_event);\n    }\n}\n\nngx_int_t\nngx_dubbo_init_connection(ngx_dubbo_connection_t *dubbo_c, ngx_connection_t *c, ngx_event_handler_pt ping_handler)\n{\n    ngx_pool_cleanup_t      *cln;\n\n    ngx_memzero(dubbo_c, sizeof(ngx_dubbo_connection_t));\n\n    dubbo_c->log = c->log;\n    dubbo_c->data = (void*)c;\n\n    dubbo_c->last_request_id = 1;\n\n    dubbo_c->temp_pool = ngx_create_pool(4096, c->log);\n    if (dubbo_c->temp_pool == NULL) {\n        return NGX_ERROR;\n    }\n\n    cln = ngx_pool_cleanup_add(c->pool, 0);\n    if (cln == NULL) {\n        return NGX_ERROR;\n    }\n\n    cln->handler = ngx_dubbo_cleanup;\n    cln->data = dubbo_c;\n\n    dubbo_c->ping_event.handler = ping_handler;\n    dubbo_c->ping_event.data = c;\n    dubbo_c->ping_event.log = c->log;\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "modules/mod_dubbo/ngx_dubbo.h",
    "content": "\n/*\n * Copyright (C) Mengqi Wu (Pull)\n * Copyright (C) 2017-2019 Alibaba Group Holding Limited\n */\n\n#ifndef _NGX_DUBBO_H_\n#define _NGX_DUBBO_H_\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n#include <ngx_event_connect.h>\n#include \"ngx_multi_upstream_module.h\"\n\nint ngx_dubbo_is_big_endian();\n\n#define ngx_dubbo_swap64(val) (((val) >> 56)   |\\\n        (((val) & 0x00ff000000000000ll) >> 40) |\\\n        (((val) & 0x0000ff0000000000ll) >> 24) |\\\n        (((val) & 0x000000ff00000000ll) >> 8)  |\\\n        (((val) & 0x00000000ff000000ll) << 8)  |\\\n        (((val) & 0x0000000000ff0000ll) << 24) |\\\n        (((val) & 0x000000000000ff00ll) << 40) |\\\n        (((val) << 56)))\n\n#define ngx_dubbo_hton64(val) ngx_dubbo_is_big_endian() ? val : ngx_dubbo_swap64(val)\n#define ngx_dubbo_ntoh64(val) ngx_dubbo_hton64(val)\n\n#if (NGX_HAVE_PACK_PRAGMA)\n#pragma pack(push, 1)\n#elif (NGX_SOLARIS)\n#pragma pack(1)\n#else\n#error \"dubbo module needs structure packing pragma support\"\n#endif\n\ntypedef struct\n{\n    u_char magic_0;\n    u_char magic_1;\n    u_char type;\n    u_char status;\n\n    uint64_t reqid;\n\n    uint32_t payloadlen;\n} ngx_dubbo_header_t;\n\ntypedef struct {\n    ngx_dubbo_header_t header;\n\n    u_char *payload;\n} ngx_dubbo_req_t;\n\ntypedef struct {\n    ngx_dubbo_header_t header;\n\n    u_char*  payload;\n    size_t   payload_alloc; \n} ngx_dubbo_resp_t;\n\n#if (NGX_HAVE_PACK_PRAGMA)\n#pragma pack(pop)\n#elif (NGX_SOLARIS)\n#pragma pack()\n#else\n#error \"dubbo module needs structure packing pragma support\"\n#endif\n\n#define MAGIC_VALUE_0                 0xda\n#define MAGIC_VALUE_1                 0xbb\n#define DUBBO_FLAG_REQ                0x80\n#define DUBBO_FLAG_TWOWAY             0x40\n#define DUBBO_FLAG_PING               0x20\n\ntypedef enum {\n    DUBBO_PARSE_READ_HEADER = 0,\n    DUBBO_PARSE_READ_PAYLOAD = 1,\n} ngx_dubbo_parse_state_t;\n\ntypedef struct {\n    ngx_pool_t                     *temp_pool;\n\n\n    ngx_log_t                      *log;\n\n    void*                          *data;\n\n    ngx_int_t                       last_request_id;\n\n    ngx_flag_t                      read_header;\n    ngx_dubbo_resp_t                resp;\n\n    ngx_dubbo_parse_state_t         parse_state;\n    size_t                          remain;\n\n    ngx_event_t                     ping_event;\n} ngx_dubbo_connection_t;\n\ntypedef enum {\n    DUBBO_ARG_STR=0,\n    DUBBO_ARG_INT,\n    DUBBO_ARG_LSTR,\n    DUBBO_ARG_MAP,\n\n    DUBBO_ARG_MAX\n} ngx_dubbo_arg_type_t;\n\ntypedef struct {\n    ngx_dubbo_arg_type_t type;\n\n    union ngx_dubbo_value_t {\n        int          n;\n        ngx_str_t    str;\n        ngx_array_t *pstr;\n        ngx_array_t *m;\n    } value;\n} ngx_dubbo_arg_t;\n\nngx_int_t ngx_dubbo_encode_request(ngx_dubbo_connection_t *dubbo_c, ngx_str_t *service_name, ngx_str_t *service_version, ngx_str_t *method_name, ngx_array_t *args, ngx_multi_request_t *multi_r);\nngx_int_t ngx_dubbo_encode_ping_request(ngx_dubbo_connection_t *dubbo_c, ngx_multi_request_t *multi_r);\nngx_int_t ngx_dubbo_decode_response(ngx_dubbo_connection_t *dubbo_c, ngx_chain_t *in);\n\nngx_int_t ngx_dubbo_hessian2_encode_str(ngx_pool_t *pool, ngx_str_t *in, ngx_str_t *out);\nngx_int_t ngx_dubbo_hessian2_encode_int(ngx_pool_t *pool, int n, ngx_str_t *out);\nngx_int_t ngx_dubbo_hessian2_encode_lstr(ngx_pool_t *pool, ngx_array_t *lstr, ngx_str_t *out);\nngx_int_t ngx_dubbo_hessian2_encode_map(ngx_pool_t *pool, ngx_array_t *in, ngx_str_t *out);\n\nngx_int_t ngx_dubbo_hessian2_decode_str(ngx_pool_t *pool, ngx_str_t *in, ngx_str_t *result, ngx_log_t *log);\nngx_int_t ngx_dubbo_hessian2_decode_payload_map(ngx_pool_t *pool, ngx_str_t *in, ngx_array_t **result, ngx_log_t *log);\nngx_int_t ngx_dubbo_hessian2_encode_payload_map(ngx_pool_t *pool, ngx_array_t *in, ngx_str_t *out);\n\nngx_dubbo_connection_t* ngx_dubbo_create_connection(ngx_connection_t *c, ngx_event_handler_pt ping_handler);\nngx_int_t ngx_dubbo_init_connection(ngx_dubbo_connection_t *dubbo_c, ngx_connection_t *c, ngx_event_handler_pt ping_handler);\n\n#endif /* _NGX_DUBBO_H_ */\n"
  },
  {
    "path": "modules/mod_dubbo/ngx_dubbo_util.cpp",
    "content": "\n/*\n * Copyright (C) Mengqi Wu (Pull)\n * Copyright (C) 2017-2019 Alibaba Group Holding Limited\n */\n\n#include <string>\n#include <memory>\n#include <objects.h>\n#include <utils.h>\n#include <hessian2_output.h>\n#include <hessian2_input.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n#include <ngx_dubbo.h>\n\nusing namespace std;\nusing namespace hessian;\n\nngx_int_t ngx_dubbo_hessian2_encode_str(ngx_pool_t *pool, ngx_str_t *in, ngx_str_t *out)\n{\n    try {\n        string str;\n        hessian2_output hout(&str);\n        hout.write_utf8_string((const char*)in->data, in->len);\n\n        out->data = (u_char*)ngx_palloc(pool, str.length());\n        if (out->data == NULL) {\n            return NGX_ERROR;\n        }\n\n        ngx_memcpy(out->data, str.c_str(), str.length());\n        out->len = str.length();\n\n        return NGX_OK;\n    } catch (io_exception &e) {\n        ngx_log_error(NGX_LOG_ERR, pool->log, 0, \"dubbo: parse exception failed %s\", e.what());\n        return NGX_ERROR;\n    } catch (...) {\n        return NGX_ERROR;\n    }\n}\n\nngx_int_t ngx_dubbo_hessian2_encode_map(ngx_pool_t *pool, ngx_array_t *in, ngx_str_t *out)\n{\n    try {\n        string str;\n        hessian2_output hout(&str);\n\n        Map strMap;\n        ngx_keyval_t *kv = (ngx_keyval_t*)in->elts;\n        for (size_t i=0; i<in->nelts; i++) {\n            string key((const char*)kv[i].key.data, kv[i].key.len);\n            string value((const char*)kv[i].value.data, kv[i].value.len);\n            ObjectValue key_obj(key);\n            ObjectValue value_obj(value);\n            strMap.put(key_obj, value_obj);\n        }\n        hout.write_object(&strMap);\n\n        out->data = (u_char*)ngx_palloc(pool, str.length());\n        if (out->data == NULL) {\n            return NGX_ERROR;\n        }\n\n        ngx_memcpy(out->data, str.c_str(), str.length());\n        out->len = str.length();\n\n        return NGX_OK;\n    } catch (io_exception &e) {\n        ngx_log_error(NGX_LOG_ERR, pool->log, 0, \"dubbo: parse exception failed %s\", e.what());\n        return NGX_ERROR;\n    } catch (...) {\n        return NGX_ERROR;\n    }\n}\n\nngx_int_t ngx_dubbo_hessian2_encode_payload_map(ngx_pool_t *pool, ngx_array_t *in, ngx_str_t *out)\n{\n    try {\n        string str;\n        hessian2_output hout(&str);\n\n        Map strMap;\n        ngx_keyval_t *kv = (ngx_keyval_t*)in->elts;\n        for (size_t i=0; i<in->nelts; i++) {\n            string key((const char*)kv[i].key.data, kv[i].key.len);\n            if (0 == (key.compare(\"body\"))) {\n                ByteArray *tmp = new ByteArray((const char*)kv[i].value.data, kv[i].value.len);\n                ObjectValue key_obj(key);\n                ObjectValue value_obj((Object*)tmp);\n                strMap.put(key_obj, value_obj);\n            } else {\n                string value((const char*)kv[i].value.data, kv[i].value.len);\n                ObjectValue key_obj(key);\n                ObjectValue value_obj(value);\n                strMap.put(key_obj, value_obj);\n\n            }\n        }\n        hout.write_object(&strMap);\n\n        out->data = (u_char*)ngx_palloc(pool, str.length());\n        if (out->data == NULL) {\n            return NGX_ERROR;\n        }\n\n        ngx_memcpy(out->data, str.c_str(), str.length());\n        out->len = str.length();\n\n        return NGX_OK;\n    } catch (io_exception &e) {\n        ngx_log_error(NGX_LOG_ERR, pool->log, 0, \"dubbo: parse exception failed %s\", e.what());\n        return NGX_ERROR;\n    } catch (...) {\n        return NGX_ERROR;\n    }\n}\n\n\nngx_int_t\nngx_dubbo_hessian2_decode_payload_map(ngx_pool_t *pool, ngx_str_t *in, ngx_array_t **result, ngx_log_t *log)\n{\n    ngx_array_t     *pres;\n    ngx_keyval_t    *kv;\n\n    try {\n        hessian2_input hin((const char*)in->data, in->len);\n        //int attachment = hin.read_int32();\n        hin.read_int32(); //attachment\n\n\n        std::pair<Object*, bool> ret = hin.read_object();\n\n        Map* pmap = (Map*)(ret.first);\n\n        if (pmap == NULL) {\n            ngx_log_error(NGX_LOG_ERR, log, 0, \"dubbo: parse result map failed %V\", in);\n            return NGX_ERROR;\n        }\n\n        Safeguard<Map> safeguard(pmap);\n\n        pres = ngx_array_create(pool, pmap->size(), sizeof(ngx_keyval_t));\n        if (pres == NULL) {\n            return NGX_ERROR;\n        }\n        *result = pres;\n\n        Map::data_type& dmap = (Map::data_type&)pmap->data();\n\n        for (Map::data_type::iterator it = dmap.begin(); it != dmap.end(); it++) {\n            String *sKey = (String*)it->first;\n            Object *oValue = NULL;\n            ByteArray *bValue = NULL;\n\n            kv = (ngx_keyval_t*)ngx_array_push(pres);\n            if (kv == NULL) {\n                return NGX_ERROR;\n            }\n            if (sKey) {\n                string p = sKey->to_string();\n                kv->key.data = (u_char*)ngx_palloc(pool, sKey->size());\n                if (kv->key.data == NULL) {\n                    return NGX_ERROR;\n                }\n                ngx_memcpy(kv->key.data, p.c_str(), p.length());\n                kv->key.len = p.length();\n\n                if (0 == p.compare(\"body\")) {\n                    bValue = (ByteArray*)it->second;\n                } else {\n                    oValue = it->second;\n                }\n            }\n\n            if (oValue) {\n                string p = oValue->to_string();\n                kv->value.data = (u_char*)ngx_palloc(pool, p.length());\n                if (kv->value.data == NULL) {\n                    return NGX_ERROR;\n                }\n                ngx_memcpy(kv->value.data, p.c_str(), p.length());\n                kv->value.len = p.length();\n            }\n\n            if (bValue) {\n                string p = bValue->to_string();\n                kv->value.data = (u_char*)ngx_palloc(pool, bValue->size());\n                if (kv->value.data == NULL) {\n                    return NGX_ERROR;\n                }\n                ngx_memcpy(kv->value.data, p.c_str(), p.length());\n                kv->value.len = p.length();\n            }\n        }\n\n        return NGX_OK;\n    } catch (io_exception &e) {\n        ngx_log_error(NGX_LOG_ERR, log, 0, \"dubbo: parse exception failed %s\", e.what());\n        return NGX_ERROR;\n    } catch (...) {\n        ngx_log_error(NGX_LOG_ERR, log, 0, \"dubbo: parse result failed %V\", in);\n        return NGX_ERROR;\n    }\n}\n\n//ngx_int_t ngx_dubbo_hessian2_encode_req_props(ngx_pool_t *pool, ngx_str_t *traceid, ngx_str_t *rpcid, ngx_str_t *userdata, ngx_str_t *out);\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "modules/mod_dubbo/ngx_http_dubbo_module.c",
    "content": "\n/*\n * Copyright (C) Mengqi Wu (Pull)\n * Copyright (C) 2017-2019 Alibaba Group Holding Limited\n */\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n#include \"ngx_dubbo.h\"\n#include \"ngx_http_dubbo_module.h\"\n#include \"ngx_multi_upstream_module.h\"\n\ntypedef struct {\n    ngx_http_upstream_conf_t    upstream;\n\n    ngx_http_complex_value_t    service_name;\n    ngx_http_complex_value_t    service_version;\n    ngx_http_complex_value_t    method;\n\n    ngx_array_t                *args_in;\n\n    ngx_flag_t                  pass_all_headers;\n    ngx_flag_t                  pass_body;\n    ngx_flag_t                  ups_info;\n\n    ngx_msec_t                  heartbeat_interval;\n} ngx_http_dubbo_loc_conf_t;\n\ntypedef struct {\n    ngx_str_t                    key;\n    ngx_str_t                    value;\n    ngx_int_t                    key_var_index;\n    ngx_int_t                    value_var_index;\n} ngx_http_dubbo_arg_t;\n\ntypedef enum {\n    ngx_http_dubbo_parse_st_start = 0,\n\n    ngx_http_dubbo_parse_st_payload = 1,\n    ngx_http_dubbo_parse_st_props_len = 2,\n\n    ngx_http_dubbo_parse_end\n} ngx_http_dubbo_parse_state_e;\n\ntypedef struct {\n    ngx_chain_t                          *in;\n    ngx_chain_t                          *out;\n    ngx_chain_t                          *free;\n    ngx_chain_t                          *busy;\n\n    ngx_http_request_t                   *request;\n    ngx_dubbo_resp_t                      dubbo_resp;\n    ngx_http_dubbo_parse_state_e          state;\n    ngx_dubbo_connection_t               *connection;\n\n    ngx_array_t                          *result;\n    ngx_str_t                            *response_body;\n} ngx_http_dubbo_ctx_t;\n\ntypedef ngx_int_t (*ngx_http_dubbo_response_handler_pt)(ngx_http_request_t *r);\n\nstatic ngx_int_t ngx_http_dubbo_create_request(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_dubbo_reinit_request(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_dubbo_create_dubbo_request(ngx_http_request_t *r, ngx_connection_t *pc\n        , ngx_multi_request_t **multi_rptr, ngx_chain_t *in);\n\nstatic ngx_int_t ngx_http_dubbo_body_output_filter(void *data, ngx_chain_t *in);\nstatic ngx_int_t ngx_http_dubbo_parse_filter(ngx_http_request_t *r);\n\nstatic ngx_http_dubbo_ctx_t* ngx_http_dubbo_get_ctx(ngx_http_request_t *r);\n\nstatic ngx_int_t ngx_http_dubbo_filter_init(void *data);\nstatic ngx_int_t ngx_http_dubbo_filter(void *data, ssize_t bytes);\nstatic void ngx_http_dubbo_abort_request(ngx_http_request_t *r);\nstatic void ngx_http_dubbo_finalize_request(ngx_http_request_t *r,\n    ngx_int_t rc);\n\nstatic void *ngx_http_dubbo_create_loc_conf(ngx_conf_t *cf);\nstatic char *ngx_http_dubbo_merge_loc_conf(ngx_conf_t *cf,\n    void *parent, void *child);\n\nstatic char *ngx_http_dubbo_pass(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n\nstatic char *ngx_http_dubbo_pass_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\n\nstatic ngx_int_t ngx_http_dubbo_add_response_header(ngx_http_request_t *r, ngx_str_t *name, ngx_str_t *value);\n\nstatic ngx_conf_bitmask_t  ngx_http_dubbo_next_upstream_masks[] = {\n    { ngx_string(\"error\"), NGX_HTTP_UPSTREAM_FT_ERROR },\n    { ngx_string(\"timeout\"), NGX_HTTP_UPSTREAM_FT_TIMEOUT },\n    { ngx_string(\"invalid_header\"), NGX_HTTP_UPSTREAM_FT_INVALID_HEADER },\n    { ngx_string(\"non_idempotent\"), NGX_HTTP_UPSTREAM_FT_NON_IDEMPOTENT },\n    { ngx_string(\"http_500\"), NGX_HTTP_UPSTREAM_FT_HTTP_500 },\n    { ngx_string(\"http_502\"), NGX_HTTP_UPSTREAM_FT_HTTP_502 },\n    { ngx_string(\"http_503\"), NGX_HTTP_UPSTREAM_FT_HTTP_503 },\n    { ngx_string(\"http_504\"), NGX_HTTP_UPSTREAM_FT_HTTP_504 },\n    { ngx_string(\"http_403\"), NGX_HTTP_UPSTREAM_FT_HTTP_403 },\n    { ngx_string(\"http_404\"), NGX_HTTP_UPSTREAM_FT_HTTP_404 },\n    { ngx_string(\"http_429\"), NGX_HTTP_UPSTREAM_FT_HTTP_429 },\n    { ngx_string(\"off\"), NGX_HTTP_UPSTREAM_FT_OFF },\n    { ngx_null_string, 0 }\n};\n\nstatic ngx_str_t  ngx_http_dubbo_hide_headers[] = {\n    ngx_string(\"Date\"),\n    ngx_string(\"Server\"),\n    ngx_string(\"X-Pad\"),\n    ngx_string(\"X-Accel-Expires\"),\n    ngx_string(\"X-Accel-Redirect\"),\n    ngx_string(\"X-Accel-Limit-Rate\"),\n    ngx_string(\"X-Accel-Buffering\"),\n    ngx_string(\"X-Accel-Charset\"),\n    ngx_null_string\n};\n\nstatic ngx_command_t  ngx_http_dubbo_commands[] = {\n\n    { ngx_string(\"dubbo_pass\"),\n      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE4,\n      ngx_http_dubbo_pass,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"dubbo_bind\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,\n      ngx_http_upstream_bind_set_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_dubbo_loc_conf_t, upstream.local),\n      NULL },\n\n    { ngx_string(\"dubbo_socket_keepalive\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_dubbo_loc_conf_t, upstream.socket_keepalive),\n      NULL },\n\n    { ngx_string(\"dubbo_connect_timeout\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_dubbo_loc_conf_t, upstream.connect_timeout),\n      NULL },\n\n    { ngx_string(\"dubbo_send_timeout\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_dubbo_loc_conf_t, upstream.send_timeout),\n      NULL },\n\n    { ngx_string(\"dubbo_intercept_errors\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_dubbo_loc_conf_t, upstream.intercept_errors),\n      NULL },\n\n    { ngx_string(\"dubbo_buffer_size\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_dubbo_loc_conf_t, upstream.buffer_size),\n      NULL },\n\n    { ngx_string(\"dubbo_read_timeout\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_dubbo_loc_conf_t, upstream.read_timeout),\n      NULL },\n\n    { ngx_string(\"dubbo_next_upstream\"),\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_dubbo_loc_conf_t, upstream.next_upstream),\n      &ngx_http_dubbo_next_upstream_masks },\n\n    { ngx_string(\"dubbo_next_upstream_tries\"),\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_dubbo_loc_conf_t, upstream.next_upstream_tries),\n      NULL },\n\n    { ngx_string(\"dubbo_next_upstream_timeout\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_dubbo_loc_conf_t, upstream.next_upstream_timeout),\n      NULL },\n\n    { ngx_string(\"dubbo_pass_header\"),\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_dubbo_loc_conf_t, upstream.pass_headers),\n      NULL },\n\n    { ngx_string(\"dubbo_hide_header\"),\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_dubbo_loc_conf_t, upstream.hide_headers),\n      NULL },\n\n    { ngx_string(\"dubbo_ignore_headers\"),\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_dubbo_loc_conf_t, upstream.ignore_headers),\n      &ngx_http_upstream_ignore_headers_masks },\n\n\n    { ngx_string(\"dubbo_pass_set\"),\n      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE2,\n      ngx_http_dubbo_pass_set,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"dubbo_pass_all_headers\"),\n      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_dubbo_loc_conf_t, pass_all_headers),\n      NULL },\n\n    { ngx_string(\"dubbo_pass_body\"),\n      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_dubbo_loc_conf_t, pass_body),\n      NULL },\n\n    { ngx_string(\"dubbo_heartbeat_interval\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_dubbo_loc_conf_t, heartbeat_interval),\n      NULL },\n\n    { ngx_string(\"dubbo_upstream_error_info\"),\n      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_dubbo_loc_conf_t, ups_info),\n      NULL },\n\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_dubbo_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    NULL,                                  /* 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    ngx_http_dubbo_create_loc_conf,        /* create location configuration */\n    ngx_http_dubbo_merge_loc_conf          /* merge location configuration */\n};\n\n\nngx_module_t  ngx_http_dubbo_module = {\n    NGX_MODULE_V1,\n    &ngx_http_dubbo_module_ctx,            /* module context */\n    ngx_http_dubbo_commands,               /* 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\nconst ngx_str_t ngx_http_dubbo_str_body = ngx_string(\"body\");\nconst ngx_str_t ngx_http_dubbo_str_status = ngx_string(\"status\");\nconst ngx_str_t ngx_http_dubbo_content_type = ngx_string(\"Content-Type\");\nconst ngx_str_t ngx_http_dubbo_content_type_text = ngx_string(\"text/html\");\n\nstatic ngx_int_t\nngx_http_dubbo_handler(ngx_http_request_t *r)\n{\n    ngx_int_t                    rc;\n    ngx_http_upstream_t         *u;\n    ngx_http_dubbo_ctx_t        *ctx;\n    ngx_http_dubbo_loc_conf_t   *dlcf;\n\n    if (ngx_http_upstream_create(r) != NGX_OK) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_dubbo_ctx_t));\n    if (ctx == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    ctx->request = r;\n\n    ngx_http_set_ctx(r, ctx, ngx_http_dubbo_module);\n\n    u = r->upstream;\n\n    ngx_str_set(&u->schema, \"dubbo://\");\n    u->output.tag = (ngx_buf_tag_t) &ngx_http_dubbo_module;\n\n    dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dubbo_module);\n\n    u->conf = &dlcf->upstream;\n\n    if (ngx_http_set_content_type(r) != NGX_OK) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n \n    u->create_request = ngx_http_dubbo_create_request;\n    u->reinit_request = ngx_http_dubbo_reinit_request;\n    u->process_header = ngx_http_dubbo_parse_filter;\n    u->abort_request = ngx_http_dubbo_abort_request;\n    u->finalize_request = ngx_http_dubbo_finalize_request;\n\n    r->state = 0;\n\n#if 0 //just support no buffering upstream for the moment\n    u->buffering = dlcf->upstream.buffering;\n\n    u->pipe = ngx_pcalloc(r->pool, sizeof(ngx_event_pipe_t));\n    if (u->pipe == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n#else\n    u->buffering = 0;\n#endif\n\n    u->input_filter_init = ngx_http_dubbo_filter_init;\n    u->input_filter = ngx_http_dubbo_filter;\n    u->input_filter_ctx = ctx;\n\n    //only support buffering mode\n    r->request_body_no_buffering = 0;\n\n    u->multi_mode = NGX_MULTI_UPS_NEED_MULTI;\n\n    rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init);\n\n    if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {\n        return rc;\n    }\n\n    return NGX_DONE;\n}\n\nngx_int_t\nngx_http_dubbo_response_handler(ngx_connection_t *pc, ngx_http_request_t *r, ngx_array_t *result)\n{\n    ngx_http_upstream_t             *u;\n    ngx_keyval_t                    *kv;\n    ngx_uint_t                       i;\n    ngx_chain_t                     *cl;\n    ngx_buf_t                       *buf;\n    ngx_uint_t                       status;\n\n    u = r->upstream;\n\n    if (u->out_bufs != NULL) {\n        ngx_log_error(NGX_LOG_ERR, pc->log, 0, \"dubbo [%V]: out_bufs is not NULL, %p\", r);\n        return NGX_ERROR;\n    }\n\n    u->headers_in.status_n = NGX_HTTP_BAD_GATEWAY;\n    u->state->status = NGX_HTTP_BAD_GATEWAY;\n\n    kv = result->elts;\n    for (i=0; i < result->nelts; i++) {\n        if (kv[i].key.len == 4 && 0 == ngx_strncasecmp(kv[i].key.data,\n                    ngx_http_dubbo_str_body.data, ngx_http_dubbo_str_body.len)) {\n            if (kv[i].value.len > 0 && kv[i].value.data != NULL) {\n                cl = ngx_chain_get_free_buf(r->pool, &u->free_bufs);\n                if (cl == NULL) {\n                    return NGX_ERROR;\n                }\n\n                u->out_bufs = cl;\n                buf = u->out_bufs->buf;\n\n                buf->flush = 1;\n                buf->memory = 1;\n\n                buf->pos = kv[i].value.data;\n                buf->last = kv[i].value.data + kv[i].value.len;\n            }\n\n            u->headers_in.content_length_n = kv[i].value.len;\n        } else if (kv[i].key.len == 6 && 0 == ngx_strncasecmp(kv[i].key.data, ngx_http_dubbo_str_status.data, ngx_http_dubbo_str_status.len)) {\n            status = ngx_atoi(kv[i].value.data, kv[i].value.len);\n            u->headers_in.status_n = status;\n            u->state->status = status;\n        } else {\n            if (NGX_OK != ngx_http_dubbo_add_response_header(r, &kv[i].key, &kv[i].value)) {\n                return NGX_ERROR;\n            }\n        }\n    }\n\n    return NGX_OK;\n}\n\nstatic ngx_int_t\nngx_http_dubbo_create_request(ngx_http_request_t *r)\n{\n    ngx_http_upstream_t     *u;\n\n    u = r->upstream;\n\n    u->output.output_filter = ngx_http_dubbo_body_output_filter;\n    u->output.filter_ctx = r;\n\n    return NGX_OK;\n}\n\nstatic ngx_int_t\nngx_http_dubbo_body_output_filter(void *data, ngx_chain_t *in)\n{\n    ngx_http_request_t      *r = data;\n    ngx_connection_t        *pc = r->upstream->peer.connection;\n    ngx_http_request_t      *fake_r = pc->data;\n    ngx_chain_t             *out, *cl, **ll, *tmp;\n    ngx_multi_request_t     *multi_r;\n    ngx_buf_t               *b;\n    u_char                  *start;\n    ngx_int_t                rc;\n    ngx_multi_connection_t  *multi_c;\n    ngx_dubbo_connection_t  *dubbo_c;\n    ngx_http_dubbo_loc_conf_t *dlcf;\n\n    ngx_http_dubbo_ctx_t    *ctx = ngx_http_dubbo_get_ctx(fake_r);\n\n    if (!r->upstream->multi) {\n        ngx_log_error(NGX_LOG_ERR, pc->log, 0, \"dubbo: only support upstream multi module\");\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    multi_c = ngx_get_multi_connection(pc);\n    dubbo_c = ctx->connection;\n\n    dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dubbo_module);\n\n    if (r == fake_r && in != NULL) {\n        ngx_log_error(NGX_LOG_ERR, pc->log, 0, \"dubbo: body output failed %p, %p\", pc, r);\n\n        return NGX_ERROR;\n    }\n\n    out = NULL;\n    ll = &out;\n\n    if (ctx->out) {\n\n        *ll = ctx->out;\n\n        for (cl = ctx->out, ll = &cl->next; cl; cl = cl->next) {\n            ll = &cl->next;\n        }\n\n        ctx->out = NULL;\n    }\n\n    if (r != fake_r) { //no need send dubbo request when fake_r\n\n        if (NGX_OK != ngx_http_dubbo_create_dubbo_request(r, pc, &multi_r, in)) {\n            ngx_log_error(NGX_LOG_ERR, pc->log, 0, \"dubbo: http create request failed %p, %p\", pc, r);\n            return NGX_ERROR;\n        }\n\n        for (cl = multi_r->out; cl; cl = cl->next) {\n            tmp = ngx_chain_get_free_buf(fake_r->pool, &ctx->free);\n            if (tmp == NULL) {\n                return NGX_ERROR;\n            }\n\n            b = tmp->buf;\n            start = b->start;\n\n            ngx_memcpy(b, cl->buf, sizeof(ngx_buf_t));\n\n            /*\n             * restore b->start to preserve memory allocated in the buffer,\n             * to reuse it later for headers and control frames\n             */\n\n            b->start = start;\n\n            b->tag = (ngx_buf_tag_t) &ngx_http_dubbo_body_output_filter;\n            b->shadow = cl->buf;\n            b->last_shadow = 1;\n\n            b->last_buf = 0;\n            b->last_in_chain = 0;\n\n            *ll = tmp;\n            ll = &tmp->next;\n        }\n\n        ngx_queue_insert_head(&multi_c->send_list, &multi_r->backend_queue);\n\n        //init front list\n        if (r->backend_r == NULL) {\n            r->backend_r = ngx_pcalloc(r->connection->pool, sizeof(ngx_queue_t));\n            if (r->backend_r == NULL) {\n                return NGX_ERROR;\n            }\n\n            ngx_queue_init(r->backend_r);\n        }\n\n        //add to front list to remove on front close early\n        ngx_queue_insert_tail(r->backend_r, &multi_r->front_queue);\n    }\n\n    rc = ngx_chain_writer(&fake_r->upstream->writer, out);\n\n    ngx_chain_update_chains(fake_r->pool, &ctx->free, &ctx->busy, &out,\n            (ngx_buf_tag_t) &ngx_http_dubbo_body_output_filter);\n\n    for (cl = ctx->free; cl; cl = cl->next) {\n\n        /* mark original buffers as sent */\n        if (cl->buf->shadow) {\n            if (cl->buf->last_shadow) {\n                b = cl->buf->shadow;\n                b->pos = b->last;\n            }\n\n            cl->buf->shadow = NULL;\n        }\n    }\n\n    ngx_add_timer(&dubbo_c->ping_event, dlcf->heartbeat_interval);\n\n    return rc;\n}\n\nngx_int_t\nngx_http_dubbo_get_variable(ngx_http_request_t *r, ngx_str_t *name, ngx_str_t *value)\n{\n    u_char                      *low;\n    ngx_str_t                    var;\n    ngx_uint_t                   hash;\n    ngx_http_variable_value_t   *vv;\n\n    if (0 >= name->len || NULL == name->data) {\n        return NGX_ERROR;\n    }\n\n    low = ngx_pnalloc(r->pool, name->len);\n    if (low == NULL) {\n        return NGX_ERROR;\n    }\n\n    hash = ngx_hash_strlow(low, name->data, name->len);\n    var.data = low;\n    var.len = name->len;\n\n    vv = ngx_http_get_variable(r, &var, hash);\n\n    if (vv == NULL || vv->not_found || vv->valid == 0) {\n        return NGX_ERROR;\n    }\n\n    value->data = vv->data;\n    value->len = vv->len;\n\n    return NGX_OK;\n}\n\nstatic ngx_int_t\nngx_http_dubbo_create_dubbo_request(ngx_http_request_t *r, ngx_connection_t *pc, ngx_multi_request_t **multi_rptr, ngx_chain_t *in)\n{\n    ngx_http_dubbo_ctx_t        *ctx;\n    ngx_multi_request_t         *multi_r;\n    ngx_dubbo_connection_t      *dubbo_c;\n    ngx_http_dubbo_loc_conf_t   *dlcf;\n\n    ngx_str_t                   *service_name;\n    ngx_str_t                   *service_version;\n    ngx_str_t                   *method;\n\n    ngx_array_t                 *args;\n    ngx_dubbo_arg_t             *arg;\n    ngx_keyval_t                *kv;\n    ngx_uint_t                   n;\n\n    size_t                       len = 0;\n    ngx_int_t                    size;\n    ngx_buf_t                   *body = NULL;\n    ngx_chain_t                 *cl;\n\n    ngx_http_variable_value_t   *vv;\n    size_t                       i;\n\n    ctx = ngx_http_dubbo_get_ctx(r);\n    dubbo_c = ctx->connection;\n\n    //read body\n    for (cl = in; cl; cl = cl->next) {\n        len += ngx_buf_size(cl->buf);\n    }\n\n    if (len > 0) {\n        body = ngx_create_temp_buf(r->pool, len);\n        if (body == NULL) {\n            return NGX_ERROR;\n        }\n        for (cl = in; cl; cl = cl->next) {\n            if (cl->buf->in_file) {\n                size = ngx_read_file(cl->buf->file, body->last,\n                        cl->buf->file_last - cl->buf->file_pos, cl->buf->file_pos);\n\n                if (size == NGX_ERROR) {\n                    return NGX_ERROR;\n                }\n\n                body->last += size;\n            } else {\n                body->last = ngx_cpymem(body->last, cl->buf->pos, cl->buf->last - cl->buf->pos);\n            }\n        }\n    }\n\n    if (dubbo_c == NULL) {\n        return NGX_ERROR;\n    }\n\n    multi_r = ngx_create_multi_request(pc, r);\n    if (multi_r == NULL) {\n        return NGX_ERROR;\n    }\n\n    *multi_rptr = multi_r;\n    \n    dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dubbo_module);\n\n    service_name = ngx_palloc(r->pool, sizeof(ngx_str_t));\n    if (service_name == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (ngx_http_complex_value(r, &dlcf->service_name, service_name)\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    service_version = ngx_palloc(r->pool, sizeof(ngx_str_t));\n    if (service_version == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (ngx_http_complex_value(r, &dlcf->service_version, service_version)\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    method = ngx_palloc(r->pool, sizeof(ngx_str_t));\n    if (method == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (ngx_http_complex_value(r, &dlcf->method, method)\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    args = ngx_array_create(dubbo_c->temp_pool, 1, sizeof(ngx_dubbo_arg_t));\n    if (args == NULL) {\n        return NGX_ERROR;\n    }\n\n    arg = (ngx_dubbo_arg_t*)ngx_array_push(args);\n    if (arg == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (dlcf->args_in == NULL) {\n        n = 1;\n    } else {\n        n = dlcf->args_in->nelts;\n    }\n\n    arg->type = DUBBO_ARG_MAP;\n    arg->value.m = ngx_array_create(dubbo_c->temp_pool, n, sizeof(ngx_keyval_t));\n\n    if (body && dlcf->pass_body) {\n        kv = (ngx_keyval_t*)ngx_array_push(arg->value.m);\n        if (kv == NULL) {\n            return NGX_ERROR;\n        }\n\n        kv->key.data = ngx_http_dubbo_str_body.data;\n        kv->key.len = ngx_http_dubbo_str_body.len;\n        kv->value.data = body->pos;\n        kv->value.len = body->last - body->pos;\n    }\n\n    if (dlcf->pass_all_headers) {\n        //pass all\n        ngx_uint_t                              i;\n        ngx_list_part_t                        *part;\n        ngx_table_elt_t                        *header;\n\n        part = &r->headers_in.headers.part;\n        header = 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                header = part->elts;\n                i = 0;\n            }\n\n            kv = (ngx_keyval_t*)ngx_array_push(arg->value.m);\n            if (kv == NULL) {\n                return NGX_ERROR;\n            }\n\n            kv->key.data = header[i].lowcase_key;\n            kv->key.len = header[i].key.len;\n            kv->value.data = header[i].value.data;\n            kv->value.len = header[i].value.len;\n        }\n    }\n\n    if (dlcf->args_in != NULL) {\n        //pass set\n        ngx_http_dubbo_arg_t *args_in = dlcf->args_in->elts;\n        for (i = 0; i < dlcf->args_in->nelts; i++) {\n            kv = (ngx_keyval_t*)ngx_array_push(arg->value.m);\n            if (kv == NULL) {\n                return NGX_ERROR;\n            }\n\n            //get key value\n            if (args_in[i].key_var_index != NGX_CONF_UNSET) {\n                vv = ngx_http_get_indexed_variable(r, args_in[i].key_var_index);\n                if (vv == NULL || vv->not_found) {\n                    ngx_log_error(NGX_LOG_WARN, r->connection->log, 0\n                            , \"dubbo: cannot found pass set key from variable index %ui, %V\"\n                            , args_in[i].key_var_index, &args_in[i].key);\n                    ngx_str_null(&kv->key);\n                } else {\n                    kv->key.data = vv->data;\n                    kv->key.len = vv->len;\n                }\n            } else {\n                kv->key = args_in[i].key;\n            }\n\n            if (args_in[i].value_var_index != NGX_CONF_UNSET) {\n                vv = ngx_http_get_indexed_variable(r, args_in[i].value_var_index);\n                if (vv == NULL || vv->not_found) {\n                    ngx_log_error(NGX_LOG_WARN, r->connection->log, 0\n                            , \"dubbo: cannot found pass set key from variable index %ui, %V\"\n                            , args_in[i].value_var_index, &args_in[i].value);\n                    ngx_str_null(&kv->value);\n                } else {\n                    kv->value.data = vv->data;\n                    kv->value.len = vv->len;\n                }\n            } else {\n                kv->value = args_in[i].value;\n            }\n        }\n    }\n\n    if (NGX_ERROR == ngx_dubbo_encode_request(dubbo_c, service_name,\n                                              service_version, method,\n                                              args, multi_r))\n    {\n        ngx_log_error(NGX_LOG_ERR, dubbo_c->log, 0,\n                      \"dubbo: encode request failed\");\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\nstatic ngx_int_t\nngx_http_dubbo_reinit_request(ngx_http_request_t *r)\n{\n    return NGX_OK;\n}\n\nstatic ngx_int_t\nngx_http_dubbo_filter_init(void *data)\n{\n    ngx_http_dubbo_ctx_t  *ctx = data;\n\n    ngx_http_upstream_t  *u;\n\n    u = ctx->request->upstream;\n\n    u->length = 1;\n\n    return NGX_OK;\n}\n\nstatic ngx_int_t\nngx_http_dubbo_filter(void *data, ssize_t bytes)\n{\n    ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, \"dubbo: dubbo filter not used\");\n\n    return NGX_ERROR;\n}\n\nstatic void\nngx_http_dubbo_abort_request(ngx_http_request_t *r)\n{\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"abort http dubbo request\");\n    return;\n}\n\n\nstatic void\nngx_http_dubbo_finalize_request(ngx_http_request_t *r, ngx_int_t rc)\n{\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"finalize http dubbo request\");\n    return;\n}\n\nstatic void *\nngx_http_dubbo_create_loc_conf(ngx_conf_t *cf)\n{\n    ngx_http_dubbo_loc_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_dubbo_loc_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc():\n     *\n     *     conf->upstream.ignore_headers = 0;\n     *     conf->upstream.next_upstream = 0;\n     *     conf->upstream.hide_headers_hash = { NULL, 0 };\n     *     conf->upstream.ssl_name = NULL;\n     *\n     */\n\n    conf->upstream.local = NGX_CONF_UNSET_PTR;\n    conf->upstream.next_upstream_tries = NGX_CONF_UNSET_UINT;\n    conf->upstream.connect_timeout = NGX_CONF_UNSET_MSEC;\n    conf->upstream.send_timeout = NGX_CONF_UNSET_MSEC;\n    conf->upstream.read_timeout = NGX_CONF_UNSET_MSEC;\n    conf->upstream.next_upstream_timeout = NGX_CONF_UNSET_MSEC;\n\n    conf->upstream.buffer_size = NGX_CONF_UNSET_SIZE;\n\n    conf->upstream.hide_headers = NGX_CONF_UNSET_PTR;\n    conf->upstream.pass_headers = NGX_CONF_UNSET_PTR;\n\n    conf->upstream.intercept_errors = NGX_CONF_UNSET;\n\n#if (NGX_HTTP_SSL)\n    conf->upstream.ssl_session_reuse = NGX_CONF_UNSET;\n    conf->upstream.ssl_server_name = NGX_CONF_UNSET;\n    conf->upstream.ssl_verify = NGX_CONF_UNSET;\n#endif\n\n    /* the hardcoded values */\n    conf->upstream.cyclic_temp_file = 0;\n    conf->upstream.buffering = 0;\n    conf->upstream.ignore_client_abort = 0;\n    conf->upstream.send_lowat = 0;\n    conf->upstream.bufs.num = 0;\n    conf->upstream.busy_buffers_size = 0;\n    conf->upstream.max_temp_file_size = 0;\n    conf->upstream.temp_file_write_size = 0;\n    conf->upstream.pass_request_headers = 1;\n    conf->upstream.pass_request_body = 1;\n    conf->upstream.force_ranges = 0;\n    conf->upstream.pass_trailers = 1;\n    conf->upstream.preserve_output = 1;\n\n    ngx_str_set(&conf->upstream.module, \"dubbo\");\n\n    conf->pass_all_headers = NGX_CONF_UNSET;\n    conf->pass_body = NGX_CONF_UNSET;\n    conf->ups_info = NGX_CONF_UNSET;\n    conf->args_in = NULL;\n    conf->heartbeat_interval = NGX_CONF_UNSET_MSEC;\n\n    return conf;\n}\n\n\nstatic char *\nngx_http_dubbo_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_hash_init_t            hash;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    ngx_http_dubbo_loc_conf_t *prev = parent;\n    ngx_http_dubbo_loc_conf_t *conf = child;\n\n    ngx_conf_merge_ptr_value(conf->upstream.local,\n            prev->upstream.local, NULL);\n\n    ngx_conf_merge_value(conf->upstream.socket_keepalive,\n            prev->upstream.socket_keepalive, 0);\n\n    ngx_conf_merge_uint_value(conf->upstream.next_upstream_tries,\n            prev->upstream.next_upstream_tries, 0);\n\n    ngx_conf_merge_msec_value(conf->upstream.connect_timeout,\n            prev->upstream.connect_timeout, 60000);\n\n    ngx_conf_merge_msec_value(conf->upstream.send_timeout,\n            prev->upstream.send_timeout, 60000);\n\n    ngx_conf_merge_msec_value(conf->upstream.read_timeout,\n            prev->upstream.read_timeout, 60000);\n\n    ngx_conf_merge_msec_value(conf->upstream.next_upstream_timeout,\n            prev->upstream.next_upstream_timeout, 0);\n\n    ngx_conf_merge_size_value(conf->upstream.buffer_size,\n            prev->upstream.buffer_size,\n            (size_t) ngx_pagesize);\n\n    ngx_conf_merge_bitmask_value(conf->upstream.ignore_headers,\n            prev->upstream.ignore_headers,\n            NGX_CONF_BITMASK_SET);\n\n    ngx_conf_merge_bitmask_value(conf->upstream.next_upstream,\n            prev->upstream.next_upstream,\n            (NGX_CONF_BITMASK_SET\n             |NGX_HTTP_UPSTREAM_FT_ERROR\n             |NGX_HTTP_UPSTREAM_FT_TIMEOUT));\n\n    if (conf->upstream.next_upstream & NGX_HTTP_UPSTREAM_FT_OFF) {\n        conf->upstream.next_upstream = NGX_CONF_BITMASK_SET\n            |NGX_HTTP_UPSTREAM_FT_OFF;\n    }\n\n    ngx_conf_merge_value(conf->upstream.intercept_errors,\n            prev->upstream.intercept_errors, 0);\n\n#if (NGX_HTTP_SSL)\n\n    ngx_conf_merge_value(conf->upstream.ssl_session_reuse,\n            prev->upstream.ssl_session_reuse, 1);\n\n    if (conf->upstream.ssl_name == NULL) {\n        conf->upstream.ssl_name = prev->upstream.ssl_name;\n    }\n\n    ngx_conf_merge_value(conf->upstream.ssl_server_name,\n            prev->upstream.ssl_server_name, 0);\n    ngx_conf_merge_value(conf->upstream.ssl_verify,\n            prev->upstream.ssl_verify, 0);\n\n#endif\n\n    hash.max_size = 512;\n    hash.bucket_size = ngx_align(64, ngx_cacheline_size);\n    hash.name = \"dubbo_headers_hash\";\n\n    if (ngx_http_upstream_hide_headers_hash(cf, &conf->upstream,\n                &prev->upstream, ngx_http_dubbo_hide_headers, &hash)\n            != NGX_OK)\n    {\n        return NGX_CONF_ERROR;\n    }\n\n    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);\n\n    if (clcf->noname && conf->upstream.upstream == NULL) {\n        conf->upstream.upstream = prev->upstream.upstream;\n#if (NGX_HTTP_SSL)\n        conf->upstream.ssl = prev->upstream.ssl;\n#endif\n    }\n\n    if (clcf->lmt_excpt && clcf->handler == NULL && conf->upstream.upstream) {\n        clcf->handler = ngx_http_dubbo_handler;\n    }\n\n    if (conf->service_name.value.data == NULL) {\n        conf->service_name = prev->service_name;\n    }\n\n    if (conf->service_version.value.data == NULL) {\n        conf->service_version = prev->service_version;\n    }\n\n    if (conf->method.value.data == NULL) {\n        conf->method = prev->method;\n    }\n\n    ngx_conf_merge_ptr_value(conf->args_in, prev->args_in, NULL);\n    ngx_conf_merge_value(conf->pass_all_headers, prev->pass_all_headers, 1);\n    ngx_conf_merge_value(conf->pass_body, prev->pass_body, 1);\n    ngx_conf_merge_value(conf->ups_info, prev->ups_info, 0);\n\n    ngx_conf_merge_msec_value(conf->heartbeat_interval,\n                              prev->heartbeat_interval, 60000);\n\n    return NGX_CONF_OK;\n}\n\nstatic char *\nngx_http_dubbo_compile_complex_value(ngx_conf_t *cf, ngx_str_t *value,\n                                     ngx_http_complex_value_t *cv)\n{\n    ngx_http_compile_complex_value_t   ccv;\n\n    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n\n    ccv.cf = cf;\n    ccv.value = value;\n    ccv.complex_value = cv;\n\n    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\nstatic char *\nngx_http_dubbo_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_dubbo_loc_conf_t *dlcf = conf;\n\n    ngx_str_t                 *value;\n    ngx_url_t                  u;\n    ngx_http_core_loc_conf_t  *clcf;\n    char                      *msg;\n\n    if (dlcf->upstream.upstream) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    if ((msg = ngx_http_dubbo_compile_complex_value(cf, &value[1],\n                                                    &dlcf->service_name))\n        != NGX_CONF_OK)\n    {\n        return msg;\n    }\n\n    if ((msg = ngx_http_dubbo_compile_complex_value(cf, &value[2],\n                                                    &dlcf->service_version))\n        != NGX_CONF_OK)\n    {\n        return msg;\n    }\n\n    if ((msg = ngx_http_dubbo_compile_complex_value(cf, &value[3],\n                                                    &dlcf->method))\n        != NGX_CONF_OK)\n    {\n        return msg;\n    }\n\n    ngx_memzero(&u, sizeof(ngx_url_t));\n\n    u.url = value[4];\n    u.no_resolve = 1;\n\n    dlcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0);\n    if (dlcf->upstream.upstream == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);\n\n    clcf->handler = ngx_http_dubbo_handler;\n\n    if (clcf->name.data[clcf->name.len - 1] == '/') {\n        clcf->auto_redirect = 1;\n    }\n\n    return NGX_CONF_OK;\n}\n\nstatic char *\nngx_http_dubbo_pass_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_dubbo_loc_conf_t *dlcf = conf;\n\n    ngx_str_t                 *value;\n    ngx_http_dubbo_arg_t        *arg;\n\n    if (dlcf->upstream.upstream) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    if (dlcf->args_in == NULL) {\n        dlcf->args_in = ngx_array_create(cf->pool, 4, sizeof(ngx_http_dubbo_arg_t));\n        if (NULL == dlcf->args_in) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    arg = (ngx_http_dubbo_arg_t*)ngx_array_push(dlcf->args_in);\n    if (arg == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    arg->key = value[1];\n    arg->value = value[2];\n    arg->key_var_index = NGX_CONF_UNSET;\n    arg->value_var_index = NGX_CONF_UNSET;\n    if (*value[1].data == '$') {\n        arg->key.data += 1;\n        arg->key.len -= 1;\n\n        arg->key_var_index = ngx_http_get_variable_index(cf, &arg->key);\n    }\n\n    if (*value[2].data == '$') {\n        arg->value.data += 1;\n        arg->value.len -= 1;\n\n        arg->value_var_index = ngx_http_get_variable_index(cf, &arg->value);\n        if (arg->value_var_index == NGX_ERROR) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    return NGX_CONF_OK;\n}\n\nstatic void\nngx_http_dubbo_ping_handler(ngx_event_t *ev)\n{\n    ngx_connection_t        *pc;\n    ngx_http_request_t      *fake_r;\n    ngx_http_upstream_t     *fake_u;\n    ngx_multi_request_t     *multi_r;\n    ngx_dubbo_connection_t  *dubbo_c;\n    ngx_http_dubbo_ctx_t    *ctx;\n    u_char                  *start;\n    ngx_chain_t             *cl, *tmp, **ll;\n    ngx_buf_t               *b;\n\n    pc = ev->data;\n    fake_r = pc->data;\n    fake_u = fake_r->upstream;\n\n    ctx = ngx_http_dubbo_get_ctx(fake_r);\n    dubbo_c = ctx->connection;\n\n    multi_r = ngx_create_multi_request(pc, fake_r);\n    if (multi_r == NULL) {\n        return;\n    }\n\n    if (NGX_ERROR == ngx_dubbo_encode_ping_request(dubbo_c, multi_r)) {\n        ngx_log_error(NGX_LOG_ERR, dubbo_c->log, 0,\n                      \"dubbo: encode ping request failed\");\n        return;\n    }\n\n    for (cl = ctx->out, ll = &ctx->out; cl; cl = cl->next) {\n        ll = &cl->next;\n    }\n\n    for (cl = multi_r->out; cl; cl = cl->next) {\n        tmp = ngx_chain_get_free_buf(fake_r->pool, &ctx->free);\n        if (tmp == NULL) {\n            return;\n        }\n\n        b = tmp->buf;\n        start = b->start;\n\n        ngx_memcpy(b, cl->buf, sizeof(ngx_buf_t));\n\n        /*\n         * restore b->start to preserve memory allocated in the buffer,\n         * to reuse it later for headers and control frames\n         */\n\n        b->start = start;\n\n        b->tag = (ngx_buf_tag_t) &ngx_http_dubbo_body_output_filter;\n        b->shadow = cl->buf;\n        b->last_shadow = 1;\n\n        b->last_buf = 0;\n        b->last_in_chain = 0;\n\n        *ll = tmp;\n        ll = &tmp->next;\n    }\n\n    ngx_post_event(fake_u->peer.connection->write, &ngx_posted_events);\n    ngx_log_error(NGX_LOG_INFO, dubbo_c->log, 0,\n                  \"dubbo: send ping request [%ul] frame to backend\", multi_r->id);\n    return;\n}\n\nstatic ngx_int_t\nngx_http_dubbo_add_response_header(ngx_http_request_t *r, ngx_str_t *name, ngx_str_t *value)\n{\n    ngx_table_elt_t                 *h;\n    ngx_http_upstream_header_t      *hh;\n    ngx_http_upstream_main_conf_t   *umcf;\n    ngx_http_upstream_t             *u;\n\n    umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);\n    u = r->upstream;\n\n    h = ngx_list_push(&u->headers_in.headers);\n    if (h == NULL) {\n        return NGX_ERROR;\n    }\n\n    h->key = *name;\n    h->value = *value;\n    h->lowcase_key = ngx_pcalloc(r->pool, h->key.len);\n    if (h->lowcase_key == NULL) {\n        return NGX_ERROR;\n    }\n    ngx_strlow(h->lowcase_key, h->key.data, h->key.len);\n    h->hash = ngx_hash_key(h->lowcase_key, h->key.len);\n\n    hh = ngx_hash_find(&umcf->headers_in_hash, h->hash,\n            h->lowcase_key, h->key.len);\n\n    if (hh && hh->handler(r, h, hh->offset) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\nstatic ngx_int_t\nngx_http_dubbo_parse_filter(ngx_http_request_t *r)\n{\n    ngx_http_request_t              *fake_r;\n    ngx_http_upstream_t             *fake_u;\n    ngx_chain_t                      in;\n    ngx_http_dubbo_ctx_t            *ctx, *fake_ctx;\n    ngx_int_t                        ret;\n    ngx_http_request_t              *real_r;\n\n    ngx_queue_t                     *q, *n;\n    ngx_multi_request_t             *multi_r, *tmp;\n    ngx_dubbo_resp_t                *resp;\n    ngx_str_t                        body;\n    ngx_flag_t                       find;\n    ngx_dubbo_connection_t          *dubbo_c;\n    ngx_multi_connection_t          *multi_c;\n    ngx_connection_t                *pc;\n\n    ngx_http_dubbo_loc_conf_t       *dlcf;\n    ngx_keyval_t                    *kv;\n\n    fake_r = r;\n    fake_u = r->upstream;\n\n    in.buf = &fake_u->buffer;\n    in.next = NULL;\n\n    if (!fake_u->multi) {\n        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                      \"dubbo: only support upstream multi module\");\n        return NGX_ERROR;\n    }\n\n    fake_ctx = ngx_http_dubbo_get_ctx(fake_r);\n    ctx = fake_ctx;\n\n    dubbo_c = ctx->connection;\n\n    pc = fake_u->peer.connection;\n\n    multi_c = ngx_get_multi_connection(pc);\n\n    switch (ctx->state) {\n        case ngx_http_dubbo_parse_st_start:\n            multi_c->cur = NULL;\n            for ( ; ; ) {\n                ret = ngx_dubbo_decode_response(dubbo_c, &in);\n\n                if (ret == NGX_ERROR) {\n                    ngx_log_error(NGX_LOG_WARN, dubbo_c->log, 0, \"dubbo: response parse error\");\n                    return NGX_ERROR;\n                } else if (ret == NGX_DONE) {\n                    resp = &dubbo_c->resp;\n\n                    if (resp->header.type & DUBBO_FLAG_PING) {\n                        //ping frame\n                        if (resp->header.type & DUBBO_FLAG_REQ) {\n                            ngx_log_error(NGX_LOG_INFO, dubbo_c->log, 0,\n                                          \"dubbo: get a ping request frame [%ul] from backend\", resp->header.reqid);\n#if 0\n                            //no need send response just moment\n                            ngx_post_event(fake_u->peer.connection->write, &ngx_posted_events);\n#endif\n                        } else {\n                            ngx_log_error(NGX_LOG_INFO, dubbo_c->log, 0,\n                                          \"dubbo: get a ping response frame [%ul] from backend\", resp->header.reqid);\n                        }\n                        continue;\n                    }\n\n                    find = 0;\n                    for (q = ngx_queue_head(&multi_c->send_list);\n                            q != ngx_queue_sentinel(&multi_c->send_list);\n                            q = ngx_queue_next(q))\n                    {\n                        multi_r = ngx_queue_data(q, ngx_multi_request_t, backend_queue);\n                        if (multi_r->id == resp->header.reqid) { //find\n                            find = 1;\n                            ngx_queue_remove(q);\n\n                            //clean front list for multi_r \n                            real_r = multi_r->data;\n                            if (real_r->backend_r) {\n                                for (n = ngx_queue_head(real_r->backend_r);\n                                        n != ngx_queue_sentinel(real_r->backend_r);\n                                        n = ngx_queue_next(n))\n                                {\n                                    tmp = ngx_queue_data(n, ngx_multi_request_t, front_queue); \n                                    if (tmp == multi_r) {\n                                        ngx_queue_remove(n);\n                                        break;\n                                    }\n                                }\n                            } else {\n                                ngx_log_error(NGX_LOG_ERR, dubbo_c->log, 0, \"dubbo: find dubbo_r but front list null\");\n                                //free dubbo_r and pool\n                                ngx_destroy_pool(multi_r->pool);\n                                return NGX_ERROR;\n                            }\n\n                            body.data = resp->payload;\n                            body.len = resp->header.payloadlen;\n\n                            multi_c->cur = multi_r->data;\n\n                            if (NGX_OK != ngx_dubbo_hessian2_decode_payload_map(real_r->pool,\n                                        &body, &ctx->result, dubbo_c->log)) {\n\n                                ngx_log_error(NGX_LOG_WARN, dubbo_c->log,\n                                              0, \"dubbo: response decode result failed %V\", &body);\n\n                                dlcf = ngx_http_get_module_loc_conf(real_r, ngx_http_dubbo_module);\n                                if (dlcf->ups_info) {\n                                    ctx->result = ngx_array_create(real_r->pool, 2, sizeof(ngx_keyval_t));\n                                    if (ctx->result == NULL) {\n                                        return NGX_ERROR;\n                                    };\n                                    kv = (ngx_keyval_t*)ngx_array_push(ctx->result);\n                                    kv->key = ngx_http_dubbo_str_body;\n                                    kv->value = body;\n\n                                    kv = (ngx_keyval_t*)ngx_array_push(ctx->result);\n                                    kv->key = ngx_http_dubbo_content_type;\n                                    kv->value = ngx_http_dubbo_content_type_text;\n                                } else {\n                                    real_r->upstream->headers_in.status_n = NGX_HTTP_BAD_GATEWAY;\n                                    real_r->upstream->state->status = NGX_HTTP_BAD_GATEWAY;\n                                    ngx_destroy_pool(multi_r->pool);\n                                    return NGX_HTTP_UPSTREAM_PARSE_ERROR;\n                                }\n                            }\n\n                            if (NGX_OK != ngx_http_dubbo_response_handler(pc, real_r, ctx->result)) {\n                                ngx_log_error(NGX_LOG_ERR, dubbo_c->log, 0, \"dubbo: response handler failed %V\", &body);\n                                real_r->upstream->headers_in.status_n = NGX_HTTP_INTERNAL_SERVER_ERROR;\n                                real_r->upstream->state->status = NGX_HTTP_INTERNAL_SERVER_ERROR;\n                                ngx_destroy_pool(multi_r->pool);\n                                return NGX_ERROR;\n                            }\n\n                            ctx->state = ngx_http_dubbo_parse_st_payload;\n                            ngx_destroy_pool(multi_r->pool);\n                            return NGX_HTTP_UPSTREAM_HEADER_END;\n                        }\n                    }\n\n                    if (!find) {\n                        ngx_log_error(NGX_LOG_ERR, dubbo_c->log, 0,\n                                      \"dubbo: response cannot find request %ui\", resp->header.reqid);\n                    }\n\n                    continue;\n                } else {\n                    ngx_log_error(NGX_LOG_INFO, dubbo_c->log, 0, \"dubbo: response parse again\");\n                    break;\n                }\n            }\n\n            return NGX_AGAIN;\n        case ngx_http_dubbo_parse_st_payload:\n            if (multi_c->cur == NULL) {\n                ngx_log_error(NGX_LOG_ERR, dubbo_c->log, 0, \"dubbo [%V]: parse body not found real_r\");\n                break;\n            }\n            real_r = multi_c->cur;\n            real_r->upstream->length = 0;\n            ctx->state = ngx_http_dubbo_parse_st_start;\n            return NGX_HTTP_UPSTREAM_GET_BODY_DATA;\n            //return NGX_ERROR;\n        default:\n            ngx_log_error(NGX_LOG_ERR, dubbo_c->log, 0, \"dubbo: parse state error\");\n            break;\n    }\n\n    return NGX_ERROR;\n}\n\nstatic void\nngx_http_dubbo_cleanup(void *data)\n{\n#if 0\n    ngx_dubbo_connection_t  *dubbo_c = data;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, dubbo_c->log, 0,\n                   \"dubbo cleanup\");\n#endif\n    return;\n}\n\nstatic ngx_int_t\nngx_http_dubbo_get_connection_data(ngx_http_request_t *r,\n        ngx_http_dubbo_ctx_t *ctx, ngx_peer_connection_t *pc)\n{\n    ngx_connection_t        *c;\n    ngx_pool_cleanup_t      *cln;\n\n    c = pc->connection;\n\n    for (cln = c->pool->cleanup; cln; cln = cln->next) {\n        if (cln->handler == ngx_http_dubbo_cleanup) {\n            ctx->connection = cln->data;\n            break;\n        }\n    }\n\n    if (ctx->connection == NULL) {\n        cln = ngx_pool_cleanup_add(c->pool, sizeof(ngx_dubbo_connection_t));\n        if (cln == NULL) {\n            return NGX_ERROR;\n        }\n\n        cln->handler = ngx_http_dubbo_cleanup;\n        ctx->connection = cln->data;\n\n        if(NGX_OK != ngx_dubbo_init_connection(ctx->connection, c, ngx_http_dubbo_ping_handler)) {\n            return NGX_ERROR;\n        }\n\n        ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                      \"dubbo: pc %p create dubbo connection %p\", c, ctx->connection);\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_http_dubbo_ctx_t*\nngx_http_dubbo_get_ctx(ngx_http_request_t *r)\n{\n    ngx_http_dubbo_ctx_t    *ctx;\n    ngx_http_upstream_t     *u;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_dubbo_module);\n\n    if (ctx == NULL) {\n        //need create ctx when fake_r\n        ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_dubbo_ctx_t));\n        if (ctx == NULL) {\n            return NULL;\n        }\n\n        ctx->request = r;\n\n        ngx_http_set_ctx(r, ctx, ngx_http_dubbo_module);\n\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                \"create dubbo ctx on create request, maybe fake_r\");\n    }\n\n    if (ctx->connection == NULL) {\n        u = r->upstream;\n\n        if (ngx_http_dubbo_get_connection_data(r, ctx, &u->peer) != NGX_OK) {\n            return NULL;\n        }\n    }\n\n    return ctx;\n}\n"
  },
  {
    "path": "modules/mod_dubbo/ngx_http_dubbo_module.h",
    "content": "\n/*\n * Copyright (C) Mengqi Wu (Pull)\n * Copyright (C) 2017-2019 Alibaba Group Holding Limited\n */\n\n\n#ifndef _NGX_HTTP_DUBBO_H_\n#define _NGX_HTTP_DUBBO_H_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n#include <ngx_event_connect.h>\n\n#include \"ngx_dubbo.h\"\n\ntypedef struct {\n    ngx_queue_t              queue;\n\n    uint64_t                 reqid;\n\n    ngx_http_request_t      *r;\n\n    ngx_buf_t               *buf;\n\n    void                    *data;\n} ngx_http_dubbo_request_t;\n\n\ntypedef struct {\n    ngx_pool_t                     *pool;   /* response data */\n    void                           *data;\n    ngx_buf_t                       backend_buf;\n\n    ngx_queue_t                     send_list;\n    ngx_queue_t                     wait_list;\n} ngx_http_dubbo_connection_t;\n\nngx_dubbo_connection_t* ngx_http_get_dubbo_connection(ngx_connection_t *pc);\n\nngx_int_t ngx_http_dubbo_parse(ngx_connection_t *c, ngx_chain_t *in);\n\n#endif /* _NGX_HTTP_DUBBO_H_ */\n"
  },
  {
    "path": "modules/mod_dubbo/utils/exceptions.h",
    "content": "#ifndef _HESSIAN_EXCEPTIONS_H_\n#define _HESSIAN_EXCEPTIONS_H_\n\n#include <string>\n\nnamespace hessian {\n\nclass Object;\n\nclass io_exception : public std::exception {\n    public:\n        explicit io_exception(const std::string& what): _message(what) {}\n        virtual ~io_exception() throw() {}\n        const char* what() const throw() { return _message.c_str(); }\n        virtual void raise() const { throw *this; }\n    protected:\n        std::string _message;\n};\n\nclass class_cast_exception : public std::exception {\n    public:\n        explicit class_cast_exception(const std::string& what): _message(what) {}\n        virtual ~class_cast_exception() throw() {}\n        const char* what() const throw() { return _message.c_str(); }\n        virtual void raise() const { throw *this; }\n    protected:\n        std::string _message;\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "modules/mod_dubbo/utils/objects.cc",
    "content": "#include \"objects.h\"\n#include \"utils.h\"\n#include \"hessian2_output.h\"\n#include <sstream>\n\n/*\n * Object implementation\n * @author jifeng\n */\nnamespace hessian {\n\nusing namespace std;\n\n/*\n * maybe not init, cannot use on init\n */\nconst string Boolean::DEFAULT_CLASSNAME(\"boolean\");\nconst string Integer::DEFAULT_CLASSNAME(\"int\");\nconst string Long::DEFAULT_CLASSNAME(\"long\");\nconst string Double::DEFAULT_CLASSNAME(\"double\");\nconst string Date::DEFAULT_CLASSNAME(\"java.util.Date\");\nconst string String::DEFAULT_CLASSNAME(\"java.lang.String\");\nconst string ByteArray::DEFAULT_CLASSNAME(\"[B\"); // byte[]\nconst string List::DEFAULT_CLASSNAME(\"java.util.ArrayList\");\nconst string Map::DEFAULT_CLASSNAME(\"java.util.HashMap\");\nconst string Exception::DEFAULT_CLASSNAME(\"java.lang.RuntimeException\");\n\n/**\n * ObjectValue implementations\n */\npair<Object*, bool> ObjectValue::get_object() const {\n    /*\n     * use get_object() convert value to Object\n     * maybe need delete, need check pair.second\n     */\n    switch (_type) {\n        case OBJ:        return pair<Object*, bool>(_value.obj, false);\n        case C_OBJ:      return pair<Object*, bool>(_value.obj, false);\n        case IVAL:       return pair<Object*, bool>(new Integer(_value.ival), true);\n        case LVAL:       return pair<Object*, bool>(new Long(_value.lval), true);\n        case BVAL:       return pair<Object*, bool>(new Boolean(_value.bval), true);\n        case C_CHAR_PTR: return pair<Object*, bool>(new String(_value.c_char_ptr), true);\n        case C_STR_REF:  return pair<Object*, bool>(new String(_value.c_str_ptr->c_str(), _value.c_str_ptr->size()), true); // \n        case C_STR_PTR:  return pair<Object*, bool>(new String(_value.c_str_ptr), true);\n        case STR_PTR:    return pair<Object*, bool>(new String(_value.str_ptr, false), true);\n        case CVAL:       return pair<Object*, bool>(new String(new string(1, _value.cval), true, \"char\"), true);\n        case SVAL:       return pair<Object*, bool>(new Integer(_value.sval), true);\n        case DVAL:       return pair<Object*, bool>(new Double(_value.dval), true);\n        default:         return pair<Object*, bool>(NULL, false);\n    }\n}\n\n/**\n * Compare Object* in Map\n * sort on type_id first, than use value\n * sort on ptr when not base type\n */\nbool object_ptr_less_comparator::operator() (const Object* const &left, const Object* const &right) const {\n    if (left == NULL) {\n        return right == NULL ? false : true;\n    }\n    if (right == NULL) {\n        return false;\n    }\n\n    uint32_t left_obj_t = left->type_id();\n    uint32_t right_obj_t = right->type_id();\n    if (left_obj_t != right_obj_t) {\n        return left_obj_t < right_obj_t;\n    }\n\n    switch (left_obj_t) {\n        case Object::STRING:\n            return ((String*) left)->to_string() < ((String*) right)->to_string();\n\n        case Object::INTEGER:\n            return ((Integer*) left)->to_int() < ((Integer*) right)->to_int();\n\n        case Object::LONG:\n            return ((Long*) left)->to_long() < ((Long*) right)->to_long();\n\n        case Object::NULL_OBJECT:\n            return false;\n\n        case Object::BOOLEAN:\n            return ((Boolean*) left)->to_bool() < ((Boolean*) right)->to_bool();\n\n        case Object::DOUBLE:\n            return ((Double*) left)->to_double() < ((Double*) right)->to_double();\n\n        case Object::DATE:\n            return ((Date*) left)->to_udc_date() < ((Date*) right)->to_udc_date();\n\n        case Object::BYTE_ARRAY:\n            return ((ByteArray*) left)->to_string() < ((ByteArray*) right)->to_string();\n\n        case Object::LIST:\n        case Object::MAP:\n        default: // require identical equality\n            return ((uintptr_t) left) < ((uintptr_t) right);\n    }\n}\n\n/*\n * extend object type_id\n */\nstatic uint32_t last_ext_object_id = Object::EXT_OBJECT;\nstatic uint32_t last_ext_list_id = Object::EXT_LIST;\nstatic uint32_t last_ext_map_id = Object::EXT_MAP;\n\nuint32_t Object::generate_type_id(ObjectType ext_type) {\n    switch (ext_type) {\n        case Object::EXT_MAP:\n            return ++last_ext_map_id;\n        case Object::EXT_LIST:\n            return ++last_ext_list_id;\n        case Object::EXT_OBJECT:\n        default:\n            return ++last_ext_object_id;\n    }\n}\n\n/*\n * Object implementations\n */\nbool Object::to_bool() const {\n    switch (type_id()) {\n        case BOOLEAN: return ((Boolean*) this)->to_bool();\n        case INTEGER: return ((Integer*) this)->to_int() == 0;\n        case LONG:    return ((Long*) this)->to_long() == 0L;\n        case DOUBLE:  return ((Double*) this)->to_double() == 0.0;\n        case STRING:  return ((String*) this)->to_string() == \"true\";\n        default:\n                      throw class_cast_exception(\"can not cast to bool from class: \" + _classname);\n    }\n}\n\nint32_t Object::to_int() const {\n    switch (type_id()) {\n        case INTEGER: return ((Integer*) this)->to_int();\n        case LONG:    return (int32_t) ((Long*) this)->to_long();\n        case BOOLEAN: return (int32_t) ((Boolean*) this)->to_bool();\n        case DOUBLE:  return (int32_t) ((Double*) this)->to_double();\n        case STRING:  return string_to_int32(((String*) this)->to_string());\n        default:\n                      throw class_cast_exception(\"can not cast to int from class: \" + _classname);\n    }\n}\n\nint64_t Object::to_long() const {\n    switch (type_id()) {\n        case LONG:    return ((Long*) this)->to_long();\n        case INTEGER: return (int64_t) ((Integer*) this)->to_int();\n        case DATE:    return ((Date*) this)->to_udc_date();\n        case BOOLEAN: return (int64_t) ((Boolean*) this)->to_bool();\n        case DOUBLE:  return (int64_t) ((Double*) this)->to_double();\n        case STRING:  return string_to_int64(((String*) this)->to_string());\n        default:\n                      throw class_cast_exception(\"can not cast to long from class: \" + _classname);\n    }\n}\n\ndouble Object::to_double() const {\n    switch (type_id()) {\n        case DOUBLE:  return ((Double*) this)->to_double();\n        case LONG:    return (double) ((Long*) this)->to_long();\n        case INTEGER: return (double) ((Integer*) this)->to_int();\n        case BOOLEAN: return (double) ((Boolean*) this)->to_bool();\n        case STRING:  return string_to_double(((String*) this)->to_string());\n        default:\n                      throw class_cast_exception(\"can not cast to double from class: \" + _classname);\n    }\n}\n\nstring Object::to_string() const {\n    switch (type_id()) {\n        case STRING:     return ((String*) this)->data();\n        case LONG:       return int64_to_string(((Long*) this)->to_long());\n        case INTEGER:    return int32_to_string(((Integer*) this)->to_int());\n        case BOOLEAN:    return bool_to_string(((Boolean*) this)->to_bool());\n        case DOUBLE:     return double_to_string(((Double*) this)->to_double());\n        case DATE:       return int64_to_string(((Date*) this)->to_udc_date());\n        case BYTE_ARRAY: return ((ByteArray*) this)->to_string();\n        default:\n                         throw class_cast_exception(\"can not cast to string from class: \" + _classname);\n    }\n}\n\nList* Object::to_list() {\n    if (!instance_of<List>(this)) {\n        throw class_cast_exception(\"can not cast to list from class: \" + _classname);\n    }\n    return static_cast<List*>(this);\n}\n\nMap* Object::to_map() {\n    if (!instance_of<Map>(this)) {\n        throw class_cast_exception(\"can not cast to map from class: \" + _classname);\n    }\n    return static_cast<Map*>(this);\n}\n\n/*\n * String implementations\n */\nString::String(const char* utf_8_c_str, uint32_t size, const string& classname)\n    : Object(classname, TYPE_ID),\n    _str(new string(utf_8_c_str, size)), _chain_delete(true) {\n    }\n\nString::String(const char* utf_8_c_str, const string& classname)\n    : Object(classname, TYPE_ID),\n    _str(new string(utf_8_c_str)), _chain_delete(true) {\n    }\n\nString::String(const string& utf_8_str, const string& classname)\n    : Object(classname, TYPE_ID),\n    _str(new string(utf_8_str)), _chain_delete(true) {\n    }\n\nString::String(const string* utf_8_str_ptr, const string& classname)\n    : Object(classname, TYPE_ID),\n    _str(const_cast<string*>(utf_8_str_ptr)), _chain_delete(false) {\n    }\n\nString::String(string* utf_8_str_ptr, bool chain_delete, const string& classname)\n    : Object(classname, TYPE_ID),\n    _str(utf_8_str_ptr), _chain_delete(chain_delete) {\n    }\n\nString::~String() {\n    if (_chain_delete) {\n        delete _str;\n    };\n}\n\nstring* String::detach() {\n    if (_chain_delete) {\n        _chain_delete = false;\n        return _str;\n    } else {\n        return NULL;\n    }\n}\n\n/*\n * ByteArray implementations\n */\nByteArray::ByteArray(const char* bytes, uint32_t size, const string& classname)\n    : Object(classname, TYPE_ID),\n    _str(new string(bytes, size)), _chain_delete(true) {\n    }\n\nByteArray::ByteArray(const char* bytes, const string& classname)\n    : Object(classname, TYPE_ID),\n    _str(new string(bytes)), _chain_delete(true) {\n    }\n\nByteArray::ByteArray(const string& byte_str, const string& classname)\n    : Object(classname, TYPE_ID),\n    _str(new string(byte_str)), _chain_delete(true) {\n    }\n\nByteArray::ByteArray(const string* byte_str_ptr, const string& classname)\n    : Object(classname, TYPE_ID),\n    _str(const_cast<string*>(byte_str_ptr)), _chain_delete(false) {\n    }\n\nByteArray::ByteArray(string* byte_str_ptr, bool chain_delete, const string& classname)\n    : Object(classname, TYPE_ID),\n    _str(byte_str_ptr), _chain_delete(chain_delete) {\n    }\n\nByteArray::~ByteArray() {\n    if (_chain_delete) {\n        delete _str;\n    };\n}\n\nstring* ByteArray::detach() {\n    if (_chain_delete) {\n        _chain_delete = false;\n        return _str;\n    } else {\n        return NULL;\n    }\n}\n\n/*\n * List implementations\n */\nList::~List() {\n    for (data_type::iterator it = _delete_chain.begin() ; it != _delete_chain.end(); ++it) {\n        delete (*it);\n    }\n}\n\nvoid List::push_back_ptr(Object* element, bool chain_delete) {\n    if (chain_delete) {\n        _delete_chain.push_back(element);\n    }\n    _list.push_back(element);\n}\n\nvoid List::push_back(const ObjectValue& element, bool chain_delete) {\n    pair<Object*, bool> ret = element.get_object();\n    if (ret.second || chain_delete) {\n        _delete_chain.push_back(ret.first);\n    }\n    _list.push_back(ret.first);\n}\n\nvoid List::add(const ObjectValue& element, bool chain_delete) {\n    push_back(element, chain_delete);\n}\n\nvoid List::set(uint32_t pos, const ObjectValue& value, bool chain_delete) {\n    pair<Object*, bool> ret = value.get_object();\n    if (ret.second || chain_delete) {\n        _delete_chain.push_back(ret.first);\n    }\n    _list.at(pos) = ret.first;\n}\n\nObject* List::get(uint32_t pos) const {\n    return _list.at(pos);\n}\n\nObject* List::operator[] (uint32_t pos) const {\n    return get(pos);\n}\n\nbool List::detach(Object* detachment) {\n    for (vector<Object*>::iterator it = _delete_chain.begin(); it != _delete_chain.end(); ++it) {\n        if ((*it) == detachment) {\n            _delete_chain.erase(it);\n            return true;\n        }\n    }\n    return false;\n}\n\n/*\n * Map implementations\n */\nMap::~Map() {\n    for (vector<Object*>::iterator it = _delete_chain.begin(); it != _delete_chain.end(); ++it) {\n        delete *it;\n    }\n}\n\nvoid Map::put(Object* key, Object* value,\n        bool chain_delete_key, bool chain_delete_value) {\n    if (chain_delete_key) {\n        _delete_chain.push_back(key);\n    }\n    if (chain_delete_value) {\n        _delete_chain.push_back(value);\n    }\n    _map[key] = value;\n}\n\nvoid Map::put(const ObjectValue& key, const ObjectValue& value,\n        bool chain_delete_key, bool chain_delete_value) {\n    pair<Object*, bool> ret_key = key.get_object();\n    pair<Object*, bool> ret_value = value.get_object();\n    if (ret_key.second || chain_delete_key) {\n        _delete_chain.push_back(ret_key.first);\n    }\n    if (ret_value.second || chain_delete_value) {\n        _delete_chain.push_back(ret_value.first);\n    }\n    _map[ret_key.first] = ret_value.first;\n}\n\nObject* Map::get(const ObjectValue& key) const {\n    pair<Object*, bool> ret_key = key.get_object();\n    data_type::const_iterator it = _map.find(ret_key.first);\n    if (ret_key.second) {\n        delete ret_key.first;\n    }\n    if (it == _map.end()) {\n        return NULL;\n    } else {\n        return (*it).second;\n    }\n}\n\nObject* Map::get(const char* c_str_key) const {\n    const string key_str(c_str_key);\n    String key(const_cast<string*>(&key_str), false);\n    data_type::const_iterator it = _map.find(&key);\n    if (it == _map.end()) {\n        return NULL;\n    } else {\n        return (*it).second;\n    }\n}\n\nObject* Map::get_ptr(Object* const key) const {\n    data_type::const_iterator it = _map.find(key);\n    if (it == _map.end()) {\n        return NULL;\n    } else {\n        return (*it).second;\n    }\n}\n\nObject* Map::operator[](const ObjectValue& key) const {\n    return get(key);\n}\n\nbool Map::detach(Object* detachment) {\n    for (vector<Object*>::iterator it = _delete_chain.begin(); it != _delete_chain.end(); ++it) {\n        if ((*it) == detachment) {\n            _delete_chain.erase(it);\n            return true;\n        }\n    }\n    return false;\n}\n\n}\n"
  },
  {
    "path": "modules/mod_dubbo/utils/objects.h",
    "content": "#ifndef _HESSIAN_OBJECTS_H_\n#define _HESSIAN_OBJECTS_H_\n\n#include \"exceptions.h\"\n#include <stdint.h>\n#include <string>\n#include <vector>\n#include <map>\n\nnamespace hessian {\n\nclass List;\nclass Map;\nclass ObjectValue;\n\nclass Object {\n    public:\n        /**\n         * Object type\n         */\n        typedef enum ObjectType {\n            NULL_OBJECT = 0,                      // NULL\n            BOOLEAN, INTEGER, LONG, DOUBLE, DATE, // basic type\n            STRING, BYTE_ARRAY,                   // string byte\n            WEAK_REF,                             // weak object\n\n            //need ref on serializa\n            LIST,                                 // list set\n            MAP,                                  // map hashmap\n            //extend type\n            EXT_OBJECT = MAP + 1,                 // extend object (by serializa)\n            EXT_LIST = 100,                       // extend object (by list)\n            EXT_MAP = 200,                        // extend object (by map)\n            EXCEPTION                             // exception\n        } ObjectType;\n\n        Object(const std::string& classname, uint32_t type_id)\n            : _type_id(type_id), _classname(classname) {}\n\n        virtual ~Object() {}\n\n        /** object type **/\n        uint32_t type_id() const { return _type_id; }\n\n        /** object classname **/\n        const std::string& classname() const { return _classname; }\n        void set_classname(const std::string& classname) { _classname = classname; }\n\n        /*\n         * convert object to basic type (support weak convert, like long->int)\n         * throw exception when failed\n         */\n        bool to_bool() const;\n        int32_t to_int() const;\n        int64_t to_long() const;\n        double to_double() const;\n        std::string to_string() const;\n        List* to_list();\n        Map* to_map();\n\n        /** generate extend object type_id */\n        static uint32_t generate_type_id(ObjectType ext_type);\n\n    private:\n        const uint32_t _type_id;\n\n    protected:\n        std::string _classname;\n\n    private:\n        Object(const Object& other);\n        Object& operator=(const Object& other);\n};\n\ntemplate <class T>\ninline bool instance_of(const Object* obj) {\n    return obj && obj->type_id() == T::TYPE_ID;\n}\n\ntemplate <class T>\ninline bool pointer_of(const Object* obj) {\n    return !obj || obj->type_id() == T::TYPE_ID;\n}\n\n/**\n * null reference, equal in (Object*) NULL\n */\nclass NullObject : public Object {\n    public:\n        typedef void* data_type;\n        static const uint32_t TYPE_ID = NULL_OBJECT;\n        NullObject(const std::string& classname = \"null\") : Object(classname, TYPE_ID) {}\n\n        bool is_null() const { return true; }\n        data_type data() const { return NULL; }\n};\n\n/**\n * bool type, equal Java boolean/Boolean\n */\nclass Boolean : public Object {\n    public:\n        typedef bool data_type;\n        static const uint32_t TYPE_ID = BOOLEAN;\n        static const std::string DEFAULT_CLASSNAME;\n\n        Boolean(data_type value, const std::string& classname = DEFAULT_CLASSNAME)\n            : Object(classname, TYPE_ID), _value(value) {}\n\n        bool to_bool() const { return _value; }\n        data_type data() const { return _value; }\n    protected:\n        data_type _value;\n};\n\n/**\n * int32_t, equal Java byte/int/Integer/short/Short\n */\nclass Integer : public Object {\n    public:\n        typedef int32_t data_type;\n        static const uint32_t TYPE_ID = INTEGER;\n        static const std::string DEFAULT_CLASSNAME;\n\n        Integer(data_type value, const std::string& classname = DEFAULT_CLASSNAME)\n            : Object(classname, TYPE_ID), _value(value) {}\n\n        int32_t to_int() const { return _value; }\n        data_type data() const { return _value; }\n    protected:\n        data_type _value;\n};\n\n/**\n * int64_t, equal Java long/Long\n */\nclass Long : public Object {\n    public:\n        typedef int64_t data_type;\n        static const uint32_t TYPE_ID = LONG;\n        static const std::string DEFAULT_CLASSNAME;\n\n        Long(data_type value, const std::string& classname = DEFAULT_CLASSNAME)\n            : Object(classname, TYPE_ID), _value(value) {}\n\n        int64_t to_long() const { return _value; }\n        data_type data() const { return _value; }\n    protected:\n        data_type _value;\n};\n\n/**\n * double, equal Java double/Double/float/Float\n */\nclass Double : public Object {\n    public:\n        typedef double data_type;\n        static const uint32_t TYPE_ID = DOUBLE;\n        static const std::string DEFAULT_CLASSNAME;\n\n        Double(data_type value, const std::string& classname = DEFAULT_CLASSNAME)\n            : Object(classname, TYPE_ID), _value(value) {}\n\n        double to_double() const { return _value; }\n        data_type data() const { return _value; }\n    protected:\n        data_type _value;\n};\n\n/**\n * date type, equal Java java.util.Date\n * since UTC 1970.1.1\n */\nclass Date : public Object {\n    public:\n        typedef int64_t data_type;\n        static const uint32_t TYPE_ID = DATE;\n        static const std::string DEFAULT_CLASSNAME;\n\n        Date(data_type value, const std::string& classname = DEFAULT_CLASSNAME)\n            : Object(classname, TYPE_ID), _value(value) {}\n\n        int64_t to_udc_date() const { return _value; }\n        data_type data() const { return _value; }\n    protected:\n        data_type _value;\n};\n\n/**\n * string type, equal Java char/String/char[]/Character\n */\nclass String : public Object {\n    public:\n        typedef std::string data_type;\n        static const uint32_t TYPE_ID = STRING;\n        static const std::string DEFAULT_CLASSNAME;\n\n        String(const char* utf_8_c_str, uint32_t size,\n                const std::string& classname = DEFAULT_CLASSNAME);\n\n        String(const char* utf_8_c_str,\n                const std::string& classname = DEFAULT_CLASSNAME);\n\n        String(const std::string& utf_8_str,\n                const std::string& classname = DEFAULT_CLASSNAME);\n\n        String(const std::string* utf_8_str_ptr,\n                const std::string& classname = DEFAULT_CLASSNAME);\n\n        String(std::string* utf_8_str_ptr, bool chain_delete = false,\n                const std::string& classname = DEFAULT_CLASSNAME);\n\n        virtual ~String();\n\n        uint32_t size() const { return _str->size(); }\n\n        std::string to_string() const { return *_str; };\n        const data_type& data() const { return *_str; }\n\n        /*\n         * return real string point, and detach auto recycle\n         * invoker need delete it\n         * return NULL when real string no need auto recycle\n         */\n        std::string* detach();\n    protected:\n        std::string* _str;\n    private:\n        bool _chain_delete;\n};\n\n/**\n * byte type, equal Java byte[]/Byte/\"[B\"\n */\nclass ByteArray : public Object {\n    public:\n        typedef std::string data_type;\n        static const uint32_t TYPE_ID = BYTE_ARRAY;\n        static const std::string DEFAULT_CLASSNAME;\n\n        ByteArray(const char* bytes, uint32_t size,\n                const std::string& classname = DEFAULT_CLASSNAME);\n\n        ByteArray(const char* bytes,\n                const std::string& classname = DEFAULT_CLASSNAME);\n\n        ByteArray(const std::string& byte_str,\n                const std::string& classname = DEFAULT_CLASSNAME);\n\n        ByteArray(const std::string* byte_str_ptr,\n                const std::string& classname = DEFAULT_CLASSNAME);\n\n        ByteArray(std::string* byte_str_ptr, bool chain_delete = false,\n                const std::string& classname = DEFAULT_CLASSNAME);\n\n        virtual ~ByteArray();\n\n        uint32_t size() const { return _str->size(); }\n\n        std::string to_string() const { return *_str; }\n        const data_type& data() const { return *_str; }\n\n        /*\n         * return real string point, and detach auto recycle\n         * invoker need delete it\n         * return NULL when real string no need auto recycle\n         */\n        std::string* detach();\n    protected:\n        std::string* _str;\n    private:\n        bool _chain_delete;\n};\n\n/*\n * ref type\n */\nclass Reference : public Object {\n    public:\n        typedef Object* data_type;\n        static const uint32_t TYPE_ID = WEAK_REF;\n\n        Reference(data_type value)\n            : Object(value->classname(), TYPE_ID), _value(value), _chain_delete(false) {}\n        Reference(data_type value, const std::string& classname, bool chain_delete = false)\n            : Object(classname, TYPE_ID), _value(value), _chain_delete(chain_delete) {}\n\n        ~Reference() { if (_chain_delete) delete _value; }\n\n        data_type data() const { return _value; }\n        data_type detach() { _chain_delete = false; return _value; }\n    protected:\n        data_type _value;\n        bool      _chain_delete;\n};\n\n/**\n * list type, equal Java except char[] and byte[] array\n * and Collection/List/Set/Iterator/Enumeration\n * object like \"[Ljava.lang.Class;\", \"[Ljava.lang.Object;\", \"[Ljava.lang.String;\"\n * maybe like \"[java.lang.Class\", \"[object\", \"[string\",\n * maybe need use Reference replace class name\n * for multidimensional array need multi \"[\". like \"[[int\", \"int[][]\"\n */\nclass List : public Object {\n    public:\n        typedef std::vector<Object*> data_type;\n        static const uint32_t TYPE_ID = LIST;\n        static const std::string DEFAULT_CLASSNAME;\n\n        List(const std::string& classname = DEFAULT_CLASSNAME, uint32_t type_id = TYPE_ID)\n            : Object(classname, type_id) {}\n        virtual ~List();\n\n        void push_back_ptr(Object* element, bool chain_delete = false);\n        void push_back(const ObjectValue& element, bool chain_delete = false);\n        void add(const ObjectValue& element, bool chain_delete = false);\n        void set(uint32_t pos, const ObjectValue& value, bool chain_delete = false);\n        Object* get(uint32_t pos) const;\n\n        const data_type& data() const { return _list; }\n\n        uint32_t size() const { return _list.size(); }\n        void reserve(uint32_t n) { _list.reserve(n); }\n\n        // use operator[] for query purpose only\n        Object* operator[] (uint32_t pos) const;\n\n        /**\n         * delete detachment from List's chain_delete, make it not auto recycle\n         * invoker make sure recycle memory, if detachment is not auto recycle return null\n         */\n        bool detach(Object* detachment);\n    protected:\n        data_type _list;\n        std::vector<Object*> _delete_chain;\n};\n\ntemplate <>\ninline bool instance_of<List>(const Object* obj) {\n    return obj && (obj->type_id() == List::TYPE_ID ||\n            (obj->type_id() >= Object::EXT_LIST && obj->type_id() < Object::EXT_MAP));\n}\n\ntemplate <>\ninline bool pointer_of<List>(const Object* obj) {\n    return !obj || (obj->type_id() == List::TYPE_ID ||\n            (obj->type_id() >= Object::EXT_LIST && obj->type_id() < Object::EXT_MAP));\n}\n\n/**\n * Object* comparer on Map \n */\nclass object_ptr_less_comparator {\n    public:\n        bool operator() (const Object* const &left, const Object* const &right) const;\n};\n\n/**\n * map type, equal Java Map type\n */\nclass Map : public Object {\n    public:\n        typedef std::map<Object*, Object*, object_ptr_less_comparator> data_type;\n        static const uint32_t TYPE_ID = MAP;\n        static const std::string DEFAULT_CLASSNAME;\n\n        Map(const std::string& classname = DEFAULT_CLASSNAME, uint32_t type_id = TYPE_ID)\n            : Object(classname, type_id) {}\n        virtual ~Map();\n\n        void put(Object* key, Object* value,\n                bool chain_delete_key = false, bool chain_delete_value = false);\n        void put(const ObjectValue& key, const ObjectValue& value,\n                bool chain_delete_key = false, bool chain_delete_value = false);\n        Object* get(const ObjectValue& key) const;\n        Object* get(const char* c_str_key) const;\n        Object* get_ptr(Object* const key) const;\n\n        const data_type& data() const { return _map; }\n\n        uint32_t size() const { return _map.size(); }\n\n        // use operator[] for query purpose only\n        Object* operator[](const ObjectValue& key) const;\n\n        /**\n         * remote detachment from Map chain_delete, make it not auto recycle\n         * invoker make sure delete detachment\n         * if detachment is not auto recycle, may return false\n         */\n        bool detach(Object* detachment);\n    protected:\n        data_type _map;\n        std::vector<Object*> _delete_chain;\n};\n\ntemplate <>\ninline bool instance_of<Map>(const Object* obj) {\n    return obj && (obj->type_id() == Map::TYPE_ID || obj->type_id() >= Object::EXT_MAP);\n}\n\ntemplate <>\ninline bool pointer_of<Map>(const Object* obj) {\n    return !obj || (obj->type_id() == Map::TYPE_ID || obj->type_id() >= Object::EXT_MAP);\n}\n\n/**\n * convert basic type to Object*\n */\nclass ObjectValue {\n    public:\n        ObjectValue(bool    bval) : _type(BVAL) { _value.bval = bval; }\n        ObjectValue(int8_t  cval) : _type(CVAL) { _value.cval = cval; }\n        ObjectValue(int16_t sval) : _type(SVAL) { _value.sval = sval; }\n        ObjectValue(int32_t ival) : _type(IVAL) { _value.ival = ival; }\n        ObjectValue(int64_t lval) : _type(LVAL) { _value.lval = lval; }\n        ObjectValue(double  dval) : _type(DVAL) { _value.dval = dval; }\n\n        ObjectValue(const char* c_char_ptr) : _type(C_CHAR_PTR) { _value.c_char_ptr = c_char_ptr; }\n        ObjectValue(const std::string& str) : _type(C_STR_REF) { _value.c_str_ptr = &str; }\n        ObjectValue(const std::string* c_str_ptr) : _type(C_STR_PTR) { _value.c_str_ptr = c_str_ptr; }\n        ObjectValue(std::string* str_ptr) : _type(STR_PTR) { _value.str_ptr = str_ptr; }\n\n        ObjectValue(Object* obj) : _type(OBJ) { _value.obj = obj; }\n        ObjectValue(const Object* c_obj) : _type(C_OBJ) { _value.c_obj = c_obj; }\n\n        /**\n         * return ObjectValue for Object\n         * pair.second mark Object need auto recycle\n         */\n        std::pair<Object*, bool> get_object() const;\n\n    private:\n        union {\n            bool    bval;\n            int8_t  cval;\n            int16_t sval;\n            int32_t ival;\n            int64_t lval;\n            double  dval;\n\n            const char*   c_char_ptr;\n            const std::string* c_str_ptr;\n            std::string*       str_ptr;\n            Object*       obj;\n            const Object* c_obj;\n        } _value;\n\n        enum object_data_type {\n            BVAL, CVAL, SVAL, IVAL, LVAL, DVAL,\n            C_CHAR_PTR, C_STR_REF, C_STR_PTR, STR_PTR,\n            OBJ, C_OBJ\n        } _type;\n};\n\n/* ============================================================================\n * extend Java Object\n * ========================================================================= */\n/**\n * exception object\n */\nclass Exception : public Map {\n    public:\n        static const std::string DEFAULT_CLASSNAME;\n        static const uint32_t TYPE_ID = EXCEPTION;\n\n        Exception(const std::string& detail_message = \"\", const std::string& classname = DEFAULT_CLASSNAME)\n            : Map(classname, TYPE_ID), _detail_message(detail_message),\n            _stack_trace(NULL), _cause(NULL) {}\n        virtual ~Exception() {}\n\n        const char* what() const { return _detail_message.c_str(); }\n\n        void set_cause(Exception* cause, bool chain_delete = false) {\n            _cause = cause; if (cause && chain_delete) _delete_chain.push_back(cause); }\n        const Exception* cause() const { return _cause; }\n\n        void set_detail_message(const std::string& message) { _detail_message = message; }\n        std::string* mutable_detail_message() { return &_detail_message; }\n        const std::string& detail_message() const { return _detail_message; }\n\n        void set_stack_trace(List* stack_trace, bool chain_delete = false) {\n            _stack_trace = stack_trace; if (stack_trace && chain_delete) _delete_chain.push_back(stack_trace); }\n        const List* stack_trace() const { return _stack_trace; }\n\n    protected:\n        std::string _detail_message;\n        List*       _stack_trace; // List<String*>*\n        Exception*  _cause;\n};\n\n/**\n * warp C/C++ type to Object\n */\ntemplate <class T>\nclass ExtObject : public Object {\n    public:\n        typedef T data_type;\n        static const uint32_t TYPE_ID;\n        static const std::string DEFAULT_CLASSNAME;\n\n        ExtObject(T* ptr,\n                bool chain_delete = false,\n                const std::string& classname = DEFAULT_CLASSNAME)\n            : Object(classname, TYPE_ID), _ptr(ptr), _chain_delete(chain_delete) {}\n\n        ~ExtObject() { if (_chain_delete) delete _ptr; }\n\n        T* data() const { return _ptr; }\n        void set_data(T* ptr) { if (_chain_delete) { delete _ptr; } _ptr = ptr; }\n    protected:\n        T*   _ptr;\n        bool _chain_delete;\n};\n\n}\n#endif\n"
  },
  {
    "path": "modules/mod_dubbo/utils/utils.cc",
    "content": "#ifndef __STDC_FORMAT_MACROS\n#define __STDC_FORMAT_MACROS\n#endif\n\n#include \"utils.h\"\n#include <string>\n#include <sstream>\n#include <ostream>\n#include <inttypes.h>\n\nnamespace hessian {\n\nusing namespace std;\n\nint ngx_hessian_is_big_endian() {\n    const int n = 1;\n    if(*(char *)&n) {\n        return 0;\n    }\n    return 1;\n}\n\nunion double_int64 {\n    double  dval;\n    int64_t lval;\n};\n\nint64_t double_to_long(double dval) {\n    // *(int64_t*)(&d_val)\n    union double_int64 x;\n    x.dval = dval;\n    return x.lval;\n}\n\ndouble long_to_double(int64_t lval) {\n    // *(double*)(&lval)\n    union double_int64 x;\n    x.lval = lval;\n    return x.dval;\n}\n\nstring bool_to_string(bool bval) {\n    return bval ? string(\"true\") : string(\"false\");\n}\n\nstring int32_to_string(int32_t ival) {\n    char buff[32];\n    return string(buff, snprintf(buff, sizeof(buff), \"%\" PRId32, ival));\n}\n\nstring int64_to_string(int64_t lval) {\n    char buff[32];\n    return string(buff, snprintf(buff, sizeof(buff), \"%\" PRId64, lval));\n}\n\nstring double_to_string(double dval) {\n    char buff[32];\n    return string(buff, snprintf(buff, sizeof(buff), \"%f\", dval));\n}\n\nint32_t cstr_to_int32(const char* cstr) {\n    return strtol(cstr, NULL, 0);\n}\n\nint64_t cstr_to_int64(const char* cstr) {\n#if __WORDSIZE == 64\n    return strtol(cstr, NULL, 0);\n#else\n    return strtoll(cstr, NULL, 0);\n#endif\n}\n\ndouble cstr_to_double(const char* cstr) {\n    return strtod(cstr, NULL);\n}\n\nstring to_hex_string(const void* ch, size_t size) {\n    static char alpha[] = \"0123456789ABCDEF\";\n    string hex_str;\n    hex_str.reserve(size * 3);\n    for (size_t i = 0; i < size; ++i) {\n        uint8_t c = *((const uint8_t*)ch + i);\n        hex_str.push_back(alpha[c >> 4]);\n        hex_str.push_back(alpha[c & 0xF]);\n        hex_str.push_back(' ');\n    }\n\n    return hex_str;\n}\n\nvoid write_hex_to_stream(ostream& os, const void* ch, size_t size) {\n    static char alpha[] = \"0123456789ABCDEF\";\n    for (size_t i = 0; i < size; ++i) {\n        uint8_t c = *((const uint8_t*)ch + i);\n        os << alpha[c >> 4] << alpha[c & 0xF] << ' ';\n    }\n}\n\n/**\n * debug function \n * @param caption title, not output when NULL\n * @param ptr start pos need output\n * @param len len nedd output\n */\nvoid hexdump(const char* caption, const void* ptr, unsigned int len) {\n    unsigned char* buf = (unsigned char*) ptr;\n    unsigned int i, j;\n    if (caption)\n        printf(\"\\n%s (%p@%d)\\n\", caption, ptr, len);\n    for (i=0; i < len; i+=16) {\n        printf(\"%08x  \", i);\n        for (j=0; j<8; j++)\n            if (i+j < len)\n                printf(\"%02x \", buf[i+j]);\n            else\n                printf(\"   \");\n        printf(\" \");\n        for (; j<16; j++)\n            if (i+j < len)\n                printf(\"%02x \", buf[i+j]);\n            else\n                printf(\"   \");\n        printf(\" |\");\n        for (j=0; j<16; j++)\n            if (i+j < len)\n                printf(\"%c\", isprint(buf[i+j]) ? buf[i+j] : '.');\n        printf(\"|\\n\");\n    }\n    printf(\"%08x\\n\", len);\n}\n}\n\n"
  },
  {
    "path": "modules/mod_dubbo/utils/utils.h",
    "content": "#ifndef DUBBO_UTILS_H\n#define DUBBO_UTILS_H\n\n#ifndef __STDC_LIMIT_MACROS\n#define __STDC_LIMIT_MACROS\n#endif\n\n#include <stdint.h>\n#include <vector>\n#include <string>\n#include \"ngx_config.h\"\n\nnamespace hessian {\n\nint ngx_hessian_is_big_endian();\n\n#define ngx_hessian_swap64(val) (((val) >> 56)   |\\\n        (((val) & 0x00ff000000000000ll) >> 40) |\\\n        (((val) & 0x0000ff0000000000ll) >> 24) |\\\n        (((val) & 0x000000ff00000000ll) >> 8)  |\\\n        (((val) & 0x00000000ff000000ll) << 8)  |\\\n        (((val) & 0x0000000000ff0000ll) << 24) |\\\n        (((val) & 0x000000000000ff00ll) << 40) |\\\n        (((val) << 56)))\n\n#define ngx_hessian_hton64(val) ngx_hessian_is_big_endian() ? val : ngx_hessian_swap64(val)\n#define ngx_hessian_ntoh64(val) ngx_hessian_hton64(val)\n\n#define CONST_C_STRING(const_c_str) const_c_str, sizeof(const_c_str) - 1\n\nint64_t double_to_long(double dval);\ndouble long_to_double(int64_t lval);\n\ninline bool string_ends_with(const std::string& target, const std::string& end) {\n    int pos = target.size() - end.size();\n    return pos >= 0 && ((int)target.rfind(end, pos)) == pos;\n}\n\n/*\n * string convert\n */\nstd::string bool_to_string(bool bval);\nstd::string int32_to_string(int32_t ival);\nstd::string int64_to_string(int64_t lval);\nstd::string double_to_string(double dval);\n\nint32_t cstr_to_int32(const char* cstr);\ninline int32_t string_to_int32(const std::string& str) { return cstr_to_int32(str.c_str()); };\nint64_t cstr_to_int64(const char* cstr);\ninline int64_t string_to_int64(const std::string& str) { return cstr_to_int64(str.c_str()); };\ndouble cstr_to_double(const char* cstr);\ninline double string_to_double(const std::string& str) { return cstr_to_double(str.c_str()); };\n\nstd::string to_hex_string(const void* ch, size_t size);\nvoid write_hex_to_stream(std::ostream& os, const void* ch, size_t size);\n\n/**\n * debug function, output hex\n * @param caption title, not output when NULL\n * @param ptr start pos when output\n * @param len len when output\n */\nvoid hexdump(const char* caption, const void* ptr, unsigned int len);\n\ntemplate <class T>\nclass Safeguard {\n    public:\n        Safeguard(): m_pt(NULL) { }\n        Safeguard(T* pt): m_pt(pt) { }\n        ~Safeguard() { if (m_pt != NULL) delete m_pt; }\n\n        void reset(T* pt) { m_pt = pt; }\n        void release() { m_pt = NULL; }\n    private:\n        T* m_pt;\n};\n\n}\n\n#endif\n"
  },
  {
    "path": "modules/mod_strategy/config",
    "content": "\n\n# proc for strategy sync\nPROCS_MODULES=\"$PROCS_MODULES ngx_proc_strategy_module\"\nNGX_ADDON_SRCS=\"$NGX_ADDON_SRCS $ngx_addon_dir/ngx_proc_strategy_module.c\"\n\nHTTP_INCS=\"$HTTP_INCS $ngx_addon_dir\"\n\nHTTP_AUX_FILTER_MODULES=\"$HTTP_AUX_FILTER_MODULES ngx_http_strategy_module\"\nNGX_ADDON_SRCS=\"$NGX_ADDON_SRCS \\\n\t\t$ngx_addon_dir/ngx_http_strategy_module.c\"\n"
  },
  {
    "path": "modules/mod_strategy/ngx_http_strategy_module.c",
    "content": "\n /*\n * Copyright (C) 2010-2019 Alibaba Group Holding Limited\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n#include <nginx.h>\n\n#include <ngx_proc_strategy_module.h>\n\nstatic char * ngx_http_strategy_init_main_conf(ngx_conf_t *cf, void *conf);\nstatic char * ngx_http_strategy_zone(ngx_conf_t *cf, void *conf);\nstatic ngx_int_t ngx_http_strategy_module_init(ngx_cycle_t *cycle);\n\nextern ngx_module_t ngx_proc_strategy_module;\n\nstatic ngx_command_t  ngx_http_strategy_commands[] = {\n      ngx_null_command\n};\n\nstatic ngx_http_module_t  ngx_http_strategy_module_ctx = {\n    NULL,                                   /* preconfiguration */\n    NULL,                                   /* postconfiguration */\n\n    NULL,                                   /* create main configuration */\n    ngx_http_strategy_init_main_conf,       /* init main configuration */\n\n    NULL,                                   /* create server configuration */\n    NULL,                                   /* merge server configuration */\n\n    NULL,                                   /* create location configration */\n    NULL                                    /* merge location configration */\n};\n\nngx_module_t  ngx_http_strategy_module = {\n    NGX_MODULE_V1,\n    &ngx_http_strategy_module_ctx,          /* module context */\n    ngx_http_strategy_commands,             /* module directives */\n    NGX_HTTP_MODULE,                        /* module type */\n    NULL,                                   /* init master */\n    ngx_http_strategy_module_init,          /* 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\nstatic char *\nngx_http_strategy_init_main_conf(ngx_conf_t *cf, void *conf)\n{\n    return ngx_http_strategy_zone(cf, conf);\n}\n\nstatic ngx_int_t\nngx_http_strategy_init_zone(ngx_shm_zone_t *shm_zone, void *data)\n{\n    ngx_slab_pool_t                 *shpool;\n    ngx_strategy_frame_app_t        *app;\n\n    shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;\n\n    app = shm_zone->data;\n    if (app == NULL) {\n        ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, 0,\n                \"[strategy] init zone app is null\");\n        return NGX_ERROR;\n    }\n\n    app->ctx.slab = shpool;\n\n    return NGX_OK;\n}\n\nstatic char *\nngx_http_strategy_zone(ngx_conf_t *cf, void *conf)\n{\n    ngx_str_t               shm_name;\n    ngx_shm_zone_t          *shm_zone;\n\n    ngx_proc_strategy_main_conf_t   *smcf;\n    ngx_strategy_frame_app_t        *app;\n    ngx_uint_t                       i;\n\n    static ngx_int_t ngx_http_strategy_shm_generation = 0;\n\n    smcf = ngx_proc_get_main_conf(cf->cycle->conf_ctx, ngx_proc_strategy_module);\n    if (smcf == NULL) {\n        return NGX_CONF_OK;\n    }\n\n    smcf->already_init = 1;\n    \n    app = smcf->frame_apps.elts;\n    for (i = 0; i < smcf->frame_apps.nelts; i++) {\n        /* Shared memory size is 0, skip create */\n        if (app[i].ctx.shm_size == 0) {\n            continue;\n        }\n\n        shm_name.data = ngx_pnalloc(cf->pool, sizeof(\"strategy_zone_#\") + NGX_INT_T_LEN + app[i].ctx.name.len);\n        \n        if (shm_name.data == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        shm_name.len = ngx_sprintf(shm_name.data, \"strategy_zone_%V#%ui\",\n                                &app[i].ctx.name,\n                                ngx_http_strategy_shm_generation)\n                    - shm_name.data;\n\n        shm_zone = ngx_shared_memory_add(cf, &shm_name, app[i].ctx.shm_size,\n                                        &ngx_http_strategy_module);\n        if (shm_zone == NULL) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                            \"[strategy] ngx_shared_memory_add failed: %V\", &shm_name);\n            return NGX_CONF_ERROR;\n        }\n\n        if (shm_zone->data) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                            \"[strategy] duplicate zone \\\"%V\\\"\", &shm_name);\n            return NGX_CONF_ERROR;\n        }\n\n        shm_zone->init = ngx_http_strategy_init_zone;\n        shm_zone->data = &app[i];\n    }\n\n    ngx_http_strategy_shm_generation ++;\n\n    return NGX_CONF_OK;\n}\n\nstatic ngx_int_t ngx_http_strategy_module_init(ngx_cycle_t *cycle)\n{\n    ngx_proc_strategy_main_conf_t   *smcf;\n    ngx_strategy_frame_app_t        *app;\n    ngx_uint_t                       i;\n    ngx_int_t                        rc;\n\n    smcf = ngx_proc_get_main_conf(cycle->conf_ctx, ngx_proc_strategy_module);\n    if (smcf == NULL) {\n        return NGX_OK;\n    }\n\n    app = smcf->frame_apps.elts;\n    for (i = 0; i < smcf->frame_apps.nelts; i++) {\n        if (app[i].app_init == NULL) {\n            continue;\n        }\n\n        rc = app[i].app_init(&app[i].ctx, cycle, app[i].ctx.slab);\n        if (rc != NGX_OK) {\n            ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,\n                    \"[strategy] init app:%V failed\", &app[i].ctx.name);\n            \n            return NGX_ERROR;\n        }\n    }\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "modules/mod_strategy/ngx_proc_strategy_module.c",
    "content": "\n /*\n * Copyright (C) 2010-2019 Alibaba Group Holding Limited\n */\n\n\n#include \"ngx_proc_strategy_module.h\"\n\nstatic ngx_int_t ngx_proc_strategy_prepare(ngx_cycle_t *cycle);\nstatic void ngx_proc_strategy_exit_worker(ngx_cycle_t *cycle);\nstatic ngx_int_t ngx_proc_strategy_init_worker(ngx_cycle_t *cycle);\nstatic void * ngx_proc_strategy_create_main_conf(ngx_conf_t *cf);\n\nstatic ngx_command_t ngx_proc_strategy_commands[] = {\n    ngx_null_command\n};\n\n\nstatic ngx_proc_module_t ngx_proc_strategy_module_ctx = {\n    ngx_string(\"strategy\"),\n    ngx_proc_strategy_create_main_conf,\n    NULL,\n    NULL,\n    NULL,\n    ngx_proc_strategy_prepare,\n    ngx_proc_strategy_init_worker,\n    NULL,\n    ngx_proc_strategy_exit_worker\n};\n\n\nngx_module_t ngx_proc_strategy_module = {\n    NGX_MODULE_V1,\n    &ngx_proc_strategy_module_ctx,\n    ngx_proc_strategy_commands,\n    NGX_PROC_MODULE,\n    NULL,\n    NULL,\n    NULL,\n    NULL,\n    NULL,\n    NULL,\n    NULL,\n    NGX_MODULE_V1_PADDING\n};\n\n\nextern ngx_module_t ngx_http_strategy_module;\n\n\nstatic ngx_int_t\nngx_proc_strategy_prepare(ngx_cycle_t *cycle)\n{\n    return NGX_OK;\n}\n\nstatic void\nngx_strategy_timer_handler(ngx_event_t *ev)\n{\n    ngx_strategy_frame_app_t    *frame_app = ev->data;\n    ngx_int_t                   rc;\n\n    if (ngx_exiting || ngx_quit) {\n        return;\n    }\n\n    if (frame_app->ctx.interval == NGX_CONF_UNSET_MSEC) {\n        ngx_log_error(NGX_LOG_WARN, ngx_cycle->log, 0, \"[strategy] callback interval not set:%V\", &frame_app->ctx.name);\n        return;\n    }\n\n    if (frame_app->app_callback == NULL) {\n        ngx_log_error(NGX_LOG_WARN, ngx_cycle->log, 0, \"[strategy] app_callback not set:%V\", &frame_app->ctx.name);\n        return;\n    }\n\n    rc = frame_app->app_callback(&frame_app->ctx);\n    if (rc != NGX_OK) {\n        ngx_log_error(NGX_LOG_WARN, ngx_cycle->log, 0, \"[strategy] app_callback error:%V\", &frame_app->ctx.name);\n    } else if (frame_app->ctx.interval > 0) {\n        ngx_memzero(&frame_app->ctx.tm_event, sizeof(ngx_event_t));\n        frame_app->ctx.tm_event.handler = ngx_strategy_timer_handler;\n        frame_app->ctx.tm_event.log = ngx_cycle->log;\n        frame_app->ctx.tm_event.data = frame_app;\n        ngx_add_timer(&frame_app->ctx.tm_event, frame_app->ctx.interval);\n    }\n}\n\nstatic ngx_int_t\nngx_proc_strategy_init_worker(ngx_cycle_t *cycle)\n{\n    ngx_uint_t                     i;\n    \n    ngx_proc_strategy_main_conf_t  *dmcf;\n    ngx_strategy_sync_app_t     *app;\n    ngx_strategy_frame_app_t    *frame_app;\n\n    dmcf = ngx_proc_get_main_conf(cycle->conf_ctx, ngx_proc_strategy_module);\n\n    ngx_log_error(NGX_LOG_WARN, cycle->log, 0, \"[strategy] init app : normal_app num=%d\", dmcf->apps.nelts);\n\n    /* normal app */\n    app = dmcf->apps.elts;\n    for (i = 0; i < dmcf->apps.nelts; i++) {\n        app[i].init(app[i].data);\n    }\n\n    /* frame app */\n    ngx_log_error(NGX_LOG_WARN, cycle->log, 0, \"[strategy] init app : frame_app num=%d\", dmcf->frame_apps.nelts);\n\n    frame_app = dmcf->frame_apps.elts;\n    for (i = 0; i < dmcf->frame_apps.nelts; i++) {\n        if (frame_app[i].app_callback == NULL) {\n            continue;\n        }\n        \n        frame_app[i].ctx.tm_event.data = &frame_app[i];\n\n        ngx_strategy_timer_handler(&frame_app[i].ctx.tm_event);\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_proc_strategy_exit_worker(ngx_cycle_t *cycle)\n{\n    ngx_proc_strategy_main_conf_t  *dmcf;\n    ngx_strategy_frame_app_t *app;\n    ngx_uint_t                   i;\n    ngx_int_t                    rc;\n\n    dmcf = ngx_proc_get_main_conf(cycle->conf_ctx, ngx_proc_strategy_module);\n\n    app = dmcf->frame_apps.elts;\n    for (i = 0; i < dmcf->frame_apps.nelts; i++) {\n        if (app[i].app_uninit == NULL) {\n            continue;\n        }\n\n        rc = app[i].app_uninit(&app[i].ctx, cycle);\n        if (rc != NGX_OK) {\n            ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,\n                    \"[strategy] app=%V uninit failed\", &app[i].ctx.name);\n        }\n    }\n}\n\nngx_int_t\nngx_strategy_register(ngx_conf_t *cf, ngx_strategy_init_func init, void *data)\n{\n    ngx_strategy_sync_app_t * app;\n    ngx_proc_strategy_main_conf_t * umcf;\n   \n    umcf = ngx_proc_get_main_conf(cf->cycle->conf_ctx, ngx_proc_strategy_module);\n    if (umcf == NULL) {\n        return NGX_ERROR;\n    }\n\n    app = ngx_array_push(&umcf->apps);\n    if (app == NULL) {\n        return NGX_ERROR;\n    }\n\n    app->init = init;\n    app->data = data;\n\n    return NGX_OK;\n}\n\n\nstatic void *\nngx_proc_strategy_create_main_conf(ngx_conf_t *cf)\n{\n    ngx_proc_strategy_main_conf_t  *conf;\n    ngx_int_t rc = NGX_OK;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_proc_strategy_main_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    rc = ngx_array_init(&conf->apps, cf->pool, 37, sizeof(ngx_strategy_sync_app_t));\n    if (rc != NGX_OK) {\n        return NULL;\n    }\n\n    rc = ngx_array_init(&conf->frame_apps, cf->pool, 37, sizeof(ngx_strategy_frame_app_t));\n    if (rc != NGX_OK) {\n        return NULL;\n    }\n\n    conf->already_init = 0;\n\n    return conf;\n}\n\n\nngx_int_t ngx_check_strategy_process(ngx_conf_t *cf)\n{\n    ngx_proc_main_conf_t           *cmcf;\n    ngx_proc_conf_t                **cpcfp;\n    ngx_uint_t                       i;\n\n    cmcf = ngx_proc_get_main_conf(cf->cycle->conf_ctx, ngx_proc_core_module);\n    if (cmcf == NULL) {\n        return NGX_ERROR;\n    }\n\n    cpcfp = cmcf->processes.elts;\n    for (i = 0; i < cmcf->processes.nelts; i++) {\n        if (ngx_strcmp(cpcfp[i]->name.data, \"strategy\") == 0) {\n            break;\n        }\n    }\n\n    if (i == cmcf->processes.nelts) {\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\nngx_int_t\nngx_strategy_frame_register(ngx_conf_t *cf, ngx_strategy_frame_app_t * inapp)\n{\n    ngx_strategy_frame_app_t        *app;\n    ngx_proc_strategy_main_conf_t   *umcf;\n    ngx_int_t                       rc;\n   \n    umcf = ngx_proc_get_main_conf(cf->cycle->conf_ctx, ngx_proc_strategy_module);\n    if (umcf == NULL) {\n        return NGX_ERROR;\n    }\n\n    rc = ngx_check_strategy_process(cf);\n    if (rc != NGX_OK) {\n        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                \"[strategy] no \\\"process strategy {}\\\" in proc configuration\");\n        return NGX_ERROR;\n    }\n\n    /* The registration behavior must be created before the shared memory */\n    if (umcf->already_init) {\n        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                \"[strategy] already init when call register, pls adjust module build order\");\n        return NGX_ERROR;\n    }\n\n    app = ngx_array_push(&umcf->frame_apps);\n    if (app == NULL) {\n        return NGX_ERROR;\n    }\n\n    app->ctx.name.data = ngx_palloc(cf->pool, inapp->ctx.name.len);\n    if (app->ctx.name.data == NULL) {\n        ngx_log_error(NGX_LOG_WARN, cf->log, 0,\n                \"[strategy] alloc name memory failed: %V\", &inapp->ctx.name);\n\n        return NGX_ERROR;\n    }\n\n    memcpy(app->ctx.name.data, inapp->ctx.name.data, inapp->ctx.name.len);\n\n    app->ctx.name.len = inapp->ctx.name.len;\n\n    app->ctx.shm_size = inapp->ctx.shm_size;\n    app->ctx.interval = inapp->ctx.interval;\n    app->ctx.data     = inapp->ctx.data;\n\n    app->app_init = inapp->app_init;\n    app->app_uninit = inapp->app_uninit;\n    app->app_callback = inapp->app_callback;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_strategy_slot_init_elem(ngx_strategy_slot_app_t * slot_app,\n        ngx_strategy_slot_ctx_t *ctx,\n        ngx_cycle_t *cycle,\n        ngx_slab_pool_t * slab)\n{\n    u_char * addr;\n\n    ngx_int_t   max_pool_size = slot_app->pool_size + sizeof(ngx_shm_pool_t);\n\n    ctx->data = ngx_slab_alloc(slab, slot_app->slot_size);\n    if (ctx->data == NULL) {\n        ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,\n                 \"[strategy] slab alloc ctx failed\");\n        return NGX_ERROR;\n    }\n\n    addr = ngx_slab_alloc(slab, max_pool_size);\n    if (addr == NULL) {\n        ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,\n                 \"[strategy] slab alloc failed: max_pool_size=%d\", max_pool_size);\n        return NGX_ERROR;\n    }\n\n    ctx->pool = ngx_shm_create_pool(addr, max_pool_size);\n    if (ctx->pool == NULL) {\n        ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,\n                 \"[strategy] pool create failed: max_pool_size=%d\", max_pool_size);\n        return NGX_ERROR;\n    }\n\n    if (slot_app->update == NULL) {\n        ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,\n                 \"[strategy] slot update not set\");\n        return NGX_ERROR;\n    }\n\n    ctx->valid = 0;\n\n    return NGX_OK;\n}\n\nstatic ngx_int_t\nngx_strategy_slot_init(ngx_strategy_frame_ctx_t * ctx,\n        ngx_cycle_t *cycle, ngx_slab_pool_t * slab)\n{\n    ngx_strategy_slot_app_t *slot_app;\n    ngx_int_t               rc, i;\n\n    slot_app = ctx->data;\n    if (slot_app == NULL) {\n        ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,\n                \"[strategy] slot_init: slot_app is null\");\n        return NGX_ERROR;\n    }\n\n    slot_app->shm_ctx = ngx_slab_alloc(slab, sizeof(ngx_strategy_slot_shm_ctx_t));\n    if (slot_app->shm_ctx == NULL) {\n        ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,\n                \"[strategy] slot_init: shm_ctx alloc failed: appname=%V\", &ctx->name);\n        return NGX_ERROR;\n    }\n\n    slot_app->shm_ctx->current = 0;\n\n    for (i = 0; i < 2; i++) {\n        ngx_strategy_slot_ctx_t *current;\n\n        current = &slot_app->shm_ctx->slots[i];\n\n        rc = ngx_strategy_slot_init_elem(slot_app, current, cycle, slab);\n        if (rc == NGX_ERROR) {\n            ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,\n                    \"[strategy] init slot %d failed: %V\", i, &ctx->name);\n            return rc;\n        }\n\n        rc = slot_app->update(cycle, slot_app->data, current->pool, current->data, 0);\n        if (rc != NGX_OK) {\n            ngx_log_error(NGX_LOG_WARN, cycle->log, 0,\n                    \"[strategy] update slot %d failed: %V\", i, &ctx->name);\n        } else {\n            current->valid = 1;\n        }\n    }\n    \n    return NGX_OK;\n}\n\nstatic ngx_int_t\nngx_strategy_slot_callback(ngx_strategy_frame_ctx_t * ctx)\n{\n    ngx_strategy_slot_app_t     *slot_app;\n    ngx_int_t                   rc;\n    ngx_strategy_slot_ctx_t     *reserved;\n    ngx_int_t                   need_update = 0;\n\n    slot_app = ctx->data;\n    if (slot_app == NULL || slot_app->shm_ctx == NULL) {\n        ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, 0,\n                \"[strategy] slot_callback failed: slot_app or shm_ctx is null: slot_app=%p\", slot_app);\n        return NGX_ERROR;\n    }\n    \n    /* 1. Check for updates and rebuild if there are updates */\n    reserved = &slot_app->shm_ctx->slots[(slot_app->shm_ctx->current + 1) % 2];\n\n    if (slot_app->check_update == NULL) {\n        need_update = 1;\n    }\n    else {\n        check_update_status status = slot_app->check_update(slot_app->data, reserved->data);\n        if (status == STATUS_CHECK_UPDATE_FAILED) {\n            ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, 0,\n                    \"[strategy][monitor] update config failed: appname=%V\", &ctx->name);\n        }\n        else if (status == STATUS_CHECK_NEED_UPDATE) {\n            need_update = 1;\n        }\n    }\n\n    if (need_update) {\n        rc = slot_app->update((ngx_cycle_t *)ngx_cycle,\n            slot_app->data, reserved->pool,\n            reserved->data, slot_app->print_detail);\n        if (rc != NGX_OK) {\n            ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, 0,\n                 \"[strategy] update failed: appname=%V\", &ctx->name);\n            return NGX_OK;\n        }\n\n        if (ngx_shm_pool_used_rate(reserved->pool) >= slot_app->shm_warn_mem_rate) {\n            ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, 0,\n                \"[strategy][monitor] space: appname=%V, shm_size=%d, free=%d, used_rate=%d\",\n                &ctx->name,\n                ngx_shm_pool_size(reserved->pool),\n                ngx_shm_pool_free_size(reserved->pool),\n                ngx_shm_pool_used_rate(reserved->pool)\n                );\n        }\n       \n        reserved->valid = 1;\n    }\n\n    /* 2. If there is an update switch the memory block */\n    if (need_update) {\n        slot_app->shm_ctx->current = (slot_app->shm_ctx->current + 1) % 2;\n        ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, 0,\n                 \"[strategy] update area rule: appname=%V, current=%d\",\n                 &ctx->name, slot_app->shm_ctx->current);\n    }\n\n    return NGX_OK;\n}\n\nngx_strategy_slot_app_t*\nngx_strategy_slot_app_register(ngx_conf_t *cf, ngx_strategy_slot_app_t * app)\n{\n    ngx_strategy_frame_app_t frame_app;\n    ngx_strategy_slot_app_t * slot_app;\n    ngx_int_t               rc;\n    \n    slot_app = ngx_pcalloc(cf->pool, sizeof(ngx_strategy_slot_app_t));\n    if (slot_app == NULL) {\n        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                \"[strategy] slot_app alloc mem failed\");\n        return NULL;\n    }\n\n    slot_app->slot_size = app->slot_size;\n    slot_app->pool_size = app->pool_size;\n    slot_app->print_detail = app->print_detail;\n    slot_app->shm_warn_mem_rate = app->shm_warn_mem_rate;\n\n    slot_app->update = app->update;\n    slot_app->check_update = app->check_update;\n    slot_app->data = app->data;\n\n    memset(&frame_app, 0, sizeof(frame_app));\n    frame_app.ctx = app->frame_ctx;\n    frame_app.ctx.data = slot_app;\n    frame_app.app_init = ngx_strategy_slot_init;\n    frame_app.app_callback = ngx_strategy_slot_callback;\n    \n    if (frame_app.ctx.shm_size == 0) {\n        frame_app.ctx.shm_size = ngx_shm_cal_slab_pool_size(slot_app->pool_size);\n        if (frame_app.ctx.shm_size ==  NGX_ERROR) {\n            ngx_log_error(NGX_LOG_EMERG, cf->log, 0, \n                    \"[strategy] shm_size cal error, slot_app->pool_size: %d\", \n                    slot_app->pool_size);\n            return NULL;\n        }\n        ngx_log_error(NGX_LOG_DEBUG, cf->log, 0,\n                \"[strategy] shm_size:%d, pool_size:%d\", frame_app.ctx.shm_size,\n                slot_app->pool_size);\n    }\n\n    rc = ngx_strategy_frame_register(cf, &frame_app);\n    if (rc != NGX_OK) {\n        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                \"[strategy] frame_register failed: %V\", &frame_app.ctx.name);\n        return NULL;\n    }\n    \n    return slot_app;\n}\n\nvoid * ngx_strategy_get_current_slot(ngx_strategy_slot_app_t *app)\n{\n    ngx_strategy_slot_ctx_t     *current;\n\n    if (app == NULL || app->shm_ctx == NULL) {\n        ngx_log_error(NGX_LOG_DEBUG, ngx_cycle->log, 0,\n                \"[strategy] get slot: app or shm_ctx is null: app=%p\", app);\n        return NULL;\n    }\n    \n    current = &app->shm_ctx->slots[(app->shm_ctx->current) % 2];\n\n    if (current->valid == 0) {\n        return NULL;\n    }\n\n    return current->data;\n}\n\n/* Shared memory needs to allocate 2 pools of the same size */\n#define NGX_STRATEGY_SHM_POOL_NUM           2\n/* The value of M divided by N is rounded up，must M >= 1 and N > 0 */\n#define NGX_CEIL_INT(M,N)                   (((M) - 1) / (N) + 1)\n\nngx_int_t\nngx_shm_cal_slab_pool_size(ngx_int_t app_pool_size)\n{\n    if ((app_pool_size < 0)  \n        || (app_pool_size > (NGX_MAX_INT_T_VALUE / NGX_STRATEGY_SHM_POOL_NUM)))\n    {\n        /* Make sure app_pool_size is within the valid range */\n        return NGX_ERROR;\n    }\n    /* The assignment of ngx_pagesize and ngx_pagesize_shift is in ngx_os_init,\n        which is called earlier than ngx_init_cycle */\n\n    /* Get ngx_pagesize size from global variable */\n    ngx_uint_t pagesize = ngx_pagesize;\n    /* Get ngx_pagesize_shift */\n    ngx_uint_t pagesize_shift = ngx_pagesize_shift;\n\n    ngx_uint_t extra_size = 0;\n\n    extra_size += sizeof(ngx_slab_pool_t);\n   \n    /* slab classification array size */\n    ngx_uint_t grade_array_size = pagesize_shift * sizeof(ngx_slab_page_t);\n    \n    extra_size += ngx_max(pagesize, grade_array_size + grade_array_size); \n\n    ngx_uint_t page_num = NGX_CEIL_INT(app_pool_size + sizeof(ngx_shm_pool_t), pagesize)\n                          * NGX_STRATEGY_SHM_POOL_NUM; \n    \n    ngx_uint_t page_array_size = page_num * sizeof(ngx_slab_page_t) + pagesize; \n    extra_size += page_array_size;\n\n    \n    extra_size = (pagesize_shift + NGX_CEIL_INT(extra_size, pagesize)) * pagesize;\n\n    \n    ngx_int_t total_size = page_num * pagesize + extra_size;\n\n    if (total_size < 0) { \n        return NGX_ERROR;\n    }\n\n    return total_size;\n}\n"
  },
  {
    "path": "modules/mod_strategy/ngx_proc_strategy_module.h",
    "content": "\n /*\n * Copyright (C) 2010-2019 Alibaba Group Holding Limited\n */\n\n\n#ifndef NGX_PROC_STRATEGY_MODULE_H\n#define NGX_PROC_STRATEGY_MODULE_H\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n#include <nginx.h>\n\n#include \"ngx_comm_shm.h\"\n\n/*\n* The first way of use.\n* call back only when the independent process starts,\n* and it is up to the business module to decide whether the timer operation is required\n*/\ntypedef ngx_int_t (*ngx_strategy_init_func)(void * data);\n\n\nngx_int_t ngx_strategy_register(ngx_conf_t *cf, ngx_strategy_init_func init, void *data);\nngx_int_t ngx_check_strategy_process(ngx_conf_t *cf);\n\n/*\n* The second way to use.\n* it is to create shared memory and timer callbacks\n*/\ntypedef struct {\n    ngx_strategy_init_func init;\n    void * data;\n} ngx_strategy_sync_app_t;\n\n\ntypedef struct {\n    /* incoming parameters */\n\n    /* App name */\n    ngx_str_t    name;\n    /* Required shared memory size */\n    ngx_int_t    shm_size;\n    /* Callback interval\n     *     NGX_CONF_UNSET_MSEC means no call\n     *     0 means only call once\n     *     > 0 callback interval */\n    ngx_msec_t   interval;\n    /* Callback data, the framework does not analyze, transparent transmission */\n    void         *data;\n\n    /* The following internal parameters cannot be modified externally */\n    ngx_event_t         tm_event;\n    ngx_slab_pool_t     *slab;\n} ngx_strategy_frame_ctx_t;\n\n\ntypedef ngx_int_t (*ngx_strategy_frame_init)(ngx_strategy_frame_ctx_t * ctx,\n        ngx_cycle_t *cycle, ngx_slab_pool_t * slab);\ntypedef ngx_int_t (*ngx_strategy_frame_uninit)(ngx_strategy_frame_ctx_t * ctx,\n        ngx_cycle_t *cycle);\ntypedef ngx_int_t (*ngx_strategy_frame_callback)(ngx_strategy_frame_ctx_t * ctx);\n\ntypedef struct {\n    ngx_strategy_frame_ctx_t                  ctx;\n    \n    /* Called during initialization,\n     * the shared memory has been created and needs to be initialized */\n    ngx_strategy_frame_init                   app_init;\n    /* Called on exit */\n    ngx_strategy_frame_uninit                 app_uninit;\n    /* Callback at interval */\n    ngx_strategy_frame_callback               app_callback;\n} ngx_strategy_frame_app_t;\n\n/**\n * @brief Register APP, registration can be in the init_main_conf stage\n * \n * @param cf  ngx_conf_t\n * @param app Configuration information, memory will be reallocated inside the module, and temporary variables can be passed in\n * @return ngx_int_t\n *          NGX_OK registration success\n *          NGX_ERROR registration failed\n */\nngx_int_t ngx_strategy_frame_register(ngx_conf_t *cf, ngx_strategy_frame_app_t * app);\n\n\n\ntypedef struct {\n    ngx_array_t apps;           /* ngx_strategy_sync_app_t */\n\n    ngx_array_t frame_apps;     /* ngx_strategy_frame_app_t */\n\n    ngx_int_t   already_init;\n} ngx_proc_strategy_main_conf_t;\n\n/*\n* The third way of use\n* Create two shared memory switches,\n* and the module only needs to implement two callbacks, check_update and update\n*/\ntypedef ngx_int_t (*ngx_strategy_slot_update)(ngx_cycle_t *cycle,\n    void * context,\n    ngx_shm_pool_t * pool,\n    void * data,\n    ngx_int_t print_detail);\n\n\ntypedef enum {\n    STATUS_CHECK_UPDATE_FAILED,\n    STATUS_CHECK_NEED_UPDATE,\n    STATUS_CHECK_NO_UPDATE,\n} check_update_status;\n\ntypedef check_update_status (*ngx_strategy_slot_check_update)(void * context, void * data);\n\ntypedef struct {\n    ngx_shm_pool_t  *pool;\n    void            *data;\n    ngx_int_t       valid;\n} ngx_strategy_slot_ctx_t;\n\ntypedef struct {\n    ngx_int_t                   current;\n    ngx_strategy_slot_ctx_t     slots[2];\n} ngx_strategy_slot_shm_ctx_t;\n\ntypedef struct {\n    /* incoming parameters */\n    ngx_strategy_frame_ctx_t frame_ctx;\n\n    /* Incoming parameters, structure size */\n    ngx_int_t               slot_size;\n    ngx_int_t               pool_size;\n    ngx_int_t               print_detail;\n    ngx_int_t               shm_warn_mem_rate;\n\n    /* Incoming parameters, additional environment information */\n    void                    *data;\n\n    /* Incoming parameters, callback method */\n    ngx_strategy_slot_update            update;\n    ngx_strategy_slot_check_update      check_update;\n    \n    /* Internal structure, cannot modify */\n    ngx_strategy_slot_shm_ctx_t  *shm_ctx;\n} ngx_strategy_slot_app_t;\n\nngx_strategy_slot_app_t* ngx_strategy_slot_app_register(ngx_conf_t *cf, ngx_strategy_slot_app_t * app);\n\nvoid * ngx_strategy_get_current_slot(ngx_strategy_slot_app_t *app);\n\n/* According to app_pool_size, calculate the size of the nginx slab_pool that needs to be created */\nngx_int_t ngx_shm_cal_slab_pool_size(ngx_int_t app_pool_size);\n\n#endif // NGX_PROC_STRATEGY_MODULE_H\n\n"
  },
  {
    "path": "modules/mod_xudp/config",
    "content": "ngx_addon_name=ngx_xudp_module\n\nngx_feature=\"xudp\"\nngx_feature_name=\"T_NGX_HAVE_XUDP\"\nngx_feature_run=no\nngx_feature_incs=\"#include <xudp.h>\n    #include <xquic_xdp.h>\"\nngx_feature_path=\nngx_feature_libs='-L$XUDP_LIB -lxudp'\nngx_feature_test=\"xudp * r ;\n                r = xudp_init(NULL,0) ;\n                if (XUDP_VERSION < 2002001) { exit(-1); }\n                if (r == NULL) { exit(-1); }\n                if (r != NULL) { xudp_free(r); }\n                return 1;\"\nCC_TEST_FLAGS=\"-I$XUDP_INC -I$XQUIC_XDP_INC\"\n. auto/feature\n\nif [ $ngx_found = no ]; then\n    cat << END\n    $0: error: xudp library not found.\nEND\nelse\n\n    CORE_MODULES=\"$CORE_MODULES \\\n                ngx_xudp_core_module\"\n\n    HTTP_MODULES=\"$HTTP_MODULES \\\n                ngx_xudp_module\"\n\n    NGX_ADDON_DEPS=\"$NGX_ADDON_DEPS \\\n                    $ngx_addon_dir/ngx_xudp_module.h \\\n                    $ngx_addon_dir/ngx_xudp_inc.h   \\\n                    $ngx_addon_dir/ngx_xudp.h\"\n\n    NGX_ADDON_SRCS=\"$NGX_ADDON_SRCS \\\n                    $ngx_addon_dir/ngx_xudp_module.c \\\n                    $ngx_addon_dir/ngx_xudp.c\"\n\n    CORE_INCS=\"$CORE_INCS \\\n           $ngx_addon_dir \\\n           $XUDP_INC \\\n           $XQUIC_XDP_INC\"\n\n    CORE_LIBS=\"$CORE_LIBS \\\n           -L$XUDP_LIB -lxudp\"\nfi\n\nhave=T_NGX_UDPV2 . auto/have"
  },
  {
    "path": "modules/mod_xudp/ngx_xudp.c",
    "content": "/*\n * Copyright (C) 2020-2023 Alibaba Group Holding Limited\n */\n\n#include <ngx_xudp.h>\n#include <ngx_xudp_internal.h>\n#include <netinet/in.h>\n\nxudp         *ngx_xudp_engine  = NULL;\nxudp_conf    ngx_xudp_conf     = {0};\n\n#if (T_NGX_XQUIC)\nstruct kern_xquic ngx_xudp_xquic_kern_cid_route_info = {0};\n#endif\n\nngx_listening_t *\nngx_xudp_get_tx(void) {\n    return ngx_cycle->xudp_ctx ? ngx_cycle->xudp_ctx->tx : NULL;\n}\n\nssize_t\nngx_xudp_sendmmsg(ngx_connection_t *c ,struct iovec *msg_iov, unsigned int vlen,\n                  const struct sockaddr *peer_addr, socklen_t peer_addrlen, int push)\n{\n    ngx_listening_t    *tx;\n    ngx_connection_t   *tx_c;\n    ngx_xudp_channel_t *xudp_ch;\n    struct xudp_addr    xaddr;\n    ngx_event_t        *wev;\n    unsigned int        packet_count;\n    int err, xudp_flags;\n\n    tx = ngx_xudp_get_tx();\n    /* xudp off */\n    if (tx == NULL) {\n        /* force sendmmsg degrade */\n        return NGX_DECLINED;\n    }\n\n    tx_c    = tx->connection;\n    wev     = tx_c->write;\n    xudp_ch = tx->ngx_xudp_ch;\n\n    memcpy(&xaddr.to, peer_addr, peer_addrlen);\n    memcpy(&xaddr.from, c->local_sockaddr, c->local_socklen);\n\n    xudp_flags = XUDP_FLAG_SRC_PORT | XUDP_FLAG_SRC_IP;\n\n    if (c->local_sockaddr->sa_family == AF_INET) {\n        struct sockaddr_in *v4 = (struct sockaddr_in*) (c->local_sockaddr);\n        if (v4->sin_addr.s_addr == INADDR_ANY) {\n            xudp_flags &= (~XUDP_FLAG_SRC_IP);\n        }\n    }else if (c->local_sockaddr->sa_family == AF_INET6){\n        struct sockaddr_in6 *v6 = (struct sockaddr_in6*) (c->local_sockaddr);\n        if (IN6_IS_ADDR_UNSPECIFIED(&v6->sin6_addr)) {\n            xudp_flags &= (~XUDP_FLAG_SRC_IP);\n        }\n    }\n\n    /**\n     * xudp send data:\n     * 1. move the send buffer to xudp_send_channel\n     * if the threshold (100) of buffer is reached, buffer will be flush to NIC immediately\n     * 2. flush data to NIC via xudp_commit_channel actively\n    * */\n    for(packet_count = 0; packet_count < vlen; packet_count++) {\n        err = xudp_send_channel(xudp_ch->ch, msg_iov->iov_base, msg_iov->iov_len, (struct sockaddr *) &xaddr, xudp_flags);\n        msg_iov++;\n        if (err < 0) {\n            if (ngx_xudp_error_is_fatal(err)) {\n                ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, \"|xudp|nginx|xudp_send_channel failed [%d]\", err);\n                return err;\n            }\n            break;\n        }\n    }\n\n    if (packet_count < vlen) {\n        wev->ready = 0;\n    }\n\n    if (packet_count > 0) {\n        if (!push) {\n            ngx_post_event(&(xudp_ch->commit), &ngx_posted_commit);\n        }else {\n            /* flush data to NIC actively */\n            err = xudp_commit_channel(xudp_ch->ch);\n            if (err < 0 && ngx_xudp_error_is_fatal(err)) {\n                ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, \"|xudp|nginx|xudp_commit_channel failed [err:%d]\", err);\n                return err;\n            }\n        }\n    }\n\n    return packet_count;\n}"
  },
  {
    "path": "modules/mod_xudp/ngx_xudp.h",
    "content": "/*\n * Copyright (C) 2020-2023 Alibaba Group Holding Limited\n */\n\n#ifndef _NGX_XUDP_H_INCLUDED_\n#define _NGX_XUDP_H_INCLUDED_\n\n#include <ngx_core.h>\n\n\n/**\n * get send channel of the worker\n * from xudp2.0, xudp listener can be use as tx\n * */\nngx_listening_t *ngx_xudp_get_tx(void);\n\n\n/**\n *  send data\n *  @param push send data to NIC immediately\n *  @ls tx from `ngx_xudp_get_tx`\n * */\nssize_t ngx_xudp_sendmmsg(ngx_connection_t *c , struct iovec *msg_iov, unsigned int vlen,\n                          const struct sockaddr *peer_addr, socklen_t peer_addrlen, int push);\n\n\n/**\n * @param c\n * @return  whether the connection should use xudp for tx or not\n * */\nstatic ngx_inline ngx_int_t\nngx_xudp_is_tx_enable(ngx_connection_t *c)\n{\nreturn !!c->xudp_tx;\n}\n\n\n/**\n * @param c\n * @return  disable connection for xudp tx\n * */\nstatic ngx_inline void\nngx_xudp_disable_tx(ngx_connection_t *c)\n{\n    c->xudp_tx = 0 ;\n    ngx_memory_barrier();\n}\n\n\n/**\n * @param c\n * @return  enable connection for xudp tx\n * */\nvoid ngx_xudp_enable_tx(ngx_connection_t *c);\n\n/**\n * @param\n * @return whether the error is fatal\n * */\nngx_int_t ngx_xudp_error_is_fatal(int error);\n\n#endif"
  },
  {
    "path": "modules/mod_xudp/ngx_xudp_inc.h",
    "content": "/*\n * Copyright (C) 2020-2023 Alibaba Group Holding Limited\n */\n\n#ifndef _NGX_XUDP_INC_H_INCLUDED_\n#define _NGX_XUDP_INC_H_INCLUDED_\n\n#ifndef _NGX_CORE_H_INCLUDED_\n#error  \"don't include this file directly, include <ngx_core.h> instead \"\n#endif\n\nstruct ngx_xudp_cycle_ctx_s;\ntypedef struct ngx_xudp_cycle_ctx_s ngx_xudp_cycle_ctx_t;\n\nstruct ngx_xudp_channel_s;\ntypedef struct ngx_xudp_channel_s ngx_xudp_channel_t;\n\nstruct ngx_xudp_conf_parser_s;\ntypedef struct ngx_xudp_conf_parser_s ngx_xudp_conf_parser_t;\n\n\n/**\n * open xudp listening sockets\n * @return NGX_OK for success , other for error\n * */\nngx_int_t ngx_xudp_open_listening_sockets(ngx_cycle_t *cycle);\n\n\n/**\n * release relationship between worker and xdp\n * worker will not send and recv traffic via xudp\n * xudp will release all the xdp socket and shm of the worker\n * */\nvoid ngx_xudp_terminate_xudp_binding(ngx_cycle_t *cycle);\n\n#endif"
  },
  {
    "path": "modules/mod_xudp/ngx_xudp_internal.h",
    "content": "/*\n * Copyright (C) 2020-2023 Alibaba Group Holding Limited\n */\n\n#ifndef _NGX_XUDP_INTERNAL_H_INCLUDED_\n#define _NGX_XUDP_INTERNAL_H_INCLUDED_\n\n#include <xudp.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n\n\nextern xudp         *ngx_xudp_engine;\nextern xudp_conf    ngx_xudp_conf;\n\ntypedef struct ngx_xudp_port_map_node_s ngx_xudp_port_map_node_t;\n\nstruct ngx_xudp_port_map_node_s\n{\n    /* ngx_queue_t of listenr based on IP addr */\n    ngx_radix_tree_t    *regular  ;\n\n    /* wildcard listener */\n    ngx_queue_t          wildcard ;\n};\n\nstruct ngx_xudp_cycle_ctx_s\n{\n    /* mapping based on 16-bit port */\n    ngx_radix_tree_t    *ports_map ;\n    /* send listener */\n    ngx_listening_t     *tx;\n    /* group */\n    xudp_group          *group;\n};\n\nstruct ngx_xudp_channel_s\n{\n    /* read channel */\n    xudp_channel    *ch ;\n    /* force to flush buffer */\n    ngx_event_t     commit;\n};\n\nstruct ngx_xudp_conf_parser_s\n{\n#define NGX_XUDP_DEFAULT_XUDP_ADDR_SZ      10\n    /* array for ngx_sockaddr_t */\n    ngx_array_t        *ngx_xudp_addr_arr;\n    /* temporary array for port string  */\n    ngx_array_t        *ngx_xudp_temp_port_arr;\n    /* */\n    ngx_regex_t         *ngx_reg;\n};\n\n#if (T_NGX_XQUIC)\n\n#include <xquic_xdp.h>\n\nextern struct kern_xquic ngx_xudp_xquic_kern_cid_route_info;\n\n#endif\n\n#endif"
  },
  {
    "path": "modules/mod_xudp/ngx_xudp_module.c",
    "content": "/*\n * Copyright (C) 2020-2023 Alibaba Group Holding Limited\n */\n\n#include <ngx_xudp_module.h>\n#include <ngx_xudp_internal.h>\n#include <ngx_http.h>\n#include <ngx_xudp.h>\n#include <ngx_process_cycle.h>\n\n\n#ifndef XUDP_XQUIC_MAP_NAME\n#define XUDP_XQUIC_MAP_NAME \"map_xquic\"\n#endif\n\n#ifndef XUDP_XQUIC_MAP_DEFAULT_KEY\n#define XUDP_XQUIC_MAP_DEFAULT_KEY (0)\n#endif\n\n\n#define NGX_RADIX32_MASK   0xffffffff\n\n#if (NGX_HAVE_INET6)\nstatic uint32_t _ngx_radix128_mask[4] = {NGX_RADIX32_MASK,NGX_RADIX32_MASK,NGX_RADIX32_MASK,NGX_RADIX32_MASK};\n#define NGX_RADIX128_MASK ((u_char *) (&_ngx_radix128_mask[0]))\n#endif\n\nstatic ngx_inline ngx_int_t\nngx_xudp_error(ngx_xudp_conf_t *xcf, ngx_int_t errorcode)\n{\nreturn xcf->allow_degrade ? NGX_OK : errorcode;\n}\n\n/**\n * load xudp engine\n * @allow_degrade allow degrade to system udp if xudp load faield\n * @return NGX_OK for success , other for failed\n * */\nstatic ngx_int_t ngx_xudp_load(ngx_cycle_t *cycle);\n\n/**\n * stop xudp , udp packet will pass to kernel\n * */\nstatic void ngx_xudp_stop(ngx_cycle_t *cycle);\n\n/**\n * cleanup xudp engine\n * */\nstatic void ngx_xudp_free(ngx_cycle_t *cycle);\n\n/**\n * clear xudp ctx\n * */\nstatic xudp *ngx_xudp_clear(ngx_cycle_t *cycle);\n\n/**\n * convert nginx log level to xudp level\n * */\nstatic int ngx_xudp_from_ngx_log_level(ngx_cycle_t *cycle);\n\n/**\n * xudp engine log callback\n * */\nstatic int ngx_xudp_log(char *buf, int size, void *data);\n\n/**\n * create xudp core module configure\n * */\nstatic void *ngx_xudp_core_module_create_conf(ngx_cycle_t *cycle);\n\n/**\n * init xudp core module configure with default value , and\n * load xudp engine if necessary\n * */\nstatic char *ngx_xudp_core_module_init_conf(ngx_cycle_t *cycle, void *conf);\n\n/**\n * create xudp listening sockets\n * */\nstatic ngx_int_t ngx_xudp_create_listening_sockets(ngx_cycle_t *cycle);\n\n/**\n * according to xudp listening ports, create radix tree\n * */\nstatic ngx_int_t ngx_xudp_create_address_map(ngx_cycle_t *cycle);\n\n/**\n * flush NIC send queue \n * */\nstatic void ngx_xudp_flush_send_data(ngx_event_t *ev);\n\nstatic ngx_int_t ngx_xudp_core_module_init_module(ngx_cycle_t *cycle);\n\n/**\n * add xudp listening before event module\n * @return NGX_OK for success , other for failed\n * rely on `ngx_xudp_add_listening` , `ngx_xudp_create_address_map`\n * */\nstatic ngx_int_t ngx_xudp_core_module_init_process(ngx_cycle_t *cycle);\n\nstatic void ngx_xudp_core_module_exit_process(ngx_cycle_t *cycle);\n\n/**\n * core module exit master\n * */\nstatic void ngx_xudp_core_module_exit_master(ngx_cycle_t *cycle);\n\n// --- //\n\n/**\n * get xudp variable\n * */\nstatic ngx_int_t ngx_xudp_variable (ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data);\n\n/**\n * http module pre configure handler\n * */\nstatic ngx_int_t ngx_xudp_add_http_variables(ngx_conf_t *cf);\n\n/**\n * http module post configure handler\n * */\n\nstatic ngx_int_t ngx_xudp_http_postconfiguration(ngx_conf_t *cf);\n\n/**\n * set xudp listening recv & write event callback\n * @return NGX_OK for success ,other for failed\n * rely on  `ngx_event_xudp_recvmsg`\n * */\nstatic ngx_int_t ngx_xudp_module_init_process(ngx_cycle_t *cycle);\n\n/**\n * xudp recv event callback\n * */\nstatic void ngx_event_xudp_recvmsg(ngx_event_t *rev);\n\n/**\n * choose connection based on the destination address of the packet\n * */\nstatic ngx_listening_t *ngx_xudp_find_listening(ngx_listening_t *xudp_ls, const struct sockaddr *sa);\n\n\n/** xudp core module */\nstatic ngx_command_t ngx_xudp_core_commands[] = {\n\n        {\n                ngx_string(\"xudp_off\"),\n                NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_FLAG,\n                ngx_conf_set_flag_slot,\n                0,\n                offsetof(ngx_xudp_conf_t, no_xudp),\n                NULL\n        },\n        {\n                ngx_string(\"xudp_no_tx\"),\n                NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_FLAG,\n                ngx_conf_set_flag_slot,\n                0,\n                offsetof(ngx_xudp_conf_t, no_xudp_tx),\n                NULL\n        },\n        {\n                ngx_string(\"xudp_core_path\"),\n                NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_1MORE,\n                ngx_conf_set_str_slot,\n                0,\n                offsetof(ngx_xudp_conf_t, dispatcher_path),\n                NULL\n        },\n        {\n                ngx_string(\"xudp_sndnum\"),\n                NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_1MORE,\n                ngx_conf_set_num_slot,\n                0,\n                offsetof(ngx_xudp_conf_t, sndnum),\n                NULL\n        },\n        {\n                ngx_string(\"xudp_rcvnum\"),\n                NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_1MORE,\n                ngx_conf_set_num_slot,\n                0,\n                offsetof(ngx_xudp_conf_t, rcvnum),\n                NULL\n        },\n\n        {\n                ngx_string(\"xudp_retries_interval\"),\n                NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_1MORE,\n                ngx_conf_set_msec_slot,\n                0,\n                offsetof(ngx_xudp_conf_t, retries_interval),\n                NULL\n        },\n\n        {\n                ngx_string(\"max_retries\"),\n                NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_1MORE,\n                ngx_conf_set_num_slot,\n                0,\n                offsetof(ngx_xudp_conf_t, max_retries),\n                NULL\n        },\n\n        ngx_null_command\n};\n\nstatic ngx_core_module_t  ngx_xudp_core_module_ctx = {\n        ngx_string(\"xudp_core\"),\n        ngx_xudp_core_module_create_conf,\n        ngx_xudp_core_module_init_conf,\n};\n\nngx_module_t ngx_xudp_core_module = {\n        NGX_MODULE_V1,\n        &ngx_xudp_core_module_ctx,             /* module context */\n        ngx_xudp_core_commands,                /* module directives */\n        NGX_CORE_MODULE,                       /* module type */\n        NULL,                                  /* init master */\n        ngx_xudp_core_module_init_module,      /* init module */\n        ngx_xudp_core_module_init_process,     /* init process */\n        NULL,                                  /* init thread */\n        NULL,                                  /* exit thread */\n        ngx_xudp_core_module_exit_process,     /* exit process */\n        ngx_xudp_core_module_exit_master,      /* exit master */\n        NGX_MODULE_V1_PADDING\n};\n\nstatic void *\nngx_xudp_core_module_create_conf(ngx_cycle_t *cycle)\n{\n    ngx_xudp_conf_t *xcf;\n    xcf = ngx_pcalloc(cycle->pool, sizeof(ngx_xudp_conf_t));\n    if (xcf == NULL) {\n        return NULL;\n    }\n\n    ngx_array_init(&xcf->xudp_address, cycle->pool, NGX_CONF_MAX_ARGS, sizeof(ngx_sockaddr_t));\n\n    xcf->rcvnum             = NGX_CONF_UNSET_UINT;\n    xcf->sndnum             = NGX_CONF_UNSET_UINT;\n    xcf->no_xudp            = NGX_CONF_UNSET;\n    xcf->no_xudp_tx         = NGX_CONF_UNSET;\n    xcf->retries_interval   = NGX_CONF_UNSET_MSEC;\n    xcf->max_retries        = NGX_CONF_UNSET;\n    xcf->allow_degrade      = 1;\n\n    return xcf;\n}\n\n\n/**\n * fill sockaddr in (r) with target af and port\n * @param port required network order\n * return (r)\n * */\nstatic struct sockaddr *\nngx_xudp_built_sockaddr(ngx_sockaddr_t *r, int af, int port)\n{\n    /* zero */\n    ngx_memzero(r, sizeof(*r));\n\n    /* set family */\n    r->sockaddr.sa_family = af ;\n\n    if (af == AF_INET) {\n        r->sockaddr_in.sin_port = port;\n    }else if(af == AF_INET6) {\n#if (NGX_HAVE_INET6)\n        r->sockaddr_in6.sin6_port = port;\n#endif\n    }\n\n    return &r->sockaddr;\n}\n\nstatic ngx_int_t\nngx_xudp_add_address(ngx_xudp_conf_t *xcf, struct sockaddr *sa)\n{\n    ngx_sockaddr_t *addr;\n\n    addr = (ngx_sockaddr_t*) ngx_array_push(&xcf->xudp_address);\n    if (addr == NULL) {\n        return NGX_ERROR;\n    }\n\n    switch(sa->sa_family) {\n        case AF_INET:\n            ngx_memcpy(addr, sa, sizeof(struct sockaddr_in));\n            break;\n        case AF_INET6:\n            ngx_memcpy(addr, sa, sizeof(struct sockaddr_in6));\n            break;\n        default:\n            /* un support family*/\n            return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\nstatic ngx_int_t\nngx_xudp_get_address_from_http_core_module(ngx_xudp_conf_t *xcf, ngx_http_core_main_conf_t *cmcf)\n{\n    size_t                       i, j, r;\n    ngx_http_conf_port_t        *port;\n    ngx_http_conf_addr_t        *addr;\n    ngx_sockaddr_t               wildcard;\n\n    if (cmcf->ports == NULL) {\n        return NGX_OK;\n    }\n\n    port = (ngx_http_conf_port_t*) cmcf->ports->elts;\n\n    for(i = 0; i < cmcf->ports->nelts; i++) {\n        if (port[i].xudp) {\n            r = ngx_xudp_add_address(xcf, ngx_xudp_built_sockaddr(&wildcard, port[i].family, port[i].port));\n            if (r != NGX_OK) {\n                return r;\n            }\n            continue;\n        }\n        addr = (ngx_http_conf_addr_t*) port[i].addrs.elts;\n        for(j = 0; j < port[i].addrs.nelts; j++) {\n\n            if (!addr[j].opt.xudp) {\n                continue;\n            }\n\n            r = ngx_xudp_add_address(xcf, addr[j].opt.sockaddr);\n            if (r != NGX_OK) {\n                return r;\n            }\n        }\n    }\n\n    return NGX_OK;\n}\n\nstatic ngx_int_t\nngx_xudp_http_postconfiguration(ngx_conf_t *cf)\n{\n    ngx_xudp_conf_t             *xcf;\n    ngx_cycle_t                 *cycle;\n    ngx_http_core_main_conf_t   *cmcf;\n\n    cycle = cf->cycle;\n\n    xcf = (ngx_xudp_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_xudp_core_module);\n\n    cmcf = (ngx_http_core_main_conf_t*) ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);\n\n    /* get xudp binding address from http configure */\n    if (ngx_xudp_get_address_from_http_core_module(xcf, cmcf) != NGX_OK) {\n        return  NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\nstatic char *\nngx_xudp_core_module_init_conf(ngx_cycle_t *cycle, void *conf)\n{\n    ngx_core_conf_t *ccf;\n    ngx_xudp_conf_t *xcf;\n    u_char          *c_str;\n\n    xcf = (ngx_xudp_conf_t *) (conf);\n    ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);\n\n#define NGX_XUDP_RCVNUM  1024\n    ngx_conf_init_uint_value(xcf->rcvnum, NGX_XUDP_RCVNUM);\n#undef NGX_XUDP_RCVNUM\n\n#define NGX_XUDP_SNDNUM   1024\n    ngx_conf_init_uint_value(xcf->sndnum, NGX_XUDP_SNDNUM);\n#undef NGX_XUDP_SNDNUM\n\n    if (xcf->no_xudp == NGX_CONF_UNSET) {\n        xcf->no_xudp = 0;\n    }\n\n    if (xcf->no_xudp_tx == NGX_CONF_UNSET) {\n        xcf->no_xudp_tx = 0;\n    }\n\n    ngx_conf_init_msec_value(xcf->retries_interval, 200);\n    ngx_conf_init_uint_value(xcf->max_retries, 2);\n\n    ngx_xudp_conf.isolate_group = 1;\n    ngx_xudp_conf.group_num     = ccf->worker_processes;\n\n    // xudp log info\n    ngx_xudp_conf.log_cb    = ngx_xudp_log;\n    ngx_xudp_conf.log_level = ngx_xudp_from_ngx_log_level(cycle);\n\n    ngx_xudp_conf.noarp     = 1;\n    ngx_xudp_conf.force_xdp = 1;\n\n    ngx_xudp_conf.sndnum    = xcf->sndnum;\n    ngx_xudp_conf.rcvnum    = xcf->rcvnum;\n\n    /* for xudp_dump, default to 2MB */\n    ngx_xudp_conf.dump_prepare_size = 2 * 1024 * 1024;\n\n    if (xcf->dispatcher_path.data) {\n        c_str = ngx_pcalloc(cycle->pool, xcf->dispatcher_path.len + 1);\n        if (!c_str) {\n            return \"nomem\";\n        }\n        ngx_memcpy(c_str, xcf->dispatcher_path.data, xcf->dispatcher_path.len);\n        ngx_xudp_conf.map_dict_n_max_pid    = /**true*/ 1;\n        ngx_xudp_conf.flow_dispatch         = XUDP_FLOW_DISPATCH_TYPE_CUSTOM;\n        ngx_xudp_conf.xdp_custom_path       = (char *) c_str;\n    }else {\n#if (T_NGX_XQUIC)\n        return \"xquic over xudp required xquic dispatcher\";\n#endif\n    }\n\n    xcf->on = xcf->xudp_address.nelts && !xcf->no_xudp;\n    return NGX_CONF_OK;\n}\n\nstatic ngx_int_t\nngx_xudp_core_module_init_module(ngx_cycle_t *cycle)\n{\n    int  ret, need_wait;\n    ngx_uint_t loop;\n    ngx_xudp_conf_t        *xcf;\n    xcf = (ngx_xudp_conf_t *)ngx_get_conf(cycle->conf_ctx, ngx_xudp_core_module);\n\n    if (ngx_process != NGX_PROCESS_SINGLE && ngx_process != NGX_PROCESS_MASTER) {\n        return NGX_OK;\n    }\n\n    need_wait = 0;\n    loop  = 0;\n\n    /* free old xudp engine if necessary */\n    if (ngx_xudp_engine) {\n\n        /* notify old worker to unbind xudp asap */\n        ngx_xudp_signal_worker_process(cycle);\n\n        /* free current ngx_xudp_engine */\n        ngx_xudp_free(cycle);\n\n        /* wait old worker for unbinding */\n        need_wait = 1;\n\n        ngx_memory_barrier();\n    }\n\n    /* just return */\n    if (!xcf->on) {\n        /* in case of multi master */\n        xudp_xdp_clear();\n        return NGX_OK;\n    }\n\n    do {\n\n        loop++;\n\n        if (need_wait) {\n            ngx_msleep(xcf->retries_interval);\n        }\n\n        /* try load new xudp engine */\n        if (ngx_xudp_load(cycle) == NGX_OK) {\n            break;\n        }\n\n    }while(need_wait && loop < xcf->max_retries);\n\n    if (ngx_xudp_engine == NULL) {\n        goto failed;\n    }\n\n    /* has custom dispatcher */\n    if (xcf->dispatcher_path.data) {\n#if (T_NGX_XQUIC)\n        /* required xquic cid route */\n        if (ngx_xquic_is_cid_route_on(cycle)) {\n\n            int key = XUDP_XQUIC_MAP_DEFAULT_KEY;\n            struct kern_xquic  value = {0};\n\n            value.capture       = 1;\n            value.mask          = ngx_xquic_cid_worker_id_secret(cycle);\n            value.offset        = ngx_xquic_cid_worker_id_offset(cycle);\n            value.salt_range    = ngx_xquic_cid_worker_id_salt_range(cycle);\n\n            /* need compare */\n            if (ngx_xudp_xquic_kern_cid_route_info.capture) {\n                if (ngx_memcmp(&value, &ngx_xudp_xquic_kern_cid_route_info, sizeof(value)) != 0) {\n                    ngx_log_error(NGX_LOG_ERR, cycle->log, 0,\n                        \"|xudp|nginx|update cid route stuff may result in incorrect udp dispatch with old workers\");\n                    goto failed;\n                }\n            }\n            /* sync */\n            memcpy(&ngx_xudp_xquic_kern_cid_route_info, &value, sizeof(value));\n            ret = xudp_bpf_map_update(ngx_xudp_engine, XUDP_XQUIC_MAP_NAME, &key , &ngx_xudp_xquic_kern_cid_route_info);\n            if (ret != 0) {\n                ngx_log_error(NGX_LOG_ERR, cycle->log, 0,\n                    \"|xudp|nginx|update map[%s] xquic failed [xudp_error:%d]\", XUDP_XQUIC_MAP_NAME, ret);\n                goto failed;\n            }\n        }else {\n            ngx_log_error(NGX_LOG_ERR, cycle->log, 0, \"|xudp|nginx|xudp required xquic enable cid route, degrade to system\");\n            goto failed;\n        }\n#endif\n    }\n\n    return NGX_OK;\n\n    failed:\n    ngx_xudp_free(cycle);\n    return ngx_xudp_error(xcf, NGX_ERROR);\n}\n\nngx_int_t\nngx_xudp_open_listening_sockets(ngx_cycle_t *cycle)\n{\n    ngx_xudp_cycle_ctx_t    *ctx;\n    ngx_xudp_conf_t         *xcf;\n    xcf = (ngx_xudp_conf_t *)ngx_get_conf(cycle->conf_ctx, ngx_xudp_core_module);\n\n    /* no xudp */\n    if (ngx_xudp_engine == NULL) {\n        return NGX_OK;\n    }\n\n    ctx = ngx_palloc(cycle->pool,sizeof(*ctx));\n    if (!ctx) {\n        // fatal error , ignore degrade\n        return NGX_ERROR ;\n    }\n\n    do {\n\n        cycle->xudp_ctx = ctx;\n\n        if (ngx_xudp_create_listening_sockets(cycle) != NGX_OK) {\n            break;\n        }\n\n        if (ngx_xudp_create_address_map(cycle) != NGX_OK) {\n            break;\n        }\n\n        return NGX_OK;\n\n    }while(0);\n\n    cycle->xudp_ctx = NULL;\n    return ngx_xudp_error(xcf, NGX_ERROR);\n}\n\n\nstatic ngx_int_t\nngx_xudp_core_module_init_process(ngx_cycle_t *cycle)\n{\n    /**\n     * NOTICE\n     * xdp socket in the worker needs CAP_NET_RAW and CAP_NET_ADMIN at least\n     * nginx with setuid, the worker cannot save any permission\n     * \n     * there are three methods:\n     * 1. set ngx_xudp_core_module_init_process as a empty function, execute ngx_xudp_open_listening_sockets before setuid\n     * 2. for the ngx_xudp_core_module_init_process, execute setuid in the end \n     * 3. save permission during the linux setuid via keep_caps, manage permission of worker\n     * \n     * Now, xudp uses the method 1.\n     * If nginx supports linux permission mod, the method 3 will be better.\n     * */\n    return NGX_OK;\n    //return ngx_xudp_open_listening_sockets(cycle);\n}\n\nstatic void\nngx_xudp_core_module_exit_process(ngx_cycle_t *cycle)\n{\n    ngx_xudp_clear(cycle);\n}\n\nstatic void\nngx_xudp_core_module_exit_master(ngx_cycle_t *cycle)\n{\n    (void) cycle;\n    if (ngx_xudp_engine) {\n        ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0, \"free xudp\");\n        ngx_xudp_free(cycle);\n    }\n}\n\n/** xudp module */\n\nstatic ngx_http_variable_t  ngx_xudp_vars[] = {\n\n        { ngx_string(\"xudp\"), NULL, ngx_xudp_variable,\n                                       0, NGX_HTTP_VAR_NOCACHEABLE, 0 },\n\n        { ngx_null_string, NULL, NULL, 0, 0, 0 }\n};\n\nstatic ngx_http_module_t  ngx_xudp_module_ctx = {\n        ngx_xudp_add_http_variables,\n        ngx_xudp_http_postconfiguration,\n\n        NULL,\n        NULL,\n\n        NULL,\n        NULL,\n\n        NULL,\n        NULL,\n};\n\nngx_module_t ngx_xudp_module = {\n        NGX_MODULE_V1,\n        &ngx_xudp_module_ctx,                  /* module context */\n        NULL,                                  /* module directives */\n        NGX_HTTP_MODULE,                       /* module type */\n        NULL,                                  /* init master */\n        NULL,                                  /* init module */\n        ngx_xudp_module_init_process,          /* 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\nstatic ngx_int_t\nngx_xudp_add_http_variables(ngx_conf_t *cf)\n{\n    ngx_http_variable_t  *var, *v;\n    for (v = ngx_xudp_vars; v->name.len; v++) {\n        var = ngx_http_add_variable(cf, &v->name, v->flags);\n        if (var == NULL) {\n            return NGX_ERROR;\n        }\n        var->get_handler = v->get_handler;\n        var->data = v->data;\n    }\n    return NGX_OK;\n}\n\n#define NGX_XUDP_STR    \"XUDP\"\nstatic ngx_int_t\nngx_xudp_variable (ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data)\n{\n    ngx_connection_t *c;\n\n    c = r->connection;\n\n    if (c->listening && c->listening->xudp) {\n        v->valid = 1;\n        v->no_cacheable = 0;\n        v->not_found = 0;\n        v->data = (u_char *) NGX_XUDP_STR;\n        v->len  = sizeof(NGX_XUDP_STR) - 1;\n        return NGX_OK;\n    }\n\n    *v = ngx_http_variable_null_value;\n    return NGX_OK;\n}\n\n#undef NGX_XUDP_STR\n\nstatic ngx_int_t\nngx_xudp_module_init_process(ngx_cycle_t *cycle)\n{\n    ngx_listening_t     *ls;\n#if (T_DYRELOAD)\n    ngx_uint_t           j;\n    ngx_listening_t    **lsp;\n#endif\n    ngx_connection_t    *c;\n    ngx_event_t         *rev, *wev;\n    size_t               i;\n\n/* for each listening socket */\n#if (T_DYRELOAD)\n    i = 0;\n    lsp = cycle->listening.elts;\n    for (j = 0; j < cycle->listening.nelts; j++) {\n        ls = lsp[j];\n#else\n    ls = cycle->listening.elts;\n    for (i = 0; i < cycle->listening.nelts; i++) {\n#endif\n\n#if (T_PROCESS_IDX)\n        if (ls[i].proc_idx &&ls[i].proc_idx != ngx_process_idx) {\n            continue;\n        }\n#endif\n\n        if (!ls[i].for_xudp || ls[i].worker != ngx_worker) {\n            continue;\n        }\n\n        c   = ls[i].connection;\n        rev = c->read;\n        wev = c->write;\n\n        rev->handler        = ngx_event_xudp_recvmsg ;\n        wev->handler        = ngx_udpv2_write_handler_mainlogic;\n\n        wev->log            = c->log;\n\n        if (xudp_channel_is_tx(ls[i].ngx_xudp_ch->ch)) {\n            cycle->xudp_ctx->tx = &ls[i];\n        }\n\n    }\n\n    return NGX_OK;\n}\n\nstatic void\nngx_xudp_flush_send_data(ngx_event_t *ev)\n{\n    xudp_commit_channel((xudp_channel*)(ev->data));\n}\n\nstatic ngx_xudp_port_map_node_t*\nngx_xudp_get_or_create_ports_map(ngx_pool_t *pool, ngx_radix_tree_t *ports_map, u16 port)\n{\n    ngx_xudp_port_map_node_t    *v;\n    /* find */\n    v = (ngx_xudp_port_map_node_t *) ngx_radix32tree_find(ports_map, port);\n    if (v == (void *) NGX_RADIX_NO_VALUE) {\n        /* create */\n        v = ngx_palloc(pool, sizeof(*v));\n        if (v == NULL) {\n            return NULL;\n        }\n        /* init */\n        v->regular = NULL;\n        ngx_queue_init(&v->wildcard);\n        /* map port*/\n        ngx_radix32tree_insert(ports_map, port, NGX_RADIX32_MASK, (uintptr_t) v);\n    }\n    return v;\n}\n\nstatic ngx_int_t\nngx_xudp_built_address_map(ngx_pool_t *pool, ngx_radix_tree_t *ports_map, ngx_listening_t *ls)\n{\n    ngx_xudp_port_map_node_t    *v;\n    struct sockaddr_in          *addr;\n#if (NGX_HAVE_INET6)\n    u_char                     *p;\n    struct sockaddr_in6         *addr6;\n#endif\n    ngx_queue_t                 *q = NULL;\n\n    if (ls->sockaddr->sa_family == AF_INET) {\n\n        addr = (struct sockaddr_in *)ls->sockaddr;\n        /* get ports map */\n        v = ngx_xudp_get_or_create_ports_map(pool, ports_map, ntohs(addr->sin_port));\n        if (v == NULL) {\n            return NGX_ERROR;\n        }\n\n        if (addr->sin_addr.s_addr == INADDR_ANY) {\n            q = &v->wildcard;\n        } else {\n            if (v->regular == NULL) {\n                v->regular = ngx_radix_tree_create(pool, NGX_CONF_MAX_ARGS);\n                if (!v->regular) {\n                    return NGX_ERROR;\n                }\n            }\n            q = (void*) ngx_radix32tree_find(v->regular, addr->sin_addr.s_addr);\n            if (q == (void*) NGX_RADIX_NO_VALUE) {\n                /* create */\n                q = ngx_palloc(pool, sizeof(*q));\n                if (q == NULL) {\n                    return NGX_ERROR;\n                }\n                ngx_queue_init(q);\n                ngx_radix32tree_insert(v->regular, addr->sin_addr.s_addr, NGX_RADIX32_MASK, (uintptr_t) q);\n            }\n        }\n    } else\n#if (NGX_HAVE_INET6)\n        if (ls->sockaddr->sa_family == AF_INET6)\n    {\n        addr6 = (struct sockaddr_in6 *)ls->sockaddr;\n        p = addr6->sin6_addr.s6_addr;\n        /* get ports map */\n        v = ngx_xudp_get_or_create_ports_map(pool, ports_map, ntohs(addr6->sin6_port));\n        if (v == NULL) {\n            return NGX_ERROR;\n        }\n\n        if (IN6_IS_ADDR_UNSPECIFIED(&addr6->sin6_addr)) {\n            q = &v->wildcard;\n        } else {\n\n            if (v->regular == NULL) {\n                v->regular = ngx_radix_tree_create(pool, NGX_CONF_MAX_ARGS);\n                if (!v->regular) {\n                    return NGX_ERROR;\n                }\n            }\n\n            q = (void*) ngx_radix128tree_find(v->regular, p);\n            if (q == (void*) NGX_RADIX_NO_VALUE) {\n                /* create */\n                q = ngx_palloc(pool,sizeof(*q));\n                if (q == NULL) {\n                    return NGX_ERROR;\n                }\n                ngx_queue_init(q);\n                ngx_radix128tree_insert(v->regular, p, NGX_RADIX128_MASK, (uintptr_t) q);\n            }\n        }\n    } else\n#endif\n    {}\n    ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0,\"|xudp|add fd %d to xudp|\", ls->fd);\n    /* add to list */\n    if (q) {\n        ngx_queue_insert_tail(q, &ls->xudp_sentinel);\n    } else {\n        return NGX_ERROR;\n    }\n    return NGX_OK;\n}\n\nstatic ngx_int_t\nngx_xudp_create_address_map(ngx_cycle_t *cycle)\n{\n    ngx_radix_tree_t    *ports_map;\n    size_t               i;\n    ngx_listening_t     *ls ;\n#if (T_DYRELOAD)\n    ngx_uint_t           j;\n    ngx_listening_t    **lsp;\n#endif\n\n    ports_map = ngx_radix_tree_create(cycle->pool, NGX_CONF_MAX_ARGS);\n    if (!ports_map) {\n        return NGX_ERROR;\n    }\n\n    i = 0 ;\n\n    /* for each listening socket */\n#if (T_DYRELOAD)\n    i = 0;\n    lsp = cycle->listening.elts;\n    for (j = 0; j < cycle->listening.nelts; j++) {\n        ls = lsp[j];\n#else\n    ls = cycle->listening.elts;\n    for (i = 0; i < cycle->listening.nelts; i++) {\n#endif\n\n#if (T_PROCESS_IDX)\n        if (ls[i].proc_idx &&ls[i].proc_idx != ngx_process_idx) {\n            continue;\n        }\n#endif\n\n#if ! (T_RELOAD)\n#if (NGX_HAVE_REUSEPORT)\n        if (ls[i].reuseport && ls[i].worker != ngx_worker) {\n            continue;\n        }\n#endif\n#endif\n\n        if (!ls[i].xudp) {\n            continue;\n        }\n\n        if (ngx_xudp_built_address_map(cycle->pool, ports_map, &ls[i]) != NGX_OK) {\n            return NGX_ERROR;\n        }\n        ls[i].support_udpv2 = 1;\n    }\n\n    cycle->xudp_ctx->ports_map = ports_map;\n    return NGX_OK ;\n}\n\nstatic ngx_int_t\nngx_xudp_create_listening_sockets(ngx_cycle_t *cycle)\n{\n    ngx_conf_t           cf;\n    struct sockaddr_in  *xsk_addr;\n    ngx_listening_t     *ls;\n    xudp_channel        *ch;\n    ngx_xudp_channel_t  *xudp_ch;\n    ngx_pid_t            pid;\n    xudp_group          *group;\n\n    xsk_addr = ngx_pcalloc(cycle->pool, sizeof(*xsk_addr));\n    if (!xsk_addr) {\n        return NGX_ERROR;\n    }\n\n    pid = getpid();\n\n    xsk_addr->sin_family = AF_UNSPEC;\n\n    //mock cf\n    cf.cycle = cycle;\n    cf.log   = cycle->log;\n    cf.pool  = cycle->pool;\n\n    group =  xudp_group_new(ngx_xudp_engine, ngx_worker);\n    if (group == NULL) {\n        ngx_xudp_stop(cycle);\n        ngx_log_error(NGX_LOG_ERR, cycle->log, 0, \"|xudp|nginx|xudp_group_new fail\");\n        goto failed;\n    }\n\n    xudp_group_channel_foreach(ch, group) {\n\n        xudp_ch = ngx_palloc(cycle->pool, sizeof(ngx_xudp_channel_t));\n        if (!xudp_ch) {\n            ngx_log_error(NGX_LOG_ERR, cycle->log, 0, \"|xudp|nginx|palloc xudp_channels fail\");\n            goto failed;\n        }\n\n        xudp_ch->ch = ch ;\n        xudp_ch->commit.handler = ngx_xudp_flush_send_data ;\n        xudp_ch->commit.data    = ch ;\n        xudp_ch->commit.log     = cycle->log;\n\n        ls              = ngx_create_listening(&cf, (struct sockaddr *) xsk_addr, sizeof(*xsk_addr));\n        if (!ls) {\n            goto failed;\n        }\n\n        ls->fd          = xudp_channel_get_fd(ch);\n        ls->type        = SOCK_DGRAM;\n        ls->listen      = 1;\n        ls->for_xudp    = 1;\n        ls->worker      = xudp_channel_get_groupid(ch);\n        ls->logp        = cycle->log;\n        // protect against load balancing\n        ls->reuseport   = 1 ;\n        ls->ngx_xudp_ch = xudp_ch;\n        ngx_log_error(NGX_LOG_INFO, cycle->log, 0, \"|xudp|nginx|init[sockfd:%d] success\", ls->fd);\n    }\n\n    // set group key\n    xudp_dict_set_group_key(group, pid);\n    cycle->xudp_ctx->group = group;\n    return NGX_OK;\n    failed:\n    if (group != NULL) {\n        xudp_group_free(group);\n    }\n    return NGX_ERROR;\n}\n\nstatic int\nngx_xudp_log(char *buf, int size, void *data)\n{\n    char buffer [size + 1] ;\n    memcpy(buffer, buf, size);\n    buffer[size] = '\\0';\n    /** .*s is not work in ngx_log */\n    ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0, \"|xudp|libs|[%s]\",buffer);\n    return size ;\n}\n\nstatic ngx_int_t\nngx_xudp_load(ngx_cycle_t *cycle)\n{\n    size_t              sz, ret;\n    ngx_sockaddr_t     *addr;\n    ngx_xudp_conf_t    *xcf;\n\n    xcf    = (ngx_xudp_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_xudp_core_module);\n\n    sz     = xcf->xudp_address.nelts;\n    addr   = (ngx_sockaddr_t*) xcf->xudp_address.elts;\n\n    if (ngx_xudp_engine == NULL) {\n        ngx_xudp_engine = xudp_init(&ngx_xudp_conf, sizeof(ngx_xudp_conf));\n        if (!ngx_xudp_engine) {\n            ngx_log_error(NGX_LOG_ERR, cycle->log, 0, \"|xudp|nginx|create failed\");\n            goto error;\n        }\n        ret = xudp_bind(ngx_xudp_engine, (struct sockaddr*) addr, sizeof(ngx_sockaddr_t), sz);\n        if (ret != 0) {\n            ngx_log_error(NGX_LOG_ERR, cycle->log, 0, \"|xudp|nginx|bind failed, degrade to system udp [xudp_error:%d]\", ret);\n            goto error;\n        }\n    }\n    return NGX_OK;\n    error:\n    ngx_xudp_free(cycle);\n    return NGX_ERROR;\n}\n\nstatic void\nngx_xudp_stop(ngx_cycle_t *cycle)\n{\n    int key = 0;\n    ngx_xudp_xquic_kern_cid_route_info.capture = 0 ;\n    xudp_bpf_map_update(ngx_xudp_engine, XUDP_XQUIC_MAP_NAME, &key , &ngx_xudp_xquic_kern_cid_route_info);\n    return;\n}\n\nstatic xudp *\nngx_xudp_clear(ngx_cycle_t *cycle)\n{\n    xudp *engine;\n    engine = ngx_xudp_engine;\n    ngx_xudp_engine = NULL;\n\n    if (cycle->xudp_ctx) {\n        if (cycle->xudp_ctx->group) {\n            xudp_group_free(cycle->xudp_ctx->group);\n            cycle->xudp_ctx->group = NULL;\n        }\n        cycle->xudp_ctx = NULL;\n    }\n\n    return engine;\n}\n\nstatic void\nngx_xudp_free(ngx_cycle_t *cycle)\n{\n    xudp *engine;\n    engine = ngx_xudp_clear(cycle);\n    if (engine) {\n        xudp_free(engine);\n    }\n}\n\nstatic ngx_listening_t *\nngx_xudp_find_listening(ngx_listening_t *xudp_ls, const struct sockaddr *sa)\n{\n    ngx_listening_t *ls;\n    ngx_queue_t     *q;\n    ngx_radix_tree_t *ports_map;\n    ngx_xudp_port_map_node_t * m;\n    struct sockaddr_in      *addr = NULL;\n#if (NGX_HAVE_INET6)\n    u_char                  *p;\n    struct sockaddr_in6     *addr6 = NULL;\n#endif // NGX_HAVE_INET6\n    int port ;\n\n    if (sa->sa_family == AF_INET) {\n        addr = (struct sockaddr_in *) sa ;\n        port = ntohs(addr->sin_port);\n    }else\n#if (NGX_HAVE_INET6)\n        if (sa->sa_family == AF_INET6) {\n        addr6 = (struct sockaddr_in6 *) sa ;\n        port  = ntohs(addr6->sin6_port);\n    }else\n#endif\n    {\n        return NULL ;\n    }\n\n    q = (void*) NGX_RADIX_NO_VALUE;\n    ports_map = ngx_cycle->xudp_ctx->ports_map;\n\n    m = (void*) ngx_radix32tree_find(ports_map, port);\n    if (m == (void*) NGX_RADIX_NO_VALUE) {\n        return NULL ;\n    }\n\n    if (m->regular) {\n        if (sa->sa_family == AF_INET) {\n            q = (void*) ngx_radix32tree_find(m->regular, addr->sin_addr.s_addr);\n        }else\n#if (NGX_HAVE_INET6)\n            if (sa->sa_family == AF_INET6) {\n            p = addr6->sin6_addr.s6_addr;\n            q = (void*) ngx_radix128tree_find(m->regular, p);\n        }\n#endif\n        if (q == (void*)NGX_RADIX_NO_VALUE) {\n            q = &m->wildcard;\n        }\n    }else {\n        q = &m->wildcard;\n    }\n\n    /* unlikey */\n    if (ngx_queue_empty(q)) {\n        return NULL;\n    }\n\n    ngx_queue_t *h = ngx_queue_head(q);\n    ls = ngx_queue_data(h, ngx_listening_t, xudp_sentinel);\n    ngx_queue_remove(h);\n    ngx_queue_insert_tail(q, h);\n\n    return ls;\n}\n\nstatic inline socklen_t\nngx_xudp_copy_addr(ngx_sockaddr_t *dest, const struct sockaddr *source)\n{\n    switch(source->sa_family)\n    {\n        case AF_INET:\n            memcpy(dest, source, sizeof(struct sockaddr_in));\n            return sizeof(struct sockaddr_in);\n        case AF_INET6:\n            memcpy(dest, source, sizeof(struct sockaddr_in6));\n            return sizeof(struct sockaddr_in6);\n        default:\n            break;\n    }\n    /* unkown address family */\n    return 0;\n}\n\nstatic void\nxudp_redirect_udpv2(ngx_listening_t *ls, ngx_udpv2_packets_hdr_t *urphdr, xudp_msg *msg)\n{\n    ngx_listening_t     *target;\n    ngx_udpv2_packet_t  *upkt;\n\n    upkt = NGX_UDPV2_PACKETS_HDR_FIRST_PACKET(urphdr);\n\n    target = ngx_xudp_find_listening(ls, (struct sockaddr *)&msg->local_addr);\n\n    if (target != NULL) {\n\n        urphdr->ls = target;\n\n        upkt->pkt_socklen        = ngx_xudp_copy_addr(&upkt->pkt_sockaddr, (struct sockaddr*) &msg->peer_addr);\n        upkt->pkt_local_socklen  = ngx_xudp_copy_addr(&upkt->pkt_local_sockaddr, (struct sockaddr*) &msg->local_addr);\n\n        upkt->pkt_sz             = msg->size;\n        upkt->pkt_payload        = (u_char*) msg->p;\n\n        urphdr->npkts = 1;\n        ngx_udpv2_dispatch_traffic(urphdr);\n        urphdr->npkts = 0;\n\n    } else {\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, \"|xudp|nginx|ngx_xudp_find_listening failed\");\n    }\n}\n\nstatic void\nngx_event_xudp_recvmsg(ngx_event_t *rev)\n{\n    ngx_connection_t *c ;\n    ngx_listening_t  *ls;\n    int n, i, j;\n\n    ngx_udpv2_packets_hdr_t urphdr = NGX_UDPV2_PACKETS_HDR_INIT(urphdr);\n    ngx_udpv2_packet_t upkt;\n\n    /* handle traffic balance */\n    if (rev->timedout) {\n        if (ngx_enable_accept_events((ngx_cycle_t *) ngx_cycle) != NGX_OK) {\n            return;\n        }\n        rev->timedout = 0;\n    }\n\n    c = (ngx_connection_t *) (rev->data);\n    ls = c->listening;\n    xudp_def_msg(hdr, 32);\n\n    NGX_UDPV2_PACKETS_HDR_ADD_PACKET(&urphdr, &upkt);\n    upkt.pkt_micrs = 0 ;\n\n    do {\n\n        n = xudp_recv_channel(ls->ngx_xudp_ch->ch, hdr, 0);\n        ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, 0, \"|xudp|nginx|ngx_event_xudp_recvmsg recv[n=%d]\",n);\n\n        if (n <= 0) {\n            break ;\n        }\n\n        j = hdr->used - 1;\n\n        for(i = 0; i < j; i++) {\n            __builtin_prefetch(&hdr->msg[i + 1]);\n            __builtin_prefetch(hdr->msg[i + 1].p);\n            xudp_redirect_udpv2(ls, &urphdr, &hdr->msg[i]);\n        }\n\n        if (j >= 0) {\n            xudp_redirect_udpv2(ls, &urphdr, &hdr->msg[j]);\n        }\n\n        xudp_recycle(hdr);\n\n    }while(1);\n\n    ngx_udpv2_process_posted_traffic();\n}\n\nstatic int\nngx_xudp_from_ngx_log_level(ngx_cycle_t *cycle)\n{\n    ngx_uint_t lev;\n    if (cycle->log) {\n        lev = cycle->log->log_level;\n        if (lev <= NGX_LOG_ERR) {\n            return XUDP_LOG_ERR;\n        }else if(lev <= NGX_LOG_WARN) {\n            return XUDP_LOG_WARN;\n        }else if(lev <= NGX_LOG_INFO) {\n            return XUDP_LOG_INFO;\n        }else if(lev == NGX_LOG_DEBUG) {\n            return XUDP_LOG_DEBUG;\n        }\n    }\n    return XUDP_LOG_ERR;\n}\n\nngx_int_t\nngx_xudp_error_is_fatal(int error)\n{\n    if (error == -XUDP_ERR_CQ_NOSPACE || error == -XUDP_ERR_TX_NOSPACE) {\n        return 0;\n    }\n    return 1;\n}\n\n/**\n * @param c\n * @return  enable connection for xudp tx\n * */\nvoid\nngx_xudp_enable_tx(ngx_connection_t *c)\n{\n    ngx_xudp_conf_t     *xcf;\n    xcf = (ngx_xudp_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx, ngx_xudp_core_module);\n    if (!ngx_xudp_engine || xcf->no_xudp_tx) {\n        return ;\n    }\n    if (c->listening && c->listening->xudp) {\n        c->xudp_tx = 1;\n    }\n}\n\nvoid\nngx_xudp_terminate_xudp_binding(ngx_cycle_t *cycle)\n{\n    ngx_uint_t         i;\n    ngx_listening_t   *ls;\n    ngx_connection_t  *c;\n#if (T_DYRELOAD)\n    ngx_uint_t         j;\n    ngx_listening_t  **lsp;\n#endif\n    xudp               *engine;\n\n    if (!ngx_xudp_engine) {\n        return;\n    }\n\n    /* reset global ngx_xudp_engine */\n    engine  = ngx_xudp_clear(cycle);\n    ngx_memory_barrier();\n\n    i = 0;\n\n    /* for each listening socket */\n#if (T_DYRELOAD)\n    i = 0;\n    lsp = cycle->listening.elts;\n    for (j = 0; j < cycle->listening.nelts; j++) {\n        ls = lsp[j];\n#else\n    ls = cycle->listening.elts;\n    for (i = 0; i < cycle->listening.nelts; i++) {\n#endif\n\n#if (T_PROCESS_IDX)\n        if (ls[i].proc_idx &&ls[i].proc_idx != ngx_process_idx) {\n            continue;\n        }\n#endif\n\n#if ! (T_RELOAD)\n#if (NGX_HAVE_REUSEPORT)\n        if (ls[i].reuseport && ls[i].worker != ngx_worker) {\n            continue;\n        }\n#endif\n#endif\n\n        if (!ls[i].for_xudp) {\n            continue;\n        }\n\n        if (ls[i].worker != ngx_worker) {\n            continue;\n        }\n\n        if (ls[i].fd == (ngx_socket_t) -1) {\n            continue;\n        }\n\n        c = ls[i].connection;\n        if (c) {\n\n            if (c->read->active) {\n                ngx_del_event(c->read, NGX_READ_EVENT, 0);\n            }\n\n            if (c->write->active) {\n                /**\n                 * trigger all the events that run on this event\n                 * because the ngx_xudp_engine has been cleared in advance,\n                 * xudp send will be failed\n                 * trigger degrade at last\n                 * */\n                ngx_event_process_posted((ngx_cycle_t *) ngx_cycle, &ls[i].writable_queue);\n                /* del from event poll */\n                ngx_del_event(c->read, NGX_WRITE_EVENT, 0);\n            }\n\n            c->fd = (ngx_socket_t) -1;\n            ngx_free_connection(c);\n            ls[i].connection = NULL;\n        }\n\n        if (ngx_close_socket(ls[i].fd) == -1) {\n            ngx_log_error(NGX_LOG_DEBUG, cycle->log, ngx_socket_errno,\n                          \"close xudp socket() %V failed \", &ls[i].addr_text);\n        }\n\n        ls[i].fd = (ngx_socket_t) -1;\n    }\n\n    /* call unbind */\n    xudp_unbind(engine);\n}\n"
  },
  {
    "path": "modules/mod_xudp/ngx_xudp_module.h",
    "content": "/*\n * Copyright (C) 2020-2023 Alibaba Group Holding Limited\n */\n\n#ifndef _NGX_XUDP_MODULE_H_INCLUDED_\n#define _NGX_XUDP_MODULE_H_INCLUDED_\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\nstruct ngx_xudp_conf_s;\ntypedef struct ngx_xudp_conf_s ngx_xudp_conf_t;\n\nstruct ngx_xudp_conf_s\n{\n    /* xudp configure address */\n    ngx_array_t      xudp_address;\n    /* xudp core path */\n    ngx_str_t        dispatcher_path;\n    /* xudp is on */\n    ngx_int_t        on;\n    /* sndbuf */\n    ngx_uint_t       sndnum;\n    /* rcvbuf */\n    ngx_uint_t       rcvnum;\n    /* allow degrade ,default true */\n    ngx_flag_t       allow_degrade;\n    /* force xudp off */\n    ngx_flag_t       no_xudp;\n    /* force xudp tx off */\n    ngx_flag_t       no_xudp_tx;\n    /* interval waiting xudp load */\n    ngx_msec_t       retries_interval;\n    /* max count for retry xudp load */\n    ngx_uint_t       max_retries;\n};\n\n#endif //_NGX_XUDP_MODULE_H_INCLUDED_"
  },
  {
    "path": "modules/mod_xudp/xquic-xdp/dispatch_xquic.c",
    "content": "/*\n * Copyright (C) 2020-2023 Alibaba Group Holding Limited\n * SPDX-License-Identifier: GPL-2.0\n */\n\n#include \"kern_core.c\"\n#include \"xquic_xdp.h\"\n\nbpf_map map_xquic = {\n    .type = BPF_MAP_TYPE_ARRAY,\n    .key_size = sizeof(int),\n    .value_size = sizeof(struct kern_xquic),\n    .max_entries = 1,\n};\n\n#define XQUIC_WORKER_PID(key)\t((key) & 0x3fffff)\n\n/**\n* source code from nginx , reprogramming for kernel ebpf\n*/\nstatic u32\nngx_murmur_hash2(struct xudp_ctx *ctx, u8 *data, u8 len, u8 *err)\n{\n    u32  h, k;\n\n    h = 0 ^ len;\n\n#define MURMUR_ROUND()                          \\\n    if (len < 4) break;                         \\\n    do {                                        \\\n        if (!access_ok(ctx, (u32*) (data))) {   \\\n            goto violence;                      \\\n        }                                       \\\n        k  = data[0];                           \\\n        k |= data[1] << 8;                      \\\n        k |= data[2] << 16;                     \\\n        k |= data[3] << 24;                     \\\n        k *= 0x5bd1e995;                        \\\n        k ^= k >> 24;                           \\\n        k *= 0x5bd1e995;                        \\\n        h *= 0x5bd1e995;                        \\\n        h ^= k;                                 \\\n        data += 4;                              \\\n        len -= 4;                               \\\n    }while(0)\n\n    do {\n\n        /**\n         * DCID max length is 20 bytes\n         * */\n        MURMUR_ROUND();\n        MURMUR_ROUND();\n        MURMUR_ROUND();\n        MURMUR_ROUND();\n        MURMUR_ROUND();\n\n    }while(0);\n\n#undef MURMUR_ROUND\n\n    switch (len) {\n        case 3:\n        {\n            if (!access_ok(ctx, data + 2)) {\n                goto violence;\n            }\n            h ^= data[2] << 16;\n        }\n        case 2:\n        {\n            if (!access_ok(ctx, data + 1)) {\n                goto violence;\n            }\n            h ^= data[1] << 8;\n        }\n        case 1:\n        {\n            if (!access_ok(ctx, data)) {\n                goto violence;\n            }\n            h ^= data[0];\n            h *= 0x5bd1e995;\n        }\n    }\n\n    h ^= h >> 13;\n    h *= 0x5bd1e995;\n    h ^= h >> 15;\n    return h;\n\nviolence:\n    *err = 1;\n    return 0;\n}\n\nstatic int\nxskmap_dispatch(struct xudp_ctx *ctx)\n{\n    struct kern_xquic *xquic;\n\n    int r, xquic_key;\n    u32 *cipher_worker, worker, pid, salt;\n    u8 *dcid, err;\n    u8 *p;\n\n    xquic_key = XUDP_XQUIC_MAP_DEFAULT_KEY;\n\n    xquic = bpf_map_lookup_elem(&map_xquic, (const void *) &xquic_key);\n\n    /* nginx can off xudp */\n    if (!xquic || xquic->capture == 0) {\n        /* pass to kernel */\n        return XDP_PASS;\n    }\n\n    /* get UDP payload */\n    p = (u8*) (ctx->hdrs.udp + 1);\n    if (!access_ok(ctx, p)) {\n        goto fail;\n    }\n\n    if ((p[0] & 0xC0) != 0x40) {\n        /* pass to kernel , all non-short header packets pass to kernel */\n        goto fail;\n    }\n\n    /* short header |HEADER(1)|DCID| */\n    dcid = p + 1;\n\n    err = 0;\n    /* calculate salt */\n    salt = ngx_murmur_hash2(ctx, dcid, xquic->salt_range, &err);\n    if (err) {\n        /* invalid quic packet */\n        goto fail;\n    }\n\n    /* get cipher worker */\n    cipher_worker = (u32*)(dcid + xquic->offset);\n    if (!access_ok(ctx, cipher_worker)) {\n        goto fail;\n    }\n\n    /* decrypt */\n    worker = (bpf_ntohl(*cipher_worker) ^ xquic->mask) - salt;\n\n    /* get PID */\n    pid = XQUIC_WORKER_PID(worker);\n\n    /* try use PID */\n    r = xskmap_dict_go(ctx, pid);\n    /* success */\n    if (r >= 0) {\n        return r;\n    }\n\nfail:\n    // all error should pass to kernel\n    return XDP_PASS;\n}\n"
  },
  {
    "path": "modules/mod_xudp/xquic-xdp/readme.md",
    "content": "## config\n```\nmake config root=/path/to/libxudp\n```\n\n## build\n```\nmake\n```\n\n## how to use\n\nsee test.c. this from libxudp/tools/xudp_echo_server.c\n\nsee the commit b6170f0557db95a2ef74346515759d35f4cc70de to know how to\nuse this xquic xdp.\n\n"
  },
  {
    "path": "modules/mod_xudp/xquic-xdp/test.c",
    "content": "/*\n * Copyright (C) 2020-2023 Alibaba Group Holding Limited\n */\n\n#include \"xudp.h\"\n#include <sys/epoll.h>\n#include <malloc.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <signal.h>\n\n#include <sys/types.h>\n#include <sys/stat.h>\n#include <unistd.h>\n#include <stdio.h>\n#include <fcntl.h>\n\n#include \"xquic_xdp.h\"\n\nstruct connect{\n\txudp *x;\n\txudp_channel *ch;\n\tvoid (*handler)(struct connect *);\n\tint gid;\n};\n\nstatic xudp *gx;\n\nvoid handler(struct connect *c)\n{\n\txudp_msg *m;\n\txudp_channel *ch = c->ch;\n\tint n, i, ret;\n\n    \txudp_def_msg(hdr, 100);\n\n\twhile (true) {\n        \thdr->used = 0;\n\n\t\tn = xudp_recv_channel(ch, hdr, 0);\n\t\tif (n < 0)\n\t\t\tbreak;\n\n\t\tfor (i = 0; i < hdr->used; ++i) {\n            \t\tm = hdr->msg + i;\n\n\t\t\tprintf(\"usec: %lld gid: %d recv msg: %.*s\",\n\t\t\t       m->usec, c->gid, m->size, m->p);\n\n\t\t\tret = xudp_send_channel(ch, m->p, m->size, &m->peer_addr, 0);\n\n\t\t\tif (ret < 0) {\n\t\t\t\tprintf(\"xudp_send_one fail. %d\\n\", ret);\n\t\t\t}\n\t\t}\n\n\n\t\txudp_recycle(hdr);\n\n\t\txudp_commit_channel(ch);\n\t}\n}\n\nstatic int epoll_add(xudp *x, int efd, int gid)\n{\n\tstruct epoll_event e;\n\tstruct connect *c;\n\txudp_channel *ch;\n\txudp_group *g;\n\tint fd, key;\n\n\tkey = '0' + gid;\n\n\te.events = EPOLLIN;\n\n\tg = xudp_group_get(x, 0);\n\n\txudp_group_channel_foreach(ch, g) {\n\n\t\tfd = xudp_channel_get_fd(ch);\n\n\t\tc = malloc(sizeof(*c));\n\t\tc->ch = ch;\n\t\tc->x = x;\n\t\tc->handler = handler;\n\t\tc->gid = gid;\n\n\t\te.data.ptr = c;\n\n\n\t\tepoll_ctl(efd, EPOLL_CTL_ADD, fd, &e);\n\t}\n\n\txudp_dict_set_group_key(g, key);\n\n\treturn 0;\n}\n\nstatic int loop(int efd)\n{\n\tstruct connect *c;\n\tstruct epoll_event e[1024];\n\tint n, i;\n\n\twhile (1) {\n\t\tn = epoll_wait(efd, e, sizeof(e)/sizeof(e[0]), -1);\n\n\t\tif (n == 0)\n\t\t\tcontinue;\n\n\t\tif (n < 0) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tfor (i = 0; i < n; ++i) {\n\t\t\tc = e[i].data.ptr;\n\t\t\tc->handler(c);\n\t\t}\n\t}\n}\n\nstatic void int_exit(int sig)\n{\n\t(void)sig;\n\txudp_free(gx);\n\texit(EXIT_SUCCESS);\n}\n\nstatic int xquic_xdp_load(xudp_conf *conf)\n{\n\tint xdp_custom_size;\n\tvoid *xdp_custom;\n\tstruct stat stat;\n\tint fd, err, n;\n\n\tfd = open(\"kern_xquic.o\", O_RDONLY);\n\tif (fd < 0)\n\t\treturn -1;\n\n\terr = fstat(fd, &stat);\n\tif (err)\n\t\treturn -1;\n\n\txdp_custom_size = stat.st_size;\n\txdp_custom = malloc(xdp_custom_size);\n\n\tn = read(fd, xdp_custom, xdp_custom_size);\n\tif (n != xdp_custom_size)\n\t\treturn -1;\n\n\tconf->flow_dispatch = XUDP_FLOW_DISPATCH_TYPE_CUSTOM;\n\tconf->xdp_custom = xdp_custom;\n\tconf->xdp_custom_size = xdp_custom_size;\n\n\t// config this by the key type\n\t// xquic may set this config to true: map_dict_n_max_pid\n\tconf->map_dict_n = 100;\n\n\treturn 0;\n}\n\nstatic int xquic_args(xudp *x)\n{\n\tstruct kern_xquic kern_xquic = {};\n\tint key = 0;\n\n\tkern_xquic.offset = 0;\n\tkern_xquic.mask = (1 << 8) - 1;\n\n\treturn xudp_bpf_map_update(x, XUDP_XQUIC_MAP_NAME, &key, &kern_xquic);\n}\n\nint main(int argc, char *argv[])\n{\n\tstruct sockaddr_in in = {};\n\txudp *x;\n\tint efd, ret;\n\tchar *addr; int port;\n\n\txudp_conf conf = {};\n\n\tif (argc != 3) {\n\t\taddr = \"0.0.0.0\";\n\t\tport = 8080;\n\t} else {\n\t\taddr = argv[1];\n\t\tport = atoi(argv[2]);\n\t}\n\n\tprintf(\"bind %s:%d\\n\", addr, port);\n\n\tconf.group_num     = 2;\n\tconf.log_with_time = true;\n\n\t/* for xquic */\n\tif (xquic_xdp_load(&conf)) {\n\t\tprintf(\"xquic xdp load fail\\n\");\n\t\treturn -1;\n\t}\n\t/* for xquic end */\n\n\tx = xudp_init(&conf, sizeof(conf));\n\tif (!x) {\n\t\tprintf(\"xudp init fail\\n\");\n\t\treturn -1;\n\t}\n\tgx = x;\n\n\tin.sin_family      = AF_INET;\n\tin.sin_addr.s_addr = inet_addr(addr);\n\tin.sin_port        = htons(port);\n\tret = xudp_bind(x, &in, 1);\n\n\tif (ret) {\n\t\tprintf(\"xudp bind fail %d\\n\", ret);\n\t\treturn -1;\n\t}\n\n\t/* for xquic */\n\tif (xquic_args(x)) {\n\t\tprintf(\"xquic xdp config fail\\n\");\n\t\treturn -1;\n\t}\n\t/* for xquic end */\n\n\tefd = epoll_create(1024);\n\n\tepoll_add(x, efd, 0);\n\tepoll_add(x, efd, 1);\n\n\tsignal(SIGINT, int_exit);\n\tsignal(SIGTERM, int_exit);\n\tsignal(SIGABRT, int_exit);\n\n\tloop(efd);\n}\n"
  },
  {
    "path": "modules/mod_xudp/xquic-xdp/xquic_xdp.h",
    "content": "/*\n * Copyright (C) 2020-2023 Alibaba Group Holding Limited\n */\n\n#ifndef  __XQUIC_H__\n#define __XQUIC_H__\n\nstruct kern_xquic {\n    /* capturing network traffic in xudp */\n    /* system will control traffic for capture=0 */\n    u8  capture;\n    /* offset of worker id in cid */\n    u8  offset;\n    /* just padding */\n    u16 padding;\n    /* secret for worker id*/\n    u32 mask;\n    /* salt range for worker id*/\n    u32 salt_range;\n};\n\n#define XUDP_XQUIC_MAP_DEFAULT_KEY  (0)\n\n#define XUDP_XQUIC_MAP_NAME \"map_xquic\"\n\n#endif\n\n\n"
  },
  {
    "path": "modules/ngx_backtrace_module/config",
    "content": "ngx_addon_name=ngx_backtrace_module\nHTTP_MODULES=\"$HTTP_MODULES ngx_backtrace_module\"\nNGX_ADDON_SRCS=\"$NGX_ADDON_SRCS $ngx_addon_dir/ngx_backtrace_module.c\"\nLINK=\"$LINK -rdynamic\"\n\nngx_feature=\"BACKTRACE\"\nngx_feature_name=\nngx_feature_run=no\nngx_feature_incs=\"#include <execinfo.h>\"\nngx_feature_path=\nngx_feature_test=\"void *buffer[10];\n                  backtrace(buffer, sizeof(buffer) / sizeof(buffer[0]));\"\n. auto/feature\n\nif [ $ngx_found = no ]; then\n    cat << END\n    \n    $0: error: the backtrace module requires backtrace function.\n    You can either do not enable the module or update your libc library.\n    \nEND\n\n    exit 1\nfi\n"
  },
  {
    "path": "modules/ngx_backtrace_module/ngx_backtrace_module.c",
    "content": "\n/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n#include <execinfo.h>\n\n\n#define NGX_BACKTRACE_DEFAULT_STACK_MAX_SIZE 30\n\n\ntypedef struct {\n    int     signo;\n    char   *signame;\n    char   *name;\n    void  (*handler)(int signo);\n} ngx_signal_t;\n\n\nstatic char *ngx_backtrace_files(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic void ngx_error_signal_handler(int signo);\nstatic ngx_int_t ngx_backtrace_init_worker(ngx_cycle_t *cycle);\nstatic void *ngx_backtrace_create_conf(ngx_cycle_t *cycle);\n\n\ntypedef struct {\n    ngx_log_t              *log;\n    ngx_int_t               max_stack_size;\n} ngx_backtrace_conf_t;\n\n\nstatic ngx_signal_t  ngx_backtrace_signals[] = {\n    { SIGABRT, \"SIGABRT\", \"\", ngx_error_signal_handler },\n\n#ifdef SIGBUS\n    { SIGBUS, \"SIGBUS\", \"\", ngx_error_signal_handler },\n#endif\n\n    { SIGFPE, \"SIGFPE\", \"\", ngx_error_signal_handler },\n\n    { SIGILL, \"SIGILL\", \"\", ngx_error_signal_handler },\n\n    { SIGIOT, \"SIGIOT\", \"\", ngx_error_signal_handler },\n\n    { SIGSEGV, \"SIGSEGV\", \"\", ngx_error_signal_handler },\n\n    { 0, NULL, \"\", NULL }\n};\n\n\nstatic ngx_command_t  ngx_backtrace_commands[] = {\n\n    { ngx_string(\"backtrace_log\"),\n      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,\n      ngx_backtrace_files,\n      0,\n      0,\n      NULL },\n\n    { ngx_string(\"backtrace_max_stack_size\"),\n      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_num_slot,\n      0,\n      offsetof(ngx_backtrace_conf_t, max_stack_size),\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_core_module_t  ngx_backtrace_module_ctx = {\n    ngx_string(\"backtrace\"),\n    ngx_backtrace_create_conf,\n    NULL\n};\n\n\nngx_module_t  ngx_backtrace_module = {\n    NGX_MODULE_V1,\n    &ngx_backtrace_module_ctx,             /* module context */\n    ngx_backtrace_commands,                /* module directives */\n    NGX_CORE_MODULE,                       /* module type */\n    NULL,                                  /* init master */\n    NULL,                                  /* init module */\n    ngx_backtrace_init_worker,             /* 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_init_error_signals(ngx_log_t *log)\n{\n    ngx_signal_t      *sig;\n    struct sigaction   sa;\n\n    for (sig = ngx_backtrace_signals; sig->signo != 0; sig++) {\n        ngx_memzero(&sa, sizeof(struct sigaction));\n        sa.sa_handler = sig->handler;\n        sigemptyset(&sa.sa_mask);\n        if (sigaction(sig->signo, &sa, NULL) == -1) {\n            ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,\n                          \"sigaction(%s) failed\", sig->signame);\n            return NGX_ERROR;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_error_signal_handler(int signo)\n{\n    void                 *buffer;\n    size_t                size;\n    ngx_log_t            *log;\n    ngx_signal_t         *sig;\n    struct sigaction      sa;\n    ngx_backtrace_conf_t *bcf;\n\n    for (sig = ngx_backtrace_signals; sig->signo != 0; sig++) {\n        if (sig->signo == signo) {\n            break;\n        }\n    }\n\n    bcf = (ngx_backtrace_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,\n                                                ngx_backtrace_module);\n\n    log = bcf->log ? bcf->log : ngx_cycle->log;\n    ngx_log_error(NGX_LOG_ERR, log, 0,\n                  \"nginx coredump by signal %d (%s)\", signo, sig->signame);\n\n    ngx_memzero(&sa, sizeof(struct sigaction));\n    sa.sa_handler = SIG_DFL;\n    sigemptyset(&sa.sa_mask);\n    if (sigaction(signo, &sa, NULL) == -1) {\n        ngx_log_error(NGX_LOG_ERR, log, ngx_errno,\n                      \"sigaction(%s) failed\", sig->signame);\n    }\n\n    if (bcf->max_stack_size == NGX_CONF_UNSET) {\n        bcf->max_stack_size = NGX_BACKTRACE_DEFAULT_STACK_MAX_SIZE;\n    }\n\n    buffer = ngx_pcalloc(ngx_cycle->pool, sizeof(void *) * bcf->max_stack_size);\n    if (buffer == NULL) {\n        goto invalid;\n    }\n\n    size = backtrace(buffer, bcf->max_stack_size);\n    backtrace_symbols_fd(buffer, size, log->file->fd);\n\ninvalid:\n\n    kill(ngx_getpid(), signo);\n}\n\n\nstatic char *\nngx_backtrace_files(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n    ngx_backtrace_conf_t *bcf;\n\n    bcf = (ngx_backtrace_conf_t *) ngx_get_conf(cf->cycle->conf_ctx,\n                                                ngx_backtrace_module);\n\n    return ngx_log_set_log(cf, &bcf->log);\n}\n\n\nstatic ngx_int_t\nngx_backtrace_init_worker(ngx_cycle_t *cycle)\n{\n    if (ngx_init_error_signals(cycle->log) == NGX_ERROR) {\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void *\nngx_backtrace_create_conf(ngx_cycle_t *cycle)\n{\n    ngx_backtrace_conf_t  *bcf;\n\n    bcf = ngx_pcalloc(cycle->pool, sizeof(ngx_backtrace_conf_t));\n    if (bcf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc()\n     *\n     *     bcf->log = NULL;\n     */\n\n    bcf->max_stack_size = NGX_CONF_UNSET;\n\n    return bcf;\n}\n"
  },
  {
    "path": "modules/ngx_debug_conn/README.cn",
    "content": "ngx_debug_conn\n==============\n\n该模块可以提供NGINX/Tengine连接的状态信息。\n\n示例\n=======\n\n获取NGINX/Tengine连接的状态信息\n---------------------------------\n\n```\n http {\n    server {\n        listen 80;\n\n        location = /debug_conn {\n            debug_conn;\n        }\n    }\n }\n```\n\n请求URI /debug_conn，可以获取到该NGINX/Tengine连接的使用情况统计。\n页面输出如下：\n\n```\n$ curl 'localhost:80/debug_conn'\npid:70568\nconnections:3\n--------- [1] --------\nconns[i]: 0\n      fd: 6\n    addr: 0.0.0.0:80\n    sent: 0\n  action: (null: listening)\n handler: r:000000010DAEBEC0 w:0000000000000000\nrequests: 0\npoolsize: 0\n--------- [2] --------\nconns[i]: 1\n      fd: 7\n    addr: (null)\n    sent: 0\n  action: (null: channel)\n handler: r:000000010DAFB770 w:0000000000000000\nrequests: 0\npoolsize: 0\n--------- [3] --------\nconns[i]: 2\n      fd: 3\n    addr: 127.0.0.1\n    sent: 0\n  action: (null)\n handler: r:000000010DB28CA0 w:000000010DB28CA0\nrequests: 1\npoolsize: 0\n********* request ******\n     uri: http://localhost/debug_conn\n handler: r:000000010DB26820 w:000000010DB29770\nstartsec: 1542356262\npoolsize: 0\n```\n\n连接的使用情况统计\n-----------------------------------\n\n数据说明\n====\n\n每个数据段落如\"[1]\"包含当前标号对应连接的状态信息，数据项意义如下：\n\n* __conns__: 当前连接标号，用于信息统计\n* __fd__: 当前连接的句柄号\n* __addr__: 当前连接的监听地址\n* __sent__: 当前连接的已发送数据量\n* __action__: 当前连接的log action\n* __handler__: 当前连接的读写事件挂载handler地址，配合addr2line来查询对应函数\n* __requests__: 当前连接上的请求量\n* __poolsize__: 当前连接的内存池大小\n* __request__: 当前连接上的请求\n* __uri__: 当前连接上的请求的请求地址\n* __handler__: 当前连接上的请求的读写事件挂载handler地址，配合addr2line来查询对应函数\n* __startsec__: 当前连接上的请求的起始时间戳\n* __poolsize__: 当前连接上的请求的内存池大小\n\nNGINX兼容性\n===================\n\n* 1.13.4 (stable version of 1.13.x) 及其更高版本\n\nTengine兼容性\n=====================\n\n* 2.1.1 (stable version of 2.1.x) 及其更高版本\n\n安装说明\n=======\n\n源码安装，执行如下命令：\n\n```\n$ wget http://nginx.org/download/nginx-1.13.4.tar.gz\n$ tar -xzvf nginx-1.13.4.tar.gz\n$ cd nginx-1.13.4/\n$ ./configure --add-module=/path/to/ngx_debug_conn\n$ make -j4 && make install\n```\n\n\n配置指令\n=========\n\n语法: **debug_conn**\n\n默认: `none`\n\n位置: `server, location`\n\nNGINX/Tenigne的连接状态信息可以通过该location访问得到。\n\n注意信息\n=========\n\n```\n********* request ******\n```\n\nrequest数据段落仅在当前连接上有请求存在时展示\n\n"
  },
  {
    "path": "modules/ngx_debug_conn/README.md",
    "content": "ngx_debug_conn\n==============\n\nThis module provides access to information of connection usage for nginx/tengine.\n\nExample\n=======\n\nGet information of connection usage.\n---------------------------------\n\n```\n http {\n    server {\n        listen 80;\n\n        location = /debug_conn {\n            debug_conn;\n        }\n    }\n }\n```\n\nRequesting URI /debug_conn, you will get information of connection usage for nginx/tengine.\nThe output page may look like as follows:\n\n```\n$ curl 'localhost:80/debug_conn'\npid:70568\nconnections:3\n--------- [1] --------\nconns[i]: 0\n      fd: 6\n    addr: 0.0.0.0:80\n    sent: 0\n  action: (null: listening)\n handler: r:000000010DAEBEC0 w:0000000000000000\nrequests: 0\npoolsize: 0\n--------- [2] --------\nconns[i]: 1\n      fd: 7\n    addr: (null)\n    sent: 0\n  action: (null: channel)\n handler: r:000000010DAFB770 w:0000000000000000\nrequests: 0\npoolsize: 0\n--------- [3] --------\nconns[i]: 2\n      fd: 3\n    addr: 127.0.0.1\n    sent: 0\n  action: (null)\n handler: r:000000010DB28CA0 w:000000010DB28CA0\nrequests: 1\npoolsize: 0\n********* request ******\n     uri: http://localhost/debug_conn\n handler: r:000000010DB26820 w:000000010DB29770\nstartsec: 1542356262\npoolsize: 0\n```\n\nGet information of connection usage\n-----------------------------------\n\nData\n====\n\nEvery block like \"[1]\" except the related connection usage as follows:\n\n* __conns__: sequence of current connection\n* __fd__: file description of current connection\n* __addr__: listening address of current connection\n* __sent__: data sent size of current connection\n* __action__: log action of current connection\n* __handler__: read/write event handler of current connection, use addr2line to find the real function\n* __requests__: request numbers of current connection\n* __poolsize__: memory pool size of current connection\n* __request__: request of current connection\n* __uri__: request uri of current connection\n* __handler__: read/write event handler of the request, use addr2line to find the real function\n* __startsec__: start timestamp of the request\n* __poolsize__: memory pool size of the request\n\nNginx Compatibility\n===================\n\nThe latest module is compatible with the following versions of nginx:\n\n* 1.13.4 (stable version of 1.13.x) and later\n\nTengine Compatibility\n=====================\n\n* 2.1.1 (stable version of 2.1.x) and later\n\nInstall\n=======\n\nInstall this module from source:\n\n```\n$ wget http://nginx.org/download/nginx-1.13.4.tar.gz\n$ tar -xzvf nginx-1.13.4.tar.gz\n$ cd nginx-1.13.4/\n$ ./configure --add-module=/path/to/ngx_debug_conn\n$ make -j4 && make install\n```\n\nDirective\n=========\n\nSyntax: **debug_conn**\n\nDefault: `none`\n\nContext: `server, location`\n\nThe information of nginx connection usage will be accessible from the surrounding location.\n\nException\n=========\n\n```\n********* request ******\n```\n\nThe request block will only show when request exists in connection.\n"
  },
  {
    "path": "modules/ngx_debug_conn/config",
    "content": "ngx_addon_name=ngx_http_debug_conn_module\nHTTP_MODULES=\"$HTTP_MODULES ngx_http_debug_conn_module\"\nNGX_ADDON_SRCS=\"$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_debug_conn_module.c\"\n"
  },
  {
    "path": "modules/ngx_debug_conn/ngx_http_debug_conn_module.c",
    "content": "\n/*\n * Copyright (C) 2016 Alibaba Group Holding Limited\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\n#if (NGX_DEBUG_POOL)\n#define ngx_pool_size(p) ((p)->size)\n#else\n#define ngx_pool_size(p) ((size_t) 0)\n#endif\n\n#if (NGX_HTTP_SSL)\n#define ngx_request_scheme(r) ((r)->connection->ssl ? \"https\" : \"http\")\n#else\n#define ngx_request_scheme(r) \"http\"\n#endif\n\n\nstatic char *ngx_http_debug_conn(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\nstatic ngx_int_t ngx_http_debug_conn_buf(ngx_pool_t *pool, ngx_buf_t *b);\n\nstatic ngx_command_t  ngx_http_debug_conn_commands[] = {\n\n    { ngx_string(\"debug_conn\"),\n      NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,\n      ngx_http_debug_conn,\n      0,\n      0,\n      NULL },\n\n    ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_debug_conn_module_ctx = {\n    NULL,                          /* preconfiguration */\n    NULL,                          /* 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\nngx_module_t  ngx_http_debug_conn_module = {\n    NGX_MODULE_V1,\n    &ngx_http_debug_conn_module_ctx,    /* module context */\n    ngx_http_debug_conn_commands,       /* 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_debug_conn_handler(ngx_http_request_t *r)\n{\n    ngx_int_t    rc;\n    ngx_buf_t   *b;\n    ngx_chain_t  out;\n\n    if (r->method != NGX_HTTP_GET) {\n        return NGX_HTTP_NOT_ALLOWED;\n    }\n\n    rc = ngx_http_discard_request_body(r);\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));\n    if (b == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    if (ngx_http_debug_conn_buf(r->pool, b) == NGX_ERROR) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    r->headers_out.status = NGX_HTTP_OK;\n    r->headers_out.content_length_n = b->last - b->pos;\n\n    rc = ngx_http_send_header(r);\n\n    if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {\n        return rc;\n    }\n\n    out.buf = b;\n    out.next = NULL;\n\n    return ngx_http_output_filter(r, &out);\n}\n\n\nstatic ngx_int_t\nngx_http_debug_conn_buf(ngx_pool_t *pool, ngx_buf_t *b)\n{\n    u_char              *p;\n    size_t               size;\n    ngx_uint_t           i, k, n;\n    ngx_str_t            addr, action, host, uri;\n    ngx_connection_t    *c;\n    ngx_http_request_t  *r;\n\n#define NGX_CONN_TITLE_SIZE     (sizeof(NGX_CONN_TITLE_FORMAT) - 1 + NGX_TIME_T_LEN + NGX_INT_T_LEN)     /* sizeof pid_t equals time_t */\n#define NGX_CONN_TITLE_FORMAT   \"pid:%P\\n\"                  \\\n                                \"connections:%ui\\n\"\n\n#define NGX_CONN_ENTRY_SIZE     (sizeof(NGX_CONN_ENTRY_FORMAT) - 1 + \\\n                                 NGX_SIZE_T_LEN * 2 + NGX_SOCKADDR_STRLEN + 32 /* action */ + \\\n                                 NGX_OFF_T_LEN +  NGX_INT_T_LEN * 3 + NGX_PTR_SIZE * 2 * 2)\n#define NGX_CONN_ENTRY_FORMAT   \"--------- [%ui] --------\\n\"\\\n                                \"conns[i]: %ui\\n\"         \\\n                                \"      fd: %z\\n\"          \\\n                                \"    addr: %V\\n\"          \\\n                                \"    sent: %O\\n\"          \\\n                                \"  action: %V\\n\"          \\\n                                \" handler: r:%p w:%p\\n\"   \\\n                                \"requests: %ui\\n\"         \\\n                                \"poolsize: %z\\n\"\n\n#define NGX_REQ_ENTRY_SIZE      (sizeof(NGX_REQ_ENTRY_FORMAT) - 1 + \\\n                                 sizeof(\"https\") - 1 + 32 /* host */ + 64 /* uri */ + \\\n                                 NGX_TIME_T_LEN + NGX_PTR_SIZE * 2 * 2 + NGX_SIZE_T_LEN)\n#define NGX_REQ_ENTRY_FORMAT    \"********* request ******\\n\"\\\n                                \"     uri: %s://%V%V\\n\"   \\\n                                \" handler: r:%p w:%p\\n\"   \\\n                                \"startsec: %T\\n\"          \\\n                                \"poolsize: %z\\n\"\n\n    n = ngx_cycle->connection_n - ngx_cycle->free_connection_n;\n\n    size = NGX_CONN_TITLE_SIZE + n * (NGX_CONN_ENTRY_SIZE + NGX_REQ_ENTRY_SIZE);\n    p = ngx_palloc(pool, size);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    b->pos = p;\n\n    p = ngx_sprintf(p, NGX_CONN_TITLE_FORMAT, ngx_pid, n);\n\n    /* lines of entry */\n\n    k = 0;\n\n    for (i = 0; i < ngx_cycle->connection_n; i++) {\n        c = &ngx_cycle->connections[i];\n\n        if (c->fd <= 0) {\n            continue;\n        }\n\n        k++;\n\n        if (n == 0) {\n            break;\n        }\n        n--;\n\n        /* addr_text */\n\n        if (c->addr_text.data != NULL) {\n            addr.data = c->addr_text.data;\n            addr.len = ngx_min(c->addr_text.len, NGX_SOCKADDR_STRLEN);\n\n        } else if (c->listening && c->listening->addr_text.data != NULL) {\n            addr.data = c->listening->addr_text.data;\n            addr.len = ngx_min(c->listening->addr_text.len, NGX_SOCKADDR_STRLEN);\n\n        } else {\n            ngx_str_set(&addr, \"(null)\");\n        }\n\n        /* action */\n\n        if (c->log->action != NULL) {\n            action.data = (u_char *) c->log->action;\n            action.len = ngx_min(ngx_strlen(c->log->action), 32);\n\n#if (NGX_SSL)\n        } else if (c->ssl) {\n\n            ngx_str_set(&action, \"(null: ssl)\");\n#endif\n\n        } else if (c->listening && c->listening->connection == c) {\n            ngx_str_set(&action, \"(null: listening)\");\n\n        } else if (c->data == NULL) {\n            ngx_str_set(&action, \"(null: channel)\");\n\n        } else {\n            ngx_str_set(&action, \"(null)\");\n        }\n\n        /* entry format of connection */\n\n        p = ngx_snprintf(p, NGX_CONN_ENTRY_SIZE, NGX_CONN_ENTRY_FORMAT,\n                         k, i,\n                         c->fd,\n                         &addr,\n                         c->sent,\n                         &action,\n                         c->read->handler, c->write->handler,\n                         c->requests,\n                         c->pool ? ngx_pool_size(c->pool) : (size_t) 0);\n\n        /* c->data: http request */\n\n        if (c->data != NULL) {\n            r = (ngx_http_request_t *) c->data;\n            if (r->signature == NGX_HTTP_MODULE && r->connection == c) {\n\n                /* request host */\n\n                if (r->headers_in.server.len) {\n                    host.data = r->headers_in.server.data;\n                    host.len = ngx_min(r->headers_in.server.len, 32);\n                } else {\n                    ngx_str_set(&host, \"\");\n                }\n\n                /* request uri */\n\n                uri.data = r->unparsed_uri.data;\n                uri.len = ngx_min(r->unparsed_uri.len, 64);\n\n                /* entry format of request */\n\n                p = ngx_snprintf(p, NGX_REQ_ENTRY_SIZE, NGX_REQ_ENTRY_FORMAT,\n                                 ngx_request_scheme(r), &host, &uri,\n                                 r->read_event_handler, r->write_event_handler,\n                                 r->start_sec,\n                                 r->pool ? ngx_pool_size(r->pool) : (size_t) 0);\n            }\n        }\n\n        p[-1] = '\\n';  /* make sure last char is newline */\n    }\n\n    b->last = p;\n    b->memory = 1;\n    b->last_buf = 1;\n\n    return NGX_OK;\n}\n\n\nstatic char *\nngx_http_debug_conn(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_core_loc_conf_t *clcf;\n\n    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);\n    clcf->handler = ngx_http_debug_conn_handler;\n\n    return NGX_CONF_OK;\n}\n"
  },
  {
    "path": "modules/ngx_debug_conn/t/test.t",
    "content": "#!/usr/bin/perl\n\n# Copyright (C) 2015 Alibaba Group Holding Limited\n\nuse warnings;\nuse strict;\n\nuse Test::More;\nuse File::Copy;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->plan(1)\n        ->write_file_expand('nginx.conf', <<'EOF');\n\nmaster_process off;\ndaemon         off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n    server {\n        listen       127.0.0.1:8080;\n\n        location /debug_conn {\n            debug_conn;\n        }\n    }\n}\n\nEOF\n\n###############################################################################\n\n$t->run();\n\nmy $status = http_get(\"/debug_conn\");\n\nlike($status, qr#uri: http://localhost/debug_conn#,\n     'debug_conn returns information of ngx_cycle->connections[]');\n\nprint \"--- debug for verbose mode ---\\n\",\n      \"$status\",\n      \"------------------------------\\n\";\n\n$t->stop();\n"
  },
  {
    "path": "modules/ngx_debug_pool/config",
    "content": "ngx_addon_name=ngx_http_debug_pool_module\nHTTP_MODULES=\"$HTTP_MODULES ngx_http_debug_pool_module\"\nNGX_ADDON_SRCS=\"$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_debug_pool_module.c\"\n\n# use ngx_debug_pool/ngx_palloc.* instead of src/core/ngx_palloc.*\n\nCORE_DEPS=`echo \"$CORE_DEPS\" | sed \"s/[^ \\n\\t]*ngx_palloc.h//g\"`\nCORE_DEPS=\"$CORE_DEPS $ngx_addon_dir/ngx_palloc.h\"\nCORE_INCS=\"$ngx_addon_dir $CORE_INCS\"\n\nCORE_SRCS=`echo \"$CORE_SRCS\" | sed \"s/[^ \\n\\t]*ngx_palloc.c//g\"`\nNGX_ADDON_SRCS=\"$NGX_ADDON_SRCS $ngx_addon_dir/ngx_palloc.c\"\n\nhave=NGX_DEBUG_POOL . auto/have\n"
  },
  {
    "path": "modules/ngx_debug_pool/debug_pool.gdb",
    "content": "# compile nginx/tengine with mod_debug_pool module\ndefine debug_pool\n  set $_i = 0\n  set $_ss = 0\n  set $_ns = 0\n  set $_cs = 0\n  set $_ls = 0\n  while $_i < 997\n    set $_ps = ngx_pool_stats[$_i]\n    while $_ps != 0x0\n      printf \"size:%12u num:%12u cnum:%12u lnum:%12u %s:%d\\n\", \\\n        $_ps->size, $_ps->num, $_ps->cnum, $_ps->lnum, $_ps->func, $_i\n      set $_ss = $_ss + $_ps->size\n      set $_ns = $_ns + $_ps->num\n      set $_cs = $_cs + $_ps->cnum\n      set $_ls = $_ls + $_ps->lnum\n      set $_ps = $_ps->next\n    end\n    set $_i = $_i + 1\n  end\n  printf \"size:%12u num:%12u cnum:%12u lnum:%12u [SUMMARY]\\n\", $_ss, $_ns, $_cs, $_ls\nend\n"
  },
  {
    "path": "modules/ngx_debug_pool/ngx_http_debug_pool_module.c",
    "content": "\n/*\n * Copyright (C) 2015 Alibaba Group Holding Limited\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\nstatic char *ngx_http_debug_pool(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\nstatic ngx_int_t ngx_http_debug_pool_buf(ngx_pool_t *pool, ngx_buf_t *b);\n\nstatic ngx_command_t  ngx_http_debug_pool_commands[] = {\n\n    { ngx_string(\"debug_pool\"),\n      NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,\n      ngx_http_debug_pool,\n      0,\n      0,\n      NULL },\n\n    ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_debug_pool_module_ctx = {\n    NULL,                          /* preconfiguration */\n    NULL,                          /* 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\nngx_module_t  ngx_http_debug_pool_module = {\n    NGX_MODULE_V1,\n    &ngx_http_debug_pool_module_ctx,    /* module context */\n    ngx_http_debug_pool_commands,       /* 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_debug_pool_handler(ngx_http_request_t *r)\n{\n    ngx_int_t    rc;\n    ngx_buf_t   *b;\n    ngx_chain_t  out;\n\n    if (r->method != NGX_HTTP_GET) {\n        return NGX_HTTP_NOT_ALLOWED;\n    }\n\n    rc = ngx_http_discard_request_body(r);\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));\n    if (b == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    if (ngx_http_debug_pool_buf(r->pool, b) == NGX_ERROR) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    r->headers_out.status = NGX_HTTP_OK;\n    r->headers_out.content_length_n = b->last - b->pos;\n\n    rc = ngx_http_send_header(r);\n\n    if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {\n        return rc;\n    }\n\n    out.buf = b;\n    out.next = NULL;\n\n    return ngx_http_output_filter(r, &out);\n}\n\n\nstatic ngx_int_t\nngx_http_debug_pool_buf(ngx_pool_t *pool, ngx_buf_t *b)\n{\n    u_char              *p, *unit;\n    size_t               size, s, n, cn, ln;\n    ngx_uint_t           i;\n    ngx_pool_stat_t     *stat;\n\n#define NGX_POOL_PID_SIZE       (NGX_TIME_T_LEN + sizeof(\"pid:\\n\") - 1)     /* sizeof pid_t equals time_t */\n#define NGX_POOL_PID_FORMAT     \"pid:%P\\n\"\n#define NGX_POOL_ENTRY_SIZE     (48 /* func */ + 12 * 4 + sizeof(\"size: num: cnum: lnum: \\n\") - 1)\n#define NGX_POOL_ENTRY_FORMAT   \"size:%12z num:%12z cnum:%12z lnum:%12z %s\\n\"\n#define NGX_POOL_SUMMARY_SIZE   (12 * 4 + sizeof(\"size: num: cnum: lnum: [SUMMARY]\\n\") - 1)\n#define NGX_POOL_SUMMARY_FORMAT \"size:%10z%2s num:%12z cnum:%12z lnum:%12z [SUMMARY]\\n\"\n\n    size = NGX_POOL_PID_SIZE + ngx_pool_stats_num * NGX_POOL_ENTRY_SIZE\n           + NGX_POOL_SUMMARY_SIZE;\n    p = ngx_palloc(pool, size);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    b->pos = p;\n\n    p = ngx_sprintf(p, NGX_POOL_PID_FORMAT, ngx_pid);\n\n    /* lines of entry */\n\n    s = n = cn = ln = 0;\n\n    for (i = 0; i < NGX_POOL_STATS_MAX; i++) {\n        for (stat = ngx_pool_stats[i]; stat != NULL; stat = stat->next) {\n            p = ngx_snprintf(p, NGX_POOL_ENTRY_SIZE, NGX_POOL_ENTRY_FORMAT,\n                             stat->size, stat->num, stat->cnum, stat->lnum,\n                             stat->func);\n            s += stat->size;\n            n += stat->num;\n            cn += stat->cnum;\n            ln += stat->lnum;\n        }\n    }\n\n    /* summary line */\n\n    unit = (u_char *) \" B\";\n\n    if (s > 1024 * 1024) {\n        s = s / (1024 * 1024);\n        unit = (u_char *) \"MB\";\n    } else if (s > 1024) {\n        s = s / 1024;\n        unit = (u_char *) \"KB\";\n    }\n\n    p = ngx_snprintf(p, NGX_POOL_SUMMARY_SIZE, NGX_POOL_SUMMARY_FORMAT,\n                     s, unit, n, cn, ln);\n\n    b->last = p;\n    b->memory = 1;\n    b->last_buf = 1;\n\n    return NGX_OK;\n}\n\n\nstatic char *\nngx_http_debug_pool(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_core_loc_conf_t *clcf;\n\n    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);\n    clcf->handler = ngx_http_debug_pool_handler;\n\n    return NGX_CONF_OK;\n}\n"
  },
  {
    "path": "modules/ngx_debug_pool/ngx_palloc.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\nstatic ngx_inline void *ngx_palloc_small(ngx_pool_t *pool, size_t size,\n    ngx_uint_t align);\nstatic void *ngx_palloc_block(ngx_pool_t *pool, size_t size);\nstatic void *ngx_palloc_large(ngx_pool_t *pool, size_t size);\n\n#if (NGX_DEBUG_POOL)\n\n/* used if allocating stat failed */\nstatic ngx_pool_stat_t  ngx_pool_default_stat = {\n    (u_char *) \"default\", 0, 0, 0, 0, NULL\n};\n\nngx_pool_stat_t *ngx_pool_stats[NGX_POOL_STATS_MAX] = { NULL };\nngx_int_t        ngx_pool_stats_num = 0;\n\nstatic ngx_pool_stat_t *\nngx_pstat(u_char *func)\n{\n    uint32_t         index;\n    ngx_pool_stat_t *stat, **pstat;\n\n    /* try to find stat */\n\n    index = ((((uint64_t) func) & 0xffffffff) >> 2) % NGX_POOL_STATS_MAX;\n    pstat = &ngx_pool_stats[index];\n    for (;;) {\n        stat = *pstat;\n        if (stat == NULL) {\n            break;\n        }\n        if (stat->func == func) {\n            return stat;\n        }\n        pstat = &stat->next;\n    }\n\n    /* alloc new stat */\n\n    stat = ngx_calloc(sizeof(ngx_pool_stat_t), ngx_cycle->log);\n    if (stat == NULL) {\n        return &ngx_pool_default_stat;\n    }\n    stat->func = func;\n    *pstat = stat;\n    ngx_pool_stats_num++;\n\n    return stat;\n}\n#endif\n\nngx_pool_t *\n#if (NGX_DEBUG_POOL)\n__ngx_create_pool(size_t size, ngx_log_t *log, u_char *func, ngx_int_t line)\n#else\nngx_create_pool(size_t size, ngx_log_t *log)\n#endif\n{\n    ngx_pool_t  *p;\n\n#if (NGX_DEBUG_POOL)\n    if (size < NGX_MIN_POOL_SIZE) {\n        size = NGX_MIN_POOL_SIZE;\n    }\n#endif\n\n    p = ngx_memalign(NGX_POOL_ALIGNMENT, size, log);\n    if (p == NULL) {\n        return NULL;\n    }\n\n#if (NGX_DEBUG_POOL)\n    p->stat = ngx_pstat(func);\n    p->stat->size += size;\n    p->stat->num += 1;\n    p->stat->cnum += 1;\n    p->size = size;\n#endif\n\n    p->d.last = (u_char *) p + sizeof(ngx_pool_t);\n    p->d.end = (u_char *) p + size;\n    p->d.next = NULL;\n    p->d.failed = 0;\n\n    size = size - sizeof(ngx_pool_t);\n    p->max = (size < NGX_MAX_ALLOC_FROM_POOL) ? size : NGX_MAX_ALLOC_FROM_POOL;\n\n    p->current = p;\n    p->chain = NULL;\n    p->large = NULL;\n    p->cleanup = NULL;\n    p->log = log;\n\n    return p;\n}\n\n\nvoid\nngx_destroy_pool(ngx_pool_t *pool)\n{\n    ngx_pool_t          *p, *n;\n    ngx_pool_large_t    *l;\n    ngx_pool_cleanup_t  *c;\n\n    for (c = pool->cleanup; c; c = c->next) {\n        if (c->handler) {\n            ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0,\n                           \"run cleanup: %p\", c);\n            c->handler(c->data);\n        }\n    }\n\n#if (NGX_DEBUG)\n\n    /*\n     * we could allocate the pool->log from this pool\n     * so we cannot use this log while free()ing the pool\n     */\n\n    for (l = pool->large; l; l = l->next) {\n        ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0, \"free: %p\", l->alloc);\n    }\n\n    for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) {\n        ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, pool->log, 0,\n                       \"free: %p, unused: %uz\", p, p->d.end - p->d.last);\n\n        if (n == NULL) {\n            break;\n        }\n    }\n\n#endif\n\n    for (l = pool->large; l; l = l->next) {\n        if (l->alloc) {\n#if (NGX_DEBUG_POOL)\n            pool->stat->size -= l->size;\n#endif\n            ngx_free(l->alloc);\n        }\n    }\n\n#if (NGX_DEBUG_POOL)\n    pool->stat->size -= pool->size;\n    pool->stat->cnum -= 1;\n#endif\n\n    for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) {\n        ngx_free(p);\n\n        if (n == NULL) {\n            break;\n        }\n    }\n}\n\n\nvoid\nngx_reset_pool(ngx_pool_t *pool)\n{\n    ngx_pool_t        *p;\n    ngx_pool_large_t  *l;\n\n    for (l = pool->large; l; l = l->next) {\n        if (l->alloc) {\n#if (NGX_DEBUG_POOL)\n            pool->stat->size -= l->size;\n#endif\n            ngx_free(l->alloc);\n        }\n    }\n\n    for (p = pool; p; p = p->d.next) {\n        p->d.last = (u_char *) p + sizeof(ngx_pool_t);\n        p->d.failed = 0;\n    }\n\n    pool->current = pool;\n    pool->chain = NULL;\n    pool->large = NULL;\n}\n\n\nvoid *\nngx_palloc(ngx_pool_t *pool, size_t size)\n{\n#if !(NGX_DEBUG_PALLOC)\n    if (size <= pool->max) {\n        return ngx_palloc_small(pool, size, 1);\n    }\n#endif\n\n    return ngx_palloc_large(pool, size);\n}\n\n\nvoid *\nngx_pnalloc(ngx_pool_t *pool, size_t size)\n{\n#if !(NGX_DEBUG_PALLOC)\n    if (size <= pool->max) {\n        return ngx_palloc_small(pool, size, 0);\n    }\n#endif\n\n    return ngx_palloc_large(pool, size);\n}\n\n\nstatic ngx_inline void *\nngx_palloc_small(ngx_pool_t *pool, size_t size, ngx_uint_t align)\n{\n    u_char      *m;\n    ngx_pool_t  *p;\n\n    p = pool->current;\n\n    do {\n        m = p->d.last;\n\n        if (align) {\n            m = ngx_align_ptr(m, NGX_ALIGNMENT);\n        }\n\n        if ((size_t) (p->d.end - m) >= size) {\n            p->d.last = m + size;\n\n            return m;\n        }\n\n        p = p->d.next;\n\n    } while (p);\n\n    return ngx_palloc_block(pool, size);\n}\n\n\nstatic void *\nngx_palloc_block(ngx_pool_t *pool, size_t size)\n{\n    u_char      *m;\n    size_t       psize;\n    ngx_pool_t  *p, *new;\n\n    psize = (size_t) (pool->d.end - (u_char *) pool);\n\n    m = ngx_memalign(NGX_POOL_ALIGNMENT, psize, pool->log);\n    if (m == NULL) {\n        return NULL;\n    }\n\n#if (NGX_DEBUG_POOL)\n    pool->size += psize;\n    pool->stat->size += psize;\n#endif\n\n    new = (ngx_pool_t *) m;\n\n    new->d.end = m + psize;\n    new->d.next = NULL;\n    new->d.failed = 0;\n\n    m += sizeof(ngx_pool_data_t);\n    m = ngx_align_ptr(m, NGX_ALIGNMENT);\n    new->d.last = m + size;\n\n    for (p = pool->current; p->d.next; p = p->d.next) {\n        if (p->d.failed++ > 4) {\n            pool->current = p->d.next;\n        }\n    }\n\n    p->d.next = new;\n\n    return m;\n}\n\n\nstatic void *\nngx_palloc_large(ngx_pool_t *pool, size_t size)\n{\n    void              *p;\n    ngx_uint_t         n;\n    ngx_pool_large_t  *large;\n\n#if (NGX_DEBUG_POOL)\n    pool->stat->lnum += 1;\n#endif\n\n    p = ngx_alloc(size, pool->log);\n    if (p == NULL) {\n        return NULL;\n    }\n\n    n = 0;\n\n    for (large = pool->large; large; large = large->next) {\n        if (large->alloc == NULL) {\n            large->alloc = p;\n#if (NGX_DEBUG_POOL)\n            large->size = size;\n            pool->stat->size += size;\n#endif\n            return p;\n        }\n\n        if (n++ > 3) {\n            break;\n        }\n    }\n\n    large = ngx_palloc_small(pool, sizeof(ngx_pool_large_t), 1);\n    if (large == NULL) {\n        ngx_free(p);\n        return NULL;\n    }\n\n    large->alloc = p;\n    large->next = pool->large;\n    pool->large = large;\n\n#if (NGX_DEBUG_POOL)\n    large->size = size;\n    pool->stat->size += size;\n#endif\n\n    return p;\n}\n\n\nvoid *\nngx_pmemalign(ngx_pool_t *pool, size_t size, size_t alignment)\n{\n    void              *p;\n    ngx_pool_large_t  *large;\n\n    p = ngx_memalign(alignment, size, pool->log);\n    if (p == NULL) {\n        return NULL;\n    }\n\n    large = ngx_palloc_small(pool, sizeof(ngx_pool_large_t), 1);\n    if (large == NULL) {\n        ngx_free(p);\n        return NULL;\n    }\n\n    large->alloc = p;\n    large->next = pool->large;\n    pool->large = large;\n\n#if (NGX_DEBUG_POOL)\n    large->size = size;\n    pool->stat->size += size;\n#endif\n\n    return p;\n}\n\n\nngx_int_t\nngx_pfree(ngx_pool_t *pool, void *p)\n{\n    ngx_pool_large_t  *l;\n\n    for (l = pool->large; l; l = l->next) {\n        if (p == l->alloc) {\n            ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0,\n                           \"free: %p\", l->alloc);\n            ngx_free(l->alloc);\n            l->alloc = NULL;\n\n#if (NGX_DEBUG_POOL)\n            pool->stat->size -= l->size;\n            l->size = 0;\n#endif\n\n            return NGX_OK;\n        }\n    }\n\n    return NGX_DECLINED;\n}\n\n\nvoid *\nngx_pcalloc(ngx_pool_t *pool, size_t size)\n{\n    void *p;\n\n    p = ngx_palloc(pool, size);\n    if (p) {\n        ngx_memzero(p, size);\n    }\n\n    return p;\n}\n\n\n#if (T_DEPRECATED)\nvoid *\nngx_prealloc(ngx_pool_t *pool, void *p, size_t old_size, size_t new_size)\n{\n    void *new;\n    ngx_pool_t *node;\n\n    if (p == NULL) {\n        return ngx_palloc(pool, new_size);\n    }\n\n    if (new_size == 0) {\n        if ((u_char *) p + old_size == pool->d.last) {\n           pool->d.last = p;\n        } else {\n           ngx_pfree(pool, p);\n        }\n\n        return NULL;\n    }\n\n    if (old_size <= pool->max) {\n        for (node = pool; node; node = node->d.next) {\n            if ((u_char *)p + old_size == node->d.last\n                && (u_char *)p + new_size <= node->d.end) {\n                node->d.last = (u_char *)p + new_size;\n                return p;\n            }\n        }\n    }\n\n    if (new_size <= old_size) {\n       return p;\n    }\n\n    new = ngx_palloc(pool, new_size);\n    if (new == NULL) {\n        return NULL;\n    }\n\n    ngx_memcpy(new, p, old_size);\n\n    ngx_pfree(pool, p);\n\n    return new;\n}\n#endif\n\n\nngx_pool_cleanup_t *\nngx_pool_cleanup_add(ngx_pool_t *p, size_t size)\n{\n    ngx_pool_cleanup_t  *c;\n\n    c = ngx_palloc(p, sizeof(ngx_pool_cleanup_t));\n    if (c == NULL) {\n        return NULL;\n    }\n\n    if (size) {\n        c->data = ngx_palloc(p, size);\n        if (c->data == NULL) {\n            return NULL;\n        }\n\n    } else {\n        c->data = NULL;\n    }\n\n    c->handler = NULL;\n    c->next = p->cleanup;\n\n    p->cleanup = c;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, p->log, 0, \"add cleanup: %p\", c);\n\n    return c;\n}\n\n\nvoid\nngx_pool_run_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\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\nvoid\nngx_pool_cleanup_file(void *data)\n{\n    ngx_pool_cleanup_file_t  *c = data;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, c->log, 0, \"file cleanup: fd:%d\",\n                   c->fd);\n\n    if (ngx_close_file(c->fd) == NGX_FILE_ERROR) {\n        ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,\n                      ngx_close_file_n \" \\\"%s\\\" failed\", c->name);\n    }\n}\n\n\nvoid\nngx_pool_delete_file(void *data)\n{\n    ngx_pool_cleanup_file_t  *c = data;\n\n    ngx_err_t  err;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, c->log, 0, \"file cleanup: fd:%d %s\",\n                   c->fd, c->name);\n\n    if (ngx_delete_file(c->name) == NGX_FILE_ERROR) {\n        err = ngx_errno;\n\n        if (err != NGX_ENOENT) {\n            ngx_log_error(NGX_LOG_CRIT, c->log, err,\n                          ngx_delete_file_n \" \\\"%s\\\" failed\", c->name);\n        }\n    }\n\n    if (ngx_close_file(c->fd) == NGX_FILE_ERROR) {\n        ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,\n                      ngx_close_file_n \" \\\"%s\\\" failed\", c->name);\n    }\n}\n\n\n#if 0\n\nstatic void *\nngx_get_cached_block(size_t size)\n{\n    void                     *p;\n    ngx_cached_block_slot_t  *slot;\n\n    if (ngx_cycle->cache == NULL) {\n        return NULL;\n    }\n\n    slot = &ngx_cycle->cache[(size + ngx_pagesize - 1) / ngx_pagesize];\n\n    slot->tries++;\n\n    if (slot->number) {\n        p = slot->block;\n        slot->block = slot->block->next;\n        slot->number--;\n        return p;\n    }\n\n    return NULL;\n}\n\n#endif\n"
  },
  {
    "path": "modules/ngx_debug_pool/ngx_palloc.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_PALLOC_H_INCLUDED_\n#define _NGX_PALLOC_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\n/*\n * NGX_MAX_ALLOC_FROM_POOL should be (ngx_pagesize - 1), i.e. 4095 on x86.\n * On Windows NT it decreases a number of locked pages in a kernel.\n */\n#define NGX_MAX_ALLOC_FROM_POOL  (ngx_pagesize - 1)\n\n#define NGX_DEFAULT_POOL_SIZE    (16 * 1024)\n\n#define NGX_POOL_ALIGNMENT       16\n#define NGX_MIN_POOL_SIZE                                                     \\\n    ngx_align((sizeof(ngx_pool_t) + 2 * sizeof(ngx_pool_large_t)),            \\\n              NGX_POOL_ALIGNMENT)\n\n\ntypedef void (*ngx_pool_cleanup_pt)(void *data);\n\ntypedef struct ngx_pool_cleanup_s  ngx_pool_cleanup_t;\n\nstruct ngx_pool_cleanup_s {\n    ngx_pool_cleanup_pt   handler;\n    void                 *data;\n    ngx_pool_cleanup_t   *next;\n};\n\n\ntypedef struct ngx_pool_large_s  ngx_pool_large_t;\n\nstruct ngx_pool_large_s {\n    ngx_pool_large_t     *next;\n    void                 *alloc;\n#if  (NGX_DEBUG_POOL)\n    size_t                size;\n#endif\n};\n\n\ntypedef struct {\n    u_char               *last;\n    u_char               *end;\n    ngx_pool_t           *next;\n    ngx_uint_t            failed;\n} ngx_pool_data_t;\n\n\n#if (NGX_DEBUG_POOL)\n\n#define NGX_POOL_STATS_MAX      997 /* prime */\n\ntypedef struct ngx_pool_stat_s   ngx_pool_stat_t;\n\nstruct ngx_pool_stat_s {\n    u_char               *func;\n    size_t                size;\n    size_t                num;      /* number of total pools */\n    size_t                cnum;     /* number of current used pools */\n    size_t                lnum;     /* number of calling ngx_palloc_large() */\n    ngx_pool_stat_t      *next;\n};\n\nextern ngx_pool_stat_t *ngx_pool_stats[NGX_POOL_STATS_MAX];\nextern ngx_int_t        ngx_pool_stats_num;\n#endif\n\n\nstruct ngx_pool_s {\n    ngx_pool_data_t       d;\n    size_t                max;\n    ngx_pool_t           *current;\n    ngx_chain_t          *chain;\n    ngx_pool_large_t     *large;\n    ngx_pool_cleanup_t   *cleanup;\n    ngx_log_t            *log;\n#if  (NGX_DEBUG_POOL)\n    size_t                size;\n    ngx_pool_stat_t      *stat;\n#endif\n};\n\n\ntypedef struct {\n    ngx_fd_t              fd;\n    u_char               *name;\n    ngx_log_t            *log;\n} ngx_pool_cleanup_file_t;\n\n#if (T_DEPRECATED)\nvoid *ngx_prealloc(ngx_pool_t *pool, void *p, size_t old_size, size_t new_size);\n#endif\n\nvoid *ngx_alloc(size_t size, ngx_log_t *log);\nvoid *ngx_calloc(size_t size, ngx_log_t *log);\n\n#if  (NGX_DEBUG_POOL)\nngx_pool_t *__ngx_create_pool(size_t size, ngx_log_t *log, u_char *func, ngx_int_t line);\n#define ngx_create_pool(size, log) __ngx_create_pool(size, log, (u_char *) __func__, __LINE__)\n#else\nngx_pool_t *ngx_create_pool(size_t size, ngx_log_t *log);\n#endif\n\nvoid ngx_destroy_pool(ngx_pool_t *pool);\nvoid ngx_reset_pool(ngx_pool_t *pool);\n\nvoid *ngx_palloc(ngx_pool_t *pool, size_t size);\nvoid *ngx_pnalloc(ngx_pool_t *pool, size_t size);\nvoid *ngx_pcalloc(ngx_pool_t *pool, size_t size);\nvoid *ngx_pmemalign(ngx_pool_t *pool, size_t size, size_t alignment);\nngx_int_t ngx_pfree(ngx_pool_t *pool, void *p);\n\n\nngx_pool_cleanup_t *ngx_pool_cleanup_add(ngx_pool_t *p, size_t size);\nvoid ngx_pool_run_cleanup_file(ngx_pool_t *p, ngx_fd_t fd);\nvoid ngx_pool_cleanup_file(void *data);\nvoid ngx_pool_delete_file(void *data);\n\n\n#endif /* _NGX_PALLOC_H_INCLUDED_ */\n"
  },
  {
    "path": "modules/ngx_debug_pool/t/test.t",
    "content": "#!/usr/bin/perl\n\n# Copyright (C) 2015 Alibaba Group Holding Limited\n\nuse warnings;\nuse strict;\n\nuse Test::More;\nuse File::Copy;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->plan(1)\n        ->write_file_expand('nginx.conf', <<'EOF');\n\nmaster_process off;\ndaemon         off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n    server {\n        listen       127.0.0.1:8080;\n\n        location /debug_pool {\n            debug_pool;\n        }\n    }\n}\n\nEOF\n\n###############################################################################\n\n$t->run();\n\nmy $status = http_get(\"/debug_pool\");\n\nlike($status, qr/pid:\\d+\\nsize: *\\d+ num: *\\d+ cnum: *\\d+ lnum: *\\d+ \\w+/,\n     'debug_pool returns information about memory pool usage');\n\nprint \"--- debug for verbose mode ---\\n\",\n      \"$status\",\n      \"------------------------------\\n\";\n\n$t->stop();\n"
  },
  {
    "path": "modules/ngx_debug_timer/README.cn",
    "content": "ngx_debug_timer\n==============\n\n该模块可以提供NGINX/Tengine定时器的状态信息。\n\n示例\n=======\n\n获取NGINX/Tengine定时器的状态信息\n---------------------------------\n\n```\n http {\n    server {\n        listen 80;\n\n        location = /debug_timer {\n            debug_timer;\n        }\n    }\n }\n```\n\n请求URI /debug_timer，可以获取到该NGINX/Tengine定时器的使用情况统计。\n页面输出如下：\n\n```\n$ curl 'localhost:80/debug_timer'\npid:80490\ntimer:2\n--------- [0] --------\ntimers[i]: 00007F837D02C4B8\n    timer: 148\n       ev: 00007F837D02C488\n     data: 00007F837D02C450\n  handler: 000000010778B450\n   action:\n--------- [1] --------\ntimers[i]: 00007F837D02C698\n    timer: 1263\n       ev: 00007F837D02C668\n     data: 00007F837D02C630\n  handler: 000000010778B450\n   action:\n```\n\n定时器的使用情况统计\n-----------------------------------\n\n数据说明\n====\n\n每个数据段落如\"[0]\"包含当前标号对应定时器的状态信息，数据项意义如下：\n\n* __timers__: 当前定时器地址\n* __timer__: 当前定时器的超时时间\n* __ev__: 当前定时器的关联事件\n* __data__: 当前定时器的关联事件字段\n* __handler__: 当前定时器的关联事件handler，配合addr2line来查询对应函数\n* __action__: 当前定时器的log action\n\nNGINX兼容性\n===================\n\n* 1.13.4 (stable version of 1.13.x) 及其更高版本\n\nTengine兼容性\n=====================\n\n* 2.1.1 (stable version of 2.1.x) 及其更高版本\n\n安装说明\n=======\n\n源码安装，执行如下命令：\n\n```\n$ wget http://nginx.org/download/nginx-1.13.4.tar.gz\n$ tar -xzvf nginx-1.13.4.tar.gz\n$ cd nginx-1.13.4/\n$ ./configure --add-module=/path/to/ngx_debug_timer\n$ make -j4 && make install\n```\n\n\n配置指令\n=========\n\n语法: **debug_timer**\n\n默认: `none`\n\n位置: `server, location`\n\nNGINX/Tenigne的定时器状态信息可以通过该location访问得到。\n\n注意信息\n=========\n\n\n"
  },
  {
    "path": "modules/ngx_debug_timer/README.md",
    "content": "ngx_debug_timer\n==============\n\nThis module provides access to information of timer usage for nginx/tengine.\n\nExample\n=======\n\nGet information of timer usage.\n---------------------------------\n\n```\n http {\n    server {\n        listen 80;\n\n        location = /debug_timer {\n            debug_timer;\n        }\n    }\n }\n```\n\nRequesting URI /debug_timer, you will get information of timer usage for nginx/tengine.\nThe output page may look like as follows:\n\n```\n$ curl 'localhost:80/debug_timer'\npid:80490\ntimer:2\n--------- [0] --------\ntimers[i]: 00007F837D02C4B8\n    timer: 148\n       ev: 00007F837D02C488\n     data: 00007F837D02C450\n  handler: 000000010778B450\n   action:\n--------- [1] --------\ntimers[i]: 00007F837D02C698\n    timer: 1263\n       ev: 00007F837D02C668\n     data: 00007F837D02C630\n  handler: 000000010778B450\n   action:\n```\n\nGet information of timer usage\n-----------------------------------\n\nData\n====\n\nEvery block like \"[0]\" except the related timer usage as follows:\n\n* __timers__: address of current timer\n* __timer__: timeout of current timer\n* __ev__: related event of current timer\n* __data__: related event data of current timer\n* __handler__: related event handler of current timer, use addr2line to find the real function\n* __action__: log action of current timer\n\nNginx Compatibility\n===================\n\nThe latest module is compatible with the following versions of nginx:\n\n* 1.13.4 (stable version of 1.13.x) and later\n\nTengine Compatibility\n=====================\n\n* 2.1.1 (stable version of 2.1.x) and later\n\nInstall\n=======\n\nInstall this module from source:\n\n```\n$ wget http://nginx.org/download/nginx-1.13.4.tar.gz\n$ tar -xzvf nginx-1.13.4.tar.gz\n$ cd nginx-1.13.4/\n$ ./configure --add-module=/path/to/ngx_debug_timer\n$ make -j4 && make install\n```\n\nDirective\n=========\n\nSyntax: **debug_timer**\n\nDefault: `none`\n\nContext: `server, location`\n\nThe information of nginx timer usage will be accessible from the surrounding location.\n\nException\n=========\n"
  },
  {
    "path": "modules/ngx_debug_timer/config",
    "content": "ngx_addon_name=ngx_http_debug_timer_module\nHTTP_MODULES=\"$HTTP_MODULES ngx_http_debug_timer_module\"\nNGX_ADDON_SRCS=\"$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_debug_timer_module.c\"\n"
  },
  {
    "path": "modules/ngx_debug_timer/ngx_http_debug_timer_module.c",
    "content": "\n/*\n * Copyright (C) 2018 Alibaba Group Holding Limited\n */\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n#include <execinfo.h>\n#include <ngx_event_timer.h>\n\n\nstatic char *ngx_http_debug_timer(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\nstatic void ngx_http_debug_timer_traversal(ngx_array_t *array, ngx_rbtree_node_t *root);\nstatic ngx_int_t ngx_http_debug_timer_buf(ngx_pool_t *pool, ngx_buf_t *b);\n\nstatic ngx_command_t  ngx_http_debug_timer_commands[] = {\n\n    { ngx_string(\"debug_timer\"),\n      NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,\n      ngx_http_debug_timer,\n      0,\n      0,\n      NULL },\n\n    ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_debug_timer_module_ctx = {\n    NULL,                          /* preconfiguration */\n    NULL,                          /* 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\nngx_module_t  ngx_http_debug_timer_module = {\n    NGX_MODULE_V1,\n    &ngx_http_debug_timer_module_ctx,   /* module context */\n    ngx_http_debug_timer_commands,      /* 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_debug_timer_handler(ngx_http_request_t *r)\n{\n    ngx_int_t    rc;\n    ngx_buf_t   *b;\n    ngx_chain_t  out;\n\n    if (r->method != NGX_HTTP_GET) {\n        return NGX_HTTP_NOT_ALLOWED;\n    }\n\n    rc = ngx_http_discard_request_body(r);\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));\n    if (b == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    if (ngx_http_debug_timer_buf(r->pool, b) == NGX_ERROR) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    r->headers_out.status = NGX_HTTP_OK;\n    r->headers_out.content_length_n = b->last - b->pos;\n\n    rc = ngx_http_send_header(r);\n\n    if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {\n        return rc;\n    }\n\n    out.buf = b;\n    out.next = NULL;\n\n    return ngx_http_output_filter(r, &out);\n}\n\n\nstatic void\nngx_http_debug_timer_traversal(ngx_array_t *array, ngx_rbtree_node_t *root)\n{\n    ngx_rbtree_node_t              **node;\n\n    if (array != NULL && root != NULL\n        && root != ngx_event_timer_rbtree.sentinel)\n    {\n        ngx_http_debug_timer_traversal(array, root->left);\n        node = ngx_array_push(array);\n        if (node == NULL) {\n            return;\n        }\n        *node = (ngx_rbtree_node_t *) root;\n        ngx_http_debug_timer_traversal(array, root->right);\n    }\n}\n\n\nstatic ngx_int_t\nngx_http_debug_timer_buf(ngx_pool_t *pool, ngx_buf_t *b)\n{\n    u_char              *p;\n    size_t               size;\n    ngx_uint_t           i, n;\n    ngx_event_t         *ev;\n    ngx_array_t         *array;\n    ngx_msec_int_t       timer;\n    ngx_rbtree_node_t   *root;\n    ngx_rbtree_node_t  **nodes, *node;\n\n#define NGX_TIMER_TITLE_SIZE     (sizeof(NGX_TIMER_TITLE_FORMAT) - 1 + NGX_TIME_T_LEN + NGX_INT_T_LEN)     /* sizeof pid_t equals time_t */\n#define NGX_TIMER_TITLE_FORMAT   \"pid:%P\\n\"                  \\\n                                 \"timer:%ui\\n\"\n\n#define NGX_TIMER_ENTRY_SIZE     (sizeof(NGX_TIMER_ENTRY_FORMAT) - 1 + \\\n                                  NGX_INT_T_LEN * 2 + NGX_PTR_SIZE * 4 + 256 /* func name */)\n#define NGX_TIMER_ENTRY_FORMAT  \"--------- [%ui] --------\\n\"\\\n                                \"timers[i]: %p\\n\"          \\\n                                \"    timer: %ui\\n\"          \\\n                                \"       ev: %p\\n\"           \\\n                                \"     data: %p\\n\"           \\\n                                \"  handler: %p\\n\"           \\\n                                \"   action: %s\\n\"\n\n    root = ngx_event_timer_rbtree.root;\n\n    array = ngx_array_create(pool, 10, sizeof(ngx_rbtree_node_t **));\n    if (array == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_http_debug_timer_traversal(array, root);\n\n    n = array->nelts;\n\n    size = NGX_TIMER_TITLE_SIZE + n * NGX_TIMER_ENTRY_SIZE;\n    p = ngx_palloc(pool, size);\n    if (p == NULL) {\n        ngx_array_destroy(array);\n        return NGX_ERROR;\n    }\n\n    b->pos = p;\n\n    p = ngx_sprintf(p, NGX_TIMER_TITLE_FORMAT, ngx_pid, n);\n\n    nodes = (ngx_rbtree_node_t **) array->elts;\n\n    for (i = 0; i < n; i++) {\n        node = nodes[i]; /* node: timer */\n        ev = (ngx_event_t *) ((char *) node - (intptr_t)&((ngx_event_t *) 0x0)->timer);\n\n         /* entry format of timer and ev */\n\n        timer = (ngx_msec_int_t) (node->key - ngx_current_msec);\n\n        p = ngx_snprintf(p, NGX_TIMER_ENTRY_SIZE, NGX_TIMER_ENTRY_FORMAT,\n                         i, node, timer, ev, ev->data, ev->handler,\n                         (ev->log->action != NULL) ? ev->log->action : \"\");\n    }\n\n    ngx_array_destroy(array);\n\n    p[-1] = '\\n';  /* make sure last char is newline */\n\n    b->last = p;\n    b->memory = 1;\n    b->last_buf = 1;\n\n    return NGX_OK;\n}\n\n\nstatic char *\nngx_http_debug_timer(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_core_loc_conf_t *clcf;\n\n    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);\n    clcf->handler = ngx_http_debug_timer_handler;\n\n    return NGX_CONF_OK;\n}\n"
  },
  {
    "path": "modules/ngx_debug_timer/t/test.t",
    "content": "#!/usr/bin/perl\n\n# Copyright (C) 2018 Alibaba Group Holding Limited\n\nuse warnings;\nuse strict;\n\nuse Test::More;\nuse File::Copy;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->plan(1)\n        ->write_file_expand('nginx.conf', <<'EOF');\n\nmaster_process off;\ndaemon         off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n    server {\n        listen       127.0.0.1:8080;\n\n        location /debug_timer {\n            debug_timer;\n        }\n    }\n}\n\nEOF\n\n###############################################################################\n\n$t->run();\n\nmy $status = http_get(\"/debug_timer\");\n\nlike($status, qr#200 OK#, 'debug_timer returns information of timers and related events');\n\nprint \"--- debug for verbose mode ---\\n\",\n      \"$status\",\n      \"------------------------------\\n\";\n\n$t->stop();\n"
  },
  {
    "path": "modules/ngx_http_concat_module/config",
    "content": "ngx_addon_name=ngx_http_concat_module\n\n# Build for recent nginx versions\nif test -n \"$ngx_module_link\"; then\n\n    ngx_module_type=HTTP_AUX_FILTER\n    ngx_module_name=ngx_http_concat_module\n    ngx_module_incs=\n    ngx_module_deps=\n    ngx_module_srcs=\"$ngx_addon_dir/ngx_http_concat_module.c\"\n    ngx_module_libs=\n\n    . auto/module\n\n# Build for older nginx versions\nelse\n\n    HTTP_MODULES=\"$HTTP_MODULES ngx_http_concat_module\"\n    HTTP_AUX_FILTER_MODULES=\"$HTTP_AUX_FILTER_MODULES ngx_http_concat_module\"\n    NGX_ADDON_SRCS=\"$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_concat_module.c\"\n\nfi\n"
  },
  {
    "path": "modules/ngx_http_concat_module/ngx_http_concat_module.c",
    "content": "\n/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\ntypedef struct {\n    ngx_flag_t   enable;\n    ngx_uint_t   max_files;\n    ngx_flag_t   unique;\n    ngx_str_t    delimiter;\n    ngx_flag_t   ignore_file_error;\n\n    ngx_hash_t   types;\n    ngx_array_t *types_keys;\n} ngx_http_concat_loc_conf_t;\n\n\nstatic ngx_int_t ngx_http_concat_add_path(ngx_http_request_t *r,\n    ngx_array_t *uris, size_t max, ngx_str_t *path, u_char *p, u_char *v);\nstatic ngx_int_t ngx_http_concat_init(ngx_conf_t *cf);\nstatic void *ngx_http_concat_create_loc_conf(ngx_conf_t *cf);\nstatic char *ngx_http_concat_merge_loc_conf(ngx_conf_t *cf, void *parent,\n    void *child);\n\n\nstatic ngx_str_t  ngx_http_concat_default_types[] = {\n    ngx_string(\"application/javascript\"),\n    ngx_string(\"text/css\"),\n    ngx_null_string\n};\n\n\nstatic ngx_command_t  ngx_http_concat_commands[] = {\n\n    { ngx_string(\"concat\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_concat_loc_conf_t, enable),\n      NULL },\n\n    { ngx_string(\"concat_max_files\"),\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_concat_loc_conf_t, max_files),\n      NULL },\n\n    { ngx_string(\"concat_unique\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_concat_loc_conf_t, unique),\n      NULL },\n\n    { ngx_string(\"concat_types\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,\n      ngx_http_types_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_concat_loc_conf_t, types_keys),\n      &ngx_http_concat_default_types[0] },\n\n    { ngx_string(\"concat_delimiter\"),\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_concat_loc_conf_t, delimiter),\n      NULL },\n\n    { ngx_string(\"concat_ignore_file_error\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_concat_loc_conf_t, ignore_file_error),\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_concat_module_ctx = {\n    NULL,                                /* preconfiguration */\n    ngx_http_concat_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    ngx_http_concat_create_loc_conf,     /* create location configuration */\n    ngx_http_concat_merge_loc_conf       /* merge location configuration */\n};\n\n\nngx_module_t  ngx_http_concat_module = {\n    NGX_MODULE_V1,\n    &ngx_http_concat_module_ctx,         /* module context */\n    ngx_http_concat_commands,            /* 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_concat_handler(ngx_http_request_t *r)\n{\n    off_t                       length;\n    size_t                      root, last_len;\n    time_t                      last_modified;\n    u_char                     *p, *v, *e, *last, *last_type;\n    ngx_int_t                   rc;\n    ngx_str_t                  *uri, *filename, path;\n    ngx_buf_t                  *b;\n    ngx_uint_t                  i, j, level;\n    ngx_flag_t                  timestamp;\n    ngx_array_t                 uris;\n    ngx_chain_t                 out, **last_out, *cl;\n    ngx_open_file_info_t        of;\n    ngx_http_core_loc_conf_t   *ccf;\n    ngx_http_concat_loc_conf_t *clcf;\n\n    if (r->uri.data[r->uri.len - 1] != '/') {\n        return NGX_DECLINED;\n    }\n\n    if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {\n        return NGX_DECLINED;\n    }\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_concat_module);\n\n    if (!clcf->enable) {\n        return NGX_DECLINED;\n    }\n\n    /* the length of args must be greater than or equal to 2 */\n    if (r->args.len < 2 || r->args.data[0] != '?') {\n        return NGX_DECLINED;\n    }\n\n    rc = ngx_http_discard_request_body(r);\n\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    last = ngx_http_map_uri_to_path(r, &path, &root, 0);\n    if (last == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    path.len = last - path.data;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http concat root: \\\"%V\\\"\", &path);\n\n    ccf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n#if (NGX_SUPPRESS_WARN)\n    ngx_memzero(&uris, sizeof(ngx_array_t));\n#endif\n\n    if (ngx_array_init(&uris, r->pool, 8, sizeof(ngx_str_t)) != NGX_OK) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    e = r->args.data + r->args.len;\n    for (p = r->args.data + 1, v = p, timestamp = 0; p != e; p++) {\n\n        if (*p == ',') {\n            if (p == v || timestamp == 1) {\n                v = p + 1;\n                timestamp = 0;\n                continue;\n            }\n\n            rc = ngx_http_concat_add_path(r, &uris, clcf->max_files, &path,\n                                          p, v);\n            if (rc != NGX_OK) {\n                return rc;\n            }\n\n            v = p + 1;\n\n        } else if (*p == '?') {\n            if (timestamp == 1) {\n                v = p;\n                continue;\n            }\n\n            rc = ngx_http_concat_add_path(r, &uris, clcf->max_files, &path,\n                                          p, v);\n            if (rc != NGX_OK) {\n                return rc;\n            }\n\n            v = p;\n            timestamp = 1;\n        }\n    }\n\n    if (p - v > 0 && timestamp == 0) {\n        rc = ngx_http_concat_add_path(r, &uris, clcf->max_files, &path, p, v);\n        if (rc != NGX_OK) {\n            return rc;\n        }\n    }\n\n    last_modified = 0;\n    last_len = 0;\n    last_out = NULL;\n    b = NULL;\n    last_type = NULL;\n    length = 0;\n    uri = uris.elts;\n    for (i = 0; i < uris.nelts; i++) {\n        filename = uri + i;\n\n        for (j = filename->len - 1; j > 1; j--) {\n            if (filename->data[j] == '.' && filename->data[j - 1] != '/') {\n\n                r->exten.len = filename->len - j - 1;\n                r->exten.data = &filename->data[j + 1];\n                break;\n\n            } else if (filename->data[j] == '/') {\n                break;\n            }\n        }\n\n        r->headers_out.content_type.len = 0;\n        if (ngx_http_set_content_type(r) != NGX_OK) {\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n\n        r->headers_out.content_type_lowcase = NULL;\n        if (ngx_http_test_content_type(r, &clcf->types) == NULL) {\n            return NGX_HTTP_BAD_REQUEST;\n        }\n\n        if (clcf->unique) { /* test if all the content types are the same */\n            if ((i > 0)\n                && (last_len != r->headers_out.content_type_len\n                    || (last_type != NULL\n                        && r->headers_out.content_type_lowcase != NULL\n                        && ngx_memcmp(last_type,\n                                      r->headers_out.content_type_lowcase,\n                                      last_len) != 0)))\n            {\n                return NGX_HTTP_BAD_REQUEST;\n            }\n\n            last_len = r->headers_out.content_type_len;\n            last_type = r->headers_out.content_type_lowcase;\n        }\n\n        ngx_memzero(&of, sizeof(ngx_open_file_info_t));\n\n        of.read_ahead = ccf->read_ahead;\n        of.directio = ccf->directio;\n        of.valid = ccf->open_file_cache_valid;\n        of.min_uses = ccf->open_file_cache_min_uses;\n        of.errors = ccf->open_file_cache_errors;\n        of.events = ccf->open_file_cache_events;\n\n        if (ngx_open_cached_file(ccf->open_file_cache, filename, &of, r->pool)\n            != NGX_OK)\n        {\n            switch (of.err) {\n\n            case 0:\n                return NGX_HTTP_INTERNAL_SERVER_ERROR;\n\n            case NGX_ENOENT:\n            case NGX_ENOTDIR:\n            case NGX_ENAMETOOLONG:\n\n                level = NGX_LOG_ERR;\n                rc = NGX_HTTP_NOT_FOUND;\n                break;\n\n            case NGX_EACCES:\n\n                level = NGX_LOG_ERR;\n                rc = NGX_HTTP_FORBIDDEN;\n                break;\n\n            default:\n\n                level = NGX_LOG_CRIT;\n                rc = NGX_HTTP_INTERNAL_SERVER_ERROR;\n                break;\n            }\n\n            if (rc != NGX_HTTP_NOT_FOUND || ccf->log_not_found) {\n                ngx_log_error(level, r->connection->log, of.err,\n                              \"%s \\\"%V\\\" failed\", of.failed, filename);\n            }\n\n            if (clcf->ignore_file_error\n                && (rc == NGX_HTTP_NOT_FOUND || rc == NGX_HTTP_FORBIDDEN))\n            {\n                continue;\n            }\n\n            return rc;\n        }\n\n        if (!of.is_file) {\n            ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,\n                          \"\\\"%V\\\" is not a regular file\", filename);\n            if (clcf->ignore_file_error) {\n                continue;\n            }\n\n            return NGX_HTTP_NOT_FOUND;\n        }\n\n        if (of.size == 0) {\n            continue;\n        }\n\n        length += of.size;\n        if (last_out == NULL) {\n            last_modified = of.mtime;\n\n        } else {\n            if (of.mtime > last_modified) {\n                last_modified = of.mtime;\n            }\n        }\n\n        b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));\n        if (b == NULL) {\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n\n        b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t));\n        if (b->file == NULL) {\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n\n        b->file_pos = 0;\n        b->file_last = of.size;\n\n        b->in_file = b->file_last ? 1 : 0;\n\n        b->file->fd = of.fd;\n        b->file->name = *filename;\n        b->file->log = r->connection->log;\n\n        b->file->directio = of.is_directio;\n\n        if (last_out == NULL) {\n            out.buf = b;\n            last_out = &out.next;\n            out.next = NULL;\n\n        } else {\n            cl = ngx_alloc_chain_link(r->pool);\n            if (cl == NULL) {\n                return NGX_HTTP_INTERNAL_SERVER_ERROR;\n            }\n\n            cl->buf = b;\n\n            *last_out = cl;\n            last_out = &cl->next;\n            cl->next = NULL;\n        }\n\n        if (i + 1 == uris.nelts || clcf->delimiter.len == 0) {\n            continue;\n        }\n\n        b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));\n        if (b == NULL) {\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n\n        b->pos = clcf->delimiter.data;\n        b->last = b->pos + clcf->delimiter.len;\n        b->memory = 1;\n        length += clcf->delimiter.len;\n\n        cl = ngx_alloc_chain_link(r->pool);\n        if (cl == NULL) {\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n\n        cl->buf = b;\n        *last_out = cl;\n        last_out = &cl->next;\n        cl->next = NULL;\n    }\n\n    r->headers_out.status = NGX_HTTP_OK;\n    r->headers_out.content_length_n = length;\n    r->headers_out.last_modified_time = last_modified;\n\n    if (b == NULL) {\n        r->header_only = 1;\n    }\n\n    rc = ngx_http_send_header(r);\n    if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {\n        return rc;\n    }\n\n    if (b != NULL) {\n        b->last_in_chain = 1;\n        b->last_buf = 1;\n    }\n\n    return ngx_http_output_filter(r, &out);\n}\n\n\nstatic ngx_int_t\nngx_http_concat_add_path(ngx_http_request_t *r, ngx_array_t *uris,\n    size_t max, ngx_str_t *path, u_char *p, u_char *v)\n{\n    u_char     *d;\n    ngx_str_t  *uri, args;\n    ngx_uint_t  flags;\n\n    if (p == v) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"client sent zero concat filename\");\n        return NGX_HTTP_BAD_REQUEST;\n    }\n\n    if (uris->nelts >= max) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"client sent too many concat filenames\");\n        return NGX_HTTP_BAD_REQUEST;\n    }\n\n    uri = ngx_array_push(uris);\n    if (uri == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    uri->len = path->len + p - v;\n    uri->data = ngx_pnalloc(r->pool, uri->len + 1);  /* + '\\0' */\n    if (uri->data == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    d = ngx_cpymem(uri->data, path->data, path->len);\n    d = ngx_cpymem(d, v, p - v);\n    *d = '\\0';\n\n    args.len = 0;\n    args.data = NULL;\n    flags = NGX_HTTP_LOG_UNSAFE;\n\n    if (ngx_http_parse_unsafe_uri(r, uri, &args, &flags) != NGX_OK) {\n        return NGX_HTTP_BAD_REQUEST;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http concat add file: \\\"%s\\\"\", uri->data);\n\n    return NGX_OK;\n}\n\n\nstatic void *\nngx_http_concat_create_loc_conf(ngx_conf_t *cf)\n{\n    ngx_http_concat_loc_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_concat_loc_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc():\n     *\n     *     conf->types = { NULL };\n     *     conf->types_keys = NULL;\n     */\n\n    conf->enable = NGX_CONF_UNSET;\n    conf->ignore_file_error = NGX_CONF_UNSET;\n    conf->max_files = NGX_CONF_UNSET_UINT;\n    conf->unique = NGX_CONF_UNSET;\n\n    return conf;\n}\n\n\nstatic char *\nngx_http_concat_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_http_concat_loc_conf_t *prev = parent;\n    ngx_http_concat_loc_conf_t *conf = child;\n\n    ngx_conf_merge_value(conf->enable, prev->enable, 0);\n    ngx_conf_merge_str_value(conf->delimiter, prev->delimiter, \"\");\n    ngx_conf_merge_value(conf->ignore_file_error, prev->ignore_file_error, 0);\n    ngx_conf_merge_uint_value(conf->max_files, prev->max_files, 10);\n    ngx_conf_merge_value(conf->unique, prev->unique, 1);\n\n    if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,\n                             &prev->types_keys, &prev->types,\n                             ngx_http_concat_default_types)\n        != NGX_OK)\n    {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_concat_init(ngx_conf_t *cf)\n{\n    ngx_http_handler_pt       *h;\n    ngx_http_core_main_conf_t *cmcf;\n\n    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);\n\n    h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);\n    if (h == NULL) {\n        return NGX_ERROR;\n    }\n\n    *h = ngx_http_concat_handler;\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "modules/ngx_http_footer_filter_module/config",
    "content": "ngx_addon_name=ngx_http_footer_filter_module\nHTTP_FOOTER_FILTER_SRCS=\"$ngx_addon_dir/ngx_http_footer_filter_module.c\"\n\nif test -n \"$ngx_module_link\"; then\n    ngx_module_type=HTTP_AUX_FILTER\n    ngx_module_name=ngx_http_footer_filter_module\n    ngx_module_incs=\n    ngx_module_deps=\n    ngx_module_srcs=$HTTP_FOOTER_FILTER_SRCS\n    ngx_module_libs=\n\n    . auto/module\nelse\n    HTTP_AUX_FILTER_MODULES=\"$HTTP_AUX_FILTER_MODULES ngx_http_footer_filter_module\"\n    NGX_ADDON_SRCS=\"$NGX_ADDON_SRCS $HTTP_FOOTER_FILTER_SRCS\"\nfi\n\n"
  },
  {
    "path": "modules/ngx_http_footer_filter_module/ngx_http_footer_filter_module.c",
    "content": "\n/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\ntypedef struct {\n    ngx_hash_t                          types;\n    ngx_array_t                        *types_keys;\n    ngx_http_complex_value_t           *variable;\n} ngx_http_footer_loc_conf_t;\n\n\ntypedef struct {\n    ngx_str_t                           footer;\n} ngx_http_footer_ctx_t;\n\n\nstatic char *ngx_http_footer_filter(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic void *ngx_http_footer_create_loc_conf(ngx_conf_t *cf);\nstatic char *ngx_http_footer_merge_loc_conf(ngx_conf_t *cf,\n    void *parent, void *child);\nstatic ngx_int_t ngx_http_footer_filter_init(ngx_conf_t *cf);\n\n\nstatic ngx_command_t  ngx_http_footer_filter_commands[] = {\n\n    { ngx_string(\"footer\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_http_footer_filter,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"footer_types\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,\n      ngx_http_types_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_footer_loc_conf_t, types_keys),\n      &ngx_http_html_default_types[0] },\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_footer_filter_module_ctx = {\n    NULL,                               /* proconfiguration */\n    ngx_http_footer_filter_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    ngx_http_footer_create_loc_conf,    /* create location configuration */\n    ngx_http_footer_merge_loc_conf      /* merge location configuration */\n};\n\n\nngx_module_t  ngx_http_footer_filter_module = {\n    NGX_MODULE_V1,\n    &ngx_http_footer_filter_module_ctx, /* module context */\n    ngx_http_footer_filter_commands,    /* 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_http_output_header_filter_pt ngx_http_next_header_filter;\nstatic ngx_http_output_body_filter_pt   ngx_http_next_body_filter;\n\n\nstatic ngx_int_t\nngx_http_footer_header_filter(ngx_http_request_t *r)\n{\n    ngx_http_footer_ctx_t       *ctx;\n    ngx_http_footer_loc_conf_t  *lcf;\n\n    lcf = ngx_http_get_module_loc_conf(r, ngx_http_footer_filter_module);\n\n    if (lcf->variable == (ngx_http_complex_value_t *) -1\n        || r->header_only\n        || (r->method & NGX_HTTP_HEAD)\n        || r != r->main\n        || r->headers_out.status == NGX_HTTP_NO_CONTENT\n        || (r->headers_out.content_encoding\n            && r->headers_out.content_encoding->value.len)\n        || ngx_http_test_content_type(r, &lcf->types) == NULL)\n    {\n        return ngx_http_next_header_filter(r);\n    }\n\n    ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_footer_ctx_t));\n    if (ctx == NULL) {\n       return NGX_ERROR;\n    }\n\n    if (ngx_http_complex_value(r, lcf->variable, &ctx->footer) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    ngx_http_set_ctx(r, ctx, ngx_http_footer_filter_module);\n\n    if (r->headers_out.content_length_n != -1) {\n        r->headers_out.content_length_n += ctx->footer.len;\n    }\n\n    if (r->headers_out.content_length) {\n        r->headers_out.content_length->hash = 0;\n        r->headers_out.content_length = NULL;\n    }\n\n    ngx_http_clear_accept_ranges(r);\n\n    return ngx_http_next_header_filter(r);\n}\n\n\nstatic ngx_int_t\nngx_http_footer_body_filter(ngx_http_request_t *r, ngx_chain_t *in)\n{\n    ngx_buf_t             *buf;\n    ngx_uint_t             last;\n    ngx_chain_t           *cl, *nl;\n    ngx_http_footer_ctx_t *ctx;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http footer body filter\");\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_footer_filter_module);\n    if (ctx == NULL) {\n        return ngx_http_next_body_filter(r, in);\n    }\n\n    last = 0;\n\n    for (cl = in; cl; cl = cl->next) {\n         if (cl->buf->last_buf) {\n             last = 1;\n             break;\n         }\n    }\n\n    if (!last) {\n        return ngx_http_next_body_filter(r, in);\n    }\n\n    buf = ngx_calloc_buf(r->pool);\n    if (buf == NULL) {\n        return NGX_ERROR;\n    }\n\n    buf->pos = ctx->footer.data;\n    buf->last = buf->pos + ctx->footer.len;\n    buf->start = buf->pos;\n    buf->end = buf->last;\n    buf->last_buf = 1;\n    buf->memory = 1;\n\n    if (ngx_buf_size(cl->buf) == 0) {\n        cl->buf = buf;\n    } else {\n        nl = ngx_alloc_chain_link(r->pool);\n        if (nl == NULL) {\n            return NGX_ERROR;\n        }\n\n        nl->buf = buf;\n        nl->next = NULL;\n        cl->next = nl;\n        cl->buf->last_buf = 0;\n    }\n\n    return ngx_http_next_body_filter(r, in);\n}\n\n\nstatic char *\nngx_http_footer_filter(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_footer_loc_conf_t *flcf = conf;\n\n    ngx_str_t                    *value;\n    ngx_http_complex_value_t    **cv;\n\n    cv = &flcf->variable;\n\n    if (*cv != NULL) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    if (value[1].len) {\n        cmd->offset = offsetof(ngx_http_footer_loc_conf_t, variable);\n        return ngx_http_set_complex_value_slot(cf, cmd, conf);\n    }\n\n    *cv = (ngx_http_complex_value_t *) -1;\n\n    return NGX_OK;\n}\n\n\nstatic void *\nngx_http_footer_create_loc_conf(ngx_conf_t *cf)\n{\n    ngx_http_footer_loc_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_footer_loc_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc():\n     *\n     *     conf->types = { NULL };\n     *     conf->types_keys = NULL;\n     *     conf->variable = NULL;\n     */\n\n    return conf;\n}\n\n\nstatic char *\nngx_http_footer_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_http_footer_loc_conf_t  *prev = parent;\n    ngx_http_footer_loc_conf_t  *conf = child;\n\n    if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,\n                             &prev->types_keys,&prev->types,\n                             ngx_http_html_default_types)\n        != NGX_OK)\n    {\n       return NGX_CONF_ERROR;\n    }\n\n    if (conf->variable == NULL) {\n        conf->variable = prev->variable;\n    }\n\n    if (conf->variable == NULL) {\n        conf->variable = (ngx_http_complex_value_t *) -1;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_footer_filter_init(ngx_conf_t *cf)\n{\n    ngx_http_next_body_filter = ngx_http_top_body_filter;\n    ngx_http_top_body_filter = ngx_http_footer_body_filter;\n\n    ngx_http_next_header_filter = ngx_http_top_header_filter;\n    ngx_http_top_header_filter = ngx_http_footer_header_filter;\n\n    return NGX_OK;\n}\n\n"
  },
  {
    "path": "modules/ngx_http_lua_module/.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": "modules/ngx_http_lua_module/.travis.yml",
    "content": "dist: bionic\n\nbranches:\n  only:\n    - \"master\"\n\nos: linux\n\nlanguage: c\n\ncompiler:\n  - gcc\n\naddons:\n  apt:\n    packages:\n      - ack\n      - axel\n      - cpanminus\n      - libtest-base-perl\n      - libtext-diff-perl\n      - liburi-perl\n      - libwww-perl\n      - libtest-longstring-perl\n      - liblist-moreutils-perl\n      - libgd-dev\n\ncache:\n  directories:\n    - download-cache\n\nenv:\n  global:\n    - JOBS=3\n    - NGX_BUILD_JOBS=$JOBS\n    - LUAJIT_PREFIX=/opt/luajit21\n    - LUAJIT_LIB=$LUAJIT_PREFIX/lib\n    - LUAJIT_INC=$LUAJIT_PREFIX/include/luajit-2.1\n    - LUA_INCLUDE_DIR=$LUAJIT_INC\n    - PCRE_VER=8.45\n    - PCRE_PREFIX=/opt/pcre\n    - PCRE_LIB=$PCRE_PREFIX/lib\n    - PCRE_INC=$PCRE_PREFIX/include\n    - OPENSSL_PREFIX=/opt/ssl\n    - OPENSSL_LIB=$OPENSSL_PREFIX/lib\n    - OPENSSL_INC=$OPENSSL_PREFIX/include\n    - LIBDRIZZLE_PREFIX=/opt/drizzle\n    - LIBDRIZZLE_INC=$LIBDRIZZLE_PREFIX/include/libdrizzle-1.0\n    - LIBDRIZZLE_LIB=$LIBDRIZZLE_PREFIX/lib\n    - LD_LIBRARY_PATH=$LUAJIT_LIB:$LD_LIBRARY_PATH\n    - DRIZZLE_VER=2011.07.21\n    - TEST_NGINX_SLEEP=0.006\n  jobs:\n    - NGINX_VERSION=1.21.4 OPENSSL_VER=1.1.0l OPENSSL_PATCH_VER=1.1.0d\n    - NGINX_VERSION=1.21.4 OPENSSL_VER=1.1.1s OPENSSL_PATCH_VER=1.1.1f\n\nservices:\n  - memcached\n  - redis\n  - mysql\n\nbefore_install:\n  - sudo apt update\n  - sudo apt install --only-upgrade ca-certificates\n  - '! grep -n -P ''(?<=.{80}).+'' --color `find src -name ''*.c''` `find . -name ''*.h''` || (echo \"ERROR: Found C source lines exceeding 80 columns.\" > /dev/stderr; exit 1)'\n  - '! grep -n -P ''\\t+'' --color `find src -name ''*.c''` `find . -name ''*.h''` || (echo \"ERROR: Cannot use tabs.\" > /dev/stderr; exit 1)'\n  - /usr/bin/env perl $(command -v cpanm) --sudo --notest Test::Nginx IPC::Run > build.log 2>&1 || (cat build.log && exit 1)\n  - pyenv global 2.7\ninstall:\n  - if [ ! -f download-cache/drizzle7-$DRIZZLE_VER.tar.gz ]; then wget -P download-cache http://openresty.org/download/drizzle7-$DRIZZLE_VER.tar.gz; fi\n  - if [ ! -f download-cache/pcre-$PCRE_VER.tar.gz ]; then wget -P download-cache https://downloads.sourceforge.net/project/pcre/pcre/${PCRE_VER}/pcre-${PCRE_VER}.tar.gz; fi\n  - if [ ! -f download-cache/openssl-$OPENSSL_VER.tar.gz ]; then wget -P download-cache https://www.openssl.org/source/openssl-$OPENSSL_VER.tar.gz || wget -P download-cache https://www.openssl.org/source/old/${OPENSSL_VER//[a-z]/}/openssl-$OPENSSL_VER.tar.gz; fi\n  - git clone https://github.com/openresty/test-nginx.git\n  - git clone https://github.com/openresty/openresty.git ../openresty\n  - git clone https://github.com/openresty/no-pool-nginx.git ../no-pool-nginx\n  - git clone https://github.com/openresty/openresty-devel-utils.git\n  - git clone https://github.com/openresty/mockeagain.git\n  - git clone https://github.com/openresty/lua-cjson.git lua-cjson\n  - git clone https://github.com/openresty/lua-upstream-nginx-module.git ../lua-upstream-nginx-module\n  - git clone https://github.com/openresty/echo-nginx-module.git ../echo-nginx-module\n  - git clone https://github.com/openresty/nginx-eval-module.git ../nginx-eval-module\n  - git clone https://github.com/simpl/ngx_devel_kit.git ../ndk-nginx-module\n  - git clone https://github.com/FRiCKLE/ngx_coolkit.git ../coolkit-nginx-module\n  - git clone https://github.com/openresty/headers-more-nginx-module.git ../headers-more-nginx-module\n  - git clone https://github.com/openresty/drizzle-nginx-module.git ../drizzle-nginx-module\n  - git clone https://github.com/openresty/set-misc-nginx-module.git ../set-misc-nginx-module\n  - git clone https://github.com/openresty/memc-nginx-module.git ../memc-nginx-module\n  - git clone https://github.com/openresty/rds-json-nginx-module.git ../rds-json-nginx-module\n  - git clone https://github.com/openresty/srcache-nginx-module.git ../srcache-nginx-module\n  - git clone https://github.com/openresty/redis2-nginx-module.git ../redis2-nginx-module\n  - git clone https://github.com/openresty/lua-resty-core.git ../lua-resty-core\n  - git clone https://github.com/openresty/lua-resty-lrucache.git ../lua-resty-lrucache\n  - git clone https://github.com/openresty/lua-resty-mysql.git ../lua-resty-mysql\n  - git clone https://github.com/openresty/lua-resty-string.git ../lua-resty-string\n  - git clone https://github.com/openresty/stream-lua-nginx-module.git ../stream-lua-nginx-module\n  - git clone -b v2.1-agentzh https://github.com/openresty/luajit2.git luajit2\n\nbefore_script:\n  - mysql -uroot -e 'create database ngx_test; grant all on ngx_test.* to \"ngx_test\"@\"%\" identified by \"ngx_test\"; flush privileges;'\n\nscript:\n  - export PATH=$PWD/work/nginx/sbin:$PWD/openresty-devel-utils:$PATH\n  - ngx-releng > check.txt || true\n  - lines=`wc -l check.txt | awk '{print $1}'`; if [ $lines -gt 5 ]; then cat check.txt; exit 1; fi\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  - cd luajit2/\n  - make -j$JOBS CCDEBUG=-g Q= PREFIX=$LUAJIT_PREFIX CC=$CC XCFLAGS='-DLUA_USE_APICHECK -DLUA_USE_ASSERT -msse4.2' > build.log 2>&1 || (cat build.log && exit 1)\n  - sudo make install PREFIX=$LUAJIT_PREFIX > build.log 2>&1 || (cat build.log && exit 1)\n  - cd ..\n  - tar xzf download-cache/drizzle7-$DRIZZLE_VER.tar.gz && cd drizzle7-$DRIZZLE_VER\n  - ./configure --prefix=$LIBDRIZZLE_PREFIX --without-server > 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  - cd ../mockeagain/ && make CC=$CC -j$JOBS && cd ..\n  - cd lua-cjson/ && make -j$JOBS && sudo make install && cd ..\n  - tar zxf download-cache/pcre-$PCRE_VER.tar.gz\n  - cd pcre-$PCRE_VER/\n  - ./configure --prefix=$PCRE_PREFIX --enable-jit --enable-utf --enable-unicode-properties > build.log 2>&1 || (cat build.log && exit 1)\n  - make -j$JOBS > build.log 2>&1 || (cat build.log && exit 1)\n  - sudo PATH=$PATH make install > build.log 2>&1 || (cat build.log && exit 1)\n  - cd ..\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\n  - ./config shared enable-ssl3 enable-ssl3-method -g --prefix=$OPENSSL_PREFIX -DPURIFY > build.log 2>&1 || (cat build.log && exit 1)\n  - make -j$JOBS > build.log 2>&1 || (cat build.log && exit 1)\n  - sudo make PATH=$PATH install_sw > build.log 2>&1 || (cat build.log && exit 1)\n  - cd ..\n  - export NGX_BUILD_CC=$CC\n  - sh util/build-without-ssl.sh $NGINX_VERSION > build.log 2>&1 || (cat build.log && exit 1)\n  - sh util/build-with-dd.sh $NGINX_VERSION > build.log 2>&1 || (cat build.log && exit 1)\n  - rm -fr buildroot\n  - sh util/build.sh $NGINX_VERSION > build.log 2>&1 || (cat build.log && exit 1)\n  - nginx -V\n  - python3 ./util/nc_server.py &\n  - ldd `which nginx`|grep -E 'luajit|ssl|pcre'\n  - export LD_PRELOAD=$PWD/mockeagain/mockeagain.so\n  - export LD_LIBRARY_PATH=$PWD/mockeagain:$LD_LIBRARY_PATH\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  - /usr/bin/env perl $(command -v prove) -I. -Itest-nginx/lib -r t/\n"
  },
  {
    "path": "modules/ngx_http_lua_module/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\nThis is a core component of OpenResty. If you are using this module, then you are essentially using OpenResty :)\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* [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.19](https://github.com/openresty/lua-nginx-module/tags), which was released\non 3 Nov, 2020.\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 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[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, 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[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.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 result in a core dump file) immediately. This option is useful for debugging VM panics. This option was first introduced in the `v0.9.8` release.\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: implement the `bind()` method for stream-typed cosockets.\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* review and apply vadim-pavlov's patch for [ngx.location.capture](#ngxlocationcapture)'s `extra_headers` option\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\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](http://www.kyne.com.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\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-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[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* [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* [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* [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_trusted_certificate](#lua_ssl_trusted_certificate)\n* [lua_ssl_verify_depth](#lua_ssl_verify_depth)\n* [lua_ssl_conf_command](#lua_ssl_conf_command)\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* [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](https://cloud.githubusercontent.com/assets/2137369/15272097/77d1c09e-1a37-11e6-97ef-d9767035fc3e.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 verson `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\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[access_by_lua*](#access_by_lua)) 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\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\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\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\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\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\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 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](#tcpsocksslhandshake) method.\n\nThe support for the `TLSv1.3` parameter requires version `v0.10.12` *and* OpenSSL 1.1.1.\n\nThis directive was first introduced in the `v0.9.11` release.\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:** *no*\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_trusted_certificate](#lua_ssl_trusted_certificate).\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_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\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 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[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.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: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: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* [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` and etc) will destroy the original request `ngx.ctx` data (if any) and the new request will have an empty `ngx.ctx` table. For instance,\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* `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\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 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()*\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\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\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;*\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\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;*\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\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.\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. Returns `nil` if `str` is not well formed.\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 formated string can be used as the http header time (for example, being used in `Last-Modified` header). The parameter `sec` is the time stamp in seconds (like those returned from [ngx.time](#ngxtime)).\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* [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: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\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)*\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.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 the connection pool is full, subsequent\n\tconnect operations will be queued into a queue equal to this option's\n\tvalue (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 connections\n\tin the pool is 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\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)\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)\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: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 ore more Nginx subrequests. You must call [ngx.thread.wait](#ngxthreadwait) to wait for those \"light thread\" to terminate before quitting the \"world\". A notable exception here is that you can abort pending subrequests by calling [ngx.exit](#ngxexit) with and only with the status code `ngx.ERROR` (-1), `408`, `444`, or `499`.\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\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, and etc) into the timer callback, either explicitly as user arguments or implicitly as upvalues for the callback closure. There are several exceptions, however: you *cannot* pass any thread objects returned by [coroutine.create](#coroutinecreate) and [ngx.thread.spawn](#ngxthreadspawn) or any cosocket objects returned by [ngx.socket.tcp](#ngxsockettcp), [ngx.socket.udp](#ngxsocketudp), and [ngx.req.socket](#ngxreqsocket) because these objects' lifetime is bound to the request context creating them while the timer callback is detached from the creating request's context (by design) and runs in its own (fake) request context. If you try to share the thread or cosocket objects across the boundary of the creating request, then you will get the \"no co ctx found\" error (for threads) or \"bad request\" (for cosockets). It is fine, however, to create all these objects inside your timer callback.\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.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* `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 `arg`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 `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": "modules/ngx_http_lua_module/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_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_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_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_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_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_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; 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\nhave=T_NGX_HTTP_HAVE_LUA_MODULE . auto/have\n"
  },
  {
    "path": "modules/ngx_http_lua_module/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.19], which was released\non 3 Nov, 2020.\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== 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== 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_trusted_certificate ==\n\n'''syntax:''' ''lua_ssl_trusted_certificate <file>''\n\n'''default:''' ''no''\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_trusted_certificate|lua_ssl_trusted_certificate]].\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\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== 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()''\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\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*''\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\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*''\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\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\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. Returns <code>nil</code> if <code>str</code> is not well formed.\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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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  10025\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\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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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    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                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    /*  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    /*  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#if (T_NGX_XQUIC)\n        if (!r->xqstream) {\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 (T_NGX_XQUIC)\n        }\n#endif\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) {\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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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->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\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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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\nstruct ngx_http_lua_balancer_peer_data_s {\n    /* the round robin data must be first */\n    ngx_http_upstream_rr_peer_data_t    rrp;\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\n    ngx_str_t                          *host;\n    in_port_t                           port;\n\n    int                                 last_peer_state;\n\n#if !(HAVE_NGX_UPSTREAM_TIMEOUT_FIELDS)\n    unsigned                            cloned_upstream_conf;  /* :1 */\n#endif\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);\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);\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\n    ngx_http_upstream_srv_conf_t      *uscf;\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    uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);\n\n    if (uscf->peer.init_upstream) {\n        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                           \"load balancing method redefined\");\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,\n    ngx_http_upstream_srv_conf_t *us)\n{\n    if (ngx_http_upstream_init_round_robin(cf, us) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    /* this callback is called upon individual requests */\n    us->peer.init = ngx_http_lua_balancer_init_peer;\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            *bcf;\n    ngx_http_lua_balancer_peer_data_t  *bp;\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    r->upstream->peer.data = &bp->rrp;\n\n    if (ngx_http_upstream_init_round_robin_peer(r, us) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    r->upstream->peer.get = ngx_http_lua_balancer_get_peer;\n    r->upstream->peer.free = ngx_http_lua_balancer_free_peer;\n\n#if (NGX_HTTP_SSL)\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    bcf = ngx_http_conf_upstream_srv_conf(us, ngx_http_lua_module);\n\n    bp->conf = bcf;\n    bp->request = r;\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    lua_State                          *L;\n    ngx_int_t                           rc;\n    ngx_http_request_t                 *r;\n    ngx_http_lua_ctx_t                 *ctx;\n    ngx_http_lua_srv_conf_t            *lscf;\n    ngx_http_lua_main_conf_t           *lmcf;\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 peer, tries: %ui\", pc->tries);\n\n    lscf = bp->conf;\n\n    r = bp->request;\n\n    ngx_http_lua_assert(lscf->balancer.handler && r);\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_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->total_tries++;\n\n    lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);\n\n    /* balancer_by_lua does not support yielding and\n     * there cannot be any conflicts among concurrent requests,\n     * thus it is safe to store the peer data in the main conf.\n     */\n    lmcf->balancer_peer_data = bp;\n\n    rc = lscf->balancer.handler(r, lscf, L);\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->sockaddr && bp->socklen) {\n        pc->sockaddr = bp->sockaddr;\n        pc->socklen = bp->socklen;\n        pc->cached = 0;\n        pc->connection = NULL;\n        pc->name = bp->host;\n\n        bp->rrp.peers->single = 0;\n\n        if (bp->more_tries) {\n            r->upstream->peer.tries += bp->more_tries;\n        }\n\n        dd(\"tries: %d\", (int) r->upstream->peer.tries);\n\n        return NGX_OK;\n    }\n\n    return ngx_http_upstream_get_round_robin_peer(pc, &bp->rrp);\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_http_lua_balancer_peer_data_t  *bp = data;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                   \"lua balancer free peer, tries: %ui\", pc->tries);\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        return;\n    }\n\n    /* fallback */\n\n    ngx_http_upstream_free_round_robin_peer(pc, data, state);\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 ngx_http_upstream_set_round_robin_peer_session(pc, &bp->rrp);\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    ngx_http_upstream_save_round_robin_peer_session(pc, &bp->rrp);\n    return;\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, 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_main_conf_t           *lmcf;\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    lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);\n\n    /* we cannot read r->upstream->peer.data here directly because\n     * it could be overridden by other modules like\n     * ngx_http_upstream_keepalive_module.\n     */\n    bp = lmcf->balancer_peer_data;\n    if (bp == NULL) {\n        *err = \"no upstream peer data found\";\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    if (url.addrs && url.addrs[0].sockaddr) {\n        bp->sockaddr = url.addrs[0].sockaddr;\n        bp->socklen = url.addrs[0].socklen;\n        bp->host = &url.addrs[0].name;\n\n    } else {\n        *err = \"no host allowed\";\n        return NGX_ERROR;\n    }\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#endif\n    ngx_http_lua_main_conf_t           *lmcf;\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    lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);\n\n    bp = lmcf->balancer_peer_data;\n    if (bp == NULL) {\n        *err = \"no upstream peer data found\";\n        return NGX_ERROR;\n    }\n\n#if !(HAVE_NGX_UPSTREAM_TIMEOUT_FIELDS)\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\n    ngx_http_lua_main_conf_t           *lmcf;\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    lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);\n\n    bp = lmcf->balancer_peer_data;\n    if (bp == NULL) {\n        *err = \"no upstream peer data found\";\n        return NGX_ERROR;\n    }\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\n    ngx_http_lua_balancer_peer_data_t  *bp;\n    ngx_http_lua_main_conf_t           *lmcf;\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    lmcf = ngx_http_get_module_main_conf(r, ngx_http_lua_module);\n\n    bp = lmcf->balancer_peer_data;\n    if (bp == NULL) {\n        *err = \"no upstream peer data found\";\n        return NGX_ERROR;\n    }\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 = u->request_bufs->next;\n    }\n\n    return u->create_request(r);\n}\n\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "modules/ngx_http_lua_module/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\n\n#endif /* _NGX_HTTP_LUA_BALANCER_H_INCLUDED_ */\n"
  },
  {
    "path": "modules/ngx_http_lua_module/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    /*  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    /*  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        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 and we set the \"last_buf\" or\n             * \"last_in_chain\" flag in the last buf of \"in\" */\n\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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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 while 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 variable 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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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#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#include <pcre.h>\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\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 16 bit */\n#define NGX_HTTP_LUA_CONTEXT_SET                0x0001\n#define NGX_HTTP_LUA_CONTEXT_REWRITE            0x0002\n#define NGX_HTTP_LUA_CONTEXT_ACCESS             0x0004\n#define NGX_HTTP_LUA_CONTEXT_CONTENT            0x0008\n#define NGX_HTTP_LUA_CONTEXT_LOG                0x0010\n#define NGX_HTTP_LUA_CONTEXT_HEADER_FILTER      0x0020\n#define NGX_HTTP_LUA_CONTEXT_BODY_FILTER        0x0040\n#define NGX_HTTP_LUA_CONTEXT_TIMER              0x0080\n#define NGX_HTTP_LUA_CONTEXT_INIT_WORKER        0x0100\n#define NGX_HTTP_LUA_CONTEXT_BALANCER           0x0200\n#define NGX_HTTP_LUA_CONTEXT_SSL_CERT           0x0400\n#define NGX_HTTP_LUA_CONTEXT_SSL_SESS_STORE     0x0800\n#define NGX_HTTP_LUA_CONTEXT_SSL_SESS_FETCH     0x1000\n#define NGX_HTTP_LUA_CONTEXT_EXIT_WORKER        0x2000\n#define NGX_HTTP_LUA_CONTEXT_SSL_CLIENT_HELLO   0x4000\n#define NGX_HTTP_LUA_CONTEXT_SERVER_REWRITE     0x8000\n\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 union ngx_http_lua_srv_conf_u  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_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_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#   if (LUA_HAVE_PCRE_JIT)\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\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_http_lua_balancer_peer_data_t      *balancer_peer_data;\n                    /* neither yielding nor recursion is possible in\n                     * balancer_by_lua*, so there cannot be any races among\n                     * concurrent requests and it is safe to store the peer\n                     * data pointer in the main conf.\n                     */\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};\n\n\nunion ngx_http_lua_srv_conf_u {\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_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\ntypedef struct {\n#if (NGX_HTTP_SSL)\n    ngx_ssl_t              *ssl;  /* shared by SSL cosockets */\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#if (nginx_version >= 1019004)\n    ngx_array_t            *ssl_conf_commands;\n#endif\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     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                  *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} ngx_http_lua_loc_conf_t;\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    uint16_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_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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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#if (T_NGX_XQUIC)\n        if (!r->xqstream) {\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#if (T_NGX_XQUIC)\n        }\n#endif\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    /*  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    /*  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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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_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_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    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_CONTENT\n                                       | NGX_HTTP_LUA_CONTEXT_TIMER\n                                       | NGX_HTTP_LUA_CONTEXT_HEADER_FILTER\n                                       | NGX_HTTP_LUA_CONTEXT_BALANCER\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                        | 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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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_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_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": "modules/ngx_http_lua_module/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_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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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    /*  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    /*  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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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 (T_NGX_XQUIC)\n    if (mr->xqstream) {\n        return luaL_error(L, \"http3 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\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    count = part->nelts;\n    while (part->next != NULL) {\n        part = part->next;\n        count += part->nelts;\n    }\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\n    if (count <= 0) {\n        return NGX_OK;\n    }\n\n    n = 0;\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 (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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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\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        *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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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\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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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    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_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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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    /*  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    /*  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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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(ngx_http_request_t *r, int status)\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    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 {\n        r->headers_out.status_line.len = 0;\n    }\n\n    return NGX_OK;\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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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_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#include \"ngx_http_lua_headers.h\"\n#include \"ngx_http_lua_headers_out.h\"\n#include \"ngx_http_lua_pipe.h\"\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_set_ssl(ngx_conf_t *cf,\n    ngx_http_lua_loc_conf_t *llcf);\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\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    /* 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(\"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    { 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_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#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    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    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#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->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\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 = 100;\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_session_store_handler = NULL;\n     *      lscf->srv.ssl_session_store_src = { 0, NULL };\n     *      lscf->srv.ssl_session_store_chunkname = NULL;\n     *      lscf->srv.ssl_session_store_src_key = NULL;\n     *\n     *      lscf->srv.ssl_session_fetch_handler = NULL;\n     *      lscf->srv.ssl_session_fetch_src = { 0, NULL };\n     *      lscf->srv.ssl_session_fetch_chunkname = NULL;\n     *      lscf->srv.ssl_session_fetch_src_key = NULL;\n     *\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->balancer.src_ref = LUA_REFNIL;\n\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->rewrite_src = {{ 0, NULL }, NULL, NULL, NULL};\n     *      conf->rewrite_src_key = NULL;\n     *      conf->rewrite_handler = NULL;\n     *\n     *      conf->content_src = {{ 0, NULL }, NULL, NULL, NULL};\n     *      conf->content_src_key = NULL;\n     *      conf->content_handler = NULL;\n     *\n     *      conf->log_src = {{ 0, NULL }, NULL, NULL, NULL};\n     *      conf->log_src_key = NULL;\n     *      conf->log_handler = 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     *\n     *      conf->body_filter_src = {{ 0, NULL }, NULL, NULL, NULL};\n     *      conf->body_filter_src_key = NULL;\n     *      conf->body_filter_handler = 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     */\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->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#if (nginx_version >= 1019004)\n    conf->ssl_conf_commands = NGX_CONF_UNSET_PTR;\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->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    ngx_conf_merge_bitmask_value(conf->ssl_protocols, prev->ssl_protocols,\n                                 (NGX_CONF_BITMASK_SET|NGX_SSL_SSLv3\n                                  |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1\n                                  |NGX_SSL_TLSv1_2));\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_str_value(conf->ssl_trusted_certificate,\n                             prev->ssl_trusted_certificate, \"\");\n    ngx_conf_merge_str_value(conf->ssl_crl, prev->ssl_crl, \"\");\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 (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_set_ssl(ngx_conf_t *cf, ngx_http_lua_loc_conf_t *llcf)\n{\n    ngx_pool_cleanup_t  *cln;\n\n    llcf->ssl = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_t));\n    if (llcf->ssl == NULL) {\n        return NGX_ERROR;\n    }\n\n    llcf->ssl->log = cf->log;\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_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#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#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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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_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_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_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_CONTENT);\n\n    if (!r->header_sent && !ctx->header_sent) {\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\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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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\nstatic void *(*old_pcre_malloc)(size_t);\nstatic void (*old_pcre_free)(void *ptr);\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... */\nstatic void *\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\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 /* NGX_PCRE */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "modules/ngx_http_lua_module/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)\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#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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/src/ngx_http_lua_phase.h",
    "content": "#ifndef _NGX_HTTP_LUA_PHASE_H_INCLUDED_\n#define _NGX_HTTP_LUA_PHASE_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n\n\nvoid ngx_http_lua_inject_phase_api(lua_State *L);\n\n\n#endif /* _NGX_HTTP_LUA_PHASE_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "modules/ngx_http_lua_module/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    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    c = proc->pipe->stdout_ctx->c;\n    if (c) {\n        rev = c->read;\n        ngx_http_lua_pipe_clear_event(rev);\n    }\n\n    wait_co_ctx->cleanup = NULL;\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    c = proc->pipe->stderr_ctx->c;\n    if (c) {\n        rev = c->read;\n        ngx_http_lua_pipe_clear_event(rev);\n    }\n\n    wait_co_ctx->cleanup = NULL;\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    c = proc->pipe->stdin_ctx->c;\n    if (c) {\n        wev = c->write;\n        ngx_http_lua_pipe_clear_event(wev);\n    }\n\n    wait_co_ctx->cleanup = NULL;\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    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    wait_co_ctx->cleanup = NULL;\n}\n\n\n#endif /* HAVE_NGX_LUA_PIPE */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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\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)\n#   define LUA_HAVE_PCRE_DFA 1\n#else\n#   define LUA_HAVE_PCRE_DFA 0\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    pcre                         *regex;\n    pcre_extra                   *regex_sd;\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    pcre         *regex;\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    pcre                    *regex;\n    pcre_extra              *regex_sd;\n    int                      ncaptures;\n    int                     *captures;\n    int                      captures_len;\n    uint8_t                  flags;\n} ngx_http_lua_regex_ctx_t;\n\n\nstatic void ngx_http_lua_regex_free_study_data(ngx_pool_t *pool,\n    pcre_extra *sd);\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, pcre_extra *sd)\n{\n    ngx_pool_t              *old_pool;\n\n    old_pool = ngx_http_lua_pcre_malloc_init(pool);\n\n#if LUA_HAVE_PCRE_JIT\n    pcre_free_study(sd);\n#else\n    pcre_free(sd);\n#endif\n\n    ngx_http_lua_pcre_malloc_done(old_pool);\n}\n\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\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        pcre_jit_stack_free(lmcf->jit_stack);\n\n        ngx_http_lua_pcre_malloc_done(old_pool);\n    }\n\n    old_pool = ngx_http_lua_pcre_malloc_init(pool);\n\n    lmcf->jit_stack = pcre_jit_stack_alloc(NGX_LUA_RE_MIN_JIT_STACK_SIZE,\n                                           size);\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 /* LUA_HAVE_PCRE_JIT */\n}\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    pcre_extra              *sd = NULL;\n    ngx_http_lua_regex_t    *re;\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\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#if (LUA_HAVE_PCRE_JIT)\n\n    if (flags & NGX_LUA_RE_MODE_JIT) {\n\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->jit_stack) {\n        pcre_assign_jit_stack(sd, NULL, lmcf->jit_stack);\n    }\n\n#endif /* LUA_HAVE_PCRE_JIT */\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    if (flags & NGX_LUA_RE_MODE_DFA) {\n        ovecsize = 2;\n        re_comp.captures = 0;\n\n    } else {\n        ovecsize = (re_comp.captures + 1) * 3;\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 (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\n    re->regex = re_comp.regex;\n    re->regex_sd = sd;\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    if (sd) {\n        ngx_http_lua_regex_free_study_data(pool, sd);\n    }\n\n    if (pool) {\n        ngx_destroy_pool(pool);\n    }\n\n    return NULL;\n}\n\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]), 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\nvoid\nngx_http_lua_ffi_destroy_regex(ngx_http_lua_regex_t *re)\n{\n    ngx_pool_t                  *old_pool;\n\n    dd(\"destroy regex called\");\n\n    if (re == NULL || re->pool == NULL) {\n        return;\n    }\n\n    if (re->regex_sd) {\n        old_pool = ngx_http_lua_pcre_malloc_init(re->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        re->regex_sd = NULL;\n    }\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    return pcre_version();\n}\n\n\n#endif /* NGX_PCRE */\n\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "modules/ngx_http_lua_module/src/ngx_http_lua_regex.h",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef _NGX_HTTP_LUA_REGEX_H_INCLUDED_\n#define _NGX_HTTP_LUA_REGEX_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n#include \"ngx_http_lua_script.h\"\n\n\n#if (NGX_PCRE)\nvoid ngx_http_lua_inject_regex_api(lua_State *L);\nngx_int_t ngx_http_lua_ffi_set_jit_stack_size(int size, u_char *errstr,\n    size_t *errstr_size);\n#endif\n\n\n#endif /* _NGX_HTTP_LUA_REGEX_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "modules/ngx_http_lua_module/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_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;\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        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\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        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        len += cl->buf->last - cl->buf->pos;\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    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    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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/src/ngx_http_lua_req_method.h",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef _NGX_HTTP_LUA_METHOD_H_INCLUDED_\n#define _NGX_HTTP_LUA_METHOD_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n\n\nvoid ngx_http_lua_inject_req_method_api(lua_State *L);\n\n\n#endif /* _NGX_HTTP_LUA_METHOD_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "modules/ngx_http_lua_module/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                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    /*  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    /*  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#if (T_NGX_XQUIC)\n        if (!r->xqstream) {\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 (T_NGX_XQUIC)\n        }\n#endif\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            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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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                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            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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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            ngx_queue_remove(&sd->queue);\n            ngx_queue_insert_head(&ctx->sh->lru_queue, &sd->queue);\n\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            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.b, 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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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 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 ngx_int_t 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};\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\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\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\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 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    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        u->resolved->host = url.addrs[0].name;\n\n    } else {\n        u->resolved->host = host;\n        u->resolved->port = url.default_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 && !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    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_REWRITE\n                               | NGX_HTTP_LUA_CONTEXT_ACCESS\n                               | NGX_HTTP_LUA_CONTEXT_CONTENT\n                               | NGX_HTTP_LUA_CONTEXT_TIMER\n                               | NGX_HTTP_LUA_CONTEXT_SSL_CERT);\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    /* TODO: we may reuse the userdata here */\n    lua_rawseti(L, 1, SOCKET_BIND_INDEX);\n\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\n    pc = &u->peer;\n\n    pc->log = r->connection->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    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 = 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->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\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_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, dc->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, dc->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\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    u->input_filter_ctx = u;\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    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_error(NGX_LOG_ERR, r->connection->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_error(NGX_LOG_ERR, r->connection->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 pattern 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 pattern 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 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_error(NGX_LOG_ERR, r->connection->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_int_t                    rc;\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            rc = ngx_http_lua_socket_push_input_data(r, ctx, u, L);\n            if (rc == NGX_ERROR) {\n                lua_pushnil(L);\n                lua_pushliteral(L, \"no memory\");\n                return 2;\n            }\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    rc = ngx_http_lua_socket_push_input_data(r, ctx, u, L);\n    if (rc == NGX_ERROR) {\n        lua_pushnil(L);\n        lua_pushliteral(L, \"no memory\");\n        return 2;\n    }\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 settimeout: 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    /* empty */\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, r->connection->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, r->connection->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, r->connection->log, 0,\n                          \"lua tcp socket connect timed out,\"\n                          \" when connecting to %V:%ud\",\n                          &c->addr_text, ngx_inet_get_port(u->peer.sockaddr));\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, r->connection->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->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 arguments, \"\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    u->input_filter_ctx = cp;\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    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_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 || 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 zero arguments, 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 (T_NGX_XQUIC)\n    if (r->xqstream) {\n        return luaL_error(L, \"http3 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_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    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    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    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    llcf = ngx_http_get_module_loc_conf(r, ngx_http_lua_module);\n\n    if (spool == NULL) {\n        /* create a new socket pool for the current peer key */\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        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 (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 (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    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            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\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    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    n = recv(c->fd, buf, 1, MSG_PEEK);\n\n    if (n == -1 && ngx_socket_errno == NGX_EAGAIN) {\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 ngx_int_t\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    return NGX_OK;\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    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    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\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": "modules/ngx_http_lua_module/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\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\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\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": "modules/ngx_http_lua_module/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);\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);\n\n\nenum {\n    SOCKET_CTX_INDEX = 1,\n    SOCKET_TIMEOUT_INDEX = 2,\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_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 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    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\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    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);\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 = r->connection->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_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_error(NGX_LOG_ERR, r->connection->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_error(NGX_LOG_ERR, r->connection->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(u->udp_connection.connection,\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, r->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)\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#endif\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": "modules/ngx_http_lua_module/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_msec_t                       read_timeout;\n\n    ngx_http_upstream_resolved_t    *resolved;\n\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 */\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": "modules/ngx_http_lua_module/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\n#if (NGX_HTTP_SSL)\n\n\nint ngx_http_lua_ssl_ctx_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    return NGX_OK;\n}\n\n\n#endif /* NGX_HTTP_SSL */\n"
  },
  {
    "path": "modules/ngx_http_lua_module/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;    /* fake 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    int                      exit_code;  /* exit code for openssl's\n                                            set_client_hello_cb or\n                                            set_cert_cb callback */\n\n    int                      ctx_ref;  /*  reference to anchor\n                                           request ctx data in lua\n                                           registry */\n\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} ngx_http_lua_ssl_ctx_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;\n\n\n#endif\n\n\n#endif  /* _NGX_HTTP_LUA_SSL_H_INCLUDED_ */\n"
  },
  {
    "path": "modules/ngx_http_lua_module/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\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                           \"lua_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#if (T_NGX_XQUIC)\n    if (c->xquic_conn) {\n        ngx_http_xquic_connection_t *qc = (ngx_http_xquic_connection_t *)c->data;\n        hc = qc->http_connection;\n    }\n#endif\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#if (T_NGX_XQUIC)\n    fc->xquic_conn = c->xquic_conn;\n#endif\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                       \"lua_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\n#if 1\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#endif\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\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 done\");\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                   \"lua_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\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_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_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_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 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    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 *ca_certs,\n    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)              *chain = ca_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 (chain != NULL) {\n        ca_store = X509_STORE_new();\n        if (ca_store == NULL) {\n            *err = \"X509_STORE_new() failed\";\n            return NGX_ERROR;\n        }\n\n        /* construct name chain */\n\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(chain); i++) {\n            x509 = sk_X509_value(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        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        SSL_set_client_CA_list(ssl_conn, name_chain);\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)\n{\n    if (r->connection == NULL || r->connection->ssl == NULL) {\n        return NULL;\n    }\n\n    return r->connection->ssl->connection;\n}\n\n\n#endif /* NGX_HTTP_SSL */\n"
  },
  {
    "path": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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                           \"lua_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 < 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                       \"lua_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\n#if 1\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#endif\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\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 done\");\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                   \"lua_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    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}\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    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\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#ifdef 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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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#endif\n\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)\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, *nextupdate;\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    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#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": "modules/ngx_http_lua_module/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 done\");\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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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\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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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\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);\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_vars_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);\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_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#if (NGX_HTTP_V2)\n    if (r->main->stream) {\n        return luaL_error(L, \"http2 requests not supported yet\");\n    }\n#endif\n\n#if (T_NGX_XQUIC)\n    if (r->main->xqstream) {\n        return luaL_error(L, \"http3 requests not supported yet\");\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_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\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        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_vars_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 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\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)\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) != NGX_OK) {\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_vars_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    ngx_time_t                    *tp;\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#if (T_NGX_XQUIC)\n    sr->xqstream = r->xqstream;\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#if (T_NGX_VARS)\n    sr->raw_uri = r->raw_uri;\n#endif\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    tp = ngx_timeofday();\n    sr->start_sec = tp->sec;\n    sr->start_msec = tp->msec;\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)\n{\n    ngx_table_elt_t                 *clh, *header;\n    ngx_list_part_t                 *part;\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        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    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    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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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    return r->start_sec + r->start_msec / 1000.0;\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": "modules/ngx_http_lua_module/src/ngx_http_lua_time.h",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef _NGX_HTTP_LUA_TIME_H_INCLUDED_\n#define _NGX_HTTP_LUA_TIME_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n\n\nvoid ngx_http_lua_inject_time_api(lua_State *L);\nvoid ngx_http_lua_inject_req_time_api(lua_State *L);\n\n\n#endif /* _NGX_HTTP_LUA_TIME_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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, 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_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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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\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            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 (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        if (r->headers_out.content_length) {\n            r->headers_out.content_length->hash = 0;\n        }\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_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                    ngx_http_lua_del_thread(r, L, ctx, ctx->cur_co_ctx);\n                    ctx->uthreads--;\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                    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                ngx_http_lua_del_thread(r, L, ctx, ctx->cur_co_ctx);\n                ctx->uthreads--;\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 (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 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 (T_NGX_XQUIC)\n    if (r->xqstream) {\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\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 (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    /* 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 (pool) {\n        ngx_destroy_pool(pool);\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_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\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "modules/ngx_http_lua_module/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_CONTENT               \\\n                                | NGX_HTTP_LUA_CONTEXT_TIMER                 \\\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\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\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_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_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\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\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\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": "modules/ngx_http_lua_module/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    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": "modules/ngx_http_lua_module/src/ngx_http_lua_variable.h",
    "content": "\n/*\n * Copyright (C) Xiaozhe Wang (chaoslawful)\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef _NGX_HTTP_LUA_VARIABLE_H_INCLUDED_\n#define _NGX_HTTP_LUA_VARIABLE_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n\n\nvoid ngx_http_lua_inject_variable_api(lua_State *L);\n\n\n#endif /* _NGX_HTTP_LUA_VARIABLE_H_INCLUDED_ */\n\n/* vi:set ft=c ts=4 sw=4 et fdm=marker: */\n"
  },
  {
    "path": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/src/ngx_http_lua_worker.h",
    "content": "\n/*\n * Copyright (C) Yichun Zhang (agentzh)\n */\n\n\n#ifndef _NGX_HTTP_LUA_WORKER_H_INCLUDED_\n#define _NGX_HTTP_LUA_WORKER_H_INCLUDED_\n\n\n#include \"ngx_http_lua_common.h\"\n\n\nvoid ngx_http_lua_inject_worker_api(lua_State *L);\n\n\n#endif /* _NGX_HTTP_LUA_WORKER_H_INCLUDED_ */\n"
  },
  {
    "path": "modules/ngx_http_lua_module/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    int                      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        /* inject API via ffi */\n        lua_getglobal(vm, \"require\");\n        lua_pushstring(vm, \"resty.core.regex\");\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.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    ctx->next = ctxpool->next;\n    ctxpool->next = ctx;\n\n    /* clean Lua stack */\n    lua_settop(ctx->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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/t/.gitignore",
    "content": "servroot\n\n"
  },
  {
    "path": "modules/ngx_http_lua_module/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-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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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\nAPI disabled in the context of set_by_lua*\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\nAPI disabled in the context of set_by_lua*\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": "modules/ngx_http_lua_module/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"
  },
  {
    "path": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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\nalert\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--- 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--- 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--- 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\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\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\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\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--- 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\n\n\n=== TEST 28: accepts NGX_DECLINED\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\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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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"
  },
  {
    "path": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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 + 33) + 1;\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--- 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\n\".*Set-Cookie: TestCookie1=foo\\r\nSet-Cookie: TestCookie2=bar.*\"\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_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--- 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\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 8.8.8.8;\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\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 12354;\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:12354;\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\n--- main_config\nworker_shutdown_timeout 1;\n--- config\nlocation /t {\n    content_by_lua_block {\n        local function thread_func()\n            local sock = ngx.socket.tcp()\n            local ok, err = sock:connect(\"127.0.0.1\", 65110)\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()\n            ngx.thread.spawn(thread_func)\n        end\n\n        ngx.timer.at(1, timer_func)\n        ngx.say(\"Hello world\")\n    }\n}\n--- request\n    GET /t\n--- response_body\nHello world\n--- shutdown_error_log eval\nqr|failed to read a line: closed|\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"
  },
  {
    "path": "modules/ngx_http_lua_module/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() * 2 + 9);\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\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\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\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\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\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\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\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\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\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\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"
  },
  {
    "path": "modules/ngx_http_lua_module/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\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 chomp\nX-Foo: a\\r\\n.*?X-Foo: bc\\r\\n\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 chomp\nX-Foo: a\\r\\n.*?X-Foo: abc\\r\\n\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\n\".*Foo: a\\r\nFoo: b.*\"\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\n\".*Foo: a\\r\nFoo: b.*\"\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--- 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\nLocation: http://localhost:$ServerPort/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\nLocation: http://localhost:$ServerPort/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\nLocation: http://localhost:$ServerPort/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\nLocation: http://localhost:$ServerPort/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 chop\ncache-Control: private\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\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\nmy Content-Length: 8589934591\nupstream prematurely closed connection while sending to client\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 chomp\nAge: \\d\\r\\n\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 chomp\nAge: \\d\\r\\n\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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/t/020-subrequest.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse 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\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#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--- 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\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:19112; #$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: 19112\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:19112; #$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: 19112\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:19113;\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: 19113\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:19113;\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: 19113\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:19113;\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: 19113\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:19113;\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: 19113\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:19113;\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: 19113\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:19113;\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: 19113\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:19113;\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: 19113\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:19113;\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: 19113\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:19113;\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: 19113\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:19113;\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: 19113\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:19113;\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: 19113\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:19113;\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: 19113\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\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\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\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"
  },
  {
    "path": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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: Location: /echo\\r\\n\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": "modules/ngx_http_lua_module/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\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        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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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--- 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\n\n\n=== TEST 7: working with ngx_auth_request (simplest form)\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\n\n\n=== TEST 8: working with ngx_auth_request\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\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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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\nplan tests => repeat_each() * (blocks() * 4 + 15);\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": "modules/ngx_http_lua_module/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: Location: /foo\\r\\n\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": "modules/ngx_http_lua_module/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\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": "modules/ngx_http_lua_module/t/023-rewrite/req-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 + 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"
  },
  {
    "path": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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--- 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--- 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--- 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": "modules/ngx_http_lua_module/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--- 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--- 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--- 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--- 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--- 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--- 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--- 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--- 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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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    $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, when connecting to 127.0.0.2:12345\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": "modules/ngx_http_lua_module/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\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\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\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, when connecting to 127.0.0.2:12345\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\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\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\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\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\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\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\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\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\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\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\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\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\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\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\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\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\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\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\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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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\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\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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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\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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/t/024-access/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\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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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--- 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\n\n\n=== TEST 7: working with ngx_auth_request (simplest form)\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\n\n\n=== TEST 8: working with ngx_auth_request\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\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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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\nplan tests => repeat_each() * (blocks() * 4 + 15);\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": "modules/ngx_http_lua_module/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: Location: /foo\\r\\n\n--- response_body_like: 302 Found\n--- error_code: 302\n"
  },
  {
    "path": "modules/ngx_http_lua_module/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\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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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--- 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--- 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--- 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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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--- 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--- 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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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"
  },
  {
    "path": "modules/ngx_http_lua_module/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\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\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\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\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\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"
  },
  {
    "path": "modules/ngx_http_lua_module/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--- 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\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": "modules/ngx_http_lua_module/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"
  },
  {
    "path": "modules/ngx_http_lua_module/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            ngx.say(\"Foo: \", h[\"Foo\"] or \"nil\")\n            ngx.say(\"Bar: \", h[\"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\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\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\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\nFoo: a\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\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;\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}\nCORE::join(\"\", @k) . \"found 100 headers\\n\";\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;\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\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;\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}\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;\nmy $i = 1;\nwhile ($i <= 105) {\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}\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\nqr/Connection: close\\r\nTest-Header: 1\\r\n\\r\n$/\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\nqr/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\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\nBar: baz\nConnection: close\nHost: localhost\nMy-Foo: bar\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\n\"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\n\\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\n\"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\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\n\"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\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\n\"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\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\nBar: baz\nConnection: close\nHost: localhost\nMy-Foo: bar\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\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\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\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\nfound 3 headers.\n--- timeout: 4\n--- no_error_log\nlua exceeding request header limit\n[error]\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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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\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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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\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": "modules/ngx_http_lua_module/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\nerror: pcre_compile() failed: missing ) in \"(abc\"\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\nerror: pcre_compile() failed: missing ) in \"([0-9]+\"\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_like chop\n^error: pcre_exec\\(\\) failed: -10$\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\nerror: pcre_exec() failed: -8\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": "modules/ngx_http_lua_module/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\nerror: pcre_compile() failed: missing ) in \"(abc\"\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_like chop\nerror: pcre_exec\\(\\) failed: -10\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\nerror: pcre_exec() failed: -8\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": "modules/ngx_http_lua_module/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\nerror: pcre_compile() failed: missing ) in \"(abc\"\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_like chop\nerror: pcre_exec\\(\\) failed: -10\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\nerror: pcre_exec() failed: -8\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": "modules/ngx_http_lua_module/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_like chop\nerror: pcre_exec\\(\\) failed: -10\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\nerror: pcre_exec() failed: -8\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": "modules/ngx_http_lua_module/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\nerror: pcre_compile() failed: missing ) in \"(abc\"\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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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\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--- 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\n\n\n=== TEST 22: lua error (nil)\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\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\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\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\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\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\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\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\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\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\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\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\nAPI disabled in the context of header_filter_by_lua*\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\nAPI disabled in the context of header_filter_by_lua*\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\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\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\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\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\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\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\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"
  },
  {
    "path": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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.01\")\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.002\")\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.002\")\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.01\")\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--- 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--- 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--- 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--- 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": "modules/ngx_http_lua_module/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 + 52 );\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\nX-Old: \\S+/client_body_temp/\\d+\\r\n.*?X-New: \\S+/html/a\\.txt\\r\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\nX-Old: \\S+/client_body_temp/\\d+\\r\n.*?X-New: \\S+/html/a\\.txt\\r\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\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\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\n--- no_error_log\n[alert]\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]\n--- no_error_log\na client request body is buffered to a temporary file\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]\n--- no_error_log\na client request body is buffered to a temporary file\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]\n--- no_error_log\na client request body is buffered to a temporary file\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\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\n.*?X-New: \\S+/html/a\\.txt\\r\n--- response_body\nWill you change this world?\n--- no_error_log\n[error]\n[alert]\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": "modules/ngx_http_lua_module/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\nProxy-Host: 127.0.0.1\\:\\d+\\r\nProxy-Port: \\d+\\r\nProxy-Add-X-Forwarded-For: 127.0.0.1\\r\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 8091;\n        location / {\n            content_by_lua_block{\n                ngx.print(\"this is backend peer 8091\")\n            }\n        }\n    }\n    server {\n        # this is the real entry point\n        listen 8092;\n        location / {\n            content_by_lua_block{\n                ngx.print(\"this is backend peer 8092\")\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=8091\", \"GET /balancer?port=8092\"]\n--- response_body eval\n[\"this is backend peer 8091\", \"this is backend peer 8092\"]\n"
  },
  {
    "path": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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\npcre JIT compiling result: 1\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\npcre JIT compiling result: 1\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\nqr/pcre JIT compiling result: \\d+/\n\n--- grep_error_log_out eval\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\nqr/pcre JIT compiling result: \\d+/\n\n--- grep_error_log_out eval\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\nerror: pcre_compile() failed: missing ) in \"(abc\"\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\nerror: pcre_exec() failed: -8\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": "modules/ngx_http_lua_module/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"
  },
  {
    "path": "modules/ngx_http_lua_module/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\npcre JIT compiling result: 1\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\npcre JIT compiling result: 1\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\npcre JIT compiling result: 1\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\npcre JIT compiling result: 1\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\nqr/pcre JIT compiling result: \\d+/\n\n--- grep_error_log_out eval\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\nqr/pcre JIT compiling result: \\d+/\n\n--- grep_error_log_out eval\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\nqr/pcre JIT compiling result: \\d+/\n\n--- grep_error_log_out eval\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\nqr/pcre JIT compiling result: \\d+/\n\n--- grep_error_log_out eval\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\nerror: pcre_compile() failed: missing ) in \"(abc\"\n--- no_error_log\n[error]\n"
  },
  {
    "path": "modules/ngx_http_lua_module/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\nerror: pcre_compile() failed: missing ) in \"(abc\"\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": "modules/ngx_http_lua_module/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\npcre JIT compiling result: 1\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\npcre JIT compiling result: 1\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\nqr/pcre JIT compiling result: \\d+/\n\n--- grep_error_log_out eval\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\nqr/pcre JIT compiling result: \\d+/\n\n--- grep_error_log_out eval\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\nerror: pcre_compile() failed: missing ) in \"(abc\"\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\nerror: pcre_compile() failed: missing ) in \"(abc\"\n--- no_error_log\n[error]\n"
  },
  {
    "path": "modules/ngx_http_lua_module/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\nerror: pcre_compile() failed: missing ) in \"(abc\"\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\nerror: pcre_compile() failed: missing ) in \"(abc\"\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": "modules/ngx_http_lua_module/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\npcre JIT compiling result: 1\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\npcre JIT compiling result: 1\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\nqr/pcre JIT compiling result: \\d+/\n\n--- grep_error_log_out eval\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\nqr/pcre JIT compiling result: \\d+/\n\n--- grep_error_log_out eval\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\nerror: pcre_compile() failed: missing ) in \"(abc\"\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\nerror: pcre_compile() failed: missing ) in \"(abc\"\n--- no_error_log\n[error]\n"
  },
  {
    "path": "modules/ngx_http_lua_module/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\nerror: pcre_compile() failed: missing ) in \"(abc\"\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\nerror: pcre_compile() failed: missing ) in \"(abc\"\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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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\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--- 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\n[\nqr/lua writes elapsed [12](?:\\.\\d+)? sec/,\nqr/lua flush requires waiting: buffered 0x[0-9a-f]+, delayed:1/,\n]\n\n--- no_error_log\n[error]\n--- timeout: 4\n"
  },
  {
    "path": "modules/ngx_http_lua_module/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}\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() * 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": "modules/ngx_http_lua_module/t/058-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 + 21);\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: 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            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--- 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\n\n\n=== TEST 5: connection refused (tcp)\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, when connecting to 127.0.0.2:12345\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        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--- 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--- 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--- 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--- 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--- 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--- 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--- 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--- 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--- 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--- 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--- 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--- 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--- 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--- 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--- 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--- 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\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        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\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        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\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        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--- 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--- 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--- 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--- 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--- 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--- 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--- 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--- 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--- 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--- 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--- 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--- 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--- 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--- 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--- 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--- 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--- 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--- 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--- 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--- 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--- 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--- 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--- 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--- 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--- 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--- 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\n\n\n=== TEST 66: receiveany send data after read side closed\n--- config\n    server_tokens off;\n    location = /t {\n        content_by_lua_block {\n            local sock = ngx.socket.tcp()\n            sock:settimeout(500)\n            assert(sock:connect(\"127.0.0.1\", 7658))\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: 7658\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--- 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--- 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\n\n\n=== TEST 69: receiveany with limited, max is smaller than data\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\n\n\n=== TEST 70: send tables of string fragments (with floating point number too)\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--- 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"
  },
  {
    "path": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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: 116\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\n116\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 = 116\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 = 6\n--- no_error_log\n[error]\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: 116\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 = 16\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 = 6\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: 6\\\\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 = 7\n--- no_error_log\n[error]\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\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"
  },
  {
    "path": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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    $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, when connecting to 127.0.0.2:12345\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, when connecting to 127.0.0.2:12345\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, when connecting to 127.0.0.2:12345\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, when connecting to 127.0.0.2:12345\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, when connecting to 127.0.0.2:12345\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\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": "modules/ngx_http_lua_module/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--- 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--- 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--- 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--- 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--- 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--- 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--- 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--- 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--- 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--- 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--- 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--- 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--- 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--- 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--- 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--- 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--- 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\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"
  },
  {
    "path": "modules/ngx_http_lua_module/t/067-req-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 + 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\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"
  },
  {
    "path": "modules/ngx_http_lua_module/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--- 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--- 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--- 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--- 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--- 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--- 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--- 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--- 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--- 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--- 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--- 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, when connecting to\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\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, when connecting to\n\n\n\n=== TEST 46: conn queuing: resume connect operation if resumed connect failed (could not be resolved)\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"
  },
  {
    "path": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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"
  },
  {
    "path": "modules/ngx_http_lua_module/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"
  },
  {
    "path": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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\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\nAPI disabled in the context of log_by_lua*\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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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--- 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--- 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--- 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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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    } else {\n        $ENV{TEST_NGINX_POSTPONE_OUTPUT} = 1;\n        $ENV{TEST_NGINX_EVENT_TYPE} = 'poll';\n        $ENV{MOCKEAGAIN}='w'\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"
  },
  {
    "path": "modules/ngx_http_lua_module/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\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\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\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\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"
  },
  {
    "path": "modules/ngx_http_lua_module/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\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\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": "modules/ngx_http_lua_module/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--- 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--- 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--- 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--- 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--- 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--- 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--- 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\n\n\n=== TEST 8: bad option table\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\n\n\n=== TEST 9: ambiguous boundary patterns (--abc), small buffer\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--- 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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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() + 15);\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\n--- config\n    server_tokens off;\n    location /t {\n        #set $port 5000;\n        set $port $TEST_NGINX_SERVER_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\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"
  },
  {
    "path": "modules/ngx_http_lua_module/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--- 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\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\n\n\n=== TEST 11: set GET to WebDAV methods\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"
  },
  {
    "path": "modules/ngx_http_lua_module/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"
  },
  {
    "path": "modules/ngx_http_lua_module/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, when connecting to 127.0.0.2:12345\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": "modules/ngx_http_lua_module/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\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\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--- 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\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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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\", 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\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        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()\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": "modules/ngx_http_lua_module/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);\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\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\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[warn]\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--- 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\n\n\n=== TEST 17: exit(444) 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(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\n\n\n=== TEST 18: exit(408) 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(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\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[warn]\n"
  },
  {
    "path": "modules/ngx_http_lua_module/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--- 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"
  },
  {
    "path": "modules/ngx_http_lua_module/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--- 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"
  },
  {
    "path": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/t/100-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\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\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\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\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"
  },
  {
    "path": "modules/ngx_http_lua_module/t/101-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\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": "modules/ngx_http_lua_module/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)\\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)\\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": "modules/ngx_http_lua_module/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\n1.1\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": "modules/ngx_http_lua_module/t/104-req-raw-header.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 + 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": "modules/ngx_http_lua_module/t/105-pressure.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#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\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": "modules/ngx_http_lua_module/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--- 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--- 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:12345\n--- udp_listen: 12345\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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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--- 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": "modules/ngx_http_lua_module/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    } 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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/t/116-raw-req-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() * 43;\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"
  },
  {
    "path": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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\nerror: pcre_compile() failed: missing ) in \"(abc\"\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_like chop\n^error: pcre_exec\\(\\) failed: -10$\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\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\nerror: pcre_exec() failed: -8\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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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\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:12345\n--- udp_listen: 12345\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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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);\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: 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"
  },
  {
    "path": "modules/ngx_http_lua_module/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 7658;\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: 7658\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 7658;\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: 7658\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": "modules/ngx_http_lua_module/t/129-ssl-socket.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() * (blocks() * 7 - 3);\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\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\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.bing.com\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.bing.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.bing.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: 57 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\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    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;\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-SHA;\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-SHA (SSLv3|TLSv1)/]\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;\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;\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, cipher: \"ECDHE-RSA-AES256-SHA (SSLv3|TLSv1)/]\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\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                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                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)\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(2000)\n\n            do\n\n            for i = 1, 3 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 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: \\1\nlua ssl save session: \\1\nlua ssl free session: \\1\nlua ssl free session: \\1\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 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\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)/\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--- 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 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\nconnected: 1\nfailed to do SSL handshake: 18: self signed certificate\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--- error_log\nlua 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\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--- 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--- 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"
  },
  {
    "path": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/t/131-duplex-req-socket.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} = 'slow';\n}\n\nuse Test::Nginx::Socket::Lua;\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": "modules/ngx_http_lua_module/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--- 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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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 + 9);\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--- no_error_log\n[warn]\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[\n'[warn]',\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--- no_error_log\n[warn]\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--- no_error_log\n[warn]\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--- no_error_log\n[warn]\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--- no_error_log\n[warn]\n\n\n\n=== TEST 7: ngx.req.get_method() works\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--- no_error_log\n[warn]\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--- no_error_log\n[warn]\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--- 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--- no_error_log\n[warn]\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_llua_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--- no_error_log\n[warn]\n"
  },
  {
    "path": "modules/ngx_http_lua_module/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` };\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\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'lua_certificate_by_lua: handler return value: -1, cert cb exit code: 0',\nqr/\\[info\\] .*? SSL_do_handshake\\(\\) failed .*?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'lua_certificate_by_lua: cert cb exit code: 0',\nqr/\\[info\\] .*? SSL_do_handshake\\(\\) failed .*?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'lua_certificate_by_lua: handler return value: 500, cert cb exit code: 0',\nqr/\\[info\\] .*? SSL_do_handshake\\(\\) failed .*?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'lua_certificate_by_lua: cert cb exit code: 0',\nqr/\\[info\\] .*? SSL_do_handshake\\(\\) failed .*?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\\] .*?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:12345 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\", 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                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"
  },
  {
    "path": "modules/ngx_http_lua_module/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\n} else {\n    plan tests => repeat_each() * (blocks() * 5 + 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    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        int depth, char **err);\n\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 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_ssl_verify_client(r, cert, 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(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, -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\nFAILED: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 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_ssl_verify_client(r, cert, 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(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--- 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"
  },
  {
    "path": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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\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\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\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\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\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\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\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\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\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\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\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\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"
  },
  {
    "path": "modules/ngx_http_lua_module/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);\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\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--- 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\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\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\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\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\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\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\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\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\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_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_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\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\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\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\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--- no_error_log\n[error]\n[alert]\n[emerg]\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\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\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\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\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[warn]\n[error]\n[alert]\n[emerg]\n\n\n\n=== TEST 16: ssl_session_fetch_by_lua* always runs when using SSLv3 (SSLv3 does not support session tickets)\n--- http_config\n    ssl_session_fetch_by_lua_block { print(\"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        ssl_protocols SSLv3;\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 SSLv3;\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/ssl_session_fetch_by_lua\\(nginx\\.conf:\\d+\\):.*?,|\\bssl session fetch: 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 ?\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:\\d+\\):1: ssl_session_fetch_by_lua\\* is running!,\n/m,\nqr/^reusable connection: 0\nssl session fetch: connection reusable: 0\nssl_session_fetch_by_lua\\(nginx\\.conf:\\d+\\):1: ssl_session_fetch_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:\\d+\\):1: ssl_session_fetch_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:\\d+\\):1: ssl_session_fetch_by_lua\\* is running!,\n/m,\n]\n--- no_error_log\n[error]\n[alert]\n[emerg]\n\n\n\n=== TEST 17: 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\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\n\n\n=== TEST 18: 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\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\n\n\n=== TEST 19: 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\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 20: 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\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"
  },
  {
    "path": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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    $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, when connecting to 127.0.0.2:12345\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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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 'no_plan';\n\n#worker_connections(1014);\n#master_on();\n#workers(2);\n#log_level('warn');\n\nrepeat_each(1);\n\n#plan tests => repeat_each() * (blocks() * 3 + 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": "modules/ngx_http_lua_module/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--- 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": "modules/ngx_http_lua_module/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--- 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--- 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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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}\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": "modules/ngx_http_lua_module/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    } 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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/t/162-fake-merge.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() * 3);\n\n#no_diff();\nno_long_string();\nrun_tests();\n\n__DATA__\n\n=== TEST 1: get fake_var \n--- http_config\n    init_worker_by_lua '\n        local a = 1\n    ';\n--- config\n    location /t {\n        content_by_lua '\n            ngx.say(\"fake_var = \", ngx.var.fake_var)\n        ';\n    }\n--- request\n    GET /t\n--- response_body\nfake_var = 1\n--- no_error_log\n[error]\n"
  },
  {
    "path": "modules/ngx_http_lua_module/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.bing.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.bing.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.bing.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: 57 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": "modules/ngx_http_lua_module/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--- error_code: 301\n--- response_headers_like\nLocation: http:\\/\\/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--- error_code: 301\n--- response_headers_like\nLocation: http:\\/\\/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--- error_code: 301\n--- response_headers_like\nLocation: http:\\/\\/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--- error_code: 301\n--- response_headers_like\nLocation: http://localhost:\\d+\\/t\\/%E4%B8%AD%E6%96%87\\/\\?q=name\n--- response_body_like\n.*301 Moved Permanently.*\n"
  },
  {
    "path": "modules/ngx_http_lua_module/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    } 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 12345;\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\", 12345)\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": "modules/ngx_http_lua_module/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}\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\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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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} else {\n    plan tests => repeat_each() * (blocks() * 6 + 6);\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        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 127.0.0.2:8080 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(\"127.0.0.2\", 8080)\n                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:8080 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(\"127.0.0.2\", 8080)\n                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'lua_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 127.0.0.2:8080 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(\"127.0.0.2\", 8080)\n                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:8080 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(\"127.0.0.2\", 8080)\n                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'lua_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:8080 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\", 8080)\n                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'lua_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 127.0.0.2:8080 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(\"127.0.0.2\", 8080)\n                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'lua_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_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 {\n        listen unix:$TEST_NGINX_HTML_DIR/nginx.sock;\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:12345 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\", 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                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"
  },
  {
    "path": "modules/ngx_http_lua_module/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--- 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--- 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.re.match\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, a, b = ngx.run_worker_thread(\"testpool\", \"hello\", \"hello\")\n        ngx.say(ok, \" : \", a, \" : \", b)\n    }\n}\n--- user_files\n>>> hello.lua\nlocal function hello()\n  local m, err = ngx.re.match(\"hello, 1234\", \"([0-9])[0-9]+\")\n  return m[0], m[1]\nend\nreturn {hello=hello}\n--- request\nGET /hello\n--- response_body\ntrue : 1234 : 1\n\n\n\n=== TEST 25: ngx.re.find\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, a = ngx.run_worker_thread(\"testpool\", \"hello\", \"hello\")\n        ngx.say(ok, \" : \", a)\n    }\n}\n--- user_files\n>>> hello.lua\nlocal function hello()\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        return string.sub(str, from, to)\n    end\nend\nreturn {hello=hello}\n--- request\nGET /hello\n--- response_body\ntrue : 234\n\n\n\n=== TEST 26: ngx.re.gmatch\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)\n        ngx.say(ret[1])\n        ngx.say(ret[2])\n    }\n}\n--- user_files\n>>> hello.lua\nlocal function hello()\n    local ret = {}\n    for m in ngx.re.gmatch(\"hello, world\", \"[a-z]+\", \"j\") do\n        if m then\n            table.insert(ret, m[0])\n        end\n    end\n    return ret\nend\nreturn {hello=hello}\n--- request\nGET /hello\n--- response_body\ntrue\nhello\nworld\n\n\n\n=== TEST 27: ngx.re.sub\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, a, b = ngx.run_worker_thread(\"testpool\", \"hello\", \"hello\")\n        ngx.say(ok)\n        ngx.say(a)\n        ngx.say(b)\n    }\n}\n--- user_files\n>>> hello.lua\nlocal function hello()\n    local newstr, n = ngx.re.sub(\"hello, 1234\", \"[0-9]\", \"$$\")\n    return newstr, n\nend\nreturn {hello=hello}\n--- request\nGET /hello\n--- response_body\ntrue\nhello, $234\n1\n\n\n\n=== TEST 28: ngx.re.gsub\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, a, b = ngx.run_worker_thread(\"testpool\", \"hello\", \"hello\")\n        ngx.say(ok)\n        ngx.say(a)\n        ngx.say(b)\n    }\n}\n--- user_files\n>>> hello.lua\nlocal function hello()\n    local newstr, n, err = ngx.re.gsub(\"hello, world\", \"([a-z])[a-z]+\", \"[$0,$1]\", \"i\")\n    return newstr, n\nend\nreturn {hello=hello}\n--- request\nGET /hello\n--- response_body\ntrue\n[hello,h], [world,w]\n2\n\n\n\n=== TEST 29: 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 30: 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 31: 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 32: 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 33: 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 34: 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 35: 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 36: 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 37: 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 38: 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 39: 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 40: 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 41: 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 42: 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 43: shdict get_stale\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 44: 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 45: 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 46: 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 47: 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 48: 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 49: 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 50: 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 51: 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 52: 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 53: 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 54: 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"
  },
  {
    "path": "modules/ngx_http_lua_module/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: Location: /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\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\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": "modules/ngx_http_lua_module/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"
  },
  {
    "path": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/t/186-cosocket-busy-bufs.t",
    "content": "# vim:set ft= ts=4 sw=4 et fdm=marker:\n\nuse Test::Nginx::Socket 'no_plan';\nuse Test::Nginx::Socket::Lua::Stream;\n\nlog_level('warn');\nrepeat_each(2);\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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/t/cert/mtls_cert_gen/.gitignore",
    "content": "*.pem\n*.csr\ncfssl\ncfssljson\n"
  },
  {
    "path": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/t/cert/test.crl",
    "content": "-----BEGIN X509 CRL-----\nMIIBjzCB+QIBATANBgkqhkiG9w0BAQUFADCBlzELMAkGA1UEBhMCVVMxEzARBgNV\nBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xEjAQBgNVBAoM\nCU9wZW5SZXN0eTESMBAGA1UECwwJT3BlblJlc3R5MREwDwYDVQQDDAh0ZXN0LmNv\nbTEgMB4GCSqGSIb3DQEJARYRYWdlbnR6aEBnbWFpbC5jb20XDTE0MDcyMTIxNDEy\nMloXDTE0MDgyMDIxNDEyMlowHDAaAgkApQ5tVpK3luIXDTE0MDcyMTIxNDEwMlqg\nDzANMAsGA1UdFAQEAgIQATANBgkqhkiG9w0BAQUFAAOBgQBDZ6UY0Qg7qDoLrXXl\ngJElFilZ7LiKPqjE3+Rfx7XkgdbPxjGCr77TfMm+smdvawk7WHv1AOvRH7kGrgGT\nkGJZwqJ4vKa/NpEWJIMAZ1Gq9BIH/Ig6ffmPk+S9ozcVHKJDW7x4nMuotyj1hILN\nEePv78DZCYMZgf8WwMElNgz6Hw==\n-----END X509 CRL-----\n"
  },
  {
    "path": "modules/ngx_http_lua_module/t/cert/test.crt",
    "content": "-----BEGIN CERTIFICATE-----\nMIICqTCCAhICCQClDm1WkreW4jANBgkqhkiG9w0BAQUFADCBlzELMAkGA1UEBhMC\nVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28x\nEjAQBgNVBAoMCU9wZW5SZXN0eTESMBAGA1UECwwJT3BlblJlc3R5MREwDwYDVQQD\nDAh0ZXN0LmNvbTEgMB4GCSqGSIb3DQEJARYRYWdlbnR6aEBnbWFpbC5jb20wIBcN\nMTQwNzIxMDMyMzQ3WhgPMjE1MTA2MTMwMzIzNDdaMIGXMQswCQYDVQQGEwJVUzET\nMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzESMBAG\nA1UECgwJT3BlblJlc3R5MRIwEAYDVQQLDAlPcGVuUmVzdHkxETAPBgNVBAMMCHRl\nc3QuY29tMSAwHgYJKoZIhvcNAQkBFhFhZ2VudHpoQGdtYWlsLmNvbTCBnzANBgkq\nhkiG9w0BAQEFAAOBjQAwgYkCgYEA6P18zUvtmaKQK2xePy8ZbFwSyTLw+jW6t9eZ\naiTec8X3ibN9WemrxHzkTRikxP3cAQoITRuZiQvF4Q7DO6wMkz/b0zwfgX5uedGq\n047AJP6n/mwlDOjGSNomBLoXQzo7tVe60ikEm3ZyDUqnJPJMt3hImO5XSop4MPMu\nZa9WhFcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQA4OBb9bOyWB1//93nSXX1mdENZ\nIQeyTK0Dd6My76lnZxnZ4hTWrvvd0b17KLDU6JnS2N5ee3ATVkojPidRLWLIhnh5\n0eXrcKalbO2Ce6nShoFvQCQKXN2Txmq2vO/Mud2bHAWwJALg+qi1Iih/gVYB9sct\nFLg8zFOzRlYiU+6Mmw==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "modules/ngx_http_lua_module/t/cert/test.key",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIICXgIBAAKBgQDo/XzNS+2ZopArbF4/LxlsXBLJMvD6Nbq315lqJN5zxfeJs31Z\n6avEfORNGKTE/dwBCghNG5mJC8XhDsM7rAyTP9vTPB+Bfm550arTjsAk/qf+bCUM\n6MZI2iYEuhdDOju1V7rSKQSbdnINSqck8ky3eEiY7ldKingw8y5lr1aEVwIDAQAB\nAoGBANgB66sKMga2SKN5nQdHS3LDCkevCutu1OWM5ZcbB4Kej5kC57xsf+tzPtab\nemeIVGhCPOAALqB4YcT+QtMX967oM1MjcFbtH7si5oq6UYyp3i0G9Si6jIoVHz3+\n8yOUaqwKbK+bRX8VS0YsHZmBsPK5ryN50iUwsU08nemoA94BAkEA9GS9Q5OPeFkM\ntFxsIQ1f2FSsZAuN/1cpZgJqY+YaAN7MSPGTWyfd7nWG/Zgk3GO9/2ihh4gww+7B\nTo09GkmW4QJBAPQOHC2V+t2TA98+6Lj6+TYwcGEkhOENfVpH25mQ+kXgF/1Bd6rA\nnosT1bdAY+SnmWXbSw6Kv5C20Em+bEX8WjcCQCSRRjhsRdVODbaW9Z7kb2jhEoJN\nsEt6cTlQNzcHYPCsZYisjM3g4zYg47fiIfHQAsfKkhDDcfh/KvFj9LaQOEECQQCH\neBWYEDpSJ7rsfqT7mQQgWj7nDThdG/nK1TxGP71McBmg0Gg2dfkLRhVJRQqt74Is\nkc9V4Rp4n6F6baL4Lh19AkEA6pZZer0kg3Kv9hjhaITIKUYdfIp9vYnDRWbQlBmR\natV8V9u9q2ETZvqfHpN+9Lu6NYR4yXIEIRf1bnIZ/mr9eQ==\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "modules/ngx_http_lua_module/t/cert/test2.crt",
    "content": "-----BEGIN CERTIFICATE-----\nMIIChzCCAfACCQDjCkJpJUtZmjANBgkqhkiG9w0BAQUFADCBhjELMAkGA1UEBhMC\nVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28x\nEjAQBgNVBAoMCU9wZW5SZXN0eTESMBAGA1UEAwwJdGVzdDIuY29tMSIwIAYJKoZI\nhvcNAQkBFhNvcGVucmVzdHlAZ21haWwuY29tMCAXDTE0MDkxMzAwMTgxMFoYDzIx\nMTQwODIwMDAxODEwWjCBhjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3Ju\naWExFjAUBgNVBAcMDVNhbiBGcmFuY2lzY28xEjAQBgNVBAoMCU9wZW5SZXN0eTES\nMBAGA1UEAwwJdGVzdDIuY29tMSIwIAYJKoZIhvcNAQkBFhNvcGVucmVzdHlAZ21h\naWwuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDy+OVI2u5NBOeB2Cyz\nGnwy9b7Ao4CSi05XtUxh2IoVdzYZz6c4PFb9C1ad52LDdRStiQT5A7+RKLj6Kr7f\nJrKFziJxMy4g4Kdn9G659vE7CWu/UAVjRUtc+mTBAEfjdbumizmHLG7DmnNhGl3R\nNGiVNLsUInSMGfUlJRzZJXhI4QIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAEMmRvyN\nN7uE24Tc6TR19JadNHK8g3YGktRoXWiqd/y0HY4NRPgvnK/nX7CY/wXa1j+uDO8K\ne6/Ldm5RZrjtvfHJmTSAu8zkqTJz8bqRDH7kzL5Ni2Ky2x8r9dtB0ImpOiSlwvZN\nsnMvbrxEdwBiqlC9prV2f9aG+ACo1KnPL0j6\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "modules/ngx_http_lua_module/t/cert/test2.key",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nMIICXAIBAAKBgQDy+OVI2u5NBOeB2CyzGnwy9b7Ao4CSi05XtUxh2IoVdzYZz6c4\nPFb9C1ad52LDdRStiQT5A7+RKLj6Kr7fJrKFziJxMy4g4Kdn9G659vE7CWu/UAVj\nRUtc+mTBAEfjdbumizmHLG7DmnNhGl3RNGiVNLsUInSMGfUlJRzZJXhI4QIDAQAB\nAoGAEqBB83PVENJvbOTFiHVfUAjGtr3R/Wnwd4jOcjHHZB3fZ9sjVoxJntxfp3s1\ndwZir2rxlqVS6i3VAFiGiVTOGo2Vvzhw2J7f58twCECmnLb2f863AkGEYe4dAndD\nGHGD0WI0CBMD1sT18YCj561o0Wol5deWH0gM9pr2N3HkeIECQQD6hUKFlFhrpaHP\nWNJsl6BxgE6pB5kxLcMcpIQ7P+kHUvtyvCJl5QZJqPrpPGjRsAI5Ph92rpsp/zDp\n/IZNWGVjAkEA+Ele31Rt+XbV32MrLKZgBDBk+Pzss5LTn9fZ5v1k/7hrMk2VVWvk\nAD6n5QiGe/g59woANpPb1T9l956SBf0d6wJABTXOS17pc9uvANP1FGMW6CVl/Wf2\nDKrJ+weE5IKQwyE7r4gwIvRfbBrClSU3fNzvPueG2f4JphbzmnoxBNzIxwJAYivY\nmGNwzHehXx99/byXMHDWK+EN0n8WsBgP75Z3rekEcbJdfpYXY8Via1vwmOnwOW65\n4NqbzHix37PSNw37GwJBALxaGNpREO2Tk+oWOvsD2QyviMVae3mXAJHc6nLVdKDM\nq0YvDT6VdeNYYFTkAuzJacsVXOpn6AnUMFj0OBedMhc=\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/t/cert/test_ecdsa.key",
    "content": "-----BEGIN PRIVATE KEY-----\nMIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg0vwBPGgv1hE6RnQo\n3imyoceR+5dLsKegodOlBwnWtbuhRANCAAT/OtGmlIlbtvvJ3OP0dm5lyEMCrMnp\nDTDjwBPnUZ2f+16LCmNsdtEJ0r0Sd4GMo4Lss2JpwzPy2SLGEj3KwGKS\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "modules/ngx_http_lua_module/t/cert/test_passphrase.crt",
    "content": "-----BEGIN CERTIFICATE-----\nMIICozCCAgwCCQDEutRdSs3vZjANBgkqhkiG9w0BAQUFADCBlDELMAkGA1UEBhMC\nQ04xEjAQBgNVBAgMCUd1YW5nZG9uZzERMA8GA1UEBwwIU2hlblpoZW4xEjAQBgNV\nBAoMCU9wZW5SZXN0eTESMBAGA1UECwwJT3BlblJlc3R5MREwDwYDVQQDDAh0ZXN0\nLmNvbTEjMCEGCSqGSIb3DQEJARYUZ3VhbmdsaW5sdkBnbWFpbC5jb20wIBcNMTYw\nNDI4MTQ0MzI4WhgPMjE1MTAzMjcxNDQzMjhaMIGUMQswCQYDVQQGEwJDTjESMBAG\nA1UECAwJR3Vhbmdkb25nMREwDwYDVQQHDAhTaGVuWmhlbjESMBAGA1UECgwJT3Bl\nblJlc3R5MRIwEAYDVQQLDAlPcGVuUmVzdHkxETAPBgNVBAMMCHRlc3QuY29tMSMw\nIQYJKoZIhvcNAQkBFhRndWFuZ2xpbmx2QGdtYWlsLmNvbTCBnzANBgkqhkiG9w0B\nAQEFAAOBjQAwgYkCgYEA2KZ+HdH9R2tarxD8PKqu5EYq2BNGlFRg1xJmrw0XZBRM\nUP/VPb+sIeioooz36uhiXfQjExlpBCA/0zNAN+HbFyqpPPTf1qLGrj/dqeE4MJaN\nBwzxiv3fZnENT65u2qbiFWIY+ATNHgA20d50nxNNjPTzLbkx/nYXL92r4kuAGk0C\nAwEAATANBgkqhkiG9w0BAQUFAAOBgQCfMo0qbcs3kwl1tcNBO5hCcUUJRzyv041V\nff/nZ/JPIMo/LSZd12K82G/dLRN7uRT9nzqtm+JRkHALHWWWFKi6bdg1vcdOTWqC\n08bCkJHQoXJQQLvvA6gNvnR+0b7L4CrCmrcyYgKDLXVGNP9Wv/PqSWWbxsmqngkA\nMvy6CVytFw==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "modules/ngx_http_lua_module/t/cert/test_passphrase.key",
    "content": "-----BEGIN RSA PRIVATE KEY-----\nProc-Type: 4,ENCRYPTED\nDEK-Info: DES-EDE3-CBC,679ACC8E69ACAA92\n\nSsrjp3VU4somCNPiXkWqcudDnvnwbyj/Q0pS07at3lXKbhQSgI1Tzhg9Pm3BXXj5\nmkLdeGG5ocrj1Q9dhtmZgZeHHQIiynZBhjBu1Y+HPef8jXOWLrCOi8EKiWkJ2qG3\nV1KFM/95CcDt0mRLykUXEL3IpUst05SFb9XwiLokB7ypeu3NhgNUHjL6G+ubB4ri\nTOUjCW4pEoNHjdC22IiqSncwCVhluYSGhr6ktHKehZMhYIXmL1wmSLdhTlsPXCQl\nxvYILQ2vJcKIR1BkeYYPD/OQC6zCZlXIErzfgeZiz2+NTudKYpb9VmsQKsO+R8L7\ntZ/fNaR0vk8bbimMHgStAV4acVsC/7WxsqOjMJ8VTq1iqhYPl6N7kRdR3H3kSSOm\ncN9T3SrOHDVaHbnWgToaOE4mKFjvFSLIOcWgus0iOHWXmY+SLG+Ndag3oVB6R9oB\ncAHX19mq99+GhzA8IV4I0En2UCKQhnGPvkM+9mcCDxhRETlwncDjlMGOHpQ65J9r\neReVPIpnDkvHxPGTtsR3ZHTdWTZb+C0W2N3QIlJKrOzxFmfoj++yG3tMX42aDY0g\nDVkrXgcKobiWN0AVrJNAwfG7uObKSCFYgz/0RRMCO4cjXRW99nxdjVDZhyc6R0Te\njzuF04okkOLNb25n2hP+yIULrn+6Nv/uHtFI0j0n3hOzcKh//dNbACSAKgkHni9g\nJKDFJXgLJxf+Wc3So0DF9gYMKJJ+WbcdVT9gkC7RyQHlC90Pn7kNXzHr0ZawUsNI\nZxSkL4dMhYAfA4lUBJbOkwbSurv97LinOSRffpM0Nmf7VNw/Ue15eg==\n-----END RSA PRIVATE KEY-----\n"
  },
  {
    "path": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/t/data/fake-merge-module/config",
    "content": "ngx_addon_name=ngx_http_fake_merge_module\nHTTP_MODULES=\"$HTTP_MODULES ngx_http_fake_merge_module\"\nNGX_ADDON_SRCS=\"$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_fake_merge_module.c\"\n"
  },
  {
    "path": "modules/ngx_http_lua_module/t/data/fake-merge-module/ngx_http_fake_merge_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_flag_t a;\n} ngx_http_fake_merge_main_conf_t;\n\n\ntypedef struct {\n    ngx_flag_t a;\n} ngx_http_fake_merge_srv_conf_t;\n\n\ntypedef struct {\n    ngx_flag_t a;\n} ngx_http_fake_merge_loc_conf_t;\n\n\nstatic ngx_int_t ngx_http_fake_merge_add_variable(ngx_conf_t *cf);\nstatic ngx_int_t ngx_http_fake_merge_var(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_fake_merge_init(ngx_conf_t *cf);\nstatic void *ngx_http_fake_merge_create_main_conf(ngx_conf_t *cf);\nstatic void *ngx_http_fake_merge_create_loc_conf(ngx_conf_t *cf);\nstatic char *ngx_http_fake_merge_merge_loc_conf(ngx_conf_t *cf, void *prev,\n    void *conf);\n\n\n/* flow identify module configure struct */\nstatic ngx_http_module_t  ngx_http_fake_merge_module_ctx = {\n    ngx_http_fake_merge_init,             /* preconfiguration */\n    NULL,                                 /* postconfiguration */\n\n    ngx_http_fake_merge_create_main_conf, /* create main configuration */\n    NULL,                                 /* init main configuration */\n\n    NULL,                                 /* create server configuration */\n    NULL,                                 /* merge server configuration */\n\n    ngx_http_fake_merge_create_loc_conf,  /* create location configuration */\n    ngx_http_fake_merge_merge_loc_conf    /* merge location configuration */\n};\n\n\n/* flow identify module struct */\nngx_module_t  ngx_http_fake_merge_module = {\n    NGX_MODULE_V1,\n    &ngx_http_fake_merge_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_http_variable_t  ngx_http_fake_merge_variables[] = {\n\n    { ngx_string(\"fake_var\"), NULL,\n      ngx_http_fake_merge_var, 0,\n      NGX_HTTP_VAR_NOCACHEABLE, 0 },\n\n    { ngx_null_string, NULL, NULL, 0, 0, 0 }\n};\n\n\nstatic ngx_int_t\nngx_http_fake_merge_var(ngx_http_request_t *r, ngx_http_variable_value_t *v,\n    uintptr_t data)\n{\n    ngx_http_fake_merge_main_conf_t * fmcf;\n    static char *str[] = {\"0\", \"1\"};\n\n    fmcf = ngx_http_get_module_main_conf(r, ngx_http_fake_merge_module);\n    if (fmcf == NULL) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"get module main conf failed if fake_var\");\n        return NGX_ERROR;\n    }\n\n    v->len = 1;\n    v->data = (u_char *) str[fmcf->a];\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t \nngx_http_fake_merge_add_variable(ngx_conf_t *cf)\n{\n    ngx_http_variable_t  *var, *v;\n\n    for (v = ngx_http_fake_merge_variables; v->name.len; v++) {\n        var = ngx_http_add_variable(cf, &v->name, v->flags);\n        if (var == NULL) {\n            return NGX_ERROR;\n        }\n        \n        var->get_handler = v->get_handler;\n        var->data = v->data;\n        v->index = ngx_http_get_variable_index(cf, &v->name);\n        if (v->index == (ngx_uint_t) NGX_ERROR) {\n            return NGX_ERROR;\n        }\n    }\n\n    return NGX_OK;\n\n}\n\n\n/* postconfiguration init */\nstatic ngx_int_t ngx_http_fake_merge_init(ngx_conf_t *cf)\n{\n    ngx_http_fake_merge_loc_conf_t   *flcf;\n\n    flcf = ngx_http_conf_get_module_loc_conf(cf,\n                                              ngx_http_fake_merge_module);\n    if (flcf == NULL) {\n        return NGX_ERROR;\n    }\n\n    flcf->a = 1;\n \n    if (ngx_http_fake_merge_add_variable(cf) != NGX_OK) {\n        return NGX_ERROR;\n    }\n    \n    return NGX_OK;\n}\n\n\n/* create main configure */\nstatic void *ngx_http_fake_merge_create_main_conf(ngx_conf_t *cf)\n{\n    ngx_http_fake_merge_main_conf_t   *fmcf;\n\n    fmcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_fake_merge_main_conf_t));\n    if (fmcf == NULL) {\n        ngx_conf_log_error(NGX_LOG_ALERT, cf, 0, \"create module main conf\");\n        return NULL;\n    }\n\n    return fmcf;\n}\n\n\n/* create location configure */\nstatic void *ngx_http_fake_merge_create_loc_conf(ngx_conf_t *cf)\n{\n    ngx_http_fake_merge_loc_conf_t   *flcf;\n\n    flcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_fake_merge_loc_conf_t));\n    if (flcf == NULL) {\n        return NULL;\n    }\n\n    flcf->a = NGX_CONF_UNSET;\n\n    return flcf;\n}\n\n\n/* merge location configure */\nstatic char *\nngx_http_fake_merge_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_http_fake_merge_loc_conf_t    *conf = child;\n    ngx_http_fake_merge_loc_conf_t    *prev = parent;\n    ngx_http_fake_merge_main_conf_t   *fmcf;\n\n    ngx_conf_merge_value(conf->a, prev->a, 0);\n\n    fmcf = ngx_http_conf_get_module_main_conf(cf,\n                                              ngx_http_fake_merge_module);\n    if (fmcf == NULL) {\n        ngx_conf_log_error(NGX_LOG_ALERT, cf, 0,\n                           \"get module main conf failed in merge loc conf\");\n        return NGX_CONF_ERROR;\n    }\n   \n\n    fmcf->a = conf->a;\n\n    return NGX_CONF_OK;\n}\n"
  },
  {
    "path": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/tapset/ngx_lua.stp",
    "content": "function ngx_http_lua_ctx_context(r)\n{\n\n}\n\n"
  },
  {
    "path": "modules/ngx_http_lua_module/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$PCRE_INC -I$OPENSSL_INC -DDDEBUG=1\" \\\n            --with-http_v2_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$PCRE_LIB -L$OPENSSL_LIB -Wl,-rpath,$PCRE_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": "modules/ngx_http_lua_module/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\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\ntime ngx-build $force $version \\\n            --with-threads \\\n            --with-pcre-jit \\\n            --with-ipv6 \\\n            --with-cc-opt=\"-DNGX_LUA_USE_ASSERT -I$PCRE_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$PCRE_LIB -Wl,-rpath,$PCRE_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": "modules/ngx_http_lua_module/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\ntime ngx-build $force $version \\\n            --with-threads \\\n            --with-pcre-jit \\\n            --with-ipv6 \\\n            --with-cc-opt=\"-DNGX_LUA_USE_ASSERT -I$PCRE_INC -I$OPENSSL_INC\" \\\n            --with-http_v2_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$PCRE_LIB -L$OPENSSL_LIB -Wl,-rpath,$PCRE_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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/util/releng",
    "content": "#!/bin/bash\n\n./update-readme\nack '(?<=\\#define)\\s*DDEBUG\\s*1' src\necho ====================================================\nack '(?<=_version_string) \"\\d+\\.\\d+\\.\\d+\"' src\nack '(?<=This document describes rds-json-nginx-module v)\\d+\\.\\d+' README\n\n"
  },
  {
    "path": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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": "modules/ngx_http_lua_module/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"
  },
  {
    "path": "modules/ngx_http_proxy_connect_module/config",
    "content": "ngx_addon_name=ngx_http_proxy_connect_module\n\nif test -n \"$ngx_module_link\"; then\n    ngx_module_type=HTTP\n    ngx_module_name=ngx_http_proxy_connect_module\n    ngx_module_srcs=\"$ngx_addon_dir/ngx_http_proxy_connect_module.c\"\n\n    . auto/module\nelse\n    HTTP_MODULES=\"$HTTP_MODULES ngx_http_proxy_connect_module\"\n    NGX_ADDON_SRCS=\"$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_proxy_connect_module.c\"\nfi\n\nhave=NGX_HTTP_PROXY_CONNECT . auto/have\n"
  },
  {
    "path": "modules/ngx_http_proxy_connect_module/ngx_http_proxy_connect_module.c",
    "content": "/*\n * Copyright (C) 2010-2013 Alibaba Group Holding Limited\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#define NGX_HTTP_PROXY_CONNECT_ESTABLISTHED     \\\n    \"HTTP/1.1 200 Connection Established\\r\\n\"   \\\n    \"Proxy-agent: nginx\\r\\n\\r\\n\"\n\n\ntypedef struct ngx_http_proxy_connect_upstream_s\n    ngx_http_proxy_connect_upstream_t;\ntypedef struct ngx_http_proxy_connect_address_s\n    ngx_http_proxy_connect_address_t;\n\ntypedef void (*ngx_http_proxy_connect_upstream_handler_pt)(\n    ngx_http_request_t *r, ngx_http_proxy_connect_upstream_t *u);\n\n\ntypedef struct {\n    ngx_flag_t                           accept_connect;\n    ngx_flag_t                           allow_port_all;\n    ngx_array_t                         *allow_ports;\n\n    ngx_msec_t                           data_timeout;\n    ngx_msec_t                           send_timeout;\n    ngx_msec_t                           connect_timeout;\n\n    size_t                               send_lowat;\n    size_t                               buffer_size;\n\n    ngx_http_complex_value_t            *address;\n    ngx_http_proxy_connect_address_t    *local;\n\n    ngx_http_complex_value_t            *response;\n} ngx_http_proxy_connect_loc_conf_t;\n\n\ntypedef struct {\n    ngx_msec_t                       resolve_time;\n    ngx_msec_t                       connect_time;\n    ngx_msec_t                       first_byte_time;\n\n    /* TODO:\n    off_t                            bytes_received;\n    off_t                            bytes_sent;\n    */\n} ngx_http_proxy_connect_upstream_state_t;\n\n\nstruct ngx_http_proxy_connect_upstream_s {\n    ngx_http_proxy_connect_loc_conf_t             *conf;\n\n    ngx_http_proxy_connect_upstream_handler_pt     read_event_handler;\n    ngx_http_proxy_connect_upstream_handler_pt     write_event_handler;\n\n    ngx_peer_connection_t                          peer;\n\n    ngx_http_request_t                            *request;\n\n    ngx_http_upstream_resolved_t                  *resolved;\n\n    ngx_buf_t                                      from_client;\n\n    ngx_output_chain_ctx_t                         output;\n\n    ngx_buf_t                                      buffer;\n\n    /* 1: DNS resolving succeeded */\n    ngx_flag_t                                     _resolved;\n\n    /* 1: connection established */\n    ngx_flag_t                                     connected;\n\n    ngx_msec_t                                     start_time;\n\n    ngx_http_proxy_connect_upstream_state_t        state;\n};\n\nstruct ngx_http_proxy_connect_address_s {\n    ngx_addr_t                      *addr;\n    ngx_http_complex_value_t        *value;\n#if (NGX_HAVE_TRANSPARENT_PROXY)\n    ngx_uint_t                       transparent; /* unsigned  transparent:1; */\n#endif\n};\n\ntypedef struct {\n    ngx_http_proxy_connect_upstream_t           *u;\n\n    ngx_flag_t                      send_established;\n    ngx_flag_t                      send_established_done;\n\n    ngx_buf_t                       buf;    /* CONNECT response */\n\n    ngx_msec_t                      connect_timeout;\n    ngx_msec_t                      send_timeout;\n    ngx_msec_t                      data_timeout;\n\n} ngx_http_proxy_connect_ctx_t;\n\n\nstatic ngx_int_t ngx_http_proxy_connect_init(ngx_conf_t *cf);\nstatic ngx_int_t ngx_http_proxy_connect_add_variables(ngx_conf_t *cf);\nstatic ngx_int_t ngx_http_proxy_connect_connect_addr_variable(\n    ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data);\nstatic char *ngx_http_proxy_connect(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_http_proxy_connect_allow(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic void *ngx_http_proxy_connect_create_loc_conf(ngx_conf_t *cf);\nstatic char *ngx_http_proxy_connect_merge_loc_conf(ngx_conf_t *cf, void *parent,\n    void *child);\nstatic void ngx_http_proxy_connect_write_downstream(ngx_http_request_t *r);\nstatic void ngx_http_proxy_connect_read_downstream(ngx_http_request_t *r);\nstatic void ngx_http_proxy_connect_send_handler(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_proxy_connect_allow_handler(ngx_http_request_t *r,\n    ngx_http_proxy_connect_loc_conf_t *plcf);\nstatic char* ngx_http_proxy_connect_bind(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic ngx_int_t ngx_http_proxy_connect_set_local(ngx_http_request_t *r,\n  ngx_http_proxy_connect_upstream_t *u, ngx_http_proxy_connect_address_t *local);\nstatic ngx_int_t ngx_http_proxy_connect_variable_get_time(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic void ngx_http_proxy_connect_variable_set_time(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_proxy_connect_resolve_time_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_proxy_connect_connect_time_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_proxy_connect_first_byte_time_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_proxy_connect_variable_get_response(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic void ngx_http_proxy_connect_variable_set_response(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\n\nstatic ngx_int_t ngx_http_proxy_connect_sock_ntop(ngx_http_request_t *r,\n    ngx_http_proxy_connect_upstream_t *u);\nstatic ngx_int_t ngx_http_proxy_connect_create_peer(ngx_http_request_t *r,\n    ngx_http_upstream_resolved_t *ur);\n\n\n\nstatic ngx_command_t  ngx_http_proxy_connect_commands[] = {\n\n    { ngx_string(\"proxy_connect\"),\n      NGX_HTTP_SRV_CONF|NGX_CONF_NOARGS,\n      ngx_http_proxy_connect,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_proxy_connect_loc_conf_t, accept_connect),\n      NULL },\n\n    { ngx_string(\"proxy_connect_allow\"),\n      NGX_HTTP_SRV_CONF|NGX_CONF_1MORE,\n      ngx_http_proxy_connect_allow,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"proxy_connect_data_timeout\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_proxy_connect_loc_conf_t, data_timeout),\n      NULL },\n\n    { ngx_string(\"proxy_connect_read_timeout\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_proxy_connect_loc_conf_t, data_timeout),\n      NULL },\n\n    { ngx_string(\"proxy_connect_send_timeout\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_proxy_connect_loc_conf_t, send_timeout),\n      NULL },\n\n    { ngx_string(\"proxy_connect_connect_timeout\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_proxy_connect_loc_conf_t, connect_timeout),\n      NULL },\n\n    { ngx_string(\"proxy_connect_send_lowat\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_proxy_connect_loc_conf_t, send_lowat),\n      NULL },\n\n    { ngx_string(\"proxy_connect_address\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_http_set_complex_value_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_proxy_connect_loc_conf_t, address),\n      NULL },\n\n    { ngx_string(\"proxy_connect_bind\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE12,\n      ngx_http_proxy_connect_bind,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_proxy_connect_loc_conf_t, local),\n      NULL },\n\n    { ngx_string(\"proxy_connect_response\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_http_set_complex_value_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_proxy_connect_loc_conf_t, response),\n      NULL },\n\n    ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_proxy_connect_module_ctx = {\n    ngx_http_proxy_connect_add_variables,   /* preconfiguration */\n    ngx_http_proxy_connect_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    ngx_http_proxy_connect_create_loc_conf, /* create location configuration */\n    ngx_http_proxy_connect_merge_loc_conf   /* merge location configuration */\n};\n\n\nngx_module_t  ngx_http_proxy_connect_module = {\n    NGX_MODULE_V1,\n    &ngx_http_proxy_connect_module_ctx,     /* module context */\n    ngx_http_proxy_connect_commands,        /* 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_http_variable_t  ngx_http_proxy_connect_vars[] = {\n\n    { ngx_string(\"connect_addr\"), NULL,\n      ngx_http_proxy_connect_connect_addr_variable,\n      0, NGX_HTTP_VAR_NOCACHEABLE, 0 },\n\n    { ngx_string(\"proxy_connect_connect_timeout\"),\n      ngx_http_proxy_connect_variable_set_time,\n      ngx_http_proxy_connect_variable_get_time,\n      offsetof(ngx_http_proxy_connect_ctx_t, connect_timeout),\n      NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_CHANGEABLE, 0 },\n\n    { ngx_string(\"proxy_connect_data_timeout\"),\n      ngx_http_proxy_connect_variable_set_time,\n      ngx_http_proxy_connect_variable_get_time,\n      offsetof(ngx_http_proxy_connect_ctx_t, data_timeout),\n      NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_CHANGEABLE, 0 },\n\n    { ngx_string(\"proxy_connect_read_timeout\"),\n      ngx_http_proxy_connect_variable_set_time,\n      ngx_http_proxy_connect_variable_get_time,\n      offsetof(ngx_http_proxy_connect_ctx_t, data_timeout),\n      NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_CHANGEABLE, 0 },\n\n    { ngx_string(\"proxy_connect_send_timeout\"),\n      ngx_http_proxy_connect_variable_set_time,\n      ngx_http_proxy_connect_variable_get_time,\n      offsetof(ngx_http_proxy_connect_ctx_t, send_timeout),\n      NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_CHANGEABLE, 0 },\n\n    { ngx_string(\"proxy_connect_resolve_time\"), NULL,\n      ngx_http_proxy_connect_resolve_time_variable, 0,\n      NGX_HTTP_VAR_NOCACHEABLE, 0 },\n\n    { ngx_string(\"proxy_connect_connect_time\"), NULL,\n      ngx_http_proxy_connect_connect_time_variable, 0,\n      NGX_HTTP_VAR_NOCACHEABLE, 0 },\n\n    { ngx_string(\"proxy_connect_first_byte_time\"), NULL,\n      ngx_http_proxy_connect_first_byte_time_variable, 0,\n      NGX_HTTP_VAR_NOCACHEABLE, 0 },\n\n    { ngx_string(\"proxy_connect_response\"),\n      ngx_http_proxy_connect_variable_set_response,\n      ngx_http_proxy_connect_variable_get_response,\n      offsetof(ngx_http_proxy_connect_ctx_t, buf),\n      NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_CHANGEABLE, 0 },\n\n    { ngx_null_string, NULL, NULL, 0, 0, 0 }\n};\n\n\n#if 1\n\n#if defined(nginx_version) && nginx_version >= 1005008\n#define __ngx_sock_ntop ngx_sock_ntop\n#else\n#define __ngx_sock_ntop(sa, slen, p, len, port) ngx_sock_ntop(sa, p, len, port)\n#endif\n\n/*\n * #if defined(nginx_version) && nginx_version <= 1009015\n *\n * from src/core/ngx_inet.c: ngx_inet_set_port & ngx_parse_addr_port\n *\n * redefined to __ngx_inet_set_port & __ngx_parse_addr_port to\n * avoid too many `#if nginx_version > ...` macro\n */\nstatic void\n__ngx_inet_set_port(struct sockaddr *sa, in_port_t port)\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        sin6->sin6_port = htons(port);\n        break;\n#endif\n\n#if (NGX_HAVE_UNIX_DOMAIN)\n    case AF_UNIX:\n        break;\n#endif\n\n    default: /* AF_INET */\n        sin = (struct sockaddr_in *) sa;\n        sin->sin_port = htons(port);\n        break;\n    }\n}\n\n\nstatic ngx_int_t\n__ngx_parse_addr_port(ngx_pool_t *pool, ngx_addr_t *addr, u_char *text,\n    size_t len)\n{\n    u_char     *p, *last;\n    size_t      plen;\n    ngx_int_t   rc, port;\n\n    rc = ngx_parse_addr(pool, addr, text, len);\n\n    if (rc != NGX_DECLINED) {\n        return rc;\n    }\n\n    last = text + len;\n\n#if (NGX_HAVE_INET6)\n    if (len && text[0] == '[') {\n\n        p = ngx_strlchr(text, last, ']');\n\n        if (p == NULL || p == last - 1 || *++p != ':') {\n            return NGX_DECLINED;\n        }\n\n        text++;\n        len -= 2;\n\n    } else\n#endif\n\n    {\n        p = ngx_strlchr(text, last, ':');\n\n        if (p == NULL) {\n            return NGX_DECLINED;\n        }\n    }\n\n    p++;\n    plen = last - p;\n\n    port = ngx_atoi(p, plen);\n\n    if (port < 1 || port > 65535) {\n        return NGX_DECLINED;\n    }\n\n    len -= plen + 1;\n\n    rc = ngx_parse_addr(pool, addr, text, len);\n\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    __ngx_inet_set_port(addr->sockaddr, (in_port_t) port);\n\n    return NGX_OK;\n}\n\n#endif\n\n\nstatic ngx_int_t\nngx_http_proxy_connect_get_peer(ngx_peer_connection_t *pc, void *data)\n{\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_proxy_connect_test_connect(ngx_connection_t *c)\n{\n    int        err;\n    socklen_t  len;\n\n#if (NGX_HAVE_KQUEUE)\n\n    if (ngx_event_flags & NGX_USE_KQUEUE_EVENT)  {\n        if (c->write->pending_eof || c->read->pending_eof) {\n            if (c->write->pending_eof) {\n                err = c->write->kq_errno;\n\n            } else {\n                err = c->read->kq_errno;\n            }\n\n            c->log->action = \"connecting to upstream\";\n            (void) ngx_connection_error(c, err,\n                              \"proxy_connet: upstream connect failed (kevent)\");\n            return NGX_ERROR;\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            c->log->action = \"connecting to upstream\";\n            (void) ngx_connection_error(c, err,\n                                      \"proxy_connect: upstream connect failed\");\n            return NGX_ERROR;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_proxy_connect_finalize_request(ngx_http_request_t *r,\n    ngx_http_proxy_connect_upstream_t *u, ngx_int_t rc)\n{\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"proxy_connect: finalize upstream request: %i\", rc);\n\n    r->keepalive = 0;\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 && u->peer.sockaddr) {\n        u->peer.free(&u->peer, u->peer.data, 0);\n        u->peer.sockaddr = NULL;\n    }\n\n    if (u->peer.connection) {\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"proxy_connect: close upstream connection: %d\",\n                       u->peer.connection->fd);\n\n        if (u->peer.connection->pool) {\n            ngx_destroy_pool(u->peer.connection->pool);\n        }\n\n        ngx_close_connection(u->peer.connection);\n    }\n\n    u->peer.connection = NULL;\n\n    if (rc == NGX_DECLINED) {\n        return;\n    }\n\n    r->connection->log->action = \"sending to client\";\n\n    if (rc == NGX_HTTP_REQUEST_TIME_OUT\n        || rc == NGX_HTTP_CLIENT_CLOSED_REQUEST)\n    {\n        ngx_http_finalize_request(r, rc);\n        return;\n    }\n\n    if (u->connected && rc >= NGX_HTTP_SPECIAL_RESPONSE) {\n        rc = NGX_ERROR;\n    }\n\n    ngx_http_finalize_request(r, rc);\n}\n\n\nstatic void\nngx_http_proxy_connect_send_connection_established(ngx_http_request_t *r)\n{\n    ngx_int_t                              n;\n    ngx_buf_t                             *b;\n    ngx_connection_t                      *c;\n    ngx_http_core_loc_conf_t              *clcf;\n    ngx_http_proxy_connect_upstream_t     *u;\n    ngx_http_proxy_connect_ctx_t          *ctx;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_connect_module);\n    c = r->connection;\n    u = ctx->u;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"proxy_connect: send 200 connection established\");\n\n    u->connected = 1;\n\n    if (u->state.connect_time == (ngx_msec_t) -1) {\n        u->state.connect_time = ngx_current_msec - u->start_time;\n    }\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    b = &ctx->buf;\n\n    /* modify CONNECT response via proxy_connect_response directive */\n    {\n    ngx_str_t                               resp;\n    ngx_http_proxy_connect_loc_conf_t      *plcf;\n\n    plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_connect_module);\n\n    if (plcf->response\n        && ngx_http_complex_value(r, plcf->response, &resp) == NGX_OK)\n    {\n        if (resp.len > 0) {\n            b->pos = resp.data;\n            b->last = b->pos + resp.len;\n        }\n    }\n    }\n\n    ctx->send_established = 1;\n\n    for (;;) {\n        n = c->send(c, b->pos, b->last - b->pos);\n\n        if (n >= 0) {\n\n            r->headers_out.status = 200;    /* fixed that $status is 000 */\n\n            b->pos += n;\n\n            if (b->pos == b->last) {\n                ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                              \"proxy_connect: sent 200 connection established\");\n\n                if (c->write->timer_set) {\n                    ngx_del_timer(c->write);\n                }\n\n                ctx->send_established_done = 1;\n\n                r->write_event_handler =\n                                        ngx_http_proxy_connect_write_downstream;\n                r->read_event_handler = ngx_http_proxy_connect_read_downstream;\n\n                if (ngx_handle_write_event(c->write, clcf->send_lowat)\n                    != NGX_OK)\n                {\n                    ngx_http_proxy_connect_finalize_request(r, u,\n                                                NGX_HTTP_INTERNAL_SERVER_ERROR);\n                    return;\n                }\n\n                if (r->header_in->last > r->header_in->pos || c->read->ready) {\n                    r->read_event_handler(r);\n                    return;\n                }\n\n                return;\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        ngx_http_proxy_connect_finalize_request(r, u, NGX_ERROR);\n        return;\n    }\n\n    /* n == NGX_AGAIN */\n\n    r->write_event_handler = ngx_http_proxy_connect_send_handler;\n\n    ngx_add_timer(c->write, ctx->data_timeout);\n\n    if (ngx_handle_write_event(c->write, clcf->send_lowat) != NGX_OK) {\n        ngx_http_proxy_connect_finalize_request(r, u,\n                                                NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return;\n    }\n\n    return;\n}\n\n\nstatic void\nngx_http_proxy_connect_tunnel(ngx_http_request_t *r,\n    ngx_uint_t from_upstream, ngx_uint_t do_write)\n{\n    char                               *recv_action, *send_action;\n    size_t                              size;\n    ssize_t                             n;\n    ngx_buf_t                          *b;\n    ngx_uint_t                          flags;\n    ngx_connection_t                   *c, *pc, *dst, *src;\n    ngx_http_proxy_connect_ctx_t       *ctx;\n    ngx_http_proxy_connect_upstream_t  *u;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_connect_module);\n\n    c = r->connection;\n    u = ctx->u;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"proxy_connect: tunnel fu:%ui write:%ui\",\n                   from_upstream, do_write);\n\n    pc = u->peer.connection;\n\n    if (from_upstream) {\n        src = pc;\n        dst = c;\n        b = &u->buffer;\n        recv_action = \"proxying and reading from upstream\";\n        send_action = \"proxying and sending to client\";\n\n    } else {\n        src = c;\n        dst = pc;\n        b = &u->from_client;\n\n        if (r->header_in->last > r->header_in->pos) {\n            b = r->header_in;\n            b->end = b->last;\n            do_write = 1;\n        }\n\n        if (b->start == NULL) {\n            b->start = ngx_palloc(r->pool, u->conf->buffer_size);\n            if (b->start == NULL) {\n                ngx_http_proxy_connect_finalize_request(r, u, NGX_ERROR);\n                return;\n            }\n\n            b->pos = b->start;\n            b->last = b->start;\n            b->end = b->start + u->conf->buffer_size;\n            b->temporary = 1;\n        }\n        recv_action = \"proxying and reading from client\";\n        send_action = \"proxying and sending to upstream\";\n    }\n\n    for ( ;; ) {\n\n        if (do_write) {\n\n            size = b->last - b->pos;\n\n            if (size && dst->write->ready) {\n                c->log->action = send_action;\n\n                n = dst->send(dst, b->pos, size);\n\n                if (n == NGX_AGAIN) {\n                    break;\n                }\n\n                if (n == NGX_ERROR) {\n                    ngx_http_proxy_connect_finalize_request(r, u, NGX_ERROR);\n                    return;\n                }\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                }\n            }\n        }\n\n        size = b->end - b->last;\n\n        if (size && src->read->ready) {\n\n            c->log->action = recv_action;\n\n            n = src->recv(src, b->last, size);\n\n            if (n == NGX_AGAIN || n == 0) {\n                break;\n            }\n\n            if (n > 0) {\n                do_write = 1;\n                b->last += n;\n\n                if (from_upstream) {\n                    if (u->state.first_byte_time == (ngx_msec_t) -1) {\n                        u->state.first_byte_time = ngx_current_msec\n                            - u->start_time;\n                    }\n                }\n\n                continue;\n            }\n\n            if (n == NGX_ERROR) {\n                src->read->eof = 1;\n            }\n        }\n\n        break;\n    }\n\n    c->log->action = \"proxying connection\";\n\n    /* test finalize */\n\n    if ((pc->read->eof && u->buffer.pos == u->buffer.last)\n        || (c->read->eof && u->from_client.pos == u->from_client.last)\n        || (c->read->eof && pc->read->eof))\n    {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"proxy_connect: tunnel done\");\n        ngx_http_proxy_connect_finalize_request(r, u, 0);\n        return;\n    }\n\n    flags = src->read->eof ? NGX_CLOSE_EVENT : 0;\n\n    if (ngx_handle_read_event(src->read, flags) != NGX_OK) {\n        ngx_http_proxy_connect_finalize_request(r, u, NGX_ERROR);\n        return;\n    }\n\n    if (dst) {\n        if (ngx_handle_write_event(dst->write, 0) != NGX_OK) {\n            ngx_http_proxy_connect_finalize_request(r, u, NGX_ERROR);\n            return;\n        }\n\n        if (!c->read->delayed && !pc->read->delayed) {\n            ngx_add_timer(c->write, ctx->data_timeout);\n\n        } else if (c->write->timer_set) {\n            ngx_del_timer(c->write);\n        }\n    }\n}\n\n\nstatic void\nngx_http_proxy_connect_read_downstream(ngx_http_request_t *r)\n{\n    ngx_http_proxy_connect_ctx_t       *ctx;\n\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"proxy connect read downstream\");\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_connect_module);\n\n    if (r->connection->read->timedout) {\n        r->connection->timedout = 1;\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"proxy_connect: client read timed out\");\n        ngx_http_proxy_connect_finalize_request(r, ctx->u,\n                                                NGX_HTTP_REQUEST_TIME_OUT);\n        return;\n    }\n\n    ngx_http_proxy_connect_tunnel(r, 0, 0);\n}\n\n\nstatic void\nngx_http_proxy_connect_write_downstream(ngx_http_request_t *r)\n{\n    ngx_http_proxy_connect_ctx_t       *ctx;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"proxy connect write downstream\");\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_connect_module);\n\n    if (r->connection->write->timedout) {\n        r->connection->timedout = 1;\n        ngx_connection_error(r->connection, NGX_ETIMEDOUT,\n                             \"proxy_connect: connection timed out\");\n        ngx_http_proxy_connect_finalize_request(r, ctx->u,\n                                                NGX_HTTP_REQUEST_TIME_OUT);\n        return;\n    }\n\n    ngx_http_proxy_connect_tunnel(r, 1, 1);\n}\n\n\nstatic void\nngx_http_proxy_connect_read_upstream(ngx_http_request_t *r,\n    ngx_http_proxy_connect_upstream_t *u)\n{\n    ngx_connection_t                    *c;\n    ngx_http_proxy_connect_ctx_t        *ctx;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"proxy_connect: upstream read handler\");\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_connect_module);\n\n    c = u->peer.connection;\n\n    if (c->read->timedout) {\n        ngx_log_error(NGX_LOG_ERR, c->log, 0,\n                      \"proxy_connect: upstream read timed out (peer:%V)\",\n                      u->peer.name);\n        ngx_http_proxy_connect_finalize_request(r, u, NGX_HTTP_GATEWAY_TIME_OUT);\n        return;\n    }\n\n    if (!ctx->send_established &&\n        ngx_http_proxy_connect_test_connect(c) != NGX_OK)\n    {\n        ngx_http_proxy_connect_finalize_request(r, u, NGX_HTTP_BAD_GATEWAY);\n        return;\n    }\n\n    if (u->buffer.start == NULL) {\n        u->buffer.start = ngx_palloc(r->pool, u->conf->buffer_size);\n        if (u->buffer.start == NULL) {\n            ngx_http_proxy_connect_finalize_request(r, u,\n                                               NGX_HTTP_INTERNAL_SERVER_ERROR);\n            return;\n        }\n\n        u->buffer.pos = u->buffer.start;\n        u->buffer.last = u->buffer.start;\n        u->buffer.end = u->buffer.start + u->conf->buffer_size;\n        u->buffer.temporary = 1;\n    }\n\n    if (!ctx->send_established_done) {\n        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n            ngx_http_proxy_connect_finalize_request(r, u,\n                                               NGX_HTTP_INTERNAL_SERVER_ERROR);\n            return;\n        }\n\n        return;\n    }\n\n    ngx_http_proxy_connect_tunnel(r, 1, 0);\n}\n\n\nstatic void\nngx_http_proxy_connect_write_upstream(ngx_http_request_t *r,\n    ngx_http_proxy_connect_upstream_t *u)\n{\n    ngx_connection_t  *c;\n    ngx_http_proxy_connect_ctx_t          *ctx;\n\n    c = u->peer.connection;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"proxy_connect: upstream write handler %s\",\n                   u->connected ? \"\" : \"(connect)\");\n\n    if (c->write->timedout) {\n        ngx_log_error(NGX_LOG_ERR, c->log, 0,\n                      \"proxy_connect: upstream %s timed out (peer:%V)\",\n                      u->connected ? \"write\" : \"connect\", u->peer.name);\n        ngx_http_proxy_connect_finalize_request(r, u,\n                                                NGX_HTTP_GATEWAY_TIME_OUT);\n        return;\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_connect_module);\n\n    if (c->write->timer_set) {\n        ngx_del_timer(c->write);\n    }\n\n    if (!ctx->send_established &&\n        ngx_http_proxy_connect_test_connect(c) != NGX_OK)\n    {\n        ngx_http_proxy_connect_finalize_request(r, u, NGX_HTTP_BAD_GATEWAY);\n        return;\n    }\n\n    if (!ctx->send_established) {\n        ngx_http_proxy_connect_send_connection_established(r);\n        return;\n    }\n\n    if (!ctx->send_established_done) {\n        if (ngx_handle_write_event(c->write, 0) != NGX_OK) {\n            ngx_http_proxy_connect_finalize_request(r, u,\n                                               NGX_HTTP_INTERNAL_SERVER_ERROR);\n            return;\n        }\n\n        return;\n    }\n\n    ngx_http_proxy_connect_tunnel(r, 0, 1);\n}\n\n\nstatic void\nngx_http_proxy_connect_send_handler(ngx_http_request_t *r)\n{\n    ngx_connection_t                 *c;\n    ngx_http_proxy_connect_ctx_t     *ctx;\n\n    c = r->connection;\n    ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_connect_module);\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"proxy_connect: send connection established handler\");\n\n    if (c->write->timedout) {\n        c->timedout = 1;\n        ngx_log_error(NGX_LOG_ERR, c->log, 0,\n                      \"proxy_connect: client write timed out\");\n        ngx_http_proxy_connect_finalize_request(r, ctx->u,\n                                                NGX_HTTP_REQUEST_TIME_OUT);\n        return;\n    }\n\n    if (ctx->buf.pos != ctx->buf.last) {\n        ngx_http_proxy_connect_send_connection_established(r);\n    }\n}\n\n\nstatic void\nngx_http_proxy_connect_upstream_handler(ngx_event_t *ev)\n{\n    ngx_connection_t                    *c;\n    ngx_http_request_t                  *r;\n    ngx_http_log_ctx_t                  *lctx;\n    ngx_http_proxy_connect_upstream_t   *u;\n\n    c = ev->data;\n    u = c->data;\n\n    r = u->request;\n    c = r->connection;\n\n    lctx = c->log->data;\n    lctx->current_request = r;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"proxy_connect: upstream handler: \\\"%V:%V\\\"\",\n                   &r->connect_host, &r->connect_port);\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 void\nngx_http_proxy_connect_process_connect(ngx_http_request_t *r,\n    ngx_http_proxy_connect_upstream_t *u)\n{\n    ngx_int_t                        rc;\n    ngx_connection_t                *c;\n    ngx_peer_connection_t           *pc;\n    ngx_http_upstream_resolved_t    *ur;\n    ngx_http_proxy_connect_ctx_t    *ctx;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_connect_module);\n\n    r->connection->log->action = \"connecting to upstream\";\n\n    if (ngx_http_proxy_connect_set_local(r, u, u->conf->local) != NGX_OK) {\n        ngx_http_proxy_connect_finalize_request(r, u, NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return;\n    }\n\n    pc = &u->peer;\n    ur = u->resolved;\n\n    pc->sockaddr = ur->sockaddr;\n    pc->socklen = ur->socklen;\n    pc->name = &ur->host;\n\n    pc->get = ngx_http_proxy_connect_get_peer;\n\n    u->start_time = ngx_current_msec;\n    u->state.connect_time = (ngx_msec_t) -1;\n    u->state.first_byte_time = (ngx_msec_t) -1;\n\n    rc = ngx_event_connect_peer(&u->peer);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"proxy_connect: ngx_event_connect_peer() returns %i\", rc);\n\n    /*\n     * We do not retry next upstream if current connecting fails.\n     * So there is no ngx_http_proxy_connect_upstream_next() function\n     */\n\n    if (rc == NGX_ERROR) {\n        ngx_http_proxy_connect_finalize_request(r, u,\n                                                NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return;\n    }\n\n    if (rc == NGX_BUSY) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"proxy_connect: no live connection\");\n        ngx_http_proxy_connect_finalize_request(r, u, NGX_HTTP_BAD_GATEWAY);\n        return;\n    }\n\n    if (rc == NGX_DECLINED) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"proxy_connect: connection error\");\n        ngx_http_proxy_connect_finalize_request(r, u, NGX_HTTP_BAD_GATEWAY);\n        return;\n    }\n\n    /* rc == NGX_OK || rc == NGX_AGAIN || rc == NGX_DONE */\n\n    c = pc->connection;\n\n    c->data = u;\n\n    c->write->handler = ngx_http_proxy_connect_upstream_handler;\n    c->read->handler = ngx_http_proxy_connect_upstream_handler;\n\n    u->write_event_handler = ngx_http_proxy_connect_write_upstream;\n    u->read_event_handler = ngx_http_proxy_connect_read_upstream;\n\n    c->sendfile &= r->connection->sendfile;\n    c->log = r->connection->log;\n\n    if (c->pool == NULL) {\n\n        c->pool = ngx_create_pool(128, r->connection->log);\n        if (c->pool == NULL) {\n            ngx_http_proxy_connect_finalize_request(r, u,\n                                                NGX_HTTP_INTERNAL_SERVER_ERROR);\n            return;\n        }\n    }\n\n    c->pool->log = c->log;\n    c->read->log = c->log;\n    c->write->log = c->log;\n\n    if (rc == NGX_AGAIN) {\n        ngx_add_timer(c->write, ctx->connect_timeout);\n        return;\n    }\n\n    ngx_http_proxy_connect_send_connection_established(r);\n}\n\n\nstatic void\nngx_http_proxy_connect_resolve_handler(ngx_resolver_ctx_t *ctx)\n{\n    ngx_connection_t                            *c;\n    ngx_http_request_t                          *r;\n    ngx_http_upstream_resolved_t                *ur;\n    ngx_http_proxy_connect_upstream_t           *u;\n\n#if defined(nginx_version) && nginx_version >= 1013002\n    ngx_uint_t run_posted = ctx->async;\n#endif\n\n    u = ctx->data;\n    r = u->request;\n    ur = u->resolved;\n    c = r->connection;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"proxy_connect: resolve handler\");\n\n    if (ctx->state) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"proxy_connect: %V could not be resolved (%i: %s)\",\n                      &ctx->name, ctx->state,\n                      ngx_resolver_strerror(ctx->state));\n\n        ngx_http_proxy_connect_finalize_request(r, u, NGX_HTTP_BAD_GATEWAY);\n        goto failed;\n    }\n\n    ur->naddrs = ctx->naddrs;\n    ur->addrs = ctx->addrs;\n\n#if (NGX_DEBUG)\n    {\n#   if defined(nginx_version) && nginx_version >= 1005008\n    ngx_uint_t  i;\n    ngx_str_t   addr;\n    u_char      text[NGX_SOCKADDR_STRLEN];\n\n    addr.data = text;\n\n    for (i = 0; i < ctx->naddrs; i++) {\n        addr.len = ngx_sock_ntop(ur->addrs[i].sockaddr, ur->addrs[i].socklen,\n                                 text, NGX_SOCKADDR_STRLEN, 0);\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"proxy_connect: name was resolved to %V\", &addr);\n    }\n#   else\n    ngx_uint_t  i;\n    in_addr_t   addr;\n\n    for (i = 0; i < ctx->naddrs; i++) {\n        addr = ntohl(ctx->addrs[i]);\n\n        ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"proxy_connect: name was resolved to %ud.%ud.%ud.%ud\",\n                       (addr >> 24) & 0xff, (addr >> 16) & 0xff,\n                       (addr >> 8) & 0xff, addr & 0xff);\n    }\n#   endif\n    }\n#endif\n\n    if (ngx_http_proxy_connect_create_peer(r, ur) != NGX_OK) {\n        ngx_http_proxy_connect_finalize_request(r, u,\n                                                NGX_HTTP_INTERNAL_SERVER_ERROR);\n        goto failed;\n    }\n\n    ngx_resolve_name_done(ctx);\n    ur->ctx = NULL;\n\n    u->_resolved = 1;\n\n    if (u->state.resolve_time == (ngx_msec_t) -1) {\n        u->state.resolve_time = ngx_current_msec - u->start_time;\n    }\n\n    ngx_http_proxy_connect_process_connect(r, u);\n\nfailed:\n\n#if defined(nginx_version) && nginx_version >= 1013002\n    if (run_posted) {\n        ngx_http_run_posted_requests(c);\n    }\n#else\n    ngx_http_run_posted_requests(c);\n#endif\n}\n\n\nstatic ngx_int_t\nngx_http_proxy_connect_create_peer(ngx_http_request_t *r,\n    ngx_http_upstream_resolved_t *ur)\n{\n    u_char                                      *p;\n    ngx_int_t                                    i, len;\n    socklen_t                                    socklen;\n    struct sockaddr                             *sockaddr;\n\n    i = ngx_random() % ur->naddrs;  /* i<-0 for ur->naddrs == 1 */\n\n#if defined(nginx_version) && nginx_version >= 1005008\n\n    socklen = ur->addrs[i].socklen;\n\n    sockaddr = ngx_palloc(r->pool, socklen);\n    if (sockaddr == NULL) {\n        return NGX_ERROR;\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#else\n    /* for nginx older than 1.5.8 */\n\n    socklen = sizeof(struct sockaddr_in);\n\n    sockaddr = ngx_pcalloc(r->pool, socklen);\n    if (sockaddr == NULL) {\n        return NGX_ERROR;\n    }\n\n    ((struct sockaddr_in *) sockaddr)->sin_family = AF_INET;\n    ((struct sockaddr_in *) sockaddr)->sin_addr.s_addr = ur->addrs[i];\n    ((struct sockaddr_in *) sockaddr)->sin_port = htons(ur->port);\n\n#endif\n\n    p = ngx_pnalloc(r->pool, NGX_SOCKADDR_STRLEN);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    len = __ngx_sock_ntop(sockaddr, socklen, p, NGX_SOCKADDR_STRLEN, 1);\n\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    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_proxy_connect_upstream_create(ngx_http_request_t *r,\n    ngx_http_proxy_connect_ctx_t *ctx)\n{\n    ngx_http_proxy_connect_upstream_t       *u;\n\n    u = ngx_pcalloc(r->pool, sizeof(ngx_http_proxy_connect_upstream_t));\n    if (u == NULL) {\n        return NGX_ERROR;\n    }\n\n    ctx->u = u;\n\n    u->peer.log = r->connection->log;\n    u->peer.log_error = NGX_ERROR_ERR;\n\n    u->request = r;\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_proxy_connect_check_broken_connection(ngx_http_request_t *r,\n    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    ngx_http_proxy_connect_ctx_t       *ctx;\n    ngx_http_proxy_connect_upstream_t  *u;\n\n    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, ev->log, 0,\n                   \"proxy_connect: check client, write event:%d, \\\"%V:%V\\\"\",\n                   ev->write, &r->connect_host, &r->connect_port);\n\n    c = r->connection;\n    ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_connect_module);\n    u = ctx->u;\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                ngx_http_proxy_connect_finalize_request(r, u,\n                                               NGX_HTTP_INTERNAL_SERVER_ERROR);\n                return;\n            }\n        }\n\n        ngx_http_proxy_connect_finalize_request(r, u,\n                                               NGX_HTTP_CLIENT_CLOSED_REQUEST);\n\n        return;\n    }\n\n#if (NGX_HAVE_KQUEUE)\n\n    if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {\n\n        if (!ev->pending_eof) {\n            return;\n        }\n\n        ev->eof = 1;\n        c->error = 1;\n\n        if (ev->kq_errno) {\n            ev->error = 1;\n        }\n\n        if (u->peer.connection) {\n            ngx_log_error(NGX_LOG_INFO, ev->log, ev->kq_errno,\n                          \"proxy_connect: kevent() reported that client \"\n                          \"prematurely closed connection, so upstream \"\n                          \" connection is closed too\");\n            ngx_http_proxy_connect_finalize_request(r, u,\n                                               NGX_HTTP_CLIENT_CLOSED_REQUEST);\n            return;\n        }\n\n        ngx_log_error(NGX_LOG_INFO, ev->log, ev->kq_errno,\n                      \"proxy_connect: kevent() reported that client \"\n                      \"prematurely closed connection\");\n\n        if (u->peer.connection == NULL) {\n            ngx_http_proxy_connect_finalize_request(r, u,\n                                               NGX_HTTP_CLIENT_CLOSED_REQUEST);\n        }\n\n        return;\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                   \"proxy_connect: client recv(): %d\", n);\n\n    if (ev->write && (n >= 0 || err == NGX_EAGAIN)) {\n        return;\n    }\n\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            ngx_http_proxy_connect_finalize_request(r, u,\n                                               NGX_HTTP_INTERNAL_SERVER_ERROR);\n            return;\n        }\n    }\n\n    if (n > 0) {\n        return;\n    }\n\n    if (n == -1) {\n        if (err == NGX_EAGAIN) {\n            return;\n        }\n\n        ev->error = 1;\n\n    } else { /* n == 0 */\n        err = 0;\n    }\n\n    ev->eof = 1;\n    c->error = 1;\n\n    if (u->peer.connection) {\n        ngx_log_error(NGX_LOG_INFO, ev->log, err,\n                      \"proxy_connect: client prematurely closed connection, \"\n                      \"so upstream connection is closed too\");\n        ngx_http_proxy_connect_finalize_request(r, u,\n                                           NGX_HTTP_CLIENT_CLOSED_REQUEST);\n        return;\n    }\n\n    ngx_log_error(NGX_LOG_INFO, ev->log, err,\n                  \"proxy_connect: client prematurely closed connection\");\n\n    if (u->peer.connection == NULL) {\n        ngx_http_proxy_connect_finalize_request(r, u,\n                                           NGX_HTTP_CLIENT_CLOSED_REQUEST);\n    }\n}\n\n\nstatic void\nngx_http_proxy_connect_rd_check_broken_connection(ngx_http_request_t *r)\n{\n    ngx_http_proxy_connect_check_broken_connection(r, r->connection->read);\n}\n\n\nstatic void\nngx_http_proxy_connect_wr_check_broken_connection(ngx_http_request_t *r)\n{\n    ngx_http_proxy_connect_check_broken_connection(r, r->connection->write);\n}\n\n\nstatic ngx_int_t\nngx_http_proxy_connect_handler(ngx_http_request_t *r)\n{\n    ngx_url_t                            url;\n    ngx_int_t                            rc;\n    ngx_resolver_ctx_t                  *rctx, temp;\n    ngx_http_core_loc_conf_t            *clcf;\n    ngx_http_proxy_connect_ctx_t        *ctx;\n    ngx_http_proxy_connect_upstream_t   *u;\n    ngx_http_proxy_connect_loc_conf_t   *plcf;\n\n    plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_connect_module);\n\n    if (r->method != NGX_HTTP_CONNECT || !plcf->accept_connect) {\n        return NGX_DECLINED;\n    }\n\n    rc = ngx_http_proxy_connect_allow_handler(r, plcf);\n\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_connect_module);;\n\n    if (ngx_http_proxy_connect_upstream_create(r, ctx) != NGX_OK) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    u = ctx->u;\n\n    u->conf = plcf;\n\n    ngx_memzero(&url, sizeof(ngx_url_t));\n\n    if (plcf->address) {\n        if (ngx_http_complex_value(r, plcf->address, &url.url) != NGX_OK) {\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n\n        if (url.url.len == 0 || url.url.data == NULL) {\n            url.url.len = r->connect_host.len;\n            url.url.data = r->connect_host.data;\n        }\n\n    } else {\n        url.url.len = r->connect_host.len;\n        url.url.data = r->connect_host.data;\n    }\n\n    url.default_port = r->connect_port_n;\n    url.no_resolve = 1;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"proxy_connect: connect handler: parse url: %V\" , &url.url);\n\n    if (ngx_parse_url(r->pool, &url) != NGX_OK) {\n        if (url.err) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"proxy_connect: %s in connect host \\\"%V\\\"\",\n                          url.err, &url.url);\n            return NGX_HTTP_FORBIDDEN;\n        }\n\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    r->read_event_handler = ngx_http_proxy_connect_rd_check_broken_connection;\n    r->write_event_handler = ngx_http_proxy_connect_wr_check_broken_connection;\n\n    /* NOTE:\n     *   We use only one address in u->resolved,\n     *   and u->resolved.host is \"<address:port>\" format.\n     */\n\n    u->resolved = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_resolved_t));\n    if (u->resolved == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    /* rc = NGX_DECLINED */\n\n    if (url.addrs) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"proxy_connect: upstream address given directly\");\n\n        u->resolved->sockaddr = url.addrs[0].sockaddr;\n        u->resolved->socklen = url.addrs[0].socklen;\n#if defined(nginx_version) && nginx_version >= 1011007\n        u->resolved->name = url.addrs[0].name;\n#endif\n        u->resolved->naddrs = 1;\n    }\n\n    u->resolved->host = url.host;\n    u->resolved->port = (in_port_t) (url.no_port ? r->connect_port_n : url.port);\n    u->resolved->no_port = url.no_port;\n\n    if (u->resolved->sockaddr) {\n\n        rc = ngx_http_proxy_connect_sock_ntop(r, u);\n\n        if (rc != NGX_OK) {\n            return rc;\n        }\n\n        r->main->count++;\n\n        ngx_http_proxy_connect_process_connect(r, u);\n\n        return NGX_DONE;\n    }\n\n    ngx_str_t *host = &url.host;\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n    temp.name = *host;\n\n    u->start_time = ngx_current_msec;\n    u->state.resolve_time = (ngx_msec_t) -1;\n\n    rctx = ngx_resolve_start(clcf->resolver, &temp);\n    if (rctx == NULL) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"proxy_connect: failed to start the resolver\");\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    if (rctx == NGX_NO_RESOLVER) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"proxy_connect: no resolver defined to resolve %V\",\n                      &r->connect_host);\n        return NGX_HTTP_BAD_GATEWAY;\n    }\n\n    rctx->name = *host;\n#if !defined(nginx_version) || nginx_version < 1005008\n    rctx->type = NGX_RESOLVE_A;\n#endif\n    rctx->handler = ngx_http_proxy_connect_resolve_handler;\n    rctx->data = u;\n    rctx->timeout = clcf->resolver_timeout;\n\n    u->resolved->ctx = rctx;\n\n    r->main->count++;\n\n    if (ngx_resolve_name(rctx) != NGX_OK) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"proxy_connect: fail to run resolver immediately\");\n\n        u->resolved->ctx = NULL;\n        r->main->count--;\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    return NGX_DONE;\n}\n\n\nstatic ngx_int_t\nngx_http_proxy_connect_sock_ntop(ngx_http_request_t *r,\n    ngx_http_proxy_connect_upstream_t *u)\n{\n    u_char                          *p;\n    ngx_int_t                        len;\n    ngx_http_upstream_resolved_t    *ur;\n\n    ur = u->resolved;\n\n    /* fix u->resolved->host to \"<address:port>\" format */\n\n    p = ngx_pnalloc(r->pool, NGX_SOCKADDR_STRLEN);\n    if (p == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    len = __ngx_sock_ntop(ur->sockaddr, ur->socklen, p, NGX_SOCKADDR_STRLEN, 1);\n\n    u->resolved->host.data = p;\n    u->resolved->host.len = len;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_proxy_connect_allow_handler(ngx_http_request_t *r,\n    ngx_http_proxy_connect_loc_conf_t *plcf)\n{\n    ngx_uint_t  i, allow;\n    in_port_t   (*ports)[2];\n\n    allow = 0;\n\n    if (plcf->allow_port_all) {\n        allow = 1;\n\n    } else if (plcf->allow_ports) {\n        ports = plcf->allow_ports->elts;\n\n        for (i = 0; i < plcf->allow_ports->nelts; i++) {\n            /*\n             * connect_port == port\n             * OR\n             * port <= connect_port <= eport\n             */\n            if ((ports[i][1] == 0 && r->connect_port_n == ports[i][0])\n                || (ports[i][0] <= r->connect_port_n && r->connect_port_n <= ports[i][1]))\n            {\n                allow = 1;\n                break;\n            }\n        }\n\n    } else {\n        if (r->connect_port_n == 443 || r->connect_port_n == 563) {\n            allow = 1;\n        }\n    }\n\n    if (allow == 0) {\n        return NGX_HTTP_FORBIDDEN;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic char *\nngx_http_proxy_connect_allow(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    u_char                              *p;\n    in_port_t                           *ports;\n    ngx_int_t                            port, eport;\n    ngx_uint_t                           i;\n    ngx_str_t                           *value;\n    ngx_http_proxy_connect_loc_conf_t   *plcf = conf;\n\n    if (plcf->allow_ports != NGX_CONF_UNSET_PTR) {\n        return \"is duplicate\";\n    }\n\n    plcf->allow_ports = ngx_array_create(cf->pool, 2, sizeof(in_port_t[2]));\n    if (plcf->allow_ports == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    value = cf->args->elts;\n\n    for (i = 1; i < cf->args->nelts; i++) {\n\n        if (value[i].len == 3 && ngx_strncmp(value[i].data, \"all\", 3) == 0) {\n            plcf->allow_port_all = 1;\n            continue;\n        }\n\n        p = ngx_strlchr(value[i].data, value[i].data + value[i].len, '-');\n\n        if (p != NULL) {\n            port = ngx_atoi(value[i].data, p - value[i].data);\n            p++;\n            eport = ngx_atoi(p, value[i].data + value[i].len - p);\n\n            if (port == NGX_ERROR || port < 1 || port > 65535\n                || eport == NGX_ERROR || eport < 1 || eport > 65535\n                || port > eport)\n            {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"invalid port range \\\"%V\\\" in \\\"%V\\\" directive\",\n                                   &value[i], &cmd->name);\n                return  NGX_CONF_ERROR;\n            }\n\n        } else {\n\n            port = ngx_atoi(value[i].data, value[i].len);\n\n            if (port == NGX_ERROR || port < 1 || port > 65535) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"invalid value \\\"%V\\\" in \\\"%V\\\" directive\",\n                                   &value[i], &cmd->name);\n                return  NGX_CONF_ERROR;\n            }\n\n            eport = 0;\n        }\n\n        ports = ngx_array_push(plcf->allow_ports);\n        if (ports == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        ports[0] = port;\n        ports[1] = eport;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_proxy_connect(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_core_loc_conf_t            *clcf;\n    ngx_http_proxy_connect_loc_conf_t   *pclcf;\n\n    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);\n    clcf->handler = ngx_http_proxy_connect_handler;\n\n    pclcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_proxy_connect_module);\n    pclcf->accept_connect = 1;\n\n    return NGX_CONF_OK;\n}\n\n\nchar *\nngx_http_proxy_connect_bind(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n    char  *p = conf;\n\n    ngx_int_t                           rc;\n    ngx_str_t                          *value;\n    ngx_http_complex_value_t            cv;\n    ngx_http_proxy_connect_address_t  **plocal, *local;\n    ngx_http_compile_complex_value_t    ccv;\n\n    plocal = (ngx_http_proxy_connect_address_t **) (p + cmd->offset);\n\n    if (*plocal != NGX_CONF_UNSET_PTR) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    if (cf->args->nelts == 2 && ngx_strcmp(value[1].data, \"off\") == 0) {\n        *plocal = NULL;\n        return NGX_CONF_OK;\n    }\n\n    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n\n    ccv.cf = cf;\n    ccv.value = &value[1];\n    ccv.complex_value = &cv;\n\n    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    local = ngx_pcalloc(cf->pool, sizeof(ngx_http_proxy_connect_address_t));\n    if (local == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    *plocal = local;\n\n    if (cv.lengths) {\n        local->value = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));\n        if (local->value == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        *local->value = cv;\n\n    } else {\n        local->addr = ngx_palloc(cf->pool, sizeof(ngx_addr_t));\n        if (local->addr == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        rc = __ngx_parse_addr_port(cf->pool, local->addr, value[1].data,\n                                   value[1].len);\n\n        switch (rc) {\n        case NGX_OK:\n            local->addr->name = value[1];\n            break;\n\n        case NGX_DECLINED:\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"invalid address \\\"%V\\\"\", &value[1]);\n            /* fall through */\n\n        default:\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    if (cf->args->nelts > 2) {\n        if (ngx_strcmp(value[2].data, \"transparent\") == 0) {\n#if (NGX_HAVE_TRANSPARENT_PROXY)\n            local->transparent = 1;\n#else\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"transparent proxying is not supported \"\n                               \"on this platform, ignored\");\n#endif\n        } else {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"invalid parameter \\\"%V\\\"\", &value[2]);\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_proxy_connect_set_local(ngx_http_request_t *r,\n    ngx_http_proxy_connect_upstream_t *u, ngx_http_proxy_connect_address_t *local)\n{\n    ngx_int_t    rc;\n    ngx_str_t    val;\n    ngx_addr_t  *addr;\n\n    if (local == NULL) {\n        u->peer.local = NULL;\n        return NGX_OK;\n    }\n\n#if (NGX_HAVE_TRANSPARENT_PROXY)\n    u->peer.transparent = local->transparent;\n#endif\n\n    if (local->value == NULL) {\n        u->peer.local = local->addr;\n        return NGX_OK;\n    }\n\n    if (ngx_http_complex_value(r, local->value, &val) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    if (val.len == 0) {\n        return NGX_OK;\n    }\n\n    addr = ngx_palloc(r->pool, sizeof(ngx_addr_t));\n    if (addr == NULL) {\n        return NGX_ERROR;\n    }\n\n    rc = __ngx_parse_addr_port(r->pool, addr, val.data, val.len);\n    if (rc == NGX_ERROR) {\n        return NGX_ERROR;\n    }\n\n    if (rc != NGX_OK) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"proxy_connect: invalid local address \\\"%V\\\"\", &val);\n        return NGX_OK;\n    }\n\n    addr->name = val;\n    u->peer.local = addr;\n\n    return NGX_OK;\n}\n\n\nstatic void *\nngx_http_proxy_connect_create_loc_conf(ngx_conf_t *cf)\n{\n    ngx_http_proxy_connect_loc_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_proxy_connect_loc_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc():\n     *\n     *     conf->address = NULL;\n     */\n\n    conf->accept_connect = NGX_CONF_UNSET;\n    conf->allow_port_all = NGX_CONF_UNSET;\n    conf->allow_ports = NGX_CONF_UNSET_PTR;\n\n    conf->connect_timeout = NGX_CONF_UNSET_MSEC;\n    conf->send_timeout = NGX_CONF_UNSET_MSEC;\n    conf->data_timeout = NGX_CONF_UNSET_MSEC;\n\n    conf->send_lowat = NGX_CONF_UNSET_SIZE;\n    conf->buffer_size = NGX_CONF_UNSET_SIZE;\n\n    conf->local = NGX_CONF_UNSET_PTR;\n\n    return conf;\n}\n\n\nstatic char *\nngx_http_proxy_connect_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_http_proxy_connect_loc_conf_t    *prev = parent;\n    ngx_http_proxy_connect_loc_conf_t    *conf = child;\n\n    ngx_conf_merge_value(conf->accept_connect, prev->accept_connect, 0);\n    ngx_conf_merge_value(conf->allow_port_all, prev->allow_port_all, 0);\n    ngx_conf_merge_ptr_value(conf->allow_ports, prev->allow_ports, NULL);\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, prev->send_timeout, 60000);\n\n    ngx_conf_merge_msec_value(conf->data_timeout, prev->data_timeout, 60000);\n\n    ngx_conf_merge_size_value(conf->send_lowat, prev->send_lowat, 0);\n\n    ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size, 16384);\n\n    if (conf->address == NULL) {\n        conf->address = prev->address;\n    }\n\n    ngx_conf_merge_ptr_value(conf->local, prev->local, NULL);\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_proxy_connect_connect_addr_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n\n    ngx_http_proxy_connect_upstream_t     *u;\n    ngx_http_proxy_connect_ctx_t          *ctx;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_connect_module);\n\n    if (ctx == NULL) {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    u = ctx->u;\n\n    if (u == NULL || u->peer.name == NULL) {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    v->len = u->peer.name->len;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = u->peer.name->data;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_proxy_connect_variable_get_time(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    u_char                          *p;\n    ngx_msec_t                      *msp, ms;\n    ngx_http_proxy_connect_ctx_t    *ctx;\n\n    if (r->method != NGX_HTTP_CONNECT) {\n        return NGX_OK;\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_connect_module);\n\n    if (ctx == NULL) {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    msp = (ngx_msec_t *) ((char *) ctx + data);\n    ms = *msp;\n\n    p = ngx_pnalloc(r->pool, NGX_TIME_T_LEN);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    v->len = ngx_sprintf(p, \"%M\", ms) - p;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = p;\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_proxy_connect_variable_set_time(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    ngx_str_t                        s;\n    ngx_msec_t                      *msp, ms;\n    ngx_http_proxy_connect_ctx_t    *ctx;\n\n    if (r->method != NGX_HTTP_CONNECT) {\n        return;\n    }\n\n    s.len = v->len;\n    s.data = v->data;\n\n    ms = ngx_parse_time(&s, 0);\n\n    if (ms == (ngx_msec_t) NGX_ERROR) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"proxy_connect: invalid msec \\\"%V\\\" (ctx offset=%ui)\",\n                      &s, data);\n        return;\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_connect_module);\n\n    if (ctx == NULL) {\n#if 0\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"proxy_connect: no ctx found\");\n#endif\n        return;\n    }\n\n    msp = (ngx_msec_t *) ((char *) ctx + data);\n\n    *msp = ms;\n}\n\n\nstatic ngx_int_t\nngx_http_proxy_connect_resolve_time_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    u_char                             *p;\n    size_t                              len;\n    ngx_msec_int_t                      ms;\n    ngx_http_proxy_connect_ctx_t       *ctx;\n    ngx_http_proxy_connect_upstream_t  *u;\n\n    if (r->method != NGX_HTTP_CONNECT) {\n        return NGX_OK;\n    }\n\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_connect_module);\n\n    if (ctx == NULL) {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    u = ctx->u;\n\n    if (u == NULL || !u->resolved) {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    len = NGX_TIME_T_LEN + 4;\n\n    p = ngx_pnalloc(r->pool, len);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    v->data = p;\n\n    ms = u->state.resolve_time;\n\n    if (ms != -1) {\n        ms = ngx_max(ms, 0);\n        p = ngx_sprintf(p, \"%T.%03M\", (time_t) ms / 1000, ms % 1000);\n\n    } else {\n        *p++ = '-';\n    }\n\n    v->len = p - v->data;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_proxy_connect_connect_time_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    u_char                             *p;\n    size_t                              len;\n    ngx_msec_int_t                      ms;\n    ngx_http_proxy_connect_ctx_t       *ctx;\n    ngx_http_proxy_connect_upstream_t  *u;\n\n    if (r->method != NGX_HTTP_CONNECT) {\n        return NGX_OK;\n    }\n\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_connect_module);\n\n    if (ctx == NULL) {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    u = ctx->u;\n\n    if (u == NULL || !u->connected) {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    len = NGX_TIME_T_LEN + 4;\n\n    p = ngx_pnalloc(r->pool, len);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    v->data = p;\n\n    ms = u->state.connect_time;\n\n    if (ms != -1) {\n        ms = ngx_max(ms, 0);\n        p = ngx_sprintf(p, \"%T.%03M\", (time_t) ms / 1000, ms % 1000);\n\n    } else {\n        *p++ = '-';\n    }\n\n    v->len = p - v->data;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_proxy_connect_first_byte_time_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    u_char                             *p;\n    size_t                              len;\n    ngx_msec_int_t                      ms;\n    ngx_http_proxy_connect_ctx_t       *ctx;\n    ngx_http_proxy_connect_upstream_t  *u;\n\n    if (r->method != NGX_HTTP_CONNECT) {\n        return NGX_OK;\n    }\n\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_connect_module);\n\n    if (ctx == NULL) {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    u = ctx->u;\n\n    if (u == NULL || !u->connected) {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    len = NGX_TIME_T_LEN + 4;\n\n    p = ngx_pnalloc(r->pool, len);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    v->data = p;\n\n    ms = u->state.first_byte_time;\n\n    if (ms != -1) {\n        ms = ngx_max(ms, 0);\n        p = ngx_sprintf(p, \"%T.%03M\", (time_t) ms / 1000, ms % 1000);\n\n    } else {\n        *p++ = '-';\n    }\n\n    v->len = p - v->data;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_proxy_connect_variable_get_response(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    ngx_http_proxy_connect_ctx_t       *ctx;\n\n    if (r->method != NGX_HTTP_CONNECT) {\n        return NGX_OK;\n    }\n\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_connect_module);\n\n    if (ctx == NULL) {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    v->data = ctx->buf.pos;\n    v->len = ctx->buf.last - ctx->buf.pos;\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_proxy_connect_variable_set_response(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    ngx_http_proxy_connect_ctx_t       *ctx;\n\n    if (r->method != NGX_HTTP_CONNECT) {\n        return;\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_connect_module);\n\n    if (ctx == NULL) {\n        return;\n    }\n\n    ctx->buf.pos = (u_char *) v->data;\n    ctx->buf.last = ctx->buf.pos + v->len;\n}\n\nstatic ngx_int_t\nngx_http_proxy_connect_add_variables(ngx_conf_t *cf)\n{\n    ngx_http_variable_t  *var, *v;\n\n    for (v = ngx_http_proxy_connect_vars; v->name.len; v++) {\n        var = ngx_http_add_variable(cf, &v->name, v->flags);\n        if (var == NULL) {\n            return NGX_ERROR;\n        }\n\n        *var = *v;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_proxy_connect_post_read_handler(ngx_http_request_t *r)\n{\n    ngx_http_proxy_connect_ctx_t      *ctx;\n    ngx_http_proxy_connect_loc_conf_t *pclcf;\n\n    if (r->method == NGX_HTTP_CONNECT) {\n\n        pclcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_connect_module);\n\n        if (!pclcf->accept_connect) {\n            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                          \"proxy_connect: client sent connect method\");\n            return NGX_HTTP_NOT_ALLOWED;\n        }\n\n        /* init ctx */\n\n        ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_proxy_connect_ctx_t));\n        if (ctx == NULL) {\n            return NGX_ERROR;\n        }\n\n        ctx->buf.pos = (u_char *) NGX_HTTP_PROXY_CONNECT_ESTABLISTHED;\n        ctx->buf.last = ctx->buf.pos +\n                        sizeof(NGX_HTTP_PROXY_CONNECT_ESTABLISTHED) - 1;\n        ctx->buf.memory = 1;\n\n        ctx->connect_timeout = pclcf->connect_timeout;\n        ctx->send_timeout = pclcf->send_timeout;\n        ctx->data_timeout = pclcf->data_timeout;\n\n        ngx_http_set_ctx(r, ctx, ngx_http_proxy_connect_module);\n    }\n\n    return NGX_DECLINED;\n}\n\n\nstatic ngx_int_t\nngx_http_proxy_connect_init(ngx_conf_t *cf)\n{\n    ngx_http_core_main_conf_t  *cmcf;\n    ngx_http_handler_pt        *h;\n\n    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);\n\n    h = ngx_array_push(&cmcf->phases[NGX_HTTP_POST_READ_PHASE].handlers);\n    if (h == NULL) {\n        return NGX_ERROR;\n    }\n\n    *h = ngx_http_proxy_connect_post_read_handler;\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "modules/ngx_http_proxy_connect_module/t/http_proxy_connect.t",
    "content": "#!/usr/bin/perl\n\n# Copyright (C) 2010-2013 Alibaba Group Holding Limited\n\n# Tests for connect method support.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse IO::Select;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy/)->plan(22);\n\n###############################################################################\n\nmy $test_enable_rewrite_phase = 1;\n\nif (defined $ENV{TEST_DISABLE_REWRITE_PHASE}) {\n    $test_enable_rewrite_phase = 0;\n}\n\nprint(\"+ test_enable_rewrite_phase: $test_enable_rewrite_phase\\n\");\n\n# --- init DNS server ---\n\n# SRV record, not used\nmy %route_map;\n\n# A record\nmy %aroute_map = (\n    'www.test-a.com' => [[300, \"127.0.0.1\"]],\n    'www.test-b.com' => [[300, \"127.0.0.1\"]],\n    'get-default-response.com' => [[300, \"127.0.0.1\"]],\n    'set-response-header.com' => [[300, \"127.0.0.1\"]],\n    'set-response-status.com' => [[300, \"127.0.0.1\"]],\n);\n\n###############################################################################\n\nmy $nginx_conf = <<'EOF';\n\n%%TEST_GLOBALS%%\n\ndaemon         off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    #LUA_PACKAGE_PATH\n    # If you build nginx with lua-nginx-module, please enable           # directive \"lua_package_path\". For more details, see:              #  https://github.com/openresty/lua-nginx-module#installation\n    #lua_package_path \"/path/to/lib/lua/?.lua;;\";\n\n    log_format connect '$remote_addr - $remote_user [$time_local] \"$request\" '\n                       '$status $body_bytes_sent var:$connect_host-$connect_port-$connect_addr';\n\n    access_log %%TESTDIR%%/connect.log connect;\n\n    resolver 127.0.0.1:%%PORT_8981_UDP%% ipv6=off;      # NOTE: cannot connect ipv6 address ::1 in mac os x.\n\n    server {\n        listen  127.0.0.1:8081;\n        listen  127.0.0.1:8082;   # address.com\n        listen  127.0.0.1:8083;   # bind.conm\n        server_name server_8081;\n        access_log off;\n        location / {\n            return 200 \"backend server: addr:$remote_addr port:$server_port host:$host\\n\";\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        set $proxy_remote_address \"\";\n        set $proxy_local_address \"\";\n        # forward proxy for CONNECT method\n        proxy_connect;\n        proxy_connect_allow 443 80 8081;\n        proxy_connect_connect_timeout 10s;\n        proxy_connect_data_timeout 10s;\n        proxy_connect_address $proxy_remote_address;\n        proxy_connect_bind $proxy_local_address;\n\n        if ($host = \"address.com\") {\n            set $proxy_remote_address \"127.0.0.01:8082\";\n        }\n\n        if ($host = \"bind.com\") {\n            set $proxy_remote_address \"127.0.0.01:8083\";\n            set $proxy_local_address \"127.0.0.1\";   # NOTE that we cannot bind 127.0.0.3 in mac os x.\n        }\n\n        if ($host = \"proxy-remote-address-resolve-domain.com\") {\n            set $proxy_remote_address \"www.test-a.com:8081\";\n        }\n\n        location / {\n            proxy_pass http://127.0.0.01:8081;\n        }\n\n        location = /hello {\n            return 200 \"world\";\n        }\n\n        # used to output connect.log\n        location = /connect.log {\n            access_log off;\n            root %%TESTDIR%%/;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  forbidden.example.com;\n\n        # It will forbid CONNECT request without proxy_connect command enabled.\n\n        return 200;\n    }\n}\n\nEOF\n\n$t->write_file_expand('nginx.conf', $nginx_conf);\n\n$t->run_daemon(\\&dns_daemon, port(8981), $t);\n$t->waitforfile($t->testdir . '/' . port(8981));\n\neval {\n    $t->run();\n};\n\nif ($@) {\n    print(\"+ Retry new nginx conf: remove \\\"ipv6=off\\\"\\n\");\n\n    $nginx_conf =~ s/ ipv6=off;/;/g;        # remove ipv6=off in resolver directive.\n    $t->write_file_expand('nginx.conf', $nginx_conf);\n    $t->run();\n}\n\nlike(http_connect_request('127.0.0.1', '8081', '/'), qr/backend server/, '200 Connection Established');\nlike(http_connect_request('www.test-a.com', '8081', '/'), qr/host:www\\.test-a\\.com/, '200 Connection Established server name');\nlike(http_connect_request('www.test-b.com', '8081', '/'), qr/host:www\\.test-b\\.com/, '200 Connection Established server name');\nlike(http_connect_request('www.no-dns-reply.com', '80', '/'), qr/502/, '200 Connection Established server name');\nlike(http_connect_request('127.0.0.1', '9999', '/'), qr/403/, '200 Connection Established not allowed port');\nlike(http_get('/'), qr/backend server/, 'Get method: proxy_pass');\nlike(http_get('/hello'), qr/world/, 'Get method: return 200');\nlike(http_connect_request('forbidden.example.com', '8080', '/'), qr/405 Not Allowed/, 'forbid CONNECT request without proxy_connect command enabled');\n\n# proxy_remote_address directive supports dynamic domain resolving.\nlike(http_connect_request('proxy-remote-address-resolve-domain.com', '8081', '/'),\n     qr/host:proxy-remote-address-resolve-domain\\.com/,\n     'proxy_remote_address supports dynamic domain resovling');\n\nif ($test_enable_rewrite_phase) {\n    like(http_connect_request('address.com', '8081', '/'), qr/backend server: addr:127.0.0.1 port:8082/, 'set remote address');\n    like(http_connect_request('bind.com', '8081', '/'), qr/backend server: addr:127.0.0.1 port:8083/, 'set local address and remote address');\n}\n\n\n# test $connect_host, $connect_port\nmy $log = http_get('/connect.log');\nlike($log, qr/CONNECT 127\\.0\\.0\\.1:8081.*var:127\\.0\\.0\\.1-8081-127\\.0\\.0\\.1:8081/, '$connect_host, $connect_port, $connect_addr');\nlike($log, qr/CONNECT www\\.no-dns-reply\\.com:80.*var:www\\.no-dns-reply\\.com-80--/, 'dns resolver fail');\n\n$t->stop();\n\n###############################################################################\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon         off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    #LUA_PACKAGE_PATH\n    # If you build nginx with lua-nginx-module, please enable           # directive \"lua_package_path\". For more details, see:              #  https://github.com/openresty/lua-nginx-module#installation\n    #lua_package_path \"/path/to/lib/lua/?.lua;;\";\n\n    access_log off;\n\n    server {\n        listen  127.0.0.1:8082;\n        location / {\n            return 200 \"backend server: $remote_addr $server_port\\n\";\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        # forward proxy for CONNECT method\n\n        proxy_connect;\n        proxy_connect_allow all;\n\n        proxy_connect_address 127.0.0.01:8082;\n\n        if ($host = \"if-return-skip.com\") {\n            return 200 \"if-return\\n\";\n        }\n\n        return 200 \"skip proxy connect: $host,$uri,$request_uri,$args\\n\";\n    }\n}\n\nEOF\n\n\n$t->run();\nif ($test_enable_rewrite_phase) {\n    like(http_connect_request('address.com', '8081', '/'), qr/skip proxy connect/, 'skip proxy connect module without rewrite phase enabled');\n    like(http_connect_request('if-return-skip.com', '8081', '/'), qr/if-return/, 'skip proxy connect module without rewrite phase enabled: if/return');\n} else {\n    like(http_connect_request('address.com', '8081', '/'), qr/backend server: 127.0.0.1 8082/, 'set remote address without nginx variable');\n}\n$t->stop();\n\n###############################################################################\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon         off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    #LUA_PACKAGE_PATH\n    # If you build nginx with lua-nginx-module, please enable           # directive \"lua_package_path\". For more details, see:              #  https://github.com/openresty/lua-nginx-module#installation\n    #lua_package_path \"/path/to/lib/lua/?.lua;;\";\n\n    access_log off;\n\n    server {\n        listen       127.0.0.1:8080;\n        proxy_connect;\n        proxy_connect_allow all;\n    }\n}\n\nEOF\n\n\n$t->run();\n\n$t->write_file('test.html', 'test page');\n\nlike(http_get('/test.html'), qr/test page/, '200 for default root directive without location {}');\nlike(http_get('/404'), qr/ 404 Not Found/, '404 for default root directive without location {}');\n\n$t->stop();\n\n###############################################################################\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon         off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    #LUA_PACKAGE_PATH\n    # If you build nginx with lua-nginx-module, please enable           # directive \"lua_package_path\". For more details, see:              #  https://github.com/openresty/lua-nginx-module#installation\n    #lua_package_path \"/path/to/lib/lua/?.lua;;\";\n\n    access_log off;\n\n    resolver 127.0.0.1:%%PORT_8981_UDP%% ipv6=off;      # NOTE: cannot connect ipv6 address ::1 in mac os x.\n\n    server {\n        listen       127.0.0.1:8080;\n        proxy_connect;\n        proxy_connect_allow all;\n\n        if ($host = \"get-default-response.com\") {\n            return 403 \"|$proxy_connect_response|\";\n        }\n\n        if ($host = \"set-response-header.com\") {\n            set $proxy_connect_response \"HTTP/1.1 200\\r\\nFoo: bar\\r\\n\\r\\n\";\n        }\n\n        if ($host = \"set-response-status.com\") {\n            set $proxy_connect_response \"HTTP/1.1 403\\r\\n\\r\\n\";\n        }\n    }\n\n    server {\n        listen  8081;\n        location / {\n            return 200 \"backend\";\n        }\n    }\n}\n\nEOF\n\n# test $proxy_connect_response variable\n\n$t->run();\n\nif ($test_enable_rewrite_phase) {\n    like(http_connect_request('www.test-a.com', '8081', '/'), qr/OK/, 'nothing changed with CONNECT response');\n\n    like(http_connect_request_raw('get-default-response.com', '8081', '/'),\n         qr/\\|HTTP\\/1\\.1 200 Connection Established\\r\\nProxy-agent: nginx\\r\\n\\r\\n\\|/,\n        'get default CONNECT response');\n\n    like(http_connect_request('set-response-header.com', '8081', '/'), qr/Foo: bar\\r/, 'added header \"Foo: bar\" to CONNECT response');\n    like(http_connect_request('set-response-status.com', '8081', '/'), qr/HTTP\\/1.1 403/, 'change CONNECT response status');\n}\n\n$t->stop();\n\n###############################################################################\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon         off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    #LUA_PACKAGE_PATH\n    # If you build nginx with lua-nginx-module, please enable           # directive \"lua_package_path\". For more details, see:              #  https://github.com/openresty/lua-nginx-module#installation\n    #lua_package_path \"/path/to/lib/lua/?.lua;;\";\n\n    access_log off;\n\n    resolver 127.0.0.1:%%PORT_8981_UDP%% ipv6=off;      # NOTE: cannot connect ipv6 address ::1 in mac os x.\n\n    server {\n        listen       127.0.0.1:8080;\n        proxy_connect;\n        proxy_connect_allow all;\n\n        proxy_connect_response \"HTTP/1.1 200 Connection Established\\r\\nProxy-agent: nginx\\r\\nX-Proxy-Connected-Addr: $connect_addr\\r\\n\\r\\n\";\n    }\n\n    server {\n        listen  8081;\n        location / {\n            return 200 \"backend\";\n        }\n    }\n}\n\nEOF\n\n# test proxy_connect_response directive\n\n$t->run();\n\nif ($test_enable_rewrite_phase) {\n    like(http_connect_request('set-response-header.com', '8081', '/'), qr/X-Proxy-Connected-Addr: 127.0.0.1:8081\\r/, 'added header \"Foo: bar\" to CONNECT response');\n}\n\n$t->stop();\n\n###############################################################################\n\nsub http_connect_request {\n    my ($host, $port, $url) = @_;\n    my $r = http_connect($host, $port, <<EOF);\nGET $url HTTP/1.0\nHost: $host\nConnection: close\n\nEOF\n    return $r\n}\n\nsub http_connect($;%) {\n    my ($host, $port, $request, %extra) = @_;\n    my $reply;\n    eval {\n        local $SIG{ALRM} = sub { die \"timeout\\n\" };\n        local $SIG{PIPE} = sub { die \"sigpipe\\n\" };\n        alarm(2);\n        my $s = IO::Socket::INET->new(\n            Proto => 'tcp',\n            PeerAddr => '127.0.0.1:8080'\n        );\n        $s->print(<<EOF);\nCONNECT $host:$port HTTP/1.1\nHost: $host\n\nEOF\n        select undef, undef, undef, $extra{sleep} if $extra{sleep};\n        return '' if $extra{aborted};\n        my $n = $s->sysread($reply, 65536);\n        return unless $n;\n        if ($reply !~ /HTTP\\/1\\.[01] 200 Connection Established\\r\\nProxy-agent: .+\\r\\n\\r\\n/) {\n            return $reply;\n        }\n        log_out($request);\n        $s->print($request);\n        local $/;\n        select undef, undef, undef, $extra{sleep} if $extra{sleep};\n        return '' if $extra{aborted};\n        $reply = $s->getline();\n        alarm(0);\n    };\n    alarm(0);\n    if ($@) {\n        log_in(\"died: $@\");\n        return undef;\n    }\n    log_in($reply);\n    return $reply;\n}\n\nsub http_connect_request_raw {\n    my ($host, $port, $url) = @_;\n    my $r = http_connect_raw($host, $port, <<EOF);\nGET $url HTTP/1.0\nHost: $host\nConnection: close\n\nEOF\n    return $r\n}\n\nsub http_connect_raw($;%) {\n    my ($host, $port, $request, %extra) = @_;\n    my $reply;\n    eval {\n        local $SIG{ALRM} = sub { die \"timeout\\n\" };\n        local $SIG{PIPE} = sub { die \"sigpipe\\n\" };\n        alarm(2);\n        my $s = IO::Socket::INET->new(\n            Proto => 'tcp',\n            PeerAddr => '127.0.0.1:8080'\n        );\n        $s->print(<<EOF);\nCONNECT $host:$port HTTP/1.0\nHost: $host\n\nEOF\n        select undef, undef, undef, $extra{sleep} if $extra{sleep};\n        return '' if $extra{aborted};\n        my $n = $s->sysread($reply, 65536);\n        return unless $n;\n        return $reply;\n\n        # ignore data flow over CONNECT tunnel\n        #log_out($request);\n        #$s->print($request);\n        #local $/;\n        #select undef, undef, undef, $extra{sleep} if $extra{sleep};\n        #return '' if $extra{aborted};\n        #$reply =  $s->getline();\n        #alarm(0);\n    };\n    alarm(0);\n    if ($@) {\n        log_in(\"died: $@\");\n        return undef;\n    }\n    log_in($reply);\n    return $reply;\n}\n\n###############################################################################\n\nsub reply_handler {\n\tmy ($recv_data, $port, $state, %extra) = @_;\n\n\tmy (@name, @rdata);\n\n\tuse constant NOERROR\t=> 0;\n\tuse constant FORMERR\t=> 1;\n\tuse constant SERVFAIL\t=> 2;\n\tuse constant NXDOMAIN\t=> 3;\n\n\tuse constant A\t\t=> 1;\n\tuse constant CNAME\t=> 5;\n\tuse constant DNAME\t=> 39;\n\n\tuse constant IN\t\t=> 1;\n\n\t# default values\n\n\tmy ($hdr, $rcode, $ttl) = (0x8180, NOERROR, 3600);\n\n\t# decode name\n\n\tmy ($len, $offset) = (undef, 12);\n\twhile (1) {\n\t\t$len = unpack(\"\\@$offset C\", $recv_data);\n\t\tlast if $len == 0;\n\t\t$offset++;\n\t\tpush @name, unpack(\"\\@$offset A$len\", $recv_data);\n\t\t$offset += $len;\n\t}\n\n\t$offset -= 1;\n\tmy ($id, $type, $class) = unpack(\"n x$offset n2\", $recv_data);\n\n\tmy $name = join('.', @name);\n\n        if (($type == A) && exists($aroute_map{$name})) {\n\n            my @records = @{$aroute_map{$name}};\n\n            for (my $i = 0; $i < scalar(@records); $i++) {\n                my ($ttl, $origin_addr) = @{$records[$i]};\n                push @rdata, rd_addr($ttl, $origin_addr);\n\n                #print(\"dns reply: $name $ttl $class $type $origin_addr\\n\");\n            }\n        }\n\n\t$len = @name;\n\tpack(\"n6 (C/a*)$len x n2\", $id, $hdr | $rcode, 1, scalar @rdata,\n\t\t0, 0, @name, $type, $class) . join('', @rdata);\n}\n\nsub rd_addr {\n\tmy ($ttl, $addr) = @_;\n\n\tmy $code = 'split(/\\./, $addr)';\n\n\treturn pack 'n3N', 0xc00c, A, IN, $ttl if $addr eq '';\n\n\tpack 'n3N nC4', 0xc00c, A, IN, $ttl, eval \"scalar $code\", eval($code);\n}\n\nsub dns_daemon {\n\tmy ($port, $t, %extra) = @_;\n\n        print(\"+ dns daemon: try to listen on 127.0.0.1:$port\\n\");\n\n\tmy ($data, $recv_data);\n\tmy $socket = IO::Socket::INET->new(\n\t\tLocalAddr => '127.0.0.1',\n\t\tLocalPort => $port,\n\t\tProto => 'udp',\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\tmy $sel = IO::Select->new($socket);\n\tmy $tcp = 0;\n\n\tif ($extra{tcp}) {\n\t\t$tcp = port(8983, socket => 1);\n\t\t$sel->add($tcp);\n\t}\n\n\tlocal $SIG{PIPE} = 'IGNORE';\n\n\t# track number of relevant queries\n\n\tmy %state = (\n\t\tcnamecnt\t=> 0,\n\t\ttwocnt\t\t=> 0,\n\t\tttlcnt\t\t=> 0,\n\t\tttl0cnt\t\t=> 0,\n\t\tcttlcnt\t\t=> 0,\n\t\tcttl2cnt\t=> 0,\n\t\tmanycnt\t\t=> 0,\n\t\tcasecnt\t\t=> 0,\n\t\tidcnt\t\t=> 0,\n\t\tfecnt\t\t=> 0,\n\t);\n\n\t# signal we are ready\n\n\topen my $fh, '>', $t->testdir() . '/' . $port;\n\tclose $fh;\n\n\twhile (my @ready = $sel->can_read) {\n\t\tforeach my $fh (@ready) {\n\t\t\tif ($tcp == $fh) {\n\t\t\t\tmy $new = $fh->accept;\n\t\t\t\t$new->autoflush(1);\n\t\t\t\t$sel->add($new);\n\n\t\t\t} elsif ($socket == $fh) {\n\t\t\t\t$fh->recv($recv_data, 65536);\n\t\t\t\t$data = reply_handler($recv_data, $port,\n\t\t\t\t\t\\%state);\n\t\t\t\t$fh->send($data);\n\n\t\t\t} else {\n\t\t\t\t$fh->recv($recv_data, 65536);\n\t\t\t\tunless (length $recv_data) {\n\t\t\t\t\t$sel->remove($fh);\n\t\t\t\t\t$fh->close;\n\t\t\t\t\tnext;\n\t\t\t\t}\n\nagain:\n\t\t\t\tmy $len = unpack(\"n\", $recv_data);\n\t\t\t\t$data = substr $recv_data, 2, $len;\n\t\t\t\t$data = reply_handler($data, $port, \\%state,\n\t\t\t\t\ttcp => 1);\n\t\t\t\t$data = pack(\"n\", length $data) . $data;\n\t\t\t\t$fh->send($data);\n\t\t\t\t$recv_data = substr $recv_data, 2 + $len;\n\t\t\t\tgoto again if length $recv_data;\n\t\t\t}\n\t\t}\n\t}\n}\n\n###############################################################################\n"
  },
  {
    "path": "modules/ngx_http_proxy_connect_module/t/http_proxy_connect_lua.t",
    "content": "#!/usr/bin/perl\n\n# Tests for connect method support.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse IO::Select;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy lua/)->plan(1);\n\n###############################################################################\n\nmy $test_enable_rewrite_phase = 1;\n\nif (defined $ENV{TEST_DISABLE_REWRITE_PHASE}) {\n    $test_enable_rewrite_phase = 0;\n}\n\nprint(\"+ test_enable_rewrite_phase: $test_enable_rewrite_phase\\n\");\n\n# --- init DNS server ---\n\n# SRV record, not used\nmy %route_map;\n\n# A record\nmy %aroute_map = (\n    'www.test-a.com' => [[300, \"127.0.0.1\"]],\n    'www.test-b.com' => [[300, \"127.0.0.1\"]],\n    'set-response-header.com' => [[300, \"127.0.0.1\"]],\n    'set-response-status.com' => [[300, \"127.0.0.1\"]],\n);\n\n###############################################################################\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon         off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    #LUA_PACKAGE_PATH\n    # If you build nginx with lua-nginx-module, please enable           \n    # directive \"lua_package_path\". For more details, see:              \n    #  https://github.com/openresty/lua-nginx-module#installation\n    #lua_package_path \"/path/to/lib/lua/?.lua;;\";\n\n    access_log off;\n\n    resolver 127.0.0.1:%%PORT_8981_UDP%% ipv6=off;      # NOTE: cannot connect ipv6 address ::1 in mac os x.\n\n    server {\n        listen       127.0.0.1:8080;\n        proxy_connect;\n        proxy_connect_allow all;\n\n        rewrite_by_lua '\n            if ngx.var.host == \"set-response-header.com\" then\n                ngx.var.proxy_connect_response =\n                  string.format(\"HTTP/1.1 200\\\\r\\\\nProxy-agent: nginx/%s\\\\r\\\\n\\\\r\\\\n\", ngx.var.nginx_version)\n            end\n        ';\n    }\n\n    server {\n        listen  8081;\n        location / {\n            return 200 \"backend\";\n        }\n    }\n}\n\nEOF\n\n# test $proxy_connect_response variable via lua-nginx-module\n\n$t->run_daemon(\\&dns_daemon, port(8981), $t);\n$t->waitforfile($t->testdir . '/' . port(8981));\n\n$t->run();\n\nif ($test_enable_rewrite_phase) {\n    like(http_connect_request('set-response-header.com', '8081', '/'), qr/Proxy-agent: nginx\\/[\\d.]+\\r/, 'modify Proxy-agent to nginx version');\n}\n\n$t->stop();\n\n###############################################################################\n\nsub http_connect_request {\n    my ($host, $port, $url) = @_;\n    my $r = http_connect($host, $port, <<EOF);\nGET $url HTTP/1.0\nHost: $host\nConnection: close\n\nEOF\n    return $r\n}\n\nsub http_connect($;%) {\n    my ($host, $port, $request, %extra) = @_;\n    my $reply;\n    eval {\n        local $SIG{ALRM} = sub { die \"timeout\\n\" };\n        local $SIG{PIPE} = sub { die \"sigpipe\\n\" };\n        alarm(2);\n        my $s = IO::Socket::INET->new(\n            Proto => 'tcp',\n            PeerAddr => '127.0.0.1:8080'\n        );\n        $s->print(<<EOF);\nCONNECT $host:$port HTTP/1.1\nHost: $host\n\nEOF\n        select undef, undef, undef, $extra{sleep} if $extra{sleep};\n        return '' if $extra{aborted};\n        my $n = $s->sysread($reply, 65536);\n        return unless $n;\n        if ($reply !~ /HTTP\\/1\\.[01] 200 Connection Established\\r\\nProxy-agent: .+\\r\\n\\r\\n/) {\n            return $reply;\n        }\n        log_out($request);\n        $s->print($request);\n        local $/;\n        select undef, undef, undef, $extra{sleep} if $extra{sleep};\n        return '' if $extra{aborted};\n        $reply = $s->getline();\n        alarm(0);\n    };\n    alarm(0);\n    if ($@) {\n        log_in(\"died: $@\");\n        return undef;\n    }\n    log_in($reply);\n    return $reply;\n}\n\n###############################################################################\n\nsub reply_handler {\n\tmy ($recv_data, $port, $state, %extra) = @_;\n\n\tmy (@name, @rdata);\n\n\tuse constant NOERROR\t=> 0;\n\tuse constant FORMERR\t=> 1;\n\tuse constant SERVFAIL\t=> 2;\n\tuse constant NXDOMAIN\t=> 3;\n\n\tuse constant A\t\t=> 1;\n\tuse constant CNAME\t=> 5;\n\tuse constant DNAME\t=> 39;\n\n\tuse constant IN\t\t=> 1;\n\n\t# default values\n\n\tmy ($hdr, $rcode, $ttl) = (0x8180, NOERROR, 3600);\n\n\t# decode name\n\n\tmy ($len, $offset) = (undef, 12);\n\twhile (1) {\n\t\t$len = unpack(\"\\@$offset C\", $recv_data);\n\t\tlast if $len == 0;\n\t\t$offset++;\n\t\tpush @name, unpack(\"\\@$offset A$len\", $recv_data);\n\t\t$offset += $len;\n\t}\n\n\t$offset -= 1;\n\tmy ($id, $type, $class) = unpack(\"n x$offset n2\", $recv_data);\n\n\tmy $name = join('.', @name);\n\n        if (($type == A) && exists($aroute_map{$name})) {\n\n            my @records = @{$aroute_map{$name}};\n\n            for (my $i = 0; $i < scalar(@records); $i++) {\n                my ($ttl, $origin_addr) = @{$records[$i]};\n                push @rdata, rd_addr($ttl, $origin_addr);\n\n                #print(\"dns reply: $name $ttl $class $type $origin_addr\\n\");\n            }\n        }\n\n\t$len = @name;\n\tpack(\"n6 (C/a*)$len x n2\", $id, $hdr | $rcode, 1, scalar @rdata,\n\t\t0, 0, @name, $type, $class) . join('', @rdata);\n}\n\nsub rd_addr {\n\tmy ($ttl, $addr) = @_;\n\n\tmy $code = 'split(/\\./, $addr)';\n\n\treturn pack 'n3N', 0xc00c, A, IN, $ttl if $addr eq '';\n\n\tpack 'n3N nC4', 0xc00c, A, IN, $ttl, eval \"scalar $code\", eval($code);\n}\n\nsub dns_daemon {\n\tmy ($port, $t, %extra) = @_;\n\n        print(\"+ dns daemon: try to listen on 127.0.0.1:$port\\n\");\n\n\tmy ($data, $recv_data);\n\tmy $socket = IO::Socket::INET->new(\n\t\tLocalAddr => '127.0.0.1',\n\t\tLocalPort => $port,\n\t\tProto => 'udp',\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\tmy $sel = IO::Select->new($socket);\n\tmy $tcp = 0;\n\n\tif ($extra{tcp}) {\n\t\t$tcp = port(8983, socket => 1);\n\t\t$sel->add($tcp);\n\t}\n\n\tlocal $SIG{PIPE} = 'IGNORE';\n\n\t# track number of relevant queries\n\n\tmy %state = (\n\t\tcnamecnt\t=> 0,\n\t\ttwocnt\t\t=> 0,\n\t\tttlcnt\t\t=> 0,\n\t\tttl0cnt\t\t=> 0,\n\t\tcttlcnt\t\t=> 0,\n\t\tcttl2cnt\t=> 0,\n\t\tmanycnt\t\t=> 0,\n\t\tcasecnt\t\t=> 0,\n\t\tidcnt\t\t=> 0,\n\t\tfecnt\t\t=> 0,\n\t);\n\n\t# signal we are ready\n\n\topen my $fh, '>', $t->testdir() . '/' . $port;\n\tclose $fh;\n\n\twhile (my @ready = $sel->can_read) {\n\t\tforeach my $fh (@ready) {\n\t\t\tif ($tcp == $fh) {\n\t\t\t\tmy $new = $fh->accept;\n\t\t\t\t$new->autoflush(1);\n\t\t\t\t$sel->add($new);\n\n\t\t\t} elsif ($socket == $fh) {\n\t\t\t\t$fh->recv($recv_data, 65536);\n\t\t\t\t$data = reply_handler($recv_data, $port,\n\t\t\t\t\t\\%state);\n\t\t\t\t$fh->send($data);\n\n\t\t\t} else {\n\t\t\t\t$fh->recv($recv_data, 65536);\n\t\t\t\tunless (length $recv_data) {\n\t\t\t\t\t$sel->remove($fh);\n\t\t\t\t\t$fh->close;\n\t\t\t\t\tnext;\n\t\t\t\t}\n\nagain:\n\t\t\t\tmy $len = unpack(\"n\", $recv_data);\n\t\t\t\t$data = substr $recv_data, 2, $len;\n\t\t\t\t$data = reply_handler($data, $port, \\%state,\n\t\t\t\t\ttcp => 1);\n\t\t\t\t$data = pack(\"n\", length $data) . $data;\n\t\t\t\t$fh->send($data);\n\t\t\t\t$recv_data = substr $recv_data, 2 + $len;\n\t\t\t\tgoto again if length $recv_data;\n\t\t\t}\n\t\t}\n\t}\n}\n\n###############################################################################\n"
  },
  {
    "path": "modules/ngx_http_proxy_connect_module/t/http_proxy_connect_resolve_variables.t",
    "content": "#!/usr/bin/perl\n\n# Copyright (C) 2010-2013 Alibaba Group Holding Limited\n\n# Tests for connect method support.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n# use Test::Simple 'no_plan';\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse Net::DNS::Nameserver;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy/)->plan(14);\n\n###############################################################################\n\nmy $test_enable_rewrite_phase = 1;\n\nif (defined $ENV{TEST_DISABLE_REWRITE_PHASE}) {\n    $test_enable_rewrite_phase = 0;\n}\n\nprint(\"+ test_enable_rewrite_phase: $test_enable_rewrite_phase\\n\");\n\nplan(skip_all => 'No rewrite phase enabled') if ($test_enable_rewrite_phase == 0);\n\n# --- init DNS server ---\n\n# SRV record, not used\nmy %route_map;\n\n# A record\nmy %aroute_map = (\n    'test-connect-timeout.com' => [[300, \"8.8.8.8\"]],\n    'test-read-timeout.com' => [[300, \"8.8.8.8\"]],\n);\n\n# AAAA record (ipv6)\nmy %aaaaroute_map;\n# my %aaaaroute_map = (\n#     'www.test-a.com' => [[300, \"[::1]\"]],\n#     'www.test-b.com' => [[300, \"[::1]\"]],\n#     #'www.test-a.com' => [[300, \"127.0.0.1\"]],\n#     #'www.test-b.com' => [[300, \"127.0.0.1\"]],\n# );\n\n###############################################################################\n\nmy $nginx_conf = <<'EOF';\n\n%%TEST_GLOBALS%%\n\ndaemon         off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    #LUA_PACKAGE_PATH\n    # If you build nginx with lua-nginx-module, please enable           \n    # directive \"lua_package_path\". For more details, see:              \n    #  https://github.com/openresty/lua-nginx-module#installation\n    #lua_package_path \"/path/to/lib/lua/?.lua;;\";\n\n#    lua_load_resty_core off;\n\n    log_format connect '$remote_addr - $remote_user [$time_local] \"$request\" '\n                       '$status $body_bytes_sent var:$connect_host-$connect_port-$connect_addr '\n                       'resolve:$proxy_connect_resolve_time,'\n                       'connect:$proxy_connect_connect_time,'\n                       'fbt:$proxy_connect_first_byte_time,';\n\n    access_log %%TESTDIR%%/connect.log connect;\n    error_log %%TESTDIR%%/connect_error.log info;\n\n    resolver 127.0.0.1:%%PORT_8981_UDP%% ipv6=off;      # NOTE: cannot connect ipv6 address ::1 in mac os x.\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        # forward proxy for CONNECT method\n        proxy_connect;\n        proxy_connect_allow all;\n        proxy_connect_connect_timeout 10s;\n        proxy_connect_data_timeout 10s;\n\n        set $proxy_connect_connect_timeout  \"101ms\";\n        set $proxy_connect_data_timeout     \"103ms\";\n\n        if ($uri = \"/200\") {\n            return 200;\n        }\n\n        if ($host = \"test-connect-timeout.com\") {\n            set $proxy_connect_connect_timeout \"1ms\";\n        }\n        if ($host = \"test-read-timeout.com\") {\n            set $proxy_connect_connect_timeout  \"3ms\";\n            set $proxy_connect_data_timeout     \"1ms\";\n        }\n\n        if ($request ~ \"127.0.0.1:8082\") {\n            # must be larger than 1s (server 8082 lua sleep(1s))\n            set $proxy_connect_data_timeout \"1200ms\";\n        }\n\n        if ($request ~ \"127.0.0.1:8083\") {\n            # must be larger than 0.5s (server 8082 lua sleep(0.5s))\n            set $proxy_connect_data_timeout \"700ms\";\n        }\n\n        if ($request ~ \"127.0.0.01:8082\") {\n            # must be less than 1s (server 8082 lua sleep(1s))\n            set $proxy_connect_data_timeout \"800ms\";\n        }\n\n        if ($request ~ \"127.0.0.01:8083\") {\n            # must be less than 0.5s (server 8082 lua sleep(1s))\n            set $proxy_connect_data_timeout \"300ms\";\n        }\n\n        location / {\n            proxy_pass http://127.0.0.01:8081;\n        }\n    }\n\n    server {\n        listen 8081;\n        access_log off;\n        return 200 \"8081 server\";\n    }\n\n    # for $proxy_connect_first_byte_time testing\n    server {\n        access_log off;\n        listen 8082;\n\n        rewrite_by_lua '\n            ngx.sleep(1)\n            ngx.say(\"8082 server fbt\")\n            ngx.exit(ngx.HTTP_OK)\n        ';\n\n    }\n    server {\n        access_log off;\n        listen 8083;\n        rewrite_by_lua '\n            ngx.sleep(0.5)\n            ngx.say(\"8083 server fbt\")\n            ngx.exit(ngx.HTTP_OK)\n        ';\n\n    }\n\n}\n\nEOF\n\n$t->write_file_expand('nginx.conf', $nginx_conf);\n\n$t->run_daemon(\\&dns_daemon, port(8981), $t);\n$t->waitforfile($t->testdir . '/' . port(8981));\n\neval {\n    $t->run();\n};\n\nif ($@) {\n    print(\"+ Retry new nginx conf: remove \\\"ipv6=off\\\"\\n\");\n\n    $nginx_conf =~ s/ ipv6=off;/;/g;        # remove ipv6=off in resolver directive.\n    $t->write_file_expand('nginx.conf', $nginx_conf);\n    $t->run();\n}\n\n#if (not $test_enable_rewrite_phase) {\n#  exit\n#}\n\nTODO: {\n    # $proxy_connect_connect_time has value, $proxy_connect_connect_time is \"-\"\n    local $TODO = '# This case will pass, if connecting 8.8.8.8 timed out.';\n    http_connect_request('test-connect-timeout.com', '8888', '/');\n    like($t->read_file('connect.log'),\n         qr/\"CONNECT test-connect-timeout.com:8888 HTTP\\/1.1\" 504 .+ resolve:\\d+\\.\\d+,connect:-/,\n        'connect timed out log: get $var & status=504');\n    like($t->read_file('connect_error.log'),\n         qr/proxy_connect: upstream connect timed out \\(peer:8\\.8\\.8\\.8:8888\\) while connecting to upstream/,\n        'connect timed out error log');\n}\n\n# Both $proxy_connect_resolve_time & $proxy_connect_connect_time are empty string.\nhttp_get('/200');\nlike($t->read_file('connect.log'),\n     qr/GET \\/200.*resolve:,connect:,/,\n     'For GET request, both $proxy_connect_resolve_time & $proxy_connect_connect_time are empty string');\n\n\n# Both $proxy_connect_resolve_time & $proxy_connect_connect_time have value.\nhttp_connect_request('127.0.0.1', '8081', '/');\nlike($t->read_file('connect.log'),\n     qr/\"CONNECT 127.0.0.1:8081 HTTP\\/1.1\" 200 .+ resolve:0\\.\\d+,connect:0\\.\\d+,/,\n     'For CONNECT request, test both $proxy_connect_resolve_time & $proxy_connect_connect_time');\n\n# DNS resolving fails. Both $proxy_connect_resolve_time & $proxy_connect_connect_time are \"-\".\nhttp_connect_request('non-existent-domain.com', '8081', '/');\nlike($t->read_file('connect.log'),\n     qr/\"CONNECT non-existent-domain.com:8081 HTTP\\/1.1\" 502 .+ resolve:-,connect:-,/,\n     'For CONNECT request, test both $proxy_connect_resolve_time & $proxy_connect_connect_time');\nlike($t->read_file('connect_error.log'),\n     qr/proxy_connect: non-existent-domain.com could not be resolved .+Host not found/,\n     'test error.log for 502 respsone');\n\n# test first byte time\n# fbt:~1s\nmy $r;\n$r = http_connect_request('127.0.0.1', '8082', '/');\nlike($r, qr/8082 server fbt/, \"test first byte time: 1s, receive response from backend server\");\nlike($t->read_file('connect.log'),\n     qr/\"CONNECT 127.0.0.1:8082 HTTP\\/1.1\" 200 .+ resolve:0\\....,connect:0\\....,fbt:1\\....,/,\n     'test first byte time: 1s');\n\n# fbt:~0.5s\n$r = http_connect_request('127.0.0.1', '8083', '/');\nlike($r, qr/8083 server fbt/, \"test first byte time: 0.5s, receive response from backend server\");\n\nlike($t->read_file('connect.log'),\n     qr/\"CONNECT 127.0.0.1:8083 HTTP\\/1.1\" 200 .+ resolve:0\\....,connect:0\\....,fbt:0\\.5..,/,\n     'test first byte time: 0.5s');\n\n# test timeout\n$t->write_file('connect_error.log', \"\");\n$r = http_connect_request('127.0.0.01', '8082', '/');\nis($r, \"\", \"test first byte time: 1s, timeout\");\n#'2022/11/24 20:51:13 [info] 15239#0: *15 proxy_connect: connection timed out (110: Connection timed out) while proxying connection, client: 127.0.0.1, server: localhost, request: \"CONNECT 127.0.0.01:8082 HTTP/1.1\", host: \"127.0.0.01\"\nlike($t->read_file('connect_error.log'),\n     qr/\\[info\\].* proxy_connect: connection timed out.+ request: \"CONNECT 127\\.0\\.0\\.01:8082 HTTP\\/...\"/,\n     'test first byte time: 1s, check timeout in error log');\n\n$t->write_file('connect_error.log', \"\");\n$r = http_connect_request('127.0.0.01', '8083', '/');\nis($r, \"\", \"test first byte time: 0.5s, timeout\");\nlike($t->read_file('connect_error.log'),\n     qr/\\[info\\].* proxy_connect: connection timed out.+ request: \"CONNECT 127\\.0\\.0\\.01:8083 HTTP\\/...\"/,\n     'test first byte time: 1s, check timeout in error log');\n\n$t->stop();\n\n###############################################################################\n\nsub http_connect_request {\n    my ($host, $port, $url) = @_;\n    my $r = http_connect($host, $port, <<EOF);\nGET $url HTTP/1.0\nHost: $host\nConnection: close\n\nEOF\n    return $r\n}\n\nsub http_connect($;%) {\n    my ($host, $port, $request, %extra) = @_;\n    my $reply;\n    eval {\n        local $SIG{ALRM} = sub { die \"timeout\\n\" };\n        local $SIG{PIPE} = sub { die \"sigpipe\\n\" };\n        alarm(2);\n        my $s = IO::Socket::INET->new(\n            Proto => 'tcp',\n            PeerAddr => '127.0.0.1:8080'\n        );\n        $s->print(<<EOF);\nCONNECT $host:$port HTTP/1.1\nHost: $host\n\nEOF\n        select undef, undef, undef, $extra{sleep} if $extra{sleep};\n        return '' if $extra{aborted};\n        my $n = $s->sysread($reply, 65536);\n        return unless $n;\n        if ($reply !~ /HTTP\\/1\\.[01] 200 Connection Established\\r\\nProxy-agent: .+\\r\\n\\r\\n/) {\n            return $reply;\n        }\n        log_out($request);\n        $s->print($request);\n        local $/;\n        select undef, undef, undef, $extra{sleep} if $extra{sleep};\n        return '' if $extra{aborted};\n        $reply = $s->getline();\n        alarm(0);\n    };\n    alarm(0);\n    if ($@) {\n        log_in(\"died: $@\");\n        return undef;\n    }\n    log_in($reply);\n    return $reply;\n}\n\n###############################################################################\n\nsub reply_handler {\n\tmy ($recv_data, $port, $state, %extra) = @_;\n\n\tmy (@name, @rdata);\n\n\tuse constant NOERROR\t=> 0;\n\tuse constant FORMERR\t=> 1;\n\tuse constant SERVFAIL\t=> 2;\n\tuse constant NXDOMAIN\t=> 3;\n\n\tuse constant A\t\t=> 1;\n\tuse constant CNAME\t=> 5;\n\tuse constant DNAME\t=> 39;\n\n\tuse constant IN\t\t=> 1;\n\n\t# default values\n\n\tmy ($hdr, $rcode, $ttl) = (0x8180, NOERROR, 3600);\n\n\t# decode name\n\n\tmy ($len, $offset) = (undef, 12);\n\twhile (1) {\n\t\t$len = unpack(\"\\@$offset C\", $recv_data);\n\t\tlast if $len == 0;\n\t\t$offset++;\n\t\tpush @name, unpack(\"\\@$offset A$len\", $recv_data);\n\t\t$offset += $len;\n\t}\n\n\t$offset -= 1;\n\tmy ($id, $type, $class) = unpack(\"n x$offset n2\", $recv_data);\n\n\tmy $name = join('.', @name);\n\n        if (($type == A) && exists($aroute_map{$name})) {\n\n            my @records = @{$aroute_map{$name}};\n\n            for (my $i = 0; $i < scalar(@records); $i++) {\n                my ($ttl, $origin_addr) = @{$records[$i]};\n                push @rdata, rd_addr($ttl, $origin_addr);\n\n                #print(\"dns reply: $name $ttl $class $type $origin_addr\\n\");\n            }\n        }\n\n\t$len = @name;\n\tpack(\"n6 (C/a*)$len x n2\", $id, $hdr | $rcode, 1, scalar @rdata,\n\t\t0, 0, @name, $type, $class) . join('', @rdata);\n}\n\nsub rd_addr {\n\tmy ($ttl, $addr) = @_;\n\n\tmy $code = 'split(/\\./, $addr)';\n\n\treturn pack 'n3N', 0xc00c, A, IN, $ttl if $addr eq '';\n\n\tpack 'n3N nC4', 0xc00c, A, IN, $ttl, eval \"scalar $code\", eval($code);\n}\n\nsub dns_daemon {\n\tmy ($port, $t, %extra) = @_;\n\n        print(\"+ dns daemon: try to listen on 127.0.0.1:$port\\n\");\n\n\tmy ($data, $recv_data);\n\tmy $socket = IO::Socket::INET->new(\n\t\tLocalAddr => '127.0.0.1',\n\t\tLocalPort => $port,\n\t\tProto => 'udp',\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\tmy $sel = IO::Select->new($socket);\n\tmy $tcp = 0;\n\n\tif ($extra{tcp}) {\n\t\t$tcp = port(8983, socket => 1);\n\t\t$sel->add($tcp);\n\t}\n\n\tlocal $SIG{PIPE} = 'IGNORE';\n\n\t# track number of relevant queries\n\n\tmy %state = (\n\t\tcnamecnt\t=> 0,\n\t\ttwocnt\t\t=> 0,\n\t\tttlcnt\t\t=> 0,\n\t\tttl0cnt\t\t=> 0,\n\t\tcttlcnt\t\t=> 0,\n\t\tcttl2cnt\t=> 0,\n\t\tmanycnt\t\t=> 0,\n\t\tcasecnt\t\t=> 0,\n\t\tidcnt\t\t=> 0,\n\t\tfecnt\t\t=> 0,\n\t);\n\n\t# signal we are ready\n\n\topen my $fh, '>', $t->testdir() . '/' . $port;\n\tclose $fh;\n\n\twhile (my @ready = $sel->can_read) {\n\t\tforeach my $fh (@ready) {\n\t\t\tif ($tcp == $fh) {\n\t\t\t\tmy $new = $fh->accept;\n\t\t\t\t$new->autoflush(1);\n\t\t\t\t$sel->add($new);\n\n\t\t\t} elsif ($socket == $fh) {\n\t\t\t\t$fh->recv($recv_data, 65536);\n\t\t\t\t$data = reply_handler($recv_data, $port,\n\t\t\t\t\t\\%state);\n\t\t\t\t$fh->send($data);\n\n\t\t\t} else {\n\t\t\t\t$fh->recv($recv_data, 65536);\n\t\t\t\tunless (length $recv_data) {\n\t\t\t\t\t$sel->remove($fh);\n\t\t\t\t\t$fh->close;\n\t\t\t\t\tnext;\n\t\t\t\t}\n\nagain:\n\t\t\t\tmy $len = unpack(\"n\", $recv_data);\n\t\t\t\t$data = substr $recv_data, 2, $len;\n\t\t\t\t$data = reply_handler($data, $port, \\%state,\n\t\t\t\t\ttcp => 1);\n\t\t\t\t$data = pack(\"n\", length $data) . $data;\n\t\t\t\t$fh->send($data);\n\t\t\t\t$recv_data = substr $recv_data, 2 + $len;\n\t\t\t\tgoto again if length $recv_data;\n\t\t\t}\n\t\t}\n\t}\n}\n\n###############################################################################\n"
  },
  {
    "path": "modules/ngx_http_proxy_connect_module/t/http_proxy_connect_timeout.t",
    "content": "#!/usr/bin/perl\n\n# Copyright (C) 2010-2013 Alibaba Group Holding Limited\n\n# Tests for connect method support.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\nuse Time::HiRes;\n# use Test::Simple 'no_plan';\nuse Test::Nginx::Stream qw/ stream /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse Net::DNS::Nameserver;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy/)->plan(16);\n\n###############################################################################\n\nmy $nginx_conf = <<'EOF';\n%%TEST_GLOBALS%%\ndaemon         off;\nevents { }\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    #LUA_PACKAGE_PATH\n    # If you build nginx with lua-nginx-module, please enable\n    # directive \"lua_package_path\". For more details, see:\n    #  https://github.com/openresty/lua-nginx-module#installation\n    #lua_package_path \"/path/to/lib/lua/?.lua;;\";\n\n    log_format connect '$remote_addr - $remote_user [$time_local] \"$request\" '\n                       '$status $body_bytes_sent var:$connect_host-$connect_port-$connect_addr '\n                       ' c:$proxy_connect_connect_timeout,r:$proxy_connect_data_timeout';\n\n    access_log %%TESTDIR%%/connect.log connect;\n    error_log %%TESTDIR%%/connect_error.log error;\n\n    resolver 127.0.0.1:%%PORT_8981_UDP%% ipv6=off;      # NOTE: cannot connect ipv6 address ::1 in mac os x.\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        # forward proxy for CONNECT method\n        proxy_connect;\n        proxy_connect_allow all;\n        proxy_connect_connect_timeout 10s;\n        proxy_connect_data_timeout 10s;\n\n        set $proxy_connect_connect_timeout  \"101ms\";\n        set $proxy_connect_data_timeout     \"103ms\";\n\n        if ($host = \"test-connect-timeout.com\") {\n            set $proxy_connect_connect_timeout \"1ms\";\n        }\n        if ($host = \"test-read-timeout.com\") {\n            set $proxy_connect_connect_timeout  \"3ms\";\n            set $proxy_connect_data_timeout     \"1ms\";\n        }\n\n        location / {\n            proxy_pass http://127.0.0.01:8081;\n        }\n    }\n}\n\nEOF\n\n$t->write_file_expand('nginx.conf', $nginx_conf);\n\neval {\n    $t->run();\n};\n\nif ($@) {\n    print(\"+ Retry new nginx conf: remove \\\"ipv6=off\\\"\\n\");\n\n    $nginx_conf =~ s/ ipv6=off;/;/g;        # remove ipv6=off in resolver directive.\n    $t->write_file_expand('nginx.conf', $nginx_conf);\n    $t->run();\n}\n\n$t->run_daemon(\\&dns_daemon, port(8981), $t);\n$t->waitforfile($t->testdir . '/' . port(8981));\n\n\nTODO: {\n    local $TODO = '# This case will pass, if connecting 8.8.8.8 timed out.';\n    like(http_connect_request('test-connect-timeout.com', '8888', '/'), qr/504/, 'connect timed out: set $var');\n    like($t->read_file('connect.log'),\n         qr/\"CONNECT test-connect-timeout.com:8888 HTTP\\/1.1\" 504 .+ c:1,r:103/,\n        'connect timed out log: get $var & status=504');\n    like($t->read_file('connect_error.log'),\n         qr/proxy_connect: upstream connect timed out \\(peer:8\\.8\\.8\\.8:8888\\) while connecting to upstream/,\n        'connect timed out error log');\n}\n\nhttp_connect_request('test-read-timeout.com', '8888', '/');\n\n# test reading variables of $proxy_connect_*_timeout\nlike($t->read_file('connect.log'),\n     qr/\"CONNECT test-connect-timeout.com:8888 HTTP\\/1.1\" ... .+ c:1,r:103/,\n     'connect timed out log: get $var');\nlike($t->read_file('connect.log'),\n     qr/\"CONNECT test-read-timeout.com:8888 HTTP\\/1.1\" ... .+ c:3,r:1/,\n     'connect/read timed out log: get $var');\n\n$t->stop();\n\n###############################################################################\n\n$nginx_conf = <<'EOF';\n%%TEST_GLOBALS%%\ndaemon         off;\nevents { }\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    #LUA_PACKAGE_PATH\n    # If you build nginx with lua-nginx-module, please enable\n    # directive \"lua_package_path\". For more details, see:\n    #  https://github.com/openresty/lua-nginx-module#installation\n    #lua_package_path \"/path/to/lib/lua/?.lua;;\";\n\n    log_format connect '$remote_addr - $remote_user [$time_local] \"$request\" '\n                       '$status $body_bytes_sent var:$connect_host-$connect_port-$connect_addr '\n                       ' c:$proxy_connect_connect_timeout,r:$proxy_connect_data_timeout';\n\n    access_log %%TESTDIR%%/connect.log connect;\n    error_log %%TESTDIR%%/connect_timeout_error.log debug;\n\n    resolver 127.0.0.1:8085 ipv6=off;      # NOTE: cannot connect ipv6 address ::1 in mac os x.\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        # forward proxy for CONNECT method\n        proxy_connect;\n        proxy_connect_allow all;\n\n        proxy_connect_data_timeout 10s;\n\n        proxy_connect_address \"127.0.0.1:8081\";\n\n        if ($http_x_timeout) {\n            set $proxy_connect_data_timeout     $http_x_timeout;\n        }\n\n        location / {\n            proxy_pass http://127.0.0.1:8081;\n        }\n    }\n}\n\nEOF\n\n$t->write_file_expand('nginx.conf', $nginx_conf);\n\n$t->run_daemon(\\&stream_daemon);\n\neval {\n    $t->run();\n};\n\nif ($@) {\n    print(\"+ Retry new nginx conf: remove \\\"ipv6=off\\\"\\n\");\n\n    $nginx_conf =~ s/ ipv6=off;/;/g;        # remove ipv6=off in resolver directive.\n    $t->write_file_expand('nginx.conf', $nginx_conf);\n    $t->run();\n}\n\nprint(\"+ try to waitforsocket...\\n\");\n$t->waitforsocket('127.0.0.1:' . port(8081))\n  or die \"Can't start stream backend server\";\nprint(\"+ try to waitforsocket...done\\n\");\n\n# test time out of data proxying ( proxy_connect_data_timeout)\nmy $str = '1234567890' x 10 . 'F';\nmy $length = length($str);\nmy $sport =  port(8081);\nmy $s;\n\n# test: timeout expired or not\n\n$s = stream('127.0.0.1:' . port(8080));\n\nlike($s->io(<<EOF, read_timeout => 0.5), qr/200 Connection Established/, \"establish CONNECT tunnel\");\nCONNECT 127.0.0.1:$sport HTTP/1.1\nHost: stream\nX-Timeout: 1000ms\n\nEOF\n\nmy $i;\nfor ($i = 0; $i < 8; $i++) {\n    my $ms = $i/10;\n    Time::HiRes::sleep($ms);    # < timeout\n    is($s->io($str, length => $length), $str,\n       \"timeout not expired, then sleep $ms s\");\n}\n\n# check log\n#2022/11/29 13:49:28 [info] 1746#0: *3 proxy_connect: connection timed out (110: Connection timed out) while proxying connection, client: 127.0.0.1, server: localhost, request: \"CONNECT 127.0.0.1:8081 HTTP/1.1\", host: \"stream\"\nunlike($t->read_file('connect_timeout_error.log'),\n     qr/proxy_connect: connection timed out .* \"CONNECT .*\", host: \"stream\"/,\n     'get log: not timed out');\n\nTime::HiRes::sleep(1.2);    # > timeout\nlike($t->read_file('connect_timeout_error.log'),\n     qr/proxy_connect: connection timed out .* \"CONNECT .*\", host: \"stream\"/,\n     'get log: timed out');\n\n$t->stop();\n\n###############################################################################\n\n\nsub http_connect_request {\n    my ($host, $port, $url) = @_;\n    my $r = http_connect($host, $port, <<EOF);\nGET $url HTTP/1.0\nHost: $host\nConnection: close\n\nEOF\n    return $r\n}\n\nsub http_connect($;%) {\n    my ($host, $port, $request, %extra) = @_;\n    my $reply;\n    eval {\n        local $SIG{ALRM} = sub { die \"timeout\\n\" };\n        local $SIG{PIPE} = sub { die \"sigpipe\\n\" };\n        alarm(2);\n        my $s = IO::Socket::INET->new(\n            Proto => 'tcp',\n            PeerAddr => '127.0.0.1:8080'\n        );\n        $s->print(<<EOF);\nCONNECT $host:$port HTTP/1.1\nHost: $host\n\nEOF\n        select undef, undef, undef, $extra{sleep} if $extra{sleep};\n        return '' if $extra{aborted};\n        my $n = $s->sysread($reply, 65536);\n        return unless $n;\n        if ($reply !~ /HTTP\\/1\\.[01] 200 Connection Established\\r\\nProxy-agent: .+\\r\\n\\r\\n/) {\n            return $reply;\n        }\n        log_out($request);\n        $s->print($request);\n        local $/;\n        select undef, undef, undef, $extra{sleep} if $extra{sleep};\n        return '' if $extra{aborted};\n        $reply = $s->getline();\n        alarm(0);\n    };\n    alarm(0);\n    if ($@) {\n        log_in(\"died: $@\");\n        return undef;\n    }\n    log_in($reply);\n    return $reply;\n}\n\n# --- DNS Server ---\n\nsub reply_handler {\n\tmy ($recv_data, $port, $state) = @_;\n\n\tmy (@name, @rdata);\n\n\tuse constant NOERROR\t=> 0;\n\tuse constant SERVFAIL\t=> 2;\n\tuse constant NXDOMAIN\t=> 3;\n\n\tuse constant A\t\t=> 1;\n\tuse constant CNAME\t=> 5;\n\tuse constant AAAA\t=> 28;\n\tuse constant DNAME\t=> 39;\n\n\tuse constant IN\t\t=> 1;\n\n\t# default values\n\n\tmy ($hdr, $rcode, $ttl) = (0x8180, NOERROR, 3600);\n\n\t# decode name\n\n\tmy ($len, $offset) = (undef, 12);\n\twhile (1) {\n\t\t$len = unpack(\"\\@$offset C\", $recv_data);\n\t\tlast if $len == 0;\n\t\t$offset++;\n\t\tpush @name, unpack(\"\\@$offset A$len\", $recv_data);\n\t\t$offset += $len;\n\t}\n\n\t$offset -= 1;\n\tmy ($id, $type, $class) = unpack(\"n x$offset n2\", $recv_data);\n\n\tmy $name = join('.', @name);\n\n\tif (($name eq 'test-connect-timeout.com') || \n            ($name eq 'test-read-timeout.com')) \n        {\n\t\tif ($type == A) {\n\t\t\tpush @rdata, rd_addr($ttl, '8.8.8.8');\n\t\t}\n\t}\n\n\t$len = @name;\n\tpack(\"n6 (C/a*)$len x n2\", $id, $hdr | $rcode, 1, scalar @rdata,\n\t\t0, 0, @name, $type, $class) . join('', @rdata);\n}\n\nsub rd_addr {\n\tmy ($ttl, $addr) = @_;\n\n\tmy $code = 'split(/\\./, $addr)';\n\n\tpack 'n3N nC4', 0xc00c, A, IN, $ttl, eval \"scalar $code\", eval($code);\n}\n\nsub expand_ip6 {\n\tmy ($addr) = @_;\n\n\tsubstr ($addr, index($addr, \"::\"), 2) =\n\t\tjoin \"0\", map { \":\" } (0 .. 8 - (split /:/, $addr) + 1);\n\tmap { hex \"0\" x (4 - length $_) . \"$_\" } split /:/, $addr;\n}\n\nsub rd_addr6 {\n\tmy ($ttl, $addr) = @_;\n\n\tpack 'n3N nn8', 0xc00c, AAAA, IN, $ttl, 16, expand_ip6($addr);\n}\n\nsub dns_daemon {\n\tmy ($port, $t) = @_;\n\n\tmy ($data, $recv_data);\n\tmy $socket = IO::Socket::INET->new(\n\t\tLocalAddr => '127.0.0.1',\n\t\tLocalPort => $port,\n\t\tProto => 'udp',\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\t# track number of relevant queries\n\n\tmy %state = (\n\t\tcnamecnt\t=> 0,\n\t\ttwocnt\t\t=> 0,\n\t\tmanycnt\t\t=> 0,\n\t);\n\n\t# signal we are ready\n\n\topen my $fh, '>', $t->testdir() . '/' . $port;\n\tclose $fh;\n\n\twhile (1) {\n\t\t$socket->recv($recv_data, 65536);\n\t\t$data = reply_handler($recv_data, $port, \\%state);\n\t\t$socket->send($data);\n\t}\n}\n\n################################################################################\n\nsub stream_daemon {\n\tmy $server = IO::Socket::INET->new(\n\t\tProto => 'tcp',\n\t\tLocalAddr => '127.0.0.1:' . port(8081),\n\t\tListen => 5,\n\t\tReuse => 1\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n        print(\"+ stream daemon started.\\n\");\n\n\tmy $sel = IO::Select->new($server);\n\n\tlocal $SIG{PIPE} = 'IGNORE';\n\n\twhile (my @ready = $sel->can_read) {\n\t\tforeach my $fh (@ready) {\n\t\t\tif ($server == $fh) {\n\t\t\t\tmy $new = $fh->accept;\n\t\t\t\t$new->autoflush(1);\n\t\t\t\t$sel->add($new);\n\n\t\t\t} elsif (stream_handle_client($fh)) {\n\t\t\t\t$sel->remove($fh);\n\t\t\t\t$fh->close;\n\t\t\t}\n\t\t}\n\t}\n}\n\nsub stream_handle_client {\n\tmy ($client) = @_;\n\n\tlog2c(\"(new connection $client)\");\n\n\t$client->sysread(my $buffer, 65536) or return 1;\n\n        log2i(\"$client $buffer\");\n        $client->syswrite($buffer);\n        return 0;\n\n        log2i(\"$client $buffer\");\n\n\tmy $close = $buffer =~ /F/;\n\n\tlog2o(\"$client $buffer\");\n\n\t$client->syswrite($buffer);\n\n\treturn $close;\n}\n\nsub log2i { Test::Nginx::log_core('|| <<', @_); }\nsub log2o { Test::Nginx::log_core('|| >>', @_); }\nsub log2c { Test::Nginx::log_core('||', @_); }\n##############################################################################\n"
  },
  {
    "path": "modules/ngx_http_reqstat_module/config",
    "content": "ngx_addon_name=ngx_http_reqstat_module\nHTTP_REQSTAT_SRCS=\"$ngx_addon_dir/ngx_http_reqstat_module.c\"\nHTTP_REQSTAT_DEPS=\"$ngx_addon_dir/ngx_http_reqstat.h\"\n\nif test -n \"$ngx_module_link\"; then\n    ngx_module_type=HTTP\n    ngx_module_name=$ngx_addon_name\n    ngx_module_deps=\"$HTTP_REQSTAT_DEPS\"\n    ngx_module_srcs=\"$HTTP_REQSTAT_SRCS\"\n\n    . auto/module\nelse\n    HTTP_MODULES=\"$HTTP_MODULES ngx_http_reqstat_module\"\n    NGX_ADDON_SRCS=\"$NGX_ADDON_SRCS $HTTP_REQSTAT_SRCS\"\n    NGX_ADDON_DEPS=\"$NGX_ADDON_DEPS $HTTP_REQSTAT_DEPS\"\n    HTTP_INCS=\"$HTTP_INCS $ngx_addon_dir\"\nfi\n\nhave=T_NGX_REQ_STATUS . auto/have\n"
  },
  {
    "path": "modules/ngx_http_reqstat_module/ngx_http_reqstat.h",
    "content": "#include <nginx.h>\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\n#define NGX_HTTP_REQSTAT_RSRV    29\n#define NGX_HTTP_REQSTAT_MAX     50\n#define NGX_HTTP_REQSTAT_USER    NGX_HTTP_REQSTAT_MAX - NGX_HTTP_REQSTAT_RSRV\n\n\n#define variable_index(str, index)  { ngx_string(str), index }\n\ntypedef struct ngx_http_reqstat_rbnode_s ngx_http_reqstat_rbnode_t;\n\ntypedef struct variable_index_s variable_index_t;\n\nstruct variable_index_s {\n    ngx_str_t                    name;\n    ngx_int_t                    index;\n};\n\nstruct ngx_http_reqstat_rbnode_s {\n    u_char                       color;\n    u_char                       padding[3];\n    uint32_t                     len;\n\n    ngx_queue_t                  queue;\n    ngx_queue_t                  visit;\n\n    ngx_atomic_t                 bytes_in;\n    ngx_atomic_t                 bytes_out;\n    ngx_atomic_t                 conn_total;\n    ngx_atomic_t                 req_total;\n    ngx_atomic_t                 http_2xx;\n    ngx_atomic_t                 http_3xx;\n    ngx_atomic_t                 http_4xx;\n    ngx_atomic_t                 http_5xx;\n    ngx_atomic_t                 other_status;\n    ngx_atomic_t                 http_200;\n    ngx_atomic_t                 http_206;\n    ngx_atomic_t                 http_302;\n    ngx_atomic_t                 http_304;\n    ngx_atomic_t                 http_403;\n    ngx_atomic_t                 http_404;\n    ngx_atomic_t                 http_416;\n    ngx_atomic_t                 http_499;\n    ngx_atomic_t                 http_500;\n    ngx_atomic_t                 http_502;\n    ngx_atomic_t                 http_503;\n    ngx_atomic_t                 http_504;\n    ngx_atomic_t                 http_508;\n    ngx_atomic_t                 other_detail_status;\n    ngx_atomic_t                 http_ups_4xx;\n    ngx_atomic_t                 http_ups_5xx;\n    ngx_atomic_t                 rt;\n    ngx_atomic_t                 ureq;\n    ngx_atomic_t                 urt;\n    ngx_atomic_t                 utries;\n    ngx_atomic_t                 extra[NGX_HTTP_REQSTAT_USER];\n\n    ngx_atomic_int_t             excess;\n\n    ngx_msec_t                   last_visit;\n\n    u_char                       data[1];\n};\n\n\ntypedef struct {\n    ngx_flag_t                   lazy;\n    ngx_array_t                 *monitor;\n    ngx_array_t                 *display;\n    ngx_array_t                 *bypass;\n    ngx_int_t                    index;\n    ngx_array_t                 *user_select;\n    ngx_array_t                 *user_defined_str;\n} ngx_http_reqstat_conf_t;\n\n\ntypedef struct {\n    ngx_rbtree_t                 rbtree;\n    ngx_rbtree_node_t            sentinel;\n    ngx_queue_t                  queue;\n    ngx_queue_t                  visit;\n} ngx_http_reqstat_shctx_t;\n\n\ntypedef struct {\n    ngx_str_t                   *val;\n    ngx_slab_pool_t             *shpool;\n    ngx_http_reqstat_shctx_t    *sh;\n    ngx_http_complex_value_t     value;\n    ngx_array_t                 *user_defined;\n    ngx_int_t                    key_len;\n    ngx_uint_t                   recycle_rate;\n    ngx_int_t                    alloc_already_fail;\n} ngx_http_reqstat_ctx_t;\n\n\ntypedef struct {\n    ngx_uint_t                   recv;\n    ngx_uint_t                   sent;\n    ngx_array_t                  monitor_index;\n    ngx_array_t                  value_index;\n    ngx_flag_t                   bypass;\n    ngx_http_reqstat_conf_t     *conf;\n} ngx_http_reqstat_store_t;\n\n\n#define NGX_HTTP_REQSTAT_BYTES_IN                                       \\\n    offsetof(ngx_http_reqstat_rbnode_t, bytes_in)\n\n#define NGX_HTTP_REQSTAT_BYTES_OUT                                      \\\n    offsetof(ngx_http_reqstat_rbnode_t, bytes_out)\n\n#define NGX_HTTP_REQSTAT_CONN_TOTAL                                     \\\n    offsetof(ngx_http_reqstat_rbnode_t, conn_total)\n\n#define NGX_HTTP_REQSTAT_REQ_TOTAL                                      \\\n    offsetof(ngx_http_reqstat_rbnode_t, req_total)\n\n#define NGX_HTTP_REQSTAT_2XX                                            \\\n    offsetof(ngx_http_reqstat_rbnode_t, http_2xx)\n\n#define NGX_HTTP_REQSTAT_3XX                                            \\\n    offsetof(ngx_http_reqstat_rbnode_t, http_3xx)\n\n#define NGX_HTTP_REQSTAT_4XX                                            \\\n    offsetof(ngx_http_reqstat_rbnode_t, http_4xx)\n\n#define NGX_HTTP_REQSTAT_5XX                                            \\\n    offsetof(ngx_http_reqstat_rbnode_t, http_5xx)\n\n#define NGX_HTTP_REQSTAT_OTHER_STATUS                                   \\\n    offsetof(ngx_http_reqstat_rbnode_t, other_status)\n\n#define NGX_HTTP_REQSTAT_200                                            \\\n    offsetof(ngx_http_reqstat_rbnode_t, http_200)\n\n#define NGX_HTTP_REQSTAT_206                                            \\\n    offsetof(ngx_http_reqstat_rbnode_t, http_206)\n\n#define NGX_HTTP_REQSTAT_302                                            \\\n    offsetof(ngx_http_reqstat_rbnode_t, http_302)\n\n#define NGX_HTTP_REQSTAT_304                                            \\\n    offsetof(ngx_http_reqstat_rbnode_t, http_304)\n\n#define NGX_HTTP_REQSTAT_403                                            \\\n    offsetof(ngx_http_reqstat_rbnode_t, http_403)\n\n#define NGX_HTTP_REQSTAT_404                                            \\\n    offsetof(ngx_http_reqstat_rbnode_t, http_404)\n\n#define NGX_HTTP_REQSTAT_416                                            \\\n    offsetof(ngx_http_reqstat_rbnode_t, http_416)\n\n#define NGX_HTTP_REQSTAT_499                                            \\\n    offsetof(ngx_http_reqstat_rbnode_t, http_499)\n\n#define NGX_HTTP_REQSTAT_500                                            \\\n    offsetof(ngx_http_reqstat_rbnode_t, http_500)\n\n#define NGX_HTTP_REQSTAT_502                                            \\\n    offsetof(ngx_http_reqstat_rbnode_t, http_502)\n\n#define NGX_HTTP_REQSTAT_503                                            \\\n    offsetof(ngx_http_reqstat_rbnode_t, http_503)\n\n#define NGX_HTTP_REQSTAT_504                                            \\\n    offsetof(ngx_http_reqstat_rbnode_t, http_504)\n\n#define NGX_HTTP_REQSTAT_508                                            \\\n    offsetof(ngx_http_reqstat_rbnode_t, http_508)\n\n#define NGX_HTTP_REQSTAT_OTHER_DETAIL_STATUS                            \\\n    offsetof(ngx_http_reqstat_rbnode_t, other_detail_status)\n\n#define NGX_HTTP_REQSTAT_RT                                             \\\n    offsetof(ngx_http_reqstat_rbnode_t, rt)\n\n#define NGX_HTTP_REQSTAT_UPS_REQ                                        \\\n    offsetof(ngx_http_reqstat_rbnode_t, ureq)\n\n#define NGX_HTTP_REQSTAT_UPS_RT                                         \\\n    offsetof(ngx_http_reqstat_rbnode_t, urt)\n\n#define NGX_HTTP_REQSTAT_UPS_TRIES                                      \\\n    offsetof(ngx_http_reqstat_rbnode_t, utries)\n\n#define NGX_HTTP_REQSTAT_UPS_4XX                                        \\\n    offsetof(ngx_http_reqstat_rbnode_t, http_ups_4xx)\n\n#define NGX_HTTP_REQSTAT_UPS_5XX                                        \\\n    offsetof(ngx_http_reqstat_rbnode_t, http_ups_5xx)\n\n#define NGX_HTTP_REQSTAT_EXTRA(slot)                                    \\\n    (offsetof(ngx_http_reqstat_rbnode_t, extra)                         \\\n         + sizeof(ngx_atomic_t) * slot)\n\n#define NGX_HTTP_REQSTAT_REQ_FIELD(node, offset)                        \\\n    ((ngx_atomic_t *) ((char *) node + offset))\n\n\nngx_http_reqstat_rbnode_t *\n    ngx_http_reqstat_rbtree_lookup(ngx_shm_zone_t *shm_zone, ngx_str_t *val);\n"
  },
  {
    "path": "modules/ngx_http_reqstat_module/ngx_http_reqstat_module.c",
    "content": "/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#include \"ngx_http_reqstat.h\"\n\n\nstatic ngx_http_input_body_filter_pt  ngx_http_next_input_body_filter;\nextern ngx_int_t (*ngx_http_write_filter_stat)(ngx_http_request_t *r);\n\n\nstatic variable_index_t REQSTAT_RSRV_VARIABLES[NGX_HTTP_REQSTAT_RSRV] = {\n    variable_index(\"bytes_in\", 0),\n    variable_index(\"bytes_out\", 1),\n    variable_index(\"conn_total\", 2),\n    variable_index(\"req_total\", 3),\n    variable_index(\"http_2xx\", 4),\n    variable_index(\"http_3xx\", 5),\n    variable_index(\"http_4xx\", 6),\n    variable_index(\"http_5xx\", 7),\n    variable_index(\"http_other_status\", 8),\n    variable_index(\"rt\", 9),\n    variable_index(\"ups_req\", 10),\n    variable_index(\"ups_rt\", 11),\n    variable_index(\"ups_tries\", 12),\n    variable_index(\"http_200\", 13),\n    variable_index(\"http_206\", 14),\n    variable_index(\"http_302\", 15),\n    variable_index(\"http_304\", 16),\n    variable_index(\"http_403\", 17),\n    variable_index(\"http_404\", 18),\n    variable_index(\"http_416\", 19),\n    variable_index(\"http_499\", 20),\n    variable_index(\"http_500\", 21),\n    variable_index(\"http_502\", 22),\n    variable_index(\"http_503\", 23),\n    variable_index(\"http_504\", 24),\n    variable_index(\"http_508\", 25),\n    variable_index(\"http_other_detail_status\", 26),\n    variable_index(\"http_ups_4xx\", 27),\n    variable_index(\"http_ups_5xx\", 28),\n};\n\n\noff_t ngx_http_reqstat_fields[29] = {\n    NGX_HTTP_REQSTAT_BYTES_IN,\n    NGX_HTTP_REQSTAT_BYTES_OUT,\n    NGX_HTTP_REQSTAT_CONN_TOTAL,\n    NGX_HTTP_REQSTAT_REQ_TOTAL,\n    NGX_HTTP_REQSTAT_2XX,\n    NGX_HTTP_REQSTAT_3XX,\n    NGX_HTTP_REQSTAT_4XX,\n    NGX_HTTP_REQSTAT_5XX,\n    NGX_HTTP_REQSTAT_OTHER_STATUS,\n    NGX_HTTP_REQSTAT_RT,\n    NGX_HTTP_REQSTAT_UPS_REQ,\n    NGX_HTTP_REQSTAT_UPS_RT,\n    NGX_HTTP_REQSTAT_UPS_TRIES,\n    NGX_HTTP_REQSTAT_200,\n    NGX_HTTP_REQSTAT_206,\n    NGX_HTTP_REQSTAT_302,\n    NGX_HTTP_REQSTAT_304,\n    NGX_HTTP_REQSTAT_403,\n    NGX_HTTP_REQSTAT_404,\n    NGX_HTTP_REQSTAT_416,\n    NGX_HTTP_REQSTAT_499,\n    NGX_HTTP_REQSTAT_500,\n    NGX_HTTP_REQSTAT_502,\n    NGX_HTTP_REQSTAT_503,\n    NGX_HTTP_REQSTAT_504,\n    NGX_HTTP_REQSTAT_508,\n    NGX_HTTP_REQSTAT_OTHER_DETAIL_STATUS,\n    NGX_HTTP_REQSTAT_UPS_4XX,\n    NGX_HTTP_REQSTAT_UPS_5XX\n};\n\n\nstatic void *ngx_http_reqstat_create_main_conf(ngx_conf_t *cf);\nstatic void *ngx_http_reqstat_create_loc_conf(ngx_conf_t *cf);\nstatic char *ngx_http_reqstat_merge_loc_conf(ngx_conf_t *cf, void *parent,\n    void *child);\nstatic ngx_int_t ngx_http_reqstat_init(ngx_conf_t *cf);\nstatic ngx_int_t ngx_http_reqstat_add_variable(ngx_conf_t *cf);\n\nstatic ngx_int_t ngx_http_reqstat_variable_enable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\n\nstatic char *ngx_http_reqstat_show(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_http_reqstat_show_field(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_http_reqstat_zone(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_http_reqstat_zone_add_indicator(ngx_conf_t *cf,\n    ngx_command_t *cmd, void *conf);\nstatic char *ngx_http_reqstat_zone_key_length(ngx_conf_t *cf,\n    ngx_command_t *cmd, void *conf);\nstatic char *ngx_http_reqstat_zone_recycle(ngx_conf_t *cf,\n    ngx_command_t *cmd, void *conf);\nstatic char *ngx_http_reqstat(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic void ngx_http_reqstat_count(void *data, off_t offset,\n    ngx_int_t incr);\nstatic ngx_int_t ngx_http_reqstat_init_zone(ngx_shm_zone_t *shm_zone,\n    void *data);\n\nstatic ngx_int_t ngx_http_reqstat_log_handler(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_reqstat_init_handler(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_reqstat_show_handler(ngx_http_request_t *r);\n\nstatic void ngx_http_reqstat_rbtree_insert_value(ngx_rbtree_node_t *temp,\n    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);\nstatic ngx_http_reqstat_store_t *\n    ngx_http_reqstat_create_store(ngx_http_request_t *r,\n    ngx_http_reqstat_conf_t *rlcf);\nstatic ngx_int_t ngx_http_reqstat_check_enable(ngx_http_request_t *r,\n    ngx_http_reqstat_conf_t **rlcf, ngx_http_reqstat_store_t **store);\n\nstatic ngx_int_t ngx_http_reqstat_input_body_filter(ngx_http_request_t *r,\n    ngx_buf_t *buf);\n\nngx_int_t ngx_http_reqstat_log_flow(ngx_http_request_t *r);\n\n\nstatic ngx_command_t   ngx_http_reqstat_commands[] = {\n\n    { ngx_string(\"req_status_zone\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_2MORE,\n      ngx_http_reqstat_zone,\n      0,\n      0,\n      NULL },\n\n    { ngx_string(\"req_status\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,\n      ngx_http_reqstat,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"req_status_lazy\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_reqstat_conf_t, lazy),\n      NULL },\n\n    { ngx_string(\"req_status_bypass\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,\n      ngx_http_set_predicate_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_reqstat_conf_t, bypass),\n      NULL },\n\n    { ngx_string(\"req_status_show\"),\n      NGX_HTTP_LOC_CONF|NGX_CONF_ANY,\n      ngx_http_reqstat_show,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"req_status_show_field\"),\n      NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,\n      ngx_http_reqstat_show_field,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"req_status_zone_add_indicator\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_2MORE,\n      ngx_http_reqstat_zone_add_indicator,\n      0,\n      0,\n      NULL },\n\n    { ngx_string(\"req_status_zone_key_length\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE2,\n      ngx_http_reqstat_zone_key_length,\n      0,\n      0,\n      NULL },\n\n    { ngx_string(\"req_status_zone_recycle\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE3,\n      ngx_http_reqstat_zone_recycle,\n      0,\n      0,\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_reqstat_module_ctx = {\n    ngx_http_reqstat_add_variable,         /* preconfiguration */\n    ngx_http_reqstat_init,                 /* postconfiguration */\n\n    ngx_http_reqstat_create_main_conf,     /* create main configuration */\n    NULL,                                  /* init main configuration */\n\n    NULL,                                  /* create server configuration */\n    NULL,                                  /* merge server configuration */\n\n    ngx_http_reqstat_create_loc_conf,      /* create location configuration */\n    ngx_http_reqstat_merge_loc_conf        /* merge location configuration */\n};\n\n\nngx_module_t  ngx_http_reqstat_module = {\n    NGX_MODULE_V1,\n    &ngx_http_reqstat_module_ctx,          /* module context */\n    ngx_http_reqstat_commands,             /* 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 void *\nngx_http_reqstat_create_main_conf(ngx_conf_t *cf)\n{\n    return ngx_pcalloc(cf->pool, sizeof(ngx_http_reqstat_conf_t));\n}\n\n\nstatic void *\nngx_http_reqstat_create_loc_conf(ngx_conf_t *cf)\n{\n    ngx_http_reqstat_conf_t      *conf;\n\n    conf = ngx_palloc(cf->pool, sizeof(ngx_http_reqstat_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    conf->lazy = NGX_CONF_UNSET;\n    conf->bypass = NGX_CONF_UNSET_PTR;\n    conf->monitor = NGX_CONF_UNSET_PTR;\n    conf->display = NGX_CONF_UNSET_PTR;\n    conf->user_select = NGX_CONF_UNSET_PTR;\n    conf->user_defined_str = NGX_CONF_UNSET_PTR;\n\n    return conf;\n}\n\n\nstatic char *\nngx_http_reqstat_merge_loc_conf(ngx_conf_t *cf, void *parent,\n    void *child)\n{\n    ngx_http_reqstat_conf_t      *conf = child;\n    ngx_http_reqstat_conf_t      *prev = parent;\n\n    ngx_conf_merge_value(conf->lazy, prev->lazy, 0);\n    ngx_conf_merge_ptr_value(conf->bypass, prev->bypass, NULL);\n    ngx_conf_merge_ptr_value(conf->monitor, prev->monitor, NULL);\n    ngx_conf_merge_ptr_value(conf->display, prev->display, NULL);\n    ngx_conf_merge_ptr_value(conf->user_select, prev->user_select, NULL);\n    ngx_conf_merge_ptr_value(conf->user_defined_str, prev->user_defined_str, NULL);\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_reqstat_add_variable(ngx_conf_t *cf)\n{\n    ngx_str_t                  name = ngx_string(\"reqstat_enable\");\n    ngx_http_variable_t       *var;\n    ngx_http_reqstat_conf_t   *rmcf;\n\n    rmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_reqstat_module);\n\n    var = ngx_http_add_variable(cf, &name, 0);\n    if (var == NULL) {\n        return NGX_ERROR;\n    }\n\n    var->get_handler = ngx_http_reqstat_variable_enable;\n    var->data = 0;\n\n    rmcf->index = ngx_http_get_variable_index(cf, &name);\n\n    if (rmcf->index == NGX_ERROR) {\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_reqstat_init(ngx_conf_t *cf)\n{\n    ngx_http_handler_pt          *h;\n    ngx_http_core_main_conf_t    *cmcf;\n\n    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);\n\n    h = ngx_array_push(&cmcf->phases[NGX_HTTP_LOG_PHASE].handlers);\n    if (h == NULL) {\n        return NGX_ERROR;\n    }\n\n    *h = ngx_http_reqstat_log_handler;\n\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_reqstat_init_handler;\n\n    ngx_http_next_input_body_filter = ngx_http_top_input_body_filter;\n    ngx_http_top_input_body_filter = ngx_http_reqstat_input_body_filter;\n\n    ngx_http_write_filter_stat = ngx_http_reqstat_log_flow;\n\n    return NGX_OK;\n}\n\n\nstatic char *\nngx_http_reqstat_show_field(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_int_t                  valid, *index;\n    ngx_str_t                 *value, *user;\n    ngx_uint_t                 i, j;\n    ngx_http_reqstat_conf_t   *rmcf, *rlcf;\n\n    rlcf = conf;\n    rmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_reqstat_module);\n\n    rlcf->user_select = ngx_array_create(cf->pool, cf->args->nelts - 1,\n                                         sizeof(ngx_int_t));\n    if (rlcf->user_select == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    index = ngx_array_push_n(rlcf->user_select, cf->args->nelts - 1);\n    if (index == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    value = cf->args->elts;\n    for (i = 1; i < cf->args->nelts; i++) {\n        valid = 0;\n\n        if (value[i].data[0] == '$') {\n            value[i].data++;\n            value[i].len--;\n\n            if (rmcf->user_defined_str) {\n\n                user = rmcf->user_defined_str->elts;\n\n                for (j = 0; j < rmcf->user_defined_str->nelts; j++) {\n                    if (value[i].len != user[j].len\n                            || ngx_strncmp(value[i].data, user[j].data, value[i].len)\n                            != 0)\n                    {\n                        continue;\n                    }\n\n                    *index++ = NGX_HTTP_REQSTAT_RSRV + j;\n                    valid = 1;\n                    break;\n                }\n            }\n\n        } else {\n            for (j = 0; j < NGX_HTTP_REQSTAT_RSRV; j++) {\n                if (value[i].len != REQSTAT_RSRV_VARIABLES[j].name.len\n                    || ngx_strncmp(REQSTAT_RSRV_VARIABLES[j].name.data,\n                                   value[i].data, value[i].len) != 0)\n                {\n                    continue;\n                }\n\n                *index++ = REQSTAT_RSRV_VARIABLES[j].index;\n                valid = 1;\n                break;\n            }\n        }\n\n        if (!valid) {\n            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                          \"field \\\"%V\\\" does not exist\",\n                          &value[i]);\n\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_reqstat_show(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_str_t                    *value;\n    ngx_uint_t                    i;\n    ngx_shm_zone_t               *shm_zone, **z;\n    ngx_http_core_loc_conf_t     *clcf;\n\n    ngx_http_reqstat_conf_t      *rlcf = conf;\n\n    value = cf->args->elts;\n\n    if (rlcf->display != NGX_CONF_UNSET_PTR) {\n        return \"is duplicate\";\n    }\n\n    if (cf->args->nelts == 1) {\n        rlcf->display = NULL;\n        goto reg_handler;\n    }\n\n    rlcf->display = ngx_array_create(cf->pool, cf->args->nelts - 1,\n                                     sizeof(ngx_shm_zone_t *));\n    if (rlcf->display == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    for (i = 1; i < cf->args->nelts; i++) {\n        shm_zone = ngx_shared_memory_add(cf, &value[i], 0,\n                                         &ngx_http_reqstat_module);\n        if (shm_zone == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        z = ngx_array_push(rlcf->display);\n        *z = shm_zone;\n    }\n\nreg_handler:\n\n    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);\n    clcf->handler = ngx_http_reqstat_show_handler;\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_reqstat_zone_add_indicator(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n    ngx_int_t                         *i;\n    ngx_str_t                         *value, *name;\n    ngx_uint_t                         j;\n    ngx_shm_zone_t                    *shm_zone;\n    ngx_http_reqstat_ctx_t            *ctx;\n    ngx_http_reqstat_conf_t           *rlcf;\n\n    rlcf = conf;\n    value = cf->args->elts;\n\n    shm_zone = ngx_shared_memory_add(cf, &value[1], 0,\n                                     &ngx_http_reqstat_module);\n    if (shm_zone == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (shm_zone->data == NULL) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"zone \\\"%V\\\" should be defined first\",\n                           &value[1]);\n        return NGX_CONF_ERROR;\n    }\n\n    ctx = shm_zone->data;\n\n    if (ctx->user_defined != NULL) {\n        return \"is duplicate\";\n    }\n\n    if (cf->args->nelts > NGX_HTTP_REQSTAT_USER + 2) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"too many user defined variables\");\n        return NGX_CONF_ERROR;\n    }\n\n    ctx->user_defined = ngx_array_create(cf->pool, cf->args->nelts - 2,\n                                         sizeof(ngx_int_t));\n    if (ctx->user_defined == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    rlcf->user_defined_str = ngx_array_create(cf->pool, cf->args->nelts - 2,\n                                              sizeof(ngx_str_t));\n    if (rlcf->user_defined_str == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    for (j = 2; j < cf->args->nelts; j++) {\n        if (value[j].data[0] == '$') {\n            value[j].data++;\n            value[j].len--;\n        }\n\n        name = ngx_array_push(rlcf->user_defined_str);\n        name->len = value[j].len;\n        name->data = value[j].data;\n\n        i = ngx_array_push(ctx->user_defined);\n        *i = ngx_http_get_variable_index(cf, &value[j]);\n        if (*i == NGX_ERROR) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"failed to obtain variable \\\"%V\\\"\",\n                               &value[j]);\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_reqstat_zone_key_length(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n    ngx_str_t                         *value;\n    ngx_shm_zone_t                    *shm_zone;\n    ngx_http_reqstat_ctx_t            *ctx;\n\n    value = cf->args->elts;\n\n    shm_zone = ngx_shared_memory_add(cf, &value[1], 0,\n                                     &ngx_http_reqstat_module);\n    if (shm_zone == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (shm_zone->data == NULL) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"zone \\\"%V\\\" should be defined first\",\n                           &value[1]);\n        return NGX_CONF_ERROR;\n    }\n\n    ctx = shm_zone->data;\n\n    ctx->key_len = ngx_atoi(value[2].data, value[2].len);\n    if (ctx->key_len == NGX_ERROR) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid key length\");\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_reqstat_zone_recycle(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n    ngx_int_t                          rate, scale;\n    ngx_str_t                         *value;\n    ngx_shm_zone_t                    *shm_zone;\n    ngx_http_reqstat_ctx_t            *ctx;\n\n    value = cf->args->elts;\n\n    shm_zone = ngx_shared_memory_add(cf, &value[1], 0,\n                                     &ngx_http_reqstat_module);\n    if (shm_zone == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (shm_zone->data == NULL) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"zone \\\"%V\\\" should be defined first\",\n                           &value[1]);\n        return NGX_CONF_ERROR;\n    }\n\n    ctx = shm_zone->data;\n\n    rate = ngx_atoi(value[2].data, value[2].len);\n    if (rate == NGX_ERROR) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid threshold\");\n        return NGX_CONF_ERROR;\n    }\n\n    scale = ngx_atoi(value[3].data, value[3].len);\n    if (scale == NGX_ERROR) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid scale\");\n        return NGX_CONF_ERROR;\n    }\n\n    ctx->recycle_rate = rate * 1000 / scale;\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_reqstat_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ssize_t                            size;\n    ngx_str_t                         *value;\n    ngx_shm_zone_t                    *shm_zone;\n    ngx_http_reqstat_ctx_t            *ctx;\n    ngx_http_compile_complex_value_t   ccv;\n\n    value = cf->args->elts;\n\n    size = ngx_parse_size(&value[3]);\n    if (size == NGX_ERROR) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid zone size \\\"%V\\\"\", &value[3]);\n        return NGX_CONF_ERROR;\n    }\n\n    if (size < (ssize_t) (8 * ngx_pagesize)) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"zone \\\"%V\\\" is too small\", &value[1]);\n        return NGX_CONF_ERROR;\n    }\n\n    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_reqstat_ctx_t));\n    if (ctx == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (ngx_http_script_variables_count(&value[2]) == 0) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"the value \\\"%V\\\" is a constant\",\n                           &value[2]);\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n\n    ccv.cf = cf;\n    ccv.value = &value[2];\n    ccv.complex_value = &ctx->value;\n\n    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    ctx->val = ngx_palloc(cf->pool, sizeof(ngx_str_t));\n    if (ctx->val == NULL) {\n        return NGX_CONF_ERROR;\n    }\n    *ctx->val = value[2];\n\n    ctx->key_len = 152;          /* now an item is 640B at length. */\n    ctx->recycle_rate = 167;     /* rate threshold is 10r/min */\n    ctx->alloc_already_fail = 0;\n\n    shm_zone = ngx_shared_memory_add(cf, &value[1], size,\n                                     &ngx_http_reqstat_module);\n    if (shm_zone == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (shm_zone->data) {\n        ctx = shm_zone->data;\n\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"%V \\\"%V\\\" is already bound to value \\\"%V\\\"\",\n                           &cmd->name, &value[1], ctx->val);\n        return NGX_CONF_ERROR;\n    }\n\n    shm_zone->init = ngx_http_reqstat_init_zone;\n    shm_zone->data = ctx;\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_reqstat(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_str_t                    *value;\n    ngx_uint_t                    i, j;\n    ngx_shm_zone_t               *shm_zone, **z;\n    ngx_http_reqstat_conf_t      *smcf;\n\n    ngx_http_reqstat_conf_t      *rlcf = conf;\n\n    value = cf->args->elts;\n    smcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_reqstat_module);\n\n    if (rlcf->monitor != NGX_CONF_UNSET_PTR) {\n        return \"is duplicate\";\n    }\n\n    if (smcf->monitor == NULL) {\n        smcf->monitor = ngx_array_create(cf->pool, cf->args->nelts - 1,\n                                         sizeof(ngx_shm_zone_t *));\n        if (smcf->monitor == NULL) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    rlcf->monitor = ngx_array_create(cf->pool, cf->args->nelts - 1,\n                                     sizeof(ngx_shm_zone_t *));\n    if (rlcf->monitor == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    for (i = 1; i < cf->args->nelts; i++) {\n        shm_zone = ngx_shared_memory_add(cf, &value[i], 0,\n                                         &ngx_http_reqstat_module);\n        if (shm_zone == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        z = ngx_array_push(rlcf->monitor);\n        *z = shm_zone;\n\n        z = smcf->monitor->elts;\n        for (j = 0; j < smcf->monitor->nelts; j++) {\n            if (!ngx_strcmp(value[i].data, z[j]->shm.name.data)) {\n                break;\n            }\n        }\n\n        if (j == smcf->monitor->nelts) {\n            z = ngx_array_push(smcf->monitor);\n            if (z == NULL) {\n                return NGX_CONF_ERROR;\n            }\n            *z = shm_zone;\n        }\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_reqstat_variable_enable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    u_char                       *p;\n    ngx_http_reqstat_store_t     *store;\n\n    p = ngx_pnalloc(r->pool, 1 + sizeof(uintptr_t));\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    v->len = 1;\n    v->valid = 1;\n    v->not_found = 0;\n    v->no_cacheable = 0;\n    v->data = p;\n\n    store = ngx_http_get_module_ctx(r, ngx_http_reqstat_module);\n    if (store == NULL || store->bypass) {\n        *p = '0';\n\n    } else {\n        *p++ = '1';\n        *((uintptr_t *) p) = (uintptr_t) store;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_reqstat_init_handler(ngx_http_request_t *r)\n{\n    ngx_http_reqstat_conf_t      *rmcf, *rlcf;\n    ngx_http_reqstat_store_t     *store;\n\n    store = ngx_http_get_module_ctx(r, ngx_http_reqstat_module);\n    rmcf = ngx_http_get_module_main_conf(r, ngx_http_reqstat_module);\n    rlcf = ngx_http_get_module_loc_conf(r, ngx_http_reqstat_module);\n\n    if (store == NULL) {\n        if (r->variables[rmcf->index].valid) {\n            return NGX_DECLINED;\n        }\n\n        store = ngx_http_reqstat_create_store(r, rlcf);\n        if (store == NULL) {\n            return NGX_ERROR;\n        }\n\n        ngx_http_set_ctx(r, store, ngx_http_reqstat_module);\n    }\n\n    if (ngx_http_get_indexed_variable(r, rmcf->index) == NULL) {\n        return NGX_ERROR;\n    }\n\n    return NGX_DECLINED;\n}\n\n\nstatic ngx_int_t\nngx_http_reqstat_log_handler(ngx_http_request_t *r)\n{\n    u_char                       *p;\n    ngx_str_t                     val, *value;\n    ngx_int_t                    *indicator, iv;\n    ngx_uint_t                    i, j, k, status, utries;\n    ngx_time_t                   *tp;\n    ngx_msec_int_t                ms, total_ms;\n    ngx_shm_zone_t              **shm_zone, *z;\n    ngx_http_reqstat_ctx_t       *ctx;\n    ngx_http_reqstat_conf_t      *rcf;\n    ngx_http_reqstat_store_t     *store;\n    ngx_http_reqstat_rbnode_t    *fnode, **fnode_store;\n    ngx_http_upstream_state_t    *state;\n    ngx_http_variable_value_t    *v;\n\n    switch (ngx_http_reqstat_check_enable(r, &rcf, &store)) {\n        case NGX_ERROR:\n            return NGX_ERROR;\n\n        case NGX_DECLINED:\n        case NGX_AGAIN:\n            return NGX_OK;\n\n        default:\n            break;\n    }\n\n    shm_zone = rcf->monitor->elts;\n    fnode_store = store->monitor_index.elts;\n    value = store->value_index.elts;\n    for (i = 0; i < store->monitor_index.nelts; i++) {\n        if (fnode_store[i] == NULL) {\n            continue;\n        }\n\n        z = shm_zone[i];\n        ctx = z->data;\n\n        if (rcf->lazy) {\n            if (ngx_http_complex_value(r, &ctx->value, &val) != NGX_OK) {\n                ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,\n                              \"failed to reap the key \\\"%V\\\"\", ctx->val);\n                continue;\n            }\n\n            if (value[i].len == val.len\n                && ngx_strncmp(value[i].data, val.data, val.len) == 0)\n            {\n                fnode = fnode_store[i];\n\n            } else {\n                fnode = ngx_http_reqstat_rbtree_lookup(shm_zone[i], &val);\n\n                if (fnode == NULL) {\n                    ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,\n                                  \"failed to alloc node in zone \\\"%V\\\", \"\n                                  \"enlarge it please\",\n                                  &z->shm.name);\n\n                    fnode = fnode_store[i];\n\n                } else {\n                    ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_BYTES_OUT,\n                                           r->connection->sent);\n                    ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_BYTES_IN,\n                                           r->connection->received);\n                    if (store) {\n                        store->recv = r->connection->received;\n                    }\n                }\n            }\n\n        } else {\n            fnode = fnode_store[i];\n        }\n\n        if (r->connection->requests == 1) {\n            ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_CONN_TOTAL, 1);\n        }\n\n        ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_REQ_TOTAL, 1);\n        ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_BYTES_IN,\n                               r->connection->received\n                                    - (store ? store->recv : 0));\n\n        if (r->err_status) {\n            status = r->err_status;\n\n        } else if (r->headers_out.status) {\n            status = r->headers_out.status;\n\n        } else if (r->http_version == NGX_HTTP_VERSION_9) {\n            status = 9;\n\n        } else {\n            status = 0;\n        }\n\n        if (status >= 200 && status < 300) {\n            ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_2XX, 1);\n\n            switch (status) {\n            case 200:\n                ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_200, 1);\n                break;\n\n            case 206:\n                ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_206, 1);\n                break;\n\n            default:\n                ngx_http_reqstat_count(fnode,\n                                       NGX_HTTP_REQSTAT_OTHER_DETAIL_STATUS, 1);\n                break;\n            }\n\n        } else if (status >= 300 && status < 400) {\n            ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_3XX, 1);\n\n            switch (status) {\n            case 302:\n                ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_302, 1);\n                break;\n\n            case 304:\n                ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_304, 1);\n                break;\n\n            default:\n                ngx_http_reqstat_count(fnode,\n                                       NGX_HTTP_REQSTAT_OTHER_DETAIL_STATUS, 1);\n                break;\n            }\n\n        } else if (status >= 400 && status < 500) {\n            ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_4XX, 1);\n\n            switch (status) {\n            case 403:\n                ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_403, 1);\n                break;\n\n            case 404:\n                ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_404, 1);\n                break;\n\n            case 416:\n                ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_416, 1);\n                break;\n\n            case 499:\n                ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_499, 1);\n                break;\n\n            default:\n                ngx_http_reqstat_count(fnode,\n                                       NGX_HTTP_REQSTAT_OTHER_DETAIL_STATUS, 1);\n                break;\n            }\n\n        } else if (status >= 500 && status < 600) {\n            ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_5XX, 1);\n\n            switch (status) {\n            case 500:\n                ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_500, 1);\n                break;\n\n            case 502:\n                ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_502, 1);\n                break;\n\n            case 503:\n                ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_503, 1);\n                break;\n\n            case 504:\n                ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_504, 1);\n                break;\n\n            case 508:\n                ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_508, 1);\n                break;\n\n            default:\n                ngx_http_reqstat_count(fnode,\n                                       NGX_HTTP_REQSTAT_OTHER_DETAIL_STATUS, 1);\n                break;\n            }\n\n        } else {\n            ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_OTHER_STATUS, 1);\n\n            ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_OTHER_DETAIL_STATUS,\n                                   1);\n        }\n\n        /* response status of last upstream peer */\n\n        if (r->upstream_states != NULL && r->upstream_states->nelts > 0) {\n            ngx_http_upstream_state_t *state = r->upstream_states->elts;\n            status = state[r->upstream_states->nelts - 1].status;\n\n            if (status >= 400 && status < 500) {\n                ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_UPS_4XX, 1);\n\n            } else if (status >= 500 && status < 600) {\n                ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_UPS_5XX, 1);\n            }\n        }\n\n        tp = ngx_timeofday();\n\n        ms = (ngx_msec_int_t)\n             ((tp->sec - r->start_sec) * 1000 + (tp->msec - r->start_msec));\n        ms = ngx_max(ms, 0);\n        ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_RT, ms);\n\n        if (r->upstream_states != NULL && r->upstream_states->nelts > 0) {\n            ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_UPS_REQ, 1);\n\n            j = 0;\n            total_ms = 0;\n            utries = 0;\n            state = r->upstream_states->elts;\n\n            for ( ;; ) {\n\n                utries++;\n\n#if nginx_version >= 1009002\n                ms = state[j].response_time;\n#else\n                ms = (ngx_msec_int_t) (state[j].response_sec * 1000\n                                               + state[j].response_msec);\n#endif\n                ms = ngx_max(ms, 0);\n                total_ms += ms;\n\n                if (++j == r->upstream_states->nelts) {\n                    break;\n                }\n\n                if (state[j].peer == NULL) {\n                    if (++j == r->upstream_states->nelts) {\n                        break;\n                    }\n                }\n            }\n\n            ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_UPS_RT,\n                                   total_ms);\n            ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_UPS_TRIES,\n                                   utries);\n        }\n\n        if (ctx->user_defined) {\n            indicator = ctx->user_defined->elts;\n            for (j = 0; j < ctx->user_defined->nelts; j++) {\n                v = ngx_http_get_indexed_variable(r, indicator[j]);\n                if (v == NULL || v->not_found || !v->valid) {\n                    ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,\n                                  \"variable is uninitialized\");\n                    continue;\n                }\n\n                for (k = 0, p = v->data + v->len - 1; p >= v->data; p--) {\n                    if (*p == '.') {\n                        k = v->data + v->len - 1 - p;\n                        continue;\n                    }\n\n                    if (*p < '0' || *p > '9') {\n                        break;\n                    }\n                }\n\n                p++;\n\n                if (k) {\n                    iv = ngx_atofp(p, v->data + v->len - p, k);\n\n                } else {\n                    iv = ngx_atoi(p, v->data + v->len - p);\n                }\n\n                if (iv == NGX_ERROR) {\n                    continue;\n                }\n\n                ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_EXTRA(j), iv);\n            }\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_reqstat_show_handler(ngx_http_request_t *r)\n{\n    ngx_int_t                     rc, *user, index;\n    ngx_buf_t                    *b;\n    ngx_uint_t                    i, j;\n    ngx_array_t                  *display;\n    ngx_chain_t                  *tl, out, **cl;\n    ngx_queue_t                  *q;\n    ngx_shm_zone_t              **shm_zone;\n    ngx_http_reqstat_ctx_t       *ctx;\n    ngx_http_reqstat_conf_t      *rlcf;\n    ngx_http_reqstat_conf_t      *smcf;\n    ngx_http_reqstat_rbnode_t    *node;\n\n    rlcf = ngx_http_get_module_loc_conf(r, ngx_http_reqstat_module);\n    smcf = ngx_http_get_module_main_conf(r, ngx_http_reqstat_module);\n\n    display = rlcf->display == NULL ? smcf->monitor : rlcf->display;\n    if (display == NULL) {\n        r->headers_out.status = NGX_HTTP_NO_CONTENT;\n        return ngx_http_send_header(r);\n    }\n\n    r->headers_out.status = NGX_HTTP_OK;\n    ngx_http_clear_content_length(r);\n\n    rc = ngx_http_send_header(r);\n    if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {\n        return rc;\n    }\n\n    shm_zone = display->elts;\n\n    cl = &out.next;\n\n    for (i = 0; i < display->nelts; i++) {\n\n        ctx = shm_zone[i]->data;\n\n        for (q = ngx_queue_head(&ctx->sh->queue);\n             q != ngx_queue_sentinel(&ctx->sh->queue);\n             q = ngx_queue_next(q))\n        {\n            node = ngx_queue_data(q, ngx_http_reqstat_rbnode_t, queue);\n\n            if (node->conn_total == 0) {\n                continue;\n            }\n\n            tl = ngx_alloc_chain_link(r->pool);\n            if (tl == NULL) {\n                return NGX_HTTP_INTERNAL_SERVER_ERROR;\n            }\n\n            b = ngx_calloc_buf(r->pool);\n            if (b == NULL) {\n                return NGX_HTTP_INTERNAL_SERVER_ERROR;\n            }\n\n            tl->buf = b;\n            b->start = ngx_pcalloc(r->pool, 512);\n            if (b->start == NULL) {\n                return NGX_HTTP_INTERNAL_SERVER_ERROR;\n            }\n\n            b->end = b->start + 512;\n            b->last = b->pos = b->start;\n            b->temporary = 1;\n\n            b->last = ngx_slprintf(b->last, b->end, \"%*s,\",\n                                   (size_t) node->len, node->data);\n            if (rlcf->user_select) {\n                user = rlcf->user_select->elts;\n                for (j = 0; j < rlcf->user_select->nelts; j++) {\n                    if (user[j] < NGX_HTTP_REQSTAT_RSRV) {\n                        index = user[j];\n                        b->last = ngx_slprintf(b->last, b->end, \"%uA,\",\n                                        *NGX_HTTP_REQSTAT_REQ_FIELD(node,\n                                              ngx_http_reqstat_fields[index]));\n\n                    } else {\n                        index = user[j] - NGX_HTTP_REQSTAT_RSRV;\n                        b->last = ngx_slprintf(b->last, b->end, \"%uA,\",\n                                        *NGX_HTTP_REQSTAT_REQ_FIELD(node,\n                                               NGX_HTTP_REQSTAT_EXTRA(index)));\n                    }\n                }\n\n            } else {\n\n                for (j = 0; j < NGX_HTTP_REQSTAT_RSRV; j++) {\n                    b->last = ngx_slprintf(b->last, b->end, \"%uA,\",\n                                       *NGX_HTTP_REQSTAT_REQ_FIELD(node,\n                                                  ngx_http_reqstat_fields[j]));\n                }\n\n                if (ctx->user_defined) {\n                    for (j = 0; j < ctx->user_defined->nelts; j++) {\n                        b->last = ngx_slprintf(b->last, b->end, \"%uA,\",\n                                           *NGX_HTTP_REQSTAT_REQ_FIELD(node,\n                                                   NGX_HTTP_REQSTAT_EXTRA(j)));\n                    }\n                }\n            }\n\n            *(b->last - 1) = '\\n';\n\n            tl->next = NULL;\n            *cl = tl;\n            cl = &tl->next;\n        }\n    }\n\n    tl = ngx_alloc_chain_link(r->pool);\n    if (tl == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    tl->buf = ngx_calloc_buf(r->pool);\n    if (tl->buf == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    tl->buf->last_buf = 1;\n    tl->next = NULL;\n    *cl = tl;\n\n    return ngx_http_output_filter(r, out.next);\n}\n\n\nvoid\nngx_http_reqstat_count(void *data, off_t offset, ngx_int_t incr)\n{\n    ngx_http_reqstat_rbnode_t    *node = data;\n\n    (void) ngx_atomic_fetch_add(NGX_HTTP_REQSTAT_REQ_FIELD(node, offset), incr);\n}\n\n\nngx_http_reqstat_rbnode_t *\nngx_http_reqstat_rbtree_lookup(ngx_shm_zone_t *shm_zone, ngx_str_t *val)\n{\n    size_t                        size, len;\n    uint32_t                      hash;\n    ngx_int_t                     rc, excess;\n    ngx_time_t                   *tp;\n    ngx_msec_t                    now;\n    ngx_queue_t                  *q;\n    ngx_msec_int_t                ms;\n    ngx_rbtree_node_t            *node, *sentinel;\n    ngx_http_reqstat_ctx_t       *ctx;\n    ngx_http_reqstat_rbnode_t    *rs;\n\n    ctx = shm_zone->data;\n\n    hash = ngx_murmur_hash2(val->data, val->len);\n\n    node = ctx->sh->rbtree.root;\n    sentinel = ctx->sh->rbtree.sentinel;\n\n    tp = ngx_timeofday();\n    now = (ngx_msec_t) (tp->sec * 1000 + tp->msec);\n\n    ngx_shmtx_lock(&ctx->shpool->mutex);\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        rs = (ngx_http_reqstat_rbnode_t *) &node->color;\n\n        /* len < node->len */\n\n        if (val->len < (size_t) rs->len) {\n            node = node->left;\n            continue;\n        }\n\n        rc = ngx_strncmp(val->data, rs->data, (size_t) rs->len);\n\n        if (rc == 0) {\n\n            ms = (ngx_msec_int_t) (now - rs->last_visit);\n\n            rs->excess = rs->excess - ngx_abs(ms) * ctx->recycle_rate / 1000\n                       + 1000;\n            rs->last_visit = now;\n\n            if (rs->excess > 0) {\n                ngx_queue_remove(&rs->visit);\n                ngx_queue_insert_head(&ctx->sh->visit, &rs->visit);\n            }\n\n            ngx_log_debug2(NGX_LOG_DEBUG_CORE, shm_zone->shm.log, 0,\n                           \"reqstat lookup exist: %*s\", rs->len, rs->data);\n\n            ngx_shmtx_unlock(&ctx->shpool->mutex);\n\n            return rs;\n        }\n\n        node = (rc < 0) ? node->left : node->right;\n    }\n\n    rc = 0;\n    node = NULL;\n\n    size = offsetof(ngx_rbtree_node_t, color)\n         + offsetof(ngx_http_reqstat_rbnode_t, data)\n         + ctx->key_len;\n\n    if (ctx->alloc_already_fail == 0) {\n        node = ngx_slab_alloc_locked(ctx->shpool, size);\n        if (node == NULL) {\n            ctx->alloc_already_fail = 1;\n        }\n    }\n\n    if (node == NULL) {\n\n        /* try to free a vacant node */\n        q = ngx_queue_last(&ctx->sh->visit);\n        rs = ngx_queue_data(q, ngx_http_reqstat_rbnode_t, visit);\n\n        ms = (ngx_msec_int_t) (now - rs->last_visit);\n\n        excess = rs->excess - ngx_abs(ms) * ctx->recycle_rate / 1000;\n\n        ngx_log_debug3(NGX_LOG_DEBUG_CORE, shm_zone->shm.log, 0,\n                       \"reqstat lookup try recycle: %*s, %d\", rs->len, rs->data, excess);\n\n        if (excess < 0) {\n\n            rc = 1;\n\n            node = (ngx_rbtree_node_t *)\n                            ((char *) rs - offsetof(ngx_rbtree_node_t, color));\n            ngx_rbtree_delete(&ctx->sh->rbtree, node);\n            ngx_queue_remove(&rs->visit);\n\n            ngx_log_debug2(NGX_LOG_DEBUG_CORE, shm_zone->shm.log, 0,\n                           \"reqstat lookup recycle: %*s\", rs->len, rs->data);\n\n            rs->conn_total = 0;\n\n            ngx_memzero((void *) &rs->bytes_in, size - offsetof(ngx_rbtree_node_t, color)\n                                            - offsetof(ngx_http_reqstat_rbnode_t, bytes_in));\n\n        } else {\n            ngx_shmtx_unlock(&ctx->shpool->mutex);\n            return NULL;\n        }\n    }\n\n    node->key = hash;\n\n    rs = (ngx_http_reqstat_rbnode_t *) &node->color;\n\n    len = ngx_min(ctx->key_len, (ssize_t) val->len);\n    ngx_memcpy(rs->data, val->data, len);\n    rs->len = len;\n\n    ngx_rbtree_insert(&ctx->sh->rbtree, node);\n    ngx_queue_insert_head(&ctx->sh->visit, &rs->visit);\n    if (!rc) {\n        ngx_queue_insert_head(&ctx->sh->queue, &rs->queue);\n    }\n\n    rs->last_visit = now;\n    rs->excess = 1000;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_CORE, shm_zone->shm.log, 0, \"reqstat lookup build: %*s\", rs->len, rs->data);\n\n    ngx_shmtx_unlock(&ctx->shpool->mutex);\n\n    return rs;\n}\n\n\nstatic ngx_int_t\nngx_http_reqstat_init_zone(ngx_shm_zone_t *shm_zone, void *data)\n{\n    size_t                        n;\n    ngx_http_reqstat_ctx_t       *ctx, *octx;\n\n    octx = data;\n    ctx = shm_zone->data;\n\n    if (octx != NULL) {\n        if (ngx_strcmp(ctx->val->data, octx->val->data) != 0) {\n            ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0,\n                          \"reqstat \\\"%V\\\" uses the value str \\\"%V\\\" \"\n                          \"while previously it used \\\"%V\\\"\",\n                          &shm_zone->shm.name, ctx->val, octx->val);\n            return NGX_ERROR;\n        }\n\n        ctx->shpool = octx->shpool;\n        ctx->sh = octx->sh;\n\n        return NGX_OK;\n    }\n\n    ctx->shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;\n\n    ctx->sh = ngx_slab_alloc(ctx->shpool, sizeof(ngx_http_reqstat_shctx_t));\n    if (ctx->sh == NULL) {\n        return NGX_ERROR;\n    }\n\n    ctx->shpool->data = ctx->sh;\n\n    n = sizeof(\" in req_status zone \\\"\\\"\") + shm_zone->shm.name.len;\n    ctx->shpool->log_ctx = ngx_slab_alloc(ctx->shpool, n);\n    if (ctx->shpool->log_ctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_sprintf(ctx->shpool->log_ctx,\n                \" in req_status zone \\\"%V\\\"%Z\",\n                &shm_zone->shm.name);\n\n    ngx_rbtree_init(&ctx->sh->rbtree, &ctx->sh->sentinel,\n                    ngx_http_reqstat_rbtree_insert_value);\n\n    ngx_queue_init(&ctx->sh->queue);\n    ngx_queue_init(&ctx->sh->visit);\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_reqstat_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_reqstat_rbnode_t   *rsn, *rsnt;\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            rsn = (ngx_http_reqstat_rbnode_t *) &node->color;\n            rsnt = (ngx_http_reqstat_rbnode_t *) &temp->color;\n\n            p = (ngx_memn2cmp(rsn->data, rsnt->data, rsn->len, rsnt->len) < 0)\n                ? &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_reqstat_input_body_filter(ngx_http_request_t *r, ngx_buf_t *buf)\n{\n    ngx_uint_t                    i, diff;\n    ngx_http_reqstat_conf_t      *rcf;\n    ngx_http_reqstat_store_t     *store;\n    ngx_http_reqstat_rbnode_t    *fnode, **fnode_store;\n\n    switch (ngx_http_reqstat_check_enable(r, &rcf, &store)) {\n        case NGX_ERROR:\n            return NGX_ERROR;\n\n        case NGX_DECLINED:\n        case NGX_AGAIN:\n            return ngx_http_next_input_body_filter(r, buf);\n\n        default:\n            break;\n    }\n\n    diff = r->connection->received - store->recv;\n    store->recv = r->connection->received;\n\n    fnode_store = store->monitor_index.elts;\n    for (i = 0; i < store->monitor_index.nelts; i++) {\n        fnode = fnode_store[i];\n        if (fnode == NULL) {\n            continue;\n        }\n\n        ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_BYTES_IN, diff);\n    }\n\n    return ngx_http_next_input_body_filter(r, buf);\n}\n\n\nngx_int_t\nngx_http_reqstat_log_flow(ngx_http_request_t *r)\n{\n    ngx_uint_t                    i, diff;\n    ngx_http_reqstat_conf_t      *rcf;\n    ngx_http_reqstat_store_t     *store;\n    ngx_http_reqstat_rbnode_t    *fnode, **fnode_store;\n\n    switch (ngx_http_reqstat_check_enable(r, &rcf, &store)) {\n        case NGX_ERROR:\n            return NGX_ERROR;\n\n        case NGX_DECLINED:\n        case NGX_AGAIN:\n            return NGX_OK;\n\n        default:\n            break;\n    }\n\n    diff = r->connection->sent - store->sent;\n    store->sent = r->connection->sent;\n\n    fnode_store = store->monitor_index.elts;\n    for (i = 0; i < store->monitor_index.nelts; i++) {\n        fnode = fnode_store[i];\n        if (fnode == NULL) {\n            continue;\n        }\n\n        ngx_http_reqstat_count(fnode, NGX_HTTP_REQSTAT_BYTES_OUT, diff);\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_http_reqstat_store_t *\nngx_http_reqstat_create_store(ngx_http_request_t *r,\n    ngx_http_reqstat_conf_t *rlcf)\n{\n    ngx_str_t                     val, *value;\n    ngx_uint_t                    i;\n    ngx_shm_zone_t              **shm_zone, *z;\n    ngx_http_reqstat_ctx_t       *ctx;\n    ngx_http_reqstat_store_t     *store;\n    ngx_http_reqstat_rbnode_t    *fnode, **fnode_store;\n\n    store = ngx_pcalloc(r->pool, sizeof(ngx_http_reqstat_store_t));\n    if (store == NULL) {\n        return NULL;\n    }\n\n    if (rlcf->monitor == NULL) {\n        store->bypass = 1;\n        return store;\n    }\n\n    store->conf = rlcf;\n\n    switch (ngx_http_test_predicates(r, rlcf->bypass)) {\n\n    case NGX_ERROR:\n        return NULL;\n\n    case NGX_DECLINED:\n        store->bypass = 1;\n        return store;\n\n    default: /* NGX_OK */\n        break;\n    }\n\n    if (ngx_array_init(&store->monitor_index, r->pool, rlcf->monitor->nelts,\n                       sizeof(ngx_http_reqstat_rbnode_t *)) == NGX_ERROR)\n    {\n        return NULL;\n    }\n\n    if (ngx_array_init(&store->value_index, r->pool, rlcf->monitor->nelts,\n                       sizeof(ngx_str_t)) == NGX_ERROR)\n    {\n        return NULL;\n    }\n\n    shm_zone = rlcf->monitor->elts;\n    for (i = 0; i < rlcf->monitor->nelts; i++) {\n        z = shm_zone[i];\n        ctx = z->data;\n\n        if (ngx_http_complex_value(r, &ctx->value, &val) != NGX_OK) {\n            ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,\n                          \"failed to reap the key \\\"%V\\\"\", ctx->val);\n            continue;\n        }\n\n        value = ngx_array_push(&store->value_index);\n        *value = val;\n\n        fnode = ngx_http_reqstat_rbtree_lookup(shm_zone[i], &val);\n        if (fnode == NULL) {\n            ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,\n                          \"failed to alloc node in zone \\\"%V\\\", \"\n                          \"enlarge it please\",\n                          &z->shm.name);\n        }\n\n        fnode_store = ngx_array_push(&store->monitor_index);\n        *fnode_store = fnode;\n    }\n\n    return store;\n}\n\n\nstatic ngx_int_t\nngx_http_reqstat_check_enable(ngx_http_request_t *r,\n    ngx_http_reqstat_conf_t **rlcf, ngx_http_reqstat_store_t **store)\n{\n    ngx_http_reqstat_conf_t      *rcf;\n    ngx_http_reqstat_store_t     *s;\n    ngx_http_variable_value_t    *v;\n\n    rcf = ngx_http_get_module_main_conf(r, ngx_http_reqstat_module);\n    if (r->variables[rcf->index].valid) {\n        v = ngx_http_get_flushed_variable(r, rcf->index);\n\n        if (v->data[0] == '0') {\n            return NGX_DECLINED;\n        }\n\n        s = (ngx_http_reqstat_store_t *) (*((uintptr_t *) &v->data[1]));\n        rcf = s->conf;\n\n    } else {\n        rcf = ngx_http_get_module_loc_conf(r, ngx_http_reqstat_module);\n\n        s = ngx_http_get_module_ctx(r, ngx_http_reqstat_module);\n\n        if (s == NULL) {\n            s = ngx_http_reqstat_create_store(r, rcf);\n            if (s == NULL) {\n                return NGX_ERROR;\n            }\n        }\n\n        if (s->bypass) {\n            return NGX_DECLINED;\n        }\n    }\n\n    *rlcf = rcf;\n    *store = s;\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "modules/ngx_http_slice_module/config",
    "content": "ngx_addon_name=ngx_http_slice_module\nHTTP_MODULES=\"$HTTP_MODULES ngx_http_slice_module\"\nHTTP_INCS=\"$HTTP_INCS $ngx_addon_dir/\"\nNGX_ADDON_SRCS=\"$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_slice_module.c\"\n"
  },
  {
    "path": "modules/ngx_http_slice_module/ngx_http_slice_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\ntypedef struct {\n    ngx_str_t                      begin;\n    ngx_str_t                      end;\n\n    ngx_str_t                      header;\n    ngx_flag_t                     header_first;\n\n    ngx_str_t                      footer;\n    ngx_flag_t                     footer_last;\n} ngx_http_slice_loc_conf_t;\n\n\nstatic void *ngx_http_slice_create_loc_conf(ngx_conf_t *cf);\nstatic char *ngx_http_slice_merge_loc_conf(ngx_conf_t *cf, void *parent,\n    void *child);\nstatic char *ngx_http_slice(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\n\n\nstatic ngx_command_t  ngx_http_slice_commands[] = {\n\n    { ngx_string(\"slice\"),\n      NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,\n      ngx_http_slice,\n      0,\n      0,\n      NULL },\n\n    { ngx_string(\"slice_arg_begin\"),\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_slice_loc_conf_t, begin),\n      NULL },\n\n    { ngx_string(\"slice_arg_end\"),\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_slice_loc_conf_t, end),\n      NULL },\n\n    { ngx_string(\"slice_header\"),\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_slice_loc_conf_t, header),\n      NULL },\n\n    { ngx_string(\"slice_footer\"),\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_slice_loc_conf_t, footer),\n      NULL },\n\n    { ngx_string(\"slice_header_first\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_slice_loc_conf_t, header_first),\n      NULL },\n\n    { ngx_string(\"slice_footer_last\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_slice_loc_conf_t, footer_last),\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_slice_module_ctx = {\n    NULL,                          /* preconfiguration */\n    NULL,                          /* 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    ngx_http_slice_create_loc_conf,/* create location configuration */\n    ngx_http_slice_merge_loc_conf  /* merge location configuration */\n};\n\n\nngx_module_t  ngx_http_slice_module = {\n    NGX_MODULE_V1,\n    &ngx_http_slice_module_ctx,    /* module context */\n    ngx_http_slice_commands,       /* 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_slice_handler(ngx_http_request_t *r)\n{\n    u_char                    *last;\n    off_t                      begin, end, len;\n    size_t                     root;\n    ngx_int_t                  rc;\n    ngx_uint_t                 level, i;\n    ngx_str_t                  path, value;\n    ngx_log_t                 *log;\n    ngx_buf_t                 *b;\n    ngx_chain_t                out[3];\n    ngx_open_file_info_t       of;\n    ngx_http_core_loc_conf_t  *clcf;\n    ngx_http_slice_loc_conf_t *slcf;\n\n    if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {\n        return NGX_HTTP_NOT_ALLOWED;\n    }\n\n    if (r->uri.data[r->uri.len - 1] == '/') {\n        return NGX_DECLINED;\n    }\n\n    slcf = ngx_http_get_module_loc_conf(r, ngx_http_slice_module);\n\n    rc = ngx_http_discard_request_body(r);\n\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    last = ngx_http_map_uri_to_path(r, &path, &root, 0);\n    if (last == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    log = r->connection->log;\n\n    path.len = last - path.data;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,\n                   \"http slice filename: \\\"%V\\\"\", &path);\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    ngx_memzero(&of, sizeof(ngx_open_file_info_t));\n\n    of.read_ahead = clcf->read_ahead;\n    of.directio = clcf->directio;\n    of.valid = clcf->open_file_cache_valid;\n    of.min_uses = clcf->open_file_cache_min_uses;\n    of.errors = clcf->open_file_cache_errors;\n    of.events = clcf->open_file_cache_events;\n\n    if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)\n        != NGX_OK)\n    {\n        switch (of.err) {\n\n        case 0:\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n\n        case NGX_ENOENT:\n        case NGX_ENOTDIR:\n        case NGX_ENAMETOOLONG:\n\n            level = NGX_LOG_ERR;\n            rc = NGX_HTTP_NOT_FOUND;\n            break;\n\n        case NGX_EACCES:\n\n            level = NGX_LOG_ERR;\n            rc = NGX_HTTP_FORBIDDEN;\n            break;\n\n        default:\n\n            level = NGX_LOG_CRIT;\n            rc = NGX_HTTP_INTERNAL_SERVER_ERROR;\n            break;\n        }\n\n        if (rc != NGX_HTTP_NOT_FOUND || clcf->log_not_found) {\n            ngx_log_error(level, log, of.err,\n                          \"%s \\\"%s\\\" failed\", of.failed, path.data);\n        }\n\n        return rc;\n    }\n\n    if (!of.is_file) {\n\n        if (ngx_close_file(of.fd) == NGX_FILE_ERROR) {\n            ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,\n                          ngx_close_file_n \" \\\"%s\\\" failed\", path.data);\n        }\n\n        return NGX_DECLINED;\n    }\n\n    r->root_tested = !r->error_page;\n\n    begin = 0;\n    end = of.size;\n\n    if (r->args.len) {\n\n        if (ngx_http_arg(r, slcf->begin.data, slcf->begin.len, &value)\n            == NGX_OK)\n        {\n            begin = ngx_atoof(value.data, value.len);\n\n            if (begin == NGX_ERROR || begin >= of.size) {\n                begin = 0;\n            }\n        }\n\n        if (ngx_http_arg(r, slcf->end.data, slcf->end.len, &value) == NGX_OK) {\n\n            end = ngx_atoof(value.data, value.len);\n\n            if (end == NGX_ERROR || end >= of.size) {\n                end = of.size;\n            }\n        }\n    }\n\n    end = end < begin ? of.size : end;\n\n    len = (end == begin) ? 0 : ((end - begin)\n            + ((begin == 0 && slcf->header_first) ? slcf->header.len : 0)\n            + ((end == of.size && slcf->footer_last) ? slcf->footer.len : 0));\n\n    log->action = \"sending slice to client\";\n\n    r->headers_out.status = NGX_HTTP_OK;\n    r->headers_out.content_length_n = len;\n    r->headers_out.last_modified_time = of.mtime;\n\n    if (ngx_http_set_content_type(r) != NGX_OK) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    if (len == 0) {\n        r->header_only = 1;\n        return ngx_http_send_header(r);\n    }\n\n    /*\n     * add header when the first header is not denied\n     */\n    if (slcf->header.len\n        && !(begin == 0 && !slcf->header_first))\n    {\n        b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));\n        if (b == NULL) {\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n\n        b->pos = slcf->header.data;\n        b->last = slcf->header.data + slcf->header.len;\n        b->memory = 1;\n\n        out[0].buf = b;\n        out[0].next = &out[1];\n\n        i = 0;\n    } else {\n        i = 1;\n    }\n\n    b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));\n    if (b == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t));\n    if (b->file == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    r->allow_ranges = 1;\n\n    rc = ngx_http_send_header(r);\n\n    if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {\n        return rc;\n    }\n\n    b->file_pos = begin;\n    b->file_last = end;\n\n    b->in_file = b->file_last ? 1: 0;\n    b->last_buf = 1;\n    b->last_in_chain = 1;\n\n    b->file->fd = of.fd;\n    b->file->name = path;\n    b->file->log = log;\n    b->file->directio = of.is_directio;\n\n    out[1].buf = b;\n    out[1].next = NULL;\n\n    /*\n     * add footer when the last footer is not denied\n     */\n    if (slcf->footer.len\n        && !(end == of.size && !slcf->footer_last))\n    {\n        b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));\n        if (b == NULL) {\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n\n        b->pos = slcf->footer.data;\n        b->last = slcf->footer.data + slcf->footer.len;\n        b->memory = 1;\n        b->last_buf = 1;\n        b->last_in_chain = 1;\n\n        out[2].buf = b;\n        out[2].next = NULL;\n\n        out[1].buf->last_buf = 0;\n        out[1].buf->last_in_chain = 0;\n        out[1].next = &out[2];\n    }\n\n    return ngx_http_output_filter(r, &out[i]);\n}\n\n\nstatic void *\nngx_http_slice_create_loc_conf(ngx_conf_t *cf)\n{\n    ngx_http_slice_loc_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_slice_loc_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc() :\n     *\n     *     conf->begin = { 0, NULL }\n     *     conf->end = { 0, NULL }\n     *     conf->header = { 0, NULL }\n     *     conf->footer = { 0, NULL }\n     */\n\n    conf->header_first = NGX_CONF_UNSET;\n    conf->footer_last = NGX_CONF_UNSET;\n\n    return conf;\n}\n\n\nstatic char *\nngx_http_slice_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_http_slice_loc_conf_t  *prev = parent;\n    ngx_http_slice_loc_conf_t  *conf = child;\n\n    ngx_conf_merge_str_value(conf->begin, prev->begin, \"begin\");\n    ngx_conf_merge_str_value(conf->end, prev->end, \"end\");\n    ngx_conf_merge_str_value(conf->header, prev->header, \"\");\n    ngx_conf_merge_str_value(conf->footer, prev->footer, \"\");\n    ngx_conf_merge_value(conf->header_first, prev->header_first, 1);\n    ngx_conf_merge_value(conf->footer_last, prev->footer_last, 1);\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_slice(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_core_loc_conf_t  *clcf;\n\n    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);\n    clcf->handler = ngx_http_slice_handler;\n\n    return NGX_CONF_OK;\n}\n"
  },
  {
    "path": "modules/ngx_http_sysguard_module/config",
    "content": "ngx_addon_name=ngx_http_sysguard_module\nHTTP_MODULES=\"$HTTP_MODULES ngx_http_sysguard_module\"\nNGX_ADDON_SRCS=\"$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_sysguard_module.c\"\n\nhave=T_NGX_HTTP_SYSGUARD . auto/have\n"
  },
  {
    "path": "modules/ngx_http_sysguard_module/ngx_http_sysguard_module.c",
    "content": "/*\n * Copyright (C) 2010-2017 Alibaba Group Holding Limited\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\n#define NGX_HTTP_SYSGUARD_MODE_OR  0\n#define NGX_HTTP_SYSGUARD_MODE_AND 1\n\n\ntypedef struct {\n    time_t           stamp;\n    ngx_uint_t       requests;\n    time_t           sec;\n    ngx_msec_int_t   msec;\n} ngx_http_sysguard_rt_node_t;\n\ntypedef struct {\n    ngx_http_sysguard_rt_node_t  *slots;\n    ngx_int_t                     nr_slots;\n    ngx_int_t                     current;\n\n    time_t                        cached_rt_exptime;\n    ngx_int_t                     cached_rt;\n} ngx_http_sysguard_rt_ring_t;\n\ntypedef struct {\n    ngx_flag_t                    enable;\n\n    ngx_int_t                     load;\n    ngx_str_t                     load_action;\n    ngx_int_t                     cpuusage;\n    ngx_str_t                     cpuusage_action;\n    ngx_int_t                     swap;\n    ngx_str_t                     swap_action;\n    size_t                        free;\n    ngx_str_t                     free_action;\n    ngx_int_t                     rt;\n    ngx_int_t                     rt_period;\n    ngx_str_t                     rt_action;\n    time_t                        interval;\n    time_t                        cpu_interval;\n\n    ngx_uint_t                    log_level;\n    ngx_uint_t                    mode;\n\n    ngx_http_sysguard_rt_ring_t  *rt_ring;\n} ngx_http_sysguard_conf_t;\n\n\nstatic void *ngx_http_sysguard_create_conf(ngx_conf_t *cf);\nstatic char *ngx_http_sysguard_merge_conf(ngx_conf_t *cf, void *parent,\n    void *child);\nstatic char *ngx_http_sysguard_load(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_http_sysguard_cpuusage(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_http_sysguard_mem(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_http_sysguard_rt(ngx_conf_t *cf,\n    ngx_command_t *cmd, void *conf);\nstatic ngx_int_t ngx_http_sysguard_init(ngx_conf_t *cf);\n\n\nstatic ngx_conf_enum_t  ngx_http_sysguard_log_levels[] = {\n    { ngx_string(\"info\"), NGX_LOG_INFO },\n    { ngx_string(\"notice\"), NGX_LOG_NOTICE },\n    { ngx_string(\"warn\"), NGX_LOG_WARN },\n    { ngx_string(\"error\"), NGX_LOG_ERR },\n    { ngx_null_string, 0 }\n};\n\nstatic ngx_conf_enum_t  ngx_http_sysguard_modes[] = {\n    { ngx_string(\"or\"), NGX_HTTP_SYSGUARD_MODE_OR },\n    { ngx_string(\"and\"), NGX_HTTP_SYSGUARD_MODE_AND },\n    { ngx_null_string, 0 }\n};\n\n\nstatic ngx_command_t  ngx_http_sysguard_commands[] = {\n\n    { ngx_string(\"sysguard\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_sysguard_conf_t, enable),\n      NULL },\n\n    { ngx_string(\"sysguard_mode\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_enum_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_sysguard_conf_t, mode),\n      &ngx_http_sysguard_modes },\n\n    { ngx_string(\"sysguard_load\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,\n      ngx_http_sysguard_load,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"sysguard_cpu\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,\n      ngx_http_sysguard_cpuusage,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"sysguard_mem\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,\n      ngx_http_sysguard_mem,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"sysguard_rt\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,\n      ngx_http_sysguard_rt,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"sysguard_interval\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_sec_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_sysguard_conf_t, interval),\n      NULL },\n\n    { ngx_string(\"sysguard_log_level\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_enum_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_sysguard_conf_t, log_level),\n      &ngx_http_sysguard_log_levels },\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_sysguard_module_ctx = {\n    NULL,                                   /* preconfiguration */\n    ngx_http_sysguard_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    ngx_http_sysguard_create_conf,          /* create location configuration */\n    ngx_http_sysguard_merge_conf            /* merge location configuration */\n};\n\n\nngx_module_t  ngx_http_sysguard_module = {\n    NGX_MODULE_V1,\n    &ngx_http_sysguard_module_ctx,          /* module context */\n    ngx_http_sysguard_commands,             /* 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 time_t        ngx_http_sysguard_cached_load_exptime;\nstatic time_t        ngx_http_sysguard_cached_mem_exptime;\nstatic time_t        ngx_http_sysguard_cached_cpuusage_exptime;\nstatic time_t        ngx_http_sysguard_cached_cpuinfo_exptime;\nstatic ngx_int_t     ngx_http_sysguard_cached_load;\nstatic ngx_int_t     ngx_http_sysguard_cached_cpuusage;\nstatic ngx_cpuinfo_t ngx_http_sysguard_cached_pre_cputime;\nstatic ngx_cpuinfo_t ngx_http_sysguard_cached_cur_cputime;\nstatic ngx_int_t     ngx_http_sysguard_cached_swapstat;\nstatic size_t        ngx_http_sysguard_cached_free;\n\n\nstatic ngx_int_t\nngx_http_sysguard_update_load(ngx_http_request_t *r, time_t exptime)\n{\n    ngx_int_t  load, rc;\n\n    ngx_http_sysguard_cached_load_exptime = ngx_time() + exptime;\n\n    rc = ngx_getloadavg(&load, 1, r->connection->log);\n    if (rc == NGX_ERROR) {\n\n        ngx_http_sysguard_cached_load = 0;\n\n        return NGX_ERROR;\n    }\n\n    ngx_http_sysguard_cached_load = load;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_sysguard_update_cpuusage(ngx_http_request_t *r, time_t exptime)\n{\n    time_t         cpu_diff, total_diff;\n    ngx_cpuinfo_t  pre, cur;\n\n    pre = ngx_http_sysguard_cached_pre_cputime;\n    cur = ngx_http_sysguard_cached_cur_cputime;\n\n    ngx_http_sysguard_cached_cpuusage_exptime = ngx_time() + exptime;\n\n    cpu_diff = (cur.usr + cur.nice + cur.sys) - (pre.usr + pre.nice + pre.sys);\n\n    total_diff = (cur.usr + cur.nice + cur.sys + cur.iowait + cur.irq\n                 + cur.softirq + cur.idle)\n                 - (pre.usr + pre.nice + pre.sys + pre.iowait + pre.irq\n                 + pre.softirq + pre.idle);\n\n    if (total_diff == 0) {\n        total_diff = 1;\n    }\n\n    ngx_http_sysguard_cached_cpuusage = cpu_diff * 100 * 100 / total_diff;\n\n    return NGX_OK;\n}\n\n\nvoid\nngx_http_sysguard_update_cpuinfo(ngx_http_request_t *r)\n{\n    ngx_int_t                       rc;\n    ngx_cpuinfo_t                   cpuinfo;\n    ngx_http_sysguard_conf_t       *glcf;\n    ngx_str_t                       cpunumber;\n\n    ngx_str_set(&cpunumber, \"cpu\");\n\n    glcf = ngx_http_get_module_loc_conf(r, ngx_http_sysguard_module);\n\n    if (!glcf->enable) {\n        return;\n    }\n\n    if (glcf->cpuusage == NGX_CONF_UNSET) {\n        return;\n    }\n\n    if (ngx_http_sysguard_cached_cpuinfo_exptime < ngx_time()) {\n        rc = ngx_getcpuinfo(&cpunumber, &cpuinfo, r->connection->log);\n        if (rc == NGX_ERROR) {\n            ngx_memzero(&cpuinfo, sizeof(ngx_cpuinfo_t));\n            return;\n        }\n\n        ngx_http_sysguard_cached_pre_cputime = ngx_http_sysguard_cached_cur_cputime;\n        ngx_http_sysguard_cached_cur_cputime = cpuinfo;\n        ngx_http_sysguard_cached_cpuinfo_exptime = ngx_time() + glcf->cpu_interval;\n    }\n\n    return;\n}\n\n\nstatic ngx_int_t\nngx_http_sysguard_update_mem(ngx_http_request_t *r, time_t exptime)\n{\n    ngx_int_t      rc;\n    ngx_meminfo_t  m;\n\n    ngx_http_sysguard_cached_mem_exptime = ngx_time() + exptime;\n\n    rc = ngx_getmeminfo(&m, r->connection->log);\n    if (rc == NGX_ERROR) {\n\n        ngx_http_sysguard_cached_swapstat = 0;\n        ngx_http_sysguard_cached_free = NGX_CONF_UNSET_SIZE;\n\n        return NGX_ERROR;\n    }\n\n    ngx_http_sysguard_cached_swapstat = m.totalswap == 0\n        ? 0 : (m.totalswap - m.freeswap) * 100 / m.totalswap;\n    ngx_http_sysguard_cached_free = m.freeram + m.cachedram + m.bufferram;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_sysguard_update_rt(ngx_http_request_t *r, time_t exptime)\n{\n    ngx_uint_t                    rt = 0, rt_sec = 0,\n                                  rt_requests = 0;\n    ngx_int_t                     i, head, processed = 0;\n    ngx_msec_int_t                rt_msec = 0;\n    ngx_http_sysguard_conf_t     *glcf;\n    ngx_http_sysguard_rt_ring_t  *ring;\n    ngx_http_sysguard_rt_node_t  *node, *cur_node;\n\n    glcf = ngx_http_get_module_loc_conf(r, ngx_http_sysguard_module);\n\n    ring = glcf->rt_ring;\n\n    ring->cached_rt_exptime = ngx_time() + exptime;\n\n    i = ring->current;\n\n    head = (ring->current + 1) % ring->nr_slots;\n\n    cur_node = &ring->slots[ring->current];\n\n    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"sysguard update rt: i: %i, c:%i h: %i\",\n                   i, ring->current, head);\n\n    for ( ; (i != head) && (processed < glcf->rt_period); i--, processed++) {\n\n        node = &ring->slots[i];\n\n        ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"node in loop: i: %i, p:%i, sec: %T, msec: %i, r: %ui\",\n                       i, processed, node->sec, node->msec, node->requests);\n\n        if (node->stamp == 0\n            || (cur_node->stamp - node->stamp) != processed)\n        {\n\n            ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"continue: i: %i, p:%i, node tamp: %T, \"\n                           \"cur stamp: %T\",\n                           i, processed, node->stamp, cur_node->stamp);\n\n           goto cont;\n        }\n\n        rt_sec += node->sec;\n        rt_msec += node->msec;\n        rt_requests += node->requests;\n\ncont:\n        /* wrap back to beginning */\n        if (i == 0) {\n            i = ring->nr_slots;\n        }\n    }\n\n    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"rt sec: %ui, rt msec:%i, rc requests: %ui\",\n                   rt_sec, rt_msec, rt_requests);\n\n    rt_msec += (ngx_msec_int_t) (rt_sec * 1000);\n    rt_msec = ngx_max(rt_msec, 0);\n\n    if (rt_requests != 0 && rt_msec > 0) {\n\n        rt_msec = rt_msec / rt_requests;\n\n        rt = rt_msec / 1000 * 1000 + rt_msec % 1000;\n    }\n\n    ring->cached_rt = rt;\n\n    return NGX_OK;\n}\n\n\nvoid\nngx_http_sysguard_update_rt_node(ngx_http_request_t *r)\n{\n    ngx_http_sysguard_rt_ring_t    *ring;\n    ngx_http_sysguard_rt_node_t    *node;\n    time_t                          cur_sec, off;\n    ngx_uint_t                      cur_msec;\n    ngx_http_sysguard_conf_t       *glcf;\n\n    glcf = ngx_http_get_module_loc_conf(r, ngx_http_sysguard_module);\n\n    if (!glcf->enable) {\n        return;\n    }\n\n    if (glcf->rt == NGX_CONF_UNSET) {\n        return;\n    }\n\n    cur_sec = ngx_cached_time->sec;\n    cur_msec = ngx_cached_time->msec;\n\n    ring = glcf->rt_ring;\n\n    node = &ring->slots[ring->current];\n\n    off = cur_sec - node->stamp;\n\n    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"sysguard update rt node: off: %T, stamp:%T, cur time: %T\",\n                   off, node->stamp, cur_sec);\n\n    if (off) {\n\n        ring->current = (ring->current + off) % ring->nr_slots;\n\n        node = &ring->slots[ring->current];\n\n        memset(node, 0, sizeof(ngx_http_sysguard_rt_node_t));\n\n        node->stamp = cur_sec;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"sysguard update rt node: new current: %i\",\n                   ring->current);\n\n    node->sec += cur_sec - r->start_sec;\n    node->msec += cur_msec - r->start_msec;\n    node->requests++;\n}\n\n\nstatic ngx_int_t\nngx_http_sysguard_do_redirect(ngx_http_request_t *r, ngx_str_t *path)\n{\n    if (path->len == 0) {\n        return NGX_HTTP_SERVICE_UNAVAILABLE;\n    } else if (path->data[0] == '@') {\n        (void) ngx_http_named_location(r, path);\n    } else {\n        (void) ngx_http_internal_redirect(r, path, &r->args);\n    }\n\n    ngx_http_finalize_request(r, NGX_DONE);\n\n    return NGX_DONE;\n}\n\n\nstatic ngx_int_t\nngx_http_sysguard_handler(ngx_http_request_t *r)\n{\n    ngx_http_sysguard_conf_t  *glcf;\n    ngx_int_t                  load_log = 0, swap_log = 0,\n                               free_log = 0, rt_log = 0,\n                               cpu_log = 0;\n    ngx_str_t                 *action = NULL;\n\n    if (r->main->sysguard_set) {\n        return NGX_DECLINED;\n    }\n\n    glcf = ngx_http_get_module_loc_conf(r, ngx_http_sysguard_module);\n\n    if (!glcf->enable) {\n        return NGX_DECLINED;\n    }\n\n    r->main->sysguard_set = 1;\n\n    /* load */\n\n    if (glcf->load != NGX_CONF_UNSET) {\n\n        if (ngx_http_sysguard_cached_load_exptime < ngx_time()) {\n            ngx_http_sysguard_update_load(r, glcf->interval);\n        }\n\n        ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"http sysguard handler load: %1.3f %1.3f %V %V\",\n                       ngx_http_sysguard_cached_load * 1.0 / 1000,\n                       glcf->load * 1.0 / 1000,\n                       &r->uri,\n                       &glcf->load_action);\n\n        if (ngx_http_sysguard_cached_load > glcf->load) {\n\n            if (glcf->mode == NGX_HTTP_SYSGUARD_MODE_OR) {\n\n                ngx_log_error(glcf->log_level, r->connection->log, 0,\n                              \"sysguard load limited, current:%1.3f conf:%1.3f\",\n                              ngx_http_sysguard_cached_load * 1.0 / 1000,\n                              glcf->load * 1.0 / 1000);\n\n                return ngx_http_sysguard_do_redirect(r, &glcf->load_action);\n            } else {\n                action = &glcf->load_action;\n                load_log = 1;\n            }\n        } else {\n            if (glcf->mode == NGX_HTTP_SYSGUARD_MODE_AND) {\n                goto out;\n            }\n        }\n    }\n\n    /* cpu */\n\n    if (glcf->cpuusage != NGX_CONF_UNSET) {\n\n        if (ngx_http_sysguard_cached_cpuusage_exptime < ngx_time()) {\n            ngx_http_sysguard_update_cpuusage(r, glcf->interval);\n        }\n\n        ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"http sysguard handler cpuusage: %d %d %V %V\",\n                       ngx_http_sysguard_cached_cpuusage ,\n                       glcf->cpuusage,\n                       &r->uri,\n                       &glcf->cpuusage_action);\n\n        if (ngx_http_sysguard_cached_cpuusage > glcf->cpuusage) {\n\n            if (glcf->mode == NGX_HTTP_SYSGUARD_MODE_OR) {\n\n                ngx_log_error(glcf->log_level, r->connection->log, 0,\n                              \"sysguard cpuusage limited, current:%d conf:%d\",\n                              ngx_http_sysguard_cached_cpuusage,\n                              glcf->cpuusage);\n\n                return ngx_http_sysguard_do_redirect(r, &glcf->cpuusage_action);\n\n            } else {\n                action = &glcf->cpuusage_action;\n                cpu_log = 1;\n            }\n\n        } else {\n            if (glcf->mode == NGX_HTTP_SYSGUARD_MODE_AND) {\n                goto out;\n            }\n        }\n    }\n\n    /* swap */\n\n    if (glcf->swap != NGX_CONF_UNSET) {\n\n        if (ngx_http_sysguard_cached_mem_exptime < ngx_time()) {\n            ngx_http_sysguard_update_mem(r, glcf->interval);\n        }\n\n        ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"http sysguard handler swap: %i %i %V %V\",\n                       ngx_http_sysguard_cached_swapstat,\n                       glcf->swap,\n                       &r->uri,\n                       &glcf->swap_action);\n\n        if (ngx_http_sysguard_cached_swapstat > glcf->swap) {\n\n            if (glcf->mode == NGX_HTTP_SYSGUARD_MODE_OR) {\n\n                ngx_log_error(glcf->log_level, r->connection->log, 0,\n                              \"sysguard swap limited, current:%i conf:%i\",\n                              ngx_http_sysguard_cached_swapstat,\n                              glcf->swap);\n\n                return ngx_http_sysguard_do_redirect(r, &glcf->swap_action);\n            } else {\n                action = &glcf->swap_action;\n                swap_log = 1;\n            }\n        } else {\n            if (glcf->mode == NGX_HTTP_SYSGUARD_MODE_AND) {\n                goto out;\n            }\n        }\n    }\n\n    /* mem free */\n\n    if (glcf->free != NGX_CONF_UNSET_SIZE) {\n\n        if (ngx_http_sysguard_cached_mem_exptime < ngx_time()) {\n            ngx_http_sysguard_update_mem(r, glcf->interval);\n        }\n\n        if (ngx_http_sysguard_cached_free != NGX_CONF_UNSET_SIZE) {\n\n            ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"http sysguard handler free: %uz %uz %V %V\",\n                           ngx_http_sysguard_cached_free,\n                           glcf->free,\n                           &r->uri,\n                           &glcf->free_action);\n\n            if (ngx_http_sysguard_cached_free < glcf->free) {\n\n                if (glcf->mode == NGX_HTTP_SYSGUARD_MODE_OR) {\n\n                    ngx_log_error(glcf->log_level, r->connection->log, 0,\n                                  \"sysguard free limited, \"\n                                  \"current:%uzM conf:%uzM\",\n                                  ngx_http_sysguard_cached_free / 1024 / 1024,\n                                  glcf->free / 1024 / 1024);\n\n                    return ngx_http_sysguard_do_redirect(r, &glcf->free_action);\n                } else {\n                    action = &glcf->free_action;\n                    free_log = 1;\n                }\n            } else {\n                if (glcf->mode == NGX_HTTP_SYSGUARD_MODE_AND) {\n                    goto out;\n                }\n            }\n        }\n    }\n\n    /* response time */\n\n    if (glcf->rt != NGX_CONF_UNSET) {\n\n        if (glcf->rt_ring->cached_rt_exptime < ngx_time()) {\n            ngx_http_sysguard_update_rt(r, glcf->interval);\n        }\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"http sysguard handler rt: %1.3f %1.3f\",\n                       glcf->rt_ring->cached_rt * 1.0 / 1000,\n                       glcf->rt * 1.0 / 1000);\n\n        if (glcf->rt_ring->cached_rt > glcf->rt) {\n\n            if (glcf->mode == NGX_HTTP_SYSGUARD_MODE_OR) {\n\n                ngx_log_error(glcf->log_level, r->connection->log, 0,\n                              \"sysguard rt limited, current:%1.3f conf:%1.3f\",\n                              glcf->rt_ring->cached_rt * 1.0 / 1000,\n                              glcf->rt * 1.0 / 1000);\n\n                return ngx_http_sysguard_do_redirect(r, &glcf->rt_action);\n            } else {\n                action = &glcf->rt_action;\n                rt_log = 1;\n            }\n        } else {\n            if (glcf->mode == NGX_HTTP_SYSGUARD_MODE_AND) {\n                goto out;\n            }\n        }\n    }\n\n    if (glcf->mode == NGX_HTTP_SYSGUARD_MODE_AND && action) {\n\n        if (load_log) {\n            ngx_log_error(glcf->log_level, r->connection->log, 0,\n                          \"sysguard load limited, current:%1.3f conf:%1.3f\",\n                          ngx_http_sysguard_cached_load * 1.0 / 1000,\n                          glcf->load * 1.0 / 1000);\n        }\n\n        if (cpu_log) {\n            ngx_log_error(glcf->log_level, r->connection->log, 0,\n                          \"sysguard cpu limited, current:%d conf:%1d\",\n                          ngx_http_sysguard_cached_cpuusage,\n                          glcf->cpuusage);\n        }\n\n        if (swap_log) {\n            ngx_log_error(glcf->log_level, r->connection->log, 0,\n                          \"sysguard swap limited, current:%i conf:%i\",\n                          ngx_http_sysguard_cached_swapstat,\n                          glcf->swap);\n        }\n\n        if (free_log) {\n            ngx_log_error(glcf->log_level, r->connection->log, 0,\n                          \"sysguard free limited, current:%uzM conf:%uzM\",\n                          ngx_http_sysguard_cached_free / 1024 / 1024,\n                          glcf->free / 1024 / 1024);\n        }\n\n        if (rt_log) {\n            ngx_log_error(glcf->log_level, r->connection->log, 0,\n                          \"sysguard rt limited, current:%1.3f conf:%1.3f\",\n                          glcf->rt_ring->cached_rt * 1.0 / 1000,\n                          glcf->rt * 1.0 / 1000);\n        }\n\n        return ngx_http_sysguard_do_redirect(r, action);\n    }\n\nout:\n    return NGX_DECLINED;\n}\n\n\nstatic void *\nngx_http_sysguard_create_conf(ngx_conf_t *cf)\n{\n    ngx_http_sysguard_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_sysguard_conf_t));\n    if (conf == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    /*\n     * set by ngx_pcalloc():\n     *\n     *     conf->load_action = {0, NULL};\n     *     conf->cpuusage_action = {0, NULL};\n     *     conf->swap_action = {0, NULL};\n     *     conf->rt_action = {0, NULL};\n     *     conf->ring = NULL;\n     */\n\n    conf->enable = NGX_CONF_UNSET;\n    conf->load = NGX_CONF_UNSET;\n    conf->cpuusage = NGX_CONF_UNSET;\n    conf->swap = NGX_CONF_UNSET;\n    conf->free = NGX_CONF_UNSET_SIZE;\n    conf->rt = NGX_CONF_UNSET;\n    conf->rt_period = NGX_CONF_UNSET;\n    conf->interval = NGX_CONF_UNSET;\n    conf->cpu_interval = NGX_CONF_UNSET;\n    conf->log_level = NGX_CONF_UNSET_UINT;\n    conf->mode = NGX_CONF_UNSET_UINT;\n\n    return conf;\n}\n\n\nstatic char *\nngx_http_sysguard_merge_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_http_sysguard_conf_t  *prev = parent;\n    ngx_http_sysguard_conf_t  *conf = child;\n\n    ngx_conf_merge_value(conf->enable, prev->enable, 0);\n    ngx_conf_merge_str_value(conf->load_action, prev->load_action, \"\");\n    ngx_conf_merge_str_value(conf->cpuusage_action, prev->cpuusage_action, \"\");\n    ngx_conf_merge_str_value(conf->swap_action, prev->swap_action, \"\");\n    ngx_conf_merge_str_value(conf->free_action, prev->free_action, \"\");\n    ngx_conf_merge_str_value(conf->rt_action, prev->rt_action, \"\");\n    ngx_conf_merge_value(conf->load, prev->load, NGX_CONF_UNSET);\n    ngx_conf_merge_value(conf->cpuusage, prev->cpuusage, NGX_CONF_UNSET);\n    ngx_conf_merge_value(conf->swap, prev->swap, NGX_CONF_UNSET);\n    ngx_conf_merge_size_value(conf->free, prev->free, NGX_CONF_UNSET_SIZE);\n    ngx_conf_merge_value(conf->rt, prev->rt, NGX_CONF_UNSET);\n    ngx_conf_merge_value(conf->rt_period, prev->rt_period, 1);\n    ngx_conf_merge_value(conf->interval, prev->interval, 1);\n    ngx_conf_merge_value(conf->cpu_interval, prev->cpu_interval, 3);\n    ngx_conf_merge_uint_value(conf->log_level, prev->log_level, NGX_LOG_ERR);\n    ngx_conf_merge_uint_value(conf->mode, prev->mode,\n                              NGX_HTTP_SYSGUARD_MODE_OR);\n\n\n    if (conf->rt != NGX_CONF_UNSET) {\n        /* init glcf->ring */\n        conf->rt_ring = ngx_pcalloc(cf->pool,\n                                    sizeof(ngx_http_sysguard_rt_ring_t));\n        if (conf->rt_ring == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        conf->rt_ring->slots = ngx_pcalloc(cf->pool,\n                         sizeof(ngx_http_sysguard_rt_node_t) * conf->rt_period);\n        if (conf->rt_ring->slots == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        conf->rt_ring->nr_slots = conf->rt_period;\n        conf->rt_ring->current = 0;\n        conf->rt_ring->slots[0].stamp = ngx_time();\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_sysguard_load(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_sysguard_conf_t  *glcf = conf;\n\n    ngx_str_t  *value;\n    ngx_uint_t  i, scale;\n\n    value = cf->args->elts;\n    i = 1;\n    scale = 1;\n\n    if (ngx_strncmp(value[i].data, \"load=\", 5) == 0) {\n\n        if (glcf->load != NGX_CONF_UNSET) {\n            return \"is duplicate\";\n        }\n\n        if (value[i].len == 5) {\n            goto invalid;\n        }\n\n        value[i].data += 5;\n        value[i].len -= 5;\n\n        if (ngx_strncmp(value[i].data, \"ncpu*\", 5) == 0) {\n            value[i].data += 5;\n            value[i].len -= 5;\n            scale = ngx_ncpu;\n        }\n\n        glcf->load = ngx_atofp(value[i].data, value[i].len, 3);\n        if (glcf->load == NGX_ERROR) {\n            goto invalid;\n        }\n\n        glcf->load = glcf->load * scale;\n\n        if (cf->args->nelts == 2) {\n            return NGX_CONF_OK;\n        }\n\n        i++;\n\n        if (ngx_strncmp(value[i].data, \"action=\", 7) != 0) {\n            goto invalid;\n        }\n\n        if (value[i].len == 7) {\n            goto invalid;\n        }\n\n        if (value[i].data[7] != '/' && value[i].data[7] != '@') {\n            goto invalid;\n        }\n\n        glcf->load_action.data = value[i].data + 7;\n        glcf->load_action.len = value[i].len - 7;\n\n        return NGX_CONF_OK;\n    }\n\ninvalid:\n\n    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                       \"invalid parameter \\\"%V\\\"\", &value[i]);\n\n    return NGX_CONF_ERROR;\n}\n\n\nstatic char *\nngx_http_sysguard_cpuusage(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_sysguard_conf_t  *glcf = conf;\n\n    ngx_str_t  *value;\n    ngx_uint_t  i;\n\n    value = cf->args->elts;\n    glcf->cpu_interval = 3;\n\n    for (i = 1; i < cf->args->nelts; i++) {\n        if (ngx_strncmp(value[i].data, \"usage=\", 6) == 0) {\n\n            if (glcf->cpuusage != NGX_CONF_UNSET) {\n                return \"is duplicate\";\n            }\n\n            if (value[i].len == 6) {\n                goto invalid;\n            }\n\n            value[i].data += 6;\n            value[i].len -= 6;\n\n            glcf->cpuusage = ngx_atofp(value[i].data, value[i].len, 2);\n            if (glcf->cpuusage == NGX_ERROR) {\n                goto invalid;\n            }\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"period=\", 7) == 0) {\n\n            if (value[i].len == 7) {\n                goto invalid;\n            }\n\n            value[i].data += 7;\n            value[i].len -= 7;\n            glcf->cpu_interval = ngx_parse_time(&value[i], 1);\n            if (glcf->cpu_interval == (time_t) NGX_ERROR) {\n                goto invalid;\n            }\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"action=\", 7) == 0) {\n\n            if (value[i].len == 7) {\n                goto invalid;\n            }\n\n            if (value[i].data[7] != '/' && value[i].data[7] != '@') {\n                goto invalid;\n            }\n\n            glcf->cpuusage_action.data = value[i].data + 7;\n            glcf->cpuusage_action.len = value[i].len - 7;\n\n            continue;\n        }\n\n    }\n\n    return NGX_CONF_OK;\n\ninvalid:\n\n    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                       \"invalid parameter \\\"%V\\\"\", &value[i]);\n\n    return NGX_CONF_ERROR;\n}\n\n\nstatic char *\nngx_http_sysguard_mem(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_sysguard_conf_t  *glcf = conf;\n\n    ngx_str_t  *value, ss;\n    ngx_uint_t  i;\n\n    value = cf->args->elts;\n    i = 1;\n\n    if (ngx_strncmp(value[i].data, \"swapratio=\", 10) == 0) {\n\n        if (glcf->swap != NGX_CONF_UNSET) {\n            return \"is duplicate\";\n        }\n\n        if (value[i].data[value[i].len - 1] != '%') {\n            goto invalid;\n        }\n\n        glcf->swap = ngx_atofp(value[i].data + 10, value[i].len - 11, 2);\n        if (glcf->swap == NGX_ERROR) {\n            goto invalid;\n        }\n\n        if (cf->args->nelts == 2) {\n            return NGX_CONF_OK;\n        }\n\n        i++;\n\n        if (ngx_strncmp(value[i].data, \"action=\", 7) != 0) {\n            goto invalid;\n        }\n\n        if (value[i].len == 7) {\n            goto invalid;\n        }\n\n        if (value[i].data[7] != '/' && value[i].data[7] != '@') {\n            goto invalid;\n        }\n\n        glcf->swap_action.data = value[i].data + 7;\n        glcf->swap_action.len = value[i].len - 7;\n\n        return NGX_CONF_OK;\n\n    } else if (ngx_strncmp(value[i].data, \"free=\", 5) == 0) {\n\n        if (glcf->free != NGX_CONF_UNSET_SIZE) {\n            return \"is duplicate\";\n        }\n\n        ss.data = value[i].data + 5;\n        ss.len = value[i].len - 5;\n\n        glcf->free = ngx_parse_size(&ss);\n        if (glcf->free == (size_t) NGX_ERROR) {\n            goto invalid;\n        }\n\n        if (cf->args->nelts == 2) {\n            return NGX_CONF_OK;\n        }\n\n        i++;\n\n        if (ngx_strncmp(value[i].data, \"action=\", 7) != 0) {\n            goto invalid;\n        }\n\n        if (value[i].len == 7) {\n            goto invalid;\n        }\n\n        if (value[i].data[7] != '/' && value[i].data[7] != '@') {\n            goto invalid;\n        }\n\n        glcf->free_action.data = value[i].data + 7;\n        glcf->free_action.len = value[i].len - 7;\n\n        return NGX_CONF_OK;\n    }\n\ninvalid:\n\n    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                       \"invalid parameter \\\"%V\\\"\", &value[i]);\n\n    return NGX_CONF_ERROR;\n}\n\n\nstatic char *\nngx_http_sysguard_rt(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_sysguard_conf_t  *glcf = conf;\n\n    ngx_str_t  *value, ss;\n    ngx_uint_t  i;\n\n    value = cf->args->elts;\n\n    for (i = 1; i < cf->args->nelts; i++) {\n        if (ngx_strncmp(value[i].data, \"rt=\", 3) == 0) {\n\n            if (glcf->rt != NGX_CONF_UNSET) {\n                return \"is duplicate\";\n            }\n\n            glcf->rt = ngx_atofp(value[i].data + 3, value[i].len - 3, 3);\n            if (glcf->rt == NGX_ERROR) {\n                goto invalid;\n            }\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"period=\", 7) == 0) {\n\n            ss.data = value[i].data + 7;\n            ss.len = value[i].len - 7;\n\n            glcf->rt_period = ngx_parse_time(&ss, 1);\n            if (glcf->rt_period == NGX_ERROR) {\n                goto invalid;\n            }\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"action=\", 7) == 0) {\n\n            if (value[i].len == 7) {\n                goto invalid;\n            }\n\n            if (value[i].data[7] != '/' && value[i].data[7] != '@') {\n                goto invalid;\n            }\n\n            glcf->rt_action.data = value[i].data + 7;\n            glcf->rt_action.len = value[i].len - 7;\n\n            continue;\n        }\n    }\n\n    return NGX_CONF_OK;\n\ninvalid:\n\n    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                       \"invalid parameter \\\"%V\\\"\", &value[i]);\n\n    return NGX_CONF_ERROR;\n}\n\n\nstatic ngx_int_t\nngx_http_sysguard_log_handler(ngx_http_request_t *r)\n{\n    ngx_http_sysguard_update_rt_node(r);\n    ngx_http_sysguard_update_cpuinfo(r);\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_sysguard_init(ngx_conf_t *cf)\n{\n    ngx_http_handler_pt        *h;\n    ngx_http_core_main_conf_t  *cmcf;\n\n    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);\n\n    h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers);\n    if (h == NULL) {\n        return NGX_ERROR;\n    }\n\n    *h = ngx_http_sysguard_handler;\n\n    h = ngx_array_push(&cmcf->phases[NGX_HTTP_LOG_PHASE].handlers);\n    if (h == NULL) {\n        return NGX_ERROR;\n    }\n\n    *h = ngx_http_sysguard_log_handler;\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "modules/ngx_http_tfs_module/config",
    "content": "ngx_feature_name=\nngx_feature_run=no\nngx_feature_incs=\"#include <yajl/yajl_parse.h> \\\n                  #include <yajl/yajl_gen.h>\"\nngx_feature_test=\"yajl_version();\"\n\nif [ -n \"$LIBYAJL_INC\" -o -n \"$LIBYAJL_LIB\" ]; then\n    # explicit set libdyajl lib path\n    ngx_feature=\"libdyajl library in directories specified by LIBYAJL_INC ($LIBYAJL_INC) and LIBYAJL_LIB ($LIBYAJL_LIB)\"\n    ngx_feature_path=\"$LIBYAJL_INC\"\n    if [ $NGX_RPATH = YES ]; then\n        ngx_feature_libs=\"-R$LIBYAJL_LIB -L$LIBYAJL_LIB -lyajl\"\n    else\n        ngx_feature_libs=\"-L$LIBYAJL_LIB -lyajl\"\n    fi\n    . auto/feature\nelse\n    # auto-discovery\n    ngx_feature=\"libyajl library\"\n    ngx_feature_path=\n    ngx_feature_libs=\"-lyajl\"\n    . auto/feature\n\n    if [ $ngx_found = no ]; then\n        # FreeBSD, OpenBSD, linux\n        ngx_feature=\"libyajl library in /usr/local/\"\n        ngx_feature_path=\"/usr/local/include/yajl /usr/local/include\"\n        if [ $NGX_RPATH = YES ]; then\n            ngx_feature_libs=\"-R/usr/local/lib -L/usr/local/lib -lyajl\"\n        else\n            ngx_feature_libs=\"-L/usr/local/lib -lyajl\"\n        fi\n        . auto/feature\n    fi\n\n    if [ $ngx_found = no ]; then\n        # NetBSD\n        ngx_feature=\"libyajl library in /usr/pkg/\"\n        ngx_feature_path=\"/usr/pkg/include/yajl /usr/pkg/include\"\n        if [ $NGX_RPATH = YES ]; then\n            ngx_feature_libs=\"-R/usr/pkg/lib -L/usr/pkg/lib -lyajl\"\n        else\n            ngx_feature_libs=\"-L/usr/pkg/lib -lyajl\"\n        fi\n        . auto/feature\n    fi\n\n    if [ $ngx_found = no ]; then\n        # MacPorts\n        ngx_feature=\"libyajl library in /opt/local/\"\n        ngx_feature_path=\"/opt/local/include/yajl /opt/local/include\"\n        if [ $NGX_RPATH = YES ]; then\n            ngx_feature_libs=\"-R/opt/local/lib -L/opt/local/lib -lyajl\"\n        else\n            ngx_feature_libs=\"-L/opt/local/lib -lyajl\"\n        fi\n        . auto/feature\n    fi\nfi\n\nif [ $ngx_found = yes ]; then\n    CORE_INCS=\"$CORE_INCS $ngx_feature_path\"\n    CORE_LIBS=\"$CORE_LIBS $ngx_feature_libs\"\nelse\n cat << END\n $0: error: the ngx_tfs addon requires the libyajl library.\nEND\n exit 1\nfi\n\n\nngx_addon_name=\"ngx_http_tfs_module\"\nHTTP_MODULES=\"$HTTP_MODULES ngx_http_tfs_module\"\n\nHTTP_TFS=YES\n\nNGX_ADDON_SRCS=\"$NGX_ADDON_SRCS \n                      $ngx_addon_dir/ngx_tfs_common.c \\\n                      $ngx_addon_dir/ngx_http_tfs.c \\\n                      $ngx_addon_dir/ngx_http_tfs_module.c \\\n                      $ngx_addon_dir/ngx_http_tfs_restful.c \\\n                      $ngx_addon_dir/ngx_http_tfs_serialization.c \\\n                      $ngx_addon_dir/ngx_http_connection_pool.c \\\n                      $ngx_addon_dir/ngx_http_tfs_json.c \\\n                      $ngx_addon_dir/ngx_http_tfs_rc_server_info.c \\\n                      $ngx_addon_dir/ngx_http_tfs_raw_fsname.c \\\n                      $ngx_addon_dir/ngx_http_tfs_tair_helper.c \\\n                      $ngx_addon_dir/ngx_http_tfs_duplicate.c \\\n                      $ngx_addon_dir/ngx_http_tfs_block_cache.c \\\n                      $ngx_addon_dir/ngx_http_tfs_local_block_cache.c \\\n                      $ngx_addon_dir/ngx_http_tfs_remote_block_cache.c \\\n                      $ngx_addon_dir/ngx_http_tfs_timers.c \\\n                      $ngx_addon_dir/ngx_http_tfs_rc_server_message.c \\\n                      $ngx_addon_dir/ngx_http_tfs_name_server_message.c \\\n                      $ngx_addon_dir/ngx_http_tfs_data_server_message.c \\\n                      $ngx_addon_dir/ngx_http_tfs_root_server_message.c \\\n                      $ngx_addon_dir/ngx_http_tfs_meta_server_message.c \\\n                      $ngx_addon_dir/ngx_http_tfs_peer_connection.c \\\n                      $ngx_addon_dir/ngx_http_tfs_server_handler.c \"\n\n\nHTTP_INCS=\"$HTTP_INCS $ngx_addon_dir\"\n\nNGX_ADDON_DEPS=\"$NGX_ADDON_DEPS $ngx_addon_dir/ngx_http_tfs.h \\\n                      $ngx_addon_dir/ngx_http_tfs_errno.h \\\n                      $ngx_addon_dir/ngx_tfs_common.h \\\n                      $ngx_addon_dir/ngx_http_tfs_restful.h \\\n                      $ngx_addon_dir/ngx_http_tfs_restful.h \\\n                      $ngx_addon_dir/ngx_http_tfs_protocol.h \\\n                      $ngx_addon_dir/ngx_http_tfs_serialization.h \\\n                      $ngx_addon_dir/ngx_http_connection_pool.h \\\n                      $ngx_addon_dir/ngx_http_tfs_json.h \\\n                      $ngx_addon_dir/ngx_http_tfs_rc_server_info.h \\\n                      $ngx_addon_dir/ngx_http_tfs_raw_fsname.h \\\n                      $ngx_addon_dir/ngx_http_tfs_tair_helper.h \\\n                      $ngx_addon_dir/ngx_http_tfs_duplicate.h \\\n                      $ngx_addon_dir/ngx_http_tfs_block_cache.h \\\n                      $ngx_addon_dir/ngx_http_tfs_local_block_cache.h \\\n                      $ngx_addon_dir/ngx_http_tfs_remote_block_cache.h \\\n                      $ngx_addon_dir/ngx_http_tfs_timers.h \\\n                      $ngx_addon_dir/ngx_http_tfs_rc_server_message.h \\\n                      $ngx_addon_dir/ngx_http_tfs_name_server_message.h \\\n                      $ngx_addon_dir/ngx_http_tfs_data_server_message.h \\\n                      $ngx_addon_dir/ngx_http_tfs_root_server_message.h \\\n                      $ngx_addon_dir/ngx_http_tfs_meta_server_message.h \\\n                      $ngx_addon_dir/ngx_http_tfs_peer_connection.h \\\n                      $ngx_addon_dir/ngx_http_tfs_server_handler.h \"\n"
  },
  {
    "path": "modules/ngx_http_tfs_module/ngx_http_connection_pool.c",
    "content": "\n/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#include <ngx_http_connection_pool.h>\n\n\nstatic void ngx_http_connection_pool_close(ngx_connection_t *c);\nstatic void ngx_http_connection_pool_close_handler(ngx_event_t *ev);\nstatic void ngx_http_connection_pool_dummy_handler(ngx_event_t *ev);\n\nstatic ngx_int_t ngx_http_connection_pool_get(ngx_peer_connection_t *pc,\n    void *data);\nstatic void ngx_http_connection_pool_free(ngx_peer_connection_t *pc,\n    void *data, ngx_uint_t state);\n\n\nngx_http_connection_pool_t *\nngx_http_connection_pool_init(ngx_pool_t *pool, ngx_uint_t max_cached,\n    ngx_uint_t bucket_count)\n{\n    ngx_uint_t                      j, k;\n    ngx_http_connection_pool_t     *conn_pool;\n    ngx_http_connection_pool_elt_t *cached;\n\n    conn_pool = ngx_pcalloc(pool, sizeof(ngx_http_connection_pool_t));\n    if (conn_pool == NULL) {\n        return NULL;\n    }\n\n    conn_pool->bucket_count = bucket_count;\n    conn_pool->max_cached = max_cached;\n\n    conn_pool->cache = ngx_pcalloc(pool, sizeof(ngx_queue_t) * bucket_count);\n    if (conn_pool->cache == NULL) {\n        return NULL;\n    }\n\n    conn_pool->free = ngx_pcalloc(pool, sizeof(ngx_queue_t) * bucket_count);\n    if (conn_pool->free == NULL) {\n        return NULL;\n    }\n\n    for (j = 0; j < bucket_count; j++) {\n        ngx_queue_init(&conn_pool->cache[j]);\n        ngx_queue_init(&conn_pool->free[j]);\n        cached = ngx_pcalloc(pool,\n                           sizeof(ngx_http_connection_pool_elt_t) * max_cached);\n        if (cached == NULL) {\n            return NULL;\n        }\n\n        for (k = 0; k < max_cached; k++) {\n            ngx_queue_insert_head(&conn_pool->free[j], &cached[k].queue);\n        }\n    }\n\n    conn_pool->get_peer = ngx_http_connection_pool_get;\n    conn_pool->free_peer = ngx_http_connection_pool_free;\n    return conn_pool;\n}\n\n\nngx_int_t\nngx_http_connection_pool_get(ngx_peer_connection_t *pc, void *data)\n{\n    u_char                         pc_addr[32] = {'\\0'};\n    ngx_uint_t                     bucket_id, hash;\n    ngx_queue_t                    *q, *cache, *free;\n    ngx_connection_t               *c;\n    ngx_http_connection_pool_t     *p;\n    ngx_http_connection_pool_elt_t *item;\n\n    p = data;\n\n#if (NGX_DEBUG)\n    p->count--;\n#endif\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0, \"get keepalive peer\");\n\n    p->failed = 0;\n\n    hash = ngx_murmur_hash2((u_char *) pc->sockaddr, pc->socklen);\n    bucket_id = hash % p->bucket_count;\n\n    cache = &p->cache[bucket_id];\n    free = &p->free[bucket_id];\n\n    ngx_sprintf(pc_addr, \"%s:%d\",\n                inet_ntoa(((struct sockaddr_in*)(pc->sockaddr))->sin_addr),\n                ntohs(((struct sockaddr_in*)(pc->sockaddr))->sin_port));\n\n    for (q = ngx_queue_head(cache);\n         q != ngx_queue_sentinel(cache);\n         q = ngx_queue_next(q))\n    {\n        item = ngx_queue_data(q, ngx_http_connection_pool_elt_t, queue);\n        c = item->connection;\n\n        if (ngx_memn2cmp((u_char *) &item->sockaddr, (u_char *) pc->sockaddr,\n                         item->socklen, pc->socklen)\n            == 0)\n        {\n            ngx_queue_remove(q);\n            ngx_queue_insert_head(free, q);\n\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                           \"get keepalive peer: using connection %p\", c);\n\n            c->idle = 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            pc->connection = c;\n            pc->cached = 1;\n\n            item->free = free;\n            return NGX_DONE;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nvoid\nngx_http_connection_pool_free(ngx_peer_connection_t *pc,\n    void *data, ngx_uint_t state)\n{\n    ngx_http_connection_pool_t     *p = data;\n    ngx_http_connection_pool_elt_t *item;\n\n    ngx_uint_t         hash, bucket_id;\n    ngx_queue_t       *q, *cache, *free;\n    ngx_connection_t  *c;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0, \"free keepalive peer\");\n\n    /* remember failed state - peer.free() may be called more than once */\n\n    if (state & NGX_PEER_FAILED) {\n        p->failed = 1;\n    }\n\n    /* cache valid connections */\n\n    c = pc->connection;\n\n    if (p->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        return;\n    }\n\n    if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n        return;\n    }\n\n#if (NGX_DEBUG)\n    p->count++;\n#endif\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                   \"free keepalive peer: saving connection %p\", c);\n\n    hash = ngx_murmur_hash2((u_char *) pc->sockaddr, pc->socklen);\n    bucket_id = hash % p->bucket_count;\n\n    cache = &p->cache[bucket_id];\n    free = &p->free[bucket_id];\n\n    if (ngx_queue_empty(free)) {\n        q = ngx_queue_last(cache);\n        ngx_queue_remove(q);\n\n        item = ngx_queue_data(q, ngx_http_connection_pool_elt_t, queue);\n\n        ngx_http_connection_pool_close(item->connection);\n\n    } else {\n        q = ngx_queue_head(free);\n        ngx_queue_remove(q);\n\n        item = ngx_queue_data(q, ngx_http_connection_pool_elt_t, queue);\n    }\n\n    item->connection = c;\n    item->free = free;\n    ngx_queue_insert_head(cache, q);\n\n    pc->connection = NULL;\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->write->handler = ngx_http_connection_pool_dummy_handler;\n    c->read->handler = ngx_http_connection_pool_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\n    if (c->read->ready) {\n        ngx_http_connection_pool_close_handler(c->read);\n    }\n}\n\n\nstatic void\nngx_http_connection_pool_close_handler(ngx_event_t *ev)\n{\n    ngx_http_connection_pool_elt_t  *item;\n\n    int                n;\n    char               buf[1];\n    ngx_connection_t  *c;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0, \"keepalive close handler\");\n\n    c = ev->data;\n\n    if (c->close) {\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        /* stale event */\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\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0,\n                   \"connection pool close connection\");\n\n    ngx_http_connection_pool_close(c);\n\n    ngx_queue_remove(&item->queue);\n    ngx_queue_insert_head(item->free, &item->queue);\n}\n\n\nstatic void\nngx_http_connection_pool_close(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        if (ngx_ssl_shutdown(c) == NGX_AGAIN) {\n            c->ssl->handler = ngx_http_connection_pool_close;\n            return;\n        }\n    }\n\n#endif\n\n    ngx_destroy_pool(c->pool);\n    ngx_close_connection(c);\n}\n\n\nstatic void\nngx_http_connection_pool_dummy_handler(ngx_event_t *ev)\n{\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0, \"keepalive dummy handler\");\n}\n\n#if (NGX_DEBUG)\nvoid\nngx_http_connection_pool_check(ngx_http_connection_pool_t *conn_pool,\n    ngx_log_t *log)\n{\n    if (conn_pool->count != 0) {\n        ngx_log_error(NGX_LOG_ERR, log, 0,\n                      \"<== conn pool check ==> \"\n                      \"some keepalive peer do not free!,  conn_pool count: %i\",\n                      conn_pool->count);\n\n    } else {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, log, 0,\n                       \"<== conn pool check ==> all keepalive peers are free\");\n    }\n}\n#endif\n"
  },
  {
    "path": "modules/ngx_http_tfs_module/ngx_http_connection_pool.h",
    "content": "\n/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#ifndef _NGX_HTTP_CONNECTION_POOL_H_INCLUDED_\n#define _NGX_HTTP_CONNECTION_POOL_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\ntypedef struct ngx_http_connection_pool_s ngx_http_connection_pool_t;\n\n\ntypedef struct {\n    ngx_queue_t              queue;\n    ngx_connection_t        *connection;\n    socklen_t                socklen;\n    u_char                   sockaddr[NGX_SOCKADDRLEN];\n    ngx_queue_t             *free;\n} ngx_http_connection_pool_elt_t;\n\n\nstruct ngx_http_connection_pool_s {\n    ngx_queue_t             *cache;\n    ngx_queue_t             *free;\n    ngx_uint_t               max_cached;\n    ngx_uint_t               bucket_count;\n\n    ngx_uint_t               failed;       /* unsigned:1 */\n    ngx_pool_t              *pool;\n\n#if (NGX_DEBUG)\n    ngx_int_t                count;        /* check get&free op pairs */\n#endif\n\n    ngx_event_get_peer_pt    get_peer;\n    ngx_event_free_peer_pt   free_peer;\n};\n\n\nngx_http_connection_pool_t *ngx_http_connection_pool_init(ngx_pool_t *pool,\n    ngx_uint_t max_count, ngx_uint_t bucket_count);\n\n#if (NGX_DEBUG)\nvoid ngx_http_connection_pool_check(ngx_http_connection_pool_t *coon_pool,\n    ngx_log_t *log);\n#endif\n\n\n#endif  /* _NGX_HTTP_CONNECTION_POOL_H_INCLUDED_ */\n"
  },
  {
    "path": "modules/ngx_http_tfs_module/ngx_http_tfs.c",
    "content": "\n/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#include <nginx.h>\n#include <ngx_http_tfs.h>\n#include <ngx_http_tfs_errno.h>\n#include <ngx_http_tfs_duplicate.h>\n#include <ngx_http_tfs_root_server_message.h>\n#include <ngx_http_tfs_meta_server_message.h>\n#include <ngx_http_tfs_rc_server_message.h>\n#include <ngx_http_tfs_name_server_message.h>\n#include <ngx_http_tfs_data_server_message.h>\n#include <ngx_http_tfs_remote_block_cache.h>\n\n\n#define ngx_http_tfs_clear_content_len()\\\n    t->header_only |= 1;                \\\n    r->headers_out.content_length_n = 0\n\n\nstatic ngx_str_t rcs_name = ngx_string(\"rc server\");\nstatic ngx_str_t ds_name = ngx_string(\"data server\");\nstatic ngx_str_t ms_name = ngx_string(\"meta server\");\n\n\nstatic void ngx_http_tfs_event_handler(ngx_event_t *ev);\n\nstatic void ngx_http_tfs_process_buf_overflow(ngx_http_request_t *r,\n    ngx_http_tfs_t *t);\nstatic void ngx_http_tfs_set_header_line(ngx_http_tfs_t *t);\n\nstatic void ngx_http_tfs_dummy_handler(ngx_http_request_t *r,\n    ngx_http_tfs_t *t);\nstatic void ngx_http_tfs_read_handler(ngx_http_request_t *r, ngx_http_tfs_t *t);\nstatic void ngx_http_tfs_send_handler(ngx_http_request_t *r, ngx_http_tfs_t *t);\nstatic void ngx_http_tfs_send(ngx_http_request_t *r, ngx_http_tfs_t *t);\nstatic void ngx_http_tfs_send_response(ngx_http_request_t *r,\n    ngx_http_tfs_t *t);\n\nstatic void ngx_http_tfs_process_non_buffered_downstream(ngx_http_request_t *r);\nstatic void ngx_http_tfs_process_non_buffered_request(ngx_http_tfs_t *t,\n    ngx_uint_t do_write);\n\nstatic void ngx_http_tfs_process_upstream_request(ngx_http_request_t *r,\n    ngx_http_tfs_t *t);\n\nstatic void ngx_http_tfs_handle_connection_failure(ngx_http_tfs_t *t,\n    ngx_http_tfs_peer_connection_t *tp);\nstatic void ngx_http_tfs_rd_check_broken_connection(ngx_http_request_t *r);\nstatic void ngx_http_tfs_wr_check_broken_connection(ngx_http_request_t *r);\nstatic void ngx_http_tfs_check_broken_connection(ngx_http_request_t *r,\n    ngx_event_t *ev);\n\n\nextern ngx_module_t  ngx_http_tfs_module;\n\n\nngx_int_t\nngx_http_tfs_init(ngx_http_tfs_t *t)\n{\n    ngx_int_t                  rc;\n    ngx_http_request_t        *r;\n    ngx_http_tfs_rc_ctx_t     *rc_ctx;\n    ngx_http_tfs_rcs_info_t   *rc_info;\n    ngx_http_tfs_upstream_t   *upstream;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    t->read_event_handler = ngx_http_tfs_read_handler;\n    t->write_event_handler = ngx_http_tfs_send_handler;\n    r = NULL;\n    rc_info = NULL;\n    rc_ctx = NULL;\n    upstream = t->loc_conf->upstream;\n\n    if (t->r_ctx.action.code != NGX_HTTP_TFS_ACTION_KEEPALIVE) {\n        r = t->data;\n        r->read_event_handler = ngx_http_tfs_rd_check_broken_connection;\n        r->write_event_handler = ngx_http_tfs_wr_check_broken_connection;\n\n        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n        if (clcf == NULL) {\n            return NGX_ERROR;\n        }\n\n        t->output.alignment = clcf->directio_alignment;\n        t->output.bufs.size = clcf->client_body_buffer_size;\n\n    } else {\n        /* rc-keepalive */\n        t->output.alignment = 512;\n        t->output.bufs.size = (size_t) 2 * ngx_pagesize;\n    }\n\n    t->output.pool = t->pool;\n    t->output.bufs.num = 1;\n    t->output.output_filter = ngx_chain_writer;\n    t->output.filter_ctx = &t->writer;\n    t->header_size = sizeof(ngx_http_tfs_header_t);\n    t->writer.pool = t->pool;\n\n    rc = ngx_http_tfs_peer_init(t);\n    if (rc != NGX_OK) {\n        ngx_log_error(NGX_LOG_ERR, t->log, 0,\n                      \"tfs peer init failed\");\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    /* header and body */\n    t->recv_chain = ngx_http_tfs_alloc_chains(t->pool, 2);\n    if (t->recv_chain == NULL) {\n        ngx_log_error(NGX_LOG_ERR, t->log, 0,\n                      \"tfs alloc chains failed\");\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    if (t->r_ctx.action.code != NGX_HTTP_TFS_ACTION_KEEPALIVE) {\n\n        if (!upstream->enable_rcs) {\n            switch(t->r_ctx.action.code) {\n            case NGX_HTTP_TFS_ACTION_REMOVE_FILE:\n                t->state = NGX_HTTP_TFS_STATE_REMOVE_GET_BLK_INFO;\n                break;\n            case NGX_HTTP_TFS_ACTION_STAT_FILE:\n                t->state = NGX_HTTP_TFS_STATE_STAT_GET_BLK_INFO;\n                break;\n            case NGX_HTTP_TFS_ACTION_READ_FILE:\n                t->state = NGX_HTTP_TFS_STATE_READ_GET_BLK_INFO;\n                break;\n            case NGX_HTTP_TFS_ACTION_WRITE_FILE:\n                t->state = NGX_HTTP_TFS_STATE_WRITE_CLUSTER_ID_NS;\n                break;\n            default:\n                ngx_shmtx_unlock(&rc_ctx->shpool->mutex);\n                return NGX_ERROR;\n            }\n\n            t->name_server_addr.ip =\n                ((struct sockaddr_in*)\n                 (upstream->ups_addr->sockaddr))->sin_addr.s_addr;\n            t->name_server_addr.port =\n                ntohs(((struct sockaddr_in*)\n                       (upstream->ups_addr->sockaddr))->sin_port);\n\n            /* skip get cluster id from ns */\n            if (t->r_ctx.action.code == NGX_HTTP_TFS_ACTION_WRITE_FILE) {\n                if (t->main_conf->cluster_id > 0) {\n                    t->file.cluster_id = t->main_conf->cluster_id;\n                    t->state = NGX_HTTP_TFS_STATE_WRITE_GET_BLK_INFO;\n                }\n\n                /* prepare each segment's data */\n                if (t->is_large_file) {\n                    rc = ngx_http_tfs_get_segment_for_write(t);\n                    if (rc == NGX_ERROR) {\n                        return NGX_ERROR;\n                    }\n                }\n            }\n\n            if (!t->is_large_file\n                || (t->r_ctx.action.code != NGX_HTTP_TFS_ACTION_WRITE_FILE))\n            {\n                /* fill meta segment */\n                rc = ngx_http_tfs_get_meta_segment(t);\n                if (rc == NGX_ERROR) {\n                    ngx_log_error(NGX_LOG_ERR, t->log, 0,\n                                  \"tfs get meta segment failed\");\n                    return NGX_HTTP_INTERNAL_SERVER_ERROR;\n                }\n\n                /* small file */\n                if (t->r_ctx.action.code == NGX_HTTP_TFS_ACTION_WRITE_FILE) {\n                    /* parse meta segment */\n                    if (t->r_ctx.write_meta_segment) {\n                        rc = ngx_http_tfs_parse_meta_segment(t, t->send_body);\n                        if (rc == NGX_ERROR) {\n                            return NGX_ERROR;\n                        }\n                        t->send_body = t->meta_segment_data;\n                    }\n\n                    t->file.segment_data[0].data = t->send_body;\n                    t->file.segment_data[0].segment_info.size =\n                                  ngx_http_tfs_get_chain_buf_size(t->send_body);\n                    t->file.left_length =\n                                      t->file.segment_data[0].segment_info.size;\n                    t->file.segment_data[0].oper_size =\n                                        ngx_min(t->file.left_length,\n                                                NGX_HTTP_TFS_MAX_FRAGMENT_SIZE);\n\n                } else {\n                    /* set oper size && offset,\n                     * large file must read the whole meta segment */\n                    if (t->is_large_file) {\n                        t->is_process_meta_seg = NGX_HTTP_TFS_YES;\n                        t->file.file_offset = 0;\n                        t->file.left_length = NGX_HTTP_TFS_MAX_SIZE;\n\n                    } else {\n                        t->file.file_offset = t->r_ctx.offset;\n                        t->file.left_length = t->r_ctx.size;\n                    }\n\n                    t->file.segment_data[0].oper_offset = t->file.file_offset;\n                    t->file.segment_data[0].oper_size =\n                                       ngx_min(t->file.left_length,\n                                               NGX_HTTP_TFS_MAX_READ_FILE_SIZE);\n                }\n            }\n\n        } else {\n            /* skip rc server */\n            rc_ctx = upstream->rc_ctx;\n            ngx_shmtx_lock(&rc_ctx->shpool->mutex);\n            rc_info = ngx_http_tfs_rcs_lookup(rc_ctx, t->r_ctx.appkey);\n            ngx_shmtx_unlock(&rc_ctx->shpool->mutex);\n            if (rc_info != NULL) {\n                t->rc_info_node = rc_info;\n\n                if (t->r_ctx.action.code == NGX_HTTP_TFS_ACTION_GET_APPID) {\n                    rc = ngx_http_tfs_set_output_appid(t, rc_info->app_id);\n                    if (rc == NGX_ERROR) {\n                        ngx_log_error(NGX_LOG_ERR, t->log, 0,\n                                      \"tfs set output appid failed\");\n                        return NGX_ERROR;\n                    }\n\n                    ngx_http_tfs_send_response(r, t);\n                    return NGX_OK;\n                }\n\n                /* TODO: use fine granularity mutex(per rc_info_node mutex) */\n                rc = ngx_http_tfs_misc_ctx_init(t, rc_info);\n                if (rc == NGX_DECLINED) {\n                    if (t->decline_handler) {\n                        rc = t->decline_handler(t);\n                        if (rc == NGX_ERROR) {\n                            return rc;\n                        }\n                    }\n                    return NGX_OK;\n                }\n\n                if (rc != NGX_OK) {\n                    return rc;\n                }\n            }\n        }\n    }\n\n    t->tfs_peer = ngx_http_tfs_select_peer(t);\n    if (t->tfs_peer == NULL) {\n        ngx_log_error(NGX_LOG_ERR, t->log, 0, \"tfs select peer failed\");\n\n        return NGX_ERROR;\n    }\n\n    t->recv_chain->buf = &t->header_buffer;\n    t->recv_chain->next->buf = &t->tfs_peer->body_buffer;\n\n    ngx_http_tfs_connect(t);\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_tfs_lookup_block_cache(ngx_http_tfs_t *t)\n{\n    ngx_int_t                         rc;\n    ngx_http_tfs_inet_t              *addr;\n    ngx_http_tfs_segment_data_t      *segment_data;\n    ngx_http_tfs_block_cache_key_t    key;\n    ngx_http_tfs_block_cache_value_t  value;\n\n    segment_data = &t->file.segment_data[t->file.segment_index];\n    key.ns_addr = *((uint64_t*)(&t->name_server_addr));\n    key.block_id = segment_data->segment_info.block_id;\n\n    rc = ngx_http_tfs_block_cache_lookup(&t->block_cache_ctx, t->pool, t->log,\n                                         &key, &value);\n\n    switch (rc) {\n    case NGX_DECLINED:\n        /* remote cache handler will deal */\n        if (t->block_cache_ctx.use_cache & NGX_HTTP_TFS_REMOTE_BLOCK_CACHE) {\n            return NGX_DECLINED;\n        }\n        break;\n    case NGX_OK:\n        /* local cache hit */\n        segment_data->cache_hit = NGX_HTTP_TFS_LOCAL_BLOCK_CACHE;\n\n        segment_data->block_info.ds_count = value.ds_count;\n        segment_data->block_info.ds_addrs = (ngx_http_tfs_inet_t *)\n                                             value.ds_addrs;\n\n        addr = ngx_http_tfs_select_data_server(t, segment_data);\n        if (addr == NULL) {\n            ngx_http_tfs_remove_block_cache(t, segment_data);\n\n        } else {\n            /* skip GET_BLK_INFO state */\n            t->state += 1;\n            ngx_http_tfs_peer_set_addr(t->pool,\n                          &t->tfs_peer_servers[NGX_HTTP_TFS_DATA_SERVER], addr);\n        }\n\n       break;\n    case NGX_ERROR:\n        /* block cache should not affect, go for ns */\n        ngx_log_error(NGX_LOG_ERR, t->log, 0,\n                      \"lookup block cache failed.\");\n        break;\n    }\n    rc = NGX_OK;\n\n    ngx_http_tfs_finalize_state(t, rc);\n\n    return rc;\n}\n\n\nvoid\nngx_http_tfs_remove_block_cache(ngx_http_tfs_t *t,\n    ngx_http_tfs_segment_data_t *segment_data)\n{\n    ngx_http_tfs_block_cache_key_t  key;\n\n    key.ns_addr = *((int64_t *)(&t->name_server_addr));\n    key.block_id = segment_data->segment_info.block_id;\n    ngx_http_tfs_block_cache_remove(&t->block_cache_ctx, t->pool, t->log,\n                                    &key, segment_data->cache_hit);\n\n    /* only when cache dirty, need retry current ns */\n    if (segment_data->cache_hit != NGX_HTTP_TFS_NO_BLOCK_CACHE) {\n        t->retry_curr_ns = NGX_HTTP_TFS_YES;\n    }\n\n    segment_data->cache_hit = NGX_HTTP_TFS_NO_BLOCK_CACHE;\n}\n\n\nngx_int_t\nngx_http_tfs_batch_lookup_block_cache(ngx_http_tfs_t *t)\n{\n    uint32_t                         i, j, block_count;\n    ngx_int_t                        rc;\n    ngx_array_t                      keys, kvs;\n    ngx_http_tfs_segment_data_t     *segment_data;\n    ngx_http_tfs_block_cache_kv_t   *kv;\n    ngx_http_tfs_block_cache_key_t  *key;\n\n    block_count = t->file.segment_count - t->file.segment_index;\n    if (block_count > NGX_HTTP_TFS_MAX_BATCH_COUNT) {\n        block_count = NGX_HTTP_TFS_MAX_BATCH_COUNT;\n    }\n\n    rc = ngx_array_init(&keys, t->pool, block_count,\n                        sizeof(ngx_http_tfs_block_cache_key_t));\n    if (rc == NGX_ERROR) {\n        return rc;\n    }\n\n    segment_data = &t->file.segment_data[t->file.segment_index];\n    for (i = 0; i < block_count; i++) {\n        key = (ngx_http_tfs_block_cache_key_t *) ngx_array_push(&keys);\n        key->ns_addr = *((uint64_t*)(&t->name_server_addr));\n        key->block_id = segment_data[i].segment_info.block_id;\n    }\n\n    rc = ngx_array_init(&kvs, t->pool, block_count,\n                        sizeof(ngx_http_tfs_block_cache_kv_t));\n    if (rc == NGX_ERROR) {\n        return rc;\n    }\n\n    rc = ngx_http_tfs_block_cache_batch_lookup(&t->block_cache_ctx,\n                                               t->pool, t->log,\n                                               &keys, &kvs);\n    /* local cache hit(maybe partial) */\n    if (rc != NGX_ERROR && kvs.nelts > 0) {\n        /* local block cache hit count */\n        t->file.curr_batch_count += kvs.nelts;\n        kv = kvs.elts;\n        for (i = 0; i < kvs.nelts; i++, kv++) {\n            /* find out segment */\n            for (j = 0; j < block_count; j++) {\n                if (segment_data[j].segment_info.block_id == kv->key->block_id\n                    && segment_data[j].block_info.ds_addrs == NULL)\n                {\n                    break;\n                }\n            }\n\n            segment_data[j].block_info.ds_count = kv->value->ds_count;\n            segment_data[j].block_info.ds_addrs = (ngx_http_tfs_inet_t *)\n                                                   kv->value->ds_addrs;\n\n            segment_data[j].cache_hit = NGX_HTTP_TFS_LOCAL_BLOCK_CACHE;\n        }\n    }\n\n    switch (rc) {\n    case NGX_DECLINED:\n        /* remote cache handler will deal */\n        if (t->block_cache_ctx.use_cache & NGX_HTTP_TFS_REMOTE_BLOCK_CACHE) {\n            return NGX_DECLINED;\n        }\n        rc = NGX_OK;\n        break;\n    case NGX_OK:\n        /* local cache all hit */\n        t->decline_handler = ngx_http_tfs_batch_process_start;\n        rc = NGX_DECLINED;\n        break;\n    case NGX_ERROR:\n        /* block cache should not affect, go for ns */\n        ngx_log_error(NGX_LOG_ERR, t->log, 0,\n                      \"batch lookup block cache failed.\");\n        rc = NGX_OK;\n    }\n\n    ngx_http_tfs_finalize_state(t, rc);\n\n    return rc;\n}\n\n\nngx_int_t\nngx_http_tfs_connect(ngx_http_tfs_t *t)\n{\n    ngx_int_t                        rc;\n    ngx_connection_t                *c;\n    ngx_http_request_t              *r;\n    ngx_peer_connection_t           *p;\n    ngx_http_tfs_peer_connection_t  *tp;\n\n    tp = t->tfs_peer;\n    p = &tp->peer;\n    r = t->data;\n\n    p->log->action = \"connecting server\";\n\n    rc = t->create_request(t);\n\n    if (rc == NGX_ERROR) {\n        ngx_log_error(NGX_LOG_ERR, p->log, 0, \"create %V (%s) request failed\",\n            p->name, tp->peer_addr_text);\n        ngx_http_tfs_finalize_request(r, t, NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return rc;\n    }\n\n    ngx_log_error(NGX_LOG_DEBUG, p->log, 0, \"connecting %V, addr: %s\",\n                  p->name, tp->peer_addr_text);\n\n    rc = ngx_event_connect_peer(p);\n\n    if (rc == NGX_ERROR || rc == NGX_BUSY || rc == NGX_DECLINED) {\n        ngx_log_error(NGX_LOG_ERR, p->log, 0,\n                      \"connect to (%V: %s) failed\", p->name,\n                      tp->peer_addr_text);\n        ngx_http_tfs_handle_connection_failure(t, t->tfs_peer);\n        return rc;\n    }\n\n    c = p->connection;\n    c->data = t;\n\n    c->read->handler = ngx_http_tfs_event_handler;\n    c->write->handler = ngx_http_tfs_event_handler;\n\n    c->sendfile &= r->connection->sendfile;\n    t->output.sendfile = c->sendfile;\n\n    if (c->pool == NULL) {\n        c->pool = ngx_create_pool(128, r->connection->log);\n        if (c->pool == NULL) {\n            ngx_log_error(NGX_LOG_ERR, p->log, 0,\n                          \"create connection pool failed\");\n            ngx_http_tfs_finalize_request(r, t, NGX_HTTP_INTERNAL_SERVER_ERROR);\n            return NGX_ERROR;\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    t->writer.out = NULL;\n    t->writer.last = &t->writer.out;\n    t->writer.connection = c;\n    t->writer.limit = 0;\n\n    if (rc == NGX_AGAIN) {\n        ngx_add_timer(c->write, t->main_conf->tfs_connect_timeout);\n        return NGX_AGAIN;\n    }\n\n    ngx_http_tfs_send(r, t);\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_tfs_reinit(ngx_http_request_t *r, ngx_http_tfs_t *t)\n{\n    ngx_chain_t  *cl, *cl_next;\n\n    t->request_sent = 0;\n\n    for (cl = t->request_bufs; cl; cl = cl_next) {\n        cl_next = cl->next;\n        ngx_free_chain(r->pool, cl);\n    }\n\n    /* reinit the subrequest's ngx_output_chain() context */\n    if (r->request_body && r->request_body->temp_file\n        && r != r->main && t->output.buf)\n    {\n        t->output.free = ngx_alloc_chain_link(r->pool);\n        if (t->output.free == NULL) {\n            return NGX_ERROR;\n        }\n\n        t->output.free->buf = t->output.buf;\n        t->output.free->next = NULL;\n\n        t->output.buf->pos = t->output.buf->start;\n        t->output.buf->last = t->output.buf->start;\n    }\n\n    t->output.buf = NULL;\n    t->output.in = NULL;\n    t->output.busy = NULL;\n\n    t->header_buffer.pos = t->header_buffer.start;\n    t->header_buffer.last = t->header_buffer.start;\n\n    t->parse_state = NGX_HTTP_TFS_HEADER;\n    t->header_size = sizeof(ngx_http_tfs_header_t);\n    t->write_event_handler = ngx_http_tfs_send_handler;\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_tfs_event_handler(ngx_event_t *ev)\n{\n    ngx_http_tfs_t      *t;\n    ngx_connection_t    *c;\n    ngx_http_request_t  *r;\n    ngx_http_log_ctx_t  *ctx;\n\n    c = ev->data;\n    t = c->data;\n\n    r = t->data;\n    c = r->connection;\n\n    if (t->r_ctx.action.code != NGX_HTTP_TFS_ACTION_KEEPALIVE) {\n        ctx = c->log->data;\n        ctx->current_request = r;\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http tfs request: \\\"%V?%V\\\"\", &r->uri, &r->args);\n\n    if (ev->write) {\n        t->write_event_handler(r, t);\n\n    } else {\n        t->read_event_handler(r, t);\n    }\n\n    ngx_http_run_posted_requests(c);\n}\n\n\nstatic void\nngx_http_tfs_send(ngx_http_request_t *r, ngx_http_tfs_t *t)\n{\n    ngx_int_t                       rc;\n    ngx_connection_t               *c;\n    ngx_http_tfs_peer_connection_t *tp;\n\n    tp = t->tfs_peer;\n    c = tp->peer.connection;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http tfs send request to %V, addr: %s\", tp->peer.name,\n                   tp->peer_addr_text);\n\n    if (!t->request_sent && ngx_http_tfs_test_connect(c) != NGX_OK) {\n        ngx_http_tfs_handle_connection_failure(t, tp);\n        return;\n    }\n\n    c->log->action = \"sending request to server\";\n\n    /* start send  */\n    rc = ngx_output_chain(&t->output, t->request_sent ? NULL : t->request_bufs);\n\n    t->request_sent = 1;\n\n    if (rc == NGX_ERROR) {\n        ngx_log_error(NGX_LOG_ERR, c->log, 0,\n                      \"ngx output chain failed\");\n        ngx_http_tfs_finalize_request(r, t, NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return;\n    }\n\n    if (c->write->timer_set) {\n        ngx_del_timer(c->write);\n    }\n\n    if (rc == NGX_AGAIN) {\n        ngx_add_timer(c->write, t->main_conf->tfs_send_timeout);\n\n        if (ngx_handle_write_event(c->write, t->main_conf->send_lowat)\n            != NGX_OK)\n        {\n            ngx_log_error(NGX_LOG_ERR, c->log, 0,\n                          \"ngx handle write event failed\");\n            ngx_http_tfs_finalize_request(r, t, NGX_HTTP_INTERNAL_SERVER_ERROR);\n            return;\n        }\n\n        return;\n    }\n\n    /* rc == NGX_OK */\n    if (c->tcp_nopush == NGX_TCP_NOPUSH_SET) {\n        if (ngx_tcp_push(c->fd) == NGX_ERROR) {\n            ngx_log_error(NGX_LOG_CRIT, c->log, ngx_socket_errno,\n                          ngx_tcp_push_n \" failed\");\n            ngx_http_tfs_finalize_request(r, t, NGX_HTTP_INTERNAL_SERVER_ERROR);\n            return;\n        }\n\n        c->tcp_nopush = NGX_TCP_NOPUSH_UNSET;\n    }\n\n    t->write_event_handler = ngx_http_tfs_dummy_handler;\n\n    if (ngx_handle_write_event(c->write, 0) != NGX_OK) {\n        ngx_log_error(NGX_LOG_ERR, c->log, 0,\n                      \"ngx handle write event failed\");\n        ngx_http_tfs_finalize_request(r, t, NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return;\n    }\n\n    ngx_add_timer(c->read, t->main_conf->tfs_read_timeout);\n\n    if (c->read->ready) {\n        ngx_http_tfs_read_handler(r, t);\n        return;\n    }\n}\n\n\nstatic void\nngx_http_tfs_dummy_handler(ngx_http_request_t *r, ngx_http_tfs_t *t)\n{\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http tfs dummy handler\");\n}\n\n\nstatic ngx_int_t\nngx_http_tfs_alloc_buf(ngx_http_tfs_t *t)\n{\n    ngx_http_request_t             *r;\n    ngx_http_tfs_peer_connection_t *tp;\n\n    tp = t->tfs_peer;\n    r = t->data;\n\n    if (t->header_buffer.start == NULL) {\n        t->header_buffer.start = ngx_palloc(r->pool, t->main_conf->buffer_size);\n        if (t->header_buffer.start == NULL) {\n            return NGX_ERROR;\n        }\n\n        t->header_buffer.pos = t->header_buffer.start;\n        t->header_buffer.last = t->header_buffer.start;\n        t->header_buffer.temporary = 1;\n    }\n\n    t->header_buffer.end = t->header_buffer.start + t->header_size;\n\n    if (tp->body_buffer.start == NULL) {\n        tp->body_buffer.start = ngx_palloc(r->pool,\n                                           t->main_conf->body_buffer_size);\n        if (tp->body_buffer.start == NULL) {\n            return NGX_ERROR;\n        }\n\n        tp->body_buffer.pos = tp->body_buffer.start;\n        tp->body_buffer.last = tp->body_buffer.start;\n        tp->body_buffer.end = tp->body_buffer.start\n                               + t->main_conf->body_buffer_size;\n        tp->body_buffer.temporary = 1;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_tfs_process_header(ngx_http_tfs_t *t, ngx_int_t n)\n{\n    ngx_int_t                        body_size, rc;\n    ngx_http_tfs_peer_connection_t  *tp;\n\n    tp = t->tfs_peer;\n\n    if (n < t->header_size) {\n        t->header_buffer.last += n;\n        t->header_size -= n;\n        return NGX_AGAIN;\n    }\n\n    t->header = (void *) t->header_buffer.pos;\n    t->header_buffer.last += t->header_size;\n    body_size = n - t->header_size;\n    if (body_size > 0) {\n        tp->body_buffer.last += body_size;\n    }\n    if (t->input_filter != NULL) {\n        rc = t->input_filter(t);\n        if (rc != NGX_OK) {\n            /* error or NGX_AGAIN or NGX_DONE */\n            return rc;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nvoid\nngx_http_tfs_finalize_state(ngx_http_tfs_t *t, ngx_int_t rc)\n{\n    uint16_t                         action;\n    ngx_http_request_t              *r;\n    ngx_peer_connection_t           *p;\n    ngx_http_tfs_rc_ctx_t           *rc_ctx;\n    ngx_http_tfs_rcs_info_t         *rc_info;\n    ngx_http_tfs_peer_connection_t  *tp;\n\n    r = t->data;\n    tp = t->tfs_peer;\n    p = NULL;\n\n    if (tp) {\n        p = &tp->peer;\n        if (p) {\n            ngx_log_error(NGX_LOG_INFO, t->log, 0,\n                      \"http tfs finalize state %V, %i\", p->name, rc);\n        }\n    }\n\n    /* if one sub process fails, fail all */\n    if (t->parent) {\n        if (t->parent->sp_fail_count > 0) {\n            ngx_log_error(NGX_LOG_ERR, t->log, 0,\n                          \"other sub process failed, will fail myself\");\n\n            ngx_http_tfs_finalize_request(r, t, NGX_ERROR);\n            return;\n        }\n    }\n\n    if (t->r_ctx.action.code == NGX_HTTP_TFS_ACTION_KEEPALIVE) {\n        /* NGX_DONE or ERROR */\n        if (rc != NGX_OK) {\n            ngx_http_tfs_finalize_request(r, t, NGX_DONE);\n            return;\n        }\n    }\n\n    if (rc == NGX_HTTP_CLIENT_CLOSED_REQUEST\n        || rc == NGX_HTTP_REQUEST_TIME_OUT)\n    {\n        ngx_log_error(NGX_LOG_ERR, t->log, 0,\n                      \"client prematurely closed connection or timed out\");\n        ngx_http_tfs_finalize_request(r, t, rc);\n        return;\n    }\n\n    if (rc == NGX_ERROR) {\n        if (p) {\n            ngx_log_error(NGX_LOG_ERR, t->log, 0,\n                \"http tfs process %V request failed\", p->name);\n        }\n\n        ngx_http_tfs_finalize_request(r, t, NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return;\n    }\n\n    if (rc >= NGX_HTTP_SPECIAL_RESPONSE\n        || rc <= NGX_HTTP_TFS_EXIT_GENERAL_ERROR)\n    {\n        t->tfs_status = rc;\n        ngx_http_tfs_send_response(r, t);\n        return;\n    }\n\n    if (rc == NGX_HTTP_TFS_AGAIN) {\n        if (t->retry_handler) {\n            rc = t->retry_handler(t);\n            if (rc == NGX_OK || rc == NGX_DECLINED) {\n                return;\n            }\n\n            if (rc == NGX_HTTP_TFS_EXIT_SERVER_OBJECT_NOT_FOUND) {\n                ngx_log_error(NGX_LOG_ERR, t->log, 0,\n                              \"can not find retry server\");\n                ngx_http_tfs_finalize_request(r, t, NGX_HTTP_NOT_FOUND);\n                return;\n            }\n        }\n\n        t->tfs_status = NGX_ERROR;\n        ngx_http_tfs_send_response(r, t);\n\n        return;\n    }\n\n    if (rc == NGX_DONE) {\n        /* need stat data */\n        if (!t->parent) {\n            if (t->srv_conf->log != NULL) {\n                action = t->r_ctx.action.code;\n                if (action == NGX_HTTP_TFS_ACTION_REMOVE_FILE){\n                    if (t->r_ctx.unlink_type == NGX_HTTP_TFS_UNLINK_UNDELETE) {\n                        action = NGX_HTTP_TFS_ACTION_UNDELETE_FILE;\n\n                    } else if (t->r_ctx.unlink_type == NGX_HTTP_TFS_UNLINK_CONCEAL) {\n                        action = NGX_HTTP_TFS_ACTION_CONCEAL_FILE;\n\n                    } else if (t->r_ctx.unlink_type == NGX_HTTP_TFS_UNLINK_REVEAL) {\n                        action = NGX_HTTP_TFS_ACTION_REVEAL_FILE;\n                    }\n                }\n\n                ngx_log_error(NGX_LOG_INFO, t->srv_conf->log, 0,\n                              \"%d, %uL, %V, %V, %uD, %uL, %uL, %uL, %V\",\n                              action,\n                              t->loc_conf->upstream->enable_rcs ?\n                              t->rc_info_node->app_id : NGX_HTTP_TFS_DEFAULT_APPID,\n                              &t->file_name,\n                              &t->r_ctx.file_suffix,\n                              t->r_ctx.fsname.file.block_id,\n                              ngx_http_tfs_raw_fsname_get_file_id(t->r_ctx.fsname),\n                              t->r_ctx.offset,\n                              t->stat_info.size,\n                              &r->connection->addr_text);\n            }\n\n            if (t->loc_conf->upstream->enable_rcs) {\n                rc_ctx = t->loc_conf->upstream->rc_ctx;\n                ngx_shmtx_lock(&rc_ctx->shpool->mutex);\n                rc_info = ngx_http_tfs_rcs_lookup(rc_ctx, t->r_ctx.appkey);\n\n                if (rc_info != NULL) {\n                    switch (t->r_ctx.action.code) {\n                    case NGX_HTTP_TFS_ACTION_WRITE_FILE:\n                        ngx_http_tfs_rcs_stat_update(t, rc_info, NGX_HTTP_TFS_OPER_WRITE);\n                        break;\n                    case NGX_HTTP_TFS_ACTION_REMOVE_FILE:\n                        ngx_http_tfs_rcs_stat_update(t, rc_info, NGX_HTTP_TFS_OPER_UNLINK);\n                        break;\n                    case NGX_HTTP_TFS_ACTION_READ_FILE:\n                        ngx_http_tfs_rcs_stat_update(t, rc_info, NGX_HTTP_TFS_OPER_READ);\n                        break;\n                    default:\n                        ngx_http_tfs_rcs_stat_update(t, rc_info, NGX_HTTP_TFS_OPER_INVALID);\n                        break;\n                    }\n                }\n                ngx_shmtx_unlock(&rc_ctx->shpool->mutex);\n            }\n        }\n\n        /* need send data */\n        ngx_http_tfs_send_response(r, t);\n\n        return;\n    }\n\n    if (p && p->free) {\n        p->free(p, p->data, 0);\n    }\n\n    if (rc == NGX_DECLINED) {\n        if (t->decline_handler) {\n            rc = t->decline_handler(t);\n            if (rc == NGX_ERROR) {\n                ngx_http_tfs_finalize_request(r, t,\n                                              NGX_HTTP_INTERNAL_SERVER_ERROR);\n            }\n        }\n        return;\n    }\n\n    /* rc == NGX_OK */\n    if (ngx_http_tfs_reinit(r, t) != NGX_OK) {\n        ngx_log_error(NGX_LOG_ERR, t->log, 0, \"tfs reinit failed\");\n        ngx_http_tfs_finalize_request(r, t, NGX_HTTP_INTERNAL_SERVER_ERROR);\n\n        return;\n    }\n\n    t->tfs_peer = ngx_http_tfs_select_peer(t);\n    if (t->tfs_peer == NULL) {\n        ngx_log_error(NGX_LOG_ERR, t->log, 0, \"tfs select peer failed\");\n        ngx_http_tfs_finalize_request(r, t, NGX_HTTP_INTERNAL_SERVER_ERROR);\n\n        return;\n    }\n\n    t->recv_chain->buf = &t->header_buffer;\n    t->recv_chain->next->buf = &t->tfs_peer->body_buffer;\n\n    ngx_log_error(NGX_LOG_INFO, t->log, 0,\n                  \"http tfs process next peer is  %V, addr: %s\",\n                  t->tfs_peer->peer.name,\n                  t->tfs_peer->peer_addr_text);\n\n    ngx_http_tfs_connect(t);\n}\n\n\nstatic void\nngx_http_tfs_process_upstream_request(ngx_http_request_t *r, ngx_http_tfs_t *t)\n{\n    ngx_int_t                        n, rc;\n    ngx_chain_t                     *chain;\n    ngx_connection_t                *c;\n    ngx_http_tfs_peer_connection_t  *tp;\n\n    tp = t->tfs_peer;\n    c = tp->peer.connection;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http tfs process request body for %V, addr: %s\",\n                   tp->peer.name,\n                   tp->peer_addr_text);\n\n    /* for test ds retry */\n    //if (ngx_strncmp(p->name->data, ds_name.data, p->name->len) == 0) {\n    //    ngx_http_tfs_handle_connection_failure(t, tp);\n    //    return;\n    //}\n\n    if (!t->request_sent && ngx_http_tfs_test_connect(c) != NGX_OK) {\n        ngx_http_tfs_handle_connection_failure(t, tp);\n        return;\n    }\n\n    rc = ngx_http_tfs_alloc_buf(t);\n    if (rc == NGX_ERROR) {\n        ngx_log_error(NGX_LOG_ERR, c->log, 0,\n                      \"tfs alloc buf failed\");\n        ngx_http_tfs_finalize_request(r, t, NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return;\n    }\n\n    if (c->read->timer_set) {\n        ngx_del_timer(c->read);\n    }\n\n    for ( ;; ) {\n\n        for (chain = t->recv_chain; chain; chain = chain->next) {\n            if (chain->buf->last != chain->buf->end) {\n                break;\n            }\n        }\n\n        if (chain == NULL) {\n            /* need send data */\n            ngx_http_tfs_process_buf_overflow(r, t);\n            return;\n        }\n\n        n = c->recv_chain(c, chain, 0);\n\n        if (n == NGX_AGAIN) {\n            if (chain->buf->last == chain->buf->end) {\n                ngx_http_tfs_process_buf_overflow(r, t);\n                return;\n            }\n\n            ngx_add_timer(c->read, t->main_conf->tfs_read_timeout);\n            if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n                ngx_log_error(NGX_LOG_ERR, c->log, 0,\n                              \"tfs handle read event failed\");\n                ngx_http_tfs_finalize_request(r, t,\n                                              NGX_HTTP_INTERNAL_SERVER_ERROR);\n                return;\n            }\n\n            return;\n        }\n\n        if (n == 0) {\n            ngx_log_error(NGX_LOG_ERR, c->log, 0,\n                          \"tfs prematurely closed connection\");\n        }\n\n        if (n == NGX_ERROR || n == 0) {\n            ngx_log_error(NGX_LOG_ERR, c->log, 0, \"recv chain error\");\n            ngx_http_tfs_finalize_request(r, t, NGX_HTTP_INTERNAL_SERVER_ERROR);\n\n            return;\n        }\n\n        if (t->parse_state == NGX_HTTP_TFS_HEADER) {\n            rc = ngx_http_tfs_process_header(t, n);\n\n            if (rc == NGX_AGAIN) {\n                continue;\n            }\n\n            if (rc < 0 || rc == NGX_DONE) {\n                break;\n            }\n\n            t->parse_state = NGX_HTTP_TFS_BODY;\n\n        } else {\n            tp->body_buffer.last += n;\n        }\n\n        rc = t->process_request_body(t);\n\n        if (rc == NGX_AGAIN) {\n            continue;\n        }\n\n        break;\n    }\n\n    /* rc == NGX_OK */\n    ngx_http_tfs_finalize_state(t, rc);\n}\n\n\nstatic void\nngx_http_tfs_send_response(ngx_http_request_t *r, ngx_http_tfs_t *t)\n{\n    int                        tcp_nodelay;\n    ngx_int_t                  rc;\n    ngx_http_tfs_t            *parent_tfs;\n    ngx_connection_t          *c;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    /* sub process */\n    if (t->parent) {\n        if (t->tfs_status != NGX_OK) {\n            ngx_http_tfs_finalize_request(r, t, NGX_ERROR);\n            return;\n        }\n\n        if (t->r_ctx.action.code == NGX_HTTP_TFS_ACTION_WRITE_FILE) {\n            ngx_http_tfs_finalize_request(r, t, NGX_DONE);\n            return;\n        }\n\n        if (t->r_ctx.action.code == NGX_HTTP_TFS_ACTION_READ_FILE) {\n            /* output in the right turn */\n            if (t->parent->sp_curr != t->sp_curr) {\n                t->sp_ready = NGX_HTTP_TFS_YES;\n                ngx_log_debug2(NGX_LOG_DEBUG_HTTP, t->log, 0,\n                               \"curr output segment is [%uD], \"\n                               \"[%uD] is ready, wait for call...\",\n                               t->parent->sp_curr, t->sp_curr);\n                return;\n            }\n            /* set ctx */\n            ngx_http_set_ctx(r, t, ngx_http_tfs_module);\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, t->log, 0,\n                           \"segment[%uD] output...\",\n                           t->sp_curr);\n        }\n    }\n\n    if (t->parent == NULL) {\n        parent_tfs = t;\n\n    } else {\n        parent_tfs = t->parent;\n    }\n\n    if (!parent_tfs->header_sent) {\n        ngx_http_tfs_set_header_line(t);\n\n        rc = ngx_http_send_header(r);\n\n        if (rc == NGX_ERROR || rc > NGX_OK || r->post_action) {\n            ngx_http_tfs_finalize_state(t, rc);\n            return;\n        }\n\n        parent_tfs->header_sent = 1;\n\n        if (t->header_only) {\n            ngx_http_tfs_finalize_request(r, t, rc);\n            return;\n        }\n    }\n\n    c = r->connection;\n\n    if (r->request_body && r->request_body->temp_file) {\n        ngx_pool_run_cleanup_file(r->pool, r->request_body->temp_file->file.fd);\n        r->request_body->temp_file->file.fd = NGX_INVALID_FILE;\n    }\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    r->write_event_handler = ngx_http_tfs_process_non_buffered_downstream;\n\n    r->limit_rate = 0;\n    r->limit_rate_set = 1;\n\n    if (clcf->tcp_nodelay && c->tcp_nodelay == NGX_TCP_NODELAY_UNSET) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"tcp_nodelay\");\n\n        tcp_nodelay = 1;\n\n        if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY,\n                (const void *) &tcp_nodelay, sizeof(int)) == -1)\n        {\n            ngx_connection_error(c, ngx_socket_errno,\n                                 \"setsockopt(TCP_NODELAY) failed\");\n\n            ngx_http_tfs_finalize_request(r, t, 0);\n            return;\n        }\n\n        c->tcp_nodelay = NGX_TCP_NODELAY_SET;\n    }\n\n    ngx_http_tfs_process_non_buffered_downstream(r);\n    return;\n}\n\n\nstatic void\nngx_http_tfs_set_header_line(ngx_http_tfs_t *t)\n{\n    ngx_http_request_t          *r;\n    ngx_http_tfs_restful_ctx_t  *ctx;\n\n    r = t->data;\n    ctx = &t->r_ctx;\n\n    /* common error */\n    switch (t->tfs_status) {\n    case NGX_ERROR:\n    case NGX_HTTP_TFS_EXIT_GENERAL_ERROR:\n        r->headers_out.status = NGX_HTTP_INTERNAL_SERVER_ERROR;\n        goto error_header;\n\n    case NGX_HTTP_SPECIAL_RESPONSE ... NGX_HTTP_INTERNAL_SERVER_ERROR:\n        r->headers_out.status = t->tfs_status;\n        goto error_header;\n\n    case NGX_HTTP_TFS_EXIT_INVALID_FILE_NAME:\n    case NGX_HTTP_TFS_EXIT_READ_OFFSET_ERROR:\n    case NGX_HTTP_TFS_EXIT_DISK_OPER_INCOMPLETE:\n    case NGX_HTTP_TFS_EXIT_INVALID_ARGU_ERROR:\n    case NGX_HTTP_TFS_EXIT_PHYSIC_BLOCK_OFFSET_ERROR:\n        r->headers_out.status = NGX_HTTP_BAD_REQUEST;\n        goto error_header;\n\n    case NGX_HTTP_TFS_EXIT_OVER_MAX_SUB_DIRS_COUNT:\n    case NGX_HTTP_TFS_EXIT_OVER_MAX_SUB_DIRS_DEEP:\n    case NGX_HTTP_TFS_EXIT_OVER_MAX_SUB_FILES_COUNT:\n        r->headers_out.status = NGX_HTTP_FORBIDDEN;\n        goto error_header;\n\n    case NGX_HTTP_TFS_EXIT_SERVER_OBJECT_NOT_FOUND:\n    case NGX_HTTP_TFS_EXIT_BLOCK_NOT_FOUND:\n    case NGX_HTTP_TFS_EXIT_META_NOT_FOUND_ERROR:\n    case NGX_HTTP_TFS_EXIT_FILE_INFO_ERROR:\n    case NGX_HTTP_TFS_EXIT_FILE_STATUS_ERROR:\n        r->headers_out.status = NGX_HTTP_NOT_FOUND;\n        goto error_header;\n\n    case NGX_HTTP_TFS_EXIT_WRITE_EXIST_POS_ERROR:\n    case NGX_HTTP_TFS_EXIT_VERSION_CONFLICT_ERROR:\n        /* TODO: maybe should handle this using retry */\n        r->headers_out.status = NGX_HTTP_CONFLICT;\n        goto error_header;\n    }\n\n    switch(ctx->action.code) {\n    case NGX_HTTP_TFS_ACTION_KEEPALIVE:\n        ngx_http_tfs_clear_content_len();\n        r->headers_out.status = NGX_HTTP_OK;\n        break;\n\n    case NGX_HTTP_TFS_ACTION_GET_APPID:\n        r->headers_out.content_type_len = sizeof(\"application/json\") - 1;\n        ngx_str_set(&r->headers_out.content_type, \"application/json\");\n        r->headers_out.status = NGX_HTTP_OK;\n        break;\n\n    case NGX_HTTP_TFS_ACTION_CREATE_DIR:\n    case NGX_HTTP_TFS_ACTION_CREATE_FILE:\n        switch (t->state) {\n        case NGX_HTTP_TFS_STATE_ACTION_DONE:\n            ngx_http_tfs_clear_content_len();\n            r->headers_out.status = NGX_HTTP_CREATED;\n            break;\n            /* errno */\n\n        default:\n            switch (t->tfs_status) {\n            case NGX_HTTP_TFS_EXIT_TARGET_EXIST_ERROR:\n                r->headers_out.status = NGX_HTTP_CONFLICT;\n                break;\n\n            case NGX_HTTP_TFS_EXIT_PARENT_EXIST_ERROR:\n                r->headers_out.status = NGX_HTTP_NOT_FOUND;\n                break;\n\n            default:\n                r->headers_out.status = NGX_HTTP_INTERNAL_SERVER_ERROR;\n                break;\n            }\n\n            goto error_header;\n        }\n        break;\n\n    case NGX_HTTP_TFS_ACTION_WRITE_FILE:\n        switch (t->state) {\n        case NGX_HTTP_TFS_STATE_WRITE_DONE:\n            if (t->r_ctx.version == 1) {\n                r->headers_out.content_type_len = sizeof(\"application/json\")- 1;\n                ngx_str_set(&r->headers_out.content_type, \"application/json\");\n            } else {\n                ngx_http_tfs_clear_content_len();\n            }\n            r->headers_out.status = NGX_HTTP_OK;\n            break;\n            /* errno */\n        default:\n            switch (t->tfs_status) {\n            case NGX_HTTP_TFS_EXIT_TARGET_EXIST_ERROR:\n            case NGX_HTTP_TFS_EXIT_PARENT_EXIST_ERROR:\n            case NGX_HTTP_TFS_EXIT_NOT_CREATE_ERROR:\n                r->headers_out.status = NGX_HTTP_NOT_FOUND;\n                break;\n\n            case NGX_HTTP_TFS_EXIT_WRITE_EXIST_POS_ERROR:\n                r->headers_out.status = NGX_HTTP_BAD_REQUEST;\n                break;\n\n            default:\n                r->headers_out.status = NGX_HTTP_INTERNAL_SERVER_ERROR;\n                break;\n            }\n\n            goto error_header;\n        }\n\n        break;\n\n    case NGX_HTTP_TFS_ACTION_STAT_FILE:\n        switch (t->state) {\n        case NGX_HTTP_TFS_STATE_STAT_DONE:\n            if (t->r_ctx.chk_exist) {\n                t->header_only |= 1;\n                /* set content length if have */\n                if (t->file_stat.size > 0) {\n                    r->headers_out.content_length_n = t->file_stat.size;\n                }\n\n            } else {\n                r->headers_out.content_type_len = sizeof(\"application/json\") - 1;\n                ngx_str_set(&r->headers_out.content_type, \"application/json\");\n            }\n            /* set last-modified if have */\n            if (t->file_stat.modify_time > 0) {\n                r->headers_out.last_modified_time = t->file_stat.modify_time;\n            }\n\n            r->headers_out.status = NGX_HTTP_OK;\n            break;\n\n            /* errno */\n        default:\n            switch (t->tfs_status) {\n            default:\n                r->headers_out.status = NGX_HTTP_INTERNAL_SERVER_ERROR;\n                break;\n            }\n\n            goto error_header;\n        }\n        break;\n\n    case NGX_HTTP_TFS_ACTION_REMOVE_FILE:\n        switch (t->state) {\n        case NGX_HTTP_TFS_STATE_REMOVE_DONE:\n            ngx_http_tfs_clear_content_len();\n            r->headers_out.status = NGX_HTTP_OK;\n            break;\n            /* errno */\n        default:\n            switch (t->tfs_status) {\n            case NGX_HTTP_TFS_EXIT_TARGET_EXIST_ERROR:\n            case NGX_HTTP_TFS_EXIT_PARENT_EXIST_ERROR:\n                r->headers_out.status = NGX_HTTP_NOT_FOUND;\n                break;\n            default:\n                r->headers_out.status = NGX_HTTP_INTERNAL_SERVER_ERROR;\n                break;\n            }\n\n            goto error_header;\n        }\n        break;\n\n    case NGX_HTTP_TFS_ACTION_READ_FILE:\n        switch (t->state) {\n            /* maybe process buf overflow */\n        case NGX_HTTP_TFS_STATE_READ_READ_DATA:\n        case NGX_HTTP_TFS_STATE_READ_DONE:\n            if (t->r_ctx.chk_file_hole && t->json_output) {\n                r->headers_out.content_type_len = sizeof(\"application/json\")- 1;\n                ngx_str_set(&r->headers_out.content_type, \"application/json\");\n\n            } else {\n                if (t->headers_in.content_type != NULL) {\n                    r->headers_out.content_type_len = t->headers_in.content_type->value.len;\n                    r->headers_out.content_type = t->headers_in.content_type->value;\n                    r->headers_out.content_type_lowcase = NULL;\n                }\n            }\n\n            /* set last-modified if have */\n            if (t->file_stat.modify_time > 0) {\n                r->headers_out.last_modified_time = t->file_stat.modify_time;\n            }\n\n            r->headers_out.status = NGX_HTTP_OK;\n            break;\n\n        default:\n            switch (t->tfs_status) {\n            case NGX_HTTP_TFS_EXIT_TARGET_EXIST_ERROR:\n            case NGX_HTTP_TFS_EXIT_PARENT_EXIST_ERROR:\n                r->headers_out.status = NGX_HTTP_NOT_FOUND;\n                break;\n            default:\n                r->headers_out.status = NGX_HTTP_INTERNAL_SERVER_ERROR;\n                break;\n            }\n\n            goto error_header;\n        }\n        break;\n\n    case NGX_HTTP_TFS_ACTION_REMOVE_DIR:\n        switch (t->state) {\n        case NGX_HTTP_TFS_STATE_ACTION_DONE:\n            ngx_http_tfs_clear_content_len();\n            r->headers_out.status = NGX_HTTP_OK;\n            break;\n\n        default:\n            switch (t->tfs_status) {\n            case NGX_HTTP_TFS_EXIT_TARGET_EXIST_ERROR:\n            case NGX_HTTP_TFS_EXIT_PARENT_EXIST_ERROR:\n                r->headers_out.status = NGX_HTTP_NOT_FOUND;\n                break;\n            case NGX_HTTP_TFS_EXIT_DELETE_DIR_WITH_FILE_ERROR:\n                r->headers_out.status = NGX_HTTP_FORBIDDEN;\n                break;\n            default:\n                r->headers_out.status = NGX_HTTP_INTERNAL_SERVER_ERROR;\n                break;\n            }\n\n            goto error_header;\n        }\n        break;\n\n    case NGX_HTTP_TFS_ACTION_LS_DIR:\n    case NGX_HTTP_TFS_ACTION_LS_FILE:\n        switch (t->state) {\n        case NGX_HTTP_TFS_STATE_ACTION_DONE:\n            if (t->r_ctx.chk_exist) {\n                ngx_http_tfs_clear_content_len();\n\n            } else {\n                r->headers_out.content_type_len = sizeof(\"application/json\")- 1;\n                ngx_str_set(&r->headers_out.content_type, \"application/json\");\n            }\n            r->headers_out.status = NGX_HTTP_OK;\n            break;\n            /* errno */\n        default:\n            switch (t->tfs_status) {\n            case NGX_HTTP_TFS_EXIT_TARGET_EXIST_ERROR:\n            case NGX_HTTP_TFS_EXIT_PARENT_EXIST_ERROR:\n                r->headers_out.status = NGX_HTTP_NOT_FOUND;\n                break;\n            default:\n                r->headers_out.status = NGX_HTTP_INTERNAL_SERVER_ERROR;\n                break;\n            }\n            goto error_header;\n        }\n        break;\n\n    case NGX_HTTP_TFS_ACTION_MOVE_DIR:\n    case NGX_HTTP_TFS_ACTION_MOVE_FILE:\n        switch (t->state) {\n        case NGX_HTTP_TFS_STATE_ACTION_DONE:\n            ngx_http_tfs_clear_content_len();\n            r->headers_out.status = NGX_HTTP_OK;\n            break;\n            /* errno */\n        default:\n            switch (t->tfs_status) {\n            case NGX_HTTP_TFS_EXIT_TARGET_EXIST_ERROR:\n            case NGX_HTTP_TFS_EXIT_PARENT_EXIST_ERROR:\n                r->headers_out.status = NGX_HTTP_NOT_FOUND;\n                break;\n            case NGX_HTTP_TFS_EXIT_MOVE_TO_SUB_DIR_ERROR:\n                r->headers_out.status = NGX_HTTP_FORBIDDEN;\n                break;\n            default:\n                r->headers_out.status = NGX_HTTP_INTERNAL_SERVER_ERROR;\n                break;\n            }\n            goto error_header;\n        }\n\n    default:\n        break;\n    }\n\n    ngx_log_error(NGX_LOG_INFO, t->log, 0,\n                  \"%V success\", &ctx->action.msg);\n    return;\n\nerror_header:\n    ngx_http_tfs_clear_content_len();\n\n    ngx_log_error(NGX_LOG_ERR, t->log, 0, \"%V failed, err(%d)\",\n                  &ctx->action.msg, t->tfs_status);\n}\n\n\nstatic void\nngx_http_tfs_process_non_buffered_downstream(ngx_http_request_t *r)\n{\n    ngx_event_t       *wev;\n    ngx_http_tfs_t    *t;\n    ngx_connection_t  *c;\n\n    c = r->connection;\n    wev = c->write;\n    t = ngx_http_get_module_ctx(r, ngx_http_tfs_module);\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http tfs upstream process downstream\");\n\n    c->log->action = \"sending to client\";\n\n    if (wev->timedout) {\n        c->timedout = 1;\n        ngx_connection_error(c, NGX_ETIMEDOUT, \"client timed out\");\n\n        /* write need roll back, remove all segments */\n        if (t->r_ctx.version == 1\n            && t->r_ctx.action.code == NGX_HTTP_TFS_ACTION_WRITE_FILE\n            && t->r_ctx.is_raw_update == NGX_HTTP_TFS_NO\n            && !t->parent)\n        {\n            r->write_event_handler = ngx_http_request_empty_handler;\n            t->state = NGX_HTTP_TFS_STATE_WRITE_GET_BLK_INFO;\n            t->is_rolling_back = NGX_HTTP_TFS_YES;\n            t->file.segment_index = 0;\n            ngx_http_tfs_finalize_state(t, NGX_OK);\n            return;\n        }\n\n        ngx_http_tfs_finalize_request(t->data, t,\n                                      NGX_HTTP_REQUEST_TIME_OUT);\n        return;\n    }\n\n    ngx_http_tfs_process_non_buffered_request(t, 1);\n}\n\n\nstatic void\nngx_http_tfs_process_non_buffered_request(ngx_http_tfs_t *t,\n    ngx_uint_t do_write)\n{\n    size_t                     size;\n    ssize_t                    n;\n    ngx_int_t                  rc, finalize_state;\n    ngx_buf_t                 *b;\n    ngx_connection_t          *downstream, *upstream;\n    ngx_http_request_t        *r;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    r = t->data;\n    finalize_state = 0;\n    rc = 0;\n    b = NULL;\n    downstream = r->connection;\n    upstream = NULL;\n\n    if (t->r_ctx.version == 1) {\n        /* data server */\n        b = &t->tfs_peer_servers[NGX_HTTP_TFS_DATA_SERVER].body_buffer;\n        upstream =t->tfs_peer_servers[NGX_HTTP_TFS_DATA_SERVER].peer.connection;\n\n    } else if (t->r_ctx.action.code != NGX_HTTP_TFS_ACTION_GET_APPID) {\n        b = &t->tfs_peer->body_buffer;\n        upstream = t->tfs_peer->peer.connection;\n    }\n\n    for ( ;; ) {\n        if (do_write) {\n\n            if (t->out_bufs || t->busy_bufs) {\n\n                rc = ngx_http_output_filter(r, t->out_bufs);\n\n                if (rc == NGX_ERROR) {\n                    ngx_http_tfs_finalize_request(r, t, 0);\n                    return;\n                }\n\n#if defined(nginx_version) && (nginx_version > 1001003)\n                ngx_chain_update_chains(t->pool, &t->free_bufs, &t->busy_bufs,\n                                        &t->out_bufs, t->output.tag);\n#else\n                ngx_chain_update_chains(&t->free_bufs, &t->busy_bufs,\n                                        &t->out_bufs, t->output.tag);\n#endif\n            }\n\n            /* send all end */\n            if (t->busy_bufs == NULL) {\n\n                /* sub process */\n                if (t->parent) {\n                    if (t->r_ctx.action.code == NGX_HTTP_TFS_ACTION_READ_FILE\n                        && t->state == NGX_HTTP_TFS_STATE_READ_DONE)\n                    {\n                        ngx_http_tfs_clear_buf(b);\n                        ngx_http_tfs_finalize_request(r, t, NGX_DONE);\n                        return;\n                    }\n                }\n\n                t->output_size += t->main_conf->body_buffer_size;\n\n                if (t->r_ctx.action.code == NGX_HTTP_TFS_ACTION_GET_APPID\n                    || (t->r_ctx.action.code == NGX_HTTP_TFS_ACTION_READ_FILE\n                        && t->state == NGX_HTTP_TFS_STATE_READ_DONE)\n                    || (t->r_ctx.version == 2\n                        && t->state == NGX_HTTP_TFS_STATE_ACTION_DONE)\n                    || (t->r_ctx.action.code == NGX_HTTP_TFS_ACTION_STAT_FILE\n                        && t->state == NGX_HTTP_TFS_STATE_STAT_DONE)\n                    || (t->r_ctx.action.code == NGX_HTTP_TFS_ACTION_WRITE_FILE\n                        && t->state == NGX_HTTP_TFS_STATE_WRITE_DONE))\n                {\n                    /* need log size */\n                    ngx_log_error(NGX_LOG_INFO, t->log, 0,\n                                  \"%V, output %uL byte\",\n                                  &t->r_ctx.action.msg, t->output_size);\n                    ngx_http_tfs_finalize_request(r, t, 0);\n                    return;\n                }\n\n                ngx_http_tfs_clear_buf(b);\n            }\n        }\n\n        size = b->end - b->last;\n\n        if (t->length > 0 && size && upstream->read->ready) {\n            n = upstream->recv(upstream, b->last, size);\n\n            if (n == NGX_AGAIN) {\n                break;\n            }\n\n            if (n > 0) {\n                b->last += n;\n                do_write = 1;\n\n                /* copy buf to out_bufs */\n                rc = t->process_request_body(t);\n                if (rc == NGX_ERROR) {\n                    ngx_log_error(NGX_LOG_ERR, t->log, 0,\n                                  \"process request body failed\");\n                    ngx_http_tfs_finalize_request(t->data, t,\n                                                NGX_HTTP_INTERNAL_SERVER_ERROR);\n                    return;\n                }\n\n                /* ngx_ok or ngx_done */\n                if (rc != NGX_AGAIN) {\n                    finalize_state = 1;\n                    break;\n                }\n            }\n\n            continue;\n        }\n\n        break;\n    }\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    if (downstream->data == r) {\n        if (ngx_handle_write_event(downstream->write, clcf->send_lowat)\n            != NGX_OK)\n        {\n            ngx_http_tfs_finalize_request(r, t, 0);\n            return;\n        }\n    }\n\n    if (downstream->write->active && !downstream->write->ready) {\n        ngx_add_timer(downstream->write, clcf->send_timeout);\n\n    } else if (downstream->write->timer_set) {\n        ngx_del_timer(downstream->write);\n    }\n\n    if (t->length > 0 && upstream->read->active && !upstream->read->ready) {\n        ngx_add_timer(upstream->read, t->main_conf->tfs_read_timeout);\n\n    } else if (upstream->read->timer_set) {\n        ngx_del_timer(upstream->read);\n    }\n\n    if (finalize_state) {\n        ngx_http_tfs_finalize_state(t, rc);\n    }\n}\n\n\nstatic void\nngx_http_tfs_process_buf_overflow(ngx_http_request_t *r, ngx_http_tfs_t *t)\n{\n    ngx_int_t  rc;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                  \"tfs process buf overflow, %V\", t->tfs_peer->peer.name);\n\n    if ((t->r_ctx.action.code == NGX_HTTP_TFS_ACTION_READ_FILE\n         && t->state == NGX_HTTP_TFS_STATE_READ_READ_DATA))\n    {\n        rc = t->process_request_body(t);\n\n        if (rc != NGX_AGAIN) {\n            if (rc == NGX_ERROR) {\n                ngx_log_error(NGX_LOG_ERR, t->log, 0,\n                              \"process request body failed\");\n                ngx_http_tfs_finalize_request(t->data, t,\n                                              NGX_HTTP_INTERNAL_SERVER_ERROR);\n\n            } else {\n                ngx_http_tfs_finalize_state(t, rc);\n            }\n            return;\n        }\n\n        if (ngx_handle_read_event(t->tfs_peer->peer.connection->read, 0)\n            != NGX_OK)\n        {\n            ngx_log_error(NGX_LOG_ERR, t->log, 0,\n                          \"ngx handle read event failed\");\n            ngx_http_tfs_finalize_request(t->data, t,\n                                          NGX_HTTP_INTERNAL_SERVER_ERROR);\n            return;\n        }\n\n        ngx_http_tfs_send_response(r, t);\n\n        return;\n\n    }\n\n    ngx_log_error(NGX_LOG_ERR, t->log, 0,\n                  \"action: %V should not come to here process buf overflow\",\n                  &t->r_ctx.action.msg);\n\n    ngx_http_tfs_finalize_request(r, t, NGX_HTTP_INTERNAL_SERVER_ERROR);\n}\n\n\nvoid\nngx_http_tfs_finalize_request(ngx_http_request_t *r, ngx_http_tfs_t *t,\n    ngx_int_t rc)\n{\n    ngx_uint_t              i;\n    ngx_http_tfs_t         *next_st;\n    ngx_peer_connection_t  *p;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"finalize http tfs request: %i\", rc);\n\n    if (t->parent\n        && t->r_ctx.action.code == NGX_HTTP_TFS_ACTION_READ_FILE\n        && t->parent->sp_curr != t->sp_curr)\n    {\n        if (rc != NGX_DONE) {\n            t->sp_fail_count++;\n        }\n        /* output in the right turn */\n        t->sp_ready = NGX_HTTP_TFS_YES;\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, t->log, 0,\n                       \"curr output segment is [%uD], [%uD] is ready, wait for call...\",\n                       t->parent->sp_curr, t->sp_curr);\n        return;\n    }\n\n    for (i = 0; i < t->tfs_peer_count; i++) {\n        p = &t->tfs_peer_servers[i].peer;\n        if (p->free) {\n            p->free(p, p->data, 0);\n        }\n\n        if (p->connection) {\n            if (p->free) {\n                p->free(p, p->data, 0);\n            }\n\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"close http upstream connection: %d\",\n                           p->connection->fd);\n\n            if (p->connection->pool) {\n                ngx_destroy_pool(p->connection->pool);\n            }\n\n            ngx_close_connection(p->connection);\n        }\n\n        p->connection = NULL;\n    }\n#if (NGX_DEBUG)\n    ngx_http_connection_pool_check(t->main_conf->conn_pool, t->log);\n#endif\n\n    /* sub process return here */\n    if (t->parent) {\n        /* free st for reuse */\n        next_st = t->next;\n        ngx_http_tfs_free_st(t);\n\n        r->write_event_handler = ngx_http_request_empty_handler;\n\n        if (rc == NGX_DONE) {\n            t->parent->sp_succ_count++;\n            t->parent->stat_info.size += t->stat_info.size;\n            if (t->r_ctx.action.code == NGX_HTTP_TFS_ACTION_WRITE_FILE) {\n                t->parent->file.left_length -=\n                                        t->file.segment_data->segment_info.size;\n            }\n\n        } else {\n            t->parent->sp_fail_count++;\n            if (rc == NGX_HTTP_REQUEST_TIME_OUT) {\n                t->parent->request_timeout = NGX_HTTP_TFS_YES;\n            }\n        }\n        t->parent->sp_done_count++;\n        t->parent->sp_curr++;\n        /* all sub process done, wake up parent process */\n        if (t->parent->sp_done_count == t->parent->sp_count){\n            t->parent->sp_callback(t->parent);\n\n        } else {\n            if (t->r_ctx.action.code == NGX_HTTP_TFS_ACTION_READ_FILE) {\n                /* wake up next sub process */\n                ngx_log_debug1(NGX_LOG_DEBUG_HTTP, t->log, 0,\n                               \"segment[%uD] output complete, call next...\",\n                               t->sp_curr);\n                if (next_st) {\n                    next_st->sp_callback(next_st);\n                }\n            }\n        }\n\n        return;\n    }\n\n    /* rc-keepalive */\n    if (t->r_ctx.action.code == NGX_HTTP_TFS_ACTION_KEEPALIVE) {\n        t->finalize_request(t);\n        return;\n    }\n\n    if (t->json_output) {\n        ngx_http_tfs_json_destroy(t->json_output);\n    }\n\n    r->connection->log->action = \"sending to client\";\n    if (rc == NGX_OK) {\n        rc = ngx_http_send_special(r, NGX_HTTP_LAST);\n    }\n\n    ngx_http_finalize_request(r, rc);\n}\n\n\nstatic void\nngx_http_tfs_read_handler(ngx_http_request_t *r, ngx_http_tfs_t *t)\n{\n    ngx_connection_t               *c;\n    ngx_http_tfs_peer_connection_t *tp;\n\n    tp = t->tfs_peer;\n    c = tp->peer.connection;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http tfs process tfs(%V) data\", tp->peer.name);\n\n    c->log->action = \"reading response header from tfs\";\n\n    if (c->read->timedout) {\n        ngx_log_error(NGX_LOG_ERR, c->log, 0,\n                      \"read from (%V: %s) timeout\", tp->peer.name,\n                      tp->peer_addr_text);\n        ngx_http_tfs_handle_connection_failure(t, tp);\n        return;\n    }\n\n    ngx_http_tfs_process_upstream_request(r, t);\n}\n\n\nstatic void\nngx_http_tfs_send_handler(ngx_http_request_t *r, ngx_http_tfs_t *t)\n{\n    ngx_connection_t                *c;\n    ngx_http_tfs_peer_connection_t  *tp;\n\n    tp = t->tfs_peer;\n    c = tp->peer.connection;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"http tfs send request\");\n\n    c->log->action = \"sending request to tfs\";\n\n    if (c->write->timedout) {\n        ngx_log_error(NGX_LOG_ERR, c->log, 0,\n                      \"connect or send to (%V: %s) timeout\", tp->peer.name,\n                      tp->peer_addr_text);\n        ngx_http_tfs_handle_connection_failure(t, tp);\n        return;\n    }\n\n    ngx_http_tfs_send(r, t);\n}\n\n\nstatic void\nngx_http_tfs_handle_connection_failure(ngx_http_tfs_t *t,\n    ngx_http_tfs_peer_connection_t *tp)\n{\n    ngx_connection_t            *c;\n    ngx_peer_connection_t       *p;\n#if (NGX_DEBUG)\n    ngx_http_connection_pool_t  *pool;\n#endif\n\n    p = &tp->peer;\n    c = p->connection;\n#if (NGX_DEBUG)\n    /* failure connection can not be freed,\n     * so make sure get&free op pairs are right\n     */\n    pool = p->data;\n    pool->count++;\n#endif\n\n    /* close failure connection */\n    if (c != NULL) {\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, t->log, 0,\n                       \"close http upstream connection: %d\",\n                       c->fd);\n\n        if (c->pool) {\n            ngx_destroy_pool(c->pool);\n        }\n\n        ngx_close_connection(c);\n    }\n    p->connection = NULL;\n\n    /* connect metaserver fail, get new table from rootserver */\n    if (ngx_strcmp(p->name->data, ms_name.data) == 0) {\n        t->state = NGX_HTTP_TFS_STATE_ACTION_GET_META_TABLE;\n        ngx_http_tfs_peer_set_addr(t->pool,\n                         &t->tfs_peer_servers[NGX_HTTP_TFS_ROOT_SERVER],\n                         (ngx_http_tfs_inet_t *)&t->loc_conf->meta_root_server);\n        ngx_http_tfs_finalize_state(t, NGX_OK);\n        return;\n    }\n\n    /* connect dataserver fail, remove block cache */\n    if (ngx_strcmp(p->name->data, ds_name.data) == 0) {\n        ngx_http_tfs_remove_block_cache(t,\n                                  &t->file.segment_data[t->file.segment_index]);\n    }\n\n    /*connect rcserver fail, select another rc server*/\n    if (ngx_strncmp(p->name->data, rcs_name.data, p->name->len) == 0) {\n        ngx_log_error(NGX_LOG_WARN, t->log, 0,\n                \"select a new rc server\");\n        ngx_http_tfs_select_rc_server(t);\n    }\n\n    ngx_http_tfs_finalize_state(t, NGX_HTTP_TFS_AGAIN);\n}\n\n\nngx_int_t\nngx_http_tfs_set_output_appid(ngx_http_tfs_t *t, uint64_t app_id)\n{\n    ngx_chain_t  *cl, **ll;\n\n    t->json_output = ngx_http_tfs_json_init(t->log, t->pool);\n    if (t->json_output == NULL) {\n        return NGX_ERROR;\n    }\n\n    for (cl = t->out_bufs, ll = &t->out_bufs; cl; cl = cl->next) {\n        ll = &cl->next;\n    }\n\n    cl = ngx_http_tfs_json_appid(t->json_output, app_id);\n    if (cl == NULL) {\n        return NGX_ERROR;\n    }\n\n    *ll = cl;\n    return NGX_OK;\n}\n\n\nvoid\nngx_http_tfs_set_custom_initial_parameters(ngx_http_tfs_t *t)\n{\n    /* for stat log */\n    t->file_name = t->r_ctx.file_path_s;\n    t->r_ctx.file_suffix = t->r_ctx.file_path_d;\n\n    switch(t->r_ctx.action.code) {\n    case NGX_HTTP_TFS_ACTION_CREATE_DIR:\n    case NGX_HTTP_TFS_ACTION_CREATE_FILE:\n        t->last_file_path = t->r_ctx.file_path_s;\n        break;\n\n    case NGX_HTTP_TFS_ACTION_MOVE_DIR:\n    case NGX_HTTP_TFS_ACTION_MOVE_FILE:\n        t->last_file_path = t->r_ctx.file_path_d;\n        break;\n\n    case NGX_HTTP_TFS_ACTION_LS_DIR:\n    case NGX_HTTP_TFS_ACTION_LS_FILE:\n        /* set initial ls parameter */\n        t->last_file_path = t->r_ctx.file_path_s;\n        t->last_file_pid = -1;\n        t->last_file_type = t->r_ctx.file_type;\n        break;\n\n    case NGX_HTTP_TFS_ACTION_READ_FILE:\n    case NGX_HTTP_TFS_ACTION_REMOVE_FILE:\n        t->file.file_offset = t->r_ctx.offset;\n        t->file.left_length = t->r_ctx.size;\n        break;\n    }\n}\n\n\nngx_int_t\nngx_http_tfs_misc_ctx_init(ngx_http_tfs_t *t, ngx_http_tfs_rcs_info_t *rc_info)\n{\n    ngx_int_t                         rc;\n    ngx_http_request_t               *r;\n    ngx_http_tfs_inet_t              *addr;\n    ngx_http_tfs_logical_cluster_t   *logical_cluster;\n\n    /* raw tfs */\n    if (t->r_ctx.version == 1) {\n        switch (t->r_ctx.action.code) {\n        case NGX_HTTP_TFS_ACTION_STAT_FILE:\n            t->state = NGX_HTTP_TFS_STATE_STAT_GET_BLK_INFO;\n            break;\n\n        case NGX_HTTP_TFS_ACTION_READ_FILE:\n            t->state = NGX_HTTP_TFS_STATE_READ_GET_BLK_INFO;\n            break;\n\n        case NGX_HTTP_TFS_ACTION_WRITE_FILE:\n            t->state = NGX_HTTP_TFS_STATE_WRITE_GET_BLK_INFO;\n            break;\n\n        case NGX_HTTP_TFS_ACTION_REMOVE_FILE:\n            t->state = NGX_HTTP_TFS_STATE_REMOVE_GET_BLK_INFO;\n            if (!t->is_large_file && rc_info->need_duplicate) {\n                /* undelete, conceal and reveal\n                 * do not go through de-duplicating\n                 */\n                if (t->r_ctx.unlink_type == NGX_HTTP_TFS_UNLINK_DELETE) {\n                    t->is_stat_dup_file = NGX_HTTP_TFS_YES;\n                    t->r_ctx.read_stat_type = NGX_HTTP_TFS_READ_STAT_FORCE;\n                    t->use_dedup = NGX_HTTP_TFS_YES;\n                    t->dedup_ctx.data = t;\n                }\n            }\n            break;\n        }\n\n        rc = ngx_http_tfs_select_name_server(t, rc_info, &t->name_server_addr,\n                                             &t->name_server_addr_text);\n        if (rc == NGX_ERROR) {\n            return NGX_HTTP_NOT_FOUND;\n        }\n\n        ngx_http_tfs_peer_set_addr(t->pool,\n          &t->tfs_peer_servers[NGX_HTTP_TFS_NAME_SERVER], &t->name_server_addr);\n\n        if (!t->is_large_file\n            || (t->r_ctx.action.code != NGX_HTTP_TFS_ACTION_WRITE_FILE))\n        {\n            /* fill meta segment */\n            rc = ngx_http_tfs_get_meta_segment(t);\n            if (rc == NGX_ERROR) {\n                ngx_log_error(NGX_LOG_ERR, t->log, 0,\n                              \"tfs get meta segment failed\");\n                return NGX_HTTP_INTERNAL_SERVER_ERROR;\n            }\n\n            /* small file */\n            if (t->r_ctx.action.code == NGX_HTTP_TFS_ACTION_WRITE_FILE) {\n                /* parse meta segment */\n                if (t->r_ctx.write_meta_segment) {\n                    rc = ngx_http_tfs_parse_meta_segment(t, t->send_body);\n                    if (rc == NGX_ERROR) {\n                        return NGX_ERROR;\n                    }\n                    t->send_body = t->meta_segment_data;\n                }\n\n                t->file.segment_data[0].data = t->send_body;\n                /* copy data to orig_data so that we can retry write */\n                rc = ngx_chain_add_copy_with_buf(t->pool,\n                    &t->file.segment_data[0].orig_data, t->file.segment_data[0].data);\n                if (rc == NGX_ERROR) {\n                    return NGX_ERROR;\n                }\n\n                t->file.segment_data[0].segment_info.size =\n                                 ngx_http_tfs_get_chain_buf_size(t->send_body);\n                t->file.left_length= t->file.segment_data[0].segment_info.size;\n                t->file.segment_data[0].oper_size =\n                   ngx_min(t->file.left_length, NGX_HTTP_TFS_MAX_FRAGMENT_SIZE);\n\n            } else {\n                /* set oper size && offset,\n                 * large file must read the whole meta segment\n                 */\n                if (t->is_large_file) {\n                    t->is_process_meta_seg = NGX_HTTP_TFS_YES;\n                    t->file.file_offset = 0;\n                    t->file.left_length = NGX_HTTP_TFS_MAX_SIZE;\n\n                } else {\n                    t->file.file_offset = t->r_ctx.offset;\n                    t->file.left_length = t->r_ctx.size;\n                }\n\n                t->file.segment_data[0].oper_offset = t->file.file_offset;\n                t->file.segment_data[0].oper_size =\n                  ngx_min(t->file.left_length, NGX_HTTP_TFS_MAX_READ_FILE_SIZE);\n            }\n        }\n\n    } else if (t->r_ctx.version == 2) {  /* custom tfs file */\n        /* check permission */\n        if (t->r_ctx.action.code != NGX_HTTP_TFS_ACTION_READ_FILE\n            && t->r_ctx.action.code != NGX_HTTP_TFS_ACTION_LS_DIR\n            && t->r_ctx.action.code != NGX_HTTP_TFS_ACTION_LS_FILE)\n        {\n            if (t->r_ctx.app_id != rc_info->app_id) {\n                return NGX_HTTP_UNAUTHORIZED;\n            }\n        }\n\n        /* check root server */\n        if (rc_info->meta_root_server == 0) {\n            return NGX_HTTP_BAD_REQUEST;\n        }\n\n        if (t->loc_conf->meta_root_server != rc_info->meta_root_server) {\n            /* update root server & meta table */\n            t->loc_conf->meta_root_server = rc_info->meta_root_server;\n            t->loc_conf->meta_server_table.version = 0;\n            ngx_http_tfs_peer_set_addr(t->pool,\n                             &t->tfs_peer_servers[NGX_HTTP_TFS_ROOT_SERVER],\n                             (ngx_http_tfs_inet_t *)&rc_info->meta_root_server);\n        }\n\n        /* next => root server */\n        t->state += 1;\n\n        /* check meta table */\n        if (t->loc_conf->meta_server_table.version != 0) {\n            /* skip root server */\n            t->state += 1;\n\n            ngx_http_tfs_set_custom_initial_parameters(t);\n\n            addr = ngx_http_tfs_select_meta_server(t);\n\n            ngx_http_tfs_peer_set_addr(t->pool,\n                          &t->tfs_peer_servers[NGX_HTTP_TFS_META_SERVER], addr);\n        }\n    }\n\n    /* prepare:read:\n     * remote block cache instance write(large file and custom file):\n     * each segment's data\n     */\n    switch (t->r_ctx.action.code) {\n    case NGX_HTTP_TFS_ACTION_REMOVE_FILE:\n        t->group_seq = -1;\n        /* remove large_file && dedup unlink need to stat/read file */\n        if (t->r_ctx.version == 2\n            || !t->is_stat_dup_file\n            || t->state != NGX_HTTP_TFS_STATE_REMOVE_GET_BLK_INFO)\n        {\n            break;\n        }\n    case NGX_HTTP_TFS_ACTION_STAT_FILE:\n    case NGX_HTTP_TFS_ACTION_READ_FILE:\n        if (t->main_conf->enable_remote_block_cache == NGX_CONF_UNSET) {\n            if (rc_info->use_remote_block_cache) {\n                t->block_cache_ctx.use_cache |= NGX_HTTP_TFS_REMOTE_BLOCK_CACHE;\n            }\n        }\n\n        if (t->block_cache_ctx.use_cache & NGX_HTTP_TFS_REMOTE_BLOCK_CACHE) {\n            rc = ngx_http_tfs_get_remote_block_cache_instance(\n                                             &t->block_cache_ctx.remote_ctx,\n                                             &rc_info->remote_block_cache_info);\n            if (rc == NGX_ERROR) {\n                ngx_log_error(NGX_LOG_ERR, t->log, 0,\n                              \"get remote block cache instance failed.\");\n                t->block_cache_ctx.use_cache &=~NGX_HTTP_TFS_REMOTE_BLOCK_CACHE;\n            }\n        }\n\n        /* lookup block cache */\n        if (t->r_ctx.version == 1) {\n            t->decline_handler = ngx_http_tfs_lookup_block_cache;\n            return NGX_DECLINED;\n        }\n        break;\n\n    case NGX_HTTP_TFS_ACTION_WRITE_FILE:\n        t->group_seq = -1;\n        if (t->is_large_file || t->r_ctx.version == 2) {\n            rc = ngx_http_tfs_get_segment_for_write(t);\n            if (rc == NGX_ERROR) {\n                return NGX_ERROR;\n            }\n        }\n    }\n\n    /* dedup related */\n    if (t->r_ctx.version == 1\n        && t->r_ctx.action.code == NGX_HTTP_TFS_ACTION_WRITE_FILE\n        && !t->is_large_file\n        && rc_info->need_duplicate\n        && !t->r_ctx.no_dedup)\n    {\n        /* update is not allowed when using dedup */\n        if (t->r_ctx.is_raw_update > 0) {\n            return NGX_HTTP_BAD_REQUEST;\n        }\n\n        t->dedup_ctx.data = t;\n        logical_cluster = &rc_info->logical_clusters[t->logical_cluster_index];\n        rc = ngx_http_tfs_get_dedup_instance(&t->dedup_ctx,\n                                         &logical_cluster->dup_server_info,\n                                         logical_cluster->dup_server_addr_hash);\n        if (rc == NGX_ERROR) {\n            ngx_log_error(NGX_LOG_ERR, t->log, 0,\n                          \"get dedup instance failed.\");\n            /* no dedup */\n            return NGX_OK;\n        }\n\n        t->use_dedup = NGX_HTTP_TFS_YES;\n        /* dedup do not allow retry other ns */\n        t->retry_curr_ns = NGX_HTTP_TFS_YES;\n\n        r = t->data;\n        t->dedup_ctx.file_data = r->request_body->bufs;\n        t->decline_handler = ngx_http_tfs_get_duplicate_info;\n\n        return NGX_DECLINED;\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_tfs_get_duplicate_info(ngx_http_tfs_t *t)\n{\n    ngx_int_t  rc;\n\n    rc = ngx_http_tfs_dedup_get(&t->dedup_ctx, t->pool, t->log);\n    if (rc == NGX_ERROR) {\n        if (t->r_ctx.action.code == NGX_HTTP_TFS_ACTION_REMOVE_FILE\n            && t->state == NGX_HTTP_TFS_STATE_REMOVE_READ_META_SEGMENT)\n        {\n            /* get dup info from tair failed, do not unlink file */\n            t->state = NGX_HTTP_TFS_STATE_REMOVE_DONE;\n            rc = NGX_DONE;\n\n        } else {\n            /* no dedup */\n            rc = NGX_OK;\n        }\n\n        ngx_http_tfs_finalize_state(t, rc);\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_tfs_set_duplicate_info(ngx_http_tfs_t *t)\n{\n    ngx_int_t  rc;\n\n    rc = ngx_http_tfs_dedup_set(&t->dedup_ctx, t->pool,\n                                t->log);\n    /* save tair failed */\n    if (rc == NGX_ERROR) {\n        if (t->r_ctx.action.code == NGX_HTTP_TFS_ACTION_WRITE_FILE) {\n            switch (t->state) {\n            case NGX_HTTP_TFS_STATE_WRITE_STAT_DUP_FILE:\n                /* stat success and file status normal */\n                /* need save new tfs file, no more dedup */\n                t->state = NGX_HTTP_TFS_STATE_WRITE_CLUSTER_ID_NS;\n                t->is_stat_dup_file = NGX_HTTP_TFS_NO;\n                t->use_dedup = NGX_HTTP_TFS_NO;\n                /* need reset output buf */\n                t->out_bufs = NULL;\n                /* need reset block id and file id */\n                t->file.segment_data[0].segment_info.block_id = 0;\n                t->file.segment_data[0].segment_info.file_id = 0;\n                rc = NGX_OK;\n                break;\n            case NGX_HTTP_TFS_STATE_WRITE_DONE:\n                rc = NGX_DONE;\n            }\n        }\n\n        ngx_http_tfs_finalize_state(t, rc);\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_tfs_batch_process_start(ngx_http_tfs_t *t)\n{\n    uint32_t                         i;\n    ngx_http_tfs_t                  *st, **tt;\n    ngx_http_tfs_inet_t             *addr;\n    ngx_http_tfs_segment_data_t     *segment_data;\n    ngx_http_tfs_peer_connection_t  *data_server;\n\n    segment_data = &t->file.segment_data[t->file.segment_index];\n\n    t->sp_count = 0;\n    t->sp_done_count = 0;\n    t->sp_fail_count = 0;\n    t->sp_succ_count = 0;\n    t->sp_curr = t->file.segment_index;\n    t->sp_callback = ngx_http_tfs_batch_process_end;\n    tt = &t->next;\n\n    /* create sub process */\n    for (i = 0; i < t->file.curr_batch_count; i++) {\n        st = ngx_http_tfs_alloc_st(t);\n        if (st == NULL) {\n            return NGX_ERROR;\n        }\n\n        st->sp_callback = ngx_http_tfs_batch_process_next;\n\n        /* send(to upstream servers) and output(to client) bufs */\n        st->request_bufs = NULL;\n        st->out_bufs = NULL;\n\n        /* set remote block cache ctx */\n        st->block_cache_ctx.remote_ctx.data = st;\n\n        /* assign segments to each st */\n        st->file.segment_index = 0;\n        st->file.segment_data = &segment_data[i];\n        st->sp_curr = t->file.segment_index + i;\n        st->sp_ready = NGX_HTTP_TFS_NO;\n        st->stat_info.size = 0;\n\n        if (t->r_ctx.action.code == NGX_HTTP_TFS_ACTION_WRITE_FILE) {\n            st->file.left_length = st->file.segment_data->segment_info.size;\n            st->state = NGX_HTTP_TFS_STATE_WRITE_CREATE_FILE_NAME;\n\n        } else if (t->r_ctx.action.code == NGX_HTTP_TFS_ACTION_READ_FILE) {\n            /* custom file need check file hole before each segment */\n            if (t->r_ctx.version == 2) {\n                st->file.file_hole_size = 0;\n                if (i < t->file.segment_count\n                   && (t->file.file_offset\n                       < segment_data[i].segment_info.offset))\n                {\n                    st->file.file_hole_size = ngx_min(t->file.left_length,\n                        (uint64_t)(segment_data[i].segment_info.offset\n                                   - t->file.file_offset));\n                    t->file.file_offset += st->file.file_hole_size;\n                    t->file.left_length -= st->file.file_hole_size;\n                    ngx_log_error(NGX_LOG_DEBUG, t->log, 0,\n                                  \"find file hole, size: %uL\",\n                                  st->file.file_hole_size);\n                }\n            }\n            st->file.file_offset = st->file.segment_data->oper_offset;\n            st->file.left_length = st->file.segment_data->oper_size;\n            t->file.file_offset += st->file.segment_data->oper_size;\n            t->file.left_length -= st->file.segment_data->oper_size;\n\n            st->state = NGX_HTTP_TFS_STATE_READ_READ_DATA;\n        }\n\n        /* select data server */\n        addr = ngx_http_tfs_select_data_server(st, st->file.segment_data);\n        if (addr == NULL) {\n            return NGX_ERROR;\n        }\n\n        data_server = &st->tfs_peer_servers[NGX_HTTP_TFS_DATA_SERVER];\n        ngx_http_tfs_peer_set_addr(t->pool, data_server, addr);\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, t->log, 0,\n                       \"block_id: %uD, select data server: %s\",\n                       st->file.segment_data->segment_info.block_id,\n                       data_server->peer_addr_text);\n\n        if (ngx_http_tfs_reinit(t->data, st) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        st->tfs_peer = ngx_http_tfs_select_peer(st);\n        if (st->tfs_peer == NULL) {\n            return NGX_ERROR;\n        }\n\n        st->recv_chain->buf = &st->header_buffer;\n        st->recv_chain->next->buf = &st->tfs_peer->body_buffer;\n\n        *tt = st;\n        tt = &st->next;\n\n        t->sp_count++;\n\n        if (t->file.left_length == 0) {\n            break;\n        }\n    }\n    *tt = NULL;\n\n    /* start sub process */\n    for (st = t->next; st; st = t->next) {\n        /* st->next may be modified after recycled to free_st */\n        t->next = st->next;\n        ngx_http_tfs_connect(st);\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_tfs_batch_process_end(ngx_http_tfs_t *t)\n{\n    ngx_int_t            rc = NGX_ERROR;\n    ngx_buf_t           *body_buffer;\n    ngx_http_request_t  *r;\n\n    /* error in sub process */\n    if (t->sp_fail_count > 0) {\n        ngx_log_error(NGX_LOG_ERR, t->log, 0,\n                      \"sub process error, rest segment count: %D \",\n                      t->file.segment_count - t->file.segment_index);\n\n        /* write need roll back, remove all segments writtern */\n        if (t->r_ctx.version == 1\n            && t->r_ctx.action.code == NGX_HTTP_TFS_ACTION_WRITE_FILE)\n        {\n            t->state = NGX_HTTP_TFS_STATE_WRITE_GET_BLK_INFO;\n            t->is_rolling_back = NGX_HTTP_TFS_YES;\n            t->file.segment_count = t->file.segment_index + t->sp_count;\n            t->file.segment_index = 0;\n            ngx_http_tfs_finalize_state(t, NGX_OK);\n            return NGX_OK;\n        }\n\n        if (t->request_timeout) {\n            ngx_http_tfs_finalize_request(t->data, t,\n                                          NGX_HTTP_REQUEST_TIME_OUT);\n\n        } else if (t->client_abort) {\n            ngx_http_tfs_finalize_request(t->data, t,\n                                          NGX_HTTP_CLIENT_CLOSED_REQUEST);\n\n        } else {\n            ngx_http_tfs_finalize_state(t, NGX_ERROR);\n        }\n        return NGX_ERROR;\n    }\n\n    t->file.segment_index += t->sp_count;\n    t->file.curr_batch_count = 0;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, t->log, 0,\n                   \"batch process segment count: %uD, rest segment count: %D \",\n                   t->sp_count, t->file.segment_count - t->file.segment_index);\n\n    if (t->r_ctx.action.code == NGX_HTTP_TFS_ACTION_WRITE_FILE) {\n        /* large_file data segment && custom file */\n        if (t->r_ctx.version == 1 && t->is_large_file) {\n            t->state = NGX_HTTP_TFS_STATE_WRITE_GET_BLK_INFO;\n\n            /* need roll back, remove all segments writtern */\n            if (t->client_abort) {\n                t->is_rolling_back = NGX_HTTP_TFS_YES;\n                t->file.segment_count = t->file.segment_index;\n                t->file.segment_index = 0;\n\n            } else {\n                /* all data write over */\n                if (t->file.left_length == 0) {\n                    rc = ngx_http_tfs_set_meta_segment_data(t);\n                    if (rc == NGX_ERROR) {\n                        ngx_http_tfs_finalize_state(t, NGX_ERROR);\n                        return NGX_ERROR;\n                    }\n                }\n            }\n\n        } else if (t->r_ctx.version == 2) {\n            t->state = NGX_HTTP_TFS_STATE_WRITE_WRITE_MS;\n        }\n\n        rc = NGX_OK;\n\n    } else if (t->r_ctx.action.code == NGX_HTTP_TFS_ACTION_READ_FILE) {\n        if (t->file.segment_index < t->file.segment_count\n            && t->file.left_length > 0)\n        {\n            t->state = NGX_HTTP_TFS_STATE_READ_GET_BLK_INFO;\n\n            /* batch lookup block cache */\n            t->block_cache_ctx.curr_lookup_cache =\n                                               NGX_HTTP_TFS_LOCAL_BLOCK_CACHE;\n            return ngx_http_tfs_batch_lookup_block_cache(t);\n        }\n\n        /* read over, restore request's ctx */\n        r = t->data;\n        ngx_http_set_ctx(r, t, ngx_http_tfs_module);\n        rc = NGX_DONE;\n\n        if (t->is_large_file) {\n            t->state = NGX_HTTP_TFS_STATE_READ_DONE;\n            t->file_name = t->r_ctx.file_path_s;\n        }\n\n        if (t->r_ctx.version == 2) {\n            if (t->file.still_have) {\n                t->state = NGX_HTTP_TFS_STATE_READ_GET_FRAG_INFO;\n                body_buffer =\n                 &t->tfs_peer_servers[NGX_HTTP_TFS_META_SERVER].body_buffer;\n                ngx_http_tfs_clear_buf(body_buffer);\n                rc = NGX_OK;\n\n            } else {\n                t->state = NGX_HTTP_TFS_STATE_READ_DONE;\n            }\n        }\n    }\n\n    ngx_http_tfs_finalize_state(t, rc);\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_tfs_batch_process_next(ngx_http_tfs_t *t)\n{\n    if (t->sp_ready) {\n        if (t->sp_fail_count > 0 || t->parent->sp_fail_count > 0) {\n            ngx_log_error(NGX_LOG_ERR, t->log, 0,\n                          \"(other) sub process failed\");\n\n            ngx_http_tfs_finalize_request(t->data, t, NGX_ERROR);\n            return NGX_OK;\n        }\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, t->log, 0,\n                       \"segment[%uD] wake up, will output...\",\n                       t->sp_curr);\n        ngx_http_tfs_send_response(t->data, t);\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_tfs_rd_check_broken_connection(ngx_http_request_t *r)\n{\n    ngx_http_tfs_check_broken_connection(r, r->connection->read);\n}\n\n\nstatic void\nngx_http_tfs_wr_check_broken_connection(ngx_http_request_t *r)\n{\n    ngx_http_tfs_check_broken_connection(r, r->connection->write);\n}\n\n\nstatic void\nngx_http_tfs_check_broken_connection(ngx_http_request_t *r,\n    ngx_event_t *ev)\n{\n    int               n;\n    char              buf[1];\n    ngx_err_t         err;\n    ngx_int_t         event;\n    ngx_http_tfs_t    *t;\n    ngx_connection_t  *c;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ev->log, 0,\n                   \"http tfs check client, write event:%d, \\\"%V\\\"\",\n                   ev->write, &r->uri);\n\n    c = r->connection;\n    t = ngx_http_get_module_ctx(r, ngx_http_tfs_module);\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            ngx_del_event(ev, event, 0);\n        }\n\n        t->client_abort = NGX_HTTP_TFS_YES;\n\n        return;\n    }\n\n#if (NGX_HAVE_KQUEUE)\n\n    if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {\n\n        if (!ev->pending_eof) {\n            return;\n        }\n\n        ev->eof = 1;\n        c->error = 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        t->client_abort = NGX_HTTP_TFS_YES;\n\n        return;\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 tfs recv(): %d\", n);\n\n    if (ev->write && (n >= 0 || err == NGX_EAGAIN)) {\n        return;\n    }\n\n    if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && ev->active) {\n\n        event = ev->write ? NGX_WRITE_EVENT : NGX_READ_EVENT;\n\n        ngx_del_event(ev, event, 0);\n    }\n\n    if (n > 0) {\n        return;\n    }\n\n    if (n == -1) {\n        if (err == NGX_EAGAIN) {\n            return;\n        }\n\n        ev->error = 1;\n\n    } else { /* n == 0 */\n        err = 0;\n    }\n\n    ev->eof = 1;\n    c->error = 1;\n\n    ngx_log_error(NGX_LOG_INFO, ev->log, err,\n                  \"client prematurely closed connection\");\n\n    t->client_abort = NGX_HTTP_TFS_YES;\n}\n"
  },
  {
    "path": "modules/ngx_http_tfs_module/ngx_http_tfs.h",
    "content": "\n/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#ifndef _NGX_HTTP_TFS_H_INCLUDED_\n#define _NGX_HTTP_TFS_H_INCLUDED_\n\n\n#include <ngx_core.h>\n#include <ngx_http.h>\n#include <ngx_tfs_common.h>\n#include <ngx_http_tfs_json.h>\n#include <ngx_http_tfs_protocol.h>\n#include <ngx_http_tfs_restful.h>\n#include <ngx_http_connection_pool.h>\n#include <ngx_http_tfs_rc_server_info.h>\n#include <ngx_http_tfs_block_cache.h>\n#include <ngx_http_tfs_duplicate.h>\n#include <ngx_http_tfs_raw_fsname.h>\n#include <ngx_http_tfs_peer_connection.h>\n#include <ngx_http_tfs_tair_helper.h>\n#include <ngx_http_tfs_timers.h>\n\n\ntypedef ngx_table_elt_t *(*ngx_http_tfs_create_header_pt)\n    (ngx_http_request_t *r);\n\n\ntypedef struct {\n    ngx_str_t                      state_msg;\n    ngx_int_t                      state;\n} ngx_http_tfs_state_t;\n\n\ntypedef struct {\n    uint32_t                       ds_count;\n    ngx_http_tfs_inet_t           *ds_addrs;\n    int32_t                        version;\n    int32_t                        lease_id;\n} ngx_http_tfs_block_info_t;\n\n\ntypedef struct {\n    uint32_t                       block_id;\n    uint64_t                       file_id;\n    int64_t                        offset;  /* offset in the file */\n    uint32_t                       size;\n    uint32_t                       crc;\n} NGX_PACKED ngx_http_tfs_segment_info_t;\n\n\ntypedef struct {\n    u_char                         file_name[NGX_HTTP_TFS_FILE_NAME_LEN];\n    int64_t                        offset;\n    uint32_t                       size;\n    uint32_t                       crc;\n} NGX_PACKED ngx_http_tfs_tmp_segment_info_t;\n\n\nstruct ngx_http_tfs_segment_data_s {\n    uint8_t                        cache_hit;\n    ngx_http_tfs_segment_info_t    segment_info;\n    /* read/write offset inside this segment */\n    uint32_t                       oper_offset;\n    /* read/write size inside this segment */\n    uint32_t                       oper_size;\n    union {\n        uint64_t                   write_file_number;\n    };\n    ngx_http_tfs_block_info_t      block_info;\n    ngx_uint_t                     ds_retry;\n    ngx_uint_t                     ds_index;\n    ngx_chain_t                   *data;\n    ngx_chain_t                   *orig_data; /* for write retry */\n} NGX_PACKED;\n\n\ntypedef struct {\n    uint8_t                        still_have; /* for custom file */\n    uint32_t                       cluster_id;\n    uint32_t                       open_mode;\n    /* not for large_file's data */\n    int64_t                        file_offset;\n    uint64_t                       left_length;\n    uint64_t                       file_hole_size;\n    uint32_t                       last_write_segment_index;\n    uint32_t                       segment_index;\n    uint32_t                       segment_count;\n    ngx_http_tfs_segment_data_t   *segment_data;\n    uint32_t                       curr_batch_count;\n} NGX_PACKED ngx_http_tfs_file_t;\n\n\nstruct  ngx_http_tfs_upstream_s {\n    ngx_str_t                      lock_file;\n    ngx_msec_t                     rcs_interval;\n\n    ngx_str_t                      rcs_zone_name;\n    ngx_shm_zone_t                *rcs_shm_zone;\n    ngx_http_tfs_rc_ctx_t         *rc_ctx;\n    uint8_t                        rcserver_index;\n    uint32_t                       rc_servers_count;\n    uint64_t                       rc_servers[NGX_HTTP_TFS_MAX_RCSERVER_COUNT];\n\n    /* upstream name and port */\n    in_port_t                      port;\n    ngx_str_t                      host;\n    ngx_addr_t                    *ups_addr;\n\n    struct sockaddr_in             local_addr;\n    u_char                         local_addr_text[NGX_INET_ADDRSTRLEN];\n\n    ngx_flag_t                     enable_rcs;\n\n    ngx_http_tfs_timers_data_t    *timer_data;\n\n    unsigned                       used:1;\n};\n\n\nstruct  ngx_http_tfs_loc_conf_s {\n    ngx_msec_t                     timeout;\n\n    size_t                         max_temp_file_size;\n    size_t                         temp_file_write_size;\n\n    size_t                         busy_buffers_size_conf;\n\n    uint64_t                       meta_root_server;\n    ngx_http_tfs_meta_table_t      meta_server_table;\n\n    ngx_http_tfs_upstream_t       *upstream;\n};\n\n\ntypedef struct {\n\n    ngx_log_t                     *log;\n} ngx_http_tfs_srv_conf_t;\n\n\nstruct  ngx_http_tfs_main_conf_s {\n    ngx_msec_t                     tfs_connect_timeout;\n    ngx_msec_t                     tfs_send_timeout;\n    ngx_msec_t                     tfs_read_timeout;\n\n    ngx_msec_t                     tair_timeout;\n    ngx_http_tfs_tair_instance_t   dup_instances[NGX_HTTP_TFS_MAX_CLUSTER_COUNT];\n\n    size_t                         send_lowat;\n    size_t                         buffer_size;\n    size_t                         body_buffer_size;\n    size_t                         busy_buffers_size;\n\n    ngx_shm_zone_t                *block_cache_shm_zone;\n\n    ngx_flag_t                     enable_remote_block_cache;\n    ngx_http_tfs_tair_instance_t   remote_block_cache_instance;\n    ngx_http_tfs_local_block_cache_ctx_t *local_block_cache_ctx;\n\n    ngx_http_connection_pool_t    *conn_pool;\n\n    uint32_t                       cluster_id;\n\n    ngx_array_t                    upstreams;\n};\n\n\ntypedef ngx_int_t (*tfs_peer_handler_pt)(ngx_http_tfs_t *t);\ntypedef void (*ngx_http_tfs_handler_pt)(ngx_http_request_t *r,\n    ngx_http_tfs_t *t);\ntypedef ngx_int_t (*ngx_http_tfs_sub_process_pt)(ngx_http_tfs_t *t);\n\n\ntypedef struct {\n    ngx_list_t                     headers;\n\n    ngx_uint_t                     status_n;\n    ngx_str_t                      status_line;\n\n    ngx_table_elt_t               *status;\n    ngx_table_elt_t               *date;\n    ngx_table_elt_t               *server;\n    ngx_table_elt_t               *connection;\n\n    ngx_table_elt_t               *expires;\n    ngx_table_elt_t               *etag;\n    ngx_table_elt_t               *x_accel_expires;\n    ngx_table_elt_t               *x_accel_redirect;\n    ngx_table_elt_t               *x_accel_limit_rate;\n\n    ngx_table_elt_t               *content_type;\n    ngx_table_elt_t               *content_length;\n\n    ngx_table_elt_t               *last_modified;\n    ngx_table_elt_t               *location;\n    ngx_table_elt_t               *accept_ranges;\n    ngx_table_elt_t               *www_authenticate;\n\n#if (NGX_HTTP_GZIP)\n    ngx_table_elt_t               *content_encoding;\n#endif\n\n    off_t                          content_length_n;\n\n    ngx_array_t                    cache_control;\n} ngx_http_tfs_headers_in_t;\n\n\nstruct ngx_http_tfs_s {\n    ngx_http_tfs_handler_pt        read_event_handler;\n    ngx_http_tfs_handler_pt        write_event_handler;\n\n    ngx_http_tfs_peer_connection_t *tfs_peer;\n    ngx_http_tfs_peer_connection_t *tfs_peer_servers;\n    uint8_t                       tfs_peer_count;\n\n    ngx_http_tfs_loc_conf_t       *loc_conf;\n    ngx_http_tfs_srv_conf_t       *srv_conf;\n    ngx_http_tfs_main_conf_t      *main_conf;\n\n    ngx_http_tfs_restful_ctx_t     r_ctx;\n\n    ngx_output_chain_ctx_t         output;\n    ngx_chain_writer_ctx_t         writer;\n\n    ngx_chain_t                   *request_bufs;\n    ngx_chain_t                   *send_body;\n    ngx_pool_t                    *pool;\n\n    ngx_buf_t                      header_buffer;\n\n    ngx_chain_t                   *recv_chain;\n\n    ngx_chain_t                   *out_bufs;\n    ngx_chain_t                   *busy_bufs;\n    ngx_chain_t                   *free_bufs;\n\n    ngx_http_tfs_rcs_info_t       *rc_info_node;\n    ngx_rbtree_node_t             *node;\n\n    ngx_uint_t                     logical_cluster_index;\n    ngx_uint_t                     rw_cluster_index;\n\n    /* keep alive */\n    ngx_queue_t                   *curr_ka_queue;\n\n    /* header pointer */\n    void                          *header;\n    ngx_int_t                      header_size;\n\n    tfs_peer_handler_pt            create_request;\n    tfs_peer_handler_pt            input_filter;\n    tfs_peer_handler_pt            retry_handler;\n    tfs_peer_handler_pt            process_request_body;\n    tfs_peer_handler_pt            finalize_request;\n    tfs_peer_handler_pt            decline_handler;\n\n    void                          *finalize_data;\n    void                          *data;\n\n    ngx_int_t                      request_sent;\n    ngx_uint_t                     sent_size;\n    off_t                          length;\n\n    ngx_log_t                     *log;\n\n    ngx_int_t                      parse_state;\n\n    /* final file name */\n    ngx_str_t                      file_name;\n\n    ngx_int_t                      state;\n\n    /* custom file */\n    ngx_http_tfs_custom_meta_info_t meta_info;\n    ngx_str_t                      last_file_path;\n    int64_t                        last_file_pid;\n    uint8_t                        last_file_type;\n    ngx_int_t                     *dir_lens;\n    ngx_int_t                      last_dir_level;\n    uint16_t                       orig_action;\n    ngx_array_t                    file_holes;\n\n    ngx_http_tfs_headers_in_t      headers_in;\n\n    /* delete */\n    ngx_int_t                      group_count;\n    ngx_int_t                      group_seq;\n\n    /* name ip */\n    ngx_http_tfs_inet_t            name_server_addr;\n    ngx_str_t                      name_server_addr_text;\n\n    ngx_http_tfs_json_gen_t       *json_output;\n\n    ngx_uint_t                     status;\n    ngx_str_t                      status_line;\n    ngx_int_t                      tfs_status;\n\n    uint64_t                       output_size;\n\n    /* de-duplicate info */\n    ngx_http_tfs_dedup_ctx_t       dedup_ctx;\n\n    /* stat info */\n    ngx_http_tfs_stat_info_t       stat_info;\n\n    /* file info */\n    ngx_chain_t                   *meta_segment_data;\n    ngx_http_tfs_file_t            file;\n    ngx_http_tfs_segment_head_t   *seg_head;\n    ngx_http_tfs_raw_file_stat_t   file_stat;\n    ngx_buf_t                     *readv2_rsp_tail_buf;\n    uint8_t                        read_ver;\n    uint8_t                        retry_count;\n\n    /* block cache */\n    ngx_http_tfs_block_cache_ctx_t block_cache_ctx;\n\n    /* for parallel write segments */\n    ngx_http_tfs_t                *parent;\n    ngx_http_tfs_t                *next;\n    ngx_http_tfs_t                *free_sts;\n    ngx_http_tfs_sub_process_pt    sp_callback;\n    uint32_t                       sp_count;\n    uint32_t                       sp_done_count;\n    uint32_t                       sp_fail_count;\n    uint32_t                       sp_succ_count;\n    uint32_t                       sp_curr;\n    unsigned                       sp_ready:1;\n\n    unsigned                       header_only:1;\n    unsigned                       has_split_frag:1;\n    /* for custrom file read */\n    unsigned                       is_first_segment:1;\n\n    unsigned                       use_dedup:1;\n    unsigned                       is_stat_dup_file:1;\n    unsigned                       is_large_file:1;\n    unsigned                       is_process_meta_seg:1;\n    unsigned                       retry_curr_ns:1;\n    unsigned                       request_timeout:1;\n    unsigned                       client_abort:1;\n    unsigned                       is_rolling_back:1;\n    unsigned                       header_sent:1;\n};\n\n\nngx_int_t ngx_http_tfs_init(ngx_http_tfs_t *t);\nvoid ngx_http_tfs_finalize_request(ngx_http_request_t *r,\n    ngx_http_tfs_t *t, ngx_int_t rc);\nvoid ngx_http_tfs_finalize_state(ngx_http_tfs_t *t, ngx_int_t rc);\nngx_int_t ngx_http_tfs_reinit(ngx_http_request_t *r, ngx_http_tfs_t *t);\nngx_int_t ngx_http_tfs_connect(ngx_http_tfs_t *t);\n/* block cache related */\nngx_int_t ngx_http_tfs_lookup_block_cache(ngx_http_tfs_t *t);\nngx_int_t ngx_http_tfs_batch_lookup_block_cache(ngx_http_tfs_t *t);\nvoid ngx_http_tfs_remove_block_cache(ngx_http_tfs_t *t,\n    ngx_http_tfs_segment_data_t *segment_data);\n\nngx_int_t ngx_http_tfs_set_output_appid(ngx_http_tfs_t *t, uint64_t app_id);\nvoid ngx_http_tfs_set_custom_initial_parameters(ngx_http_tfs_t *t);\nngx_int_t ngx_http_tfs_misc_ctx_init(ngx_http_tfs_t *t,\n    ngx_http_tfs_rcs_info_t *rc_node);\n\n/* dedup related */\nngx_int_t ngx_http_tfs_get_duplicate_info(ngx_http_tfs_t *t);\nngx_int_t ngx_http_tfs_set_duplicate_info(ngx_http_tfs_t *t);\n\n/* sub process related */\nngx_int_t ngx_http_tfs_batch_process_start(ngx_http_tfs_t *t);\nngx_int_t ngx_http_tfs_batch_process_end(ngx_http_tfs_t *t);\nngx_int_t ngx_http_tfs_batch_process_next(ngx_http_tfs_t *t);\n\n\n#endif /* _NGX_TFS_H_INCLUDED_ */\n"
  },
  {
    "path": "modules/ngx_http_tfs_module/ngx_http_tfs_block_cache.c",
    "content": "\n/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#include <ngx_http_tfs.h>\n#include <ngx_http_tfs_block_cache.h>\n#include <ngx_http_tfs_local_block_cache.h>\n#include <ngx_http_tfs_remote_block_cache.h>\n\n\nngx_int_t\nngx_http_tfs_block_cache_lookup(ngx_http_tfs_block_cache_ctx_t *ctx,\n    ngx_pool_t *pool, ngx_log_t *log, ngx_http_tfs_block_cache_key_t *key,\n    ngx_http_tfs_block_cache_value_t *value)\n{\n    ngx_int_t  rc = NGX_DECLINED;\n\n    if (ctx->curr_lookup_cache == NGX_HTTP_TFS_LOCAL_BLOCK_CACHE) {\n\n        ctx->curr_lookup_cache = NGX_HTTP_TFS_REMOTE_BLOCK_CACHE;\n\n        if (ctx->use_cache & NGX_HTTP_TFS_LOCAL_BLOCK_CACHE) {\n            rc = ngx_http_tfs_local_block_cache_lookup(ctx->local_ctx,\n                                                       pool, log, key, value);\n\n            if (rc == NGX_OK) {\n                return rc;\n            }\n        }\n    }\n\n    if (ctx->curr_lookup_cache == NGX_HTTP_TFS_REMOTE_BLOCK_CACHE) {\n\n        ctx->curr_lookup_cache = NGX_HTTP_TFS_NO_BLOCK_CACHE;\n\n        if (ctx->use_cache & NGX_HTTP_TFS_REMOTE_BLOCK_CACHE) {\n            rc = ngx_http_tfs_remote_block_cache_lookup(&ctx->remote_ctx,\n                                                        pool, log, key);\n        }\n    }\n\n    return rc;\n}\n\n\nvoid\nngx_http_tfs_block_cache_insert(ngx_http_tfs_block_cache_ctx_t *ctx,\n    ngx_pool_t *pool, ngx_log_t *log, ngx_http_tfs_block_cache_key_t *key,\n    ngx_http_tfs_block_cache_value_t *value)\n{\n    if (ctx->use_cache & NGX_HTTP_TFS_REMOTE_BLOCK_CACHE) {\n        ngx_http_tfs_remote_block_cache_insert(&ctx->remote_ctx,\n                                               pool, log, key, value);\n    }\n\n    if (ctx->use_cache & NGX_HTTP_TFS_LOCAL_BLOCK_CACHE) {\n        ngx_http_tfs_local_block_cache_insert(ctx->local_ctx, log, key, value);\n    }\n}\n\n\nvoid\nngx_http_tfs_block_cache_remove(ngx_http_tfs_block_cache_ctx_t *ctx,\n    ngx_pool_t *pool, ngx_log_t *log, ngx_http_tfs_block_cache_key_t* key,\n    uint8_t hit_status)\n{\n    if (hit_status != NGX_HTTP_TFS_NO_BLOCK_CACHE\n        && (ctx->use_cache & NGX_HTTP_TFS_LOCAL_BLOCK_CACHE))\n    {\n        ngx_http_tfs_local_block_cache_remove(ctx->local_ctx, log, key);\n    }\n\n    if (hit_status == NGX_HTTP_TFS_REMOTE_BLOCK_CACHE) {\n        ngx_http_tfs_remote_block_cache_remove(&ctx->remote_ctx,\n                                               pool, log, key);\n    }\n}\n\n\nngx_int_t\nngx_http_tfs_block_cache_batch_lookup(ngx_http_tfs_block_cache_ctx_t *ctx,\n    ngx_pool_t *pool, ngx_log_t *log, ngx_array_t *keys,\n    ngx_array_t *kvs)\n{\n    uint32_t                        i;\n    ngx_int_t                       rc;\n    ngx_uint_t                      local_miss_count;\n    ngx_array_t                     local_miss_keys;\n    ngx_http_tfs_t                 *t;\n    ngx_http_tfs_segment_data_t    *segment_data;\n    ngx_http_tfs_block_cache_key_t *key;\n\n    rc = NGX_DECLINED;\n\n    if (ctx->curr_lookup_cache == NGX_HTTP_TFS_LOCAL_BLOCK_CACHE) {\n\n        ctx->curr_lookup_cache = NGX_HTTP_TFS_REMOTE_BLOCK_CACHE;\n\n        if (ctx->use_cache & NGX_HTTP_TFS_LOCAL_BLOCK_CACHE) {\n            rc = ngx_http_tfs_local_block_cache_batch_lookup(ctx->local_ctx,\n                                                             pool, log, keys,\n                                                             kvs);\n\n            if (rc == NGX_OK || rc == NGX_ERROR) {\n                return rc;\n            }\n        }\n    }\n\n    /* rc == NGX_DECLIEND */\n    if (ctx->curr_lookup_cache == NGX_HTTP_TFS_REMOTE_BLOCK_CACHE) {\n\n        ctx->curr_lookup_cache = NGX_HTTP_TFS_NO_BLOCK_CACHE;\n\n        if (ctx->use_cache & NGX_HTTP_TFS_REMOTE_BLOCK_CACHE) {\n            t = ctx->remote_ctx.data;\n            local_miss_count = keys->nelts - kvs->nelts;\n\n            rc = ngx_array_init(&local_miss_keys, t->pool, local_miss_count,\n                                sizeof(ngx_http_tfs_block_cache_key_t));\n            if (rc == NGX_ERROR) {\n                return rc;\n            }\n\n            segment_data = &t->file.segment_data[t->file.segment_index];\n            for (i = 0; i < keys->nelts; i++, segment_data++) {\n                if (segment_data->cache_hit == NGX_HTTP_TFS_NO_BLOCK_CACHE) {\n                    key = (ngx_http_tfs_block_cache_key_t *)\n                           ngx_array_push(&local_miss_keys);\n                    key->ns_addr = *((uint64_t*)(&t->name_server_addr));\n                    key->block_id = segment_data->segment_info.block_id;\n                }\n            }\n\n            rc = ngx_http_tfs_remote_block_cache_batch_lookup(&ctx->remote_ctx,\n                                                              pool, log,\n                                                              &local_miss_keys);\n        }\n    }\n\n    return rc;\n}\n\n\nngx_int_t\nngx_http_tfs_block_cache_cmp(ngx_http_tfs_block_cache_key_t *left,\n    ngx_http_tfs_block_cache_key_t *right)\n{\n    if (left->ns_addr == right->ns_addr) {\n\n        if (left->block_id == right->block_id) {\n            return 0;\n        }\n\n        if (left->block_id < right->block_id) {\n            return -1;\n        }\n\n        return 1;\n    }\n\n    if (left->ns_addr < right->ns_addr) {\n        return -1;\n    }\n\n    return 1;\n}\n"
  },
  {
    "path": "modules/ngx_http_tfs_module/ngx_http_tfs_block_cache.h",
    "content": "\n/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#ifndef _NGX_HTTP_TFS_BLOCK_CACHE_H_INCLUDED_\n#define _NGX_HTTP_TFS_BLOCK_CACHE_H_INCLUDED_\n\n\n#include <ngx_tfs_common.h>\n#include <ngx_http_tfs_tair_helper.h>\n\n\n#define NGX_HTTP_TFS_BLOCK_CACHE_KEY_SIZE sizeof(ngx_http_tfs_block_cache_key_t)\n\n#define NGX_HTTP_TFS_REMOTE_BLOCK_CACHE_VALUE_BASE_SIZE sizeof(uint32_t)\n\n#define NGX_HTTP_TFS_BLOCK_CACHE_DISCARD_ITEM_COUNT 10000\n\n#define NGX_HTTP_TFS_BLOCK_CACHE_STAT_COUNT  (3000 * 60 * 60)\n\n#define NGX_HTTP_TFS_NO_BLOCK_CACHE      0x0\n#define NGX_HTTP_TFS_LOCAL_BLOCK_CACHE   0x1\n#define NGX_HTTP_TFS_REMOTE_BLOCK_CACHE  0x2\n\n\ntypedef struct {\n    uint64_t                             ns_addr;\n    uint32_t                             block_id;\n} __attribute__ ((__packed__)) ngx_http_tfs_block_cache_key_t;\n\n\ntypedef struct {\n    uint32_t                             ds_count;\n    uint64_t                            *ds_addrs;\n} ngx_http_tfs_block_cache_value_t;\n\n\ntypedef struct {\n    ngx_http_tfs_block_cache_key_t      *key;\n    ngx_http_tfs_block_cache_value_t    *value;\n} ngx_http_tfs_block_cache_kv_t;\n\n\ntypedef struct {\n    ngx_rbtree_t                         rbtree;\n    ngx_rbtree_node_t                    sentinel;\n    ngx_queue_t                          queue;\n    uint64_t                             discard_item_count;\n    uint64_t                             hit_count;\n    uint64_t                             miss_count;\n} ngx_http_tfs_block_cache_shctx_t;\n\n\ntypedef struct {\n    ngx_http_tfs_block_cache_shctx_t    *sh;\n    ngx_slab_pool_t                     *shpool;\n} ngx_http_tfs_local_block_cache_ctx_t;\n\n\ntypedef struct {\n    void                                *data;\n    ngx_http_tfs_tair_instance_t        *tair_instance;\n} ngx_http_tfs_remote_block_cache_ctx_t;\n\n\ntypedef struct {\n    ngx_http_tfs_local_block_cache_ctx_t *local_ctx;\n    ngx_http_tfs_remote_block_cache_ctx_t remote_ctx;\n    uint8_t                               use_cache;\n    uint8_t                               curr_lookup_cache;\n} ngx_http_tfs_block_cache_ctx_t;\n\n\nngx_int_t ngx_http_tfs_block_cache_lookup(ngx_http_tfs_block_cache_ctx_t *ctx,\n    ngx_pool_t *pool, ngx_log_t *log, ngx_http_tfs_block_cache_key_t* key,\n    ngx_http_tfs_block_cache_value_t *value);\nvoid ngx_http_tfs_block_cache_insert(ngx_http_tfs_block_cache_ctx_t *ctx,\n    ngx_pool_t *pool, ngx_log_t *log, ngx_http_tfs_block_cache_key_t *key,\n    ngx_http_tfs_block_cache_value_t *value);\nvoid ngx_http_tfs_block_cache_remove(ngx_http_tfs_block_cache_ctx_t *ctx,\n    ngx_pool_t *pool, ngx_log_t *log, ngx_http_tfs_block_cache_key_t *key,\n    uint8_t hit_status);\nngx_int_t ngx_http_tfs_block_cache_batch_lookup(\n    ngx_http_tfs_block_cache_ctx_t *ctx,\n    ngx_pool_t *pool, ngx_log_t *log, ngx_array_t* keys, ngx_array_t *kvs);\nvoid ngx_http_tfs_block_cache_batch_insert(ngx_http_tfs_block_cache_ctx_t *ctx,\n    ngx_pool_t *pool, ngx_log_t *log, ngx_array_t* kvs);\nngx_int_t ngx_http_tfs_block_cache_cmp(ngx_http_tfs_block_cache_key_t *left,\n    ngx_http_tfs_block_cache_key_t *right);\n\n\n#endif /* _NGX_HTTP_TFS_BLOCK_CACHE_H_INCLUDED_ */\n"
  },
  {
    "path": "modules/ngx_http_tfs_module/ngx_http_tfs_data_server_message.c",
    "content": "\n/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#include <ngx_http_tfs_data_server_message.h>\n#include <ngx_http_tfs_json.h>\n#include <ngx_http_tfs_protocol.h>\n#include <ngx_http_tfs_errno.h>\n#include <ngx_http_tfs_duplicate.h>\n\n\nstatic ngx_chain_t *ngx_http_tfs_create_createfile_message(ngx_http_tfs_t *t,\n    ngx_http_tfs_segment_data_t *segment_data);\nstatic ngx_chain_t *ngx_http_tfs_create_write_message(ngx_http_tfs_t *t,\n    ngx_http_tfs_segment_data_t *segment_data);\nstatic ngx_chain_t *ngx_http_tfs_create_closefile_message(ngx_http_tfs_t *t,\n    ngx_http_tfs_segment_data_t *segment_data);\nstatic ngx_chain_t *ngx_http_tfs_create_read_message(ngx_http_tfs_t *t,\n    ngx_http_tfs_segment_data_t *segment_data, uint8_t read_ver,\n    uint8_t read_flag);\nstatic ngx_chain_t *ngx_http_tfs_create_unlink_message(ngx_http_tfs_t *t,\n    ngx_http_tfs_segment_data_t *segment_data);\nstatic ngx_chain_t * ngx_http_tfs_create_stat_message(ngx_http_tfs_t *t,\n    ngx_http_tfs_segment_data_t *segment_data);\n\nstatic ngx_int_t ngx_http_tfs_parse_createfile_message(ngx_http_tfs_t *t,\n    ngx_http_tfs_segment_data_t *segment_data);\nstatic ngx_int_t ngx_http_tfs_parse_write_message(ngx_http_tfs_t *t);\nstatic ngx_int_t ngx_http_tfs_parse_closefile_message(ngx_http_tfs_t *t);\nstatic ngx_int_t ngx_http_tfs_parse_read_message(ngx_http_tfs_t *t);\nstatic ngx_int_t ngx_http_tfs_parse_remove_message(ngx_http_tfs_t *t);\nstatic ngx_int_t ngx_http_tfs_parse_statfile_message(ngx_http_tfs_t *t,\n    ngx_http_tfs_segment_data_t *segment_data);\n\nstatic int32_t ngx_http_tfs_find_segment(uint32_t seg_count,\n    ngx_http_tfs_segment_info_t *seg_info, int64_t offset);\nstatic ngx_int_t ngx_http_tfs_copy_body_buffer(ngx_http_tfs_t *t,\n    ssize_t bytes, u_char *body);\n\n\nngx_http_tfs_inet_t *\nngx_http_tfs_select_data_server(ngx_http_tfs_t *t,\n    ngx_http_tfs_segment_data_t *segment_data)\n{\n    ngx_http_tfs_block_info_t  *block_info;\n\n    block_info = &segment_data->block_info;\n\n    switch(t->r_ctx.action.code) {\n    case NGX_HTTP_TFS_ACTION_STAT_FILE:\n    case NGX_HTTP_TFS_ACTION_READ_FILE:\n        if (block_info->ds_count > 0) {\n            if (segment_data->ds_retry > 0) {\n                segment_data->ds_index %= block_info->ds_count;\n\n            } else {\n                segment_data->ds_index = ngx_random() % block_info->ds_count;\n            }\n        }\n        break;\n\n    case NGX_HTTP_TFS_ACTION_WRITE_FILE:\n    case NGX_HTTP_TFS_ACTION_REMOVE_FILE:\n        if (t->is_stat_dup_file) {\n            if (block_info->ds_count > 0) {\n                if (segment_data->ds_retry > 0) {\n                    segment_data->ds_index %= block_info->ds_count;\n\n                } else {\n                    segment_data->ds_index =ngx_random() % block_info->ds_count;\n                }\n            }\n\n        } else {\n            /* write retry ns */\n            if (segment_data->ds_retry > 0) {\n                return NULL;\n            }\n            segment_data->ds_index = 0;\n        }\n        break;\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, t->log, 0,\n                   \"select data server, ds_retry: %ui, ds_index: %ui\",\n                   segment_data->ds_retry, segment_data->ds_index);\n\n    if (segment_data->ds_retry++ >= block_info->ds_count) {\n        return NULL;\n    }\n\n    return &block_info->ds_addrs[segment_data->ds_index++];\n}\n\n\nngx_chain_t *\nngx_http_tfs_data_server_create_message(ngx_http_tfs_t *t)\n{\n    uint32_t                      meta_segment_size;\n    uint16_t                      action;\n    ngx_int_t                     rc;\n    ngx_chain_t                  *cl;\n    ngx_http_tfs_segment_data_t  *segment_data;\n\n    cl = NULL;\n    meta_segment_size = 0;\n    action = t->r_ctx.action.code;\n    segment_data = &t->file.segment_data[t->file.segment_index];\n\n    switch (action) {\n    case NGX_HTTP_TFS_ACTION_STAT_FILE:\n        if (t->r_ctx.chk_exist == NGX_HTTP_TFS_NO) {\n            t->json_output = ngx_http_tfs_json_init(t->log, t->pool);\n            if (t->json_output == NULL) {\n                return NULL;\n            }\n        }\n        if (t->is_large_file) {\n            segment_data->oper_size = sizeof(ngx_http_tfs_segment_head_t);\n            return ngx_http_tfs_create_read_message(t, segment_data,\n                            NGX_HTTP_TFS_READ_V2, NGX_HTTP_TFS_READ_STAT_FORCE);\n        }\n\n        return ngx_http_tfs_create_stat_message(t, segment_data);\n\n    case NGX_HTTP_TFS_ACTION_READ_FILE:\n        t->read_ver = NGX_HTTP_TFS_READ;\n        t->header_size = sizeof(ngx_http_tfs_ds_read_response_t);\n        /* large file need read meta segment first */\n        if (t->is_large_file && t->is_process_meta_seg) {\n            if (t->meta_segment_data == NULL) {\n                /* for files smaller than 140GB, 2MB is fairly enough */\n                cl = ngx_http_tfs_chain_get_free_buf(t->pool, &t->free_bufs,\n                    NGX_HTTP_TFS_MAX_FRAGMENT_SIZE);\n                if (cl == NULL) {\n                    return NULL;\n                }\n                t->tfs_peer->body_buffer = *(cl->buf);\n                t->meta_segment_data = cl;\n            }\n        }\n\n        /* use readv2 if read from start */\n        /* unless is large file data segment */\n        if (t->r_ctx.version == 1\n            && t->file.file_offset == 0\n            && !t->parent)\n        {\n            t->read_ver = NGX_HTTP_TFS_READ_V2;\n        }\n        /* custom file need fill file hole */\n        if (t->r_ctx.version == 2 && t->file.file_hole_size > 0) {\n            rc = ngx_http_tfs_fill_file_hole(t, t->file.file_hole_size);\n            if (rc == NGX_ERROR) {\n                return NULL;\n            }\n            t->stat_info.size += t->file.file_hole_size;\n            t->file.file_hole_size = 0;\n        }\n        return ngx_http_tfs_create_read_message(t, segment_data,\n                                                t->read_ver,\n                                                t->r_ctx.read_stat_type);\n    case NGX_HTTP_TFS_ACTION_WRITE_FILE:\n        switch(t->state) {\n        case NGX_HTTP_TFS_STATE_WRITE_STAT_DUP_FILE:\n            return ngx_http_tfs_create_stat_message(t, segment_data);\n        case NGX_HTTP_TFS_STATE_WRITE_CREATE_FILE_NAME:\n            return ngx_http_tfs_create_createfile_message(t, segment_data);\n        case NGX_HTTP_TFS_STATE_WRITE_WRITE_DATA:\n            return ngx_http_tfs_create_write_message(t, segment_data);\n        case NGX_HTTP_TFS_STATE_WRITE_CLOSE_FILE:\n            return ngx_http_tfs_create_closefile_message(t, segment_data);\n        case NGX_HTTP_TFS_STATE_WRITE_DELETE_DATA:\n            return ngx_http_tfs_create_unlink_message(t, segment_data);\n        default:\n            return NULL;\n        }\n\n    case NGX_HTTP_TFS_ACTION_REMOVE_FILE:\n        switch(t->state) {\n        case NGX_HTTP_TFS_STATE_REMOVE_STAT_FILE:\n            return ngx_http_tfs_create_stat_message(t, segment_data);\n\n        case NGX_HTTP_TFS_STATE_REMOVE_READ_META_SEGMENT:\n            t->read_ver = NGX_HTTP_TFS_READ;\n            if (t->meta_segment_data == NULL) {\n                if (t->use_dedup) {\n                    meta_segment_size = t->file_stat.size;\n                    t->file.left_length = t->file_stat.size;\n                }\n                /* if is large file, for files smaller than 140GB,\n                 * 2MB is fairly enough\n                 */\n                if (t->is_large_file) {\n                    meta_segment_size = NGX_HTTP_TFS_MAX_FRAGMENT_SIZE;\n                    t->file.left_length = NGX_HTTP_TFS_MAX_SIZE;\n                }\n                cl = ngx_http_tfs_chain_get_free_buf(t->pool, &t->free_bufs,\n                    meta_segment_size);\n                if (cl == NULL) {\n                    return NULL;\n                }\n                t->meta_segment_data = cl;\n\n                /* avoid alloc body_buffer twice */\n                if (!t->is_large_file && t->use_dedup) {\n                    t->dedup_ctx.save_body_buffer = t->tfs_peer->body_buffer;\n                }\n\n                t->tfs_peer->body_buffer = *(cl->buf);\n            }\n            t->header_size = sizeof(ngx_http_tfs_ds_read_response_t);\n\n            /* use readv2 to get file size if we do not know that */\n            if (t->file.left_length == NGX_HTTP_TFS_MAX_SIZE) {\n                t->read_ver = NGX_HTTP_TFS_READ_V2;\n            }\n            return ngx_http_tfs_create_read_message(t, segment_data,\n                                     t->read_ver, NGX_HTTP_TFS_READ_STAT_FORCE);\n        case NGX_HTTP_TFS_STATE_REMOVE_DELETE_DATA:\n            return ngx_http_tfs_create_unlink_message(t, segment_data);\n\n        default:\n            return NULL;\n        }\n    }\n\n    return cl;\n}\n\n\nngx_int_t\nngx_http_tfs_data_server_parse_message(ngx_http_tfs_t *t)\n{\n    ngx_http_tfs_segment_data_t  *segment_data;\n\n    segment_data = &t->file.segment_data[t->file.segment_index];\n\n    switch (t->r_ctx.action.code) {\n    case NGX_HTTP_TFS_ACTION_READ_FILE:\n        return ngx_http_tfs_parse_read_message(t);\n\n    case NGX_HTTP_TFS_ACTION_STAT_FILE:\n        return ngx_http_tfs_parse_statfile_message(t, segment_data);\n\n    case NGX_HTTP_TFS_ACTION_WRITE_FILE:\n        switch(t->state) {\n        case NGX_HTTP_TFS_STATE_WRITE_STAT_DUP_FILE:\n            return ngx_http_tfs_parse_statfile_message(t, segment_data);\n        case NGX_HTTP_TFS_STATE_WRITE_CREATE_FILE_NAME:\n            return ngx_http_tfs_parse_createfile_message(t, segment_data);\n        case NGX_HTTP_TFS_STATE_WRITE_WRITE_DATA:\n            return ngx_http_tfs_parse_write_message(t);\n        case NGX_HTTP_TFS_STATE_WRITE_CLOSE_FILE:\n            return ngx_http_tfs_parse_closefile_message(t);\n        case NGX_HTTP_TFS_STATE_WRITE_DELETE_DATA:\n            return ngx_http_tfs_parse_remove_message(t);\n        default:\n            return NGX_ERROR;\n        }\n\n    case NGX_HTTP_TFS_ACTION_REMOVE_FILE:\n        switch(t->state) {\n        case NGX_HTTP_TFS_STATE_REMOVE_STAT_FILE:\n            return ngx_http_tfs_parse_statfile_message(t, segment_data);\n        case NGX_HTTP_TFS_STATE_REMOVE_READ_META_SEGMENT:\n            return ngx_http_tfs_parse_read_message(t);\n        case NGX_HTTP_TFS_STATE_REMOVE_DELETE_DATA:\n            return ngx_http_tfs_parse_remove_message(t);\n        default:\n            return NGX_ERROR;\n        }\n    }\n\n    return NGX_ERROR;\n}\n\n\nstatic ngx_chain_t *\nngx_http_tfs_create_createfile_message(ngx_http_tfs_t *t,\n    ngx_http_tfs_segment_data_t *segment_data)\n{\n    size_t                         size;\n    ngx_buf_t                     *b;\n    ngx_chain_t                   *cl;\n    ngx_http_tfs_ds_msg_header_t  *req;\n\n    size = sizeof(ngx_http_tfs_ds_msg_header_t);\n\n    b = ngx_create_temp_buf(t->pool, size);\n    if (b == NULL) {\n        return NULL;\n    }\n\n    req = (ngx_http_tfs_ds_msg_header_t *) b->pos;\n\n    req->base_header.type = NGX_HTTP_TFS_CREATE_FILENAME_MESSAGE;\n    req->base_header.len = size - sizeof(ngx_http_tfs_header_t);\n    req->base_header.flag = NGX_HTTP_TFS_PACKET_FLAG;\n    req->base_header.version = NGX_HTTP_TFS_PACKET_VERSION;\n    req->base_header.id = ngx_http_tfs_generate_packet_id();\n    req->block_id = segment_data->segment_info.block_id;\n    req->file_id = segment_data->segment_info.file_id;\n\n    req->base_header.crc = ngx_http_tfs_crc(NGX_HTTP_TFS_PACKET_FLAG,\n                                         (const char *) (&req->base_header + 1),\n                                         req->base_header.len);\n\n    b->last += size;\n\n    cl = ngx_alloc_chain_link(t->pool);\n    if (cl == NULL) {\n        return NULL;\n    }\n\n    cl->buf = b;\n    cl->next = NULL;\n    return cl;\n}\n\n\nstatic ngx_chain_t *\nngx_http_tfs_create_write_message(ngx_http_tfs_t *t,\n    ngx_http_tfs_segment_data_t *segment_data)\n{\n    u_char                           *p, exit;\n    size_t                            size, body_size, b_size;\n    uint32_t                          crc;\n    ngx_int_t                         rc;\n    ngx_buf_t                        *b;\n    ngx_uint_t                        i;\n    ngx_chain_t                      *cl, *body, *ch;\n    ngx_http_tfs_crc_t                t_crc;\n    ngx_http_tfs_block_info_t        *block_info;\n    ngx_http_tfs_ds_write_request_t  *req;\n\n    exit = 0;\n\n    block_info = &segment_data->block_info;\n    size = sizeof(ngx_http_tfs_ds_write_request_t) +\n        /* ds count */\n        sizeof(uint32_t) +\n        /* ds list */\n        block_info->ds_count * sizeof(uint64_t) +\n        /* flag verion lease_id */\n        sizeof(uint64_t) * 3 ;\n\n    b = ngx_create_temp_buf(t->pool, size);\n    if (b == NULL) {\n        return NULL;\n    }\n\n    req = (ngx_http_tfs_ds_write_request_t *) b->pos;\n\n    req->header.base_header.type = NGX_HTTP_TFS_WRITE_DATA_MESSAGE;\n    req->header.base_header.flag = NGX_HTTP_TFS_PACKET_FLAG;\n    req->header.base_header.version = NGX_HTTP_TFS_PACKET_VERSION;\n    req->header.base_header.id = ngx_http_tfs_generate_packet_id();\n    req->header.block_id = segment_data->segment_info.block_id;\n    req->header.file_id = segment_data->segment_info.file_id;\n    req->offset = segment_data->oper_offset;\n    req->is_server = 0;\n    req->file_number = segment_data->write_file_number;\n\n    p = b->pos + sizeof(ngx_http_tfs_ds_write_request_t);\n\n    /* ds count */\n    *((uint32_t *) p) = 3 + block_info->ds_count;\n    p += sizeof(uint32_t);\n    /* ds list */\n    for (i = 0; i < block_info->ds_count; i++) {\n        *((uint64_t *) p) = *((uint64_t *)&block_info->ds_addrs[i]);\n        p += sizeof(uint64_t);\n    }\n\n    /* flag, useless */\n    *((uint64_t *) p) = -1;\n    p += sizeof(uint64_t);\n    /* version */\n    *((uint64_t *) p) = block_info->version;\n    p += sizeof(uint64_t);\n    /* lease id */\n    *((uint64_t *) p) = block_info->lease_id;\n    b->last += size;\n\n    req->length = segment_data->oper_size;\n\n    crc = ngx_http_tfs_crc(NGX_HTTP_TFS_PACKET_FLAG,\n                           (const char *) (&req->header.base_header + 1),\n                           (size - sizeof(ngx_http_tfs_header_t)));\n\n    cl = ngx_alloc_chain_link(t->pool);\n    if (cl == NULL) {\n        return NULL;\n    }\n    ch = cl;\n    cl->buf = b;\n\n    body_size = 0;\n    body = segment_data->data;\n\n    t_crc.crc = crc;\n    t_crc.data_crc = segment_data->segment_info.crc;\n\n    /* body buf is one or two bufs,\n     * please see ngx_http_read_client_request_body\n     */\n    while (body) {\n        b_size = ngx_buf_size(body->buf);\n        body_size += b_size;\n\n        b = ngx_alloc_buf(t->pool);\n        if (b == NULL) {\n            return NULL;\n        }\n\n        ngx_memcpy(b, body->buf, sizeof(ngx_buf_t));\n\n        if (body_size > NGX_HTTP_TFS_MAX_FRAGMENT_SIZE) {\n            /* need more writes*/\n            body_size -= b_size;\n            b_size = NGX_HTTP_TFS_MAX_FRAGMENT_SIZE - body_size;\n            body_size = NGX_HTTP_TFS_MAX_FRAGMENT_SIZE;\n            exit = 1;\n        }\n\n        rc = ngx_http_tfs_compute_buf_crc(&t_crc, b, b_size, t->log);\n        if (rc == NGX_ERROR) {\n            return NULL;\n        }\n\n        cl->next = ngx_alloc_chain_link(t->pool);\n        if (cl->next == NULL) {\n            return NULL;\n        }\n\n        cl = cl->next;\n        cl->buf = b;\n\n        if (exit) {\n            break;\n        }\n\n        body = body->next;\n    }\n    cl->next = NULL;\n\n    ngx_log_error(NGX_LOG_INFO, t->log, 0,\n                  \"write segment index %uD, block id: %uD, file id: %uL, \"\n                  \"offset: %D, length: %uD, crc: %uD\",\n                  t->file.segment_index, segment_data->segment_info.block_id,\n                  segment_data->segment_info.file_id, req->offset,\n                  req->length, t_crc.data_crc);\n\n    segment_data->segment_info.crc = t_crc.data_crc;\n    req->header.base_header.len = size - sizeof(ngx_http_tfs_header_t)\n                                   + req->length;\n    req->header.base_header.crc = t_crc.crc;\n\n    return ch;\n}\n\n\nstatic ngx_chain_t *\nngx_http_tfs_create_closefile_message(ngx_http_tfs_t *t,\n    ngx_http_tfs_segment_data_t *segment_data)\n{\n    u_char                           *p;\n    size_t                            size;\n    ngx_buf_t                        *b;\n    ngx_uint_t                        i;\n    ngx_chain_t                      *cl;\n    ngx_http_tfs_block_info_t        *block_info;\n    ngx_http_tfs_ds_close_request_t  *req;\n\n    block_info = &segment_data->block_info;\n    size = sizeof(ngx_http_tfs_ds_close_request_t) +\n        /* ds count */\n        sizeof(uint32_t) +\n        /* ds list */\n        block_info->ds_count * sizeof(uint64_t) +\n        /* flag verion lease_id */\n        sizeof(uint64_t) * 3 +\n        /* size and file size */\n        sizeof(uint32_t) * 2 +\n        /* option flag */\n        sizeof(uint32_t);\n\n    b = ngx_create_temp_buf(t->pool, size);\n    if (b == NULL) {\n        return NULL;\n    }\n\n    req = (ngx_http_tfs_ds_close_request_t *) b->pos;\n\n    req->header.base_header.type = NGX_HTTP_TFS_CLOSE_FILE_MESSAGE;\n    req->header.base_header.flag = NGX_HTTP_TFS_PACKET_FLAG;\n    req->header.base_header.version = NGX_HTTP_TFS_PACKET_VERSION;\n    req->header.base_header.id = ngx_http_tfs_generate_packet_id();\n    req->header.base_header.len = size - sizeof(ngx_http_tfs_header_t);\n    req->header.block_id = segment_data->segment_info.block_id;\n    req->header.file_id = segment_data->segment_info.file_id;\n    req->mode = NGX_HTTP_TFS_CLOSE_FILE_MASTER;\n    req->crc = segment_data->segment_info.crc;\n    req->file_number = segment_data->write_file_number;\n\n    p = b->pos + sizeof(ngx_http_tfs_ds_close_request_t);\n\n    /* ds count */\n    *((uint32_t *) p) = 3 + block_info->ds_count;\n    p += sizeof(uint32_t);\n    /* ds list */\n    for (i = 0; i < block_info->ds_count; i++) {\n        *((uint64_t *) p) = *((uint64_t *)&block_info->ds_addrs[i]);\n        p += sizeof(uint64_t);\n    }\n\n    /* flag, useless */\n    *((uint64_t *) p) = -1;\n    p += sizeof(uint64_t);\n    /* version */\n    *((uint64_t *) p) = block_info->version;\n    p += sizeof(uint64_t);\n    /* lease id */\n    *((uint64_t *) p) = block_info->lease_id;\n    p += sizeof(uint64_t);\n\n    /* block size, useless */\n    *((uint32_t *) p) = 0;\n    p += sizeof(uint32_t);\n    /* file size, useless */\n    *((uint32_t *) p) = 0;\n    p += sizeof(uint32_t);\n\n    *((uint32_t *) p) = NGX_HTTP_TFS_FILE_DEFAULT_OPTION;\n\n    req->header.base_header.crc = ngx_http_tfs_crc(NGX_HTTP_TFS_PACKET_FLAG,\n                                  (const char *) (&req->header.base_header + 1),\n                                  (size - sizeof(ngx_http_tfs_header_t)));\n\n    b->last += size;\n\n    cl = ngx_alloc_chain_link(t->pool);\n    if (cl == NULL) {\n        return NULL;\n    }\n    cl->next = NULL;\n    cl->buf = b;\n    return cl;\n}\n\n\nstatic ngx_chain_t *\nngx_http_tfs_create_read_message(ngx_http_tfs_t *t,\n    ngx_http_tfs_segment_data_t *segment_data, uint8_t read_ver,\n    uint8_t read_flag)\n{\n    size_t                           size;\n    ngx_buf_t                       *b;\n    ngx_chain_t                     *cl;\n    ngx_http_tfs_ds_read_request_t  *req;\n\n    size = sizeof(ngx_http_tfs_ds_read_request_t);\n\n    b = ngx_create_temp_buf(t->pool, size);\n    if (b == NULL) {\n        return NULL;\n    }\n\n    req = (ngx_http_tfs_ds_read_request_t *) b->pos;\n\n    if (read_ver == NGX_HTTP_TFS_READ) {\n        req->header.base_header.type = NGX_HTTP_TFS_READ_DATA_MESSAGE;\n\n    } else if (read_ver == NGX_HTTP_TFS_READ_V2) {\n        req->header.base_header.type = NGX_HTTP_TFS_READ_DATA_MESSAGE_V2;\n    }\n    req->header.base_header.flag = NGX_HTTP_TFS_PACKET_FLAG;\n    req->header.base_header.version = NGX_HTTP_TFS_PACKET_VERSION;\n    req->header.base_header.id = ngx_http_tfs_generate_packet_id();\n    req->header.base_header.len = size - sizeof(ngx_http_tfs_header_t);\n    req->header.block_id = segment_data->segment_info.block_id;\n    req->header.file_id = segment_data->segment_info.file_id;\n    req->offset = segment_data->oper_offset;\n    req->length = segment_data->oper_size;\n    req->flag = read_flag;\n    req->header.base_header.crc = ngx_http_tfs_crc(NGX_HTTP_TFS_PACKET_FLAG,\n                                  (const char *) (&req->header.base_header + 1),\n                                  (size - sizeof(ngx_http_tfs_header_t)));\n\n    b->last += size;\n\n    ngx_log_error(NGX_LOG_INFO, t->log, 0,\n                  \"read segment index %uD, block id: %uD, \"\n                  \"file id: %uL, offset: %D, length: %uD\",\n                  t->file.segment_index,\n                  segment_data->segment_info.block_id,\n                  segment_data->segment_info.file_id, req->offset,\n                  req->length);\n\n    cl = ngx_alloc_chain_link(t->pool);\n    if (cl == NULL) {\n        return NULL;\n    }\n    cl->buf = b;\n    cl->next = NULL;\n\n    return cl;\n}\n\n\nstatic ngx_chain_t *\nngx_http_tfs_create_unlink_message(ngx_http_tfs_t *t,\n    ngx_http_tfs_segment_data_t *segment_data)\n{\n    u_char                            *p;\n    size_t                             size;\n    ngx_buf_t                         *b;\n    ngx_uint_t                         i;\n    ngx_chain_t                       *cl;\n    ngx_http_tfs_block_info_t         *block_info;\n    ngx_http_tfs_ds_unlink_request_t  *req;\n\n    block_info = &segment_data->block_info;\n    size = sizeof(ngx_http_tfs_ds_unlink_request_t) +\n        /* ds count */\n        sizeof(uint32_t) +\n        /* ds list */\n        block_info->ds_count * sizeof(uint64_t) +\n        /* flag verion lease_id */\n        sizeof(uint64_t) * 3  +\n        /* option flag */\n        sizeof(uint32_t);\n\n    b = ngx_create_temp_buf(t->pool, size);\n    if (b == NULL) {\n        return NULL;\n    }\n\n    req = (ngx_http_tfs_ds_unlink_request_t *) b->pos;\n\n    req->header.base_header.type = NGX_HTTP_TFS_UNLINK_FILE_MESSAGE;\n    req->header.base_header.flag = NGX_HTTP_TFS_PACKET_FLAG;\n    req->header.base_header.version = NGX_HTTP_TFS_PACKET_VERSION;\n    req->header.base_header.id = ngx_http_tfs_generate_packet_id();\n    req->header.base_header.len = size - sizeof(ngx_http_tfs_header_t);\n    req->header.block_id = segment_data->segment_info.block_id;\n    req->header.file_id = segment_data->segment_info.file_id;\n    req->server_mode = NGX_HTTP_TFS_REMOVE_FILE_MASTER;\n    if (t->r_ctx.version == 1) {\n        req->server_mode |= t->r_ctx.unlink_type;\n\n    } else if (t->r_ctx.version == 2) {\n        req->server_mode |= NGX_HTTP_TFS_UNLINK_DELETE;\n    }\n\n    p = b->pos + sizeof(ngx_http_tfs_ds_unlink_request_t);\n\n    /* ds count */\n    *((uint32_t *) p) = 3 + block_info->ds_count;\n    p += sizeof(uint32_t);\n    /* ds list */\n    for (i = 0; i < block_info->ds_count; i++) {\n        *((uint64_t *) p) = *((uint64_t *)&block_info->ds_addrs[i]);\n        p += sizeof(uint64_t);\n    }\n\n    /* flag, useless */\n    *((uint64_t *) p) = -1;\n    p += sizeof(uint64_t);\n    /* version */\n    *((uint64_t *) p) = block_info->version;\n    p += sizeof(uint64_t);\n    /* lease id */\n    *((uint64_t *) p) = block_info->lease_id;\n    p += sizeof(uint64_t);\n\n    /* option */\n    *((uint32_t *) p) = NGX_HTTP_TFS_FILE_DEFAULT_OPTION;\n\n    req->header.base_header.crc = ngx_http_tfs_crc(NGX_HTTP_TFS_PACKET_FLAG,\n                                  (const char *) (&req->header.base_header + 1),\n                                  (size - sizeof(ngx_http_tfs_header_t)));\n\n    b->last += size;\n\n    ngx_log_error(NGX_LOG_INFO, t->log, 0,\n                  \"unlink segment index %uD, block id: %uD, \"\n                  \"file id: %uL, type: %i\",\n                  t->file.segment_index,\n                  segment_data->segment_info.block_id,\n                  segment_data->segment_info.file_id, t->r_ctx.unlink_type);\n\n    cl = ngx_alloc_chain_link(t->pool);\n    if (cl == NULL) {\n        return NULL;\n    }\n    cl->next = NULL;\n    cl->buf = b;\n    return cl;\n}\n\n\nstatic ngx_chain_t *\nngx_http_tfs_create_stat_message(ngx_http_tfs_t *t,\n    ngx_http_tfs_segment_data_t *segment_data)\n{\n    size_t                           size;\n    ngx_buf_t                       *b;\n    ngx_chain_t                     *cl;\n    ngx_http_tfs_ds_stat_request_t  *req;\n\n    size = sizeof(ngx_http_tfs_ds_stat_request_t);\n\n    b = ngx_create_temp_buf(t->pool, size);\n    if (b == NULL) {\n        return NULL;\n    }\n\n    req = (ngx_http_tfs_ds_stat_request_t *) b->pos;\n\n    req->header.base_header.type = NGX_HTTP_TFS_FILE_INFO_MESSAGE;\n    req->header.base_header.flag = NGX_HTTP_TFS_PACKET_FLAG;\n    req->header.base_header.version = NGX_HTTP_TFS_PACKET_VERSION;\n    req->header.base_header.id = ngx_http_tfs_generate_packet_id();\n    req->header.base_header.len = size - sizeof(ngx_http_tfs_header_t);\n    req->header.block_id = segment_data->segment_info.block_id;\n    req->header.file_id = segment_data->segment_info.file_id;\n    req->mode = t->r_ctx.read_stat_type;\n\n    req->header.base_header.crc = ngx_http_tfs_crc(NGX_HTTP_TFS_PACKET_FLAG,\n                                  (const char *) (&req->header.base_header + 1),\n                                  (size - sizeof(ngx_http_tfs_header_t)));\n\n    b->last += size;\n\n    cl = ngx_alloc_chain_link(t->pool);\n    if (cl == NULL) {\n        return NULL;\n    }\n    cl->buf = b;\n    cl->next = NULL;\n\n    return cl;\n}\n\n\nstatic ngx_int_t\nngx_http_tfs_parse_createfile_message(ngx_http_tfs_t *t,\n    ngx_http_tfs_segment_data_t *segment_data)\n{\n    uint16_t                         type;\n    ngx_str_t                        action;\n    ngx_http_tfs_header_t           *header;\n    ngx_http_tfs_ds_cf_reponse_t    *resp;\n    ngx_http_tfs_peer_connection_t  *tp;\n\n    header = (ngx_http_tfs_header_t *) t->header;\n    tp = t->tfs_peer;\n    type = header->type;\n\n    switch (type) {\n\n    case NGX_HTTP_TFS_STATUS_MESSAGE:\n        ngx_str_set(&action, \"create file(data server)\");\n        return ngx_http_tfs_status_message(&tp->body_buffer, &action, t->log);\n    }\n\n    resp = (ngx_http_tfs_ds_cf_reponse_t *) tp->body_buffer.pos;\n\n    t->r_ctx.fsname.file.seq_id = resp->file_id;\n    ngx_http_tfs_raw_fsname_set_suffix((&t->r_ctx.fsname),\n                                       (&t->r_ctx.file_suffix));\n    segment_data->segment_info.file_id =\n                          ngx_http_tfs_raw_fsname_get_file_id(t->r_ctx.fsname);\n    segment_data->write_file_number = resp->file_number;\n\n    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, t->log, 0,\n                   \"create file success, seq id: %uD, \"\n                   \"file id: %uL, file number: %uL\",\n                   t->r_ctx.fsname.file.seq_id,\n                   segment_data->segment_info.file_id,\n                   segment_data->write_file_number);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_tfs_parse_write_message(ngx_http_tfs_t *t)\n{\n    uint16_t                type;\n    ngx_str_t               action;\n    ngx_http_tfs_header_t  *header;\n\n    header = (ngx_http_tfs_header_t *) t->header;\n    type = header->type;\n\n    switch (type) {\n\n    case NGX_HTTP_TFS_STATUS_MESSAGE:\n        ngx_str_set(&action, \"write data(data server)\");\n        return ngx_http_tfs_status_message(&t->tfs_peer->body_buffer, &action,\n                                           t->log);\n    default:\n        ngx_log_error(NGX_LOG_INFO, t->log, 0,\n                      \"write file(ds) response msg type is invalid %d \", type);\n    }\n\n    return NGX_ERROR;\n}\n\n\nstatic ngx_int_t\nngx_http_tfs_parse_closefile_message(ngx_http_tfs_t *t)\n{\n    uint16_t                type;\n    ngx_str_t               action;\n    ngx_http_tfs_header_t  *header;\n\n    header = (ngx_http_tfs_header_t *) t->header;\n    type = header->type;\n\n    switch (type) {\n\n    case NGX_HTTP_TFS_STATUS_MESSAGE:\n        ngx_str_set(&action, \"close file(data server)\");\n        return ngx_http_tfs_status_message(&t->tfs_peer->body_buffer, &action,\n                                           t->log);\n\n    default:\n        ngx_log_error(NGX_LOG_INFO, t->log, 0,\n                      \"close file response msg type is invalid  %d \", type);\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_tfs_parse_read_message(ngx_http_tfs_t *t)\n{\n    size_t                                   size, left_len, tail_len;\n    int32_t                                  code, err_len;\n    uint16_t                                 type;\n    ngx_int_t                                rc;\n    ngx_str_t                                err_msg;\n    ngx_buf_t                               *b;\n    ngx_http_tfs_peer_connection_t          *tp;\n    ngx_http_tfs_ds_read_response_t         *resp;\n    ngx_http_tfs_ds_readv2_response_tail_t  *readv2_rsp_tail;\n\n    resp = (ngx_http_tfs_ds_read_response_t *) t->header;\n    tp = t->tfs_peer;\n    type = resp->header.type;\n    b = &tp->body_buffer;\n\n    switch (type) {\n    case NGX_HTTP_TFS_STATUS_MESSAGE:\n        /* weird status message, err_code is in resp->data_len */\n        code = resp->data_len;\n        if (code != NGX_HTTP_TFS_STATUS_MESSAGE_OK) {\n            err_len = *(uint32_t*) (b->pos);\n            if (err_len > 0) {\n                err_msg.data = b->pos + sizeof(uint32_t);\n                err_msg.len = err_len;\n            }\n\n            ngx_log_error(NGX_LOG_ERR, t->log, 0,\n                          \"read data (data server: %s) failed, \"\n                          \"error code (%d) err_msg(%V)\",\n                          tp->peer_addr_text, code, &err_msg);\n        }\n\n        return NGX_HTTP_TFS_AGAIN;\n    }\n\n    size = ngx_buf_size(b);\n\n    /* read v2 */\n    if (t->read_ver == NGX_HTTP_TFS_READ_V2) {\n        /* recv file_info */\n        if (t->length < 0\n            || (size_t) t->length <= NGX_HTTP_TFS_READ_V2_TAIL_LEN)\n        {\n            t->length -= size;\n            if (t->length == 0) {\n                t->readv2_rsp_tail_buf->last =\n                         ngx_cpymem(t->readv2_rsp_tail_buf->last, b->pos, size);\n                readv2_rsp_tail = (ngx_http_tfs_ds_readv2_response_tail_t *)\n                                   t->readv2_rsp_tail_buf->pos;\n                if (readv2_rsp_tail->file_info_len\n                    != NGX_HTTP_TFS_RAW_FILE_INFO_SIZE)\n                {\n                    return NGX_ERROR;\n                }\n                ngx_http_tfs_wrap_raw_file_info(&readv2_rsp_tail->file_info, &t->file_stat);\n                t->file.left_length = ngx_min(t->file.left_length, (uint64_t)t->file_stat.size);\n            }\n            return NGX_OK;\n        }\n\n        /* recv data only or data + file_info */\n        left_len = t->length - size;\n        if (left_len < NGX_HTTP_TFS_READ_V2_TAIL_LEN) {\n            tail_len = NGX_HTTP_TFS_READ_V2_TAIL_LEN - left_len;\n            size -= tail_len;\n            t->length -= tail_len;\n            /* all recvd */\n            if (left_len == 0) {\n                readv2_rsp_tail = (ngx_http_tfs_ds_readv2_response_tail_t *)\n                                   (b->pos + size);\n                /* should not happened */\n                if (readv2_rsp_tail->file_info_len\n                    != NGX_HTTP_TFS_RAW_FILE_INFO_SIZE)\n                {\n                    return NGX_ERROR;\n                }\n                ngx_http_tfs_wrap_raw_file_info(&readv2_rsp_tail->file_info, &t->file_stat);\n                t->file.left_length = ngx_min(t->file.left_length, (uint64_t)t->file_stat.size);\n\n            /* all data and partial file_info recvd */\n            } else if (left_len > 0) {\n                t->readv2_rsp_tail_buf = ngx_create_temp_buf(t->pool,\n                                                 NGX_HTTP_TFS_READ_V2_TAIL_LEN);\n                if (t->readv2_rsp_tail_buf == NULL) {\n                    return NGX_ERROR;\n                }\n                t->readv2_rsp_tail_buf->last =\n                                        ngx_cpymem(t->readv2_rsp_tail_buf->last,\n                                                   b->pos + size, tail_len);\n\n            } else {\n                return NGX_ERROR;\n            }\n\n        /* only data recvd */\n        } else if (left_len == NGX_HTTP_TFS_READ_V2_TAIL_LEN) {\n            t->readv2_rsp_tail_buf = ngx_create_temp_buf(t->pool,\n                                                 NGX_HTTP_TFS_READ_V2_TAIL_LEN);\n            if (t->readv2_rsp_tail_buf == NULL) {\n                return NGX_ERROR;\n            }\n        }\n    }\n\n    if ((!t->is_large_file && !t->is_stat_dup_file )\n        || (t->is_large_file && !t->is_process_meta_seg))\n    {\n        rc = ngx_http_tfs_copy_body_buffer(t, size, b->pos);\n        if (rc == NGX_ERROR) {\n            return rc;\n        }\n    }\n\n    t->stat_info.size += size;\n    t->length -= size;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_tfs_parse_remove_message(ngx_http_tfs_t *t)\n{\n    int32_t                     code, err_len;\n    uint16_t                    type;\n    uint64_t                    file_size;\n    ngx_str_t                   err;\n    ngx_int_t                   rc;\n    ngx_http_tfs_header_t      *header;\n    ngx_http_tfs_status_msg_t  *resp;\n\n    header = (ngx_http_tfs_header_t *) t->header;\n    type = header->type;\n\n    switch (type) {\n    case NGX_HTTP_TFS_STATUS_MESSAGE:\n        resp = (ngx_http_tfs_status_msg_t *) t->tfs_peer->body_buffer.pos;\n        err.len = 0;\n        code = resp->code;\n\n        if (code != NGX_HTTP_TFS_STATUS_MESSAGE_OK) {\n            err_len = resp->error_len;\n            if (err_len > 0) {\n                err.data = resp->error_str;\n                err.len = err_len;\n            }\n\n            ngx_log_error(NGX_LOG_ERR, t->log, 0,\n                          \"remove_file failed, error code (%d) err_msg(%V)\",\n                          code, &err);\n            if (code <= NGX_HTTP_TFS_EXIT_GENERAL_ERROR) {\n                return code;\n            }\n\n            return NGX_HTTP_TFS_EXIT_GENERAL_ERROR;\n        }\n\n        /* on success, return is remove file's size */\n        err_len = resp->error_len;\n        file_size = 0;\n        if (err_len > 1) {\n            rc = ngx_http_tfs_atoull(resp->error_str,\n                                     err_len - 1,\n                                     (unsigned long long *) &file_size);\n            if (rc == NGX_ERROR) {\n                return NGX_ERROR;\n            }\n            t->stat_info.size += file_size;\n        }\n\n        ngx_log_error(NGX_LOG_INFO, t->log, 0,\n                      \"remove_file success, file_size: %uL \",\n                      file_size);\n\n        return NGX_OK;\n    default:\n        ngx_log_error(NGX_LOG_INFO, t->log, 0,\n                      \"remove file(ds) response msg type is invalid %d \", type);\n    }\n\n    return NGX_ERROR;\n}\n\n\nstatic ngx_int_t\nngx_http_tfs_parse_statfile_message(ngx_http_tfs_t *t,\n    ngx_http_tfs_segment_data_t *segment_data)\n{\n    uint16_t                               type;\n    ngx_int_t                              rc;\n    ngx_str_t                              action;\n    ngx_http_tfs_header_t                 *header;\n    ngx_http_tfs_peer_connection_t        *tp;\n    ngx_http_tfs_ds_stat_response_t       *resp;\n    ngx_http_tfs_ds_sp_readv2_response_t  *resp2;\n\n\n    header = (ngx_http_tfs_header_t *) t->header;\n    tp = t->tfs_peer;\n    type = header->type;\n\n    switch (type) {\n    case NGX_HTTP_TFS_STATUS_MESSAGE:\n        ngx_str_set(&action, \"stat file(data server)\");\n        rc = ngx_http_tfs_status_message(&tp->body_buffer, &action, t->log);\n        return rc;\n    }\n\n    if (!t->is_large_file) {\n        resp = (ngx_http_tfs_ds_stat_response_t *) tp->body_buffer.pos;\n        if (resp->data_len <= 0) {\n            return NGX_HTTP_TFS_EXIT_GENERAL_ERROR;\n        }\n        ngx_http_tfs_wrap_raw_file_info(&resp->file_info, &t->file_stat);\n\n    } else {\n        resp2 = (ngx_http_tfs_ds_sp_readv2_response_t *) tp->body_buffer.pos;\n        if (resp2->data_len == NGX_HTTP_TFS_EXIT_NO_LOGICBLOCK_ERROR) {\n            ngx_http_tfs_remove_block_cache(t, segment_data);\n            return NGX_HTTP_TFS_AGAIN;\n        }\n\n        /* file deleted */\n        if (resp2->data_len == NGX_HTTP_TFS_EXIT_FILE_INFO_ERROR) {\n            t->file_stat.id =\n                     ngx_http_tfs_raw_fsname_get_file_id(t->r_ctx.fsname);\n            t->file_stat.offset = -1;\n            t->file_stat.size = -1;\n            t->file_stat.u_size = -1;\n            t->file_stat.modify_time = -1;\n            t->file_stat.create_time = -1;\n            t->file_stat.flag = NGX_HTTP_TFS_FILE_DELETED;\n            t->file_stat.crc = 0;\n\n        } else {\n            if (resp2->data_len != sizeof(ngx_http_tfs_segment_head_t)\n                || resp2->file_info_len <= 0)\n            {\n                return NGX_HTTP_TFS_EXIT_GENERAL_ERROR;\n            }\n            t->file_stat.size = resp2->seg_head.size;\n            t->file_stat.u_size =\n                                 resp2->file_info.u_size + resp2->seg_head.size;\n        }\n\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_tfs_get_meta_segment(ngx_http_tfs_t *t)\n{\n    ngx_http_tfs_segment_info_t  *segment_info;\n\n    t->file.segment_count = 1;\n\n    if (t->file.segment_data == NULL) {\n        t->file.segment_data = ngx_pcalloc(t->pool,\n                                           sizeof(ngx_http_tfs_segment_data_t));\n        if (t->file.segment_data == NULL) {\n            return NGX_ERROR;\n        }\n    }\n\n    segment_info = &t->file.segment_data[0].segment_info;\n    segment_info->block_id = t->r_ctx.fsname.file.block_id;\n    segment_info->file_id =\n                           ngx_http_tfs_raw_fsname_get_file_id(t->r_ctx.fsname);\n    segment_info->offset = 0;\n    segment_info->size = 0;\n\n    ngx_log_error(NGX_LOG_INFO, t->log, 0,\n                  \"meta segment: block_id: %uD, fileid: %uL, \"\n                  \"seq_id: %uD, suffix: %uD\",\n                  segment_info->block_id,\n                  segment_info->file_id,\n                  t->r_ctx.fsname.file.seq_id,\n                  t->r_ctx.fsname.file.suffix);\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_tfs_set_meta_segment_data(ngx_http_tfs_t *t)\n{\n    uint32_t                      i, segment_count;\n    uint64_t                      size;\n    ngx_int_t                     rc;\n    ngx_buf_t                    *b;\n    ngx_chain_t                  *cl;\n    ngx_http_tfs_segment_info_t  *seg_info;\n    ngx_http_tfs_segment_data_t  *segment_data;\n\n    segment_count = t->file.segment_count;\n    /* prepare meta segment's data */\n    size = sizeof(ngx_http_tfs_segment_head_t) +\n        segment_count * sizeof(ngx_http_tfs_segment_info_t);\n    b = ngx_create_temp_buf(t->pool, size);\n    if (b == NULL) {\n        return NGX_ERROR;\n    }\n    t->seg_head = (ngx_http_tfs_segment_head_t*)b->pos;\n    t->seg_head->count = segment_count;\n    t->seg_head->size = t->r_ctx.size;\n    seg_info = (ngx_http_tfs_segment_info_t *)\n                (b->pos + sizeof(ngx_http_tfs_segment_head_t));\n    for (i = 0; i < segment_count; i++) {\n        *seg_info = t->file.segment_data[i].segment_info;\n        seg_info++;\n    }\n    b->last += size;\n    cl = ngx_alloc_chain_link(t->pool);\n    if (cl == NULL) {\n        return NGX_ERROR;\n    }\n    cl->buf = b;\n    cl->next = NULL;\n    /* put meta segment in the last segment\n       which we pre-alloc in ngx_http_tfs_get_segment_for_write */\n    t->file.segment_count += 1;\n    segment_data = &t->file.segment_data[t->file.segment_index];\n\n    segment_data->data = cl;\n    /* copy data to orig_data so that we can retry write */\n    rc = ngx_chain_add_copy_with_buf(t->pool,\n        &segment_data->orig_data, segment_data->data);\n    if (rc == NGX_ERROR) {\n        return NGX_ERROR;\n    }\n\n    segment_data->oper_size = size;\n    segment_data->segment_info.size = size;\n\n    t->file.left_length = size;\n    t->is_process_meta_seg = NGX_HTTP_TFS_YES;\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_tfs_parse_meta_segment(ngx_http_tfs_t *t, ngx_chain_t *data)\n{\n    ssize_t                           n;\n    uint64_t                          data_size;\n    uint32_t                          i, segment_count;\n    ngx_buf_t                        *b, *tmp_b;\n    ngx_int_t                         rc;\n    ngx_str_t                         tfs_name;\n    ngx_chain_t                      *body, *cl;\n    ngx_http_tfs_raw_fsname_t         fsname;\n    ngx_http_tfs_segment_head_t      *seg_head;\n    ngx_http_tfs_segment_info_t      *seg_info;\n    ngx_http_tfs_tmp_segment_info_t  *tmp_seg_info;\n\n    if (data == NULL || t->meta_segment_data != NULL) {\n        return NGX_ERROR;\n    }\n\n    /* maybe in two bufs, make it in a continuous buf */\n    data_size = ngx_http_tfs_get_chain_buf_size(data);\n    tmp_b = ngx_create_temp_buf(t->pool, data_size);\n    if (tmp_b == NULL) {\n        return NGX_ERROR;\n    }\n    body = data;\n    while (body) {\n        data_size = ngx_buf_size(body->buf);\n        if (ngx_buf_in_memory(body->buf)) {\n            tmp_b->last = ngx_cpymem(tmp_b->last, body->buf->pos, data_size);\n\n        } else {\n            /* read data from file */\n            n = ngx_read_file(body->buf->file, tmp_b->last, (size_t) data_size,\n                              body->buf->file_pos);\n            if (n != (ssize_t)data_size) {\n                ngx_log_error(NGX_LOG_ERR, t->log, 0,\n                              ngx_read_file_n \" read only \"\n                              \"%z of %uL from \\\"%s\\\"\",\n                              n, data_size, body->buf->file->name.data);\n                return NGX_ERROR;\n            }\n            tmp_b->last += n;\n        }\n        body = body->next;\n    }\n\n    seg_head = (ngx_http_tfs_segment_head_t*)(tmp_b->start);\n    segment_count = seg_head->count;\n    tmp_b->pos += sizeof(ngx_http_tfs_segment_head_t);\n\n    b = ngx_create_temp_buf(t->pool, sizeof(ngx_http_tfs_segment_head_t) +\n                           segment_count * sizeof(ngx_http_tfs_segment_info_t));\n    if (b == NULL) {\n        return NGX_ERROR;\n    }\n    t->seg_head = (ngx_http_tfs_segment_head_t*)b->pos;\n    t->seg_head->count = segment_count;\n    t->seg_head->size = seg_head->size;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, t->log, 0,\n                   \"parse meta segment, segment head: \"\n                   \"segment_count: %uD, size: %uL\",\n                   segment_count, seg_head->size);\n\n    tmp_seg_info = (ngx_http_tfs_tmp_segment_info_t*)(tmp_b->pos);\n    seg_info = (ngx_http_tfs_segment_info_t *)\n                (b->pos + sizeof(ngx_http_tfs_segment_head_t));\n    tfs_name.len = NGX_HTTP_TFS_FILE_NAME_LEN;\n    for (i = 0; i < segment_count; i++) {\n        tfs_name.data = tmp_seg_info->file_name;\n        rc = ngx_http_tfs_raw_fsname_parse(&tfs_name, NULL, &fsname);\n        if (rc != NGX_OK) {\n            return NGX_ERROR;\n        }\n        seg_info->block_id = fsname.file.block_id;\n        seg_info->file_id = ngx_http_tfs_raw_fsname_get_file_id(fsname);\n        seg_info->offset = tmp_seg_info->offset;\n        seg_info->size = tmp_seg_info->size;\n        seg_info->crc = tmp_seg_info->crc;\n        ngx_log_debug6(NGX_LOG_DEBUG_HTTP, t->log, 0,\n                       \"parse meta segment, segment info: file_name: %V,\"\n                       \" block_id: %uD, file_id: %uL, \"\n                       \"offset: %L, size: %D, crc: %uD\",\n                       &tfs_name, seg_info->block_id, seg_info->file_id,\n                       seg_info->offset,\n                       seg_info->size, seg_info->crc);\n        seg_info++;\n        tmp_seg_info++;\n    }\n    b->last += sizeof(ngx_http_tfs_segment_head_t)\n                + segment_count * sizeof(ngx_http_tfs_segment_info_t);\n    cl = ngx_alloc_chain_link(t->pool);\n    if (cl == NULL) {\n        return NGX_ERROR;\n    }\n    cl->buf = b;\n    cl->next = NULL;\n    t->meta_segment_data = cl;\n\n    return NGX_OK;\n}\n\n\n/*\n * We use binary search to find the segment we need\n * if found, return index, or return index to insert.\n */\n\nint32_t\nngx_http_tfs_find_segment(uint32_t seg_count,\n    ngx_http_tfs_segment_info_t *seg_info, int64_t offset)\n{\n    int32_t  start, end, middle;\n\n    start = 0;\n    end = seg_count - 1;\n    middle = (start + end) / 2;\n    while (start <= end) {\n        if (seg_info[middle].offset == offset) {\n            return middle;\n        }\n        if (seg_info[middle].offset < offset) {\n            start = middle + 1;\n\n        } else {\n            end = middle - 1;\n        }\n        middle = (start + end) / 2;\n    }\n    return -start;\n}\n\n\nngx_int_t\nngx_http_tfs_get_segment_for_read(ngx_http_tfs_t *t)\n{\n    uint32_t                      buf_size, seg_count, max_seg_count, i;\n    uint64_t                      start_offset, end_offset, data_size;\n    int32_t                       start_seg, end_seg;\n    ngx_buf_t                    *b;\n    ngx_http_tfs_segment_info_t  *seg_info;\n    ngx_http_tfs_segment_data_t  *first_segment, *last_segment;\n\n    if (t->meta_segment_data == NULL) {\n        return NGX_ERROR;\n    }\n    b = t->meta_segment_data->buf;\n    if (b == NULL) {\n        return NGX_ERROR;\n    }\n\n    buf_size = ngx_buf_size(b);\n    if (buf_size < (sizeof(ngx_http_tfs_segment_head_t) +\n                    sizeof(ngx_http_tfs_segment_info_t)))\n    {\n        return NGX_ERROR;\n    }\n\n    t->seg_head = (ngx_http_tfs_segment_head_t *)(b->pos);\n    seg_info = (ngx_http_tfs_segment_info_t *)\n                (b->pos + sizeof(ngx_http_tfs_segment_head_t));\n\n    if (t->r_ctx.size == NGX_HTTP_TFS_MAX_SIZE) {\n        data_size = t->seg_head->size;\n\n    } else {\n        data_size = t->r_ctx.size;\n    }\n\n    start_offset = t->r_ctx.offset;\n    end_offset = start_offset + data_size;\n    if (start_offset >= t->seg_head->size) {\n        return NGX_DONE;\n    }\n\n    /* find out the segment we should start with */\n    seg_count = t->seg_head->count;\n    max_seg_count = (b->last - (u_char *) seg_info)\n                     / sizeof(ngx_http_tfs_segment_info_t);\n    if (t->seg_head->count > max_seg_count) {\n        ngx_log_error(NGX_LOG_ERR, t->log, 0,\n                      \"seg_count in seg_head larger than max seg_count, \"\n                      \"%uD > %uD, seg_head may be corrupted.\",\n                      t->seg_head->count, max_seg_count);\n        seg_count = max_seg_count - 1;\n    }\n    start_seg = ngx_http_tfs_find_segment(seg_count, seg_info, start_offset);\n    if (start_seg < 0) {\n        start_seg = 0 - start_seg - 1;\n        if (((uint64_t) seg_info[start_seg].offset + seg_info[start_seg].size)\n            <= start_offset)\n        {\n            return NGX_ERROR;\n        }\n    }\n\n    /* find out the last segment */\n    end_seg = ngx_http_tfs_find_segment(seg_count, seg_info, end_offset);\n    if (end_seg > 0) {\n        end_seg -= 1;\n\n    } else if (end_seg < 0) {\n        end_seg = 0 - end_seg - 1;\n\n    } else {\n        return NGX_ERROR;\n    }\n\n    seg_count = end_seg - start_seg + 1;\n\n    /* alloc segment_data */\n    t->file.segment_data = ngx_pcalloc(t->pool,\n                               sizeof(ngx_http_tfs_segment_data_t) * seg_count);\n    if (t->file.segment_data == NULL) {\n        return NGX_ERROR;\n    }\n\n    t->file.segment_index = 0;\n    t->file.segment_count = seg_count;\n    t->file.left_length = data_size;\n\n    for (i = 0; start_seg <= end_seg; i++, start_seg++) {\n        t->file.segment_data[i].segment_info = seg_info[start_seg];\n        t->file.segment_data[i].oper_size =\n                                      t->file.segment_data[i].segment_info.size;\n    }\n\n    /* first segment's oper_offset and oper_size are special for pread */\n    first_segment = &t->file.segment_data[0];\n    first_segment->oper_offset = t->r_ctx.offset;\n    if (first_segment->segment_info.offset > 0) {\n        first_segment->oper_offset -= first_segment->segment_info.offset;\n    }\n    first_segment->oper_size =\n        first_segment->segment_info.size - first_segment->oper_offset;\n\n    /*\n     * last segment's oper_size is special,\n     * notice that last_segment maybe the same as first_semgnt\n     */\n    last_segment = &t->file.segment_data[seg_count - 1];\n    last_segment->oper_size = ngx_min((end_offset\n                                       - (last_segment->segment_info.offset\n                                          + last_segment->oper_offset)),\n                                      last_segment->segment_info.size);\n\n#if (NGX_DEBUG)\n    for (i = 0; i < seg_count; i++) {\n        ngx_log_debug3(NGX_LOG_DEBUG_HTTP, t->log, 0,\n                      \"segment index: %d, oper_offset: %uD, oper_size: %uD\",\n                      i, t->file.segment_data[i].oper_offset,\n                      t->file.segment_data[i].oper_size);\n    }\n#endif\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_tfs_get_segment_for_write(ngx_http_tfs_t *t)\n{\n    size_t        data_size, buf_size, size;\n    int64_t       offset;\n    uint32_t      left_size;\n    ngx_int_t     seg_count, i, rc;\n    ngx_buf_t    *b;\n    ngx_chain_t  *body, *cl, **ll;\n\n    if (t->send_body == NULL) {\n        return NGX_ERROR;\n    }\n\n    body = t->send_body;\n    offset = 0;\n\n    /*\n     * body buf is one or two bufs ,\n     * please see ngx_http_read_client_request_body\n     */\n    data_size = ngx_http_tfs_get_chain_buf_size(body);\n    t->file.left_length = data_size;\n\n    seg_count = (data_size + NGX_HTTP_TFS_MAX_FRAGMENT_SIZE - 1)\n                 / NGX_HTTP_TFS_MAX_FRAGMENT_SIZE;\n    /* alloc one more so we can put large file's meta segment here */\n    size = sizeof(ngx_http_tfs_segment_data_t) * (seg_count + 1);\n\n    if (t->file.segment_data == NULL) {\n        t->file.segment_data = ngx_pcalloc(t->pool, size);\n        if (t->file.segment_data == NULL) {\n            return NGX_ERROR;\n        }\n    }\n\n    t->file.segment_count = seg_count;\n    t->file.segment_index = 0;\n    t->file.last_write_segment_index = 0;\n\n    if (t->is_large_file) {\n        offset = 0;  /* large file do not support pwrite */\n\n    } else if (t->r_ctx.version == 2) {\n        offset = t->r_ctx.offset;\n    }\n\n    for (i = 0; i < seg_count; i++) {\n        t->file.segment_data[i].segment_info.offset = offset;\n        t->file.segment_data[i].segment_info.size =\n            ngx_min(data_size, NGX_HTTP_TFS_MAX_FRAGMENT_SIZE);\n        t->file.segment_data[i].oper_size =\n                                      t->file.segment_data[i].segment_info.size;\n        if (t->is_large_file\n            || (t->r_ctx.version == 2 && offset != NGX_HTTP_TFS_APPEND_OFFSET))\n        {\n            offset += NGX_HTTP_TFS_MAX_FRAGMENT_SIZE;\n        }\n        data_size -= t->file.segment_data[i].segment_info.size;\n\n        /* prepare each segment's data */\n        left_size = t->file.segment_data[i].segment_info.size;\n        ll = &t->file.segment_data[i].data;\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, t->log, 0,\n                      \"prepare segment[%i]'s data\", i);\n\n        while (left_size > 0) {\n            while (body && ngx_buf_size(body->buf) == 0) {\n                ngx_log_debug0(NGX_LOG_DEBUG_HTTP, t->log, 0,\n                              \"zero body buf\");\n                body = body->next;\n            }\n            if (body == NULL) {\n                ngx_log_error(NGX_LOG_ERR, t->log, 0,\n                              \"prepare segment data[%i] failed for early end.\",\n                              i);\n                return NGX_ERROR;\n            }\n            buf_size = ngx_min(ngx_buf_size(body->buf), left_size);\n\n            b = ngx_alloc_buf(t->pool);\n            if (b == NULL) {\n                return NGX_ERROR;\n            }\n            ngx_memcpy(b, body->buf, sizeof(ngx_buf_t));\n            if (ngx_buf_in_memory(b)) {\n                b->last = b->pos + buf_size;\n                ngx_log_debug3(NGX_LOG_DEBUG_HTTP, t->log, 0,\n                               \"pos: %uD, last: %uD, size: %z\",\n                               (b->pos - b->start),\n                               (b->last - b->start),\n                               buf_size);\n\n            } else {\n                b->file_last = b->file_pos + buf_size;\n                ngx_log_debug3(NGX_LOG_DEBUG_HTTP, t->log, 0,\n                               \"pos: %O, last: %O, size: %z\",\n                               b->file_pos, b->file_last, buf_size);\n            }\n\n            cl = ngx_alloc_chain_link(t->pool);\n            if (cl == NULL) {\n                return NGX_ERROR;\n            }\n            cl->buf = b;\n            cl->next = NULL;\n            *ll = cl;\n            ll = &cl->next;\n\n            if (ngx_buf_in_memory(body->buf)) {\n                body->buf->pos += buf_size;\n\n            } else {\n                body->buf->file_pos += buf_size;\n            }\n\n            left_size -= buf_size;\n        }\n        /* copy data to orig_data so that we can retry write */\n        rc = ngx_chain_add_copy_with_buf(t->pool,\n            &t->file.segment_data[i].orig_data, t->file.segment_data[i].data);\n        if (rc == NGX_ERROR) {\n            return NGX_ERROR;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_tfs_get_segment_for_delete(ngx_http_tfs_t *t)\n{\n    uint32_t                      buf_size, seg_count, max_seg_count, i;\n    ngx_buf_t                    *b;\n    ngx_http_tfs_segment_info_t  *seg_info;\n\n    if (t->meta_segment_data == NULL) {\n        return NGX_ERROR;\n    }\n    b = t->meta_segment_data->buf;\n    if (b == NULL) {\n        return NGX_ERROR;\n    }\n\n    buf_size = ngx_buf_size(b);\n    if (buf_size < (sizeof(ngx_http_tfs_segment_head_t) +\n                    sizeof(ngx_http_tfs_segment_info_t)))\n    {\n        return NGX_ERROR;\n    }\n\n    t->seg_head = (ngx_http_tfs_segment_head_t*)(b->pos);\n    seg_info = (ngx_http_tfs_segment_info_t*)\n        (b->pos + sizeof(ngx_http_tfs_segment_head_t));\n\n    /* all data segments plus meta segment */\n    seg_count = t->seg_head->count + 1;\n    max_seg_count = (b->last - (u_char *) seg_info)\n                    / sizeof(ngx_http_tfs_segment_info_t);\n    if (t->seg_head->count > max_seg_count) {\n        ngx_log_error(NGX_LOG_ERR, t->log, 0,\n                      \"seg_count in seg_head larger than max seg_count, \"\n                      \"%uD > %uD, seg_head may be corrupted\",\n                      t->seg_head->count, max_seg_count);\n        seg_count = max_seg_count;\n    }\n\n    t->file.segment_data = ngx_http_tfs_prealloc(t->pool, t->file.segment_data,\n                              sizeof(ngx_http_tfs_segment_data_t),\n                              sizeof(ngx_http_tfs_segment_data_t) * seg_count);\n    if (t->file.segment_data == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_memzero(&t->file.segment_data[1],\n                sizeof(ngx_http_tfs_segment_data_t) * (seg_count - 1));\n\n    t->file.segment_index = 0;\n    t->file.segment_count = seg_count;\n\n    for (i = 1; i < t->file.segment_count; i++) {\n        t->file.segment_data[i].segment_info = seg_info[i-1];\n    }\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_tfs_copy_body_buffer(ngx_http_tfs_t *t, ssize_t bytes, u_char *body)\n{\n    ngx_http_request_t  *r = t->data;\n\n    ngx_chain_t  *cl, **ll;\n\n    for (cl = t->out_bufs, ll = &t->out_bufs; cl; cl = cl->next) {\n        ll = &cl->next;\n    }\n\n    cl = ngx_chain_get_free_buf(r->pool, &t->free_bufs);\n    if (cl == NULL) {\n        return NGX_ERROR;\n    }\n\n    *ll = cl;\n\n    cl->buf->flush = 1;\n    cl->buf->memory = 1;\n\n    cl->buf->pos = body;\n    cl->buf->last = body + bytes;\n    cl->buf->tag = t->output.tag;\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_tfs_fill_file_hole(ngx_http_tfs_t *t, size_t file_hole_size)\n{\n    size_t     size;\n    ngx_int_t  rc;\n    ngx_buf_t  *b, *zero_buf;\n\n    b = &t->tfs_peer_servers[NGX_HTTP_TFS_DATA_SERVER].body_buffer;\n    if (b->start == NULL) {\n        b->start = ngx_palloc(t->pool, NGX_HTTP_TFS_MAX_FRAGMENT_SIZE);\n        if (b->start == NULL) {\n            return NGX_ERROR;\n        }\n\n        b->pos = b->start;\n        b->last = b->start;\n        b->end = b->start + NGX_HTTP_TFS_MAX_FRAGMENT_SIZE;\n        b->temporary = 1;\n    }\n\n    size = b->end - b->last;\n\n    /* file hole can be fill once */\n    if (file_hole_size <= size) {\n        ngx_memzero(b->last, file_hole_size);\n        rc = ngx_http_tfs_copy_body_buffer(t, file_hole_size, b->last);\n        if (rc == NGX_ERROR) {\n            return rc;\n        }\n\n        b->pos += file_hole_size;\n        b->last += file_hole_size;\n\n        ngx_log_error(NGX_LOG_DEBUG, t->log, 0,\n                      \"fill file hole once, size: %uL\", file_hole_size);\n\n    } else {\n        zero_buf = ngx_create_temp_buf(t->pool, NGX_HTTP_TFS_ZERO_BUF_SIZE);\n        if (zero_buf == NULL) {\n            return NGX_ERROR;\n        }\n        ngx_memzero(zero_buf->start, NGX_HTTP_TFS_ZERO_BUF_SIZE);\n\n        while (file_hole_size > 0) {\n            size = ngx_min(NGX_HTTP_TFS_ZERO_BUF_SIZE, file_hole_size);\n            rc = ngx_http_tfs_copy_body_buffer(t, size, zero_buf->pos);\n            if (rc == NGX_ERROR) {\n                return rc;\n            }\n\n            file_hole_size -= size;\n\n            ngx_log_error(NGX_LOG_DEBUG, t->log, 0,\n                          \"fill file hole, size: %z, remain hole size: %uL\",\n                          size, file_hole_size);\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_tfs_check_file_hole(ngx_http_tfs_file_t *file, ngx_array_t *file_holes, ngx_log_t *log)\n{\n    int64_t                         curr_length;\n    uint32_t                        segment_count, i;\n    ngx_http_tfs_segment_data_t    *segment_data;\n    ngx_http_tfs_file_hole_info_t  *file_hole_info;\n\n    if (file == NULL || file_holes == NULL) {\n        return NGX_ERROR;\n    }\n\n    segment_data = file->segment_data;\n    if (segment_data != NULL) {\n        segment_count = file->segment_count;\n        for (i = 0; i < segment_count; i++, segment_data++) {\n            if (file->file_offset < segment_data->segment_info.offset) {\n                curr_length = ngx_min(file->left_length,\n                    (uint64_t)(segment_data->segment_info.offset - file->file_offset));\n                file_hole_info = ngx_array_push(file_holes);\n                if (file_hole_info == NULL) {\n                    return NGX_ERROR;\n                }\n\n                file_hole_info->offset = file->file_offset;\n                file_hole_info->length = curr_length;\n\n                ngx_log_error(NGX_LOG_DEBUG, log, 0,\n                              \"find file hole, offset: %uL, length: %uL\",\n                              file_hole_info->offset, file_hole_info->length);\n\n                file->file_offset += curr_length;\n                file->left_length -= curr_length;\n                if (file->left_length == 0) {\n                    break;\n                }\n            }\n            file->file_offset += segment_data->oper_size;\n            file->left_length -= segment_data->oper_size;\n            if (file->left_length == 0) {\n                break;\n            }\n        }\n    }\n\n    if (!file->still_have) {\n        /* left is all file hole(beyond last segment) */\n        if (file->left_length > 0) {\n            file_hole_info = ngx_array_push(file_holes);\n            if (file_hole_info == NULL) {\n                return NGX_ERROR;\n            }\n\n            file_hole_info->offset = file->file_offset;\n            file_hole_info->length = file->left_length;\n\n            ngx_log_error(NGX_LOG_DEBUG, log, 0,\n                          \"find file hole, offset: %uL, length: %uL\",\n                          file_hole_info->offset, file_hole_info->length);\n            file->file_offset += file->left_length;\n            file->left_length = 0;\n        }\n\n        return NGX_DONE;\n    }\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "modules/ngx_http_tfs_module/ngx_http_tfs_data_server_message.h",
    "content": "\n/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#ifndef _NGX_HTTP_TFS_DATA_SERVER_MESSAGE_H_INCLUDED_\n#define _NGX_HTTP_TFS_DATA_SERVER_MESSAGE_H_INCLUDED_\n\n\n#include <ngx_http_tfs.h>\n\n\nngx_chain_t *ngx_http_tfs_data_server_create_message(ngx_http_tfs_t *t);\nngx_int_t ngx_http_tfs_data_server_parse_message(ngx_http_tfs_t *t);\nngx_http_tfs_inet_t *ngx_http_tfs_select_data_server(ngx_http_tfs_t *t,\n    ngx_http_tfs_segment_data_t *segment_data);\n\nngx_int_t ngx_http_tfs_get_meta_segment(ngx_http_tfs_t *t);\nngx_int_t ngx_http_tfs_set_meta_segment_data(ngx_http_tfs_t *t);\nngx_int_t ngx_http_tfs_parse_meta_segment(ngx_http_tfs_t *t, ngx_chain_t *data);\nngx_int_t ngx_http_tfs_get_segment_for_write(ngx_http_tfs_t *t);\n\nngx_int_t ngx_http_tfs_get_segment_for_read(ngx_http_tfs_t *t);\nngx_int_t ngx_http_tfs_get_segment_for_delete(ngx_http_tfs_t *t);\n\nngx_int_t ngx_http_tfs_fill_file_hole(ngx_http_tfs_t *t, size_t file_hole_size);\nngx_int_t ngx_http_tfs_check_file_hole(ngx_http_tfs_file_t *file,\n    ngx_array_t *file_holes, ngx_log_t *log);\n\n\n#endif  /* _NGX_HTTP_TFS_DATA_SERVER_MESSAGE_H_INCLUDED_ */\n"
  },
  {
    "path": "modules/ngx_http_tfs_module/ngx_http_tfs_duplicate.c",
    "content": "\n/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#include <ngx_md5.h>\n#include <ngx_http_tfs_duplicate.h>\n#include <ngx_http_tfs_data_server_message.h>\n#include <ngx_http_tfs_remote_block_cache.h>\n\n\nstatic void ngx_http_tfs_dedup_get_handler(ngx_http_tair_key_value_t *kv,\n    ngx_int_t rc, void *data);\nstatic void ngx_http_tfs_dedup_set_handler(ngx_int_t rc, void *data);\nstatic void ngx_http_tfs_dedup_remove_handler(ngx_int_t rc, void *data);\nstatic void ngx_http_tfs_dedup_callback(ngx_http_tfs_dedup_ctx_t *ctx,\n    ngx_int_t rc);\n\nngx_int_t ngx_http_tfs_dedup_check_suffix(ngx_str_t *tfs_name,\n    ngx_str_t *suffix);\nngx_int_t ngx_http_tfs_dedup_check_filename(ngx_str_t *dup_name,\n    ngx_http_tfs_raw_fsname_t* fsname);\n\n\nngx_int_t\nngx_http_tfs_dedup_get(ngx_http_tfs_dedup_ctx_t *ctx,\n    ngx_pool_t *pool, ngx_log_t * log)\n{\n    u_char               *p;\n    ssize_t               data_len;\n    ngx_int_t             rc;\n    ngx_http_tair_data_t  tair_key;\n\n    data_len = 0;\n\n    rc = ngx_http_tfs_sum_md5(ctx->file_data, ctx->tair_key, &data_len, log);\n    if (rc == NGX_ERROR) {\n        return NGX_ERROR;\n    }\n\n    p = ctx->tair_key;\n    p += NGX_HTTP_TFS_MD5_RESULT_LEN;\n\n    *(uint32_t *) p = htonl(data_len);\n\n    tair_key.type = NGX_HTTP_TAIR_BYTEARRAY;\n    tair_key.data = ctx->tair_key;\n    tair_key.len = NGX_HTTP_TFS_DUPLICATE_KEY_SIZE;\n\n    ctx->md5_sumed = 1;\n\n    rc = ngx_http_tfs_tair_get_helper(ctx->tair_instance, pool, log,\n                                      &tair_key,\n                                      ngx_http_tfs_dedup_get_handler,\n                                      ctx);\n\n    return rc;\n}\n\n\nstatic void\nngx_http_tfs_dedup_get_handler(ngx_http_tair_key_value_t *kv, ngx_int_t rc,\n    void *data)\n{\n    u_char                    *p;\n    ngx_http_tfs_t            *t;\n    ngx_http_tfs_dedup_ctx_t  *ctx;\n\n    ctx = data;\n    t = ctx->data;\n\n    if (rc == NGX_HTTP_ETAIR_SUCCESS) {\n        p = kv->value->data;\n        if (p != NULL\n            && (kv->value->len > NGX_HTTP_TFS_DUPLICATE_VALUE_BASE_SIZE))\n        {\n            ctx->file_ref_count = *(int32_t *)p;\n            p += sizeof(int32_t);\n            ctx->dup_file_name.len = kv->value->len - sizeof(int32_t);\n            ctx->dup_file_name.data = ngx_pcalloc(t->pool,\n                                                  ctx->dup_file_name.len);\n            if (ctx->dup_file_name.data == NULL) {\n                rc = NGX_ERROR;\n\n            } else {\n                ngx_memcpy(ctx->dup_file_name.data, p, ctx->dup_file_name.len);\n                rc = NGX_OK;\n            }\n            ctx->dup_version = kv->version;\n\n        } else {\n            rc = NGX_ERROR;\n        }\n        ngx_log_debug3(NGX_LOG_DEBUG_HTTP, t->log, 0,\n                       \"get duplicate info: \"\n                       \"file name: %V, file ref count: %d, dup_version: %d\",\n                       &ctx->dup_file_name,\n                       ctx->file_ref_count,\n                       ctx->dup_version);\n\n    } else {\n        rc = NGX_ERROR;\n        ctx->dup_version = NGX_HTTP_TFS_DUPLICATE_INITIAL_MAGIC_VERSION;\n    }\n    ngx_http_tfs_dedup_callback(ctx, rc);\n}\n\n\nngx_int_t\nngx_http_tfs_dedup_set(ngx_http_tfs_dedup_ctx_t *ctx,\n    ngx_pool_t *pool, ngx_log_t * log)\n{\n    u_char               *p;\n    ssize_t               data_len;\n    ngx_int_t             rc;\n    ngx_http_tair_data_t  tair_key, tair_value;\n\n    data_len = 0;\n\n    if (!ctx->md5_sumed) {\n        rc = ngx_http_tfs_sum_md5(ctx->file_data, ctx->tair_key, &data_len,\n                                  log);\n        if (rc == NGX_ERROR) {\n            return NGX_ERROR;\n        }\n\n        p = ctx->tair_key;\n        p += NGX_HTTP_TFS_MD5_RESULT_LEN;\n\n        *(uint32_t *) p = htonl(data_len);\n        ctx->md5_sumed = 1;\n    }\n\n    tair_key.len = NGX_HTTP_TFS_DUPLICATE_KEY_SIZE;\n    tair_key.data = ctx->tair_key;\n    tair_key.type = NGX_HTTP_TAIR_BYTEARRAY;\n\n    tair_value.len =\n        NGX_HTTP_TFS_DUPLICATE_VALUE_BASE_SIZE + ctx->dup_file_name.len;\n    tair_value.data = ngx_pcalloc(pool, tair_value.len);\n    if (tair_value.data == NULL) {\n        return NGX_ERROR;\n    }\n    ngx_memcpy(tair_value.data, &ctx->file_ref_count,\n               NGX_HTTP_TFS_DUPLICATE_VALUE_BASE_SIZE);\n    ngx_memcpy(tair_value.data + NGX_HTTP_TFS_DUPLICATE_VALUE_BASE_SIZE,\n               ctx->dup_file_name.data, ctx->dup_file_name.len);\n    tair_value.type = NGX_HTTP_TAIR_BYTEARRAY;\n    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, log, 0,\n                   \"set duplicate info: \"\n                   \"file name: %V, file ref count: %d, dup_version: %d\",\n                   &ctx->dup_file_name,\n                   ctx->file_ref_count,\n                   ctx->dup_version);\n\n    rc = ngx_http_tfs_tair_put_helper(ctx->tair_instance, pool, log,\n                                      &tair_key, &tair_value, 0/*expire*/,\n                                      ctx->dup_version,\n                                      ngx_http_tfs_dedup_set_handler, ctx);\n\n    return rc;\n}\n\n\nstatic void\nngx_http_tfs_dedup_set_handler(ngx_int_t rc, void *data)\n{\n    ngx_http_tfs_dedup_ctx_t  *ctx;\n\n    ctx = data;\n\n    if (rc == NGX_HTTP_ETAIR_SUCCESS) {\n        rc = NGX_OK;\n\n    } else {\n        rc = NGX_ERROR;\n    }\n    ngx_http_tfs_dedup_callback(ctx, rc);\n}\n\n\nngx_int_t\nngx_http_tfs_dedup_remove(ngx_http_tfs_dedup_ctx_t *ctx,\n    ngx_pool_t *pool, ngx_log_t * log)\n{\n    u_char                *p;\n    ssize_t                data_len;\n    ngx_int_t              rc;\n    ngx_array_t            tair_keys;\n    ngx_http_tair_data_t  *tair_key;\n\n    data_len = 0;\n\n    if (!ctx->md5_sumed) {\n        rc = ngx_http_tfs_sum_md5(ctx->file_data, ctx->tair_key, &data_len,\n                                  log);\n        if (rc == NGX_ERROR) {\n            return NGX_ERROR;\n        }\n\n        p = ctx->tair_key;\n        p += NGX_HTTP_TFS_MD5_RESULT_LEN;\n\n        *(uint32_t *) p = htonl(data_len);\n        ctx->md5_sumed = 1;\n    }\n\n    rc = ngx_array_init(&tair_keys, pool, 1, sizeof(ngx_http_tair_data_t));\n    if (rc == NGX_ERROR) {\n        return rc;\n    }\n    tair_key = (ngx_http_tair_data_t*) ngx_array_push(&tair_keys);\n\n    tair_key->type = NGX_HTTP_TAIR_BYTEARRAY;\n    tair_key->data = ctx->tair_key;\n    tair_key->len = NGX_HTTP_TFS_DUPLICATE_KEY_SIZE;\n\n    rc = ngx_http_tfs_tair_delete_helper(ctx->tair_instance, pool, log,\n                                         &tair_keys,\n                                         ngx_http_tfs_dedup_remove_handler,\n                                         ctx);\n\n    return rc;\n}\n\n\nstatic void\nngx_http_tfs_dedup_remove_handler(ngx_int_t rc, void *data)\n{\n    ngx_http_tfs_dedup_ctx_t  *ctx;\n\n    ctx = data;\n\n    if (rc == NGX_HTTP_ETAIR_SUCCESS) {\n        rc = NGX_OK;\n\n    } else {\n        rc = NGX_ERROR;\n    }\n\n    ngx_http_tfs_dedup_callback(ctx, rc);\n}\n\n\nngx_int_t\nngx_http_tfs_dedup_check_suffix(ngx_str_t *tfs_name, ngx_str_t *suffix)\n{\n    ngx_int_t  rc;\n\n    rc = NGX_ERROR;\n    if ((tfs_name->len == NGX_HTTP_TFS_FILE_NAME_LEN && suffix->len == 0)\n        || (tfs_name->len > NGX_HTTP_TFS_FILE_NAME_LEN && suffix->len > 0\n            && ((tfs_name->len - NGX_HTTP_TFS_FILE_NAME_LEN) == suffix->len)\n            && (ngx_strncmp(suffix->data,\n                            tfs_name->data + NGX_HTTP_TFS_FILE_NAME_LEN,\n                            suffix->len) == 0)))\n    {\n        rc = NGX_OK;\n    }\n\n    return rc;\n}\n\n\nngx_int_t\nngx_http_tfs_dedup_check_filename(ngx_str_t *dup_file_name,\n    ngx_http_tfs_raw_fsname_t* fsname)\n{\n    ngx_int_t                  rc;\n    ngx_str_t                  dup_file_suffix = ngx_null_string;\n    ngx_http_tfs_raw_fsname_t  dup_fsname;\n\n    rc = ngx_http_tfs_raw_fsname_parse(dup_file_name, &dup_file_suffix,\n                                       &dup_fsname);\n    if (rc == NGX_OK) {\n        if (fsname->cluster_id == dup_fsname.cluster_id\n            && fsname->file.block_id == dup_fsname.file.block_id\n            && fsname->file.seq_id == dup_fsname.file.seq_id\n            && fsname->file.suffix == dup_fsname.file.suffix)\n        {\n            return NGX_OK;\n        }\n    }\n\n    return NGX_ERROR;\n}\n\n\nstatic void\nngx_http_tfs_dedup_callback(ngx_http_tfs_dedup_ctx_t *ctx, ngx_int_t rc)\n{\n    ngx_http_tfs_t           *t;\n    ngx_http_tfs_rcs_info_t  *rc_info;\n\n    t = ctx->data;\n    rc_info = t->rc_info_node;\n\n    switch (t->r_ctx.action.code) {\n    case NGX_HTTP_TFS_ACTION_REMOVE_FILE:\n        switch(t->state) {\n        case NGX_HTTP_TFS_STATE_REMOVE_READ_META_SEGMENT:\n            /* exist in tair */\n            if (rc == NGX_OK) {\n                /* check file name */\n                rc = ngx_http_tfs_dedup_check_filename(&ctx->dup_file_name,\n                                                       &t->r_ctx.fsname);\n                if (rc == NGX_OK) {\n                    /* file name match, modify ref count and save tair */\n                    if (t->r_ctx.unlink_type == NGX_HTTP_TFS_UNLINK_DELETE) {\n                        if (--ctx->file_ref_count <= 0) {\n                            /* if ref count is 0,\n                             * remove key from tair then unlink file\n                             */\n                            t->state = NGX_HTTP_TFS_STATE_REMOVE_GET_BLK_INFO;\n                            t->is_stat_dup_file = NGX_HTTP_TFS_NO;\n                            t->tfs_peer->body_buffer = ctx->save_body_buffer;\n                            ctx->file_data = t->meta_segment_data;\n                            rc = ngx_http_tfs_dedup_remove(ctx, t->pool,\n                                                           t->log);\n                            /* do not care delete tair fail,\n                             * go on unlinking file\n                             */\n                            if (rc == NGX_ERROR) {\n                                ngx_http_tfs_finalize_state(t, NGX_OK);\n                            }\n\n                            return;\n                        }\n\n                        /* file_ref_count > 0, just save tair */\n                        t->state = NGX_HTTP_TFS_STATE_REMOVE_DONE;\n                        ctx->file_data = t->meta_segment_data;\n                        rc = ngx_http_tfs_dedup_set(ctx, t->pool, t->log);\n                        /* do not care save tair fail, return success */\n                        if (rc == NGX_ERROR) {\n                            ngx_http_tfs_finalize_state(t, NGX_DONE);\n                        }\n\n                        return;\n                    }\n                }\n\n                /* file name not match, unlink file */\n                t->tfs_peer->body_buffer = ctx->save_body_buffer;\n                t->state = NGX_HTTP_TFS_STATE_REMOVE_GET_BLK_INFO;\n                t->is_stat_dup_file = NGX_HTTP_TFS_NO;\n                ngx_http_tfs_finalize_state(t, NGX_OK);\n\n                return;\n            }\n\n            /* not exist in tair, unlink file */\n            t->tfs_peer->body_buffer = ctx->save_body_buffer;\n            t->state = NGX_HTTP_TFS_STATE_REMOVE_GET_BLK_INFO;\n            t->is_stat_dup_file = NGX_HTTP_TFS_NO;\n            ngx_http_tfs_finalize_state(t, NGX_OK);\n            return;\n        case NGX_HTTP_TFS_STATE_REMOVE_GET_BLK_INFO:\n        case NGX_HTTP_TFS_STATE_REMOVE_DELETE_DATA:\n            ngx_http_tfs_finalize_state(t, NGX_OK);\n            return;\n        case NGX_HTTP_TFS_STATE_REMOVE_DONE:\n            ngx_http_tfs_finalize_state(t, NGX_DONE);\n            return;\n        }\n        break;\n    case NGX_HTTP_TFS_ACTION_WRITE_FILE:\n        switch(t->state) {\n        case NGX_HTTP_TFS_STATE_WRITE_CLUSTER_ID_NS:\n        case NGX_HTTP_TFS_STATE_WRITE_GET_BLK_INFO:\n            /* exist in tair */\n            if (rc == NGX_OK) {\n                /* check suffix */\n                rc = ngx_http_tfs_dedup_check_suffix(&ctx->dup_file_name,\n                                                     &t->r_ctx.file_suffix);\n                if (rc == NGX_OK) {\n                    /* suffix match, need to stat file */\n                    rc = ngx_http_tfs_raw_fsname_parse(&ctx->dup_file_name,\n                                                       &ctx->dup_file_suffix,\n                                                       &t->r_ctx.fsname);\n                    if (rc == NGX_OK) {\n                        t->file.cluster_id = t->r_ctx.fsname.cluster_id;\n                        t->is_stat_dup_file = NGX_HTTP_TFS_YES;\n                        t->state = NGX_HTTP_TFS_STATE_WRITE_GET_BLK_INFO;\n                    }\n\n                } else {\n                    /* suffix not match, need save new tfs file,\n                     * do not save tair\n                     */\n                    t->use_dedup = NGX_HTTP_TFS_NO;\n                }\n            } /* not exist in tair need save new tfs file and tair */\n\n            /* need reset meta segment */\n            rc = ngx_http_tfs_get_meta_segment(t);\n            if (rc != NGX_OK) {\n                ngx_log_error(NGX_LOG_ERR, t->log, 0,\n                              \"tfs get meta segment failed\");\n                ngx_http_tfs_finalize_request(t->data, t,\n                                              NGX_HTTP_INTERNAL_SERVER_ERROR);\n                return;\n            }\n\n            /* lookup block cache */\n            if (t->is_stat_dup_file) {\n                /* dedup write may need to stat file */\n                if (rc_info->use_remote_block_cache) {\n                    rc = ngx_http_tfs_get_remote_block_cache_instance(\n                              &t->block_cache_ctx.remote_ctx,\n                              &rc_info->remote_block_cache_info);\n                    if (rc == NGX_ERROR) {\n                        ngx_log_error(NGX_LOG_ERR, t->log, 0,\n                                     \"get remote block cache instance failed.\");\n\n                    } else {\n                        t->block_cache_ctx.use_cache |=\n                            NGX_HTTP_TFS_REMOTE_BLOCK_CACHE;\n                    }\n                }\n\n                ngx_http_tfs_lookup_block_cache(t);\n\n                return;\n            }\n\n            ngx_http_tfs_finalize_state(t, NGX_OK);\n            break;\n        case NGX_HTTP_TFS_STATE_WRITE_STAT_DUP_FILE:\n            if (rc == NGX_OK) {\n                t->state = NGX_HTTP_TFS_STATE_WRITE_DONE;\n                ngx_http_tfs_finalize_state(t, NGX_DONE);\n\n            } else {\n                /* save tair(add ref count) failed,\n                 * need save new tfs file, do not save tair\n                 */\n                t->state = NGX_HTTP_TFS_STATE_WRITE_CLUSTER_ID_NS;\n                t->is_stat_dup_file = NGX_HTTP_TFS_NO;\n                t->use_dedup = NGX_HTTP_TFS_NO;\n                /* need reset output buf */\n                t->out_bufs = NULL;\n                /* need reset block id and file id */\n                t->file.segment_data[0].segment_info.block_id = 0;\n                t->file.segment_data[0].segment_info.file_id = 0;\n                ngx_http_tfs_finalize_state(t, NGX_OK);\n            }\n            break;\n        case NGX_HTTP_TFS_STATE_WRITE_DONE:\n            ngx_http_tfs_finalize_state(t, NGX_DONE);\n            break;\n        }\n    }\n    return;\n}\n\n\n#ifdef NGX_HTTP_TFS_USE_TAIR\n\nngx_int_t\nngx_http_tfs_get_dedup_instance(ngx_http_tfs_dedup_ctx_t *ctx,\n    ngx_http_tfs_tair_server_addr_info_t *server_addr_info,\n    uint32_t server_addr_hash)\n{\n    ngx_str_t                     *st;\n    ngx_int_t                      rc, i;\n    ngx_array_t                    config_server;\n    ngx_http_tfs_t                *t;\n    ngx_http_etair_server_conf_t  *server;\n    ngx_http_tfs_tair_instance_t  *instance;\n\n    t = ctx->data;\n\n    for (i = 0; i < NGX_HTTP_TFS_MAX_CLUSTER_COUNT; i++) {\n        instance = &t->main_conf->dup_instances[i];\n\n        if (instance->server == NULL) {\n            break;\n        }\n\n        if (instance->server_addr_hash == server_addr_hash) {\n            ctx->tair_instance = instance;\n            return NGX_OK;\n        }\n    }\n\n    /* not found && full, clear */\n    if (i > NGX_HTTP_TFS_MAX_CLUSTER_COUNT) {\n        for (i = 0; i < NGX_HTTP_TFS_MAX_CLUSTER_COUNT; i++) {\n            instance = &t->main_conf->dup_instances[i];\n            if (instance->server != NULL) {\n                ngx_http_etair_destory_server(instance->server,\n                                              (ngx_cycle_t *) ngx_cycle);\n                instance->server = NULL;\n            }\n        }\n        instance = &t->main_conf->dup_instances[0];\n    }\n\n    rc = ngx_array_init(&config_server, t->pool,\n                        NGX_HTTP_TFS_TAIR_CONFIG_SERVER_COUNT,\n                        sizeof(ngx_str_t));\n    if (rc == NGX_ERROR) {\n        return NGX_ERROR;\n    }\n\n    for (i = 0; i < NGX_HTTP_TFS_TAIR_CONFIG_SERVER_COUNT; i++) {\n        if (server_addr_info->server[i].len > 0 ) {\n            st = (ngx_str_t *) ngx_array_push(&config_server);\n            *st = server_addr_info->server[i];\n        }\n    }\n\n    server = &server_addr_info->server[NGX_HTTP_TFS_TAIR_CONFIG_SERVER_COUNT];\n    server = ngx_http_etair_create_server(server,\n                                          &config_server,\n                                          t->main_conf->tair_timeout,\n                                          (ngx_cycle_t *) ngx_cycle);\n    if (server == NULL) {\n        return NGX_ERROR;\n    }\n\n    instance->server = server;\n    instance->server_addr_hash = server_addr_hash;\n    instance->area = server_addr_info->area;\n    ctx->tair_instance = instance;\n\n    return NGX_OK;\n}\n\n#else\n\nngx_int_t\nngx_http_tfs_get_dedup_instance(ngx_http_tfs_dedup_ctx_t *ctx,\n    ngx_http_tfs_tair_server_addr_info_t *server_addr_info,\n    uint32_t server_addr_hash)\n{\n    return NGX_ERROR;\n}\n\n#endif\n"
  },
  {
    "path": "modules/ngx_http_tfs_module/ngx_http_tfs_duplicate.h",
    "content": "\n/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#ifndef _NGX_HTTP_TFS_DUPLICATE_H_INCLUDED_\n#define _NGX_HTTP_TFS_DUPLICATE_H_INCLUDED_\n\n\n#include <ngx_tfs_common.h>\n#include <ngx_http_tfs_tair_helper.h>\n\n\ntypedef struct {\n    u_char                            tair_key[NGX_HTTP_TFS_DUPLICATE_KEY_SIZE];\n    int32_t                           dup_version;\n    int32_t                           file_ref_count;\n    ngx_str_t                         dup_file_name;\n    ngx_str_t                         dup_file_suffix;\n    ngx_buf_t                         save_body_buffer;\n    ngx_http_tfs_t                   *data;\n    ngx_http_tfs_tair_instance_t     *tair_instance;\n    ngx_chain_t                      *file_data;\n    unsigned                          md5_sumed:1;\n} ngx_http_tfs_dedup_ctx_t;\n\n\nngx_int_t ngx_http_tfs_dedup_get(ngx_http_tfs_dedup_ctx_t *ctx,\n    ngx_pool_t *pool, ngx_log_t *log);\nngx_int_t ngx_http_tfs_dedup_set(ngx_http_tfs_dedup_ctx_t *ctx,\n    ngx_pool_t *pool, ngx_log_t *log);\nngx_int_t ngx_http_tfs_dedup_remove(ngx_http_tfs_dedup_ctx_t *ctx,\n    ngx_pool_t *pool, ngx_log_t *log);\n\nngx_int_t ngx_http_tfs_get_dedup_instance(ngx_http_tfs_dedup_ctx_t *ctx,\n    ngx_http_tfs_tair_server_addr_info_t *server_addr_info,\n    uint32_t server_addr_hash);\n\n\n#endif  /* _NGX_HTTP_TFS_DUPLICATE_H_INCLUDED_ */\n"
  },
  {
    "path": "modules/ngx_http_tfs_module/ngx_http_tfs_errno.h",
    "content": "\n/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#ifndef _NGX_HTTP_TFS_ERRNO_H_INCLUDED_\n#define _NGX_HTTP_TFS_ERRNO_H_INCLUDED_\n\n\n#include <nginx.h>\n\n\n#define NGX_HTTP_TFS_EXIT_GENERAL_ERROR            -1000\n#define NGX_HTTP_TFS_EXIT_CONFIG_ERROR             -1001\n#define NGX_HTTP_TFS_EXIT_UNKNOWN_MSGTYPE          -1002\n#define NGX_HTTP_TFS_EXIT_INVALID_ARGU             -1003\n#define NGX_HTTP_TFS_EXIT_ALL_SEGMENT_             -1004\n#define NGX_HTTP_TFS_EXIT_INVALIDFD_ERROR          -1005\n#define NGX_HTTP_TFS_EXIT_NOT_INIT_ERROR           -1006\n#define NGX_HTTP_TFS_EXIT_INVALID_ARGU_ERROR       -1007\n#define NGX_HTTP_TFS_EXIT_NOT_PERM_OPER            -1008\n#define NGX_HTTP_TFS_EXIT_NOT_OPEN_ERROR           -1009\n#define NGX_HTTP_TFS_EXIT_CHECK_CRC_ERROR          -1010\n#define NGX_HTTP_TFS_EXIT_SERIALIZE_ERROR          -1011\n#define NGX_HTTP_TFS_EXIT_DESERIALIZE_ERROR        -1012\n/* access permission error */\n#define NGX_HTTP_TFS_EXIT_ACCESS_PERMISSION_ERROR  -1013\n/* system parameter error */\n#define NGX_HTTP_TFS_EXIT_SYSTEM_PARAMETER_ERROR   -1014\n#define NGX_HTTP_TFS_EXIT_UNIQUE_META_NOT_EXIST    -1015\n/* fuction parameter error */\n#define NGX_HTTP_TFS_EXIT_PARAMETER_ERROR          -1016\n/* mmap file failed */\n#define NGX_HTTP_TFS_EXIT_MMAP_FILE_ERROR          -1017\n/* lru value not found by key */\n#define NGX_HTTP_TFS_EXIT_LRU_VALUE_NOT_EXIST      -1018\n/* lru value existed */\n#define NGX_HTTP_TFS_EXIT_LRU_VALUE_EXIST          -1019\n/* channel id invalid */\n#define NGX_HTTP_TFS_EXIT_CHANNEL_ID_INVALID       -1020\n/* data packet timeout */\n#define NGX_HTTP_TFS_EXIT_DATA_PACKET_TIMEOUT      -1021\n\n#define NGX_HTTP_TFS_EXIT_FILE_OP_ERROR            -2000\n#define NGX_HTTP_TFS_EXIT_OPEN_FILE_ERROR          -2001\n#define NGX_HTTP_TFS_EXIT_INVALID_FD               -2002\n#define NGX_HTTP_TFS_EXIT_RECORD_SIZE_ERROR        -2003\n#define NGX_HTTP_TFS_EXIT_READ_FILE_ERROR          -2004\n#define NGX_HTTP_TFS_EXIT_WRITE_FILE_ERROR         -2005\n#define NGX_HTTP_TFS_EXIT_FILESYSTEM_ERROR         -2006\n#define NGX_HTTP_TFS_EXIT_FILE_FORMAT_ERROR        -2007\n#define NGX_HTTP_TFS_EXIT_SLOTS_OFFSET_SIZE_ERROR  -2008\n#define NGX_HTTP_TFS_EXIT_FILE_BUSY_ERROR          -2009\n\n#define NGX_HTTP_TFS_EXIT_NETWORK_ERROR            -3000\n#define NGX_HTTP_TFS_EXIT_IOCTL_ERROR              -3001\n#define NGX_HTTP_TFS_EXIT_CONNECT_ERROR            -3002\n#define NGX_HTTP_TFS_EXIT_SENDMSG_ERROR            -3003\n#define NGX_HTTP_TFS_EXIT_RECVMSG_ERROR            -3004\n#define NGX_HTTP_TFS_EXIT_TIMEOUT_ERROR            -3005\n/* waitid exist error */\n#define NGX_HTTP_TFS_EXIT_WAITID_EXIST_ERROR       -3006\n/* waitid not found in waitid set */\n#define NGX_HTTP_TFS_EXIT_WAITID_NOT_FOUND_ERROR   -3007\n/* socket nof found in socket map */\n#define NGX_HTTP_TFS_EXIT_SOCKET_NOT_FOUND_ERROR   -3008\n\n#define NGX_HTTP_TFS_EXIT_TFS_ERROR                -5000\n#define NGX_HTTP_TFS_EXIT_NO_BLOCK                 -5001\n#define NGX_HTTP_TFS_EXIT_NO_DATASERVER            -5002\n#define NGX_HTTP_TFS_EXIT_BLOCK_NOT_FOUND          -5003\n#define NGX_HTTP_TFS_EXIT_DATASERVER_NOT_FOUND     -5004\n/* lease not found */\n#define NGX_HTTP_TFS_EXIT_CANNOT_GET_LEASE         -5005\n#define NGX_HTTP_TFS_EXIT_COMMIT_ERROR             -5006\n#define NGX_HTTP_TFS_EXIT_LEASE_EXPIRED            -5007\n#define NGX_HTTP_TFS_EXIT_BINLOG_ERROR             -5008\n#define NGX_HTTP_TFS_EXIT_NO_REPLICATE             -5009\n#define NGX_HTTP_TFS_EXIT_BLOCK_BUSY               -5010\n/* update block information version error */\n#define NGX_HTTP_TFS_EXIT_UPDATE_BLOCK_INFO_VERSION_ERROR     -5011\n/* access mode error */\n#define NGX_HTTP_TFS_EXIT_ACCESS_MODE_ERROR                   -5012\n/* play log error */\n#define NGX_HTTP_TFS_EXIT_PLAY_LOG_ERROR                      -5013\n/* current nameserver only read */\n#define NGX_HTTP_TFS_EXIT_NAMESERVER_ONLY_READ                -5014\n/* current block already exist */\n#define NGX_HTTP_TFS_EXIT_BLOCK_ALREADY_EXIST                 -5015\n/* create block by block id failed */\n#define NGX_HTTP_TFS_EXIT_CREATE_BLOCK_BY_ID_ERROR            -5016\n/* server object not found in XXX */\n#define NGX_HTTP_TFS_EXIT_SERVER_OBJECT_NOT_FOUND             -5017\n/* update relation error */\n#define NGX_HTTP_TFS_EXIT_UPDATE_RELATION_ERROR               -5018\n/* nameserver in safe_mode_time, discard newblk packet */\n#define NGX_HTTP_TFS_EXIT_DISCARD_NEWBLK_ERROR                -5019\n\n/* write offset error */\n#define NGX_HTTP_TFS_EXIT_WRITE_OFFSET_ERROR                  -8001\n/* read offset error */\n#define NGX_HTTP_TFS_EXIT_READ_OFFSET_ERROR                   -8002\n/* block id is zero, fatal error */\n#define NGX_HTTP_TFS_EXIT_BLOCKID_ZERO_ERROR                  -8003\n/* block is used up, fatal error */\n#define NGX_HTTP_TFS_EXIT_BLOCK_EXHAUST_ERROR                 -8004\n/* need extend too much physcial block when extend block */\n#define NGX_HTTP_TFS_EXIT_PHYSICALBLOCK_NUM_ERROR             -8005\n/* can't find logic block */\n#define NGX_HTTP_TFS_EXIT_NO_LOGICBLOCK_ERROR                 -8006\n/* input point is null */\n#define NGX_HTTP_TFS_EXIT_POINTER_NULL                        -8007\n/* cat find unused fileid in limited times */\n#define NGX_HTTP_TFS_EXIT_CREATE_FILEID_ERROR                 -8008\n/* block id conflict */\n#define NGX_HTTP_TFS_EXIT_BLOCKID_CONFLICT_ERROR              -8009\n/* LogicBlock already Exists */\n#define NGX_HTTP_TFS_EXIT_BLOCK_EXIST_ERROR                   -8010\n/* compact block error */\n#define NGX_HTTP_TFS_EXIT_COMPACT_BLOCK_ERROR                 -8011\n/* read or write length is less than required */\n#define NGX_HTTP_TFS_EXIT_DISK_OPER_INCOMPLETE                -8012\n/* datafile is NULL  / crc / getdata error */\n#define NGX_HTTP_TFS_EXIT_DATA_FILE_ERROR                     -8013\n/* too much data file */\n#define NGX_HTTP_TFS_EXIT_DATAFILE_OVERLOAD                   -8014\n/* data file is expired */\n#define NGX_HTTP_TFS_EXIT_DATAFILE_EXPIRE_ERROR               -8015\n/* file flag or id error when read file */\n#define NGX_HTTP_TFS_EXIT_FILE_INFO_ERROR                     -8016\n/* fileid is same in rename file */\n#define NGX_HTTP_TFS_EXIT_RENAME_FILEID_SAME_ERROR            -8017\n/* file status error(in unlinkfile) */\n#define NGX_HTTP_TFS_EXIT_FILE_STATUS_ERROR                   -8018\n/* action is not defined(in unlinkfile) */\n#define NGX_HTTP_TFS_EXIT_FILE_ACTION_ERROR                   -8019\n/* file system is not inited */\n#define NGX_HTTP_TFS_EXIT_FS_NOTINIT_ERROR                    -8020\n/* file system's bit map conflict */\n#define NGX_HTTP_TFS_EXIT_BITMAP_CONFLICT_ERROR               -8021\n/* physical block is already exist in file system */\n#define NGX_HTTP_TFS_EXIT_PHYSIC_UNEXPECT_FOUND_ERROR         -8022\n#define NGX_HTTP_TFS_EXIT_BLOCK_SETED_ERROR                   -8023\n/* index is loaded when create or load */\n#define NGX_HTTP_TFS_EXIT_INDEX_ALREADY_LOADED_ERROR          -8024\n/* meta not found in index */\n#define NGX_HTTP_TFS_EXIT_META_NOT_FOUND_ERROR                -8025\n/* meta found in index when insert */\n#define NGX_HTTP_TFS_EXIT_META_UNEXPECT_FOUND_ERROR           -8026\n/* require offset is out of index size */\n#define NGX_HTTP_TFS_EXIT_META_OFFSET_ERROR                   -8027\n/* bucket size is conflict with before */\n#define NGX_HTTP_TFS_EXIT_BUCKET_CONFIGURE_ERROR              -8028\n/* index already exist when create index */\n#define NGX_HTTP_TFS_EXIT_INDEX_UNEXPECT_EXIST_ERROR          -8029\n/* index is corrupted, and index is created */\n#define NGX_HTTP_TFS_EXIT_INDEX_CORRUPT_ERROR                 -8030\n/* ds version error */\n#define NGX_HTTP_TFS_EXIT_BLOCK_DS_VERSION_ERROR              -8031\n/* ns version error */\n#define NGX_HTTP_TFS_EXIT_BLOCK_NS_VERSION_ERROR              -8332\n/* offset is out of physical block size */\n#define NGX_HTTP_TFS_EXIT_PHYSIC_BLOCK_OFFSET_ERROR           -8033\n/* file size is little than fileinfo */\n#define NGX_HTTP_TFS_EXIT_READ_FILE_SIZE_ERROR                -8034\n/* connect to ds fail */\n#define NGX_HTTP_TFS_EXIT_DS_CONNECT_ERROR                    -8035\n/* too much block checker */\n#define NGX_HTTP_TFS_EXIT_BLOCK_CHECKER_OVERLOAD              -8036\n/* fallocate is not implement */\n#define NGX_HTTP_TFS_EXIT_FALLOCATE_NOT_IMPLEMENT             -8037\n/* sync file failed */\n#define NGX_HTTP_TFS_EXIT_SYNC_FILE_ERROR                     -8038\n\n#define NGX_HTTP_TFS_EXIT_SESSION_EXIST_ERROR                 -9001\n#define NGX_HTTP_TFS_EXIT_SESSIONID_INVALID_ERROR             -9002\n#define NGX_HTTP_TFS_EXIT_APP_NOTEXIST_ERROR                  -9010\n#define NGX_HTTP_TFS_EXIT_APPID_PERMISSION_DENY               -9011\n\n#define NGX_HTTP_TFS_EXIT_SYSTEM_ERROR                        -10000\n#define NGX_HTTP_TFS_EXIT_REGISTER_OPLOG_SYNC_ERROR           -12000\n#define NGX_HTTP_TFS_EXIT_MAKEDIR_ERROR                       -13000\n\n#define NGX_HTTP_TFS_EXIT_UNKNOWN_SQL_ERROR                   -14000\n#define NGX_HTTP_TFS_EXIT_TARGET_EXIST_ERROR                  -14001\n#define NGX_HTTP_TFS_EXIT_PARENT_EXIST_ERROR                  -14002\n#define NGX_HTTP_TFS_EXIT_DELETE_DIR_WITH_FILE_ERROR          -14003\n#define NGX_HTTP_TFS_EXIT_VERSION_CONFLICT_ERROR              -14004\n#define NGX_HTTP_TFS_EXIT_NOT_CREATE_ERROR                    -14005\n#define NGX_HTTP_TFS_EXIT_CLUSTER_ID_ERROR                    -14006\n#define NGX_HTTP_TFS_EXIT_FRAG_META_OVERFLOW_ERROR            -14007\n#define NGX_HTTP_TFS_EXIT_UPDATE_FRAG_INFO_ERROR              -14008\n#define NGX_HTTP_TFS_EXIT_WRITE_EXIST_POS_ERROR               -14009\n#define NGX_HTTP_TFS_EXIT_INVALID_FILE_NAME                   -14010\n#define NGX_HTTP_TFS_EXIT_MOVE_TO_SUB_DIR_ERROR               -14011\n#define NGX_HTTP_TFS_EXIT_OVER_MAX_SUB_DIRS_COUNT             -14012\n#define NGX_HTTP_TFS_EXIT_OVER_MAX_SUB_DIRS_DEEP              -14013\n#define NGX_HTTP_TFS_EXIT_OVER_MAX_SUB_FILES_COUNT            -14014\n\n/* server register fail */\n#define NGX_HTTP_TFS_EXIT_REGISTER_ERROR                      -15000\n/* server register fail, server is existed */\n#define NGX_HTTP_TFS_EXIT_REGISTER_EXIST_ERROR                -15001\n/* renew lease fail, server is not existed */\n#define NGX_HTTP_TFS_EXIT_REGISTER_NOT_EXIST_ERROR            -15002\n/* table version error */\n#define NGX_HTTP_TFS_EXIT_TABLE_VERSION_ERROR                 -15003\n/* bucket id invalid */\n#define NGX_HTTP_TFS_EXIT_BUCKET_ID_INVLAID                   -15004\n/* bucket not exist */\n#define NGX_HTTP_TFS_EXIT_BUCKET_NOT_EXIST                    -15005\n/* new table not exist */\n#define NGX_HTTP_TFS_EXIT_NEW_TABLE_NOT_EXIST                 -15005\n/* new table invalid */\n#define NGX_HTTP_TFS_EXIT_NEW_TABLE_INVALID                   -15005\n\n\n#endif  /* _NGX_HTTP_TFS_ERRNO_H_INCLUDED_ */\n"
  },
  {
    "path": "modules/ngx_http_tfs_module/ngx_http_tfs_json.c",
    "content": "\n/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#include <ngx_tfs_common.h>\n#include <ngx_http_tfs_json.h>\n\n\nngx_http_tfs_json_gen_t *\nngx_http_tfs_json_init(ngx_log_t *log, ngx_pool_t *pool)\n{\n    yajl_gen                  g;\n    ngx_http_tfs_json_gen_t  *tj_gen;\n\n    g = yajl_gen_alloc(NULL);\n    if (g == NULL) {\n        ngx_log_error(NGX_LOG_ERR, log, errno, \"alloc yajl_gen failed\");\n        return NULL;\n    }\n\n    tj_gen = ngx_pcalloc(pool, sizeof(ngx_http_tfs_json_gen_t));\n    if (tj_gen == NULL) {\n        return NULL;\n    }\n\n    yajl_gen_config(g, yajl_gen_beautify, 1);\n\n    tj_gen->gen = g;\n    tj_gen->pool = pool;\n    tj_gen->log = log;\n\n    return tj_gen;\n}\n\n\nvoid\nngx_http_tfs_json_destroy(ngx_http_tfs_json_gen_t *tj_gen)\n{\n    if (tj_gen != NULL) {\n        yajl_gen_free(tj_gen->gen);\n    }\n}\n\n\nngx_chain_t *\nngx_http_tfs_json_custom_file_info(ngx_http_tfs_json_gen_t *tj_gen,\n    ngx_http_tfs_custom_meta_info_t *meta_info, uint8_t file_type)\n{\n    size_t                       size;\n    u_char                       time_buf[NGX_HTTP_TFS_GMT_TIME_SIZE];\n    yajl_gen                     g;\n    uint32_t                     count;\n    ngx_buf_t                   *b;\n    ngx_int_t                    is_file;\n    ngx_uint_t                   i;\n    ngx_chain_t                 *cl;\n    ngx_http_tfs_custom_file_t  *file;\n\n    g = tj_gen->gen;\n    size = 0;\n\n    if (file_type == NGX_HTTP_TFS_CUSTOM_FT_DIR) {\n        yajl_gen_array_open(g);\n    }\n\n    for(; meta_info; meta_info = meta_info->next) {\n        count = meta_info->file_count;\n        file = meta_info->files;\n\n        for (i = 0; i < count; i++) {\n            yajl_gen_map_open(g);\n\n            yajl_gen_string(g, (const unsigned char *) \"NAME\", 4);\n            yajl_gen_string(g, (const unsigned char *) file[i].file_name.data,\n                            file[i].file_name.len);\n\n            yajl_gen_string(g, (const unsigned char *) \"PID\", 3);\n            yajl_gen_integer(g, file[i].file_info.pid);\n\n            yajl_gen_string(g, (const unsigned char *) \"ID\", 2);\n            yajl_gen_integer(g, file[i].file_info.id);\n\n            yajl_gen_string(g, (const unsigned char *) \"SIZE\", 4);\n            yajl_gen_integer(g, file[i].file_info.size);\n\n            yajl_gen_string(g, (const unsigned char *) \"IS_FILE\", 7);\n            if (file_type == NGX_HTTP_TFS_CUSTOM_FT_DIR) {\n                is_file = (file[i].file_info.pid >> 63) & 0x01;\n            } else {\n                is_file = 1;\n            }\n            yajl_gen_bool(g, is_file);\n\n            ngx_http_tfs_time(time_buf, file[i].file_info.create_time);\n            yajl_gen_string(g, (const unsigned char *) \"CREATE_TIME\", 11);\n            yajl_gen_string(g, time_buf, NGX_HTTP_TFS_GMT_TIME_SIZE);\n\n            ngx_http_tfs_time(time_buf, file[i].file_info.modify_time);\n            yajl_gen_string(g, (const unsigned char *) \"MODIFY_TIME\", 11);\n            yajl_gen_string(g, time_buf, NGX_HTTP_TFS_GMT_TIME_SIZE);\n\n            yajl_gen_string(g, (const unsigned char *) \"VER_NO\", 6);\n            yajl_gen_integer(g, file[i].file_info.ver_no);\n\n            yajl_gen_map_close(g);\n        }\n    }\n\n    if (file_type == NGX_HTTP_TFS_CUSTOM_FT_DIR) {\n        yajl_gen_array_close(g);\n    }\n\n    cl = ngx_alloc_chain_link(tj_gen->pool);\n    if (cl == NULL) {\n        return NULL;\n    }\n    cl->next = NULL;\n\n    b = ngx_calloc_buf(tj_gen->pool);\n    if (b == NULL) {\n        return NULL;\n    }\n\n    yajl_gen_get_buf(g, (const unsigned char **) &b->pos, &size);\n    b->last = b->pos + size;\n    b->end = b->last;\n    b->temporary = 1;\n    b->flush = 1;\n    /* b->last_buf = 1; */\n    cl->buf = b;\n\n    return cl;\n}\n\n\nngx_chain_t *\nngx_http_tfs_json_file_name(ngx_http_tfs_json_gen_t *tj_gen,\n    ngx_str_t *file_name)\n{\n    size_t        size;\n    yajl_gen      g;\n    ngx_buf_t    *b;\n    ngx_chain_t  *cl;\n\n    g = tj_gen->gen;\n    size = 0;\n\n    yajl_gen_map_open(g);\n    yajl_gen_string(g, (const unsigned char *) \"TFS_FILE_NAME\", 13);\n    yajl_gen_string(g, (const unsigned char *) file_name->data, file_name->len);\n    yajl_gen_map_close(g);\n\n    cl = ngx_alloc_chain_link(tj_gen->pool);\n    if (cl == NULL) {\n        return NULL;\n    }\n    cl->next = NULL;\n\n    b = ngx_calloc_buf(tj_gen->pool);\n    if (b == NULL) {\n        return NULL;\n    }\n    yajl_gen_get_buf(g, (const unsigned char **) &b->pos, &size);\n    b->last = b->pos + size;\n    b->end = b->last;\n    b->temporary = 1;\n    b->flush = 1;\n    /* b->last_buf = 1; */\n    cl->buf = b;\n    return cl;\n}\n\n\nngx_chain_t *\nngx_http_tfs_json_raw_file_stat(ngx_http_tfs_json_gen_t *tj_gen,\n    u_char* file_name, uint32_t block_id,\n    ngx_http_tfs_raw_file_stat_t *file_stat)\n{\n    size_t        size;\n    u_char        time_buf[NGX_HTTP_TFS_GMT_TIME_SIZE];\n    yajl_gen      g;\n    ngx_buf_t    *b;\n    ngx_chain_t  *cl;\n\n    g = tj_gen->gen;\n    size = 0;\n\n    yajl_gen_map_open(g);\n\n    yajl_gen_string(g, (const unsigned char *) \"FILE_NAME\", 9);\n    yajl_gen_string(g, (const unsigned char *) file_name, 18);\n\n    yajl_gen_string(g, (const unsigned char *) \"BLOCK_ID\", 8);\n    yajl_gen_integer(g, block_id);\n\n    yajl_gen_string(g, (const unsigned char *) \"FILE_ID\", 7);\n    yajl_gen_integer(g, file_stat->id);\n\n    yajl_gen_string(g, (const unsigned char *) \"OFFSET\", 6);\n    yajl_gen_integer(g, file_stat->offset);\n\n    yajl_gen_string(g, (const unsigned char *) \"SIZE\", 4);\n    yajl_gen_integer(g, file_stat->size);\n\n    yajl_gen_string(g, (const unsigned char *) \"OCCUPY_SIZE\", 11);\n    yajl_gen_integer(g, file_stat->u_size);\n\n    ngx_http_tfs_time(time_buf, file_stat->modify_time);\n    yajl_gen_string(g, (const unsigned char *) \"MODIFY_TIME\", 11);\n    yajl_gen_string(g, time_buf, NGX_HTTP_TFS_GMT_TIME_SIZE);\n\n    ngx_http_tfs_time(time_buf, file_stat->create_time);\n    yajl_gen_string(g, (const unsigned char *) \"CREATE_TIME\", 11);\n    yajl_gen_string(g, time_buf, NGX_HTTP_TFS_GMT_TIME_SIZE);\n\n    yajl_gen_string(g, (const unsigned char *) \"STATUS\", 6);\n    yajl_gen_integer(g, file_stat->flag);\n\n    yajl_gen_string(g, (const unsigned char *) \"CRC\", 3);\n    yajl_gen_integer(g, file_stat->crc);\n\n    yajl_gen_map_close(g);\n\n    cl = ngx_alloc_chain_link(tj_gen->pool);\n    if (cl == NULL) {\n        return NULL;\n    }\n    cl->next = NULL;\n\n    b = ngx_calloc_buf(tj_gen->pool);\n    if (b == NULL) {\n        return NULL;\n    }\n\n    yajl_gen_get_buf(g, (const unsigned char **) &b->pos, &size);\n\n    b->last = b->pos + size;\n    b->end = b->last;\n    b->temporary = 1;\n    b->flush = 1;\n\n    /* b->last_buf = 1; */\n\n    cl->buf = b;\n\n    return cl;\n}\n\n\nngx_chain_t *\nngx_http_tfs_json_appid(ngx_http_tfs_json_gen_t *tj_gen,\n    uint64_t app_id)\n{\n    size_t        size;\n    yajl_gen      g;\n    ngx_buf_t    *b;\n    ngx_chain_t  *cl;\n\n    g = tj_gen->gen;\n    size = 0;\n\n    u_char str_appid[NGX_INT64_LEN] = {'\\0'};\n    ngx_sprintf(str_appid, \"%uL\", app_id);\n\n    yajl_gen_map_open(g);\n    yajl_gen_string(g, (const unsigned char *) \"APP_ID\", 6);\n    yajl_gen_string(g, (const unsigned char *) str_appid,\n                    ngx_strlen(str_appid));\n    yajl_gen_map_close(g);\n\n    cl = ngx_alloc_chain_link(tj_gen->pool);\n    if (cl == NULL) {\n        return NULL;\n    }\n    cl->next = NULL;\n\n    b = ngx_calloc_buf(tj_gen->pool);\n    if (b == NULL) {\n        return NULL;\n    }\n\n    yajl_gen_get_buf(g, (const unsigned char **) &b->pos, &size);\n    b->last = b->pos + size;\n    b->end = b->last;\n    b->temporary = 1;\n    b->flush = 1;\n    /* b->last_buf = 1; */\n    cl->buf = b;\n    return cl;\n}\n\n\nngx_chain_t *\nngx_http_tfs_json_file_hole_info(ngx_http_tfs_json_gen_t *tj_gen,\n    ngx_array_t *file_holes)\n{\n    size_t                          size;\n    yajl_gen                        g;\n    ngx_buf_t                      *b;\n    ngx_uint_t                      i;\n    ngx_chain_t                    *cl;\n    ngx_http_tfs_file_hole_info_t  *file_hole_info;\n\n    g = tj_gen->gen;\n    size = 0;\n\n    yajl_gen_array_open(g);\n\n    file_hole_info = (ngx_http_tfs_file_hole_info_t *) file_holes->elts;\n    for(i = 0; i < file_holes->nelts; i++, file_hole_info++) {\n        yajl_gen_map_open(g);\n\n        yajl_gen_string(g, (const unsigned char *) \"OFFSET\", 6);\n        yajl_gen_integer(g, file_hole_info->offset);\n\n        yajl_gen_string(g, (const unsigned char *) \"LENGTH\", 6);\n        yajl_gen_integer(g, file_hole_info->length);\n\n        yajl_gen_map_close(g);\n    }\n\n    yajl_gen_array_close(g);\n\n    cl = ngx_alloc_chain_link(tj_gen->pool);\n    if (cl == NULL) {\n        return NULL;\n    }\n    cl->next = NULL;\n\n    b = ngx_calloc_buf(tj_gen->pool);\n    if (b == NULL) {\n        return NULL;\n    }\n\n    yajl_gen_get_buf(g, (const unsigned char **) &b->pos, &size);\n    b->last = b->pos + size;\n    b->end = b->last;\n    b->temporary = 1;\n    b->flush = 1;\n    /* b->last_buf = 1; */\n    cl->buf = b;\n    return cl;\n}\n"
  },
  {
    "path": "modules/ngx_http_tfs_module/ngx_http_tfs_json.h",
    "content": "\n/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#ifndef _NGX_HTTP_TFS_JSON_H_INCLUDED_\n#define _NGX_HTTP_TFS_JSON_H_INCLUDED_\n\n\n#include <yajl/yajl_parse.h>\n#include <yajl/yajl_gen.h>\n#include <ngx_http_tfs_protocol.h>\n\n\ntypedef struct {\n    yajl_gen          gen;\n    ngx_log_t        *log;\n    ngx_pool_t       *pool;\n} ngx_http_tfs_json_gen_t;\n\n\nngx_http_tfs_json_gen_t *ngx_http_tfs_json_init(ngx_log_t *log,\n    ngx_pool_t *pool);\n\nvoid ngx_http_tfs_json_destroy(ngx_http_tfs_json_gen_t *tj_gen);\n\nngx_chain_t *ngx_http_tfs_json_custom_file_info(ngx_http_tfs_json_gen_t *tj_gen,\n    ngx_http_tfs_custom_meta_info_t *info, uint8_t file_type);\n\nngx_chain_t *ngx_http_tfs_json_file_name(ngx_http_tfs_json_gen_t *tj_gen,\n    ngx_str_t *file_name);\n\nngx_chain_t *ngx_http_tfs_json_raw_file_stat(ngx_http_tfs_json_gen_t *tj_gen,\n    u_char *file_name, uint32_t block_id,\n    ngx_http_tfs_raw_file_stat_t *file_stat);\n\nngx_chain_t * ngx_http_tfs_json_appid(ngx_http_tfs_json_gen_t *tj_gen,\n    uint64_t app_id);\nngx_chain_t * ngx_http_tfs_json_file_hole_info(ngx_http_tfs_json_gen_t *tj_gen,\n    ngx_array_t *file_holes);\n\n\n#endif  /* _NGX_HTTP_TFS_JSON_H_INCLUDED_ */\n"
  },
  {
    "path": "modules/ngx_http_tfs_module/ngx_http_tfs_local_block_cache.c",
    "content": "\n/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#include <ngx_http_tfs_local_block_cache.h>\n\n\nstatic void ngx_http_tfs_local_block_cache_rbtree_insert_value(\n    ngx_rbtree_node_t *temp, ngx_rbtree_node_t *node,\n    ngx_rbtree_node_t *sentinel);\n\n\nngx_int_t\nngx_http_tfs_local_block_cache_init_zone(ngx_shm_zone_t *shm_zone, void *data)\n{\n    size_t                                 len;\n    ngx_http_tfs_local_block_cache_ctx_t  *ctx;\n    ngx_http_tfs_local_block_cache_ctx_t  *octx = data;\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,\n                             sizeof(ngx_http_tfs_block_cache_shctx_t));\n    if (ctx->sh == NULL) {\n        return NGX_ERROR;\n    }\n\n    ctx->sh->discard_item_count = NGX_HTTP_TFS_BLOCK_CACHE_DISCARD_ITEM_COUNT;\n    ctx->sh->hit_count = 0;\n    ctx->sh->miss_count = 0;\n\n    ctx->shpool->data = ctx->sh;\n\n    ngx_rbtree_init(&ctx->sh->rbtree, &ctx->sh->sentinel,\n                    ngx_http_tfs_local_block_cache_rbtree_insert_value);\n\n    ngx_queue_init(&ctx->sh->queue);\n\n    len = sizeof(\" in tfs block cache 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 tfs block cache zone \\\"%V\\\"%Z\",\n                &shm_zone->shm.name);\n\n    return NGX_OK;\n}\n\n\nvoid\nngx_http_tfs_local_block_cache_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_tfs_block_cache_node_t  *tbn, *tbnt;\n\n    for ( ;; ) {\n\n        if (node->key < temp->key) {\n            p = &temp->left;\n\n        } else if (node->key > temp->key) {\n            p = &temp->right;\n\n        } else {\n            /* node->key == temp->key */\n            tbn = (ngx_http_tfs_block_cache_node_t *) &node->color;\n            tbnt = (ngx_http_tfs_block_cache_node_t *) &temp->color;\n\n            p = (ngx_http_tfs_block_cache_cmp(&tbn->key, &tbnt->key) < 0)\n                 ? &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\nngx_int_t\nngx_http_tfs_local_block_cache_lookup(ngx_http_tfs_local_block_cache_ctx_t *ctx,\n    ngx_pool_t *pool, ngx_log_t *log, ngx_http_tfs_block_cache_key_t* key,\n    ngx_http_tfs_block_cache_value_t *value)\n{\n    double                            hit_ratio;\n    ngx_int_t                         rc;\n    ngx_uint_t                        hash;\n    ngx_slab_pool_t                  *shpool;\n    ngx_rbtree_node_t                *node, *sentinel;\n    ngx_http_tfs_block_cache_node_t  *bcn;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, log, 0,\n                   \"lookup local block cache, ns addr: %uL, block id: %uD\",\n                   key->ns_addr, key->block_id);\n\n    shpool = ctx->shpool;\n    ngx_shmtx_lock(&shpool->mutex);\n\n    node = ctx->sh->rbtree.root;\n    sentinel = ctx->sh->rbtree.sentinel;\n\n    hash = ngx_murmur_hash2((u_char*)key, NGX_HTTP_TFS_BLOCK_CACHE_KEY_SIZE);\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        bcn = (ngx_http_tfs_block_cache_node_t *) &node->color;\n        rc = ngx_http_tfs_block_cache_cmp(key, &bcn->key);\n        if (rc == 0) {\n            value->ds_count = bcn->count;\n            value->ds_addrs = ngx_pcalloc(pool,\n                                          value->ds_count * sizeof(uint64_t));\n            if (value->ds_addrs == NULL) {\n                ngx_shmtx_unlock(&shpool->mutex);\n                return NGX_ERROR;\n            }\n            ngx_memcpy(value->ds_addrs, bcn->data,\n                       value->ds_count * sizeof(uint64_t));\n            ngx_queue_remove(&bcn->queue);\n            ngx_queue_insert_head(&ctx->sh->queue, &bcn->queue);\n            ctx->sh->hit_count++;\n            if (ctx->sh->hit_count >= NGX_HTTP_TFS_BLOCK_CACHE_STAT_COUNT) {\n                hit_ratio = 100 * (double)((double)ctx->sh->hit_count\n                                           / (double)(ctx->sh->hit_count\n                                                      + ctx->sh->miss_count));\n                ngx_log_error(NGX_LOG_INFO, log, 0,\n                              \"local block cache hit_ratio: %.2f%%\",\n                              hit_ratio);\n                ctx->sh->hit_count = 0;\n                ctx->sh->miss_count = 0;\n            }\n            ngx_shmtx_unlock(&shpool->mutex);\n            return NGX_OK;\n        }\n        node = (rc < 0) ? node->left : node->right;\n    }\n    ctx->sh->miss_count++;\n    ngx_shmtx_unlock(&shpool->mutex);\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, log, 0,\n                   \"lookup local block cache, \"\n                   \"ns addr: %uL, block id: %uD not found\",\n                   key->ns_addr, key->block_id);\n\n    return NGX_DECLINED;\n}\n\n\nngx_int_t\nngx_http_tfs_local_block_cache_insert(ngx_http_tfs_local_block_cache_ctx_t *ctx,\n    ngx_log_t *log, ngx_http_tfs_block_cache_key_t *key,\n    ngx_http_tfs_block_cache_value_t *value)\n{\n    size_t                            n;\n    ngx_slab_pool_t                  *shpool;\n    ngx_rbtree_node_t                *node;\n    ngx_http_tfs_block_cache_node_t  *bcn;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, log, 0,\n                   \"insert local block cache, ns addr: %uL, block id: %uD\",\n                   key->ns_addr, key->block_id);\n\n    shpool = ctx->shpool;\n\n    ngx_shmtx_lock(&shpool->mutex);\n\n    n = offsetof(ngx_rbtree_node_t, color)\n        + offsetof(ngx_http_tfs_block_cache_node_t, data)\n        + value->ds_count * sizeof(uint64_t);\n\n    node = ngx_slab_alloc_locked(shpool, n);\n    if (node == NULL) { // full, discard\n        ngx_http_tfs_local_block_cache_discard(ctx);\n        node = ngx_slab_alloc_locked(shpool, n);\n        if (node == NULL) {\n            ngx_shmtx_unlock(&shpool->mutex);\n            return NGX_ERROR;\n        }\n    }\n\n    bcn = (ngx_http_tfs_block_cache_node_t *) &node->color;\n\n    node->key = ngx_murmur_hash2((u_char*)key,\n                                 NGX_HTTP_TFS_BLOCK_CACHE_KEY_SIZE);\n    ngx_memcpy(&bcn->key, key, NGX_HTTP_TFS_BLOCK_CACHE_KEY_SIZE);\n    bcn->len = (u_char) value->ds_count * sizeof(uint64_t);\n    bcn->count = value->ds_count;\n    ngx_memcpy(bcn->data, value->ds_addrs, bcn->len);\n\n    ngx_rbtree_insert(&(ctx->sh->rbtree), node);\n    ngx_queue_insert_head(&ctx->sh->queue, &bcn->queue);\n\n    ngx_shmtx_unlock(&shpool->mutex);\n\n    return NGX_OK;\n}\n\n\nvoid\nngx_http_tfs_local_block_cache_remove(ngx_http_tfs_local_block_cache_ctx_t *ctx,\n    ngx_log_t *log, ngx_http_tfs_block_cache_key_t* key)\n{\n    ngx_int_t                         rc;\n    ngx_uint_t                        hash;\n    ngx_slab_pool_t                  *shpool;\n    ngx_rbtree_node_t                *node, *sentinel;\n    ngx_http_tfs_block_cache_node_t  *bcn;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, log, 0,\n                   \"remove local block cache, ns addr: %uL, block id: %uD\",\n                   key->ns_addr, key->block_id);\n\n    shpool = ctx->shpool;\n    ngx_shmtx_lock(&shpool->mutex);\n\n    node = ctx->sh->rbtree.root;\n    sentinel = ctx->sh->rbtree.sentinel;\n\n    hash = ngx_murmur_hash2((u_char*)key, NGX_HTTP_TFS_BLOCK_CACHE_KEY_SIZE);\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        do {\n            bcn = (ngx_http_tfs_block_cache_node_t *) &node->color;\n            rc = ngx_http_tfs_block_cache_cmp(key, &bcn->key);\n            if (rc == 0) {\n                ngx_rbtree_delete(&ctx->sh->rbtree, node);\n                ngx_slab_free_locked(ctx->shpool, node);\n                ngx_queue_remove(&bcn->queue);\n                ngx_shmtx_unlock(&shpool->mutex);\n                return;\n            }\n            node = (rc < 0) ? node->left : node->right;\n        } while (node != sentinel && hash == node->key);\n        break;\n    }\n    ngx_shmtx_unlock(&shpool->mutex);\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, log, 0,\n                   \"remove local block cache, \"\n                   \"ns addr: %uL, block id: %uD not found\",\n                   key->ns_addr, key->block_id);\n\n}\n\n\nvoid\nngx_http_tfs_local_block_cache_discard(\n    ngx_http_tfs_local_block_cache_ctx_t *ctx)\n{\n    ngx_uint_t                        i;\n    ngx_queue_t                      *q, *h, *p;\n    ngx_rbtree_node_t                *node;\n    ngx_http_tfs_block_cache_node_t  *bcn;\n\n    h = &ctx->sh->queue;\n    if (ngx_queue_empty(h)) {\n        return;\n    }\n    q = ngx_queue_last(h);\n\n    for (i = 0; i < ctx->sh->discard_item_count; i++) {\n        if (q == ngx_queue_sentinel(h)) {\n            ngx_queue_init(h);\n            return;\n        }\n\n        p = ngx_queue_prev(q);\n\n        bcn = ngx_queue_data(q, ngx_http_tfs_block_cache_node_t, queue);\n\n        node = (ngx_rbtree_node_t *)\n            ((u_char *) bcn - 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        q = p;\n    }\n\n    q->next = h;\n    h->prev = q;\n}\n\n\nngx_int_t\nngx_http_tfs_local_block_cache_batch_lookup(\n    ngx_http_tfs_local_block_cache_ctx_t *ctx,\n    ngx_pool_t *pool, ngx_log_t *log, ngx_array_t *keys, ngx_array_t *kvs)\n{\n    double                           hit_ratio;\n    ngx_int_t                        rc;\n    ngx_uint_t                       i, hash, hit_count;\n    ngx_slab_pool_t                  *shpool;\n    ngx_rbtree_node_t                *node, *sentinel;\n    ngx_http_tfs_block_cache_kv_t    *kv;\n    ngx_http_tfs_block_cache_key_t   *key;\n    ngx_http_tfs_block_cache_node_t  *bcn;\n    ngx_http_tfs_block_cache_value_t *value;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,\n                   \"batch lookup local block cache, block count: %ui\",\n                   keys->nelts);\n\n    key = keys->elts;\n    shpool = ctx->shpool;\n    rc = NGX_ERROR;\n    ngx_shmtx_lock(&shpool->mutex);\n\n    sentinel = ctx->sh->rbtree.sentinel;\n    hit_count = 0;\n\n    for (i = 0; i < keys->nelts; i++, key++) {\n        node = ctx->sh->rbtree.root;\n        hash = ngx_murmur_hash2((u_char*)key,\n                                NGX_HTTP_TFS_BLOCK_CACHE_KEY_SIZE);\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            bcn = (ngx_http_tfs_block_cache_node_t *) &node->color;\n            rc = ngx_http_tfs_block_cache_cmp(key, &bcn->key);\n            if (rc == 0) {\n                value = ngx_pcalloc(pool,\n                                    sizeof(ngx_http_tfs_block_cache_value_t));\n                if (value == NULL) {\n                    ngx_shmtx_unlock(&shpool->mutex);\n                    return NGX_ERROR;\n                }\n\n                value->ds_count = bcn->count;\n                value->ds_addrs = ngx_pcalloc(pool,\n                                            value->ds_count * sizeof(uint64_t));\n                if (value->ds_addrs == NULL) {\n                    ngx_shmtx_unlock(&shpool->mutex);\n                    return NGX_ERROR;\n                }\n                ngx_memcpy(value->ds_addrs, bcn->data,\n                           value->ds_count * sizeof(uint64_t));\n\n                kv = (ngx_http_tfs_block_cache_kv_t *)ngx_array_push(kvs);\n                kv->key = key;\n                kv->value = value;\n\n                ngx_queue_remove(&bcn->queue);\n                ngx_queue_insert_head(&ctx->sh->queue, &bcn->queue);\n                hit_count++;\n                break;\n            }\n            node = (rc < 0) ? node->left : node->right;\n        }\n\n        if (node == sentinel) {\n            ctx->sh->miss_count++;\n        }\n    }\n\n    ctx->sh->hit_count += hit_count;\n    if (ctx->sh->hit_count >= NGX_HTTP_TFS_BLOCK_CACHE_STAT_COUNT) {\n        hit_ratio = 100 * (double)((double)ctx->sh->hit_count\n                                   / (double)(ctx->sh->hit_count\n                                              + ctx->sh->miss_count));\n        ngx_log_error(NGX_LOG_INFO, log, 0,\n                      \"local block cache hit_ratio: %.2f%%\",\n                      hit_ratio);\n        ctx->sh->hit_count = 0;\n        ctx->sh->miss_count = 0;\n    }\n\n    ngx_shmtx_unlock(&shpool->mutex);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,\n                   \"batch lookup local block cache, hit_count: %ui\",\n                   kvs->nelts);\n\n    /* not all hit */\n    if (hit_count < keys->nelts) {\n        rc = NGX_DECLINED;\n    }\n\n    return rc;\n}\n"
  },
  {
    "path": "modules/ngx_http_tfs_module/ngx_http_tfs_local_block_cache.h",
    "content": "\n/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#ifndef _NGX_HTTP_TFS_LOCAL_BLOCK_CACHE_H_INCLUDED_\n#define _NGX_HTTP_TFS_LOCAL_BLOCK_CACHE_H_INCLUDED_\n\n#include <ngx_http_tfs_block_cache.h>\n\n\ntypedef struct {\n    u_char                                  color;\n    u_char                                  dummy;\n    ngx_queue_t                             queue;\n\n    ngx_http_tfs_block_cache_key_t          key;\n\n    u_char                                  len;\n    u_short                                 count;\n    u_char                                  data[1];\n} ngx_http_tfs_block_cache_node_t;\n\n\nngx_int_t ngx_http_tfs_local_block_cache_init_zone(ngx_shm_zone_t *shm_zone,\n    void *data);\n\nngx_int_t ngx_http_tfs_local_block_cache_lookup(\n    ngx_http_tfs_local_block_cache_ctx_t *ctx,\n    ngx_pool_t *pool, ngx_log_t *log,\n    ngx_http_tfs_block_cache_key_t *key,\n    ngx_http_tfs_block_cache_value_t *value);\n\nngx_int_t ngx_http_tfs_local_block_cache_insert(\n    ngx_http_tfs_local_block_cache_ctx_t *ctx,\n    ngx_log_t *log, ngx_http_tfs_block_cache_key_t *key,\n    ngx_http_tfs_block_cache_value_t *value);\n\nvoid ngx_http_tfs_local_block_cache_remove(\n    ngx_http_tfs_local_block_cache_ctx_t *ctx,\n    ngx_log_t *log, ngx_http_tfs_block_cache_key_t *key);\n\nvoid ngx_http_tfs_local_block_cache_discard(\n    ngx_http_tfs_local_block_cache_ctx_t *ctx);\n\nngx_int_t ngx_http_tfs_local_block_cache_batch_lookup(\n    ngx_http_tfs_local_block_cache_ctx_t *ctx,\n    ngx_pool_t *pool, ngx_log_t *log, ngx_array_t *keys, ngx_array_t *kvs);\n\n\n\n#endif /* _NGX_HTTP_TFS_LOCAL_BLOCK_CACHE_H_INCLUDED_ */\n"
  },
  {
    "path": "modules/ngx_http_tfs_module/ngx_http_tfs_meta_server_message.c",
    "content": "\n/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#include <ngx_http_tfs_meta_server_message.h>\n#include <ngx_http_tfs_json.h>\n#include <ngx_http_tfs_protocol.h>\n#include <ngx_http_tfs_errno.h>\n#include <ngx_http_tfs_duplicate.h>\n\n\nstatic ngx_chain_t *ngx_http_tfs_create_write_meta_message(ngx_http_tfs_t *t);\nstatic ngx_chain_t *ngx_http_tfs_create_read_meta_message(ngx_http_tfs_t *t,\n    int64_t req_offset, uint64_t req_size);\nstatic ngx_chain_t *ngx_http_tfs_create_action_message(ngx_http_tfs_t *t,\n    ngx_str_t *file_path_s, ngx_str_t *file_path_d);\nstatic ngx_chain_t *ngx_http_tfs_create_ls_message(ngx_http_tfs_t *t);\n\nstatic ngx_int_t ngx_http_tfs_parse_write_meta_message(ngx_http_tfs_t *t);\nstatic ngx_int_t ngx_http_tfs_parse_read_meta_message(ngx_http_tfs_t *t);\nstatic ngx_int_t ngx_http_tfs_parse_action_message(ngx_http_tfs_t *t);\nstatic ngx_int_t ngx_http_tfs_parse_ls_message(ngx_http_tfs_t *t);\n\n\nngx_http_tfs_inet_t *\nngx_http_tfs_select_meta_server(ngx_http_tfs_t *t)\n{\n    uint32_t                hash, index;\n    ngx_http_tfs_meta_hh_t  h;\n\n    h.app_id = ngx_hton64(t->r_ctx.app_id);\n    h.user_id = ngx_hton64(t->r_ctx.user_id);\n\n    hash = ngx_http_tfs_murmur_hash((u_char *) &h,\n                                    sizeof(ngx_http_tfs_meta_hh_t));\n\n    index = hash % NGX_HTTP_TFS_METASERVER_COUNT;\n\n    return &(t->loc_conf->meta_server_table.table[index]);\n}\n\n\nngx_chain_t *\nngx_http_tfs_meta_server_create_message(ngx_http_tfs_t *t)\n{\n    uint16_t      msg_type;\n    ngx_chain_t  *cl;\n\n    cl = NULL;\n    msg_type = t->r_ctx.action.code;\n\n    switch (msg_type) {\n\n    case NGX_HTTP_TFS_ACTION_CREATE_DIR:\n    case NGX_HTTP_TFS_ACTION_CREATE_FILE:\n        ngx_log_error(NGX_LOG_DEBUG, t->log, 0,\n                      \"will create path: \"\n                      \"last_dir_level: %i, dir_len: %i, last_file_path: %V\",\n                      t->last_dir_level,\n                      t->last_file_path.len,\n                      &t->last_file_path);\n        cl = ngx_http_tfs_create_action_message(t, &t->last_file_path, NULL);\n        break;\n\n    case NGX_HTTP_TFS_ACTION_MOVE_DIR:\n    case NGX_HTTP_TFS_ACTION_MOVE_FILE:\n        cl = ngx_http_tfs_create_action_message(t, &t->r_ctx.file_path_s,\n                                                &t->last_file_path);\n        break;\n\n    case NGX_HTTP_TFS_ACTION_REMOVE_DIR:\n        cl = ngx_http_tfs_create_action_message(t, &t->r_ctx.file_path_s, NULL);\n        break;\n\n    case NGX_HTTP_TFS_ACTION_READ_FILE:\n        cl = ngx_http_tfs_create_read_meta_message(t, t->file.file_offset,\n                                                   t->file.left_length);\n        break;\n\n    case NGX_HTTP_TFS_ACTION_WRITE_FILE:\n        switch (t->state) {\n        case NGX_HTTP_TFS_STATE_WRITE_CLUSTER_ID_MS:\n            cl = ngx_http_tfs_create_read_meta_message(t, 0, 0);\n            break;\n        case NGX_HTTP_TFS_STATE_WRITE_WRITE_MS:\n            cl = ngx_http_tfs_create_write_meta_message(t);\n            break;\n        }\n        break;\n\n    case NGX_HTTP_TFS_ACTION_REMOVE_FILE:\n        switch (t->state) {\n        case NGX_HTTP_TFS_STATE_REMOVE_GET_FRAG_INFO:\n            cl = ngx_http_tfs_create_read_meta_message(t, t->file.file_offset,\n                                                       t->file.left_length);\n            break;\n        case NGX_HTTP_TFS_STATE_REMOVE_NOTIFY_MS:\n            cl = ngx_http_tfs_create_action_message(t, &t->r_ctx.file_path_s,\n                                                    NULL);\n            break;\n        }\n        break;\n\n    case NGX_HTTP_TFS_ACTION_LS_DIR:\n    case NGX_HTTP_TFS_ACTION_LS_FILE:\n        t->json_output = ngx_http_tfs_json_init(t->log, t->pool);\n        if (t->json_output == NULL) {\n            return NULL;\n        }\n        cl = ngx_http_tfs_create_ls_message(t);\n        break;\n    }\n\n    return cl;\n}\n\n\nngx_int_t\nngx_http_tfs_meta_server_parse_message(ngx_http_tfs_t *t)\n{\n    uint16_t  action;\n\n    action = t->r_ctx.action.code;\n\n    switch (action) {\n\n    case NGX_HTTP_TFS_ACTION_CREATE_DIR:\n    case NGX_HTTP_TFS_ACTION_CREATE_FILE:\n    case NGX_HTTP_TFS_ACTION_REMOVE_DIR:\n    case NGX_HTTP_TFS_ACTION_MOVE_DIR:\n    case NGX_HTTP_TFS_ACTION_MOVE_FILE:\n        return ngx_http_tfs_parse_action_message(t);\n\n    case NGX_HTTP_TFS_ACTION_REMOVE_FILE:\n        switch (t->state) {\n        case NGX_HTTP_TFS_STATE_REMOVE_GET_FRAG_INFO:\n            return ngx_http_tfs_parse_read_meta_message(t);\n        case NGX_HTTP_TFS_STATE_REMOVE_NOTIFY_MS:\n            return ngx_http_tfs_parse_action_message(t);\n        default:\n            return NGX_ERROR;\n        }\n\n    case NGX_HTTP_TFS_ACTION_LS_DIR:\n    case NGX_HTTP_TFS_ACTION_LS_FILE:\n        return ngx_http_tfs_parse_ls_message(t);\n\n    case NGX_HTTP_TFS_ACTION_READ_FILE:\n        return ngx_http_tfs_parse_read_meta_message(t);\n\n    case NGX_HTTP_TFS_ACTION_WRITE_FILE:\n        switch (t->state) {\n        case NGX_HTTP_TFS_STATE_WRITE_CLUSTER_ID_MS:\n            return ngx_http_tfs_parse_read_meta_message(t);\n\n        case NGX_HTTP_TFS_STATE_WRITE_WRITE_MS:\n            return ngx_http_tfs_parse_write_meta_message(t);\n\n        default:\n            return NGX_ERROR;\n        }\n    default:\n        return NGX_ERROR;\n    }\n\n    return NGX_ERROR;\n}\n\n\nstatic ngx_chain_t *\nngx_http_tfs_create_write_meta_message(ngx_http_tfs_t *t)\n{\n    u_char                             *p;\n    size_t                              size, frag_size;\n    ngx_buf_t                          *b;\n    ngx_int_t                           need_write_frag_count, i;\n    ngx_chain_t                        *cl;\n    ngx_http_tfs_restful_ctx_t         *r_ctx;\n    ngx_http_tfs_segment_data_t        *segment_data;\n    ngx_http_tfs_meta_frag_info_t      *wfi;\n    ngx_http_tfs_ms_base_msg_header_t  *req;\n\n    r_ctx = &t->r_ctx;\n    need_write_frag_count =\n        t->file.segment_index - t->file.last_write_segment_index;\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, t->log, 0 ,\n                   \"last_write_segment_index: %uD, segment_index: %uD\",\n                   t->file.last_write_segment_index, t->file.segment_index);\n\n    frag_size = sizeof(ngx_http_tfs_meta_frag_info_t) +\n        sizeof(ngx_http_tfs_meta_frag_meta_info_t) * need_write_frag_count;\n\n    size = sizeof(ngx_http_tfs_ms_base_msg_header_t) +\n        r_ctx->file_path_s.len + 1 +\n        /* version */\n        sizeof(uint64_t) +\n        frag_size;\n\n    b = ngx_create_temp_buf(t->pool, size);\n    if (b == NULL) {\n        return NULL;\n    }\n\n    req = (ngx_http_tfs_ms_base_msg_header_t *) b->pos;\n    req->header.type = NGX_HTTP_TFS_WRITE_FILEPATH_MESSAGE;\n    req->header.flag = NGX_HTTP_TFS_PACKET_FLAG;\n    req->header.version = NGX_HTTP_TFS_PACKET_VERSION;\n    req->header.id = ngx_http_tfs_generate_packet_id();\n    req->app_id = r_ctx->app_id;\n    req->user_id = r_ctx->user_id;\n    req->file_len = r_ctx->file_path_s.len + 1;\n    p = ngx_cpymem(req->file_path_s, r_ctx->file_path_s.data,\n                   r_ctx->file_path_s.len + 1);\n\n    *((uint64_t *)p) = t->loc_conf->meta_server_table.version;\n\n    wfi = (ngx_http_tfs_meta_frag_info_t*)(p + sizeof(uint64_t));\n    wfi->cluster_id = t->file.cluster_id;\n    wfi->frag_count = need_write_frag_count;\n    segment_data = &t->file.segment_data[t->file.last_write_segment_index];\n    for (i = 0; i < need_write_frag_count; i++) {\n#if (NGX_DEBUG)\n        ngx_http_tfs_dump_segment_data(segment_data, t->log);\n#endif\n        wfi->frag_meta[i].block_id = segment_data->segment_info.block_id;\n        wfi->frag_meta[i].file_id = segment_data->segment_info.file_id;\n        wfi->frag_meta[i].offset = segment_data->segment_info.offset;\n        wfi->frag_meta[i].size = segment_data->segment_info.size;\n        segment_data++;\n    }\n    t->file.last_write_segment_index += need_write_frag_count;\n\n    b->last += size;\n\n    req->header.len = size - sizeof(ngx_http_tfs_header_t);\n    req->header.crc = ngx_http_tfs_crc(NGX_HTTP_TFS_PACKET_FLAG,\n                                       (const char *) (&req->header + 1),\n                                       size - sizeof(ngx_http_tfs_header_t));\n\n    cl = ngx_alloc_chain_link(t->pool);\n    if (cl == NULL) {\n        return NULL;\n    }\n\n    cl->buf = b;\n    cl->next = NULL;\n\n    return cl;\n}\n\n\nstatic ngx_chain_t *\nngx_http_tfs_create_read_meta_message(ngx_http_tfs_t *t, int64_t req_offset,\n    uint64_t req_size)\n{\n    u_char                             *p;\n    size_t                              size, max_frag_count, req_frag_count;\n    ngx_buf_t                          *b;\n    ngx_chain_t                        *cl;\n    ngx_http_tfs_restful_ctx_t         *r_ctx;\n    ngx_http_tfs_ms_base_msg_header_t  *req;\n\n    r_ctx = &t->r_ctx;\n\n    size = sizeof(ngx_http_tfs_ms_base_msg_header_t) +\n        /* file */\n        r_ctx->file_path_s.len +\n        /* \\0 */\n        1 +\n        /* version */\n        sizeof(uint64_t) +\n        /* offset */\n        sizeof(uint64_t) +\n        /* size */\n        sizeof(uint64_t);\n\n    b = ngx_create_temp_buf(t->pool, size);\n    if (b == NULL) {\n        return NULL;\n    }\n\n    req = (ngx_http_tfs_ms_base_msg_header_t *) b->pos;\n    req->header.type = NGX_HTTP_TFS_READ_FILEPATH_MESSAGE;\n    req->header.len = size - sizeof(ngx_http_tfs_header_t);\n    req->header.flag = NGX_HTTP_TFS_PACKET_FLAG;\n    req->header.version = NGX_HTTP_TFS_PACKET_VERSION;\n    req->header.id = ngx_http_tfs_generate_packet_id();\n    req->app_id = r_ctx->app_id;\n    req->user_id = r_ctx->user_id;\n    req->file_len = r_ctx->file_path_s.len + 1;\n    p = ngx_cpymem(req->file_path_s, r_ctx->file_path_s.data,\n                   r_ctx->file_path_s.len + 1);\n\n    *((uint64_t *)p) = t->loc_conf->meta_server_table.version;\n    p += sizeof(uint64_t);\n\n    *((uint64_t *) p) = req_offset;\n    p += sizeof(uint64_t);\n\n    max_frag_count = (t->main_conf->body_buffer_size\n                      - sizeof(ngx_http_tfs_ms_read_response_t))\n        / sizeof(ngx_http_tfs_meta_frag_meta_info_t);\n    req_frag_count = req_size / (NGX_HTTP_TFS_MAX_FRAGMENT_SIZE);\n\n    ngx_log_error(NGX_LOG_INFO, t->log, 0 ,\n                  \"max_frag_count: %uz, req_frag_count: %uz, data size: %uz\",\n                  max_frag_count, req_frag_count, req_size);\n\n    if (req_frag_count > max_frag_count) {\n        *((uint64_t *) p) =\n            (max_frag_count - 1) * NGX_HTTP_TFS_MAX_FRAGMENT_SIZE;\n        t->has_split_frag = NGX_HTTP_TFS_YES;\n\n    } else {\n        *((uint64_t *) p) = req_size;\n        t->has_split_frag = NGX_HTTP_TFS_NO;\n    }\n\n    req->header.crc = ngx_http_tfs_crc(NGX_HTTP_TFS_PACKET_FLAG,\n                                       (const char *) (&req->header + 1),\n                                       req->header.len);\n\n    b->last += size;\n\n    cl = ngx_alloc_chain_link(t->pool);\n    if (cl == NULL) {\n        return NULL;\n    }\n\n    cl->buf = b;\n    cl->next = NULL;\n\n    return cl;\n}\n\n\nstatic ngx_chain_t *\nngx_http_tfs_create_action_message(ngx_http_tfs_t *t, ngx_str_t *file_path_s,\n    ngx_str_t *file_path_d)\n{\n    size_t                              size;\n    u_char                             *p;\n    ngx_buf_t                          *b;\n    ngx_chain_t                        *cl;\n    ngx_http_tfs_restful_ctx_t         *r_ctx;\n    ngx_http_tfs_ms_base_msg_header_t  *req;\n\n    r_ctx = &t->r_ctx;\n\n    size = sizeof(ngx_http_tfs_ms_base_msg_header_t) +\n        /* file path */\n        file_path_s->len +\n        /* version */\n        sizeof(uint64_t) +\n        /* new file path len */\n        sizeof(uint32_t) +\n        /* '/0' */\n        1 +\n        /* action */\n        sizeof(uint8_t);\n\n    if (file_path_d != NULL && file_path_d->data != NULL) {\n        size += file_path_d->len + 1;\n    }\n\n    b = ngx_create_temp_buf(t->pool, size);\n    if (b == NULL) {\n        return NULL;\n    }\n\n    req = (ngx_http_tfs_ms_base_msg_header_t *) b->pos;\n    req->header.type = NGX_HTTP_TFS_FILEPATH_ACTION_MESSAGE;\n    req->header.len = size - sizeof(ngx_http_tfs_header_t);\n    req->header.flag = NGX_HTTP_TFS_PACKET_FLAG;\n    req->header.version = NGX_HTTP_TFS_PACKET_VERSION;\n    req->header.id = ngx_http_tfs_generate_packet_id();\n    req->app_id = r_ctx->app_id;\n    req->user_id = r_ctx->user_id;\n    req->file_len = file_path_s->len + 1;\n    p = ngx_cpymem(req->file_path_s, file_path_s->data, file_path_s->len + 1);\n\n    *((uint64_t *)p) = t->loc_conf->meta_server_table.version;\n    p += sizeof(uint64_t);\n\n    if (file_path_d != NULL && file_path_d->data != NULL) {\n        /* new file path */\n        *((uint32_t *)p) = file_path_d->len + 1;\n        p += sizeof(uint32_t);\n        p = ngx_cpymem(p, file_path_d->data, file_path_d->len + 1);\n\n    } else {\n        *((uint32_t *)p) = 0;\n        p += sizeof(uint32_t);\n    }\n\n    /* start body */\n    *p = r_ctx->action.code;\n\n    req->header.crc = ngx_http_tfs_crc(NGX_HTTP_TFS_PACKET_FLAG,\n                                       (const char *) (&req->header + 1),\n                                       req->header.len);\n    b->last += size;\n\n    cl = ngx_alloc_chain_link(t->pool);\n    if (cl == NULL) {\n        return NULL;\n    }\n\n    cl->buf = b;\n    cl->next = NULL;\n\n    return cl;\n}\n\n\nstatic ngx_chain_t *\nngx_http_tfs_create_ls_message(ngx_http_tfs_t *t)\n{\n    size_t                            size;\n    u_char                           *p;\n    ngx_buf_t                        *b;\n    ngx_chain_t                      *cl;\n    ngx_http_tfs_restful_ctx_t       *r_ctx;\n    ngx_http_tfs_ms_ls_msg_header_t  *req;\n\n    r_ctx = &t->r_ctx;\n\n    size = sizeof(ngx_http_tfs_ms_ls_msg_header_t) +\n        /* file path */\n        t->last_file_path.len +\n        /* '/0' */\n        1 +\n        /* file type */\n        sizeof(uint8_t) +\n        /* version */\n        sizeof(uint64_t);\n\n    b = ngx_create_temp_buf(t->pool, size);\n    if (b == NULL) {\n        return NULL;\n    }\n\n    req = (ngx_http_tfs_ms_ls_msg_header_t *) b->pos;\n    req->header.type = NGX_HTTP_TFS_LS_FILEPATH_MESSAGE;\n    req->header.len = size - sizeof(ngx_http_tfs_header_t);\n    req->header.flag = NGX_HTTP_TFS_PACKET_FLAG;\n    req->header.version = NGX_HTTP_TFS_PACKET_VERSION;\n    req->header.id = ngx_http_tfs_generate_packet_id();\n    req->app_id = r_ctx->app_id;\n    req->user_id = r_ctx->user_id;\n    req->file_len = t->last_file_path.len + 1;\n    req->pid = t->last_file_pid;\n    p = ngx_cpymem(req->file_path, t->last_file_path.data,\n                   t->last_file_path.len + 1);\n\n    *p = t->last_file_type;\n    p += sizeof(uint8_t);\n\n    *((uint64_t *)p) = t->loc_conf->meta_server_table.version;\n\n    req->header.crc = ngx_http_tfs_crc(NGX_HTTP_TFS_PACKET_FLAG,\n                                       (const char *) (&req->header + 1),\n                                       req->header.len);\n    b->last += size;\n\n    cl = ngx_alloc_chain_link(t->pool);\n    if (cl == NULL) {\n        return NULL;\n    }\n\n    cl->buf = b;\n    cl->next = NULL;\n\n    return cl;\n}\n\n\nstatic ngx_int_t\nngx_http_tfs_parse_write_meta_message(ngx_http_tfs_t *t)\n{\n    uint16_t                        type;\n    ngx_str_t                       action;\n    ngx_http_tfs_header_t           *header;\n    ngx_http_tfs_peer_connection_t  *tp;\n\n    header = (ngx_http_tfs_header_t *) t->header;\n    tp = t->tfs_peer;\n    type = header->type;\n\n    switch (type) {\n\n    case NGX_HTTP_TFS_STATUS_MESSAGE:\n        ngx_str_set(&action, \"write message (meta server)\");\n        return ngx_http_tfs_status_message(&tp->body_buffer, &action, t->log);\n    default:\n        ngx_log_error(NGX_LOG_WARN, t->log, 0,\n                      \" file type is %d \", type);\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_tfs_parse_read_meta_message(ngx_http_tfs_t *t)\n{\n    u_char                              *p;\n    int64_t                              curr_length;\n    uint16_t                             type;\n    uint32_t                             count;\n    uint64_t                             end_offset;\n    ngx_int_t                            rc;\n    ngx_str_t                            action;\n    ngx_uint_t                           i;\n    ngx_http_tfs_header_t               *header;\n    ngx_http_tfs_segment_data_t         *first_segment, *last_segment,\n                                        *segment_data;\n    ngx_http_tfs_file_hole_info_t       *file_hole_info;\n    ngx_http_tfs_peer_connection_t      *tp;\n    ngx_http_tfs_ms_read_response_t     *resp;\n    ngx_http_tfs_meta_frag_meta_info_t  *fmi;\n\n    header = (ngx_http_tfs_header_t *) t->header;\n    tp = t->tfs_peer;\n    type = header->type;\n\n    switch (type) {\n    case NGX_HTTP_TFS_STATUS_MESSAGE:\n        ngx_str_set(&action, \"read file(meta server)\");\n        return ngx_http_tfs_status_message(&tp->body_buffer, &action, t->log);\n    }\n\n    resp = (ngx_http_tfs_ms_read_response_t *) tp->body_buffer.pos;\n\n    count = resp->frag_info.frag_count & ~(1 << (sizeof(uint32_t) * 8 - 1));\n\n    t->file.cluster_id = resp->frag_info.cluster_id;\n\n    if (t->r_ctx.action.code == NGX_HTTP_TFS_ACTION_WRITE_FILE) {\n        return NGX_OK;\n    }\n\n    if (count == 0) {\n        return NGX_DECLINED;\n    }\n\n    if (t->file.segment_data == NULL) {\n        t->file.segment_data =\n            ngx_pcalloc(t->pool, sizeof(ngx_http_tfs_segment_data_t) * count);\n        if (t->file.segment_data == NULL) {\n            return NGX_ERROR;\n        }\n        /* the first semgent offset is special for pread */\n        t->is_first_segment = NGX_HTTP_TFS_YES;\n\n    } else {\n        /* need realloc */\n        if (count > t->file.segment_count) {\n            t->file.segment_data = ngx_http_tfs_prealloc(t->pool,\n                          t->file.segment_data,\n                          sizeof(ngx_http_tfs_segment_data_t)\n                           * t->file.segment_count,\n                          sizeof(ngx_http_tfs_segment_data_t) * count);\n            if (t->file.segment_data == NULL) {\n                return NGX_ERROR;\n            }\n        }\n        /* reuse */\n        ngx_memzero(t->file.segment_data,\n                    sizeof(ngx_http_tfs_segment_data_t) * count);\n    }\n\n    t->file.segment_count = count;\n    t->file.still_have = resp->still_have ? :t->has_split_frag;\n    t->file.segment_index = 0;\n\n    p = tp->body_buffer.pos + sizeof(ngx_http_tfs_ms_read_response_t);\n    fmi = (ngx_http_tfs_meta_frag_meta_info_t *) p;\n\n    for (i = 0; i < count; i++, fmi++) {\n        t->file.segment_data[i].segment_info.block_id = fmi->block_id;\n        t->file.segment_data[i].segment_info.file_id = fmi->file_id;\n        t->file.segment_data[i].segment_info.offset = fmi->offset;\n        t->file.segment_data[i].segment_info.size = fmi->size;\n        t->file.segment_data[i].oper_size = fmi->size;\n    }\n\n    /* the first semgent's oper_offset and oper_size are special for pread */\n    if (t->r_ctx.action.code == NGX_HTTP_TFS_ACTION_READ_FILE) {\n        first_segment = &t->file.segment_data[0];\n        if (t->is_first_segment) {\n            /* skip file hole */\n            first_segment->oper_offset =\n                ngx_max(t->r_ctx.offset, first_segment->segment_info.offset);\n            if (first_segment->segment_info.offset > 0) {\n                first_segment->oper_offset %=first_segment->segment_info.offset;\n            }\n            first_segment->oper_size =\n                first_segment->segment_info.size - first_segment->oper_offset;\n            t->is_first_segment = NGX_HTTP_TFS_NO;\n            if (t->r_ctx.chk_file_hole) {\n                rc = ngx_array_init(&t->file_holes, t->pool,\n                                    NGX_HTTP_TFS_INIT_FILE_HOLE_COUNT,\n                                    sizeof(ngx_http_tfs_file_hole_info_t));\n                if (rc == NGX_ERROR) {\n                    return NGX_ERROR;\n                }\n            }\n        }\n\n        /* last segment(also special) has been readed, set its oper_size*/\n        /* notice that it maybe the same as first_segment */\n        if (!t->file.still_have) {\n            last_segment = &t->file.segment_data[count - 1];\n            end_offset = t->file.file_offset + t->file.left_length;\n            if (end_offset\n                > ((uint64_t)last_segment->segment_info.offset\n                   + last_segment->oper_offset))\n            {\n                last_segment->oper_size =\n                    ngx_min((end_offset - (last_segment->segment_info.offset\n                                           + last_segment->oper_offset)),\n                            last_segment->segment_info.size);\n\n            } else { /* end_offset in file hole */\n                last_segment->oper_size = 0;\n            }\n        }\n\n        /* check file hole */\n        if (t->r_ctx.chk_file_hole) {\n            segment_data = t->file.segment_data;\n            for (i = 0; i < count; i++, segment_data++) {\n                /* must be file hole, add to array */\n                if (t->file.file_offset < segment_data->segment_info.offset) {\n                    curr_length =\n                        ngx_min(t->file.left_length,\n                                (uint64_t)(segment_data->segment_info.offset\n                                           - t->file.file_offset));\n                    file_hole_info = ngx_array_push(&t->file_holes);\n                    if (file_hole_info == NULL) {\n                        return NGX_ERROR;\n                    }\n\n                    file_hole_info->offset = t->file.file_offset;\n                    file_hole_info->length = curr_length;\n\n                    ngx_log_error(NGX_LOG_DEBUG, t->log, 0,\n                                  \"find file hole, offset: %uL, length: %uL\",\n                                  file_hole_info->offset,\n                                  file_hole_info->length);\n\n                    t->file.file_offset += curr_length;\n                    t->file.left_length -= curr_length;\n                    if (t->file.left_length == 0) {\n                        return NGX_DECLINED;\n                    }\n                }\n                t->file.file_offset += segment_data->oper_size;\n                t->file.left_length -= segment_data->oper_size;\n                if (t->file.left_length == 0) {\n                    return NGX_DECLINED;\n                }\n            }\n            return NGX_OK;\n        }\n    }\n\n#if (NGX_DEBUG)\n    for (i = 0; i < count; i++) {\n        ngx_log_debug3(NGX_LOG_DEBUG_HTTP, t->log, 0,\n                       \"segment index: %d, oper_offset: %uD, oper_size: %uD\",\n                       i, t->file.segment_data[i].oper_offset,\n                       t->file.segment_data[i].oper_size);\n    }\n#endif\n\n    ngx_log_error(NGX_LOG_DEBUG, t->log, 0,\n                  \"still_have is %d, frag count is %d\",\n                  t->file.still_have, count);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_tfs_parse_action_message(ngx_http_tfs_t *t)\n{\n    uint16_t                         type;\n    ngx_str_t                        action;\n    ngx_http_tfs_header_t           *header;\n    ngx_http_tfs_peer_connection_t  *tp;\n\n    tp = t->tfs_peer;\n    header = (ngx_http_tfs_header_t *) t->header;\n    type = header->type;\n\n    switch (type) {\n\n    case NGX_HTTP_TFS_STATUS_MESSAGE:\n        ngx_str_set(&action, \"action (meta server)\");\n        return ngx_http_tfs_status_message(&tp->body_buffer, &action, t->log);\n    default:\n        break;\n    }\n\n    return NGX_ERROR;\n}\n\n\nstatic ngx_int_t\nngx_http_tfs_parse_ls_message(ngx_http_tfs_t *t)\n{\n    u_char                           *p;\n    uint16_t                          type;\n    uint32_t                          count, i;\n    ngx_buf_t                        *b;\n    ngx_str_t                         action;\n    ngx_http_tfs_header_t            *header;\n    ngx_http_tfs_custom_file_t       *file;\n    ngx_http_tfs_ms_ls_response_t    *resp;\n    ngx_http_tfs_peer_connection_t   *tp;\n    ngx_http_tfs_custom_meta_info_t  *meta_info, **new_meta_info;\n\n    tp = t->tfs_peer;\n    header = (ngx_http_tfs_header_t *) t->header;\n\n    type = header->type;\n\n    switch (type) {\n    case NGX_HTTP_TFS_STATUS_MESSAGE:\n        ngx_str_set(&action, \"ls file(meta server)\");\n        return ngx_http_tfs_status_message(&tp->body_buffer, &action, t->log);\n    }\n\n    b = &tp->body_buffer;\n    resp = (ngx_http_tfs_ms_ls_response_t *) b->pos;\n    count = resp->count;\n\n    t->file.still_have = resp->still_have;\n    t->length -= ngx_buf_size(b);\n\n    if (count == 0) {\n        if (t->r_ctx.action.code == NGX_HTTP_TFS_ACTION_LS_FILE) {\n            ngx_log_error(NGX_LOG_DEBUG, t->log, 0, \"file(%V) not exist\",\n                          &t->r_ctx.file_path_s);\n            return NGX_HTTP_TFS_EXIT_TARGET_EXIST_ERROR;\n        } else {\n            return NGX_OK;\n        }\n    }\n\n    meta_info = &t->meta_info;\n    if (meta_info->files == NULL) {\n        meta_info->files = ngx_pcalloc(t->pool,\n                                    sizeof(ngx_http_tfs_custom_file_t) * count);\n        if (meta_info->files == NULL) {\n            return NGX_ERROR;\n        }\n        meta_info->file_count = count;\n        meta_info->file_index = 0;\n\n    } else {\n        for(; meta_info->next; meta_info = meta_info->next);\n\n        if (meta_info->file_index == meta_info->file_count) {\n            new_meta_info = &meta_info->next;\n            meta_info = ngx_pcalloc(t->pool,\n                                    sizeof(ngx_http_tfs_custom_meta_info_t));\n            if (meta_info == NULL) {\n                return NGX_ERROR;\n            }\n            meta_info->files =\n                ngx_pcalloc(t->pool,sizeof(ngx_http_tfs_custom_file_t) * count);\n            if (meta_info->files == NULL) {\n                return NGX_ERROR;\n            }\n            meta_info->file_count = count;\n\n            *new_meta_info = meta_info;\n        }\n    }\n\n    file = meta_info->files;\n\n    p = b->pos + sizeof(ngx_http_tfs_ms_ls_response_t);\n\n    for (i = meta_info->file_index;\n         i < meta_info->file_count && p < (b->last - sizeof(uint32_t));\n         i++)\n    {\n        file[i].file_name.len = *((uint32_t *) p); /* include '\\0'*/\n        p += sizeof(uint32_t);\n        if (p + file[i].file_name.len >= b->last) {\n            p -= sizeof(uint32_t);\n            break;\n        }\n        if (file[i].file_name.len > 0) {\n            file[i].file_name.data = ngx_pcalloc(t->pool,\n                                                 file[i].file_name.len - 1);\n            ngx_memcpy(file[i].file_name.data, p, file[i].file_name.len - 1);\n            p += file[i].file_name.len;\n\n            file[i].file_name.len -= 1; /* exclude '\\0'*/\n        }\n        if (p + sizeof(ngx_http_tfs_custom_file_info_t) >= b->last) {\n            p -= file[i].file_name.len + 1 + sizeof(uint32_t);\n            break;\n        }\n        ngx_memcpy(&file[i].file_info, p,\n                   sizeof(ngx_http_tfs_custom_file_info_t));\n        p += sizeof(ngx_http_tfs_custom_file_info_t);\n    }\n\n    b->pos = p;\n    meta_info->file_index = i;\n    meta_info->rest_file_count = meta_info->file_count - meta_info->file_index;\n\n    if (t->r_ctx.action.code == NGX_HTTP_TFS_ACTION_LS_FILE) {\n        file[0].file_name = t->r_ctx.file_path_s;\n\n    } else if (resp->still_have && t->length == 0) {\n        t->last_file_path = file[meta_info->file_count - 1].file_name;\n        t->last_file_pid = file[meta_info->file_count - 1].file_info.pid;\n        t->last_file_type = (((t->last_file_pid >> 63) & 0x01) == 0x01) ?\n            NGX_HTTP_TFS_CUSTOM_FT_FILE : NGX_HTTP_TFS_CUSTOM_FT_DIR;\n        ngx_log_error(NGX_LOG_DEBUG, t->log, 0, \"ls last file path: %V\",\n                      &t->last_file_path);\n    }\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "modules/ngx_http_tfs_module/ngx_http_tfs_meta_server_message.h",
    "content": "\n/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#ifndef _NGX_HTTP_TFS_META_SERVER_MESSAGE_H_INCLUDED_\n#define _NGX_HTTP_TFS_META_SERVER_MESSAGE_H_INCLUDED_\n\n\n#include <ngx_http_tfs.h>\n\n\nngx_chain_t *ngx_http_tfs_meta_server_create_message(ngx_http_tfs_t *t);\nngx_int_t ngx_http_tfs_meta_server_parse_message(ngx_http_tfs_t *t);\nngx_http_tfs_inet_t *ngx_http_tfs_select_meta_server(ngx_http_tfs_t *t);\n\n\n#endif  /* _NGX_HTTP_TFS_META_SERVER_MESSAGE_H_INCLUDED_ */\n"
  },
  {
    "path": "modules/ngx_http_tfs_module/ngx_http_tfs_module.c",
    "content": "\n/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#include <ngx_core.h>\n#include <ngx_event.h>\n#include <ngx_http.h>\n#include <ngx_config.h>\n#include <ngx_http_tfs.h>\n#include <ngx_http_tfs_timers.h>\n#include <ngx_http_tfs_local_block_cache.h>\n\n\n#define NGX_HTTP_TFS_BLOCK_CACHE_ZONE_NAME \"tfs_module_block_cache_zone\"\n\n#define NGX_HTTP_TFS_UPSTREAM_CREATE           1\n#define NGX_HTTP_TFS_UPSTREAM_FIND             2\n\n#define NGX_HTTP_TFS_RCSERVER_TYPE             \"rcs\"\n#define NGX_HTTP_TFS_NAMESERVER_TYPE           \"ns\"\n\n\nstatic void *ngx_http_tfs_create_main_conf(ngx_conf_t *cf);\nstatic char *ngx_http_tfs_init_main_conf(ngx_conf_t *cf, void *conf);\n\nstatic void *ngx_http_tfs_create_srv_conf(ngx_conf_t *cf);\nstatic char *ngx_http_tfs_merge_srv_conf(ngx_conf_t *cf,\n    void *parent, void *child);\n\nstatic void *ngx_http_tfs_create_loc_conf(ngx_conf_t *cf);\nstatic char *ngx_http_tfs_merge_loc_conf(ngx_conf_t *cf,\n    void *parent, void *child);\n\nstatic ngx_int_t ngx_http_tfs_module_init(ngx_cycle_t *cycle);\n\nstatic char *ngx_http_tfs_upstream(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_http_tfs_pass(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n\nstatic char *ngx_http_tfs_log(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n\nstatic char *ngx_http_tfs_rcs_interface(ngx_conf_t *cf,\n    ngx_http_tfs_upstream_t *tu);\n\nstatic char *ngx_http_tfs_lowat_check(ngx_conf_t *cf, void *post, void *data);\nstatic void ngx_http_tfs_read_body_handler(ngx_http_request_t *r);\nstatic char *ngx_http_tfs_keepalive(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n\nstatic char *ngx_http_tfs_rcs_heartbeat(ngx_conf_t *cf,\n    ngx_http_tfs_upstream_t *tu);\n\nstatic char *ngx_http_tfs_rcs_zone(ngx_conf_t *cf, ngx_http_tfs_upstream_t *tu);\nstatic char *ngx_http_tfs_block_cache_zone(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n\nstatic char *ngx_http_tfs_upstream_parse(ngx_conf_t *cf, ngx_command_t *dummy,\n    void *conf);\n\n/* rc server keepalive */\nstatic ngx_int_t ngx_http_tfs_check_init_worker(ngx_cycle_t *cycle);\n#ifdef NGX_HTTP_TFS_USE_TAIR\n/* destroy tair servers */\nstatic void ngx_http_tfs_check_exit_worker(ngx_cycle_t *cycle);\n#endif\n\n\nstatic ngx_conf_post_t  ngx_http_tfs_lowat_post =\n    { ngx_http_tfs_lowat_check };\n\n\nstatic ngx_command_t  ngx_http_tfs_commands[] = {\n\n    { ngx_string(\"tfs_upstream\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE1,\n      ngx_http_tfs_upstream,\n      0,\n      0,\n      NULL },\n\n    { ngx_string(\"tfs_pass\"),\n      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE12,\n      ngx_http_tfs_pass,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"tfs_keepalive\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE2,\n      ngx_http_tfs_keepalive,\n      0,\n      0,\n      NULL },\n\n    { ngx_string(\"tfs_log\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE12,\n      ngx_http_tfs_log,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"tfs_connect_timeout\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      0,\n      offsetof(ngx_http_tfs_main_conf_t, tfs_connect_timeout),\n      NULL },\n\n    { ngx_string(\"tfs_send_timeout\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      0,\n      offsetof(ngx_http_tfs_main_conf_t, tfs_send_timeout),\n      NULL },\n\n    { ngx_string(\"tfs_read_timeout\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      0,\n      offsetof(ngx_http_tfs_main_conf_t, tfs_read_timeout),\n      NULL },\n\n    { ngx_string(\"tair_timeout\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      0,\n      offsetof(ngx_http_tfs_main_conf_t, tair_timeout),\n      NULL },\n\n    { ngx_string(\"tfs_send_lowat\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      0,\n      offsetof(ngx_http_tfs_main_conf_t, send_lowat),\n      &ngx_http_tfs_lowat_post },\n\n    { ngx_string(\"tfs_buffer_size\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      0,\n      offsetof(ngx_http_tfs_main_conf_t, buffer_size),\n      NULL },\n\n    { ngx_string(\"tfs_body_buffer_size\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      0,\n      offsetof(ngx_http_tfs_main_conf_t, body_buffer_size),\n      NULL },\n\n    { ngx_string(\"tfs_block_cache_zone\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_1MORE,\n      ngx_http_tfs_block_cache_zone,\n      0,\n      0,\n      NULL },\n\n    { ngx_string(\"tfs_enable_remote_block_cache\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      0,\n      offsetof(ngx_http_tfs_main_conf_t, enable_remote_block_cache),\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_tfs_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    NULL,                                  /* postconfiguration */\n\n    ngx_http_tfs_create_main_conf,         /* create main configuration */\n    ngx_http_tfs_init_main_conf,           /* init main configuration */\n\n    ngx_http_tfs_create_srv_conf,          /* create server configuration */\n    ngx_http_tfs_merge_srv_conf,           /* merge server configuration */\n\n    ngx_http_tfs_create_loc_conf,          /* create location configuration */\n    ngx_http_tfs_merge_loc_conf            /* merge location configuration */\n};\n\n\nngx_module_t  ngx_http_tfs_module = {\n    NGX_MODULE_V1,\n    &ngx_http_tfs_module_ctx,              /* module context */\n    ngx_http_tfs_commands,                 /* module directives */\n    NGX_HTTP_MODULE,                       /* module type */\n    NULL,                                  /* init master */\n    ngx_http_tfs_module_init,              /* init module */\n    ngx_http_tfs_check_init_worker,        /* init process */\n    NULL,                                  /* init thread */\n    NULL,                                  /* exit thread */\n#ifdef NGX_HTTP_TFS_USE_TAIR\n    ngx_http_tfs_check_exit_worker,        /* exit process */\n#else\n    NULL,\n#endif\n    NULL,                                  /* exit master */\n    NGX_MODULE_V1_PADDING\n};\n\n\nstatic ngx_int_t\nngx_http_tfs_handler(ngx_http_request_t *r)\n{\n    ngx_int_t                  rc;\n    ngx_http_tfs_t            *t;\n    ngx_http_tfs_loc_conf_t   *tlcf;\n    ngx_http_tfs_srv_conf_t   *tscf;\n    ngx_http_tfs_main_conf_t  *tmcf;\n\n    tlcf = ngx_http_get_module_loc_conf(r, ngx_http_tfs_module);\n    tscf = ngx_http_get_module_srv_conf(r, ngx_http_tfs_module);\n    tmcf = ngx_http_get_module_main_conf(r, ngx_http_tfs_module);\n\n    t = ngx_pcalloc(r->pool, sizeof(ngx_http_tfs_t));\n\n    if (t == NULL) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"alloc ngx_http_tfs_t failed\");\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    t->pool = r->pool;\n    t->data = r;\n    t->log = r->connection->log;\n    t->loc_conf = tlcf;\n    t->srv_conf = tscf;\n    t->main_conf = tmcf;\n    t->output.tag = (ngx_buf_tag_t) &ngx_http_tfs_module;\n    if (tmcf->local_block_cache_ctx != NULL) {\n        t->block_cache_ctx.use_cache |= NGX_HTTP_TFS_LOCAL_BLOCK_CACHE;\n        t->block_cache_ctx.local_ctx = tmcf->local_block_cache_ctx;\n    }\n    if (tmcf->enable_remote_block_cache == NGX_HTTP_TFS_YES) {\n        t->block_cache_ctx.use_cache |= NGX_HTTP_TFS_REMOTE_BLOCK_CACHE;\n    }\n    t->block_cache_ctx.remote_ctx.data = t;\n    t->block_cache_ctx.remote_ctx.tair_instance =\n                                             &tmcf->remote_block_cache_instance;\n    t->block_cache_ctx.curr_lookup_cache = NGX_HTTP_TFS_LOCAL_BLOCK_CACHE;\n\n    rc = ngx_http_restful_parse(r, &t->r_ctx);\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    t->header_only = r->header_only;\n\n    if (!t->loc_conf->upstream->enable_rcs && t->r_ctx.version == 2) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"custom file requires tfs_enable_rcs on,\"\n                      \" and make sure you have MetaServer and RootServer!\");\n        return NGX_HTTP_BAD_REQUEST;\n    }\n\n    switch (t->r_ctx.action.code) {\n    case NGX_HTTP_TFS_ACTION_CREATE_DIR:\n    case NGX_HTTP_TFS_ACTION_CREATE_FILE:\n    case NGX_HTTP_TFS_ACTION_REMOVE_DIR:\n    case NGX_HTTP_TFS_ACTION_REMOVE_FILE:\n    case NGX_HTTP_TFS_ACTION_MOVE_DIR:\n    case NGX_HTTP_TFS_ACTION_MOVE_FILE:\n    case NGX_HTTP_TFS_ACTION_LS_DIR:\n    case NGX_HTTP_TFS_ACTION_LS_FILE:\n    case NGX_HTTP_TFS_ACTION_STAT_FILE:\n    case NGX_HTTP_TFS_ACTION_KEEPALIVE:\n    case NGX_HTTP_TFS_ACTION_READ_FILE:\n    case NGX_HTTP_TFS_ACTION_GET_APPID:\n        rc = ngx_http_discard_request_body(r);\n\n        if (rc != NGX_OK) {\n            return rc;\n        }\n\n        r->headers_out.content_length_n = -1;\n        ngx_http_set_ctx(r, t, ngx_http_tfs_module);\n        r->main->count++;\n        ngx_http_tfs_read_body_handler(r);\n        break;\n    case NGX_HTTP_TFS_ACTION_WRITE_FILE:\n        r->headers_out.content_length_n = -1;\n        ngx_http_set_ctx(r, t, ngx_http_tfs_module);\n        rc = ngx_http_read_client_request_body(r,\n                                               ngx_http_tfs_read_body_handler);\n        if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {\n            return rc;\n        }\n        break;\n    }\n\n    return NGX_DONE;\n}\n\n\nngx_http_tfs_upstream_t *\nngx_http_tfs_upstream_add(ngx_conf_t *cf, ngx_url_t *u, ngx_uint_t flags)\n{\n    ngx_uint_t                 i;\n    ngx_http_tfs_upstream_t   *tu, **tup;\n    ngx_http_tfs_main_conf_t  *tmcf;\n\n    if (!(flags & NGX_HTTP_TFS_UPSTREAM_CREATE)) {\n\n        if (ngx_parse_url(cf->pool, u) != NGX_OK) {\n            if (u->err) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"%s in tfs upstream \\\"%V\\\"\",\n                                   u->err, &u->url);\n            }\n\n            return NULL;\n        }\n    }\n\n    tmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_tfs_module);\n\n    tup = tmcf->upstreams.elts;\n\n    for (i = 0; i < tmcf->upstreams.nelts; i++)  {\n\n        if (tup[i]->host.len != u->host.len\n            || ngx_strncasecmp(tup[i]->host.data, u->host.data, u->host.len)\n               != 0)\n        {\n            continue;\n        }\n\n        if (flags & NGX_HTTP_TFS_UPSTREAM_CREATE)\n        {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"duplicate tfs upstream \\\"%V\\\"\", &u->host);\n            return NULL;\n        }\n\n        return tup[i];\n    }\n\n    if (flags & NGX_HTTP_TFS_UPSTREAM_FIND) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \" host not found in tfs upstream \\\"%V\\\"\", &u->url);\n        return NULL;\n    }\n\n    /* NGX_HTTP_TFS_UPSTREAM_CREATE */\n\n    tu = ngx_pcalloc(cf->pool, sizeof(ngx_http_tfs_upstream_t));\n    if (tu == NULL) {\n        return NULL;\n    }\n\n    tu->host = u->host;\n\n    tup = ngx_array_push(&tmcf->upstreams);\n    if (tup == NULL) {\n        return NULL;\n    }\n\n    *tup = tu;\n\n    return tu;\n}\n\n\nstatic char *\nngx_http_tfs_upstream(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    char                     *rv;\n    ngx_url_t                 u;\n    ngx_str_t                *value;\n    ngx_conf_t                pcf;\n    ngx_http_tfs_upstream_t  *tu;\n\n    ngx_memzero(&u, sizeof(ngx_url_t));\n\n    value = cf->args->elts;\n    u.host = value[1];\n    u.no_resolve = 1;\n\n    tu = ngx_http_tfs_upstream_add(cf, &u, NGX_HTTP_TFS_UPSTREAM_CREATE);\n    if (tu == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    /* parse inside tfs_upstream{} */\n\n    pcf = *cf;\n    cf->ctx = tu;\n    cf->handler = ngx_http_tfs_upstream_parse;\n    cf->handler_conf = conf;\n\n    rv = ngx_conf_parse(cf, NULL);\n\n    *cf = pcf;\n\n    if (rv != NGX_CONF_OK) {\n        return rv;\n    }\n\n    if (tu->ups_addr == NULL) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"no servers are inside tfs upstream\");\n        return NGX_CONF_ERROR;\n    }\n\n    if (tu->enable_rcs) {\n        if (tu->local_addr_text[0] == '\\0') {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"type rcs_server must set rcs_interface \"\n                               \"directives in tfs_upstream block\");\n            return NGX_CONF_ERROR;\n        }\n\n        if (tu->lock_file.len == 0) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"type rcs must set rcs_heartbeat directives\"\n                               \" in tfs_upstream block\");\n            return NGX_CONF_ERROR;\n        }\n\n        if (tu->rc_ctx == NULL) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"type rcs must set \"\n                               \"rcs_zone directives in tfs_upstream block\");\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    return rv;\n}\n\n\nstatic char *\nngx_http_tfs_upstream_parse(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)\n{\n    ngx_url_t                 u;\n    ngx_str_t                *value, *server_addr;\n    ngx_http_tfs_upstream_t  *tu;\n\n    tu = cf->ctx;\n\n    value = cf->args->elts;\n\n    if (ngx_strcmp(value[0].data, \"server\") == 0) {\n\n        value = cf->args->elts;\n        server_addr = &value[1];\n\n        ngx_memzero(&u, sizeof(ngx_url_t));\n\n        u.url.len = server_addr->len;\n        u.url.data = server_addr->data;\n\n        if (ngx_parse_url(cf->pool, &u) != NGX_OK) {\n            if (u.err) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"%s in tfs \\\"%V\\\"\", u.err, &u.url);\n            }\n\n            return NGX_CONF_ERROR;\n        }\n\n        tu->ups_addr = u.addrs;\n\n        return NGX_CONF_OK;\n    }\n\n    if (ngx_strcmp(value[0].data, \"type\") == 0) {\n\n        if (cf->args->nelts != 2) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"invalid number of arguments in \"\n                               \"\\\"%s\\\" directive\",\n                               &value[0]);\n            return NGX_CONF_ERROR;\n        }\n\n        if ((sizeof(NGX_HTTP_TFS_NAMESERVER_TYPE) - 1) == value[1].len\n             && ngx_strcmp(value[1].data, NGX_HTTP_TFS_NAMESERVER_TYPE) == 0)\n        {\n            tu->enable_rcs = NGX_HTTP_TFS_NO;\n\n        } else if ((sizeof(NGX_HTTP_TFS_RCSERVER_TYPE) - 1) == value[1].len\n                   &&ngx_strcmp(value[1].data, NGX_HTTP_TFS_RCSERVER_TYPE) == 0)\n        {\n            tu->enable_rcs = NGX_HTTP_TFS_YES;\n\n        } else {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"invalid type \\\"%V\\\" in type directive\",\n                               &value[1]);\n            return NGX_CONF_ERROR;\n        }\n\n        return NGX_CONF_OK;\n    }\n\n    if (ngx_strcmp(value[0].data, \"rcs_zone\") == 0) {\n        return ngx_http_tfs_rcs_zone(cf, tu);\n    }\n\n    if (ngx_strcmp(value[0].data, \"rcs_interface\") == 0) {\n        return ngx_http_tfs_rcs_interface(cf, tu);\n    }\n\n    if (ngx_strcmp(value[0].data, \"rcs_heartbeat\") == 0) {\n        return ngx_http_tfs_rcs_heartbeat(cf, tu);\n    }\n\n    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                       \"invalid parameter \\\"%V\\\"\", &value[0]);\n\n    return NGX_CONF_ERROR;\n}\n\n\nstatic char *\nngx_http_tfs_keepalive(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_tfs_main_conf_t  *tmcf = conf;\n\n    ngx_int_t                    max_cached, bucket_count;\n    ngx_str_t                   *value, s;\n    ngx_uint_t                   i;\n    ngx_http_connection_pool_t  *p;\n\n    value = cf->args->elts;\n    max_cached = 0;\n    bucket_count = 0;\n\n    for (i = 1; i < cf->args->nelts; i++) {\n        if (ngx_strncmp(value[i].data, \"max_cached=\", 11) == 0) {\n\n            s.len = value[i].len - 11;\n            s.data = value[i].data + 11;\n\n            max_cached = ngx_atoi(s.data, s.len);\n\n            if (max_cached == NGX_ERROR || max_cached == 0) {\n                goto invalid;\n            }\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"bucket_count=\", 13) == 0) {\n\n            s.len = value[i].len - 13;\n            s.data = value[i].data + 13;\n\n            bucket_count = ngx_atoi(s.data, s.len);\n            if (bucket_count == NGX_ERROR || bucket_count == 0) {\n                goto invalid;\n            }\n            continue;\n        }\n\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid parameter \\\"%V\\\"\", &value[i]);\n        return NGX_CONF_ERROR;\n    }\n\n    p = ngx_http_connection_pool_init(cf->pool, max_cached, bucket_count);\n    if (p == NULL) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, \"connection pool init failed\");\n        return NGX_CONF_ERROR;\n    }\n\n    tmcf->conn_pool = p;\n    return NGX_CONF_OK;\n\ninvalid:\n    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                       \"invalid value \\\"%V\\\" in \\\"%V\\\" directive\",\n                       &value[i], &cmd->name);\n    return NGX_CONF_ERROR;\n}\n\n\nstatic char *\nngx_http_tfs_rcs_heartbeat(ngx_conf_t *cf, ngx_http_tfs_upstream_t *tu)\n{\n    ngx_str_t  *value, s;\n    ngx_msec_t  interval;\n    ngx_uint_t  i;\n\n    value = cf->args->elts;\n\n    for (i = 1; i < cf->args->nelts; i++) {\n        if (ngx_strncmp(value[i].data, \"lock_file=\", 10) == 0) {\n            s.data = value[i].data + 10;\n            s.len = value[i].len - 10;\n\n            if (ngx_conf_full_name(cf->cycle, &s, 0) != NGX_OK) {\n                goto rcs_timers_error;\n            }\n\n            tu->lock_file = s;\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"interval=\", 9) == 0) {\n            s.data = value[i].data + 9;\n            s.len = value[i].len - 9;\n\n            interval = ngx_parse_time(&s, 0);\n\n            if (interval == (ngx_msec_t) NGX_ERROR) {\n                return \"invalid value\";\n            }\n\n            tu->rcs_interval = interval;\n            continue;\n        }\n\n        goto rcs_timers_error;\n    }\n\n    if (tu->lock_file.len == 0) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"tfs_poll directive must have lock file\");\n        return NGX_CONF_ERROR;\n    }\n\n    if (tu->rcs_interval < NGX_HTTP_TFS_MIN_TIMER_DELAY) {\n        tu->rcs_interval = NGX_HTTP_TFS_MIN_TIMER_DELAY;\n        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                           \"tfs_poll interval is small, \"\n                           \"so reset this value to 1000\");\n    }\n\n    return NGX_CONF_OK;\n\nrcs_timers_error:\n    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                       \"invalid value \\\"%V\\\" in \\\"%V\\\" directive\",\n                       &value[i], &value[0]);\n    return NGX_CONF_ERROR;\n}\n\n\nstatic char *\nngx_http_tfs_rcs_interface(ngx_conf_t *cf, ngx_http_tfs_upstream_t *tu)\n{\n    ngx_int_t   rc;\n    ngx_str_t  *value;\n\n    if (cf->args->nelts != 2) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid number of arguments in \"\n                           \"\\\"rcs_interface\\\" directive\");\n        return NGX_CONF_ERROR;\n    }\n\n    value = cf->args->elts;\n    rc = ngx_http_tfs_get_local_ip(value[1], &tu->local_addr);\n    if (rc == NGX_ERROR) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, \"device is invalid(%V)\",\n                           &value[1]);\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_inet_ntop(AF_INET, &tu->local_addr.sin_addr, tu->local_addr_text,\n                  NGX_INET_ADDRSTRLEN);\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_tfs_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_tfs_srv_conf_t  *tscf = conf;\n\n    if (tscf->log != NULL) {\n        return \"is duplicate\";\n    }\n\n    return ngx_log_set_log(cf, &tscf->log);\n}\n\n\nstatic void *\nngx_http_tfs_create_srv_conf(ngx_conf_t *cf)\n{\n    ngx_http_tfs_srv_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_tfs_srv_conf_t));\n    if (conf == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    return conf;\n}\n\n\nstatic char *\nngx_http_tfs_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    return NGX_CONF_OK;\n}\n\n\nstatic void *\nngx_http_tfs_create_main_conf(ngx_conf_t *cf)\n{\n    ngx_http_tfs_main_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_tfs_main_conf_t));\n    if (conf == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    conf->tfs_connect_timeout = NGX_CONF_UNSET_MSEC;\n    conf->tfs_send_timeout = NGX_CONF_UNSET_MSEC;\n    conf->tfs_read_timeout = NGX_CONF_UNSET_MSEC;\n\n    conf->tair_timeout = NGX_CONF_UNSET_MSEC;\n\n    conf->send_lowat = NGX_CONF_UNSET_SIZE;\n    conf->buffer_size = NGX_CONF_UNSET_SIZE;\n    conf->body_buffer_size = NGX_CONF_UNSET_SIZE;\n\n    conf->conn_pool = NGX_CONF_UNSET_PTR;\n\n    conf->enable_remote_block_cache = NGX_CONF_UNSET;\n\n    if (ngx_array_init(&conf->upstreams, cf->pool, 4,\n                       sizeof(ngx_http_tfs_upstream_t *))\n        != NGX_OK)\n    {\n        return NULL;\n    }\n\n    return conf;\n}\n\n\nstatic char *\nngx_http_tfs_init_main_conf(ngx_conf_t *cf, void *conf)\n{\n    ngx_http_tfs_main_conf_t *tmcf = conf;\n\n    if (tmcf->tfs_connect_timeout == NGX_CONF_UNSET_MSEC) {\n        tmcf->tfs_connect_timeout = 3000;\n    }\n\n    if (tmcf->tfs_send_timeout == NGX_CONF_UNSET_MSEC) {\n        tmcf->tfs_send_timeout = 3000;\n    }\n\n    if (tmcf->tfs_read_timeout == NGX_CONF_UNSET_MSEC) {\n        tmcf->tfs_read_timeout = 3000;\n    }\n\n    if (tmcf->tair_timeout == NGX_CONF_UNSET_MSEC) {\n        tmcf->tair_timeout = 3000;\n    }\n\n    if (tmcf->send_lowat == NGX_CONF_UNSET_SIZE) {\n        tmcf->send_lowat = 0;\n    }\n\n    if (tmcf->buffer_size == NGX_CONF_UNSET_SIZE) {\n        tmcf->buffer_size = (size_t) ngx_pagesize / 2;\n    }\n\n    if (tmcf->body_buffer_size == NGX_CONF_UNSET_SIZE) {\n        tmcf->body_buffer_size = NGX_HTTP_TFS_DEFAULT_BODY_BUFFER_SIZE;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic void *\nngx_http_tfs_create_loc_conf(ngx_conf_t *cf)\n{\n    ngx_http_tfs_loc_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_tfs_loc_conf_t));\n    if (conf == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    return conf;\n}\n\n\nstatic char *ngx_http_tfs_merge_loc_conf(ngx_conf_t *cf,\n    void *parent, void *child)\n{\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_tfs_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_int_t                  add;\n    ngx_str_t                 *value, s;\n    ngx_url_t                  u;\n    ngx_http_tfs_loc_conf_t   *tlcf;\n    ngx_http_tfs_main_conf_t  *tmcf;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    value = cf->args->elts;\n\n    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);\n    tmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_tfs_module);\n    tlcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_tfs_module);\n\n    if (ngx_strncasecmp(value[1].data, (u_char *) \"tfs://\", 6) == 0) {\n        add = 6;\n\n    } else {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid URL prefix in tfs_pass\");\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_memzero(&u, sizeof(ngx_url_t));\n\n    u.url.len = value[1].len - add;\n    u.url.data = value[1].data + add;\n    u.uri_part = 1;\n    u.no_resolve = 1;\n\n    tlcf->upstream = ngx_http_tfs_upstream_add(cf, &u,\n                                               NGX_HTTP_TFS_UPSTREAM_FIND);\n    if (tlcf->upstream == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    clcf->handler = ngx_http_tfs_handler;\n\n    if (clcf->name.data[clcf->name.len - 1] == '/') {\n        clcf->auto_redirect = 1;\n    }\n\n    if (tlcf->upstream->enable_rcs) {\n        if (tlcf->upstream->local_addr_text[0] == '\\0') {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"in tfs module must assign net device name, \"\n                               \"use directives \\\"rcs_interface\\\" \");\n            return NGX_CONF_ERROR;\n        }\n\n        tlcf->upstream->rcs_shm_zone = ngx_shared_memory_add(cf,\n                                              &tlcf->upstream->rcs_zone_name, 0,\n                                              &ngx_http_tfs_module);\n        if (tlcf->upstream->rcs_shm_zone == NULL) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"in tfs module must assign rcs shm zone,\"\n                               \"use directives \\\"rcs_zone\\\" \");\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    if (tmcf->local_block_cache_ctx != NULL) {\n        s.data = (u_char *) NGX_HTTP_TFS_BLOCK_CACHE_ZONE_NAME;\n        s.len = sizeof(NGX_HTTP_TFS_BLOCK_CACHE_ZONE_NAME) - 1;\n\n        tmcf->block_cache_shm_zone = ngx_shared_memory_add(cf, &s, 0,\n                                                          &ngx_http_tfs_module);\n        if (tmcf->block_cache_shm_zone == NULL) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"in tfs module must assign block cache shm zone,\"\n                               \"use directives \\\"tfs_block_cache_zone\\\" \");\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    tlcf->upstream->used = 1;\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_tfs_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                           \"\\\"tfs_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                       \"\\\"tfs_send_lowat\\\" is not supported, ignored\");\n\n    *np = 0;\n\n#endif\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_tfs_rcs_zone(ngx_conf_t *cf, ngx_http_tfs_upstream_t *tu)\n{\n    ssize_t                 size;\n    ngx_str_t              *value, s, name;\n    ngx_uint_t              i;\n    ngx_shm_zone_t         *shm_zone;\n    ngx_http_tfs_rc_ctx_t  *ctx;\n\n    value = cf->args->elts;\n    size = 0;\n    name.len = 0;\n\n    if (cf->args->nelts != 3) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid number of arguments in \"\n                           \"\\\"rcs_zone\\\" directive\");\n        return NGX_CONF_ERROR;\n    }\n\n    for (i = 1; i < cf->args->nelts; i++) {\n\n        if (ngx_strncmp(value[i].data, \"size=\", 5) == 0) {\n            s.len = value[i].len - 5;\n            s.data = value[i].data + 5;\n\n            size = ngx_parse_size(&s);\n\n            if (size == NGX_ERROR) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"invalid zone size \\\"%V\\\"\", &value[i]);\n                return NGX_CONF_ERROR;\n            }\n\n            if (size < (ssize_t) (8 * ngx_pagesize)) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"zone \\\"%V\\\" is too small\", &value[i]);\n                return NGX_CONF_ERROR;\n            }\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"name=\", 5) == 0) {\n            name.len = value[i].len - 5;\n            name.data = value[i].data + 5;\n\n            continue;\n        }\n\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid parameter \\\"%V\\\"\", &value[i]);\n        return NGX_CONF_ERROR;\n    }\n\n\n    if (size == 0) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"\\\"rcs_zone\\\" must have \\\"size\\\" parameter\");\n        return NGX_CONF_ERROR;\n    }\n\n    if (name.len == 0) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"\\\"rcs_zone\\\" must have  \\\"name\\\" parameter\");\n        return NGX_CONF_ERROR;\n    }\n\n    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_tfs_rc_ctx_t));\n    if (ctx == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    shm_zone = ngx_shared_memory_add(cf, &name, size,\n                                     &ngx_http_tfs_module);\n    if (shm_zone == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    shm_zone->init = ngx_http_tfs_rc_server_init_zone;\n    shm_zone->data = ctx;\n\n    tu->rc_ctx = ctx;\n    tu->rcs_zone_name = name;\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_tfs_block_cache_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    size_t                                 size;\n    ngx_str_t                             *value, s, name;\n    ngx_uint_t                             i;\n    ngx_shm_zone_t                        *shm_zone;\n    ngx_http_tfs_main_conf_t              *tmcf = conf;\n    ngx_http_tfs_local_block_cache_ctx_t  *ctx;\n\n    value = cf->args->elts;\n    size = 0;\n\n    for (i = 1; i < cf->args->nelts; i++) {\n\n        if (ngx_strncmp(value[i].data, \"size=\", 5) == 0) {\n            s.len = value[i].len - 5;\n            s.data = value[i].data + 5;\n\n            size = ngx_parse_size(&s);\n            if (size > 8191) {\n                continue;\n            }\n        }\n\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid parameter \\\"%V\\\"\", &value[i]);\n        return NGX_CONF_ERROR;\n    }\n\n\n    if (size == 0) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"\\\"%V\\\" must have \\\"size\\\" parameter\",\n                           &cmd->name);\n        return NGX_CONF_ERROR;\n    }\n\n    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_tfs_local_block_cache_ctx_t));\n    if (ctx == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    name.data = (u_char *) NGX_HTTP_TFS_BLOCK_CACHE_ZONE_NAME;\n    name.len = sizeof(NGX_HTTP_TFS_BLOCK_CACHE_ZONE_NAME) - 1;\n\n    shm_zone = ngx_shared_memory_add(cf, &name, size,\n                                     &ngx_http_tfs_module);\n    if (shm_zone == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    shm_zone->init = ngx_http_tfs_local_block_cache_init_zone;\n    shm_zone->data = ctx;\n\n    tmcf->local_block_cache_ctx = ctx;\n\n    return NGX_CONF_OK;\n}\n\n\nstatic void\nngx_http_tfs_read_body_handler(ngx_http_request_t *r)\n{\n    ngx_int_t          rc;\n    ngx_http_tfs_t    *t;\n    ngx_connection_t  *c;\n\n    c = r->connection;\n    t = ngx_http_get_module_ctx(r, ngx_http_tfs_module);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http init tfs, client timer: %d\", c->read->timer_set);\n\n    if (c->read->timer_set) {\n        ngx_del_timer(c->read);\n    }\n\n    if (ngx_event_flags & NGX_USE_CLEAR_EVENT) {\n\n        if (!c->write->active) {\n            if (ngx_add_event(c->write, NGX_WRITE_EVENT, NGX_CLEAR_EVENT)\n                == NGX_ERROR)\n            {\n                ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n                return;\n            }\n        }\n    }\n\n    if (t->r_ctx.large_file\n        || t->r_ctx.fsname.file_type == NGX_HTTP_TFS_LARGE_FILE_TYPE)\n    {\n        t->is_large_file = NGX_HTTP_TFS_YES;\n    }\n\n    if (r->request_body) {\n        t->send_body = r->request_body->bufs;\n        if (t->send_body == NULL) {\n            ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);\n            return;\n        }\n        if (r->headers_in.content_length_n > NGX_HTTP_TFS_USE_LARGE_FILE_SIZE\n            && t->r_ctx.version == 1)\n        {\n            t->is_large_file = NGX_HTTP_TFS_YES;\n        }\n        /* save large file data len here */\n        if (t->is_large_file) {\n            t->r_ctx.size = r->headers_in.content_length_n;\n        }\n    }\n\n    rc = ngx_http_tfs_init(t);\n\n    if (rc != NGX_OK) {\n        switch (rc) {\n        case NGX_HTTP_SPECIAL_RESPONSE ... NGX_HTTP_INTERNAL_SERVER_ERROR:\n            ngx_http_finalize_request(r, rc);\n            break;\n        default:\n            ngx_log_error(NGX_LOG_ERR, t->log, 0,\n                          \"ngx_http_tfs_init failed\");\n            ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n        }\n    }\n}\n\n\nstatic ngx_int_t\nngx_http_tfs_module_init(ngx_cycle_t *cycle)\n{\n    ngx_uint_t                   i;\n    ngx_http_tfs_upstream_t    **tup;\n    ngx_http_tfs_main_conf_t    *tmcf;\n    ngx_http_tfs_timers_data_t  *data;\n\n    tmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_tfs_module);\n    if (tmcf == NULL) {\n        return NGX_ERROR;\n    }\n\n    tup = tmcf->upstreams.elts;\n\n    for (i = 0; i < tmcf->upstreams.nelts; i++) {\n        if (!tup[i]->enable_rcs\n            || !tup[i]->lock_file.len\n            || !tup[i]->used)\n        {\n            return NGX_OK;\n        }\n\n        data = ngx_pcalloc(cycle->pool, sizeof(ngx_http_tfs_timers_data_t));\n        if (data == NULL) {\n            return NGX_ERROR;\n        }\n\n        data->main_conf = tmcf;\n        data->upstream = tup[i];\n        data->lock = ngx_http_tfs_timers_init(cycle, tup[i]->lock_file.data);\n        if (data->lock == NULL)\n        {\n            return NGX_ERROR;\n        }\n\n        tup[i]->timer_data = data;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_tfs_check_init_worker(ngx_cycle_t *cycle)\n{\n    ngx_uint_t                 i;\n    ngx_http_tfs_upstream_t  **tup;\n    ngx_http_tfs_main_conf_t  *tmcf;\n\n    /* rc keepalive */\n    tmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_tfs_module);\n    if (tmcf == NULL) {\n        return NGX_ERROR;\n    }\n\n    tup = tmcf->upstreams.elts;\n\n    for (i = 0; i < tmcf->upstreams.nelts; i++) {\n        if (!tup[i]->enable_rcs\n            || !tup[i]->lock_file.len\n            || !tup[i]->used)\n        {\n            return NGX_OK;\n        }\n\n        if (ngx_http_tfs_add_rcs_timers(cycle, tup[i]->timer_data) == NGX_ERROR)\n        {\n            return NGX_ERROR;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\n#ifdef NGX_HTTP_TFS_USE_TAIR\nstatic void\nngx_http_tfs_check_exit_worker(ngx_cycle_t *cycle)\n{\n    ngx_int_t                      i;\n    ngx_http_tfs_main_conf_t      *tmcf;\n    ngx_http_tfs_tair_instance_t  *dup_instance;\n\n    tmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_tfs_module);\n    if (tmcf == NULL) {\n        return;\n    }\n\n    /* destroy duplicate server */\n    for (i = 0; i < NGX_HTTP_TFS_MAX_CLUSTER_COUNT; i++) {\n        dup_instance = &tmcf->dup_instances[i];\n        if (dup_instance->server != NULL) {\n            ngx_http_etair_destory_server(dup_instance->server,\n                                          (ngx_cycle_t *) ngx_cycle);\n        }\n    }\n\n    /* destroy remote block cache server */\n    if (tmcf->remote_block_cache_instance.server != NULL) {\n        ngx_http_etair_destory_server(tmcf->remote_block_cache_instance.server,\n                                      (ngx_cycle_t *) ngx_cycle);\n    }\n\n}\n#endif\n"
  },
  {
    "path": "modules/ngx_http_tfs_module/ngx_http_tfs_name_server_message.c",
    "content": "\n/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#include <ngx_http_tfs_errno.h>\n#include <ngx_http_tfs_name_server_message.h>\n\n\nstatic ngx_chain_t *ngx_http_tfs_create_block_info_message(ngx_http_tfs_t *t,\n    ngx_http_tfs_segment_data_t *segment_data);\nstatic ngx_chain_t *ngx_http_tfs_create_batch_block_info_message(\n    ngx_http_tfs_t *t);\nstatic ngx_chain_t *ngx_http_tfs_create_ctl_message(ngx_http_tfs_t *t,\n    uint8_t cmd);\n\nstatic ngx_int_t ngx_http_tfs_parse_block_info_message(ngx_http_tfs_t *t,\n    ngx_http_tfs_segment_data_t *segment_data);\nstatic ngx_int_t ngx_http_tfs_parse_batch_block_info_message(ngx_http_tfs_t *t,\n    ngx_http_tfs_segment_data_t *segment_data);\nstatic ngx_int_t ngx_http_tfs_parse_ctl_message(ngx_http_tfs_t *t, uint8_t cmd);\n\n\nngx_int_t\nngx_http_tfs_select_name_server(ngx_http_tfs_t *t,\n    ngx_http_tfs_rcs_info_t *rc_info, ngx_http_tfs_inet_t *addr,\n    ngx_str_t *addr_text)\n{\n    uint32_t                            cluster_id, curr_cluster_id, info_count,\n                                        curr_block_id;\n    ngx_str_t                          *cluster_id_text;\n    ngx_uint_t                          i, j;\n    ngx_http_tfs_group_info_t          *group_info;\n    ngx_http_tfs_logical_cluster_t     *logical_cluster;\n    ngx_http_tfs_physical_cluster_t    *physical_cluster;\n    ngx_http_tfs_cluster_group_info_t  *cluster_group_info;\n\n    curr_cluster_id = 0;\n\n    if (rc_info == NULL) {\n        return NGX_ERROR;\n    }\n    if (t->r_ctx.version == 1) {\n        curr_cluster_id = t->r_ctx.fsname.cluster_id;\n\n    } else if (t->r_ctx.version == 2){\n        curr_cluster_id = t->file.cluster_id;\n    }\n\n    switch(t->r_ctx.action.code) {\n    case NGX_HTTP_TFS_ACTION_STAT_FILE:\n    case NGX_HTTP_TFS_ACTION_READ_FILE:\n        for (;\n             t->logical_cluster_index < rc_info->logical_cluster_count;\n             t->logical_cluster_index++)\n        {\n            logical_cluster =\n                           &rc_info->logical_clusters[t->logical_cluster_index];\n            for (;\n                 t->rw_cluster_index < logical_cluster->rw_cluster_count;\n                 t->rw_cluster_index++)\n            {\n                physical_cluster =\n                             &logical_cluster->rw_clusters[t->rw_cluster_index];\n                cluster_id_text = &physical_cluster->cluster_id_text;\n                cluster_id = ngx_http_tfs_get_cluster_id(cluster_id_text->data);\n                if (curr_cluster_id > 0 && cluster_id != curr_cluster_id) {\n                    continue;\n                }\n                ngx_log_debug3(NGX_LOG_DEBUG_HTTP, t->log, 0,\n                               \"read/stat, select logical cluster \"\n                               \"index: %ui, rw_cluster_index: %ui, \"\n                               \"nameserver: %V\",\n                               t->logical_cluster_index, t->rw_cluster_index,\n                               &physical_cluster->ns_vip_text);\n                (*addr) = physical_cluster->ns_vip;\n                (*addr_text) = physical_cluster->ns_vip_text;\n                return NGX_OK;\n            }\n            t->rw_cluster_index = 0;\n        }\n\n        break;\n\n    case NGX_HTTP_TFS_ACTION_WRITE_FILE:\n        if (t->r_ctx.is_raw_update == NGX_HTTP_TFS_NO) {\n            for (;\n                 t->logical_cluster_index < rc_info->logical_cluster_count;\n                 t->logical_cluster_index++)\n            {\n                logical_cluster =\n                               &rc_info->logical_clusters[t->logical_cluster_index];\n                for (;\n                     t->rw_cluster_index < logical_cluster->rw_cluster_count;\n                     t->rw_cluster_index++)\n                {\n                    physical_cluster =\n                                 &logical_cluster->rw_clusters[t->rw_cluster_index];\n                    if (physical_cluster->access_type\n                        == NGX_HTTP_TFS_ACCESS_READ_AND_WRITE)\n                    {\n                        /* custom file should ensure writing\n                         * all frags in the same cluster */\n                        if (curr_cluster_id > 0) {\n                            cluster_id_text = &physical_cluster->cluster_id_text;\n                            cluster_id =\n                                ngx_http_tfs_get_cluster_id(cluster_id_text->data);\n                            if (cluster_id != curr_cluster_id) {\n                                continue;\n                            }\n                        }\n                        ngx_log_debug3(NGX_LOG_DEBUG_HTTP, t->log, 0,\n                                       \"write, select logical cluster \"\n                                       \"index: %ui, rw_cluster_index: %ui, \"\n                                       \"nameserver: %V\",\n                                       t->logical_cluster_index,\n                                       t->rw_cluster_index,\n                                       &physical_cluster->ns_vip_text);\n                        (*addr) = physical_cluster->ns_vip;\n                        (*addr_text) = physical_cluster->ns_vip_text;\n\n                        /* check if need get cluster id from ns */\n                        if (t->state == NGX_HTTP_TFS_STATE_WRITE_GET_BLK_INFO) {\n                            if (physical_cluster->cluster_id > 0) {\n                                t->file.cluster_id = physical_cluster->cluster_id;\n\n                            } else {\n                                t->state = NGX_HTTP_TFS_STATE_WRITE_CLUSTER_ID_NS;\n                            }\n                        }\n\n                        return NGX_OK;\n                    }\n                }\n                t->rw_cluster_index = 0;\n            }\n\n            break;\n        }\n\n        /* update raw file */\n    case NGX_HTTP_TFS_ACTION_REMOVE_FILE:\n        for (i = 0; i < rc_info->unlink_cluster_group_count; i++) {\n            cluster_group_info = &rc_info->unlink_cluster_groups[i];\n            group_info = cluster_group_info->group_info;\n            cluster_id = cluster_group_info->cluster_id;\n            info_count = cluster_group_info->info_count;\n\n            if ((curr_cluster_id > 0 && cluster_id != curr_cluster_id)\n                || info_count < 1)\n            {\n                continue;\n            }\n\n            /* only one cluster, select it */\n            if (info_count == 1) {\n                (*addr) = group_info[0].ns_vip;\n                (*addr_text) = group_info[0].ns_vip_text;\n                if (t->r_ctx.action.code == NGX_HTTP_TFS_ACTION_REMOVE_FILE) {\n                    t->state = NGX_HTTP_TFS_STATE_REMOVE_GET_BLK_INFO;\n\n                } else if (t->r_ctx.action.code == NGX_HTTP_TFS_ACTION_WRITE_FILE) {\n                    t->state = NGX_HTTP_TFS_STATE_WRITE_GET_BLK_INFO;\n                }\n                ngx_log_debug1(NGX_LOG_DEBUG_HTTP, t->log, 0,\n                               \"unlink, select nameserver: %V\",\n                               &group_info[0].ns_vip_text);\n                goto find_logical_cluster_index;\n            }\n\n            /* two clusters or more */\n            if (cluster_group_info->group_count <= 0) {\n                (*addr) = group_info[0].ns_vip;\n                (*addr_text) = group_info[0].ns_vip_text;\n                if (t->r_ctx.action.code == NGX_HTTP_TFS_ACTION_REMOVE_FILE) {\n                    t->state = NGX_HTTP_TFS_STATE_REMOVE_GET_GROUP_COUNT;\n\n                } else if (t->r_ctx.action.code == NGX_HTTP_TFS_ACTION_WRITE_FILE) {\n                    t->state = NGX_HTTP_TFS_STATE_WRITE_GET_GROUP_COUNT;\n                }\n                return NGX_OK;\n            }\n\n            /* if there are two clusters or more but group count is 1, sth wrong in TFS cluster configure */\n            if (cluster_group_info->group_count == 1) {\n                ngx_log_error(NGX_LOG_ERR, t->log, 0,\n                               \"unlink, can not select nameserver.\");\n                return NGX_ERROR;\n            }\n\n            for (j = 0; j < info_count; j++) {\n                if (group_info[j].group_seq < 0) {\n                    (*addr) = group_info[j].ns_vip;\n                    (*addr_text) = group_info[j].ns_vip_text;\n                    if (t->r_ctx.action.code == NGX_HTTP_TFS_ACTION_REMOVE_FILE) {\n                        t->state = NGX_HTTP_TFS_STATE_REMOVE_GET_GROUP_SEQ;\n\n                    } else if (t->r_ctx.action.code == NGX_HTTP_TFS_ACTION_WRITE_FILE) {\n                        t->state = NGX_HTTP_TFS_STATE_WRITE_GET_GROUP_SEQ;\n                    }\n                    return NGX_OK;\n                }\n\n                if (t->r_ctx.version == 1) {\n                    curr_block_id = t->r_ctx.fsname.file.block_id;\n                } else {\n                    curr_block_id =\n                        t->file.segment_data[0].segment_info.block_id;\n                }\n                if (ngx_http_tfs_group_seq_match(curr_block_id,\n                                              cluster_group_info->group_count,\n                                              group_info[j].group_seq))\n                {\n                    (*addr) = group_info[j].ns_vip;\n                    (*addr_text) = group_info[j].ns_vip_text;\n                    if (t->r_ctx.action.code == NGX_HTTP_TFS_ACTION_REMOVE_FILE) {\n                        t->state = NGX_HTTP_TFS_STATE_REMOVE_GET_BLK_INFO;\n\n                    } else if (t->r_ctx.action.code == NGX_HTTP_TFS_ACTION_WRITE_FILE) {\n                        t->state = NGX_HTTP_TFS_STATE_WRITE_GET_BLK_INFO;\n                    }\n\n                    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, t->log, 0,\n                                   \"unlink, select nameserver: %V\",\n                                   &group_info[j].ns_vip_text);\n                    goto find_logical_cluster_index;\n                }\n            }\n        }\n\n        return NGX_ERROR;\n\nfind_logical_cluster_index:\n\n        /* find out which logical cluster this addr belongs to\n         * so that we can use the right de-dup addr\n         */\n        t->logical_cluster_index = 0;\n        for ( ;\n              t->logical_cluster_index < rc_info->logical_cluster_count;\n              t->logical_cluster_index++)\n        {\n            logical_cluster =\n                &rc_info->logical_clusters[t->logical_cluster_index];\n            t->rw_cluster_index = 0;\n            for ( ;\n                  t->rw_cluster_index < logical_cluster->rw_cluster_count;\n                  t->rw_cluster_index++)\n            {\n                physical_cluster =\n                    &logical_cluster->rw_clusters[t->rw_cluster_index];\n                if (*(uint64_t*)(&physical_cluster->ns_vip)\n                    == *(uint64_t*)(addr))\n                {\n                    return NGX_OK;\n                }\n            }\n        }\n        ngx_log_error(NGX_LOG_ERR, t->log, 0,\n                      \"can not find logical cluster index of ns: %V\",\n                      addr_text);\n        return NGX_ERROR;\n    }\n\n    return NGX_ERROR;\n}\n\n\nngx_chain_t *\nngx_http_tfs_name_server_create_message(ngx_http_tfs_t *t)\n{\n    uint16_t      action;\n    ngx_chain_t  *cl;\n    ngx_http_tfs_segment_data_t  *segment_data;\n\n    cl = NULL;\n    t->file.open_mode = 0;\n    action = t->r_ctx.action.code;\n    segment_data = &t->file.segment_data[t->file.segment_index];\n\n    switch (action) {\n    case NGX_HTTP_TFS_ACTION_STAT_FILE:\n        t->file.open_mode = NGX_HTTP_TFS_OPEN_MODE_STAT\n                             | NGX_HTTP_TFS_OPEN_MODE_READ;\n        ngx_log_error(NGX_LOG_INFO, t->log, 0, \"get block info from ns\");\n\n        cl = ngx_http_tfs_create_block_info_message(t, segment_data);\n        break;\n\n    case NGX_HTTP_TFS_ACTION_READ_FILE:\n        t->file.open_mode |= NGX_HTTP_TFS_OPEN_MODE_READ;\n        ngx_log_error(NGX_LOG_INFO, t->log, 0, \"get block info from ns\");\n\n        if (!t->parent\n            && (t->r_ctx.version == 2\n                || (t->is_large_file && !t->is_process_meta_seg)))\n        {\n            cl = ngx_http_tfs_create_batch_block_info_message(t);\n\n        } else {\n            cl = ngx_http_tfs_create_block_info_message(t, segment_data);\n        }\n        break;\n\n    case NGX_HTTP_TFS_ACTION_WRITE_FILE:\n        t->file.open_mode = NGX_HTTP_TFS_OPEN_MODE_WRITE\n                             | NGX_HTTP_TFS_OPEN_MODE_CREATE;\n        switch(t->state) {\n        case NGX_HTTP_TFS_STATE_WRITE_GET_GROUP_COUNT:\n            ngx_log_error(NGX_LOG_INFO, t->log, 0, \"get group count from ns\");\n            cl = ngx_http_tfs_create_ctl_message(t,\n                NGX_HTTP_TFS_CMD_GET_GROUP_COUNT);\n            break;\n\n        case NGX_HTTP_TFS_STATE_WRITE_GET_GROUP_SEQ:\n            ngx_log_error(NGX_LOG_INFO, t->log, 0, \"get group seq from ns\");\n            cl = ngx_http_tfs_create_ctl_message(t,\n                NGX_HTTP_TFS_CMD_GET_GROUP_SEQ);\n            break;\n\n        case NGX_HTTP_TFS_STATE_WRITE_CLUSTER_ID_NS:\n            ngx_log_error(NGX_LOG_INFO, t->log, 0, \"get cluster id from ns\");\n            cl = ngx_http_tfs_create_ctl_message(t,\n                NGX_HTTP_TFS_CMD_GET_CLUSTER_ID_NS);\n            break;\n        case NGX_HTTP_TFS_STATE_WRITE_GET_BLK_INFO:\n            ngx_log_error(NGX_LOG_INFO, t->log, 0, \"get block info from ns\");\n            if (t->is_stat_dup_file) {\n                t->file.open_mode = NGX_HTTP_TFS_OPEN_MODE_STAT\n                                     | NGX_HTTP_TFS_OPEN_MODE_READ;\n            }\n            if (!t->parent\n                && !t->is_rolling_back\n                && (t->r_ctx.version == 2\n                 || (t->is_large_file && !t->is_process_meta_seg)))\n            {\n                cl = ngx_http_tfs_create_batch_block_info_message(t);\n\n            } else {\n                cl = ngx_http_tfs_create_block_info_message(t, segment_data);\n            }\n            break;\n        }\n        break;\n    case NGX_HTTP_TFS_ACTION_REMOVE_FILE:\n        switch(t->state) {\n        case NGX_HTTP_TFS_STATE_REMOVE_GET_GROUP_COUNT:\n            ngx_log_error(NGX_LOG_INFO, t->log, 0, \"get group count from ns\");\n            cl = ngx_http_tfs_create_ctl_message(t,\n                                              NGX_HTTP_TFS_CMD_GET_GROUP_COUNT);\n            break;\n        case NGX_HTTP_TFS_STATE_REMOVE_GET_GROUP_SEQ:\n            ngx_log_error(NGX_LOG_INFO, t->log, 0, \"get group seq from ns\");\n            cl = ngx_http_tfs_create_ctl_message(t,\n                                                NGX_HTTP_TFS_CMD_GET_GROUP_SEQ);\n            break;\n        default:\n            t->file.open_mode = NGX_HTTP_TFS_OPEN_MODE_WRITE;\n            if (t->is_stat_dup_file) {\n                t->file.open_mode = NGX_HTTP_TFS_OPEN_MODE_STAT\n                                     | NGX_HTTP_TFS_OPEN_MODE_READ;\n            }\n\n            ngx_log_error(NGX_LOG_INFO, t->log, 0, \"get block info from ns\");\n            cl = ngx_http_tfs_create_block_info_message(t, segment_data);\n        }\n        break;\n    default:\n        return NULL;\n    }\n\n    return cl;\n}\n\n\nngx_int_t\nngx_http_tfs_name_server_parse_message(ngx_http_tfs_t *t)\n{\n    uint16_t                      action;\n    ngx_int_t                     rc;\n    ngx_http_tfs_segment_data_t  *segment_data;\n\n    rc = NGX_ERROR;\n    action = t->r_ctx.action.code;\n    segment_data = &t->file.segment_data[t->file.segment_index];\n\n    switch (action) {\n    case NGX_HTTP_TFS_ACTION_STAT_FILE:\n        return ngx_http_tfs_parse_block_info_message(t, segment_data);\n\n    case NGX_HTTP_TFS_ACTION_READ_FILE:\n        if (!t->parent\n            && (t->r_ctx.version == 2\n                || (t->is_large_file && !t->is_process_meta_seg)))\n        {\n            rc = ngx_http_tfs_parse_batch_block_info_message(t, segment_data);\n\n        } else {\n            rc = ngx_http_tfs_parse_block_info_message(t, segment_data);\n        }\n        return rc;\n\n    case NGX_HTTP_TFS_ACTION_WRITE_FILE:\n        switch(t->state) {\n        case NGX_HTTP_TFS_STATE_WRITE_GET_GROUP_COUNT:\n            return ngx_http_tfs_parse_ctl_message(t,\n                NGX_HTTP_TFS_CMD_GET_GROUP_COUNT);\n\n        case NGX_HTTP_TFS_STATE_WRITE_GET_GROUP_SEQ:\n            return ngx_http_tfs_parse_ctl_message(t,\n                NGX_HTTP_TFS_CMD_GET_GROUP_SEQ);\n\n        case NGX_HTTP_TFS_STATE_WRITE_CLUSTER_ID_NS:\n            return ngx_http_tfs_parse_ctl_message(t,\n                NGX_HTTP_TFS_CMD_GET_CLUSTER_ID_NS);\n\n        case NGX_HTTP_TFS_STATE_WRITE_GET_BLK_INFO:\n            if (!t->parent\n                && !t->is_rolling_back\n                && (t->r_ctx.version == 2\n                    || (t->is_large_file && !t->is_process_meta_seg)))\n            {\n                rc = ngx_http_tfs_parse_batch_block_info_message(t,\n                                                                 segment_data);\n\n            } else {\n                rc = ngx_http_tfs_parse_block_info_message(t, segment_data);\n            }\n        }\n        return rc;\n\n    case NGX_HTTP_TFS_ACTION_REMOVE_FILE:\n        switch(t->state) {\n        case NGX_HTTP_TFS_STATE_REMOVE_GET_GROUP_COUNT:\n            rc = ngx_http_tfs_parse_ctl_message(t,\n                                              NGX_HTTP_TFS_CMD_GET_GROUP_COUNT);\n            return rc;\n\n        case NGX_HTTP_TFS_STATE_REMOVE_GET_GROUP_SEQ:\n            rc = ngx_http_tfs_parse_ctl_message(t,\n                                                NGX_HTTP_TFS_CMD_GET_GROUP_SEQ);\n            return rc;\n\n        case NGX_HTTP_TFS_STATE_REMOVE_GET_BLK_INFO:\n            return ngx_http_tfs_parse_block_info_message(t, segment_data);\n\n        default:\n            break;\n        }\n    }\n\n    return NGX_ERROR;\n}\n\n\nstatic ngx_chain_t *\nngx_http_tfs_create_block_info_message(ngx_http_tfs_t *t,\n    ngx_http_tfs_segment_data_t *segment_data)\n{\n    size_t                                size;\n    ngx_buf_t                             *b;\n    ngx_chain_t                           *cl;\n    ngx_http_tfs_ns_block_info_request_t  *req;\n\n    size = sizeof(ngx_http_tfs_ns_block_info_request_t);\n\n    b = ngx_create_temp_buf(t->pool, size);\n    if (b == NULL) {\n        return NULL;\n    }\n\n    req = (ngx_http_tfs_ns_block_info_request_t *) b->pos;\n\n    req->header.type = NGX_HTTP_TFS_GET_BLOCK_INFO_MESSAGE;\n    req->header.len = size - sizeof(ngx_http_tfs_header_t);\n    req->header.flag = NGX_HTTP_TFS_PACKET_FLAG;\n    req->header.version = NGX_HTTP_TFS_PACKET_VERSION;\n    req->header.id = ngx_http_tfs_generate_packet_id();\n    req->mode = t->file.open_mode;\n    req->block_id = segment_data->segment_info.block_id;\n    req->fs_count = 0;\n\n    req->header.crc = ngx_http_tfs_crc(NGX_HTTP_TFS_PACKET_FLAG,\n        (const char *) (&req->header + 1), req->header.len);\n\n    b->last += size;\n\n    cl = ngx_alloc_chain_link(t->pool);\n    if (cl == NULL) {\n        return NULL;\n    }\n\n    cl->buf = b;\n    cl->next = NULL;\n\n    return cl;\n}\n\n\nstatic ngx_chain_t *\nngx_http_tfs_create_batch_block_info_message(ngx_http_tfs_t *t)\n{\n    size_t                                       size;\n    uint32_t                                     block_count, real_block_count;\n    ngx_uint_t                                   i, j;\n    ngx_buf_t                                   *b;\n    ngx_chain_t                                 *cl;\n    ngx_http_tfs_segment_data_t                 *segment_data;\n    ngx_http_tfs_ns_batch_block_info_request_t  *req;\n\n    block_count = t->file.segment_count - t->file.segment_index;\n    if (block_count > NGX_HTTP_TFS_MAX_BATCH_COUNT) {\n        block_count = NGX_HTTP_TFS_MAX_BATCH_COUNT;\n    }\n\n    real_block_count = block_count;\n    if (t->file.open_mode & NGX_HTTP_TFS_OPEN_MODE_READ) {\n        segment_data = &t->file.segment_data[t->file.segment_index];\n        for (i = 0; i < block_count; i++, segment_data++) {\n            if (segment_data->cache_hit != NGX_HTTP_TFS_NO_BLOCK_CACHE) {\n                real_block_count--;\n            }\n        }\n    }\n\n    size = sizeof(ngx_http_tfs_ns_batch_block_info_request_t)\n            + real_block_count * sizeof(uint32_t);\n\n    b = ngx_create_temp_buf(t->pool, size);\n    if (b == NULL) {\n        return NULL;\n    }\n\n    req = (ngx_http_tfs_ns_batch_block_info_request_t *) b->pos;\n\n    req->header.type = NGX_HTTP_TFS_BATCH_GET_BLOCK_INFO_MESSAGE;\n    req->header.len = size - sizeof(ngx_http_tfs_header_t);\n    req->header.flag = NGX_HTTP_TFS_PACKET_FLAG;\n    req->header.version = NGX_HTTP_TFS_PACKET_VERSION;\n    req->header.id = ngx_http_tfs_generate_packet_id();\n    req->mode = t->file.open_mode;\n    req->block_count = real_block_count;\n    segment_data = &t->file.segment_data[t->file.segment_index];\n    for (i = 0, j = 0; i < block_count; i++, segment_data++) {\n        if (t->file.open_mode & NGX_HTTP_TFS_OPEN_MODE_READ) {\n            if (segment_data->cache_hit == NGX_HTTP_TFS_NO_BLOCK_CACHE) {\n                req->block_ids[j++] = segment_data->segment_info.block_id;\n            }\n\n        } else {\n            req->block_ids[i] = 0;\n        }\n    }\n    req->header.crc = ngx_http_tfs_crc(NGX_HTTP_TFS_PACKET_FLAG,\n                                       (const char *) (&req->header + 1),\n                                       req->header.len);\n\n    b->last += size;\n\n    cl = ngx_alloc_chain_link(t->pool);\n    if (cl == NULL) {\n        return NULL;\n    }\n\n    cl->buf = b;\n    cl->next = NULL;\n\n    return cl;\n}\n\n\nstatic ngx_chain_t *\nngx_http_tfs_create_ctl_message(ngx_http_tfs_t *t, uint8_t cmd)\n{\n    size_t                          size;\n    ngx_buf_t                      *b;\n    ngx_chain_t                    *cl;\n    ngx_http_tfs_ns_ctl_request_t  *req;\n\n    size = sizeof(ngx_http_tfs_ns_ctl_request_t);\n\n    b = ngx_create_temp_buf(t->pool, size);\n    if (b == NULL) {\n        return NULL;\n    }\n\n    req = (ngx_http_tfs_ns_ctl_request_t *) b->pos;\n    req->header.type = NGX_HTTP_TFS_CLIENT_CMD_MESSAGE;\n    req->header.len = size - sizeof(ngx_http_tfs_header_t);\n    req->header.flag = NGX_HTTP_TFS_PACKET_FLAG;\n    req->header.version = NGX_HTTP_TFS_PACKET_VERSION;\n    req->header.id = ngx_http_tfs_generate_packet_id();\n    req->cmd = NGX_HTTP_TFS_CLIENT_CMD_SET_PARAM;\n    req->value2 = cmd;\n\n    req->header.crc = ngx_http_tfs_crc(NGX_HTTP_TFS_PACKET_FLAG,\n                                       (const char *) (&req->header + 1),\n                                       req->header.len);\n\n    b->last += size;\n\n    cl = ngx_alloc_chain_link(t->pool);\n    if (cl == NULL) {\n        return NULL;\n    }\n\n    cl->buf = b;\n    cl->next = NULL;\n\n    return cl;\n}\n\n\nngx_int_t\nngx_http_tfs_parse_block_info_message(ngx_http_tfs_t *t,\n    ngx_http_tfs_segment_data_t *segment_data)\n{\n    u_char                                 *p;\n    uint16_t                                type;\n    uint32_t                                ds_count;\n    ngx_str_t                               err_msg;\n    ngx_uint_t                              i;\n    ngx_http_tfs_header_t                  *header;\n    ngx_http_tfs_block_info_t              *block_info;\n    ngx_http_tfs_block_cache_key_t          key;\n    ngx_http_tfs_block_cache_value_t        value;\n    ngx_http_tfs_peer_connection_t         *tp;\n    ngx_http_tfs_ns_block_info_response_t  *resp;\n\n    header = (ngx_http_tfs_header_t *) t->header;\n    tp = t->tfs_peer;\n    type = header->type;\n\n    switch (type) {\n    case NGX_HTTP_TFS_STATUS_MESSAGE:\n        ngx_str_set(&err_msg, \"get block info (name server)\");\n        return ngx_http_tfs_status_message(&tp->body_buffer, &err_msg, t->log);\n    }\n\n    resp = (ngx_http_tfs_ns_block_info_response_t *) tp->body_buffer.pos;\n\n    p = tp->body_buffer.pos + sizeof(ngx_http_tfs_ns_block_info_response_t);\n\n    if (t->file.open_mode & NGX_HTTP_TFS_OPEN_MODE_WRITE) {\n        if (resp->ds_count <= 3) {\n            return NGX_HTTP_TFS_EXIT_GENERAL_ERROR;\n        }\n\n        /* flag version leaseid */\n        ds_count = resp->ds_count - 3;\n\n    } else {\n        ds_count = resp->ds_count;\n    }\n\n    t->r_ctx.fsname.file.block_id = resp->block_id;\n\n    segment_data->segment_info.block_id = resp->block_id;\n    block_info = &segment_data->block_info;\n\n    block_info->ds_count = ds_count;\n    block_info->ds_addrs = ngx_pcalloc(t->pool,\n                                       sizeof(ngx_http_tfs_inet_t) * ds_count);\n    if (block_info->ds_addrs == NULL) {\n        return NGX_ERROR;\n    }\n\n    for (i = 0; i < ds_count; i++) {\n        block_info->ds_addrs[i].ip = *(uint32_t *) p;\n        block_info->ds_addrs[i].port = *(uint32_t *) (p + sizeof(uint32_t));\n\n        p += sizeof(uint32_t) * 2;\n    }\n\n    /* insert block cache */\n    if (t->file.open_mode & NGX_HTTP_TFS_OPEN_MODE_READ\n        || t->file.open_mode & NGX_HTTP_TFS_OPEN_MODE_STAT)\n    {\n        key.ns_addr = *((uint64_t*)(&t->name_server_addr));\n        key.block_id = resp->block_id;\n        value.ds_count = block_info->ds_count;\n        value.ds_addrs = (uint64_t *)block_info->ds_addrs;\n        ngx_http_tfs_block_cache_insert(&t->block_cache_ctx,\n                                        t->pool, t->log, &key, &value);\n    }\n\n    if (t->file.open_mode & NGX_HTTP_TFS_OPEN_MODE_WRITE) {\n        /* flag */\n        p += sizeof(uint64_t);\n        /* version */\n        block_info->version = *((int32_t *) p);\n        p += sizeof(int64_t);\n        /* lease id */\n        block_info->lease_id = *((int32_t *) p);\n    }\n\n    segment_data->ds_retry = 0;\n\n    ngx_log_debug5(NGX_LOG_DEBUG_HTTP, t->log, 0,\n                   \"get block info from \"\n                   \"nameserver: %V, block id: %uD, ds count: %uD, \"\n                   \"version: %D, lease id: %D\",\n                   &t->name_server_addr_text, resp->block_id,\n                   block_info->ds_count,\n                   block_info->version, block_info->lease_id);\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_tfs_parse_batch_block_info_message(ngx_http_tfs_t *t,\n    ngx_http_tfs_segment_data_t *segment_data)\n{\n    u_char                                       *p;\n    uint16_t                                      type;\n    uint32_t                                      block_count, complete_count,\n                                                  ds_count, block_id;\n    ngx_str_t                                     err_msg;\n    ngx_uint_t                                    i, j, k;\n    ngx_http_tfs_header_t                        *header;\n    ngx_http_tfs_block_info_t                    *block_info;\n    ngx_http_tfs_peer_connection_t               *tp;\n    ngx_http_tfs_block_cache_key_t                key;\n    ngx_http_tfs_block_cache_value_t              value;\n    ngx_http_tfs_ns_batch_block_info_response_t  *resp;\n\n    header = (ngx_http_tfs_header_t *) t->header;\n    tp = t->tfs_peer;\n    type = header->type;\n\n    switch (type) {\n    case NGX_HTTP_TFS_STATUS_MESSAGE:\n        ngx_str_set(&err_msg, \"batch get block info (name server)\");\n        return ngx_http_tfs_status_message(&tp->body_buffer, &err_msg, t->log);\n    }\n\n    resp = (ngx_http_tfs_ns_batch_block_info_response_t *) tp->body_buffer.pos;\n\n    p = tp->body_buffer.pos\n         + sizeof(ngx_http_tfs_ns_batch_block_info_response_t);\n\n    block_count = t->file.segment_count - t->file.segment_index;\n    if (block_count > NGX_HTTP_TFS_MAX_BATCH_COUNT) {\n        block_count = NGX_HTTP_TFS_MAX_BATCH_COUNT;\n    }\n    t->file.curr_batch_count = resp->block_count;\n\n    for (i = 0; i < resp->block_count; i++) {\n        j = i;\n        /* block id */\n        block_id = *(uint32_t *) p;\n        p += sizeof(uint32_t);\n        /* read need find the right segment according to block id */\n        if (t->file.open_mode & NGX_HTTP_TFS_OPEN_MODE_READ) {\n            for (j = 0; j < block_count; j++) {\n                if (segment_data[j].segment_info.block_id == block_id) {\n                    break;\n                }\n            }\n            if (j == block_count) {\n                return NGX_HTTP_TFS_AGAIN;\n            }\n        }\n        segment_data[j].segment_info.block_id = block_id;\n\n        block_info = &segment_data[j].block_info;\n\n        /* ds count */\n        ds_count = *(uint32_t *) p;\n        p += sizeof(uint32_t);\n\n        if (t->file.open_mode & NGX_HTTP_TFS_OPEN_MODE_WRITE) {\n            if (ds_count <= 3) {\n                return NGX_HTTP_TFS_EXIT_GENERAL_ERROR;\n            }\n\n            /* flag version leaseid */\n            block_info->ds_count = ds_count - 3;\n\n        } else {\n            if (ds_count < 1) {\n                return NGX_HTTP_TFS_EXIT_GENERAL_ERROR;\n            }\n\n            block_info->ds_count = ds_count;\n        }\n\n        block_info->ds_addrs = ngx_pcalloc(t->pool,\n                                        sizeof(ngx_http_tfs_inet_t) * ds_count);\n        if (block_info->ds_addrs == NULL) {\n            return NGX_ERROR;\n        }\n\n        for (k = 0; k < block_info->ds_count; k++) {\n            block_info->ds_addrs[k].ip = *(uint32_t *) p;\n            block_info->ds_addrs[k].port = *(uint32_t *) (p + sizeof(uint32_t));\n\n            p += sizeof(uint32_t) * 2;\n        }\n\n        if (t->file.open_mode & NGX_HTTP_TFS_OPEN_MODE_WRITE) {\n            /* flag */\n            p += sizeof(uint64_t);\n            /* version */\n            block_info->version = *((int32_t *) p);\n            p += sizeof(int64_t);\n            /* lease id */\n            block_info->lease_id = *((int32_t *) p);\n            p += sizeof(int64_t);\n        }\n\n        ngx_log_debug5(NGX_LOG_DEBUG_HTTP, t->log, 0,\n                       \"batch get block info from nameserver: \"\n                       \"%V, block id: %uD, \"\n                       \"ds count: %uD, version: %D, lease id: %D\",\n                       &t->name_server_addr_text, block_id,\n                       block_info->ds_count,\n                       block_info->version, block_info->lease_id);\n\n        /* insert block cache  */\n        if (t->file.open_mode & NGX_HTTP_TFS_OPEN_MODE_READ) {\n            key.ns_addr = *((uint64_t *)(&t->name_server_addr));\n            key.block_id = block_id;\n            value.ds_count = block_info->ds_count;\n            value.ds_addrs = (uint64_t *)block_info->ds_addrs;\n            ngx_http_tfs_block_cache_insert(&t->block_cache_ctx,\n                                            t->pool, t->log, &key, &value);\n        }\n\n        /* reset segment status */\n        segment_data[j].ds_retry = 0;\n    }\n\n    /* check if all semgents complete */\n    if (t->file.open_mode & NGX_HTTP_TFS_OPEN_MODE_READ) {\n        complete_count = 0;\n        for (i = 0; i < block_count; i++) {\n            if (segment_data[i].block_info.ds_addrs != NULL) {\n                complete_count++;\n                continue;\n            }\n\n            /* maybe has duplicate block, find in already complete segment */\n            for (j = 0; j < i; j++) {\n                if (segment_data[i].segment_info.block_id\n                    != segment_data[j].segment_info.block_id)\n                {\n                    continue;\n                }\n\n                /* TODO: check this */\n                segment_data[i].block_info = segment_data[j].block_info;\n                segment_data[i].ds_retry = 0;\n                complete_count++;\n                break;\n            }\n        }\n        if (complete_count != block_count) {\n            return NGX_HTTP_TFS_AGAIN;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_tfs_parse_ctl_message(ngx_http_tfs_t *t, uint8_t cmd)\n{\n    uint32_t                         code;\n    ngx_int_t                        cluster_id;\n    ngx_http_tfs_status_msg_t       *res;\n    ngx_http_tfs_peer_connection_t  *tp;\n\n    tp = t->tfs_peer;\n    res = (ngx_http_tfs_status_msg_t *) tp->body_buffer.pos;\n    code = res->code;\n\n    if (code == NGX_HTTP_TFS_STATUS_MESSAGE_OK && res->error_len > 0) {\n        switch(cmd) {\n        case NGX_HTTP_TFS_CMD_GET_CLUSTER_ID_NS:\n            cluster_id = ngx_atoi(res->error_str, res->error_len - 1);\n            cluster_id = cluster_id - '0';\n\n            if (cluster_id == NGX_ERROR) {\n                ngx_log_error(NGX_LOG_ERR, t->log, 0,\n                              \"invalid cluster id \\\"%s\\\" \", res->error_str);\n                return NGX_ERROR;\n            }\n\n            t->file.cluster_id = cluster_id;\n            break;\n        case NGX_HTTP_TFS_CMD_GET_GROUP_COUNT:\n            t->group_count = ngx_atoi(res->error_str, res->error_len - 1);\n            if (t->group_count == NGX_ERROR || t->group_count <= 0) {\n                ngx_log_error(NGX_LOG_WARN, t->log, 0,\n                              \"invalid  group count \\\"%s\\\" \", res->error_str);\n                t->group_count = 1; /* compatible with old ns(1.3) */\n            }\n            break;\n        case NGX_HTTP_TFS_CMD_GET_GROUP_SEQ:\n            t->group_seq = ngx_atoi(res->error_str, res->error_len - 1);\n            if (t->group_seq == NGX_ERROR || t->group_seq < 0) {\n                ngx_log_error(NGX_LOG_WARN, t->log, 0,\n                              \"invalid  group seq \\\"%s\\\" \", res->error_str);\n                t->group_seq = 0; /* compatible with old ns(1.3) */\n            }\n        }\n\n    } else {\n        ngx_log_error(NGX_LOG_ERR, t->log, 0,\n                      \"tfs name server ctl message invalid\");\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "modules/ngx_http_tfs_module/ngx_http_tfs_name_server_message.h",
    "content": "\n/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#ifndef _NGX_HTTP_TFS_NAME_SERVER_MESSAGE_H_INCLUDED_\n#define _NGX_HTTP_TFS_NAME_SERVER_MESSAGE_H_INCLUDED_\n\n\n#include <ngx_http_tfs.h>\n\n\nngx_chain_t *ngx_http_tfs_name_server_create_message(ngx_http_tfs_t *t);\nngx_int_t ngx_http_tfs_name_server_parse_message(ngx_http_tfs_t *t);\nngx_int_t ngx_http_tfs_select_name_server(ngx_http_tfs_t *t,\n    ngx_http_tfs_rcs_info_t *rc_info, ngx_http_tfs_inet_t *addr,\n    ngx_str_t *addr_text);\n\n\n#endif  /* _NGX_HTTP_TFS_NAME_SERVER_MESSAGE_H_INCLUDED_ */\n"
  },
  {
    "path": "modules/ngx_http_tfs_module/ngx_http_tfs_peer_connection.c",
    "content": "\n/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#include <ngx_http_tfs_peer_connection.h>\n#include <ngx_http_tfs_server_handler.h>\n#include <ngx_http_tfs_errno.h>\n\n\nstatic ngx_str_t rcs_name = ngx_string(\"rc server\");\nstatic ngx_str_t ns_name = ngx_string(\"name server\");\nstatic ngx_str_t ds_name = ngx_string(\"data server\");\nstatic ngx_str_t rs_name = ngx_string(\"root server\");\nstatic ngx_str_t ms_name = ngx_string(\"meta server\");\n\n\nngx_int_t\nngx_http_tfs_peer_init(ngx_http_tfs_t *t)\n{\n    char                            *addr;\n    uint16_t                         port;\n    ngx_http_connection_pool_t      *conn_pool;\n    ngx_http_tfs_peer_connection_t  *rc_server, *name_server, *root_server,\n                                    *meta_server, *data_server;\n\n    conn_pool = t->main_conf->conn_pool;\n\n    t->tfs_peer_servers = ngx_pcalloc(t->pool,\n        sizeof(ngx_http_tfs_peer_connection_t) * NGX_HTTP_TFS_SERVER_COUNT);\n    if (t->tfs_peer_servers == NULL) {\n        return NGX_ERROR;\n    }\n\n    name_server = &t->tfs_peer_servers[NGX_HTTP_TFS_NAME_SERVER];\n    data_server = &t->tfs_peer_servers[NGX_HTTP_TFS_DATA_SERVER];\n    root_server = &t->tfs_peer_servers[NGX_HTTP_TFS_ROOT_SERVER];\n    meta_server = &t->tfs_peer_servers[NGX_HTTP_TFS_META_SERVER];\n\n    /* rc server */\n    if (t->loc_conf->upstream->enable_rcs) {\n        rc_server = &t->tfs_peer_servers[NGX_HTTP_TFS_RC_SERVER];\n        rc_server->peer.sockaddr = t->loc_conf->upstream->ups_addr->sockaddr;\n        rc_server->peer.socklen = t->loc_conf->upstream->ups_addr->socklen;\n        rc_server->peer.log = t->log;\n        rc_server->peer.name = &rcs_name;\n        rc_server->peer.data = conn_pool;\n        rc_server->peer.get = conn_pool->get_peer;\n        rc_server->peer.free = conn_pool->free_peer;\n        rc_server->peer.log_error = NGX_ERROR_ERR;\n        addr = inet_ntoa(((struct sockaddr_in*)\n                          (rc_server->peer.sockaddr))->sin_addr);\n        port = ntohs(((struct sockaddr_in*)\n                      (rc_server->peer.sockaddr))->sin_port);\n        ngx_sprintf(rc_server->peer_addr_text, \"%s:%d\", addr, port);\n\n    } else {\n        name_server->peer.sockaddr = t->loc_conf->upstream->ups_addr->sockaddr;\n        name_server->peer.socklen = t->loc_conf->upstream->ups_addr->socklen;\n        addr = inet_ntoa(((struct sockaddr_in*)\n                          (name_server->peer.sockaddr))->sin_addr);\n        port = ntohs(((struct sockaddr_in*)\n                      (name_server->peer.sockaddr))->sin_port);\n        ngx_sprintf(name_server->peer_addr_text, \"%s:%d\", addr, port);\n    }\n\n    /* name server */\n    name_server->peer.log = t->log;\n    name_server->peer.name = &ns_name;\n    name_server->peer.data = conn_pool;\n    name_server->peer.get = conn_pool->get_peer;\n    name_server->peer.free = conn_pool->free_peer;\n    name_server->peer.log_error = NGX_ERROR_ERR;\n\n    /* data server */\n    data_server->peer.log = t->log;\n    data_server->peer.name = &ds_name;\n    data_server->peer.data = conn_pool;\n    data_server->peer.get = conn_pool->get_peer;\n    data_server->peer.free = conn_pool->free_peer;\n    data_server->peer.log_error = NGX_ERROR_ERR;\n\n    if (t->r_ctx.version == 1) {\n        t->tfs_peer_count = 3;\n\n    } else {\n        /* root server */\n        root_server->peer.log = t->log;\n        root_server->peer.name = &rs_name;\n        root_server->peer.data = conn_pool;\n        root_server->peer.get = conn_pool->get_peer;\n        root_server->peer.free = conn_pool->free_peer;\n        root_server->peer.log_error = NGX_ERROR_ERR;\n\n        /* meta server */\n        meta_server->peer.log = t->log;\n        meta_server->peer.name = &ms_name;\n        meta_server->peer.data = conn_pool;\n        meta_server->peer.get = conn_pool->get_peer;\n        meta_server->peer.free = conn_pool->free_peer;\n        meta_server->peer.log_error = NGX_ERROR_ERR;\n\n        t->tfs_peer_count = 5;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_http_tfs_peer_connection_t *\nngx_http_tfs_select_peer_v1(ngx_http_tfs_t *t)\n{\n    switch (t->r_ctx.action.code) {\n    case NGX_HTTP_TFS_ACTION_REMOVE_FILE:\n        switch (t->state) {\n        case NGX_HTTP_TFS_STATE_REMOVE_START:\n            t->create_request = ngx_http_tfs_create_rcs_request;\n            t->process_request_body = ngx_http_tfs_process_rcs;\n            t->input_filter = NULL;\n            return &t->tfs_peer_servers[NGX_HTTP_TFS_RC_SERVER];\n\n        case NGX_HTTP_TFS_STATE_REMOVE_GET_GROUP_COUNT:\n        case NGX_HTTP_TFS_STATE_REMOVE_GET_GROUP_SEQ:\n        case NGX_HTTP_TFS_STATE_REMOVE_GET_BLK_INFO:\n            t->create_request = ngx_http_tfs_create_ns_request;\n            t->process_request_body = ngx_http_tfs_process_ns;\n            t->input_filter = NULL;\n            t->retry_handler = NULL;\n            return &t->tfs_peer_servers[NGX_HTTP_TFS_NAME_SERVER];\n\n        case NGX_HTTP_TFS_STATE_REMOVE_STAT_FILE:\n            t->create_request = ngx_http_tfs_create_ds_request;\n            t->process_request_body = ngx_http_tfs_process_ds;\n            t->input_filter = NULL;\n            t->retry_handler = ngx_http_tfs_retry_ds;\n            return &t->tfs_peer_servers[NGX_HTTP_TFS_DATA_SERVER];\n\n        case NGX_HTTP_TFS_STATE_REMOVE_READ_META_SEGMENT:\n            t->create_request = ngx_http_tfs_create_ds_request;\n            t->process_request_body = ngx_http_tfs_process_ds_read;\n            t->input_filter = ngx_http_tfs_process_ds_input_filter;\n            t->retry_handler = ngx_http_tfs_retry_ds;\n            return &t->tfs_peer_servers[NGX_HTTP_TFS_DATA_SERVER];\n\n        case NGX_HTTP_TFS_STATE_REMOVE_DELETE_DATA:\n            t->create_request = ngx_http_tfs_create_ds_request;\n            t->process_request_body = ngx_http_tfs_process_ds;\n            t->input_filter = NULL;\n            t->retry_handler = NULL;\n            return &t->tfs_peer_servers[NGX_HTTP_TFS_DATA_SERVER];\n\n        case NGX_HTTP_TFS_STATE_REMOVE_DONE:\n            return t->tfs_peer;\n\n        default:\n            return NULL;\n        }\n        break;\n    case NGX_HTTP_TFS_ACTION_READ_FILE:\n        switch (t->state) {\n        case NGX_HTTP_TFS_STATE_READ_START:\n            t->create_request = ngx_http_tfs_create_rcs_request;\n            t->process_request_body = ngx_http_tfs_process_rcs;\n            t->input_filter = NULL;\n            return &t->tfs_peer_servers[NGX_HTTP_TFS_RC_SERVER];\n\n        case NGX_HTTP_TFS_STATE_READ_GET_BLK_INFO:\n            t->create_request = ngx_http_tfs_create_ns_request;\n            t->process_request_body = ngx_http_tfs_process_ns;\n            t->input_filter = NULL;\n            t->retry_handler = ngx_http_tfs_retry_ns;\n            return &t->tfs_peer_servers[NGX_HTTP_TFS_NAME_SERVER];\n\n        case NGX_HTTP_TFS_STATE_READ_READ_DATA:\n            t->create_request = ngx_http_tfs_create_ds_request;\n            t->process_request_body = ngx_http_tfs_process_ds_read;\n            t->input_filter = ngx_http_tfs_process_ds_input_filter;\n            t->retry_handler = ngx_http_tfs_retry_ds;\n            return &t->tfs_peer_servers[NGX_HTTP_TFS_DATA_SERVER];\n\n        case NGX_HTTP_TFS_STATE_READ_DONE:\n            t->input_filter = NULL;\n            return t->tfs_peer;\n\n        default:\n            return NULL;\n        }\n        break;\n    case NGX_HTTP_TFS_ACTION_WRITE_FILE:\n        switch (t->state) {\n        case NGX_HTTP_TFS_STATE_WRITE_START:\n            t->create_request = ngx_http_tfs_create_rcs_request;\n            t->process_request_body = ngx_http_tfs_process_rcs;\n            t->input_filter = NULL;\n            return &t->tfs_peer_servers[NGX_HTTP_TFS_RC_SERVER];\n\n        case NGX_HTTP_TFS_STATE_WRITE_GET_GROUP_COUNT:\n        case NGX_HTTP_TFS_STATE_WRITE_GET_GROUP_SEQ:\n        case NGX_HTTP_TFS_STATE_WRITE_CLUSTER_ID_NS:\n        case NGX_HTTP_TFS_STATE_WRITE_GET_BLK_INFO:\n            t->create_request = ngx_http_tfs_create_ns_request;\n            t->process_request_body = ngx_http_tfs_process_ns;\n            t->input_filter = NULL;\n            t->retry_handler = ngx_http_tfs_retry_ns;\n            return &t->tfs_peer_servers[NGX_HTTP_TFS_NAME_SERVER];\n\n        case NGX_HTTP_TFS_STATE_WRITE_STAT_DUP_FILE:\n        case NGX_HTTP_TFS_STATE_WRITE_CREATE_FILE_NAME:\n        case NGX_HTTP_TFS_STATE_WRITE_WRITE_DATA:\n        case NGX_HTTP_TFS_STATE_WRITE_CLOSE_FILE:\n        case NGX_HTTP_TFS_STATE_WRITE_DELETE_DATA:\n            t->create_request = ngx_http_tfs_create_ds_request;\n            t->process_request_body = ngx_http_tfs_process_ds;\n            t->input_filter = NULL;\n            t->retry_handler = ngx_http_tfs_retry_ds;\n            return &t->tfs_peer_servers[NGX_HTTP_TFS_DATA_SERVER];\n\n        case NGX_HTTP_TFS_STATE_WRITE_DONE:\n            t->input_filter = NULL;\n            return t->tfs_peer;\n        default:\n            return NULL;\n        }\n        break;\n    case NGX_HTTP_TFS_ACTION_STAT_FILE:\n        switch (t->state) {\n        case NGX_HTTP_TFS_STATE_STAT_START:\n            t->create_request = ngx_http_tfs_create_rcs_request;\n            t->process_request_body = ngx_http_tfs_process_rcs;\n            t->input_filter = NULL;\n            return &t->tfs_peer_servers[NGX_HTTP_TFS_RC_SERVER];\n\n        case NGX_HTTP_TFS_STATE_STAT_GET_BLK_INFO:\n            t->create_request = ngx_http_tfs_create_ns_request;\n            t->process_request_body = ngx_http_tfs_process_ns;\n            t->input_filter = NULL;\n            t->retry_handler = ngx_http_tfs_retry_ns;\n            return &t->tfs_peer_servers[NGX_HTTP_TFS_NAME_SERVER];\n\n        case NGX_HTTP_TFS_STATE_STAT_STAT_FILE:\n            t->create_request = ngx_http_tfs_create_ds_request;\n            t->process_request_body = ngx_http_tfs_process_ds;\n            t->input_filter = NULL;\n            t->retry_handler = ngx_http_tfs_retry_ds;\n            return &t->tfs_peer_servers[NGX_HTTP_TFS_DATA_SERVER];\n\n        default:\n            return NULL;\n        }\n        break;\n\n    case NGX_HTTP_TFS_ACTION_KEEPALIVE:\n        t->create_request = ngx_http_tfs_create_rcs_request;\n        t->process_request_body = ngx_http_tfs_process_rcs;\n        t->input_filter = NULL;\n        return &t->tfs_peer_servers[NGX_HTTP_TFS_RC_SERVER];\n\n    default:\n        break;\n    }\n\n    return NULL;\n}\n\n\nstatic ngx_http_tfs_peer_connection_t *\nngx_http_tfs_select_peer_v2(ngx_http_tfs_t *t)\n{\n    switch (t->r_ctx.action.code) {\n    case NGX_HTTP_TFS_ACTION_GET_APPID:\n            t->create_request = ngx_http_tfs_create_rcs_request;\n            t->process_request_body = ngx_http_tfs_process_rcs;\n            return &t->tfs_peer_servers[NGX_HTTP_TFS_RC_SERVER];\n\n    case NGX_HTTP_TFS_ACTION_REMOVE_FILE:\n        switch (t->state) {\n        case NGX_HTTP_TFS_STATE_REMOVE_START:\n            t->create_request = ngx_http_tfs_create_rcs_request;\n            t->process_request_body = ngx_http_tfs_process_rcs;\n            return &t->tfs_peer_servers[NGX_HTTP_TFS_RC_SERVER];\n\n        case NGX_HTTP_TFS_STATE_REMOVE_GET_META_TABLE:\n            t->create_request = ngx_http_tfs_create_rs_request;\n            t->process_request_body = ngx_http_tfs_process_rs;\n            return &t->tfs_peer_servers[NGX_HTTP_TFS_ROOT_SERVER];\n\n        case NGX_HTTP_TFS_STATE_REMOVE_NOTIFY_MS:\n        case NGX_HTTP_TFS_STATE_REMOVE_GET_FRAG_INFO:\n            t->create_request = ngx_http_tfs_create_ms_request;\n            t->process_request_body = ngx_http_tfs_process_ms;\n            return &t->tfs_peer_servers[NGX_HTTP_TFS_META_SERVER];\n\n        case NGX_HTTP_TFS_STATE_REMOVE_GET_GROUP_COUNT:\n        case NGX_HTTP_TFS_STATE_REMOVE_GET_GROUP_SEQ:\n        case NGX_HTTP_TFS_STATE_REMOVE_GET_BLK_INFO:\n            t->create_request = ngx_http_tfs_create_ns_request;\n            t->process_request_body = ngx_http_tfs_process_ns;\n            return &t->tfs_peer_servers[NGX_HTTP_TFS_NAME_SERVER];\n\n        case NGX_HTTP_TFS_STATE_REMOVE_DELETE_DATA:\n            t->create_request = ngx_http_tfs_create_ds_request;\n            t->process_request_body = ngx_http_tfs_process_ds;\n            return &t->tfs_peer_servers[NGX_HTTP_TFS_DATA_SERVER];\n\n        case NGX_HTTP_TFS_STATE_REMOVE_DONE:\n            return t->tfs_peer;\n        default:\n            return NULL;\n        }\n        break;\n    case NGX_HTTP_TFS_ACTION_READ_FILE:\n        switch (t->state) {\n        case NGX_HTTP_TFS_STATE_READ_START:\n            t->create_request = ngx_http_tfs_create_rcs_request;\n            t->process_request_body = ngx_http_tfs_process_rcs;\n            t->input_filter = NULL;\n            t->retry_handler = NULL;\n            return &t->tfs_peer_servers[NGX_HTTP_TFS_RC_SERVER];\n\n        case NGX_HTTP_TFS_STATE_READ_GET_META_TABLE:\n            t->create_request = ngx_http_tfs_create_rs_request;\n            t->process_request_body = ngx_http_tfs_process_rs;\n            t->input_filter = NULL;\n            t->retry_handler = NULL;\n            return &t->tfs_peer_servers[NGX_HTTP_TFS_ROOT_SERVER];\n\n        case NGX_HTTP_TFS_STATE_READ_GET_FRAG_INFO:\n            t->create_request = ngx_http_tfs_create_ms_request;\n            t->process_request_body = ngx_http_tfs_process_ms;\n            t->input_filter = NULL;\n            t->retry_handler = NULL;\n            return &t->tfs_peer_servers[NGX_HTTP_TFS_META_SERVER];\n\n        case NGX_HTTP_TFS_STATE_READ_GET_BLK_INFO:\n            t->create_request = ngx_http_tfs_create_ns_request;\n            t->process_request_body = ngx_http_tfs_process_ns;\n            t->input_filter = NULL;\n            t->retry_handler = ngx_http_tfs_retry_ns;\n            return &t->tfs_peer_servers[NGX_HTTP_TFS_NAME_SERVER];\n\n        case NGX_HTTP_TFS_STATE_READ_READ_DATA:\n            t->create_request = ngx_http_tfs_create_ds_request;\n            t->process_request_body = ngx_http_tfs_process_ds_read;\n            t->input_filter = ngx_http_tfs_process_ds_input_filter;\n            t->retry_handler = ngx_http_tfs_retry_ds;\n            return &t->tfs_peer_servers[NGX_HTTP_TFS_DATA_SERVER];\n\n        case NGX_HTTP_TFS_STATE_READ_DONE:\n            t->input_filter = NULL;\n            t->retry_handler = NULL;\n            return t->tfs_peer;\n        default:\n            return NULL;\n        }\n        break;\n    case NGX_HTTP_TFS_ACTION_WRITE_FILE:\n        switch (t->state) {\n        case NGX_HTTP_TFS_STATE_WRITE_START:\n            t->create_request = ngx_http_tfs_create_rcs_request;\n            t->process_request_body = ngx_http_tfs_process_rcs;\n            t->input_filter = NULL;\n            t->retry_handler = NULL;\n            return &t->tfs_peer_servers[NGX_HTTP_TFS_RC_SERVER];\n\n        case NGX_HTTP_TFS_STATE_WRITE_GET_META_TABLE:\n            t->create_request = ngx_http_tfs_create_rs_request;\n            t->process_request_body = ngx_http_tfs_process_rs;\n            t->input_filter = NULL;\n            t->retry_handler = NULL;\n            return &t->tfs_peer_servers[NGX_HTTP_TFS_ROOT_SERVER];\n\n        case NGX_HTTP_TFS_STATE_WRITE_CLUSTER_ID_MS:\n        case NGX_HTTP_TFS_STATE_WRITE_WRITE_MS:\n            t->create_request = ngx_http_tfs_create_ms_request;\n            t->process_request_body = ngx_http_tfs_process_ms;\n            t->input_filter = NULL;\n            t->retry_handler = NULL;\n            return &t->tfs_peer_servers[NGX_HTTP_TFS_META_SERVER];\n\n        case NGX_HTTP_TFS_STATE_WRITE_CLUSTER_ID_NS:\n        case NGX_HTTP_TFS_STATE_WRITE_GET_BLK_INFO:\n            t->create_request = ngx_http_tfs_create_ns_request;\n            t->process_request_body = ngx_http_tfs_process_ns;\n            t->input_filter = NULL;\n            t->retry_handler = ngx_http_tfs_retry_ns;\n            return &t->tfs_peer_servers[NGX_HTTP_TFS_NAME_SERVER];\n\n        case NGX_HTTP_TFS_STATE_WRITE_CREATE_FILE_NAME:\n        case NGX_HTTP_TFS_STATE_WRITE_WRITE_DATA:\n        case NGX_HTTP_TFS_STATE_WRITE_CLOSE_FILE:\n            t->create_request = ngx_http_tfs_create_ds_request;\n            t->process_request_body = ngx_http_tfs_process_ds;\n            t->input_filter = NULL;\n            /* FIXME: it's better to retry_ns instead of ds when write failed */\n            t->retry_handler = ngx_http_tfs_retry_ds;\n            return &t->tfs_peer_servers[NGX_HTTP_TFS_DATA_SERVER];\n\n        case NGX_HTTP_TFS_STATE_WRITE_DONE:\n            t->input_filter = NULL;\n            t->retry_handler = NULL;\n            return t->tfs_peer;\n        default:\n            return NULL;\n        }\n        break;\n    case NGX_HTTP_TFS_ACTION_CREATE_FILE:\n    case NGX_HTTP_TFS_ACTION_CREATE_DIR:\n    case NGX_HTTP_TFS_ACTION_LS_FILE:\n    case NGX_HTTP_TFS_ACTION_LS_DIR:\n    case NGX_HTTP_TFS_ACTION_MOVE_DIR:\n    case NGX_HTTP_TFS_ACTION_MOVE_FILE:\n    case NGX_HTTP_TFS_ACTION_REMOVE_DIR:\n        switch (t->state) {\n        case NGX_HTTP_TFS_STATE_ACTION_START:\n            t->create_request = ngx_http_tfs_create_rcs_request;\n            t->process_request_body = ngx_http_tfs_process_rcs;\n            t->input_filter = NULL;\n            return &t->tfs_peer_servers[NGX_HTTP_TFS_RC_SERVER];\n\n        case NGX_HTTP_TFS_STATE_ACTION_GET_META_TABLE:\n            t->create_request = ngx_http_tfs_create_rs_request;\n            t->process_request_body = ngx_http_tfs_process_rs;\n            t->input_filter = NULL;\n            return &t->tfs_peer_servers[NGX_HTTP_TFS_ROOT_SERVER];\n\n        case NGX_HTTP_TFS_STATE_ACTION_PROCESS:\n            t->create_request = ngx_http_tfs_create_ms_request;\n            if (t->r_ctx.action.code == NGX_HTTP_TFS_ACTION_LS_DIR) {\n                t->process_request_body = ngx_http_tfs_process_ms_ls_dir;\n                t->input_filter = ngx_http_tfs_process_ms_input_filter;\n            } else {\n                t->process_request_body = ngx_http_tfs_process_ms;\n                t->input_filter = NULL;\n            }\n            return &t->tfs_peer_servers[NGX_HTTP_TFS_META_SERVER];\n\n        case NGX_HTTP_TFS_STATE_ACTION_DONE:\n            t->input_filter = NULL;\n            return t->tfs_peer;\n        default:\n            return NULL;\n        }\n        break;\n    default:\n        break;\n    }\n\n    return NULL;\n}\n\n\nngx_http_tfs_peer_connection_t *\nngx_http_tfs_select_peer(ngx_http_tfs_t *t)\n{\n    if (t->r_ctx.version == 1) {\n        return ngx_http_tfs_select_peer_v1(t);\n    }\n\n    if (t->r_ctx.version == 2) {\n        return ngx_http_tfs_select_peer_v2(t);\n    }\n\n    return NULL;\n}\n\n"
  },
  {
    "path": "modules/ngx_http_tfs_module/ngx_http_tfs_peer_connection.h",
    "content": "\n/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#ifndef _NGX_HTTP_TFS_PEER_CONNECTION_INCLUDED_\n#define _NGX_HTTP_TFS_PEER_CONNECTION_INCLUDED_\n\n\n#include <ngx_core.h>\n#include <ngx_http.h>\n#include <ngx_http_tfs.h>\n\n\nstruct ngx_http_tfs_peer_connection_s {\n    ngx_peer_connection_t            peer;\n    u_char                           peer_addr_text[24];\n    ngx_buf_t                        body_buffer;\n    ngx_pool_t                      *pool;\n};\n\n\nngx_int_t ngx_http_tfs_peer_init(ngx_http_tfs_t *t);\nngx_http_tfs_peer_connection_t *ngx_http_tfs_select_peer(ngx_http_tfs_t *t);\n\n\n#endif  /* _NGX_HTTP_TFS_PEER_CONNECTION_INCLUDED_ */\n"
  },
  {
    "path": "modules/ngx_http_tfs_module/ngx_http_tfs_protocol.h",
    "content": "\n/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#ifndef _NGX_HTTP_TFS_PROTOCOL_H_\n#define _NGX_HTTP_TFS_PROTOCOL_H_\n\n\n#define NGX_PACKED __attribute__ ((__packed__))\n\n#define NGX_HTTP_TFS_PACKET_FLAG            0x4E534654      /* TFSN */\n#define NGX_HTTP_TFS_PACKET_VERSION         2\n\n#define NGX_HTTP_TFS_READ                   0\n#define NGX_HTTP_TFS_READ_V2                1\n\n#define NGX_HTTP_TFS_RAW_FILE_INFO_SIZE     sizeof(ngx_http_tfs_raw_file_info_t)\n#define NGX_HTTP_TFS_READ_V2_TAIL_LEN           \\\n    sizeof(ngx_http_tfs_ds_readv2_response_tail_t)\n\ntypedef enum\n{\n    NGX_HTTP_TFS_STATUS_MESSAGE = 1,\n    NGX_HTTP_TFS_GET_BLOCK_INFO_MESSAGE = 2,\n    NGX_HTTP_TFS_SET_BLOCK_INFO_MESSAGE = 3,\n    NGX_HTTP_TFS_CARRY_BLOCK_MESSAGE = 4,\n    NGX_HTTP_TFS_SET_DATASERVER_MESSAGE = 5,\n    NGX_HTTP_TFS_UPDATE_BLOCK_INFO_MESSAGE = 6,\n    NGX_HTTP_TFS_READ_DATA_MESSAGE = 7,\n    NGX_HTTP_TFS_RESP_READ_DATA_MESSAGE = 8,\n    NGX_HTTP_TFS_WRITE_DATA_MESSAGE = 9,\n    NGX_HTTP_TFS_CLOSE_FILE_MESSAGE = 10,\n    NGX_HTTP_TFS_UNLINK_FILE_MESSAGE = 11,\n    NGX_HTTP_TFS_REPLICATE_BLOCK_MESSAGE = 12,\n    NGX_HTTP_TFS_COMPACT_BLOCK_MESSAGE = 13,\n    NGX_HTTP_TFS_GET_SERVER_STATUS_MESSAGE = 14,\n    NGX_HTTP_TFS_SHOW_SERVER_INFORMATION_MESSAGE = 15,\n    NGX_HTTP_TFS_SUSPECT_DATASERVER_MESSAGE = 16,\n    NGX_HTTP_TFS_FILE_INFO_MESSAGE = 17,\n    NGX_HTTP_TFS_RESP_FILE_INFO_MESSAGE = 18,\n    NGX_HTTP_TFS_RENAME_FILE_MESSAGE = 19,\n    NGX_HTTP_TFS_CLIENT_CMD_MESSAGE = 20,\n    NGX_HTTP_TFS_CREATE_FILENAME_MESSAGE = 21,\n    NGX_HTTP_TFS_RESP_CREATE_FILENAME_MESSAGE = 22,\n    NGX_HTTP_TFS_ROLLBACK_MESSAGE = 23,\n    NGX_HTTP_TFS_RESP_HEART_MESSAGE = 24,\n    NGX_HTTP_TFS_RESET_BLOCK_VERSION_MESSAGE = 25,\n    NGX_HTTP_TFS_BLOCK_FILE_INFO_MESSAGE = 26,\n    NGX_HTTP_TFS_LEGACY_UNIQUE_FILE_MESSAGE = 27,\n    NGX_HTTP_TFS_LEGACY_RETRY_COMMAND_MESSAGE = 28,\n    NGX_HTTP_TFS_NEW_BLOCK_MESSAGE = 29,\n    NGX_HTTP_TFS_REMOVE_BLOCK_MESSAGE = 30,\n    NGX_HTTP_TFS_LIST_BLOCK_MESSAGE = 31,\n    NGX_HTTP_TFS_RESP_LIST_BLOCK_MESSAGE = 32,\n    NGX_HTTP_TFS_BLOCK_WRITE_COMPLETE_MESSAGE = 33,\n    NGX_HTTP_TFS_BLOCK_RAW_META_MESSAGE = 34,\n    NGX_HTTP_TFS_WRITE_RAW_DATA_MESSAGE = 35,\n    NGX_HTTP_TFS_WRITE_INFO_BATCH_MESSAGE = 36,\n    NGX_HTTP_TFS_BLOCK_COMPACT_COMPLETE_MESSAGE = 37,\n    NGX_HTTP_TFS_READ_DATA_MESSAGE_V2 = 38,\n    NGX_HTTP_TFS_RESP_READ_DATA_MESSAGE_V2 = 39,\n    NGX_HTTP_TFS_LIST_BITMAP_MESSAGE =40,\n    NGX_HTTP_TFS_RESP_LIST_BITMAP_MESSAGE = 41,\n    NGX_HTTP_TFS_RELOAD_CONFIG_MESSAGE = 42,\n    NGX_HTTP_TFS_SERVER_META_INFO_MESSAGE = 43,\n    NGX_HTTP_TFS_RESP_SERVER_META_INFO_MESSAGE = 44,\n    NGX_HTTP_TFS_READ_RAW_DATA_MESSAGE = 45,\n    NGX_HTTP_TFS_RESP_READ_RAW_DATA_MESSAGE = 46,\n    NGX_HTTP_TFS_REPLICATE_INFO_MESSAGE = 47,\n    NGX_HTTP_TFS_ACCESS_STAT_INFO_MESSAGE = 48,\n    NGX_HTTP_TFS_READ_SCALE_IMAGE_MESSAGE = 49,\n    NGX_HTTP_TFS_OPLOG_SYNC_MESSAGE = 50,\n    NGX_HTTP_TFS_OPLOG_SYNC_RESPONSE_MESSAGE = 51,\n    NGX_HTTP_TFS_MASTER_AND_SLAVE_HEART_MESSAGE = 52,\n    NGX_HTTP_TFS_MASTER_AND_SLAVE_HEART_RESPONSE_MESSAGE = 53,\n    NGX_HTTP_TFS_HEARTBEAT_AND_NS_HEART_MESSAGE = 54,\n    NGX_HTTP_TFS_OWNER_CHECK_MESSAGE = 55,\n    NGX_HTTP_TFS_GET_BLOCK_LIST_MESSAGE = 56,\n    NGX_HTTP_TFS_CRC_ERROR_MESSAGE = 57,\n    NGX_HTTP_TFS_ADMIN_CMD_MESSAGE = 58,\n    NGX_HTTP_TFS_BATCH_GET_BLOCK_INFO_MESSAGE = 59,\n    NGX_HTTP_TFS_BATCH_SET_BLOCK_INFO_MESSAGE = 60,\n    NGX_HTTP_TFS_REMOVE_BLOCK_RESPONSE_MESSAGE = 61,\n    NGX_HTTP_TFS_READ_DATA_MESSAGE_V3 = 62,\n    NGX_HTTP_TFS_RESP_READ_DATA_MESSAGE_V3 = 63,\n    NGX_HTTP_TFS_DUMP_PLAN_MESSAGE = 64,\n    NGX_HTTP_TFS_DUMP_PLAN_RESPONSE_MESSAGE = 65,\n    NGX_HTTP_TFS_REQ_RC_LOGIN_MESSAGE = 66,\n    NGX_HTTP_TFS_RSP_RC_LOGIN_MESSAGE = 67,\n    NGX_HTTP_TFS_REQ_RC_KEEPALIVE_MESSAGE = 68,\n    NGX_HTTP_TFS_RSP_RC_KEEPALIVE_MESSAGE = 69,\n    NGX_HTTP_TFS_REQ_RC_LOGOUT_MESSAGE = 70,\n    NGX_HTTP_TFS_REQ_RC_RELOAD_MESSAGE = 71,\n    NGX_HTTP_TFS_GET_DATASERVER_INFORMATION_MESSAGE = 72,\n    NGX_HTTP_TFS_GET_DATASERVER_INFORMATION_RESPONSE_MESSAGE = 73,\n    NGX_HTTP_TFS_FILEPATH_ACTION_MESSAGE = 74,\n    NGX_HTTP_TFS_WRITE_FILEPATH_MESSAGE = 75,\n    NGX_HTTP_TFS_READ_FILEPATH_MESSAGE = 76,\n    NGX_HTTP_TFS_RESP_READ_FILEPATH_MESSAGE = 77,\n    NGX_HTTP_TFS_LS_FILEPATH_MESSAGE = 78,\n    NGX_HTTP_TFS_RESP_LS_FILEPATH_MESSAGE = 79,\n    NGX_HTTP_TFS_REQ_RT_UPDATE_TABLE_MESSAGE = 80,\n    NGX_HTTP_TFS_RSP_RT_UPDATE_TABLE_MESSAGE = 81,\n    NGX_HTTP_TFS_REQ_RT_MS_KEEPALIVE_MESSAGE = 82,\n    NGX_HTTP_TFS_RSP_RT_MS_KEEPALIVE_MESSAGE = 83,\n    NGX_HTTP_TFS_REQ_RT_GET_TABLE_MESSAGE = 84,\n    NGX_HTTP_TFS_RSP_RT_GET_TABLE_MESSAGE = 85,\n    NGX_HTTP_TFS_REQ_RT_RS_KEEPALIVE_MESSAGE = 86,\n    NGX_HTTP_TFS_RSP_RT_RS_KEEPALIVE_MESSAGE = 87,\n    NGX_HTTP_TFS_LOCAL_PACKET = 500\n} ngx_http_tfs_status_msg_e;\n\n\ntypedef enum\n{\n    NGX_HTTP_TFS_STATUS_MESSAGE_OK = 0,\n    NGX_HTTP_TFS_STATUS_MESSAGE_ERROR,\n    NGX_HTTP_TFS_STATUS_NEED_SEND_BLOCK_INFO,\n    NGX_HTTP_TFS_STATUS_MESSAGE_PING,\n    NGX_HTTP_TFS_STATUS_MESSAGE_REMOVE,\n    NGX_HTTP_TFS_STATUS_MESSAGE_BLOCK_FULL,\n    NGX_HTTP_TFS_STATUS_MESSAGE_ACCESS_DENIED\n} ngx_http_tfs_message_status_t;\n\n\ntypedef enum\n{\n    NGX_HTTP_TFS_ACTION_NON = 0,\n    NGX_HTTP_TFS_ACTION_CREATE_DIR = 1,\n    NGX_HTTP_TFS_ACTION_CREATE_FILE = 2,\n    NGX_HTTP_TFS_ACTION_REMOVE_DIR = 3,\n    NGX_HTTP_TFS_ACTION_REMOVE_FILE = 4,\n    NGX_HTTP_TFS_ACTION_MOVE_DIR = 5,\n    NGX_HTTP_TFS_ACTION_MOVE_FILE = 6,\n    NGX_HTTP_TFS_ACTION_READ_FILE = 7,\n    NGX_HTTP_TFS_ACTION_LS_DIR = 8,\n    NGX_HTTP_TFS_ACTION_LS_FILE = 9,\n    NGX_HTTP_TFS_ACTION_WRITE_FILE = 10,\n    NGX_HTTP_TFS_ACTION_STAT_FILE = 11,\n    NGX_HTTP_TFS_ACTION_KEEPALIVE = 12,\n    NGX_HTTP_TFS_ACTION_GET_APPID = 13,\n    NGX_HTTP_TFS_ACTION_UNDELETE_FILE = 14,\n    NGX_HTTP_TFS_ACTION_CONCEAL_FILE = 15,\n    NGX_HTTP_TFS_ACTION_REVEAL_FILE = 16,\n} ngx_http_tfs_action_e;\n\n\ntypedef enum\n{\n    NGX_HTTP_TFS_OPEN_MODE_DEFAULT = 0,\n    NGX_HTTP_TFS_OPEN_MODE_READ = 1,\n    NGX_HTTP_TFS_OPEN_MODE_WRITE = 2,\n    NGX_HTTP_TFS_OPEN_MODE_CREATE = 4,\n    NGX_HTTP_TFS_OPEN_MODE_NEWBLK = 8,\n    NGX_HTTP_TFS_OPEN_MODE_NOLEASE = 16,\n    NGX_HTTP_TFS_OPEN_MODE_STAT = 32,\n    NGX_HTTP_TFS_OPEN_MODE_LARGE = 64,\n    NGX_HTTP_TFS_OPEN_MODE_UNLINK = 128\n} ngx_http_tfs_open_mode_e;\n\n\ntypedef enum\n{\n    NGX_HTTP_TFS_CUSTOM_FT_FILE = 1,\n    NGX_HTTP_TFS_CUSTOM_FT_DIR,\n    NGX_HTTP_TFS_CUSTOM_FT_PWRITE_FILE\n} ngx_http_tfs_custom_file_type_e;\n\n\ntypedef enum\n{\n    NGX_HTTP_TFS_CLIENT_CMD_EXPBLK = 1,\n    NGX_HTTP_TFS_CLIENT_CMD_LOADBLK,\n    NGX_HTTP_TFS_CLIENT_CMD_COMPACT,\n    NGX_HTTP_TFS_CLIENT_CMD_IMMEDIATELY_REPL,\n    NGX_HTTP_TFS_CLIENT_CMD_REPAIR_GROUP,\n    NGX_HTTP_TFS_CLIENT_CMD_SET_PARAM,\n    NGX_HTTP_TFS_CLIENT_CMD_UNLOADBLK,\n    NGX_HTTP_TFS_CLIENT_CMD_FORCE_DATASERVER_REPORT,\n    NGX_HTTP_TFS_CLIENT_CMD_ROTATE_LOG,\n    NGX_HTTP_TFS_CLIENT_CMD_GET_BALANCE_PERCENT,\n    NGX_HTTP_TFS_CLIENT_CMD_SET_BALANCE_PERCENT\n} ngx_http_tfs_ns_ctl_type_e;\n\n\ntypedef enum\n{\n    NGX_HTTP_TFS_CLOSE_FILE_MASTER = 100,\n    NGX_HTTP_TFS_CLOSE_FILE_SLAVER\n} ngx_http_tfs_close_mode_e;\n\n\ntypedef enum\n{\n    NGX_HTTP_TFS_REMOVE_FILE_MASTER = 0,\n    NGX_HTTP_TFS_REMOVE_FILE_SLAVER\n} ngx_http_tfs_remove_mode_e;\n\n\ntypedef enum\n{\n    NGX_HTTP_TFS_FILE_DEFAULT_OPTION = 0,\n    NGX_HTTP_TFS_FILE_NO_SYNC_LOG = 1,\n    NGX_HTTP_TFS_FILE_CLOSE_FLAG_WRITE_DATA_FAILED = 2\n} ngx_http_tfs_close_option_e;\n\n\ntypedef enum\n{\n    NGX_HTTP_TFS_UNLINK_DELETE = 0,\n    NGX_HTTP_TFS_UNLINK_UNDELETE = 2,\n    NGX_HTTP_TFS_UNLINK_CONCEAL = 4,\n    NGX_HTTP_TFS_UNLINK_REVEAL = 6\n} ngx_http_tfs_unlink_type_e;\n\n\ntypedef enum\n{\n    NGX_HTTP_TFS_FILE_NORMAL = 0,\n    NGX_HTTP_TFS_FILE_DELETED = 1,\n    NGX_HTTP_TFS_FILE_INVALID = 2,\n    NGX_HTTP_TFS_FILE_CONCEAL = 4\n} ngx_http_tfs_file_status_e;\n\n\ntypedef enum\n{\n    NGX_HTTP_TFS_ACCESS_FORBIDEN = 0,\n    NGX_HTTP_TFS_ACCESS_READ_ONLY = 1,\n    NGX_HTTP_TFS_ACCESS_READ_AND_WRITE = 2,\n} ngx_http_tfs_access_type_e;\n\n\ntypedef struct {\n    uint32_t                                flag;\n    uint32_t                                len;\n    uint16_t                                type;\n    uint16_t                                version;\n    uint64_t                                id;\n    uint32_t                                crc;\n} NGX_PACKED ngx_http_tfs_header_t;\n\n\ntypedef struct {\n    int32_t                                 code;\n    uint32_t                                error_len;\n    u_char                                  error_str[];\n} NGX_PACKED ngx_http_tfs_status_msg_t;\n\n\n/* root server */\ntypedef struct {\n    ngx_http_tfs_header_t                   header;\n    uint8_t                                 reserse;\n} NGX_PACKED ngx_http_tfs_rs_request_t;\n\n\ntypedef struct {\n    uint64_t                                version;\n    uint64_t                                length;\n    u_char                                  table[];\n} NGX_PACKED ngx_http_tfs_rs_response_t;\n\n\n/* meta server */\ntypedef struct {\n    uint32_t        block_id;\n    uint64_t        file_id;\n    int64_t         offset;\n    uint32_t        size;\n} NGX_PACKED ngx_http_tfs_meta_frag_meta_info_t;\n\n\ntypedef struct {\n    uint32_t                               cluster_id;\n\n    /* highest is split flag */\n    uint32_t                               frag_count;\n    ngx_http_tfs_meta_frag_meta_info_t     frag_meta[];\n} NGX_PACKED ngx_http_tfs_meta_frag_info_t;\n\n\ntypedef struct {\n    ngx_http_tfs_header_t                   header;\n    uint64_t                                app_id;\n    uint64_t                                user_id;\n    uint32_t                                file_len;\n    u_char                                  file_path_s[];\n} NGX_PACKED ngx_http_tfs_ms_base_msg_header_t;\n\n\ntypedef struct {\n    ngx_http_tfs_header_t                   header;\n    uint64_t                                app_id;\n    uint64_t                                user_id;\n    int64_t                                 pid;\n    uint32_t                                file_len;\n    u_char                                  file_path[];\n} NGX_PACKED ngx_http_tfs_ms_ls_msg_header_t;\n\n\ntypedef struct {\n    /* ignore header */\n    uint8_t               still_have;\n    uint32_t              count;\n} NGX_PACKED ngx_http_tfs_ms_ls_response_t;\n\n\ntypedef struct {\n    /* ignore header */\n    uint8_t                           still_have;\n    ngx_http_tfs_meta_frag_info_t     frag_info;\n} NGX_PACKED ngx_http_tfs_ms_read_response_t;\n\n\n/* rc server  */\ntypedef struct {\n    ngx_http_tfs_header_t                   header;\n    uint32_t                                appkey_len;\n    u_char                                  appkey[];\n    /* uint64_t                             app_ip */\n} NGX_PACKED ngx_http_tfs_rcs_login_msg_header_t;\n\n\n/* name server */\ntypedef struct {\n    ngx_http_tfs_header_t                   header;\n    uint32_t                                mode;\n\n    uint32_t                                block_id;\n    uint32_t                                fs_count;\n    u_char                                  fs_id[];\n} NGX_PACKED ngx_http_tfs_ns_block_info_request_t;\n\n\ntypedef struct {\n    ngx_http_tfs_header_t                   header;\n    uint32_t                                mode;\n\n    uint32_t                                block_count;\n    uint32_t                                block_ids[];\n} NGX_PACKED ngx_http_tfs_ns_batch_block_info_request_t;\n\n\ntypedef struct {\n    /* ignore header */\n    uint32_t                                block_id;\n    uint32_t                                ds_count;\n    uint64_t                                ds_addrs[];\n} NGX_PACKED ngx_http_tfs_ns_block_info_response_t;\n\n\ntypedef struct {\n    /* ignore header */\n    uint32_t                                block_count;\n} NGX_PACKED ngx_http_tfs_ns_batch_block_info_response_t;\n\n\ntypedef struct {\n    ngx_http_tfs_header_t                   header;\n\n    int32_t                                 cmd;\n    int64_t                                 value1;\n    int32_t                                 value2;\n    int32_t                                 value3;\n    int64_t                                 value4;\n} NGX_PACKED ngx_http_tfs_ns_ctl_request_t;\n\n\n/* data server */\ntypedef struct {\n    ngx_http_tfs_header_t                   base_header;\n    uint32_t                                block_id;\n    uint64_t                                file_id;\n} NGX_PACKED ngx_http_tfs_ds_msg_header_t;\n\n\ntypedef struct {\n    ngx_http_tfs_ds_msg_header_t            header;\n    int32_t                                 offset;\n    uint32_t                                length;\n    uint8_t                                 flag;\n} NGX_PACKED ngx_http_tfs_ds_read_request_t;\n\n\ntypedef struct {\n    ngx_http_tfs_header_t                    header;\n    int32_t                                  data_len;\n} NGX_PACKED ngx_http_tfs_ds_read_response_t;\n\n\ntypedef struct {\n    /* ignore header */\n    uint32_t                                block_id;\n    uint64_t                                file_id;\n    uint64_t                                file_number;\n} NGX_PACKED ngx_http_tfs_ds_cf_reponse_t;\n\n\ntypedef struct {\n    ngx_http_tfs_ds_msg_header_t             header;\n    int32_t                                  offset;\n    uint32_t                                 length;\n    int32_t                                  is_server;\n    uint64_t                                 file_number;\n} NGX_PACKED ngx_http_tfs_ds_write_request_t;\n\n\ntypedef struct {\n    ngx_http_tfs_ds_msg_header_t             header;\n    uint32_t                                 server_mode;\n} NGX_PACKED ngx_http_tfs_ds_unlink_request_t;\n\n\ntypedef struct {\n    ngx_http_tfs_ds_msg_header_t             header;\n    int32_t                                  mode;\n    uint32_t                                 crc;\n    uint64_t                                 file_number;\n} NGX_PACKED ngx_http_tfs_ds_close_request_t;\n\n\ntypedef struct {\n    ngx_http_tfs_ds_msg_header_t            header;\n    uint32_t                                mode;\n} NGX_PACKED ngx_http_tfs_ds_stat_request_t;\n\n\ntypedef struct {\n    /* ignore header */\n    uint32_t                                 data_len;\n    ngx_http_tfs_raw_file_info_t             file_info;\n} NGX_PACKED ngx_http_tfs_ds_stat_response_t;\n\n\ntypedef struct {\n    uint32_t                                 file_info_len;\n    ngx_http_tfs_raw_file_info_t             file_info;\n} NGX_PACKED ngx_http_tfs_ds_readv2_response_tail_t;\n\n\ntypedef struct {\n    uint32_t                                 count; /* segment count */\n\n    /* total size of all data segments */\n    uint64_t                                 size;\n    u_char                                   reserve[64];\n} NGX_PACKED ngx_http_tfs_segment_head_t;\n\n\ntypedef struct {\n    /* ignore header */\n    int32_t                                  data_len;\n    ngx_http_tfs_segment_head_t              seg_head;\n    uint32_t                                 file_info_len;\n    ngx_http_tfs_raw_file_info_t             file_info;\n} NGX_PACKED ngx_http_tfs_ds_sp_readv2_response_t;\n\n\ntypedef struct {\n    ngx_str_t                                file_name;\n    ngx_http_tfs_custom_file_info_t          file_info;\n} ngx_http_tfs_custom_file_t;\n\n\ntypedef struct ngx_http_tfs_custom_meta_info_s ngx_http_tfs_custom_meta_info_t;\n\nstruct ngx_http_tfs_custom_meta_info_s {\n    uint32_t                                   file_count;\n    uint32_t                                   rest_file_count;\n    uint32_t                                   file_index;\n    ngx_http_tfs_custom_file_t                *files;\n    ngx_http_tfs_custom_meta_info_t           *next;\n};\n\n\ntypedef struct {\n    uint64_t                                 offset;\n    uint64_t                                 length;\n} ngx_http_tfs_file_hole_info_t;\n\n\n\n\n#endif  /* _NGX_HTTP_TFS_PROTOCOL_H_ */\n"
  },
  {
    "path": "modules/ngx_http_tfs_module/ngx_http_tfs_raw_fsname.c",
    "content": "\n/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#include <ngx_http_tfs_raw_fsname.h>\n\n#define NGX_HTTP_TFS_KEY_MASK_LEN  10     /* strlen(NGX_HTTP_TFS_KEY_MASK) */\n\nstatic const u_char* NGX_HTTP_TFS_KEY_MASK = (u_char *) \"Taobao-inc\";\n\nstatic const u_char enc_table[] = \"0JoU8EaN3xf19hIS2d.6p\"\n    \"ZRFBYurMDGw7K5m4CyXsbQjg_vTOAkcHVtzqWilnLPe\";\n\nstatic const u_char dec_table[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  \\\n    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,18,0,0,11,16,8,  \\\n    36,34,19,32,4,12,0,0,0,0,0,0,0,49,24,37,29,5,23,30,52,14,1,33,61,28,7, \\\n    48,62,42,22,15,47,3,53,57,39,25,21,0,0,0,0,45,0,6,41,51,17,63,10,44,13,\\\n    58,43,50,59,35,60,2,20,56,27,40,54,26,46,31,9,38,55,0,0,0,0,0,0,0,0,0, \\\n    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,0,0,0, \\\n    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,0,0,0, \\\n    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,0,0,0, \\\n    0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};\n\n\nstatic void\nxor_mask(const u_char* source, const int32_t len, u_char* target)\n{\n    int32_t i = 0;\n\n    for (; i < len; i++) {\n        target[i] =\n            source[i] ^ NGX_HTTP_TFS_KEY_MASK[i % NGX_HTTP_TFS_KEY_MASK_LEN];\n    }\n}\n\n\nngx_int_t\nngx_http_tfs_raw_fsname_parse(ngx_str_t *tfs_name, ngx_str_t *suffix,\n    ngx_http_tfs_raw_fsname_t* fsname)\n{\n    ngx_uint_t  suffix_len;\n\n    if (fsname != NULL && tfs_name->data != NULL && tfs_name->data[0] != '\\0') {\n        ngx_memzero(fsname, sizeof(ngx_http_tfs_raw_fsname_t));\n        fsname->file_type = ngx_http_tfs_raw_fsname_check_file_type(tfs_name);\n        if (fsname->file_type == NGX_HTTP_TFS_INVALID_FILE_TYPE) {\n            return NGX_ERROR;\n        } else {\n            /* if two suffix exist, check consistency */\n            if (suffix != NULL\n                && suffix->data != NULL\n                && tfs_name->len > NGX_HTTP_TFS_FILE_NAME_LEN)\n            {\n                suffix_len = tfs_name->len - NGX_HTTP_TFS_FILE_NAME_LEN;\n                if (suffix->len != suffix_len) {\n                    return NGX_ERROR;\n                }\n                suffix_len = suffix->len > suffix_len ? suffix_len :suffix->len;\n                if (ngx_memcmp(suffix->data,\n                               tfs_name->data + NGX_HTTP_TFS_FILE_NAME_LEN,\n                               suffix_len))\n                {\n                    return NGX_ERROR;\n                }\n            }\n\n            ngx_http_tfs_raw_fsname_decode(tfs_name->data + 2,\n                                           (u_char*) &(fsname->file));\n            if (suffix != NULL && suffix->data == NULL) {\n                suffix->data = tfs_name->data + NGX_HTTP_TFS_FILE_NAME_LEN;\n                suffix->len = tfs_name->len - NGX_HTTP_TFS_FILE_NAME_LEN;\n            }\n\n            ngx_http_tfs_raw_fsname_set_suffix(fsname, suffix);\n            if (fsname->cluster_id == 0) {\n                fsname->cluster_id = tfs_name->data[1] - '0';\n            }\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nu_char*\nngx_http_tfs_raw_fsname_get_name(ngx_http_tfs_raw_fsname_t* fsname,\n    unsigned large_flag, ngx_int_t simple_name)\n{\n    if (fsname != NULL) {\n        if (simple_name) {\n            /* zero suffix */\n            fsname->file.suffix = 0;\n        }\n\n        ngx_http_tfs_raw_fsname_encode((u_char*) &(fsname->file),\n                                       fsname->file_name + 2);\n\n        if (large_flag) {\n            fsname->file_name[0] = NGX_HTTP_TFS_LARGE_FILE_KEY_CHAR;\n\n        } else {\n            fsname->file_name[0] = NGX_HTTP_TFS_SMALL_FILE_KEY_CHAR;\n        }\n        fsname->file_name[1] = (u_char) ('0' + fsname->cluster_id);\n        fsname->file_name[NGX_HTTP_TFS_FILE_NAME_LEN] = '\\0';\n\n        return fsname->file_name;\n    }\n\n    return NULL;\n}\n\n\nngx_http_tfs_raw_file_type_e\nngx_http_tfs_raw_fsname_check_file_type(ngx_str_t *tfs_name)\n{\n    ngx_http_tfs_raw_file_type_e file_type = NGX_HTTP_TFS_INVALID_FILE_TYPE;\n\n    if (tfs_name->data != NULL\n        && tfs_name->len >= NGX_HTTP_TFS_FILE_NAME_LEN)\n    {\n        if (tfs_name->data[0] == NGX_HTTP_TFS_LARGE_FILE_KEY_CHAR) {\n            file_type = NGX_HTTP_TFS_LARGE_FILE_TYPE;\n\n        } else if (tfs_name->data[0] == NGX_HTTP_TFS_SMALL_FILE_KEY_CHAR) {\n            file_type = NGX_HTTP_TFS_SMALL_FILE_TYPE;\n        }\n    }\n\n    return file_type;\n}\n\n\nvoid\nngx_http_tfs_raw_fsname_encode(u_char *input, u_char *output)\n{\n    u_char      buffer[NGX_HTTP_TFS_FILE_NAME_EXCEPT_SUFFIX_LEN];\n    uint32_t    value;\n    ngx_uint_t  i, k;\n\n    k = 0;\n\n    if (input != NULL && output != NULL) {\n        xor_mask(input, NGX_HTTP_TFS_FILE_NAME_EXCEPT_SUFFIX_LEN, buffer);\n        for (i = 0; i < NGX_HTTP_TFS_FILE_NAME_EXCEPT_SUFFIX_LEN; i += 3) {\n            value = ((buffer[i] << 16) & 0xff0000)\n                     + ((buffer[i + 1] << 8) & 0xff00) + (buffer[i + 2] & 0xff);\n            output[k++] = enc_table[value >> 18];\n            output[k++] = enc_table[(value >> 12) & 0x3f];\n            output[k++] = enc_table[(value >> 6) & 0x3f];\n            output[k++] = enc_table[value & 0x3f];\n        }\n    }\n}\n\n\nvoid\nngx_http_tfs_raw_fsname_decode(u_char *input, u_char *output)\n{\n    u_char      buffer[NGX_HTTP_TFS_FILE_NAME_EXCEPT_SUFFIX_LEN];\n    uint32_t    value;\n    ngx_uint_t  i, k;\n\n    k = 0;\n\n    if (input != NULL && output != NULL) {\n        for (i = 0; i < NGX_HTTP_TFS_FILE_NAME_LEN - 2; i += 4) {\n            value = (dec_table[input[i] & 0xff] << 18)\n                     + (dec_table[input[i + 1] & 0xff] << 12)\n                        + (dec_table[input[i + 2] & 0xff] << 6)\n                           + dec_table[input[i + 3] & 0xff];\n            buffer[k++] = (u_char) ((value >> 16) & 0xff);\n            buffer[k++] = (u_char) ((value >> 8) & 0xff);\n            buffer[k++] = (u_char) (value & 0xff);\n        }\n        xor_mask(buffer, NGX_HTTP_TFS_FILE_NAME_EXCEPT_SUFFIX_LEN, output);\n    }\n}\n"
  },
  {
    "path": "modules/ngx_http_tfs_module/ngx_http_tfs_raw_fsname.h",
    "content": "\n/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#ifndef _NGX_HTTP_TFS_RAW_FSNAME_H_INCLUDED_\n#define _NGX_HTTP_TFS_RAW_FSNAME_H_INCLUDED_\n\n\n#include <ngx_core.h>\n#include <ngx_http.h>\n#include <ngx_tfs_common.h>\n\n\ntypedef enum {\n    NGX_HTTP_TFS_INVALID_FILE_TYPE = 0,\n    NGX_HTTP_TFS_SMALL_FILE_TYPE,\n    NGX_HTTP_TFS_LARGE_FILE_TYPE\n} ngx_http_tfs_raw_file_type_e;\n\n\ntypedef struct {\n    uint32_t                       block_id;\n    uint32_t                       seq_id;\n    uint32_t                       suffix;\n} ngx_http_tfs_raw_fsname_filebits_t;\n\n\ntypedef struct {\n    u_char                         file_name[NGX_HTTP_TFS_FILE_NAME_BUFF_LEN];\n\n    ngx_http_tfs_raw_fsname_filebits_t  file;\n\n    uint32_t                       cluster_id;\n    ngx_http_tfs_raw_file_type_e   file_type;\n} ngx_http_tfs_raw_fsname_t;\n\n\n#define ngx_http_tfs_raw_fsname_set_suffix(fsname, fs_suffix) do {      \\\n        if ((fs_suffix != NULL)                                         \\\n             && (fs_suffix->data != NULL)                               \\\n             && (fs_suffix->len != 0))                                  \\\n        {                                                               \\\n            fsname->file.suffix = ngx_http_tfs_raw_fsname_hash(         \\\n                fs_suffix->data, fs_suffix->len);                       \\\n        }                                                               \\\n    } while(0)\n\n\n#define ngx_http_tfs_raw_fsname_set_file_id(fsname, id) \\\n    fsname->file.suffix = (id >> 32);                   \\\n    fsname->file.seq_id = (id & 0xFFFFFFFF)\n\n\n#define ngx_http_tfs_raw_fsname_get_file_id(fsname) \\\n    ((((uint64_t)(fsname.file.suffix)) << 32) | fsname.file.seq_id)\n\n\n#define ngx_http_tfs_group_seq_match(block_id, group_count, group_seq)  \\\n    ((block_id % group_count) == (ngx_uint_t) group_seq)\n\n\nngx_http_tfs_raw_file_type_e ngx_http_tfs_raw_fsname_check_file_type(\n    ngx_str_t *tfs_name);\nvoid ngx_http_tfs_raw_fsname_encode(u_char * input, u_char *output);\nvoid ngx_http_tfs_raw_fsname_decode(u_char * input, u_char *output);\n\nngx_int_t ngx_http_tfs_raw_fsname_parse(ngx_str_t *tfs_name, ngx_str_t *suffix,\n    ngx_http_tfs_raw_fsname_t *fsname);\nu_char* ngx_http_tfs_raw_fsname_get_name(ngx_http_tfs_raw_fsname_t *fsname,\n    unsigned large_flag, ngx_int_t no_suffix);\n\n\n#endif  /* _NGX_HTTP_TFS_RAW_FSNAME_H_INCLUDED_ */\n"
  },
  {
    "path": "modules/ngx_http_tfs_module/ngx_http_tfs_rc_server_info.c",
    "content": "\n/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#include <ngx_http_tfs_rc_server_info.h>\n#include <ngx_http_tfs.h>\n\n\nstatic void ngx_http_tfs_rcs_rbtree_insert_value(ngx_rbtree_node_t *temp,\n    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);\n\n\nngx_http_tfs_rcs_info_t *\nngx_http_tfs_rcs_lookup(ngx_http_tfs_rc_ctx_t *ctx,\n    ngx_str_t appkey)\n{\n    ngx_int_t                 rc;\n    ngx_uint_t                hash;\n    ngx_rbtree_node_t        *node, *sentinel;\n    ngx_http_tfs_rcs_info_t  *tr;\n\n    node = ctx->sh->rbtree.root;\n    sentinel = ctx->sh->rbtree.sentinel;\n\n    hash = ngx_murmur_hash2(appkey.data, appkey.len);\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        tr = (ngx_http_tfs_rcs_info_t *) &node->color;\n        rc = ngx_memn2cmp(appkey.data, tr->appkey.data, appkey.len,\n                          tr->appkey.len);\n\n        if (rc == 0) {\n            ngx_queue_remove(&tr->queue);\n            ngx_queue_insert_head(&ctx->sh->queue, &tr->queue);\n\n            return tr;\n        }\n\n        node = (rc < 0) ? node->left : node->right;\n     }\n\n    return NULL;\n}\n\n\nvoid\nngx_http_tfs_rc_server_destroy_node(ngx_http_tfs_rc_ctx_t *rc_ctx,\n    ngx_http_tfs_rcs_info_t *rc_info_node)\n{\n    ngx_str_t                            *block_cache_info;\n    ngx_uint_t                            i, j;\n    ngx_rbtree_node_t                    *node;\n    ngx_http_tfs_group_info_t            *group_info;\n    ngx_http_tfs_logical_cluster_t       *logical_cluster;\n    ngx_http_tfs_physical_cluster_t      *physical_cluster;\n    ngx_http_tfs_cluster_group_info_t    *cluster_group_info;\n    ngx_http_tfs_tair_server_addr_info_t *dup_server_info;\n\n    if (rc_info_node == NULL) {\n        return;\n    }\n\n    if (rc_info_node->session_id.len <= 0\n        || rc_info_node->session_id.data == NULL)\n    {\n        goto last_free;\n    }\n\n    ngx_slab_free_locked(rc_ctx->shpool, rc_info_node->session_id.data);\n    ngx_str_null(&rc_info_node->session_id);\n\n    if (rc_info_node->rc_servers_count <= 0\n        || rc_info_node->rc_servers == NULL)\n    {\n        goto last_free;\n    }\n\n    ngx_slab_free_locked(rc_ctx->shpool, rc_info_node->rc_servers);\n    block_cache_info = &rc_info_node->remote_block_cache_info;\n    rc_info_node->rc_servers = NULL;\n\n    logical_cluster = rc_info_node->logical_clusters;\n    for (i = 0; i < rc_info_node->logical_cluster_count; i++) {\n        if (logical_cluster->need_duplicate) {\n            dup_server_info = &logical_cluster->dup_server_info;\n\n            for (i = 0; i < NGX_HTTP_TFS_TAIR_SERVER_ADDR_PART_COUNT; i++) {\n                if (dup_server_info->server[i].data == NULL) {\n                    goto last_free;\n                }\n                ngx_slab_free_locked(rc_ctx->shpool,\n                                     dup_server_info->server[i].data);\n                ngx_str_null(&dup_server_info->server[i]);\n            }\n        }\n\n        physical_cluster = logical_cluster->rw_clusters;\n        for (j = 0; j < logical_cluster->rw_cluster_count; j++) {\n            if (physical_cluster[j].cluster_id_text.len <= 0\n                || physical_cluster[j].cluster_id_text.data == NULL)\n            {\n                goto last_free;\n            }\n            ngx_slab_free_locked(rc_ctx->shpool,\n                                 physical_cluster[j].cluster_id_text.data);\n            ngx_str_null(&physical_cluster[j].cluster_id_text);\n            physical_cluster[j].cluster_id = 0;\n\n            if (physical_cluster[j].ns_vip_text.len <= 0\n                || physical_cluster[j].ns_vip_text.data == NULL)\n            {\n                goto last_free;\n            }\n            ngx_slab_free_locked(rc_ctx->shpool,\n                                 physical_cluster[j].ns_vip_text.data);\n            ngx_str_null(&physical_cluster[j].ns_vip_text);\n            physical_cluster++;\n        }\n        logical_cluster++;\n    }\n\n    if (block_cache_info->len <= 0 || block_cache_info->data == NULL)\n    {\n        goto last_free;\n    }\n\n    ngx_slab_free_locked(rc_ctx->shpool, block_cache_info->data);\n    ngx_str_null(&rc_info_node->remote_block_cache_info);\n\n    cluster_group_info = rc_info_node->unlink_cluster_groups;\n    for (i = 0; i < rc_info_node->unlink_cluster_group_count; i++) {\n        for (j = 0; j < cluster_group_info[i].info_count; j++) {\n            group_info = &cluster_group_info[i].group_info[j];\n            if (group_info->ns_vip_text.len <= 0\n                || group_info->ns_vip_text.data == NULL)\n            {\n                break;\n            }\n            ngx_slab_free_locked(rc_ctx->shpool, group_info->ns_vip_text.data);\n            ngx_str_null(&group_info->ns_vip_text);\n        }\n    }\n\nlast_free:\n    node = (ngx_rbtree_node_t *)\n        ((u_char *) rc_info_node - offsetof(ngx_rbtree_node_t, color));\n    ngx_slab_free_locked(rc_ctx->shpool, node);\n}\n\n\nvoid\nngx_http_tfs_rc_server_expire(ngx_http_tfs_rc_ctx_t *ctx)\n{\n    ngx_queue_t             *q, *kp_q;\n    ngx_rbtree_node_t       *node;\n    ngx_http_tfs_rcs_info_t *rc_info_node;\n\n    if (ngx_queue_empty(&ctx->sh->queue)) {\n        return;\n    }\n\n    q = ngx_queue_last(&ctx->sh->queue);\n\n    rc_info_node = ngx_queue_data(q, ngx_http_tfs_rcs_info_t, queue);\n    kp_q = &rc_info_node->kp_queue;\n\n    ngx_queue_remove(q);\n    ngx_queue_remove(kp_q);\n\n    node = (ngx_rbtree_node_t *)\n        ((u_char *) rc_info_node - offsetof(ngx_rbtree_node_t, color));\n\n    ngx_rbtree_delete(&ctx->sh->rbtree, node);\n\n    ngx_http_tfs_rc_server_destroy_node(ctx, rc_info_node);\n}\n\n\nngx_int_t\nngx_http_tfs_rc_server_init_zone(ngx_shm_zone_t *shm_zone, void *data)\n{\n    ngx_http_tfs_rc_ctx_t  *octx = data;\n\n    size_t                 len;\n    ngx_http_tfs_rc_ctx_t *ctx;\n\n    ctx = shm_zone->data;\n\n    if (octx) {\n\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_tfs_rc_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_tfs_rcs_rbtree_insert_value);\n    ngx_queue_init(&ctx->sh->queue);\n    ngx_queue_init(&ctx->sh->kp_queue);\n\n    len = sizeof(\" in tfs rc servers 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 tfs rc servers zone \\\"%V\\\"%Z\",\n                &shm_zone->shm.name);\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_tfs_rcs_rbtree_insert_value(ngx_rbtree_node_t *temp,\n    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)\n{\n    ngx_int_t                 rc;\n    ngx_rbtree_node_t       **p;\n    ngx_http_tfs_rcs_info_t  *trn, *trnt;\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            trn = (ngx_http_tfs_rcs_info_t *) &node->color;\n            trnt = (ngx_http_tfs_rcs_info_t *) &temp->color;\n\n            rc = ngx_memn2cmp(trn->appkey.data, trnt->appkey.data,\n                              trn->appkey.len, trn->appkey.len);\n            if (rc < 0) {\n                p = &temp->left;\n\n            } else if (rc > 0) {\n                p = &temp->right;\n\n            } else {\n                return;\n            }\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\nvoid\nngx_http_tfs_rcs_set_group_info_by_addr(ngx_http_tfs_rcs_info_t *rc_info,\n    ngx_int_t group_count, ngx_int_t group_seq, ngx_http_tfs_inet_t addr)\n{\n    ngx_uint_t                          i, j;\n    ngx_http_tfs_group_info_t          *group_info;\n    ngx_http_tfs_cluster_group_info_t  *cluster_group_info;\n\n    cluster_group_info = rc_info->unlink_cluster_groups;\n\n    for (i = 0; i < rc_info->unlink_cluster_group_count; i++) {\n        group_info = cluster_group_info[i].group_info;\n\n        for (j = 0; j < cluster_group_info[i].info_count; j++) {\n\n            if (ngx_memcmp(&group_info[j].ns_vip, &addr,\n                           sizeof(ngx_http_tfs_inet_t)) == 0)\n            {\n                group_info[j].group_seq = group_seq;\n                cluster_group_info[i].group_count = group_count;\n                return;\n            }\n        }\n    }\n}\n\n\nvoid\nngx_http_tfs_dump_rc_info(ngx_http_tfs_rcs_info_t *rc_info, ngx_log_t *log)\n{\n    uint32_t                            i, j, k;\n    ngx_http_tfs_group_info_t          *group_info;\n    ngx_http_tfs_logical_cluster_t     *logical_clusters;\n    ngx_http_tfs_physical_cluster_t    *physical_clusters;\n    ngx_http_tfs_cluster_group_info_t  *unlink_cluster_groups;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, \"=========dump rc info for appkey: %V =========\",\n                   &rc_info->appkey);\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, log, 0, \"appid: %uL, logical_cluster_count: %uD\",\n                   rc_info->app_id, rc_info->logical_cluster_count);\n    logical_clusters = rc_info->logical_clusters;\n    for (i = 0; i < rc_info->logical_cluster_count; i++) {\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, \"need_duplicate: %ud\",\n                       logical_clusters[i].need_duplicate);\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, \"rw_cluster_count: %uD\",\n                       logical_clusters[i].rw_cluster_count);\n        physical_clusters = logical_clusters[i].rw_clusters;\n        for (j = 0; j < logical_clusters[i].rw_cluster_count; j++) {\n            ngx_log_debug4(NGX_LOG_DEBUG_HTTP, log, 0,\n                           \"cluster_stat: %uD, access_type: %uD, cluster_id: %V, ns_vip: %V\",\n                           physical_clusters[j].cluster_stat,\n                           physical_clusters[j].access_type,\n                           &physical_clusters[j].cluster_id_text,\n                           &physical_clusters[j].ns_vip_text);\n        }\n    }\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, \"unlink_cluster_group_count: %ud\",\n                   rc_info->unlink_cluster_group_count);\n    unlink_cluster_groups = rc_info->unlink_cluster_groups;\n    for (j = 0; j < rc_info->unlink_cluster_group_count; j++) {\n        ngx_log_debug3(NGX_LOG_DEBUG_HTTP, log, 0, \"cluster_id: %ud, info_count: %uD, group_count: %D\",\n                       unlink_cluster_groups[j].cluster_id,\n                       unlink_cluster_groups[j].info_count,\n                       unlink_cluster_groups[j].group_count);\n        group_info = unlink_cluster_groups[j].group_info;\n        for (k = 0; k < unlink_cluster_groups[j].info_count; k++) {\n            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, log, 0, \"group_seq: %D, ns_vip: %V\",\n                           group_info[k].group_seq,\n                           &group_info[k].ns_vip_text);\n        }\n    }\n}\n\n\nngx_int_t\nngx_http_tfs_rcs_stat_update(ngx_http_tfs_t *t,\n    ngx_http_tfs_rcs_info_t *rc_info, ngx_http_tfs_oper_type_e oper_type)\n{\n    if (t == NULL || rc_info ==  NULL || oper_type >= NGX_HTTP_TFS_OPER_COUNT) {\n        return NGX_ERROR;\n    }\n\n    int32_t index = oper_type;\n\n    if (rc_info->stat_rcs[index].oper_app_id == 0 ) {\n        rc_info->stat_rcs[index].oper_app_id = rc_info->app_id;\n        rc_info->stat_rcs[index].oper_type = oper_type;\n    }\n\n    ++rc_info->stat_rcs[index].oper_times;\n    rc_info->stat_rcs[index].oper_size += t->stat_info.size;\n    ++rc_info->stat_rcs[index].oper_succ;\n    rc_info->stat_rcs[index].oper_rt += ngx_http_tfs_get_request_time(t);\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "modules/ngx_http_tfs_module/ngx_http_tfs_rc_server_info.h",
    "content": "\n/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#ifndef _NGX_HTTP_TFS_RC_SERVER_INFO_H_INCLUDED_\n#define _NGX_HTTP_TFS_RC_SERVER_INFO_H_INCLUDED_\n\n\n#include <ngx_core.h>\n#include <ngx_config.h>\n#include <ngx_http.h>\n#include <ngx_tfs_common.h>\n#include <ngx_http_tfs_tair_helper.h>\n\n\n#define ngx_http_tfs_get_cluster_id(cluster_id_data)                 \\\n    (cluster_id_data[1] - '0')\n\n\ntypedef struct {\n    int32_t                      group_seq;    /* get from ns */\n    ngx_str_t                    ns_vip_text;\n    ngx_http_tfs_inet_t          ns_vip;\n} ngx_http_tfs_group_info_t;\n\n\ntypedef struct {\n    uint32_t                     cluster_id;\n    int32_t                      group_count;  /* get from ns */\n    uint32_t                     info_count;\n    ngx_http_tfs_group_info_t    group_info[NGX_HTTP_TFS_MAX_CLUSTER_ID_COUNT];\n} ngx_http_tfs_cluster_group_info_t;\n\n\ntypedef struct {\n    uint32_t                     cluster_stat;\n    uint32_t                     access_type;\n    uint32_t                     cluster_id;   /* get from ns */\n    ngx_str_t                    cluster_id_text;\n    ngx_str_t                    ns_vip_text;\n    ngx_http_tfs_inet_t          ns_vip;\n} ngx_http_tfs_physical_cluster_t;\n\n\ntypedef struct {\n    uint8_t                      need_duplicate;\n    uint32_t                     dup_server_addr_hash;\n    ngx_http_tfs_tair_server_addr_info_t dup_server_info;\n\n    /* for read and write */\n    uint32_t                     rw_cluster_count;\n    ngx_http_tfs_physical_cluster_t rw_clusters[NGX_HTTP_TFS_MAX_CLUSTER_COUNT];\n} ngx_http_tfs_logical_cluster_t;\n\n\ntypedef enum {\n     NGX_HTTP_TFS_OPER_INVALID = 0,\n     NGX_HTTP_TFS_OPER_READ,\n     NGX_HTTP_TFS_OPER_WRITE,\n     NGX_HTTP_TFS_OPER_UNLINK,\n     NGX_HTTP_TFS_OPER_COUNT\n} ngx_http_tfs_oper_type_e;\n\n\ntypedef struct {\n    ngx_http_tfs_oper_type_e             oper_type;\n    uint32_t                             oper_app_id;\n    uint64_t                             oper_times;\n    uint64_t                             oper_size;\n    uint64_t                             oper_rt;\n    uint64_t                             oper_succ;\n} ngx_http_tfs_stat_rcs_t;\n\n\ntypedef struct {\n    u_char                       color;\n    u_char                       dummy;\n    ngx_queue_t                  queue;\n\n    /* for keep alive, fixed sequence */\n    ngx_queue_t                  kp_queue;\n\n    ngx_str_t                    appkey;\n    uint64_t                     app_id;\n    ngx_str_t                    session_id;\n    uint32_t                     rc_servers_count;\n    uint64_t                    *rc_servers;\n\n    /* logical cluster */\n    uint32_t                     logical_cluster_count;\n    ngx_http_tfs_logical_cluster_t logical_clusters[NGX_HTTP_TFS_MAX_CLUSTER_COUNT];\n\n    uint8_t                      need_duplicate;\n\n    uint32_t                     report_interval;\n    uint64_t                     modify_time;\n    uint64_t                     meta_root_server;\n    ngx_str_t                    remote_block_cache_info;\n\n    ngx_http_tfs_stat_rcs_t      stat_rcs[NGX_HTTP_TFS_OPER_COUNT];\n\n    /* for unlink & update */\n    uint8_t                      unlink_cluster_group_count; /* this ~= unlink_cluster_count get from rcs */\n    ngx_http_tfs_cluster_group_info_t  unlink_cluster_groups[NGX_HTTP_TFS_MAX_CLUSTER_COUNT];\n\n    uint32_t                     use_remote_block_cache;\n} ngx_http_tfs_rcs_info_t;\n\n\ntypedef struct {\n    ngx_rbtree_t                 rbtree;\n    ngx_rbtree_node_t            sentinel;\n    ngx_queue_t                  queue;\n\n    /* for keep alive, fixed sequence */\n    ngx_queue_t                  kp_queue;\n} ngx_http_tfs_rc_shctx_t;\n\n\ntypedef struct {\n    ngx_http_tfs_rc_shctx_t     *sh;\n    ngx_slab_pool_t             *shpool;\n} ngx_http_tfs_rc_ctx_t;\n\n\nngx_int_t ngx_http_tfs_rc_server_init_zone(ngx_shm_zone_t *shm_zone,\n    void *data);\nvoid ngx_http_tfs_rc_server_expire(ngx_http_tfs_rc_ctx_t *ctx);\nngx_http_tfs_rcs_info_t *ngx_http_tfs_rcs_lookup(ngx_http_tfs_rc_ctx_t *ctx,\n    ngx_str_t appkey);\nvoid ngx_http_tfs_rc_server_destroy_node(ngx_http_tfs_rc_ctx_t *ctx,\n    ngx_http_tfs_rcs_info_t *rc_info_node);\nvoid ngx_http_tfs_rcs_set_group_info_by_addr(ngx_http_tfs_rcs_info_t *rc_info,\n    ngx_int_t group_count, ngx_int_t seq_id, ngx_http_tfs_inet_t addr);\nvoid ngx_http_tfs_dump_rc_info(ngx_http_tfs_rcs_info_t *rc_info, ngx_log_t *log);\nngx_int_t ngx_http_tfs_rcs_stat_update(ngx_http_tfs_t *t,\n    ngx_http_tfs_rcs_info_t *rc_info, ngx_http_tfs_oper_type_e oper_type);\n\n#endif  /* _NGX_HTTP_TFS_RC_SERVER_INFO_H_INCLUDED_ */\n\n"
  },
  {
    "path": "modules/ngx_http_tfs_module/ngx_http_tfs_rc_server_message.c",
    "content": "\n/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#include <ngx_http_tfs_serialization.h>\n#include <ngx_http_tfs_rc_server_message.h>\n\n#define ngx_http_tfs_expire_and_alloc(data, len) do {                   \\\n        ngx_http_tfs_rc_server_expire(rc_ctx);          \\\n        data = ngx_slab_alloc_locked(rc_ctx->shpool, len);    \\\n        if (data == NULL) {                             \\\n            return NGX_ERROR;                           \\\n        }                                               \\\n    } while(0)\n\n\nstatic ngx_chain_t *ngx_http_tfs_create_login_message(ngx_http_tfs_t *t);\nstatic ngx_chain_t * ngx_http_tfs_create_keepalive_message(ngx_http_tfs_t *t);\n\nstatic ngx_int_t ngx_http_tfs_parse_login_message(ngx_http_tfs_t *t);\nstatic ngx_int_t ngx_http_tfs_parse_keepalive_message(ngx_http_tfs_t *t);\n\nstatic ngx_int_t\nngx_http_tfs_parse_rc_info(ngx_http_tfs_rcs_info_t *rc_info_node,\n    ngx_http_tfs_rc_ctx_t *rc_ctx, u_char *data);\n\nstatic ngx_int_t\nngx_http_tfs_create_info_node(ngx_http_tfs_t *t, ngx_http_tfs_rc_ctx_t *rc_ctx,\n    u_char *data, ngx_str_t appkey);\n\nstatic ngx_int_t\nngx_http_tfs_update_info_node(ngx_http_tfs_t *t, ngx_http_tfs_rc_ctx_t *rc_ctx,\n    ngx_http_tfs_rcs_info_t *rc_info_node, u_char *base_info);\n\nstatic ngx_int_t ngx_http_tfs_parse_session_id(ngx_str_t *session_id,\n    uint64_t *app_id);\nstatic void ngx_http_tfs_update_rc_servers(ngx_http_tfs_t *t,\n    const ngx_http_tfs_rcs_info_t *rc_info_node);\n\n\nngx_chain_t *\nngx_http_tfs_rc_server_create_message(ngx_http_tfs_t *t)\n{\n    uint16_t  msg_type;\n\n    msg_type = t->r_ctx.action.code;\n\n    switch(msg_type) {\n    case NGX_HTTP_TFS_ACTION_KEEPALIVE:\n        return ngx_http_tfs_create_keepalive_message(t);\n    default:\n        return ngx_http_tfs_create_login_message(t);\n    }\n}\n\n\nngx_int_t\nngx_http_tfs_rc_server_parse_message(ngx_http_tfs_t *t)\n{\n    if (t->r_ctx.action.code == NGX_HTTP_TFS_ACTION_KEEPALIVE) {\n        return ngx_http_tfs_parse_keepalive_message(t);\n    }\n\n    return ngx_http_tfs_parse_login_message(t);\n}\n\n\nngx_chain_t *\nngx_http_tfs_create_login_message(ngx_http_tfs_t *t)\n{\n    ngx_buf_t                            *b;\n    ngx_chain_t                          *cl;\n    struct sockaddr_in                   *addr;\n    ngx_http_tfs_rcs_login_msg_header_t  *req;\n\n    b = ngx_create_temp_buf(t->pool,\n                            sizeof(ngx_http_tfs_rcs_login_msg_header_t)\n                             + sizeof(uint64_t) + t->r_ctx.appkey.len + 1);\n    if (b == NULL) {\n        return NULL;\n    }\n\n    req = (ngx_http_tfs_rcs_login_msg_header_t *) b->pos;\n    req->header.flag = NGX_HTTP_TFS_PACKET_FLAG;\n    req->header.len = sizeof(uint64_t) + t->r_ctx.appkey.len\n                       + sizeof(uint32_t) + 1;\n    req->header.type = NGX_HTTP_TFS_REQ_RC_LOGIN_MESSAGE;\n    req->header.version = NGX_HTTP_TFS_PACKET_VERSION;\n    req->header.id = ngx_http_tfs_generate_packet_id();\n\n    req->appkey_len = t->r_ctx.appkey.len + 1;\n\n    b->last += sizeof(ngx_http_tfs_rcs_login_msg_header_t);\n\n    /* app key */\n    ngx_memcpy(b->last, t->r_ctx.appkey.data, t->r_ctx.appkey.len);\n    b->last += t->r_ctx.appkey.len;\n    *(b->last) = '\\0';\n    b->last += 1;\n\n    /* app ip */\n    addr = &(t->loc_conf->upstream->local_addr);\n    ngx_memcpy(b->last, &(addr->sin_addr.s_addr), sizeof(uint64_t));\n    b->last += sizeof(uint64_t);\n\n    req->header.crc = ngx_http_tfs_crc(NGX_HTTP_TFS_PACKET_FLAG,\n                                       (const char *) (&req->header + 1),\n                                       req->header.len);\n\n    cl = ngx_alloc_chain_link(t->pool);\n    if (cl == NULL) {\n        return NULL;\n    }\n\n    cl->buf = b;\n    cl->next = NULL;\n\n    return cl;\n}\n\n\nngx_chain_t *\nngx_http_tfs_create_keepalive_message(ngx_http_tfs_t *t)\n{\n    u_char                   *p, *tmp_ptr;\n    ssize_t                  size, base_size;\n    uint32_t                 rc_stat_size;\n    ngx_int_t                rc, count;\n    ngx_buf_t                *b;\n    ngx_queue_t              *q, *queue;\n    ngx_chain_t              *cl, **ll;\n    ngx_http_tfs_rc_ctx_t    *rc_ctx;\n    ngx_http_tfs_header_t    *header;\n    ngx_http_tfs_rcs_info_t  *rc_info;\n\n    count = 0;\n    rc_ctx = t->loc_conf->upstream->rc_ctx;\n    ll = NULL;\n    cl = NULL;\n\n    base_size = sizeof(ngx_http_tfs_header_t)\n        /* session id and client version len */\n        + sizeof(uint32_t) * 2\n        /* client version */\n        + sizeof(NGX_HTTP_TFS_CLIENT_VERSION)\n        /* cache_size cache_time modify_time */\n        + sizeof(uint64_t) * 3\n        /* is_logout */\n        + sizeof(uint8_t)\n        /* stat info */\n        + sizeof(uint32_t)\n        + sizeof(uint64_t)\n        /* last_report_time */\n        + sizeof(uint64_t);\n\n    queue = &rc_ctx->sh->kp_queue;\n    if (ngx_queue_empty(queue)) {\n        goto keepalive_create_error;\n    }\n\n    q = t->curr_ka_queue;\n    if (q == NULL) {\n        q = ngx_queue_head(queue);\n        t->curr_ka_queue = q;\n    }\n\n    rc_info = ngx_queue_data(q, ngx_http_tfs_rcs_info_t, kp_queue);\n\n    ngx_log_error(NGX_LOG_INFO, t->log, 0,\n                  \"will do keepalive for appkey: %V\", &rc_info->appkey);\n\n    /* rc_stat_size = oper_count * (key_size + value_size)\n     * key_size = sizeof(oper_type)\n     * vlaue_size = sizeof(ngx_http_tfs_stat_rcs_t) - sizeof(oper_app_id) */\n    rc_stat_size = NGX_HTTP_TFS_OPER_COUNT * (sizeof(uint32_t) + sizeof(ngx_http_tfs_stat_rcs_t) - sizeof(uint32_t));\n\n    size = base_size + rc_info->session_id.len + 1 + rc_stat_size;\n    /*size = base_size + rc_info->session_id.len + 1;*/\n\n    b = ngx_create_temp_buf(t->pool, size);\n    if (b == NULL) {\n        goto keepalive_create_error;\n    }\n\n    header = (ngx_http_tfs_header_t *) b->pos;\n    header->flag = NGX_HTTP_TFS_PACKET_FLAG;\n    header->len = size - sizeof(ngx_http_tfs_header_t);\n    header->type = NGX_HTTP_TFS_REQ_RC_KEEPALIVE_MESSAGE;\n    header->version = NGX_HTTP_TFS_PACKET_VERSION;\n    header->id = ngx_http_tfs_generate_packet_id();\n\n    p = (u_char *)(header + 1);\n\n    /* include '\\0' */\n    *((uint32_t *) p) = rc_info->session_id.len + 1;\n    p += sizeof(uint32_t);\n\n    p = ngx_cpymem(p, rc_info->session_id.data, rc_info->session_id.len);\n    *p = '\\0';\n    p += sizeof(uint8_t);\n\n    *((uint32_t *) p) = sizeof(NGX_HTTP_TFS_CLIENT_VERSION);\n    p += sizeof(uint32_t);\n\n    p = ngx_cpymem(p, NGX_HTTP_TFS_CLIENT_VERSION,\n                   sizeof(NGX_HTTP_TFS_CLIENT_VERSION));\n\n    ngx_memzero(p, sizeof(uint64_t) * 3 + sizeof(uint32_t) + sizeof(uint8_t));\n\n    /* cache_size cache_time */\n    p += sizeof(uint64_t) * 2;\n\n    *((uint64_t *) p) = rc_info->modify_time;\n    /* modify_time and is_logout */\n    p += sizeof(uint64_t) + sizeof(uint8_t);\n\n    /* stat size */\n    tmp_ptr = p;\n    /**((uint32_t *)p) = NGX_HTTP_TFS_OPER_COUNT;*/\n    p += sizeof(uint32_t);\n\n    /* set rcs stat */\n    rc = ngx_http_tfs_serialize_rcs_stat(&p, rc_info, &count);\n    if (rc != NGX_OK) {\n        goto keepalive_create_error;\n    }\n    *((uint32_t *)tmp_ptr) = count;\n\n    /* cache hit_ratio and last_report_time */\n    /*p += sizeof(uint64_t) * 2;*/\n    p += sizeof(uint64_t);\n    (*(uint64_t *)p) = time(NULL);\n    p += sizeof(uint64_t);\n\n    /* modify_time is_logout stat_info */\n    /*p += sizeof(uint64_t) * 3 + sizeof(uint32_t) + sizeof(uint8_t);*/\n\n    header->crc = ngx_http_tfs_crc(NGX_HTTP_TFS_PACKET_FLAG,\n        (const char *) (header + 1), header->len);\n\n    b->last += size;\n\n    if (ll == NULL) {\n        cl = ngx_alloc_chain_link(t->pool);\n        if (cl == NULL) {\n            goto keepalive_create_error;\n        }\n\n        cl->next = NULL;\n        cl->buf = b;\n\n    } else {\n        *ll = ngx_alloc_chain_link(t->pool);\n        if (*ll == NULL) {\n            goto keepalive_create_error;\n        }\n\n        (*ll)->next = NULL;\n        (*ll)->buf = b;\n    }\n\n    return cl;\n\nkeepalive_create_error:\n    return NULL;\n}\n\n\nngx_int_t\nngx_http_tfs_parse_login_message(ngx_http_tfs_t *t)\n{\n    uint16_t                         type;\n    ngx_str_t                        err_msg;\n    ngx_int_t                        rc;\n    ngx_http_tfs_header_t           *header;\n    ngx_http_tfs_rc_ctx_t           *rc_ctx;\n    ngx_http_tfs_rcs_info_t         *rc_info;\n    ngx_http_tfs_peer_connection_t  *tp;\n\n    header = (ngx_http_tfs_header_t *) t->header;\n    tp = t->tfs_peer;\n    type = header->type;\n    rc_ctx = t->loc_conf->upstream->rc_ctx;\n\n    switch (type) {\n    case NGX_HTTP_TFS_STATUS_MESSAGE:\n        ngx_str_set(&err_msg, \"login rc\");\n        return ngx_http_tfs_status_message(&tp->body_buffer, &err_msg, t->log);\n    }\n\n    ngx_shmtx_lock(&rc_ctx->shpool->mutex);\n    rc_info = ngx_http_tfs_rcs_lookup(rc_ctx, t->r_ctx.appkey);\n\n    rc = NGX_OK;\n\n    if (rc_info == NULL) {\n        rc = ngx_http_tfs_create_info_node(t, rc_ctx, tp->body_buffer.pos,\n                                           t->r_ctx.appkey);\n\n    } else {\n        t->rc_info_node = rc_info;\n    }\n    ngx_shmtx_unlock(&rc_ctx->shpool->mutex);\n\n#if (NGX_DEBUG)\n    ngx_http_tfs_dump_rc_info(t->rc_info_node, t->log);\n#endif\n\n    if (rc == NGX_OK) {\n        rc = ngx_http_tfs_parse_session_id(&t->rc_info_node->session_id,\n                                           &t->rc_info_node->app_id);\n        if (rc == NGX_ERROR) {\n            ngx_log_error(NGX_LOG_ERR, t->log, 0,\n                          \"invalid session id: %V\",\n                          &t->rc_info_node->session_id);\n        }\n    }\n    return rc;\n}\n\n\nngx_int_t\nngx_http_tfs_parse_keepalive_message(ngx_http_tfs_t *t)\n{\n    u_char                          *p, update;\n    uint16_t                         type;\n    ngx_str_t                        err_msg;\n    ngx_int_t                        i, rc;\n    ngx_queue_t                     *q, *queue;\n    ngx_rbtree_node_t               *node;\n    ngx_http_tfs_header_t           *header;\n    ngx_http_tfs_rc_ctx_t           *rc_ctx;\n    ngx_http_tfs_stat_rcs_t         *stat_rcs;\n    ngx_http_tfs_rcs_info_t         *rc_info;\n    ngx_http_tfs_peer_connection_t  *tp;\n\n    header = (ngx_http_tfs_header_t *) t->header;\n    tp = t->tfs_peer;\n    type = header->type;\n\n    switch (type) {\n    case NGX_HTTP_TFS_STATUS_MESSAGE:\n        ngx_str_set(&err_msg, \"keepalive rc\");\n        return ngx_http_tfs_status_message(&tp->body_buffer, &err_msg, t->log);\n    }\n\n    p = tp->body_buffer.pos;\n    update = *p;\n    p++;\n\n    if (!update) {\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, t->log, 0,\n                      \"rc keepalive, update flag: %d\", update);\n    } else {\n        ngx_log_error(NGX_LOG_WARN, t->log, 0,\n                      \"rc keepalive, update flag: %d\", update);\n    }\n\n    rc_ctx = t->loc_conf->upstream->rc_ctx;\n\n    queue = &rc_ctx->sh->kp_queue;\n    if (ngx_queue_empty(queue)) {\n        return NGX_ERROR;\n    }\n\n    q = t->curr_ka_queue;\n    if (q == NULL) {\n        return NGX_ERROR;\n    }\n    t->curr_ka_queue = ngx_queue_next(q);\n\n    ngx_shmtx_lock(&rc_ctx->shpool->mutex);\n    rc_info = ngx_queue_data(q, ngx_http_tfs_rcs_info_t, kp_queue);\n\n    stat_rcs = rc_info->stat_rcs;\n    for (i = 0;i < NGX_HTTP_TFS_OPER_COUNT; i++) {\n        ngx_memzero(&stat_rcs[i], sizeof(ngx_http_tfs_stat_rcs_t));\n    }\n\n    if (update == NGX_HTTP_TFS_NO) {\n        ngx_shmtx_unlock(&rc_ctx->shpool->mutex);\n        return NGX_OK;\n    }\n\n    /* FIXME: do not consider rc_info_node being expired, it hardly occurs\n     * e.g. a single rc_info_node occupys nearly 2KB space,\n     * 10MB for tfs_rcs_zone can hold at least 5000 rc_infos.\n     */\n\n    /* update info node */\n    /* FIXME: sth terrible may happen here\n     * if someone has get the rc_info before lock */\n    rc = ngx_http_tfs_update_info_node(t, rc_ctx, rc_info, p);\n    /* rc_info has been destroyed, remove from queue and rbtree */\n    if (rc == NGX_ERROR) {\n        ngx_queue_remove(&rc_info->queue);\n        ngx_queue_remove(&rc_info->kp_queue);\n\n        node = (ngx_rbtree_node_t *)\n            ((u_char *) rc_info - offsetof(ngx_rbtree_node_t, color));\n        ngx_rbtree_delete(&rc_ctx->sh->rbtree, node);\n\n        ngx_http_tfs_rc_server_destroy_node(rc_ctx, rc_info);\n    }\n    ngx_shmtx_unlock(&rc_ctx->shpool->mutex);\n\n#if (NGX_DEBUG)\n    ngx_http_tfs_dump_rc_info(rc_info, t->log);\n#endif\n\n    return rc;\n}\n\n\nstatic ngx_int_t\nngx_http_tfs_parse_rc_info(ngx_http_tfs_rcs_info_t *rc_info_node,\n    ngx_http_tfs_rc_ctx_t *rc_ctx,  u_char *data)\n{\n    u_char                                *p;\n    uint32_t                               cluster_id, cluster_id_len;\n    uint32_t                               len, unlink_cluster_count;\n    ngx_int_t                              dup_info_size, rc;\n    ngx_uint_t                             i, j;\n    ngx_http_tfs_group_info_t             *group_info;\n    ngx_http_tfs_logical_cluster_t        *logical_cluster;\n    ngx_http_tfs_physical_cluster_t       *physical_cluster;\n    ngx_http_tfs_cluster_group_info_t     *cluster_group_info;\n    ngx_http_tfs_tair_server_addr_info_t  *dup_server_info;\n\n    p = data;\n\n    /* rc servers count */\n    rc_info_node->rc_servers_count = *((uint32_t *) p);\n    p += sizeof(uint32_t);\n\n    if (rc_info_node->rc_servers_count > 0) {\n        rc_info_node->rc_servers =\n            ngx_slab_alloc_locked(rc_ctx->shpool,\n                                  rc_info_node->rc_servers_count\n                                   * sizeof(uint64_t));\n        if (rc_info_node->rc_servers == NULL) {\n            ngx_http_tfs_expire_and_alloc(rc_info_node->rc_servers,\n                                          rc_info_node->rc_servers_count\n                                           * sizeof(uint64_t));\n        }\n\n        ngx_memcpy(rc_info_node->rc_servers, p,\n                   rc_info_node->rc_servers_count * sizeof(uint64_t));\n        p += sizeof(uint64_t) * rc_info_node->rc_servers_count;\n    }\n\n    /* logical cluster count */\n    rc_info_node->logical_cluster_count = *((uint32_t *) p);\n    p += sizeof(uint32_t);\n\n    logical_cluster = rc_info_node->logical_clusters;\n    for (i = 0; i < rc_info_node->logical_cluster_count; i++) {\n        logical_cluster->need_duplicate = *p;\n        p += sizeof(uint8_t);\n\n        if (logical_cluster->need_duplicate) {\n            len = *((uint32_t *) p);\n            p += sizeof(uint32_t);\n\n            if (len > 0) {\n                dup_info_size = len - 1;\n                dup_server_info = &logical_cluster->dup_server_info;\n\n                rc = ngx_http_tfs_parse_tair_server_addr_info(dup_server_info,\n                                                              p,\n                                                              dup_info_size,\n                                                              rc_ctx->shpool,\n                                                              1);\n                if (rc == NGX_ERROR) {\n                    return NGX_ERROR;\n                }\n\n                logical_cluster->dup_server_addr_hash =\n                    ngx_murmur_hash2(p, dup_info_size);\n                p += dup_info_size + 1;\n\n                rc_info_node->need_duplicate = 1;\n            }\n        }\n\n        logical_cluster->rw_cluster_count = *((uint32_t *) p);\n        p += sizeof(uint32_t);\n\n        physical_cluster = logical_cluster->rw_clusters;\n        for (j = 0; j < logical_cluster->rw_cluster_count; j++) {\n            /* cluster stat */\n            physical_cluster->cluster_stat = *((uint32_t *) p);\n            p += sizeof(uint32_t);\n\n            /* access type */\n            physical_cluster->access_type = *((uint32_t *) p);\n            p += sizeof(uint32_t);\n\n            /* cluster id */\n            len = *((uint32_t *) p);\n            if (len <= 0) {\n                physical_cluster->cluster_id_text.len = 0;\n                return NGX_ERROR;\n            }\n\n            physical_cluster->cluster_id_text.len = len - 1;\n            p += sizeof(uint32_t);\n\n            physical_cluster->cluster_id_text.data =\n                ngx_slab_alloc_locked(rc_ctx->shpool,\n                                      physical_cluster->cluster_id_text.len);\n            if (physical_cluster->cluster_id_text.data == NULL) {\n                ngx_http_tfs_expire_and_alloc(\n                                         physical_cluster->cluster_id_text.data,\n                                         physical_cluster->cluster_id_text.len);\n            }\n            ngx_memcpy(physical_cluster->cluster_id_text.data, p,\n                       physical_cluster->cluster_id_text.len);\n            /* this cluster id need get from ns */\n            physical_cluster->cluster_id = 0;\n            p += physical_cluster->cluster_id_text.len + 1;\n\n            /* name server vip */\n            len = *((uint32_t *) p);\n            if (len <= 0) {\n                physical_cluster->ns_vip_text.len = 0;\n                return NGX_ERROR;\n            }\n\n            physical_cluster->ns_vip_text.len = len - 1;\n            p += sizeof(uint32_t);\n\n            physical_cluster->ns_vip_text.data =\n                ngx_slab_alloc_locked(rc_ctx->shpool,\n                                      physical_cluster->ns_vip_text.len);\n            if (physical_cluster->ns_vip_text.data == NULL) {\n                ngx_http_tfs_expire_and_alloc(physical_cluster->ns_vip_text.data,\n                                             physical_cluster->ns_vip_text.len);\n            }\n            ngx_memcpy(physical_cluster->ns_vip_text.data, p,\n                       physical_cluster->ns_vip_text.len);\n\n            p += physical_cluster->ns_vip_text.len + 1;\n\n            ngx_http_tfs_parse_inet(&physical_cluster->ns_vip_text,\n                                    &physical_cluster->ns_vip);\n\n            physical_cluster++;\n        }\n\n        logical_cluster++;\n    }\n\n    /* report interval */\n    rc_info_node->report_interval = *((uint32_t *) p);\n    p += sizeof(uint32_t);\n\n    /* modify time */\n    rc_info_node->modify_time = *((uint64_t *) p);\n    p += sizeof(uint64_t);\n\n    /* root server */\n    rc_info_node->meta_root_server = *((uint64_t *) p);\n    p += sizeof(uint64_t);\n\n    /* remote block cache */\n    len = *((uint32_t *) p);\n    p += sizeof(uint32_t);\n    rc_info_node->remote_block_cache_info.len = 0;\n\n    if (len > 0) {\n        rc_info_node->remote_block_cache_info.len = len - 1;\n\n        rc_info_node->remote_block_cache_info.data =\n            ngx_slab_alloc_locked(rc_ctx->shpool,\n                                  rc_info_node->remote_block_cache_info.len);\n        if (rc_info_node->remote_block_cache_info.data == NULL) {\n            ngx_http_tfs_expire_and_alloc(\n                                     rc_info_node->remote_block_cache_info.data,\n                                     rc_info_node->remote_block_cache_info.len);\n        }\n\n        ngx_memcpy(rc_info_node->remote_block_cache_info.data, p,\n                   len - 1);\n        p += len;\n    }\n\n    /* unlink & update cluster */\n    /* this count is physical cluster count */\n    unlink_cluster_count = *((uint32_t *) p);\n    p += sizeof(uint32_t);\n\n    rc_info_node->unlink_cluster_group_count = 0;\n\n    for (i = 0; i < unlink_cluster_count; i++) {\n        /* skip cluster_stat */\n        p += sizeof(uint32_t);\n        /* skip access type */\n        p += sizeof(uint32_t);\n\n        cluster_id_len = *((uint32_t *) p);\n        p += sizeof(uint32_t);\n\n        cluster_id = ngx_http_tfs_get_cluster_id(p);\n        p += cluster_id_len;\n\n        for (j = 0; j < rc_info_node->unlink_cluster_group_count; j++) {\n            /* find exist cluster_group_info */\n            if (rc_info_node->unlink_cluster_groups[j].cluster_id == cluster_id) {\n                cluster_group_info = &rc_info_node->unlink_cluster_groups[j];\n                break;\n            }\n        }\n\n        /* new cluster_group_info */\n        if (j >= rc_info_node->unlink_cluster_group_count) {\n            cluster_group_info = &rc_info_node->unlink_cluster_groups[rc_info_node->unlink_cluster_group_count++];\n            cluster_group_info->info_count = 0;\n            cluster_group_info->group_count = 0;\n            cluster_group_info->cluster_id = cluster_id;\n        }\n\n        group_info = &cluster_group_info->group_info[cluster_group_info->info_count++];\n\n        /* name server vip */\n        len = *((uint32_t *) p);\n        if (len <= 0) {\n            group_info->ns_vip_text.len = 0;\n            return NGX_ERROR;\n        }\n\n        group_info->ns_vip_text.len = len - 1;\n        p += sizeof(uint32_t);\n\n        group_info->ns_vip_text.data =\n            ngx_slab_alloc_locked(rc_ctx->shpool, group_info->ns_vip_text.len);\n        if (group_info->ns_vip_text.data == NULL) {\n            ngx_http_tfs_expire_and_alloc(group_info->ns_vip_text.data,\n                                          group_info->ns_vip_text.len);\n        }\n\n        memcpy(group_info->ns_vip_text.data, p, group_info->ns_vip_text.len);\n\n        group_info->group_seq = -1;\n        p += len;\n\n        ngx_http_tfs_parse_inet(&group_info->ns_vip_text, &group_info->ns_vip);\n    }\n\n    /* use remote cache flag */\n    rc_info_node->use_remote_block_cache = *((uint32_t *) p);\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_tfs_update_rc_servers(ngx_http_tfs_t *t, const ngx_http_tfs_rcs_info_t *rc_info_node)\n{\n    ngx_http_tfs_upstream_t       *upstream;\n\n    upstream = t->loc_conf->upstream;\n    if (rc_info_node->rc_servers_count > NGX_HTTP_TFS_MAX_RCSERVER_COUNT) {\n        upstream->rc_servers_count = NGX_HTTP_TFS_MAX_RCSERVER_COUNT;\n\n    } else {\n        upstream->rc_servers_count = rc_info_node->rc_servers_count;\n    }\n\n    ngx_memcpy(upstream->rc_servers, rc_info_node->rc_servers, upstream->rc_servers_count * sizeof(uint64_t));\n    upstream->rcserver_index = 0;\n}\n\n\nstatic ngx_int_t\nngx_http_tfs_update_info_node(ngx_http_tfs_t *t, ngx_http_tfs_rc_ctx_t *rc_ctx,\n    ngx_http_tfs_rcs_info_t *rc_info_node, u_char *base_info)\n{\n    u_char                                *p;\n    ngx_int_t                              rc;\n    ngx_uint_t                             i, j;\n    ngx_http_tfs_group_info_t             *group_info;\n    ngx_http_tfs_logical_cluster_t        *logical_cluster;\n    ngx_http_tfs_physical_cluster_t       *physical_cluster;\n    ngx_http_tfs_cluster_group_info_t     *cluster_group_info;\n    ngx_http_tfs_tair_server_addr_info_t  *dup_server_info;\n\n    p = base_info;\n\n    /* free old rc servers */\n    if (rc_info_node->rc_servers != NULL) {\n        ngx_slab_free_locked(rc_ctx->shpool, rc_info_node->rc_servers);\n    }\n    rc_info_node->rc_servers_count = 0;\n\n    /* free old cluster data */\n    logical_cluster = rc_info_node->logical_clusters;\n    for (i = 0; i < rc_info_node->logical_cluster_count; i++) {\n        /* free old duplicate server info */\n        if (logical_cluster->need_duplicate) {\n            dup_server_info = &logical_cluster->dup_server_info;\n\n            for (j = 0; j < NGX_HTTP_TFS_TAIR_SERVER_ADDR_PART_COUNT; j++) {\n                if (dup_server_info->server[j].data == NULL) {\n                    break;\n                }\n                ngx_slab_free_locked(rc_ctx->shpool,\n                                     dup_server_info->server[j].data);\n                ngx_str_null(&dup_server_info->server[j]);\n            }\n            logical_cluster->dup_server_addr_hash = -1;\n            logical_cluster->need_duplicate = 0;\n        }\n\n        physical_cluster = logical_cluster->rw_clusters;\n        for (j = 0; j < logical_cluster->rw_cluster_count; i++) {\n            if (physical_cluster->cluster_id_text.len <= 0\n                || physical_cluster->cluster_id_text.data == NULL)\n            {\n                break;\n            }\n            ngx_slab_free_locked(rc_ctx->shpool,\n                                 physical_cluster->cluster_id_text.data);\n            ngx_str_null(&physical_cluster->cluster_id_text);\n            physical_cluster->cluster_id = 0;\n\n            if (physical_cluster->ns_vip_text.len <= 0\n                || physical_cluster->ns_vip_text.data == NULL)\n            {\n                break;\n            }\n            ngx_slab_free_locked(rc_ctx->shpool,\n                                 physical_cluster->ns_vip_text.data);\n            ngx_str_null(&physical_cluster->ns_vip_text);\n\n            physical_cluster++;\n        }\n        logical_cluster->rw_cluster_count = 0;\n\n        logical_cluster++;\n    }\n    rc_info_node->logical_cluster_count = 0;\n\n    /* reset need duplicate flag */\n    rc_info_node->need_duplicate = 0;\n\n    /* free old remote block cache info */\n    if (rc_info_node->remote_block_cache_info.len > 0\n        && rc_info_node->remote_block_cache_info.data != NULL)\n    {\n        ngx_slab_free_locked(rc_ctx->shpool,\n                             rc_info_node->remote_block_cache_info.data);\n        ngx_str_null(&rc_info_node->remote_block_cache_info);\n    }\n    rc_info_node->remote_block_cache_info.len = 0;\n\n    /* free old unlink cluster */\n    cluster_group_info = rc_info_node->unlink_cluster_groups;\n    for (i = 0; i < rc_info_node->unlink_cluster_group_count; i++) {\n        for (j = 0; j < cluster_group_info[i].info_count; j++) {\n            group_info = &cluster_group_info[i].group_info[j];\n            if (group_info->ns_vip_text.len <= 0\n                || group_info->ns_vip_text.data == NULL)\n            {\n                break;\n            }\n            ngx_slab_free_locked(rc_ctx->shpool, group_info->ns_vip_text.data);\n            ngx_str_null(&group_info->ns_vip_text);\n        }\n    }\n    rc_info_node->unlink_cluster_group_count = 0;\n\n    /* parse rc info */\n    rc = ngx_http_tfs_parse_rc_info(rc_info_node, rc_ctx, p);\n    if (rc == NGX_ERROR) {\n        return NGX_ERROR;\n    }\n\n    t->rc_info_node = rc_info_node;\n    ngx_http_tfs_update_rc_servers(t, rc_info_node);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_tfs_create_info_node(ngx_http_tfs_t *t,\n    ngx_http_tfs_rc_ctx_t *rc_ctx,\n    u_char *data, ngx_str_t appkey)\n{\n    u_char                   *p;\n    size_t                    n;\n    uint32_t                  len;\n    ngx_int_t                 rc;\n    ngx_rbtree_node_t        *node;\n    ngx_http_tfs_rcs_info_t  *rc_info_node;\n\n    rc_info_node = NULL;\n\n    n = offsetof(ngx_rbtree_node_t, color)\n        + sizeof(ngx_http_tfs_rcs_info_t);\n\n    node = ngx_slab_alloc_locked(rc_ctx->shpool, n);\n    if (node == NULL) {\n        ngx_http_tfs_expire_and_alloc(node, n);\n    }\n\n    rc_info_node = (ngx_http_tfs_rcs_info_t *) &node->color;\n\n    node->key = ngx_murmur_hash2(appkey.data, appkey.len);\n\n    rc_info_node->appkey.data = ngx_slab_alloc_locked(rc_ctx->shpool,\n                                                      appkey.len);\n    if (rc_info_node->appkey.data == NULL) {\n        ngx_http_tfs_rc_server_expire(rc_ctx);\n        rc_info_node->appkey.data = ngx_slab_alloc_locked(rc_ctx->shpool,\n                                                          appkey.len);\n        if (rc_info_node->appkey.data == NULL) {\n            goto login_error;\n        }\n    }\n\n    ngx_memcpy(rc_info_node->appkey.data, appkey.data, appkey.len);\n    rc_info_node->appkey.len = appkey.len;\n\n    /* parse session id */\n    len = *((uint32_t *) data);\n    p = data + sizeof(uint32_t);\n    if (len <= 0) {\n        rc_info_node->session_id.len = 0;\n        goto login_error;\n    }\n\n    rc_info_node->session_id.len = len - 1;\n    rc_info_node->session_id.data = ngx_slab_alloc_locked(rc_ctx->shpool, len);\n    if (rc_info_node->session_id.data == NULL) {\n        ngx_http_tfs_rc_server_expire(rc_ctx);\n        rc_info_node->session_id.data =\n            ngx_slab_alloc_locked(rc_ctx->shpool, rc_info_node->session_id.len);\n        if (rc_info_node->session_id.data == NULL) {\n            goto login_error;\n        }\n    }\n\n    ngx_memcpy(rc_info_node->session_id.data, p, rc_info_node->session_id.len);\n\n    p += rc_info_node->session_id.len + 1;\n\n    /* parse rc info */\n    rc = ngx_http_tfs_parse_rc_info(rc_info_node, rc_ctx, p);\n    if (rc == NGX_ERROR) {\n        goto login_error;\n    }\n\n    ngx_http_tfs_update_rc_servers(t, rc_info_node);\n\n    t->rc_info_node = rc_info_node;\n    ngx_rbtree_insert(&rc_ctx->sh->rbtree, node);\n    ngx_queue_insert_head(&rc_ctx->sh->queue, &rc_info_node->queue);\n    ngx_queue_insert_tail(&rc_ctx->sh->kp_queue, &rc_info_node->kp_queue);\n\n    return NGX_OK;\n\nlogin_error:\n\n    ngx_http_tfs_rc_server_destroy_node(rc_ctx, rc_info_node);\n    t->rc_info_node = NULL;\n    return NGX_ERROR;\n}\n\n\nstatic ngx_int_t\nngx_http_tfs_parse_session_id(ngx_str_t *session_id, uint64_t *app_id)\n{\n  char        *first_pos;\n  const char  separator_key = '-';\n\n  first_pos = ngx_strchr(session_id->data, separator_key);\n  if (first_pos == NULL) {\n      return NGX_ERROR;\n  }\n\n  return ngx_http_tfs_atoull(session_id->data,\n                             ((u_char *)first_pos - session_id->data),\n                             (unsigned long long *) app_id);\n}\n\n\nvoid\nngx_http_tfs_select_rc_server(ngx_http_tfs_t *t)\n{\n    struct sockaddr_in       *addr_in;\n    ngx_http_tfs_inet_t      *addr;\n    ngx_http_tfs_upstream_t  *upstream;\n\n    upstream = t->loc_conf->upstream;\n\n    if (upstream->rc_servers_count == 0) {\n        return;\n    }\n\n    if (++upstream->rcserver_index >= upstream->rc_servers_count) {\n        upstream->rcserver_index = 0;\n    }\n\n    addr_in = (struct sockaddr_in *)upstream->ups_addr->sockaddr;\n    addr = (ngx_http_tfs_inet_t*)&upstream->rc_servers[upstream->rcserver_index];\n    addr_in->sin_addr.s_addr = addr->ip;\n    addr_in->sin_port = htons(addr->port);\n}\n"
  },
  {
    "path": "modules/ngx_http_tfs_module/ngx_http_tfs_rc_server_message.h",
    "content": "\n/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#ifndef _NGX_TFS_RC_SERVER_MESSAGE_H_INCLUDED_\n#define _NGX_TFS_RC_SERVER_MESSAGE_H_INCLUDED_\n\n\n#include <ngx_http_tfs.h>\n\n\nngx_chain_t *ngx_http_tfs_rc_server_create_message(ngx_http_tfs_t *t);\nngx_int_t ngx_http_tfs_rc_server_parse_message(ngx_http_tfs_t *t);\nvoid ngx_http_tfs_select_rc_server(ngx_http_tfs_t *t);\n\n\n#endif  /* _NGX_TFS_RC_SERVER_MESSAGE_H_INCLUDED_ */\n"
  },
  {
    "path": "modules/ngx_http_tfs_module/ngx_http_tfs_remote_block_cache.c",
    "content": "\n/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#include <ngx_http_tfs.h>\n#include <ngx_http_tfs_data_server_message.h>\n#include <ngx_http_tfs_local_block_cache.h>\n#include <ngx_http_tfs_remote_block_cache.h>\n\n\nstatic void ngx_http_tfs_remote_block_cache_get_handler(\n    ngx_http_tair_key_value_t *kv, ngx_int_t rc, void *data);\nstatic void ngx_http_tfs_remote_block_cache_dummy_handler(ngx_int_t rc,\n    void *data);\n\nstatic void ngx_http_tfs_remote_block_cache_mget_handler(ngx_array_t *kvs,\n    ngx_int_t rc, void *data);\n\nngx_int_t\nngx_http_tfs_remote_block_cache_lookup(\n    ngx_http_tfs_remote_block_cache_ctx_t *ctx,\n    ngx_pool_t *pool, ngx_log_t *log, ngx_http_tfs_block_cache_key_t* key)\n{\n    ngx_int_t             rc;\n    ngx_http_tair_data_t  tair_key;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, log, 0,\n                   \"lookup remote block cache, ns addr: %uL, block id: %uD\",\n                   key->ns_addr, key->block_id);\n\n    tair_key.type = NGX_HTTP_TAIR_INT;\n    tair_key.data = (u_char *)key;\n    tair_key.len = NGX_HTTP_TFS_BLOCK_CACHE_KEY_SIZE;\n\n    rc = ngx_http_tfs_tair_get_helper(\n                                    ctx->tair_instance,\n                                    pool, log,\n                                    &tair_key,\n                                    ngx_http_tfs_remote_block_cache_get_handler,\n                                    (void *)ctx);\n\n    return rc;\n}\n\n\nstatic void\nngx_http_tfs_remote_block_cache_get_handler(ngx_http_tair_key_value_t *kv,\n    ngx_int_t rc, void *data)\n{\n    u_char                                 *p, *q;\n    uint32_t                                ds_count;\n    ngx_http_tfs_t                         *t;\n    ngx_http_tfs_inet_t                    *addr;\n    ngx_http_tfs_segment_data_t            *segment_data;\n    ngx_http_tfs_block_cache_key_t          key;\n    ngx_http_tfs_block_cache_value_t        value;\n    ngx_http_tfs_remote_block_cache_ctx_t  *ctx = data;\n\n    t = ctx->data;\n    segment_data = &t->file.segment_data[t->file.segment_index];\n    if (rc == NGX_HTTP_ETAIR_SUCCESS) {\n        q = kv->key.data;\n        p = kv->value->data;\n        if (p != NULL\n            && (kv->value->len\n                > NGX_HTTP_TFS_REMOTE_BLOCK_CACHE_VALUE_BASE_SIZE))\n        {\n            key.ns_addr = *(uint64_t *)q;\n            q += sizeof(uint64_t);\n            key.block_id = *(uint32_t *)q;\n\n            ds_count = *(uint32_t *)p;\n            p += sizeof(uint32_t);\n\n            if (ds_count > 0) {\n                segment_data->block_info.ds_count = ds_count;\n                segment_data->block_info.ds_addrs = ngx_pcalloc(t->pool,\n                                       sizeof(ngx_http_tfs_inet_t) * ds_count);\n                if (segment_data->block_info.ds_addrs == NULL) {\n                    ngx_http_tfs_finalize_request(t->data, t,\n                                                NGX_HTTP_INTERNAL_SERVER_ERROR);\n                    return;\n                }\n                ngx_memcpy(segment_data->block_info.ds_addrs, p,\n                           ds_count * sizeof(ngx_http_tfs_inet_t));\n\n                /* insert local block cache */\n                if (t->block_cache_ctx.use_cache\n                    & NGX_HTTP_TFS_LOCAL_BLOCK_CACHE)\n                {\n                    value.ds_count = ds_count;\n                    value.ds_addrs =\n                        (uint64_t *)segment_data->block_info.ds_addrs;\n                    ngx_http_tfs_local_block_cache_insert(\n                                                   t->block_cache_ctx.local_ctx,\n                                                   t->log, &key, &value);\n                }\n\n                /* skip GET_BLK_INFO state */\n                t->state += 1;\n\n                segment_data->cache_hit = NGX_HTTP_TFS_REMOTE_BLOCK_CACHE;\n\n                /* select data server */\n                addr = ngx_http_tfs_select_data_server(t, segment_data);\n\n                ngx_http_tfs_peer_set_addr(t->pool,\n                                           &t->tfs_peer_servers[NGX_HTTP_TFS_DATA_SERVER],\n                                           addr);\n\n            } else {\n                /* remote block cache invalid, need remove it */\n                ngx_http_tfs_remote_block_cache_remove(ctx, t->pool, t->log,\n                                                       &key);\n            }\n        }\n\n    } else {\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, t->log, 0,\n                       \"lookup remote block cache, \"\n                       \"ns addr: %V, block id: %uD not found\",\n                       &t->name_server_addr_text,\n                       segment_data->segment_info.block_id);\n    }\n\n    ngx_http_tfs_finalize_state(t, NGX_OK);\n}\n\n\nngx_int_t\nngx_http_tfs_remote_block_cache_insert(\n    ngx_http_tfs_remote_block_cache_ctx_t *ctx,\n    ngx_pool_t *pool, ngx_log_t *log,\n    ngx_http_tfs_block_cache_key_t *key,\n    ngx_http_tfs_block_cache_value_t *value)\n{\n    ngx_int_t             rc;\n    ngx_pool_t           *tmp_pool;\n    ngx_http_tair_data_t  tair_key, tair_value;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, log, 0,\n                   \"insert remote block cache, \"\n                   \"ns addr: %uL, block id: %uD\",\n                   key->ns_addr, key->block_id);\n\n    tair_key.type = NGX_HTTP_TAIR_INT;\n    tair_key.data = (u_char *)key;\n    tair_key.len = NGX_HTTP_TFS_BLOCK_CACHE_KEY_SIZE;\n\n    tair_value.len = NGX_HTTP_TFS_REMOTE_BLOCK_CACHE_VALUE_BASE_SIZE\n                      + value->ds_count * sizeof(uint64_t);\n    tair_value.data = ngx_pcalloc(pool, tair_value.len);\n    if (tair_value.data == NULL) {\n        return NGX_ERROR;\n    }\n    *(uint32_t*)tair_value.data = value->ds_count;\n    ngx_memcpy(tair_value.data+ NGX_HTTP_TFS_REMOTE_BLOCK_CACHE_VALUE_BASE_SIZE,\n               value->ds_addrs, value->ds_count * sizeof(uint64_t));\n    tair_value.type = NGX_HTTP_TAIR_INT;\n\n    /* since we do not care returns,\n     * we make a tmp pool and destroy it in callback\n     */\n    tmp_pool = ngx_create_pool(4096, log);\n    if (tmp_pool == NULL) {\n        return NGX_ERROR;\n    }\n\n    rc = ngx_http_tfs_tair_put_helper(\n                                  ctx->tair_instance,\n                                  tmp_pool, log,\n                                  &tair_key, &tair_value,\n                                  0/*expire*/, 0/* do not care version */,\n                                  ngx_http_tfs_remote_block_cache_dummy_handler,\n                                  (void *)tmp_pool);\n\n    return rc;\n}\n\n\nstatic void\nngx_http_tfs_remote_block_cache_dummy_handler(ngx_int_t rc, void *data)\n{\n    ngx_destroy_pool((ngx_pool_t *)data);\n}\n\n\nvoid\nngx_http_tfs_remote_block_cache_remove(\n    ngx_http_tfs_remote_block_cache_ctx_t *ctx,\n    ngx_pool_t *pool, ngx_log_t *log, ngx_http_tfs_block_cache_key_t* key)\n{\n    ngx_int_t              rc;\n    ngx_pool_t            *tmp_pool;\n    ngx_array_t            tair_keys;\n    ngx_http_tair_data_t  *tair_key;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, log, 0,\n                   \"remove remote block cache, ns addr: %uL, block id: %uD\",\n                   key->ns_addr, key->block_id);\n\n    rc = ngx_array_init(&tair_keys, pool, 1, sizeof(ngx_http_tair_data_t));\n    if (rc == NGX_ERROR) {\n        return;\n    }\n    tair_key = (ngx_http_tair_data_t*) ngx_array_push(&tair_keys);\n\n    tair_key->type = NGX_HTTP_TAIR_INT;\n    tair_key->data = (u_char *)key;\n    tair_key->len = NGX_HTTP_TFS_BLOCK_CACHE_KEY_SIZE;\n\n    /* since we do not care returns,\n     * we make a tmp pool and destroy it in callback\n     */\n    tmp_pool = ngx_create_pool(4096, log);\n    if (tmp_pool == NULL) {\n        return;\n    }\n\n    (void) ngx_http_tfs_tair_delete_helper(\n                                  ctx->tair_instance,\n                                  tmp_pool, log,\n                                  &tair_keys,\n                                  ngx_http_tfs_remote_block_cache_dummy_handler,\n                                  (void *)tmp_pool);\n\n}\n\n\nngx_int_t\nngx_http_tfs_remote_block_cache_batch_lookup(\n    ngx_http_tfs_remote_block_cache_ctx_t *ctx,\n    ngx_pool_t *pool, ngx_log_t *log, ngx_array_t* keys)\n{\n    ngx_int_t                        rc;\n    ngx_uint_t                       i;\n    ngx_array_t                     *tair_kvs;\n    ngx_http_tair_key_value_t       *tair_kv;\n    ngx_http_tfs_block_cache_key_t  *key;\n\n    tair_kvs = ngx_array_create(pool, keys->nelts,\n                                sizeof(ngx_http_tair_key_value_t));\n    if (tair_kvs == NULL) {\n        return NGX_ERROR;\n    }\n\n    key = keys->elts;\n    for (i = 0; i < keys->nelts; i++, key++) {\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, log, 0,\n                       \"batch lookup remote block cache, \"\n                       \"ns addr: %uL, block id: %uD\",\n                       key->ns_addr, key->block_id);\n\n        tair_kv = (ngx_http_tair_key_value_t *)ngx_array_push(tair_kvs);\n        if (tair_kv == NULL) {\n            return NGX_ERROR;\n        }\n\n        tair_kv->key.type = NGX_HTTP_TAIR_INT;\n        tair_kv->key.data = (u_char *)key;\n        tair_kv->key.len = NGX_HTTP_TFS_BLOCK_CACHE_KEY_SIZE;\n    }\n\n    rc = ngx_http_tfs_tair_mget_helper(\n                                   ctx->tair_instance,\n                                   pool, log,\n                                   tair_kvs,\n                                   ngx_http_tfs_remote_block_cache_mget_handler,\n                                   (void *)ctx);\n    return rc;\n}\n\n\nstatic void\nngx_http_tfs_remote_block_cache_mget_handler(ngx_array_t *kvs, ngx_int_t rc,\n    void *data)\n{\n    u_char                                 *p, *q;\n    uint32_t                                ds_count, block_count;\n    ngx_uint_t                              i, j, hit_count;\n    ngx_http_tfs_t                         *t;\n    ngx_http_tair_key_value_t              *kv;\n    ngx_http_tfs_segment_data_t            *segment_data;\n    ngx_http_tfs_block_cache_key_t          key;\n    ngx_http_tfs_block_cache_value_t        value;\n    ngx_http_tfs_remote_block_cache_ctx_t  *ctx = data;\n\n    t = ctx->data;\n\n    segment_data = &t->file.segment_data[t->file.segment_index];\n    block_count = t->file.segment_count - t->file.segment_index;\n    if (block_count > NGX_HTTP_TFS_MAX_BATCH_COUNT) {\n        block_count = NGX_HTTP_TFS_MAX_BATCH_COUNT;\n    }\n\n    if (rc == NGX_OK) {\n        kv = kvs->elts;\n        hit_count = 0;\n        for (i = 0; i < kvs->nelts; i++, kv++) {\n            if (kv->rc != NGX_HTTP_ETAIR_SUCCESS) {\n                continue;\n            }\n            q = kv->key.data;\n            p = kv->value->data;\n            if (p != NULL\n                && (kv->value->len\n                    > NGX_HTTP_TFS_REMOTE_BLOCK_CACHE_VALUE_BASE_SIZE))\n            {\n                key.ns_addr = *(uint64_t *)q;\n                q += sizeof(uint64_t);\n                key.block_id = *(uint32_t *)q;\n\n                ds_count = *(uint32_t *)p;\n                p += sizeof(uint32_t);\n\n                if (ds_count > 0) {\n                    /* find out segment */\n                    for (j = 0; j < block_count; j++) {\n                        if(segment_data[j].segment_info.block_id == key.block_id\n                           && segment_data[j].block_info.ds_addrs == NULL)\n                        {\n                            break;\n                        }\n                    }\n                    /* not found, some error happen */\n                    if (j == block_count) {\n                        continue;\n                    }\n\n                    segment_data[j].block_info.ds_count = ds_count;\n                    segment_data[j].block_info.ds_addrs = ngx_pcalloc(t->pool,\n                                        ds_count * sizeof(ngx_http_tfs_inet_t));\n                    if (segment_data[j].block_info.ds_addrs == NULL) {\n                        ngx_http_tfs_finalize_request(t->data, t,\n                                                NGX_HTTP_INTERNAL_SERVER_ERROR);\n                        return;\n                    }\n                    ngx_memcpy(segment_data[j].block_info.ds_addrs, p,\n                               ds_count * sizeof(ngx_http_tfs_inet_t));\n\n                    if (t->block_cache_ctx.use_cache\n                        & NGX_HTTP_TFS_LOCAL_BLOCK_CACHE)\n                    {\n                        value.ds_count = ds_count;\n                        value.ds_addrs =\n                            (uint64_t *)segment_data[j].block_info.ds_addrs;\n                        ngx_http_tfs_local_block_cache_insert(\n                            t->block_cache_ctx.local_ctx, t->log, &key, &value);\n                    }\n\n                    hit_count++;\n                    segment_data[j].cache_hit = NGX_HTTP_TFS_REMOTE_BLOCK_CACHE;\n\n                    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, t->log, 0,\n                                   \"remote block cache hit, \"\n                                   \"ns addr: %V, block id: %uD\",\n                                   &t->name_server_addr_text,\n                                   segment_data[j].segment_info.block_id);\n\n                } else {\n                    /* remote block cache invalid, need remove it */\n                    ngx_http_tfs_remote_block_cache_remove(ctx, t->pool, t->log,\n                                                           &key);\n                }\n            }\n        }\n\n        if (hit_count > 0) {\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, t->log, 0,\n                           \"batch lookup remote block cache, hit_count: %ui\",\n                           hit_count);\n\n            /* remote block cache hit count */\n            t->file.curr_batch_count += hit_count;\n\n            if (hit_count == kvs->nelts) {\n                /* all cache hit, start batch process */\n                t->decline_handler = ngx_http_tfs_batch_process_start;\n                rc = NGX_DECLINED;\n            }\n        }\n\n    } else {\n        rc = NGX_OK;\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, t->log, 0,\n                       \"remote block cache miss\");\n    }\n\n    ngx_http_tfs_finalize_state(t, rc);\n}\n\n\n#ifdef NGX_HTTP_TFS_USE_TAIR\nngx_int_t\nngx_http_tfs_get_remote_block_cache_instance(\n    ngx_http_tfs_remote_block_cache_ctx_t *ctx,\n    ngx_str_t *server_addr)\n{\n    size_t                                server_addr_len;\n    uint32_t                              server_addr_hash;\n    ngx_int_t                             rc, i;\n    ngx_str_t                            *st, *group_name;\n    ngx_array_t                           config_server;\n    ngx_http_tfs_t                       *t;\n    ngx_http_tfs_tair_instance_t         *instance;\n    ngx_http_tfs_tair_server_addr_info_t  server_addr_info;\n\n    if (server_addr->len == 0\n        || server_addr->data == NULL)\n    {\n        return NGX_ERROR;\n    }\n\n    t = ctx->data;\n    server_addr_len = server_addr->len;\n    server_addr_hash = ngx_murmur_hash2(server_addr->data, server_addr_len);\n\n    instance = ctx->tair_instance;\n    if (instance->server != NULL) {\n        if (instance->server_addr_hash == server_addr_hash) {\n            return NGX_OK;\n        }\n\n        ngx_http_etair_destory_server(instance->server,\n                                      (ngx_cycle_t *) ngx_cycle);\n        instance->server = NULL;\n    }\n\n    rc = ngx_http_tfs_parse_tair_server_addr_info(&server_addr_info,\n                                                  server_addr->data,\n                                                  server_addr_len,\n                                                  t->pool, 0);\n    if (rc == NGX_ERROR) {\n        return NGX_ERROR;\n    }\n\n    rc = ngx_array_init(&config_server, t->pool,\n                        NGX_HTTP_TFS_TAIR_CONFIG_SERVER_COUNT,\n                        sizeof(ngx_str_t));\n    if (rc == NGX_ERROR) {\n        return NGX_ERROR;\n    }\n\n    for (i = 0; i < NGX_HTTP_TFS_TAIR_CONFIG_SERVER_COUNT; i++) {\n        if (server_addr_info.server[i].len > 0 ) {\n            st = (ngx_str_t *) ngx_array_push(&config_server);\n            *st = server_addr_info.server[i];\n        }\n    }\n\n    group_name = &server_addr_info.server[NGX_HTTP_TFS_TAIR_CONFIG_SERVER_COUNT];\n    instance->server = ngx_http_etair_create_server(group_name,\n                                                    &config_server,\n                                                    t->main_conf->tair_timeout,\n                                                    (ngx_cycle_t *) ngx_cycle);\n    if (instance->server == NULL) {\n        return NGX_ERROR;\n    }\n    instance->server_addr_hash = server_addr_hash;\n    instance->area = server_addr_info.area;\n\n    return NGX_OK;\n}\n\n#else\n\nngx_int_t\nngx_http_tfs_get_remote_block_cache_instance(\n    ngx_http_tfs_remote_block_cache_ctx_t *ctx,\n    ngx_str_t *server_addr)\n{\n    return NGX_ERROR;\n}\n\n#endif\n\n"
  },
  {
    "path": "modules/ngx_http_tfs_module/ngx_http_tfs_remote_block_cache.h",
    "content": "\n/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#ifndef _NGX_HTTP_TFS_REMOTE_BLOCK_CACHE_H_INCLUDED_\n#define _NGX_HTTP_TFS_REMOTE_BLOCK_CACHE_H_INCLUDED_\n\n\n#include <ngx_http_tfs_block_cache.h>\n\n\nngx_int_t ngx_http_tfs_remote_block_cache_lookup(\n    ngx_http_tfs_remote_block_cache_ctx_t *ctx,\n    ngx_pool_t *pool, ngx_log_t *log, ngx_http_tfs_block_cache_key_t* key);\n\nngx_int_t ngx_http_tfs_remote_block_cache_insert(\n    ngx_http_tfs_remote_block_cache_ctx_t *ctx,\n    ngx_pool_t *pool, ngx_log_t *log, ngx_http_tfs_block_cache_key_t *key,\n    ngx_http_tfs_block_cache_value_t *value);\n\nvoid ngx_http_tfs_remote_block_cache_remove(\n    ngx_http_tfs_remote_block_cache_ctx_t *ctx,\n    ngx_pool_t *pool, ngx_log_t *log, ngx_http_tfs_block_cache_key_t *key);\n\nngx_int_t ngx_http_tfs_remote_block_cache_batch_lookup(\n    ngx_http_tfs_remote_block_cache_ctx_t *ctx,\n    ngx_pool_t *pool, ngx_log_t *log, ngx_array_t* keys);\n\nngx_int_t ngx_http_tfs_remote_block_cache_batch_insert(\n    ngx_http_tfs_remote_block_cache_ctx_t *ctx,\n    ngx_pool_t *pool, ngx_log_t *log, ngx_array_t *kvs);\n\nngx_int_t ngx_http_tfs_get_remote_block_cache_instance(\n    ngx_http_tfs_remote_block_cache_ctx_t *ctx,\n    ngx_str_t *server_addr);\n\n\n#endif /* _NGX_HTTP_TFS_REMOTE_BLOCK_CACHE_H_INCLUDED_ */\n"
  },
  {
    "path": "modules/ngx_http_tfs_module/ngx_http_tfs_restful.c",
    "content": "\n/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#include <ngx_tfs_common.h>\n#include <ngx_http_tfs_restful.h>\n#include <ngx_http_tfs_protocol.h>\n\n\n#define NGX_HTTP_TFS_VERSION1 \"v1\"\n#define NGX_HTTP_TFS_VERSION2 \"v2\"\n\n\nstatic ngx_str_t ali_move_source = ngx_string(\"x-ali-move-source\");\n\n\nstatic ngx_int_t\nngx_http_restful_parse_raw(ngx_http_request_t *r,\n    ngx_http_tfs_restful_ctx_t *ctx, u_char *data)\n{\n    u_char  *p, ch, *start, *last, *meta_data;\n\n    enum {\n        sw_appkey = 0,\n        sw_metadata,\n        sw_name,\n    } state;\n\n    state = sw_appkey;\n    last = r->uri.data + r->uri.len;\n    start = data;\n    meta_data = NULL;\n\n    for (p = data; p < last; p++) {\n        ch = *p;\n\n        switch (state) {\n\n        case sw_appkey:\n            if (ch == '/') {\n                ctx->appkey.data = start;\n                ctx->appkey.len = p - start;\n\n                state = sw_metadata;\n                if (p + 1 < last) {\n                    meta_data = p + 1;\n                }\n            }\n\n            break;\n\n        case sw_metadata:\n            if (ch == '/') {\n                if (ngx_memcmp(meta_data, \"metadata\", 8) == 0) {\n                    if (p + 1 < last) {\n                        ctx->meta = NGX_HTTP_TFS_YES;\n                        ctx->file_path_s.data = p + 1;\n                        state = sw_name;\n\n                    } else {\n                        return NGX_ERROR;\n                    }\n                }\n            }\n        case sw_name:\n            break;\n        }\n    }\n\n    if (r->method == NGX_HTTP_GET || r->method == NGX_HTTP_DELETE\n        || r->method == NGX_HTTP_PUT || r->method == NGX_HTTP_HEAD) {\n        if (state == sw_appkey) {\n            return NGX_ERROR;\n        }\n\n        if (state == sw_metadata) {\n            ctx->file_path_s.data = meta_data;\n        }\n\n        if (state == sw_name) {\n            if (r->method == NGX_HTTP_DELETE || r->method == NGX_HTTP_PUT) {\n                return NGX_ERROR;\n            }\n        }\n        ctx->file_path_s.len = p - ctx->file_path_s.data;\n        if (ctx->file_path_s.len < 1\n            || ctx->file_path_s.len > NGX_HTTP_TFS_MAX_FILE_NAME_LEN)\n        {\n            return NGX_ERROR;\n        }\n\n    } else {\n        if (state == sw_appkey) {\n            ctx->appkey.len = p - start;\n            if (ctx->appkey.len == 0) {\n                return NGX_ERROR;\n            }\n            ctx->appkey.data = start;\n\n        } else {\n            return NGX_ERROR;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_restful_parse_custom_name(ngx_http_request_t *r,\n    ngx_http_tfs_restful_ctx_t *ctx, u_char *data)\n{\n    u_char    *p, ch, *start, *last, *appid, *meta_data;\n    ngx_int_t  rc;\n\n    enum {\n        sw_appkey = 0,\n        sw_metadata,\n        sw_appid,\n        sw_uid,\n        sw_type,\n        sw_name,\n    } state;\n\n    state = sw_appkey;\n    last = r->uri.data + r->uri.len;\n    start = data;\n    appid = NULL;\n    meta_data = NULL;\n\n    for (p = data; p < last; p++) {\n        ch = *p;\n\n        switch (state) {\n        case sw_appkey:\n            if (ch == '/') {\n                ctx->appkey.data = start;\n                ctx->appkey.len = p - start;\n\n                start = p + 1;\n                /* GET /v2/appkey/appid */\n                if (start < last) {\n                    if (*start == 'a') {\n                        state = sw_name;\n                        appid = start;\n                    } else if (*start == 'm') {\n                        state = sw_metadata;\n                        meta_data = start;\n                    } else {\n                        state = sw_appid;\n                    }\n                }\n            }\n            break;\n        case sw_metadata:\n            if (ch == '/') {\n                if (ngx_memcmp(meta_data, \"metadata\", 8) == 0) {\n                    if (p + 1 < last) {\n                        ctx->meta = NGX_HTTP_TFS_YES;\n                        start = p + 1;\n                        state = sw_appid;\n\n                    } else {\n                        return NGX_ERROR;\n                    }\n                }\n            }\n            break;\n        case sw_appid:\n            if (ch == '/') {\n                rc = ngx_http_tfs_atoull(start, p - start,\n                                         (unsigned long long *)&ctx->app_id);\n                if (rc == NGX_ERROR || ctx->app_id == 0) {\n                    return NGX_ERROR;\n                }\n\n                start = p + 1;\n                state = sw_uid;\n                break;\n            }\n\n            if (ch < '0' || ch > '9') {\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                              \"appid is invalid\");\n                return NGX_ERROR;\n            }\n\n            if ((size_t) (p - start) > (NGX_INT64_LEN - 1)) {\n                return NGX_ERROR;\n            }\n\n            break;\n        case sw_uid:\n            if (ch == '/') {\n                rc = ngx_http_tfs_atoull(start, p - start,\n                                         (unsigned long long *)&ctx->user_id);\n                if (rc == NGX_ERROR || ctx->user_id == 0) {\n                    return NGX_ERROR;\n                }\n                start = p + 1;\n                state = sw_type;\n                break;\n            }\n\n            if (ch < '0' || ch > '9') {\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                              \"userid is invalid\");\n                return NGX_ERROR;\n            }\n\n            if ((size_t) (p - start) > NGX_INT64_LEN - 1) {\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                              \"userid is too big\");\n                return NGX_ERROR;\n            }\n            break;\n        case sw_type:\n            if (ch == '/') {\n                if (ngx_strncmp(start, \"file\", p - start) == 0) {\n                    ctx->file_type = NGX_HTTP_TFS_CUSTOM_FT_FILE;\n\n                } else if (ngx_strncmp(start, \"dir\", p - start) == 0) {\n                    ctx->file_type = NGX_HTTP_TFS_CUSTOM_FT_DIR;\n\n                } else {\n                    return NGX_ERROR;\n                }\n                ctx->file_path_s.data = p;\n                state = sw_name;\n            }\n            break;\n        case sw_name:\n            break;\n        }\n    }\n\n    if (r->method == NGX_HTTP_GET && appid != NULL) {\n        if (ngx_memcmp(appid, \"appid\", 5) == 0) {\n            ctx->get_appid = NGX_HTTP_TFS_YES;\n            ctx->file_path_s.data = appid;\n            ctx->file_path_s.len = 5;\n            return NGX_OK;\n        }\n        return NGX_ERROR;\n    }\n\n    ctx->file_path_s.len = p - ctx->file_path_s.data;\n    if (ctx->file_path_s.len < 1\n        || ctx->file_path_s.len > NGX_HTTP_TFS_MAX_FILE_NAME_LEN)\n    {\n        return NGX_ERROR;\n    }\n\n    /* forbid file actions on \"/\" */\n    if (ctx->file_type == NGX_HTTP_TFS_CUSTOM_FT_FILE\n        && ctx->file_path_s.len == 1)\n    {\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_restful_parse_uri(ngx_http_request_t *r,\n    ngx_http_tfs_restful_ctx_t *ctx)\n{\n    u_char  *p, ch, *last;\n\n    enum {\n        sw_start = 0,\n        sw_version_prefix,\n        sw_version,\n        sw_backslash,\n    } state;\n\n    state = sw_start;\n    last = r->uri.data + r->uri.len;\n\n    for (p = r->uri.data; p < last; p++) {\n        ch = *p;\n\n        switch (state) {\n        case sw_start:\n            state = sw_version_prefix;\n            break;\n        case sw_version_prefix:\n            if (ch != 'v') {\n                ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                               \"version invalid %V \", &r->uri);\n                return NGX_ERROR;\n            }\n            state = sw_version;\n            break;\n\n        case sw_version:\n            if (ch < '1' || ch > '9') {\n                return NGX_ERROR;\n            }\n\n            ctx->version = ch - '0';\n            if (ctx->version > 2) {\n                return NGX_ERROR;\n            }\n\n            state = sw_backslash;\n            break;\n\n        case sw_backslash:\n            if (ch != '/') {\n                return NGX_ERROR;\n            }\n\n            if (ctx->version == 1) {\n                return ngx_http_restful_parse_raw(r, ctx, ++p);\n            }\n\n            if (ctx->version == 2) {\n                return ngx_http_restful_parse_custom_name(r, ctx, ++p);\n            }\n\n            return NGX_ERROR;\n        }\n    }\n\n    return NGX_ERROR;\n}\n\n\nstatic ngx_int_t\nngx_http_restful_parse_action(ngx_http_request_t *r,\n    ngx_http_tfs_restful_ctx_t *ctx)\n{\n    ngx_int_t  rc;\n    ngx_str_t  arg_value, file_path_d, file_temp_path;\n\n    switch(r->method) {\n    case NGX_HTTP_GET:\n        if (ctx->get_appid) {\n            ctx->action.code = NGX_HTTP_TFS_ACTION_GET_APPID;\n            ngx_str_set(&ctx->action.msg, \"get_appid\");\n            return NGX_OK;\n        }\n        if (ctx->file_type == NGX_HTTP_TFS_CUSTOM_FT_FILE) {\n            if (ctx->meta) {\n                ctx->action.code = NGX_HTTP_TFS_ACTION_LS_FILE;\n                ngx_str_set(&ctx->action.msg, \"ls_file\");\n                return NGX_OK;\n            }\n\n            ctx->action.code = NGX_HTTP_TFS_ACTION_READ_FILE;\n            ngx_str_set(&ctx->action.msg, \"read_file\");\n\n            if (r->headers_in.range != NULL) {\n                return NGX_HTTP_BAD_REQUEST;\n            }\n\n            if (ngx_http_arg(r, (u_char *) \"check_hole\", 10, &arg_value)\n                == NGX_OK)\n            {\n                ctx->chk_file_hole = ngx_atoi(arg_value.data, arg_value.len);\n                if (ctx->chk_file_hole == NGX_ERROR\n                    || (ctx->chk_file_hole != NGX_HTTP_TFS_NO\n                        && ctx->chk_file_hole != NGX_HTTP_TFS_YES))\n                {\n                    return NGX_HTTP_BAD_REQUEST;\n                }\n            }\n\n            if (ngx_http_arg(r, (u_char *) \"offset\", 6, &arg_value) == NGX_OK) {\n                ctx->offset = ngx_http_tfs_atoll(arg_value.data, arg_value.len);\n                if (ctx->offset == NGX_ERROR) {\n                    return NGX_HTTP_BAD_REQUEST;\n                }\n            }\n\n            if (ngx_http_arg(r, (u_char *) \"size\", 4, &arg_value) == NGX_OK) {\n                rc = ngx_http_tfs_atoull(arg_value.data, arg_value.len,\n                                         (unsigned long long *)&ctx->size);\n                if (rc == NGX_ERROR) {\n                    return NGX_HTTP_BAD_REQUEST;\n                }\n                if (ctx->size == 0) {\n                    return NGX_HTTP_BAD_REQUEST;\n                }\n                return NGX_OK;\n            }\n\n            ctx->size = NGX_HTTP_TFS_MAX_SIZE;\n\n            return NGX_OK;\n        }\n\n        ctx->action.code = NGX_HTTP_TFS_ACTION_LS_DIR;\n        ngx_str_set(&ctx->action.msg, \"ls_dir\");\n        return NGX_OK;\n    case NGX_HTTP_POST:\n        if (ngx_http_tfs_parse_headerin(r, &ali_move_source, &file_path_d)\n            == NGX_OK)\n        {\n            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                          \"move from %V to %V\",\n                          &file_path_d, &ctx->file_path_s);\n\n            if (file_path_d.len < 1\n                || file_path_d.len > NGX_HTTP_TFS_MAX_FILE_NAME_LEN\n                || ctx->file_path_s.len == 1)\n            {\n                return NGX_HTTP_BAD_REQUEST;\n            }\n\n            if (ctx->file_path_s.len == file_path_d.len\n                && ngx_strncmp(ctx->file_path_s.data, file_path_d.data,\n                               file_path_d.len) == 0)\n            {\n                return NGX_HTTP_BAD_REQUEST;\n            }\n\n            file_temp_path = ctx->file_path_s;\n            ctx->file_path_s = file_path_d;\n            ctx->file_path_d = file_temp_path;\n            if (ctx->file_type == NGX_HTTP_TFS_CUSTOM_FT_FILE) {\n                ctx->action.code = NGX_HTTP_TFS_ACTION_MOVE_FILE;\n                ngx_str_set(&ctx->action.msg, \"move_file\");\n\n            } else {\n                ctx->action.code = NGX_HTTP_TFS_ACTION_MOVE_DIR;\n                ngx_str_set(&ctx->action.msg, \"move_dir\");\n            }\n\n        } else {\n            if (ctx->file_type == NGX_HTTP_TFS_CUSTOM_FT_FILE) {\n                ctx->action.code = NGX_HTTP_TFS_ACTION_CREATE_FILE;\n                ngx_str_set(&ctx->action.msg, \"create_file\");\n\n            } else {\n                /* forbid create \"/\" */\n                if (ctx->file_path_s.len == 1) {\n                    return NGX_HTTP_BAD_REQUEST;\n                }\n\n                ctx->action.code = NGX_HTTP_TFS_ACTION_CREATE_DIR;\n                ngx_str_set(&ctx->action.msg, \"create_dir\");\n            }\n        }\n        if (ngx_http_arg(r, (u_char *) \"recursive\", 9, &arg_value) == NGX_OK) {\n            if (arg_value.len != 1) {\n                return NGX_HTTP_BAD_REQUEST;\n            }\n            ctx->recursive = ngx_atoi(arg_value.data, arg_value.len);\n            if (ctx->recursive == NGX_ERROR\n                || (ctx->recursive != NGX_HTTP_TFS_NO\n                    && ctx->recursive != NGX_HTTP_TFS_YES))\n            {\n                return NGX_HTTP_BAD_REQUEST;\n            }\n        }\n        break;\n    case NGX_HTTP_PUT:\n        if (ctx->file_type == NGX_HTTP_TFS_CUSTOM_FT_FILE) {\n            ctx->action.code = NGX_HTTP_TFS_ACTION_WRITE_FILE;\n            ngx_str_set(&ctx->action.msg, \"write_file\");\n            if (ngx_http_arg(r, (u_char *) \"offset\", 6, &arg_value) == NGX_OK) {\n                ctx->offset = ngx_http_tfs_atoll(arg_value.data, arg_value.len);\n                if (ctx->offset == NGX_ERROR) {\n                    return NGX_HTTP_BAD_REQUEST;\n                }\n\n            } else {\n                /* no specify offset, append by default */\n                ctx->offset = NGX_HTTP_TFS_APPEND_OFFSET;\n            }\n            return NGX_OK;\n        }\n        /* forbid put aciont on dir */\n        return NGX_ERROR;\n    case NGX_HTTP_DELETE:\n        if (ctx->file_type == NGX_HTTP_TFS_CUSTOM_FT_FILE) {\n            ctx->action.code = NGX_HTTP_TFS_ACTION_REMOVE_FILE;\n            ngx_str_set(&ctx->action.msg, \"remove_file\");\n            /* for t->file.left_length */\n            ctx->size = NGX_HTTP_TFS_MAX_SIZE;\n\n            return NGX_OK;\n        }\n\n        /* forbid delete \"/\" */\n        if (ctx->file_path_s.len == 1) {\n            return NGX_HTTP_BAD_REQUEST;\n        }\n\n        ctx->action.code = NGX_HTTP_TFS_ACTION_REMOVE_DIR;\n        ngx_str_set(&ctx->action.msg, \"remove_dir\");\n        break;\n    case NGX_HTTP_HEAD:\n        if (ctx->file_type == NGX_HTTP_TFS_CUSTOM_FT_FILE) {\n            ctx->action.code = NGX_HTTP_TFS_ACTION_LS_FILE;\n            ngx_str_set(&ctx->action.msg, \"ls_file\");\n\n        } else {\n            ctx->action.code = NGX_HTTP_TFS_ACTION_LS_DIR;\n            ngx_str_set(&ctx->action.msg, \"ls_dir\");\n        }\n        ctx->chk_exist = NGX_HTTP_TFS_YES;\n        return NGX_OK;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_restful_parse_action_raw(ngx_http_request_t *r,\n    ngx_http_tfs_restful_ctx_t *ctx)\n{\n    ngx_int_t  rc;\n    ngx_str_t  arg_value;\n\n    switch(r->method) {\n    case NGX_HTTP_GET:\n        if (ngx_http_arg(r, (u_char *) \"suffix\", 6, &arg_value) == NGX_OK) {\n            ctx->file_suffix = arg_value;\n        }\n\n        rc = ngx_http_tfs_raw_fsname_parse(&ctx->file_path_s, &ctx->file_suffix,\n                                           &ctx->fsname);\n        if (rc != NGX_OK) {\n            return NGX_HTTP_BAD_REQUEST;\n        }\n\n        if (ngx_http_arg(r, (u_char *) \"type\", 4, &arg_value) == NGX_OK) {\n            if (arg_value.len != 1) {\n                return NGX_HTTP_BAD_REQUEST;\n            }\n            ctx->read_stat_type = ngx_atoi(arg_value.data, arg_value.len);\n            /* normal_read/stat(0) or force_read/stat(1) */\n            if (ctx->read_stat_type == NGX_ERROR\n                || (ctx->read_stat_type != NGX_HTTP_TFS_READ_STAT_NORMAL\n                    && ctx->read_stat_type != NGX_HTTP_TFS_READ_STAT_FORCE))\n            {\n                return NGX_HTTP_BAD_REQUEST;\n            }\n        }\n        if (ctx->meta) {\n            ctx->action.code = NGX_HTTP_TFS_ACTION_STAT_FILE;\n            ngx_str_set(&ctx->action.msg, \"stat_file\");\n\n        } else {\n            ctx->action.code = NGX_HTTP_TFS_ACTION_READ_FILE;\n            ngx_str_set(&ctx->action.msg, \"read_file\");\n            if (ngx_http_arg(r, (u_char *) \"offset\", 6, &arg_value) == NGX_OK) {\n                ctx->offset = ngx_http_tfs_atoll(arg_value.data, arg_value.len);\n                if (ctx->offset == NGX_ERROR) {\n                    return NGX_HTTP_BAD_REQUEST;\n                }\n            }\n\n            if (ngx_http_arg(r, (u_char *) \"size\", 4, &arg_value) == NGX_OK) {\n                rc = ngx_http_tfs_atoull(arg_value.data,\n                                         arg_value.len,\n                                         (unsigned long long *)&ctx->size);\n                if (rc == NGX_ERROR) {\n                    return NGX_HTTP_BAD_REQUEST;\n                }\n\n                if (ctx->size == 0) {\n                    return NGX_HTTP_BAD_REQUEST;\n                }\n\n                return NGX_OK;\n            }\n\n            ctx->size = NGX_HTTP_TFS_MAX_SIZE;\n        }\n        break;\n\n    case NGX_HTTP_POST:\n        ctx->action.code = NGX_HTTP_TFS_ACTION_WRITE_FILE;\n        if (ngx_http_arg(r, (u_char *) \"suffix\", 6, &arg_value) == NGX_OK) {\n            ctx->file_suffix = arg_value;\n        }\n\n        if (ngx_http_arg(r, (u_char *) \"simple_name\", 11, &arg_value)\n            == NGX_OK)\n        {\n            if (arg_value.len != 1) {\n                return NGX_HTTP_BAD_REQUEST;\n            }\n            ctx->simple_name = ngx_atoi(arg_value.data, arg_value.len);\n            if (ctx->simple_name == NGX_ERROR\n                || (ctx->simple_name != NGX_HTTP_TFS_NO\n                    && ctx->simple_name != NGX_HTTP_TFS_YES))\n            {\n                return NGX_HTTP_BAD_REQUEST;\n            }\n        }\n\n        if (ngx_http_arg(r, (u_char *) \"large_file\", 10, &arg_value)\n            == NGX_OK)\n        {\n            if (arg_value.len != 1) {\n                return NGX_HTTP_BAD_REQUEST;\n            }\n            ctx->large_file = ngx_atoi(arg_value.data, arg_value.len);\n            if (ctx->large_file == NGX_ERROR\n                || (ctx->large_file != NGX_HTTP_TFS_NO\n                    && ctx->large_file != NGX_HTTP_TFS_YES))\n            {\n                return NGX_HTTP_BAD_REQUEST;\n            }\n        }\n\n        if (ngx_http_arg(r, (u_char *) \"meta_segment\", 12, &arg_value)\n            == NGX_OK)\n        {\n            if (arg_value.len != 1) {\n                return NGX_HTTP_BAD_REQUEST;\n            }\n            ctx->write_meta_segment = ngx_atoi(arg_value.data, arg_value.len);\n            if (ctx->write_meta_segment == NGX_ERROR\n                || (ctx->write_meta_segment != NGX_HTTP_TFS_NO\n                    && ctx->write_meta_segment != NGX_HTTP_TFS_YES))\n            {\n                return NGX_HTTP_BAD_REQUEST;\n            }\n        }\n\n        if (ngx_http_arg(r, (u_char *) \"no_dedup\", 8, &arg_value) == NGX_OK) {\n            if (arg_value.len != 1) {\n                return NGX_HTTP_BAD_REQUEST;\n            }\n            ctx->no_dedup = ngx_atoi(arg_value.data, arg_value.len);\n            if (ctx->no_dedup == NGX_ERROR\n                || (ctx->no_dedup != NGX_HTTP_TFS_NO\n                    && ctx->no_dedup != NGX_HTTP_TFS_YES))\n            {\n                return NGX_HTTP_BAD_REQUEST;\n            }\n        }\n\n        ngx_str_set(&ctx->action.msg, \"write_file\");\n        break;\n\n    case NGX_HTTP_DELETE:\n        ctx->action.code = NGX_HTTP_TFS_ACTION_REMOVE_FILE;\n        ngx_str_set(&ctx->action.msg, \"remove_file\");\n\n        /* for outer user use */\n        if (ngx_http_arg(r, (u_char *) \"hide\", 4, &arg_value) == NGX_OK) {\n            if (arg_value.len != 1) {\n                return NGX_HTTP_BAD_REQUEST;\n            }\n            ctx->unlink_type = ngx_atoi(arg_value.data, arg_value.len);\n            /* hide(1) or reveal(0)*/\n            if (ctx->unlink_type == NGX_ERROR\n                || (ctx->unlink_type != 0 && ctx->unlink_type != 1))\n            {\n                return NGX_HTTP_BAD_REQUEST;\n            }\n            /* convert to actual type */\n            if (ctx->unlink_type == 1) {\n                ctx->unlink_type = NGX_HTTP_TFS_UNLINK_CONCEAL;\n\n            } else {\n                ctx->unlink_type = NGX_HTTP_TFS_UNLINK_REVEAL;\n            }\n        }\n\n        if (ngx_http_arg(r, (u_char *) \"type\", 4, &arg_value) == NGX_OK) {\n            if (arg_value.len != 1) {\n                return NGX_HTTP_BAD_REQUEST;\n            }\n            ctx->unlink_type = ngx_atoi(arg_value.data, arg_value.len);\n            /* del(0) or undel(2) or hide(4) or reveal(6)*/\n            if (ctx->unlink_type == NGX_ERROR\n                || (ctx->unlink_type != NGX_HTTP_TFS_UNLINK_DELETE\n                    && ctx->unlink_type != NGX_HTTP_TFS_UNLINK_UNDELETE\n                    && ctx->unlink_type != NGX_HTTP_TFS_UNLINK_CONCEAL\n                    && ctx->unlink_type != NGX_HTTP_TFS_UNLINK_REVEAL))\n            {\n                return NGX_HTTP_BAD_REQUEST;\n            }\n        }\n\n        if (ngx_http_arg(r, (u_char *) \"suffix\", 6, &arg_value) == NGX_OK) {\n            ctx->file_suffix = arg_value;\n        }\n\n        rc = ngx_http_tfs_raw_fsname_parse(&ctx->file_path_s, &ctx->file_suffix,\n                                           &ctx->fsname);\n        if (rc != NGX_OK) {\n            return NGX_HTTP_BAD_REQUEST;\n        }\n\n        /* large file not support UNDELETE */\n        if ((ctx->fsname.file_type == NGX_HTTP_TFS_LARGE_FILE_TYPE)\n            && ctx->unlink_type == NGX_HTTP_TFS_UNLINK_UNDELETE)\n        {\n            return NGX_HTTP_BAD_REQUEST;\n        }\n        break;\n\n    case NGX_HTTP_HEAD:\n        if (ngx_http_arg(r, (u_char *) \"suffix\", 6, &arg_value) == NGX_OK) {\n            ctx->file_suffix = arg_value;\n        }\n\n        rc = ngx_http_tfs_raw_fsname_parse(&ctx->file_path_s, &ctx->file_suffix,\n                                           &ctx->fsname);\n        if (rc != NGX_OK) {\n            return NGX_HTTP_BAD_REQUEST;\n        }\n\n        if (ngx_http_arg(r, (u_char *) \"type\", 4, &arg_value) == NGX_OK) {\n            if (arg_value.len != 1) {\n                return NGX_HTTP_BAD_REQUEST;\n            }\n            ctx->read_stat_type = ngx_atoi(arg_value.data, arg_value.len);\n            /* normal_read/stat(0) or force_read/stat(1) */\n            if (ctx->read_stat_type == NGX_ERROR\n                || (ctx->read_stat_type != NGX_HTTP_TFS_READ_STAT_NORMAL\n                    && ctx->read_stat_type != NGX_HTTP_TFS_READ_STAT_FORCE))\n            {\n                return NGX_HTTP_BAD_REQUEST;\n            }\n        }\n        ctx->action.code = NGX_HTTP_TFS_ACTION_STAT_FILE;\n        ngx_str_set(&ctx->action.msg, \"stat_file\");\n        ctx->chk_exist = NGX_HTTP_TFS_YES;\n        break;\n\n    case NGX_HTTP_PUT:\n        ctx->action.code = NGX_HTTP_TFS_ACTION_WRITE_FILE;\n        if (ngx_http_arg(r, (u_char *) \"suffix\", 6, &arg_value) == NGX_OK) {\n            ctx->file_suffix = arg_value;\n        }\n\n        if (ngx_http_arg(r, (u_char *) \"simple_name\", 11, &arg_value)\n            == NGX_OK)\n        {\n            if (arg_value.len != 1) {\n                return NGX_HTTP_BAD_REQUEST;\n            }\n            ctx->simple_name = ngx_atoi(arg_value.data, arg_value.len);\n            if (ctx->simple_name == NGX_ERROR\n                || (ctx->simple_name != NGX_HTTP_TFS_NO\n                    && ctx->simple_name != NGX_HTTP_TFS_YES))\n            {\n                return NGX_HTTP_BAD_REQUEST;\n            }\n        }\n\n        if (ctx->file_path_s.data == NULL) {\n            return NGX_HTTP_BAD_REQUEST;\n        }\n\n        /* large file not support update */\n        if (ngx_http_arg(r, (u_char *) \"large_file\", 10, &arg_value) == NGX_OK) {\n            return NGX_HTTP_BAD_REQUEST;\n        }\n\n        rc = ngx_http_tfs_raw_fsname_parse(&ctx->file_path_s, &ctx->file_suffix,\n                                           &ctx->fsname);\n        /* large file not support update */\n        if (rc != NGX_OK\n            || (ctx->fsname.file_type == NGX_HTTP_TFS_LARGE_FILE_TYPE))\n        {\n            return NGX_HTTP_BAD_REQUEST;\n        }\n\n        ctx->is_raw_update = NGX_HTTP_TFS_YES;\n        ngx_str_set(&ctx->action.msg, \"write_file\");\n        break;\n\n    default:\n        return NGX_HTTP_BAD_REQUEST;\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_restful_parse(ngx_http_request_t *r, ngx_http_tfs_restful_ctx_t *ctx)\n{\n    ngx_int_t  rc;\n\n    rc = ngx_http_restful_parse_uri(r, ctx);\n    if (rc == NGX_ERROR) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, \"parse uri failed\");\n        return NGX_HTTP_BAD_REQUEST;\n    }\n\n    if (ctx->version == 1) {\n        rc = ngx_http_restful_parse_action_raw(r, ctx);\n    }\n\n    if (ctx->version == 2) {\n        rc = ngx_http_restful_parse_action(r, ctx);\n    }\n\n    return rc;\n}\n"
  },
  {
    "path": "modules/ngx_http_tfs_module/ngx_http_tfs_restful.h",
    "content": "\n/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#ifndef _NGX_HTTP_TFS_RESTFUL_H_INCLUDED_\n#define _NGX_HTTP_TFS_RESTFUL_H_INCLUDED_\n\n\n#include <ngx_core.h>\n#include <ngx_http.h>\n#include <ngx_http_tfs_raw_fsname.h>\n\n\ntypedef struct {\n    ngx_str_t                    file_path_s;\n    ngx_str_t                    file_path_d;\n\n    ngx_str_t                    file_suffix;\n    ngx_http_tfs_raw_fsname_t    fsname;\n\n    ngx_str_t                    appkey;\n    uint64_t                     app_id;\n    uint64_t                     user_id;\n\n    /* action */\n    ngx_http_tfs_action_t        action;\n    int64_t                      offset;\n    uint64_t                     size;\n\n    ngx_int_t                    unlink_type;\n    ngx_int_t                    simple_name;\n    uint8_t                      version;\n    uint8_t                      file_type;\n    ngx_int_t                    large_file;\n\n    ngx_int_t                    read_stat_type;\n    ngx_int_t                    write_meta_segment;\n    ngx_int_t                    no_dedup;\n    ngx_int_t                    chk_file_hole;\n    ngx_int_t                    recursive;\n\n    unsigned                     meta:1;\n    unsigned                     get_appid:1;\n    unsigned                     chk_exist:1;\n    unsigned                     is_raw_update:1;\n} ngx_http_tfs_restful_ctx_t;\n\n\nngx_int_t ngx_http_restful_parse(ngx_http_request_t *r,\n    ngx_http_tfs_restful_ctx_t *ctx);\n\n\n#endif  /* _NGX_HTTP_TFS_RESTFUL_H_INCLUDED_ */\n"
  },
  {
    "path": "modules/ngx_http_tfs_module/ngx_http_tfs_root_server_message.c",
    "content": "\n/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#include <ngx_http_tfs_root_server_message.h>\n#include <zlib.h>\n\n\nngx_chain_t *\nngx_http_tfs_root_server_create_message(ngx_pool_t *pool)\n{\n    ngx_buf_t                  *b;\n    ngx_chain_t                *cl;\n    ngx_http_tfs_rs_request_t  *req;\n\n    b = ngx_create_temp_buf(pool, sizeof(ngx_http_tfs_rs_request_t));\n    if (b == NULL) {\n        return NULL;\n    }\n\n    req = (ngx_http_tfs_rs_request_t *) b->pos;\n    req->header.flag = NGX_HTTP_TFS_PACKET_FLAG;\n    req->header.len = sizeof(uint8_t);\n    req->header.type = NGX_HTTP_TFS_REQ_RT_GET_TABLE_MESSAGE;\n    req->header.version = NGX_HTTP_TFS_PACKET_VERSION;\n    req->header.crc = ngx_http_tfs_crc(NGX_HTTP_TFS_PACKET_FLAG,\n                                       (const char *) (&req->header + 1),\n                                       req->header.len);\n    req->header.id = ngx_http_tfs_generate_packet_id();\n\n    b->last += sizeof(ngx_http_tfs_rs_request_t);\n\n    cl = ngx_alloc_chain_link(pool);\n    if (cl == NULL) {\n        return NULL;\n    }\n\n    cl->buf = b;\n    cl->next = NULL;\n\n    return cl;\n}\n\n\nngx_int_t\nngx_http_tfs_root_server_parse_message(ngx_http_tfs_t *t)\n{\n    uLongf                           table_length;\n    ngx_int_t                        rc;\n    ngx_http_tfs_rs_response_t      *resp;\n    ngx_http_tfs_peer_connection_t  *tp;\n\n    tp = t->tfs_peer;\n    resp = (ngx_http_tfs_rs_response_t *) (tp->body_buffer.pos);\n    table_length = NGX_HTTP_TFS_METASERVER_COUNT * sizeof(uint64_t);\n\n    rc = uncompress((Bytef *) (t->loc_conf->meta_server_table.table),\n                    &table_length, resp->table, resp->length);\n    if (rc != Z_OK) {\n        ngx_log_error(NGX_LOG_ERR, t->log, errno, \"uncompress error\");\n        return NGX_ERROR;\n    }\n\n    t->loc_conf->meta_server_table.version = resp->version;\n\n    return NGX_OK;\n}\n\n\n"
  },
  {
    "path": "modules/ngx_http_tfs_module/ngx_http_tfs_root_server_message.h",
    "content": "\n/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#ifndef _NGX_HTTP_TFS_ROOT_SERVER_MESSAGE_H_INCLUDED_\n#define _NGX_HTTP_TFS_ROOT_SERVER_MESSAGE_H_INCLUDED_\n\n\n#include <ngx_http_tfs.h>\n\n\nngx_chain_t *ngx_http_tfs_root_server_create_message(ngx_pool_t *pool);\nngx_int_t ngx_http_tfs_root_server_parse_message(ngx_http_tfs_t *t);\n\n\n#endif  /* _NGX_HTTP_TFS_ROOT_SERVER_MESSAGE_H_INCLUDED_ */\n"
  },
  {
    "path": "modules/ngx_http_tfs_module/ngx_http_tfs_serialization.c",
    "content": "\n/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#include <ngx_http_tfs_serialization.h>\n\n\nngx_int_t\nngx_http_tfs_serialize_string(u_char **p,\n    ngx_str_t *string)\n{\n    if (p == NULL || *p == NULL || string == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (string->len == 0) {\n        *((uint32_t *)*p) = 0;\n\n    } else {\n        *((uint32_t *)*p) = string->len + 1;\n    }\n    *p += sizeof(uint32_t);\n\n    if (string->len > 0) {\n        ngx_memcpy(*p, string->data, string->len);\n        *p += string->len + 1;\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_tfs_deserialize_string(u_char **p, ngx_pool_t *pool,\n    ngx_str_t *string)\n{\n    if (p == NULL || *p == NULL || pool == NULL || string == NULL) {\n        return NGX_ERROR;\n    }\n\n    string->len = *((uint32_t *)*p);\n    (*p) += sizeof(uint32_t);\n\n    if (string->len > 0) {\n        /* this length includes '/0' */\n        string->len -= 1;\n        string->data = ngx_pcalloc(pool, string->len);\n        if (string->data == NULL) {\n            return NGX_ERROR;\n        }\n        ngx_memcpy(string->data, (*p), string->len);\n        (*p) += string->len + 1;\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_tfs_deserialize_vstring(u_char **p, ngx_pool_t *pool,\n    uint32_t *count, ngx_str_t **string)\n{\n    uint32_t   new_count, i;\n    ngx_int_t  rc;\n\n    /* count */\n    new_count = *((uint32_t *)*p);\n    (*p) += sizeof(uint32_t);\n\n    /* string */\n    if (new_count > 0) {\n        if (*string == NULL) {\n            *string = ngx_pcalloc(pool, sizeof(ngx_str_t) * new_count);\n            if (*string == NULL) {\n                return NGX_ERROR;\n            }\n\n        } else if (new_count > *count) {\n            *string = ngx_prealloc(pool, *string, sizeof(ngx_str_t) * (*count),\n                                   sizeof(ngx_str_t) * new_count);\n            if (*string == NULL) {\n                return NGX_ERROR;\n            }\n            ngx_memzero(*string, sizeof(ngx_str_t) * new_count);\n        }\n        for (i = 0; i < new_count; i++) {\n            rc = ngx_http_tfs_deserialize_string(p, pool, (*string) + i);\n            if (rc == NGX_ERROR) {\n                return NGX_ERROR;\n            }\n        }\n    }\n    *count = new_count;\n\n    return NGX_OK;\n}\n\n\n/*ngx_int_t\nngx_http_tfs_serialize_bucket_meta_info(u_char **p,\n    ngx_http_tfs_bucket_meta_info_t *bucket_meta_info)\n{\n    if (p == NULL || *p == NULL || bucket_meta_info == NULL) {\n        return NGX_ERROR;\n    }\n\n    *((uint32_t *)*p) = NGX_HTTP_TFS_BUCKET_META_INFO_CREATE_TIME_TAG;\n    (*p) += sizeof(uint32_t);\n\n    *((int64_t *)*p) = bucket_meta_info->create_time;\n    (*p) += sizeof(int64_t);\n\n    *((uint32_t *)*p) = NGX_HTTP_TFS_BUCKET_META_INFO_OWNER_ID_TAG;\n    (*p) += sizeof(uint32_t);\n\n    *((uint64_t *)*p) = bucket_meta_info->owner_id;\n    (*p) += sizeof(uint64_t);\n\n    *((uint32_t *)*p) = NGX_HTTP_TFS_END_TAG;\n    (*p) += sizeof(uint32_t);\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_tfs_deserialize_bucket_meta_info(u_char **p,\n    ngx_http_tfs_bucket_meta_info_t *bucket_meta_info)\n{\n    uint32_t   type_tag;\n    ngx_int_t  rc;\n\n    if (p == NULL || *p == NULL || bucket_meta_info == NULL) {\n        return NGX_ERROR;\n    }\n\n    rc = NGX_OK;\n    while (rc == NGX_OK) {\n        type_tag = *((uint32_t *)*p);\n        (*p) += sizeof(uint32_t);\n\n        switch (type_tag) {\n        case NGX_HTTP_TFS_BUCKET_META_INFO_CREATE_TIME_TAG:\n            bucket_meta_info->create_time = *((int64_t *)*p);\n            (*p) += sizeof(int64_t);\n            break;\n        case NGX_HTTP_TFS_BUCKET_META_INFO_OWNER_ID_TAG:\n            bucket_meta_info->owner_id = *((uint64_t *)*p);\n            (*p) += sizeof(uint64_t);\n            break;\n        case NGX_HTTP_TFS_END_TAG:\n            break;\n        default:\n            rc = NGX_ERROR;\n            break;\n        }\n\n        if (type_tag == NGX_HTTP_TFS_END_TAG) {\n            break;\n        }\n    }\n\n    return rc;\n}\n\n\nngx_int_t\nngx_http_tfs_serialize_object_meta_info(u_char **p,\n    ngx_http_tfs_object_meta_info_t *object_meta_info)\n{\n    if (p == NULL || *p == NULL || object_meta_info == NULL) {\n        return NGX_ERROR;\n    }\n\n    *((uint32_t *)*p) = NGX_HTTP_TFS_OBJECT_META_INFO_CREATE_TIME_TAG;\n    (*p) += sizeof(uint32_t);\n\n    *((int64_t *)*p) = object_meta_info->create_time;\n    (*p) += sizeof(int64_t);\n\n    *((uint32_t *)*p) = NGX_HTTP_TFS_OBJECT_META_INFO_MODIFY_TIME_TAG;\n    (*p) += sizeof(uint32_t);\n\n    *((int64_t *)*p) = object_meta_info->modify_time;\n    (*p) += sizeof(int64_t);\n\n    *((uint32_t *)*p) = NGX_HTTP_TFS_OBJECT_META_INFO_BIG_FILE_SIZE_TAG;\n    (*p) += sizeof(uint32_t);\n\n    *((uint64_t *)*p) = object_meta_info->big_file_size;\n    (*p) += sizeof(uint64_t);\n\n    *((uint32_t *)*p) = NGX_HTTP_TFS_OBJECT_META_INFO_MAX_TFS_FILE_SIZE_TAG;\n    (*p) += sizeof(uint32_t);\n\n    *((uint32_t *)*p) = object_meta_info->max_tfs_file_size;\n    (*p) += sizeof(uint32_t);\n\n    *((uint32_t *)*p) = NGX_HTTP_TFS_OBJECT_META_INFO_OWNER_ID_TAG;\n    (*p) += sizeof(uint32_t);\n\n    *((uint64_t *)*p) = object_meta_info->owner_id;\n    (*p) += sizeof(uint64_t);\n\n    *((uint32_t *)*p) = NGX_HTTP_TFS_END_TAG;\n    (*p) += sizeof(uint32_t);\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_tfs_deserialize_object_meta_info(u_char **p,\n    ngx_http_tfs_object_meta_info_t *object_meta_info)\n{\n    uint32_t   type_tag;\n    ngx_int_t  rc;\n\n    if (p == NULL || *p == NULL || object_meta_info == NULL) {\n        return NGX_ERROR;\n    }\n\n    rc = NGX_OK;\n    while (rc == NGX_OK) {\n        type_tag = *((uint32_t *)*p);\n        (*p) += sizeof(uint32_t);\n\n        switch (type_tag) {\n        case NGX_HTTP_TFS_OBJECT_META_INFO_CREATE_TIME_TAG:\n            object_meta_info->create_time = *((int64_t *)*p);\n            (*p) += sizeof(int64_t);\n            break;\n        case NGX_HTTP_TFS_OBJECT_META_INFO_MODIFY_TIME_TAG:\n            object_meta_info->modify_time = *((int64_t *)*p);\n            (*p) += sizeof(int64_t);\n            break;\n        case NGX_HTTP_TFS_OBJECT_META_INFO_MAX_TFS_FILE_SIZE_TAG:\n            object_meta_info->max_tfs_file_size = *((uint32_t *)*p);\n            (*p) += sizeof(uint32_t);\n            break;\n        case NGX_HTTP_TFS_OBJECT_META_INFO_BIG_FILE_SIZE_TAG:\n            object_meta_info->big_file_size = *((uint64_t *)*p);\n            (*p) += sizeof(uint64_t);\n            break;\n        case NGX_HTTP_TFS_OBJECT_META_INFO_OWNER_ID_TAG:\n            object_meta_info->owner_id = *((uint64_t *)*p);\n            (*p) += sizeof(uint64_t);\n            break;\n        case NGX_HTTP_TFS_END_TAG:\n            break;\n        default:\n            rc = NGX_ERROR;\n            break;\n        }\n\n        if (type_tag == NGX_HTTP_TFS_END_TAG) {\n            break;\n        }\n    }\n\n    return rc;\n}\n\n\nngx_int_t\nngx_http_tfs_serialize_tfs_file_info(u_char **p,\n    ngx_http_tfs_file_info_t *tfs_file_info)\n{\n    if (p == NULL || *p == NULL || tfs_file_info == NULL) {\n        return NGX_ERROR;\n    }\n\n    *((uint32_t *)*p) = NGX_HTTP_TFS_FILE_INFO_CLUSTER_ID_TAG;\n    (*p) += sizeof(uint32_t);\n\n    *((int32_t *)*p) = tfs_file_info->cluster_id;\n    (*p) += sizeof(int32_t);\n\n    *((uint32_t *)*p) = NGX_HTTP_TFS_FILE_INFO_BLOCK_ID_TAG;\n    (*p) += sizeof(uint32_t);\n\n    *((uint64_t *)*p) = tfs_file_info->block_id;\n    (*p) += sizeof(uint64_t);\n\n    *((uint32_t *)*p) = NGX_HTTP_TFS_FILE_INFO_FILE_ID_TAG;\n    (*p) += sizeof(uint32_t);\n\n    *((uint64_t *)*p) = tfs_file_info->file_id;\n    (*p) += sizeof(uint64_t);\n\n    *((uint32_t *)*p) = NGX_HTTP_TFS_FILE_INFO_OFFSET_TAG;\n    (*p) += sizeof(uint32_t);\n\n    *((int64_t *)*p) = tfs_file_info->offset;\n    (*p) += sizeof(int64_t);\n\n    *((uint32_t *)*p) = NGX_HTTP_TFS_FILE_INFO_FILE_SIZE_TAG;\n    (*p) += sizeof(uint32_t);\n\n    *((uint64_t *)*p) = tfs_file_info->size;\n    (*p) += sizeof(uint64_t);\n\n    *((uint32_t *)*p) = NGX_HTTP_TFS_END_TAG;\n    (*p) += sizeof(uint32_t);\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_tfs_deserialize_tfs_file_info(u_char **p,\n    ngx_http_tfs_file_info_t *tfs_file_info)\n{\n    uint32_t   type_tag;\n    ngx_int_t  rc;\n\n    if (p == NULL || *p == NULL || tfs_file_info == NULL) {\n        return NGX_ERROR;\n    }\n\n    rc = NGX_OK;\n    while (rc == NGX_OK) {\n        type_tag = *((uint32_t *)*p);\n        (*p) += sizeof(uint32_t);\n\n        switch (type_tag) {\n        case NGX_HTTP_TFS_FILE_INFO_CLUSTER_ID_TAG:\n            tfs_file_info->cluster_id = *((int32_t *)*p);\n            (*p) += sizeof(int32_t);\n            break;\n        case NGX_HTTP_TFS_FILE_INFO_BLOCK_ID_TAG:\n            tfs_file_info->block_id = *((uint64_t *)*p);\n            (*p) += sizeof(uint64_t);\n            break;\n        case NGX_HTTP_TFS_FILE_INFO_FILE_ID_TAG:\n            tfs_file_info->file_id = *((uint64_t *)*p);\n            (*p) += sizeof(uint64_t);\n            break;\n        case NGX_HTTP_TFS_FILE_INFO_OFFSET_TAG:\n            tfs_file_info->offset = *((int64_t *)*p);\n            (*p) += sizeof(int64_t);\n            break;\n        case NGX_HTTP_TFS_FILE_INFO_FILE_SIZE_TAG:\n            tfs_file_info->size = *((uint64_t *)*p);\n            (*p) += sizeof(uint64_t);\n            break;\n        case NGX_HTTP_TFS_END_TAG:\n            break;\n        default:\n            rc = NGX_ERROR;\n            break;\n        }\n\n        if (type_tag == NGX_HTTP_TFS_END_TAG) {\n            break;\n        }\n    }\n\n    return rc;\n}\n\n\nngx_int_t\nngx_http_tfs_serialize_customize_info(u_char **p,\n    ngx_http_tfs_customize_info_t *customize_info)\n{\n    if (p == NULL || *p == NULL || customize_info == NULL) {\n        return NGX_ERROR;\n    }\n\n    *((uint32_t *)*p) = NGX_HTTP_TFS_CUSTOMIZE_INFO_OTAG_TAG;\n    (*p) += sizeof(uint32_t);\n\n    *((uint32_t *)*p) = customize_info->otag_len + 1;\n    (*p) += sizeof(uint32_t);\n\n    ngx_memcpy(*p, customize_info->otag, customize_info->otag_len);\n    (*p) += customize_info->otag_len + 1;\n\n    *((uint32_t *)*p) = NGX_HTTP_TFS_END_TAG;\n    (*p) += sizeof(uint32_t);\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_tfs_deserialize_customize_info(u_char **p, ngx_pool_t *pool,\n    ngx_http_tfs_customize_info_t *customize_info)\n{\n    uint32_t   type_tag;\n    ngx_int_t  rc;\n\n    if (p == NULL || *p == NULL || pool == NULL || customize_info == NULL) {\n        return NGX_ERROR;\n    }\n\n    rc = NGX_OK;\n    while (rc == NGX_OK) {\n        type_tag = *((uint32_t *)*p);\n        (*p) += sizeof(uint32_t);\n\n        switch (type_tag) {\n        case NGX_HTTP_TFS_CUSTOMIZE_INFO_OTAG_TAG:\n            customize_info->otag_len = *((uint32_t *)*p) - 1;\n            (*p) += sizeof(uint32_t);\n\n            if (customize_info->otag_len > NGX_HTTP_TFS_MAX_CUSTOMIZE_INFO_SIZE) {\n                return NGX_ERROR;\n            }\n\n            customize_info->otag = ngx_pcalloc(pool, customize_info->otag_len);\n            if (customize_info->otag == NULL) {\n                return NGX_ERROR;\n            }\n\n            ngx_memcpy(customize_info->otag, *p, customize_info->otag_len);\n            (*p) += customize_info->otag_len + 1;\n\n            break;\n        case NGX_HTTP_TFS_END_TAG:\n            break;\n        default:\n            rc = NGX_ERROR;\n            break;\n        }\n\n        if (type_tag == NGX_HTTP_TFS_END_TAG) {\n            break;\n        }\n    }\n\n    return rc;\n}\n\n\nngx_int_t\nngx_http_tfs_serialize_object_info(u_char **p,\n    ngx_http_tfs_object_info_t *object_info)\n{\n    uint32_t  i;\n\n    if (p == NULL || *p == NULL || object_info == NULL) {\n        return NGX_ERROR;\n    }\n\n    *((uint32_t *)*p) = NGX_HTTP_TFS_OBJECT_INFO_V_TFS_FILE_INFO_TAG;\n    (*p) += sizeof(uint32_t);\n\n    *((uint32_t *)*p) = object_info->tfs_file_count;\n    (*p) += sizeof(uint32_t);\n\n    for (i = 0; i < object_info->tfs_file_count; i++) {\n        ngx_http_tfs_serialize_tfs_file_info(p, object_info->tfs_file_infos + i);\n    }\n\n    *((uint32_t *)*p) = NGX_HTTP_TFS_OBJECT_INFO_HAS_META_INFO_TAG;\n    (*p) += sizeof(uint32_t);\n\n    *((uint8_t *)*p) = object_info->has_meta_info;\n    (*p) += sizeof(uint8_t);\n\n    if (object_info->has_meta_info) {\n        *((uint32_t *)*p) = NGX_HTTP_TFS_OBJECT_INFO_META_INFO_TAG;\n        (*p) += sizeof(uint32_t);\n\n        ngx_http_tfs_serialize_object_meta_info(p, &object_info->meta_info);\n    }\n\n    *((uint32_t *)*p) = NGX_HTTP_TFS_OBJECT_INFO_HAS_CUSTOMIZE_INFO_TAG;\n    (*p) += sizeof(uint32_t);\n\n    *((uint8_t *)*p) = object_info->has_customize_info;\n    (*p) += sizeof(uint8_t);\n\n    if (object_info->has_customize_info) {\n        *((uint32_t *)*p) = NGX_HTTP_TFS_OBJECT_INFO_CUSTOMIZE_INFO_TAG;\n        (*p) += sizeof(uint32_t);\n\n        ngx_http_tfs_serialize_customize_info(p, &object_info->customize_info);\n    }\n\n    *((uint32_t *)*p) = NGX_HTTP_TFS_END_TAG;\n    (*p) += sizeof(uint32_t);\n\n    return NGX_OK;\n}*/\n\n\n//ngx_int_t\n//ngx_http_tfs_deserialize_object_info(u_char **p, ngx_pool_t *pool,\n//    ngx_http_tfs_object_info_t *object_info)\n//{\n//    uint32_t   type_tag, file_info_count, i;\n//    ngx_int_t  rc;\n//\n//    if (p == NULL || *p == NULL || pool == NULL || object_info == NULL) {\n//        return NGX_ERROR;\n//    }\n//\n//    rc = NGX_OK;\n//    while (rc == NGX_OK) {\n//        type_tag = *((uint32_t *)*p);\n//        (*p) += sizeof(uint32_t);\n//\n//        switch (type_tag) {\n//        case NGX_HTTP_TFS_OBJECT_INFO_V_TFS_FILE_INFO_TAG:\n//            file_info_count = *((uint32_t *)*p);\n//            (*p) += sizeof(uint32_t);\n//            if (file_info_count > 0) {\n//                if (object_info->tfs_file_infos == NULL) {\n//                    object_info->tfs_file_infos = ngx_pcalloc(pool,\n//                        sizeof(ngx_http_tfs_file_info_t) * file_info_count);\n//                    if (object_info->tfs_file_infos == NULL) {\n//                        return NGX_ERROR;\n//                    }\n//\n//                } else {\n//                    /* need realloc */\n//                    if (file_info_count > object_info->tfs_file_count) {\n//                        object_info->tfs_file_infos = ngx_prealloc(pool,\n//                            object_info->tfs_file_infos,\n//                            sizeof(ngx_http_tfs_file_info_t) * object_info->tfs_file_count,\n//                            sizeof(ngx_http_tfs_file_info_t) * file_info_count);\n//                        if (object_info->tfs_file_infos == NULL) {\n//                            return NGX_ERROR;\n//                        }\n//                    }\n//                    /* reuse */\n//                    ngx_memzero(object_info->tfs_file_infos,\n//                        sizeof(ngx_http_tfs_file_info_t) * file_info_count);\n//                }\n//                object_info->tfs_file_count = file_info_count;\n//                for (i = 0; i < file_info_count; i++) {\n//                    rc = ngx_http_tfs_deserialize_tfs_file_info(p, object_info->tfs_file_infos + i);\n//                    if (rc == NGX_ERROR) {\n//                        return NGX_ERROR;\n//                    }\n//                }\n//            }\n//            break;\n//        case NGX_HTTP_TFS_OBJECT_INFO_HAS_META_INFO_TAG:\n//            object_info->has_meta_info = *((uint8_t *)*p);\n//            (*p) += sizeof(uint8_t);\n//            break;\n//        case NGX_HTTP_TFS_OBJECT_INFO_META_INFO_TAG:\n//            rc = ngx_http_tfs_deserialize_object_meta_info(p, &object_info->meta_info);\n//            if (rc == NGX_ERROR) {\n//                return NGX_ERROR;\n//            }\n//            break;\n//        case NGX_HTTP_TFS_OBJECT_INFO_HAS_CUSTOMIZE_INFO_TAG:\n//            object_info->has_customize_info = *((uint8_t *)*p);\n//            (*p) += sizeof(uint8_t);\n//            break;\n//        case NGX_HTTP_TFS_OBJECT_INFO_CUSTOMIZE_INFO_TAG:\n//            rc = ngx_http_tfs_deserialize_customize_info(p, pool, &object_info->customize_info);\n//            if (rc == NGX_ERROR) {\n//                return NGX_ERROR;\n//            }\n//            break;\n//        case NGX_HTTP_TFS_END_TAG:\n//            break;\n//        default:\n//            rc = NGX_ERROR;\n//            break;\n//        }\n//\n//        if (type_tag == NGX_HTTP_TFS_END_TAG) {\n//            break;\n//        }\n//    }\n//\n//    return rc;\n//}\n//\n//\n//ngx_int_t\n//ngx_http_tfs_serialize_user_info(u_char **p,\n//    ngx_http_tfs_user_info_t *user_info)\n//{\n//    if (p == NULL || *p == NULL || user_info == NULL) {\n//        return NGX_ERROR;\n//    }\n//\n//    *((uint32_t *)*p) = NGX_HTTP_TFS_USER_INFO_OWNER_ID_TAG;\n//    (*p) += sizeof(uint32_t);\n//\n//    *((uint64_t *)*p) = user_info->owner_id;\n//    (*p) += sizeof(uint64_t);\n//\n//    *((uint32_t *)*p) = NGX_HTTP_TFS_END_TAG;\n//    (*p) += sizeof(uint32_t);\n//\n//    return NGX_OK;\n//}\n//\n//\n//ngx_int_t\n//ngx_http_tfs_deserialize_kv_meta_table(u_char **p,\n//    ngx_http_tfs_kv_meta_table_t *kv_meta_table)\n//{\n//    uint32_t   type_tag, table_size, i;\n//    ngx_int_t  rc;\n//\n//    if (p == NULL || *p == NULL || kv_meta_table == NULL) {\n//        return NGX_ERROR;\n//    }\n//\n//    rc = NGX_OK;\n//    while (rc == NGX_OK) {\n//        type_tag = *((uint32_t *)*p);\n//        (*p) += sizeof(uint32_t);\n//\n//        switch (type_tag) {\n//        case NGX_HTTP_TFS_KV_META_TABLE_V_META_TABLE_TAG:\n//            table_size = *((uint32_t *)*p);\n//            (*p) += sizeof(uint32_t);\n//            if (table_size == 0) {\n//                return NGX_ERROR;\n//            }\n//\n//            for (i = 0; i < table_size; i++) {\n//                *(uint64_t *)(&kv_meta_table->table[i]) = *((uint64_t *)*p);\n//                (*p) += sizeof(uint64_t);\n//            }\n//            kv_meta_table->size = table_size;\n//            break;\n//        case NGX_HTTP_TFS_END_TAG:\n//            break;\n//        default:\n//            rc = NGX_ERROR;\n//            break;\n//        }\n//\n//        if (type_tag == NGX_HTTP_TFS_END_TAG) {\n//            break;\n//        }\n//    }\n//\n//    return rc;\n//}\n\n\nngx_int_t\nngx_http_tfs_serialize_rcs_stat(u_char **p,\n    ngx_http_tfs_rcs_info_t  *rc_info, ngx_int_t *count)\n{\n    ngx_int_t                    i;\n    ngx_http_tfs_stat_rcs_t     *stat_rcs;\n\n    if (p == NULL || rc_info == NULL || count == NULL) {\n        return NGX_ERROR;\n    }\n\n    *count = 0;\n    stat_rcs = rc_info->stat_rcs;\n\n    for (i = 0; i < NGX_HTTP_TFS_OPER_COUNT; ++i) {\n        if (stat_rcs[i].oper_app_id == 0) {\n            continue;\n        }\n\n        *((uint32_t *)*p) = (stat_rcs[i].oper_app_id << 16) | stat_rcs[i].oper_type;\n        (*p) += sizeof(uint32_t);\n\n        *((uint32_t *)*p) = (stat_rcs[i].oper_app_id << 16) | stat_rcs[i].oper_type;\n        (*p) += sizeof(uint32_t);\n        *((uint64_t *)*p) = stat_rcs[i].oper_times;\n        (*p) += sizeof(uint64_t);\n        *((uint64_t *)*p) = stat_rcs[i].oper_size;\n        (*p) += sizeof(uint64_t);\n        *((uint64_t *)*p) = stat_rcs[i].oper_rt;\n        (*p) += sizeof(uint64_t);\n        *((uint64_t *)*p) = stat_rcs[i].oper_succ;\n        (*p) += sizeof(uint64_t);\n\n        ++(*count);\n    }\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "modules/ngx_http_tfs_module/ngx_http_tfs_serialization.h",
    "content": "\n/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#ifndef _NGX_HTTP_TFS_SERIALIZATION_H_INCLUDED_\n#define _NGX_HTTP_TFS_SERIALIZATION_H_INCLUDED_\n\n\n#include <ngx_tfs_common.h>\n#include <ngx_http_tfs_rc_server_info.h>\n\nngx_int_t ngx_http_tfs_serialize_string(u_char **p, ngx_str_t *string);\n\nngx_int_t ngx_http_tfs_deserialize_string(u_char **p, ngx_pool_t *pool,\n    ngx_str_t *string);\n\n//ngx_int_t ngx_http_tfs_serialize_vstring(u_char **p, ngx_str_t *string);\n\nngx_int_t ngx_http_tfs_deserialize_vstring(u_char **p, ngx_pool_t *pool,\n    uint32_t *count, ngx_str_t **string);\n\n// TODO:\n//ngx_int_t ngx_http_tfs_serialize_bucket_meta_info(u_char **p,\n//    ngx_http_tfs_bucket_meta_info_t *bucket_meta_info);\n//\n//ngx_int_t ngx_http_tfs_deserialize_bucket_meta_info(u_char **p,\n//    ngx_http_tfs_bucket_meta_info_t *bucket_meta_info);\n//\n//ngx_int_t ngx_http_tfs_serialize_object_meta_info(u_char **p,\n//    ngx_http_tfs_object_meta_info_t *object_meta_info);\n//\n//ngx_int_t ngx_http_tfs_deserialize_object_meta_info(u_char **p,\n//    ngx_http_tfs_object_meta_info_t *object_meta_info);\n//\n//ngx_int_t ngx_http_tfs_serialize_tfs_file_info(u_char **p,\n//    ngx_http_tfs_file_info_t *tfs_file_info);\n//\n//ngx_int_t ngx_http_tfs_deserialize_tfs_file_info(u_char **p,\n//    ngx_http_tfs_file_info_t *tfs_file_info);\n//\n//ngx_int_t ngx_http_tfs_serialize_customize_info(u_char **p,\n//    ngx_http_tfs_customize_info_t *customize_info);\n//\n//ngx_int_t ngx_http_tfs_deserialize_customize_info(u_char **p, ngx_pool_t *pool,\n//    ngx_http_tfs_customize_info_t *customize_info);\n//\n//ngx_int_t ngx_http_tfs_serialize_object_info(u_char **p,\n//    ngx_http_tfs_object_info_t *object_info);\n//\n//ngx_int_t ngx_http_tfs_deserialize_object_info(u_char **p, ngx_pool_t *pool,\n//    ngx_http_tfs_object_info_t *object_info);\n//\n//ngx_int_t ngx_http_tfs_serialize_user_info(u_char **p,\n//    ngx_http_tfs_user_info_t *user_info);\n//\n//ngx_int_t ngx_http_tfs_deserialize_kv_meta_table(u_char **p,\n//    ngx_http_tfs_kv_meta_table_t *kv_meta_table);\n\nngx_int_t ngx_http_tfs_serialize_rcs_stat(u_char **p,\n    ngx_http_tfs_rcs_info_t  *rc_info, ngx_int_t *count);\n\n#endif   /* _NGX_HTTP_TFS_SERIALIZATION_H_INCLUDED_ */\n"
  },
  {
    "path": "modules/ngx_http_tfs_module/ngx_http_tfs_server_handler.c",
    "content": "\n/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#include <ngx_http_tfs_errno.h>\n#include <ngx_http_tfs_duplicate.h>\n#include <ngx_http_tfs_server_handler.h>\n#include <ngx_http_tfs_root_server_message.h>\n#include <ngx_http_tfs_meta_server_message.h>\n#include <ngx_http_tfs_rc_server_message.h>\n#include <ngx_http_tfs_name_server_message.h>\n#include <ngx_http_tfs_data_server_message.h>\n#include <ngx_http_tfs_remote_block_cache.h>\n\n\nngx_int_t\nngx_http_tfs_create_rs_request(ngx_http_tfs_t *t)\n{\n    ngx_chain_t  *cl;\n\n    cl = ngx_http_tfs_root_server_create_message(t->pool);\n    if (cl == NULL) {\n        return NGX_ERROR;\n    }\n\n    t->request_bufs = cl;\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_tfs_process_rs(ngx_http_tfs_t *t)\n{\n    ngx_int_t                        rc;\n    ngx_buf_t                       *b;\n    ngx_http_tfs_inet_t             *addr;\n    ngx_http_tfs_header_t           *header;\n    ngx_http_tfs_peer_connection_t  *tp;\n\n    header = (ngx_http_tfs_header_t *) t->header;\n    tp = t->tfs_peer;\n    b = &tp->body_buffer;\n\n    if (ngx_buf_size(b) < header->len) {\n        return NGX_AGAIN;\n    }\n\n    rc = ngx_http_tfs_root_server_parse_message(t);\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    t->state += 1;\n\n    ngx_http_tfs_set_custom_initial_parameters(t);\n\n    addr = ngx_http_tfs_select_meta_server(t);\n    ngx_http_tfs_peer_set_addr(t->pool,\n                               &t->tfs_peer_servers[NGX_HTTP_TFS_META_SERVER],\n                               addr);\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_tfs_create_ms_request(ngx_http_tfs_t *t)\n{\n    ngx_chain_t                              *cl;\n\n    cl = ngx_http_tfs_meta_server_create_message(t);\n    if (cl == NULL) {\n        return NGX_ERROR;\n    }\n\n    t->request_bufs = cl;\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_tfs_process_ms(ngx_http_tfs_t *t)\n{\n    ngx_buf_t                        *b;\n    ngx_int_t                         rc, dir_levels, parent_dir_len;\n    ngx_chain_t                      *cl, **ll;\n    ngx_http_tfs_header_t            *header;\n    ngx_http_tfs_peer_connection_t   *tp;\n    ngx_http_tfs_logical_cluster_t   *logical_cluster;\n    ngx_http_tfs_physical_cluster_t  *physical_cluster;\n\n    header = (ngx_http_tfs_header_t *) t->header;\n    tp = t->tfs_peer;\n    b = &tp->body_buffer;\n\n    if (ngx_buf_size(b) < header->len) {\n        return NGX_AGAIN;\n    }\n\n    rc = ngx_http_tfs_meta_server_parse_message(t);\n    if (rc == NGX_ERROR) {\n        return NGX_ERROR;\n    }\n\n    b->pos += header->len;\n\n    /* need update meta table */\n    if (rc == NGX_HTTP_TFS_EXIT_LEASE_EXPIRED\n        || rc == NGX_HTTP_TFS_EXIT_TABLE_VERSION_ERROR)\n    {\n        t->state = NGX_HTTP_TFS_STATE_ACTION_GET_META_TABLE;\n        ngx_http_tfs_clear_buf(b);\n\n        ngx_http_tfs_peer_set_addr(t->pool,\n                         &t->tfs_peer_servers[NGX_HTTP_TFS_ROOT_SERVER],\n                         (ngx_http_tfs_inet_t *)&t->loc_conf->meta_root_server);\n\n        ngx_log_error(NGX_LOG_DEBUG, t->log, 0,\n                      \"need update meta table, rc: %i\", rc);\n\n        return NGX_OK;\n    }\n\n    /* parent dir not exist, recursive create them */\n    if (rc == NGX_HTTP_TFS_EXIT_PARENT_EXIST_ERROR && t->r_ctx.recursive) {\n        switch (t->r_ctx.action.code) {\n        case NGX_HTTP_TFS_ACTION_CREATE_DIR:\n        case NGX_HTTP_TFS_ACTION_CREATE_FILE:\n        case NGX_HTTP_TFS_ACTION_MOVE_DIR:\n        case NGX_HTTP_TFS_ACTION_MOVE_FILE:\n            if (t->dir_lens == NULL) {\n                parent_dir_len = ngx_http_tfs_get_parent_dir(&t->last_file_path,\n                                                             &dir_levels);\n                t->dir_lens = ngx_pcalloc(t->pool,\n                                          sizeof(ngx_int_t) * dir_levels);\n                if (t->dir_lens == NULL) {\n                    return NGX_ERROR;\n                }\n                t->last_dir_level = 0;\n                t->dir_lens[0] = t->last_file_path.len;\n\n            } else {\n                parent_dir_len = ngx_http_tfs_get_parent_dir(&t->last_file_path,\n                                                             NULL);\n            }\n            t->last_dir_level++;\n            t->dir_lens[t->last_dir_level] = parent_dir_len;\n            t->last_file_path.len = t->dir_lens[t->last_dir_level];\n            t->orig_action = t->r_ctx.action.code;\n            /* temporarily modify */\n            t->r_ctx.action.code = NGX_HTTP_TFS_ACTION_CREATE_DIR;\n            return NGX_OK;\n        }\n    }\n\n    /* parent dir may be created by others\n     * during the recursive creating process\n     */\n    if (rc == NGX_HTTP_TFS_EXIT_TARGET_EXIST_ERROR && t->last_dir_level > 0) {\n        rc = NGX_OK;\n    }\n\n    if (rc == NGX_OK || rc == NGX_DECLINED) {\n        switch (t->r_ctx.action.code) {\n        case NGX_HTTP_TFS_ACTION_CREATE_DIR:\n        case NGX_HTTP_TFS_ACTION_CREATE_FILE:\n        case NGX_HTTP_TFS_ACTION_MOVE_DIR:\n        case NGX_HTTP_TFS_ACTION_MOVE_FILE:\n            if (t->dir_lens != NULL) {\n                if (t->last_dir_level > 0) {\n                    if (t->last_dir_level == 1) {\n                        t->r_ctx.action.code = t->orig_action;\n                    }\n                    t->last_file_path.len = t->dir_lens[--(t->last_dir_level)];\n                    return NGX_OK;\n                }\n            }\n        case NGX_HTTP_TFS_ACTION_REMOVE_DIR:\n            t->state = NGX_HTTP_TFS_STATE_ACTION_DONE;\n            return NGX_DONE;\n\n        case NGX_HTTP_TFS_ACTION_REMOVE_FILE:\n            switch (t->state) {\n            case NGX_HTTP_TFS_STATE_REMOVE_GET_FRAG_INFO:\n                if (rc == NGX_DECLINED) {\n                    t->state = NGX_HTTP_TFS_STATE_REMOVE_NOTIFY_MS;\n                    ngx_http_tfs_clear_buf(b);\n                    return NGX_OK;\n                }\n                t->state = NGX_HTTP_TFS_STATE_REMOVE_GET_BLK_INFO;\n                break;\n            case NGX_HTTP_TFS_STATE_REMOVE_NOTIFY_MS:\n                t->state = NGX_HTTP_TFS_STATE_REMOVE_DONE;\n                return NGX_DONE;\n            }\n            break;\n        case NGX_HTTP_TFS_ACTION_LS_FILE:\n            if (t->r_ctx.chk_exist == NGX_HTTP_TFS_NO && t->meta_info.file_count > 0) {\n                /* need json output */\n                for (cl = t->out_bufs, ll = &t->out_bufs; cl; cl = cl->next) {\n                    ll = &cl->next;\n                }\n\n                cl = ngx_http_tfs_json_custom_file_info(t->json_output,\n                                                        &t->meta_info,\n                                                        t->r_ctx.file_type);\n                if (cl == NULL) {\n                    return NGX_ERROR;\n                }\n\n                *ll = cl;\n            }\n\n            t->state = NGX_HTTP_TFS_STATE_ACTION_DONE;\n            return NGX_DONE;\n\n        case NGX_HTTP_TFS_ACTION_READ_FILE:\n            if (rc == NGX_DECLINED\n                || (t->r_ctx.chk_file_hole && !t->file.still_have))\n            {\n                if (t->r_ctx.chk_file_hole) {\n                    /* need json output */\n                    if (t->file_holes.nelts > 0) {\n                        t->json_output = ngx_http_tfs_json_init(t->log,\n                                                                t->pool);\n                        if (t->json_output == NULL) {\n                            return NGX_ERROR;\n                        }\n\n                        for (cl = t->out_bufs, ll = &t->out_bufs;\n                             cl;\n                             cl = cl->next)\n                        {\n                            ll = &cl->next;\n                        }\n\n                        cl = ngx_http_tfs_json_file_hole_info(t->json_output,\n                                                              &t->file_holes);\n                        if (cl == NULL) {\n                            return NGX_ERROR;\n                        }\n\n                        *ll = cl;\n                    }\n\n                }\n                t->state = NGX_HTTP_TFS_STATE_READ_DONE;\n                return NGX_DONE;\n            }\n\n            if (t->r_ctx.chk_file_hole) {\n                ngx_http_tfs_clear_buf(b);\n                return NGX_OK;\n            }\n\n            t->state = NGX_HTTP_TFS_STATE_READ_GET_BLK_INFO;\n            break;\n\n        case NGX_HTTP_TFS_ACTION_WRITE_FILE:\n            switch (t->state) {\n            case NGX_HTTP_TFS_STATE_WRITE_CLUSTER_ID_MS:\n                t->state = NGX_HTTP_TFS_STATE_WRITE_CLUSTER_ID_NS;\n                break;\n\n            case NGX_HTTP_TFS_STATE_WRITE_WRITE_MS:\n                if (t->file.left_length == 0) {\n                    t->state = NGX_HTTP_TFS_STATE_WRITE_DONE;\n                    return NGX_DONE;\n                }\n                t->state = NGX_HTTP_TFS_STATE_WRITE_GET_BLK_INFO;\n            }\n            break;\n        default:\n            return NGX_ERROR;\n        }\n\n        /* NGX_OK */\n\n        /* only select once */\n        if (t->name_server_addr_text.len == 0) {\n            rc = ngx_http_tfs_select_name_server(t, t->rc_info_node,\n                                                 &t->name_server_addr,\n                                                 &t->name_server_addr_text);\n            if (rc == NGX_ERROR) {\n                /* in order to return 404 */\n                return NGX_HTTP_TFS_EXIT_SERVER_OBJECT_NOT_FOUND;\n            }\n\n            ngx_http_tfs_peer_set_addr(t->pool,\n                                 &t->tfs_peer_servers[NGX_HTTP_TFS_NAME_SERVER],\n                                 &t->name_server_addr);\n\n            /* skip get cluster id from ns */\n            if (t->r_ctx.action.code == NGX_HTTP_TFS_ACTION_WRITE_FILE\n                && t->state == NGX_HTTP_TFS_STATE_WRITE_CLUSTER_ID_NS)\n            {\n                logical_cluster =\n                   &t->rc_info_node->logical_clusters[t->logical_cluster_index];\n                physical_cluster =\n                    &logical_cluster->rw_clusters[t->rw_cluster_index];\n                if (physical_cluster->cluster_id > 0) {\n                    if (t->file.cluster_id == 0) {\n                        t->file.cluster_id = physical_cluster->cluster_id;\n\n                    } else if (t->file.cluster_id\n                               != physical_cluster->cluster_id)\n                    {\n                        ngx_log_error(NGX_LOG_ERR, t->log, 0,\n                                      \"error, cluster id conflict: \"\n                                      \"%uD(ns) <> %uD(ms)\",\n                                      physical_cluster->cluster_id,\n                                      t->file.cluster_id);\n                        return NGX_ERROR;\n                    }\n                    t->state = NGX_HTTP_TFS_STATE_WRITE_GET_BLK_INFO;\n                }\n            }\n        }\n\n        if (t->r_ctx.action.code == NGX_HTTP_TFS_ACTION_READ_FILE) {\n            /* lookup block cache */\n            t->block_cache_ctx.curr_lookup_cache =\n                NGX_HTTP_TFS_LOCAL_BLOCK_CACHE;\n            t->decline_handler = ngx_http_tfs_batch_lookup_block_cache;\n            return NGX_DECLINED;\n        }\n\n        return NGX_OK;\n    }\n\n    return rc;\n}\n\n\nngx_int_t\nngx_http_tfs_process_ms_ls_dir(ngx_http_tfs_t *t)\n{\n    ngx_buf_t                        *b;\n    ngx_int_t                         rc;\n    ngx_chain_t                      *cl, **ll;\n    ngx_http_tfs_ms_ls_response_t    *fake_rsp;\n    ngx_http_tfs_peer_connection_t   *tps;\n    ngx_http_tfs_peer_connection_t   *tp;\n    ngx_http_tfs_custom_meta_info_t  *meta_info;\n\n    tp = t->tfs_peer;\n    b = &tp->body_buffer;\n    tps = t->tfs_peer_servers;\n\n    if (t->length != ngx_buf_size(b) && b->last != b->end) {\n        return NGX_AGAIN;\n    }\n\n    rc = ngx_http_tfs_meta_server_parse_message(t);\n    if (rc == NGX_ERROR) {\n        return NGX_ERROR;\n    }\n\n    /* need update meta table */\n    if (rc == NGX_HTTP_TFS_EXIT_LEASE_EXPIRED\n        || rc == NGX_HTTP_TFS_EXIT_TABLE_VERSION_ERROR)\n    {\n        t->state = NGX_HTTP_TFS_STATE_ACTION_GET_META_TABLE;\n        ngx_http_tfs_clear_buf(b);\n\n        ngx_http_tfs_peer_set_addr(t->pool,\n                                   &tps[NGX_HTTP_TFS_ROOT_SERVER],\n                                   (ngx_http_tfs_inet_t *)\n                                    &t->loc_conf->meta_root_server);\n\n        ngx_log_error(NGX_LOG_DEBUG, t->log, 0,\n                      \"need update meta table, rc: %i\", rc);\n\n        return NGX_OK;\n    }\n\n    if (rc == NGX_OK) {\n        if (t->length == 0) {\n            if (!t->r_ctx.chk_exist) {\n                if (t->file.still_have) {\n                    ngx_http_tfs_clear_buf(b);\n                    return NGX_OK;\n                }\n\n                if (t->meta_info.file_count > 0) {\n                    /* need json output */\n                    for (cl = t->out_bufs, ll = &t->out_bufs; cl; cl = cl->next)\n                    {\n                        ll = &cl->next;\n                    }\n\n                    cl = ngx_http_tfs_json_custom_file_info(t->json_output,\n                                                            &t->meta_info,\n                                                            t->r_ctx.file_type);\n                    if (cl == NULL) {\n                        return NGX_ERROR;\n                    }\n\n                    *ll = cl;\n                }\n            }\n\n            t->state = NGX_HTTP_TFS_STATE_ACTION_DONE;\n            return NGX_DONE;\n        }\n\n        /* t->length > 0 */\n        /* find current meta_info */\n        for(meta_info = &t->meta_info;\n            meta_info->next;\n            meta_info = meta_info->next);\n\n        if (meta_info->rest_file_count > 0) {\n            /* fake next ls_dir response head */\n            fake_rsp = (ngx_http_tfs_ms_ls_response_t *) b->start;\n            fake_rsp->still_have = 1;\n            fake_rsp->count = meta_info->rest_file_count;\n            b->last =\n                ngx_movemem(b->start + sizeof(ngx_http_tfs_ms_ls_response_t),\n                            b->pos, ngx_buf_size(b));\n            b->pos = b->start;\n            /* FIXME: fake len will be minus later, ugly */\n            t->length += ngx_buf_size(b);\n            return NGX_AGAIN;\n        }\n\n    }\n\n    return rc;\n}\n\n\nngx_int_t\nngx_http_tfs_create_rcs_request(ngx_http_tfs_t *t)\n{\n    ngx_chain_t  *cl;\n\n    cl = ngx_http_tfs_rc_server_create_message(t);\n    if (cl == NULL) {\n        return NGX_ERROR;\n    }\n\n    t->request_bufs = cl;\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_tfs_process_rcs(ngx_http_tfs_t *t)\n{\n    ngx_buf_t                       *b;\n    ngx_int_t                        rc;\n    ngx_http_tfs_rc_ctx_t           *rc_ctx;\n    ngx_http_tfs_rcs_info_t         *rc_info;\n    ngx_http_tfs_peer_connection_t  *tp;\n\n    tp = t->tfs_peer;\n    b = &tp->body_buffer;\n    rc_ctx = t->loc_conf->upstream->rc_ctx;\n\n    rc = ngx_http_tfs_rc_server_parse_message(t);\n\n    ngx_http_tfs_clear_buf(b);\n\n    if (t->r_ctx.action.code == NGX_HTTP_TFS_ACTION_KEEPALIVE) {\n        if (t->curr_ka_queue == ngx_queue_sentinel(&rc_ctx->sh->kp_queue)) {\n            rc = NGX_DONE;\n        }\n\n        return rc;\n    }\n\n    if (rc == NGX_ERROR || rc <= NGX_HTTP_TFS_EXIT_GENERAL_ERROR) {\n        return rc;\n    }\n\n    rc_info = t->rc_info_node;\n\n    if (t->r_ctx.action.code == NGX_HTTP_TFS_ACTION_GET_APPID) {\n        rc = ngx_http_tfs_set_output_appid(t, rc_info->app_id);\n        if (rc == NGX_ERROR) {\n            return NGX_ERROR;\n        }\n\n        return NGX_DONE;\n    }\n\n    /* TODO: use fine granularity mutex(per rc_info_node mutex) */\n    rc = ngx_http_tfs_misc_ctx_init(t, rc_info);\n\n    return rc;\n}\n\n\nngx_int_t\nngx_http_tfs_create_ns_request(ngx_http_tfs_t *t)\n{\n    ngx_chain_t                              *cl;\n\n    cl = ngx_http_tfs_name_server_create_message(t);\n    if (cl == NULL) {\n        return NGX_ERROR;\n    }\n\n    t->request_bufs = cl;\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_tfs_process_ns(ngx_http_tfs_t *t)\n{\n    uint32_t                          cluster_id;\n    ngx_buf_t                        *b;\n    ngx_int_t                         rc;\n    ngx_str_t                        *cluster_id_text;\n    ngx_http_tfs_inet_t              *addr;\n    ngx_http_tfs_header_t            *header;\n    ngx_http_tfs_rcs_info_t          *rc_info;\n    ngx_http_tfs_peer_connection_t   *tp;\n    ngx_http_tfs_logical_cluster_t   *logical_cluster;\n    ngx_http_tfs_physical_cluster_t  *physical_cluster;\n\n    header = (ngx_http_tfs_header_t *) t->header;\n    tp = t->tfs_peer;\n    b = &tp->body_buffer;\n\n    if (ngx_buf_size(b) < header->len) {\n        return NGX_AGAIN;\n    }\n\n    rc = ngx_http_tfs_name_server_parse_message(t);\n\n    ngx_http_tfs_clear_buf(b);\n    if (rc == NGX_ERROR) {\n        return rc;\n    }\n\n    if (rc <= NGX_HTTP_TFS_EXIT_GENERAL_ERROR) {\n        return NGX_HTTP_TFS_AGAIN;\n    }\n\n    switch (t->r_ctx.action.code) {\n    case NGX_HTTP_TFS_ACTION_STAT_FILE:\n        t->state = NGX_HTTP_TFS_STATE_STAT_STAT_FILE;\n        break;\n    case NGX_HTTP_TFS_ACTION_READ_FILE:\n        if (!t->parent\n            && (t->r_ctx.version == 2\n                || (t->is_large_file && !t->is_process_meta_seg)))\n        {\n            t->decline_handler = ngx_http_tfs_batch_process_start;\n            return NGX_DECLINED;\n        }\n        t->state = NGX_HTTP_TFS_STATE_READ_READ_DATA;\n        break;\n    case NGX_HTTP_TFS_ACTION_WRITE_FILE:\n        switch(t->state) {\n        case NGX_HTTP_TFS_STATE_WRITE_CLUSTER_ID_NS:\n            /* save cluster id */\n            if (t->loc_conf->upstream->enable_rcs) {\n                rc_info = t->rc_info_node;\n                logical_cluster =\n                    &rc_info->logical_clusters[t->logical_cluster_index];\n                physical_cluster =\n                    &logical_cluster->rw_clusters[t->rw_cluster_index];\n                /* check ns cluster id with rc configure */\n                cluster_id_text = &physical_cluster->cluster_id_text;\n                cluster_id = ngx_http_tfs_get_cluster_id(cluster_id_text->data);\n                if (t->file.cluster_id != cluster_id) {\n                    ngx_log_error(NGX_LOG_ERR,\n                                  t->log, 0,\n                                  \"error, cluster id conflict: \"\n                                  \"%uD(ns) <> %uD(rcs)\",\n                                  t->file.cluster_id,\n                                  cluster_id);\n                    return NGX_ERROR;\n                }\n                physical_cluster->cluster_id = t->file.cluster_id;\n\n            } else {\n                t->main_conf->cluster_id = t->file.cluster_id;\n            }\n            t->state = NGX_HTTP_TFS_STATE_WRITE_GET_BLK_INFO;\n            return rc;\n\n        case NGX_HTTP_TFS_STATE_WRITE_GET_GROUP_COUNT:\n            if (t->group_count != 1) {\n                t->state = NGX_HTTP_TFS_STATE_WRITE_GET_GROUP_SEQ;\n                return rc;\n            }\n            /* group_count == 1, maybe able to make choice */\n            t->group_seq = 0;\n        case NGX_HTTP_TFS_STATE_WRITE_GET_GROUP_SEQ:\n            rc_info = t->rc_info_node;\n            ngx_http_tfs_rcs_set_group_info_by_addr(rc_info,\n                                                    t->group_count,\n                                                    t->group_seq,\n                                                    t->name_server_addr);\n            rc = ngx_http_tfs_select_name_server(t, rc_info,\n                                                 &t->name_server_addr,\n                                                 &t->name_server_addr_text);\n            if (rc == NGX_ERROR) {\n                return NGX_HTTP_TFS_EXIT_SERVER_OBJECT_NOT_FOUND;\n            }\n\n            tp->peer.free(&tp->peer, tp->peer.data, 0);\n\n            ngx_http_tfs_peer_set_addr(t->pool,\n                                       &t->tfs_peer_servers[NGX_HTTP_TFS_NAME_SERVER],\n                                       &t->name_server_addr);\n            return rc;\n\n        case NGX_HTTP_TFS_STATE_WRITE_GET_BLK_INFO:\n            if (t->is_stat_dup_file) {\n                t->state = NGX_HTTP_TFS_STATE_WRITE_STAT_DUP_FILE;\n\n            } else if (t->is_rolling_back) {\n                t->state = NGX_HTTP_TFS_STATE_WRITE_DELETE_DATA;\n\n            } else {\n                if (!t->parent\n                    && (t->r_ctx.version == 2\n                        || (t->is_large_file && !t->is_process_meta_seg)))\n                {\n                    t->decline_handler = ngx_http_tfs_batch_process_start;\n                    return NGX_DECLINED;\n                }\n                t->state = NGX_HTTP_TFS_STATE_WRITE_CREATE_FILE_NAME;\n            }\n            break;\n        }\n        break;\n    case NGX_HTTP_TFS_ACTION_REMOVE_FILE:\n        switch (t->state) {\n        case NGX_HTTP_TFS_STATE_REMOVE_GET_GROUP_COUNT:\n            /* maybe able to make choice */\n            if (t->group_count != 1) {\n                t->state = NGX_HTTP_TFS_STATE_REMOVE_GET_GROUP_SEQ;\n            }\n            /* group_count == 1, maybe able to make choice */\n            t->group_seq = 0;\n        case NGX_HTTP_TFS_STATE_REMOVE_GET_GROUP_SEQ:\n            rc_info = t->rc_info_node;\n            ngx_http_tfs_rcs_set_group_info_by_addr(rc_info,\n                                                    t->group_count,\n                                                    t->group_seq,\n                                                    t->name_server_addr);\n            rc = ngx_http_tfs_select_name_server(t, rc_info,\n                                                 &t->name_server_addr,\n                                                 &t->name_server_addr_text);\n            if (rc == NGX_ERROR) {\n                /* in order to return 404 */\n                return NGX_HTTP_TFS_EXIT_SERVER_OBJECT_NOT_FOUND;\n            }\n\n            tp->peer.free(&tp->peer, tp->peer.data, 0);\n\n            ngx_http_tfs_peer_set_addr(t->pool,\n                             &t->tfs_peer_servers[NGX_HTTP_TFS_NAME_SERVER],\n                             &t->name_server_addr);\n            return rc;\n        case NGX_HTTP_TFS_STATE_REMOVE_GET_BLK_INFO:\n            if (t->is_large_file\n                && t->r_ctx.unlink_type == NGX_HTTP_TFS_UNLINK_DELETE\n                && t->meta_segment_data == NULL)\n            {\n                t->state = NGX_HTTP_TFS_STATE_REMOVE_READ_META_SEGMENT;\n\n            } else if (t->is_stat_dup_file) {\n                t->state = NGX_HTTP_TFS_STATE_REMOVE_STAT_FILE;\n\n            } else {\n                t->state = NGX_HTTP_TFS_STATE_REMOVE_DELETE_DATA;\n            }\n        }\n        break;\n    }\n\n    addr = ngx_http_tfs_select_data_server(t,\n                                  &t->file.segment_data[t->file.segment_index]);\n    if (addr == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_http_tfs_peer_set_addr(t->pool,\n                               &t->tfs_peer_servers[NGX_HTTP_TFS_DATA_SERVER],\n                               addr);\n    return rc;\n}\n\n\nvoid\nngx_http_tfs_reset_segment_data(ngx_http_tfs_t *t)\n{\n    uint32_t                      block_count, i;\n    ngx_http_tfs_segment_data_t  *segment_data;\n\n    /* reset current lookup cache */\n    t->block_cache_ctx.curr_lookup_cache = NGX_HTTP_TFS_LOCAL_BLOCK_CACHE;\n\n    block_count = t->file.segment_count - t->file.segment_index;\n    if (block_count > NGX_HTTP_TFS_MAX_BATCH_COUNT) {\n        block_count = NGX_HTTP_TFS_MAX_BATCH_COUNT;\n    }\n\n    segment_data = &t->file.segment_data[t->file.segment_index];\n    for (i = 0; i < block_count; i++, segment_data++) {\n        segment_data->cache_hit = NGX_HTTP_TFS_NO_BLOCK_CACHE;\n        segment_data->block_info.ds_addrs = NULL;\n        segment_data->ds_retry = 0;\n        segment_data->ds_index = 0;\n    }\n\n    t->file.curr_batch_count = 0;\n}\n\n\nngx_int_t\nngx_http_tfs_retry_ns(ngx_http_tfs_t *t)\n{\n    ngx_int_t                        rc;\n    ngx_http_tfs_peer_connection_t  *tp;\n\n    tp = t->tfs_peer;\n    tp->peer.free(&tp->peer, tp->peer.data, 0);\n\n    if (!t->retry_curr_ns) {\n        t->rw_cluster_index++;\n        rc = ngx_http_tfs_select_name_server(t, t->rc_info_node,\n                                             &t->name_server_addr,\n                                             &t->name_server_addr_text);\n        if (rc == NGX_ERROR) {\n            return NGX_HTTP_TFS_EXIT_SERVER_OBJECT_NOT_FOUND;\n        }\n\n        ngx_http_tfs_peer_set_addr(t->pool,\n                                 &t->tfs_peer_servers[NGX_HTTP_TFS_NAME_SERVER],\n                                 &t->name_server_addr);\n\n        ngx_http_tfs_reset_segment_data(t);\n\n    } else {\n        t->retry_curr_ns = NGX_HTTP_TFS_NO;\n    }\n\n    switch (t->r_ctx.action.code) {\n    case NGX_HTTP_TFS_ACTION_READ_FILE:\n    case NGX_HTTP_TFS_ACTION_STAT_FILE:\n        /* lookup block cache */\n        if (t->block_cache_ctx.curr_lookup_cache\n            != NGX_HTTP_TFS_NO_BLOCK_CACHE)\n        {\n            if (!t->parent\n                && (t->r_ctx.version == 2\n                    || (t->is_large_file && !t->is_process_meta_seg)))\n            {\n                t->decline_handler = ngx_http_tfs_batch_lookup_block_cache;\n\n            } else {\n                t->decline_handler = ngx_http_tfs_lookup_block_cache;\n            }\n            return t->decline_handler(t);\n        }\n        break;\n    case NGX_HTTP_TFS_ACTION_WRITE_FILE:\n        /* update not allow retry */\n        if (t->r_ctx.is_raw_update) {\n            return NGX_ERROR;\n        }\n\n        /* stat failed, do not dedup, save new tfs file and do not save tair */\n        if (t->is_stat_dup_file) {\n            t->is_stat_dup_file = NGX_HTTP_TFS_NO;\n            t->use_dedup = NGX_HTTP_TFS_NO;\n            t->state = NGX_HTTP_TFS_STATE_WRITE_CLUSTER_ID_NS;\n            t->file.segment_data[0].segment_info.block_id = 0;\n            t->file.segment_data[0].segment_info.file_id = 0;\n        }\n    }\n\n    if (ngx_http_tfs_reinit(t->data, t) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    ngx_http_tfs_connect(t);\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_tfs_create_ds_request(ngx_http_tfs_t *t)\n{\n    ngx_chain_t  *cl;\n\n    cl = ngx_http_tfs_data_server_create_message(t);\n    if (cl == NULL) {\n        return NGX_ERROR;\n    }\n\n    t->request_bufs = cl;\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_tfs_process_ds(ngx_http_tfs_t *t)\n{\n    size_t                           b_size;\n    uint32_t                         body_len, len_to_update;\n    ngx_int_t                        rc;\n    ngx_buf_t                       *b, *body_buffer;\n    ngx_chain_t                     *cl, **ll;\n    ngx_http_request_t              *r;\n    ngx_http_tfs_header_t           *header;\n    ngx_http_tfs_segment_data_t     *segment_data;\n    ngx_http_tfs_peer_connection_t  *tp;\n\n    header = (ngx_http_tfs_header_t *) t->header;\n    tp = t->tfs_peer;\n    b = &tp->body_buffer;\n\n    body_len = header->len;\n    if (ngx_buf_size(b) < body_len) {\n        return NGX_AGAIN;\n    }\n\n    rc = ngx_http_tfs_data_server_parse_message(t);\n    if (rc == NGX_ERROR) {\n        return NGX_ERROR;\n    }\n\n    ngx_http_tfs_clear_buf(b);\n\n    segment_data = &t->file.segment_data[t->file.segment_index];\n\n    switch (t->r_ctx.action.code) {\n    case NGX_HTTP_TFS_ACTION_STAT_FILE:\n        t->file_name = t->r_ctx.file_path_s;\n        if (rc == NGX_OK) {\n            t->state = NGX_HTTP_TFS_STATE_STAT_DONE;\n\n            if (t->r_ctx.chk_exist == NGX_HTTP_TFS_NO) {\n                /* need json output */\n                for (cl = t->out_bufs, ll = &t->out_bufs; cl; cl = cl->next) {\n                    ll = &cl->next;\n                }\n\n                cl = ngx_http_tfs_json_raw_file_stat(\n                                  t->json_output,\n                                  ngx_http_tfs_raw_fsname_get_name(&t->r_ctx.fsname,\n                                                                   t->is_large_file,\n                                                                   0),\n                                  t->r_ctx.fsname.file.block_id,\n                                  &t->file_stat);\n                if (cl == NULL) {\n                    return NGX_ERROR;\n                }\n\n                *ll = cl;\n            }\n\n            return NGX_DONE;\n        }\n\n        if (rc == NGX_HTTP_TFS_EXIT_NO_LOGICBLOCK_ERROR) {\n            ngx_http_tfs_remove_block_cache(t, segment_data);\n        }\n\n        return NGX_HTTP_TFS_AGAIN;\n\n    case NGX_HTTP_TFS_ACTION_WRITE_FILE:\n        switch(t->state) {\n        case NGX_HTTP_TFS_STATE_WRITE_STAT_DUP_FILE:\n            if (rc == NGX_OK) {\n                if (t->file_stat.flag == NGX_HTTP_TFS_FILE_NORMAL) {\n                    rc = ngx_http_tfs_set_output_file_name(t);\n                    if (rc == NGX_ERROR) {\n                        return NGX_ERROR;\n                    }\n                    r = t->data;\n                    t->dedup_ctx.file_data = r->request_body->bufs;\n                    t->dedup_ctx.file_ref_count += 1;\n                    t->decline_handler = ngx_http_tfs_set_duplicate_info;\n                    return NGX_DECLINED;\n                }\n\n            } else {\n                /* stat success but file is deleted or concealed */\n                /* need save new tfs file, but do not save tair */\n                if (rc == NGX_HTTP_TFS_EXIT_FILE_INFO_ERROR\n                    || rc == NGX_HTTP_TFS_EXIT_META_NOT_FOUND_ERROR)\n                {\n                    t->state = NGX_HTTP_TFS_STATE_WRITE_CLUSTER_ID_NS;\n                    t->is_stat_dup_file = NGX_HTTP_TFS_NO;\n                    t->use_dedup = NGX_HTTP_TFS_NO;\n                    /* need reset block id and file id */\n                    t->file.segment_data[0].segment_info.block_id = 0;\n                    t->file.segment_data[0].segment_info.file_id = 0;\n                    rc = NGX_OK;\n\n                } else {\n                    /* stat failed will goto retry */\n                    rc = NGX_HTTP_TFS_AGAIN;\n                }\n            }\n            break;\n        case NGX_HTTP_TFS_STATE_WRITE_CREATE_FILE_NAME:\n            if (rc == NGX_OK) {\n                t->state = NGX_HTTP_TFS_STATE_WRITE_WRITE_DATA;\n\n            } else {\n                /* create failed retry */\n                return NGX_HTTP_TFS_AGAIN;\n            }\n            break;\n        case NGX_HTTP_TFS_STATE_WRITE_WRITE_DATA:\n            /* write failed retry */\n            if (rc != NGX_OK) {\n                return NGX_HTTP_TFS_AGAIN;\n            }\n\n            /* write success, update data buf, offset and crc */\n            cl = segment_data->data;\n            len_to_update = segment_data->oper_size;\n            while (len_to_update > 0) {\n                while (cl && ngx_buf_size(cl->buf) == 0) {\n                    cl = cl->next;\n                }\n                if (cl == NULL) {\n                    ngx_log_error(NGX_LOG_ERR, t->log, 0,\n                                  \"update send data offset \"\n                                  \"failed for early end.\");\n                    return NGX_ERROR;\n                }\n                b_size = ngx_min(ngx_buf_size(cl->buf), len_to_update);\n                if (ngx_buf_in_memory(cl->buf)) {\n                    cl->buf->pos += b_size;\n\n                } else {\n                    cl->buf->file_pos += b_size;\n                }\n                len_to_update -= b_size;\n            }\n            segment_data->data = cl;\n\n            t->file.left_length -= segment_data->oper_size;\n            t->stat_info.size += segment_data->oper_size;\n            segment_data->oper_offset += segment_data->oper_size;\n            segment_data->oper_size = ngx_min(t->file.left_length,\n                                              NGX_HTTP_TFS_MAX_FRAGMENT_SIZE);\n\n            if (t->r_ctx.version == 1) {\n                if (t->file.left_length > 0 && !t->is_large_file) {\n                    t->state = NGX_HTTP_TFS_STATE_WRITE_WRITE_DATA;\n                    return NGX_OK;\n                }\n            }\n            t->state = NGX_HTTP_TFS_STATE_WRITE_CLOSE_FILE;\n            break;\n        case NGX_HTTP_TFS_STATE_WRITE_CLOSE_FILE:\n            /* close failed retry */\n            if (rc != NGX_OK) {\n                return NGX_HTTP_TFS_AGAIN;\n            }\n\n            /* sub process return here */\n            if (t->parent) {\n                return NGX_DONE;\n            }\n\n            t->file.segment_index++;\n\n            /* small file or large_file meta segment */\n            if (t->r_ctx.version == 1) {\n                /* client abort need roll back, remove all segments written */\n                if (t->client_abort && t->r_ctx.is_raw_update == NGX_HTTP_TFS_NO) {\n                    t->state = NGX_HTTP_TFS_STATE_WRITE_GET_BLK_INFO;\n                    t->is_rolling_back = NGX_HTTP_TFS_YES;\n                    t->file.segment_index = 0;\n                    return NGX_OK;\n                }\n\n                t->state = NGX_HTTP_TFS_STATE_WRITE_DONE;\n                rc = ngx_http_tfs_set_output_file_name(t);\n                if (rc == NGX_ERROR) {\n                    return NGX_ERROR;\n                }\n                /* when new tfs file is saved,\n                 * do not care saving tair is success or not\n                 */\n                if (t->use_dedup) {\n                    r = t->data;\n                    t->dedup_ctx.file_data = r->request_body->bufs;\n                    t->dedup_ctx.file_ref_count += 1;\n                    t->decline_handler = ngx_http_tfs_set_duplicate_info;\n                    return NGX_DECLINED;\n                }\n                return NGX_DONE;\n            }\n            break;\n\n         /* is rolling back */\n         case NGX_HTTP_TFS_STATE_WRITE_DELETE_DATA:\n             t->file.segment_index++;\n             if (t->file.segment_index >= t->file.segment_count) {\n                 if (t->client_abort) {\n                     return NGX_HTTP_CLIENT_CLOSED_REQUEST;\n                 }\n\n                 if (t->request_timeout) {\n                     return NGX_HTTP_REQUEST_TIME_OUT;\n                 }\n\n                 return NGX_ERROR;\n             }\n\n             t->state = NGX_HTTP_TFS_STATE_WRITE_GET_BLK_INFO;\n             return NGX_OK;\n        }\n        break;\n    case NGX_HTTP_TFS_ACTION_REMOVE_FILE:\n        switch(t->state) {\n        case NGX_HTTP_TFS_STATE_REMOVE_STAT_FILE:\n            if (rc == NGX_OK) {\n                if (t->file_stat.flag == NGX_HTTP_TFS_FILE_NORMAL\n                    || t->file_stat.flag == NGX_HTTP_TFS_FILE_CONCEAL)\n                {\n                    t->state = NGX_HTTP_TFS_STATE_REMOVE_READ_META_SEGMENT;\n                    segment_data->oper_size =\n                                     ngx_min(t->file_stat.size,\n                                             NGX_HTTP_TFS_MAX_READ_FILE_SIZE);\n                    return NGX_OK;\n                }\n\n                /* file is deleted */\n                return NGX_HTTP_TFS_EXIT_FILE_STATUS_ERROR;\n            }\n            /* stat failed will goto retry */\n            return NGX_HTTP_TFS_AGAIN;\n       case NGX_HTTP_TFS_STATE_REMOVE_DELETE_DATA:\n            if (rc != NGX_OK) {\n                return rc;\n            }\n\n            /* small file */\n            if (t->r_ctx.version == 1 && !t->is_large_file) {\n                t->state = NGX_HTTP_TFS_STATE_REMOVE_DONE;\n                t->file_name = t->r_ctx.file_path_s;\n                return NGX_DONE;\n            }\n\n            /* large_file && custom file */\n            t->file.segment_index++;\n            if (t->file.segment_index >= t->file.segment_count) {\n                if (t->r_ctx.version == 1) {\n                    /* large file */\n                    t->state = NGX_HTTP_TFS_STATE_REMOVE_DONE;\n                    t->file_name = t->r_ctx.file_path_s;\n                    return NGX_DONE;\n                }\n\n                if (t->r_ctx.version == 2) {\n                    if (!t->file.still_have) {\n                        t->state = NGX_HTTP_TFS_STATE_REMOVE_NOTIFY_MS;\n\n                    } else {\n                        t->state = NGX_HTTP_TFS_STATE_REMOVE_GET_FRAG_INFO;\n                        t->file.file_offset = segment_data->segment_info.offset\n                                            + segment_data->segment_info.size;\n                        t->file.segment_index = 0;\n                    }\n\n                    body_buffer =\n                     &t->tfs_peer_servers[NGX_HTTP_TFS_META_SERVER].body_buffer;\n                    ngx_http_tfs_clear_buf(body_buffer);\n                }\n\n            } else {\n                t->state = NGX_HTTP_TFS_STATE_REMOVE_GET_BLK_INFO;\n            }\n            break;\n        }\n    }\n\n    return rc;\n}\n\n\nngx_int_t\nngx_http_tfs_retry_ds(ngx_http_tfs_t *t)\n{\n    ngx_int_t                       rc;\n    ngx_http_tfs_inet_t             *addr;\n    ngx_http_tfs_segment_data_t     *segment_data;\n    ngx_http_tfs_peer_connection_t  *tp;\n\n    tp = t->tfs_peer;\n    tp->peer.free(&tp->peer, tp->peer.data, 0);\n\n    segment_data = &t->file.segment_data[t->file.segment_index];\n    addr = ngx_http_tfs_select_data_server(t, segment_data);\n    if (addr == NULL) {\n        switch(t->r_ctx.action.code) {\n        case NGX_HTTP_TFS_ACTION_STAT_FILE:\n            t->state = NGX_HTTP_TFS_STATE_STAT_GET_BLK_INFO;\n            break;\n        case NGX_HTTP_TFS_ACTION_READ_FILE:\n            t->state = NGX_HTTP_TFS_STATE_READ_GET_BLK_INFO;\n            break;\n        case NGX_HTTP_TFS_ACTION_REMOVE_FILE:\n            if (t->is_large_file && t->is_process_meta_seg) {\n                return NGX_HTTP_TFS_EXIT_SERVER_OBJECT_NOT_FOUND;\n            }\n\n            /* TODO: dedup */\n            return NGX_ERROR;\n        case NGX_HTTP_TFS_ACTION_WRITE_FILE:\n            /* update not allow retry */\n            if (t->r_ctx.is_raw_update) {\n                return NGX_ERROR;\n            }\n\n            /* stat retry_ds failed, do not dedup,\n             * save new tfs file and do not save tair\n             */\n            if (t->is_stat_dup_file) {\n                t->is_stat_dup_file = NGX_HTTP_TFS_NO;\n                t->use_dedup = NGX_HTTP_TFS_NO;\n                t->state = NGX_HTTP_TFS_STATE_WRITE_CLUSTER_ID_NS;\n                t->file.segment_data[0].segment_info.block_id = 0;\n                t->file.segment_data[0].segment_info.file_id = 0;\n                t->retry_curr_ns = NGX_HTTP_TFS_YES;\n\n            } else {\n                /* allow retry other writable clusters */\n                if (++t->retry_count <= NGX_HTTP_TFS_MAX_RETRY_COUNT) {\n                    t->retry_curr_ns = NGX_HTTP_TFS_YES;\n                }\n                t->state = NGX_HTTP_TFS_STATE_WRITE_GET_BLK_INFO;\n                segment_data->segment_info.block_id = 0;\n                segment_data->segment_info.file_id = 0;\n                segment_data->write_file_number = 0;\n                segment_data->segment_info.crc = 0;\n                /* reset all write data from orig_data */\n                segment_data->data = NULL;\n                rc = ngx_chain_add_copy_with_buf(t->pool,\n                    &segment_data->data, segment_data->orig_data);\n                if (rc == NGX_ERROR) {\n                    return NGX_ERROR;\n                }\n\n                t->file.left_length = segment_data->segment_info.size;\n                segment_data->oper_offset = 0;\n                segment_data->oper_size = ngx_min(t->file.left_length,\n                                                  NGX_HTTP_TFS_MAX_FRAGMENT_SIZE);\n            }\n            break;\n        default:\n            return NGX_ERROR;\n        }\n\n        t->tfs_peer = ngx_http_tfs_select_peer(t);\n        if (t->tfs_peer == NULL) {\n            return NGX_ERROR;\n        }\n\n        t->recv_chain->buf = &t->header_buffer;\n        t->recv_chain->next->buf = &t->tfs_peer->body_buffer;\n\n        /* reset ds retry count */\n        segment_data->ds_retry = 0;\n\n        if (t->retry_handler == NULL) {\n            return NGX_ERROR;\n        }\n\n        return t->retry_handler(t);\n    }\n\n    ngx_http_tfs_peer_set_addr(t->pool,\n                               &t->tfs_peer_servers[NGX_HTTP_TFS_DATA_SERVER],\n                               addr);\n\n    if (ngx_http_tfs_reinit(t->data, t) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    ngx_http_tfs_connect(t);\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_tfs_process_ds_read(ngx_http_tfs_t *t)\n{\n    size_t                           size;\n    ngx_int_t                        rc;\n    ngx_buf_t                       *b;\n    ngx_http_tfs_segment_data_t     *segment_data;\n    ngx_http_tfs_peer_connection_t  *tp;\n    ngx_http_tfs_logical_cluster_t  *logical_cluster;\n\n    tp = t->tfs_peer;\n    b = &tp->body_buffer;\n\n    size = ngx_buf_size(b);\n    if (size == 0) {\n        ngx_log_error(NGX_LOG_INFO, t->log, 0, \"process ds read is zero\");\n        return NGX_AGAIN;\n    }\n\n    rc = ngx_http_tfs_data_server_parse_message(t);\n    if (rc == NGX_ERROR || rc == NGX_HTTP_TFS_AGAIN) {\n        ngx_http_tfs_clear_buf(b);\n        return NGX_ERROR;\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, t->log, 0,\n                   \"t->length is %O, rc is %i\",\n                   t->length, rc);\n\n    b->pos += size;\n\n    if (t->length > 0) {\n        return NGX_AGAIN;\n    }\n\n    segment_data = &t->file.segment_data[t->file.segment_index];\n\n    switch (t->r_ctx.action.code) {\n    case NGX_HTTP_TFS_ACTION_READ_FILE:\n        if (t->length == 0) {\n            t->file.left_length -= segment_data->oper_size;\n            t->file.file_offset += segment_data->oper_size;\n\n            if (t->file.left_length == 0) {\n                /* large_file meta segment */\n                if (t->is_large_file && t->is_process_meta_seg) {\n                    /* ready to read data segments */\n                    *(t->meta_segment_data->buf) = *b;\n                    /* reset buf pos to get whole file data */\n                    t->meta_segment_data->buf->pos = t->meta_segment_data->buf->start;\n                    rc = ngx_http_tfs_get_segment_for_read(t);\n                    if (rc == NGX_ERROR) {\n                        ngx_log_error(NGX_LOG_ERR, t->log, 0,\n                                      \"get segment for read failed\");\n                        return NGX_ERROR;\n                    }\n\n                    if (rc == NGX_DONE) {\n                        /* pread and start_offset > file size */\n                        t->state = NGX_HTTP_TFS_STATE_READ_DONE;\n                        t->file_name = t->r_ctx.file_path_s;\n\n                        return NGX_DONE;\n                    }\n\n                    t->is_process_meta_seg = NGX_HTTP_TFS_NO;\n                    /* later will be alloc */\n                    ngx_memzero(&t->tfs_peer->body_buffer, sizeof(ngx_buf_t));\n\n                    t->state = NGX_HTTP_TFS_STATE_READ_GET_BLK_INFO;\n\n                    t->block_cache_ctx.curr_lookup_cache =\n                                                 NGX_HTTP_TFS_LOCAL_BLOCK_CACHE;\n                    t->decline_handler = ngx_http_tfs_batch_lookup_block_cache;\n                    return NGX_DECLINED;\n                }\n\n                /* sub process also return here */\n                t->state = NGX_HTTP_TFS_STATE_READ_DONE;\n                t->file_name = t->r_ctx.file_path_s;\n\n                return NGX_DONE;\n            }\n\n            /* small file */\n            if ((t->r_ctx.version == 1 && !t->is_large_file)\n                || (t->is_large_file && t->is_process_meta_seg))\n            {\n                segment_data->oper_size = ngx_min(t->file.left_length,\n                                               NGX_HTTP_TFS_MAX_READ_FILE_SIZE);\n                segment_data->oper_offset = t->file.file_offset;\n                return rc;\n            }\n        }\n        break;\n\n        /* NGX_HTTP_TFS_STATE_REMOVE_READ_META_SEGMENT */\n    case NGX_HTTP_TFS_ACTION_REMOVE_FILE:\n        if (t->length == 0) {\n            t->file.left_length -= segment_data->oper_size;\n\n            if (t->file.left_length == 0) {\n                if (!t->is_large_file && t->use_dedup) {\n                    logical_cluster =\n                     &t->rc_info_node->logical_clusters[t->logical_cluster_index];\n\n                    rc = ngx_http_tfs_get_dedup_instance(&t->dedup_ctx,\n                                        &logical_cluster->dup_server_info,\n                                        logical_cluster->dup_server_addr_hash);\n\n                    if (rc == NGX_ERROR) {\n                        ngx_log_error(NGX_LOG_ERR, t->log, 0,\n                                      \"get dedup instance failed.\");\n                        /* get dedup instance fail, do not unlink file,\n                         * return success\n                         */\n                        t->state = NGX_HTTP_TFS_STATE_REMOVE_DONE;\n                        return NGX_DONE;\n                    }\n                    *(t->meta_segment_data->buf) = t->tfs_peer->body_buffer;\n                    /* reset buf pos to get whole file data */\n                    t->meta_segment_data->buf->pos =\n                                               t->meta_segment_data->buf->start;\n                    t->dedup_ctx.file_data = t->meta_segment_data;\n                    t->decline_handler = ngx_http_tfs_get_duplicate_info;\n                    return NGX_DECLINED;\n                }\n                if (t->is_large_file) {\n                    *(t->meta_segment_data->buf) = t->tfs_peer->body_buffer;\n                    /* reset buf pos to get whole file data */\n                    t->meta_segment_data->buf->pos = t->meta_segment_data->buf->start;\n                    rc = ngx_http_tfs_get_segment_for_delete(t);\n                    if (rc == NGX_ERROR) {\n                        ngx_log_error(NGX_LOG_ERR, t->log, 0,\n                                      \"get segment for delete failed\");\n                        return NGX_ERROR;\n                    }\n                    t->is_process_meta_seg = NGX_HTTP_TFS_NO;\n                    /* later will be alloc */\n                    ngx_memzero(&t->tfs_peer->body_buffer, sizeof(ngx_buf_t));\n                    t->state = NGX_HTTP_TFS_STATE_REMOVE_DELETE_DATA;\n                }\n\n            } else {\n                t->file.file_offset += segment_data->oper_size;\n                segment_data->oper_size = ngx_min(t->file.left_length,\n                                               NGX_HTTP_TFS_MAX_READ_FILE_SIZE);\n                segment_data->oper_offset = t->file.file_offset;\n            }\n        }\n        break;\n    }\n\n    return rc;\n}\n\n\nngx_int_t\nngx_http_tfs_process_ds_input_filter(ngx_http_tfs_t *t)\n{\n    int16_t                           msg_type;\n    uint32_t                          body_len;\n    ngx_int_t                         rc;\n    ngx_buf_t                        *b;\n    ngx_http_tfs_segment_data_t      *segment_data;\n    ngx_http_tfs_peer_connection_t   *tp;\n    ngx_http_tfs_ds_read_response_t  *resp;\n\n    resp = (ngx_http_tfs_ds_read_response_t *) t->header;\n    msg_type = resp->header.type;\n    if (msg_type == NGX_HTTP_TFS_STATUS_MESSAGE) {\n        t->length = resp->header.len - sizeof(uint32_t);\n        return NGX_OK;\n    }\n\n    segment_data = &t->file.segment_data[t->file.segment_index];\n    tp = t->tfs_peer;\n    b = &tp->body_buffer;\n\n    if (resp->data_len < 0) {\n        if (resp->data_len == NGX_HTTP_TFS_EXIT_NO_LOGICBLOCK_ERROR) {\n            ngx_http_tfs_remove_block_cache(t, segment_data);\n\n        } else if (resp->data_len == -22) {\n            /* for compatibility,\n             * old dataserver will return this instead of -1007\n             */\n            resp->data_len = NGX_HTTP_TFS_EXIT_INVALID_ARGU_ERROR;\n        }\n\n        /* must be bad request, do not retry */\n        if (resp->data_len == NGX_HTTP_TFS_EXIT_READ_OFFSET_ERROR\n            || resp->data_len == NGX_HTTP_TFS_EXIT_INVALID_ARGU_ERROR\n            || resp->data_len == NGX_HTTP_TFS_EXIT_PHYSIC_BLOCK_OFFSET_ERROR)\n        {\n            return resp->data_len;\n        }\n        ngx_log_error(NGX_LOG_ERR, t->log, 0,\n                      \"read file(block id: %uD, file id: %uL) \"\n                      \"from (%s) fail, error code: %D, will retry\",\n                      segment_data->segment_info.block_id,\n                      segment_data->segment_info.file_id,\n                      tp->peer_addr_text, resp->data_len);\n        ngx_http_tfs_clear_buf(b);\n        return NGX_HTTP_TFS_AGAIN;\n    }\n\n    if (resp->data_len == 0) {\n        t->state = NGX_HTTP_TFS_STATE_READ_DONE;\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, t->log, 0, \"read len is 0\");\n        return NGX_DONE;\n    }\n\n    if (resp->data_len >= NGX_HTTP_TFS_IMAGE_TYPE_SIZE) {\n        /* we need to check small file or large file's first data segment */\n        /* or custom file's first segment */\n        if (((t->parent == NULL && !t->is_process_meta_seg)\n             || (t->parent && t->sp_curr == 0))\n            && t->headers_in.content_type == NULL)\n        {\n            if (ngx_buf_size(b) < NGX_HTTP_TFS_IMAGE_TYPE_SIZE) {\n                return NGX_AGAIN;\n            }\n\n            t->headers_in.content_type = ngx_pcalloc(t->pool, sizeof(ngx_table_elt_t));\n            if (t->headers_in.content_type == NULL) {\n                return NGX_ERROR;\n            }\n\n            rc = ngx_http_tfs_get_content_type(b->pos, &t->headers_in.content_type->value);\n            if (rc != NGX_OK) {\n                ngx_log_debug0(NGX_LOG_DEBUG_HTTP, t->log, 0, \"unknown content type\");\n            }\n        }\n    }\n\n    body_len = resp->header.len - sizeof(uint32_t);\n    t->length = body_len;\n    /* in readv2, body_len = resp->data_len + 40 */\n    segment_data->oper_size = resp->data_len;\n    /* sub process only read once */\n    if (t->parent) {\n        t->file.left_length = resp->data_len;\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, t->log, 0,\n                   \"read len is %O, data len is %D\",\n                   t->length, resp->data_len);\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_tfs_process_ms_input_filter(ngx_http_tfs_t *t)\n{\n    ngx_http_tfs_header_t  *header;\n\n    header = (ngx_http_tfs_header_t *) t->header;\n    t->length = header->len;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, t->log, 0,\n                   \"ls dir len is %O\",\n                   t->length);\n\n    return NGX_OK;\n}\n\n\n"
  },
  {
    "path": "modules/ngx_http_tfs_module/ngx_http_tfs_server_handler.h",
    "content": "\n/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#ifndef _NGX_HTTP_TFS_SERVER_HANDLER_H_INCLUDED_\n#define _NGX_HTTP_TFS_SERVER_HANDLER_H_INCLUDED_\n\n\n#include <ngx_http_tfs.h>\n\n\n/* root server */\nngx_int_t ngx_http_tfs_create_rs_request(ngx_http_tfs_t *t);\nngx_int_t ngx_http_tfs_process_rs(ngx_http_tfs_t *t);\n\n\n/* meta server */\nngx_int_t ngx_http_tfs_create_ms_request(ngx_http_tfs_t *t);\nngx_int_t ngx_http_tfs_process_ms_input_filter(ngx_http_tfs_t *t);\nngx_int_t ngx_http_tfs_process_ms(ngx_http_tfs_t *t);\nngx_int_t ngx_http_tfs_process_ms_ls_dir(ngx_http_tfs_t *t);\n\n\n/* rc server */\nngx_int_t ngx_http_tfs_create_rcs_request(ngx_http_tfs_t *t);\nngx_int_t ngx_http_tfs_process_rcs(ngx_http_tfs_t *t);\n\n\n/* name server */\nngx_int_t ngx_http_tfs_create_ns_request(ngx_http_tfs_t *t);\nngx_int_t ngx_http_tfs_process_ns(ngx_http_tfs_t *t);\n\n\n/* data server */\nngx_int_t ngx_http_tfs_create_ds_request(ngx_http_tfs_t *t);\nngx_int_t ngx_http_tfs_process_ds_input_filter(ngx_http_tfs_t *t);\nngx_int_t ngx_http_tfs_process_ds(ngx_http_tfs_t *t);\nngx_int_t ngx_http_tfs_process_ds_read(ngx_http_tfs_t *t);\n\nngx_int_t ngx_http_tfs_retry_ds(ngx_http_tfs_t *t);\nngx_int_t ngx_http_tfs_retry_ns(ngx_http_tfs_t *t);\n\n\n#endif  /* _NGX_HTTP_TFS_SERVER_HANDLER_H_INCLUDED_ */\n\n"
  },
  {
    "path": "modules/ngx_http_tfs_module/ngx_http_tfs_tair_helper.c",
    "content": "\n/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#include <ngx_http_tfs_tair_helper.h>\n\n\n#ifdef NGX_HTTP_TFS_USE_TAIR\n\nngx_int_t\nngx_http_tfs_tair_get_helper(ngx_http_tfs_tair_instance_t *instance,\n    ngx_pool_t *pool, ngx_log_t *log,\n    ngx_http_tair_data_t *key, ngx_http_tair_get_handler_pt callback,\n    void *data)\n{\n    ngx_int_t  rc;\n\n    if (instance == NULL || key == NULL) {\n        return NGX_ERROR;\n    }\n\n    rc = ngx_http_tair_get(instance->server, pool, log, *key,\n                           instance->area, callback, data);\n\n    if (rc != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    return NGX_DECLINED;\n}\n\n\nngx_int_t ngx_http_tfs_tair_mget_helper(ngx_http_tfs_tair_instance_t *instance,\n    ngx_pool_t *pool, ngx_log_t *log, ngx_array_t *kvs,\n    ngx_http_tair_mget_handler_pt callback, void *data)\n{\n    ngx_int_t  rc;\n\n    if (instance == NULL || kvs == NULL) {\n        return NGX_ERROR;\n    }\n\n    rc = ngx_http_tair_mget(instance->server, pool, log, kvs,\n                            instance->area, callback, data);\n\n    if (rc != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    return NGX_DECLINED;\n}\n\n\nngx_int_t\nngx_http_tfs_tair_put_helper(ngx_http_tfs_tair_instance_t *instance,\n    ngx_pool_t *pool, ngx_log_t *log,\n    ngx_http_tair_data_t *key, ngx_http_tair_data_t *value,\n    ngx_int_t expire, ngx_int_t version,\n    ngx_http_tair_handler_pt callback, void *data)\n{\n    ngx_int_t  rc;\n\n    if (instance == NULL || key == NULL || value == NULL) {\n        return NGX_ERROR;\n    }\n\n    rc = ngx_http_tair_put(instance->server, pool, log, *key,\n                           *value, instance->area, 0 /*nx*/, expire,\n                           version, callback, data);\n\n    if (rc != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    return NGX_DECLINED;\n}\n\n\nngx_int_t\nngx_http_tfs_tair_delete_helper(ngx_http_tfs_tair_instance_t *instance,\n    ngx_pool_t *pool, ngx_log_t *log, ngx_array_t *keys,\n    ngx_http_tair_handler_pt callback, void *data)\n{\n    ngx_int_t  rc;\n\n    if (instance == NULL || keys == NULL) {\n        return NGX_ERROR;\n    }\n\n    rc = ngx_http_tair_delete(instance->server, pool, log, keys,\n                              instance->area, callback, data);\n\n    if (rc != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    return NGX_DECLINED;\n}\n\n#else\n\nngx_int_t\nngx_http_tfs_tair_get_helper(ngx_http_tfs_tair_instance_t *instance,\n    ngx_pool_t *pool, ngx_log_t *log,\n    ngx_http_tair_data_t *key, ngx_http_tair_get_handler_pt callback,\n    void *data)\n{\n    return NGX_ERROR;\n}\n\n\nngx_int_t ngx_http_tfs_tair_mget_helper(ngx_http_tfs_tair_instance_t *instance,\n    ngx_pool_t *pool, ngx_log_t *log,\n    ngx_array_t *kvs, ngx_http_tair_mget_handler_pt callback, void *data)\n{\n    return NGX_ERROR;\n}\n\n\nngx_int_t\nngx_http_tfs_tair_put_helper(ngx_http_tfs_tair_instance_t *instance,\n    ngx_pool_t *pool, ngx_log_t *log,\n    ngx_http_tair_data_t *key, ngx_http_tair_data_t *value,\n    ngx_int_t expire, ngx_int_t version,\n    ngx_http_tair_handler_pt callback, void *data)\n{\n    return NGX_ERROR;\n}\n\n\nngx_int_t\nngx_http_tfs_tair_delete_helper(ngx_http_tfs_tair_instance_t *instance,\n    ngx_pool_t *pool, ngx_log_t *log,\n    ngx_array_t *keys, ngx_http_tair_handler_pt callback, void *data)\n{\n    return NGX_ERROR;\n}\n\n#endif\n\n\nngx_int_t\nngx_http_tfs_parse_tair_server_addr_info(\n    ngx_http_tfs_tair_server_addr_info_t *info,\n    u_char *addr, uint32_t len, void *pool, uint8_t shared_memory)\n{\n    u_char    *temp, *p;\n    ssize_t    info_size;\n    ngx_int_t  i;\n\n    p = addr;\n\n    for (i = 0; i < NGX_HTTP_TFS_TAIR_SERVER_ADDR_PART_COUNT; i++) {\n        temp = ngx_strlchr(p, p + len, ';');\n        if (temp == NULL) {\n            return NGX_ERROR;\n        }\n\n        info_size = temp - p;\n        if (shared_memory) {\n            info->server[i].data =\n                ngx_slab_alloc_locked((ngx_slab_pool_t *)pool, info_size);\n        } else {\n            info->server[i].data = ngx_pcalloc((ngx_pool_t *)pool, info_size);\n        }\n        if (info->server[i].data == NULL) {\n            return NGX_ERROR;\n        }\n        info->server[i].len = info_size;\n        memcpy(info->server[i].data, p, info_size);\n\n        p += info_size + 1;\n        len -= (info_size + 1);\n        if (len <= 0) {\n            return NGX_ERROR;\n        }\n    }\n\n    info->area = ngx_atoi(p, len);\n    if (info->area == NGX_ERROR) {\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "modules/ngx_http_tfs_module/ngx_http_tfs_tair_helper.h",
    "content": "\n/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#ifndef _NGX_HTTP_TFS_TAIR_HELPER_H_INCLUDED_\n#define _NGX_HTTP_TFS_TAIR_HELPER_H_INCLUDED_\n\n\n#include <ngx_tfs_common.h>\n#ifdef NGX_HTTP_TFS_USE_TAIR\n#include <ngx_http_etair_type.h>\n#endif\n\n\ntypedef struct {\n    uint32_t                          server_addr_hash;\n    ngx_int_t                         area;\n\n#ifdef NGX_HTTP_TFS_USE_TAIR\n    ngx_http_etair_server_conf_t     *server;\n#endif\n} ngx_http_tfs_tair_instance_t;\n\n\ntypedef struct {\n    ngx_str_t  server[NGX_HTTP_TFS_TAIR_SERVER_ADDR_PART_COUNT];\n    ngx_int_t  area;\n} ngx_http_tfs_tair_server_addr_info_t;\n\n\n#ifndef NGX_HTTP_TFS_USE_TAIR\n\n#define NGX_HTTP_TAIR_BYTEARRAY       9\n#define NGX_HTTP_TAIR_INT             1\n#define NGX_HTTP_ETAIR_SUCCESS        0\n\n\ntypedef struct {\n\n    size_t                            len;\n    u_char                           *data;\n\n    ngx_uint_t                        type;\n} ngx_http_tair_data_t;\n\ntypedef struct {\n    ngx_http_tair_data_t              key;\n    ngx_http_tair_data_t             *value;\n\n    ngx_int_t                         version;\n    ngx_int_t                         exptime;\n\n    ngx_int_t                         rc;\n} ngx_http_tair_key_value_t;\n\ntypedef void (*ngx_http_tair_handler_pt)(ngx_int_t rc, void *data);\ntypedef void (*ngx_http_tair_get_handler_pt)(ngx_http_tair_key_value_t *kv,\n    ngx_int_t rc, void *data);\ntypedef void (*ngx_http_tair_mget_handler_pt)(ngx_array_t *kvs, ngx_int_t rc,\n    void *data);\n\n#endif\n\nngx_int_t ngx_http_tfs_tair_get_helper(ngx_http_tfs_tair_instance_t *instance,\n    ngx_pool_t *pool, ngx_log_t *log,\n    ngx_http_tair_data_t *key, ngx_http_tair_get_handler_pt callback,\n    void *data);\n\nngx_int_t ngx_http_tfs_tair_mget_helper(ngx_http_tfs_tair_instance_t *instance,\n    ngx_pool_t *pool, ngx_log_t *log,\n    ngx_array_t *kvs, ngx_http_tair_mget_handler_pt callback, void *data);\n\nngx_int_t\nngx_http_tfs_tair_put_helper(ngx_http_tfs_tair_instance_t *instance,\n    ngx_pool_t *pool, ngx_log_t *log,\n    ngx_http_tair_data_t *key, ngx_http_tair_data_t *value,\n    ngx_int_t expire, ngx_int_t version,\n    ngx_http_tair_handler_pt callback, void *data);\n\nngx_int_t\nngx_http_tfs_tair_delete_helper(ngx_http_tfs_tair_instance_t *instance,\n    ngx_pool_t *pool, ngx_log_t *log,\n    ngx_array_t *keys, ngx_http_tair_handler_pt callback, void *data);\n\nngx_int_t\nngx_http_tfs_parse_tair_server_addr_info(\n    ngx_http_tfs_tair_server_addr_info_t *info,\n    u_char *addr, uint32_t len, void* pool, uint8_t shared_memory);\n\n\n\n#endif  /* _NGX_HTTP_TFS_TAIR_HELPER_H_INCLUDED_ */\n"
  },
  {
    "path": "modules/ngx_http_tfs_module/ngx_http_tfs_timers.c",
    "content": "\n/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#include <ngx_http_tfs_timers.h>\n#include <nginx.h>\n\n\nstatic void ngx_http_tfs_timeout_handler(ngx_event_t *event);\n\n\nngx_http_tfs_timers_lock_t *\nngx_http_tfs_timers_init(ngx_cycle_t *cycle,\n    u_char *lock_file)\n{\n    u_char                     *shared;\n    size_t                      size;\n    ngx_shm_t                   shm;\n    ngx_http_tfs_timers_lock_t *lock;\n\n    /* cl should be equal or bigger than cache line size */\n\n    size = 128; /* ngx_http_tfs_kp_mutex */\n\n    shm.size = size;\n    shm.name.len = sizeof(\"nginx_tfs_keepalive_zone\");\n    shm.name.data = (u_char *) \"nginx_tfs_keepalive_zone\";\n    shm.log = cycle->log;\n\n    if (ngx_shm_alloc(&shm) != NGX_OK) {\n        return NULL;\n    }\n\n    shared = shm.addr;\n\n    lock = ngx_palloc(cycle->pool, sizeof(ngx_http_tfs_timers_lock_t));\n    if (lock == NULL) {\n        return NULL;\n    }\n\n    lock->ngx_http_tfs_kp_mutex_ptr = (ngx_atomic_t *) shared;\n    lock->ngx_http_tfs_kp_mutex.spin = (ngx_uint_t) -1;\n\n#if defined(nginx_version) && (nginx_version > 1001008)\n\n    if (ngx_shmtx_create(&lock->ngx_http_tfs_kp_mutex,\n                         (ngx_shmtx_sh_t *) shared, lock_file)\n        != NGX_OK)\n    {\n        return NULL;\n    }\n\n#else\n\n    if (ngx_shmtx_create(&lock->ngx_http_tfs_kp_mutex, shared, lock_file)\n        != NGX_OK)\n    {\n        return NULL;\n    }\n#endif\n\n    return lock;\n}\n\n\nngx_int_t\nngx_http_tfs_add_rcs_timers(ngx_cycle_t *cycle,\n    ngx_http_tfs_timers_data_t *data)\n{\n    ngx_event_t         *ev;\n    ngx_connection_t    *dummy;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cycle->log, 0,\n                   \"http check tfs rc servers\");\n\n    ev = ngx_pcalloc(cycle->pool, sizeof(ngx_event_t));\n    if (ev == NULL) {\n        return NGX_ERROR;\n    }\n\n    dummy = ngx_pcalloc(cycle->pool, sizeof(ngx_connection_t));\n    if (dummy == NULL) {\n        return NGX_ERROR;\n    }\n\n    dummy->data = data;\n    ev->handler = ngx_http_tfs_timeout_handler;\n    ev->log = cycle->log;\n    ev->data = dummy;\n    ev->timer_set = 0;\n\n    ngx_add_timer(ev, data->upstream->rcs_interval);\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_tfs_timers_finalize_request_handler(ngx_http_tfs_t *t)\n{\n    ngx_event_t                 *event;\n    ngx_connection_t            *dummy;\n    ngx_http_tfs_timers_data_t  *data;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, t->log, 0, \"http tfs timers finalize\");\n\n    event = t->finalize_data;\n    dummy = event->data;\n    data = dummy->data;\n\n    ngx_destroy_pool(t->pool);\n    ngx_shmtx_unlock(&data->lock->ngx_http_tfs_kp_mutex);\n    ngx_add_timer(event, data->upstream->rcs_interval);\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_tfs_timeout_handler(ngx_event_t *event)\n{\n    ngx_int_t                   rc;\n    ngx_pool_t                  *pool;\n    ngx_http_tfs_t              *t;\n    ngx_connection_t            *dummy;\n    ngx_http_request_t          *r;\n    ngx_http_tfs_timers_data_t  *data;\n\n    dummy = event->data;\n    data = dummy->data;\n    if (ngx_shmtx_trylock(&data->lock->ngx_http_tfs_kp_mutex)) {\n\n        if (ngx_queue_empty(&data->upstream->rc_ctx->sh->kp_queue)) {\n            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, event->log, 0,\n                           \"empty rc keepalive queue\");\n            ngx_shmtx_unlock(&data->lock->ngx_http_tfs_kp_mutex);\n            ngx_add_timer(event, data->upstream->rcs_interval);\n            return;\n        }\n\n        pool = ngx_create_pool(8192, event->log);\n        if (pool == NULL) {\n            ngx_shmtx_unlock(&data->lock->ngx_http_tfs_kp_mutex);\n            return;\n        }\n\n        /* fake ngx_http_request_t */\n        r = ngx_pcalloc(pool, sizeof(ngx_http_request_t));\n        if (r == NULL) {\n            ngx_shmtx_unlock(&data->lock->ngx_http_tfs_kp_mutex);\n            return;\n        }\n\n        r->pool = pool;\n        r->connection = ngx_pcalloc(pool, sizeof(ngx_connection_t));\n        if (r->connection == NULL) {\n            ngx_destroy_pool(pool);\n            ngx_shmtx_unlock(&data->lock->ngx_http_tfs_kp_mutex);\n            return;\n        }\n        r->connection->log = event->log;\n        /* in order to return from ngx_http_run_posted_requests()  */\n        r->connection->destroyed = 1;\n\n        t = ngx_pcalloc(pool, sizeof(ngx_http_tfs_t));\n        if (t == NULL) {\n            ngx_destroy_pool(pool);\n            ngx_shmtx_unlock(&data->lock->ngx_http_tfs_kp_mutex);\n            return;\n        }\n\n        t->pool = pool;\n        t->data = r;\n        t->log = event->log;\n        t->finalize_request = ngx_http_tfs_timers_finalize_request_handler;\n        t->finalize_data = event;\n\n        t->r_ctx.action.code = NGX_HTTP_TFS_ACTION_KEEPALIVE;\n        t->r_ctx.version = 1;\n        t->loc_conf = ngx_pcalloc(pool, sizeof(ngx_http_tfs_loc_conf_t));\n        if (t->loc_conf == NULL) {\n            ngx_destroy_pool(pool);\n            ngx_shmtx_unlock(&data->lock->ngx_http_tfs_kp_mutex);\n            return;\n        }\n        t->loc_conf->upstream = data->upstream;\n        t->main_conf = data->main_conf;\n\n        rc = ngx_http_tfs_init(t);\n        if (rc == NGX_ERROR) {\n            ngx_destroy_pool(pool);\n            ngx_shmtx_unlock(&data->lock->ngx_http_tfs_kp_mutex);\n            return;\n        }\n\n    } else {\n        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, event->log, 0,\n                       \"tfs kp mutex lock failed\");\n    }\n}\n"
  },
  {
    "path": "modules/ngx_http_tfs_module/ngx_http_tfs_timers.h",
    "content": "\n/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#ifndef _NGX_HTTP_TFS_TIMERS_H_INCLUDED_\n#define _NGX_HTTP_TFS_TIMERS_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n#include <ngx_http_tfs.h>\n\n\nstruct  ngx_http_tfs_timers_lock_s {\n    ngx_atomic_t                   *ngx_http_tfs_kp_mutex_ptr;\n    ngx_shmtx_t                     ngx_http_tfs_kp_mutex;\n};\n\n\nstruct  ngx_http_tfs_timers_data_s {\n    ngx_http_tfs_main_conf_t       *main_conf;\n    ngx_http_tfs_upstream_t        *upstream;\n    ngx_http_tfs_timers_lock_t     *lock;\n};\n\nngx_int_t  ngx_http_tfs_add_rcs_timers(ngx_cycle_t *cycle,\n    ngx_http_tfs_timers_data_t *data);\nngx_http_tfs_timers_lock_t *ngx_http_tfs_timers_init(ngx_cycle_t *cycle,\n    u_char *lock_file);\n\n\n#endif  /* _NGX_HTTP_TFS_TIMERS_H_INCLUDED_ */\n"
  },
  {
    "path": "modules/ngx_http_tfs_module/ngx_tfs_common.c",
    "content": "\n/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#include <ngx_tfs_common.h>\n#include <ngx_http_tfs_protocol.h>\n#include <ngx_http_tfs_errno.h>\n#include <sys/ioctl.h>\n#include <sys/socket.h>\n#include <net/if.h>\n#include <netinet/in.h>\n#include <net/if_arp.h>\n#include <ngx_md5.h>\n#include <ngx_http_tfs_peer_connection.h>\n\n\nstatic char  *week[] = { \"Sun\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\" };\nstatic char  *months[] = { \"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\",\n                           \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\" };\n\n\nngx_int_t\nngx_http_tfs_test_connect(ngx_connection_t *c)\n{\n    int        err;\n    socklen_t  len;\n\n#if (NGX_HAVE_KQUEUE)\n\n    if (ngx_event_flags & NGX_USE_KQUEUE_EVENT)  {\n        if (c->write->pending_eof) {\n            c->log->action = \"connecting to upstream\";\n            (void) ngx_connection_error(c, c->write->kq_errno,\n                \"kevent() reported that connect() failed\");\n            return NGX_ERROR;\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            c->log->action = \"connecting to upstream\";\n            (void) ngx_connection_error(c, err, \"connect() failed\");\n            return NGX_ERROR;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nuint64_t\nngx_http_tfs_generate_packet_id(void)\n{\n    static uint64_t id = 2;\n\n    if (id >= INT_MAX - 1) {\n        id = 1;\n    }\n\n    return ++id;\n}\n\n\nngx_chain_t *\nngx_http_tfs_alloc_chains(ngx_pool_t *pool, size_t count)\n{\n    ngx_uint_t               i;\n    ngx_chain_t             *cl, **ll;\n\n    ll = &cl;\n\n    for (i = 0; i < count; i++) {\n        *ll = ngx_alloc_chain_link(pool);\n        if (*ll == NULL) {\n            return NULL;\n        }\n\n        ll = &(*ll)->next;\n    }\n\n    (*ll) = NULL;\n\n    return cl;\n}\n\n\nngx_chain_t *\nngx_http_tfs_chain_get_free_buf(ngx_pool_t *p,\n    ngx_chain_t **free, size_t size)\n{\n    ngx_chain_t  *cl;\n\n    if (*free) {\n        cl = *free;\n        if ((size_t) (cl->buf->end - cl->buf->start) >= size) {\n            *free = cl->next;\n            cl->next = NULL;\n            return cl;\n        }\n    }\n\n    cl = ngx_alloc_chain_link(p);\n    if (cl == NULL) {\n        return NULL;\n    }\n\n    cl->buf = ngx_create_temp_buf(p, size);\n    if (cl->buf == NULL) {\n        return NULL;\n    }\n\n    cl->next = NULL;\n\n    return cl;\n}\n\n\nvoid\nngx_http_tfs_free_chains(ngx_chain_t **free, ngx_chain_t **out)\n{\n    ngx_chain_t              *cl;\n\n    cl = *out;\n\n    while(cl) {\n        cl->buf->pos = cl->buf->start;\n        cl->buf->last = cl->buf->start;\n        cl->buf->file_pos = 0;\n\n        cl->next = *free;\n        *free = cl;\n    }\n}\n\n\nngx_int_t\nngx_http_tfs_parse_headerin(ngx_http_request_t *r, ngx_str_t *header_name,\n    ngx_str_t *value)\n{\n    ngx_uint_t        i;\n    ngx_list_part_t  *part;\n    ngx_table_elt_t  *header;\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 (header[i].hash == 0) {\n            continue;\n        }\n\n        if (header_name->len ==  header[i].key.len\n            && ngx_strncasecmp(header[i].key.data, header_name->data,\n                               header_name->len) == 0)\n        {\n            *value = header[i].value;\n            return NGX_OK;\n        }\n    }\n\n    return NGX_DECLINED;\n}\n\n\nngx_int_t\nngx_http_tfs_compute_buf_crc(ngx_http_tfs_crc_t *t_crc, ngx_buf_t *b,\n    size_t size, ngx_log_t *log)\n{\n    u_char  *dst;\n    ssize_t  n;\n\n    if (ngx_buf_in_memory(b)) {\n        t_crc->crc = ngx_http_tfs_crc(t_crc->crc,\n                                      (const char *) (b->pos), size);\n        t_crc->data_crc = ngx_http_tfs_crc(t_crc->data_crc,\n                                           (const char *) (b->pos), size);\n        b->last = b->pos + size;\n        return NGX_OK;\n    }\n\n    dst = ngx_alloc(size, log);\n    if (dst == NULL) {\n        return 0;\n    }\n\n    n = ngx_read_file(b->file, dst, (size_t) size, b->file_pos);\n\n    if (n == NGX_ERROR) {\n        goto crc_error;\n    }\n\n    if (n != (ssize_t) size) {\n        ngx_log_error(NGX_LOG_ALERT, log, 0,\n                      ngx_read_file_n \" read only %z of %O from \\\"%s\\\"\",\n                      n, size, b->file->name.data);\n        goto crc_error;\n    }\n\n    t_crc->crc = ngx_http_tfs_crc(t_crc->crc, (const char *) dst, size);\n    t_crc->data_crc = ngx_http_tfs_crc(t_crc->data_crc,\n                                       (const char *) dst, size);\n    free(dst);\n\n    b->file_last = b->file_pos + n;\n    return NGX_OK;\n\ncrc_error:\n    free(dst);\n    return NGX_ERROR;\n}\n\n\nngx_int_t\nngx_http_tfs_peer_set_addr(ngx_pool_t *pool, ngx_http_tfs_peer_connection_t *p,\n    ngx_http_tfs_inet_t *addr)\n{\n    struct sockaddr_in     *in;\n    ngx_peer_connection_t  *peer;\n\n    if (addr == NULL) {\n        return NGX_ERROR;\n    }\n\n    in = ngx_pcalloc(pool, sizeof(struct sockaddr_in));\n    if (in == NULL) {\n        return NGX_ERROR;\n    }\n\n    in->sin_family = AF_INET;\n    in->sin_port = htons(addr->port);\n    in->sin_addr.s_addr = addr->ip;\n\n    peer = &p->peer;\n    peer->sockaddr = (struct sockaddr *) in;\n    peer->socklen = sizeof(struct sockaddr_in);\n\n    ngx_sprintf(p->peer_addr_text, \"%s:%d\",\n                inet_ntoa(in->sin_addr),\n                ntohs(in->sin_port));\n\n    return NGX_OK;\n}\n\n\nuint32_t\nngx_http_tfs_murmur_hash(u_char *data, size_t len)\n{\n    uint32_t  h, k;\n\n    h = NGX_HTTP_TFS_MUR_HASH_SEED ^ len;\n\n    while (len >= 4) {\n        k  = data[0];\n        k |= data[1] << 8;\n        k |= data[2] << 16;\n        k |= data[3] << 24;\n\n        k *= 0x5bd1e995;\n        k ^= k >> 24;\n        k *= 0x5bd1e995;\n\n        h *= 0x5bd1e995;\n        h ^= k;\n\n        data += 4;\n        len -= 4;\n    }\n\n    switch (len) {\n    case 3:\n        h ^= data[2] << 16;\n    case 2:\n        h ^= data[1] << 8;\n    case 1:\n        h ^= data[0];\n        h *= 0x5bd1e995;\n    }\n\n    h ^= h >> 13;\n    h *= 0x5bd1e995;\n    h ^= h >> 15;\n\n    return h;\n}\n\n\nngx_int_t\nngx_http_tfs_parse_inet(ngx_str_t *u, ngx_http_tfs_inet_t *addr)\n{\n    u_char    *port, *last;\n    size_t     len;\n    ngx_int_t  n;\n\n    last = u->data + u->len;\n\n    port = ngx_strlchr(u->data, last, ':');\n\n    if (port) {\n        port++;\n\n        len = last - port;\n\n        if (len == 0) {\n            return NGX_ERROR;\n        }\n\n        n = ngx_atoi(port, len);\n\n        if (n < 1 || n > 65535) {\n            return NGX_ERROR;\n        }\n\n        addr->port = n;\n\n        addr->ip = ngx_inet_addr(u->data, u->len - len - 1);\n        if (addr->ip == INADDR_NONE) {\n            return NGX_ERROR;\n        }\n\n    } else {\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nint32_t\nngx_http_tfs_raw_fsname_hash(const u_char *str, const int32_t len)\n{\n    int32_t  h, i;\n\n    h = 0;\n\n    if (str == NULL || len <=0) {\n        return 0;\n    }\n\n    for (i = 0; i < len; ++i) {\n        h += str[i];\n        h *= 7;\n    }\n\n    return (h | 0x80000000);\n}\n\n\nngx_int_t\nngx_http_tfs_get_local_ip(ngx_str_t device, struct sockaddr_in *addr)\n{\n    int           sock;\n    struct ifreq  ifr;\n\n    if((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {\n        return NGX_ERROR;\n    }\n\n    ngx_memcpy(ifr.ifr_name, device.data, device.len);\n    ifr.ifr_name[device.len] ='\\0';\n\n    if(ioctl(sock, SIOCGIFADDR, &ifr) < 0) {\n        close(sock);\n        return NGX_ERROR;\n    }\n\n    *addr = *((struct sockaddr_in *) &ifr.ifr_addr);\n\n    close(sock);\n    return NGX_OK;\n}\n\n\nngx_buf_t *\nngx_http_tfs_copy_buf_chain(ngx_pool_t *pool, ngx_chain_t *in)\n{\n    ngx_int_t    len;\n    ngx_buf_t   *buf;\n    ngx_chain_t *cl;\n\n    if (in->next == NULL) {\n        return in->buf;\n    }\n\n    len = 0;\n\n    for (cl = in; cl; cl = cl->next) {\n        len += ngx_buf_size(cl->buf);\n    }\n\n    buf = ngx_create_temp_buf(pool, len);\n\n    if (buf == NULL) {\n        return NULL;\n    }\n\n    for (cl = in; cl; cl = cl->next) {\n        buf->last = ngx_copy(buf->last, cl->buf->pos, ngx_buf_size(cl->buf));\n    }\n    return buf;\n}\n\n\nngx_int_t\nngx_http_tfs_sum_md5(ngx_chain_t *data, u_char *md5_final,\n    ssize_t *data_len, ngx_log_t *log)\n{\n    u_char    *buf;\n    ssize_t    n, buf_size;\n    ngx_md5_t  md5;\n\n    ngx_md5_init(&md5);\n\n    while(data) {\n        if (ngx_buf_in_memory(data->buf)) {\n            ngx_md5_update(&md5, data->buf->pos, ngx_buf_size(data->buf));\n            *data_len += ngx_buf_size(data->buf);\n\n        } else {\n            /* two buf */\n            buf_size = ngx_buf_size(data->buf);\n            buf = ngx_alloc(buf_size, log);\n            if (buf == NULL) {\n                return NGX_ERROR;\n            }\n\n            n = ngx_read_file(data->buf->file, buf,\n                              buf_size, data->buf->file_pos);\n            if (n == NGX_ERROR) {\n                free(buf);\n                return NGX_ERROR;\n            }\n\n            if (n != buf_size) {\n                ngx_log_error(NGX_LOG_ALERT, log, 0,\n                              ngx_read_file_n \" read only %z of %O from \\\"%s\\\"\",\n                              n, buf_size, data->buf->file->name.data);\n                free(buf);\n                return NGX_ERROR;\n            }\n\n            ngx_md5_update(&md5, buf, n);\n            free(buf);\n            *data_len += buf_size;\n        }\n\n        data = data->next;\n    }\n\n    ngx_md5_final(md5_final, &md5);\n\n    return NGX_OK;\n}\n\n\nu_char *\nngx_http_tfs_time(u_char *buf, time_t t)\n{\n    ngx_tm_t  tm;\n\n    ngx_gmtime(t, &tm);\n\n    return ngx_sprintf(buf, \"%s, %02d %s %4d %02d:%02d:%02d GMT\",\n                       week[tm.ngx_tm_wday],\n                       tm.ngx_tm_mday,\n                       months[tm.ngx_tm_mon - 1],\n                       tm.ngx_tm_year,\n                       tm.ngx_tm_hour,\n                       tm.ngx_tm_min,\n                       tm.ngx_tm_sec);\n}\n\n\nngx_int_t\nngx_http_tfs_status_message(ngx_buf_t *b, ngx_str_t *action, ngx_log_t *log)\n{\n    int32_t                     code, err_len;\n    ngx_str_t                   err;\n    ngx_http_tfs_status_msg_t  *res;\n\n    res = (ngx_http_tfs_status_msg_t *) b->pos;\n    err.len = 0;\n    code = res->code;\n\n    if (code != NGX_HTTP_TFS_STATUS_MESSAGE_OK) {\n        err_len = res->error_len;\n        if (err_len > 0) {\n            err.data = res->error_str;\n            err.len = err_len;\n        }\n\n        ngx_log_error(NGX_LOG_ERR, log, 0,\n                      \"%V failed error code (%d) err_msg(%V)\",\n                      action, code, &err);\n        if (code <= NGX_HTTP_TFS_EXIT_GENERAL_ERROR) {\n            return code;\n        }\n\n        return NGX_HTTP_TFS_EXIT_GENERAL_ERROR;\n    }\n\n    ngx_log_error(NGX_LOG_INFO, log, 0, \"%V success \", action);\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_tfs_get_parent_dir(ngx_str_t *file_path, ngx_int_t *dir_level)\n{\n    ngx_uint_t  i, last_slash_pos;\n\n    last_slash_pos = 0;\n\n    if (dir_level != NULL) {\n        *dir_level = 0;\n    }\n\n    for (i = 0; i < (file_path->len - 1); i++) {\n        if (file_path->data[i] == '/'\n            && (file_path->data[i + 1]) != '/')\n        {\n            last_slash_pos = i;\n            if (dir_level != NULL) {\n                (*dir_level)++;\n            }\n        }\n    }\n\n    return last_slash_pos + 1;\n}\n\n\nngx_int_t\nngx_http_tfs_set_output_file_name(ngx_http_tfs_t *t)\n{\n    ngx_chain_t  *cl, **ll;\n\n    if (t->json_output == NULL) {\n        t->json_output = ngx_http_tfs_json_init(t->log, t->pool);\n        if (t->json_output == NULL) {\n            return NGX_ERROR;\n        }\n    }\n\n    for (cl = t->out_bufs, ll = &t->out_bufs; cl; cl = cl->next) {\n        ll = &cl->next;\n    }\n\n    /* set final return file name */\n    if (t->r_ctx.fsname.cluster_id == 0) {\n        t->r_ctx.fsname.cluster_id = t->file.cluster_id;\n    }\n    t->file_name.len = NGX_HTTP_TFS_FILE_NAME_LEN;\n    if (t->r_ctx.simple_name) {\n        t->file_name.len += t->r_ctx.file_suffix.len;\n    }\n    t->file_name.data = ngx_palloc(t->pool, t->file_name.len);\n    ngx_memcpy(t->file_name.data,\n               ngx_http_tfs_raw_fsname_get_name(&t->r_ctx.fsname,\n                                                t->is_large_file,\n                                                t->r_ctx.simple_name),\n               NGX_HTTP_TFS_FILE_NAME_LEN);\n\n    if (t->r_ctx.simple_name) {\n        if (t->r_ctx.file_suffix.data != NULL) {\n            ngx_memcpy(t->file_name.data + NGX_HTTP_TFS_FILE_NAME_LEN,\n                       t->r_ctx.file_suffix.data, t->r_ctx.file_suffix.len);\n        }\n    }\n\n    /* set dup_file_name(put to tair) */\n    if (t->use_dedup) {\n        t->dedup_ctx.dup_file_name.len =\n            NGX_HTTP_TFS_FILE_NAME_LEN + t->r_ctx.file_suffix.len;\n        t->dedup_ctx.dup_file_name.data =\n            ngx_palloc(t->pool, t->dedup_ctx.dup_file_name.len);\n        if (t->dedup_ctx.dup_file_name.data == NULL) {\n            return NGX_ERROR;\n        }\n        ngx_memcpy(t->dedup_ctx.dup_file_name.data,\n                   ngx_http_tfs_raw_fsname_get_name(&t->r_ctx.fsname, 0, 0),\n                   NGX_HTTP_TFS_FILE_NAME_LEN);\n        if (t->r_ctx.file_suffix.data != NULL) {\n            ngx_memcpy(t->dedup_ctx.dup_file_name.data\n                       + NGX_HTTP_TFS_FILE_NAME_LEN,\n                       t->r_ctx.file_suffix.data, t->r_ctx.file_suffix.len);\n        }\n    }\n\n    cl = ngx_http_tfs_json_file_name(t->json_output, &t->file_name);\n    if (cl == NULL) {\n        return NGX_ERROR;\n    }\n\n    *ll = cl;\n    return NGX_OK;\n}\n\n\nlong long\nngx_http_tfs_atoll(u_char *line, size_t n)\n{\n    long long value;\n\n    if (n == 0) {\n        return NGX_ERROR;\n    }\n\n    for (value = 0; n--; line++) {\n        if (*line < '0' || *line > '9') {\n            return NGX_ERROR;\n        }\n\n        value = value * 10 + (*line - '0');\n    }\n\n    if (value < 0) {\n        return NGX_ERROR;\n\n    } else {\n        return value;\n    }\n}\n\n\nngx_int_t\nngx_http_tfs_atoull(u_char *line, size_t n, unsigned long long *value)\n{\n    unsigned long long res;\n\n    for (res = 0; n--; line++) {\n        unsigned int val;\n\n        if (*line < '0' || *line > '9') {\n            return NGX_ERROR;\n        }\n\n        val = *line - '0';\n\n        /*\n         * Check for overflow\n         */\n\n        if (res & (~0ull << 60)) {\n\n            if (res > ((ULLONG_MAX - val) / 10)) {\n                return NGX_ERROR;\n            }\n        }\n\n        res = res * 10 + val;\n    }\n\n    *value = res;\n\n    return NGX_OK;\n}\n\n\nvoid *\nngx_http_tfs_prealloc(ngx_pool_t *pool, void *p,\n    size_t old_size, size_t new_size)\n{\n    void *new;\n\n    if (p == NULL) {\n        return ngx_palloc(pool, new_size);\n    }\n\n    if (new_size == 0) {\n        if ((u_char *) p + old_size == pool->d.last) {\n           pool->d.last = p;\n        } else {\n           ngx_pfree(pool, p);\n        }\n\n        return NULL;\n    }\n\n    if ((u_char *) p + old_size == pool->d.last\n        && (u_char *) p + new_size <= pool->d.end)\n    {\n        pool->d.last = (u_char *) p + new_size;\n        return p;\n    }\n\n    new = ngx_palloc(pool, new_size);\n    if (new == NULL) {\n        return NULL;\n    }\n\n    ngx_memcpy(new, p, old_size);\n\n    ngx_pfree(pool, p);\n\n    return new;\n}\n\n\nuint64_t\nngx_http_tfs_get_chain_buf_size(ngx_chain_t *data)\n{\n    uint64_t      size;\n    ngx_chain_t  *cl;\n\n    size = 0;\n    cl = data;\n    while (cl) {\n        size += ngx_buf_size(cl->buf);\n        cl = cl->next;\n    }\n\n    return size;\n}\n\n\nvoid\nngx_http_tfs_dump_segment_data(ngx_http_tfs_segment_data_t *segment,\n    ngx_log_t *log)\n{\n    ngx_log_debug7(NGX_LOG_DEBUG_HTTP, log, 0,\n                   \"=========dump segment data=========\\n\"\n                   \"block id: %uD, file id: %uL, \"\n                   \"offset: %L, size: %uL, crc: %uD, \"\n                   \"oper_offset: %uD, oper_size: %uL\",\n                   segment->segment_info.block_id,\n                   segment->segment_info.file_id,\n                   segment->segment_info.offset,\n                   segment->segment_info.size,\n                   segment->segment_info.crc,\n                   segment->oper_offset,\n                   segment->oper_size);\n}\n\n\nngx_http_tfs_t *\nngx_http_tfs_alloc_st(ngx_http_tfs_t *t)\n{\n    ngx_buf_t       *b;\n    ngx_http_tfs_t  *st;\n\n    st = t->free_sts;\n\n    if (st) {\n        t->free_sts = st->next;\n        return st;\n    }\n\n    st = ngx_palloc(t->pool, sizeof(ngx_http_tfs_t));\n    if (st == NULL) {\n        return NULL;\n    }\n    ngx_memcpy(st, t, sizeof(ngx_http_tfs_t));\n    st->parent = t;\n\n    /* each st should have independent send/recv buf/peer/out_bufs,\n     * and we only care about data server and name server(retry need)\n     */\n\n    /* recv(from upstream servers) bufs */\n    st->recv_chain = ngx_http_tfs_alloc_chains(t->pool, 2);\n    if (st->recv_chain == NULL) {\n        return NULL;\n    }\n    st->header_buffer.start = NULL;\n\n    /* peers */\n    st->tfs_peer_servers = ngx_pcalloc(t->pool,\n        sizeof(ngx_http_tfs_peer_connection_t) * NGX_HTTP_TFS_SERVER_COUNT);\n    if (st->tfs_peer_servers == NULL) {\n        return NULL;\n    }\n\n    /* name server related */\n    ngx_memcpy(&st->tfs_peer_servers[NGX_HTTP_TFS_NAME_SERVER],\n               &t->tfs_peer_servers[NGX_HTTP_TFS_NAME_SERVER],\n               sizeof(ngx_http_tfs_peer_connection_t));\n    st->tfs_peer_servers[NGX_HTTP_TFS_NAME_SERVER].body_buffer.start = NULL;\n    st->tfs_peer_servers[NGX_HTTP_TFS_NAME_SERVER].peer.connection = NULL;\n\n    /* data server related */\n    ngx_memcpy(&st->tfs_peer_servers[NGX_HTTP_TFS_DATA_SERVER],\n               &t->tfs_peer_servers[NGX_HTTP_TFS_DATA_SERVER],\n               sizeof(ngx_http_tfs_peer_connection_t));\n    b = &st->tfs_peer_servers[NGX_HTTP_TFS_DATA_SERVER].body_buffer;\n    if (t->r_ctx.action.code == NGX_HTTP_TFS_ACTION_WRITE_FILE) {\n        b->start = NULL;\n\n    } else if (t->r_ctx.action.code == NGX_HTTP_TFS_ACTION_READ_FILE){\n        /* alloc buf that can hold all segment's data,\n         * so that ngx_http_tfs_process_buf_overflow would not happen\n         */\n        b->start = ngx_palloc(t->pool, NGX_HTTP_TFS_MAX_FRAGMENT_SIZE);\n        if (b->start == NULL) {\n            return NULL;\n        }\n\n        b->pos = b->start;\n        b->last = b->start;\n        b->end = b->start + NGX_HTTP_TFS_MAX_FRAGMENT_SIZE;\n        b->temporary = 1;\n    }\n\n    st->output.filter_ctx = &st->writer;\n\n    st->is_large_file = NGX_HTTP_TFS_NO;\n    st->file.segment_count = 1;\n\n    return st;\n}\n\n\nngx_int_t\nngx_http_tfs_get_content_type(u_char *data, ngx_str_t *type)\n{\n    if (memcmp(data, \"GIF\", 3) == 0) {\n        ngx_str_set(type, \"image/gif\");\n        return NGX_OK;\n    }\n\n    if (memcmp(data, \"\\xff\\xd8\\xff\", 3) == 0) {\n        ngx_str_set(type, \"image/jpeg\");\n        return NGX_OK;\n    }\n\n    if (memcmp(data, \"\\x89\\x50\\x4e\\x47\\x0d\\x0a\\x1a\\x0a\", 8) == 0) {\n        ngx_str_set(type, \"image/png\");\n        return NGX_OK;\n    }\n\n    if ((memcmp(data, \"CWS\", 3) == 0)\n              ||(memcmp(data, \"FWS\", 3) == 0))\n    {\n        ngx_str_set(type, \"application/x-shockwave-flash\");\n        return NGX_OK;\n    }\n\n    if ((memcmp(data, \"BM\", 2) == 0)\n              ||(memcmp(data, \"BA\", 2) == 0)\n              ||(memcmp(data, \"CI\", 2) == 0)\n              ||(memcmp(data, \"CP\", 2) == 0)\n              ||(memcmp(data, \"IC\", 2) == 0)\n              ||(memcmp(data, \"PI\", 2) == 0))\n    {\n        ngx_str_set(type, \"image/bmp\");\n        return NGX_OK;\n    }\n\n    if ((memcmp(data, \"\\115\\115\\000\\052\", 4) == 0)\n            ||(memcmp(data, \"\\111\\111\\052\\000\", 4) == 0)\n            ||(memcmp(data, \"\\115\\115\\000\\053\\000\\010\\000\\000\", 8) == 0)\n            ||(memcmp(data, \"\\111\\111\\053\\000\\010\\000\\000\\000\", 8) == 0))\n    {\n        ngx_str_set(type, \"image/tiff\");\n        return NGX_OK;\n    }\n\n    return NGX_AGAIN;\n}\n\n\nngx_msec_int_t\nngx_http_tfs_get_request_time(ngx_http_tfs_t *t)\n{\n    ngx_time_t                *tp;\n    ngx_msec_int_t             ms;\n    ngx_http_request_t        *r;\n#if (T_NGX_RET_CACHE)\n    struct timeval             tv;\n    ngx_http_core_loc_conf_t  *clcf;\n#endif\n\n    r = t->data;\n\n#if (T_NGX_RET_CACHE)\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n    if (clcf->request_time_cache) {\n        tp = ngx_timeofday();\n        ms = (ngx_msec_int_t)\n                 ((tp->sec - r->start_sec) * 1000 + (tp->msec - r->start_msec));\n    } else {\n        ngx_gettimeofday(&tv);\n        ms = (tv.tv_sec - r->start_sec) * 1000\n                 + (tv.tv_usec / 1000 - r->start_msec);\n    }\n\n#else \n\n    tp = ngx_timeofday();\n    ms = (ngx_msec_int_t)\n             ((tp->sec - r->start_sec) * 1000 + (tp->msec - r->start_msec));\n#endif\n\n    ms = ngx_max(ms, 0);\n\n    return ms;\n}\n\n\nngx_int_t\nngx_chain_add_copy_with_buf(ngx_pool_t *pool, ngx_chain_t **chain, ngx_chain_t *in)\n{\n    ngx_buf_t    *b;\n    ngx_chain_t  *cl, **ll;\n\n    ll = chain;\n    for (cl = *chain; cl; cl = cl->next) {\n        ll = &cl->next;\n    }\n\n    while (in) {\n        b = ngx_alloc_buf(pool);\n        if (b == NULL) {\n            return NGX_ERROR;\n        }\n        ngx_memcpy(b, in->buf, sizeof(ngx_buf_t));\n        cl = ngx_alloc_chain_link(pool);\n        if (cl == NULL) {\n            return NGX_ERROR;\n        }\n        cl->buf = b;\n        *ll = cl;\n        ll = &cl->next;\n        in = in->next;\n    }\n\n    *ll = NULL;\n\n    return NGX_OK;\n}\n\n\nvoid\nngx_http_tfs_wrap_raw_file_info(ngx_http_tfs_raw_file_info_t *file_info,\n    ngx_http_tfs_raw_file_stat_t *file_stat)\n{\n    if (file_info != NULL && file_stat != NULL) {\n        file_stat->id = file_info->id;\n        file_stat->offset = file_info->offset;\n        file_stat->size = file_info->size;\n        file_stat->u_size = file_info->u_size;\n        file_stat->modify_time = file_info->modify_time;\n        file_stat->create_time = file_info->create_time;\n        file_stat->flag = file_info->flag;\n        file_stat->crc = file_info->crc;\n    }\n}\n"
  },
  {
    "path": "modules/ngx_http_tfs_module/ngx_tfs_common.h",
    "content": "\n/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#ifndef _NGX_HTTP_TFS_COMMON_H_INCLUDED_\n#define _NGX_HTTP_TFS_COMMON_H_INCLUDED_\n\n\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n#define NGX_PACKED __attribute__ ((__packed__))\n\n#define NGX_HTTP_TFS_HEADER                           0\n#define NGX_HTTP_TFS_BODY                             1\n\n#define NGX_HTTP_TFS_YES                              1\n#define NGX_HTTP_TFS_NO                               0\n#define NGX_HTTP_TFS_AGAIN                            -20\n#define NGX_HTTP_TFS_MAX_RETRY_COUNT                  2\n\n#define NGX_HTTP_TFS_NGINX_APPKEY                     \"tfs\"\n#define NGX_HTTP_TFS_DEFAULT_APPID                    1\n#define NGX_HTTP_TFS_RCS_LOCK_FILE                    \"nginx_rcs.lock\"\n\n#define NGX_HTTP_TFS_MD5_RESULT_LEN                   16\n#define NGX_HTTP_TFS_DUPLICATE_KEY_SIZE             \\\n    (sizeof(uint32_t) + NGX_HTTP_TFS_MD5_RESULT_LEN)\n#define NGX_HTTP_TFS_DUPLICATE_VALUE_BASE_SIZE        sizeof(int32_t)\n#define NGX_HTTP_TFS_DUPLICATE_INITIAL_MAGIC_VERSION  0x0fffffff\n\n/* rcs, ns, ds, rs, ms */\n#define NGX_HTTP_TFS_SERVER_COUNT                     5\n#define NGX_HTTP_TFS_METASERVER_COUNT                 10240\n/* master_conifg_server;slave_config_server;group */\n#define NGX_HTTP_TFS_TAIR_SERVER_ADDR_PART_COUNT      3\n/* master && slave */\n#define NGX_HTTP_TFS_TAIR_CONFIG_SERVER_COUNT         2\n\n#define NGX_HTTP_TFS_KEEPALIVE_ACTION                 \"keepalive\"\n\n#define NGX_HTTP_TFS_MAX_READ_FILE_SIZE               (512 * 1024)\n#define NGX_HTTP_TFS_USE_LARGE_FILE_SIZE              (15 * 1024 * 1024)\n#define NGX_HTTP_TFS_MAX_SIZE                         (ULLONG_MAX - 1)\n\n#define NGX_HTTP_TFS_DEFAULT_BODY_BUFFER_SIZE         (2 * 1024 * 1024)\n#define NGX_HTTP_TFS_ZERO_BUF_SIZE                    (512 * 1024)\n#define NGX_HTTP_TFS_INIT_FILE_HOLE_COUNT             5\n\n#define NGX_HTTP_TFS_APPEND_OFFSET                     -1\n\n/* tfs file name standard name length */\n#define NGX_HTTP_TFS_SMALL_FILE_KEY_CHAR              'T'\n#define NGX_HTTP_TFS_LARGE_FILE_KEY_CHAR              'L'\n#define NGX_HTTP_TFS_FILE_NAME_LEN                     18\n#define NGX_HTTP_TFS_FILE_NAME_BUFF_LEN                19\n#define NGX_HTTP_TFS_FILE_NAME_EXCEPT_SUFFIX_LEN       12\n#define NGX_HTTP_TFS_MAX_FILE_NAME_LEN                 256\n#define NGX_HTTP_TFS_MAX_SUFFIX_LEN                    109 /* 128 - 19 */\n\n#define NGX_HTTP_TFS_MAX_RCSERVER_COUNT                5\n#define NGX_HTTP_TFS_MAX_CLUSTER_COUNT                 10\n#define NGX_HTTP_TFS_MAX_CLUSTER_ID_COUNT              10\n\n#define NGX_HTTP_TFS_CMD_GET_CLUSTER_ID_NS             20\n#define NGX_HTTP_TFS_CMD_GET_GROUP_COUNT               22\n#define NGX_HTTP_TFS_CMD_GET_GROUP_SEQ                 23\n\n#define NGX_HTTP_TFS_GMT_TIME_SIZE                  \\\n    (sizeof(\"Mon, 28 Sep 1970 06:00:00 GMT\") - 1)\n\n#define NGX_HTTP_TFS_MAX_FRAGMENT_SIZE                 (2 * 1024 * 1024)\n#define NGX_HTTP_TFS_MAX_BATCH_COUNT                   8\n\n\n#define NGX_HTTP_TFS_MUR_HASH_SEED                     97\n\n#define NGX_HTTP_TFS_CLIENT_VERSION                    \"NGINX\"\n\n#define NGX_HTTP_TFS_MIN_TIMER_DELAY                    1000\n\n#define NGX_HTTP_TFS_READ_STAT_NORMAL                   0\n#define NGX_HTTP_TFS_READ_STAT_FORCE                    1\n\n#define NGX_HTTP_TFS_IMAGE_TYPE_SIZE                    8\n\n#define NGX_BSWAP_64(x)                         \\\n    ((((x) & 0xff00000000000000ull) >> 56)      \\\n    | (((x) & 0x00ff000000000000ull) >> 40)     \\\n    | (((x) & 0x0000ff0000000000ull) >> 24)     \\\n    | (((x) & 0x000000ff00000000ull) >> 8)      \\\n    | (((x) & 0x00000000ff000000ull) << 8)      \\\n    | (((x) & 0x0000000000ff0000ull) << 24)     \\\n    | (((x) & 0x000000000000ff00ull) << 40)     \\\n    | (((x) & 0x00000000000000ffull) << 56))\n\n#define ngx_http_tfs_clear_buf(b) \\\n    (b)->pos = (b)->start;        \\\n    (b)->last = (b)->start;\n\n#if (NGX_HAVE_BIG_ENDIAN)\n\n#define ngx_hton64(x) x\n\n#define ngx_ntoh64(x) x\n\n#else\n\n#define ngx_hton64(x)                           \\\n    NGX_BSWAP_64(x)\n\n#define ngx_ntoh64(x)                           \\\n    NGX_BSWAP_64(x)\n\n\n#endif\n\ntypedef struct ngx_http_tfs_s ngx_http_tfs_t;\ntypedef struct ngx_http_tfs_peer_connection_s ngx_http_tfs_peer_connection_t;\n\ntypedef struct ngx_http_tfs_main_conf_s ngx_http_tfs_main_conf_t;\ntypedef struct ngx_http_tfs_loc_conf_s ngx_http_tfs_loc_conf_t;\ntypedef struct ngx_http_tfs_upstream_s ngx_http_tfs_upstream_t;\n\ntypedef struct ngx_http_tfs_inet_s ngx_http_tfs_inet_t;\ntypedef struct ngx_http_tfs_meta_hh_s  ngx_http_tfs_meta_hh_t;\n\ntypedef struct ngx_http_tfs_segment_data_s ngx_http_tfs_segment_data_t;\n\ntypedef struct ngx_http_tfs_timers_lock_s ngx_http_tfs_timers_lock_t;\ntypedef struct ngx_http_tfs_timers_data_s ngx_http_tfs_timers_data_t;\n\ntypedef struct {\n    uint64_t           size;\n} ngx_http_tfs_stat_info_t;\n\n\ntypedef struct {\n    uint32_t     crc;\n    uint32_t     data_crc;\n} ngx_http_tfs_crc_t;\n\n\ntypedef struct {\n    uint16_t             code;\n    ngx_str_t            msg;\n} ngx_http_tfs_action_t;\n\n\nstruct ngx_http_tfs_meta_hh_s {\n    uint64_t       app_id;\n    uint64_t       user_id;\n};\n\n\nstruct ngx_http_tfs_inet_s {\n    uint32_t       ip;\n    uint32_t       port;\n};\n\ntypedef struct {\n    ngx_http_tfs_inet_t          table[NGX_HTTP_TFS_METASERVER_COUNT];\n    uint64_t                     version;\n} ngx_http_tfs_meta_table_t;\n\n\ntypedef struct {\n    uint64_t                   id;\n    int32_t                    offset;\n    int64_t                    size;\n    int64_t                    u_size;\n    int32_t                    modify_time;\n    int32_t                    create_time;\n    int32_t                    flag;\n    uint32_t                   crc;\n} NGX_PACKED ngx_http_tfs_raw_file_stat_t;\n\n\ntypedef struct {\n    uint64_t                   id;\n    int32_t                    offset;\n    int32_t                    size;\n    int32_t                    u_size;\n    int32_t                    modify_time;\n    int32_t                    create_time;\n    int32_t                    flag;\n    uint32_t                   crc;\n} NGX_PACKED ngx_http_tfs_raw_file_info_t;\n\n\ntypedef struct {\n    int64_t                    pid;\n    int64_t                    id;\n    uint32_t                   create_time;\n    uint32_t                   modify_time;\n    uint64_t                   size;\n    uint16_t                   ver_no;\n} NGX_PACKED ngx_http_tfs_custom_file_info_t;\n\n\ntypedef enum {\n    NGX_HTTP_TFS_RC_SERVER = 0,\n    NGX_HTTP_TFS_NAME_SERVER,\n    NGX_HTTP_TFS_DATA_SERVER,\n    NGX_HTTP_TFS_ROOT_SERVER,\n    NGX_HTTP_TFS_META_SERVER,\n} ngx_http_tfs_peer_server_e;\n\n\ntypedef enum {\n    NGX_HTTP_TFS_STATE_WRITE_START = 0,\n    NGX_HTTP_TFS_STATE_WRITE_GET_META_TABLE,\n    NGX_HTTP_TFS_STATE_WRITE_CLUSTER_ID_MS,\n    NGX_HTTP_TFS_STATE_WRITE_GET_GROUP_COUNT,\n    NGX_HTTP_TFS_STATE_WRITE_GET_GROUP_SEQ,\n    NGX_HTTP_TFS_STATE_WRITE_CLUSTER_ID_NS,\n    NGX_HTTP_TFS_STATE_WRITE_GET_BLK_INFO,\n    NGX_HTTP_TFS_STATE_WRITE_STAT_DUP_FILE,\n    NGX_HTTP_TFS_STATE_WRITE_CREATE_FILE_NAME,\n    NGX_HTTP_TFS_STATE_WRITE_WRITE_DATA,\n    NGX_HTTP_TFS_STATE_WRITE_CLOSE_FILE,\n    NGX_HTTP_TFS_STATE_WRITE_WRITE_MS,\n    NGX_HTTP_TFS_STATE_WRITE_DONE,\n    NGX_HTTP_TFS_STATE_WRITE_DELETE_DATA,\n} ngx_http_tfs_state_write_e;\n\n\ntypedef enum {\n    NGX_HTTP_TFS_STATE_READ_START = 0,\n    NGX_HTTP_TFS_STATE_READ_GET_META_TABLE,\n    NGX_HTTP_TFS_STATE_READ_GET_FRAG_INFO,\n    NGX_HTTP_TFS_STATE_READ_GET_BLK_INFO,\n    NGX_HTTP_TFS_STATE_READ_READ_DATA,\n    NGX_HTTP_TFS_STATE_READ_DONE,\n} ngx_http_tfs_state_read_e;\n\n\ntypedef enum {\n    NGX_HTTP_TFS_STATE_REMOVE_START = 0,\n    NGX_HTTP_TFS_STATE_REMOVE_GET_META_TABLE,\n    NGX_HTTP_TFS_STATE_REMOVE_GET_FRAG_INFO,\n    NGX_HTTP_TFS_STATE_REMOVE_GET_GROUP_COUNT,\n    NGX_HTTP_TFS_STATE_REMOVE_GET_GROUP_SEQ,\n    NGX_HTTP_TFS_STATE_REMOVE_GET_BLK_INFO,\n    NGX_HTTP_TFS_STATE_REMOVE_STAT_FILE,\n    NGX_HTTP_TFS_STATE_REMOVE_READ_META_SEGMENT,\n    NGX_HTTP_TFS_STATE_REMOVE_DELETE_DATA,\n    NGX_HTTP_TFS_STATE_REMOVE_NOTIFY_MS,\n    NGX_HTTP_TFS_STATE_REMOVE_DONE,\n} ngx_http_tfs_state_remove_e;\n\n\ntypedef enum {\n    NGX_HTTP_TFS_STATE_STAT_START = 0,\n    NGX_HTTP_TFS_STATE_STAT_GET_BLK_INFO,\n    NGX_HTTP_TFS_STATE_STAT_STAT_FILE,\n    NGX_HTTP_TFS_STATE_STAT_DONE,\n} ngx_http_tfs_state_stat_e;\n\n\ntypedef enum {\n    NGX_HTTP_TFS_STATE_ACTION_START = 0,\n    NGX_HTTP_TFS_STATE_ACTION_GET_META_TABLE,\n    NGX_HTTP_TFS_STATE_ACTION_PROCESS,\n    NGX_HTTP_TFS_STATE_ACTION_DONE,\n} ngx_http_tfs_state_action_e;\n\n\nstatic inline uint32_t\nngx_http_tfs_crc(uint32_t crc, const char *data, size_t len)\n{\n    size_t i;\n\n    for (i = 0; i < len; ++i) {\n        crc = (crc >> 8) ^ ngx_crc32_table256[(crc ^ *data++) & 0xff];\n    }\n\n    return crc;\n}\n\nngx_chain_t *ngx_http_tfs_alloc_chains(ngx_pool_t *pool, size_t count);\nngx_chain_t *ngx_http_tfs_chain_get_free_buf(ngx_pool_t *p,\n    ngx_chain_t **free, size_t size);\nvoid ngx_http_tfs_free_chains(ngx_chain_t **free, ngx_chain_t **out);\n\nngx_int_t ngx_http_tfs_test_connect(ngx_connection_t *c);\nuint64_t ngx_http_tfs_generate_packet_id(void);\n\nngx_int_t ngx_http_tfs_parse_headerin(ngx_http_request_t *r,\n    ngx_str_t *header_name, ngx_str_t *value);\n\nngx_int_t ngx_http_tfs_compute_buf_crc(ngx_http_tfs_crc_t *t_crc, ngx_buf_t *b,\n    size_t size, ngx_log_t *log);\n\nngx_int_t ngx_http_tfs_peer_set_addr(ngx_pool_t *pool,\n    ngx_http_tfs_peer_connection_t *p, ngx_http_tfs_inet_t *addr);\n\nuint32_t ngx_http_tfs_murmur_hash(u_char *data, size_t len);\n\nngx_int_t ngx_http_tfs_parse_inet(ngx_str_t *u, ngx_http_tfs_inet_t *addr);\nint32_t ngx_http_tfs_raw_fsname_hash(const u_char *str, const int32_t len);\nngx_int_t ngx_http_tfs_get_local_ip(ngx_str_t device, struct sockaddr_in *addr);\nngx_buf_t *ngx_http_tfs_copy_buf_chain(ngx_pool_t *pool, ngx_chain_t *in);\nngx_int_t ngx_http_tfs_sum_md5(ngx_chain_t *body, u_char *md5_final,\n    ssize_t *body_size, ngx_log_t *log);\nu_char *ngx_http_tfs_time(u_char *buf, time_t t);\n\nngx_int_t ngx_http_tfs_status_message(ngx_buf_t *b, ngx_str_t *action,\n    ngx_log_t *log);\nngx_int_t ngx_http_tfs_get_parent_dir(ngx_str_t *file_path,\n    ngx_int_t *dir_level);\nngx_int_t ngx_http_tfs_set_output_file_name(ngx_http_tfs_t *t);\nlong long ngx_http_tfs_atoll(u_char *line, size_t n);\nngx_int_t ngx_http_tfs_atoull(u_char *line, size_t n,\n    unsigned long long *value);\nvoid *ngx_http_tfs_prealloc(ngx_pool_t *pool, void *p, size_t old_size,\n    size_t new_size);\nuint64_t ngx_http_tfs_get_chain_buf_size(ngx_chain_t *data);\n\nvoid ngx_http_tfs_dump_segment_data(ngx_http_tfs_segment_data_t *segment,\n    ngx_log_t *log);\nngx_http_tfs_t *ngx_http_tfs_alloc_st(ngx_http_tfs_t *t);\n\n#define ngx_http_tfs_free_st(t)           \\\n        t->next = t->parent->free_sts;   \\\n        t->parent->free_sts = t;         \\\n\n\nngx_int_t ngx_http_tfs_get_content_type(u_char *data, ngx_str_t *type);\nngx_msec_int_t ngx_http_tfs_get_request_time(ngx_http_tfs_t *t);\nngx_int_t ngx_chain_add_copy_with_buf(ngx_pool_t *pool, ngx_chain_t **chain,\n    ngx_chain_t *in);\nvoid ngx_http_tfs_wrap_raw_file_info(ngx_http_tfs_raw_file_info_t *file_info,\n    ngx_http_tfs_raw_file_stat_t *file_stat);\n\n\n#endif  /* _NGX_HTTP_TFS_COMMON_H_INCLUDED_ */\n"
  },
  {
    "path": "modules/ngx_http_trim_filter_module/config",
    "content": "ngx_addon_name=ngx_http_trim_filter_module\nHTTP_TRIM_FILTER_SRCS=\"$ngx_addon_dir/ngx_http_trim_filter_module.c\"\n\nif test -n \"$ngx_module_link\"; then\n    ngx_module_type=HTTP_AUX_FILTER\n    ngx_module_name=ngx_http_trim_filter_module\n    ngx_module_incs=\n    ngx_module_deps=\n    ngx_module_srcs=$HTTP_TRIM_FILTER_SRCS\n    ngx_module_libs=\n\n    . auto/module\nelse\n    HTTP_AUX_FILTER_MODULES=\"$HTTP_AUX_FILTER_MODULES ngx_http_trim_filter_module\"\n    NGX_ADDON_SRCS=\"$NGX_ADDON_SRCS $HTTP_TRIM_FILTER_SRCS\"\nfi\n"
  },
  {
    "path": "modules/ngx_http_trim_filter_module/ngx_http_trim_filter_module.c",
    "content": "\n/*\n *  Copyright (C) 2010-2013 Alibaba Group Holding Limited\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\n#define NGX_HTTP_TRIM_FLAG      \"http_trim\"\n\n#define NGX_HTTP_TRIM_SAVE_SLASH        -1\n#define NGX_HTTP_TRIM_SAVE_JSCSS        -2\n#define NGX_HTTP_TRIM_SAVE_SPACE        -3\n#define NGX_HTTP_TRIM_SAVE_HACKCSS      -4\n#define NGX_HTTP_TRIM_SAVE_JAVASCRIPT   -5\n\n#define NGX_HTTP_TRIM_TAG_PRE            1\n#define NGX_HTTP_TRIM_TAG_STYLE          2\n#define NGX_HTTP_TRIM_TAG_SCRIPT         3\n#define NGX_HTTP_TRIM_TAG_TEXTAREA       4\n\ntypedef struct {\n    ngx_hash_t                   types;\n    ngx_array_t                 *types_keys;\n\n    ngx_http_complex_value_t    *js;\n    ngx_http_complex_value_t    *css;\n    ngx_http_complex_value_t    *trim;\n} ngx_http_trim_loc_conf_t;\n\n\ntypedef struct {\n    u_char          prev;\n\n    ngx_chain_t    *in;\n    ngx_chain_t    *free;\n    ngx_chain_t    *busy;\n\n    size_t          looked;\n    size_t          saved_comment;\n\n    off_t           tin;\n    off_t           tout;\n\n    ngx_int_t       tag;\n    ngx_int_t       saved;\n    ngx_int_t       count;\n    ngx_uint_t      state;\n\n    unsigned        js_enable:1;\n    unsigned        css_enable:1;\n} ngx_http_trim_ctx_t;\n\n\ntypedef enum {\n    trim_state_text = 0,\n    trim_state_text_whitespace,         /* \\r \\t ' ' */\n    trim_state_tag,                     /* <  */\n    trim_state_tag_text,\n    trim_state_tag_attribute,\n    trim_state_tag_whitespace,          /* \\r \\n \\t ' ' */\n    trim_state_tag_single_quote,        /* '  */\n    trim_state_tag_double_quote,        /* \"  */\n    trim_state_tag_s,                   /* <s */\n    trim_state_tag_pre_begin,           /* <pre */\n    trim_state_tag_pre,\n    trim_state_tag_pre_angle,\n    trim_state_tag_pre_nest,\n    trim_state_tag_pre_end,             /* <pre    </pre> */\n    trim_state_tag_textarea_begin,      /* <textarea */\n    trim_state_tag_textarea_end,        /* <textarea </textarea> */\n    trim_state_tag_style_begin,         /* <style */\n    trim_state_tag_style_end,           /* <style    </style> */\n    trim_state_tag_style_css_end,       /* <style    </style> */\n    trim_state_tag_style_css_text,      /* <style type=\"text/css\" */\n    trim_state_tag_style_css_whitespace,\n    trim_state_tag_style_css_single_quote,\n    trim_state_tag_style_css_single_quote_esc,\n    trim_state_tag_style_css_double_quote,\n    trim_state_tag_style_css_double_quote_esc,\n    trim_state_tag_style_css_comment,\n    trim_state_tag_style_css_comment_begin,\n    trim_state_tag_style_css_comment_end,\n    trim_state_tag_style_css_comment_begin_empty,\n    trim_state_tag_style_css_comment_empty,\n    trim_state_tag_style_css_comment_begin_hack,\n    trim_state_tag_style_css_comment_hack,\n    trim_state_tag_style_css_comment_hack_text,\n    trim_state_tag_style_css_comment_hack_text_begin,\n    trim_state_tag_style_css_comment_hack_text_end,\n    trim_state_tag_style_css_comment_hack_text_last,\n    trim_state_tag_script_begin,        /* <script */\n    trim_state_tag_script_end,          /* <script   </script> */\n    trim_state_tag_script_js_end,       /* <script   </script> */\n    trim_state_tag_script_js_text,      /* <script type=\"text/javascript\" */\n    trim_state_tag_script_js_single_quote,\n    trim_state_tag_script_js_single_quote_esc,\n    trim_state_tag_script_js_double_quote,\n    trim_state_tag_script_js_double_quote_esc,\n    trim_state_tag_script_js_re_begin,\n    trim_state_tag_script_js_re,\n    trim_state_tag_script_js_re_esc,\n    trim_state_tag_script_js_whitespace,\n    trim_state_tag_script_js_comment_begin,\n    trim_state_tag_script_js_single_comment,\n    trim_state_tag_script_js_single_comment_end,\n    trim_state_tag_script_js_multi_comment,\n    trim_state_tag_script_js_multi_comment_end,\n    trim_state_comment_begin,           /* <!-- */\n    trim_state_comment_ie_begin,        /* <!--[if */\n    trim_state_comment_hack_begin,\n    trim_state_comment_end,             /* <!--  --> */\n    trim_state_comment_ie_end,          /* <!--[if  <![endif]--> */\n    trim_state_comment_hack_end,\n} ngx_http_trim_state_e;\n\n\n\n/* '(' ',' '=' ':' '[' '!' '&' '|' '?' ';' '>' '~' '*' '{' */\n\nstatic uint32_t   trim_js_prefix[] = {\n    0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */\n\n                /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #\"!  */\n    0xec001542, /* 1110 1100 0000 0000  0001 0101 0100 0010 */\n\n                /* _^]\\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */\n    0x08000000, /* 0000 1000 0000 0000  0000 0000 0000 0000 */\n\n                /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */\n    0x58000000, /* 0101 1000 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\n\n/* ';' '>' '{' '}' ',' ':' */\n\nstatic uint32_t   trim_css_prefix[] = {\n    0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */\n\n                /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #\"!  */\n    0x4c001000, /* 0100 1100 0000 0000  0001 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    0x28000000, /* 0010 1000 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\nstatic ngx_int_t ngx_http_trim_parse(ngx_http_request_t *r, ngx_chain_t *in,\n    ngx_http_trim_ctx_t *ctx);\n\nstatic ngx_int_t ngx_http_trim_add_variables(ngx_conf_t *cf);\nstatic ngx_int_t ngx_http_trim_bytes_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_trim_original_bytes_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\n\nstatic void *ngx_http_trim_create_loc_conf(ngx_conf_t *cf);\nstatic char *ngx_http_trim_merge_loc_conf(ngx_conf_t *cf,\n    void *parent, void *child);\nstatic ngx_int_t ngx_http_trim_filter_init(ngx_conf_t *cf);\n\n\nstatic ngx_command_t  ngx_http_trim_commands[] = {\n\n    { ngx_string(\"trim\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_http_set_complex_value_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_trim_loc_conf_t, trim),\n      NULL },\n\n    { ngx_string(\"trim_js\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_http_set_complex_value_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_trim_loc_conf_t, js),\n      NULL },\n\n    { ngx_string(\"trim_css\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_http_set_complex_value_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_trim_loc_conf_t, css),\n      NULL },\n\n    { ngx_string(\"trim_types\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,\n      ngx_http_types_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_trim_loc_conf_t, types_keys),\n      &ngx_http_html_default_types[0] },\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_trim_filter_module_ctx = {\n    ngx_http_trim_add_variables,             /* preconfiguration */\n    ngx_http_trim_filter_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    ngx_http_trim_create_loc_conf,           /* create location configuration */\n    ngx_http_trim_merge_loc_conf             /* merge location configuration */\n};\n\n\nngx_module_t  ngx_http_trim_filter_module = {\n    NGX_MODULE_V1,\n    &ngx_http_trim_filter_module_ctx,        /* module context */\n    ngx_http_trim_commands,                  /* 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_http_output_header_filter_pt ngx_http_next_header_filter;\nstatic ngx_http_output_body_filter_pt   ngx_http_next_body_filter;\n\n\nstatic ngx_str_t ngx_http_trim_pre = ngx_string(\"</pre>\");\nstatic ngx_str_t ngx_http_trim_style = ngx_string(\"</style>\");\nstatic ngx_str_t ngx_http_trim_script = ngx_string(\"</script>\");\nstatic ngx_str_t ngx_http_trim_style_css = ngx_string(\"text/css\");\nstatic ngx_str_t ngx_http_trim_script_js = ngx_string(\"text/javascript\");\nstatic ngx_str_t ngx_http_trim_comment = ngx_string(\"-->\");\nstatic ngx_str_t ngx_http_trim_textarea = ngx_string(\"</textarea>\");\nstatic ngx_str_t ngx_http_trim_comment_ie = ngx_string(\"[if\");\nstatic ngx_str_t ngx_http_trim_comment_ie_end = ngx_string(\"<![endif]-->\");\n\nstatic ngx_str_t ngx_http_trim_saved_html = ngx_string(\"<!--[if\");\nstatic ngx_str_t ngx_http_trim_saved_jscss = ngx_string(\"/**\");\nstatic ngx_str_t ngx_http_trim_saved_css_hack = ngx_string(\"/*\\\\*\");\n\nstatic ngx_http_variable_t ngx_http_trim_vars[] = {\n\n    { ngx_string(\"trim_bytes\"), NULL,\n      ngx_http_trim_bytes_variable, 0, 0, 0 },\n\n    { ngx_string(\"trim_original_bytes\"), NULL,\n      ngx_http_trim_original_bytes_variable, 0, 0, 0 },\n\n    { ngx_null_string, NULL, NULL, 0, 0, 0 }\n};\n\n\nstatic ngx_int_t\nngx_http_trim_header_filter(ngx_http_request_t *r)\n{\n    ngx_int_t                  rc;\n    ngx_str_t                  flag;\n    ngx_http_trim_ctx_t       *ctx;\n    ngx_http_trim_loc_conf_t  *conf;\n\n    conf = ngx_http_get_module_loc_conf(r, ngx_http_trim_filter_module);\n\n    if (!conf->trim\n        || r->headers_out.status != NGX_HTTP_OK\n        || (r->method & NGX_HTTP_HEAD)\n        || r->headers_out.content_length_n == 0\n        || (r->headers_out.content_encoding\n            && r->headers_out.content_encoding->value.len)\n        || ngx_http_test_content_type(r, &conf->types) == NULL)\n    {\n        return ngx_http_next_header_filter(r);\n    }\n\n    rc = ngx_http_arg(r, (u_char *) NGX_HTTP_TRIM_FLAG,\n                      sizeof(NGX_HTTP_TRIM_FLAG) - 1, &flag);\n\n    if(rc == NGX_OK\n       && flag.len == sizeof(\"off\") - 1\n       && ngx_strncmp(flag.data, \"off\", sizeof(\"off\") - 1) == 0)\n    {\n        return ngx_http_next_header_filter(r);\n    }\n\n    if (ngx_http_complex_value(r, conf->trim, &flag) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    if (!(flag.len == sizeof(\"on\") - 1\n          && ngx_strncmp(flag.data, \"on\", sizeof(\"on\") - 1) == 0))\n    {\n        return ngx_http_next_header_filter(r);\n    }\n\n    ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_trim_ctx_t));\n    if (ctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (conf->js) {\n        if (ngx_http_complex_value(r, conf->js, &flag) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        if (flag.len == sizeof(\"on\") - 1\n            && ngx_strncmp(flag.data, \"on\", sizeof(\"on\") - 1) == 0)\n        {\n            ctx->js_enable = 1;\n        }\n    }\n\n    if (conf->css) {\n        if (ngx_http_complex_value(r, conf->css, &flag) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        if (flag.len == sizeof(\"on\") - 1\n            && ngx_strncmp(flag.data, \"on\", sizeof(\"on\") - 1) == 0)\n        {\n            ctx->css_enable = 1;\n        }\n    }\n\n    ctx->prev = ' ';\n\n    ngx_http_set_ctx(r, ctx, ngx_http_trim_filter_module);\n\n    r->filter_need_temporary = 1;\n    r->main_filter_need_in_memory = 1;\n\n    ngx_http_clear_content_length(r);\n    ngx_http_clear_accept_ranges(r);\n\n    return ngx_http_next_header_filter(r);\n}\n\n\nstatic ngx_int_t\nngx_http_trim_body_filter(ngx_http_request_t *r, ngx_chain_t *in)\n{\n    ngx_int_t             rc;\n    ngx_chain_t          *cl, *ln, *out, **ll;\n    ngx_http_trim_ctx_t  *ctx;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http trim filter\");\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_trim_filter_module);\n    if (ctx == NULL) {\n        return ngx_http_next_body_filter(r, in);\n    }\n\n    if (in == NULL) {\n        return ngx_http_next_body_filter(r, in);\n    }\n\n    ctx->in = NULL;\n    if (ngx_chain_add_copy(r->pool, &ctx->in, in) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    out = NULL;\n    ll = &out;\n\n    for (ln = ctx->in; ln; ln = ln->next) {\n        ctx->tin += ln->buf->last - ln->buf->pos;\n\n        rc = ngx_http_trim_parse(r, ln, ctx);\n        if (rc == NGX_ERROR) {\n            return rc;\n        }\n\n        if (ctx->saved) {\n            cl = ngx_chain_get_free_buf(r->pool, &ctx->free);\n            if (cl == NULL) {\n                return NGX_ERROR;\n            }\n\n            cl->buf->tag = (ngx_buf_tag_t) &ngx_http_trim_filter_module;\n            cl->buf->memory = 1;\n\n            if (ctx->saved > 0) {\n                cl->buf->pos = ngx_http_trim_saved_html.data;\n                cl->buf->last = cl->buf->pos + ctx->saved;\n\n            } else if (ctx->saved == NGX_HTTP_TRIM_SAVE_SLASH) {\n                cl->buf->pos = ngx_http_trim_saved_jscss.data;\n                cl->buf->last = cl->buf->pos + 1;\n\n            } else if (ctx->saved == NGX_HTTP_TRIM_SAVE_SPACE) {\n                cl->buf->pos = (u_char *) \" \";\n                cl->buf->last = cl->buf->pos + 1;\n\n            } else if (ctx->saved == NGX_HTTP_TRIM_SAVE_JSCSS) {\n                cl->buf->pos = ngx_http_trim_saved_jscss.data;\n                cl->buf->last = cl->buf->pos + ngx_http_trim_saved_jscss.len;\n\n            } else if (ctx->saved == NGX_HTTP_TRIM_SAVE_HACKCSS) {\n                cl->buf->pos = ngx_http_trim_saved_css_hack.data;\n                cl->buf->last = cl->buf->pos + ngx_http_trim_saved_css_hack.len;\n\n            } else if (ctx->saved == NGX_HTTP_TRIM_SAVE_JAVASCRIPT) {\n                cl->buf->pos = ngx_http_trim_script.data;\n                cl->buf->last = cl->buf->pos + ngx_http_trim_script.len - 1;\n            }\n\n            *ll = cl;\n            ll = &cl->next;\n\n            ctx->tout += cl->buf->last - cl->buf->pos;\n\n            ctx->saved = 0;\n        }\n\n        if(ln->buf->in_file\n           && (ln->buf->file_last - ln->buf->file_pos)\n               != (off_t) (ln->buf->last - ln->buf->pos))\n        {\n            ln->buf->in_file = 0;\n        }\n\n        if (ngx_buf_size(ln->buf) == 0) {\n            if (ln->buf->last_buf) {\n                cl = ngx_chain_get_free_buf(r->pool, &ctx->free);\n                if (cl == NULL) {\n                    return NGX_ERROR;\n                }\n\n                ngx_memzero(cl->buf, sizeof(ngx_buf_t));\n                cl->buf->tag = (ngx_buf_tag_t) &ngx_http_trim_filter_module;\n                cl->buf->last_buf = 1;\n\n                *ll = cl;\n                ll = &cl->next;\n\n            }  else {\n                if (ln->next == NULL) {\n                    *ll = NULL;\n                }\n            }\n\n        } else {\n            *ll = ln;\n            ll = &ln->next;\n        }\n\n    }\n\n    if (out == NULL) {\n        return NGX_OK;\n    }\n\n    rc = ngx_http_next_body_filter(r, out);\n\n    ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &out,\n                           (ngx_buf_tag_t) &ngx_http_trim_filter_module);\n\n    return rc;\n}\n\n\nstatic ngx_int_t\nngx_http_trim_parse(ngx_http_request_t *r, ngx_chain_t *in,\n    ngx_http_trim_ctx_t *ctx)\n{\n    u_char                    *read, *write, ch, look;\n    size_t                     size;\n    ngx_buf_t                 *b, *buf;\n\n    b = in->buf;\n    buf = in->buf;\n    size = ngx_buf_size(buf);\n\n    if (size == 0) {\n        return NGX_OK;\n    }\n\n    if (!buf->temporary) {\n        b = ngx_create_temp_buf(r->pool, size);\n        if (b == NULL) {\n            return NGX_ERROR;\n        }\n\n        b->sync = buf->sync;\n        b->flush = buf->flush;\n        b->last_buf = buf->last_buf;\n        b->last_in_chain = buf->last_in_chain;\n    }\n\n    for (write = b->pos, read = buf->pos; read < buf->last; read++) {\n\n        ch = ngx_tolower(*read);\n\n        switch (ctx->state) {\n\n        case trim_state_text:\n            switch (ch) {\n            case '\\r':\n                continue;\n            case '\\n':\n                ctx->state = trim_state_text_whitespace;\n                if (ctx->prev == '\\n') {\n                    continue;\n\n                } else {\n                    break;\n                }\n            case '\\t':\n            case ' ':\n                ctx->state = trim_state_text_whitespace;\n                continue;\n            case '<':\n                ctx->state = trim_state_tag;\n                ctx->saved_comment = 1;\n                continue;\n            default:\n                break;\n            }\n            break;\n\n        case trim_state_tag:\n            switch (ch) {\n            case '\\r':\n            case '\\n':\n            case '\\t':\n            case ' ':\n                ctx->state = trim_state_text_whitespace;\n                break;\n            case '!':\n                ctx->state = trim_state_comment_begin;\n                ctx->looked = 0;        /* --> */\n                ctx->saved_comment++;\n                continue;\n            case 'p':\n                ctx->state = trim_state_tag_pre_begin;\n                ctx->looked = 3;        /* </pre> */\n                break;\n            case 't':\n                ctx->state = trim_state_tag_textarea_begin;\n                ctx->looked = 3;       /* </textarea> */\n                break;\n            case 's':\n                ctx->state = trim_state_tag_s;\n                break;\n            case '<':\n                break;\n            case '>':\n                ctx->state = trim_state_text;\n                break;\n            default:\n                if ((ch >= 'a' && ch <= 'z') || ch == '/') {\n                    ctx->state = trim_state_tag_text;\n\n                } else {\n                    ctx->state = trim_state_text;\n                }\n                break;\n            }\n\n            if ((size_t) (read - buf->pos) >= ctx->saved_comment) {\n                write = ngx_cpymem(write, ngx_http_trim_saved_html.data,\n                                   ctx->saved_comment);\n\n            } else {\n                ctx->saved = ctx->saved_comment;\n            }\n\n            if (ctx->state == trim_state_tag\n                || ctx->state == trim_state_text_whitespace)\n            {\n               ctx->prev = '<';\n               continue;\n            }\n\n            break;\n\n        case trim_state_tag_text:\n            switch (ch) {\n            case '\\r':\n            case '\\n':\n            case '\\t':\n            case ' ':\n                ctx->state = trim_state_tag_whitespace;\n                continue;\n            case '>':\n                ctx->state = trim_state_text;\n                break;\n            default:\n                break;\n            }\n            break;\n\n        case trim_state_tag_attribute:\n            switch (ch) {\n            case '\\r':\n            case '\\n':\n            case '\\t':\n            case ' ':\n                if (ctx->prev != '=') {\n                    ctx->state = trim_state_tag_whitespace;\n                }\n                continue;\n            case '\\'':\n                ctx->state = trim_state_tag_single_quote;\n                break;\n            case '\"':\n                ctx->state = trim_state_tag_double_quote;\n                break;\n            case '>':\n                if (ctx->tag == NGX_HTTP_TRIM_TAG_PRE) {\n                    ctx->state = trim_state_tag_pre;\n\n                } else if (ctx->tag == NGX_HTTP_TRIM_TAG_TEXTAREA) {\n                    ctx->state = trim_state_tag_textarea_end;\n\n                } else if (ctx->tag == NGX_HTTP_TRIM_TAG_SCRIPT) {\n                    if (ctx->js_enable\n                        && ctx->looked == ngx_http_trim_script_js.len)\n                    {\n                        ctx->state = trim_state_tag_script_js_text;\n\n                    } else {\n                        ctx->state = trim_state_tag_script_end;\n                    }\n\n                } else if (ctx->tag == NGX_HTTP_TRIM_TAG_STYLE) {\n                    if (ctx->css_enable\n                        && ctx->looked == ngx_http_trim_style_css.len)\n                    {\n                        ctx->state = trim_state_tag_style_css_text;\n\n                    } else {\n                        ctx->state = trim_state_tag_style_end;\n                    }\n\n                } else {\n                    ctx->state = trim_state_text;\n                }\n\n                ctx->tag = 0;\n                ctx->looked = 0;\n                break;\n            default:\n                break;\n            }\n            break;\n\n        case trim_state_tag_s:\n            switch (ch) {\n            case '\\r':\n            case '\\n':\n            case '\\t':\n            case ' ':\n                ctx->state = trim_state_tag_whitespace;\n                continue;\n            case 't':\n                ctx->state = trim_state_tag_style_begin;\n                ctx->looked = 4;    /* </style> */\n                break;\n            case 'c':\n                ctx->state = trim_state_tag_script_begin;\n                ctx->looked = 4;    /* </script> */\n                break;\n            case '>':\n                ctx->state = trim_state_text;\n                break;\n            default:\n                ctx->state = trim_state_tag_text;\n                break;\n            }\n            break;\n\n        case trim_state_comment_begin:\n            look = ngx_http_trim_comment.data[ctx->looked++];\n            if (ch == look) {\n                if (ctx->looked == ngx_http_trim_comment.len - 1) { /* --> */\n                    ctx->state = trim_state_comment_hack_begin;\n                    ctx->looked = 0;\n                }\n\n                ctx->saved_comment++;\n                continue;\n            }\n\n            switch (ch) {\n            case '\\r':\n            case '\\n':\n            case '\\t':\n            case ' ':\n                ctx->state = trim_state_tag_whitespace;\n                continue;\n            case '>':\n                ctx->state = trim_state_text;\n                break;\n            default:\n                ctx->state = trim_state_tag_text;\n                break;\n            }\n\n            if ((size_t) (read - buf->pos) >= ctx->saved_comment) {\n                write = ngx_cpymem(write, ngx_http_trim_saved_html.data,\n                                   ctx->saved_comment);\n\n            } else {\n                ctx->saved = ctx->saved_comment;\n            }\n\n            break;\n\n        case trim_state_comment_hack_begin:\n            switch (ch) {\n            case '#':\n                ctx->state = trim_state_comment_hack_end;\n                ctx->looked = 0;\n                break;\n            case 'e':\n                ctx->state = trim_state_comment_hack_end;\n                ctx->looked = 0;\n                break;\n            case '[':\n                ctx->state = trim_state_comment_ie_begin;\n                ctx->looked = 1;\n                ctx->saved_comment++;\n                continue;\n            case '-':\n                ctx->state = trim_state_comment_end;\n                ctx->looked = 1;\n                continue;\n            default:\n                ctx->state = trim_state_comment_end;\n                ctx->looked = 0;\n                continue;\n            }\n\n            if ((size_t) (read - buf->pos) >= ctx->saved_comment) {\n                write = ngx_cpymem(write, ngx_http_trim_saved_html.data,\n                                   ctx->saved_comment);\n\n            } else {\n                ctx->saved = ctx->saved_comment;\n            }\n\n            break;\n\n        case trim_state_comment_ie_begin:\n            look = ngx_http_trim_comment_ie.data[ctx->looked++];\n            if (ch == look) {\n                if (ctx->looked == ngx_http_trim_comment_ie.len) { /* [if */\n                    ctx->state = trim_state_comment_ie_end;\n                    ctx->looked = 0;\n\n                    if ((size_t) (read - buf->pos) >= ctx->saved_comment) {\n                        write = ngx_cpymem(write, ngx_http_trim_saved_html.data,\n                                           ctx->saved_comment);\n\n                    } else {\n                         ctx->saved = ctx->saved_comment;\n                    }\n\n                    break;\n                }\n\n                ctx->saved_comment++;\n                continue;\n            }\n\n            switch (ch) {\n            case '-':\n                ctx->state = trim_state_comment_end;\n                ctx->looked = 1;\n                break;\n            default:\n                ctx->state = trim_state_comment_end;\n                ctx->looked = 0;\n                break;\n            }\n\n            continue;\n\n        case trim_state_tag_pre_begin:\n            look = ngx_http_trim_pre.data[ctx->looked++];    /* <pre> */\n            if (ch == look) {\n                if (ctx->looked == ngx_http_trim_pre.len) {\n                    ctx->state = trim_state_tag_pre;\n                    ctx->count = 1;\n                    ctx->looked = 0;\n                }\n                break;\n            }\n\n            switch (ch) {\n            case '\\r':\n            case '\\n':\n            case '\\t':\n            case ' ':\n                if (ctx->looked == ngx_http_trim_pre.len) {\n                    ctx->tag = NGX_HTTP_TRIM_TAG_PRE;\n                    ctx->count = 1;\n                }\n\n                ctx->state = trim_state_tag_whitespace;\n                ctx->looked = 0;\n                continue;\n            case '>':\n                ctx->state = trim_state_text;\n                break;\n            default:\n                ctx->state = trim_state_tag_text;\n                break;\n            }\n            break;\n\n        case trim_state_tag_textarea_begin:\n            look = ngx_http_trim_textarea.data[ctx->looked++]; /* <textarea> */\n            if (ch == look) {\n                if (ctx->looked == ngx_http_trim_textarea.len) {\n                    ctx->state = trim_state_tag_textarea_end;\n                    ctx->looked = 0;\n                }\n                break;\n            }\n\n            switch (ch) {\n            case '\\r':\n            case '\\n':\n            case '\\t':\n            case ' ':\n                if (ctx->looked == ngx_http_trim_textarea.len) {\n                    ctx->tag = NGX_HTTP_TRIM_TAG_TEXTAREA;\n                }\n\n                ctx->state = trim_state_tag_whitespace;\n                ctx->looked = 0;\n                continue;\n            case '>':\n                ctx->state = trim_state_text;\n                break;\n            default:\n                ctx->state = trim_state_tag_text;\n                break;\n            }\n            break;\n\n        case trim_state_tag_script_begin:\n            look = ngx_http_trim_script.data[ctx->looked++];    /* <script> */\n            if (ch == look) {\n                if (ctx->looked == ngx_http_trim_script.len) {\n                    if (ctx->js_enable) {\n                        ctx->state = trim_state_tag_script_js_text;\n\n                    } else {\n                        ctx->state = trim_state_tag_script_end;\n                    }\n\n                    ctx->looked = 0;\n                }\n                break;\n            }\n\n            switch (ch) {\n            case '\\r':\n            case '\\n':\n            case '\\t':\n            case ' ':\n                if (ctx->looked == ngx_http_trim_script.len) {\n                    ctx->tag = NGX_HTTP_TRIM_TAG_SCRIPT;\n                }\n\n                ctx->state = trim_state_tag_whitespace;\n                ctx->looked = 0;\n                continue;\n            case '>':\n                ctx->state = trim_state_text;\n                break;\n            default:\n                ctx->state = trim_state_tag_text;\n                break;\n            }\n            break;\n\n        case trim_state_tag_script_js_text:\n            switch (ch) {\n            case '\\r':\n                continue;\n            case '\\n':\n            case '\\t':\n            case ' ':\n                ctx->state = trim_state_tag_script_js_whitespace;\n                if (trim_js_prefix[ctx->prev >> 5] & (1 << (ctx->prev & 0x1f))\n                    || ctx->prev == ch)\n                {\n                    continue;\n\n                } else {\n                    break;\n                }\n            case '\\'':\n                ctx->state = trim_state_tag_script_js_single_quote;\n                break;\n            case '\"':\n                ctx->state = trim_state_tag_script_js_double_quote;\n                break;\n            case '<':\n                ctx->state = trim_state_tag_script_js_end;\n                ctx->looked = 1;\n                break;\n            case '/':\n                if (trim_js_prefix[ctx->prev >> 5] & (1 << (ctx->prev & 0x1f))\n                    || ctx->prev == '+' || ctx->prev == '-')\n                {\n                    ctx->state = trim_state_tag_script_js_re_begin;\n\n                } else {\n                    ctx->state = trim_state_tag_script_js_comment_begin;\n                }\n                continue;\n            default:\n                break;\n            }\n            break;\n\n        case trim_state_tag_script_js_single_quote:\n            switch (ch) {\n            case '\\\\':\n                ctx->state = trim_state_tag_script_js_single_quote_esc;\n                break;\n            case '\\'':\n                ctx->state = trim_state_tag_script_js_text;\n                break;\n            default:\n                break;\n            }\n            break;\n\n        case trim_state_tag_script_js_double_quote:\n            switch (ch) {\n            case '\\\\':\n                ctx->state = trim_state_tag_script_js_double_quote_esc;\n                break;\n            case '\"':\n                ctx->state = trim_state_tag_script_js_text;\n                break;\n            default:\n                break;\n            }\n            break;\n\n        case trim_state_tag_script_js_single_quote_esc:\n            ctx->state = trim_state_tag_script_js_single_quote;\n            break;\n\n        case trim_state_tag_script_js_double_quote_esc:\n            ctx->state = trim_state_tag_script_js_double_quote;\n            break;\n\n        case trim_state_tag_script_js_re_begin:\n            switch (ch) {\n            case '/':\n                ctx->state = trim_state_tag_script_js_single_comment;\n                continue;\n            case '*':\n                ctx->state = trim_state_tag_script_js_multi_comment;\n                continue;\n            case '\\\\':\n                ctx->state = trim_state_tag_script_js_re_esc;\n                if (read > buf->pos) {\n                    *write++ = '/';\n\n                } else {\n                    ctx->saved = NGX_HTTP_TRIM_SAVE_SLASH;\n                }\n                break;\n            default:\n                ctx->state = trim_state_tag_script_js_re;\n                if (read > buf->pos) {\n                    *write++ = '/';\n\n                } else {\n                    ctx->saved = NGX_HTTP_TRIM_SAVE_SLASH;\n                }\n                break;\n             }\n             break;\n\n        case trim_state_tag_script_js_re:\n            switch (ch) {\n            case '/':\n                ctx->state = trim_state_tag_script_js_text;\n                break;\n            case '\\\\':\n                ctx->state = trim_state_tag_script_js_re_esc;\n                break;\n            default:\n                break;\n            }\n            break;\n\n        case trim_state_tag_script_js_re_esc:\n            ctx->state = trim_state_tag_script_js_re;\n            break;\n\n        case trim_state_tag_script_js_comment_begin:\n            switch (ch) {\n            case '/':\n                ctx->state = trim_state_tag_script_js_single_comment;\n                continue;\n            case '*':\n                ctx->state = trim_state_tag_script_js_multi_comment;\n                continue;\n            default:\n                ctx->state = trim_state_tag_script_js_text;\n                if (read > buf->pos) {\n                    *write++ = '/';\n\n                } else {\n                    ctx->saved = NGX_HTTP_TRIM_SAVE_SLASH;\n                }\n                break;\n             }\n             break;\n\n        case trim_state_tag_script_js_single_comment:\n            switch (ch) {\n            case '<':\n                ctx->looked = 1;\n                ctx->state = trim_state_tag_script_js_single_comment_end;\n                continue;\n            case '\\n':\n                ctx->state = trim_state_tag_script_js_text;\n                if (trim_js_prefix[ctx->prev >> 5] & (1 << (ctx->prev & 0x1f))\n                    || ctx->prev == ch)\n                {\n                    continue;\n\n                } else {\n                    break;\n                }\n            default:\n                continue;\n            }\n            break;\n\n        case trim_state_tag_script_js_single_comment_end:\n            look = ngx_http_trim_script.data[ctx->looked++];\n            if (ch == look) {\n                if (ctx->looked == ngx_http_trim_script.len) {\n                    ctx->state = trim_state_text;\n                    ctx->looked = 0;\n\n                    if ((size_t) (read - buf->pos)\n                        >= ngx_http_trim_script.len - 1)\n                    {\n                        write = ngx_cpymem(write, ngx_http_trim_script.data,\n                                           ngx_http_trim_script.len - 1);\n\n                    } else {\n                        ctx->saved = NGX_HTTP_TRIM_SAVE_JAVASCRIPT;\n                    }\n\n                    break;\n                }\n\n                continue;\n            }\n\n            switch (ch) {\n            case '\\r':\n            case '\\n':\n            case '\\t':\n            case ' ':\n                if (ctx->looked == ngx_http_trim_script.len) {\n                    ctx->state = trim_state_tag_whitespace;\n\n                    if ((size_t) (read - buf->pos)\n                        >= ngx_http_trim_script.len - 1)\n                    {\n                        write = ngx_cpymem(write, ngx_http_trim_script.data,\n                                           ngx_http_trim_script.len - 1);\n\n                    } else {\n                        ctx->saved = NGX_HTTP_TRIM_SAVE_JAVASCRIPT;\n                    }\n\n                    ctx->prev = 't';\n                    ctx->looked = 0;\n                    continue;\n                }\n                ctx->looked = 0;\n                break;\n            case '<':\n                ctx->looked = 1;\n                break;\n            default:\n                ctx->state = trim_state_tag_script_js_single_comment;\n                ctx->looked = 0;\n                break;\n            }\n\n            continue;\n\n        case trim_state_tag_script_js_multi_comment:\n            switch (ch) {\n            case '*':\n                ctx->state = trim_state_tag_script_js_multi_comment_end;\n                break;\n            default:\n                break;\n            }\n            continue;\n\n        case trim_state_tag_script_js_multi_comment_end:\n            switch (ch) {\n            case '/':\n                ctx->state = trim_state_tag_script_js_text;\n                break;\n            case '*':\n                break;\n            default:\n                ctx->state = trim_state_tag_script_js_multi_comment;\n                break;\n            }\n            continue;\n\n        case trim_state_tag_script_end:\n            look = ngx_http_trim_script.data[ctx->looked++];\n            if (ch == look) {\n                if (ctx->looked == ngx_http_trim_script.len) {\n                    ctx->state = trim_state_text;\n                }\n                break;\n            }\n\n            switch (ch) {\n            case '\\r':\n            case '\\n':\n            case '\\t':\n            case ' ':\n                if (ctx->looked == ngx_http_trim_script.len) {\n                    ctx->state = trim_state_tag_whitespace;\n                    ctx->looked = 0;\n                    continue;\n                }\n\n                ctx->looked = 0;\n                break;\n            case '<':\n                ctx->looked = 1;\n                break;\n            default:\n                ctx->looked = 0;\n                break;\n            }\n            break;\n\n        case trim_state_tag_script_js_end:\n            look = ngx_http_trim_script.data[ctx->looked++];\n            if (ch == look) {\n                if (ctx->looked == ngx_http_trim_script.len) {\n                    ctx->state = trim_state_text;\n                }\n                break;\n            }\n\n            switch (ch) {\n            case '\\r':\n            case '\\n':\n            case '\\t':\n            case ' ':\n                if (ctx->looked == ngx_http_trim_script.len) {\n                    ctx->state = trim_state_tag_whitespace;\n                    ctx->looked = 0;\n                    continue;\n                }\n\n                ctx->looked = 0;\n                break;\n            case '<':\n                ctx->looked = 1;\n                break;\n            default:\n                ctx->state = trim_state_tag_script_js_text;\n                ctx->looked = 0;\n                break;\n            }\n            break;\n\n        case trim_state_tag_script_js_whitespace:\n            switch (ch) {\n            case '\\n':\n                if (trim_js_prefix[ctx->prev >> 5] & (1 << (ctx->prev & 0x1f))\n                    || ctx->prev == ch)\n                {\n                    continue;\n\n                } else {\n                    break;\n                }\n            case '\\r':\n            case '\\t':\n            case ' ':\n                 continue;\n            case '\\'':\n                ctx->state = trim_state_tag_script_js_single_quote;\n                break;\n            case '\"':\n                ctx->state = trim_state_tag_script_js_double_quote;\n                break;\n            case '<':\n                ctx->state = trim_state_tag_script_js_end;\n                ctx->looked = 1;\n                break;\n            case '/':\n                if (trim_js_prefix[ctx->prev >> 5] & (1 << (ctx->prev & 0x1f))\n                    || ctx->prev == '+' || ctx->prev == '-')\n                {\n                    ctx->state = trim_state_tag_script_js_re_begin;\n\n                } else {\n                    ctx->state = trim_state_tag_script_js_comment_begin;\n                }\n                continue;\n            default:\n                ctx->state = trim_state_tag_script_js_text;\n                break;\n            }\n            break;\n\n        case trim_state_tag_style_begin:\n            look = ngx_http_trim_style.data[ctx->looked++];    /* <style> */\n            if (ch == look) {\n                if (ctx->looked == ngx_http_trim_style.len) {\n                    if (ctx->css_enable) {\n                        ctx->state = trim_state_tag_style_css_text;\n\n                    } else {\n                        ctx->state = trim_state_tag_style_end;\n                    }\n\n                    ctx->looked = 0;\n                }\n                break;\n            }\n\n            switch (ch) {\n            case '\\r':\n            case '\\n':\n            case '\\t':\n            case ' ':\n                if (ctx->looked == ngx_http_trim_style.len) {\n                    ctx->tag = NGX_HTTP_TRIM_TAG_STYLE;\n                }\n\n                ctx->state = trim_state_tag_whitespace;\n                ctx->looked = 0;\n                continue;\n            case '>':\n                ctx->state = trim_state_text;\n                break;\n            default:\n                ctx->state = trim_state_tag_text;\n                break;\n            }\n            break;\n\n        case trim_state_tag_style_css_text:\n            switch (ch) {\n            case '\\r':\n                continue;\n            case '\\n':\n            case '\\t':\n            case ' ':\n                if (!(trim_css_prefix[ctx->prev >> 5] & (1 << (ctx->prev & 0x1f)))) {\n                    ctx->state = trim_state_tag_style_css_whitespace;\n                }\n                continue;\n            case '\\'':\n                ctx->state = trim_state_tag_style_css_single_quote;\n                break;\n            case '\"':\n                ctx->state = trim_state_tag_style_css_double_quote;\n                break;\n            case '<':\n                ctx->state = trim_state_tag_style_css_end;\n                ctx->looked = 1;\n                break;\n            case '/':\n                ctx->state = trim_state_tag_style_css_comment_begin;\n                continue;\n            default:\n                break;\n            }\n            break;\n\n        case trim_state_tag_style_css_single_quote:\n            switch (ch) {\n            case '\\\\':\n                ctx->state = trim_state_tag_style_css_single_quote_esc;\n                break;\n            case '\\'':\n                ctx->state = trim_state_tag_style_css_text;\n                break;\n            default:\n                break;\n            }\n            break;\n\n        case trim_state_tag_style_css_double_quote:\n            switch (ch) {\n            case '\\\\':\n                ctx->state = trim_state_tag_style_css_double_quote_esc;\n                break;\n            case '\"':\n                ctx->state = trim_state_tag_style_css_text;\n                break;\n            default:\n                break;\n            }\n            break;\n\n        case trim_state_tag_style_css_single_quote_esc:\n            ctx->state = trim_state_tag_style_css_single_quote;\n            break;\n\n        case trim_state_tag_style_css_double_quote_esc:\n            ctx->state = trim_state_tag_style_css_double_quote;\n            break;\n\n        case trim_state_tag_style_css_comment_begin:\n            switch (ch) {\n            case '*':\n                ctx->state = trim_state_tag_style_css_comment_begin_empty;\n                continue;\n            case '/':\n                ctx->state = trim_state_tag_style_css_comment_begin;\n                break;\n            default:\n                ctx->state = trim_state_tag_style_css_text;\n                break;\n            }\n\n            if (read > buf->pos) {\n                *write++ = '/';\n\n            } else {\n                ctx->saved = NGX_HTTP_TRIM_SAVE_SLASH;\n            }\n\n            if (ch == '/') {\n                continue;\n\n            }\n            break;\n\n        case trim_state_tag_style_css_comment_begin_empty:\n            switch (ch) {\n            case '*':\n                ctx->state = trim_state_tag_style_css_comment_empty;\n                break;\n            case '\\\\':\n                ctx->state = trim_state_tag_style_css_comment_begin_hack;\n                break;\n            default:\n                ctx->state = trim_state_tag_style_css_comment;\n                break;\n            }\n            continue;\n\n        case trim_state_tag_style_css_comment:\n            switch (ch) {\n            case '*':\n                ctx->state = trim_state_tag_style_css_comment_end;\n                break;\n            case '\\\\':\n                ctx->state = trim_state_tag_style_css_comment_begin_hack;\n                break;\n            default:\n                break;\n            }\n            continue;\n\n        case trim_state_tag_style_css_comment_empty:\n            switch (ch) {\n            case '/':\n                ctx->state = trim_state_tag_style_css_text;\n\n                if ((size_t) (read - buf->pos)\n                    >= ngx_http_trim_saved_jscss.len)\n                {\n                    write = ngx_cpymem(write, ngx_http_trim_saved_jscss.data,\n                                       ngx_http_trim_saved_jscss.len);\n\n                } else {\n                     ctx->saved = NGX_HTTP_TRIM_SAVE_JSCSS;\n                }\n                break;\n            case '*':\n                ctx->state = trim_state_tag_style_css_comment_end;\n                continue;\n            case '\\\\':\n                ctx->state = trim_state_tag_style_css_comment_begin_hack;\n                continue;\n            default:\n                ctx->state = trim_state_tag_style_css_comment;\n                continue;\n            }\n            break;\n\n        case trim_state_tag_style_css_comment_begin_hack:\n            switch (ch) {\n            case '*':\n                ctx->state = trim_state_tag_style_css_comment_hack;\n                break;\n            default:\n                ctx->state = trim_state_tag_style_css_comment;\n                break;\n            }\n            continue;\n\n        case trim_state_tag_style_css_comment_hack:\n            switch (ch) {\n            case '/':\n                ctx->state = trim_state_tag_style_css_comment_hack_text;\n\n                if ((size_t) (read - buf->pos)\n                    >= ngx_http_trim_saved_css_hack.len)\n                {\n                    write = ngx_cpymem(write, ngx_http_trim_saved_css_hack.data,\n                                       ngx_http_trim_saved_css_hack.len);\n\n                } else {\n                    ctx->saved = NGX_HTTP_TRIM_SAVE_HACKCSS;\n                }\n                break;\n            case '*':\n                ctx->state = trim_state_tag_style_css_comment_end;\n                continue;\n            case '\\\\':\n                ctx->state = trim_state_tag_style_css_comment_begin_hack;\n                continue;\n            default:\n                ctx->state = trim_state_tag_style_css_comment;\n                continue;\n            }\n            break;\n\n        case trim_state_tag_style_css_comment_hack_text:\n            switch (ch) {\n            case '/':\n                ctx->state = trim_state_tag_style_css_comment_hack_text_begin;\n                break;\n            default:\n                break;\n            }\n            break;\n\n        case trim_state_tag_style_css_comment_hack_text_begin:\n            switch (ch) {\n            case '*':\n                ctx->state = trim_state_tag_style_css_comment_hack_text_end;\n                break;\n            case '/':\n                break;\n            default:\n                ctx->state = trim_state_tag_style_css_comment_hack_text;\n                break;\n            }\n            break;\n\n        case trim_state_tag_style_css_comment_hack_text_end:\n            switch (ch) {\n            case '*':\n                ctx->state = trim_state_tag_style_css_comment_hack_text_last;\n                break;\n            default:\n                continue;\n            }\n            break;\n\n        case trim_state_tag_style_css_comment_hack_text_last:\n            switch (ch) {\n            case '*':\n                ctx->state = trim_state_tag_style_css_comment_hack_text_last;\n                break;\n            case '/':\n                ctx->state = trim_state_tag_style_css_text;\n                break;\n            default:\n                ctx->state = trim_state_tag_style_css_comment_hack_text_end;\n                break;\n            }\n            break;\n\n        case trim_state_tag_style_css_comment_end:\n            switch (ch) {\n            case '/':\n                ctx->state = trim_state_tag_style_css_text;\n                break;\n            case '*':\n                break;\n            case '\\\\':\n                ctx->state = trim_state_tag_style_css_comment_begin_hack;\n                break;\n            default:\n                ctx->state = trim_state_tag_style_css_comment;\n                break;\n            }\n            continue;\n\n        case trim_state_tag_style_css_whitespace:\n            switch (ch) {\n            case '\\r':\n            case '\\n':\n            case '\\t':\n            case ' ':\n                 continue;\n            case '\\'':\n                ctx->state = trim_state_tag_style_css_single_quote;\n                break;\n            case '\"':\n                ctx->state = trim_state_tag_style_css_double_quote;\n                break;\n            case '<':\n                ctx->state = trim_state_tag_style_css_end;\n                ctx->looked = 1;\n                break;\n            case '/':\n                ctx->state = trim_state_tag_style_css_comment_begin;\n                break;\n            default:\n                ctx->state = trim_state_tag_style_css_text;\n                break;\n            }\n\n            if (!(trim_css_prefix[ch >> 5] & (1 << (ch & 0x1f)))) {\n                if (read > buf->pos) {\n                    *write++ = ' ';\n\n                } else {\n                    ctx->saved = NGX_HTTP_TRIM_SAVE_SPACE;\n                }\n            }\n\n            if (ch == '/') {\n                continue;\n            }\n\n            break;\n\n        case trim_state_tag_style_end:\n            look = ngx_http_trim_style.data[ctx->looked++];\n            if (ch == look) {\n                if (ctx->looked == ngx_http_trim_style.len) {\n                    ctx->state = trim_state_text;\n                }\n                break;\n            }\n\n            switch (ch) {\n            case '\\r':\n            case '\\n':\n            case '\\t':\n            case ' ':\n                if (ctx->looked == ngx_http_trim_style.len) {\n                    ctx->state = trim_state_tag_whitespace;\n                    ctx->looked = 0;\n                    continue;\n                }\n\n                ctx->looked = 0;\n                break;\n            case '<':\n                ctx->looked = 1;\n                break;\n            default:\n                ctx->looked = 0;\n                break;\n            }\n            break;\n\n        case trim_state_tag_style_css_end:\n            look = ngx_http_trim_style.data[ctx->looked++];\n            if (ch == look) {\n                if (ctx->looked == ngx_http_trim_style.len) {\n                    ctx->state = trim_state_text;\n                }\n                break;\n            }\n\n            switch (ch) {\n            case '\\r':\n            case '\\n':\n            case '\\t':\n            case ' ':\n                if (ctx->looked == ngx_http_trim_style.len) {\n                    ctx->state = trim_state_tag_whitespace;\n                    ctx->looked = 0;\n                    continue;\n                }\n\n                ctx->looked = 0;\n                break;\n            case '<':\n                ctx->looked = 1;\n                break;\n            default:\n                ctx->state = trim_state_tag_style_css_text;\n                ctx->looked = 0;\n                break;\n            }\n            break;\n\n        case trim_state_comment_end:\n            look = ngx_http_trim_comment.data[ctx->looked++];  /* --> */\n            if (ch == look) {\n                if (ctx->looked == ngx_http_trim_comment.len) {\n                    ctx->state = trim_state_text;\n                }\n                continue;\n            }\n\n            switch (ch) {\n            case '-':\n                ctx->looked--;\n                break;\n            default:\n                ctx->looked = 0;\n                break;\n            }\n            continue;\n\n        case trim_state_comment_ie_end:        /*  <![endif]-->  */\n            look = ngx_http_trim_comment_ie_end.data[ctx->looked++];\n            if (ch == look) {\n                if (ctx->looked == ngx_http_trim_comment_ie_end.len) {\n                    ctx->state = trim_state_text;\n                }\n                break;\n            }\n\n            switch (ch) {\n            case '<':\n                ctx->looked = 1;\n                break;\n            default:\n                ctx->looked = 0;\n                break;\n            }\n            break;\n\n        case trim_state_comment_hack_end:\n            look = ngx_http_trim_comment.data[ctx->looked++];  /* --> */\n            if (ch == look) {\n                if (ctx->looked == ngx_http_trim_comment.len) {\n                    ctx->state = trim_state_text;\n                }\n                break;\n            }\n\n            switch (ch) {\n            case '-':\n                ctx->looked--;\n                break;\n            default:\n                ctx->looked = 0;\n                break;\n            }\n\n            break;\n\n        case trim_state_tag_pre:\n            switch (ch) {\n            case '<':\n                ctx->state = trim_state_tag_pre_angle;\n                break;\n            default:\n                break;\n            }\n            break;\n\n        case trim_state_tag_pre_angle:\n            switch (ch) {\n            case '/':\n                ctx->state = trim_state_tag_pre_end;\n                ctx->looked = 2;\n                break;\n            case 'p':\n                ctx->state = trim_state_tag_pre_nest;\n                ctx->looked = 3;\n                break;\n            case '<':\n                break;\n            default:\n                ctx->state = trim_state_tag_pre;\n                break;\n            }\n            break;\n\n        case trim_state_tag_pre_nest:\n            look = ngx_http_trim_pre.data[ctx->looked++];\n            if (ch == look) {\n                if (ctx->looked == ngx_http_trim_pre.len) {\n                    ctx->count++;\n                    ctx->state = trim_state_tag_pre;\n                }\n                break;\n            }\n\n            switch (ch) {\n            case '\\r':\n            case '\\n':\n            case '\\t':\n            case ' ':\n                if (ctx->looked == ngx_http_trim_pre.len) {\n                    ctx->count++;\n                    ctx->tag = NGX_HTTP_TRIM_TAG_PRE;\n                    ctx->state = trim_state_tag_whitespace;\n                    continue;\n\n                } else {\n                    ctx->state = trim_state_tag_pre;\n                }\n\n                break;\n            case '<':\n                ctx->state = trim_state_tag_pre_angle;\n                break;\n            default:\n                ctx->state = trim_state_tag_pre;\n                break;\n            }\n            break;\n\n        case trim_state_tag_pre_end:\n            look = ngx_http_trim_pre.data[ctx->looked++];\n            if (ch == look) {\n                if (ctx->looked == ngx_http_trim_pre.len) {\n                    if (--ctx->count > 0) {\n                        ctx->state = trim_state_tag_pre;\n\n                    } else {\n                        ctx->state = trim_state_text;\n                    }\n                }\n                break;\n            }\n\n            switch (ch) {\n            case '\\r':\n            case '\\n':\n            case '\\t':\n            case ' ':\n                if (ctx->looked == ngx_http_trim_pre.len) {\n                    if (--ctx->count > 0 ) {\n                        ctx->tag = NGX_HTTP_TRIM_TAG_PRE;\n                    }\n\n                    ctx->state = trim_state_tag_whitespace;\n                    ctx->looked = 0;\n                    continue;\n                }\n\n                ctx->looked = 0;\n                break;\n            case '<':\n                ctx->state = trim_state_tag_pre_angle;\n                break;\n            default:\n                ctx->state = trim_state_tag_pre;\n                break;\n            }\n            break;\n\n        case trim_state_tag_textarea_end:\n            look = ngx_http_trim_textarea.data[ctx->looked++];\n            if (ch == look) {\n                if (ctx->looked == ngx_http_trim_textarea.len) {\n                    ctx->state = trim_state_text;\n                }\n                break;\n            }\n\n            switch (ch) {\n            case '\\r':\n            case '\\n':\n            case '\\t':\n            case ' ':\n                if (ctx->looked == ngx_http_trim_textarea.len) {\n                    ctx->state = trim_state_tag_whitespace;\n                    ctx->looked = 0;\n                    continue;\n                }\n\n                ctx->looked = 0;\n                break;\n            case '<':\n                ctx->looked = 1;\n                break;\n            default:\n                ctx->looked = 0;\n                break;\n            }\n            break;\n\n        case trim_state_text_whitespace:\n            switch (ch) {\n            case '\\r':\n            case '\\t':\n            case ' ':\n                continue;\n            case '\\n':\n                if (ctx->prev == '\\n') {\n                    continue;\n\n                } else {\n                    break;\n                }\n            case '<':\n                ctx->state = trim_state_tag;\n                ctx->saved_comment = 1;\n                break;\n            default:\n                ctx->state = trim_state_text;\n                break;\n            }\n\n            if (ch != '\\n' && ctx->prev != '\\n') {\n                if (read > buf->pos) {\n                    *write++ = ' ';\n\n                } else {\n                    ctx->saved = NGX_HTTP_TRIM_SAVE_SPACE;\n                }\n            }\n\n            if (ch == '<') {\n                continue;\n            }\n\n            break;\n\n        case trim_state_tag_whitespace:\n            switch (ch) {\n            case '\\r':\n            case '\\n':\n            case '\\t':\n            case ' ':\n                continue;\n            case '\\'':\n                ctx->state = trim_state_tag_single_quote;\n                break;\n            case '\"':\n                ctx->state = trim_state_tag_double_quote;\n                break;\n            case '>':\n                if (ctx->tag == NGX_HTTP_TRIM_TAG_PRE) {\n                    ctx->state = trim_state_tag_pre;\n\n                } else if (ctx->tag == NGX_HTTP_TRIM_TAG_TEXTAREA) {\n                    ctx->state = trim_state_tag_textarea_end;\n\n                } else if (ctx->tag == NGX_HTTP_TRIM_TAG_SCRIPT) {\n                    if (ctx->js_enable\n                        && ctx->looked == ngx_http_trim_script_js.len)\n                    {\n                        ctx->state = trim_state_tag_script_js_text;\n\n                    } else {\n                        ctx->state = trim_state_tag_script_end;\n                    }\n\n                } else if (ctx->tag == NGX_HTTP_TRIM_TAG_STYLE) {\n                    if (ctx->css_enable\n                        && ctx->looked == ngx_http_trim_style_css.len)\n                    {\n                        ctx->state = trim_state_tag_style_css_text;\n\n                    } else {\n                        ctx->state = trim_state_tag_style_end;\n                    }\n\n                } else {\n                    ctx->state = trim_state_text;\n                }\n\n                ctx->tag = 0;\n                ctx->looked = 0;\n                break;\n            default:\n                ctx->state = trim_state_tag_attribute;\n                break;\n            }\n\n            if (ch != '>' && ch != '=') {\n                if (read > buf->pos) {\n                    *write++ = ' ';\n\n                } else {\n                    ctx->saved = NGX_HTTP_TRIM_SAVE_SPACE;\n                }\n            }\n\n            break;\n\n        case trim_state_tag_single_quote:\n            switch (ch) {\n            case '\\'':\n                ctx->state = trim_state_tag_attribute;\n                break;\n            default:\n                break;\n            }\n\n            if (ctx->js_enable && ctx->tag == NGX_HTTP_TRIM_TAG_SCRIPT) {\n                if (ctx->looked != ngx_http_trim_script_js.len) {\n                    look = ngx_http_trim_script_js.data[ctx->looked++];\n                    if (ch != look) {\n                        ctx->looked = 0;\n                    }\n                }\n            }\n\n            if (ctx->css_enable && ctx->tag == NGX_HTTP_TRIM_TAG_STYLE) {\n                if (ctx->looked != ngx_http_trim_style_css.len) {\n                    look = ngx_http_trim_style_css.data[ctx->looked++];\n                    if (ch != look) {\n                        ctx->looked = 0;\n                    }\n                }\n            }\n\n            break;\n\n        case trim_state_tag_double_quote:\n            switch (ch) {\n            case '\"':\n                ctx->state = trim_state_tag_attribute;\n                break;\n            default:\n                break;\n            }\n\n            if (ctx->js_enable && ctx->tag == NGX_HTTP_TRIM_TAG_SCRIPT) {\n                if (ctx->looked != ngx_http_trim_script_js.len) {\n                    look = ngx_http_trim_script_js.data[ctx->looked++];\n                    if (ch != look) {\n                        ctx->looked = 0;\n                    }\n                }\n            }\n\n            if (ctx->css_enable && ctx->tag == NGX_HTTP_TRIM_TAG_STYLE) {\n                if (ctx->looked != ngx_http_trim_style_css.len) {\n                    look = ngx_http_trim_style_css.data[ctx->looked++];\n                    if (ch != look) {\n                        ctx->looked = 0;\n                    }\n                }\n            }\n\n            break;\n\n        default:\n            break;\n        }\n\n        *write++ = *read;\n         ctx->prev = *read;\n    }\n\n    if (!buf->temporary) {\n        in->buf = b;\n        buf->pos = buf->last;\n    }\n\n    b->last = write;\n    ctx->tout += b->last - b->pos;\n\n    return NGX_OK;\n}\n\nstatic ngx_int_t\nngx_http_trim_add_variables(ngx_conf_t *cf)\n{\n    ngx_http_variable_t   *var, *v;\n\n    for (v = ngx_http_trim_vars; v->name.len; v++) {\n        var = ngx_http_add_variable(cf, &v->name, v->flags);\n        if (var == NULL) {\n            return NGX_ERROR;\n        }\n\n        var->get_handler = v->get_handler;\n        var->data = v->data;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_trim_bytes_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    ngx_http_trim_ctx_t   *ctx;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_trim_filter_module);\n\n    if (ctx == NULL) {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    v->data = ngx_pnalloc(r->pool, NGX_OFF_T_LEN);\n    if (v->data == NULL) {\n        return NGX_ERROR;\n    }\n\n    v->len = ngx_sprintf(v->data, \"%O\", ctx->tin - ctx->tout) - v->data;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_trim_original_bytes_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    ngx_http_trim_ctx_t   *ctx;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_trim_filter_module);\n\n    if (ctx == NULL) {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    v->data = ngx_pnalloc(r->pool, NGX_OFF_T_LEN);\n    if (v->data == NULL) {\n        return NGX_ERROR;\n    }\n\n    v->len = ngx_sprintf(v->data, \"%O\", ctx->tin) - v->data;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    return NGX_OK;\n}\n\n\nstatic void *\nngx_http_trim_create_loc_conf(ngx_conf_t *cf)\n{\n    ngx_http_trim_loc_conf_t *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_trim_loc_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc():\n     *\n     *     conf->types = { NULL };\n     *     conf->types_keys = NULL;\n     *     conf->trim = NULL;\n     *     conf->js = NULL;\n     *     conf->css = NULL;\n     */\n\n    return conf;\n}\n\n\nstatic char *\nngx_http_trim_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_http_trim_loc_conf_t *prev = parent;\n    ngx_http_trim_loc_conf_t *conf = child;\n\n    if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,\n                             &prev->types_keys, &prev->types,\n                             ngx_http_html_default_types)\n        != NGX_OK)\n    {\n        return NGX_CONF_ERROR;\n    }\n\n    if (conf->trim == NULL) {\n        conf->trim = prev->trim;\n    }\n\n    if (conf->js == NULL) {\n        conf->js = prev->js;\n    }\n\n    if (conf->css == NULL) {\n        conf->css = prev->css;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_trim_filter_init(ngx_conf_t *cf)\n{\n    ngx_http_next_header_filter = ngx_http_top_header_filter;\n    ngx_http_top_header_filter = ngx_http_trim_header_filter;\n\n    ngx_http_next_body_filter = ngx_http_top_body_filter;\n    ngx_http_top_body_filter = ngx_http_trim_body_filter;\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "modules/ngx_http_upstream_check_module/config",
    "content": "ngx_feature=\"ngx_http_upstream_check_module\"\nngx_feature_name=\nngx_feature_run=no\nngx_feature_incs=\nngx_feature_libs=\nngx_feature_path=\"$ngx_addon_dir\"\nngx_feature_deps=\"$ngx_addon_dir/ngx_http_upstream_check_module.h\"\nngx_check_src=\"$ngx_addon_dir/ngx_http_upstream_check_module.c\"\nngx_feature_test=\"int a;\"\n. auto/feature\n\nif [ $ngx_found = yes ]; then\n    have=NGX_HTTP_UPSTREAM_CHECK . auto/have\n    CORE_INCS=\"$CORE_INCS $ngx_feature_path\"\n    ngx_addon_name=ngx_http_upstream_check_module\n    HTTP_MODULES=\"$HTTP_MODULES ngx_http_upstream_check_module\"\n    NGX_ADDON_DEPS=\"$NGX_ADDON_DEPS $ngx_feature_deps\"\n    NGX_ADDON_SRCS=\"$NGX_ADDON_SRCS $ngx_check_src\"\nelse\n    cat << END\n    $0: error: the ngx_http_upstream_check_module addon error.\nEND\n    exit 1\nfi\n\nngx_feature=\"compiler structure-packing pragma\"\nngx_feature_name=\"NGX_HAVE_PACK_PRAGMA\"\nngx_feature_run=yes\nngx_feature_incs=\nngx_feature_path=\nngx_feature_libs=\nngx_feature_test=\"#pragma pack(push, 1)\n                  struct test_s {\n                      char foo;\n                      int  bar;\n                  };\n                  #pragma pack(pop)\n\n                  if (sizeof(struct test_s) != (sizeof(char) + sizeof(int)))\n                      return 1;\"\n. auto/feature\n\n"
  },
  {
    "path": "modules/ngx_http_upstream_check_module/ngx_http_upstream_check_module.c",
    "content": "/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n * Copyright (C) 2010-2013 Weibin Yao (yaoweibin@gmail.com)\n */\n\n\n#include <ngx_core.h>\n#include <ngx_http.h>\n#include <ngx_config.h>\n\n\ntypedef struct ngx_http_upstream_check_peer_s ngx_http_upstream_check_peer_t;\ntypedef struct ngx_http_upstream_check_srv_conf_s\n    ngx_http_upstream_check_srv_conf_t;\n\n\n#if (NGX_HAVE_PACK_PRAGMA)\n#pragma pack(push, 1)\n#elif (NGX_SOLARIS)\n#pragma pack(1)\n#else\n#error \"ngx_http_upstream_check_module needs structure packing pragma support\"\n#endif\n\ntypedef struct {\n    u_char                                   major;\n    u_char                                   minor;\n} ngx_ssl_protocol_version_t;\n\n\ntypedef struct {\n    u_char                                   msg_type;\n    ngx_ssl_protocol_version_t               version;\n    uint16_t                                 length;\n\n    u_char                                   handshake_type;\n    u_char                                   handshake_length[3];\n    ngx_ssl_protocol_version_t               hello_version;\n\n    time_t                                   time;\n    u_char                                   random[28];\n\n    u_char                                   others[0];\n} ngx_ssl_server_hello_t;\n\n\ntypedef struct {\n    u_char                                   packet_length[3];\n    u_char                                   packet_number;\n\n    u_char                                   protocol_version;\n    u_char                                   others[0];\n} ngx_mysql_handshake_init_t;\n\n\ntypedef struct {\n    uint16_t                                 preamble;\n    uint16_t                                 length;\n    u_char                                   type;\n} ngx_ajp_raw_packet_t;\n\n\n#if (NGX_HAVE_PACK_PRAGMA)\n#pragma pack(pop)\n#elif (NGX_SOLARIS)\n#pragma pack()\n#else\n#error \"ngx_http_upstream_check_module needs structure packing pragma support\"\n#endif\n\n\ntypedef struct {\n    ngx_buf_t                                send;\n    ngx_buf_t                                recv;\n\n    ngx_uint_t                               state;\n    ngx_http_status_t                        status;\n\n    size_t                                   padding;\n    size_t                                   length;\n} ngx_http_upstream_check_ctx_t;\n\n\ntypedef struct {\n    ngx_shmtx_t                              mutex;\n    ngx_shmtx_sh_t                           lock;\n\n    ngx_pid_t                                owner;\n\n    ngx_msec_t                               access_time;\n\n    ngx_uint_t                               fall_count;\n    ngx_uint_t                               rise_count;\n\n    ngx_uint_t                               busyness;\n    ngx_uint_t                               access_count;\n\n    ngx_uint_t                               checksum;\n\n    struct sockaddr                         *sockaddr;\n    socklen_t                                socklen;\n\n    ngx_int_t                                ref;\n    ngx_uint_t                               delete;\n\n    ngx_atomic_t                             down;\n\n    u_char                                   padding[64];\n} ngx_http_upstream_check_peer_shm_t;\n\n\ntypedef struct {\n    ngx_uint_t                               generation;\n    ngx_uint_t                               checksum;\n    ngx_uint_t                               number;\n    ngx_uint_t                               max_number;\n\n    /* ngx_http_upstream_check_status_peer_t */\n    ngx_http_upstream_check_peer_shm_t       peers[1];\n} ngx_http_upstream_check_peers_shm_t;\n\n\n#define NGX_HTTP_CHECK_CONNECT_DONE          0x0001\n#define NGX_HTTP_CHECK_SEND_DONE             0x0002\n#define NGX_HTTP_CHECK_RECV_DONE             0x0004\n#define NGX_HTTP_CHECK_ALL_DONE              0x0008\n\n\ntypedef ngx_int_t (*ngx_http_upstream_check_packet_init_pt)\n    (ngx_http_upstream_check_peer_t *peer);\ntypedef ngx_int_t (*ngx_http_upstream_check_packet_parse_pt)\n    (ngx_http_upstream_check_peer_t *peer);\ntypedef void (*ngx_http_upstream_check_packet_clean_pt)\n    (ngx_http_upstream_check_peer_t *peer);\n\nstruct ngx_http_upstream_check_peer_s {\n    ngx_flag_t                               state;\n    ngx_pool_t                              *pool;\n    ngx_uint_t                               index;\n    ngx_uint_t                               max_busy;\n    ngx_str_t                               *upstream_name;\n    ngx_addr_t                              *check_peer_addr;\n    ngx_addr_t                              *peer_addr;\n    ngx_event_t                              check_ev;\n    ngx_event_t                              check_timeout_ev;\n    ngx_peer_connection_t                    pc;\n\n    void                                    *check_data;\n    ngx_event_handler_pt                     send_handler;\n    ngx_event_handler_pt                     recv_handler;\n\n    ngx_http_upstream_check_packet_init_pt   init;\n    ngx_http_upstream_check_packet_parse_pt  parse;\n    ngx_http_upstream_check_packet_clean_pt  reinit;\n\n    ngx_http_upstream_check_peer_shm_t      *shm;\n    ngx_http_upstream_check_srv_conf_t      *conf;\n\n    unsigned                                 delete;\n};\n\n\ntypedef struct {\n    ngx_str_t                                check_shm_name;\n    ngx_uint_t                               checksum;\n    ngx_array_t                              peers;\n    ngx_slab_pool_t                         *shpool;\n\n    ngx_http_upstream_check_peers_shm_t     *peers_shm;\n} ngx_http_upstream_check_peers_t;\n\n\n#define NGX_HTTP_CHECK_TCP                   0x0001\n#define NGX_HTTP_CHECK_HTTP                  0x0002\n#define NGX_HTTP_CHECK_SSL_HELLO             0x0004\n#define NGX_HTTP_CHECK_MYSQL                 0x0008\n#define NGX_HTTP_CHECK_AJP                   0x0010\n\n#define NGX_CHECK_HTTP_1XX                   0x0002\n#define NGX_CHECK_HTTP_2XX                   0x0004\n#define NGX_CHECK_HTTP_3XX                   0x0008\n#define NGX_CHECK_HTTP_4XX                   0x0010\n#define NGX_CHECK_HTTP_5XX                   0x0020\n\n#define NGX_CHECK_HTTP_ERR                   0x8000\n\ntypedef struct {\n    ngx_uint_t                               type;\n\n    ngx_str_t                                name;\n\n    ngx_str_t                                default_send;\n\n    /* HTTP */\n    ngx_uint_t                               default_status_alive;\n\n    ngx_event_handler_pt                     send_handler;\n    ngx_event_handler_pt                     recv_handler;\n\n    ngx_http_upstream_check_packet_init_pt   init;\n    ngx_http_upstream_check_packet_parse_pt  parse;\n    ngx_http_upstream_check_packet_clean_pt  reinit;\n\n    unsigned need_pool;\n    unsigned need_keepalive;\n} ngx_check_conf_t;\n\n\ntypedef void (*ngx_http_upstream_check_status_format_pt) (ngx_buf_t *b,\n    ngx_http_upstream_check_peers_t *peers, ngx_uint_t flag);\n\ntypedef struct {\n    ngx_str_t                                format;\n    ngx_str_t                                content_type;\n\n    ngx_http_upstream_check_status_format_pt output;\n} ngx_check_status_conf_t;\n\n\n#define NGX_CHECK_STATUS_DOWN                0x0001\n#define NGX_CHECK_STATUS_UP                  0x0002\n\ntypedef struct {\n    ngx_check_status_conf_t                 *format;\n    ngx_flag_t                               flag;\n} ngx_http_upstream_check_status_ctx_t;\n\n\ntypedef ngx_int_t (*ngx_http_upstream_check_status_command_pt)\n    (ngx_http_upstream_check_status_ctx_t *ctx, ngx_str_t *value);\n\ntypedef struct {\n    ngx_str_t                                 name;\n    ngx_http_upstream_check_status_command_pt handler;\n} ngx_check_status_command_t;\n\n\ntypedef struct {\n    ngx_uint_t                               check_shm_size;\n    ngx_http_upstream_check_peers_t         *peers;\n} ngx_http_upstream_check_main_conf_t;\n\n\nstruct ngx_http_upstream_check_srv_conf_s {\n    ngx_uint_t                               port;\n    ngx_uint_t                               fall_count;\n    ngx_uint_t                               rise_count;\n    ngx_msec_t                               check_interval;\n    ngx_msec_t                               check_timeout;\n    ngx_uint_t                               check_keepalive_requests;\n\n    ngx_check_conf_t                        *check_type_conf;\n    ngx_str_t                                send;\n\n    union {\n        ngx_uint_t                           return_code;\n        ngx_uint_t                           status_alive;\n    } code;\n\n    ngx_array_t                             *fastcgi_params;\n\n    ngx_uint_t                               default_down;\n    ngx_uint_t                               unique;\n};\n\n\ntypedef struct {\n    ngx_check_status_conf_t                 *format;\n} ngx_http_upstream_check_loc_conf_t;\n\n\ntypedef struct {\n    u_char  version;\n    u_char  type;\n    u_char  request_id_hi;\n    u_char  request_id_lo;\n    u_char  content_length_hi;\n    u_char  content_length_lo;\n    u_char  padding_length;\n    u_char  reserved;\n} ngx_http_fastcgi_header_t;\n\n\ntypedef struct {\n    u_char  role_hi;\n    u_char  role_lo;\n    u_char  flags;\n    u_char  reserved[5];\n} ngx_http_fastcgi_begin_request_t;\n\n\ntypedef struct {\n    u_char  version;\n    u_char  type;\n    u_char  request_id_hi;\n    u_char  request_id_lo;\n} ngx_http_fastcgi_header_small_t;\n\n\ntypedef struct {\n    ngx_http_fastcgi_header_t         h0;\n    ngx_http_fastcgi_begin_request_t  br;\n    ngx_http_fastcgi_header_small_t   h1;\n} ngx_http_fastcgi_request_start_t;\n\n\n#define NGX_HTTP_FASTCGI_RESPONDER      1\n\n#define NGX_HTTP_FASTCGI_KEEP_CONN      1\n\n#define NGX_HTTP_FASTCGI_BEGIN_REQUEST  1\n#define NGX_HTTP_FASTCGI_ABORT_REQUEST  2\n#define NGX_HTTP_FASTCGI_END_REQUEST    3\n#define NGX_HTTP_FASTCGI_PARAMS         4\n#define NGX_HTTP_FASTCGI_STDIN          5\n#define NGX_HTTP_FASTCGI_STDOUT         6\n#define NGX_HTTP_FASTCGI_STDERR         7\n#define NGX_HTTP_FASTCGI_DATA           8\n\n\ntypedef enum {\n    ngx_http_fastcgi_st_version = 0,\n    ngx_http_fastcgi_st_type,\n    ngx_http_fastcgi_st_request_id_hi,\n    ngx_http_fastcgi_st_request_id_lo,\n    ngx_http_fastcgi_st_content_length_hi,\n    ngx_http_fastcgi_st_content_length_lo,\n    ngx_http_fastcgi_st_padding_length,\n    ngx_http_fastcgi_st_reserved,\n    ngx_http_fastcgi_st_data,\n    ngx_http_fastcgi_st_padding\n} ngx_http_fastcgi_state_e;\n\n\nstatic ngx_http_fastcgi_request_start_t  ngx_http_fastcgi_request_start = {\n    { 1,                                               /* version */\n      NGX_HTTP_FASTCGI_BEGIN_REQUEST,                  /* type */\n      0,                                               /* request_id_hi */\n      1,                                               /* request_id_lo */\n      0,                                               /* content_length_hi */\n      sizeof(ngx_http_fastcgi_begin_request_t),        /* content_length_lo */\n      0,                                               /* padding_length */\n      0 },                                             /* reserved */\n\n    { 0,                                               /* role_hi */\n      NGX_HTTP_FASTCGI_RESPONDER,                      /* role_lo */\n      0, /* NGX_HTTP_FASTCGI_KEEP_CONN */              /* flags */\n      { 0, 0, 0, 0, 0 } },                             /* reserved[5] */\n\n    { 1,                                               /* version */\n      NGX_HTTP_FASTCGI_PARAMS,                         /* type */\n      0,                                               /* request_id_hi */\n      1 },                                             /* request_id_lo */\n\n};\n\n\n#define upstream_check_index_invalid(check_ctx, index)     \\\n    (check_ctx == NULL                                     \\\n     || index >= check_ctx->peers_shm->number              \\\n     || index >= check_ctx->peers_shm->max_number)\n\n\n#define PEER_NORMAL   0x00\n#define PEER_DELETING 0x01\n#define PEER_DELETED  0x02\n\n\nstatic ngx_uint_t ngx_http_upstream_check_add_dynamic_peer_shm(\n    ngx_pool_t *pool, ngx_http_upstream_check_srv_conf_t *ucscf,\n    ngx_addr_t *peer_addr);\nstatic void ngx_http_upstream_check_clear_dynamic_peer_shm(\n    ngx_http_upstream_check_peer_shm_t *peer_shm);\n\nstatic ngx_int_t ngx_http_upstream_check_add_timers(ngx_cycle_t *cycle);\nstatic ngx_int_t ngx_http_upstream_check_add_timer(\n    ngx_http_upstream_check_peer_t *peer, ngx_check_conf_t *check_conf,\n    ngx_msec_t timer, ngx_log_t *log);\n\nstatic ngx_int_t ngx_http_upstream_check_peek_one_byte(ngx_connection_t *c);\n\nstatic void ngx_http_upstream_check_begin_handler(ngx_event_t *event);\nstatic void ngx_http_upstream_check_connect_handler(ngx_event_t *event);\n\nstatic void ngx_http_upstream_check_peek_handler(ngx_event_t *event);\n\nstatic void ngx_http_upstream_check_send_handler(ngx_event_t *event);\nstatic void ngx_http_upstream_check_recv_handler(ngx_event_t *event);\n\nstatic void ngx_http_upstream_check_discard_handler(ngx_event_t *event);\nstatic void ngx_http_upstream_check_dummy_handler(ngx_event_t *event);\n\nstatic ngx_int_t ngx_http_upstream_check_http_init(\n    ngx_http_upstream_check_peer_t *peer);\nstatic ngx_int_t ngx_http_upstream_check_http_parse(\n    ngx_http_upstream_check_peer_t *peer);\nstatic ngx_int_t ngx_http_upstream_check_parse_status_line(\n    ngx_http_upstream_check_ctx_t *ctx, ngx_buf_t *b,\n    ngx_http_status_t *status);\nstatic void ngx_http_upstream_check_http_reinit(\n    ngx_http_upstream_check_peer_t *peer);\n\nstatic ngx_buf_t *ngx_http_upstream_check_create_fastcgi_request(\n    ngx_pool_t *pool, ngx_str_t *params, ngx_uint_t num);\n\nstatic ngx_int_t ngx_http_upstream_check_fastcgi_parse(\n    ngx_http_upstream_check_peer_t *peer);\nstatic ngx_int_t ngx_http_upstream_check_fastcgi_process_record(\n    ngx_http_upstream_check_ctx_t *ctx, ngx_buf_t *b,\n    ngx_http_status_t *status);\nstatic ngx_int_t ngx_http_upstream_check_parse_fastcgi_status(\n    ngx_http_upstream_check_ctx_t *ctx, ngx_buf_t *b,\n    ngx_http_status_t *status);\n\nstatic ngx_int_t ngx_http_upstream_check_ssl_hello_init(\n    ngx_http_upstream_check_peer_t *peer);\nstatic ngx_int_t ngx_http_upstream_check_ssl_hello_parse(\n    ngx_http_upstream_check_peer_t *peer);\nstatic void ngx_http_upstream_check_ssl_hello_reinit(\n    ngx_http_upstream_check_peer_t *peer);\n\nstatic ngx_int_t ngx_http_upstream_check_mysql_init(\n    ngx_http_upstream_check_peer_t *peer);\nstatic ngx_int_t ngx_http_upstream_check_mysql_parse(\n    ngx_http_upstream_check_peer_t *peer);\nstatic void ngx_http_upstream_check_mysql_reinit(\n    ngx_http_upstream_check_peer_t *peer);\n\nstatic ngx_int_t ngx_http_upstream_check_ajp_init(\n    ngx_http_upstream_check_peer_t *peer);\nstatic ngx_int_t ngx_http_upstream_check_ajp_parse(\n    ngx_http_upstream_check_peer_t *peer);\nstatic void ngx_http_upstream_check_ajp_reinit(\n    ngx_http_upstream_check_peer_t *peer);\n\nstatic void ngx_http_upstream_check_status_update(\n    ngx_http_upstream_check_peer_t *peer,\n    ngx_int_t result);\n\nstatic void ngx_http_upstream_check_clean_event(\n    ngx_http_upstream_check_peer_t *peer);\n\nstatic void ngx_http_upstream_check_timeout_handler(ngx_event_t *event);\nstatic void ngx_http_upstream_check_finish_handler(ngx_event_t *event);\n\nstatic ngx_int_t ngx_http_upstream_check_need_exit();\nstatic void ngx_http_upstream_check_clear_all_events();\nstatic void ngx_http_upstream_check_clear_peer(\n    ngx_http_upstream_check_peer_t  *peer);\n\nstatic ngx_int_t ngx_http_upstream_check_status_handler(\n    ngx_http_request_t *r);\n\nstatic void ngx_http_upstream_check_status_parse_args(ngx_http_request_t *r,\n    ngx_http_upstream_check_status_ctx_t *ctx);\n\nstatic ngx_int_t ngx_http_upstream_check_status_command_format(\n    ngx_http_upstream_check_status_ctx_t *ctx, ngx_str_t *value);\nstatic ngx_int_t ngx_http_upstream_check_status_command_status(\n    ngx_http_upstream_check_status_ctx_t *ctx, ngx_str_t *value);\n\nstatic void ngx_http_upstream_check_status_html_format(ngx_buf_t *b,\n    ngx_http_upstream_check_peers_t *peers, ngx_uint_t flag);\nstatic void ngx_http_upstream_check_status_csv_format(ngx_buf_t *b,\n    ngx_http_upstream_check_peers_t *peers, ngx_uint_t flag);\nstatic void ngx_http_upstream_check_status_json_format(ngx_buf_t *b,\n    ngx_http_upstream_check_peers_t *peers, ngx_uint_t flag);\nstatic void ngx_http_upstream_check_status_prometheus_format(ngx_buf_t *b,\n    ngx_http_upstream_check_peers_t *peers, ngx_uint_t flag);\n\nstatic ngx_int_t ngx_http_upstream_check_addr_change_port(ngx_pool_t *pool,\n    ngx_addr_t *dst, ngx_addr_t *src, ngx_uint_t port);\n\nstatic ngx_check_conf_t *ngx_http_get_check_type_conf(ngx_str_t *str);\n\nstatic char *ngx_http_upstream_check(ngx_conf_t *cf,\n    ngx_command_t *cmd, void *conf);\nstatic char *ngx_http_upstream_check_keepalive_requests(ngx_conf_t *cf,\n    ngx_command_t *cmd, void *conf);\nstatic char *ngx_http_upstream_check_http_send(ngx_conf_t *cf,\n    ngx_command_t *cmd, void *conf);\nstatic char *ngx_http_upstream_check_http_expect_alive(ngx_conf_t *cf,\n    ngx_command_t *cmd, void *conf);\n\nstatic char *ngx_http_upstream_check_fastcgi_params(ngx_conf_t *cf,\n    ngx_command_t *cmd, void *conf);\n\nstatic char *ngx_http_upstream_check_shm_size(ngx_conf_t *cf,\n    ngx_command_t *cmd, void *conf);\n\nstatic ngx_check_status_conf_t *ngx_http_get_check_status_format_conf(\n    ngx_str_t *str);\nstatic char *ngx_http_upstream_check_status(ngx_conf_t *cf,\n    ngx_command_t *cmd, void *conf);\n\nstatic void *ngx_http_upstream_check_create_main_conf(ngx_conf_t *cf);\nstatic char *ngx_http_upstream_check_init_main_conf(ngx_conf_t *cf,\n    void *conf);\n\nstatic void *ngx_http_upstream_check_create_srv_conf(ngx_conf_t *cf);\nstatic char *ngx_http_upstream_check_init_srv_conf(ngx_conf_t *cf, void *conf);\n\nstatic ngx_uint_t ngx_http_upstream_check_unique_peer(\n    ngx_http_upstream_check_peers_t *peers, ngx_addr_t *peer_addr,\n    ngx_http_upstream_check_srv_conf_t *peer_conf);\n\nstatic void *ngx_http_upstream_check_create_loc_conf(ngx_conf_t *cf);\nstatic char * ngx_http_upstream_check_merge_loc_conf(ngx_conf_t *cf,\n    void *parent, void *child);\n\n#define MAX_DYNAMIC_PEER 4096\n#define SHM_NAME_LEN     256\n\nstatic char *ngx_http_upstream_check_init_shm(ngx_conf_t *cf, void *conf);\n\nstatic ngx_int_t ngx_http_upstream_check_get_shm_name(ngx_str_t *shm_name,\n    ngx_pool_t *pool, ngx_uint_t generation);\nstatic ngx_shm_zone_t *ngx_shared_memory_find(ngx_cycle_t *cycle,\n    ngx_str_t *name, void *tag);\nstatic ngx_http_upstream_check_peer_shm_t *\nngx_http_upstream_check_find_shm_peer(\n    ngx_http_upstream_check_peers_shm_t *peers_shm, ngx_addr_t *addr);\n\nstatic ngx_int_t ngx_http_upstream_check_init_shm_peer(\n    ngx_http_upstream_check_peer_shm_t *peer_shm,\n    ngx_http_upstream_check_peer_shm_t *opeer_shm,\n    ngx_uint_t init_down, ngx_pool_t *pool, ngx_str_t *peer_name);\n\nstatic ngx_int_t ngx_http_upstream_check_init_shm_zone(\n    ngx_shm_zone_t *shm_zone, void *data);\n\n\nstatic ngx_int_t ngx_http_upstream_check_init_process(ngx_cycle_t *cycle);\n\n\nstatic ngx_conf_bitmask_t  ngx_check_http_expect_alive_masks[] = {\n    { ngx_string(\"http_1xx\"), NGX_CHECK_HTTP_1XX },\n    { ngx_string(\"http_2xx\"), NGX_CHECK_HTTP_2XX },\n    { ngx_string(\"http_3xx\"), NGX_CHECK_HTTP_3XX },\n    { ngx_string(\"http_4xx\"), NGX_CHECK_HTTP_4XX },\n    { ngx_string(\"http_5xx\"), NGX_CHECK_HTTP_5XX },\n    { ngx_null_string, 0 }\n};\n\n\nstatic ngx_command_t  ngx_http_upstream_check_commands[] = {\n\n    { ngx_string(\"check\"),\n      NGX_HTTP_UPS_CONF|NGX_CONF_1MORE,\n      ngx_http_upstream_check,\n      0,\n      0,\n      NULL },\n\n    { ngx_string(\"check_keepalive_requests\"),\n      NGX_HTTP_UPS_CONF|NGX_CONF_TAKE1,\n      ngx_http_upstream_check_keepalive_requests,\n      0,\n      0,\n      NULL },\n\n    { ngx_string(\"check_http_send\"),\n      NGX_HTTP_UPS_CONF|NGX_CONF_TAKE1,\n      ngx_http_upstream_check_http_send,\n      0,\n      0,\n      NULL },\n\n    { ngx_string(\"check_http_expect_alive\"),\n      NGX_HTTP_UPS_CONF|NGX_CONF_1MORE,\n      ngx_http_upstream_check_http_expect_alive,\n      0,\n      0,\n      NULL },\n\n    { ngx_string(\"check_fastcgi_param\"),\n      NGX_HTTP_UPS_CONF|NGX_CONF_TAKE2,\n      ngx_http_upstream_check_fastcgi_params,\n      0,\n      0,\n      NULL },\n\n    { ngx_string(\"check_shm_size\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,\n      ngx_http_upstream_check_shm_size,\n      0,\n      0,\n      NULL },\n\n    { ngx_string(\"check_status\"),\n      NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1|NGX_CONF_NOARGS,\n      ngx_http_upstream_check_status,\n      0,\n      0,\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_upstream_check_module_ctx = {\n    NULL,                                    /* preconfiguration */\n    NULL,                                    /* postconfiguration */\n\n    ngx_http_upstream_check_create_main_conf,/* create main configuration */\n    ngx_http_upstream_check_init_main_conf,  /* init main configuration */\n\n    ngx_http_upstream_check_create_srv_conf, /* create server configuration */\n    NULL,                                    /* merge server configuration */\n\n    ngx_http_upstream_check_create_loc_conf, /* create location configuration */\n    ngx_http_upstream_check_merge_loc_conf   /* merge location configuration */\n};\n\n\nngx_module_t  ngx_http_upstream_check_module = {\n    NGX_MODULE_V1,\n    &ngx_http_upstream_check_module_ctx,   /* module context */\n    ngx_http_upstream_check_commands,      /* module directives */\n    NGX_HTTP_MODULE,                       /* module type */\n    NULL,                                  /* init master */\n    NULL,                                  /* init module */\n    ngx_http_upstream_check_init_process,  /* 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_str_t fastcgi_default_request;\nstatic ngx_str_t fastcgi_default_params[] = {\n    ngx_string(\"REQUEST_METHOD\"), ngx_string(\"GET\"),\n    ngx_string(\"REQUEST_URI\"), ngx_string(\"/\"),\n    ngx_string(\"SCRIPT_FILENAME\"), ngx_string(\"index.php\"),\n};\n\n\n#define NGX_SSL_RANDOM \"NGX_HTTP_CHECK_SSL_HELLO\\n\\n\\n\\n\"\n\n/*\n * This is the SSLv3 CLIENT HELLO packet used in conjunction with the\n * check type of ssl_hello to ensure that the remote server speaks SSL.\n *\n * Check RFC 2246 (TLSv1.0) sections A.3 and A.4 for details.\n */\nstatic char sslv3_client_hello_pkt[] = {\n    \"\\x16\"                /* ContentType         : 0x16 = Hanshake           */\n    \"\\x03\\x00\"            /* ProtocolVersion     : 0x0300 = SSLv3            */\n    \"\\x00\\x79\"            /* ContentLength       : 0x79 bytes after this one */\n    \"\\x01\"                /* HanshakeType        : 0x01 = CLIENT HELLO       */\n    \"\\x00\\x00\\x75\"        /* HandshakeLength     : 0x75 bytes after this one */\n    \"\\x03\\x00\"            /* Hello Version       : 0x0300 = v3               */\n    \"\\x00\\x00\\x00\\x00\"    /* Unix GMT Time (s)   : filled with <now> (@0x0B) */\n    NGX_SSL_RANDOM        /* Random              : must be exactly 28 bytes  */\n    \"\\x00\"                /* Session ID length   : empty (no session ID)     */\n    \"\\x00\\x4E\"            /* Cipher Suite Length : 78 bytes after this one   */\n    \"\\x00\\x01\" \"\\x00\\x02\" \"\\x00\\x03\" \"\\x00\\x04\" /* 39 most common ciphers :  */\n    \"\\x00\\x05\" \"\\x00\\x06\" \"\\x00\\x07\" \"\\x00\\x08\" /* 0x01...0x1B, 0x2F...0x3A  */\n    \"\\x00\\x09\" \"\\x00\\x0A\" \"\\x00\\x0B\" \"\\x00\\x0C\" /* This covers RSA/DH,       */\n    \"\\x00\\x0D\" \"\\x00\\x0E\" \"\\x00\\x0F\" \"\\x00\\x10\" /* various bit lengths,      */\n    \"\\x00\\x11\" \"\\x00\\x12\" \"\\x00\\x13\" \"\\x00\\x14\" /* SHA1/MD5, DES/3DES/AES... */\n    \"\\x00\\x15\" \"\\x00\\x16\" \"\\x00\\x17\" \"\\x00\\x18\"\n    \"\\x00\\x19\" \"\\x00\\x1A\" \"\\x00\\x1B\" \"\\x00\\x2F\"\n    \"\\x00\\x30\" \"\\x00\\x31\" \"\\x00\\x32\" \"\\x00\\x33\"\n    \"\\x00\\x34\" \"\\x00\\x35\" \"\\x00\\x36\" \"\\x00\\x37\"\n    \"\\x00\\x38\" \"\\x00\\x39\" \"\\x00\\x3A\"\n    \"\\x01\"                /* Compression Length  : 0x01 = 1 byte for types   */\n    \"\\x00\"                /* Compression Type    : 0x00 = NULL compression   */\n};\n\n\n#define NGX_SSL_HANDSHAKE    0x16\n#define NGX_SSL_SERVER_HELLO 0x02\n\n\n#define NGX_AJP_CPING        0x0a\n#define NGX_AJP_CPONG        0x09\n\n\nstatic char ngx_ajp_cping_packet[] = {\n    0x12, 0x34, 0x00, 0x01, NGX_AJP_CPING, 0x00\n};\n\nstatic char ngx_ajp_cpong_packet[] = {\n    0x41, 0x42, 0x00, 0x01, NGX_AJP_CPONG\n};\n\n\nstatic ngx_check_conf_t  ngx_check_types[] = {\n\n    { NGX_HTTP_CHECK_TCP,\n      ngx_string(\"tcp\"),\n      ngx_null_string,\n      0,\n      ngx_http_upstream_check_peek_handler,\n      ngx_http_upstream_check_peek_handler,\n      NULL,\n      NULL,\n      NULL,\n      0,\n      0 },\n\n    { NGX_HTTP_CHECK_HTTP,\n      ngx_string(\"http\"),\n      ngx_string(\"GET / HTTP/1.0\\r\\n\\r\\n\"),\n      NGX_CONF_BITMASK_SET | NGX_CHECK_HTTP_2XX | NGX_CHECK_HTTP_3XX,\n      ngx_http_upstream_check_send_handler,\n      ngx_http_upstream_check_recv_handler,\n      ngx_http_upstream_check_http_init,\n      ngx_http_upstream_check_http_parse,\n      ngx_http_upstream_check_http_reinit,\n      1,\n      1 },\n\n    { NGX_HTTP_CHECK_HTTP,\n      ngx_string(\"fastcgi\"),\n      ngx_null_string,\n      0,\n      ngx_http_upstream_check_send_handler,\n      ngx_http_upstream_check_recv_handler,\n      ngx_http_upstream_check_http_init,\n      ngx_http_upstream_check_fastcgi_parse,\n      ngx_http_upstream_check_http_reinit,\n      1,\n      0 },\n\n    { NGX_HTTP_CHECK_SSL_HELLO,\n      ngx_string(\"ssl_hello\"),\n      ngx_string(sslv3_client_hello_pkt),\n      0,\n      ngx_http_upstream_check_send_handler,\n      ngx_http_upstream_check_recv_handler,\n      ngx_http_upstream_check_ssl_hello_init,\n      ngx_http_upstream_check_ssl_hello_parse,\n      ngx_http_upstream_check_ssl_hello_reinit,\n      1,\n      0 },\n\n    { NGX_HTTP_CHECK_MYSQL,\n      ngx_string(\"mysql\"),\n      ngx_null_string,\n      0,\n      ngx_http_upstream_check_send_handler,\n      ngx_http_upstream_check_recv_handler,\n      ngx_http_upstream_check_mysql_init,\n      ngx_http_upstream_check_mysql_parse,\n      ngx_http_upstream_check_mysql_reinit,\n      1,\n      0 },\n\n    { NGX_HTTP_CHECK_AJP,\n      ngx_string(\"ajp\"),\n      ngx_string(ngx_ajp_cping_packet),\n      0,\n      ngx_http_upstream_check_send_handler,\n      ngx_http_upstream_check_recv_handler,\n      ngx_http_upstream_check_ajp_init,\n      ngx_http_upstream_check_ajp_parse,\n      ngx_http_upstream_check_ajp_reinit,\n      1,\n      0 },\n\n    { 0,\n      ngx_null_string,\n      ngx_null_string,\n      0,\n      NULL,\n      NULL,\n      NULL,\n      NULL,\n      NULL,\n      0,\n      0 }\n};\n\n\nstatic ngx_check_status_conf_t  ngx_check_status_formats[] = {\n\n    { ngx_string(\"html\"),\n      ngx_string(\"text/html\"),\n      ngx_http_upstream_check_status_html_format },\n\n    { ngx_string(\"csv\"),\n      ngx_string(\"text/plain\"),\n      ngx_http_upstream_check_status_csv_format },\n\n    { ngx_string(\"json\"),\n      ngx_string(\"application/json\"), /* RFC 4627 */\n      ngx_http_upstream_check_status_json_format },\n\n    { ngx_string(\"prometheus\"),\n      ngx_string(\"text/plain\"),\n      ngx_http_upstream_check_status_prometheus_format },\n\n    { ngx_null_string, ngx_null_string, NULL }\n};\n\n\nstatic ngx_check_status_command_t ngx_check_status_commands[] =  {\n\n    { ngx_string(\"format\"),\n      ngx_http_upstream_check_status_command_format },\n\n    { ngx_string(\"status\"),\n      ngx_http_upstream_check_status_command_status },\n\n    { ngx_null_string, NULL }\n};\n\n\nstatic ngx_uint_t ngx_http_upstream_check_shm_generation = 0;\nstatic ngx_http_upstream_check_peers_t *check_peers_ctx = NULL;\n\n\nngx_uint_t\nngx_http_upstream_check_add_dynamic_peer(ngx_pool_t *pool,\n    ngx_http_upstream_srv_conf_t *us, ngx_addr_t *peer_addr)\n{\n    void                                 *elts;\n    ngx_uint_t                            i, index;\n    ngx_http_upstream_check_peer_t       *peer, *p, *np;\n    ngx_http_upstream_check_peers_t      *peers;\n    ngx_http_upstream_check_srv_conf_t   *ucscf;\n    ngx_http_upstream_check_main_conf_t  *ucmcf;\n    ngx_http_upstream_check_peer_shm_t   *peer_shm;\n    ngx_http_upstream_check_peers_shm_t  *peers_shm;\n\n    if (check_peers_ctx == NULL || us->srv_conf == NULL) {\n        return NGX_ERROR;\n    }\n\n    ucscf = ngx_http_conf_upstream_srv_conf(us, ngx_http_upstream_check_module);\n\n    if(ucscf->check_interval == 0) {\n        return NGX_ERROR;\n    }\n\n    index = ngx_http_upstream_check_add_dynamic_peer_shm(pool,\n                                                         ucscf, peer_addr);\n    if (index == (ngx_uint_t) NGX_ERROR) {\n        return index;\n    }\n\n    peers_shm = check_peers_ctx->peers_shm;\n    peer_shm = peers_shm->peers;\n\n    ucmcf = ngx_http_cycle_get_module_main_conf(ngx_cycle,\n                                               ngx_http_upstream_check_module);\n    peers = ucmcf->peers;\n    peer = NULL;\n\n    p = peers->peers.elts;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pool->log, 0,\n                   \"http upstream check add dynamic upstream: %p, n: %ui\",\n                   p, peers->peers.nelts);\n\n    for (i = 0; i < peers->peers.nelts; i++) {\n\n        ngx_log_debug3(NGX_LOG_DEBUG_HTTP, pool->log, 0,\n                       \"http upstream check add [%ui], index=%ui, delete:%ud\",\n                       i, p[i].index, p[i].delete);\n\n        if (p[i].delete) {\n            p[i].delete = 0;\n            peer = &p[i];\n            break;\n        }\n    }\n\n    if (peer == NULL) {\n\n        elts = peers->peers.elts;\n\n        peer = ngx_array_push(&peers->peers);\n        if (peer == NULL) {\n            return NGX_ERROR;\n        }\n\n        if (elts != peers->peers.elts) {\n\n            ngx_log_error(NGX_LOG_INFO, pool->log, 0,\n                          \"http upstream check add peer realloc memory\");\n\n            /* reset all upstream peers' timers */\n            p = elts;\n            np = peers->peers.elts;\n\n            for (i = 0; i < peers->peers.nelts - 1; i++) {\n\n                if (p[i].delete) {\n                    continue;\n                }\n                ngx_log_error(NGX_LOG_INFO, pool->log, 0,\n                              \"http upstream %V old peer: %p, new peer: %p,\"\n                              \"old timer: %p, new timer: %p\",\n                              np[i].upstream_name,\n                              np[i].check_ev.data, &np[i],\n                              &p[i].check_ev, &np[i].check_ev);\n\n                ngx_http_upstream_check_clear_peer(&p[i]);\n\n                ngx_memzero(&np[i].pc, sizeof(ngx_peer_connection_t));\n                np[i].check_data = NULL;\n                np[i].pool = NULL;\n\n                ngx_http_upstream_check_add_timer(&np[i],\n                                                  np[i].conf->check_type_conf,\n                                                  0, pool->log);\n            }\n        }\n    }\n\n    ngx_memzero(peer, sizeof(ngx_http_upstream_check_peer_t));\n\n    peer->conf = ucscf;\n    peer->index = index;\n    peer->upstream_name = &us->host;\n    peer->peer_addr = peer_addr;\n\n    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, pool->log, 0,\n                   \"http upstream check add dynamic upstream: %V, \"\n                   \"peer: %V, index: %ui\",\n                   &us->host, &peer_addr->name, index);\n\n    if (ucscf->port) {\n        peer->check_peer_addr = ngx_pcalloc(pool, sizeof(ngx_addr_t));\n        if (peer->check_peer_addr == NULL) {\n            return NGX_ERROR;\n        }\n\n        if (ngx_http_upstream_check_addr_change_port(pool,\n                peer->check_peer_addr, peer_addr, ucscf->port)\n            != NGX_OK) {\n\n            return NGX_ERROR;\n        }\n\n    } else {\n        peer->check_peer_addr = peer->peer_addr;\n    }\n\n    peer->shm = &peer_shm[index];\n\n    ngx_http_upstream_check_add_timer(peer, ucscf->check_type_conf,\n                                      0, pool->log);\n\n    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, pool->log, 0,\n                   \"http upstream check add peer: %p, index: %ui, shm->ref: %i\",\n                   peer, peer->index, peer->shm->ref);\n\n    peers->checksum +=\n        ngx_murmur_hash2(peer_addr->name.data, peer_addr->name.len);\n\n    return peer->index;\n}\n\n\nngx_uint_t\nngx_http_upstream_check_add_peer(ngx_conf_t *cf,\n    ngx_http_upstream_srv_conf_t *us, ngx_addr_t *peer_addr)\n{\n    ngx_uint_t                            index;\n    ngx_http_upstream_check_peer_t       *peer;\n    ngx_http_upstream_check_peers_t      *peers;\n    ngx_http_upstream_check_srv_conf_t   *ucscf;\n    ngx_http_upstream_check_main_conf_t  *ucmcf;\n\n    if (us->srv_conf == NULL) {\n        return NGX_ERROR;\n    }\n\n    ucscf = ngx_http_conf_upstream_srv_conf(us, ngx_http_upstream_check_module);\n\n    if(ucscf->check_interval == 0) {\n        return NGX_ERROR;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, cf->log, 0,\n                   \"http upstream check add upstream process: %ui\",\n                   ngx_process);\n\n    if (ngx_process == NGX_PROCESS_WORKER) {\n        return ngx_http_upstream_check_add_dynamic_peer(cf->pool, us, peer_addr);\n    }\n\n    ucmcf = ngx_http_conf_get_module_main_conf(cf,\n                                               ngx_http_upstream_check_module);\n    peers = ucmcf->peers;\n\n    if (ucscf->unique) {\n        index = ngx_http_upstream_check_unique_peer(peers, peer_addr, ucscf);\n        if (index != (ngx_uint_t) NGX_ERROR) {\n            return index;\n        }\n    }\n\n    peer = ngx_array_push(&peers->peers);\n    if (peer == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_memzero(peer, sizeof(ngx_http_upstream_check_peer_t));\n\n    peer->index = peers->peers.nelts - 1;\n    peer->conf = ucscf;\n    peer->upstream_name = &us->host;\n    peer->peer_addr = peer_addr;\n\n    if (ucscf->port) {\n        peer->check_peer_addr = ngx_pcalloc(cf->pool, sizeof(ngx_addr_t));\n        if (peer->check_peer_addr == NULL) {\n            return NGX_ERROR;\n        }\n\n        if (ngx_http_upstream_check_addr_change_port(cf->pool,\n                peer->check_peer_addr, peer_addr, ucscf->port)\n            != NGX_OK) {\n\n            return NGX_ERROR;\n        }\n\n    } else {\n        peer->check_peer_addr = peer->peer_addr;\n    }\n\n    peers->checksum +=\n        ngx_murmur_hash2(peer_addr->name.data, peer_addr->name.len);\n\n    return peer->index;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_check_addr_change_port(ngx_pool_t *pool, ngx_addr_t *dst,\n    ngx_addr_t *src, ngx_uint_t port)\n{\n    size_t                len;\n    u_char               *p;\n    struct sockaddr_in   *sin;\n#if (NGX_HAVE_INET6)\n    struct sockaddr_in6  *sin6;\n#endif\n\n    dst->socklen = src->socklen;\n    dst->sockaddr = ngx_palloc(pool, dst->socklen);\n    if (dst->sockaddr == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_memcpy(dst->sockaddr, src->sockaddr, dst->socklen);\n\n    switch (dst->sockaddr->sa_family) {\n\n    case AF_INET:\n\n        len = NGX_INET_ADDRSTRLEN + sizeof(\":65535\") - 1;\n        sin = (struct sockaddr_in *) dst->sockaddr;\n        sin->sin_port = htons(port);\n\n        break;\n\n#if (NGX_HAVE_INET6)\n    case AF_INET6:\n\n        len = NGX_INET6_ADDRSTRLEN + sizeof(\":65535\") - 1;\n        sin6 = (struct sockaddr_in6 *) dst->sockaddr;\n        sin6->sin6_port = htons(port);\n\n        break;\n#endif\n\n    default:\n        return NGX_ERROR;\n    }\n\n    p = ngx_pnalloc(pool, len);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    len = ngx_sock_ntop(dst->sockaddr, dst->socklen, p, len, 1);\n\n    dst->name.len = len;\n    dst->name.data = p;\n\n    return NGX_OK;\n}\n\n\nvoid\nngx_http_upstream_check_delete_dynamic_peer(ngx_str_t *name,\n    ngx_addr_t *peer_addr)\n{\n    ngx_uint_t                            i;\n    ngx_http_upstream_check_peer_t       *peer, *chosen;\n    ngx_http_upstream_check_peers_t      *peers;\n\n    chosen = NULL;\n    peers = check_peers_ctx;\n    peer = peers->peers.elts;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                   \"http upstream check delete dynamic upstream: %p, n: %ui\",\n                   peer, peers->peers.nelts);\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                   \"http upstream check delete dynamic upstream: %V, \"\n                   \"peer: %V\", name, &peer_addr->name);\n\n    for (i = 0; i < peers->peers.nelts; i++) {\n        if (peer[i].delete) {\n            continue;\n        }\n\n        ngx_log_debug3(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                       \"http upstream check delete [%ui], index=%ui, addr:%V\",\n                       i, peer[i].index, &peer[i].peer_addr->name);\n\n        if (peer[i].upstream_name->len != name->len\n            || ngx_strncmp(peer[i].upstream_name->data,\n                           name->data, name->len) != 0) {\n            continue;\n        }\n\n        if (peer[i].peer_addr->socklen != peer_addr->socklen\n            || ngx_memcmp(peer[i].peer_addr->sockaddr, peer_addr->sockaddr,\n                          peer_addr->socklen) != 0) {\n            continue;\n        }\n\n        chosen = &peer[i];\n        break;\n    }\n\n    if (chosen == NULL) {\n        return;\n    }\n\n    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                   \"http upstream check delete peer: %p, index: %ui, \"\n                   \"shm->ref: %i\",\n                   chosen, chosen->index, chosen->shm->ref);\n\n    ngx_shmtx_lock(&chosen->shm->mutex);\n\n    if (chosen->shm->owner == ngx_pid) {\n        chosen->shm->owner = NGX_INVALID_PID;\n    }\n\n    chosen->shm->ref--;\n    if (chosen->shm->ref <= 0 && chosen->shm->delete != PEER_DELETED) {\n        ngx_http_upstream_check_clear_dynamic_peer_shm(chosen->shm);\n        chosen->shm->delete = PEER_DELETED;\n    }\n    ngx_shmtx_unlock(&chosen->shm->mutex);\n\n    ngx_http_upstream_check_clear_peer(chosen);\n}\n\n\nstatic ngx_uint_t\nngx_http_upstream_check_add_dynamic_peer_shm(ngx_pool_t *pool,\n    ngx_http_upstream_check_srv_conf_t *ucscf, ngx_addr_t *peer_addr)\n{\n    ngx_int_t                             rc;\n    ngx_uint_t                            i, index;\n    ngx_slab_pool_t                      *shpool;\n    ngx_http_upstream_check_peer_shm_t   *peer_shm;\n    ngx_http_upstream_check_peers_shm_t  *peers_shm;\n\n    if (check_peers_ctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    shpool = check_peers_ctx->shpool;\n    peers_shm = check_peers_ctx->peers_shm;\n    peer_shm = peers_shm->peers;\n    index = NGX_ERROR;\n\n    ngx_shmtx_lock(&shpool->mutex);\n\n    for (i = 0; i < peers_shm->number; i++) {\n\n        /* TODO: lock the peer mutex */\n        if (peer_shm[i].delete == PEER_DELETED) {\n            continue;\n        }\n\n        /* TODO: check the peer configure */\n        /* Merge the duplicate peer */\n        /* check the peer configure by check_type and check_send */\n        if (peer_addr->socklen == peer_shm[i].socklen\n            && ngx_memcmp(peer_addr->sockaddr, peer_shm[i].sockaddr,\n                          peer_addr->socklen) == 0\n            && peer_shm[i].checksum\n               == ngx_murmur_hash2(ucscf->send.data, ucscf->send.len))\n        {\n                ngx_shmtx_unlock(&shpool->mutex);\n                return i;\n        }\n    }\n\n    for (i = 0; i < peers_shm->number; i++) {\n\n        if (peer_shm[i].delete == PEER_DELETED) {\n            peer_shm[i].delete = PEER_NORMAL;\n            index = i;\n            break;\n        }\n    }\n\n    if (index == (ngx_uint_t) NGX_ERROR) {\n        if (peers_shm->number >= peers_shm->max_number) {\n            goto fail;\n        }\n\n        index = peers_shm->number++;\n    }\n\n    ngx_memzero(&peer_shm[index], sizeof(ngx_http_upstream_check_peer_shm_t));\n\n    peer_shm[index].socklen = peer_addr->socklen;\n    peer_shm[index].sockaddr = ngx_slab_alloc_locked(shpool,\n                                                     peer_shm->socklen);\n    if (peer_shm[index].sockaddr == NULL) {\n        goto fail;\n    }\n\n    ngx_memcpy(peer_shm[index].sockaddr, peer_addr->sockaddr,\n               peer_addr->socklen);\n\n    rc = ngx_http_upstream_check_init_shm_peer(&peer_shm[index], NULL,\n                                               ucscf->default_down, pool,\n                                               &peer_addr->name);\n    if (rc != NGX_OK) {\n        goto fail;\n    }\n\n    /* Set tag to peer_shm */\n    peer_shm[index].checksum = ngx_murmur_hash2(ucscf->send.data, ucscf->send.len);\n\n    ngx_shmtx_unlock(&shpool->mutex);\n    return index;\n\nfail:\n\n    ngx_shmtx_unlock(&shpool->mutex);\n    return NGX_ERROR;\n}\n\n\nstatic void\nngx_http_upstream_check_clear_dynamic_peer_shm(\n    ngx_http_upstream_check_peer_shm_t *peer_shm)\n{\n    if (check_peers_ctx == NULL) {\n        return;\n    }\n\n    ngx_slab_free_locked(check_peers_ctx->shpool, peer_shm->sockaddr);\n}\n\n\n\nstatic ngx_uint_t\nngx_http_upstream_check_unique_peer(ngx_http_upstream_check_peers_t *peers,\n    ngx_addr_t *peer_addr, ngx_http_upstream_check_srv_conf_t *peer_conf)\n{\n    ngx_uint_t                           i;\n    ngx_http_upstream_check_peer_t      *peer;\n    ngx_http_upstream_check_srv_conf_t  *opeer_conf;\n\n    peer = peers->peers.elts;\n    for (i = 0; i < peers->peers.nelts; i++) {\n\n        if (peer[i].delete) {\n            continue;\n        }\n\n        if (peer[i].peer_addr->socklen != peer_addr->socklen) {\n            continue;\n        }\n\n        if (ngx_memcmp(peer[i].peer_addr->sockaddr,\n                       peer_addr->sockaddr, peer_addr->socklen) != 0) {\n            continue;\n        }\n\n        opeer_conf = peer[i].conf;\n\n        if (opeer_conf->check_type_conf != peer_conf->check_type_conf) {\n            continue;\n        }\n\n        if (opeer_conf->send.len != peer_conf->send.len) {\n            continue;\n        }\n\n        if (ngx_strncmp(opeer_conf->send.data,\n                        peer_conf->send.data, peer_conf->send.len) != 0) {\n            continue;\n        }\n\n        if (opeer_conf->code.status_alive != peer_conf->code.status_alive) {\n            continue;\n        }\n\n        return i;\n    }\n\n    return NGX_ERROR;\n}\n\n\nngx_uint_t\nngx_http_upstream_check_peer_down(ngx_uint_t index)\n{\n    ngx_http_upstream_check_peer_shm_t   *peer_shm;\n\n    if (upstream_check_index_invalid(check_peers_ctx, index)) {\n        return 0;\n    }\n\n    peer_shm = check_peers_ctx->peers_shm->peers;\n\n    return (peer_shm[index].down);\n}\n\n\nngx_uint_t\nngx_http_upstream_check_upstream_down(ngx_str_t *upstream)\n{\n    ngx_uint_t i;\n    ngx_http_upstream_check_peer_t *peers;\n\n    if (check_peers_ctx == NULL) {\n        return 0;\n    }\n\n    peers = check_peers_ctx->peers.elts;\n    for (i = 0; i < check_peers_ctx->peers.nelts; i++) {\n        if (peers[i].upstream_name->len == upstream->len\n            && ngx_strncmp(peers[i].upstream_name->data, upstream->data, upstream->len) == 0)\n        {\n            if (!peers[i].shm->down) {\n                return 0;\n            }\n        }\n    }\n\n    return 1;\n}\n\n\n/* TODO: this interface can count each peer's busyness */\nvoid\nngx_http_upstream_check_get_peer(ngx_uint_t index)\n{\n    ngx_http_upstream_check_peer_t  *peer;\n\n    if (upstream_check_index_invalid(check_peers_ctx, index)) {\n        return;\n    }\n\n    peer = check_peers_ctx->peers.elts;\n\n    ngx_shmtx_lock(&peer[index].shm->mutex);\n\n    peer[index].shm->busyness++;\n    peer[index].shm->access_count++;\n\n    ngx_shmtx_unlock(&peer[index].shm->mutex);\n}\n\n\nvoid\nngx_http_upstream_check_free_peer(ngx_uint_t index)\n{\n    ngx_http_upstream_check_peer_t  *peer;\n\n    if (upstream_check_index_invalid(check_peers_ctx, index)) {\n        return;\n    }\n\n    peer = check_peers_ctx->peers.elts;\n\n    ngx_shmtx_lock(&peer[index].shm->mutex);\n\n    if (peer[index].shm->busyness > 0) {\n        peer[index].shm->busyness--;\n    }\n\n    ngx_shmtx_unlock(&peer[index].shm->mutex);\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_check_add_timers(ngx_cycle_t *cycle)\n{\n    ngx_uint_t                           i;\n    ngx_msec_t                           t, delay;\n    ngx_http_upstream_check_peer_t      *peer;\n    ngx_http_upstream_check_peers_t     *peers;\n    ngx_http_upstream_check_srv_conf_t  *ucscf;\n    ngx_http_upstream_check_peer_shm_t  *peer_shm;\n    ngx_http_upstream_check_peers_shm_t *peers_shm;\n\n    peers = check_peers_ctx;\n    if (peers == NULL) {\n        return NGX_OK;\n    }\n\n    peers_shm = peers->peers_shm;\n    if (peers_shm == NULL) {\n        return NGX_OK;\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, cycle->log, 0,\n                   \"http check upstream init_process, shm_name: %V, \"\n                   \"peer number: %ud\",\n                   &peers->check_shm_name,\n                   peers->peers.nelts);\n\n    srandom(ngx_pid);\n\n    peer = peers->peers.elts;\n    peer_shm = peers_shm->peers;\n\n    for (i = 0; i < peers->peers.nelts; i++) {\n\n        ucscf = peer[i].conf;\n\n        /*\n         * We add a random start time here, since we don't want to trigger\n         * the check events too close to each other at the beginning.\n         */\n        delay = ucscf->check_interval > 1000 ? ucscf->check_interval : 1000;\n        t = ngx_random() % delay;\n\n        peer[i].shm = &peer_shm[i];\n\n        ngx_http_upstream_check_add_timer(&peer[i], ucscf->check_type_conf, t, cycle->log);\n\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_check_add_timer(ngx_http_upstream_check_peer_t *peer,\n    ngx_check_conf_t *check_conf, ngx_msec_t timer, ngx_log_t *log)\n{\n    peer->check_ev.handler = ngx_http_upstream_check_begin_handler;\n    peer->check_ev.log = log;\n    peer->check_ev.data = peer;\n    peer->check_ev.timer_set = 0;\n\n    peer->check_timeout_ev.handler =\n        ngx_http_upstream_check_timeout_handler;\n    peer->check_timeout_ev.log = log;\n    peer->check_timeout_ev.data = peer;\n    peer->check_timeout_ev.timer_set = 0;\n\n    if (check_conf->need_pool) {\n        peer->pool = ngx_create_pool(ngx_pagesize, log);\n        if (peer->pool == NULL) {\n            return NGX_ERROR;\n        }\n    }\n\n    peer->send_handler = check_conf->send_handler;\n    peer->recv_handler = check_conf->recv_handler;\n\n    peer->init = check_conf->init;\n    peer->parse = check_conf->parse;\n    peer->reinit = check_conf->reinit;\n\n    ngx_add_timer(&peer->check_ev, timer);\n\n    /* TODO: lock */\n    peer->shm->ref++;\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_upstream_check_begin_handler(ngx_event_t *event)\n{\n    ngx_msec_t                           interval;\n    ngx_http_upstream_check_peer_t      *peer;\n    ngx_http_upstream_check_srv_conf_t  *ucscf;\n    ngx_http_upstream_check_peers_shm_t *peers_shm;\n\n    if (ngx_http_upstream_check_need_exit()) {\n        return;\n    }\n\n    if (check_peers_ctx == NULL) {\n        return;\n    }\n\n    peers_shm = check_peers_ctx->peers_shm;\n    peer = event->data;\n    ucscf = peer->conf;\n\n    ngx_add_timer(event, ucscf->check_interval / 2);\n\n    /* This process is processing this peer now. */\n    if (peer->shm->owner == ngx_pid ||\n        peer->check_timeout_ev.timer_set) {\n        return;\n    }\n\n    /* \n     * The current time maybe delayed(some operation take too long)\n     * We don't need to trigger the check event at this point.\n     */\n    if (ngx_current_msec < peer->shm->access_time) {\n        ngx_log_error(NGX_LOG_WARN, event->log, 0,\n                    \"time maybe delayed, got current_msec:%M, shm_access_time:%M\",\n                    ngx_current_msec, peer->shm->access_time);\n        return;\n    }\n\n    ngx_shmtx_lock(&peer->shm->mutex);\n\n    /* interval should be protected by mutex. */\n    interval = ngx_current_msec - peer->shm->access_time;\n    ngx_log_debug5(NGX_LOG_DEBUG_HTTP, event->log, 0,\n                   \"http check begin handler index: %ui, owner: %P, \"\n                   \"ngx_pid: %P, interval: %M, check_interval: %M\",\n                   peer->index, peer->shm->owner,\n                   ngx_pid, interval,\n                   ucscf->check_interval);\n\n    if (peers_shm->generation != ngx_http_upstream_check_shm_generation) {\n        ngx_shmtx_unlock(&peer->shm->mutex);\n        return;\n    }\n\n    if ((interval >= ucscf->check_interval)\n         && (peer->shm->owner == NGX_INVALID_PID)) {\n\n        peer->shm->owner = ngx_pid;\n\n    } else if (interval >= (ucscf->check_interval << 4)) {\n\n        /*\n         * If the check peer has been untouched for 2^4 times of\n         * the check interval, activate the current timer.\n         * Sometimes, the checking process may disappear\n         * under some abnormal circumstances, and the clean event\n         * will never be triggered.\n         */\n        peer->shm->owner = ngx_pid;\n        peer->shm->access_time = ngx_current_msec;\n    }\n\n    ngx_shmtx_unlock(&peer->shm->mutex);\n\n    if (peer->shm->owner == ngx_pid) {\n        ngx_http_upstream_check_connect_handler(event);\n    }\n}\n\n\nstatic void\nngx_http_upstream_check_connect_handler(ngx_event_t *event)\n{\n    ngx_int_t                            rc;\n    ngx_connection_t                    *c;\n    ngx_http_upstream_check_peer_t      *peer;\n    ngx_http_upstream_check_srv_conf_t  *ucscf;\n\n    if (ngx_http_upstream_check_need_exit()) {\n        return;\n    }\n\n    peer = event->data;\n    ucscf = peer->conf;\n\n    if (peer->pc.connection != NULL) {\n        c = peer->pc.connection;\n        if ((rc = ngx_http_upstream_check_peek_one_byte(c)) == NGX_OK) {\n            goto upstream_check_connect_done;\n        } else {\n            ngx_close_connection(c);\n            peer->pc.connection = NULL;\n        }\n    }\n    ngx_memzero(&peer->pc, sizeof(ngx_peer_connection_t));\n\n    peer->pc.sockaddr = peer->check_peer_addr->sockaddr;\n    peer->pc.socklen = peer->check_peer_addr->socklen;\n    peer->pc.name = &peer->check_peer_addr->name;\n\n    peer->pc.get = ngx_event_get_peer;\n    peer->pc.log = event->log;\n    peer->pc.log_error = NGX_ERROR_ERR;\n\n    peer->pc.cached = 0;\n    peer->pc.connection = NULL;\n\n    rc = ngx_event_connect_peer(&peer->pc);\n\n    if (rc == NGX_ERROR || rc == NGX_DECLINED) {\n        ngx_http_upstream_check_status_update(peer, 0);\n        ngx_http_upstream_check_clean_event(peer);\n        return;\n    }\n\n    /* NGX_OK or NGX_AGAIN */\n    c = peer->pc.connection;\n    c->data = peer;\n    c->log = peer->pc.log;\n    c->sendfile = 0;\n    c->read->log = c->log;\n    c->write->log = c->log;\n    c->pool = peer->pool;\n\nupstream_check_connect_done:\n    peer->state = NGX_HTTP_CHECK_CONNECT_DONE;\n\n    c->write->handler = peer->send_handler;\n    c->read->handler = peer->recv_handler;\n\n    ngx_add_timer(&peer->check_timeout_ev, ucscf->check_timeout);\n\n    /* The kqueue's loop interface needs it. */\n    if (rc == NGX_OK) {\n        c->write->handler(c->write);\n    }\n}\n\nstatic ngx_int_t\nngx_http_upstream_check_peek_one_byte(ngx_connection_t *c)\n{\n    char                            buf[1];\n    ngx_int_t                       n;\n    ngx_err_t                       err;\n\n    n = recv(c->fd, buf, 1, MSG_PEEK);\n    err = ngx_socket_errno;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, err,\n                   \"http check upstream recv(): %i, fd: %d\",\n                   n, c->fd);\n\n    if (n == 1 || (n == -1 && err == NGX_EAGAIN)) {\n        return NGX_OK;\n    } else {\n        return NGX_ERROR;\n    }\n}\n\nstatic void\nngx_http_upstream_check_peek_handler(ngx_event_t *event)\n{\n    ngx_connection_t               *c;\n    ngx_http_upstream_check_peer_t *peer;\n\n    if (ngx_http_upstream_check_need_exit()) {\n        return;\n    }\n\n    c = event->data;\n    peer = c->data;\n\n    if (ngx_http_upstream_check_peek_one_byte(c) == NGX_OK) {\n        ngx_http_upstream_check_status_update(peer, 1);\n\n    } else {\n        c->error = 1;\n        ngx_http_upstream_check_status_update(peer, 0);\n    }\n\n    ngx_http_upstream_check_clean_event(peer);\n\n    ngx_http_upstream_check_finish_handler(event);\n}\n\n\nstatic void\nngx_http_upstream_check_discard_handler(ngx_event_t *event)\n{\n    u_char                          buf[4096];\n    ssize_t                         size;\n    ngx_connection_t               *c;\n    ngx_http_upstream_check_peer_t *peer;\n\n    c = event->data;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"upstream check discard handler\");\n\n    if (ngx_http_upstream_check_need_exit()) {\n        return;\n    }\n\n    peer = c->data;\n\n    while (1) {\n        size = c->recv(c, buf, 4096);\n\n        if (size > 0) {\n            continue;\n\n        } else if (size == NGX_AGAIN) {\n            break;\n\n        } else {\n            if (size == 0) {\n                ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                               \"peer closed its half side of the connection\");\n            }\n\n            goto check_discard_fail;\n        }\n    }\n\n    if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n        goto check_discard_fail;\n    }\n\n    return;\n\n check_discard_fail:\n    c->error = 1;\n    ngx_http_upstream_check_clean_event(peer);\n}\n\n\nstatic void\nngx_http_upstream_check_dummy_handler(ngx_event_t *event)\n{\n    return;\n}\n\n\nstatic void\nngx_http_upstream_check_send_handler(ngx_event_t *event)\n{\n    ssize_t                         size;\n    ngx_connection_t               *c;\n    ngx_http_upstream_check_ctx_t  *ctx;\n    ngx_http_upstream_check_peer_t *peer;\n\n    if (ngx_http_upstream_check_need_exit()) {\n        return;\n    }\n\n    c = event->data;\n    peer = c->data;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"http check send.\");\n\n    if (c->pool == NULL) {\n        ngx_log_error(NGX_LOG_ERR, event->log, 0,\n                      \"check pool NULL with peer: %V \",\n                      &peer->check_peer_addr->name);\n\n        goto check_send_fail;\n    }\n\n    if (peer->state != NGX_HTTP_CHECK_CONNECT_DONE) {\n        if (ngx_handle_write_event(c->write, 0) != NGX_OK) {\n\n            ngx_log_error(NGX_LOG_ERR, event->log, 0,\n                          \"check handle write event error with peer: %V \",\n                          &peer->check_peer_addr->name);\n\n            goto check_send_fail;\n        }\n\n        return;\n    }\n\n    if (peer->check_data == NULL) {\n\n        peer->check_data = ngx_pcalloc(peer->pool,\n                                       sizeof(ngx_http_upstream_check_ctx_t));\n        if (peer->check_data == NULL) {\n            goto check_send_fail;\n        }\n\n        if (peer->init == NULL || peer->init(peer) != NGX_OK) {\n\n            ngx_log_error(NGX_LOG_ERR, event->log, 0,\n                          \"check init error with peer: %V \",\n                          &peer->check_peer_addr->name);\n\n            goto check_send_fail;\n        }\n    }\n\n    ctx = peer->check_data;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http check send total: %z\",\n                   ctx->send.last - ctx->send.pos);\n\n    while (ctx->send.pos < ctx->send.last) {\n\n        size = c->send(c, ctx->send.pos, ctx->send.last - ctx->send.pos);\n\n#if (NGX_DEBUG)\n        {\n        ngx_err_t  err;\n\n        err = (size >=0) ? 0 : ngx_socket_errno;\n        ngx_log_error(NGX_LOG_DEBUG, ngx_cycle->log, err,\n                       \"http check send size: %z, total: %z\",\n                       size, ctx->send.last - ctx->send.pos);\n        }\n#endif\n\n        if (size >= 0) {\n            ctx->send.pos += size;\n        } else if (size == NGX_AGAIN) {\n            return;\n        } else {\n            c->error = 1;\n            goto check_send_fail;\n        }\n    }\n\n    if (ctx->send.pos == ctx->send.last) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"http check send done.\");\n        peer->state = NGX_HTTP_CHECK_SEND_DONE;\n        c->requests++;\n    }\n\n    return;\n\ncheck_send_fail:\n    ngx_http_upstream_check_status_update(peer, 0);\n    ngx_http_upstream_check_clean_event(peer);\n}\n\n\nstatic void\nngx_http_upstream_check_recv_handler(ngx_event_t *event)\n{\n    u_char                         *new_buf;\n    ssize_t                         size, n;\n    ngx_int_t                       rc;\n    ngx_connection_t               *c;\n    ngx_http_upstream_check_ctx_t  *ctx;\n    ngx_http_upstream_check_peer_t *peer;\n\n    if (ngx_http_upstream_check_need_exit()) {\n        return;\n    }\n\n    c = event->data;\n    peer = c->data;\n\n    if (peer->state != NGX_HTTP_CHECK_SEND_DONE) {\n\n        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n            goto check_recv_fail;\n        }\n\n        return;\n    }\n\n    ctx = peer->check_data;\n\n    if (ctx->recv.start == NULL) {\n        /* half of the page_size, is it enough? */\n        ctx->recv.start = ngx_palloc(c->pool, ngx_pagesize / 2);\n        if (ctx->recv.start == NULL) {\n            goto check_recv_fail;\n        }\n\n        ctx->recv.last = ctx->recv.pos = ctx->recv.start;\n        ctx->recv.end = ctx->recv.start + ngx_pagesize / 2;\n    }\n\n    while (1) {\n        n = ctx->recv.end - ctx->recv.last;\n\n        /* buffer not big enough? enlarge it by twice */\n        if (n == 0) {\n            size = ctx->recv.end - ctx->recv.start;\n            new_buf = ngx_palloc(c->pool, size * 2);\n            if (new_buf == NULL) {\n                goto check_recv_fail;\n            }\n\n            ngx_memcpy(new_buf, ctx->recv.start, size);\n\n            ctx->recv.pos = ctx->recv.start = new_buf;\n            ctx->recv.last = new_buf + size;\n            ctx->recv.end = new_buf + size * 2;\n\n            n = ctx->recv.end - ctx->recv.last;\n        }\n\n        size = c->recv(c, ctx->recv.last, n);\n\n#if (NGX_DEBUG)\n        {\n        ngx_err_t  err;\n\n        err = (size >= 0) ? 0 : ngx_socket_errno;\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, err,\n                       \"http check recv size: %z, peer: %V \",\n                       size, &peer->check_peer_addr->name);\n        }\n#endif\n\n        if (size > 0) {\n            ctx->recv.last += size;\n            continue;\n        } else if (size == 0 || size == NGX_AGAIN) {\n            break;\n        } else {\n            c->error = 1;\n            goto check_recv_fail;\n        }\n    }\n\n    rc = peer->parse(peer);\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http check parse rc: %i, peer: %V \",\n                   rc, &peer->check_peer_addr->name);\n\n    switch (rc) {\n\n    case NGX_AGAIN:\n        /* The peer has closed its half side of the connection. */\n        return;\n\n    case NGX_ERROR:\n        ngx_log_error(NGX_LOG_ERR, event->log, 0,\n                      \"check protocol %V error with peer: %V \",\n                      &peer->conf->check_type_conf->name,\n                      &peer->check_peer_addr->name);\n\n        ngx_http_upstream_check_status_update(peer, 0);\n        break;\n\n    case NGX_OK:\n        /* fall through */\n\n    default:\n        ngx_http_upstream_check_status_update(peer, 1);\n        break;\n    }\n\n    peer->state = NGX_HTTP_CHECK_RECV_DONE;\n    ngx_http_upstream_check_clean_event(peer);\n    return;\n\ncheck_recv_fail:\n\n    ngx_http_upstream_check_status_update(peer, 0);\n    ngx_http_upstream_check_clean_event(peer);\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_check_http_init(ngx_http_upstream_check_peer_t *peer)\n{\n    ngx_http_upstream_check_ctx_t       *ctx;\n    ngx_http_upstream_check_srv_conf_t  *ucscf;\n\n    ctx = peer->check_data;\n    ucscf = peer->conf;\n\n    ctx->send.start = ctx->send.pos = (u_char *)ucscf->send.data;\n    ctx->send.end = ctx->send.last = ctx->send.start + ucscf->send.len;\n\n    ctx->recv.start = ctx->recv.pos = NULL;\n    ctx->recv.end = ctx->recv.last = NULL;\n\n    ctx->state = 0;\n\n    ngx_memzero(&ctx->status, sizeof(ngx_http_status_t));\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_check_http_parse(ngx_http_upstream_check_peer_t *peer)\n{\n    ngx_int_t                            rc;\n    ngx_uint_t                           code, code_n;\n    ngx_http_upstream_check_ctx_t       *ctx;\n    ngx_http_upstream_check_srv_conf_t  *ucscf;\n\n    ucscf = peer->conf;\n    ctx = peer->check_data;\n\n    if ((ctx->recv.last - ctx->recv.pos) > 0) {\n\n        rc = ngx_http_upstream_check_parse_status_line(ctx,\n                                                       &ctx->recv,\n                                                       &ctx->status);\n        if (rc == NGX_AGAIN) {\n            return rc;\n        }\n\n        if (rc == NGX_ERROR) {\n            ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                          \"http parse status line error with peer: %V \",\n                          &peer->check_peer_addr->name);\n            return rc;\n        }\n\n        code = ctx->status.code;\n\n        if (code > 0 && code < 200) {\n            code_n = NGX_CHECK_HTTP_1XX;\n        } else if (code >= 200 && code < 300) {\n            code_n = NGX_CHECK_HTTP_2XX;\n        } else if (code >= 300 && code < 400) {\n            code_n = NGX_CHECK_HTTP_3XX;\n        } else if (code >= 400 && code < 500) {\n            peer->pc.connection->error = 1;\n            code_n = NGX_CHECK_HTTP_4XX;\n        } else if (code >= 500 && code < 600) {\n            peer->pc.connection->error = 1;\n            code_n = NGX_CHECK_HTTP_5XX;\n        } else {\n            peer->pc.connection->error = 1;\n            code_n = NGX_CHECK_HTTP_ERR;\n        }\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                       \"http_parse: code_n: %ui, conf: %ui\",\n                       code_n, ucscf->code.status_alive);\n\n        if (code_n & ucscf->code.status_alive) {\n            return NGX_OK;\n        } else {\n            return NGX_ERROR;\n        }\n    } else {\n        return NGX_AGAIN;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_check_fastcgi_process_record(\n    ngx_http_upstream_check_ctx_t *ctx, ngx_buf_t *b, ngx_http_status_t *status)\n{\n    u_char                     ch, *p;\n    ngx_http_fastcgi_state_e   state;\n\n    state = ctx->state;\n\n    for (p = b->pos; p < b->last; p++) {\n\n        ch = *p;\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                       \"http fastcgi record byte: %02Xd\", ch);\n\n        switch (state) {\n\n        case ngx_http_fastcgi_st_version:\n            if (ch != 1) {\n                ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                              \"upstream sent unsupported FastCGI \"\n                              \"protocol version: %d\", ch);\n                return NGX_ERROR;\n            }\n            state = ngx_http_fastcgi_st_type;\n            break;\n\n        case ngx_http_fastcgi_st_type:\n            switch (ch) {\n            case NGX_HTTP_FASTCGI_STDOUT:\n            case NGX_HTTP_FASTCGI_STDERR:\n            case NGX_HTTP_FASTCGI_END_REQUEST:\n                status->code = (ngx_uint_t) ch;\n                break;\n            default:\n                ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                              \"upstream sent invalid FastCGI \"\n                              \"record type: %d\", ch);\n                return NGX_ERROR;\n\n            }\n            state = ngx_http_fastcgi_st_request_id_hi;\n            break;\n\n        /* we support the single request per connection */\n\n        case ngx_http_fastcgi_st_request_id_hi:\n            if (ch != 0) {\n                ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                              \"upstream sent unexpected FastCGI \"\n                              \"request id high byte: %d\", ch);\n                return NGX_ERROR;\n            }\n            state = ngx_http_fastcgi_st_request_id_lo;\n            break;\n\n        case ngx_http_fastcgi_st_request_id_lo:\n            if (ch != 1) {\n                ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                              \"upstream sent unexpected FastCGI \"\n                              \"request id low byte: %d\", ch);\n                return NGX_ERROR;\n            }\n            state = ngx_http_fastcgi_st_content_length_hi;\n            break;\n\n        case ngx_http_fastcgi_st_content_length_hi:\n            ctx->length = ch << 8;\n            state = ngx_http_fastcgi_st_content_length_lo;\n            break;\n\n        case ngx_http_fastcgi_st_content_length_lo:\n            ctx->length |= (size_t) ch;\n            state = ngx_http_fastcgi_st_padding_length;\n            break;\n\n        case ngx_http_fastcgi_st_padding_length:\n            ctx->padding = (size_t) ch;\n            state = ngx_http_fastcgi_st_reserved;\n            break;\n\n        case ngx_http_fastcgi_st_reserved:\n            state = ngx_http_fastcgi_st_data;\n\n            b->pos = p + 1;\n            ctx->state = state;\n\n            return NGX_OK;\n\n        /* suppress warning */\n        case ngx_http_fastcgi_st_data:\n        case ngx_http_fastcgi_st_padding:\n            break;\n        }\n    }\n\n    ctx->state = state;\n\n    return NGX_AGAIN;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_check_fastcgi_parse(ngx_http_upstream_check_peer_t *peer)\n{\n    ngx_int_t                            rc;\n    ngx_flag_t                           done;\n    ngx_uint_t                           type, code, code_n;\n    ngx_http_upstream_check_ctx_t       *ctx;\n    ngx_http_upstream_check_srv_conf_t  *ucscf;\n\n    ucscf = peer->conf;\n    ctx = peer->check_data;\n\n    if ((ctx->recv.last - ctx->recv.pos) <= 0) {\n        return NGX_AGAIN;\n    }\n\n    done = 0;\n\n    for ( ;; ) {\n\n        if (ctx->state < ngx_http_fastcgi_st_data) {\n            rc = ngx_http_upstream_check_fastcgi_process_record(ctx,\n                    &ctx->recv, &ctx->status);\n\n            type = ctx->status.code;\n\n            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                           \"fastcgi_parse rc: [%i], type: [%ui]\", rc, type);\n\n            if (rc == NGX_AGAIN) {\n                return rc;\n            }\n\n            if (rc == NGX_ERROR) {\n                ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                   \"check fastcgi parse status line error with peer: %V\",\n                   &peer->check_peer_addr->name);\n\n                return rc;\n            }\n\n            if (type != NGX_HTTP_FASTCGI_STDOUT\n                && type != NGX_HTTP_FASTCGI_STDERR)\n            {\n                ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                   \"check fastcgi sent unexpected FastCGI record: %d\", type);\n\n                return NGX_ERROR;\n            }\n\n            if (type == NGX_HTTP_FASTCGI_STDOUT && ctx->length == 0) {\n                ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                   \"check fastcgi prematurely closed FastCGI stdout\");\n\n                return NGX_ERROR;\n            }\n        }\n\n        if (ctx->state == ngx_http_fastcgi_st_padding) {\n\n            if (ctx->recv.pos + ctx->padding < ctx->recv.last) {\n                ctx->status.code = ngx_http_fastcgi_st_version;\n                ctx->recv.pos += ctx->padding;\n\n                continue;\n            }\n\n            if (ctx->recv.pos + ctx->padding == ctx->recv.last) {\n                ctx->status.code = ngx_http_fastcgi_st_version;\n                ctx->recv.pos = ctx->recv.last;\n\n                return NGX_AGAIN;\n            }\n\n            ctx->padding -= ctx->recv.last - ctx->recv.pos;\n            ctx->recv.pos = ctx->recv.last;\n\n            return NGX_AGAIN;\n        }\n\n        if (ctx->status.code == NGX_HTTP_FASTCGI_STDERR) {\n\n            ngx_log_error(NGX_LOG_WARN, ngx_cycle->log, 0,\n                          \"fastcgi check error\");\n\n            return NGX_ERROR;\n        }\n\n        /* ctx->status.code == NGX_HTTP_FASTCGI_STDOUT */\n\n        if (ctx->recv.pos + ctx->length < ctx->recv.last) {\n            ctx->recv.last = ctx->recv.pos + ctx->length;\n        } else {\n            return NGX_ERROR;\n        }\n\n        ctx->status.code = 0;\n\n        for ( ;; ) {\n            rc = ngx_http_upstream_check_parse_fastcgi_status(ctx,\n                                                              &ctx->recv,\n                                                              &ctx->status);\n            ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, 0,\n                          \"fastcgi http parse status line rc: %i \", rc);\n\n            if (rc == NGX_ERROR) {\n                ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                   \"fastcgi http parse status line error with peer: %V \",\n                    &peer->check_peer_addr->name);\n                return NGX_ERROR;\n            }\n\n            if (rc == NGX_AGAIN) {\n                break;\n            }\n\n            if (rc == NGX_DONE) {\n                done = 1;\n                ngx_log_error(NGX_LOG_DEBUG, ngx_cycle->log, 0,\n                              \"fastcgi http parse status: %i\",\n                              ctx->status.code);\n                break;\n            }\n\n            /* rc = NGX_OK */\n        }\n\n        if (ucscf->code.status_alive == 0 || done == 0) {\n            return NGX_OK;\n        }\n\n        code = ctx->status.code;\n\n        if (code > 0 && code < 200) {\n            code_n = NGX_CHECK_HTTP_1XX;\n        } else if (code >= 200 && code < 300) {\n            code_n = NGX_CHECK_HTTP_2XX;\n        } else if (code >= 300 && code < 400) {\n            code_n = NGX_CHECK_HTTP_3XX;\n        } else if (code >= 400 && code < 500) {\n            code_n = NGX_CHECK_HTTP_4XX;\n        } else if (code >= 500 && code < 600) {\n            code_n = NGX_CHECK_HTTP_5XX;\n        } else {\n            code_n = NGX_CHECK_HTTP_ERR;\n        }\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                       \"fastcgi http_parse: code_n: %ui, conf: %ui\",\n                       code_n, ucscf->code.status_alive);\n\n        if (code_n & ucscf->code.status_alive) {\n            return NGX_OK;\n        } else {\n            return NGX_ERROR;\n        }\n\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_check_parse_fastcgi_status(ngx_http_upstream_check_ctx_t *ctx,\n    ngx_buf_t *b, ngx_http_status_t *status)\n{\n    u_char      c, ch, *p, *name_s, *name_e;\n    ngx_flag_t  find;\n\n    enum {\n        sw_start = 0,\n        sw_name,\n        sw_space_before_value,\n        sw_value,\n        sw_space_after_value,\n        sw_ignore_line,\n        sw_almost_done,\n        sw_header_almost_done\n    } state;\n\n    /* the last '\\0' is not needed because string is zero terminated */\n\n    static u_char  lowcase[] =\n        \"\\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        \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0-\\0\\0\" \"0123456789\\0\\0\\0\\0\\0\\0\"\n        \"\\0abcdefghijklmnopqrstuvwxyz\\0\\0\\0\\0\\0\"\n        \"\\0abcdefghijklmnopqrstuvwxyz\\0\\0\\0\\0\\0\"\n        \"\\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        \"\\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        \"\\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        \"\\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\n    status->count = 0;\n    status->code = 0;\n    find = 0;\n    name_s = name_e = NULL;\n    state = sw_start;\n\n    for (p = b->pos; p < b->last; p++) {\n        ch = *p;\n\n        switch (state) {\n\n        /* first char */\n        case sw_start:\n\n            switch (ch) {\n            case CR:\n                state = sw_header_almost_done;\n                break;\n            case LF:\n                goto header_done;\n            default:\n                state = sw_name;\n\n                c = lowcase[ch];\n\n                if (c) {\n                    name_s = p;\n                    break;\n                }\n\n                if (ch == '\\0') {\n                    return NGX_ERROR;\n                }\n\n\n                break;\n            }\n\n            break;\n\n        /* header name */\n        case sw_name:\n            c = lowcase[ch];\n\n            if (c) {\n                break;\n            }\n\n            if (ch == ':') {\n                name_e = p;\n#if (NGX_DEBUG)\n                ngx_str_t name;\n                name.data = name_s;\n                name.len = name_e - name_s;\n                ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                               \"fastcgi header: %V\", &name);\n#endif\n                state = sw_space_before_value;\n\n                if (ngx_strncasecmp(name_s, (u_char *) \"status\",\n                                    name_e - name_s)\n                    == 0)\n                {\n\n                    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                                   \"find status header\");\n\n                    find = 1;\n                }\n\n                break;\n            }\n\n            if (ch == CR) {\n                state = sw_almost_done;\n                break;\n            }\n\n            if (ch == LF) {\n                goto done;\n            }\n\n            /* IIS may send the duplicate \"HTTP/1.1 ...\" lines */\n            if (ch == '\\0') {\n                return NGX_ERROR;\n            }\n\n            break;\n\n        /* space* before header value */\n        case sw_space_before_value:\n            switch (ch) {\n            case ' ':\n                break;\n            case CR:\n                state = sw_almost_done;\n                break;\n            case LF:\n                goto done;\n            case '\\0':\n                return NGX_ERROR;\n            default:\n                state = sw_value;\n                if (find) {\n                    if (ch < '1' || ch > '9') {\n                        return NGX_ERROR;\n                    }\n\n                    status->code = status->code * 10 + ch - '0';\n                    if (status->count++ != 0) {\n                        return NGX_ERROR;\n                    }\n                }\n\n                break;\n            }\n\n            break;\n\n        /* header value */\n        case sw_value:\n\n            if (find) {\n                if (ch < '0' || ch > '9') {\n                    return NGX_ERROR;\n                }\n\n                status->code = status->code * 10 + ch - '0';\n\n                if (++status->count == 3) {\n                    return NGX_DONE;\n                }\n            }\n\n            switch (ch) {\n            case ' ':\n                state = sw_space_after_value;\n                break;\n            case CR:\n                state = sw_almost_done;\n                break;\n            case LF:\n                goto done;\n            case '\\0':\n                return NGX_ERROR;\n            }\n\n            break;\n\n        /* space* before end of header line */\n        case sw_space_after_value:\n            switch (ch) {\n            case ' ':\n                break;\n            case CR:\n                state = sw_almost_done;\n                break;\n            case LF:\n                state = sw_start;\n                break;\n            case '\\0':\n                return NGX_ERROR;\n            default:\n                state = sw_value;\n                break;\n            }\n            break;\n\n        /* ignore header line */\n        case sw_ignore_line:\n            switch (ch) {\n            case LF:\n                state = sw_start;\n                break;\n            default:\n                break;\n            }\n            break;\n\n        /* end of header line */\n        case sw_almost_done:\n            switch (ch) {\n            case LF:\n                goto done;\n            case CR:\n                break;\n            default:\n                return NGX_ERROR;\n            }\n            break;\n\n        /* end of header */\n        case sw_header_almost_done:\n            switch (ch) {\n            case LF:\n                goto header_done;\n            default:\n                return NGX_ERROR;\n            }\n        }\n    }\n\n    b->pos = p;\n    ctx->state = state;\n\n    return NGX_AGAIN;\n\ndone:\n\n    b->pos = p + 1;\n    ctx->state = sw_start;\n\n    return NGX_OK;\n\nheader_done:\n\n    b->pos = p + 1;\n    ctx->state = sw_start;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_check_parse_status_line(ngx_http_upstream_check_ctx_t *ctx,\n    ngx_buf_t *b, ngx_http_status_t *status)\n{\n    u_char ch, *p;\n    enum {\n        sw_start = 0,\n        sw_H,\n        sw_HT,\n        sw_HTT,\n        sw_HTTP,\n        sw_first_major_digit,\n        sw_major_digit,\n        sw_first_minor_digit,\n        sw_minor_digit,\n        sw_status,\n        sw_space_after_status,\n        sw_status_text,\n        sw_almost_done\n    } state;\n\n    state = ctx->state;\n\n    for (p = b->pos; p < b->last; p++) {\n        ch = *p;\n\n        switch (state) {\n\n        /* \"HTTP/\" */\n        case sw_start:\n            if (ch != 'H') {\n                return NGX_ERROR;\n            }\n\n            state = sw_H;\n            break;\n\n        case sw_H:\n            if (ch != 'T') {\n                return NGX_ERROR;\n            }\n\n            state = sw_HT;\n            break;\n\n        case sw_HT:\n            if (ch != 'T') {\n                return NGX_ERROR;\n            }\n\n            state = sw_HTT;\n            break;\n\n        case sw_HTT:\n            if (ch != 'P') {\n                return NGX_ERROR;\n            }\n\n            state = sw_HTTP;\n            break;\n\n        case sw_HTTP:\n            if (ch != '/') {\n                return NGX_ERROR;\n            }\n\n            state = sw_first_major_digit;\n            break;\n\n        /* the first digit of major HTTP version */\n        case sw_first_major_digit:\n            if (ch < '1' || ch > '9') {\n                return NGX_ERROR;\n            }\n\n            state = sw_major_digit;\n            break;\n\n        /* the major HTTP version or dot */\n        case sw_major_digit:\n            if (ch == '.') {\n                state = sw_first_minor_digit;\n                break;\n            }\n\n            if (ch < '0' || ch > '9') {\n                return NGX_ERROR;\n            }\n\n            break;\n\n        /* the first digit of minor HTTP version */\n        case sw_first_minor_digit:\n            if (ch < '0' || ch > '9') {\n                return NGX_ERROR;\n            }\n\n            state = sw_minor_digit;\n            break;\n\n        /* the minor HTTP version or the end of the request line */\n        case sw_minor_digit:\n            if (ch == ' ') {\n                state = sw_status;\n                break;\n            }\n\n            if (ch < '0' || ch > '9') {\n                return NGX_ERROR;\n            }\n\n            break;\n\n        /* HTTP status code */\n        case sw_status:\n            if (ch == ' ') {\n                break;\n            }\n\n            if (ch < '0' || ch > '9') {\n                return NGX_ERROR;\n            }\n\n            status->code = status->code * 10 + ch - '0';\n\n            if (++status->count == 3) {\n                state = sw_space_after_status;\n                status->start = p - 2;\n            }\n\n            break;\n\n        /* space or end of line */\n        case sw_space_after_status:\n            switch (ch) {\n            case ' ':\n                state = sw_status_text;\n                break;\n            case '.':                    /* IIS may send 403.1, 403.2, etc */\n                state = sw_status_text;\n                break;\n            case CR:\n                state = sw_almost_done;\n                break;\n            case LF:\n                goto done;\n            default:\n                return NGX_ERROR;\n            }\n            break;\n\n        /* any text until end of line */\n        case sw_status_text:\n            switch (ch) {\n            case CR:\n                state = sw_almost_done;\n\n                break;\n            case LF:\n                goto done;\n            }\n            break;\n\n        /* end of status line */\n        case sw_almost_done:\n            status->end = p - 1;\n            if (ch == LF) {\n                goto done;\n            } else {\n                return NGX_ERROR;\n            }\n        }\n    }\n\n    b->pos = p;\n    ctx->state = state;\n\n    return NGX_AGAIN;\n\ndone:\n\n    b->pos = p + 1;\n\n    if (status->end == NULL) {\n        status->end = p;\n    }\n\n    ctx->state = sw_start;\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_upstream_check_http_reinit(ngx_http_upstream_check_peer_t *peer)\n{\n    ngx_http_upstream_check_ctx_t  *ctx;\n\n    ctx = peer->check_data;\n\n    ctx->send.pos = ctx->send.start;\n    ctx->send.last = ctx->send.end;\n\n    ctx->recv.pos = ctx->recv.last = ctx->recv.start;\n\n    ctx->state = 0;\n\n    ngx_memzero(&ctx->status, sizeof(ngx_http_status_t));\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_check_ssl_hello_init(ngx_http_upstream_check_peer_t *peer)\n{\n    ngx_http_upstream_check_ctx_t       *ctx;\n    ngx_http_upstream_check_srv_conf_t  *ucscf;\n\n    ctx = peer->check_data;\n    ucscf = peer->conf;\n\n    ctx->send.start = ctx->send.pos = (u_char *)ucscf->send.data;\n    ctx->send.end = ctx->send.last = ctx->send.start + ucscf->send.len;\n\n    ctx->recv.start = ctx->recv.pos = NULL;\n    ctx->recv.end = ctx->recv.last = NULL;\n\n    return NGX_OK;\n}\n\n\n/* a rough check of server ssl_hello responses */\nstatic ngx_int_t\nngx_http_upstream_check_ssl_hello_parse(ngx_http_upstream_check_peer_t *peer)\n{\n    size_t                         size;\n    ngx_ssl_server_hello_t        *resp;\n    ngx_http_upstream_check_ctx_t *ctx;\n\n    ctx = peer->check_data;\n\n    size = ctx->recv.last - ctx->recv.pos;\n    if (size < sizeof(ngx_ssl_server_hello_t)) {\n        return NGX_AGAIN;\n    }\n\n    resp = (ngx_ssl_server_hello_t *) ctx->recv.pos;\n\n    ngx_log_debug7(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                   \"http check ssl_parse, type: %ud, version: %ud.%ud, \"\n                   \"length: %ud, handshanke_type: %ud, hello_version: %ud.%ud\",\n                   resp->msg_type, resp->version.major, resp->version.minor,\n                   ntohs(resp->length), resp->handshake_type,\n                   resp->hello_version.major, resp->hello_version.minor);\n\n    if (resp->msg_type != NGX_SSL_HANDSHAKE) {\n        return NGX_ERROR;\n    }\n\n    if (resp->handshake_type != NGX_SSL_SERVER_HELLO) {\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_upstream_check_ssl_hello_reinit(ngx_http_upstream_check_peer_t *peer)\n{\n    ngx_http_upstream_check_ctx_t *ctx;\n\n    ctx = peer->check_data;\n\n    ctx->send.pos = ctx->send.start;\n    ctx->send.last = ctx->send.end;\n\n    ctx->recv.pos = ctx->recv.last = ctx->recv.start;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_check_mysql_init(ngx_http_upstream_check_peer_t *peer)\n{\n    ngx_http_upstream_check_ctx_t       *ctx;\n    ngx_http_upstream_check_srv_conf_t  *ucscf;\n\n    ctx = peer->check_data;\n    ucscf = peer->conf;\n\n    ctx->send.start = ctx->send.pos = (u_char *)ucscf->send.data;\n    ctx->send.end = ctx->send.last = ctx->send.start + ucscf->send.len;\n\n    ctx->recv.start = ctx->recv.pos = NULL;\n    ctx->recv.end = ctx->recv.last = NULL;\n\n    return NGX_OK;\n}\n\n\n/* a rough check of mysql greeting responses */\nstatic ngx_int_t\nngx_http_upstream_check_mysql_parse(ngx_http_upstream_check_peer_t *peer)\n{\n    size_t                         size;\n    ngx_mysql_handshake_init_t    *handshake;\n    ngx_http_upstream_check_ctx_t *ctx;\n\n    ctx = peer->check_data;\n\n    size = ctx->recv.last - ctx->recv.pos;\n    if (size < sizeof(ngx_mysql_handshake_init_t)) {\n        return NGX_AGAIN;\n    }\n\n    handshake = (ngx_mysql_handshake_init_t *) ctx->recv.pos;\n\n    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                   \"mysql_parse: packet_number=%ud, protocol=%ud, server=%s\",\n                   handshake->packet_number, handshake->protocol_version,\n                   handshake->others);\n\n    /* The mysql greeting packet's serial number always begins with 0. */\n    if (handshake->packet_number != 0x00) {\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_upstream_check_mysql_reinit(ngx_http_upstream_check_peer_t *peer)\n{\n    ngx_http_upstream_check_ctx_t *ctx;\n\n    ctx = peer->check_data;\n\n    ctx->send.pos = ctx->send.start;\n    ctx->send.last = ctx->send.end;\n\n    ctx->recv.pos = ctx->recv.last = ctx->recv.start;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_check_ajp_init(ngx_http_upstream_check_peer_t *peer)\n{\n    ngx_http_upstream_check_ctx_t       *ctx;\n    ngx_http_upstream_check_srv_conf_t  *ucscf;\n\n    ctx = peer->check_data;\n    ucscf = peer->conf;\n\n    ctx->send.start = ctx->send.pos = (u_char *)ucscf->send.data;\n    ctx->send.end = ctx->send.last = ctx->send.start + ucscf->send.len;\n\n    ctx->recv.start = ctx->recv.pos = NULL;\n    ctx->recv.end = ctx->recv.last = NULL;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_check_ajp_parse(ngx_http_upstream_check_peer_t *peer)\n{\n    size_t                         size;\n    u_char                        *p;\n    ngx_http_upstream_check_ctx_t *ctx;\n\n    ctx = peer->check_data;\n\n    size = ctx->recv.last - ctx->recv.pos;\n    if (size < sizeof(ngx_ajp_cpong_packet)) {\n        return NGX_AGAIN;\n    }\n\n    p = ctx->recv.pos;\n\n#if (NGX_DEBUG)\n    {\n    ngx_ajp_raw_packet_t  *ajp;\n\n    ajp = (ngx_ajp_raw_packet_t *) p;\n    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                   \"ajp_parse: preamble=0x%uxd, length=0x%uxd, type=0x%uxd\",\n                   ntohs(ajp->preamble), ntohs(ajp->length), ajp->type);\n    }\n#endif\n\n    if (ngx_memcmp(ngx_ajp_cpong_packet, p, sizeof(ngx_ajp_cpong_packet)) == 0)\n    {\n        return NGX_OK;\n    } else {\n        return NGX_ERROR;\n    }\n}\n\n\nstatic void\nngx_http_upstream_check_ajp_reinit(ngx_http_upstream_check_peer_t *peer)\n{\n    ngx_http_upstream_check_ctx_t  *ctx;\n\n    ctx = peer->check_data;\n\n    ctx->send.pos = ctx->send.start;\n    ctx->send.last = ctx->send.end;\n\n    ctx->recv.pos = ctx->recv.last = ctx->recv.start;\n}\n\n\nstatic void\nngx_http_upstream_check_status_update(ngx_http_upstream_check_peer_t *peer,\n    ngx_int_t result)\n{\n    ngx_http_upstream_check_srv_conf_t  *ucscf;\n\n    ucscf = peer->conf;\n\n    ngx_shmtx_lock(&peer->shm->mutex);\n\n    if (peer->shm->delete == PEER_DELETED) {\n\n        ngx_shmtx_unlock(&peer->shm->mutex);\n        return;\n    }\n\n    if (result) {\n        peer->shm->rise_count++;\n        peer->shm->fall_count = 0;\n        if (peer->shm->down && peer->shm->rise_count >= ucscf->rise_count) {\n            peer->shm->down = 0;\n            ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                          \"enable check peer: %V \",\n                          &peer->check_peer_addr->name);\n        }\n    } else {\n        peer->shm->rise_count = 0;\n        peer->shm->fall_count++;\n        if (!peer->shm->down && peer->shm->fall_count >= ucscf->fall_count) {\n            peer->shm->down = 1;\n            ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                          \"disable check peer: %V \",\n                          &peer->check_peer_addr->name);\n        }\n    }\n\n    peer->shm->access_time = ngx_current_msec;\n\n    ngx_shmtx_unlock(&peer->shm->mutex);\n}\n\n\nstatic void\nngx_http_upstream_check_clean_event(ngx_http_upstream_check_peer_t *peer)\n{\n    ngx_connection_t                    *c;\n    ngx_http_upstream_check_srv_conf_t  *ucscf;\n    ngx_check_conf_t                    *cf;\n\n    c = peer->pc.connection;\n    ucscf = peer->conf;\n    cf = ucscf->check_type_conf;\n\n    if (c) {\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"http check clean event: index:%i, fd: %d\",\n                       peer->index, c->fd);\n        if (c->error == 0 &&\n            cf->need_keepalive &&\n            (c->requests < ucscf->check_keepalive_requests))\n        {\n            c->write->handler = ngx_http_upstream_check_dummy_handler;\n            c->read->handler = ngx_http_upstream_check_discard_handler;\n        } else {\n            ngx_close_connection(c);\n            peer->pc.connection = NULL;\n        }\n    }\n\n    if (peer->check_timeout_ev.timer_set) {\n        ngx_del_timer(&peer->check_timeout_ev);\n    }\n\n    peer->state = NGX_HTTP_CHECK_ALL_DONE;\n\n    if (peer->check_data != NULL && peer->reinit) {\n        peer->reinit(peer);\n    }\n\n    peer->shm->owner = NGX_INVALID_PID;\n}\n\n\nstatic void\nngx_http_upstream_check_timeout_handler(ngx_event_t *event)\n{\n    ngx_http_upstream_check_peer_t  *peer;\n\n    if (ngx_http_upstream_check_need_exit()) {\n        return;\n    }\n\n    peer = event->data;\n    peer->pc.connection->error = 1;\n\n    ngx_log_error(NGX_LOG_ERR, event->log, 0,\n                  \"check time out with peer: %V \",\n                  &peer->check_peer_addr->name);\n\n    ngx_http_upstream_check_status_update(peer, 0);\n    ngx_http_upstream_check_clean_event(peer);\n}\n\n\nstatic void\nngx_http_upstream_check_finish_handler(ngx_event_t *event)\n{\n    if (ngx_http_upstream_check_need_exit()) {\n        return;\n    }\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_check_need_exit()\n{\n    if (ngx_terminate || ngx_exiting || ngx_quit) {\n        ngx_http_upstream_check_clear_all_events();\n        return 1;\n    }\n\n    return 0;\n}\n\n\nstatic void\nngx_http_upstream_check_clear_all_events()\n{\n    ngx_uint_t                       i;\n    ngx_http_upstream_check_peer_t  *peer;\n    ngx_http_upstream_check_peers_t *peers;\n\n    static ngx_flag_t                has_cleared = 0;\n\n    if (has_cleared || check_peers_ctx == NULL) {\n        return;\n    }\n\n    ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0,\n                  \"clear all the events on %P \", ngx_pid);\n\n    has_cleared = 1;\n\n    peers = check_peers_ctx;\n\n    peer = peers->peers.elts;\n    for (i = 0; i < peers->peers.nelts; i++) {\n        if (peer[i].delete) {\n            continue;\n        }\n\n        ngx_http_upstream_check_clear_peer(&peer[i]);\n    }\n}\n\n\nstatic void\nngx_http_upstream_check_clear_peer(ngx_http_upstream_check_peer_t  *peer)\n{\n    if (peer != peer->check_ev.data) {\n        ngx_log_error(NGX_LOG_CRIT, ngx_cycle->log, 0,\n                      \"different peer: %p, data: %p, timer: %p\",\n                      peer, peer->check_ev.data, &peer->check_ev);\n    }\n\n    if (peer->pc.connection) {\n        ngx_close_connection(peer->pc.connection);\n        peer->pc.connection = NULL;\n    }\n\n    if (peer->check_ev.timer_set) {\n        ngx_del_timer(&peer->check_ev);\n    }\n\n    if (peer->check_timeout_ev.timer_set) {\n        ngx_del_timer(&peer->check_timeout_ev);\n    }\n\n    if (peer->pool != NULL) {\n        ngx_destroy_pool(peer->pool);\n        peer->pool = NULL;\n    }\n\n    ngx_memzero(peer, sizeof(ngx_http_upstream_check_peer_t));\n\n    peer->delete = 1;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_check_status_handler(ngx_http_request_t *r)\n{\n    size_t                                 buffer_size;\n    ngx_int_t                              rc;\n    ngx_buf_t                             *b;\n    ngx_chain_t                            out;\n    ngx_http_upstream_check_peers_t       *peers;\n    ngx_http_upstream_check_loc_conf_t    *uclcf;\n    ngx_http_upstream_check_status_ctx_t  *ctx;\n\n    if (r->method != NGX_HTTP_GET && r->method != NGX_HTTP_HEAD) {\n        return NGX_HTTP_NOT_ALLOWED;\n    }\n\n    rc = ngx_http_discard_request_body(r);\n\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    uclcf = ngx_http_get_module_loc_conf(r, ngx_http_upstream_check_module);\n\n    ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_check_status_ctx_t));\n    if (ctx == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    ngx_http_upstream_check_status_parse_args(r, ctx);\n\n    if (ctx->format == NULL) {\n        ctx->format = uclcf->format;\n    }\n\n    r->headers_out.content_type_len = ctx->format->content_type.len;\n    r->headers_out.content_type = ctx->format->content_type;\n    r->headers_out.content_type_lowcase = NULL;\n\n    if (r->method == NGX_HTTP_HEAD) {\n        r->headers_out.status = NGX_HTTP_OK;\n\n        rc = ngx_http_send_header(r);\n\n        if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {\n            return rc;\n        }\n    }\n\n    peers = check_peers_ctx;\n    if (peers == NULL) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"http upstream check module can not find any check \"\n                      \"server, make sure you've added the check servers\");\n\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    /* 1/4 pagesize for each record */\n    buffer_size = peers->peers.nelts * ngx_pagesize / 4;\n    buffer_size = ngx_align(buffer_size, ngx_pagesize) + ngx_pagesize;\n\n    b = ngx_create_temp_buf(r->pool, buffer_size);\n    if (b == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    out.buf = b;\n    out.next = NULL;\n\n    ctx->format->output(b, peers, ctx->flag);\n\n    r->headers_out.status = NGX_HTTP_OK;\n    r->headers_out.content_length_n = b->last - b->pos;\n\n    if (r->headers_out.content_length_n == 0) {\n        r->header_only = 1;\n    }\n\n    b->last_buf = 1;\n\n    rc = ngx_http_send_header(r);\n\n    if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {\n        return rc;\n    }\n\n    return ngx_http_output_filter(r, &out);\n}\n\n\nstatic void\nngx_http_upstream_check_status_parse_args(ngx_http_request_t *r,\n    ngx_http_upstream_check_status_ctx_t *ctx)\n{\n    ngx_str_t                    value;\n    ngx_uint_t                   i;\n    ngx_check_status_command_t  *command;\n\n    if (r->args.len == 0) {\n        return;\n    }\n\n    for (i = 0; /* void */ ; i++) {\n\n        command = &ngx_check_status_commands[i];\n\n        if (command->name.len == 0) {\n            break;\n        }\n\n        if (ngx_http_arg(r, command->name.data, command->name.len, &value)\n            == NGX_OK) {\n\n           if (command->handler(ctx, &value) != NGX_OK) {\n               ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                             \"http upstream check, bad argument: \\\"%V\\\"\",\n                             &value);\n           }\n        }\n    }\n\n    ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0,\n            \"http upstream check, flag: \\\"%ui\\\"\", ctx->flag);\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_check_status_command_format(\n    ngx_http_upstream_check_status_ctx_t *ctx, ngx_str_t *value)\n{\n    ctx->format = ngx_http_get_check_status_format_conf(value);\n    if (ctx->format == NULL) {\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_check_status_command_status(\n    ngx_http_upstream_check_status_ctx_t *ctx, ngx_str_t *value)\n{\n    if (value->len == (sizeof(\"down\") - 1)\n        && ngx_strncasecmp(value->data, (u_char *) \"down\", value->len) == 0) {\n\n        ctx->flag |= NGX_CHECK_STATUS_DOWN;\n\n    } else if (value->len == (sizeof(\"up\") - 1)\n               && ngx_strncasecmp(value->data, (u_char *) \"up\", value->len)\n                  == 0) {\n\n        ctx->flag |= NGX_CHECK_STATUS_UP;\n\n    } else {\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_upstream_check_status_html_format(ngx_buf_t *b,\n    ngx_http_upstream_check_peers_t *peers, ngx_uint_t flag)\n{\n    ngx_uint_t                      i, count;\n    ngx_http_upstream_check_peer_t *peer;\n\n    peer = peers->peers.elts;\n\n    count = 0;\n\n    /* TODO: two locks */\n    for (i = 0; i < peers->peers.nelts; i++) {\n\n        if (peer[i].delete) {\n            continue;\n        }\n\n        if (flag & NGX_CHECK_STATUS_DOWN) {\n\n            if (!peer[i].shm->down) {\n                continue;\n            }\n\n        } else if (flag & NGX_CHECK_STATUS_UP) {\n\n            if (peer[i].shm->down) {\n                continue;\n            }\n        }\n\n        count++;\n    }\n\n    b->last = ngx_snprintf(b->last, b->end - b->last,\n            \"<!DOCTYPE html PUBLIC \\\"-//W3C//DTD XHTML 1.0 Strict//EN\\n\"\n            \"\\\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\\\">\\n\"\n            \"<html xmlns=\\\"http://www.w3.org/1999/xhtml\\\">\\n\"\n            \"<head>\\n\"\n            \"  <title>Nginx http upstream check status</title>\\n\"\n            \"</head>\\n\"\n            \"<body>\\n\"\n            \"<h1>Nginx http upstream check status</h1>\\n\"\n            \"<h2>Check upstream server number: %ui, generation: %ui</h2>\\n\"\n            \"<table style=\\\"background-color:white\\\" cellspacing=\\\"0\\\" \"\n            \"       cellpadding=\\\"3\\\" border=\\\"1\\\">\\n\"\n            \"  <tr bgcolor=\\\"#C0C0C0\\\">\\n\"\n            \"    <th>Index</th>\\n\"\n            \"    <th>Upstream</th>\\n\"\n            \"    <th>Name</th>\\n\"\n            \"    <th>Status</th>\\n\"\n            \"    <th>Rise counts</th>\\n\"\n            \"    <th>Fall counts</th>\\n\"\n            \"    <th>Check type</th>\\n\"\n            \"    <th>Check port</th>\\n\"\n            \"  </tr>\\n\",\n            count, ngx_http_upstream_check_shm_generation);\n\n    for (i = 0; i < peers->peers.nelts; i++) {\n\n        if (peer[i].delete) {\n            continue;\n        }\n\n        if (flag & NGX_CHECK_STATUS_DOWN) {\n\n            if (!peer[i].shm->down) {\n                continue;\n            }\n\n        } else if (flag & NGX_CHECK_STATUS_UP) {\n\n            if (peer[i].shm->down) {\n                continue;\n            }\n        }\n\n        b->last = ngx_snprintf(b->last, b->end - b->last,\n                \"  <tr%s>\\n\"\n                \"    <td>%ui</td>\\n\"\n                \"    <td>%V</td>\\n\"\n                \"    <td>%V</td>\\n\"\n                \"    <td>%s</td>\\n\"\n                \"    <td>%ui</td>\\n\"\n                \"    <td>%ui</td>\\n\"\n                \"    <td>%V</td>\\n\"\n                \"    <td>%ui</td>\\n\"\n                \"  </tr>\\n\",\n                peer[i].shm->down ? \" bgcolor=\\\"#FF0000\\\"\" : \"\",\n                i,\n                peer[i].upstream_name,\n                &peer[i].peer_addr->name,\n                peer[i].shm->down ? \"down\" : \"up\",\n                peer[i].shm->rise_count,\n                peer[i].shm->fall_count,\n                &peer[i].conf->check_type_conf->name,\n                peer[i].conf->port);\n    }\n\n    b->last = ngx_snprintf(b->last, b->end - b->last,\n            \"</table>\\n\"\n            \"</body>\\n\"\n            \"</html>\\n\");\n}\n\n\nstatic void\nngx_http_upstream_check_status_csv_format(ngx_buf_t *b,\n    ngx_http_upstream_check_peers_t *peers, ngx_uint_t flag)\n{\n    ngx_uint_t                       i;\n    ngx_http_upstream_check_peer_t  *peer;\n\n    peer = peers->peers.elts;\n    for (i = 0; i < peers->peers.nelts; i++) {\n\n        if (peer[i].delete) {\n            continue;\n        }\n\n        if (flag & NGX_CHECK_STATUS_DOWN) {\n\n            if (!peer[i].shm->down) {\n                continue;\n            }\n\n        } else if (flag & NGX_CHECK_STATUS_UP) {\n\n            if (peer[i].shm->down) {\n                continue;\n            }\n        }\n\n        b->last = ngx_snprintf(b->last, b->end - b->last,\n                \"%ui,%V,%V,%s,%ui,%ui,%V,%ui\\n\",\n                i,\n                peer[i].upstream_name,\n                &peer[i].peer_addr->name,\n                peer[i].shm->down ? \"down\" : \"up\",\n                peer[i].shm->rise_count,\n                peer[i].shm->fall_count,\n                &peer[i].conf->check_type_conf->name,\n                peer[i].conf->port);\n    }\n}\n\n\nstatic void\nngx_http_upstream_check_status_json_format(ngx_buf_t *b,\n    ngx_http_upstream_check_peers_t *peers, ngx_uint_t flag)\n{\n    ngx_uint_t                       count, upCount, downCount, i, last;\n    ngx_http_upstream_check_peer_t  *peer;\n\n    peer = peers->peers.elts;\n\n    count = 0;\n    upCount = 0;\n    downCount = 0;\n\n    for (i = 0; i < peers->peers.nelts; i++) {\n\n        if (peer[i].delete) {\n            continue;\n        }\n\n        if (flag & NGX_CHECK_STATUS_DOWN) {\n\n            if (!peer[i].shm->down) {\n                continue;\n            }\n\n        } else if (flag & NGX_CHECK_STATUS_UP) {\n\n            if (peer[i].shm->down) {\n                continue;\n            }\n        }\n\n        count++;\n        if (peer[i].shm->down) {\n            downCount++;\n        } else {\n            upCount++;\n        }\n    }\n\n    b->last = ngx_snprintf(b->last, b->end - b->last,\n            \"{\\\"servers\\\": {\\n\"\n            \"  \\\"total\\\": %ui,\\n\"\n            \"  \\\"up\\\": %ui,\\n\"\n            \"  \\\"down\\\": %ui,\\n\"\n            \"  \\\"generation\\\": %ui,\\n\"\n            \"  \\\"server\\\": [\\n\",\n            count,\n            upCount,\n            downCount,\n            ngx_http_upstream_check_shm_generation);\n\n    last = 0;\n    for (i = 0; i < peers->peers.nelts; i++) {\n\n        if (peer[i].delete) {\n            continue;\n        }\n\n        if (flag & NGX_CHECK_STATUS_DOWN) {\n\n            if (!peer[i].shm->down) {\n                continue;\n            }\n\n        } else if (flag & NGX_CHECK_STATUS_UP) {\n\n            if (peer[i].shm->down) {\n                continue;\n            }\n        }\n\n        last++;\n\n        b->last = ngx_snprintf(b->last, b->end - b->last,\n                \"    {\\\"index\\\": %ui, \"\n                \"\\\"upstream\\\": \\\"%V\\\", \"\n                \"\\\"name\\\": \\\"%V\\\", \"\n                \"\\\"status\\\": \\\"%s\\\", \"\n                \"\\\"rise\\\": %ui, \"\n                \"\\\"fall\\\": %ui, \"\n                \"\\\"type\\\": \\\"%V\\\", \"\n                \"\\\"port\\\": %ui}\"\n                \"%s\\n\",\n                i,\n                peer[i].upstream_name,\n                &peer[i].peer_addr->name,\n                peer[i].shm->down ? \"down\" : \"up\",\n                peer[i].shm->rise_count,\n                peer[i].shm->fall_count,\n                &peer[i].conf->check_type_conf->name,\n                peer[i].conf->port,\n                (last == count) ? \"\" : \",\");\n    }\n\n    b->last = ngx_snprintf(b->last, b->end - b->last,\n            \"  ]\\n\");\n\n    b->last = ngx_snprintf(b->last, b->end - b->last,\n            \"}}\\n\");\n}\n\n\nstatic void\nngx_http_upstream_check_status_prometheus_format(ngx_buf_t *b,\n    ngx_http_upstream_check_peers_t *peers, ngx_uint_t flag)\n{\n    ngx_uint_t                       count, upCount, downCount, i;\n    ngx_http_upstream_check_peer_t  *peer;\n\n    peer = peers->peers.elts;\n\n    count = 0;\n    upCount = 0;\n    downCount = 0;\n\n    for (i = 0; i < peers->peers.nelts; i++) {\n\n        if (peer[i].delete) {\n            continue;\n        }\n\n        if (flag & NGX_CHECK_STATUS_DOWN) {\n\n            if (!peer[i].shm->down) {\n                continue;\n            }\n\n        } else if (flag & NGX_CHECK_STATUS_UP) {\n\n            if (peer[i].shm->down) {\n                continue;\n            }\n        }\n\n        count++;\n        if (peer[i].shm->down) {\n            downCount++;\n        } else {\n            upCount++;\n        }\n    }\n\n    b->last = ngx_snprintf(b->last, b->end - b->last,\n            \"# HELP nginx_upstream_count_total Nginx total number of servers\\n\"\n            \"# TYPE nginx_upstream_count_total gauge\\n\"\n            \"nginx_upstream_count_total %ui\\n\"\n            \"# HELP nginx_upstream_count_up Nginx total number of servers that are UP\\n\"\n            \"# TYPE nginx_upstream_count_up gauge\\n\"\n            \"nginx_upstream_count_up %ui\\n\"\n            \"# HELP nginx_upstream_count_down Nginx total number of servers that are DOWN\\n\"\n            \"# TYPE nginx_upstream_count_down gauge\\n\"\n            \"nginx_upstream_count_down %ui\\n\"\n            \"# HELP nginx_upstream_count_generation Nginx generation\\n\"\n            \"# TYPE nginx_upstream_count_generation gauge\\n\"\n            \"nginx_upstream_count_generation %ui\\n\",\n            count,\n            upCount,\n            downCount,\n            ngx_http_upstream_check_shm_generation);\n\n    b->last = ngx_snprintf(b->last, b->end - b->last,\n            \"# HELP nginx_upstream_server_rise Nginx rise counter\\n\"\n            \"# TYPE nginx_upstream_server_rise counter\\n\");\n\n    for (i = 0; i < peers->peers.nelts; i++) {\n\n        if (peer[i].delete) {\n            continue;\n        }\n\n        if (flag & NGX_CHECK_STATUS_DOWN) {\n\n            if (!peer[i].shm->down) {\n                continue;\n            }\n\n        } else if (flag & NGX_CHECK_STATUS_UP) {\n\n            if (peer[i].shm->down) {\n                continue;\n            }\n        }\n\n        b->last = ngx_snprintf(b->last, b->end - b->last,\n                \"nginx_upstream_server_rise{index=\\\"%ui\\\",upstream=\\\"%V\\\",name=\\\"%V\\\",status=\\\"%s\\\",type=\\\"%V\\\",port=\\\"%ui\\\"} %ui\\n\",\n                i,\n                peer[i].upstream_name,\n                &peer[i].peer_addr->name,\n                peer[i].shm->down ? \"down\" : \"up\",\n                &peer[i].conf->check_type_conf->name,\n                peer[i].conf->port,\n                peer[i].shm->rise_count);\n    }\n\n    b->last = ngx_snprintf(b->last, b->end - b->last,\n            \"# HELP nginx_upstream_server_fall Nginx fall counter\\n\"\n            \"# TYPE nginx_upstream_server_fall counter\\n\");\n\n    for (i = 0; i < peers->peers.nelts; i++) {\n\n        if (peer[i].delete) {\n            continue;\n        }\n\n        if (flag & NGX_CHECK_STATUS_DOWN) {\n\n            if (!peer[i].shm->down) {\n                continue;\n            }\n\n        } else if (flag & NGX_CHECK_STATUS_UP) {\n\n            if (peer[i].shm->down) {\n                continue;\n            }\n        }\n\n        b->last = ngx_snprintf(b->last, b->end - b->last,\n                \"nginx_upstream_server_fall{index=\\\"%ui\\\",upstream=\\\"%V\\\",name=\\\"%V\\\",status=\\\"%s\\\",type=\\\"%V\\\",port=\\\"%ui\\\"} %ui\\n\",\n                i,\n                peer[i].upstream_name,\n                &peer[i].peer_addr->name,\n                peer[i].shm->down ? \"down\" : \"up\",\n                &peer[i].conf->check_type_conf->name,\n                peer[i].conf->port,\n                peer[i].shm->fall_count);\n    }\n\n    b->last = ngx_snprintf(b->last, b->end - b->last,\n            \"# HELP nginx_upstream_server_active Nginx active 1 for UP / 0 for DOWN\\n\"\n            \"# TYPE nginx_upstream_server_active gauge\\n\");\n\n    for (i = 0; i < peers->peers.nelts; i++) {\n\n        if (peer[i].delete) {\n            continue;\n        }\n\n        if (flag & NGX_CHECK_STATUS_DOWN) {\n\n            if (!peer[i].shm->down) {\n                continue;\n            }\n\n        } else if (flag & NGX_CHECK_STATUS_UP) {\n\n            if (peer[i].shm->down) {\n                continue;\n            }\n        }\n\n        b->last = ngx_snprintf(b->last, b->end - b->last,\n                \"nginx_upstream_server_active{index=\\\"%ui\\\",upstream=\\\"%V\\\",name=\\\"%V\\\",type=\\\"%V\\\",port=\\\"%ui\\\"} %ui\\n\",\n                i,\n                peer[i].upstream_name,\n                &peer[i].peer_addr->name,\n                &peer[i].conf->check_type_conf->name,\n                peer[i].conf->port,\n                peer[i].shm->down ? 0 : 1);\n    }\n}\n\n\nstatic ngx_check_conf_t *\nngx_http_get_check_type_conf(ngx_str_t *str)\n{\n    ngx_uint_t  i;\n\n    for (i = 0; /* void */ ; i++) {\n\n        if (ngx_check_types[i].type == 0) {\n            break;\n        }\n\n        if (str->len != ngx_check_types[i].name.len) {\n            continue;\n        }\n\n        if (ngx_strncmp(str->data, ngx_check_types[i].name.data,\n                        str->len) == 0)\n        {\n            return &ngx_check_types[i];\n        }\n    }\n\n    return NULL;\n}\n\n\nstatic char *\nngx_http_upstream_check(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_str_t                           *value, s;\n    ngx_uint_t                           i, port, rise, fall, default_down, unique;\n    ngx_msec_t                           interval, timeout;\n    ngx_check_conf_t                    *check;\n    ngx_http_upstream_check_srv_conf_t  *ucscf;\n\n    /* default values */\n    port = 0;\n    rise = 2;\n    fall = 5;\n    interval = 30000;\n    timeout = 1000;\n    default_down = 1;\n    unique = 0;\n\n    value = cf->args->elts;\n\n    ucscf = ngx_http_conf_get_module_srv_conf(cf,\n                                              ngx_http_upstream_check_module);\n    if (ucscf == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    for (i = 1; i < cf->args->nelts; i++) {\n\n        if (ngx_strncmp(value[i].data, \"type=\", 5) == 0) {\n            s.len = value[i].len - 5;\n            s.data = value[i].data + 5;\n\n            ucscf->check_type_conf = ngx_http_get_check_type_conf(&s);\n\n            if (ucscf->check_type_conf == NULL) {\n                goto invalid_check_parameter;\n            }\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"port=\", 5) == 0) {\n            s.len = value[i].len - 5;\n            s.data = value[i].data + 5;\n\n            port = ngx_atoi(s.data, s.len);\n            if (port == (ngx_uint_t) NGX_ERROR || port == 0) {\n                goto invalid_check_parameter;\n            }\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"interval=\", 9) == 0) {\n            s.len = value[i].len - 9;\n            s.data = value[i].data + 9;\n\n            interval = ngx_atoi(s.data, s.len);\n            if (interval == (ngx_msec_t) NGX_ERROR || interval == 0) {\n                goto invalid_check_parameter;\n            }\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"timeout=\", 8) == 0) {\n            s.len = value[i].len - 8;\n            s.data = value[i].data + 8;\n\n            timeout = ngx_atoi(s.data, s.len);\n            if (timeout == (ngx_msec_t) NGX_ERROR || timeout == 0) {\n                goto invalid_check_parameter;\n            }\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"rise=\", 5) == 0) {\n            s.len = value[i].len - 5;\n            s.data = value[i].data + 5;\n\n            rise = ngx_atoi(s.data, s.len);\n            if (rise == (ngx_uint_t) NGX_ERROR || rise == 0) {\n                goto invalid_check_parameter;\n            }\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"fall=\", 5) == 0) {\n            s.len = value[i].len - 5;\n            s.data = value[i].data + 5;\n\n            fall = ngx_atoi(s.data, s.len);\n            if (fall == (ngx_uint_t) NGX_ERROR || fall == 0) {\n                goto invalid_check_parameter;\n            }\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"default_down=\", 13) == 0) {\n            s.len = value[i].len - 13;\n            s.data = value[i].data + 13;\n\n            if (ngx_strcasecmp(s.data, (u_char *) \"true\") == 0) {\n                default_down = 1;\n            } else if (ngx_strcasecmp(s.data, (u_char *) \"false\") == 0) {\n                default_down = 0;\n            } else {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"invalid value \\\"%s\\\", \"\n                                   \"it must be \\\"true\\\" or \\\"false\\\"\",\n                                   value[i].data);\n                return NGX_CONF_ERROR;\n            }\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"unique=\", 7) == 0) {\n            s.len = value[i].len - 7;\n            s.data = value[i].data + 7;\n\n            if (ngx_strcasecmp(s.data, (u_char *) \"true\") == 0) {\n                unique = 1;\n            } else if (ngx_strcasecmp(s.data, (u_char *) \"false\") == 0) {\n                unique = 0;\n            } else {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"invalid value \\\"%s\\\", \"\n                                   \"it must be \\\"true\\\" or \\\"false\\\"\",\n                                   value[i].data);\n                return NGX_CONF_ERROR;\n            }\n\n            continue;\n        }\n\n        goto invalid_check_parameter;\n    }\n\n    ucscf->port = port;\n    ucscf->check_interval = interval;\n    ucscf->check_timeout = timeout;\n    ucscf->fall_count = fall;\n    ucscf->rise_count = rise;\n    ucscf->default_down = default_down;\n    ucscf->unique = unique;\n\n    if (ucscf->check_type_conf == NGX_CONF_UNSET_PTR) {\n        ngx_str_set(&s, \"tcp\");\n        ucscf->check_type_conf = ngx_http_get_check_type_conf(&s);\n    }\n\n    check = ucscf->check_type_conf;\n\n    if (ucscf->send.len == 0) {\n        ucscf->send.data = check->default_send.data;\n        ucscf->send.len = check->default_send.len;\n    }\n\n    if (ucscf->code.status_alive == 0) {\n        ucscf->code.status_alive = check->default_status_alive;\n    }\n\n    return NGX_CONF_OK;\n\ninvalid_check_parameter:\n\n    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                       \"invalid parameter \\\"%V\\\"\", &value[i]);\n\n    return NGX_CONF_ERROR;\n}\n\n\nstatic char *\nngx_http_upstream_check_keepalive_requests(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n    ngx_str_t                           *value;\n    ngx_http_upstream_check_srv_conf_t  *ucscf;\n    ngx_uint_t                           requests;\n\n    value = cf->args->elts;\n\n    ucscf = ngx_http_conf_get_module_srv_conf(cf,\n                                              ngx_http_upstream_check_module);\n\n    requests = ngx_atoi(value[1].data, value[1].len);\n    if (requests == (ngx_uint_t) NGX_ERROR || requests == 0) {\n        return \"invalid value\";\n    }\n\n    ucscf->check_keepalive_requests = requests;\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_upstream_check_http_send(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n    ngx_str_t                           *value;\n    ngx_http_upstream_check_srv_conf_t  *ucscf;\n\n    value = cf->args->elts;\n\n    ucscf = ngx_http_conf_get_module_srv_conf(cf,\n                                              ngx_http_upstream_check_module);\n\n    if (ucscf->check_type_conf == NGX_CONF_UNSET_PTR) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid check_http_send should set [check] first\");\n        return NGX_CONF_ERROR;\n    }\n\n    if (value[1].len\n        && (ucscf->check_type_conf->name.len != 4\n            || ngx_strncmp(ucscf->check_type_conf->name.data,\n                           \"http\", 4) != 0))\n    {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid check_http_send for type \\\"%V\\\"\",\n                           &ucscf->check_type_conf->name);\n        return NGX_CONF_ERROR;\n    }\n\n    ucscf->send = value[1];\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_upstream_check_fastcgi_params(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n    ngx_str_t                           *value, *k, *v;\n    ngx_http_upstream_check_srv_conf_t  *ucscf;\n\n    value = cf->args->elts;\n\n    ucscf = ngx_http_conf_get_module_srv_conf(cf,\n                                              ngx_http_upstream_check_module);\n\n    k = ngx_array_push(ucscf->fastcgi_params);\n    if (k == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    v = ngx_array_push(ucscf->fastcgi_params);\n    if (v == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    *k = value[1];\n    *v = value[2];\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_upstream_check_http_expect_alive(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n    ngx_str_t                           *value;\n    ngx_uint_t                           bit, i, m;\n    ngx_conf_bitmask_t                  *mask;\n    ngx_http_upstream_check_srv_conf_t  *ucscf;\n\n    value = cf->args->elts;\n    mask = ngx_check_http_expect_alive_masks;\n\n    ucscf = ngx_http_conf_get_module_srv_conf(cf,\n                                              ngx_http_upstream_check_module);\n    bit = 0;\n\n    for (i = 1; i < cf->args->nelts; i++) {\n        for (m = 0; mask[m].name.len != 0; m++) {\n\n            if (mask[m].name.len != value[i].len\n                || ngx_strcasecmp(mask[m].name.data, value[i].data) != 0)\n            {\n                continue;\n            }\n\n            if (bit & mask[m].mask) {\n                ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                                   \"duplicate value \\\"%s\\\"\", value[i].data);\n\n            } else {\n                bit |= mask[m].mask;\n            }\n\n            break;\n        }\n\n        if (mask[m].name.len == 0) {\n            ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                               \"invalid value \\\"%s\\\"\", value[i].data);\n\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    ucscf->code.status_alive = bit;\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_upstream_check_shm_size(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_str_t                            *value;\n    ngx_http_upstream_check_main_conf_t  *ucmcf;\n\n    ucmcf = ngx_http_conf_get_module_main_conf(cf,\n                                               ngx_http_upstream_check_module);\n    if (ucmcf->check_shm_size) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    ucmcf->check_shm_size = ngx_parse_size(&value[1]);\n    if (ucmcf->check_shm_size == (size_t) NGX_ERROR) {\n        return \"invalid value\";\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_check_status_conf_t *\nngx_http_get_check_status_format_conf(ngx_str_t *str)\n{\n    ngx_uint_t  i;\n\n    for (i = 0; /* void */ ; i++) {\n\n        if (ngx_check_status_formats[i].format.len == 0) {\n            break;\n        }\n\n        if (str->len != ngx_check_status_formats[i].format.len) {\n            continue;\n        }\n\n        if (ngx_strncmp(str->data, ngx_check_status_formats[i].format.data,\n                        str->len) == 0)\n        {\n            return &ngx_check_status_formats[i];\n        }\n    }\n\n    return NULL;\n}\n\n\nstatic char *\nngx_http_upstream_check_status(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_str_t                           *value;\n    ngx_http_core_loc_conf_t            *clcf;\n    ngx_http_upstream_check_loc_conf_t  *uclcf;\n\n    value = cf->args->elts;\n\n    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);\n\n    clcf->handler = ngx_http_upstream_check_status_handler;\n\n    if (cf->args->nelts == 2) {\n        uclcf = ngx_http_conf_get_module_loc_conf(cf,\n                                              ngx_http_upstream_check_module);\n\n        uclcf->format = ngx_http_get_check_status_format_conf(&value[1]);\n        if (uclcf->format == NULL) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"invalid check format \\\"%V\\\"\", &value[1]);\n\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic void *\nngx_http_upstream_check_create_main_conf(ngx_conf_t *cf)\n{\n    ngx_http_upstream_check_main_conf_t  *ucmcf;\n\n    ucmcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_check_main_conf_t));\n    if (ucmcf == NULL) {\n        return NULL;\n    }\n\n    ucmcf->peers = ngx_pcalloc(cf->pool,\n                               sizeof(ngx_http_upstream_check_peers_t));\n    if (ucmcf->peers == NULL) {\n        return NULL;\n    }\n\n    ucmcf->peers->checksum = 0;\n\n#if (NGX_DEBUG)\n\n    if (ngx_array_init(&ucmcf->peers->peers, cf->pool, 1,\n                       sizeof(ngx_http_upstream_check_peer_t)) != NGX_OK)\n    {\n        return NULL;\n    }\n\n#else\n    if (ngx_array_init(&ucmcf->peers->peers, cf->pool, 1024,\n                       sizeof(ngx_http_upstream_check_peer_t)) != NGX_OK)\n    {\n        return NULL;\n    }\n#endif\n\n    return ucmcf;\n}\n\n\nstatic ngx_buf_t *\nngx_http_upstream_check_create_fastcgi_request(ngx_pool_t *pool,\n    ngx_str_t *params, ngx_uint_t num)\n{\n    size_t                      size, len, padding;\n    ngx_buf_t                  *b;\n    ngx_str_t                  *k, *v;\n    ngx_uint_t                  i, j;\n    ngx_http_fastcgi_header_t  *h;\n\n    len = 0;\n    for (i = 0, j = 0; i < num; i++, j = i * 2) {\n        k = &params[j];\n        v = &params[j + 1];\n\n        len += 1 + k->len + ((v->len > 127) ? 4 : 1) + v->len;\n    }\n\n    padding = 8 - len % 8;\n    padding = (padding == 8) ? 0 : padding;\n\n    size = sizeof(ngx_http_fastcgi_header_t)\n        + sizeof(ngx_http_fastcgi_begin_request_t)\n\n        + sizeof(ngx_http_fastcgi_header_t)  /* NGX_HTTP_FASTCGI_PARAMS */\n        + len + padding\n        + sizeof(ngx_http_fastcgi_header_t)  /* NGX_HTTP_FASTCGI_PARAMS */\n\n        + sizeof(ngx_http_fastcgi_header_t); /* NGX_HTTP_FASTCGI_STDIN */\n\n\n    b = ngx_create_temp_buf(pool, size);\n    if (b == NULL) {\n        return NULL;\n    }\n\n    ngx_http_fastcgi_request_start.br.flags = 0;\n\n    ngx_memcpy(b->pos, &ngx_http_fastcgi_request_start,\n               sizeof(ngx_http_fastcgi_request_start_t));\n\n    h = (ngx_http_fastcgi_header_t *)\n        (b->pos + sizeof(ngx_http_fastcgi_header_t)\n         + sizeof(ngx_http_fastcgi_begin_request_t));\n\n    h->content_length_hi = (u_char) ((len >> 8) & 0xff);\n    h->content_length_lo = (u_char) (len & 0xff);\n    h->padding_length = (u_char) padding;\n    h->reserved = 0;\n\n    b->last = b->pos + sizeof(ngx_http_fastcgi_header_t)\n        + sizeof(ngx_http_fastcgi_begin_request_t)\n        + sizeof(ngx_http_fastcgi_header_t);\n\n    for (i = 0, j = 0; i < num; i++, j = i * 2) {\n        k = &params[j];\n        v = &params[j + 1];\n\n        if (k->len > 127) {\n            *b->last++ = (u_char) (((k->len >> 24) & 0x7f) | 0x80);\n            *b->last++ = (u_char) ((k->len >> 16) & 0xff);\n            *b->last++ = (u_char) ((k->len >> 8) & 0xff);\n            *b->last++ = (u_char) (k->len & 0xff);\n\n        } else {\n            *b->last++ = (u_char) k->len;\n        }\n\n        if (v->len > 127) {\n            *b->last++ = (u_char) (((v->len >> 24) & 0x7f) | 0x80);\n            *b->last++ = (u_char) ((v->len >> 16) & 0xff);\n            *b->last++ = (u_char) ((v->len >> 8) & 0xff);\n            *b->last++ = (u_char) (v->len & 0xff);\n\n        } else {\n            *b->last++ = (u_char) v->len;\n        }\n\n        b->last = ngx_copy(b->last, k->data, k->len);\n        b->last = ngx_copy(b->last, v->data, v->len);\n    }\n\n    if (padding) {\n        ngx_memzero(b->last, padding);\n        b->last += padding;\n    }\n\n    h = (ngx_http_fastcgi_header_t *) b->last;\n    b->last += sizeof(ngx_http_fastcgi_header_t);\n\n    h->version = 1;\n    h->type = NGX_HTTP_FASTCGI_PARAMS;\n    h->request_id_hi = 0;\n    h->request_id_lo = 1;\n    h->content_length_hi = 0;\n    h->content_length_lo = 0;\n    h->padding_length = 0;\n    h->reserved = 0;\n\n    h = (ngx_http_fastcgi_header_t *) b->last;\n    b->last += sizeof(ngx_http_fastcgi_header_t);\n\n    return b;\n}\n\n\nstatic char *\nngx_http_upstream_check_init_main_conf(ngx_conf_t *cf, void *conf)\n{\n    ngx_buf_t                      *b;\n    ngx_uint_t                      i;\n    ngx_http_upstream_srv_conf_t  **uscfp;\n    ngx_http_upstream_main_conf_t  *umcf;\n\n    umcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_upstream_module);\n\n    b = ngx_http_upstream_check_create_fastcgi_request(cf->pool,\n            fastcgi_default_params,\n            sizeof(fastcgi_default_params) / sizeof(ngx_str_t) / 2);\n\n    if (b == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    fastcgi_default_request.data = b->pos;\n    fastcgi_default_request.len = b->last - b->pos;\n\n    uscfp = umcf->upstreams.elts;\n\n    for (i = 0; i < umcf->upstreams.nelts; i++) {\n\n        if (ngx_http_upstream_check_init_srv_conf(cf, uscfp[i]) != NGX_OK) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    return ngx_http_upstream_check_init_shm(cf, conf);\n}\n\n\nstatic void *\nngx_http_upstream_check_create_srv_conf(ngx_conf_t *cf)\n{\n    ngx_http_upstream_check_srv_conf_t  *ucscf;\n\n    ucscf = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_check_srv_conf_t));\n    if (ucscf == NULL) {\n        return NULL;\n    }\n\n    ucscf->fastcgi_params = ngx_array_create(cf->pool, 2 * 4, sizeof(ngx_str_t));\n    if (ucscf->fastcgi_params == NULL) {\n        return NULL;\n    }\n\n    ucscf->port = NGX_CONF_UNSET_UINT;\n    ucscf->fall_count = NGX_CONF_UNSET_UINT;\n    ucscf->rise_count = NGX_CONF_UNSET_UINT;\n    ucscf->check_timeout = NGX_CONF_UNSET_MSEC;\n    ucscf->check_keepalive_requests = 1;\n    ucscf->check_type_conf = NGX_CONF_UNSET_PTR;\n\n    return ucscf;\n}\n\n\nstatic void *\nngx_http_upstream_check_create_loc_conf(ngx_conf_t *cf)\n{\n    ngx_http_upstream_check_loc_conf_t  *uclcf;\n\n    uclcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_check_loc_conf_t));\n    if (uclcf == NULL) {\n        return NULL;\n    }\n\n    uclcf->format = NGX_CONF_UNSET_PTR;\n\n    return uclcf;\n}\n\n\nstatic char *\nngx_http_upstream_check_init_srv_conf(ngx_conf_t *cf, void *conf)\n{\n    ngx_str_t                           s;\n    ngx_buf_t                          *b;\n    ngx_check_conf_t                   *check;\n    ngx_http_upstream_srv_conf_t       *us = conf;\n    ngx_http_upstream_check_srv_conf_t *ucscf;\n\n    if (us->srv_conf == NULL) {\n        return NGX_CONF_OK;\n    }\n\n    ucscf = ngx_http_conf_upstream_srv_conf(us, ngx_http_upstream_check_module);\n\n    if (ucscf->port == NGX_CONF_UNSET_UINT) {\n        ucscf->port = 0;\n    }\n\n    if (ucscf->fall_count == NGX_CONF_UNSET_UINT) {\n        ucscf->fall_count = 2;\n    }\n\n    if (ucscf->rise_count == NGX_CONF_UNSET_UINT) {\n        ucscf->rise_count = 5;\n    }\n\n    if (ucscf->check_interval == NGX_CONF_UNSET_MSEC) {\n        ucscf->check_interval = 0;\n    }\n\n    if (ucscf->check_timeout == NGX_CONF_UNSET_MSEC) {\n        ucscf->check_timeout = 1000;\n    }\n\n    if (ucscf->check_keepalive_requests == NGX_CONF_UNSET_UINT) {\n        ucscf->check_keepalive_requests = 1;\n    }\n\n    if (ucscf->check_type_conf == NGX_CONF_UNSET_PTR) {\n        ucscf->check_type_conf = NULL;\n    }\n\n    check = ucscf->check_type_conf;\n\n    if (check) {\n        if (ucscf->send.len == 0) {\n            ngx_str_set(&s, \"fastcgi\");\n\n            if (check == ngx_http_get_check_type_conf(&s)) {\n\n                if (ucscf->fastcgi_params->nelts == 0) {\n                    ucscf->send.data = fastcgi_default_request.data;\n                    ucscf->send.len = fastcgi_default_request.len;\n\n                } else {\n                    b = ngx_http_upstream_check_create_fastcgi_request(\n                            cf->pool, ucscf->fastcgi_params->elts,\n                            ucscf->fastcgi_params->nelts / 2);\n                    if (b == NULL) {\n                        return NGX_CONF_ERROR;\n                    }\n\n                    ucscf->send.data = b->pos;\n                    ucscf->send.len = b->last - b->pos;\n                }\n            } else {\n                ucscf->send.data = check->default_send.data;\n                ucscf->send.len = check->default_send.len;\n            }\n        }\n\n\n        if (ucscf->code.status_alive == 0) {\n            ucscf->code.status_alive = check->default_status_alive;\n        }\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_upstream_check_merge_loc_conf(ngx_conf_t *cf, void *parent,\n    void *child)\n{\n    ngx_str_t                            format = ngx_string(\"html\");\n    ngx_http_upstream_check_loc_conf_t  *prev = parent;\n    ngx_http_upstream_check_loc_conf_t  *conf = child;\n\n    ngx_conf_merge_ptr_value(conf->format, prev->format,\n                             ngx_http_get_check_status_format_conf(&format));\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_upstream_check_init_shm(ngx_conf_t *cf, void *conf)\n{\n    ngx_str_t                            *shm_name;\n    ngx_uint_t                            shm_size;\n    ngx_shm_zone_t                       *shm_zone;\n    ngx_http_upstream_check_main_conf_t  *ucmcf = conf;\n\n    ngx_http_upstream_check_shm_generation++;\n\n    shm_name = &ucmcf->peers->check_shm_name;\n\n    ngx_http_upstream_check_get_shm_name(shm_name, cf->pool,\n                                ngx_http_upstream_check_shm_generation);\n\n    /* The default check shared memory size is 1M */\n    shm_size = 1 * 1024 * 1024;\n\n    shm_size = shm_size < ucmcf->check_shm_size ?\n                          ucmcf->check_shm_size : shm_size;\n\n    shm_zone = ngx_shared_memory_add(cf, shm_name, shm_size,\n                                     &ngx_http_upstream_check_module);\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, cf->log, 0,\n                   \"http upstream check, upsteam:%V, shm_zone size:%ui\",\n                   shm_name, shm_size);\n\n    shm_zone->data = cf->pool;\n    check_peers_ctx = ucmcf->peers;\n\n    shm_zone->init = ngx_http_upstream_check_init_shm_zone;\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_check_get_shm_name(ngx_str_t *shm_name, ngx_pool_t *pool,\n    ngx_uint_t generation)\n{\n    u_char  *last;\n\n    shm_name->data = ngx_palloc(pool, SHM_NAME_LEN);\n    if (shm_name->data == NULL) {\n        return NGX_ERROR;\n    }\n\n    last = ngx_snprintf(shm_name->data, SHM_NAME_LEN, \"%s#%ui\",\n                        \"ngx_http_upstream_check\", generation);\n\n    shm_name->len = last - shm_name->data;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_check_init_shm_zone(ngx_shm_zone_t *shm_zone, void *data)\n{\n    size_t                               size;\n    ngx_str_t                            oshm_name;\n    ngx_int_t                            rc;\n    ngx_uint_t                           i, same, number;\n    ngx_pool_t                          *pool;\n    ngx_shm_zone_t                      *oshm_zone;\n    ngx_slab_pool_t                     *shpool;\n    ngx_http_upstream_check_peer_t      *peer;\n    ngx_http_upstream_check_peers_t     *peers;\n    ngx_http_upstream_check_srv_conf_t  *ucscf;\n    ngx_http_upstream_check_peer_shm_t  *peer_shm, *opeer_shm;\n    ngx_http_upstream_check_peers_shm_t *peers_shm, *opeers_shm;\n\n    opeers_shm = NULL;\n    peers_shm = NULL;\n    ngx_str_set(&oshm_name, \"\");\n\n    same = 0;\n    peers = check_peers_ctx;\n    if (peers == NULL) {\n        return NGX_OK;\n    }\n\n    number = peers->peers.nelts;\n\n    pool = shm_zone->data;\n    if (pool == NULL) {\n        pool = ngx_cycle->pool;\n    }\n\n    shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;\n\n    if (data) {\n        opeers_shm = data;\n\n        if ((opeers_shm->number == number)\n            && (opeers_shm->checksum == peers->checksum)) {\n\n            peers_shm = data;\n            same = 1;\n        }\n    }\n\n    if (!same) {\n\n        if (ngx_http_upstream_check_shm_generation > 1) {\n\n            ngx_http_upstream_check_get_shm_name(&oshm_name,\n                    pool, ngx_http_upstream_check_shm_generation - 1);\n\n            /* The global variable ngx_cycle still points to the old one */\n            oshm_zone = ngx_shared_memory_find((ngx_cycle_t *) ngx_cycle,\n                                               &oshm_name,\n                                               &ngx_http_upstream_check_module);\n\n            if (oshm_zone) {\n                opeers_shm = oshm_zone->data;\n\n                ngx_log_debug2(NGX_LOG_DEBUG_HTTP, shm_zone->shm.log, 0,\n                               \"http upstream check, find oshm_zone:%p, \"\n                               \"opeers_shm: %p\",\n                               oshm_zone, opeers_shm);\n            }\n        }\n\n        size = sizeof(*peers_shm) +\n               (number - 1 + MAX_DYNAMIC_PEER) * sizeof(ngx_http_upstream_check_peer_shm_t);\n\n        peers_shm = ngx_slab_alloc(shpool, size);\n\n        if (peers_shm == NULL) {\n            goto failure;\n        }\n\n        ngx_memzero(peers_shm, size);\n    }\n\n    peers_shm->generation = ngx_http_upstream_check_shm_generation;\n    peers_shm->checksum = peers->checksum;\n    peers_shm->number = number;\n    peers_shm->max_number = number + MAX_DYNAMIC_PEER;\n\n    peer = peers->peers.elts;\n\n    for (i = 0; i < number; i++) {\n\n        peer_shm = &peers_shm->peers[i];\n\n        if (same) {\n            continue;\n        }\n\n        peer_shm->socklen = peer[i].peer_addr->socklen;\n        peer_shm->sockaddr = ngx_slab_alloc(shpool, peer_shm->socklen);\n        if (peer_shm->sockaddr == NULL) {\n            goto failure;\n        }\n\n        ngx_memcpy(peer_shm->sockaddr, peer[i].peer_addr->sockaddr,\n                   peer_shm->socklen);\n\n        if (opeers_shm) {\n\n            opeer_shm = ngx_http_upstream_check_find_shm_peer(opeers_shm,\n                                                             peer[i].peer_addr);\n            if (opeer_shm) {\n                ngx_log_debug1(NGX_LOG_DEBUG_HTTP, shm_zone->shm.log, 0,\n                               \"http upstream check, inherit opeer: %V \",\n                               &peer[i].peer_addr->name);\n\n                rc = ngx_http_upstream_check_init_shm_peer(peer_shm, opeer_shm,\n                         0, pool, &peer[i].peer_addr->name);\n                if (rc != NGX_OK) {\n                    return NGX_ERROR;\n                }\n\n                continue;\n            }\n        }\n\n        ucscf = peer[i].conf;\n        rc = ngx_http_upstream_check_init_shm_peer(peer_shm, NULL,\n                                                   ucscf->default_down, pool,\n                                                   &peer[i].peer_addr->name);\n        if (rc != NGX_OK) {\n            return NGX_ERROR;\n        }\n    }\n\n    peers->shpool = shpool;\n    peers->peers_shm = peers_shm;\n    shm_zone->data = peers_shm;\n\n    return NGX_OK;\n\nfailure:\n    ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0,\n                  \"http upstream check_shm_size is too small, \"\n                  \"you should specify a larger size.\");\n    return NGX_ERROR;\n}\n\n\nstatic ngx_shm_zone_t *\nngx_shared_memory_find(ngx_cycle_t *cycle, ngx_str_t *name, void *tag)\n{\n    ngx_uint_t        i;\n    ngx_shm_zone_t   *shm_zone;\n    ngx_list_part_t  *part;\n\n    part = (ngx_list_part_t *) &(cycle->shared_memory.part);\n    shm_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            part = part->next;\n            shm_zone = part->elts;\n            i = 0;\n        }\n\n        if (name->len != shm_zone[i].shm.name.len) {\n            continue;\n        }\n\n        if (ngx_strncmp(name->data, shm_zone[i].shm.name.data, name->len)\n                != 0)\n        {\n            continue;\n        }\n\n        if (tag != shm_zone[i].tag) {\n            continue;\n        }\n\n        return &shm_zone[i];\n    }\n\n    return NULL;\n}\n\n\nstatic ngx_http_upstream_check_peer_shm_t *\nngx_http_upstream_check_find_shm_peer(ngx_http_upstream_check_peers_shm_t *p,\n    ngx_addr_t *addr)\n{\n    ngx_uint_t                          i;\n    ngx_http_upstream_check_peer_shm_t *peer_shm;\n\n    for (i = 0; i < p->number; i++) {\n\n        peer_shm = &p->peers[i];\n\n        if (addr->socklen != peer_shm->socklen) {\n            continue;\n        }\n\n        if (ngx_memcmp(addr->sockaddr, peer_shm->sockaddr,\n                       addr->socklen) == 0) {\n            return peer_shm;\n        }\n    }\n\n    return NULL;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_check_init_shm_peer(ngx_http_upstream_check_peer_shm_t *psh,\n    ngx_http_upstream_check_peer_shm_t *opsh, ngx_uint_t init_down,\n    ngx_pool_t *pool, ngx_str_t *name)\n{\n    u_char  *file;\n\n    if (opsh) {\n        psh->access_time  = opsh->access_time;\n        psh->access_count = opsh->access_count;\n\n        psh->fall_count   = opsh->fall_count;\n        psh->rise_count   = opsh->rise_count;\n        psh->busyness     = opsh->busyness;\n\n        psh->down         = opsh->down;\n\n    } else{\n        psh->access_time  = 0;\n        psh->access_count = 0;\n\n        psh->fall_count   = 0;\n        psh->rise_count   = 0;\n        psh->busyness     = 0;\n\n        psh->down         = init_down;\n    }\n\n    psh->owner = NGX_INVALID_PID;\n\n#if (NGX_HAVE_ATOMIC_OPS)\n\n    file = NULL;\n\n#else\n\n    file = ngx_pnalloc(pool, ngx_cycle->lock_file.len + name->len);\n    if (file == NULL) {\n        return NGX_ERROR;\n    }\n\n    (void) ngx_sprintf(file, \"%V%V%Z\", &ngx_cycle->lock_file, name);\n\n#endif\n\n    if (ngx_shmtx_create(&psh->mutex, &psh->lock, file) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_check_init_process(ngx_cycle_t *cycle)\n{\n    ngx_http_upstream_check_main_conf_t *ucmcf;\n\n    ucmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_upstream_check_module);\n    if (ucmcf == NULL) {\n        return NGX_OK;\n    }\n\n    return ngx_http_upstream_check_add_timers(cycle);\n}\n"
  },
  {
    "path": "modules/ngx_http_upstream_check_module/ngx_http_upstream_check_module.h",
    "content": "#define NGX_HAVE_HTTP_UPSTREAM_CHECK\n"
  },
  {
    "path": "modules/ngx_http_upstream_consistent_hash_module/config",
    "content": "ddon_name=ngx_http_upstream_consistent_hash_module\nHTTP_MODULES=\"$HTTP_MODULES ngx_http_upstream_consistent_hash_module\"\nNGX_ADDON_SRCS=\"$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_upstream_consistent_hash_module.c\"\n\nif ! echo \"$CORE_DEPS\" | grep \"ngx_segment\" > /dev/null\nthen\n    NGX_ADDON_SRCS=\"$NGX_ADDON_SRCS $ngx_addon_dir/ngx_segment_tree.c\"\n    NGX_ADDON_DEPS=\"$NGX_ADDON_DEPS $ngx_addon_dir/ngx_segment_tree.h\"\nfi\n"
  },
  {
    "path": "modules/ngx_http_upstream_consistent_hash_module/ngx_http_upstream_consistent_hash_module.c",
    "content": "\n/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#include <ngx_core.h>\n#include <ngx_http.h>\n#include <ngx_config.h>\n#include <ngx_md5.h>\n\n#define NGX_CHASH_GREAT                     1\n#define NGX_CHASH_EQUAL                     0\n#define NGX_CHASH_LESS                      -1\n#define NGX_CHASH_VIRTUAL_NODE_NUMBER       160\n\n#if (NGX_HTTP_UPSTREAM_CHECK)\n#include \"ngx_http_upstream_check_module.h\"\n#endif\n\ntypedef struct {\n    time_t                                  timeout;\n    ngx_int_t                               id;\n    ngx_queue_t                             queue;\n} ngx_http_upstream_chash_down_server_t;\n\ntypedef struct {\n    u_char                                  down;\n    uint32_t                                hash;\n    ngx_uint_t                              index;\n    ngx_uint_t                              rnindex;\n    ngx_http_upstream_rr_peer_t            *peer;\n} ngx_http_upstream_chash_server_t;\n\ntypedef struct {\n    ngx_uint_t                              number;\n    ngx_queue_t                             down_servers;\n    ngx_array_t                            *values;\n    ngx_array_t                            *lengths;\n    ngx_segment_tree_t                     *tree;\n    ngx_http_upstream_chash_server_t     ***real_node;\n    ngx_http_upstream_chash_server_t       *servers;\n    ngx_http_upstream_chash_down_server_t  *d_servers;\n} ngx_http_upstream_chash_srv_conf_t;\n\ntypedef struct {\n    uint32_t                                hash;\n\n#if (NGX_HTTP_SSL)\n    ngx_ssl_session_t                  *ssl_session;\n#endif\n\n    ngx_http_upstream_chash_server_t       *server;\n    ngx_http_upstream_chash_srv_conf_t     *ucscf;\n} ngx_http_upstream_chash_peer_data_t;\n\n\nstatic void *ngx_http_upstream_chash_create_srv_conf(ngx_conf_t *cf);\nstatic ngx_int_t ngx_http_upstream_init_chash(ngx_conf_t *cf,\n    ngx_http_upstream_srv_conf_t *us);\nstatic ngx_int_t ngx_http_upstream_chash_cmp(const void *one, const void *two);\nstatic ngx_int_t ngx_http_upstream_init_chash_peer(ngx_http_request_t *r,\n    ngx_http_upstream_srv_conf_t *us);\nstatic ngx_int_t ngx_http_upstream_get_chash_peer(ngx_peer_connection_t *pc,\n    void *data);\nstatic void ngx_http_upstream_free_chash_peer(ngx_peer_connection_t *pc,\n    void *data, ngx_uint_t state);\nstatic char *ngx_http_upstream_chash(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic uint32_t ngx_http_upstream_chash_get_server_index(\n    ngx_http_upstream_chash_server_t *servers, uint32_t n, uint32_t hash);\nstatic void ngx_http_upstream_chash_delete_node(\n    ngx_http_upstream_chash_srv_conf_t *ucscf,\n    ngx_http_upstream_chash_server_t *server);\n\n#if (NGX_HTTP_SSL)\nstatic ngx_int_t ngx_http_upstream_chash_set_peer_session(\n    ngx_peer_connection_t *pc, void *data);\nstatic void ngx_http_upstream_chash_save_peer_session(ngx_peer_connection_t *pc,\n    void *data);\n#endif\n\n\nstatic ngx_command_t ngx_http_upstream_chash_commands[] = {\n\n    { ngx_string(\"consistent_hash\"),\n      NGX_HTTP_UPS_CONF | NGX_CONF_TAKE1,\n      ngx_http_upstream_chash,\n      0,\n      0,\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t ngx_http_upstream_consistent_hash_module_ctx = {\n    NULL,                   /* preconfiguration */\n    NULL,                   /* postconfiguration */\n\n    NULL,                   /* create main configuration */\n    NULL,                   /* init main configuration */\n\n    ngx_http_upstream_chash_create_srv_conf,\n                            /* create server configuration*/\n    NULL,                   /* merge server configuration */\n\n    NULL,                   /* create location configuration */\n    NULL                    /* merge location configuration */\n};\n\nngx_module_t ngx_http_upstream_consistent_hash_module = {\n    NGX_MODULE_V1,\n    &ngx_http_upstream_consistent_hash_module_ctx,\n                            /* module context */\n    ngx_http_upstream_chash_commands,\n                            /* 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 void *\nngx_http_upstream_chash_create_srv_conf(ngx_conf_t *cf)\n{\n    ngx_http_upstream_chash_srv_conf_t *ucscf;\n\n    ucscf = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_chash_srv_conf_t));\n    if (ucscf == NULL) {\n        return NULL;\n    }\n\n    return ucscf;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_init_chash(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us)\n{\n    u_char                               hash_buf[256];\n    ngx_int_t                            j, weight;\n    ngx_uint_t                           sid, id, hash_len;\n    ngx_uint_t                           i, n, *number, rnindex;\n    ngx_http_upstream_rr_peer_t         *peer;\n    ngx_http_upstream_rr_peers_t        *peers;\n    ngx_http_upstream_chash_server_t    *server;\n    ngx_http_upstream_chash_srv_conf_t  *ucscf;\n\n    if (ngx_http_upstream_init_round_robin(cf, us) == NGX_ERROR) {\n        return NGX_ERROR;\n    }\n\n    ucscf = ngx_http_conf_upstream_srv_conf(us,\n                                     ngx_http_upstream_consistent_hash_module);\n    if (ucscf == NULL) {\n        return NGX_ERROR;\n    }\n\n    us->peer.init = ngx_http_upstream_init_chash_peer;\n\n    peers = (ngx_http_upstream_rr_peers_t *) us->peer.data;\n    if (peers == NULL) {\n        return NGX_ERROR;\n    }\n\n    n = peers->number;\n    ucscf->number = 0;\n    ucscf->real_node = ngx_pcalloc(cf->pool, n *\n                                   sizeof(ngx_http_upstream_chash_server_t**));\n    if (ucscf->real_node == NULL) {\n        return NGX_ERROR;\n    }\n    for (i = 0; i < n; i++) {\n        ucscf->number += peers->peer[i].weight * NGX_CHASH_VIRTUAL_NODE_NUMBER;\n        ucscf->real_node[i] = ngx_pcalloc(cf->pool,\n                                    (peers->peer[i].weight\n                                     * NGX_CHASH_VIRTUAL_NODE_NUMBER + 1) *\n                                     sizeof(ngx_http_upstream_chash_server_t*));\n        if (ucscf->real_node[i] == NULL) {\n            return NGX_ERROR;\n        }\n    }\n\n    ucscf->servers = ngx_pcalloc(cf->pool,\n                                 (ucscf->number + 1) *\n                                  sizeof(ngx_http_upstream_chash_server_t));\n\n    if (ucscf->servers == NULL) {\n        return NGX_ERROR;\n    }\n\n    ucscf->d_servers = ngx_pcalloc(cf->pool,\n                                (ucscf->number + 1) *\n                                sizeof(ngx_http_upstream_chash_down_server_t));\n\n    if (ucscf->d_servers == NULL) {\n        return NGX_ERROR;\n    }\n\n    ucscf->number = 0;\n    for (i = 0; i < n; i++) {\n\n        peer = &peers->peer[i];\n        sid = (ngx_uint_t) ngx_atoi(peer->id.data, peer->id.len);\n\n        if (sid == (ngx_uint_t) NGX_ERROR || sid > 65535) {\n\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, cf->log, 0, \"server id %d\", sid);\n\n            ngx_snprintf(hash_buf, 256, \"%V%Z\", &peer->name);\n            hash_len = ngx_strlen(hash_buf);\n            sid = ngx_murmur_hash2(hash_buf, hash_len);\n        }\n\n        weight = peer->weight * NGX_CHASH_VIRTUAL_NODE_NUMBER;\n\n        if (weight >= 1 << 14) {\n            ngx_log_error(NGX_LOG_WARN, cf->log, 0,\n                          \"weigth[%d] is too large, is must be less than %d\",\n                          weight / NGX_CHASH_VIRTUAL_NODE_NUMBER,\n                          (1 << 14) / NGX_CHASH_VIRTUAL_NODE_NUMBER);\n            weight = 1 << 14;\n        }\n\n        for (j = 0; j < weight; j++) {\n            server = &ucscf->servers[++ucscf->number];\n            server->peer = peer;\n            server->rnindex = i;\n\n            id = sid * 256 * 16 + j;\n            server->hash = ngx_murmur_hash2((u_char *) (&id), 4);\n        }\n    }\n\n    ngx_qsort(ucscf->servers + 1, ucscf->number,\n              sizeof(ngx_http_upstream_chash_server_t),\n              (const void *)ngx_http_upstream_chash_cmp);\n\n    number = ngx_calloc(n * sizeof(ngx_uint_t), cf->log);\n    if (number == NULL) {\n        return NGX_ERROR;\n    }\n\n    for (i = 1; i <= ucscf->number; i++) {\n        ucscf->servers[i].index = i;\n        ucscf->d_servers[i].id = i;\n        rnindex = ucscf->servers[i].rnindex;\n        ucscf->real_node[rnindex][number[rnindex]] = &ucscf->servers[i];\n        number[rnindex]++;\n    }\n\n    ngx_free(number);\n\n    ucscf->tree = ngx_pcalloc(cf->pool, sizeof(ngx_segment_tree_t));\n    if (ucscf->tree == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_segment_tree_init(ucscf->tree, ucscf->number, cf->pool);\n    ucscf->tree->build(ucscf->tree, 1, 1, ucscf->number);\n\n    ngx_queue_init(&ucscf->down_servers);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_chash_cmp(const void *one, const void *two)\n{\n    ngx_http_upstream_chash_server_t *frist, *second;\n\n    frist = (ngx_http_upstream_chash_server_t *)one;\n    second = (ngx_http_upstream_chash_server_t *) two;\n\n    if (frist->hash > second->hash) {\n        return NGX_CHASH_GREAT;\n\n    } else if (frist->hash == second->hash) {\n        return NGX_CHASH_EQUAL;\n\n    } else {\n        return NGX_CHASH_LESS;\n    }\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_init_chash_peer(ngx_http_request_t *r,\n    ngx_http_upstream_srv_conf_t *us)\n{\n    ngx_str_t                            hash_value;\n    ngx_http_upstream_chash_srv_conf_t  *ucscf;\n    ngx_http_upstream_chash_peer_data_t *uchpd;\n\n    ucscf = ngx_http_conf_upstream_srv_conf(us,\n                                     ngx_http_upstream_consistent_hash_module);\n    if (ucscf == NULL) {\n        return NGX_ERROR;\n    }\n\n    uchpd = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_chash_peer_data_t));\n    if (uchpd == NULL) {\n        return NGX_ERROR;\n    }\n\n    uchpd->ucscf = ucscf;\n    if (ngx_http_script_run(r, &hash_value,\n                ucscf->lengths->elts, 0, ucscf->values->elts) == NULL) {\n        return NGX_ERROR;\n    }\n\n    uchpd->hash = ngx_murmur_hash2(hash_value.data, hash_value.len);\n\n    r->upstream->peer.get = ngx_http_upstream_get_chash_peer;\n    r->upstream->peer.free = ngx_http_upstream_free_chash_peer;\n    r->upstream->peer.data = uchpd;\n\n#if (NGX_HTTP_SSL)\n    r->upstream->peer.set_session = ngx_http_upstream_chash_set_peer_session;\n    r->upstream->peer.save_session = ngx_http_upstream_chash_save_peer_session;\n#endif\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_get_chash_peer(ngx_peer_connection_t *pc, void *data)\n{\n\n    time_t                                  now;\n    uint32_t                                index, index1, index2;\n    uint32_t                                diff1, diff2;\n    ngx_queue_t                            *q, *temp;\n    ngx_segment_node_t                      node, *p;\n    ngx_http_upstream_rr_peer_t            *peer;\n    ngx_http_upstream_chash_server_t       *server;\n    ngx_http_upstream_chash_srv_conf_t     *ucscf;\n    ngx_http_upstream_chash_peer_data_t    *uchpd = data;\n    ngx_http_upstream_chash_down_server_t  *down_server;\n\n    ucscf = uchpd->ucscf;\n\n    if (!ngx_queue_empty(&ucscf->down_servers)) {\n        q = ngx_queue_head(&ucscf->down_servers);\n        while(q != ngx_queue_sentinel(&ucscf->down_servers)) {\n            temp = ngx_queue_next(q);\n            down_server = ngx_queue_data(q,\n                                         ngx_http_upstream_chash_down_server_t,\n                                         queue);\n            now = ngx_time();\n            if (now >= down_server->timeout) {\n                peer = ucscf->servers[down_server->id].peer;\n#if (NGX_HTTP_UPSTREAM_CHECK)\n                if (!ngx_http_upstream_check_peer_down(peer->check_index)) {\n#endif\n                    peer->fails = 0;\n                    peer->down = 0;\n                    ucscf->servers[down_server->id].down = 0;\n\n                    ngx_queue_remove(&down_server->queue);\n                    node.key = down_server->id;\n                    ucscf->tree->insert(ucscf->tree, 1, 1, ucscf->number,\n                                        down_server->id, &node);\n#if (NGX_HTTP_UPSTREAM_CHECK)\n                }\n#endif\n            }\n            q = temp;\n        }\n    }\n\n    pc->cached = 0;\n    pc->connection = NULL;\n\n    index = ngx_http_upstream_chash_get_server_index(ucscf->servers,\n                                                     ucscf->number,\n                                                     uchpd->hash);\n    server = &ucscf->servers[index];\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                   \"consistent hash [peer name]:%V %ud\",\n                   &server->peer->name, server->hash);\n\n    if (\n#if (NGX_HTTP_UPSTREAM_CHECK)\n            ngx_http_upstream_check_peer_down(server->peer->check_index) ||\n#endif\n            server->peer->fails > server->peer->max_fails\n            || server->peer->down\n        ) {\n\n        ngx_http_upstream_chash_delete_node(ucscf, server);\n\n        while (1) {\n\n            p = ucscf->tree->query(ucscf->tree, 1, 1, ucscf->number,\n                                   1, index - 1);\n            index1 = p->key;\n\n            p = ucscf->tree->query(ucscf->tree, 1, 1, ucscf->number,\n                                   index + 1, ucscf->number);\n            index2 = p->key;\n\n            if (index1 == ucscf->tree->extreme) {\n\n                if (index2 == ucscf->tree->extreme) {\n                    ngx_log_error(NGX_LOG_ERR, pc->log, 0,\n                                  \"all servers are down!\");\n                    return NGX_BUSY;\n\n                } else {\n                    index1 = index2;\n                    server = &ucscf->servers[index2];\n                }\n\n            } else if (index2 == ucscf->tree->extreme) {\n                server = &ucscf->servers[index1];\n\n            } else {\n\n                if (ucscf->servers[index1].hash > uchpd->hash) {\n                    diff1 = ucscf->servers[index1].hash - uchpd->hash;\n\n                } else {\n                    diff1 = uchpd->hash - ucscf->servers[index1].hash;\n                }\n\n                if (uchpd->hash > ucscf->servers[index2].hash) {\n                    diff2 = uchpd->hash - ucscf->servers[index2].hash;\n\n                } else {\n                    diff2 = ucscf->servers[index2].hash - uchpd->hash;\n                }\n\n                index1 = diff1 > diff2 ? index2 : index1;\n\n                server = &ucscf->servers[index1];\n            }\n\n            if (\n#if (NGX_HTTP_UPSTREAM_CHECK)\n            ngx_http_upstream_check_peer_down(server->peer->check_index) ||\n#endif\n                server->peer->fails > server->peer->max_fails\n                || server->peer->down)\n            {\n                ngx_http_upstream_chash_delete_node(ucscf, server);\n\n            } else {\n                break;\n            }\n\n            index = index1;\n        }\n    }\n\n    if (server->down) {\n        ngx_log_error(NGX_LOG_ERR, pc->log, 0, \"all servers are down\");\n        return NGX_BUSY;\n    }\n\n    uchpd->server = server;\n    peer = server->peer;\n\n    pc->name = &peer->name;\n    pc->sockaddr = peer->sockaddr;\n    pc->socklen = peer->socklen;\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_upstream_chash_delete_node(ngx_http_upstream_chash_srv_conf_t *ucscf,\n    ngx_http_upstream_chash_server_t *server)\n{\n    ngx_http_upstream_chash_server_t **servers, *p;\n    servers = ucscf->real_node[server->rnindex];\n\n    for (; *servers; servers++) {\n        p = *servers;\n        if (!p->down) {\n            ucscf->tree->del(ucscf->tree, 1, 1, ucscf->number, p->index);\n            p->down = 1;\n            ucscf->d_servers[p->index].timeout = ngx_time()\n                                               + p->peer->fail_timeout;\n            ngx_queue_insert_head(&ucscf->down_servers,\n                                  &ucscf->d_servers[p->index].queue);\n        }\n    }\n}\n\n\nstatic uint32_t\nngx_http_upstream_chash_get_server_index(\n    ngx_http_upstream_chash_server_t *servers, uint32_t n, uint32_t hash)\n{\n    uint32_t  low, hight, mid;\n\n    low = 1;\n    hight = n;\n\n    while (low < hight) {\n        mid = (low + hight) >> 1;\n        if (servers[mid].hash == hash) {\n            return mid;\n\n        } else if (servers[mid].hash < hash) {\n            low = mid + 1;\n\n        } else {\n            hight = mid;\n        }\n    }\n\n    if (low == n && servers[low].hash < hash) {\n      return 1;\n    }\n\n    return low;\n}\n\n\nstatic void\nngx_http_upstream_free_chash_peer(ngx_peer_connection_t *pc, void *data,\n    ngx_uint_t state)\n{\n    ngx_http_upstream_chash_peer_data_t *uchpd = data;\n    ngx_log_debug(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                  \"consistent hash free  peer %ui\", state);\n\n    if (uchpd->server == NULL) {\n        return;\n    }\n\n    if (state & NGX_PEER_FAILED) {\n        uchpd->server->peer->fails++;\n    }\n}\n\n\nstatic char *\nngx_http_upstream_chash(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_str_t                           *value;\n    ngx_http_script_compile_t            sc;\n    ngx_http_upstream_srv_conf_t        *uscf;\n    ngx_http_upstream_chash_srv_conf_t  *ucscf;\n\n    uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);\n    if (uscf == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    ucscf = ngx_http_conf_upstream_srv_conf(uscf,\n                                     ngx_http_upstream_consistent_hash_module);\n    if(ucscf == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    value = cf->args->elts;\n    if (value == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));\n\n    sc.cf = cf;\n    sc.source = &value[1];\n    sc.lengths = &ucscf->lengths;\n    sc.values = &ucscf->values;\n    sc.complete_lengths = 1;\n    sc.complete_values = 1;\n\n    if (ngx_http_script_compile(&sc) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    uscf->peer.init_upstream = ngx_http_upstream_init_chash;\n\n    uscf->flags = NGX_HTTP_UPSTREAM_CREATE\n#if (T_NGX_HTTP_UPSTREAM_ID) \n                  |NGX_HTTP_UPSTREAM_ID\n#endif                  \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\n#if (NGX_HTTP_SSL)\n\nstatic ngx_int_t\nngx_http_upstream_chash_set_peer_session(ngx_peer_connection_t *pc, void *data)\n{\n    ngx_http_upstream_chash_peer_data_t *uchpd = data;\n\n    ngx_int_t            rc;\n    ngx_ssl_session_t   *ssl_session;\n\n    ssl_session = uchpd->ssl_session;\n    rc = ngx_ssl_set_session(pc->connection, ssl_session);\n\n#if OPENSSL_VERSION_NUMBER < 0x10100000L\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                   \"set session: %p:%d\",\n                   ssl_session, ssl_session ? ssl_session->references : 0);\n#endif\n\n    return rc;\n}\n\n\nstatic void\nngx_http_upstream_chash_save_peer_session(ngx_peer_connection_t *pc, void *data)\n{\n    ngx_http_upstream_chash_peer_data_t *uchpd = data;\n\n    ngx_ssl_session_t   *old_ssl_session, *ssl_session;\n\n    ssl_session = ngx_ssl_get_session(pc->connection);\n\n    if (ssl_session == NULL) {\n        return;\n    }\n\n#if OPENSSL_VERSION_NUMBER < 0x10100000L\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                   \"save session: %p:%d\", ssl_session, ssl_session->references);\n#endif\n\n    old_ssl_session = uchpd->ssl_session;\n    uchpd->ssl_session = ssl_session;\n\n    if (old_ssl_session) {\n#if OPENSSL_VERSION_NUMBER < 0x10100000L\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                       \"old session: %p:%d\",\n                       old_ssl_session, old_ssl_session->references);\n#endif\n\n        ngx_ssl_free_session(old_ssl_session);\n    }\n}\n\n#endif\n"
  },
  {
    "path": "modules/ngx_http_upstream_dynamic_module/config",
    "content": "ngx_addon_name=ngx_http_upstream_dynamic_module\nHTTP_UPSTREAM_DYNAMIC_SRCS=\"$ngx_addon_dir/ngx_http_upstream_dynamic_module.c\"\n\nif test -n \"$ngx_module_link\"; then\n    ngx_module_type=HTTP\n    ngx_module_name=$ngx_addon_name\n    ngx_module_srcs=\"$HTTP_UPSTREAM_DYNAMIC_SRCS\"\n\n    . auto/module\nelse\n    HTTP_MODULES=\"$HTTP_MODULES ngx_http_upstream_dynamic_module\"\n    NGX_ADDON_SRCS=\"$NGX_ADDON_SRCS $HTTP_UPSTREAM_DYNAMIC_SRCS\"\nfi\n\nhave=T_NGX_HTTP_DYNAMIC_RESOLVE  . auto/have\n"
  },
  {
    "path": "modules/ngx_http_upstream_dynamic_module/ngx_http_upstream_dynamic_module.c",
    "content": "\n/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\n#define NGX_HTTP_UPSTREAM_DR_INIT         0\n#define NGX_HTTP_UPSTREAM_DR_OK           1\n#define NGX_HTTP_UPSTREAM_DR_FAILED       2\n\n#define NGX_HTTP_UPSTREAM_DYN_RESOLVE_NEXT 0\n#define NGX_HTTP_UPSTREAM_DYN_RESOLVE_STALE 1\n#define NGX_HTTP_UPSTREAM_DYN_RESOLVE_SHUTDOWN 2\n\n\ntypedef struct {\n    ngx_int_t                         enabled;\n    ngx_int_t                         fallback;\n    time_t                            fail_timeout;\n    time_t                            fail_check;\n\n    ngx_http_upstream_init_pt         original_init_upstream;\n    ngx_http_upstream_init_peer_pt    original_init_peer;\n\n} ngx_http_upstream_dynamic_srv_conf_t;\n\n\ntypedef struct {\n    ngx_http_upstream_dynamic_srv_conf_t  *conf;\n\n    ngx_http_upstream_t               *upstream;\n\n    void                              *data;\n\n    ngx_http_request_t                *request;\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_upstream_dynamic_peer_data_t;\n\n\nstatic ngx_int_t ngx_http_upstream_init_dynamic_peer(ngx_http_request_t *r,\n    ngx_http_upstream_srv_conf_t *us);\nstatic ngx_int_t ngx_http_upstream_get_dynamic_peer(ngx_peer_connection_t *pc,\n    void *data);\nstatic void ngx_http_upstream_free_dynamic_peer(ngx_peer_connection_t *pc,\n    void *data, ngx_uint_t state);\n\n\n#if (NGX_HTTP_SSL)\nstatic ngx_int_t ngx_http_upstream_dynamic_set_session(\n    ngx_peer_connection_t *pc, void *data);\nstatic void ngx_http_upstream_dynamic_save_session(ngx_peer_connection_t *pc,\n    void *data);\n#endif\n\nstatic void *ngx_http_upstream_dynamic_create_conf(ngx_conf_t *cf);\nstatic char *ngx_http_upstream_dynamic(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n\nextern void ngx_http_upstream_finalize_request(ngx_http_request_t *r,\n    ngx_http_upstream_t *u, ngx_int_t rc);\nextern void ngx_http_upstream_connect(ngx_http_request_t *r,\n    ngx_http_upstream_t *u);\n\n\n\nstatic ngx_command_t  ngx_http_upstream_dynamic_commands[] = {\n\n    { ngx_string(\"dynamic_resolve\"),\n      NGX_HTTP_UPS_CONF|NGX_CONF_TAKE12|NGX_CONF_NOARGS,\n      ngx_http_upstream_dynamic,\n      0,\n      0,\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_upstream_dynamic_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    NULL,                                  /* postconfiguration */\n\n    NULL,                                  /* create main configuration */\n    NULL,                                  /* init main configuration */\n\n    ngx_http_upstream_dynamic_create_conf, /* 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_upstream_dynamic_module = {\n    NGX_MODULE_V1,\n    &ngx_http_upstream_dynamic_module_ctx, /* module context */\n    ngx_http_upstream_dynamic_commands,    /* 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_upstream_init_dynamic(ngx_conf_t *cf,\n    ngx_http_upstream_srv_conf_t *us)\n{\n    ngx_uint_t                             i;\n    ngx_http_upstream_dynamic_srv_conf_t  *dcf;\n    ngx_http_upstream_server_t            *server;\n    ngx_str_t                              host;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cf->log, 0,\n                   \"init dynamic resolve\");\n\n    dcf = ngx_http_conf_upstream_srv_conf(us,\n                                          ngx_http_upstream_dynamic_module);\n\n    /*\n     * Keep one static address for each server to resolve name only one\n     * time. And server[].addrs should not be used in this case.\n     */\n\n    if (us->servers) {\n        server = us->servers->elts;\n\n        for (i = 0; i < us->servers->nelts; i++) {\n            host = server[i].host;\n            if (ngx_inet_addr(host.data, host.len) == INADDR_NONE) {\n                if (server[i].naddrs > 1) {\n                    server[i].naddrs = 1;\n                }\n            }\n        }\n    }\n\n    if (dcf->original_init_upstream(cf, us) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    if (us->servers) {\n        server = us->servers->elts;\n\n        for (i = 0; i < us->servers->nelts; i++) {\n            host = server[i].host;\n            if (ngx_inet_addr(host.data, host.len) == INADDR_NONE) {\n                break;\n            }\n        }\n\n        if (i == us->servers->nelts) {\n            dcf->enabled = 0;\n\n            return NGX_OK;\n        }\n    }\n\n    dcf->original_init_peer = us->peer.init;\n\n    us->peer.init = ngx_http_upstream_init_dynamic_peer;\n\n    dcf->enabled = 1;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_init_dynamic_peer(ngx_http_request_t *r,\n    ngx_http_upstream_srv_conf_t *us)\n{\n    ngx_http_upstream_dynamic_peer_data_t  *dp;\n    ngx_http_upstream_dynamic_srv_conf_t   *dcf;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"init dynamic peer\");\n\n    dcf = ngx_http_conf_upstream_srv_conf(us,\n                                          ngx_http_upstream_dynamic_module);\n\n    dp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_dynamic_peer_data_t));\n    if (dp == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (dcf->original_init_peer(r, us) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    dp->conf = dcf;\n    dp->upstream = r->upstream;\n    dp->data = r->upstream->peer.data;\n    dp->original_get_peer = r->upstream->peer.get;\n    dp->original_free_peer = r->upstream->peer.free;\n    dp->request = r;\n\n    r->upstream->peer.data = dp;\n    r->upstream->peer.get = ngx_http_upstream_get_dynamic_peer;\n    r->upstream->peer.free = ngx_http_upstream_free_dynamic_peer;\n\n#if (NGX_HTTP_SSL)\n    dp->original_set_session = r->upstream->peer.set_session;\n    dp->original_save_session = r->upstream->peer.save_session;\n    r->upstream->peer.set_session = ngx_http_upstream_dynamic_set_session;\n    r->upstream->peer.save_session = ngx_http_upstream_dynamic_save_session;\n#endif\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_upstream_dynamic_handler(ngx_resolver_ctx_t *ctx)\n{\n    ngx_http_request_t                    *r;\n    ngx_http_upstream_t                   *u;\n    ngx_peer_connection_t                 *pc;\n#if defined(nginx_version) && nginx_version >= 1005008\n    socklen_t                              socklen;\n    struct sockaddr                       *sockaddr, *csockaddr;\n#else\n    struct sockaddr_in                    *sin, *csin;\n#endif\n    in_port_t                              port;\n    ngx_str_t                             *addr;\n    u_char                                *p;\n\n    size_t                                 len;\n    ngx_http_upstream_dynamic_srv_conf_t  *dscf;\n    ngx_http_upstream_dynamic_peer_data_t *bp;\n\n    bp = ctx->data;\n    r = bp->request;\n    u = r->upstream;\n    pc = &u->peer;\n    dscf = bp->conf;\n\n    if (ctx->state) {\n\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"%V could not be resolved (%i: %s)\",\n                      &ctx->name, ctx->state,\n                      ngx_resolver_strerror(ctx->state));\n\n        dscf->fail_check = ngx_time();\n\n        pc->resolved = NGX_HTTP_UPSTREAM_DR_FAILED;\n\n    } else {\n        /* dns query ok */\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(ctx->addrs[i].sockaddr, ctx->addrs[i].socklen,\n                                     text, 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        dscf->fail_check = 0;\n#if defined(nginx_version) && nginx_version >= 1005008\n        csockaddr = ctx->addrs[0].sockaddr;\n        socklen = ctx->addrs[0].socklen;\n\n        if (ngx_cmp_sockaddr(pc->sockaddr, pc->socklen, csockaddr, socklen, 0)\n            == NGX_OK)\n        {\n            pc->resolved = NGX_HTTP_UPSTREAM_DR_OK;\n            goto out;\n        }\n\n        sockaddr = ngx_pcalloc(r->pool, socklen);\n        if (sockaddr == NULL) {\n            ngx_http_upstream_finalize_request(r, u,\n                                               NGX_HTTP_INTERNAL_SERVER_ERROR);\n            return;\n        }\n\n        ngx_memcpy(sockaddr, csockaddr, socklen);\n        port = ngx_inet_get_port(pc->sockaddr);\n        \n        switch (sockaddr->sa_family) {\n#if (NGX_HAVE_INET6)\n        case AF_INET6:\n            ((struct sockaddr_in6 *) sockaddr)->sin6_port = htons(port);\n            break;\n#endif\n        default: /* AF_INET */\n            ((struct sockaddr_in *) sockaddr)->sin_port = htons(port);\n        }\n\n        p = ngx_pnalloc(r->pool, NGX_SOCKADDR_STRLEN);\n        if (p == NULL) {\n            ngx_http_upstream_finalize_request(r, u,\n                                               NGX_HTTP_INTERNAL_SERVER_ERROR);\n            return;\n        }\n\n        len = ngx_sock_ntop(sockaddr, socklen, p, NGX_SOCKADDR_STRLEN, 1);\n\n        addr = ngx_palloc(r->pool, sizeof(ngx_str_t));\n        if (addr == NULL) {\n            ngx_http_upstream_finalize_request(r, u,\n                                               NGX_HTTP_INTERNAL_SERVER_ERROR);\n            return;\n        }\n\n        addr->data = p;\n        addr->len = len;\n        pc->sockaddr = sockaddr;\n        pc->socklen = socklen;\n        pc->name = addr;\n\n#else\n        /* for nginx older than 1.5.8 */\n\n        sin = ngx_pcalloc(r->pool, sizeof(struct sockaddr_in));\n        if (sin == NULL) {\n            ngx_http_upstream_finalize_request(r, u,\n                                               NGX_HTTP_INTERNAL_SERVER_ERROR);\n            return;\n        }\n\n        ngx_memcpy(sin, pc->sockaddr, pc->socklen);\n\n        /* only the first IP addr is used in version 1 */\n\n        csin = (struct sockaddr_in *) ctx->addrs[0].sockaddr;\n        if (sin->sin_addr.s_addr == csin->sin_addr.s_addr) {\n\n            pc->resolved = NGX_HTTP_UPSTREAM_DR_OK;\n\n            goto out;\n        }\n\n        sin->sin_addr.s_addr = csin->sin_addr.s_addr;\n\n        len = NGX_INET_ADDRSTRLEN + sizeof(\":65535\") - 1;\n\n        p = ngx_pnalloc(r->pool, len);\n        if (p == NULL) {\n            ngx_http_upstream_finalize_request(r, u,\n                                               NGX_HTTP_INTERNAL_SERVER_ERROR);\n            return;\n        }\n\n        port = ntohs(sin->sin_port);\n        len = ngx_inet_ntop(AF_INET, &sin->sin_addr.s_addr,\n                            p, NGX_INET_ADDRSTRLEN);\n        len = ngx_sprintf(&p[len], \":%d\", port) - p;\n\n        addr = ngx_palloc(r->pool, sizeof(ngx_str_t));\n        if (addr == NULL) {\n            ngx_http_upstream_finalize_request(r, u,\n                                               NGX_HTTP_INTERNAL_SERVER_ERROR);\n            return;\n        }\n\n        addr->data = p;\n        addr->len = len;\n\n        pc->sockaddr = (struct sockaddr *) sin;\n        pc->socklen = sizeof(struct sockaddr_in);\n        pc->name = addr;\n#endif\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                \"name was resolved to %V\", pc->name);\n\n        pc->resolved = NGX_HTTP_UPSTREAM_DR_OK;\n    }\n\nout:\n    ngx_resolve_name_done(ctx);\n    u->dyn_resolve_ctx = NULL;\n\n    ngx_http_upstream_connect(r, u);\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_get_dynamic_peer(ngx_peer_connection_t *pc, void *data)\n{\n    ngx_http_upstream_dynamic_peer_data_t  *bp = data;\n    ngx_http_request_t                     *r;\n    ngx_http_core_loc_conf_t               *clcf;\n    ngx_resolver_ctx_t                     *ctx, temp;\n    ngx_http_upstream_t                    *u;\n    ngx_int_t                               rc;\n    ngx_http_upstream_dynamic_srv_conf_t   *dscf;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                   \"get dynamic peer\");\n\n    /* The \"get\" function will be called twice if\n     * one host is resolved into an IP address.\n     * (via 'ngx_http_upstream_connect' if resolved successfully)\n     *\n     * So here we need to determine if it is the first\n     * time call or the second time call.\n     */\n    if (pc->resolved == NGX_HTTP_UPSTREAM_DR_OK) {\n        return NGX_OK;\n    }\n\n    dscf = bp->conf;\n    r = bp->request;\n    u = r->upstream;\n\n    if (pc->resolved == NGX_HTTP_UPSTREAM_DR_FAILED) {\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                       \"resolve failed! fallback: %ui\", dscf->fallback);\n\n        switch (dscf->fallback) {\n\n        case NGX_HTTP_UPSTREAM_DYN_RESOLVE_STALE:\n            return NGX_OK;\n\n        case NGX_HTTP_UPSTREAM_DYN_RESOLVE_SHUTDOWN:\n            ngx_http_upstream_finalize_request(r, u, NGX_HTTP_BAD_GATEWAY);\n            return NGX_YIELD;\n\n        default:\n            /* default fallback action: check next upstream */\n            return NGX_DECLINED;\n        }\n\n        return NGX_DECLINED;\n    }\n\n    if (dscf->fail_check\n        && (ngx_time() - dscf->fail_check < dscf->fail_timeout))\n    {\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                       \"in fail timeout period, fallback: %ui\", dscf->fallback);\n\n        switch (dscf->fallback) {\n\n        case NGX_HTTP_UPSTREAM_DYN_RESOLVE_STALE:\n            return bp->original_get_peer(pc, bp->data);\n\n        case NGX_HTTP_UPSTREAM_DYN_RESOLVE_SHUTDOWN:\n            ngx_http_upstream_finalize_request(r, u, NGX_HTTP_BAD_GATEWAY);\n            return NGX_YIELD;\n\n        default:\n            /* default fallback action: check next upstream, still need\n             * to get peer in fail timeout period\n             */\n            return bp->original_get_peer(pc, bp->data);\n        }\n\n        return NGX_DECLINED;\n    }\n\n    /* NGX_HTTP_UPSTREAM_DYN_RESOLVE_INIT,  ask balancer */\n\n    rc = bp->original_get_peer(pc, bp->data);\n\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    /* resolve name */\n\n    if (pc->host == NULL) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                       \"load balancer doesn't support dyn resolve!\");\n        return NGX_OK;\n    }\n\n    if (ngx_inet_addr(pc->host->data, pc->host->len) != INADDR_NONE) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                       \"host is an IP address, connect directly!\");\n        return NGX_OK;\n    }\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n    if (clcf->resolver == NULL) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"resolver has not been configured!\");\n        return NGX_OK;\n    }\n\n    temp.name = *pc->host;\n\n    ctx = ngx_resolve_start(clcf->resolver, &temp);\n    if (ctx == NULL) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"resolver start failed!\");\n        return NGX_OK;\n    }\n\n    if (ctx == NGX_NO_RESOLVER) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"resolver started but no resolver!\");\n        return NGX_OK;\n    }\n\n    ctx->name = *pc->host;\n    /* TODO remove */\n    // ctx->type = NGX_RESOLVE_A;\n    /* END */\n    ctx->handler = ngx_http_upstream_dynamic_handler;\n    ctx->data = bp;\n    ctx->timeout = clcf->resolver_timeout;\n\n    u->dyn_resolve_ctx = ctx;\n\n    if (ngx_resolve_name(ctx) != NGX_OK) {\n        ngx_log_error(NGX_LOG_ERR, pc->log, 0,\n                      \"resolver name failed!\\n\");\n\n        u->dyn_resolve_ctx = NULL;\n\n        return NGX_OK;\n    }\n\n    return NGX_YIELD;\n}\n\n\nstatic void\nngx_http_upstream_free_dynamic_peer(ngx_peer_connection_t *pc, void *data,\n    ngx_uint_t state)\n{\n    ngx_http_upstream_dynamic_peer_data_t  *bp = data;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                   \"free dynamic peer\");\n\n    bp->original_free_peer(pc, bp->data, state);\n}\n\n\n#if (NGX_HTTP_SSL)\n\nstatic ngx_int_t\nngx_http_upstream_dynamic_set_session(ngx_peer_connection_t *pc, void *data)\n{\n    ngx_http_upstream_dynamic_peer_data_t  *dp = data;\n\n    return dp->original_set_session(pc, dp->data);\n}\n\n\nstatic void\nngx_http_upstream_dynamic_save_session(ngx_peer_connection_t *pc, void *data)\n{\n    ngx_http_upstream_dynamic_peer_data_t  *dp = data;\n\n    dp->original_save_session(pc, dp->data);\n\n    return;\n}\n\n#endif\n\n\nstatic void *\nngx_http_upstream_dynamic_create_conf(ngx_conf_t *cf)\n{\n    ngx_http_upstream_dynamic_srv_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool,\n                       sizeof(ngx_http_upstream_dynamic_srv_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc():\n     *\n     *     conf->original_init_upstream = NULL;\n     *     conf->original_init_peer = NULL;\n     */\n\n    return conf;\n}\n\n\nstatic char *\nngx_http_upstream_dynamic(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_upstream_srv_conf_t            *uscf;\n    ngx_http_upstream_dynamic_srv_conf_t    *dcf;\n    ngx_str_t   *value, s;\n    ngx_uint_t   i;\n    time_t       fail_timeout;\n    ngx_int_t    fallback;\n\n    uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);\n\n    dcf = ngx_http_conf_upstream_srv_conf(uscf,\n                                          ngx_http_upstream_dynamic_module);\n\n    if (dcf->original_init_upstream) {\n        return \"is duplicate\";\n    }\n\n    dcf->original_init_upstream = uscf->peer.init_upstream\n                                  ? uscf->peer.init_upstream\n                                  : ngx_http_upstream_init_round_robin;\n\n    uscf->peer.init_upstream = ngx_http_upstream_init_dynamic;\n\n    /* read options */\n\n    value = cf->args->elts;\n\n    for (i = 1; i < cf->args->nelts; i++) {\n\n        if (ngx_strncmp(value[i].data, \"fail_timeout=\", 13) == 0) {\n\n            s.len = value[i].len - 13;\n            s.data = &value[i].data[13];\n\n            fail_timeout = ngx_parse_time(&s, 1);\n\n            if (fail_timeout == (time_t) NGX_ERROR) {\n                return \"invalid fail_timeout\";\n            }\n\n            dcf->fail_timeout = fail_timeout;\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"fallback=\", 9) == 0) {\n\n            s.len = value[i].len - 9;\n            s.data = &value[i].data[9];\n\n            if (ngx_strncmp(s.data, \"next\", 4) == 0) {\n                fallback = NGX_HTTP_UPSTREAM_DYN_RESOLVE_NEXT;\n            } else if (ngx_strncmp(s.data, \"stale\", 5) == 0) {\n                fallback = NGX_HTTP_UPSTREAM_DYN_RESOLVE_STALE;\n            } else if (ngx_strncmp(s.data, \"shutdown\", 8) == 0) {\n                fallback = NGX_HTTP_UPSTREAM_DYN_RESOLVE_SHUTDOWN;\n            } else {\n                return \"invalid fallback action\";\n            }\n\n            dcf->fallback = fallback;\n\n            continue;\n        }\n    }\n\n    return NGX_CONF_OK;\n}\n"
  },
  {
    "path": "modules/ngx_http_upstream_dyups_module/config",
    "content": "ngx_addon_name=ngx_http_dyups_module\n\nHTTP_DYUPS_SRCS=\"$ngx_addon_dir/ngx_http_dyups_module.c\"\nHTTP_DYUPS_DEPS=\"$ngx_addon_dir/ngx_http_dyups.h\"\n\ndyups_lua() {\n    echo \" + dyups module support lua\"\n    have=NGX_DYUPS_LUA . auto/have\n    HTTP_DYUPS_SRCS=\"$HTTP_DYUPS_SRCS $ngx_addon_dir/ngx_http_dyups_lua.c\"\n    HTTP_DYUPS_DEPS=\"$HTTP_DYUPS_DEPS $ngx_addon_dir/ngx_http_dyups_lua.h\"\n}\n\nif test -n \"$ngx_module_link\"; then\n    if test -n \"$HTTP_LUA_SRCS\"; then\n        dyups_lua\n        if test -n \"$ngx_http_lua_module_LIBS\"; then\n            ngx_module_libs=\"$ngx_module_libs $ngx_http_lua_module_LIBS\"\n        fi\n    fi\n    ngx_module_type=HTTP\n    ngx_module_name=$ngx_addon_name\n    ngx_module_srcs=\"$HTTP_DYUPS_SRCS\"\n    ngx_module_incs=\"$ngx_addon_dir\"\n    ngx_module_deps=\"$HTTP_DYUPS_DEPS\"\n    . auto/module\nelse\n    if $HTTP_AUX_FILTER_MODULES | grep \"ngx_http_lua_module\" > /dev/null; then\n        dyups_lua\n    fi\n    HTTP_MODULES=\"$HTTP_MODULES ngx_http_dyups_module\"\n    HTTP_INCS=\"$HTTP_INCS $ngx_addon_dir/\"\n    NGX_ADDON_SRCS=\"$NGX_ADDON_SRCS $HTTP_DYUPS_SRCS\"\n    NGX_ADDON_DEPS=\"$NGX_ADDON_DEPS $HTTP_DYUPS_DEPS\"\nfi\n\nhave=NGX_DYUPS . auto/have\n"
  },
  {
    "path": "modules/ngx_http_upstream_dyups_module/ngx_http_dyups.h",
    "content": "/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#ifndef _NGX_HTTP_DYUPS_H_INCLUDE_\n#define _NGX_HTTP_DYUPS_H_INCLUDE_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\nngx_int_t ngx_dyups_update_upstream(ngx_str_t *name, ngx_buf_t *buf,\n    ngx_str_t *rv);\n\nngx_int_t ngx_dyups_delete_upstream(ngx_str_t *name, ngx_str_t *rv);\n\n\ntypedef ngx_int_t (*ngx_dyups_add_upstream_filter_pt)\n    (ngx_http_upstream_main_conf_t *umcf, ngx_http_upstream_srv_conf_t *uscf);\ntypedef ngx_int_t (*ngx_dyups_del_upstream_filter_pt)\n    (ngx_http_upstream_main_conf_t *umcf, ngx_http_upstream_srv_conf_t *uscf);\n\n\nextern ngx_flag_t ngx_http_dyups_api_enable;\nextern ngx_dyups_add_upstream_filter_pt ngx_dyups_add_upstream_top_filter;\nextern ngx_dyups_del_upstream_filter_pt ngx_dyups_del_upstream_top_filter;\n\n#endif\n"
  },
  {
    "path": "modules/ngx_http_upstream_dyups_module/ngx_http_dyups_lua.c",
    "content": "/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#include <ngx_http.h>\n#include <ngx_http_dyups.h>\n#include <ngx_http_dyups_lua.h>\n\n\nstatic int ngx_http_dyups_lua_register(lua_State *L);\nstatic int ngx_http_lua_update_upstream(lua_State *L);\nstatic int ngx_http_lua_delete_upstream(lua_State *L);\n\n\nstatic int\nngx_http_lua_update_upstream(lua_State *L)\n{\n    size_t     size;\n    ngx_int_t  status;\n    ngx_str_t  name, rv;\n    ngx_buf_t  buf;\n\n    if (lua_gettop(L) != 2) {\n        return luaL_error(L, \"exactly 2 arguments expected\");\n    }\n\n    name.data = (u_char *) luaL_checklstring(L, 1, &name.len);\n    buf.pos = buf.start = (u_char *) luaL_checklstring(L, 2, &size);\n    buf.last = buf.end = buf.pos + size;\n\n    status = ngx_dyups_update_upstream(&name, &buf, &rv);\n\n    lua_pushinteger(L, (lua_Integer) status);\n    lua_pushlstring(L, (char *) rv.data, rv.len);\n\n    return 2;\n}\n\n\nstatic int\nngx_http_lua_delete_upstream(lua_State *L)\n{\n    ngx_int_t  status;\n    ngx_str_t  name, rv;\n\n    if (lua_gettop(L) != 1) {\n        return luaL_error(L, \"exactly 1 argument expected\");\n    }\n\n    name.data = (u_char *) luaL_checklstring(L, 1, &name.len);\n\n    status = ngx_dyups_delete_upstream(&name, &rv);\n\n    lua_pushinteger(L, (lua_Integer) status);\n    lua_pushlstring(L, (char *) rv.data, rv.len);\n\n    return 2;\n}\n\n\nstatic int\nngx_http_dyups_lua_register(lua_State *L)\n{\n    lua_createtable(L, 0, 1);\n\n    lua_pushcfunction(L, ngx_http_lua_update_upstream);\n    lua_setfield(L, -2, \"update\");\n\n    lua_pushcfunction(L, ngx_http_lua_delete_upstream);\n    lua_setfield(L, -2, \"delete\");\n\n    return 1;\n}\n\n\nngx_int_t\nngx_http_dyups_lua_preload(ngx_conf_t *cf)\n{\n    if (ngx_http_lua_add_package_preload(cf, \"ngx.dyups\",\n                                         ngx_http_dyups_lua_register)\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "modules/ngx_http_upstream_dyups_module/ngx_http_dyups_lua.h",
    "content": "/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#ifndef _NGX_HTTP_DYUPS_LUA_H_INCLUDE_\n#define _NGX_HTTP_DYUPS_LUA_H_INCLUDE_\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http_lua_api.h>\n#include <lualib.h>\n#include <lauxlib.h>\n\n\nngx_int_t ngx_http_dyups_lua_preload(ngx_conf_t *cf);\n\n\n#endif\n"
  },
  {
    "path": "modules/ngx_http_upstream_dyups_module/ngx_http_dyups_module.c",
    "content": "/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#include <ngx_http.h>\n#include <ngx_http_dyups.h>\n#ifdef NGX_DYUPS_LUA\n#include <ngx_http_dyups_lua.h>\n#endif\n\n\n#define NGX_DYUPS_DELETING     1\n#define NGX_DYUPS_DELETED      2\n\n#define NGX_DYUPS_SHM_NAME_LEN 256\n\n#define NGX_DYUPS_DELETE       1\n#define NGX_DYUPS_ADD          2\n\n#define ngx_dyups_add_timer(ev, timeout)                                      \\\n    if (!ngx_exiting && !ngx_quit) ngx_add_timer(ev, (timeout))\n\n\ntypedef struct {\n    ngx_uint_t                     idx;\n    ngx_uint_t                    *ref;\n    ngx_uint_t                     deleted;\n    ngx_flag_t                     dynamic;\n    ngx_pool_t                    *pool;\n    ngx_http_conf_ctx_t           *ctx;\n    ngx_http_upstream_srv_conf_t  *upstream;\n} ngx_http_dyups_srv_conf_t;\n\n\ntypedef struct {\n    ngx_flag_t                     enable;\n    ngx_flag_t                     trylock;\n    ngx_array_t                    dy_upstreams;/* ngx_http_dyups_srv_conf_t */\n    ngx_str_t                      shm_name;\n    ngx_uint_t                     shm_size;\n    ngx_msec_t                     read_msg_timeout;\n    ngx_flag_t                     read_msg_log;\n} ngx_http_dyups_main_conf_t;\n\n\ntypedef struct {\n    ngx_uint_t                           ref;\n    ngx_http_upstream_init_peer_pt       init;\n} ngx_http_dyups_upstream_srv_conf_t;\n\n\ntypedef struct {\n    void                                *data;\n    ngx_http_dyups_upstream_srv_conf_t  *scf;\n    ngx_event_get_peer_pt                get;\n    ngx_event_free_peer_pt               free;\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} ngx_http_dyups_ctx_t;\n\n\ntypedef struct ngx_dyups_status_s {\n    ngx_pid_t                            pid;\n    ngx_msec_t                           time;\n} ngx_dyups_status_t;\n\n\ntypedef struct ngx_dyups_shctx_s {\n    ngx_queue_t                          msg_queue;\n    ngx_uint_t                           version;\n    ngx_dyups_status_t                  *status;\n} ngx_dyups_shctx_t;\n\n\ntypedef struct ngx_dyups_global_ctx_s {\n    ngx_event_t                          msg_timer;\n    ngx_slab_pool_t                     *shpool;\n    ngx_dyups_shctx_t                   *sh;\n} ngx_dyups_global_ctx_t;\n\n\ntypedef struct ngx_dyups_msg_s {\n    ngx_queue_t                          queue;\n    ngx_str_t                            name;\n    ngx_str_t                            content;\n    ngx_int_t                            count;\n    ngx_uint_t                           flag;\n    ngx_pid_t                           *pid;\n} ngx_dyups_msg_t;\n\n\nstatic ngx_int_t ngx_http_dyups_pre_conf(ngx_conf_t *cf);\nstatic ngx_int_t ngx_http_dyups_init(ngx_conf_t *cf);\nstatic void *ngx_http_dyups_create_main_conf(ngx_conf_t *cf);\nstatic char *ngx_http_dyups_init_main_conf(ngx_conf_t *cf, void *conf);\nstatic char *ngx_http_dyups_cmd_deprecated(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_http_dyups_interface(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic ngx_int_t ngx_http_dyups_interface_handler(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_dyups_interface_read_body(ngx_http_request_t *r);\nstatic ngx_buf_t *ngx_http_dyups_read_body(ngx_http_request_t *r);\nstatic ngx_buf_t *ngx_http_dyups_read_body_from_file(ngx_http_request_t *r);\nstatic void ngx_http_dyups_body_handler(ngx_http_request_t *r);\nstatic void ngx_http_dyups_send_response(ngx_http_request_t *r,\n    ngx_int_t status, ngx_str_t *content);\nstatic ngx_int_t ngx_http_dyups_do_get(ngx_http_request_t *r,\n    ngx_array_t *resource);\nstatic ngx_int_t ngx_http_dyups_do_delete(ngx_http_request_t *r,\n    ngx_array_t *resource);\nstatic ngx_http_dyups_srv_conf_t *ngx_dyups_find_upstream(ngx_str_t *name,\n    ngx_int_t *idx);\nstatic ngx_int_t ngx_dyups_add_server(ngx_http_dyups_srv_conf_t *duscf,\n    ngx_buf_t *buf);\nstatic ngx_int_t ngx_dyups_init_upstream(ngx_http_dyups_srv_conf_t *duscf,\n    ngx_str_t *name, ngx_uint_t index);\nstatic void ngx_dyups_mark_upstream_delete(ngx_http_dyups_srv_conf_t *duscf);\nstatic ngx_int_t ngx_http_dyups_init_peer(ngx_http_request_t *r,\n    ngx_http_upstream_srv_conf_t *us);\nstatic ngx_int_t ngx_http_dyups_get_peer(ngx_peer_connection_t *pc, void *data);\nstatic void ngx_http_dyups_free_peer(ngx_peer_connection_t *pc, void *data,\n    ngx_uint_t state);\nstatic void *ngx_http_dyups_create_srv_conf(ngx_conf_t *cf);\nstatic ngx_buf_t *ngx_http_dyups_show_list(ngx_http_request_t *r);\nstatic ngx_buf_t *ngx_http_dyups_show_detail(ngx_http_request_t *r);\nstatic ngx_buf_t *ngx_http_dyups_show_upstream(ngx_http_request_t *r,\n    ngx_http_dyups_srv_conf_t *duscf);\nstatic ngx_int_t ngx_http_dyups_init_shm_zone(ngx_shm_zone_t *shm_zone,\n    void *data);\nstatic char *ngx_http_dyups_init_shm(ngx_conf_t *cf, void *conf);\nstatic ngx_int_t ngx_http_dyups_get_shm_name(ngx_str_t *shm_name,\n    ngx_pool_t *pool, ngx_uint_t generation);\nstatic ngx_int_t ngx_http_dyups_init_process(ngx_cycle_t *cycle);\nstatic void ngx_http_dyups_exit_process(ngx_cycle_t *cycle);\nstatic void ngx_http_dyups_read_msg(ngx_event_t *ev);\nstatic void ngx_http_dyups_read_msg_locked(ngx_event_t *ev);\nstatic ngx_int_t ngx_http_dyups_send_msg(ngx_str_t *name, ngx_buf_t *body,\n    ngx_uint_t flag);\nstatic void ngx_dyups_destroy_msg(ngx_slab_pool_t *shpool,\n    ngx_dyups_msg_t *msg);\nstatic ngx_int_t ngx_dyups_sync_cmd(ngx_pool_t *pool, ngx_str_t *name,\n    ngx_str_t *content, ngx_uint_t flag);\nstatic ngx_array_t *ngx_dyups_parse_path(ngx_pool_t *pool, ngx_str_t *path);\nstatic ngx_int_t ngx_dyups_do_delete(ngx_str_t *name, ngx_str_t *rv);\nstatic ngx_int_t ngx_dyups_do_update(ngx_str_t *name, ngx_buf_t *buf,\n    ngx_str_t *rv);\nstatic ngx_int_t ngx_dyups_sandbox_update(ngx_buf_t *buf, ngx_str_t *rv);\nstatic void ngx_dyups_purge_msg(ngx_pid_t opid, ngx_pid_t npid);\nstatic void ngx_http_dyups_clean_request(void *data);\n\n\n#if (NGX_HTTP_SSL)\nstatic ngx_int_t ngx_http_dyups_set_peer_session(ngx_peer_connection_t *pc,\n    void *data);\nstatic void ngx_http_dyups_save_peer_session(ngx_peer_connection_t *pc,\n    void *data);\nstatic void ngx_http_dyups_free_peer_sessions(void *data);\n#endif\n\n\nstatic ngx_int_t ngx_dyups_add_upstream_filter(\n    ngx_http_upstream_main_conf_t *umcf, ngx_http_upstream_srv_conf_t *uscf);\nstatic ngx_int_t ngx_dyups_del_upstream_filter(\n    ngx_http_upstream_main_conf_t *umcf, ngx_http_upstream_srv_conf_t *uscf);\n\n\nngx_int_t (*ngx_dyups_add_upstream_top_filter)\n    (ngx_http_upstream_main_conf_t *umcf, ngx_http_upstream_srv_conf_t *uscf);\nngx_int_t (*ngx_dyups_del_upstream_top_filter)\n    (ngx_http_upstream_main_conf_t *umcf, ngx_http_upstream_srv_conf_t *uscf);\n\n\nstatic ngx_command_t  ngx_http_dyups_commands[] = {\n\n    { ngx_string(\"dyups_interface\"),\n      NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,\n      ngx_http_dyups_interface,\n      0,\n      0,\n      NULL },\n\n    { ngx_string(\"dyups_read_msg_timeout\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      offsetof(ngx_http_dyups_main_conf_t, read_msg_timeout),\n      NULL },\n\n    { ngx_string(\"dyups_read_msg_log\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      offsetof(ngx_http_dyups_main_conf_t, read_msg_log),\n      NULL },\n\n    { ngx_string(\"dyups_shm_zone_size\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      offsetof(ngx_http_dyups_main_conf_t, shm_size),\n      NULL },\n\n    { ngx_string(\"dyups_upstream_conf\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,\n      ngx_http_dyups_cmd_deprecated,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"dyups_trylock\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      offsetof(ngx_http_dyups_main_conf_t, trylock),\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_dyups_module_ctx = {\n    ngx_http_dyups_pre_conf,          /* preconfiguration */\n    ngx_http_dyups_init,              /* postconfiguration */\n\n    ngx_http_dyups_create_main_conf,  /* create main configuration */\n    ngx_http_dyups_init_main_conf,    /* init main configuration */\n\n    ngx_http_dyups_create_srv_conf,   /* 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_dyups_module = {\n    NGX_MODULE_V1,\n    &ngx_http_dyups_module_ctx,    /* module context */\n    ngx_http_dyups_commands,       /* module directives */\n    NGX_HTTP_MODULE,               /* module type */\n    NULL,                          /* init master */\n    NULL,                          /* init module */\n    ngx_http_dyups_init_process,   /* init process */\n    NULL,                          /* init thread */\n    NULL,                          /* exit thread */\n    ngx_http_dyups_exit_process,   /* exit process */\n    NULL,                          /* exit master */\n    NGX_MODULE_V1_PADDING\n};\n\n\nngx_flag_t ngx_http_dyups_api_enable = 0;\nstatic ngx_http_upstream_srv_conf_t ngx_http_dyups_deleted_upstream;\nstatic ngx_uint_t ngx_http_dyups_shm_generation = 0;\nstatic ngx_dyups_global_ctx_t ngx_dyups_global_ctx;\n\n\nstatic ngx_int_t\nngx_http_dyups_pre_conf(ngx_conf_t *cf)\n{\n    ngx_dyups_add_upstream_top_filter = ngx_dyups_add_upstream_filter;\n    ngx_dyups_del_upstream_top_filter = ngx_dyups_del_upstream_filter;\n\n    return NGX_OK;\n}\n\n\nstatic char *\nngx_http_dyups_interface(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_core_loc_conf_t    *clcf;\n    ngx_http_dyups_main_conf_t  *dmcf;\n\n    dmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_dyups_module);\n    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);\n    clcf->handler = ngx_http_dyups_interface_handler;\n    dmcf->enable = 1;\n\n    return NGX_CONF_OK;\n}\n\n\nstatic void *\nngx_http_dyups_create_main_conf(ngx_conf_t *cf)\n{\n    ngx_http_dyups_main_conf_t  *dmcf;\n\n    dmcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_dyups_main_conf_t));\n    if (dmcf == NULL) {\n        return NULL;\n    }\n\n#if (NGX_DEBUG)\n\n    if (ngx_array_init(&dmcf->dy_upstreams, cf->pool, 1,\n                       sizeof(ngx_http_dyups_srv_conf_t))\n        != NGX_OK)\n    {\n        return NULL;\n    }\n\n#else\n\n    if (ngx_array_init(&dmcf->dy_upstreams, cf->pool, 1024,\n                       sizeof(ngx_http_dyups_srv_conf_t))\n        != NGX_OK)\n    {\n        return NULL;\n    }\n\n#endif\n\n    dmcf->enable = NGX_CONF_UNSET;\n    dmcf->shm_size = NGX_CONF_UNSET_UINT;\n    dmcf->read_msg_timeout = NGX_CONF_UNSET_MSEC;\n    dmcf->read_msg_log = NGX_CONF_UNSET;\n    dmcf->trylock = NGX_CONF_UNSET;\n\n    return dmcf;\n}\n\n\nstatic char *\nngx_http_dyups_init_main_conf(ngx_conf_t *cf, void *conf)\n{\n    ngx_http_dyups_main_conf_t  *dmcf = conf;\n\n    if (dmcf->enable == NGX_CONF_UNSET) {\n        dmcf->enable = 0;\n    }\n\n    dmcf->enable = dmcf->enable || ngx_http_dyups_api_enable;\n\n    if (dmcf->trylock == NGX_CONF_UNSET) {\n        dmcf->trylock = 0;\n    }\n\n    if (!dmcf->enable) {\n        return NGX_CONF_OK;\n    }\n\n    if (dmcf->read_msg_timeout == NGX_CONF_UNSET_MSEC) {\n        dmcf->read_msg_timeout = 1000;\n    }\n\n    if (dmcf->shm_size == NGX_CONF_UNSET_UINT) {\n        dmcf->shm_size = 2 * 1024 * 1024;\n    }\n\n    return ngx_http_dyups_init_shm(cf, conf);\n}\n\n\nstatic char *\nngx_http_dyups_init_shm(ngx_conf_t *cf, void *conf)\n{\n    ngx_http_dyups_main_conf_t *dmcf = conf;\n\n    ngx_shm_zone_t  *shm_zone;\n\n    ngx_http_dyups_shm_generation++;\n\n    if (ngx_http_dyups_get_shm_name(&dmcf->shm_name, cf->pool,\n                                     ngx_http_dyups_shm_generation)\n        != NGX_OK)\n    {\n        return NGX_CONF_ERROR;\n    }\n\n    shm_zone = ngx_shared_memory_add(cf, &dmcf->shm_name, dmcf->shm_size,\n                                     &ngx_http_dyups_module);\n    if (shm_zone == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_log_error(NGX_LOG_DEBUG, cf->log, 0,\n                  \"[dyups] init shm:%V, size:%ui\", &dmcf->shm_name,\n                  dmcf->shm_size);\n\n    shm_zone->data = cf->pool;\n    shm_zone->init = ngx_http_dyups_init_shm_zone;\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_dyups_get_shm_name(ngx_str_t *shm_name, ngx_pool_t *pool,\n    ngx_uint_t generation)\n{\n    u_char  *last;\n\n    shm_name->data = ngx_palloc(pool, NGX_DYUPS_SHM_NAME_LEN);\n    if (shm_name->data == NULL) {\n        return NGX_ERROR;\n    }\n\n    last = ngx_snprintf(shm_name->data, NGX_DYUPS_SHM_NAME_LEN, \"%s#%ui\",\n                        \"ngx_http_dyups_module\", generation);\n\n    shm_name->len = last - shm_name->data;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_dyups_init_shm_zone(ngx_shm_zone_t *shm_zone, void *data)\n{\n    ngx_slab_pool_t    *shpool;\n    ngx_dyups_shctx_t  *sh;\n\n    shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;\n\n    sh = ngx_slab_alloc(shpool, sizeof(ngx_dyups_shctx_t));\n    if (sh == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_dyups_global_ctx.sh = sh;\n    ngx_dyups_global_ctx.shpool = shpool;\n\n    ngx_queue_init(&sh->msg_queue);\n\n    sh->version = 0;\n    sh->status = NULL;\n\n    return NGX_OK;\n}\n\n\nstatic void *\nngx_http_dyups_create_srv_conf(ngx_conf_t *cf)\n{\n    ngx_http_dyups_upstream_srv_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_dyups_upstream_srv_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    /*\n      conf->init = NULL;\n    */\n    return conf;\n}\n\n\nstatic ngx_int_t\nngx_http_dyups_init(ngx_conf_t *cf)\n{\n    ngx_url_t                            u;\n    ngx_uint_t                           i;\n    ngx_http_dyups_srv_conf_t           *duscf;\n    ngx_http_upstream_server_t          *us;\n    ngx_http_dyups_main_conf_t          *dmcf;\n    ngx_http_upstream_srv_conf_t       **uscfp;\n    ngx_http_upstream_main_conf_t       *umcf;\n    ngx_http_dyups_upstream_srv_conf_t  *dscf;\n\n    dmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_dyups_module);\n    umcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_upstream_module);\n\n    if (!dmcf->enable) {\n        return NGX_OK;\n    }\n\n    uscfp = umcf->upstreams.elts;\n    for (i = 0; i < umcf->upstreams.nelts; i++) {\n\n        duscf = ngx_array_push(&dmcf->dy_upstreams);\n        if (duscf == NULL) {\n            return NGX_ERROR;\n        }\n\n        ngx_memzero(duscf, sizeof(ngx_http_dyups_srv_conf_t));\n\n        duscf->pool = NULL;\n        duscf->upstream = uscfp[i];\n        duscf->dynamic = (uscfp[i]->port == 0\n                          && uscfp[i]->srv_conf && uscfp[i]->servers\n                          && uscfp[i]->flags & NGX_HTTP_UPSTREAM_CREATE);\n        duscf->deleted = 0;\n        duscf->idx = i;\n\n        if (duscf->dynamic) {\n            dscf = duscf->upstream->srv_conf[ngx_http_dyups_module.ctx_index];\n            duscf->ref = &dscf->ref;\n        }\n    }\n\n    /* alloc a dummy upstream */\n\n    ngx_memzero(&ngx_http_dyups_deleted_upstream,\n                sizeof(ngx_http_upstream_srv_conf_t));\n    ngx_http_dyups_deleted_upstream.srv_conf = ((ngx_http_conf_ctx_t *)\n                                                (cf->ctx))->srv_conf;\n    ngx_http_dyups_deleted_upstream.servers = ngx_array_create(cf->pool, 1,\n                                           sizeof(ngx_http_upstream_server_t));\n\n    us = ngx_array_push(ngx_http_dyups_deleted_upstream.servers);\n    if (us == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_memzero(&u, sizeof(ngx_url_t));\n    ngx_memzero(us, sizeof(ngx_http_upstream_server_t));\n\n    u.default_port = 80;\n    ngx_str_set(&u.url, \"0.0.0.0\");\n\n    if (ngx_parse_url(cf->pool, &u) != NGX_OK) {\n        if (u.err) {\n            ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,\n                          \"[dyups] %s in init\", u.err);\n        }\n\n        return NGX_ERROR;\n    }\n\n    us->addrs = u.addrs;\n    us->naddrs = u.naddrs;\n    us->down = 1;\n\n    ngx_str_set(&ngx_http_dyups_deleted_upstream.host,\n                \"_dyups_upstream_down_host_\");\n    ngx_http_dyups_deleted_upstream.file_name = (u_char *) \"dyups_upstream\";\n\n#ifdef NGX_DYUPS_LUA\n    return ngx_http_dyups_lua_preload(cf);\n#else\n    return NGX_OK;\n#endif\n}\n\n\nstatic ngx_int_t\nngx_http_dyups_init_process(ngx_cycle_t *cycle)\n{\n    ngx_int_t                    i;\n    ngx_pid_t                    pid;\n    ngx_time_t                  *tp;\n    ngx_msec_t                   now, delay;\n    ngx_event_t                 *timer;\n    ngx_core_conf_t             *ccf;\n    ngx_slab_pool_t             *shpool;\n    ngx_dyups_shctx_t           *sh;\n    ngx_dyups_status_t          *status;\n    ngx_http_dyups_main_conf_t  *dmcf;\n\n    ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);\n\n    dmcf = ngx_http_cycle_get_module_main_conf(ngx_cycle,\n                                               ngx_http_dyups_module);\n\n    if (!dmcf || !dmcf->enable || ngx_process == NGX_PROCESS_HELPER) {\n        ngx_http_dyups_api_enable = 0;\n        return NGX_OK;\n    }\n\n    ngx_http_dyups_api_enable = 1;\n\n    timer = &ngx_dyups_global_ctx.msg_timer;\n    ngx_memzero(timer, sizeof(ngx_event_t));\n\n    timer->handler = ngx_http_dyups_read_msg;\n    timer->log = cycle->log;\n    timer->data = dmcf;\n\n    /*\n     * when init process, break up timer, in case of shpool->mutex compete\n     */\n    delay = dmcf->read_msg_timeout > 1000 ? dmcf->read_msg_timeout : 1000;\n    ngx_add_timer(timer, ngx_random() % delay);\n\n    shpool = ngx_dyups_global_ctx.shpool;\n    sh = ngx_dyups_global_ctx.sh;\n\n    ngx_shmtx_lock(&shpool->mutex);\n\n    if (sh->status == NULL) {\n        sh->status = ngx_slab_alloc_locked(shpool,\n                           sizeof(ngx_dyups_status_t) * ccf->worker_processes);\n\n        if (sh->status == NULL) {\n            ngx_shmtx_unlock(&shpool->mutex);\n            return NGX_ERROR;\n        }\n\n        ngx_memzero(sh->status,\n                    sizeof(ngx_dyups_status_t) * ccf->worker_processes);\n\n        ngx_shmtx_unlock(&shpool->mutex);\n        return NGX_OK;\n    }\n\n    if (sh->version != 0) {\n        ngx_shmtx_unlock(&shpool->mutex);\n\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,\n                      \"[dyups] process start after abnormal exits\");\n\n        ngx_msleep(dmcf->read_msg_timeout * 2);\n\n        ngx_time_update();\n        tp = ngx_timeofday();\n        now = (ngx_msec_t) (tp->sec * 1000 + tp->msec);\n\n        ngx_shmtx_lock(&shpool->mutex);\n\n        if (sh->status == NULL) {\n            ngx_shmtx_unlock(&shpool->mutex);\n            return NGX_OK;\n        }\n\n        status = &sh->status[0];\n\n        for (i = 1; i < ccf->worker_processes; i++) {\n\n            ngx_log_error(NGX_LOG_WARN, cycle->log, 0,\n                          \"[dyups] process %P %ui %ui\",\n                          sh->status[i].pid, status->time, sh->status[i].time);\n\n            if (status->time > sh->status[i].time) {\n                status = &sh->status[i];\n            }\n        }\n\n        pid = status->pid;\n        status->time = now;\n        status->pid = ngx_pid;\n\n        ngx_log_error(NGX_LOG_WARN, cycle->log, 0,\n                      \"[dyups] new process is %P, old process is %P\",\n                      ngx_pid, pid);\n\n        ngx_dyups_purge_msg(pid, ngx_pid);\n    }\n\n    ngx_shmtx_unlock(&shpool->mutex);\n    return NGX_OK;\n}\n\n\nstatic void\nngx_dyups_purge_msg(ngx_pid_t opid, ngx_pid_t npid)\n{\n    ngx_int_t            i;\n    ngx_queue_t         *q;\n    ngx_dyups_msg_t     *msg;\n    ngx_dyups_shctx_t   *sh;\n\n    sh = ngx_dyups_global_ctx.sh;\n\n    for (q = ngx_queue_last(&sh->msg_queue);\n         q != ngx_queue_sentinel(&sh->msg_queue);\n         q = ngx_queue_prev(q))\n    {\n        msg = ngx_queue_data(q, ngx_dyups_msg_t, queue);\n\n        for (i = 0; i < msg->count; i++) {\n            if (msg->pid[i] == opid) {\n\n                ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, 0,\n                              \"[dyups] restore one pid conflict\"\n                              \" old: %P, new: %P\", opid, npid);\n                msg->pid[i] = npid;\n            }\n        }\n    }\n}\n\n\nstatic void\nngx_http_dyups_exit_process(ngx_cycle_t *cycle)\n{\n    ngx_uint_t                   i;\n    ngx_http_dyups_srv_conf_t   *duscfs, *duscf;\n    ngx_http_dyups_main_conf_t  *dumcf;\n\n    dumcf = ngx_http_cycle_get_module_main_conf(ngx_cycle,\n                                                ngx_http_dyups_module);\n    if (!dumcf) {\n        return;\n    }\n\n    duscfs = dumcf->dy_upstreams.elts;\n    for (i = 0; i < dumcf->dy_upstreams.nelts; i++) {\n\n        duscf = &duscfs[i];\n\n        if (duscf->pool) {\n            ngx_destroy_pool(duscf->pool);\n            duscf->pool = NULL;\n        }\n    }\n}\n\n\nstatic ngx_int_t\nngx_http_dyups_interface_handler(ngx_http_request_t *r)\n{\n    ngx_array_t  *res;\n    ngx_event_t  *timer;\n\n    timer = &ngx_dyups_global_ctx.msg_timer;\n\n    res = ngx_dyups_parse_path(r->pool, &r->uri);\n    if (res == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    if (r->method == NGX_HTTP_GET) {\n        ngx_http_dyups_read_msg(timer);\n        return ngx_http_dyups_do_get(r, res);\n    }\n\n    if (r->method == NGX_HTTP_DELETE) {\n        return ngx_http_dyups_do_delete(r, res);\n    }\n\n    return ngx_http_dyups_interface_read_body(r);\n}\n\n\nngx_int_t\nngx_dyups_delete_upstream(ngx_str_t *name, ngx_str_t *rv)\n{\n    ngx_int_t                    status, rc;\n    ngx_event_t                 *timer;\n    ngx_slab_pool_t             *shpool;\n    ngx_http_dyups_main_conf_t  *dmcf;\n\n    dmcf = ngx_http_cycle_get_module_main_conf(ngx_cycle,\n                                               ngx_http_dyups_module);\n    timer = &ngx_dyups_global_ctx.msg_timer;\n    shpool = ngx_dyups_global_ctx.shpool;\n\n    if (!ngx_http_dyups_api_enable) {\n        ngx_str_set(rv, \"API disabled\\n\");\n        return NGX_HTTP_NOT_ALLOWED;\n    }\n\n    if (!dmcf->trylock) {\n\n        ngx_shmtx_lock(&shpool->mutex);\n\n    } else {\n\n        if (!ngx_shmtx_trylock(&shpool->mutex)) {\n            return NGX_HTTP_CONFLICT;\n        }\n\n    }\n\n    ngx_http_dyups_read_msg_locked(timer);\n\n    status = ngx_dyups_do_delete(name, rv);\n    if (status != NGX_HTTP_OK) {\n        goto finish;\n    }\n\n    rc = ngx_http_dyups_send_msg(name, NULL, NGX_DYUPS_DELETE);\n    if (rc != NGX_OK) {\n        ngx_str_set(rv, \"alert: delte success but not sync to other process\");\n        ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, \"[dyups] %V\", &rv);\n        status = NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n finish:\n\n    ngx_shmtx_unlock(&shpool->mutex);\n\n    return status;\n}\n\n\nstatic ngx_int_t\nngx_http_dyups_do_get(ngx_http_request_t *r, ngx_array_t *resource)\n{\n    ngx_int_t                   rc, status, dumy;\n    ngx_buf_t                  *buf;\n    ngx_str_t                  *value;\n    ngx_chain_t                 out;\n    ngx_http_dyups_srv_conf_t  *duscf;\n\n    rc = ngx_http_discard_request_body(r);\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    if (resource->nelts == 0) {\n        return NGX_HTTP_NOT_FOUND;\n    }\n\n    buf = NULL;\n    value = resource->elts;\n\n    if (value[0].len == 4\n        && ngx_strncasecmp(value[0].data, (u_char *) \"list\", 4) == 0)\n    {\n        buf = ngx_http_dyups_show_list(r);\n        if (buf == NULL) {\n            status = NGX_HTTP_INTERNAL_SERVER_ERROR;\n            goto finish;\n        }\n    }\n\n    if (value[0].len == 6\n        && ngx_strncasecmp(value[0].data, (u_char *) \"detail\", 6) == 0)\n    {\n        buf = ngx_http_dyups_show_detail(r);\n        if (buf == NULL) {\n            status = NGX_HTTP_INTERNAL_SERVER_ERROR;\n            goto finish;\n        }\n    }\n\n    if (value[0].len == 8\n        && ngx_strncasecmp(value[0].data, (u_char *) \"upstream\", 8) == 0)\n    {\n        if (resource->nelts != 2) {\n            status = NGX_HTTP_NOT_FOUND;\n            goto finish;\n        }\n\n        duscf = ngx_dyups_find_upstream(&value[1], &dumy);\n        if (duscf == NULL || duscf->deleted) {\n            status = NGX_HTTP_NOT_FOUND;\n            goto finish;\n        }\n\n        buf = ngx_http_dyups_show_upstream(r, duscf);\n        if (buf == NULL) {\n            status = NGX_HTTP_INTERNAL_SERVER_ERROR;\n            goto finish;\n        }\n    }\n\n    if (buf != NULL && ngx_buf_size(buf) == 0) {\n        status = NGX_HTTP_NO_CONTENT;\n    } else {\n        status = buf ? NGX_HTTP_OK : NGX_HTTP_NOT_FOUND;\n    }\n\nfinish:\n\n    r->headers_out.status = status;\n\n    if (status != NGX_HTTP_OK) {\n        r->headers_out.content_length_n = 0;\n    } else {\n        r->headers_out.content_length_n = ngx_buf_size(buf);\n    }\n\n    rc = ngx_http_send_header(r);\n    if (rc == NGX_ERROR || rc > NGX_OK) {\n        return rc;\n    }\n\n    if (status != NGX_HTTP_OK) {\n        return ngx_http_send_special(r, NGX_HTTP_FLUSH);\n    }\n\n    buf->last_buf = 1;\n    out.buf = buf;\n    out.next = NULL;\n\n    return ngx_http_output_filter(r, &out);\n}\n\n\nstatic ngx_buf_t *\nngx_http_dyups_show_list(ngx_http_request_t *r)\n{\n    ngx_uint_t                   i, len;\n    ngx_str_t                    host;\n    ngx_buf_t                   *buf;\n    ngx_http_dyups_srv_conf_t   *duscfs, *duscf;\n    ngx_http_dyups_main_conf_t  *dumcf;\n\n    dumcf = ngx_http_get_module_main_conf(r, ngx_http_dyups_module);\n\n    len = 0;\n    duscfs = dumcf->dy_upstreams.elts;\n    for (i = 0; i < dumcf->dy_upstreams.nelts; i++) {\n\n        duscf = &duscfs[i];\n\n        if (!duscf->dynamic) {\n            continue;\n        }\n\n        if (duscf->deleted) {\n            continue;\n        }\n\n        len += duscf->upstream->host.len + 1;\n    }\n\n    buf = ngx_create_temp_buf(r->pool, len);\n    if (buf == NULL) {\n        return NULL;\n    }\n\n    for (i = 0; i < dumcf->dy_upstreams.nelts; i++) {\n\n        duscf = &duscfs[i];\n\n        if (!duscf->dynamic) {\n            continue;\n        }\n\n        if (duscf->deleted) {\n            continue;\n        }\n\n        host = duscf->upstream->host;\n        buf->last = ngx_sprintf(buf->last, \"%V\\n\", &host);\n    }\n\n    return buf;\n}\n\n\nstatic ngx_buf_t *\nngx_http_dyups_show_detail(ngx_http_request_t *r)\n{\n    ngx_uint_t                   i, j, len;\n    ngx_str_t                    host;\n    ngx_buf_t                   *buf;\n    ngx_http_dyups_srv_conf_t   *duscfs, *duscf;\n    ngx_http_dyups_main_conf_t  *dumcf;\n    ngx_http_upstream_server_t  *us;\n\n    dumcf = ngx_http_get_module_main_conf(r, ngx_http_dyups_module);\n\n    len = 0;\n    duscfs = dumcf->dy_upstreams.elts;\n    for (i = 0; i < dumcf->dy_upstreams.nelts; i++) {\n\n        duscf = &duscfs[i];\n\n        if (!duscf->dynamic) {\n            continue;\n        }\n\n        if (duscf->deleted) {\n            continue;\n        }\n\n        len += duscf->upstream->host.len + 1;\n\n        for (j = 0; j < duscf->upstream->servers->nelts; j++) {\n            len += sizeof(\"server \") + 256;\n        }\n    }\n\n    buf = ngx_create_temp_buf(r->pool, len);\n    if (buf == NULL) {\n        return NULL;\n    }\n\n    for (i = 0; i < dumcf->dy_upstreams.nelts; i++) {\n\n        duscf = &duscfs[i];\n\n        if (!duscf->dynamic) {\n            continue;\n        }\n\n        if (duscf->deleted) {\n            continue;\n        }\n\n        host = duscf->upstream->host;\n        buf->last = ngx_sprintf(buf->last, \"%V\\n\", &host);\n\n        us = duscf->upstream->servers->elts;\n        for (j = 0; j < duscf->upstream->servers->nelts; j++) {\n            buf->last = ngx_sprintf(buf->last,\n                                    \"server %V weight=%i \"\n#ifdef NGX_HTTP_UPSTREAM_MAX_CONNS\n                                    \"max_conns=%i \"\n#endif\n                                    \"max_fails=%i \"\n                                    \"fail_timeout=%T backup=%d down=%d\\n\",\n                                    &us[j].addrs->name,\n                                    us[j].weight,\n#ifdef NGX_HTTP_UPSTREAM_MAX_CONNS\n                                    us[j].max_conns,\n#endif\n                                    us[j].max_fails,\n                                    us[j].fail_timeout,\n                                    us[j].backup,\n                                    us[j].down);\n        }\n        buf->last = ngx_sprintf(buf->last, \"\\n\");\n    }\n\n    return buf;\n}\n\n\nstatic ngx_buf_t *\nngx_http_dyups_show_upstream(ngx_http_request_t *r,\n    ngx_http_dyups_srv_conf_t *duscf)\n{\n    ngx_uint_t                   i, len;\n    ngx_buf_t                   *buf;\n    ngx_http_upstream_server_t  *us;\n\n    len = 0;\n    for (i = 0; i < duscf->upstream->servers->nelts; i++) {\n        len += sizeof(\"server \") + 81;\n    }\n\n    buf = ngx_create_temp_buf(r->pool, len);\n    if (buf == NULL) {\n        return NULL;\n    }\n\n    us = duscf->upstream->servers->elts;\n    for (i = 0; i < duscf->upstream->servers->nelts; i++) {\n        buf->last = ngx_sprintf(buf->last, \"server %V\\n\",\n                                &us[i].addrs->name);\n    }\n\n    return buf;\n}\n\n\nstatic ngx_int_t\nngx_dyups_do_delete(ngx_str_t *name, ngx_str_t *rv)\n{\n    ngx_int_t                   dumy;\n    ngx_http_dyups_srv_conf_t  *duscf;\n\n    duscf = ngx_dyups_find_upstream(name, &dumy);\n\n    if (duscf == NULL || duscf->deleted) {\n\n        ngx_log_error(NGX_LOG_DEBUG, ngx_cycle->log, 0,\n                      \"[dyups] not find upstream %V %p\", name, duscf);\n\n        ngx_str_set(rv, \"not found uptream\");\n        return NGX_HTTP_NOT_FOUND;\n    }\n\n    ngx_dyups_mark_upstream_delete(duscf);\n\n    ngx_str_set(rv, \"success\");\n\n    return NGX_HTTP_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_dyups_do_delete(ngx_http_request_t *r, ngx_array_t *resource)\n{\n    ngx_str_t   *value, name, rv;\n    ngx_int_t    status, rc;\n    ngx_buf_t   *b;\n    ngx_chain_t  out;\n\n    rc = ngx_http_discard_request_body(r);\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    if (resource->nelts != 2) {\n        ngx_str_set(&rv, \"not support this interface\");\n        status = NGX_HTTP_NOT_ALLOWED;\n        goto finish;\n    }\n\n    value = resource->elts;\n\n    if (value[0].len != 8\n        || ngx_strncasecmp(value[0].data, (u_char *) \"upstream\", 8) != 0)\n    {\n        ngx_str_set(&rv, \"not support this api\");\n        status = NGX_HTTP_NOT_ALLOWED;\n        goto finish;\n    }\n\n    name = value[1];\n\n    status = ngx_dyups_delete_upstream(&name, &rv);\n\nfinish:\n\n    r->headers_out.status = status;\n    r->headers_out.content_length_n = rv.len;\n\n    rc = ngx_http_send_header(r);\n    if (rc == NGX_ERROR || rc > NGX_OK) {\n        return rc;\n    }\n\n    if (rv.len == 0) {\n        return ngx_http_send_special(r, NGX_HTTP_FLUSH);\n    }\n\n    b = ngx_create_temp_buf(r->pool, rv.len);\n    if (b == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    b->pos = rv.data;\n    b->last = rv.data + rv.len;\n    b->last_buf = 1;\n\n    out.buf = b;\n    out.next = NULL;\n\n    return ngx_http_output_filter(r, &out);\n}\n\n\nstatic ngx_int_t\nngx_http_dyups_interface_read_body(ngx_http_request_t *r)\n{\n    ngx_int_t  rc;\n\n    rc = ngx_http_read_client_request_body(r, ngx_http_dyups_body_handler);\n\n    if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {\n        return rc;\n    }\n\n    return NGX_DONE;\n}\n\n\nstatic void\nngx_http_dyups_body_handler(ngx_http_request_t *r)\n{\n    ngx_str_t                   *value, rv, name;\n    ngx_int_t                    status;\n    ngx_buf_t                   *body;\n    ngx_array_t                 *res;\n\n    ngx_str_set(&rv, \"\");\n\n    if (r->method != NGX_HTTP_POST) {\n        status = NGX_HTTP_NOT_ALLOWED;\n        goto finish;\n    }\n\n    res = ngx_dyups_parse_path(r->pool, &r->uri);\n    if (res == NULL) {\n        ngx_str_set(&rv, \"out of memory\");\n        status = NGX_HTTP_INTERNAL_SERVER_ERROR;\n        goto finish;\n    }\n\n    if (r->request_body == NULL || r->request_body->bufs == NULL) {\n        status = NGX_HTTP_NO_CONTENT;\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"[dyups] interface no content\");\n        ngx_str_set(&rv, \"no content\\n\");\n        goto finish;\n    }\n\n    if (r->request_body->temp_file) {\n\n        body = ngx_http_dyups_read_body_from_file(r);\n    } else {\n\n        body = ngx_http_dyups_read_body(r);\n    }\n\n    if (body == NULL) {\n        status = NGX_HTTP_INTERNAL_SERVER_ERROR;\n        ngx_str_set(&rv, \"out of memory\\n\");\n        goto finish;\n    }\n\n    if (res->nelts != 2) {\n        ngx_str_set(&rv, \"not support this interface\");\n        status = NGX_HTTP_NOT_FOUND;\n        goto finish;\n    }\n\n    /*\n      url: /upstream\n      body: server ip:port weight\n    */\n\n    value = res->elts;\n\n    if (value[0].len != 8\n        || ngx_strncasecmp(value[0].data, (u_char *) \"upstream\", 8) != 0)\n    {\n        ngx_str_set(&rv, \"not support this api\");\n        status = NGX_HTTP_NOT_FOUND;\n        goto finish;\n    }\n\n    name = value[1];\n\n    ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                  \"[dyups] post upstream name: %V\", &name);\n\n    status = ngx_dyups_update_upstream(&name, body, &rv);\n\nfinish:\n\n    ngx_http_dyups_send_response(r, status, &rv);\n}\n\n\nngx_int_t\nngx_dyups_update_upstream(ngx_str_t *name, ngx_buf_t *buf, ngx_str_t *rv)\n{\n    ngx_int_t                    status;\n    ngx_event_t                 *timer;\n    ngx_slab_pool_t             *shpool;\n    ngx_http_dyups_main_conf_t  *dmcf;\n\n    dmcf = ngx_http_cycle_get_module_main_conf(ngx_cycle,\n                                               ngx_http_dyups_module);\n    timer = &ngx_dyups_global_ctx.msg_timer;\n    shpool = ngx_dyups_global_ctx.shpool;\n\n    if (!ngx_http_dyups_api_enable) {\n        ngx_str_set(rv, \"API disabled\\n\");\n        return NGX_HTTP_NOT_ALLOWED;\n    }\n\n    if (!dmcf->trylock) {\n\n        ngx_shmtx_lock(&shpool->mutex);\n\n    } else {\n\n        if (!ngx_shmtx_trylock(&shpool->mutex)) {\n            ngx_str_set(rv, \"wait and try again\\n\");\n            return NGX_HTTP_CONFLICT;\n        }\n    }\n\n    ngx_http_dyups_read_msg_locked(timer);\n\n    status = ngx_dyups_sandbox_update(buf, rv);\n    if (status != NGX_HTTP_OK) {\n        goto finish;\n    }\n\n    status = ngx_dyups_do_update(name, buf, rv);\n    if (status == NGX_HTTP_OK) {\n\n        if (ngx_http_dyups_send_msg(name, buf, NGX_DYUPS_ADD)) {\n            ngx_str_set(rv, \"alert: update success \"\n                        \"but not sync to other process\");\n            status = NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n    }\n\n finish:\n\n    ngx_shmtx_unlock(&shpool->mutex);\n\n    return status;\n}\n\n\nstatic ngx_int_t\nngx_dyups_do_update(ngx_str_t *name, ngx_buf_t *buf, ngx_str_t *rv)\n{\n    ngx_int_t                       rc, idx;\n    ngx_http_dyups_srv_conf_t      *duscf;\n    ngx_http_dyups_main_conf_t     *dumcf;\n    ngx_http_upstream_srv_conf_t  **uscfp;\n    ngx_http_upstream_main_conf_t  *umcf;\n\n    umcf = ngx_http_cycle_get_module_main_conf(ngx_cycle,\n                                               ngx_http_upstream_module);\n    dumcf = ngx_http_cycle_get_module_main_conf(ngx_cycle,\n                                                ngx_http_dyups_module);\n\n    duscf = ngx_dyups_find_upstream(name, &idx);\n    if (duscf) {\n        ngx_log_error(NGX_LOG_DEBUG, ngx_cycle->log, 0,\n                      \"[dyups] upstream reuse, idx: [%i]\", idx);\n\n        if (!duscf->deleted) {\n            ngx_log_error(NGX_LOG_DEBUG, ngx_cycle->log, 0,\n                          \"[dyups] upstream delete first\");\n            ngx_dyups_mark_upstream_delete(duscf);\n\n            duscf = ngx_dyups_find_upstream(name, &idx);\n\n            ngx_log_error(NGX_LOG_DEBUG, ngx_cycle->log, 0,\n                          \"[dyups] find another, idx: [%i]\", idx);\n        }\n    }\n\n    if (idx == -1) {\n        /* need create a new upstream */\n\n        ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, 0,\n                      \"[dyups] create upstream %V\", name);\n\n        duscf = ngx_array_push(&dumcf->dy_upstreams);\n        if (duscf == NULL) {\n            ngx_str_set(rv, \"out of memory\");\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n\n        uscfp = ngx_array_push(&umcf->upstreams);\n        if (uscfp == NULL) {\n            ngx_str_set(rv, \"out of memory\");\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n\n        ngx_memzero(duscf, sizeof(ngx_http_dyups_srv_conf_t));\n        idx = umcf->upstreams.nelts - 1;\n    }\n\n    duscf->idx = idx;\n    rc = ngx_dyups_init_upstream(duscf, name, idx);\n\n    if (rc != NGX_OK) {\n        ngx_str_set(rv, \"init upstream failed\");\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    /* init upstream */\n    rc = ngx_dyups_add_server(duscf, buf);\n    if (rc != NGX_OK) {\n        ngx_str_set(rv, \"add server failed\");\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    ngx_str_set(rv, \"success\");\n\n    return NGX_HTTP_OK;\n}\n\n\nstatic ngx_int_t\nngx_dyups_sandbox_update(ngx_buf_t *buf, ngx_str_t *rv)\n{\n    ngx_int_t  rc;\n    ngx_str_t  dumy;\n\n    ngx_str_t  sandbox = ngx_string(\"_dyups_upstream_sandbox_\");\n\n    rc = ngx_dyups_do_update(&sandbox, buf, rv);\n\n    (void) ngx_dyups_do_delete(&sandbox, &dumy);\n\n    return rc;\n}\n\n\nstatic char *\nngx_dyups_parse_upstream(ngx_conf_t *cf, ngx_buf_t *buf)\n{\n    char                       *rc;\n    ngx_buf_t                   b;\n    ngx_str_t                   s;\n    ngx_uint_t                  i;\n    ngx_hash_t                  vh, vh_prev;\n    ngx_array_t                 va, va_prev;\n    ngx_conf_file_t             conf_file;\n    ngx_http_variable_t        *v;\n    ngx_hash_keys_arrays_t      vk;\n    ngx_http_core_main_conf_t  *cmcf;\n\n    b = *buf;   /* avoid modifying @buf */\n\n    ngx_memzero(&conf_file, sizeof(ngx_conf_file_t));\n    conf_file.file.fd = NGX_INVALID_FILE;\n    conf_file.buffer = &b;\n\n    cf->conf_file = &conf_file;\n\n    rc = ngx_conf_parse(cf, NULL);\n    if (rc != NGX_CONF_OK) {\n        return rc;\n    }\n\n    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);\n\n    va_prev = cmcf->variables;\n    vh_prev = cmcf->variables_hash;\n\n    ngx_memzero(&va, sizeof(va));\n    ngx_memzero(&vh, sizeof(vh));\n    ngx_memzero(&vk, sizeof(vk));\n\n    cmcf->variables      = va;\n    cmcf->variables_hash = vh;\n    cmcf->variables_keys = &vk;\n\n    v = va_prev.elts;\n    for (i = 0; i < va_prev.nelts; i++) {\n\n        if (v[i].get_handler) {\n            continue;\n        }\n\n        s.len = v[i].name.len;\n        s.data = ngx_pstrdup(ngx_cycle->pool, &v[i].name);\n        if (!s.data) {\n            rc = NGX_CONF_ERROR;\n            break;\n        }\n\n        /*\n         * variable name will be assign to cmcf->variables[idx].name directly\n         * so the lifetime of v[i].name should be the same as cmcf\n         */\n        v[i].name = s;\n\n        cmcf->variables.elts = &v[i];\n        cmcf->variables.nelts = 1;\n        if (ngx_http_variables_init_vars(cf) != NGX_OK) {\n            rc = NGX_CONF_ERROR;\n            break;\n        }\n    }\n\n    cmcf->variables      = va_prev;\n    cmcf->variables_hash = vh_prev;\n    cmcf->variables_keys = NULL;\n\n    return rc;\n}\n\n\nstatic ngx_int_t\nngx_dyups_add_server(ngx_http_dyups_srv_conf_t *duscf, ngx_buf_t *buf)\n{\n    ngx_conf_t                           cf;\n    ngx_http_upstream_init_pt            init;\n    ngx_http_upstream_srv_conf_t        *uscf;\n    ngx_http_dyups_upstream_srv_conf_t  *dscf;\n\n    uscf = duscf->upstream;\n\n    if (uscf->servers == NULL) {\n        uscf->servers = ngx_array_create(duscf->pool, 4,\n                                         sizeof(ngx_http_upstream_server_t));\n        if (uscf->servers == NULL) {\n            return NGX_ERROR;\n        }\n    }\n\n    ngx_memzero(&cf, sizeof(ngx_conf_t));\n    cf.name = \"dyups_init_module_conf\";\n    cf.pool = duscf->pool;\n    cf.cycle = (ngx_cycle_t *) ngx_cycle;\n    cf.module_type = NGX_HTTP_MODULE;\n    cf.cmd_type = NGX_HTTP_UPS_CONF;\n    cf.log = ngx_cycle->log;\n    cf.ctx = duscf->ctx;\n    cf.args = ngx_array_create(duscf->pool, 10, sizeof(ngx_str_t));\n    if (cf.args == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (ngx_dyups_parse_upstream(&cf, buf) != NGX_CONF_OK) {\n        return NGX_ERROR;\n    }\n\n    ngx_memzero(&cf, sizeof(ngx_conf_t));\n    cf.name = \"dyups_init_upstream\";\n    cf.cycle = (ngx_cycle_t *) ngx_cycle;\n    cf.pool = duscf->pool;\n    cf.module_type = NGX_HTTP_MODULE;\n    cf.cmd_type = NGX_HTTP_MAIN_CONF;\n    cf.log = ngx_cycle->log;\n    cf.ctx = duscf->ctx;\n\n\n    init = uscf->peer.init_upstream ? uscf->peer.init_upstream:\n        ngx_http_upstream_init_round_robin;\n\n    if (init(&cf, uscf) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n#if (T_NGX_HTTP_UPSTREAM_RANDOM)\n    {\n\n    ngx_http_upstream_rr_peers_t        *peers, *backup;\n\n    /* add init_number initialization */\n\n    peers = uscf->peer.data;\n    peers->init_number = ngx_random() % peers->number;\n    backup = peers->next;\n\n    if (backup) {\n        backup->init_number = ngx_random() % backup->number;\n    }\n\n    }\n#endif\n\n    dscf = uscf->srv_conf[ngx_http_dyups_module.ctx_index];\n    dscf->init = uscf->peer.init;\n\n    uscf->peer.init = ngx_http_dyups_init_peer;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_http_dyups_srv_conf_t *\nngx_dyups_find_upstream(ngx_str_t *name, ngx_int_t *idx)\n{\n    ngx_uint_t                      i;\n    ngx_http_dyups_srv_conf_t      *duscfs, *duscf, *duscf_del;\n    ngx_http_dyups_main_conf_t     *dumcf;\n    ngx_http_upstream_srv_conf_t   *uscf;\n\n    dumcf = ngx_http_cycle_get_module_main_conf(ngx_cycle,\n                                                ngx_http_dyups_module);\n    *idx = -1;\n    duscf_del = NULL;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                   \"[dyups] find dynamic upstream\");\n\n    duscfs = dumcf->dy_upstreams.elts;\n    for (i = 0; i < dumcf->dy_upstreams.nelts; i++) {\n\n        duscf = &duscfs[i];\n        if (!duscf->dynamic) {\n            continue;\n        }\n\n        if (duscf->deleted == NGX_DYUPS_DELETING) {\n\n            ngx_log_error(NGX_LOG_DEBUG, ngx_cycle->log, 0,\n                          \"[dyups] find upstream idx: %ui ref: %ui \"\n                          \"on %V deleting\",\n                          i, *(duscf->ref), &duscf->upstream->host);\n\n            if (*(duscf->ref) == 0) {\n                ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, 0,\n                              \"[dyups] free dynamic upstream in find upstream\"\n                              \" %ui\", duscf->idx);\n\n                duscf->deleted = NGX_DYUPS_DELETED;\n\n                if (duscf->pool) {\n                    ngx_destroy_pool(duscf->pool);\n                    duscf->pool = NULL;\n                }\n            }\n        }\n\n        if (duscf->deleted == NGX_DYUPS_DELETING) {\n            continue;\n        }\n\n        if (duscf->deleted == NGX_DYUPS_DELETED) {\n            *idx = i;\n            duscf_del = duscf;\n            continue;\n        }\n\n        uscf = duscf->upstream;\n\n        if (uscf->host.len != name->len\n            || ngx_strncasecmp(uscf->host.data, name->data, uscf->host.len)\n               != 0)\n        {\n            continue;\n        }\n\n        *idx = i;\n\n        return duscf;\n    }\n\n    return duscf_del;\n}\n\n\nstatic ngx_int_t\nngx_dyups_init_upstream(ngx_http_dyups_srv_conf_t *duscf, ngx_str_t *name,\n    ngx_uint_t index)\n{\n    ngx_uint_t                           mi, m;\n    ngx_conf_t                           cf;\n    ngx_module_t                        **modules;\n    ngx_http_module_t                   *module;\n    ngx_http_conf_ctx_t                 *ctx;\n    ngx_http_upstream_srv_conf_t        *uscf, **uscfp;\n    ngx_http_upstream_main_conf_t       *umcf;\n    ngx_http_dyups_upstream_srv_conf_t  *dscf;\n\n    umcf = ngx_http_cycle_get_module_main_conf(ngx_cycle,\n                                               ngx_http_upstream_module);\n    uscfp = umcf->upstreams.elts;\n\n    duscf->pool = ngx_create_pool(512, ngx_cycle->log);\n    if (duscf->pool == NULL) {\n        return NGX_ERROR;\n    }\n\n    uscf = ngx_pcalloc(duscf->pool, sizeof(ngx_http_upstream_srv_conf_t));\n    if (uscf == NULL) {\n        return NGX_ERROR;\n    }\n\n    uscf->flags = NGX_HTTP_UPSTREAM_CREATE\n                 |NGX_HTTP_UPSTREAM_WEIGHT\n#ifdef NGX_HTTP_UPSTREAM_MAX_CONNS\n                 |NGX_HTTP_UPSTREAM_MAX_CONNS\n#endif\n                 |NGX_HTTP_UPSTREAM_MAX_FAILS\n                 |NGX_HTTP_UPSTREAM_FAIL_TIMEOUT\n                 |NGX_HTTP_UPSTREAM_DOWN\n                 |NGX_HTTP_UPSTREAM_BACKUP;\n\n    uscf->host.data = ngx_pstrdup(duscf->pool, name);\n    if (uscf->host.data == NULL) {\n        return NGX_ERROR;\n    }\n\n    uscf->host.len = name->len;\n    uscf->file_name = (u_char *) \"dynamic_upstream\";\n    uscf->line = 0;\n    uscf->port = 0;\n#if nginx_version < 1011006\n    uscf->default_port = 0;\n#endif\n    uscfp[index] = uscf;\n\n    duscf->dynamic = 1;\n    duscf->upstream = uscf;\n\n    ngx_memzero(&cf, sizeof(ngx_conf_t));\n    cf.module_type = NGX_HTTP_MODULE;\n    cf.cmd_type = NGX_HTTP_MAIN_CONF;\n    cf.pool = duscf->pool;\n    cf.ctx = ngx_cycle->conf_ctx[ngx_http_module.index];\n    cf.cycle = (ngx_cycle_t *) ngx_cycle;\n\n    ctx = ngx_pcalloc(duscf->pool, sizeof(ngx_http_conf_ctx_t));\n    if (ctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    ctx->main_conf = ((ngx_http_conf_ctx_t *)\n                      ngx_cycle->conf_ctx[ngx_http_module.index])->main_conf;\n\n    ctx->srv_conf = ngx_pcalloc(cf.pool, sizeof(void *) * ngx_http_max_module);\n    if (ctx->srv_conf == NULL) {\n        return NGX_ERROR;\n    }\n\n    ctx->srv_conf[ngx_http_upstream_module.ctx_index] = uscf;\n    uscf->srv_conf = ctx->srv_conf;\n\n#if nginx_version >= 1009011\n    modules = ngx_cycle->modules;\n#else\n    modules = ngx_modules;\n#endif\n\n    for (m = 0; modules[m]; m++) {\n        if (modules[m]->type != NGX_HTTP_MODULE) {\n            continue;\n        }\n\n        if (modules[m]->index == ngx_http_core_module.index) {\n            continue;\n        }\n\n        module = modules[m]->ctx;\n        mi = modules[m]->ctx_index;\n\n        if (module->create_srv_conf) {\n            ctx->srv_conf[mi] = module->create_srv_conf(&cf);\n            if (ctx->srv_conf[mi] == NULL) {\n                return NGX_ERROR;\n            }\n        }\n    }\n\n    dscf = uscf->srv_conf[ngx_http_dyups_module.ctx_index];\n    duscf->ref = &dscf->ref;\n    duscf->ctx = ctx;\n    duscf->deleted = 0;\n\n    ngx_dyups_add_upstream_top_filter(umcf, uscf);\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_dyups_mark_upstream_delete(ngx_http_dyups_srv_conf_t *duscf)\n{\n    ngx_uint_t                      i;\n    ngx_http_upstream_server_t     *us;\n    ngx_http_upstream_srv_conf_t   *uscf, **uscfp;\n    ngx_http_upstream_main_conf_t  *umcf;\n\n    uscf = duscf->upstream;\n    umcf = ngx_http_cycle_get_module_main_conf(ngx_cycle,\n                                               ngx_http_upstream_module);\n    uscfp = umcf->upstreams.elts;\n\n    ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, 0,\n                  \"[dyups] delete upstream \\\"%V\\\"\", &duscf->upstream->host);\n\n    ngx_dyups_del_upstream_top_filter(umcf, uscf);\n\n    us = uscf->servers->elts;\n    for (i = 0; i < uscf->servers->nelts; i++) {\n        us[i].down = 1;\n\n#if (NGX_HTTP_UPSTREAM_CHECK)\n        ngx_uint_t  j;\n\n        for (j = 0; j < us[i].naddrs; j++) {\n            ngx_http_upstream_check_delete_dynamic_peer(&uscf->host,\n                                                        &us[i].addrs[j]);\n        }\n#endif\n    }\n\n#if (NGX_HTTP_SSL)\n    if (uscf->peer.data) {\n        ngx_http_dyups_free_peer_sessions(uscf->peer.data);\n    }\n#endif\n\n    uscfp[duscf->idx] = &ngx_http_dyups_deleted_upstream;\n    duscf->deleted = NGX_DYUPS_DELETING;\n}\n\n\nstatic void\nngx_http_dyups_send_response(ngx_http_request_t *r, ngx_int_t status,\n    ngx_str_t *content)\n{\n    ngx_int_t    rc;\n    ngx_buf_t   *b;\n    ngx_chain_t  out;\n\n    r->headers_out.status = status;\n    r->headers_out.content_length_n = content->len;\n\n    rc = ngx_http_send_header(r);\n    if (rc == NGX_ERROR || rc > NGX_OK) {\n        ngx_http_finalize_request(r, rc);\n        return;\n    }\n\n    if (content->len == 0) {\n        ngx_http_finalize_request(r, ngx_http_send_special(r, NGX_HTTP_FLUSH));\n        return;\n    }\n\n    b = ngx_create_temp_buf(r->pool, content->len);\n    if (b == NULL) {\n        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return;\n    }\n\n    b->pos = content->data;\n    b->last = content->data + content->len;\n    b->last_buf = 1;\n\n    out.buf = b;\n    out.next = NULL;\n\n    ngx_http_finalize_request(r, ngx_http_output_filter(r, &out));\n}\n\n\nstatic ngx_buf_t *\nngx_http_dyups_read_body(ngx_http_request_t *r)\n{\n    size_t        len;\n    ngx_buf_t    *buf, *next, *body;\n    ngx_chain_t  *cl;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"[dyups] interface read post body\");\n\n    cl = r->request_body->bufs;\n    buf = cl->buf;\n\n    if (cl->next == NULL) {\n\n        return buf;\n\n    } else {\n\n        next = cl->next->buf;\n        len = (buf->last - buf->pos) + (next->last - next->pos);\n\n        body = ngx_create_temp_buf(r->pool, len);\n        if (body == NULL) {\n            return NULL;\n        }\n\n        body->last = ngx_cpymem(body->last, buf->pos, buf->last - buf->pos);\n        body->last = ngx_cpymem(body->last, next->pos, next->last - next->pos);\n    }\n\n    return body;\n}\n\n\nstatic ngx_buf_t *\nngx_http_dyups_read_body_from_file(ngx_http_request_t *r)\n{\n    size_t        len;\n    ssize_t       size;\n    ngx_buf_t    *buf, *body;\n    ngx_chain_t  *cl;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"[dyups] interface read post body from file\");\n\n    len = 0;\n    cl = r->request_body->bufs;\n\n    while (cl) {\n\n        buf = cl->buf;\n\n        if (buf->in_file) {\n            len += buf->file_last - buf->file_pos;\n\n        } else {\n            len += buf->last - buf->pos;\n        }\n\n        cl = cl->next;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"[dyups] interface read post body file size %ui\", len);\n\n    body = ngx_create_temp_buf(r->pool, len);\n    if (body == NULL) {\n        return NULL;\n    }\n\n    cl = r->request_body->bufs;\n\n    while (cl) {\n\n        buf = cl->buf;\n\n        if (buf->in_file) {\n\n            size = ngx_read_file(buf->file, body->last,\n                                 buf->file_last - buf->file_pos, buf->file_pos);\n\n            if (size == NGX_ERROR) {\n                return NULL;\n            }\n\n            body->last += size;\n\n        } else {\n\n            body->last = ngx_cpymem(body->last, buf->pos, buf->last - buf->pos);\n        }\n\n        cl = cl->next;\n    }\n\n    return body;\n}\n\n\nngx_array_t *\nngx_dyups_parse_path(ngx_pool_t *pool, ngx_str_t *path)\n{\n    u_char       *p, *last, *end;\n    ngx_str_t    *str;\n    ngx_array_t  *array;\n\n    array = ngx_array_create(pool, 8, sizeof(ngx_str_t));\n    if (array == NULL) {\n        return NULL;\n    }\n\n    p = path->data + 1;\n    last = path->data + path->len;\n\n    while(p < last) {\n        end = ngx_strlchr(p, last, '/');\n        str = ngx_array_push(array);\n\n        if (str == NULL) {\n            return NULL;\n        }\n\n        if (end) {\n            str->data = p;\n            str->len = end - p;\n\n        } else {\n            str->data = p;\n            str->len = last - p;\n\n        }\n\n        p += str->len + 1;\n    }\n\n#if (NGX_DEBUG)\n    ngx_str_t  *arg;\n    ngx_uint_t  i;\n\n    arg = array->elts;\n    for (i = 0; i < array->nelts; i++) {\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                       \"[dyups] res[%i]:%V\", i, &arg[i]);\n    }\n#endif\n\n    return array;\n}\n\n\nstatic ngx_int_t\nngx_http_dyups_init_peer(ngx_http_request_t *r,\n    ngx_http_upstream_srv_conf_t *us)\n{\n    ngx_int_t                            rc;\n    ngx_pool_cleanup_t                  *cln;\n    ngx_http_dyups_ctx_t                *ctx;\n    ngx_http_dyups_upstream_srv_conf_t  *dscf;\n\n    dscf = us->srv_conf[ngx_http_dyups_module.ctx_index];\n\n    rc = dscf->init(r, us);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                   \"[dyups] dynamic upstream init peer: %i\",\n                   rc);\n\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_dyups_ctx_t));\n    if (ctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    ctx->scf = dscf;\n    ctx->data = r->upstream->peer.data;\n    ctx->get = r->upstream->peer.get;\n    ctx->free = r->upstream->peer.free;\n\n    r->upstream->peer.data = ctx;\n    r->upstream->peer.get = ngx_http_dyups_get_peer;\n    r->upstream->peer.free = ngx_http_dyups_free_peer;\n\n#if (NGX_HTTP_SSL)\n    ctx->original_set_session = r->upstream->peer.set_session;\n    ctx->original_save_session = r->upstream->peer.save_session;\n    r->upstream->peer.set_session = ngx_http_dyups_set_peer_session;\n    r->upstream->peer.save_session = ngx_http_dyups_save_peer_session;\n#endif\n\n    cln = ngx_pool_cleanup_add(r->pool, 0);\n    if (cln == NULL) {\n        return NGX_ERROR;\n    }\n\n    dscf->ref++;\n\n    cln->handler = ngx_http_dyups_clean_request;\n    cln->data = &dscf->ref;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_dyups_get_peer(ngx_peer_connection_t *pc, void *data)\n{\n    ngx_http_dyups_ctx_t  *ctx = data;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                   \"[dyups] dynamic upstream get handler count %i\",\n                   ctx->scf->ref);\n\n    return ctx->get(pc, ctx->data);\n}\n\n\nstatic void\nngx_http_dyups_free_peer(ngx_peer_connection_t *pc, void *data,\n    ngx_uint_t state)\n{\n    ngx_http_dyups_ctx_t  *ctx = data;\n\n    ngx_pool_cleanup_t  *cln;\n\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                   \"[dyups] dynamic upstream free handler count %i\",\n                   ctx->scf->ref);\n\n    /* upstream connect failed */\n    if (pc->connection == NULL) {\n        goto done;\n    }\n\n    if (pc->cached) {\n        goto done;\n    }\n\n    ctx->scf->ref++;\n\n    cln = ngx_pool_cleanup_add(pc->connection->pool, 0);\n    if (cln == NULL) {\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                      \"[dyups] dynamic upstream free peer may cause memleak %i\",\n                      ctx->scf->ref);\n        goto done;\n    }\n\n    cln->handler = ngx_http_dyups_clean_request;\n    cln->data = &ctx->scf->ref;\n\n done:\n\n    ctx->free(pc, ctx->data, state);\n}\n\n\nstatic void\nngx_http_dyups_clean_request(void *data)\n{\n    ngx_uint_t  *ref = data;\n\n    (*ref)--;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                   \"[dyups] http clean request count %i\", *ref);\n}\n\n\nstatic void\nngx_http_dyups_read_msg(ngx_event_t *ev)\n{\n    ngx_uint_t                   i, count, s_count, d_count;\n    ngx_slab_pool_t             *shpool;\n    ngx_http_dyups_srv_conf_t   *duscfs, *duscf;\n    ngx_http_dyups_main_conf_t  *dmcf;\n\n    dmcf = ev->data;\n    shpool = ngx_dyups_global_ctx.shpool;\n\n    count = 0;\n    s_count = 0;\n    d_count = 0;\n\n    duscfs = dmcf->dy_upstreams.elts;\n    for (i = 0; i < dmcf->dy_upstreams.nelts; i++) {\n\n        duscf = &duscfs[i];\n\n        if (!duscf->dynamic) {\n            s_count++;\n            continue;\n        }\n\n        if (duscf->deleted) {\n            d_count++;\n            continue;\n        }\n\n        count++;\n    }\n\n    if (dmcf->read_msg_log == 1) {\n        ngx_log_error(NGX_LOG_INFO, ev->log, 0,\n                      \"[dyups] has %ui upstreams, %ui static, %ui deleted, all %ui\",\n                      count, s_count, d_count, dmcf->dy_upstreams.nelts);\n    }\n\n#if (NGX_HTTP_UPSTREAM_CHECK)\n    if (!ngx_shmtx_trylock(&shpool->mutex)) {\n        goto finish;\n    }\n#else\n    ngx_shmtx_lock(&shpool->mutex);\n#endif\n\n    ngx_http_dyups_read_msg_locked(ev);\n\n    ngx_shmtx_unlock(&shpool->mutex);\n\n#if (NGX_HTTP_UPSTREAM_CHECK)\nfinish:\n#endif\n    ngx_dyups_add_timer(ev, dmcf->read_msg_timeout);\n}\n\n\nstatic void\nngx_http_dyups_read_msg_locked(ngx_event_t *ev)\n{\n    ngx_int_t            i, rc;\n    ngx_str_t            name, content;\n    ngx_flag_t           found;\n    ngx_time_t          *tp;\n    ngx_pool_t          *pool;\n    ngx_msec_t           now;\n    ngx_queue_t         *q, *t;\n    ngx_core_conf_t     *ccf;\n    ngx_slab_pool_t     *shpool;\n    ngx_dyups_msg_t     *msg;\n    ngx_dyups_shctx_t   *sh;\n    ngx_dyups_status_t  *status;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ev->log, 0,\n                   \"[dyups] read msg %P\", ngx_pid);\n\n    ccf = (ngx_core_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,\n                                           ngx_core_module);\n\n    sh = ngx_dyups_global_ctx.sh;\n    shpool = ngx_dyups_global_ctx.shpool;\n\n    tp = ngx_timeofday();\n    now = (ngx_msec_t) (tp->sec * 1000 + tp->msec);\n\n    for (i = 0; i < ccf->worker_processes; i++) {\n        status = &sh->status[i];\n\n        if (status->pid == 0 || status->pid == ngx_pid) {\n\n            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ev->log, 0,\n                           \"[dyups] process %P update time %ui\",\n                           status->pid, status->time);\n\n            status->pid = ngx_pid;\n            status->time = now;\n            break;\n        }\n    }\n\n    if (ngx_queue_empty(&sh->msg_queue)) {\n        return;\n    }\n\n    pool = ngx_create_pool(ngx_pagesize, ev->log);\n    if (pool == NULL) {\n        return;\n    }\n\n    for (q = ngx_queue_last(&sh->msg_queue);\n         q != ngx_queue_sentinel(&sh->msg_queue);\n         q = ngx_queue_prev(q))\n    {\n        msg = ngx_queue_data(q, ngx_dyups_msg_t, queue);\n\n        if (msg->count == ccf->worker_processes) {\n            t = ngx_queue_next(q); ngx_queue_remove(q); q = t;\n\n            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ev->log, 0,\n                                  \"[dyups] destroy msg %V:%V\",\n                                  &msg->name, &msg->content);\n\n            ngx_dyups_destroy_msg(shpool, msg);\n            continue;\n        }\n\n        found = 0;\n        for (i = 0; i < msg->count; i++) {\n\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ev->log, 0,\n                           \"[dyups] msg pids [%P]\", msg->pid[i]);\n\n            if (msg->pid[i] == ngx_pid) {\n                found = 1;\n                break;\n            }\n        }\n\n        if (found) {\n            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ev->log, 0,\n                           \"[dyups] msg %V count %ui found\",\n                           &msg->name, msg->count);\n            continue;\n        }\n\n        msg->pid[i] = ngx_pid;\n        msg->count++;\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ev->log, 0,\n                       \"[dyups] msg %V count %ui\", &msg->name, msg->count);\n\n        name = msg->name;\n        content = msg->content;\n\n        rc = ngx_dyups_sync_cmd(pool, &name, &content, msg->flag);\n        if (rc != NGX_OK) {\n            ngx_log_error(NGX_LOG_ALERT, ev->log, 0,\n                          \"[dyups] read msg error, may cause the \"\n                          \"config inaccuracy, name:%V, content:%V\",\n                          &name, &content);\n        }\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0,\n                   \"[dyups] read end\");\n\n    ngx_destroy_pool(pool);\n\n    return;\n}\n\n\nstatic ngx_int_t\nngx_http_dyups_send_msg(ngx_str_t *name, ngx_buf_t *body, ngx_uint_t flag)\n{\n    ngx_core_conf_t    *ccf;\n    ngx_slab_pool_t    *shpool;\n    ngx_dyups_msg_t    *msg;\n    ngx_dyups_shctx_t  *sh;\n\n    ccf = (ngx_core_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,\n                                           ngx_core_module);\n\n    sh = ngx_dyups_global_ctx.sh;\n    shpool = ngx_dyups_global_ctx.shpool;\n\n    msg = ngx_slab_alloc_locked(shpool, sizeof(ngx_dyups_msg_t));\n    if (msg == NULL) {\n        goto failed;\n    }\n\n    ngx_memzero(msg, sizeof(ngx_dyups_msg_t));\n\n    msg->flag = flag;\n    msg->count = 0;\n    msg->pid = ngx_slab_alloc_locked(shpool,\n                                     sizeof(ngx_pid_t) * ccf->worker_processes);\n\n    if (msg->pid == NULL) {\n        goto failed;\n    }\n\n    ngx_memzero(msg->pid, sizeof(ngx_pid_t) * ccf->worker_processes);\n    msg->pid[0] = ngx_pid;\n    msg->count++;\n\n    msg->name.data = ngx_slab_alloc_locked(shpool, name->len);\n    if (msg->name.data == NULL) {\n        goto failed;\n    }\n\n    ngx_memcpy(msg->name.data, name->data, name->len);\n    msg->name.len = name->len;\n\n    if (body) {\n        msg->content.data = ngx_slab_alloc_locked(shpool,\n                                                  body->last - body->pos);\n        if (msg->content.data == NULL) {\n            goto failed;\n        }\n\n        ngx_memcpy(msg->content.data, body->pos, body->last - body->pos);\n        msg->content.len = body->last - body->pos;\n\n    } else {\n        msg->content.data = NULL;\n        msg->content.len = 0;\n    }\n\n    sh->version++;\n\n    if (sh->version == 0) {\n        sh->version = 1;\n    };\n\n    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                   \"[dyups] send msg %V count %ui version: %ui\",\n                   &msg->name, msg->count, sh->version);\n\n    ngx_queue_insert_head(&sh->msg_queue, &msg->queue);\n\n    return NGX_OK;\n\nfailed:\n\n    if (msg) {\n        ngx_dyups_destroy_msg(shpool, msg);\n    }\n\n    return NGX_ERROR;\n}\n\n\nstatic void\nngx_dyups_destroy_msg(ngx_slab_pool_t *shpool, ngx_dyups_msg_t *msg)\n{\n    if (msg->pid) {\n        ngx_slab_free_locked(shpool, msg->pid);\n    }\n\n    if (msg->name.data) {\n        ngx_slab_free_locked(shpool, msg->name.data);\n    }\n\n    if (msg->content.data) {\n        ngx_slab_free_locked(shpool, msg->content.data);\n    }\n\n    ngx_slab_free_locked(shpool, msg);\n}\n\n\nstatic ngx_int_t\nngx_dyups_sync_cmd(ngx_pool_t *pool, ngx_str_t *name, ngx_str_t *content,\n    ngx_uint_t flag)\n{\n    ngx_int_t     rc;\n    ngx_buf_t     body;\n    ngx_str_t     rv;\n\n    if (flag == NGX_DYUPS_DELETE) {\n\n        rc = ngx_dyups_do_delete(name, &rv);\n\n        ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, 0,\n                       \"[dyups] sync del: %V rv: %V rc: %i\",\n                       name, &rv, rc);\n\n        if (rc != NGX_HTTP_OK) {\n            return NGX_ERROR;\n        }\n\n        return NGX_OK;\n\n    } else if (flag == NGX_DYUPS_ADD) {\n\n        body.start = body.pos = content->data;\n        body.end = body.last = content->data + content->len;\n        body.temporary = 1;\n\n        rc = ngx_dyups_do_update(name, &body, &rv);\n\n        ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, 0,\n                      \"[dyups] sync add: %V rv: %V rc: %i\",\n                      name, &rv, rc);\n\n        if (rc != NGX_HTTP_OK) {\n            return NGX_ERROR;\n        }\n\n        return NGX_OK;\n    }\n\n    return NGX_ERROR;\n}\n\n\n#if (NGX_HTTP_SSL)\n\nstatic ngx_int_t\nngx_http_dyups_set_peer_session(ngx_peer_connection_t *pc, void *data)\n{\n    ngx_http_dyups_ctx_t  *ctx = data;\n\n    return ctx->original_set_session(pc, ctx->data);\n}\n\n\nstatic void\nngx_http_dyups_save_peer_session(ngx_peer_connection_t *pc, void *data)\n{\n    ngx_http_dyups_ctx_t  *ctx = data;\n\n    ctx->original_save_session(pc, ctx->data);\n    return;\n}\n\nstatic void\nngx_http_dyups_free_peer_session(void *data)\n{\n    ngx_ssl_session_t               *old_ssl_session;\n    ngx_http_upstream_rr_peer_t     *peer = data;\n\n    old_ssl_session = peer->ssl_session;\n    peer->ssl_session = NULL;\n\n    if (old_ssl_session) {\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                       \"[dyups] free old session: %p\", old_ssl_session);\n        ngx_ssl_free_session(old_ssl_session);\n    }\n}\n\n\nstatic void\nngx_http_dyups_free_peer_sessions(void *data)\n{\n    ngx_http_upstream_rr_peer_t     *peer;\n    ngx_http_upstream_rr_peers_t    *peers = data;\n\n    if (peers->single) {\n        ngx_http_dyups_free_peer_session(peers->peer);\n\n    } else {\n\n        for (peer = peers->peer; peer; peer = peer->next) {\n            ngx_http_dyups_free_peer_session(peer);\n        }\n    }\n\n    if (peers->next) {\n        ngx_http_dyups_free_peer_sessions(peers->next);\n    }\n}\n\n#endif\n\n\nstatic ngx_int_t\nngx_dyups_add_upstream_filter(ngx_http_upstream_main_conf_t *umcf,\n    ngx_http_upstream_srv_conf_t *uscf)\n{\n#if (NGX_HTTP_UPSTREAM_RBTREE)\n    uscf->node.key = ngx_crc32_short(uscf->host.data, uscf->host.len);\n    ngx_rbtree_insert(&umcf->rbtree, &uscf->node);\n#endif\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_dyups_del_upstream_filter(ngx_http_upstream_main_conf_t *umcf,\n    ngx_http_upstream_srv_conf_t *uscf)\n{\n#if (NGX_HTTP_UPSTREAM_RBTREE)\n    ngx_rbtree_delete(&umcf->rbtree, &uscf->node);\n#endif\n    return NGX_OK;\n}\n\n\nstatic char *\nngx_http_dyups_cmd_deprecated(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                       \"invalid directive \\\"%V\\\" of ngx_http_dyups_module, \"\n                       \"it has been deprecated\", &cmd->name);\n\n    return NGX_CONF_OK;\n}\n"
  },
  {
    "path": "modules/ngx_http_upstream_iwrr_module/config",
    "content": "ngx_addon_name=ngx_http_upstream_iwrr_module\nHTTP_UPSTREAM_IWRR_SRCS=\"$ngx_addon_dir/ngx_http_upstream_iwrr_module.c\"\n\nif test -n \"$ngx_module_link\"; then\n    ngx_module_type=HTTP\n    ngx_module_name=$ngx_addon_name\n    ngx_module_deps=\n    ngx_module_srcs=\"$HTTP_UPSTREAM_IWRR_SRCS\"\n\n    . auto/module\nelse\n    HTTP_MODULES=\"$HTTP_MODULES ngx_http_upstream_iwrr_module\"\n    NGX_ADDON_SRCS=\"$NGX_ADDON_SRCS $HTTP_UPSTREAM_IWRR_SRCS\"\nfi"
  },
  {
    "path": "modules/ngx_http_upstream_iwrr_module/ngx_http_upstream_iwrr_module.c",
    "content": "\n/*\n * Copyright (C) 2010-2023 Alibaba Group Holding Limited\n * Copyright (C) 2010-2023 Zhuozhi Ji (jizhuozhi.george@gmail.com)\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n#if (NGX_HTTP_UPSTREAM_CHECK)\n#include \"ngx_http_upstream_check_module.h\"\n#endif\n\ntypedef struct {\n    ngx_queue_t                     queue;\n    ngx_uint_t                      index;\n    ngx_uint_t                      weight;\n    ngx_uint_t                      remainder;\n    ngx_http_upstream_rr_peer_t    *peer;\n} ngx_http_upstream_iwrr_queue_t;\n\ntypedef struct ngx_http_upstream_iwrr_srv_conf_s ngx_http_upstream_iwrr_srv_conf_t;\n\nstruct ngx_http_upstream_iwrr_srv_conf_s {\n    ngx_uint_t                             init_number;\n    ngx_http_upstream_iwrr_queue_t        *active;\n    ngx_http_upstream_iwrr_queue_t        *expired;\n    ngx_http_upstream_iwrr_srv_conf_t     *next;\n};\n\ntypedef struct {\n    /* the round robin data must be first */\n    ngx_http_upstream_rr_peer_data_t    rrp;\n\n    ngx_http_upstream_iwrr_srv_conf_t  *uiscf;\n} ngx_http_upstream_iwrr_peer_data_t;\n\nstatic char *ngx_http_upstream_iwrr(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic void *ngx_http_upstream_iwrr_create_srv_conf(ngx_conf_t *cf);\nstatic ngx_int_t ngx_http_upstream_init_iwrr(ngx_conf_t *cf,\n    ngx_http_upstream_srv_conf_t *us);\nstatic ngx_int_t ngx_http_upstream_init_iwrr_peer(ngx_http_request_t *r, \n    ngx_http_upstream_srv_conf_t *us);\nstatic ngx_int_t ngx_http_upstream_get_iwrr_peer(ngx_peer_connection_t *pc,\n    void *data);\nstatic ngx_http_upstream_rr_peer_t *ngx_http_upstream_get_iwrr(\n    ngx_http_upstream_iwrr_peer_data_t *uip);\nstatic ngx_http_upstream_iwrr_queue_t *ngx_http_upstream_iwrr_queue_next(\n    ngx_http_upstream_iwrr_srv_conf_t *uiscf);\n\nstatic inline ngx_uint_t ngx_http_upstream_iwrr_gcd(ngx_uint_t a, ngx_uint_t b);\n\nstatic ngx_command_t  ngx_http_upstream_iwrr_commands[] = {\n\n    { ngx_string(\"iwrr\"),\n      NGX_HTTP_UPS_CONF|NGX_CONF_NOARGS,\n      ngx_http_upstream_iwrr,\n      0,\n      0,\n      NULL },\n    \n      ngx_null_command\n};\n\nstatic ngx_http_module_t  ngx_http_upstream_iwrr_module_ctx = {\n    NULL,                                      /* preconfiguration */\n    NULL,                                      /* postconfiguration */\n\n    NULL,                                      /* create main configuration */\n    NULL,                                      /* init main configuration */\n\n    ngx_http_upstream_iwrr_create_srv_conf,    /* create server configuration */\n    NULL,                                      /* merge server configuration */\n\n    NULL,                                      /* create location configuration */\n    NULL                                       /* merge location configuration */\n};\n\nngx_module_t  ngx_http_upstream_iwrr_module = {\n    NGX_MODULE_V1,\n    &ngx_http_upstream_iwrr_module_ctx,    /* module context */\n    ngx_http_upstream_iwrr_commands,       /* 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\nstatic char *\nngx_http_upstream_iwrr(ngx_conf_t *cf, ngx_command_t *cmd, \n    void *conf)\n{\n    ngx_http_upstream_srv_conf_t            *uscf;\n\n    uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);\n\n    if (uscf->peer.init_upstream) {\n        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                           \"load balancing method redefined\");\n    }\n\n    uscf->peer.init_upstream = ngx_http_upstream_init_iwrr;\n\n    uscf->flags = NGX_HTTP_UPSTREAM_CREATE\n                  |NGX_HTTP_UPSTREAM_WEIGHT\n                  |NGX_HTTP_UPSTREAM_BACKUP\n                  |NGX_HTTP_UPSTREAM_MAX_FAILS\n                  |NGX_HTTP_UPSTREAM_FAIL_TIMEOUT\n#if defined(nginx_version) && nginx_version >= 1011005\n                  |NGX_HTTP_UPSTREAM_MAX_CONNS\n#endif\n                  |NGX_HTTP_UPSTREAM_DOWN;\n    \n    return NGX_CONF_OK;\n}\n\nstatic void *\nngx_http_upstream_iwrr_create_srv_conf(ngx_conf_t *cf) \n{\n    ngx_http_upstream_iwrr_srv_conf_t     *uiscf;\n\n    uiscf = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_iwrr_srv_conf_t));\n    if (uiscf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc():\n     *\n     *     uiscf->active = NULL;\n     *     uiscf->expired = NULL;\n     *     uiscf->next = NULL;\n     */\n\n    uiscf->init_number = NGX_CONF_UNSET_UINT;\n\n    return uiscf;\n}\n\nstatic ngx_int_t\nngx_http_upstream_init_iwrr(ngx_conf_t *cf,\n    ngx_http_upstream_srv_conf_t *us)\n{\n    ngx_http_upstream_rr_peers_t          *peers;\n    ngx_http_upstream_rr_peer_t           *peer;\n    ngx_http_upstream_iwrr_srv_conf_t     *uiscf;\n    ngx_http_upstream_iwrr_queue_t        *item;\n    ngx_uint_t                             i, g;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cf->log, 0, \"init iwrr\");\n\n    if (ngx_http_upstream_init_round_robin(cf, us) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    us->peer.init = ngx_http_upstream_init_iwrr_peer;\n\n    uiscf = ngx_http_conf_upstream_srv_conf(us, ngx_http_upstream_iwrr_module);\n\n    peers = (ngx_http_upstream_rr_peers_t *) us->peer.data;\n\n    for (peers = (ngx_http_upstream_rr_peers_t *) us->peer.data;\n         peers;\n         peers = peers->next)\n    {\n\n        g = 0;\n\n        for (peer = peers->peer;\n             peer;\n             peer = peer->next)\n        {\n            g = ngx_http_upstream_iwrr_gcd(g, peer->weight);\n        }\n\n        uiscf->active = ngx_palloc(cf->pool, sizeof(ngx_http_upstream_iwrr_queue_t));\n        if (uiscf->active == NULL) {\n            return NGX_ERROR;\n        }\n        ngx_queue_init(&uiscf->active->queue);\n\n        uiscf->expired = ngx_palloc(cf->pool, sizeof(ngx_http_upstream_iwrr_queue_t));\n        if (uiscf->active == NULL) {\n            return NGX_ERROR;\n        }\n        ngx_queue_init(&uiscf->expired->queue);\n\n        for (peer = peers->peer, i = 0;\n             peer;\n             peer = peer->next, i++)\n        {\n\n            item = ngx_palloc(cf->pool, sizeof(ngx_http_upstream_iwrr_queue_t));\n            if (item == NULL) {\n                return NGX_ERROR;\n            }\n\n            item->index = i;\n            item->weight = peer->weight / g;\n            item->remainder = item->weight;\n            item->peer = peer;\n            ngx_queue_insert_tail(&uiscf->active->queue, &item->queue);\n        }\n\n        if (peers->next) {\n            uiscf->next = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_iwrr_srv_conf_t));\n            if (uiscf->next == NULL) {\n                return NGX_ERROR;\n            }\n\n            uiscf->next->init_number = NGX_CONF_UNSET_UINT;\n\n            uiscf = uiscf->next;\n        }\n    }\n\n    return NGX_OK;\n}\n\nstatic ngx_int_t\nngx_http_upstream_init_iwrr_peer(ngx_http_request_t *r,\n    ngx_http_upstream_srv_conf_t *us)\n{\n    ngx_http_upstream_iwrr_srv_conf_t     *uiscf;\n    ngx_http_upstream_iwrr_peer_data_t    *uip;\n\n    uiscf = ngx_http_conf_upstream_srv_conf(us, ngx_http_upstream_iwrr_module);\n    if (uiscf == NULL) {\n        return NGX_ERROR;\n    }\n\n    uip = ngx_palloc(r->pool, sizeof(ngx_http_upstream_iwrr_peer_data_t));\n    if (uip == NULL) {\n        return NGX_ERROR;\n    }\n\n    uip->uiscf = uiscf;\n    r->upstream->peer.data = &uip->rrp;\n\n    if (ngx_http_upstream_init_round_robin_peer(r, us) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    r->upstream->peer.get = ngx_http_upstream_get_iwrr_peer;\n\n    return NGX_OK;\n}\n\nstatic ngx_int_t\nngx_http_upstream_get_iwrr_peer(ngx_peer_connection_t *pc, void *data)\n{\n    ngx_http_upstream_iwrr_peer_data_t    *uip = data;\n\n    ngx_int_t                                rc;\n    ngx_uint_t                               i, n;\n    ngx_http_upstream_rr_peer_t             *peer;\n    ngx_http_upstream_rr_peers_t            *peers;\n    ngx_http_upstream_rr_peer_data_t        *rrp;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                    \"get iwrr peer, try: %ui\", pc->tries);\n\n    pc->cached = 0;\n    pc->connection = NULL;\n\n    rrp = &uip->rrp;\n\n    peers = rrp->peers;\n    ngx_http_upstream_rr_peers_wlock(peers);\n\n    if (peers->single) {\n        peer = peers->peer;\n\n        if (peer->down) {\n            goto failed;\n        }\n\n        if (peer->max_conns && peer->conns >= peer->max_conns) {\n            goto failed;\n        }\n\n#if (NGX_HTTP_UPSTREAM_CHECK)\n        if (ngx_http_upstream_check_peer_down(peer->check_index)) {\n            goto failed;\n        }\n#endif\n        rrp->current = peer;\n\n    } else {\n\n        /* there are several peers */\n\n        peer = ngx_http_upstream_get_iwrr(uip);\n\n        if (peer == NULL) {\n            goto failed;\n        }\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                       \"get iwrr peer, current: %p %i\",\n                       peer, peer->current_weight);\n    }\n    \n    pc->sockaddr = peer->sockaddr;\n    pc->socklen = peer->socklen;\n    pc->name = &peer->name;\n#if (T_NGX_HTTP_DYNAMIC_RESOLVE)\n    pc->host = &peer->host;\n#endif\n\n    peer->conns++;\n\n    ngx_http_upstream_rr_peers_unlock(peers);\n\n    return NGX_OK;\n\n\nfailed:\n\n    if (peers->next) {\n\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0, \"backup servers\");\n\n        rrp->peers = peers->next;\n\n        uip->uiscf = uip->uiscf ? uip->uiscf->next : uip->uiscf;\n\n        n = (rrp->peers->number + (8 * sizeof(uintptr_t) - 1))\n                / (8 * sizeof(uintptr_t));\n\n        for (i = 0; i < n; i++) {\n            rrp->tried[i] = 0;\n        }\n\n        ngx_http_upstream_rr_peers_unlock(peers);\n\n        rc = ngx_http_upstream_get_iwrr_peer(pc, uip);\n\n        if (rc != NGX_BUSY) {\n            return rc;\n        }\n\n        ngx_http_upstream_rr_peers_wlock(peers);\n    }\n\n    ngx_http_upstream_rr_peers_unlock(peers);\n\n    pc->name = peers->name;\n\n    return NGX_BUSY;\n}\n\nstatic ngx_http_upstream_rr_peer_t *\nngx_http_upstream_get_iwrr(ngx_http_upstream_iwrr_peer_data_t *uip)\n{\n    time_t                                 now;\n    uintptr_t                              m;\n    ngx_uint_t                             i, j, n;\n    ngx_http_upstream_rr_peer_t           *peer;\n    ngx_http_upstream_rr_peers_t          *peers;\n    ngx_http_upstream_rr_peer_data_t      *rrp;\n    ngx_http_upstream_iwrr_srv_conf_t     *uiscf;\n    ngx_http_upstream_iwrr_queue_t        *item;\n\n    now = ngx_time();\n\n    rrp = &uip->rrp;\n    peers = rrp->peers;\n    uiscf = uip->uiscf;\n\n#if (T_NGX_HTTP_UPSTREAM_RANDOM)\n    if (uiscf->init_number == NGX_CONF_UNSET_UINT) {\n        uiscf->init_number = ngx_random() % peers->number;\n\n        for (i = 0; i < uiscf->init_number; i++) {\n            ngx_http_upstream_iwrr_queue_next(uiscf);\n        }\n    }\n#endif\n\n    for (j = 0; j < peers->number; j++) {\n        item = ngx_http_upstream_iwrr_queue_next(uiscf);\n\n        i = item->index;\n        peer = item->peer;\n\n        n = i / (8 * sizeof(uintptr_t));\n        m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));\n\n        if (rrp->tried[n] & m) {\n            continue;\n        }\n\n        if (peer->down) {\n            continue;\n        }\n\n#if (NGX_HTTP_UPSTREAM_CHECK)\n        if (ngx_http_upstream_check_peer_down(peer->check_index)) {\n            continue;\n        }\n#endif\n\n        if (peer->max_fails\n            && peer->fails >= peer->max_fails\n            && now - peer->checked <= peer->fail_timeout)\n        {\n            continue;\n        }\n\n        if (peer->max_conns && peer->conns >= peer->max_conns) {\n            continue;\n        }\n\n        rrp->current = peer;\n\n        return peer;\n    }\n\n    return NULL;\n}\n\nstatic ngx_http_upstream_iwrr_queue_t *\nngx_http_upstream_iwrr_queue_next(ngx_http_upstream_iwrr_srv_conf_t *uiscf)\n{\n    ngx_http_upstream_iwrr_queue_t      *temp, *item;\n    \n    if (ngx_queue_empty(&uiscf->active->queue)) {\n        temp = uiscf->active;\n        uiscf->active = uiscf->expired;\n        uiscf->expired = temp;\n    }\n\n    item = (ngx_http_upstream_iwrr_queue_t *) ngx_queue_head(&uiscf->active->queue);\n    ngx_queue_remove(&item->queue);\n\n    item->remainder--;\n    if (item->remainder) {\n        ngx_queue_insert_tail(&uiscf->active->queue, &item->queue);\n    } else {\n        item->remainder = item->weight;\n        ngx_queue_insert_tail(&uiscf->expired->queue, &item->queue);\n    }\n\n    return item;\n}\n\nstatic inline ngx_uint_t ngx_http_upstream_iwrr_gcd(ngx_uint_t a, ngx_uint_t b)\n{\n    ngx_uint_t  r;\n    while (b) {\n        r = a % b;\n        a = b;\n        b = r;\n    }\n    return a;\n}"
  },
  {
    "path": "modules/ngx_http_upstream_keepalive_module/config",
    "content": "ngx_addon_name=ngx_http_upstream_keepalive_module\nHTTP_MODULES=\"$HTTP_MODULES ngx_http_upstream_keepalive_module\"\nNGX_ADDON_SRCS=\"$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_upstream_keepalive_module.c\"\n\n\nif [ \"$HTTP_UPSTREAM_KEEPALIVE\" != NO ]; then\n    echo \"$0: error: must disable nginx offical module with \\\"--without-http_upstream_keepalive_module\\\" option\"\n    exit 1\nfi\n"
  },
  {
    "path": "modules/ngx_http_upstream_keepalive_module/ngx_http_upstream_keepalive_module.c",
    "content": "\n/*\n * Copyright (C) cfsego\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n * Copyright (C) Maxim Dounin\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\ntypedef struct {\n    ngx_uint_t                         max_cached;\n    ngx_msec_t                         keepalive_timeout;\n    ngx_uint_t                         max_key_length;\n    ngx_uint_t                         pool_size;\n\n    ngx_queue_t                        cache;\n    ngx_queue_t                        free;\n    ngx_queue_t                        dummy;\n\n    ngx_http_upstream_init_pt          original_init_upstream;\n    ngx_http_upstream_init_peer_pt     original_init_peer;\n\n    ngx_http_complex_value_t          *slice_key;\n    ngx_int_t                          slice_conn;\n    ngx_int_t                          slice_var_index;\n\n    ngx_rbtree_t                      *index;\n    ngx_queue_t                        index_pool;\n\n} ngx_http_upstream_keepalive_srv_conf_t;\n\n\ntypedef struct {\n    u_char                             color;\n    u_char                             count;\n    u_short                            len;\n    ngx_queue_t                        cache; \n    ngx_queue_t                        index;\n    u_char                             data[1];\n} ngx_http_upstream_keepalive_node_t;\n\n\ntypedef struct {\n    ngx_http_upstream_keepalive_srv_conf_t  *conf;\n\n    ngx_http_request_t                *request;\n    ngx_http_upstream_t               *upstream;\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_str_t                          key;\n    uint32_t                           hash;\n\n} ngx_http_upstream_keepalive_peer_data_t;\n\n\ntypedef struct {\n    ngx_http_upstream_keepalive_node_t      *node;\n    ngx_http_upstream_keepalive_srv_conf_t  *conf;\n\n    ngx_queue_t                        queue;\n    ngx_queue_t                        index;\n    ngx_connection_t                  *connection;\n\n    socklen_t                          socklen;\n    u_char                             sockaddr[NGX_SOCKADDRLEN];\n\n} ngx_http_upstream_keepalive_cache_t;\n\n\nstatic ngx_int_t ngx_http_upstream_init_keepalive_peer(ngx_http_request_t *r,\n    ngx_http_upstream_srv_conf_t *us);\nstatic ngx_int_t ngx_http_upstream_get_keepalive_peer(ngx_peer_connection_t *pc,\n    void *data);\nstatic void ngx_http_upstream_free_keepalive_peer(ngx_peer_connection_t *pc,\n    void *data, ngx_uint_t state);\nstatic ngx_http_upstream_keepalive_node_t *ngx_http_upstream_keepalive_lookup(\n    ngx_http_upstream_keepalive_peer_data_t *kp);\n\nstatic void ngx_http_upstream_keepalive_dummy_handler(ngx_event_t *ev);\nstatic void ngx_http_upstream_keepalive_close_handler(ngx_event_t *ev);\nstatic void ngx_http_upstream_keepalive_close(ngx_connection_t *c);\nstatic void ngx_http_upstream_keepalive_cleanup(void *data);\n\nstatic void ngx_http_upstream_keepalive_insert_value(ngx_rbtree_node_t *temp,\n    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);\nstatic ngx_int_t ngx_http_upstream_keepalive_get_peer_in_slice(\n    ngx_peer_connection_t *pc, ngx_http_upstream_keepalive_peer_data_t *kp);\nstatic ngx_int_t\n    ngx_http_upstream_do_get_keepalive_peer(ngx_peer_connection_t *pc,\n    ngx_queue_t *cache, ngx_queue_t *free, off_t offset);\n\n#if (NGX_HTTP_SSL)\nstatic ngx_int_t ngx_http_upstream_keepalive_set_session(\n    ngx_peer_connection_t *pc, void *data);\nstatic void ngx_http_upstream_keepalive_save_session(ngx_peer_connection_t *pc,\n    void *data);\n#endif\n\nstatic void *ngx_http_upstream_keepalive_create_conf(ngx_conf_t *cf);\nstatic char *ngx_http_upstream_keepalive(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_http_upstream_keepalive_timeout(ngx_conf_t *cf,\n    ngx_command_t *cmd, void *conf);\nstatic ngx_int_t ngx_http_upstream_init_keepalive(ngx_conf_t *cf,\n    ngx_http_upstream_srv_conf_t *us);\n\nstatic ngx_int_t ngx_http_upstream_keepalive_param_skey(ngx_conf_t *cf,\n    void *conf, ngx_str_t *val);\nstatic ngx_int_t ngx_http_upstream_keepalive_param_sconn(ngx_conf_t *cf,\n    void *conf, ngx_str_t *val);\nstatic ngx_int_t ngx_http_upstream_keepalive_param_dyn(ngx_conf_t *cf,\n    void *conf, ngx_str_t *val);\nstatic ngx_int_t ngx_http_upstream_keepalive_param_klen(ngx_conf_t *cf,\n    void *conf, ngx_str_t *val);\nstatic ngx_int_t ngx_http_upstream_keepalive_param_psize(ngx_conf_t *cf,\n    void *conf, ngx_str_t *val);\n\n\nstatic ngx_command_t  ngx_http_upstream_keepalive_commands[] = {\n\n    { ngx_string(\"keepalive\"),\n      NGX_HTTP_UPS_CONF|NGX_CONF_1MORE,\n      ngx_http_upstream_keepalive,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"keepalive_timeout\"),\n      NGX_HTTP_UPS_CONF|NGX_CONF_TAKE1,\n      ngx_http_upstream_keepalive_timeout,\n      0,\n      0,\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_upstream_keepalive_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    NULL,                                  /* postconfiguration */\n\n    NULL,                                  /* create main configuration */\n    NULL,                                  /* init main configuration */\n\n    ngx_http_upstream_keepalive_create_conf, /* 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_upstream_keepalive_module = {\n    NGX_MODULE_V1,\n    &ngx_http_upstream_keepalive_module_ctx, /* module context */\n    ngx_http_upstream_keepalive_commands,  /* 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    ngx_int_t (*cb)(ngx_conf_t *cf, void *conf, ngx_str_t *val);\n} ngx_http_upstream_keepalive_param;\n\n\nstatic ngx_http_upstream_keepalive_param keepalive_params[] = {\n    { ngx_string(\"slice_poolsize\"), ngx_http_upstream_keepalive_param_psize },\n    { ngx_string(\"slice_keylen\"), ngx_http_upstream_keepalive_param_klen },\n    { ngx_string(\"slice_conn\"), ngx_http_upstream_keepalive_param_sconn },\n    { ngx_string(\"slice_key\"), ngx_http_upstream_keepalive_param_skey },\n    { ngx_string(\"slice_dyn\"), ngx_http_upstream_keepalive_param_dyn }\n};\n\n\n/* DONE */\nstatic void\nngx_http_upstream_keepalive_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_upstream_keepalive_node_t   *ukn, *uknt;\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            ukn = (ngx_http_upstream_keepalive_node_t *) &node->color;\n            uknt = (ngx_http_upstream_keepalive_node_t *) &temp->color;\n\n            p = (ngx_memn2cmp(ukn->data, uknt->data, ukn->len, uknt->len) < 0)\n                ? &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\n/* DONE */\nstatic ngx_int_t\nngx_http_upstream_keepalive_param_skey(ngx_conf_t *cf, void *conf,\n    ngx_str_t *val)\n{\n    ngx_http_compile_complex_value_t         ccv;\n    ngx_http_upstream_keepalive_srv_conf_t  *kcf = conf;\n\n    kcf->slice_key = ngx_palloc(cf->pool,\n                                sizeof(ngx_http_complex_value_t));\n    if (kcf->slice_key == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n\n    ccv.cf = cf;\n    ccv.value = val;\n    ccv.complex_value = kcf->slice_key;\n\n    return ngx_http_compile_complex_value(&ccv);\n}\n\n\n/* DONE */\nstatic ngx_int_t\nngx_http_upstream_keepalive_param_sconn(ngx_conf_t *cf, void *conf,\n    ngx_str_t *val)\n{\n    ngx_http_upstream_keepalive_srv_conf_t  *kcf = conf;\n\n    kcf->slice_conn = ngx_atoi(val->data, val->len);\n\n    return kcf->slice_conn == NGX_ERROR ? NGX_ERROR : NGX_OK;\n}\n\n\n/* DONE */\nstatic ngx_int_t\nngx_http_upstream_keepalive_param_dyn(ngx_conf_t *cf, void *conf,\n    ngx_str_t *val)\n{\n    ngx_http_upstream_keepalive_srv_conf_t  *kcf = conf;\n\n    if (val->data[0] == '$') {\n        val->data++;\n        val->len--;\n    }\n\n    kcf->slice_var_index = ngx_http_get_variable_index(cf, val);\n\n    return kcf->slice_var_index == NGX_ERROR ? NGX_ERROR : NGX_OK;\n}\n\n\n/* DONE */\nstatic ngx_int_t\nngx_http_upstream_keepalive_param_klen(ngx_conf_t *cf, void *conf,\n    ngx_str_t *val)\n{\n    ngx_int_t  n;\n\n    ngx_http_upstream_keepalive_srv_conf_t  *kcf = conf;\n\n    n = ngx_atoi(val->data, val->len);\n    kcf->max_key_length = n;\n\n    return n == NGX_ERROR ? NGX_ERROR : NGX_OK;\n}\n\n\n/* DONE */\nstatic ngx_int_t\nngx_http_upstream_keepalive_param_psize(ngx_conf_t *cf, void *conf,\n    ngx_str_t *val)\n{\n    ngx_int_t  n;\n\n    ngx_http_upstream_keepalive_srv_conf_t  *kcf = conf;\n\n    n = ngx_atoi(val->data, val->len);\n    kcf->pool_size = n;\n\n    return n == NGX_ERROR ? NGX_ERROR : NGX_OK;\n}\n\n\n/* DONE */\nstatic char *\nngx_http_upstream_keepalive(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_int_t    n;\n    ngx_str_t   *value, tmp;\n    ngx_uint_t   i, j;\n\n    ngx_http_upstream_srv_conf_t            *uscf;\n    ngx_http_upstream_keepalive_srv_conf_t  *kcf = conf;\n\n    uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);\n\n    if (kcf->original_init_upstream) {\n        return \"is duplicate\";\n    }\n\n    kcf->original_init_upstream = uscf->peer.init_upstream\n                                ? uscf->peer.init_upstream\n                                : ngx_http_upstream_init_round_robin;\n\n    uscf->peer.init_upstream = ngx_http_upstream_init_keepalive;\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    kcf->max_cached = n;\n\n    for (i = 2; i < cf->args->nelts; i++) {\n\n        for (j = 0;\n             j < sizeof(keepalive_params)\n                              / sizeof(ngx_http_upstream_keepalive_param);\n             j++)\n        {\n            if (value[i].len > keepalive_params[j].name.len + 1\n                && ngx_strncmp(value[i].data, keepalive_params[j].name.data,\n                               keepalive_params[j].name.len)\n                    == 0)\n            {\n                tmp.data = value[i].data + keepalive_params[j].name.len + 1;\n                tmp.len = value[i].len - keepalive_params[j].name.len - 1;\n\n                if (keepalive_params[j].cb(cf, kcf, &tmp) != NGX_OK) {\n                    goto invalid;\n                }\n\n                goto next;\n            }\n        }\n\n        goto invalid;\n\nnext:\n        continue;\n    }\n\n    if (kcf->slice_key && kcf->slice_var_index == NGX_CONF_UNSET\n        && kcf->slice_conn == NGX_CONF_UNSET)\n    {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"specific either slice_conn or slice_dyn\");\n\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n\ninvalid:\n\n    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                       \"invalid parameter \\\"%V\\\"\", &value[i]);\n\n    return NGX_CONF_ERROR;\n}\n\n\n/* DONE */\nstatic ngx_int_t\nngx_http_upstream_init_keepalive(ngx_conf_t *cf,\n    ngx_http_upstream_srv_conf_t *us)\n{\n    size_t                                   size;\n    u_char                                  *index;\n    ngx_uint_t                               i;\n    ngx_rbtree_node_t                       *sentinel;\n    ngx_pool_cleanup_t                      *cln;\n    ngx_http_upstream_keepalive_node_t      *node;\n    ngx_http_upstream_keepalive_cache_t     *cached;\n    ngx_http_upstream_keepalive_srv_conf_t  *kcf;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cf->log, 0,\n                   \"init keepalive\");\n\n    kcf = ngx_http_conf_upstream_srv_conf(us,\n                                          ngx_http_upstream_keepalive_module);\n\n    if (kcf->original_init_upstream(cf, us) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    kcf->original_init_peer = us->peer.init;\n\n    us->peer.init = ngx_http_upstream_init_keepalive_peer;\n\n    /* allocate cache items and add to free queue */\n\n    cached = ngx_pcalloc(cf->pool,\n                sizeof(ngx_http_upstream_keepalive_cache_t) * kcf->max_cached);\n    if (cached == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_queue_init(&kcf->cache);\n    ngx_queue_init(&kcf->free);\n    ngx_queue_init(&kcf->dummy);\n\n    for (i = 0; i < kcf->max_cached; i++) {\n        ngx_queue_insert_head(&kcf->free, &cached[i].queue);\n        cached[i].conf = kcf;\n    }\n\n    if (kcf->slice_key) {\n        kcf->index = ngx_palloc(cf->pool, sizeof(ngx_rbtree_t));\n        if (kcf->index == NULL) {\n            return NGX_ERROR;\n        }\n\n        sentinel = ngx_pcalloc(cf->pool, sizeof(ngx_rbtree_node_t));\n        if (sentinel == NULL) {\n            return NGX_ERROR;\n        }\n\n        ngx_rbtree_init(kcf->index, sentinel,\n                        ngx_http_upstream_keepalive_insert_value);\n\n        size = offsetof(ngx_rbtree_node_t, color)\n             + offsetof(ngx_http_upstream_keepalive_node_t, data)\n             + kcf->max_key_length;\n\n        index = ngx_pcalloc(cf->pool, size * kcf->pool_size);\n        if (index == NULL) {\n            return NGX_ERROR;\n        }\n\n        ngx_queue_init(&kcf->index_pool);\n\n        for (i = 0; i < kcf->pool_size; i++, index += size) {\n            node = (ngx_http_upstream_keepalive_node_t *)\n                              (index + offsetof(ngx_rbtree_node_t, color));\n            ngx_queue_insert_head(&kcf->index_pool, &node->index);\n        }\n    }\n\n    cln = ngx_pool_cleanup_add(cf->pool, 0);\n    if (cln == NULL) {\n        return NGX_ERROR;\n    }\n\n    cln->handler = ngx_http_upstream_keepalive_cleanup;\n    cln->data = kcf;\n\n    return NGX_OK;\n}\n\n\n/* DONE */\nstatic void\nngx_http_upstream_keepalive_cleanup(void *data)\n{\n    ngx_queue_t                              *q, *cache;\n    ngx_connection_t                         *c;\n    ngx_http_upstream_keepalive_cache_t      *item;\n    ngx_http_upstream_keepalive_srv_conf_t   *kcf = data;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                   \"keepalive cleanup\");\n\n    /* destroy all the event and timers */\n\n    cache = &kcf->cache;\n\n    for (q = ngx_queue_head(cache);\n         q != ngx_queue_sentinel(cache);\n         q = ngx_queue_next(q))\n    {\n        item = ngx_queue_data(q, ngx_http_upstream_keepalive_cache_t, queue);\n        c = item->connection;\n\n        if (c && c->idle) {\n            ngx_http_upstream_keepalive_close(c);\n        }\n    }\n}\n\n\n/* DONE */\nstatic ngx_int_t\nngx_http_upstream_init_keepalive_peer(ngx_http_request_t *r,\n    ngx_http_upstream_srv_conf_t *us)\n{\n    ngx_http_upstream_keepalive_peer_data_t  *kp;\n    ngx_http_upstream_keepalive_srv_conf_t   *kcf;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"init keepalive peer\");\n\n    kcf = ngx_http_conf_upstream_srv_conf(us,\n                                          ngx_http_upstream_keepalive_module);\n\n    kp = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_keepalive_peer_data_t));\n    if (kp == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (kcf->original_init_peer(r, us) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    kp->conf = kcf;\n    kp->request = r;\n\n    if (kcf->index) {\n        if (ngx_http_complex_value(r, kcf->slice_key, &kp->key) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        if (kp->key.len > kcf->max_key_length) {\n            kp->key.len = kcf->max_key_length;\n        }\n\n        kp->hash = ngx_murmur_hash2(kp->key.data, kp->key.len);\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"init keepalive slice \\\"%V\\\"\", &kp->key);\n    }\n\n    kp->upstream = r->upstream;\n    kp->data = r->upstream->peer.data;\n    kp->original_get_peer = r->upstream->peer.get;\n    kp->original_free_peer = r->upstream->peer.free;\n\n    r->upstream->peer.data = kp;\n    r->upstream->peer.get = ngx_http_upstream_get_keepalive_peer;\n    r->upstream->peer.free = ngx_http_upstream_free_keepalive_peer;\n\n#if (NGX_HTTP_SSL)\n    kp->original_set_session = r->upstream->peer.set_session;\n    kp->original_save_session = r->upstream->peer.save_session;\n    r->upstream->peer.set_session = ngx_http_upstream_keepalive_set_session;\n    r->upstream->peer.save_session = ngx_http_upstream_keepalive_save_session;\n#endif\n\n    return NGX_OK;\n}\n\n\nstatic ngx_http_upstream_keepalive_node_t *\nngx_http_upstream_keepalive_lookup(ngx_http_upstream_keepalive_peer_data_t *kp)\n{\n    ngx_int_t                            rc;\n    ngx_rbtree_node_t                   *node, *sentinel;\n    ngx_http_upstream_keepalive_node_t  *ukn;\n\n    node = kp->conf->index->root;\n    sentinel = kp->conf->index->sentinel;\n\n    while (node != sentinel) {\n\n        if (kp->hash < node->key) {\n            node = node->left;\n            continue;\n        }\n\n        if (kp->hash > node->key) {\n            node = node->right;\n            continue;\n        }\n\n        /* hash == node->key */\n\n        ukn = (ngx_http_upstream_keepalive_node_t *) &node->color;\n\n        rc = ngx_memn2cmp(kp->key.data, ukn->data, kp->key.len, ukn->len);\n\n        if (rc == 0) {\n            return ukn;\n        }\n\n        node = (rc < 0) ? node->left : node->right;\n    }\n\n    return NULL;\n}\n\n\n/* DONE */\nstatic ngx_int_t\nngx_http_upstream_keepalive_get_peer_in_slice(ngx_peer_connection_t *pc,\n    ngx_http_upstream_keepalive_peer_data_t *kp)\n{\n    ngx_int_t                            rc;\n    ngx_http_upstream_keepalive_node_t  *ukn;\n\n    ukn = ngx_http_upstream_keepalive_lookup(kp);\n\n    if (ukn) {\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                   \"getting connection in slice \\\"%V\\\"\", &kp->key);\n\n        rc = ngx_http_upstream_do_get_keepalive_peer(\n                    pc,\n                    &ukn->cache,\n                    &kp->conf->free,\n                    offsetof(ngx_http_upstream_keepalive_cache_t, index));\n\n        if (rc == NGX_DONE) {\n            ukn->count--;\n        }\n\n        return rc;\n    }\n\n    return NGX_OK;\n}\n\n\n/* DONE */\nstatic ngx_int_t\nngx_http_upstream_do_get_keepalive_peer(ngx_peer_connection_t *pc,\n    ngx_queue_t *cache, ngx_queue_t *free, off_t offset)\n{\n    ngx_queue_t       *q;\n    ngx_connection_t  *c;\n\n    ngx_http_upstream_keepalive_cache_t  *item;\n\n    for (q = ngx_queue_head(cache);\n         q != ngx_queue_sentinel(cache);\n         q = ngx_queue_next(q))\n    {\n        item = (ngx_http_upstream_keepalive_cache_t *) ((u_char *) q - offset);\n        c = item->connection;\n\n        if (ngx_memn2cmp((u_char *) &item->sockaddr, (u_char *) pc->sockaddr,\n                         item->socklen, pc->socklen)\n            == 0)\n        {\n            ngx_queue_remove(&item->index);\n            ngx_queue_remove(&item->queue);\n            ngx_queue_insert_head(free, &item->queue);\n\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                           \"get keepalive peer: using connection %p\", c);\n\n            c->idle = 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->connection = c;\n            pc->cached = 1;\n\n            return NGX_DONE;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\n/* DONE */\nstatic ngx_int_t\nngx_http_upstream_get_keepalive_peer(ngx_peer_connection_t *pc, void *data)\n{\n    ngx_http_upstream_keepalive_peer_data_t  *kp = data;\n\n    ngx_int_t  rc;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                   \"get keepalive peer\");\n\n    /* ask balancer */\n\n    rc = kp->original_get_peer(pc, kp->data);\n\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    /* search cache for suitable connection */\n\n    if (kp->conf->index) {\n        return ngx_http_upstream_keepalive_get_peer_in_slice(pc, kp);\n    }\n\n    return ngx_http_upstream_do_get_keepalive_peer(\n                        pc,\n                        &kp->conf->cache,\n                        &kp->conf->free,\n                        offsetof(ngx_http_upstream_keepalive_cache_t, queue));\n}\n\n\n/* DONE */\nstatic void\nngx_http_upstream_free_keepalive_peer(ngx_peer_connection_t *pc, void *data,\n    ngx_uint_t state)\n{\n    ngx_http_upstream_keepalive_node_t       *ukn;\n    ngx_http_upstream_keepalive_cache_t      *item;\n    ngx_http_upstream_keepalive_peer_data_t  *kp = data;\n\n    ngx_int_t                   n;\n    ngx_queue_t                *q;\n    ngx_connection_t           *c;\n    ngx_rbtree_node_t          *node;\n    ngx_http_upstream_t        *u;\n    ngx_http_variable_value_t  *v;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                   \"free keepalive peer\");\n\n    /* cache valid connections */\n\n    u = kp->upstream;\n    c = pc->connection;\n    ukn = NULL;\n\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 closed;\n    }\n\n    if (!u->keepalive) {\n        goto closed;\n    }\n\n    if (!u->request_body_sent) {\n        goto closed;\n    }\n\n    if (ngx_terminate || ngx_exiting) {\n        goto closed;\n    }\n\n    if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n        goto closed;\n    }\n\n    if (kp->conf->index) {\n        if (kp->conf->slice_var_index != NGX_CONF_UNSET) {\n            v = ngx_http_get_indexed_variable(kp->request,\n                                              kp->conf->slice_var_index);\n\n            if (v == NULL || v->not_found || v->valid == 0) {\n                ngx_log_error(NGX_LOG_WARN, pc->log, 0,\n                              \"keepalive slice: variable is uninitialized\");\n                goto closed;\n            }\n\n            n = ngx_atoi(v->data, v->len);\n            if (n == NGX_ERROR) {\n                ngx_log_error(NGX_LOG_WARN, pc->log, 0,\n                              \"keepalive slice: invalid variable value\");\n                goto closed;\n            }\n\n        } else {\n            n = kp->conf->slice_conn;\n        }\n\n        if (n == 0) {\n            ngx_log_error(NGX_LOG_INFO, pc->log, 0,\n                          \"keepalive slice: closed, disabled\");\n            goto closed;\n        }\n\n        ukn = ngx_http_upstream_keepalive_lookup(kp);\n        if (ukn && ukn->count >= n) {\n            ngx_log_error(NGX_LOG_INFO, pc->log, 0,\n                          \"keepalive slice: closed, too many conn\");\n            goto closed;\n        }\n\n        if (ngx_queue_empty(&kp->conf->index_pool)) {\n            ngx_log_error(NGX_LOG_INFO, pc->log, 0,\n                          \"keepalive slice: closed, full pool\");\n            goto closed;\n        }\n\n        if (ukn == NULL) {\n            q = ngx_queue_head(&kp->conf->index_pool);\n            ngx_queue_remove(q);\n            ukn = ngx_queue_data(q, ngx_http_upstream_keepalive_node_t, index);\n            node = (ngx_rbtree_node_t *)\n                         ((u_char *) ukn - offsetof(ngx_rbtree_node_t, color));\n\n            node->key = kp->hash;\n            ukn->len = kp->key.len;\n            ngx_memcpy(ukn->data, kp->key.data, ukn->len);\n            ngx_rbtree_insert(kp->conf->index, node);\n            ngx_queue_init(&ukn->cache);\n            ukn->count = 0;\n        }\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                   \"free keepalive peer: saving connection %p\", c);\n\n    if (ngx_queue_empty(&kp->conf->free)) {\n\n        q = ngx_queue_last(&kp->conf->cache);\n        ngx_queue_remove(q);\n\n        item = ngx_queue_data(q, ngx_http_upstream_keepalive_cache_t, queue);\n        ngx_queue_remove(&item->index);\n\n        ngx_http_upstream_keepalive_close(item->connection);\n\n    } else {\n        q = ngx_queue_head(&kp->conf->free);\n        ngx_queue_remove(q);\n\n        item = ngx_queue_data(q, ngx_http_upstream_keepalive_cache_t, queue);\n    }\n\n    item->connection = c;\n    ngx_queue_insert_head(&kp->conf->cache, q);\n    if (ukn) {\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                       \"saving connection in slice \\\"%V\\\"\", &kp->key);\n\n        ngx_queue_insert_head(&ukn->cache, &item->index);\n        ukn->count++;\n        item->node = ukn;\n\n    } else {\n        ngx_queue_insert_head(&kp->conf->dummy, &item->index);\n        item->node = NULL;\n    }\n\n    pc->connection = NULL;\n\n    if (c->read->timer_set) {\n        ngx_del_timer(c->read);\n    }\n    if (c->write->timer_set) {\n        ngx_del_timer(c->write);\n    }\n\n    if (kp->conf->keepalive_timeout != NGX_CONF_UNSET_MSEC &&\n        kp->conf->keepalive_timeout != 0)\n    {\n        ngx_add_timer(c->read, kp->conf->keepalive_timeout);\n    }\n\n    c->write->handler = ngx_http_upstream_keepalive_dummy_handler;\n    c->read->handler = ngx_http_upstream_keepalive_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\n    if (c->read->ready) {\n        ngx_http_upstream_keepalive_close_handler(c->read);\n    }\n\nclosed:\n\n    kp->original_free_peer(pc, kp->data, state);\n}\n\n\n/* DONE */\nstatic void\nngx_http_upstream_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\n/* DONE */\nstatic void\nngx_http_upstream_keepalive_close_handler(ngx_event_t *ev)\n{\n    ngx_http_upstream_keepalive_srv_conf_t  *conf;\n    ngx_http_upstream_keepalive_cache_t     *item;\n\n    int                n;\n    char               buf[1];\n    ngx_connection_t  *c;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0,\n                   \"keepalive close handler\");\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                       \"keepalive max idle timeout\");\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        /* stale event */\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    conf = item->conf;\n\n    ngx_http_upstream_keepalive_close(c);\n\n    ngx_queue_remove(&item->queue);\n    ngx_queue_remove(&item->index);\n    ngx_queue_insert_head(&conf->free, &item->queue);\n\n    if (item->node) {\n        item->node->count--;\n    }\n}\n\n\n/* DONE */\nstatic void\nngx_http_upstream_keepalive_close(ngx_connection_t *c)\n{\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        if (ngx_ssl_shutdown(c) == NGX_AGAIN) {\n            c->ssl->handler = ngx_http_upstream_keepalive_close;\n            return;\n        }\n    }\n\n#endif\n\n    ngx_destroy_pool(c->pool);\n    ngx_close_connection(c);\n}\n\n\n#if (NGX_HTTP_SSL)\n\n/* DONE */\nstatic ngx_int_t\nngx_http_upstream_keepalive_set_session(ngx_peer_connection_t *pc, void *data)\n{\n    ngx_http_upstream_keepalive_peer_data_t  *kp = data;\n\n    return kp->original_set_session(pc, kp->data);\n}\n\n\n/* DONE */\nstatic void\nngx_http_upstream_keepalive_save_session(ngx_peer_connection_t *pc, void *data)\n{\n    ngx_http_upstream_keepalive_peer_data_t  *kp = data;\n\n    kp->original_save_session(pc, kp->data);\n    return;\n}\n\n#endif\n\n\n/* DONE */\nstatic void *\nngx_http_upstream_keepalive_create_conf(ngx_conf_t *cf)\n{\n    ngx_http_upstream_keepalive_srv_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool,\n                       sizeof(ngx_http_upstream_keepalive_srv_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc():\n     *\n     *     conf->original_init_upstream = NULL;\n     *     conf->original_init_peer = NULL;\n     *     conf->slice_key = NULL;\n     *     conf->sentinel = NULL;\n     *     conf->index = NULL;\n     */\n\n    conf->max_cached = 1;\n    conf->pool_size = 20;\n    conf->max_key_length = 40;   /* 128B at length */\n    conf->keepalive_timeout = NGX_CONF_UNSET_MSEC;\n    conf->slice_var_index = NGX_CONF_UNSET;\n    conf->slice_conn = NGX_CONF_UNSET;\n\n    return conf;\n}\n\n\n/* DONE */\nstatic char *\nngx_http_upstream_keepalive_timeout(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n    ngx_http_upstream_srv_conf_t  *uscf;\n    ngx_http_upstream_keepalive_srv_conf_t  *kcf;\n\n    ngx_str_t   *value;\n    ngx_msec_t   timeout;\n\n    uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);\n\n    kcf = ngx_http_conf_upstream_srv_conf(uscf,\n                                          ngx_http_upstream_keepalive_module);\n\n    if (kcf->keepalive_timeout != NGX_CONF_UNSET_MSEC) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    timeout = ngx_parse_time(&value[1], 0);\n    if (timeout == (ngx_msec_t) NGX_ERROR) {\n        return \"invalid value\";\n    }\n\n    kcf->keepalive_timeout = timeout;\n\n    return NGX_CONF_OK;\n}\n\n"
  },
  {
    "path": "modules/ngx_http_upstream_keepalive_module/ngx_http_upstream_keepalive_module.md",
    "content": "Background\n===========================================\nWe use Apache with mpm-itk as our upstream server and tengine as frontend proxy with caching of static resources.\n\nApache with mpm-itk means that apache preforks processes that listens for connections, and when it knows what vhost the connection will use, it changes its user and group to that vhosts settings. This thus drops root privileges and means that the connection cannot be re-used for another vhost. It can however be reused within the same vhost. If a keepalive connection attempts to access another vhost than the process can handle, then the connection is reset and the client will have to start a new connection.\n\nIf tengine could keep a small cache of upstream keepalive connections, since reusing existing connections would be beneficial for performance and scalability, and especially latency.\nBut for this to work with mpm-itk, I request the possibility to set those connections to be vhost-local, thus only re-using a connection if one exists for the same vhost. This could be solved either by setting a key to differentiate them (like how proxy cache is solved), or by just comparing the hostname and domain parts of the url (less flexible, but still a lot better than nothing.\n\nThis could also have limits on how many connections to cache per vhost, to avoid one vhost dominating the cache. And a upper time limit for each connection, to override keepalive timeout sent by the upstream server if it is too long. (it could be out of the control of the tengine user and could thus potentially be set too long).\n\n\nINSTALL\n===========================================\nThis module is enabled by the configuration parameter '--without-http_upstream_keepalive_module --add-module=modules/ngx_http_upstream_keepalive_module/'. It is the substitution of the origin, and compatible with it.\n\nExample\n===========================================\n\n    upstream {\n        server 127.0.0.1;\n        keepalive 30 slice_key=$host slice_conn=2;\n    }\n\nor\n\n    map $host $conn {\n        hostnames;\n        default 0;\n        *.allow.com 2;\n    }\n    \n    upstream {\n        server 127.0.0.1;\n        keepalive 30 slice_key=$host slice_dyn=$conn;\n    }\n\nDirective\n===========================================\n\n**Syntax**: *keepalive connections [slice_key=key] [slice_conn=sconn] [slice_dyn=$dyn] [slice_poolsize=poolsize] [slice_keylen=keylength]*\n\n**Default**: *none*\n\n**Context**: *ups*\n\nActivates the cache for connections to upstream servers.\n\nThe `connections` parameter sets the maximum number of idle keepalive connections to upstream servers that are preserved in the cache of each worker process. When this number is exceeded, the least recently used connections are closed.\n\nThe keep-alive connections will be saved into buckets based on values of server address and `slice_key`. If you want to keep some connections for each virtual vhost, you can set `slice_key=$host` or `slice_key=$server_name:$server_port`.\n\nThe maximum number of connections held by each bucket is defined by `slice_conn` as a whole, or by `slice_dyn` more specifically.\n\nThe maximum number of bucket is defined by `slice_poolsize`, which means the maximum number of slice_key. The default value is 20.\n\nFor each key defined by `slice_key`, the module cuts them off if the key length is greater than the length defined by `slice_keylen`. The default length is 40.\n\n\n**Syntax**: *keepalive_timeout timeout*\n\n**Default**: *none*\n\n**Context**: *ups*\n\nSet a timeout during which a keep-alive upstream connection will stay open.\n"
  },
  {
    "path": "modules/ngx_http_upstream_keepalive_module/proxy_keepalive.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n# (C) cfsego\n\n# Tests for proxy with keepalive.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse IO::Socket::INET;\nuse Socket qw/ CRLF /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy ssi rewrite/)\n\t->plan(242)->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\nworker_processes 1;\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    map $server_name:$server_port $dyn {\n       localhost:8080 1;\n       default 0;\n    }\n\n    upstream backend {\n        server 127.0.0.1:8081;\n        keepalive 1;\n    }\n\n    upstream backend1 {\n        server 127.0.0.1:8081;\n        keepalive 1 slice_key=$host slice_conn=1;\n    }\n\n    upstream backend2 {\n        server 127.0.0.1:8081;\n        keepalive 1 slice_key=$server_name:$server_port slice_dyn=$dyn;\n    }\n\n    upstream backend3 {\n        server 127.0.0.1:8081;\n        keepalive 1 slice_key=$host slice_conn=1 slice_poolsize=0;\n    }\n\n    upstream backend4 {\n        server 127.0.0.1:8081 max_fails=0;\n        keepalive 1 slice_key=$uri slice_conn=1 slice_keylen=7;\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        proxy_read_timeout 2s;\n        proxy_http_version 1.1;\n        proxy_set_header Connection \"\";\n\n        location / {\n            proxy_pass http://backend;\n        }\n\n        location /unbuffered/ {\n            proxy_pass http://backend;\n            proxy_buffering off;\n        }\n\n        location /inmemory/ {\n            ssi on;\n            rewrite ^ /ssi.html break;\n        }\n\n        location /ups1/ {\n            proxy_pass http://backend1/;\n        }\n\n        location /ups1/unbuffered/ {\n            proxy_pass http://backend1/;\n            proxy_buffering off;\n        }\n\n        location /ups1/inmemory/ {\n            ssi on;\n            rewrite ^ /ssi_ups1.html break;\n        }\n\n        location /ups2/ {\n            proxy_pass http://backend2/;\n        }\n\n        location /ups2/unbuffered/ {\n            proxy_pass http://backend2/;\n            proxy_buffering off;\n        }\n\n        location /ups2/inmemory/ {\n            ssi on;\n            rewrite ^ /ssi_ups2.html break;\n        }\n\n        location /ups3/ {\n            proxy_pass http://backend3/;\n        }\n\n        location /ups3/unbuffered/ {\n            proxy_pass http://backend3/;\n            proxy_buffering off;\n        }\n\n        location /ups3/inmemory/ {\n            ssi on;\n            rewrite ^ /ssi_ups3.html break;\n        }\n\n        location /ups4/ {\n            rewrite /ups4(/.*)$ $1 break;\n            proxy_pass http://backend4;\n        }\n\n        location /ups4/buffered/ {\n            rewrite /ups4/buffered(/.*)$ $1 break;\n            proxy_pass http://backend4;\n            proxy_buffering off;\n        }\n\n        location /ups4/unbuffered/ {\n            rewrite /ups4/unbuffered(/.*)$ $1 break;\n            proxy_pass http://backend4;\n            proxy_buffering off;\n        }\n\n        location /ups4/inmemory/ {\n            ssi on;\n            rewrite ^ /ssi_ups4.html break;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('ssi.html',\n\t'<!--#include virtual=\"/include$request_uri\" set=\"x\" -->' .\n\t'set: <!--#echo var=\"x\" -->');\n\n$t->write_file('ssi_ups1.html',\n\t'<!--#include virtual=\"/ups1/include$request_uri\" set=\"x\" -->' .\n\t'set: <!--#echo var=\"x\" -->');\n\n$t->write_file('ssi_ups2.html',\n\t'<!--#include virtual=\"/ups2/include$request_uri\" set=\"x\" -->' .\n\t'set: <!--#echo var=\"x\" -->');\n\n$t->write_file('ssi_ups3.html',\n\t'<!--#include virtual=\"/ups3/include$request_uri\" set=\"x\" -->' .\n\t'set: <!--#echo var=\"x\" -->');\n\n$t->write_file('ssi_ups4.html',\n\t'<!--#include virtual=\"/ups4/include$request_uri\" set=\"x\" -->' .\n\t'set: <!--#echo var=\"x\" -->');\n\n$t->run_daemon(\\&http_daemon);\n$t->run();\n\n$t->waitforsocket('127.0.0.1:8081')\n\tor die \"Can't start test backend\";\n\n###############################################################################\n\n# There are 3 mostly independend modes of upstream operation:\n#\n# 1. Buffered, i.e. normal mode with \"proxy_buffering on;\"\n# 2. Unbuffered, i.e. \"proxy_buffering off;\".\n# 3. In memory, i.e. ssi <!--#include ... set -->\n#\n# These all should be tested.\n\nmy ($r, $n);\n\n# buffered\n\nlike($r = http_get('/buffered/length1'), qr/SEE-THIS/, 'buffered');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/buffered/length2'), qr/X-Connection: $n.*SEE/ms, 'buffered 2');\n\nlike($r = http_get('/buffered/chunked1'), qr/SEE-THIS/, 'buffered chunked');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/buffered/chunked2'), qr/X-Connection: $n/,\n\t'buffered chunked 2');\n\nlike($r = http_get('/buffered/complex1'), qr/(0123456789){100}/,\n\t'buffered complex chunked');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/buffered/complex2'), qr/X-Connection: $n/,\n\t'buffered complex chunked 2');\n\nlike($r = http_get('/buffered/chunk01'), qr/200 OK/, 'buffered 0 chunk');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/buffered/chunk02'), qr/X-Connection: $n/, 'buffered 0 chunk 2');\n\nlike($r = http_head('/buffered/length/head1'), qr/(?!SEE-THIS)/,\n\t'buffered head');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_head('/buffered/length/head2'), qr/X-Connection: $n/,\n\t'buffered head 2');\n\nlike($r = http_get('/buffered/empty1'), qr/200 OK/, 'buffered empty');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/buffered/empty2'), qr/X-Connection: $n/, 'buffered empty 2');\n\nlike($r = http_get('/buffered/304nolen1'), qr/304 Not/, 'buffered 304');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/buffered/304nolen2'), qr/X-Connection: $n/, 'buffered 304 2');\n\nlike($r = http_get('/buffered/304len1'), qr/304 Not/,\n\t'buffered 304 with length');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/buffered/304len2'), qr/X-Connection: $n/,\n\t'buffered 304 with length 2');\n\n# unbuffered\n\nlike($r = http_get('/unbuffered/length1'), qr/SEE-THIS/, 'unbuffered');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/unbuffered/length2'), qr/X-Connection: $n/, 'unbuffered 2');\n\nlike($r = http_get('/unbuffered/chunked1'), qr/SEE-THIS/, 'unbuffered chunked');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/unbuffered/chunked2'), qr/X-Connection: $n/,\n\t'unbuffered chunked 2');\n\nlike($r = http_get('/unbuffered/complex1'), qr/(0123456789){100}/,\n\t'unbuffered complex chunked');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/unbuffered/complex2'), qr/X-Connection: $n/,\n\t'unbuffered complex chunked 2');\n\nlike($r = http_get('/unbuffered/chunk01'), qr/200 OK/, 'unbuffered 0 chunk');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/unbuffered/chunk02'), qr/X-Connection: $n/,\n\t'unbuffered 0 chunk 2');\n\nlike($r = http_get('/unbuffered/empty1'), qr/200 OK/, 'unbuffered empty');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/unbuffered/empty2'), qr/X-Connection: $n/,\n\t'unbuffered empty 2');\n\nlike($r = http_head('/unbuffered/length/head1'), qr/(?!SEE-THIS)/,\n\t'unbuffered head');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_head('/unbuffered/length/head2'), qr/X-Connection: $n/,\n\t'unbuffered head 2');\n\nlike($r = http_get('/unbuffered/304nolen1'), qr/304 Not/, 'unbuffered 304');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/unbuffered/304nolen2'), qr/X-Connection: $n/,\n\t'unbuffered 304 2');\n\nlike($r = http_get('/unbuffered/304len1'), qr/304 Not/,\n\t'unbuffered 304 with length');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/unbuffered/304len2'), qr/X-Connection: $n/,\n\t'unbuffered 304 with length 2');\n\n# in memory\n\nlike($r = http_get('/inmemory/length1'), qr/SEE-THIS/, 'inmemory');\n$r =~ m/SEE-THIS(\\d+)/; $n = $1;\nlike(http_get('/inmemory/length2'), qr/SEE-THIS$n/, 'inmemory 2');\n\nlike($r = http_get('/inmemory/empty1'), qr/200 OK/, 'inmemory empty');\n$r =~ m/SEE-THIS(\\d+)/; $n = $1;\nlike(http_get('/inmemory/empty2'), qr/200 OK/, 'inmemory empty 2');\n\nlike($r = http_get('/inmemory/chunked1'), qr/SEE-THIS/, 'inmemory chunked');\n$r =~ m/SEE-THIS(\\d+)/; $n = $1;\nlike(http_get('/inmemory/chunked2'), qr/SEE-THIS$n/, 'inmemory chunked 2');\n\nlike($r = http_get('/inmemory/complex1'), qr/(0123456789){100}/,\n\t'inmemory complex chunked');\n$r =~ m/SEE-THIS(\\d+)/; $n = $1;\nlike(http_get('/inmemory/complex2'), qr/SEE-THIS$n/,\n\t'inmemory complex chunked 2');\n\nlike(http_get('/inmemory/chunk01'), qr/set: $/, 'inmemory 0 chunk');\nlike(http_get('/inmemory/chunk02'), qr/set: $/, 'inmemory 0 chunk 2');\n\n# closed connection tests\n\nlike(http_get('/buffered/closed1'), qr/200 OK/, 'buffered closed 1');\nlike(http_get('/buffered/closed2'), qr/200 OK/, 'buffered closed 2');\nlike(http_get('/unbuffered/closed1'), qr/200 OK/, 'unbuffered closed 1');\nlike(http_get('/unbuffered/closed2'), qr/200 OK/, 'unbuffered closed 2');\nlike(http_get('/inmemory/closed1'), qr/200 OK/, 'inmemory closed 1');\nlike(http_get('/inmemory/closed2'), qr/200 OK/, 'inmemory closed 2');\n\n# check for errors, shouldn't be any\n\nlike(`grep -F '[alert]' ${\\($t->testdir())}/error.log`, qr/^$/s, 'no alerts');\nlike(`grep -F '[error]' ${\\($t->testdir())}/error.log`, qr/^$/s, 'no errors');\n\n#############################################################################\n\n# buffered\n\nlike($r = http_get('/ups1/buffered/length1'), qr/SEE-THIS/, 'buffered');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/ups1/buffered/length2'), qr/X-Connection: $n.*SEE/ms, 'buffered 2');\n\nlike($r = http_get('/ups1/buffered/chunked1'), qr/SEE-THIS/, 'buffered chunked');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/ups1/buffered/chunked2'), qr/X-Connection: $n/,\n\t'buffered chunked 2');\n\nlike($r = http_get('/ups1/buffered/complex1'), qr/(0123456789){100}/,\n\t'buffered complex chunked');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/ups1/buffered/complex2'), qr/X-Connection: $n/,\n\t'buffered complex chunked 2');\n\nlike($r = http_get('/ups1/buffered/chunk01'), qr/200 OK/, 'buffered 0 chunk');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/ups1/buffered/chunk02'), qr/X-Connection: $n/, 'buffered 0 chunk 2');\n\nlike($r = http_head('/ups1/buffered/length/head1'), qr/(?!SEE-THIS)/,\n\t'buffered head');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_head('/ups1/buffered/length/head2'), qr/X-Connection: $n/,\n\t'buffered head 2');\n\nlike($r = http_get('/ups1/buffered/empty1'), qr/200 OK/, 'buffered empty');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/ups1/buffered/empty2'), qr/X-Connection: $n/, 'buffered empty 2');\n\nlike($r = http_get('/ups1/buffered/304nolen1'), qr/304 Not/, 'buffered 304');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/ups1/buffered/304nolen2'), qr/X-Connection: $n/, 'buffered 304 2');\n\nlike($r = http_get('/ups1/buffered/304len1'), qr/304 Not/,\n\t'buffered 304 with length');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/ups1/buffered/304len2'), qr/X-Connection: $n/,\n\t'buffered 304 with length 2');\n\n# unbuffered\n\nlike($r = http_get('/ups1/unbuffered/length1'), qr/SEE-THIS/, 'unbuffered');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/ups1/unbuffered/length2'), qr/X-Connection: $n/, 'unbuffered 2');\n\nlike($r = http_get('/ups1/unbuffered/chunked1'), qr/SEE-THIS/, 'unbuffered chunked');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/ups1/unbuffered/chunked2'), qr/X-Connection: $n/,\n\t'unbuffered chunked 2');\n\nlike($r = http_get('/ups1/unbuffered/complex1'), qr/(0123456789){100}/,\n\t'unbuffered complex chunked');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/ups1/unbuffered/complex2'), qr/X-Connection: $n/,\n\t'unbuffered complex chunked 2');\n\nlike($r = http_get('/ups1/unbuffered/chunk01'), qr/200 OK/, 'unbuffered 0 chunk');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/ups1/unbuffered/chunk02'), qr/X-Connection: $n/,\n\t'unbuffered 0 chunk 2');\n\nlike($r = http_get('/ups1/unbuffered/empty1'), qr/200 OK/, 'unbuffered empty');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/ups1/unbuffered/empty2'), qr/X-Connection: $n/,\n\t'unbuffered empty 2');\n\nlike($r = http_head('/ups1/unbuffered/length/head1'), qr/(?!SEE-THIS)/,\n\t'unbuffered head');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_head('/ups1/unbuffered/length/head2'), qr/X-Connection: $n/,\n\t'unbuffered head 2');\n\nlike($r = http_get('/ups1/unbuffered/304nolen1'), qr/304 Not/, 'unbuffered 304');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/ups1/unbuffered/304nolen2'), qr/X-Connection: $n/,\n\t'unbuffered 304 2');\n\nlike($r = http_get('/ups1/unbuffered/304len1'), qr/304 Not/,\n\t'unbuffered 304 with length');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/ups1/unbuffered/304len2'), qr/X-Connection: $n/,\n\t'unbuffered 304 with length 2');\n\n# in memory\n\nlike($r = http_get('/ups1/inmemory/length1'), qr/SEE-THIS/, 'inmemory');\n$r =~ m/SEE-THIS(\\d+)/; $n = $1;\nlike(http_get('/ups1/inmemory/length2'), qr/SEE-THIS$n/, 'inmemory 2');\n\nlike($r = http_get('/ups1/inmemory/empty1'), qr/200 OK/, 'inmemory empty');\n$r =~ m/SEE-THIS(\\d+)/; $n = $1;\nlike(http_get('/ups1/inmemory/empty2'), qr/200 OK/, 'inmemory empty 2');\n\nlike($r = http_get('/ups1/inmemory/chunked1'), qr/SEE-THIS/, 'inmemory chunked');\n$r =~ m/SEE-THIS(\\d+)/; $n = $1;\nlike(http_get('/ups1/inmemory/chunked2'), qr/SEE-THIS$n/, 'inmemory chunked 2');\n\nlike($r = http_get('/ups1/inmemory/complex1'), qr/(0123456789){100}/,\n\t'inmemory complex chunked');\n$r =~ m/SEE-THIS(\\d+)/; $n = $1;\nlike(http_get('/ups1/inmemory/complex2'), qr/SEE-THIS$n/,\n\t'inmemory complex chunked 2');\n\nlike(http_get('/ups1/inmemory/chunk01'), qr/set: $/, 'inmemory 0 chunk');\nlike(http_get('/ups1/inmemory/chunk02'), qr/set: $/, 'inmemory 0 chunk 2');\n\n# closed connection tests\n\nlike(http_get('/ups1/buffered/closed1'), qr/200 OK/, 'buffered closed 1');\nlike(http_get('/ups1/buffered/closed2'), qr/200 OK/, 'buffered closed 2');\nlike(http_get('/ups1/unbuffered/closed1'), qr/200 OK/, 'unbuffered closed 1');\nlike(http_get('/ups1/unbuffered/closed2'), qr/200 OK/, 'unbuffered closed 2');\nlike(http_get('/ups1/inmemory/closed1'), qr/200 OK/, 'inmemory closed 1');\nlike(http_get('/ups1/inmemory/closed2'), qr/200 OK/, 'inmemory closed 2');\n\n#############################################################################\n\n# buffered\n\nlike($r = http_get('/ups2/buffered/length1'), qr/SEE-THIS/, 'buffered');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/ups2/buffered/length2'), qr/X-Connection: $n.*SEE/ms, 'buffered 2');\n\nlike($r = http_get('/ups2/buffered/chunked1'), qr/SEE-THIS/, 'buffered chunked');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/ups2/buffered/chunked2'), qr/X-Connection: $n/,\n\t'buffered chunked 2');\n\nlike($r = http_get('/ups2/buffered/complex1'), qr/(0123456789){100}/,\n\t'buffered complex chunked');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/ups2/buffered/complex2'), qr/X-Connection: $n/,\n\t'buffered complex chunked 2');\n\nlike($r = http_get('/ups2/buffered/chunk01'), qr/200 OK/, 'buffered 0 chunk');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/ups2/buffered/chunk02'), qr/X-Connection: $n/, 'buffered 0 chunk 2');\n\nlike($r = http_head('/ups2/buffered/length/head1'), qr/(?!SEE-THIS)/,\n\t'buffered head');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_head('/ups2/buffered/length/head2'), qr/X-Connection: $n/,\n\t'buffered head 2');\n\nlike($r = http_get('/ups2/buffered/empty1'), qr/200 OK/, 'buffered empty');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/ups2/buffered/empty2'), qr/X-Connection: $n/, 'buffered empty 2');\n\nlike($r = http_get('/ups2/buffered/304nolen1'), qr/304 Not/, 'buffered 304');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/ups2/buffered/304nolen2'), qr/X-Connection: $n/, 'buffered 304 2');\n\nlike($r = http_get('/ups2/buffered/304len1'), qr/304 Not/,\n\t'buffered 304 with length');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/ups2/buffered/304len2'), qr/X-Connection: $n/,\n\t'buffered 304 with length 2');\n\n# unbuffered\n\nlike($r = http_get('/ups2/unbuffered/length1'), qr/SEE-THIS/, 'unbuffered');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/ups2/unbuffered/length2'), qr/X-Connection: $n/, 'unbuffered 2');\n\nlike($r = http_get('/ups2/unbuffered/chunked1'), qr/SEE-THIS/, 'unbuffered chunked');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/ups2/unbuffered/chunked2'), qr/X-Connection: $n/,\n\t'unbuffered chunked 2');\n\nlike($r = http_get('/ups2/unbuffered/complex1'), qr/(0123456789){100}/,\n\t'unbuffered complex chunked');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/ups2/unbuffered/complex2'), qr/X-Connection: $n/,\n\t'unbuffered complex chunked 2');\n\nlike($r = http_get('/ups2/unbuffered/chunk01'), qr/200 OK/, 'unbuffered 0 chunk');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/ups2/unbuffered/chunk02'), qr/X-Connection: $n/,\n\t'unbuffered 0 chunk 2');\n\nlike($r = http_get('/ups2/unbuffered/empty1'), qr/200 OK/, 'unbuffered empty');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/ups2/unbuffered/empty2'), qr/X-Connection: $n/,\n\t'unbuffered empty 2');\n\nlike($r = http_head('/ups2/unbuffered/length/head1'), qr/(?!SEE-THIS)/,\n\t'unbuffered head');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_head('/ups2/unbuffered/length/head2'), qr/X-Connection: $n/,\n\t'unbuffered head 2');\n\nlike($r = http_get('/ups2/unbuffered/304nolen1'), qr/304 Not/, 'unbuffered 304');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/ups2/unbuffered/304nolen2'), qr/X-Connection: $n/,\n\t'unbuffered 304 2');\n\nlike($r = http_get('/ups2/unbuffered/304len1'), qr/304 Not/,\n\t'unbuffered 304 with length');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/ups2/unbuffered/304len2'), qr/X-Connection: $n/,\n\t'unbuffered 304 with length 2');\n\n# in memory\n\nlike($r = http_get('/ups2/inmemory/length1'), qr/SEE-THIS/, 'inmemory');\n$r =~ m/SEE-THIS(\\d+)/; $n = $1;\nlike(http_get('/ups2/inmemory/length2'), qr/SEE-THIS$n/, 'inmemory 2');\n\nlike($r = http_get('/ups2/inmemory/empty1'), qr/200 OK/, 'inmemory empty');\n$r =~ m/SEE-THIS(\\d+)/; $n = $1;\nlike(http_get('/ups2/inmemory/empty2'), qr/200 OK/, 'inmemory empty 2');\n\nlike($r = http_get('/ups2/inmemory/chunked1'), qr/SEE-THIS/, 'inmemory chunked');\n$r =~ m/SEE-THIS(\\d+)/; $n = $1;\nlike(http_get('/ups2/inmemory/chunked2'), qr/SEE-THIS$n/, 'inmemory chunked 2');\n\nlike($r = http_get('/ups2/inmemory/complex1'), qr/(0123456789){100}/,\n\t'inmemory complex chunked');\n$r =~ m/SEE-THIS(\\d+)/; $n = $1;\nlike(http_get('/ups2/inmemory/complex2'), qr/SEE-THIS$n/,\n\t'inmemory complex chunked 2');\n\nlike(http_get('/ups2/inmemory/chunk01'), qr/set: $/, 'inmemory 0 chunk');\nlike(http_get('/ups2/inmemory/chunk02'), qr/set: $/, 'inmemory 0 chunk 2');\n\n# closed connection tests\n\nlike(http_get('/ups2/buffered/closed1'), qr/200 OK/, 'buffered closed 1');\nlike(http_get('/ups2/buffered/closed2'), qr/200 OK/, 'buffered closed 2');\nlike(http_get('/ups2/unbuffered/closed1'), qr/200 OK/, 'unbuffered closed 1');\nlike(http_get('/ups2/unbuffered/closed2'), qr/200 OK/, 'unbuffered closed 2');\nlike(http_get('/ups2/inmemory/closed1'), qr/200 OK/, 'inmemory closed 1');\nlike(http_get('/ups2/inmemory/closed2'), qr/200 OK/, 'inmemory closed 2');\n\n###############################################################################\n\n# buffered\n\nlike($r = http_get('/ups3/buffered/length1'), qr/SEE-THIS/, 'buffered');\n$r =~ m/X-Connection: (\\d+)/; $n = $1; $n += 1;\nlike(http_get('/ups3/buffered/length2'), qr/X-Connection: $n.*SEE/ms, 'buffered 2');\n\nlike($r = http_get('/ups3/buffered/chunked1'), qr/SEE-THIS/, 'buffered chunked');\n$r =~ m/X-Connection: (\\d+)/; $n = $1; $n += 1;\nlike(http_get('/ups3/buffered/chunked2'), qr/X-Connection: $n/,\n\t'buffered chunked 2');\n\nlike($r = http_get('/ups3/buffered/complex1'), qr/(0123456789){100}/,\n\t'buffered complex chunked');\n$r =~ m/X-Connection: (\\d+)/; $n = $1; $n += 1;\nlike(http_get('/ups3/buffered/complex2'), qr/X-Connection: $n/,\n\t'buffered complex chunked 2');\n\nlike($r = http_get('/ups3/buffered/chunk01'), qr/200 OK/, 'buffered 0 chunk');\n$r =~ m/X-Connection: (\\d+)/; $n = $1; $n += 1;\nlike(http_get('/ups3/buffered/chunk02'), qr/X-Connection: $n/, 'buffered 0 chunk 2');\n\nlike($r = http_head('/ups3/buffered/length/head1'), qr/(?!SEE-THIS)/,\n\t'buffered head');\n$r =~ m/X-Connection: (\\d+)/; $n = $1; $n += 1;\nlike(http_head('/ups3/buffered/length/head2'), qr/X-Connection: $n/,\n\t'buffered head 2');\n\nlike($r = http_get('/ups3/buffered/empty1'), qr/200 OK/, 'buffered empty');\n$r =~ m/X-Connection: (\\d+)/; $n = $1; $n += 1;\nlike(http_get('/ups3/buffered/empty2'), qr/X-Connection: $n/, 'buffered empty 2');\n\nlike($r = http_get('/ups3/buffered/304nolen1'), qr/304 Not/, 'buffered 304');\n$r =~ m/X-Connection: (\\d+)/; $n = $1; $n += 1;\nlike(http_get('/ups3/buffered/304nolen2'), qr/X-Connection: $n/, 'buffered 304 2');\n\nlike($r = http_get('/ups3/buffered/304len1'), qr/304 Not/,\n\t'buffered 304 with length');\n$r =~ m/X-Connection: (\\d+)/; $n = $1; $n += 1;\nlike(http_get('/ups3/buffered/304len2'), qr/X-Connection: $n/,\n\t'buffered 304 with length 2');\n\n# unbuffered\n\nlike($r = http_get('/ups3/unbuffered/length1'), qr/SEE-THIS/, 'unbuffered');\n$r =~ m/X-Connection: (\\d+)/; $n = $1; $n += 1;\nlike(http_get('/ups3/unbuffered/length2'), qr/X-Connection: $n/, 'unbuffered 2');\n\nlike($r = http_get('/ups3/unbuffered/chunked1'), qr/SEE-THIS/, 'unbuffered chunked');\n$r =~ m/X-Connection: (\\d+)/; $n = $1; $n += 1;\nlike(http_get('/ups3/unbuffered/chunked2'), qr/X-Connection: $n/,\n\t'unbuffered chunked 2');\n\nlike($r = http_get('/ups3/unbuffered/complex1'), qr/(0123456789){100}/,\n\t'unbuffered complex chunked');\n$r =~ m/X-Connection: (\\d+)/; $n = $1; $n += 1;\nlike(http_get('/ups3/unbuffered/complex2'), qr/X-Connection: $n/,\n\t'unbuffered complex chunked 2');\n\nlike($r = http_get('/ups3/unbuffered/chunk01'), qr/200 OK/, 'unbuffered 0 chunk');\n$r =~ m/X-Connection: (\\d+)/; $n = $1; $n += 1;\nlike(http_get('/ups3/unbuffered/chunk02'), qr/X-Connection: $n/,\n\t'unbuffered 0 chunk 2');\n\nlike($r = http_get('/ups3/unbuffered/empty1'), qr/200 OK/, 'unbuffered empty');\n$r =~ m/X-Connection: (\\d+)/; $n = $1; $n += 1;\nlike(http_get('/ups3/unbuffered/empty2'), qr/X-Connection: $n/,\n\t'unbuffered empty 2');\n\nlike($r = http_head('/ups3/unbuffered/length/head1'), qr/(?!SEE-THIS)/,\n\t'unbuffered head');\n$r =~ m/X-Connection: (\\d+)/; $n = $1; $n += 1;\nlike(http_head('/ups3/unbuffered/length/head2'), qr/X-Connection: $n/,\n\t'unbuffered head 2');\n\nlike($r = http_get('/ups3/unbuffered/304nolen1'), qr/304 Not/, 'unbuffered 304');\n$r =~ m/X-Connection: (\\d+)/; $n = $1; $n += 1;\nlike(http_get('/ups3/unbuffered/304nolen2'), qr/X-Connection: $n/,\n\t'unbuffered 304 2');\n\nlike($r = http_get('/ups3/unbuffered/304len1'), qr/304 Not/,\n\t'unbuffered 304 with length');\n$r =~ m/X-Connection: (\\d+)/; $n = $1; $n += 1;\nlike(http_get('/ups3/unbuffered/304len2'), qr/X-Connection: $n/,\n\t'unbuffered 304 with length 2');\n\n# in memory\n\nlike($r = http_get('/ups3/inmemory/length1'), qr/SEE-THIS/, 'inmemory');\n$r =~ m/SEE-THIS(\\d+)/; $n = $1; $n += 1; $n = sprintf(\"%03d\", $n);\nlike(http_get('/ups3/inmemory/length2'), qr/SEE-THIS$n/, 'inmemory 2');\n\nlike($r = http_get('/ups3/inmemory/empty1'), qr/200 OK/, 'inmemory empty');\n$r =~ m/SEE-THIS(\\d+)/; $n = $1; $n += 1; $n = sprintf(\"%03d\", $n);\nlike(http_get('/ups3/inmemory/empty2'), qr/200 OK/, 'inmemory empty 2');\n\nlike($r = http_get('/ups3/inmemory/chunked1'), qr/SEE-THIS/, 'inmemory chunked');\n$r =~ m/SEE-THIS(\\d+)/; $n = $1; $n += 1; $n = sprintf(\"%03d\", $n);\nlike(http_get('/ups3/inmemory/chunked2'), qr/SEE-THIS$n/, 'inmemory chunked 2');\n\nlike($r = http_get('/ups3/inmemory/complex1'), qr/(0123456789){100}/,\n\t'inmemory complex chunked');\n$r =~ m/SEE-THIS(\\d+)/; $n = $1; $n += 1; $n = sprintf(\"%03d\", $n);\nlike(http_get('/ups3/inmemory/complex2'), qr/SEE-THIS$n/,\n\t'inmemory complex chunked 2');\n\nlike(http_get('/ups3/inmemory/chunk01'), qr/set: $/, 'inmemory 0 chunk');\nlike(http_get('/ups3/inmemory/chunk02'), qr/set: $/, 'inmemory 0 chunk 2');\n\n# closed connection tests\n\nlike(http_get('/ups3/buffered/closed1'), qr/200 OK/, 'buffered closed 1');\nlike(http_get('/ups3/buffered/closed2'), qr/200 OK/, 'buffered closed 2');\nlike(http_get('/ups3/unbuffered/closed1'), qr/200 OK/, 'unbuffered closed 1');\nlike(http_get('/ups3/unbuffered/closed2'), qr/200 OK/, 'unbuffered closed 2');\nlike(http_get('/ups3/inmemory/closed1'), qr/200 OK/, 'inmemory closed 1');\nlike(http_get('/ups3/inmemory/closed2'), qr/200 OK/, 'inmemory closed 2');\n\n###############################################################################\n\n# buffered\n\nlike($r = http_get('/ups4/buffered/length1'), qr/SEE-THIS/, 'buffered');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/ups4/buffered/length2'), qr/X-Connection: $n.*SEE/ms, 'buffered 2');\nhttp_get('/ups4/buffered/length/closed');\n\nlike($r = http_get('/ups4/buffered/chunked1'), qr/SEE-THIS/, 'buffered chunked');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/ups4/buffered/chunked2'), qr/X-Connection: $n/,\n\t'buffered chunked 2');\nhttp_get('/ups4/buffered/chunked/closed');\n\nlike($r = http_get('/ups4/buffered/complex1'), qr/(0123456789){100}/,\n\t'buffered complex chunked');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/ups4/buffered/complex2'), qr/X-Connection: $n/,\n\t'buffered complex chunked 2');\nhttp_get('/ups4/buffered/complex/closed');\n\nlike($r = http_get('/ups4/buffered/chunk01'), qr/200 OK/, 'buffered 0 chunk');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/ups4/buffered/chunk02'), qr/X-Connection: $n/, 'buffered 0 chunk 2');\nhttp_get('/ups4/buffered/chunk0/closed');\n\nlike($r = http_head('/ups4/buffered/length/head1'), qr/(?!SEE-THIS)/,\n\t'buffered head');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_head('/ups4/buffered/length/head2'), qr/X-Connection: $n/,\n\t'buffered head 2');\nhttp_get('/ups4/buffered/length/closed');\n\nlike($r = http_get('/ups4/buffered/empty1'), qr/200 OK/, 'buffered empty');\nlike(http_get('/ups4/buffered/empty2'), qr/504 Gateway Time-out/, 'buffered empty 2');\nhttp_get('/ups4/buffered/empty1/closed');\n\nlike($r = http_get('/ups4/buffered/304nolen1'), qr/304 Not/, 'buffered 304');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/ups4/buffered/304nolen2'), qr/X-Connection: $n/, 'buffered 304 2');\nhttp_get('/ups4/buffered/304nolen/closed');\n\nlike($r = http_get('/ups4/buffered/304len1'), qr/304 Not/,\n\t'buffered 304 with length');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/ups4/buffered/304len2'), qr/X-Connection: $n/,\n\t'buffered 304 with length 2');\nhttp_get('/ups4/buffered/304len/closed');\n\n# unbuffered\n\nlike($r = http_get('/ups4/unbuffered/length1'), qr/SEE-THIS/, 'unbuffered');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/ups4/unbuffered/length2'), qr/X-Connection: $n/, 'unbuffered 2');\nhttp_get('/ups4/unbuffered/length/closed');\n\nlike($r = http_get('/ups4/unbuffered/chunked1'), qr/SEE-THIS/, 'unbuffered chunked');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/ups4/unbuffered/chunked2'), qr/X-Connection: $n/,\n\t'unbuffered chunked 2');\nhttp_get('/ups4/unbuffered/chunked/closed');\n\nlike($r = http_get('/ups4/unbuffered/complex1'), qr/(0123456789){100}/,\n\t'unbuffered complex chunked');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/ups4/unbuffered/complex2'), qr/X-Connection: $n/,\n\t'unbuffered complex chunked 2');\nhttp_get('/ups4/unbuffered/complex/closed');\n\nlike($r = http_get('/ups4/unbuffered/chunk01'), qr/200 OK/, 'unbuffered 0 chunk');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/ups4/unbuffered/chunk02'), qr/X-Connection: $n/,\n\t'unbuffered 0 chunk 2');\nhttp_get('/ups4/unbuffered/chunk0/closed');\n\nlike($r = http_get('/ups4/unbuffered/empty1'), qr/200 OK/, 'unbuffered empty');\nlike(http_get('/ups4/unbuffered/empty2'), qr/504 Gateway Time-out/,\n\t'unbuffered empty 2');\nhttp_get('/ups4/unbuffered/empty1/closed');\n\nlike($r = http_head('/ups4/unbuffered/length/head1'), qr/(?!SEE-THIS)/,\n\t'unbuffered head');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_head('/ups4/unbuffered/length/head2'), qr/X-Connection: $n/,\n\t'unbuffered head 2');\nhttp_get('/ups4/unbuffered/length/closed');\n\nlike($r = http_get('/ups4/unbuffered/304nolen1'), qr/304 Not/, 'unbuffered 304');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/ups4/unbuffered/304nolen2'), qr/X-Connection: $n/,\n\t'unbuffered 304 2');\nhttp_get('/ups4/unbuffered/304nolen/closed');\n\nlike($r = http_get('/ups4/unbuffered/304len1'), qr/304 Not/,\n\t'unbuffered 304 with length');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/ups4/unbuffered/304len2'), qr/X-Connection: $n/,\n\t'unbuffered 304 with length 2');\nhttp_get('/ups4/unbuffered/304len/closed');\n\n# in memory\n\nlike($r = http_get('/ups4/inmemory/length1'), qr/SEE-THIS/, 'inmemory');\n$r =~ m/SEE-THIS(\\d+)/; $n = $1;\nlike(http_get('/ups4/inmemory/length2'), qr/SEE-THIS$n/, 'inmemory 2');\nhttp_get('/ups4/inmemory/length/closed');\n\nlike($r = http_get('/ups4/inmemory/empty1'), qr/200 OK/, 'inmemory empty');\n$r =~ m/SEE-THIS(\\d+)/; $n = $1;\nlike(http_get('/ups4/inmemory/empty2'), qr/200 OK/,\n        'inmemory empty 2');\nhttp_get('/ups4/inmemory/empty1/closed');\n\nlike($r = http_get('/ups4/inmemory/chunked1'), qr/SEE-THIS/, 'inmemory chunked');\n$r =~ m/SEE-THIS(\\d+)/; $n = $1;\nlike(http_get('/ups4/inmemory/chunked2'), qr/SEE-THIS$n/, 'inmemory chunked 2');\nhttp_get('/ups4/inmemory/chunked/closed');\n\nlike($r = http_get('/ups4/inmemory/complex1'), qr/(0123456789){100}/,\n\t'inmemory complex chunked');\n$r =~ m/SEE-THIS(\\d+)/; $n = $1;\nlike(http_get('/ups4/inmemory/complex2'), qr/SEE-THIS$n/,\n\t'inmemory complex chunked 2');\nhttp_get('/ups4/inmemory/complex/closed');\n\nlike(http_get('/ups4/inmemory/chunk01'), qr/set: $/, 'inmemory 0 chunk');\nlike(http_get('/ups4/inmemory/chunk02'), qr/set: $/, 'inmemory 0 chunk 2');\nhttp_get('/ups4/inmemory/chunk0/closed');\n\n# closed connection tests\n\nlike(http_get('/ups4/buffered/closed1'), qr/200 OK/, 'buffered closed 1');\nlike(http_get('/ups4/buffered/closed2'), qr/200 OK/, 'buffered closed 2');\nlike(http_get('/ups4/unbuffered/closed1'), qr/200 OK/, 'unbuffered closed 1');\nlike(http_get('/ups4/unbuffered/closed2'), qr/200 OK/, 'unbuffered closed 2');\nlike(http_get('/ups4/inmemory/closed1'), qr/200 OK/, 'inmemory closed 1');\nlike(http_get('/ups4/inmemory/closed2'), qr/200 OK/, 'inmemory closed 2');\n\n###############################################################################\n\nsub http_daemon {\n\tmy $server = IO::Socket::INET->new(\n\t\tProto => 'tcp',\n\t\tLocalHost => '127.0.0.1:8081',\n\t\tListen => 5,\n\t\tReuse => 1\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\tmy $ccount = 0;\n\tmy $rcount = 0;\n\n\t# dumb server which is able to keep connections alive\n\n\twhile (my $client = $server->accept()) {\n\t\tTest::Nginx::log_core('||',\n\t\t\t\"connection from \" . $client->peerhost());\n\t\t$client->autoflush(1);\n\t\t$ccount++;\n\n\t\twhile (1) {\n\t\t\tmy $headers = '';\n\t\t\tmy $uri = '';\n\n\t\t\twhile (<$client>) {\n\t\t\t\tTest::Nginx::log_core('||', $_);\n\t\t\t\t$headers .= $_;\n\t\t\t\tlast if (/^\\x0d?\\x0a?$/);\n\t\t\t}\n\n\t\t\tlast if $headers eq '';\n\t\t\t$rcount++;\n\n\t\t\t$uri = $1 if $headers =~ /^\\S+\\s+([^ ]+)\\s+HTTP/i;\n\n\t\t\tif ($uri =~ m/closed/) {\n\t\t\t\tprint $client\n\t\t\t\t\t\"HTTP/1.1 200 OK\" . CRLF .\n\t\t\t\t\t\"X-Request: $rcount\" . CRLF .\n\t\t\t\t\t\"X-Connection: $ccount\" . CRLF .\n\t\t\t\t\t\"Connection: close\" . CRLF .\n\t\t\t\t\t\"Content-Length: 12\" . CRLF . CRLF .\n\t\t\t\t\t\"0123456789\" . CRLF;\n\t\t\t\tlast;\n\n\t\t\t} elsif ($uri =~ m/length/) {\n\t\t\t\tprint $client\n\t\t\t\t\t\"HTTP/1.1 200 OK\" . CRLF .\n\t\t\t\t\t\"X-Request: $rcount\" . CRLF .\n\t\t\t\t\t\"X-Connection: $ccount\" . CRLF .\n\t\t\t\t\t\"Content-Length: 26\" . CRLF . CRLF;\n\t\t\t\tprint $client \"TEST-OK-IF-YOU-SEE-THIS\" .\n\t\t\t\t\tsprintf(\"%03d\", $ccount)\n\t\t\t\t\tunless $headers =~ /^HEAD/i;\n\n\t\t\t} elsif ($uri =~ m/empty/) {\n\t\t\t\tprint $client\n\t\t\t\t\t\"HTTP/1.1 200 OK\" . CRLF .\n\t\t\t\t\t\"X-Request: $rcount\" . CRLF .\n\t\t\t\t\t\"X-Connection: $ccount\" . CRLF .\n\t\t\t\t\t\"Content-Length: 0\" . CRLF . CRLF;\n\n\t\t\t} elsif ($uri =~ m/304nolen/) {\n\t\t\t\tprint $client\n\t\t\t\t\t\"HTTP/1.1 304 Not Modified\" . CRLF .\n\t\t\t\t\t\"X-Request: $rcount\" . CRLF .\n\t\t\t\t\t\"X-Connection: $ccount\" . CRLF . CRLF;\n\n\t\t\t} elsif ($uri =~ m/304len/) {\n\t\t\t\tprint $client\n\t\t\t\t\t\"HTTP/1.1 304 Not Modified\" . CRLF .\n\t\t\t\t\t\"X-Request: $rcount\" . CRLF .\n\t\t\t\t\t\"X-Connection: $ccount\" . CRLF .\n\t\t\t\t\t\"Content-Length: 100\" . CRLF . CRLF;\n\n\t\t\t} elsif ($uri =~ m/chunked/) {\n\t\t\t\tprint $client\n\t\t\t\t\t\"HTTP/1.1 200 OK\" . CRLF .\n\t\t\t\t\t\"X-Request: $rcount\" . CRLF .\n\t\t\t\t\t\"X-Connection: $ccount\" . CRLF .\n\t\t\t\t\t\"Transfer-Encoding: chunked\" . CRLF .\n\t\t\t\t\tCRLF;\n\t\t\t\tprint $client\n\t\t\t\t\t\"1a\" . CRLF .\n\t\t\t\t\t\"TEST-OK-IF-YOU-SEE-THIS\" .\n\t\t\t\t\tsprintf(\"%03d\", $ccount) . CRLF .\n\t\t\t\t\t\"0\" . CRLF . CRLF\n\t\t\t\t\tunless $headers =~ /^HEAD/i;\n\n\t\t\t} elsif ($uri =~ m/complex/) {\n\t\t\t\tprint $client\n\t\t\t\t\t\"HTTP/1.1 200 OK\" . CRLF .\n\t\t\t\t\t\"X-Request: $rcount\" . CRLF .\n\t\t\t\t\t\"X-Connection: $ccount\" . CRLF .\n\t\t\t\t\t\"Transfer-Encoding: chunked\" . CRLF .\n\t\t\t\t\tCRLF;\n\n\t\t\t\tif ($headers !~ /^HEAD/i) {\n\t\t\t\t\tfor my $n (1..100) {\n\t\t\t\t\t\tprint $client\n\t\t\t\t\t\t\t\"a\" . CRLF .\n\t\t\t\t\t\t\t\"0123456789\" . CRLF;\n\t\t\t\t\t\tselect undef, undef, undef, 0.01\n\t\t\t\t\t\t\tif $n % 50 == 0;\n\t\t\t\t\t}\n\t\t\t\t\tprint $client\n\t\t\t\t\t\t\"1a\" . CRLF .\n\t\t\t\t\t\t\"TEST-OK-IF-YOU-SEE-THIS\" .\n\t\t\t\t\t\tsprintf(\"%03d\", $ccount) .\n\t\t\t\t\t\tCRLF .\n\t\t\t\t\t\t\"0\" . CRLF;\n\t\t\t\t\tselect undef, undef, undef, 0.05;\n\t\t\t\t\tprint $client CRLF;\n\t\t\t\t}\n\n\t\t\t} elsif ($uri =~ m/chunk0/) {\n\t\t\t\tprint $client\n\t\t\t\t\t\"HTTP/1.1 200 OK\" . CRLF .\n\t\t\t\t\t\"X-Request: $rcount\" . CRLF .\n\t\t\t\t\t\"X-Connection: $ccount\" . CRLF .\n\t\t\t\t\t\"Transfer-Encoding: chunked\" . CRLF .\n\t\t\t\t\tCRLF;\n\t\t\t\tprint $client\n\t\t\t\t\t\"0\" . CRLF . CRLF\n\t\t\t\t\tunless $headers =~ /^HEAD/i;\n\n\t\t\t} else {\n\t\t\t\tprint $client\n\t\t\t\t\t\"HTTP/1.1 404 Not Found\" . CRLF .\n\t\t\t\t\t\"X-Request: $rcount\" . CRLF .\n\t\t\t\t\t\"X-Connection: $ccount\" . CRLF .\n\t\t\t\t\t\"Connection: close\" . CRLF . CRLF .\n\t\t\t\t\t\"Oops, '$uri' not found\" . CRLF;\n\t\t\t\tlast;\n\t\t\t}\n\t\t}\n\n\t\tclose $client;\n\t}\n}\n\n###############################################################################\n"
  },
  {
    "path": "modules/ngx_http_upstream_session_sticky_module/config",
    "content": "ngx_addon_name=ngx_http_upstream_session_sticky_module\n\n# Build for recent nginx versions\nif test -n \"$ngx_module_link\"; then\n\n    ngx_module_type=HTTP_AUX_FILTER\n    ngx_module_name=ngx_http_upstream_session_sticky_module\n    ngx_module_incs=\n    ngx_module_deps=\n    ngx_module_srcs=\"$ngx_addon_dir/ngx_http_upstream_session_sticky_module.c\"\n    ngx_module_libs=\n\n    . auto/module\n\n# Build for older nginx versions\nelse\n\n    HTTP_AUX_FILTER_MODULES=\"$HTTP_AUX_FILTER_MODULES ngx_http_upstream_session_sticky_module\"\n    NGX_ADDON_SRCS=\"$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_upstream_session_sticky_module.c\"\n\nfi\n"
  },
  {
    "path": "modules/ngx_http_upstream_session_sticky_module/ngx_http_upstream_session_sticky_module.c",
    "content": "\n/*\n * Copyright (C) 2010-2013 Alibaba Group Holding Limited\n */\n\n\n#include <ngx_core.h>\n#include <ngx_http.h>\n#include <ngx_config.h>\n#include <ngx_md5.h>\n\n\n#define NGX_HTTP_SESSION_STICKY_DELIMITER       '|'\n#define NGX_HTTP_SESSION_STICKY_PREFIX          0x0001\n#define NGX_HTTP_SESSION_STICKY_INDIRECT        0x0002\n#define NGX_HTTP_SESSION_STICKY_INSERT          0x0004\n#define NGX_HTTP_SESSION_STICKY_REWRITE         0x0008\n#define NGX_HTTP_SESSION_STICKY_FALLBACK_ON     0x0010\n#define NGX_HTTP_SESSION_STICKY_FALLBACK_OFF    0x0020\n#define NGX_HTTP_SESSION_STICKY_MD5             0x0040\n#define NGX_HTTP_SESSION_STICKY_PLAIN           0X0080\n\n#define is_space(c) ((c) == ' ' || (c) == '\\t' || (c) == '\\n')\n\n\ntypedef struct {\n    ngx_str_t                           sid;\n    ngx_str_t                          *name;\n    ngx_http_upstream_rr_peer_t        *peer;\n    struct sockaddr                    *sockaddr;\n    socklen_t                           socklen;\n\n#if (NGX_HTTP_UPSTREAM_CHECK)\n    ngx_uint_t                          check_index;\n#endif\n} ngx_http_ss_server_t;\n\n\ntypedef struct {\n    ngx_uint_t                          flag;\n\n    ngx_int_t                           maxidle;\n    ngx_int_t                           maxlife;\n    ngx_str_t                           cookie;\n    ngx_str_t                           domain;\n    ngx_str_t                           path;\n    ngx_str_t                           maxage;\n\n    ngx_uint_t                          number;\n    ngx_http_ss_server_t               *server;\n} ngx_http_upstream_ss_srv_conf_t;\n\n\ntypedef struct {\n    ngx_http_upstream_srv_conf_t       *uscf;\n} ngx_http_ss_loc_conf_t;\n\n\ntypedef struct {\n    time_t                              lastseen;\n    time_t                              firstseen;\n    ngx_str_t                           s_lastseen;\n    ngx_str_t                           s_firstseen;\n    ngx_str_t                           sid;\n\n    ngx_int_t                           tries;\n    ngx_flag_t                          frist;\n\n    ngx_http_upstream_ss_srv_conf_t    *sscf;\n} ngx_http_ss_ctx_t;\n\n\ntypedef struct {\n    /* the round robin data must be first */\n    ngx_http_upstream_rr_peer_data_t    rrp;\n    ngx_http_request_t                 *r;\n\n    ngx_event_get_peer_pt               get_rr_peer;\n\n    ngx_http_upstream_ss_srv_conf_t    *sscf;\n} ngx_http_upstream_ss_peer_data_t;\n\n\nstatic ngx_int_t\n    ngx_http_upstream_session_sticky_init_peer(ngx_http_request_t *r,\n    ngx_http_upstream_srv_conf_t *us);\nstatic ngx_int_t\n    ngx_http_upstream_session_sticky_get_peer(ngx_peer_connection_t *pc,\n    void *data);\nstatic ngx_int_t ngx_http_session_sticky_header_handler(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_session_sticky_get_cookie(ngx_http_request_t *r);\nstatic void ngx_http_session_sticky_tmtoa(ngx_http_request_t *r,\n    ngx_str_t *str, time_t t);\nstatic ngx_int_t ngx_http_session_sticky_header_filter(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_session_sticky_prefix(ngx_http_request_t *r,\n    ngx_table_elt_t *table);\nstatic ngx_int_t ngx_http_session_sticky_rewrite(ngx_http_request_t *r,\n    ngx_table_elt_t *table);\nstatic ngx_int_t ngx_http_session_sticky_insert(ngx_http_request_t *r);\n\nstatic void *ngx_http_upstream_session_sticky_create_srv_conf(ngx_conf_t *cf);\nstatic void *ngx_http_session_sticky_create_loc_conf(ngx_conf_t *cf);\nstatic char *ngx_http_session_sticky_merge_loc_conf(ngx_conf_t *cf,\n    void *parent, void *child);\n\nstatic ngx_int_t ngx_http_session_sticky_init(ngx_conf_t *cf);\nstatic char *ngx_http_upstream_session_sticky(ngx_conf_t *cf,\n    ngx_command_t *cmd, void *conf);\nstatic char *ngx_http_session_sticky_hide_cookie(ngx_conf_t *cf,\n    ngx_command_t *cmd,\n    void *conf);\nstatic ngx_int_t ngx_http_upstream_session_sticky_init_upstream(ngx_conf_t *cf,\n    ngx_http_upstream_srv_conf_t *us);\nstatic ngx_int_t ngx_http_upstream_session_sticky_set_sid(ngx_conf_t *cf,\n    ngx_http_ss_server_t *s);\n\n\nstatic ngx_conf_deprecated_t ngx_conf_deprecated_session_sticky_header = {\n    ngx_conf_deprecated, \"session_sticky_header\", \"session_sticky_hide_cookie\"\n};\n\nstatic ngx_http_output_header_filter_pt ngx_http_ss_next_header_filter;\n\n\nstatic ngx_command_t ngx_http_session_sticky_commands[] = {\n\n    { ngx_string(\"session_sticky\"),\n      NGX_HTTP_UPS_CONF|NGX_CONF_ANY|NGX_CONF_1MORE,\n      ngx_http_upstream_session_sticky,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"session_sticky_hide_cookie\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_http_session_sticky_hide_cookie,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"session_sticky_header\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_http_session_sticky_hide_cookie,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t ngx_http_session_sticky_ctx = {\n    NULL,                                /* preconfiguration */\n    ngx_http_session_sticky_init,        /* postconfiguration */\n\n    NULL,                                /* create main configuration */\n    NULL,                                /* init main configuration */\n\n    ngx_http_upstream_session_sticky_create_srv_conf,\n                                         /* create server configuration */\n    NULL,                                /* merge server configuration */\n\n    ngx_http_session_sticky_create_loc_conf,\n                                         /* create location configuration */\n    ngx_http_session_sticky_merge_loc_conf\n                                         /* merge location configuration */\n};\n\n\nngx_module_t ngx_http_upstream_session_sticky_module = {\n    NGX_MODULE_V1,\n    &ngx_http_session_sticky_ctx,        /* module context */\n    ngx_http_session_sticky_commands,    /* 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_upstream_session_sticky_init_peer(ngx_http_request_t *r,\n    ngx_http_upstream_srv_conf_t *us)\n{\n    ngx_int_t                          rc;\n    ngx_http_ss_ctx_t                 *ctx;\n    ngx_http_upstream_ss_srv_conf_t   *sscf;\n    ngx_http_upstream_ss_peer_data_t  *sspd;\n\n    sspd = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_ss_peer_data_t));\n    if (sspd == NULL) {\n        return NGX_ERROR;\n    }\n\n    r->upstream->peer.data = &sspd->rrp;\n    rc = ngx_http_upstream_init_round_robin_peer(r, us);\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    sscf = ngx_http_conf_upstream_srv_conf(us,\n                                    ngx_http_upstream_session_sticky_module);\n    ctx = ngx_http_get_module_ctx(r, ngx_http_upstream_session_sticky_module);\n    if (ctx == NULL) {\n        ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_ss_ctx_t));\n        if (ctx == NULL) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"session sticky ctx allocated failed\");\n            return NGX_ERROR;\n        }\n\n        ctx->sscf = sscf;\n\n        ngx_http_set_ctx(r, ctx, ngx_http_upstream_session_sticky_module);\n\n        rc = ngx_http_session_sticky_get_cookie(r);\n        if (rc != NGX_OK) {\n            return rc;\n        }\n\n    } else {\n        if (ctx->sscf != sscf) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                \"different sscf with header_handler\");\n        }\n    }\n\n    sspd->r = r;\n    sspd->sscf = sscf;\n    sspd->get_rr_peer = ngx_http_upstream_get_round_robin_peer;\n\n    r->upstream->peer.get = ngx_http_upstream_session_sticky_get_peer;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_session_sticky_header_handler(ngx_http_request_t *r)\n{\n    ngx_http_ss_ctx_t               *ctx;\n    ngx_http_ss_loc_conf_t          *slcf;\n    ngx_http_upstream_srv_conf_t    *uscf;\n    ngx_http_upstream_ss_srv_conf_t *sscf;\n\n    slcf = ngx_http_get_module_loc_conf(r,\n                                    ngx_http_upstream_session_sticky_module);\n\n    if (slcf->uscf == NGX_CONF_UNSET_PTR) {\n        return NGX_DECLINED;\n    }\n\n    uscf = slcf->uscf;\n    sscf = ngx_http_conf_upstream_srv_conf(uscf,\n                                    ngx_http_upstream_session_sticky_module);\n    if (sscf != NULL &&\n        (sscf->flag & NGX_HTTP_SESSION_STICKY_REWRITE)) {\n        return NGX_DECLINED;\n    }\n\n    ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_ss_ctx_t));\n    if (ctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_http_set_ctx(r, ctx, ngx_http_upstream_session_sticky_module);\n    ctx->sscf = sscf;\n\n    return ngx_http_session_sticky_get_cookie(r);\n}\n\n\nstatic ngx_int_t\nngx_http_session_sticky_get_cookie(ngx_http_request_t *r)\n{\n    time_t                           now;\n    u_char                          *p, *v, *vv, *st, *last, *end;\n    ngx_int_t                        diff, delimiter, legal;\n    ngx_str_t                       *cookie = NULL;\n    ngx_http_ss_ctx_t               *ctx;\n    ngx_http_upstream_ss_srv_conf_t *sscf;\n    ngx_table_elt_t                 *cookies;\n    enum {\n        pre_key = 0,\n        key,\n        pre_equal,\n        pre_value,\n        value\n    } state;\n\n    legal = 1;\n    ctx = ngx_http_get_module_ctx(r, ngx_http_upstream_session_sticky_module);\n    sscf = ctx->sscf;\n    ctx->tries = 1;\n\n    p = NULL;\n    now = ngx_time();\n\n    for (cookies = r->headers_in.cookie; cookies; cookies = cookies->next) {\n        cookie = &cookies->value;\n        p = ngx_strnstr(cookie->data, (char *) sscf->cookie.data, cookie->len);\n        if (p == NULL) {\n            continue;\n        }\n\n        if (*(p + sscf->cookie.len) == ' ' || *(p + sscf->cookie.len) == '=') {\n            break;\n        }\n    }\n\n    if (cookies == NULL || cookie == NULL || cookie->len == 0) {\n        goto not_found;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"session sticky cookie: \\\"%V\\\"\", cookie);\n    st = p;\n    v = p + sscf->cookie.len + 1;\n    last = cookie->data + cookie->len;\n\n    state = 0;\n    while (p < last) {\n        switch (state) {\n        case pre_key:\n            if (*p == ';') {\n                goto not_found;\n\n            } else if (!is_space(*p)) {\n                state = key;\n            }\n\n            break;\n\n        case key:\n            if (is_space(*p)) {\n                state = pre_equal;\n\n            } else if (*p == '=') {\n                state = pre_value;\n            }\n\n            break;\n\n        case pre_equal:\n            if (*p == '=') {\n                state = pre_value;\n\n            } else if (!is_space(*p)) {\n                goto not_found;\n            }\n\n            break;\n\n        case pre_value:\n            if (!is_space(*p)) {\n                state = value;\n                v = p--;\n            }\n\n            break;\n\n        case value:\n            if (*p == ';') {\n                end = p + 1;\n                goto success;\n            }\n\n            if (p + 1 == last) {\n                end = last;\n                p++;\n                goto success;\n            }\n\n            break;\n\n        default:\n                break;\n        }\n\n        p++;\n    }\n\nnot_found:\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"session sticky [firstseen]\");\n    ctx->frist = 1;\n    ctx->sid.len = 0;\n    ctx->sid.data = NULL;\n    ctx->firstseen = now;\n    ctx->lastseen = now;\n\n    ngx_http_session_sticky_tmtoa(r, &ctx->s_lastseen, ctx->lastseen);\n    ngx_http_session_sticky_tmtoa(r, &ctx->s_firstseen, ctx->firstseen);\n\n    if (ctx->s_lastseen.data == NULL || ctx->s_firstseen.data == NULL) {\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n\nsuccess:\n\n    if (sscf->flag & NGX_HTTP_SESSION_STICKY_PREFIX) {\n\n        for (vv = v; vv < p; vv++) {\n            if (*vv == '~') {\n                end = vv + 1;\n                break;\n            }\n        }\n        if (vv >= p) {\n            goto not_found;\n        }\n        st = v;\n\n    } else {\n        vv = p;\n    }\n\n    if ((sscf->flag & NGX_HTTP_SESSION_STICKY_INSERT)\n        && sscf->maxidle != NGX_CONF_UNSET)\n    {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"session_sticky mode [insert]\");\n\n        delimiter = 0;\n        for (p = v; p < vv; p++) {\n            if (*p == NGX_HTTP_SESSION_STICKY_DELIMITER) {\n                delimiter++;\n                if (delimiter == 1) {\n                    ctx->sid.len = p - v;\n                    ctx->sid.data = ngx_pnalloc(r->pool, ctx->sid.len);\n                    if (ctx->sid.data == NULL) {\n                        return NGX_ERROR;\n                    }\n                    ngx_memcpy(ctx->sid.data, v, ctx->sid.len);\n                    v = p + 1;\n\n                } else if(delimiter == 2) {\n                    ctx->s_lastseen.len = p - v;\n                    ctx->s_lastseen.data = ngx_pnalloc(r->pool,\n                                                       ctx->s_lastseen.len);\n                    if (ctx->s_lastseen.data == NULL) {\n                        return NGX_ERROR;\n                    }\n                    ngx_memcpy(ctx->s_lastseen.data, v, ctx->s_lastseen.len);\n                    v = p + 1;\n                    break;\n\n                } else {\n                    legal = 0;\n                    goto finish;\n                }\n            }\n        }\n\n        if (p >= vv || v >= vv) {\n            legal = 0;\n            goto finish;\n\n        }\n\n        ctx->s_firstseen.len = vv - v;\n        ctx->s_firstseen.data = ngx_pnalloc(r->pool, ctx->s_firstseen.len);\n        if (ctx->s_firstseen.data == NULL) {\n            return NGX_ERROR;\n        }\n        ngx_memcpy(ctx->s_firstseen.data, v, ctx->s_firstseen.len);\n\n        ctx->firstseen = ngx_atotm(ctx->s_firstseen.data, ctx->s_firstseen.len);\n        ctx->lastseen = ngx_atotm(ctx->s_lastseen.data, ctx->s_lastseen.len);\n\n        if (ctx->firstseen == NGX_ERROR || ctx->lastseen == NGX_ERROR) {\n            legal = 0;\n            goto finish;\n        }\n\n        if (ctx->sid.len != 0) {\n            diff = (ngx_int_t) (now - ctx->lastseen);\n            if (diff > ctx->sscf->maxidle || diff < -86400) {\n                legal = 0;\n                goto finish;\n            }\n\n            diff = (ngx_int_t) (now - ctx->firstseen);\n            if (diff > ctx->sscf->maxlife || diff < -86400) {\n                legal = 0;\n                goto finish;\n            }\n        }\n\n        ngx_http_session_sticky_tmtoa(r, &ctx->s_lastseen, now);\n\n    } else {\n        ctx->sid.len = vv - v;\n        ctx->sid.data = ngx_pnalloc(r->pool, ctx->sid.len);\n        if (ctx->sid.data == NULL) {\n            return NGX_ERROR;\n        }\n        ngx_memcpy(ctx->sid.data, v, ctx->sid.len);\n    }\n\nfinish:\n\n    if (sscf->flag\n        & (NGX_HTTP_SESSION_STICKY_PREFIX | NGX_HTTP_SESSION_STICKY_INDIRECT))\n    {\n        cookie->len -= (end - st);\n\n        if (cookie->len == 0) {\n            cookies->hash = 0;\n            return NGX_OK;\n        }\n\n        while (end < last) {\n            *st++ = *end++;\n        }\n    }\n\n    if (legal == 0) {\n        goto not_found;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"session sticky sid [%V]\", &ctx->sid);\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_session_sticky_get_peer(ngx_peer_connection_t *pc, void *data)\n{\n    ngx_int_t                          rc;\n    ngx_uint_t                         i, n;\n    ngx_http_ss_ctx_t                 *ctx;\n    ngx_http_request_t                *r;\n    ngx_http_ss_server_t              *server;\n    ngx_http_upstream_ss_srv_conf_t   *sscf;\n    ngx_http_upstream_ss_peer_data_t  *sspd = data;\n\n    sscf = sspd->sscf;\n    r = sspd->r;\n    n = sscf->number;\n    server = sscf->server;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_upstream_session_sticky_module);\n\n    if (ctx->frist == 1 || ctx->sid.len == 0) {\n        goto failed;\n    }\n\n    if (ctx->tries == 0\n        && !(ctx->sscf->flag & NGX_HTTP_SESSION_STICKY_FALLBACK_OFF))\n    {\n        goto failed;\n    }\n\n    for (i = 0; i < n; i++) {\n        if (ctx->sid.len == server[i].sid.len\n            && ngx_strncmp(ctx->sid.data, server[i].sid.data,\n                           ctx->sid.len) == 0)\n        {\n#if (NGX_HTTP_UPSTREAM_CHECK)\n            if (ngx_http_upstream_check_peer_down(server[i].check_index)) {\n                if (ctx->sscf->flag & NGX_HTTP_SESSION_STICKY_FALLBACK_OFF) {\n                    return NGX_BUSY;\n\n                } else {\n                    goto failed;\n                }\n            }\n#endif\n            pc->name = server[i].name;\n            pc->socklen = server[i].socklen;\n            pc->sockaddr = server[i].sockaddr;\n\n            ctx->sid.len = server[i].sid.len;\n            ctx->sid.data = server[i].sid.data;\n            sspd->rrp.current = server[i].peer;\n            ctx->tries--;\n\n            return NGX_OK;\n        }\n    }\n\nfailed:\n    if (ctx->frist != 1 &&\n        (ctx->sscf->flag & NGX_HTTP_SESSION_STICKY_FALLBACK_OFF))\n    {\n        return NGX_BUSY;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"session sticky failed, sid[%V]\", &ctx->sid);\n\n    rc = sspd->get_rr_peer(pc, &sspd->rrp);\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    for (i = 0; i < n; i++) {\n        if (server[i].name->len == pc->name->len\n            && ngx_strncmp(server[i].name->data, pc->name->data,\n                           pc->name->len) == 0)\n        {\n            ctx->sid.len = server[i].sid.len;\n            ctx->sid.data = server[i].sid.data;\n            break;\n        }\n    }\n    ctx->frist = 1;\n\n    return rc;\n}\n\n\nstatic void\nngx_http_session_sticky_tmtoa(ngx_http_request_t *r, ngx_str_t *str, time_t t)\n{\n    time_t      temp;\n    ngx_uint_t  len;\n\n    len = 0;\n    temp = t;\n    while (temp) {\n        len++;\n        temp /= 10;\n    }\n\n    str->len = len;\n    str->data = ngx_pcalloc(r->pool, len);\n    if (str->data == NULL) {\n        return;\n    }\n\n    while (t) {\n        str->data[--len] = t % 10 + '0';\n        t /= 10;\n    }\n}\n\n\nstatic ngx_int_t\nngx_http_session_sticky_header_filter(ngx_http_request_t *r)\n{\n    ngx_int_t                rc;\n    ngx_uint_t               i;\n    ngx_list_part_t         *part;\n    ngx_table_elt_t         *table;\n    ngx_http_ss_ctx_t       *ctx;\n    ngx_http_ss_loc_conf_t  *slcf;\n\n    if (r->headers_out.status >= NGX_HTTP_BAD_REQUEST) {\n        return ngx_http_ss_next_header_filter(r);\n    }\n\n    slcf = ngx_http_get_module_loc_conf(r,\n               ngx_http_upstream_session_sticky_module);\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_upstream_session_sticky_module);\n    if (ctx == NULL || ctx->sscf == NULL || ctx->sscf->flag == 0) {\n        return ngx_http_ss_next_header_filter(r);\n    }\n\n    if ((slcf->uscf == NGX_CONF_UNSET_PTR)\n         && ((ctx->sscf->flag & NGX_HTTP_SESSION_STICKY_PREFIX)\n            || (ctx->sscf->flag & NGX_HTTP_SESSION_STICKY_INDIRECT)))\n    {\n        return ngx_http_ss_next_header_filter(r);\n    }\n\n    if (ctx->sscf->flag\n        & (NGX_HTTP_SESSION_STICKY_PREFIX | NGX_HTTP_SESSION_STICKY_REWRITE))\n    {\n        part = &r->headers_out.headers.part;\n        while (part) {\n            table = (ngx_table_elt_t *) part->elts;\n            for (i = 0; i < part->nelts; i++) {\n                if (table[i].key.len == (sizeof(\"set-cookie\") - 1)\n                    && ngx_strncasecmp(table[i].key.data,\n                                       (u_char *) \"set-cookie\",\n                                       table[i].key.len) == 0)\n                {\n                    if (ctx->sscf->flag & NGX_HTTP_SESSION_STICKY_REWRITE) {\n\n                        rc = ngx_http_session_sticky_rewrite(r, &table[i]);\n                        if (rc == NGX_AGAIN) {\n                            continue;\n\n                        } else if (rc == NGX_ERROR) {\n                            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                                          \"session_sticky [rewrite]\"\n                                          \"set-cookie failed\");\n                        }\n\n                        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log,\n                                       0,\n                                       \"session_sticky [rewrite] set-cookie:%V\",\n                                       &table[i].value);\n\n                        return ngx_http_ss_next_header_filter(r);\n                    }\n\n                    rc = ngx_http_session_sticky_prefix(r, &table[i]);\n                    if (rc == NGX_AGAIN) {\n                        continue;\n\n                    } else if (rc == NGX_ERROR) {\n                        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                                      \"session_sticky [prefix]\"\n                                      \"set-cookie failed\");\n                    }\n\n                    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log,\n                                   0, \"session_sticky [prefix]\"\n                                   \"set-cookie: %V\",\n                                   &table[i].value);\n\n                    return ngx_http_ss_next_header_filter(r);\n                }\n            }\n\n            part = part->next;\n        }\n\n    } else if (ctx->sscf->flag & NGX_HTTP_SESSION_STICKY_INSERT) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"session_sticky [insert]\");\n\n        rc = ngx_http_session_sticky_insert(r);\n        if (rc != NGX_OK) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"session_sticky [insert] failed\");\n        }\n    }\n\n    return ngx_http_ss_next_header_filter(r);\n}\n\n\nstatic ngx_int_t\nngx_http_session_sticky_prefix(ngx_http_request_t *r, ngx_table_elt_t *table)\n{\n    u_char             *p, *s, *t, *last;\n    ngx_http_ss_ctx_t  *ctx;\n    enum {\n        pre_equal = 0,\n        pre_value\n    } state;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_upstream_session_sticky_module);\n    p = ngx_strlcasestrn(table->value.data,\n                         table->value.data + table->value.len,\n                         ctx->sscf->cookie.data,\n                         ctx->sscf->cookie.len - 1);\n    if (p == NULL) {\n        return NGX_AGAIN;\n    }\n\n    last = table->value.data + table->value.len;\n    state = 0;\n    p += ctx->sscf->cookie.len;\n    while (p < last) {\n        switch (state) {\n        case pre_equal:\n            if (*p == '=') {\n                state = pre_value;\n            }\n            break;\n\n        case pre_value:\n            if (*p == ';') {\n                goto success;\n            } else if (!is_space(*p)) {\n                goto success;\n            }\n            break;\n\n        default:\n            break;\n        }\n\n        p++;\n    }\n\n    return NGX_AGAIN;\n\nsuccess:\n\n    table->value.len += ctx->sid.len + 1;\n    s = ngx_pnalloc(r->pool, table->value.len);\n    if (s == NULL) {\n        return NGX_ERROR;\n    }\n\n    t = s;\n    t = ngx_cpymem(t, table->value.data, p - table->value.data);\n    t = ngx_cpymem(t, ctx->sid.data, ctx->sid.len);\n    *t++ = '~';\n    t = ngx_cpymem(t, p, last - p);\n\n    table->value.data = s;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_session_sticky_rewrite(ngx_http_request_t *r, ngx_table_elt_t *table)\n{\n    u_char             *p, *st, *en, *last, *start;\n    ngx_http_ss_ctx_t  *ctx;\n    enum {\n        pre_equal = 0,\n        pre_value,\n        value\n    } state;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_upstream_session_sticky_module);\n    p = ngx_strlcasestrn(table->value.data,\n                         table->value.data + table->value.len,\n                         ctx->sscf->cookie.data,\n                         ctx->sscf->cookie.len - 1);\n    if (p == NULL) {\n        return NGX_AGAIN;\n    }\n\n    st = p;\n    start = table->value.data;\n    last = table->value.data + table->value.len;\n\n    state = 0;\n    while (p < last) {\n        switch (state) {\n        case pre_equal:\n            if (*p == '=') {\n                state = pre_value;\n\n            } else if (*p == ';') {\n                goto success;\n            }\n\n            break;\n\n        case pre_value:\n            if (!is_space(*p)) {\n                state = value;\n                p--;\n            }\n            break;\n\n        case value:\n            if (*p == ';') {\n                goto success;\n            }\n            break;\n\n        default:\n            break;\n        }\n\n        p++;\n    }\n\n    if (p >= last && (state == value || state == pre_equal)) {\n        goto success;\n    }\n\n    return NGX_AGAIN;\n\nsuccess:\n\n    en = p;\n    table->value.len = table->value.len\n                     - (en - st)\n                     + ctx->sscf->cookie.len\n                     + 1 /* '=' */\n                     + ctx->sid.len;\n\n    p = ngx_pnalloc(r->pool, table->value.len);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    table->value.data = p;\n    p = ngx_cpymem(p, start, st - start);\n    p = ngx_cpymem(p, ctx->sscf->cookie.data, ctx->sscf->cookie.len);\n    *p++ = '=';\n    p = ngx_cpymem(p, ctx->sid.data, ctx->sid.len);\n    p = ngx_cpymem(p, en, last - en);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_session_sticky_insert(ngx_http_request_t *r)\n{\n    u_char             *p;\n    ngx_uint_t          i;\n    ngx_list_part_t    *part;\n    ngx_table_elt_t    *set_cookie, *table;\n    ngx_http_ss_ctx_t  *ctx;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_upstream_session_sticky_module);\n    if (ctx->frist != 1 && ctx->sscf->maxidle == NGX_CONF_UNSET) {\n        return NGX_OK;\n    }\n\n    set_cookie = NULL;\n    if (ctx->sscf->flag & NGX_HTTP_SESSION_STICKY_INDIRECT) {\n        part = &r->headers_out.headers.part;\n        while (part && set_cookie == NULL) {\n            table = (ngx_table_elt_t *) part->elts;\n            for (i = 0; i < part->nelts; i++) {\n                if (table[i].key.len == (sizeof(\"set-cookie\") - 1)\n                    && ngx_strncasecmp(table[i].key.data,\n                                       (u_char *) \"set-cookie\",\n                                       table[i].key.len) == 0)\n                {\n                    p = ngx_strlcasestrn(table[i].value.data,\n                                         table[i].value.data +\n                                         table[i].value.len,\n                                         ctx->sscf->cookie.data,\n                                         ctx->sscf->cookie.len - 1);\n                    if (p != NULL) {\n                        set_cookie = &table[i];\n                        break;\n                    }\n                }\n            }\n            part = part->next;\n        }\n    }\n\n    if (set_cookie == NULL) {\n        set_cookie = ngx_list_push(&r->headers_out.headers);\n        if (set_cookie == NULL) {\n            return NGX_ERROR;\n        }\n\n        set_cookie->hash = 1;\n        ngx_str_set(&set_cookie->key, \"Set-Cookie\");\n    }\n\n    set_cookie->value.len = ctx->sscf->cookie.len\n                          + sizeof(\"=\") - 1\n                          + ctx->sid.len\n                          + sizeof(\"; Domain=\") - 1\n                          + ctx->sscf->domain.len\n                          + sizeof(\"; Path=\") - 1\n                          + ctx->sscf->path.len;\n\n    if (ctx->sscf->maxidle != NGX_CONF_UNSET) {\n        set_cookie->value.len = set_cookie->value.len\n                              + ctx->s_lastseen.len\n                              + ctx->s_firstseen.len\n                              + 2; /* '|' and '|' */\n    } else {\n        set_cookie->value.len = set_cookie->value.len\n                              + sizeof(\"; Max-Age=\") - 1\n                              + ctx->sscf->maxage.len\n                              + sizeof(\"; Expires=\") - 1\n                              + sizeof(\"Xxx, 00-Xxx-00 00:00:00 GMT\") - 1;\n    }\n\n    p = ngx_pnalloc(r->pool, set_cookie->value.len);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    set_cookie->value.data = p;\n\n    p = ngx_cpymem(p, ctx->sscf->cookie.data, ctx->sscf->cookie.len);\n    *p++ = '=';\n    p = ngx_cpymem(p, ctx->sid.data, ctx->sid.len);\n    if (ctx->sscf->maxidle != NGX_CONF_UNSET) {\n        *(p++) = NGX_HTTP_SESSION_STICKY_DELIMITER;\n        p = ngx_cpymem(p, ctx->s_lastseen.data, ctx->s_lastseen.len);\n        *(p++) = NGX_HTTP_SESSION_STICKY_DELIMITER;\n        p = ngx_cpymem(p, ctx->s_firstseen.data, ctx->s_firstseen.len);\n    }\n    if (ctx->sscf->domain.len) {\n        p = ngx_cpymem(p, \"; Domain=\", sizeof(\"; Domain=\") - 1);\n        p = ngx_cpymem(p, ctx->sscf->domain.data, ctx->sscf->domain.len);\n    }\n    if (ctx->sscf->path.len) {\n        p = ngx_cpymem(p, \"; Path=\", sizeof(\"; Path=\") - 1);\n        p = ngx_cpymem(p, ctx->sscf->path.data, ctx->sscf->path.len);\n    }\n    if (ctx->sscf->maxidle == NGX_CONF_UNSET && ctx->sscf->maxage.len) {\n        p = ngx_cpymem(p, \"; Max-Age=\", sizeof(\"; Max-Age=\") - 1);\n        p = ngx_cpymem(p, ctx->sscf->maxage.data, ctx->sscf->maxage.len);\n        p = ngx_cpymem(p, \"; Expires=\", sizeof(\"; Expires=\") - 1);\n        ngx_uint_t maxage = ngx_atoi(ctx->sscf->maxage.data,\n                                      ctx->sscf->maxage.len);\n        p = ngx_http_cookie_time(p, ngx_time() + maxage);\n    }\n\n    set_cookie->value.len = p - set_cookie->value.data;\n\n    return NGX_OK;\n}\n\n\nstatic void *\nngx_http_upstream_session_sticky_create_srv_conf(ngx_conf_t *cf)\n{\n    ngx_http_upstream_ss_srv_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_ss_srv_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    conf->maxlife = NGX_CONF_UNSET;\n    conf->maxidle = NGX_CONF_UNSET;\n\n    conf->flag = NGX_HTTP_SESSION_STICKY_INSERT | NGX_HTTP_SESSION_STICKY_MD5;\n    conf->cookie.data = (u_char *) \"route\";\n    conf->cookie.len = sizeof(\"route\") - 1;\n    conf->path.data = (u_char *) \"/\";\n    conf->path.len = 1;\n\n    return conf;\n}\n\n\nstatic void *\nngx_http_session_sticky_create_loc_conf(ngx_conf_t *cf)\n{\n    ngx_http_ss_loc_conf_t  *conf;\n\n    conf = ngx_palloc(cf->pool, sizeof(ngx_http_ss_loc_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    conf->uscf = NGX_CONF_UNSET_PTR;\n    return conf;\n}\n\n\nstatic char *\nngx_http_session_sticky_merge_loc_conf(ngx_conf_t *cf, void *parent,\n    void *child)\n{\n    ngx_http_ss_loc_conf_t  *prev = parent;\n    ngx_http_ss_loc_conf_t  *conf = child;\n\n    ngx_conf_merge_ptr_value(conf->uscf, prev->uscf, NGX_CONF_UNSET_PTR);\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_session_sticky_init(ngx_conf_t *cf)\n{\n    ngx_http_handler_pt       *h;\n    ngx_http_core_main_conf_t *cmcf;\n\n    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);\n\n    h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers);\n    if (h == NULL) {\n        return NGX_ERROR;\n    }\n\n    *h = ngx_http_session_sticky_header_handler;\n\n    ngx_http_ss_next_header_filter = ngx_http_top_header_filter;\n    ngx_http_top_header_filter = ngx_http_session_sticky_header_filter;\n\n    return NGX_OK;\n}\n\n\nstatic char *\nngx_http_upstream_session_sticky(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n    ngx_int_t                        rc;\n    ngx_uint_t                       i;\n    ngx_str_t                       *value;\n    ngx_http_upstream_srv_conf_t    *uscf;\n    ngx_http_upstream_ss_srv_conf_t *sscf = conf;\n\n    uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);\n\n    uscf->peer.init_upstream = ngx_http_upstream_session_sticky_init_upstream;\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    value = cf->args->elts;\n    for (i = 1; i < cf->args->nelts; i++) {\n        if (ngx_strncmp(value[i].data, \"cookie=\", 7) == 0){\n            sscf->cookie.data = value[i].data + 7;\n            sscf->cookie.len = value[i].len - 7;\n            if (sscf->cookie.len == 0) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, \"invalid cookie\");\n                return NGX_CONF_ERROR;\n            }\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"domain=\", 7) == 0) {\n            sscf->domain.data = value[i].data + 7;\n            sscf->domain.len = value[i].len - 7;\n            if (sscf->domain.len == 0) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, \"invalid domain\");\n                return NGX_CONF_ERROR;\n            }\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"path=\", 5) == 0) {\n            sscf->path.data = value[i].data + 5;\n            sscf->path.len = value[i].len - 5;\n            if (sscf->path.len == 0) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, \"invalid path\");\n                return NGX_CONF_ERROR;\n            }\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"maxage=\", 7) == 0) {\n            sscf->maxage.data = value[i].data + 7;\n            sscf->maxage.len = value[i].len - 7;\n            rc = ngx_atoi(sscf->maxage.data, sscf->maxage.len);\n            if (rc == NGX_ERROR) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, \"invalid maxage\");\n                return NGX_CONF_ERROR;\n            }\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"maxidle=\", 8) == 0) {\n            sscf->maxidle = ngx_atotm(value[i].data + 8, value[i].len - 8);\n            if (sscf->maxidle <= NGX_ERROR) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, \"invalid maxidle\");\n                return NGX_CONF_ERROR;\n            }\n\n            if (sscf->maxlife == NGX_CONF_UNSET) {\n                sscf->maxlife = NGX_MAX_INT32_VALUE;\n            }\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"maxlife=\", 8) == 0) {\n            sscf->maxlife = ngx_atotm(value[i].data + 8, value[i].len - 8);\n            if (sscf->maxlife <= NGX_ERROR) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, \"invalid maxlife\");\n                return NGX_CONF_ERROR;\n            }\n\n            if (sscf->maxidle == NGX_CONF_UNSET) {\n                sscf->maxidle = NGX_MAX_INT32_VALUE;\n            }\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"mode=\", 5) == 0) {\n            value[i].data = value[i].data + 5;\n            value[i].len = value[i].len - 5;\n\n            if (ngx_strncmp(value[i].data, \"insert\", 6) == 0) {\n                sscf->flag |= NGX_HTTP_SESSION_STICKY_INSERT;\n\n            } else if (ngx_strncmp(value[i].data, \"prefix\", 6) == 0) {\n                sscf->flag |= NGX_HTTP_SESSION_STICKY_PREFIX;\n                sscf->flag &= (~NGX_HTTP_SESSION_STICKY_INSERT);\n\n            } else if (ngx_strncmp(value[i].data, \"rewrite\", 7) == 0) {\n                sscf->flag |= NGX_HTTP_SESSION_STICKY_REWRITE;\n                sscf->flag &= (~NGX_HTTP_SESSION_STICKY_INDIRECT);\n                sscf->flag &= (~NGX_HTTP_SESSION_STICKY_INSERT);\n\n            } else {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, \"invalid mode\");\n                return NGX_CONF_ERROR;\n            }\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"option=\", 7) == 0) {\n            value[i].data = value[i].data + 7;\n            value[i].len = value[i].len - 7;\n\n            if (ngx_strncmp(value[i].data, \"indirect\", 8) == 0) {\n                sscf->flag |= NGX_HTTP_SESSION_STICKY_INDIRECT;\n\n            } else if (ngx_strncmp(value[i].data, \"direct\", 6) == 0) {\n                sscf->flag &= ~NGX_HTTP_SESSION_STICKY_INDIRECT;\n\n            } else {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, \"invalid option\");\n                return NGX_CONF_ERROR;\n            }\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"fallback=\", 9) == 0) {\n            value[i].data = value[i].data + 9;\n            value[i].len = value[i].len - 9;\n\n            if (ngx_strncmp(value[i].data, \"on\", 2) == 0) {\n                sscf->flag |= NGX_HTTP_SESSION_STICKY_FALLBACK_ON;\n\n            } else if (ngx_strncmp(value[i].data, \"off\", 3) == 0) {\n                sscf->flag |= NGX_HTTP_SESSION_STICKY_FALLBACK_OFF;\n\n            } else {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, \"invalid fallback\");\n                return NGX_CONF_ERROR;\n            }\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"hash=\", 5) == 0) {\n            value[i].data = value[i].data + 5;\n            value[i].len = value[i].len - 5;\n\n            if (ngx_strncmp(value[i].data, \"plain\", 5) == 0) {\n                sscf->flag = (sscf->flag & (~NGX_HTTP_SESSION_STICKY_MD5))\n                           | NGX_HTTP_SESSION_STICKY_PLAIN;\n\n            } else if (ngx_strncmp(value[i].data, \"md5\", 4) == 0) {\n                sscf->flag = (sscf->flag & (~NGX_HTTP_SESSION_STICKY_PLAIN))\n                           | NGX_HTTP_SESSION_STICKY_MD5;\n\n            } else {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, \"invalid hash mode\");\n                return NGX_CONF_ERROR;\n            }\n\n            continue;\n        }\n\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, \"invalid argument\");\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_session_sticky_hide_cookie(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n    ngx_http_ss_loc_conf_t  *slcf = conf;\n\n    size_t      add;\n    ngx_str_t  *value;\n    ngx_url_t   u;\n\n    value = (ngx_str_t *) cf->args->elts;\n    if (ngx_strncmp(value[0].data, \"session_sticky_header\", 21) == 0) {\n        ngx_conf_deprecated(cf,\n                            &ngx_conf_deprecated_session_sticky_header, NULL);\n    }\n\n    if (ngx_strncmp(value[1].data, \"upstream=\", 9) == 0) {\n        add = 9;\n        ngx_memzero(&u, sizeof(ngx_url_t));\n\n        u.url.len = value[1].len - add;\n        u.url.data = value[1].data + add;\n        u.uri_part = 1;\n        u.no_resolve = 1;\n\n        slcf->uscf = ngx_http_upstream_add(cf, &u, 0);\n        if (slcf->uscf == NULL) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"invalid upstream name\");\n            return NGX_CONF_ERROR;\n        }\n        return NGX_CONF_OK;\n    }\n\n    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, \"invalid argument of \\\"%V\\\"\",\n                       &value[1]);\n    return NGX_CONF_ERROR;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_session_sticky_init_upstream(ngx_conf_t *cf,\n    ngx_http_upstream_srv_conf_t *us)\n{\n    ngx_uint_t                        number, i;\n    ngx_http_upstream_rr_peer_t      *peer;\n    ngx_http_upstream_rr_peers_t     *peers;\n    ngx_http_upstream_ss_srv_conf_t  *sscf;\n\n    if (ngx_http_upstream_init_round_robin(cf, us) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    sscf = ngx_http_conf_upstream_srv_conf(us,\n                                    ngx_http_upstream_session_sticky_module);\n    if (sscf == NULL) {\n        return NGX_ERROR;\n    }\n\n    peers = (ngx_http_upstream_rr_peers_t *) us->peer.data;\n    number = peers->number;\n\n    sscf->server = ngx_palloc(cf->pool, number * sizeof(ngx_http_ss_server_t));\n    if (sscf->server == NULL) {\n        return NGX_ERROR;\n    }\n\n    sscf->number = number;\n\n    for (peer = peers->peer, i = 0; peer; peer = peer->next, i++) {\n\n        sscf->server[i].name = &peer->name;\n        sscf->server[i].sockaddr = peer->sockaddr;\n        sscf->server[i].socklen = peer->socklen;\n\n        sscf->server[i].peer = peer;\n\n#if (NGX_HTTP_UPSTREAM_CHECK)\n        sscf->server[i].check_index = peer->check_index;\n#endif\n        if (sscf->flag & NGX_HTTP_SESSION_STICKY_PLAIN) {\n            if (peer->id.len == 0) {\n                sscf->server[i].sid.data = peer->name.data;\n                sscf->server[i].sid.len = peer->name.len;\n                continue;\n            }\n\n            sscf->server[i].sid.data = peer->id.data;\n            sscf->server[i].sid.len = peer->id.len;\n\n        } else if (ngx_http_upstream_session_sticky_set_sid(\n                                                cf, &sscf->server[i]) != NGX_OK)\n        {\n            return NGX_ERROR;\n        }\n    }\n\n    us->peer.init = ngx_http_upstream_session_sticky_init_peer;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_session_sticky_set_sid(ngx_conf_t *cf,\n    ngx_http_ss_server_t *s)\n{\n    u_char     buf[16];\n    ngx_md5_t  md5;\n\n    s->sid.len = 32;\n    s->sid.data = ngx_pnalloc(cf->pool, 32);\n    if (s->sid.data == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_md5_init(&md5);\n    ngx_md5_update(&md5, s->name->data, s->name->len);\n    ngx_md5_final(buf, &md5);\n\n    ngx_hex_dump(s->sid.data, buf, 16);\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "modules/ngx_http_upstream_vnswrr_module/config",
    "content": "ngx_addon_name=ngx_http_upstream_vnswrr_module\nHTTP_UPSTREAM_VNSWRR_SRCS=\"$ngx_addon_dir/ngx_http_upstream_vnswrr_module.c\"\n\nif test -n \"$ngx_module_link\"; then\n    ngx_module_type=HTTP\n    ngx_module_name=$ngx_addon_name\n    ngx_module_deps=\n    ngx_module_srcs=\"$HTTP_UPSTREAM_VNSWRR_SRCS\"\n\n    . auto/module\nelse\n    HTTP_MODULES=\"$HTTP_MODULES ngx_http_upstream_vnswrr_module\"\n    NGX_ADDON_SRCS=\"$NGX_ADDON_SRCS $HTTP_UPSTREAM_VNSWRR_SRCS\"\nfi\n"
  },
  {
    "path": "modules/ngx_http_upstream_vnswrr_module/ngx_http_upstream_vnswrr_module.c",
    "content": "\n/*\n *  Copyright (C) 2010-2019 Alibaba Group Holding Limited\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n#if (NGX_HTTP_UPSTREAM_CHECK)\n#include \"ngx_http_upstream_check_module.h\"\n#endif\n\n\ntypedef struct  ngx_http_upstream_rr_vpeers_s ngx_http_upstream_rr_vpeers_t;\n\n\nstruct ngx_http_upstream_rr_vpeers_s {\n    ngx_int_t                    rindex;\n    ngx_http_upstream_rr_peer_t *vpeer;\n};\n\n\ntypedef struct ngx_http_upstream_vnswrr_srv_conf_s\n    ngx_http_upstream_vnswrr_srv_conf_t;\n\n\nstruct ngx_http_upstream_vnswrr_srv_conf_s {\n    ngx_uint_t                            vnumber;\n    ngx_uint_t                            last_number;\n    ngx_uint_t                            init_number;\n    ngx_uint_t                            max_init;\n    ngx_uint_t                            gcd;\n    ngx_http_upstream_rr_peer_t          *last_peer;\n    ngx_http_upstream_rr_vpeers_t        *vpeers;\n    ngx_http_upstream_vnswrr_srv_conf_t  *next;\n};\n\n\ntypedef struct {\n    /* the round robin data must be first */\n    ngx_http_upstream_rr_peer_data_t      rrp;\n\n    ngx_http_upstream_vnswrr_srv_conf_t  *uvnscf;\n} ngx_http_upstream_vnswrr_peer_data_t;\n\n\nstatic char *ngx_http_upstream_vnswrr(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic void *ngx_http_upstream_vnswrr_create_srv_conf(ngx_conf_t *cf);\nstatic ngx_int_t ngx_http_upstream_init_vnswrr(ngx_conf_t *cf,\n    ngx_http_upstream_srv_conf_t *us);\nstatic ngx_int_t ngx_http_upstream_init_vnswrr_peer(ngx_http_request_t *r,\n    ngx_http_upstream_srv_conf_t *us);\nstatic ngx_int_t ngx_http_upstream_get_vnswrr_peer(ngx_peer_connection_t *pc,\n    void *data);\nstatic ngx_int_t ngx_http_upstream_get_rr_peer(ngx_http_upstream_rr_peers_t *peers,\n    ngx_http_upstream_rr_peer_t **rpeer);\nstatic ngx_http_upstream_rr_peer_t *ngx_http_upstream_get_vnswrr(\n    ngx_http_upstream_vnswrr_peer_data_t *vnsp);\nstatic void ngx_http_upstream_init_virtual_peers(\n    ngx_http_upstream_rr_peers_t *peers,\n    ngx_http_upstream_vnswrr_srv_conf_t *uvnscf,\n    ngx_uint_t s, ngx_uint_t e);\n\nstatic ngx_uint_t ngx_http_upstream_gcd(ngx_uint_t a, ngx_uint_t b);\n\nstatic ngx_command_t  ngx_http_upstream_vnswrr_commands[] = {\n\n    { ngx_string(\"vnswrr\"),\n      NGX_HTTP_UPS_CONF|NGX_CONF_NOARGS|NGX_CONF_TAKE1,\n      ngx_http_upstream_vnswrr,\n      0,\n      0,\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_upstream_vnswrr_module_ctx = {\n    NULL,                                      /* preconfiguration */\n    NULL,                                      /* postconfiguration */\n\n    NULL,                                      /* create main configuration */\n    NULL,                                      /* init main configuration */\n\n    ngx_http_upstream_vnswrr_create_srv_conf,  /* 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_upstream_vnswrr_module = {\n    NGX_MODULE_V1,\n    &ngx_http_upstream_vnswrr_module_ctx,  /* module context */\n    ngx_http_upstream_vnswrr_commands,     /* 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 void *\nngx_http_upstream_vnswrr_create_srv_conf(ngx_conf_t *cf)\n{\n    ngx_http_upstream_vnswrr_srv_conf_t *uvnscf;\n\n    uvnscf = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_vnswrr_srv_conf_t));\n    if (uvnscf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc():\n     *\n     *     uvnscf->vnumber = 0;\n     *     uvnscf->vpeers = NULL;\n     *     uvnscf->last_peer = NULL;\n     *     uvnscf->next = NULL;\n     */\n\n    uvnscf->init_number = NGX_CONF_UNSET_UINT;\n    uvnscf->last_number = NGX_CONF_UNSET_UINT;\n\n    return uvnscf;\n}\n\n\nstatic char *\nngx_http_upstream_vnswrr(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_upstream_srv_conf_t            *uscf;\n    ngx_http_upstream_vnswrr_srv_conf_t     *uvnscf;\n    ngx_str_t                               *value;\n    ngx_int_t                                max_init;\n\n    uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);\n\n    if (uscf->peer.init_upstream) {\n        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                           \"load balancing method redefined\");\n    }\n\n    uscf->peer.init_upstream = ngx_http_upstream_init_vnswrr;\n\n    uscf->flags = NGX_HTTP_UPSTREAM_CREATE\n                  |NGX_HTTP_UPSTREAM_WEIGHT\n                  |NGX_HTTP_UPSTREAM_BACKUP\n                  |NGX_HTTP_UPSTREAM_MAX_FAILS\n                  |NGX_HTTP_UPSTREAM_FAIL_TIMEOUT\n#if defined(nginx_version) && nginx_version >= 1011005\n                  |NGX_HTTP_UPSTREAM_MAX_CONNS\n#endif\n                  |NGX_HTTP_UPSTREAM_DOWN;\n\n    uvnscf = ngx_http_conf_upstream_srv_conf(uscf,\n                                ngx_http_upstream_vnswrr_module);\n\n    value = cf->args->elts;\n\n    max_init = 0;\n\n    if (cf->args->nelts > 1) {\n\n        if (ngx_strncmp(value[1].data, \"max_init=\", 9) == 0) {\n\n            max_init = ngx_atoi(&value[1].data[9], value[1].len - 9);\n\n            if (max_init == NGX_ERROR) {\n\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"invalid parameter \\\"%V\\\"\", &value[1]);\n\n                return NGX_CONF_ERROR;\n            }\n        }\n    }\n\n    uvnscf->max_init = max_init;\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_init_vnswrr(ngx_conf_t *cf,\n    ngx_http_upstream_srv_conf_t *us)\n{\n    ngx_http_upstream_rr_peers_t           *peers, *backup;\n    ngx_http_upstream_vnswrr_srv_conf_t    *uvnscf, *ubvnscf;\n    ngx_http_upstream_server_t             *server;\n    ngx_uint_t                              i, g, bg, max_init;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cf->log, 0, \"init vnswrr\");\n\n    if (ngx_http_upstream_init_round_robin(cf, us) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    g = 0;\n    bg = 0;\n    if (us->servers) {\n        server = us->servers->elts;\n\n        for (i = 0; i < us->servers->nelts; i++) {\n            if (server[i].backup) {\n                bg = ngx_http_upstream_gcd(bg, server[i].weight);\n            } else {\n                g = ngx_http_upstream_gcd(g , server[i].weight);\n            }\n        }\n    }\n    if (g == 0) {\n        g = 1;\n    }\n    if (bg == 0) {\n        bg = 1;\n    }\n\n    uvnscf = ngx_http_conf_upstream_srv_conf(us,\n                                ngx_http_upstream_vnswrr_module);\n    if (uvnscf == NULL) {\n        return NGX_ERROR;\n    }\n\n    peers = (ngx_http_upstream_rr_peers_t *) us->peer.data;\n\n    max_init = uvnscf->max_init;\n\n    uvnscf->init_number = NGX_CONF_UNSET_UINT;\n    uvnscf->last_number = NGX_CONF_UNSET_UINT;\n    uvnscf->last_peer = NULL;\n    uvnscf->next = NULL;\n    uvnscf->gcd = g;\n\n    if (!max_init) {\n        uvnscf->max_init = peers->number;\n\n    } else if (max_init > peers->total_weight) {\n        uvnscf->max_init = peers->total_weight;\n    }\n\n    us->peer.init = ngx_http_upstream_init_vnswrr_peer;\n\n    if (peers->weighted) {\n        uvnscf->vpeers = ngx_pcalloc(cf->pool,\n                                    sizeof(ngx_http_upstream_rr_vpeers_t)\n                                    * peers->total_weight / uvnscf->gcd);\n        if (uvnscf->vpeers == NULL) {\n            return NGX_ERROR;\n        }\n\n        ngx_http_upstream_init_virtual_peers(peers, uvnscf, 0, uvnscf->max_init);\n\n    }\n\n    /* backup peers */\n    backup = peers->next;\n    if (backup) {\n        ubvnscf = ngx_pcalloc(cf->pool,\n                              sizeof(ngx_http_upstream_vnswrr_srv_conf_t));\n        if (ubvnscf == NULL) {\n            return NGX_ERROR;\n        }\n\n        ubvnscf->init_number = NGX_CONF_UNSET_UINT;\n        ubvnscf->last_number = NGX_CONF_UNSET_UINT;\n        ubvnscf->last_peer = NULL;\n        ubvnscf->gcd = bg;\n        \n        ubvnscf->max_init = max_init;\n\n        if (!max_init) {\n            ubvnscf->max_init = backup->number;\n\n        } else if (max_init > backup->total_weight) {\n            ubvnscf->max_init = backup->total_weight;\n        }\n\n        uvnscf->next = ubvnscf;\n\n        if (!backup->weighted) {\n            return NGX_OK;\n        }\n\n        ubvnscf->vpeers = ngx_pcalloc(cf->pool,\n                                      sizeof(ngx_http_upstream_rr_vpeers_t)\n                                      * backup->total_weight / ubvnscf->gcd);\n        if (ubvnscf->vpeers == NULL) {\n            return NGX_ERROR;\n        }\n\n        ngx_http_upstream_init_virtual_peers(backup, ubvnscf, 0, ubvnscf->max_init);\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_init_vnswrr_peer(ngx_http_request_t *r,\n    ngx_http_upstream_srv_conf_t *us)\n{\n    ngx_http_upstream_vnswrr_srv_conf_t    *uvnscf;\n    ngx_http_upstream_vnswrr_peer_data_t   *vnsp;\n\n    uvnscf = ngx_http_conf_upstream_srv_conf(us,\n                                          ngx_http_upstream_vnswrr_module);\n\n    vnsp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_vnswrr_peer_data_t));\n    if (vnsp == NULL) {\n        return NGX_ERROR;\n    }\n\n    vnsp->uvnscf = uvnscf;\n    r->upstream->peer.data = &vnsp->rrp;\n\n    if (ngx_http_upstream_init_round_robin_peer(r, us) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    r->upstream->peer.get = ngx_http_upstream_get_vnswrr_peer;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_get_vnswrr_peer(ngx_peer_connection_t *pc, void *data)\n{\n    ngx_http_upstream_vnswrr_peer_data_t  *vnsp = data;\n\n    ngx_int_t                              rc;\n    ngx_uint_t                             i, n;\n    ngx_http_upstream_rr_peer_t           *peer;\n    ngx_http_upstream_rr_peers_t          *peers;\n    ngx_http_upstream_rr_peer_data_t      *rrp;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                   \"get vnswrr peer, try: %ui\", pc->tries);\n\n    pc->cached = 0;\n    pc->connection = NULL;\n\n    rrp = &vnsp->rrp;\n\n    peers = rrp->peers;\n    ngx_http_upstream_rr_peers_wlock(peers);\n\n    if (peers->single) {\n        peer = peers->peer;\n\n        if (peer->down) {\n            goto failed;\n        }\n\n#if defined(nginx_version) && nginx_version >= 1011005\n        if (peer->max_conns && peer->conns >= peer->max_conns) {\n            goto failed;\n        }\n#endif\n\n#if (NGX_HTTP_UPSTREAM_CHECK)\n        if (ngx_http_upstream_check_peer_down(peer->check_index)) {\n            goto failed;\n        }\n#endif\n        rrp->current = peer;\n\n    } else {\n\n        /* there are several peers */\n\n        peer = ngx_http_upstream_get_vnswrr(vnsp);\n\n        if (peer == NULL) {\n            goto failed;\n        }\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                       \"get vnswrr peer, current: %p %i\",\n                       peer, peer->current_weight);\n    }\n\n    pc->sockaddr = peer->sockaddr;\n    pc->socklen = peer->socklen;\n    pc->name = &peer->name;\n#if (T_NGX_HTTP_DYNAMIC_RESOLVE)\n    pc->host = &peer->host;\n#endif    \n\n    peer->conns++;\n\n    ngx_http_upstream_rr_peers_unlock(peers);\n\n    return NGX_OK;\n\nfailed:\n\n    if (peers->next) {\n\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0, \"backup servers\");\n\n        rrp->peers = peers->next;\n\n        vnsp->uvnscf = vnsp->uvnscf ? vnsp->uvnscf->next : vnsp->uvnscf;\n\n        n = (rrp->peers->number + (8 * sizeof(uintptr_t) - 1))\n                / (8 * sizeof(uintptr_t));\n\n        for (i = 0; i < n; i++) {\n            rrp->tried[i] = 0;\n        }\n\n        ngx_http_upstream_rr_peers_unlock(peers);\n\n        rc = ngx_http_upstream_get_vnswrr_peer(pc, vnsp);\n\n        if (rc != NGX_BUSY) {\n            return rc;\n        }\n\n        ngx_http_upstream_rr_peers_wlock(peers);\n    }\n\n    ngx_http_upstream_rr_peers_unlock(peers);\n\n    pc->name = peers->name;\n\n    return NGX_BUSY;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_get_rr_peer(ngx_http_upstream_rr_peers_t *peers,\n    ngx_http_upstream_rr_peer_t **rpeer)\n{\n    ngx_int_t                      total;\n    ngx_uint_t                     i, p;\n    ngx_http_upstream_rr_peer_t  *peer, *best;\n\n    best = NULL;\n    p = 0;\n    total = 0;\n    for (peer = peers->peer, i = 0; peer; peer = peer->next, i++) {\n        peer->current_weight += peer->effective_weight;\n        total += peer->effective_weight;\n\n        if (best == NULL || peer->current_weight > best->current_weight) {\n            best = peer;\n            p = i;\n        }\n    }\n\n    *rpeer = best;\n    if (best == NULL) {\n        return NGX_ERROR;\n    }\n\n    best->current_weight -= total;\n\n    return p;\n}\n\n\nstatic ngx_http_upstream_rr_peer_t *\nngx_http_upstream_get_vnswrr(ngx_http_upstream_vnswrr_peer_data_t  *vnsp)\n{\n    time_t                                  now;\n    uintptr_t                               m;\n    ngx_uint_t                              i, n, p, flag, begin_number;\n    ngx_http_upstream_rr_peer_t            *peer, *best;\n    ngx_http_upstream_rr_peers_t           *peers;\n    ngx_http_upstream_rr_vpeers_t          *vpeers;\n    ngx_http_upstream_rr_peer_data_t       *rrp;\n    ngx_http_upstream_vnswrr_srv_conf_t    *uvnscf;\n\n    now = ngx_time();\n\n    best = NULL;\n\n#if (NGX_SUPPRESS_WARN)\n    p = 0;\n#endif\n\n    rrp = &vnsp->rrp;\n    peers = rrp->peers;\n    uvnscf = vnsp->uvnscf;\n    vpeers = uvnscf->vpeers;\n\n    if (uvnscf->last_number == NGX_CONF_UNSET_UINT) {\n        uvnscf->init_number = ngx_random() % peers->number;\n\n        if (peers->weighted) {\n            peer = vpeers[uvnscf->init_number].vpeer;\n\n        } else {\n            for (peer = peers->peer, i = 0; i < uvnscf->init_number; i++) {\n                peer = peer->next;\n            }\n        }\n\n        uvnscf->last_number = uvnscf->init_number;\n        uvnscf->last_peer = peer;\n    }\n\n    if (peers->weighted) {\n        /* batch initialization vpeers at runtime. */\n        if (uvnscf->vnumber != peers->total_weight / uvnscf->gcd\n            && (uvnscf->last_number + 1 == uvnscf->vnumber))\n        {\n            n = peers->total_weight / uvnscf->gcd - uvnscf->vnumber;\n            if (n > uvnscf->max_init) {\n                n = uvnscf->max_init;\n            }\n\n            ngx_http_upstream_init_virtual_peers(peers, uvnscf, uvnscf->vnumber,\n\t\t\t                         n + uvnscf->vnumber);\n\n        }\n\n        begin_number = (uvnscf->last_number + 1) % uvnscf->vnumber;\n        peer = vpeers[begin_number].vpeer;\n\n    } else {\n        if (uvnscf->last_peer && uvnscf->last_peer->next) {\n            begin_number = (uvnscf->last_number + 1) % peers->number;\n            peer = uvnscf->last_peer->next;\n\n        } else {\n            begin_number = 0;\n            peer = peers->peer;\n        }\n    }\n\n    for (i = begin_number, flag = 1; i != begin_number || flag;\n         i = peers->weighted\n         ? ((i + 1) % uvnscf->vnumber) : ((i + 1) % peers->number),\n         peer = peers->weighted\n         ? vpeers[i].vpeer : (peer->next ? peer->next : peers->peer))\n    {\n\n        flag = 0;\n        if (peers->weighted) {\n\n            n = peers->total_weight / uvnscf->gcd - uvnscf->vnumber;\n            if (n > uvnscf->max_init) {\n                n = uvnscf->max_init;\n            }\n\n            if (n > 0) {\n                ngx_http_upstream_init_virtual_peers(peers, uvnscf, uvnscf->vnumber,\n                                        n + uvnscf->vnumber);\n            }\n\n            n = vpeers[i].rindex / (8 * sizeof(uintptr_t));\n            m = (uintptr_t) 1 << vpeers[i].rindex % (8 * sizeof(uintptr_t));\n\n        } else {\n            n =  i / (8 * sizeof(uintptr_t));\n            m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));\n        }\n\n        if (rrp->tried[n] & m) {\n            continue;\n        }\n\n        if (peer->down) {\n            continue;\n        }\n\n        if (peer->max_fails\n            && peer->fails >= peer->max_fails\n            && now - peer->checked <= peer->fail_timeout)\n        {\n            continue;\n        }\n\n#if defined(nginx_version) && nginx_version >= 1011005\n        if (peer->max_conns && peer->conns >= peer->max_conns) {\n            continue;\n        }\n#endif\n\n#if (NGX_HTTP_UPSTREAM_CHECK)\n        if (ngx_http_upstream_check_peer_down(peer->check_index)) {\n            continue;\n        }\n#endif\n\n        best = peer;\n        uvnscf->last_peer = peer;\n        uvnscf->last_number = i;\n        p = i;\n        break;\n    }\n\n    if (best == NULL) {\n        return NULL;\n    }\n\n    rrp->current = best;\n\n    if (peers->weighted) {\n        n = vpeers[p].rindex / (8 * sizeof(uintptr_t));\n        m = (uintptr_t) 1 << vpeers[p].rindex % (8 * sizeof(uintptr_t));\n\n    } else {\n        n = p / (8 * sizeof(uintptr_t));\n        m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t));\n    }\n\n    rrp->tried[n] |= m;\n\n    if (now - best->checked > best->fail_timeout) {\n        best->checked = now;\n    }\n\n    return best;\n}\n\n\nstatic void \nngx_http_upstream_init_virtual_peers(ngx_http_upstream_rr_peers_t *peers,\n                                     ngx_http_upstream_vnswrr_srv_conf_t *uvnscf,\n                                     ngx_uint_t s, ngx_uint_t e)\n{\n    ngx_uint_t                              i;\n    ngx_int_t                               rindex;\n    ngx_http_upstream_rr_peer_t            *peer;\n    ngx_http_upstream_rr_vpeers_t          *vpeers;\n\n    if (uvnscf == NULL || peers == NULL) {\n        return;\n    }\n\n    vpeers = uvnscf->vpeers;\n    \n    for (i = s; i < e; i++) {\n        rindex = ngx_http_upstream_get_rr_peer(peers, &peer);\n        if (rindex == NGX_ERROR) {\n            ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                          \"get rr peer is null in upstream \\\"%V\\\" \",\n                          peers->name);\n            if (i != 0) {\n                i--;\n            }\n    \n            continue;\n        }\n    \n        vpeers[i].vpeer = peer;\n        vpeers[i].rindex = rindex;\n    }\n    \n    uvnscf->vnumber = i;\n\n    return;\n}\n\nngx_uint_t ngx_http_upstream_gcd(ngx_uint_t a, ngx_uint_t b)\n{\n    ngx_uint_t r;\n    while (b) {\n        r = a % b;\n        a = b;\n        b = r;\n    }\n    return a;\n}"
  },
  {
    "path": "modules/ngx_http_user_agent_module/config",
    "content": "ngx_addon_name=ngx_http_user_agent_module\nHTTP_USER_AGENT_SRCS=\"$ngx_addon_dir/ngx_http_user_agent_module.c\"\n\nif test -n \"$ngx_module_link\"; then\n    ngx_module_type=HTTP\n    ngx_module_name=$ngx_addon_name\n    ngx_module_deps=\n    ngx_module_srcs=\"$HTTP_USER_AGENT_SRCS\"\n\n    . auto/module\nelse\n    HTTP_MODULES=\"$HTTP_MODULES ngx_http_user_agent_module\"\n    NGX_ADDON_SRCS=\"$NGX_ADDON_SRCS $HTTP_USER_AGENT_SRCS\"\n    HTTP_USER_AGENT=YES\nfi\n"
  },
  {
    "path": "modules/ngx_http_user_agent_module/ngx_http_user_agent_module.c",
    "content": "\n/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\n#define NGX_HTTP_UA_MATCH_LE            '-'\n#define NGX_HTTP_UA_MATCH_GE            '+'\n#define NGX_HTTP_UA_MATCH_INTERVAL      '~'\n#define NGX_HTTP_UA_MATCH_EXACT         '='\n\n#define NGX_HTTP_UA_MAX_OFFSET          8\n\n#define NGX_HTTP_UA_MAX_VERSION_VALUE   99999999999999999ULL\n#define NGX_HTTP_UA_MIN_VERSION_VALUE   0ULL\n#define NGX_HTTP_UA_MAX_INT64           1000000000000ULL\n\n\ntypedef struct {\n    uint64_t                            left;\n    uint64_t                            right;\n\n    ngx_http_variable_value_t          *var;\n} ngx_http_user_agent_interval_t;\n\n\ntypedef struct {\n    ngx_trie_t                         *trie;\n    ngx_http_variable_value_t          *default_value;\n    ngx_pool_t                         *pool;\n} ngx_http_user_agent_ctx_t;\n\n\nstatic char *ngx_http_user_agent_block(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_http_user_agent(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic ngx_int_t ngx_http_user_agent_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_http_user_agent_interval_t *ngx_http_user_agent_get_version(\n    ngx_conf_t *cf, ngx_str_t *value);\n\n\nstatic ngx_command_t ngx_http_user_agent_commands[] = {\n\n    { ngx_string(\"user_agent\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE1,\n      ngx_http_user_agent_block,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      0,\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t ngx_http_user_agent_module_ctx = {\n    NULL,                               /* preconfiguration */\n    NULL,                               /* 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 lcoation configuration */\n};\n\n\nngx_module_t ngx_http_user_agent_module = {\n    NGX_MODULE_V1,\n    &ngx_http_user_agent_module_ctx,    /* module context */\n    ngx_http_user_agent_commands,       /* 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 char *\nngx_http_user_agent_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    char                      *rv;\n    ngx_str_t                 *value, name;\n    ngx_conf_t                 save;\n    ngx_http_variable_t       *var;\n    ngx_http_user_agent_ctx_t *ctx;\n\n    value = cf->args->elts;\n\n    name = value[1];\n    name.data++;\n    name.len--;\n\n    var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE);\n    if (var == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    ctx = ngx_palloc(cf->pool, sizeof(ngx_http_user_agent_ctx_t));\n    if (ctx == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    ctx->pool = cf->pool;\n    ctx->trie = ngx_trie_create(ctx->pool);\n    if (ctx->trie == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    ctx->default_value = NULL;\n\n    var->get_handler = ngx_http_user_agent_variable;\n    var->data = (uintptr_t) ctx;\n\n    save = *cf;\n    cf->ctx = ctx;\n    cf->handler = ngx_http_user_agent;\n    cf->handler_conf = conf;\n\n    rv = ngx_conf_parse(cf, NULL);\n    if (NGX_OK != ctx->trie->build_clue(ctx->trie)) {\n        return NGX_CONF_ERROR;\n    }\n\n    *cf = save;\n    if (ctx->default_value == NULL) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, \"no default value\");\n        rv = NGX_CONF_ERROR;\n    }\n\n    return rv;\n}\n\n\nstatic ngx_http_user_agent_interval_t *\nngx_http_user_agent_get_version(ngx_conf_t *cf, ngx_str_t *value)\n{\n    char                            op;\n    uint64_t                        ver, scale, version;\n    ngx_uint_t                      i, n;\n    ngx_http_user_agent_interval_t *interval;\n\n    op = NGX_HTTP_UA_MATCH_EXACT;\n    scale = NGX_HTTP_UA_MAX_INT64;\n    version = 0;\n    ver = 0;\n    n = 0;\n\n    interval = ngx_palloc(cf->pool, sizeof(ngx_http_user_agent_interval_t));\n    if(interval == NULL) {\n        return NULL;\n    }\n\n    interval->var = ngx_pcalloc(cf->pool, sizeof(ngx_http_variable_value_t));\n    if (interval->var == NULL) {\n        return NULL;\n    }\n\n    interval->left = NGX_HTTP_UA_MIN_VERSION_VALUE;\n    interval->right = NGX_HTTP_UA_MAX_VERSION_VALUE;\n\n    for (i = 0; i < value->len; i++) {\n        if (value->data[i] >= '0' && value->data[i] <= '9') {\n            ver = ver * 10 + value->data[i] - '0';\n            continue;\n        }\n\n        if (value->data[i] == '.') {\n            version += ver * scale;\n            ver = 0;\n            scale /= 10000;\n\n        } else if (value->data[i] == NGX_HTTP_UA_MATCH_LE) {\n            if (i != value->len - 1) {\n                goto error;\n            }\n\n            op = NGX_HTTP_UA_MATCH_LE;\n        } else if (value->data[i] == NGX_HTTP_UA_MATCH_EXACT) {\n            if (i != value->len - 1) {\n                goto error;\n            }\n        } else if (value->data[i] == NGX_HTTP_UA_MATCH_GE) {\n            if (i != value->len - 1) {\n                goto error;\n            }\n\n            op = NGX_HTTP_UA_MATCH_GE;\n        } else if (value->data[i] == NGX_HTTP_UA_MATCH_INTERVAL) {\n            op = NGX_HTTP_UA_MATCH_INTERVAL;\n            if (n >= 2) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, \"too many versions\");\n                return NULL;\n            }\n\n            version += ver * scale;\n            interval->left = version;\n            n++;\n\n            ver = 0;\n            scale = NGX_HTTP_UA_MAX_INT64;\n            version = 0;\n\n            if (i + 1 >= value->len) {\n                goto error;\n            }\n\n            if (!(value->data[i + 1] >= '0'&&value->data[i + 1] <= '9')) {\n                goto error;\n            }\n        } else {\n            goto error;\n        }\n    }\n\n    version += ver * scale;\n    if (op == NGX_HTTP_UA_MATCH_LE || op == NGX_HTTP_UA_MATCH_INTERVAL) {\n        interval->right = version;\n\n    } else if (op == NGX_HTTP_UA_MATCH_GE) {\n        interval->left = version;\n\n    } else if (op == NGX_HTTP_UA_MATCH_EXACT) {\n        interval->left = version;\n        interval->right = version;\n    }\n\n    return interval;\n\nerror:\n    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, \"invalid version\");\n    return NULL;\n}\n\n\nstatic char *\nngx_http_user_agent(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_str_t                      *args, *name, file;\n    ngx_uint_t                      i, nelts, mode;\n    ngx_trie_t                     *trie;\n    ngx_array_t                    *value;\n    ngx_trie_node_t                *node;\n    ngx_http_user_agent_ctx_t      *ctx;\n    ngx_http_user_agent_interval_t *interval, *p;\n\n    ctx = cf->ctx;\n    trie = ctx->trie;\n\n    args = cf->args->elts;\n    nelts = cf->args->nelts;\n\n    name = NULL;\n\n    if (nelts <= 1) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                 \"invalid first parameter\");\n        return NGX_CONF_ERROR;\n\n    }\n\n    if (nelts == 2) {\n        if (ngx_strcmp(args[0].data, \"include\") == 0) {\n\n            file = args[1];\n            if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK) {\n                return NGX_CONF_ERROR;\n            }\n\n            ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, \"include %s\",\n                           file.data);\n            return ngx_conf_parse(cf, &file);\n        }\n\n        if (ngx_strcmp(args[0].data, \"default\") == 0) {\n\n            if (ctx->default_value != NULL) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, \"is duplicate\");\n                return NGX_CONF_ERROR;\n            }\n\n            ctx->default_value = ngx_pcalloc(ctx->pool,\n                                             sizeof(ngx_http_variable_t));\n            if (ctx->default_value == NULL) {\n                return NGX_CONF_ERROR;\n            }\n\n            ctx->default_value->len = args[1].len;\n            ctx->default_value->data = args[1].data;\n\n            ctx->default_value->not_found = 0;\n            ctx->default_value->no_cacheable =0;\n            ctx->default_value->valid =1;\n\n            return NGX_CONF_OK;\n        }\n\n        if (ngx_strcmp(args[0].data, \"greedy\") == 0) {\n            mode = NGX_TRIE_REVERSE | NGX_TRIE_CONTINUE;\n            trie->insert(trie, args + 1, mode);\n\n            return NGX_CONF_OK;\n        }\n    }\n\n    if (nelts == 2) {\n\n        name = args;\n\n        interval = ngx_pcalloc(ctx->pool,\n                               sizeof(ngx_http_user_agent_interval_t));\n        if (interval == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        interval->var = ngx_pcalloc(ctx->pool,\n                                    sizeof(ngx_http_variable_value_t));\n        if (interval->var == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        interval->left = NGX_HTTP_UA_MIN_VERSION_VALUE;\n        interval->right = NGX_HTTP_UA_MAX_VERSION_VALUE;\n\n        interval->var->len = args[1].len;\n        interval->var->data = args[1].data;\n\n        interval->var->not_found = 0;\n        interval->var->no_cacheable = 0;\n        interval->var->valid = 1;\n\n        goto insert;\n    }\n\n    if (nelts == 3) {\n\n        name = args;\n        interval = ngx_http_user_agent_get_version(cf, args + 1);\n        if (interval == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        interval->var->len = args[2].len;\n        interval->var->data = args[2].data;\n\n        interval->var->not_found = 0;\n        interval->var->no_cacheable =0;\n        interval->var->valid = 1;\n\n        goto insert;\n    }\n\n    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, \"too many args\");\n    return NGX_CONF_ERROR;\n\ninsert:\n\n    mode = NGX_TRIE_REVERSE;\n    node = trie->insert(trie, name, mode);\n    if (node == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    value = (ngx_array_t *) node->value;\n    if (value == NULL) {\n        value = ngx_array_create(ctx->pool, 2,\n                                 sizeof(ngx_http_user_agent_interval_t));\n        if (value == NULL) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    p = (ngx_http_user_agent_interval_t *) value->elts;\n    for (i = 0; i < value->nelts; i++) {\n        if ((p[i].left >= interval->left && p[i].left <= interval->right)\n            || (p[i].right >= interval->left && p[i].right <= interval->right)\n            || (interval->left >= p[i].left && interval->left <= p[i].right)\n            || (interval->right >= p[i].left && interval->right <= p[i].right))\n        {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, \"interval covered\");\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    p = (ngx_http_user_agent_interval_t *) ngx_array_push(value);\n    if (p == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    *p = *interval;\n    node->value = (void *) value;\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_user_agent_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    uint64_t                        ver, scale, version;\n    ngx_int_t                       i, n, pos, offset;\n    ngx_str_t                      *user_agent;\n    ngx_trie_t                     *trie;\n    ngx_array_t                    *value;\n    ngx_http_user_agent_ctx_t      *uacf;\n    ngx_http_user_agent_interval_t *array;\n\n    uacf = (ngx_http_user_agent_ctx_t *) data;\n    trie = uacf->trie;\n\n    if (r->headers_in.user_agent == NULL) {\n      goto end;\n    }\n\n    user_agent = &(r->headers_in.user_agent->value);\n\n    value = trie->query(trie, user_agent, &pos, NGX_TRIE_REVERSE);\n    if (value == NULL || pos < 0) {\n        goto end;\n    }\n\n    version = 0;\n    scale = NGX_HTTP_UA_MAX_INT64;\n    ver = 0;\n    offset = 0;\n\n    for (/* void */; pos < (ngx_int_t) user_agent->len; pos++, offset++) {\n        if (user_agent->data[pos] >= '0'\n            && user_agent->data[pos] <= '9') {\n            break;\n\n        } else if (user_agent->data[pos] == ';'\n                   || user_agent->data[pos] == '('\n                   || user_agent->data[pos] == ')')\n        {\n            break;\n        }\n\n        if(offset >= NGX_HTTP_UA_MAX_OFFSET) {\n            break;\n        }\n    }\n\n    array = value->elts;\n    n = value->nelts;\n\n    for (/* void */ ; pos < (ngx_int_t) user_agent->len; pos++) {\n\n        if (user_agent->data[pos] == '.') {\n            version += ver * scale;\n            ver = 0;\n            scale /= 10000;\n            continue;\n\n        } else if(user_agent->data[pos] >= '0'\n                  && user_agent->data[pos] <= '9') {\n\n            ver = ver * 10 +user_agent->data[pos] - '0';\n            continue;\n        }\n\n        break;\n    }\n\n    version += ver * scale;\n    for (i = 0; i < n; i++) {\n        if (version >= array[i].left && version <= array[i].right) {\n            *v = *(array[i].var);\n            return NGX_OK;\n        }\n    }\n\nend:\n\n    *v = *uacf->default_value;\n    return NGX_OK;\n}\n"
  },
  {
    "path": "modules/ngx_http_xquic_module/README.md",
    "content": "# ngx_http_xquic_module\n\nTengine ngx_http_xquic_module 主要用于在服务端启用 QUIC/HTTP3 监听服务。\n\n# 编译\n\nngx_http_xquic_module 编译依赖\n\n依赖库：\n\n* Tongsuo: https://github.com/Tongsuo-Project/Tongsuo\n* xquic: https://github.com/alibaba/xquic\n\n```shell\n# 下载 Tongsuo，示例中下载 8.3.2 版本\nwget -c \"https://github.com/Tongsuo-Project/Tongsuo/archive/refs/tags/8.3.2.tar.gz\"\ntar -x 8.3.2.tar.gz\n\n# 下载 xquic，示例中下载 1.6.0 版本\nwget -c \"https://github.com/alibaba/xquic/archive/refs/tags/v1.6.0.tar.gz\"\ntar -xf v1.6.0.tar.gz\n\n# 下载 Tengine 3.0.0 以上版本，示例从 master 获取最新版本，也可下载指定版本\ngit clone git@github.com:alibaba/tengine.git\n\n# 编译 Tongsuo\ncd Tongsuo-8.3.2\n./config --prefix=/usr/local/babassl\nmake\nmake install\nexport SSL_TYPE_STR=\"babassl\"\nexport SSL_PATH_STR=\"${PWD}\"\nexport SSL_INC_PATH_STR=\"${PWD}/include\"\nexport SSL_LIB_PATH_STR=\"${PWD}/libssl.a;${PWD}/libcrypto.a\"\ncd ../../\n\n# 编译 xquic 库\ncd xquic-1.6.0/\nmkdir -p build; cd build\ncmake -DXQC_SUPPORT_SENDMMSG_BUILD=1 -DXQC_ENABLE_BBR2=1 -DXQC_DISABLE_RENO=0 -DSSL_TYPE=${SSL_TYPE_STR} -DSSL_PATH=${SSL_PATH_STR} -DSSL_INC_PATH=${SSL_INC_PATH_STR} -DSSL_LIB_PATH=${SSL_LIB_PATH_STR} ..\nmake\ncp \"libxquic.so\" /usr/local/lib/\ncd ..\n\n# 编译 Tengine\ncd tengine\n\n# 注：xquic 依赖 ngx_http_v2_module，需要参数 --with-http_v2_module\n./configure \\\n  --prefix=/usr/local/tengine \\\n  --sbin-path=sbin/tengine \\\n  --with-xquic-inc=\"../xquic-1.6.0/include\" \\\n  --with-xquic-lib=\"../xquic-1.6.0/build\" \\\n  --with-http_v2_module \\\n  --without-http_rewrite_module \\\n  --add-module=modules/ngx_http_xquic_module \\\n  --with-openssl=\"../Tongsuo-8.3.2\"\n\nmake\nmake install\n```\n\n精简示例配置，其中 default-fake-certificate.pem 为可用证书。\n\n```nginx\nworker_processes  1;\n\nerror_log  logs/error.log debug;\n\nevents {\n    worker_connections  1024;\n}\n\nxquic_log   \"pipe:rollback /usr/local/tengine/logs/tengine-xquic.log baknum=10 maxsize=1G interval=1d adjust=600\" info;\n\nhttp {\n    xquic_ssl_certificate        /usr/local/tengine/ssl/default-fake-certificate.pem;\n    xquic_ssl_certificate_key    /usr/local/tengine/ssl/default-fake-certificate.pem;\n\n    server {\n        listen 2443 xquic reuseport;\n\n        location / {\n        }\n    }\n}\n```\n\n启动 tengine\n\n```shell\n/usr/local/tengine/sbin/tengine -p /usr/local/tengine/ -c conf/nginx.conf\n```\n\n启动后 tengine 监听 2443 UDP 端口，此端口可以接收 HTTP3 请求，可以通过编译 xquic 自带的 `test_client` 测试（cmake 编译 xquic 时需要带 `-DXQC_ENABLE_TESTING=1` 参数）\n\n```shell\n./test_client -a 127.0.0.1 -p 2443 -u https://domain/\n```\n\n更为详细的指令可参考官网文档 [XQUIC模块](http://tengine.taobao.org/document_cn/xquic_cn.html)\n\n# 浏览器使用 HTTP3\n\n**注意：浏览器访问需要确保证书受信。**\n\n浏览器默认不会使用 `HTTP3` 请求，需要服务端响应包头 `Alt-Svc` 进行升级说明，浏览器通过响应包头感知到服务端是支持 `HTTP3` 的，下次请求会尝试使用 `HTTP3`。\n\n```nginx\nworker_processes  1;\n\nuser root;\n\nerror_log  logs/error.log debug;\n\nevents {\n    worker_connections  1024;\n}\n\nxquic_log   \"pipe:rollback /usr/local/tengine/logs/tengine-xquic.log baknum=10 maxsize=1G interval=1d adjust=600\" info;\n\nhttp {\n    xquic_ssl_certificate        /usr/local/tengine/ssl/default-fake-certificate.pem;\n    xquic_ssl_certificate_key    /usr/local/tengine/ssl/default-fake-certificate.pem;\n\n    server {\n        listen 2443 xquic reuseport;\n\n        location / {\n        }\n    }\n\n    server {\n        listen 80 default_server reuseport backlog=4096;\n        listen 443 default_server reuseport backlog=4096 ssl http2;\n        listen 443 default_server reuseport backlog=4096 xquic;\n\n        server_name s1.test.com;\n\n        add_header Alt-Svc 'h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000' always;\n\n        ssl_certificate     /etc/ingress-controller/ssl/s1.crt;\n        ssl_certificate_key /etc/ingress-controller/ssl/s1.key;\n    }\n\n    server {\n        listen 80;\n        listen 443 ssl http2;\n        listen 443 xquic;\n\n        server_name s2.test.com;\n\n        add_header Alt-Svc 'h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000' always;\n\n        ssl_certificate     /etc/ingress-controller/ssl/s2.crt;\n        ssl_certificate_key /etc/ingress-controller/ssl/s2.key;\n    }\n}\n```\n\n通过以上配置，浏览器访问对应域名，第一次访问 `HTTP2`，下次访问会切换至 `HTTP3`。\n\n**注意**：\n\n在生产环境中，处于安全性考虑，一般情况会以普通用户权限启动 `Tenigne`，而 `xquic` 功能在普通用户权限下，监听端口必须配置为 1024 以上，如监听 2443 端口，那对外的四层负载均衡需要做 443 到 2443 端口的映射，`Tenigne` `Server`段配置示例：\n\n```nginx\n    server {\n        listen 80 default_server reuseport backlog=4096;\n        listen 443 default_server reuseport backlog=4096 ssl http2;\n        listen 2443 default_server reuseport backlog=4096 xquic;\n\n        add_header Alt-Svc 'h3=\":443\"; ma=2592000,h3-29=\":443\"; ma=2592000' always;\n\n        ssl_certificate     /etc/ingress-controller/ssl/s1.crt;\n        ssl_certificate_key /etc/ingress-controller/ssl/s1.key;\n    }\n```\n\n四层负载均衡配置示例：\n\n```yaml\n  type: LoadBalancer\n  ports:\n  - port: 80\n    name: tengine-tcp-80\n    protocol: TCP\n    targetPort: 80\n  - port: 443\n    name: tengine-tcp-443\n    protocol: TCP\n    targetPort: 443\n  - port: 443\n    name: tengine-udp-443\n    protocol: UDP\n    targetPort: 2443\n  selector:\n    app: tengine\n```\n\n对用户来讲，还是通过 443 端口访问，通过四层负载均衡设备，转换为 `Tengine` 的 2443 端口。\n"
  },
  {
    "path": "modules/ngx_http_xquic_module/config",
    "content": "ngx_addon_name=\"ngx_http_xquic_module\"\n\nnext=ngx_http_chunked_filter_module\nHTTP_FILTER_MODULES=`echo $HTTP_FILTER_MODULES \\\n                     | sed \"s/$next/$next ngx_http_xquic_filter_module/\"`\n\nnext=ngx_http_v2_module\nHTTP_MODULES=`echo $HTTP_MODULES \\\n                     | sed \"s/$next/$next ngx_http_xquic_module/\"`\n\n\nNGX_ADDON_DEPS=\"$NGX_ADDON_DEPS \\\n                $ngx_addon_dir/ngx_xquic.h \\\n                $ngx_addon_dir/ngx_http_xquic_module.h \\\n                $ngx_addon_dir/ngx_http_xquic.h \\\n                $ngx_addon_dir/ngx_xquic_intercom.h \\\n                $ngx_addon_dir/ngx_xquic_recv.h \\\n                $ngx_addon_dir/ngx_xquic_send.h \\\n                $ngx_addon_dir/ngx_http_v3_stream.h\"\n\nNGX_ADDON_SRCS=\"$NGX_ADDON_SRCS \\\n                $ngx_addon_dir/ngx_xquic.c \\\n                $ngx_addon_dir/ngx_http_xquic_module.c \\\n                $ngx_addon_dir/ngx_http_xquic.c \\\n                $ngx_addon_dir/ngx_xquic_intercom.c \\\n                $ngx_addon_dir/ngx_xquic_recv.c \\\n                $ngx_addon_dir/ngx_xquic_send.c \\\n                $ngx_addon_dir/ngx_http_v3_stream.c \\\n                $ngx_addon_dir/ngx_http_xquic_filter_module.c\"\n\nCORE_INCS=\"$CORE_INCS \\\n           $ngx_addon_dir \\\n           $XQUIC_INC\"\n\nCORE_LIBS=\"$CORE_LIBS \\\n           -L$XQUIC_LIB -lxquic -Wl,-rpath=$XQUIC_LIB -lrt -pthread -lm\"\n\nhave=T_NGX_XQUIC . auto/have\n"
  },
  {
    "path": "modules/ngx_http_xquic_module/ngx_http_v3_stream.c",
    "content": "/*\n * Copyright (C) 2020-2023 Alibaba Group Holding Limited\n */\n\n#include <nginx.h>\n\n#include <ngx_http_v3_stream.h>\n#include <ngx_http_xquic_module.h>\n#include <ngx_xquic_send.h>\n#include <ngx_xquic.h>\n#include <ngx_http_xquic.h>\n\n\n#include <xquic/xquic.h>\n#include <xquic/xquic_typedef.h>\n\n\n\nstatic void ngx_http_v3_run_request(ngx_http_request_t *r, ngx_http_v3_stream_t *h3_stream);\nstatic void ngx_http_v3_stream_free(ngx_http_v3_stream_t *h3_stream);\nvoid ngx_http_v3_stream_cancel(ngx_http_v3_stream_t *h3_stream,\n    ngx_int_t status);\n\n\nstatic void\nngx_http_v3_close_stream_handler(ngx_event_t *ev)\n{\n    ngx_connection_t    *fc;\n    ngx_http_request_t  *r;\n\n    fc = ev->data;\n    r = fc->data;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"|xquic|http3 close stream handler|%p|\", r->xqstream);\n\n    ngx_http_v3_close_stream(r->xqstream, 0);\n}\n\n\nstatic void\nngx_http_v3_request_cleanup(void *data)\n{\n    ngx_http_v3_stream_t *stream = data;\n    if (stream) {\n        stream->request_freed = 1;\n    }\n}\n\n\nstatic ngx_http_v3_stream_t *\nngx_http_v3_create_stream(ngx_http_xquic_connection_t *h3c, uint64_t stream_id)\n{\n    ngx_log_t                  *log;\n    ngx_event_t                *rev, *wev;\n    ngx_connection_t           *fc;\n    ngx_http_log_ctx_t         *ctx;\n    ngx_http_request_t         *r;\n    ngx_http_cleanup_t         *cln;\n    ngx_http_v3_stream_t       *stream;\n    ngx_http_core_srv_conf_t   *cscf;\n    ngx_http_xquic_main_conf_t *qmcf = ngx_http_cycle_get_module_main_conf(ngx_cycle, ngx_http_xquic_module);\n\n\n    /* get fake connection */\n    fc = h3c->free_fake_connections;\n\n    if (fc) {\n        /* use (void *)data as next point  */\n        h3c->free_fake_connections = fc->data;\n\n        rev = fc->read;\n        wev = fc->write;\n        log = fc->log;\n        ctx = log->data;\n\n    } else {\n        fc = ngx_palloc(h3c->pool, sizeof(ngx_connection_t));\n        if (fc == NULL) {\n            return NULL;\n        }\n\n        rev = ngx_palloc(h3c->pool, sizeof(ngx_event_t));\n        if (rev == NULL) {\n            return NULL;\n        }\n\n        wev = ngx_palloc(h3c->pool, sizeof(ngx_event_t));\n        if (wev == NULL) {\n            return NULL;\n        }\n\n        log = ngx_palloc(h3c->pool, sizeof(ngx_log_t));\n        if (log == NULL) {\n            return NULL;\n        }\n\n        ctx = ngx_palloc(h3c->pool, sizeof(ngx_http_log_ctx_t));\n        if (ctx == NULL) {\n            return NULL;\n        }\n\n        ctx->connection = fc;\n        ctx->request = NULL;\n        ctx->current_request = NULL;\n    }\n\n    ngx_memcpy(log, h3c->connection->log, sizeof(ngx_log_t));\n\n    log->data = ctx;\n\n    ngx_memzero(rev, sizeof(ngx_event_t));\n\n    rev->data = fc;\n    rev->ready = 1;\n    rev->handler = ngx_http_v3_close_stream_handler;\n    rev->log = log;\n\n    ngx_memcpy(wev, rev, sizeof(ngx_event_t));\n\n    wev->write = 1;\n\n    ngx_memcpy(fc, h3c->connection, sizeof(ngx_connection_t));\n\n    fc->data = h3c->http_connection;\n    fc->read = rev;\n    fc->write = wev;\n    fc->sent = 0;\n    fc->log = log;\n    fc->buffered = 0;\n    fc->sndlowat = 1;\n    fc->tcp_nodelay = NGX_TCP_NODELAY_DISABLED;\n\n\n    /* create request */\n    r = ngx_http_create_request(fc);\n    if (r == NULL) {\n        return NULL;\n    }\n\n    ngx_str_set(&r->http_protocol, \"HTTP/3.0\");\n\n    r->http_version = NGX_HTTP_VERSION_30;\n    r->valid_location = 1;\n\n    fc->data = r;\n    h3c->connection->requests++;\n\n    cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);\n\n    r->header_in = ngx_create_temp_buf(r->pool,\n                                       cscf->client_header_buffer_size);\n    if (r->header_in == NULL) {\n        ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return NULL;\n    }\n\n    if (ngx_list_init(&r->headers_in.headers, r->pool, 20,\n                      sizeof(ngx_table_elt_t))\n        != NGX_OK)\n    {\n        ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return NULL;\n    }\n\n    r->headers_in.connection_type = NGX_HTTP_CONNECTION_CLOSE;\n\n    /* create stream */\n#if 0\n    stream = ngx_pcalloc(r->pool, sizeof(ngx_http_v3_stream_t));\n    if (stream == NULL) {\n        goto free_request;\n    }\n#endif\n\n    stream = h3c->free_streams;\n\n    if (stream) {\n        /* use (void *)data as next point  */\n        h3c->free_streams = stream->next;\n        ngx_memzero(stream, sizeof(ngx_http_v3_stream_t));\n\n    } else {\n        stream = ngx_pcalloc(h3c->pool, sizeof(ngx_http_v3_stream_t));\n        if (stream == NULL) {\n            goto free_request;\n        }\n    }\n\n    stream->id = stream_id;\n\n    /* use in ngx_http_v3_finalize_connection, don't alloc from request pool */\n    stream->list_node = ngx_pcalloc(h3c->pool, sizeof(ngx_xquic_list_node_t));\n    if (stream->list_node == NULL) {\n        goto free_request;\n    }\n    stream->list_node->entry = stream;\n\n    /* insert to streams_index */\n    ngx_uint_t index = ngx_http_xquic_index(qmcf, stream->id);\n    stream->list_node->next = h3c->streams_index[index];\n    h3c->streams_index[index] = stream->list_node;\n\n    /* relate to request */\n    r->xqstream = stream;\n\n    stream->request = r;\n    stream->connection = h3c;\n\n    h3c->processing++;\n\n    cln = ngx_http_cleanup_add(r, 0);\n    if (cln == NULL) {\n        goto free_request;\n    }\n\n    cln->handler = ngx_http_v3_request_cleanup;\n    cln->data = r->xqstream;\n\n    return stream;\n\nfree_request:\n    ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n    return NULL;\n}\n\n\nngx_int_t\nngx_http_v3_check_request_limit(ngx_http_v3_stream_t *user_stream)\n{\n    ngx_http_xquic_main_conf_t  *qmcf = ngx_http_cycle_get_module_main_conf(ngx_cycle, ngx_http_xquic_module);\n\n    /* limit is not configured */\n    if (qmcf->max_quic_qps == NGX_CONF_UNSET_UINT) {\n        return NGX_OK;\n    }\n\n    /* check max qps limit */\n    ngx_atomic_uint_t quic_qps_nexttime = *ngx_stat_quic_qps_nexttime;\n    if (ngx_current_msec <= quic_qps_nexttime) {\n        /* still in current stat round, check cps limit. decline if reach max QPS limit */\n        if (*ngx_stat_quic_qps >= qmcf->max_quic_qps) {\n            ngx_log_error(NGX_LOG_WARN, ngx_cycle->log, 0, \"|xquic|reached max qps limit\"\n                      \"|limit:%ui|\", qmcf->max_quic_qps);\n            return NGX_DECLINED;\n        }\n\n    } else {\n        /* start a new stat round */\n        ngx_atomic_cmp_set(ngx_stat_quic_qps_nexttime,\n            *ngx_stat_quic_qps_nexttime, ngx_current_msec + 1000);\n        ngx_atomic_cmp_set(ngx_stat_quic_qps, *ngx_stat_quic_qps, 0);\n    }\n\n    return NGX_OK;\n}\n\n\nvoid\nngx_http_v3_stream_refuse(ngx_http_v3_stream_t *h3_stream,\n    ngx_int_t status)\n{\n    h3_stream->cancel_status = status;\n\n    if (!h3_stream->request_closed) {\n        ngx_http_finalize_request(h3_stream->request, status);\n        h3_stream->request = NULL;\n    }\n}\n\n\nint \nngx_http_v3_request_create_notify(xqc_h3_request_t *h3_request, void *user_data)\n{\n    ngx_log_error(NGX_LOG_DEBUG, ngx_cycle->log, 0, \n                                \"|xquic|xqc_http_v3_request_create_notify|\");\n\n    ngx_http_xquic_connection_t *h3c = xqc_h3_get_conn_user_data_by_request(h3_request);\n\n    ngx_log_error(NGX_LOG_DEBUG, ngx_cycle->log, 0, \n                                \"|xquic|xqc_http_v3_request_create_notify in connection|%p|\", h3c);\n\n    xqc_stream_id_t stream_id = xqc_h3_stream_id(h3_request);\n\n    ngx_http_v3_stream_t *user_stream = ngx_http_v3_create_stream(h3c, (uint64_t)stream_id);\n    user_stream->h3_request = h3_request;\n    xqc_h3_request_set_user_data(h3_request, user_stream);\n\n    /* limit while allow creation of usere_stream, which will be freed in request_close_notify */\n    if (ngx_http_v3_check_request_limit(user_stream) != NGX_OK) {\n        /* if request is limited, refuse it */\n        ngx_http_v3_stream_refuse(user_stream, NGX_HTTP_REQUEST_LIMITED);\n        (void) ngx_atomic_fetch_add(ngx_stat_quic_queries_refused, 1);\n        return NGX_OK;\n    }\n\n\n    /* add stat */\n    (void) ngx_atomic_fetch_add(ngx_stat_quic_qps, 1);\n    (void) ngx_atomic_fetch_add(ngx_stat_quic_queries, 1);\n\n    return NGX_OK;\n}\n\n\n/**\n * only free user_stream here,\n * make sure user_stream has the same life cycle of h3_request in xquic engine\n */\nint \nngx_http_v3_request_close_notify(xqc_h3_request_t *h3_request, \n    void *user_data)\n{\n    ngx_http_v3_stream_t        *h3_stream = (ngx_http_v3_stream_t *)user_data;\n    xqc_request_stats_t          stats = xqc_h3_request_get_stats(h3_request);\n\n    ngx_log_error(NGX_LOG_DEBUG, ngx_cycle->log, 0, \n                                \"|xquic|xqc_http_v3_request_close_notify|err=%d|\", stats.stream_err);\n\n    if (!h3_stream->run_request) {\n        ngx_log_error(NGX_LOG_WARN, ngx_cycle->log, 0, \n                                    \"|xquic|h3 request closed before run|err=%d|\", stats.stream_err);\n        /* h3_stream must be freed at last, so h3_stream is not reused now */\n        if (!h3_stream->request_freed) {\n            ngx_http_finalize_request(h3_stream->request, NGX_ERROR);\n            h3_stream->request = NULL;\n        }\n    }\n\n    if (!h3_stream->request_closed) {\n        h3_stream->engine_inner_closed = 1;\n        ngx_http_v3_close_stream(h3_stream, 0);\n        return NGX_OK;\n    }\n\n    if (h3_stream->closed) {\n        ngx_log_error(NGX_LOG_WARN, ngx_cycle->log, 0, \n                                \"|xquic|ngx_http_v3_request_close_notify|free stream twice|stream_id=%ui|err=%d|\", \n                                h3_stream->id, stats.stream_err);\n        return NGX_OK;\n    }\n\n    ngx_http_v3_close_stream(h3_stream, 0);\n    /* free user_stream & insert into free_streams */\n    ngx_http_v3_stream_free(h3_stream);\n\n    return NGX_OK;\n}\n\n\nint \nngx_http_v3_request_write_notify(xqc_h3_request_t *h3_request, \n    void *user_data)\n{\n    ngx_log_error(NGX_LOG_DEBUG, ngx_cycle->log, 0, \n                                \"|xquic|xqc_http_v3_request_write_notify|\");\n\n    ngx_http_v3_stream_t *h3_stream = (ngx_http_v3_stream_t *) user_data;\n\n    /* request closed */\n    if (h3_stream->request_closed) {\n        ngx_log_error(NGX_LOG_DEBUG, ngx_cycle->log, 0, \n                                \"|xquic|xqc_http_v3_request_write_notify|request_closed|\");\n        return NGX_OK;\n    }\n\n    h3_stream->wait_to_write = 0;\n\n\n    /* don't have data to send */\n    if (h3_stream->queued == 0) {\n        return NGX_OK;\n    }\n\n    /* don't need limit here */\n    if (ngx_http_xquic_send_chain(h3_stream->request->connection, NULL, 0) == NGX_CHAIN_ERROR) {\n\n        ngx_log_error(NGX_LOG_WARN, ngx_cycle->log, 0, \n                                \"|xquic|ngx_http_write_filter error|\");\n\n        return NGX_OK;\n    }\n\n    return NGX_OK;\n}\n\n\n\n\nstatic ngx_int_t\nngx_http_v3_parse_path(ngx_http_request_t *r, \n    ngx_http_v3_header_t *header)\n{\n    if (r->unparsed_uri.len) {\n        ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,\n                      \"|xquic|client sent duplicate :path header|\");\n\n        return NGX_DECLINED;\n    }\n\n    if (header->value.len == 0) {\n        ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,\n                      \"|xquic|client sent empty :path header|\");\n\n        return NGX_DECLINED;\n    }\n\n    r->uri_start = header->value.data;\n    r->uri_end = header->value.data + header->value.len;\n\n    if (ngx_http_parse_uri(r) != NGX_OK) {\n        ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,\n                      \"|xquic|client sent invalid :path header: \\\"%V\\\"|\",\n                      &header->value);\n\n        return NGX_DECLINED;\n    }\n\n    if (ngx_http_process_request_uri(r) != NGX_OK) {\n        /*\n         * request has been finalized already\n         * in ngx_http_process_request_uri()\n         */\n        return NGX_ABORT;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_v3_parse_method(ngx_http_request_t *r, \n    ngx_http_v3_header_t *header)\n{\n    size_t         k, len;\n    ngx_uint_t     n;\n    const u_char  *p, *m;\n\n    /*\n     * This array takes less than 256 sequential bytes,\n     * and if typical CPU cache line size is 64 bytes,\n     * it is prefetched for 4 load operations.\n     */\n    static const struct {\n        u_char            len;\n        const u_char      method[11];\n        uint32_t          value;\n    } tests[] = {\n        { 3, \"GET\",       NGX_HTTP_GET },\n        { 4, \"POST\",      NGX_HTTP_POST },\n        { 4, \"HEAD\",      NGX_HTTP_HEAD },\n        { 7, \"OPTIONS\",   NGX_HTTP_OPTIONS },\n        { 8, \"PROPFIND\",  NGX_HTTP_PROPFIND },\n        { 3, \"PUT\",       NGX_HTTP_PUT },\n        { 5, \"MKCOL\",     NGX_HTTP_MKCOL },\n        { 6, \"DELETE\",    NGX_HTTP_DELETE },\n        { 4, \"COPY\",      NGX_HTTP_COPY },\n        { 4, \"MOVE\",      NGX_HTTP_MOVE },\n        { 9, \"PROPPATCH\", NGX_HTTP_PROPPATCH },\n        { 4, \"LOCK\",      NGX_HTTP_LOCK },\n        { 6, \"UNLOCK\",    NGX_HTTP_UNLOCK },\n        { 5, \"PATCH\",     NGX_HTTP_PATCH },\n        { 5, \"TRACE\",     NGX_HTTP_TRACE }\n    }, *test;\n\n    if (r->method_name.len) {\n        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                      \"client sent duplicate :method header\");\n\n        return NGX_DECLINED;\n    }\n\n    if (header->value.len == 0) {\n        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                      \"client sent empty :method header\");\n\n        return NGX_DECLINED;\n    }\n\n    r->method_name.len = header->value.len;\n    r->method_name.data = header->value.data;\n\n    len = r->method_name.len;\n    n = sizeof(tests) / sizeof(tests[0]);\n    test = tests;\n\n    do {\n        if (len == test->len) {\n            p = r->method_name.data;\n            m = test->method;\n            k = len;\n\n            do {\n                if (*p++ != *m++) {\n                    goto next;\n                }\n            } while (--k);\n\n            r->method = test->value;\n            return NGX_OK;\n        }\n\n    next:\n        test++;\n\n    } while (--n);\n\n    p = r->method_name.data;\n\n    do {\n        if ((*p < 'A' || *p > 'Z') && *p != '_') {\n            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                          \"client sent invalid method: \\\"%V\\\"\",\n                          &r->method_name);\n\n            return NGX_DECLINED;\n        }\n\n        p++;\n\n    } while (--len);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_v3_parse_scheme(ngx_http_request_t *r, \n    ngx_http_v3_header_t *header)\n{\n    if (r->schema_start) {\n        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                      \"client sent duplicate :schema header\");\n\n        return NGX_DECLINED;\n    }\n\n    if (header->value.len == 0) {\n        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                      \"client sent empty :schema header\");\n\n        return NGX_DECLINED;\n    }\n\n    r->schema_start = header->value.data;\n    r->schema_end = header->value.data + header->value.len;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_v3_parse_authority(ngx_http_request_t *r, \n    ngx_http_v3_header_t *header)\n{\n    ngx_table_elt_t            *h;\n    ngx_http_header_t          *hh;\n    ngx_http_core_main_conf_t  *cmcf;\n\n    static ngx_str_t host = ngx_string(\"host\");\n\n    h = ngx_list_push(&r->headers_in.headers);\n    if (h == NULL) {\n        return NGX_ERROR;\n    }\n\n    h->hash = ngx_hash_key(host.data, host.len);\n\n    h->key.len = host.len;\n    h->key.data = host.data;\n\n    h->value.len = header->value.len;\n    h->value.data = header->value.data;\n\n    h->lowcase_key = host.data;\n\n    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);\n\n    hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash,\n                       h->lowcase_key, h->key.len);\n\n    if (hh == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (hh->handler(r, h, hh->offset) != NGX_OK) {\n        /*\n         * request has been finalized already\n         * in ngx_http_process_host()\n         */\n        return NGX_ABORT;\n    }\n\n    return NGX_OK;\n}\n\n\n\nstatic ngx_int_t\nngx_http_v3_pseudo_header(ngx_http_request_t *r, \n    ngx_http_v3_header_t *header)\n{\n    header->name.len--;\n    header->name.data++;\n\n    switch (header->name.len) {\n    case 4:\n        if (ngx_memcmp(header->name.data, \"path\", sizeof(\"path\") - 1)\n            == 0)\n        {\n            return ngx_http_v3_parse_path(r, header);\n        }\n\n        break;\n\n    case 6:\n        if (ngx_memcmp(header->name.data, \"method\", sizeof(\"method\") - 1)\n            == 0)\n        {\n            return ngx_http_v3_parse_method(r, header);\n        }\n\n        if (ngx_memcmp(header->name.data, \"scheme\", sizeof(\"scheme\") - 1)\n            == 0)\n        {\n            return ngx_http_v3_parse_scheme(r, header);\n        }\n\n        break;\n\n    case 9:\n        if (ngx_memcmp(header->name.data, \"authority\", sizeof(\"authority\") - 1)\n            == 0)\n        {\n            return ngx_http_v3_parse_authority(r, header);\n        }\n\n        break;\n    }\n\n    ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,\n                  \"|xquic|client sent unknown pseudo header \\\"%V\\\"|\",\n                  &header->name);\n\n    return NGX_ERROR;\n}\n\n\nstatic ngx_int_t\nngx_http_v3_cookie(ngx_http_request_t *r, ngx_http_v3_header_t *header)\n{\n    ngx_str_t    *val;\n    ngx_array_t  *cookies;\n\n    cookies = r->xqstream->cookies;\n\n    if (cookies == NULL) {\n        cookies = ngx_array_create(r->pool, 2, sizeof(ngx_str_t));\n        if (cookies == NULL) {\n            return NGX_ERROR;\n        }\n\n        r->xqstream->cookies = cookies;\n    }\n\n    val = ngx_array_push(cookies);\n    if (val == NULL) {\n        return NGX_ERROR;\n    }\n\n    val->len = header->value.len;\n    val->data = header->value.data;\n\n    return NGX_OK;\n}\n\n\n\nngx_int_t\nngx_http_v3_request_process_header(ngx_http_request_t *r,\n    xqc_http_header_t * in_header)\n{\n    ngx_table_elt_t            *h;\n    ngx_http_header_t          *hh;\n    ngx_http_core_main_conf_t  *cmcf;\n\n    static ngx_str_t cookie = ngx_string(\"cookie\");\n\n\n    /* invalid name length */\n    if (in_header->name.iov_len <= 0) {\n        return NGX_ERROR;\n    }\n\n    /* copy to tmp_header for parsing */\n    ngx_http_v3_header_t tmp_header;\n    ngx_http_v3_header_t *header = &tmp_header;\n    \n    header->name.data = ngx_pcalloc(r->pool, in_header->name.iov_len + 1);\n    if (header->name.data == NULL) {\n        return NGX_ERROR;\n    }\n    ngx_memcpy(header->name.data, in_header->name.iov_base, in_header->name.iov_len);\n    header->name.len = in_header->name.iov_len;\n    header->name.data[header->name.len] = '\\0';\n\n\n    header->value.data = ngx_pcalloc(r->pool, in_header->value.iov_len + 1);\n    if (header->value.data == NULL) {\n        return NGX_ERROR;\n    }\n    ngx_memcpy(header->value.data, in_header->value.iov_base, in_header->value.iov_len);\n    header->value.len = in_header->value.iov_len;  \n    header->value.data[header->value.len] = '\\0';\n\n    /* check for pseudo header */\n    if (header->name.data[0] == ':') {\n        return ngx_http_v3_pseudo_header(r, header);\n    }\n\n\n    /* check for cookies */\n    if (header->name.len == cookie.len\n        && ngx_memcmp(header->name.data, cookie.data, cookie.len) == 0)\n    {\n        return ngx_http_v3_cookie(r, header);\n    }\n\n    /* copy to headers_in */\n    h = ngx_list_push(&r->headers_in.headers);\n    if (h == NULL) {\n        return NGX_ERROR;\n    }\n\n    h->key.len = header->name.len;\n    h->key.data = header->name.data;\n\n    /* TODO Optimization: precalculate hash and handler for indexed headers. */\n    h->hash = ngx_hash_key(h->key.data, h->key.len);\n\n    h->value.len = header->value.len;\n    h->value.data = header->value.data;\n\n    h->lowcase_key = h->key.data;\n    \n    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);\n\n    hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash,\n                       h->lowcase_key, h->key.len);\n\n    if (hh && hh->handler(r, h, hh->offset) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_v3_init_request_body(ngx_http_request_t *r)\n{\n    ngx_buf_t                 *buf;\n    ngx_temp_file_t           *tf;\n    ngx_http_request_body_t   *rb;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t));\n    if (rb == NULL) {\n        return NGX_ERROR;\n    }\n\n    r->request_body = rb;\n\n    if (r->xqstream->in_closed) {\n        return NGX_OK;\n    }\n\n    rb->rest = r->headers_in.content_length_n;\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    if (r->request_body_in_file_only\n        || rb->rest > (off_t) clcf->client_body_buffer_size\n        /*|| rb->rest < 0*/)\n    {\n        tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t));\n        if (tf == NULL) {\n            return NGX_ERROR;\n        }\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 = r->request_body_in_persistent_file;\n        tf->clean = r->request_body_in_clean_file;\n\n        if (r->request_body_file_group_access) {\n            tf->access = 0660;\n        }\n\n        rb->temp_file = tf;\n\n        if (r->xqstream->in_closed\n            && 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        buf = ngx_calloc_buf(r->pool);\n        if (buf == NULL) {\n            return NGX_ERROR;\n        }\n\n    } else {\n\n        if (rb->rest == 0) {\n            return NGX_OK;\n        }\n\n        buf = ngx_create_temp_buf(r->pool,\n                rb->rest < 0 ? clcf->client_body_buffer_size : ngx_min((size_t)rb->rest, clcf->client_body_buffer_size));\n        if (buf == NULL) {\n            return NGX_ERROR;\n        }\n    }\n\n    rb->buf = buf;\n\n    rb->bufs = ngx_alloc_chain_link(r->pool);\n    if (rb->bufs == NULL) {\n        return NGX_ERROR;\n    }\n\n    rb->bufs->buf = buf;\n    rb->bufs->next = NULL;\n\n    rb->rest = 0;\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_v3_recv_body(ngx_http_request_t         *r, \n    ngx_http_v3_stream_t      *stream, \n    xqc_h3_request_t          *h3_request)\n{\n    ngx_http_core_loc_conf_t  *clcf;\n    ngx_buf_t                 *buf;\n    ngx_int_t                  rc;\n    ngx_connection_t          *fc;\n    off_t                      len;\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n    fc = r->connection;\n\n    /* check if skip data */\n    if (stream->skip_data) {\n        stream->in_closed = 1;\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n                       \"|xquic|ngx_http_v3_recv_body|skipping http3 DATA, reason: %d|\",\n                       stream->skip_data);\n\n        return NGX_ERROR;\n    }\n\n    if (r->request_body && r->request_body->buf) {\n        buf = r->request_body->buf;\n\n    } else {\n        buf = stream->body_buffer;\n\n        if (buf == NULL) {\n            len = clcf->client_body_buffer_size;\n\n            buf = ngx_create_temp_buf(r->pool, (size_t) len);\n            if (buf == NULL) {\n                ngx_log_error(NGX_LOG_WARN, fc->log, 0,\n                              \"|xquic|ngx_http_v3_recv_body|create temp buf error|\");\n\n                return NGX_ERROR;\n            }\n\n            stream->body_buffer = buf;\n        }\n    }\n\n    ssize_t size = 0;\n    uint8_t fin = 0;\n\n    do {\n        if (buf->last == buf->end) {\n            len = buf->end - buf->start;\n            off_t pos_len = buf->pos - buf->start;\n\n            u_char *new_buf = ngx_pcalloc(r->pool, len * 2);\n            if (new_buf == NULL) {\n                ngx_log_error(NGX_LOG_WARN, fc->log, 0,\n                              \"|xquic|ngx_http_v3_recv_body|ngx_pcalloc error|\");\n\n                return NGX_ERROR;\n            }\n\n            ngx_memcpy(new_buf, buf->start, len);\n            buf->pos = new_buf + pos_len;\n            buf->last = new_buf + len;\n            buf->end = new_buf + len * 2;\n            ngx_pfree(r->pool, buf->start);\n            buf->start = new_buf;\n        }\n\n        ngx_log_error(NGX_LOG_DEBUG, fc->log, 0,\n                      \"|xquic|ngx_http_v3_recv_body|buf->size:%z|\", buf->end - buf->last);\n\n        size = xqc_h3_request_recv_body(h3_request, buf->last, buf->end - buf->last, &fin);\n        if (size == -XQC_EAGAIN) {\n            break;\n        }\n        if (size < 0) {\n            ngx_log_error(NGX_LOG_WARN, fc->log, 0,\n                          \"|xquic|ngx_http_v3_recv_body|xqc_h3_request_recv_body error:%z|\", size);\n            return NGX_ERROR;\n        }\n\n        buf->last += size;\n        ngx_log_error(NGX_LOG_DEBUG, fc->log, 0,\n                      \"|xquic|ngx_http_v3_recv_body|xqc_h3_request_recv_body size:%z|\", size);\n\n    } while (size > 0 && !fin);\n\n    if (r->request_body) {\n        rc = ngx_http_v3_process_request_body(r, NULL, 0, fin);\n\n        if (rc != NGX_OK) {\n            ngx_log_error(NGX_LOG_WARN, fc->log, 0,\n                          \"|xquic|ngx_http_v3_recv_body|ngx_http_v3_process_request_body error:%z|\", rc);\n            return NGX_ERROR;\n        }\n\n    }\n\n    if (fin) {\n\n        stream->in_closed = 1;\n    }\n\n    return NGX_OK;\n}\n\n\nvoid\nngx_http_v3_state_headers_complete(ngx_http_v3_stream_t *h3_stream)\n{\n    ngx_http_v3_run_request(h3_stream->request, h3_stream);\n}\n\n\nint \nngx_http_v3_request_read_notify(xqc_h3_request_t *h3_request, xqc_request_notify_flag_t flag,\n    void *user_data)\n{\n    size_t                       i;\n    unsigned char                fin = 0;\n    ngx_http_v3_stream_t        *user_stream = (ngx_http_v3_stream_t *) user_data;\n    ngx_http_request_t          *r = user_stream->request;\n  //ngx_connection_t            *fc = r->connection;\n  //ngx_http_request_body_t     *rb = r->request_body;\n    ngx_int_t                    ret;\n\n    ngx_log_error(NGX_LOG_DEBUG, ngx_cycle->log, 0, \n                                \"|xquic|ngx_http_v3_request_read_notify|\");\n\n    if (user_stream->request_closed) {\n        ngx_log_error(NGX_LOG_WARN, ngx_cycle->log, 0, \n                                \"|xquic|ngx_http_v3_request_read_notify|request_closed|\");\n        return NGX_OK;\n    }\n\n    if (user_stream->header_recvd == 0\n        && (flag & XQC_REQ_NOTIFY_READ_HEADER)) \n    {\n        xqc_http_headers_t *headers;\n        headers = xqc_h3_request_recv_headers(h3_request, &fin);\n        if (headers == NULL) {\n            ngx_log_error(NGX_LOG_WARN, ngx_cycle->log, 0,\n                            \"|xquic|xqc_h3_request_recv_headers error|\");\n            return NGX_ERROR;\n        }\n\n        for (i = 0; i < headers->count; i++) {\n            ngx_log_error(NGX_LOG_DEBUG, ngx_cycle->log, 0,\n                            \"|xquic|header name:%*s value:%*s|\",\n                            headers->headers[i].name.iov_len, headers->headers[i].name.iov_base, \n                            headers->headers[i].value.iov_len, headers->headers[i].value.iov_base);\n\n            ret = ngx_http_v3_request_process_header(user_stream->request, \n                        &(headers->headers[i]));\n\n            /* NGX_ABORT - request has been closed */\n            /* NGX_ERROR,NGX_DECLINED - request err but not closed */   \n\n            if (ret != NGX_OK) {\n                ngx_log_error(NGX_LOG_WARN, ngx_cycle->log, 0,\n                                \"|xquic|ngx_http_v3_request_process_header error|header name:%*s value:%*s|\",\n                                headers->headers[i].name.iov_len, headers->headers[i].name.iov_base, \n                                headers->headers[i].value.iov_len,headers->headers[i].value.iov_base);\n                return NGX_ERROR; \n            }\n        }\n\n        user_stream->header_recvd = 1;\n\n        if (fin) {\n            user_stream->in_closed = 1;\n        }\n\n        /* MUST finish read header first, then run request */\n        ngx_http_v3_state_headers_complete(user_stream);\n    }\n\n    /* wait for request body */\n    if (!(flag & XQC_REQ_NOTIFY_READ_BODY)) {\n        return NGX_OK;\n    }\n\n    if (user_stream->request_closed) {\n        ngx_log_error(NGX_LOG_WARN, ngx_cycle->log, 0,\n                   \"|xquic|ngx_http_v3_recv_body error, request closed|\");\n        return NGX_ERROR;\n    }\n\n    /* read request body */\n    if (ngx_http_v3_recv_body(r, user_stream, h3_request) != NGX_OK) {\n        ngx_log_error(NGX_LOG_WARN, ngx_cycle->log, 0,\n                        \"|xquic|ngx_http_v3_recv_body error|\");\n        return NGX_ERROR; \n    }\n\n    return NGX_OK;\n}\n\n\n\n\nstatic ngx_int_t\nngx_http_v3_construct_request_line(ngx_http_request_t *r)\n{\n    u_char  *p;\n\n    static const u_char ending[] = \" HTTP/3.0\";\n\n    if (r->method_name.len == 0\n        || r->unparsed_uri.len == 0)\n    {\n        ngx_http_v3_stream_cancel(r->xqstream, NGX_HTTP_BAD_REQUEST);\n        return NGX_ERROR;\n    }\n\n    r->request_line.len = r->method_name.len + 1\n                          + r->unparsed_uri.len\n                          + sizeof(ending) - 1;\n\n    p = ngx_pnalloc(r->pool, r->request_line.len + 1);\n    if (p == NULL) {\n        ngx_http_v3_stream_cancel(r->xqstream, NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return NGX_ERROR;\n    }\n\n    r->request_line.data = p;\n\n    p = ngx_cpymem(p, r->method_name.data, r->method_name.len);\n\n    *p++ = ' ';\n\n    p = ngx_cpymem(p, r->unparsed_uri.data, r->unparsed_uri.len);\n\n    ngx_memcpy(p, ending, sizeof(ending));\n\n    /* some modules expect the space character after method name */\n    r->method_name.data = r->request_line.data;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"|xquic|http3 http request line: \\\"%V\\\"|\", &r->request_line);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_v3_construct_cookie_header(ngx_http_request_t *r)\n{\n    u_char                     *buf, *p, *end;\n    size_t                      len;\n    ngx_str_t                  *vals;\n    ngx_uint_t                  i;\n    ngx_array_t                *cookies;\n    ngx_table_elt_t            *h;\n    ngx_http_header_t          *hh;\n    ngx_http_core_main_conf_t  *cmcf;\n\n    static ngx_str_t cookie = ngx_string(\"cookie\");\n\n    cookies = r->xqstream->cookies;\n\n    if (cookies == NULL) {\n        return NGX_OK;\n    }\n\n    vals = cookies->elts;\n\n    i = 0;\n    len = 0;\n\n    do {\n        len += vals[i].len + 2;\n    } while (++i != cookies->nelts);\n\n    len -= 2;\n\n    buf = ngx_pnalloc(r->pool, len + 1);\n    if (buf == NULL) {\n        ngx_http_v3_close_stream(r->xqstream, NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return NGX_ERROR;\n    }\n\n    p = buf;\n    end = buf + len;\n\n    for (i = 0; /* void */ ; i++) {\n\n        p = ngx_cpymem(p, vals[i].data, vals[i].len);\n\n        if (p == end) {\n            *p = '\\0';\n            break;\n        }\n\n        *p++ = ';'; *p++ = ' ';\n    }\n\n    h = ngx_list_push(&r->headers_in.headers);\n    if (h == NULL) {\n        ngx_http_v3_close_stream(r->xqstream, NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return NGX_ERROR;\n    }\n\n    h->hash = ngx_hash_key(cookie.data, cookie.len);\n\n    h->key.len = cookie.len;\n    h->key.data = cookie.data;\n\n    h->value.len = len;\n    h->value.data = buf;\n\n    h->lowcase_key = cookie.data;\n\n    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);\n\n    hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash,\n                       h->lowcase_key, h->key.len);\n\n    if (hh == NULL) {\n        ngx_http_v3_close_stream(r->xqstream, NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return NGX_ERROR;\n    }\n\n    if (hh->handler(r, h, hh->offset) != NGX_OK) {\n        /*\n         * request has been finalized already\n         * in ngx_http_process_multi_header_lines()\n         */\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\n\nstatic void\nngx_http_v3_run_request(ngx_http_request_t *r, \n    ngx_http_v3_stream_t *h3_stream)\n{\n    /* MUST only run once */\n    if (h3_stream->run_request) {\n        return;\n    }\n    h3_stream->run_request = 1;\n\n    if (ngx_http_v3_construct_request_line(r) != NGX_OK) {\n        return;\n    }\n\n    if (ngx_http_v3_construct_cookie_header(r) != NGX_OK) {\n        return;\n    }\n\n    r->http_state = NGX_HTTP_PROCESS_REQUEST_STATE;\n\n    if (ngx_http_process_request_header(r) != NGX_OK) {\n        ngx_http_v3_stream_cancel(r->xqstream, NGX_HTTP_BAD_REQUEST);\n        return;\n    }\n\n    if (r->headers_in.content_length_n == -1 && !r->xqstream->in_closed) {\n        r->headers_in.chunked = 1;\n    }\n\n    ngx_http_process_request(r);\n}\n\n\n/**\n * Abnormal\n */\nvoid\nngx_http_v3_stream_cancel(ngx_http_v3_stream_t *h3_stream,\n    ngx_int_t status)\n{\n    xqc_h3_request_t *h3_request = h3_stream->h3_request;\n    h3_stream->cancel_status = status;\n\n    /* engine will call ngx_http_v3_request_close_notify and free stream */\n    ngx_int_t ret = xqc_h3_request_close(h3_request);\n    if (ret != NGX_OK) {\n        ngx_log_error(NGX_LOG_WARN, ngx_cycle->log, 0, \n                    \"|xquic|xqc_h3_request_close err|%i|\", ret);\n    }\n\n    if (!h3_stream->request_closed) {\n        ngx_http_finalize_request(h3_stream->request, status);\n        h3_stream->request = NULL;\n    }\n}\n\n\nstatic void\nngx_http_v3_stream_free(ngx_http_v3_stream_t *h3_stream)\n{\n    ngx_http_xquic_connection_t  *h3c;\n\n    h3c = h3_stream->connection;\n\n    /* don't delete node from streams_index, free in conn close */\n    h3_stream->list_node->entry = NULL;\n    h3_stream->list_node = NULL;\n    ngx_memzero(h3_stream, sizeof(ngx_http_v3_stream_t));\n\n    /* recycle stream */\n    h3_stream->next = h3c->free_streams;\n    h3c->free_streams = h3_stream;        \n    h3_stream->closed = 1;\n}\n\n\n/**\n * Normal - ngx_http_close_request\n */\nvoid\nngx_http_v3_close_stream(ngx_http_v3_stream_t *h3_stream, \n    ngx_int_t rc)\n{\n    ngx_event_t                  *ev;\n    ngx_connection_t             *fc;\n    ngx_http_xquic_connection_t  *h3c;\n    ngx_http_request_t           *r = NULL;\n\n    if (h3_stream->request_closed || h3_stream->closed) {\n        return;\n    }\n\n    h3c = h3_stream->connection;\n    fc = h3_stream->request->connection;\n    r = h3_stream->request;\n\n    if (h3_stream->engine_inner_closed == 0 && h3_stream->queued) {\n        fc->write->handler = ngx_http_v3_close_stream_handler;\n        fc->read->handler = ngx_http_v3_close_stream_handler;\n        return;\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n                   \"|xquic| close stream %ui, processing %ui|\",\n                   h3_stream->id, h3c->processing);\n\n\n    h3_stream->request_closed = 1;\n\n    if (!h3_stream->closed \n        && h3_stream->engine_inner_closed) \n    {\n        /* only free here if engine_inner_closed */\n        ngx_http_v3_stream_free(h3_stream);\n    }\n\n    /* h3_stream has been freed */\n\n    fc = r->connection;\n\n    /* Do not need to close xquic h3 request here */\n\n    ngx_http_free_request(r, rc);\n\n    ev = fc->read;\n\n    if (ev->timer_set) {\n        ngx_del_timer(ev);\n    }\n\n#if (nginx_version >= 1007005 || tengine_version >= 2000002)\n    if (ev->posted) {\n#else\n    if (ev->prev) {\n#endif\n        ngx_delete_posted_event(ev);\n    }\n\n    ev = fc->write;\n\n    if (ev->timer_set) {\n        ngx_del_timer(ev);\n    }\n\n#if (nginx_version >= 1007005 || tengine_version >= 2000002)\n    if (ev->posted) {\n#else\n    if (ev->prev) {\n#endif\n        ngx_delete_posted_event(ev);\n    }\n\n    /* use (void *)data as next point  */\n    fc->data = (void *) h3c->free_fake_connections;\n    h3c->free_fake_connections = fc;\n\n\n    h3c->processing--;\n\n    if (h3c->processing == 0) {\n        if (h3c->closing && h3c->wait_to_close) {\n            ngx_log_debug(NGX_LOG_DEBUG_HTTP, h3c->connection->log, 0,\n                          \"|xquic|close connection after stream close|\");\n            ngx_http_v3_finalize_connection(h3c, NGX_XQUIC_CONN_NO_ERR);\n\n            return;\n        }\n\n    }\n}\n\n\n\n"
  },
  {
    "path": "modules/ngx_http_xquic_module/ngx_http_v3_stream.h",
    "content": "/*\n * Copyright (C) 2020-2023 Alibaba Group Holding Limited\n */\n\n#ifndef _NGX_HTTP_V3_STREAM_H_INCLUDED_\n#define _NGX_HTTP_V3_STREAM_H_INCLUDED_\n\n\n#include <ngx_core.h>\n#include <ngx_config.h>\n#include <ngx_http.h>\n#include <ngx_http_xquic.h>\n\n#include <xquic/xquic.h>\n#include <xquic/xqc_http3.h>\n\n\n#define NGX_HTTP_V3_DATA_DISCARD         1\n#define NGX_HTTP_V3_DATA_ERROR           2\n#define NGX_HTTP_V3_DATA_INTERNAL_ERROR  3\n\n\n\nint ngx_http_v3_request_create_notify(xqc_h3_request_t *h3_request, void *user_data);\nint ngx_http_v3_request_close_notify(xqc_h3_request_t *h3_request, void *user_data);\nint ngx_http_v3_request_write_notify(xqc_h3_request_t *h3_request, void *user_data);\nint ngx_http_v3_request_read_notify(xqc_h3_request_t *h3_request, xqc_request_notify_flag_t flag,\n    void *user_data);\nint ngx_http_v3_request_send(xqc_h3_request_t *h3_request, \n    ngx_http_v3_stream_t *user_stream);\n\nngx_int_t ngx_http_v3_init_request_body(ngx_http_request_t *r);\nngx_int_t ngx_http_v3_recv_body(ngx_http_request_t *r, ngx_http_v3_stream_t *stream, \n    xqc_h3_request_t *h3_request);\n\n\nvoid ngx_http_v3_close_stream(ngx_http_v3_stream_t *h3_stream, ngx_int_t rc);\n\n#endif /* _NGX_HTTP_V3_STREAM_H_INCLUDED_ */\n\n"
  },
  {
    "path": "modules/ngx_http_xquic_module/ngx_http_xquic.c",
    "content": "/*\n * Copyright (C) 2020-2023 Alibaba Group Holding Limited\n */\n\n/**  \n * for http v3 connection\n */\n\n#include <ngx_http_xquic.h>\n#include <ngx_http_xquic_module.h>\n#include <ngx_http_v3_stream.h>\n#include <ngx_xquic_recv.h>\n#include <ngx_xquic.h>\n\n#include <sys/socket.h>\n#include <netinet/udp.h>\n\n#if (T_NGX_HAVE_XUDP)\n#include <ngx_xudp.h>\n#endif\n\n\n#ifdef T_NGX_HTTP_HAVE_LUA_MODULE\n#include <ngx_http_lua_ssl_certby.h>\nextern ngx_module_t ngx_http_lua_module;\n#endif\n\nngx_int_t\nngx_http_v3_conn_check_concurrent_cnt(ngx_http_xquic_main_conf_t *qmcf)\n{\n    /* limit not configured */\n    if (qmcf->max_quic_concurrent_connection_cnt == NGX_CONF_UNSET_UINT) {\n        return NGX_OK;\n    }\n\n    /* decline if limitation is set and reached max connection count limit */\n    ngx_atomic_uint_t quic_concurrent_conn_cnt = *ngx_stat_quic_concurrent_conns;\n    if (quic_concurrent_conn_cnt >= qmcf->max_quic_concurrent_connection_cnt) {\n        ngx_log_error(NGX_LOG_WARN, ngx_cycle->log, 0, \"|xquic|reached max connection limit\"\n                      \"|limit:%ui|cnt:%ui|\", qmcf->max_quic_concurrent_connection_cnt, quic_concurrent_conn_cnt);\n        return NGX_DECLINED;\n    }\n\n    return NGX_OK;\n}\n\nngx_int_t\nngx_http_v3_conn_check_cps(ngx_http_xquic_main_conf_t *qmcf)\n{\n    /* limit not configured */\n    if (qmcf->max_quic_cps == NGX_CONF_UNSET_UINT) {\n        return NGX_OK;\n    }\n\n    /* check max cps limit */\n    ngx_atomic_uint_t quic_cps_nexttime = *ngx_stat_quic_cps_nexttime;\n    if (ngx_current_msec <= quic_cps_nexttime) {\n        /* still in current stat round, check cps limit, decline if reached max cps limit */\n        if (*ngx_stat_quic_cps >= qmcf->max_quic_cps) {\n            ngx_log_error(NGX_LOG_WARN, ngx_cycle->log, 0, \"|xquic|reached max cps limit\"\n                    \"|limit:%ui|now:%ui|next_time:%ui|\", qmcf->max_quic_cps, ngx_current_msec, quic_cps_nexttime);\n            return NGX_DECLINED;\n        }\n\n    } else {\n        /* start a new stat round */\n        ngx_atomic_cmp_set(ngx_stat_quic_cps_nexttime,\n            *ngx_stat_quic_cps_nexttime, ngx_current_msec + 1000);\n        ngx_atomic_cmp_set(ngx_stat_quic_cps, *ngx_stat_quic_cps, 0);\n    }\n\n    return NGX_OK;\n}\n\nint \nngx_xquic_conn_accept(xqc_engine_t *engine, xqc_connection_t *conn, \n    const xqc_cid_t * cid, void * user_data)\n{\n    ngx_log_error(NGX_LOG_DEBUG, ngx_cycle->log, 0, \n                    \"|xquic|ngx_xquic_server_conn_accept|dcid=%s|\", xqc_dcid_str(cid));\n\n    if (user_data == NULL) {\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, \n                      \"|xquic|ngx_xquic_server_conn_accept|user_data is NULL|dcid=%s|\", xqc_dcid_str(cid));\n        return XQC_ERROR;\n    }\n\n    ngx_connection_t *lc = (ngx_connection_t *)user_data;\n\n    ngx_http_xquic_main_conf_t  *qmcf = ngx_http_cycle_get_module_main_conf(ngx_cycle, ngx_http_xquic_module);\n\n    /* check connection limit */\n    if (ngx_http_v3_conn_check_concurrent_cnt(qmcf) != NGX_OK\n        || ngx_http_v3_conn_check_cps(qmcf) != NGX_OK)\n    {\n        (void) ngx_atomic_fetch_add(ngx_stat_quic_conns_refused, 1);\n        return XQC_ERROR;\n    }\n  \n\n    socklen_t peer_addrlen; \n    socklen_t local_addrlen;\n    struct sockaddr_storage peer_addr;\n    struct sockaddr_storage local_addr;\n    if (xqc_conn_get_peer_addr(conn, (struct sockaddr *)(&peer_addr), sizeof(peer_addr), &peer_addrlen) != XQC_OK) {\n        ngx_log_error(NGX_LOG_WARN, ngx_cycle->log, 0, \n                    \"|xquic|ngx accept copy peer addr fail|\");\n        return NGX_ERROR;\n    }\n    if (xqc_conn_get_local_addr(conn, (struct sockaddr *)(&local_addr), sizeof(local_addr), &local_addrlen) != XQC_OK) {\n        ngx_log_error(NGX_LOG_WARN, ngx_cycle->log, 0, \n                    \"|xquic|ngx accept copy local addr fail|\");\n        return NGX_ERROR;\n    }\n\n    /* init user data */\n    ngx_http_xquic_connection_t* qc = ngx_http_v3_create_connection(\n                            (ngx_connection_t *)lc, cid, \n                            (struct sockaddr *)&local_addr, local_addrlen,\n                            (struct sockaddr *)&peer_addr, peer_addrlen,\n                            qmcf->xquic_engine);\n    if (qc == NULL) {\n        ngx_log_error(NGX_LOG_WARN, ngx_cycle->log, 0, \n                    \"|xquic|ngx_http_v3_create_connection fail|\");\n        return NGX_ERROR;\n    }\n\n    xqc_conn_set_transport_user_data(conn, qc);\n    /* temporarily set the alp user_data of conn, will overwrite when h3_conn_create_notify callback triggers */\n    xqc_conn_set_alp_user_data(conn, qc);\n\n    /* add connection count and cps statistics */\n    (void) ngx_atomic_fetch_add(ngx_stat_quic_conns, 1);\n    (void) ngx_atomic_fetch_add(ngx_stat_quic_cps, 1);\n    (void) ngx_atomic_fetch_add(ngx_stat_quic_concurrent_conns, 1);\n\n    return NGX_OK;\n}\n\nvoid\nngx_xquic_conn_refuse(xqc_engine_t *engine, xqc_connection_t *conn, \n    const xqc_cid_t *cid, void *user_data)\n{\n    ngx_log_error(NGX_LOG_WARN, ngx_cycle->log, 0, \n                    \"|xquic|ngx_xquic_server_conn_refuse|scid=%s|\", xqc_dcid_str(cid));\n\n    uint64_t err = xqc_conn_get_errno(conn);\n\n    ngx_http_xquic_connection_t *qc = (ngx_http_xquic_connection_t *)user_data;\n    if (qc == NULL) {\n        ngx_log_error(NGX_LOG_WARN, ngx_cycle->log, 0, \n                    \"|xquic|user_data is NULL|cid:%s|\", xqc_dcid_str(cid));\n        return;\n    }\n\n    ngx_http_v3_finalize_connection(qc, err);\n\n    (void) ngx_atomic_fetch_add(ngx_stat_quic_concurrent_conns, -1);\n}\n\nngx_int_t\nngx_http_find_virtual_server_inner(ngx_connection_t *c,\n    ngx_http_virtual_names_t *virtual_names, ngx_str_t *host,\n    ngx_http_request_t *r, ngx_http_core_srv_conf_t **cscfp);\n\nxqc_int_t\nngx_http_v3_cert_cb(const char *sni, void **chain,\n    void **cert, void **key, void *conn_user_data)\n{\n    ngx_int_t                       ret;\n    int                             ssl_ret;\n    ngx_str_t                       host;\n    ngx_connection_t               *c;\n    ngx_http_connection_t          *hc;\n    ngx_http_ssl_srv_conf_t        *sscf;\n    ngx_http_core_srv_conf_t       *cscf;\n    ngx_http_xquic_connection_t    *qc;\n    STACK_OF(X509)                 *cert_chain;\n    X509                           *certificate;\n    EVP_PKEY                       *private_key;\n\n    if (NULL == sni || NULL == conn_user_data) {\n        return -XQC_EPARAM;\n    }\n\n    host.data = (u_char *)sni;\n    host.len = strlen(sni);\n\n    /* default http connection */\n    qc = (ngx_http_xquic_connection_t *)conn_user_data;\n    hc = qc->http_connection;\n    c = qc->connection;\n\n    /* The ngx_http_find_virtual_server() function requires ngx_http_connection_t in c->data */\n    c->data = hc;\n\n    /*\n     * get the server core conf by sni, this is useful when multiple server\n     * block listen on the same port. but useless when there is noly a single\n     * server block\n    */\n   ret = ngx_http_find_virtual_server_inner(c,\n            hc->addr_conf->virtual_names, &host, NULL, &cscf);\n   c->data = qc;\n\n    if (ret == NGX_OK) {\n        hc->ssl_servername = ngx_palloc(c->pool, sizeof(ngx_str_t));\n        if (hc->ssl_servername == NULL) {\n            ngx_log_error(NGX_LOG_WARN, ngx_cycle->log, 0,\n                        \"|xquic|crete ssl_servername fail|\");\n\n            return XQC_ERROR;\n        }\n\n        /* get server config */\n        *hc->ssl_servername = host;\n        hc->conf_ctx = cscf->ctx;\n\n    } else {\n        /* try to get ssl config from the default connection */\n        ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, 0,\n                     \"|xquic|can't find virtual server, use default server|\");\n    }\n\n#ifdef T_NGX_HTTP_HAVE_LUA_MODULE\n    ngx_http_lua_srv_conf_t *lscf = NULL;\n\n    lscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_lua_module);\n    if (lscf != NULL && lscf->srv.ssl_cert_src.len)  {\n        ngx_ssl_conn_t *ssl_conn = qc->ssl_conn;\n\n        ngx_http_lua_ssl_cert_handler(ssl_conn, NULL);\n        *chain = NULL;\n        *cert = NULL;\n        *key = NULL;\n\n        return XQC_OK;\n    }\n#endif\n\n    /* get http ssl config */\n    sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_ssl_module);\n    if (NULL == sscf || NULL == sscf->ssl.ctx) {\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                    \"|xquic|CFG or CTX not found||sni:%s|\", sni);\n        return XQC_ERROR;\n    }\n\n    ssl_ret = SSL_CTX_get0_chain_certs(sscf->ssl.ctx, &cert_chain);\n    if (ssl_ret != 1) {\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                    \"|xquic|get chain certificate fail|err=%i\", ssl_ret);\n        return XQC_ERROR;\n    }\n\n    certificate = SSL_CTX_get0_certificate(sscf->ssl.ctx);\n    private_key = SSL_CTX_get0_privatekey(sscf->ssl.ctx);\n\n    if (NULL == certificate) {\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                    \"|xquic|get certificate fail|\");\n        return XQC_ERROR;\n    }\n\n    if (NULL == private_key) {\n        /*  keyless server */\n        ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, 0,\n                    \"|xquic|get private key fail, \\\"ssl_keyless off\\\" config \"\n                    \"lost|sni:%s\", sni);\n        return XQC_ERROR;\n    }\n\n    *chain = cert_chain;\n    *cert = certificate;\n    *key = private_key;\n\n    return XQC_OK;\n}\n\nint \nngx_http_v3_conn_create_notify(xqc_h3_conn_t *h3_conn, \n    const xqc_cid_t *cid, void *user_data)\n{\n    ngx_connection_t               *c;\n\n    /* we set alp user_data when accept connection */\n    ngx_http_xquic_connection_t *user_conn = (ngx_http_xquic_connection_t *) user_data;\n    user_conn->ssl_conn = (ngx_ssl_conn_t *) xqc_h3_conn_get_ssl(h3_conn);\n\n    ngx_log_error(NGX_LOG_DEBUG, ngx_cycle->log, 0, \n                    \"|xquic|ngx_http_v3_conn_create_notify|%p|\", user_conn->engine);\n\n    xqc_h3_conn_set_user_data(h3_conn, user_conn);\n\n    c = user_conn->connection;\n\n    if (SSL_set_ex_data(user_conn->ssl_conn, ngx_ssl_connection_index, c) == 0)\n    {\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, \"|xquic|SSL_set_ex_data() failed|\");\n        return XQC_ERROR;\n    }\n    \n    c->xquic_conn = 1;\n\n    ngx_ssl_connection_t *p_ssl = ngx_pcalloc(c->pool, sizeof(ngx_ssl_connection_t));\n    if (p_ssl ==  NULL) {\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, \"|xquic|alloc ngx_ssl_connection_t failed|\");\n        return XQC_ERROR;\n    }\n    p_ssl->connection = user_conn->ssl_conn;\n    c->ssl = p_ssl;\n\n#if (T_NGX_SSL_HANDSHAKE_TIME)\n    /* ssl handshake start time */\n    ngx_time_t *tp = ngx_timeofday();\n    c->ssl->handshake_start_msec = tp->sec * 1000 + tp->msec;\n#endif\n\n    return NGX_OK;\n}\n\n\nint \nngx_http_v3_conn_close_notify(xqc_h3_conn_t *h3_conn, \n    const xqc_cid_t *cid, void *user_data) \n{\n    uint64_t err = xqc_h3_conn_get_errno(h3_conn);\n\n    ngx_log_error(NGX_LOG_DEBUG, ngx_cycle->log, 0, \n                    \"|xquic|ngx_http_v3_conn_close_notify|err=%i|\", err);\n\n    if (err != H3_NO_ERROR) {\n        ngx_log_error(NGX_LOG_WARN, ngx_cycle->log, 0, \n                    \"|xquic|ngx_http_v3_conn_close|err=%i|\", err);\n    }\n\n    ngx_http_xquic_connection_t *h3c = (ngx_http_xquic_connection_t *)user_data;\n    ngx_http_v3_finalize_connection(h3c, err);\n\n    (void) ngx_atomic_fetch_add(ngx_stat_quic_concurrent_conns, -1);\n\n    return NGX_OK;\n}\n\n\nvoid \nngx_http_v3_conn_handshake_finished(xqc_h3_conn_t *h3_conn, void *user_data)\n{\n    ngx_http_xquic_connection_t *user_conn = (ngx_http_xquic_connection_t *) user_data;\n\n    ngx_log_error(NGX_LOG_DEBUG, ngx_cycle->log, 0, \n                    \"|xquic|ngx_http_v3_conn_handshake_finished|dcid=%s|\", \n                    xqc_dcid_str(&user_conn->dcid));\n\n\n    /* TODO */\n#if (T_NGX_SSL_HANDSHAKE_TIME)\n    ngx_connection_t *c = user_conn->connection;\n    if (c != NULL) {\n        ngx_time_t *tp;\n        tp = ngx_timeofday();\n        c->ssl->handshake_end_msec = tp->sec * 1000 + tp->msec;\n    }\n#endif\n}\n\n\nvoid\nngx_http_v3_conn_update_cid_notify(xqc_connection_t *conn, const xqc_cid_t *retire_cid,\n    const xqc_cid_t *new_cid, void *conn_user_data)\n{\n    ngx_http_xquic_connection_t *user_conn = (ngx_http_xquic_connection_t *) conn_user_data;\n\n    ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, 0, \n                \"|xquic|ngx_http_v3_conn_update_cid_notify|old_cid=%s|new_cid:%s|\", \n                xqc_dcid_str(retire_cid), xqc_scid_str(new_cid));\n\n    memcpy(&user_conn->dcid, new_cid, sizeof(xqc_cid_t));\n}\n\n\nngx_int_t\nngx_http_v3_read_request_body(ngx_http_request_t *r)\n{\n    off_t                      len;\n    ngx_http_v3_stream_t      *stream;\n    ngx_http_request_body_t   *rb;\n    ngx_http_core_loc_conf_t  *clcf;\n    ngx_buf_t                 *buf;\n    ngx_int_t                  rc;\n    ngx_connection_t          *fc;\n\n    stream = r->xqstream;\n    rb = r->request_body;\n    fc = r->connection;\n\n    ngx_log_error(NGX_LOG_DEBUG, fc->log, 0,\n                  \"|xquic|ngx_http_v3_read_request_body|\");\n\n    if (stream->skip_data) {\n        r->request_body_no_buffering = 0;\n        rb->post_handler(r);\n        return NGX_OK;\n    }\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    len = r->headers_in.content_length_n;\n\n    if (r->request_body_no_buffering && !stream->in_closed) {\n\n        if (len < 0 || len > (off_t) clcf->client_body_buffer_size) {\n            len = clcf->client_body_buffer_size;\n        }\n\n        rb->buf = ngx_create_temp_buf(r->pool, (size_t) len);\n\n    } else if (len >= 0 && len <= (off_t) clcf->client_body_buffer_size\n               && !r->request_body_in_file_only)\n    {\n        rb->buf = ngx_create_temp_buf(r->pool, (size_t) len);\n\n    } else {\n        rb->buf = ngx_create_temp_buf(r->pool, (size_t) clcf->client_body_buffer_size);\n        if (rb->buf != NULL) {\n            rb->buf->sync = 1;\n        }\n    }\n\n    if (rb->buf == NULL) {\n        stream->skip_data = 1;\n        ngx_log_error(NGX_LOG_WARN, fc->log, 0,\n                      \"|xquic|ngx_http_v3_read_request_body|create request_body error|\");\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    rb->rest = 1;\n\n    buf = stream->body_buffer;\n\n    if (stream->in_closed) {\n        r->request_body_no_buffering = 0;\n\n        if (buf) {\n            rc = ngx_http_v3_process_request_body(r, buf->pos, buf->last - buf->pos, 1);\n\n            ngx_pfree(r->pool, buf->start);\n\n            return rc;\n        }\n\n        return ngx_http_v3_process_request_body(r, NULL, 0, 1);\n    }\n\n    if (buf) {\n        rc = ngx_http_v3_process_request_body(r, buf->pos, buf->last - buf->pos, 0);\n\n        ngx_pfree(r->pool, buf->start);\n\n        if (rc != NGX_OK) {\n            stream->skip_data = 1;\n            return rc;\n        }\n    } else {\n        ngx_add_timer(r->connection->read, clcf->client_body_timeout);\n    }\n\n    r->read_event_handler = ngx_http_v3_read_client_request_body_handler;\n    r->write_event_handler = ngx_http_request_empty_handler;\n\n    return NGX_AGAIN;\n}\n\n\nngx_int_t\nngx_http_v3_read_unbuffered_request_body(ngx_http_request_t *r)\n{\n    ngx_buf_t                   *buf;\n    ngx_int_t                    rc;\n    ngx_connection_t            *fc;\n    ngx_http_v3_stream_t        *stream;\n\n    stream = r->xqstream;\n    fc = r->connection;\n\n    ngx_log_error(NGX_LOG_DEBUG, fc->log, 0,\n                  \"|ngx_http_v3_read_unbuffered_request_body|\");\n\n    if (fc->read->timedout) {\n        stream->skip_data = 1;\n        fc->timedout = 1;\n\n        return NGX_HTTP_REQUEST_TIME_OUT;\n    }\n\n    if (fc->error) {\n        stream->skip_data = 1;\n        return NGX_HTTP_BAD_REQUEST;\n    }\n\n    rc = ngx_http_v3_filter_request_body(r);\n\n    if (rc != NGX_OK) {\n        stream->skip_data = 1;\n        return rc;\n    }\n\n    if (!r->request_body->rest) {\n        return NGX_OK;\n    }\n\n    if (r->request_body->busy != NULL) {\n        return NGX_AGAIN;\n    }\n\n    buf = r->request_body->buf;\n\n    buf->pos = buf->last = buf->start;\n\n    return NGX_AGAIN;\n}\n\n\nngx_int_t\nngx_http_v3_process_request_body(ngx_http_request_t *r, u_char *pos,\n    size_t size, ngx_uint_t last)\n{\n    ngx_buf_t                 *buf;\n    ngx_int_t                  rc;\n    ngx_connection_t          *fc;\n    ngx_http_request_body_t   *rb;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    fc = r->connection;\n    rb = r->request_body;\n    buf = rb->buf;\n\n    ngx_log_error(NGX_LOG_DEBUG, fc->log, 0,\n                  \"|xquic|ngx_http_v3_process_request_body|size:%O, last:%O|\", size, last);\n\n    if (size) {\n        if (buf->sync) {\n            buf->pos = buf->start = pos;\n            buf->last = buf->end = pos + size;\n\n            r->request_body_in_file_only = 1;\n\n        } else {\n            if (size > (size_t) (buf->end - buf->last)) {\n                ngx_log_error(NGX_LOG_INFO, fc->log, 0,\n                              \"|xquic|ngx_http_v3_process_request_body\"\n                              \"|client intended to send body data larger than declared|%O > %O|\",\n                              size, buf->end - buf->last);\n\n                return NGX_HTTP_BAD_REQUEST;\n            }\n\n            buf->last = ngx_cpymem(buf->last, pos, size);\n\n            ngx_log_error(NGX_LOG_DEBUG, fc->log, 0,\n                          \"|xquic|ngx_http_v3_process_request_body|size:%O|\", size);\n        }\n    }\n\n    if (last) {\n        ngx_log_error(NGX_LOG_DEBUG, fc->log, 0,\n                      \"|xquic|ngx_http_v3_process_request_body|last buf|\");\n\n        rb->rest = 0;\n\n        if (fc->read->timer_set) {\n            ngx_del_timer(fc->read);\n        }\n\n        if (r->request_body_no_buffering) {\n            ngx_post_event(fc->read, &ngx_posted_events);\n            ngx_log_error(NGX_LOG_DEBUG, fc->log, 0,\n                          \"|xquic|ngx_http_v3_process_request_body|ngx_post_event|\");\n            return NGX_OK;\n        }\n\n        rc = ngx_http_v3_filter_request_body(r);\n\n        if (rc != NGX_OK) {\n            ngx_log_error(NGX_LOG_DEBUG, fc->log, 0,\n                          \"|xquic|ngx_http_v3_process_request_body\"\n                          \"|ngx_http_v3_filter_request_body error:%O|\", rc);\n            return rc;\n        }\n\n        if (buf->sync) {\n            /* prevent reusing this buffer in the upstream module */\n            rb->buf = NULL;\n        }\n\n        if (r->headers_in.chunked) {\n            r->headers_in.content_length_n = rb->received;\n        }\n\n        r->read_event_handler = ngx_http_block_reading;\n        ngx_log_error(NGX_LOG_DEBUG, fc->log, 0,\n                      \"|xquic|ngx_http_v3_process_request_body|post_handler|\");\n\n        rb->post_handler(r);\n\n        return NGX_OK;\n    }\n\n    if (buf->pos == buf->last) {\n        return NGX_OK;\n    }\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n    ngx_add_timer(fc->read, clcf->client_body_timeout);\n\n    if (r->request_body_no_buffering) {\n        ngx_post_event(fc->read, &ngx_posted_events);\n        ngx_log_error(NGX_LOG_DEBUG, fc->log, 0,\n                      \"|xquic|ngx_http_v3_process_request_body|ngx_post_event|\");\n        return NGX_OK;\n    }\n\n    if (buf->sync) {\n        return ngx_http_v3_filter_request_body(r);\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_v3_filter_request_body(ngx_http_request_t *r)\n{\n    ngx_buf_t                 *b, *buf;\n    ngx_int_t                  rc;\n    ngx_chain_t               *cl;\n    ngx_http_request_body_t   *rb;\n    ngx_connection_t          *fc;\n    ngx_http_core_loc_conf_t  *clcf;\n    ngx_uint_t                 fin;\n//    size_t                     size;\n\n    rb = r->request_body;\n    fc = r->connection;\n    buf = rb->buf;\n    fin = rb->rest == 0 ? 1 : 0;\n\n    ngx_log_error(NGX_LOG_DEBUG, fc->log, 0,\n                  \"|xquic|ngx_http_v3_filter_request_body|size:%O, fin:%O|\", buf->last - buf->pos, fin);\n\n    if (buf->pos == buf->last && rb->rest) {\n        cl = NULL;\n        goto update;\n    }\n\n    cl = ngx_chain_get_free_buf(r->pool, &rb->free);\n    if (cl == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    b = cl->buf;\n\n    ngx_memzero(b, sizeof(ngx_buf_t));\n\n    if (buf->pos != buf->last) {\n        r->request_length += buf->last - buf->pos;\n        rb->received += buf->last - buf->pos;\n\n        if (r->headers_in.content_length_n != -1) {\n            if (rb->received > r->headers_in.content_length_n) {\n                ngx_log_error(NGX_LOG_INFO, fc->log, 0,\n                              \"|xquic|ngx_http_v3_filter_request_body\"\n                              \"|client intended to send body data larger than declared|:%O > %O|\",\n                              rb->received, r->headers_in.content_length_n);\n\n                return NGX_HTTP_BAD_REQUEST;\n            }\n\n        } else {\n            clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n            if (clcf->client_max_body_size\n                && rb->received > clcf->client_max_body_size)\n            {\n                ngx_log_error(NGX_LOG_ERR, fc->log, 0,\n                              \"|xquic|ngx_http_v3_filter_request_body\"\n                              \"|client intended to send too large chunked body:%O > %O|\",\n                              rb->received, clcf->client_max_body_size);\n\n                return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE;\n            }\n        }\n\n        b->temporary = 1;\n        b->pos = buf->pos;\n        b->last = buf->last;\n        b->start = b->pos;\n        b->end = b->last;\n    }\n\n    buf->pos = buf->last = buf->start;\n    ngx_log_error(NGX_LOG_DEBUG, fc->log, 0,\n                  \"|xquic|ngx_http_v3_filter_request_body|received:%O|\", rb->received);\n\n    if (!rb->rest) {\n        if (r->headers_in.content_length_n != -1\n            && r->headers_in.content_length_n != rb->received)\n        {\n            ngx_log_error(NGX_LOG_INFO, fc->log, 0,\n                          \"|xquic|ngx_http_v3_filter_request_body\"\n                          \"|client prematurely closed stream:only %O out of %O bytes of request body received|\",\n                          rb->received, r->headers_in.content_length_n);\n\n            return NGX_HTTP_BAD_REQUEST;\n        }\n\n        b->last_buf = 1;\n    }\n\n    b->tag = (ngx_buf_tag_t) &ngx_http_v3_filter_request_body;\n    b->flush = r->request_body_no_buffering;\n\nupdate:\n\n    rc = ngx_http_top_request_body_filter(r, cl);\n\n    ngx_chain_update_chains(r->pool, &rb->free, &rb->busy, &cl,\n                            (ngx_buf_tag_t) &ngx_http_v3_filter_request_body);\n\n    return rc;\n}\n\n\nvoid\nngx_http_v3_read_client_request_body_handler(ngx_http_request_t *r)\n{\n    ngx_connection_t  *fc;\n\n    fc = r->connection;\n\n    ngx_log_error(NGX_LOG_DEBUG, fc->log, 0,\n                  \"|xquic|ngx_http_v3_read_client_request_body_handler|\");\n\n    if (fc->read->timedout) {\n        ngx_log_error(NGX_LOG_INFO, fc->log, NGX_ETIMEDOUT,\n                      \"|xquic|ngx_http_v3_read_client_request_body_handler|client timed out|\");\n\n        fc->timedout = 1;\n        r->xqstream->skip_data = 1;\n\n        ngx_http_finalize_request(r, NGX_HTTP_REQUEST_TIME_OUT);\n        return;\n    }\n\n    if (fc->error) {\n        ngx_log_error(NGX_LOG_INFO, fc->log, 0,\n                      \"|xquic|ngx_http_v3_read_client_request_body_handler|client prematurely closed stream|\");\n\n        r->xqstream->skip_data = 1;\n\n        ngx_http_finalize_request(r, NGX_HTTP_CLIENT_CLOSED_REQUEST);\n        return;\n    }\n}\n\n\nstatic ngx_int_t\nngx_http_xquic_connect(ngx_http_xquic_connection_t *qc, ngx_connection_t *lc)\n{\n    int                              value;\n    u_char                           text[NGX_SOCKADDR_STRLEN];\n    ngx_str_t                        addr;\n    ngx_log_t                       *log;\n    ngx_event_t                     *rev, *wev;\n    ngx_socket_t                     s;\n    ngx_listening_t                 *ls;\n    ngx_connection_t                *c;\n    ngx_http_xquic_main_conf_t      *qmcf;\n\n    ls = lc->listening;\n\n    switch(qc->local_sockaddr->sa_family) {\n#if (NGX_HAVE_INET6)\n    case AF_INET6:\n        s = ngx_socket(AF_INET6, SOCK_DGRAM, 0);\n        break;\n#endif\n    default: /* AF_INET */\n        s = ngx_socket(AF_INET, SOCK_DGRAM, 0);\n        break;\n    }\n\n    if (s == (ngx_socket_t) -1) {\n        ngx_log_error(NGX_LOG_EMERG, lc->log, ngx_socket_errno,\n                      \"xquic\" ngx_socket_n \" %V failed\",\n                      &ls->addr_text);\n        return NGX_ERROR;\n    }\n\n    c = ngx_get_connection(s, lc->log);\n\n    if (c == NULL) {\n        if (ngx_close_socket(s) == -1) {\n            ngx_log_error(NGX_LOG_EMERG, lc->log, ngx_socket_errno,\n                          \"quic\" ngx_close_socket_n \" failed\");\n        }\n\n        return NGX_ERROR;\n    }\n\n    value = 1;\n    if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,\n                   (const void *) &value, sizeof(int))\n        == -1)\n    {\n        ngx_log_error(NGX_LOG_EMERG, lc->log, ngx_socket_errno,\n                      \"|xquic| setsockopt(SO_REUSEADDR) %V failed|\",\n                       &ls->addr_text);\n\n        goto failed;\n    }\n\n    qmcf = ngx_http_cycle_get_module_main_conf(ngx_cycle, ngx_http_xquic_module);\n\n    if (qmcf->new_udp_hash == 1 &&\n        setsockopt(s, SOL_UDP, 200,\n                   (const void *) &value, sizeof(int))\n        == -1)\n    {\n        ngx_log_error(NGX_LOG_EMERG, lc->log, ngx_socket_errno,\n                      \"|xquic| setsockopt(new-udp-hash) %V failed|\",\n                      &ls->addr_text);\n    }\n\n    int socket_sndbuf = qmcf->socket_sndbuf;\n    if (setsockopt(s, SOL_SOCKET, SO_SNDBUF,\n                   (const void *) &(socket_sndbuf), sizeof(int))\n        == -1)\n    {\n        ngx_log_error(NGX_LOG_EMERG, lc->log, ngx_socket_errno,\n                      \"|xquic| setsockopt(SO_SNDBUF, %d) %V failed, ignored|\",\n                      qmcf->socket_sndbuf, &ls->addr_text);\n    }    \n\n    int socket_rcvbuf = qmcf->socket_rcvbuf;\n    if (setsockopt(s, SOL_SOCKET, SO_RCVBUF,\n                   (const void *) &(socket_rcvbuf), sizeof(int))\n        == -1)\n    {\n        ngx_log_error(NGX_LOG_EMERG, lc->log, ngx_socket_errno,\n                      \"|xquic| setsockopt(SO_RCVBUF, %d) %V failed, ignored|\",\n                      qmcf->socket_rcvbuf, &ls->addr_text);\n    }\n\n    if (ngx_nonblocking(s) == -1) {\n        ngx_log_error(NGX_LOG_EMERG, lc->log, ngx_socket_errno,\n                      \"xquic\" ngx_nonblocking_n \" failed\");\n\n        goto failed;\n    }\n\n    if (bind(s, qc->local_sockaddr, qc->local_socklen) == -1) {\n        addr.data = text;\n        addr.len = ngx_sock_ntop(qc->local_sockaddr, qc->local_socklen,\n                                 text, NGX_SOCKADDR_STRLEN, 1);\n        ngx_log_error(NGX_LOG_EMERG, lc->log, ngx_socket_errno,\n                      \"|xquic|bind() to %V failed|\", &addr);\n\n        goto failed;\n    }\n\n    if (connect(s, qc->peer_sockaddr, qc->peer_socklen) == -1) {\n        addr.data = text;\n        addr.len = ngx_sock_ntop(qc->peer_sockaddr, qc->peer_socklen, text,\n                                 NGX_SOCKADDR_STRLEN, 1);\n\n        ngx_log_error(NGX_LOG_EMERG, lc->log, ngx_socket_errno,\n                      \"|xquic|connect() to %V failed|\", &addr);\n\n        goto failed;\n    }\n\n    log = ngx_palloc(qc->pool, sizeof(ngx_log_t));\n    if (log == NULL) {\n        goto failed;\n    }\n\n    *log = ls->log;\n\n    log->data = NULL;\n    log->handler = NULL;\n\n    c->log = log;\n    c->type = SOCK_DGRAM;\n\n    c->listening = ls;\n    c->pool = qc->pool;\n    c->sockaddr = qc->peer_sockaddr;\n    c->socklen = qc->peer_socklen;\n    c->local_sockaddr = qc->local_sockaddr;\n    c->local_socklen = qc->local_socklen;\n    c->addr_text = qc->addr_text;\n    c->ssl = NULL;\n#if (NGX_SLIGHT_SSL)\n    c->s_ssl = NULL;\n#endif\n\n    rev = c->read;\n    wev = c->write;\n\n    wev->ready = 1;\n\n    rev->log = c->log;\n    wev->log = c->log;\n\n    c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1);\n\n#if (NGX_DEBUG)\n    {\n        if (lc->log->log_level & NGX_LOG_DEBUG_HTTP) {\n            addr.data = text;\n            addr.len = ngx_sock_ntop(qc->peer_sockaddr, qc->peer_socklen, text,\n                                     NGX_SOCKADDR_STRLEN, 1);\n            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, lc->log, 0,\n                           \"|xquic|create connect fd %d to %V|\", s, &addr);\n        }\n    }\n#endif\n\n    qc->connection = c;\n\n    return NGX_OK;\n\nfailed:\n    ngx_close_connection(c);\n    return NGX_ERROR;\n}\n\n\nstatic u_char *\nngx_http_xquic_log_error(ngx_log_t *log, u_char *buf, size_t len)\n{\n    u_char              *p;\n    ngx_http_request_t  *r;\n    ngx_http_log_ctx_t  *ctx;\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    ctx = log->data;\n\n    p = ngx_snprintf(buf, len, \", xquic connection, client: %V\", &ctx->connection->addr_text);\n    len -= p - buf;\n\n    r = ctx->request;\n\n    if (r) {\n        return r->log_handler(r, ctx->current_request, p, len);\n\n    } else {\n        p = ngx_snprintf(p, len, \", server: %V\",\n                         &ctx->connection->listening->addr_text);\n    }\n\n    return p;\n}\n\n\nvoid\nngx_http_xquic_session_process_packet(ngx_http_xquic_connection_t *qc, \n    ngx_xquic_recv_packet_t *packet, size_t recv_size)\n{\n    uint64_t recv_time = ngx_xquic_get_time();\n    ngx_log_error(NGX_LOG_DEBUG, qc->connection->log, 0,\n                    \"|xquic|xqc_server_read_handler recv_size=%zd, recv_time=%llu, recv_total=%d|\", \n                    recv_size, recv_time, ++qc->recv_packets_num);\n\n    if (xqc_engine_packet_process(qc->engine, (u_char *)packet->buf, recv_size,\n                                  qc->local_sockaddr, qc->local_socklen,\n                                  qc->peer_sockaddr, qc->peer_socklen,\n                                  (xqc_msec_t) recv_time, NULL) != 0) \n    {\n        ngx_log_error(NGX_LOG_DEBUG, qc->connection->log, 0,\n                    \"|xquic|xqc_server_read_handler: packet process err|\");\n        return;\n    }\n}\n\n\n/**\n * used to recv udp packets\n */\nstatic void\nngx_http_xquic_read_handler(ngx_event_t *rev)\n{\n    ssize_t                        n;\n    ngx_connection_t              *c, *lc;\n    ngx_xquic_recv_packet_t        packet;\n    ngx_http_xquic_connection_t   *qc;\n\n    c = rev->data;\n    qc = c->data;\n\n    if (rev->timedout) {\n        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, \"|xquic|client timed out|\");\n        return;\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"|xquic|connection read handler|\");\n\n    do {\n        n = ngx_xquic_recv(c, packet.buf, sizeof(packet.buf));\n\n        if (n == NGX_AGAIN) {\n            break;\n        } else if (n == 0) {\n            ngx_log_error(NGX_LOG_WARN, c->log, 0,\n                          \"|xquic|ngx_xquic_recv 0|\");\n            break;\n        } else if (n < 0) {\n            ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,\n                        \"|xquic|recv = %z|\", n);\n \n\n            if (n == NGX_DONE && qc->processing == 0) {\n                ngx_http_v3_connection_error(qc, NGX_XQUIC_CONN_NO_ERR, \"client request done\");\n            } else {\n                ngx_http_v3_connection_error(qc, NGX_XQUIC_CONN_RECV_ERR, \"read packet error\");\n            }\n\n            goto finish_recv;\n        }\n\n        packet.len = n;\n\n        /* check QUIC magic bit */\n        if (!NGX_XQUIC_CHECK_MAGIC_BIT(packet.buf)) {\n            ngx_log_debug(NGX_LOG_WARN, c->log, 0,\n                          \"|xquic|invalid packet head|\");\n            continue;\n        }\n\n        /* get dcid here */\n        ngx_xquic_packet_get_cid(&packet, qc->engine);\n\n        //ngx_xquic_server_session_process_packet(qc, &packet, n);\n\n        if (xqc_cid_is_equal(&(qc->dcid), &(packet.xquic.dcid)) != NGX_OK) {\n            ngx_log_error(NGX_LOG_NOTICE, c->log, 0, \"|xquic|ngx_http_xquic_read_handler: \"\n                          \"packet connectionID %s != %s (qc connection ID), processing: %d|\",\n                          xqc_dcid_str(&packet.xquic.dcid), xqc_scid_str(&qc->dcid), qc->processing);\n\n            if (qc->processing == 0) {\n                lc = c->listening->connection;\n\n                ngx_memcpy(&packet.sockaddr, qc->peer_sockaddr, qc->peer_socklen);\n                packet.socklen = qc->peer_socklen;\n                ngx_memcpy(&packet.local_sockaddr, qc->local_sockaddr, qc->local_socklen);\n                packet.local_socklen = qc->local_socklen;\n\n                /* ngx_http_v3_finalize_connection(qc, NGX_XQUIC_CONN_HANDSHAKE_ERR); */\n                ngx_xquic_dispatcher_process_packet(lc, &packet);\n\n                return;\n            }\n\t\t\t//from the same source address\n\t\t\t//if not client retry handshake, just drop the packets with different cid\n        } else {\n            ngx_http_xquic_session_process_packet(qc, &packet, n);\n        }\n\n        if (qc->xquic_off) {\n            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"xquic not allow\");\n        \n            ngx_http_v3_connection_error(qc, NGX_XQUIC_CONN_NO_ERR, \"xquic not allow\");\n        \n            return;\n        }\n\n    } while (rev->ready);\n\nfinish_recv:\n    xqc_engine_finish_recv(qc->engine);\n}\n\n\n/**\n * connection readmsg_handler used to recv udp packets\n */\nstatic void\nngx_http_xquic_readmsg_handler(ngx_event_t *rev)\n{\n    ngx_int_t                       rc;\n    ngx_connection_t               *c, *lc;\n    ngx_http_xquic_connection_t    *qc;\n    static ngx_xquic_recv_packet_t  packet;\n    ngx_http_xquic_main_conf_t     *qmcf;\n\n\n    c = rev->data;\n    qc = c->data;\n    lc = c->listening->connection;\n\n    qmcf = ngx_http_cycle_get_module_main_conf(ngx_cycle, ngx_http_xquic_module);\n\n    if (rev->timedout) {\n        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, \"|xquic| client readmsg timed out|\");\n        return;\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"|xquic| connection readmsg handler|\");\n\n    for ( ;; ) {\n        packet.local_socklen = qc->local_socklen;\n        ngx_memcpy(&packet.local_sockaddr, qc->local_sockaddr, qc->local_socklen);\n\n        rc = ngx_xquic_recv_packet(c, &packet, rev->log, qmcf->xquic_engine);\n\n        if (rc == NGX_AGAIN) {\n            break;\n        } else if (rc < 0) {\n\n\n            if (rc == NGX_DONE && qc->processing == 0) {\n                ngx_http_v3_connection_error(qc, NGX_XQUIC_CONN_NO_ERR, \"client request done\");\n            } else {\n                ngx_http_v3_connection_error(qc, NGX_XQUIC_CONN_RECV_ERR, \"read packet error\");\n            }\n\n            goto finish_recv;\n        }\n\n        ngx_xquic_dispatcher_process_packet(lc, &packet);\n\n        if (qc->xquic_off) {\n            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"xquic not allow\");\n        \n            ngx_http_v3_connection_error(qc, NGX_XQUIC_CONN_NO_ERR, \"xquic not allow\");\n        \n            return;\n        }\n    }\n\n    rev->ready = 0;\n    rev->handler = ngx_http_xquic_read_handler;\n\nfinish_recv:\n    xqc_engine_finish_recv(qmcf->xquic_engine);\n}\n\n\n/**\n * init http v3 connection\n */\nstatic ngx_int_t\nngx_http_xquic_init_connection(ngx_http_xquic_connection_t *qc)\n{\n    ngx_uint_t                   i;\n    ngx_http_port_t             *port;\n    ngx_connection_t            *c;\n    ngx_http_log_ctx_t          *ctx;\n    struct sockaddr_in          *sin;\n    ngx_http_in_addr_t          *addr;\n#if (NGX_HAVE_INET6)\n    ngx_http_in6_addr_t         *addr6;\n    struct sockaddr_in6         *sin6;\n#endif\n    ngx_http_connection_t       *hc;\n\n\n    hc = ngx_pcalloc(qc->pool, sizeof(ngx_http_connection_t));\n    if (hc == NULL) {\n        return NGX_ERROR;\n    }\n\n    qc->http_connection = hc;\n\n    /* find the server configuration for the address:port */\n\n    c = qc->connection;\n    port = c->listening->servers;\n\n    if (port->naddrs > 1) {\n\n        switch(qc->local_sockaddr->sa_family) {\n#if (NGX_HAVE_INET6)\n        case AF_INET6:\n            sin6 = (struct sockaddr_in6 *) qc->local_sockaddr;\n\n            addr6 = port->addrs;\n\n            /* the last address is \"*\" */\n\n            for (i = 0; i < port->naddrs - 1; i++) {\n                if (ngx_memcmp(&addr6[i].addr6, &sin6->sin6_addr, 16) == 0) {\n                    break;\n                }\n            }\n\n            hc->addr_conf = &addr6[i].conf;\n\n            break;\n#endif\n        default: /* AF_INET */\n            sin = (struct sockaddr_in *) qc->local_sockaddr;\n\n            addr = port->addrs;\n\n            /* the last address is \"*\" */\n\n            for (i = 0; i < port->naddrs - 1; i++) {\n                if (addr[i].addr == sin->sin_addr.s_addr) {\n                    break;\n                }\n            }\n\n            hc->addr_conf = &addr[i].conf;\n            break;\n        }\n\n    } else {\n        switch(qc->local_sockaddr->sa_family) {\n#if (NGX_HAVE_INET6)\n        case AF_INET6:\n            addr6 = port->addrs;\n            hc->addr_conf = &addr6[0].conf;\n            break;\n#endif\n        default: /* AF_INET */\n            addr = port->addrs;\n            hc->addr_conf = &addr[0].conf;\n            break;\n        }\n    }\n\n    /* the default server configuration for the address:port */\n    hc->conf_ctx = hc->addr_conf->default_server->ctx;\n\n    ctx = ngx_palloc(qc->pool, sizeof(ngx_http_log_ctx_t));\n    if (ctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    ctx->connection = c;\n    ctx->request = NULL;\n    ctx->current_request = NULL;\n\n    c->log->connection = c->number;\n    c->log->handler = ngx_http_xquic_log_error;\n    c->log->data = ctx;\n    c->log->action = \"xquic waiting for request\";\n\n    c->log_error = NGX_ERROR_INFO;\n\n    c->data = qc;\n    c->read->handler = ngx_http_xquic_readmsg_handler;\n    c->write->handler = ngx_http_xquic_write_handler;\n\n    if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\n/**\n * create http v3 connection\n */\nngx_http_xquic_connection_t *\nngx_http_v3_create_connection(ngx_connection_t *lc, const xqc_cid_t *connection_id,\n                                struct sockaddr *local_sockaddr, socklen_t local_socklen,\n                                struct sockaddr *peer_sockaddr, socklen_t peer_socklen,\n                                xqc_engine_t *engine)\n{\n    ngx_int_t                    rc;\n    ngx_pool_t                  *pool;\n    ngx_listening_t             *ls;\n    ngx_http_xquic_connection_t *qc;\n    ngx_http_xquic_main_conf_t  *qmcf = ngx_http_cycle_get_module_main_conf(ngx_cycle, \n                                                                    ngx_http_xquic_module);\n\n\n    pool = ngx_create_pool(lc->listening->pool_size, lc->log);\n    if (pool == NULL) {\n        return NULL;\n    }\n\n    qc = ngx_pcalloc(pool, sizeof(ngx_http_xquic_connection_t));\n    if (qc == NULL) {\n        ngx_destroy_pool(pool);\n        return NULL;\n    }\n\n    qc->pool = pool;\n    qc->dcid = *connection_id;\n    qc->engine = engine;\n\n    qc->start_msec = ngx_current_msec;\n    qc->fb_time = (ngx_msec_t) -1;\n    qc->handshake_time = (ngx_msec_t) -1;\n\n    /* init stream_index */\n    qc->streams_index = ngx_pcalloc(qc->pool, ngx_http_xquic_index_size(qmcf)\n                                              * sizeof(ngx_xquic_list_node_t *));\n    if (qc->streams_index == NULL) {\n        ngx_destroy_pool(pool);\n        return NULL;\n    }\n\n    qc->peer_sockaddr = ngx_palloc(pool, peer_socklen);\n    if (qc->peer_sockaddr == NULL) {\n        ngx_destroy_pool(pool);\n        return NULL;\n    }\n\n    qc->local_sockaddr = ngx_palloc(pool, local_socklen);\n    if (qc->local_sockaddr == NULL) {\n        ngx_destroy_pool(pool);\n        return NULL;\n    }\n\n    ngx_memcpy(qc->peer_sockaddr, peer_sockaddr, peer_socklen);\n    ngx_memcpy(qc->local_sockaddr, local_sockaddr, local_socklen);\n\n    qc->peer_socklen = peer_socklen;\n    qc->local_socklen = local_socklen;\n\n    ls = lc->listening;\n    qc->addr_text.data = ngx_pnalloc(pool, ls->addr_text_max_len);\n    if (qc->addr_text.data == NULL) {\n        ngx_destroy_pool(pool);\n        return NULL;\n    }\n    qc->addr_text.len = ngx_sock_ntop(qc->peer_sockaddr, qc->peer_socklen,\n                                      qc->addr_text.data,\n                                      ls->addr_text_max_len, 0);\n    if (qc->addr_text.len == 0) {\n        ngx_destroy_pool(pool);\n        return NULL;\n    }\n\n    rc = ngx_http_xquic_connect(qc, lc);\n    if (rc == NGX_ERROR) {\n        ngx_log_error(NGX_LOG_ERR, lc->log, 0, \"|xquic|quic connect failed|\");\n        ngx_destroy_pool(pool);\n        return NULL;\n    }\n\n    if(ngx_http_xquic_init_connection(qc) != NGX_OK) {\n        ngx_close_connection(qc->connection);\n        ngx_destroy_pool(pool);\n        return NULL;\n    }\n\n#if (NGX_STAT_STUB)\n    (void) ngx_atomic_fetch_add(ngx_stat_active, 1);\n#endif\n\n#if (T_NGX_HAVE_XUDP)\n    /* enable by default */\n    ngx_xudp_enable_tx(qc->connection);\n#endif\n\n    //ngx_quic_monitor_register(qc);\n\n    return qc;\n}\n\n\n/**\n * used in xqc engine h3 conn close callback\n * to free h3 connection\n */\nvoid\nngx_http_v3_finalize_connection(ngx_http_xquic_connection_t *h3c,\n    ngx_uint_t status)\n{\n    ngx_uint_t                       i, size;\n    ngx_event_t                     *ev;\n    ngx_connection_t                *c, *fc;\n    ngx_http_request_t              *r;\n    ngx_http_v3_stream_t            *stream = NULL;\n    ngx_xquic_list_node_t           *node = NULL;\n    ngx_http_xquic_main_conf_t      *qmcf = ngx_http_cycle_get_module_main_conf(ngx_cycle, ngx_http_xquic_module);\n\n\n    c = h3c->connection;\n\n    h3c->blocked = 1;\n    h3c->closing = 1;\n    h3c->wait_to_close = 0;\n\n    c->error = 1;\n\n    if (!h3c->processing) {\n        ngx_http_close_connection(c);\n        return;\n    }\n\n    c->read->handler = ngx_http_empty_handler;\n    c->write->handler = ngx_http_empty_handler;\n\n\n    /* check all the streams */\n    size = ngx_http_xquic_index_size(qmcf);\n\n    for (i = 0; i < size; i++) {\n\n        if (h3c->streams_index[i] == NULL) {\n            continue;\n        }\n\n        /* may delete stream in the loop, will not delete node */\n        for (node = h3c->streams_index[i]; node != NULL; node = node->next) {\n\n            stream = node->entry;\n\n            if (stream == NULL || stream->request_closed) {\n                continue;\n            }\n\n            ngx_log_error(NGX_LOG_WARN, ngx_cycle->log, 0, \n                    \"|xquic|find unclosed stream while finalizing request|stream_id=%i|\", stream->id);\n\n            stream->handled = 0;\n\n            r = stream->request;\n\n            /* stream->request may be closed before engine close h3 stream */\n            if (r == NULL) {\n                continue;\n            }\n            \n            fc = r->connection;\n\n            fc->error = 1;\n\n            if (stream->queued) {\n                stream->queued = 0;\n\n                ev = fc->write;\n                ev->delayed = 0;\n\n            } else {\n                ev = fc->read;\n            }\n\n            ev->eof = 1;\n            ev->handler(ev);\n\n            /* ev->handler may call ngx_http_v3_close_stream.\n             * struct stream will memset to zero and stream->closed will set to 1 in ngx_http_v3_close_stream */\n            if (r == stream->request && !stream->closed) {\n                ngx_http_v3_close_stream(stream, 0);\n            }\n        }\n    }\n\n    h3c->blocked = 0;\n\n    if (h3c->processing) {\n        h3c->wait_to_close = 1;\n        return;\n    }\n\n    ngx_http_close_connection(c);\n}\n\n\n/**\n * call xqc_h3_conn_close, and free connection in h3_conn_close_notify\n */\nvoid\nngx_http_v3_connection_error(ngx_http_xquic_connection_t *qc, \n    ngx_uint_t err, const char *err_details)\n{\n    ngx_event_t                 *ev;\n    ngx_http_xquic_main_conf_t  *qmcf = ngx_http_cycle_get_module_main_conf(ngx_cycle, ngx_http_xquic_module);\n\n    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, qc->connection->log, 0,\n                  \"|xquic|ngx_xquic_server_session_close: close connection_id: %ul|err=%i|%s|\",\n                  qc->connection_id, err, err_details);\n\n    ev = qc->connection->read;\n\n    ev->handler = ngx_http_empty_handler;\n\n    /* xquic close connection here */\n    ngx_int_t ret = xqc_h3_conn_close(qmcf->xquic_engine, &(qc->dcid));\n    if (ret != NGX_OK) {\n        ngx_log_error(NGX_LOG_WARN, qc->connection->log, 0,\n                      \"|xquic|xqc_h3_conn_close err|connection_id: %ul|err=%i|%s|\",\n                      qc->connection_id, ret, err_details);\n    }\n}\n\n\n"
  },
  {
    "path": "modules/ngx_http_xquic_module/ngx_http_xquic.h",
    "content": "/*\n * Copyright (C) 2020-2023 Alibaba Group Holding Limited\n */\n\n#ifndef _NGX_HTTP_XQUIC_H_INCLUDED_\n#define _NGX_HTTP_XQUIC_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n#include <ngx_http.h>\n#include <ngx_xquic.h>\n\n#include <xquic/xquic_typedef.h>\n#include <xquic/xquic.h>\n#include <xquic/xqc_http3.h>\n\n\n\ntypedef struct ngx_http_xquic_connection_s ngx_http_xquic_connection_t;\ntypedef struct ngx_xquic_list_node_s   ngx_xquic_list_node_t;\n\n\n#define NGX_XQUIC_CONN_NO_ERR           0\n#define NGX_XQUIC_CONN_RECV_ERR         1\n#define NGX_XQUIC_CONN_WRITE_ERR        2\n#define NGX_XQUIC_CONN_INTERNAL_ERR     3\n#define NGX_XQUIC_CONN_HANDSHAKE_ERR    4\n\n\n\n\nstruct ngx_http_xquic_connection_s {\n    ngx_connection_t               *connection;\n    ngx_http_connection_t          *http_connection;\n\n    ngx_ssl_conn_t                 *ssl_conn;\n\n    ngx_connection_t               *free_fake_connections;\n\n    ngx_http_v3_stream_t           *free_streams;\n\n    ngx_xquic_list_node_t         **streams_index;\n\n    uint64_t                        connection_id;\n    xqc_cid_t                       dcid;\n\n    ngx_uint_t                      processing;\n\n    ngx_uint_t                      streams_cnt;\n\n    uint64_t                        recv_packets_num;\n\n    ngx_pool_t                     *pool;\n\n    struct sockaddr                *peer_sockaddr;\n    socklen_t                       peer_socklen;\n\n    struct sockaddr                *local_sockaddr;\n    socklen_t                       local_socklen;\n\n    ngx_str_t                       addr_text;\n\n    ngx_str_t                      *sni;\n\n    ngx_msec_t                      start_msec;\n    ngx_msec_t                      fb_time;\n    ngx_msec_t                      handshake_time;\n\n    uint64_t                        stream_cnt;\n    uint64_t                        stream_body_sent;\n    uint64_t                        stream_req_time;\n\n    unsigned                        xquic_off:1;\n    unsigned                        closing:1;\n    unsigned                        logged:1;\n    unsigned                        krej_sent:1;\n    unsigned                        kshlo_sent:1;\n    unsigned                        blocked:1;\n    unsigned                        destroyed:1;\n    unsigned                        wait_to_close:1;\n\n\n    void                           *stats_ctx;\n\n    xqc_engine_t                   *engine;\n};\n\n\ntypedef struct {\n    ngx_str_t                       name;\n    ngx_str_t                       value;\n} ngx_http_v3_header_t;\n\n\nstruct ngx_xquic_list_node_s {\n    ngx_xquic_list_node_t   *next;\n    void                    *entry;\n};\n\nstruct ngx_http_v3_stream_s {\n    ngx_http_request_t           *request;\n    ngx_http_xquic_connection_t  *connection;\n\n    ngx_msec_t                    start_msec;\n    ngx_msec_t                    req_time;\n\n    uint64_t                      body_sent;\n    ngx_uint_t                    queued;\n\n    ngx_buf_t                    *body_buffer;\n\n    uint32_t                      id;\n\n    unsigned                      skip_data:2;\n    unsigned                      closed:1;\n    unsigned                      header_recvd:1;\n    unsigned                      in_closed:1;\n    unsigned                      out_closed:1;\n    unsigned                      engine_inner_closed:1;\n    unsigned                      request_closed:1;\n    unsigned                      run_request:1;\n    unsigned                      handled:1;\n    unsigned                      wait_to_write:1;\n    unsigned                      request_freed:1;\n\n    void                         *xquic_stream;\n    size_t                        send_offset;\n\n    ngx_chain_t                  *output_queue;\n    ngx_chain_t                 **last_chain;\n    ngx_chain_t                  *free_bufs;\n\n    void                         *h3_request;\n    xqc_http_headers_t            resp_headers;\n\n    ngx_array_t                  *cookies;\n\n    size_t                        send_body_max;\n    size_t                        send_body_len;\n    u_char                       *send_body;\n\n    ngx_int_t                     cancel_status;\n\n    void                         *next;\n    ngx_xquic_list_node_t        *list_node;\n};\n\n\nngx_http_xquic_connection_t * ngx_http_v3_create_connection(ngx_connection_t *lc, const xqc_cid_t *connection_id,\n                                struct sockaddr *local_sockaddr, socklen_t local_socklen,\n                                struct sockaddr *peer_sockaddr, socklen_t peer_socklen,\n                                xqc_engine_t *engine);\n\nvoid ngx_http_v3_finalize_connection(ngx_http_xquic_connection_t *h3c,\n    ngx_uint_t status);\n\nvoid ngx_http_v3_connection_error(ngx_http_xquic_connection_t *qc, \n    ngx_uint_t err, const char *err_details);\n\n\nngx_chain_t *ngx_http_xquic_send_chain(ngx_connection_t *fc, ngx_chain_t *in, off_t limit);\n\nngx_int_t ngx_http_v3_read_request_body(ngx_http_request_t *r);\n\nngx_int_t ngx_http_v3_read_unbuffered_request_body(ngx_http_request_t *r);\n\nngx_int_t ngx_http_v3_process_request_body(ngx_http_request_t *r, u_char *pos,\n    size_t size, ngx_uint_t last);\n\nngx_int_t ngx_http_v3_filter_request_body(ngx_http_request_t *r);\n\nvoid ngx_http_v3_read_client_request_body_handler(ngx_http_request_t *r);\n\n\nxqc_int_t ngx_http_v3_cert_cb(const char *sni, void **chain,\n    void **cert, void **key, void *conn_user_data);\n\n\n#endif /* _NGX_HTTP_XQUIC_H_INCLUDED_ */\n\n"
  },
  {
    "path": "modules/ngx_http_xquic_module/ngx_http_xquic_filter_module.c",
    "content": "/*\n * Copyright (C) 2020-2023 Alibaba Group Holding Limited\n */\n\n#include <ngx_http_xquic.h>\n#include <ngx_http_v3_stream.h>\n#include <ngx_http_xquic_module.h>\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n#include <nginx.h>\n\n#include <xquic/xqc_errno.h>\n\n\n#define NGX_HTTP_XQUIC_NAME_SERVER           \"server\"\n#define NGX_HTTP_XQUIC_NAME_DATE             \"date\"\n#define NGX_HTTP_XQUIC_NAME_CONTENT_TYPE     \"content-type\"\n#define NGX_HTTP_XQUIC_NAME_CONTENT_LENGTH   \"content-length\"\n#define NGX_HTTP_XQUIC_NAME_LAST_MODIFIED    \"last-modified\"\n#define NGX_HTTP_XQUIC_NAME_LOCATION         \"location\"\n#define NGX_HTTP_XQUIC_NAME_VARY             \"vary\"\n\n#define NGX_XQUIC_HEADERS_INIT_CAPACITY      64\n#define NGX_XQUIC_TMP_BUF_SIZE               1024\n\n\n\nstatic ngx_http_output_header_filter_pt  ngx_http_next_header_filter;\n\nstatic ngx_int_t ngx_http_xquic_filter_init(ngx_conf_t *cf);\n\n\nstatic ngx_http_module_t  ngx_http_xquic_filter_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    ngx_http_xquic_filter_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\nngx_module_t  ngx_http_xquic_filter_module = {\n    NGX_MODULE_V1,\n    &ngx_http_xquic_filter_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\nssize_t\nngx_http_xquic_stream_send_header(ngx_http_v3_stream_t *qstream)\n{\n    ssize_t ret = 0;\n    uint8_t header_only = (qstream->request->header_only == 1);\n\n    ret = xqc_h3_request_send_headers(qstream->h3_request,\n                        &(qstream->resp_headers), header_only);\n    if (ret < 0) {\n        ngx_log_error(NGX_LOG_WARN, ngx_cycle->log, 0,\n                    \"|xquic|xqc_h3_request_send_headers error|ret=%z|\", ret);\n        qstream->queued--;\n        return NGX_ERROR;\n    } else {\n        ngx_log_error(NGX_LOG_DEBUG, ngx_cycle->log, 0,\n                    \"|xquic|xqc_h3_request_send_headers success size=%z|\", ret);\n        qstream->queued--;\n    }\n\n    if (header_only) {\n        return ret;\n    }\n\n    return ret;\n}\n\n\nssize_t\nngx_http_xquic_stream_send_body(ngx_http_v3_stream_t *qstream,\n    u_char *buf, size_t size, int fin)\n{\n    ssize_t ret = xqc_h3_request_send_body(qstream->h3_request,\n                                           buf, size, fin);\n\n    if (ret == -XQC_EAGAIN) {\n\n        /* inner buf full, congestion control, flow control */\n        ngx_log_error(NGX_LOG_DEBUG, ngx_cycle->log, 0,\n                    \"|xquic|xqc_h3_request_send_body EAGAIN|\");\n        return NGX_AGAIN;\n\n    } else if (ret < 0) {\n        ngx_log_error(NGX_LOG_DEBUG, ngx_cycle->log, 0,\n                    \"|xquic|xqc_h3_request_send_body error %z|\", ret);\n        return NGX_ERROR;\n    }\n\n    qstream->body_sent += ret;\n\n    ngx_log_error(NGX_LOG_DEBUG, ngx_cycle->log, 0,\n                    \"|xquic|xqc_h3_request_send_body offset=%z|%i|\", qstream->body_sent, fin);\n\n    return ret;\n}\n\n\n\nngx_int_t\nngx_http_xquic_headers_initial(xqc_http_headers_t *headers)\n{\n    headers->headers = NULL;\n    headers->count = 0;\n    headers->capacity = 0;\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_xquic_headers_realloc_buf(ngx_http_request_t *r,\n    xqc_http_headers_t *headers, size_t capacity)\n{\n\n    if(headers->count > capacity){\n        return NGX_ERROR;\n    }\n    xqc_http_header_t * old = headers->headers;\n\n    headers->headers = ngx_pcalloc(r->pool, sizeof(xqc_http_header_t) * capacity);\n\n    if(headers->headers == NULL){\n        ngx_pfree(r->pool, old);\n        headers->count = 0;\n        headers->capacity = 0;\n        return NGX_ERROR;\n    }\n\n    headers->capacity = capacity;\n    memcpy(headers->headers, old, headers->count * sizeof(xqc_http_headers_t));\n    ngx_pfree(r->pool, old);\n\n    return NGX_OK;\n}\n\n\n\nngx_int_t\nngx_http_xquic_headers_create_buf(ngx_http_request_t *r,\n    xqc_http_headers_t *headers, size_t capacity)\n{\n    headers->headers = ngx_pcalloc(r->pool, sizeof(xqc_http_header_t) * capacity);\n    if (headers->headers == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_memset(headers->headers, 0, sizeof(xqc_http_header_t) * capacity);\n    headers->count = 0;\n    headers->capacity = capacity;\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_xquic_header_save(ngx_http_request_t *r,\n    xqc_http_headers_t * headers, ngx_str_t * name, ngx_str_t * value)\n{\n    if(headers->capacity == 0){\n        ngx_http_xquic_headers_create_buf(r, headers, NGX_XQUIC_HEADERS_INIT_CAPACITY);\n    }\n\n    if(headers->count >= headers->capacity){\n        size_t capacity = headers->capacity + NGX_XQUIC_HEADERS_INIT_CAPACITY;\n        if(ngx_http_xquic_headers_realloc_buf(r, headers, capacity) < 0){\n            return NGX_ERROR;\n        }\n    }\n\n    xqc_http_header_t * header  = &headers->headers[headers->count++];\n\n    header->name.iov_base = ngx_pcalloc(r->pool, name->len + 1);\n    header->name.iov_len = name->len;\n    header->value.iov_base = ngx_pcalloc(r->pool, value->len + 1);\n    header->value.iov_len = value->len;\n    ngx_memcpy(header->name.iov_base, name->data, header->name.iov_len);\n    ngx_memcpy(header->value.iov_base, value->data, header->value.iov_len);\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_xquic_save_response_headers(ngx_http_request_t *r)\n{\n    ngx_list_part_t           *part;\n    ngx_table_elt_t           *header;\n    ngx_uint_t                 i;\n    u_char                     tmp_buf[NGX_XQUIC_TMP_BUF_SIZE];\n    ngx_str_t                  name_status = ngx_string(\":status\");\n    ngx_str_t                  name_location = ngx_string(\"location\");\n    ngx_str_t                  value;\n\n    /* put in xqstream->resp_headers */\n    ngx_http_v3_stream_t *qstream = r->xqstream;\n    ngx_http_xquic_headers_initial(&(qstream->resp_headers));\n\n    /* set status & location */\n    ngx_snprintf(tmp_buf, sizeof(tmp_buf), \"%3D\", r->headers_out.status);\n    value.data = tmp_buf;\n    value.len = 3;\n    if (ngx_http_xquic_header_save(r, &(qstream->resp_headers),\n            &name_status, &value) != NGX_OK)\n    {\n        ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,\n                      \"|xquic|add response header value fail: \\\"%V: %V\\\"|\",\n                      &name_status, &value);\n        return NGX_ERROR;\n    }\n\n    if (r->headers_out.location && r->headers_out.location->value.len) {\n\n        if (ngx_http_xquic_header_save(r, &(qstream->resp_headers),\n                &name_location, &(r->headers_out.location->value)) != NGX_OK)\n        {\n            ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,\n                      \"|xquic|add response header value fail: \\\"%V: %V\\\"|\",\n                      &name_status, &value);\n            return NGX_ERROR;\n        }\n    }\n\n\n    part = &r->headers_out.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 (header[i].hash == 0) {\n            continue;\n        }\n\n        if (header[i].key.len > NGX_HTTP_V3_MAX_FIELD) {\n            ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,\n                          \"too long response header name: \\\"%V\\\"\",\n                          &header[i].key);\n            return NGX_ERROR;\n        }\n\n        if (header[i].value.len > NGX_HTTP_V3_MAX_FIELD) {\n            ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,\n                          \"too long response header value: \\\"%V: %V\\\"\",\n                          &header[i].key, &header[i].value);\n            return NGX_ERROR;\n        }\n\n        if (ngx_http_xquic_header_save(r, &(qstream->resp_headers),\n                &header[i].key, &header[i].value) != NGX_OK)\n        {\n            ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,\n                          \"|xquic|add response header value fail: \\\"%V: %V\\\"|\",\n                          &header[i].key, &header[i].value);\n            return NGX_ERROR;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_xquic_header_filter(ngx_http_request_t *r)\n{\n    u_char                    *p;\n    u_char                     addr[NGX_SOCKADDR_STRLEN];\n    size_t                     len;\n    ngx_int_t                  bytes_sent;\n    ngx_str_t                  host, location;\n    ngx_uint_t                 port;\n    ngx_connection_t          *fc;\n    ngx_table_elt_t           *h;\n    ngx_http_core_loc_conf_t  *clcf;\n    ngx_http_core_srv_conf_t  *cscf;\n    struct sockaddr_in        *sin;\n#if (NGX_HAVE_INET6)\n    struct sockaddr_in6       *sin6;\n#endif\n\n    if (!r->xqstream) {\n        return ngx_http_next_header_filter(r);\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, \"|xquic|xquic header filter|\");\n\n    if (r->xqstream->engine_inner_closed) {\n        ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,\n                            \"|xquic|inner closed and fail to send header|\");\n        return NGX_ERROR;\n    }\n\n    if (r->header_sent) {\n        return NGX_OK;\n    }\n\n    r->header_sent = 1;\n\n    if (r != r->main) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, \"|xquic|not main request in xquic|\");\n        return NGX_OK;\n    }\n\n    fc = r->connection;\n\n    if (fc->error) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0, \"|xquic|fake connection error|\");\n        return NGX_ERROR;\n    }\n\n    if (r->method == NGX_HTTP_HEAD) {\n        r->header_only = 1;\n    }\n\n    switch (r->headers_out.status) {\n\n    case NGX_HTTP_OK:\n    case NGX_HTTP_PARTIAL_CONTENT:\n        break;\n\n    case NGX_HTTP_NO_CONTENT:\n        r->header_only = 1;\n\n        ngx_str_null(&r->headers_out.content_type);\n\n        r->headers_out.content_length = NULL;\n        r->headers_out.content_length_n = -1;\n\n        r->headers_out.last_modified_time = -1;\n        r->headers_out.last_modified = NULL;\n        break;\n\n    case NGX_HTTP_NOT_MODIFIED:\n        r->header_only = 1;\n        break;\n\n    default:\n        r->headers_out.last_modified_time = -1;\n        r->headers_out.last_modified = NULL;\n        break;\n    }\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    if (r->headers_out.location && r->headers_out.location->value.len) {\n\n        if (r->headers_out.location->value.data[0] == '/') {\n            if (clcf->server_name_in_redirect) {\n                cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);\n                host = cscf->server_name;\n\n            } else if (r->headers_in.server.len) {\n                host = r->headers_in.server;\n\n            } else {\n                host.len = NGX_SOCKADDR_STRLEN;\n                host.data = addr;\n\n                if (ngx_connection_local_sockaddr(fc, &host, 0) != NGX_OK) {\n                    return NGX_ERROR;\n                }\n            }\n\n            switch (fc->local_sockaddr->sa_family) {\n\n#if (NGX_HAVE_INET6)\n            case AF_INET6:\n                sin6 = (struct sockaddr_in6 *) fc->local_sockaddr;\n                port = ntohs(sin6->sin6_port);\n                break;\n#endif\n#if (NGX_HAVE_UNIX_DOMAIN)\n            case AF_UNIX:\n                port = 0;\n                break;\n#endif\n            default: /* AF_INET */\n                sin = (struct sockaddr_in *) fc->local_sockaddr;\n                port = ntohs(sin->sin_port);\n                break;\n            }\n\n            location.len = sizeof(\"https://\") - 1 + host.len\n                           + r->headers_out.location->value.len;\n\n            if (clcf->port_in_redirect) {\n                port = (port == 443) ? 0 : port;\n            } else {\n                port = 0;\n            }\n\n            if (port) {\n                location.len += sizeof(\":65535\") - 1;\n            }\n\n            location.data = ngx_pnalloc(r->pool, location.len);\n            if (location.data == NULL) {\n                return NGX_ERROR;\n            }\n\n            p = ngx_cpymem(location.data, \"https\", sizeof(\"https\") - 1);\n\n            *p++ = ':'; *p++ = '/'; *p++ = '/';\n            p = ngx_cpymem(p, host.data, host.len);\n\n            if (port) {\n                p = ngx_sprintf(p, \":%ui\", port);\n            }\n\n            p = ngx_cpymem(p, r->headers_out.location->value.data,\n                              r->headers_out.location->value.len);\n\n            /* update r->headers_out.location->value for possible logging */\n\n            r->headers_out.location->value.len = p - location.data;\n            r->headers_out.location->value.data = location.data;\n            ngx_str_set(&r->headers_out.location->key, \"Location\");\n        }\n\n        r->headers_out.location->hash = 0;\n    }\n\n#if (NGX_HTTP_GZIP)\n    if (r->gzip_vary) {\n        if (!clcf->gzip_vary) {\n            r->gzip_vary = 0;\n        }\n    }\n#endif\n\n    /* server */\n    if (r->headers_out.server == NULL) {\n        h = ngx_list_push(&r->headers_out.headers);\n        if (h == NULL) {\n            return NGX_ERROR;\n        }\n\n        h->hash = 1;\n        ngx_str_set(&h->key, NGX_HTTP_XQUIC_NAME_SERVER);\n        if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) {\n#if (T_NGX_SERVER_INFO)\n            ngx_str_set(&h->value, TENGINE_VER);\n#else\n            ngx_str_set(&h->value, NGINX_VER);\n#endif\n        } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) {\n#if (T_NGX_SERVER_INFO)\n            ngx_str_set(&h->value, TENGINE_VER_BUILD);\n#else\n            ngx_str_set(&h->value, NGINX_VER_BUILD);\n#endif\n        } else {\n            ngx_str_set(&h->value, TENGINE);\n        }\n        r->headers_out.server = h;\n    }\n\n    /* date */\n    if (r->headers_out.date == NULL) {\n        h = ngx_list_push(&r->headers_out.headers);\n        if (h == NULL) {\n            return NGX_ERROR;\n        }\n\n        h->hash = 1;\n        ngx_str_set(&h->key, NGX_HTTP_XQUIC_NAME_DATE);\n        h->value = ngx_cached_http_time;\n\n        r->headers_out.date = h;\n    }\n\n    /* content-type */\n    if (r->headers_out.content_type.len) {\n        h = ngx_list_push(&r->headers_out.headers);\n        if (h == NULL) {\n            return NGX_ERROR;\n        }\n\n        h->hash = 1;\n        ngx_str_set(&h->key, NGX_HTTP_XQUIC_NAME_CONTENT_TYPE);\n\n        if (r->headers_out.content_type_len == r->headers_out.content_type.len\n            && r->headers_out.charset.len)\n        {\n            len = r->headers_out.content_type.len + sizeof(\"; charset=\") - 1\n                  + r->headers_out.charset.len;\n\n            p = ngx_palloc(r->pool, len);\n            if (p == NULL) {\n                return NGX_ERROR;\n            }\n\n            p = ngx_cpymem(p, r->headers_out.content_type.data,\n                             r->headers_out.content_type.len);\n\n            p = ngx_cpymem(p, \"; charset=\", sizeof(\"; charset=\") - 1);\n\n            p = ngx_cpymem(p, r->headers_out.charset.data,\n                             r->headers_out.charset.len);\n\n            /* update r->headers_out.content_type for possible logging */\n\n            r->headers_out.content_type.len = len;\n            r->headers_out.content_type.data = p - len;\n        }\n\n\n        h->value = r->headers_out.content_type;\n    }\n\n    /* content-length */\n    if (r->headers_out.content_length == NULL\n        && r->headers_out.content_length_n >= 0)\n    {\n        h = ngx_list_push(&r->headers_out.headers);\n        if (h == NULL) {\n            return NGX_ERROR;\n        }\n\n        h->hash = 1;\n        ngx_str_set(&h->key, NGX_HTTP_XQUIC_NAME_CONTENT_LENGTH);\n\n        h->value.data = ngx_palloc(r->pool, NGX_INT_T_LEN);\n        if (h->value.data == NULL) {\n            return NGX_ERROR;\n        }\n\n        h->value.len = ngx_sprintf(h->value.data, \"%O\", r->headers_out.content_length_n)\n                       - h->value.data;\n\n        r->headers_out.content_length = h;\n    }\n\n    /* last-modified */\n    if (r->headers_out.last_modified == NULL\n        && r->headers_out.last_modified_time != -1)\n    {\n        h = ngx_list_push(&r->headers_out.headers);\n        if (h == NULL) {\n            return NGX_ERROR;\n        }\n\n        h->hash = 1;\n        ngx_str_set(&h->key, NGX_HTTP_XQUIC_NAME_LAST_MODIFIED);\n\n        h->value.data = ngx_palloc(r->pool, sizeof(\"Wed, 31 Dec 1986 18:00:00 GMT\") - 1);\n        if (h->value.data == NULL) {\n            return NGX_ERROR;\n        }\n\n        h->value.len = ngx_http_time(h->value.data, r->headers_out.last_modified_time)\n                       - h->value.data;\n\n        r->headers_out.last_modified = h;\n    }\n\n    /* vary: accept-encoding */\n#if (NGX_HTTP_GZIP)\n    if (r->gzip_vary) {\n        h = ngx_list_push(&r->headers_out.headers);\n        if (h == NULL) {\n            return NGX_ERROR;\n        }\n\n        h->hash = 1;\n        ngx_str_set(&h->key, NGX_HTTP_XQUIC_NAME_VARY);\n        ngx_str_set(&h->value, \"Accept-Encoding\");\n\n    }\n#endif\n\n    if (ngx_http_xquic_save_response_headers(r) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    /* xqc_h3_send_headers may return err, so we should set send_chain before send header */\n    fc->send_chain = ngx_http_xquic_send_chain;\n    fc->need_last_buf = 1;\n\n    r->xqstream->queued++;\n\n    bytes_sent = ngx_http_xquic_stream_send_header(r->xqstream);\n    if (bytes_sent < 0) {\n        ngx_log_error(NGX_LOG_WARN, fc->log, 0, \"|xquic|quic send header error|\");\n        return NGX_ERROR;\n    }\n\n    r->header_size += bytes_sent;\n    fc->sent += bytes_sent;\n\n    return NGX_OK;\n}\n\n\nngx_chain_t *\nngx_http_xquic_send_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)\n{\n    size_t                   size;\n    ssize_t                  n = 0;\n    off_t                    send = 0, buf_size = 0;\n    ngx_http_request_t      *r;\n    ngx_http_v3_stream_t    *h3_stream;\n    ngx_chain_t             *last_out = NULL, *last_chain, *cl;\n    ngx_buf_t               *buf;\n\n    r = c->data;\n\n    if (r->xqstream->engine_inner_closed) {\n        ngx_log_error(NGX_LOG_WARN, c->log, 0,\n                            \"|xquic|inner closed and fail to send chain|\");\n        return NGX_CHAIN_ERROR;\n    }\n\n\n    if (limit == 0 || limit > (off_t) (NGX_MAX_SIZE_T_VALUE - ngx_pagesize)) {\n        limit = NGX_MAX_SIZE_T_VALUE - ngx_pagesize;\n    }\n\n    /* update h3_stream->output_queue here */\n    h3_stream = r->xqstream;\n\n    last_chain = h3_stream->output_queue;\n\n    while (last_chain != NULL) {\n        if (last_chain->next == NULL) {\n            break;\n        }\n        last_chain = last_chain->next;\n    }\n\n    for ( /* void */ ; in; in = in->next) {\n        if (in == NULL) {\n            break;\n        }\n\n        cl = ngx_chain_get_free_buf(h3_stream->request->pool,\n                                    &h3_stream->free_bufs);\n        if (cl == NULL) {\n            goto RETURN_ERROR;\n        }\n\n        cl->next = NULL;\n        buf = cl->buf;\n        buf_size = ngx_buf_size(in->buf);\n        \n        if (!buf->start) {\n            buf->start = ngx_palloc(h3_stream->request->pool,\n                                    buf_size);\n            if (buf->start == NULL) {\n                goto RETURN_ERROR;\n            }\n        \n            buf->end = buf->start + buf_size;\n            buf->last = buf->end;\n        \n            buf->tag = (ngx_buf_tag_t) &ngx_http_xquic_module;\n            buf->memory = 1;\n        }\n        \n        buf->pos = buf->start;\n        buf->last = buf->pos;\n        \n        buf->last = ngx_cpymem(buf->last, in->buf->pos, buf_size);\n        buf->last_buf = in->buf->last_buf;\n        in->buf->pos += buf_size;\n\n        /* update output_queue */\n        if (last_chain == NULL) {\n            h3_stream->output_queue = cl;\n            \n        } else {\n            last_chain->next = cl;\n        }\n\n        last_chain = cl;\n        r->xqstream->queued++; /* used to count buffers not sent*/\n    }\n\n\n    last_out = h3_stream->output_queue;\n    send = 0;\n    \n    for ( /* void */ ; last_out; last_out = last_out->next) {\n\n        if (h3_stream->wait_to_write) {\n            break;\n        }\n\n        if (ngx_buf_special(last_out->buf)) {\n            if (last_out->buf->last_buf) {\n                ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0,\n                               \"|xquic|ngx_http_xquic_send_chain|send size %ui|last=%i|\",\n                               r->xqstream->body_sent, last_out->buf->last_buf);\n\n                n = ngx_http_xquic_stream_send_body(r->xqstream, NULL, 0, 1);\n\n                if (n == NGX_AGAIN) {\n\n                    /* need to send NULL + FIN again */\n                    //return in;\n                    goto RETURN_EAGAIN;\n\n                } else if (n < 0) {\n                    ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,\n                            \"|xquic|ngx_http_xquic_send_chain|send body fin error|\");\n                    r->xqstream->queued--;\n                    goto RETURN_ERROR;\n                }\n\n                r->xqstream->queued--;\n                //return NULL;\n                goto FINISH;\n            }\n            continue;\n        }\n\n        /* not support sendfile */\n        if (!ngx_buf_in_memory(last_out->buf)) {\n            ngx_log_error(NGX_LOG_ALERT, c->log, 0,\n                          \"|xquic|ngx_http_xquic_send_chain|not memory buf|\"\n                          \"t:%d r:%d f:%d %p %p-%p %p %O-%O|\",\n                          last_out->buf->temporary,\n                          last_out->buf->recycled,\n                          last_out->buf->in_file,\n                          last_out->buf->start,\n                          last_out->buf->pos,\n                          last_out->buf->last,\n                          last_out->buf->file,\n                          last_out->buf->file_pos,\n                          last_out->buf->file_last);\n\n            ngx_debug_point();\n\n            goto RETURN_ERROR;\n        }\n\n        if (send >= limit) {\n            break;\n        }\n\n        size = last_out->buf->last - last_out->buf->pos;\n\n        n = ngx_http_xquic_stream_send_body(r->xqstream,\n                                             last_out->buf->pos, size,\n                                             last_out->buf->last_buf);\n\n        ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0,\n                       \"|xquic|ngx_http_xquic_send_chain|send tot_size %ui, size %O, n=%z|last=%i|\",\n                       r->xqstream->body_sent, size, n, last_out->buf->last_buf);\n\n\n        if (n < 0) {\n            if (n == NGX_AGAIN) {\n                h3_stream->wait_to_write = 1;\n            } else {\n                ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0,\n                        \"|xquic|ngx_http_xquic_send_chain|send body error, body_sent %ui, size %O, n=%z|last=%i|\",\n                        r->xqstream->body_sent, size, n, last_out->buf->last_buf);\n            }\n        \n            break;\n        }\n\n        c->sent += n;\n        send += n;\n        size -= n;\n        last_out->buf->pos += n;  /* in->buf->pos = in->buf->last */\n\n        if (size != 0) {\n            /* xquic inner send buffer full will cause n < size */\n\n            break;\n        } else {\n            /* finish sending this buffer */\n            r->xqstream->queued--;\n        }\n    }\n\nRETURN_EAGAIN:\n\nFINISH:\n\n    r->xqstream->output_queue = last_out;\n\n    return in;\n\nRETURN_ERROR:\n\n    r->xqstream->output_queue = last_out;\n\n    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"|xquic|ngx_http_xquic_send_chain|send tot_size %ui, size %O, n=%z|chain error|\", \n                   r->xqstream->body_sent, send, n);\n\n    return NGX_CHAIN_ERROR;\n}\n\nngx_int_t\nngx_http_xquic_filter_init(ngx_conf_t *cf)\n{\n    ngx_http_next_header_filter = ngx_http_top_header_filter;\n    ngx_http_top_header_filter = ngx_http_xquic_header_filter;\n\n\n    return NGX_OK;\n}\n\n"
  },
  {
    "path": "modules/ngx_http_xquic_module/ngx_http_xquic_module.c",
    "content": "/*\n * Copyright (C) 2020-2023 Alibaba Group Holding Limited\n */\n\n#include <ngx_http_xquic_module.h>\n#include <ngx_xquic.h>\n\n\n#define NGX_XQUIC_DEFAULT_DOMAIN_SOCKET_PATH \"/dev/shm/tengine/xquic\"\n\n\n#define NGX_XQUIC_LOG_REPORT    0\n#define NGX_XQUIC_LOG_FATAL     1\n#define NGX_XQUIC_LOG_ERROR     2\n#define NGX_XQUIC_LOG_WARN      3\n#define NGX_XQUIC_LOG_STATS     4\n#define NGX_XQUIC_LOG_INFO      5\n#define NGX_XQUIC_LOG_DEBUG     6\n\n\ntypedef ngx_int_t (*ngx_ssl_variable_handler_pt)(SSL *ssl,\n    ngx_pool_t *pool, ngx_str_t *s);\n\n\nstatic ngx_str_t ngx_xquic_log_levels[] = {\n    ngx_string(\"report\"),  \n    ngx_string(\"fatal\"),\n    ngx_string(\"error\"),\n    ngx_string(\"warn\"),\n    ngx_string(\"stats\"),    \n    ngx_string(\"info\"),\n    ngx_string(\"debug\"),\n    ngx_null_string\n};\n\n\nstatic ngx_int_t ngx_http_xquic_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic void ngx_http_xquic_off_set_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_xquic_off_get_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_xquic_connection_id_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_xquic_stream_id_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_xquic_ssl_static_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_xquic_ssl_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_xquic_add_variables(ngx_conf_t *cf);\nstatic char * ngx_http_xquic_streams_index_mask(ngx_conf_t *cf, void *post, void *data);\nstatic char * ngx_http_xquic_set_log_level(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\nstatic void * ngx_http_xquic_create_main_conf(ngx_conf_t *cf);\nstatic char * ngx_http_xquic_init_main_conf(ngx_conf_t *cf, void *conf);\nstatic void * ngx_http_xquic_create_srv_conf(ngx_conf_t *cf);\nstatic char * ngx_http_xquic_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child);\nstatic ngx_int_t ngx_http_xquic_process_init(ngx_cycle_t *cycle);\nstatic void ngx_http_xquic_process_exit(ngx_cycle_t *cycle);\nstatic ngx_int_t ngx_http_xquic_init(ngx_conf_t *cf);\nstatic ngx_int_t ngx_http_xquic_access_handler(ngx_http_request_t *r);\nstatic char * ngx_http_set_xquic_status(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\n\n\nngx_http_xquic_main_conf_t *ngx_http_xquic_main_conf = NULL;\n\n\nstatic ngx_conf_post_t  ngx_http_xquic_streams_index_mask_post =\n    { ngx_http_xquic_streams_index_mask };\n\n\n\nstatic ngx_command_t  ngx_http_xquic_commands[] = {\n\n    { ngx_string(\"xquic_ssl_certificate\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_slot,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      offsetof(ngx_http_xquic_main_conf_t, certificate),\n      NULL },\n\n    { ngx_string(\"xquic_ssl_certificate_key\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_slot,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      offsetof(ngx_http_xquic_main_conf_t, certificate_key),\n      NULL },\n      \n    { ngx_string(\"xquic_ssl_session_ticket_key\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_slot,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      offsetof(ngx_http_xquic_main_conf_t, session_ticket_key),\n      NULL },\n\n    { ngx_string(\"xquic_stateless_reset_token_key\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_slot,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      offsetof(ngx_http_xquic_main_conf_t, stateless_reset_token_key),\n      NULL },\n\n    { ngx_string(\"xquic_log_file\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_slot,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      offsetof(ngx_http_xquic_main_conf_t, log_file_path),\n      NULL },\n\n    { ngx_string(\"xquic_use_new_udp_hash\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      offsetof(ngx_http_xquic_main_conf_t, new_udp_hash),\n      NULL },\n\n    { ngx_string(\"xquic_log_level\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,\n      ngx_http_xquic_set_log_level,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      offsetof(ngx_http_xquic_main_conf_t, log_level),\n      NULL },\n\n    { ngx_string(\"xquic_socket_sndbuf\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_num_slot,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      offsetof(ngx_http_xquic_main_conf_t, socket_sndbuf),\n      NULL },\n\n    { ngx_string(\"xquic_qpack_encoder_dynamic_table_size\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      offsetof(ngx_http_xquic_main_conf_t, qpack_encoder_dynamic_table_capacity),\n      NULL },\n\n    { ngx_string(\"xquic_qpack_decoder_dynamic_table_size\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      offsetof(ngx_http_xquic_main_conf_t, qpack_decoder_dynamic_table_capacity),\n      NULL },\n\n    { ngx_string(\"xquic_socket_rcvbuf\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_num_slot,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      offsetof(ngx_http_xquic_main_conf_t, socket_rcvbuf),\n      NULL },\n\n    { ngx_string(\"xquic_congestion_control\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_slot,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      offsetof(ngx_http_xquic_main_conf_t, congestion_control),\n      NULL },\n\n    { ngx_string(\"xquic_pacing\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      offsetof(ngx_http_xquic_main_conf_t, pacing_on),\n      NULL },\n\n    { ngx_string(\"xquic_streams_index_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_xquic_main_conf_t, streams_index_mask),\n      &ngx_http_xquic_streams_index_mask_post },\n\n#if (NGX_XQUIC_SUPPORT_CID_ROUTE)\n\n    { ngx_string(\"xquic_cid_route\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      offsetof(ngx_http_xquic_main_conf_t, cid_route),\n      NULL },\n\n    { ngx_string(\"xquic_server_id_offset\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_num_slot,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      offsetof(ngx_http_xquic_main_conf_t, cid_server_id_offset),\n      NULL },\n\n    { ngx_string(\"xquic_server_id_length\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_num_slot,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      offsetof(ngx_http_xquic_main_conf_t, cid_server_id_length),\n      NULL },\n\n    { ngx_string(\"xquic_worker_id_offset\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_num_slot,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      offsetof(ngx_http_xquic_main_conf_t, cid_worker_id_offset),\n      NULL },\n\n#endif\n\n    { ngx_string(\"xquic_max_concurrent_connection_cnt\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_num_slot,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      offsetof(ngx_http_xquic_main_conf_t, max_quic_concurrent_connection_cnt),\n      NULL },\n\n    { ngx_string(\"xquic_max_cps\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_num_slot,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      offsetof(ngx_http_xquic_main_conf_t, max_quic_cps),\n      NULL },\n\n    { ngx_string(\"xquic_max_qps\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_num_slot,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      offsetof(ngx_http_xquic_main_conf_t, max_quic_qps),\n      NULL },\n\n    { ngx_string(\"xquic_status\"),\n      NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS|NGX_CONF_TAKE1,\n      ngx_http_set_xquic_status,\n      0,\n      0,\n      NULL },\n\n    { ngx_string(\"xquic_anti_amplification_limit\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_num_slot,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      offsetof(ngx_http_xquic_main_conf_t, anti_amplification_limit),\n      NULL },\n\n    { ngx_string(\"xquic_keyupdate_pkt_threshold\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_num_slot,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      offsetof(ngx_http_xquic_main_conf_t, keyupdate_pkt_threshold),\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_xquic_module_ctx = {\n    ngx_http_xquic_add_variables,          /* preconfiguration */\n    ngx_http_xquic_init,                   /* postconfiguration */\n\n    ngx_http_xquic_create_main_conf,       /* create main configuration */\n    ngx_http_xquic_init_main_conf,         /* init main configuration */\n\n    ngx_http_xquic_create_srv_conf,        /* create server configuration */\n    ngx_http_xquic_merge_srv_conf,         /* merge server configuration */\n\n    NULL,                                  /* create location configuration */\n    NULL                                   /* merge location configuration */\n};\n\n\nngx_module_t  ngx_http_xquic_module = {\n    NGX_MODULE_V1,\n    &ngx_http_xquic_module_ctx,            /* module context */\n    ngx_http_xquic_commands,               /* module directives */\n    NGX_HTTP_MODULE,                       /* module type */\n    NULL,                                  /* init master */\n    NULL,                                  /* init module */\n    ngx_http_xquic_process_init,           /* init process */\n    NULL,                                  /* init thread */\n    NULL,                                  /* exit thread */\n    ngx_http_xquic_process_exit,           /* exit process */\n    NULL,                                  /* exit master */\n    NGX_MODULE_V1_PADDING\n};\n\n\nstatic ngx_http_variable_t  ngx_http_xquic_vars[] = {\n\n    { ngx_string(\"xquic\"), NULL,\n      ngx_http_xquic_variable, 0, 0, 0 },\n\n    { ngx_string(\"xquic_off\"),\n      ngx_http_xquic_off_set_variable,\n      ngx_http_xquic_off_get_variable,\n      0, NGX_HTTP_VAR_CHANGEABLE, 0 },\n\n    { ngx_string(\"xquic_connection_id\"), NULL,\n      ngx_http_xquic_connection_id_variable, 0, 0, 0 },\n\n    { ngx_string(\"xquic_stream_id\"), NULL,\n      ngx_http_xquic_stream_id_variable, 0, 0, 0 },\n\n    { ngx_string(\"xquic_ssl_protocol\"), NULL, ngx_xquic_ssl_static_variable,\n      (uintptr_t) ngx_xquic_ssl_get_protocol, NGX_HTTP_VAR_CHANGEABLE, 0 },\n\n    { ngx_string(\"xquic_ssl_cipher\"), NULL, ngx_xquic_ssl_static_variable,\n      (uintptr_t) ngx_xquic_ssl_get_cipher_name, NGX_HTTP_VAR_CHANGEABLE, 0 },\n\n    { ngx_string(\"xquic_ssl_session_reused\"), NULL, ngx_xquic_ssl_variable,\n      (uintptr_t) ngx_xquic_ssl_get_session_reused, NGX_HTTP_VAR_CHANGEABLE, 0 },\n\n    { ngx_null_string, NULL, NULL, 0, 0, 0 }\n};\n\n\nstatic ngx_int_t\nngx_http_xquic_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    v->not_found = 1;\n\n    if (r->xqstream) {\n        v->len = sizeof(\"xquic\") - 1;\n        v->valid = 1;\n        v->no_cacheable = 0;\n        v->not_found = 0;\n        v->data = (u_char *) \"xquic\";\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_xquic_off_set_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    ngx_http_xquic_connection_t  *qc;\n\n    if (r->xqstream) {\n        qc = r->xqstream->connection;\n        if (v->len == 2 && ngx_strncasecmp(v->data, (u_char *) \"on\", 2) == 0) {\n            qc->xquic_off = 1;\n        } else if (v->len == 3 && ngx_strncasecmp(v->data, (u_char *) \"off\", 3) == 0) {\n            qc->xquic_off = 0;\n        }\n    }\n}\n\nstatic ngx_int_t\nngx_http_xquic_off_get_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    ngx_http_xquic_connection_t  *qc;\n\n    v->not_found = 1;\n\n    if (r->xqstream) {\n        qc = r->xqstream->connection;\n\n        v->data = ngx_pnalloc(r->pool, sizeof(\"off\") - 1);\n        if (v->data == NULL) {\n            return NGX_ERROR;\n        }\n\n        if (qc->xquic_off == 0) {\n            v->len = 3;\n            v->data[0] = 'o';\n            v->data[1] = 'f';\n            v->data[2] = 'f';\n        } else {\n            v->len = 2;\n            v->data[0] = 'o';\n            v->data[1] = 'n';\n        }\n\n        v->valid = 1;\n        v->no_cacheable = 0;\n        v->not_found = 0;\n    }\n\n    return NGX_OK;\n}\n\n\n\nstatic ngx_int_t\nngx_http_xquic_connection_id_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    u_char                       *p;\n    ngx_http_xquic_connection_t  *qc;\n\n    v->not_found = 1;\n\n    if (r->xqstream != NULL && r->xqstream->connection != NULL) {\n        qc = r->xqstream->connection;\n\n        p = ngx_pnalloc(r->pool, qc->dcid.cid_len * 2);\n        if (p == NULL) {\n            return NGX_ERROR;\n        }\n\n        v->len = ngx_snprintf(p, qc->dcid.cid_len * 2, \"%s\", xqc_dcid_str(&qc->dcid)) - p;\n        v->valid = 1;\n        v->no_cacheable = 0;\n        v->not_found = 0;\n        v->data = p;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_xquic_stream_id_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    u_char                      *p;\n\n    v->not_found = 1;\n\n    if (r->xqstream) {\n        p = ngx_pnalloc(r->pool, NGX_INT64_LEN);\n        if (p == NULL) {\n            return NGX_ERROR;\n        }\n\n        v->len = ngx_snprintf(p, NGX_OFF_T_LEN, \"%O\", r->xqstream->id) - p;\n        v->valid = 1;\n        v->no_cacheable = 0;\n        v->not_found = 0;\n        v->data = p;\n     }\n\n    return NGX_OK;\n}\n\n\n\nstatic ngx_int_t\nngx_http_xquic_add_variables(ngx_conf_t *cf)\n{\n    ngx_http_variable_t  *var, *v;\n\n    for (v = ngx_http_xquic_vars; v->name.len; v++) {\n        var = ngx_http_add_variable(cf, &v->name, v->flags);\n        if (var == NULL) {\n            return NGX_ERROR;\n        }\n\n        var->get_handler = v->get_handler;\n        var->set_handler = v->set_handler;\n        var->data = v->data;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic char *\nngx_http_xquic_streams_index_mask(ngx_conf_t *cf, void *post, void *data)\n{\n    ngx_uint_t *np = data;\n\n    ngx_uint_t  mask;\n\n    mask = *np - 1;\n\n    if (*np == 0 || (*np & mask)) {\n        return \"must be a power of two\";\n    }\n\n    *np = mask;\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_xquic_set_log_level(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_str_t        *value;\n    ngx_uint_t        i;\n    ngx_http_xquic_main_conf_t *qmcf = conf;\n\n    value = cf->args->elts;\n\n    if (qmcf->log_level != NGX_CONF_UNSET_UINT) {\n        return \"is duplicate\";\n    }\n\n    for (i = 0; i <= NGX_XQUIC_LOG_DEBUG; i++) {\n        if (value[1].len == ngx_xquic_log_levels[i].len\n            && ngx_strncmp(value[1].data, ngx_xquic_log_levels[i].data, value[1].len) == 0)\n        {\n            qmcf->log_level = i;\n            return NGX_CONF_OK;\n        }\n    }\n\n    return \"invalid log level\";\n}\n\n\n\nstatic void *\nngx_http_xquic_create_main_conf(ngx_conf_t *cf)\n{\n    ngx_http_xquic_main_conf_t *qmcf;\n\n    qmcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_xquic_main_conf_t));\n    if (qmcf == NULL) {\n        return NULL;\n    }\n\n    /* set by ngx_pcalloc\n     *     qmcf->intercom_socket_path = { NULL, 0 };\n     *     qmcf->congestion_control = { NULL, 0 };\n     */\n\n    qmcf->log_level = NGX_CONF_UNSET_UINT;\n    qmcf->intercom_pool_size = NGX_CONF_UNSET_SIZE;\n    qmcf->new_udp_hash = NGX_CONF_UNSET;\n    qmcf->conn_max_streams_can_create = NGX_CONF_UNSET_UINT;\n\n    qmcf->socket_rcvbuf = NGX_CONF_UNSET;\n    qmcf->socket_sndbuf = NGX_CONF_UNSET;\n\n    qmcf->streams_index_mask = NGX_CONF_UNSET_UINT;\n\n    qmcf->pacing_on = NGX_CONF_UNSET;\n\n    qmcf->qpack_encoder_dynamic_table_capacity = NGX_CONF_UNSET_SIZE;\n    qmcf->qpack_decoder_dynamic_table_capacity = NGX_CONF_UNSET_SIZE;\n\n#if (NGX_XQUIC_SUPPORT_CID_ROUTE)\n    qmcf->cid_route             = NGX_CONF_UNSET;\n    qmcf->cid_server_id_offset  = NGX_CONF_UNSET_UINT;\n    qmcf->cid_server_id_length  = NGX_CONF_UNSET_UINT;\n    qmcf->cid_worker_id_offset  = NGX_CONF_UNSET_UINT;\n#endif\n\n    qmcf->max_quic_concurrent_connection_cnt = NGX_CONF_UNSET_UINT;\n    qmcf->max_quic_cps = NGX_CONF_UNSET_UINT;\n    qmcf->max_quic_qps = NGX_CONF_UNSET_UINT;\n\n    qmcf->anti_amplification_limit = NGX_CONF_UNSET_UINT;\n\n    qmcf->keyupdate_pkt_threshold = NGX_CONF_UNSET_UINT;\n\n    ngx_http_xquic_main_conf = qmcf;\n\n    return qmcf;\n}\n\n\nstatic char *\nngx_http_xquic_init_main_conf(ngx_conf_t *cf, void *conf)\n{\n    ngx_http_xquic_main_conf_t *qmcf = conf;\n\n    if (qmcf->log_level == NGX_CONF_UNSET_UINT) {\n        qmcf->log_level = NGX_XQUIC_LOG_ERROR;\n    }\n\n    if (qmcf->intercom_pool_size == NGX_CONF_UNSET_SIZE) {\n        qmcf->intercom_pool_size = 4096;\n    }\n\n    if (qmcf->new_udp_hash == NGX_CONF_UNSET) {\n        qmcf->new_udp_hash = 0;\n    }\n\n    if (qmcf->socket_rcvbuf == NGX_CONF_UNSET) {\n        qmcf->socket_rcvbuf = 1*1024*1024;\n    }\n\n    if (qmcf->socket_sndbuf == NGX_CONF_UNSET) {\n        qmcf->socket_sndbuf = 1*1024*1024;\n    }\n\n    if (qmcf->conn_max_streams_can_create == NGX_CONF_UNSET_UINT) {\n        qmcf->conn_max_streams_can_create = 4096;\n    }\n\n    if (qmcf->streams_index_mask == NGX_CONF_UNSET_UINT) {\n        qmcf->streams_index_mask = 32 - 1;\n    }\n\n    if (qmcf->intercom_socket_path.data == NULL) {\n        qmcf->intercom_socket_path.data = (u_char *) NGX_XQUIC_DEFAULT_DOMAIN_SOCKET_PATH;\n        qmcf->intercom_socket_path.len = sizeof(NGX_XQUIC_DEFAULT_DOMAIN_SOCKET_PATH) - 1;\n    }\n\n    if (qmcf->certificate.data == NULL) {\n        ngx_str_set(&(qmcf->certificate), \"./server.crt\");\n    }\n\n    if (qmcf->certificate_key.data == NULL) {\n        ngx_str_set(&(qmcf->certificate_key), \"./server.key\");\n    }\n\n    if (qmcf->session_ticket_key.data == NULL) {\n        ngx_str_set(&(qmcf->session_ticket_key), \"./session_ticket.key\");\n    }\n\n    if (qmcf->stateless_reset_token_key.data == NULL) {\n        ngx_str_set(&(qmcf->stateless_reset_token_key), \".@34dshj+={}\");\n    }\n\n    if (qmcf->log_file_path.data == NULL) {\n        ngx_str_set(&(qmcf->log_file_path), \"./xquic_log\");\n    }\n\n    if (qmcf->congestion_control.data == NULL) {\n        ngx_str_set(&(qmcf->congestion_control), \"bbr\");\n    }\n\n    if (qmcf->pacing_on == NGX_CONF_UNSET) {\n        qmcf->pacing_on = 0;\n    }\n\n    if (qmcf->qpack_encoder_dynamic_table_capacity == NGX_CONF_UNSET_SIZE) {\n        qmcf->qpack_encoder_dynamic_table_capacity = 16 * 1024;\n    }\n\n    if (qmcf->qpack_decoder_dynamic_table_capacity == NGX_CONF_UNSET_SIZE) {\n        qmcf->qpack_decoder_dynamic_table_capacity = 16 * 1024;\n    }\n\n#if (NGX_XQUIC_SUPPORT_CID_ROUTE)\n\n#define NGX_QUIC_CID_ROUTE_FIRST_OCTER              (1)\n#define NGX_QUIC_CID_ROUTE_SERVER_ID                (3)\n#define NGX_QUIC_CID_ROUTE_ENTROPY                  (4)\n\n#define NGX_QUIC_CID_ROUTE_WORKER_ID_OFFSET         (NGX_QUIC_CID_ROUTE_FIRST_OCTER + NGX_QUIC_CID_ROUTE_SERVER_ID + NGX_QUIC_CID_ROUTE_ENTROPY)\n\n    ngx_conf_init_uint_value(qmcf->cid_server_id_offset, NGX_QUIC_CID_ROUTE_FIRST_OCTER);\n    ngx_conf_init_uint_value(qmcf->cid_server_id_length, NGX_QUIC_CID_ROUTE_SERVER_ID);\n    ngx_conf_init_uint_value(qmcf->cid_worker_id_offset, NGX_QUIC_CID_ROUTE_WORKER_ID_OFFSET);\n\n    /* enable by default */\n    if (qmcf->cid_route != 0) {\n        qmcf->cid_route = 1;\n        ngx_xquic_init_cid_route(cf->cycle, qmcf);\n    }\n\n    /* overlap */\n    if (qmcf->cid_worker_id_offset < qmcf->cid_server_id_offset + qmcf->cid_server_id_length \n        && qmcf->cid_worker_id_offset + NGX_QUIC_CID_ROUTE_WORKER_ID_LENGTH > qmcf->cid_server_id_offset)\n    {\n        return \"|xquic|overlap server id and worker id|\";\n    }\n\n    /* get cid length */\n    qmcf->cid_len = ngx_max(qmcf->cid_worker_id_offset + NGX_QUIC_CID_ROUTE_WORKER_ID_LENGTH, qmcf->cid_server_id_offset + qmcf->cid_server_id_length);\n\n#define NGX_QUIC_CID_MAX_LEN  (20)\n    if (qmcf->cid_len > NGX_QUIC_CID_MAX_LEN) {\n        return \"|xquic|exceed max cid length|\";\n    }\n#undef NGX_QUIC_CID_MAX_LEN\n\n    /* entropy space check */\n    if (qmcf->cid_len - NGX_QUIC_CID_ROUTE_WORKER_ID_LENGTH - qmcf->cid_server_id_length < NGX_QUIC_CID_ROUTE_ENTROPY) {\n        ngx_log_error(NGX_LOG_WARN, ngx_cycle->log, 0, \"|xquic|insufficient entropy space|\");\n    }\n\n#undef NGX_QUIC_CID_ROUTE_WORKER_ID_OFFSET\n#undef NGX_QUIC_CID_ROUTE_ENTROPY\n#undef NGX_QUIC_CID_ROUTE_SERVER_ID\n#undef NGX_QUIC_CID_ROUTE_FIRST_OCTER\n\n#endif\n\n    return NGX_CONF_OK;\n}\n\n\nstatic void *\nngx_http_xquic_create_srv_conf(ngx_conf_t *cf)\n{\n    ngx_http_xquic_srv_conf_t *qscf;\n\n    qscf = ngx_pcalloc(cf->pool, sizeof(ngx_http_xquic_srv_conf_t));\n    if (qscf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc\n     *     qscf->support_versions = 0;\n     */\n\n    qscf->post_enable = NGX_CONF_UNSET;\n\n    qscf->idle_conn_timeout = NGX_CONF_UNSET_MSEC;\n    qscf->max_idle_conn_timeout = NGX_CONF_UNSET_MSEC;\n\n    qscf->time_wait = NGX_CONF_UNSET_MSEC;\n    qscf->time_wait_max_conns = NGX_CONF_UNSET_UINT;\n\n    qscf->session_flow_control_window = NGX_CONF_UNSET_SIZE;\n    qscf->stream_flow_control_window = NGX_CONF_UNSET_SIZE;\n\n    return qscf;\n}\n\nstatic char *\nngx_http_xquic_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_http_xquic_srv_conf_t *prev = parent;\n    ngx_http_xquic_srv_conf_t *conf = child;\n\n    ngx_conf_merge_msec_value(conf->idle_conn_timeout,\n                              prev->idle_conn_timeout, 30000);\n\n    ngx_conf_merge_msec_value(conf->max_idle_conn_timeout,\n                              prev->max_idle_conn_timeout, 60000);\n\n    ngx_conf_merge_msec_value(conf->time_wait, prev->time_wait, 200000);\n    ngx_conf_merge_uint_value(conf->time_wait_max_conns,\n                              prev->time_wait_max_conns, 10000);\n\n    ngx_conf_merge_size_value(conf->session_flow_control_window,\n                              prev->session_flow_control_window, 1 *1024 * 1024);\n    ngx_conf_merge_size_value(conf->stream_flow_control_window,\n                              prev->stream_flow_control_window, 128 * 1024);\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_xquic_process_init(ngx_cycle_t *cycle)\n{\n    if (ngx_http_xquic_main_conf == NULL) {\n        /* if nginx.conf without http {} then xquic main conf equal null */\n        return NGX_OK;\n    }\n\n    if (ngx_xquic_process_init(cycle) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\nstatic void\nngx_http_xquic_process_exit(ngx_cycle_t *cycle)\n{\n    if (ngx_http_xquic_main_conf == NULL) {\n        return;\n    }\n\n    ngx_xquic_process_exit(cycle);\n}\n\n\nstatic ngx_int_t\nngx_http_xquic_init(ngx_conf_t *cf)\n{\n    ngx_http_handler_pt         *h;\n    ngx_http_core_main_conf_t   *cmcf;\n\n    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);\n\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_xquic_access_handler;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_xquic_access_handler(ngx_http_request_t *r)\n{\n    ngx_http_xquic_connection_t   *qc;\n\n    if (!r->xqstream) {\n        return NGX_DECLINED;\n    }\n\n    qc = r->xqstream->connection;\n\n    if (qc->xquic_off) {\n        ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                      \"xquic access handler: quic off\");\n\n        return NGX_HTTP_CLIENT_CLOSED_REQUEST;\n    }\n\n    // ngx_http_quic_status_init(qc, &(r->headers_in.host)->value);\n\n    return NGX_DECLINED;\n}\n\n\nstatic ngx_int_t\nngx_http_xquic_status_handler(ngx_http_request_t *r)\n{\n    size_t             size;\n    ngx_int_t          rc;\n    ngx_buf_t         *b;\n    ngx_chain_t        out;\n    ngx_atomic_int_t   cps, active, rq, limit_conns, limit_reqs;\n\n    if (r->method != NGX_HTTP_GET && r->method != NGX_HTTP_HEAD) {\n        return NGX_HTTP_NOT_ALLOWED;\n    }\n\n    rc = ngx_http_discard_request_body(r);\n\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    r->headers_out.content_type_len = sizeof(\"text/plain\") - 1;\n    ngx_str_set(&r->headers_out.content_type, \"text/plain\");\n    r->headers_out.content_type_lowcase = NULL;\n\n    if (r->method == NGX_HTTP_HEAD) {\n        r->headers_out.status = NGX_HTTP_OK;\n\n        rc = ngx_http_send_header(r);\n\n        if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {\n            return rc;\n        }\n    }\n\n    size = sizeof(\"xquic: accepts active requests limit_conns limit_requests\\n\") - 1\n           + 8 + 5 * NGX_ATOMIC_T_LEN;\n\n    b = ngx_create_temp_buf(r->pool, size);\n    if (b == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    out.buf = b;\n    out.next = NULL;\n\n    cps = *ngx_stat_quic_conns;\n    active = *ngx_stat_quic_concurrent_conns;\n    rq = *ngx_stat_quic_queries;\n    limit_conns = *ngx_stat_quic_conns_refused;\n    limit_reqs = *ngx_stat_quic_queries_refused;\n\n    b->last = ngx_cpymem(b->last, \"xquic: accepts active requests limit_conns limit_requests\\n\",\n                         sizeof(\"xquic: accepts active requests limit_conns limit_requests\\n\") - 1);\n\n    b->last = ngx_sprintf(b->last, \" %uA %uA %uA %uA %uA \\n\", cps, active, rq, limit_conns, limit_reqs);\n\n    r->headers_out.status = NGX_HTTP_OK;\n    r->headers_out.content_length_n = b->last - b->pos;\n\n    b->last_buf = (r == r->main) ? 1 : 0;\n    b->last_in_chain = 1;\n\n    rc = ngx_http_send_header(r);\n\n    if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {\n        return rc;\n    }\n\n    return ngx_http_output_filter(r, &out);\n}\n\n\nstatic char *\nngx_http_set_xquic_status(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_core_loc_conf_t  *clcf;\n\n    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);\n    clcf->handler = ngx_http_xquic_status_handler;\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_xquic_ssl_static_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    ngx_ssl_variable_handler_pt   handler = (ngx_ssl_variable_handler_pt) data;\n    size_t                        len;\n    ngx_str_t                     s;\n    ngx_http_xquic_connection_t  *qc;\n\n    if (r->xqstream && r->xqstream->connection)  {\n        qc = r->xqstream->connection;\n        if (qc->ssl_conn) {\n            (void) handler(qc->ssl_conn, NULL, &s);\n            v->data = s.data;\n            for (len = 0; v->data[len]; len++) { /* void */ }\n            v->len = len;\n            v->valid = 1;\n            v->no_cacheable = 0;\n            v->not_found = 0;\n\n            return NGX_OK;\n        }\n    }\n\n    v->not_found = 1;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_xquic_ssl_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,\n    uintptr_t data)\n{\n    ngx_ssl_variable_handler_pt   handler = (ngx_ssl_variable_handler_pt) data;\n    ngx_str_t                     s;\n    ngx_http_xquic_connection_t  *qc;\n\n    if (r->xqstream && r->xqstream->connection)  {\n        qc = r->xqstream->connection;\n        if (qc->ssl_conn) {\n            if (handler(qc->ssl_conn, r->pool, &s) != NGX_OK) {            \n                return NGX_ERROR;\n            }\n\n            v->len = s.len;\n            v->data = s.data;\n\n            if (v->len) {\n                v->valid = 1;\n                v->no_cacheable = 0;\n                v->not_found = 0;\n\n                return NGX_OK;\n            }\n        }\n    }\n\n    v->not_found = 1;\n\n    return NGX_OK;\n}\n\nngx_int_t\nngx_xquic_ssl_get_protocol(SSL *ssl, ngx_pool_t *pool, ngx_str_t *s)\n{\n    s->data = (u_char *) SSL_get_version(ssl);\n    return NGX_OK;\n}\n\nngx_int_t\nngx_xquic_ssl_get_cipher_name(SSL *ssl, ngx_pool_t *pool, ngx_str_t *s)\n{\n    s->data = (u_char *) SSL_get_cipher_name(ssl);\n    return NGX_OK;\n}\n\nngx_int_t\nngx_xquic_ssl_get_session_reused(SSL *ssl, ngx_pool_t *pool, ngx_str_t *s)\n{\n    if (SSL_session_reused(ssl)) {\n        ngx_str_set(s, \"r\");\n\n    } else {\n        ngx_str_set(s, \".\");\n    }\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "modules/ngx_http_xquic_module/ngx_http_xquic_module.h",
    "content": "/*\n * Copyright (C) 2020-2023 Alibaba Group Holding Limited\n */\n\n#ifndef _NGX_HTTP_XQUIC_MODULE_H_INCLUDED_\n#define _NGX_HTTP_XQUIC_MODULE_H_INCLUDED_\n\n\n#include <ngx_core.h>\n#include <ngx_config.h>\n#include <ngx_http.h>\n#include <xquic/xquic.h>\n\n\n#define ngx_http_xquic_index_size(qmcf)  (qmcf->streams_index_mask + 1)\n#define ngx_http_xquic_index(qmcf, sid)  (((sid) >> 1) & qmcf->streams_index_mask)\n\ntypedef struct {\n    xqc_engine_t               *xquic_engine;\n    xqc_engine_ssl_config_t     engine_ssl_config;\n\n    ngx_event_t                 engine_ev_timer;\n\n    ngx_fd_t                    log_fd;\n\n    ngx_str_t                   certificate;\n    ngx_str_t                   certificate_key;\n    ngx_str_t                   session_ticket_key;\n    ngx_str_t                   stateless_reset_token_key;\n\n    ngx_str_t                   log_file_path;\n    ngx_uint_t                  log_level;\n\n    size_t                      intercom_pool_size;\n    ngx_str_t                   intercom_socket_path;\n\n    ngx_str_t                   congestion_control;\n    ngx_flag_t                  pacing_on;\n\n    ngx_flag_t                  new_udp_hash;\n\n    ngx_int_t                   socket_rcvbuf;\n    ngx_int_t                   socket_sndbuf;\n\n    ngx_uint_t                  conn_max_streams_can_create;\n\n    ngx_uint_t                  streams_index_mask;\n\n    /* for HTTP/3 */\n    size_t                      qpack_encoder_dynamic_table_capacity;\n    size_t                      qpack_decoder_dynamic_table_capacity;\n#if (T_NGX_UDPV2)\n    /* udp bacth */\n    ngx_event_t                 udpv2_batch;\n#endif\n\n#if (NGX_XQUIC_SUPPORT_CID_ROUTE)\n    /* for cid route , 0 for off, other for on*/\n    ngx_flag_t                  cid_route;\n    uint32_t                    cid_len;\n    ngx_uint_t                  cid_server_id_offset;\n    ngx_uint_t                  cid_server_id_length;\n    ngx_uint_t                  cid_worker_id_offset;\n    /* salt range start from zero */\n    uint32_t                    cid_worker_id_salt_range;\n    uint32_t                    cid_worker_id_secret;\n#endif\n\n    /* max concurrent quic connection count */\n    ngx_uint_t                  max_quic_concurrent_connection_cnt;\n    /* max concurrent connection created per second */\n    ngx_uint_t                  max_quic_cps;\n    /* max concurrent incoming query per second */\n    ngx_uint_t                  max_quic_qps;\n    \n    /* anti-amplification limit */\n    ngx_uint_t                  anti_amplification_limit;\n\n    /* packet limit of a single 1-rtt key */\n    ngx_uint_t                  keyupdate_pkt_threshold;\n} ngx_http_xquic_main_conf_t;\n\n\ntypedef struct {\n\n    ngx_uint_t               support_versions;\n\n    ngx_flag_t               post_enable;\n\n    ngx_msec_t               idle_conn_timeout;\n    ngx_msec_t               max_idle_conn_timeout;\n\n    ngx_msec_t               time_wait;\n    ngx_uint_t               time_wait_max_conns;\n\n    size_t                   session_flow_control_window;\n    size_t                   stream_flow_control_window;\n\n//    ngx_quic_certificate_t  *cert;\n} ngx_http_xquic_srv_conf_t;\n\n\nextern ngx_module_t ngx_http_xquic_module;\nextern ngx_http_xquic_main_conf_t *ngx_http_xquic_main_conf;\n\n\n#if (NGX_XQUIC_SUPPORT_CID_ROUTE)\n/**\n * init xquic cid route stuff\n * @return NGX_OK on success, other for failed\n * */\nngx_int_t ngx_xquic_init_cid_route(ngx_cycle_t *, ngx_http_xquic_main_conf_t *qmcf);\n#endif\n\nngx_int_t ngx_xquic_ssl_get_protocol(SSL *ssl, ngx_pool_t *pool,\n    ngx_str_t *s);\nngx_int_t ngx_xquic_ssl_get_cipher_name(SSL *ssl, ngx_pool_t *pool,\n    ngx_str_t *s);\nngx_int_t ngx_xquic_ssl_get_session_reused(SSL *ssl, ngx_pool_t *pool,\n    ngx_str_t *s);\n\n#endif /* _NGX_HTTP_XQUIC_MODULE_H_INCLUDED_ */\n\n"
  },
  {
    "path": "modules/ngx_http_xquic_module/ngx_xquic.c",
    "content": "/*\n * Copyright (C) 2020-2023 Alibaba Group Holding Limited\n */\n\n/**\n * for engine and socket operation\n */\n\n#include <ngx_xquic.h>\n#include <ngx_http_xquic_module.h>\n#include <ngx_http_xquic.h>\n#include <ngx_xquic_intercom.h>\n#include <ngx_xquic_recv.h>\n#include <sys/stat.h>\n#include <fcntl.h>\n\n#include <xquic/xquic_typedef.h>\n#include <xquic/xquic.h>\n\n#define NGX_XQUIC_TMP_BUF_LEN 512\n#define NGX_XQUIC_SUPPORT_CID_ROUTE 1\n#if (T_NGX_UDPV2)\nstatic void ngx_xquic_batch_udp_traffic(ngx_event_t * ev);\n#endif\n\n\nxqc_engine_callback_t ngx_xquic_engine_callback = {\n\n#if (NGX_XQUIC_SUPPORT_CID_ROUTE)\n    .cid_generate_cb = ngx_xquic_cid_generate_cb,\n#endif\n\n    .set_event_timer = ngx_xquic_engine_set_event_timer,\n    .log_callbacks = {\n        .xqc_log_write_err = ngx_xquic_log_write_err,\n        .xqc_log_write_stat = ngx_xquic_log_write_stat,\n    },\n};\n\nxqc_transport_callbacks_t ngx_xquic_transport_callbacks = {\n\n    .server_accept = ngx_xquic_conn_accept,\n    .server_refuse = ngx_xquic_conn_refuse,\n    .write_socket = ngx_xquic_server_send,\n#if defined(T_NGX_XQUIC_SUPPORT_SENDMMSG)\n    .write_mmsg  = ngx_xquic_server_send_mmsg,\n#endif\n    .conn_update_cid_notify = ngx_http_v3_conn_update_cid_notify,\n    .conn_cert_cb = ngx_http_v3_cert_cb,\n};\n\n\nuint64_t \nngx_xquic_get_time()\n{\n    /* take the time in microseconds */\n    struct timeval tv;\n    gettimeofday(&tv, NULL);\n    uint64_t ul = tv.tv_sec * 1000000 + tv.tv_usec;\n    return  ul;\n}\n\n\n/* TODO: close file */\nngx_int_t \nngx_xquic_read_file_data( char * data, size_t data_len, char *filename)\n{\n    FILE * fp = fopen(filename, \"rb\");\n\n    if(fp == NULL){\n        return -1;\n    }\n    fseek(fp, 0 , SEEK_END);\n    size_t total_len  = ftell(fp);\n    fseek(fp, 0, SEEK_SET);\n    if(total_len > data_len){\n        return -1;\n    }\n\n    size_t read_len = fread(data, 1, total_len, fp);\n    if (read_len != total_len){\n\n        return -1;\n    }\n\n    return read_len;\n}\n\n\n/* run main logic */\nvoid \nngx_xquic_engine_timer_callback(ngx_event_t *ev)\n{\n    xqc_engine_t * engine = (xqc_engine_t *)(ev->data);\n\n    xqc_engine_main_logic(engine);\n    return;\n}\n\n\nvoid \nngx_xquic_engine_init_event_timer(ngx_http_xquic_main_conf_t *qmcf, xqc_engine_t *engine)\n{\n    ngx_event_t *ev = &(qmcf->engine_ev_timer);\n    ngx_log_error(NGX_LOG_DEBUG, ngx_cycle->log, 0, \n                    \"|xquic|ngx_xquic_init_event_timer|%p|\", engine);  \n\n    ngx_memzero(ev, sizeof(ngx_event_t));\n    ev->handler = ngx_xquic_engine_timer_callback;\n    ev->log = ngx_cycle->log;\n    ev->data = engine;\n\n#if (T_NGX_UDPV2)\n    ngx_memcpy(&qmcf->udpv2_batch, ev, sizeof(*ev));\n    qmcf->udpv2_batch.handler = ngx_xquic_batch_udp_traffic;\n#endif\n}\n\n\nvoid \nngx_xquic_engine_set_event_timer(xqc_msec_t wake_after, void *engine_user_data)\n{\n    ngx_http_xquic_main_conf_t *qmcf = (ngx_http_xquic_main_conf_t *)engine_user_data;\n    ngx_msec_t wake_after_ms = wake_after / 1000;\n\n    if(wake_after_ms == 0){\n        wake_after_ms = 1; //most event timer interval 1\n    }\n\n    if (qmcf->engine_ev_timer.timer_set){\n        ngx_del_timer(&(qmcf->engine_ev_timer));\n    }\n\n    ngx_add_timer(&(qmcf->engine_ev_timer), wake_after_ms);\n}\n\n\nngx_int_t\nngx_xquic_engine_init_alpn_ctx(ngx_cycle_t *cycle, xqc_engine_t *engine)\n{\n    ngx_int_t ret = NGX_OK;\n\n    xqc_h3_callbacks_t h3_cbs = {\n        .h3c_cbs = {\n            .h3_conn_create_notify = ngx_http_v3_conn_create_notify,\n            .h3_conn_close_notify = ngx_http_v3_conn_close_notify,\n            .h3_conn_handshake_finished = ngx_http_v3_conn_handshake_finished,\n        },\n        .h3r_cbs = {\n            .h3_request_write_notify = ngx_http_v3_request_write_notify,\n            .h3_request_read_notify = ngx_http_v3_request_read_notify,\n            .h3_request_create_notify = ngx_http_v3_request_create_notify,\n            .h3_request_close_notify = ngx_http_v3_request_close_notify,\n        }\n    };\n\n    /* init http3 context */\n    ret = xqc_h3_ctx_init(engine, &h3_cbs);\n    if (ret != XQC_OK) {\n        ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, \"init h3 context error, ret: %d\\n\", ret);\n        return ret;\n    }\n\n    return ret;\n}\n\nngx_int_t\nngx_xquic_engine_init(ngx_cycle_t *cycle)\n{\n    ngx_http_xquic_main_conf_t  *qmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_xquic_module);\n    xqc_engine_ssl_config_t *engine_ssl_config = NULL;\n    xqc_config_t config;\n\n    if (xqc_engine_get_default_config(&config, XQC_ENGINE_SERVER) < 0) {\n        return NGX_ERROR;\n    }\n\n    if (qmcf->stateless_reset_token_key.len > 0\n        && qmcf->stateless_reset_token_key.len <= XQC_RESET_TOKEN_MAX_KEY_LEN)\n    {\n        strncpy(config.reset_token_key, (char *)qmcf->stateless_reset_token_key.data, XQC_RESET_TOKEN_MAX_KEY_LEN);\n        config.reset_token_keylen = qmcf->stateless_reset_token_key.len;\n    }\n\n    if (qmcf == NULL) {\n        ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, \n                    \"|xquic|ngx_xquic_engine_init: get main conf fail|\");\n        return NGX_ERROR;\n    }\n\n    if (qmcf->xquic_engine != NULL) {\n        return NGX_OK;\n    }\n\n    /* enable cid negotiate */\n#if (NGX_XQUIC_SUPPORT_CID_ROUTE)\n    if (ngx_xquic_is_cid_route_on(cycle)) {\n        config.cid_negotiate = 1;\n        config.cid_len       = qmcf->cid_len;\n        /* using time and pid as the seed for a new sequence of pseudo-random integer */\n        srandom(time(NULL) + getpid());\n    }\n#endif\n\n    /* init log level */\n    config.cfg_log_level = qmcf->log_level;\n    config.cfg_log_timestamp = 0;\n\n#if defined(T_NGX_XQUIC_SUPPORT_SENDMMSG)\n    /* set sendmmsg */\n    config.sendmmsg_on = 1;\n#endif\n\n    /* init ssl config */\n    engine_ssl_config = &(qmcf->engine_ssl_config);\n\n    if (qmcf->certificate.len == 0 || qmcf->certificate.data == NULL\n        || qmcf->certificate_key.len == 0 || qmcf->certificate_key.data == NULL)\n    {\n        ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, \n                    \"|xquic|ngx_xquic_engine_init: null certificate or key|\");     \n        return NGX_ERROR;\n    }\n\n    /* copy cert key */\n    engine_ssl_config->private_key_file = ngx_pcalloc(cycle->pool, qmcf->certificate_key.len + 1);\n    if (engine_ssl_config->private_key_file == NULL) {\n        ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, \n                    \"|xquic|ngx_xquic_engine_init: fail to alloc memory|\");     \n        return NGX_ERROR;\n    }\n    ngx_memcpy(engine_ssl_config->private_key_file, qmcf->certificate_key.data, qmcf->certificate_key.len);\n\n    /* copy cert */\n    engine_ssl_config->cert_file = ngx_pcalloc(cycle->pool, qmcf->certificate.len + 1);\n    if (engine_ssl_config->cert_file == NULL) {\n        ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, \n                    \"|xquic|ngx_xquic_engine_init: fail to alloc memory|\");     \n        return NGX_ERROR;\n    }\n    ngx_memcpy(engine_ssl_config->cert_file, qmcf->certificate.data, qmcf->certificate.len);\n\n    engine_ssl_config->ciphers = XQC_TLS_CIPHERS;\n    engine_ssl_config->groups = XQC_TLS_GROUPS;\n\n    /* copy session ticket */\n    char g_ticket_file[NGX_XQUIC_TMP_BUF_LEN]={0};\n    char g_session_ticket_key[NGX_XQUIC_TMP_BUF_LEN];    \n    if (qmcf->session_ticket_key.data != NULL \n        && qmcf->session_ticket_key.len != 0\n        && qmcf->session_ticket_key.len < NGX_XQUIC_TMP_BUF_LEN) \n    {\n        ngx_memcpy(g_ticket_file, qmcf->session_ticket_key.data, qmcf->session_ticket_key.len);\n\n        int ticket_key_len  = ngx_xquic_read_file_data(g_session_ticket_key, \n                                                       sizeof(g_session_ticket_key), \n                                                       g_ticket_file);\n\n        ngx_log_error(NGX_LOG_DEBUG, cycle->log, 0, \n                      \"|xquic|ngx_xquic_engine_init: ticket_key_len=%i|\", ticket_key_len); \n\n        if(ticket_key_len < 0){\n            engine_ssl_config->session_ticket_key_data = NULL;\n            engine_ssl_config->session_ticket_key_len = 0;\n        } else {\n            engine_ssl_config->session_ticket_key_data = ngx_pcalloc(cycle->pool, (size_t)ticket_key_len);\n            if (engine_ssl_config->session_ticket_key_data == NULL) {\n                ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, \n                            \"|xquic|ngx_xquic_engine_init: fail to alloc memory|\");     \n                return NGX_ERROR;\n            }\n            engine_ssl_config->session_ticket_key_len = ticket_key_len;\n            ngx_memcpy(engine_ssl_config->session_ticket_key_data, g_session_ticket_key, ticket_key_len);\n        }\n    }\n\n    /* create engine */\n    qmcf->xquic_engine = xqc_engine_create(XQC_ENGINE_SERVER, &config, engine_ssl_config, \n                                           &ngx_xquic_engine_callback, &ngx_xquic_transport_callbacks, qmcf);\n    if (qmcf->xquic_engine == NULL) {\n        ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, \n                    \"|xquic|xqc_engine_create: fail|\");\n        return NGX_ERROR;\n    }\n\n    /* init http3 alpn context */\n    if (ngx_xquic_engine_init_alpn_ctx(cycle, qmcf->xquic_engine) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    /* set congestion control */\n    xqc_cong_ctrl_callback_t cong_ctrl;\n    if (qmcf->congestion_control.len == sizeof(\"bbr\")-1\n        && ngx_strncmp(qmcf->congestion_control.data, \"bbr\", sizeof(\"bbr\")-1) == 0) \n    {\n        cong_ctrl = xqc_bbr_cb;\n    } else if (qmcf->congestion_control.len == sizeof(\"reno\")-1\n        && ngx_strncmp(qmcf->congestion_control.data, \"reno\", sizeof(\"reno\")-1) == 0) \n    {\n        cong_ctrl = xqc_reno_cb;\n    } else if (qmcf->congestion_control.len == sizeof(\"cubic\")-1\n        && ngx_strncmp(qmcf->congestion_control.data, \"cubic\", sizeof(\"cubic\")-1) == 0) \n    {\n        cong_ctrl = xqc_cubic_cb;\n    } else {\n        ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, \n                    \"|xquic|unknown xquic_congestion_control|%V|\", &qmcf->congestion_control);     \n        return NGX_ERROR;\n    }\n\n    int pacing_on = (qmcf->pacing_on? 1 : 0);\n\n    xqc_conn_settings_t conn_settings = {\n        .pacing_on  =   pacing_on,\n        .cong_ctrl_callback = cong_ctrl,\n    };\n\n    if (qmcf->anti_amplification_limit != NGX_CONF_UNSET_UINT) {\n        conn_settings.anti_amplification_limit = qmcf->anti_amplification_limit;\n    }\n\n    if (qmcf->keyupdate_pkt_threshold != NGX_CONF_UNSET_UINT) {\n        conn_settings.keyupdate_pkt_threshold = qmcf->keyupdate_pkt_threshold;\n    }\n\n    xqc_server_set_conn_settings(&conn_settings);\n\n    xqc_h3_engine_set_enc_max_dtable_capacity(qmcf->xquic_engine, \n                                        qmcf->qpack_encoder_dynamic_table_capacity);\n    xqc_h3_engine_set_dec_max_dtable_capacity(qmcf->xquic_engine, \n                                        qmcf->qpack_decoder_dynamic_table_capacity);\n\n\n    /* init event timer */\n    ngx_xquic_engine_init_event_timer(qmcf, qmcf->xquic_engine);\n\n    ngx_log_error(NGX_LOG_DEBUG, cycle->log, 0, \n                \"|xquic|xquic_engine|%p|\", qmcf->xquic_engine);  \n\n\n    return NGX_OK;\n}\n\n#if (T_NGX_UDPV2)\n\nstatic void\nngx_xquic_batch_udp_traffic(ngx_event_t * ev)\n{\n    xqc_engine_t *xquic_engine;\n    xquic_engine = (xqc_engine_t *)(ev->data);\n    xqc_engine_finish_recv(xquic_engine);\n    ngx_accept_disabled = ngx_cycle->connection_n / 8\n                              - ngx_cycle->free_connection_n;\n}\n\nstatic ngx_udpv2_traffic_filter_retcode\nngx_xquic_udp_accept_filter(ngx_listening_t *ls, const ngx_udpv2_packet_t *upkt){\n\n    ngx_connection_t *lc;\n    ngx_http_xquic_main_conf_t *qmcf;\n\n    lc      = ls->connection ;\n    qmcf    = (ngx_http_xquic_main_conf_t *)(lc->data);\n\n    // feed to xquic\n    ngx_xquic_dispatcher_process(lc, upkt);\n    // posted udpv2 event \n    ngx_post_event(&qmcf->udpv2_batch, &ngx_udpv2_posted_event);\n\n    return NGX_UDPV2_DONE;\n}\n#endif\n\nngx_int_t\nngx_xquic_process_init(ngx_cycle_t *cycle)\n{\n    int                          with_xquic = 0;\n    ngx_http_xquic_main_conf_t  *qmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_xquic_module);\n\n\n    ngx_log_debug0(NGX_LOG_DEBUG_CORE, cycle->log, 0, \"|xquic|ngx_xquic_process_init|\");\n\n    /* socket init */\n    ngx_event_t       *rev;\n    ngx_listening_t   *ls;\n    ngx_connection_t  *c;\n    unsigned int       i;\n\n    ls = (ngx_listening_t *)(cycle->listening.elts);\n    for (i = 0; i < cycle->listening.nelts; i++) {\n\n#if !(T_RELOAD)\n#if (NGX_HAVE_REUSEPORT)\n        if (ls[i].reuseport && ls[i].worker != ngx_worker) {\n            continue;\n        }\n#endif\n#endif\n\n        if (ls[i].fd == -1) {\n            continue;\n        }\n\n        if (!ls[i].xquic) {\n            continue;\n        }\n\n        with_xquic = 1;\n\n        c = ls[i].connection;\n        rev = c->read;\n\n#if (T_NGX_UDPV2)\n        /* outofband */\n        if (ls[i].support_udpv2) {\n            ngx_udpv2_reset_dispatch_filter(&ls[i]);\n            ngx_udpv2_push_dispatch_filter(cycle, &ls[i], ngx_xquic_udp_accept_filter);\n        }\n#endif\n\n        rev->handler = ngx_xquic_event_recv;\n        c->data = qmcf;\n        if (c->data == NULL) {\n            ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, \n                        \"|xquic|ngx_xquic_process_init|qmcf equals NULL|\");\n            return NGX_ERROR;  \n        }\n    }\n\n    /* socket init end */\n    if (with_xquic && ngx_xquic_engine_init(cycle) != NGX_OK) {\n        ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, \n                    \"|xquic|ngx_xquic_process_init|engine_init fail|\");\n        return NGX_ERROR;\n    }\n\n    if (with_xquic && ngx_xquic_intercom_init(cycle, qmcf->xquic_engine) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nvoid\nngx_xquic_process_exit(ngx_cycle_t *cycle)\n{\n    ngx_http_xquic_main_conf_t  *qmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_xquic_module);\n\n    ngx_log_debug0(NGX_LOG_DEBUG_CORE, cycle->log, 0, \"|xquic|ngx_xquic_process_exit|\");\n\n    if (qmcf->xquic_engine) {\n        xqc_engine_destroy(qmcf->xquic_engine);\n        qmcf->xquic_engine = NULL;\n\n        ngx_xquic_intercom_exit();\n    }\n}\n\n\nvoid \nngx_xquic_log_write_err(xqc_log_level_t lvl, const void *buf, size_t size, void *engine_user_data)\n{\n    ngx_log_error(NGX_LOG_WARN, ngx_cycle->log, 0, \"|xquic|lib%*s|\", size, buf);\n}\n\n\nvoid \nngx_xquic_log_write_stat(xqc_log_level_t lvl, const void *buf, size_t size, void *engine_user_data)\n{\n    ngx_log_xquic(NGX_LOG_WARN, ngx_cycle->x_log, 0, \"%*s|\", size, buf);\n}\n\n\n#if (NGX_XQUIC_SUPPORT_CID_ROUTE)\n\nngx_int_t\nngx_xquic_is_cid_route_on(ngx_cycle_t *cycle)\n{\n    ngx_http_xquic_main_conf_t *qmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_xquic_module);\n    return !!qmcf->cid_route;\n}\n\nngx_inline ngx_int_t\nngx_xquic_init_cid_route(ngx_cycle_t *cycle, ngx_http_xquic_main_conf_t *qmcf)\n{\n    ngx_cycle_t                 *old_cycle;\n    ngx_http_xquic_main_conf_t  *old_qmcf;\n\n    if (!qmcf) {\n        return NGX_ERROR;\n    }\n\n    old_cycle = cycle->old_cycle;\n    old_qmcf  = (old_cycle && !ngx_is_init_cycle(old_cycle)) ? ngx_http_cycle_get_module_main_conf(old_cycle, ngx_http_xquic_module) : NULL;\n\n    /* set salt range */\n    qmcf->cid_worker_id_salt_range  = qmcf->cid_worker_id_offset;\n\n    /* keep the same cid_worker_id_secret for the tengine reload */\n    if (old_qmcf) {\n        /* use same cid_worker_id_secret */\n        qmcf->cid_worker_id_secret  = old_qmcf->cid_worker_id_secret;\n    }else {\n        srandom(time(NULL));\n        /* generate security stuff */\n        qmcf->cid_worker_id_secret  = random();\n    }\n\n    return NGX_OK;\n}\n\nngx_int_t\nngx_xquic_enable_cid_route(ngx_cycle_t *cycle)\n{\n    ngx_http_xquic_main_conf_t *qmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_xquic_module);\n    if (!qmcf) {\n        return NGX_ERROR;\n    }\n\n    if (qmcf->cid_route == NGX_CONF_UNSET) {\n        /* need init xquic module first */\n        return NGX_ERROR;\n    }else if (qmcf->cid_route) {\n        /* already enable */\n        return NGX_OK;\n    }\n\n    qmcf->cid_route = 1;\n    return ngx_xquic_init_cid_route(cycle, qmcf);\n}\n\nssize_t\nngx_xquic_cid_generate_cb(const xqc_cid_t *ori_cid, uint8_t *cid_buf, size_t cid_buflen, void *engine_user_data)\n{\n    (void) engine_user_data;\n\n    size_t   current_cid_buflen;\n    const uint8_t *current_cid_buf;\n\n    current_cid_buf     = NULL;\n    current_cid_buflen  = 0;\n\n    if (ori_cid) {\n        current_cid_buf = ori_cid->cid_buf;\n        current_cid_buflen = ori_cid->cid_len;\n    }\n\n    return ngx_xquic_generate_route_cid(cid_buf, cid_buflen, current_cid_buf, current_cid_buflen);\n}\n\nuint32_t\nngx_xquic_cid_length(ngx_cycle_t *cycle)\n{\n    ngx_http_xquic_main_conf_t *qmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_xquic_module);\n    return qmcf->cid_len;\n}\n\nuint32_t\nngx_xquic_cid_worker_id_salt_range(ngx_cycle_t *cycle)\n{\n    ngx_http_xquic_main_conf_t *qmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_xquic_module);\n    return qmcf->cid_worker_id_salt_range;\n}\n\nuint32_t\nngx_xquic_cid_worker_id_offset(ngx_cycle_t *cycle)\n{\n    ngx_http_xquic_main_conf_t *qmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_xquic_module);\n    return qmcf->cid_worker_id_offset;\n}\n\nuint32_t\nngx_xquic_cid_worker_id_secret(ngx_cycle_t *cycle)\n{\n    ngx_http_xquic_main_conf_t *qmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_xquic_module);\n    return qmcf->cid_worker_id_secret;\n}\n\nstatic inline void\nngx_xquic_random_buf(unsigned char *p, size_t len)\n{\n    uint32_t r;\n    while (len > sizeof(r)) {\n       r = random();\n       ngx_memcpy(p, &r, sizeof(r));\n       p   += sizeof(r);\n       len -= sizeof(r);\n    }\n    if (len > 0) {\n        r = random();\n        ngx_memcpy(p, &r, len);\n    }\n}\n\nssize_t\nngx_xquic_generate_route_cid(unsigned char *buf, size_t len, const uint8_t *current_cid_buf, size_t current_cid_buflen)\n{\n    ngx_core_conf_t *ccf;\n    ngx_http_xquic_main_conf_t  *qmcf;\n    uint32_t worker, salt;\n    int32_t delta;\n\n    qmcf = ngx_http_cycle_get_module_main_conf(ngx_cycle, ngx_http_xquic_module);\n    ccf = (ngx_core_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx, ngx_core_module);\n\n    if (XQC_UNLIKELY(len < qmcf->cid_len)) {\n        /**\n        * just return 0 to force xquic generate random cid\n        * Notes: broke the DCID spec\n        */\n        ngx_log_error(NGX_LOG_WARN, ngx_cycle->log, 0, \"|xquic|dismatch cid length %d (required %d)|\", len, qmcf->cid_len);\n        return 0;\n    }\n\n    /* fill with random data */\n    ngx_xquic_random_buf(buf, qmcf->cid_len);\n\n    /* calculate salt */\n    salt = ngx_murmur_hash2(buf, qmcf->cid_worker_id_salt_range);\n\n    /**\n     * required :\n     * 1. pid < 2 ^ 22  (SYSTEM LIMITED)\n     * 2. ngx_worker < 1024\n     * */\n\n    delta = ngx_worker - salt % ccf->worker_processes;\n    if (delta < 0) {\n        delta += ccf->worker_processes;\n    }\n\n#define PID_MAX_BIT 22\n    /* set worker delta */\n    worker = delta << PID_MAX_BIT;\n    /* set PID */\n    worker = worker | getpid();\n    /* encrypt worker */\n    worker = htonl( (worker + salt) ^ qmcf->cid_worker_id_secret);\n    /* set worker id */\n    ngx_memcpy(buf + qmcf->cid_worker_id_offset, &worker, sizeof(worker));\n    return qmcf->cid_len;\n}\n\n#ifdef UINT64_MAX\nstatic ngx_inline uint32_t\nngx_sum_complement(uint64_t a, uint64_t b, uint32_t c)\n{\n    return (a + b) % c;\n}\n#endif\n\nngx_int_t\nngx_xquic_get_target_worker_from_cid(ngx_xquic_recv_packet_t *packet)\n{\n    ngx_core_conf_t             *ccf;\n    ngx_http_xquic_main_conf_t  *qmcf;\n    uint32_t                     worker, salt;\n    u_char                      *dcid;\n\n    ccf  = (ngx_core_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx, ngx_core_module);\n    qmcf = ngx_http_cycle_get_module_main_conf(ngx_cycle, ngx_http_xquic_module);\n    dcid = packet->xquic.dcid.cid_buf;\n\n    if (packet->xquic.dcid.cid_len >= qmcf->cid_len) {\n        /* calculate salt */\n        salt = ngx_murmur_hash2(dcid, qmcf->cid_worker_id_salt_range);\n        /* get cipher worker */\n        memcpy(&worker, dcid + qmcf->cid_worker_id_offset, sizeof(worker));\n        /* decrypt */\n        worker = (ntohl(worker) ^ qmcf->cid_worker_id_secret) - salt;\n\n#ifdef UINT64_MAX\n        return ngx_sum_complement(worker >> PID_MAX_BIT, salt, ccf->worker_processes);\n#else\n        /**\n         * For the mathematics, ((worker >> PID_MAX_BIT) + salt) % ccf->worker_processes is better\n         * (worker >> PID_MAX_BIT) + salt may overflow in practice\n         * */\n        return ((worker >> PID_MAX_BIT) % ccf->worker_processes + salt % ccf->worker_processes)\n                % ccf->worker_processes;\n#endif\n\n#undef PID_MAX_BIT\n    }\n\n    return ngx_murmur_hash2(dcid, packet->xquic.dcid.cid_len) % ccf->worker_processes;\n}\n#endif\n"
  },
  {
    "path": "modules/ngx_http_xquic_module/ngx_xquic.h",
    "content": "/*\n * Copyright (C) 2020-2023 Alibaba Group Holding Limited\n */\n\n#ifndef _T_NGX_XQUIC_H_INCLUDED_\n#define _T_NGX_XQUIC_H_INCLUDED_\n\n\n#include <ngx_core.h>\n#include <ngx_config.h>\n#include <ngx_http.h>\n#include <ngx_http_v3_stream.h>\n#include <ngx_xquic_send.h>\n\n#include <xquic/xquic_typedef.h>\n#include <xquic/xquic.h>\n\n#define NGX_HTTP_V3_INT_OCTETS           4\n#define NGX_HTTP_V3_MAX_FIELD                                                 \\\n    (127 + (1 << (NGX_HTTP_V3_INT_OCTETS - 1) * 7) - 1)\n\n#define NGX_XQUIC_SUPPORT_CID_ROUTE 1\n\nint ngx_xquic_conn_accept(xqc_engine_t *engine, xqc_connection_t *conn, \n    const xqc_cid_t * cid, void * user_data);\nvoid ngx_xquic_conn_refuse(xqc_engine_t *engine, xqc_connection_t *conn, \n    const xqc_cid_t *cid, void *user_data);\n\nint ngx_http_v3_conn_create_notify(xqc_h3_conn_t *h3_conn, const xqc_cid_t *cid, void *user_data);\nint ngx_http_v3_conn_close_notify(xqc_h3_conn_t *h3_conn, const xqc_cid_t *cid, void *user_data);\nvoid ngx_http_v3_conn_handshake_finished(xqc_h3_conn_t *h3_conn, void *user_data);\nvoid ngx_http_v3_conn_update_cid_notify(xqc_connection_t *conn, const xqc_cid_t *retire_cid,\n    const xqc_cid_t *new_cid, void *conn_user_data);\n\nvoid ngx_xquic_engine_set_event_timer(xqc_msec_t wake_after, void *user_data);\n\nvoid ngx_xquic_log_write_err(xqc_log_level_t lvl, const void *buf, size_t size, void *engine_user_data);\nvoid ngx_xquic_log_write_stat(xqc_log_level_t lvl, const void *buf, size_t size, void *engine_user_data);\n\nngx_int_t ngx_xquic_process_init(ngx_cycle_t *cycle);\nvoid ngx_xquic_process_exit(ngx_cycle_t *cycle);\nngx_int_t ngx_xquic_engine_init(ngx_cycle_t *cycle);\n\nuint64_t ngx_xquic_get_time();\n\n#if (NGX_XQUIC_SUPPORT_CID_ROUTE)\n\nssize_t\nngx_xquic_cid_generate_cb(const xqc_cid_t *ori_cid, uint8_t *cid_buf, size_t cid_buflen, void *engine_user_data);\n\n/* worker ID is 4 bytes */\n#define NGX_QUIC_CID_ROUTE_WORKER_ID_LENGTH         (4)\n\n/**\n * @return CID length based on negotiation result\n * */\nuint32_t    ngx_xquic_cid_length(ngx_cycle_t *cycle);\n\n/**\n * enable CID router by other mod \n * @return  NGX_OK for success, other for failed\n * */\nngx_int_t   ngx_xquic_enable_cid_route(ngx_cycle_t *cycle);\n\n/**\n * return 1 on cid route on, other for off\n * */\nngx_int_t   ngx_xquic_is_cid_route_on(ngx_cycle_t *cycle);\n\n/**\n * offset of worker ID in the CID\n * */\nuint32_t    ngx_xquic_cid_worker_id_offset(ngx_cycle_t *cycle);\n\n/**\n * process secret key of the worker ID\n * */\nuint32_t    ngx_xquic_cid_worker_id_secret(ngx_cycle_t *cycle);\n\n/**\n * process salt range of the worker ID\n * */\nuint32_t    ngx_xquic_cid_worker_id_salt_range(ngx_cycle_t *cycle);\n\n#endif\n\n#endif /* _T_NGX_XQUIC_H_INCLUDED_ */\n\n"
  },
  {
    "path": "modules/ngx_http_xquic_module/ngx_xquic_intercom.c",
    "content": "/*\n * Copyright (C) 2020-2023 Alibaba Group Holding Limited\n */\n\n#include <ngx_core.h>\n#include <ngx_http.h>\n#include <ngx_xquic_intercom.h>\n\ntypedef struct {\n    ngx_pool_t           *pool;\n\n    ngx_connection_t     *connection;\n\n    struct sockaddr_un   *addr;\n    ngx_int_t            *addrlen;\n\n    ngx_log_t            *log;\n\n    xqc_engine_t         *xquic_engine;\n} ngx_xquic_intercom_ctx_t;\n\nstatic ngx_int_t ngx_xquic_intercom_create_socket(ngx_xquic_intercom_ctx_t *ctx, const char *path);\nstatic void ngx_xquic_intercom_recv_handler(ngx_event_t *rev);\n\nstatic uint64_t ngx_xquic_stat_send_cnt = 0;\nstatic uint64_t ngx_xquic_stat_recv_cnt = 0;\nstatic uint64_t ngx_xquic_stat_send_eagain_cnt = 0;\n\nstatic ngx_xquic_intercom_ctx_t *ctx = NULL;\n\nngx_int_t\nngx_xquic_intercom_init(ngx_cycle_t *cycle, void *engine)\n{\n    u_char                       path[4096] = { 0 };\n    ngx_int_t                    i;\n    ngx_pool_t                  *pool;\n    ngx_core_conf_t             *ccf;\n    ngx_http_xquic_main_conf_t  *qmcf;\n\n    ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);\n \n    if (ccf->worker_processes <= 1 || ngx_process != NGX_PROCESS_WORKER) {\n        return NGX_OK;\n    }\n\n    qmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_xquic_module);\n\n    pool = ngx_create_pool(qmcf->intercom_pool_size, cycle->log);\n    if (pool == NULL) {\n        ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,\n                      \"|xquic|ngx_xquic_intercom_init: create pool size %d failed|\",\n                      qmcf->intercom_pool_size);\n        return NGX_ERROR;\n    }\n\n    ctx = ngx_pcalloc(pool, sizeof(ngx_xquic_intercom_ctx_t));\n    if (ctx == NULL) {\n        ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,\n                      \"|xquic|ngx_xquic_intercom_init: create ctx failed|\");\n        return NGX_ERROR;\n    }\n\n    ctx->pool = pool;\n    ctx->log = cycle->log;\n    ctx->xquic_engine = engine;\n\n    ctx->addr = ngx_pcalloc(pool, ccf->worker_processes * sizeof(struct sockaddr_un));\n    if (ctx->addr == NULL) {\n        ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,\n                      \"ngx_xquic_intercom_init: alloc addr failed\");\n        return NGX_ERROR;\n    }\n\n    ctx->addrlen = ngx_pcalloc(pool, ccf->worker_processes * sizeof(ngx_int_t));\n    if (ctx->addrlen == NULL) {\n        ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,\n                      \"ngx_xquic_intercom_init: alloc addrlen failed\");\n        return NGX_ERROR;\n    }\n\n    *ngx_snprintf(path, sizeof(path), \"%V\", &qmcf->intercom_socket_path) = 0;\n\n    if (ngx_create_full_path(path, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)) {\n        ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,\n                      \"ngx_xquic_intercom_init: failed to create path: %s\", (char *) path);\n\n        return NGX_ERROR;\n    }\n\n    for (i = 0; i < ccf->worker_processes; i++) {\n        *ngx_snprintf(path, sizeof(path), \"%V#%uD\", &qmcf->intercom_socket_path, i) = 0;\n\n        if ((ngx_uint_t) i == ngx_worker) {\n            if (ngx_xquic_intercom_create_socket(ctx, (const char *) path) != NGX_OK) {\n                return NGX_ERROR;\n            }\n        } else {\n            ctx->addr[i].sun_family = AF_UNIX;\n            ngx_memcpy(ctx->addr[i].sun_path, path, strlen((const char *)path));\n            ctx->addrlen[i] = strlen((const char *) path) + sizeof(ctx->addr[i].sun_family);\n        }\n    }\n\n    return NGX_OK;\n}\n\nvoid\nngx_xquic_intercom_exit()\n{\n    if (ctx) {\n        ngx_close_connection(ctx->connection);\n        ngx_destroy_pool(ctx->pool);\n    }   \n}\n\nstatic ngx_int_t\nngx_xquic_intercom_create_socket(ngx_xquic_intercom_ctx_t *ctx, const char *path)\n{\n    socklen_t            addr_len;\n    ngx_event_t         *rev;\n    ngx_socket_t         s;\n    ngx_connection_t    *c;\n    struct sockaddr_un   addr;\n\n    unlink((const char *) path);\n\n    s = socket(AF_UNIX, SOCK_DGRAM, 0);\n    if (s == (ngx_socket_t) -1) {\n        ngx_log_error(NGX_LOG_EMERG, ctx->log, ngx_socket_errno,\n                      \"|xquic|intercom create unix domain socket failed|\");\n        return NGX_ERROR;\n    }\n\n    c = ngx_get_connection(s, ctx->log);\n    if (c == NULL) {\n        if (ngx_close_socket(s) == -1) {\n            ngx_log_error(NGX_LOG_EMERG, ctx->log, ngx_socket_errno,\n                          \"|xquic|intercom close socket failed|\");\n        }\n\n        return NGX_ERROR;\n    }\n\n    ngx_memzero(&addr, sizeof(addr));\n\n    addr.sun_family = AF_UNIX;\n    ngx_memcpy(addr.sun_path, path, strlen((const char *) path));\n\n    addr_len = strlen((const char *) path) + sizeof(addr.sun_family);\n\n    if (ngx_nonblocking(s) == -1) {\n        ngx_log_error(NGX_LOG_EMERG, ctx->log, ngx_socket_errno,\n                      \"|xquic|intercom set nonblocking failed|\");\n        goto failed;\n    }\n\n    if (bind(s, (struct sockaddr *) &addr, addr_len) == -1) {\n        ngx_log_error(NGX_LOG_EMERG, ctx->log, ngx_socket_errno,\n                      \"|xquic|intercom bind() to %s failed|\", path);\n\n        goto failed;\n    }\n\n    c->pool = ctx->pool;\n    c->log = ctx->log;\n    c->data = ctx;\n\n    rev = c->read;\n    rev->log = ctx->log;\n    rev->data = c;\n    rev->handler = ngx_xquic_intercom_recv_handler;\n\n    if (ngx_add_event(rev, NGX_READ_EVENT, 0) != NGX_OK) {\n        ngx_log_error(NGX_LOG_EMERG, ctx->log, 0,\n                      \"|xquic|intercom add read event failed|\");\n        goto failed;\n    }\n\n    ctx->connection = c;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->log, 0,\n                   \"|xquic|intercom create socket %s|\", path);\n\n    return NGX_OK;\n\nfailed:\n    ngx_close_connection(c);\n    return NGX_ERROR;\n}\n\n\nstatic void\nngx_xquic_intercom_recv_handler(ngx_event_t *rev)\n{\n    ngx_int_t                n;\n    ngx_err_t                err;\n    ngx_connection_t        *c;\n    ngx_xquic_recv_packet_t   packet;\n    ngx_http_xquic_main_conf_t  *qmcf = ngx_http_cycle_get_module_main_conf(ngx_cycle, ngx_http_xquic_module);\n\n\n    c = rev->data;\n\n    for ( ;; ) {\n        n = recv(c->fd, (void *) &packet, sizeof(ngx_xquic_recv_packet_t), 0);\n\n        if (n < 0) {\n            err = ngx_socket_errno;\n\n            if (err == NGX_EAGAIN || err == NGX_EINTR) {\n                ngx_log_debug0(NGX_LOG_DEBUG_EVENT, rev->log, 0,\n                               \"|xquic|ngx_quic_intercom_recv_handler: recv() not ready|\");\n            } else {\n                ngx_log_error(NGX_LOG_ERR, rev->log, ngx_socket_errno,\n                              \"|xquic|ngx_quic_intercom_recv_handler: recv() failed|\");\n            }\n\n            goto finish_recv;\n        }\n\n        ngx_log_debug2(NGX_LOG_DEBUG_EVENT, rev->log, 0,\n                       \"|xquic|ngx_quic_intercom_recv_handler: worker %d recv connection_id %s packet|\",\n                       ngx_worker, xqc_dcid_str(&packet.xquic.dcid));\n\n        if (n != sizeof(ngx_xquic_recv_packet_t)) {\n            ngx_log_error(NGX_LOG_ERR, rev->log, ngx_socket_errno,\n                          \"|xquic|ngx_quic_intercom_recv_handler: worker %d recv connection_id %s packet error %d|\",\n                          ngx_worker, xqc_dcid_str(&packet.xquic.dcid), n);\n        } else {\n            ngx_xquic_stat_recv_cnt++;\n            ngx_xquic_recv_from_intercom(&packet);\n        }\n    }\n\nfinish_recv:\n    xqc_engine_finish_recv(qmcf->xquic_engine);\n}\n\nvoid\nngx_xquic_intercom_send(ngx_int_t worker_num, ngx_xquic_recv_packet_t *packet)\n{\n    ngx_int_t           n;\n    ngx_err_t           err;\n    ngx_connection_t   *c;\n\n    c = ctx->connection;\n\n    n = sendto(c->fd, packet, sizeof(ngx_xquic_recv_packet_t), 0,\n               &ctx->addr[worker_num], ctx->addrlen[worker_num]);\n\n    ngx_xquic_stat_send_cnt++;\n\n    ngx_log_debug6(NGX_LOG_DEBUG_EVENT, ctx->log, 0,\n                   \"|xquic|intercom_send: worker %d -> %d send connection_id %s packet, \"\n                   \"recv(%ul), send(%ul), eagain(%ul)|\",\n                   ngx_worker, worker_num, xqc_dcid_str(&packet->xquic.dcid),\n                   ngx_xquic_stat_recv_cnt, ngx_xquic_stat_send_cnt, ngx_xquic_stat_send_eagain_cnt);\n\n    if (n < 0) {\n        err = ngx_socket_errno;\n\n        if (err == NGX_EAGAIN || err == NGX_EINTR) {\n            ngx_xquic_stat_send_eagain_cnt++;\n            if (ngx_xquic_stat_send_eagain_cnt % 100 == 1) {\n                ngx_log_error(NGX_LOG_ERR, ctx->log, ngx_socket_errno,\n                             \"|xquic|ngx_xquic_intercom_send: worker %d -> %d send packet error, \"\n                             \"recv(%ul), send(%ul), eagain(%ul)|\",\n                             ngx_worker, worker_num,\n                             ngx_xquic_stat_recv_cnt, ngx_xquic_stat_send_cnt, ngx_xquic_stat_send_eagain_cnt);\n            }\n        } else {\n            ngx_log_error(NGX_LOG_ERR, ctx->log, ngx_socket_errno,\n                          \"|xquic|ngx_xquic_intercom_send: worker %d -> %d send connection_id %ul packet error|\",\n                          ngx_worker, worker_num, packet->xquic.connection_id);\n        }\n\n        return;\n    }\n\n    if (n != sizeof(ngx_xquic_recv_packet_t)) {\n        ngx_log_error(NGX_LOG_ERR, ctx->log, ngx_socket_errno,\n                      \"|xquic|ngx_xquic_intercom_send: worker %d -> %d send connection_id %ul packet uncomplete(%d != %d)|\",\n                      ngx_worker, worker_num, packet->xquic.connection_id, n, sizeof(ngx_xquic_recv_packet_t));\n    }\n}\n\n\nngx_int_t\nngx_xquic_intercom_packet_hash(ngx_xquic_recv_packet_t *packet)\n{\n    ngx_core_conf_t *ccf;\n    ngx_int_t target_worker;\n\n    if (ctx == NULL) {\n        return ngx_worker;\n    }\n\n#if (NGX_XQUIC_SUPPORT_CID_ROUTE)\n    if (ngx_xquic_is_cid_route_on((ngx_cycle_t *) ngx_cycle)) {\n        return ngx_xquic_get_target_worker_from_cid(packet);\n    }\n#endif\n\n    ccf = (ngx_core_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx, ngx_core_module);\n    target_worker = ngx_murmur_hash2(packet->xquic.dcid.cid_buf, packet->xquic.dcid.cid_len)\n                                                % ccf->worker_processes;\n\n\n    return target_worker;\n}\n\n"
  },
  {
    "path": "modules/ngx_http_xquic_module/ngx_xquic_intercom.h",
    "content": "/*\n * Copyright (C) 2020-2023 Alibaba Group Holding Limited\n */\n\n#ifndef _T_NGX_XQUIC_INTERCOM_INCLUDED_H_\n#define _T_NGX_XQUIC_INTERCOM_INCLUDED_H_\n\n#include <ngx_xquic_recv.h>\n#include <ngx_http_xquic_module.h>\n\n\nngx_int_t ngx_xquic_intercom_init(ngx_cycle_t *cycle, void *engine);\nvoid ngx_xquic_intercom_exit();\n\nvoid ngx_xquic_intercom_send(ngx_int_t worker_num, ngx_xquic_recv_packet_t *packet);\n\nngx_int_t ngx_xquic_intercom_packet_hash(ngx_xquic_recv_packet_t *packet);\n\n\n#endif /* _T_NGX_XQUIC_INTERCOM_INCLUDED_H_ */\n\n"
  },
  {
    "path": "modules/ngx_http_xquic_module/ngx_xquic_recv.c",
    "content": "/*\n * Copyright (C) 2020-2023 Alibaba Group Holding Limited\n */\n\n\n#include <ngx_xquic_recv.h>\n#include <ngx_xquic_intercom.h>\n#include <ngx_http_xquic_module.h>\n#include <ngx_xquic.h>\n\n#include <xquic/xquic.h>\n#include <xquic/xqc_errno.h>\n\n\nngx_inline void\nngx_xquic_packet_get_cid_raw(xqc_engine_t *engine, unsigned char *payload, size_t sz, \n    xqc_cid_t *dcid, xqc_cid_t *scid)\n{\n    uint8_t cid_len = xqc_engine_config_get_cid_len(engine);\n    ngx_int_t rc = xqc_packet_parse_cid(dcid, scid, cid_len, payload, sz);\n    if (rc != NGX_OK) {\n        ngx_log_error(NGX_LOG_WARN, ngx_cycle->log, 0,\n                    \"|xquic|xqc_packet_parse_cid err|%i|config_cid_len:%d|dcid_len:%d|scid_len:%d|pkt_sz:%d|\",\n                        rc, cid_len, dcid->cid_len, scid->cid_len, sz);\n    }\n}\n\n\nvoid\nngx_xquic_packet_get_cid(ngx_xquic_recv_packet_t *packet,\n    xqc_engine_t *engine)\n{\n    return ngx_xquic_packet_get_cid_raw(engine, (unsigned char *)packet->buf, packet->len, &packet->xquic.dcid, &packet->xquic.scid);\n}\n\nngx_int_t\nngx_xquic_recv(ngx_connection_t *c, 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 = recv(c->fd, buf, size, 0);\n\n        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"ngx_quic_recv: fd:%d %d of %d\", c->fd, n, size);\n\n        if (n >= 0) {\n            return n;\n        }\n\n        err = ngx_socket_errno;\n\n        if (err == NGX_EAGAIN || err == NGX_EINTR) {\n            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,\n                           \"ngx_quic_recv: recv() not ready\");\n            n = NGX_AGAIN;\n        } else if (err == NGX_ECONNREFUSED) {\n            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,\n                           \"ngx_quic_recv: recv() get icmp\");\n            n = NGX_DONE;\n        } else {\n            n = ngx_connection_error(c, err, \"quic recv() failed\");\n            break;\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\n\nngx_int_t\nngx_xquic_recv_packet(ngx_connection_t *c, \n    ngx_xquic_recv_packet_t *packet, ngx_log_t *log, xqc_engine_t *engine)\n{\n    ssize_t                          n;\n    struct iovec                     iov[1];\n    struct msghdr                    msg;\n    ngx_err_t                        err;\n\n#if (NGX_HAVE_MSGHDR_MSG_CONTROL)\n\n#if (NGX_HAVE_IP_PKTINFO)\n    u_char             msg_control[CMSG_SPACE(sizeof(struct in_pktinfo))];\n#elif (NGX_HAVE_IP_RECVDSTADDR)\n    u_char             msg_control[CMSG_SPACE(sizeof(struct in_addr))];\n#endif\n\n#if (NGX_HAVE_INET6 && NGX_HAVE_IPV6_RECVPKTINFO)\n    u_char             msg_control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];\n#endif\n\n#endif\n    \n    iov[0].iov_base = (void *) &packet->buf;\n    iov[0].iov_len = sizeof(packet->buf);\n\n    msg.msg_name = &packet->sockaddr;\n    msg.msg_namelen = NGX_SOCKADDRLEN;\n    msg.msg_iov = iov;\n    msg.msg_iovlen = 1;\n\n#if (NGX_HAVE_MSGHDR_MSG_CONTROL)\n\n#if (NGX_HAVE_IP_RECVDSTADDR || NGX_HAVE_IP_PKTINFO)\n    if (packet->local_sockaddr.sa_family == AF_INET) {\n        msg.msg_control = &msg_control;\n        msg.msg_controllen = sizeof(msg_control);\n    }\n#endif\n\n#if (NGX_HAVE_INET6 && NGX_HAVE_IPV6_RECVPKTINFO)\n    if (packet->local_sockaddr.sa_family == AF_INET6) {\n        msg.msg_control = &msg_control6;\n        msg.msg_controllen = sizeof(msg_control6);\n    }\n#endif\n\n#endif\n\n    do {\n        n = recvmsg(c->fd, &msg, 0);\n\n        if (n >= 0) {\n            break;\n        }\n\n        err = ngx_socket_errno;\n\n        if (err == NGX_EAGAIN || err == NGX_EINTR) {\n            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,\n                           \"ngx_quic_recv_packet: recvmsg() not ready\");\n            n = NGX_AGAIN;\n        } else if (err == NGX_ECONNREFUSED) {\n            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,\n                           \"ngx_quic_recv_packet: recvmsg() get icmp\");\n            n = NGX_DONE;\n        } else {\n            n = ngx_connection_error(c, err, \"quic recvmsg() failed\");\n            break;\n        }\n    } while (err == NGX_EINTR);\n\n    if (n < 0) {\n        return n;\n    }\n\n    packet->len = n;\n    packet->socklen = msg.msg_namelen;\n\n#if (NGX_HAVE_MSGHDR_MSG_CONTROL)\n\n    {\n        struct cmsghdr   *cmsg;\n        struct sockaddr  *sockaddr = &packet->local_sockaddr;\n        socklen_t        *socklen  = &packet->local_socklen;\n\n        for (cmsg = CMSG_FIRSTHDR(&msg);\n                cmsg != NULL;\n                cmsg = CMSG_NXTHDR(&msg, cmsg))\n        {\n\n#if (NGX_HAVE_IP_RECVDSTADDR)\n\n            if (cmsg->cmsg_level == IPPROTO_IP\n                    && cmsg->cmsg_type == IP_RECVDSTADDR\n                    && packet->local_sockaddr.sa_family == AF_INET)\n            {\n                struct in_addr      *addr;\n                struct sockaddr_in  *sin;\n\n                addr = (struct in_addr *) CMSG_DATA(cmsg);\n                sin = (struct sockaddr_in *) sockaddr;\n                sin->sin_family = AF_INET;\n                sin->sin_addr = *addr;\n                *socklen = sizeof(struct sockaddr_in);\n\n                break;\n            }\n\n#elif (NGX_HAVE_IP_PKTINFO)\n\n            if (cmsg->cmsg_level == IPPROTO_IP\n                    && cmsg->cmsg_type == IP_PKTINFO\n                    && packet->local_sockaddr.sa_family == AF_INET)\n            {\n                struct in_pktinfo   *pkt;\n                struct sockaddr_in  *sin;\n\n                pkt = (struct in_pktinfo *) CMSG_DATA(cmsg);\n                sin = (struct sockaddr_in *) sockaddr;\n                sin->sin_family = AF_INET;\n                sin->sin_addr = pkt->ipi_addr;\n                *socklen = sizeof(struct sockaddr_in);\n\n                break;\n            }\n\n#endif\n\n#if (NGX_HAVE_INET6 && NGX_HAVE_IPV6_RECVPKTINFO)\n\n            if (cmsg->cmsg_level == IPPROTO_IPV6\n                    && cmsg->cmsg_type == IPV6_PKTINFO\n                    && packet->local_sockaddr.sa_family == AF_INET6)\n            {\n                struct in6_pktinfo   *pkt6;\n                struct sockaddr_in6  *sin6;\n\n                pkt6 = (struct in6_pktinfo *) CMSG_DATA(cmsg);\n                sin6 = (struct sockaddr_in6 *) sockaddr;\n                sin6->sin6_family = AF_INET6;\n                sin6->sin6_addr = pkt6->ipi6_addr;\n                *socklen = sizeof(struct sockaddr_in6);\n\n                break;\n            }\n\n#endif\n\n        }\n    }\n\n#endif\n\n#if (NGX_DEBUG)\n    {\n        ngx_str_t caddr, saddr;\n        u_char    ctext[NGX_SOCKADDR_STRLEN];\n        u_char    stext[NGX_SOCKADDR_STRLEN];\n\n        if (log->log_level & NGX_LOG_DEBUG_EVENT) {\n            caddr.data = ctext;\n            caddr.len = ngx_sock_ntop(&packet->sockaddr, packet->socklen, ctext,\n                    NGX_SOCKADDR_STRLEN, 1);\n            saddr.data = stext;\n            saddr.len = ngx_sock_ntop(&packet->local_sockaddr, packet->local_socklen, stext,\n                    NGX_SOCKADDR_STRLEN, 1);\n\n            ngx_log_debug4(NGX_LOG_DEBUG_EVENT, log, 0,\n                    \"ngx_xquic_recv_packet: %V->%V fd:%d n:%z\",\n                    &caddr, &saddr, c->fd, n);\n        }\n\n    }\n#endif\n\n    /* get dcid here */\n    ngx_xquic_packet_get_cid(packet, engine);\n\n    return NGX_OK;\n}\n\n\n\nvoid\nngx_xquic_event_recv(ngx_event_t *ev)\n{\n    ngx_int_t                         rc;\n    ngx_listening_t                  *ls;\n    ngx_connection_t                 *lc;\n    ngx_event_conf_t                 *ecf;\n    static ngx_xquic_recv_packet_t    packet;\n    ngx_http_xquic_main_conf_t       *qmcf;\n\n    ecf = ngx_event_get_conf(ngx_cycle->conf_ctx, ngx_event_core_module);\n\n    if (ngx_event_flags & NGX_USE_RTSIG_EVENT) {\n        ev->available = 1;\n    } else if (!(ngx_event_flags & NGX_USE_KQUEUE_EVENT)) {\n        ev->available = ecf->multi_accept;\n    }\n\n    lc = ev->data;\n    ls = lc->listening;\n    ev->ready = 0;\n\n    qmcf = ngx_http_cycle_get_module_main_conf(ngx_cycle, ngx_http_xquic_module);\n\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,\n                   \"ngx_xquic_event_recv on %V, ready: %d\",\n                   &ls->addr_text, ev->available);\n\n    do {\n        packet.local_socklen = ls->socklen;\n        ngx_memcpy(&packet.local_sockaddr, ls->sockaddr, ls->socklen);\n\n        rc = ngx_xquic_recv_packet(lc, &packet, ev->log, qmcf->xquic_engine);\n        if (rc != NGX_OK) {\n            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ev->log, ngx_socket_errno,\n                           \"ngx_xquic_recv_packet: return rc=%i.\", rc);\n            goto finish_recv;\n        }\n\n#if (NGX_STAT_STUB)\n        (void) ngx_atomic_fetch_add(ngx_stat_accepted, 1);\n#endif\n\n        ngx_accept_disabled = ngx_cycle->connection_n / 8\n                              - ngx_cycle->free_connection_n;\n\n        ngx_xquic_dispatcher_process_packet(lc, &packet);\n\n        if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {\n            ev->available --;\n        }\n    } while (ev->available);\n\nfinish_recv:\n    xqc_engine_finish_recv(qmcf->xquic_engine);\n}\n\n\n\nvoid\nngx_xquic_dispatcher_process_packet(ngx_connection_t *c, ngx_xquic_recv_packet_t *packet)\n{\n\n    if (ngx_terminate || ngx_exiting) {\n        return;\n    }\n\n    if (c->data == NULL) {\n        ngx_log_error(NGX_LOG_WARN, c->log, 0,\n                      \"|xquic|ngx_xquic_dispatcher_process_packet: engine NULL|\");\n        return;\n    }\n\n    /* check QUIC magic bit */\n    if (!NGX_XQUIC_CHECK_MAGIC_BIT(packet->buf)) {\n        ngx_log_error(NGX_LOG_WARN, c->log, 0,\n                      \"|xquic|invalid packet head|\");\n        return;\n    }\n\n    /* check healthcheck */\n    if (packet->len >= sizeof(NGX_XQUIC_HEALTH_CHECK)\n        && ngx_strncmp(packet->buf, NGX_XQUIC_HEALTH_CHECK, sizeof(NGX_XQUIC_HEALTH_CHECK)-1) == 0) \n    {\n        ngx_log_debug(NGX_LOG_DEBUG, c->log, 0,\n                      \"|xquic|health check|\");\n        return;\n    }\n\n    /* check healthcheck, REQ/RSP mode */\n    if (packet->len == sizeof(NGX_XQUIC_HEALTH_CHECK_REQ)-1\n        && ngx_strncmp(packet->buf, NGX_XQUIC_HEALTH_CHECK_REQ, sizeof(NGX_XQUIC_HEALTH_CHECK_REQ)-1) == 0)\n    {\n        ngx_log_debug(NGX_LOG_DEBUG, c->log, 0,\n                      \"|xquic|health check, req/rsp mode|\");\n        sendto(c->fd, NGX_XQUIC_HEALTH_CHECK_RSP, sizeof(NGX_XQUIC_HEALTH_CHECK_RSP)-1, 0, &packet->sockaddr, packet->socklen);\n        return;\n    }\n    \n    ngx_http_xquic_main_conf_t  *qmcf = ngx_http_cycle_get_module_main_conf(ngx_cycle, ngx_http_xquic_module);\n\n#if (NGX_DEBUG)\n    {\n        ngx_str_t  addr, addr2;\n        u_char     text[NGX_SOCKADDR_STRLEN], text2[NGX_SOCKADDR_STRLEN];\n        if (c->log->log_level & NGX_LOG_DEBUG_EVENT) {\n            addr.data = text;\n            addr.len = ngx_sock_ntop(&packet->local_sockaddr, packet->local_socklen, text,\n                                     NGX_SOCKADDR_STRLEN, 1);\n            addr2.data = text2;\n            addr2.len = ngx_sock_ntop(&packet->sockaddr, packet->socklen, text2,\n                                     NGX_SOCKADDR_STRLEN, 1);\n\n            ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                           \"|xquic|ngx_xquic_dispatcher_process_packet: %V -> %V len:%d|\",\n                           &addr2, &addr, packet->len);\n        }\n    }\n#endif\n\n\n    ngx_int_t worker_num = ngx_xquic_intercom_packet_hash(packet);\n\n    ngx_log_error(NGX_LOG_DEBUG, ngx_cycle->log, 0,\n                    \"|xquic|packet_get_cid|dcid=%s|targetWorkerId=%i|ngx_worker=%ui|\", \n                    xqc_dcid_str(&packet->xquic.dcid), worker_num, ngx_worker);\n\n    if (ngx_worker != (ngx_uint_t) worker_num) {\n        ngx_xquic_intercom_send(worker_num, packet);\n        return;\n    }\n\n    uint64_t recv_time = ngx_xquic_get_time();\n    ngx_log_error(NGX_LOG_DEBUG, c->log, 0,\n                    \"|xquic|xqc_server_read_handler recv_size=%zd, recv_time=%llu|\", \n                    packet->len, recv_time);\n\n    if (xqc_engine_packet_process(qmcf->xquic_engine, (u_char *)packet->buf, packet->len,\n                                  (struct sockaddr *) &(packet->local_sockaddr), packet->local_socklen,\n                                  (struct sockaddr *) &(packet->sockaddr), packet->socklen, \n                                  (xqc_msec_t) recv_time, c) != 0) \n    {\n        ngx_log_error(NGX_LOG_DEBUG, c->log, 0,\n                    \"|xquic|xqc_server_read_handler: packet process err|\");\n        return;\n    }\n}\n\n\nstatic u_short\nngx_xquic_sockaddr_port(struct sockaddr *sa)\n{\n    u_short               port;\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        port = sin6->sin6_port;\n        break;\n#endif\n \n    default: /* AF_INET */\n        sin = (struct sockaddr_in *) sa;\n        port = sin->sin_port;\n        break;\n    }\n\n    return port;\n}\n\n\n\nngx_int_t\nngx_xquic_cmp_sockaddr(struct sockaddr *sa1, struct sockaddr *sa2)\n{\n    struct sockaddr_in   *sin1, *sin2;\n#if (NGX_HAVE_INET6)\n    struct sockaddr_in6  *sin61, *sin62;\n#endif\n\n    if (sa1->sa_family != sa2->sa_family) {\n        return NGX_DECLINED;\n    }    \n\n    switch (sa1->sa_family) {\n\n#if (NGX_HAVE_INET6)\n    case AF_INET6:\n        sin61 = (struct sockaddr_in6 *) sa1;\n        sin62 = (struct sockaddr_in6 *) sa2;\n\n        if (sin61->sin6_port != sin62->sin6_port) {\n            return NGX_DECLINED;\n        }    \n\n        if (ngx_memcmp(&sin61->sin6_addr, &sin62->sin6_addr, 16) != 0) { \n            return NGX_DECLINED;\n        }    \n\n        break;\n#endif\n\n    default: /* AF_INET */\n\n        sin1 = (struct sockaddr_in *) sa1;\n        sin2 = (struct sockaddr_in *) sa2;\n\n        if (sin1->sin_port != sin2->sin_port) {\n            return NGX_DECLINED;\n        }    \n\n        if (sin1->sin_addr.s_addr != sin2->sin_addr.s_addr) {\n            return NGX_DECLINED;\n        }    \n\n        break;\n    }    \n\n    return NGX_OK;\n}\n\n\n\n\nvoid\nngx_xquic_recv_from_intercom(ngx_xquic_recv_packet_t *packet)\n{\n    u_char                  text[NGX_SOCKADDR_STRLEN];\n    ngx_str_t               addr;\n    ngx_uint_t              i;\n    ngx_listening_t        *ls;\n    ngx_connection_t       *c;\n\n    if (ngx_terminate || ngx_exiting) {\n        return;\n    }\n\n    ls = (ngx_listening_t *) ngx_cycle->listening.elts;\n    for (i = 0; i < ngx_cycle->listening.nelts; i++) {\n\n#if !(T_RELOAD)\n#if (NGX_HAVE_REUSEPORT)\n        if (ls[i].reuseport && ls[i].worker != ngx_worker) {\n            continue;\n        }\n#endif\n#endif\n\n        if (ls[i].fd == -1) {\n            continue;\n        }\n\n        if (!ls[i].xquic) {\n            continue;\n        }\n\n        if (ls[i].wildcard) {\n            /* listen on *:port  */\n\n            if ((ls[i].sockaddr)->sa_family != packet->local_sockaddr.sa_family) {\n                continue;\n            }\n\n            if (ngx_xquic_sockaddr_port(ls[i].sockaddr) != ngx_xquic_sockaddr_port(&packet->local_sockaddr)) {\n                continue;\n            }\n        } else {\n            if (ngx_xquic_cmp_sockaddr(ls[i].sockaddr, &packet->local_sockaddr) != NGX_OK) {\n                continue;\n            }\n        }\n\n        c = ls[i].connection;\n\n        ngx_xquic_dispatcher_process_packet(c, packet);\n\n        return;\n    }\n\n    addr.data = text;\n    addr.len = ngx_sock_ntop(&packet->local_sockaddr, packet->local_socklen, text,\n                             NGX_SOCKADDR_STRLEN, 1);\n\n    ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                  \"|xquic|recv_from_intercom: can't find dispatcher for packet|by address %V|\",\n                  &addr);\n}\n\n#if (T_NGX_UDPV2)\n\nstatic void\nngx_xquic_udpv2_dispatch_packet(xqc_engine_t *engine, const ngx_udpv2_packet_t *upkt, void *user_data)\n{\n    uint64_t recv_time;\n    recv_time = upkt->pkt_micrs ? upkt->pkt_micrs : ngx_xquic_get_time();\n\n    if (xqc_engine_packet_process(engine, (u_char *)upkt->pkt_payload, upkt->pkt_sz,\n                                  (struct sockaddr *) &(upkt->pkt_local_sockaddr), upkt->pkt_local_socklen,\n                                  (struct sockaddr *) &(upkt->pkt_sockaddr), upkt->pkt_socklen,\n                                  (xqc_msec_t) recv_time, user_data) != 0)\n    {\n        ngx_log_error(NGX_LOG_DEBUG, ngx_cycle->log, 0,\n                    \"|xquic|xqc_server_read_handler: packet process err|\");\n        return;\n    }\n}\n\nvoid\nngx_xquic_dispatcher_process(ngx_connection_t *c, const ngx_udpv2_packet_t *upkt)\n{\n\n    ngx_http_xquic_main_conf_t  *qmcf;\n\n    if (ngx_terminate || ngx_exiting) {\n        return;\n    }\n\n    if (c->data == NULL) {\n        ngx_log_error(NGX_LOG_WARN, c->log, 0,\n                      \"|xquic|ngx_xquic_dispatcher_process_packet: engine NULL|\");\n        return;\n    }\n\n    qmcf = (ngx_http_xquic_main_conf_t *)(c->data);\n\n    /* check QUIC magic bit */\n    if (!NGX_XQUIC_CHECK_MAGIC_BIT(upkt->pkt_payload)) {\n        ngx_log_error(NGX_LOG_WARN, c->log, 0,\n                      \"|xquic|invalid packet head|\");\n        return;\n    }\n\n    /* check healthcheck */\n    if (upkt->pkt_sz >= sizeof(NGX_XQUIC_HEALTH_CHECK)\n        && ngx_strncmp(upkt->pkt_payload, NGX_XQUIC_HEALTH_CHECK, sizeof(NGX_XQUIC_HEALTH_CHECK)-1) == 0)\n    {\n        ngx_log_debug(NGX_LOG_DEBUG, c->log, 0,\n                      \"|xquic|health check|\");\n        return;\n    }\n\n    /* check healthcheck, REQ/RSP mode */\n    if (upkt->pkt_sz == sizeof(NGX_XQUIC_HEALTH_CHECK_REQ)-1\n        && ngx_strncmp(upkt->pkt_payload, NGX_XQUIC_HEALTH_CHECK_REQ, sizeof(NGX_XQUIC_HEALTH_CHECK_REQ)-1) == 0)\n    {\n        ngx_log_debug(NGX_LOG_DEBUG, c->log, 0,\n                      \"|xquic|health check, req/rsp mode|\");\n        sendto(c->fd, NGX_XQUIC_HEALTH_CHECK_RSP, sizeof(NGX_XQUIC_HEALTH_CHECK_RSP)-1, 0, &(upkt->pkt_sockaddr.sockaddr), upkt->pkt_socklen);\n        return;\n    }\n\n#if (NGX_DEBUG)\n    {\n        ngx_str_t  addr, addr2;\n        u_char     text[NGX_SOCKADDR_STRLEN], text2[NGX_SOCKADDR_STRLEN];\n        if (c->log->log_level & NGX_LOG_DEBUG_EVENT) {\n            addr.data = text;\n            addr.len = ngx_sock_ntop((struct sockaddr *) &(upkt->pkt_local_sockaddr), upkt->pkt_local_socklen, text,\n                                     NGX_SOCKADDR_STRLEN, 1);\n            addr2.data = text2;\n            addr2.len = ngx_sock_ntop((struct sockaddr *) &(upkt->pkt_sockaddr), upkt->pkt_socklen, text2,\n                                     NGX_SOCKADDR_STRLEN, 1);\n\n            ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                           \"|xquic|ngx_xquic_dispatcher_process: %V -> %V len:%d|\",\n                           &addr2, &addr, upkt->pkt_sz);\n        }\n    }\n#endif\n\n    ngx_xquic_udpv2_dispatch_packet(qmcf->xquic_engine, upkt, c);\n}\n#endif"
  },
  {
    "path": "modules/ngx_http_xquic_module/ngx_xquic_recv.h",
    "content": "/*\n * Copyright (C) 2020-2023 Alibaba Group Holding Limited\n */\n\n#ifndef _T_NGX_XQUIC_RECV_H_INCLUDED_\n#define _T_NGX_XQUIC_RECV_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_event.h>\n#include <ngx_http.h>\n\n#include <xquic/xquic_typedef.h>\n\n\ntypedef struct {\n    union {\n        struct sockaddr   sockaddr;\n        u_char            sa[NGX_SOCKADDRLEN];\n    };\n    socklen_t             socklen;\n\n    union {\n        struct sockaddr   local_sockaddr;\n        u_char            lsa[NGX_SOCKADDRLEN];\n    };\n    socklen_t             local_socklen;\n\n    char                  buf[1500];\n    struct {\n        char              unused;\n        uint64_t          connection_id;\n        xqc_cid_t         dcid;\n        xqc_cid_t         scid;\n    } xquic;\n\n    size_t                len;\n} ngx_xquic_recv_packet_t;\n\nngx_int_t ngx_xquic_recv(ngx_connection_t *c, char *buf, size_t size);\nngx_int_t ngx_xquic_recv_packet(ngx_connection_t *c, ngx_xquic_recv_packet_t *packet, ngx_log_t *log, xqc_engine_t *engine);\nvoid ngx_xquic_event_recv(ngx_event_t *ev);\nvoid ngx_xquic_dispatcher_process_packet(ngx_connection_t *c, ngx_xquic_recv_packet_t *packet);\nvoid ngx_xquic_recv_from_intercom(ngx_xquic_recv_packet_t *packet);\nvoid ngx_xquic_packet_get_cid(ngx_xquic_recv_packet_t *packet, \n    xqc_engine_t *engine);\n\nvoid ngx_xquic_packet_get_cid_raw(xqc_engine_t *engine, unsigned char *payload, size_t sz,\n    xqc_cid_t *dcid, xqc_cid_t *scid);\n\n#if (T_NGX_UDPV2)\nvoid ngx_xquic_dispatcher_process(ngx_connection_t *c, const ngx_udpv2_packet_t *upkt);\n#endif\n\n#if (NGX_XQUIC_SUPPORT_CID_ROUTE)\n/**\n * generate local CID based on spec of worker ID\n * */\nssize_t     ngx_xquic_generate_route_cid(unsigned char *buf, size_t len, const uint8_t *current_cid_buf, size_t current_cid_buflen);\n\n/**\n * choose the specific worker based on received packets\n * */\nngx_int_t   ngx_xquic_get_target_worker_from_cid(ngx_xquic_recv_packet_t *packet);\n#endif\n\n#define NGX_XQUIC_CHECK_MAGIC_BIT(pos) (((*(pos)) & 0x40) == 0x40)\n#define NGX_XQUIC_HEALTH_CHECK  \"Healthcheck\"\n#define NGX_XQUIC_HEALTH_CHECK_REQ  \"UDPSTATUS\"\n#define NGX_XQUIC_HEALTH_CHECK_RSP  \"UDPOK\"\n\n#endif /* _T_NGX_XQUIC_RECV_H_INCLUDED_ */\n\n"
  },
  {
    "path": "modules/ngx_http_xquic_module/ngx_xquic_send.c",
    "content": "/*\n * Copyright (C) 2020-2023 Alibaba Group Holding Limited\n */\n\n#include <ngx_xquic_send.h>\n#include <ngx_http_xquic_module.h>\n#include <ngx_http_v3_stream.h>\n#include <xquic/xquic.h>\n\n#if (T_NGX_HAVE_XUDP)\n#include <ngx_xudp.h>\n#endif\n\n#define NGX_XQUIC_MAX_SEND_MSG_ONCE  XQC_MAX_SEND_MSG_ONCE\n\nstatic ssize_t ngx_http_xquic_on_write_block(ngx_http_xquic_connection_t *qc, ngx_event_t *wev);\n\nvoid\nngx_http_xquic_write_handler(ngx_event_t *wev)\n{\n    ngx_int_t                     rc;\n    ngx_connection_t             *c;\n    ngx_http_xquic_connection_t  *qc;\n\n    c = wev->data;\n    qc = c->data;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"|xquic|ngx_http_xquic_write_handler|\");\n\n    // del write event\n    ngx_del_event(wev, NGX_WRITE_EVENT, 0);\n\n    if (wev->timedout) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"http3 write event timed out\");\n        c->error = 1;\n        ngx_http_v3_connection_error(qc, NGX_XQUIC_CONN_WRITE_ERR, \"write event timed out\");\n        return;\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"xquic write handler\");\n\n    qc->blocked = 1;\n\n    rc = xqc_conn_continue_send(qc->engine, &qc->dcid);\n\n    if (rc < 0) {\n\n        ngx_log_error(NGX_LOG_WARN, c->log, 0, \"|xquic|write handler continue send|rc=%i|\", rc);\n\n        c->error = 1;\n        ngx_http_v3_connection_error(qc, NGX_XQUIC_CONN_WRITE_ERR, \n                                    \"xqc_conn_continue_send err\");\n        return;\n    }\n\n    qc->blocked = 0;\n\n    //ngx_http_v3_handle_connection(qc);\n}\n\nssize_t \nngx_xquic_server_send(const unsigned char *buf, size_t size,\n    const struct sockaddr *peer_addr, socklen_t peer_addrlen, void *user_data)\n{\n\n    ngx_log_error(NGX_LOG_DEBUG, ngx_cycle->log, 0,\n                                    \"|xquic|ngx_xquic_server_send|%p|%z|\", buf, size);\n\n    /* while sending reset, user_data may be empty */\n    ngx_http_xquic_connection_t *qc = (ngx_http_xquic_connection_t *)user_data; \n    if (qc == NULL) {\n        ngx_log_error(NGX_LOG_WARN, ngx_cycle->log, 0,\n                                    \"|xquic|ngx_xquic_server_send|user_conn=NULL|\");\n        return XQC_SOCKET_ERROR;\n    }\n\n    ssize_t res = 0;\n    ngx_socket_t fd = qc->connection->fd;\n    ngx_log_error(NGX_LOG_DEBUG, ngx_cycle->log, 0,\n                    \"|xquic|xqc_server_send size=%z now=%i|dcid=%s|\", \n                    size, ngx_xquic_get_time(), xqc_dcid_str(&qc->dcid));\n    do {\n        errno = 0;\n        res = sendto(fd, buf, size, 0, peer_addr, peer_addrlen);\n        ngx_log_error(NGX_LOG_DEBUG, ngx_cycle->log, 0,\n                        \"|xquic|xqc_server_send write %zd, %s|\", res, strerror(errno));\n\n        if ((res < 0) && (errno == EAGAIN)) {\n            break;\n        }\n\n    } while ((res < 0) && (errno == EINTR));\n\n    if ((res < 0) && (errno == EAGAIN)) {\n        return ngx_http_xquic_on_write_block(qc, qc->connection->write);\n    } else if (res < 0) {\n\n        ngx_log_error(NGX_LOG_WARN, ngx_cycle->log, 0,\n                    \"|xquic|ngx_xquic_server_send|socket err|\");          \n        return XQC_SOCKET_ERROR;\n    }\n\n    return res;\n}\n\n\n#if defined(T_NGX_XQUIC_SUPPORT_SENDMMSG)\nssize_t \nngx_xquic_server_send_mmsg(const struct iovec *msg_iov, unsigned int vlen,\n    const struct sockaddr *peer_addr, socklen_t peer_addrlen, void *user_data)\n{\n    ngx_event_t               *wev;\n    ssize_t                    res = 0;\n    unsigned int               i = 0;\n\n    struct mmsghdr             msg[NGX_XQUIC_MAX_SEND_MSG_ONCE];\n\n    memset(msg, 0, sizeof(msg));\n\n    ngx_http_xquic_connection_t *qc = (ngx_http_xquic_connection_t *)user_data;\n\n    if (qc == NULL) {\n        ngx_log_error(NGX_LOG_WARN, ngx_cycle->log, 0,\n                                    \"|xquic|ngx_xquic_server_send_mmsg|user_conn=NULL|\");\n        return (ssize_t)NGX_ERROR;\n    }\n\n    ngx_socket_t fd = qc->connection->fd;\n    ngx_log_error(NGX_LOG_DEBUG, ngx_cycle->log, 0,\n                    \"|xquic|ngx_xquic_server_send_mmsg|vlen=%z now=%i|dcid=%s|\",\n                    vlen, ngx_xquic_get_time(), xqc_dcid_str(&qc->dcid));\n\n    wev = qc->connection->write;\n\n#if (T_NGX_UDPV2)\n#if (T_NGX_HAVE_XUDP)\n    if (ngx_xudp_is_tx_enable(qc->connection)) {\n        res = ngx_xudp_sendmmsg(qc->connection, (struct iovec *) msg_iov, vlen, peer_addr, peer_addrlen, /**push*/ 1);\n        if (res == vlen) {\n            return res;\n        }else if(res < vlen) {\n            if (res < 0) {\n                if (ngx_xudp_error_is_fatal(res)) {\n                    goto degrade;\n                }\n                /* reset res to 0 */\n                res = 0;\n            }\n            ngx_queue_t *q = ngx_udpv2_active_writable_queue(ngx_xudp_get_tx());\n            if (q != NULL) {\n                ngx_post_event(wev, q);\n                return res;\n            }\n        }\n        /* degrade to system */\ndegrade:\n        ngx_xudp_disable_tx(qc->connection);\n        if (wev->posted) {\n            ngx_delete_posted_event(wev);\n        }\n    }\n#endif\n#endif\n\n    for(i = 0 ; i < vlen; i++){\n        msg[i].msg_hdr.msg_iov = (struct iovec *) msg_iov + i;\n        msg[i].msg_hdr.msg_iovlen = 1;\n    }\n\n    res = sendmmsg(fd, msg, vlen, 0);\n\n    if (res < 0 && (errno == EAGAIN)) {\n        return ngx_http_xquic_on_write_block(qc, wev);\n    } else if (res < 0) {\n\n        ngx_log_error(NGX_LOG_WARN, ngx_cycle->log, 0,\n            \"|xquic|ngx_xquic_server_send_mmsg err|total_len=%z now=%i|dcid=%s|send_len=%z|errno=%s|\",\n            vlen, ngx_xquic_get_time(), xqc_dcid_str(&qc->dcid), res, strerror(errno));\n        return XQC_SOCKET_ERROR;\n    }\n\n    ngx_log_error(NGX_LOG_DEBUG, ngx_cycle->log, 0,\n            \"|xquic|ngx_xquic_server_send_mmsg success|total_len=%z now=%i|dcid=%s|send_len=%z|\",\n            vlen, ngx_xquic_get_time(), xqc_dcid_str(&qc->dcid), res);\n\n\n    return res;\n}\n#endif\n\n\nstatic ngx_inline ssize_t\nngx_http_xquic_on_write_block(ngx_http_xquic_connection_t *qc, ngx_event_t *wev)\n{\n    ngx_http_core_loc_conf_t    *clcf;\n\n    clcf    = ngx_http_get_module_loc_conf(qc->http_connection->conf_ctx,\n                                        ngx_http_core_module);\n\n    wev->ready = 0;\n\n    if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) {\n        ngx_log_error(NGX_LOG_WARN, ngx_cycle->log, 0,\n            \"|xquic|ngx_handle_write_event err|\");\n        return XQC_SOCKET_ERROR;\n    }\n    return XQC_SOCKET_EAGAIN;\n}\n"
  },
  {
    "path": "modules/ngx_http_xquic_module/ngx_xquic_send.h",
    "content": "/*\n * Copyright (C) 2020-2023 Alibaba Group Holding Limited\n */\n\n#ifndef _T_NGX_XQUIC_SEND_H_INCLUDED_\n#define _T_NGX_XQUIC_SEND_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_event.h>\n#include <ngx_http.h>\n#if defined(__linux__)\n#include <linux/version.h>\n#endif\n\n#if defined(LINUX_VERSION_CODE)\n    #if LINUX_VERSION_CODE > KERNEL_VERSION(3,0,0)\n        //The sendmmsg() system call was added in Linux 3.0.\n        #define T_NGX_XQUIC_SUPPORT_SENDMMSG\n    #endif\n#endif\n\nssize_t ngx_xquic_server_send(const unsigned char *buf, size_t size,\n    const struct sockaddr *peer_addr, socklen_t peer_addrlen, void *user_data);\n\n#if defined(T_NGX_XQUIC_SUPPORT_SENDMMSG)\nssize_t ngx_xquic_server_send_mmsg(const struct iovec *msg_iov, unsigned int vlen,\n    const struct sockaddr *peer_addr, socklen_t peer_addrlen, void *user_data);\n#endif\n\nvoid ngx_http_xquic_write_handler(ngx_event_t *wev);\n\n#endif /* _T_NGX_XQUIC_SEND_H_INCLUDED_ */\n\n"
  },
  {
    "path": "modules/ngx_ingress_module/config",
    "content": "\nngx_addon_name=ngx_ingress_module\nHTTP_MODULES=\"$HTTP_MODULES ngx_ingress_module\"\n\nNGX_ADDON_SRCS=\"$NGX_ADDON_SRCS $ngx_addon_dir/ngx_ingress_module.c $ngx_addon_dir/ngx_ingress_protobuf.c $ngx_addon_dir/ingress.pb-c.c\"\n\nHTTP_INCS=\"$HTTP_INCS $ngx_addon_dir\"\n\nNGX_ADDON_DEPS=\"$NGX_ADDON_DEPS                                     \\\n                     $ngx_addon_dir/ngx_ingress_module.h            \\\n                     $ngx_addon_dir/ngx_ingress_protobuf.h\"\n\n\nCORE_LIBS=\"$CORE_LIBS -lrt -lprotobuf-c\"\n\nhave=T_INGRESS_SHARED_MEMORY_PB . auto/have\n\n"
  },
  {
    "path": "modules/ngx_ingress_module/ingress.pb-c.c",
    "content": "/* Generated by the protocol buffer compiler.  DO NOT EDIT! */\n/* Generated from: ingress.proto */\n\n/* Do not generate deprecated warnings for self */\n#ifndef PROTOBUF_C__NO_DEPRECATED\n#define PROTOBUF_C__NO_DEPRECATED\n#endif\n\n#include \"ingress.pb-c.h\"\nvoid   ingress__tag_value_str_list__init\n                     (Ingress__TagValueStrList         *message)\n{\n  static const Ingress__TagValueStrList init_value = INGRESS__TAG_VALUE_STR_LIST__INIT;\n  *message = init_value;\n}\nsize_t ingress__tag_value_str_list__get_packed_size\n                     (const Ingress__TagValueStrList *message)\n{\n  assert(message->base.descriptor == &ingress__tag_value_str_list__descriptor);\n  return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));\n}\nsize_t ingress__tag_value_str_list__pack\n                     (const Ingress__TagValueStrList *message,\n                      uint8_t       *out)\n{\n  assert(message->base.descriptor == &ingress__tag_value_str_list__descriptor);\n  return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);\n}\nsize_t ingress__tag_value_str_list__pack_to_buffer\n                     (const Ingress__TagValueStrList *message,\n                      ProtobufCBuffer *buffer)\n{\n  assert(message->base.descriptor == &ingress__tag_value_str_list__descriptor);\n  return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);\n}\nIngress__TagValueStrList *\n       ingress__tag_value_str_list__unpack\n                     (ProtobufCAllocator  *allocator,\n                      size_t               len,\n                      const uint8_t       *data)\n{\n  return (Ingress__TagValueStrList *)\n     protobuf_c_message_unpack (&ingress__tag_value_str_list__descriptor,\n                                allocator, len, data);\n}\nvoid   ingress__tag_value_str_list__free_unpacked\n                     (Ingress__TagValueStrList *message,\n                      ProtobufCAllocator *allocator)\n{\n  if(!message)\n    return;\n  assert(message->base.descriptor == &ingress__tag_value_str_list__descriptor);\n  protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);\n}\nvoid   ingress__tag_item_condition__init\n                     (Ingress__TagItemCondition         *message)\n{\n  static const Ingress__TagItemCondition init_value = INGRESS__TAG_ITEM_CONDITION__INIT;\n  *message = init_value;\n}\nsize_t ingress__tag_item_condition__get_packed_size\n                     (const Ingress__TagItemCondition *message)\n{\n  assert(message->base.descriptor == &ingress__tag_item_condition__descriptor);\n  return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));\n}\nsize_t ingress__tag_item_condition__pack\n                     (const Ingress__TagItemCondition *message,\n                      uint8_t       *out)\n{\n  assert(message->base.descriptor == &ingress__tag_item_condition__descriptor);\n  return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);\n}\nsize_t ingress__tag_item_condition__pack_to_buffer\n                     (const Ingress__TagItemCondition *message,\n                      ProtobufCBuffer *buffer)\n{\n  assert(message->base.descriptor == &ingress__tag_item_condition__descriptor);\n  return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);\n}\nIngress__TagItemCondition *\n       ingress__tag_item_condition__unpack\n                     (ProtobufCAllocator  *allocator,\n                      size_t               len,\n                      const uint8_t       *data)\n{\n  return (Ingress__TagItemCondition *)\n     protobuf_c_message_unpack (&ingress__tag_item_condition__descriptor,\n                                allocator, len, data);\n}\nvoid   ingress__tag_item_condition__free_unpacked\n                     (Ingress__TagItemCondition *message,\n                      ProtobufCAllocator *allocator)\n{\n  if(!message)\n    return;\n  assert(message->base.descriptor == &ingress__tag_item_condition__descriptor);\n  protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);\n}\nvoid   ingress__tag_item__init\n                     (Ingress__TagItem         *message)\n{\n  static const Ingress__TagItem init_value = INGRESS__TAG_ITEM__INIT;\n  *message = init_value;\n}\nsize_t ingress__tag_item__get_packed_size\n                     (const Ingress__TagItem *message)\n{\n  assert(message->base.descriptor == &ingress__tag_item__descriptor);\n  return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));\n}\nsize_t ingress__tag_item__pack\n                     (const Ingress__TagItem *message,\n                      uint8_t       *out)\n{\n  assert(message->base.descriptor == &ingress__tag_item__descriptor);\n  return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);\n}\nsize_t ingress__tag_item__pack_to_buffer\n                     (const Ingress__TagItem *message,\n                      ProtobufCBuffer *buffer)\n{\n  assert(message->base.descriptor == &ingress__tag_item__descriptor);\n  return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);\n}\nIngress__TagItem *\n       ingress__tag_item__unpack\n                     (ProtobufCAllocator  *allocator,\n                      size_t               len,\n                      const uint8_t       *data)\n{\n  return (Ingress__TagItem *)\n     protobuf_c_message_unpack (&ingress__tag_item__descriptor,\n                                allocator, len, data);\n}\nvoid   ingress__tag_item__free_unpacked\n                     (Ingress__TagItem *message,\n                      ProtobufCAllocator *allocator)\n{\n  if(!message)\n    return;\n  assert(message->base.descriptor == &ingress__tag_item__descriptor);\n  protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);\n}\nvoid   ingress__tag_rule__init\n                     (Ingress__TagRule         *message)\n{\n  static const Ingress__TagRule init_value = INGRESS__TAG_RULE__INIT;\n  *message = init_value;\n}\nsize_t ingress__tag_rule__get_packed_size\n                     (const Ingress__TagRule *message)\n{\n  assert(message->base.descriptor == &ingress__tag_rule__descriptor);\n  return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));\n}\nsize_t ingress__tag_rule__pack\n                     (const Ingress__TagRule *message,\n                      uint8_t       *out)\n{\n  assert(message->base.descriptor == &ingress__tag_rule__descriptor);\n  return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);\n}\nsize_t ingress__tag_rule__pack_to_buffer\n                     (const Ingress__TagRule *message,\n                      ProtobufCBuffer *buffer)\n{\n  assert(message->base.descriptor == &ingress__tag_rule__descriptor);\n  return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);\n}\nIngress__TagRule *\n       ingress__tag_rule__unpack\n                     (ProtobufCAllocator  *allocator,\n                      size_t               len,\n                      const uint8_t       *data)\n{\n  return (Ingress__TagRule *)\n     protobuf_c_message_unpack (&ingress__tag_rule__descriptor,\n                                allocator, len, data);\n}\nvoid   ingress__tag_rule__free_unpacked\n                     (Ingress__TagRule *message,\n                      ProtobufCAllocator *allocator)\n{\n  if(!message)\n    return;\n  assert(message->base.descriptor == &ingress__tag_rule__descriptor);\n  protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);\n}\nvoid   ingress__tag_router__init\n                     (Ingress__TagRouter         *message)\n{\n  static const Ingress__TagRouter init_value = INGRESS__TAG_ROUTER__INIT;\n  *message = init_value;\n}\nsize_t ingress__tag_router__get_packed_size\n                     (const Ingress__TagRouter *message)\n{\n  assert(message->base.descriptor == &ingress__tag_router__descriptor);\n  return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));\n}\nsize_t ingress__tag_router__pack\n                     (const Ingress__TagRouter *message,\n                      uint8_t       *out)\n{\n  assert(message->base.descriptor == &ingress__tag_router__descriptor);\n  return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);\n}\nsize_t ingress__tag_router__pack_to_buffer\n                     (const Ingress__TagRouter *message,\n                      ProtobufCBuffer *buffer)\n{\n  assert(message->base.descriptor == &ingress__tag_router__descriptor);\n  return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);\n}\nIngress__TagRouter *\n       ingress__tag_router__unpack\n                     (ProtobufCAllocator  *allocator,\n                      size_t               len,\n                      const uint8_t       *data)\n{\n  return (Ingress__TagRouter *)\n     protobuf_c_message_unpack (&ingress__tag_router__descriptor,\n                                allocator, len, data);\n}\nvoid   ingress__tag_router__free_unpacked\n                     (Ingress__TagRouter *message,\n                      ProtobufCAllocator *allocator)\n{\n  if(!message)\n    return;\n  assert(message->base.descriptor == &ingress__tag_router__descriptor);\n  protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);\n}\nvoid   ingress__path_router__init\n                     (Ingress__PathRouter         *message)\n{\n  static const Ingress__PathRouter init_value = INGRESS__PATH_ROUTER__INIT;\n  *message = init_value;\n}\nsize_t ingress__path_router__get_packed_size\n                     (const Ingress__PathRouter *message)\n{\n  assert(message->base.descriptor == &ingress__path_router__descriptor);\n  return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));\n}\nsize_t ingress__path_router__pack\n                     (const Ingress__PathRouter *message,\n                      uint8_t       *out)\n{\n  assert(message->base.descriptor == &ingress__path_router__descriptor);\n  return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);\n}\nsize_t ingress__path_router__pack_to_buffer\n                     (const Ingress__PathRouter *message,\n                      ProtobufCBuffer *buffer)\n{\n  assert(message->base.descriptor == &ingress__path_router__descriptor);\n  return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);\n}\nIngress__PathRouter *\n       ingress__path_router__unpack\n                     (ProtobufCAllocator  *allocator,\n                      size_t               len,\n                      const uint8_t       *data)\n{\n  return (Ingress__PathRouter *)\n     protobuf_c_message_unpack (&ingress__path_router__descriptor,\n                                allocator, len, data);\n}\nvoid   ingress__path_router__free_unpacked\n                     (Ingress__PathRouter *message,\n                      ProtobufCAllocator *allocator)\n{\n  if(!message)\n    return;\n  assert(message->base.descriptor == &ingress__path_router__descriptor);\n  protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);\n}\nvoid   ingress__host_router__init\n                     (Ingress__HostRouter         *message)\n{\n  static const Ingress__HostRouter init_value = INGRESS__HOST_ROUTER__INIT;\n  *message = init_value;\n}\nsize_t ingress__host_router__get_packed_size\n                     (const Ingress__HostRouter *message)\n{\n  assert(message->base.descriptor == &ingress__host_router__descriptor);\n  return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));\n}\nsize_t ingress__host_router__pack\n                     (const Ingress__HostRouter *message,\n                      uint8_t       *out)\n{\n  assert(message->base.descriptor == &ingress__host_router__descriptor);\n  return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);\n}\nsize_t ingress__host_router__pack_to_buffer\n                     (const Ingress__HostRouter *message,\n                      ProtobufCBuffer *buffer)\n{\n  assert(message->base.descriptor == &ingress__host_router__descriptor);\n  return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);\n}\nIngress__HostRouter *\n       ingress__host_router__unpack\n                     (ProtobufCAllocator  *allocator,\n                      size_t               len,\n                      const uint8_t       *data)\n{\n  return (Ingress__HostRouter *)\n     protobuf_c_message_unpack (&ingress__host_router__descriptor,\n                                allocator, len, data);\n}\nvoid   ingress__host_router__free_unpacked\n                     (Ingress__HostRouter *message,\n                      ProtobufCAllocator *allocator)\n{\n  if(!message)\n    return;\n  assert(message->base.descriptor == &ingress__host_router__descriptor);\n  protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);\n}\nvoid   ingress__appname_router__init\n                     (Ingress__AppnameRouter         *message)\n{\n  static const Ingress__AppnameRouter init_value = INGRESS__APPNAME_ROUTER__INIT;\n  *message = init_value;\n}\nsize_t ingress__appname_router__get_packed_size\n                     (const Ingress__AppnameRouter *message)\n{\n  assert(message->base.descriptor == &ingress__appname_router__descriptor);\n  return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));\n}\nsize_t ingress__appname_router__pack\n                     (const Ingress__AppnameRouter *message,\n                      uint8_t       *out)\n{\n  assert(message->base.descriptor == &ingress__appname_router__descriptor);\n  return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);\n}\nsize_t ingress__appname_router__pack_to_buffer\n                     (const Ingress__AppnameRouter *message,\n                      ProtobufCBuffer *buffer)\n{\n  assert(message->base.descriptor == &ingress__appname_router__descriptor);\n  return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);\n}\nIngress__AppnameRouter *\n       ingress__appname_router__unpack\n                     (ProtobufCAllocator  *allocator,\n                      size_t               len,\n                      const uint8_t       *data)\n{\n  return (Ingress__AppnameRouter *)\n     protobuf_c_message_unpack (&ingress__appname_router__descriptor,\n                                allocator, len, data);\n}\nvoid   ingress__appname_router__free_unpacked\n                     (Ingress__AppnameRouter *message,\n                      ProtobufCAllocator *allocator)\n{\n  if(!message)\n    return;\n  assert(message->base.descriptor == &ingress__appname_router__descriptor);\n  protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);\n}\nvoid   ingress__apirouter__init\n                     (Ingress__APIRouter         *message)\n{\n  static const Ingress__APIRouter init_value = INGRESS__APIROUTER__INIT;\n  *message = init_value;\n}\nsize_t ingress__apirouter__get_packed_size\n                     (const Ingress__APIRouter *message)\n{\n  assert(message->base.descriptor == &ingress__apirouter__descriptor);\n  return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));\n}\nsize_t ingress__apirouter__pack\n                     (const Ingress__APIRouter *message,\n                      uint8_t       *out)\n{\n  assert(message->base.descriptor == &ingress__apirouter__descriptor);\n  return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);\n}\nsize_t ingress__apirouter__pack_to_buffer\n                     (const Ingress__APIRouter *message,\n                      ProtobufCBuffer *buffer)\n{\n  assert(message->base.descriptor == &ingress__apirouter__descriptor);\n  return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);\n}\nIngress__APIRouter *\n       ingress__apirouter__unpack\n                     (ProtobufCAllocator  *allocator,\n                      size_t               len,\n                      const uint8_t       *data)\n{\n  return (Ingress__APIRouter *)\n     protobuf_c_message_unpack (&ingress__apirouter__descriptor,\n                                allocator, len, data);\n}\nvoid   ingress__apirouter__free_unpacked\n                     (Ingress__APIRouter *message,\n                      ProtobufCAllocator *allocator)\n{\n  if(!message)\n    return;\n  assert(message->base.descriptor == &ingress__apirouter__descriptor);\n  protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);\n}\nvoid   ingress__router__init\n                     (Ingress__Router         *message)\n{\n  static const Ingress__Router init_value = INGRESS__ROUTER__INIT;\n  *message = init_value;\n}\nsize_t ingress__router__get_packed_size\n                     (const Ingress__Router *message)\n{\n  assert(message->base.descriptor == &ingress__router__descriptor);\n  return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));\n}\nsize_t ingress__router__pack\n                     (const Ingress__Router *message,\n                      uint8_t       *out)\n{\n  assert(message->base.descriptor == &ingress__router__descriptor);\n  return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);\n}\nsize_t ingress__router__pack_to_buffer\n                     (const Ingress__Router *message,\n                      ProtobufCBuffer *buffer)\n{\n  assert(message->base.descriptor == &ingress__router__descriptor);\n  return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);\n}\nIngress__Router *\n       ingress__router__unpack\n                     (ProtobufCAllocator  *allocator,\n                      size_t               len,\n                      const uint8_t       *data)\n{\n  return (Ingress__Router *)\n     protobuf_c_message_unpack (&ingress__router__descriptor,\n                                allocator, len, data);\n}\nvoid   ingress__router__free_unpacked\n                     (Ingress__Router *message,\n                      ProtobufCAllocator *allocator)\n{\n  if(!message)\n    return;\n  assert(message->base.descriptor == &ingress__router__descriptor);\n  protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);\n}\nvoid   ingress__timeout__init\n                     (Ingress__Timeout         *message)\n{\n  static const Ingress__Timeout init_value = INGRESS__TIMEOUT__INIT;\n  *message = init_value;\n}\nsize_t ingress__timeout__get_packed_size\n                     (const Ingress__Timeout *message)\n{\n  assert(message->base.descriptor == &ingress__timeout__descriptor);\n  return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));\n}\nsize_t ingress__timeout__pack\n                     (const Ingress__Timeout *message,\n                      uint8_t       *out)\n{\n  assert(message->base.descriptor == &ingress__timeout__descriptor);\n  return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);\n}\nsize_t ingress__timeout__pack_to_buffer\n                     (const Ingress__Timeout *message,\n                      ProtobufCBuffer *buffer)\n{\n  assert(message->base.descriptor == &ingress__timeout__descriptor);\n  return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);\n}\nIngress__Timeout *\n       ingress__timeout__unpack\n                     (ProtobufCAllocator  *allocator,\n                      size_t               len,\n                      const uint8_t       *data)\n{\n  return (Ingress__Timeout *)\n     protobuf_c_message_unpack (&ingress__timeout__descriptor,\n                                allocator, len, data);\n}\nvoid   ingress__timeout__free_unpacked\n                     (Ingress__Timeout *message,\n                      ProtobufCAllocator *allocator)\n{\n  if(!message)\n    return;\n  assert(message->base.descriptor == &ingress__timeout__descriptor);\n  protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);\n}\nvoid   ingress__upstream__init\n                     (Ingress__Upstream         *message)\n{\n  static const Ingress__Upstream init_value = INGRESS__UPSTREAM__INIT;\n  *message = init_value;\n}\nsize_t ingress__upstream__get_packed_size\n                     (const Ingress__Upstream *message)\n{\n  assert(message->base.descriptor == &ingress__upstream__descriptor);\n  return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));\n}\nsize_t ingress__upstream__pack\n                     (const Ingress__Upstream *message,\n                      uint8_t       *out)\n{\n  assert(message->base.descriptor == &ingress__upstream__descriptor);\n  return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);\n}\nsize_t ingress__upstream__pack_to_buffer\n                     (const Ingress__Upstream *message,\n                      ProtobufCBuffer *buffer)\n{\n  assert(message->base.descriptor == &ingress__upstream__descriptor);\n  return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);\n}\nIngress__Upstream *\n       ingress__upstream__unpack\n                     (ProtobufCAllocator  *allocator,\n                      size_t               len,\n                      const uint8_t       *data)\n{\n  return (Ingress__Upstream *)\n     protobuf_c_message_unpack (&ingress__upstream__descriptor,\n                                allocator, len, data);\n}\nvoid   ingress__upstream__free_unpacked\n                     (Ingress__Upstream *message,\n                      ProtobufCAllocator *allocator)\n{\n  if(!message)\n    return;\n  assert(message->base.descriptor == &ingress__upstream__descriptor);\n  protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);\n}\nvoid   ingress__metadata__init\n                     (Ingress__Metadata         *message)\n{\n  static const Ingress__Metadata init_value = INGRESS__METADATA__INIT;\n  *message = init_value;\n}\nsize_t ingress__metadata__get_packed_size\n                     (const Ingress__Metadata *message)\n{\n  assert(message->base.descriptor == &ingress__metadata__descriptor);\n  return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));\n}\nsize_t ingress__metadata__pack\n                     (const Ingress__Metadata *message,\n                      uint8_t       *out)\n{\n  assert(message->base.descriptor == &ingress__metadata__descriptor);\n  return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);\n}\nsize_t ingress__metadata__pack_to_buffer\n                     (const Ingress__Metadata *message,\n                      ProtobufCBuffer *buffer)\n{\n  assert(message->base.descriptor == &ingress__metadata__descriptor);\n  return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);\n}\nIngress__Metadata *\n       ingress__metadata__unpack\n                     (ProtobufCAllocator  *allocator,\n                      size_t               len,\n                      const uint8_t       *data)\n{\n  return (Ingress__Metadata *)\n     protobuf_c_message_unpack (&ingress__metadata__descriptor,\n                                allocator, len, data);\n}\nvoid   ingress__metadata__free_unpacked\n                     (Ingress__Metadata *message,\n                      ProtobufCAllocator *allocator)\n{\n  if(!message)\n    return;\n  assert(message->base.descriptor == &ingress__metadata__descriptor);\n  protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);\n}\nvoid   ingress__action__init\n                     (Ingress__Action         *message)\n{\n  static const Ingress__Action init_value = INGRESS__ACTION__INIT;\n  *message = init_value;\n}\nsize_t ingress__action__get_packed_size\n                     (const Ingress__Action *message)\n{\n  assert(message->base.descriptor == &ingress__action__descriptor);\n  return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));\n}\nsize_t ingress__action__pack\n                     (const Ingress__Action *message,\n                      uint8_t       *out)\n{\n  assert(message->base.descriptor == &ingress__action__descriptor);\n  return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);\n}\nsize_t ingress__action__pack_to_buffer\n                     (const Ingress__Action *message,\n                      ProtobufCBuffer *buffer)\n{\n  assert(message->base.descriptor == &ingress__action__descriptor);\n  return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);\n}\nIngress__Action *\n       ingress__action__unpack\n                     (ProtobufCAllocator  *allocator,\n                      size_t               len,\n                      const uint8_t       *data)\n{\n  return (Ingress__Action *)\n     protobuf_c_message_unpack (&ingress__action__descriptor,\n                                allocator, len, data);\n}\nvoid   ingress__action__free_unpacked\n                     (Ingress__Action *message,\n                      ProtobufCAllocator *allocator)\n{\n  if(!message)\n    return;\n  assert(message->base.descriptor == &ingress__action__descriptor);\n  protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);\n}\nvoid   ingress__virtual_service__init\n                     (Ingress__VirtualService         *message)\n{\n  static const Ingress__VirtualService init_value = INGRESS__VIRTUAL_SERVICE__INIT;\n  *message = init_value;\n}\nsize_t ingress__virtual_service__get_packed_size\n                     (const Ingress__VirtualService *message)\n{\n  assert(message->base.descriptor == &ingress__virtual_service__descriptor);\n  return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));\n}\nsize_t ingress__virtual_service__pack\n                     (const Ingress__VirtualService *message,\n                      uint8_t       *out)\n{\n  assert(message->base.descriptor == &ingress__virtual_service__descriptor);\n  return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);\n}\nsize_t ingress__virtual_service__pack_to_buffer\n                     (const Ingress__VirtualService *message,\n                      ProtobufCBuffer *buffer)\n{\n  assert(message->base.descriptor == &ingress__virtual_service__descriptor);\n  return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);\n}\nIngress__VirtualService *\n       ingress__virtual_service__unpack\n                     (ProtobufCAllocator  *allocator,\n                      size_t               len,\n                      const uint8_t       *data)\n{\n  return (Ingress__VirtualService *)\n     protobuf_c_message_unpack (&ingress__virtual_service__descriptor,\n                                allocator, len, data);\n}\nvoid   ingress__virtual_service__free_unpacked\n                     (Ingress__VirtualService *message,\n                      ProtobufCAllocator *allocator)\n{\n  if(!message)\n    return;\n  assert(message->base.descriptor == &ingress__virtual_service__descriptor);\n  protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);\n}\nvoid   ingress__config__init\n                     (Ingress__Config         *message)\n{\n  static const Ingress__Config init_value = INGRESS__CONFIG__INIT;\n  *message = init_value;\n}\nsize_t ingress__config__get_packed_size\n                     (const Ingress__Config *message)\n{\n  assert(message->base.descriptor == &ingress__config__descriptor);\n  return protobuf_c_message_get_packed_size ((const ProtobufCMessage*)(message));\n}\nsize_t ingress__config__pack\n                     (const Ingress__Config *message,\n                      uint8_t       *out)\n{\n  assert(message->base.descriptor == &ingress__config__descriptor);\n  return protobuf_c_message_pack ((const ProtobufCMessage*)message, out);\n}\nsize_t ingress__config__pack_to_buffer\n                     (const Ingress__Config *message,\n                      ProtobufCBuffer *buffer)\n{\n  assert(message->base.descriptor == &ingress__config__descriptor);\n  return protobuf_c_message_pack_to_buffer ((const ProtobufCMessage*)message, buffer);\n}\nIngress__Config *\n       ingress__config__unpack\n                     (ProtobufCAllocator  *allocator,\n                      size_t               len,\n                      const uint8_t       *data)\n{\n  return (Ingress__Config *)\n     protobuf_c_message_unpack (&ingress__config__descriptor,\n                                allocator, len, data);\n}\nvoid   ingress__config__free_unpacked\n                     (Ingress__Config *message,\n                      ProtobufCAllocator *allocator)\n{\n  if(!message)\n    return;\n  assert(message->base.descriptor == &ingress__config__descriptor);\n  protobuf_c_message_free_unpacked ((ProtobufCMessage*)message, allocator);\n}\nstatic const ProtobufCFieldDescriptor ingress__tag_value_str_list__field_descriptors[1] =\n{\n  {\n    \"value\",\n    1,\n    PROTOBUF_C_LABEL_REPEATED,\n    PROTOBUF_C_TYPE_STRING,\n    offsetof(Ingress__TagValueStrList, n_value),\n    offsetof(Ingress__TagValueStrList, value),\n    NULL,\n    NULL,\n    0,             /* flags */\n    0,NULL,NULL    /* reserved1,reserved2, etc */\n  },\n};\nstatic const unsigned ingress__tag_value_str_list__field_indices_by_name[] = {\n  0,   /* field[0] = value */\n};\nstatic const ProtobufCIntRange ingress__tag_value_str_list__number_ranges[1 + 1] =\n{\n  { 1, 0 },\n  { 0, 1 }\n};\nconst ProtobufCMessageDescriptor ingress__tag_value_str_list__descriptor =\n{\n  PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,\n  \"Ingress.TagValueStrList\",\n  \"TagValueStrList\",\n  \"Ingress__TagValueStrList\",\n  \"Ingress\",\n  sizeof(Ingress__TagValueStrList),\n  1,\n  ingress__tag_value_str_list__field_descriptors,\n  ingress__tag_value_str_list__field_indices_by_name,\n  1,  ingress__tag_value_str_list__number_ranges,\n  (ProtobufCMessageInit) ingress__tag_value_str_list__init,\n  NULL,NULL,NULL    /* reserved[123] */\n};\nstatic const ProtobufCFieldDescriptor ingress__tag_item_condition__field_descriptors[5] =\n{\n  {\n    \"value_str\",\n    1,\n    PROTOBUF_C_LABEL_OPTIONAL,\n    PROTOBUF_C_TYPE_STRING,\n    0,   /* quantifier_offset */\n    offsetof(Ingress__TagItemCondition, value_str),\n    NULL,\n    NULL,\n    0,             /* flags */\n    0,NULL,NULL    /* reserved1,reserved2, etc */\n  },\n  {\n    \"value_list\",\n    2,\n    PROTOBUF_C_LABEL_OPTIONAL,\n    PROTOBUF_C_TYPE_MESSAGE,\n    0,   /* quantifier_offset */\n    offsetof(Ingress__TagItemCondition, value_list),\n    &ingress__tag_value_str_list__descriptor,\n    NULL,\n    0,             /* flags */\n    0,NULL,NULL    /* reserved1,reserved2, etc */\n  },\n  {\n    \"divisor\",\n    3,\n    PROTOBUF_C_LABEL_OPTIONAL,\n    PROTOBUF_C_TYPE_UINT64,\n    offsetof(Ingress__TagItemCondition, has_divisor),\n    offsetof(Ingress__TagItemCondition, divisor),\n    NULL,\n    NULL,\n    0,             /* flags */\n    0,NULL,NULL    /* reserved1,reserved2, etc */\n  },\n  {\n    \"remainder\",\n    4,\n    PROTOBUF_C_LABEL_OPTIONAL,\n    PROTOBUF_C_TYPE_UINT64,\n    offsetof(Ingress__TagItemCondition, has_remainder),\n    offsetof(Ingress__TagItemCondition, remainder),\n    NULL,\n    NULL,\n    0,             /* flags */\n    0,NULL,NULL    /* reserved1,reserved2, etc */\n  },\n  {\n    \"operator\",\n    5,\n    PROTOBUF_C_LABEL_OPTIONAL,\n    PROTOBUF_C_TYPE_ENUM,\n    offsetof(Ingress__TagItemCondition, has_operator_),\n    offsetof(Ingress__TagItemCondition, operator_),\n    &ingress__operator_type__descriptor,\n    NULL,\n    0,             /* flags */\n    0,NULL,NULL    /* reserved1,reserved2, etc */\n  },\n};\nstatic const unsigned ingress__tag_item_condition__field_indices_by_name[] = {\n  2,   /* field[2] = divisor */\n  4,   /* field[4] = operator */\n  3,   /* field[3] = remainder */\n  1,   /* field[1] = value_list */\n  0,   /* field[0] = value_str */\n};\nstatic const ProtobufCIntRange ingress__tag_item_condition__number_ranges[1 + 1] =\n{\n  { 1, 0 },\n  { 0, 5 }\n};\nconst ProtobufCMessageDescriptor ingress__tag_item_condition__descriptor =\n{\n  PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,\n  \"Ingress.TagItemCondition\",\n  \"TagItemCondition\",\n  \"Ingress__TagItemCondition\",\n  \"Ingress\",\n  sizeof(Ingress__TagItemCondition),\n  5,\n  ingress__tag_item_condition__field_descriptors,\n  ingress__tag_item_condition__field_indices_by_name,\n  1,  ingress__tag_item_condition__number_ranges,\n  (ProtobufCMessageInit) ingress__tag_item_condition__init,\n  NULL,NULL,NULL    /* reserved[123] */\n};\nstatic const ProtobufCFieldDescriptor ingress__tag_item__field_descriptors[4] =\n{\n  {\n    \"location\",\n    1,\n    PROTOBUF_C_LABEL_OPTIONAL,\n    PROTOBUF_C_TYPE_ENUM,\n    offsetof(Ingress__TagItem, has_location),\n    offsetof(Ingress__TagItem, location),\n    &ingress__location_type__descriptor,\n    NULL,\n    0,             /* flags */\n    0,NULL,NULL    /* reserved1,reserved2, etc */\n  },\n  {\n    \"key\",\n    2,\n    PROTOBUF_C_LABEL_OPTIONAL,\n    PROTOBUF_C_TYPE_STRING,\n    0,   /* quantifier_offset */\n    offsetof(Ingress__TagItem, key),\n    NULL,\n    NULL,\n    0,             /* flags */\n    0,NULL,NULL    /* reserved1,reserved2, etc */\n  },\n  {\n    \"condition\",\n    3,\n    PROTOBUF_C_LABEL_OPTIONAL,\n    PROTOBUF_C_TYPE_MESSAGE,\n    0,   /* quantifier_offset */\n    offsetof(Ingress__TagItem, condition),\n    &ingress__tag_item_condition__descriptor,\n    NULL,\n    0,             /* flags */\n    0,NULL,NULL    /* reserved1,reserved2, etc */\n  },\n  {\n    \"match_type\",\n    4,\n    PROTOBUF_C_LABEL_OPTIONAL,\n    PROTOBUF_C_TYPE_ENUM,\n    offsetof(Ingress__TagItem, has_match_type),\n    offsetof(Ingress__TagItem, match_type),\n    &ingress__match_type__descriptor,\n    NULL,\n    0,             /* flags */\n    0,NULL,NULL    /* reserved1,reserved2, etc */\n  },\n};\nstatic const unsigned ingress__tag_item__field_indices_by_name[] = {\n  2,   /* field[2] = condition */\n  1,   /* field[1] = key */\n  0,   /* field[0] = location */\n  3,   /* field[3] = match_type */\n};\nstatic const ProtobufCIntRange ingress__tag_item__number_ranges[1 + 1] =\n{\n  { 1, 0 },\n  { 0, 4 }\n};\nconst ProtobufCMessageDescriptor ingress__tag_item__descriptor =\n{\n  PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,\n  \"Ingress.TagItem\",\n  \"TagItem\",\n  \"Ingress__TagItem\",\n  \"Ingress\",\n  sizeof(Ingress__TagItem),\n  4,\n  ingress__tag_item__field_descriptors,\n  ingress__tag_item__field_indices_by_name,\n  1,  ingress__tag_item__number_ranges,\n  (ProtobufCMessageInit) ingress__tag_item__init,\n  NULL,NULL,NULL    /* reserved[123] */\n};\nstatic const ProtobufCFieldDescriptor ingress__tag_rule__field_descriptors[1] =\n{\n  {\n    \"items\",\n    1,\n    PROTOBUF_C_LABEL_REPEATED,\n    PROTOBUF_C_TYPE_MESSAGE,\n    offsetof(Ingress__TagRule, n_items),\n    offsetof(Ingress__TagRule, items),\n    &ingress__tag_item__descriptor,\n    NULL,\n    0,             /* flags */\n    0,NULL,NULL    /* reserved1,reserved2, etc */\n  },\n};\nstatic const unsigned ingress__tag_rule__field_indices_by_name[] = {\n  0,   /* field[0] = items */\n};\nstatic const ProtobufCIntRange ingress__tag_rule__number_ranges[1 + 1] =\n{\n  { 1, 0 },\n  { 0, 1 }\n};\nconst ProtobufCMessageDescriptor ingress__tag_rule__descriptor =\n{\n  PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,\n  \"Ingress.TagRule\",\n  \"TagRule\",\n  \"Ingress__TagRule\",\n  \"Ingress\",\n  sizeof(Ingress__TagRule),\n  1,\n  ingress__tag_rule__field_descriptors,\n  ingress__tag_rule__field_indices_by_name,\n  1,  ingress__tag_rule__number_ranges,\n  (ProtobufCMessageInit) ingress__tag_rule__init,\n  NULL,NULL,NULL    /* reserved[123] */\n};\nstatic const ProtobufCFieldDescriptor ingress__tag_router__field_descriptors[2] =\n{\n  {\n    \"service_name\",\n    1,\n    PROTOBUF_C_LABEL_OPTIONAL,\n    PROTOBUF_C_TYPE_STRING,\n    0,   /* quantifier_offset */\n    offsetof(Ingress__TagRouter, service_name),\n    NULL,\n    NULL,\n    0,             /* flags */\n    0,NULL,NULL    /* reserved1,reserved2, etc */\n  },\n  {\n    \"rules\",\n    2,\n    PROTOBUF_C_LABEL_REPEATED,\n    PROTOBUF_C_TYPE_MESSAGE,\n    offsetof(Ingress__TagRouter, n_rules),\n    offsetof(Ingress__TagRouter, rules),\n    &ingress__tag_rule__descriptor,\n    NULL,\n    0,             /* flags */\n    0,NULL,NULL    /* reserved1,reserved2, etc */\n  },\n};\nstatic const unsigned ingress__tag_router__field_indices_by_name[] = {\n  1,   /* field[1] = rules */\n  0,   /* field[0] = service_name */\n};\nstatic const ProtobufCIntRange ingress__tag_router__number_ranges[1 + 1] =\n{\n  { 1, 0 },\n  { 0, 2 }\n};\nconst ProtobufCMessageDescriptor ingress__tag_router__descriptor =\n{\n  PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,\n  \"Ingress.TagRouter\",\n  \"TagRouter\",\n  \"Ingress__TagRouter\",\n  \"Ingress\",\n  sizeof(Ingress__TagRouter),\n  2,\n  ingress__tag_router__field_descriptors,\n  ingress__tag_router__field_indices_by_name,\n  1,  ingress__tag_router__number_ranges,\n  (ProtobufCMessageInit) ingress__tag_router__init,\n  NULL,NULL,NULL    /* reserved[123] */\n};\nstatic const ProtobufCFieldDescriptor ingress__path_router__field_descriptors[3] =\n{\n  {\n    \"prefix\",\n    1,\n    PROTOBUF_C_LABEL_OPTIONAL,\n    PROTOBUF_C_TYPE_STRING,\n    0,   /* quantifier_offset */\n    offsetof(Ingress__PathRouter, prefix),\n    NULL,\n    NULL,\n    0,             /* flags */\n    0,NULL,NULL    /* reserved1,reserved2, etc */\n  },\n  {\n    \"service_name\",\n    2,\n    PROTOBUF_C_LABEL_OPTIONAL,\n    PROTOBUF_C_TYPE_STRING,\n    0,   /* quantifier_offset */\n    offsetof(Ingress__PathRouter, service_name),\n    NULL,\n    NULL,\n    0,             /* flags */\n    0,NULL,NULL    /* reserved1,reserved2, etc */\n  },\n  {\n    \"tags\",\n    3,\n    PROTOBUF_C_LABEL_REPEATED,\n    PROTOBUF_C_TYPE_MESSAGE,\n    offsetof(Ingress__PathRouter, n_tags),\n    offsetof(Ingress__PathRouter, tags),\n    &ingress__tag_router__descriptor,\n    NULL,\n    0,             /* flags */\n    0,NULL,NULL    /* reserved1,reserved2, etc */\n  },\n};\nstatic const unsigned ingress__path_router__field_indices_by_name[] = {\n  0,   /* field[0] = prefix */\n  1,   /* field[1] = service_name */\n  2,   /* field[2] = tags */\n};\nstatic const ProtobufCIntRange ingress__path_router__number_ranges[1 + 1] =\n{\n  { 1, 0 },\n  { 0, 3 }\n};\nconst ProtobufCMessageDescriptor ingress__path_router__descriptor =\n{\n  PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,\n  \"Ingress.PathRouter\",\n  \"PathRouter\",\n  \"Ingress__PathRouter\",\n  \"Ingress\",\n  sizeof(Ingress__PathRouter),\n  3,\n  ingress__path_router__field_descriptors,\n  ingress__path_router__field_indices_by_name,\n  1,  ingress__path_router__number_ranges,\n  (ProtobufCMessageInit) ingress__path_router__init,\n  NULL,NULL,NULL    /* reserved[123] */\n};\nstatic const ProtobufCFieldDescriptor ingress__host_router__field_descriptors[4] =\n{\n  {\n    \"host\",\n    1,\n    PROTOBUF_C_LABEL_OPTIONAL,\n    PROTOBUF_C_TYPE_STRING,\n    0,   /* quantifier_offset */\n    offsetof(Ingress__HostRouter, host),\n    NULL,\n    NULL,\n    0,             /* flags */\n    0,NULL,NULL    /* reserved1,reserved2, etc */\n  },\n  {\n    \"service_name\",\n    2,\n    PROTOBUF_C_LABEL_OPTIONAL,\n    PROTOBUF_C_TYPE_STRING,\n    0,   /* quantifier_offset */\n    offsetof(Ingress__HostRouter, service_name),\n    NULL,\n    NULL,\n    0,             /* flags */\n    0,NULL,NULL    /* reserved1,reserved2, etc */\n  },\n  {\n    \"paths\",\n    3,\n    PROTOBUF_C_LABEL_REPEATED,\n    PROTOBUF_C_TYPE_MESSAGE,\n    offsetof(Ingress__HostRouter, n_paths),\n    offsetof(Ingress__HostRouter, paths),\n    &ingress__path_router__descriptor,\n    NULL,\n    0,             /* flags */\n    0,NULL,NULL    /* reserved1,reserved2, etc */\n  },\n  {\n    \"tags\",\n    4,\n    PROTOBUF_C_LABEL_REPEATED,\n    PROTOBUF_C_TYPE_MESSAGE,\n    offsetof(Ingress__HostRouter, n_tags),\n    offsetof(Ingress__HostRouter, tags),\n    &ingress__tag_router__descriptor,\n    NULL,\n    0,             /* flags */\n    0,NULL,NULL    /* reserved1,reserved2, etc */\n  },\n};\nstatic const unsigned ingress__host_router__field_indices_by_name[] = {\n  0,   /* field[0] = host */\n  2,   /* field[2] = paths */\n  1,   /* field[1] = service_name */\n  3,   /* field[3] = tags */\n};\nstatic const ProtobufCIntRange ingress__host_router__number_ranges[1 + 1] =\n{\n  { 1, 0 },\n  { 0, 4 }\n};\nconst ProtobufCMessageDescriptor ingress__host_router__descriptor =\n{\n  PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,\n  \"Ingress.HostRouter\",\n  \"HostRouter\",\n  \"Ingress__HostRouter\",\n  \"Ingress\",\n  sizeof(Ingress__HostRouter),\n  4,\n  ingress__host_router__field_descriptors,\n  ingress__host_router__field_indices_by_name,\n  1,  ingress__host_router__number_ranges,\n  (ProtobufCMessageInit) ingress__host_router__init,\n  NULL,NULL,NULL    /* reserved[123] */\n};\nstatic const ProtobufCFieldDescriptor ingress__appname_router__field_descriptors[3] =\n{\n  {\n    \"appname\",\n    1,\n    PROTOBUF_C_LABEL_OPTIONAL,\n    PROTOBUF_C_TYPE_STRING,\n    0,   /* quantifier_offset */\n    offsetof(Ingress__AppnameRouter, appname),\n    NULL,\n    NULL,\n    0,             /* flags */\n    0,NULL,NULL    /* reserved1,reserved2, etc */\n  },\n  {\n    \"service_name\",\n    2,\n    PROTOBUF_C_LABEL_OPTIONAL,\n    PROTOBUF_C_TYPE_STRING,\n    0,   /* quantifier_offset */\n    offsetof(Ingress__AppnameRouter, service_name),\n    NULL,\n    NULL,\n    0,             /* flags */\n    0,NULL,NULL    /* reserved1,reserved2, etc */\n  },\n  {\n    \"tags\",\n    3,\n    PROTOBUF_C_LABEL_REPEATED,\n    PROTOBUF_C_TYPE_MESSAGE,\n    offsetof(Ingress__AppnameRouter, n_tags),\n    offsetof(Ingress__AppnameRouter, tags),\n    &ingress__tag_router__descriptor,\n    NULL,\n    0,             /* flags */\n    0,NULL,NULL    /* reserved1,reserved2, etc */\n  },\n};\nstatic const unsigned ingress__appname_router__field_indices_by_name[] = {\n  0,   /* field[0] = appname */\n  1,   /* field[1] = service_name */\n  2,   /* field[2] = tags */\n};\nstatic const ProtobufCIntRange ingress__appname_router__number_ranges[1 + 1] =\n{\n  { 1, 0 },\n  { 0, 3 }\n};\nconst ProtobufCMessageDescriptor ingress__appname_router__descriptor =\n{\n  PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,\n  \"Ingress.AppnameRouter\",\n  \"AppnameRouter\",\n  \"Ingress__AppnameRouter\",\n  \"Ingress\",\n  sizeof(Ingress__AppnameRouter),\n  3,\n  ingress__appname_router__field_descriptors,\n  ingress__appname_router__field_indices_by_name,\n  1,  ingress__appname_router__number_ranges,\n  (ProtobufCMessageInit) ingress__appname_router__init,\n  NULL,NULL,NULL    /* reserved[123] */\n};\nstatic const ProtobufCFieldDescriptor ingress__apirouter__field_descriptors[3] =\n{\n  {\n    \"service_name\",\n    1,\n    PROTOBUF_C_LABEL_OPTIONAL,\n    PROTOBUF_C_TYPE_STRING,\n    0,   /* quantifier_offset */\n    offsetof(Ingress__APIRouter, service_name),\n    NULL,\n    NULL,\n    0,             /* flags */\n    0,NULL,NULL    /* reserved1,reserved2, etc */\n  },\n  {\n    \"api\",\n    2,\n    PROTOBUF_C_LABEL_OPTIONAL,\n    PROTOBUF_C_TYPE_STRING,\n    0,   /* quantifier_offset */\n    offsetof(Ingress__APIRouter, api),\n    NULL,\n    NULL,\n    0,             /* flags */\n    0,NULL,NULL    /* reserved1,reserved2, etc */\n  },\n  {\n    \"tags\",\n    3,\n    PROTOBUF_C_LABEL_REPEATED,\n    PROTOBUF_C_TYPE_MESSAGE,\n    offsetof(Ingress__APIRouter, n_tags),\n    offsetof(Ingress__APIRouter, tags),\n    &ingress__tag_router__descriptor,\n    NULL,\n    0,             /* flags */\n    0,NULL,NULL    /* reserved1,reserved2, etc */\n  },\n};\nstatic const unsigned ingress__apirouter__field_indices_by_name[] = {\n  1,   /* field[1] = api */\n  0,   /* field[0] = service_name */\n  2,   /* field[2] = tags */\n};\nstatic const ProtobufCIntRange ingress__apirouter__number_ranges[1 + 1] =\n{\n  { 1, 0 },\n  { 0, 3 }\n};\nconst ProtobufCMessageDescriptor ingress__apirouter__descriptor =\n{\n  PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,\n  \"Ingress.APIRouter\",\n  \"APIRouter\",\n  \"Ingress__APIRouter\",\n  \"Ingress\",\n  sizeof(Ingress__APIRouter),\n  3,\n  ingress__apirouter__field_descriptors,\n  ingress__apirouter__field_indices_by_name,\n  1,  ingress__apirouter__number_ranges,\n  (ProtobufCMessageInit) ingress__apirouter__init,\n  NULL,NULL,NULL    /* reserved[123] */\n};\nstatic const ProtobufCFieldDescriptor ingress__router__field_descriptors[3] =\n{\n  {\n    \"host_router\",\n    1,\n    PROTOBUF_C_LABEL_OPTIONAL,\n    PROTOBUF_C_TYPE_MESSAGE,\n    0,   /* quantifier_offset */\n    offsetof(Ingress__Router, host_router),\n    &ingress__host_router__descriptor,\n    NULL,\n    0,             /* flags */\n    0,NULL,NULL    /* reserved1,reserved2, etc */\n  },\n  {\n    \"appname_router\",\n    2,\n    PROTOBUF_C_LABEL_OPTIONAL,\n    PROTOBUF_C_TYPE_MESSAGE,\n    0,   /* quantifier_offset */\n    offsetof(Ingress__Router, appname_router),\n    &ingress__appname_router__descriptor,\n    NULL,\n    0,             /* flags */\n    0,NULL,NULL    /* reserved1,reserved2, etc */\n  },\n  {\n    \"api_router\",\n    3,\n    PROTOBUF_C_LABEL_OPTIONAL,\n    PROTOBUF_C_TYPE_MESSAGE,\n    0,   /* quantifier_offset */\n    offsetof(Ingress__Router, api_router),\n    &ingress__apirouter__descriptor,\n    NULL,\n    0,             /* flags */\n    0,NULL,NULL    /* reserved1,reserved2, etc */\n  },\n};\nstatic const unsigned ingress__router__field_indices_by_name[] = {\n  2,   /* field[2] = api_router */\n  1,   /* field[1] = appname_router */\n  0,   /* field[0] = host_router */\n};\nstatic const ProtobufCIntRange ingress__router__number_ranges[1 + 1] =\n{\n  { 1, 0 },\n  { 0, 3 }\n};\nconst ProtobufCMessageDescriptor ingress__router__descriptor =\n{\n  PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,\n  \"Ingress.Router\",\n  \"Router\",\n  \"Ingress__Router\",\n  \"Ingress\",\n  sizeof(Ingress__Router),\n  3,\n  ingress__router__field_descriptors,\n  ingress__router__field_indices_by_name,\n  1,  ingress__router__number_ranges,\n  (ProtobufCMessageInit) ingress__router__init,\n  NULL,NULL,NULL    /* reserved[123] */\n};\nstatic const ProtobufCFieldDescriptor ingress__timeout__field_descriptors[3] =\n{\n  {\n    \"connect_timeout\",\n    1,\n    PROTOBUF_C_LABEL_OPTIONAL,\n    PROTOBUF_C_TYPE_UINT32,\n    offsetof(Ingress__Timeout, has_connect_timeout),\n    offsetof(Ingress__Timeout, connect_timeout),\n    NULL,\n    NULL,\n    0,             /* flags */\n    0,NULL,NULL    /* reserved1,reserved2, etc */\n  },\n  {\n    \"read_timeout\",\n    2,\n    PROTOBUF_C_LABEL_OPTIONAL,\n    PROTOBUF_C_TYPE_UINT32,\n    offsetof(Ingress__Timeout, has_read_timeout),\n    offsetof(Ingress__Timeout, read_timeout),\n    NULL,\n    NULL,\n    0,             /* flags */\n    0,NULL,NULL    /* reserved1,reserved2, etc */\n  },\n  {\n    \"write_timeout\",\n    3,\n    PROTOBUF_C_LABEL_OPTIONAL,\n    PROTOBUF_C_TYPE_UINT32,\n    offsetof(Ingress__Timeout, has_write_timeout),\n    offsetof(Ingress__Timeout, write_timeout),\n    NULL,\n    NULL,\n    0,             /* flags */\n    0,NULL,NULL    /* reserved1,reserved2, etc */\n  },\n};\nstatic const unsigned ingress__timeout__field_indices_by_name[] = {\n  0,   /* field[0] = connect_timeout */\n  1,   /* field[1] = read_timeout */\n  2,   /* field[2] = write_timeout */\n};\nstatic const ProtobufCIntRange ingress__timeout__number_ranges[1 + 1] =\n{\n  { 1, 0 },\n  { 0, 3 }\n};\nconst ProtobufCMessageDescriptor ingress__timeout__descriptor =\n{\n  PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,\n  \"Ingress.Timeout\",\n  \"Timeout\",\n  \"Ingress__Timeout\",\n  \"Ingress\",\n  sizeof(Ingress__Timeout),\n  3,\n  ingress__timeout__field_descriptors,\n  ingress__timeout__field_indices_by_name,\n  1,  ingress__timeout__number_ranges,\n  (ProtobufCMessageInit) ingress__timeout__init,\n  NULL,NULL,NULL    /* reserved[123] */\n};\nstatic const ProtobufCFieldDescriptor ingress__upstream__field_descriptors[2] =\n{\n  {\n    \"target\",\n    1,\n    PROTOBUF_C_LABEL_OPTIONAL,\n    PROTOBUF_C_TYPE_STRING,\n    0,   /* quantifier_offset */\n    offsetof(Ingress__Upstream, target),\n    NULL,\n    NULL,\n    0,             /* flags */\n    0,NULL,NULL    /* reserved1,reserved2, etc */\n  },\n  {\n    \"weight\",\n    2,\n    PROTOBUF_C_LABEL_OPTIONAL,\n    PROTOBUF_C_TYPE_UINT32,\n    offsetof(Ingress__Upstream, has_weight),\n    offsetof(Ingress__Upstream, weight),\n    NULL,\n    NULL,\n    0,             /* flags */\n    0,NULL,NULL    /* reserved1,reserved2, etc */\n  },\n};\nstatic const unsigned ingress__upstream__field_indices_by_name[] = {\n  0,   /* field[0] = target */\n  1,   /* field[1] = weight */\n};\nstatic const ProtobufCIntRange ingress__upstream__number_ranges[1 + 1] =\n{\n  { 1, 0 },\n  { 0, 2 }\n};\nconst ProtobufCMessageDescriptor ingress__upstream__descriptor =\n{\n  PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,\n  \"Ingress.Upstream\",\n  \"Upstream\",\n  \"Ingress__Upstream\",\n  \"Ingress\",\n  sizeof(Ingress__Upstream),\n  2,\n  ingress__upstream__field_descriptors,\n  ingress__upstream__field_indices_by_name,\n  1,  ingress__upstream__number_ranges,\n  (ProtobufCMessageInit) ingress__upstream__init,\n  NULL,NULL,NULL    /* reserved[123] */\n};\nstatic const ProtobufCFieldDescriptor ingress__metadata__field_descriptors[2] =\n{\n  {\n    \"key\",\n    1,\n    PROTOBUF_C_LABEL_OPTIONAL,\n    PROTOBUF_C_TYPE_STRING,\n    0,   /* quantifier_offset */\n    offsetof(Ingress__Metadata, key),\n    NULL,\n    NULL,\n    0,             /* flags */\n    0,NULL,NULL    /* reserved1,reserved2, etc */\n  },\n  {\n    \"value\",\n    2,\n    PROTOBUF_C_LABEL_OPTIONAL,\n    PROTOBUF_C_TYPE_STRING,\n    0,   /* quantifier_offset */\n    offsetof(Ingress__Metadata, value),\n    NULL,\n    NULL,\n    0,             /* flags */\n    0,NULL,NULL    /* reserved1,reserved2, etc */\n  },\n};\nstatic const unsigned ingress__metadata__field_indices_by_name[] = {\n  0,   /* field[0] = key */\n  1,   /* field[1] = value */\n};\nstatic const ProtobufCIntRange ingress__metadata__number_ranges[1 + 1] =\n{\n  { 1, 0 },\n  { 0, 2 }\n};\nconst ProtobufCMessageDescriptor ingress__metadata__descriptor =\n{\n  PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,\n  \"Ingress.Metadata\",\n  \"Metadata\",\n  \"Ingress__Metadata\",\n  \"Ingress\",\n  sizeof(Ingress__Metadata),\n  2,\n  ingress__metadata__field_descriptors,\n  ingress__metadata__field_indices_by_name,\n  1,  ingress__metadata__number_ranges,\n  (ProtobufCMessageInit) ingress__metadata__init,\n  NULL,NULL,NULL    /* reserved[123] */\n};\nstatic const ProtobufCFieldDescriptor ingress__action__field_descriptors[4] =\n{\n  {\n    \"action_type\",\n    1,\n    PROTOBUF_C_LABEL_OPTIONAL,\n    PROTOBUF_C_TYPE_ENUM,\n    offsetof(Ingress__Action, has_action_type),\n    offsetof(Ingress__Action, action_type),\n    &ingress__action_type__descriptor,\n    NULL,\n    0,             /* flags */\n    0,NULL,NULL    /* reserved1,reserved2, etc */\n  },\n  {\n    \"value_type\",\n    2,\n    PROTOBUF_C_LABEL_OPTIONAL,\n    PROTOBUF_C_TYPE_ENUM,\n    offsetof(Ingress__Action, has_value_type),\n    offsetof(Ingress__Action, value_type),\n    &ingress__action_value_type__descriptor,\n    NULL,\n    0,             /* flags */\n    0,NULL,NULL    /* reserved1,reserved2, etc */\n  },\n  {\n    \"key\",\n    3,\n    PROTOBUF_C_LABEL_OPTIONAL,\n    PROTOBUF_C_TYPE_STRING,\n    0,   /* quantifier_offset */\n    offsetof(Ingress__Action, key),\n    NULL,\n    NULL,\n    0,             /* flags */\n    0,NULL,NULL    /* reserved1,reserved2, etc */\n  },\n  {\n    \"value\",\n    4,\n    PROTOBUF_C_LABEL_OPTIONAL,\n    PROTOBUF_C_TYPE_STRING,\n    0,   /* quantifier_offset */\n    offsetof(Ingress__Action, value),\n    NULL,\n    NULL,\n    0,             /* flags */\n    0,NULL,NULL    /* reserved1,reserved2, etc */\n  },\n};\nstatic const unsigned ingress__action__field_indices_by_name[] = {\n  0,   /* field[0] = action_type */\n  2,   /* field[2] = key */\n  3,   /* field[3] = value */\n  1,   /* field[1] = value_type */\n};\nstatic const ProtobufCIntRange ingress__action__number_ranges[1 + 1] =\n{\n  { 1, 0 },\n  { 0, 4 }\n};\nconst ProtobufCMessageDescriptor ingress__action__descriptor =\n{\n  PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,\n  \"Ingress.Action\",\n  \"Action\",\n  \"Ingress__Action\",\n  \"Ingress\",\n  sizeof(Ingress__Action),\n  4,\n  ingress__action__field_descriptors,\n  ingress__action__field_indices_by_name,\n  1,  ingress__action__number_ranges,\n  (ProtobufCMessageInit) ingress__action__init,\n  NULL,NULL,NULL    /* reserved[123] */\n};\nstatic const ProtobufCFieldDescriptor ingress__virtual_service__field_descriptors[6] =\n{\n  {\n    \"service_name\",\n    1,\n    PROTOBUF_C_LABEL_OPTIONAL,\n    PROTOBUF_C_TYPE_STRING,\n    0,   /* quantifier_offset */\n    offsetof(Ingress__VirtualService, service_name),\n    NULL,\n    NULL,\n    0,             /* flags */\n    0,NULL,NULL    /* reserved1,reserved2, etc */\n  },\n  {\n    \"upstreams\",\n    2,\n    PROTOBUF_C_LABEL_REPEATED,\n    PROTOBUF_C_TYPE_MESSAGE,\n    offsetof(Ingress__VirtualService, n_upstreams),\n    offsetof(Ingress__VirtualService, upstreams),\n    &ingress__upstream__descriptor,\n    NULL,\n    0,             /* flags */\n    0,NULL,NULL    /* reserved1,reserved2, etc */\n  },\n  {\n    \"timeout_ms\",\n    3,\n    PROTOBUF_C_LABEL_OPTIONAL,\n    PROTOBUF_C_TYPE_MESSAGE,\n    0,   /* quantifier_offset */\n    offsetof(Ingress__VirtualService, timeout_ms),\n    &ingress__timeout__descriptor,\n    NULL,\n    0,             /* flags */\n    0,NULL,NULL    /* reserved1,reserved2, etc */\n  },\n  {\n    \"force_https\",\n    4,\n    PROTOBUF_C_LABEL_OPTIONAL,\n    PROTOBUF_C_TYPE_BOOL,\n    offsetof(Ingress__VirtualService, has_force_https),\n    offsetof(Ingress__VirtualService, force_https),\n    NULL,\n    NULL,\n    0,             /* flags */\n    0,NULL,NULL    /* reserved1,reserved2, etc */\n  },\n  {\n    \"metadata\",\n    5,\n    PROTOBUF_C_LABEL_REPEATED,\n    PROTOBUF_C_TYPE_MESSAGE,\n    offsetof(Ingress__VirtualService, n_metadata),\n    offsetof(Ingress__VirtualService, metadata),\n    &ingress__metadata__descriptor,\n    NULL,\n    0,             /* flags */\n    0,NULL,NULL    /* reserved1,reserved2, etc */\n  },\n  {\n    \"action\",\n    6,\n    PROTOBUF_C_LABEL_REPEATED,\n    PROTOBUF_C_TYPE_MESSAGE,\n    offsetof(Ingress__VirtualService, n_action),\n    offsetof(Ingress__VirtualService, action),\n    &ingress__action__descriptor,\n    NULL,\n    0,             /* flags */\n    0,NULL,NULL    /* reserved1,reserved2, etc */\n  },\n};\nstatic const unsigned ingress__virtual_service__field_indices_by_name[] = {\n  5,   /* field[5] = action */\n  3,   /* field[3] = force_https */\n  4,   /* field[4] = metadata */\n  0,   /* field[0] = service_name */\n  2,   /* field[2] = timeout_ms */\n  1,   /* field[1] = upstreams */\n};\nstatic const ProtobufCIntRange ingress__virtual_service__number_ranges[1 + 1] =\n{\n  { 1, 0 },\n  { 0, 6 }\n};\nconst ProtobufCMessageDescriptor ingress__virtual_service__descriptor =\n{\n  PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,\n  \"Ingress.VirtualService\",\n  \"VirtualService\",\n  \"Ingress__VirtualService\",\n  \"Ingress\",\n  sizeof(Ingress__VirtualService),\n  6,\n  ingress__virtual_service__field_descriptors,\n  ingress__virtual_service__field_indices_by_name,\n  1,  ingress__virtual_service__number_ranges,\n  (ProtobufCMessageInit) ingress__virtual_service__init,\n  NULL,NULL,NULL    /* reserved[123] */\n};\nstatic const ProtobufCFieldDescriptor ingress__config__field_descriptors[2] =\n{\n  {\n    \"routers\",\n    1,\n    PROTOBUF_C_LABEL_REPEATED,\n    PROTOBUF_C_TYPE_MESSAGE,\n    offsetof(Ingress__Config, n_routers),\n    offsetof(Ingress__Config, routers),\n    &ingress__router__descriptor,\n    NULL,\n    0,             /* flags */\n    0,NULL,NULL    /* reserved1,reserved2, etc */\n  },\n  {\n    \"services\",\n    2,\n    PROTOBUF_C_LABEL_REPEATED,\n    PROTOBUF_C_TYPE_MESSAGE,\n    offsetof(Ingress__Config, n_services),\n    offsetof(Ingress__Config, services),\n    &ingress__virtual_service__descriptor,\n    NULL,\n    0,             /* flags */\n    0,NULL,NULL    /* reserved1,reserved2, etc */\n  },\n};\nstatic const unsigned ingress__config__field_indices_by_name[] = {\n  0,   /* field[0] = routers */\n  1,   /* field[1] = services */\n};\nstatic const ProtobufCIntRange ingress__config__number_ranges[1 + 1] =\n{\n  { 1, 0 },\n  { 0, 2 }\n};\nconst ProtobufCMessageDescriptor ingress__config__descriptor =\n{\n  PROTOBUF_C__MESSAGE_DESCRIPTOR_MAGIC,\n  \"Ingress.Config\",\n  \"Config\",\n  \"Ingress__Config\",\n  \"Ingress\",\n  sizeof(Ingress__Config),\n  2,\n  ingress__config__field_descriptors,\n  ingress__config__field_indices_by_name,\n  1,  ingress__config__number_ranges,\n  (ProtobufCMessageInit) ingress__config__init,\n  NULL,NULL,NULL    /* reserved[123] */\n};\nstatic const ProtobufCEnumValue ingress__location_type__enum_values_by_number[6] =\n{\n  { \"LocUnDefined\", \"INGRESS__LOCATION_TYPE__LocUnDefined\", 0 },\n  { \"LocHttpHeader\", \"INGRESS__LOCATION_TYPE__LocHttpHeader\", 1 },\n  { \"LocHttpQuery\", \"INGRESS__LOCATION_TYPE__LocHttpQuery\", 2 },\n  { \"LocNginxVar\", \"INGRESS__LOCATION_TYPE__LocNginxVar\", 3 },\n  { \"LocXBizInfo\", \"INGRESS__LOCATION_TYPE__LocXBizInfo\", 4 },\n  { \"LocHttpCookie\", \"INGRESS__LOCATION_TYPE__LocHttpCookie\", 5 },\n};\nstatic const ProtobufCIntRange ingress__location_type__value_ranges[] = {\n{0, 0},{0, 6}\n};\nstatic const ProtobufCEnumValueIndex ingress__location_type__enum_values_by_name[6] =\n{\n  { \"LocHttpCookie\", 5 },\n  { \"LocHttpHeader\", 1 },\n  { \"LocHttpQuery\", 2 },\n  { \"LocNginxVar\", 3 },\n  { \"LocUnDefined\", 0 },\n  { \"LocXBizInfo\", 4 },\n};\nconst ProtobufCEnumDescriptor ingress__location_type__descriptor =\n{\n  PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC,\n  \"Ingress.LocationType\",\n  \"LocationType\",\n  \"Ingress__LocationType\",\n  \"Ingress\",\n  6,\n  ingress__location_type__enum_values_by_number,\n  6,\n  ingress__location_type__enum_values_by_name,\n  1,\n  ingress__location_type__value_ranges,\n  NULL,NULL,NULL,NULL   /* reserved[1234] */\n};\nstatic const ProtobufCEnumValue ingress__match_type__enum_values_by_number[4] =\n{\n  { \"MatchUnDefined\", \"INGRESS__MATCH_TYPE__MatchUnDefined\", 0 },\n  { \"WholeMatch\", \"INGRESS__MATCH_TYPE__WholeMatch\", 1 },\n  { \"StrListInMatch\", \"INGRESS__MATCH_TYPE__StrListInMatch\", 2 },\n  { \"ModCompare\", \"INGRESS__MATCH_TYPE__ModCompare\", 3 },\n};\nstatic const ProtobufCIntRange ingress__match_type__value_ranges[] = {\n{0, 0},{0, 4}\n};\nstatic const ProtobufCEnumValueIndex ingress__match_type__enum_values_by_name[4] =\n{\n  { \"MatchUnDefined\", 0 },\n  { \"ModCompare\", 3 },\n  { \"StrListInMatch\", 2 },\n  { \"WholeMatch\", 1 },\n};\nconst ProtobufCEnumDescriptor ingress__match_type__descriptor =\n{\n  PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC,\n  \"Ingress.MatchType\",\n  \"MatchType\",\n  \"Ingress__MatchType\",\n  \"Ingress\",\n  4,\n  ingress__match_type__enum_values_by_number,\n  4,\n  ingress__match_type__enum_values_by_name,\n  1,\n  ingress__match_type__value_ranges,\n  NULL,NULL,NULL,NULL   /* reserved[1234] */\n};\nstatic const ProtobufCEnumValue ingress__operator_type__enum_values_by_number[6] =\n{\n  { \"OperatorUnDefined\", \"INGRESS__OPERATOR_TYPE__OperatorUnDefined\", 0 },\n  { \"OperatorEqual\", \"INGRESS__OPERATOR_TYPE__OperatorEqual\", 1 },\n  { \"OperatorGreater\", \"INGRESS__OPERATOR_TYPE__OperatorGreater\", 2 },\n  { \"OperatorLess\", \"INGRESS__OPERATOR_TYPE__OperatorLess\", 3 },\n  { \"OperatorGreaterEqual\", \"INGRESS__OPERATOR_TYPE__OperatorGreaterEqual\", 4 },\n  { \"OperatorLessEqual\", \"INGRESS__OPERATOR_TYPE__OperatorLessEqual\", 5 },\n};\nstatic const ProtobufCIntRange ingress__operator_type__value_ranges[] = {\n{0, 0},{0, 6}\n};\nstatic const ProtobufCEnumValueIndex ingress__operator_type__enum_values_by_name[6] =\n{\n  { \"OperatorEqual\", 1 },\n  { \"OperatorGreater\", 2 },\n  { \"OperatorGreaterEqual\", 4 },\n  { \"OperatorLess\", 3 },\n  { \"OperatorLessEqual\", 5 },\n  { \"OperatorUnDefined\", 0 },\n};\nconst ProtobufCEnumDescriptor ingress__operator_type__descriptor =\n{\n  PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC,\n  \"Ingress.OperatorType\",\n  \"OperatorType\",\n  \"Ingress__OperatorType\",\n  \"Ingress\",\n  6,\n  ingress__operator_type__enum_values_by_number,\n  6,\n  ingress__operator_type__enum_values_by_name,\n  1,\n  ingress__operator_type__value_ranges,\n  NULL,NULL,NULL,NULL   /* reserved[1234] */\n};\nstatic const ProtobufCEnumValue ingress__action_type__enum_values_by_number[6] =\n{\n  { \"ActionUnDefined\", \"INGRESS__ACTION_TYPE__ActionUnDefined\", 0 },\n  { \"ActionAddReqHeader\", \"INGRESS__ACTION_TYPE__ActionAddReqHeader\", 1 },\n  { \"ActionAppendReqHeader\", \"INGRESS__ACTION_TYPE__ActionAppendReqHeader\", 2 },\n  { \"ActionAddRespHeader\", \"INGRESS__ACTION_TYPE__ActionAddRespHeader\", 3 },\n  { \"ActionAppendRespHeader\", \"INGRESS__ACTION_TYPE__ActionAppendRespHeader\", 4 },\n  { \"ActionAddParam\", \"INGRESS__ACTION_TYPE__ActionAddParam\", 5 },\n};\nstatic const ProtobufCIntRange ingress__action_type__value_ranges[] = {\n{0, 0},{0, 6}\n};\nstatic const ProtobufCEnumValueIndex ingress__action_type__enum_values_by_name[6] =\n{\n  { \"ActionAddParam\", 5 },\n  { \"ActionAddReqHeader\", 1 },\n  { \"ActionAddRespHeader\", 3 },\n  { \"ActionAppendReqHeader\", 2 },\n  { \"ActionAppendRespHeader\", 4 },\n  { \"ActionUnDefined\", 0 },\n};\nconst ProtobufCEnumDescriptor ingress__action_type__descriptor =\n{\n  PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC,\n  \"Ingress.ActionType\",\n  \"ActionType\",\n  \"Ingress__ActionType\",\n  \"Ingress\",\n  6,\n  ingress__action_type__enum_values_by_number,\n  6,\n  ingress__action_type__enum_values_by_name,\n  1,\n  ingress__action_type__value_ranges,\n  NULL,NULL,NULL,NULL   /* reserved[1234] */\n};\nstatic const ProtobufCEnumValue ingress__action_value_type__enum_values_by_number[3] =\n{\n  { \"ActionValueUnDefined\", \"INGRESS__ACTION_VALUE_TYPE__ActionValueUnDefined\", 0 },\n  { \"ActionStaticValue\", \"INGRESS__ACTION_VALUE_TYPE__ActionStaticValue\", 1 },\n  { \"ActionDynamicValue\", \"INGRESS__ACTION_VALUE_TYPE__ActionDynamicValue\", 2 },\n};\nstatic const ProtobufCIntRange ingress__action_value_type__value_ranges[] = {\n{0, 0},{0, 3}\n};\nstatic const ProtobufCEnumValueIndex ingress__action_value_type__enum_values_by_name[3] =\n{\n  { \"ActionDynamicValue\", 2 },\n  { \"ActionStaticValue\", 1 },\n  { \"ActionValueUnDefined\", 0 },\n};\nconst ProtobufCEnumDescriptor ingress__action_value_type__descriptor =\n{\n  PROTOBUF_C__ENUM_DESCRIPTOR_MAGIC,\n  \"Ingress.ActionValueType\",\n  \"ActionValueType\",\n  \"Ingress__ActionValueType\",\n  \"Ingress\",\n  3,\n  ingress__action_value_type__enum_values_by_number,\n  3,\n  ingress__action_value_type__enum_values_by_name,\n  1,\n  ingress__action_value_type__value_ranges,\n  NULL,NULL,NULL,NULL   /* reserved[1234] */\n};\n"
  },
  {
    "path": "modules/ngx_ingress_module/ingress.pb-c.h",
    "content": "/* Generated by the protocol buffer compiler.  DO NOT EDIT! */\n/* Generated from: ingress.proto */\n\n#ifndef PROTOBUF_C_ingress_2eproto__INCLUDED\n#define PROTOBUF_C_ingress_2eproto__INCLUDED\n\n#include <protobuf-c/protobuf-c.h>\n\nPROTOBUF_C__BEGIN_DECLS\n\n#if PROTOBUF_C_VERSION_NUMBER < 1000000\n# error This file was generated by a newer version of protoc-c which is incompatible with your libprotobuf-c headers. Please update your headers.\n#elif 1003003 < PROTOBUF_C_MIN_COMPILER_VERSION\n# error This file was generated by an older version of protoc-c which is incompatible with your libprotobuf-c headers. Please regenerate this file with a newer version of protoc-c.\n#endif\n\n\ntypedef struct _Ingress__TagValueStrList Ingress__TagValueStrList;\ntypedef struct _Ingress__TagItemCondition Ingress__TagItemCondition;\ntypedef struct _Ingress__TagItem Ingress__TagItem;\ntypedef struct _Ingress__TagRule Ingress__TagRule;\ntypedef struct _Ingress__TagRouter Ingress__TagRouter;\ntypedef struct _Ingress__PathRouter Ingress__PathRouter;\ntypedef struct _Ingress__HostRouter Ingress__HostRouter;\ntypedef struct _Ingress__AppnameRouter Ingress__AppnameRouter;\ntypedef struct _Ingress__APIRouter Ingress__APIRouter;\ntypedef struct _Ingress__Router Ingress__Router;\ntypedef struct _Ingress__Timeout Ingress__Timeout;\ntypedef struct _Ingress__Upstream Ingress__Upstream;\ntypedef struct _Ingress__Metadata Ingress__Metadata;\ntypedef struct _Ingress__Action Ingress__Action;\ntypedef struct _Ingress__VirtualService Ingress__VirtualService;\ntypedef struct _Ingress__Config Ingress__Config;\n\n\n/* --- enums --- */\n\ntypedef enum _Ingress__LocationType {\n  /*\n   * first element must be zero\n   */\n  INGRESS__LOCATION_TYPE__LocUnDefined = 0,\n  /*\n   * Tag from http header\n   */\n  INGRESS__LOCATION_TYPE__LocHttpHeader = 1,\n  /*\n   * Tag from http query \n   */\n  INGRESS__LOCATION_TYPE__LocHttpQuery = 2,\n  /*\n   * Tag from nginx var \n   */\n  INGRESS__LOCATION_TYPE__LocNginxVar = 3,\n  /*\n   * Tag from x-biz-info \n   */\n  INGRESS__LOCATION_TYPE__LocXBizInfo = 4,\n  /*\n   * Tag from http cookie\n   */\n  INGRESS__LOCATION_TYPE__LocHttpCookie = 5\n    PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(INGRESS__LOCATION_TYPE)\n} Ingress__LocationType;\ntypedef enum _Ingress__MatchType {\n  /*\n   * first element must be zero\n   */\n  INGRESS__MATCH_TYPE__MatchUnDefined = 0,\n  /*\n   * String matches exactly\n   */\n  INGRESS__MATCH_TYPE__WholeMatch = 1,\n  /*\n   * String list match\n   */\n  INGRESS__MATCH_TYPE__StrListInMatch = 2,\n  /*\n   * mod result compare value\n   */\n  INGRESS__MATCH_TYPE__ModCompare = 3\n    PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(INGRESS__MATCH_TYPE)\n} Ingress__MatchType;\ntypedef enum _Ingress__OperatorType {\n  /*\n   * first element must be zero\n   */\n  INGRESS__OPERATOR_TYPE__OperatorUnDefined = 0,\n  /*\n   * equal operation\n   */\n  INGRESS__OPERATOR_TYPE__OperatorEqual = 1,\n  /*\n   * greater operation\n   */\n  INGRESS__OPERATOR_TYPE__OperatorGreater = 2,\n  /*\n   * less operation\n   */\n  INGRESS__OPERATOR_TYPE__OperatorLess = 3,\n  /*\n   * greater or equal \n   */\n  INGRESS__OPERATOR_TYPE__OperatorGreaterEqual = 4,\n  /*\n   * less or equal\n   */\n  INGRESS__OPERATOR_TYPE__OperatorLessEqual = 5\n    PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(INGRESS__OPERATOR_TYPE)\n} Ingress__OperatorType;\ntypedef enum _Ingress__ActionType {\n  /*\n   * first element must be zero\n   */\n  INGRESS__ACTION_TYPE__ActionUnDefined = 0,\n  /*\n   * Action add http request header, add action do not care about duplicate\n   */\n  INGRESS__ACTION_TYPE__ActionAddReqHeader = 1,\n  /*\n   * Action append http request header\n   */\n  INGRESS__ACTION_TYPE__ActionAppendReqHeader = 2,\n  /*\n   * Action add http response header\n   */\n  INGRESS__ACTION_TYPE__ActionAddRespHeader = 3,\n  /*\n   * Action append http response header\n   */\n  INGRESS__ACTION_TYPE__ActionAppendRespHeader = 4,\n  /*\n   * Action add http request query param\n   */\n  INGRESS__ACTION_TYPE__ActionAddParam = 5\n    PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(INGRESS__ACTION_TYPE)\n} Ingress__ActionType;\ntypedef enum _Ingress__ActionValueType {\n  /*\n   *first element must be zero\n   */\n  INGRESS__ACTION_VALUE_TYPE__ActionValueUnDefined = 0,\n  /*\n   * value from configure\n   */\n  INGRESS__ACTION_VALUE_TYPE__ActionStaticValue = 1,\n  /*\n   * value from nginx var\n   */\n  INGRESS__ACTION_VALUE_TYPE__ActionDynamicValue = 2\n    PROTOBUF_C__FORCE_ENUM_TO_BE_INT_SIZE(INGRESS__ACTION_VALUE_TYPE)\n} Ingress__ActionValueType;\n\n/* --- messages --- */\n\nstruct  _Ingress__TagValueStrList\n{\n  ProtobufCMessage base;\n  /*\n   * string list  \n   */\n  size_t n_value;\n  char **value;\n};\n#define INGRESS__TAG_VALUE_STR_LIST__INIT \\\n { PROTOBUF_C_MESSAGE_INIT (&ingress__tag_value_str_list__descriptor) \\\n    , 0,NULL }\n\n\nstruct  _Ingress__TagItemCondition\n{\n  ProtobufCMessage base;\n  /*\n   * string match value, for WholeMatch,PrefixMatch,SuffixMatch, and RegMatch\n   */\n  char *value_str;\n  /*\n   * string list for match, for StrListInMatch\n   */\n  Ingress__TagValueStrList *value_list;\n  /*\n   * mode divisor, for ModCompare\n   */\n  protobuf_c_boolean has_divisor;\n  uint64_t divisor;\n  /*\n   * compare remainder, for ModCompare\n   */\n  protobuf_c_boolean has_remainder;\n  uint64_t remainder;\n  /*\n   * >, <, =, >=, <=, for ModCompare\n   */\n  protobuf_c_boolean has_operator_;\n  Ingress__OperatorType operator_;\n};\n#define INGRESS__TAG_ITEM_CONDITION__INIT \\\n { PROTOBUF_C_MESSAGE_INIT (&ingress__tag_item_condition__descriptor) \\\n    , NULL, NULL, 0, 0, 0, 0, 0, INGRESS__OPERATOR_TYPE__OperatorUnDefined }\n\n\nstruct  _Ingress__TagItem\n{\n  ProtobufCMessage base;\n  /*\n   * which location to get the Tag\n   */\n  protobuf_c_boolean has_location;\n  Ingress__LocationType location;\n  /*\n   * The name of the key to be parsed\n   */\n  char *key;\n  /*\n   * The name of the value to be parsed\n   */\n  Ingress__TagItemCondition *condition;\n  /*\n   * matching method\n   */\n  protobuf_c_boolean has_match_type;\n  Ingress__MatchType match_type;\n};\n#define INGRESS__TAG_ITEM__INIT \\\n { PROTOBUF_C_MESSAGE_INIT (&ingress__tag_item__descriptor) \\\n    , 0, INGRESS__LOCATION_TYPE__LocUnDefined, NULL, NULL, 0, INGRESS__MATCH_TYPE__MatchUnDefined }\n\n\nstruct  _Ingress__TagRule\n{\n  ProtobufCMessage base;\n  /*\n   * 'and' condition\n   */\n  size_t n_items;\n  Ingress__TagItem **items;\n};\n#define INGRESS__TAG_RULE__INIT \\\n { PROTOBUF_C_MESSAGE_INIT (&ingress__tag_rule__descriptor) \\\n    , 0,NULL }\n\n\nstruct  _Ingress__TagRouter\n{\n  ProtobufCMessage base;\n  char *service_name;\n  /*\n   * 'or' condition\n   */\n  size_t n_rules;\n  Ingress__TagRule **rules;\n};\n#define INGRESS__TAG_ROUTER__INIT \\\n { PROTOBUF_C_MESSAGE_INIT (&ingress__tag_router__descriptor) \\\n    , NULL, 0,NULL }\n\n\nstruct  _Ingress__PathRouter\n{\n  ProtobufCMessage base;\n  char *prefix;\n  char *service_name;\n  size_t n_tags;\n  Ingress__TagRouter **tags;\n};\n#define INGRESS__PATH_ROUTER__INIT \\\n { PROTOBUF_C_MESSAGE_INIT (&ingress__path_router__descriptor) \\\n    , NULL, NULL, 0,NULL }\n\n\nstruct  _Ingress__HostRouter\n{\n  ProtobufCMessage base;\n  char *host;\n  char *service_name;\n  size_t n_paths;\n  Ingress__PathRouter **paths;\n  size_t n_tags;\n  Ingress__TagRouter **tags;\n};\n#define INGRESS__HOST_ROUTER__INIT \\\n { PROTOBUF_C_MESSAGE_INIT (&ingress__host_router__descriptor) \\\n    , NULL, NULL, 0,NULL, 0,NULL }\n\n\nstruct  _Ingress__AppnameRouter\n{\n  ProtobufCMessage base;\n  char *appname;\n  char *service_name;\n  size_t n_tags;\n  Ingress__TagRouter **tags;\n};\n#define INGRESS__APPNAME_ROUTER__INIT \\\n { PROTOBUF_C_MESSAGE_INIT (&ingress__appname_router__descriptor) \\\n    , NULL, NULL, 0,NULL }\n\n\nstruct  _Ingress__APIRouter\n{\n  ProtobufCMessage base;\n  char *service_name;\n  char *api;\n  size_t n_tags;\n  Ingress__TagRouter **tags;\n};\n#define INGRESS__APIROUTER__INIT \\\n { PROTOBUF_C_MESSAGE_INIT (&ingress__apirouter__descriptor) \\\n    , NULL, NULL, 0,NULL }\n\n\nstruct  _Ingress__Router\n{\n  ProtobufCMessage base;\n  Ingress__HostRouter *host_router;\n  Ingress__AppnameRouter *appname_router;\n  Ingress__APIRouter *api_router;\n};\n#define INGRESS__ROUTER__INIT \\\n { PROTOBUF_C_MESSAGE_INIT (&ingress__router__descriptor) \\\n    , NULL, NULL, NULL }\n\n\nstruct  _Ingress__Timeout\n{\n  ProtobufCMessage base;\n  protobuf_c_boolean has_connect_timeout;\n  uint32_t connect_timeout;\n  protobuf_c_boolean has_read_timeout;\n  uint32_t read_timeout;\n  protobuf_c_boolean has_write_timeout;\n  uint32_t write_timeout;\n};\n#define INGRESS__TIMEOUT__INIT \\\n { PROTOBUF_C_MESSAGE_INIT (&ingress__timeout__descriptor) \\\n    , 0, 0, 0, 0, 0, 0 }\n\n\nstruct  _Ingress__Upstream\n{\n  ProtobufCMessage base;\n  char *target;\n  protobuf_c_boolean has_weight;\n  uint32_t weight;\n};\n#define INGRESS__UPSTREAM__INIT \\\n { PROTOBUF_C_MESSAGE_INIT (&ingress__upstream__descriptor) \\\n    , NULL, 0, 0 }\n\n\nstruct  _Ingress__Metadata\n{\n  ProtobufCMessage base;\n  char *key;\n  char *value;\n};\n#define INGRESS__METADATA__INIT \\\n { PROTOBUF_C_MESSAGE_INIT (&ingress__metadata__descriptor) \\\n    , NULL, NULL }\n\n\nstruct  _Ingress__Action\n{\n  ProtobufCMessage base;\n  /*\n   * action type \n   */\n  protobuf_c_boolean has_action_type;\n  Ingress__ActionType action_type;\n  /*\n   * action value type\n   */\n  protobuf_c_boolean has_value_type;\n  Ingress__ActionValueType value_type;\n  /*\n   * action key\n   */\n  char *key;\n  /*\n   * action value\n   */\n  char *value;\n};\n#define INGRESS__ACTION__INIT \\\n { PROTOBUF_C_MESSAGE_INIT (&ingress__action__descriptor) \\\n    , 0, INGRESS__ACTION_TYPE__ActionUnDefined, 0, INGRESS__ACTION_VALUE_TYPE__ActionValueUnDefined, NULL, NULL }\nstruct  _Ingress__VirtualService\n{\n  ProtobufCMessage base;\n  char *service_name;\n  size_t n_upstreams;\n  Ingress__Upstream **upstreams;\n  Ingress__Timeout *timeout_ms;\n  protobuf_c_boolean has_force_https;\n  protobuf_c_boolean force_https;\n  size_t n_metadata;\n  Ingress__Metadata **metadata;\n  size_t n_action;\n  Ingress__Action **action;\n};\n#define INGRESS__VIRTUAL_SERVICE__INIT \\\n { PROTOBUF_C_MESSAGE_INIT (&ingress__virtual_service__descriptor) \\\n    , NULL, 0,NULL, NULL, 0, 0, 0,NULL, 0,NULL }\n\n\nstruct  _Ingress__Config\n{\n  ProtobufCMessage base;\n  size_t n_routers;\n  Ingress__Router **routers;\n  size_t n_services;\n  Ingress__VirtualService **services;\n};\n#define INGRESS__CONFIG__INIT \\\n { PROTOBUF_C_MESSAGE_INIT (&ingress__config__descriptor) \\\n    , 0,NULL, 0,NULL }\n\n\n/* Ingress__TagValueStrList methods */\nvoid   ingress__tag_value_str_list__init\n                     (Ingress__TagValueStrList         *message);\nsize_t ingress__tag_value_str_list__get_packed_size\n                     (const Ingress__TagValueStrList   *message);\nsize_t ingress__tag_value_str_list__pack\n                     (const Ingress__TagValueStrList   *message,\n                      uint8_t             *out);\nsize_t ingress__tag_value_str_list__pack_to_buffer\n                     (const Ingress__TagValueStrList   *message,\n                      ProtobufCBuffer     *buffer);\nIngress__TagValueStrList *\n       ingress__tag_value_str_list__unpack\n                     (ProtobufCAllocator  *allocator,\n                      size_t               len,\n                      const uint8_t       *data);\nvoid   ingress__tag_value_str_list__free_unpacked\n                     (Ingress__TagValueStrList *message,\n                      ProtobufCAllocator *allocator);\n/* Ingress__TagItemCondition methods */\nvoid   ingress__tag_item_condition__init\n                     (Ingress__TagItemCondition         *message);\nsize_t ingress__tag_item_condition__get_packed_size\n                     (const Ingress__TagItemCondition   *message);\nsize_t ingress__tag_item_condition__pack\n                     (const Ingress__TagItemCondition   *message,\n                      uint8_t             *out);\nsize_t ingress__tag_item_condition__pack_to_buffer\n                     (const Ingress__TagItemCondition   *message,\n                      ProtobufCBuffer     *buffer);\nIngress__TagItemCondition *\n       ingress__tag_item_condition__unpack\n                     (ProtobufCAllocator  *allocator,\n                      size_t               len,\n                      const uint8_t       *data);\nvoid   ingress__tag_item_condition__free_unpacked\n                     (Ingress__TagItemCondition *message,\n                      ProtobufCAllocator *allocator);\n/* Ingress__TagItem methods */\nvoid   ingress__tag_item__init\n                     (Ingress__TagItem         *message);\nsize_t ingress__tag_item__get_packed_size\n                     (const Ingress__TagItem   *message);\nsize_t ingress__tag_item__pack\n                     (const Ingress__TagItem   *message,\n                      uint8_t             *out);\nsize_t ingress__tag_item__pack_to_buffer\n                     (const Ingress__TagItem   *message,\n                      ProtobufCBuffer     *buffer);\nIngress__TagItem *\n       ingress__tag_item__unpack\n                     (ProtobufCAllocator  *allocator,\n                      size_t               len,\n                      const uint8_t       *data);\nvoid   ingress__tag_item__free_unpacked\n                     (Ingress__TagItem *message,\n                      ProtobufCAllocator *allocator);\n/* Ingress__TagRule methods */\nvoid   ingress__tag_rule__init\n                     (Ingress__TagRule         *message);\nsize_t ingress__tag_rule__get_packed_size\n                     (const Ingress__TagRule   *message);\nsize_t ingress__tag_rule__pack\n                     (const Ingress__TagRule   *message,\n                      uint8_t             *out);\nsize_t ingress__tag_rule__pack_to_buffer\n                     (const Ingress__TagRule   *message,\n                      ProtobufCBuffer     *buffer);\nIngress__TagRule *\n       ingress__tag_rule__unpack\n                     (ProtobufCAllocator  *allocator,\n                      size_t               len,\n                      const uint8_t       *data);\nvoid   ingress__tag_rule__free_unpacked\n                     (Ingress__TagRule *message,\n                      ProtobufCAllocator *allocator);\n/* Ingress__TagRouter methods */\nvoid   ingress__tag_router__init\n                     (Ingress__TagRouter         *message);\nsize_t ingress__tag_router__get_packed_size\n                     (const Ingress__TagRouter   *message);\nsize_t ingress__tag_router__pack\n                     (const Ingress__TagRouter   *message,\n                      uint8_t             *out);\nsize_t ingress__tag_router__pack_to_buffer\n                     (const Ingress__TagRouter   *message,\n                      ProtobufCBuffer     *buffer);\nIngress__TagRouter *\n       ingress__tag_router__unpack\n                     (ProtobufCAllocator  *allocator,\n                      size_t               len,\n                      const uint8_t       *data);\nvoid   ingress__tag_router__free_unpacked\n                     (Ingress__TagRouter *message,\n                      ProtobufCAllocator *allocator);\n/* Ingress__PathRouter methods */\nvoid   ingress__path_router__init\n                     (Ingress__PathRouter         *message);\nsize_t ingress__path_router__get_packed_size\n                     (const Ingress__PathRouter   *message);\nsize_t ingress__path_router__pack\n                     (const Ingress__PathRouter   *message,\n                      uint8_t             *out);\nsize_t ingress__path_router__pack_to_buffer\n                     (const Ingress__PathRouter   *message,\n                      ProtobufCBuffer     *buffer);\nIngress__PathRouter *\n       ingress__path_router__unpack\n                     (ProtobufCAllocator  *allocator,\n                      size_t               len,\n                      const uint8_t       *data);\nvoid   ingress__path_router__free_unpacked\n                     (Ingress__PathRouter *message,\n                      ProtobufCAllocator *allocator);\n/* Ingress__HostRouter methods */\nvoid   ingress__host_router__init\n                     (Ingress__HostRouter         *message);\nsize_t ingress__host_router__get_packed_size\n                     (const Ingress__HostRouter   *message);\nsize_t ingress__host_router__pack\n                     (const Ingress__HostRouter   *message,\n                      uint8_t             *out);\nsize_t ingress__host_router__pack_to_buffer\n                     (const Ingress__HostRouter   *message,\n                      ProtobufCBuffer     *buffer);\nIngress__HostRouter *\n       ingress__host_router__unpack\n                     (ProtobufCAllocator  *allocator,\n                      size_t               len,\n                      const uint8_t       *data);\nvoid   ingress__host_router__free_unpacked\n                     (Ingress__HostRouter *message,\n                      ProtobufCAllocator *allocator);\n/* Ingress__AppnameRouter methods */\nvoid   ingress__appname_router__init\n                     (Ingress__AppnameRouter         *message);\nsize_t ingress__appname_router__get_packed_size\n                     (const Ingress__AppnameRouter   *message);\nsize_t ingress__appname_router__pack\n                     (const Ingress__AppnameRouter   *message,\n                      uint8_t             *out);\nsize_t ingress__appname_router__pack_to_buffer\n                     (const Ingress__AppnameRouter   *message,\n                      ProtobufCBuffer     *buffer);\nIngress__AppnameRouter *\n       ingress__appname_router__unpack\n                     (ProtobufCAllocator  *allocator,\n                      size_t               len,\n                      const uint8_t       *data);\nvoid   ingress__appname_router__free_unpacked\n                     (Ingress__AppnameRouter *message,\n                      ProtobufCAllocator *allocator);\n/* Ingress__APIRouter methods */\nvoid   ingress__apirouter__init\n                     (Ingress__APIRouter         *message);\nsize_t ingress__apirouter__get_packed_size\n                     (const Ingress__APIRouter   *message);\nsize_t ingress__apirouter__pack\n                     (const Ingress__APIRouter   *message,\n                      uint8_t             *out);\nsize_t ingress__apirouter__pack_to_buffer\n                     (const Ingress__APIRouter   *message,\n                      ProtobufCBuffer     *buffer);\nIngress__APIRouter *\n       ingress__apirouter__unpack\n                     (ProtobufCAllocator  *allocator,\n                      size_t               len,\n                      const uint8_t       *data);\nvoid   ingress__apirouter__free_unpacked\n                     (Ingress__APIRouter *message,\n                      ProtobufCAllocator *allocator);\n/* Ingress__Router methods */\nvoid   ingress__router__init\n                     (Ingress__Router         *message);\nsize_t ingress__router__get_packed_size\n                     (const Ingress__Router   *message);\nsize_t ingress__router__pack\n                     (const Ingress__Router   *message,\n                      uint8_t             *out);\nsize_t ingress__router__pack_to_buffer\n                     (const Ingress__Router   *message,\n                      ProtobufCBuffer     *buffer);\nIngress__Router *\n       ingress__router__unpack\n                     (ProtobufCAllocator  *allocator,\n                      size_t               len,\n                      const uint8_t       *data);\nvoid   ingress__router__free_unpacked\n                     (Ingress__Router *message,\n                      ProtobufCAllocator *allocator);\n/* Ingress__Timeout methods */\nvoid   ingress__timeout__init\n                     (Ingress__Timeout         *message);\nsize_t ingress__timeout__get_packed_size\n                     (const Ingress__Timeout   *message);\nsize_t ingress__timeout__pack\n                     (const Ingress__Timeout   *message,\n                      uint8_t             *out);\nsize_t ingress__timeout__pack_to_buffer\n                     (const Ingress__Timeout   *message,\n                      ProtobufCBuffer     *buffer);\nIngress__Timeout *\n       ingress__timeout__unpack\n                     (ProtobufCAllocator  *allocator,\n                      size_t               len,\n                      const uint8_t       *data);\nvoid   ingress__timeout__free_unpacked\n                     (Ingress__Timeout *message,\n                      ProtobufCAllocator *allocator);\n/* Ingress__Upstream methods */\nvoid   ingress__upstream__init\n                     (Ingress__Upstream         *message);\nsize_t ingress__upstream__get_packed_size\n                     (const Ingress__Upstream   *message);\nsize_t ingress__upstream__pack\n                     (const Ingress__Upstream   *message,\n                      uint8_t             *out);\nsize_t ingress__upstream__pack_to_buffer\n                     (const Ingress__Upstream   *message,\n                      ProtobufCBuffer     *buffer);\nIngress__Upstream *\n       ingress__upstream__unpack\n                     (ProtobufCAllocator  *allocator,\n                      size_t               len,\n                      const uint8_t       *data);\nvoid   ingress__upstream__free_unpacked\n                     (Ingress__Upstream *message,\n                      ProtobufCAllocator *allocator);\n/* Ingress__Metadata methods */\nvoid   ingress__metadata__init\n                     (Ingress__Metadata         *message);\nsize_t ingress__metadata__get_packed_size\n                     (const Ingress__Metadata   *message);\nsize_t ingress__metadata__pack\n                     (const Ingress__Metadata   *message,\n                      uint8_t             *out);\nsize_t ingress__metadata__pack_to_buffer\n                     (const Ingress__Metadata   *message,\n                      ProtobufCBuffer     *buffer);\nIngress__Metadata *\n       ingress__metadata__unpack\n                     (ProtobufCAllocator  *allocator,\n                      size_t               len,\n                      const uint8_t       *data);\nvoid   ingress__metadata__free_unpacked\n                     (Ingress__Metadata *message,\n                      ProtobufCAllocator *allocator);\n/* Ingress__Action methods */\nvoid   ingress__action__init\n                     (Ingress__Action         *message);\nsize_t ingress__action__get_packed_size\n                     (const Ingress__Action   *message);\nsize_t ingress__action__pack\n                     (const Ingress__Action   *message,\n                      uint8_t             *out);\nsize_t ingress__action__pack_to_buffer\n                     (const Ingress__Action   *message,\n                      ProtobufCBuffer     *buffer);\nIngress__Action *\n       ingress__action__unpack\n                     (ProtobufCAllocator  *allocator,\n                      size_t               len,\n                      const uint8_t       *data);\nvoid   ingress__action__free_unpacked\n                     (Ingress__Action *message,\n                      ProtobufCAllocator *allocator);\n/* Ingress__VirtualService methods */\nvoid   ingress__virtual_service__init\n                     (Ingress__VirtualService         *message);\nsize_t ingress__virtual_service__get_packed_size\n                     (const Ingress__VirtualService   *message);\nsize_t ingress__virtual_service__pack\n                     (const Ingress__VirtualService   *message,\n                      uint8_t             *out);\nsize_t ingress__virtual_service__pack_to_buffer\n                     (const Ingress__VirtualService   *message,\n                      ProtobufCBuffer     *buffer);\nIngress__VirtualService *\n       ingress__virtual_service__unpack\n                     (ProtobufCAllocator  *allocator,\n                      size_t               len,\n                      const uint8_t       *data);\nvoid   ingress__virtual_service__free_unpacked\n                     (Ingress__VirtualService *message,\n                      ProtobufCAllocator *allocator);\n/* Ingress__Config methods */\nvoid   ingress__config__init\n                     (Ingress__Config         *message);\nsize_t ingress__config__get_packed_size\n                     (const Ingress__Config   *message);\nsize_t ingress__config__pack\n                     (const Ingress__Config   *message,\n                      uint8_t             *out);\nsize_t ingress__config__pack_to_buffer\n                     (const Ingress__Config   *message,\n                      ProtobufCBuffer     *buffer);\nIngress__Config *\n       ingress__config__unpack\n                     (ProtobufCAllocator  *allocator,\n                      size_t               len,\n                      const uint8_t       *data);\nvoid   ingress__config__free_unpacked\n                     (Ingress__Config *message,\n                      ProtobufCAllocator *allocator);\n/* --- per-message closures --- */\n\ntypedef void (*Ingress__TagValueStrList_Closure)\n                 (const Ingress__TagValueStrList *message,\n                  void *closure_data);\ntypedef void (*Ingress__TagItemCondition_Closure)\n                 (const Ingress__TagItemCondition *message,\n                  void *closure_data);\ntypedef void (*Ingress__TagItem_Closure)\n                 (const Ingress__TagItem *message,\n                  void *closure_data);\ntypedef void (*Ingress__TagRule_Closure)\n                 (const Ingress__TagRule *message,\n                  void *closure_data);\ntypedef void (*Ingress__TagRouter_Closure)\n                 (const Ingress__TagRouter *message,\n                  void *closure_data);\ntypedef void (*Ingress__PathRouter_Closure)\n                 (const Ingress__PathRouter *message,\n                  void *closure_data);\ntypedef void (*Ingress__HostRouter_Closure)\n                 (const Ingress__HostRouter *message,\n                  void *closure_data);\ntypedef void (*Ingress__AppnameRouter_Closure)\n                 (const Ingress__AppnameRouter *message,\n                  void *closure_data);\ntypedef void (*Ingress__APIRouter_Closure)\n                 (const Ingress__APIRouter *message,\n                  void *closure_data);\ntypedef void (*Ingress__Router_Closure)\n                 (const Ingress__Router *message,\n                  void *closure_data);\ntypedef void (*Ingress__Timeout_Closure)\n                 (const Ingress__Timeout *message,\n                  void *closure_data);\ntypedef void (*Ingress__Upstream_Closure)\n                 (const Ingress__Upstream *message,\n                  void *closure_data);\ntypedef void (*Ingress__Metadata_Closure)\n                 (const Ingress__Metadata *message,\n                  void *closure_data);\ntypedef void (*Ingress__Action_Closure)\n                 (const Ingress__Action *message,\n                  void *closure_data);\ntypedef void (*Ingress__VirtualService_Closure)\n                 (const Ingress__VirtualService *message,\n                  void *closure_data);\ntypedef void (*Ingress__Config_Closure)\n                 (const Ingress__Config *message,\n                  void *closure_data);\n\n/* --- services --- */\n\n\n/* --- descriptors --- */\n\nextern const ProtobufCEnumDescriptor    ingress__location_type__descriptor;\nextern const ProtobufCEnumDescriptor    ingress__match_type__descriptor;\nextern const ProtobufCEnumDescriptor    ingress__operator_type__descriptor;\nextern const ProtobufCEnumDescriptor    ingress__action_type__descriptor;\nextern const ProtobufCEnumDescriptor    ingress__action_value_type__descriptor;\nextern const ProtobufCMessageDescriptor ingress__tag_value_str_list__descriptor;\nextern const ProtobufCMessageDescriptor ingress__tag_item_condition__descriptor;\nextern const ProtobufCMessageDescriptor ingress__tag_item__descriptor;\nextern const ProtobufCMessageDescriptor ingress__tag_rule__descriptor;\nextern const ProtobufCMessageDescriptor ingress__tag_router__descriptor;\nextern const ProtobufCMessageDescriptor ingress__path_router__descriptor;\nextern const ProtobufCMessageDescriptor ingress__host_router__descriptor;\nextern const ProtobufCMessageDescriptor ingress__appname_router__descriptor;\nextern const ProtobufCMessageDescriptor ingress__apirouter__descriptor;\nextern const ProtobufCMessageDescriptor ingress__router__descriptor;\nextern const ProtobufCMessageDescriptor ingress__timeout__descriptor;\nextern const ProtobufCMessageDescriptor ingress__upstream__descriptor;\nextern const ProtobufCMessageDescriptor ingress__metadata__descriptor;\nextern const ProtobufCMessageDescriptor ingress__action__descriptor;\nextern const ProtobufCMessageDescriptor ingress__virtual_service__descriptor;\nextern const ProtobufCMessageDescriptor ingress__config__descriptor;\n\nPROTOBUF_C__END_DECLS\n\n\n#endif  /* PROTOBUF_C_ingress_2eproto__INCLUDED */\n"
  },
  {
    "path": "modules/ngx_ingress_module/ingress.proto",
    "content": "syntax = \"proto2\";\n\npackage Ingress;\n\nenum LocationType {\n  LocUnDefined  = 0;    // first element must be zero\n  LocHttpHeader = 1;    // Tag from http header\n  LocHttpQuery  = 2;    // Tag from http query \n  LocNginxVar   = 3;    // Tag from nginx var \n  LocXBizInfo   = 4;    // Tag from x-biz-info \n  LocHttpCookie = 5;    // Tag from http cookie\n}\n\nenum MatchType {\n  MatchUnDefined    = 0;    // first element must be zero\n  WholeMatch        = 1;    // String matches exactly\n  StrListInMatch    = 2;    // String list match\n  ModCompare        = 3;    // mod result compare value\n}\n\nenum OperatorType {\n  OperatorUnDefined     = 0; // first element must be zero\n  OperatorEqual         = 1; // equal operation\n  OperatorGreater       = 2; // greater operation\n  OperatorLess          = 3; // less operation\n  OperatorGreaterEqual  = 4; // greater or equal \n  OperatorLessEqual     = 5; // less or equal\n}\n\nenum ActionType\n{\n  ActionUnDefined           = 0;    // first element must be zero\n  ActionAddReqHeader        = 1;    // Action add http request header, add action do not care about duplicate\n  ActionAppendReqHeader     = 2;    // Action append http request header\n  ActionAddRespHeader       = 3;    // Action add http response header\n  ActionAppendRespHeader    = 4;    // Action append http response header\n  ActionAddParam            = 5;    // Action add http request query param\n}\n\nenum ActionValueType\n{\n  ActionValueUnDefined      = 0;    //first element must be zero\n  ActionStaticValue         = 1;    // value from configure\n  ActionDynamicValue        = 2;    // value from nginx var\n}\n\n\nmessage TagValueStrList {\n    repeated string value = 1; // string list  \n}\n\nmessage TagItemCondition {\n  optional string value_str = 1;            // string match value, for WholeMatch,PrefixMatch,SuffixMatch, and RegMatch\n  optional TagValueStrList value_list = 2;  // string list for match, for StrListInMatch\n  optional uint64 divisor = 3;              // mode divisor, for ModCompare\n  optional uint64 remainder = 4;            // compare remainder, for ModCompare\n  optional OperatorType operator = 5;       // >, <, =, >=, <=, for ModCompare\n}\n\nmessage TagItem {\n  optional LocationType location = 1;       // which location to get the Tag\n  optional string key = 2;                  // The name of the key to be parsed\n  optional TagItemCondition condition = 3;  // The name of the value to be parsed\n  optional MatchType match_type = 4;        // matching method\n}\n\nmessage TagRule {\n  repeated TagItem items = 1;       // 'and' condition\n}\n\nmessage TagRouter {\n  optional string service_name = 1;\n  repeated TagRule rules = 2;       // 'or' condition\n}\n\nmessage PathRouter {\n  optional string prefix = 1;\n  optional string service_name = 2;\n  repeated TagRouter tags = 3;\n}\n\nmessage HostRouter {\n  optional string host = 1;\n  optional string service_name = 2;\n\n  repeated PathRouter paths = 3;\n  repeated TagRouter tags = 4;\n}\n\nmessage AppnameRouter {\n  optional string appname = 1;\n  optional string service_name = 2;\n\n  repeated TagRouter tags = 3;\n}\n\nmessage APIRouter {\n  optional string service_name = 1;\n  optional string api = 2;\n\n  repeated TagRouter tags = 3;\n}\n\nmessage Router\n{\n  optional HostRouter host_router = 1;\n\n  optional AppnameRouter appname_router = 2;\n  optional APIRouter api_router = 3;\n}\n\nmessage Timeout\n{\n  optional uint32 connect_timeout = 1;\n  optional uint32 read_timeout = 2;\n  optional uint32 write_timeout = 3;\n}\n\nmessage Upstream\n{\n  optional string target = 1;\n  optional uint32 weight = 2;\n}\n\nmessage Metadata\n{\n  optional string key = 1;\n  optional string value = 2;\n}\n\n\n\nmessage Action\n{\n  optional ActionType action_type = 1;          // action type \n  optional ActionValueType value_type = 2;      // action value type\n  optional string key = 3;                      // action key\n  optional string value = 4;                    // action value\n\n}\n\nmessage UnitRedirect\n{\n  optional string from = 1;\n  optional string to = 2;\n}\nmessage UnitWeight\n{\n  optional string unit = 1;\n  optional uint32 weight = 2; \n}\n\nmessage Unit\n{\n  optional string generic_unit = 1;\n  repeated UnitRedirect redirects = 2;\n  repeated UnitWeight weights = 3;\n}\n\nmessage VirtualService\n{\n  optional string service_name = 1;\n\n  repeated Upstream upstreams = 2;\n\n  optional Timeout timeout_ms = 3;\n  optional bool force_https = 4;\n\n  repeated Metadata metadata = 5;\n  repeated Action   action = 6;\n}\n\nmessage Config\n{\n  repeated Router routers = 1;\n  repeated VirtualService services = 2;\n}\n"
  },
  {
    "path": "modules/ngx_ingress_module/ngx_ingress_module.c",
    "content": "\n/*\n * Copyright (C) 2020-2023 Alibaba Group Holding Limited\n */\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n#include <ngx_buf.h>\n\n#include <ngx_comm_string.h>\n#include <ngx_comm_shm.h>\n\n#include <ngx_ingress_protobuf.h>\n\n#include <ngx_ingress_module.h>\n\n#define NGX_INGRESS_UPDATE_INTERVAL             (30 * 1000)\n#define NGX_INGRESS_SHM_POOL_SIZE               (32 * 1024 * 1024)\n#define NGX_INGRESS_HASH_SIZE                   1323323\n#define NGX_INGRESS_GATEWAY_MAX_NAME_BUF_LEN    255\n#define NGX_INGRESS_DEFAULT_GATEWAY_NUM         10\n\n#define NGX_INGRESS_CTX_VAR          \"__ingress_ctx__\"\n\n#define NGX_INGRESS_TAG_MATCH_SUCCESS           NGX_OK\n#define NGX_INGRESS_TAG_MATCH_FAIL              NGX_DONE\n#define NGX_INGRESS_TAG_MATCH_ERROR             NGX_ERROR\n\n#define NGX_INGRESS_TAG_ACTION_APPEND_SEPARATOR \",\"\n#define NGX_INGRESS_TAG_EAGLEEYE_APPEND_SEPARATOR \"&\"\n\nstatic ngx_int_t ngx_ingress_add_variables(ngx_conf_t *cf);\nstatic ngx_int_t ngx_ingress_ctx_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data);\nstatic char *ngx_conf_set_ingress_gateway(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\nstatic void ngx_ingress_exit_process(ngx_cycle_t *cycle);\n\nstatic char *ngx_ingress_gateway_set_msec_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\nstatic char *ngx_ingress_gateway_set_num_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\nstatic char *ngx_ingress_gateway_set_size_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\nstatic char *ngx_ingress_gateway_shm_config(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\nstatic ngx_int_t ngx_ingress_route_target_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_ingress_force_https_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_ingress_get_time_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data);\nstatic char *ngx_ingress_gateway_metadata(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\nextern int ngx_ingress_metadata_compare(const void *c1, const void *c2);\n\ntypedef struct {\n    ngx_int_t   initialized;\n\n    ngx_str_t   target;\n    ngx_int_t   force_https;\n\n    ngx_msec_t  connect_timeout;\n    ngx_msec_t  read_timeout;\n    ngx_msec_t  write_timeout;\n\n    ngx_array_t metadata;       /* ngx_ingress_metadata_t */\n    ngx_array_t action_a;       /* ngx_ingress_action_t */\n} ngx_ingress_ctx_t;\n\n/* function declare */\nstatic void * ngx_ingress_create_main_conf(ngx_conf_t *cf);\n\nstatic void * ngx_ingress_create_loc_conf(ngx_conf_t *cf);\nstatic char * ngx_ingress_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child);\nstatic char * ngx_ingress_init_main_conf(ngx_conf_t *cf, void *conf);\n\ncheck_update_status ngx_ingress_check_update(void * context, void * data);\nngx_int_t ngx_ingress_update(ngx_cycle_t *cycle, void * context, ngx_shm_pool_t * pool, void * data, ngx_int_t print_detail);\n\nstatic ngx_command_t ngx_ingress_commands[] = {\n    { ngx_string(\"ingress_gateway\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_ingress_gateway,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"ingress_gateway_update_interval\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,\n      ngx_ingress_gateway_set_msec_slot,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      offsetof(ngx_ingress_gateway_t, update_check_interval),\n      NULL },\n\n    { ngx_string(\"ingress_gateway_hash_num\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,\n      ngx_ingress_gateway_set_num_slot,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      offsetof(ngx_ingress_gateway_t, hash_size),\n      NULL },\n    \n    { ngx_string(\"ingress_gateway_pool_size\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,\n      ngx_ingress_gateway_set_size_slot,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      offsetof(ngx_ingress_gateway_t, pool_size),\n      NULL },\n\n    { ngx_string(\"ingress_gateway_shm_config\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE4,\n      ngx_ingress_gateway_shm_config,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      0,\n      NULL },\n    \n    { ngx_string(\"ingress_gateway_metadata\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,\n      ngx_ingress_gateway_metadata,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n    \n    ngx_null_command\n};\n\nstatic ngx_http_module_t ngx_ingress_module_ctx = {\n    ngx_ingress_add_variables,          /* preconfiguration */\n    NULL,                               /* postconfiguration */\n\n    ngx_ingress_create_main_conf,       /* create main configuration */\n    ngx_ingress_init_main_conf,         /* init main configuration */\n\n    NULL,                               /* create server configuration */\n    NULL,                               /* merge server configuration */\n\n    ngx_ingress_create_loc_conf,        /* create location configuration */\n    ngx_ingress_merge_loc_conf          /* merge location configuration */\n};\n\nngx_module_t ngx_ingress_module = {\n    NGX_MODULE_V1,\n    &ngx_ingress_module_ctx,                    /* module context */\n    ngx_ingress_commands,                       /* 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    ngx_ingress_exit_process,                   /* exit process */\n    NULL,                                       /* exit master */\n    NGX_MODULE_V1_PADDING\n};\n\n\nstatic void *\nngx_ingress_create_main_conf(ngx_conf_t *cf)\n{\n    ngx_ingress_main_conf_t  *conf;\n    ngx_int_t                 rc;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_ingress_main_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    rc = ngx_array_init(&conf->gateways,\n                        cf->pool,\n                        NGX_INGRESS_DEFAULT_GATEWAY_NUM,\n                        sizeof(ngx_ingress_gateway_t));\n    if (rc != NGX_OK) {\n        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                \"|ingress|create main gaetways array failed|\");\n        return NULL;\n    }\n\n    conf->ctx_var_index = NGX_CONF_UNSET;\n\n    return conf;\n}\n\nstatic char *\nngx_ingress_init_main_conf(ngx_conf_t *cf, void *conf)\n{\n    ngx_ingress_main_conf_t     *imcf = (ngx_ingress_main_conf_t*)conf;\n    ngx_strategy_slot_app_t     app;\n\n    ngx_str_t                   gw_prefix = ngx_string(\"ingress_gateway_\");\n    ngx_uint_t                  i;\n\n    u_char                      name_buf[NGX_INGRESS_GATEWAY_MAX_NAME_BUF_LEN];\n    size_t                      name_len;\n\n    ngx_ingress_gateway_t *gateway = (ngx_ingress_gateway_t *)imcf->gateways.elts;\n    for (i = 0; i < imcf->gateways.nelts; i++) {\n        /* check config valid */\n        if (gateway[i].shm_name.len == 0\n            || gateway[i].shm_size == 0)\n        {\n            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                    \"|ingress|gateway %V is not configured|\", &gateway[i].name);\n            return NGX_CONF_ERROR;\n        }\n\n        /* attach shared memory */\n        gateway[i].shared = ngx_ingress_shared_memory_create(&gateway[i].shm_name, gateway[i].shm_size, &gateway[i].lock_file);\n        if (gateway[i].shared == NULL) {\n            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                    \"|ingress|gateway %V is open shared failed|\", &gateway[i].name);\n            return NGX_CONF_ERROR;\n        }\n\n        /* set default value */\n        ngx_conf_init_msec_value(gateway[i].update_check_interval, NGX_INGRESS_UPDATE_INTERVAL);\n        ngx_conf_init_size_value(gateway[i].pool_size, NGX_INGRESS_SHM_POOL_SIZE);\n        ngx_conf_init_value(gateway[i].hash_size, NGX_INGRESS_HASH_SIZE);\n\n        /* register double buffered shared memory */\n        memset(&app, 0, sizeof(app));\n        if (gw_prefix.len + gateway[i].name.len >=  NGX_INGRESS_GATEWAY_MAX_NAME_BUF_LEN) {\n            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                \"|ingress|gateway name %V is too long|\", &gateway[i].name);\n            return NGX_CONF_ERROR;\n        }\n\n        name_len = ngx_snprintf(name_buf, NGX_INGRESS_GATEWAY_MAX_NAME_BUF_LEN, \"%V%V\", &gw_prefix, &gateway[i].name) - name_buf;\n        \n        app.frame_ctx.name.data = name_buf;\n        app.frame_ctx.name.len = name_len;\n        app.frame_ctx.interval = gateway[i].update_check_interval;\n        app.data = &gateway[i];\n        app.check_update = ngx_ingress_check_update;\n        app.update = ngx_ingress_update;\n        app.slot_size = sizeof(ngx_ingress_t);\n        app.pool_size = gateway[i].pool_size;\n        app.shm_warn_mem_rate = 0;\n\n        gateway[i].ingress_app = ngx_strategy_slot_app_register(cf, &app);\n        if (gateway[i].ingress_app == NULL) {\n            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                    \"|ingress|register strategy ingress_app failed\");\n            return NGX_CONF_ERROR;\n        }\n\n        ngx_log_error(NGX_LOG_DEBUG, cf->log, 0, \n                    \"|ingress|register strategy %V successfully|\", \n                    &gateway[i].name);\n    }\n\n    ngx_str_t ngx_ingress_ctx_name = ngx_string(NGX_INGRESS_CTX_VAR);\n    imcf->ctx_var_index = ngx_http_get_variable_index(cf, &ngx_ingress_ctx_name);\n    if (imcf->ctx_var_index == NGX_ERROR) {\n        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                      \"|ingress|ctx_var_index failed|\");\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\nstatic void *\nngx_ingress_create_loc_conf(ngx_conf_t *cf)\n{\n    ngx_ingress_loc_conf_t  *sscf;\n\n    sscf = ngx_pcalloc(cf->pool, sizeof(ngx_ingress_loc_conf_t));\n    if (sscf == NULL) {\n        return NULL;\n    }\n\n    return sscf;\n}\n\n\nstatic char *\nngx_ingress_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_ingress_loc_conf_t   *prev = parent;\n    ngx_ingress_loc_conf_t   *conf = child;\n\n    if (conf->gateway == NULL) {\n        conf->gateway = prev->gateway;\n    }\n\n    return NGX_CONF_OK;\n}\n\nstatic ngx_int_t\nngx_ingress_check_upstream_enable(ngx_ingress_service_t *service)\n{\n    ngx_int_t   enable = 0;\n\n    if (service->upstreams == NULL || service->upstreams->nelts == 0) {\n        /* No upstream is processed according to no rules */\n        return enable;\n    }\n\n    enable = 1;\n    return enable;\n}\n\nngx_int_t \nngx_ingress_tag_value_compar(const void *v1, const void *v2) \n{\n    ngx_str_t *s1 = (ngx_str_t *)v1;\n    ngx_str_t *s2 = (ngx_str_t *)v2;\n    return ngx_comm_strcasecmp(s1, s2);\n}\n\nngx_int_t\nngx_ingress_tag_mod_compar(ngx_str_t *tag_value, ngx_int_t divisor,\n    ngx_int_t remainder, ngx_ingress_tag_operator_e op) \n{\n    ngx_int_t ret = NGX_INGRESS_TAG_MATCH_FAIL;\n    ngx_int_t mod_value = ngx_atoi(tag_value->data, tag_value->len);\n\n    if (mod_value == NGX_ERROR || divisor == 0) {\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, \"|ingress|mod_value atoi error|\"); \n        return NGX_INGRESS_TAG_MATCH_ERROR;\n    }\n    \n    ngx_int_t mod_r = mod_value % divisor;\n\n    switch (op) {\n    case INGRESS__OPERATOR_TYPE__OperatorEqual:\n        if (mod_r == remainder) {\n            ret = NGX_INGRESS_TAG_MATCH_SUCCESS;\n        }\n        break;\n    case INGRESS__OPERATOR_TYPE__OperatorGreater:\n        if (mod_r > remainder) {\n            ret = NGX_INGRESS_TAG_MATCH_SUCCESS;\n        }\n        break;\n    case INGRESS__OPERATOR_TYPE__OperatorLess:\n        if (mod_r < remainder) {\n            ret = NGX_INGRESS_TAG_MATCH_SUCCESS;\n        }\n        break;\n    case INGRESS__OPERATOR_TYPE__OperatorGreaterEqual:\n        if (mod_r >= remainder) {\n            ret = NGX_INGRESS_TAG_MATCH_SUCCESS;\n        }\n        break;\n    case INGRESS__OPERATOR_TYPE__OperatorLessEqual:\n        if (mod_r <= remainder) {\n            ret = NGX_INGRESS_TAG_MATCH_SUCCESS;\n        }\n        break;\n    case INGRESS__OPERATOR_TYPE__OperatorUnDefined:\n    default:\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, \"|ingress|invalid op value:%d|\", op); \n        ret = NGX_INGRESS_TAG_MATCH_ERROR;\n        break;\n    }\n\n    return ret;\n}\n\n/*\n *  return value:\n *  NGX_INGRESS_TAG_MATCH_SUCCESS means the value matched successfully\n *  NGX_INGRESS_TAG_MATCH_ERROR means an error occurred \n *  NGX_INGRESS_TAG_MATCH_FAIL means the value failed to match\n */\nstatic ngx_inline ngx_int_t\nngx_ingress_cmp_tag_value(ngx_ingress_tag_match_type_e match_type,\n    ngx_ingress_tag_condition_t *p_cond, ngx_str_t *tag_value)\n{\n    ngx_int_t ret = NGX_INGRESS_TAG_MATCH_FAIL;\n    void *s_result = NULL;\n    switch (match_type) {\n    case INGRESS__MATCH_TYPE__WholeMatch:\n        if (ngx_comm_strcasecmp(tag_value, &p_cond->value_str) == 0) {    \n            ret = NGX_INGRESS_TAG_MATCH_SUCCESS;\n        }\n        break;\n    case INGRESS__MATCH_TYPE__StrListInMatch:\n        s_result = ngx_shm_search_array(p_cond->value_a, tag_value, (ngx_shm_compar_func)ngx_ingress_tag_value_compar);\n        if (s_result != NULL) {\n            ret = NGX_INGRESS_TAG_MATCH_SUCCESS;\n        }\n        break;\n    case INGRESS__MATCH_TYPE__ModCompare:\n        ret = ngx_ingress_tag_mod_compar(tag_value, p_cond->divisor, p_cond->remainder, p_cond->op);\n        break;\n    case INGRESS__MATCH_TYPE__MatchUnDefined:\n    default:\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n            \"|ingress|invalid match type|%d|\", match_type);\n        ret = NGX_INGRESS_TAG_MATCH_ERROR;\n        break;\n    }\n\n    return ret;\n}\n\n/*\n * function: get tag rule value from http request\n * return: NGX_OK means that the tag rule's value has found\n * other value means that the tag rule's value hasn't found, or there is an error\n */\nstatic ngx_inline ngx_int_t\nngx_ingress_get_req_tag_value(ngx_http_request_t *r, ngx_ingress_tag_value_location_e location,\n    ngx_str_t *tag_key, ngx_str_t *tag_value)\n{\n    ngx_int_t               ret = NGX_ERROR;\n    ngx_table_elt_t        *cookie;\n    tag_value->data = NULL;\n    tag_value->len = 0;\n    switch (location) {\n    case INGRESS__LOCATION_TYPE__LocHttpHeader:\n        ret = ngx_http_header_in(r, (u_char *)tag_key->data, tag_key->len, tag_value);\n        break;\n    case INGRESS__LOCATION_TYPE__LocHttpQuery:\n        ret = ngx_http_arg(r, (u_char *)tag_key->data, tag_key->len, tag_value); \n        break;\n    case INGRESS__LOCATION_TYPE__LocNginxVar:\n        ret = NGX_ABORT; \n        break;\n    case INGRESS__LOCATION_TYPE__LocXBizInfo:\n        ret = NGX_ABORT;\n        break;\n    case INGRESS__LOCATION_TYPE__LocHttpCookie:\n        cookie = ngx_http_parse_multi_header_lines(r, r->headers_in.cookie, tag_key, tag_value);\n        if (cookie != NULL && tag_value->data != NULL) {\n            ret = NGX_OK;\n        }\n        break;\n    case INGRESS__LOCATION_TYPE__LocUnDefined:\n    default:\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n            \"|ingress|invalid loc type|%d|\", location);\n        ret = NGX_ERROR; \n        break;\n    }\n    return ret;\n}\n\nstatic ngx_ingress_service_t *\nngx_ingress_get_tag_match_service(ngx_ingress_gateway_t *gateway,\nngx_http_request_t *r, ngx_shm_array_t *tags)\n{\n    ngx_uint_t                      i, j, k;\n    ngx_ingress_service_t          *service = NULL;\n    ngx_int_t                       ret = NGX_ERROR;\n    ngx_str_t                       value;\n\n    ngx_ingress_tag_router_t *tag_router = tags->elts;\n\n    /* Traversing each tag route (sorted in the array by priority), the first match is returned */\n    for (i = 0; i < tags->nelts; i++) {\n\n        ngx_ingress_tag_rule_t *tag_rule = tag_router[i].rules->elts;\n\n        /* Traversing each tag rule (sorted in the array by priority), the first match is returned */\n        for (j = 0; j < tag_router[i].rules->nelts; j++) {\n            if (tag_rule[j].items->nelts == 0) { /* no items */\n                continue;\n            }\n\n            ngx_ingress_tag_item_t *tag_item = tag_rule[j].items->elts;\n            \n            /* Traversing each tag item, each item must match before returning */\n            for (k = 0; k < tag_rule[j].items->nelts; k++) {\n\n                ret = ngx_ingress_get_req_tag_value(r, tag_item[k].location, &tag_item[k].key, &value);\n                /* The request does not carry the target parameter */\n                if (ret != NGX_OK) {\n                    break;\n                } else {\n                    ret = ngx_ingress_cmp_tag_value(tag_item[k].match_type, &tag_item[k].condition, &value);\n                    if (ret != NGX_INGRESS_TAG_MATCH_SUCCESS) {\n                        break;\n                    }\n                }\n            }\n\n            /* every item matched */\n            if (k == tag_rule[j].items->nelts) {\n                if (ngx_ingress_check_upstream_enable(tag_router[i].service)) {\n                    return tag_router[i].service;\n                }\n            }\n        }\n    }\n\n    return service;\n}\n\nngx_int_t\nngx_ingress_service_queue_head_insert(ngx_http_request_t *r, ngx_queue_t *head, ngx_ingress_service_t *service)\n{\n    ngx_ingress_service_queue_t *service_queue = ngx_pcalloc(r->pool, sizeof(ngx_ingress_service_queue_t));\n    if (service_queue == NULL) {\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                \"|ingress|ingress alloc service queue error|\");\n        return NGX_ERROR;\n    }\n    service_queue->service = service;\n    ngx_queue_insert_head(head, &service_queue->queue_node); \n    return NGX_OK;\n}\n\nstatic ngx_int_t\nngx_ingress_match_service(ngx_ingress_gateway_t *gateway, ngx_http_request_t* r, ngx_queue_t *head)\n{\n    ngx_uint_t i;\n    ngx_ingress_t *current;\n    ngx_ingress_service_t *service = NULL;\n    ngx_ingress_host_router_t host_key;\n    ngx_ingress_host_router_t *host_router;\n    ngx_int_t rc;\n\n    current = ngx_strategy_get_current_slot(gateway->ingress_app);\n    if (current == NULL) {\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                    \"|ingress|get ingress_app failed|\");\n        return NGX_ERROR;\n    }\n\n    /* request no host */\n    if (r->headers_in.server.len == 0) {\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                    \"|ingress|request no host|\");\n        return NGX_ERROR;\n    }\n\n    host_key.host = r->headers_in.server;\n    host_router = (ngx_ingress_host_router_t *)ngx_shm_hash_get(current->host_map, &host_key);\n    if (host_router == NULL) {\n        /* wildcard match */\n        u_char *p = ngx_strlchr(host_key.host.data, host_key.host.data + host_key.host.len, '.');\n        if (p != NULL) {\n            ngx_int_t prefix_len = p - host_key.host.data + 1;\n\n            host_key.host.data += prefix_len;\n            host_key.host.len -= prefix_len;\n            host_router = (ngx_ingress_host_router_t *)ngx_shm_hash_get(current->wildcard_host_map, &host_key);\n        }\n    }\n\n    if (host_router == NULL) {\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                    \"|ingress|ingress host router not found|%V|\", &host_key.host);\n        return NGX_ERROR;\n    }\n    \n    if (host_router->service) {\n        rc = ngx_ingress_service_queue_head_insert(r, head, host_router->service);\n        if (rc != NGX_OK) {\n            ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                    \"|ingress|host service insert service queue failed|\");\n            return NGX_ERROR;\n        }\n    }\n\n\n    /* if host route has tag router, match */\n    if (host_router->tags) {\n        service = ngx_ingress_get_tag_match_service(gateway, r, host_router->tags);\n        if (service) {\n            rc = ngx_ingress_service_queue_head_insert(r, head, service);\n            if (rc != NGX_OK) {\n                ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                        \"|ingress|host tag service insert service queue failed|\");\n                return NGX_ERROR;\n            }\n        }\n    }\n\n    /* match path */\n    ngx_ingress_path_router_t *path_router = host_router->paths->elts;\n    for (i = 0; i < host_router->paths->nelts; i++) {\n        if (ngx_comm_prefix_casecmp(&r->uri, &path_router[i].prefix) == 0) {\n            ngx_log_error(NGX_LOG_DEBUG, ngx_cycle->log, 0,\n                    \"|ingress|match prefix prefix|%V|%V|\",\n                    &host_key.host,\n                    &r->uri);\n            \n            if (ngx_ingress_check_upstream_enable(path_router[i].service)) {\n                rc = ngx_ingress_service_queue_head_insert(r, head, path_router[i].service);\n                if (rc != NGX_OK) {\n                    ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                            \"|ingress|path service insert service queue failed|\");\n                    return NGX_ERROR;\n                }\n            }\n\n            /* if path route has tag router, match first */\n            if (path_router[i].tags) {\n                service = ngx_ingress_get_tag_match_service(gateway, r, path_router[i].tags);\n                if (service) {\n                    rc = ngx_ingress_service_queue_head_insert(r, head, service);\n                    if (rc != NGX_OK) {\n                        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                                \"|ingress|path service with tag insert service queue failed|\");\n                        return NGX_ERROR;\n                    }\n                }\n            }\n            \n            break;\n        }\n    }\n\n    ngx_log_error(NGX_LOG_DEBUG, ngx_cycle->log, 0,\n                  \"|ingress|match host|%V|%V|\", &host_key.host, &r->uri);\n    \n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_ingress_update(ngx_cycle_t *cycle,\n    void * context,\n    ngx_shm_pool_t * pool,\n    void * data,\n    ngx_int_t print_detail)\n{\n    ngx_ingress_gateway_t   *gateway = context;\n    ngx_ingress_t *ingress = data;\n\n    ngx_int_t rc;\n\n    ngx_ingress_shared_memory_config_t shm_pb_config;\n\n    if (print_detail) {\n        ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,\n                 \"|ingress|need to update|\");\n    }\n    \n    ingress->pool = pool;\n\n    rc = ngx_ingress_shared_memory_read_pb(gateway->shared, &shm_pb_config, ngx_ingress_pb_read_body);\n    if (rc != NGX_OK) {\n        ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,\n                 \"|ingress|shared memory read pb failed|%V|%i|\", &gateway->name, rc);\n        return NGX_ERROR;\n    }\n\n    if (ingress->version != 0 && shm_pb_config.pbconfig->n_services == 0) {\n        /* empty config protection */\n        ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,\n                 \"|ingress|shared memory empty protection|%V|%i|\", &gateway->name, rc);\n        return NGX_ERROR;\n    }\n\n    ngx_shm_pool_reset(ingress->pool);\n\n    rc = ngx_ingress_update_shm_by_pb(gateway, &shm_pb_config, ingress);\n    if (rc != NGX_OK) {\n        ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,\n                 \"|ingress|ngx_ingress_update failed|%V|\", &gateway->name);\n    }\n\n    ngx_ingress_shared_memory_free_pb(&shm_pb_config);\n\n    ingress->version = shm_pb_config.version;\n\n    ngx_log_error(NGX_LOG_ERR, cycle->log, 0,\n                  \"|ingress|update ingress md5|%*s|\",\n                  NGX_COMM_MD5_HEX_LEN, shm_pb_config.md5_digit);\n\n    if (rc == NGX_ERROR) {\n        ngx_ingress_shared_memory_write_status(gateway->shared, NGX_INGRESS_SHARED_MEMORY_TYPE_ERR);\n        ngx_log_error(NGX_LOG_ERR, cycle->log, 0, \"|ingress|update ingress rule error|\");\n    }\n    else {\n        ngx_ingress_shared_memory_write_status(gateway->shared, NGX_INGRESS_SHARED_MEMORY_TYPE_SUCCESS);\n        ngx_log_error(NGX_LOG_WARN, cycle->log, 0, \"|ingress|update ingress rule succ|\");\n    }\n\n    return rc;\n}\n\ncheck_update_status\nngx_ingress_check_update(void * context,\n    void * data)\n{\n    ngx_ingress_gateway_t       *gateway = context;\n    ngx_ingress_t               *ingress = data;\n\n    ngx_ingress_shared_memory_config_t   shm_pb_config;\n\n    ngx_int_t need_update = 0, rc;\n\n    ngx_int_t shm_key = 0;\n\n    rc = ngx_ingress_shared_memory_read_pb(gateway->shared, &shm_pb_config, ngx_ingress_pb_read_version);\n    if (rc != NGX_OK) {\n        ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, 0,\n                 \"|ingress|shared memory read pb failed|%x|%i|\", shm_key, rc);\n        return STATUS_CHECK_NO_UPDATE;\n    }\n\n    if (ingress->version != shm_pb_config.version) {\n        need_update = 1;\n    }\n\n    ngx_ingress_shared_memory_free_pb(&shm_pb_config);\n\n    if (need_update) {\n        return STATUS_CHECK_NEED_UPDATE;\n    }\n    return STATUS_CHECK_NO_UPDATE;\n}\n\n\n\nstatic ngx_http_variable_t  ngx_ingress_vars[] = {\n\n    { ngx_string(NGX_INGRESS_CTX_VAR), NULL,\n      ngx_ingress_ctx_variable, 0,\n      NGX_HTTP_VAR_NOCACHEABLE, 0 },\n    \n    { ngx_string(\"ingress_route_target\"), NULL,\n      ngx_ingress_route_target_variable, 0,\n      NGX_HTTP_VAR_NOCACHEABLE, 0 },\n    \n    { ngx_string(\"ingress_force_https\"), NULL,\n      ngx_ingress_force_https_variable, 0,\n      NGX_HTTP_VAR_NOCACHEABLE, 0 },\n    \n    { ngx_string(\"ingress_read_timeout\"), NULL,\n      ngx_ingress_get_time_variable, offsetof(ngx_ingress_ctx_t, read_timeout),\n      NGX_HTTP_VAR_NOCACHEABLE, 0 },\n    \n    { ngx_string(\"ingress_connect_timeout\"), NULL,\n      ngx_ingress_get_time_variable, offsetof(ngx_ingress_ctx_t, connect_timeout),\n      NGX_HTTP_VAR_NOCACHEABLE, 0 },\n    \n    { ngx_string(\"ingress_write_timeout\"), NULL,\n      ngx_ingress_get_time_variable, offsetof(ngx_ingress_ctx_t, write_timeout),\n      NGX_HTTP_VAR_NOCACHEABLE, 0 },\n\n    { ngx_null_string, NULL, NULL, 0, 0, 0 }\n};\n\nstatic ngx_int_t\nngx_ingress_add_variables(ngx_conf_t *cf)\n{\n   ngx_http_variable_t  *var, *v;\n\n    for (v = ngx_ingress_vars; v->name.len; v++) {\n        var = ngx_http_add_variable(cf, &v->name, v->flags);\n        if (var == NULL) {\n            return NGX_ERROR;\n        }\n\n        var->get_handler = v->get_handler;\n        var->data = v->data;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_ingress_ctx_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    ngx_ingress_ctx_t               *ctx;\n\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 1;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_ingress_module);\n    if (ctx == NULL) {\n        ctx = ngx_pcalloc(r->pool, sizeof(ngx_ingress_ctx_t));\n        if (ctx == NULL) {\n            return NGX_ERROR;\n        }\n\n        ngx_http_set_ctx(r, ctx, ngx_ingress_module);\n    }\n\n    v->not_found = 0;\n\n    v->data = (u_char *) ctx;\n    v->len = sizeof(ctx);\n\n    return NGX_OK;\n}\n\n\nstatic void*\nngx_ingress_get_gateway(ngx_conf_t *cf, ngx_ingress_main_conf_t *imcf, ngx_str_t *gateway_name)\n{\n    /* check gateway exist */\n    ngx_uint_t               i;\n    ngx_ingress_gateway_t *gateway = (ngx_ingress_gateway_t *)imcf->gateways.elts;\n    for (i = 0; i < imcf->gateways.nelts; i++) {\n        if (ngx_comm_strcmp(&gateway[i].name, gateway_name) == 0) {\n            return &gateway[i];\n        }\n    }\n\n    /* create new gateway */\n    gateway = ngx_array_push(&imcf->gateways);\n    if (gateway == NULL) {\n        return NULL;\n    }\n\n    ngx_memset(gateway, 0, sizeof(ngx_ingress_gateway_t));\n\n    /* init gateway */\n    gateway->hash_size = NGX_CONF_UNSET_UINT;\n    gateway->pool_size = NGX_CONF_UNSET_SIZE;\n    gateway->update_check_interval = NGX_CONF_UNSET_MSEC;\n\n    gateway->name.data = ngx_palloc(cf->pool, gateway_name->len);\n    if (gateway->name.data == NULL) {\n        return NULL;\n    }\n    ngx_memcpy(gateway->name.data, gateway_name->data, gateway_name->len);\n    gateway->name.len = gateway_name->len;\n\n    return gateway;\n}\n\nstatic char *\nngx_conf_set_ingress_gateway(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_str_t           *value = cf->args->elts;\n    ngx_str_t           *gateway_name = &value[1];\n\n    ngx_ingress_loc_conf_t       *dlcf = conf;\n    ngx_ingress_main_conf_t      *imcf = NULL;\n\n    imcf = ngx_http_cycle_get_module_main_conf(cf->cycle, ngx_ingress_module);\n    if (imcf == NULL) {\n        return \"ingress gateway get main conf failed\";\n    }\n    \n    ngx_ingress_gateway_t *gateway = ngx_ingress_get_gateway(cf, imcf, gateway_name);\n    if (gateway == NULL) {\n        return \"ingress alloc gateway failed\";\n    }\n\n    dlcf->gateway = gateway;\n\n    return NGX_CONF_OK;\n}\n\n\nstatic void\nngx_ingress_exit_process(ngx_cycle_t *cycle)\n{\n    ngx_ingress_main_conf_t   *imcf;\n    ngx_uint_t                 i;\n\n    imcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_ingress_module);\n    if (imcf == NULL) {\n        ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,\n                      \"|ingress|exit process get main conf failed|\");\n        return;\n    }\n\n    ngx_ingress_gateway_t *gateway = (ngx_ingress_gateway_t *)imcf->gateways.elts;\n    for (i = 0; i < imcf->gateways.nelts; i++) {\n        if (gateway[i].shared == NULL) {\n            continue;\n        }\n        ngx_ingress_shared_memory_free(gateway[i].shared);\n        gateway[i].shared = NULL;\n    }\n}\n\nstatic char *\nngx_ingress_gateway_set_msec_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_str_t                       *value;\n    ngx_str_t                       gateway_name, data_value;\n    ngx_ingress_main_conf_t         *imcf = conf;\n\n    ngx_msec_t       *msp;\n\n    value = cf->args->elts;\n\n    gateway_name = value[1];\n    data_value = value[2];\n\n    ngx_ingress_gateway_t *gateway = ngx_ingress_get_gateway(cf, imcf, &gateway_name);\n    if (gateway == NULL) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"|ingress|alloc gateway failed\");\n        return NGX_CONF_ERROR;\n    }\n\n    msp = (ngx_msec_t *)  ((u_char*)gateway + cmd->offset);\n    if (*msp != NGX_CONF_UNSET_MSEC) {\n        return \"is duplicate\";\n    }\n\n    *msp = ngx_parse_time(&data_value, 0);\n    if (*msp == (ngx_msec_t) NGX_ERROR) {\n        return \"invalid value\";\n    }\n    \n    return NGX_CONF_OK;\n}\n\nstatic char *\nngx_ingress_gateway_set_num_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_str_t                           *value;\n    ngx_str_t                            gateway_name, data_value;\n    ngx_ingress_main_conf_t             *dmcf = conf;\n\n    ngx_int_t        *np;\n\n    value = cf->args->elts;\n\n    gateway_name = value[1];\n    data_value = value[2];\n\n    ngx_ingress_gateway_t *gateway = ngx_ingress_get_gateway(cf, dmcf, &gateway_name);\n    if (gateway == NULL) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"|ingress|alloc gateway failed\");\n        return NGX_CONF_ERROR;\n    }\n\n    np = (ngx_int_t *) ((u_char*)gateway + cmd->offset);\n\n    if (*np != NGX_CONF_UNSET) {\n        return \"is duplicate\";\n    }\n\n    *np = ngx_atoi(data_value.data, data_value.len);\n    if (*np == NGX_ERROR) {\n        return \"invalid number\";\n    }\n    \n    return NGX_CONF_OK;\n}\n\nstatic char *\nngx_ingress_gateway_set_size_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_str_t                       *value;\n    ngx_str_t                        gateway_name, data_value;\n    ngx_ingress_main_conf_t         *dmcf = conf;\n\n    size_t                          *sp;\n\n    value = cf->args->elts;\n\n    gateway_name = value[1];\n    data_value = value[2];\n\n    ngx_ingress_gateway_t * gateway = ngx_ingress_get_gateway(cf, dmcf, &gateway_name);\n    if (gateway == NULL) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"|ingress|alloc gateway failed\");\n        return NGX_CONF_ERROR;\n    }\n\n    sp = (size_t *) ((u_char*)gateway + cmd->offset);\n\n    if (*sp != NGX_CONF_UNSET_SIZE) {\n        return \"is duplicate\";\n    }\n\n    *sp = ngx_parse_size(&data_value);\n    if (*sp == (size_t) NGX_ERROR) {\n        return \"invalid value\";\n    }\n    \n    return NGX_CONF_OK;\n}\n\nstatic char *\nngx_ingress_gateway_shm_config(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_str_t                       *value;\n    ngx_str_t                        gateway_name;\n    ngx_ingress_main_conf_t         *dmcf = conf;\n\n    size_t                          shm_size;\n\n    value = cf->args->elts;\n\n    gateway_name = value[1];\n\n    /*\n      ingress_gateway_shm_config $gateway_name $shm_name shm_size lock_file\n      gateway_name: gateway name\n      shm_name: shared memory name\n      shm_size: shared memory size\n      lock_file: lock file path\n    */\n\n    ngx_ingress_gateway_t * gateway = ngx_ingress_get_gateway(cf, dmcf, &gateway_name);\n    if (gateway == NULL) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"|ingress|alloc gateway failed\");\n        return NGX_CONF_ERROR;\n    }\n\n    gateway->shm_name = value[2];\n\n    shm_size = ngx_parse_size(&value[3]);\n    if (shm_size == (size_t) NGX_ERROR) {\n        return \"invalid value\";\n    }\n    gateway->shm_size = shm_size;\n\n    gateway->lock_file = value[4];\n\n    if (ngx_conf_full_name(cf->cycle, &gateway->lock_file, 1) != NGX_OK) {\n        ngx_log_error(NGX_LOG_EMERG, cf->log, ngx_errno,\n                      \"|ingress|lock file full path failed: %V|\", &gateway->lock_file);\n                      return NGX_CONF_ERROR;\n    }\n    \n    return NGX_CONF_OK;\n}\n\nstatic ngx_int_t\nngx_ingress_service_get_value_from_nginx_var(ngx_http_request_t *r, ngx_str_t *key,\n    ngx_str_t *var_value)\n{\n    ngx_str_t var_name;\n    u_char *p_strlow = ngx_pnalloc(r->pool, key->len);\n    if (p_strlow == NULL) {\n        return NGX_ERROR;\n    }\n    ngx_uint_t hash = ngx_hash_strlow(p_strlow, key->data, key->len);\n    var_name.data = p_strlow;\n    var_name.len = key->len;\n    ngx_http_variable_value_t *vv = ngx_http_get_variable(r, &var_name, hash);\n\n    if (vv == NULL || vv->not_found || vv->len == 0) {\n        return NGX_ERROR;\n    }\n\n    var_value->len = vv->len;\n    var_value->data = vv->data;\n    return NGX_OK;\n}\n\n/*\n * add header, if header key which add already exists in request,\n * this fuction will add a new header with the same key\n */\nstatic ngx_int_t\nngx_ingress_service_request_add_header(ngx_http_request_t *r, ngx_str_t *key,\n    ngx_str_t *value)\n{\n    ngx_table_elt_t             *h;\n    h = ngx_list_push(&r->headers_in.headers);\n    if (h == NULL) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n            \"|ingress|ingress add header push headers error|\");\n        return NGX_ERROR;\n    }\n\n    h->key.len = key->len;\n    h->key.data = key->data;\n    h->hash = ngx_hash_key_lc(h->key.data, h->key.len);\n    \n    h->lowcase_key = ngx_pnalloc(r->pool, h->key.len);\n    if (h->lowcase_key == NULL) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n            \"|ingress|ingress add header alloc error|\");\n        ngx_list_delete(&r->headers_in.headers, h);\n        return NGX_ERROR;\n    }\n    ngx_strlow(h->lowcase_key, key->data, key->len);\n\n    h->value.data = value->data;\n    h->value.len = value->len;\n\n    return NGX_OK;\n}\n\n/*\n * If header key exists in the request, value will be appended\n * otherwise, new header will be added to the request\n * Based on RFC2616, the multiple message-header fields with the same field-name \n * MAY be present in a message if and only if the entire field-value for that\n * header field is defined as a comma-separated list [i.e., #(values)].\n * For the header Eagleeye-UserData, the APPEND_SEPARATOR will use '&' specifically.\n */\nstatic ngx_int_t\nngx_ingress_service_request_append_header(ngx_http_request_t *r, ngx_str_t *key,\n    ngx_str_t *value)\n{\n    ngx_uint_t                   i, hash, tag_len;\n    ngx_table_elt_t             *h;\n    ngx_list_part_t             *part;\n    ngx_str_t                    new_value = ngx_null_string;\n    u_char                      *p = NULL;\n    ngx_int_t                    rc; \n\n    if (value->len == 0) {\n        return NGX_OK;\n    }\n\n    part = &r->headers_in.headers.part;\n    h = part->elts;\n    hash = ngx_hash_key_lc(key->data, key->len);\n\n    for (i = 0; /* void */; i++) {\n        if (i >= part->nelts) {\n            if (part->next == NULL) {\n                break;\n            }\n            part = part->next;\n            h = part->elts;\n            i = 0;\n        }\n\n        if (hash == h[i].hash \n            && key->len == h[i].key.len\n            && ngx_strncasecmp(key->data, h[i].lowcase_key, key->len) == 0)\n        {\n            tag_len = sizeof(NGX_INGRESS_TAG_ACTION_APPEND_SEPARATOR) - 1;\n            new_value.len = h[i].value.len + value->len + tag_len;\n            new_value.data = ngx_pnalloc(r->pool, new_value.len);\n            if (new_value.data == NULL) {\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                        \"|ingress|ingress append header alloc error|\");\n                return NGX_ERROR;\n            }\n\n            p = ngx_copy(new_value.data, h[i].value.data, h[i].value.len);\n            p = ngx_copy(p, NGX_INGRESS_TAG_ACTION_APPEND_SEPARATOR, tag_len);\n            p = ngx_copy(p, value->data, value->len);\n            h[i].value.data = new_value.data;\n            h[i].value.len = new_value.len;\n            return NGX_OK;\n        }\n    }\n\n    /* match failed */\n    rc = ngx_ingress_service_request_add_header(r, key, value);\n    if (rc != NGX_OK) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                \"|ingress|ingress append header do add error|\");\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\nstatic ngx_int_t\nngx_ingress_service_response_add_header(ngx_http_request_t *r, ngx_str_t *key,\n    ngx_str_t *value)\n{\n    ngx_table_elt_t  *h;\n    h = ngx_list_push(&r->headers_out.headers);\n    if (h == NULL) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                \"|ingress|ingress do action push headers_out error|\");\n        return NGX_ERROR;\n    }\n    \n    h->hash = 1;\n    h->key = *key;\n    h->value = *value;\n\n    return NGX_OK;\n}\n\n/*\n * add param: no matter the key is exist in the query or not\n * add action append 'key=value' at the end of the query\n *\n */\nstatic ngx_int_t\nngx_ingress_service_query_add_param(ngx_http_request_t *r, ngx_str_t *key, ngx_str_t *value)\n{\n    u_char *p;\n    ngx_str_t new_unparsed_uri = ngx_null_string;\n    new_unparsed_uri.len = r->unparsed_uri.len + key->len + value->len + 2;\n    new_unparsed_uri.data = ngx_pnalloc(r->pool, new_unparsed_uri.len);\n    if (new_unparsed_uri.data == NULL) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                \"|ingress|ingress action add param alloc error|\");\n        return NGX_ERROR;\n    }\n\n    if (r->args.len == 0) {\n        p = ngx_copy(new_unparsed_uri.data, r->unparsed_uri.data, r->unparsed_uri.len);\n        p = ngx_copy(p, \"?\", 1);\n        p = ngx_copy(p, key->data, key->len);\n        p = ngx_copy(p, \"=\", 1);\n        p = ngx_copy(p, value->data, value->len);\n    } else {\n        p = ngx_copy(new_unparsed_uri.data, r->unparsed_uri.data, r->unparsed_uri.len);\n        p = ngx_copy(p, \"&\", 1);\n        p = ngx_copy(p, key->data, key->len);\n        p = ngx_copy(p, \"=\", 1);\n        p = ngx_copy(p, value->data, value->len);\n    }\n\n    r->unparsed_uri.data = new_unparsed_uri.data;\n    r->unparsed_uri.len = new_unparsed_uri.len;\n    return NGX_OK;\n}\n\nstatic ngx_int_t\nngx_ingress_service_do_action(ngx_http_request_t *r, ngx_ingress_action_t *action)\n{\n    ngx_int_t rc = NGX_ERROR;\n    ngx_str_t value = ngx_null_string, key = ngx_null_string;\n    \n    if (action->key.data == NULL || action->key.len == 0 \n        || action->value.len == 0 || action->value.data == NULL) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                \"|ingress|ingress action key or value NULL|\");\n        return NGX_ERROR;\n    }\n\n    key.data = action->key.data;\n    key.len = action->key.len;\n\n    switch (action->value_type) {\n    case INGRESS__ACTION_VALUE_TYPE__ActionValueUnDefined:\n        /* action value type not defined */\n        break;\n    case INGRESS__ACTION_VALUE_TYPE__ActionStaticValue:\n        value.data = action->value.data;\n        value.len = action->value.len;\n        break;\n    case INGRESS__ACTION_VALUE_TYPE__ActionDynamicValue:\n        rc = ngx_ingress_service_get_value_from_nginx_var(r, &action->value, &value);\n        if (rc != NGX_OK || value.len == 0 || value.data == NULL ) {\n            ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0,\n                    \"|ingress|ingress find nginx var failed, %V|\", &action->value);\n            return NGX_OK; /* nginx var not found is not error */\n        }\n        break;\n    default:\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                \"|ingress|ingress action value type error|\");\n        return NGX_ERROR; \n    }\n\n    switch (action->action_type) {\n    case INGRESS__ACTION_TYPE__ActionAddReqHeader:\n        if (value.len == 0 || value.data == NULL) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                    \"|ingress|ingress action add req header value invalid|\");\n            return NGX_ERROR;;\n        }\n        \n        rc = ngx_ingress_service_request_add_header(r, &key, &value);\n        break;\n    case INGRESS__ACTION_TYPE__ActionAppendReqHeader:\n        if (value.len == 0 || value.data == NULL) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                    \"|ingress|ingress action append req header value invalid|\");\n            return NGX_ERROR;\n        }\n \n        rc = ngx_ingress_service_request_append_header(r, &key, &value);\n        break;\n    case INGRESS__ACTION_TYPE__ActionAddRespHeader:\n        if (value.len == 0 || value.data == NULL) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                    \"|ingress|ingress action add resp header value invalid|\");\n            break;\n        }\n \n        rc = ngx_ingress_service_response_add_header(r, &key, &value); \n        break;\n    case INGRESS__ACTION_TYPE__ActionAddParam:\n        if (value.len == 0 || value.data == NULL) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                    \"|ingress|ingress action add param value invalid|\");\n            break;\n        }\n\n        rc = ngx_ingress_service_query_add_param(r, &key, &value);\n        break;\n    case INGRESS__ACTION_TYPE__ActionUnDefined:\n    default:\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                \"|ingress|ingress action type invalid|\");\n        return NGX_ERROR;\n    }\n   \n    return rc;\n}\n\nngx_int_t \nngx_ingress_read_value_from_service_queue(ngx_ingress_ctx_t *ctx,\n    ngx_http_request_t *r, ngx_queue_t *head)\n\n{\n    ngx_ingress_service_t *target_service = NULL;\n    ngx_ingress_service_t *timeout_service = NULL;\n    ngx_ingress_service_t *force_https_service = NULL;\n    ngx_ingress_service_t *action_service = NULL;\n    ngx_int_t   action_num = 0;\n    ngx_int_t   metadata_num = 0;\n    ngx_int_t   rc;\n    ngx_queue_t *node;\n    for (node = ngx_queue_head(head); node != ngx_queue_sentinel(head); node = ngx_queue_next(node)) {\n        ngx_ingress_service_queue_t *service_queue =\n            ngx_queue_data(node, ngx_ingress_service_queue_t, queue_node);\n        if (service_queue->service == NULL) {\n            continue;\n        }\n        if (service_queue->service->metadata->nelts > 0) {\n            metadata_num += service_queue->service->metadata->nelts; \n        }\n        \n        if (service_queue->service->upstreams != NULL\n            && service_queue->service->upstreams->nelts > 0\n            && target_service == NULL) {\n            target_service = service_queue->service;\n        }\n\n        if (service_queue->service->timeout.set_flag == NGX_INGRESS_TIMEOUT_SET\n            && timeout_service == NULL) {\n            timeout_service = service_queue->service;\n        }\n\n        if (service_queue->service->force_https != NGX_INGRESS_FORCE_HTTPS_UNSET\n            && force_https_service == NULL) {\n            force_https_service = service_queue->service;\n        }\n\n        if (service_queue->service->action_a != NULL\n            && service_queue->service->action_a->nelts > 0\n            && action_service == NULL) {\n            action_num += service_queue->service->action_a->nelts;\n            action_service = service_queue->service;\n        }\n    }\n\n    if (target_service == NULL) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                \"|ingress|get target from service queue error|\");\n        /* some api may match host service without target, no need match again, so return declined */\n        return NGX_DECLINED;\n    }\n\n    ngx_ingress_upstream_t *ups = target_service->upstreams->elts;\n    ngx_int_t ups_index = 0;\n    if (target_service->upstream_weight != 0) {\n        ngx_int_t  offset = ngx_random() % target_service->upstream_weight;\n        for (ngx_uint_t i = 0; i < target_service->upstreams->nelts; i++) {\n            if (ups[i].start <= offset && ups[i].end > offset) {\n                ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0,\n                        \"|ingress|hit weight target|%i|%V|\", ups_index, &ups[ups_index].target);\n                ups_index = i;\n                break;\n            }\n        }\n    }\n\n    ctx->target.len = ups[ups_index].target.len;\n    ctx->target.data = ngx_palloc(r->pool, ctx->target.len);\n    if (ctx->target.data == NULL) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"|ingress|runtime target alloc failed|\");\n        return NGX_ERROR;\n    }\n    ngx_memcpy(ctx->target.data, ups[ups_index].target.data, ctx->target.len);\n\n    if (timeout_service != NULL) {\n        ctx->connect_timeout = timeout_service->timeout.connect_timeout;\n        ctx->write_timeout = timeout_service->timeout.write_timeout;\n        ctx->read_timeout = timeout_service->timeout.read_timeout;\n    } else { /* timeout unset, default value 0 */\n        ctx->connect_timeout = 0;\n        ctx->write_timeout = 0;\n        ctx->read_timeout = 0;\n    }\n    \n    if (force_https_service != NULL) {\n        ctx->force_https = force_https_service->force_https;\n    } else {\n        ctx->force_https = 0;\n    }\n\n    rc = ngx_array_init(&ctx->action_a, r->pool, action_num, \n            sizeof(ngx_ingress_action_t));\n \n    if (rc != NGX_OK) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                \"|ingress|init ctx alloc action array failed|\");\n        return NGX_ERROR;\n    }\n\n    if (action_service != NULL) {\n        ngx_ingress_action_t *shm_action = action_service->action_a->elts;\n        for (ngx_uint_t i = 0; i < action_service->action_a->nelts; i++) {\n            ngx_ingress_action_t *action = ngx_array_push(&ctx->action_a);\n            if (action == NULL) {\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                        \"|ingress|init ctx alloc action failed|\");\n                return NGX_ERROR;\n            }\n           \n            action->action_type = shm_action[i].action_type;\n            action->value_type = shm_action[i].value_type;\n            \n            action->key.data = ngx_palloc(r->pool, shm_action[i].key.len);\n            if (action->key.data == NULL) {\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                        \"|ingress|init ctx alloc action key failed:%d|\", shm_action[i].key.len);\n                return NGX_ERROR;\n            }\n            ngx_memcpy(action->key.data, shm_action[i].key.data, shm_action[i].key.len);\n            action->key.len = shm_action[i].key.len;\n            \n            action->value.data = ngx_palloc(r->pool, shm_action[i].value.len);\n            if (action->value.data == NULL) {\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                        \"|ingress|init ctx alloc action value failed:%d|\", shm_action[i].value.len);\n                return NGX_ERROR;\n            }\n            ngx_memcpy(action->value.data, shm_action[i].value.data, shm_action[i].value.len);\n            action->value.len = shm_action[i].value.len;\n        }\n    }\n\n    rc = ngx_array_init(&ctx->metadata, r->pool, metadata_num, sizeof(ngx_ingress_metadata_t));\n    if (rc != NGX_OK) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                \"|ingress|init ctx metadata array failed|\");\n        return NGX_ERROR;\n    }\n \n    for (node = ngx_queue_head(head); node != ngx_queue_sentinel(head); node = ngx_queue_next(node)) {\n        ngx_ingress_service_queue_t *service_queue =\n            ngx_queue_data(node, ngx_ingress_service_queue_t, queue_node);\n        if (service_queue->service == NULL) {\n            continue;\n        }\n        ngx_ingress_metadata_t *shm_metas = service_queue->service->metadata->elts;\n        for (ngx_uint_t i = 0; i < service_queue->service->metadata->nelts; i++) {\n            ngx_ingress_metadata_t *metadata = ngx_array_push(&ctx->metadata);\n            if (metadata == NULL) {\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                        \"|ingress|init ctx push metadata failed|\");\n                return NGX_ERROR;\n            }\n\n            metadata->key.data = ngx_palloc(r->pool, shm_metas[i].key.len);\n            if (metadata->key.data == NULL) {\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                        \"|ingress|init ctx alloc meta key failed|\");\n                return NGX_ERROR;\n            }\n            ngx_memcpy(metadata->key.data, shm_metas[i].key.data, shm_metas[i].key.len);\n            metadata->key.len = shm_metas[i].key.len;\n\n            metadata->value.data = ngx_palloc(r->pool, shm_metas[i].value.len);\n            if (metadata->value.data == NULL) {\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                        \"|ingress|init ctx alloc meta value failed|\");\n                return NGX_ERROR;\n            }\n            ngx_memcpy(metadata->value.data, shm_metas[i].value.data, shm_metas[i].value.len);\n            metadata->value.len = shm_metas[i].value.len;\n        }\n    }\n    if (metadata_num > 0) {\n        qsort(ctx->metadata.elts, ctx->metadata.nelts, ctx->metadata.size, ngx_ingress_metadata_compare); \n    }\n\n    return NGX_OK;\n}\n\n/* \n * return NGX_ERROR: may match again\n * return NGX_DECLINED: no need to match again\n */\n \nstatic ngx_int_t\nngx_ingress_init_ctx(ngx_ingress_ctx_t *ctx, ngx_http_request_t *r)\n{\n    ngx_ingress_loc_conf_t              *ilcf = NULL;\n    ngx_int_t                            rc;\n\n    ilcf = ngx_http_get_module_loc_conf(r, ngx_ingress_module);\n    if (ilcf->gateway == NULL) {\n        return NGX_DECLINED;\n    }\n\n    ngx_queue_t service_head;\n    ngx_queue_init(&service_head);\n\n    rc = ngx_ingress_match_service(ilcf->gateway, r, &service_head);\n    if (rc != NGX_OK || ngx_queue_empty(&service_head)) {\n        ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0,\n                \"|ingress|route service not found|\");\n        \n        /* not found probably, no need to match again  */\n        return NGX_DECLINED;\n    }\n    \n    rc = ngx_ingress_read_value_from_service_queue(ctx, r, &service_head);\n    if (rc != NGX_OK) {\n        ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0,\n                \"|ingress|read value from service queue failed|\");\n        return rc;\n    }\n\n    return NGX_OK;\n}\n\nstatic ngx_ingress_ctx_t *\nngx_ingress_get_ctx(ngx_ingress_main_conf_t *imcf,\n                    ngx_http_request_t *r)\n{\n    ngx_ingress_ctx_t               *ctx = NULL;\n    ngx_http_variable_value_t       *vv;\n    ngx_int_t                        rc;\n    ngx_uint_t                       i;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_ingress_module);\n    if (ctx != NULL) {\n        goto ret;\n    }\n\n    vv = ngx_http_get_indexed_variable(r, imcf->ctx_var_index);\n    if (vv == NULL || vv->not_found) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"|ingress|get ctx variable failed|\");\n        return NULL;\n    }\n\n    ctx = (ngx_ingress_ctx_t *) vv->data;\n    ngx_http_set_ctx(r, ctx, ngx_ingress_module);\n\nret:\n    if (!ctx->initialized) {\n        rc = ngx_ingress_init_ctx(ctx, r);\n        if (rc == NGX_ERROR) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"|ingress|init ctx failed|\");\n            return NULL;\n        }\n        ctx->initialized = 1; /* initialized flag 1 indicates no need to match again */\n\n        if (rc == NGX_OK && ctx->action_a.nelts > 0) {\n            /* \n             * action should do while ctx->initialized equal 1,\n             * so it can avoid to do action duplicately\n             */\n            ngx_ingress_action_t *action = ctx->action_a.elts;     \n            for (i = 0; i < ctx->action_a.nelts; i++) {\n                rc = ngx_ingress_service_do_action(r, &action[i]);\n                if (rc != NGX_OK) {\n                    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                            \"|ingress|ingress service do action error|\");\n                    /* just log error */\n                }\n            }\n        }  \n        \n    }\n\n    return ctx;\n}\n\nstatic ngx_int_t\nngx_ingress_route_target_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    ngx_ingress_ctx_t               *ctx;\n\n    ngx_ingress_main_conf_t             *imcf = NULL;\n\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 1;\n\n    imcf = ngx_http_get_module_main_conf(r, ngx_ingress_module);\n    ctx = ngx_ingress_get_ctx(imcf, r);\n    if (ctx == NULL) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"|ingress|route target get ctx failed|\");\n        return NGX_ERROR;\n    }\n\n    v->not_found = 0;\n    v->data = ctx->target.data;\n    v->len = ctx->target.len;\n\n    return NGX_OK;\n}\n\nstatic ngx_int_t\nngx_ingress_force_https_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    ngx_ingress_ctx_t               *ctx;\n\n    ngx_ingress_main_conf_t             *imcf = NULL;\n\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 1;\n\n    imcf = ngx_http_get_module_main_conf(r, ngx_ingress_module);\n    ctx = ngx_ingress_get_ctx(imcf, r);\n    if (ctx == NULL) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"|ingress|route service get ctx failed|\");\n        return NGX_ERROR;\n    }\n\n    v->not_found = 0;\n    v->len = 1;\n    v->valid = 1;\n    if (ctx->force_https) {\n        v->data = (u_char*)\"1\";\n    } else {\n        v->data = (u_char*)\"0\";\n    }\n\n    ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0,\n                  \"|ingress|force https variable|%d|\", ctx->force_https);\n\n    return NGX_OK;\n}\n\nstatic ngx_int_t\nngx_ingress_get_time_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    ngx_ingress_ctx_t               *ctx;\n\n    ngx_ingress_main_conf_t             *imcf = NULL;\n\n    v->valid = 0;\n    v->no_cacheable = 0;\n    v->not_found = 1;\n\n    imcf = ngx_http_get_module_main_conf(r, ngx_ingress_module);\n    ctx = ngx_ingress_get_ctx(imcf, r);\n    if (ctx == NULL) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"|ingress|route target get ctx failed|\");\n        return NGX_ERROR;\n    }\n\n    ngx_msec_t  *tp;\n\n    tp = (ngx_msec_t *) ((char *) ctx + data);\n    if (*tp == 0 || *tp == NGX_CONF_UNSET_MSEC) {\n        ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0,\n                      \"|ingress|ignore unset timeout|\");\n        return NGX_ERROR;\n    }\n\n    v->data = ngx_pnalloc(r->pool, NGX_INT_T_LEN);\n    if (v->data == NULL) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"|ingress|gettime alloc failed|\");\n        return NGX_ERROR;\n    }\n\n    v->len = ngx_sprintf(v->data, \"%Mms\", *tp) - v->data;\n    v->valid = 1;\n    v->not_found = 0;\n\n    return NGX_OK;\n}\n\ntypedef struct {\n    ngx_str_t key;\n} ngx_ingress_metadata_ctx_t;\n\n\nstatic ngx_int_t\nngx_ingress_gateway_metadata_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,\n    uintptr_t data)\n{\n    ngx_ingress_metadata_ctx_t     *metadata_ctx = (ngx_ingress_metadata_ctx_t*)data;\n\n    ngx_ingress_ctx_t              *ingress_ctx;\n    ngx_str_t                       value = ngx_null_string;\n\n    ngx_ingress_main_conf_t             *imcf = NULL;\n\n    v->valid = 0;\n    v->no_cacheable = 0;\n    v->not_found = 1;\n\n    imcf = ngx_http_get_module_main_conf(r, ngx_ingress_module);\n    ingress_ctx = ngx_ingress_get_ctx(imcf, r);\n\n    if (ingress_ctx == NULL) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"|ingress|route metadata get ctx failed|\");\n        return NGX_ERROR;\n    }\n\n    /* get meta value */\n\n    ngx_ingress_metadata_t key;\n    ngx_ingress_metadata_t *meta_value;\n\n    key.key = metadata_ctx->key;\n\n    meta_value = bsearch(&key,\n                         ingress_ctx->metadata.elts,\n                         ingress_ctx->metadata.nelts,\n                         ingress_ctx->metadata.size,\n                         ngx_ingress_metadata_compare);\n    \n    if (meta_value != NULL) {\n        value.len = meta_value->value.len;\n        value.data = meta_value->value.data;\n    }\n    \n    v->valid = 1;\n    v->no_cacheable = 1;\n    v->not_found = 0;\n    v->data = value.data;\n    v->len = value.len;\n\n    return NGX_OK;\n}\n\nstatic char *\nngx_ingress_gateway_metadata(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_str_t               *value          = cf->args->elts;\n    ngx_http_variable_t     *var;\n\n    ngx_ingress_metadata_ctx_t *ctx = ngx_palloc(cf->pool, sizeof(ngx_ingress_metadata_ctx_t));\n    if (ctx == NULL) {\n        ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, 0,\n                      \"|ingress|metadata ctx alloc failed|\");\n        return NGX_CONF_ERROR;\n    }\n\n    /* key */\n    ctx->key = value[1];\n\n    /* variable */\n    if (value[2].data[0] == '$') {\n        value[2].data++;\n        value[2].len--;\n    }\n\n    var = ngx_http_add_variable(cf, &value[2], NGX_HTTP_VAR_CHANGEABLE);\n    if (var == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    var->get_handler = ngx_ingress_gateway_metadata_variable;\n    var->data = (uintptr_t)ctx;\n\n    return NGX_CONF_OK;\n}\n\n"
  },
  {
    "path": "modules/ngx_ingress_module/ngx_ingress_module.h",
    "content": "/*\n * Copyright (C) 2020-2023 Alibaba Group Holding Limited\n */\n\n#ifndef NGX_INGRESS_MODULE_H\n#define NGX_INGRESS_MODULE_H\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n#include <ngx_buf.h>\n\n#include <ngx_comm_string.h>\n#include <ngx_comm_shm.h>\n#include <ngx_proc_strategy_module.h>\n\n#define NGX_INGRESS_FORCE_HTTPS_UNSET   -1\n#define NGX_INGRESS_TIMEOUT_UNSET       0\n#define NGX_INGRESS_TIMEOUT_SET         1\n\ntypedef struct {\n    ngx_msec_t      connect_timeout;\n    ngx_msec_t      read_timeout;\n    ngx_msec_t      write_timeout;\n    ngx_int_t       set_flag;\n} ngx_ingress_timeout_t;\n\ntypedef struct {\n    ngx_int_t       start;\n    ngx_int_t       end;\n\n    ngx_str_t       target;\n} ngx_ingress_upstream_t;\n\ntypedef struct {\n    ngx_str_t           key;\n    ngx_str_t           value;\n} ngx_ingress_metadata_t;\n\ntypedef Ingress__LocationType ngx_ingress_tag_value_location_e;\ntypedef Ingress__MatchType ngx_ingress_tag_match_type_e;\ntypedef Ingress__OperatorType ngx_ingress_tag_operator_e;\ntypedef Ingress__ActionType ngx_ingress_action_type_e;\ntypedef Ingress__ActionValueType ngx_ingress_action_value_type_e; \n\ntypedef struct {\n    ngx_str_t                   value_str;\n    ngx_shm_array_t            *value_a;        /* ngx_str_t, already sorted */\n    ngx_int_t                   divisor;\n    ngx_int_t                   remainder;\n    ngx_ingress_tag_operator_e  op;\n} ngx_ingress_tag_condition_t;\n\ntypedef struct {\n    ngx_ingress_action_type_e       action_type;\n    ngx_ingress_action_value_type_e value_type;\n    ngx_str_t                       key;\n    ngx_str_t                       value;\n} ngx_ingress_action_t;\n\ntypedef struct {\n    ngx_str_t                   name;\n\n    ngx_int_t                   upstream_weight;\n    ngx_shm_array_t            *upstreams;   /* ngx_ingress_upstream_t */\n\n    ngx_ingress_timeout_t       timeout;\n    ngx_int_t                   force_https;\n\n    ngx_shm_array_t            *action_a;         /* ngx_ingress_action_t */\n\n    ngx_shm_array_t            *metadata;       /* ngx_ingress_metadata_t */\n} ngx_ingress_service_t;\n\ntypedef struct {\n    ngx_queue_t queue_node;\n    ngx_ingress_service_t *service;\n} ngx_ingress_service_queue_t;\n\ntypedef struct {\n    ngx_ingress_tag_value_location_e    location;       /* match field location */\n    ngx_str_t                           key;            /* match field name */\n    ngx_ingress_tag_match_type_e        match_type;     /* match field type */\n    ngx_ingress_tag_condition_t         condition;      /* match condition */ \n} ngx_ingress_tag_item_t;\n\ntypedef struct {\n    ngx_shm_array_t        *items;              /* ngx_ingress_tag_item_t */\n} ngx_ingress_tag_rule_t;\n\ntypedef struct {\n    ngx_shm_array_t         *rules;             /* ngx_ingress_tag_rule_t */\n    ngx_ingress_service_t   *service;\n} ngx_ingress_tag_router_t;\n\ntypedef struct {\n    ngx_str_t                prefix;\n    ngx_shm_array_t         *tags;              /* ngx_ingress_tag_router_t: The number of elements is 0 and assigned to NULL */\n    ngx_ingress_service_t   *service;\n} ngx_ingress_path_router_t;\n\ntypedef struct {\n    ngx_str_t                   host;\n    ngx_shm_array_t            *paths;          /* ngx_ingress_path_router_t */\n    ngx_shm_array_t            *tags;           /* ngx_ingress_tag_router_t: The number of elements is 0 and assigned to NULL */\n    ngx_ingress_service_t      *service;\n} ngx_ingress_host_router_t;\n\n\ntypedef struct {\n    ngx_shm_hash_t      *host_map;                  /* ngx_ingress_host_router_t */\n    ngx_shm_hash_t      *wildcard_host_map;         /* ngx_ingress_host_router_t */\n\n    ngx_shm_hash_t      *service_map;               /* ngx_ingress_service_t */\n\n    uint64_t            version;\n\n    ngx_shm_pool_t      *pool;\n} ngx_ingress_t;\n\n\ntypedef struct {\n    ngx_str_t                        name;\n\n    ngx_str_t                        shm_name;\n    ngx_uint_t                       shm_size;\n\n    ngx_str_t                        lock_file;\n\n    ngx_msec_t                       update_check_interval;\n    size_t                           pool_size;\n    ngx_int_t                        hash_size;\n\n    ngx_ingress_shared_memory_t     *shared;\n    ngx_strategy_slot_app_t         *ingress_app;\n} ngx_ingress_gateway_t;\n\n\ntypedef struct {\n    ngx_array_t                  gateways;  /* ngx_ingress_gateway_t */\n\n    ngx_int_t                    ctx_var_index;\n} ngx_ingress_main_conf_t;\n\ntypedef struct {\n    ngx_ingress_gateway_t *gateway;\n} ngx_ingress_loc_conf_t;\n\n\nngx_int_t ngx_ingress_update_shm_by_pb(ngx_ingress_gateway_t *gateway, ngx_ingress_shared_memory_config_t *shm_pb_config, ngx_ingress_t *ingress);\nngx_int_t ngx_ingress_tag_value_compar(const void *v1, const void *v2);\n\n#endif // NGX_INGRESS_MODULE_H\n"
  },
  {
    "path": "modules/ngx_ingress_module/ngx_ingress_protobuf.c",
    "content": "/*\n * Copyright (C) 2020-2023 Alibaba Group Holding Limited\n */\n\n#include \"ngx_ingress_protobuf.h\"\n\n#include <ngx_comm_serialize.h>\n#include <ngx_ingress_module.h>\n\n#include <sys/ipc.h>\n#include <sys/shm.h>\n\n#include <sys/file.h>\n\nngx_int_t\nngx_ingress_shared_memory_init(ngx_ingress_shared_memory_t * shared, ngx_str_t *shm_name, ngx_uint_t shm_size, ngx_str_t *lock_file)\n{\n    int shm_fd = 0;\n\tint flag = 0600;\n    \n    if((shm_fd = shm_open((const char*)shm_name->data, O_RDWR, flag)) < 0) {\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                      \"|ingress|shared shmget failed|\");\n\t\treturn NGX_ERROR;\n\t}\n\n    shared->base_address = (u_char *) mmap(NULL, shm_size, PROT_READ|PROT_WRITE, MAP_SHARED, shm_fd, 0);\n    if(shared->base_address == NULL) {\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                      \"|ingress|shared shmat failed|\");\n        close(shm_fd);\n\t\treturn NGX_ERROR;\n\t}\n\n    shared->shm_size = shm_size;\n    shared->shm_fd = shm_fd;\n    shared->lock_fd = ngx_open_file(lock_file->data, NGX_FILE_RDWR, NGX_FILE_OPEN,\n                                   NGX_FILE_DEFAULT_ACCESS);\n\n    if (shared->lock_fd == NGX_INVALID_FILE) {\n        ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno,\n                    \"|ingress|open lock file \\\"%s\\\" failed|\", lock_file);\n        close(shm_fd);\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\nvoid\nngx_ingress_shared_memory_uninit(ngx_ingress_shared_memory_t *shared)\n{\n    if (shared->base_address != NULL) {\n        munmap(shared->base_address, shared->shm_size);\n        shared->base_address = NULL;\n    }\n    if (shared->shm_fd > 0) {\n        close(shared->shm_fd);\n    }\n    \n    if (shared->lock_fd != NGX_INVALID_FILE) {\n        ngx_close_file(shared->lock_fd);\n        shared->lock_fd = NGX_INVALID_FILE;\n    }\n}\n\nngx_ingress_shared_memory_t *\nngx_ingress_shared_memory_create(ngx_str_t *shm_name, ngx_uint_t shm_size, ngx_str_t *lock_file)\n{\n    ngx_ingress_shared_memory_t *shared = NULL;\n\n    shared = ngx_calloc(sizeof(ngx_ingress_shared_memory_t), ngx_cycle->log);\n    if (shared == NULL) {\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                      \"|ingress|shared alloc failed|\");\n        return NULL;\n    }\n\n    ngx_int_t rc = ngx_ingress_shared_memory_init(shared, shm_name, shm_size, lock_file);\n    if (rc != NGX_OK) {\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                      \"|ingress|shared memory init failed|\");\n        ngx_free(shared);\n        return NULL;\n    }\n\n    return shared;\n}\n\nvoid\nngx_ingress_shared_memory_free(ngx_ingress_shared_memory_t *shared)\n{\n    ngx_ingress_shared_memory_uninit(shared);\n    ngx_free(shared);\n}\n\n\nngx_int_t\nngx_ingress_shared_memory_write_status(\n    ngx_ingress_shared_memory_t *shared,\n    ngx_ingress_shared_memory_status_e status)\n{\n    u_char      *pos = shared->base_address;\n    uint32_t     left = shared->shm_size;\n    ngx_int_t    rc;\n    ngx_int_t    err;\n\n    /* 加锁 */\n    err = flock(shared->lock_fd, LOCK_EX|LOCK_NB);\n    if (err != 0) {\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                      \"|ingress|trylock failed|%d|%i|\", err, shared->lock_fd);\n        return NGX_AGAIN;\n    }\n\n    /* write status */\n    rc = ngx_serialize_write_uint32(&pos, &left, (uint32_t)status);\n\n    flock(shared->lock_fd, LOCK_UN);\n\n    if (rc != NGX_OK) {\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                      \"|ingress|shared write status failed|\");\n    }\n    \n    return rc;\n}\n\nngx_int_t\nngx_ingress_shared_memory_read_pb_locked(\n    ngx_ingress_shared_memory_t *shared,\n    ngx_ingress_shared_memory_config_t *shm_pb_config,\n    ngx_ingress_pb_read_mode_e mode)\n{\n    ngx_int_t rc;\n\n    uint32_t status, type, length, left;\n    u_char *pos = shared->base_address;\n\n    ngx_str_t src;\n    u_char md5_hex[NGX_COMM_MD5_HEX_LEN];\n\n    left = shared->shm_size;\n\n    shm_pb_config->pbconfig = NULL;\n\n    /* read Status */\n    rc = ngx_serialize_read_uint32(&pos, &left, &status);\n    if (rc != NGX_OK) {\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                      \"|ingress|shared read status failed|\");\n        return NGX_ERROR;\n    }\n\n    /* read version */\n    rc = ngx_serialize_read_uint64(&pos, &left, &shm_pb_config->version);\n    if (rc != NGX_OK) {\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                      \"|ingress|shared read version failed|\");\n        return NGX_ERROR;\n    }\n\n    if (mode == ngx_ingress_pb_read_version) {\n        return NGX_OK;\n    }\n\n    /* read Config-Type */\n    rc = ngx_serialize_read_uint32(&pos, &left, &type);\n    if (rc != NGX_OK) {\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                      \"|ingress|shared read config type failed|\");\n        return NGX_ERROR;\n    }\n    shm_pb_config->type = type;\n\n    if (shm_pb_config->type != NGX_INGRESS_SHARED_MEMORY_TYPE_SERVICE) {\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                      \"|ingress|unknown config type|%d|\", shm_pb_config->type);\n        return NGX_ERROR;\n    }\n\n    /* read Config-MD5 */\n    rc = ngx_serialize_read_data(&pos, &left, shm_pb_config->md5_digit, NGX_COMM_MD5_HEX_LEN);\n    if (rc != NGX_OK) {\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                      \"|ingress|shared read config md5 failed|\");\n        return NGX_ERROR;\n    }\n\n    /* read Config-Length */\n    rc = ngx_serialize_read_uint32(&pos, &left, &length);\n    if (rc != NGX_OK) {\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                      \"|ingress|shared read config len failed|\");\n        return NGX_ERROR;\n    }\n\n    /* check Configs MD5 */\n    if (left < length) {\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                      \"|ingress|shared pb len too small|\");\n        return NGX_ERROR;\n    }\n    src.data = pos;\n    src.len = length;\n\n    ngx_comm_md5_string(&src, md5_hex);\n    if (memcmp(md5_hex, shm_pb_config->md5_digit, NGX_COMM_MD5_HEX_LEN) != 0) {\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                      \"|ingress|shared md5 not equal|\");\n        return NGX_ERROR;\n    }\n\n    /* parse PB */\n    Ingress__Config * pbconfig = ingress__config__unpack(NULL, src.len, src.data);\n    if (pbconfig == NULL) {\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                      \"|ingress|shared parse pb failed|\");\n        return NGX_ERROR;\n    }\n\n    shm_pb_config->pbconfig = pbconfig;\n\n    return NGX_OK;\n}\n\nngx_int_t\nngx_ingress_shared_memory_read_pb(\n    ngx_ingress_shared_memory_t *shared,\n    ngx_ingress_shared_memory_config_t *shm_pb_config,\n    ngx_ingress_pb_read_mode_e mode)\n{\n    ngx_int_t rc;\n    int err;\n\n    err = flock(shared->lock_fd, LOCK_EX|LOCK_NB);\n    if (err != 0) {\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                      \"|ingress|trylock failed|%d|%i|\", err, shared->lock_fd);\n        return NGX_AGAIN;\n    }\n\n    ngx_log_error(NGX_LOG_DEBUG, ngx_cycle->log, 0,\n                  \"|ingress|trylock success|%d|%i|\", err, shared->lock_fd);\n\n    rc = ngx_ingress_shared_memory_read_pb_locked(shared, shm_pb_config, mode);\n    \n    flock(shared->lock_fd, LOCK_UN);\n\n    return rc;\n}\n\nvoid\nngx_ingress_shared_memory_free_pb(ngx_ingress_shared_memory_config_t *shm_pb_config)\n{\n    if (shm_pb_config->pbconfig != NULL) {\n        ingress__config__free_unpacked(shm_pb_config->pbconfig, NULL);\n    }\n}\n\nstatic int\nngx_ingress_host_compare(const void * p1, const void* p2) {\n    ngx_ingress_host_router_t * v1 = (ngx_ingress_host_router_t*)p1;\n    ngx_ingress_host_router_t * v2 = (ngx_ingress_host_router_t*)p2;\n\n    return ngx_comm_strcasecmp(&v1->host, &v2->host);\n}\n\nstatic ngx_uint_t\nngx_ingress_host_hash(const void * p) {\n    ngx_uint_t hash;\n    ngx_ingress_host_router_t * v1 = (ngx_ingress_host_router_t*)p;\n\n    hash = ngx_hash_key_lc(v1->host.data, v1->host.len);\n\n    return hash;\n}\n\nstatic int\nngx_ingress_service_compare(const void * p1, const void* p2) {\n    ngx_ingress_service_t * v1 = (ngx_ingress_service_t*)p1;\n    ngx_ingress_service_t * v2 = (ngx_ingress_service_t*)p2;\n\n    return ngx_comm_strcmp(&v1->name, &v2->name);\n}\n\nstatic ngx_uint_t\nngx_ingress_service_hash(const void * p) {\n    ngx_uint_t hash;\n    ngx_ingress_service_t * v1 = (ngx_ingress_service_t*)p;\n\n    hash = ngx_hash_key(v1->name.data, v1->name.len);\n\n    return hash;\n}\n\nstatic ngx_ingress_service_t *\nngx_ingress_get_service(ngx_ingress_t *ingress, char* service_name)\n{\n    ngx_ingress_service_t service_key;\n    ngx_ingress_service_t *service;\n\n    if (service_name == NULL) {\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                      \"|ingress|service is null|\");\n        return NULL;\n    }\n\n    service_key.name.len = ngx_strlen(service_name);\n    service_key.name.data = (u_char*)service_name;\n    service = (ngx_ingress_service_t *)ngx_shm_hash_get(ingress->service_map, &service_key);\n    if (service == NULL) {\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                      \"|ingress|service not found|%V|\", &service_key.name);\n        return NULL;\n    }\n\n    return service;\n}\n\nint\nngx_ingress_metadata_compare(const void *c1, const void *c2)\n{\n    ngx_ingress_metadata_t *meta1 = (ngx_ingress_metadata_t*)c1;\n    ngx_ingress_metadata_t *meta2 = (ngx_ingress_metadata_t*)c2;\n\n    return ngx_comm_str_compare(&meta1->key, &meta2->key);\n}\n\nstatic ngx_int_t \nngx_ingress_service_action_data_check(ngx_ingress_action_t *action) \n{\n    ngx_int_t rc = NGX_ERROR;\n    switch (action->action_type) {\n    case INGRESS__ACTION_TYPE__ActionAddReqHeader:\n    case INGRESS__ACTION_TYPE__ActionAppendReqHeader:\n    case INGRESS__ACTION_TYPE__ActionAddRespHeader:\n    case INGRESS__ACTION_TYPE__ActionAppendRespHeader:\n    case INGRESS__ACTION_TYPE__ActionAddParam:\n        switch (action->value_type) {\n        case INGRESS__ACTION_VALUE_TYPE__ActionStaticValue:\n        case INGRESS__ACTION_VALUE_TYPE__ActionDynamicValue:\n            if (action->key.data != NULL && action->value.data != NULL) {\n                rc = NGX_OK;\n            } else {\n                ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                    \"|ingress|service action value or key NULL|\");\n            }\n            break;\n        case INGRESS__ACTION_VALUE_TYPE__ActionValueUnDefined:\n        default:\n            ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, \"|ingress|service action value type error|\");\n            break;\n        }\n        break;\n    case INGRESS__ACTION_TYPE__ActionUnDefined:\n    default:\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, \"|ingress|service action type error|\");\n        break;\n    }\n    return rc;\n}\n\nstatic ngx_int_t\nngx_ingress_service_parse_action(ngx_ingress_t *ingress, \n    ngx_ingress_action_t *shm_action, Ingress__Action *pb_action)\n{\n    ngx_memset(shm_action, 0, sizeof(ngx_ingress_action_t));\n    shm_action->action_type = pb_action->action_type;\n    if (pb_action->has_value_type) {\n        shm_action->value_type = pb_action->value_type;\n    } \n    if (pb_action->key != NULL && ngx_strlen(pb_action->key) != 0) {\n        shm_action->key.len = ngx_strlen(pb_action->key);\n        shm_action->key.data\n            = ngx_shm_pool_calloc(ingress->pool, shm_action->key.len);\n        if (shm_action->key.data == NULL) {\n            ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                    \"|ingress|alloc action key error, len:%d|\", shm_action->key.len);\n            return NGX_ERROR;\n        }\n        ngx_memcpy(shm_action->key.data, pb_action->key, shm_action->key.len);\n    }\n\n    if (pb_action->value != NULL && ngx_strlen(pb_action->value) != 0) {\n        shm_action->value.len = ngx_strlen(pb_action->value);\n        shm_action->value.data =\n            ngx_shm_pool_calloc(ingress->pool, shm_action->value.len);\n        \n        if (shm_action->value.data == NULL) {\n            ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                    \"|ingress|alloc action value error, len:%d|\",\n                    shm_action->value.len);\n            return NGX_ERROR;\n        }\n        ngx_memcpy(shm_action->value.data, pb_action->value, shm_action->value.len);\n    }\n    \n    if (ngx_ingress_service_action_data_check(shm_action) != NGX_OK) {\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                \"|ingress|action data check error|\");\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\nstatic ngx_int_t\nngx_ingress_update_shm_service(ngx_ingress_t *ingress,\n    ngx_ingress_service_t *shm_service,\n    Ingress__VirtualService *pbservice\n    )\n{\n    size_t                  i;\n    ngx_int_t               weight, start;\n\n    /* name */\n    if (pbservice->service_name == NULL) {\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                        \"|ingress|pb service name is null|\");\n        return NGX_ERROR;\n    }\n\n    ngx_int_t len = strlen(pbservice->service_name);\n    shm_service->name.data = ngx_shm_pool_calloc(ingress->pool, len);\n    if (shm_service->name.data == NULL) {\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                        \"|ingress|alloc service name failed|\");\n        return NGX_ERROR;\n    }\n    shm_service->name.len = len;\n    ngx_memcpy(shm_service->name.data, pbservice->service_name, len);\n\n    /* force https */\n    if (pbservice->has_force_https) {\n        shm_service->force_https = pbservice->force_https;\n    } else {\n        shm_service->force_https = NGX_INGRESS_FORCE_HTTPS_UNSET; \n    }\n\n    /* timeout */\n    if (pbservice->timeout_ms != NULL) {\n        shm_service->timeout.set_flag = NGX_INGRESS_TIMEOUT_SET; \n        if (pbservice->timeout_ms->has_connect_timeout) {\n            shm_service->timeout.connect_timeout = pbservice->timeout_ms->connect_timeout;\n        } else {\n            shm_service->timeout.connect_timeout = NGX_CONF_UNSET_MSEC;\n        }\n\n        if (pbservice->timeout_ms->has_read_timeout) {\n            shm_service->timeout.read_timeout = pbservice->timeout_ms->read_timeout;\n        } else {\n            shm_service->timeout.read_timeout = NGX_CONF_UNSET_MSEC;\n        }\n\n        if (pbservice->timeout_ms->has_write_timeout) {\n            shm_service->timeout.write_timeout = pbservice->timeout_ms->write_timeout;\n        } else {\n            shm_service->timeout.write_timeout = NGX_CONF_UNSET_MSEC;\n        }\n    } else {\n        shm_service->timeout.set_flag = NGX_INGRESS_TIMEOUT_UNSET;\n    }\n\n    /* upstreams */\n    if (pbservice->n_upstreams == 0) {\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                            \"|ingress|service no upstream|service=%V|\", &shm_service->name);\n        return NGX_ERROR;\n    }\n\n    shm_service->upstreams = ngx_shm_array_create(ingress->pool, pbservice->n_upstreams, sizeof(ngx_ingress_upstream_t));\n    if (shm_service->upstreams == NULL) {\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                            \"|ingress|alloc upstreams failed|service=%V|\", &shm_service->name);\n        return NGX_ERROR;\n    }\n\n    weight = 0;\n    start = 0;\n\n    Ingress__Upstream **upstreams = pbservice->upstreams;\n    for (i = 0; i < pbservice->n_upstreams; i++) {\n        ngx_ingress_upstream_t *shm_ups = ngx_shm_array_push(shm_service->upstreams);\n        if (shm_ups == NULL) {\n            ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                            \"|ingress|shm_ups push array failed|service=%V|\", &shm_service->name);\n            return NGX_ERROR;\n        }\n        if (upstreams[i]->has_weight) {\n            weight += upstreams[i]->weight;\n            shm_ups->start = start;\n            start += upstreams[i]->weight;\n            shm_ups->end = start;\n        }\n\n        if (upstreams[i]->target == NULL) {\n            ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                            \"|ingress|target is null|service=%V|\", &shm_service->name);\n            return NGX_ERROR;\n        }\n\n        ngx_int_t target_len = strlen(upstreams[i]->target);\n        shm_ups->target.data = ngx_shm_pool_calloc(ingress->pool, target_len);\n        if (shm_ups->target.data == NULL) {\n            ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                            \"|ingress|alloc target failed|service=%V|\", &shm_service->name);\n            return NGX_ERROR;\n        }\n        shm_ups->target.len = target_len;\n        ngx_memcpy(shm_ups->target.data, upstreams[i]->target, target_len);\n\n        ngx_log_error(NGX_LOG_DEBUG, ngx_cycle->log, 0,\n                      \"|ingress|shm_ups weight|%i|%i|%V|\", shm_ups->start, shm_ups->end, &shm_ups->target);\n    }\n    shm_service->upstream_weight = weight;\n\n    /* metadata */\n    Ingress__Metadata **pb_metadata = pbservice->metadata;\n\n    shm_service->metadata = ngx_shm_array_create(ingress->pool, pbservice->n_metadata, sizeof(ngx_ingress_metadata_t));\n    if (shm_service->metadata == NULL) {\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                            \"|ingress|alloc metadata failed|service=%V|\", &shm_service->name);\n        return NGX_ERROR;\n    }\n\n    for (i = 0; i < pbservice->n_metadata; i++) {\n        ngx_ingress_metadata_t *metadata = ngx_shm_array_push(shm_service->metadata);\n        if (metadata == NULL) {\n            ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                            \"|ingress|alloc metadata failed|service=%V|\", &shm_service->name);\n            return NGX_ERROR;\n        }\n\n        if (pb_metadata[i]->key == NULL || pb_metadata[i]->value == NULL) {\n            ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                            \"|ingress|meta data key or value is null|service=%V|\", &shm_service->name);\n            return NGX_ERROR;\n        }\n\n        ngx_int_t len = ngx_strlen(pb_metadata[i]->key);\n        metadata->key.data = ngx_shm_pool_calloc(ingress->pool, len);\n        if (metadata->key.data == NULL) {\n            ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                            \"|ingress|alloc metadata key failed|\");\n            return NGX_ERROR;\n        }\n        metadata->key.len = len;\n        ngx_memcpy(metadata->key.data, pb_metadata[i]->key, len);\n\n        len = ngx_strlen(pb_metadata[i]->value);\n        metadata->value.data = ngx_shm_pool_calloc(ingress->pool, len);\n        if (metadata->value.data == NULL) {\n            ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                            \"|ingress|alloc metadata value failed|\");\n            return NGX_ERROR;\n        }\n        metadata->value.len = len;\n        ngx_memcpy(metadata->value.data, pb_metadata[i]->value, len);\n    }\n\n    shm_service->action_a = ngx_shm_array_create(ingress->pool,\n            pbservice->n_action, sizeof(ngx_ingress_action_t));\n    if (shm_service->action_a == NULL) {\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                    \"|ingress|alloc action_a failed|service=%V|\", &shm_service->name);\n        return NGX_ERROR;\n    }\n\n    Ingress__Action **pb_action_a = pbservice->action;\n    for (i = 0; i < pbservice->n_action; i++) {\n        ngx_ingress_action_t *shm_action = ngx_shm_array_push(shm_service->action_a);\n        if (shm_action == NULL) {\n            ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                    \"|ingress|push action_a failed|service=%V|\", &shm_service->name);\n            return NGX_ERROR;\n        }\n        if (pb_action_a[i]->has_action_type) {\n            if (ngx_ingress_service_parse_action(ingress, shm_action, pb_action_a[i]) != NGX_OK) {\n                ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                        \"|ingress|parse action error|service=%V|\", &shm_service->name);\n                return NGX_ERROR;\n            }\n        } else {\n            ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                    \"|ingress|action type null|service=%V|\", &shm_service->name);\n        \n            return NGX_ERROR;\n        }\n\n    }\n\n    ngx_shm_sort_array(shm_service->metadata, ngx_ingress_metadata_compare);\n\n    return NGX_OK;\n}\n\nstatic int\nngx_path_prefix_compare(const void *c1, const void *c2)\n{\n    ngx_ingress_path_router_t *router1 = (ngx_ingress_path_router_t*)c1;\n    ngx_ingress_path_router_t *router2 = (ngx_ingress_path_router_t*)c2;\n\n    /* Shorter prefixes match first */\n    if (router1->prefix.len > router2->prefix.len) {\n        return -1;\n    } else if (router1->prefix.len < router2->prefix.len) {\n        return 1;\n    }\n\n    return ngx_comm_str_compare(&router1->prefix, &router2->prefix);\n}\n\n\n/* \n * this function check the tag item's data when reading configuration.\n */\nstatic ngx_int_t\nngx_ingress_pb_tag_item_data_check(ngx_ingress_tag_item_t *item)\n{\n    ngx_int_t ret = NGX_ERROR;\n    switch (item->match_type) {\n        case INGRESS__MATCH_TYPE__MatchUnDefined:\n            ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                \"|ingress|tag item match type invalid|\");\n            break;\n        case INGRESS__MATCH_TYPE__WholeMatch:\n            if (item->condition.value_str.data == NULL || item->condition.value_str.len == 0) {\n                ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                    \"|ingress|tag item condition value_str invalid|\");\n                break;\n            }\n            ret = NGX_OK;\n            break;\n        case INGRESS__MATCH_TYPE__StrListInMatch:\n            if (item->condition.value_a == NULL || item->condition.value_a->nelts == 0) {\n                ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                    \"|ingress|tag item condition value_a invalid|\");\n                break;\n            }\n            ret = NGX_OK;\n            break;\n        case INGRESS__MATCH_TYPE__ModCompare:\n            if (item->condition.divisor == 0\n                || item->condition.op == INGRESS__OPERATOR_TYPE__OperatorUnDefined) {\n                ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                    \"|ingress|tag item condition divisor or operator invalid|\");\n                break;\n            }\n            ret = NGX_OK;\n            break;\n        default:\n            break;\n    }  \n\n    return ret;\n}\n\nstatic ngx_int_t\nngx_ingress_pb_parse_tag_condition(ngx_ingress_t *ingress,\n    ngx_ingress_tag_condition_t *cond, Ingress__TagItemCondition *pb_cond)\n{\n    ngx_uint_t i = 0;\n    if (pb_cond->value_str != NULL && strlen(pb_cond->value_str) > 0) {\n        cond->value_str.len = strlen(pb_cond->value_str);\n        cond->value_str.data = ngx_shm_pool_calloc(ingress->pool, cond->value_str.len);\n        if (cond->value_str.data == NULL) {\n            ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                \"|ingress|tag condition value_str alloc error, len:%d|\", cond->value_str.len);\n            return NGX_ERROR;\n        }\n        ngx_memcpy(cond->value_str.data, pb_cond->value_str, cond->value_str.len);\n    }\n    \n    if (pb_cond->value_list != NULL && pb_cond->value_list->n_value > 0) {\n        cond->value_a = ngx_shm_array_create(ingress->pool,\n                pb_cond->value_list->n_value, sizeof(ngx_str_t));\n        if (cond->value_a == NULL) {\n            ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                \"|ingress|tag condition value_a alloc error, n_value:%d\", pb_cond->value_list->n_value);\n            return NGX_ERROR;\n        }\n        for (i = 0; i < pb_cond->value_list->n_value; i++) {\n            char *p_v = pb_cond->value_list->value[i];\n            if (p_v == NULL || strlen(p_v) == 0) {\n                ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                    \"|ingress|tag condition value_list str NULL|\");\n                return NGX_ERROR;\n            }\n         \n            ngx_str_t *v_str = ngx_shm_array_push(cond->value_a); \n            if (v_str == NULL) {\n                ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                    \"|ingress|tag condition value_a push error|\");\n                return NGX_ERROR;\n            }\n            v_str->len = strlen(p_v);\n            v_str->data = ngx_shm_pool_calloc(ingress->pool, v_str->len);\n            if (v_str->data == NULL) {\n                ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                    \"|ingress|tag condition value alloc error, len:%d|\", v_str->len);\n                return NGX_ERROR;\n            }\n            ngx_memcpy(v_str->data, p_v, v_str->len);\n        }\n        ngx_shm_sort_array(cond->value_a, (ngx_shm_compar_func)ngx_ingress_tag_value_compar);\n    }\n\n    if (pb_cond->has_divisor) {\n        cond->divisor = pb_cond->divisor;\n    }\n\n    if (pb_cond->has_remainder) {\n        cond->remainder = pb_cond->remainder;\n    }\n\n    if (pb_cond->has_operator_) {\n        cond->op = pb_cond->operator_;\n    }\n\n    return NGX_OK;\n}\n\nstatic ngx_int_t\nngx_ingress_pb_parse_tag_item(ngx_ingress_t *ingress,\n    ngx_ingress_tag_item_t *shm_item, Ingress__TagItem *pb_item)\n{\n    shm_item->location = pb_item->location;\n    shm_item->match_type = pb_item->match_type;\n    \n    /* parse key from pb */\n    if (pb_item->key == NULL || ngx_strlen(pb_item->key) == 0) {\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n            \"|ingress|tag itme key NULL|\");\n        return NGX_ERROR;\n    }\n    shm_item->key.len = ngx_strlen(pb_item->key);\n    shm_item->key.data = ngx_shm_pool_calloc(ingress->pool, shm_item->key.len);\n    if (shm_item->key.data == NULL) {\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n            \"|ingress|tag item key alloc error, key len:%d|\", shm_item->key.len);\n        return NGX_ERROR;\n    }\n    ngx_memcpy(shm_item->key.data, pb_item->key, shm_item->key.len);\n    \n    /* parse condition from pb */\n    if (pb_item->condition == NULL) {\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n            \"|ingress|tag itme condition NULL|\");\n        return NGX_ERROR; \n    }\n    \n    if (ngx_ingress_pb_parse_tag_condition(ingress, &shm_item->condition,\n            pb_item->condition) != NGX_OK) {\n        return NGX_ERROR;\n    } \n   \n    if (ngx_ingress_pb_tag_item_data_check(shm_item) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\nstatic ngx_int_t\nngx_ingress_update_shm_tag_routers(ngx_ingress_t *ingress,\n    size_t n_tags, Ingress__TagRouter **pb_tag_routers,\n    ngx_shm_array_t **pptags)\n{\n    size_t                  i, j, k;\n    ngx_shm_array_t        *ptags = NULL;\n\n#if 0\n\n#define MAX_TAG_ROUTE_NUMS      2\n#define MAX_TAG_RULE_NUMS       2\n#define MAX_TAG_ITEM_NUMS       2\n\n    Ingress__TagRouter      tag_route[MAX_TAG_ROUTE_NUMS];\n    Ingress__TagRouter     *p_tag_route[MAX_TAG_ROUTE_NUMS];\n    Ingress__TagRule        tag_rule[MAX_TAG_RULE_NUMS];\n    Ingress__TagRule       *p_tag_rule[MAX_TAG_RULE_NUMS];\n    Ingress__TagItem        tag_item[MAX_TAG_ITEM_NUMS];\n    Ingress__TagItem       *p_tag_item[MAX_TAG_ITEM_NUMS];\n    char                   *service_names[MAX_TAG_ROUTE_NUMS] = {\"*.wap.keruyun.test/\",\n                    \"100x100w.heyi.test/\", \"127.api.taobao.net/\", \"110.daily.taobao.net/\"};\n    char                   *keys[MAX_TAG_ITEM_NUMS] = {\"appkey\", \"x-appkey\", \"Appkey\", \"X-Appkey\"};\n    char                   *values[MAX_TAG_ITEM_NUMS] = {\"21646297\", \"21380790\", \"23524755\", \"25005935\"};\n    pb_tag_routers = &p_tag_route;\n    n_tags = MAX_TAG_ROUTE_NUMS;\n\n    for (i = 0; i < MAX_TAG_ROUTE_NUMS; i++) {\n        tag_route[i].n_rules = MAX_TAG_RULE_NUMS;\n        tag_route[i].service_name = service_names[i];\n        tag_route[i].rules = &p_tag_rule;\n        p_tag_route[i] = &tag_route[i];\n    }\n\n    for (j = 0; j < MAX_TAG_RULE_NUMS; j++) {\n        tag_rule[j].n_items = MAX_TAG_ITEM_NUMS;\n        tag_rule[j].items = &p_tag_item;\n        p_tag_rule[j] = &tag_rule[j];\n    }\n\n    for (k = 0; k < MAX_TAG_ITEM_NUMS; k++) {\n        tag_item[k].has_location = 1;\n        tag_item[k].has_match_type = 1;\n        tag_item[k].location = INGRESS__LOCATION_TYPE__LocHttpHeader;\n        tag_item[k].match_type = INGRESS__MATCH_TYPE__WholeMatch;\n        tag_item[k].key = keys[k];\n        tag_item[k].value = values[k];\n        p_tag_item[k] = &tag_item[k];\n    }\n#endif\n\n    if (n_tags) {\n        ptags = ngx_shm_array_create(ingress->pool, n_tags, sizeof(ngx_ingress_tag_router_t));\n        if (ptags == NULL) {\n            ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                            \"|ingress|tag router array alloc failed|\");\n            return NGX_ERROR;\n        }\n\n        /* Traverse and process each Tag Route */\n        for (i = 0; i < n_tags; i++) {\n\n            /* Each tag route must clearly indicate the target service */\n            if (pb_tag_routers[i]->service_name == NULL) {\n                ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                                \"|ingress|pb service name is null|\");\n                return NGX_ERROR;\n            }\n\n            /* Each tag route must have clear matching rules */\n            if (pb_tag_routers[i]->n_rules == 0) {\n                ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                                \"|ingress|pb rules is empty|\");\n                return NGX_ERROR;\n            }\n\n            ngx_ingress_tag_router_t *shm_tag = ngx_shm_array_push(ptags);\n\n            if (shm_tag == NULL) {\n                ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                        \"|ingress|tag router alloc failed|\");\n                return NGX_ERROR;\n            }\n\n            shm_tag->rules = ngx_shm_array_create(ingress->pool, pb_tag_routers[i]->n_rules, sizeof(ngx_ingress_tag_rule_t));\n\n            if (shm_tag->rules == NULL) {\n                ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                        \"|ingress|tag rule array alloc failed|\");\n                return NGX_ERROR;\n            }\n\n            /* Traverse and process each Tag Rule */\n            for (j = 0; j < pb_tag_routers[i]->n_rules; j++) {\n\n                Ingress__TagRule ** pb_rules = pb_tag_routers[i]->rules;\n                ngx_ingress_tag_rule_t *shm_rule = ngx_shm_array_push(shm_tag->rules);\n                if (shm_rule == NULL) {\n                    ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                            \"|ingress|tag rule alloc failed|\");\n                    return NGX_ERROR;\n                }\n\n                /* Each tag rule must have an explicit match */\n                if (pb_rules[j]->n_items == 0) {\n                    ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                                    \"|ingress|pb rules is empty|\");\n                    return NGX_ERROR;\n                }\n\n                shm_rule->items = ngx_shm_array_create(ingress->pool, pb_rules[j]->n_items, sizeof(ngx_ingress_tag_item_t));\n                if (shm_rule->items == NULL) {\n                    ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                            \"|ingress|tag item array alloc failed|\");\n                    return NGX_ERROR;\n                }\n\n                /* Traverse and process each Tag Item */\n                for (k = 0; k < pb_rules[j]->n_items; k++) {\n\n                    Ingress__TagItem ** pb_items = pb_rules[j]->items;\n                    ngx_ingress_tag_item_t *shm_item = ngx_shm_array_push(shm_rule->items);\n                    if (shm_item == NULL) {\n                        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                                \"|ingress|tag item alloc failed|\");\n                        return NGX_ERROR;\n                    }\n                    ngx_memset(shm_item, 0, sizeof(ngx_ingress_tag_item_t));\n\n                    if (pb_items[k]->has_location && pb_items[k]->has_match_type) {\n                        if (ngx_ingress_pb_parse_tag_item(ingress, shm_item, pb_items[k]) != NGX_OK) {\n                            ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                                    \"|ingress|pb parse tag item error|\");\n                        }\n                    } else {\n                        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                                \"|ingress|miss loc type or match type|\");\n                        return NGX_ERROR;\n                    }\n                }\n            }\n\n            /* matched service */\n            shm_tag->service = ngx_ingress_get_service(ingress, pb_tag_routers[i]->service_name);\n            if (shm_tag->service == NULL) {\n                ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                                \"|ingress|service not exist|\");\n                return NGX_ERROR;\n            }\n        }\n\n        *pptags = ptags;\n\n    } else {\n        *pptags = NULL;\n    }\n\n    return NGX_OK;\n}\n\nstatic ngx_int_t\nngx_ingress_update_shm_host(ngx_ingress_t *ingress,\n    ngx_ingress_host_router_t *shm_host,\n    Ingress__HostRouter *pbrouter,\n    ngx_str_t *remove_prefix\n    )\n{\n    size_t                  i;\n    ngx_int_t               rc = NGX_ERROR;\n\n    if (pbrouter->host == NULL) {\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                        \"|ingress|pbhost is null|\");\n        return NGX_ERROR;\n    }\n\n    /* host */\n    ngx_int_t len = ngx_strlen(pbrouter->host) - remove_prefix->len;\n    shm_host->host.data = ngx_shm_pool_calloc(ingress->pool, len);\n    if (shm_host->host.data == NULL) {\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                        \"|ingress|host alloc failed|\");\n        return NGX_ERROR;\n    }\n    shm_host->host.len = len;\n    ngx_strlow(shm_host->host.data, (u_char*)pbrouter->host + remove_prefix->len, len);\n\n    ngx_log_error(NGX_LOG_DEBUG, ngx_cycle->log, 0,\n                  \"|ingress|add host|%V|%V|\", &shm_host->host, remove_prefix);\n\n    /* path */\n    shm_host->paths = ngx_shm_array_create(ingress->pool, pbrouter->n_paths, sizeof(ngx_ingress_path_router_t));\n    if (shm_host->paths == NULL) {\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                        \"|ingress|path alloc failed|host=%V|\", &shm_host->host);\n        return NGX_ERROR;\n    }\n\n    Ingress__PathRouter **pbpath = pbrouter->paths;\n    for (i = 0; i < pbrouter->n_paths; i++) {\n        ngx_ingress_path_router_t *shm_path = ngx_shm_array_push(shm_host->paths);\n        if (shm_path == NULL) {\n            ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                        \"|ingress|prefix router alloc failed|host=%V|\", &shm_host->host);\n            return NGX_ERROR;\n        }\n\n        if (pbpath[i]->prefix == NULL) {\n            ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                        \"|ingress|path prefix is null|host=%V|\", &shm_host->host);\n            return NGX_ERROR; \n        }\n\n        len = ngx_strlen(pbpath[i]->prefix);\n        shm_path->prefix.data = ngx_shm_pool_calloc(ingress->pool, len);\n        if (shm_path->prefix.data == NULL) {\n            ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                        \"|ingress|prefix data alloc failed|host=%V|\", &shm_host->host);\n            return NGX_ERROR;\n        }\n        shm_path->prefix.len = len;\n        ngx_strlow(shm_path->prefix.data, (u_char*)pbpath[i]->prefix, len);\n\n        shm_path->service = ngx_ingress_get_service(ingress, pbpath[i]->service_name);\n        if (shm_path->service == NULL) {\n            ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                            \"|ingress|service not exist|host=%V|prefix=%V|\", &shm_host->host, &shm_path->prefix);\n            return NGX_ERROR;\n        }\n        ngx_log_error(NGX_LOG_DEBUG, ngx_cycle->log, 0,\n                      \"|ingress|prefix service|host=%V|prefix=%V|%p|\", &shm_host->host, &shm_path->prefix, shm_path->service);\n\n        /* Subdivided PATH granularity, different tags match routes */\n        rc = ngx_ingress_update_shm_tag_routers(ingress, pbpath[i]->n_tags, pbpath[i]->tags, &shm_path->tags);\n        if (rc != NGX_OK) {\n            ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                        \"|ingress|update path tag routes failed|%V|%V|\", &shm_host->host, &shm_path->prefix);\n            return NGX_ERROR;\n        }\n    }\n\n    ngx_shm_sort_array(shm_host->paths, ngx_path_prefix_compare);\n\n    /* Under the host granularity, different tags match routes */\n    rc = ngx_ingress_update_shm_tag_routers(ingress, pbrouter->n_tags, pbrouter->tags, &shm_host->tags);\n    if (rc != NGX_OK) {\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                    \"|ingress|update path tag routes failed|%V|\", &shm_host->host);\n        return NGX_ERROR;\n    }\n\n    /* service */\n    if (pbrouter->service_name == NULL || ngx_strlen(pbrouter->service_name) == 0) {\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                      \"|ingress|host service is null|\");\n        return NGX_ERROR;\n    }\n\n    shm_host->service = ngx_ingress_get_service(ingress, pbrouter->service_name);\n    if (shm_host->service == NULL) {\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                        \"|ingress|service not exist|host=%V|\", &shm_host->host);\n        \n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\nngx_int_t\nngx_ingress_update_shm_by_pb(ngx_ingress_gateway_t *gateway, ngx_ingress_shared_memory_config_t *shm_pb_config, ngx_ingress_t *ingress)\n{\n    size_t                          i;\n    ngx_int_t                       rc;\n\n    ingress->service_map = ngx_shm_hash_create(ingress->pool, gateway->hash_size, ngx_ingress_service_hash, ngx_ingress_service_compare);\n    if (ingress->service_map == NULL) {\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                      \"|ingress|service map create failed|gateway=%V|\", &gateway->name);\n        return NGX_ERROR;\n    }\n\n    Ingress__VirtualService **pbservice = shm_pb_config->pbconfig->services;\n    /* service */\n    for (i = 0; i < shm_pb_config->pbconfig->n_services; i++) {\n        ngx_ingress_service_t *shm_service = ngx_shm_pool_calloc(ingress->pool, sizeof(ngx_ingress_service_t));\n        if (shm_service == NULL) {\n            ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                          \"|ingress|alloc service failed|gateway=%V|\", &gateway->name);\n            return NGX_ERROR;\n        }\n\n        rc = ngx_ingress_update_shm_service(ingress, shm_service, pbservice[i]);\n        if (rc != NGX_OK) {\n            ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                          \"|ingress|update service failed|gateway=%V|\", &gateway->name);\n            return NGX_ERROR;\n        }\n\n        rc = ngx_shm_hash_add(ingress->service_map, shm_service);\n        if (rc != NGX_OK) {\n            ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                          \"|ingress|service ngx_shm_hash_add failed|service=%V|\", &shm_service->name);\n            return NGX_ERROR;\n        }\n    }\n\n    /* router */\n    ingress->host_map = ngx_shm_hash_create(ingress->pool, gateway->hash_size, ngx_ingress_host_hash, ngx_ingress_host_compare);\n    if (ingress->host_map == NULL) {\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                      \"|ingress|host map create failed|gateway=%V|\", &gateway->name);\n        return NGX_ERROR;\n    }\n    ingress->wildcard_host_map = ngx_shm_hash_create(ingress->pool, gateway->hash_size, ngx_ingress_host_hash, ngx_ingress_host_compare);\n    if (ingress->wildcard_host_map == NULL) {\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                      \"|ingress|wildcard_host_map create failed|gateway=%V|\", &gateway->name);\n        return NGX_ERROR;\n    }\n\n    Ingress__Router **pbrouter = shm_pb_config->pbconfig->routers;\n\n    for (i = 0; i < shm_pb_config->pbconfig->n_routers; i++) {\n        if (pbrouter[i]->host_router != NULL) {\n            Ingress__HostRouter *pb_host_router = pbrouter[i]->host_router;\n\n            ngx_str_t wildcard_prefix = ngx_string(\"*.\");\n            ngx_str_t remove_prefix = ngx_null_string;\n\n            ngx_ingress_host_router_t *shm_host = ngx_shm_pool_calloc(ingress->pool, sizeof(ngx_ingress_host_router_t));\n            if (shm_host == NULL) {\n                ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                              \"|ingress|host router alloc failed|gateway=%V|\", &gateway->name);\n                return NGX_ERROR;\n            }\n\n            ngx_shm_hash_t *host_map = ingress->host_map;\n            if (pb_host_router->host != NULL\n                && ngx_strncmp(pb_host_router->host, wildcard_prefix.data, wildcard_prefix.len) == 0)\n            {\n                ngx_log_error(NGX_LOG_DEBUG, ngx_cycle->log, 0,\n                              \"|ingress|match wildcard|host=%s|\", pb_host_router->host);\n\n                host_map = ingress->wildcard_host_map;\n                remove_prefix = wildcard_prefix;\n            }\n\n            rc = ngx_ingress_update_shm_host(ingress, shm_host, pb_host_router, &remove_prefix);\n            if (rc != NGX_OK) {\n                ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                              \"|ingress|update host router failed|gateway=%V|\", &gateway->name);\n                return NGX_ERROR;\n            }\n\n            rc = ngx_shm_hash_add(host_map, shm_host);\n            if (rc != NGX_OK) {\n                ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                              \"|ingress|host ngx_shm_hash_add failed|host=%V\", &shm_host->host);\n                return NGX_ERROR;\n            }\n\n            ngx_log_error(NGX_LOG_DEBUG, ngx_cycle->log, 0,\n                          \"|ingress|host add succ|host=%V\", &shm_host->host);\n        }\n    }\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "modules/ngx_ingress_module/ngx_ingress_protobuf.h",
    "content": "/*\n * Copyright (C) 2020-2023 Alibaba Group Holding Limited\n */\n\n#ifndef NGX_INGRESS_PROTOBUF_H\n#define NGX_INGRESS_PROTOBUF_H\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n#include <ngx_buf.h>\n#include <ngx_comm_encrypt.h>\n\n#include \"ingress.pb-c.h\"\n\ntypedef enum {\n    NGX_INGRESS_SHARED_MEMORY_TYPE_EMPTY        = 0,\n    NGX_INGRESS_SHARED_MEMORY_TYPE_SERVICE      = 1,\n} ngx_ingress_shared_memory_type_e;\n\ntypedef enum {\n    NGX_INGRESS_SHARED_MEMORY_TYPE_SUCCESS      = 0,\n    NGX_INGRESS_SHARED_MEMORY_TYPE_ERR          = 1,\n} ngx_ingress_shared_memory_status_e;\n\ntypedef struct {\n    ngx_ingress_shared_memory_type_e     type;\n    uint64_t                             version;\n    u_char                               md5_digit[NGX_COMM_MD5_HEX_LEN];\n    Ingress__Config                     *pbconfig;\n} ngx_ingress_shared_memory_config_t;\n\ntypedef struct {\n    ngx_uint_t      shm_size;\n    int             shm_fd;\n    u_char         *base_address;\n\n    ngx_fd_t        lock_fd;\n} ngx_ingress_shared_memory_t;\n\nngx_ingress_shared_memory_t *ngx_ingress_shared_memory_create(ngx_str_t *shm_name, ngx_uint_t shm_size, ngx_str_t *lock_file);\nvoid ngx_ingress_shared_memory_free(ngx_ingress_shared_memory_t *shared);\n\nngx_int_t ngx_ingress_shared_memory_write_status(ngx_ingress_shared_memory_t *shared, ngx_ingress_shared_memory_status_e status);\n\ntypedef enum {\n    ngx_ingress_pb_read_version = 1,\n    ngx_ingress_pb_read_body = 2,\n} ngx_ingress_pb_read_mode_e;\n\nngx_int_t ngx_ingress_shared_memory_read_pb(ngx_ingress_shared_memory_t *shared, ngx_ingress_shared_memory_config_t *shm_pb_config, ngx_ingress_pb_read_mode_e mode);\nvoid ngx_ingress_shared_memory_free_pb(ngx_ingress_shared_memory_config_t *shm_pb_config);\n\n\n#endif // NGX_INGRESS_PROTOBUF_H\n"
  },
  {
    "path": "modules/ngx_multi_upstream_module/config",
    "content": "if [ \"$ngx_module_link\" = DYNAMIC ]; then\n    cat << END\n    $0: error: ngx_multi_upstream not support build as a dynamic module.\nEND\n    exit 1\nfi\n\nngx_addon_name=ngx_http_multi_upstream_module\nHTTP_MODULES=\"$HTTP_MODULES ngx_http_multi_upstream_module\"\nCORE_DEPS=\"$CORE_DEPS $ngx_addon_dir/ngx_multi_upstream_module.h $ngx_addon_dir/ngx_http_multi_upstream_module.h $ngx_addon_dir/ngx_http_multi_upstream.c\"\nNGX_ADDON_SRCS=\"$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_multi_upstream_module.c $ngx_addon_dir/ngx_multi_upstream_module.c\"\nHTTP_INCS=\"$HTTP_INCS $ngx_addon_dir\"\n\nif [ $STREAM != NO ]; then\n    ngx_module_name=ngx_stream_multi_upstream_module\n    ngx_module_type=STREAM\n    ngx_module_link=ADDON\n    ngx_module_incs=$ngx_addon_dir \n    ngx_module_deps=$ngx_addon_dir/ngx_stream_multi_upstream_module.h\n    ngx_module_srcs=$ngx_addon_dir/ngx_stream_multi_upstream_module.c\n\n    if [ $STREAM = DYNAMIC ]; then\n        ngx_module_link=DYNAMIC\n    else\n        ngx_module_link=ADDON\n    fi\n\n    . auto/module\nfi\n\nhave=T_NGX_MULTI_UPSTREAM . auto/have\n"
  },
  {
    "path": "modules/ngx_multi_upstream_module/ngx_http_multi_upstream.c",
    "content": "\n/*\n * Copyright (C) Mengqi Wu (Pull)\n * Copyright (C) 2017-2019 Alibaba Group Holding Limited\n */\n\nvoid\nngx_http_multi_upstream_send_response(ngx_http_request_t *r, ngx_http_upstream_t *u)\n{\n    int                        tcp_nodelay;\n    ngx_int_t                  rc;\n    ngx_connection_t          *c;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http multi upstream send response: %p, %p\", r, u);\n\n    rc = ngx_http_send_header(r);\n\n    if (rc == NGX_ERROR || rc > NGX_OK || r->post_action) {\n        ngx_http_upstream_finalize_request(r, u, rc);\n        return;\n    }\n\n    u->header_sent = 1;\n\n    c = r->connection;\n\n    if (r->header_only) {\n\n        if (!u->buffering) {\n            ngx_http_upstream_finalize_request(r, u, rc);\n            return;\n        }\n\n        if (!u->cacheable && !u->store) {\n            ngx_http_upstream_finalize_request(r, u, rc);\n            return;\n        }\n\n        u->pipe->downstream_error = 1;\n    }\n\n    if (r->request_body && r->request_body->temp_file\n            && !u->conf->preserve_output) {\n        ngx_pool_run_cleanup_file(r->pool, r->request_body->temp_file->file.fd);\n        r->request_body->temp_file->file.fd = NGX_INVALID_FILE;\n    }\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    if (!u->buffering) {\n\n        if (u->input_filter == NULL) {\n            u->input_filter_init = ngx_http_upstream_non_buffered_filter_init;\n            u->input_filter = ngx_http_upstream_non_buffered_filter;\n            u->input_filter_ctx = r;\n        }\n\n        u->read_event_handler = ngx_http_upstream_process_non_buffered_upstream;\n        r->write_event_handler =\n                             ngx_http_upstream_process_non_buffered_downstream;\n\n        r->limit_rate = 0;\n        r->limit_rate_set = 1;\n\n        if (u->input_filter_init(u->input_filter_ctx) == NGX_ERROR) {\n            ngx_http_upstream_finalize_request(r, u, NGX_ERROR);\n            return;\n        }\n\n        if (clcf->tcp_nodelay && c->tcp_nodelay == NGX_TCP_NODELAY_UNSET) {\n            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"tcp_nodelay\");\n\n            tcp_nodelay = 1;\n\n            if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY,\n                               (const void *) &tcp_nodelay, sizeof(int)) == -1)\n            {\n                ngx_connection_error(c, ngx_socket_errno,\n                                     \"setsockopt(TCP_NODELAY) failed\");\n                ngx_http_upstream_finalize_request(r, u, NGX_ERROR);\n                return;\n            }\n\n            c->tcp_nodelay = NGX_TCP_NODELAY_SET;\n        }\n\n        if (u->length == 0) {\n            if (ngx_http_send_special(r, NGX_HTTP_FLUSH) == NGX_ERROR) {\n                ngx_http_upstream_finalize_request(r, u, NGX_ERROR);\n                return;\n            }\n\n            ngx_http_upstream_process_non_buffered_downstream(r);\n        }\n\n        return;\n    }\n}\n\n\n//backend pc next, will close pc and do upstream_next for each front relate the pc\nvoid\nngx_http_multi_upstream_next(ngx_connection_t *pc, ngx_uint_t ft_type)\n{\n    ngx_multi_connection_t      *multi_c;\n    ngx_multi_data_t            *item;\n    ngx_queue_t                 *data, *q, *tmp;\n    ngx_http_request_t          *r;\n\n    multi_c = ngx_get_multi_connection(pc);\n    data = &multi_c->data;\n\n    ngx_http_multi_upstream_connection_detach(pc);\n\n    for ( ; ; ) {\n        if (ngx_queue_empty(data)) {\n            break;\n        }\n\n        q = ngx_queue_last(data);\n        item = ngx_queue_data(q, ngx_multi_data_t, queue);\n        r = item->data;\n        ngx_http_upstream_next(r, r->upstream, ft_type);\n\n        tmp = ngx_queue_last(data);\n        if (tmp == q) {\n            ngx_queue_remove(tmp);\n            ngx_log_error(NGX_LOG_ERR, pc->log, 0,\n                    \"multi connection next but queue exist %p\", pc);\n            continue;\n        }\n    }\n\n    ngx_http_multi_upstream_connection_close(pc);\n}\n\n//backend pc finalize, will close pc and do finalize for each front relate  the pc\nvoid\nngx_http_multi_upstream_finalize_request(ngx_connection_t *c, ngx_int_t rc)\n{\n    ngx_multi_connection_t      *multi_c;\n    ngx_multi_data_t            *item;\n    ngx_queue_t                 *data, *q, *tmp;\n    ngx_http_request_t          *r;\n\n    multi_c = ngx_get_multi_connection(c);\n    data = &multi_c->data;\n\n    ngx_http_multi_upstream_connection_detach(c);\n\n    for ( ; ; ) {\n        if (ngx_queue_empty(data)) {\n            break;\n        }\n\n        q = ngx_queue_last(data);\n        item = ngx_queue_data(q, ngx_multi_data_t, queue);\n        r = item->data;\n        ngx_http_upstream_finalize_request(r, r->upstream, rc);\n\n        tmp = ngx_queue_last(data);\n        if (tmp == q) {\n            ngx_queue_remove(tmp);\n            ngx_log_error(NGX_LOG_ERR, c->log, 0,\n                    \"multi connection finalize but queue exist %p\", c);\n            continue;\n        }\n    }\n\n    ngx_http_multi_upstream_connection_close(c);\n}\n\n\nvoid\nngx_http_multi_upstream_process_non_buffered_request(ngx_http_request_t *r)\n{\n    ngx_int_t                  rc;\n    ngx_http_upstream_t       *u;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    ngx_connection_t          *downstream;\n\n    u = r->upstream;\n    downstream = r->connection;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n            \"multi: http upstream send body: %p, %p\", r, u);\n\n    if (u->out_bufs || u->busy_bufs || downstream->buffered) {\n        rc = ngx_http_output_filter(r, u->out_bufs);\n\n        if (rc == NGX_ERROR) {\n            ngx_http_upstream_finalize_request(r, u, NGX_ERROR);\n            return;\n        }\n\n        ngx_chain_update_chains(r->pool, &u->free_bufs, &u->busy_bufs,\n                &u->out_bufs, u->output.tag);\n    }\n\n    if (u->busy_bufs == NULL) {\n\n        if (u->length == 0) {\n\n            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                    \"http multi upstream finalize: %p, %p\", r, u);\n            ngx_http_upstream_finalize_request(r, u, 0);\n            return;\n        }\n\n        ngx_reset_pool(u->send_pool);\n\n    }\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    if (downstream->data == r) {\n        if (ngx_handle_write_event(downstream->write, clcf->send_lowat)\n                != NGX_OK)\n        {\n            ngx_http_upstream_finalize_request(r, u, NGX_ERROR);\n            return;\n        }\n    }\n\n    if (downstream->write->active && !downstream->write->ready) {\n        ngx_add_timer(downstream->write, clcf->send_timeout);\n    } else if (downstream->write->timer_set) {\n        ngx_del_timer(downstream->write);\n    }\n#if 0\n    if (ngx_handle_read_event(upstream->read, 0) != NGX_OK) {\n        ngx_http_upstream_finalize_request(r, u, NGX_ERROR);\n        return;\n    }\n\n    if (upstream->read->active && !upstream->read->ready) {\n        ngx_add_timer(upstream->read, u->conf->read_timeout);\n    } else if (upstream->read->timer_set) {\n        ngx_del_timer(upstream->read);\n    }\n#endif\n}\n\nngx_int_t\nngx_http_multi_upstream_write_handler(ngx_connection_t *pc)\n{\n    ngx_http_request_t      *fake_r, *real_r;\n    ngx_http_upstream_t     *fake_u, *real_u;\n    ngx_multi_connection_t  *multi_c;\n    ngx_queue_t             *q, tmp_queue;\n\n    fake_r = pc->data;\n    fake_u = fake_r->upstream;\n\n    multi_c = ngx_get_multi_connection(pc);\n\n    ngx_queue_init(&tmp_queue);\n\n    while (!ngx_queue_empty(&multi_c->waiting_list)) {\n        q = ngx_queue_head(&multi_c->waiting_list);\n\n        ngx_queue_remove(q);\n        real_r = ngx_queue_data(q, ngx_http_request_t, waiting_queue);\n        real_r->waiting = 0;\n\n        ngx_queue_insert_tail(&tmp_queue, q);\n    }\n\n    while (!ngx_queue_empty(&tmp_queue)) {\n        q = ngx_queue_head(&tmp_queue);\n\n        ngx_queue_remove(q);\n\n        real_r = ngx_queue_data(q, ngx_http_request_t, waiting_queue);\n\n        real_u = real_r->upstream;\n\n        if (real_u->write_event_handler) {\n            real_u->write_event_handler(real_r, real_u);\n        }\n    }\n\n    if (fake_u->write_event_handler) {\n        fake_u->write_event_handler(fake_r, fake_u);\n    }\n\n    return NGX_OK;\n}\n\nvoid\nngx_http_multi_upstream_read_handler(ngx_connection_t *pc)\n{\n    ssize_t                  n;\n    ngx_int_t                rc;\n    ngx_http_request_t      *r, *fake_r, *real_r;\n    ngx_http_upstream_t     *u, *fake_u, *real_u;\n    ngx_connection_t        *c, *real_c;\n    ngx_multi_connection_t  *multi_c;\n    ngx_buf_t               *b;\n\n    c = pc;\n    multi_c = ngx_get_multi_connection(pc);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                   \"multi: http upstream read handler %p\", pc);\n\n    pc->log->action = \"reading from multi upstream\";\n\n    if (ngx_http_upstream_test_connect(pc) != NGX_OK) {\n        ngx_http_multi_upstream_next(pc, NGX_HTTP_UPSTREAM_FT_ERROR);\n        return;\n    }\n\n    r = pc->data;           //fake_r\n    u = r->upstream;        //fake_u\n    fake_u = r->upstream;\n\n    fake_r = r;\n\n    if (u->buffer.start == NULL) {\n        u->buffer.start = ngx_palloc(r->pool, u->conf->buffer_size);\n        if (u->buffer.start == NULL) {\n            ngx_http_upstream_finalize_request(r, u,\n                                               NGX_HTTP_INTERNAL_SERVER_ERROR);\n            return;\n        }\n\n        u->buffer.pos = u->buffer.start;\n        u->buffer.last = u->buffer.start;\n        u->buffer.end = u->buffer.start + u->conf->buffer_size;\n        u->buffer.temporary = 1;\n\n        u->buffer.tag = u->output.tag;\n\n        if (ngx_list_init(&u->headers_in.headers, r->pool, 8,\n                          sizeof(ngx_table_elt_t))\n            != NGX_OK)\n        {\n            ngx_http_multi_upstream_finalize_request(pc,\n                                                     NGX_HTTP_INTERNAL_SERVER_ERROR);\n            return;\n        }\n\n        if (ngx_list_init(&u->headers_in.trailers, r->pool, 2,\n                          sizeof(ngx_table_elt_t))\n            != NGX_OK)\n        {\n            ngx_http_multi_upstream_finalize_request(pc,\n                                                     NGX_HTTP_INTERNAL_SERVER_ERROR);\n            return;\n        }\n    }\n\n    //fake_u buffer\n    b = &fake_u->buffer;\n\n    for ( ;; ) {\n        if (b->last == b->end) {\n            ngx_log_debug4(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                           \"multi: read buffer full %p, %p, %p, %p\"\n                           , b->start, b->end, b->pos, b->last);\n        } else {\n            n = c->recv(c, b->last, b->end - b->last);\n\n            if (n == NGX_AGAIN) {\n                ngx_add_timer(pc->read, u->conf->read_timeout);\n\n                if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n                    ngx_http_multi_upstream_finalize_request(pc,\n                            NGX_HTTP_INTERNAL_SERVER_ERROR);\n                    return;\n                }\n\n                return;\n            }\n\n            if (n == 0) {\n                ngx_log_error(NGX_LOG_ERR, c->log, 0,\n                        \"upstream prematurely closed connection\");\n            }\n\n            if (n == NGX_ERROR || n == 0) {\n                ngx_http_multi_upstream_next(pc, NGX_HTTP_UPSTREAM_FT_ERROR);\n                return;\n            }\n\n            b->last += n;\n        }\n\n#if 0\n        u->valid_header_in = 0;\n\n        u->peer.cached = 0;\n#endif\n\n        for ( ; ; ) {\n            ngx_log_debug5(NGX_LOG_DEBUG_HTTP, c->log, 0\n                    , \"multi: process parse start: %d, %p, %p, %p, %p\"\n                    , b->last - b->pos, b->start, b->end, b->pos, b->last);\n            rc = u->process_header(fake_r);\n\n            ngx_log_debug5(NGX_LOG_DEBUG_HTTP, c->log, 0\n                    , \"multi: process parse end: %d, %p, %p, %p, %p\"\n                    , b->last - b->pos, b->start, b->end, b->pos, b->last);\n\n            if (rc == NGX_AGAIN) {\n                if (b->last == b->end && b->pos == b->last) {\n                    b->pos = b->start;\n                    b->last = b->start;\n                }\n\n                break;\n            }\n\n            if (rc == NGX_HTTP_UPSTREAM_INVALID_HEADER) {\n                ngx_http_multi_upstream_next(pc, NGX_HTTP_UPSTREAM_FT_INVALID_HEADER);\n                return;\n            }\n\n            if (rc == NGX_ERROR) {\n                ngx_http_multi_upstream_finalize_request(pc, NGX_HTTP_INTERNAL_SERVER_ERROR);\n                return;\n            }\n\n            /* rc == NGX_OK || rc == NGX_ERROR */\n\n            if (!multi_c->cur) {\n                ngx_log_error(NGX_LOG_ERR, c->log, 0,\n                              \"multi: upstream next because parse cur is empty\");\n                ngx_http_multi_upstream_finalize_request(pc, NGX_HTTP_INTERNAL_SERVER_ERROR);\n                return;\n            }\n\n            real_r = multi_c->cur;\n            real_u = real_r->upstream;\n            real_c = real_r->connection;\n\n            if (rc == NGX_HTTP_UPSTREAM_HEADER_END) {\n                real_u->state->header_time = ngx_current_msec - real_u->state->response_time;\n\n                if (real_u->headers_in.status_n >= NGX_HTTP_SPECIAL_RESPONSE) {\n\n                    if (ngx_http_upstream_test_next(real_r, real_u) == NGX_OK) {\n                        continue;\n                    }\n\n                    if (ngx_http_upstream_intercept_errors(real_r, real_u) == NGX_OK) {\n                        continue;\n                    }\n                }\n\n                if (ngx_http_upstream_process_headers(real_r, real_u) != NGX_OK) {\n                    continue;\n                }\n\n                ngx_http_multi_upstream_send_response(real_r, real_u);\n            } else if (rc == NGX_HTTP_UPSTREAM_GET_BODY_DATA) {\n                if (!real_u->header_sent) {\n                    ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                                  \"multi: get body immediate %p\", fake_r);\n                    //handle header first\n                    real_u->state->header_time = ngx_current_msec - real_u->state->response_time;\n                    if (real_u->headers_in.status_n >= NGX_HTTP_SPECIAL_RESPONSE) {\n                        if (ngx_http_upstream_test_next(real_r, real_u) == NGX_OK) {\n                            continue;\n                        }\n\n                        if (ngx_http_upstream_intercept_errors(real_r, real_u) == NGX_OK) {\n                            continue;\n                        }\n                    }\n\n                    if (ngx_http_upstream_process_headers(real_r, real_u) != NGX_OK) {\n                        continue;\n                    }\n\n                    ngx_http_multi_upstream_send_response(real_r, real_u);\n                }\n\n                ngx_http_multi_upstream_process_non_buffered_request(real_r);\n            } else if (rc == NGX_HTTP_UPSTREAM_PARSE_ERROR) {\n                ngx_log_error(NGX_LOG_WARN, c->log, 0,\n                              \"multi: parse get error %p\", fake_r);\n                if (!real_u->header_sent) {\n                    ngx_http_upstream_finalize_request(real_r, real_u, NGX_HTTP_BAD_GATEWAY);\n                } else {\n                    ngx_http_upstream_finalize_request(real_r, real_u, NGX_ERROR);\n                }\n            } else {\n                ngx_log_error(NGX_LOG_ERR, c->log, 0,\n                              \"multi: parse code unknown: %d\", rc);\n                if (!real_u->header_sent) {\n                    ngx_http_upstream_finalize_request(real_r, real_u, NGX_HTTP_INTERNAL_SERVER_ERROR);\n                } else {\n                    ngx_http_upstream_finalize_request(real_r, real_u, NGX_ERROR);\n                }\n            }\n\n            ngx_http_run_posted_requests(real_c);\n        }\n    }\n}\n\n//backend handler main\nvoid\nngx_http_multi_upstream_process(ngx_connection_t *pc, ngx_uint_t do_write)\n{\n    if (do_write) {\n        //write\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                \"multi: http upstream process write\");\n\n        pc->log->action = \"multi sending to upstream\";\n\n        ngx_http_multi_upstream_write_handler(pc);\n        return;\n    } else {\n        //read\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                \"multi: http upstream process read\");\n\n        pc->log->action = \"multi reading from upstream\";\n\n        ngx_http_multi_upstream_read_handler(pc);\n        return;\n    }\n}\n\nvoid\nngx_http_multi_upstream_handler(ngx_event_t *ev)\n{\n    ngx_connection_t    *pc;\n\n    pc = ev->data;\n\n    if (ev->timedout) {\n        ngx_http_multi_upstream_next(pc, NGX_HTTP_UPSTREAM_FT_TIMEOUT);\n        return;\n    }\n\n    ngx_http_multi_upstream_process(pc, ev->write);\n}\n\nvoid\nngx_http_multi_upstream_send_pool_cleanup(void *data)\n{\n    ngx_pool_t      *pool = data;\n\n    ngx_destroy_pool(pool);\n}\n\n\n//impl for start send request, run every ngx_http_request_t\nvoid\nngx_http_multi_upstream_init_request(ngx_connection_t *pc, ngx_http_request_t *r)\n{\n    ngx_http_upstream_t         *u;\n    ngx_http_request_t          *fake_r;\n    ngx_pool_cleanup_t          *cln;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"multi: http upstream init request: %p, %p\", pc, r);\n\n    u = r->upstream;\n    fake_r = pc->data;\n\n    u->write_event_handler = ngx_http_upstream_send_request_handler;\n    u->read_event_handler = ngx_http_upstream_process_header;\n\n    u->output.sendfile = pc->sendfile;\n\n    /* init or reinit the ngx_output_chain() and ngx_chain_writer() contexts */\n    u->writer.pool = fake_r->pool;\n    u->writer.out = NULL;\n    u->writer.last = &u->writer.out;\n    u->writer.connection = pc;\n    u->writer.limit = 0;\n\n    if (u->request_sent) {\n        if (ngx_http_upstream_reinit(r, u) != NGX_OK) {\n            ngx_http_upstream_finalize_request(r, u,\n                                               NGX_HTTP_INTERNAL_SERVER_ERROR);\n            return;\n        }\n    }\n\n    if (r->request_body\n        && r->request_body->buf\n        && r->request_body->temp_file\n        && r == r->main)\n    {\n        /*\n         * the r->request_body->buf can be reused for one request only,\n         * the subrequests should allocate their own temporary bufs\n         */\n\n        u->output.free = ngx_alloc_chain_link(r->pool);\n        if (u->output.free == NULL) {\n            ngx_http_upstream_finalize_request(r, u,\n                                               NGX_HTTP_INTERNAL_SERVER_ERROR);\n            return;\n        }\n\n        u->output.free->buf = r->request_body->buf;\n        u->output.free->next = NULL;\n        u->output.allocated = 1;\n\n        r->request_body->buf->pos = r->request_body->buf->start;\n        r->request_body->buf->last = r->request_body->buf->start;\n        r->request_body->buf->tag = u->output.tag;\n    }\n\n    u->request_sent = 0;\n\n    if (u->buffer.start == NULL) {\n        u->buffer.start = ngx_palloc(r->pool, u->conf->buffer_size);\n        if (u->buffer.start == NULL) {\n            ngx_http_upstream_finalize_request(r, u,\n                                               NGX_HTTP_INTERNAL_SERVER_ERROR);\n            return;\n        }\n\n        u->buffer.pos = u->buffer.start;\n        u->buffer.last = u->buffer.start;\n        u->buffer.end = u->buffer.start + u->conf->buffer_size;\n        u->buffer.temporary = 1;\n\n        u->buffer.tag = u->output.tag;\n\n        if (ngx_list_init(&u->headers_in.headers, r->pool, 8,\n                          sizeof(ngx_table_elt_t))\n            != NGX_OK)\n        {\n            ngx_http_multi_upstream_finalize_request(pc,\n                                                     NGX_HTTP_INTERNAL_SERVER_ERROR);\n            return;\n        }\n\n        if (ngx_list_init(&u->headers_in.trailers, r->pool, 2,\n                          sizeof(ngx_table_elt_t))\n            != NGX_OK)\n        {\n            ngx_http_multi_upstream_finalize_request(pc,\n                                                     NGX_HTTP_INTERNAL_SERVER_ERROR);\n            return;\n        }\n    }\n\n    if (u->send_pool == NULL) {\n        u->send_pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, r->connection->log);\n        if (u->send_pool == NULL) {\n            return;\n        }\n\n        cln = ngx_pool_cleanup_add(r->pool, 0);\n        if (cln == NULL) {\n            return;\n        }\n\n        cln->handler = ngx_http_multi_upstream_send_pool_cleanup;\n        cln->data = u->send_pool;\n\n    }\n\n    ngx_http_upstream_send_request(r, u, 1);\n}\n\n//impl for init multi connection, run every ngx_connection_t success\nvoid\nngx_http_multi_upstream_connect_init(ngx_connection_t *pc)\n{\n    ngx_multi_connection_t              *multi_c;\n    ngx_http_request_t                  *fake_r, *r;\n    ngx_http_upstream_t                 *fake_u;\n    ngx_queue_t                         *data, *q;\n    ngx_array_t                          tmp;\n    ngx_multi_data_t                    *item;\n    size_t                               i;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                   \"multi: http upstream init connection: %p\", pc);\n\n    fake_r = pc->data;\n    fake_u = fake_r->upstream;\n\n    pc->read->handler = ngx_http_multi_upstream_handler;\n    pc->write->handler = ngx_http_multi_upstream_handler;\n\n    fake_u->write_event_handler = ngx_http_upstream_send_request_handler;\n    fake_u->read_event_handler = ngx_http_upstream_process_header;\n    fake_u->output.filter_ctx = fake_r;\n    fake_u->output.sendfile = pc->sendfile;\n\n    fake_u->writer.out = NULL;\n    fake_u->writer.last = &fake_u->writer.out;\n    fake_u->writer.connection = pc;\n    fake_u->writer.limit = 0;\n\n\n    //init\n    multi_c = ngx_get_multi_connection(pc);\n    multi_c->connected = 1;\n    data = &multi_c->data;\n\n    if (NGX_OK != ngx_array_init(&tmp, pc->pool, 4, sizeof(ngx_multi_data_t))) {\n        return;\n    }\n\n    for (q = ngx_queue_head(data);\n            q != ngx_queue_sentinel(data);\n            q = ngx_queue_next(q))\n    {\n        item = ngx_array_push(&tmp);\n        if (item == NULL) {\n            return;\n        }\n        *item = *(ngx_multi_data_t*) q;\n    }\n\n    item = tmp.elts;\n    for (i=0; i < tmp.nelts; i++) {\n\n        r = item[i].data;\n\n        ngx_http_multi_upstream_init_request(pc, r);\n    }\n}\n\nvoid\nngx_http_multi_upstream_connect_handler(ngx_event_t *ev)\n{\n    ngx_connection_t                    *pc;\n\n    ngx_http_request_t                  *r;\n    ngx_http_upstream_t                 *u;\n\n    pc = ev->data;\n    r = pc->data;\n    u = r->upstream;\n\n    if (ev->timedout) {\n        ngx_log_error(NGX_LOG_ERR, pc->log, 0, \"multi: connect timeout %p\", pc);\n        ngx_http_multi_upstream_next(pc, NGX_HTTP_UPSTREAM_FT_TIMEOUT);\n        return;\n    }\n\n    ngx_del_timer(pc->write);\n\n    if (ngx_http_upstream_test_connect(pc) != NGX_OK) {\n        ngx_log_error(NGX_LOG_ERR, pc->log, 0, \"multi: connect failed %p\", pc);\n        ngx_http_multi_upstream_next(pc, NGX_HTTP_UPSTREAM_FT_ERROR);\n        return;\n    }\n\n#if (NGX_HTTP_SSL)\n\n    //do ssl handshake first if need\n    if (u->ssl && pc->ssl == NULL) {\n        ngx_http_upstream_ssl_init_connection(r, u, pc);\n        return;\n    }\n\n#endif\n\n    ngx_http_multi_upstream_connect_init(pc);\n}\n"
  },
  {
    "path": "modules/ngx_multi_upstream_module/ngx_http_multi_upstream_module.c",
    "content": "\n/*\n * Copyright (C) Mengqi Wu (Pull)\n * Copyright (C) 2017-2019 Alibaba Group Holding Limited\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n#include \"ngx_multi_upstream_module.h\"\n#include \"ngx_http_multi_upstream_module.h\"\n\ntypedef struct {\n    ngx_uint_t                               max_cached;\n\n    ngx_queue_t                              cache;\n\n    ngx_http_upstream_init_pt                original_init_upstream;\n    ngx_http_upstream_init_peer_pt           original_init_peer;\n\n} ngx_http_multi_upstream_srv_conf_t;\n\n\ntypedef struct {\n    ngx_http_multi_upstream_srv_conf_t      *conf;\n\n    ngx_queue_t                              queue;\n    void                                    *connection;\n    void                                    *request;\n\n    socklen_t                                socklen;\n    u_char                                   sockaddr[NGX_SOCKADDRLEN];\n    uint64_t                                 id;\n    unsigned int                             used;\n} ngx_http_multi_upstream_cache_t;\n\n\ntypedef struct {\n    ngx_http_multi_upstream_srv_conf_t      *conf;\n\n    ngx_http_request_t                      *request;\n    ngx_http_upstream_t                     *upstream;\n\n    void                                    *data;\n\n    ngx_event_get_peer_pt                    original_get_peer;\n    ngx_event_free_peer_pt                   original_free_peer;\n    ngx_event_notify_peer_pt                 original_notify_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_multi_upstream_peer_data_t;\n\n\nstatic ngx_int_t ngx_http_multi_upstream_init(ngx_conf_t *cf,\n    ngx_http_upstream_srv_conf_t *us);\nstatic ngx_int_t ngx_http_multi_upstream_init_peer(ngx_http_request_t *r,\n    ngx_http_upstream_srv_conf_t *us);\nstatic ngx_int_t ngx_http_multi_upstream_get_peer(ngx_peer_connection_t *pc,\n    void *data);\nstatic void ngx_http_multi_upstream_free_peer(ngx_peer_connection_t *pc,\n    void *data, ngx_uint_t state);\nstatic void ngx_http_multi_upstream_notify_peer(ngx_peer_connection_t *pc, \n    void *data, ngx_uint_t type);\n\nstatic ngx_int_t ngx_multi_upstream_get_peer_null(ngx_peer_connection_t *pc,\n    void *data);\n\nstatic ngx_int_t ngx_http_multi_upstream_add_data(ngx_connection_t *c, ngx_http_request_t *r);\n\n#if (NGX_HTTP_SSL)\nstatic ngx_int_t ngx_http_multi_upstream_set_session(\n    ngx_peer_connection_t *pc, void *data);\nstatic void ngx_http_multi_upstream_save_session(ngx_peer_connection_t *pc,\n    void *data);\n#endif\n\nstatic ngx_int_t\nngx_http_multi_upstream_init_connection(ngx_connection_t *c,\n    ngx_peer_connection_t *pc, void *data);\nstatic void\nngx_http_multi_upstream_free_fake_request(void *data);\n\nstatic void *ngx_http_multi_upstream_create_conf(ngx_conf_t *cf);\nstatic char *ngx_http_multi_upstream(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n\nstatic ngx_command_t  ngx_http_multi_upstream_commands[] = {\n    {\n        ngx_string(\"multi\"),\n        NGX_HTTP_UPS_CONF|NGX_CONF_TAKE1,\n        ngx_http_multi_upstream,\n        NGX_HTTP_SRV_CONF_OFFSET,\n        0,\n        NULL \n    },\n    \n    ngx_null_command\n};\n\nstatic ngx_http_module_t  ngx_http_multi_upstream_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    NULL,                                  /* postconfiguration */\n\n    NULL,                                  /* create main configuration */\n    NULL,                                  /* init main configuration */\n\n    ngx_http_multi_upstream_create_conf,   /* create server configuration */\n    NULL,                                  /* merge server configuration */\n\n    NULL,                                  /* create location configuration */\n    NULL                                   /* merge location configuration */\n};\n\nngx_module_t  ngx_http_multi_upstream_module = {\n    NGX_MODULE_V1,\n    &ngx_http_multi_upstream_module_ctx,    /* module context */\n    ngx_http_multi_upstream_commands,       /* 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\nstatic char *\nngx_http_multi_upstream(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_upstream_srv_conf_t            *uscf;\n    ngx_http_multi_upstream_srv_conf_t      *kcf = conf;\n\n    ngx_int_t    n;\n    ngx_str_t   *value;\n\n    if (kcf->max_cached) {\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                           \"multi: invalid value \\\"%V\\\" in \\\"%V\\\" directive\",\n                           &value[1], &cmd->name);\n        return NGX_CONF_ERROR;\n    }\n\n    kcf->max_cached = n;\n\n    uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);\n\n    kcf->original_init_upstream = uscf->peer.init_upstream\n                                  ? uscf->peer.init_upstream\n                                  : ngx_http_upstream_init_round_robin;\n\n    uscf->peer.init_upstream = ngx_http_multi_upstream_init;\n\n    return NGX_CONF_OK;\n}\n\nstatic ngx_int_t\nngx_http_multi_upstream_init(ngx_conf_t *cf,\n    ngx_http_upstream_srv_conf_t *us)\n{\n    ngx_http_multi_upstream_srv_conf_t  *kcf;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cf->log, 0,\n                   \"multi: init multi upstream\");\n\n    kcf = ngx_http_conf_upstream_srv_conf(us,\n                                          ngx_http_multi_upstream_module);\n\n    if (kcf->original_init_upstream(cf, us) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    kcf->original_init_peer = us->peer.init;\n\n    us->peer.init = ngx_http_multi_upstream_init_peer;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_multi_upstream_init_peer(ngx_http_request_t *r,\n    ngx_http_upstream_srv_conf_t *us)\n{\n    ngx_http_multi_upstream_peer_data_t  *kp;\n    ngx_http_multi_upstream_srv_conf_t   *kcf;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"multi: init multi upstream peer\");\n\n    kcf = ngx_http_conf_upstream_srv_conf(us,\n                                          ngx_http_multi_upstream_module);\n\n    kp = ngx_pcalloc(r->connection->pool, sizeof(ngx_http_multi_upstream_peer_data_t));\n    if (kp == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (kcf->original_init_peer(r, us) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    kp->conf = kcf;\n    kp->request = r;\n    kp->upstream = r->upstream;\n    kp->data = r->upstream->peer.data;\n    kp->original_get_peer  = r->upstream->peer.get;\n    kp->original_free_peer = r->upstream->peer.free;\n    kp->original_notify_peer = r->upstream->peer.notify;\n\n    r->upstream->peer.data = kp;\n    r->upstream->peer.get  = ngx_http_multi_upstream_get_peer;\n    r->upstream->peer.free = ngx_http_multi_upstream_free_peer;\n    r->upstream->peer.notify = ngx_http_multi_upstream_notify_peer;\n    r->upstream->multi = 1;\n\n#if (NGX_HTTP_SSL)\n    kp->original_set_session  = r->upstream->peer.set_session;\n    kp->original_save_session = r->upstream->peer.save_session;\n    r->upstream->peer.set_session = ngx_http_multi_upstream_set_session;\n    r->upstream->peer.save_session = ngx_http_multi_upstream_save_session;\n#endif\n\n    return NGX_OK;\n}\n\nstatic ngx_int_t\nngx_multi_upstream_get_peer_null(ngx_peer_connection_t *pc, void *data)\n{\n    return NGX_OK;\n}\n\nstatic ngx_int_t\nngx_http_multi_upstream_get_peer(ngx_peer_connection_t *pc, void *data)\n{\n    ngx_http_multi_upstream_peer_data_t     *kp = data;\n    ngx_http_multi_upstream_cache_t         *item, *best;\n    ngx_int_t                                rc;\n    ngx_uint_t                               cnt;\n    ngx_queue_t                             *q, *cache;\n    ngx_connection_t                        *c;\n    ngx_event_get_peer_pt                    save_handler;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                   \"multi: get multi upstream peer\");\n\n    /* ask balancer */\n\n    rc = kp->original_get_peer(pc, kp->data);\n\n    if (rc != NGX_OK) {\n        ngx_log_error(NGX_LOG_ERR, pc->log,\n                      0, \"multi: balancer get: %i\", rc);\n        return rc;\n    }\n\n    /* search cache for suitable connection */\n    cache = &kp->conf->cache;\n\n    best = NULL;\n    cnt  = 0;\n\n    for (q = ngx_queue_head(cache);\n         q != ngx_queue_sentinel(cache);\n         q = ngx_queue_next(q))\n    {\n        item = ngx_queue_data(q, ngx_http_multi_upstream_cache_t, queue);\n        c = item->connection;\n\n        ngx_log_error(NGX_LOG_INFO, c->log,\n                      0, \"multi: connect list, c: %p\", c);\n\n        if (ngx_memn2cmp((u_char *) &item->sockaddr, (u_char *) pc->sockaddr,\n                         item->socklen, pc->socklen)\n            == 0)\n        {\n            if (best == NULL) {\n                best = item;\n            } else {\n                if (best->used > item->used) {\n                    best = item;\n                }\n            }\n            cnt++;\n        }\n    }\n\n    if (cnt >= kp->conf->max_cached) {\n        c = best->connection;\n        best->used++;\n        goto found;\n    }\n\n    /*not find, connect new*/\n    save_handler = pc->get;\n    pc->get = ngx_multi_upstream_get_peer_null;\n    rc = ngx_event_connect_peer(pc);\n    pc->get = save_handler;\n\n    if (pc->connection == NULL) {\n        ngx_log_error(NGX_LOG_ERR, pc->log,\n                      0, \"multi: get new connection error\");\n        return NGX_ERROR;\n    }\n\n    c = pc->connection;\n    if (ngx_http_multi_upstream_init_connection(c, pc, data) != NGX_OK) {\n        ngx_log_error(NGX_LOG_ERR, pc->log,\n                      0, \"multi: init new connection failed, c: %p\", c);\n        return NGX_ERROR;\n    }\n\n    ngx_log_error(NGX_LOG_INFO, c->log,\n                  0, \"multi: get new connection, c: %p, code %d\", c, rc);\n\n    if (rc == NGX_OK) {\n        ngx_log_error(NGX_LOG_WARN, c->log,\n                      0, \"multi: get new connection NGX_OK, maybe connected immediately\");\n        return NGX_DONE;\n    } else {\n        return rc;\n    }\n\nfound:\n    ngx_log_error(NGX_LOG_INFO, pc->log, 0,\n                   \"multi: get multi peer, using connection %p\", c);\n\n    if (NGX_OK != ngx_http_multi_upstream_add_data(c, kp->request)) {\n        return NGX_ERROR;\n    }\n\n    pc->connection = c;\n    pc->cached = 1;\n\n    return NGX_DONE;\n}\n\nstatic ngx_int_t\nngx_http_multi_upstream_add_data(ngx_connection_t *c, ngx_http_request_t *r)\n{\n    ngx_multi_connection_t          *multi_c;\n    ngx_multi_data_t                *item_data;\n\n    multi_c = ngx_get_multi_connection(c);\n    \n    item_data = ngx_pcalloc(c->pool, sizeof(ngx_multi_data_t));\n    if (item_data == NULL) {\n        return NGX_ERROR;\n    }\n\n    item_data->data = r;\n    ngx_queue_insert_tail(&multi_c->data, &item_data->queue);\n    r->multi_item = &item_data->queue;\n\n    return NGX_OK;\n}\n\nstatic void\nngx_http_multi_upstream_free_fake_request(void *data)\n{\n    ngx_http_request_t  *fake_r = data;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fake_r->connection->log, 0,\n                   \"multi upstream fake request cleanup: %p\", fake_r);\n\n    fake_r->logged = 1;\n    ngx_http_free_request(fake_r, 0);\n}\n\nngx_int_t\nngx_http_multi_upstream_init_connection(ngx_connection_t *c,\n    ngx_peer_connection_t *pc, void *data)\n{\n    ngx_http_multi_upstream_peer_data_t  *kp = data;\n    ngx_http_multi_upstream_cache_t      *item;\n    ngx_multi_connection_t               *multi_c;\n    ngx_http_request_t                   *r;\n    ngx_http_request_t                   *fake_r;\n    ngx_http_log_ctx_t                   *log_ctx;\n    ngx_http_upstream_t                  *u, *fake_u;\n    ngx_pool_cleanup_t                   *cln;\n\n    c->pool = ngx_create_pool(128, kp->request->connection->log);\n    if (c->pool == NULL) {\n        return NGX_ERROR;\n    }\n\n    item = ngx_pcalloc(c->pool, sizeof(ngx_http_multi_upstream_cache_t));\n    if (item == NULL) {\n        return NGX_ERROR;\n    }\n\n    item->connection = c;\n    item->socklen    = pc->socklen;\n    item->used       = 1;\n    item->conf       = kp->conf;\n\n    ngx_memcpy(&item->sockaddr, pc->sockaddr, pc->socklen);\n\n    ngx_queue_insert_head(&kp->conf->cache, &item->queue);\n\n    //init multi connection\n    multi_c = ngx_create_multi_connection(c);\n    multi_c->connection = c;\n    c->multi_c = multi_c;\n\n    r = kp->request;\n\n    c->data = r->main->http_connection;\n    fake_r = ngx_http_create_request(c);\n    if (fake_r == NULL) {\n        return NGX_ERROR;\n    }\n\n    cln = ngx_pool_cleanup_add(c->pool, sizeof(ngx_pool_cleanup_file_t));\n    if (cln == NULL) {\n        return NGX_ERROR;\n    }\n\n    cln->handler = ngx_http_multi_upstream_free_fake_request;\n    cln->data = fake_r;\n\n    fake_r->main_conf = r->main_conf;\n    fake_r->srv_conf = r->srv_conf;\n    fake_r->loc_conf = r->loc_conf;\n    fake_r->upstream = ngx_pcalloc(c->pool, sizeof(ngx_http_upstream_t));\n    if (fake_r->upstream == NULL) {\n        return NGX_ERROR;\n    }\n\n    u = r->upstream;\n    fake_u = fake_r->upstream;\n\n    //*fake_r->upstream = *r->upstream;\n    fake_u->peer.connection = c;\n#if (NGX_HAVE_FILE_AIO || NGX_COMPAT)\n    fake_u->output.aio_handler = u->output.aio_handler;\n#endif\n\n#if (NGX_THREADS || NGX_COMPAT)\n    fake_u->output.thread_handler = u->output.thread_handler;\n#endif\n    fake_u->output.output_filter = u->output.output_filter;\n    fake_u->output.pool = fake_r->pool;\n    fake_u->writer.pool = fake_r->pool;\n    fake_u->input_filter_ctx = fake_r;\n    fake_u->conf = u->conf;\n    fake_u->upstream = u->upstream;\n    fake_u->state = ngx_pcalloc(c->pool, sizeof(ngx_http_upstream_state_t));\n    if (fake_u->state == NULL) {\n        return NGX_ERROR;\n    }\n\n    fake_u->read_event_handler = u->read_event_handler;\n    fake_u->write_event_handler = u->write_event_handler;\n    fake_u->input_filter_init = u->input_filter_init;\n    fake_u->input_filter = u->input_filter;\n    fake_u->input_filter_ctx = NULL;\n#if (NGX_HTTP_CACHE)\n    fake_u->create_key = u->create_key;\n#endif\n    fake_u->create_request = u->create_request;\n    fake_u->reinit_request = u->reinit_request;\n    fake_u->process_header = u->process_header;\n    fake_u->abort_request = u->abort_request;\n    fake_u->finalize_request = u->finalize_request;\n    fake_u->rewrite_redirect = u->rewrite_redirect;\n    fake_u->rewrite_cookie = u->rewrite_cookie;\n\n\n    fake_u->multi = 1;\n\n    fake_r->connection = c;\n\n    c->data = fake_r;\n\n    log_ctx = ngx_pcalloc(c->pool, sizeof(ngx_http_log_ctx_t));\n    if (log_ctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    log_ctx->connection = c;\n    log_ctx->request = fake_r;\n    log_ctx->current_request = fake_r;\n\n    c->log = ngx_pcalloc(c->pool, sizeof(ngx_log_t));\n    if (c->log == NULL) {\n        return NGX_ERROR;\n    }\n    *c->log = *kp->request->connection->log;\n    c->log->data = log_ctx;\n    fake_r->upstream->peer.log = c->log;\n\n    c->log->connection = c->number;\n    c->log->handler = NULL;\n    c->log->data = log_ctx;\n    c->log_error = NGX_ERROR_INFO;\n\n    c->pool->log = c->log;\n\n    c->read->log = c->log;\n    c->write->log = c->log;\n    fake_r->pool->log = c->log;\n\n    return ngx_http_multi_upstream_add_data(c, kp->request);\n}\n\nstatic void\nngx_http_multi_upstream_free_peer(ngx_peer_connection_t *pc, void *data,\n    ngx_uint_t state)\n{\n    ngx_http_multi_upstream_peer_data_t     *kp = data;\n    ngx_queue_t                             *q;\n    ngx_uint_t                               old_tries;\n    ngx_multi_connection_t                  *multi_c;\n\n    ngx_multi_request_t                     *multi_r;\n    size_t                                   len;\n    ngx_chain_t                             *cl;\n    ngx_http_request_t                      *request;\n\n    if (pc->connection == NULL) {\n        ngx_log_error(NGX_LOG_WARN, ngx_cycle->log,\n                      0, \"multi: free upstream connection null\");\n        return;\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_STREAM, pc->connection->log, 0, \"multi: free multi stream peer\");\n\n    multi_c = ngx_get_multi_connection(pc->connection);\n    request = kp->request;\n\n    if (request->backend_r) {\n        while (!ngx_queue_empty(request->backend_r)) {\n            q = ngx_queue_head(request->backend_r);\n\n            ngx_queue_remove(q);\n\n            multi_r = ngx_queue_data(q, ngx_multi_request_t, front_queue);\n\n            //clean send_list on backend connection\n            ngx_queue_remove(&multi_r->backend_queue);\n\n            len = 0;\n            for (cl = multi_r->out; cl; cl = cl->next) {\n                len += ngx_buf_size(cl->buf);\n            }\n\n            if (len == 0) {\n                //free multi_r and pool\n                ngx_destroy_pool(multi_r->pool);\n            } else {\n                //add leak list wait send finish cleanup\n                ngx_queue_insert_tail(&multi_c->leak_list, &multi_r->backend_queue);\n            }\n        }\n    }\n\n    if (request->waiting) {\n        ngx_queue_remove(&request->waiting_queue);\n        request->waiting = 0;\n    }\n\n    ngx_log_error(NGX_LOG_INFO, pc->log, 0, \"multi: free request c: %p, r: %p end\",\n                  pc->connection, request);\n\n    q = request->multi_item;\n    if (q) {\n        ngx_queue_remove(q);\n        request->multi_item = NULL;\n\n        old_tries = pc->tries;\n\n        kp->original_free_peer(pc, kp->data, state);\n\n        //work around single tries is 0\n        pc->tries = old_tries - 1;\n\n        ngx_log_error(NGX_LOG_INFO, pc->connection->log,\n                0, \"multi: free http request %p, %p\", request, pc->connection);\n    } else {\n        ngx_log_error(NGX_LOG_WARN, pc->connection->log,\n                0, \"multi: free http request not found %p, %p\", request, pc->connection);\n    }\n\n    pc->connection = NULL;\n\n    return;\n}\n\nstatic void\nngx_http_multi_upstream_notify_peer(ngx_peer_connection_t *pc, \n    void *data, ngx_uint_t type)\n{\n    ngx_http_multi_upstream_peer_data_t   *kp = data;\n\n    if (kp->original_notify_peer) {\n        kp->original_notify_peer(pc, kp->data, type);\n    }\n}\n\n#if (NGX_HTTP_SSL)\n\nstatic ngx_int_t\nngx_http_multi_upstream_set_session(ngx_peer_connection_t *pc, void *data)\n{\n    ngx_http_multi_upstream_peer_data_t  *kp = data;\n\n    return kp->original_set_session(pc, kp->data);\n}\n\nstatic void\nngx_http_multi_upstream_save_session(ngx_peer_connection_t *pc, void *data)\n{\n    ngx_http_multi_upstream_peer_data_t  *kp = data;\n\n    kp->original_save_session(pc, kp->data);\n    return;\n}\n\n#endif\n\nstatic void *\nngx_http_multi_upstream_create_conf(ngx_conf_t *cf)\n{\n    ngx_http_multi_upstream_srv_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool,\n                       sizeof(ngx_http_multi_upstream_srv_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc():\n     *\n     *     conf->original_init_upstream = NULL;\n     *     conf->original_init_peer = NULL;\n     *     conf->max_cached = 0;\n     */\n\n    ngx_queue_init(&conf->cache);\n\n    return conf;\n}\n\nngx_int_t\nngx_http_multi_upstream_connection_detach(ngx_connection_t *c)\n{\n    ngx_http_request_t                      *r;\n    ngx_http_multi_upstream_srv_conf_t      *kcf;\n    ngx_http_upstream_srv_conf_t            *uscf;\n    ngx_http_upstream_t                     *u;\n    ngx_queue_t                             *cache, *q;\n    ngx_http_multi_upstream_cache_t         *item;\n    ngx_multi_connection_t                  *multi_c;\n\n    r = c->data;\n    u = r->upstream;\n\n    uscf = u->upstream;\n\n    kcf = ngx_http_conf_upstream_srv_conf(uscf, \n            ngx_http_multi_upstream_module);\n\n    multi_c = ngx_get_multi_connection(c);\n\n    //remove pc from backend connection pool\n    cache = &kcf->cache;\n    \n    for (q = ngx_queue_head(cache);\n         q != ngx_queue_sentinel(cache);\n         q = ngx_queue_next(q))\n    {\n        item = ngx_queue_data(q, ngx_http_multi_upstream_cache_t, queue);\n\n        if (c == item->connection) {\n            //found\n            if (!ngx_queue_empty(&multi_c->data)) {\n                ngx_log_error(NGX_LOG_WARN, c->log, \n                              0, \"multi: multi connection detach not empty %p\", c);\n            }\n\n            ngx_log_error(NGX_LOG_INFO, c->log, \n                          0, \"multi: multi connection detach %p\", c);\n\n            ngx_queue_remove(&item->queue);\n\n            return NGX_OK;\n        }\n    }\n\n    ngx_log_error(NGX_LOG_WARN, c->log, \n                  0, \"multi: multi connection detach not found %p\", c);\n\n    return NGX_DONE;\n}\n\nngx_int_t\nngx_http_multi_upstream_connection_close(ngx_connection_t *c)\n{\n#if (NGX_HTTP_SSL)\n    /* TODO: do not shutdown persistent connection */\n    if (c->ssl) {\n\n        /*\n         * We send the \"close notify\" shutdown alert to the upstream only\n         * and do not wait its \"close notify\" shutdown alert.\n         * It is acceptable according to the TLS standard.\n         */\n\n        c->ssl->no_wait_shutdown = 1;\n\n        (void) ngx_ssl_shutdown(c);\n    }\n#endif\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n            \"multi: close http upstream connection: %d\", c->fd);\n\n    if (c->pool) {\n        ngx_destroy_pool(c->pool);\n    }\n\n    c->destroyed = 1;\n\n    ngx_close_connection(c);\n\n    return NGX_OK;\n}\n\nngx_flag_t\nngx_http_multi_connection_fake(ngx_http_request_t *r) \n{\n    return r->upstream->peer.connection == r->connection;\n}\n"
  },
  {
    "path": "modules/ngx_multi_upstream_module/ngx_http_multi_upstream_module.h",
    "content": "\n/*\n * Copyright (C) Mengqi Wu (Pull)\n * Copyright (C) 2017-2019 Alibaba Group Holding Limited\n */\n\n\n#ifndef _NGX_HTTP_MULTI_UPSTREAM_MODULE_H_\n#define _NGX_HTTP_MULTI_UPSTREAM_MODULE_H_\n\n#include \"ngx_multi_upstream_module.h\"\n\ntypedef ngx_int_t (*ngx_http_multi_upstream_handler_pt)(ngx_connection_t *pc, ngx_http_request_t *r);\n\nngx_int_t ngx_http_multi_upstream_connection_detach(ngx_connection_t *c);\nngx_int_t ngx_http_multi_upstream_connection_close(ngx_connection_t *c);\n\nngx_flag_t ngx_http_multi_connection_fake(ngx_http_request_t *r);\n\n#endif /* _NGX_HTTP_MULTI_UPSTREAM_MODULE_H_ */\n"
  },
  {
    "path": "modules/ngx_multi_upstream_module/ngx_multi_upstream_module.c",
    "content": "\n/*\n * Copyright (C) Mengqi Wu (Pull)\n * Copyright (C) 2017-2019 Alibaba Group Holding Limited\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n#include \"ngx_multi_upstream_module.h\"\n\nngx_multi_connection_t* \nngx_get_multi_connection(ngx_connection_t *c)\n{\n    ngx_multi_connection_t  *multi_c;\n\n    multi_c = c->multi_c;\n\n    return multi_c;\n}\n\nngx_flag_t \nngx_multi_connected(ngx_connection_t *c)\n{\n    ngx_multi_connection_t  *multi_c;\n\n    multi_c = ngx_get_multi_connection(c);\n\n    return multi_c->connected;\n}\n\nstatic void\nngx_multi_cleanup(void *data)\n{\n    ngx_multi_connection_t      *multi_c = data;\n    ngx_multi_request_t         *multi_r;\n    ngx_queue_t                 *q;\n\n    //clean multi_r on sending\n    while (!ngx_queue_empty(&multi_c->send_list)) {\n        q = ngx_queue_head(&multi_c->send_list);\n\n        ngx_queue_remove(q);\n\n        multi_r = ngx_queue_data(q, ngx_multi_request_t, backend_queue);\n\n        ngx_log_error(NGX_LOG_WARN, multi_c->connection->log, 0, \n                      \"multi: cleanup send list has multi_r unfinished %p, %p\",\n                      multi_r, multi_r->data);\n\n        //clean front list on front connection\n        ngx_queue_remove(&multi_r->front_queue);\n\n        //free multi_r and pool\n        ngx_destroy_pool(multi_r->pool);\n    }\n\n    while (!ngx_queue_empty(&multi_c->leak_list)) {\n        q = ngx_queue_head(&multi_c->leak_list);\n\n        ngx_queue_remove(q);\n\n        multi_r = ngx_queue_data(q, ngx_multi_request_t, backend_queue);\n\n        ngx_log_error(NGX_LOG_WARN, multi_c->connection->log, 0,\n                      \"multi: cleanup leak list has multi_r unfinished %p, %p\",\n                      multi_r, multi_r->data);\n\n        //free multi_r and pool\n        ngx_destroy_pool(multi_r->pool);\n    }\n}\n\nngx_multi_connection_t*\nngx_create_multi_connection(ngx_connection_t *c)\n{\n    ngx_multi_connection_t      *multi_c;\n    ngx_pool_cleanup_t          *cln;\n\n    //init multi connection\n    multi_c = ngx_pcalloc(c->pool, sizeof(ngx_multi_connection_t));\n    if (multi_c == NULL) {\n        return NULL;\n    }\n\n    ngx_queue_init(&multi_c->data);\n    ngx_queue_init(&multi_c->send_list);\n    ngx_queue_init(&multi_c->leak_list);\n    ngx_queue_init(&multi_c->waiting_list);\n    \n    multi_c->connection = c;\n\n    cln = ngx_pool_cleanup_add(c->pool, 0);\n    if (cln == NULL) {\n        return NULL;\n    }\n\n    cln->handler = ngx_multi_cleanup;\n    cln->data = multi_c;\n\n    return multi_c;\n}\n\nngx_multi_request_t*\nngx_create_multi_request(ngx_connection_t *c, void *data)\n{\n    ngx_multi_request_t     *multi_r;\n    ngx_pool_t              *pool;\n\n    pool = ngx_create_pool(4096, c->log);\n    if (pool == NULL) {\n        return NULL;\n    }\n\n    multi_r = ngx_pcalloc(pool, sizeof(ngx_multi_request_t));\n    if (multi_r == NULL) {\n        ngx_destroy_pool(pool);\n        return NULL;\n    }\n\n    multi_r->data = data;\n    multi_r->pool = pool;\n\n    return multi_r;\n}\n\nvoid\nngx_multi_clean_leak(ngx_connection_t *c)\n{\n    ngx_queue_t             *q;\n    ngx_multi_connection_t  *multi_c;\n    ngx_multi_request_t     *multi_r;\n\n    multi_c = ngx_get_multi_connection(c);\n\n    if (multi_c) {\n        while (!ngx_queue_empty(&multi_c->leak_list)) {\n            q = ngx_queue_head(&multi_c->leak_list);\n\n            ngx_queue_remove(q);\n\n            multi_r = ngx_queue_data(q, ngx_multi_request_t, backend_queue);\n\n            //free hsf_r and pool\n            ngx_destroy_pool(multi_r->pool);\n        }\n    }\n}\n\n"
  },
  {
    "path": "modules/ngx_multi_upstream_module/ngx_multi_upstream_module.h",
    "content": "\n/*\n * Copyright (C) Mengqi Wu (Pull)\n * Copyright (C) 2017-2019 Alibaba Group Holding Limited\n */\n\n\n#ifndef _NGX_MULTI_UPSTREAM_MODULE_H_\n#define _NGX_MULTI_UPSTREAM_MODULE_H_\n\n#define NGX_HTTP_UPSTREAM_HEADER_END         41\n#define NGX_HTTP_UPSTREAM_GET_BODY_DATA      42\n#define NGX_HTTP_UPSTREAM_PARSE_ERROR        43\n\n#define NGX_MULTI_UPS_SUPPORT_MULTI     0x01\n#define NGX_MULTI_UPS_NEED_MULTI        0x03\n\ntypedef ngx_int_t (*ngx_multi_upstream_handler_pt)(ngx_connection_t *pc);\ntypedef ngx_int_t (*ngx_multi_upstream_free_pt)(ngx_connection_t *pc, void *data);\n\ntypedef struct {\n    ngx_connection_t    *connection;\n\n    ngx_queue_t          data;          //front session or request list\n    ngx_queue_t          send_list;     //backend request list sending\n    ngx_queue_t          leak_list;     //backend request list sending but front close\n    ngx_queue_t          waiting_list;  //waiting backend send block\n\n    void                *data_c;\n\n    ngx_uint_t           connected:1;\n\n    void                *cur;\n} ngx_multi_connection_t;\n\ntypedef struct {\n    ngx_queue_t          queue;\n    void                *data;\n} ngx_multi_data_t;\n\ntypedef struct {\n    ngx_queue_t          backend_queue;\n    ngx_queue_t          front_queue;\n\n    void                *data;\n\n    ngx_uint_t           id;                    //id for multi\n\n    ngx_pool_t          *pool;\n\n    ngx_chain_t         *out;\n\n    void                *ctx;\n} ngx_multi_request_t;\n\ntypedef enum {\n    NGX_FRONT_OK = 0,\n    NGX_FRONT_PARSE_ERR = 1,\n    NGX_FRONT_SEND_ERR = 2,\n    NGX_BACKEND_PARSE_ERR = 3,\n    NGX_BACKEND_SEND_ERR = 4\n} ngx_multi_code_t; \n\nngx_multi_connection_t* ngx_get_multi_connection(ngx_connection_t *c);\nngx_flag_t ngx_multi_connected(ngx_connection_t *c);\n\nngx_multi_connection_t* ngx_create_multi_connection(ngx_connection_t *c);\n\nngx_multi_request_t* ngx_create_multi_request(ngx_connection_t *c, void *data);\n\nvoid ngx_multi_clean_leak(ngx_connection_t *c);\n\n#endif /* _NGX_MULTI_UPSTREAM_MODULE_H_ */\n"
  },
  {
    "path": "modules/ngx_multi_upstream_module/ngx_stream_multi_upstream_module.c",
    "content": "\n/*\n * Copyright (C) Mengqi Wu (Pull)\n * Copyright (C) 2017-2018 Alibaba Group Holding Limited\n */\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_stream.h>\n\n#include \"ngx_multi_upstream_module.h\"\n#include \"ngx_stream_multi_upstream_module.h\"\n\ntypedef struct {\n    ngx_uint_t                               max_cached;\n\n    ngx_queue_t                              cache;\n\n    ngx_stream_upstream_init_pt              original_init_upstream;\n    ngx_stream_upstream_init_peer_pt         original_init_peer;\n\n} ngx_stream_multi_upstream_srv_conf_t;\n\n\ntypedef struct {\n    ngx_stream_multi_upstream_srv_conf_t    *conf;\n\n    ngx_queue_t                              queue;\n    void                                    *connection;\n    void                                    *request;\n\n    socklen_t                                socklen;\n    u_char                                   sockaddr[NGX_SOCKADDRLEN];\n    uint64_t                                 id;\n    unsigned int                             used;\n} ngx_stream_multi_upstream_cache_t;\n\n\ntypedef struct {\n    ngx_stream_multi_upstream_srv_conf_t    *conf;\n\n    ngx_stream_session_t                    *session;\n    ngx_stream_upstream_t                   *upstream;\n\n    void                                    *data;\n\n    ngx_event_get_peer_pt                    original_get_peer;\n    ngx_event_free_peer_pt                   original_free_peer;\n    ngx_event_notify_peer_pt                 original_notify_peer;\n\n#if (NGX_STREAM_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_stream_multi_upstream_peer_data_t;\n\n\nstatic ngx_int_t ngx_stream_multi_upstream_init(ngx_conf_t *cf,\n    ngx_stream_upstream_srv_conf_t *us);\nstatic ngx_int_t ngx_stream_multi_upstream_init_peer(ngx_stream_session_t *s,\n    ngx_stream_upstream_srv_conf_t *us);\nstatic ngx_int_t ngx_stream_multi_upstream_get_peer(ngx_peer_connection_t *pc,\n    void *data);\nstatic void ngx_stream_multi_upstream_free_peer(ngx_peer_connection_t *pc,\n    void *data, ngx_uint_t state);\nstatic void ngx_stream_multi_upstream_notify_peer(ngx_peer_connection_t *pc,\n    void *data, ngx_uint_t type);\n\nstatic ngx_int_t ngx_multi_upstream_get_peer_null(ngx_peer_connection_t *pc,\n    void *data);\n\nstatic ngx_int_t ngx_stream_multi_upstream_add_data(ngx_connection_t *c, ngx_stream_session_t *s);\n\n#if (NGX_STREAM_SSL)\nstatic ngx_int_t ngx_stream_multi_upstream_set_session(\n    ngx_peer_connection_t *pc, void *data);\nstatic void ngx_stream_multi_upstream_save_session(ngx_peer_connection_t *pc,\n    void *data);\n#endif\n\nstatic ngx_int_t\nngx_stream_multi_upstream_init_connection(ngx_connection_t *c,\n    ngx_peer_connection_t *pc, void *data);\n\nstatic void *ngx_stream_multi_upstream_create_conf(ngx_conf_t *cf);\nstatic char *ngx_stream_multi_upstream(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n\nstatic ngx_command_t  ngx_stream_multi_upstream_commands[] = {\n    {\n        ngx_string(\"multi\"),\n        NGX_STREAM_UPS_CONF|NGX_CONF_TAKE1,\n        ngx_stream_multi_upstream,\n        NGX_STREAM_SRV_CONF_OFFSET,\n        0,\n        NULL\n    },\n\n    ngx_null_command\n};\n\nstatic ngx_stream_module_t  ngx_stream_multi_upstream_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    NULL,                                  /* postconfiguration */\n\n    NULL,                                  /* create main configuration */\n    NULL,                                  /* init main configuration */\n\n    ngx_stream_multi_upstream_create_conf, /* create server configuration */\n    NULL                                   /* merge server configuration */\n};\n\n\nngx_module_t  ngx_stream_multi_upstream_module = {\n    NGX_MODULE_V1,\n    &ngx_stream_multi_upstream_module_ctx, /* module context */\n    ngx_stream_multi_upstream_commands,    /* module directives */\n    NGX_STREAM_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\nstatic char *\nngx_stream_multi_upstream(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_stream_upstream_srv_conf_t          *uscf;\n    ngx_stream_multi_upstream_srv_conf_t    *kcf = conf;\n\n    ngx_int_t    n;\n    ngx_str_t   *value;\n\n    if (kcf->max_cached) {\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    kcf->max_cached = n;\n\n    uscf = ngx_stream_conf_get_module_srv_conf(cf, ngx_stream_upstream_module);\n\n    kcf->original_init_upstream = uscf->peer.init_upstream\n                                  ? uscf->peer.init_upstream\n                                  : ngx_stream_upstream_init_round_robin;\n\n    uscf->peer.init_upstream = ngx_stream_multi_upstream_init;\n\n    return NGX_CONF_OK;\n}\n\nstatic ngx_int_t\nngx_stream_multi_upstream_init(ngx_conf_t *cf,\n    ngx_stream_upstream_srv_conf_t *us)\n{\n    ngx_stream_multi_upstream_srv_conf_t  *kcf;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_STREAM, cf->log, 0,\n                   \"multi: init multi stream\");\n\n    kcf = ngx_stream_conf_upstream_srv_conf(us,\n                                          ngx_stream_multi_upstream_module);\n\n    if (kcf->original_init_upstream(cf, us) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    kcf->original_init_peer = us->peer.init;\n\n    us->peer.init = ngx_stream_multi_upstream_init_peer;\n\n    ngx_queue_init(&kcf->cache);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_multi_upstream_init_peer(ngx_stream_session_t *s,\n    ngx_stream_upstream_srv_conf_t *us)\n{\n    ngx_stream_multi_upstream_peer_data_t  *kp;\n    ngx_stream_multi_upstream_srv_conf_t   *kcf;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,\n                   \"multi: init multi stream peer\");\n\n    kcf = ngx_stream_conf_upstream_srv_conf(us,\n                                          ngx_stream_multi_upstream_module);\n\n    kp = ngx_palloc(s->connection->pool, sizeof(ngx_stream_multi_upstream_peer_data_t));\n    if (kp == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (kcf->original_init_peer(s, us) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    kp->conf = kcf;\n    kp->session = s;\n    kp->upstream = s->upstream;\n    kp->data = s->upstream->peer.data;\n    kp->original_get_peer  = s->upstream->peer.get;\n    kp->original_free_peer = s->upstream->peer.free;\n    kp->original_notify_peer = s->upstream->peer.notify;\n\n    s->upstream->peer.data = kp;\n    s->upstream->peer.get  = ngx_stream_multi_upstream_get_peer;\n    s->upstream->peer.free = ngx_stream_multi_upstream_free_peer;\n    s->upstream->peer.notify = ngx_stream_multi_upstream_notify_peer;\n    s->upstream->multi = 1;\n\n#if (NGX_STREAM_SSL)\n    kp->original_set_session  = s->upstream->peer.set_session;\n    kp->original_save_session = s->upstream->peer.save_session;\n    s->upstream->peer.set_session = ngx_stream_multi_upstream_set_session;\n    s->upstream->peer.save_session = ngx_stream_multi_upstream_save_session;\n#endif\n\n    return NGX_OK;\n}\n\nstatic ngx_int_t\nngx_multi_upstream_get_peer_null(ngx_peer_connection_t *pc, void *data)\n{\n    return NGX_OK;\n}\n\nstatic ngx_int_t\nngx_stream_multi_upstream_get_peer(ngx_peer_connection_t *pc, void *data)\n{\n    ngx_stream_multi_upstream_peer_data_t   *kp = data;\n    ngx_stream_multi_upstream_cache_t       *item, *best;\n    ngx_int_t                                rc;\n    ngx_uint_t                              cnt;\n    ngx_queue_t                             *q, *cache;\n    ngx_connection_t                        *c;\n    ngx_event_get_peer_pt                    save_handler;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_STREAM, pc->log, 0,\n                   \"multi: get multi stream peer\");\n\n    /* ask balancer */\n\n    rc = kp->original_get_peer(pc, kp->data);\n\n    if (rc != NGX_OK) {\n        ngx_log_error(NGX_LOG_ERR, kp->session->connection->log,\n                      0, \"multi: balancer get: %i\", rc);\n        return rc;\n    }\n\n    /* search cache for suitable connection */\n    cache = &kp->conf->cache;\n\n    best = NULL;\n    cnt  = 0;\n\n    for (q = ngx_queue_head(cache);\n         q != ngx_queue_sentinel(cache);\n         q = ngx_queue_next(q))\n    {\n        item = ngx_queue_data(q, ngx_stream_multi_upstream_cache_t, queue);\n        c = item->connection;\n\n        ngx_log_error(NGX_LOG_INFO, c->log,\n                      0, \"multi: connect list, c: %p\", c);\n\n        if (ngx_memn2cmp((u_char *) &item->sockaddr, (u_char *) pc->sockaddr,\n                         item->socklen, pc->socklen)\n            == 0)\n        {\n            if (best == NULL) {\n                best = item;\n\n            } else {\n\n                if (best->used > item->used) {\n                    best = item;\n                }\n            }\n            cnt++;\n        }\n    }\n\n    if (cnt >= kp->conf->max_cached) {\n        c = best->connection;\n        best->used++;\n        goto found;\n    }\n\n    /*not find, connect new*/\n    save_handler = pc->get;\n    pc->get = ngx_multi_upstream_get_peer_null;\n    rc = ngx_event_connect_peer(pc);\n    pc->get = save_handler;\n\n    if (pc->connection == NULL) {\n        ngx_log_error(NGX_LOG_ERR, kp->session->connection->log,\n                      0, \"multi: get new connection error\");\n        return NGX_ERROR;\n    }\n\n    c = pc->connection;\n    if (ngx_stream_multi_upstream_init_connection(c, pc, data) != NGX_OK) {\n        ngx_log_error(NGX_LOG_ERR, c->log,\n                      0, \"multi: init new connection failed, c: %p\", c);\n        return NGX_ERROR;\n    }\n\n    ngx_log_error(NGX_LOG_INFO, c->log,\n                  0, \"multi: get new connection, c: %p, code %d\", c, rc);\n\n    if (rc == NGX_OK) {\n        ngx_log_error(NGX_LOG_WARN, c->log,\n                      0, \"multi: get new connection NGX_OK, maybe connected immediately\");\n        return NGX_DONE;\n    } else {\n        return rc;\n    }\n\nfound:\n\n    ngx_log_error(NGX_LOG_INFO, pc->log, 0,\n                   \"multi: get multi peer, using connection %p\", c);\n\n    if (NGX_OK != ngx_stream_multi_upstream_add_data(c, kp->session)) {\n        return NGX_ERROR;\n    }\n\n    pc->connection = c;\n    pc->cached = 1;\n\n    return NGX_DONE;\n}\n\nstatic ngx_int_t\nngx_stream_multi_upstream_add_data(ngx_connection_t *c, ngx_stream_session_t *s)\n{\n    ngx_multi_connection_t          *multi_c;\n    ngx_multi_data_t                *item_data;\n\n    multi_c = ngx_get_multi_connection(c);\n\n    item_data = ngx_pcalloc(c->pool, sizeof(ngx_multi_data_t));\n    if (item_data == NULL) {\n        return NGX_ERROR;\n    }\n\n    item_data->data = s;\n    ngx_queue_insert_tail(&multi_c->data, &item_data->queue);\n    s->multi_item = &item_data->queue;\n\n    return NGX_OK;\n}\n\nngx_int_t\nngx_stream_multi_upstream_init_connection(ngx_connection_t *c,\n    ngx_peer_connection_t *pc, void *data)\n{\n    ngx_stream_multi_upstream_peer_data_t  *kp = data;\n    ngx_stream_multi_upstream_cache_t      *item;\n    ngx_multi_connection_t                 *multi_c;\n    ngx_stream_session_t                   *fake_s, *s;\n\n    c->pool = ngx_create_pool(128, kp->session->connection->log);\n    if (c->pool == NULL) {\n        return NGX_ERROR;\n    }\n\n    item = ngx_pcalloc(c->pool, sizeof(ngx_stream_multi_upstream_cache_t));\n    if (item == NULL) {\n        return NGX_ERROR;\n    }\n\n    item->connection = c;\n    item->socklen    = pc->socklen;\n    item->used       = 1;\n    item->conf       = kp->conf;\n\n    ngx_memcpy(&item->sockaddr, pc->sockaddr, pc->socklen);\n\n    ngx_queue_insert_head(&kp->conf->cache, &item->queue);\n\n    //init multi connection\n    multi_c = ngx_pcalloc(c->pool, sizeof(ngx_multi_connection_t));\n    if (multi_c == NULL) {\n        return NGX_ERROR;\n    }\n    ngx_queue_init(&multi_c->data);\n\n    fake_s = ngx_pcalloc(c->pool, sizeof(ngx_stream_session_t));\n    if (fake_s == NULL) {\n        return NGX_ERROR;\n    }\n\n    //init fake_s\n#if 0\n    *fake_s = *kp->session;\n#endif\n    s = kp->session;\n    fake_s->signature = s->signature;\n    fake_s->connection = c;  //just use backend pc fake\n    c->listening = s->connection->listening;\n    fake_s->received = s->received;\n    fake_s->start_sec = s->start_sec;\n    fake_s->start_msec = s->start_msec;\n    fake_s->main_conf = s->main_conf;\n    fake_s->srv_conf = s->srv_conf;\n    fake_s->phase_handler = s->phase_handler;\n    fake_s->status = s->status;\n\n    fake_s->ssl = 0;\n    fake_s->stat_processing = s->stat_processing;\n    fake_s->health_check = s->health_check;\n\n    fake_s->upstream = ngx_pcalloc(c->pool, sizeof(ngx_stream_upstream_t));\n    if (fake_s->upstream == NULL) {\n        return NGX_ERROR;\n    }\n#if 0\n    *fake_s->upstream = *kp->session->upstream;\n#endif\n    fake_s->upstream->peer.connection = c;\n    fake_s->upstream->peer.name = s->upstream->peer.name;\n    fake_s->upstream->free = NULL;\n    fake_s->upstream->upstream_out = NULL;\n    fake_s->upstream->upstream_busy = NULL;\n    fake_s->upstream->downstream_out = NULL;\n    fake_s->upstream->downstream_busy = NULL;\n\n    fake_s->upstream->upstream = s->upstream->upstream;\n    fake_s->upstream->resolved = NULL;\n    fake_s->upstream->state = ngx_pcalloc(c->pool, sizeof(ngx_stream_upstream_state_t));\n    if (fake_s->upstream->state == NULL) {\n        return NGX_ERROR;\n    }\n    fake_s->upstream->state->peer = fake_s->upstream->peer.name;\n    fake_s->upstream->multi = 1;\n\n    fake_s->ctx = NULL;\n    fake_s->upstream_states = NULL;\n    fake_s->variables = NULL;\n    fake_s->log_handler = NULL;\n#if (NGX_PCRE)\n    fake_s->ncaptures = s->ncaptures;\n    fake_s->captures = NULL;\n    fake_s->captures_data = NULL;\n#endif\n\n    c->data = fake_s;\n    c->multi_c = multi_c;\n\n    c->log = ngx_palloc(c->pool, sizeof(ngx_log_t));\n    if (c->log == NULL) {\n        return NGX_ERROR;\n    }\n    *c->log = *kp->session->connection->log;\n    c->log->data = fake_s;\n    fake_s->upstream->peer.log = c->log;\n    c->pool->log = c->log;\n\n    c->read->log = c->log;\n    c->write->log = c->log;\n\n    return ngx_stream_multi_upstream_add_data(c, kp->session);\n}\n\nstatic void\nngx_stream_multi_upstream_free_peer(ngx_peer_connection_t *pc, void *data,\n    ngx_uint_t state)\n{\n    ngx_stream_multi_upstream_peer_data_t   *kp = data;\n    ngx_queue_t                             *q;\n    ngx_uint_t                               old_tries;\n    ngx_multi_connection_t                  *multi_c;\n\n    ngx_multi_request_t                     *multi_r;\n    size_t                                   len;\n    ngx_chain_t                             *cl;\n    ngx_stream_session_t                    *session;\n\n\n    if (pc->connection == NULL) {\n        ngx_log_error(NGX_LOG_WARN, ngx_cycle->log,\n                      0, \"multi: free upstream connection null\");\n        return;\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_STREAM, pc->log, 0,\n                   \"multi: free multi stream peer\");\n\n    multi_c = ngx_get_multi_connection(pc->connection);\n\n    session = kp->session;\n    if (session->backend_r) {\n        while (!ngx_queue_empty(session->backend_r)) {\n            q = ngx_queue_head(session->backend_r);\n\n            ngx_queue_remove(q);\n\n            multi_r = ngx_queue_data(q, ngx_multi_request_t, front_queue);\n\n            //clean send_list on backend connection\n            ngx_queue_remove(&multi_r->backend_queue);\n\n            len = 0;\n            for (cl = multi_r->out; cl; cl = cl->next) {\n                len += ngx_buf_size(cl->buf);\n            }\n\n            if (len == 0) {\n                //free multi_r and pool\n                ngx_destroy_pool(multi_r->pool);\n            } else {\n                //add leak list wait send finish cleanup\n                ngx_queue_insert_tail(&multi_c->leak_list, &multi_r->backend_queue);\n            }\n        }\n    }\n\n    if (session->waiting) {\n        ngx_queue_remove(&session->waiting_queue);\n        session->waiting = 0;\n    }\n\n    ngx_log_error(NGX_LOG_INFO, pc->log, 0, \"multi: free request c: %p, r: %p end\",\n                  pc->connection, session);\n\n    q = session->multi_item;\n    if (q) {\n        ngx_queue_remove(q);\n        session->multi_item = NULL;\n\n        old_tries = pc->tries;\n\n        kp->original_free_peer(pc, kp->data, state);\n\n        //work around single tries is 0\n        pc->tries = old_tries - 1;\n\n        ngx_log_error(NGX_LOG_INFO, pc->connection->log,\n                0, \"multi: free stream session %p, %p\", session, pc->connection);\n    } else {\n        ngx_log_error(NGX_LOG_WARN, pc->connection->log,\n                      0, \"multi: free stream session not found %p, %p\",\n                      session, pc->connection);\n    }\n\n    return;\n}\n\nstatic void\nngx_stream_multi_upstream_notify_peer(ngx_peer_connection_t *pc,\n    void *data, ngx_uint_t type)\n{\n    ngx_stream_multi_upstream_peer_data_t   *kp = data;\n\n    if (kp->original_notify_peer) {\n        kp->original_notify_peer(pc, kp->data, type);\n    }\n}\n\n#if (NGX_STREAM_SSL)\n\nstatic ngx_int_t\nngx_stream_multi_upstream_set_session(ngx_peer_connection_t *pc, void *data)\n{\n    ngx_stream_multi_upstream_peer_data_t  *kp = data;\n\n    return kp->original_set_session(pc, kp->data);\n}\n\nstatic void\nngx_stream_multi_upstream_save_session(ngx_peer_connection_t *pc, void *data)\n{\n    ngx_stream_multi_upstream_peer_data_t  *kp = data;\n\n    kp->original_save_session(pc, kp->data);\n    return;\n}\n\n#endif\n\nstatic void *\nngx_stream_multi_upstream_create_conf(ngx_conf_t *cf)\n{\n    ngx_stream_multi_upstream_srv_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool,\n                       sizeof(ngx_stream_multi_upstream_srv_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc():\n     *\n     *     conf->original_init_upstream = NULL;\n     *     conf->original_init_peer = NULL;\n     *     conf->max_cached = 0;\n     */\n\n    return conf;\n}\n\nngx_multi_connection_t*\nngx_stream_session_get_multi_connection(ngx_stream_session_t *s)\n{\n    ngx_connection_t        *pc;\n\n    pc = s->upstream->peer.connection;\n\n    return ngx_get_multi_connection(pc);\n}\n\nngx_stream_session_t*\nngx_stream_multi_get_session(ngx_connection_t *c)\n{\n    ngx_stream_session_t        *session;\n\n    ngx_multi_connection_t      *multi_c;\n    ngx_queue_t                 *q;\n    ngx_multi_data_t            *mdata;\n\n    multi_c = ngx_get_multi_connection(c);\n\n    if (ngx_queue_empty(&multi_c->data)) {\n        return NULL;\n    } else {\n        q = ngx_queue_head(&multi_c->data);\n        mdata = ngx_queue_data(q, ngx_multi_data_t, queue);\n\n        session = mdata->data;\n\n        return session;\n    }\n}\n\nngx_int_t\nngx_stream_multi_upstream_connection_detach(ngx_connection_t *c)\n{\n    ngx_stream_session_t                    *session;\n    ngx_stream_multi_upstream_srv_conf_t    *kcf;\n    ngx_stream_upstream_srv_conf_t          *uscf;\n    ngx_stream_upstream_t                   *u;\n    ngx_queue_t                             *cache, *q;\n    ngx_stream_multi_upstream_cache_t       *item;\n    ngx_multi_connection_t                  *multi_c;\n\n    session = c->data;\n    u = session->upstream;\n\n    uscf = u->upstream;\n\n    kcf = ngx_stream_conf_upstream_srv_conf(uscf,\n            ngx_stream_multi_upstream_module);\n\n    cache = &kcf->cache;\n\n    for (q = ngx_queue_head(cache);\n         q != ngx_queue_sentinel(cache);\n         q = ngx_queue_next(q))\n    {\n        item = ngx_queue_data(q, ngx_stream_multi_upstream_cache_t, queue);\n\n        if (c == item->connection) {\n            //found\n            multi_c = ngx_get_multi_connection(c);\n            if (!ngx_queue_empty(&multi_c->data)) {\n                ngx_log_error(NGX_LOG_WARN, c->log,\n                              0, \"multi: multi connection detach not empty %p\", c);\n            }\n\n            ngx_log_error(NGX_LOG_INFO, c->log,\n                          0, \"multi: multi connection detach %p\", c);\n\n            ngx_queue_remove(&item->queue);\n\n            return NGX_OK;\n        }\n    }\n\n    ngx_log_error(NGX_LOG_WARN, c->log,\n                  0, \"multi: multi connection detach not found %p\", c);\n\n    return NGX_DONE;\n}\n\nngx_int_t\nngx_stream_multi_upstream_connection_close(ngx_connection_t *c)\n{\n    ngx_pool_t *pool = c->pool;\n\n#if (NGX_STREAM_SSL)\n    if (c->ssl) {\n        c->ssl->no_wait_shutdown = 1;\n        (void) ngx_ssl_shutdown(c);\n    }\n#endif\n\n    ngx_log_error(NGX_LOG_INFO, c->log,\n            0, \"multi: multi connection real close %p\", c);\n\n    ngx_close_connection(c);\n\n    if (pool) {\n        ngx_destroy_pool(pool);\n    }\n\n    return NGX_OK;\n}\n\nngx_flag_t\nngx_stream_multi_connection_fake(ngx_stream_session_t *s)\n{\n    return s->upstream->peer.connection == s->connection;\n}\n"
  },
  {
    "path": "modules/ngx_multi_upstream_module/ngx_stream_multi_upstream_module.h",
    "content": "\n/*\n * Copyright (C) Mengqi Wu (Pull)\n * Copyright (C) 2017-2018 Alibaba Group Holding Limited\n */\n\n#ifndef _NGX_STREAM_MULTI_UPSTREAM_H_\n#define _NGX_STREAM_MULTI_UPSTREAM_H_\n\n#include \"ngx_stream.h\"\n#include \"ngx_multi_upstream_module.h\"\n\nngx_stream_session_t* ngx_stream_multi_get_session(ngx_connection_t *c);\n\nngx_int_t ngx_stream_multi_upstream_connection_detach(ngx_connection_t *c);\nngx_int_t ngx_stream_multi_upstream_connection_close(ngx_connection_t *c);\n\nngx_flag_t ngx_stream_multi_connection_fake(ngx_stream_session_t *s);\n\n#endif\n"
  },
  {
    "path": "modules/ngx_slab_stat/README.cn",
    "content": "ngx_slab_stat\n==============\n\n该模块可以提供NGINX/Tengine共享内存的状态信息。\n\n示例\n=======\n\n获取NGINX/Tengine共享内存池的slab和空闲页状态信息\n---------------------------------\n\n```\n http {\n    server {\n        listen 80;\n\n        location = /slab_stat {\n            slab_stat;\n        }\n    }\n }\n```\n\n请求URI /slab_stat，可以获取到该NGINX/Tengine实例的共享内存使用情况统计。\n页面输出如下：\n\n```\n$ curl http://localhost:80/slab_stat\n* shared memory: one\ntotal:      102400(KB) free:      101792(KB) size:           4(KB)\npages:      101792(KB) start:0000000003496000 end:0000000009800000\nslot:           8(Bytes) total:           0 used:           0 reqs:           0 fails:           0\nslot:          16(Bytes) total:           0 used:           0 reqs:           0 fails:           0\nslot:          32(Bytes) total:         127 used:           1 reqs:           1 fails:           0\nslot:          64(Bytes) total:           0 used:           0 reqs:           0 fails:           0\nslot:         128(Bytes) total:          32 used:           1 reqs:           1 fails:           0\nslot:         256(Bytes) total:           0 used:           0 reqs:           0 fails:           0\nslot:         512(Bytes) total:           0 used:           0 reqs:           0 fails:           0\nslot:        1024(Bytes) total:           0 used:           0 reqs:           0 fails:           0\nslot:        2048(Bytes) total:           0 used:           0 reqs:           0 fails:           0\n```\n\n共享内存使用情况统计\n-----------------------------------\n\n数据说明\n====\n\n每个数据段落的前三行表明共享内存的总使用信息及空闲页(free pages)信息，剩余行表明slab的使用信息，数据项意义如下：\n\n* __shared memory__: 共享内存池的名称信息\n\n* __total__: 该共享内存池总内存占用\n* __free__: 该共享内存池空闲内存\n* __size__: 该共享内存池的页大小\n\n* __pages__: 可供分配的连续页\n* __start__: 可供分配的连续页起始地址\n* __end__: 可供分配的连续页末尾地址\n\n* __slot__: 可供分配的slot(不同大小代表不同的slot队列)\n* __total__: 总的slot个数\n* __used__: 已使用的slot个数\n* __reqs__: 申请分配次数\n* __fails__: 申请失败次数\n\nNGINX兼容性\n===================\n\n* 1.13.4 (stable version of 1.13.x)\n\nNGINX版本低于 1.13.x 需要补丁支持（参考安装说明）\n\nTengine兼容性\n=====================\n\n* 2.1.1 (stable version of 2.1.x)\n\nTengine版本低于 2.1.x 需要补丁支持（参考安装说明）\n\n安装说明\n=======\n\n源码安装，执行如下命令：\n\n```\n$ wget http://nginx.org/download/nginx-1.13.4.tar.gz\n$ tar -xzvf nginx-1.13.4.tar.gz\n$ cd nginx-1.13.4/\n$ ./configure --add-module=/path/to/ngx_slab_stat\n$ make -j4 && make install\n```\n\n注意：若NGINX版本低于 1.13.x 或者 Tengine版本低于 2.1.1，请安装补丁文件`slab_stat.patch`后再编译二进制版本，该补丁文件可以自行比对NGINX 1.13.4 版本生成。\n\n```\n$ patch -p1 < /path/to/ngx_slab_stat/slab_stat.patch\n```\n\n配置指令\n=========\n\n语法: **slab_stat**\n\n默认: `none`\n\n位置: `server, location`\n\nNGINX/Tenigne实例的共享内存状态信息可以通过该location访问得到。\n\n注意信息\n=========\n\n仅支持NGINX官方指令分配的共享内存和[lua-nginx-module](https://github.com/openresty/lua-nginx-module)分配的共享内存。\n"
  },
  {
    "path": "modules/ngx_slab_stat/README.md",
    "content": "ngx_slab_stat\n==============\n\nThis module provides access to information of slab usage for nginx/tengine shared memory.\n\nExample\n=======\n\nGet information of slab and free page usage.\n---------------------------------\n\n```\n http {\n    server {\n        listen 80;\n\n        location = /slab_stat {\n            slab_stat;\n        }\n    }\n }\n```\n\nRequesting URI /slab_stat, you will get information of slab and free page usage for nginx/tengine shared memory.\nThe output page may look like as follows:\n\n```\n$ curl http://localhost:80/slab_stat\n* shared memory: one\ntotal:      102400(KB) free:      101792(KB) size:           4(KB)\npages:      101792(KB) start:0000000003496000 end:0000000009800000\nslot:           8(Bytes) total:           0 used:           0 reqs:           0 fails:           0\nslot:          16(Bytes) total:           0 used:           0 reqs:           0 fails:           0\nslot:          32(Bytes) total:         127 used:           1 reqs:           1 fails:           0\nslot:          64(Bytes) total:           0 used:           0 reqs:           0 fails:           0\nslot:         128(Bytes) total:          32 used:           1 reqs:           1 fails:           0\nslot:         256(Bytes) total:           0 used:           0 reqs:           0 fails:           0\nslot:         512(Bytes) total:           0 used:           0 reqs:           0 fails:           0\nslot:        1024(Bytes) total:           0 used:           0 reqs:           0 fails:           0\nslot:        2048(Bytes) total:           0 used:           0 reqs:           0 fails:           0\n```\n\nGet information of shared memory usage\n-----------------------------------\n\nData\n====\n\nEvery line except the first three of output content has the same format, as follows:\n\n* __shared memory__: name of current shared memory zone\n\n* __total__: total size of current shared memory zone\n* __free__: free size of current shared memory zone now\n* __size__: page size of current shared memory zone\n\n* __pages__: continuous page size that can be allocated\n* __start__: start address of current continuous page size\n* __end__: end address of current continuous page size\n\n* __slot__: slot that can be allocated\n* __total__: total number of current slot\n* __used__: used number of current slot\n* __reqs__: reqs number of current slot\n* __fails__: fails number of current slot\n\nNginx Compatibility\n===================\n\nThe latest module is compatible with the following versions of nginx:\n\n* 1.13.4 (stable version of 1.13.x)\n\nNginx cores older than 1.13.x should be patched (refer \"Install\").\n\nTengine Compatibility\n=====================\n\n* 2.1.1 (stable version of 2.1.x)\n\nTengine version older than 2.1.x should be patched (refer \"Install\").\n\nInstall\n=======\n\nInstall this module from source:\n\n```\n$ wget http://nginx.org/download/nginx-1.13.4.tar.gz\n$ tar -xzvf nginx-1.13.4.tar.gz\n$ cd nginx-1.13.4/\n$ ./configure --add-module=/path/to/ngx_slab_stat\n$ make -j4 && make install\n```\n\nNote that `slab_stat.patch` should be applied when nginx cores older than 1.13.x, you can also generate this patch by diff with nginx 1.13.x.\n\n```\n$ patch -p1 < /path/to/ngx_slab_stat/slab_stat.patch\n```\n\nDirective\n=========\n\nSyntax: **slab_stat**\n\nDefault: `none`\n\nContext: `server, location`\n\nThe information of nginx shared memory usage will be accessible from the surrounding location.\n\nException\n=========\n\nNow only support shared memory allocated from nginx and [lua-nginx-module](https://github.com/openresty/lua-nginx-module)\n"
  },
  {
    "path": "modules/ngx_slab_stat/config",
    "content": "ngx_addon_name=ngx_http_slab_stat_module\nHTTP_MODULES=\"$HTTP_MODULES ngx_http_slab_stat_module\"\nNGX_ADDON_SRCS=\"$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_slab_stat_module.c\"\n\nhave=NGX_SLAB_STAT . auto/have\n"
  },
  {
    "path": "modules/ngx_slab_stat/ngx_http_slab_stat_module.c",
    "content": "\n/*\n * Copyright (C) 2017 Alibaba Group Holding Limited\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n#include <ngx_slab.h>\n\n\nstatic char *ngx_http_slab_stat(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\nstatic ngx_int_t ngx_http_slab_stat_buf(ngx_pool_t *pool, ngx_buf_t *b);\n\nstatic ngx_command_t  ngx_http_slab_stat_commands[] = {\n\n    { ngx_string(\"slab_stat\"),\n      NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,\n      ngx_http_slab_stat,\n      0,\n      0,\n      NULL },\n\n    ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_slab_stat_module_ctx = {\n    NULL,                          /* preconfiguration */\n    NULL,                          /* 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\nngx_module_t  ngx_http_slab_stat_module = {\n    NGX_MODULE_V1,\n    &ngx_http_slab_stat_module_ctx,     /* module context */\n    ngx_http_slab_stat_commands,        /* 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_slab_stat_handler(ngx_http_request_t *r)\n{\n    ngx_int_t    rc;\n    ngx_buf_t   *b;\n    ngx_chain_t  out;\n\n    if (r->method != NGX_HTTP_GET) {\n        return NGX_HTTP_NOT_ALLOWED;\n    }\n\n    rc = ngx_http_discard_request_body(r);\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));\n    if (b == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    if (ngx_http_slab_stat_buf(r->pool, b) == NGX_ERROR) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    r->headers_out.status = NGX_HTTP_OK;\n    r->headers_out.content_length_n = b->last - b->pos;\n\n    rc = ngx_http_send_header(r);\n\n    if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {\n        return rc;\n    }\n\n    out.buf = b;\n    out.next = NULL;\n\n    return ngx_http_output_filter(r, &out);\n}\n\n\nstatic ngx_int_t\nngx_http_slab_stat_buf(ngx_pool_t *pool, ngx_buf_t *b)\n{\n    u_char                       *p;\n    size_t                        pz, size;\n    ngx_uint_t                    i, k, n;\n    ngx_shm_zone_t               *shm_zone;\n    ngx_slab_pool_t              *shpool;\n    ngx_slab_page_t              *page;\n    ngx_slab_stat_t              *stats;\n    volatile ngx_list_part_t     *part;\n\n#define NGX_SLAB_SHM_SIZE               (sizeof(\"* shared memory: \\n\") - 1)\n#define NGX_SLAB_SHM_FORMAT             \"* shared memory: %V\\n\"\n#define NGX_SLAB_SUMMARY_SIZE           \\\n    (3 * 12 + sizeof(\"total:(KB) free:(KB) size:(KB)\\n\") - 1)\n#define NGX_SLAB_SUMMARY_FORMAT         \\\n    \"total:%12z(KB) free:%12z(KB) size:%12z(KB)\\n\"\n#define NGX_SLAB_PAGE_ENTRY_SIZE        \\\n    (12 + 2 * 16 + sizeof(\"pages:(KB) start: end:\\n\") - 1)\n#define NGX_SLAB_PAGE_ENTRY_FORMAT      \\\n    \"pages:%12z(KB) start:%p end:%p\\n\"\n#define NGX_SLAB_SLOT_ENTRY_SIZE        \\\n    (12 * 5 + sizeof(\"slot:(Bytes) total: used: reqs: fails:\\n\") - 1)\n#define NGX_SLAB_SLOT_ENTRY_FORMAT      \\\n    \"slot:%12z(Bytes) total:%12z used:%12z reqs:%12z fails:%12z\\n\"\n\n    pz = 0;\n\n    /* query shared memory */\n\n    part = &ngx_cycle->shared_memory.part;\n    shm_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            part = part->next;\n            shm_zone = part->elts;\n            i = 0;\n        }\n\n        pz += NGX_SLAB_SHM_SIZE + (size_t)shm_zone[i].shm.name.len;\n        pz += NGX_SLAB_SUMMARY_SIZE;\n\n        shpool = (ngx_slab_pool_t *) shm_zone[i].shm.addr;\n\n        ngx_shmtx_lock(&shpool->mutex);\n\n        for (page = shpool->free.next; page != &shpool->free; page = page->next) {\n            pz += NGX_SLAB_PAGE_ENTRY_SIZE;\n        }\n\n        n = ngx_pagesize_shift - shpool->min_shift;\n\n        ngx_shmtx_unlock(&shpool->mutex);\n\n        for (k = 0; k < n; k++) {\n            pz += NGX_SLAB_SLOT_ENTRY_SIZE;\n        }\n\n    }\n\n    /* preallocate pz * 2 to make sure memory enough */\n    p = ngx_palloc(pool, pz * 2);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    b->pos = p;\n\n    size = 1 << ngx_pagesize_shift;\n\n    part = &ngx_cycle->shared_memory.part;\n    shm_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            part = part->next;\n            shm_zone = part->elts;\n            i = 0;\n        }\n\n        shpool = (ngx_slab_pool_t *) shm_zone[i].shm.addr;\n\n        p = ngx_snprintf(p, NGX_SLAB_SHM_SIZE + shm_zone[i].shm.name.len,\n            NGX_SLAB_SHM_FORMAT, &shm_zone[i].shm.name);\n\n        ngx_shmtx_lock(&shpool->mutex);\n\n        p = ngx_snprintf(p, NGX_SLAB_SUMMARY_SIZE, NGX_SLAB_SUMMARY_FORMAT,\n            shm_zone[i].shm.size / 1024, shpool->pfree * size / 1024,\n            size / 1024, shpool->pfree);\n\n        for (page = shpool->free.next; page != &shpool->free; page = page->next) {\n            p = ngx_snprintf(p, NGX_SLAB_PAGE_ENTRY_SIZE,\n                NGX_SLAB_PAGE_ENTRY_FORMAT, page->slab * size / 1024,\n                shpool->start, shpool->end);\n        }\n\n        stats = shpool->stats;\n\n        n = ngx_pagesize_shift - shpool->min_shift;\n\n        for (k = 0; k < n; k++) {\n            p = ngx_snprintf(p, NGX_SLAB_SLOT_ENTRY_SIZE, NGX_SLAB_SLOT_ENTRY_FORMAT,\n                1 << (k + shpool->min_shift),\n                stats[k].total, stats[k].used, stats[k].reqs, stats[k].fails);\n        }\n\n        ngx_shmtx_unlock(&shpool->mutex);\n    }\n\n    b->last = p;\n    b->memory = 1;\n    b->last_buf = 1;\n\n    return NGX_OK;\n}\n\n\nstatic char *\nngx_http_slab_stat(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_core_loc_conf_t *clcf;\n\n    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);\n    clcf->handler = ngx_http_slab_stat_handler;\n\n    return NGX_CONF_OK;\n}\n"
  },
  {
    "path": "modules/ngx_slab_stat/slab_stat.patch",
    "content": "diff --git a/src/core/ngx_slab.c b/src/core/ngx_slab.c\nindex c1125062..6695bb32 100644\n--- a/src/core/ngx_slab.c\n+++ b/src/core/ngx_slab.c\n@@ -41,6 +41,19 @@\n #endif\n \n \n+#define ngx_slab_slots(pool)                                                  \\\n+    (ngx_slab_page_t *) ((u_char *) (pool) + sizeof(ngx_slab_pool_t))\n+\n+#define ngx_slab_page_type(page)   ((page)->prev & NGX_SLAB_PAGE_MASK)\n+\n+#define ngx_slab_page_prev(page)                                              \\\n+    (ngx_slab_page_t *) ((page)->prev & ~NGX_SLAB_PAGE_MASK)\n+\n+#define ngx_slab_page_addr(pool, page)                                        \\\n+    ((((page) - (pool)->pages) << ngx_pagesize_shift)                         \\\n+     + (uintptr_t) (pool)->start)\n+\n+\n #if (NGX_DEBUG_MALLOC)\n \n #define ngx_slab_junk(p, size)     ngx_memset(p, 0xA5, size)\n@@ -76,7 +89,7 @@ ngx_slab_init(ngx_slab_pool_t *pool)\n     size_t            size;\n     ngx_int_t         m;\n     ngx_uint_t        i, n, pages;\n-    ngx_slab_page_t  *slots;\n+    ngx_slab_page_t  *slots, *page;\n \n     /* STUB */\n     if (ngx_slab_max_size == 0) {\n@@ -90,15 +103,17 @@ ngx_slab_init(ngx_slab_pool_t *pool)\n \n     pool->min_size = 1 << pool->min_shift;\n \n-    p = (u_char *) pool + sizeof(ngx_slab_pool_t);\n+    slots = ngx_slab_slots(pool);\n+\n+    p = (u_char *) slots;\n     size = pool->end - p;\n \n     ngx_slab_junk(p, size);\n \n-    slots = (ngx_slab_page_t *) p;\n     n = ngx_pagesize_shift - pool->min_shift;\n \n     for (i = 0; i < n; i++) {\n+        /* only \"next\" is used in list head */\n         slots[i].slab = 0;\n         slots[i].next = &slots[i];\n         slots[i].prev = 0;\n@@ -106,30 +121,40 @@ ngx_slab_init(ngx_slab_pool_t *pool)\n \n     p += n * sizeof(ngx_slab_page_t);\n \n-    pages = (ngx_uint_t) (size / (ngx_pagesize + sizeof(ngx_slab_page_t)));\n+    pool->stats = (ngx_slab_stat_t *) p;\n+    ngx_memzero(pool->stats, n * sizeof(ngx_slab_stat_t));\n+\n+    p += n * sizeof(ngx_slab_stat_t);\n+\n+    size -= n * (sizeof(ngx_slab_page_t) + sizeof(ngx_slab_stat_t));\n \n-    ngx_memzero(p, pages * sizeof(ngx_slab_page_t));\n+    pages = (ngx_uint_t) (size / (ngx_pagesize + sizeof(ngx_slab_page_t)));\n \n     pool->pages = (ngx_slab_page_t *) p;\n+    ngx_memzero(pool->pages, pages * sizeof(ngx_slab_page_t));\n+\n+    page = pool->pages;\n \n+    /* only \"next\" is used in list head */\n+    pool->free.slab = 0;\n+    pool->free.next = page;\n     pool->free.prev = 0;\n-    pool->free.next = (ngx_slab_page_t *) p;\n \n-    pool->pages->slab = pages;\n-    pool->pages->next = &pool->free;\n-    pool->pages->prev = (uintptr_t) &pool->free;\n+    page->slab = pages;\n+    page->next = &pool->free;\n+    page->prev = (uintptr_t) &pool->free;\n \n-    pool->start = (u_char *)\n-                  ngx_align_ptr((uintptr_t) p + pages * sizeof(ngx_slab_page_t),\n-                                 ngx_pagesize);\n+    pool->start = ngx_align_ptr(p + pages * sizeof(ngx_slab_page_t),\n+                                ngx_pagesize);\n \n     m = pages - (pool->end - pool->start) / ngx_pagesize;\n     if (m > 0) {\n         pages -= m;\n-        pool->pages->slab = pages;\n+        page->slab = pages;\n     }\n \n     pool->last = pool->pages + pages;\n+    pool->pfree = pages;\n \n     pool->log_nomem = 1;\n     pool->log_ctx = &pool->zero;\n@@ -168,8 +193,7 @@ ngx_slab_alloc_locked(ngx_slab_pool_t *pool, size_t size)\n         page = ngx_slab_alloc_pages(pool, (size >> ngx_pagesize_shift)\n                                           + ((size % ngx_pagesize) ? 1 : 0));\n         if (page) {\n-            p = (page - pool->pages) << ngx_pagesize_shift;\n-            p += (uintptr_t) pool->start;\n+            p = ngx_slab_page_addr(pool, page);\n \n         } else {\n             p = 0;\n@@ -184,166 +208,139 @@ ngx_slab_alloc_locked(ngx_slab_pool_t *pool, size_t size)\n         slot = shift - pool->min_shift;\n \n     } else {\n-        size = pool->min_size;\n         shift = pool->min_shift;\n         slot = 0;\n     }\n \n+    pool->stats[slot].reqs++;\n+\n     ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, ngx_cycle->log, 0,\n                    \"slab alloc: %uz slot: %ui\", size, slot);\n \n-    slots = (ngx_slab_page_t *) ((u_char *) pool + sizeof(ngx_slab_pool_t));\n+    slots = ngx_slab_slots(pool);\n     page = slots[slot].next;\n \n     if (page->next != page) {\n \n         if (shift < ngx_slab_exact_shift) {\n \n-            do {\n-                p = (page - pool->pages) << ngx_pagesize_shift;\n-                bitmap = (uintptr_t *) (pool->start + p);\n-\n-                map = (1 << (ngx_pagesize_shift - shift))\n-                          / (sizeof(uintptr_t) * 8);\n-\n-                for (n = 0; n < map; n++) {\n-\n-                    if (bitmap[n] != NGX_SLAB_BUSY) {\n-\n-                        for (m = 1, i = 0; m; m <<= 1, i++) {\n-                            if ((bitmap[n] & m)) {\n-                                continue;\n-                            }\n+            bitmap = (uintptr_t *) ngx_slab_page_addr(pool, page);\n \n-                            bitmap[n] |= m;\n+            map = (ngx_pagesize >> shift) / (sizeof(uintptr_t) * 8);\n \n-                            i = ((n * sizeof(uintptr_t) * 8) << shift)\n-                                + (i << shift);\n+            for (n = 0; n < map; n++) {\n \n-                            if (bitmap[n] == NGX_SLAB_BUSY) {\n-                                for (n = n + 1; n < map; n++) {\n-                                     if (bitmap[n] != NGX_SLAB_BUSY) {\n-                                         p = (uintptr_t) bitmap + i;\n+                if (bitmap[n] != NGX_SLAB_BUSY) {\n \n-                                         goto done;\n-                                     }\n-                                }\n-\n-                                prev = (ngx_slab_page_t *)\n-                                            (page->prev & ~NGX_SLAB_PAGE_MASK);\n-                                prev->next = page->next;\n-                                page->next->prev = page->prev;\n-\n-                                page->next = NULL;\n-                                page->prev = NGX_SLAB_SMALL;\n-                            }\n-\n-                            p = (uintptr_t) bitmap + i;\n-\n-                            goto done;\n+                    for (m = 1, i = 0; m; m <<= 1, i++) {\n+                        if (bitmap[n] & m) {\n+                            continue;\n                         }\n-                    }\n-                }\n \n-                page = page->next;\n+                        bitmap[n] |= m;\n \n-            } while (page);\n+                        i = (n * sizeof(uintptr_t) * 8 + i) << shift;\n \n-        } else if (shift == ngx_slab_exact_shift) {\n-\n-            do {\n-                if (page->slab != NGX_SLAB_BUSY) {\n+                        p = (uintptr_t) bitmap + i;\n \n-                    for (m = 1, i = 0; m; m <<= 1, i++) {\n-                        if ((page->slab & m)) {\n-                            continue;\n-                        }\n+                        pool->stats[slot].used++;\n \n-                        page->slab |= m;\n+                        if (bitmap[n] == NGX_SLAB_BUSY) {\n+                            for (n = n + 1; n < map; n++) {\n+                                if (bitmap[n] != NGX_SLAB_BUSY) {\n+                                    goto done;\n+                                }\n+                            }\n \n-                        if (page->slab == NGX_SLAB_BUSY) {\n-                            prev = (ngx_slab_page_t *)\n-                                            (page->prev & ~NGX_SLAB_PAGE_MASK);\n+                            prev = ngx_slab_page_prev(page);\n                             prev->next = page->next;\n                             page->next->prev = page->prev;\n \n                             page->next = NULL;\n-                            page->prev = NGX_SLAB_EXACT;\n+                            page->prev = NGX_SLAB_SMALL;\n                         }\n \n-                        p = (page - pool->pages) << ngx_pagesize_shift;\n-                        p += i << shift;\n-                        p += (uintptr_t) pool->start;\n-\n                         goto done;\n                     }\n                 }\n+            }\n \n-                page = page->next;\n+        } else if (shift == ngx_slab_exact_shift) {\n \n-            } while (page);\n+            for (m = 1, i = 0; m; m <<= 1, i++) {\n+                if (page->slab & m) {\n+                    continue;\n+                }\n \n-        } else { /* shift > ngx_slab_exact_shift */\n+                page->slab |= m;\n \n-            n = ngx_pagesize_shift - (page->slab & NGX_SLAB_SHIFT_MASK);\n-            n = 1 << n;\n-            n = ((uintptr_t) 1 << n) - 1;\n-            mask = n << NGX_SLAB_MAP_SHIFT;\n+                if (page->slab == NGX_SLAB_BUSY) {\n+                    prev = ngx_slab_page_prev(page);\n+                    prev->next = page->next;\n+                    page->next->prev = page->prev;\n \n-            do {\n-                if ((page->slab & NGX_SLAB_MAP_MASK) != mask) {\n+                    page->next = NULL;\n+                    page->prev = NGX_SLAB_EXACT;\n+                }\n \n-                    for (m = (uintptr_t) 1 << NGX_SLAB_MAP_SHIFT, i = 0;\n-                         m & mask;\n-                         m <<= 1, i++)\n-                    {\n-                        if ((page->slab & m)) {\n-                            continue;\n-                        }\n+                p = ngx_slab_page_addr(pool, page) + (i << shift);\n \n-                        page->slab |= m;\n+                pool->stats[slot].used++;\n \n-                        if ((page->slab & NGX_SLAB_MAP_MASK) == mask) {\n-                            prev = (ngx_slab_page_t *)\n-                                            (page->prev & ~NGX_SLAB_PAGE_MASK);\n-                            prev->next = page->next;\n-                            page->next->prev = page->prev;\n+                goto done;\n+            }\n \n-                            page->next = NULL;\n-                            page->prev = NGX_SLAB_BIG;\n-                        }\n+        } else { /* shift > ngx_slab_exact_shift */\n \n-                        p = (page - pool->pages) << ngx_pagesize_shift;\n-                        p += i << shift;\n-                        p += (uintptr_t) pool->start;\n+            mask = ((uintptr_t) 1 << (ngx_pagesize >> shift)) - 1;\n+            mask <<= NGX_SLAB_MAP_SHIFT;\n \n-                        goto done;\n-                    }\n+            for (m = (uintptr_t) 1 << NGX_SLAB_MAP_SHIFT, i = 0;\n+                 m & mask;\n+                 m <<= 1, i++)\n+            {\n+                if (page->slab & m) {\n+                    continue;\n+                }\n+\n+                page->slab |= m;\n+\n+                if ((page->slab & NGX_SLAB_MAP_MASK) == mask) {\n+                    prev = ngx_slab_page_prev(page);\n+                    prev->next = page->next;\n+                    page->next->prev = page->prev;\n+\n+                    page->next = NULL;\n+                    page->prev = NGX_SLAB_BIG;\n                 }\n \n-                page = page->next;\n+                p = ngx_slab_page_addr(pool, page) + (i << shift);\n \n-            } while (page);\n+                pool->stats[slot].used++;\n+\n+                goto done;\n+            }\n         }\n+\n+        ngx_slab_error(pool, NGX_LOG_ALERT, \"ngx_slab_alloc(): page is busy\");\n+        ngx_debug_point();\n     }\n \n     page = ngx_slab_alloc_pages(pool, 1);\n \n     if (page) {\n         if (shift < ngx_slab_exact_shift) {\n-            p = (page - pool->pages) << ngx_pagesize_shift;\n-            bitmap = (uintptr_t *) (pool->start + p);\n+            bitmap = (uintptr_t *) ngx_slab_page_addr(pool, page);\n \n-            s = 1 << shift;\n-            n = (1 << (ngx_pagesize_shift - shift)) / 8 / s;\n+            n = (ngx_pagesize >> shift) / ((1 << shift) * 8);\n \n             if (n == 0) {\n                 n = 1;\n             }\n \n-            bitmap[0] = (2 << n) - 1;\n+            bitmap[0] = ((uintptr_t) 2 << n) - 1;\n \n-            map = (1 << (ngx_pagesize_shift - shift)) / (sizeof(uintptr_t) * 8);\n+            map = (ngx_pagesize >> shift) / (sizeof(uintptr_t) * 8);\n \n             for (i = 1; i < map; i++) {\n                 bitmap[i] = 0;\n@@ -355,8 +352,11 @@ ngx_slab_alloc_locked(ngx_slab_pool_t *pool, size_t size)\n \n             slots[slot].next = page;\n \n-            p = ((page - pool->pages) << ngx_pagesize_shift) + s * n;\n-            p += (uintptr_t) pool->start;\n+            pool->stats[slot].total += (ngx_pagesize >> shift) - n;\n+\n+            p = ngx_slab_page_addr(pool, page) + (n << shift);\n+\n+            pool->stats[slot].used++;\n \n             goto done;\n \n@@ -368,8 +368,11 @@ ngx_slab_alloc_locked(ngx_slab_pool_t *pool, size_t size)\n \n             slots[slot].next = page;\n \n-            p = (page - pool->pages) << ngx_pagesize_shift;\n-            p += (uintptr_t) pool->start;\n+            pool->stats[slot].total += sizeof(uintptr_t) * 8;\n+\n+            p = ngx_slab_page_addr(pool, page);\n+\n+            pool->stats[slot].used++;\n \n             goto done;\n \n@@ -381,8 +384,11 @@ ngx_slab_alloc_locked(ngx_slab_pool_t *pool, size_t size)\n \n             slots[slot].next = page;\n \n-            p = (page - pool->pages) << ngx_pagesize_shift;\n-            p += (uintptr_t) pool->start;\n+            pool->stats[slot].total += ngx_pagesize >> shift;\n+\n+            p = ngx_slab_page_addr(pool, page);\n+\n+            pool->stats[slot].used++;\n \n             goto done;\n         }\n@@ -390,9 +396,12 @@ ngx_slab_alloc_locked(ngx_slab_pool_t *pool, size_t size)\n \n     p = 0;\n \n+    pool->stats[slot].fails++;\n+\n done:\n \n-    ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, ngx_cycle->log, 0, \"slab alloc: %p\", p);\n+    ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, ngx_cycle->log, 0,\n+                   \"slab alloc: %p\", (void *) p);\n \n     return (void *) p;\n }\n@@ -443,7 +452,7 @@ ngx_slab_free_locked(ngx_slab_pool_t *pool, void *p)\n {\n     size_t            size;\n     uintptr_t         slab, m, *bitmap;\n-    ngx_uint_t        n, type, slot, shift, map;\n+    ngx_uint_t        i, n, type, slot, shift, map;\n     ngx_slab_page_t  *slots, *page;\n \n     ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, ngx_cycle->log, 0, \"slab free: %p\", p);\n@@ -456,7 +465,7 @@ ngx_slab_free_locked(ngx_slab_pool_t *pool, void *p)\n     n = ((u_char *) p - pool->start) >> ngx_pagesize_shift;\n     page = &pool->pages[n];\n     slab = page->slab;\n-    type = page->prev & NGX_SLAB_PAGE_MASK;\n+    type = ngx_slab_page_type(page);\n \n     switch (type) {\n \n@@ -470,17 +479,16 @@ ngx_slab_free_locked(ngx_slab_pool_t *pool, void *p)\n         }\n \n         n = ((uintptr_t) p & (ngx_pagesize - 1)) >> shift;\n-        m = (uintptr_t) 1 << (n & (sizeof(uintptr_t) * 8 - 1));\n-        n /= (sizeof(uintptr_t) * 8);\n+        m = (uintptr_t) 1 << (n % (sizeof(uintptr_t) * 8));\n+        n /= sizeof(uintptr_t) * 8;\n         bitmap = (uintptr_t *)\n                              ((uintptr_t) p & ~((uintptr_t) ngx_pagesize - 1));\n \n         if (bitmap[n] & m) {\n+            slot = shift - pool->min_shift;\n \n             if (page->next == NULL) {\n-                slots = (ngx_slab_page_t *)\n-                                   ((u_char *) pool + sizeof(ngx_slab_pool_t));\n-                slot = shift - pool->min_shift;\n+                slots = ngx_slab_slots(pool);\n \n                 page->next = slots[slot].next;\n                 slots[slot].next = page;\n@@ -491,7 +499,7 @@ ngx_slab_free_locked(ngx_slab_pool_t *pool, void *p)\n \n             bitmap[n] &= ~m;\n \n-            n = (1 << (ngx_pagesize_shift - shift)) / 8 / (1 << shift);\n+            n = (ngx_pagesize >> shift) / ((1 << shift) * 8);\n \n             if (n == 0) {\n                 n = 1;\n@@ -501,16 +509,18 @@ ngx_slab_free_locked(ngx_slab_pool_t *pool, void *p)\n                 goto done;\n             }\n \n-            map = (1 << (ngx_pagesize_shift - shift)) / (sizeof(uintptr_t) * 8);\n+            map = (ngx_pagesize >> shift) / (sizeof(uintptr_t) * 8);\n \n-            for (n = 1; n < map; n++) {\n-                if (bitmap[n]) {\n+            for (i = 1; i < map; i++) {\n+                if (bitmap[i]) {\n                     goto done;\n                 }\n             }\n \n             ngx_slab_free_pages(pool, page, 1);\n \n+            pool->stats[slot].total -= (ngx_pagesize >> shift) - n;\n+\n             goto done;\n         }\n \n@@ -527,10 +537,10 @@ ngx_slab_free_locked(ngx_slab_pool_t *pool, void *p)\n         }\n \n         if (slab & m) {\n+            slot = ngx_slab_exact_shift - pool->min_shift;\n+\n             if (slab == NGX_SLAB_BUSY) {\n-                slots = (ngx_slab_page_t *)\n-                                   ((u_char *) pool + sizeof(ngx_slab_pool_t));\n-                slot = ngx_slab_exact_shift - pool->min_shift;\n+                slots = ngx_slab_slots(pool);\n \n                 page->next = slots[slot].next;\n                 slots[slot].next = page;\n@@ -547,6 +557,8 @@ ngx_slab_free_locked(ngx_slab_pool_t *pool, void *p)\n \n             ngx_slab_free_pages(pool, page, 1);\n \n+            pool->stats[slot].total -= sizeof(uintptr_t) * 8;\n+\n             goto done;\n         }\n \n@@ -565,11 +577,10 @@ ngx_slab_free_locked(ngx_slab_pool_t *pool, void *p)\n                               + NGX_SLAB_MAP_SHIFT);\n \n         if (slab & m) {\n+            slot = shift - pool->min_shift;\n \n             if (page->next == NULL) {\n-                slots = (ngx_slab_page_t *)\n-                                   ((u_char *) pool + sizeof(ngx_slab_pool_t));\n-                slot = shift - pool->min_shift;\n+                slots = ngx_slab_slots(pool);\n \n                 page->next = slots[slot].next;\n                 slots[slot].next = page;\n@@ -586,6 +597,8 @@ ngx_slab_free_locked(ngx_slab_pool_t *pool, void *p)\n \n             ngx_slab_free_pages(pool, page, 1);\n \n+            pool->stats[slot].total -= ngx_pagesize >> shift;\n+\n             goto done;\n         }\n \n@@ -597,7 +610,7 @@ ngx_slab_free_locked(ngx_slab_pool_t *pool, void *p)\n             goto wrong_chunk;\n         }\n \n-        if (slab == NGX_SLAB_PAGE_FREE) {\n+        if (!(slab & NGX_SLAB_PAGE_START)) {\n             ngx_slab_error(pool, NGX_LOG_ALERT,\n                            \"ngx_slab_free(): page is already free\");\n             goto fail;\n@@ -625,6 +638,8 @@ ngx_slab_free_locked(ngx_slab_pool_t *pool, void *p)\n \n done:\n \n+    pool->stats[slot].used--;\n+\n     ngx_slab_junk(p, size);\n \n     return;\n@@ -677,6 +692,8 @@ ngx_slab_alloc_pages(ngx_slab_pool_t *pool, ngx_uint_t pages)\n             page->next = NULL;\n             page->prev = NGX_SLAB_PAGE;\n \n+            pool->pfree -= pages;\n+\n             if (--pages == 0) {\n                 return page;\n             }\n@@ -705,9 +722,10 @@ static void\n ngx_slab_free_pages(ngx_slab_pool_t *pool, ngx_slab_page_t *page,\n     ngx_uint_t pages)\n {\n-    ngx_uint_t        type;\n     ngx_slab_page_t  *prev, *join;\n \n+    pool->pfree += pages;\n+\n     page->slab = pages--;\n \n     if (pages) {\n@@ -715,7 +733,7 @@ ngx_slab_free_pages(ngx_slab_pool_t *pool, ngx_slab_page_t *page,\n     }\n \n     if (page->next) {\n-        prev = (ngx_slab_page_t *) (page->prev & ~NGX_SLAB_PAGE_MASK);\n+        prev = ngx_slab_page_prev(page);\n         prev->next = page->next;\n         page->next->prev = page->prev;\n     }\n@@ -723,15 +741,14 @@ ngx_slab_free_pages(ngx_slab_pool_t *pool, ngx_slab_page_t *page,\n     join = page + page->slab;\n \n     if (join < pool->last) {\n-        type = join->prev & NGX_SLAB_PAGE_MASK;\n \n-        if (type == NGX_SLAB_PAGE) {\n+        if (ngx_slab_page_type(join) == NGX_SLAB_PAGE) {\n \n             if (join->next != NULL) {\n                 pages += join->slab;\n                 page->slab += join->slab;\n \n-                prev = (ngx_slab_page_t *) (join->prev & ~NGX_SLAB_PAGE_MASK);\n+                prev = ngx_slab_page_prev(join);\n                 prev->next = join->next;\n                 join->next->prev = join->prev;\n \n@@ -744,19 +761,18 @@ ngx_slab_free_pages(ngx_slab_pool_t *pool, ngx_slab_page_t *page,\n \n     if (page > pool->pages) {\n         join = page - 1;\n-        type = join->prev & NGX_SLAB_PAGE_MASK;\n \n-        if (type == NGX_SLAB_PAGE) {\n+        if (ngx_slab_page_type(join) == NGX_SLAB_PAGE) {\n \n             if (join->slab == NGX_SLAB_PAGE_FREE) {\n-                join = (ngx_slab_page_t *) (join->prev & ~NGX_SLAB_PAGE_MASK);\n+                join = ngx_slab_page_prev(join);\n             }\n \n             if (join->next != NULL) {\n                 pages += join->slab;\n                 join->slab += page->slab;\n \n-                prev = (ngx_slab_page_t *) (join->prev & ~NGX_SLAB_PAGE_MASK);\n+                prev = ngx_slab_page_prev(join);\n                 prev->next = join->next;\n                 join->next->prev = join->prev;\n \ndiff --git a/src/core/ngx_slab.h b/src/core/ngx_slab.h\nindex 2922a80c..eff893c3 100644\n--- a/src/core/ngx_slab.h\n+++ b/src/core/ngx_slab.h\n@@ -23,6 +23,15 @@ struct ngx_slab_page_s {\n \n \n typedef struct {\n+    ngx_uint_t        total;\n+    ngx_uint_t        used;\n+\n+    ngx_uint_t        reqs;\n+    ngx_uint_t        fails;\n+} ngx_slab_stat_t;\n+\n+\n+typedef struct {\n     ngx_shmtx_sh_t    lock;\n \n     size_t            min_size;\n@@ -32,6 +41,9 @@ typedef struct {\n     ngx_slab_page_t  *last;\n     ngx_slab_page_t   free;\n \n+    ngx_slab_stat_t  *stats;\n+    ngx_uint_t        pfree;\n+\n     u_char           *start;\n     u_char           *end;\n \n"
  },
  {
    "path": "modules/ngx_slab_stat/t/test.t",
    "content": "#!/usr/bin/perl\n\n# Copyright (C) 2017 Alibaba Group Holding Limited\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->plan(1);\n$t->write_file_expand('nginx.conf', <<'EOF');\n\nmaster_process off;\ndaemon         off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location /slab_stat {\n            slab_stat;\n        }\n    }\n}\n\nEOF\n\n###############################################################################\n\n$t->run();\n\nmy $status = http_get(\"/slab_stat\");\n\nlike($status, qr/shared memory/m,\n     'slab_stat returns information about shared memory usage');\n\nprint \"--- debug for verbose mode ---\\n\",\n      \"$status\",\n      \"------------------------------\\n\";\n\n$t->stop();\n"
  },
  {
    "path": "modules/ngx_tongsuo_ntls/README.md",
    "content": "# Quick Start\n\n## Install Tengine\n1. Get Tongsuo\n\nNTLS (TLCP and GM/T 0024) is based on Tongsuo.\n```bash\ngit clone https://github.com/Tongsuo-Project/Tongsuo.git\n```\n\n2. Get Tengine\n\n```bash\ngit clone https://github.com/alibaba/tengine.git\n```\n\n3. Build Tengine\n\n- Add ngx_tongsuo_ntls module\n- Set OpenSSL library path to Tongsuo\n- Set build options for Tongsuo: enable-ntls\n\n```bash\ncd tengine\n\n# For Tongsuo master branch\n./configure --add-module=modules/ngx_tongsuo_ntls \\\n    --with-openssl=../Tongsuo \\\n    --with-openssl-opt=\"--strict-warnings --api=1.1.1 enable-ntls\" \\\n    --with-http_ssl_module --with-stream \\\n    --with-stream_ssl_module --with-stream_sni\n\n# for Tongsuo version 8.3 or lower\n./configure --add-module=modules/ngx_tongsuo_ntls \\\n    --with-openssl=../Tongsuo \\\n    --with-openssl-opt=\"--strict-warnings enable-ntls\" \\\n    --with-http_ssl_module --with-stream \\\n    --with-stream_ssl_module --with-stream_sni\n\nmake -j\nsudo make install\n```\n\n## Run Tengine\n\nExample of nginx.conf:\n```\nworker_processes  1;\n\nevents {\n    worker_connections  1024;\n}\n\nhttp {\n    include       mime.types;\n    default_type  application/octet-stream;\n\n    server {\n        listen       443 ssl;\n        server_name  localhost;\n\n        enable_ntls  on;\n        ssl_sign_certificate        server_sign.crt;\n        ssl_sign_certificate_key    server_sign.key;\n        ssl_enc_certificate         server_enc.crt;\n        ssl_enc_certificate_key     server_enc.key;\n\n        location / {\n            return 200 \"body $ssl_protocol:$ssl_cipher\";\n        }\n    }\n}\n\nstream {\n     server {\n        listen       8443 ssl;\n\n        enable_ntls  on;\n        ssl_sign_certificate        server_sign.crt;\n        ssl_sign_certificate_key    server_sign.key;\n        ssl_enc_certificate         server_enc.crt;\n        ssl_enc_certificate_key     server_enc.key;\n\n        return \"body $ssl_protocol:$ssl_cipher\";\n    }\n}\n```\n\n## Test NTLS\n\nWe need NTLS client to test tengine NTLS server. NTLS client is provided by\nTongsuo, so we need install Tongsuo firstly.\n```bash\ngit clone https://github.com/Tongsuo-Project/Tongsuo.git\n\ncd Tongsuo\n\n./config --prefix=/opt/tongsuo enable-ntls no-shared\nmake -j\nsudo make install\n\ncd ../\n```\n\nSet TEST_OPENSSL_BINARY to the path of openssl binary provided by Tongsuo.\n\n```bash\ncd tengine\n\nTEST_OPENSSL_BINARY=/opt/tongsuo/bin/openssl \\\nTEST_NGINX_BINARY=`pwd`/objs/nginx \\\nprove -Itests/nginx-tests/nginx-tests/lib/ modules/ngx_tongsuo_ntls/t -v\n```\n\n## Reference\n- [Tongsuo website](https://www.tongsuo.net/)\n- [Tongsuo document](https://www.yuque.com/tsdoc)\n"
  },
  {
    "path": "modules/ngx_tongsuo_ntls/config",
    "content": "if [ $USE_OPENSSL = YES ]; then\n    have=T_NGX_SSL_NTLS . auto/have\nfi\n"
  },
  {
    "path": "modules/ngx_tongsuo_ntls/t/CA.pm",
    "content": "package CA;\n\n# Copyright (C) Chenglong Zhang (K1)\n# Copyright (C) 2021 Alibaba Group Holding Limited\n\n# Sign certs for NTLS test cases.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse base qw/ Exporter /;\n\nour @EXPORT = qw/ make_sm2_ca_subca_end_certs  make_sm2_end_certs\n    make_rsa_end_cert make_ec_end_cert /;\n\nour $openssl = $ENV{'TEST_OPENSSL_BINARY'} || '/opt/tongsuo/bin/openssl';\nour $is_tongsuo = `$openssl version | grep Tongsuo | wc -l`;\nour $pkey_type = $is_tongsuo == '1' ? \"sm2\" : \"ec\";\n\nsub make_sm2_subca($$) {\n    my ($t, $tag) = @_;\n    my $d = $t->testdir();\n    my $ca = \"$tag\" . \"_ca\";\n    my $subca = \"$tag\" . \"_subca\";\n    my $ca_cnf = \"$tag\" . \"_ca.cnf\";\n    my $subca_cnf = \"$tag\" . \"_subca.cnf\";\n\n    $t->write_file($subca_cnf, <<EOF);\n[ ca ]\ndefault_ca = mysubca\n\n[ mysubca ]\nnew_certs_dir = $d/$subca/newcerts\ndatabase = $d/$subca/certindex\nserial = $d/$subca/certserial\ndefault_days = 3\nunique_subject = no\n\n# The root key and root certificate.\nprivate_key = $d/$subca/subca.key\ncertificate = $d/$subca/subca.crt\n\ndefault_md = sha256\n\npolicy = myca_policy\n\n[ myca_policy ]\ncommonName = supplied\n\n[ sign_req ]\n# Extensions to add to a certificate request\nbasicConstraints = CA:FALSE\nkeyUsage = nonRepudiation, digitalSignature\n\n[ enc_req ]\n# Extensions to add to a certificate request\nbasicConstraints = CA:FALSE\nkeyUsage = keyAgreement, keyEncipherment, dataEncipherment\n\n[ req ]\ndefault_bits = 1024\nencrypt_key = no\ndistinguished_name = req_distinguished_name\n[ req_distinguished_name ]\n\nEOF\n\n    mkdir(\"$d/$subca\");\n    mkdir(\"$d/$subca/newcerts\");\n    $t->write_file(\"$subca/certserial\", '1000');\n    $t->write_file(\"$subca/certindex\", '');\n\n    # sm2 subca\n    system(\"$openssl ecparam -genkey -name SM2 \"\n        . \"-out $d/$subca.key \"\n        . \">>$d/openssl.out 2>&1\") == 0\n        or die \"Can't create subca key: $!\\n\";\n\n    system(\"$openssl req -config $d/$ca_cnf \"\n        . \"-new -key $d/$subca.key \"\n        . \"-out $d/$subca.csr \"\n        . \"-sm3 -nodes -sigopt sm2_id:1234567812345678 \"\n        . \"-subj /CN=${tag}_sub_ca \"\n        . \">>$d/openssl.out 2>&1\") == 0\n        or die \"Can't create subca csr: $!\\n\";\n\n    system(\"$openssl ca -batch \"\n        . \"-config $d/$ca_cnf \"\n        . \"-in $d/$subca.csr \"\n        . \"-cert $d/$ca.crt -keyfile $d/$ca.key \"\n        . \"-extensions v3_intermediate_ca -notext \"\n        . \"-md sm3 \"\n        . \"-out $d/$subca.crt \"\n        . \">>$d/openssl.out 2>&1\") == 0\n        or die \"Can't create subca crt: $!\\n\";\n}\n\nsub make_sm2_ca($$) {\n    my ($t, $tag) = @_;\n    my $d = $t->testdir();\n    my $ca = \"$tag\" . \"_ca\";\n    my $ca_cnf = \"$tag\" . \"_ca.cnf\";\n\n    $t->write_file($ca_cnf, <<EOF);\n[ ca ]\ndefault_ca = myca\n\n[ myca ]\nnew_certs_dir = $d/$ca/newcerts\ndatabase = $d/$ca/certindex\nserial = $d/$ca/certserial\ndefault_days = 3\n\n# The root key and root certificate.\nprivate_key = $d/$ca/ca.key\ncertificate = $d/$ca/ca.crt\n\ndefault_md = sha256\n\npolicy = myca_policy\n\n[ myca_policy ]\ncommonName = supplied\n\n[ v3_ca ]\n# Extensions for a typical CA (`man x509v3_config`).\nsubjectKeyIdentifier = hash\nauthorityKeyIdentifier = keyid:always,issuer\nbasicConstraints = critical, CA:true\nkeyUsage = critical, digitalSignature, cRLSign, keyCertSign\n\n[ v3_intermediate_ca ]\n# Extensions for a typical intermediate CA (`man x509v3_config`).\nsubjectKeyIdentifier = hash\nauthorityKeyIdentifier = keyid:always,issuer\nbasicConstraints = critical, CA:true, pathlen:0\nkeyUsage = critical, digitalSignature, cRLSign, keyCertSign\n\n[ req ]\ndefault_bits = 1024\nencrypt_key = no\ndistinguished_name = req_distinguished_name\n[ req_distinguished_name ]\nEOF\n\n    mkdir(\"$d/$ca\");\n    mkdir(\"$d/$ca/newcerts\");\n    $t->write_file(\"$ca/certserial\", '1000');\n    $t->write_file(\"$ca/certindex\", '');\n\n    # sm2 ca\n    system(\"$openssl ecparam -genkey -name SM2 \"\n        . \"-out $d/$ca.key \"\n        . \">>$d/openssl.out 2>&1\") == 0\n        or die \"Can't create ca key: $!\\n\";\n\n    system(\"$openssl req -config $d/$ca_cnf \"\n        . \"-new -key $d/$ca.key \"\n        . \"-out $d/$ca.csr \"\n        . \"-sm3 -nodes -sigopt sm2_id:1234567812345678 \"\n        . \"-subj /CN=${tag}_root_ca \"\n        . \">>$d/openssl.out 2>&1\") == 0\n        or die \"Can't create ca csr: $!\\n\";\n\n    system(\"$openssl ca -batch -selfsign \"\n        . \"-config $d/$ca_cnf \"\n        . \"-in $d/$ca.csr -keyfile $d/$ca.key \"\n        . \"-extensions v3_ca -notext \"\n        . \"-md sm3 \"\n        . \"-out $d/$ca.crt \"\n        . \">>$d/openssl.out 2>&1\") == 0\n        or die \"Can't create ca crt: $!\\n\";\n}\n\nsub make_sm2_double_certs($$) {\n    my ($t, $tag) = @_;\n    my $d = $t->testdir();\n    my $subca_cnf = $tag . \"_subca.cnf\";\n    my $subca = $tag . \"_subca\";\n\n\n    # sm2 double certs\n    system(\"$openssl ecparam -name SM2 \"\n        . \"-out $d/${tag}_sm2.param \"\n        . \">>$d/openssl.out 2>&1\") == 0\n        or die \"Can't create ${tag} param: $!\\n\";\n\n    system(\"$openssl req -config $d/$subca_cnf \"\n        . \"-newkey $pkey_type:$d/${tag}_sm2.param \"\n        . \"-nodes -keyout $d/${tag}_sign.key \"\n        . \"-sm3 -sigopt sm2_id:1234567812345678 \"\n        . \"-new -out $d/${tag}_sign.csr \"\n        . \"-subj /CN=${tag}_sign \"\n        . \">>$d/openssl.out 2>&1\") == 0\n        or die \"Can't create ${tag} sign csr: $!\\n\";\n\n    system(\"$openssl ca -batch -config $d/$subca_cnf \"\n        . \"-extensions sign_req \"\n        . \"-in $d/${tag}_sign.csr \"\n        . \"-notext -out $d/${tag}_sign.crt \"\n        . \"-cert $d/$subca.crt -keyfile $d/$subca.key \"\n        . \"-md sm3 \"\n        . \">>$d/openssl.out 2>&1\") == 0\n        or die \"Can't create ${tag} sign crt $!\\n\";\n\n    system(\"$openssl ca -batch -config $d/$subca_cnf \"\n        . \"-extensions sign_req \"\n        . \"-startdate 20000101000000Z -enddate 20010101000000Z \"\n        . \"-in $d/${tag}_sign.csr \"\n        . \"-notext -out $d/${tag}_sign_expire.crt \"\n        . \"-cert $d/$subca.crt -keyfile $d/$subca.key \"\n        . \"-md sm3 \"\n        . \">>$d/openssl.out 2>&1\") == 0\n        or die \"Can't create ${tag} sign expire crt $!\\n\";\n\n    system(\"$openssl req -config $d/$subca_cnf \"\n        . \"-newkey $pkey_type:$d/${tag}_sm2.param \"\n        . \"-nodes -keyout $d/${tag}_enc.key \"\n        . \"-sm3 -sigopt sm2_id:1234567812345678 \"\n        . \"-new -out $d/${tag}_enc.csr \"\n        . \"-subj /CN=${tag}_enc \"\n        . \">>$d/openssl.out 2>&1\") == 0\n        or die \"Can't create ${tag} enc csr: $!\\n\";\n\n    system(\"$openssl ca -batch -config $d/$subca_cnf \"\n        . \"-extensions enc_req \"\n        . \"-in $d/${tag}_enc.csr \"\n        . \"-notext -out $d/${tag}_enc.crt \"\n        . \"-cert $d/$subca.crt -keyfile $d/$subca.key \"\n        . \"-md sm3 \"\n        . \">>$d/openssl.out 2>&1\") == 0\n        or die \"Can't create ${tag} enc crt $!\\n\";\n\n    system(\"$openssl ca -batch -config $d/$subca_cnf \"\n        . \"-extensions enc_req \"\n        . \"-startdate 20000101000000Z -enddate 20010101000000Z \"\n        . \"-in $d/${tag}_enc.csr \"\n        . \"-notext -out $d/${tag}_enc_expire.crt \"\n        . \"-cert $d/$subca.crt -keyfile $d/$subca.key \"\n        . \"-md sm3 \"\n        . \">>$d/openssl.out 2>&1\") == 0\n        or die \"Can't create ${tag} enc expire crt $!\\n\";\n\n}\n\nsub make_sm2_end_certs($$) {\n    my ($t, $tag) = @_;\n    my $d = $t->testdir();\n\n    $t->write_file('openssl.conf', <<EOF);\n[ req ]\ndefault_bits = 1024\nencrypt_key = no\ndistinguished_name = req_distinguished_name\n[ req_distinguished_name ]\nEOF\n\n    foreach my $name ('sign', 'enc') {\n        system(\"$openssl ecparam -genkey -name SM2 -out $d/${tag}_$name.key \"\n            . \">>$d/openssl.out 2>&1\") == 0 or die \"Can't create key: $!\\n\";\n\n        system(\"$openssl req -x509 -new -key $d/${tag}_$name.key \"\n            . \"-config $d/openssl.conf -subj /CN=${tag}_$name/ \"\n            . \"-out $d/${tag}_$name.crt -keyout $d/${tag}_$name.key \"\n            . \">>$d/openssl.out 2>&1\") == 0\n            or die \"Can't create certificate for ${tag}_$name: $!\\n\";\n    }\n\n    $t->write_file(\"${tag}_sign_enc.crt\",\n        $t->read_file(\"${tag}_sign.crt\")\n            . $t->read_file(\"${tag}_enc.crt\"));\n}\n\nsub make_sm2_ca_subca_end_certs($$) {\n    my ($t, $tag) = @_;\n\n    make_sm2_ca($t, $tag);\n    make_sm2_subca($t, $tag);\n    make_sm2_double_certs($t, $tag);\n\n    $t->write_file(\"${tag}_ca_chain.crt\",\n        $t->read_file(\"${tag}_subca.crt\")\n            . $t->read_file(\"${tag}_ca.crt\"));\n\n    $t->write_file(\"${tag}_sign_enc.crt\",\n        $t->read_file(\"${tag}_sign.crt\")\n            . $t->read_file(\"${tag}_enc.crt\"));\n\n}\n\nsub make_rsa_end_cert($) {\n    my ($t) = @_;\n    my $d = $t->testdir();\n\n    $t->write_file('openssl.conf', <<EOF);\n[ req ]\ndefault_bits = 1024\nencrypt_key = no\ndistinguished_name = req_distinguished_name\n[ req_distinguished_name ]\nEOF\n\n    system(\"$openssl genrsa -out $d/rsa.key 1024 \"\n        . \">>$d/openssl.out 2>&1\") == 0\n        or die \"Can't create RSA pem: $!\\n\";\n\n    system(\"$openssl req -x509 -new -key $d/rsa.key \"\n        . \"-config $d/openssl.conf -subj /CN=rsa/ \"\n        . \"-out $d/rsa.crt -keyout $d/rsa.key \"\n        . \">>$d/openssl.out 2>&1\") == 0\n        or die \"Can't create certificate for rsa: $!\\n\";\n}\n\nsub make_ec_end_cert($) {\n    my ($t) = @_;\n    my $d = $t->testdir();\n\n    $t->write_file('openssl.conf', <<EOF);\n[ req ]\ndefault_bits = 1024\nencrypt_key = no\ndistinguished_name = req_distinguished_name\n[ req_distinguished_name ]\nEOF\n\n    system(\"$openssl ecparam -genkey -out $d/ec.key -name prime256v1 \"\n        . \">>$d/openssl.out 2>&1\") == 0 or die \"Can't create EC pem: $!\\n\";\n\n    system(\"$openssl req -x509 -new -key $d/ec.key \"\n        . \"-config $d/openssl.conf -subj /CN=ec/ \"\n        . \"-out $d/ec.crt -keyout $d/ec.key \"\n        . \">>$d/openssl.out 2>&1\") == 0\n        or die \"Can't create certificate for ec: $!\\n\";\n}\n###############################################################################\n\n1;\n\n###############################################################################\n"
  },
  {
    "path": "modules/ngx_tongsuo_ntls/t/ntls.t",
    "content": "#!/usr/bin/perl\n\n# Copyright (C) Chenglong Zhang (K1)\n# Copyright (C) 2021 Alibaba Group Holding Limited\n\n###############################################################################\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse lib \".\";\nuse CA qw/ make_sm2_ca_subca_end_certs make_rsa_end_cert make_ec_end_cert /;\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $openssl = $ENV{'TEST_OPENSSL_BINARY'} || \"/opt/tongsuo/bin/openssl\";\n\nmy $t = Test::Nginx->new()->has(qw/http http_ssl/)->plan(8);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nworker_processes 1;  # NOTE: The default value of Tengine worker_processes directive is `worker_processes auto;`.\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:9090 ssl;\n        server_name  localhost;\n\n        ssl_certificate_key rsa.key;\n        ssl_certificate     rsa.crt;\n\n        ssl_certificate_key ec.key;\n        ssl_certificate     ec.crt;\n\n        location / {\n            return 200 \"body $ssl_protocol\";\n        }\n    }\n\n    server {\n        listen       127.0.0.1:9091 ssl;\n        server_name  localhost;\n\n        enable_ntls  on;\n        ssl_sign_certificate        server_sign.crt;\n        ssl_sign_certificate_key    server_sign.key;\n        ssl_enc_certificate         server_enc.crt;\n        ssl_enc_certificate_key     server_enc.key;\n\n        ssl_trusted_certificate     client_ca_chain.crt;\n\n        location / {\n            return 200 \"body $ssl_protocol:$ssl_cipher\";\n        }\n    }\n\n    server {\n        listen       127.0.0.1:9092 ssl;\n        server_name  localhost;\n\n        ssl_certificate_key rsa.key;\n        ssl_certificate     rsa.crt;\n\n        ssl_certificate_key ec.key;\n        ssl_certificate     ec.crt;\n\n        enable_ntls  on;\n        ssl_sign_certificate        server_sign.crt;\n        ssl_sign_certificate_key    server_sign.key;\n        ssl_enc_certificate         server_enc.crt;\n        ssl_enc_certificate_key     server_enc.key;\n\n        location / {\n            return 200 \"body $ssl_protocol\";\n        }\n    }\n}\n\nEOF\n\nmy $d = $t->testdir();\n\nmake_rsa_end_cert($t);\nmake_ec_end_cert($t);\n\nmake_sm2_ca_subca_end_certs($t, \"client\");\nmake_sm2_ca_subca_end_certs($t, \"server\");\n\n$t->run();\n\nmy $ret1 = `/bin/echo -e \"GET / HTTP/1.0\\r\\n\\r\\n\" | $openssl s_client -connect localhost:9090 -cipher aRSA -quiet -ign_eof 2>&1`;\nmy $ret2 = `/bin/echo -e \"GET / HTTP/1.0\\r\\n\\r\\n\" | $openssl s_client -connect localhost:9090 -cipher aECDSA -quiet -ign_eof 2>&1`;\nmy $ret3 = `/bin/echo -e \"GET / HTTP/1.0\\r\\n\\r\\n\" | $openssl s_client -connect localhost:9091 -cipher ECC-SM2-SM4-CBC-SM3 -quiet -ign_eof -enable_ntls -ntls 2>&1`;\nmy $ret4 = `/bin/echo -e \"GET / HTTP/1.0\\r\\n\\r\\n\" | $openssl s_client -connect localhost:9091 -cipher ECC-SM2-SM4-GCM-SM3 -quiet -ign_eof -enable_ntls -ntls 2>&1`;\nmy $ret5 = `/bin/echo -e \"GET / HTTP/1.0\\r\\n\\r\\n\" | $openssl s_client -connect localhost:9091 -cipher ECDHE-SM2-SM4-CBC-SM3 -quiet -ign_eof  -enc_cert $d/client_enc.crt -enc_key $d/client_enc.key -sign_cert $d/client_sign.crt -sign_key $d/client_sign.key -enable_ntls -ntls 2>&1`;\nmy $ret6 = `/bin/echo -e \"GET / HTTP/1.0\\r\\n\\r\\n\" | $openssl s_client -connect localhost:9091 -cipher ECDHE-SM2-SM4-GCM-SM3 -quiet -ign_eof -enc_cert $d/client_enc.crt -enc_key $d/client_enc.key -sign_cert $d/client_sign.crt -sign_key $d/client_sign.key -enable_ntls -ntls 2>&1`;\nmy $ret7 = `/bin/echo -e \"GET / HTTP/1.0\\r\\n\\r\\n\" | $openssl s_client -connect localhost:9092 -cipher aRSA -quiet -ign_eof 2>&1`;\nmy $ret8 = `/bin/echo -e \"GET / HTTP/1.0\\r\\n\\r\\n\" | $openssl s_client -connect localhost:9092 -cipher aECDSA -quiet -ign_eof 2>&1`;\n\nlike($ret1, qr/^body TLSv(\\d|\\.)+/m, 'disable NTLS, TLS handshake success with aRSA');\nlike($ret2, qr/^body TLSv(\\d|\\.)+$/m, 'disable NTLS, TLS handshake success with aECDSA');\nlike($ret3, qr/^body NTLSv(\\d|\\.)+:ECC-SM2-SM4-CBC-SM3$/m, 'NTLS ECC-SM2-SM4-CBC-SM3 handshake success');\nlike($ret4, qr/^body NTLSv(\\d|\\.)+:ECC-SM2-SM4-GCM-SM3$/m, 'NTLS ECC-SM2-SM4-GCM-SM3 handshake success');\nlike($ret5, qr/^body NTLSv(\\d|\\.)+:ECDHE-SM2-SM4-CBC-SM3$/m, 'NTLS ECDHE-SM2-SM4-CBC-SM3 handshake success');\nlike($ret6, qr/^body NTLSv(\\d|\\.)+:ECDHE-SM2-SM4-GCM-SM3$/m, 'NTLS ECDHE-SM2-SM4-GCM-SM3 handshake success');\nlike($ret7, qr/^body TLSv(\\d|\\.)+$/m, 'enable NTLS, TLS handshake success with aRSA');\nlike($ret8, qr/^body TLSv(\\d|\\.)+$/m, 'enable NTLS, TLS handshake success with aECDSA');\n\n$t->stop();\n"
  },
  {
    "path": "modules/ngx_tongsuo_ntls/t/ntls_certificate_chain.t",
    "content": "#!/usr/bin/perl\n\n# Copyright (C) Chenglong Zhang (K1)\n# Copyright (C) 2021 Alibaba Group Holding Limited\n\n###############################################################################\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse lib \".\";\nuse CA qw/ make_sm2_ca_subca_end_certs /;\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $openssl = $ENV{'TEST_OPENSSL_BINARY'} || \"/opt/tongsuo/bin/openssl\";\n\nmy $t = Test::Nginx->new()->has(qw/http http_ssl/)->plan(3);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nworker_processes 1;  # NOTE: The default value of Tengine worker_processes directive is `worker_processes auto;`.\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:9010 ssl;\n        server_name  localhost;\n\n        enable_ntls  on;\n        ssl_sign_certificate        server_sign.crt;\n        ssl_sign_certificate_key    server_sign.key;\n        ssl_enc_certificate         server_enc.crt;\n        ssl_enc_certificate_key     server_enc.key;\n\n        location / {\n            return 200 \"$ssl_protocol\\n$ssl_cipher\";\n        }\n    }\n\n    server {\n        listen       127.0.0.1:9011 ssl;\n        server_name  localhost;\n\n        enable_ntls  on;\n        ssl_sign_certificate        server_sign_subca.crt;\n        ssl_sign_certificate_key    server_sign.key;\n        ssl_enc_certificate         server_enc.crt;\n        ssl_enc_certificate_key     server_enc.key;\n\n        location / {\n            return 200 \"body $ssl_protocol\";\n        }\n    }\n\n    server {\n        listen       127.0.0.1:9012 ssl;\n        server_name  localhost;\n\n        enable_ntls  on;\n        ssl_sign_certificate        server_sign_allca.crt;\n        ssl_sign_certificate_key    server_sign.key;\n        ssl_enc_certificate         server_enc.crt;\n        ssl_enc_certificate_key     server_enc.key;\n\n        location / {\n            return 200 \"body $ssl_protocol\";\n        }\n    }\n}\n\nEOF\n\nmake_sm2_ca_subca_end_certs($t, \"client\");\nmake_sm2_ca_subca_end_certs($t, \"server\");\n\n$t->write_file('server_sign_subca.crt',\n    $t->read_file('server_sign.crt')\n        . $t->read_file('server_subca.crt'));\n\n$t->write_file('server_sign_allca.crt',\n    $t->read_file('server_sign.crt')\n        . $t->read_file('server_subca.crt')\n        . $t->read_file('server_ca.crt'));\n\n$t->run();\n\nmy $d = $t->testdir();\n\nmy $ret1 = `/bin/echo -e \"GET / HTTP/1.0\\r\\n\\r\\n\" | $openssl s_client -connect localhost:9010 -verify_return_error -quiet -enable_ntls -ntls 2>&1`;\nmy $ret2 = `/bin/echo -e \"GET / HTTP/1.0\\r\\n\\r\\n\" | $openssl s_client -connect localhost:9011 -CAfile $d/server_ca.crt -verify_return_error -quiet -enable_ntls -ntls 2>&1`;\nmy $ret3 = `/bin/echo -e \"GET / HTTP/1.0\\r\\n\\r\\n\" | $openssl s_client -connect localhost:9012 -CAfile $d/server_ca.crt -verify_return_error -quiet -enable_ntls -ntls 2>&1`;\n\nlike($ret1, qr/^verify error/m, 'NTLS handshake no issuer certificate');\nlike($ret2, qr/^body NTLSv(\\d|\\.)+$/m, 'NTLS handshake success with subca');\nlike($ret3, qr/^body NTLSv(\\d|\\.)+$/m, 'NTLS handshake success with all ca');\n\n$t->stop();\n"
  },
  {
    "path": "modules/ngx_tongsuo_ntls/t/ntls_proxy.t",
    "content": "#!/usr/bin/perl\n\n# Copyright (C) Jin Jiu (wa5i)\n# Copyright (C) 2022 Alibaba Group Holding Limited\n\n###############################################################################\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse lib \".\";\nuse CA qw/ make_sm2_ca_subca_end_certs make_rsa_end_cert make_ec_end_cert /;\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $openssl = $ENV{'TEST_OPENSSL_BINARY'} || \"/opt/tongsuo/bin/openssl\";\n\nmy $t = Test::Nginx->new()->has(qw/http http_ssl/)->plan(9);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nworker_processes 1;  # NOTE: The default value of Tengine worker_processes directive is `worker_processes auto;`.\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:9022 ssl;\n        server_name  localhost;\n\n        ssl_certificate_key rsa.key;\n        ssl_certificate     rsa.crt;\n\n        ssl_certificate_key ec.key;\n        ssl_certificate     ec.crt;\n\n        enable_ntls  on;\n        ssl_sign_certificate        server_sign.crt;\n        ssl_sign_certificate_key    server_sign.key;\n        ssl_enc_certificate         server_enc.crt;\n        ssl_enc_certificate_key     server_enc.key;\n\n        ssl_trusted_certificate     client_ca_chain.crt;\n\n        location / {\n            return 200 \"ssl_protocol=$ssl_protocol, ssl_cipher=$ssl_cipher\";\n        }\n    }\n\n    server {\n        listen       127.0.0.1:9023 ssl;\n        server_name  localhost;\n\n        ssl_certificate_key rsa.key;\n        ssl_certificate     rsa.crt;\n\n        ssl_certificate_key ec.key;\n        ssl_certificate     ec.crt;\n\n        enable_ntls  on;\n        ssl_sign_certificate        server_sign.crt;\n        ssl_sign_certificate_key    server_sign.key;\n        ssl_enc_certificate         server_enc.crt;\n        ssl_enc_certificate_key     server_enc.key;\n\n        ssl_trusted_certificate     client_ca_chain.crt;\n\n        location / {\n            proxy_enable_ntls $arg_enable_ntls;\n            proxy_ssl_sign_certificate        client_sign.crt;\n            proxy_ssl_sign_certificate_key    client_sign.key;\n            proxy_ssl_enc_certificate         client_enc.crt;\n            proxy_ssl_enc_certificate_key     client_enc.key;\n            proxy_ssl_trusted_certificate     server_ca_chain.crt;\n            proxy_ssl_ciphers \"ECC-SM2-WITH-SM4-SM3:ECDHE-SM2-WITH-SM4-SM3:RSA\";\n\n            proxy_pass https://localhost:9022;\n        }\n\n        location /ecdhe {\n            proxy_enable_ntls $arg_enable_ntls;\n            proxy_ssl_sign_certificate        client_sign.crt;\n            proxy_ssl_sign_certificate_key    client_sign.key;\n            proxy_ssl_enc_certificate         client_enc.crt;\n            proxy_ssl_enc_certificate_key     client_enc.key;\n            proxy_ssl_trusted_certificate     server_ca_chain.crt;\n            proxy_ssl_ciphers \"ECDHE-SM2-SM4-GCM-SM3:RSA\";\n\n            proxy_pass https://localhost:9022;\n        }\n    }\n}\n\nEOF\n\nmy $d = $t->testdir();\n\nmake_rsa_end_cert($t);\nmake_ec_end_cert($t);\n\nmake_sm2_ca_subca_end_certs($t, \"client\");\nmake_sm2_ca_subca_end_certs($t, \"server\");\n\n$t->run();\n\nmy $ret1 = `/bin/echo -e \"GET / HTTP/1.0\\r\\n\\r\\n\" | $openssl s_client -connect localhost:9023 -cipher AES128-GCM-SHA256 -quiet -ign_eof 2>&1`;\nmy $ret2 = `/bin/echo -e \"GET / HTTP/1.0\\r\\n\\r\\n\" | $openssl s_client -connect localhost:9023 -cipher ECC-SM2-SM4-CBC-SM3 -quiet -ign_eof -enable_ntls -ntls 2>&1`;\nmy $ret3 = `/bin/echo -e \"GET /?enable_ntls=on HTTP/1.0\\r\\n\\r\\n\" | $openssl s_client -connect localhost:9023 -cipher AES128-GCM-SHA256 -quiet -ign_eof 2>&1`;\nmy $ret4 = `/bin/echo -e \"GET /?enable_ntls=on HTTP/1.0\\r\\n\\r\\n\" | $openssl s_client -connect localhost:9023 -cipher ECC-SM2-SM4-CBC-SM3 -quiet -ign_eof -enable_ntls -ntls 2>&1`;\nmy $ret5 = `/bin/echo -e \"GET /?enable_ntls=off HTTP/1.0\\r\\n\\r\\n\" | $openssl s_client -connect localhost:9023 -cipher ECC-SM2-SM4-GCM-SM3 -quiet -ign_eof -enable_ntls -ntls 2>&1`;\nmy $ret6 = `/bin/echo -e \"GET /?enable_ntls=xxxxx HTTP/1.0\\r\\n\\r\\n\" | $openssl s_client -connect localhost:9023 -cipher ECC-SM2-SM4-GCM-SM3 -quiet -ign_eof -enable_ntls -ntls 2>&1`;\nmy $ret7 = `/bin/echo -e \"GET /ecdhe HTTP/1.0\\r\\n\\r\\n\" | $openssl s_client -connect localhost:9023 -cipher ECDHE-SM2-SM4-CBC-SM3 -quiet -ign_eof -enc_cert $d/client_enc.crt -enc_key $d/client_enc.key -sign_cert $d/client_sign.crt -sign_key $d/client_sign.key -enable_ntls -ntls 2>&1`;\nmy $ret8 = `/bin/echo -e \"GET /ecdhe?enable_ntls=on HTTP/1.0\\r\\n\\r\\n\" | $openssl s_client -connect localhost:9023 -cipher ECDHE-SM2-SM4-GCM-SM3 -quiet -ign_eof -enc_cert $d/client_enc.crt -enc_key $d/client_enc.key -sign_cert $d/client_sign.crt -sign_key $d/client_sign.key -enable_ntls -ntls 2>&1`;\nmy $ret9 = `/bin/echo -e \"GET /ecdhe?enable_ntls=off HTTP/1.0\\r\\n\\r\\n\" | $openssl s_client -connect localhost:9023 -cipher ECDHE-SM2-SM4-GCM-SM3 -quiet -ign_eof -enc_cert $d/client_enc.crt -enc_key $d/client_enc.key -sign_cert $d/client_sign.crt -sign_key $d/client_sign.key -enable_ntls -ntls 2>&1`;\n\nlike($ret1, qr/^ssl_protocol=TLSv1\\.3.*$/m, 'client -----(TLSv1.2 AES128-GCM-SHA256)-----> server(no proxy_enable_ntls) -----(TLSv1.3)-----> origin');\nlike($ret2, qr/^ssl_protocol=TLSv1\\.3.*$/m, 'client -----(NTLSv1.1 ECC-SM2-SM4-CBC-SM3)-----> server(no proxy_enable_ntls) -----(TLSv1.3)-----> origin');\nlike($ret3, qr/^ssl_protocol=NTLSv1\\.1, ssl_cipher=ECC-SM2-SM4-CBC-SM3/m, 'client -----(TLSv1.2 AES128-GCM-SHA256)-----> server(proxy_enable_ntls=on) -----(NTLSv1.1 ECC-SM2-SM4-CBC-SM3)-----> origin');\nlike($ret4, qr/^ssl_protocol=NTLSv1\\.1, ssl_cipher=ECC-SM2-SM4-CBC-SM3/m, 'client -----(NTLSv1.1 ECC-SM2-SM4-CBC-SM3)-----> server(proxy_enable_ntls=on) -----(NTLSv1.1 ECC-SM2-SM4-CBC-SM3)-----> origin');\nlike($ret5, qr/^ssl_protocol=TLSv1\\.3.*$/m, 'client -----(NTLSv1.1 ECC-SM2-SM4-GCM-SM3)-----> server(proxy_enable_ntls=off) -----(TLSv1.3)-----> origin');\nlike($ret6, qr/^ssl_protocol=TLSv1\\.3.*$/m, 'client -----(NTLSv1.1 ECC-SM2-SM4-GCM-SM3)-----> server(proxy_enable_ntls=xxxxx) -----(TLSv1.3)-----> origin');\nlike($ret7, qr/^ssl_protocol=TLSv1\\.3.*$/m, 'client -----(NTLSv1.1 ECDHE-SM2-SM4-CBC-SM3)-----> server(no proxy_enable_ntls) -----(TLSv1.3)-----> origin');\nlike($ret8, qr/^ssl_protocol=NTLSv1\\.1, ssl_cipher=ECDHE-SM2-SM4-GCM-SM3/m, 'client -----(NTLSv1.1 ECDHE-SM2-SM4-CBC-SM3)-----> server(proxy_enable_ntls=on) -----(NTLSv1.1 ECDHE-SM2-SM4-CBC-SM3)-----> origin');\nlike($ret9, qr/^ssl_protocol=TLSv1\\.3.*$/m, 'client -----(NTLSv1.1 ECDHE-SM2-SM4-CBC-SM3)-----> server(proxy_enable_ntls=off) -----(TLSv1.3)-----> origin');\n\n$t->stop();\n"
  },
  {
    "path": "modules/ngx_tongsuo_ntls/t/ntls_session_cache.t",
    "content": "#!/usr/bin/perl\n\n# Copyright (C) Chenglong Zhang (K1)\n# Copyright (C) 2021 Alibaba Group Holding Limited\n\n###############################################################################\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse lib \".\";\nuse CA qw/ make_sm2_ca_subca_end_certs /;\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $openssl = $ENV{'TEST_OPENSSL_BINARY'} || \"/opt/tongsuo/bin/openssl\";\n\nmy $t = Test::Nginx->new()->has(qw/http http_ssl/)->plan(11);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nworker_processes 1;  # NOTE: The default value of Tengine worker_processes directive is `worker_processes auto;`.\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    enable_ntls  on;\n    ssl_sign_certificate        server_sign.crt;\n    ssl_sign_certificate_key    server_sign.key;\n    ssl_enc_certificate         server_enc.crt;\n    ssl_enc_certificate_key     server_enc.key;\n\n    ssl_session_tickets off;\n\n    server {\n        listen       127.0.0.1:9031;\n        server_name  localhost;\n\n        # Special case for enabled \"ssl\" directive.\n\n        ssl on;\n        ssl_session_cache builtin;\n        ssl_session_timeout 1;\n\n        location / {\n            return 200 \"body $ssl_session_reused\";\n        }\n    }\n\n    server {\n        listen       127.0.0.1:9032 ssl;\n        server_name  localhost;\n\n        ssl_session_cache builtin:1000;\n\n        location / {\n            return 200 \"body $ssl_session_reused\";\n        }\n    }\n\n    server {\n        listen       127.0.0.1:9033 ssl;\n        server_name  localhost;\n\n        ssl_session_cache none;\n\n        location / {\n            return 200 \"body $ssl_session_reused\";\n        }\n    }\n\n    server {\n        listen       127.0.0.1:9034 ssl;\n        server_name  localhost;\n\n        ssl_session_cache off;\n\n        location / {\n            return 200 \"body $ssl_session_reused\";\n        }\n    }\n\n    server {\n        listen       127.0.0.1:9035 ssl;\n        server_name  localhost;\n\n        ssl_session_cache shared:SSL:1m;\n        ssl_verify_client optional_no_ca;\n\n        location /reuse {\n            return 200 \"body $ssl_session_reused\";\n        }\n    }\n}\n\nEOF\n\nmy $d = $t->testdir();\n\nmake_sm2_ca_subca_end_certs($t, \"client\");\nmake_sm2_ca_subca_end_certs($t, \"server\");\n\n$t->run();\n\nmy $ret1 = `/bin/echo -e \"GET /reuse HTTP/1.0\\r\\n\\r\\n\" | $openssl s_client -connect localhost:9035 -quiet -sess_out 1.sess -enable_ntls -ntls 2>&1`;\nmy $ret2 = `/bin/echo -e \"GET /reuse HTTP/1.0\\r\\n\\r\\n\" | $openssl s_client -connect localhost:9035 -quiet -sess_in 1.sess -enable_ntls -ntls 2>&1`;\nmy $ret3 = `/bin/echo -e \"GET / HTTP/1.0\\r\\n\\r\\n\" | $openssl s_client -connect localhost:9031 -quiet -sess_out 3.sess -enable_ntls -ntls 2>&1`;\nmy $ret4 = `/bin/echo -e \"GET / HTTP/1.0\\r\\n\\r\\n\" | $openssl s_client -connect localhost:9031 -quiet -sess_in 3.sess -enable_ntls -ntls 2>&1`;\nmy $ret5 = `/bin/echo -e \"GET / HTTP/1.0\\r\\n\\r\\n\" | $openssl s_client -connect localhost:9032 -quiet -sess_out 5.sess -enable_ntls -ntls 2>&1`;\nmy $ret6 = `/bin/echo -e \"GET / HTTP/1.0\\r\\n\\r\\n\" | $openssl s_client -connect localhost:9032 -quiet -sess_in 5.sess -enable_ntls -ntls 2>&1`;\nmy $ret7 = `/bin/echo -e \"GET / HTTP/1.0\\r\\n\\r\\n\" | $openssl s_client -connect localhost:9033 -quiet -sess_out 7.sess -enable_ntls -ntls 2>&1`;\nmy $ret8 = `/bin/echo -e \"GET / HTTP/1.0\\r\\n\\r\\n\" | $openssl s_client -connect localhost:9033 -quiet -sess_in 7.sess -enable_ntls -ntls 2>&1`;\nmy $ret9 = `/bin/echo -e \"GET / HTTP/1.0\\r\\n\\r\\n\" | $openssl s_client -connect localhost:9034 -quiet -sess_out 9.sess -enable_ntls -ntls 2>&1`;\n\n# session timeout\nselect undef, undef, undef, 2.1;\nmy $ret11 = `/bin/echo -e \"GET / HTTP/1.0\\r\\n\\r\\n\" | $openssl s_client -connect localhost:9031 -quiet -sess_in 3.sess -enable_ntls -ntls 2>&1`;\n\nlike($ret1, qr/^body \\.$/m, 'shared initial session');\nlike($ret2, qr/^body r$/m, 'shared session reused');\nlike($ret3, qr/^body \\.$/m, 'builtin initial session');\nlike($ret4, qr/^body r$/m, 'builtin session reused');\nlike($ret5, qr/^body \\.$/m, 'builtin size initial session');\nlike($ret6, qr/^body r$/m, 'builtin size session reused');\nlike($ret7, qr/^body \\.$/m, 'reused none initial session');\nlike($ret8, qr/^body \\.$/m, 'session not reused 1');\nlike($ret9, qr/^body \\.$/m, 'session off initial session');\nisnt(-f \"$d/9.sess\", 1, 'session off no session out');\nlike($ret11, qr/^body \\.$/m, 'session timeout');\n\n$t->stop();\n"
  },
  {
    "path": "modules/ngx_tongsuo_ntls/t/ntls_sni.t",
    "content": "#!/usr/bin/perl\n\n# Copyright (C) Chenglong Zhang (K1)\n# Copyright (C) 2021 Alibaba Group Holding Limited\n\n###############################################################################\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse lib \".\";\nuse CA qw/ make_sm2_end_certs /;\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $openssl = $ENV{'TEST_OPENSSL_BINARY'} || \"/opt/tongsuo/bin/openssl\";\n\nmy $t = Test::Nginx->new()->has(qw/http http_ssl/)->plan(4);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nworker_processes 1;  # NOTE: The default value of Tengine worker_processes directive is `worker_processes auto;`.\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:9050 ssl;\n        server_name  server1.com;\n\n        enable_ntls  on;\n        ssl_sign_certificate        server1_sign.crt;\n        ssl_sign_certificate_key    server1_sign.key;\n        ssl_enc_certificate         server1_enc.crt;\n        ssl_enc_certificate_key     server1_enc.key;\n\n        location / {\n            return 200 $server_name;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:9050;\n        server_name  server2.com;\n\n        enable_ntls  on;\n        ssl_sign_certificate        server2_sign.crt;\n        ssl_sign_certificate_key    server2_sign.key;\n        ssl_enc_certificate         server2_enc.crt;\n        ssl_enc_certificate_key     server2_enc.key;\n\n        location / {\n            return 200 $server_name;\n        }\n    }\n}\n\nEOF\n\nmake_sm2_end_certs($t, \"server1\");\nmake_sm2_end_certs($t, \"server2\");\n\n$t->run();\n\nmy $d = $t->testdir();\n\nmy $ret1 = `/bin/echo Q | $openssl s_client -connect localhost:9050 -noservername -quiet -no_ign_eof -enable_ntls -ntls 2>&1`;\nmy $ret2 = `/bin/echo Q | $openssl s_client -connect localhost:9050 -servername server2.com -quiet -no_ign_eof -enable_ntls -ntls 2>&1`;\nmy $ret3 = `/bin/echo -e \"GET / HTTP/1.0\\r\\nHost: server2.com\\r\\n\\r\\n\" | $openssl s_client -connect localhost:9050 -servername server2.com -quiet -ign_eof -enable_ntls -ntls 2>&1`;\nmy $ret4 = `/bin/echo -e \"GET / HTTP/1.0\\r\\nHost: server2.com\\r\\n\\r\\n\" | $openssl s_client -connect localhost:9050 -servername server2.org -quiet -ign_eof -enable_ntls -ntls 2>&1`;\n\nlike($ret1, qr/CN = server1_sign$/m, 'default cert');\nlike($ret2, qr/CN = server2_sign$/m, 'sni cert');\nlike($ret3, qr/^server2.com$/m,\n    'host exists, sni exists, and host is equal sni');\nlike($ret4, qr/^server2.com$/m, 'host exists, sni not found');\n\n$t->stop();\n"
  },
  {
    "path": "modules/ngx_tongsuo_ntls/t/ntls_sni_sessions.t",
    "content": "#!/usr/bin/perl\n\n# Copyright (C) Chenglong Zhang (K1)\n# Copyright (C) 2021 Alibaba Group Holding Limited\n\n###############################################################################\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse lib \".\";\nuse CA qw/ make_sm2_end_certs /;\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $openssl = $ENV{'TEST_OPENSSL_BINARY'} || \"/opt/tongsuo/bin/openssl\";\n\nmy $t = Test::Nginx->new()->has(qw/http http_ssl/)->plan(6);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nworker_processes 1;  # NOTE: The default value of Tengine worker_processes directive is `worker_processes auto;`.\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    enable_ntls  on;\n    ssl_sign_certificate        server_sign.crt;\n    ssl_sign_certificate_key    server_sign.key;\n    ssl_enc_certificate         server_enc.crt;\n    ssl_enc_certificate_key     server_enc.key;\n\n    server {\n        listen       127.0.0.1:9040 ssl;\n        server_name  default;\n\n        ssl_session_tickets off;\n        ssl_session_cache shared:cache1:1m;\n\n        location / {\n            return 200 $ssl_server_name:$ssl_session_reused;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:9040;\n        server_name  nocache;\n\n        ssl_session_tickets off;\n        ssl_session_cache shared:cache2:1m;\n\n        location / {\n            return 200 $ssl_server_name:$ssl_session_reused;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:9041 ssl;\n        server_name  default;\n\n        ssl_session_ticket_key ticket1.key;\n\n        location / {\n            return 200 $ssl_server_name:$ssl_session_reused;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:9041;\n        server_name  tickets;\n\n        ssl_session_ticket_key ticket2.key;\n\n        location / {\n            return 200 $ssl_server_name:$ssl_session_reused;\n        }\n    }\n}\n\nEOF\n\nmake_sm2_end_certs($t, \"server\");\n\n$t->write_file('ticket1.key', '1' x 48);\n$t->write_file('ticket2.key', '2' x 48);\n\n$t->run();\n\nmy $d = $t->testdir();\n\nmy $host = \"default\";\nmy $ret1 = `/bin/echo -e \"GET / HTTP/1.0\\r\\nHost: $host\\r\\n\\r\\n\" | $openssl s_client -connect localhost:9040 -servername $host -quiet -sess_out $d/1.sess -enable_ntls -ntls 2>&1`;\nmy $ret2 = `/bin/echo -e \"GET / HTTP/1.0\\r\\nHost: $host\\r\\n\\r\\n\" | $openssl s_client -connect localhost:9040 -servername $host -quiet -sess_in $d/1.sess -enable_ntls -ntls 2>&1`;\n$host = \"nocache\";\nmy $ret3 = `/bin/echo -e \"GET / HTTP/1.0\\r\\nHost: $host\\r\\n\\r\\n\" | $openssl s_client -connect localhost:9040 -servername $host -quiet -sess_out $d/3.sess -enable_ntls -ntls 2>&1`;\nmy $ret4 = `/bin/echo -e \"GET / HTTP/1.0\\r\\nHost: $host\\r\\n\\r\\n\" | $openssl s_client -connect localhost:9040 -servername $host -quiet -sess_in $d/3.sess -enable_ntls -ntls 2>&1`;\n$host = \"tickets\";\nmy $ret5 = `/bin/echo -e \"GET / HTTP/1.0\\r\\nHost: $host\\r\\n\\r\\n\" | $openssl s_client -connect localhost:9041 -servername $host -quiet -sess_out $d/5.sess -enable_ntls -ntls 2>&1`;\nmy $ret6 = `/bin/echo -e \"GET / HTTP/1.0\\r\\nHost: $host\\r\\n\\r\\n\" | $openssl s_client -connect localhost:9041 -servername $host -quiet -sess_in $d/5.sess -enable_ntls -ntls 2>&1`;\n\nlike($ret1, qr/^default:\\.$/m, 'default server');\nlike($ret2, qr/^default:r$/m, 'default server reused');\nlike($ret3, qr/^nocache:\\.$/m, 'without cache');\nlike($ret4, qr/^nocache:r$/m, 'without cache reused');\nlike($ret5, qr/^tickets:\\.$/m, 'tickets');\nlike($ret6, qr/^tickets:r$/m, 'tickets reused');\n\n$t->stop();\n"
  },
  {
    "path": "modules/ngx_tongsuo_ntls/t/ntls_variables.t",
    "content": "#!/usr/bin/perl\n\n# Copyright (C) Chenglong Zhang (K1)\n# Copyright (C) 2021 Alibaba Group Holding Limited\n\n###############################################################################\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse lib \".\";\nuse CA qw/ make_sm2_ca_subca_end_certs /;\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $openssl = $ENV{'TEST_OPENSSL_BINARY'} || \"/opt/tongsuo/bin/openssl\";\n\nmy $t = Test::Nginx->new()->has(qw/http http_ssl/)->plan(9);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nworker_processes 1;  # NOTE: The default value of Tengine worker_processes directive is `worker_processes auto;`.\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:9060 ssl;\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        enable_ntls  on;\n        ssl_sign_certificate        server_sign.crt;\n        ssl_sign_certificate_key    server_sign.key;\n        ssl_enc_certificate         server_enc.crt;\n        ssl_enc_certificate_key     server_enc.key;\n\n        ssl_session_tickets off;\n        ssl_session_cache shared:SSL:1m;\n        ssl_verify_client optional_no_ca;\n\n        location /reuse {\n            return 200 \"body $ssl_session_reused\";\n        }\n        location /id {\n            return 200 \"body $ssl_session_id\";\n        }\n        location /cipher {\n            return 200 \"body $ssl_cipher\";\n        }\n\n        location /ciphers {\n            return 200 \"body $ssl_ciphers\";\n        }\n\n        location /client_verify {\n            return 200 \"body $ssl_client_verify\";\n        }\n\n        location /protocol {\n            return 200 \"body $ssl_protocol\";\n        }\n\n        location /issuer {\n            return 200 \"body $ssl_client_i_dn:$ssl_client_i_dn_legacy\";\n        }\n        location /subject {\n            return 200 \"body $ssl_client_s_dn:$ssl_client_s_dn_legacy\";\n        }\n        location /time {\n            return 200 \"body $ssl_client_v_start!$ssl_client_v_end!$ssl_client_v_remain\";\n        }\n\n        location /body {\n            add_header X-Body $request_body always;\n            proxy_pass http://127.0.0.1:8080/;\n        }\n    }\n}\n\nEOF\n\nmy $d = $t->testdir();\n\nmake_sm2_ca_subca_end_certs($t, \"client\");\nmake_sm2_ca_subca_end_certs($t, \"server\");\n\n$t->run();\n\nmy $ret1 = `/bin/echo -e \"GET /id HTTP/1.0\\r\\n\\r\\n\" | $openssl s_client -connect localhost:9060 -quiet -enable_ntls -ntls 2>&1`;\nmy $ret2 = http_get('/id');\nmy $ret3 = `/bin/echo -e \"GET /cipher HTTP/1.0\\r\\n\\r\\n\" | $openssl s_client -connect localhost:9060 -quiet -enable_ntls -ntls 2>&1`;\nmy $ret4 = `/bin/echo -e \"GET /ciphers HTTP/1.0\\r\\n\\r\\n\" | $openssl s_client -connect localhost:9060 -quiet -enable_ntls -ntls 2>&1`;\nmy $ret5 = `/bin/echo -e \"GET /client_verify HTTP/1.0\\r\\n\\r\\n\" | $openssl s_client -connect localhost:9060 -quiet -enable_ntls -ntls 2>&1`;\nmy $ret6 = `/bin/echo -e \"GET /protocol HTTP/1.0\\r\\n\\r\\n\" | $openssl s_client -connect localhost:9060 -quiet -enable_ntls -ntls 2>&1`;\nmy $ret7 = `/bin/echo -e \"GET /issuer HTTP/1.0\\r\\n\\r\\n\" | $openssl s_client -connect localhost:9060 -quiet -enc_cert $d/client_enc.crt -enc_key $d/client_enc.key -sign_cert $d/client_sign.crt -sign_key $d/client_sign.key -enable_ntls -ntls 2>&1`;\nmy $ret8 = `/bin/echo -e \"GET /subject HTTP/1.0\\r\\n\\r\\n\" | $openssl s_client -connect localhost:9060 -quiet -enc_cert $d/client_enc.crt -enc_key $d/client_enc.key -sign_cert $d/client_sign.crt -sign_key $d/client_sign.key -enable_ntls -ntls 2>&1`;\nmy $ret9 = `/bin/echo -e \"GET /time HTTP/1.0\\r\\n\\r\\n\" | $openssl s_client -connect localhost:9060 -quiet -enc_cert $d/client_enc.crt -enc_key $d/client_enc.key -sign_cert $d/client_sign.crt -sign_key $d/client_sign.key -enable_ntls -ntls 2>&1`;\n\nlike($ret1, qr/^body \\w{64}$/m, 'session id');\nunlike($ret2, qr/body \\w/, 'session id no ssl');\nlike($ret3, qr/^body [\\w-]+$/m, 'cipher');\nlike($ret4, qr/^body [:\\w-]+$/m, 'ciphers');\nlike($ret5, qr/^body NONE$/m, 'client verify');\nlike($ret6, qr/^body (NTLS)v(\\d|\\.)+$/m, 'protocol');\nlike($ret7, qr!^body CN=client_sub_ca:/CN=client_sub_ca!m, 'issuer');\nlike($ret8, qr!^body CN=client_sign:/CN=client_sign!m, 'subject');\nlike($ret9, qr/^body [:\\s\\w]+![:\\s\\w]+![23]$/m, 'time');\n\n$t->stop();\n"
  },
  {
    "path": "modules/ngx_tongsuo_ntls/t/ntls_verify_client.t",
    "content": "#!/usr/bin/perl\n\n# Copyright (C) Chenglong Zhang (K1)\n# Copyright (C) 2021 Alibaba Group Holding Limited\n\n###############################################################################\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse lib \".\";\nuse CA qw/ make_sm2_end_certs /;\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $openssl = $ENV{'TEST_OPENSSL_BINARY'} || \"/opt/tongsuo/bin/openssl\";\n\nmy $t = Test::Nginx->new()->has(qw/http http_ssl/)->plan(9);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nworker_processes 1;  # NOTE: The default value of Tengine worker_processes directive is `worker_processes auto;`.\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    ssl_session_cache shared:SSL:1m;\n    ssl_session_tickets off;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        enable_ntls  on;\n        ssl_sign_certificate        server_sign.crt;\n        ssl_sign_certificate_key    server_sign.key;\n        ssl_enc_certificate         server_enc.crt;\n        ssl_enc_certificate_key     server_enc.key;\n\n        ssl_verify_client on;\n        ssl_client_certificate      client1_sign_enc.crt;\n\n        location / {\n            return 200 \"ok\";\n        }\n    }\n\n    server {\n        listen       127.0.0.1:9071 ssl;\n        server_name  on;\n\n        enable_ntls  on;\n        ssl_sign_certificate        server_sign.crt;\n        ssl_sign_certificate_key    server_sign.key;\n        ssl_enc_certificate         server_enc.crt;\n        ssl_enc_certificate_key     server_enc.key;\n\n        ssl_verify_client on;\n        ssl_client_certificate      client2_sign_enc.crt;\n\n        location / {\n            return 200 \"$ssl_protocol:$ssl_client_verify\";\n        }\n    }\n\n    server {\n        listen       127.0.0.1:9071 ssl;\n        server_name  optional;\n\n        enable_ntls  on;\n        ssl_sign_certificate        server_sign.crt;\n        ssl_sign_certificate_key    server_sign.key;\n        ssl_enc_certificate         server_enc.crt;\n        ssl_enc_certificate_key     server_enc.key;\n\n        ssl_verify_client optional;\n        ssl_client_certificate      client2_sign_enc.crt;\n        ssl_trusted_certificate     client3_sign_enc.crt;\n\n        location / {\n            return 200 \"$ssl_protocol:$ssl_client_verify\";\n        }\n    }\n\n    server {\n        listen       127.0.0.1:9071 ssl;\n        server_name  optional_no_ca;\n\n        enable_ntls  on;\n        ssl_sign_certificate        server_sign.crt;\n        ssl_sign_certificate_key    server_sign.key;\n        ssl_enc_certificate         server_enc.crt;\n        ssl_enc_certificate_key     server_enc.key;\n\n        ssl_verify_client optional_no_ca;\n        ssl_client_certificate client2_sign_enc.crt;\n\n        location / {\n            return 200 \"$ssl_protocol:$ssl_client_verify\";\n        }\n    }\n\n    server {\n        listen       127.0.0.1:9071;\n        server_name  no_context;\n\n        ssl_verify_client on;\n    }\n}\n\nEOF\n\nmake_sm2_end_certs($t, \"client1\");\nmake_sm2_end_certs($t, \"client2\");\nmake_sm2_end_certs($t, \"client3\");\nmake_sm2_end_certs($t, \"server\");\n\n$t->run();\n\nmy $d = $t->testdir();\n\nlike(http_get('/t'), qr/ok/, 'plain connection');\n\nmy $sni = \"on\";\nmy $ret1 = `/bin/echo -e \"GET / HTTP/1.0\\r\\nHost: $sni\\r\\n\" | $openssl s_client -connect localhost:9071 -servername $sni -quiet -enable_ntls -ntls 2>&1`;\n$sni = \"no_context\";\nmy $ret2 = `/bin/echo -e \"GET / HTTP/1.0\\r\\nHost: $sni\\r\\n\" | $openssl s_client -connect localhost:9071 -servername $sni -quiet -enable_ntls -ntls 2>&1`;\n$sni = \"optional\";\nmy $ret3 = `/bin/echo -e \"GET / HTTP/1.0\\r\\nHost: $sni\\r\\n\" | $openssl s_client -connect localhost:9071 -servername $sni -quiet -enable_ntls -ntls 2>&1`;\nmy $ret4 = `/bin/echo -e \"GET / HTTP/1.0\\r\\nHost: $sni\\r\\n\" | $openssl s_client -connect localhost:9071 -servername $sni -quiet -sign_cert $d/client1_sign.crt -sign_key $d/client1_sign.key -enc_cert $d/client1_enc.crt -enc_key $d/client1_enc.key  -enable_ntls -ntls 2>&1`;\n$sni = \"optional_no_ca\";\nmy $ret5 = `/bin/echo -e \"GET / HTTP/1.0\\r\\nHost: $sni\\r\\n\" | $openssl s_client -connect localhost:9071 -servername $sni -quiet -sign_cert $d/client1_sign.crt -sign_key $d/client1_sign.key -enc_cert $d/client1_enc.crt -enc_key $d/client1_enc.key  -enable_ntls -ntls 2>&1`;\n$sni = \"localhost\";\nmy $ret6 = `/bin/echo -e \"GET / HTTP/1.0\\r\\nHost: $sni\\r\\n\" | $openssl s_client -connect localhost:9071 -servername $sni -quiet -sign_cert $d/client2_sign.crt -sign_key $d/client2_sign.key -enc_cert $d/client2_enc.crt -enc_key $d/client2_enc.key  -enable_ntls -ntls 2>&1`;\n$sni = \"optional\";\nmy $ret7 = `/bin/echo -e \"GET / HTTP/1.0\\r\\nHost: $sni\\r\\n\" | $openssl s_client -connect localhost:9071 -servername $sni -quiet -sign_cert $d/client2_sign.crt -sign_key $d/client2_sign.key -enc_cert $d/client2_enc.crt -enc_key $d/client2_enc.key  -enable_ntls -ntls 2>&1`;\nmy $ret8 = `/bin/echo -e \"GET / HTTP/1.0\\r\\nHost: $sni\\r\\n\" | $openssl s_client -connect localhost:9071 -servername $sni -quiet -sign_cert $d/client3_sign.crt -sign_key $d/client3_sign.key -enc_cert $d/client3_enc.crt -enc_key $d/client3_enc.key  -enable_ntls -ntls 2>&1`;\n\nlike($ret1, qr/400 Bad Request/, 'no client cert');\nlike($ret2, qr/400 Bad Request/, 'no server cert');\nlike($ret3, qr/^NTLSv(\\d|\\.)+:NONE$/m, 'no optional cert');\nlike($ret4, qr/400 Bad Request/, 'bad optional cert');\nlike($ret5, qr/^NTLSv(\\d|\\.)+:FAILED/m, 'bad optional_no_ca cert');\nlike($ret6, qr/^NTLSv(\\d|\\.)+:SUCCESS$/m, 'good cert');\nlike($ret7, qr/^NTLSv(\\d|\\.)+:SUCCESS$/m, 'good cert optional');\nlike($ret8, qr/^NTLSv(\\d|\\.)+:SUCCESS$/m, 'good cert trusted');\n\n$t->stop();\n"
  },
  {
    "path": "modules/ngx_tongsuo_ntls/t/ntls_verify_depth.t",
    "content": "#!/usr/bin/perl\n\n# Copyright (C) Chenglong Zhang (K1)\n# Copyright (C) 2021 Alibaba Group Holding Limited\n\n###############################################################################\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse lib \".\";\nuse CA qw/ make_sm2_end_certs make_sm2_ca_subca_end_certs /;\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $openssl = $ENV{'TEST_OPENSSL_BINARY'} || \"/opt/tongsuo/bin/openssl\";\n\nmy $t = Test::Nginx->new()->has(qw/http http_ssl/)->plan(2);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nworker_processes 1;  # NOTE: The default value of Tengine worker_processes directive is `worker_processes auto;`.\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    enable_ntls on;\n    ssl_sign_certificate        server_sign.crt;\n    ssl_sign_certificate_key    server_sign.key;\n    ssl_enc_certificate         server_enc.crt;\n    ssl_enc_certificate_key     server_enc.key;\n\n    ssl_verify_client on;\n\n    server {\n        listen       127.0.0.1:9080 ssl;\n        server_name  localhost;\n\n        ssl_verify_depth 0;\n        ssl_client_certificate client1_sign_enc.crt;\n\n        location / {\n            return 200 \"$ssl_protocol:$ssl_client_verify\";\n        }\n    }\n\n    server {\n        listen       127.0.0.1:9081 ssl;\n        server_name  localhost;\n\n        ssl_verify_depth 0;\n        ssl_client_certificate client2_ca_chain.crt;\n    }\n}\n\nEOF\n\nmake_sm2_end_certs($t, \"client1\");\nmake_sm2_ca_subca_end_certs($t, \"client2\");\nmake_sm2_ca_subca_end_certs($t, \"server\");\n\n$t->run();\n\nmy $d = $t->testdir();\n\nmy $ret1 = `/bin/echo -e \"GET /t HTTP/1.0\\r\\n\\r\\n\" | $openssl s_client -connect localhost:9080 -sign_cert $d/client1_sign.crt -sign_key $d/client1_sign.key -enc_cert $d/client1_enc.crt -enc_key $d/client1_enc.key -quiet -enable_ntls -ntls 2>&1`;\nmy $ret2 = `/bin/echo -e \"GET /t HTTP/1.0\\r\\n\\r\\n\" | $openssl s_client -connect localhost:9081 -sign_cert $d/client2_sign.crt -sign_key $d/client2_sign.key -enc_cert $d/client2_enc.crt -enc_key $d/client2_enc.key -quiet -enable_ntls -ntls 2>&1`;\n\nlike($ret1, qr/^NTLSv(\\d|\\.)+:SUCCESS$/m, 'NTLS verify depth');\nlike($ret2, qr/400 Bad Request/, 'NTLS verify depth limited');\n\n$t->stop();\n"
  },
  {
    "path": "modules/ngx_tongsuo_ntls/t/stream_ntls.t",
    "content": "#!/usr/bin/perl\n\n# Copyright (C) Chenglong Zhang (K1)\n# Copyright (C) 2021 Alibaba Group Holding Limited\n\n###############################################################################\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse lib '.';\nuse CA qw/ make_sm2_ca_subca_end_certs make_rsa_end_cert make_ec_end_cert /;\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $openssl = $ENV{'TEST_OPENSSL_BINARY'} || \"/opt/tongsuo/bin/openssl\";\n\nmy $t = Test::Nginx->new()->has(qw/http http_ssl/)->plan(8);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\nworker_processes 1;\n\nevents {\n}\n\nstream {\n    server {\n        listen       127.0.0.1:9140 ssl;\n\n        ssl_certificate_key rsa.key;\n        ssl_certificate     rsa.crt;\n\n        ssl_certificate_key ec.key;\n        ssl_certificate     ec.crt;\n\n        return \"body $ssl_protocol\";\n    }\n\n    server {\n        listen       127.0.0.1:9141 ssl;\n\n        enable_ntls  on;\n        ssl_sign_certificate        server_sign.crt;\n        ssl_sign_certificate_key    server_sign.key;\n        ssl_enc_certificate         server_enc.crt;\n        ssl_enc_certificate_key     server_enc.key;\n\n        ssl_verify_client           optional_no_ca;\n\n        return \"body $ssl_protocol:$ssl_cipher\";\n    }\n\n    server {\n        listen       127.0.0.1:9142 ssl;\n\n        ssl_certificate_key rsa.key;\n        ssl_certificate     rsa.crt;\n\n        ssl_certificate_key ec.key;\n        ssl_certificate     ec.crt;\n\n        enable_ntls  on;\n        ssl_sign_certificate        server_sign.crt;\n        ssl_sign_certificate_key    server_sign.key;\n        ssl_enc_certificate         server_enc.crt;\n        ssl_enc_certificate_key     server_enc.key;\n\n        return \"body $ssl_protocol\";\n    }\n}\n\nEOF\n\nmy $d = $t->testdir();\n\nmake_rsa_end_cert($t);\nmake_ec_end_cert($t);\n\nmake_sm2_ca_subca_end_certs($t, \"client\");\nmake_sm2_ca_subca_end_certs($t, \"server\");\n\n$t->run();\n\nmy $ret1 = `$openssl s_client -connect localhost:9140 -cipher aRSA -quiet -ign_eof 2>&1`;\nmy $ret2 = `$openssl s_client -connect localhost:9140 -cipher aECDSA -quiet -ign_eof 2>&1`;\nmy $ret3 = `$openssl s_client -connect localhost:9141 -cipher ECC-SM2-SM4-CBC-SM3 -quiet -ign_eof -enable_ntls -ntls 2>&1`;\nmy $ret4 = `$openssl s_client -connect localhost:9141 -cipher ECC-SM2-SM4-GCM-SM3 -quiet -ign_eof -enable_ntls -ntls 2>&1`;\nmy $ret5 = `$openssl s_client -connect localhost:9141 -cipher ECDHE-SM2-SM4-CBC-SM3 -quiet -ign_eof  -enc_cert $d/client_enc.crt -enc_key $d/client_enc.key -sign_cert $d/client_sign.crt -sign_key $d/client_sign.key -enable_ntls -ntls 2>&1`;\nmy $ret6 = `$openssl s_client -connect localhost:9141 -cipher ECDHE-SM2-SM4-GCM-SM3 -quiet -ign_eof -enc_cert $d/client_enc.crt -enc_key $d/client_enc.key -sign_cert $d/client_sign.crt -sign_key $d/client_sign.key -enable_ntls -ntls 2>&1`;\nmy $ret7 = `$openssl s_client -connect localhost:9142 -cipher aRSA -quiet -ign_eof 2>&1`;\nmy $ret8 = `$openssl s_client -connect localhost:9142 -cipher aECDSA -quiet -ign_eof 2>&1`;\n\nlike($ret1, qr/^body TLSv(\\d|\\.)+/m, 'disable NTLS, TLS handshake success with aRSA');\nlike($ret2, qr/^body TLSv(\\d|\\.)+$/m, 'disable NTLS, TLS handshake success with aECDSA');\nlike($ret3, qr/^body NTLSv(\\d|\\.)+:ECC-SM2-SM4-CBC-SM3$/m, 'NTLS ECC-SM2-SM4-CBC-SM3 handshake success');\nlike($ret4, qr/^body NTLSv(\\d|\\.)+:ECC-SM2-SM4-GCM-SM3$/m, 'NTLS ECC-SM2-SM4-GCM-SM3 handshake success');\nlike($ret5, qr/^body NTLSv(\\d|\\.)+:ECDHE-SM2-SM4-CBC-SM3$/m, 'NTLS ECDHE-SM2-SM4-CBC-SM3 handshake success');\nlike($ret6, qr/^body NTLSv(\\d|\\.)+:ECDHE-SM2-SM4-GCM-SM3$/m, 'NTLS ECDHE-SM2-SM4-GCM-SM3 handshake success');\nlike($ret7, qr/^body TLSv(\\d|\\.)+$/m, 'enable NTLS, TLS handshake success with aRSA');\nlike($ret8, qr/^body TLSv(\\d|\\.)+$/m, 'enable NTLS, TLS handshake success with aECDSA');\n\n$t->stop();\n"
  },
  {
    "path": "modules/ngx_tongsuo_ntls/t/stream_ntls_proxy.t",
    "content": "#!/usr/bin/perl\n\n# Copyright (C) Jin Jiu (wa5i)\n# Copyright (C) 2022 Alibaba Group Holding Limited\n\n###############################################################################\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse lib \".\";\nuse CA qw/ make_sm2_ca_subca_end_certs make_rsa_end_cert make_ec_end_cert /;\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $openssl = $ENV{'TEST_OPENSSL_BINARY'} || \"/opt/tongsuo/bin/openssl\";\n\nmy $t = Test::Nginx->new()->has(qw/stream stream_ssl/)->plan(12);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nworker_processes 1;  # NOTE: The default value of Tengine worker_processes directive is `worker_processes auto;`.\n\nstream {\n\n    server {\n        listen       127.0.0.1:9102 ssl;\n        server_name  localhost;\n\n        ssl_certificate_key rsa.key;\n        ssl_certificate     rsa.crt;\n\n        ssl_certificate_key ec.key;\n        ssl_certificate     ec.crt;\n\n        ssl_session_cache off;\n        ssl_session_tickets off;\n\n        enable_ntls  on;\n        ssl_sign_certificate        server_sign.crt;\n        ssl_sign_certificate_key    server_sign.key;\n        ssl_enc_certificate         server_enc.crt;\n        ssl_enc_certificate_key     server_enc.key;\n\n        ssl_verify_client           optional_no_ca;\n        ssl_trusted_certificate     client_ca_chain.crt;\n\n        return \"ssl_protocol=$ssl_protocol, ssl_cipher=$ssl_cipher\";\n    }\n\n    server {\n        listen       127.0.0.1:9103 ssl;\n        server_name  localhost;\n\n        ssl_certificate_key rsa.key;\n        ssl_certificate     rsa.crt;\n\n        ssl_certificate_key ec.key;\n        ssl_certificate     ec.crt;\n\n        enable_ntls  on;\n        ssl_sign_certificate        server_sign.crt;\n        ssl_sign_certificate_key    server_sign.key;\n        ssl_enc_certificate         server_enc.crt;\n        ssl_enc_certificate_key     server_enc.key;\n\n        ssl_verify_client           optional_no_ca;\n        ssl_trusted_certificate     client_ca_chain.crt;\n\n        proxy_ssl on;\n        proxy_enable_ntls on;\n        proxy_ssl_sign_certificate        client_sign.crt;\n        proxy_ssl_sign_certificate_key    client_sign.key;\n        proxy_ssl_enc_certificate         client_enc.crt;\n        proxy_ssl_enc_certificate_key     client_enc.key;\n        proxy_ssl_trusted_certificate     server_ca_chain.crt;\n        proxy_ssl_ciphers \"ECC-SM2-WITH-SM4-SM3:ECDHE-SM2-WITH-SM4-SM3:RSA\";\n\n        proxy_pass localhost:9102;\n    }\n\n    server {\n        listen       127.0.0.1:9104 ssl;\n        server_name  localhost;\n\n        ssl_certificate_key rsa.key;\n        ssl_certificate     rsa.crt;\n\n        ssl_certificate_key ec.key;\n        ssl_certificate     ec.crt;\n\n        enable_ntls  on;\n        ssl_sign_certificate        server_sign.crt;\n        ssl_sign_certificate_key    server_sign.key;\n        ssl_enc_certificate         server_enc.crt;\n        ssl_enc_certificate_key     server_enc.key;\n\n        ssl_verify_client           optional_no_ca;\n        ssl_trusted_certificate     client_ca_chain.crt;\n\n        proxy_ssl on;\n        proxy_enable_ntls on;\n        proxy_ssl_sign_certificate        client_sign.crt;\n        proxy_ssl_sign_certificate_key    client_sign.key;\n        proxy_ssl_enc_certificate         client_enc.crt;\n        proxy_ssl_enc_certificate_key     client_enc.key;\n        proxy_ssl_trusted_certificate     server_ca_chain.crt;\n        proxy_ssl_ciphers \"ECDHE-SM2-SM4-CBC-SM3\";\n\n        proxy_pass localhost:9102;\n    }\n\n    server {\n        listen       127.0.0.1:9105 ssl;\n        server_name  localhost;\n\n        ssl_certificate_key rsa.key;\n        ssl_certificate     rsa.crt;\n\n        ssl_certificate_key ec.key;\n        ssl_certificate     ec.crt;\n\n        enable_ntls  on;\n        ssl_sign_certificate        server_sign.crt;\n        ssl_sign_certificate_key    server_sign.key;\n        ssl_enc_certificate         server_enc.crt;\n        ssl_enc_certificate_key     server_enc.key;\n\n        ssl_verify_client           optional_no_ca;\n        ssl_trusted_certificate     client_ca_chain.crt;\n\n        proxy_ssl on;\n        proxy_enable_ntls off;\n        proxy_ssl_sign_certificate        client_sign.crt;\n        proxy_ssl_sign_certificate_key    client_sign.key;\n        proxy_ssl_enc_certificate         client_enc.crt;\n        proxy_ssl_enc_certificate_key     client_enc.key;\n        proxy_ssl_trusted_certificate     server_ca_chain.crt;\n        proxy_ssl_ciphers \"ECC-SM2-SM4-CBC-SM3:ECDHE-SM2-WITH-SM4-SM3:AES128-GCM-SHA256\";\n\n        proxy_pass localhost:9102;\n    }\n\n\n    server {\n        listen       127.0.0.1:9106 ssl;\n        server_name  localhost;\n\n        ssl_certificate_key rsa.key;\n        ssl_certificate     rsa.crt;\n\n        ssl_certificate_key ec.key;\n        ssl_certificate     ec.crt;\n\n        enable_ntls  on;\n        ssl_sign_certificate        server_sign.crt;\n        ssl_sign_certificate_key    server_sign.key;\n        ssl_enc_certificate         server_enc.crt;\n        ssl_enc_certificate_key     server_enc.key;\n\n        ssl_verify_client           optional_no_ca;\n        ssl_trusted_certificate     client_ca_chain.crt;\n\n        proxy_ssl on;\n        proxy_ssl_ciphers \"AES256-GCM-SHA384\";\n\n        proxy_pass localhost:9102;\n    }\n}\n\nEOF\n\nmy $d = $t->testdir();\n\nmake_rsa_end_cert($t);\nmake_ec_end_cert($t);\n\nmake_sm2_ca_subca_end_certs($t, \"client\");\nmake_sm2_ca_subca_end_certs($t, \"server\");\n\n$t->run();\n\nmy $ret1 = `$openssl s_client -connect localhost:9103 -cipher AES128-GCM-SHA256 -quiet -ign_eof 2>&1`;\nmy $ret2 = `$openssl s_client -connect localhost:9103 -cipher ECC-SM2-SM4-CBC-SM3 -quiet -ign_eof -enable_ntls -ntls 2>&1`;\nmy $ret3 = `$openssl s_client -connect localhost:9103 -cipher ECDHE-SM2-SM4-CBC-SM3 -quiet -ign_eof -enc_cert $d/client_enc.crt -enc_key $d/client_enc.key -sign_cert $d/client_sign.crt -sign_key $d/client_sign.key -enable_ntls -ntls 2>&1`;\nmy $ret4 = `$openssl s_client -connect localhost:9104 -cipher AES128-GCM-SHA256 -quiet -ign_eof 2>&1`;\nmy $ret5 = `$openssl s_client -connect localhost:9104 -cipher ECC-SM2-SM4-CBC-SM3 -quiet -ign_eof -enable_ntls -ntls 2>&1`;\nmy $ret6 = `$openssl s_client -connect localhost:9104 -cipher ECDHE-SM2-SM4-GCM-SM3 -quiet -ign_eof -enc_cert $d/client_enc.crt -enc_key $d/client_enc.key -sign_cert $d/client_sign.crt -sign_key $d/client_sign.key -enable_ntls -ntls 2>&1`;\nmy $ret7 = `$openssl s_client -connect localhost:9105 -cipher AES128-GCM-SHA256 -quiet -ign_eof 2>&1`;\nmy $ret8 = `$openssl s_client -connect localhost:9105 -cipher ECC-SM2-SM4-CBC-SM3 -quiet -ign_eof -enable_ntls -ntls 2>&1`;\nmy $ret9 = `$openssl s_client -connect localhost:9105 -cipher ECDHE-SM2-SM4-GCM-SM3 -quiet -ign_eof -enc_cert $d/client_enc.crt -enc_key $d/client_enc.key -sign_cert $d/client_sign.crt -sign_key $d/client_sign.key -enable_ntls -ntls 2>&1`;\nmy $ret10 = `$openssl s_client -connect localhost:9106 -cipher AES128-GCM-SHA256 -quiet -ign_eof 2>&1`;\nmy $ret11 = `$openssl s_client -connect localhost:9106 -cipher ECC-SM2-SM4-CBC-SM3 -quiet -ign_eof -enable_ntls -ntls 2>&1`;\nmy $ret12 = `$openssl s_client -connect localhost:9106 -cipher ECDHE-SM2-SM4-GCM-SM3 -quiet -ign_eof -enc_cert $d/client_enc.crt -enc_key $d/client_enc.key -sign_cert $d/client_sign.crt -sign_key $d/client_sign.key -enable_ntls -ntls 2>&1`;\n\nlike($ret1, qr/^ssl_protocol=NTLSv1\\.1, ssl_cipher=ECC-SM2-SM4-CBC-SM3/m, 'client -----(TLSv1.2 AES128-GCM-SHA256)-----> server(proxy_enable_ntls=on) -----(NTLSv1.1 ECC-SM2-SM4-CBC-SM3)-----> origin');\nlike($ret2, qr/^ssl_protocol=NTLSv1\\.1, ssl_cipher=ECC-SM2-SM4-CBC-SM3/m, 'client -----(TLSv1.2 ECC-SM2-SM4-CBC-SM3)-----> server(proxy_enable_ntls=on) -----(NTLSv1.1 ECC-SM2-SM4-CBC-SM3)-----> origin');\nlike($ret3, qr/^ssl_protocol=NTLSv1\\.1, ssl_cipher=ECC-SM2-SM4-CBC-SM3/m, 'client -----(TLSv1.2 ECDHE-SM2-SM4-CBC-SM3)-----> server(proxy_enable_ntls=on) -----(NTLSv1.1 ECC-SM2-SM4-CBC-SM3)-----> origin');\n\nlike($ret4, qr/^ssl_protocol=NTLSv1\\.1, ssl_cipher=ECDHE-SM2-SM4-CBC-SM3/m, 'client -----(TLSv1.2 AES128-GCM-SHA256)-----> server(proxy_enable_ntls=on) -----(NTLSv1.1 ECDHE-SM2-SM4-CBC-SM3)-----> origin');\nlike($ret5, qr/^ssl_protocol=NTLSv1\\.1, ssl_cipher=ECDHE-SM2-SM4-CBC-SM3/m, 'client -----(TLSv1.2 ECC-SM2-SM4-CBC-SM3)-----> server(proxy_enable_ntls=on) -----(NTLSv1.1 ECDHE-SM2-SM4-CBC-SM3)-----> origin');\nlike($ret6, qr/^ssl_protocol=NTLSv1\\.1, ssl_cipher=ECDHE-SM2-SM4-CBC-SM3/m, 'client -----(TLSv1.2 ECDHE-SM2-SM4-CBC-SM3)-----> server(proxy_enable_ntls=on) -----(NTLSv1.1 ECDHE-SM2-SM4-CBC-SM3)-----> origin');\n\nlike($ret7, qr/^ssl_protocol=TLSv1\\.3, ssl_cipher=TLS_AES_256_GCM_SHA384/m, 'client -----(TLSv1.3 AES128-GCM-SHA256)-----> server(proxy_enable_ntls=off) -----(TLSv1.3 AES128-GCM-SHA256)-----> origin');\nlike($ret8, qr/^ssl_protocol=TLSv1\\.3, ssl_cipher=TLS_AES_256_GCM_SHA384/m, 'client -----(TLSv1.3 ECC-SM2-SM4-CBC-SM3)-----> server(proxy_enable_ntls=off) -----(TLSv1.3 AES128-GCM-SHA256)-----> origin');\nlike($ret9, qr/^ssl_protocol=TLSv1\\.3, ssl_cipher=TLS_AES_256_GCM_SHA384/m, 'client -----(TLSv1.3 ECDHE-SM2-SM4-CBC-SM3)-----> server(proxy_enable_ntls=off) -----(TLSv1.3 AES128-GCM-SHA256)-----> origin');\n\nlike($ret10, qr/^ssl_protocol=TLSv1\\.3, ssl_cipher=TLS_AES_256_GCM_SHA384/m, 'client -----(TLSv1.3 AES128-GCM-SHA256)-----> server(no proxy_enable_ntls) -----(TLSv1.3 AES256-GCM-SHA384)-----> origin');\nlike($ret11, qr/^ssl_protocol=TLSv1\\.3, ssl_cipher=TLS_AES_256_GCM_SHA384/m, 'client -----(TLSv1.3 ECC-SM2-SM4-CBC-SM3)-----> server(no proxy_enable_ntls) -----(TLSv1.3 AES256-GCM-SHA384)-----> origin');\nlike($ret12, qr/^ssl_protocol=TLSv1\\.3, ssl_cipher=TLS_AES_256_GCM_SHA384/m, 'client -----(TLSv1.3 ECDHE-SM2-SM4-CBC-SM3)-----> server(no proxy_enable_ntls) -----(TLSv1.3 AES256-GCM-SHA384)-----> origin');\n\n$t->stop();\n"
  },
  {
    "path": "modules/ngx_tongsuo_ntls/t/stream_ntls_session_cache.t",
    "content": "#!/usr/bin/perl\n\n# Copyright (C) Chenglong Zhang (K1)\n# Copyright (C) 2021 Alibaba Group Holding Limited\n\n# Tests for stream ssl module.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse lib '.';\nuse CA qw/ make_sm2_end_certs /;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $openssl = $ENV{'TEST_OPENSSL_BINARY'} || \"/opt/tongsuo/bin/openssl\";\nmy $t = Test::Nginx->new()->has(qw/stream stream_ssl/);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\nworker_processes 1;\n\nevents {\n}\n\nstream {\n    enable_ntls  on;\n    ssl_sign_certificate        server_sign.crt;\n    ssl_sign_certificate_key    server_sign.key;\n    ssl_enc_certificate         server_enc.crt;\n    ssl_enc_certificate_key     server_enc.key;\n\n    ssl_session_tickets off;\n\n    server {\n        listen 127.0.0.1:9110 ssl;\n\n        ssl_session_cache builtin;\n\n        return \"body $ssl_session_reused\";\n    }\n\n    server {\n        listen 127.0.0.1:9112 ssl;\n\n        ssl_session_cache off;\n\n        return \"body $ssl_session_reused\";\n    }\n\n    server {\n        listen 127.0.0.1:9113 ssl;\n\n        ssl_session_cache builtin:1000;\n\n        return \"body $ssl_session_reused\";\n    }\n\n    server {\n        listen 127.0.0.1:9114 ssl;\n\n        ssl_session_cache shared:SSL:1m;\n\n        return \"body $ssl_session_reused\";\n    }\n}\n\nEOF\n\nmake_sm2_end_certs($t, \"server\");\n\nmy $d = $t->testdir();\n\n$t->run()->plan(8);\n###############################################################################\nmy $ret1 = `$openssl s_client -connect localhost:9110 -quiet -sess_out 1.sess -enable_ntls -ntls 2>&1`;\nmy $ret2 = `$openssl s_client -connect localhost:9110 -quiet -sess_in 1.sess -enable_ntls -ntls 2>&1`;\nmy $ret3 = `$openssl s_client -connect localhost:9112 -quiet -sess_out 3.sess -enable_ntls -ntls 2>&1`;\nmy $ret5 = `$openssl s_client -connect localhost:9113 -quiet -sess_out 5.sess -enable_ntls -ntls 2>&1`;\nmy $ret6 = `$openssl s_client -connect localhost:9113 -quiet -sess_in 5.sess -enable_ntls -ntls 2>&1`;\nmy $ret7 = `$openssl s_client -connect localhost:9114 -quiet -sess_out 7.sess -enable_ntls -ntls 2>&1`;\nmy $ret8 = `$openssl s_client -connect localhost:9114 -quiet -sess_in 7.sess -enable_ntls -ntls 2>&1`;\n\nlike($ret1, qr/^body \\.$/m, 'builtin initial session');\nlike($ret2, qr/^body r$/m, 'builtin session reused');\nlike($ret3, qr/^body .$/m, 'session off initial session');\nisnt(-f \"$d/3.sess\", 1, 'session off no session out');\nlike($ret5, qr/^body \\.$/m, 'builtin size initial session');\nlike($ret6, qr/^body r$/m, 'builtin size session reused');\nlike($ret7, qr/^body \\.$/m, 'shared initial session');\nlike($ret8, qr/^body r$/m, 'shared session reused');\n\n###############################################################################\n"
  },
  {
    "path": "modules/ngx_tongsuo_ntls/t/stream_ntls_variables.t",
    "content": "#!/usr/bin/perl\n\n# Copyright (C) Chenglong Zhang (K1)\n# Copyright (C) 2021 Alibaba Group Holding Limited\n\n# Tests for stream ssl module with variables.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse Socket;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse Test::Nginx::Stream qw/ stream /;\nuse lib '.';\nuse CA qw/ make_sm2_end_certs /;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $openssl = $ENV{'TEST_OPENSSL_BINARY'} || \"/opt/tongsuo/bin/openssl\";\nmy $t = Test::Nginx->new()->has(qw/stream stream_ssl sni stream_return/);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nstream {\n    enable_ntls  on;\n    ssl_sign_certificate        server_sign.crt;\n    ssl_sign_certificate_key    server_sign.key;\n    ssl_enc_certificate         server_enc.crt;\n    ssl_enc_certificate_key     server_enc.key;\n\n    server {\n        listen  127.0.0.1:8080;\n        listen  127.0.0.1:9121 ssl;\n        return  $ssl_session_reused:$ssl_session_id:$ssl_cipher:$ssl_protocol;\n\n        ssl_session_cache builtin;\n    }\n\n    server {\n        listen  127.0.0.1:9122 ssl;\n        return  $ssl_server_name;\n    }\n}\n\nEOF\n\nmake_sm2_end_certs($t, \"server\");\n\nmy $d = $t->testdir();\n\n\n$t->run()->plan(5);\n\n###############################################################################\n\nis(stream('127.0.0.1:' . port(8080))->read(), ':::', 'no ssl');\n\nmy $ret1 = `$openssl s_client -connect localhost:9121 -quiet -sess_out 1.sess -enable_ntls -ntls 2>&1`;\nmy $ret2 = `$openssl s_client -connect localhost:9121 -quiet -sess_in 1.sess -enable_ntls -ntls 2>&1`;\nmy $ret3 = `$openssl s_client -connect localhost:9122 -quiet -servername example.com -enable_ntls -ntls 2>&1`;\nmy $ret4 = `$openssl s_client -connect localhost:9122 -quiet -enable_ntls -ntls 2>/dev/null`;\n\nlike($ret1, qr/^\\.:(\\w{64})?:[\\w-]+:NTLSv(\\d|\\.)+$/m, 'ssl variables');\nlike($ret2, qr/^r:\\w{64}:[\\w-]+:NTLSv(\\d|\\.)+$/m, 'ssl variables - session reused');\nlike($ret3, qr/^example.com$/m, 'ssl server name');\nis($ret4, '', 'ssl server name empty');\n\n###############################################################################\n"
  },
  {
    "path": "modules/ngx_tongsuo_ntls/t/stream_ntls_verify_client.t",
    "content": "#!/usr/bin/perl\n\n# Copyright (C) Chenglong Zhang (K1)\n# Copyright (C) 2021 Alibaba Group Holding Limited\n\n# Tests for stream ssl module, ssl_verify_client.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse Socket;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse Test::Nginx::Stream qw/ stream /;\nuse lib '.';\nuse CA qw/ make_sm2_end_certs /;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $openssl = $ENV{'TEST_OPENSSL_BINARY'} || \"/opt/tongsuo/bin/openssl\";\nmy $t = Test::Nginx->new()->has(qw/stream stream_ssl stream_return/)\n    ->has_daemon('openssl');\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nstream {\n    log_format  status  $status;\n\n    enable_ntls  on;\n    ssl_sign_certificate        1.example.com_sign.crt;\n    ssl_sign_certificate_key    1.example.com_sign.key;\n    ssl_enc_certificate         1.example.com_enc.crt;\n    ssl_enc_certificate_key     1.example.com_enc.key;\n\n    server {\n        listen  127.0.0.1:8080;\n        return  $ssl_client_verify:$ssl_client_cert;\n\n        ssl_verify_client on;\n        ssl_client_certificate 2.example.com_sign_enc.crt;\n    }\n\n    server {\n        listen  127.0.0.1:9131 ssl;\n        return  $ssl_client_verify:$ssl_client_cert;\n\n        ssl_verify_client on;\n        ssl_client_certificate 2.example.com_sign_enc.crt;\n\n        access_log %%TESTDIR%%/status.log status;\n    }\n\n    server {\n        listen  127.0.0.1:9132 ssl;\n        return  $ssl_client_verify:$ssl_client_cert;\n\n        ssl_verify_client optional;\n        ssl_client_certificate 2.example.com_sign_enc.crt;\n        ssl_trusted_certificate 3.example.com_sign_enc.crt;\n    }\n\n    server {\n        listen  127.0.0.1:9133 ssl;\n        return  $ssl_client_verify:$ssl_client_cert;\n\n        ssl_verify_client optional_no_ca;\n        ssl_client_certificate 2.example.com_sign_enc.crt;\n    }\n}\n\nEOF\n\nmake_sm2_end_certs($t, \"1.example.com\");\nmake_sm2_end_certs($t, \"2.example.com\");\nmake_sm2_end_certs($t, \"3.example.com\");\n\nmy $d = $t->testdir();\n\n$t->run()->plan(10);\n\n###############################################################################\n\nis(stream('127.0.0.1:' . port(8080))->read(), ':', 'plain connection');\n\nmy $ret1 = `$openssl s_client -connect localhost:9131 -quiet -enable_ntls -ntls 2>/dev/null`;\nmy $ret2 = `$openssl s_client -connect localhost:9132 -quiet -sign_cert $d/1.example.com_sign.crt -sign_key $d/1.example.com_sign.key -enc_cert $d/1.example.com_enc.crt -enc_key $d/1.example.com_enc.key -enable_ntls -ntls 2>/dev/null`;\nmy $ret3 = `$openssl s_client -connect localhost:9132 -quiet -enable_ntls -ntls 2>/dev/null`;\nmy $ret4 = `$openssl s_client -connect localhost:9133 -quiet -sign_cert $d/1.example.com_sign.crt -sign_key $d/1.example.com_sign.key -enc_cert $d/1.example.com_enc.crt -enc_key $d/1.example.com_enc.key -enable_ntls -ntls 2>/dev/null`;\nmy $ret5 = `$openssl s_client -connect localhost:9131 -quiet -sign_cert $d/2.example.com_sign.crt -sign_key $d/2.example.com_sign.key -enc_cert $d/2.example.com_enc.crt -enc_key $d/2.example.com_enc.key -enable_ntls -ntls 2>/dev/null`;\nmy $ret6 = `$openssl s_client -connect localhost:9132 -quiet -sign_cert $d/2.example.com_sign.crt -sign_key $d/2.example.com_sign.key -enc_cert $d/2.example.com_enc.crt -enc_key $d/2.example.com_enc.key -enable_ntls -ntls 2>/dev/null`;\nmy $ret7 = `$openssl s_client -connect localhost:9132 -quiet -sign_cert $d/3.example.com_sign.crt -sign_key $d/3.example.com_sign.key -enc_cert $d/3.example.com_enc.crt -enc_key $d/3.example.com_enc.key -enable_ntls -ntls 2>/dev/null`;\nmy $ret8 = `$openssl s_client -connect localhost:9132 -quiet -sign_cert $d/3.example.com_sign.crt -sign_key $d/3.example.com_sign.key -enc_cert $d/3.example.com_enc.crt -enc_key $d/3.example.com_enc.key -enable_ntls -ntls -trace 2>/dev/null | grep -A 1 certificate_authorities`;\n\nis($ret1, '', 'no cert');\nis($ret2, '', 'bad optional cert');\nis($ret3, 'NONE:', 'no optional cert');\nlike($ret4, qr/^FAILED.*BEGIN/m, 'bad optional_no_ca cert');\nlike($ret5, qr/^SUCCESS.*BEGIN/m, 'good cert');\nlike($ret6, qr/^SUCCESS.*BEGIN/m, 'good cert optional');\nlike($ret7, qr/^SUCCESS.*BEGIN/m, 'good cert trusted');\nlike($ret8, qr/CN = 2.example.com_sign$/m, 'no trusted sent');\n\n$t->stop();\n\nis($t->read_file('status.log'), \"500\\n200\\n\", 'log');\n\n###############################################################################\n"
  },
  {
    "path": "modules/ngx_tongsuo_ntls/t/stream_sni.t",
    "content": "#!/usr/bin/perl\n\n# Copyright (C) Chenglong Zhang (K1)\n# Copyright (C) 2019 Alibaba Group Holding Limited\n\n# Stream tests for SNI.\n\n###############################################################################\n# \"For using stream_sni.t, you should configure Tengine by using --with-stream_ssl_module --with-stream_sni\";\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse Test::Nginx::Stream qw/ stream /;\nuse lib \".\";\nuse CA qw/ make_sm2_end_certs /;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $openssl = $ENV{'TEST_OPENSSL_BINARY'} || \"/opt/tongsuo/bin/openssl\";\nmy $t = Test::Nginx->new()->has(qw/stream stream_ssl stream_return stream_sni/);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nworker_processes 1;  # NOTE: The default value of Tengine worker_processes directive is `worker_processes auto;`.\n\nstream {\n    proxy_ssl on;\n    proxy_ssl_session_reuse on;\n    proxy_connect_timeout 2s;\n\n    enable_ntls  on;\n    ssl_sign_certificate        server_sign.crt;\n    ssl_sign_certificate_key    server_sign.key;\n    ssl_enc_certificate         server_enc.crt;\n    ssl_enc_certificate_key     server_enc.key;\n\n    server {\n        listen      127.0.0.1:9151 ssl;\n        server_name www.test1.com;\n\n        return \"www.test1.com\";\n    }\n\n    server {\n        listen      127.0.0.1:9151 ssl;\n        server_name www.test2.com;\n\n        return \"www.test2.com\";\n    }\n\n    server {\n        listen      127.0.0.1:9151 ssl default;\n\n        return \"default\";\n    }\n\n    server {\n        listen      127.0.0.1:9152 ssl;\n        server_name www.testsniforce.com;\n        ssl_sni_force on;\n\n        return \"www.testsniforce.com\";\n    }\n}\n\nEOF\n\n\nmake_sm2_end_certs($t, \"server\");\n\nmy $d = $t->testdir();\n\n$t->run()->plan(5);\nmy $ret1 = `$openssl s_client -connect localhost:9151 -quiet -servername \"www.test1.com\" -enable_ntls -ntls 2>&1 | grep \"test1\"`;\nmy $ret2 = `$openssl s_client -connect localhost:9151 -quiet -servername \"www.test2.com\" -enable_ntls -ntls 2>&1 | grep \"test2\"`;\nmy $ret3 = `$openssl s_client -connect localhost:9151 -quiet -servername \"www.test3.com\" -enable_ntls -ntls 2>&1 | grep \"default\"`;\nmy $ret4 = `$openssl s_client -connect localhost:9152 -quiet -servername \"www.testsniforce.com\" -enable_ntls -ntls 2>&1 | grep \"force\"`;\nmy $ret5 = `$openssl s_client -connect localhost:9152 -quiet -servername \"www.testother.com\" -enable_ntls -ntls 2>&1 | grep \"force\"`;\n\nlike($ret1, qr/www.test1.com/, 'Match www.test1.com success');\nlike($ret2, qr/www.test2.com/, 'Match www.test2.com success');\nlike($ret3, qr/default/, 'Match default success');\nlike($ret4, qr/www.testsniforce.com/, 'sni force success');\nunlike($ret5, qr/www.testsniforce.com/, 'reject unknown domain success');\n\n$t->stop();\n"
  },
  {
    "path": "packages/debian/README.Debian",
    "content": "tengine for Debian\n------------------\n\n<possible notes regarding this package - if none, delete this file>\n\n -- root <betterpm@gmail.com>  Fri, 07 Feb 2014 12:43:35 +0800\n"
  },
  {
    "path": "packages/debian/README.md",
    "content": "Installation\n===\n\n##  build the deb package   \n1. install some basic packages\n\n```\naptitude install build-essential debhelper make autoconf automake patch \\\n dpkg-dev fakeroot pbuilder gnupg dh-make libssl-dev libpcre3-dev      \n```\n\n2. build package     \nchange to source directory\n\n```\nmv packages/debian .\nDEB_BUILD_OPTIONS=nocheck dpkg-buildpackage -rfakeroot -uc -b\n```\n\n## install the deb package \nreplace the deb name with the current version\n\n```\nsudo dpkg -i tengine_2.2.1-1_amd64.deb\n```\n\n"
  },
  {
    "path": "packages/debian/README.source",
    "content": "tengine for Debian\n------------------\n\n<this file describes information about the source package, see Debian policy\nmanual section 4.14. You WILL either need to modify or delete this file>\n\n\n\n\n"
  },
  {
    "path": "packages/debian/changelog",
    "content": "tengine (2.2.1-1) unstable; urgency=low\n\n  * Initial release (Closes: #nnnn)  <nnnn is the bug number of your ITP>\n\n -- root <betterpm@gmail.com>  Fri, 07 Feb 2014 12:43:35 +0800\n"
  },
  {
    "path": "packages/debian/compat",
    "content": "8\n"
  },
  {
    "path": "packages/debian/control",
    "content": "Source: tengine\nSection: httpd\nPriority: optional\nMaintainer: min.peng <betterpm@gmail.com>\nUploaders: min.peng <betterpm@gmail.com>\nBuild-Depends:  debhelper (>= 7.0.50~), libssl-dev (>= 0.9.7), libpcre3-dev, zlib1g-dev\nStandards-Version: 3.9.2\nHomepage: http://tengine.taobao.org/\nVcs-Git: git://github.com/alibaba/tengine.git\nVcs-Browser: https://github.com/alibaba/tengine\n\nPackage: tengine\nArchitecture: any\nDepends: ${misc:Depends}, ${shlibs:Depends}, lsb-base, adduser\nDescription: small, powerful, scalable web/proxy server\n Tengine is a web server originated by Taobao, the largest e-commerce website in Asia. \n It is based on the Nginx HTTP server and has many advanced features. \n Tengine has proven to be very stable and efficient on some of the top 100 websites in the world,\n including taobao.com and tmall.com. \n"
  },
  {
    "path": "packages/debian/copyright",
    "content": "Format: http://dep.debian.net/deps/dep5\nUpstream-Name: tengine\nSource: <url://example.com>\n\nFiles: *\nCopyright: <years> <put author's name and email here>\n           <years> <likewise for another author>\nLicense: <special license>\n <Put the license of the package here indented by 1 space>\n <This follows the format of Description: lines in control file>\n .\n <Including paragraphs>\n\n# If you want to use GPL v2 or later for the /debian/* files use \n# the following clauses, or change it to suit. Delete these two lines\nFiles: debian/*\nCopyright: 2014 root <betterpm@gmail.com>\nLicense: GPL-2+\n This package is free software; you can redistribute it and/or modify\n it under the terms of the GNU General Public License as published by\n the Free Software Foundation; either version 2 of the License, or\n (at your option) any later version.\n .\n This package is distributed in the hope that it will be useful,\n but WITHOUT ANY WARRANTY; without even the implied warranty of\n MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n GNU General Public License for more details.\n .\n You should have received a copy of the GNU General Public License\n along with this program. If not, see <http://www.gnu.org/licenses/>\n .\n On Debian systems, the complete text of the GNU General\n Public License version 2 can be found in \"/usr/share/common-licenses/GPL-2\".\n\n# Please also look if there are files or directories which have a\n# different copyright/license attached and list them here.\n"
  },
  {
    "path": "packages/debian/docs",
    "content": "README\nREADME.markdown\nCHANGES.cn\nCHANGES.ru\nCHANGES.te\n"
  },
  {
    "path": "packages/debian/files",
    "content": "tengine_2.2.1-1_all.deb httpd optional\n"
  },
  {
    "path": "packages/debian/init.d",
    "content": "#!/bin/sh\n### BEGIN INIT INFO\n# Provides:          nginx\n# Required-Start:    $network $remote_fs $local_fs \n# Required-Stop:     $network $remote_fs $local_fs\n# Default-Start:     2 3 4 5\n# Default-Stop:      0 1 6\n# Short-Description: Stop/start nginx\n### END INIT INFO\n\n# Author: Sergey Budnevitch <sb@nginx.com>\n\nPATH=/sbin:/usr/sbin:/bin:/usr/bin\nDESC=nginx\nNAME=nginx\nCONFFILE=/etc/nginx/nginx.conf\nDAEMON=/usr/sbin/nginx\nPIDFILE=/var/run/$NAME.pid\nSCRIPTNAME=/etc/init.d/$NAME\n\n[ -x $DAEMON ] || exit 0\n\n[ -r /etc/default/$NAME ] && . /etc/default/$NAME\n\nDAEMON_ARGS=\"-c $CONFFILE $DAEMON_ARGS\"\n\n. /lib/init/vars.sh\n\n. /lib/lsb/init-functions\n\ndo_start()\n{\n    start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON -- \\\n        $DAEMON_ARGS\n    RETVAL=\"$?\"\n    return \"$RETVAL\"\n}\n\ndo_stop()\n{\n    # Return\n    #   0 if daemon has been stopped\n    #   1 if daemon was already stopped\n    #   2 if daemon could not be stopped\n    #   other if a failure occurred\n    start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME\n    RETVAL=\"$?\"\n    rm -f $PIDFILE\n    return \"$RETVAL\"\n}\n\ndo_reload() {\n    #\n    start-stop-daemon --stop --signal HUP --quiet --pidfile $PIDFILE --name $NAME\n    RETVAL=\"$?\"\n    return \"$RETVAL\"\n}\n\ndo_configtest() {\n    if [ \"$#\" -ne 0 ]; then\n        case \"$1\" in\n            -q)\n                FLAG=$1\n                ;;\n            *)\n                ;;\n        esac\n        shift\n    fi\n    $DAEMON -t $FLAG -c $CONFFILE\n    RETVAL=\"$?\"\n    return $RETVAL\n}\n\ndo_upgrade() {\n    OLDBINPIDFILE=$PIDFILE.oldbin\n\n    do_configtest -q || return 6\n    start-stop-daemon --stop --signal USR2 --quiet --pidfile $PIDFILE --name $NAME\n    RETVAL=\"$?\"\n    sleep 1\n    if [ -f $OLDBINPIDFILE -a -f $PIDFILE ]; then\n        start-stop-daemon --stop --signal QUIT --quiet --pidfile $OLDBINPIDFILE --name $NAME\n        RETVAL=\"$?\"\n    else\n        echo $\"Upgrade failed!\"\n        RETVAL=1\n        return $RETVAL\n    fi\n}\n\ncase \"$1\" in\n    start)\n        [ \"$VERBOSE\" != no ] && log_daemon_msg \"Starting $DESC \" \"$NAME\"\n        do_start\n        case \"$?\" in\n            0|1) [ \"$VERBOSE\" != no ] && log_end_msg 0 ;;\n            2) [ \"$VERBOSE\" != no ] && log_end_msg 1 ;;\n        esac\n        ;;\n    stop)\n        [ \"$VERBOSE\" != no ] && log_daemon_msg \"Stopping $DESC\" \"$NAME\"\n        do_stop\n        case \"$?\" in\n            0|1) [ \"$VERBOSE\" != no ] && log_end_msg 0 ;;\n            2) [ \"$VERBOSE\" != no ] && log_end_msg 1 ;;\n        esac\n        ;;\n  status)\n        status_of_proc \"$DAEMON\" \"$NAME\" && exit 0 || exit $?\n        ;;\n  configtest)\n        do_configtest\n        ;;\n  upgrade)\n        do_upgrade\n        ;;\n  reload|force-reload)\n        log_daemon_msg \"Reloading $DESC\" \"$NAME\"\n        do_reload\n        log_end_msg $?\n        ;;\n  restart|force-reload)\n        log_daemon_msg \"Restarting $DESC\" \"$NAME\"\n        do_configtest -q || exit $RETVAL\n        do_stop\n        case \"$?\" in\n            0|1)\n                do_start\n                case \"$?\" in\n                    0) log_end_msg 0 ;;\n                    1) log_end_msg 1 ;; # Old process is still running\n                    *) log_end_msg 1 ;; # Failed to start\n                esac\n                ;;\n            *)\n                # Failed to stop\n                log_end_msg 1\n                ;;\n        esac\n        ;;\n    *)\n        echo \"Usage: $SCRIPTNAME {start|stop|status|restart|reload|force-reload|upgrade|configtest}\" >&2\n        exit 3\n        ;;\nesac\n\nexit $RETVAL\n"
  },
  {
    "path": "packages/debian/logrotate",
    "content": "/var/log/nginx/*.log {\n        daily\n        missingok\n        rotate 52\n        compress\n        delaycompress\n        notifempty\n        create 640 nginx adm\n        sharedscripts\n        postrotate\n                [ -f /var/run/nginx.pid ] && kill -USR1 `cat /var/run/nginx.pid`\n        endscript\n}\n"
  },
  {
    "path": "packages/debian/nginx-debug.install",
    "content": "objs/nginx.debug\tusr/sbin\n"
  },
  {
    "path": "packages/debian/nginx.conf",
    "content": "\nuser  nginx;\nworker_processes  1;\n\nerror_log  /var/log/nginx/error.log warn;\npid        /var/run/nginx.pid;\n\n\nevents {\n    worker_connections  1024;\n}\n\n\nhttp {\n    include       /etc/nginx/mime.types;\n    default_type  application/octet-stream;\n\n    log_format  main  '$remote_addr - $remote_user [$time_local] \"$request\" '\n                      '$status $body_bytes_sent \"$http_referer\" '\n                      '\"$http_user_agent\" \"$http_x_forwarded_for\"';\n\n    access_log  /var/log/nginx/access.log  main;\n\n    sendfile        on;\n    #tcp_nopush     on;\n\n    keepalive_timeout  65;\n\n    #gzip  on;\n\n    include /etc/nginx/conf.d/*.conf;\n}\n"
  },
  {
    "path": "packages/debian/nginx.default",
    "content": "# Defaults for nginx initscript\n# sourced by /etc/init.d/nginx\n\n# Additional options that are passed to nginx\nDAEMON_ARGS=\"\"\n"
  },
  {
    "path": "packages/debian/nginx.vh.default.conf",
    "content": "server {\n    listen       80;\n    server_name  localhost;\n\n    #charset koi8-r;\n    #access_log  /var/log/nginx/log/host.access.log  main;\n\n    location / {\n        root   /usr/share/nginx/html;\n        index  index.html index.htm;\n    }\n\n    #error_page  404              /404.html;\n\n    # redirect server error pages to the static page /50x.html\n    #\n    error_page   500 502 503 504  /50x.html;\n    location = /50x.html {\n        root   /usr/share/nginx/html;\n    }\n\n    # proxy the PHP scripts to Apache listening on 127.0.0.1:80\n    #\n    #location ~ \\.php$ {\n    #    proxy_pass   http://127.0.0.1;\n    #}\n\n    # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000\n    #\n    #location ~ \\.php$ {\n    #    root           html;\n    #    fastcgi_pass   127.0.0.1:9000;\n    #    fastcgi_index  index.php;\n    #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;\n    #    include        fastcgi_params;\n    #}\n\n    # deny access to .htaccess files, if Apache's document root\n    # concurs with nginx's one\n    #\n    #location ~ /\\.ht {\n    #    deny  all;\n    #}\n}\n\n"
  },
  {
    "path": "packages/debian/nginx.vh.example_ssl.conf",
    "content": "# HTTPS server\n#\n#server {\n#    listen       443 ssl;\n#    server_name  localhost;\n\n#    ssl_certificate      /etc/nginx/cert.pem;\n#    ssl_certificate_key  /etc/nginx/cert.key;\n\n#    ssl_session_cache shared:SSL:1m;\n#    ssl_session_timeout  5m;\n\n#    ssl_ciphers  HIGH:!aNULL:!MD5;\n#    ssl_prefer_server_ciphers   on;\n\n#    location / {\n#        root   /usr/share/nginx/html;\n#        index  index.html index.htm;\n#    }\n#}\n"
  },
  {
    "path": "packages/debian/rules",
    "content": "#!/usr/bin/make -f\n\n#export DH_VERBOSE=1\nCFLAGS := $(shell dpkg-buildflags --get CFLAGS)\nLDFLAGS := $(shell dpkg-buildflags --get LDFLAGS)\nWITH_HTTP2 := $(shell printf \"Source: tengine\\nBuild-Depends: libssl-dev (>= 1.0.1)\\n\" | \\\n\tdpkg-checkbuilddeps - >/dev/null 2>&1 && \\\n\techo \"--with-http_v2_module\")\n\n%:\n\tdh $@ --parallel\n\noverride_dh_auto_configure: configure_debug\n\noverride_dh_strip:\n\tdh_strip -Xdebug\n\noverride_dh_auto_build:\n\tdh_auto_build\n\tmv objs/nginx objs/nginx.debug\n\t./configure \\\n\t\t--prefix=/etc/nginx \\\n\t\t--sbin-path=/usr/sbin/nginx \\\n\t\t--conf-path=/etc/nginx/nginx.conf \\\n\t\t--error-log-path=/var/log/nginx/error.log \\\n\t\t--http-log-path=/var/log/nginx/access.log \\\n\t\t--pid-path=/var/run/nginx.pid \\\n\t\t--lock-path=/var/run/nginx.lock \\\n\t\t--http-client-body-temp-path=/var/cache/nginx/client_temp \\\n\t\t--http-proxy-temp-path=/var/cache/nginx/proxy_temp \\\n\t\t--http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp \\\n\t\t--http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp \\\n\t\t--http-scgi-temp-path=/var/cache/nginx/scgi_temp \\\n\t\t--user=nginx \\\n\t\t--group=nginx \\\n\t\t--with-http_ssl_module \\\n\t\t--with-http_realip_module \\\n\t\t--with-http_addition_module \\\n\t\t--with-http_sub_module \\\n\t\t--with-http_dav_module \\\n\t\t--with-http_flv_module \\\n\t\t--with-http_mp4_module \\\n\t\t--with-http_gunzip_module \\\n\t\t--with-http_gzip_static_module \\\n\t\t--with-http_random_index_module \\\n\t\t--with-http_secure_link_module \\\n\t\t--with-http_stub_status_module \\\n\t\t--with-mail \\\n\t\t--with-mail_ssl_module \\\n\t\t--with-file-aio \\\n\t\t$(WITH_HTTP2) \\\n\t\t--with-cc-opt=\"$(CFLAGS)\" \\\n\t\t--with-ld-opt=\"$(LDFLAGS)\" \\\n\t\t--with-ipv6\n\tdh_auto_build\n\nconfigure_debug:\n\t./configure \\\n\t\t--prefix=/etc/nginx \\\n\t\t--sbin-path=/usr/sbin/nginx \\\n\t\t--conf-path=/etc/nginx/nginx.conf \\\n\t\t--error-log-path=/var/log/nginx/error.log \\\n\t\t--http-log-path=/var/log/nginx/access.log \\\n\t\t--pid-path=/var/run/nginx.pid \\\n\t\t--lock-path=/var/run/nginx.lock \\\n\t\t--http-client-body-temp-path=/var/cache/nginx/client_temp \\\n\t\t--http-proxy-temp-path=/var/cache/nginx/proxy_temp \\\n\t\t--http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp \\\n\t\t--http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp \\\n\t\t--http-scgi-temp-path=/var/cache/nginx/scgi_temp \\\n\t\t--user=nginx \\\n\t\t--group=nginx \\\n\t\t--with-http_ssl_module \\\n\t\t--with-http_realip_module \\\n\t\t--with-http_addition_module \\\n\t\t--with-http_sub_module \\\n\t\t--with-http_dav_module \\\n\t\t--with-http_flv_module \\\n\t\t--with-http_mp4_module \\\n\t\t--with-http_gunzip_module \\\n\t\t--with-http_gzip_static_module \\\n\t\t--with-http_random_index_module \\\n\t\t--with-http_secure_link_module \\\n\t\t--with-http_stub_status_module \\\n\t\t--with-mail \\\n\t\t--with-mail_ssl_module \\\n\t\t--with-file-aio \\\n\t\t$(WITH_HTTP2) \\\n\t\t--with-cc-opt=\"$(CFLAGS)\" \\\n\t\t--with-ld-opt=\"$(LDFLAGS)\" \\\n\t\t--with-ipv6 \\\n\t\t--with-debug\n\noverride_dh_auto_install:\n\tdh_auto_install\n\t/usr/bin/install -m 644 debian/nginx.conf debian/tengine/etc/nginx/\n\t/usr/bin/install -m 644 conf/win-utf debian/tengine/etc/nginx/\n\t/usr/bin/install -m 644 conf/koi-utf debian/tengine/etc/nginx/\n\t/usr/bin/install -m 644 conf/koi-win debian/tengine/etc/nginx/\n\t/usr/bin/install -m 644 conf/mime.types debian/tengine/etc/nginx/\n\t/usr/bin/install -m 644 conf/scgi_params debian/tengine/etc/nginx/\n\t/usr/bin/install -m 644 conf/fastcgi_params debian/tengine/etc/nginx/\n\t/usr/bin/install -m 644 conf/uwsgi_params debian/tengine/etc/nginx/\n\t/usr/bin/install -m 644 html/index.html debian/tengine/usr/share/nginx/html/\n\t/usr/bin/install -m 644 html/50x.html debian/tengine/usr/share/nginx/html/\n\t/usr/bin/install -m 644 debian/nginx.vh.default.conf debian/tengine/etc/nginx/conf.d/default.conf\n\t/usr/bin/install -m 644 debian/nginx.vh.example_ssl.conf debian/tengine/etc/nginx/conf.d/example_ssl.conf\n\t/usr/bin/install -m 755 objs/nginx  debian/tengine/usr/sbin/\n"
  },
  {
    "path": "packages/debian/source/format",
    "content": "3.0 (quilt)\n"
  },
  {
    "path": "packages/debian/tengine.debhelper.log",
    "content": "dh_auto_configure\noverride_dh_auto_build dh_auto_build\noverride_dh_auto_build dh_auto_build\ndh_auto_build\ndh_prep\ndh_installdirs\noverride_dh_auto_install dh_auto_install\ndh_auto_install\ndh_install\ndh_installdocs\ndh_installchangelogs\ndh_installexamples\ndh_installman\ndh_installcatalogs\ndh_installcron\ndh_installdebconf\ndh_installemacsen\ndh_installifupdown\ndh_installinfo\ndh_installinit\ndh_installmenu\ndh_installmime\ndh_installmodules\ndh_installlogcheck\ndh_installlogrotate\ndh_installpam\ndh_installppp\ndh_installudev\ndh_installwm\ndh_installxfonts\ndh_installgsettings\ndh_bugfiles\ndh_ucf\ndh_lintian\ndh_gconf\ndh_icons\ndh_perl\ndh_usrlocal\ndh_link\ndh_compress\ndh_fixperms\ndh_installdeb\ndh_gencontrol\ndh_md5sums\ndh_builddeb\ndh_builddeb\n"
  },
  {
    "path": "packages/debian/tengine.dirs",
    "content": "/usr/sbin\n/etc/nginx/conf.d\n/usr/share/nginx\n/usr/share/nginx/html\n/var/cache/nginx\n/var/log/nginx\n"
  },
  {
    "path": "packages/debian/tengine.doc-base.EX",
    "content": "Document: tengine\nTitle: Debian tengine Manual\nAuthor: <betterpm@gmail.com>\nAbstract: This manual describes what tengine is\n and how it can be used to\n manage online manuals on Debian systems.\nSection: unknown\n\nFormat: debiandoc-sgml\nFiles: /usr/share/doc/tengine/tengine.sgml.gz\n\nFormat: postscript\nFiles: /usr/share/doc/tengine/tengine.ps.gz\n\nFormat: text\nFiles: /usr/share/doc/tengine/tengine.text.gz\n\nFormat: HTML\nIndex: /usr/share/doc/tengine/html/index.html\nFiles: /usr/share/doc/tengine/html/*.html\n"
  },
  {
    "path": "packages/debian/tengine.docs",
    "content": "README\nCHANGES.ru\n"
  },
  {
    "path": "packages/debian/tengine.postinst",
    "content": "#!/bin/sh\n\nset -e\n\nif [ \"$1\" != \"configure\" ]; then\n    exit 0\nfi\n\n# Touch and set permisions on default log files on installation\nif [ -z \"$2\" ]; then\n    if [ -d /var/log/nginx ]; then\n        if [ ! -e /var/log/nginx/access.log ]; then\n            touch /var/log/nginx/access.log\n            chmod 640 /var/log/nginx/access.log\n            chown nginx:adm /var/log/nginx/access.log\n        fi\n\n        if [ ! -e /var/log/nginx/error.log ]; then\n            touch /var/log/nginx/error.log\n            chmod 640 /var/log/nginx/error.log\n            chown nginx:adm /var/log/nginx/error.log\n        fi\n    fi\nfi\n\nif [ -x \"/etc/init.d/nginx\" ]; then\n    if [ -f \"/var/run/nginx.pid\" ] && kill -0 `cat /var/run/nginx.pid` >/dev/null; then\n        /etc/init.d/nginx upgrade || true \n    else\n        update-rc.d nginx defaults >/dev/null\n        if [ -x \"`which invoke-rc.d 2>/dev/null`\" ]; then\n            invoke-rc.d nginx start || true\n        else\n            /etc/init.d/nginx start || true\n        fi\n    fi\nfi\n"
  },
  {
    "path": "packages/debian/tengine.postinst.debhelper",
    "content": "# Automatically added by dh_installinit\nif [ -x \"/etc/init.d/tengine\" ]; then\n\tif [ ! -e \"/etc/init/tengine.conf\" ]; then\n\t\tupdate-rc.d tengine defaults >/dev/null\n\tfi\n\tinvoke-rc.d tengine start || exit $?\nfi\n# End automatically added section\n"
  },
  {
    "path": "packages/debian/tengine.postrm",
    "content": "#!/bin/sh\n\nset -e\n\ncase \"$1\" in\n    purge)\n        rm -rf /var/cache/nginx /var/log/nginx /etc/nginx\n        ;;\n    remove)\n        rm -rf /var/cache/nginx /var/log/nginx\n        ;;\n    remove|upgrade|failed-upgrade|abort-install|abort-upgrade|disappear)\n        ;;\n    *)\n        echo \"postrm called with unknown argument \\`$1'\" >&2\n        exit 1\nesac\n\n#DEBHELPER#\n\nexit 0\n"
  },
  {
    "path": "packages/debian/tengine.postrm.debhelper",
    "content": "# Automatically added by dh_installinit\nif [ \"$1\" = \"purge\" ] ; then\n\tupdate-rc.d tengine remove >/dev/null\nfi\n# End automatically added section\n"
  },
  {
    "path": "packages/debian/tengine.preinst",
    "content": "#! /bin/sh\n# preinst script for nginx\n\nset -e\n\naddnginxuser() {\n    # creating nginx group if he isn't already there\n    if ! getent group nginx >/dev/null; then\n        addgroup --system nginx >/dev/null\n    fi\n\n    # creating nginx user if he isn't already there\n    if ! getent passwd nginx >/dev/null; then\n        adduser \\\n          --system \\\n          --disabled-login \\\n          --ingroup nginx \\\n          --no-create-home \\\n          --home /nonexistent \\\n          --gecos \"nginx user\" \\\n          --shell /bin/false \\\n          nginx  >/dev/null\n    fi\n}\n\ncase \"$1\" in\n    install)\n        addnginxuser\n        cat <<BANNER\n----------------------------------------------------------------------\n\nThanks for using Tengine!\n\nPlease find the official documentation for Tengine here:\n* http://tengine.taobao.org/\n\n\n----------------------------------------------------------------------\nBANNER\n        ;;\n    upgrade)\n        addnginxuser\n        ;;\n\n    abort-upgrade)\n        ;;\n\n    *)\n        echo \"preinst called with unknown argument \\`$1'\" >&2\n        exit 0\n        ;;\nesac\n\n#DEBHELPER#\n\nexit 0\n"
  },
  {
    "path": "packages/debian/tengine.prerm",
    "content": "#!/bin/sh\n\nset -e\n\ncase \"$1\" in\n    remove|deconfigure|remove-in-favour|deconfigure-in-favour)\n        if [ -x \"/etc/init.d/nginx\" ]; then\n            if [ -x \"`which invoke-rc.d 2>/dev/null`\" ]; then\n                invoke-rc.d nginx stop || true\n             else\n                /etc/init.d/nginx stop || true\n             fi\n        fi\n        ;;\n    upgrade|failed-upgrade)\n        ;;\n    *)\n        echo \"prerm called with unknown argument \\`$1'\" >&2\n        ;;\nesac\n\nexit 0\n"
  },
  {
    "path": "packages/debian/tengine.prerm.debhelper",
    "content": "# Automatically added by dh_installinit\nif [ -x \"/etc/init.d/tengine\" ]; then\n\tinvoke-rc.d tengine stop || exit $?\nfi\n# End automatically added section\n"
  },
  {
    "path": "packages/debian/tengine.substvars",
    "content": "misc:Depends=\n"
  },
  {
    "path": "src/core/nginx.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <nginx.h>\n\n\nstatic void ngx_show_version_info(void);\nstatic ngx_int_t ngx_add_inherited_sockets(ngx_cycle_t *cycle);\nstatic void ngx_cleanup_environment(void *data);\nstatic ngx_int_t ngx_get_options(int argc, char *const *argv);\nstatic ngx_int_t ngx_process_options(ngx_cycle_t *cycle);\nstatic ngx_int_t ngx_save_argv(ngx_cycle_t *cycle, int argc, char *const *argv);\nstatic void *ngx_core_module_create_conf(ngx_cycle_t *cycle);\nstatic char *ngx_core_module_init_conf(ngx_cycle_t *cycle, void *conf);\nstatic char *ngx_set_user(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\nstatic char *ngx_set_env(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\nstatic char *ngx_set_priority(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\nstatic char *ngx_set_cpu_affinity(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_set_worker_processes(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_load_module(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\n#if (NGX_HAVE_DLOPEN)\nstatic void ngx_unload_module(void *data);\n#endif\n#if (T_NGX_MASTER_ENV)\nstatic char *ngx_master_set_env(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\n#endif\n\n\nstatic ngx_conf_enum_t  ngx_debug_points[] = {\n    { ngx_string(\"stop\"), NGX_DEBUG_POINTS_STOP },\n    { ngx_string(\"abort\"), NGX_DEBUG_POINTS_ABORT },\n    { ngx_null_string, 0 }\n};\n\n\nstatic ngx_command_t  ngx_core_commands[] = {\n\n    { ngx_string(\"daemon\"),\n      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      0,\n      offsetof(ngx_core_conf_t, daemon),\n      NULL },\n\n    { ngx_string(\"master_process\"),\n      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      0,\n      offsetof(ngx_core_conf_t, master),\n      NULL },\n\n    { ngx_string(\"timer_resolution\"),\n      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      0,\n      offsetof(ngx_core_conf_t, timer_resolution),\n      NULL },\n\n    { ngx_string(\"pid\"),\n      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_slot,\n      0,\n      offsetof(ngx_core_conf_t, pid),\n      NULL },\n\n    { ngx_string(\"lock_file\"),\n      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_slot,\n      0,\n      offsetof(ngx_core_conf_t, lock_file),\n      NULL },\n\n    { ngx_string(\"worker_processes\"),\n      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,\n      ngx_set_worker_processes,\n      0,\n      0,\n      NULL },\n\n    { ngx_string(\"debug_points\"),\n      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_enum_slot,\n      0,\n      offsetof(ngx_core_conf_t, debug_points),\n      &ngx_debug_points },\n\n    { ngx_string(\"user\"),\n      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE12,\n      ngx_set_user,\n      0,\n      0,\n      NULL },\n\n    { ngx_string(\"worker_priority\"),\n      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,\n      ngx_set_priority,\n      0,\n      0,\n      NULL },\n\n    { ngx_string(\"worker_cpu_affinity\"),\n      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_1MORE,\n      ngx_set_cpu_affinity,\n      0,\n      0,\n      NULL },\n\n    { ngx_string(\"worker_rlimit_nofile\"),\n      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_num_slot,\n      0,\n      offsetof(ngx_core_conf_t, rlimit_nofile),\n      NULL },\n\n    { ngx_string(\"worker_rlimit_core\"),\n      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_off_slot,\n      0,\n      offsetof(ngx_core_conf_t, rlimit_core),\n      NULL },\n\n    { ngx_string(\"worker_shutdown_timeout\"),\n      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      0,\n      offsetof(ngx_core_conf_t, shutdown_timeout),\n      NULL },\n\n    { ngx_string(\"working_directory\"),\n      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_slot,\n      0,\n      offsetof(ngx_core_conf_t, working_directory),\n      NULL },\n\n    { ngx_string(\"env\"),\n      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,\n      ngx_set_env,\n      0,\n      0,\n      NULL },\n\n    { ngx_string(\"load_module\"),\n      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,\n      ngx_load_module,\n      0,\n      0,\n      NULL },\n\n#if (T_NGX_MASTER_ENV)\n\n    { ngx_string(\"master_env\"),\n      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,\n      ngx_master_set_env,\n      0,\n      0,\n      NULL },\n\n#endif\n\n#if (T_PIPE_SET_SIZE)\n    { ngx_string(\"pipe_set_size\"),\n      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      0,\n      offsetof(ngx_core_conf_t, pipe_size),\n      NULL },\n#endif\n\n      ngx_null_command\n};\n\n\nstatic ngx_core_module_t  ngx_core_module_ctx = {\n    ngx_string(\"core\"),\n    ngx_core_module_create_conf,\n    ngx_core_module_init_conf\n};\n\n\nngx_module_t  ngx_core_module = {\n    NGX_MODULE_V1,\n    &ngx_core_module_ctx,                  /* module context */\n    ngx_core_commands,                     /* module directives */\n    NGX_CORE_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_uint_t   ngx_show_help;\nstatic ngx_uint_t   ngx_show_version;\nstatic ngx_uint_t   ngx_show_configure;\n#if (NGX_SSL && NGX_SSL_ASYNC)\n/* indicate that nginx start without ngx_ssl_init()\n * which will involve OpenSSL configuration file to\n * start OpenSSL engine */\nstatic ngx_uint_t   ngx_no_ssl_init;\n#endif\nstatic u_char      *ngx_prefix;\nstatic u_char      *ngx_error_log;\nstatic u_char      *ngx_conf_file;\nstatic u_char      *ngx_conf_params;\nstatic char        *ngx_signal;\n\n\nstatic char **ngx_os_environ;\n\n\nint ngx_cdecl\nmain(int argc, char *const *argv)\n{\n    ngx_buf_t        *b;\n    ngx_log_t        *log;\n    ngx_uint_t        i;\n    ngx_cycle_t      *cycle, init_cycle;\n    ngx_conf_dump_t  *cd;\n    ngx_core_conf_t  *ccf;\n\n    ngx_debug_init();\n\n    if (ngx_strerror_init() != NGX_OK) {\n        return 1;\n    }\n\n    if (ngx_get_options(argc, argv) != NGX_OK) {\n        return 1;\n    }\n\n    if (ngx_show_version) {\n        ngx_show_version_info();\n\n        if (!ngx_test_config) {\n            return 0;\n        }\n    }\n\n    /* TODO */ ngx_max_sockets = -1;\n\n    ngx_time_init();\n\n#if (NGX_PCRE)\n    ngx_regex_init();\n#endif\n\n    ngx_pid = ngx_getpid();\n    ngx_parent = ngx_getppid();\n\n    log = ngx_log_init(ngx_prefix, ngx_error_log);\n    if (log == NULL) {\n        return 1;\n    }\n\n    /* STUB */\n#if (NGX_OPENSSL)\n#if (NGX_SSL && NGX_SSL_ASYNC)\n    if (!ngx_no_ssl_init) {\n#endif\n        ngx_ssl_init(log);\n#if (NGX_SSL && NGX_SSL_ASYNC)\n    }\n#endif\n#endif\n\n    /*\n     * init_cycle->log is required for signal handlers and\n     * ngx_process_options()\n     */\n\n    ngx_memzero(&init_cycle, sizeof(ngx_cycle_t));\n    init_cycle.log = log;\n#if (NGX_SSL && NGX_SSL_ASYNC)\n    init_cycle.no_ssl_init = ngx_no_ssl_init;\n#endif\n    ngx_cycle = &init_cycle;\n\n    init_cycle.pool = ngx_create_pool(1024, log);\n    if (init_cycle.pool == NULL) {\n        return 1;\n    }\n\n    if (ngx_save_argv(&init_cycle, argc, argv) != NGX_OK) {\n        return 1;\n    }\n\n    if (ngx_process_options(&init_cycle) != NGX_OK) {\n        return 1;\n    }\n\n    if (ngx_os_init(log) != NGX_OK) {\n        return 1;\n    }\n\n    /*\n     * ngx_crc32_table_init() requires ngx_cacheline_size set in ngx_os_init()\n     */\n\n    if (ngx_crc32_table_init() != NGX_OK) {\n        return 1;\n    }\n\n    /*\n     * ngx_slab_sizes_init() requires ngx_pagesize set in ngx_os_init()\n     */\n\n    ngx_slab_sizes_init();\n\n    if (ngx_add_inherited_sockets(&init_cycle) != NGX_OK) {\n        return 1;\n    }\n\n    if (ngx_preinit_modules() != NGX_OK) {\n        return 1;\n    }\n\n    cycle = ngx_init_cycle(&init_cycle);\n    if (cycle == NULL) {\n        if (ngx_test_config) {\n            ngx_log_stderr(0, \"configuration file %s test failed\",\n                           init_cycle.conf_file.data);\n        }\n\n        return 1;\n    }\n\n    if (ngx_test_config) {\n        if (!ngx_quiet_mode) {\n            ngx_log_stderr(0, \"configuration file %s test is successful\",\n                           cycle->conf_file.data);\n        }\n\n        if (ngx_dump_config) {\n            cd = cycle->config_dump.elts;\n\n            for (i = 0; i < cycle->config_dump.nelts; i++) {\n\n                ngx_write_stdout(\"# configuration file \");\n                (void) ngx_write_fd(ngx_stdout, cd[i].name.data,\n                                    cd[i].name.len);\n                ngx_write_stdout(\":\" NGX_LINEFEED);\n\n                b = cd[i].buffer;\n\n                (void) ngx_write_fd(ngx_stdout, b->pos, b->last - b->pos);\n                ngx_write_stdout(NGX_LINEFEED);\n            }\n        }\n\n        return 0;\n    }\n\n    if (ngx_signal) {\n        return ngx_signal_process(cycle, ngx_signal);\n    }\n\n    ngx_os_status(cycle->log);\n\n    ngx_cycle = cycle;\n\n    ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);\n\n    if (ccf->master && ngx_process == NGX_PROCESS_SINGLE) {\n        ngx_process = NGX_PROCESS_MASTER;\n    }\n\n#if !(NGX_WIN32)\n\n    if (ngx_init_signals(cycle->log) != NGX_OK) {\n        return 1;\n    }\n\n    if (!ngx_inherited && ccf->daemon) {\n        if (ngx_daemon(cycle->log) != NGX_OK) {\n            return 1;\n        }\n\n        ngx_daemonized = 1;\n    }\n\n    if (ngx_inherited) {\n        ngx_daemonized = 1;\n    }\n\n#endif\n\n#if (T_PIPES)\n    if (ngx_open_pipes(cycle) == NGX_ERROR) {\n        ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, \"can not open pipes\");\n        return 1;\n    }\n#endif\n\n    if (ngx_create_pidfile(&ccf->pid, cycle->log) != NGX_OK) {\n        return 1;\n    }\n\n    if (ngx_log_redirect_stderr(cycle) != NGX_OK) {\n        return 1;\n    }\n\n    if (log->file->fd != ngx_stderr) {\n        if (ngx_close_file(log->file->fd) == NGX_FILE_ERROR) {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                          ngx_close_file_n \" built-in log failed\");\n        }\n    }\n\n    ngx_use_stderr = 0;\n\n    if (ngx_process == NGX_PROCESS_SINGLE) {\n        ngx_single_process_cycle(cycle);\n\n    } else {\n        ngx_master_process_cycle(cycle);\n    }\n\n    return 0;\n}\n\n\nstatic void\nngx_show_version_info(void)\n{\n#if (T_NGX_SERVER_INFO) \n    ngx_write_stderr(\"Tengine version: \" TENGINE_VER_BUILD NGX_LINEFEED);\n#endif\n    ngx_write_stderr(\"nginx version: \" NGINX_VER_BUILD NGX_LINEFEED);\n\n    if (ngx_show_help) {\n        ngx_write_stderr(\n            \"Usage: nginx [-?hvVtTq] [-s signal] [-p prefix]\" NGX_LINEFEED\n            \"             [-e filename] [-c filename] [-g directives]\"\n                          NGX_LINEFEED NGX_LINEFEED\n            \"Options:\" NGX_LINEFEED\n            \"  -?,-h         : this help\" NGX_LINEFEED\n            \"  -v            : show version and exit\" NGX_LINEFEED\n            \"  -V            : show version and configure options then exit\"\n                               NGX_LINEFEED\n            \"  -t            : test configuration and exit\" NGX_LINEFEED\n            \"  -T            : test configuration, dump it and exit\"\n                               NGX_LINEFEED\n            \"  -q            : suppress non-error messages \"\n                               \"during configuration testing\" NGX_LINEFEED\n            \"  -s signal     : send signal to a master process: \"\n                               \"stop, quit, reopen, reload\" NGX_LINEFEED\n#ifdef NGX_PREFIX\n            \"  -p prefix     : set prefix path (default: \" NGX_PREFIX \")\"\n                               NGX_LINEFEED\n#else\n            \"  -p prefix     : set prefix path (default: NONE)\" NGX_LINEFEED\n#endif\n            \"  -e filename   : set error log file (default: \"\n#ifdef NGX_ERROR_LOG_STDERR\n                               \"stderr)\" NGX_LINEFEED\n#else\n                               NGX_ERROR_LOG_PATH \")\" NGX_LINEFEED\n#endif\n            \"  -c filename   : set configuration file (default: \" NGX_CONF_PATH\n                               \")\" NGX_LINEFEED\n            \"  -g directives : set global directives out of configuration \"\n                               \"file\" NGX_LINEFEED NGX_LINEFEED\n#if (T_NGX_SHOW_INFO)\n            \"  -m            : show all modules and exit\" NGX_LINEFEED\n            \"  -l            : show all directives and exit\" NGX_LINEFEED\n#endif\n        );\n    }\n\n    if (ngx_show_configure) {\n\n#ifdef NGX_COMPILER\n        ngx_write_stderr(\"built by \" NGX_COMPILER NGX_LINEFEED);\n#endif\n\n#if (NGX_SSL)\n        if (ngx_strcmp(ngx_ssl_version(), OPENSSL_VERSION_TEXT) == 0) {\n            ngx_write_stderr(\"built with \" OPENSSL_VERSION_TEXT NGX_LINEFEED);\n        } else {\n            ngx_write_stderr(\"built with \" OPENSSL_VERSION_TEXT\n                             \" (running with \");\n            ngx_write_stderr((char *) (uintptr_t) ngx_ssl_version());\n            ngx_write_stderr(\")\" NGX_LINEFEED);\n        }\n#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME\n        ngx_write_stderr(\"TLS SNI support enabled\" NGX_LINEFEED);\n#else\n        ngx_write_stderr(\"TLS SNI support disabled\" NGX_LINEFEED);\n#endif\n#endif\n\n        ngx_write_stderr(\"configure arguments:\" NGX_CONFIGURE NGX_LINEFEED);\n    }\n}\n\n\nstatic ngx_int_t\nngx_add_inherited_sockets(ngx_cycle_t *cycle)\n{\n    u_char           *p, *v, *inherited;\n    ngx_int_t         s;\n    ngx_listening_t  *ls;\n\n    inherited = (u_char *) getenv(NGINX_VAR);\n\n    if (inherited == NULL) {\n        return NGX_OK;\n    }\n\n    ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0,\n                  \"using inherited sockets from \\\"%s\\\"\", inherited);\n\n    if (ngx_array_init(&cycle->listening, cycle->pool, 10,\n                       sizeof(ngx_listening_t))\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    for (p = inherited, v = p; *p; p++) {\n        if (*p == ':' || *p == ';') {\n            s = ngx_atoi(v, p - v);\n            if (s == NGX_ERROR) {\n                ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,\n                              \"invalid socket number \\\"%s\\\" in \" NGINX_VAR\n                              \" environment variable, ignoring the rest\"\n                              \" of the variable\", v);\n                break;\n            }\n\n            v = p + 1;\n\n            ls = ngx_array_push(&cycle->listening);\n            if (ls == NULL) {\n                return NGX_ERROR;\n            }\n\n            ngx_memzero(ls, sizeof(ngx_listening_t));\n\n            ls->fd = (ngx_socket_t) s;\n            ls->inherited = 1;\n        }\n    }\n\n    if (v != p) {\n        ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,\n                      \"invalid socket number \\\"%s\\\" in \" NGINX_VAR\n                      \" environment variable, ignoring\", v);\n    }\n\n    ngx_inherited = 1;\n\n    return ngx_set_inherited_sockets(cycle);\n}\n\n\nchar **\nngx_set_environment(ngx_cycle_t *cycle, ngx_uint_t *last)\n{\n    char                **p, **env;\n    ngx_str_t            *var;\n    ngx_uint_t            i, n;\n    ngx_core_conf_t      *ccf;\n    ngx_pool_cleanup_t   *cln;\n\n    ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);\n\n    if (last == NULL && ccf->environment) {\n        return ccf->environment;\n    }\n\n    var = ccf->env.elts;\n\n    for (i = 0; i < ccf->env.nelts; i++) {\n        if (ngx_strcmp(var[i].data, \"TZ\") == 0\n            || ngx_strncmp(var[i].data, \"TZ=\", 3) == 0)\n        {\n            goto tz_found;\n        }\n    }\n\n    var = ngx_array_push(&ccf->env);\n    if (var == NULL) {\n        return NULL;\n    }\n\n    var->len = 2;\n    var->data = (u_char *) \"TZ\";\n\n    var = ccf->env.elts;\n\ntz_found:\n\n    n = 0;\n\n    for (i = 0; i < ccf->env.nelts; i++) {\n\n        if (var[i].data[var[i].len] == '=') {\n            n++;\n            continue;\n        }\n\n        for (p = ngx_os_environ; *p; p++) {\n\n            if (ngx_strncmp(*p, var[i].data, var[i].len) == 0\n                && (*p)[var[i].len] == '=')\n            {\n                n++;\n                break;\n            }\n        }\n    }\n\n    if (last) {\n        env = ngx_alloc((*last + n + 1) * sizeof(char *), cycle->log);\n        if (env == NULL) {\n            return NULL;\n        }\n\n        *last = n;\n\n    } else {\n        cln = ngx_pool_cleanup_add(cycle->pool, 0);\n        if (cln == NULL) {\n            return NULL;\n        }\n\n        env = ngx_alloc((n + 1) * sizeof(char *), cycle->log);\n        if (env == NULL) {\n            return NULL;\n        }\n\n        cln->handler = ngx_cleanup_environment;\n        cln->data = env;\n    }\n\n    n = 0;\n\n    for (i = 0; i < ccf->env.nelts; i++) {\n\n        if (var[i].data[var[i].len] == '=') {\n            env[n++] = (char *) var[i].data;\n            continue;\n        }\n\n        for (p = ngx_os_environ; *p; p++) {\n\n            if (ngx_strncmp(*p, var[i].data, var[i].len) == 0\n                && (*p)[var[i].len] == '=')\n            {\n                env[n++] = *p;\n                break;\n            }\n        }\n    }\n\n    env[n] = NULL;\n\n    if (last == NULL) {\n        ccf->environment = env;\n        environ = env;\n    }\n\n    return env;\n}\n\n\nstatic void\nngx_cleanup_environment(void *data)\n{\n    char  **env = data;\n\n    if (environ == env) {\n\n        /*\n         * if the environment is still used, as it happens on exit,\n         * the only option is to leak it\n         */\n\n        return;\n    }\n\n    ngx_free(env);\n}\n\n\nngx_pid_t\nngx_exec_new_binary(ngx_cycle_t *cycle, char *const *argv)\n{\n    char             **env, *var;\n    u_char            *p;\n    ngx_uint_t         i, n;\n    ngx_pid_t          pid;\n    ngx_exec_ctx_t     ctx;\n    ngx_core_conf_t   *ccf;\n    ngx_listening_t   *ls;\n\n    ngx_memzero(&ctx, sizeof(ngx_exec_ctx_t));\n\n    ctx.path = argv[0];\n    ctx.name = \"new binary process\";\n    ctx.argv = argv;\n\n    n = 2;\n    env = ngx_set_environment(cycle, &n);\n    if (env == NULL) {\n        return NGX_INVALID_PID;\n    }\n\n    var = ngx_alloc(sizeof(NGINX_VAR)\n                    + cycle->listening.nelts * (NGX_INT32_LEN + 1) + 2,\n                    cycle->log);\n    if (var == NULL) {\n        ngx_free(env);\n        return NGX_INVALID_PID;\n    }\n\n    p = ngx_cpymem(var, NGINX_VAR \"=\", sizeof(NGINX_VAR));\n\n    ls = cycle->listening.elts;\n    for (i = 0; i < cycle->listening.nelts; i++) {\n        p = ngx_sprintf(p, \"%ud;\", ls[i].fd);\n    }\n\n    *p = '\\0';\n\n    env[n++] = var;\n\n#if (NGX_SETPROCTITLE_USES_ENV)\n\n    /* allocate the spare 300 bytes for the new binary process title */\n\n    env[n++] = \"SPARE=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\"\n               \"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\"\n               \"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\"\n               \"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\"\n               \"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\";\n\n#endif\n\n    env[n] = NULL;\n\n#if (NGX_DEBUG)\n    {\n    char  **e;\n    for (e = env; *e; e++) {\n        ngx_log_debug1(NGX_LOG_DEBUG_CORE, cycle->log, 0, \"env: %s\", *e);\n    }\n    }\n#endif\n\n    ctx.envp = (char *const *) env;\n\n    ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);\n\n    if (ngx_rename_file(ccf->pid.data, ccf->oldpid.data) == NGX_FILE_ERROR) {\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                      ngx_rename_file_n \" %s to %s failed \"\n                      \"before executing new binary process \\\"%s\\\"\",\n                      ccf->pid.data, ccf->oldpid.data, argv[0]);\n\n        ngx_free(env);\n        ngx_free(var);\n\n        return NGX_INVALID_PID;\n    }\n\n    pid = ngx_execute(cycle, &ctx);\n\n    if (pid == NGX_INVALID_PID) {\n        if (ngx_rename_file(ccf->oldpid.data, ccf->pid.data)\n            == NGX_FILE_ERROR)\n        {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                          ngx_rename_file_n \" %s back to %s failed after \"\n                          \"an attempt to execute new binary process \\\"%s\\\"\",\n                          ccf->oldpid.data, ccf->pid.data, argv[0]);\n        }\n    }\n\n    ngx_free(env);\n    ngx_free(var);\n\n    return pid;\n}\n\n\nstatic ngx_int_t\nngx_get_options(int argc, char *const *argv)\n{\n    u_char     *p;\n    ngx_int_t   i;\n\n    for (i = 1; i < argc; i++) {\n\n        p = (u_char *) argv[i];\n\n        if (*p++ != '-') {\n            ngx_log_stderr(0, \"invalid option: \\\"%s\\\"\", argv[i]);\n            return NGX_ERROR;\n        }\n\n        while (*p) {\n\n            switch (*p++) {\n\n            case '?':\n            case 'h':\n                ngx_show_version = 1;\n                ngx_show_help = 1;\n                break;\n\n            case 'v':\n                ngx_show_version = 1;\n                break;\n\n            case 'V':\n                ngx_show_version = 1;\n                ngx_show_configure = 1;\n                break;\n\n            case 't':\n                ngx_test_config = 1;\n#if (NGX_SSL && NGX_SSL_ASYNC)\n                ngx_no_ssl_init = 1;\n#endif\n                break;\n\n            case 'T':\n                ngx_test_config = 1;\n                ngx_dump_config = 1;\n#if (NGX_SSL && NGX_SSL_ASYNC)\n                ngx_no_ssl_init = 1;\n#endif\n                break;\n\n            case 'q':\n                ngx_quiet_mode = 1;\n                break;\n\n#if (T_NGX_SHOW_INFO)\n            case 'l':\n                ngx_test_config = 1;\n                ngx_show_version = 1;\n                ngx_show_directives = 1;\n                break;\n\n            case 'm':\n                ngx_test_config = 1;\n                ngx_show_version = 1;\n                ngx_show_modules = 1;\n                break;\n#endif\n\n            case 'p':\n                if (*p) {\n                    ngx_prefix = p;\n                    goto next;\n                }\n\n                if (argv[++i]) {\n                    ngx_prefix = (u_char *) argv[i];\n                    goto next;\n                }\n\n                ngx_log_stderr(0, \"option \\\"-p\\\" requires directory name\");\n                return NGX_ERROR;\n\n            case 'e':\n                if (*p) {\n                    ngx_error_log = p;\n\n                } else if (argv[++i]) {\n                    ngx_error_log = (u_char *) argv[i];\n\n                } else {\n                    ngx_log_stderr(0, \"option \\\"-e\\\" requires file name\");\n                    return NGX_ERROR;\n                }\n\n                if (ngx_strcmp(ngx_error_log, \"stderr\") == 0) {\n                    ngx_error_log = (u_char *) \"\";\n                }\n\n                goto next;\n\n            case 'c':\n                if (*p) {\n                    ngx_conf_file = p;\n                    goto next;\n                }\n\n                if (argv[++i]) {\n                    ngx_conf_file = (u_char *) argv[i];\n                    goto next;\n                }\n\n                ngx_log_stderr(0, \"option \\\"-c\\\" requires file name\");\n                return NGX_ERROR;\n\n            case 'g':\n                if (*p) {\n                    ngx_conf_params = p;\n                    goto next;\n                }\n\n                if (argv[++i]) {\n                    ngx_conf_params = (u_char *) argv[i];\n                    goto next;\n                }\n\n                ngx_log_stderr(0, \"option \\\"-g\\\" requires parameter\");\n                return NGX_ERROR;\n\n            case 's':\n#if (NGX_SSL && NGX_SSL_ASYNC)\n                ngx_no_ssl_init = 1;\n#endif\n                if (*p) {\n                    ngx_signal = (char *) p;\n\n                } else if (argv[++i]) {\n                    ngx_signal = argv[i];\n\n                } else {\n                    ngx_log_stderr(0, \"option \\\"-s\\\" requires parameter\");\n                    return NGX_ERROR;\n                }\n\n                if (ngx_strcmp(ngx_signal, \"stop\") == 0\n                    || ngx_strcmp(ngx_signal, \"quit\") == 0\n                    || ngx_strcmp(ngx_signal, \"reopen\") == 0\n                    || ngx_strcmp(ngx_signal, \"reload\") == 0)\n                {\n                    ngx_process = NGX_PROCESS_SIGNALLER;\n                    goto next;\n                }\n\n                ngx_log_stderr(0, \"invalid option: \\\"-s %s\\\"\", ngx_signal);\n                return NGX_ERROR;\n\n            default:\n                ngx_log_stderr(0, \"invalid option: \\\"%c\\\"\", *(p - 1));\n#if (NGX_SSL && NGX_SSL_ASYNC)\n                ngx_no_ssl_init = 1;\n#endif\n                return NGX_ERROR;\n            }\n        }\n\n    next:\n\n        continue;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_save_argv(ngx_cycle_t *cycle, int argc, char *const *argv)\n{\n#if (NGX_FREEBSD)\n\n    ngx_os_argv = (char **) argv;\n    ngx_argc = argc;\n    ngx_argv = (char **) argv;\n\n#else\n    size_t     len;\n    ngx_int_t  i;\n\n    ngx_os_argv = (char **) argv;\n    ngx_argc = argc;\n\n    ngx_argv = ngx_alloc((argc + 1) * sizeof(char *), cycle->log);\n    if (ngx_argv == NULL) {\n        return NGX_ERROR;\n    }\n\n    for (i = 0; i < argc; i++) {\n        len = ngx_strlen(argv[i]) + 1;\n\n        ngx_argv[i] = ngx_alloc(len, cycle->log);\n        if (ngx_argv[i] == NULL) {\n            return NGX_ERROR;\n        }\n\n        (void) ngx_cpystrn((u_char *) ngx_argv[i], (u_char *) argv[i], len);\n    }\n\n    ngx_argv[i] = NULL;\n\n#endif\n\n    ngx_os_environ = environ;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_process_options(ngx_cycle_t *cycle)\n{\n    u_char  *p;\n    size_t   len;\n\n    if (ngx_prefix) {\n        len = ngx_strlen(ngx_prefix);\n        p = ngx_prefix;\n\n        if (len && !ngx_path_separator(p[len - 1])) {\n            p = ngx_pnalloc(cycle->pool, len + 1);\n            if (p == NULL) {\n                return NGX_ERROR;\n            }\n\n            ngx_memcpy(p, ngx_prefix, len);\n            p[len++] = '/';\n        }\n\n        cycle->conf_prefix.len = len;\n        cycle->conf_prefix.data = p;\n        cycle->prefix.len = len;\n        cycle->prefix.data = p;\n\n    } else {\n\n#ifndef NGX_PREFIX\n\n        p = ngx_pnalloc(cycle->pool, NGX_MAX_PATH);\n        if (p == NULL) {\n            return NGX_ERROR;\n        }\n\n        if (ngx_getcwd(p, NGX_MAX_PATH) == 0) {\n            ngx_log_stderr(ngx_errno, \"[emerg]: \" ngx_getcwd_n \" failed\");\n            return NGX_ERROR;\n        }\n\n        len = ngx_strlen(p);\n\n        p[len++] = '/';\n\n        cycle->conf_prefix.len = len;\n        cycle->conf_prefix.data = p;\n        cycle->prefix.len = len;\n        cycle->prefix.data = p;\n\n#else\n\n#ifdef NGX_CONF_PREFIX\n        ngx_str_set(&cycle->conf_prefix, NGX_CONF_PREFIX);\n#else\n        ngx_str_set(&cycle->conf_prefix, NGX_PREFIX);\n#endif\n        ngx_str_set(&cycle->prefix, NGX_PREFIX);\n\n#endif\n    }\n\n    if (ngx_conf_file) {\n        cycle->conf_file.len = ngx_strlen(ngx_conf_file);\n        cycle->conf_file.data = ngx_conf_file;\n\n    } else {\n        ngx_str_set(&cycle->conf_file, NGX_CONF_PATH);\n    }\n\n    if (ngx_conf_full_name(cycle, &cycle->conf_file, 0) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    for (p = cycle->conf_file.data + cycle->conf_file.len - 1;\n         p > cycle->conf_file.data;\n         p--)\n    {\n        if (ngx_path_separator(*p)) {\n            cycle->conf_prefix.len = p - cycle->conf_file.data + 1;\n            cycle->conf_prefix.data = cycle->conf_file.data;\n            break;\n        }\n    }\n\n    if (ngx_error_log) {\n        cycle->error_log.len = ngx_strlen(ngx_error_log);\n        cycle->error_log.data = ngx_error_log;\n\n    } else {\n        ngx_str_set(&cycle->error_log, NGX_ERROR_LOG_PATH);\n    }\n\n    if (ngx_conf_params) {\n        cycle->conf_param.len = ngx_strlen(ngx_conf_params);\n        cycle->conf_param.data = ngx_conf_params;\n    }\n\n    if (ngx_test_config) {\n        cycle->log->log_level = NGX_LOG_INFO;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void *\nngx_core_module_create_conf(ngx_cycle_t *cycle)\n{\n    ngx_core_conf_t  *ccf;\n\n    ccf = ngx_pcalloc(cycle->pool, sizeof(ngx_core_conf_t));\n    if (ccf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc()\n     *\n     *     ccf->pid = NULL;\n     *     ccf->oldpid = NULL;\n     *     ccf->priority = 0;\n     *     ccf->cpu_affinity_auto = 0;\n     *     ccf->cpu_affinity_n = 0;\n     *     ccf->cpu_affinity = NULL;\n     */\n\n    ccf->daemon = NGX_CONF_UNSET;\n    ccf->master = NGX_CONF_UNSET;\n    ccf->timer_resolution = NGX_CONF_UNSET_MSEC;\n    ccf->shutdown_timeout = NGX_CONF_UNSET_MSEC;\n\n    ccf->worker_processes = NGX_CONF_UNSET;\n    ccf->debug_points = NGX_CONF_UNSET;\n\n    ccf->rlimit_nofile = NGX_CONF_UNSET;\n    ccf->rlimit_core = NGX_CONF_UNSET;\n\n    ccf->user = (ngx_uid_t) NGX_CONF_UNSET_UINT;\n    ccf->group = (ngx_gid_t) NGX_CONF_UNSET_UINT;\n\n    if (ngx_array_init(&ccf->env, cycle->pool, 1, sizeof(ngx_str_t))\n        != NGX_OK)\n    {\n        return NULL;\n    }\n\n#ifdef T_PIPE_SET_SIZE\n    ccf->pipe_size = NGX_CONF_UNSET_SIZE;\n#endif\n\n    return ccf;\n}\n\n\nstatic char *\nngx_core_module_init_conf(ngx_cycle_t *cycle, void *conf)\n{\n    ngx_core_conf_t  *ccf = conf;\n\n    ngx_conf_init_value(ccf->daemon, 1);\n    ngx_conf_init_value(ccf->master, 1);\n    ngx_conf_init_msec_value(ccf->timer_resolution, 0);\n    ngx_conf_init_msec_value(ccf->shutdown_timeout, 0);\n\n    ngx_conf_init_value(ccf->worker_processes,\n#if (T_NGX_MODIFY_DEFAULT_VALUE)\n                        ngx_ncpu);\n#else\n                        1);\n#endif\n    ngx_conf_init_value(ccf->debug_points, 0);\n\n#if (NGX_HAVE_CPU_AFFINITY)\n\n    if (!ccf->cpu_affinity_auto\n        && ccf->cpu_affinity_n\n        && ccf->cpu_affinity_n != 1\n        && ccf->cpu_affinity_n != (ngx_uint_t) ccf->worker_processes)\n    {\n        ngx_log_error(NGX_LOG_WARN, cycle->log, 0,\n                      \"the number of \\\"worker_processes\\\" is not equal to \"\n                      \"the number of \\\"worker_cpu_affinity\\\" masks, \"\n                      \"using last mask for remaining worker processes\");\n    }\n\n#endif\n\n\n    if (ccf->pid.len == 0) {\n        ngx_str_set(&ccf->pid, NGX_PID_PATH);\n    }\n\n    if (ngx_conf_full_name(cycle, &ccf->pid, 0) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    ccf->oldpid.len = ccf->pid.len + sizeof(NGX_OLDPID_EXT);\n\n    ccf->oldpid.data = ngx_pnalloc(cycle->pool, ccf->oldpid.len);\n    if (ccf->oldpid.data == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_memcpy(ngx_cpymem(ccf->oldpid.data, ccf->pid.data, ccf->pid.len),\n               NGX_OLDPID_EXT, sizeof(NGX_OLDPID_EXT));\n\n\n#if !(NGX_WIN32)\n\n    if (ccf->user == (uid_t) NGX_CONF_UNSET_UINT && geteuid() == 0) {\n        struct group   *grp;\n        struct passwd  *pwd;\n\n        ngx_set_errno(0);\n        pwd = getpwnam(NGX_USER);\n        if (pwd == NULL) {\n            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                          \"getpwnam(\\\"\" NGX_USER \"\\\") failed\");\n            return NGX_CONF_ERROR;\n        }\n\n        ccf->username = NGX_USER;\n        ccf->user = pwd->pw_uid;\n\n        ngx_set_errno(0);\n        grp = getgrnam(NGX_GROUP);\n        if (grp == NULL) {\n            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                          \"getgrnam(\\\"\" NGX_GROUP \"\\\") failed\");\n            return NGX_CONF_ERROR;\n        }\n\n        ccf->group = grp->gr_gid;\n    }\n\n\n    if (ccf->lock_file.len == 0) {\n        ngx_str_set(&ccf->lock_file, NGX_LOCK_PATH);\n    }\n\n    if (ngx_conf_full_name(cycle, &ccf->lock_file, 0) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    {\n    ngx_str_t  lock_file;\n\n    lock_file = cycle->old_cycle->lock_file;\n\n    if (lock_file.len) {\n        lock_file.len--;\n\n        if (ccf->lock_file.len != lock_file.len\n            || ngx_strncmp(ccf->lock_file.data, lock_file.data, lock_file.len)\n               != 0)\n        {\n            ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,\n                          \"\\\"lock_file\\\" could not be changed, ignored\");\n        }\n\n        cycle->lock_file.len = lock_file.len + 1;\n        lock_file.len += sizeof(\".accept\");\n\n        cycle->lock_file.data = ngx_pstrdup(cycle->pool, &lock_file);\n        if (cycle->lock_file.data == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n    } else {\n        cycle->lock_file.len = ccf->lock_file.len + 1;\n        cycle->lock_file.data = ngx_pnalloc(cycle->pool,\n                                      ccf->lock_file.len + sizeof(\".accept\"));\n        if (cycle->lock_file.data == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        ngx_memcpy(ngx_cpymem(cycle->lock_file.data, ccf->lock_file.data,\n                              ccf->lock_file.len),\n                   \".accept\", sizeof(\".accept\"));\n    }\n    }\n\n#endif\n\n#ifdef T_PIPE_SET_SIZE\n    if (ccf->pipe_size != NGX_CONF_UNSET_SIZE) {\n        if (ccf->pipe_size < 64 * 1024) {\n            ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,\n                          \"\\\"pipe_size\\\" must be at least 64K, ignored\");\n            return NGX_CONF_ERROR;\n        }\n    }\n#endif\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_set_user(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n#if (NGX_WIN32)\n\n    ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                       \"\\\"user\\\" is not supported, ignored\");\n\n    return NGX_CONF_OK;\n\n#else\n\n    ngx_core_conf_t  *ccf = conf;\n\n    char             *group;\n    struct passwd    *pwd;\n    struct group     *grp;\n    ngx_str_t        *value;\n\n    if (ccf->user != (uid_t) NGX_CONF_UNSET_UINT) {\n        return \"is duplicate\";\n    }\n\n    if (geteuid() != 0) {\n        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                           \"the \\\"user\\\" directive makes sense only \"\n                           \"if the master process runs \"\n                           \"with super-user privileges, ignored\");\n        return NGX_CONF_OK;\n    }\n\n    value = cf->args->elts;\n\n    ccf->username = (char *) value[1].data;\n\n    ngx_set_errno(0);\n    pwd = getpwnam((const char *) value[1].data);\n    if (pwd == NULL) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno,\n                           \"getpwnam(\\\"%s\\\") failed\", value[1].data);\n        return NGX_CONF_ERROR;\n    }\n\n    ccf->user = pwd->pw_uid;\n\n    group = (char *) ((cf->args->nelts == 2) ? value[1].data : value[2].data);\n\n    ngx_set_errno(0);\n    grp = getgrnam(group);\n    if (grp == NULL) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno,\n                           \"getgrnam(\\\"%s\\\") failed\", group);\n        return NGX_CONF_ERROR;\n    }\n\n    ccf->group = grp->gr_gid;\n\n    return NGX_CONF_OK;\n\n#endif\n}\n\n\nstatic char *\nngx_set_env(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_core_conf_t  *ccf = conf;\n\n    ngx_str_t   *value, *var;\n    ngx_uint_t   i;\n\n    var = ngx_array_push(&ccf->env);\n    if (var == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    value = cf->args->elts;\n    *var = value[1];\n\n    for (i = 0; i < value[1].len; i++) {\n\n        if (value[1].data[i] == '=') {\n\n            var->len = i;\n\n            return NGX_CONF_OK;\n        }\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_set_priority(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_core_conf_t  *ccf = conf;\n\n    ngx_str_t        *value;\n    ngx_uint_t        n, minus;\n\n    if (ccf->priority != 0) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    if (value[1].data[0] == '-') {\n        n = 1;\n        minus = 1;\n\n    } else if (value[1].data[0] == '+') {\n        n = 1;\n        minus = 0;\n\n    } else {\n        n = 0;\n        minus = 0;\n    }\n\n    ccf->priority = ngx_atoi(&value[1].data[n], value[1].len - n);\n    if (ccf->priority == NGX_ERROR) {\n        return \"invalid number\";\n    }\n\n    if (minus) {\n        ccf->priority = -ccf->priority;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_set_cpu_affinity(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n#if (NGX_HAVE_CPU_AFFINITY)\n    ngx_core_conf_t  *ccf = conf;\n\n    u_char            ch, *p;\n    ngx_str_t        *value;\n    ngx_uint_t        i, n;\n    ngx_cpuset_t     *mask;\n\n    if (ccf->cpu_affinity) {\n        return \"is duplicate\";\n    }\n\n    mask = ngx_palloc(cf->pool, (cf->args->nelts - 1) * sizeof(ngx_cpuset_t));\n    if (mask == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    ccf->cpu_affinity_n = cf->args->nelts - 1;\n    ccf->cpu_affinity = mask;\n\n    value = cf->args->elts;\n\n    if (ngx_strcmp(value[1].data, \"auto\") == 0) {\n\n        if (cf->args->nelts > 3) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"invalid number of arguments in \"\n                               \"\\\"worker_cpu_affinity\\\" directive\");\n            return NGX_CONF_ERROR;\n        }\n\n        ccf->cpu_affinity_auto = 1;\n\n        CPU_ZERO(&mask[0]);\n        for (i = 0; i < (ngx_uint_t) ngx_min(ngx_ncpu, CPU_SETSIZE); i++) {\n            CPU_SET(i, &mask[0]);\n        }\n\n        n = 2;\n\n    } else {\n        n = 1;\n    }\n\n    for ( /* void */ ; n < cf->args->nelts; n++) {\n\n        if (value[n].len > CPU_SETSIZE) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                         \"\\\"worker_cpu_affinity\\\" supports up to %d CPUs only\",\n                         CPU_SETSIZE);\n            return NGX_CONF_ERROR;\n        }\n\n        i = 0;\n        CPU_ZERO(&mask[n - 1]);\n\n        for (p = value[n].data + value[n].len - 1;\n             p >= value[n].data;\n             p--)\n        {\n            ch = *p;\n\n            if (ch == ' ') {\n                continue;\n            }\n\n            i++;\n\n            if (ch == '0') {\n                continue;\n            }\n\n            if (ch == '1') {\n                CPU_SET(i - 1, &mask[n - 1]);\n                continue;\n            }\n\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                          \"invalid character \\\"%c\\\" in \\\"worker_cpu_affinity\\\"\",\n                          ch);\n            return NGX_CONF_ERROR;\n        }\n    }\n\n#else\n\n    ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                       \"\\\"worker_cpu_affinity\\\" is not supported \"\n                       \"on this platform, ignored\");\n#endif\n\n    return NGX_CONF_OK;\n}\n\n\nngx_cpuset_t *\nngx_get_cpu_affinity(ngx_uint_t n)\n{\n#if (NGX_HAVE_CPU_AFFINITY)\n    ngx_uint_t        i, j;\n    ngx_cpuset_t     *mask;\n    ngx_core_conf_t  *ccf;\n\n    static ngx_cpuset_t  result;\n\n    ccf = (ngx_core_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx,\n                                           ngx_core_module);\n\n    if (ccf->cpu_affinity == NULL) {\n        return NULL;\n    }\n\n    if (ccf->cpu_affinity_auto) {\n        mask = &ccf->cpu_affinity[ccf->cpu_affinity_n - 1];\n\n        for (i = 0, j = n; /* void */ ; i++) {\n\n            if (CPU_ISSET(i % CPU_SETSIZE, mask) && j-- == 0) {\n                break;\n            }\n\n            if (i == CPU_SETSIZE && j == n) {\n                /* empty mask */\n                return NULL;\n            }\n\n            /* void */\n        }\n\n        CPU_ZERO(&result);\n        CPU_SET(i % CPU_SETSIZE, &result);\n\n        return &result;\n    }\n\n    if (ccf->cpu_affinity_n > n) {\n        return &ccf->cpu_affinity[n];\n    }\n\n    return &ccf->cpu_affinity[ccf->cpu_affinity_n - 1];\n\n#else\n\n    return NULL;\n\n#endif\n}\n\n\nstatic char *\nngx_set_worker_processes(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_str_t        *value;\n    ngx_core_conf_t  *ccf;\n\n    ccf = (ngx_core_conf_t *) conf;\n\n    if (ccf->worker_processes != NGX_CONF_UNSET) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    if (ngx_strcmp(value[1].data, \"auto\") == 0) {\n        ccf->worker_processes = ngx_ncpu;\n        return NGX_CONF_OK;\n    }\n\n    ccf->worker_processes = ngx_atoi(value[1].data, value[1].len);\n\n    if (ccf->worker_processes == NGX_ERROR) {\n        return \"invalid value\";\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_load_module(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n#if (NGX_HAVE_DLOPEN)\n    void                *handle;\n    char               **names, **order;\n    ngx_str_t           *value, file;\n    ngx_uint_t           i;\n    ngx_module_t        *module, **modules;\n    ngx_pool_cleanup_t  *cln;\n\n    if (cf->cycle->modules_used) {\n        return \"is specified too late\";\n    }\n\n    value = cf->args->elts;\n\n    file = value[1];\n\n    if (ngx_conf_full_name(cf->cycle, &file, 0) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    cln = ngx_pool_cleanup_add(cf->cycle->pool, 0);\n    if (cln == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    handle = ngx_dlopen(file.data);\n    if (handle == NULL) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           ngx_dlopen_n \" \\\"%s\\\" failed (%s)\",\n                           file.data, ngx_dlerror());\n        return NGX_CONF_ERROR;\n    }\n\n    cln->handler = ngx_unload_module;\n    cln->data = handle;\n\n    modules = ngx_dlsym(handle, \"ngx_modules\");\n    if (modules == NULL) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           ngx_dlsym_n \" \\\"%V\\\", \\\"%s\\\" failed (%s)\",\n                           &value[1], \"ngx_modules\", ngx_dlerror());\n        return NGX_CONF_ERROR;\n    }\n\n    names = ngx_dlsym(handle, \"ngx_module_names\");\n    if (names == NULL) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           ngx_dlsym_n \" \\\"%V\\\", \\\"%s\\\" failed (%s)\",\n                           &value[1], \"ngx_module_names\", ngx_dlerror());\n        return NGX_CONF_ERROR;\n    }\n\n    order = ngx_dlsym(handle, \"ngx_module_order\");\n\n    for (i = 0; modules[i]; i++) {\n        module = modules[i];\n        module->name = names[i];\n\n        if (ngx_add_module(cf, &file, module, order) != NGX_OK) {\n            return NGX_CONF_ERROR;\n        }\n\n        ngx_log_debug2(NGX_LOG_DEBUG_CORE, cf->log, 0, \"module: %s i:%ui\",\n                       module->name, module->index);\n    }\n\n    return NGX_CONF_OK;\n\n#else\n\n    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                       \"\\\"load_module\\\" is not supported \"\n                       \"on this platform\");\n    return NGX_CONF_ERROR;\n\n#endif\n}\n\n\n#if (NGX_HAVE_DLOPEN)\n\nstatic void\nngx_unload_module(void *data)\n{\n    void  *handle = data;\n\n    if (ngx_dlclose(handle) != 0) {\n        ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,\n                      ngx_dlclose_n \" failed (%s)\", ngx_dlerror());\n    }\n}\n\n#endif\n\n\n#if (T_NGX_MASTER_ENV)\n\nstatic char *\nngx_master_set_env(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    size_t       klen, vlen;\n    u_char      *key, *v, *p;\n    ngx_str_t   *value;\n\n    value = cf->args->elts;\n    p = ngx_strlchr(value[1].data, value[1].data + value[1].len, '=');\n    if (p == NULL) {\n        klen = value[1].len;\n        v = (u_char *) \"1\";\n\n    } else {\n        klen = p - value[1].data;\n        vlen = value[1].len - klen - 1;\n        v = ngx_palloc(cf->pool, vlen + 1);\n        if (v == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        ngx_memcpy(v, p + 1, vlen);\n        v[vlen] = '\\0';\n    }\n\n    key = ngx_palloc(cf->pool, klen + 1);\n    if (key == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_memcpy(key, value[1].data, klen);\n    key[klen] = '\\0';\n\n    setenv((char *) key, (char *) v, 0);\n\n    return NGX_CONF_OK;\n}\n\n#endif\n"
  },
  {
    "path": "src/core/nginx.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGINX_H_INCLUDED_\n#define _NGINX_H_INCLUDED_\n\n\n#define nginx_version      1024000\n#define NGINX_VERSION      \"1.24.0\"\n#define NGINX_VER          \"nginx/\" NGINX_VERSION\n\n#define TENGINE            \"Tengine\"\n#define tengine_version    3001000\n#define TENGINE_VERSION    \"3.1.0\"\n#define TENGINE_VER        TENGINE \"/\" TENGINE_VERSION\n\n#ifdef NGX_BUILD\n#define NGINX_VER_BUILD    NGINX_VER \" (\" NGX_BUILD \")\"\n#define TENGINE_VER_BUILD  TENGINE_VER \" (\" NGX_BUILD \")\"\n#else\n#define NGINX_VER_BUILD    NGINX_VER\n#define TENGINE_VER_BUILD  TENGINE_VER\n#endif\n\n#define NGINX_VAR          \"NGINX\"\n#define NGX_OLDPID_EXT     \".oldbin\"\n\n\n#endif /* _NGINX_H_INCLUDED_ */\n"
  },
  {
    "path": "src/core/ngx_array.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\nngx_array_t *\nngx_array_create(ngx_pool_t *p, ngx_uint_t n, size_t size)\n{\n    ngx_array_t *a;\n\n    a = ngx_palloc(p, sizeof(ngx_array_t));\n    if (a == NULL) {\n        return NULL;\n    }\n\n    if (ngx_array_init(a, p, n, size) != NGX_OK) {\n        return NULL;\n    }\n\n    return a;\n}\n\n\nvoid\nngx_array_destroy(ngx_array_t *a)\n{\n    ngx_pool_t  *p;\n\n    p = a->pool;\n\n    if ((u_char *) a->elts + a->size * a->nalloc == p->d.last) {\n        p->d.last -= a->size * a->nalloc;\n    }\n\n    if ((u_char *) a + sizeof(ngx_array_t) == p->d.last) {\n        p->d.last = (u_char *) a;\n    }\n}\n\n\nvoid *\nngx_array_push(ngx_array_t *a)\n{\n    void        *elt, *new;\n    size_t       size;\n    ngx_pool_t  *p;\n\n    if (a->nelts == a->nalloc) {\n\n        /* the array is full */\n\n        size = a->size * a->nalloc;\n\n        p = a->pool;\n\n        if ((u_char *) a->elts + size == p->d.last\n            && p->d.last + a->size <= p->d.end)\n        {\n            /*\n             * the array allocation is the last in the pool\n             * and there is space for new allocation\n             */\n\n            p->d.last += a->size;\n            a->nalloc++;\n\n        } else {\n            /* allocate a new array */\n\n            new = ngx_palloc(p, 2 * size);\n            if (new == NULL) {\n                return NULL;\n            }\n\n            ngx_memcpy(new, a->elts, size);\n            a->elts = new;\n            a->nalloc *= 2;\n        }\n    }\n\n    elt = (u_char *) a->elts + a->size * a->nelts;\n    a->nelts++;\n\n    return elt;\n}\n\n\nvoid *\nngx_array_push_n(ngx_array_t *a, ngx_uint_t n)\n{\n    void        *elt, *new;\n    size_t       size;\n    ngx_uint_t   nalloc;\n    ngx_pool_t  *p;\n\n    size = n * a->size;\n\n    if (a->nelts + n > a->nalloc) {\n\n        /* the array is full */\n\n        p = a->pool;\n\n        if ((u_char *) a->elts + a->size * a->nalloc == p->d.last\n            && p->d.last + size <= p->d.end)\n        {\n            /*\n             * the array allocation is the last in the pool\n             * and there is space for new allocation\n             */\n\n            p->d.last += size;\n            a->nalloc += n;\n\n        } else {\n            /* allocate a new array */\n\n            nalloc = 2 * ((n >= a->nalloc) ? n : a->nalloc);\n\n            new = ngx_palloc(p, nalloc * a->size);\n            if (new == NULL) {\n                return NULL;\n            }\n\n            ngx_memcpy(new, a->elts, a->nelts * a->size);\n            a->elts = new;\n            a->nalloc = nalloc;\n        }\n    }\n\n    elt = (u_char *) a->elts + a->size * a->nelts;\n    a->nelts += n;\n\n    return elt;\n}\n"
  },
  {
    "path": "src/core/ngx_array.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_ARRAY_H_INCLUDED_\n#define _NGX_ARRAY_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\ntypedef struct {\n    void        *elts;\n    ngx_uint_t   nelts;\n    size_t       size;\n    ngx_uint_t   nalloc;\n    ngx_pool_t  *pool;\n} ngx_array_t;\n\n\nngx_array_t *ngx_array_create(ngx_pool_t *p, ngx_uint_t n, size_t size);\nvoid ngx_array_destroy(ngx_array_t *a);\nvoid *ngx_array_push(ngx_array_t *a);\nvoid *ngx_array_push_n(ngx_array_t *a, ngx_uint_t n);\n\n\nstatic ngx_inline ngx_int_t\nngx_array_init(ngx_array_t *array, ngx_pool_t *pool, ngx_uint_t n, size_t size)\n{\n    /*\n     * set \"array->nelts\" before \"array->elts\", otherwise MSVC thinks\n     * that \"array->nelts\" may be used without having been initialized\n     */\n\n    array->nelts = 0;\n    array->size = size;\n    array->nalloc = n;\n    array->pool = pool;\n\n    array->elts = ngx_palloc(pool, n * size);\n    if (array->elts == NULL) {\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\n#endif /* _NGX_ARRAY_H_INCLUDED_ */\n"
  },
  {
    "path": "src/core/ngx_buf.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\nngx_buf_t *\nngx_create_temp_buf(ngx_pool_t *pool, size_t size)\n{\n    ngx_buf_t *b;\n\n    b = ngx_calloc_buf(pool);\n    if (b == NULL) {\n        return NULL;\n    }\n\n    b->start = ngx_palloc(pool, size);\n    if (b->start == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_calloc_buf():\n     *\n     *     b->file_pos = 0;\n     *     b->file_last = 0;\n     *     b->file = NULL;\n     *     b->shadow = NULL;\n     *     b->tag = 0;\n     *     and flags\n     */\n\n    b->pos = b->start;\n    b->last = b->start;\n    b->end = b->last + size;\n    b->temporary = 1;\n\n    return b;\n}\n\n\nngx_chain_t *\nngx_alloc_chain_link(ngx_pool_t *pool)\n{\n    ngx_chain_t  *cl;\n\n    cl = pool->chain;\n\n    if (cl) {\n        pool->chain = cl->next;\n        return cl;\n    }\n\n    cl = ngx_palloc(pool, sizeof(ngx_chain_t));\n    if (cl == NULL) {\n        return NULL;\n    }\n\n    return cl;\n}\n\n\nngx_chain_t *\nngx_create_chain_of_bufs(ngx_pool_t *pool, ngx_bufs_t *bufs)\n{\n    u_char       *p;\n    ngx_int_t     i;\n    ngx_buf_t    *b;\n    ngx_chain_t  *chain, *cl, **ll;\n\n    p = ngx_palloc(pool, bufs->num * bufs->size);\n    if (p == NULL) {\n        return NULL;\n    }\n\n    ll = &chain;\n\n    for (i = 0; i < bufs->num; i++) {\n\n        b = ngx_calloc_buf(pool);\n        if (b == NULL) {\n            return NULL;\n        }\n\n        /*\n         * set by ngx_calloc_buf():\n         *\n         *     b->file_pos = 0;\n         *     b->file_last = 0;\n         *     b->file = NULL;\n         *     b->shadow = NULL;\n         *     b->tag = 0;\n         *     and flags\n         *\n         */\n\n        b->pos = p;\n        b->last = p;\n        b->temporary = 1;\n\n        b->start = p;\n        p += bufs->size;\n        b->end = p;\n\n        cl = ngx_alloc_chain_link(pool);\n        if (cl == NULL) {\n            return NULL;\n        }\n\n        cl->buf = b;\n        *ll = cl;\n        ll = &cl->next;\n    }\n\n    *ll = NULL;\n\n    return chain;\n}\n\n\nngx_int_t\nngx_chain_add_copy(ngx_pool_t *pool, ngx_chain_t **chain, ngx_chain_t *in)\n{\n    ngx_chain_t  *cl, **ll;\n\n    ll = chain;\n\n    for (cl = *chain; cl; cl = cl->next) {\n        ll = &cl->next;\n    }\n\n    while (in) {\n        cl = ngx_alloc_chain_link(pool);\n        if (cl == NULL) {\n            *ll = NULL;\n            return NGX_ERROR;\n        }\n\n        cl->buf = in->buf;\n        *ll = cl;\n        ll = &cl->next;\n        in = in->next;\n    }\n\n    *ll = NULL;\n\n    return NGX_OK;\n}\n\n\nngx_chain_t *\nngx_chain_get_free_buf(ngx_pool_t *p, ngx_chain_t **free)\n{\n    ngx_chain_t  *cl;\n\n    if (*free) {\n        cl = *free;\n        *free = cl->next;\n        cl->next = NULL;\n        return cl;\n    }\n\n    cl = ngx_alloc_chain_link(p);\n    if (cl == NULL) {\n        return NULL;\n    }\n\n    cl->buf = ngx_calloc_buf(p);\n    if (cl->buf == NULL) {\n        return NULL;\n    }\n\n    cl->next = NULL;\n\n    return cl;\n}\n\n\nvoid\nngx_chain_update_chains(ngx_pool_t *p, ngx_chain_t **free, ngx_chain_t **busy,\n    ngx_chain_t **out, ngx_buf_tag_t tag)\n{\n    ngx_chain_t  *cl;\n\n    if (*out) {\n        if (*busy == NULL) {\n            *busy = *out;\n\n        } else {\n            for (cl = *busy; cl->next; cl = cl->next) { /* void */ }\n\n            cl->next = *out;\n        }\n\n        *out = NULL;\n    }\n\n    while (*busy) {\n        cl = *busy;\n\n        if (cl->buf->tag != tag) {\n            *busy = cl->next;\n            ngx_free_chain(p, cl);\n            continue;\n        }\n\n        if (ngx_buf_size(cl->buf) != 0) {\n            break;\n        }\n\n        cl->buf->pos = cl->buf->start;\n        cl->buf->last = cl->buf->start;\n\n        *busy = cl->next;\n        cl->next = *free;\n        *free = cl;\n    }\n}\n\n\noff_t\nngx_chain_coalesce_file(ngx_chain_t **in, off_t limit)\n{\n    off_t         total, size, aligned, fprev;\n    ngx_fd_t      fd;\n    ngx_chain_t  *cl;\n\n    total = 0;\n\n    cl = *in;\n    fd = cl->buf->file->fd;\n\n    do {\n        size = cl->buf->file_last - cl->buf->file_pos;\n\n        if (size > limit - total) {\n            size = limit - total;\n\n            aligned = (cl->buf->file_pos + size + ngx_pagesize - 1)\n                       & ~((off_t) ngx_pagesize - 1);\n\n            if (aligned <= cl->buf->file_last) {\n                size = aligned - cl->buf->file_pos;\n            }\n\n            total += size;\n            break;\n        }\n\n        total += size;\n        fprev = cl->buf->file_pos + size;\n        cl = cl->next;\n\n    } while (cl\n             && cl->buf->in_file\n             && total < limit\n             && fd == cl->buf->file->fd\n             && fprev == cl->buf->file_pos);\n\n    *in = cl;\n\n    return total;\n}\n\n\nngx_chain_t *\nngx_chain_update_sent(ngx_chain_t *in, off_t sent)\n{\n    off_t  size;\n\n    for ( /* void */ ; in; in = in->next) {\n\n        if (ngx_buf_special(in->buf)) {\n            continue;\n        }\n\n        if (sent == 0) {\n            break;\n        }\n\n        size = ngx_buf_size(in->buf);\n\n        if (sent >= size) {\n            sent -= size;\n\n            if (ngx_buf_in_memory(in->buf)) {\n                in->buf->pos = in->buf->last;\n            }\n\n            if (in->buf->in_file) {\n                in->buf->file_pos = in->buf->file_last;\n            }\n\n            continue;\n        }\n\n        if (ngx_buf_in_memory(in->buf)) {\n            in->buf->pos += (size_t) sent;\n        }\n\n        if (in->buf->in_file) {\n            in->buf->file_pos += sent;\n        }\n\n        break;\n    }\n\n    return in;\n}\n"
  },
  {
    "path": "src/core/ngx_buf.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_BUF_H_INCLUDED_\n#define _NGX_BUF_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\ntypedef void *            ngx_buf_tag_t;\n\ntypedef struct ngx_buf_s  ngx_buf_t;\n\nstruct ngx_buf_s {\n    u_char          *pos;\n    u_char          *last;\n    off_t            file_pos;\n    off_t            file_last;\n\n    u_char          *start;         /* start of buffer */\n    u_char          *end;           /* end of buffer */\n    ngx_buf_tag_t    tag;\n    ngx_file_t      *file;\n    ngx_buf_t       *shadow;\n\n\n    /* the buf's content could be changed */\n    unsigned         temporary:1;\n\n    /*\n     * the buf's content is in a memory cache or in a read only memory\n     * and must not be changed\n     */\n    unsigned         memory:1;\n\n    /* the buf's content is mmap()ed and must not be changed */\n    unsigned         mmap:1;\n\n    unsigned         recycled:1;\n    unsigned         in_file:1;\n    unsigned         flush:1;\n    unsigned         sync:1;\n    unsigned         last_buf:1;\n    unsigned         last_in_chain:1;\n\n    unsigned         last_shadow:1;\n    unsigned         temp_file:1;\n\n    /* STUB */ int   num;\n};\n\n\nstruct ngx_chain_s {\n    ngx_buf_t    *buf;\n    ngx_chain_t  *next;\n};\n\n\ntypedef struct {\n    ngx_int_t    num;\n    size_t       size;\n} ngx_bufs_t;\n\n\ntypedef struct ngx_output_chain_ctx_s  ngx_output_chain_ctx_t;\n\ntypedef ngx_int_t (*ngx_output_chain_filter_pt)(void *ctx, ngx_chain_t *in);\n\ntypedef void (*ngx_output_chain_aio_pt)(ngx_output_chain_ctx_t *ctx,\n    ngx_file_t *file);\n\nstruct ngx_output_chain_ctx_s {\n    ngx_buf_t                   *buf;\n    ngx_chain_t                 *in;\n    ngx_chain_t                 *free;\n    ngx_chain_t                 *busy;\n\n    unsigned                     sendfile:1;\n    unsigned                     directio:1;\n    unsigned                     unaligned:1;\n    unsigned                     need_in_memory:1;\n    unsigned                     need_in_temp:1;\n    unsigned                     aio:1;\n\n#if (NGX_HAVE_FILE_AIO || NGX_COMPAT)\n    ngx_output_chain_aio_pt      aio_handler;\n#endif\n\n#if (NGX_THREADS || NGX_COMPAT)\n    ngx_int_t                  (*thread_handler)(ngx_thread_task_t *task,\n                                                 ngx_file_t *file);\n    ngx_thread_task_t           *thread_task;\n#endif\n\n    off_t                        alignment;\n\n    ngx_pool_t                  *pool;\n    ngx_int_t                    allocated;\n    ngx_bufs_t                   bufs;\n    ngx_buf_tag_t                tag;\n\n    ngx_output_chain_filter_pt   output_filter;\n    void                        *filter_ctx;\n};\n\n\ntypedef struct {\n    ngx_chain_t                 *out;\n    ngx_chain_t                **last;\n    ngx_connection_t            *connection;\n    ngx_pool_t                  *pool;\n    off_t                        limit;\n} ngx_chain_writer_ctx_t;\n\n\n#define NGX_CHAIN_ERROR     (ngx_chain_t *) NGX_ERROR\n\n\n#define ngx_buf_in_memory(b)       ((b)->temporary || (b)->memory || (b)->mmap)\n#define ngx_buf_in_memory_only(b)  (ngx_buf_in_memory(b) && !(b)->in_file)\n\n#define ngx_buf_special(b)                                                   \\\n    (((b)->flush || (b)->last_buf || (b)->sync)                              \\\n     && !ngx_buf_in_memory(b) && !(b)->in_file)\n\n#define ngx_buf_sync_only(b)                                                 \\\n    ((b)->sync && !ngx_buf_in_memory(b)                                      \\\n     && !(b)->in_file && !(b)->flush && !(b)->last_buf)\n\n#define ngx_buf_size(b)                                                      \\\n    (ngx_buf_in_memory(b) ? (off_t) ((b)->last - (b)->pos):                  \\\n                            ((b)->file_last - (b)->file_pos))\n\nngx_buf_t *ngx_create_temp_buf(ngx_pool_t *pool, size_t size);\nngx_chain_t *ngx_create_chain_of_bufs(ngx_pool_t *pool, ngx_bufs_t *bufs);\n\n\n#define ngx_alloc_buf(pool)  ngx_palloc(pool, sizeof(ngx_buf_t))\n#define ngx_calloc_buf(pool) ngx_pcalloc(pool, sizeof(ngx_buf_t))\n\nngx_chain_t *ngx_alloc_chain_link(ngx_pool_t *pool);\n#define ngx_free_chain(pool, cl)                                             \\\n    (cl)->next = (pool)->chain;                                              \\\n    (pool)->chain = (cl)\n\n\n\nngx_int_t ngx_output_chain(ngx_output_chain_ctx_t *ctx, ngx_chain_t *in);\nngx_int_t ngx_chain_writer(void *ctx, ngx_chain_t *in);\n\nngx_int_t ngx_chain_add_copy(ngx_pool_t *pool, ngx_chain_t **chain,\n    ngx_chain_t *in);\nngx_chain_t *ngx_chain_get_free_buf(ngx_pool_t *p, ngx_chain_t **free);\nvoid ngx_chain_update_chains(ngx_pool_t *p, ngx_chain_t **free,\n    ngx_chain_t **busy, ngx_chain_t **out, ngx_buf_tag_t tag);\n\noff_t ngx_chain_coalesce_file(ngx_chain_t **in, off_t limit);\n\nngx_chain_t *ngx_chain_update_sent(ngx_chain_t *in, off_t sent);\n\n#endif /* _NGX_BUF_H_INCLUDED_ */\n"
  },
  {
    "path": "src/core/ngx_conf_file.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n#define NGX_CONF_BUFFER  4096\n\nstatic ngx_int_t ngx_conf_add_dump(ngx_conf_t *cf, ngx_str_t *filename);\nstatic ngx_int_t ngx_conf_handler(ngx_conf_t *cf, ngx_int_t last);\nstatic ngx_int_t ngx_conf_read_token(ngx_conf_t *cf);\nstatic void ngx_conf_flush_files(ngx_cycle_t *cycle);\n\n\nstatic ngx_command_t  ngx_conf_commands[] = {\n\n    { ngx_string(\"include\"),\n      NGX_ANY_CONF|NGX_CONF_TAKE1,\n      ngx_conf_include,\n      0,\n      0,\n      NULL },\n\n      ngx_null_command\n};\n\n\nngx_module_t  ngx_conf_module = {\n    NGX_MODULE_V1,\n    NULL,                                  /* module context */\n    ngx_conf_commands,                     /* module directives */\n    NGX_CONF_MODULE,                       /* module type */\n    NULL,                                  /* init master */\n    NULL,                                  /* init module */\n    NULL,                                  /* init process */\n    NULL,                                  /* init thread */\n    NULL,                                  /* exit thread */\n    ngx_conf_flush_files,                  /* exit process */\n    NULL,                                  /* exit master */\n    NGX_MODULE_V1_PADDING\n};\n\n\n/* The eight fixed arguments */\n\nstatic ngx_uint_t argument_number[] = {\n    NGX_CONF_NOARGS,\n    NGX_CONF_TAKE1,\n    NGX_CONF_TAKE2,\n    NGX_CONF_TAKE3,\n    NGX_CONF_TAKE4,\n    NGX_CONF_TAKE5,\n    NGX_CONF_TAKE6,\n    NGX_CONF_TAKE7\n};\n\n\nchar *\nngx_conf_param(ngx_conf_t *cf)\n{\n    char             *rv;\n    ngx_str_t        *param;\n    ngx_buf_t         b;\n    ngx_conf_file_t   conf_file;\n\n    param = &cf->cycle->conf_param;\n\n    if (param->len == 0) {\n        return NGX_CONF_OK;\n    }\n\n    ngx_memzero(&conf_file, sizeof(ngx_conf_file_t));\n\n    ngx_memzero(&b, sizeof(ngx_buf_t));\n\n    b.start = param->data;\n    b.pos = param->data;\n    b.last = param->data + param->len;\n    b.end = b.last;\n    b.temporary = 1;\n\n    conf_file.file.fd = NGX_INVALID_FILE;\n    conf_file.file.name.data = NULL;\n    conf_file.line = 0;\n\n    cf->conf_file = &conf_file;\n    cf->conf_file->buffer = &b;\n\n    rv = ngx_conf_parse(cf, NULL);\n\n    cf->conf_file = NULL;\n\n    return rv;\n}\n\n\nstatic ngx_int_t\nngx_conf_add_dump(ngx_conf_t *cf, ngx_str_t *filename)\n{\n    off_t             size;\n    u_char           *p;\n    uint32_t          hash;\n    ngx_buf_t        *buf;\n    ngx_str_node_t   *sn;\n    ngx_conf_dump_t  *cd;\n\n    hash = ngx_crc32_long(filename->data, filename->len);\n\n    sn = ngx_str_rbtree_lookup(&cf->cycle->config_dump_rbtree, filename, hash);\n\n    if (sn) {\n        cf->conf_file->dump = NULL;\n        return NGX_OK;\n    }\n\n    p = ngx_pstrdup(cf->cycle->pool, filename);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    cd = ngx_array_push(&cf->cycle->config_dump);\n    if (cd == NULL) {\n        return NGX_ERROR;\n    }\n\n    size = ngx_file_size(&cf->conf_file->file.info);\n\n    buf = ngx_create_temp_buf(cf->cycle->pool, (size_t) size);\n    if (buf == NULL) {\n        return NGX_ERROR;\n    }\n\n    cd->name.data = p;\n    cd->name.len = filename->len;\n    cd->buffer = buf;\n\n    cf->conf_file->dump = buf;\n\n    sn = ngx_palloc(cf->temp_pool, sizeof(ngx_str_node_t));\n    if (sn == NULL) {\n        return NGX_ERROR;\n    }\n\n    sn->node.key = hash;\n    sn->str = cd->name;\n\n    ngx_rbtree_insert(&cf->cycle->config_dump_rbtree, &sn->node);\n\n    return NGX_OK;\n}\n\n\nchar *\nngx_conf_parse(ngx_conf_t *cf, ngx_str_t *filename)\n{\n    char             *rv;\n    ngx_fd_t          fd;\n    ngx_int_t         rc;\n    ngx_buf_t         buf;\n    ngx_conf_file_t  *prev, conf_file;\n    enum {\n        parse_file = 0,\n        parse_block,\n        parse_param\n    } type;\n\n#if (NGX_SUPPRESS_WARN)\n    fd = NGX_INVALID_FILE;\n    prev = NULL;\n#endif\n\n    if (filename) {\n\n        /* open configuration file */\n\n        fd = ngx_open_file(filename->data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);\n\n        if (fd == NGX_INVALID_FILE) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno,\n                               ngx_open_file_n \" \\\"%s\\\" failed\",\n                               filename->data);\n            return NGX_CONF_ERROR;\n        }\n\n        prev = cf->conf_file;\n\n        cf->conf_file = &conf_file;\n\n        if (ngx_fd_info(fd, &cf->conf_file->file.info) == NGX_FILE_ERROR) {\n            ngx_log_error(NGX_LOG_EMERG, cf->log, ngx_errno,\n                          ngx_fd_info_n \" \\\"%s\\\" failed\", filename->data);\n        }\n\n        cf->conf_file->buffer = &buf;\n\n        buf.start = ngx_alloc(NGX_CONF_BUFFER, cf->log);\n        if (buf.start == NULL) {\n            goto failed;\n        }\n\n        buf.pos = buf.start;\n        buf.last = buf.start;\n        buf.end = buf.last + NGX_CONF_BUFFER;\n        buf.temporary = 1;\n\n        cf->conf_file->file.fd = fd;\n        cf->conf_file->file.name.len = filename->len;\n        cf->conf_file->file.name.data = filename->data;\n        cf->conf_file->file.offset = 0;\n        cf->conf_file->file.log = cf->log;\n        cf->conf_file->line = 1;\n\n        type = parse_file;\n\n        if (ngx_dump_config\n#if (NGX_DEBUG)\n            || 1\n#endif\n           )\n        {\n            if (ngx_conf_add_dump(cf, filename) != NGX_OK) {\n                goto failed;\n            }\n\n        } else {\n            cf->conf_file->dump = NULL;\n        }\n\n    } else if (cf->conf_file->file.fd != NGX_INVALID_FILE) {\n\n        type = parse_block;\n\n    } else {\n        type = parse_param;\n    }\n\n\n    for ( ;; ) {\n        rc = ngx_conf_read_token(cf);\n\n        /*\n         * ngx_conf_read_token() may return\n         *\n         *    NGX_ERROR             there is error\n         *    NGX_OK                the token terminated by \";\" was found\n         *    NGX_CONF_BLOCK_START  the token terminated by \"{\" was found\n         *    NGX_CONF_BLOCK_DONE   the \"}\" was found\n         *    NGX_CONF_FILE_DONE    the configuration file is done\n         */\n\n        if (rc == NGX_ERROR) {\n            goto done;\n        }\n\n        if (rc == NGX_CONF_BLOCK_DONE) {\n\n            if (type != parse_block) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, \"unexpected \\\"}\\\"\");\n                goto failed;\n            }\n\n            goto done;\n        }\n\n        if (rc == NGX_CONF_FILE_DONE) {\n\n            if (type == parse_block) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"unexpected end of file, expecting \\\"}\\\"\");\n                goto failed;\n            }\n\n            goto done;\n        }\n\n        if (rc == NGX_CONF_BLOCK_START) {\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\n        /* rc == NGX_OK || rc == NGX_CONF_BLOCK_START */\n\n        if (cf->handler) {\n\n            /*\n             * the custom handler, i.e., that is used in the http's\n             * \"types { ... }\" directive\n             */\n\n            if (rc == NGX_CONF_BLOCK_START) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, \"unexpected \\\"{\\\"\");\n                goto failed;\n            }\n\n            rv = (*cf->handler)(cf, NULL, cf->handler_conf);\n            if (rv == NGX_CONF_OK) {\n                continue;\n            }\n\n            if (rv == NGX_CONF_ERROR) {\n                goto failed;\n            }\n\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, \"%s\", rv);\n\n            goto failed;\n        }\n\n\n        rc = ngx_conf_handler(cf, rc);\n\n        if (rc == NGX_ERROR) {\n            goto failed;\n        }\n    }\n\nfailed:\n\n    rc = NGX_ERROR;\n\ndone:\n\n    if (filename) {\n        if (cf->conf_file->buffer->start) {\n            ngx_free(cf->conf_file->buffer->start);\n        }\n\n        if (ngx_close_file(fd) == NGX_FILE_ERROR) {\n            ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,\n                          ngx_close_file_n \" %s failed\",\n                          filename->data);\n            rc = NGX_ERROR;\n        }\n\n        cf->conf_file = prev;\n    }\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_conf_handler(ngx_conf_t *cf, ngx_int_t last)\n{\n    char           *rv;\n    void           *conf, **confp;\n    ngx_uint_t      i, found;\n    ngx_str_t      *name;\n    ngx_command_t  *cmd;\n\n    name = cf->args->elts;\n\n    found = 0;\n\n    for (i = 0; cf->cycle->modules[i]; i++) {\n\n        cmd = cf->cycle->modules[i]->commands;\n        if (cmd == NULL) {\n            continue;\n        }\n\n        for ( /* void */ ; cmd->name.len; cmd++) {\n\n            if (name->len != cmd->name.len) {\n                continue;\n            }\n\n            if (ngx_strcmp(name->data, cmd->name.data) != 0) {\n                continue;\n            }\n\n            found = 1;\n\n            if (cf->cycle->modules[i]->type != NGX_CONF_MODULE\n                && cf->cycle->modules[i]->type != cf->module_type)\n            {\n                continue;\n            }\n\n            /* is the directive's location right ? */\n\n            if (!(cmd->type & cf->cmd_type)) {\n                continue;\n            }\n\n            if (!(cmd->type & NGX_CONF_BLOCK) && last != NGX_OK) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                  \"directive \\\"%s\\\" is not terminated by \\\";\\\"\",\n                                  name->data);\n                return NGX_ERROR;\n            }\n\n            if ((cmd->type & NGX_CONF_BLOCK) && last != NGX_CONF_BLOCK_START) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"directive \\\"%s\\\" has no opening \\\"{\\\"\",\n                                   name->data);\n                return NGX_ERROR;\n            }\n\n            /* is the directive's argument count right ? */\n\n            if (!(cmd->type & NGX_CONF_ANY)) {\n\n                if (cmd->type & NGX_CONF_FLAG) {\n\n                    if (cf->args->nelts != 2) {\n                        goto invalid;\n                    }\n\n                } else if (cmd->type & NGX_CONF_1MORE) {\n\n                    if (cf->args->nelts < 2) {\n                        goto invalid;\n                    }\n\n                } else if (cmd->type & NGX_CONF_2MORE) {\n\n                    if (cf->args->nelts < 3) {\n                        goto invalid;\n                    }\n\n                } else if (cf->args->nelts > NGX_CONF_MAX_ARGS) {\n\n                    goto invalid;\n\n                } else if (!(cmd->type & argument_number[cf->args->nelts - 1]))\n                {\n                    goto invalid;\n                }\n            }\n\n            /* set up the directive's configuration context */\n\n            conf = NULL;\n\n            if (cmd->type & NGX_DIRECT_CONF) {\n                conf = ((void **) cf->ctx)[cf->cycle->modules[i]->index];\n\n            } else if (cmd->type & NGX_MAIN_CONF) {\n                conf = &(((void **) cf->ctx)[cf->cycle->modules[i]->index]);\n\n            } else if (cf->ctx) {\n                confp = *(void **) ((char *) cf->ctx + cmd->conf);\n\n                if (confp) {\n                    conf = confp[cf->cycle->modules[i]->ctx_index];\n                }\n            }\n\n            rv = cmd->set(cf, cmd, conf);\n\n            if (rv == NGX_CONF_OK) {\n                return NGX_OK;\n            }\n\n            if (rv == NGX_CONF_ERROR) {\n                return NGX_ERROR;\n            }\n\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"\\\"%s\\\" directive %s\", name->data, rv);\n\n            return NGX_ERROR;\n        }\n    }\n\n    if (found) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"\\\"%s\\\" directive is not allowed here\", name->data);\n\n        return NGX_ERROR;\n    }\n\n    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                       \"unknown directive \\\"%s\\\"\", name->data);\n\n    return NGX_ERROR;\n\ninvalid:\n\n    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                       \"invalid number of arguments in \\\"%s\\\" directive\",\n                       name->data);\n\n    return NGX_ERROR;\n}\n\n\nstatic ngx_int_t\nngx_conf_read_token(ngx_conf_t *cf)\n{\n    u_char      *start, ch, *src, *dst;\n    off_t        file_size;\n    size_t       len;\n    ssize_t      n, size;\n    ngx_uint_t   found, need_space, last_space, sharp_comment, variable;\n    ngx_uint_t   quoted, s_quoted, d_quoted, start_line;\n    ngx_str_t   *word;\n    ngx_buf_t   *b, *dump;\n\n    found = 0;\n    need_space = 0;\n    last_space = 1;\n    sharp_comment = 0;\n    variable = 0;\n    quoted = 0;\n    s_quoted = 0;\n    d_quoted = 0;\n\n    cf->args->nelts = 0;\n    b = cf->conf_file->buffer;\n    dump = cf->conf_file->dump;\n    start = b->pos;\n    start_line = cf->conf_file->line;\n\n    file_size = ngx_file_size(&cf->conf_file->file.info);\n\n    for ( ;; ) {\n\n        if (b->pos >= b->last) {\n\n            if (cf->conf_file->file.offset >= file_size) {\n\n                if (cf->args->nelts > 0 || !last_space) {\n\n                    if (cf->conf_file->file.fd == NGX_INVALID_FILE) {\n                        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                           \"unexpected end of parameter, \"\n                                           \"expecting \\\";\\\"\");\n                        return NGX_ERROR;\n                    }\n\n                    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                       \"unexpected end of file, \"\n                                       \"expecting \\\";\\\" or \\\"}\\\"\");\n                    return NGX_ERROR;\n                }\n\n                return NGX_CONF_FILE_DONE;\n            }\n\n            len = b->pos - start;\n\n            if (len == NGX_CONF_BUFFER) {\n                cf->conf_file->line = start_line;\n\n                if (d_quoted) {\n                    ch = '\"';\n\n                } else if (s_quoted) {\n                    ch = '\\'';\n\n                } else {\n                    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                       \"too long parameter \\\"%*s...\\\" started\",\n                                       10, start);\n                    return NGX_ERROR;\n                }\n\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"too long parameter, probably \"\n                                   \"missing terminating \\\"%c\\\" character\", ch);\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 + len;\n            b->last = b->pos + n;\n            start = b->start;\n\n            if (dump) {\n                dump->last = ngx_cpymem(dump->last, b->pos, size);\n            }\n        }\n\n        ch = *b->pos++;\n\n        if (ch == LF) {\n            cf->conf_file->line++;\n\n            if (sharp_comment) {\n                sharp_comment = 0;\n            }\n        }\n\n        if (sharp_comment) {\n            continue;\n        }\n\n        if (quoted) {\n            quoted = 0;\n            continue;\n        }\n\n        if (need_space) {\n            if (ch == ' ' || ch == '\\t' || ch == CR || ch == LF) {\n                last_space = 1;\n                need_space = 0;\n                continue;\n            }\n\n            if (ch == ';') {\n                return NGX_OK;\n            }\n\n            if (ch == '{') {\n                return NGX_CONF_BLOCK_START;\n            }\n\n            if (ch == ')') {\n                last_space = 1;\n                need_space = 0;\n\n            } else {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"unexpected \\\"%c\\\"\", ch);\n                return NGX_ERROR;\n            }\n        }\n\n        if (last_space) {\n\n            start = b->pos - 1;\n            start_line = cf->conf_file->line;\n\n            if (ch == ' ' || ch == '\\t' || ch == CR || ch == LF) {\n                continue;\n            }\n\n            switch (ch) {\n\n            case ';':\n            case '{':\n                if (cf->args->nelts == 0) {\n                    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                       \"unexpected \\\"%c\\\"\", ch);\n                    return NGX_ERROR;\n                }\n\n                if (ch == '{') {\n                    return NGX_CONF_BLOCK_START;\n                }\n\n                return NGX_OK;\n\n            case '}':\n                if (cf->args->nelts != 0) {\n                    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                       \"unexpected \\\"}\\\"\");\n                    return NGX_ERROR;\n                }\n\n                return NGX_CONF_BLOCK_DONE;\n\n            case '#':\n                sharp_comment = 1;\n                continue;\n\n            case '\\\\':\n                quoted = 1;\n                last_space = 0;\n                continue;\n\n            case '\"':\n                start++;\n                d_quoted = 1;\n                last_space = 0;\n                continue;\n\n            case '\\'':\n                start++;\n                s_quoted = 1;\n                last_space = 0;\n                continue;\n\n            case '$':\n                variable = 1;\n                last_space = 0;\n                continue;\n\n            default:\n                last_space = 0;\n            }\n\n        } else {\n            if (ch == '{' && variable) {\n                continue;\n            }\n\n            variable = 0;\n\n            if (ch == '\\\\') {\n                quoted = 1;\n                continue;\n            }\n\n            if (ch == '$') {\n                variable = 1;\n                continue;\n            }\n\n            if (d_quoted) {\n                if (ch == '\"') {\n                    d_quoted = 0;\n                    need_space = 1;\n                    found = 1;\n                }\n\n            } else if (s_quoted) {\n                if (ch == '\\'') {\n                    s_quoted = 0;\n                    need_space = 1;\n                    found = 1;\n                }\n\n            } else if (ch == ' ' || ch == '\\t' || ch == CR || ch == LF\n                       || ch == ';' || ch == '{')\n            {\n                last_space = 1;\n                found = 1;\n            }\n\n            if (found) {\n                word = ngx_array_push(cf->args);\n                if (word == NULL) {\n                    return NGX_ERROR;\n                }\n\n                word->data = ngx_pnalloc(cf->pool, b->pos - 1 - start + 1);\n                if (word->data == NULL) {\n                    return NGX_ERROR;\n                }\n\n                for (dst = word->data, src = start, len = 0;\n                     src < b->pos - 1;\n                     len++)\n                {\n                    if (*src == '\\\\') {\n                        switch (src[1]) {\n                        case '\"':\n                        case '\\'':\n                        case '\\\\':\n                            src++;\n                            break;\n\n                        case 't':\n                            *dst++ = '\\t';\n                            src += 2;\n                            continue;\n\n                        case 'r':\n                            *dst++ = '\\r';\n                            src += 2;\n                            continue;\n\n                        case 'n':\n                            *dst++ = '\\n';\n                            src += 2;\n                            continue;\n                        }\n\n                    }\n                    *dst++ = *src++;\n                }\n                *dst = '\\0';\n                word->len = len;\n\n                if (ch == ';') {\n                    return NGX_OK;\n                }\n\n                if (ch == '{') {\n                    return NGX_CONF_BLOCK_START;\n                }\n\n                found = 0;\n            }\n        }\n    }\n}\n\n\nchar *\nngx_conf_include(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    char        *rv;\n    ngx_int_t    n;\n    ngx_str_t   *value, file, name;\n    ngx_glob_t   gl;\n\n    value = cf->args->elts;\n    file = value[1];\n\n    ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, \"include %s\", file.data);\n\n    if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (strpbrk((char *) file.data, \"*?[\") == NULL) {\n\n        ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, \"include %s\", file.data);\n\n        return ngx_conf_parse(cf, &file);\n    }\n\n    ngx_memzero(&gl, sizeof(ngx_glob_t));\n\n    gl.pattern = file.data;\n    gl.log = cf->log;\n    gl.test = 1;\n\n    if (ngx_open_glob(&gl) != NGX_OK) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno,\n                           ngx_open_glob_n \" \\\"%s\\\" failed\", file.data);\n        return NGX_CONF_ERROR;\n    }\n\n    rv = NGX_CONF_OK;\n\n    for ( ;; ) {\n        n = ngx_read_glob(&gl, &name);\n\n        if (n != NGX_OK) {\n            break;\n        }\n\n        file.len = name.len++;\n        file.data = ngx_pstrdup(cf->pool, &name);\n        if (file.data == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, \"include %s\", file.data);\n\n        rv = ngx_conf_parse(cf, &file);\n\n        if (rv != NGX_CONF_OK) {\n            break;\n        }\n    }\n\n    ngx_close_glob(&gl);\n\n    return rv;\n}\n\n\nngx_int_t\nngx_conf_full_name(ngx_cycle_t *cycle, ngx_str_t *name, ngx_uint_t conf_prefix)\n{\n    ngx_str_t  *prefix;\n\n    prefix = conf_prefix ? &cycle->conf_prefix : &cycle->prefix;\n\n    return ngx_get_full_name(cycle->pool, prefix, name);\n}\n\n\nngx_open_file_t *\nngx_conf_open_file(ngx_cycle_t *cycle, ngx_str_t *name)\n{\n    ngx_str_t         full;\n    ngx_uint_t        i;\n    ngx_list_part_t  *part;\n    ngx_open_file_t  *file;\n\n#if (NGX_SUPPRESS_WARN)\n    ngx_str_null(&full);\n#endif\n\n    if (name->len) {\n        full = *name;\n\n        if (ngx_conf_full_name(cycle, &full, 0) != NGX_OK) {\n            return NULL;\n        }\n\n        part = &cycle->open_files.part;\n        file = part->elts;\n\n        for (i = 0; /* void */ ; i++) {\n\n            if (i >= part->nelts) {\n                if (part->next == NULL) {\n                    break;\n                }\n                part = part->next;\n                file = part->elts;\n                i = 0;\n            }\n\n            if (full.len != file[i].name.len) {\n                continue;\n            }\n\n            if (ngx_strcmp(full.data, file[i].name.data) == 0) {\n                return &file[i];\n            }\n        }\n    }\n\n    file = ngx_list_push(&cycle->open_files);\n    if (file == NULL) {\n        return NULL;\n    }\n\n    if (name->len) {\n        file->fd = NGX_INVALID_FILE;\n        file->name = full;\n\n    } else {\n        file->fd = ngx_stderr;\n        file->name = *name;\n    }\n\n    file->flush = NULL;\n    file->data = NULL;\n\n    return file;\n}\n\n\nstatic void\nngx_conf_flush_files(ngx_cycle_t *cycle)\n{\n    ngx_uint_t        i;\n    ngx_list_part_t  *part;\n    ngx_open_file_t  *file;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_CORE, cycle->log, 0, \"flush files\");\n\n    part = &cycle->open_files.part;\n    file = part->elts;\n\n    for (i = 0; /* void */ ; i++) {\n\n        if (i >= part->nelts) {\n            if (part->next == NULL) {\n                break;\n            }\n            part = part->next;\n            file = part->elts;\n            i = 0;\n        }\n\n        if (file[i].flush) {\n            file[i].flush(&file[i], cycle->log);\n        }\n    }\n}\n\n\nvoid ngx_cdecl\nngx_conf_log_error(ngx_uint_t level, ngx_conf_t *cf, ngx_err_t err,\n    const char *fmt, ...)\n{\n    u_char   errstr[NGX_MAX_CONF_ERRSTR], *p, *last;\n    va_list  args;\n\n    last = errstr + NGX_MAX_CONF_ERRSTR;\n\n    va_start(args, fmt);\n    p = ngx_vslprintf(errstr, last, fmt, args);\n    va_end(args);\n\n    if (err) {\n        p = ngx_log_errno(p, last, err);\n    }\n\n    if (cf->conf_file == NULL) {\n        ngx_log_error(level, cf->log, 0, \"%*s\", p - errstr, errstr);\n        return;\n    }\n\n    if (cf->conf_file->file.fd == NGX_INVALID_FILE) {\n        ngx_log_error(level, cf->log, 0, \"%*s in command line\",\n                      p - errstr, errstr);\n        return;\n    }\n\n    ngx_log_error(level, cf->log, 0, \"%*s in %s:%ui\",\n                  p - errstr, errstr,\n                  cf->conf_file->file.name.data, cf->conf_file->line);\n}\n\n\nchar *\nngx_conf_set_flag_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    char  *p = conf;\n\n    ngx_str_t        *value;\n    ngx_flag_t       *fp;\n    ngx_conf_post_t  *post;\n\n    fp = (ngx_flag_t *) (p + cmd->offset);\n\n    if (*fp != NGX_CONF_UNSET) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    if (ngx_strcasecmp(value[1].data, (u_char *) \"on\") == 0) {\n        *fp = 1;\n\n    } else if (ngx_strcasecmp(value[1].data, (u_char *) \"off\") == 0) {\n        *fp = 0;\n\n    } else {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                     \"invalid value \\\"%s\\\" in \\\"%s\\\" directive, \"\n                     \"it must be \\\"on\\\" or \\\"off\\\"\",\n                     value[1].data, cmd->name.data);\n        return NGX_CONF_ERROR;\n    }\n\n    if (cmd->post) {\n        post = cmd->post;\n        return post->post_handler(cf, post, fp);\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nchar *\nngx_conf_set_str_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    char  *p = conf;\n\n    ngx_str_t        *field, *value;\n    ngx_conf_post_t  *post;\n\n    field = (ngx_str_t *) (p + cmd->offset);\n\n    if (field->data) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    *field = value[1];\n\n    if (cmd->post) {\n        post = cmd->post;\n        return post->post_handler(cf, post, field);\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nchar *\nngx_conf_set_str_array_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    char  *p = conf;\n\n    ngx_str_t         *value, *s;\n    ngx_array_t      **a;\n    ngx_conf_post_t   *post;\n\n    a = (ngx_array_t **) (p + cmd->offset);\n\n    if (*a == NGX_CONF_UNSET_PTR) {\n        *a = ngx_array_create(cf->pool, 4, sizeof(ngx_str_t));\n        if (*a == NULL) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    s = ngx_array_push(*a);\n    if (s == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    value = cf->args->elts;\n\n    *s = value[1];\n\n    if (cmd->post) {\n        post = cmd->post;\n        return post->post_handler(cf, post, s);\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nchar *\nngx_conf_set_keyval_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    char  *p = conf;\n\n    ngx_str_t         *value;\n    ngx_array_t      **a;\n    ngx_keyval_t      *kv;\n    ngx_conf_post_t   *post;\n\n    a = (ngx_array_t **) (p + cmd->offset);\n\n    if (*a == NGX_CONF_UNSET_PTR || *a == NULL) {\n        *a = ngx_array_create(cf->pool, 4, sizeof(ngx_keyval_t));\n        if (*a == NULL) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    kv = ngx_array_push(*a);\n    if (kv == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    value = cf->args->elts;\n\n    kv->key = value[1];\n    kv->value = value[2];\n\n    if (cmd->post) {\n        post = cmd->post;\n        return post->post_handler(cf, post, kv);\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nchar *\nngx_conf_set_num_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    char  *p = conf;\n\n    ngx_int_t        *np;\n    ngx_str_t        *value;\n    ngx_conf_post_t  *post;\n\n\n    np = (ngx_int_t *) (p + cmd->offset);\n\n    if (*np != NGX_CONF_UNSET) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n    *np = ngx_atoi(value[1].data, value[1].len);\n    if (*np == NGX_ERROR) {\n        return \"invalid number\";\n    }\n\n    if (cmd->post) {\n        post = cmd->post;\n        return post->post_handler(cf, post, np);\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nchar *\nngx_conf_set_size_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    char  *p = conf;\n\n    size_t           *sp;\n    ngx_str_t        *value;\n    ngx_conf_post_t  *post;\n\n\n    sp = (size_t *) (p + cmd->offset);\n    if (*sp != NGX_CONF_UNSET_SIZE) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    *sp = ngx_parse_size(&value[1]);\n    if (*sp == (size_t) NGX_ERROR) {\n        return \"invalid value\";\n    }\n\n    if (cmd->post) {\n        post = cmd->post;\n        return post->post_handler(cf, post, sp);\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nchar *\nngx_conf_set_off_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    char  *p = conf;\n\n    off_t            *op;\n    ngx_str_t        *value;\n    ngx_conf_post_t  *post;\n\n\n    op = (off_t *) (p + cmd->offset);\n    if (*op != NGX_CONF_UNSET) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    *op = ngx_parse_offset(&value[1]);\n    if (*op == (off_t) NGX_ERROR) {\n        return \"invalid value\";\n    }\n\n    if (cmd->post) {\n        post = cmd->post;\n        return post->post_handler(cf, post, op);\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nchar *\nngx_conf_set_msec_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    char  *p = conf;\n\n    ngx_msec_t       *msp;\n    ngx_str_t        *value;\n    ngx_conf_post_t  *post;\n\n\n    msp = (ngx_msec_t *) (p + cmd->offset);\n    if (*msp != NGX_CONF_UNSET_MSEC) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    *msp = ngx_parse_time(&value[1], 0);\n    if (*msp == (ngx_msec_t) NGX_ERROR) {\n        return \"invalid value\";\n    }\n\n    if (cmd->post) {\n        post = cmd->post;\n        return post->post_handler(cf, post, msp);\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nchar *\nngx_conf_set_sec_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    char  *p = conf;\n\n    time_t           *sp;\n    ngx_str_t        *value;\n    ngx_conf_post_t  *post;\n\n\n    sp = (time_t *) (p + cmd->offset);\n    if (*sp != NGX_CONF_UNSET) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    *sp = ngx_parse_time(&value[1], 1);\n    if (*sp == (time_t) NGX_ERROR) {\n        return \"invalid value\";\n    }\n\n    if (cmd->post) {\n        post = cmd->post;\n        return post->post_handler(cf, post, sp);\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nchar *\nngx_conf_set_bufs_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    char *p = conf;\n\n    ngx_str_t   *value;\n    ngx_bufs_t  *bufs;\n\n\n    bufs = (ngx_bufs_t *) (p + cmd->offset);\n    if (bufs->num) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    bufs->num = ngx_atoi(value[1].data, value[1].len);\n    if (bufs->num == NGX_ERROR || bufs->num == 0) {\n        return \"invalid value\";\n    }\n\n    bufs->size = ngx_parse_size(&value[2]);\n    if (bufs->size == (size_t) NGX_ERROR || bufs->size == 0) {\n        return \"invalid value\";\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nchar *\nngx_conf_set_enum_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    char  *p = conf;\n\n    ngx_uint_t       *np, i;\n    ngx_str_t        *value;\n    ngx_conf_enum_t  *e;\n\n    np = (ngx_uint_t *) (p + cmd->offset);\n\n    if (*np != NGX_CONF_UNSET_UINT) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n    e = cmd->post;\n\n    for (i = 0; e[i].name.len != 0; i++) {\n        if (e[i].name.len != value[1].len\n            || ngx_strcasecmp(e[i].name.data, value[1].data) != 0)\n        {\n            continue;\n        }\n\n        *np = e[i].value;\n\n        return NGX_CONF_OK;\n    }\n\n    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                       \"invalid value \\\"%s\\\"\", value[1].data);\n\n    return NGX_CONF_ERROR;\n}\n\n\nchar *\nngx_conf_set_bitmask_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    char  *p = conf;\n\n    ngx_uint_t          *np, i, m;\n    ngx_str_t           *value;\n    ngx_conf_bitmask_t  *mask;\n\n\n    np = (ngx_uint_t *) (p + cmd->offset);\n    value = cf->args->elts;\n    mask = cmd->post;\n\n    for (i = 1; i < cf->args->nelts; i++) {\n        for (m = 0; mask[m].name.len != 0; m++) {\n\n            if (mask[m].name.len != value[i].len\n                || ngx_strcasecmp(mask[m].name.data, value[i].data) != 0)\n            {\n                continue;\n            }\n\n            if (*np & mask[m].mask) {\n                ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                                   \"duplicate value \\\"%s\\\"\", value[i].data);\n\n            } else {\n                *np |= mask[m].mask;\n            }\n\n            break;\n        }\n\n        if (mask[m].name.len == 0) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"invalid value \\\"%s\\\"\", value[i].data);\n\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    return NGX_CONF_OK;\n}\n\n\n#if 0\n\nchar *\nngx_conf_unsupported(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    return \"unsupported on this platform\";\n}\n\n#endif\n\n\nchar *\nngx_conf_deprecated(ngx_conf_t *cf, void *post, void *data)\n{\n    ngx_conf_deprecated_t  *d = post;\n\n    ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                       \"the \\\"%s\\\" directive is deprecated, \"\n                       \"use the \\\"%s\\\" directive instead\",\n                       d->old_name, d->new_name);\n\n    return NGX_CONF_OK;\n}\n\n\nchar *\nngx_conf_check_num_bounds(ngx_conf_t *cf, void *post, void *data)\n{\n    ngx_conf_num_bounds_t  *bounds = post;\n    ngx_int_t  *np = data;\n\n    if (bounds->high == -1) {\n        if (*np >= bounds->low) {\n            return NGX_CONF_OK;\n        }\n\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"value must be equal to or greater than %i\",\n                           bounds->low);\n\n        return NGX_CONF_ERROR;\n    }\n\n    if (*np >= bounds->low && *np <= bounds->high) {\n        return NGX_CONF_OK;\n    }\n\n    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                       \"value must be between %i and %i\",\n                       bounds->low, bounds->high);\n\n    return NGX_CONF_ERROR;\n}\n"
  },
  {
    "path": "src/core/ngx_conf_file.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_CONF_FILE_H_INCLUDED_\n#define _NGX_CONF_FILE_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\n/*\n *        AAAA  number of arguments\n *      FF      command flags\n *    TT        command type, i.e. HTTP \"location\" or \"server\" command\n */\n\n#define NGX_CONF_NOARGS      0x00000001\n#define NGX_CONF_TAKE1       0x00000002\n#define NGX_CONF_TAKE2       0x00000004\n#define NGX_CONF_TAKE3       0x00000008\n#define NGX_CONF_TAKE4       0x00000010\n#define NGX_CONF_TAKE5       0x00000020\n#define NGX_CONF_TAKE6       0x00000040\n#define NGX_CONF_TAKE7       0x00000080\n\n#define NGX_CONF_MAX_ARGS    8\n\n#define NGX_CONF_TAKE12      (NGX_CONF_TAKE1|NGX_CONF_TAKE2)\n#define NGX_CONF_TAKE13      (NGX_CONF_TAKE1|NGX_CONF_TAKE3)\n\n#define NGX_CONF_TAKE23      (NGX_CONF_TAKE2|NGX_CONF_TAKE3)\n\n#define NGX_CONF_TAKE123     (NGX_CONF_TAKE1|NGX_CONF_TAKE2|NGX_CONF_TAKE3)\n#define NGX_CONF_TAKE1234    (NGX_CONF_TAKE1|NGX_CONF_TAKE2|NGX_CONF_TAKE3   \\\n                              |NGX_CONF_TAKE4)\n\n#define NGX_CONF_ARGS_NUMBER 0x000000ff\n#define NGX_CONF_BLOCK       0x00000100\n#define NGX_CONF_FLAG        0x00000200\n#define NGX_CONF_ANY         0x00000400\n#define NGX_CONF_1MORE       0x00000800\n#define NGX_CONF_2MORE       0x00001000\n\n#define NGX_DIRECT_CONF      0x00010000\n\n#define NGX_MAIN_CONF        0x01000000\n#define NGX_ANY_CONF         0xFF000000\n\n\n\n#define NGX_CONF_UNSET       -1\n#define NGX_CONF_UNSET_UINT  (ngx_uint_t) -1\n#define NGX_CONF_UNSET_PTR   (void *) -1\n#define NGX_CONF_UNSET_SIZE  (size_t) -1\n#define NGX_CONF_UNSET_MSEC  (ngx_msec_t) -1\n\n\n#define NGX_CONF_OK          NULL\n#define NGX_CONF_ERROR       (void *) -1\n\n#define NGX_CONF_BLOCK_START 1\n#define NGX_CONF_BLOCK_DONE  2\n#define NGX_CONF_FILE_DONE   3\n\n#define NGX_CORE_MODULE      0x45524F43  /* \"CORE\" */\n#define NGX_CONF_MODULE      0x464E4F43  /* \"CONF\" */\n\n\n#define NGX_MAX_CONF_ERRSTR  1024\n\n\nstruct ngx_command_s {\n    ngx_str_t             name;\n    ngx_uint_t            type;\n    char               *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\n    ngx_uint_t            conf;\n    ngx_uint_t            offset;\n    void                 *post;\n};\n\n#define ngx_null_command  { ngx_null_string, 0, NULL, 0, 0, NULL }\n\n\nstruct ngx_open_file_s {\n    ngx_fd_t              fd;\n    ngx_str_t             name;\n\n    void                (*flush)(ngx_open_file_t *file, ngx_log_t *log);\n    void                 *data;\n};\n\n\ntypedef struct {\n    ngx_file_t            file;\n    ngx_buf_t            *buffer;\n    ngx_buf_t            *dump;\n    ngx_uint_t            line;\n} ngx_conf_file_t;\n\n\ntypedef struct {\n    ngx_str_t             name;\n    ngx_buf_t            *buffer;\n} ngx_conf_dump_t;\n\n\ntypedef char *(*ngx_conf_handler_pt)(ngx_conf_t *cf,\n    ngx_command_t *dummy, void *conf);\n\n\nstruct ngx_conf_s {\n    char                 *name;\n    ngx_array_t          *args;\n\n    ngx_cycle_t          *cycle;\n    ngx_pool_t           *pool;\n    ngx_pool_t           *temp_pool;\n    ngx_conf_file_t      *conf_file;\n    ngx_log_t            *log;\n\n    void                 *ctx;\n    ngx_uint_t            module_type;\n    ngx_uint_t            cmd_type;\n\n    ngx_conf_handler_pt   handler;\n    void                 *handler_conf;\n#if (NGX_SSL && NGX_SSL_ASYNC)\n    ngx_flag_t            no_ssl_init;\n#endif\n};\n\n\ntypedef char *(*ngx_conf_post_handler_pt) (ngx_conf_t *cf,\n    void *data, void *conf);\n\ntypedef struct {\n    ngx_conf_post_handler_pt  post_handler;\n} ngx_conf_post_t;\n\n\ntypedef struct {\n    ngx_conf_post_handler_pt  post_handler;\n    char                     *old_name;\n    char                     *new_name;\n} ngx_conf_deprecated_t;\n\n\ntypedef struct {\n    ngx_conf_post_handler_pt  post_handler;\n    ngx_int_t                 low;\n    ngx_int_t                 high;\n} ngx_conf_num_bounds_t;\n\n\ntypedef struct {\n    ngx_str_t                 name;\n    ngx_uint_t                value;\n} ngx_conf_enum_t;\n\n\n#define NGX_CONF_BITMASK_SET  1\n\ntypedef struct {\n    ngx_str_t                 name;\n    ngx_uint_t                mask;\n} ngx_conf_bitmask_t;\n\n\n\nchar * ngx_conf_deprecated(ngx_conf_t *cf, void *post, void *data);\nchar *ngx_conf_check_num_bounds(ngx_conf_t *cf, void *post, void *data);\n\n\n#define ngx_get_conf(conf_ctx, module)  conf_ctx[module.index]\n\n\n\n#define ngx_conf_init_value(conf, default)                                   \\\n    if (conf == NGX_CONF_UNSET) {                                            \\\n        conf = default;                                                      \\\n    }\n\n#define ngx_conf_init_ptr_value(conf, default)                               \\\n    if (conf == NGX_CONF_UNSET_PTR) {                                        \\\n        conf = default;                                                      \\\n    }\n\n#define ngx_conf_init_uint_value(conf, default)                              \\\n    if (conf == NGX_CONF_UNSET_UINT) {                                       \\\n        conf = default;                                                      \\\n    }\n\n#define ngx_conf_init_size_value(conf, default)                              \\\n    if (conf == NGX_CONF_UNSET_SIZE) {                                       \\\n        conf = default;                                                      \\\n    }\n\n#define ngx_conf_init_msec_value(conf, default)                              \\\n    if (conf == NGX_CONF_UNSET_MSEC) {                                       \\\n        conf = default;                                                      \\\n    }\n\n#define ngx_conf_merge_value(conf, prev, default)                            \\\n    if (conf == NGX_CONF_UNSET) {                                            \\\n        conf = (prev == NGX_CONF_UNSET) ? default : prev;                    \\\n    }\n\n#define ngx_conf_merge_ptr_value(conf, prev, default)                        \\\n    if (conf == NGX_CONF_UNSET_PTR) {                                        \\\n        conf = (prev == NGX_CONF_UNSET_PTR) ? default : prev;                \\\n    }\n\n#define ngx_conf_merge_uint_value(conf, prev, default)                       \\\n    if (conf == NGX_CONF_UNSET_UINT) {                                       \\\n        conf = (prev == NGX_CONF_UNSET_UINT) ? default : prev;               \\\n    }\n\n#define ngx_conf_merge_msec_value(conf, prev, default)                       \\\n    if (conf == NGX_CONF_UNSET_MSEC) {                                       \\\n        conf = (prev == NGX_CONF_UNSET_MSEC) ? default : prev;               \\\n    }\n\n#define ngx_conf_merge_sec_value(conf, prev, default)                        \\\n    if (conf == NGX_CONF_UNSET) {                                            \\\n        conf = (prev == NGX_CONF_UNSET) ? default : prev;                    \\\n    }\n\n#define ngx_conf_merge_size_value(conf, prev, default)                       \\\n    if (conf == NGX_CONF_UNSET_SIZE) {                                       \\\n        conf = (prev == NGX_CONF_UNSET_SIZE) ? default : prev;               \\\n    }\n\n#define ngx_conf_merge_off_value(conf, prev, default)                        \\\n    if (conf == NGX_CONF_UNSET) {                                            \\\n        conf = (prev == NGX_CONF_UNSET) ? default : prev;                    \\\n    }\n\n#define ngx_conf_merge_str_value(conf, prev, default)                        \\\n    if (conf.data == NULL) {                                                 \\\n        if (prev.data) {                                                     \\\n            conf.len = prev.len;                                             \\\n            conf.data = prev.data;                                           \\\n        } else {                                                             \\\n            conf.len = sizeof(default) - 1;                                  \\\n            conf.data = (u_char *) default;                                  \\\n        }                                                                    \\\n    }\n\n#define ngx_conf_merge_bufs_value(conf, prev, default_num, default_size)     \\\n    if (conf.num == 0) {                                                     \\\n        if (prev.num) {                                                      \\\n            conf.num = prev.num;                                             \\\n            conf.size = prev.size;                                           \\\n        } else {                                                             \\\n            conf.num = default_num;                                          \\\n            conf.size = default_size;                                        \\\n        }                                                                    \\\n    }\n\n#define ngx_conf_merge_bitmask_value(conf, prev, default)                    \\\n    if (conf == 0) {                                                         \\\n        conf = (prev == 0) ? default : prev;                                 \\\n    }\n\n\nchar *ngx_conf_param(ngx_conf_t *cf);\nchar *ngx_conf_parse(ngx_conf_t *cf, ngx_str_t *filename);\nchar *ngx_conf_include(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\n\n\nngx_int_t ngx_conf_full_name(ngx_cycle_t *cycle, ngx_str_t *name,\n    ngx_uint_t conf_prefix);\nngx_open_file_t *ngx_conf_open_file(ngx_cycle_t *cycle, ngx_str_t *name);\nvoid ngx_cdecl ngx_conf_log_error(ngx_uint_t level, ngx_conf_t *cf,\n    ngx_err_t err, const char *fmt, ...);\n\n\nchar *ngx_conf_set_flag_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\nchar *ngx_conf_set_str_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\nchar *ngx_conf_set_str_array_slot(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nchar *ngx_conf_set_keyval_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\nchar *ngx_conf_set_num_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\nchar *ngx_conf_set_size_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\nchar *ngx_conf_set_off_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\nchar *ngx_conf_set_msec_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\nchar *ngx_conf_set_sec_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\nchar *ngx_conf_set_bufs_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\nchar *ngx_conf_set_enum_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\nchar *ngx_conf_set_bitmask_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\n\n\n#endif /* _NGX_CONF_FILE_H_INCLUDED_ */\n"
  },
  {
    "path": "src/core/ngx_config.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_CONFIG_H_INCLUDED_\n#define _NGX_CONFIG_H_INCLUDED_\n\n\n#include <ngx_auto_headers.h>\n\n\n#if defined __DragonFly__ && !defined __FreeBSD__\n#define __FreeBSD__        4\n#define __FreeBSD_version  480101\n#endif\n\n\n#if (NGX_FREEBSD)\n#include <ngx_freebsd_config.h>\n\n\n#elif (NGX_LINUX)\n#include <ngx_linux_config.h>\n\n\n#elif (NGX_SOLARIS)\n#include <ngx_solaris_config.h>\n\n\n#elif (NGX_DARWIN)\n#include <ngx_darwin_config.h>\n\n\n#elif (NGX_WIN32)\n#include <ngx_win32_config.h>\n\n\n#else /* POSIX */\n#include <ngx_posix_config.h>\n\n#endif\n\n\n#ifndef NGX_HAVE_SO_SNDLOWAT\n#define NGX_HAVE_SO_SNDLOWAT     1\n#endif\n\n\n#if !(NGX_WIN32)\n\n#define ngx_signal_helper(n)     SIG##n\n#define ngx_signal_value(n)      ngx_signal_helper(n)\n\n#define ngx_random               random\n\n/* TODO: #ifndef */\n#define NGX_SHUTDOWN_SIGNAL      QUIT\n#define NGX_TERMINATE_SIGNAL     TERM\n#define NGX_NOACCEPT_SIGNAL      WINCH\n#define NGX_RECONFIGURE_SIGNAL   HUP\n\n#if (NGX_LINUXTHREADS)\n#define NGX_REOPEN_SIGNAL        INFO\n#define NGX_CHANGEBIN_SIGNAL     XCPU\n#else\n#define NGX_REOPEN_SIGNAL        USR1\n#define NGX_CHANGEBIN_SIGNAL     USR2\n#endif\n\n#if (T_NGX_HAVE_XUDP)\n\n#define SIGXUDP                 __SIGRTMAX - 10\n\n/**\n * register __SIGRTMAX - 1 as the signal-exit of the worker xudp\n * then, xudp module will test validity\n * if check failed, auto downgrade\n */\n#define NGX_XUDP_TERMINATE_SIGNAL   XUDP\n#endif\n\n#define ngx_cdecl\n#define ngx_libc_cdecl\n\n#endif\n\ntypedef intptr_t        ngx_int_t;\ntypedef uintptr_t       ngx_uint_t;\ntypedef intptr_t        ngx_flag_t;\n\n\n#define NGX_INT32_LEN   (sizeof(\"-2147483648\") - 1)\n#define NGX_INT64_LEN   (sizeof(\"-9223372036854775808\") - 1)\n\n#if (NGX_PTR_SIZE == 4)\n#define NGX_INT_T_LEN   NGX_INT32_LEN\n#define NGX_MAX_INT_T_VALUE  2147483647\n\n#else\n#define NGX_INT_T_LEN   NGX_INT64_LEN\n#define NGX_MAX_INT_T_VALUE  9223372036854775807\n#endif\n\n\n#ifndef NGX_ALIGNMENT\n#define NGX_ALIGNMENT   sizeof(unsigned long)    /* platform word */\n#endif\n\n#define ngx_align(d, a)     (((d) + (a - 1)) & ~(a - 1))\n#define ngx_align_ptr(p, a)                                                   \\\n    (u_char *) (((uintptr_t) (p) + ((uintptr_t) a - 1)) & ~((uintptr_t) a - 1))\n\n\n#define ngx_abort       abort\n\n\n/* TODO: platform specific: array[NGX_INVALID_ARRAY_INDEX] must cause SIGSEGV */\n#define NGX_INVALID_ARRAY_INDEX 0x80000000\n\n\n/* TODO: auto_conf: ngx_inline   inline __inline __inline__ */\n#ifndef ngx_inline\n#define ngx_inline      inline\n#endif\n\n#ifndef INADDR_NONE  /* Solaris */\n#define INADDR_NONE  ((unsigned int) -1)\n#endif\n\n#ifdef MAXHOSTNAMELEN\n#define NGX_MAXHOSTNAMELEN  MAXHOSTNAMELEN\n#else\n#define NGX_MAXHOSTNAMELEN  256\n#endif\n\n\n#define NGX_MAX_UINT32_VALUE  (uint32_t) 0xffffffff\n#define NGX_MAX_INT32_VALUE   (uint32_t) 0x7fffffff\n\n\n#if (NGX_COMPAT)\n\n#define NGX_COMPAT_BEGIN(slots)  uint64_t spare[slots];\n#define NGX_COMPAT_END\n\n#else\n\n#define NGX_COMPAT_BEGIN(slots)\n#define NGX_COMPAT_END\n\n#endif\n\n\n#endif /* _NGX_CONFIG_H_INCLUDED_ */\n"
  },
  {
    "path": "src/core/ngx_connection.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n\n\nngx_os_io_t  ngx_io;\n\n\nstatic void ngx_drain_connections(ngx_cycle_t *cycle);\n\n\nngx_listening_t *\nngx_create_listening(ngx_conf_t *cf, struct sockaddr *sockaddr,\n    socklen_t socklen)\n{\n    size_t            len;\n    ngx_listening_t  *ls;\n    struct sockaddr  *sa;\n    u_char            text[NGX_SOCKADDR_STRLEN];\n\n    ls = ngx_array_push(&cf->cycle->listening);\n    if (ls == NULL) {\n        return NULL;\n    }\n\n    ngx_memzero(ls, sizeof(ngx_listening_t));\n\n    sa = ngx_palloc(cf->pool, socklen);\n    if (sa == NULL) {\n        return NULL;\n    }\n\n    ngx_memcpy(sa, sockaddr, socklen);\n\n    ls->sockaddr = sa;\n    ls->socklen = socklen;\n\n    len = ngx_sock_ntop(sa, socklen, text, NGX_SOCKADDR_STRLEN, 1);\n    ls->addr_text.len = len;\n\n    switch (ls->sockaddr->sa_family) {\n#if (NGX_HAVE_INET6)\n    case AF_INET6:\n        ls->addr_text_max_len = NGX_INET6_ADDRSTRLEN;\n        break;\n#endif\n#if (NGX_HAVE_UNIX_DOMAIN)\n    case AF_UNIX:\n        ls->addr_text_max_len = NGX_UNIX_ADDRSTRLEN;\n        len++;\n        break;\n#endif\n    case AF_INET:\n        ls->addr_text_max_len = NGX_INET_ADDRSTRLEN;\n        break;\n    default:\n        ls->addr_text_max_len = NGX_SOCKADDR_STRLEN;\n        break;\n    }\n\n    ls->addr_text.data = ngx_pnalloc(cf->pool, len);\n    if (ls->addr_text.data == NULL) {\n        return NULL;\n    }\n\n    ngx_memcpy(ls->addr_text.data, text, len);\n\n#if !(NGX_WIN32)\n    ngx_rbtree_init(&ls->rbtree, &ls->sentinel, ngx_udp_rbtree_insert_value);\n#endif\n\n    ls->fd = (ngx_socket_t) -1;\n    ls->type = SOCK_STREAM;\n\n    ls->backlog = NGX_LISTEN_BACKLOG;\n    ls->rcvbuf = -1;\n    ls->sndbuf = -1;\n\n#if (NGX_HAVE_SETFIB)\n    ls->setfib = -1;\n#endif\n\n#if (NGX_HAVE_TCP_FASTOPEN)\n    ls->fastopen = -1;\n#endif\n\n    return ls;\n}\n\n\nngx_int_t\nngx_clone_listening(ngx_cycle_t *cycle, ngx_listening_t *ls)\n{\n#if (NGX_HAVE_REUSEPORT)\n\n    ngx_int_t         n;\n    ngx_core_conf_t  *ccf;\n    ngx_listening_t   ols;\n\n    if (!ls->reuseport || ls->worker != 0) {\n        return NGX_OK;\n    }\n\n    ols = *ls;\n\n    ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);\n\n    for (n = 1; n < ccf->worker_processes; n++) {\n\n        /* create a socket for each worker process */\n\n        ls = ngx_array_push(&cycle->listening);\n        if (ls == NULL) {\n            return NGX_ERROR;\n        }\n\n        *ls = ols;\n        ls->worker = n;\n    }\n\n#endif\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_set_inherited_sockets(ngx_cycle_t *cycle)\n{\n    size_t                     len;\n    ngx_uint_t                 i;\n    ngx_listening_t           *ls;\n    socklen_t                  olen;\n#if (NGX_HAVE_DEFERRED_ACCEPT || NGX_HAVE_TCP_FASTOPEN)\n    ngx_err_t                  err;\n#endif\n#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)\n    struct accept_filter_arg   af;\n#endif\n#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT)\n    int                        timeout;\n#endif\n#if (NGX_HAVE_REUSEPORT)\n    int                        reuseport;\n#endif\n\n    ls = cycle->listening.elts;\n    for (i = 0; i < cycle->listening.nelts; i++) {\n\n        ls[i].sockaddr = ngx_palloc(cycle->pool, sizeof(ngx_sockaddr_t));\n        if (ls[i].sockaddr == NULL) {\n            return NGX_ERROR;\n        }\n\n        ls[i].socklen = sizeof(ngx_sockaddr_t);\n        if (getsockname(ls[i].fd, ls[i].sockaddr, &ls[i].socklen) == -1) {\n            ngx_log_error(NGX_LOG_CRIT, cycle->log, ngx_socket_errno,\n                          \"getsockname() of the inherited \"\n                          \"socket #%d failed\", ls[i].fd);\n            ls[i].ignore = 1;\n            continue;\n        }\n\n        if (ls[i].socklen > (socklen_t) sizeof(ngx_sockaddr_t)) {\n            ls[i].socklen = sizeof(ngx_sockaddr_t);\n        }\n\n        switch (ls[i].sockaddr->sa_family) {\n\n#if (NGX_HAVE_INET6)\n        case AF_INET6:\n            ls[i].addr_text_max_len = NGX_INET6_ADDRSTRLEN;\n            len = NGX_INET6_ADDRSTRLEN + sizeof(\"[]:65535\") - 1;\n            break;\n#endif\n\n#if (NGX_HAVE_UNIX_DOMAIN)\n        case AF_UNIX:\n            ls[i].addr_text_max_len = NGX_UNIX_ADDRSTRLEN;\n            len = NGX_UNIX_ADDRSTRLEN;\n            break;\n#endif\n\n        case AF_INET:\n            ls[i].addr_text_max_len = NGX_INET_ADDRSTRLEN;\n            len = NGX_INET_ADDRSTRLEN + sizeof(\":65535\") - 1;\n            break;\n\n        default:\n            ngx_log_error(NGX_LOG_CRIT, cycle->log, ngx_socket_errno,\n                          \"the inherited socket #%d has \"\n                          \"an unsupported protocol family\", ls[i].fd);\n            ls[i].ignore = 1;\n            continue;\n        }\n\n        ls[i].addr_text.data = ngx_pnalloc(cycle->pool, len);\n        if (ls[i].addr_text.data == NULL) {\n            return NGX_ERROR;\n        }\n\n        len = ngx_sock_ntop(ls[i].sockaddr, ls[i].socklen,\n                            ls[i].addr_text.data, len, 1);\n        if (len == 0) {\n            return NGX_ERROR;\n        }\n\n        ls[i].addr_text.len = len;\n\n        ls[i].backlog = NGX_LISTEN_BACKLOG;\n\n        olen = sizeof(int);\n\n        if (getsockopt(ls[i].fd, SOL_SOCKET, SO_TYPE, (void *) &ls[i].type,\n                       &olen)\n            == -1)\n        {\n            ngx_log_error(NGX_LOG_CRIT, cycle->log, ngx_socket_errno,\n                          \"getsockopt(SO_TYPE) %V failed\", &ls[i].addr_text);\n            ls[i].ignore = 1;\n            continue;\n        }\n\n        olen = sizeof(int);\n\n        if (getsockopt(ls[i].fd, SOL_SOCKET, SO_RCVBUF, (void *) &ls[i].rcvbuf,\n                       &olen)\n            == -1)\n        {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,\n                          \"getsockopt(SO_RCVBUF) %V failed, ignored\",\n                          &ls[i].addr_text);\n\n            ls[i].rcvbuf = -1;\n        }\n\n        olen = sizeof(int);\n\n        if (getsockopt(ls[i].fd, SOL_SOCKET, SO_SNDBUF, (void *) &ls[i].sndbuf,\n                       &olen)\n            == -1)\n        {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,\n                          \"getsockopt(SO_SNDBUF) %V failed, ignored\",\n                          &ls[i].addr_text);\n\n            ls[i].sndbuf = -1;\n        }\n\n#if 0\n        /* SO_SETFIB is currently a set only option */\n\n#if (NGX_HAVE_SETFIB)\n\n        olen = sizeof(int);\n\n        if (getsockopt(ls[i].fd, SOL_SOCKET, SO_SETFIB,\n                       (void *) &ls[i].setfib, &olen)\n            == -1)\n        {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,\n                          \"getsockopt(SO_SETFIB) %V failed, ignored\",\n                          &ls[i].addr_text);\n\n            ls[i].setfib = -1;\n        }\n\n#endif\n#endif\n\n#if (NGX_HAVE_REUSEPORT)\n\n        reuseport = 0;\n        olen = sizeof(int);\n\n#ifdef SO_REUSEPORT_LB\n\n        if (getsockopt(ls[i].fd, SOL_SOCKET, SO_REUSEPORT_LB,\n                       (void *) &reuseport, &olen)\n            == -1)\n        {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,\n                          \"getsockopt(SO_REUSEPORT_LB) %V failed, ignored\",\n                          &ls[i].addr_text);\n\n        } else {\n            ls[i].reuseport = reuseport ? 1 : 0;\n        }\n\n#else\n\n        if (getsockopt(ls[i].fd, SOL_SOCKET, SO_REUSEPORT,\n                       (void *) &reuseport, &olen)\n            == -1)\n        {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,\n                          \"getsockopt(SO_REUSEPORT) %V failed, ignored\",\n                          &ls[i].addr_text);\n\n        } else {\n            ls[i].reuseport = reuseport ? 1 : 0;\n        }\n#endif\n\n#endif\n\n        if (ls[i].type != SOCK_STREAM) {\n            continue;\n        }\n\n#if (NGX_HAVE_TCP_FASTOPEN)\n\n        olen = sizeof(int);\n\n        if (getsockopt(ls[i].fd, IPPROTO_TCP, TCP_FASTOPEN,\n                       (void *) &ls[i].fastopen, &olen)\n            == -1)\n        {\n            err = ngx_socket_errno;\n\n            if (err != NGX_EOPNOTSUPP && err != NGX_ENOPROTOOPT\n                && err != NGX_EINVAL)\n            {\n                ngx_log_error(NGX_LOG_NOTICE, cycle->log, err,\n                              \"getsockopt(TCP_FASTOPEN) %V failed, ignored\",\n                              &ls[i].addr_text);\n            }\n\n            ls[i].fastopen = -1;\n        }\n\n#endif\n\n#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)\n\n        ngx_memzero(&af, sizeof(struct accept_filter_arg));\n        olen = sizeof(struct accept_filter_arg);\n\n        if (getsockopt(ls[i].fd, SOL_SOCKET, SO_ACCEPTFILTER, &af, &olen)\n            == -1)\n        {\n            err = ngx_socket_errno;\n\n            if (err == NGX_EINVAL) {\n                continue;\n            }\n\n            ngx_log_error(NGX_LOG_NOTICE, cycle->log, err,\n                          \"getsockopt(SO_ACCEPTFILTER) for %V failed, ignored\",\n                          &ls[i].addr_text);\n            continue;\n        }\n\n        if (olen < sizeof(struct accept_filter_arg) || af.af_name[0] == '\\0') {\n            continue;\n        }\n\n        ls[i].accept_filter = ngx_palloc(cycle->pool, 16);\n        if (ls[i].accept_filter == NULL) {\n            return NGX_ERROR;\n        }\n\n        (void) ngx_cpystrn((u_char *) ls[i].accept_filter,\n                           (u_char *) af.af_name, 16);\n#endif\n\n#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT)\n\n        timeout = 0;\n        olen = sizeof(int);\n\n        if (getsockopt(ls[i].fd, IPPROTO_TCP, TCP_DEFER_ACCEPT, &timeout, &olen)\n            == -1)\n        {\n            err = ngx_socket_errno;\n\n            if (err == NGX_EOPNOTSUPP) {\n                continue;\n            }\n\n            ngx_log_error(NGX_LOG_NOTICE, cycle->log, err,\n                          \"getsockopt(TCP_DEFER_ACCEPT) for %V failed, ignored\",\n                          &ls[i].addr_text);\n            continue;\n        }\n\n        if (olen < sizeof(int) || timeout == 0) {\n            continue;\n        }\n\n        ls[i].deferred_accept = 1;\n#endif\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_open_listening_sockets(ngx_cycle_t *cycle)\n{\n    int               reuseaddr;\n    ngx_uint_t        i, tries, failed;\n    ngx_err_t         err;\n    ngx_log_t        *log;\n    ngx_socket_t      s;\n    ngx_listening_t  *ls;\n\n    reuseaddr = 1;\n#if (NGX_SUPPRESS_WARN)\n    failed = 0;\n#endif\n\n    log = cycle->log;\n\n    /* TODO: configurable try number */\n\n    for (tries = 5; tries; tries--) {\n        failed = 0;\n\n        /* for each listening socket */\n\n        ls = cycle->listening.elts;\n        for (i = 0; i < cycle->listening.nelts; i++) {\n\n            if (ls[i].ignore\n#if (T_NGX_HAVE_XUDP)\n                || ls[i].for_xudp\n#endif\n            ) {\n                continue;\n            }\n\n#if (NGX_HAVE_REUSEPORT)\n\n            if (ls[i].add_reuseport) {\n\n                /*\n                 * to allow transition from a socket without SO_REUSEPORT\n                 * to multiple sockets with SO_REUSEPORT, we have to set\n                 * SO_REUSEPORT on the old socket before opening new ones\n                 */\n\n                int  reuseport = 1;\n\n#ifdef SO_REUSEPORT_LB\n\n                if (setsockopt(ls[i].fd, SOL_SOCKET, SO_REUSEPORT_LB,\n                               (const void *) &reuseport, sizeof(int))\n                    == -1)\n                {\n                    ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,\n                                  \"setsockopt(SO_REUSEPORT_LB) %V failed, \"\n                                  \"ignored\",\n                                  &ls[i].addr_text);\n                }\n\n#else\n\n                if (setsockopt(ls[i].fd, SOL_SOCKET, SO_REUSEPORT,\n                               (const void *) &reuseport, sizeof(int))\n                    == -1)\n                {\n                    ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,\n                                  \"setsockopt(SO_REUSEPORT) %V failed, ignored\",\n                                  &ls[i].addr_text);\n                }\n#endif\n\n                ls[i].add_reuseport = 0;\n            }\n#endif\n\n            if (ls[i].fd != (ngx_socket_t) -1) {\n                continue;\n            }\n\n            if (ls[i].inherited) {\n\n                /* TODO: close on exit */\n                /* TODO: nonblocking */\n                /* TODO: deferred accept */\n\n                continue;\n            }\n\n            s = ngx_socket(ls[i].sockaddr->sa_family, ls[i].type, 0);\n\n            if (s == (ngx_socket_t) -1) {\n                ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,\n                              ngx_socket_n \" %V failed\", &ls[i].addr_text);\n                return NGX_ERROR;\n            }\n\n            if (ls[i].type != SOCK_DGRAM || !ngx_test_config) {\n\n                if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,\n                               (const void *) &reuseaddr, sizeof(int))\n                    == -1)\n                {\n                    ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,\n                                  \"setsockopt(SO_REUSEADDR) %V failed\",\n                                  &ls[i].addr_text);\n\n                    if (ngx_close_socket(s) == -1) {\n                        ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,\n                                      ngx_close_socket_n \" %V failed\",\n                                      &ls[i].addr_text);\n                    }\n\n                    return NGX_ERROR;\n                }\n            }\n\n#if (NGX_HAVE_REUSEPORT)\n\n            if (ls[i].reuseport && !ngx_test_config) {\n                int  reuseport;\n\n                reuseport = 1;\n\n#ifdef SO_REUSEPORT_LB\n\n                if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT_LB,\n                               (const void *) &reuseport, sizeof(int))\n                    == -1)\n                {\n                    ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,\n                                  \"setsockopt(SO_REUSEPORT_LB) %V failed\",\n                                  &ls[i].addr_text);\n\n                    if (ngx_close_socket(s) == -1) {\n                        ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,\n                                      ngx_close_socket_n \" %V failed\",\n                                      &ls[i].addr_text);\n                    }\n\n                    return NGX_ERROR;\n                }\n\n#else\n\n                if (setsockopt(s, SOL_SOCKET, SO_REUSEPORT,\n                               (const void *) &reuseport, sizeof(int))\n                    == -1)\n                {\n                    ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,\n                                  \"setsockopt(SO_REUSEPORT) %V failed\",\n                                  &ls[i].addr_text);\n\n                    if (ngx_close_socket(s) == -1) {\n                        ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,\n                                      ngx_close_socket_n \" %V failed\",\n                                      &ls[i].addr_text);\n                    }\n\n                    return NGX_ERROR;\n                }\n#endif\n            }\n#endif\n\n#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)\n\n            if (ls[i].sockaddr->sa_family == AF_INET6) {\n                int  ipv6only;\n\n                ipv6only = ls[i].ipv6only;\n\n                if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY,\n                               (const void *) &ipv6only, sizeof(int))\n                    == -1)\n                {\n                    ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,\n                                  \"setsockopt(IPV6_V6ONLY) %V failed, ignored\",\n                                  &ls[i].addr_text);\n                }\n            }\n#endif\n            /* TODO: close on exit */\n\n            if (!(ngx_event_flags & NGX_USE_IOCP_EVENT)) {\n                if (ngx_nonblocking(s) == -1) {\n                    ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,\n                                  ngx_nonblocking_n \" %V failed\",\n                                  &ls[i].addr_text);\n\n                    if (ngx_close_socket(s) == -1) {\n                        ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,\n                                      ngx_close_socket_n \" %V failed\",\n                                      &ls[i].addr_text);\n                    }\n\n                    return NGX_ERROR;\n                }\n            }\n\n            ngx_log_debug2(NGX_LOG_DEBUG_CORE, log, 0,\n                           \"bind() %V #%d \", &ls[i].addr_text, s);\n\n            if (bind(s, ls[i].sockaddr, ls[i].socklen) == -1) {\n                err = ngx_socket_errno;\n\n                if (err != NGX_EADDRINUSE || !ngx_test_config) {\n                    ngx_log_error(NGX_LOG_EMERG, log, err,\n                                  \"bind() to %V failed\", &ls[i].addr_text);\n                }\n\n                if (ngx_close_socket(s) == -1) {\n                    ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,\n                                  ngx_close_socket_n \" %V failed\",\n                                  &ls[i].addr_text);\n                }\n\n                if (err != NGX_EADDRINUSE) {\n                    return NGX_ERROR;\n                }\n\n                if (!ngx_test_config) {\n                    failed = 1;\n                }\n\n                continue;\n            }\n\n#if (NGX_HAVE_UNIX_DOMAIN)\n\n            if (ls[i].sockaddr->sa_family == AF_UNIX) {\n                mode_t   mode;\n                u_char  *name;\n\n                name = ls[i].addr_text.data + sizeof(\"unix:\") - 1;\n                mode = (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);\n\n                if (chmod((char *) name, mode) == -1) {\n                    ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                                  \"chmod() \\\"%s\\\" failed\", name);\n                }\n\n                if (ngx_test_config) {\n                    if (ngx_delete_file(name) == NGX_FILE_ERROR) {\n                        ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                                      ngx_delete_file_n \" %s failed\", name);\n                    }\n                }\n            }\n#endif\n\n            if (ls[i].type != SOCK_STREAM) {\n                ls[i].fd = s;\n                continue;\n            }\n\n            if (listen(s, ls[i].backlog) == -1) {\n                err = ngx_socket_errno;\n\n                /*\n                 * on OpenVZ after suspend/resume EADDRINUSE\n                 * may be returned by listen() instead of bind(), see\n                 * https://bugs.openvz.org/browse/OVZ-5587\n                 */\n\n                if (err != NGX_EADDRINUSE || !ngx_test_config) {\n                    ngx_log_error(NGX_LOG_EMERG, log, err,\n                                  \"listen() to %V, backlog %d failed\",\n                                  &ls[i].addr_text, ls[i].backlog);\n                }\n\n                if (ngx_close_socket(s) == -1) {\n                    ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,\n                                  ngx_close_socket_n \" %V failed\",\n                                  &ls[i].addr_text);\n                }\n\n                if (err != NGX_EADDRINUSE) {\n                    return NGX_ERROR;\n                }\n\n                if (!ngx_test_config) {\n                    failed = 1;\n                }\n\n                continue;\n            }\n\n            ls[i].listen = 1;\n\n            ls[i].fd = s;\n        }\n\n        if (!failed) {\n            break;\n        }\n\n        /* TODO: delay configurable */\n\n        ngx_log_error(NGX_LOG_NOTICE, log, 0,\n                      \"try again to bind() after 500ms\");\n\n        ngx_msleep(500);\n    }\n\n    if (failed) {\n        ngx_log_error(NGX_LOG_EMERG, log, 0, \"still could not bind()\");\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nvoid\nngx_configure_listening_sockets(ngx_cycle_t *cycle)\n{\n    int                        value;\n    ngx_uint_t                 i;\n    ngx_listening_t           *ls;\n\n#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)\n    struct accept_filter_arg   af;\n#endif\n\n    ls = cycle->listening.elts;\n    for (i = 0; i < cycle->listening.nelts; i++) {\n\n        ls[i].log = *ls[i].logp;\n\n#if (T_NGX_HAVE_XUDP)\n        if (ls[i].for_xudp) {\n            continue ;\n        }\n#endif\n\n        if (ls[i].rcvbuf != -1) {\n            if (setsockopt(ls[i].fd, SOL_SOCKET, SO_RCVBUF,\n                           (const void *) &ls[i].rcvbuf, sizeof(int))\n                == -1)\n            {\n                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,\n                              \"setsockopt(SO_RCVBUF, %d) %V failed, ignored\",\n                              ls[i].rcvbuf, &ls[i].addr_text);\n            }\n        }\n\n        if (ls[i].sndbuf != -1) {\n            if (setsockopt(ls[i].fd, SOL_SOCKET, SO_SNDBUF,\n                           (const void *) &ls[i].sndbuf, sizeof(int))\n                == -1)\n            {\n                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,\n                              \"setsockopt(SO_SNDBUF, %d) %V failed, ignored\",\n                              ls[i].sndbuf, &ls[i].addr_text);\n            }\n        }\n\n        if (ls[i].keepalive) {\n            value = (ls[i].keepalive == 1) ? 1 : 0;\n\n            if (setsockopt(ls[i].fd, SOL_SOCKET, SO_KEEPALIVE,\n                           (const void *) &value, sizeof(int))\n                == -1)\n            {\n                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,\n                              \"setsockopt(SO_KEEPALIVE, %d) %V failed, ignored\",\n                              value, &ls[i].addr_text);\n            }\n        }\n\n#if (NGX_HAVE_KEEPALIVE_TUNABLE)\n\n        if (ls[i].keepidle) {\n            value = ls[i].keepidle;\n\n#if (NGX_KEEPALIVE_FACTOR)\n            value *= NGX_KEEPALIVE_FACTOR;\n#endif\n\n            if (setsockopt(ls[i].fd, IPPROTO_TCP, TCP_KEEPIDLE,\n                           (const void *) &value, sizeof(int))\n                == -1)\n            {\n                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,\n                              \"setsockopt(TCP_KEEPIDLE, %d) %V failed, ignored\",\n                              value, &ls[i].addr_text);\n            }\n        }\n\n        if (ls[i].keepintvl) {\n            value = ls[i].keepintvl;\n\n#if (NGX_KEEPALIVE_FACTOR)\n            value *= NGX_KEEPALIVE_FACTOR;\n#endif\n\n            if (setsockopt(ls[i].fd, IPPROTO_TCP, TCP_KEEPINTVL,\n                           (const void *) &value, sizeof(int))\n                == -1)\n            {\n                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,\n                             \"setsockopt(TCP_KEEPINTVL, %d) %V failed, ignored\",\n                             value, &ls[i].addr_text);\n            }\n        }\n\n        if (ls[i].keepcnt) {\n            if (setsockopt(ls[i].fd, IPPROTO_TCP, TCP_KEEPCNT,\n                           (const void *) &ls[i].keepcnt, sizeof(int))\n                == -1)\n            {\n                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,\n                              \"setsockopt(TCP_KEEPCNT, %d) %V failed, ignored\",\n                              ls[i].keepcnt, &ls[i].addr_text);\n            }\n        }\n\n#endif\n\n#if (NGX_HAVE_SETFIB)\n        if (ls[i].setfib != -1) {\n            if (setsockopt(ls[i].fd, SOL_SOCKET, SO_SETFIB,\n                           (const void *) &ls[i].setfib, sizeof(int))\n                == -1)\n            {\n                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,\n                              \"setsockopt(SO_SETFIB, %d) %V failed, ignored\",\n                              ls[i].setfib, &ls[i].addr_text);\n            }\n        }\n#endif\n\n#if (NGX_HAVE_TCP_FASTOPEN)\n        if (ls[i].fastopen != -1) {\n            if (setsockopt(ls[i].fd, IPPROTO_TCP, TCP_FASTOPEN,\n                           (const void *) &ls[i].fastopen, sizeof(int))\n                == -1)\n            {\n                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,\n                              \"setsockopt(TCP_FASTOPEN, %d) %V failed, ignored\",\n                              ls[i].fastopen, &ls[i].addr_text);\n            }\n        }\n#endif\n\n#if 0\n        if (1) {\n            int tcp_nodelay = 1;\n\n            if (setsockopt(ls[i].fd, IPPROTO_TCP, TCP_NODELAY,\n                       (const void *) &tcp_nodelay, sizeof(int))\n                == -1)\n            {\n                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,\n                              \"setsockopt(TCP_NODELAY) %V failed, ignored\",\n                              &ls[i].addr_text);\n            }\n        }\n#endif\n\n        if (ls[i].listen) {\n\n            /* change backlog via listen() */\n\n            if (listen(ls[i].fd, ls[i].backlog) == -1) {\n                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,\n                              \"listen() to %V, backlog %d failed, ignored\",\n                              &ls[i].addr_text, ls[i].backlog);\n            }\n        }\n\n        /*\n         * setting deferred mode should be last operation on socket,\n         * because code may prematurely continue cycle on failure\n         */\n\n#if (NGX_HAVE_DEFERRED_ACCEPT)\n\n#ifdef SO_ACCEPTFILTER\n\n        if (ls[i].delete_deferred) {\n            if (setsockopt(ls[i].fd, SOL_SOCKET, SO_ACCEPTFILTER, NULL, 0)\n                == -1)\n            {\n                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,\n                              \"setsockopt(SO_ACCEPTFILTER, NULL) \"\n                              \"for %V failed, ignored\",\n                              &ls[i].addr_text);\n\n                if (ls[i].accept_filter) {\n                    ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,\n                                  \"could not change the accept filter \"\n                                  \"to \\\"%s\\\" for %V, ignored\",\n                                  ls[i].accept_filter, &ls[i].addr_text);\n                }\n\n                continue;\n            }\n\n            ls[i].deferred_accept = 0;\n        }\n\n        if (ls[i].add_deferred) {\n            ngx_memzero(&af, sizeof(struct accept_filter_arg));\n            (void) ngx_cpystrn((u_char *) af.af_name,\n                               (u_char *) ls[i].accept_filter, 16);\n\n            if (setsockopt(ls[i].fd, SOL_SOCKET, SO_ACCEPTFILTER,\n                           &af, sizeof(struct accept_filter_arg))\n                == -1)\n            {\n                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,\n                              \"setsockopt(SO_ACCEPTFILTER, \\\"%s\\\") \"\n                              \"for %V failed, ignored\",\n                              ls[i].accept_filter, &ls[i].addr_text);\n                continue;\n            }\n\n            ls[i].deferred_accept = 1;\n        }\n\n#endif\n\n#ifdef TCP_DEFER_ACCEPT\n\n        if (ls[i].add_deferred || ls[i].delete_deferred) {\n\n            if (ls[i].add_deferred) {\n                /*\n                 * There is no way to find out how long a connection was\n                 * in queue (and a connection may bypass deferred queue at all\n                 * if syncookies were used), hence we use 1 second timeout\n                 * here.\n                 */\n                value = 1;\n\n            } else {\n                value = 0;\n            }\n\n            if (setsockopt(ls[i].fd, IPPROTO_TCP, TCP_DEFER_ACCEPT,\n                           &value, sizeof(int))\n                == -1)\n            {\n                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,\n                              \"setsockopt(TCP_DEFER_ACCEPT, %d) for %V failed, \"\n                              \"ignored\",\n                              value, &ls[i].addr_text);\n\n                continue;\n            }\n        }\n\n        if (ls[i].add_deferred) {\n            ls[i].deferred_accept = 1;\n        }\n\n#endif\n\n#endif /* NGX_HAVE_DEFERRED_ACCEPT */\n\n#if (NGX_HAVE_IP_RECVDSTADDR)\n\n        if (ls[i].wildcard\n            && ls[i].type == SOCK_DGRAM\n            && ls[i].sockaddr->sa_family == AF_INET)\n        {\n            value = 1;\n\n            if (setsockopt(ls[i].fd, IPPROTO_IP, IP_RECVDSTADDR,\n                           (const void *) &value, sizeof(int))\n                == -1)\n            {\n                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,\n                              \"setsockopt(IP_RECVDSTADDR) \"\n                              \"for %V failed, ignored\",\n                              &ls[i].addr_text);\n            }\n        }\n\n#elif (NGX_HAVE_IP_PKTINFO)\n\n        if (ls[i].wildcard\n            && ls[i].type == SOCK_DGRAM\n            && ls[i].sockaddr->sa_family == AF_INET)\n        {\n            value = 1;\n\n            if (setsockopt(ls[i].fd, IPPROTO_IP, IP_PKTINFO,\n                           (const void *) &value, sizeof(int))\n                == -1)\n            {\n                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,\n                              \"setsockopt(IP_PKTINFO) \"\n                              \"for %V failed, ignored\",\n                              &ls[i].addr_text);\n            }\n        }\n\n#endif\n\n#if (NGX_HAVE_INET6 && NGX_HAVE_IPV6_RECVPKTINFO)\n\n        if (ls[i].wildcard\n            && ls[i].type == SOCK_DGRAM\n            && ls[i].sockaddr->sa_family == AF_INET6)\n        {\n            value = 1;\n\n            if (setsockopt(ls[i].fd, IPPROTO_IPV6, IPV6_RECVPKTINFO,\n                           (const void *) &value, sizeof(int))\n                == -1)\n            {\n                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_socket_errno,\n                              \"setsockopt(IPV6_RECVPKTINFO) \"\n                              \"for %V failed, ignored\",\n                              &ls[i].addr_text);\n            }\n        }\n\n#endif\n    }\n\n    return;\n}\n\n\nvoid\nngx_close_listening_sockets(ngx_cycle_t *cycle)\n{\n    ngx_uint_t         i;\n    ngx_listening_t   *ls;\n    ngx_connection_t  *c;\n\n#if (T_NGX_HAVE_XUDP)\n    ngx_xudp_terminate_xudp_binding(cycle);\n#endif\n\n    if (ngx_event_flags & NGX_USE_IOCP_EVENT) {\n        return;\n    }\n\n    ngx_accept_mutex_held = 0;\n    ngx_use_accept_mutex = 0;\n\n    ls = cycle->listening.elts;\n    for (i = 0; i < cycle->listening.nelts; i++) {\n\n        c = ls[i].connection;\n\n        if (c) {\n            if (c->read->active) {\n                if (ngx_event_flags & NGX_USE_EPOLL_EVENT) {\n\n                    /*\n                     * it seems that Linux-2.6.x OpenVZ sends events\n                     * for closed shared listening sockets unless\n                     * the events was explicitly deleted\n                     */\n#if (NGX_SSL && NGX_SSL_ASYNC)\n                    if (c->async_enable && ngx_del_async_conn) {\n                        if (c->num_async_fds) {\n                            ngx_del_async_conn(c, NGX_DISABLE_EVENT);\n                            c->num_async_fds--;\n                        }\n                    }\n#endif\n\n                    ngx_del_event(c->read, NGX_READ_EVENT, 0);\n\n                } else {\n                    ngx_del_event(c->read, NGX_READ_EVENT, NGX_CLOSE_EVENT);\n                }\n            }\n\n            ngx_free_connection(c);\n\n            c->fd = (ngx_socket_t) -1;\n        }\n\n        ngx_log_debug2(NGX_LOG_DEBUG_CORE, cycle->log, 0,\n                       \"close listening %V #%d \", &ls[i].addr_text, ls[i].fd);\n\n        if (ngx_close_socket(ls[i].fd) == -1) {\n            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_socket_errno,\n                          ngx_close_socket_n \" %V failed\", &ls[i].addr_text);\n        }\n\n#if (NGX_HAVE_UNIX_DOMAIN)\n\n        if (ls[i].sockaddr->sa_family == AF_UNIX\n            && ngx_process <= NGX_PROCESS_MASTER\n            && ngx_new_binary == 0\n            && (!ls[i].inherited || ngx_getppid() != ngx_parent))\n        {\n            u_char *name = ls[i].addr_text.data + sizeof(\"unix:\") - 1;\n\n            if (ngx_delete_file(name) == NGX_FILE_ERROR) {\n                ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_socket_errno,\n                              ngx_delete_file_n \" %s failed\", name);\n            }\n        }\n\n#endif\n\n        ls[i].fd = (ngx_socket_t) -1;\n    }\n\n    cycle->listening.nelts = 0;\n}\n\n\nngx_connection_t *\nngx_get_connection(ngx_socket_t s, ngx_log_t *log)\n{\n    ngx_uint_t         instance;\n    ngx_event_t       *rev, *wev;\n#if (NGX_SSL && NGX_SSL_ASYNC)\n    ngx_event_t       *aev;\n#endif\n    ngx_connection_t  *c;\n\n    /* disable warning: Win32 SOCKET is u_int while UNIX socket is int */\n\n    if (ngx_cycle->files && (ngx_uint_t) s >= ngx_cycle->files_n) {\n        ngx_log_error(NGX_LOG_ALERT, log, 0,\n                      \"the new socket has number %d, \"\n                      \"but only %ui files are available\",\n                      s, ngx_cycle->files_n);\n        return NULL;\n    }\n\n    ngx_drain_connections((ngx_cycle_t *) ngx_cycle);\n\n    c = ngx_cycle->free_connections;\n\n    if (c == NULL) {\n        ngx_log_error(NGX_LOG_ALERT, log, 0,\n                      \"%ui worker_connections are not enough\",\n                      ngx_cycle->connection_n);\n\n        return NULL;\n    }\n\n    ngx_cycle->free_connections = c->data;\n    ngx_cycle->free_connection_n--;\n\n    if (ngx_cycle->files && ngx_cycle->files[s] == NULL) {\n        ngx_cycle->files[s] = c;\n    }\n\n    rev = c->read;\n    wev = c->write;\n#if (NGX_SSL && NGX_SSL_ASYNC)\n    aev = c->async;\n#endif\n\n    ngx_memzero(c, sizeof(ngx_connection_t));\n\n    c->read = rev;\n    c->write = wev;\n#if (NGX_SSL && NGX_SSL_ASYNC)\n    c->async = aev;\n#endif\n\n    c->fd = s;\n    c->log = log;\n\n    instance = rev->instance;\n\n    ngx_memzero(rev, sizeof(ngx_event_t));\n    ngx_memzero(wev, sizeof(ngx_event_t));\n#if (NGX_SSL && NGX_SSL_ASYNC)\n    ngx_memzero(aev, sizeof(ngx_event_t));\n#endif\n\n    rev->instance = !instance;\n    wev->instance = !instance;\n#if (NGX_SSL && NGX_SSL_ASYNC)\n    aev->instance = !instance;\n#endif\n\n    rev->index = NGX_INVALID_INDEX;\n    wev->index = NGX_INVALID_INDEX;\n#if (NGX_SSL && NGX_SSL_ASYNC)\n    aev->index = NGX_INVALID_INDEX;\n#endif\n\n    rev->data = c;\n    wev->data = c;\n#if (NGX_SSL && NGX_SSL_ASYNC)\n    aev->data = c;\n#endif\n\n    wev->write = 1;\n#if (NGX_SSL && NGX_SSL_ASYNC)\n    aev->async = 1;\n#endif\n\n    return c;\n}\n\n\nvoid\nngx_free_connection(ngx_connection_t *c)\n{\n    c->data = ngx_cycle->free_connections;\n    ngx_cycle->free_connections = c;\n    ngx_cycle->free_connection_n++;\n\n    if (ngx_cycle->files && ngx_cycle->files[c->fd] == c) {\n        ngx_cycle->files[c->fd] = NULL;\n    }\n}\n\n\nvoid\nngx_close_connection(ngx_connection_t *c)\n{\n    ngx_err_t     err;\n    ngx_uint_t    log_error, level;\n    ngx_socket_t  fd;\n\n    if (c->fd == (ngx_socket_t) -1) {\n        ngx_log_error(NGX_LOG_ALERT, c->log, 0, \"connection already closed\");\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        ngx_del_timer(c->write);\n    }\n\n#if (NGX_SSL && NGX_SSL_ASYNC)\n    if (c->async->timer_set) {\n        ngx_del_timer(c->async);\n    }\n\n    if (c->async_enable && ngx_del_async_conn) {\n        if (c->num_async_fds) {\n            ngx_del_async_conn(c, NGX_DISABLE_EVENT);\n            c->num_async_fds--;\n        }\n    }\n#endif\n\n    if (!c->shared) {\n        if (ngx_del_conn) {\n            ngx_del_conn(c, NGX_CLOSE_EVENT);\n\n        } else {\n#if (NGX_SSL && NGX_SSL_ASYNC)\n            if (c->async_enable && ngx_del_async_conn) {\n                if (c->num_async_fds) {\n                    ngx_del_async_conn(c, NGX_DISABLE_EVENT);\n                    c->num_async_fds--;\n                }\n            }\n#endif\n            if (c->read->active || c->read->disabled) {\n                ngx_del_event(c->read, NGX_READ_EVENT, NGX_CLOSE_EVENT);\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    }\n\n    if (c->read->posted) {\n        ngx_delete_posted_event(c->read);\n    }\n\n    if (c->write->posted) {\n        ngx_delete_posted_event(c->write);\n    }\n\n#if (NGX_SSL && NGX_SSL_ASYNC)\n    if (c->async->posted) {\n        ngx_delete_posted_event(c->async);\n    }\n#endif\n\n    c->read->closed = 1;\n    c->write->closed = 1;\n#if (NGX_SSL && NGX_SSL_ASYNC)\n    c->async->closed = 1;\n#endif\n\n    ngx_reusable_connection(c, 0);\n\n    log_error = c->log_error;\n\n    ngx_free_connection(c);\n\n    fd = c->fd;\n    c->fd = (ngx_socket_t) -1;\n#if (NGX_SSL && NGX_SSL_ASYNC)\n    c->async_fd = (ngx_socket_t) -1;\n#endif\n\n    if (c->shared) {\n        return;\n    }\n\n    if (ngx_close_socket(fd) == -1) {\n\n        err = ngx_socket_errno;\n\n        if (err == NGX_ECONNRESET || err == NGX_ENOTCONN) {\n\n            switch (log_error) {\n\n            case NGX_ERROR_INFO:\n                level = NGX_LOG_INFO;\n                break;\n\n            case NGX_ERROR_ERR:\n                level = NGX_LOG_ERR;\n                break;\n\n            default:\n                level = NGX_LOG_CRIT;\n            }\n\n        } else {\n            level = NGX_LOG_CRIT;\n        }\n\n        ngx_log_error(level, c->log, err, ngx_close_socket_n \" %d failed\", fd);\n    }\n}\n\n\nvoid\nngx_reusable_connection(ngx_connection_t *c, ngx_uint_t reusable)\n{\n    ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,\n                   \"reusable connection: %ui\", reusable);\n\n    if (c->reusable) {\n        ngx_queue_remove(&c->queue);\n        ngx_cycle->reusable_connections_n--;\n\n#if (NGX_STAT_STUB)\n        (void) ngx_atomic_fetch_add(ngx_stat_waiting, -1);\n#endif\n    }\n\n    c->reusable = reusable;\n\n    if (reusable) {\n        /* need cast as ngx_cycle is volatile */\n\n        ngx_queue_insert_head(\n            (ngx_queue_t *) &ngx_cycle->reusable_connections_queue, &c->queue);\n        ngx_cycle->reusable_connections_n++;\n\n#if (NGX_STAT_STUB)\n        (void) ngx_atomic_fetch_add(ngx_stat_waiting, 1);\n#endif\n    }\n}\n\n\nstatic void\nngx_drain_connections(ngx_cycle_t *cycle)\n{\n    ngx_uint_t         i, n;\n    ngx_queue_t       *q;\n    ngx_connection_t  *c;\n\n    if (cycle->free_connection_n > cycle->connection_n / 16\n        || cycle->reusable_connections_n == 0)\n    {\n        return;\n    }\n\n    if (cycle->connections_reuse_time != ngx_time()) {\n        cycle->connections_reuse_time = ngx_time();\n\n        ngx_log_error(NGX_LOG_WARN, cycle->log, 0,\n                      \"%ui worker_connections are not enough, \"\n                      \"reusing connections\",\n                      cycle->connection_n);\n    }\n\n    c = NULL;\n    n = ngx_max(ngx_min(32, cycle->reusable_connections_n / 8), 1);\n\n    for (i = 0; i < n; i++) {\n        if (ngx_queue_empty(&cycle->reusable_connections_queue)) {\n            break;\n        }\n\n        q = ngx_queue_last(&cycle->reusable_connections_queue);\n        c = ngx_queue_data(q, ngx_connection_t, queue);\n\n        ngx_log_debug0(NGX_LOG_DEBUG_CORE, c->log, 0,\n                       \"reusing connection\");\n\n        c->close = 1;\n        c->read->handler(c->read);\n    }\n\n    if (cycle->free_connection_n == 0 && c && c->reusable) {\n\n        /*\n         * if no connections were freed, try to reuse the last\n         * connection again: this should free it as long as\n         * previous reuse moved it to lingering close\n         */\n\n        ngx_log_debug0(NGX_LOG_DEBUG_CORE, c->log, 0,\n                       \"reusing connection again\");\n\n        c->close = 1;\n        c->read->handler(c->read);\n    }\n}\n\n\nvoid\nngx_close_idle_connections(ngx_cycle_t *cycle)\n{\n    ngx_uint_t         i;\n    ngx_connection_t  *c;\n\n    c = cycle->connections;\n\n    for (i = 0; i < cycle->connection_n; i++) {\n\n        /* THREAD: lock */\n\n        if (c[i].fd != (ngx_socket_t) -1 && c[i].idle) {\n            c[i].close = 1;\n            c[i].read->handler(c[i].read);\n        }\n    }\n}\n\n\nngx_int_t\nngx_connection_local_sockaddr(ngx_connection_t *c, ngx_str_t *s,\n    ngx_uint_t port)\n{\n    socklen_t             len;\n    ngx_uint_t            addr;\n    ngx_sockaddr_t        sa;\n    struct sockaddr_in   *sin;\n#if (NGX_HAVE_INET6)\n    ngx_uint_t            i;\n    struct sockaddr_in6  *sin6;\n#endif\n\n    addr = 0;\n\n    if (c->local_socklen) {\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\n            for (i = 0; addr == 0 && i < 16; i++) {\n                addr |= sin6->sin6_addr.s6_addr[i];\n            }\n\n            break;\n#endif\n\n#if (NGX_HAVE_UNIX_DOMAIN)\n        case AF_UNIX:\n            addr = 1;\n            break;\n#endif\n\n        default: /* AF_INET */\n            sin = (struct sockaddr_in *) c->local_sockaddr;\n            addr = sin->sin_addr.s_addr;\n            break;\n        }\n    }\n\n    if (addr == 0) {\n\n        len = sizeof(ngx_sockaddr_t);\n\n        if (getsockname(c->fd, &sa.sockaddr, &len) == -1) {\n            ngx_connection_error(c, ngx_socket_errno, \"getsockname() failed\");\n            return NGX_ERROR;\n        }\n\n        c->local_sockaddr = ngx_palloc(c->pool, len);\n        if (c->local_sockaddr == NULL) {\n            return NGX_ERROR;\n        }\n\n        ngx_memcpy(c->local_sockaddr, &sa, len);\n\n        c->local_socklen = len;\n    }\n\n    if (s == NULL) {\n        return NGX_OK;\n    }\n\n    s->len = ngx_sock_ntop(c->local_sockaddr, c->local_socklen,\n                           s->data, s->len, port);\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_tcp_nodelay(ngx_connection_t *c)\n{\n    int  tcp_nodelay;\n\n    if (c->tcp_nodelay != NGX_TCP_NODELAY_UNSET) {\n        return NGX_OK;\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_CORE, c->log, 0, \"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#if (NGX_SOLARIS)\n        if (c->log_error == NGX_ERROR_INFO) {\n\n            /* Solaris returns EINVAL if a socket has been shut down */\n            c->log_error = NGX_ERROR_IGNORE_EINVAL;\n\n            ngx_connection_error(c, ngx_socket_errno,\n                                 \"setsockopt(TCP_NODELAY) failed\");\n\n            c->log_error = NGX_ERROR_INFO;\n\n            return NGX_ERROR;\n        }\n#endif\n\n        ngx_connection_error(c, ngx_socket_errno,\n                             \"setsockopt(TCP_NODELAY) failed\");\n        return NGX_ERROR;\n    }\n\n    c->tcp_nodelay = NGX_TCP_NODELAY_SET;\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_connection_error(ngx_connection_t *c, ngx_err_t err, char *text)\n{\n    ngx_uint_t  level;\n\n    /* Winsock may return NGX_ECONNABORTED instead of NGX_ECONNRESET */\n\n    if ((err == NGX_ECONNRESET\n#if (NGX_WIN32)\n         || err == NGX_ECONNABORTED\n#endif\n        ) && c->log_error == NGX_ERROR_IGNORE_ECONNRESET)\n    {\n        return 0;\n    }\n\n#if (NGX_SOLARIS)\n    if (err == NGX_EINVAL && c->log_error == NGX_ERROR_IGNORE_EINVAL) {\n        return 0;\n    }\n#endif\n\n    if (err == 0\n        || err == NGX_ECONNRESET\n#if (NGX_WIN32)\n        || err == NGX_ECONNABORTED\n#else\n        || err == NGX_EPIPE\n#endif\n        || err == NGX_ENOTCONN\n        || err == NGX_ETIMEDOUT\n        || err == NGX_ECONNREFUSED\n        || err == NGX_ENETDOWN\n        || err == NGX_ENETUNREACH\n        || err == NGX_EHOSTDOWN\n        || err == NGX_EHOSTUNREACH)\n    {\n        switch (c->log_error) {\n\n        case NGX_ERROR_IGNORE_EINVAL:\n        case NGX_ERROR_IGNORE_ECONNRESET:\n        case NGX_ERROR_INFO:\n            level = NGX_LOG_INFO;\n            break;\n\n        default:\n            level = NGX_LOG_ERR;\n        }\n\n    } else {\n        level = NGX_LOG_ALERT;\n    }\n\n    ngx_log_error(level, c->log, err, text);\n\n    return NGX_ERROR;\n}\n"
  },
  {
    "path": "src/core/ngx_connection.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_CONNECTION_H_INCLUDED_\n#define _NGX_CONNECTION_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\ntypedef struct ngx_listening_s  ngx_listening_t;\n\n#if (T_NGX_UDPV2)\n\ntypedef enum\n{\n    NGX_UDPV2_DONE = 0,\n    NGX_UDPV2_PASS,\n    NGX_UDPV2_DROP,\n} ngx_udpv2_traffic_filter_retcode;\n\n/**\n * @return\n * */\ntypedef ngx_udpv2_traffic_filter_retcode (*ngx_udpv2_traffic_filter_handler) (ngx_listening_t *ls, const ngx_udpv2_packet_t *upkt);\n\nstruct ngx_udpv2_traffic_filter_st\n{\n    ngx_udpv2_traffic_filter_handler    func;\n    ngx_queue_t                         sk;\n};\n#endif\n\nstruct ngx_listening_s {\n    ngx_socket_t        fd;\n\n    struct sockaddr    *sockaddr;\n    socklen_t           socklen;    /* size of sockaddr */\n    size_t              addr_text_max_len;\n    ngx_str_t           addr_text;\n\n    int                 type;\n\n    int                 backlog;\n    int                 rcvbuf;\n    int                 sndbuf;\n#if (NGX_HAVE_KEEPALIVE_TUNABLE)\n    int                 keepidle;\n    int                 keepintvl;\n    int                 keepcnt;\n#endif\n\n#if (T_NGX_UDPV2)\n    /* distribution of writable events */\n    ngx_queue_t                 writable_queue;\n    /* filter for udpv2 */\n    ngx_queue_t                 udpv2_filter;\n    /* current processing */\n    ngx_udpv2_packet_t*         udpv2_current_processing;\n    /* default filter */\n    ngx_udpv2_traffic_filter_t  udpv2_traffic_filter;\n#endif\n\n    /* handler of accepted connection */\n    ngx_connection_handler_pt   handler;\n\n    void               *servers;  /* array of ngx_http_in_addr_t, for example */\n\n    ngx_log_t           log;\n    ngx_log_t          *logp;\n\n    size_t              pool_size;\n    /* should be here because of the AcceptEx() preread */\n    size_t              post_accept_buffer_size;\n\n    ngx_listening_t    *previous;\n    ngx_connection_t   *connection;\n\n    ngx_rbtree_t        rbtree;\n    ngx_rbtree_node_t   sentinel;\n\n#if (T_NGX_HAVE_XUDP)\n    ngx_xudp_channel_t *ngx_xudp_ch;\n    ngx_queue_t         xudp_sentinel;\n#endif\n\n    ngx_uint_t          worker;\n\n    unsigned            open:1;\n    unsigned            remain:1;\n    unsigned            ignore:1;\n\n    unsigned            bound:1;       /* already bound */\n    unsigned            inherited:1;   /* inherited from previous process */\n    unsigned            nonblocking_accept:1;\n    unsigned            listen:1;\n    unsigned            nonblocking:1;\n    unsigned            shared:1;    /* shared between threads or processes */\n    unsigned            addr_ntop:1;\n    unsigned            wildcard:1;\n#if (T_NGX_XQUIC)\n    unsigned            xquic:1;\n#endif\n#if (NGX_HAVE_INET6)\n    unsigned            ipv6only:1;\n#endif\n    unsigned            reuseport:1;\n    unsigned            add_reuseport:1;\n    unsigned            keepalive:2;\n\n#if (T_NGX_UDPV2)\n    unsigned            support_udpv2:1;\n#endif\n\n#if (T_NGX_HAVE_XUDP)\n    unsigned            for_xudp:1;    /* xudp listener */\n    unsigned            xudp:1;\n#endif\n\n    unsigned            deferred_accept:1;\n    unsigned            delete_deferred:1;\n    unsigned            add_deferred:1;\n#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)\n    char               *accept_filter;\n#endif\n#if (NGX_HAVE_SETFIB)\n    int                 setfib;\n#endif\n\n#if (NGX_HAVE_TCP_FASTOPEN)\n    int                 fastopen;\n#endif\n\n};\n\n\ntypedef enum {\n    NGX_ERROR_ALERT = 0,\n    NGX_ERROR_ERR,\n    NGX_ERROR_INFO,\n    NGX_ERROR_IGNORE_ECONNRESET,\n    NGX_ERROR_IGNORE_EINVAL\n} ngx_connection_log_error_e;\n\n\ntypedef enum {\n    NGX_TCP_NODELAY_UNSET = 0,\n    NGX_TCP_NODELAY_SET,\n    NGX_TCP_NODELAY_DISABLED\n} ngx_connection_tcp_nodelay_e;\n\n\ntypedef enum {\n    NGX_TCP_NOPUSH_UNSET = 0,\n    NGX_TCP_NOPUSH_SET,\n    NGX_TCP_NOPUSH_DISABLED\n} ngx_connection_tcp_nopush_e;\n\n\n#define NGX_LOWLEVEL_BUFFERED  0x0f\n#define NGX_SSL_BUFFERED       0x01\n#define NGX_HTTP_V2_BUFFERED   0x02\n\n\nstruct ngx_connection_s {\n    void               *data;\n#if (T_NGX_MULTI_UPSTREAM)\n    void               *multi_c;\n#endif\n    ngx_event_t        *read;\n    ngx_event_t        *write;\n#if (NGX_SSL && NGX_SSL_ASYNC)\n    ngx_event_t        *async;\n#endif\n\n    ngx_socket_t        fd;\n#if (NGX_SSL && NGX_SSL_ASYNC)\n    ngx_socket_t        async_fd;\n#endif\n\n    ngx_recv_pt         recv;\n    ngx_send_pt         send;\n    ngx_recv_chain_pt   recv_chain;\n    ngx_send_chain_pt   send_chain;\n\n    ngx_listening_t    *listening;\n\n    off_t               sent;\n#if (T_NGX_REQ_STATUS)\n    off_t               received;\n#endif\n\n    ngx_log_t          *log;\n\n    ngx_pool_t         *pool;\n\n    int                 type;\n\n    struct sockaddr    *sockaddr;\n    socklen_t           socklen;\n    ngx_str_t           addr_text;\n\n    ngx_proxy_protocol_t  *proxy_protocol;\n\n#if (NGX_SSL || NGX_COMPAT)\n    ngx_ssl_connection_t  *ssl;\n#if (NGX_SSL_ASYNC)\n    ngx_flag_t          async_enable;\n#endif\n#endif\n\n    ngx_udp_connection_t  *udp;\n\n    struct sockaddr    *local_sockaddr;\n    socklen_t           local_socklen;\n\n    ngx_buf_t          *buffer;\n\n    ngx_queue_t         queue;\n\n    ngx_atomic_uint_t   number;\n\n    ngx_msec_t          start_time;\n    ngx_uint_t          requests;\n\n    unsigned            buffered:8;\n\n    unsigned            log_error:3;     /* ngx_connection_log_error_e */\n\n    unsigned            timedout:1;\n    unsigned            error:1;\n    unsigned            destroyed:1;\n    unsigned            pipeline:1;\n\n    unsigned            idle:1;\n    unsigned            reusable:1;\n    unsigned            close:1;\n    unsigned            shared:1;\n\n    unsigned            sendfile:1;\n    unsigned            sndlowat:1;\n    unsigned            tcp_nodelay:2;   /* ngx_connection_tcp_nodelay_e */\n    unsigned            tcp_nopush:2;    /* ngx_connection_tcp_nopush_e */\n\n    unsigned            need_last_buf:1;\n    unsigned            need_flush_buf:1;\n#if (NGX_SSL && NGX_SSL_ASYNC)\n    unsigned            num_async_fds:8;\n#endif\n\n#if (NGX_HAVE_SENDFILE_NODISKIO || NGX_COMPAT)\n    unsigned            busy_count:2;\n#endif\n\n#if (NGX_THREADS || NGX_COMPAT)\n    ngx_thread_task_t  *sendfile_task;\n#endif\n\n#if (T_NGX_HAVE_XUDP)\n    unsigned            xudp_tx:1;\n#endif\n\n#if (T_NGX_XQUIC)\n    unsigned            xquic_conn:1;\n#endif\n};\n\n\n#define ngx_set_connection_log(c, l)                                         \\\n                                                                             \\\n    c->log->file = l->file;                                                  \\\n    c->log->next = l->next;                                                  \\\n    c->log->writer = l->writer;                                              \\\n    c->log->wdata = l->wdata;                                                \\\n    if (!(c->log->log_level & NGX_LOG_DEBUG_CONNECTION)) {                   \\\n        c->log->log_level = l->log_level;                                    \\\n    }\n\n\nngx_listening_t *ngx_create_listening(ngx_conf_t *cf, struct sockaddr *sockaddr,\n    socklen_t socklen);\nngx_int_t ngx_clone_listening(ngx_cycle_t *cycle, ngx_listening_t *ls);\nngx_int_t ngx_set_inherited_sockets(ngx_cycle_t *cycle);\nngx_int_t ngx_open_listening_sockets(ngx_cycle_t *cycle);\nvoid ngx_configure_listening_sockets(ngx_cycle_t *cycle);\nvoid ngx_close_listening_sockets(ngx_cycle_t *cycle);\nvoid ngx_close_connection(ngx_connection_t *c);\nvoid ngx_close_idle_connections(ngx_cycle_t *cycle);\nngx_int_t ngx_connection_local_sockaddr(ngx_connection_t *c, ngx_str_t *s,\n    ngx_uint_t port);\nngx_int_t ngx_tcp_nodelay(ngx_connection_t *c);\nngx_int_t ngx_connection_error(ngx_connection_t *c, ngx_err_t err, char *text);\n\nngx_connection_t *ngx_get_connection(ngx_socket_t s, ngx_log_t *log);\nvoid ngx_free_connection(ngx_connection_t *c);\n\nvoid ngx_reusable_connection(ngx_connection_t *c, ngx_uint_t reusable);\n\n#endif /* _NGX_CONNECTION_H_INCLUDED_ */\n"
  },
  {
    "path": "src/core/ngx_core.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_CORE_H_INCLUDED_\n#define _NGX_CORE_H_INCLUDED_\n\n\n#include <ngx_config.h>\n\n\ntypedef struct ngx_module_s          ngx_module_t;\ntypedef struct ngx_conf_s            ngx_conf_t;\ntypedef struct ngx_cycle_s           ngx_cycle_t;\ntypedef struct ngx_pool_s            ngx_pool_t;\ntypedef struct ngx_chain_s           ngx_chain_t;\ntypedef struct ngx_log_s             ngx_log_t;\ntypedef struct ngx_open_file_s       ngx_open_file_t;\ntypedef struct ngx_command_s         ngx_command_t;\ntypedef struct ngx_file_s            ngx_file_t;\ntypedef struct ngx_event_s           ngx_event_t;\ntypedef struct ngx_event_aio_s       ngx_event_aio_t;\ntypedef struct ngx_connection_s      ngx_connection_t;\ntypedef struct ngx_thread_task_s     ngx_thread_task_t;\ntypedef struct ngx_ssl_s             ngx_ssl_t;\ntypedef struct ngx_proxy_protocol_s  ngx_proxy_protocol_t;\ntypedef struct ngx_ssl_connection_s  ngx_ssl_connection_t;\ntypedef struct ngx_udp_connection_s  ngx_udp_connection_t;\n\n#if (T_NGX_UDPV2)\ntypedef struct ngx_udpv2_packet_st                          ngx_udpv2_packet_t;\ntypedef struct ngx_udpv2_packets_hdr_st                     ngx_udpv2_packets_hdr_t;\ntypedef struct ngx_udpv2_traffic_filter_st                  ngx_udpv2_traffic_filter_t;\n#endif\n\n#if (T_NGX_HAVE_XUDP)\n#include <ngx_xudp_inc.h>\n#endif\n\ntypedef void (*ngx_event_handler_pt)(ngx_event_t *ev);\ntypedef void (*ngx_connection_handler_pt)(ngx_connection_t *c);\n\n\n#define  NGX_OK          0\n#define  NGX_ERROR      -1\n#define  NGX_AGAIN      -2\n#define  NGX_BUSY       -3\n#define  NGX_DONE       -4\n#define  NGX_DECLINED   -5\n#define  NGX_ABORT      -6\n#if (T_NGX_HTTP_DYNAMIC_RESOLVE)\n#define  NGX_YIELD      -7\n#endif\n\n\n#include <ngx_errno.h>\n#include <ngx_atomic.h>\n#include <ngx_thread.h>\n#include <ngx_rbtree.h>\n#include <ngx_time.h>\n#include <ngx_socket.h>\n#include <ngx_string.h>\n#include <ngx_files.h>\n#include <ngx_shmem.h>\n#include <ngx_process.h>\n#include <ngx_user.h>\n#include <ngx_dlopen.h>\n#include <ngx_parse.h>\n#include <ngx_parse_time.h>\n#include <ngx_log.h>\n#include <ngx_alloc.h>\n#include <ngx_sysinfo.h>\n#include <ngx_palloc.h>\n#include <ngx_buf.h>\n#include <ngx_queue.h>\n#include <ngx_array.h>\n#include <ngx_list.h>\n#include <ngx_hash.h>\n#include <ngx_file.h>\n#include <ngx_crc.h>\n#include <ngx_crc32.h>\n#include <ngx_murmurhash.h>\n#if (NGX_PCRE)\n#include <ngx_regex.h>\n#endif\n#include <ngx_trie.h>\n#include <ngx_radix_tree.h>\n#include <ngx_segment_tree.h>\n#include <ngx_times.h>\n#include <ngx_rwlock.h>\n#include <ngx_shmtx.h>\n#include <ngx_slab.h>\n#include <ngx_inet.h>\n#include <ngx_cycle.h>\n#include <ngx_resolver.h>\n#if (NGX_OPENSSL)\n#include <ngx_event_openssl.h>\n#endif\n#include <ngx_process_cycle.h>\n#include <ngx_conf_file.h>\n#include <ngx_module.h>\n#include <ngx_open_file_cache.h>\n#include <ngx_os.h>\n#include <ngx_connection.h>\n#include <ngx_syslog.h>\n#include <ngx_proxy_protocol.h>\n#if (T_PIPES)\n#include <ngx_pipe.h>\n#endif\n#if (NGX_PROCS)\n#include <ngx_proc.h>\n#endif\n\n\n#define LF     (u_char) '\\n'\n#define CR     (u_char) '\\r'\n#define CRLF   \"\\r\\n\"\n\n\n#define ngx_abs(value)       (((value) >= 0) ? (value) : - (value))\n#define ngx_max(val1, val2)  ((val1 < val2) ? (val2) : (val1))\n#define ngx_min(val1, val2)  ((val1 > val2) ? (val2) : (val1))\n\nvoid ngx_cpuinfo(void);\n\n#if (NGX_HAVE_OPENAT)\n#define NGX_DISABLE_SYMLINKS_OFF        0\n#define NGX_DISABLE_SYMLINKS_ON         1\n#define NGX_DISABLE_SYMLINKS_NOTOWNER   2\n#endif\n\n#endif /* _NGX_CORE_H_INCLUDED_ */\n"
  },
  {
    "path": "src/core/ngx_cpuinfo.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\n#if (( __i386__ || __amd64__ ) && ( __GNUC__ || __INTEL_COMPILER ))\n\n\nstatic ngx_inline void ngx_cpuid(uint32_t i, uint32_t *buf);\n\n\n#if ( __i386__ )\n\nstatic ngx_inline void\nngx_cpuid(uint32_t i, uint32_t *buf)\n{\n\n    /*\n     * we could not use %ebx as output parameter if gcc builds PIC,\n     * and we could not save %ebx on stack, because %esp is used,\n     * when the -fomit-frame-pointer optimization is specified.\n     */\n\n    __asm__ (\n\n    \"    mov    %%ebx, %%esi;  \"\n\n    \"    cpuid;                \"\n    \"    mov    %%eax, (%1);   \"\n    \"    mov    %%ebx, 4(%1);  \"\n    \"    mov    %%edx, 8(%1);  \"\n    \"    mov    %%ecx, 12(%1); \"\n\n    \"    mov    %%esi, %%ebx;  \"\n\n    : : \"a\" (i), \"D\" (buf) : \"ecx\", \"edx\", \"esi\", \"memory\" );\n}\n\n\n#else /* __amd64__ */\n\n\nstatic ngx_inline void\nngx_cpuid(uint32_t i, uint32_t *buf)\n{\n    uint32_t  eax, ebx, ecx, edx;\n\n    __asm__ (\n\n        \"cpuid\"\n\n    : \"=a\" (eax), \"=b\" (ebx), \"=c\" (ecx), \"=d\" (edx) : \"a\" (i) );\n\n    buf[0] = eax;\n    buf[1] = ebx;\n    buf[2] = edx;\n    buf[3] = ecx;\n}\n\n\n#endif\n\n\n/* auto detect the L2 cache line size of modern and widespread CPUs */\n\nvoid\nngx_cpuinfo(void)\n{\n    u_char    *vendor;\n    uint32_t   vbuf[5], cpu[4], model;\n\n    vbuf[0] = 0;\n    vbuf[1] = 0;\n    vbuf[2] = 0;\n    vbuf[3] = 0;\n    vbuf[4] = 0;\n\n    ngx_cpuid(0, vbuf);\n\n    vendor = (u_char *) &vbuf[1];\n\n    if (vbuf[0] == 0) {\n        return;\n    }\n\n    ngx_cpuid(1, cpu);\n\n    if (ngx_strcmp(vendor, \"GenuineIntel\") == 0) {\n\n        switch ((cpu[0] & 0xf00) >> 8) {\n\n        /* Pentium */\n        case 5:\n            ngx_cacheline_size = 32;\n            break;\n\n        /* Pentium Pro, II, III */\n        case 6:\n            ngx_cacheline_size = 32;\n\n            model = ((cpu[0] & 0xf0000) >> 8) | (cpu[0] & 0xf0);\n\n            if (model >= 0xd0) {\n                /* Intel Core, Core 2, Atom */\n                ngx_cacheline_size = 64;\n            }\n\n            break;\n\n        /*\n         * Pentium 4, although its cache line size is 64 bytes,\n         * it prefetches up to two cache lines during memory read\n         */\n        case 15:\n            ngx_cacheline_size = 128;\n            break;\n        }\n\n    } else if (ngx_strcmp(vendor, \"AuthenticAMD\") == 0) {\n        ngx_cacheline_size = 64;\n    }\n}\n\n#else\n\n\nvoid\nngx_cpuinfo(void)\n{\n}\n\n\n#endif\n"
  },
  {
    "path": "src/core/ngx_crc.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_CRC_H_INCLUDED_\n#define _NGX_CRC_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\n/* 32-bit crc16 */\n\nstatic ngx_inline uint32_t\nngx_crc(u_char *data, size_t len)\n{\n    uint32_t  sum;\n\n    for (sum = 0; len; len--) {\n\n        /*\n         * gcc 2.95.2 x86 and icc 7.1.006 compile\n         * that operator into the single \"rol\" opcode,\n         * msvc 6.0sp2 compiles it into four opcodes.\n         */\n        sum = sum >> 1 | sum << 31;\n\n        sum += *data++;\n    }\n\n    return sum;\n}\n\n\n#endif /* _NGX_CRC_H_INCLUDED_ */\n"
  },
  {
    "path": "src/core/ngx_crc32.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\n/*\n * The code and lookup tables are based on the algorithm\n * described at http://www.w3.org/TR/PNG/\n *\n * The 256 element lookup table takes 1024 bytes, and it may be completely\n * cached after processing about 30-60 bytes of data.  So for short data\n * we use the 16 element lookup table that takes only 64 bytes and align it\n * to CPU cache line size.  Of course, the small table adds code inside\n * CRC32 loop, but the cache misses overhead is bigger than overhead of\n * the additional code.  For example, ngx_crc32_short() of 16 bytes of data\n * takes half as much CPU clocks than ngx_crc32_long().\n */\n\n\nstatic uint32_t  ngx_crc32_table16[] = {\n    0x00000000, 0x1db71064, 0x3b6e20c8, 0x26d930ac,\n    0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c,\n    0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c,\n    0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c\n};\n\n\nuint32_t  ngx_crc32_table256[] = {\n    0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,\n    0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,\n    0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,\n    0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,\n    0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,\n    0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,\n    0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,\n    0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,\n    0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,\n    0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,\n    0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,\n    0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,\n    0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,\n    0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,\n    0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,\n    0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,\n    0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,\n    0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,\n    0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,\n    0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,\n    0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,\n    0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,\n    0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,\n    0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,\n    0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,\n    0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,\n    0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,\n    0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,\n    0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,\n    0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,\n    0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,\n    0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,\n    0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,\n    0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,\n    0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,\n    0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,\n    0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,\n    0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,\n    0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,\n    0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,\n    0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,\n    0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,\n    0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,\n    0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,\n    0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,\n    0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,\n    0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,\n    0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,\n    0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,\n    0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,\n    0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,\n    0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,\n    0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,\n    0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,\n    0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,\n    0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,\n    0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,\n    0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,\n    0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,\n    0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,\n    0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,\n    0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,\n    0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,\n    0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d\n};\n\n\nuint32_t *ngx_crc32_table_short = ngx_crc32_table16;\n\n\nngx_int_t\nngx_crc32_table_init(void)\n{\n    void  *p;\n\n    if (((uintptr_t) ngx_crc32_table_short\n          & ~((uintptr_t) ngx_cacheline_size - 1))\n        == (uintptr_t) ngx_crc32_table_short)\n    {\n        return NGX_OK;\n    }\n\n    p = ngx_alloc(16 * sizeof(uint32_t) + ngx_cacheline_size, ngx_cycle->log);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    p = ngx_align_ptr(p, ngx_cacheline_size);\n\n    ngx_memcpy(p, ngx_crc32_table16, 16 * sizeof(uint32_t));\n\n    ngx_crc32_table_short = p;\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "src/core/ngx_crc32.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_CRC32_H_INCLUDED_\n#define _NGX_CRC32_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\nextern uint32_t  *ngx_crc32_table_short;\nextern uint32_t   ngx_crc32_table256[];\n\n\nstatic ngx_inline uint32_t\nngx_crc32_short(u_char *p, size_t len)\n{\n    u_char    c;\n    uint32_t  crc;\n\n    crc = 0xffffffff;\n\n    while (len--) {\n        c = *p++;\n        crc = ngx_crc32_table_short[(crc ^ (c & 0xf)) & 0xf] ^ (crc >> 4);\n        crc = ngx_crc32_table_short[(crc ^ (c >> 4)) & 0xf] ^ (crc >> 4);\n    }\n\n    return crc ^ 0xffffffff;\n}\n\n\nstatic ngx_inline uint32_t\nngx_crc32_long(u_char *p, size_t len)\n{\n    uint32_t  crc;\n\n    crc = 0xffffffff;\n\n    while (len--) {\n        crc = ngx_crc32_table256[(crc ^ *p++) & 0xff] ^ (crc >> 8);\n    }\n\n    return crc ^ 0xffffffff;\n}\n\n\n#define ngx_crc32_init(crc)                                                   \\\n    crc = 0xffffffff\n\n\nstatic ngx_inline void\nngx_crc32_update(uint32_t *crc, u_char *p, size_t len)\n{\n    uint32_t  c;\n\n    c = *crc;\n\n    while (len--) {\n        c = ngx_crc32_table256[(c ^ *p++) & 0xff] ^ (c >> 8);\n    }\n\n    *crc = c;\n}\n\n\n#define ngx_crc32_final(crc)                                                  \\\n    crc ^= 0xffffffff\n\n\nngx_int_t ngx_crc32_table_init(void);\n\n\n#endif /* _NGX_CRC32_H_INCLUDED_ */\n"
  },
  {
    "path": "src/core/ngx_crypt.c",
    "content": "\n/*\n * Copyright (C) Maxim Dounin\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_crypt.h>\n#include <ngx_md5.h>\n#include <ngx_sha1.h>\n\n\n#if (NGX_CRYPT)\n\nstatic ngx_int_t ngx_crypt_apr1(ngx_pool_t *pool, u_char *key, u_char *salt,\n    u_char **encrypted);\nstatic ngx_int_t ngx_crypt_plain(ngx_pool_t *pool, u_char *key, u_char *salt,\n    u_char **encrypted);\nstatic ngx_int_t ngx_crypt_ssha(ngx_pool_t *pool, u_char *key, u_char *salt,\n    u_char **encrypted);\nstatic ngx_int_t ngx_crypt_sha(ngx_pool_t *pool, u_char *key, u_char *salt,\n    u_char **encrypted);\n\n\nstatic u_char *ngx_crypt_to64(u_char *p, uint32_t v, size_t n);\n\n\nngx_int_t\nngx_crypt(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted)\n{\n    if (ngx_strncmp(salt, \"$apr1$\", sizeof(\"$apr1$\") - 1) == 0) {\n        return ngx_crypt_apr1(pool, key, salt, encrypted);\n\n    } else if (ngx_strncmp(salt, \"{PLAIN}\", sizeof(\"{PLAIN}\") - 1) == 0) {\n        return ngx_crypt_plain(pool, key, salt, encrypted);\n\n    } else if (ngx_strncmp(salt, \"{SSHA}\", sizeof(\"{SSHA}\") - 1) == 0) {\n        return ngx_crypt_ssha(pool, key, salt, encrypted);\n\n    } else if (ngx_strncmp(salt, \"{SHA}\", sizeof(\"{SHA}\") - 1) == 0) {\n        return ngx_crypt_sha(pool, key, salt, encrypted);\n    }\n\n    /* fallback to libc crypt() */\n\n    return ngx_libc_crypt(pool, key, salt, encrypted);\n}\n\n\nstatic ngx_int_t\nngx_crypt_apr1(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted)\n{\n    ngx_int_t          n;\n    ngx_uint_t         i;\n    u_char            *p, *last, final[16];\n    size_t             saltlen, keylen;\n    ngx_md5_t          md5, ctx1;\n\n    /* Apache's apr1 crypt is Poul-Henning Kamp's md5 crypt with $apr1$ magic */\n\n    keylen = ngx_strlen(key);\n\n    /* true salt: no magic, max 8 chars, stop at first $ */\n\n    salt += sizeof(\"$apr1$\") - 1;\n    last = salt + 8;\n    for (p = salt; *p && *p != '$' && p < last; p++) { /* void */ }\n    saltlen = p - salt;\n\n    /* hash key and salt */\n\n    ngx_md5_init(&md5);\n    ngx_md5_update(&md5, key, keylen);\n    ngx_md5_update(&md5, (u_char *) \"$apr1$\", sizeof(\"$apr1$\") - 1);\n    ngx_md5_update(&md5, salt, saltlen);\n\n    ngx_md5_init(&ctx1);\n    ngx_md5_update(&ctx1, key, keylen);\n    ngx_md5_update(&ctx1, salt, saltlen);\n    ngx_md5_update(&ctx1, key, keylen);\n    ngx_md5_final(final, &ctx1);\n\n    for (n = keylen; n > 0; n -= 16) {\n        ngx_md5_update(&md5, final, n > 16 ? 16 : n);\n    }\n\n    ngx_memzero(final, sizeof(final));\n\n    for (i = keylen; i; i >>= 1) {\n        if (i & 1) {\n            ngx_md5_update(&md5, final, 1);\n\n        } else {\n            ngx_md5_update(&md5, key, 1);\n        }\n    }\n\n    ngx_md5_final(final, &md5);\n\n    for (i = 0; i < 1000; i++) {\n        ngx_md5_init(&ctx1);\n\n        if (i & 1) {\n            ngx_md5_update(&ctx1, key, keylen);\n\n        } else {\n            ngx_md5_update(&ctx1, final, 16);\n        }\n\n        if (i % 3) {\n            ngx_md5_update(&ctx1, salt, saltlen);\n        }\n\n        if (i % 7) {\n            ngx_md5_update(&ctx1, key, keylen);\n        }\n\n        if (i & 1) {\n            ngx_md5_update(&ctx1, final, 16);\n\n        } else {\n            ngx_md5_update(&ctx1, key, keylen);\n        }\n\n        ngx_md5_final(final, &ctx1);\n    }\n\n    /* output */\n\n    *encrypted = ngx_pnalloc(pool, sizeof(\"$apr1$\") - 1 + saltlen + 1 + 22 + 1);\n    if (*encrypted == NULL) {\n        return NGX_ERROR;\n    }\n\n    p = ngx_cpymem(*encrypted, \"$apr1$\", sizeof(\"$apr1$\") - 1);\n    p = ngx_copy(p, salt, saltlen);\n    *p++ = '$';\n\n    p = ngx_crypt_to64(p, (final[ 0]<<16) | (final[ 6]<<8) | final[12], 4);\n    p = ngx_crypt_to64(p, (final[ 1]<<16) | (final[ 7]<<8) | final[13], 4);\n    p = ngx_crypt_to64(p, (final[ 2]<<16) | (final[ 8]<<8) | final[14], 4);\n    p = ngx_crypt_to64(p, (final[ 3]<<16) | (final[ 9]<<8) | final[15], 4);\n    p = ngx_crypt_to64(p, (final[ 4]<<16) | (final[10]<<8) | final[ 5], 4);\n    p = ngx_crypt_to64(p, final[11], 2);\n    *p = '\\0';\n\n    return NGX_OK;\n}\n\n\nstatic u_char *\nngx_crypt_to64(u_char *p, uint32_t v, size_t n)\n{\n    static u_char   itoa64[] =\n        \"./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\";\n\n    while (n--) {\n        *p++ = itoa64[v & 0x3f];\n        v >>= 6;\n    }\n\n    return p;\n}\n\n\nstatic ngx_int_t\nngx_crypt_plain(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted)\n{\n    size_t   len;\n    u_char  *p;\n\n    len = ngx_strlen(key);\n\n    *encrypted = ngx_pnalloc(pool, sizeof(\"{PLAIN}\") - 1 + len + 1);\n    if (*encrypted == NULL) {\n        return NGX_ERROR;\n    }\n\n    p = ngx_cpymem(*encrypted, \"{PLAIN}\", sizeof(\"{PLAIN}\") - 1);\n    ngx_memcpy(p, key, len + 1);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_crypt_ssha(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted)\n{\n    size_t       len;\n    ngx_int_t    rc;\n    ngx_str_t    encoded, decoded;\n    ngx_sha1_t   sha1;\n\n    /* \"{SSHA}\" base64(SHA1(key salt) salt) */\n\n    /* decode base64 salt to find out true salt */\n\n    encoded.data = salt + sizeof(\"{SSHA}\") - 1;\n    encoded.len = ngx_strlen(encoded.data);\n\n    len = ngx_max(ngx_base64_decoded_length(encoded.len), 20);\n\n    decoded.data = ngx_pnalloc(pool, len);\n    if (decoded.data == NULL) {\n        return NGX_ERROR;\n    }\n\n    rc = ngx_decode_base64(&decoded, &encoded);\n\n    if (rc != NGX_OK || decoded.len < 20) {\n        decoded.len = 20;\n    }\n\n    /* update SHA1 from key and salt */\n\n    ngx_sha1_init(&sha1);\n    ngx_sha1_update(&sha1, key, ngx_strlen(key));\n    ngx_sha1_update(&sha1, decoded.data + 20, decoded.len - 20);\n    ngx_sha1_final(decoded.data, &sha1);\n\n    /* encode it back to base64 */\n\n    len = sizeof(\"{SSHA}\") - 1 + ngx_base64_encoded_length(decoded.len) + 1;\n\n    *encrypted = ngx_pnalloc(pool, len);\n    if (*encrypted == NULL) {\n        return NGX_ERROR;\n    }\n\n    encoded.data = ngx_cpymem(*encrypted, \"{SSHA}\", sizeof(\"{SSHA}\") - 1);\n    ngx_encode_base64(&encoded, &decoded);\n    encoded.data[encoded.len] = '\\0';\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_crypt_sha(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted)\n{\n    size_t      len;\n    ngx_str_t   encoded, decoded;\n    ngx_sha1_t  sha1;\n    u_char      digest[20];\n\n    /* \"{SHA}\" base64(SHA1(key)) */\n\n    decoded.len = sizeof(digest);\n    decoded.data = digest;\n\n    ngx_sha1_init(&sha1);\n    ngx_sha1_update(&sha1, key, ngx_strlen(key));\n    ngx_sha1_final(digest, &sha1);\n\n    len = sizeof(\"{SHA}\") - 1 + ngx_base64_encoded_length(decoded.len) + 1;\n\n    *encrypted = ngx_pnalloc(pool, len);\n    if (*encrypted == NULL) {\n        return NGX_ERROR;\n    }\n\n    encoded.data = ngx_cpymem(*encrypted, \"{SHA}\", sizeof(\"{SHA}\") - 1);\n    ngx_encode_base64(&encoded, &decoded);\n    encoded.data[encoded.len] = '\\0';\n\n    return NGX_OK;\n}\n\n#endif /* NGX_CRYPT */\n"
  },
  {
    "path": "src/core/ngx_crypt.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_CRYPT_H_INCLUDED_\n#define _NGX_CRYPT_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\nngx_int_t ngx_crypt(ngx_pool_t *pool, u_char *key, u_char *salt,\n    u_char **encrypted);\n\n\n#endif /* _NGX_CRYPT_H_INCLUDED_ */\n"
  },
  {
    "path": "src/core/ngx_cycle.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n\n#if (T_NGX_SHOW_INFO)\nextern ngx_uint_t  ngx_modules_n;\n#endif\n\nstatic void ngx_destroy_cycle_pools(ngx_conf_t *conf);\nstatic ngx_int_t ngx_init_zone_pool(ngx_cycle_t *cycle,\n    ngx_shm_zone_t *shm_zone);\nstatic ngx_int_t ngx_test_lockfile(u_char *file, ngx_log_t *log);\nstatic void ngx_clean_old_cycles(ngx_event_t *ev);\nstatic void ngx_shutdown_timer_handler(ngx_event_t *ev);\n\n\nvolatile ngx_cycle_t  *ngx_cycle;\nngx_array_t            ngx_old_cycles;\n\nstatic ngx_pool_t     *ngx_temp_pool;\nstatic ngx_event_t     ngx_cleaner_event;\nstatic ngx_event_t     ngx_shutdown_event;\n\nngx_uint_t             ngx_test_config;\nngx_uint_t             ngx_dump_config;\nngx_uint_t             ngx_quiet_mode;\n#if (T_NGX_SHOW_INFO)\nngx_uint_t             ngx_show_modules;\nngx_uint_t             ngx_show_directives;\n#endif\n\n\n/* STUB NAME */\nstatic ngx_connection_t  dumb;\n/* STUB */\n\n\nngx_cycle_t *\nngx_init_cycle(ngx_cycle_t *old_cycle)\n{\n    void                *rv;\n    char               **senv;\n    ngx_uint_t           i, n;\n    ngx_log_t           *log;\n    ngx_time_t          *tp;\n    ngx_conf_t           conf;\n    ngx_pool_t          *pool;\n    ngx_cycle_t         *cycle, **old;\n    ngx_shm_zone_t      *shm_zone, *oshm_zone;\n    ngx_list_part_t     *part, *opart;\n    ngx_open_file_t     *file;\n    ngx_listening_t     *ls, *nls;\n    ngx_core_conf_t     *ccf, *old_ccf;\n    ngx_core_module_t   *module;\n    char                 hostname[NGX_MAXHOSTNAMELEN];\n\n    ngx_timezone_update();\n\n    /* force localtime update with a new timezone */\n\n    tp = ngx_timeofday();\n    tp->sec = 0;\n\n    ngx_time_update();\n\n#if (T_PIPES)\n    ngx_increase_pipe_generation();\n#endif\n\n    log = old_cycle->log;\n\n    pool = ngx_create_pool(NGX_CYCLE_POOL_SIZE, log);\n    if (pool == NULL) {\n        return NULL;\n    }\n    pool->log = log;\n\n    cycle = ngx_pcalloc(pool, sizeof(ngx_cycle_t));\n    if (cycle == NULL) {\n        ngx_destroy_pool(pool);\n        return NULL;\n    }\n\n    cycle->pool = pool;\n    cycle->log = log;\n    cycle->old_cycle = old_cycle;\n#if (NGX_SSL && NGX_SSL_ASYNC)\n    cycle->no_ssl_init = old_cycle->no_ssl_init;\n#endif\n\n    cycle->conf_prefix.len = old_cycle->conf_prefix.len;\n    cycle->conf_prefix.data = ngx_pstrdup(pool, &old_cycle->conf_prefix);\n    if (cycle->conf_prefix.data == NULL) {\n        ngx_destroy_pool(pool);\n        return NULL;\n    }\n\n    cycle->prefix.len = old_cycle->prefix.len;\n    cycle->prefix.data = ngx_pstrdup(pool, &old_cycle->prefix);\n    if (cycle->prefix.data == NULL) {\n        ngx_destroy_pool(pool);\n        return NULL;\n    }\n\n    cycle->error_log.len = old_cycle->error_log.len;\n    cycle->error_log.data = ngx_pnalloc(pool, old_cycle->error_log.len + 1);\n    if (cycle->error_log.data == NULL) {\n        ngx_destroy_pool(pool);\n        return NULL;\n    }\n    ngx_cpystrn(cycle->error_log.data, old_cycle->error_log.data,\n                old_cycle->error_log.len + 1);\n\n    cycle->conf_file.len = old_cycle->conf_file.len;\n    cycle->conf_file.data = ngx_pnalloc(pool, old_cycle->conf_file.len + 1);\n    if (cycle->conf_file.data == NULL) {\n        ngx_destroy_pool(pool);\n        return NULL;\n    }\n    ngx_cpystrn(cycle->conf_file.data, old_cycle->conf_file.data,\n                old_cycle->conf_file.len + 1);\n\n    cycle->conf_param.len = old_cycle->conf_param.len;\n    cycle->conf_param.data = ngx_pstrdup(pool, &old_cycle->conf_param);\n    if (cycle->conf_param.data == NULL) {\n        ngx_destroy_pool(pool);\n        return NULL;\n    }\n\n\n    n = old_cycle->paths.nelts ? old_cycle->paths.nelts : 10;\n\n    if (ngx_array_init(&cycle->paths, pool, n, sizeof(ngx_path_t *))\n        != NGX_OK)\n    {\n        ngx_destroy_pool(pool);\n        return NULL;\n    }\n\n    ngx_memzero(cycle->paths.elts, n * sizeof(ngx_path_t *));\n\n\n    if (ngx_array_init(&cycle->config_dump, pool, 1, sizeof(ngx_conf_dump_t))\n        != NGX_OK)\n    {\n        ngx_destroy_pool(pool);\n        return NULL;\n    }\n\n    ngx_rbtree_init(&cycle->config_dump_rbtree, &cycle->config_dump_sentinel,\n                    ngx_str_rbtree_insert_value);\n\n    if (old_cycle->open_files.part.nelts) {\n        n = old_cycle->open_files.part.nelts;\n        for (part = old_cycle->open_files.part.next; part; part = part->next) {\n            n += part->nelts;\n        }\n\n    } else {\n        n = 20;\n    }\n\n    if (ngx_list_init(&cycle->open_files, pool, n, sizeof(ngx_open_file_t))\n        != NGX_OK)\n    {\n        ngx_destroy_pool(pool);\n        return NULL;\n    }\n\n\n    if (old_cycle->shared_memory.part.nelts) {\n        n = old_cycle->shared_memory.part.nelts;\n        for (part = old_cycle->shared_memory.part.next; part; part = part->next)\n        {\n            n += part->nelts;\n        }\n\n    } else {\n        n = 1;\n    }\n\n    if (ngx_list_init(&cycle->shared_memory, pool, n, sizeof(ngx_shm_zone_t))\n        != NGX_OK)\n    {\n        ngx_destroy_pool(pool);\n        return NULL;\n    }\n\n    n = old_cycle->listening.nelts ? old_cycle->listening.nelts : 10;\n\n    if (ngx_array_init(&cycle->listening, pool, n, sizeof(ngx_listening_t))\n        != NGX_OK)\n    {\n        ngx_destroy_pool(pool);\n        return NULL;\n    }\n\n    ngx_memzero(cycle->listening.elts, n * sizeof(ngx_listening_t));\n\n\n    ngx_queue_init(&cycle->reusable_connections_queue);\n\n\n    cycle->conf_ctx = ngx_pcalloc(pool, ngx_max_module * sizeof(void *));\n    if (cycle->conf_ctx == NULL) {\n        ngx_destroy_pool(pool);\n        return NULL;\n    }\n\n\n    if (gethostname(hostname, NGX_MAXHOSTNAMELEN) == -1) {\n        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, \"gethostname() failed\");\n        ngx_destroy_pool(pool);\n        return NULL;\n    }\n\n    /* on Linux gethostname() silently truncates name that does not fit */\n\n    hostname[NGX_MAXHOSTNAMELEN - 1] = '\\0';\n    cycle->hostname.len = ngx_strlen(hostname);\n\n    cycle->hostname.data = ngx_pnalloc(pool, cycle->hostname.len);\n    if (cycle->hostname.data == NULL) {\n        ngx_destroy_pool(pool);\n        return NULL;\n    }\n\n    ngx_strlow(cycle->hostname.data, (u_char *) hostname, cycle->hostname.len);\n\n\n    if (ngx_cycle_modules(cycle) != NGX_OK) {\n        ngx_destroy_pool(pool);\n        return NULL;\n    }\n\n\n    for (i = 0; cycle->modules[i]; i++) {\n        if (cycle->modules[i]->type != NGX_CORE_MODULE) {\n            continue;\n        }\n\n        module = cycle->modules[i]->ctx;\n\n        if (module->create_conf) {\n            rv = module->create_conf(cycle);\n            if (rv == NULL) {\n                ngx_destroy_pool(pool);\n                return NULL;\n            }\n            cycle->conf_ctx[cycle->modules[i]->index] = rv;\n        }\n    }\n\n\n    senv = environ;\n\n\n    ngx_memzero(&conf, sizeof(ngx_conf_t));\n    /* STUB: init array ? */\n    conf.args = ngx_array_create(pool, 10, sizeof(ngx_str_t));\n    if (conf.args == NULL) {\n        ngx_destroy_pool(pool);\n        return NULL;\n    }\n\n    conf.temp_pool = ngx_create_pool(NGX_CYCLE_POOL_SIZE, log);\n    if (conf.temp_pool == NULL) {\n        ngx_destroy_pool(pool);\n        return NULL;\n    }\n\n\n    conf.ctx = cycle->conf_ctx;\n    conf.cycle = cycle;\n    conf.pool = pool;\n    conf.log = log;\n    conf.module_type = NGX_CORE_MODULE;\n    conf.cmd_type = NGX_MAIN_CONF;\n#if (NGX_SSL && NGX_SSL_ASYNC)\n    conf.no_ssl_init = cycle->no_ssl_init;\n#endif\n\n#if 0\n    log->log_level = NGX_LOG_DEBUG_ALL;\n#endif\n\n    if (ngx_conf_param(&conf) != NGX_CONF_OK) {\n        environ = senv;\n        ngx_destroy_cycle_pools(&conf);\n        return NULL;\n    }\n\n    if (ngx_conf_parse(&conf, &cycle->conf_file) != NGX_CONF_OK) {\n        environ = senv;\n        ngx_destroy_cycle_pools(&conf);\n        return NULL;\n    }\n\n#if (T_NGX_SHOW_INFO)\n    ngx_command_t     *cmd;\n    if (ngx_show_directives) {\n        ngx_log_stderr(0, \"all available directives:\");\n\n        for (i = 0; i < cycle->modules_n; i++) {\n            ngx_log_stderr(0, \"%s:\", cycle->modules[i]->name);\n\n            cmd = cycle->modules[i]->commands;\n            if(cmd == NULL) {\n                continue;\n            }\n\n            for ( /* void */ ; cmd->name.len; cmd++) {\n                ngx_log_stderr(0, \"    %V\", &cmd->name);\n            }\n        }\n    }\n\n    if (ngx_show_modules) {\n        ngx_log_stderr(0, \"loaded modules:\");\n\n        for (i = 0; i < cycle->modules_n; i++) {\n            if (cycle->modules[i]->index < ngx_modules_n) {\n                ngx_log_stderr(0, \"    %s (static)\", cycle->modules[i]->name);\n\n            } else {\n                ngx_log_stderr(0, \"    %s (dynamic)\", cycle->modules[i]->name);\n            }\n        }\n    }\n#endif\n\n    if (ngx_test_config && !ngx_quiet_mode) {\n        ngx_log_stderr(0, \"the configuration file %s syntax is ok\",\n                       cycle->conf_file.data);\n    }\n\n    for (i = 0; cycle->modules[i]; i++) {\n        if (cycle->modules[i]->type != NGX_CORE_MODULE) {\n            continue;\n        }\n\n        module = cycle->modules[i]->ctx;\n\n        if (module->init_conf) {\n            if (module->init_conf(cycle,\n                                  cycle->conf_ctx[cycle->modules[i]->index])\n                == NGX_CONF_ERROR)\n            {\n                environ = senv;\n                ngx_destroy_cycle_pools(&conf);\n                return NULL;\n            }\n        }\n    }\n\n    if (ngx_process == NGX_PROCESS_SIGNALLER) {\n        return cycle;\n    }\n\n    ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);\n\n    if (ngx_test_config) {\n\n        if (ngx_create_pidfile(&ccf->pid, log) != NGX_OK) {\n            goto failed;\n        }\n\n    } else if (!ngx_is_init_cycle(old_cycle)) {\n\n        /*\n         * we do not create the pid file in the first ngx_init_cycle() call\n         * because we need to write the demonized process pid\n         */\n\n        old_ccf = (ngx_core_conf_t *) ngx_get_conf(old_cycle->conf_ctx,\n                                                   ngx_core_module);\n        if (ccf->pid.len != old_ccf->pid.len\n            || ngx_strcmp(ccf->pid.data, old_ccf->pid.data) != 0)\n        {\n            /* new pid file name */\n\n            if (ngx_create_pidfile(&ccf->pid, log) != NGX_OK) {\n                goto failed;\n            }\n\n            ngx_delete_pidfile(old_cycle);\n        }\n    }\n\n\n    if (ngx_test_lockfile(cycle->lock_file.data, log) != NGX_OK) {\n        goto failed;\n    }\n\n\n    if (ngx_create_paths(cycle, ccf->user) != NGX_OK) {\n        goto failed;\n    }\n\n\n    if (ngx_log_open_default(cycle) != NGX_OK) {\n        goto failed;\n    }\n\n    /* open the new files */\n\n    part = &cycle->open_files.part;\n    file = part->elts;\n\n    for (i = 0; /* void */ ; i++) {\n\n        if (i >= part->nelts) {\n            if (part->next == NULL) {\n                break;\n            }\n            part = part->next;\n            file = part->elts;\n            i = 0;\n        }\n\n        if (file[i].name.len == 0) {\n            continue;\n        }\n\n        file[i].fd = ngx_open_file(file[i].name.data,\n                                   NGX_FILE_APPEND,\n                                   NGX_FILE_CREATE_OR_OPEN,\n                                   NGX_FILE_DEFAULT_ACCESS);\n\n        ngx_log_debug3(NGX_LOG_DEBUG_CORE, log, 0,\n                       \"log: %p %d \\\"%s\\\"\",\n                       &file[i], file[i].fd, file[i].name.data);\n\n        if (file[i].fd == NGX_INVALID_FILE) {\n            ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,\n                          ngx_open_file_n \" \\\"%s\\\" failed\",\n                          file[i].name.data);\n            goto failed;\n        }\n\n#if !(NGX_WIN32)\n        if (fcntl(file[i].fd, F_SETFD, FD_CLOEXEC) == -1) {\n            ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,\n                          \"fcntl(FD_CLOEXEC) \\\"%s\\\" failed\",\n                          file[i].name.data);\n            goto failed;\n        }\n#endif\n    }\n\n#if (T_NGX_XQUIC)\n    cycle->x_log = &cycle->xquic_log;\n#endif\n    cycle->log = &cycle->new_log;\n    pool->log = &cycle->new_log;\n\n\n    /* create shared memory */\n\n    part = &cycle->shared_memory.part;\n    shm_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            part = part->next;\n            shm_zone = part->elts;\n            i = 0;\n        }\n\n        if (shm_zone[i].shm.size == 0) {\n            ngx_log_error(NGX_LOG_EMERG, log, 0,\n                          \"zero size shared memory zone \\\"%V\\\"\",\n                          &shm_zone[i].shm.name);\n            goto failed;\n        }\n\n        shm_zone[i].shm.log = cycle->log;\n\n        opart = &old_cycle->shared_memory.part;\n        oshm_zone = opart->elts;\n\n        for (n = 0; /* void */ ; n++) {\n\n            if (n >= opart->nelts) {\n                if (opart->next == NULL) {\n                    break;\n                }\n                opart = opart->next;\n                oshm_zone = opart->elts;\n                n = 0;\n            }\n\n            if (shm_zone[i].shm.name.len != oshm_zone[n].shm.name.len) {\n                continue;\n            }\n\n            if (ngx_strncmp(shm_zone[i].shm.name.data,\n                            oshm_zone[n].shm.name.data,\n                            shm_zone[i].shm.name.len)\n                != 0)\n            {\n                continue;\n            }\n\n            if (shm_zone[i].tag == oshm_zone[n].tag\n                && shm_zone[i].shm.size == oshm_zone[n].shm.size\n                && !shm_zone[i].noreuse)\n            {\n                shm_zone[i].shm.addr = oshm_zone[n].shm.addr;\n#if (NGX_WIN32)\n                shm_zone[i].shm.handle = oshm_zone[n].shm.handle;\n#endif\n\n                if (shm_zone[i].init(&shm_zone[i], oshm_zone[n].data)\n                    != NGX_OK)\n                {\n                    goto failed;\n                }\n\n                goto shm_zone_found;\n            }\n\n            break;\n        }\n\n        if (ngx_shm_alloc(&shm_zone[i].shm) != NGX_OK) {\n            goto failed;\n        }\n\n        if (ngx_init_zone_pool(cycle, &shm_zone[i]) != NGX_OK) {\n            goto failed;\n        }\n\n        if (shm_zone[i].init(&shm_zone[i], NULL) != NGX_OK) {\n            goto failed;\n        }\n\n    shm_zone_found:\n\n        continue;\n    }\n\n\n    /* handle the listening sockets */\n\n    if (old_cycle->listening.nelts) {\n        ls = old_cycle->listening.elts;\n        for (i = 0; i < old_cycle->listening.nelts; i++) {\n            ls[i].remain = 0;\n        }\n\n        nls = cycle->listening.elts;\n        for (n = 0; n < cycle->listening.nelts; n++) {\n\n            for (i = 0; i < old_cycle->listening.nelts; i++) {\n                if (ls[i].ignore) {\n                    continue;\n                }\n\n                if (ls[i].remain) {\n                    continue;\n                }\n\n                if (ls[i].type != nls[n].type) {\n                    continue;\n                }\n\n                if (ngx_cmp_sockaddr(nls[n].sockaddr, nls[n].socklen,\n                                     ls[i].sockaddr, ls[i].socklen, 1)\n                    == NGX_OK)\n                {\n                    nls[n].fd = ls[i].fd;\n                    nls[n].inherited = ls[i].inherited;\n                    nls[n].previous = &ls[i];\n                    ls[i].remain = 1;\n\n                    if (ls[i].backlog != nls[n].backlog) {\n                        nls[n].listen = 1;\n                    }\n\n#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)\n\n                    /*\n                     * FreeBSD, except the most recent versions,\n                     * could not remove accept filter\n                     */\n                    nls[n].deferred_accept = ls[i].deferred_accept;\n\n                    if (ls[i].accept_filter && nls[n].accept_filter) {\n                        if (ngx_strcmp(ls[i].accept_filter,\n                                       nls[n].accept_filter)\n                            != 0)\n                        {\n                            nls[n].delete_deferred = 1;\n                            nls[n].add_deferred = 1;\n                        }\n\n                    } else if (ls[i].accept_filter) {\n                        nls[n].delete_deferred = 1;\n\n                    } else if (nls[n].accept_filter) {\n                        nls[n].add_deferred = 1;\n                    }\n#endif\n\n#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT)\n\n                    if (ls[i].deferred_accept && !nls[n].deferred_accept) {\n                        nls[n].delete_deferred = 1;\n\n                    } else if (ls[i].deferred_accept != nls[n].deferred_accept)\n                    {\n                        nls[n].add_deferred = 1;\n                    }\n#endif\n\n#if (NGX_HAVE_REUSEPORT)\n                    if (nls[n].reuseport && !ls[i].reuseport) {\n                        nls[n].add_reuseport = 1;\n                    }\n#endif\n\n                    break;\n                }\n            }\n\n            if (nls[n].fd == (ngx_socket_t) -1) {\n                nls[n].open = 1;\n#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)\n                if (nls[n].accept_filter) {\n                    nls[n].add_deferred = 1;\n                }\n#endif\n#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT)\n                if (nls[n].deferred_accept) {\n                    nls[n].add_deferred = 1;\n                }\n#endif\n            }\n        }\n\n    } else {\n        ls = cycle->listening.elts;\n        for (i = 0; i < cycle->listening.nelts; i++) {\n            ls[i].open = 1;\n#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)\n            if (ls[i].accept_filter) {\n                ls[i].add_deferred = 1;\n            }\n#endif\n#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT)\n            if (ls[i].deferred_accept) {\n                ls[i].add_deferred = 1;\n            }\n#endif\n        }\n    }\n\n    if (ngx_open_listening_sockets(cycle) != NGX_OK) {\n        goto failed;\n    }\n\n    if (!ngx_test_config) {\n        ngx_configure_listening_sockets(cycle);\n    }\n\n\n    /* commit the new cycle configuration */\n\n    if (!ngx_use_stderr) {\n        (void) ngx_log_redirect_stderr(cycle);\n    }\n\n    pool->log = cycle->log;\n\n    if (ngx_init_modules(cycle) != NGX_OK) {\n        /* fatal */\n        exit(1);\n    }\n\n\n    /* close and delete stuff that lefts from an old cycle */\n\n    /* free the unnecessary shared memory */\n\n    opart = &old_cycle->shared_memory.part;\n    oshm_zone = opart->elts;\n\n    for (i = 0; /* void */ ; i++) {\n\n        if (i >= opart->nelts) {\n            if (opart->next == NULL) {\n                goto old_shm_zone_done;\n            }\n            opart = opart->next;\n            oshm_zone = opart->elts;\n            i = 0;\n        }\n\n        part = &cycle->shared_memory.part;\n        shm_zone = part->elts;\n\n        for (n = 0; /* void */ ; n++) {\n\n            if (n >= part->nelts) {\n                if (part->next == NULL) {\n                    break;\n                }\n                part = part->next;\n                shm_zone = part->elts;\n                n = 0;\n            }\n\n            if (oshm_zone[i].shm.name.len != shm_zone[n].shm.name.len) {\n                continue;\n            }\n\n            if (ngx_strncmp(oshm_zone[i].shm.name.data,\n                            shm_zone[n].shm.name.data,\n                            oshm_zone[i].shm.name.len)\n                != 0)\n            {\n                continue;\n            }\n\n            if (oshm_zone[i].tag == shm_zone[n].tag\n                && oshm_zone[i].shm.size == shm_zone[n].shm.size\n                && !oshm_zone[i].noreuse)\n            {\n                goto live_shm_zone;\n            }\n\n            break;\n        }\n\n        ngx_shm_free(&oshm_zone[i].shm);\n\n    live_shm_zone:\n\n        continue;\n    }\n\nold_shm_zone_done:\n\n\n    /* close the unnecessary listening sockets */\n\n    ls = old_cycle->listening.elts;\n    for (i = 0; i < old_cycle->listening.nelts; i++) {\n\n        if (ls[i].remain || ls[i].fd == (ngx_socket_t) -1) {\n            continue;\n        }\n\n        if (ngx_close_socket(ls[i].fd) == -1) {\n            ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,\n                          ngx_close_socket_n \" listening socket on %V failed\",\n                          &ls[i].addr_text);\n        }\n\n#if (NGX_HAVE_UNIX_DOMAIN)\n\n        if (ls[i].sockaddr->sa_family == AF_UNIX) {\n            u_char  *name;\n\n            name = ls[i].addr_text.data + sizeof(\"unix:\") - 1;\n\n            ngx_log_error(NGX_LOG_WARN, cycle->log, 0,\n                          \"deleting socket %s\", name);\n\n            if (ngx_delete_file(name) == NGX_FILE_ERROR) {\n                ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_socket_errno,\n                              ngx_delete_file_n \" %s failed\", name);\n            }\n        }\n\n#endif\n    }\n\n#if (T_PIPES)\n    /* open pipes */\n\n    if (!ngx_is_init_cycle(old_cycle)) {\n        if (ngx_open_pipes(cycle) == NGX_ERROR) {\n            ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, \"can not open pipes\");\n            goto failed;\n        }\n    }\n#endif\n\n    /* close the unnecessary open files */\n\n    part = &old_cycle->open_files.part;\n    file = part->elts;\n\n    for (i = 0; /* void */ ; i++) {\n\n        if (i >= part->nelts) {\n            if (part->next == NULL) {\n                break;\n            }\n            part = part->next;\n            file = part->elts;\n            i = 0;\n        }\n\n        if (file[i].fd == NGX_INVALID_FILE || file[i].fd == ngx_stderr) {\n            continue;\n        }\n\n        if (ngx_close_file(file[i].fd) == NGX_FILE_ERROR) {\n            ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,\n                          ngx_close_file_n \" \\\"%s\\\" failed\",\n                          file[i].name.data);\n        }\n    }\n\n    ngx_destroy_pool(conf.temp_pool);\n\n    if (ngx_process == NGX_PROCESS_MASTER || ngx_is_init_cycle(old_cycle)) {\n\n        ngx_destroy_pool(old_cycle->pool);\n        cycle->old_cycle = NULL;\n\n        return cycle;\n    }\n\n\n    if (ngx_temp_pool == NULL) {\n        ngx_temp_pool = ngx_create_pool(128, cycle->log);\n        if (ngx_temp_pool == NULL) {\n            ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,\n                          \"could not create ngx_temp_pool\");\n            exit(1);\n        }\n\n        n = 10;\n\n        if (ngx_array_init(&ngx_old_cycles, ngx_temp_pool, n,\n                           sizeof(ngx_cycle_t *))\n            != NGX_OK)\n        {\n            exit(1);\n        }\n\n        ngx_memzero(ngx_old_cycles.elts, n * sizeof(ngx_cycle_t *));\n\n        ngx_cleaner_event.handler = ngx_clean_old_cycles;\n        ngx_cleaner_event.log = cycle->log;\n        ngx_cleaner_event.data = &dumb;\n        dumb.fd = (ngx_socket_t) -1;\n    }\n\n    ngx_temp_pool->log = cycle->log;\n\n    old = ngx_array_push(&ngx_old_cycles);\n    if (old == NULL) {\n        exit(1);\n    }\n    *old = old_cycle;\n\n    if (!ngx_cleaner_event.timer_set) {\n        ngx_add_timer(&ngx_cleaner_event, 30000);\n        ngx_cleaner_event.timer_set = 1;\n    }\n\n    return cycle;\n\n\nfailed:\n\n    if (!ngx_is_init_cycle(old_cycle)) {\n        old_ccf = (ngx_core_conf_t *) ngx_get_conf(old_cycle->conf_ctx,\n                                                   ngx_core_module);\n        if (old_ccf->environment) {\n            environ = old_ccf->environment;\n        }\n    }\n\n    /* rollback the new cycle configuration */\n\n    part = &cycle->open_files.part;\n    file = part->elts;\n\n    for (i = 0; /* void */ ; i++) {\n\n        if (i >= part->nelts) {\n            if (part->next == NULL) {\n                break;\n            }\n            part = part->next;\n            file = part->elts;\n            i = 0;\n        }\n\n        if (file[i].fd == NGX_INVALID_FILE || file[i].fd == ngx_stderr) {\n            continue;\n        }\n\n        if (ngx_close_file(file[i].fd) == NGX_FILE_ERROR) {\n            ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,\n                          ngx_close_file_n \" \\\"%s\\\" failed\",\n                          file[i].name.data);\n        }\n    }\n\n#if (T_PIPES)\n    ngx_close_pipes();\n#endif\n\n    /* free the newly created shared memory */\n\n    part = &cycle->shared_memory.part;\n    shm_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            part = part->next;\n            shm_zone = part->elts;\n            i = 0;\n        }\n\n        if (shm_zone[i].shm.addr == NULL) {\n            continue;\n        }\n\n        opart = &old_cycle->shared_memory.part;\n        oshm_zone = opart->elts;\n\n        for (n = 0; /* void */ ; n++) {\n\n            if (n >= opart->nelts) {\n                if (opart->next == NULL) {\n                    break;\n                }\n                opart = opart->next;\n                oshm_zone = opart->elts;\n                n = 0;\n            }\n\n            if (shm_zone[i].shm.name.len != oshm_zone[n].shm.name.len) {\n                continue;\n            }\n\n            if (ngx_strncmp(shm_zone[i].shm.name.data,\n                            oshm_zone[n].shm.name.data,\n                            shm_zone[i].shm.name.len)\n                != 0)\n            {\n                continue;\n            }\n\n            if (shm_zone[i].tag == oshm_zone[n].tag\n                && shm_zone[i].shm.size == oshm_zone[n].shm.size\n                && !shm_zone[i].noreuse)\n            {\n                goto old_shm_zone_found;\n            }\n\n            break;\n        }\n\n        ngx_shm_free(&shm_zone[i].shm);\n\n    old_shm_zone_found:\n\n        continue;\n    }\n\n    if (ngx_test_config) {\n        ngx_destroy_cycle_pools(&conf);\n        return NULL;\n    }\n\n    ls = cycle->listening.elts;\n    for (i = 0; i < cycle->listening.nelts; i++) {\n        if (ls[i].fd == (ngx_socket_t) -1 || !ls[i].open) {\n            continue;\n        }\n\n        if (ngx_close_socket(ls[i].fd) == -1) {\n            ngx_log_error(NGX_LOG_EMERG, log, ngx_socket_errno,\n                          ngx_close_socket_n \" %V failed\",\n                          &ls[i].addr_text);\n        }\n    }\n\n    ngx_destroy_cycle_pools(&conf);\n\n    return NULL;\n}\n\n\nstatic void\nngx_destroy_cycle_pools(ngx_conf_t *conf)\n{\n    ngx_destroy_pool(conf->temp_pool);\n    ngx_destroy_pool(conf->pool);\n}\n\n\nstatic ngx_int_t\nngx_init_zone_pool(ngx_cycle_t *cycle, ngx_shm_zone_t *zn)\n{\n    u_char           *file;\n    ngx_slab_pool_t  *sp;\n\n    sp = (ngx_slab_pool_t *) zn->shm.addr;\n\n    if (zn->shm.exists) {\n\n        if (sp == sp->addr) {\n            return NGX_OK;\n        }\n\n#if (NGX_WIN32)\n\n        /* remap at the required address */\n\n        if (ngx_shm_remap(&zn->shm, sp->addr) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        sp = (ngx_slab_pool_t *) zn->shm.addr;\n\n        if (sp == sp->addr) {\n            return NGX_OK;\n        }\n\n#endif\n\n        ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,\n                      \"shared zone \\\"%V\\\" has no equal addresses: %p vs %p\",\n                      &zn->shm.name, sp->addr, sp);\n        return NGX_ERROR;\n    }\n\n    sp->end = zn->shm.addr + zn->shm.size;\n    sp->min_shift = 3;\n    sp->addr = zn->shm.addr;\n\n#if (NGX_HAVE_ATOMIC_OPS)\n\n    file = NULL;\n\n#else\n\n    file = ngx_pnalloc(cycle->pool,\n                       cycle->lock_file.len + zn->shm.name.len + 1);\n    if (file == NULL) {\n        return NGX_ERROR;\n    }\n\n    (void) ngx_sprintf(file, \"%V%V%Z\", &cycle->lock_file, &zn->shm.name);\n\n#endif\n\n    if (ngx_shmtx_create(&sp->mutex, &sp->lock, file) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    ngx_slab_init(sp);\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_create_pidfile(ngx_str_t *name, ngx_log_t *log)\n{\n    size_t      len;\n    ngx_int_t   rc;\n    ngx_uint_t  create;\n    ngx_file_t  file;\n    u_char      pid[NGX_INT64_LEN + 2];\n\n    if (ngx_process > NGX_PROCESS_MASTER) {\n        return NGX_OK;\n    }\n\n    ngx_memzero(&file, sizeof(ngx_file_t));\n\n    file.name = *name;\n    file.log = log;\n\n    create = ngx_test_config ? NGX_FILE_CREATE_OR_OPEN : NGX_FILE_TRUNCATE;\n\n    file.fd = ngx_open_file(file.name.data, NGX_FILE_RDWR,\n                            create, NGX_FILE_DEFAULT_ACCESS);\n\n    if (file.fd == NGX_INVALID_FILE) {\n        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,\n                      ngx_open_file_n \" \\\"%s\\\" failed\", file.name.data);\n        return NGX_ERROR;\n    }\n\n    rc = NGX_OK;\n\n    if (!ngx_test_config) {\n        len = ngx_snprintf(pid, NGX_INT64_LEN + 2, \"%P%N\", ngx_pid) - pid;\n\n        if (ngx_write_file(&file, pid, len, 0) == NGX_ERROR) {\n            rc = NGX_ERROR;\n        }\n    }\n\n    if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {\n        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,\n                      ngx_close_file_n \" \\\"%s\\\" failed\", file.name.data);\n    }\n\n    return rc;\n}\n\n\nvoid\nngx_delete_pidfile(ngx_cycle_t *cycle)\n{\n    u_char           *name;\n    ngx_core_conf_t  *ccf;\n\n    ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);\n\n    name = ngx_new_binary ? ccf->oldpid.data : ccf->pid.data;\n\n    if (ngx_delete_file(name) == NGX_FILE_ERROR) {\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                      ngx_delete_file_n \" \\\"%s\\\" failed\", name);\n    }\n}\n\n\nngx_int_t\nngx_signal_process(ngx_cycle_t *cycle, char *sig)\n{\n    ssize_t           n;\n    ngx_pid_t         pid;\n    ngx_file_t        file;\n    ngx_core_conf_t  *ccf;\n    u_char            buf[NGX_INT64_LEN + 2];\n\n    ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, \"signal process started\");\n\n    ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);\n\n    ngx_memzero(&file, sizeof(ngx_file_t));\n\n    file.name = ccf->pid;\n    file.log = cycle->log;\n\n    file.fd = ngx_open_file(file.name.data, NGX_FILE_RDONLY,\n                            NGX_FILE_OPEN, NGX_FILE_DEFAULT_ACCESS);\n\n    if (file.fd == NGX_INVALID_FILE) {\n        ngx_log_error(NGX_LOG_ERR, cycle->log, ngx_errno,\n                      ngx_open_file_n \" \\\"%s\\\" failed\", file.name.data);\n        return 1;\n    }\n\n    n = ngx_read_file(&file, buf, NGX_INT64_LEN + 2, 0);\n\n    if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                      ngx_close_file_n \" \\\"%s\\\" failed\", file.name.data);\n    }\n\n    if (n == NGX_ERROR) {\n        return 1;\n    }\n\n    while (n-- && (buf[n] == CR || buf[n] == LF)) { /* void */ }\n\n    pid = ngx_atoi(buf, ++n);\n\n    if (pid == (ngx_pid_t) NGX_ERROR) {\n        ngx_log_error(NGX_LOG_ERR, cycle->log, 0,\n                      \"invalid PID number \\\"%*s\\\" in \\\"%s\\\"\",\n                      n, buf, file.name.data);\n        return 1;\n    }\n\n    return ngx_os_signal_process(cycle, sig, pid);\n\n}\n\n\nstatic ngx_int_t\nngx_test_lockfile(u_char *file, ngx_log_t *log)\n{\n#if !(NGX_HAVE_ATOMIC_OPS)\n    ngx_fd_t  fd;\n\n    fd = ngx_open_file(file, NGX_FILE_RDWR, NGX_FILE_CREATE_OR_OPEN,\n                       NGX_FILE_DEFAULT_ACCESS);\n\n    if (fd == NGX_INVALID_FILE) {\n        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,\n                      ngx_open_file_n \" \\\"%s\\\" failed\", file);\n        return NGX_ERROR;\n    }\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\", file);\n    }\n\n    if (ngx_delete_file(file) == NGX_FILE_ERROR) {\n        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,\n                      ngx_delete_file_n \" \\\"%s\\\" failed\", file);\n    }\n\n#endif\n\n    return NGX_OK;\n}\n\n\nvoid\nngx_reopen_files(ngx_cycle_t *cycle, ngx_uid_t user)\n{\n    ngx_fd_t          fd;\n    ngx_uint_t        i;\n    ngx_list_part_t  *part;\n    ngx_open_file_t  *file;\n\n    part = &cycle->open_files.part;\n    file = part->elts;\n\n    for (i = 0; /* void */ ; i++) {\n\n        if (i >= part->nelts) {\n            if (part->next == NULL) {\n                break;\n            }\n            part = part->next;\n            file = part->elts;\n            i = 0;\n        }\n\n        if (file[i].name.len == 0) {\n            continue;\n        }\n\n        if (file[i].flush) {\n            file[i].flush(&file[i], cycle->log);\n        }\n\n        fd = ngx_open_file(file[i].name.data, NGX_FILE_APPEND,\n                           NGX_FILE_CREATE_OR_OPEN, NGX_FILE_DEFAULT_ACCESS);\n\n        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                       \"reopen file \\\"%s\\\", old:%d new:%d\",\n                       file[i].name.data, file[i].fd, fd);\n\n        if (fd == NGX_INVALID_FILE) {\n            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                          ngx_open_file_n \" \\\"%s\\\" failed\", file[i].name.data);\n            continue;\n        }\n\n#if !(NGX_WIN32)\n        if (user != (ngx_uid_t) NGX_CONF_UNSET_UINT) {\n            ngx_file_info_t  fi;\n\n            if (ngx_file_info(file[i].name.data, &fi) == NGX_FILE_ERROR) {\n                ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                              ngx_file_info_n \" \\\"%s\\\" failed\",\n                              file[i].name.data);\n\n                if (ngx_close_file(fd) == NGX_FILE_ERROR) {\n                    ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                                  ngx_close_file_n \" \\\"%s\\\" failed\",\n                                  file[i].name.data);\n                }\n\n                continue;\n            }\n\n            if (fi.st_uid != user) {\n                if (chown((const char *) file[i].name.data, user, -1) == -1) {\n                    ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                                  \"chown(\\\"%s\\\", %d) failed\",\n                                  file[i].name.data, user);\n\n                    if (ngx_close_file(fd) == NGX_FILE_ERROR) {\n                        ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                                      ngx_close_file_n \" \\\"%s\\\" failed\",\n                                      file[i].name.data);\n                    }\n\n                    continue;\n                }\n            }\n\n            if ((fi.st_mode & (S_IRUSR|S_IWUSR)) != (S_IRUSR|S_IWUSR)) {\n\n                fi.st_mode |= (S_IRUSR|S_IWUSR);\n\n                if (chmod((const char *) file[i].name.data, fi.st_mode) == -1) {\n                    ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                                  \"chmod() \\\"%s\\\" failed\", file[i].name.data);\n\n                    if (ngx_close_file(fd) == NGX_FILE_ERROR) {\n                        ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                                      ngx_close_file_n \" \\\"%s\\\" failed\",\n                                      file[i].name.data);\n                    }\n\n                    continue;\n                }\n            }\n        }\n\n        if (fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) {\n            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                          \"fcntl(FD_CLOEXEC) \\\"%s\\\" failed\",\n                          file[i].name.data);\n\n            if (ngx_close_file(fd) == NGX_FILE_ERROR) {\n                ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                              ngx_close_file_n \" \\\"%s\\\" failed\",\n                              file[i].name.data);\n            }\n\n            continue;\n        }\n#endif\n\n        if (ngx_close_file(file[i].fd) == NGX_FILE_ERROR) {\n            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                          ngx_close_file_n \" \\\"%s\\\" failed\",\n                          file[i].name.data);\n        }\n\n        file[i].fd = fd;\n    }\n\n    (void) ngx_log_redirect_stderr(cycle);\n}\n\n\nngx_shm_zone_t *\nngx_shared_memory_add(ngx_conf_t *cf, ngx_str_t *name, size_t size, void *tag)\n{\n    ngx_uint_t        i;\n    ngx_shm_zone_t   *shm_zone;\n    ngx_list_part_t  *part;\n\n    part = &cf->cycle->shared_memory.part;\n    shm_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            part = part->next;\n            shm_zone = part->elts;\n            i = 0;\n        }\n\n        if (name->len != shm_zone[i].shm.name.len) {\n            continue;\n        }\n\n        if (ngx_strncmp(name->data, shm_zone[i].shm.name.data, name->len)\n            != 0)\n        {\n            continue;\n        }\n\n        if (tag != shm_zone[i].tag) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                            \"the shared memory zone \\\"%V\\\" is \"\n                            \"already declared for a different use\",\n                            &shm_zone[i].shm.name);\n            return NULL;\n        }\n\n        if (shm_zone[i].shm.size == 0) {\n            shm_zone[i].shm.size = size;\n        }\n\n        if (size && size != shm_zone[i].shm.size) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                            \"the size %uz of shared memory zone \\\"%V\\\" \"\n                            \"conflicts with already declared size %uz\",\n                            size, &shm_zone[i].shm.name, shm_zone[i].shm.size);\n            return NULL;\n        }\n\n        return &shm_zone[i];\n    }\n\n    shm_zone = ngx_list_push(&cf->cycle->shared_memory);\n\n    if (shm_zone == NULL) {\n        return NULL;\n    }\n\n    shm_zone->data = NULL;\n    shm_zone->shm.log = cf->cycle->log;\n    shm_zone->shm.addr = NULL;\n    shm_zone->shm.size = size;\n    shm_zone->shm.name = *name;\n    shm_zone->shm.exists = 0;\n    shm_zone->init = NULL;\n    shm_zone->tag = tag;\n    shm_zone->noreuse = 0;\n\n    return shm_zone;\n}\n\n\nstatic void\nngx_clean_old_cycles(ngx_event_t *ev)\n{\n    ngx_uint_t     i, n, found, live;\n    ngx_log_t     *log;\n    ngx_cycle_t  **cycle;\n\n    log = ngx_cycle->log;\n    ngx_temp_pool->log = log;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_CORE, log, 0, \"clean old cycles\");\n\n    live = 0;\n\n    cycle = ngx_old_cycles.elts;\n    for (i = 0; i < ngx_old_cycles.nelts; i++) {\n\n        if (cycle[i] == NULL) {\n            continue;\n        }\n\n        found = 0;\n\n        for (n = 0; n < cycle[i]->connection_n; n++) {\n            if (cycle[i]->connections[n].fd != (ngx_socket_t) -1) {\n                found = 1;\n\n                ngx_log_debug1(NGX_LOG_DEBUG_CORE, log, 0, \"live fd:%ui\", n);\n\n                break;\n            }\n        }\n\n        if (found) {\n            live = 1;\n            continue;\n        }\n\n        ngx_log_debug1(NGX_LOG_DEBUG_CORE, log, 0, \"clean old cycle: %ui\", i);\n\n        ngx_destroy_pool(cycle[i]->pool);\n        cycle[i] = NULL;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_CORE, log, 0, \"old cycles status: %ui\", live);\n\n    if (live) {\n        ngx_add_timer(ev, 30000);\n\n    } else {\n        ngx_destroy_pool(ngx_temp_pool);\n        ngx_temp_pool = NULL;\n        ngx_old_cycles.nelts = 0;\n    }\n}\n\n\nvoid\nngx_set_shutdown_timer(ngx_cycle_t *cycle)\n{\n    ngx_core_conf_t  *ccf;\n\n    ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);\n\n    if (ccf->shutdown_timeout) {\n        ngx_shutdown_event.handler = ngx_shutdown_timer_handler;\n        ngx_shutdown_event.data = cycle;\n        ngx_shutdown_event.log = cycle->log;\n        ngx_shutdown_event.cancelable = 1;\n\n        ngx_add_timer(&ngx_shutdown_event, ccf->shutdown_timeout);\n    }\n}\n\n\nstatic void\nngx_shutdown_timer_handler(ngx_event_t *ev)\n{\n    ngx_uint_t         i;\n    ngx_cycle_t       *cycle;\n    ngx_connection_t  *c;\n\n    cycle = ev->data;\n\n    c = cycle->connections;\n\n    for (i = 0; i < cycle->connection_n; i++) {\n\n        if (c[i].fd == (ngx_socket_t) -1\n            || c[i].read == NULL\n            || c[i].read->accept\n            || c[i].read->channel\n            || c[i].read->resolver)\n        {\n            continue;\n        }\n\n        ngx_log_debug1(NGX_LOG_DEBUG_CORE, ev->log, 0,\n                       \"*%uA shutdown timeout\", c[i].number);\n\n        c[i].close = 1;\n        c[i].error = 1;\n\n        c[i].read->handler(c[i].read);\n    }\n}\n"
  },
  {
    "path": "src/core/ngx_cycle.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_CYCLE_H_INCLUDED_\n#define _NGX_CYCLE_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\n#ifndef NGX_CYCLE_POOL_SIZE\n#define NGX_CYCLE_POOL_SIZE     NGX_DEFAULT_POOL_SIZE\n#endif\n\n\n#define NGX_DEBUG_POINTS_STOP   1\n#define NGX_DEBUG_POINTS_ABORT  2\n\n\ntypedef struct ngx_shm_zone_s  ngx_shm_zone_t;\n\ntypedef ngx_int_t (*ngx_shm_zone_init_pt) (ngx_shm_zone_t *zone, void *data);\n\nstruct ngx_shm_zone_s {\n    void                     *data;\n    ngx_shm_t                 shm;\n    ngx_shm_zone_init_pt      init;\n    void                     *tag;\n    void                     *sync;\n    ngx_uint_t                noreuse;  /* unsigned  noreuse:1; */\n};\n\n\nstruct ngx_cycle_s {\n    void                  ****conf_ctx;\n    ngx_pool_t               *pool;\n\n    ngx_log_t                *log;\n    ngx_log_t                 new_log;\n#if (T_NGX_XQUIC)\n    ngx_log_t                *x_log;\n    ngx_log_t                 xquic_log;\n#endif\n\n#if (T_NGX_HAVE_XUDP)\n    ngx_xudp_cycle_ctx_t     *xudp_ctx ;\n#endif\n\n    ngx_uint_t                log_use_stderr;  /* unsigned  log_use_stderr:1; */\n\n    ngx_connection_t        **files;\n    ngx_connection_t         *free_connections;\n    ngx_uint_t                free_connection_n;\n\n    ngx_module_t            **modules;\n    ngx_uint_t                modules_n;\n    ngx_uint_t                modules_used;    /* unsigned  modules_used:1; */\n\n    ngx_queue_t               reusable_connections_queue;\n    ngx_uint_t                reusable_connections_n;\n    time_t                    connections_reuse_time;\n\n    ngx_array_t               listening;\n    ngx_array_t               paths;\n\n    ngx_array_t               config_dump;\n    ngx_rbtree_t              config_dump_rbtree;\n    ngx_rbtree_node_t         config_dump_sentinel;\n\n    ngx_list_t                open_files;\n    ngx_list_t                shared_memory;\n\n    ngx_uint_t                connection_n;\n    ngx_uint_t                files_n;\n\n    ngx_connection_t         *connections;\n    ngx_event_t              *read_events;\n    ngx_event_t              *write_events;\n#if (NGX_SSL && NGX_SSL_ASYNC)\n    ngx_event_t              *async_events;\n#endif\n\n    ngx_cycle_t              *old_cycle;\n\n    ngx_str_t                 conf_file;\n    ngx_str_t                 conf_param;\n    ngx_str_t                 conf_prefix;\n    ngx_str_t                 prefix;\n    ngx_str_t                 error_log;\n    ngx_str_t                 lock_file;\n    ngx_str_t                 hostname;\n#if (NGX_SSL && NGX_SSL_ASYNC)\n    ngx_flag_t                no_ssl_init;\n#endif\n};\n\n\ntypedef struct {\n    ngx_flag_t                daemon;\n    ngx_flag_t                master;\n\n    ngx_msec_t                timer_resolution;\n    ngx_msec_t                shutdown_timeout;\n\n    ngx_int_t                 worker_processes;\n    ngx_int_t                 debug_points;\n\n    ngx_int_t                 rlimit_nofile;\n    off_t                     rlimit_core;\n\n    int                       priority;\n\n    ngx_uint_t                cpu_affinity_auto;\n    ngx_uint_t                cpu_affinity_n;\n    ngx_cpuset_t             *cpu_affinity;\n\n    char                     *username;\n    ngx_uid_t                 user;\n    ngx_gid_t                 group;\n\n    ngx_str_t                 working_directory;\n    ngx_str_t                 lock_file;\n\n    ngx_str_t                 pid;\n    ngx_str_t                 oldpid;\n\n    ngx_array_t               env;\n    char                    **environment;\n\n    ngx_uint_t                transparent;  /* unsigned  transparent:1; */\n\n#if (T_PIPE_SET_SIZE)\n    size_t pipe_size;\n#endif\n\n} ngx_core_conf_t;\n\n\n#define ngx_is_init_cycle(cycle)  (cycle->conf_ctx == NULL)\n\n\nngx_cycle_t *ngx_init_cycle(ngx_cycle_t *old_cycle);\nngx_int_t ngx_create_pidfile(ngx_str_t *name, ngx_log_t *log);\nvoid ngx_delete_pidfile(ngx_cycle_t *cycle);\nngx_int_t ngx_signal_process(ngx_cycle_t *cycle, char *sig);\nvoid ngx_reopen_files(ngx_cycle_t *cycle, ngx_uid_t user);\nchar **ngx_set_environment(ngx_cycle_t *cycle, ngx_uint_t *last);\nngx_pid_t ngx_exec_new_binary(ngx_cycle_t *cycle, char *const *argv);\nngx_cpuset_t *ngx_get_cpu_affinity(ngx_uint_t n);\nngx_shm_zone_t *ngx_shared_memory_add(ngx_conf_t *cf, ngx_str_t *name,\n    size_t size, void *tag);\nvoid ngx_set_shutdown_timer(ngx_cycle_t *cycle);\n\n\nextern volatile ngx_cycle_t  *ngx_cycle;\nextern ngx_array_t            ngx_old_cycles;\nextern ngx_module_t           ngx_core_module;\nextern ngx_uint_t             ngx_test_config;\nextern ngx_uint_t             ngx_dump_config;\nextern ngx_uint_t             ngx_quiet_mode;\n#if (T_NGX_SHOW_INFO)\nextern ngx_uint_t             ngx_show_modules;\nextern ngx_uint_t             ngx_show_directives;\n#endif\n\n\n#endif /* _NGX_CYCLE_H_INCLUDED_ */\n"
  },
  {
    "path": "src/core/ngx_file.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\nstatic ngx_int_t ngx_test_full_name(ngx_str_t *name);\n\n\nstatic ngx_atomic_t   temp_number = 0;\nngx_atomic_t         *ngx_temp_number = &temp_number;\nngx_atomic_int_t      ngx_random_number = 123456;\n\n\nngx_int_t\nngx_get_full_name(ngx_pool_t *pool, ngx_str_t *prefix, ngx_str_t *name)\n{\n    size_t      len;\n    u_char     *p, *n;\n    ngx_int_t   rc;\n\n    rc = ngx_test_full_name(name);\n\n    if (rc == NGX_OK) {\n        return rc;\n    }\n\n    len = prefix->len;\n\n#if (NGX_WIN32)\n\n    if (rc == 2) {\n        len = rc;\n    }\n\n#endif\n\n    n = ngx_pnalloc(pool, len + name->len + 1);\n    if (n == NULL) {\n        return NGX_ERROR;\n    }\n\n    p = ngx_cpymem(n, prefix->data, len);\n    ngx_cpystrn(p, name->data, name->len + 1);\n\n    name->len += len;\n    name->data = n;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_test_full_name(ngx_str_t *name)\n{\n#if (NGX_WIN32)\n    u_char  c0, c1;\n\n    c0 = name->data[0];\n\n    if (name->len < 2) {\n        if (c0 == '/') {\n            return 2;\n        }\n\n        return NGX_DECLINED;\n    }\n\n    c1 = name->data[1];\n\n    if (c1 == ':') {\n        c0 |= 0x20;\n\n        if ((c0 >= 'a' && c0 <= 'z')) {\n            return NGX_OK;\n        }\n\n        return NGX_DECLINED;\n    }\n\n    if (c1 == '/') {\n        return NGX_OK;\n    }\n\n    if (c0 == '/') {\n        return 2;\n    }\n\n    return NGX_DECLINED;\n\n#else\n\n    if (name->data[0] == '/') {\n        return NGX_OK;\n    }\n\n    return NGX_DECLINED;\n\n#endif\n}\n\n\nssize_t\nngx_write_chain_to_temp_file(ngx_temp_file_t *tf, ngx_chain_t *chain)\n{\n    ngx_int_t  rc;\n\n    if (tf->file.fd == NGX_INVALID_FILE) {\n        rc = ngx_create_temp_file(&tf->file, tf->path, tf->pool,\n                                  tf->persistent, tf->clean, tf->access);\n\n        if (rc != NGX_OK) {\n            return rc;\n        }\n\n        if (tf->log_level) {\n            ngx_log_error(tf->log_level, tf->file.log, 0, \"%s %V\",\n                          tf->warn, &tf->file.name);\n        }\n    }\n\n#if (NGX_THREADS && NGX_HAVE_PWRITEV)\n\n    if (tf->thread_write) {\n        return ngx_thread_write_chain_to_file(&tf->file, chain, tf->offset,\n                                              tf->pool);\n    }\n\n#endif\n\n    return ngx_write_chain_to_file(&tf->file, chain, tf->offset, tf->pool);\n}\n\n\nngx_int_t\nngx_create_temp_file(ngx_file_t *file, ngx_path_t *path, ngx_pool_t *pool,\n    ngx_uint_t persistent, ngx_uint_t clean, ngx_uint_t access)\n{\n    size_t                    levels;\n    u_char                   *p;\n    uint32_t                  n;\n    ngx_err_t                 err;\n    ngx_str_t                 name;\n    ngx_uint_t                prefix;\n    ngx_pool_cleanup_t       *cln;\n    ngx_pool_cleanup_file_t  *clnf;\n\n    if (file->name.len) {\n        name = file->name;\n        levels = 0;\n        prefix = 1;\n\n    } else {\n        name = path->name;\n        levels = path->len;\n        prefix = 0;\n    }\n\n    file->name.len = name.len + 1 + levels + 10;\n\n    file->name.data = ngx_pnalloc(pool, file->name.len + 1);\n    if (file->name.data == NULL) {\n        return NGX_ERROR;\n    }\n\n#if 0\n    for (i = 0; i < file->name.len; i++) {\n        file->name.data[i] = 'X';\n    }\n#endif\n\n    p = ngx_cpymem(file->name.data, name.data, name.len);\n\n    if (prefix) {\n        *p = '.';\n    }\n\n    p += 1 + levels;\n\n    n = (uint32_t) ngx_next_temp_number(0);\n\n    cln = ngx_pool_cleanup_add(pool, sizeof(ngx_pool_cleanup_file_t));\n    if (cln == NULL) {\n        return NGX_ERROR;\n    }\n\n    for ( ;; ) {\n        (void) ngx_sprintf(p, \"%010uD%Z\", n);\n\n        if (!prefix) {\n            ngx_create_hashed_filename(path, file->name.data, file->name.len);\n        }\n\n        ngx_log_debug1(NGX_LOG_DEBUG_CORE, file->log, 0,\n                       \"hashed path: %s\", file->name.data);\n\n        file->fd = ngx_open_tempfile(file->name.data, persistent, access);\n\n        ngx_log_debug1(NGX_LOG_DEBUG_CORE, file->log, 0,\n                       \"temp fd:%d\", file->fd);\n\n        if (file->fd != NGX_INVALID_FILE) {\n\n            cln->handler = clean ? ngx_pool_delete_file : ngx_pool_cleanup_file;\n            clnf = cln->data;\n\n            clnf->fd = file->fd;\n            clnf->name = file->name.data;\n            clnf->log = pool->log;\n\n            return NGX_OK;\n        }\n\n        err = ngx_errno;\n\n        if (err == NGX_EEXIST_FILE) {\n            n = (uint32_t) ngx_next_temp_number(1);\n            continue;\n        }\n\n        if ((path->level[0] == 0) || (err != NGX_ENOPATH)) {\n            ngx_log_error(NGX_LOG_CRIT, file->log, err,\n                          ngx_open_tempfile_n \" \\\"%s\\\" failed\",\n                          file->name.data);\n            return NGX_ERROR;\n        }\n\n        if (ngx_create_path(file, path) == NGX_ERROR) {\n            return NGX_ERROR;\n        }\n    }\n}\n\n\nvoid\nngx_create_hashed_filename(ngx_path_t *path, u_char *file, size_t len)\n{\n    size_t      i, level;\n    ngx_uint_t  n;\n\n    i = path->name.len + 1;\n\n    file[path->name.len + path->len]  = '/';\n\n    for (n = 0; n < NGX_MAX_PATH_LEVEL; n++) {\n        level = path->level[n];\n\n        if (level == 0) {\n            break;\n        }\n\n        len -= level;\n        file[i - 1] = '/';\n        ngx_memcpy(&file[i], &file[len], level);\n        i += level + 1;\n    }\n}\n\n\nngx_int_t\nngx_create_path(ngx_file_t *file, ngx_path_t *path)\n{\n    size_t      pos;\n    ngx_err_t   err;\n    ngx_uint_t  i;\n\n    pos = path->name.len;\n\n    for (i = 0; i < NGX_MAX_PATH_LEVEL; i++) {\n        if (path->level[i] == 0) {\n            break;\n        }\n\n        pos += path->level[i] + 1;\n\n        file->name.data[pos] = '\\0';\n\n        ngx_log_debug1(NGX_LOG_DEBUG_CORE, file->log, 0,\n                       \"temp file: \\\"%s\\\"\", file->name.data);\n\n        if (ngx_create_dir(file->name.data, 0700) == NGX_FILE_ERROR) {\n            err = ngx_errno;\n            if (err != NGX_EEXIST) {\n                ngx_log_error(NGX_LOG_CRIT, file->log, err,\n                              ngx_create_dir_n \" \\\"%s\\\" failed\",\n                              file->name.data);\n                return NGX_ERROR;\n            }\n        }\n\n        file->name.data[pos] = '/';\n    }\n\n    return NGX_OK;\n}\n\n\nngx_err_t\nngx_create_full_path(u_char *dir, ngx_uint_t access)\n{\n    u_char     *p, ch;\n    ngx_err_t   err;\n\n    err = 0;\n\n#if (NGX_WIN32)\n    p = dir + 3;\n#else\n    p = dir + 1;\n#endif\n\n    for ( /* void */ ; *p; p++) {\n        ch = *p;\n\n        if (ch != '/') {\n            continue;\n        }\n\n        *p = '\\0';\n\n        if (ngx_create_dir(dir, access) == NGX_FILE_ERROR) {\n            err = ngx_errno;\n\n            switch (err) {\n            case NGX_EEXIST:\n                err = 0;\n            case NGX_EACCES:\n                break;\n\n            default:\n                return err;\n            }\n        }\n\n        *p = '/';\n    }\n\n    return err;\n}\n\n\nngx_atomic_uint_t\nngx_next_temp_number(ngx_uint_t collision)\n{\n    ngx_atomic_uint_t  n, add;\n\n    add = collision ? ngx_random_number : 1;\n\n    n = ngx_atomic_fetch_add(ngx_temp_number, add);\n\n    return n + add;\n}\n\n\nchar *\nngx_conf_set_path_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    char  *p = conf;\n\n    ssize_t      level;\n    ngx_str_t   *value;\n    ngx_uint_t   i, n;\n    ngx_path_t  *path, **slot;\n\n    slot = (ngx_path_t **) (p + cmd->offset);\n\n    if (*slot) {\n        return \"is duplicate\";\n    }\n\n    path = ngx_pcalloc(cf->pool, sizeof(ngx_path_t));\n    if (path == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    value = cf->args->elts;\n\n    path->name = value[1];\n\n    if (path->name.data[path->name.len - 1] == '/') {\n        path->name.len--;\n    }\n\n    if (ngx_conf_full_name(cf->cycle, &path->name, 0) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    path->conf_file = cf->conf_file->file.name.data;\n    path->line = cf->conf_file->line;\n\n    for (i = 0, n = 2; n < cf->args->nelts; i++, n++) {\n        level = ngx_atoi(value[n].data, value[n].len);\n        if (level == NGX_ERROR || level == 0) {\n            return \"invalid value\";\n        }\n\n        path->level[i] = level;\n        path->len += level + 1;\n    }\n\n    if (path->len > 10 + i) {\n        return \"invalid value\";\n    }\n\n    *slot = path;\n\n    if (ngx_add_path(cf, slot) == NGX_ERROR) {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nchar *\nngx_conf_merge_path_value(ngx_conf_t *cf, ngx_path_t **path, ngx_path_t *prev,\n    ngx_path_init_t *init)\n{\n    ngx_uint_t  i;\n\n    if (*path) {\n        return NGX_CONF_OK;\n    }\n\n    if (prev) {\n        *path = prev;\n        return NGX_CONF_OK;\n    }\n\n    *path = ngx_pcalloc(cf->pool, sizeof(ngx_path_t));\n    if (*path == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    (*path)->name = init->name;\n\n    if (ngx_conf_full_name(cf->cycle, &(*path)->name, 0) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    for (i = 0; i < NGX_MAX_PATH_LEVEL; i++) {\n        (*path)->level[i] = init->level[i];\n        (*path)->len += init->level[i] + (init->level[i] ? 1 : 0);\n    }\n\n    if (ngx_add_path(cf, path) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nchar *\nngx_conf_set_access_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    char  *confp = conf;\n\n    u_char      *p;\n    ngx_str_t   *value;\n    ngx_uint_t   i, right, shift, *access, user;\n\n    access = (ngx_uint_t *) (confp + cmd->offset);\n\n    if (*access != NGX_CONF_UNSET_UINT) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    *access = 0;\n    user = 0600;\n\n    for (i = 1; i < cf->args->nelts; i++) {\n\n        p = value[i].data;\n\n        if (ngx_strncmp(p, \"user:\", sizeof(\"user:\") - 1) == 0) {\n            shift = 6;\n            p += sizeof(\"user:\") - 1;\n            user = 0;\n\n        } else if (ngx_strncmp(p, \"group:\", sizeof(\"group:\") - 1) == 0) {\n            shift = 3;\n            p += sizeof(\"group:\") - 1;\n\n        } else if (ngx_strncmp(p, \"all:\", sizeof(\"all:\") - 1) == 0) {\n            shift = 0;\n            p += sizeof(\"all:\") - 1;\n\n        } else {\n            goto invalid;\n        }\n\n        if (ngx_strcmp(p, \"rw\") == 0) {\n            right = 6;\n\n        } else if (ngx_strcmp(p, \"r\") == 0) {\n            right = 4;\n\n        } else {\n            goto invalid;\n        }\n\n        *access |= right << shift;\n    }\n\n    *access |= user;\n\n    return NGX_CONF_OK;\n\ninvalid:\n\n    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, \"invalid value \\\"%V\\\"\", &value[i]);\n\n    return NGX_CONF_ERROR;\n}\n\n\nngx_int_t\nngx_add_path(ngx_conf_t *cf, ngx_path_t **slot)\n{\n    ngx_uint_t   i, n;\n    ngx_path_t  *path, **p;\n\n    path = *slot;\n\n    p = cf->cycle->paths.elts;\n    for (i = 0; i < cf->cycle->paths.nelts; i++) {\n        if (p[i]->name.len == path->name.len\n            && ngx_strcmp(p[i]->name.data, path->name.data) == 0)\n        {\n            if (p[i]->data != path->data) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"the same path name \\\"%V\\\" \"\n                                   \"used in %s:%ui and\",\n                                   &p[i]->name, p[i]->conf_file, p[i]->line);\n                return NGX_ERROR;\n            }\n\n            for (n = 0; n < NGX_MAX_PATH_LEVEL; n++) {\n                if (p[i]->level[n] != path->level[n]) {\n                    if (path->conf_file == NULL) {\n                        if (p[i]->conf_file == NULL) {\n                            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                                      \"the default path name \\\"%V\\\" has \"\n                                      \"the same name as another default path, \"\n                                      \"but the different levels, you need to \"\n                                      \"redefine one of them in http section\",\n                                      &p[i]->name);\n                            return NGX_ERROR;\n                        }\n\n                        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                                      \"the path name \\\"%V\\\" in %s:%ui has \"\n                                      \"the same name as default path, but \"\n                                      \"the different levels, you need to \"\n                                      \"define default path in http section\",\n                                      &p[i]->name, p[i]->conf_file, p[i]->line);\n                        return NGX_ERROR;\n                    }\n\n                    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                      \"the same path name \\\"%V\\\" in %s:%ui \"\n                                      \"has the different levels than\",\n                                      &p[i]->name, p[i]->conf_file, p[i]->line);\n                    return NGX_ERROR;\n                }\n\n                if (p[i]->level[n] == 0) {\n                    break;\n                }\n            }\n\n            *slot = p[i];\n\n            return NGX_OK;\n        }\n    }\n\n    p = ngx_array_push(&cf->cycle->paths);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    *p = path;\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_create_paths(ngx_cycle_t *cycle, ngx_uid_t user)\n{\n    ngx_err_t         err;\n    ngx_uint_t        i;\n    ngx_path_t      **path;\n\n    path = cycle->paths.elts;\n    for (i = 0; i < cycle->paths.nelts; i++) {\n\n        if (ngx_create_dir(path[i]->name.data, 0700) == NGX_FILE_ERROR) {\n            err = ngx_errno;\n            if (err != NGX_EEXIST) {\n                ngx_log_error(NGX_LOG_EMERG, cycle->log, err,\n                              ngx_create_dir_n \" \\\"%s\\\" failed\",\n                              path[i]->name.data);\n                return NGX_ERROR;\n            }\n        }\n\n        if (user == (ngx_uid_t) NGX_CONF_UNSET_UINT) {\n            continue;\n        }\n\n#if !(NGX_WIN32)\n        {\n        ngx_file_info_t   fi;\n\n        if (ngx_file_info(path[i]->name.data, &fi) == NGX_FILE_ERROR) {\n            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                          ngx_file_info_n \" \\\"%s\\\" failed\", path[i]->name.data);\n            return NGX_ERROR;\n        }\n\n        if (fi.st_uid != user) {\n            if (chown((const char *) path[i]->name.data, user, -1) == -1) {\n                ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                              \"chown(\\\"%s\\\", %d) failed\",\n                              path[i]->name.data, user);\n                return NGX_ERROR;\n            }\n        }\n\n        if ((fi.st_mode & (S_IRUSR|S_IWUSR|S_IXUSR))\n                                                  != (S_IRUSR|S_IWUSR|S_IXUSR))\n        {\n            fi.st_mode |= (S_IRUSR|S_IWUSR|S_IXUSR);\n\n            if (chmod((const char *) path[i]->name.data, fi.st_mode) == -1) {\n                ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                              \"chmod() \\\"%s\\\" failed\", path[i]->name.data);\n                return NGX_ERROR;\n            }\n        }\n        }\n#endif\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_ext_rename_file(ngx_str_t *src, ngx_str_t *to, ngx_ext_rename_file_t *ext)\n{\n    u_char           *name;\n    ngx_err_t         err;\n    ngx_copy_file_t   cf;\n\n#if !(NGX_WIN32)\n\n    if (ext->access) {\n        if (ngx_change_file_access(src->data, ext->access) == NGX_FILE_ERROR) {\n            ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno,\n                          ngx_change_file_access_n \" \\\"%s\\\" failed\", src->data);\n            err = 0;\n            goto failed;\n        }\n    }\n\n#endif\n\n    if (ext->time != -1) {\n        if (ngx_set_file_time(src->data, ext->fd, ext->time) != NGX_OK) {\n            ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno,\n                          ngx_set_file_time_n \" \\\"%s\\\" failed\", src->data);\n            err = 0;\n            goto failed;\n        }\n    }\n\n    if (ngx_rename_file(src->data, to->data) != NGX_FILE_ERROR) {\n        return NGX_OK;\n    }\n\n    err = ngx_errno;\n\n    if (err == NGX_ENOPATH) {\n\n        if (!ext->create_path) {\n            goto failed;\n        }\n\n        err = ngx_create_full_path(to->data, ngx_dir_access(ext->path_access));\n\n        if (err) {\n            ngx_log_error(NGX_LOG_CRIT, ext->log, err,\n                          ngx_create_dir_n \" \\\"%s\\\" failed\", to->data);\n            err = 0;\n            goto failed;\n        }\n\n        if (ngx_rename_file(src->data, to->data) != NGX_FILE_ERROR) {\n            return NGX_OK;\n        }\n\n        err = ngx_errno;\n    }\n\n#if (NGX_WIN32)\n\n    if (err == NGX_EEXIST || err == NGX_EEXIST_FILE) {\n        err = ngx_win32_rename_file(src, to, ext->log);\n\n        if (err == 0) {\n            return NGX_OK;\n        }\n    }\n\n#endif\n\n    if (err == NGX_EXDEV) {\n\n        cf.size = -1;\n        cf.buf_size = 0;\n        cf.access = ext->access;\n        cf.time = ext->time;\n        cf.log = ext->log;\n\n        name = ngx_alloc(to->len + 1 + 10 + 1, ext->log);\n        if (name == NULL) {\n            return NGX_ERROR;\n        }\n\n        (void) ngx_sprintf(name, \"%*s.%010uD%Z\", to->len, to->data,\n                           (uint32_t) ngx_next_temp_number(0));\n\n        if (ngx_copy_file(src->data, name, &cf) == NGX_OK) {\n\n            if (ngx_rename_file(name, to->data) != NGX_FILE_ERROR) {\n                ngx_free(name);\n\n                if (ngx_delete_file(src->data) == NGX_FILE_ERROR) {\n                    ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno,\n                                  ngx_delete_file_n \" \\\"%s\\\" failed\",\n                                  src->data);\n                    return NGX_ERROR;\n                }\n\n                return NGX_OK;\n            }\n\n            ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno,\n                          ngx_rename_file_n \" \\\"%s\\\" to \\\"%s\\\" failed\",\n                          name, to->data);\n\n            if (ngx_delete_file(name) == NGX_FILE_ERROR) {\n                ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno,\n                              ngx_delete_file_n \" \\\"%s\\\" failed\", name);\n\n            }\n        }\n\n        ngx_free(name);\n\n        err = 0;\n    }\n\nfailed:\n\n    if (ext->delete_file) {\n        if (ngx_delete_file(src->data) == NGX_FILE_ERROR) {\n            ngx_log_error(NGX_LOG_CRIT, ext->log, ngx_errno,\n                          ngx_delete_file_n \" \\\"%s\\\" failed\", src->data);\n        }\n    }\n\n    if (err) {\n        ngx_log_error(NGX_LOG_CRIT, ext->log, err,\n                      ngx_rename_file_n \" \\\"%s\\\" to \\\"%s\\\" failed\",\n                      src->data, to->data);\n    }\n\n    return NGX_ERROR;\n}\n\n\nngx_int_t\nngx_copy_file(u_char *from, u_char *to, ngx_copy_file_t *cf)\n{\n    char             *buf;\n    off_t             size;\n    time_t            time;\n    size_t            len;\n    ssize_t           n;\n    ngx_fd_t          fd, nfd;\n    ngx_int_t         rc;\n    ngx_uint_t        access;\n    ngx_file_info_t   fi;\n\n    rc = NGX_ERROR;\n    buf = NULL;\n    nfd = NGX_INVALID_FILE;\n\n    fd = ngx_open_file(from, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);\n\n    if (fd == NGX_INVALID_FILE) {\n        ngx_log_error(NGX_LOG_CRIT, cf->log, ngx_errno,\n                      ngx_open_file_n \" \\\"%s\\\" failed\", from);\n        goto failed;\n    }\n\n    if (cf->size != -1 && cf->access != 0 && cf->time != -1) {\n        size = cf->size;\n        access = cf->access;\n        time = cf->time;\n\n    } else {\n        if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) {\n            ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,\n                          ngx_fd_info_n \" \\\"%s\\\" failed\", from);\n\n            goto failed;\n        }\n\n        size = (cf->size != -1) ? cf->size : ngx_file_size(&fi);\n        access = cf->access ? cf->access : ngx_file_access(&fi);\n        time = (cf->time != -1) ? cf->time : ngx_file_mtime(&fi);\n    }\n\n    len = cf->buf_size ? cf->buf_size : 65536;\n\n    if ((off_t) len > size) {\n        len = (size_t) size;\n    }\n\n    buf = ngx_alloc(len, cf->log);\n    if (buf == NULL) {\n        goto failed;\n    }\n\n    nfd = ngx_open_file(to, NGX_FILE_WRONLY, NGX_FILE_TRUNCATE, access);\n\n    if (nfd == NGX_INVALID_FILE) {\n        ngx_log_error(NGX_LOG_CRIT, cf->log, ngx_errno,\n                      ngx_open_file_n \" \\\"%s\\\" failed\", to);\n        goto failed;\n    }\n\n    while (size > 0) {\n\n        if ((off_t) len > size) {\n            len = (size_t) size;\n        }\n\n        n = ngx_read_fd(fd, buf, len);\n\n        if (n == -1) {\n            ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,\n                          ngx_read_fd_n \" \\\"%s\\\" failed\", from);\n            goto failed;\n        }\n\n        if ((size_t) n != len) {\n            ngx_log_error(NGX_LOG_ALERT, cf->log, 0,\n                          ngx_read_fd_n \" has read only %z of %O from %s\",\n                          n, size, from);\n            goto failed;\n        }\n\n        n = ngx_write_fd(nfd, buf, len);\n\n        if (n == -1) {\n            ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,\n                          ngx_write_fd_n \" \\\"%s\\\" failed\", to);\n            goto failed;\n        }\n\n        if ((size_t) n != len) {\n            ngx_log_error(NGX_LOG_ALERT, cf->log, 0,\n                          ngx_write_fd_n \" has written only %z of %O to %s\",\n                          n, size, to);\n            goto failed;\n        }\n\n        size -= n;\n    }\n\n    if (ngx_set_file_time(to, nfd, time) != NGX_OK) {\n        ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,\n                      ngx_set_file_time_n \" \\\"%s\\\" failed\", to);\n        goto failed;\n    }\n\n    rc = NGX_OK;\n\nfailed:\n\n    if (nfd != NGX_INVALID_FILE) {\n        if (ngx_close_file(nfd) == NGX_FILE_ERROR) {\n            ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,\n                          ngx_close_file_n \" \\\"%s\\\" failed\", to);\n        }\n    }\n\n    if (fd != NGX_INVALID_FILE) {\n        if (ngx_close_file(fd) == NGX_FILE_ERROR) {\n            ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,\n                          ngx_close_file_n \" \\\"%s\\\" failed\", from);\n        }\n    }\n\n    if (buf) {\n        ngx_free(buf);\n    }\n\n    return rc;\n}\n\n\n/*\n * ctx->init_handler() - see ctx->alloc\n * ctx->file_handler() - file handler\n * ctx->pre_tree_handler() - handler is called before entering directory\n * ctx->post_tree_handler() - handler is called after leaving directory\n * ctx->spec_handler() - special (socket, FIFO, etc.) file handler\n *\n * ctx->data - some data structure, it may be the same on all levels, or\n *     reallocated if ctx->alloc is nonzero\n *\n * ctx->alloc - a size of data structure that is allocated at every level\n *     and is initialized by ctx->init_handler()\n *\n * ctx->log - a log\n *\n * on fatal (memory) error handler must return NGX_ABORT to stop walking tree\n */\n\nngx_int_t\nngx_walk_tree(ngx_tree_ctx_t *ctx, ngx_str_t *tree)\n{\n    void       *data, *prev;\n    u_char     *p, *name;\n    size_t      len;\n    ngx_int_t   rc;\n    ngx_err_t   err;\n    ngx_str_t   file, buf;\n    ngx_dir_t   dir;\n\n    ngx_str_null(&buf);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->log, 0,\n                   \"walk tree \\\"%V\\\"\", tree);\n\n    if (ngx_open_dir(tree, &dir) == NGX_ERROR) {\n        ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno,\n                      ngx_open_dir_n \" \\\"%s\\\" failed\", tree->data);\n        return NGX_ERROR;\n    }\n\n    prev = ctx->data;\n\n    if (ctx->alloc) {\n        data = ngx_alloc(ctx->alloc, ctx->log);\n        if (data == NULL) {\n            goto failed;\n        }\n\n        if (ctx->init_handler(data, prev) == NGX_ABORT) {\n            goto failed;\n        }\n\n        ctx->data = data;\n\n    } else {\n        data = NULL;\n    }\n\n    for ( ;; ) {\n\n        ngx_set_errno(0);\n\n        if (ngx_read_dir(&dir) == NGX_ERROR) {\n            err = ngx_errno;\n\n            if (err == NGX_ENOMOREFILES) {\n                rc = NGX_OK;\n\n            } else {\n                ngx_log_error(NGX_LOG_CRIT, ctx->log, err,\n                              ngx_read_dir_n \" \\\"%s\\\" failed\", tree->data);\n                rc = NGX_ERROR;\n            }\n\n            goto done;\n        }\n\n        len = ngx_de_namelen(&dir);\n        name = ngx_de_name(&dir);\n\n        ngx_log_debug2(NGX_LOG_DEBUG_CORE, ctx->log, 0,\n                      \"tree name %uz:\\\"%s\\\"\", len, name);\n\n        if (len == 1 && name[0] == '.') {\n            continue;\n        }\n\n        if (len == 2 && name[0] == '.' && name[1] == '.') {\n            continue;\n        }\n\n        file.len = tree->len + 1 + len;\n\n        if (file.len > buf.len) {\n\n            if (buf.len) {\n                ngx_free(buf.data);\n            }\n\n            buf.len = tree->len + 1 + len;\n\n            buf.data = ngx_alloc(buf.len + 1, ctx->log);\n            if (buf.data == NULL) {\n                goto failed;\n            }\n        }\n\n        p = ngx_cpymem(buf.data, tree->data, tree->len);\n        *p++ = '/';\n        ngx_memcpy(p, name, len + 1);\n\n        file.data = buf.data;\n\n        ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->log, 0,\n                       \"tree path \\\"%s\\\"\", file.data);\n\n        if (!dir.valid_info) {\n            if (ngx_de_info(file.data, &dir) == NGX_FILE_ERROR) {\n                ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno,\n                              ngx_de_info_n \" \\\"%s\\\" failed\", file.data);\n                continue;\n            }\n        }\n\n        if (ngx_de_is_file(&dir)) {\n\n            ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->log, 0,\n                           \"tree file \\\"%s\\\"\", file.data);\n\n            ctx->size = ngx_de_size(&dir);\n            ctx->fs_size = ngx_de_fs_size(&dir);\n            ctx->access = ngx_de_access(&dir);\n            ctx->mtime = ngx_de_mtime(&dir);\n\n            if (ctx->file_handler(ctx, &file) == NGX_ABORT) {\n                goto failed;\n            }\n\n        } else if (ngx_de_is_dir(&dir)) {\n\n            ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->log, 0,\n                           \"tree enter dir \\\"%s\\\"\", file.data);\n\n            ctx->access = ngx_de_access(&dir);\n            ctx->mtime = ngx_de_mtime(&dir);\n\n            rc = ctx->pre_tree_handler(ctx, &file);\n\n            if (rc == NGX_ABORT) {\n                goto failed;\n            }\n\n            if (rc == NGX_DECLINED) {\n                ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->log, 0,\n                               \"tree skip dir \\\"%s\\\"\", file.data);\n                continue;\n            }\n\n            if (ngx_walk_tree(ctx, &file) == NGX_ABORT) {\n                goto failed;\n            }\n\n            ctx->access = ngx_de_access(&dir);\n            ctx->mtime = ngx_de_mtime(&dir);\n\n            if (ctx->post_tree_handler(ctx, &file) == NGX_ABORT) {\n                goto failed;\n            }\n\n        } else {\n\n            ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->log, 0,\n                           \"tree special \\\"%s\\\"\", file.data);\n\n            if (ctx->spec_handler(ctx, &file) == NGX_ABORT) {\n                goto failed;\n            }\n        }\n    }\n\nfailed:\n\n    rc = NGX_ABORT;\n\ndone:\n\n    if (buf.len) {\n        ngx_free(buf.data);\n    }\n\n    if (data) {\n        ngx_free(data);\n        ctx->data = prev;\n    }\n\n    if (ngx_close_dir(&dir) == NGX_ERROR) {\n        ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno,\n                      ngx_close_dir_n \" \\\"%s\\\" failed\", tree->data);\n    }\n\n    return rc;\n}\n"
  },
  {
    "path": "src/core/ngx_file.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_FILE_H_INCLUDED_\n#define _NGX_FILE_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\nstruct ngx_file_s {\n    ngx_fd_t                   fd;\n    ngx_str_t                  name;\n    ngx_file_info_t            info;\n\n    off_t                      offset;\n    off_t                      sys_offset;\n\n    ngx_log_t                 *log;\n\n#if (NGX_THREADS || NGX_COMPAT)\n    ngx_int_t                (*thread_handler)(ngx_thread_task_t *task,\n                                               ngx_file_t *file);\n    void                      *thread_ctx;\n    ngx_thread_task_t         *thread_task;\n#endif\n\n#if (NGX_HAVE_FILE_AIO || NGX_COMPAT)\n    ngx_event_aio_t           *aio;\n#endif\n\n    unsigned                   valid_info:1;\n    unsigned                   directio:1;\n};\n\n\n#define NGX_MAX_PATH_LEVEL  3\n\n\ntypedef ngx_msec_t (*ngx_path_manager_pt) (void *data);\ntypedef ngx_msec_t (*ngx_path_purger_pt) (void *data);\ntypedef void (*ngx_path_loader_pt) (void *data);\n\n\ntypedef struct {\n    ngx_str_t                  name;\n    size_t                     len;\n    size_t                     level[NGX_MAX_PATH_LEVEL];\n\n    ngx_path_manager_pt        manager;\n    ngx_path_purger_pt         purger;\n    ngx_path_loader_pt         loader;\n    void                      *data;\n\n    u_char                    *conf_file;\n    ngx_uint_t                 line;\n} ngx_path_t;\n\n\ntypedef struct {\n    ngx_str_t                  name;\n    size_t                     level[NGX_MAX_PATH_LEVEL];\n} ngx_path_init_t;\n\n\ntypedef struct {\n    ngx_file_t                 file;\n    off_t                      offset;\n    ngx_path_t                *path;\n    ngx_pool_t                *pool;\n    char                      *warn;\n\n    ngx_uint_t                 access;\n\n    unsigned                   log_level:8;\n    unsigned                   persistent:1;\n    unsigned                   clean:1;\n    unsigned                   thread_write:1;\n} ngx_temp_file_t;\n\n\ntypedef struct {\n    ngx_uint_t                 access;\n    ngx_uint_t                 path_access;\n    time_t                     time;\n    ngx_fd_t                   fd;\n\n    unsigned                   create_path:1;\n    unsigned                   delete_file:1;\n\n    ngx_log_t                 *log;\n} ngx_ext_rename_file_t;\n\n\ntypedef struct {\n    off_t                      size;\n    size_t                     buf_size;\n\n    ngx_uint_t                 access;\n    time_t                     time;\n\n    ngx_log_t                 *log;\n} ngx_copy_file_t;\n\n\ntypedef struct ngx_tree_ctx_s  ngx_tree_ctx_t;\n\ntypedef ngx_int_t (*ngx_tree_init_handler_pt) (void *ctx, void *prev);\ntypedef ngx_int_t (*ngx_tree_handler_pt) (ngx_tree_ctx_t *ctx, ngx_str_t *name);\n\nstruct ngx_tree_ctx_s {\n    off_t                      size;\n    off_t                      fs_size;\n    ngx_uint_t                 access;\n    time_t                     mtime;\n\n    ngx_tree_init_handler_pt   init_handler;\n    ngx_tree_handler_pt        file_handler;\n    ngx_tree_handler_pt        pre_tree_handler;\n    ngx_tree_handler_pt        post_tree_handler;\n    ngx_tree_handler_pt        spec_handler;\n\n    void                      *data;\n    size_t                     alloc;\n\n    ngx_log_t                 *log;\n};\n\n\nngx_int_t ngx_get_full_name(ngx_pool_t *pool, ngx_str_t *prefix,\n    ngx_str_t *name);\n\nssize_t ngx_write_chain_to_temp_file(ngx_temp_file_t *tf, ngx_chain_t *chain);\nngx_int_t ngx_create_temp_file(ngx_file_t *file, ngx_path_t *path,\n    ngx_pool_t *pool, ngx_uint_t persistent, ngx_uint_t clean,\n    ngx_uint_t access);\nvoid ngx_create_hashed_filename(ngx_path_t *path, u_char *file, size_t len);\nngx_int_t ngx_create_path(ngx_file_t *file, ngx_path_t *path);\nngx_err_t ngx_create_full_path(u_char *dir, ngx_uint_t access);\nngx_int_t ngx_add_path(ngx_conf_t *cf, ngx_path_t **slot);\nngx_int_t ngx_create_paths(ngx_cycle_t *cycle, ngx_uid_t user);\nngx_int_t ngx_ext_rename_file(ngx_str_t *src, ngx_str_t *to,\n    ngx_ext_rename_file_t *ext);\nngx_int_t ngx_copy_file(u_char *from, u_char *to, ngx_copy_file_t *cf);\nngx_int_t ngx_walk_tree(ngx_tree_ctx_t *ctx, ngx_str_t *tree);\n\nngx_atomic_uint_t ngx_next_temp_number(ngx_uint_t collision);\n\nchar *ngx_conf_set_path_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\nchar *ngx_conf_merge_path_value(ngx_conf_t *cf, ngx_path_t **path,\n    ngx_path_t *prev, ngx_path_init_t *init);\nchar *ngx_conf_set_access_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\n\n\nextern ngx_atomic_t      *ngx_temp_number;\nextern ngx_atomic_int_t   ngx_random_number;\n\n\n#endif /* _NGX_FILE_H_INCLUDED_ */\n"
  },
  {
    "path": "src/core/ngx_hash.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\nvoid *\nngx_hash_find(ngx_hash_t *hash, ngx_uint_t key, u_char *name, size_t len)\n{\n    ngx_uint_t       i;\n    ngx_hash_elt_t  *elt;\n\n#if 0\n    ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, \"hf:\\\"%*s\\\"\", len, name);\n#endif\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 (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\nvoid *\nngx_hash_find_wc_head(ngx_hash_wildcard_t *hwc, u_char *name, size_t len)\n{\n    void        *value;\n    ngx_uint_t   i, n, key;\n\n#if 0\n    ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, \"wch:\\\"%*s\\\"\", len, name);\n#endif\n\n    n = len;\n\n    while (n) {\n        if (name[n - 1] == '.') {\n            break;\n        }\n\n        n--;\n    }\n\n    key = 0;\n\n    for (i = n; i < len; i++) {\n        key = ngx_hash(key, name[i]);\n    }\n\n#if 0\n    ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, \"key:\\\"%ui\\\"\", key);\n#endif\n\n    value = ngx_hash_find(&hwc->hash, key, &name[n], len - n);\n\n#if 0\n    ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, \"value:\\\"%p\\\"\", value);\n#endif\n\n    if (value) {\n\n        /*\n         * the 2 low bits of value have the special meaning:\n         *     00 - value is data pointer for both \"example.com\"\n         *          and \"*.example.com\";\n         *     01 - value is data pointer for \"*.example.com\" only;\n         *     10 - value is pointer to wildcard hash allowing\n         *          both \"example.com\" and \"*.example.com\";\n         *     11 - value is pointer to wildcard hash allowing\n         *          \"*.example.com\" only.\n         */\n\n        if ((uintptr_t) value & 2) {\n\n            if (n == 0) {\n\n                /* \"example.com\" */\n\n                if ((uintptr_t) value & 1) {\n                    return NULL;\n                }\n\n                hwc = (ngx_hash_wildcard_t *)\n                                          ((uintptr_t) value & (uintptr_t) ~3);\n                return hwc->value;\n            }\n\n            hwc = (ngx_hash_wildcard_t *) ((uintptr_t) value & (uintptr_t) ~3);\n\n            value = ngx_hash_find_wc_head(hwc, name, n - 1);\n\n            if (value) {\n                return value;\n            }\n\n            return hwc->value;\n        }\n\n        if ((uintptr_t) value & 1) {\n\n            if (n == 0) {\n\n                /* \"example.com\" */\n\n                return NULL;\n            }\n\n            return (void *) ((uintptr_t) value & (uintptr_t) ~3);\n        }\n\n        return value;\n    }\n\n    return hwc->value;\n}\n\n\nvoid *\nngx_hash_find_wc_tail(ngx_hash_wildcard_t *hwc, u_char *name, size_t len)\n{\n    void        *value;\n    ngx_uint_t   i, key;\n\n#if 0\n    ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, \"wct:\\\"%*s\\\"\", len, name);\n#endif\n\n    key = 0;\n\n    for (i = 0; i < len; i++) {\n        if (name[i] == '.') {\n            break;\n        }\n\n        key = ngx_hash(key, name[i]);\n    }\n\n    if (i == len) {\n        return NULL;\n    }\n\n#if 0\n    ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, \"key:\\\"%ui\\\"\", key);\n#endif\n\n    value = ngx_hash_find(&hwc->hash, key, name, i);\n\n#if 0\n    ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, \"value:\\\"%p\\\"\", value);\n#endif\n\n    if (value) {\n\n        /*\n         * the 2 low bits of value have the special meaning:\n         *     00 - value is data pointer;\n         *     11 - value is pointer to wildcard hash allowing \"example.*\".\n         */\n\n        if ((uintptr_t) value & 2) {\n\n            i++;\n\n            hwc = (ngx_hash_wildcard_t *) ((uintptr_t) value & (uintptr_t) ~3);\n\n            value = ngx_hash_find_wc_tail(hwc, &name[i], len - i);\n\n            if (value) {\n                return value;\n            }\n\n            return hwc->value;\n        }\n\n        return value;\n    }\n\n    return hwc->value;\n}\n\n\nvoid *\nngx_hash_find_combined(ngx_hash_combined_t *hash, ngx_uint_t key, u_char *name,\n    size_t len)\n{\n    void  *value;\n\n    if (hash->hash.buckets) {\n        value = ngx_hash_find(&hash->hash, key, name, len);\n\n        if (value) {\n            return value;\n        }\n    }\n\n    if (len == 0) {\n        return NULL;\n    }\n\n    if (hash->wc_head && hash->wc_head->hash.buckets) {\n        value = ngx_hash_find_wc_head(hash->wc_head, name, len);\n\n        if (value) {\n            return value;\n        }\n    }\n\n    if (hash->wc_tail && hash->wc_tail->hash.buckets) {\n        value = ngx_hash_find_wc_tail(hash->wc_tail, name, len);\n\n        if (value) {\n            return value;\n        }\n    }\n\n    return NULL;\n}\n\n\n#define NGX_HASH_ELT_SIZE(name)                                               \\\n    (sizeof(void *) + ngx_align((name)->key.len + 2, sizeof(void *)))\n\nngx_int_t\nngx_hash_init(ngx_hash_init_t *hinit, ngx_hash_key_t *names, ngx_uint_t nelts)\n{\n    u_char          *elts;\n    size_t           len;\n    u_short         *test;\n    ngx_uint_t       i, n, key, size, start, bucket_size;\n    ngx_hash_elt_t  *elt, **buckets;\n\n    if (hinit->max_size == 0) {\n        ngx_log_error(NGX_LOG_EMERG, hinit->pool->log, 0,\n                      \"could not build %s, you should \"\n                      \"increase %s_max_size: %i\",\n                      hinit->name, hinit->name, hinit->max_size);\n        return NGX_ERROR;\n    }\n\n    if (hinit->bucket_size > 65536 - ngx_cacheline_size) {\n        ngx_log_error(NGX_LOG_EMERG, hinit->pool->log, 0,\n                      \"could not build %s, too large \"\n                      \"%s_bucket_size: %i\",\n                      hinit->name, hinit->name, hinit->bucket_size);\n        return NGX_ERROR;\n    }\n\n    for (n = 0; n < nelts; n++) {\n        if (names[n].key.data == NULL) {\n            continue;\n        }\n\n        if (hinit->bucket_size < NGX_HASH_ELT_SIZE(&names[n]) + sizeof(void *))\n        {\n            ngx_log_error(NGX_LOG_EMERG, hinit->pool->log, 0,\n                          \"could not build %s, you should \"\n                          \"increase %s_bucket_size: %i\",\n                          hinit->name, hinit->name, hinit->bucket_size);\n            return NGX_ERROR;\n        }\n    }\n\n    test = ngx_alloc(hinit->max_size * sizeof(u_short), hinit->pool->log);\n    if (test == NULL) {\n        return NGX_ERROR;\n    }\n\n    bucket_size = hinit->bucket_size - sizeof(void *);\n\n    start = nelts / (bucket_size / (2 * sizeof(void *)));\n    start = start ? start : 1;\n\n    if (hinit->max_size > 10000 && nelts && hinit->max_size / nelts < 100) {\n        start = hinit->max_size - 1000;\n    }\n\n    for (size = start; size <= hinit->max_size; size++) {\n\n        ngx_memzero(test, size * sizeof(u_short));\n\n        for (n = 0; n < nelts; n++) {\n            if (names[n].key.data == NULL) {\n                continue;\n            }\n\n            key = names[n].key_hash % size;\n            len = test[key] + NGX_HASH_ELT_SIZE(&names[n]);\n\n#if 0\n            ngx_log_error(NGX_LOG_ALERT, hinit->pool->log, 0,\n                          \"%ui: %ui %uz \\\"%V\\\"\",\n                          size, key, len, &names[n].key);\n#endif\n\n            if (len > bucket_size) {\n                goto next;\n            }\n\n            test[key] = (u_short) len;\n        }\n\n        goto found;\n\n    next:\n\n        continue;\n    }\n\n    size = hinit->max_size;\n\n    ngx_log_error(NGX_LOG_WARN, hinit->pool->log, 0,\n                  \"could not build optimal %s, you should increase \"\n                  \"either %s_max_size: %i or %s_bucket_size: %i; \"\n                  \"ignoring %s_bucket_size\",\n                  hinit->name, hinit->name, hinit->max_size,\n                  hinit->name, hinit->bucket_size, hinit->name);\n\nfound:\n\n    for (i = 0; i < size; i++) {\n        test[i] = sizeof(void *);\n    }\n\n    for (n = 0; n < nelts; n++) {\n        if (names[n].key.data == NULL) {\n            continue;\n        }\n\n        key = names[n].key_hash % size;\n        len = test[key] + NGX_HASH_ELT_SIZE(&names[n]);\n\n        if (len > 65536 - ngx_cacheline_size) {\n            ngx_log_error(NGX_LOG_EMERG, hinit->pool->log, 0,\n                          \"could not build %s, you should \"\n                          \"increase %s_max_size: %i\",\n                          hinit->name, hinit->name, hinit->max_size);\n            ngx_free(test);\n            return NGX_ERROR;\n        }\n\n        test[key] = (u_short) len;\n    }\n\n    len = 0;\n\n    for (i = 0; i < size; i++) {\n        if (test[i] == sizeof(void *)) {\n            continue;\n        }\n\n        test[i] = (u_short) (ngx_align(test[i], ngx_cacheline_size));\n\n        len += test[i];\n    }\n\n    if (hinit->hash == NULL) {\n        hinit->hash = ngx_pcalloc(hinit->pool, sizeof(ngx_hash_wildcard_t)\n                                             + size * sizeof(ngx_hash_elt_t *));\n        if (hinit->hash == NULL) {\n            ngx_free(test);\n            return NGX_ERROR;\n        }\n\n        buckets = (ngx_hash_elt_t **)\n                      ((u_char *) hinit->hash + sizeof(ngx_hash_wildcard_t));\n\n    } else {\n        buckets = ngx_pcalloc(hinit->pool, size * sizeof(ngx_hash_elt_t *));\n        if (buckets == NULL) {\n            ngx_free(test);\n            return NGX_ERROR;\n        }\n    }\n\n    elts = ngx_palloc(hinit->pool, len + ngx_cacheline_size);\n    if (elts == NULL) {\n        ngx_free(test);\n        return NGX_ERROR;\n    }\n\n    elts = ngx_align_ptr(elts, ngx_cacheline_size);\n\n    for (i = 0; i < size; i++) {\n        if (test[i] == sizeof(void *)) {\n            continue;\n        }\n\n        buckets[i] = (ngx_hash_elt_t *) elts;\n        elts += test[i];\n    }\n\n    for (i = 0; i < size; i++) {\n        test[i] = 0;\n    }\n\n    for (n = 0; n < nelts; n++) {\n        if (names[n].key.data == NULL) {\n            continue;\n        }\n\n        key = names[n].key_hash % size;\n        elt = (ngx_hash_elt_t *) ((u_char *) buckets[key] + test[key]);\n\n        elt->value = names[n].value;\n        elt->len = (u_short) names[n].key.len;\n\n        ngx_strlow(elt->name, names[n].key.data, names[n].key.len);\n\n        test[key] = (u_short) (test[key] + NGX_HASH_ELT_SIZE(&names[n]));\n    }\n\n    for (i = 0; i < size; i++) {\n        if (buckets[i] == NULL) {\n            continue;\n        }\n\n        elt = (ngx_hash_elt_t *) ((u_char *) buckets[i] + test[i]);\n\n        elt->value = NULL;\n    }\n\n    ngx_free(test);\n\n    hinit->hash->buckets = buckets;\n    hinit->hash->size = size;\n\n#if 0\n\n    for (i = 0; i < size; i++) {\n        ngx_str_t   val;\n        ngx_uint_t  key;\n\n        elt = buckets[i];\n\n        if (elt == NULL) {\n            ngx_log_error(NGX_LOG_ALERT, hinit->pool->log, 0,\n                          \"%ui: NULL\", i);\n            continue;\n        }\n\n        while (elt->value) {\n            val.len = elt->len;\n            val.data = &elt->name[0];\n\n            key = hinit->key(val.data, val.len);\n\n            ngx_log_error(NGX_LOG_ALERT, hinit->pool->log, 0,\n                          \"%ui: %p \\\"%V\\\" %ui\", i, elt, &val, key);\n\n            elt = (ngx_hash_elt_t *) ngx_align_ptr(&elt->name[0] + elt->len,\n                                                   sizeof(void *));\n        }\n    }\n\n#endif\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_hash_wildcard_init(ngx_hash_init_t *hinit, ngx_hash_key_t *names,\n    ngx_uint_t nelts)\n{\n    size_t                len, dot_len;\n    ngx_uint_t            i, n, dot;\n    ngx_array_t           curr_names, next_names;\n    ngx_hash_key_t       *name, *next_name;\n    ngx_hash_init_t       h;\n    ngx_hash_wildcard_t  *wdc;\n\n    if (ngx_array_init(&curr_names, hinit->temp_pool, nelts,\n                       sizeof(ngx_hash_key_t))\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    if (ngx_array_init(&next_names, hinit->temp_pool, nelts,\n                       sizeof(ngx_hash_key_t))\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    for (n = 0; n < nelts; n = i) {\n\n#if 0\n        ngx_log_error(NGX_LOG_ALERT, hinit->pool->log, 0,\n                      \"wc0: \\\"%V\\\"\", &names[n].key);\n#endif\n\n        dot = 0;\n\n        for (len = 0; len < names[n].key.len; len++) {\n            if (names[n].key.data[len] == '.') {\n                dot = 1;\n                break;\n            }\n        }\n\n        name = ngx_array_push(&curr_names);\n        if (name == NULL) {\n            return NGX_ERROR;\n        }\n\n        name->key.len = len;\n        name->key.data = names[n].key.data;\n        name->key_hash = hinit->key(name->key.data, name->key.len);\n        name->value = names[n].value;\n\n#if 0\n        ngx_log_error(NGX_LOG_ALERT, hinit->pool->log, 0,\n                      \"wc1: \\\"%V\\\" %ui\", &name->key, dot);\n#endif\n\n        dot_len = len + 1;\n\n        if (dot) {\n            len++;\n        }\n\n        next_names.nelts = 0;\n\n        if (names[n].key.len != len) {\n            next_name = ngx_array_push(&next_names);\n            if (next_name == NULL) {\n                return NGX_ERROR;\n            }\n\n            next_name->key.len = names[n].key.len - len;\n            next_name->key.data = names[n].key.data + len;\n            next_name->key_hash = 0;\n            next_name->value = names[n].value;\n\n#if 0\n            ngx_log_error(NGX_LOG_ALERT, hinit->pool->log, 0,\n                          \"wc2: \\\"%V\\\"\", &next_name->key);\n#endif\n        }\n\n        for (i = n + 1; i < nelts; i++) {\n            if (ngx_strncmp(names[n].key.data, names[i].key.data, len) != 0) {\n                break;\n            }\n\n            if (!dot\n                && names[i].key.len > len\n                && names[i].key.data[len] != '.')\n            {\n                break;\n            }\n\n            next_name = ngx_array_push(&next_names);\n            if (next_name == NULL) {\n                return NGX_ERROR;\n            }\n\n            next_name->key.len = names[i].key.len - dot_len;\n            next_name->key.data = names[i].key.data + dot_len;\n            next_name->key_hash = 0;\n            next_name->value = names[i].value;\n\n#if 0\n            ngx_log_error(NGX_LOG_ALERT, hinit->pool->log, 0,\n                          \"wc3: \\\"%V\\\"\", &next_name->key);\n#endif\n        }\n\n        if (next_names.nelts) {\n\n            h = *hinit;\n            h.hash = NULL;\n\n            if (ngx_hash_wildcard_init(&h, (ngx_hash_key_t *) next_names.elts,\n                                       next_names.nelts)\n                != NGX_OK)\n            {\n                return NGX_ERROR;\n            }\n\n            wdc = (ngx_hash_wildcard_t *) h.hash;\n\n            if (names[n].key.len == len) {\n                wdc->value = names[n].value;\n            }\n\n            name->value = (void *) ((uintptr_t) wdc | (dot ? 3 : 2));\n\n        } else if (dot) {\n            name->value = (void *) ((uintptr_t) name->value | 1);\n        }\n    }\n\n    if (ngx_hash_init(hinit, (ngx_hash_key_t *) curr_names.elts,\n                      curr_names.nelts)\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nngx_uint_t\nngx_hash_key(u_char *data, size_t len)\n{\n    ngx_uint_t  i, key;\n\n    key = 0;\n\n    for (i = 0; i < len; i++) {\n        key = ngx_hash(key, data[i]);\n    }\n\n    return key;\n}\n\n\nngx_uint_t\nngx_hash_key_lc(u_char *data, size_t len)\n{\n    ngx_uint_t  i, key;\n\n    key = 0;\n\n    for (i = 0; i < len; i++) {\n        key = ngx_hash(key, ngx_tolower(data[i]));\n    }\n\n    return key;\n}\n\n\nngx_uint_t\nngx_hash_strlow(u_char *dst, u_char *src, size_t n)\n{\n    ngx_uint_t  key;\n\n    key = 0;\n\n    while (n--) {\n        *dst = ngx_tolower(*src);\n        key = ngx_hash(key, *dst);\n        dst++;\n        src++;\n    }\n\n    return key;\n}\n\n\nngx_int_t\nngx_hash_keys_array_init(ngx_hash_keys_arrays_t *ha, ngx_uint_t type)\n{\n    ngx_uint_t  asize;\n\n    if (type == NGX_HASH_SMALL) {\n        asize = 4;\n        ha->hsize = 107;\n\n    } else {\n        asize = NGX_HASH_LARGE_ASIZE;\n        ha->hsize = NGX_HASH_LARGE_HSIZE;\n    }\n\n    if (ngx_array_init(&ha->keys, ha->temp_pool, asize, sizeof(ngx_hash_key_t))\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    if (ngx_array_init(&ha->dns_wc_head, ha->temp_pool, asize,\n                       sizeof(ngx_hash_key_t))\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    if (ngx_array_init(&ha->dns_wc_tail, ha->temp_pool, asize,\n                       sizeof(ngx_hash_key_t))\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    ha->keys_hash = ngx_pcalloc(ha->temp_pool, sizeof(ngx_array_t) * ha->hsize);\n    if (ha->keys_hash == NULL) {\n        return NGX_ERROR;\n    }\n\n    ha->dns_wc_head_hash = ngx_pcalloc(ha->temp_pool,\n                                       sizeof(ngx_array_t) * ha->hsize);\n    if (ha->dns_wc_head_hash == NULL) {\n        return NGX_ERROR;\n    }\n\n    ha->dns_wc_tail_hash = ngx_pcalloc(ha->temp_pool,\n                                       sizeof(ngx_array_t) * ha->hsize);\n    if (ha->dns_wc_tail_hash == NULL) {\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_hash_add_key(ngx_hash_keys_arrays_t *ha, ngx_str_t *key, void *value,\n    ngx_uint_t flags)\n{\n    size_t           len;\n    u_char          *p;\n    ngx_str_t       *name;\n    ngx_uint_t       i, k, n, skip, last;\n    ngx_array_t     *keys, *hwc;\n    ngx_hash_key_t  *hk;\n\n    last = key->len;\n\n    if (flags & NGX_HASH_WILDCARD_KEY) {\n\n        /*\n         * supported wildcards:\n         *     \"*.example.com\", \".example.com\", and \"www.example.*\"\n         */\n\n        n = 0;\n\n        for (i = 0; i < key->len; i++) {\n\n            if (key->data[i] == '*') {\n                if (++n > 1) {\n                    return NGX_DECLINED;\n                }\n            }\n\n            if (key->data[i] == '.' && key->data[i + 1] == '.') {\n                return NGX_DECLINED;\n            }\n\n            if (key->data[i] == '\\0') {\n                return NGX_DECLINED;\n            }\n        }\n\n        if (key->len > 1 && key->data[0] == '.') {\n            skip = 1;\n            goto wildcard;\n        }\n\n        if (key->len > 2) {\n\n            if (key->data[0] == '*' && key->data[1] == '.') {\n                skip = 2;\n                goto wildcard;\n            }\n\n            if (key->data[i - 2] == '.' && key->data[i - 1] == '*') {\n                skip = 0;\n                last -= 2;\n                goto wildcard;\n            }\n        }\n\n        if (n) {\n            return NGX_DECLINED;\n        }\n    }\n\n    /* exact hash */\n\n    k = 0;\n\n    for (i = 0; i < last; i++) {\n        if (!(flags & NGX_HASH_READONLY_KEY)) {\n            key->data[i] = ngx_tolower(key->data[i]);\n        }\n        k = ngx_hash(k, key->data[i]);\n    }\n\n    k %= ha->hsize;\n\n    /* check conflicts in exact hash */\n\n    name = ha->keys_hash[k].elts;\n\n    if (name) {\n        for (i = 0; i < ha->keys_hash[k].nelts; i++) {\n            if (last != name[i].len) {\n                continue;\n            }\n\n            if (ngx_strncmp(key->data, name[i].data, last) == 0) {\n                return NGX_BUSY;\n            }\n        }\n\n    } else {\n        if (ngx_array_init(&ha->keys_hash[k], ha->temp_pool, 4,\n                           sizeof(ngx_str_t))\n            != NGX_OK)\n        {\n            return NGX_ERROR;\n        }\n    }\n\n    name = ngx_array_push(&ha->keys_hash[k]);\n    if (name == NULL) {\n        return NGX_ERROR;\n    }\n\n    *name = *key;\n\n    hk = ngx_array_push(&ha->keys);\n    if (hk == NULL) {\n        return NGX_ERROR;\n    }\n\n    hk->key = *key;\n    hk->key_hash = ngx_hash_key(key->data, last);\n    hk->value = value;\n\n    return NGX_OK;\n\n\nwildcard:\n\n    /* wildcard hash */\n\n    k = ngx_hash_strlow(&key->data[skip], &key->data[skip], last - skip);\n\n    k %= ha->hsize;\n\n    if (skip == 1) {\n\n        /* check conflicts in exact hash for \".example.com\" */\n\n        name = ha->keys_hash[k].elts;\n\n        if (name) {\n            len = last - skip;\n\n            for (i = 0; i < ha->keys_hash[k].nelts; i++) {\n                if (len != name[i].len) {\n                    continue;\n                }\n\n                if (ngx_strncmp(&key->data[1], name[i].data, len) == 0) {\n                    return NGX_BUSY;\n                }\n            }\n\n        } else {\n            if (ngx_array_init(&ha->keys_hash[k], ha->temp_pool, 4,\n                               sizeof(ngx_str_t))\n                != NGX_OK)\n            {\n                return NGX_ERROR;\n            }\n        }\n\n        name = ngx_array_push(&ha->keys_hash[k]);\n        if (name == NULL) {\n            return NGX_ERROR;\n        }\n\n        name->len = last - 1;\n        name->data = ngx_pnalloc(ha->temp_pool, name->len);\n        if (name->data == NULL) {\n            return NGX_ERROR;\n        }\n\n        ngx_memcpy(name->data, &key->data[1], name->len);\n    }\n\n\n    if (skip) {\n\n        /*\n         * convert \"*.example.com\" to \"com.example.\\0\"\n         *      and \".example.com\" to \"com.example\\0\"\n         */\n\n        p = ngx_pnalloc(ha->temp_pool, last);\n        if (p == NULL) {\n            return NGX_ERROR;\n        }\n\n        len = 0;\n        n = 0;\n\n        for (i = last - 1; i; i--) {\n            if (key->data[i] == '.') {\n                ngx_memcpy(&p[n], &key->data[i + 1], len);\n                n += len;\n                p[n++] = '.';\n                len = 0;\n                continue;\n            }\n\n            len++;\n        }\n\n        if (len) {\n            ngx_memcpy(&p[n], &key->data[1], len);\n            n += len;\n        }\n\n        p[n] = '\\0';\n\n        hwc = &ha->dns_wc_head;\n        keys = &ha->dns_wc_head_hash[k];\n\n    } else {\n\n        /* convert \"www.example.*\" to \"www.example\\0\" */\n\n        last++;\n\n        p = ngx_pnalloc(ha->temp_pool, last);\n        if (p == NULL) {\n            return NGX_ERROR;\n        }\n\n        ngx_cpystrn(p, key->data, last);\n\n        hwc = &ha->dns_wc_tail;\n        keys = &ha->dns_wc_tail_hash[k];\n    }\n\n\n    /* check conflicts in wildcard hash */\n\n    name = keys->elts;\n\n    if (name) {\n        len = last - skip;\n\n        for (i = 0; i < keys->nelts; i++) {\n            if (len != name[i].len) {\n                continue;\n            }\n\n            if (ngx_strncmp(key->data + skip, name[i].data, len) == 0) {\n                return NGX_BUSY;\n            }\n        }\n\n    } else {\n        if (ngx_array_init(keys, ha->temp_pool, 4, sizeof(ngx_str_t)) != NGX_OK)\n        {\n            return NGX_ERROR;\n        }\n    }\n\n    name = ngx_array_push(keys);\n    if (name == NULL) {\n        return NGX_ERROR;\n    }\n\n    name->len = last - skip;\n    name->data = ngx_pnalloc(ha->temp_pool, name->len);\n    if (name->data == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_memcpy(name->data, key->data + skip, name->len);\n\n\n    /* add to wildcard hash */\n\n    hk = ngx_array_push(hwc);\n    if (hk == NULL) {\n        return NGX_ERROR;\n    }\n\n    hk->key.len = last - 1;\n    hk->key.data = p;\n    hk->key_hash = 0;\n    hk->value = value;\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "src/core/ngx_hash.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_HASH_H_INCLUDED_\n#define _NGX_HASH_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\ntypedef struct {\n    void             *value;\n    u_short           len;\n    u_char            name[1];\n} ngx_hash_elt_t;\n\n\ntypedef struct {\n    ngx_hash_elt_t  **buckets;\n    ngx_uint_t        size;\n} ngx_hash_t;\n\n\ntypedef struct {\n    ngx_hash_t        hash;\n    void             *value;\n} ngx_hash_wildcard_t;\n\n\ntypedef struct {\n    ngx_str_t         key;\n    ngx_uint_t        key_hash;\n    void             *value;\n} ngx_hash_key_t;\n\n\ntypedef ngx_uint_t (*ngx_hash_key_pt) (u_char *data, size_t len);\n\n\ntypedef struct {\n    ngx_hash_t            hash;\n    ngx_hash_wildcard_t  *wc_head;\n    ngx_hash_wildcard_t  *wc_tail;\n} ngx_hash_combined_t;\n\n\ntypedef struct {\n    ngx_hash_t       *hash;\n    ngx_hash_key_pt   key;\n\n    ngx_uint_t        max_size;\n    ngx_uint_t        bucket_size;\n\n    char             *name;\n    ngx_pool_t       *pool;\n    ngx_pool_t       *temp_pool;\n} ngx_hash_init_t;\n\n\n#define NGX_HASH_SMALL            1\n#define NGX_HASH_LARGE            2\n\n#define NGX_HASH_LARGE_ASIZE      16384\n#define NGX_HASH_LARGE_HSIZE      10007\n\n#define NGX_HASH_WILDCARD_KEY     1\n#define NGX_HASH_READONLY_KEY     2\n\n\ntypedef struct {\n    ngx_uint_t        hsize;\n\n    ngx_pool_t       *pool;\n    ngx_pool_t       *temp_pool;\n\n    ngx_array_t       keys;\n    ngx_array_t      *keys_hash;\n\n    ngx_array_t       dns_wc_head;\n    ngx_array_t      *dns_wc_head_hash;\n\n    ngx_array_t       dns_wc_tail;\n    ngx_array_t      *dns_wc_tail_hash;\n} ngx_hash_keys_arrays_t;\n\n\ntypedef struct ngx_table_elt_s  ngx_table_elt_t;\n\nstruct ngx_table_elt_s {\n    ngx_uint_t        hash;\n    ngx_str_t         key;\n    ngx_str_t         value;\n    u_char           *lowcase_key;\n    ngx_table_elt_t  *next;\n};\n\n\nvoid *ngx_hash_find(ngx_hash_t *hash, ngx_uint_t key, u_char *name, size_t len);\nvoid *ngx_hash_find_wc_head(ngx_hash_wildcard_t *hwc, u_char *name, size_t len);\nvoid *ngx_hash_find_wc_tail(ngx_hash_wildcard_t *hwc, u_char *name, size_t len);\nvoid *ngx_hash_find_combined(ngx_hash_combined_t *hash, ngx_uint_t key,\n    u_char *name, size_t len);\n\nngx_int_t ngx_hash_init(ngx_hash_init_t *hinit, ngx_hash_key_t *names,\n    ngx_uint_t nelts);\nngx_int_t ngx_hash_wildcard_init(ngx_hash_init_t *hinit, ngx_hash_key_t *names,\n    ngx_uint_t nelts);\n\n#define ngx_hash(key, c)   ((ngx_uint_t) key * 31 + c)\nngx_uint_t ngx_hash_key(u_char *data, size_t len);\nngx_uint_t ngx_hash_key_lc(u_char *data, size_t len);\nngx_uint_t ngx_hash_strlow(u_char *dst, u_char *src, size_t n);\n\n\nngx_int_t ngx_hash_keys_array_init(ngx_hash_keys_arrays_t *ha, ngx_uint_t type);\nngx_int_t ngx_hash_add_key(ngx_hash_keys_arrays_t *ha, ngx_str_t *key,\n    void *value, ngx_uint_t flags);\n\n\n#endif /* _NGX_HASH_H_INCLUDED_ */\n"
  },
  {
    "path": "src/core/ngx_inet.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\nstatic ngx_int_t ngx_parse_unix_domain_url(ngx_pool_t *pool, ngx_url_t *u);\nstatic ngx_int_t ngx_parse_inet_url(ngx_pool_t *pool, ngx_url_t *u);\nstatic ngx_int_t ngx_parse_inet6_url(ngx_pool_t *pool, ngx_url_t *u);\nstatic ngx_int_t ngx_inet_add_addr(ngx_pool_t *pool, ngx_url_t *u,\n    struct sockaddr *sockaddr, socklen_t socklen, ngx_uint_t total);\n\n#if (T_NGX_DNS_RESOLVE_BACKUP)\nstatic ngx_int_t ngx_resolve_backup(ngx_pool_t *pool, ngx_url_t **u,\n    ngx_str_t path);\nstatic ngx_int_t ngx_resolve_using_local(ngx_pool_t *pool, ngx_url_t **u,\n    ngx_str_t path);\n#endif\n\n\n\nin_addr_t\nngx_inet_addr(u_char *text, size_t len)\n{\n    u_char      *p, c;\n    in_addr_t    addr;\n    ngx_uint_t   octet, n;\n\n    addr = 0;\n    octet = 0;\n    n = 0;\n\n    for (p = text; p < text + len; p++) {\n        c = *p;\n\n        if (c >= '0' && c <= '9') {\n            octet = octet * 10 + (c - '0');\n\n            if (octet > 255) {\n                return INADDR_NONE;\n            }\n\n            continue;\n        }\n\n        if (c == '.') {\n            addr = (addr << 8) + octet;\n            octet = 0;\n            n++;\n            continue;\n        }\n\n        return INADDR_NONE;\n    }\n\n    if (n == 3) {\n        addr = (addr << 8) + octet;\n        return htonl(addr);\n    }\n\n    return INADDR_NONE;\n}\n\n\n#if (NGX_HAVE_INET6)\n\nngx_int_t\nngx_inet6_addr(u_char *p, size_t len, u_char *addr)\n{\n    u_char      c, *zero, *digit, *s, *d;\n    size_t      len4;\n    ngx_uint_t  n, nibbles, word;\n\n    if (len == 0) {\n        return NGX_ERROR;\n    }\n\n    zero = NULL;\n    digit = NULL;\n    len4 = 0;\n    nibbles = 0;\n    word = 0;\n    n = 8;\n\n    if (p[0] == ':') {\n        p++;\n        len--;\n    }\n\n    for (/* void */; len; len--) {\n        c = *p++;\n\n        if (c == ':') {\n            if (nibbles) {\n                digit = p;\n                len4 = len;\n                *addr++ = (u_char) (word >> 8);\n                *addr++ = (u_char) (word & 0xff);\n\n                if (--n) {\n                    nibbles = 0;\n                    word = 0;\n                    continue;\n                }\n\n            } else {\n                if (zero == NULL) {\n                    digit = p;\n                    len4 = len;\n                    zero = addr;\n                    continue;\n                }\n            }\n\n            return NGX_ERROR;\n        }\n\n        if (c == '.' && nibbles) {\n            if (n < 2 || digit == NULL) {\n                return NGX_ERROR;\n            }\n\n            word = ngx_inet_addr(digit, len4 - 1);\n            if (word == INADDR_NONE) {\n                return NGX_ERROR;\n            }\n\n            word = ntohl(word);\n            *addr++ = (u_char) ((word >> 24) & 0xff);\n            *addr++ = (u_char) ((word >> 16) & 0xff);\n            n--;\n            break;\n        }\n\n        if (++nibbles > 4) {\n            return NGX_ERROR;\n        }\n\n        if (c >= '0' && c <= '9') {\n            word = word * 16 + (c - '0');\n            continue;\n        }\n\n        c |= 0x20;\n\n        if (c >= 'a' && c <= 'f') {\n            word = word * 16 + (c - 'a') + 10;\n            continue;\n        }\n\n        return NGX_ERROR;\n    }\n\n    if (nibbles == 0 && zero == NULL) {\n        return NGX_ERROR;\n    }\n\n    *addr++ = (u_char) (word >> 8);\n    *addr++ = (u_char) (word & 0xff);\n\n    if (--n) {\n        if (zero) {\n            n *= 2;\n            s = addr - 1;\n            d = s + n;\n            while (s >= zero) {\n                *d-- = *s--;\n            }\n            ngx_memzero(zero, n);\n            return NGX_OK;\n        }\n\n    } else {\n        if (zero == NULL) {\n            return NGX_OK;\n        }\n    }\n\n    return NGX_ERROR;\n}\n\n#endif\n\n\nsize_t\nngx_sock_ntop(struct sockaddr *sa, socklen_t socklen, u_char *text, size_t len,\n    ngx_uint_t port)\n{\n    u_char               *p;\n#if (NGX_HAVE_INET6 || NGX_HAVE_UNIX_DOMAIN)\n    size_t                n;\n#endif\n    struct sockaddr_in   *sin;\n#if (NGX_HAVE_INET6)\n    struct sockaddr_in6  *sin6;\n#endif\n#if (NGX_HAVE_UNIX_DOMAIN)\n    struct sockaddr_un   *saun;\n#endif\n\n    switch (sa->sa_family) {\n\n    case AF_INET:\n\n        sin = (struct sockaddr_in *) sa;\n        p = (u_char *) &sin->sin_addr;\n\n        if (port) {\n            p = ngx_snprintf(text, len, \"%ud.%ud.%ud.%ud:%d\",\n                             p[0], p[1], p[2], p[3], ntohs(sin->sin_port));\n        } else {\n            p = ngx_snprintf(text, len, \"%ud.%ud.%ud.%ud\",\n                             p[0], p[1], p[2], p[3]);\n        }\n\n        return (p - text);\n\n#if (NGX_HAVE_INET6)\n\n    case AF_INET6:\n\n        sin6 = (struct sockaddr_in6 *) sa;\n\n        n = 0;\n\n        if (port) {\n            text[n++] = '[';\n        }\n\n        n = ngx_inet6_ntop(sin6->sin6_addr.s6_addr, &text[n], len);\n\n        if (port) {\n            n = ngx_sprintf(&text[1 + n], \"]:%d\",\n                            ntohs(sin6->sin6_port)) - text;\n        }\n\n        return n;\n#endif\n\n#if (NGX_HAVE_UNIX_DOMAIN)\n\n    case AF_UNIX:\n        saun = (struct sockaddr_un *) sa;\n\n        /* on Linux sockaddr might not include sun_path at all */\n\n        if (socklen <= (socklen_t) offsetof(struct sockaddr_un, sun_path)) {\n            p = ngx_snprintf(text, len, \"unix:%Z\");\n\n        } else {\n            n = ngx_strnlen((u_char *) saun->sun_path,\n                            socklen - offsetof(struct sockaddr_un, sun_path));\n            p = ngx_snprintf(text, len, \"unix:%*s%Z\", n, saun->sun_path);\n        }\n\n        /* we do not include trailing zero in address length */\n\n        return (p - text - 1);\n\n#endif\n\n    default:\n        return 0;\n    }\n}\n\n\nsize_t\nngx_inet_ntop(int family, void *addr, u_char *text, size_t len)\n{\n    u_char  *p;\n\n    switch (family) {\n\n    case AF_INET:\n\n        p = addr;\n\n        return ngx_snprintf(text, len, \"%ud.%ud.%ud.%ud\",\n                            p[0], p[1], p[2], p[3])\n               - text;\n\n#if (NGX_HAVE_INET6)\n\n    case AF_INET6:\n        return ngx_inet6_ntop(addr, text, len);\n\n#endif\n\n    default:\n        return 0;\n    }\n}\n\n\n#if (NGX_HAVE_INET6)\n\nsize_t\nngx_inet6_ntop(u_char *p, u_char *text, size_t len)\n{\n    u_char      *dst;\n    size_t       max, n;\n    ngx_uint_t   i, zero, last;\n\n    if (len < NGX_INET6_ADDRSTRLEN) {\n        return 0;\n    }\n\n    zero = (ngx_uint_t) -1;\n    last = (ngx_uint_t) -1;\n    max = 1;\n    n = 0;\n\n    for (i = 0; i < 16; i += 2) {\n\n        if (p[i] || p[i + 1]) {\n\n            if (max < n) {\n                zero = last;\n                max = n;\n            }\n\n            n = 0;\n            continue;\n        }\n\n        if (n++ == 0) {\n            last = i;\n        }\n    }\n\n    if (max < n) {\n        zero = last;\n        max = n;\n    }\n\n    dst = text;\n    n = 16;\n\n    if (zero == 0) {\n\n        if ((max == 5 && p[10] == 0xff && p[11] == 0xff)\n            || (max == 6)\n            || (max == 7 && p[14] != 0 && p[15] != 1))\n        {\n            n = 12;\n        }\n\n        *dst++ = ':';\n    }\n\n    for (i = 0; i < n; i += 2) {\n\n        if (i == zero) {\n            *dst++ = ':';\n            i += (max - 1) * 2;\n            continue;\n        }\n\n        dst = ngx_sprintf(dst, \"%xd\", p[i] * 256 + p[i + 1]);\n\n        if (i < 14) {\n            *dst++ = ':';\n        }\n    }\n\n    if (n == 12) {\n        dst = ngx_sprintf(dst, \"%ud.%ud.%ud.%ud\", p[12], p[13], p[14], p[15]);\n    }\n\n    return dst - text;\n}\n\n#endif\n\n\nngx_int_t\nngx_ptocidr(ngx_str_t *text, ngx_cidr_t *cidr)\n{\n    u_char      *addr, *mask, *last;\n    size_t       len;\n    ngx_int_t    shift;\n#if (NGX_HAVE_INET6)\n    ngx_int_t    rc;\n    ngx_uint_t   s, i;\n#endif\n\n    addr = text->data;\n    last = addr + text->len;\n\n    mask = ngx_strlchr(addr, last, '/');\n    len = (mask ? mask : last) - addr;\n\n    cidr->u.in.addr = ngx_inet_addr(addr, len);\n\n    if (cidr->u.in.addr != INADDR_NONE) {\n        cidr->family = AF_INET;\n\n        if (mask == NULL) {\n            cidr->u.in.mask = 0xffffffff;\n            return NGX_OK;\n        }\n\n#if (NGX_HAVE_INET6)\n    } else if (ngx_inet6_addr(addr, len, cidr->u.in6.addr.s6_addr) == NGX_OK) {\n        cidr->family = AF_INET6;\n\n        if (mask == NULL) {\n            ngx_memset(cidr->u.in6.mask.s6_addr, 0xff, 16);\n            return NGX_OK;\n        }\n\n#endif\n    } else {\n        return NGX_ERROR;\n    }\n\n    mask++;\n\n    shift = ngx_atoi(mask, last - mask);\n    if (shift == NGX_ERROR) {\n        return NGX_ERROR;\n    }\n\n    switch (cidr->family) {\n\n#if (NGX_HAVE_INET6)\n    case AF_INET6:\n        if (shift > 128) {\n            return NGX_ERROR;\n        }\n\n        addr = cidr->u.in6.addr.s6_addr;\n        mask = cidr->u.in6.mask.s6_addr;\n        rc = NGX_OK;\n\n        for (i = 0; i < 16; i++) {\n\n            s = (shift > 8) ? 8 : shift;\n            shift -= s;\n\n            mask[i] = (u_char) (0xffu << (8 - s));\n\n            if (addr[i] != (addr[i] & mask[i])) {\n                rc = NGX_DONE;\n                addr[i] &= mask[i];\n            }\n        }\n\n        return rc;\n#endif\n\n    default: /* AF_INET */\n        if (shift > 32) {\n            return NGX_ERROR;\n        }\n\n        if (shift) {\n            cidr->u.in.mask = htonl((uint32_t) (0xffffffffu << (32 - shift)));\n\n        } else {\n            /* x86 compilers use a shl instruction that shifts by modulo 32 */\n            cidr->u.in.mask = 0;\n        }\n\n        if (cidr->u.in.addr == (cidr->u.in.addr & cidr->u.in.mask)) {\n            return NGX_OK;\n        }\n\n        cidr->u.in.addr &= cidr->u.in.mask;\n\n        return NGX_DONE;\n    }\n}\n\n\nngx_int_t\nngx_cidr_match(struct sockaddr *sa, ngx_array_t *cidrs)\n{\n#if (NGX_HAVE_INET6)\n    u_char           *p;\n#endif\n    in_addr_t         inaddr;\n    ngx_cidr_t       *cidr;\n    ngx_uint_t        family, i;\n#if (NGX_HAVE_INET6)\n    ngx_uint_t        n;\n    struct in6_addr  *inaddr6;\n#endif\n\n#if (NGX_SUPPRESS_WARN)\n    inaddr = 0;\n#if (NGX_HAVE_INET6)\n    inaddr6 = NULL;\n#endif\n#endif\n\n    family = sa->sa_family;\n\n    if (family == AF_INET) {\n        inaddr = ((struct sockaddr_in *) sa)->sin_addr.s_addr;\n    }\n\n#if (NGX_HAVE_INET6)\n    else if (family == AF_INET6) {\n        inaddr6 = &((struct sockaddr_in6 *) sa)->sin6_addr;\n\n        if (IN6_IS_ADDR_V4MAPPED(inaddr6)) {\n            family = AF_INET;\n\n            p = inaddr6->s6_addr;\n\n            inaddr = p[12] << 24;\n            inaddr += p[13] << 16;\n            inaddr += p[14] << 8;\n            inaddr += p[15];\n\n            inaddr = htonl(inaddr);\n        }\n    }\n#endif\n\n    for (cidr = cidrs->elts, i = 0; i < cidrs->nelts; i++) {\n        if (cidr[i].family != family) {\n            goto next;\n        }\n\n        switch (family) {\n\n#if (NGX_HAVE_INET6)\n        case AF_INET6:\n            for (n = 0; n < 16; n++) {\n                if ((inaddr6->s6_addr[n] & cidr[i].u.in6.mask.s6_addr[n])\n                    != cidr[i].u.in6.addr.s6_addr[n])\n                {\n                    goto next;\n                }\n            }\n            break;\n#endif\n\n#if (NGX_HAVE_UNIX_DOMAIN)\n        case AF_UNIX:\n            break;\n#endif\n\n        default: /* AF_INET */\n            if ((inaddr & cidr[i].u.in.mask) != cidr[i].u.in.addr) {\n                goto next;\n            }\n            break;\n        }\n\n        return NGX_OK;\n\n    next:\n        continue;\n    }\n\n    return NGX_DECLINED;\n}\n\n\nngx_int_t\nngx_parse_addr(ngx_pool_t *pool, ngx_addr_t *addr, u_char *text, size_t len)\n{\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        len = sizeof(struct sockaddr_in);\n\n#if (NGX_HAVE_INET6)\n    } else if (ngx_inet6_addr(text, len, inaddr6.s6_addr) == NGX_OK) {\n        family = AF_INET6;\n        len = sizeof(struct sockaddr_in6);\n\n#endif\n    } else {\n        return NGX_DECLINED;\n    }\n\n    addr->sockaddr = ngx_pcalloc(pool, len);\n    if (addr->sockaddr == NULL) {\n        return NGX_ERROR;\n    }\n\n    addr->sockaddr->sa_family = (u_char) family;\n    addr->socklen = len;\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    return NGX_OK;\n}\n\n\nngx_int_t\nngx_parse_addr_port(ngx_pool_t *pool, ngx_addr_t *addr, u_char *text,\n    size_t len)\n{\n    u_char     *p, *last;\n    size_t      plen;\n    ngx_int_t   rc, port;\n\n    rc = ngx_parse_addr(pool, addr, text, len);\n\n    if (rc != NGX_DECLINED) {\n        return rc;\n    }\n\n    last = text + len;\n\n#if (NGX_HAVE_INET6)\n    if (len && text[0] == '[') {\n\n        p = ngx_strlchr(text, last, ']');\n\n        if (p == NULL || p == last - 1 || *++p != ':') {\n            return NGX_DECLINED;\n        }\n\n        text++;\n        len -= 2;\n\n    } else\n#endif\n\n    {\n        p = ngx_strlchr(text, last, ':');\n\n        if (p == NULL) {\n            return NGX_DECLINED;\n        }\n    }\n\n    p++;\n    plen = last - p;\n\n    port = ngx_atoi(p, plen);\n\n    if (port < 1 || port > 65535) {\n        return NGX_DECLINED;\n    }\n\n    len -= plen + 1;\n\n    rc = ngx_parse_addr(pool, addr, text, len);\n\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    ngx_inet_set_port(addr->sockaddr, (in_port_t) port);\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_parse_url(ngx_pool_t *pool, ngx_url_t *u)\n{\n    u_char  *p;\n    size_t   len;\n\n    p = u->url.data;\n    len = u->url.len;\n\n    if (len >= 5 && ngx_strncasecmp(p, (u_char *) \"unix:\", 5) == 0) {\n        return ngx_parse_unix_domain_url(pool, u);\n    }\n\n    if (len && p[0] == '[') {\n        return ngx_parse_inet6_url(pool, u);\n    }\n\n    return ngx_parse_inet_url(pool, u);\n}\n\n\nstatic ngx_int_t\nngx_parse_unix_domain_url(ngx_pool_t *pool, ngx_url_t *u)\n{\n#if (NGX_HAVE_UNIX_DOMAIN)\n    u_char              *path, *uri, *last;\n    size_t               len;\n    struct sockaddr_un  *saun;\n\n    len = u->url.len;\n    path = u->url.data;\n\n    path += 5;\n    len -= 5;\n\n    if (u->uri_part) {\n\n        last = path + len;\n        uri = ngx_strlchr(path, last, ':');\n\n        if (uri) {\n            len = uri - path;\n            uri++;\n            u->uri.len = last - uri;\n            u->uri.data = uri;\n        }\n    }\n\n    if (len == 0) {\n        u->err = \"no path in the unix domain socket\";\n        return NGX_ERROR;\n    }\n\n    u->host.len = len++;\n    u->host.data = path;\n\n    if (len > sizeof(saun->sun_path)) {\n        u->err = \"too long path in the unix domain socket\";\n        return NGX_ERROR;\n    }\n\n    u->socklen = sizeof(struct sockaddr_un);\n    saun = (struct sockaddr_un *) &u->sockaddr;\n    saun->sun_family = AF_UNIX;\n    (void) ngx_cpystrn((u_char *) saun->sun_path, path, len);\n\n    u->addrs = ngx_pcalloc(pool, sizeof(ngx_addr_t));\n    if (u->addrs == NULL) {\n        return NGX_ERROR;\n    }\n\n    saun = ngx_pcalloc(pool, sizeof(struct sockaddr_un));\n    if (saun == NULL) {\n        return NGX_ERROR;\n    }\n\n    u->family = AF_UNIX;\n    u->naddrs = 1;\n\n    saun->sun_family = AF_UNIX;\n    (void) ngx_cpystrn((u_char *) saun->sun_path, path, len);\n\n    u->addrs[0].sockaddr = (struct sockaddr *) saun;\n    u->addrs[0].socklen = sizeof(struct sockaddr_un);\n    u->addrs[0].name.len = len + 4;\n    u->addrs[0].name.data = u->url.data;\n\n    return NGX_OK;\n\n#else\n\n    u->err = \"the unix domain sockets are not supported on this platform\";\n\n    return NGX_ERROR;\n\n#endif\n}\n\n\nstatic ngx_int_t\nngx_parse_inet_url(ngx_pool_t *pool, ngx_url_t *u)\n{\n    u_char              *host, *port, *last, *uri, *args, *dash;\n    size_t               len;\n    ngx_int_t            n;\n    struct sockaddr_in  *sin;\n\n    u->socklen = sizeof(struct sockaddr_in);\n    sin = (struct sockaddr_in *) &u->sockaddr;\n    sin->sin_family = AF_INET;\n\n    u->family = AF_INET;\n\n    host = u->url.data;\n\n    last = host + u->url.len;\n\n    port = ngx_strlchr(host, last, ':');\n\n    uri = ngx_strlchr(host, last, '/');\n\n    args = ngx_strlchr(host, last, '?');\n\n    if (args) {\n        if (uri == NULL || args < uri) {\n            uri = args;\n        }\n    }\n\n    if (uri) {\n        if (u->listen || !u->uri_part) {\n            u->err = \"invalid host\";\n            return NGX_ERROR;\n        }\n\n        u->uri.len = last - uri;\n        u->uri.data = uri;\n\n        last = uri;\n\n        if (uri < port) {\n            port = NULL;\n        }\n    }\n\n    if (port) {\n        port++;\n\n        len = last - port;\n\n        if (u->listen) {\n            dash = ngx_strlchr(port, last, '-');\n\n            if (dash) {\n                dash++;\n\n                n = ngx_atoi(dash, last - dash);\n\n                if (n < 1 || n > 65535) {\n                    u->err = \"invalid port\";\n                    return NGX_ERROR;\n                }\n\n                u->last_port = (in_port_t) n;\n\n                len = dash - port - 1;\n            }\n        }\n\n        n = ngx_atoi(port, len);\n\n        if (n < 1 || n > 65535) {\n            u->err = \"invalid port\";\n            return NGX_ERROR;\n        }\n\n        if (u->last_port && n > u->last_port) {\n            u->err = \"invalid port range\";\n            return NGX_ERROR;\n        }\n\n        u->port = (in_port_t) n;\n        sin->sin_port = htons((in_port_t) n);\n\n        u->port_text.len = last - port;\n        u->port_text.data = port;\n\n        last = port - 1;\n\n    } else {\n        if (uri == NULL) {\n\n            if (u->listen) {\n\n                /* test value as port only */\n\n                len = last - host;\n\n                dash = ngx_strlchr(host, last, '-');\n\n                if (dash) {\n                    dash++;\n\n                    n = ngx_atoi(dash, last - dash);\n\n                    if (n == NGX_ERROR) {\n                        goto no_port;\n                    }\n\n                    if (n < 1 || n > 65535) {\n                        u->err = \"invalid port\";\n\n                    } else {\n                        u->last_port = (in_port_t) n;\n                    }\n\n                    len = dash - host - 1;\n                }\n\n                n = ngx_atoi(host, len);\n\n                if (n != NGX_ERROR) {\n\n                    if (u->err) {\n                        return NGX_ERROR;\n                    }\n\n                    if (n < 1 || n > 65535) {\n                        u->err = \"invalid port\";\n                        return NGX_ERROR;\n                    }\n\n                    if (u->last_port && n > u->last_port) {\n                        u->err = \"invalid port range\";\n                        return NGX_ERROR;\n                    }\n\n                    u->port = (in_port_t) n;\n                    sin->sin_port = htons((in_port_t) n);\n                    sin->sin_addr.s_addr = INADDR_ANY;\n\n                    u->port_text.len = last - host;\n                    u->port_text.data = host;\n\n                    u->wildcard = 1;\n\n                    return ngx_inet_add_addr(pool, u, &u->sockaddr.sockaddr,\n                                             u->socklen, 1);\n                }\n            }\n        }\n\nno_port:\n\n        u->err = NULL;\n        u->no_port = 1;\n        u->port = u->default_port;\n        sin->sin_port = htons(u->default_port);\n        u->last_port = 0;\n    }\n\n    len = last - host;\n\n    if (len == 0) {\n        u->err = \"no host\";\n        return NGX_ERROR;\n    }\n\n    u->host.len = len;\n    u->host.data = host;\n\n    if (u->listen && len == 1 && *host == '*') {\n        sin->sin_addr.s_addr = INADDR_ANY;\n        u->wildcard = 1;\n        return ngx_inet_add_addr(pool, u, &u->sockaddr.sockaddr, u->socklen, 1);\n    }\n\n    sin->sin_addr.s_addr = ngx_inet_addr(host, len);\n\n    if (sin->sin_addr.s_addr != INADDR_NONE) {\n\n        if (sin->sin_addr.s_addr == INADDR_ANY) {\n            u->wildcard = 1;\n        }\n\n        return ngx_inet_add_addr(pool, u, &u->sockaddr.sockaddr, u->socklen, 1);\n    }\n\n    if (u->no_resolve) {\n        return NGX_OK;\n    }\n\n    if (ngx_inet_resolve_host(pool, u) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    u->family = u->addrs[0].sockaddr->sa_family;\n    u->socklen = u->addrs[0].socklen;\n    ngx_memcpy(&u->sockaddr, u->addrs[0].sockaddr, u->addrs[0].socklen);\n    u->wildcard = ngx_inet_wildcard(&u->sockaddr.sockaddr);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_parse_inet6_url(ngx_pool_t *pool, ngx_url_t *u)\n{\n#if (NGX_HAVE_INET6)\n    u_char               *p, *host, *port, *last, *uri, *dash;\n    size_t                len;\n    ngx_int_t             n;\n    struct sockaddr_in6  *sin6;\n\n    u->socklen = sizeof(struct sockaddr_in6);\n    sin6 = (struct sockaddr_in6 *) &u->sockaddr;\n    sin6->sin6_family = AF_INET6;\n\n    host = u->url.data + 1;\n\n    last = u->url.data + u->url.len;\n\n    p = ngx_strlchr(host, last, ']');\n\n    if (p == NULL) {\n        u->err = \"invalid host\";\n        return NGX_ERROR;\n    }\n\n    port = p + 1;\n\n    uri = ngx_strlchr(port, last, '/');\n\n    if (uri) {\n        if (u->listen || !u->uri_part) {\n            u->err = \"invalid host\";\n            return NGX_ERROR;\n        }\n\n        u->uri.len = last - uri;\n        u->uri.data = uri;\n\n        last = uri;\n    }\n\n    if (port < last) {\n        if (*port != ':') {\n            u->err = \"invalid host\";\n            return NGX_ERROR;\n        }\n\n        port++;\n\n        len = last - port;\n\n        if (u->listen) {\n            dash = ngx_strlchr(port, last, '-');\n\n            if (dash) {\n                dash++;\n\n                n = ngx_atoi(dash, last - dash);\n\n                if (n < 1 || n > 65535) {\n                    u->err = \"invalid port\";\n                    return NGX_ERROR;\n                }\n\n                u->last_port = (in_port_t) n;\n\n                len = dash - port - 1;\n            }\n        }\n\n        n = ngx_atoi(port, len);\n\n        if (n < 1 || n > 65535) {\n            u->err = \"invalid port\";\n            return NGX_ERROR;\n        }\n\n        if (u->last_port && n > u->last_port) {\n            u->err = \"invalid port range\";\n            return NGX_ERROR;\n        }\n\n        u->port = (in_port_t) n;\n        sin6->sin6_port = htons((in_port_t) n);\n\n        u->port_text.len = last - port;\n        u->port_text.data = port;\n\n    } else {\n        u->no_port = 1;\n        u->port = u->default_port;\n        sin6->sin6_port = htons(u->default_port);\n    }\n\n    len = p - host;\n\n    if (len == 0) {\n        u->err = \"no host\";\n        return NGX_ERROR;\n    }\n\n    u->host.len = len + 2;\n    u->host.data = host - 1;\n\n    if (ngx_inet6_addr(host, len, sin6->sin6_addr.s6_addr) != NGX_OK) {\n        u->err = \"invalid IPv6 address\";\n        return NGX_ERROR;\n    }\n\n    if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {\n        u->wildcard = 1;\n    }\n\n    u->family = AF_INET6;\n\n    return ngx_inet_add_addr(pool, u, &u->sockaddr.sockaddr, u->socklen, 1);\n\n#else\n\n    u->err = \"the INET6 sockets are not supported on this platform\";\n\n    return NGX_ERROR;\n\n#endif\n}\n\n\n#if (T_NGX_DNS_RESOLVE_BACKUP)\nstatic ngx_int_t\nngx_resolve_backup(ngx_pool_t *pool, ngx_url_t **u, ngx_str_t path)\n{\n    u_char                           buf[70], *p, *pos;\n    size_t                           len;\n    ngx_url_t                       *url;\n    ngx_uint_t                       i;\n    ngx_fd_t                         fd;\n    ngx_int_t                        err;\n    ngx_str_t                        tpathf, dpathf, q;\n    static ngx_int_t                 create_dir_flag = 1;\n\n    url = *u;\n    if (path.data && path.data[path.len - 1] != '/') {\n        p = ngx_pcalloc(pool, path.len + 1);\n        if (p == NULL) {\n            return NGX_ERROR;\n        }\n\n        path.len = ngx_sprintf(p, \"%V/\", &path) - p;\n        path.data = p;\n    }\n\n    if (ngx_conf_full_name((ngx_cycle_t *) ngx_cycle, &path, 0) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    if (create_dir_flag) {\n        err = ngx_create_full_path(path.data, 0755);\n        if (err != 0) {\n            ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno,\n                          ngx_create_dir_n \" \\\"%s\\\" failed\",\n                          path.data);\n            return NGX_ERROR;\n        }\n        create_dir_flag = 0;\n    }\n\n    tpathf.len = path.len + url->host.len + NGX_INT_T_LEN + 2;\n    tpathf.data = ngx_pcalloc(pool, tpathf.len);\n    if (tpathf.data == NULL) {\n        return NGX_ERROR;\n    }\n\n    tpathf.len = ngx_snprintf(tpathf.data, tpathf.len, \"%V%V.%d%Z\", &path,\n                              &url->host, ngx_pid) - tpathf.data;\n\n    dpathf.len = path.len + url->host.len;\n    dpathf.data = ngx_pstrdup(pool, &tpathf);\n    dpathf.data[dpathf.len] = '\\0';\n\n    fd = ngx_open_file(tpathf.data, NGX_FILE_WRONLY,\n                       NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS);\n    if (fd == NGX_INVALID_FILE) {\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno,\n                      ngx_open_file_n \" \\\"%s\\\" failed\",\n                      tpathf.data);\n\n        return NGX_ERROR;\n    }\n\n    for (i = 0; i < url->naddrs; i++) {\n        if (url->addrs[i].sockaddr->sa_family == AF_INET) {\n            pos = ngx_strlchr(url->addrs[i].name.data, url->addrs[i].name.data\n                              + url->addrs[i].name.len, ':');\n            if (pos == NULL) {\n                pos = url->addrs[i].name.data + url->addrs[i].name.len;\n            }\n\n            q.data = url->addrs[i].name.data;\n            q.len = pos - url->addrs[i].name.data;\n\n        } else if (url->addrs[i].sockaddr->sa_family == AF_INET6) {\n            pos = ngx_strlchr(url->addrs[i].name.data, url->addrs[i].name.data\n                              + url->addrs[i].name.len, ']');\n            if (pos == NULL) {\n                continue;\n            }\n            q.data = url->addrs[i].name.data + 1;\n            q.len = pos - q.data;\n        }\n\n        len = ngx_snprintf(buf, 69, \"%V|%d%N\", &q,\n                           url->addrs[i].sockaddr->sa_family) - buf;\n        ngx_write_fd(fd, buf, len);\n    }\n\n    ngx_close_file(fd);\n\n    if (ngx_rename_file(tpathf.data, dpathf.data) == NGX_FILE_ERROR) {\n        ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno,\n                      ngx_rename_file_n\" from \\\"%s\\\" to \\\"%s\\\" failed\",\n                      tpathf.data, dpathf.data);\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_resolve_using_local(ngx_pool_t *pool, ngx_url_t **u, ngx_str_t path)\n{\n    u_char                              *buf, *p, *s, *q, *l;\n    size_t                               len;\n    ngx_uint_t                           i;\n    ngx_str_t                            pf;\n    ngx_fd_t                             fd;\n    ngx_uint_t                           size, ip, sin_family;\n    ngx_file_info_t                      info;\n    ngx_url_t                           *url;\n    struct sockaddr_in                  *sin;\n#if (NGX_HAVE_INET6)\n    struct sockaddr_in6                 *sin6;\n    u_char                              *v6;\n#endif\n\n    url = *u;\n    if (path.data && path.data[path.len - 1] != '/') {\n        p = ngx_pcalloc(pool, path.len + 1);\n        if (p == NULL) {\n            return NGX_ERROR;\n        }\n\n        path.len = ngx_sprintf(p, \"%V/\", &path) - p;\n        path.data = p;\n    }\n\n    pf.len = path.len + url->host.len;\n    pf.data = ngx_pcalloc(pool, pf.len + 1);\n    if (pf.data == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_snprintf(pf.data, pf.len, \"%V%*s\",\n                 &path, url->host.len, url->host.data);\n\n    if (ngx_conf_full_name((ngx_cycle_t *) ngx_cycle, &pf, 0) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    fd = ngx_open_file(pf.data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);\n    if (fd == NGX_INVALID_FILE) {\n        return NGX_ERROR;\n    }\n\n    if (ngx_fd_info(fd, &info) == -1) {\n        ngx_close_file(fd);\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno,\n                      ngx_fd_info_n \" \\\"%V\\\" failed\", &pf);\n        return NGX_ERROR;\n    }\n\n    /* fix: maybe disk full */\n    size = ngx_file_size(&info);\n    if (size == 0) {\n        ngx_close_file(fd);\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                      \"dns : the cache \\\"%V\\\" is empty file\", &pf);\n        return NGX_ERROR;\n    }\n\n    buf = ngx_palloc(pool, size);\n    if (buf == NULL) {\n        ngx_close_file(fd);\n        return NGX_ERROR;\n    }\n\n    if (ngx_read_fd(fd, buf, size) == -1) {\n        ngx_close_file(fd);\n        return NGX_ERROR;\n    }\n\n    ngx_close_file(fd);\n\n    for(i = 0, p = buf; p < buf + size; i++) {\n        s = ngx_strlchr(p, buf + size, '\\n');\n        if (s == NULL) {\n            break;\n        }\n\n        p = s + 1;\n    }\n\n    url->addrs = ngx_pcalloc(pool, i * sizeof(ngx_addr_t));\n    if (url->addrs == NULL) {\n        return NGX_ERROR;\n    }\n\n    url->naddrs = 0;\n\n    for (i = 0, p = buf; p < buf + size; ) {\n        s = ngx_strlchr(p, buf + size, '\\n');\n        if (s == NULL) {\n            s = buf + size + 1;\n        }\n\n        l = ngx_strlchr(p, s, '|');\n        if (l == NULL) {\n            l = s;\n        }\n\n        if (l != s) {\n            sin_family = ngx_atoi(l + 1, s - l - 1);\n            if (sin_family != AF_INET && sin_family != AF_INET6 ) {\n                p = s + 1;\n                continue;\n            }\n\n        } else {\n            p = s + 1;\n            continue;\n        }\n\n        if (sin_family == AF_INET) {\n            ip = ngx_inet_addr(p, l - p);\n            if (ip == INADDR_NONE) {\n                ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                              \"dns: failed to parse ip \\\"%*s\\\"\",\n                              s - p, p);\n                p = s + 1;\n                continue;\n            }\n\n            sin = ngx_pcalloc(pool, sizeof(struct sockaddr_in));\n            if (sin == NULL) {\n                return NGX_ERROR;\n            }\n\n            sin->sin_family = sin_family;\n            sin->sin_port = htons(url->port);\n            sin->sin_addr.s_addr = ip;\n\n            url->addrs[i].sockaddr = (struct sockaddr *) sin;\n            url->addrs[i].socklen = sizeof(struct sockaddr_in);\n\n            len = NGX_INET_ADDRSTRLEN + sizeof(\":65535\") - 1;\n\n            q = ngx_pnalloc(pool, len);\n            if (q == NULL) {\n                return NGX_ERROR;\n            }\n\n            len = ngx_sock_ntop((struct sockaddr *) sin,\n                                sizeof(struct sockaddr_in), q, len, 1);\n\n            url->addrs[i].name.len = len;\n            url->addrs[i].name.data = q;\n            i++;\n\n        } else if (sin_family == AF_INET6) {\n#if (NGX_HAVE_INET6)\n            v6 = p;\n            len = l - p;\n\n            sin6 = ngx_pcalloc(pool, sizeof(struct sockaddr_in6));\n            if (sin6 == NULL) {\n                return NGX_ERROR;\n            }\n\n            if (ngx_inet6_addr(v6, len, sin6->sin6_addr.s6_addr) != NGX_OK) {\n                p = s + 1;\n                continue;\n            }\n\n            sin6->sin6_family = sin_family;\n            sin6->sin6_port = htons(url->port);\n\n            url->addrs[i].sockaddr = (struct sockaddr *) sin6;\n            url->addrs[i].socklen = sizeof(struct sockaddr_in6);\n\n            len = NGX_INET6_ADDRSTRLEN + sizeof(\"[]:65535\") - 1;\n\n            q = ngx_pnalloc(pool, len);\n            if (q == NULL) {\n                return NGX_ERROR;\n            }\n\n            len = ngx_sock_ntop((struct sockaddr *) sin6,\n                                sizeof(struct sockaddr), q, len, 1);\n\n            url->addrs[i].name.len = len;\n            url->addrs[i].name.data = q;\n            i++;\n#endif\n        }\n\n        p = s + 1;\n\n    }\n\n    if (i == 0) {\n        return NGX_ERROR;\n    }\n\n    url->naddrs = i;\n\n    return NGX_OK;\n\n}\n#endif\n\n\n#if (NGX_HAVE_GETADDRINFO && NGX_HAVE_INET6)\n\nngx_int_t\nngx_inet_resolve_host(ngx_pool_t *pool, ngx_url_t *u)\n{\n    u_char           *host;\n    ngx_uint_t        n;\n    struct addrinfo   hints, *res, *rp;\n#if (T_NGX_DNS_RESOLVE_BACKUP)\n    u_char           *ph;\n    ngx_str_t         path;\n#endif\n\n    host = ngx_alloc(u->host.len + 1, pool->log);\n    if (host == NULL) {\n        return NGX_ERROR;\n    }\n\n    (void) ngx_cpystrn(host, u->host.data, u->host.len + 1);\n\n    ngx_memzero(&hints, sizeof(struct addrinfo));\n    hints.ai_family = AF_UNSPEC;\n    hints.ai_socktype = SOCK_STREAM;\n#ifdef AI_ADDRCONFIG\n    hints.ai_flags = AI_ADDRCONFIG;\n#endif\n\n    if (getaddrinfo((char *) host, NULL, &hints, &res) != 0) {\n#if (T_NGX_DNS_RESOLVE_BACKUP)\n        ph = (u_char *) getenv(NGX_DNS_RESOLVE_BACKUP_PATH);\n        if (ph != NULL) {\n            path.data = ph;\n            path.len = ngx_strlen(ph);\n            if (ngx_resolve_using_local(pool, &u, path) == NGX_OK) {\n                ngx_log_error(NGX_LOG_WARN, ngx_cycle->log, 0,\n                              \"dom %V using local dns cache successed\",\n                              &u->host);\n                ngx_free(host);\n                return NGX_OK;\n            }\n        }\n#endif\n        u->err = \"host not found\";\n        ngx_free(host);\n        return NGX_ERROR;\n    }\n\n    ngx_free(host);\n\n    for (n = 0, rp = res; rp != NULL; rp = rp->ai_next) {\n\n        switch (rp->ai_family) {\n\n        case AF_INET:\n        case AF_INET6:\n            break;\n\n        default:\n            continue;\n        }\n\n        n++;\n    }\n\n    if (n == 0) {\n        u->err = \"host not found\";\n        goto failed;\n    }\n\n    /* MP: ngx_shared_palloc() */\n\n    for (rp = res; rp != NULL; rp = rp->ai_next) {\n\n        switch (rp->ai_family) {\n\n        case AF_INET:\n        case AF_INET6:\n            break;\n\n        default:\n            continue;\n        }\n\n        if (ngx_inet_add_addr(pool, u, rp->ai_addr, rp->ai_addrlen, n)\n            != NGX_OK)\n        {\n            goto failed;\n        }\n    }\n\n    freeaddrinfo(res);\n\n#if (T_NGX_DNS_RESOLVE_BACKUP)\n    ph = (u_char *) getenv(NGX_DNS_RESOLVE_BACKUP_PATH);\n    if (ph != NULL) {\n        path.data = ph;\n        path.len = ngx_strlen(ph);\n        if (ngx_resolve_backup(pool, &u, path) != NGX_OK) {\n            ngx_log_error(NGX_LOG_WARN, ngx_cycle->log, 0,\n                          \"dom %V backup local dns cache failed\", &u->host);\n        }\n    }\n#endif\n\n    return NGX_OK;\n\nfailed:\n\n    freeaddrinfo(res);\n    return NGX_ERROR;\n}\n\n#else /* !NGX_HAVE_GETADDRINFO || !NGX_HAVE_INET6 */\n\nngx_int_t\nngx_inet_resolve_host(ngx_pool_t *pool, ngx_url_t *u)\n{\n    u_char              *host;\n    ngx_uint_t           i, n;\n    struct hostent      *h;\n    struct sockaddr_in   sin;\n#if (T_NGX_DNS_RESOLVE_BACKUP)\n    u_char              *ph;\n    ngx_str_t            path;\n#endif\n\n    /* AF_INET only */\n\n    ngx_memzero(&sin, sizeof(struct sockaddr_in));\n\n    sin.sin_family = AF_INET;\n    sin.sin_addr.s_addr = ngx_inet_addr(u->host.data, u->host.len);\n\n    if (sin.sin_addr.s_addr == INADDR_NONE) {\n        host = ngx_alloc(u->host.len + 1, pool->log);\n        if (host == NULL) {\n            return NGX_ERROR;\n        }\n\n        (void) ngx_cpystrn(host, u->host.data, u->host.len + 1);\n\n        h = gethostbyname((char *) host);\n\n#if (T_NGX_DNS_RESOLVE_BACKUP)\n        ph = (u_char *) getenv(NGX_DNS_RESOLVE_BACKUP_PATH);\n        if (h == NULL && ph != NULL) {\n            path.data = ph;\n            path.len = ngx_strlen(ph);\n            if (ngx_resolve_using_local(pool, &u, path) == NGX_OK) {\n                ngx_log_error(NGX_LOG_WARN, ngx_cycle->log, 0,\n                              \"dom %V using local dns cache successed\",\n                              &u->host);\n                ngx_free(host);\n                return NGX_OK;\n            }\n        }\n#endif\n\n        ngx_free(host);\n\n        if (h == NULL || h->h_addr_list[0] == NULL) {\n            u->err = \"host not found\";\n            return NGX_ERROR;\n        }\n\n        for (n = 0; h->h_addr_list[n] != NULL; n++) { /* void */ }\n\n        /* MP: ngx_shared_palloc() */\n\n        for (i = 0; i < n; i++) {\n            sin.sin_addr.s_addr = *(in_addr_t *) (h->h_addr_list[i]);\n\n            if (ngx_inet_add_addr(pool, u, (struct sockaddr *) &sin,\n                                  sizeof(struct sockaddr_in), n)\n                != NGX_OK)\n            {\n                return NGX_ERROR;\n            }\n        }\n\n#if (T_NGX_DNS_RESOLVE_BACKUP)\n        ph = (u_char *) getenv(NGX_DNS_RESOLVE_BACKUP_PATH);\n        if (ph != NULL) {\n            path.data = ph;\n            path.len = ngx_strlen(ph);\n            if (ngx_resolve_backup(pool, &u, path) != NGX_OK) {\n                ngx_log_error(NGX_LOG_WARN, ngx_cycle->log, 0,\n                              \"dom %V backup local dns cache failed\",\n                              &u->host);\n            }\n        }\n#endif\n\n    } else {\n\n        /* MP: ngx_shared_palloc() */\n\n        if (ngx_inet_add_addr(pool, u, (struct sockaddr *) &sin,\n                              sizeof(struct sockaddr_in), 1)\n            != NGX_OK)\n        {\n            return NGX_ERROR;\n        }\n    }\n\n    return NGX_OK;\n}\n\n#endif /* NGX_HAVE_GETADDRINFO && NGX_HAVE_INET6 */\n\n\nstatic ngx_int_t\nngx_inet_add_addr(ngx_pool_t *pool, ngx_url_t *u, struct sockaddr *sockaddr,\n    socklen_t socklen, ngx_uint_t total)\n{\n    u_char           *p;\n    size_t            len;\n    ngx_uint_t        i, nports;\n    ngx_addr_t       *addr;\n    struct sockaddr  *sa;\n\n    nports = u->last_port ? u->last_port - u->port + 1 : 1;\n\n    if (u->addrs == NULL) {\n        u->addrs = ngx_palloc(pool, total * nports * sizeof(ngx_addr_t));\n        if (u->addrs == NULL) {\n            return NGX_ERROR;\n        }\n    }\n\n    for (i = 0; i < nports; i++) {\n        sa = ngx_pcalloc(pool, socklen);\n        if (sa == NULL) {\n            return NGX_ERROR;\n        }\n\n        ngx_memcpy(sa, sockaddr, socklen);\n\n        ngx_inet_set_port(sa, u->port + i);\n\n        switch (sa->sa_family) {\n\n#if (NGX_HAVE_INET6)\n        case AF_INET6:\n            len = NGX_INET6_ADDRSTRLEN + sizeof(\"[]:65536\") - 1;\n            break;\n#endif\n\n        default: /* AF_INET */\n            len = NGX_INET_ADDRSTRLEN + sizeof(\":65535\") - 1;\n        }\n\n        p = ngx_pnalloc(pool, len);\n        if (p == NULL) {\n            return NGX_ERROR;\n        }\n\n        len = ngx_sock_ntop(sa, socklen, p, len, 1);\n\n        addr = &u->addrs[u->naddrs++];\n\n        addr->sockaddr = sa;\n        addr->socklen = socklen;\n\n        addr->name.len = len;\n        addr->name.data = p;\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_cmp_sockaddr(struct sockaddr *sa1, socklen_t slen1,\n    struct sockaddr *sa2, socklen_t slen2, ngx_uint_t cmp_port)\n{\n    struct sockaddr_in   *sin1, *sin2;\n#if (NGX_HAVE_INET6)\n    struct sockaddr_in6  *sin61, *sin62;\n#endif\n#if (NGX_HAVE_UNIX_DOMAIN)\n    size_t                len;\n    struct sockaddr_un   *saun1, *saun2;\n#endif\n\n    if (sa1->sa_family != sa2->sa_family) {\n        return NGX_DECLINED;\n    }\n\n    switch (sa1->sa_family) {\n\n#if (NGX_HAVE_INET6)\n    case AF_INET6:\n\n        sin61 = (struct sockaddr_in6 *) sa1;\n        sin62 = (struct sockaddr_in6 *) sa2;\n\n        if (cmp_port && sin61->sin6_port != sin62->sin6_port) {\n            return NGX_DECLINED;\n        }\n\n        if (ngx_memcmp(&sin61->sin6_addr, &sin62->sin6_addr, 16) != 0) {\n            return NGX_DECLINED;\n        }\n\n        break;\n#endif\n\n#if (NGX_HAVE_UNIX_DOMAIN)\n    case AF_UNIX:\n\n        saun1 = (struct sockaddr_un *) sa1;\n        saun2 = (struct sockaddr_un *) sa2;\n\n        if (slen1 < slen2) {\n            len = slen1 - offsetof(struct sockaddr_un, sun_path);\n\n        } else {\n            len = slen2 - offsetof(struct sockaddr_un, sun_path);\n        }\n\n        if (len > sizeof(saun1->sun_path)) {\n            len = sizeof(saun1->sun_path);\n        }\n\n        if (ngx_memcmp(&saun1->sun_path, &saun2->sun_path, len) != 0) {\n            return NGX_DECLINED;\n        }\n\n        break;\n#endif\n\n    default: /* AF_INET */\n\n        sin1 = (struct sockaddr_in *) sa1;\n        sin2 = (struct sockaddr_in *) sa2;\n\n        if (cmp_port && sin1->sin_port != sin2->sin_port) {\n            return NGX_DECLINED;\n        }\n\n        if (sin1->sin_addr.s_addr != sin2->sin_addr.s_addr) {\n            return NGX_DECLINED;\n        }\n\n        break;\n    }\n\n    return NGX_OK;\n}\n\n\nin_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\n\nvoid\nngx_inet_set_port(struct sockaddr *sa, in_port_t port)\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        sin6->sin6_port = htons(port);\n        break;\n#endif\n\n#if (NGX_HAVE_UNIX_DOMAIN)\n    case AF_UNIX:\n        break;\n#endif\n\n    default: /* AF_INET */\n        sin = (struct sockaddr_in *) sa;\n        sin->sin_port = htons(port);\n        break;\n    }\n}\n\n\nngx_uint_t\nngx_inet_wildcard(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    case AF_INET:\n        sin = (struct sockaddr_in *) sa;\n\n        if (sin->sin_addr.s_addr == INADDR_ANY) {\n            return 1;\n        }\n\n        break;\n\n#if (NGX_HAVE_INET6)\n\n    case AF_INET6:\n        sin6 = (struct sockaddr_in6 *) sa;\n\n        if (IN6_IS_ADDR_UNSPECIFIED(&sin6->sin6_addr)) {\n            return 1;\n        }\n\n        break;\n\n#endif\n    }\n\n    return 0;\n}\n"
  },
  {
    "path": "src/core/ngx_inet.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_INET_H_INCLUDED_\n#define _NGX_INET_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\n#define NGX_INET_ADDRSTRLEN   (sizeof(\"255.255.255.255\") - 1)\n#define NGX_INET6_ADDRSTRLEN                                                 \\\n    (sizeof(\"ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255\") - 1)\n#define NGX_UNIX_ADDRSTRLEN                                                  \\\n    (sizeof(\"unix:\") - 1 +                                                   \\\n     sizeof(struct sockaddr_un) - offsetof(struct sockaddr_un, sun_path))\n\n#if (NGX_HAVE_UNIX_DOMAIN)\n#define NGX_SOCKADDR_STRLEN   NGX_UNIX_ADDRSTRLEN\n#elif (NGX_HAVE_INET6)\n#define NGX_SOCKADDR_STRLEN   (NGX_INET6_ADDRSTRLEN + sizeof(\"[]:65535\") - 1)\n#else\n#define NGX_SOCKADDR_STRLEN   (NGX_INET_ADDRSTRLEN + sizeof(\":65535\") - 1)\n#endif\n\n/* compatibility */\n#define NGX_SOCKADDRLEN       sizeof(ngx_sockaddr_t)\n\n\ntypedef union {\n    struct sockaddr           sockaddr;\n    struct sockaddr_in        sockaddr_in;\n#if (NGX_HAVE_INET6)\n    struct sockaddr_in6       sockaddr_in6;\n#endif\n#if (NGX_HAVE_UNIX_DOMAIN)\n    struct sockaddr_un        sockaddr_un;\n#endif\n} ngx_sockaddr_t;\n\n\n#if (T_NGX_DNS_RESOLVE_BACKUP)\n#define NGX_DNS_RESOLVE_BACKUP_PATH             \"NGX_DNS_RESOLVE_BACKUP_PATH\"\n#endif\n\n\ntypedef struct {\n    in_addr_t                 addr;\n    in_addr_t                 mask;\n} ngx_in_cidr_t;\n\n\n#if (NGX_HAVE_INET6)\n\ntypedef struct {\n    struct in6_addr           addr;\n    struct in6_addr           mask;\n} ngx_in6_cidr_t;\n\n#endif\n\n\ntypedef struct {\n    ngx_uint_t                family;\n    union {\n        ngx_in_cidr_t         in;\n#if (NGX_HAVE_INET6)\n        ngx_in6_cidr_t        in6;\n#endif\n    } u;\n} ngx_cidr_t;\n\n\ntypedef struct {\n    struct sockaddr          *sockaddr;\n    socklen_t                 socklen;\n    ngx_str_t                 name;\n} ngx_addr_t;\n\n\ntypedef struct {\n    ngx_str_t                 url;\n    ngx_str_t                 host;\n    ngx_str_t                 port_text;\n    ngx_str_t                 uri;\n\n    in_port_t                 port;\n    in_port_t                 default_port;\n    in_port_t                 last_port;\n    int                       family;\n\n    unsigned                  listen:1;\n    unsigned                  uri_part:1;\n    unsigned                  no_resolve:1;\n\n    unsigned                  no_port:1;\n    unsigned                  wildcard:1;\n\n    socklen_t                 socklen;\n    ngx_sockaddr_t            sockaddr;\n\n    ngx_addr_t               *addrs;\n    ngx_uint_t                naddrs;\n\n    char                     *err;\n} ngx_url_t;\n\n\nin_addr_t ngx_inet_addr(u_char *text, size_t len);\n#if (NGX_HAVE_INET6)\nngx_int_t ngx_inet6_addr(u_char *p, size_t len, u_char *addr);\nsize_t ngx_inet6_ntop(u_char *p, u_char *text, size_t len);\n#endif\nsize_t ngx_sock_ntop(struct sockaddr *sa, socklen_t socklen, u_char *text,\n    size_t len, ngx_uint_t port);\nsize_t ngx_inet_ntop(int family, void *addr, u_char *text, size_t len);\nngx_int_t ngx_ptocidr(ngx_str_t *text, ngx_cidr_t *cidr);\nngx_int_t ngx_cidr_match(struct sockaddr *sa, ngx_array_t *cidrs);\nngx_int_t ngx_parse_addr(ngx_pool_t *pool, ngx_addr_t *addr, u_char *text,\n    size_t len);\nngx_int_t ngx_parse_addr_port(ngx_pool_t *pool, ngx_addr_t *addr,\n    u_char *text, size_t len);\nngx_int_t ngx_parse_url(ngx_pool_t *pool, ngx_url_t *u);\nngx_int_t ngx_inet_resolve_host(ngx_pool_t *pool, ngx_url_t *u);\nngx_int_t ngx_cmp_sockaddr(struct sockaddr *sa1, socklen_t slen1,\n    struct sockaddr *sa2, socklen_t slen2, ngx_uint_t cmp_port);\nin_port_t ngx_inet_get_port(struct sockaddr *sa);\nvoid ngx_inet_set_port(struct sockaddr *sa, in_port_t port);\nngx_uint_t ngx_inet_wildcard(struct sockaddr *sa);\n\n\n#endif /* _NGX_INET_H_INCLUDED_ */\n"
  },
  {
    "path": "src/core/ngx_list.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\nngx_list_t *\nngx_list_create(ngx_pool_t *pool, ngx_uint_t n, size_t size)\n{\n    ngx_list_t  *list;\n\n    list = ngx_palloc(pool, sizeof(ngx_list_t));\n    if (list == NULL) {\n        return NULL;\n    }\n\n    if (ngx_list_init(list, pool, n, size) != NGX_OK) {\n        return NULL;\n    }\n\n    return list;\n}\n\n\nvoid *\nngx_list_push(ngx_list_t *l)\n{\n    void             *elt;\n    ngx_list_part_t  *last;\n\n    last = l->last;\n\n    if (last->nelts == l->nalloc) {\n\n        /* the last part is full, allocate a new list part */\n\n        last = ngx_palloc(l->pool, sizeof(ngx_list_part_t));\n        if (last == NULL) {\n            return NULL;\n        }\n\n        last->elts = ngx_palloc(l->pool, l->nalloc * l->size);\n        if (last->elts == NULL) {\n            return NULL;\n        }\n\n        last->nelts = 0;\n        last->next = NULL;\n\n        l->last->next = last;\n        l->last = last;\n    }\n\n    elt = (char *) last->elts + l->size * last->nelts;\n    last->nelts++;\n\n    return elt;\n}\n\n\n#if (T_NGX_IMPROVED_LIST)\nstatic ngx_int_t\nngx_list_delete_elt(ngx_list_t *list, ngx_list_part_t *cur, ngx_uint_t i)\n{\n    u_char *s, *d, *last;\n\n    s = (u_char *) cur->elts + i * list->size;\n    d = s + list->size;\n    last = (u_char *) cur->elts + cur->nelts * list->size;\n\n    while (d < last) {\n        *s++ = *d++;\n    }\n\n    cur->nelts--;\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_list_delete(ngx_list_t *list, void *elt)\n{\n    u_char          *data;\n    ngx_uint_t       i;\n    ngx_list_part_t *part, *pre;\n\n    part = &list->part;\n    pre = part;\n    data = 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            i = 0;\n            pre = part;\n            part = part->next;\n            data = part->elts;\n        }\n\n        if ((data + i * list->size)  == (u_char *) elt) {\n            if (&list->part != part && part->nelts == 1) {\n                pre->next = part->next;\n                if (part == list->last) {\n                    list->last = pre;\n                }\n\n                return NGX_OK;\n            }\n\n            return ngx_list_delete_elt(list, part, i);\n        }\n    }\n\n    return NGX_ERROR;\n}\n#endif\n"
  },
  {
    "path": "src/core/ngx_list.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_LIST_H_INCLUDED_\n#define _NGX_LIST_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\ntypedef struct ngx_list_part_s  ngx_list_part_t;\n\nstruct ngx_list_part_s {\n    void             *elts;\n    ngx_uint_t        nelts;\n    ngx_list_part_t  *next;\n};\n\n\ntypedef struct {\n    ngx_list_part_t  *last;\n    ngx_list_part_t   part;\n    size_t            size;\n    ngx_uint_t        nalloc;\n    ngx_pool_t       *pool;\n} ngx_list_t;\n\n\nngx_list_t *ngx_list_create(ngx_pool_t *pool, ngx_uint_t n, size_t size);\n\nstatic ngx_inline ngx_int_t\nngx_list_init(ngx_list_t *list, ngx_pool_t *pool, ngx_uint_t n, size_t size)\n{\n    list->part.elts = ngx_palloc(pool, n * size);\n    if (list->part.elts == NULL) {\n        return NGX_ERROR;\n    }\n\n    list->part.nelts = 0;\n    list->part.next = NULL;\n    list->last = &list->part;\n    list->size = size;\n    list->nalloc = n;\n    list->pool = pool;\n\n    return NGX_OK;\n}\n\n\n/*\n *\n *  the iteration through the list:\n *\n *  part = &list.part;\n *  data = part->elts;\n *\n *  for (i = 0 ;; i++) {\n *\n *      if (i >= part->nelts) {\n *          if (part->next == NULL) {\n *              break;\n *          }\n *\n *          part = part->next;\n *          data = part->elts;\n *          i = 0;\n *      }\n *\n *      ...  data[i] ...\n *\n *  }\n */\n\n\nvoid *ngx_list_push(ngx_list_t *list);\n#if (T_NGX_IMPROVED_LIST)\nngx_int_t ngx_list_delete(ngx_list_t *list, void *elt);\n#endif\n\n\n#endif /* _NGX_LIST_H_INCLUDED_ */\n"
  },
  {
    "path": "src/core/ngx_log.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\nstatic char *ngx_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\nstatic char *ngx_log_set_levels(ngx_conf_t *cf, ngx_log_t *log);\nstatic void ngx_log_insert(ngx_log_t *log, ngx_log_t *new_log);\n\n#if (T_NGX_XQUIC)\n\nstatic char *ngx_xquic_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\nngx_str_t ngx_log_xquic_backup = ngx_string(\"logs/xquic.log\");\n\n#endif\n\n#if (NGX_DEBUG)\n\nstatic void ngx_log_memory_writer(ngx_log_t *log, ngx_uint_t level,\n    u_char *buf, size_t len);\nstatic void ngx_log_memory_cleanup(void *data);\n\n\ntypedef struct {\n    u_char        *start;\n    u_char        *end;\n    u_char        *pos;\n    ngx_atomic_t   written;\n} ngx_log_memory_buf_t;\n\n#endif\n\n\nstatic ngx_command_t  ngx_errlog_commands[] = {\n\n    { ngx_string(\"error_log\"),\n      NGX_MAIN_CONF|NGX_CONF_1MORE,\n      ngx_error_log,\n      0,\n      0,\n      NULL },\n\n#if (T_NGX_XQUIC)\n    { ngx_string(\"xquic_log\"),\n      NGX_MAIN_CONF|NGX_CONF_1MORE,\n      ngx_xquic_log,\n      0,\n      0,\n      NULL },\n#endif\n\n      ngx_null_command\n};\n\n\nstatic ngx_core_module_t  ngx_errlog_module_ctx = {\n    ngx_string(\"errlog\"),\n    NULL,\n    NULL\n};\n\n\nngx_module_t  ngx_errlog_module = {\n    NGX_MODULE_V1,\n    &ngx_errlog_module_ctx,                /* module context */\n    ngx_errlog_commands,                   /* module directives */\n    NGX_CORE_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_log_t        ngx_log;\nstatic ngx_open_file_t  ngx_log_file;\nngx_uint_t              ngx_use_stderr = 1;\n\n\nstatic ngx_str_t err_levels[] = {\n    ngx_null_string,\n    ngx_string(\"emerg\"),\n    ngx_string(\"alert\"),\n    ngx_string(\"crit\"),\n    ngx_string(\"error\"),\n    ngx_string(\"warn\"),\n    ngx_string(\"notice\"),\n    ngx_string(\"info\"),\n    ngx_string(\"debug\")\n};\n\nstatic const char *debug_levels[] = {\n    \"debug_core\", \"debug_alloc\", \"debug_mutex\", \"debug_event\",\n    \"debug_http\", \"debug_mail\", \"debug_stream\"\n};\n\n\n#if (NGX_HAVE_VARIADIC_MACROS)\n\nvoid\nngx_log_error_core(ngx_uint_t level, ngx_log_t *log, ngx_err_t err,\n    const char *fmt, ...)\n\n#else\n\nvoid\nngx_log_error_core(ngx_uint_t level, ngx_log_t *log, ngx_err_t err,\n    const char *fmt, va_list args)\n\n#endif\n{\n#if (NGX_HAVE_VARIADIC_MACROS)\n    va_list      args;\n#endif\n    u_char      *p, *last, *msg;\n    ssize_t      n;\n    ngx_uint_t   wrote_stderr, debug_connection;\n    u_char       errstr[NGX_MAX_ERROR_STR];\n\n    last = errstr + NGX_MAX_ERROR_STR;\n\n    p = ngx_cpymem(errstr, ngx_cached_err_log_time.data,\n                   ngx_cached_err_log_time.len);\n\n    p = ngx_slprintf(p, last, \" [%V] \", &err_levels[level]);\n\n    /* pid#tid */\n    p = ngx_slprintf(p, last, \"%P#\" NGX_TID_T_FMT \": \",\n                    ngx_log_pid, ngx_log_tid);\n\n    if (log->connection) {\n        p = ngx_slprintf(p, last, \"*%uA \", log->connection);\n    }\n\n    msg = p;\n\n#if (NGX_HAVE_VARIADIC_MACROS)\n\n    va_start(args, fmt);\n    p = ngx_vslprintf(p, last, fmt, args);\n    va_end(args);\n\n#else\n\n    p = ngx_vslprintf(p, last, fmt, args);\n\n#endif\n\n    if (err) {\n        p = ngx_log_errno(p, last, err);\n    }\n\n    if (level != NGX_LOG_DEBUG && log->handler) {\n        p = log->handler(log, p, last - p);\n    }\n\n    if (p > last - NGX_LINEFEED_SIZE) {\n        p = last - NGX_LINEFEED_SIZE;\n    }\n\n    ngx_linefeed(p);\n\n    wrote_stderr = 0;\n    debug_connection = (log->log_level & NGX_LOG_DEBUG_CONNECTION) != 0;\n\n    while (log) {\n\n        if (log->log_level < level && !debug_connection) {\n            break;\n        }\n\n        if (log->writer) {\n            log->writer(log, level, errstr, p - errstr);\n            goto next;\n        }\n\n        if (ngx_time() == log->disk_full_time) {\n\n            /*\n             * on FreeBSD writing to a full filesystem with enabled softupdates\n             * may block process for much longer time than writing to non-full\n             * filesystem, so we skip writing to a log for one second\n             */\n\n            goto next;\n        }\n\n        n = ngx_write_fd(log->file->fd, errstr, p - errstr);\n\n        if (n == -1 && ngx_errno == NGX_ENOSPC) {\n            log->disk_full_time = ngx_time();\n        }\n\n        if (log->file->fd == ngx_stderr) {\n            wrote_stderr = 1;\n        }\n\n    next:\n\n        log = log->next;\n    }\n\n    if (!ngx_use_stderr\n        || level > NGX_LOG_WARN\n        || wrote_stderr)\n    {\n        return;\n    }\n\n    msg -= (7 + err_levels[level].len + 3);\n\n    (void) ngx_sprintf(msg, \"nginx: [%V] \", &err_levels[level]);\n\n    (void) ngx_write_console(ngx_stderr, msg, p - msg);\n}\n\n#if (T_NGX_XQUIC)\nvoid\nngx_log_xquic_core(ngx_uint_t level, ngx_log_t *log, ngx_err_t err,\n    const char *fmt, ...)\n{\n    va_list  args;\n    u_char  *p, *last, *msg;\n    u_char   errstr[NGX_MAX_ERROR_STR];\n\n    if (log->file->fd == NGX_INVALID_FILE) {\n        return;\n    }\n\n    last = errstr + NGX_MAX_ERROR_STR;\n\n    ngx_memcpy(errstr, ngx_cached_xquic_log_time.data,\n               ngx_cached_xquic_log_time.len);\n\n    p = errstr + ngx_cached_xquic_log_time.len;\n\n    msg = p;\n\n    va_start(args, fmt);\n    p = ngx_vslprintf(p, last, fmt, args);\n    va_end(args);\n\n    if (err) {\n        p = ngx_log_errno(p, last, err);\n    }\n\n    if (level != NGX_LOG_DEBUG && log->handler) {\n        p = log->handler(log, p, last - p);\n    }\n\n    if (p > last - NGX_LINEFEED_SIZE) {\n        p = last - NGX_LINEFEED_SIZE;\n    }\n\n    ngx_linefeed(p);\n\n    (void) ngx_write_fd(log->file->fd, errstr, p - errstr);\n\n    if (!ngx_use_stderr\n        || level > NGX_LOG_WARN\n        || log->file->fd == ngx_stderr)\n    {\n        return;\n    }\n\n    msg -= (7 + err_levels[level].len + 3);\n\n    (void) ngx_sprintf(msg, \"nginx: [%V] \", &err_levels[level]);\n\n    (void) ngx_write_console(ngx_stderr, msg, p - msg);\n}\n#endif\n\n#if !(NGX_HAVE_VARIADIC_MACROS)\n\nvoid ngx_cdecl\nngx_log_error(ngx_uint_t level, ngx_log_t *log, ngx_err_t err,\n    const char *fmt, ...)\n{\n    va_list  args;\n\n    if (log->log_level >= level) {\n        va_start(args, fmt);\n        ngx_log_error_core(level, log, err, fmt, args);\n        va_end(args);\n    }\n}\n\n\nvoid ngx_cdecl\nngx_log_debug_core(ngx_log_t *log, ngx_err_t err, const char *fmt, ...)\n{\n    va_list  args;\n\n    va_start(args, fmt);\n    ngx_log_error_core(NGX_LOG_DEBUG, log, err, fmt, args);\n    va_end(args);\n}\n\n#endif\n\n\nvoid ngx_cdecl\nngx_log_abort(ngx_err_t err, const char *fmt, ...)\n{\n    u_char   *p;\n    va_list   args;\n    u_char    errstr[NGX_MAX_CONF_ERRSTR];\n\n    va_start(args, fmt);\n    p = ngx_vsnprintf(errstr, sizeof(errstr) - 1, fmt, args);\n    va_end(args);\n\n    ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, err,\n                  \"%*s\", p - errstr, errstr);\n}\n\n\nvoid ngx_cdecl\nngx_log_stderr(ngx_err_t err, const char *fmt, ...)\n{\n    u_char   *p, *last;\n    va_list   args;\n    u_char    errstr[NGX_MAX_ERROR_STR];\n\n    last = errstr + NGX_MAX_ERROR_STR;\n\n    p = ngx_cpymem(errstr, \"nginx: \", 7);\n\n    va_start(args, fmt);\n    p = ngx_vslprintf(p, last, fmt, args);\n    va_end(args);\n\n    if (err) {\n        p = ngx_log_errno(p, last, err);\n    }\n\n    if (p > last - NGX_LINEFEED_SIZE) {\n        p = last - NGX_LINEFEED_SIZE;\n    }\n\n    ngx_linefeed(p);\n\n    (void) ngx_write_console(ngx_stderr, errstr, p - errstr);\n}\n\n\nu_char *\nngx_log_errno(u_char *buf, u_char *last, ngx_err_t err)\n{\n    if (buf > last - 50) {\n\n        /* leave a space for an error code */\n\n        buf = last - 50;\n        *buf++ = '.';\n        *buf++ = '.';\n        *buf++ = '.';\n    }\n\n#if (NGX_WIN32)\n    buf = ngx_slprintf(buf, last, ((unsigned) err < 0x80000000)\n                                       ? \" (%d: \" : \" (%Xd: \", err);\n#else\n    buf = ngx_slprintf(buf, last, \" (%d: \", err);\n#endif\n\n    buf = ngx_strerror(err, buf, last - buf);\n\n    if (buf < last) {\n        *buf++ = ')';\n    }\n\n    return buf;\n}\n\n\nngx_log_t *\nngx_log_init(u_char *prefix, u_char *error_log)\n{\n    u_char  *p, *name;\n    size_t   nlen, plen;\n\n    ngx_log.file = &ngx_log_file;\n    ngx_log.log_level = NGX_LOG_NOTICE;\n\n    if (error_log == NULL) {\n        error_log = (u_char *) NGX_ERROR_LOG_PATH;\n    }\n\n    name = error_log;\n    nlen = ngx_strlen(name);\n\n    if (nlen == 0) {\n        ngx_log_file.fd = ngx_stderr;\n        return &ngx_log;\n    }\n\n    p = NULL;\n\n#if (NGX_WIN32)\n    if (name[1] != ':') {\n#else\n    if (name[0] != '/') {\n#endif\n\n        if (prefix) {\n            plen = ngx_strlen(prefix);\n\n        } else {\n#ifdef NGX_PREFIX\n            prefix = (u_char *) NGX_PREFIX;\n            plen = ngx_strlen(prefix);\n#else\n            plen = 0;\n#endif\n        }\n\n        if (plen) {\n            name = malloc(plen + nlen + 2);\n            if (name == NULL) {\n                return NULL;\n            }\n\n            p = ngx_cpymem(name, prefix, plen);\n\n            if (!ngx_path_separator(*(p - 1))) {\n                *p++ = '/';\n            }\n\n            ngx_cpystrn(p, error_log, nlen + 1);\n\n            p = name;\n        }\n    }\n\n    ngx_log_file.fd = ngx_open_file(name, NGX_FILE_APPEND,\n                                    NGX_FILE_CREATE_OR_OPEN,\n                                    NGX_FILE_DEFAULT_ACCESS);\n\n    if (ngx_log_file.fd == NGX_INVALID_FILE) {\n        ngx_log_stderr(ngx_errno,\n                       \"[alert] could not open error log file: \"\n                       ngx_open_file_n \" \\\"%s\\\" failed\", name);\n#if (NGX_WIN32)\n        ngx_event_log(ngx_errno,\n                       \"could not open error log file: \"\n                       ngx_open_file_n \" \\\"%s\\\" failed\", name);\n#endif\n\n        ngx_log_file.fd = ngx_stderr;\n    }\n\n    if (p) {\n        ngx_free(p);\n    }\n\n    return &ngx_log;\n}\n\n\nngx_int_t\nngx_log_open_default(ngx_cycle_t *cycle)\n{\n    ngx_log_t  *log;\n\n    if (ngx_log_get_file_log(&cycle->new_log) != NULL) {\n        return NGX_OK;\n    }\n\n    if (cycle->new_log.log_level != 0) {\n        /* there are some error logs, but no files */\n\n        log = ngx_pcalloc(cycle->pool, sizeof(ngx_log_t));\n        if (log == NULL) {\n            return NGX_ERROR;\n        }\n\n    } else {\n        /* no error logs at all */\n        log = &cycle->new_log;\n    }\n\n    log->log_level = NGX_LOG_ERR;\n\n    log->file = ngx_conf_open_file(cycle, &cycle->error_log);\n    if (log->file == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (log != &cycle->new_log) {\n        ngx_log_insert(&cycle->new_log, log);\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_log_redirect_stderr(ngx_cycle_t *cycle)\n{\n    ngx_fd_t  fd;\n\n    if (cycle->log_use_stderr) {\n        return NGX_OK;\n    }\n\n    /* file log always exists when we are called */\n    fd = ngx_log_get_file_log(cycle->log)->file->fd;\n\n    if (fd != ngx_stderr) {\n        if (ngx_set_stderr(fd) == NGX_FILE_ERROR) {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                          ngx_set_stderr_n \" failed\");\n\n            return NGX_ERROR;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nngx_log_t *\nngx_log_get_file_log(ngx_log_t *head)\n{\n    ngx_log_t  *log;\n\n    for (log = head; log; log = log->next) {\n        if (log->file != NULL) {\n            return log;\n        }\n    }\n\n    return NULL;\n}\n\n\nstatic char *\nngx_log_set_levels(ngx_conf_t *cf, ngx_log_t *log)\n{\n    ngx_uint_t   i, n, d, found;\n    ngx_str_t   *value;\n\n    if (cf->args->nelts == 2) {\n        log->log_level = NGX_LOG_ERR;\n        return NGX_CONF_OK;\n    }\n\n    value = cf->args->elts;\n\n    for (i = 2; i < cf->args->nelts; i++) {\n        found = 0;\n\n        for (n = 1; n <= NGX_LOG_DEBUG; n++) {\n            if (ngx_strcmp(value[i].data, err_levels[n].data) == 0) {\n\n                if (log->log_level != 0) {\n                    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                       \"duplicate log level \\\"%V\\\"\",\n                                       &value[i]);\n                    return NGX_CONF_ERROR;\n                }\n\n                log->log_level = n;\n                found = 1;\n                break;\n            }\n        }\n\n        for (n = 0, d = NGX_LOG_DEBUG_FIRST; d <= NGX_LOG_DEBUG_LAST; d <<= 1) {\n            if (ngx_strcmp(value[i].data, debug_levels[n++]) == 0) {\n                if (log->log_level & ~NGX_LOG_DEBUG_ALL) {\n                    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                       \"invalid log level \\\"%V\\\"\",\n                                       &value[i]);\n                    return NGX_CONF_ERROR;\n                }\n\n                log->log_level |= d;\n                found = 1;\n                break;\n            }\n        }\n\n\n        if (!found) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"invalid log level \\\"%V\\\"\", &value[i]);\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    if (log->log_level == NGX_LOG_DEBUG) {\n        log->log_level = NGX_LOG_DEBUG_ALL;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_log_t  *dummy;\n\n    dummy = &cf->cycle->new_log;\n\n    return ngx_log_set_log(cf, &dummy);\n}\n\n\nchar *\nngx_log_set_log(ngx_conf_t *cf, ngx_log_t **head)\n{\n    ngx_log_t          *new_log;\n    ngx_str_t          *value, name;\n    ngx_syslog_peer_t  *peer;\n\n    if (*head != NULL && (*head)->log_level == 0) {\n        new_log = *head;\n\n    } else {\n\n        new_log = ngx_pcalloc(cf->pool, sizeof(ngx_log_t));\n        if (new_log == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        if (*head == NULL) {\n            *head = new_log;\n        }\n    }\n\n    value = cf->args->elts;\n\n    if (ngx_strcmp(value[1].data, \"stderr\") == 0) {\n        ngx_str_null(&name);\n        cf->cycle->log_use_stderr = 1;\n\n        new_log->file = ngx_conf_open_file(cf->cycle, &name);\n        if (new_log->file == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n    } else if (ngx_strncmp(value[1].data, \"memory:\", 7) == 0) {\n\n#if (NGX_DEBUG)\n        size_t                 size, needed;\n        ngx_pool_cleanup_t    *cln;\n        ngx_log_memory_buf_t  *buf;\n\n        value[1].len -= 7;\n        value[1].data += 7;\n\n        needed = sizeof(\"MEMLOG  :\" NGX_LINEFEED)\n                 + cf->conf_file->file.name.len\n                 + NGX_SIZE_T_LEN\n                 + NGX_INT_T_LEN\n                 + NGX_MAX_ERROR_STR;\n\n        size = ngx_parse_size(&value[1]);\n\n        if (size == (size_t) NGX_ERROR || size < needed) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"invalid buffer size \\\"%V\\\"\", &value[1]);\n            return NGX_CONF_ERROR;\n        }\n\n        buf = ngx_pcalloc(cf->pool, sizeof(ngx_log_memory_buf_t));\n        if (buf == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        buf->start = ngx_pnalloc(cf->pool, size);\n        if (buf->start == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        buf->end = buf->start + size;\n\n        buf->pos = ngx_slprintf(buf->start, buf->end, \"MEMLOG %uz %V:%ui%N\",\n                                size, &cf->conf_file->file.name,\n                                cf->conf_file->line);\n\n        ngx_memset(buf->pos, ' ', buf->end - buf->pos);\n\n        cln = ngx_pool_cleanup_add(cf->pool, 0);\n        if (cln == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        cln->data = new_log;\n        cln->handler = ngx_log_memory_cleanup;\n\n        new_log->writer = ngx_log_memory_writer;\n        new_log->wdata = buf;\n\n#else\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"nginx was built without debug support\");\n        return NGX_CONF_ERROR;\n#endif\n\n    } else if (ngx_strncmp(value[1].data, \"syslog:\", 7) == 0) {\n        peer = ngx_pcalloc(cf->pool, sizeof(ngx_syslog_peer_t));\n        if (peer == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        if (ngx_syslog_process_conf(cf, peer) != NGX_CONF_OK) {\n            return NGX_CONF_ERROR;\n        }\n\n        new_log->writer = ngx_syslog_writer;\n        new_log->wdata = peer;\n\n#if (T_PIPES) && !(NGX_WIN32)\n    } else if (ngx_strncmp(value[1].data, \"pipe:\", 5) == 0) {\n\n        if (value[1].len == 5) {\n            return NGX_CONF_ERROR;\n        }\n\n        value[1].len -= 5;\n        value[1].data += 5;\n\n        ngx_open_pipe_t *pipe_conf = ngx_conf_open_pipe(cf->cycle, &value[1], \"w\");\n        if (pipe_conf == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        new_log->file = pipe_conf->open_fd;\n\n#ifdef LOG_PIPE_NEED_BACKUP\n        if (new_log->file != NULL) {\n            name = ngx_log_error_backup;\n            if (ngx_conf_full_name(cf->cycle, &name, 0) != NGX_OK) {\n                return \"fail to set backup\";\n            }\n\n            new_log->file->name = name;\n        }\n#endif\n#endif\n\n    } else {\n        new_log->file = ngx_conf_open_file(cf->cycle, &value[1]);\n        if (new_log->file == NULL) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    if (ngx_log_set_levels(cf, new_log) != NGX_CONF_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (*head != new_log) {\n        ngx_log_insert(*head, new_log);\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic void\nngx_log_insert(ngx_log_t *log, ngx_log_t *new_log)\n{\n    ngx_log_t  tmp;\n\n    if (new_log->log_level > log->log_level) {\n\n        /*\n         * list head address is permanent, insert new log after\n         * head and swap its contents with head\n         */\n\n        tmp = *log;\n        *log = *new_log;\n        *new_log = tmp;\n\n        log->next = new_log;\n        return;\n    }\n\n    while (log->next) {\n        if (new_log->log_level > log->next->log_level) {\n            new_log->next = log->next;\n            log->next = new_log;\n            return;\n        }\n\n        log = log->next;\n    }\n\n    log->next = new_log;\n}\n\n\n#if (NGX_DEBUG)\n\nstatic void\nngx_log_memory_writer(ngx_log_t *log, ngx_uint_t level, u_char *buf,\n    size_t len)\n{\n    u_char                *p;\n    size_t                 avail, written;\n    ngx_log_memory_buf_t  *mem;\n\n    mem = log->wdata;\n\n    if (mem == NULL) {\n        return;\n    }\n\n    written = ngx_atomic_fetch_add(&mem->written, len);\n\n    p = mem->pos + written % (mem->end - mem->pos);\n\n    avail = mem->end - p;\n\n    if (avail >= len) {\n        ngx_memcpy(p, buf, len);\n\n    } else {\n        ngx_memcpy(p, buf, avail);\n        ngx_memcpy(mem->pos, buf + avail, len - avail);\n    }\n}\n\n\nstatic void\nngx_log_memory_cleanup(void *data)\n{\n    ngx_log_t *log = data;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_CORE, log, 0, \"destroy memory log buffer\");\n\n    log->wdata = NULL;\n}\n\n#endif\n\n#if (T_NGX_XQUIC)\nstatic char *\nngx_xquic_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_int_t   rc;\n    ngx_str_t  *value, name;\n\n    if (cf->cycle->xquic_log.file) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    rc = ngx_log_target(cf->cycle, &value[1], &cf->cycle->xquic_log.file);\n\n    if (rc == NGX_ERROR) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid logger \\\"%V\\\"\", &value[1]);\n        return NGX_CONF_ERROR;\n\n    } else if (rc == NGX_OK) {\n        if (cf->cycle->xquic_log.file != NULL) {\n            name = ngx_log_xquic_backup;\n            if (ngx_conf_full_name(cf->cycle, &name, 0) != NGX_OK) {\n                return \"fail to set backup\";\n            }\n\n            cf->cycle->xquic_log.file->name = name;\n        }\n    } else {\n        if (ngx_strcmp(value[1].data, \"stderr\") == 0) {\n            ngx_str_null(&name);\n\n        } else {\n            name = value[1];\n        }\n\n        cf->cycle->xquic_log.file = ngx_conf_open_file(cf->cycle, &name);\n        if (cf->cycle->xquic_log.file == NULL) {\n            return NULL;\n        }\n    }\n\n    if (cf->args->nelts == 2) {\n        cf->cycle->xquic_log.log_level = NGX_LOG_ERR;\n        return NGX_CONF_OK;\n    }\n\n    cf->cycle->xquic_log.log_level = 0;\n\n    return ngx_log_set_levels(cf, &cf->cycle->xquic_log);\n}\n#endif\n\nngx_int_t\nngx_log_target(ngx_cycle_t *cycle, ngx_str_t *value, ngx_open_file_t **file)\n{\n#if (T_PIPES)\n    ngx_open_pipe_t *pipe_conf;\n#endif\n\n    if (ngx_strncmp(value->data, \"file:\", 5) == 0) {\n        if (value->len == 5) {\n            return NGX_ERROR;\n        }\n\n        value->len -= 5;\n        value->data += 5;\n    } else if (ngx_strncmp(value->data, \"pipe:\", 5) == 0) {\n\n#if !(NGX_WIN32) && (T_PIPES)\n        if (value->len == 5) {\n            return NGX_ERROR;\n        }\n\n        value->len -= 5;\n        value->data += 5;\n\n        pipe_conf = ngx_conf_open_pipe(cycle, value, \"w\");\n        if (pipe_conf == NULL) {\n            return NGX_ERROR;\n        }\n\n        *file = pipe_conf->open_fd;\n\n        return NGX_OK;\n\n#else\n        return NGX_ERROR;\n#endif\n\n    }\n\n    return NGX_DECLINED;\n}"
  },
  {
    "path": "src/core/ngx_log.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_LOG_H_INCLUDED_\n#define _NGX_LOG_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\n#define NGX_LOG_STDERR            0\n#define NGX_LOG_EMERG             1\n#define NGX_LOG_ALERT             2\n#define NGX_LOG_CRIT              3\n#define NGX_LOG_ERR               4\n#define NGX_LOG_WARN              5\n#define NGX_LOG_NOTICE            6\n#define NGX_LOG_INFO              7\n#define NGX_LOG_DEBUG             8\n\n#define NGX_LOG_DEBUG_CORE        0x010\n#define NGX_LOG_DEBUG_ALLOC       0x020\n#define NGX_LOG_DEBUG_MUTEX       0x040\n#define NGX_LOG_DEBUG_EVENT       0x080\n#define NGX_LOG_DEBUG_HTTP        0x100\n#define NGX_LOG_DEBUG_MAIL        0x200\n#define NGX_LOG_DEBUG_STREAM      0x400\n\n/*\n * do not forget to update debug_levels[] in src/core/ngx_log.c\n * after the adding a new debug level\n */\n\n#define NGX_LOG_DEBUG_FIRST       NGX_LOG_DEBUG_CORE\n#define NGX_LOG_DEBUG_LAST        NGX_LOG_DEBUG_STREAM\n#define NGX_LOG_DEBUG_CONNECTION  0x80000000\n#define NGX_LOG_DEBUG_ALL         0x7ffffff0\n\n\ntypedef u_char *(*ngx_log_handler_pt) (ngx_log_t *log, u_char *buf, size_t len);\ntypedef void (*ngx_log_writer_pt) (ngx_log_t *log, ngx_uint_t level,\n    u_char *buf, size_t len);\n\n\nstruct ngx_log_s {\n    ngx_uint_t           log_level;\n    ngx_open_file_t     *file;\n\n    ngx_atomic_uint_t    connection;\n\n    time_t               disk_full_time;\n\n    ngx_log_handler_pt   handler;\n    void                *data;\n\n    ngx_log_writer_pt    writer;\n    void                *wdata;\n\n    /*\n     * we declare \"action\" as \"char *\" because the actions are usually\n     * the static strings and in the \"u_char *\" case we have to override\n     * their types all the time\n     */\n\n    char                *action;\n\n    ngx_log_t           *next;\n};\n\n\n#define NGX_MAX_ERROR_STR   2048\n\n\n/*********************************/\n\n#if (NGX_HAVE_C99_VARIADIC_MACROS)\n\n#define NGX_HAVE_VARIADIC_MACROS  1\n\n#define ngx_log_error(level, log, ...)                                        \\\n    if ((log)->log_level >= level) ngx_log_error_core(level, log, __VA_ARGS__)\n\nvoid ngx_log_error_core(ngx_uint_t level, ngx_log_t *log, ngx_err_t err,\n    const char *fmt, ...);\n\n#define ngx_log_debug(level, log, ...)                                        \\\n    if ((log)->log_level & level)                                             \\\n        ngx_log_error_core(NGX_LOG_DEBUG, log, __VA_ARGS__)\n\n#if (T_NGX_XQUIC)\n#define ngx_log_xquic(level, log, ...)                                        \\\n    if ((log)->log_level >= level) ngx_log_xquic_core(level, log, __VA_ARGS__)\n\nvoid ngx_log_xquic_core(ngx_uint_t level, ngx_log_t *log, ngx_err_t err,\n    const char *fmt, ...);\n#endif\n\n/*********************************/\n\n#elif (NGX_HAVE_GCC_VARIADIC_MACROS)\n\n#define NGX_HAVE_VARIADIC_MACROS  1\n\n#define ngx_log_error(level, log, args...)                                    \\\n    if ((log)->log_level >= level) ngx_log_error_core(level, log, args)\n\nvoid ngx_log_error_core(ngx_uint_t level, ngx_log_t *log, ngx_err_t err,\n    const char *fmt, ...);\n\n#define ngx_log_debug(level, log, args...)                                    \\\n    if ((log)->log_level & level)                                             \\\n        ngx_log_error_core(NGX_LOG_DEBUG, log, args)\n\n#if (T_NGX_XQUIC)\n#define ngx_log_xquic(level, log, args...)                                    \\\n    if ((log)->log_level >= level) ngx_log_xquic_core(level, log, args)\n\nvoid ngx_log_xquic_core(ngx_uint_t level, ngx_log_t *log, ngx_err_t err,\n    const char *fmt, ...);\n#endif\n\n/*********************************/\n\n#else /* no variadic macros */\n\n#define NGX_HAVE_VARIADIC_MACROS  0\n\nvoid ngx_cdecl ngx_log_error(ngx_uint_t level, ngx_log_t *log, ngx_err_t err,\n    const char *fmt, ...);\nvoid ngx_log_error_core(ngx_uint_t level, ngx_log_t *log, ngx_err_t err,\n    const char *fmt, va_list args);\n#if (T_NGX_XQUIC)\nvoid ngx_cdecl ngx_log_xquic(ngx_uint_t level, ngx_log_t *log, ngx_err_t err,\n    const char *fmt, ...);\nvoid ngx_log_xquic_core(ngx_uint_t level, ngx_log_t *log, ngx_err_t err,\n    const char *fmt, va_list args);\n#endif\nvoid ngx_cdecl ngx_log_debug_core(ngx_log_t *log, ngx_err_t err,\n    const char *fmt, ...);\n\n\n#endif /* variadic macros */\n\n\n/*********************************/\n\n#if (NGX_DEBUG)\n\n#if (NGX_HAVE_VARIADIC_MACROS)\n\n#define ngx_log_debug0(level, log, err, fmt)                                  \\\n        ngx_log_debug(level, log, err, fmt)\n\n#define ngx_log_debug1(level, log, err, fmt, arg1)                            \\\n        ngx_log_debug(level, log, err, fmt, arg1)\n\n#define ngx_log_debug2(level, log, err, fmt, arg1, arg2)                      \\\n        ngx_log_debug(level, log, err, fmt, arg1, arg2)\n\n#define ngx_log_debug3(level, log, err, fmt, arg1, arg2, arg3)                \\\n        ngx_log_debug(level, log, err, fmt, arg1, arg2, arg3)\n\n#define ngx_log_debug4(level, log, err, fmt, arg1, arg2, arg3, arg4)          \\\n        ngx_log_debug(level, log, err, fmt, arg1, arg2, arg3, arg4)\n\n#define ngx_log_debug5(level, log, err, fmt, arg1, arg2, arg3, arg4, arg5)    \\\n        ngx_log_debug(level, log, err, fmt, arg1, arg2, arg3, arg4, arg5)\n\n#define ngx_log_debug6(level, log, err, fmt,                                  \\\n                       arg1, arg2, arg3, arg4, arg5, arg6)                    \\\n        ngx_log_debug(level, log, err, fmt,                                   \\\n                       arg1, arg2, arg3, arg4, arg5, arg6)\n\n#define ngx_log_debug7(level, log, err, fmt,                                  \\\n                       arg1, arg2, arg3, arg4, arg5, arg6, arg7)              \\\n        ngx_log_debug(level, log, err, fmt,                                   \\\n                       arg1, arg2, arg3, arg4, arg5, arg6, arg7)\n\n#define ngx_log_debug8(level, log, err, fmt,                                  \\\n                       arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)        \\\n        ngx_log_debug(level, log, err, fmt,                                   \\\n                       arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)\n\n\n#else /* no variadic macros */\n\n#define ngx_log_debug0(level, log, err, fmt)                                  \\\n    if ((log)->log_level & level)                                             \\\n        ngx_log_debug_core(log, err, fmt)\n\n#define ngx_log_debug1(level, log, err, fmt, arg1)                            \\\n    if ((log)->log_level & level)                                             \\\n        ngx_log_debug_core(log, err, fmt, arg1)\n\n#define ngx_log_debug2(level, log, err, fmt, arg1, arg2)                      \\\n    if ((log)->log_level & level)                                             \\\n        ngx_log_debug_core(log, err, fmt, arg1, arg2)\n\n#define ngx_log_debug3(level, log, err, fmt, arg1, arg2, arg3)                \\\n    if ((log)->log_level & level)                                             \\\n        ngx_log_debug_core(log, err, fmt, arg1, arg2, arg3)\n\n#define ngx_log_debug4(level, log, err, fmt, arg1, arg2, arg3, arg4)          \\\n    if ((log)->log_level & level)                                             \\\n        ngx_log_debug_core(log, err, fmt, arg1, arg2, arg3, arg4)\n\n#define ngx_log_debug5(level, log, err, fmt, arg1, arg2, arg3, arg4, arg5)    \\\n    if ((log)->log_level & level)                                             \\\n        ngx_log_debug_core(log, err, fmt, arg1, arg2, arg3, arg4, arg5)\n\n#define ngx_log_debug6(level, log, err, fmt,                                  \\\n                       arg1, arg2, arg3, arg4, arg5, arg6)                    \\\n    if ((log)->log_level & level)                                             \\\n        ngx_log_debug_core(log, err, fmt, arg1, arg2, arg3, arg4, arg5, arg6)\n\n#define ngx_log_debug7(level, log, err, fmt,                                  \\\n                       arg1, arg2, arg3, arg4, arg5, arg6, arg7)              \\\n    if ((log)->log_level & level)                                             \\\n        ngx_log_debug_core(log, err, fmt,                                     \\\n                       arg1, arg2, arg3, arg4, arg5, arg6, arg7)\n\n#define ngx_log_debug8(level, log, err, fmt,                                  \\\n                       arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)        \\\n    if ((log)->log_level & level)                                             \\\n        ngx_log_debug_core(log, err, fmt,                                     \\\n                       arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)\n\n#endif\n\n#else /* !NGX_DEBUG */\n\n#define ngx_log_debug0(level, log, err, fmt)\n#define ngx_log_debug1(level, log, err, fmt, arg1)\n#define ngx_log_debug2(level, log, err, fmt, arg1, arg2)\n#define ngx_log_debug3(level, log, err, fmt, arg1, arg2, arg3)\n#define ngx_log_debug4(level, log, err, fmt, arg1, arg2, arg3, arg4)\n#define ngx_log_debug5(level, log, err, fmt, arg1, arg2, arg3, arg4, arg5)\n#define ngx_log_debug6(level, log, err, fmt, arg1, arg2, arg3, arg4, arg5, arg6)\n#define ngx_log_debug7(level, log, err, fmt, arg1, arg2, arg3, arg4, arg5,    \\\n                       arg6, arg7)\n#define ngx_log_debug8(level, log, err, fmt, arg1, arg2, arg3, arg4, arg5,    \\\n                       arg6, arg7, arg8)\n\n#endif\n\n/*********************************/\n\nngx_log_t *ngx_log_init(u_char *prefix, u_char *error_log);\nvoid ngx_cdecl ngx_log_abort(ngx_err_t err, const char *fmt, ...);\nvoid ngx_cdecl ngx_log_stderr(ngx_err_t err, const char *fmt, ...);\nu_char *ngx_log_errno(u_char *buf, u_char *last, ngx_err_t err);\nngx_int_t ngx_log_open_default(ngx_cycle_t *cycle);\nngx_int_t ngx_log_redirect_stderr(ngx_cycle_t *cycle);\nngx_log_t *ngx_log_get_file_log(ngx_log_t *head);\nchar *ngx_log_set_log(ngx_conf_t *cf, ngx_log_t **head);\nngx_int_t ngx_log_target(ngx_cycle_t *cycle, ngx_str_t *value, ngx_open_file_t **file);\n\n/*\n * ngx_write_stderr() cannot be implemented as macro, since\n * MSVC does not allow to use #ifdef inside macro parameters.\n *\n * ngx_write_fd() is used instead of ngx_write_console(), since\n * CharToOemBuff() inside ngx_write_console() cannot be used with\n * read only buffer as destination and CharToOemBuff() is not needed\n * for ngx_write_stderr() anyway.\n */\nstatic ngx_inline void\nngx_write_stderr(char *text)\n{\n    (void) ngx_write_fd(ngx_stderr, text, ngx_strlen(text));\n}\n\n\nstatic ngx_inline void\nngx_write_stdout(char *text)\n{\n    (void) ngx_write_fd(ngx_stdout, text, ngx_strlen(text));\n}\n\n\nextern ngx_module_t  ngx_errlog_module;\nextern ngx_uint_t    ngx_use_stderr;\n\n\n#endif /* _NGX_LOG_H_INCLUDED_ */\n"
  },
  {
    "path": "src/core/ngx_md5.c",
    "content": "\n/*\n * An internal implementation, based on Alexander Peslyak's\n * public domain implementation:\n * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_md5.h>\n\n\nstatic const u_char *ngx_md5_body(ngx_md5_t *ctx, const u_char *data,\n    size_t size);\n\n\nvoid\nngx_md5_init(ngx_md5_t *ctx)\n{\n    ctx->a = 0x67452301;\n    ctx->b = 0xefcdab89;\n    ctx->c = 0x98badcfe;\n    ctx->d = 0x10325476;\n\n    ctx->bytes = 0;\n}\n\n\nvoid\nngx_md5_update(ngx_md5_t *ctx, const void *data, size_t size)\n{\n    size_t  used, free;\n\n    used = (size_t) (ctx->bytes & 0x3f);\n    ctx->bytes += size;\n\n    if (used) {\n        free = 64 - used;\n\n        if (size < free) {\n            ngx_memcpy(&ctx->buffer[used], data, size);\n            return;\n        }\n\n        ngx_memcpy(&ctx->buffer[used], data, free);\n        data = (u_char *) data + free;\n        size -= free;\n        (void) ngx_md5_body(ctx, ctx->buffer, 64);\n    }\n\n    if (size >= 64) {\n        data = ngx_md5_body(ctx, data, size & ~(size_t) 0x3f);\n        size &= 0x3f;\n    }\n\n    ngx_memcpy(ctx->buffer, data, size);\n}\n\n\nvoid\nngx_md5_final(u_char result[16], ngx_md5_t *ctx)\n{\n    size_t  used, free;\n\n    used = (size_t) (ctx->bytes & 0x3f);\n\n    ctx->buffer[used++] = 0x80;\n\n    free = 64 - used;\n\n    if (free < 8) {\n        ngx_memzero(&ctx->buffer[used], free);\n        (void) ngx_md5_body(ctx, ctx->buffer, 64);\n        used = 0;\n        free = 64;\n    }\n\n    ngx_memzero(&ctx->buffer[used], free - 8);\n\n    ctx->bytes <<= 3;\n    ctx->buffer[56] = (u_char) ctx->bytes;\n    ctx->buffer[57] = (u_char) (ctx->bytes >> 8);\n    ctx->buffer[58] = (u_char) (ctx->bytes >> 16);\n    ctx->buffer[59] = (u_char) (ctx->bytes >> 24);\n    ctx->buffer[60] = (u_char) (ctx->bytes >> 32);\n    ctx->buffer[61] = (u_char) (ctx->bytes >> 40);\n    ctx->buffer[62] = (u_char) (ctx->bytes >> 48);\n    ctx->buffer[63] = (u_char) (ctx->bytes >> 56);\n\n    (void) ngx_md5_body(ctx, ctx->buffer, 64);\n\n    result[0] = (u_char) ctx->a;\n    result[1] = (u_char) (ctx->a >> 8);\n    result[2] = (u_char) (ctx->a >> 16);\n    result[3] = (u_char) (ctx->a >> 24);\n    result[4] = (u_char) ctx->b;\n    result[5] = (u_char) (ctx->b >> 8);\n    result[6] = (u_char) (ctx->b >> 16);\n    result[7] = (u_char) (ctx->b >> 24);\n    result[8] = (u_char) ctx->c;\n    result[9] = (u_char) (ctx->c >> 8);\n    result[10] = (u_char) (ctx->c >> 16);\n    result[11] = (u_char) (ctx->c >> 24);\n    result[12] = (u_char) ctx->d;\n    result[13] = (u_char) (ctx->d >> 8);\n    result[14] = (u_char) (ctx->d >> 16);\n    result[15] = (u_char) (ctx->d >> 24);\n\n    ngx_memzero(ctx, sizeof(*ctx));\n}\n\n\n/*\n * The basic MD5 functions.\n *\n * F and G are optimized compared to their RFC 1321 definitions for\n * architectures that lack an AND-NOT instruction, just like in\n * Colin Plumb's implementation.\n */\n\n#define F(x, y, z)  ((z) ^ ((x) & ((y) ^ (z))))\n#define G(x, y, z)  ((y) ^ ((z) & ((x) ^ (y))))\n#define H(x, y, z)  ((x) ^ (y) ^ (z))\n#define I(x, y, z)  ((y) ^ ((x) | ~(z)))\n\n/*\n * The MD5 transformation for all four rounds.\n */\n\n#define STEP(f, a, b, c, d, x, t, s)                                          \\\n    (a) += f((b), (c), (d)) + (x) + (t);                                      \\\n    (a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s))));                \\\n    (a) += (b)\n\n/*\n * SET() reads 4 input bytes in little-endian byte order and stores them\n * in a properly aligned word in host byte order.\n *\n * The check for little-endian architectures that tolerate unaligned\n * memory accesses is just an optimization.  Nothing will break if it\n * does not work.\n */\n\n#if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED)\n\n#define SET(n)      (*(uint32_t *) &p[n * 4])\n#define GET(n)      (*(uint32_t *) &p[n * 4])\n\n#else\n\n#define SET(n)                                                                \\\n    (block[n] =                                                               \\\n    (uint32_t) p[n * 4] |                                                     \\\n    ((uint32_t) p[n * 4 + 1] << 8) |                                          \\\n    ((uint32_t) p[n * 4 + 2] << 16) |                                         \\\n    ((uint32_t) p[n * 4 + 3] << 24))\n\n#define GET(n)      block[n]\n\n#endif\n\n\n/*\n * This processes one or more 64-byte data blocks, but does not update\n * the bit counters.  There are no alignment requirements.\n */\n\nstatic const u_char *\nngx_md5_body(ngx_md5_t *ctx, const u_char *data, size_t size)\n{\n    uint32_t       a, b, c, d;\n    uint32_t       saved_a, saved_b, saved_c, saved_d;\n    const u_char  *p;\n#if !(NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED)\n    uint32_t       block[16];\n#endif\n\n    p = data;\n\n    a = ctx->a;\n    b = ctx->b;\n    c = ctx->c;\n    d = ctx->d;\n\n    do {\n        saved_a = a;\n        saved_b = b;\n        saved_c = c;\n        saved_d = d;\n\n        /* Round 1 */\n\n        STEP(F, a, b, c, d, SET(0),  0xd76aa478, 7);\n        STEP(F, d, a, b, c, SET(1),  0xe8c7b756, 12);\n        STEP(F, c, d, a, b, SET(2),  0x242070db, 17);\n        STEP(F, b, c, d, a, SET(3),  0xc1bdceee, 22);\n        STEP(F, a, b, c, d, SET(4),  0xf57c0faf, 7);\n        STEP(F, d, a, b, c, SET(5),  0x4787c62a, 12);\n        STEP(F, c, d, a, b, SET(6),  0xa8304613, 17);\n        STEP(F, b, c, d, a, SET(7),  0xfd469501, 22);\n        STEP(F, a, b, c, d, SET(8),  0x698098d8, 7);\n        STEP(F, d, a, b, c, SET(9),  0x8b44f7af, 12);\n        STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17);\n        STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22);\n        STEP(F, a, b, c, d, SET(12), 0x6b901122, 7);\n        STEP(F, d, a, b, c, SET(13), 0xfd987193, 12);\n        STEP(F, c, d, a, b, SET(14), 0xa679438e, 17);\n        STEP(F, b, c, d, a, SET(15), 0x49b40821, 22);\n\n        /* Round 2 */\n\n        STEP(G, a, b, c, d, GET(1),  0xf61e2562, 5);\n        STEP(G, d, a, b, c, GET(6),  0xc040b340, 9);\n        STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14);\n        STEP(G, b, c, d, a, GET(0),  0xe9b6c7aa, 20);\n        STEP(G, a, b, c, d, GET(5),  0xd62f105d, 5);\n        STEP(G, d, a, b, c, GET(10), 0x02441453, 9);\n        STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14);\n        STEP(G, b, c, d, a, GET(4),  0xe7d3fbc8, 20);\n        STEP(G, a, b, c, d, GET(9),  0x21e1cde6, 5);\n        STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9);\n        STEP(G, c, d, a, b, GET(3),  0xf4d50d87, 14);\n        STEP(G, b, c, d, a, GET(8),  0x455a14ed, 20);\n        STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5);\n        STEP(G, d, a, b, c, GET(2),  0xfcefa3f8, 9);\n        STEP(G, c, d, a, b, GET(7),  0x676f02d9, 14);\n        STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20);\n\n        /* Round 3 */\n\n        STEP(H, a, b, c, d, GET(5),  0xfffa3942, 4);\n        STEP(H, d, a, b, c, GET(8),  0x8771f681, 11);\n        STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16);\n        STEP(H, b, c, d, a, GET(14), 0xfde5380c, 23);\n        STEP(H, a, b, c, d, GET(1),  0xa4beea44, 4);\n        STEP(H, d, a, b, c, GET(4),  0x4bdecfa9, 11);\n        STEP(H, c, d, a, b, GET(7),  0xf6bb4b60, 16);\n        STEP(H, b, c, d, a, GET(10), 0xbebfbc70, 23);\n        STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4);\n        STEP(H, d, a, b, c, GET(0),  0xeaa127fa, 11);\n        STEP(H, c, d, a, b, GET(3),  0xd4ef3085, 16);\n        STEP(H, b, c, d, a, GET(6),  0x04881d05, 23);\n        STEP(H, a, b, c, d, GET(9),  0xd9d4d039, 4);\n        STEP(H, d, a, b, c, GET(12), 0xe6db99e5, 11);\n        STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16);\n        STEP(H, b, c, d, a, GET(2),  0xc4ac5665, 23);\n\n        /* Round 4 */\n\n        STEP(I, a, b, c, d, GET(0),  0xf4292244, 6);\n        STEP(I, d, a, b, c, GET(7),  0x432aff97, 10);\n        STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15);\n        STEP(I, b, c, d, a, GET(5),  0xfc93a039, 21);\n        STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6);\n        STEP(I, d, a, b, c, GET(3),  0x8f0ccc92, 10);\n        STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15);\n        STEP(I, b, c, d, a, GET(1),  0x85845dd1, 21);\n        STEP(I, a, b, c, d, GET(8),  0x6fa87e4f, 6);\n        STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10);\n        STEP(I, c, d, a, b, GET(6),  0xa3014314, 15);\n        STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21);\n        STEP(I, a, b, c, d, GET(4),  0xf7537e82, 6);\n        STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10);\n        STEP(I, c, d, a, b, GET(2),  0x2ad7d2bb, 15);\n        STEP(I, b, c, d, a, GET(9),  0xeb86d391, 21);\n\n        a += saved_a;\n        b += saved_b;\n        c += saved_c;\n        d += saved_d;\n\n        p += 64;\n\n    } while (size -= 64);\n\n    ctx->a = a;\n    ctx->b = b;\n    ctx->c = c;\n    ctx->d = d;\n\n    return p;\n}\n"
  },
  {
    "path": "src/core/ngx_md5.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_MD5_H_INCLUDED_\n#define _NGX_MD5_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\ntypedef struct {\n    uint64_t  bytes;\n    uint32_t  a, b, c, d;\n    u_char    buffer[64];\n} ngx_md5_t;\n\n\nvoid ngx_md5_init(ngx_md5_t *ctx);\nvoid ngx_md5_update(ngx_md5_t *ctx, const void *data, size_t size);\nvoid ngx_md5_final(u_char result[16], ngx_md5_t *ctx);\n\n\n#endif /* _NGX_MD5_H_INCLUDED_ */\n"
  },
  {
    "path": "src/core/ngx_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Maxim Dounin\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\n#define NGX_MAX_DYNAMIC_MODULES  128\n\n\nstatic ngx_uint_t ngx_module_index(ngx_cycle_t *cycle);\nstatic ngx_uint_t ngx_module_ctx_index(ngx_cycle_t *cycle, ngx_uint_t type,\n    ngx_uint_t index);\n\n\nngx_uint_t         ngx_max_module;\n#if (!T_NGX_SHOW_INFO)\nstatic\n#endif\nngx_uint_t  ngx_modules_n;\n\n\nngx_int_t\nngx_preinit_modules(void)\n{\n    ngx_uint_t  i;\n\n    for (i = 0; ngx_modules[i]; i++) {\n        ngx_modules[i]->index = i;\n        ngx_modules[i]->name = ngx_module_names[i];\n    }\n\n    ngx_modules_n = i;\n    ngx_max_module = ngx_modules_n + NGX_MAX_DYNAMIC_MODULES;\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_cycle_modules(ngx_cycle_t *cycle)\n{\n    /*\n     * create a list of modules to be used for this cycle,\n     * copy static modules to it\n     */\n\n    cycle->modules = ngx_pcalloc(cycle->pool, (ngx_max_module + 1)\n                                              * sizeof(ngx_module_t *));\n    if (cycle->modules == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_memcpy(cycle->modules, ngx_modules,\n               ngx_modules_n * sizeof(ngx_module_t *));\n\n    cycle->modules_n = ngx_modules_n;\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_init_modules(ngx_cycle_t *cycle)\n{\n    ngx_uint_t  i;\n\n    for (i = 0; cycle->modules[i]; i++) {\n        if (cycle->modules[i]->init_module) {\n            if (cycle->modules[i]->init_module(cycle) != NGX_OK) {\n                return NGX_ERROR;\n            }\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_count_modules(ngx_cycle_t *cycle, ngx_uint_t type)\n{\n    ngx_uint_t     i, next, max;\n    ngx_module_t  *module;\n\n    next = 0;\n    max = 0;\n\n    /* count appropriate modules, set up their indices */\n\n    for (i = 0; cycle->modules[i]; i++) {\n        module = cycle->modules[i];\n\n        if (module->type != type) {\n            continue;\n        }\n\n        if (module->ctx_index != NGX_MODULE_UNSET_INDEX) {\n\n            /* if ctx_index was assigned, preserve it */\n\n            if (module->ctx_index > max) {\n                max = module->ctx_index;\n            }\n\n            if (module->ctx_index == next) {\n                next++;\n            }\n\n            continue;\n        }\n\n        /* search for some free index */\n\n        module->ctx_index = ngx_module_ctx_index(cycle, type, next);\n\n        if (module->ctx_index > max) {\n            max = module->ctx_index;\n        }\n\n        next = module->ctx_index + 1;\n    }\n\n    /*\n     * make sure the number returned is big enough for previous\n     * cycle as well, else there will be problems if the number\n     * will be stored in a global variable (as it's used to be)\n     * and we'll have to roll back to the previous cycle\n     */\n\n    if (cycle->old_cycle && cycle->old_cycle->modules) {\n\n        for (i = 0; cycle->old_cycle->modules[i]; i++) {\n            module = cycle->old_cycle->modules[i];\n\n            if (module->type != type) {\n                continue;\n            }\n\n            if (module->ctx_index > max) {\n                max = module->ctx_index;\n            }\n        }\n    }\n\n    /* prevent loading of additional modules */\n\n    cycle->modules_used = 1;\n\n    return max + 1;\n}\n\n\nngx_int_t\nngx_add_module(ngx_conf_t *cf, ngx_str_t *file, ngx_module_t *module,\n    char **order)\n{\n    void               *rv;\n    ngx_uint_t          i, m, before;\n    ngx_core_module_t  *core_module;\n\n    if (cf->cycle->modules_n >= ngx_max_module) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"too many modules loaded\");\n        return NGX_ERROR;\n    }\n\n    if (module->version != nginx_version) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"module \\\"%V\\\" version %ui instead of %ui\",\n                           file, module->version, (ngx_uint_t) nginx_version);\n        return NGX_ERROR;\n    }\n\n    if (ngx_strcmp(module->signature, NGX_MODULE_SIGNATURE) != 0) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"module \\\"%V\\\" is not binary compatible\",\n                           file);\n        return NGX_ERROR;\n    }\n\n    for (m = 0; cf->cycle->modules[m]; m++) {\n        if (ngx_strcmp(cf->cycle->modules[m]->name, module->name) == 0) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"module \\\"%s\\\" is already loaded\",\n                               module->name);\n            return NGX_ERROR;\n        }\n    }\n\n    /*\n     * if the module wasn't previously loaded, assign an index\n     */\n\n    if (module->index == NGX_MODULE_UNSET_INDEX) {\n        module->index = ngx_module_index(cf->cycle);\n\n        if (module->index >= ngx_max_module) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"too many modules loaded\");\n            return NGX_ERROR;\n        }\n    }\n\n    /*\n     * put the module into the cycle->modules array\n     */\n\n    before = cf->cycle->modules_n;\n\n    if (order) {\n        for (i = 0; order[i]; i++) {\n            if (ngx_strcmp(order[i], module->name) == 0) {\n                i++;\n                break;\n            }\n        }\n\n        for ( /* void */ ; order[i]; i++) {\n\n#if 0\n            ngx_log_debug2(NGX_LOG_DEBUG_CORE, cf->log, 0,\n                           \"module: %s before %s\",\n                           module->name, order[i]);\n#endif\n\n            for (m = 0; m < before; m++) {\n                if (ngx_strcmp(cf->cycle->modules[m]->name, order[i]) == 0) {\n\n                    ngx_log_debug3(NGX_LOG_DEBUG_CORE, cf->log, 0,\n                                   \"module: %s before %s:%i\",\n                                   module->name, order[i], m);\n\n                    before = m;\n                    break;\n                }\n            }\n        }\n    }\n\n    /* put the module before modules[before] */\n\n    if (before != cf->cycle->modules_n) {\n        ngx_memmove(&cf->cycle->modules[before + 1],\n                    &cf->cycle->modules[before],\n                    (cf->cycle->modules_n - before) * sizeof(ngx_module_t *));\n    }\n\n    cf->cycle->modules[before] = module;\n    cf->cycle->modules_n++;\n\n    if (module->type == NGX_CORE_MODULE) {\n\n        /*\n         * we are smart enough to initialize core modules;\n         * other modules are expected to be loaded before\n         * initialization - e.g., http modules must be loaded\n         * before http{} block\n         */\n\n        core_module = module->ctx;\n\n        if (core_module->create_conf) {\n            rv = core_module->create_conf(cf->cycle);\n            if (rv == NULL) {\n                return NGX_ERROR;\n            }\n\n            cf->cycle->conf_ctx[module->index] = rv;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_uint_t\nngx_module_index(ngx_cycle_t *cycle)\n{\n    ngx_uint_t     i, index;\n    ngx_module_t  *module;\n\n    index = 0;\n\nagain:\n\n    /* find an unused index */\n\n    for (i = 0; cycle->modules[i]; i++) {\n        module = cycle->modules[i];\n\n        if (module->index == index) {\n            index++;\n            goto again;\n        }\n    }\n\n    /* check previous cycle */\n\n    if (cycle->old_cycle && cycle->old_cycle->modules) {\n\n        for (i = 0; cycle->old_cycle->modules[i]; i++) {\n            module = cycle->old_cycle->modules[i];\n\n            if (module->index == index) {\n                index++;\n                goto again;\n            }\n        }\n    }\n\n    return index;\n}\n\n\nstatic ngx_uint_t\nngx_module_ctx_index(ngx_cycle_t *cycle, ngx_uint_t type, ngx_uint_t index)\n{\n    ngx_uint_t     i;\n    ngx_module_t  *module;\n\nagain:\n\n    /* find an unused ctx_index */\n\n    for (i = 0; cycle->modules[i]; i++) {\n        module = cycle->modules[i];\n\n        if (module->type != type) {\n            continue;\n        }\n\n        if (module->ctx_index == index) {\n            index++;\n            goto again;\n        }\n    }\n\n    /* check previous cycle */\n\n    if (cycle->old_cycle && cycle->old_cycle->modules) {\n\n        for (i = 0; cycle->old_cycle->modules[i]; i++) {\n            module = cycle->old_cycle->modules[i];\n\n            if (module->type != type) {\n                continue;\n            }\n\n            if (module->ctx_index == index) {\n                index++;\n                goto again;\n            }\n        }\n    }\n\n    return index;\n}\n"
  },
  {
    "path": "src/core/ngx_module.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Maxim Dounin\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_MODULE_H_INCLUDED_\n#define _NGX_MODULE_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <nginx.h>\n\n\n#define NGX_MODULE_UNSET_INDEX  (ngx_uint_t) -1\n\n\n#define NGX_MODULE_SIGNATURE_0                                                \\\n    ngx_value(NGX_PTR_SIZE) \",\"                                               \\\n    ngx_value(NGX_SIG_ATOMIC_T_SIZE) \",\"                                      \\\n    ngx_value(NGX_TIME_T_SIZE) \",\"\n\n#if (NGX_HAVE_KQUEUE)\n#define NGX_MODULE_SIGNATURE_1   \"1\"\n#else\n#define NGX_MODULE_SIGNATURE_1   \"0\"\n#endif\n\n#if (NGX_HAVE_IOCP)\n#define NGX_MODULE_SIGNATURE_2   \"1\"\n#else\n#define NGX_MODULE_SIGNATURE_2   \"0\"\n#endif\n\n#if (NGX_HAVE_FILE_AIO || NGX_COMPAT)\n#define NGX_MODULE_SIGNATURE_3   \"1\"\n#else\n#define NGX_MODULE_SIGNATURE_3   \"0\"\n#endif\n\n#if (NGX_HAVE_SENDFILE_NODISKIO || NGX_COMPAT)\n#define NGX_MODULE_SIGNATURE_4   \"1\"\n#else\n#define NGX_MODULE_SIGNATURE_4   \"0\"\n#endif\n\n#if (NGX_HAVE_EVENTFD)\n#define NGX_MODULE_SIGNATURE_5   \"1\"\n#else\n#define NGX_MODULE_SIGNATURE_5   \"0\"\n#endif\n\n#if (NGX_HAVE_EPOLL)\n#define NGX_MODULE_SIGNATURE_6   \"1\"\n#else\n#define NGX_MODULE_SIGNATURE_6   \"0\"\n#endif\n\n#if (NGX_HAVE_KEEPALIVE_TUNABLE)\n#define NGX_MODULE_SIGNATURE_7   \"1\"\n#else\n#define NGX_MODULE_SIGNATURE_7   \"0\"\n#endif\n\n#if (NGX_HAVE_INET6)\n#define NGX_MODULE_SIGNATURE_8   \"1\"\n#else\n#define NGX_MODULE_SIGNATURE_8   \"0\"\n#endif\n\n#define NGX_MODULE_SIGNATURE_9   \"1\"\n#define NGX_MODULE_SIGNATURE_10  \"1\"\n\n#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)\n#define NGX_MODULE_SIGNATURE_11  \"1\"\n#else\n#define NGX_MODULE_SIGNATURE_11  \"0\"\n#endif\n\n#define NGX_MODULE_SIGNATURE_12  \"1\"\n\n#if (NGX_HAVE_SETFIB)\n#define NGX_MODULE_SIGNATURE_13  \"1\"\n#else\n#define NGX_MODULE_SIGNATURE_13  \"0\"\n#endif\n\n#if (NGX_HAVE_TCP_FASTOPEN)\n#define NGX_MODULE_SIGNATURE_14  \"1\"\n#else\n#define NGX_MODULE_SIGNATURE_14  \"0\"\n#endif\n\n#if (NGX_HAVE_UNIX_DOMAIN)\n#define NGX_MODULE_SIGNATURE_15  \"1\"\n#else\n#define NGX_MODULE_SIGNATURE_15  \"0\"\n#endif\n\n#if (NGX_HAVE_VARIADIC_MACROS)\n#define NGX_MODULE_SIGNATURE_16  \"1\"\n#else\n#define NGX_MODULE_SIGNATURE_16  \"0\"\n#endif\n\n#define NGX_MODULE_SIGNATURE_17  \"0\"\n#define NGX_MODULE_SIGNATURE_18  \"0\"\n\n#if (NGX_HAVE_OPENAT)\n#define NGX_MODULE_SIGNATURE_19  \"1\"\n#else\n#define NGX_MODULE_SIGNATURE_19  \"0\"\n#endif\n\n#if (NGX_HAVE_ATOMIC_OPS)\n#define NGX_MODULE_SIGNATURE_20  \"1\"\n#else\n#define NGX_MODULE_SIGNATURE_20  \"0\"\n#endif\n\n#if (NGX_HAVE_POSIX_SEM)\n#define NGX_MODULE_SIGNATURE_21  \"1\"\n#else\n#define NGX_MODULE_SIGNATURE_21  \"0\"\n#endif\n\n#if (NGX_THREADS || NGX_COMPAT)\n#define NGX_MODULE_SIGNATURE_22  \"1\"\n#else\n#define NGX_MODULE_SIGNATURE_22  \"0\"\n#endif\n\n#if (NGX_PCRE)\n#define NGX_MODULE_SIGNATURE_23  \"1\"\n#else\n#define NGX_MODULE_SIGNATURE_23  \"0\"\n#endif\n\n#if (NGX_HTTP_SSL || NGX_COMPAT)\n#define NGX_MODULE_SIGNATURE_24  \"1\"\n#else\n#define NGX_MODULE_SIGNATURE_24  \"0\"\n#endif\n\n#define NGX_MODULE_SIGNATURE_25  \"1\"\n\n#if (NGX_HTTP_GZIP)\n#define NGX_MODULE_SIGNATURE_26  \"1\"\n#else\n#define NGX_MODULE_SIGNATURE_26  \"0\"\n#endif\n\n#define NGX_MODULE_SIGNATURE_27  \"1\"\n\n#if (NGX_HTTP_X_FORWARDED_FOR)\n#define NGX_MODULE_SIGNATURE_28  \"1\"\n#else\n#define NGX_MODULE_SIGNATURE_28  \"0\"\n#endif\n\n#if (NGX_HTTP_REALIP)\n#define NGX_MODULE_SIGNATURE_29  \"1\"\n#else\n#define NGX_MODULE_SIGNATURE_29  \"0\"\n#endif\n\n#if (NGX_HTTP_HEADERS)\n#define NGX_MODULE_SIGNATURE_30  \"1\"\n#else\n#define NGX_MODULE_SIGNATURE_30  \"0\"\n#endif\n\n#if (NGX_HTTP_DAV)\n#define NGX_MODULE_SIGNATURE_31  \"1\"\n#else\n#define NGX_MODULE_SIGNATURE_31  \"0\"\n#endif\n\n#if (NGX_HTTP_CACHE)\n#define NGX_MODULE_SIGNATURE_32  \"1\"\n#else\n#define NGX_MODULE_SIGNATURE_32  \"0\"\n#endif\n\n#if (NGX_HTTP_UPSTREAM_ZONE)\n#define NGX_MODULE_SIGNATURE_33  \"1\"\n#else\n#define NGX_MODULE_SIGNATURE_33  \"0\"\n#endif\n\n#if (NGX_COMPAT)\n#define NGX_MODULE_SIGNATURE_34  \"1\"\n#else\n#define NGX_MODULE_SIGNATURE_34  \"0\"\n#endif\n\n#define NGX_MODULE_SIGNATURE                                                  \\\n    NGX_MODULE_SIGNATURE_0 NGX_MODULE_SIGNATURE_1 NGX_MODULE_SIGNATURE_2      \\\n    NGX_MODULE_SIGNATURE_3 NGX_MODULE_SIGNATURE_4 NGX_MODULE_SIGNATURE_5      \\\n    NGX_MODULE_SIGNATURE_6 NGX_MODULE_SIGNATURE_7 NGX_MODULE_SIGNATURE_8      \\\n    NGX_MODULE_SIGNATURE_9 NGX_MODULE_SIGNATURE_10 NGX_MODULE_SIGNATURE_11    \\\n    NGX_MODULE_SIGNATURE_12 NGX_MODULE_SIGNATURE_13 NGX_MODULE_SIGNATURE_14   \\\n    NGX_MODULE_SIGNATURE_15 NGX_MODULE_SIGNATURE_16 NGX_MODULE_SIGNATURE_17   \\\n    NGX_MODULE_SIGNATURE_18 NGX_MODULE_SIGNATURE_19 NGX_MODULE_SIGNATURE_20   \\\n    NGX_MODULE_SIGNATURE_21 NGX_MODULE_SIGNATURE_22 NGX_MODULE_SIGNATURE_23   \\\n    NGX_MODULE_SIGNATURE_24 NGX_MODULE_SIGNATURE_25 NGX_MODULE_SIGNATURE_26   \\\n    NGX_MODULE_SIGNATURE_27 NGX_MODULE_SIGNATURE_28 NGX_MODULE_SIGNATURE_29   \\\n    NGX_MODULE_SIGNATURE_30 NGX_MODULE_SIGNATURE_31 NGX_MODULE_SIGNATURE_32   \\\n    NGX_MODULE_SIGNATURE_33 NGX_MODULE_SIGNATURE_34\n\n\n#define NGX_MODULE_V1                                                         \\\n    NGX_MODULE_UNSET_INDEX, NGX_MODULE_UNSET_INDEX,                           \\\n    NULL, 0, 0, nginx_version, NGX_MODULE_SIGNATURE\n\n#define NGX_MODULE_V1_PADDING  0, 0, 0, 0, 0, 0, 0, 0\n\n\nstruct ngx_module_s {\n    ngx_uint_t            ctx_index;\n    ngx_uint_t            index;\n\n    char                 *name;\n\n    ngx_uint_t            spare0;\n    ngx_uint_t            spare1;\n\n    ngx_uint_t            version;\n    const char           *signature;\n\n    void                 *ctx;\n    ngx_command_t        *commands;\n    ngx_uint_t            type;\n\n    ngx_int_t           (*init_master)(ngx_log_t *log);\n\n    ngx_int_t           (*init_module)(ngx_cycle_t *cycle);\n\n    ngx_int_t           (*init_process)(ngx_cycle_t *cycle);\n    ngx_int_t           (*init_thread)(ngx_cycle_t *cycle);\n    void                (*exit_thread)(ngx_cycle_t *cycle);\n    void                (*exit_process)(ngx_cycle_t *cycle);\n\n    void                (*exit_master)(ngx_cycle_t *cycle);\n\n    uintptr_t             spare_hook0;\n    uintptr_t             spare_hook1;\n    uintptr_t             spare_hook2;\n    uintptr_t             spare_hook3;\n    uintptr_t             spare_hook4;\n    uintptr_t             spare_hook5;\n    uintptr_t             spare_hook6;\n    uintptr_t             spare_hook7;\n};\n\n\ntypedef struct {\n    ngx_str_t             name;\n    void               *(*create_conf)(ngx_cycle_t *cycle);\n    char               *(*init_conf)(ngx_cycle_t *cycle, void *conf);\n} ngx_core_module_t;\n\n\nngx_int_t ngx_preinit_modules(void);\nngx_int_t ngx_cycle_modules(ngx_cycle_t *cycle);\nngx_int_t ngx_init_modules(ngx_cycle_t *cycle);\nngx_int_t ngx_count_modules(ngx_cycle_t *cycle, ngx_uint_t type);\n\n\nngx_int_t ngx_add_module(ngx_conf_t *cf, ngx_str_t *file,\n    ngx_module_t *module, char **order);\n\n\nextern ngx_module_t  *ngx_modules[];\nextern ngx_uint_t     ngx_max_module;\n\nextern char          *ngx_module_names[];\n\n\n#endif /* _NGX_MODULE_H_INCLUDED_ */\n"
  },
  {
    "path": "src/core/ngx_murmurhash.c",
    "content": "\n/*\n * Copyright (C) Austin Appleby\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\nuint32_t\nngx_murmur_hash2(u_char *data, size_t len)\n{\n    uint32_t  h, k;\n\n    h = 0 ^ len;\n\n    while (len >= 4) {\n        k  = data[0];\n        k |= data[1] << 8;\n        k |= data[2] << 16;\n        k |= data[3] << 24;\n\n        k *= 0x5bd1e995;\n        k ^= k >> 24;\n        k *= 0x5bd1e995;\n\n        h *= 0x5bd1e995;\n        h ^= k;\n\n        data += 4;\n        len -= 4;\n    }\n\n    switch (len) {\n    case 3:\n        h ^= data[2] << 16;\n        /* fall through */\n    case 2:\n        h ^= data[1] << 8;\n        /* fall through */\n    case 1:\n        h ^= data[0];\n        h *= 0x5bd1e995;\n    }\n\n    h ^= h >> 13;\n    h *= 0x5bd1e995;\n    h ^= h >> 15;\n\n    return h;\n}\n"
  },
  {
    "path": "src/core/ngx_murmurhash.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_MURMURHASH_H_INCLUDED_\n#define _NGX_MURMURHASH_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\nuint32_t ngx_murmur_hash2(u_char *data, size_t len);\n\n\n#endif /* _NGX_MURMURHASH_H_INCLUDED_ */\n"
  },
  {
    "path": "src/core/ngx_open_file_cache.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n\n\n/*\n * open file cache caches\n *    open file handles with stat() info;\n *    directories stat() info;\n *    files and directories errors: not found, access denied, etc.\n */\n\n\n#define NGX_MIN_READ_AHEAD  (128 * 1024)\n\n\nstatic void ngx_open_file_cache_cleanup(void *data);\n#if (NGX_HAVE_OPENAT)\nstatic ngx_fd_t ngx_openat_file_owner(ngx_fd_t at_fd, const u_char *name,\n    ngx_int_t mode, ngx_int_t create, ngx_int_t access, ngx_log_t *log);\n#if (NGX_HAVE_O_PATH)\nstatic ngx_int_t ngx_file_o_path_info(ngx_fd_t fd, ngx_file_info_t *fi,\n    ngx_log_t *log);\n#endif\n#endif\nstatic ngx_fd_t ngx_open_file_wrapper(ngx_str_t *name,\n    ngx_open_file_info_t *of, ngx_int_t mode, ngx_int_t create,\n    ngx_int_t access, ngx_log_t *log);\nstatic ngx_int_t ngx_file_info_wrapper(ngx_str_t *name,\n    ngx_open_file_info_t *of, ngx_file_info_t *fi, ngx_log_t *log);\nstatic ngx_int_t ngx_open_and_stat_file(ngx_str_t *name,\n    ngx_open_file_info_t *of, ngx_log_t *log);\nstatic void ngx_open_file_add_event(ngx_open_file_cache_t *cache,\n    ngx_cached_open_file_t *file, ngx_open_file_info_t *of, ngx_log_t *log);\nstatic void ngx_open_file_cleanup(void *data);\nstatic void ngx_close_cached_file(ngx_open_file_cache_t *cache,\n    ngx_cached_open_file_t *file, ngx_uint_t min_uses, ngx_log_t *log);\nstatic void ngx_open_file_del_event(ngx_cached_open_file_t *file);\nstatic void ngx_expire_old_cached_files(ngx_open_file_cache_t *cache,\n    ngx_uint_t n, ngx_log_t *log);\nstatic void ngx_open_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp,\n    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);\nstatic ngx_cached_open_file_t *\n    ngx_open_file_lookup(ngx_open_file_cache_t *cache, ngx_str_t *name,\n    uint32_t hash);\nstatic void ngx_open_file_cache_remove(ngx_event_t *ev);\n\n\nngx_open_file_cache_t *\nngx_open_file_cache_init(ngx_pool_t *pool, ngx_uint_t max, time_t inactive)\n{\n    ngx_pool_cleanup_t     *cln;\n    ngx_open_file_cache_t  *cache;\n\n    cache = ngx_palloc(pool, sizeof(ngx_open_file_cache_t));\n    if (cache == NULL) {\n        return NULL;\n    }\n\n    ngx_rbtree_init(&cache->rbtree, &cache->sentinel,\n                    ngx_open_file_cache_rbtree_insert_value);\n\n    ngx_queue_init(&cache->expire_queue);\n\n    cache->current = 0;\n    cache->max = max;\n    cache->inactive = inactive;\n\n    cln = ngx_pool_cleanup_add(pool, 0);\n    if (cln == NULL) {\n        return NULL;\n    }\n\n    cln->handler = ngx_open_file_cache_cleanup;\n    cln->data = cache;\n\n    return cache;\n}\n\n\nstatic void\nngx_open_file_cache_cleanup(void *data)\n{\n    ngx_open_file_cache_t  *cache = data;\n\n    ngx_queue_t             *q;\n    ngx_cached_open_file_t  *file;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0,\n                   \"open file cache cleanup\");\n\n    for ( ;; ) {\n\n        if (ngx_queue_empty(&cache->expire_queue)) {\n            break;\n        }\n\n        q = ngx_queue_last(&cache->expire_queue);\n\n        file = ngx_queue_data(q, ngx_cached_open_file_t, queue);\n\n        ngx_queue_remove(q);\n\n        ngx_rbtree_delete(&cache->rbtree, &file->node);\n\n        cache->current--;\n\n        ngx_log_debug1(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0,\n                       \"delete cached open file: %s\", file->name);\n\n        if (!file->err && !file->is_dir) {\n            file->close = 1;\n            file->count = 0;\n            ngx_close_cached_file(cache, file, 0, ngx_cycle->log);\n\n        } else {\n            ngx_free(file->name);\n            ngx_free(file);\n        }\n    }\n\n    if (cache->current) {\n        ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,\n                      \"%ui items still left in open file cache\",\n                      cache->current);\n    }\n\n    if (cache->rbtree.root != cache->rbtree.sentinel) {\n        ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,\n                      \"rbtree still is not empty in open file cache\");\n\n    }\n}\n\n\nngx_int_t\nngx_open_cached_file(ngx_open_file_cache_t *cache, ngx_str_t *name,\n    ngx_open_file_info_t *of, ngx_pool_t *pool)\n{\n    time_t                          now;\n    uint32_t                        hash;\n    ngx_int_t                       rc;\n    ngx_file_info_t                 fi;\n    ngx_pool_cleanup_t             *cln;\n    ngx_cached_open_file_t         *file;\n    ngx_pool_cleanup_file_t        *clnf;\n    ngx_open_file_cache_cleanup_t  *ofcln;\n\n    of->fd = NGX_INVALID_FILE;\n    of->err = 0;\n\n    if (cache == NULL) {\n\n        if (of->test_only) {\n\n            if (ngx_file_info_wrapper(name, of, &fi, pool->log)\n                == NGX_FILE_ERROR)\n            {\n                return NGX_ERROR;\n            }\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        }\n\n        cln = ngx_pool_cleanup_add(pool, sizeof(ngx_pool_cleanup_file_t));\n        if (cln == NULL) {\n            return NGX_ERROR;\n        }\n\n        rc = ngx_open_and_stat_file(name, of, pool->log);\n\n        if (rc == NGX_OK && !of->is_dir) {\n            cln->handler = ngx_pool_cleanup_file;\n            clnf = cln->data;\n\n            clnf->fd = of->fd;\n            clnf->name = name->data;\n            clnf->log = pool->log;\n        }\n\n        return rc;\n    }\n\n    cln = ngx_pool_cleanup_add(pool, sizeof(ngx_open_file_cache_cleanup_t));\n    if (cln == NULL) {\n        return NGX_ERROR;\n    }\n\n    now = ngx_time();\n\n    hash = ngx_crc32_long(name->data, name->len);\n\n    file = ngx_open_file_lookup(cache, name, hash);\n\n    if (file) {\n\n        file->uses++;\n\n        ngx_queue_remove(&file->queue);\n\n        if (file->fd == NGX_INVALID_FILE && file->err == 0 && !file->is_dir) {\n\n            /* file was not used often enough to keep open */\n\n            rc = ngx_open_and_stat_file(name, of, pool->log);\n\n            if (rc != NGX_OK && (of->err == 0 || !of->errors)) {\n                goto failed;\n            }\n\n            goto add_event;\n        }\n\n        if (file->use_event\n            || (file->event == NULL\n                && (of->uniq == 0 || of->uniq == file->uniq)\n                && now - file->created < of->valid\n#if (NGX_HAVE_OPENAT)\n                && of->disable_symlinks == file->disable_symlinks\n                && of->disable_symlinks_from == file->disable_symlinks_from\n#endif\n            ))\n        {\n            if (file->err == 0) {\n\n                of->fd = file->fd;\n                of->uniq = file->uniq;\n                of->mtime = file->mtime;\n                of->size = file->size;\n\n                of->is_dir = file->is_dir;\n                of->is_file = file->is_file;\n                of->is_link = file->is_link;\n                of->is_exec = file->is_exec;\n                of->is_directio = file->is_directio;\n\n                if (!file->is_dir) {\n                    file->count++;\n                    ngx_open_file_add_event(cache, file, of, pool->log);\n                }\n\n            } else {\n                of->err = file->err;\n#if (NGX_HAVE_OPENAT)\n                of->failed = file->disable_symlinks ? ngx_openat_file_n\n                                                    : ngx_open_file_n;\n#else\n                of->failed = ngx_open_file_n;\n#endif\n            }\n\n            goto found;\n        }\n\n        ngx_log_debug4(NGX_LOG_DEBUG_CORE, pool->log, 0,\n                       \"retest open file: %s, fd:%d, c:%d, e:%d\",\n                       file->name, file->fd, file->count, file->err);\n\n        if (file->is_dir) {\n\n            /*\n             * chances that directory became file are very small\n             * so test_dir flag allows to use a single syscall\n             * in ngx_file_info() instead of three syscalls\n             */\n\n            of->test_dir = 1;\n        }\n\n        of->fd = file->fd;\n        of->uniq = file->uniq;\n\n        rc = ngx_open_and_stat_file(name, of, pool->log);\n\n        if (rc != NGX_OK && (of->err == 0 || !of->errors)) {\n            goto failed;\n        }\n\n        if (of->is_dir) {\n\n            if (file->is_dir || file->err) {\n                goto update;\n            }\n\n            /* file became directory */\n\n        } else if (of->err == 0) {  /* file */\n\n            if (file->is_dir || file->err) {\n                goto add_event;\n            }\n\n            if (of->uniq == file->uniq) {\n\n                if (file->event) {\n                    file->use_event = 1;\n                }\n\n                of->is_directio = file->is_directio;\n\n                goto update;\n            }\n\n            /* file was changed */\n\n        } else { /* error to cache */\n\n            if (file->err || file->is_dir) {\n                goto update;\n            }\n\n            /* file was removed, etc. */\n        }\n\n        if (file->count == 0) {\n\n            ngx_open_file_del_event(file);\n\n            if (ngx_close_file(file->fd) == NGX_FILE_ERROR) {\n                ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno,\n                              ngx_close_file_n \" \\\"%V\\\" failed\", name);\n            }\n\n            goto add_event;\n        }\n\n        ngx_rbtree_delete(&cache->rbtree, &file->node);\n\n        cache->current--;\n\n        file->close = 1;\n\n        goto create;\n    }\n\n    /* not found */\n\n    rc = ngx_open_and_stat_file(name, of, pool->log);\n\n    if (rc != NGX_OK && (of->err == 0 || !of->errors)) {\n        goto failed;\n    }\n\ncreate:\n\n    if (cache->current >= cache->max) {\n        ngx_expire_old_cached_files(cache, 0, pool->log);\n    }\n\n    file = ngx_alloc(sizeof(ngx_cached_open_file_t), pool->log);\n\n    if (file == NULL) {\n        goto failed;\n    }\n\n    file->name = ngx_alloc(name->len + 1, pool->log);\n\n    if (file->name == NULL) {\n        ngx_free(file);\n        file = NULL;\n        goto failed;\n    }\n\n    ngx_cpystrn(file->name, name->data, name->len + 1);\n\n    file->node.key = hash;\n\n    ngx_rbtree_insert(&cache->rbtree, &file->node);\n\n    cache->current++;\n\n    file->uses = 1;\n    file->count = 0;\n    file->use_event = 0;\n    file->event = NULL;\n\nadd_event:\n\n    ngx_open_file_add_event(cache, file, of, pool->log);\n\nupdate:\n\n    file->fd = of->fd;\n    file->err = of->err;\n#if (NGX_HAVE_OPENAT)\n    file->disable_symlinks = of->disable_symlinks;\n    file->disable_symlinks_from = of->disable_symlinks_from;\n#endif\n\n    if (of->err == 0) {\n        file->uniq = of->uniq;\n        file->mtime = of->mtime;\n        file->size = of->size;\n\n        file->close = 0;\n\n        file->is_dir = of->is_dir;\n        file->is_file = of->is_file;\n        file->is_link = of->is_link;\n        file->is_exec = of->is_exec;\n        file->is_directio = of->is_directio;\n\n        if (!of->is_dir) {\n            file->count++;\n        }\n    }\n\n    file->created = now;\n\nfound:\n\n    file->accessed = now;\n\n    ngx_queue_insert_head(&cache->expire_queue, &file->queue);\n\n    ngx_log_debug5(NGX_LOG_DEBUG_CORE, pool->log, 0,\n                   \"cached open file: %s, fd:%d, c:%d, e:%d, u:%d\",\n                   file->name, file->fd, file->count, file->err, file->uses);\n\n    if (of->err == 0) {\n\n        if (!of->is_dir) {\n            cln->handler = ngx_open_file_cleanup;\n            ofcln = cln->data;\n\n            ofcln->cache = cache;\n            ofcln->file = file;\n            ofcln->min_uses = of->min_uses;\n            ofcln->log = pool->log;\n        }\n\n        return NGX_OK;\n    }\n\n    return NGX_ERROR;\n\nfailed:\n\n    if (file) {\n        ngx_rbtree_delete(&cache->rbtree, &file->node);\n\n        cache->current--;\n\n        if (file->count == 0) {\n\n            if (file->fd != NGX_INVALID_FILE) {\n                if (ngx_close_file(file->fd) == NGX_FILE_ERROR) {\n                    ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno,\n                                  ngx_close_file_n \" \\\"%s\\\" failed\",\n                                  file->name);\n                }\n            }\n\n            ngx_free(file->name);\n            ngx_free(file);\n\n        } else {\n            file->close = 1;\n        }\n    }\n\n    if (of->fd != NGX_INVALID_FILE) {\n        if (ngx_close_file(of->fd) == NGX_FILE_ERROR) {\n            ngx_log_error(NGX_LOG_ALERT, pool->log, ngx_errno,\n                          ngx_close_file_n \" \\\"%V\\\" failed\", name);\n        }\n    }\n\n    return NGX_ERROR;\n}\n\n\n#if (NGX_HAVE_OPENAT)\n\nstatic ngx_fd_t\nngx_openat_file_owner(ngx_fd_t at_fd, const u_char *name,\n    ngx_int_t mode, ngx_int_t create, ngx_int_t access, ngx_log_t *log)\n{\n    ngx_fd_t         fd;\n    ngx_err_t        err;\n    ngx_file_info_t  fi, atfi;\n\n    /*\n     * To allow symlinks with the same owner, use openat() (followed\n     * by fstat()) and fstatat(AT_SYMLINK_NOFOLLOW), and then compare\n     * uids between fstat() and fstatat().\n     *\n     * As there is a race between openat() and fstatat() we don't\n     * know if openat() in fact opened symlink or not.  Therefore,\n     * we have to compare uids even if fstatat() reports the opened\n     * component isn't a symlink (as we don't know whether it was\n     * symlink during openat() or not).\n     */\n\n    fd = ngx_openat_file(at_fd, name, mode, create, access);\n\n    if (fd == NGX_INVALID_FILE) {\n        return NGX_INVALID_FILE;\n    }\n\n    if (ngx_file_at_info(at_fd, name, &atfi, AT_SYMLINK_NOFOLLOW)\n        == NGX_FILE_ERROR)\n    {\n        err = ngx_errno;\n        goto failed;\n    }\n\n#if (NGX_HAVE_O_PATH)\n    if (ngx_file_o_path_info(fd, &fi, log) == NGX_ERROR) {\n        err = ngx_errno;\n        goto failed;\n    }\n#else\n    if (ngx_fd_info(fd, &fi) == NGX_FILE_ERROR) {\n        err = ngx_errno;\n        goto failed;\n    }\n#endif\n\n    if (fi.st_uid != atfi.st_uid) {\n        err = NGX_ELOOP;\n        goto failed;\n    }\n\n    return fd;\n\nfailed:\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    ngx_set_errno(err);\n\n    return NGX_INVALID_FILE;\n}\n\n\n#if (NGX_HAVE_O_PATH)\n\nstatic ngx_int_t\nngx_file_o_path_info(ngx_fd_t fd, ngx_file_info_t *fi, ngx_log_t *log)\n{\n    static ngx_uint_t  use_fstat = 1;\n\n    /*\n     * In Linux 2.6.39 the O_PATH flag was introduced that allows to obtain\n     * a descriptor without actually opening file or directory.  It requires\n     * less permissions for path components, but till Linux 3.6 fstat() returns\n     * EBADF on such descriptors, and fstatat() with the AT_EMPTY_PATH flag\n     * should be used instead.\n     *\n     * Three scenarios are handled in this function:\n     *\n     * 1) The kernel is newer than 3.6 or fstat() with O_PATH support was\n     *    backported by vendor.  Then fstat() is used.\n     *\n     * 2) The kernel is newer than 2.6.39 but older than 3.6.  In this case\n     *    the first call of fstat() returns EBADF and we fallback to fstatat()\n     *    with AT_EMPTY_PATH which was introduced at the same time as O_PATH.\n     *\n     * 3) The kernel is older than 2.6.39 but nginx was build with O_PATH\n     *    support.  Since descriptors are opened with O_PATH|O_RDONLY flags\n     *    and O_PATH is ignored by the kernel then the O_RDONLY flag is\n     *    actually used.  In this case fstat() just works.\n     */\n\n    if (use_fstat) {\n        if (ngx_fd_info(fd, fi) != NGX_FILE_ERROR) {\n            return NGX_OK;\n        }\n\n        if (ngx_errno != NGX_EBADF) {\n            return NGX_ERROR;\n        }\n\n        ngx_log_error(NGX_LOG_NOTICE, log, 0,\n                      \"fstat(O_PATH) failed with EBADF, \"\n                      \"switching to fstatat(AT_EMPTY_PATH)\");\n\n        use_fstat = 0;\n    }\n\n    if (ngx_file_at_info(fd, \"\", fi, AT_EMPTY_PATH) != NGX_FILE_ERROR) {\n        return NGX_OK;\n    }\n\n    return NGX_ERROR;\n}\n\n#endif\n\n#endif /* NGX_HAVE_OPENAT */\n\n\nstatic ngx_fd_t\nngx_open_file_wrapper(ngx_str_t *name, ngx_open_file_info_t *of,\n    ngx_int_t mode, ngx_int_t create, ngx_int_t access, ngx_log_t *log)\n{\n    ngx_fd_t  fd;\n\n#if !(NGX_HAVE_OPENAT)\n\n    fd = ngx_open_file(name->data, mode, create, access);\n\n    if (fd == NGX_INVALID_FILE) {\n        of->err = ngx_errno;\n        of->failed = ngx_open_file_n;\n        return NGX_INVALID_FILE;\n    }\n\n    return fd;\n\n#else\n\n    u_char           *p, *cp, *end;\n    ngx_fd_t          at_fd;\n    ngx_str_t         at_name;\n\n    if (of->disable_symlinks == NGX_DISABLE_SYMLINKS_OFF) {\n        fd = ngx_open_file(name->data, mode, create, access);\n\n        if (fd == NGX_INVALID_FILE) {\n            of->err = ngx_errno;\n            of->failed = ngx_open_file_n;\n            return NGX_INVALID_FILE;\n        }\n\n        return fd;\n    }\n\n    p = name->data;\n    end = p + name->len;\n\n    at_name = *name;\n\n    if (of->disable_symlinks_from) {\n\n        cp = p + of->disable_symlinks_from;\n\n        *cp = '\\0';\n\n        at_fd = ngx_open_file(p, NGX_FILE_SEARCH|NGX_FILE_NONBLOCK,\n                              NGX_FILE_OPEN, 0);\n\n        *cp = '/';\n\n        if (at_fd == NGX_INVALID_FILE) {\n            of->err = ngx_errno;\n            of->failed = ngx_open_file_n;\n            return NGX_INVALID_FILE;\n        }\n\n        at_name.len = of->disable_symlinks_from;\n        p = cp + 1;\n\n    } else if (*p == '/') {\n\n        at_fd = ngx_open_file(\"/\",\n                              NGX_FILE_SEARCH|NGX_FILE_NONBLOCK,\n                              NGX_FILE_OPEN, 0);\n\n        if (at_fd == NGX_INVALID_FILE) {\n            of->err = ngx_errno;\n            of->failed = ngx_openat_file_n;\n            return NGX_INVALID_FILE;\n        }\n\n        at_name.len = 1;\n        p++;\n\n    } else {\n        at_fd = NGX_AT_FDCWD;\n    }\n\n    for ( ;; ) {\n        cp = ngx_strlchr(p, end, '/');\n        if (cp == NULL) {\n            break;\n        }\n\n        if (cp == p) {\n            p++;\n            continue;\n        }\n\n        *cp = '\\0';\n\n        if (of->disable_symlinks == NGX_DISABLE_SYMLINKS_NOTOWNER) {\n            fd = ngx_openat_file_owner(at_fd, p,\n                                       NGX_FILE_SEARCH|NGX_FILE_NONBLOCK,\n                                       NGX_FILE_OPEN, 0, log);\n\n        } else {\n            fd = ngx_openat_file(at_fd, p,\n                           NGX_FILE_SEARCH|NGX_FILE_NONBLOCK|NGX_FILE_NOFOLLOW,\n                           NGX_FILE_OPEN, 0);\n        }\n\n        *cp = '/';\n\n        if (fd == NGX_INVALID_FILE) {\n            of->err = ngx_errno;\n            of->failed = ngx_openat_file_n;\n            goto failed;\n        }\n\n        if (at_fd != NGX_AT_FDCWD && ngx_close_file(at_fd) == NGX_FILE_ERROR) {\n            ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,\n                          ngx_close_file_n \" \\\"%V\\\" failed\", &at_name);\n        }\n\n        p = cp + 1;\n        at_fd = fd;\n        at_name.len = cp - at_name.data;\n    }\n\n    if (p == end) {\n\n        /*\n         * If pathname ends with a trailing slash, assume the last path\n         * component is a directory and reopen it with requested flags;\n         * if not, fail with ENOTDIR as per POSIX.\n         *\n         * We cannot rely on O_DIRECTORY in the loop above to check\n         * that the last path component is a directory because\n         * O_DIRECTORY doesn't work on FreeBSD 8.  Fortunately, by\n         * reopening a directory, we don't depend on it at all.\n         */\n\n        fd = ngx_openat_file(at_fd, \".\", mode, create, access);\n        goto done;\n    }\n\n    if (of->disable_symlinks == NGX_DISABLE_SYMLINKS_NOTOWNER\n        && !(create & (NGX_FILE_CREATE_OR_OPEN|NGX_FILE_TRUNCATE)))\n    {\n        fd = ngx_openat_file_owner(at_fd, p, mode, create, access, log);\n\n    } else {\n        fd = ngx_openat_file(at_fd, p, mode|NGX_FILE_NOFOLLOW, create, access);\n    }\n\ndone:\n\n    if (fd == NGX_INVALID_FILE) {\n        of->err = ngx_errno;\n        of->failed = ngx_openat_file_n;\n    }\n\nfailed:\n\n    if (at_fd != NGX_AT_FDCWD && ngx_close_file(at_fd) == NGX_FILE_ERROR) {\n        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,\n                      ngx_close_file_n \" \\\"%V\\\" failed\", &at_name);\n    }\n\n    return fd;\n#endif\n}\n\n\nstatic ngx_int_t\nngx_file_info_wrapper(ngx_str_t *name, ngx_open_file_info_t *of,\n    ngx_file_info_t *fi, ngx_log_t *log)\n{\n    ngx_int_t  rc;\n\n#if !(NGX_HAVE_OPENAT)\n\n    rc = ngx_file_info(name->data, fi);\n\n    if (rc == NGX_FILE_ERROR) {\n        of->err = ngx_errno;\n        of->failed = ngx_file_info_n;\n        return NGX_FILE_ERROR;\n    }\n\n    return rc;\n\n#else\n\n    ngx_fd_t  fd;\n\n    if (of->disable_symlinks == NGX_DISABLE_SYMLINKS_OFF) {\n\n        rc = ngx_file_info(name->data, fi);\n\n        if (rc == NGX_FILE_ERROR) {\n            of->err = ngx_errno;\n            of->failed = ngx_file_info_n;\n            return NGX_FILE_ERROR;\n        }\n\n        return rc;\n    }\n\n    fd = ngx_open_file_wrapper(name, of, NGX_FILE_RDONLY|NGX_FILE_NONBLOCK,\n                               NGX_FILE_OPEN, 0, log);\n\n    if (fd == NGX_INVALID_FILE) {\n        return NGX_FILE_ERROR;\n    }\n\n    rc = ngx_fd_info(fd, fi);\n\n    if (rc == NGX_FILE_ERROR) {\n        of->err = ngx_errno;\n        of->failed = ngx_fd_info_n;\n    }\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 \" \\\"%V\\\" failed\", name);\n    }\n\n    return rc;\n#endif\n}\n\n\nstatic ngx_int_t\nngx_open_and_stat_file(ngx_str_t *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_wrapper(name, of, &fi, log) == NGX_FILE_ERROR) {\n            of->fd = NGX_INVALID_FILE;\n            return NGX_ERROR;\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_wrapper(name, of, &fi, log) == NGX_FILE_ERROR) {\n            of->fd = NGX_INVALID_FILE;\n            return NGX_ERROR;\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_wrapper(name, of, NGX_FILE_RDONLY|NGX_FILE_NONBLOCK,\n                                   NGX_FILE_OPEN, 0, log);\n\n    } else {\n        fd = ngx_open_file_wrapper(name, of, NGX_FILE_APPEND,\n                                   NGX_FILE_CREATE_OR_OPEN,\n                                   NGX_FILE_DEFAULT_ACCESS, log);\n    }\n\n    if (fd == NGX_INVALID_FILE) {\n        of->fd = NGX_INVALID_FILE;\n        return NGX_ERROR;\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 \" \\\"%V\\\" 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 \" \\\"%V\\\" 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 \" \\\"%V\\\" failed\", name);\n        }\n\n        of->fd = NGX_INVALID_FILE;\n\n    } else {\n        of->fd = fd;\n\n        if (of->read_ahead && ngx_file_size(&fi) > NGX_MIN_READ_AHEAD) {\n            if (ngx_read_ahead(fd, of->read_ahead) == NGX_ERROR) {\n                ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,\n                              ngx_read_ahead_n \" \\\"%V\\\" failed\", name);\n            }\n        }\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 \" \\\"%V\\\" 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}\n\n\n/*\n * we ignore any possible event setting error and\n * fallback to usual periodic file retests\n */\n\nstatic void\nngx_open_file_add_event(ngx_open_file_cache_t *cache,\n    ngx_cached_open_file_t *file, ngx_open_file_info_t *of, ngx_log_t *log)\n{\n    ngx_open_file_cache_event_t  *fev;\n\n    if (!(ngx_event_flags & NGX_USE_VNODE_EVENT)\n        || !of->events\n        || file->event\n        || of->fd == NGX_INVALID_FILE\n        || file->uses < of->min_uses)\n    {\n        return;\n    }\n\n    file->use_event = 0;\n\n    file->event = ngx_calloc(sizeof(ngx_event_t), log);\n    if (file->event== NULL) {\n        return;\n    }\n\n    fev = ngx_alloc(sizeof(ngx_open_file_cache_event_t), log);\n    if (fev == NULL) {\n        ngx_free(file->event);\n        file->event = NULL;\n        return;\n    }\n\n    fev->fd = of->fd;\n    fev->file = file;\n    fev->cache = cache;\n\n    file->event->handler = ngx_open_file_cache_remove;\n    file->event->data = fev;\n\n    /*\n     * although vnode event may be called while ngx_cycle->poll\n     * destruction, however, cleanup procedures are run before any\n     * memory freeing and events will be canceled.\n     */\n\n    file->event->log = ngx_cycle->log;\n\n    if (ngx_add_event(file->event, NGX_VNODE_EVENT, NGX_ONESHOT_EVENT)\n        != NGX_OK)\n    {\n        ngx_free(file->event->data);\n        ngx_free(file->event);\n        file->event = NULL;\n        return;\n    }\n\n    /*\n     * we do not set file->use_event here because there may be a race\n     * condition: a file may be deleted between opening the file and\n     * adding event, so we rely upon event notification only after\n     * one file revalidation on next file access\n     */\n\n    return;\n}\n\n\nstatic void\nngx_open_file_cleanup(void *data)\n{\n    ngx_open_file_cache_cleanup_t  *c = data;\n\n    c->file->count--;\n\n    ngx_close_cached_file(c->cache, c->file, c->min_uses, c->log);\n\n    /* drop one or two expired open files */\n    ngx_expire_old_cached_files(c->cache, 1, c->log);\n}\n\n\nstatic void\nngx_close_cached_file(ngx_open_file_cache_t *cache,\n    ngx_cached_open_file_t *file, ngx_uint_t min_uses, ngx_log_t *log)\n{\n    ngx_log_debug5(NGX_LOG_DEBUG_CORE, log, 0,\n                   \"close cached open file: %s, fd:%d, c:%d, u:%d, %d\",\n                   file->name, file->fd, file->count, file->uses, file->close);\n\n    if (!file->close) {\n\n        file->accessed = ngx_time();\n\n        ngx_queue_remove(&file->queue);\n\n        ngx_queue_insert_head(&cache->expire_queue, &file->queue);\n\n        if (file->uses >= min_uses || file->count) {\n            return;\n        }\n    }\n\n    ngx_open_file_del_event(file);\n\n    if (file->count) {\n        return;\n    }\n\n    if (file->fd != NGX_INVALID_FILE) {\n\n        if (ngx_close_file(file->fd) == NGX_FILE_ERROR) {\n            ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,\n                          ngx_close_file_n \" \\\"%s\\\" failed\", file->name);\n        }\n\n        file->fd = NGX_INVALID_FILE;\n    }\n\n    if (!file->close) {\n        return;\n    }\n\n    ngx_free(file->name);\n    ngx_free(file);\n}\n\n\nstatic void\nngx_open_file_del_event(ngx_cached_open_file_t *file)\n{\n    if (file->event == NULL) {\n        return;\n    }\n\n    (void) ngx_del_event(file->event, NGX_VNODE_EVENT,\n                         file->count ? NGX_FLUSH_EVENT : NGX_CLOSE_EVENT);\n\n    ngx_free(file->event->data);\n    ngx_free(file->event);\n    file->event = NULL;\n    file->use_event = 0;\n}\n\n\nstatic void\nngx_expire_old_cached_files(ngx_open_file_cache_t *cache, ngx_uint_t n,\n    ngx_log_t *log)\n{\n    time_t                   now;\n    ngx_queue_t             *q;\n    ngx_cached_open_file_t  *file;\n\n    now = ngx_time();\n\n    /*\n     * n == 1 deletes one or two inactive files\n     * n == 0 deletes least recently used file by force\n     *        and one or two inactive files\n     */\n\n    while (n < 3) {\n\n        if (ngx_queue_empty(&cache->expire_queue)) {\n            return;\n        }\n\n        q = ngx_queue_last(&cache->expire_queue);\n\n        file = ngx_queue_data(q, ngx_cached_open_file_t, queue);\n\n        if (n++ != 0 && now - file->accessed <= cache->inactive) {\n            return;\n        }\n\n        ngx_queue_remove(q);\n\n        ngx_rbtree_delete(&cache->rbtree, &file->node);\n\n        cache->current--;\n\n        ngx_log_debug1(NGX_LOG_DEBUG_CORE, log, 0,\n                       \"expire cached open file: %s\", file->name);\n\n        if (!file->err && !file->is_dir) {\n            file->close = 1;\n            ngx_close_cached_file(cache, file, 0, log);\n\n        } else {\n            ngx_free(file->name);\n            ngx_free(file);\n        }\n    }\n}\n\n\nstatic void\nngx_open_file_cache_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_cached_open_file_t    *file, *file_temp;\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            file = (ngx_cached_open_file_t *) node;\n            file_temp = (ngx_cached_open_file_t *) temp;\n\n            p = (ngx_strcmp(file->name, file_temp->name) < 0)\n                    ? &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_cached_open_file_t *\nngx_open_file_lookup(ngx_open_file_cache_t *cache, ngx_str_t *name,\n    uint32_t hash)\n{\n    ngx_int_t                rc;\n    ngx_rbtree_node_t       *node, *sentinel;\n    ngx_cached_open_file_t  *file;\n\n    node = cache->rbtree.root;\n    sentinel = cache->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        file = (ngx_cached_open_file_t *) node;\n\n        rc = ngx_strcmp(name->data, file->name);\n\n        if (rc == 0) {\n            return file;\n        }\n\n        node = (rc < 0) ? node->left : node->right;\n    }\n\n    return NULL;\n}\n\n\nstatic void\nngx_open_file_cache_remove(ngx_event_t *ev)\n{\n    ngx_cached_open_file_t       *file;\n    ngx_open_file_cache_event_t  *fev;\n\n    fev = ev->data;\n    file = fev->file;\n\n    ngx_queue_remove(&file->queue);\n\n    ngx_rbtree_delete(&fev->cache->rbtree, &file->node);\n\n    fev->cache->current--;\n\n    /* NGX_ONESHOT_EVENT was already deleted */\n    file->event = NULL;\n    file->use_event = 0;\n\n    file->close = 1;\n\n    ngx_close_cached_file(fev->cache, file, 0, ev->log);\n\n    /* free memory only when fev->cache and fev->file are already not needed */\n\n    ngx_free(ev->data);\n    ngx_free(ev);\n}\n"
  },
  {
    "path": "src/core/ngx_open_file_cache.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\n#ifndef _NGX_OPEN_FILE_CACHE_H_INCLUDED_\n#define _NGX_OPEN_FILE_CACHE_H_INCLUDED_\n\n\n#define NGX_OPEN_FILE_DIRECTIO_OFF  NGX_MAX_OFF_T_VALUE\n\n\ntypedef struct {\n    ngx_fd_t                 fd;\n    ngx_file_uniq_t          uniq;\n    time_t                   mtime;\n    off_t                    size;\n    off_t                    fs_size;\n    off_t                    directio;\n    size_t                   read_ahead;\n\n    ngx_err_t                err;\n    char                    *failed;\n\n    time_t                   valid;\n\n    ngx_uint_t               min_uses;\n\n#if (NGX_HAVE_OPENAT)\n    size_t                   disable_symlinks_from;\n    unsigned                 disable_symlinks:2;\n#endif\n\n    unsigned                 test_dir:1;\n    unsigned                 test_only:1;\n    unsigned                 log:1;\n    unsigned                 errors:1;\n    unsigned                 events:1;\n\n    unsigned                 is_dir:1;\n    unsigned                 is_file:1;\n    unsigned                 is_link:1;\n    unsigned                 is_exec:1;\n    unsigned                 is_directio:1;\n} ngx_open_file_info_t;\n\n\ntypedef struct ngx_cached_open_file_s  ngx_cached_open_file_t;\n\nstruct ngx_cached_open_file_s {\n    ngx_rbtree_node_t        node;\n    ngx_queue_t              queue;\n\n    u_char                  *name;\n    time_t                   created;\n    time_t                   accessed;\n\n    ngx_fd_t                 fd;\n    ngx_file_uniq_t          uniq;\n    time_t                   mtime;\n    off_t                    size;\n    ngx_err_t                err;\n\n    uint32_t                 uses;\n\n#if (NGX_HAVE_OPENAT)\n    size_t                   disable_symlinks_from;\n    unsigned                 disable_symlinks:2;\n#endif\n\n    unsigned                 count:24;\n    unsigned                 close:1;\n    unsigned                 use_event:1;\n\n    unsigned                 is_dir:1;\n    unsigned                 is_file:1;\n    unsigned                 is_link:1;\n    unsigned                 is_exec:1;\n    unsigned                 is_directio:1;\n\n    ngx_event_t             *event;\n};\n\n\ntypedef struct {\n    ngx_rbtree_t             rbtree;\n    ngx_rbtree_node_t        sentinel;\n    ngx_queue_t              expire_queue;\n\n    ngx_uint_t               current;\n    ngx_uint_t               max;\n    time_t                   inactive;\n} ngx_open_file_cache_t;\n\n\ntypedef struct {\n    ngx_open_file_cache_t   *cache;\n    ngx_cached_open_file_t  *file;\n    ngx_uint_t               min_uses;\n    ngx_log_t               *log;\n} ngx_open_file_cache_cleanup_t;\n\n\ntypedef struct {\n\n    /* ngx_connection_t stub to allow use c->fd as event ident */\n    void                    *data;\n    ngx_event_t             *read;\n    ngx_event_t             *write;\n    ngx_fd_t                 fd;\n\n    ngx_cached_open_file_t  *file;\n    ngx_open_file_cache_t   *cache;\n} ngx_open_file_cache_event_t;\n\n\nngx_open_file_cache_t *ngx_open_file_cache_init(ngx_pool_t *pool,\n    ngx_uint_t max, time_t inactive);\nngx_int_t ngx_open_cached_file(ngx_open_file_cache_t *cache, ngx_str_t *name,\n    ngx_open_file_info_t *of, ngx_pool_t *pool);\n\n\n#endif /* _NGX_OPEN_FILE_CACHE_H_INCLUDED_ */\n"
  },
  {
    "path": "src/core/ngx_output_chain.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n\n\n#if 0\n#define NGX_SENDFILE_LIMIT  4096\n#endif\n\n/*\n * When DIRECTIO is enabled FreeBSD, Solaris, and MacOSX read directly\n * to an application memory from a device if parameters are aligned\n * to device sector boundary (512 bytes).  They fallback to usual read\n * operation if the parameters are not aligned.\n * Linux allows DIRECTIO only if the parameters are aligned to a filesystem\n * sector boundary, otherwise it returns EINVAL.  The sector size is\n * usually 512 bytes, however, on XFS it may be 4096 bytes.\n */\n\n#define NGX_NONE            1\n\n\nstatic ngx_inline ngx_int_t\n    ngx_output_chain_as_is(ngx_output_chain_ctx_t *ctx, ngx_buf_t *buf);\nstatic ngx_int_t ngx_output_chain_add_copy(ngx_pool_t *pool,\n    ngx_chain_t **chain, ngx_chain_t *in);\nstatic ngx_int_t ngx_output_chain_align_file_buf(ngx_output_chain_ctx_t *ctx,\n    off_t bsize);\nstatic ngx_int_t ngx_output_chain_get_buf(ngx_output_chain_ctx_t *ctx,\n    off_t bsize);\nstatic ngx_int_t ngx_output_chain_copy_buf(ngx_output_chain_ctx_t *ctx);\n\n\nngx_int_t\nngx_output_chain(ngx_output_chain_ctx_t *ctx, ngx_chain_t *in)\n{\n    off_t         bsize;\n    ngx_int_t     rc, last;\n    ngx_chain_t  *cl, *out, **last_out;\n\n    if (ctx->in == NULL && ctx->busy == NULL\n#if (NGX_HAVE_FILE_AIO || NGX_THREADS)\n        && !ctx->aio\n#endif\n       )\n    {\n        /*\n         * the short path for the case when the ctx->in and ctx->busy chains\n         * are empty, the incoming chain is empty too or has the single buf\n         * that does not require the copy\n         */\n\n        if (in == NULL) {\n            return ctx->output_filter(ctx->filter_ctx, in);\n        }\n\n        if (in->next == NULL\n#if (NGX_SENDFILE_LIMIT)\n            && !(in->buf->in_file && in->buf->file_last > NGX_SENDFILE_LIMIT)\n#endif\n            && ngx_output_chain_as_is(ctx, in->buf))\n        {\n            return ctx->output_filter(ctx->filter_ctx, in);\n        }\n    }\n\n    /* add the incoming buf to the chain ctx->in */\n\n    if (in) {\n        if (ngx_output_chain_add_copy(ctx->pool, &ctx->in, in) == NGX_ERROR) {\n            return NGX_ERROR;\n        }\n    }\n\n    out = NULL;\n    last_out = &out;\n    last = NGX_NONE;\n\n    for ( ;; ) {\n\n#if (NGX_HAVE_FILE_AIO || NGX_THREADS)\n        if (ctx->aio) {\n            return NGX_AGAIN;\n        }\n#endif\n\n        while (ctx->in) {\n\n            /*\n             * cycle while there are the ctx->in bufs\n             * and there are the free output bufs to copy in\n             */\n\n            bsize = ngx_buf_size(ctx->in->buf);\n\n            if (bsize == 0 && !ngx_buf_special(ctx->in->buf)) {\n\n                ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, 0,\n                              \"zero size buf in output \"\n                              \"t:%d r:%d f:%d %p %p-%p %p %O-%O\",\n                              ctx->in->buf->temporary,\n                              ctx->in->buf->recycled,\n                              ctx->in->buf->in_file,\n                              ctx->in->buf->start,\n                              ctx->in->buf->pos,\n                              ctx->in->buf->last,\n                              ctx->in->buf->file,\n                              ctx->in->buf->file_pos,\n                              ctx->in->buf->file_last);\n\n                ngx_debug_point();\n\n                ctx->in = ctx->in->next;\n\n                continue;\n            }\n\n            if (bsize < 0) {\n\n                ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, 0,\n                              \"negative size buf in output \"\n                              \"t:%d r:%d f:%d %p %p-%p %p %O-%O\",\n                              ctx->in->buf->temporary,\n                              ctx->in->buf->recycled,\n                              ctx->in->buf->in_file,\n                              ctx->in->buf->start,\n                              ctx->in->buf->pos,\n                              ctx->in->buf->last,\n                              ctx->in->buf->file,\n                              ctx->in->buf->file_pos,\n                              ctx->in->buf->file_last);\n\n                ngx_debug_point();\n\n                return NGX_ERROR;\n            }\n\n            if (ngx_output_chain_as_is(ctx, ctx->in->buf)) {\n\n                /* move the chain link to the output chain */\n\n                cl = ctx->in;\n                ctx->in = cl->next;\n\n                *last_out = cl;\n                last_out = &cl->next;\n                cl->next = NULL;\n\n                continue;\n            }\n\n            if (ctx->buf == NULL) {\n\n                rc = ngx_output_chain_align_file_buf(ctx, bsize);\n\n                if (rc == NGX_ERROR) {\n                    return NGX_ERROR;\n                }\n\n                if (rc != NGX_OK) {\n\n                    if (ctx->free) {\n\n                        /* get the free buf */\n\n                        cl = ctx->free;\n                        ctx->buf = cl->buf;\n                        ctx->free = cl->next;\n\n                        ngx_free_chain(ctx->pool, cl);\n\n                    } else if (out || ctx->allocated == ctx->bufs.num) {\n\n                        break;\n\n                    } else if (ngx_output_chain_get_buf(ctx, bsize) != NGX_OK) {\n                        return NGX_ERROR;\n                    }\n                }\n            }\n\n            rc = ngx_output_chain_copy_buf(ctx);\n\n            if (rc == NGX_ERROR) {\n                return rc;\n            }\n\n            if (rc == NGX_AGAIN) {\n                if (out) {\n                    break;\n                }\n\n                return rc;\n            }\n\n            /* delete the completed buf from the ctx->in chain */\n\n            if (ngx_buf_size(ctx->in->buf) == 0) {\n                ctx->in = ctx->in->next;\n            }\n\n            cl = ngx_alloc_chain_link(ctx->pool);\n            if (cl == NULL) {\n                return NGX_ERROR;\n            }\n\n            cl->buf = ctx->buf;\n            cl->next = NULL;\n            *last_out = cl;\n            last_out = &cl->next;\n            ctx->buf = NULL;\n        }\n\n        if (out == NULL && last != NGX_NONE) {\n\n            if (ctx->in) {\n                return NGX_AGAIN;\n            }\n\n            return last;\n        }\n\n        last = ctx->output_filter(ctx->filter_ctx, out);\n\n        if (last == NGX_ERROR || last == NGX_DONE) {\n            return last;\n        }\n\n        ngx_chain_update_chains(ctx->pool, &ctx->free, &ctx->busy, &out,\n                                ctx->tag);\n        last_out = &out;\n    }\n}\n\n\nstatic ngx_inline ngx_int_t\nngx_output_chain_as_is(ngx_output_chain_ctx_t *ctx, ngx_buf_t *buf)\n{\n    ngx_uint_t  sendfile;\n\n    if (ngx_buf_special(buf)) {\n        return 1;\n    }\n\n#if (NGX_THREADS)\n    if (buf->in_file) {\n        buf->file->thread_handler = ctx->thread_handler;\n        buf->file->thread_ctx = ctx->filter_ctx;\n    }\n#endif\n\n    sendfile = ctx->sendfile;\n\n#if (NGX_SENDFILE_LIMIT)\n\n    if (buf->in_file && buf->file_pos >= NGX_SENDFILE_LIMIT) {\n        sendfile = 0;\n    }\n\n#endif\n\n#if !(NGX_HAVE_SENDFILE_NODISKIO)\n\n    /*\n     * With DIRECTIO, disable sendfile() unless sendfile(SF_NOCACHE)\n     * is available.\n     */\n\n    if (buf->in_file && buf->file->directio) {\n        sendfile = 0;\n    }\n\n#endif\n\n    if (!sendfile) {\n\n        if (!ngx_buf_in_memory(buf)) {\n            return 0;\n        }\n\n        buf->in_file = 0;\n    }\n\n    if (ctx->need_in_memory && !ngx_buf_in_memory(buf)) {\n        return 0;\n    }\n\n    if (ctx->need_in_temp && (buf->memory || buf->mmap)) {\n        return 0;\n    }\n\n    return 1;\n}\n\n\nstatic ngx_int_t\nngx_output_chain_add_copy(ngx_pool_t *pool, ngx_chain_t **chain,\n    ngx_chain_t *in)\n{\n    ngx_chain_t  *cl, **ll;\n#if (NGX_SENDFILE_LIMIT)\n    ngx_buf_t    *b, *buf;\n#endif\n\n    ll = chain;\n\n    for (cl = *chain; cl; cl = cl->next) {\n        ll = &cl->next;\n    }\n\n    while (in) {\n\n        cl = ngx_alloc_chain_link(pool);\n        if (cl == NULL) {\n            return NGX_ERROR;\n        }\n\n#if (NGX_SENDFILE_LIMIT)\n\n        buf = in->buf;\n\n        if (buf->in_file\n            && buf->file_pos < NGX_SENDFILE_LIMIT\n            && buf->file_last > NGX_SENDFILE_LIMIT)\n        {\n            /* split a file buf on two bufs by the sendfile limit */\n\n            b = ngx_calloc_buf(pool);\n            if (b == NULL) {\n                return NGX_ERROR;\n            }\n\n            ngx_memcpy(b, buf, sizeof(ngx_buf_t));\n\n            if (ngx_buf_in_memory(buf)) {\n                buf->pos += (ssize_t) (NGX_SENDFILE_LIMIT - buf->file_pos);\n                b->last = buf->pos;\n            }\n\n            buf->file_pos = NGX_SENDFILE_LIMIT;\n            b->file_last = NGX_SENDFILE_LIMIT;\n\n            cl->buf = b;\n\n        } else {\n            cl->buf = buf;\n            in = in->next;\n        }\n\n#else\n        cl->buf = in->buf;\n        in = in->next;\n\n#endif\n\n        cl->next = NULL;\n        *ll = cl;\n        ll = &cl->next;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_output_chain_align_file_buf(ngx_output_chain_ctx_t *ctx, off_t bsize)\n{\n    size_t      size;\n    ngx_buf_t  *in;\n\n    in = ctx->in->buf;\n\n    if (in->file == NULL || !in->file->directio) {\n        return NGX_DECLINED;\n    }\n\n    ctx->directio = 1;\n\n    size = (size_t) (in->file_pos - (in->file_pos & ~(ctx->alignment - 1)));\n\n    if (size == 0) {\n\n        if (bsize >= (off_t) ctx->bufs.size) {\n            return NGX_DECLINED;\n        }\n\n        size = (size_t) bsize;\n\n    } else {\n        size = (size_t) ctx->alignment - size;\n\n        if ((off_t) size > bsize) {\n            size = (size_t) bsize;\n        }\n    }\n\n    ctx->buf = ngx_create_temp_buf(ctx->pool, size);\n    if (ctx->buf == NULL) {\n        return NGX_ERROR;\n    }\n\n    /*\n     * we do not set ctx->buf->tag, because we do not want\n     * to reuse the buf via ctx->free list\n     */\n\n#if (NGX_HAVE_ALIGNED_DIRECTIO)\n    ctx->unaligned = 1;\n#endif\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_output_chain_get_buf(ngx_output_chain_ctx_t *ctx, off_t bsize)\n{\n    size_t       size;\n    ngx_buf_t   *b, *in;\n    ngx_uint_t   recycled;\n\n    in = ctx->in->buf;\n    size = ctx->bufs.size;\n    recycled = 1;\n\n    if (in->last_in_chain) {\n\n        if (bsize < (off_t) size) {\n\n            /*\n             * allocate a small temp buf for a small last buf\n             * or its small last part\n             */\n\n            size = (size_t) bsize;\n            recycled = 0;\n\n        } else if (!ctx->directio\n                   && ctx->bufs.num == 1\n                   && (bsize < (off_t) (size + size / 4)))\n        {\n            /*\n             * allocate a temp buf that equals to a last buf,\n             * if there is no directio, the last buf size is lesser\n             * than 1.25 of bufs.size and the temp buf is single\n             */\n\n            size = (size_t) bsize;\n            recycled = 0;\n        }\n    }\n\n    b = ngx_calloc_buf(ctx->pool);\n    if (b == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (ctx->directio) {\n\n        /*\n         * allocate block aligned to a disk sector size to enable\n         * userland buffer direct usage conjunctly with directio\n         */\n\n        b->start = ngx_pmemalign(ctx->pool, size, (size_t) ctx->alignment);\n        if (b->start == NULL) {\n            return NGX_ERROR;\n        }\n\n    } else {\n        b->start = ngx_palloc(ctx->pool, size);\n        if (b->start == NULL) {\n            return NGX_ERROR;\n        }\n    }\n\n    b->pos = b->start;\n    b->last = b->start;\n    b->end = b->last + size;\n    b->temporary = 1;\n    b->tag = ctx->tag;\n    b->recycled = recycled;\n\n    ctx->buf = b;\n    ctx->allocated++;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_output_chain_copy_buf(ngx_output_chain_ctx_t *ctx)\n{\n    off_t        size;\n    ssize_t      n;\n    ngx_buf_t   *src, *dst;\n    ngx_uint_t   sendfile;\n\n    src = ctx->in->buf;\n    dst = ctx->buf;\n\n    size = ngx_buf_size(src);\n    size = ngx_min(size, dst->end - dst->pos);\n\n    sendfile = ctx->sendfile && !ctx->directio;\n\n#if (NGX_SENDFILE_LIMIT)\n\n    if (src->in_file && src->file_pos >= NGX_SENDFILE_LIMIT) {\n        sendfile = 0;\n    }\n\n#endif\n\n    if (ngx_buf_in_memory(src)) {\n        ngx_memcpy(dst->pos, src->pos, (size_t) size);\n        src->pos += (size_t) size;\n        dst->last += (size_t) size;\n\n        if (src->in_file) {\n\n            if (sendfile) {\n                dst->in_file = 1;\n                dst->file = src->file;\n                dst->file_pos = src->file_pos;\n                dst->file_last = src->file_pos + size;\n\n            } else {\n                dst->in_file = 0;\n            }\n\n            src->file_pos += size;\n\n        } else {\n            dst->in_file = 0;\n        }\n\n        if (src->pos == src->last) {\n            dst->flush = src->flush;\n            dst->last_buf = src->last_buf;\n            dst->last_in_chain = src->last_in_chain;\n        }\n\n    } else {\n\n#if (NGX_HAVE_ALIGNED_DIRECTIO)\n\n        if (ctx->unaligned) {\n            if (ngx_directio_off(src->file->fd) == NGX_FILE_ERROR) {\n                ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, ngx_errno,\n                              ngx_directio_off_n \" \\\"%s\\\" failed\",\n                              src->file->name.data);\n            }\n        }\n\n#endif\n\n#if (NGX_HAVE_FILE_AIO)\n        if (ctx->aio_handler) {\n            n = ngx_file_aio_read(src->file, dst->pos, (size_t) size,\n                                  src->file_pos, ctx->pool);\n            if (n == NGX_AGAIN) {\n                ctx->aio_handler(ctx, src->file);\n                return NGX_AGAIN;\n            }\n\n        } else\n#endif\n#if (NGX_THREADS)\n        if (ctx->thread_handler) {\n            src->file->thread_task = ctx->thread_task;\n            src->file->thread_handler = ctx->thread_handler;\n            src->file->thread_ctx = ctx->filter_ctx;\n\n            n = ngx_thread_read(src->file, dst->pos, (size_t) size,\n                                src->file_pos, ctx->pool);\n            if (n == NGX_AGAIN) {\n                ctx->thread_task = src->file->thread_task;\n                return NGX_AGAIN;\n            }\n\n        } else\n#endif\n        {\n            n = ngx_read_file(src->file, dst->pos, (size_t) size,\n                              src->file_pos);\n        }\n\n#if (NGX_HAVE_ALIGNED_DIRECTIO)\n\n        if (ctx->unaligned) {\n            ngx_err_t  err;\n\n            err = ngx_errno;\n\n            if (ngx_directio_on(src->file->fd) == NGX_FILE_ERROR) {\n                ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, ngx_errno,\n                              ngx_directio_on_n \" \\\"%s\\\" failed\",\n                              src->file->name.data);\n            }\n\n            ngx_set_errno(err);\n\n            ctx->unaligned = 0;\n        }\n\n#endif\n\n        if (n == NGX_ERROR) {\n            return (ngx_int_t) n;\n        }\n\n        if (n != size) {\n            ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, 0,\n                          ngx_read_file_n \" read only %z of %O from \\\"%s\\\"\",\n                          n, size, src->file->name.data);\n            return NGX_ERROR;\n        }\n\n        dst->last += n;\n\n        if (sendfile) {\n            dst->in_file = 1;\n            dst->file = src->file;\n            dst->file_pos = src->file_pos;\n            dst->file_last = src->file_pos + n;\n\n        } else {\n            dst->in_file = 0;\n        }\n\n        src->file_pos += n;\n\n        if (src->file_pos == src->file_last) {\n            dst->flush = src->flush;\n            dst->last_buf = src->last_buf;\n            dst->last_in_chain = src->last_in_chain;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_chain_writer(void *data, ngx_chain_t *in)\n{\n    ngx_chain_writer_ctx_t *ctx = data;\n\n    off_t              size;\n    ngx_chain_t       *cl, *ln, *chain;\n    ngx_connection_t  *c;\n\n    c = ctx->connection;\n\n    for (size = 0; in; in = in->next) {\n\n        if (ngx_buf_size(in->buf) == 0 && !ngx_buf_special(in->buf)) {\n\n            ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, 0,\n                          \"zero size buf in chain writer \"\n                          \"t:%d r:%d f:%d %p %p-%p %p %O-%O\",\n                          in->buf->temporary,\n                          in->buf->recycled,\n                          in->buf->in_file,\n                          in->buf->start,\n                          in->buf->pos,\n                          in->buf->last,\n                          in->buf->file,\n                          in->buf->file_pos,\n                          in->buf->file_last);\n\n            ngx_debug_point();\n\n            continue;\n        }\n\n        if (ngx_buf_size(in->buf) < 0) {\n\n            ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, 0,\n                          \"negative size buf in chain writer \"\n                          \"t:%d r:%d f:%d %p %p-%p %p %O-%O\",\n                          in->buf->temporary,\n                          in->buf->recycled,\n                          in->buf->in_file,\n                          in->buf->start,\n                          in->buf->pos,\n                          in->buf->last,\n                          in->buf->file,\n                          in->buf->file_pos,\n                          in->buf->file_last);\n\n            ngx_debug_point();\n\n            return NGX_ERROR;\n        }\n\n        size += ngx_buf_size(in->buf);\n\n        ngx_log_debug2(NGX_LOG_DEBUG_CORE, c->log, 0,\n                       \"chain writer buf fl:%d s:%uO\",\n                       in->buf->flush, ngx_buf_size(in->buf));\n\n        cl = ngx_alloc_chain_link(ctx->pool);\n        if (cl == NULL) {\n            return NGX_ERROR;\n        }\n\n        cl->buf = in->buf;\n        cl->next = NULL;\n        *ctx->last = cl;\n        ctx->last = &cl->next;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,\n                   \"chain writer in: %p\", ctx->out);\n\n    for (cl = ctx->out; cl; cl = cl->next) {\n\n        if (ngx_buf_size(cl->buf) == 0 && !ngx_buf_special(cl->buf)) {\n\n            ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, 0,\n                          \"zero size buf in chain writer \"\n                          \"t:%d r:%d f:%d %p %p-%p %p %O-%O\",\n                          cl->buf->temporary,\n                          cl->buf->recycled,\n                          cl->buf->in_file,\n                          cl->buf->start,\n                          cl->buf->pos,\n                          cl->buf->last,\n                          cl->buf->file,\n                          cl->buf->file_pos,\n                          cl->buf->file_last);\n\n            ngx_debug_point();\n\n            continue;\n        }\n\n        if (ngx_buf_size(cl->buf) < 0) {\n\n            ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, 0,\n                          \"negative size buf in chain writer \"\n                          \"t:%d r:%d f:%d %p %p-%p %p %O-%O\",\n                          cl->buf->temporary,\n                          cl->buf->recycled,\n                          cl->buf->in_file,\n                          cl->buf->start,\n                          cl->buf->pos,\n                          cl->buf->last,\n                          cl->buf->file,\n                          cl->buf->file_pos,\n                          cl->buf->file_last);\n\n            ngx_debug_point();\n\n            return NGX_ERROR;\n        }\n\n        size += ngx_buf_size(cl->buf);\n    }\n\n    if (size == 0 && !c->buffered) {\n        return NGX_OK;\n    }\n\n    chain = c->send_chain(c, ctx->out, ctx->limit);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,\n                   \"chain writer out: %p\", chain);\n\n    if (chain == NGX_CHAIN_ERROR) {\n        return NGX_ERROR;\n    }\n\n    if (chain && c->write->ready) {\n        ngx_post_event(c->write, &ngx_posted_next_events);\n    }\n\n    for (cl = ctx->out; cl && cl != chain; /* void */) {\n        ln = cl;\n        cl = cl->next;\n        ngx_free_chain(ctx->pool, ln);\n    }\n\n    ctx->out = chain;\n\n    if (ctx->out == NULL) {\n        ctx->last = &ctx->out;\n\n        if (!c->buffered) {\n            return NGX_OK;\n        }\n    }\n\n    return NGX_AGAIN;\n}\n"
  },
  {
    "path": "src/core/ngx_palloc.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\nstatic ngx_inline void *ngx_palloc_small(ngx_pool_t *pool, size_t size,\n    ngx_uint_t align);\nstatic void *ngx_palloc_block(ngx_pool_t *pool, size_t size);\nstatic void *ngx_palloc_large(ngx_pool_t *pool, size_t size);\n\n\nngx_pool_t *\nngx_create_pool(size_t size, ngx_log_t *log)\n{\n    ngx_pool_t  *p;\n\n    p = ngx_memalign(NGX_POOL_ALIGNMENT, size, log);\n    if (p == NULL) {\n        return NULL;\n    }\n\n    p->d.last = (u_char *) p + sizeof(ngx_pool_t);\n    p->d.end = (u_char *) p + size;\n    p->d.next = NULL;\n    p->d.failed = 0;\n\n    size = size - sizeof(ngx_pool_t);\n    p->max = (size < NGX_MAX_ALLOC_FROM_POOL) ? size : NGX_MAX_ALLOC_FROM_POOL;\n\n    p->current = p;\n    p->chain = NULL;\n    p->large = NULL;\n    p->cleanup = NULL;\n    p->log = log;\n\n    return p;\n}\n\n\nvoid\nngx_destroy_pool(ngx_pool_t *pool)\n{\n    ngx_pool_t          *p, *n;\n    ngx_pool_large_t    *l;\n    ngx_pool_cleanup_t  *c;\n\n    for (c = pool->cleanup; c; c = c->next) {\n        if (c->handler) {\n            ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0,\n                           \"run cleanup: %p\", c);\n            c->handler(c->data);\n        }\n    }\n\n#if (NGX_DEBUG)\n\n    /*\n     * we could allocate the pool->log from this pool\n     * so we cannot use this log while free()ing the pool\n     */\n\n    for (l = pool->large; l; l = l->next) {\n        ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0, \"free: %p\", l->alloc);\n    }\n\n    for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) {\n        ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, pool->log, 0,\n                       \"free: %p, unused: %uz\", p, p->d.end - p->d.last);\n\n        if (n == NULL) {\n            break;\n        }\n    }\n\n#endif\n\n    for (l = pool->large; l; l = l->next) {\n        if (l->alloc) {\n            ngx_free(l->alloc);\n        }\n    }\n\n    for (p = pool, n = pool->d.next; /* void */; p = n, n = n->d.next) {\n        ngx_free(p);\n\n        if (n == NULL) {\n            break;\n        }\n    }\n}\n\n\nvoid\nngx_reset_pool(ngx_pool_t *pool)\n{\n    ngx_pool_t        *p;\n    ngx_pool_large_t  *l;\n\n    for (l = pool->large; l; l = l->next) {\n        if (l->alloc) {\n            ngx_free(l->alloc);\n        }\n    }\n\n    for (p = pool; p; p = p->d.next) {\n        p->d.last = (u_char *) p + sizeof(ngx_pool_t);\n        p->d.failed = 0;\n    }\n\n    pool->current = pool;\n    pool->chain = NULL;\n    pool->large = NULL;\n}\n\n\nvoid *\nngx_palloc(ngx_pool_t *pool, size_t size)\n{\n#if !(NGX_DEBUG_PALLOC)\n    if (size <= pool->max) {\n        return ngx_palloc_small(pool, size, 1);\n    }\n#endif\n\n    return ngx_palloc_large(pool, size);\n}\n\n\nvoid *\nngx_pnalloc(ngx_pool_t *pool, size_t size)\n{\n#if !(NGX_DEBUG_PALLOC)\n    if (size <= pool->max) {\n        return ngx_palloc_small(pool, size, 0);\n    }\n#endif\n\n    return ngx_palloc_large(pool, size);\n}\n\n\nstatic ngx_inline void *\nngx_palloc_small(ngx_pool_t *pool, size_t size, ngx_uint_t align)\n{\n    u_char      *m;\n    ngx_pool_t  *p;\n\n    p = pool->current;\n\n    do {\n        m = p->d.last;\n\n        if (align) {\n            m = ngx_align_ptr(m, NGX_ALIGNMENT);\n        }\n\n        if ((size_t) (p->d.end - m) >= size) {\n            p->d.last = m + size;\n\n            return m;\n        }\n\n        p = p->d.next;\n\n    } while (p);\n\n    return ngx_palloc_block(pool, size);\n}\n\n\nstatic void *\nngx_palloc_block(ngx_pool_t *pool, size_t size)\n{\n    u_char      *m;\n    size_t       psize;\n    ngx_pool_t  *p, *new;\n\n    psize = (size_t) (pool->d.end - (u_char *) pool);\n\n    m = ngx_memalign(NGX_POOL_ALIGNMENT, psize, pool->log);\n    if (m == NULL) {\n        return NULL;\n    }\n\n    new = (ngx_pool_t *) m;\n\n    new->d.end = m + psize;\n    new->d.next = NULL;\n    new->d.failed = 0;\n\n    m += sizeof(ngx_pool_data_t);\n    m = ngx_align_ptr(m, NGX_ALIGNMENT);\n    new->d.last = m + size;\n\n    for (p = pool->current; p->d.next; p = p->d.next) {\n        if (p->d.failed++ > 4) {\n            pool->current = p->d.next;\n        }\n    }\n\n    p->d.next = new;\n\n    return m;\n}\n\n\nstatic void *\nngx_palloc_large(ngx_pool_t *pool, size_t size)\n{\n    void              *p;\n    ngx_uint_t         n;\n    ngx_pool_large_t  *large;\n\n    p = ngx_alloc(size, pool->log);\n    if (p == NULL) {\n        return NULL;\n    }\n\n    n = 0;\n\n    for (large = pool->large; large; large = large->next) {\n        if (large->alloc == NULL) {\n            large->alloc = p;\n            return p;\n        }\n\n        if (n++ > 3) {\n            break;\n        }\n    }\n\n    large = ngx_palloc_small(pool, sizeof(ngx_pool_large_t), 1);\n    if (large == NULL) {\n        ngx_free(p);\n        return NULL;\n    }\n\n    large->alloc = p;\n    large->next = pool->large;\n    pool->large = large;\n\n    return p;\n}\n\n\nvoid *\nngx_pmemalign(ngx_pool_t *pool, size_t size, size_t alignment)\n{\n    void              *p;\n    ngx_pool_large_t  *large;\n\n    p = ngx_memalign(alignment, size, pool->log);\n    if (p == NULL) {\n        return NULL;\n    }\n\n    large = ngx_palloc_small(pool, sizeof(ngx_pool_large_t), 1);\n    if (large == NULL) {\n        ngx_free(p);\n        return NULL;\n    }\n\n    large->alloc = p;\n    large->next = pool->large;\n    pool->large = large;\n\n    return p;\n}\n\n\nngx_int_t\nngx_pfree(ngx_pool_t *pool, void *p)\n{\n    ngx_pool_large_t  *l;\n\n    for (l = pool->large; l; l = l->next) {\n        if (p == l->alloc) {\n            ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, pool->log, 0,\n                           \"free: %p\", l->alloc);\n            ngx_free(l->alloc);\n            l->alloc = NULL;\n\n            return NGX_OK;\n        }\n    }\n\n    return NGX_DECLINED;\n}\n\n\nvoid *\nngx_pcalloc(ngx_pool_t *pool, size_t size)\n{\n    void *p;\n\n    p = ngx_palloc(pool, size);\n    if (p) {\n        ngx_memzero(p, size);\n    }\n\n    return p;\n}\n\n\n#if (T_DEPRECATED)\nvoid *\nngx_prealloc(ngx_pool_t *pool, void *p, size_t old_size, size_t new_size)\n{\n    void *new;\n    ngx_pool_t *node;\n\n    if (p == NULL) {\n        return ngx_palloc(pool, new_size);\n    }\n\n    if (new_size == 0) {\n        if ((u_char *) p + old_size == pool->d.last) {\n           pool->d.last = p;\n        } else {\n           ngx_pfree(pool, p);\n        }\n\n        return NULL;\n    }\n\n    if (old_size <= pool->max) {\n        for (node = pool; node; node = node->d.next) {\n            if ((u_char *)p + old_size == node->d.last\n                && (u_char *)p + new_size <= node->d.end) {\n                node->d.last = (u_char *)p + new_size;\n                return p;\n            }\n        }\n    }\n\n    if (new_size <= old_size) {\n       return p;\n    }\n\n    new = ngx_palloc(pool, new_size);\n    if (new == NULL) {\n        return NULL;\n    }\n\n    ngx_memcpy(new, p, old_size);\n\n    ngx_pfree(pool, p);\n\n    return new;\n}\n#endif\n\n\nngx_pool_cleanup_t *\nngx_pool_cleanup_add(ngx_pool_t *p, size_t size)\n{\n    ngx_pool_cleanup_t  *c;\n\n    c = ngx_palloc(p, sizeof(ngx_pool_cleanup_t));\n    if (c == NULL) {\n        return NULL;\n    }\n\n    if (size) {\n        c->data = ngx_palloc(p, size);\n        if (c->data == NULL) {\n            return NULL;\n        }\n\n    } else {\n        c->data = NULL;\n    }\n\n    c->handler = NULL;\n    c->next = p->cleanup;\n\n    p->cleanup = c;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, p->log, 0, \"add cleanup: %p\", c);\n\n    return c;\n}\n\n\nvoid\nngx_pool_run_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\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\nvoid\nngx_pool_cleanup_file(void *data)\n{\n    ngx_pool_cleanup_file_t  *c = data;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, c->log, 0, \"file cleanup: fd:%d\",\n                   c->fd);\n\n    if (ngx_close_file(c->fd) == NGX_FILE_ERROR) {\n        ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,\n                      ngx_close_file_n \" \\\"%s\\\" failed\", c->name);\n    }\n}\n\n\nvoid\nngx_pool_delete_file(void *data)\n{\n    ngx_pool_cleanup_file_t  *c = data;\n\n    ngx_err_t  err;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, c->log, 0, \"file cleanup: fd:%d %s\",\n                   c->fd, c->name);\n\n    if (ngx_delete_file(c->name) == NGX_FILE_ERROR) {\n        err = ngx_errno;\n\n        if (err != NGX_ENOENT) {\n            ngx_log_error(NGX_LOG_CRIT, c->log, err,\n                          ngx_delete_file_n \" \\\"%s\\\" failed\", c->name);\n        }\n    }\n\n    if (ngx_close_file(c->fd) == NGX_FILE_ERROR) {\n        ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,\n                      ngx_close_file_n \" \\\"%s\\\" failed\", c->name);\n    }\n}\n\n\n#if 0\n\nstatic void *\nngx_get_cached_block(size_t size)\n{\n    void                     *p;\n    ngx_cached_block_slot_t  *slot;\n\n    if (ngx_cycle->cache == NULL) {\n        return NULL;\n    }\n\n    slot = &ngx_cycle->cache[(size + ngx_pagesize - 1) / ngx_pagesize];\n\n    slot->tries++;\n\n    if (slot->number) {\n        p = slot->block;\n        slot->block = slot->block->next;\n        slot->number--;\n        return p;\n    }\n\n    return NULL;\n}\n\n#endif\n"
  },
  {
    "path": "src/core/ngx_palloc.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_PALLOC_H_INCLUDED_\n#define _NGX_PALLOC_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\n/*\n * NGX_MAX_ALLOC_FROM_POOL should be (ngx_pagesize - 1), i.e. 4095 on x86.\n * On Windows NT it decreases a number of locked pages in a kernel.\n */\n#define NGX_MAX_ALLOC_FROM_POOL  (ngx_pagesize - 1)\n\n#define NGX_DEFAULT_POOL_SIZE    (16 * 1024)\n\n#define NGX_POOL_ALIGNMENT       16\n#define NGX_MIN_POOL_SIZE                                                     \\\n    ngx_align((sizeof(ngx_pool_t) + 2 * sizeof(ngx_pool_large_t)),            \\\n              NGX_POOL_ALIGNMENT)\n\n\ntypedef void (*ngx_pool_cleanup_pt)(void *data);\n\ntypedef struct ngx_pool_cleanup_s  ngx_pool_cleanup_t;\n\nstruct ngx_pool_cleanup_s {\n    ngx_pool_cleanup_pt   handler;\n    void                 *data;\n    ngx_pool_cleanup_t   *next;\n};\n\n\ntypedef struct ngx_pool_large_s  ngx_pool_large_t;\n\nstruct ngx_pool_large_s {\n    ngx_pool_large_t     *next;\n    void                 *alloc;\n};\n\n\ntypedef struct {\n    u_char               *last;\n    u_char               *end;\n    ngx_pool_t           *next;\n    ngx_uint_t            failed;\n} ngx_pool_data_t;\n\n\nstruct ngx_pool_s {\n    ngx_pool_data_t       d;\n    size_t                max;\n    ngx_pool_t           *current;\n    ngx_chain_t          *chain;\n    ngx_pool_large_t     *large;\n    ngx_pool_cleanup_t   *cleanup;\n    ngx_log_t            *log;\n};\n\n\ntypedef struct {\n    ngx_fd_t              fd;\n    u_char               *name;\n    ngx_log_t            *log;\n} ngx_pool_cleanup_file_t;\n\n#if (T_DEPRECATED)\nvoid *ngx_prealloc(ngx_pool_t *pool, void *p, size_t old_size, size_t new_size);\n#endif\n\nngx_pool_t *ngx_create_pool(size_t size, ngx_log_t *log);\nvoid ngx_destroy_pool(ngx_pool_t *pool);\nvoid ngx_reset_pool(ngx_pool_t *pool);\n\nvoid *ngx_palloc(ngx_pool_t *pool, size_t size);\nvoid *ngx_pnalloc(ngx_pool_t *pool, size_t size);\nvoid *ngx_pcalloc(ngx_pool_t *pool, size_t size);\nvoid *ngx_pmemalign(ngx_pool_t *pool, size_t size, size_t alignment);\nngx_int_t ngx_pfree(ngx_pool_t *pool, void *p);\n\n\nngx_pool_cleanup_t *ngx_pool_cleanup_add(ngx_pool_t *p, size_t size);\nvoid ngx_pool_run_cleanup_file(ngx_pool_t *p, ngx_fd_t fd);\nvoid ngx_pool_cleanup_file(void *data);\nvoid ngx_pool_delete_file(void *data);\n\n\n#endif /* _NGX_PALLOC_H_INCLUDED_ */\n"
  },
  {
    "path": "src/core/ngx_parse.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\nssize_t\nngx_parse_size(ngx_str_t *line)\n{\n    u_char   unit;\n    size_t   len;\n    ssize_t  size, scale, max;\n\n    len = line->len;\n\n    if (len == 0) {\n        return NGX_ERROR;\n    }\n\n    unit = line->data[len - 1];\n\n    switch (unit) {\n    case 'K':\n    case 'k':\n        len--;\n        max = NGX_MAX_SIZE_T_VALUE / 1024;\n        scale = 1024;\n        break;\n\n    case 'M':\n    case 'm':\n        len--;\n        max = NGX_MAX_SIZE_T_VALUE / (1024 * 1024);\n        scale = 1024 * 1024;\n        break;\n\n#if (T_NGX_HTTP_SYSGUARD)\n    case 'G':\n    case 'g':\n        len--;\n        max = NGX_MAX_SIZE_T_VALUE / (1024 * 1024 * 1024);\n        scale = 1024 * 1024 * 1024;\n        break;\n#endif\n\n    default:\n        max = NGX_MAX_SIZE_T_VALUE;\n        scale = 1;\n    }\n\n    size = ngx_atosz(line->data, len);\n    if (size == NGX_ERROR || size > max) {\n        return NGX_ERROR;\n    }\n\n    size *= scale;\n\n    return size;\n}\n\n\noff_t\nngx_parse_offset(ngx_str_t *line)\n{\n    u_char  unit;\n    off_t   offset, scale, max;\n    size_t  len;\n\n    len = line->len;\n\n    if (len == 0) {\n        return NGX_ERROR;\n    }\n\n    unit = line->data[len - 1];\n\n    switch (unit) {\n    case 'K':\n    case 'k':\n        len--;\n        max = NGX_MAX_OFF_T_VALUE / 1024;\n        scale = 1024;\n        break;\n\n    case 'M':\n    case 'm':\n        len--;\n        max = NGX_MAX_OFF_T_VALUE / (1024 * 1024);\n        scale = 1024 * 1024;\n        break;\n\n    case 'G':\n    case 'g':\n        len--;\n        max = NGX_MAX_OFF_T_VALUE / (1024 * 1024 * 1024);\n        scale = 1024 * 1024 * 1024;\n        break;\n\n    default:\n        max = NGX_MAX_OFF_T_VALUE;\n        scale = 1;\n    }\n\n    offset = ngx_atoof(line->data, len);\n    if (offset == NGX_ERROR || offset > max) {\n        return NGX_ERROR;\n    }\n\n    offset *= scale;\n\n    return offset;\n}\n\n\nngx_int_t\nngx_parse_time(ngx_str_t *line, ngx_uint_t is_sec)\n{\n    u_char      *p, *last;\n    ngx_int_t    value, total, scale;\n    ngx_int_t    max, cutoff, cutlim;\n    ngx_uint_t   valid;\n    enum {\n        st_start = 0,\n        st_year,\n        st_month,\n        st_week,\n        st_day,\n        st_hour,\n        st_min,\n        st_sec,\n        st_msec,\n        st_last\n    } step;\n\n    valid = 0;\n    value = 0;\n    total = 0;\n    cutoff = NGX_MAX_INT_T_VALUE / 10;\n    cutlim = NGX_MAX_INT_T_VALUE % 10;\n    step = is_sec ? st_start : st_month;\n\n    p = line->data;\n    last = p + line->len;\n\n    while (p < last) {\n\n        if (*p >= '0' && *p <= '9') {\n            if (value >= cutoff && (value > cutoff || *p - '0' > cutlim)) {\n                return NGX_ERROR;\n            }\n\n            value = value * 10 + (*p++ - '0');\n            valid = 1;\n            continue;\n        }\n\n        switch (*p++) {\n\n        case 'y':\n            if (step > st_start) {\n                return NGX_ERROR;\n            }\n            step = st_year;\n            max = NGX_MAX_INT_T_VALUE / (60 * 60 * 24 * 365);\n            scale = 60 * 60 * 24 * 365;\n            break;\n\n        case 'M':\n            if (step >= st_month) {\n                return NGX_ERROR;\n            }\n            step = st_month;\n            max = NGX_MAX_INT_T_VALUE / (60 * 60 * 24 * 30);\n            scale = 60 * 60 * 24 * 30;\n            break;\n\n        case 'w':\n            if (step >= st_week) {\n                return NGX_ERROR;\n            }\n            step = st_week;\n            max = NGX_MAX_INT_T_VALUE / (60 * 60 * 24 * 7);\n            scale = 60 * 60 * 24 * 7;\n            break;\n\n        case 'd':\n            if (step >= st_day) {\n                return NGX_ERROR;\n            }\n            step = st_day;\n            max = NGX_MAX_INT_T_VALUE / (60 * 60 * 24);\n            scale = 60 * 60 * 24;\n            break;\n\n        case 'h':\n            if (step >= st_hour) {\n                return NGX_ERROR;\n            }\n            step = st_hour;\n            max = NGX_MAX_INT_T_VALUE / (60 * 60);\n            scale = 60 * 60;\n            break;\n\n        case 'm':\n            if (p < last && *p == 's') {\n                if (is_sec || step >= st_msec) {\n                    return NGX_ERROR;\n                }\n                p++;\n                step = st_msec;\n                max = NGX_MAX_INT_T_VALUE;\n                scale = 1;\n                break;\n            }\n\n            if (step >= st_min) {\n                return NGX_ERROR;\n            }\n            step = st_min;\n            max = NGX_MAX_INT_T_VALUE / 60;\n            scale = 60;\n            break;\n\n        case 's':\n            if (step >= st_sec) {\n                return NGX_ERROR;\n            }\n            step = st_sec;\n            max = NGX_MAX_INT_T_VALUE;\n            scale = 1;\n            break;\n\n        case ' ':\n            if (step >= st_sec) {\n                return NGX_ERROR;\n            }\n            step = st_last;\n            max = NGX_MAX_INT_T_VALUE;\n            scale = 1;\n            break;\n\n        default:\n            return NGX_ERROR;\n        }\n\n        if (step != st_msec && !is_sec) {\n            scale *= 1000;\n            max /= 1000;\n        }\n\n        if (value > max) {\n            return NGX_ERROR;\n        }\n\n        value *= scale;\n\n        if (total > NGX_MAX_INT_T_VALUE - value) {\n            return NGX_ERROR;\n        }\n\n        total += value;\n\n        value = 0;\n\n        while (p < last && *p == ' ') {\n            p++;\n        }\n    }\n\n    if (!valid) {\n        return NGX_ERROR;\n    }\n\n    if (!is_sec) {\n        if (value > NGX_MAX_INT_T_VALUE / 1000) {\n            return NGX_ERROR;\n        }\n\n        value *= 1000;\n    }\n\n    if (total > NGX_MAX_INT_T_VALUE - value) {\n        return NGX_ERROR;\n    }\n\n    return total + value;\n}\n"
  },
  {
    "path": "src/core/ngx_parse.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_PARSE_H_INCLUDED_\n#define _NGX_PARSE_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\nssize_t ngx_parse_size(ngx_str_t *line);\noff_t ngx_parse_offset(ngx_str_t *line);\nngx_int_t ngx_parse_time(ngx_str_t *line, ngx_uint_t is_sec);\n\n\n#endif /* _NGX_PARSE_H_INCLUDED_ */\n"
  },
  {
    "path": "src/core/ngx_parse_time.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\nstatic ngx_uint_t  mday[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };\n\ntime_t\nngx_parse_http_time(u_char *value, size_t len)\n{\n    u_char      *p, *end;\n    ngx_int_t    month;\n    ngx_uint_t   day, year, hour, min, sec;\n    uint64_t     time;\n    enum {\n        no = 0,\n        rfc822,   /* Tue, 10 Nov 2002 23:50:13   */\n        rfc850,   /* Tuesday, 10-Dec-02 23:50:13 */\n        isoc      /* Tue Dec 10 23:50:13 2002    */\n    } fmt;\n\n    fmt = 0;\n    end = value + len;\n\n#if (NGX_SUPPRESS_WARN)\n    day = 32;\n    year = 2038;\n#endif\n\n    for (p = value; p < end; p++) {\n        if (*p == ',') {\n            break;\n        }\n\n        if (*p == ' ') {\n            fmt = isoc;\n            break;\n        }\n    }\n\n    for (p++; p < end; p++) {\n        if (*p != ' ') {\n            break;\n        }\n    }\n\n    if (end - p < 18) {\n        return NGX_ERROR;\n    }\n\n    if (fmt != isoc) {\n        if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') {\n            return NGX_ERROR;\n        }\n\n        day = (*p - '0') * 10 + (*(p + 1) - '0');\n        p += 2;\n\n        if (*p == ' ') {\n            if (end - p < 18) {\n                return NGX_ERROR;\n            }\n            fmt = rfc822;\n\n        } else if (*p == '-') {\n            fmt = rfc850;\n\n        } else {\n            return NGX_ERROR;\n        }\n\n        p++;\n    }\n\n    switch (*p) {\n\n    case 'J':\n        month = *(p + 1) == 'a' ? 0 : *(p + 2) == 'n' ? 5 : 6;\n        break;\n\n    case 'F':\n        month = 1;\n        break;\n\n    case 'M':\n        month = *(p + 2) == 'r' ? 2 : 4;\n        break;\n\n    case 'A':\n        month = *(p + 1) == 'p' ? 3 : 7;\n        break;\n\n    case 'S':\n        month = 8;\n        break;\n\n    case 'O':\n        month = 9;\n        break;\n\n    case 'N':\n        month = 10;\n        break;\n\n    case 'D':\n        month = 11;\n        break;\n\n    default:\n        return NGX_ERROR;\n    }\n\n    p += 3;\n\n    if ((fmt == rfc822 && *p != ' ') || (fmt == rfc850 && *p != '-')) {\n        return NGX_ERROR;\n    }\n\n    p++;\n\n    if (fmt == rfc822) {\n        if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9'\n            || *(p + 2) < '0' || *(p + 2) > '9'\n            || *(p + 3) < '0' || *(p + 3) > '9')\n        {\n            return NGX_ERROR;\n        }\n\n        year = (*p - '0') * 1000 + (*(p + 1) - '0') * 100\n               + (*(p + 2) - '0') * 10 + (*(p + 3) - '0');\n        p += 4;\n\n    } else if (fmt == rfc850) {\n        if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') {\n            return NGX_ERROR;\n        }\n\n        year = (*p - '0') * 10 + (*(p + 1) - '0');\n        year += (year < 70) ? 2000 : 1900;\n        p += 2;\n    }\n\n    if (fmt == isoc) {\n        if (*p == ' ') {\n            p++;\n        }\n\n        if (*p < '0' || *p > '9') {\n            return NGX_ERROR;\n        }\n\n        day = *p++ - '0';\n\n        if (*p != ' ') {\n            if (*p < '0' || *p > '9') {\n                return NGX_ERROR;\n            }\n\n            day = day * 10 + (*p++ - '0');\n        }\n\n        if (end - p < 14) {\n            return NGX_ERROR;\n        }\n    }\n\n    if (*p++ != ' ') {\n        return NGX_ERROR;\n    }\n\n    if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') {\n        return NGX_ERROR;\n    }\n\n    hour = (*p - '0') * 10 + (*(p + 1) - '0');\n    p += 2;\n\n    if (*p++ != ':') {\n        return NGX_ERROR;\n    }\n\n    if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') {\n        return NGX_ERROR;\n    }\n\n    min = (*p - '0') * 10 + (*(p + 1) - '0');\n    p += 2;\n\n    if (*p++ != ':') {\n        return NGX_ERROR;\n    }\n\n    if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9') {\n        return NGX_ERROR;\n    }\n\n    sec = (*p - '0') * 10 + (*(p + 1) - '0');\n\n    if (fmt == isoc) {\n        p += 2;\n\n        if (*p++ != ' ') {\n            return NGX_ERROR;\n        }\n\n        if (*p < '0' || *p > '9' || *(p + 1) < '0' || *(p + 1) > '9'\n            || *(p + 2) < '0' || *(p + 2) > '9'\n            || *(p + 3) < '0' || *(p + 3) > '9')\n        {\n            return NGX_ERROR;\n        }\n\n        year = (*p - '0') * 1000 + (*(p + 1) - '0') * 100\n               + (*(p + 2) - '0') * 10 + (*(p + 3) - '0');\n    }\n\n    if (hour > 23 || min > 59 || sec > 59) {\n        return NGX_ERROR;\n    }\n\n    if (day == 29 && month == 1) {\n        if ((year & 3) || ((year % 100 == 0) && (year % 400) != 0)) {\n            return NGX_ERROR;\n        }\n\n    } else if (day > mday[month]) {\n        return NGX_ERROR;\n    }\n\n    /*\n     * shift new year to March 1 and start months from 1 (not 0),\n     * it is needed for Gauss' formula\n     */\n\n    if (--month <= 0) {\n        month += 12;\n        year -= 1;\n    }\n\n    /* Gauss' formula for Gregorian days since March 1, 1 BC */\n\n    time = (uint64_t) (\n            /* days in years including leap years since March 1, 1 BC */\n\n            365 * year + year / 4 - year / 100 + year / 400\n\n            /* days before the month */\n\n            + 367 * month / 12 - 30\n\n            /* days before the day */\n\n            + day - 1\n\n            /*\n             * 719527 days were between March 1, 1 BC and March 1, 1970,\n             * 31 and 28 days were in January and February 1970\n             */\n\n            - 719527 + 31 + 28) * 86400 + hour * 3600 + min * 60 + sec;\n\n#if (NGX_TIME_T_SIZE <= 4)\n\n    if (time > 0x7fffffff) {\n        return NGX_ERROR;\n    }\n\n#endif\n\n    return (time_t) time;\n}\n"
  },
  {
    "path": "src/core/ngx_parse_time.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_PARSE_TIME_H_INCLUDED_\n#define _NGX_PARSE_TIME_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\ntime_t ngx_parse_http_time(u_char *value, size_t len);\n\n/* compatibility */\n#define ngx_http_parse_time(value, len)  ngx_parse_http_time(value, len)\n\n\n#endif /* _NGX_PARSE_TIME_H_INCLUDED_ */\n"
  },
  {
    "path": "src/core/ngx_proxy_protocol.c",
    "content": "\n/*\n * Copyright (C) Roman Arutyunyan\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\n#define NGX_PROXY_PROTOCOL_AF_INET          1\n#define NGX_PROXY_PROTOCOL_AF_INET6         2\n\n\n#define ngx_proxy_protocol_parse_uint16(p)                                    \\\n    ( ((uint16_t) (p)[0] << 8)                                                \\\n    + (           (p)[1]) )\n\n#define ngx_proxy_protocol_parse_uint32(p)                                    \\\n    ( ((uint32_t) (p)[0] << 24)                                               \\\n    + (           (p)[1] << 16)                                               \\\n    + (           (p)[2] << 8)                                                \\\n    + (           (p)[3]) )\n\n\ntypedef struct {\n    u_char                                  signature[12];\n    u_char                                  version_command;\n    u_char                                  family_transport;\n    u_char                                  len[2];\n} ngx_proxy_protocol_header_t;\n\n\ntypedef struct {\n    u_char                                  src_addr[4];\n    u_char                                  dst_addr[4];\n    u_char                                  src_port[2];\n    u_char                                  dst_port[2];\n} ngx_proxy_protocol_inet_addrs_t;\n\n\ntypedef struct {\n    u_char                                  src_addr[16];\n    u_char                                  dst_addr[16];\n    u_char                                  src_port[2];\n    u_char                                  dst_port[2];\n} ngx_proxy_protocol_inet6_addrs_t;\n\n\ntypedef struct {\n    u_char                                  type;\n    u_char                                  len[2];\n} ngx_proxy_protocol_tlv_t;\n\n\ntypedef struct {\n    u_char                                  client;\n    u_char                                  verify[4];\n} ngx_proxy_protocol_tlv_ssl_t;\n\n\ntypedef struct {\n    ngx_str_t                               name;\n    ngx_uint_t                              type;\n} ngx_proxy_protocol_tlv_entry_t;\n\n\nstatic u_char *ngx_proxy_protocol_read_addr(ngx_connection_t *c, u_char *p,\n    u_char *last, ngx_str_t *addr);\nstatic u_char *ngx_proxy_protocol_read_port(u_char *p, u_char *last,\n    in_port_t *port, u_char sep);\nstatic u_char *ngx_proxy_protocol_v2_read(ngx_connection_t *c, u_char *buf,\n    u_char *last);\nstatic ngx_int_t ngx_proxy_protocol_lookup_tlv(ngx_connection_t *c,\n    ngx_str_t *tlvs, ngx_uint_t type, ngx_str_t *value);\n\n\nstatic ngx_proxy_protocol_tlv_entry_t  ngx_proxy_protocol_tlv_entries[] = {\n    { ngx_string(\"alpn\"),       0x01 },\n    { ngx_string(\"authority\"),  0x02 },\n    { ngx_string(\"unique_id\"),  0x05 },\n    { ngx_string(\"ssl\"),        0x20 },\n    { ngx_string(\"netns\"),      0x30 },\n    { ngx_null_string,          0x00 }\n};\n\n\nstatic ngx_proxy_protocol_tlv_entry_t  ngx_proxy_protocol_tlv_ssl_entries[] = {\n    { ngx_string(\"version\"),    0x21 },\n    { ngx_string(\"cn\"),         0x22 },\n    { ngx_string(\"cipher\"),     0x23 },\n    { ngx_string(\"sig_alg\"),    0x24 },\n    { ngx_string(\"key_alg\"),    0x25 },\n    { ngx_null_string,          0x00 }\n};\n\n\nu_char *\nngx_proxy_protocol_read(ngx_connection_t *c, u_char *buf, u_char *last)\n{\n    size_t                 len;\n    u_char                *p;\n    ngx_proxy_protocol_t  *pp;\n\n    static const u_char signature[] = \"\\r\\n\\r\\n\\0\\r\\nQUIT\\n\";\n\n    p = buf;\n    len = last - buf;\n\n    if (len >= sizeof(ngx_proxy_protocol_header_t)\n        && ngx_memcmp(p, signature, sizeof(signature) - 1) == 0)\n    {\n        return ngx_proxy_protocol_v2_read(c, buf, last);\n    }\n\n    if (len < 8 || ngx_strncmp(p, \"PROXY \", 6) != 0) {\n        goto invalid;\n    }\n\n    p += 6;\n    len -= 6;\n\n    if (len >= 7 && ngx_strncmp(p, \"UNKNOWN\", 7) == 0) {\n        ngx_log_debug0(NGX_LOG_DEBUG_CORE, c->log, 0,\n                       \"PROXY protocol unknown protocol\");\n        p += 7;\n        goto skip;\n    }\n\n    if (len < 5 || ngx_strncmp(p, \"TCP\", 3) != 0\n        || (p[3] != '4' && p[3] != '6') || p[4] != ' ')\n    {\n        goto invalid;\n    }\n\n    p += 5;\n\n    pp = ngx_pcalloc(c->pool, sizeof(ngx_proxy_protocol_t));\n    if (pp == NULL) {\n        return NULL;\n    }\n\n    p = ngx_proxy_protocol_read_addr(c, p, last, &pp->src_addr);\n    if (p == NULL) {\n        goto invalid;\n    }\n\n    p = ngx_proxy_protocol_read_addr(c, p, last, &pp->dst_addr);\n    if (p == NULL) {\n        goto invalid;\n    }\n\n    p = ngx_proxy_protocol_read_port(p, last, &pp->src_port, ' ');\n    if (p == NULL) {\n        goto invalid;\n    }\n\n    p = ngx_proxy_protocol_read_port(p, last, &pp->dst_port, CR);\n    if (p == NULL) {\n        goto invalid;\n    }\n\n    if (p == last) {\n        goto invalid;\n    }\n\n    if (*p++ != LF) {\n        goto invalid;\n    }\n\n    ngx_log_debug4(NGX_LOG_DEBUG_CORE, c->log, 0,\n                   \"PROXY protocol src: %V %d, dst: %V %d\",\n                   &pp->src_addr, pp->src_port, &pp->dst_addr, pp->dst_port);\n\n    c->proxy_protocol = pp;\n\n    return p;\n\nskip:\n\n    for ( /* void */ ; p < last - 1; p++) {\n        if (p[0] == CR && p[1] == LF) {\n            return p + 2;\n        }\n    }\n\ninvalid:\n\n    for (p = buf; p < last; p++) {\n        if (*p == CR || *p == LF) {\n            break;\n        }\n    }\n\n    ngx_log_error(NGX_LOG_ERR, c->log, 0,\n                  \"broken header: \\\"%*s\\\"\", (size_t) (p - buf), buf);\n\n    return NULL;\n}\n\n\nstatic u_char *\nngx_proxy_protocol_read_addr(ngx_connection_t *c, u_char *p, u_char *last,\n    ngx_str_t *addr)\n{\n    size_t  len;\n    u_char  ch, *pos;\n\n    pos = p;\n\n    for ( ;; ) {\n        if (p == last) {\n            return NULL;\n        }\n\n        ch = *p++;\n\n        if (ch == ' ') {\n            break;\n        }\n\n        if (ch != ':' && ch != '.'\n            && (ch < 'a' || ch > 'f')\n            && (ch < 'A' || ch > 'F')\n            && (ch < '0' || ch > '9'))\n        {\n            return NULL;\n        }\n    }\n\n    len = p - pos - 1;\n\n    addr->data = ngx_pnalloc(c->pool, len);\n    if (addr->data == NULL) {\n        return NULL;\n    }\n\n    ngx_memcpy(addr->data, pos, len);\n    addr->len = len;\n\n    return p;\n}\n\n\nstatic u_char *\nngx_proxy_protocol_read_port(u_char *p, u_char *last, in_port_t *port,\n    u_char sep)\n{\n    size_t      len;\n    u_char     *pos;\n    ngx_int_t   n;\n\n    pos = p;\n\n    for ( ;; ) {\n        if (p == last) {\n            return NULL;\n        }\n\n        if (*p++ == sep) {\n            break;\n        }\n    }\n\n    len = p - pos - 1;\n\n    n = ngx_atoi(pos, len);\n    if (n < 0 || n > 65535) {\n        return NULL;\n    }\n\n    *port = (in_port_t) n;\n\n    return p;\n}\n\n\nu_char *\nngx_proxy_protocol_write(ngx_connection_t *c, u_char *buf, u_char *last)\n{\n    ngx_uint_t  port, lport;\n\n    if (last - buf < NGX_PROXY_PROTOCOL_V1_MAX_HEADER) {\n        ngx_log_error(NGX_LOG_ALERT, c->log, 0,\n                      \"too small buffer for PROXY protocol\");\n        return NULL;\n    }\n\n    if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) {\n        return NULL;\n    }\n\n    switch (c->sockaddr->sa_family) {\n\n    case AF_INET:\n        buf = ngx_cpymem(buf, \"PROXY TCP4 \", sizeof(\"PROXY TCP4 \") - 1);\n        break;\n\n#if (NGX_HAVE_INET6)\n    case AF_INET6:\n        buf = ngx_cpymem(buf, \"PROXY TCP6 \", sizeof(\"PROXY TCP6 \") - 1);\n        break;\n#endif\n\n    default:\n        return ngx_cpymem(buf, \"PROXY UNKNOWN\" CRLF,\n                          sizeof(\"PROXY UNKNOWN\" CRLF) - 1);\n    }\n\n    buf += ngx_sock_ntop(c->sockaddr, c->socklen, buf, last - buf, 0);\n\n    *buf++ = ' ';\n\n    buf += ngx_sock_ntop(c->local_sockaddr, c->local_socklen, buf, last - buf,\n                         0);\n\n    port = ngx_inet_get_port(c->sockaddr);\n    lport = ngx_inet_get_port(c->local_sockaddr);\n\n    return ngx_slprintf(buf, last, \" %ui %ui\" CRLF, port, lport);\n}\n\n\nstatic u_char *\nngx_proxy_protocol_v2_read(ngx_connection_t *c, u_char *buf, u_char *last)\n{\n    u_char                             *end;\n    size_t                              len;\n    socklen_t                           socklen;\n    ngx_uint_t                          version, command, family, transport;\n    ngx_sockaddr_t                      src_sockaddr, dst_sockaddr;\n    ngx_proxy_protocol_t               *pp;\n    ngx_proxy_protocol_header_t        *header;\n    ngx_proxy_protocol_inet_addrs_t    *in;\n#if (NGX_HAVE_INET6)\n    ngx_proxy_protocol_inet6_addrs_t   *in6;\n#endif\n\n    header = (ngx_proxy_protocol_header_t *) buf;\n\n    buf += sizeof(ngx_proxy_protocol_header_t);\n\n    version = header->version_command >> 4;\n\n    if (version != 2) {\n        ngx_log_error(NGX_LOG_ERR, c->log, 0,\n                      \"unknown PROXY protocol version: %ui\", version);\n        return NULL;\n    }\n\n    len = ngx_proxy_protocol_parse_uint16(header->len);\n\n    if ((size_t) (last - buf) < len) {\n        ngx_log_error(NGX_LOG_ERR, c->log, 0, \"header is too large\");\n        return NULL;\n    }\n\n    end = buf + len;\n\n    command = header->version_command & 0x0f;\n\n    /* only PROXY is supported */\n    if (command != 1) {\n        ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,\n                       \"PROXY protocol v2 unsupported command %ui\", command);\n        return end;\n    }\n\n    transport = header->family_transport & 0x0f;\n\n    /* only STREAM is supported */\n    if (transport != 1) {\n        ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,\n                       \"PROXY protocol v2 unsupported transport %ui\",\n                       transport);\n        return end;\n    }\n\n    pp = ngx_pcalloc(c->pool, sizeof(ngx_proxy_protocol_t));\n    if (pp == NULL) {\n        return NULL;\n    }\n\n    family = header->family_transport >> 4;\n\n    switch (family) {\n\n    case NGX_PROXY_PROTOCOL_AF_INET:\n\n        if ((size_t) (end - buf) < sizeof(ngx_proxy_protocol_inet_addrs_t)) {\n            return NULL;\n        }\n\n        in = (ngx_proxy_protocol_inet_addrs_t *) buf;\n\n        src_sockaddr.sockaddr_in.sin_family = AF_INET;\n        src_sockaddr.sockaddr_in.sin_port = 0;\n        ngx_memcpy(&src_sockaddr.sockaddr_in.sin_addr, in->src_addr, 4);\n\n        dst_sockaddr.sockaddr_in.sin_family = AF_INET;\n        dst_sockaddr.sockaddr_in.sin_port = 0;\n        ngx_memcpy(&dst_sockaddr.sockaddr_in.sin_addr, in->dst_addr, 4);\n\n        pp->src_port = ngx_proxy_protocol_parse_uint16(in->src_port);\n        pp->dst_port = ngx_proxy_protocol_parse_uint16(in->dst_port);\n\n        socklen = sizeof(struct sockaddr_in);\n\n        buf += sizeof(ngx_proxy_protocol_inet_addrs_t);\n\n        break;\n\n#if (NGX_HAVE_INET6)\n\n    case NGX_PROXY_PROTOCOL_AF_INET6:\n\n        if ((size_t) (end - buf) < sizeof(ngx_proxy_protocol_inet6_addrs_t)) {\n            return NULL;\n        }\n\n        in6 = (ngx_proxy_protocol_inet6_addrs_t *) buf;\n\n        src_sockaddr.sockaddr_in6.sin6_family = AF_INET6;\n        src_sockaddr.sockaddr_in6.sin6_port = 0;\n        ngx_memcpy(&src_sockaddr.sockaddr_in6.sin6_addr, in6->src_addr, 16);\n\n        dst_sockaddr.sockaddr_in6.sin6_family = AF_INET6;\n        dst_sockaddr.sockaddr_in6.sin6_port = 0;\n        ngx_memcpy(&dst_sockaddr.sockaddr_in6.sin6_addr, in6->dst_addr, 16);\n\n        pp->src_port = ngx_proxy_protocol_parse_uint16(in6->src_port);\n        pp->dst_port = ngx_proxy_protocol_parse_uint16(in6->dst_port);\n\n        socklen = sizeof(struct sockaddr_in6);\n\n        buf += sizeof(ngx_proxy_protocol_inet6_addrs_t);\n\n        break;\n\n#endif\n\n    default:\n        ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,\n                       \"PROXY protocol v2 unsupported address family %ui\",\n                       family);\n        return end;\n    }\n\n    pp->src_addr.data = ngx_pnalloc(c->pool, NGX_SOCKADDR_STRLEN);\n    if (pp->src_addr.data == NULL) {\n        return NULL;\n    }\n\n    pp->src_addr.len = ngx_sock_ntop(&src_sockaddr.sockaddr, socklen,\n                                     pp->src_addr.data, NGX_SOCKADDR_STRLEN, 0);\n\n    pp->dst_addr.data = ngx_pnalloc(c->pool, NGX_SOCKADDR_STRLEN);\n    if (pp->dst_addr.data == NULL) {\n        return NULL;\n    }\n\n    pp->dst_addr.len = ngx_sock_ntop(&dst_sockaddr.sockaddr, socklen,\n                                     pp->dst_addr.data, NGX_SOCKADDR_STRLEN, 0);\n\n    ngx_log_debug4(NGX_LOG_DEBUG_CORE, c->log, 0,\n                   \"PROXY protocol v2 src: %V %d, dst: %V %d\",\n                   &pp->src_addr, pp->src_port, &pp->dst_addr, pp->dst_port);\n\n    if (buf < end) {\n        pp->tlvs.data = ngx_pnalloc(c->pool, end - buf);\n        if (pp->tlvs.data == NULL) {\n            return NULL;\n        }\n\n        ngx_memcpy(pp->tlvs.data, buf, end - buf);\n        pp->tlvs.len = end - buf;\n    }\n\n    c->proxy_protocol = pp;\n\n    return end;\n}\n\n\nngx_int_t\nngx_proxy_protocol_get_tlv(ngx_connection_t *c, ngx_str_t *name,\n    ngx_str_t *value)\n{\n    u_char                          *p;\n    size_t                           n;\n    uint32_t                         verify;\n    ngx_str_t                        ssl, *tlvs;\n    ngx_int_t                        rc, type;\n    ngx_proxy_protocol_tlv_ssl_t    *tlv_ssl;\n    ngx_proxy_protocol_tlv_entry_t  *te;\n\n    if (c->proxy_protocol == NULL) {\n        return NGX_DECLINED;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,\n                   \"PROXY protocol v2 get tlv \\\"%V\\\"\", name);\n\n    te = ngx_proxy_protocol_tlv_entries;\n    tlvs = &c->proxy_protocol->tlvs;\n\n    p = name->data;\n    n = name->len;\n\n    if (n >= 4 && p[0] == 's' && p[1] == 's' && p[2] == 'l' && p[3] == '_') {\n\n        rc = ngx_proxy_protocol_lookup_tlv(c, tlvs, 0x20, &ssl);\n        if (rc != NGX_OK) {\n            return rc;\n        }\n\n        if (ssl.len < sizeof(ngx_proxy_protocol_tlv_ssl_t)) {\n            return NGX_ERROR;\n        }\n\n        p += 4;\n        n -= 4;\n\n        if (n == 6 && ngx_strncmp(p, \"verify\", 6) == 0) {\n\n            tlv_ssl = (ngx_proxy_protocol_tlv_ssl_t *) ssl.data;\n            verify = ngx_proxy_protocol_parse_uint32(tlv_ssl->verify);\n\n            value->data = ngx_pnalloc(c->pool, NGX_INT32_LEN);\n            if (value->data == NULL) {\n                return NGX_ERROR;\n            }\n\n            value->len = ngx_sprintf(value->data, \"%uD\", verify)\n                         - value->data;\n            return NGX_OK;\n        }\n\n        ssl.data += sizeof(ngx_proxy_protocol_tlv_ssl_t);\n        ssl.len -= sizeof(ngx_proxy_protocol_tlv_ssl_t);\n\n        te = ngx_proxy_protocol_tlv_ssl_entries;\n        tlvs = &ssl;\n    }\n\n    if (n >= 2 && p[0] == '0' && p[1] == 'x') {\n\n        type = ngx_hextoi(p + 2, n - 2);\n        if (type == NGX_ERROR) {\n            ngx_log_error(NGX_LOG_ERR, c->log, 0,\n                          \"invalid PROXY protocol TLV \\\"%V\\\"\", name);\n            return NGX_ERROR;\n        }\n\n        return ngx_proxy_protocol_lookup_tlv(c, tlvs, type, value);\n    }\n\n    for ( /* void */ ; te->type; te++) {\n        if (te->name.len == n && ngx_strncmp(te->name.data, p, n) == 0) {\n            return ngx_proxy_protocol_lookup_tlv(c, tlvs, te->type, value);\n        }\n    }\n\n    ngx_log_error(NGX_LOG_ERR, c->log, 0,\n                  \"unknown PROXY protocol TLV \\\"%V\\\"\", name);\n\n    return NGX_DECLINED;\n}\n\n\nstatic ngx_int_t\nngx_proxy_protocol_lookup_tlv(ngx_connection_t *c, ngx_str_t *tlvs,\n    ngx_uint_t type, ngx_str_t *value)\n{\n    u_char                    *p;\n    size_t                     n, len;\n    ngx_proxy_protocol_tlv_t  *tlv;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,\n                   \"PROXY protocol v2 lookup tlv:%02xi\", type);\n\n    p = tlvs->data;\n    n = tlvs->len;\n\n    while (n) {\n        if (n < sizeof(ngx_proxy_protocol_tlv_t)) {\n            ngx_log_error(NGX_LOG_ERR, c->log, 0, \"broken PROXY protocol TLV\");\n            return NGX_ERROR;\n        }\n\n        tlv = (ngx_proxy_protocol_tlv_t *) p;\n        len = ngx_proxy_protocol_parse_uint16(tlv->len);\n\n        p += sizeof(ngx_proxy_protocol_tlv_t);\n        n -= sizeof(ngx_proxy_protocol_tlv_t);\n\n        if (n < len) {\n            ngx_log_error(NGX_LOG_ERR, c->log, 0, \"broken PROXY protocol TLV\");\n            return NGX_ERROR;\n        }\n\n        if (tlv->type == type) {\n            value->data = p;\n            value->len = len;\n            return NGX_OK;\n        }\n\n        p += len;\n        n -= len;\n    }\n\n    return NGX_DECLINED;\n}\n"
  },
  {
    "path": "src/core/ngx_proxy_protocol.h",
    "content": "\n/*\n * Copyright (C) Roman Arutyunyan\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_PROXY_PROTOCOL_H_INCLUDED_\n#define _NGX_PROXY_PROTOCOL_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\n#define NGX_PROXY_PROTOCOL_V1_MAX_HEADER  107\n#define NGX_PROXY_PROTOCOL_MAX_HEADER     4096\n\n\nstruct ngx_proxy_protocol_s {\n    ngx_str_t           src_addr;\n    ngx_str_t           dst_addr;\n    in_port_t           src_port;\n    in_port_t           dst_port;\n    ngx_str_t           tlvs;\n};\n\n\nu_char *ngx_proxy_protocol_read(ngx_connection_t *c, u_char *buf,\n    u_char *last);\nu_char *ngx_proxy_protocol_write(ngx_connection_t *c, u_char *buf,\n    u_char *last);\nngx_int_t ngx_proxy_protocol_get_tlv(ngx_connection_t *c, ngx_str_t *name,\n    ngx_str_t *value);\n\n\n#endif /* _NGX_PROXY_PROTOCOL_H_INCLUDED_ */\n"
  },
  {
    "path": "src/core/ngx_queue.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\n/*\n * find the middle queue element if the queue has odd number of elements\n * or the first element of the queue's second part otherwise\n */\n\nngx_queue_t *\nngx_queue_middle(ngx_queue_t *queue)\n{\n    ngx_queue_t  *middle, *next;\n\n    middle = ngx_queue_head(queue);\n\n    if (middle == ngx_queue_last(queue)) {\n        return middle;\n    }\n\n    next = ngx_queue_head(queue);\n\n    for ( ;; ) {\n        middle = ngx_queue_next(middle);\n\n        next = ngx_queue_next(next);\n\n        if (next == ngx_queue_last(queue)) {\n            return middle;\n        }\n\n        next = ngx_queue_next(next);\n\n        if (next == ngx_queue_last(queue)) {\n            return middle;\n        }\n    }\n}\n\n\n/* the stable insertion sort */\n\nvoid\nngx_queue_sort(ngx_queue_t *queue,\n    ngx_int_t (*cmp)(const ngx_queue_t *, const ngx_queue_t *))\n{\n    ngx_queue_t  *q, *prev, *next;\n\n    q = ngx_queue_head(queue);\n\n    if (q == ngx_queue_last(queue)) {\n        return;\n    }\n\n    for (q = ngx_queue_next(q); q != ngx_queue_sentinel(queue); q = next) {\n\n        prev = ngx_queue_prev(q);\n        next = ngx_queue_next(q);\n\n        ngx_queue_remove(q);\n\n        do {\n            if (cmp(prev, q) <= 0) {\n                break;\n            }\n\n            prev = ngx_queue_prev(prev);\n\n        } while (prev != ngx_queue_sentinel(queue));\n\n        ngx_queue_insert_after(prev, q);\n    }\n}\n"
  },
  {
    "path": "src/core/ngx_queue.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\n#ifndef _NGX_QUEUE_H_INCLUDED_\n#define _NGX_QUEUE_H_INCLUDED_\n\n\ntypedef struct ngx_queue_s  ngx_queue_t;\n\nstruct ngx_queue_s {\n    ngx_queue_t  *prev;\n    ngx_queue_t  *next;\n};\n\n\n#define ngx_queue_init(q)                                                     \\\n    (q)->prev = q;                                                            \\\n    (q)->next = q\n\n\n#define ngx_queue_empty(h)                                                    \\\n    (h == (h)->prev)\n\n\n#define ngx_queue_insert_head(h, x)                                           \\\n    (x)->next = (h)->next;                                                    \\\n    (x)->next->prev = x;                                                      \\\n    (x)->prev = h;                                                            \\\n    (h)->next = x\n\n\n#define ngx_queue_insert_after   ngx_queue_insert_head\n\n\n#define ngx_queue_insert_tail(h, x)                                           \\\n    (x)->prev = (h)->prev;                                                    \\\n    (x)->prev->next = x;                                                      \\\n    (x)->next = h;                                                            \\\n    (h)->prev = x\n\n\n#define ngx_queue_head(h)                                                     \\\n    (h)->next\n\n\n#define ngx_queue_last(h)                                                     \\\n    (h)->prev\n\n\n#define ngx_queue_sentinel(h)                                                 \\\n    (h)\n\n\n#define ngx_queue_next(q)                                                     \\\n    (q)->next\n\n\n#define ngx_queue_prev(q)                                                     \\\n    (q)->prev\n\n\n#if (NGX_DEBUG)\n\n#define ngx_queue_remove(x)                                                   \\\n    (x)->next->prev = (x)->prev;                                              \\\n    (x)->prev->next = (x)->next;                                              \\\n    (x)->prev = NULL;                                                         \\\n    (x)->next = NULL\n\n#else\n\n#define ngx_queue_remove(x)                                                   \\\n    (x)->next->prev = (x)->prev;                                              \\\n    (x)->prev->next = (x)->next\n\n#endif\n\n\n#define ngx_queue_split(h, q, n)                                              \\\n    (n)->prev = (h)->prev;                                                    \\\n    (n)->prev->next = n;                                                      \\\n    (n)->next = q;                                                            \\\n    (h)->prev = (q)->prev;                                                    \\\n    (h)->prev->next = h;                                                      \\\n    (q)->prev = n;\n\n\n#define ngx_queue_add(h, n)                                                   \\\n    (h)->prev->next = (n)->next;                                              \\\n    (n)->next->prev = (h)->prev;                                              \\\n    (h)->prev = (n)->prev;                                                    \\\n    (h)->prev->next = h;\n\n\n#define ngx_queue_data(q, type, link)                                         \\\n    (type *) ((u_char *) q - offsetof(type, link))\n\n\nngx_queue_t *ngx_queue_middle(ngx_queue_t *queue);\nvoid ngx_queue_sort(ngx_queue_t *queue,\n    ngx_int_t (*cmp)(const ngx_queue_t *, const ngx_queue_t *));\n\n\n#endif /* _NGX_QUEUE_H_INCLUDED_ */\n"
  },
  {
    "path": "src/core/ngx_radix_tree.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\nstatic ngx_radix_node_t *ngx_radix_alloc(ngx_radix_tree_t *tree);\n\n\nngx_radix_tree_t *\nngx_radix_tree_create(ngx_pool_t *pool, ngx_int_t preallocate)\n{\n    uint32_t           key, mask, inc;\n    ngx_radix_tree_t  *tree;\n\n    tree = ngx_palloc(pool, sizeof(ngx_radix_tree_t));\n    if (tree == NULL) {\n        return NULL;\n    }\n\n    tree->pool = pool;\n    tree->free = NULL;\n    tree->start = NULL;\n    tree->size = 0;\n\n    tree->root = ngx_radix_alloc(tree);\n    if (tree->root == NULL) {\n        return NULL;\n    }\n\n    tree->root->right = NULL;\n    tree->root->left = NULL;\n    tree->root->parent = NULL;\n    tree->root->value = NGX_RADIX_NO_VALUE;\n\n    if (preallocate == 0) {\n        return tree;\n    }\n\n    /*\n     * Preallocation of first nodes : 0, 1, 00, 01, 10, 11, 000, 001, etc.\n     * increases TLB hits even if for first lookup iterations.\n     * On 32-bit platforms the 7 preallocated bits takes continuous 4K,\n     * 8 - 8K, 9 - 16K, etc.  On 64-bit platforms the 6 preallocated bits\n     * takes continuous 4K, 7 - 8K, 8 - 16K, etc.  There is no sense to\n     * to preallocate more than one page, because further preallocation\n     * distributes the only bit per page.  Instead, a random insertion\n     * may distribute several bits per page.\n     *\n     * Thus, by default we preallocate maximum\n     *     6 bits on amd64 (64-bit platform and 4K pages)\n     *     7 bits on i386 (32-bit platform and 4K pages)\n     *     7 bits on sparc64 in 64-bit mode (8K pages)\n     *     8 bits on sparc64 in 32-bit mode (8K pages)\n     */\n\n    if (preallocate == -1) {\n        switch (ngx_pagesize / sizeof(ngx_radix_node_t)) {\n\n        /* amd64 */\n        case 128:\n            preallocate = 6;\n            break;\n\n        /* i386, sparc64 */\n        case 256:\n            preallocate = 7;\n            break;\n\n        /* sparc64 in 32-bit mode */\n        default:\n            preallocate = 8;\n        }\n    }\n\n    mask = 0;\n    inc = 0x80000000;\n\n    while (preallocate--) {\n\n        key = 0;\n        mask >>= 1;\n        mask |= 0x80000000;\n\n        do {\n            if (ngx_radix32tree_insert(tree, key, mask, NGX_RADIX_NO_VALUE)\n                != NGX_OK)\n            {\n                return NULL;\n            }\n\n            key += inc;\n\n        } while (key);\n\n        inc >>= 1;\n    }\n\n    return tree;\n}\n\n\nngx_int_t\nngx_radix32tree_insert(ngx_radix_tree_t *tree, uint32_t key, uint32_t mask,\n    uintptr_t value)\n{\n    uint32_t           bit;\n    ngx_radix_node_t  *node, *next;\n\n    bit = 0x80000000;\n\n    node = tree->root;\n    next = tree->root;\n\n    while (bit & mask) {\n        if (key & bit) {\n            next = node->right;\n\n        } else {\n            next = node->left;\n        }\n\n        if (next == NULL) {\n            break;\n        }\n\n        bit >>= 1;\n        node = next;\n    }\n\n    if (next) {\n        if (node->value != NGX_RADIX_NO_VALUE) {\n            return NGX_BUSY;\n        }\n\n        node->value = value;\n        return NGX_OK;\n    }\n\n    while (bit & mask) {\n        next = ngx_radix_alloc(tree);\n        if (next == NULL) {\n            return NGX_ERROR;\n        }\n\n        next->right = NULL;\n        next->left = NULL;\n        next->parent = node;\n        next->value = NGX_RADIX_NO_VALUE;\n\n        if (key & bit) {\n            node->right = next;\n\n        } else {\n            node->left = next;\n        }\n\n        bit >>= 1;\n        node = next;\n    }\n\n    node->value = value;\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_radix32tree_delete(ngx_radix_tree_t *tree, uint32_t key, uint32_t mask)\n{\n    uint32_t           bit;\n    ngx_radix_node_t  *node;\n\n    bit = 0x80000000;\n    node = tree->root;\n\n    while (node && (bit & mask)) {\n        if (key & bit) {\n            node = node->right;\n\n        } else {\n            node = node->left;\n        }\n\n        bit >>= 1;\n    }\n\n    if (node == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (node->right || node->left) {\n        if (node->value != NGX_RADIX_NO_VALUE) {\n            node->value = NGX_RADIX_NO_VALUE;\n            return NGX_OK;\n        }\n\n        return NGX_ERROR;\n    }\n\n    for ( ;; ) {\n        if (node->parent->right == node) {\n            node->parent->right = NULL;\n\n        } else {\n            node->parent->left = NULL;\n        }\n\n        node->right = tree->free;\n        tree->free = node;\n\n        node = node->parent;\n\n        if (node->right || node->left) {\n            break;\n        }\n\n        if (node->value != NGX_RADIX_NO_VALUE) {\n            break;\n        }\n\n        if (node->parent == NULL) {\n            break;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nuintptr_t\nngx_radix32tree_find(ngx_radix_tree_t *tree, uint32_t key)\n{\n    uint32_t           bit;\n    uintptr_t          value;\n    ngx_radix_node_t  *node;\n\n    bit = 0x80000000;\n    value = NGX_RADIX_NO_VALUE;\n    node = tree->root;\n\n    while (node) {\n        if (node->value != NGX_RADIX_NO_VALUE) {\n            value = node->value;\n        }\n\n        if (key & bit) {\n            node = node->right;\n\n        } else {\n            node = node->left;\n        }\n\n        bit >>= 1;\n    }\n\n    return value;\n}\n\n\n#if (NGX_HAVE_INET6)\n\nngx_int_t\nngx_radix128tree_insert(ngx_radix_tree_t *tree, u_char *key, u_char *mask,\n    uintptr_t value)\n{\n    u_char             bit;\n    ngx_uint_t         i;\n    ngx_radix_node_t  *node, *next;\n\n    i = 0;\n    bit = 0x80;\n\n    node = tree->root;\n    next = tree->root;\n\n    while (bit & mask[i]) {\n        if (key[i] & bit) {\n            next = node->right;\n\n        } else {\n            next = node->left;\n        }\n\n        if (next == NULL) {\n            break;\n        }\n\n        bit >>= 1;\n        node = next;\n\n        if (bit == 0) {\n            if (++i == 16) {\n                break;\n            }\n\n            bit = 0x80;\n        }\n    }\n\n    if (next) {\n        if (node->value != NGX_RADIX_NO_VALUE) {\n            return NGX_BUSY;\n        }\n\n        node->value = value;\n        return NGX_OK;\n    }\n\n    while (bit & mask[i]) {\n        next = ngx_radix_alloc(tree);\n        if (next == NULL) {\n            return NGX_ERROR;\n        }\n\n        next->right = NULL;\n        next->left = NULL;\n        next->parent = node;\n        next->value = NGX_RADIX_NO_VALUE;\n\n        if (key[i] & bit) {\n            node->right = next;\n\n        } else {\n            node->left = next;\n        }\n\n        bit >>= 1;\n        node = next;\n\n        if (bit == 0) {\n            if (++i == 16) {\n                break;\n            }\n\n            bit = 0x80;\n        }\n    }\n\n    node->value = value;\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_radix128tree_delete(ngx_radix_tree_t *tree, u_char *key, u_char *mask)\n{\n    u_char             bit;\n    ngx_uint_t         i;\n    ngx_radix_node_t  *node;\n\n    i = 0;\n    bit = 0x80;\n    node = tree->root;\n\n    while (node && (bit & mask[i])) {\n        if (key[i] & bit) {\n            node = node->right;\n\n        } else {\n            node = node->left;\n        }\n\n        bit >>= 1;\n\n        if (bit == 0) {\n            if (++i == 16) {\n                break;\n            }\n\n            bit = 0x80;\n        }\n    }\n\n    if (node == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (node->right || node->left) {\n        if (node->value != NGX_RADIX_NO_VALUE) {\n            node->value = NGX_RADIX_NO_VALUE;\n            return NGX_OK;\n        }\n\n        return NGX_ERROR;\n    }\n\n    for ( ;; ) {\n        if (node->parent->right == node) {\n            node->parent->right = NULL;\n\n        } else {\n            node->parent->left = NULL;\n        }\n\n        node->right = tree->free;\n        tree->free = node;\n\n        node = node->parent;\n\n        if (node->right || node->left) {\n            break;\n        }\n\n        if (node->value != NGX_RADIX_NO_VALUE) {\n            break;\n        }\n\n        if (node->parent == NULL) {\n            break;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nuintptr_t\nngx_radix128tree_find(ngx_radix_tree_t *tree, u_char *key)\n{\n    u_char             bit;\n    uintptr_t          value;\n    ngx_uint_t         i;\n    ngx_radix_node_t  *node;\n\n    i = 0;\n    bit = 0x80;\n    value = NGX_RADIX_NO_VALUE;\n    node = tree->root;\n\n    while (node) {\n        if (node->value != NGX_RADIX_NO_VALUE) {\n            value = node->value;\n        }\n\n        if (key[i] & bit) {\n            node = node->right;\n\n        } else {\n            node = node->left;\n        }\n\n        bit >>= 1;\n\n        if (bit == 0) {\n            i++;\n            bit = 0x80;\n        }\n    }\n\n    return value;\n}\n\n#endif\n\n\nstatic ngx_radix_node_t *\nngx_radix_alloc(ngx_radix_tree_t *tree)\n{\n    ngx_radix_node_t  *p;\n\n    if (tree->free) {\n        p = tree->free;\n        tree->free = tree->free->right;\n        return p;\n    }\n\n    if (tree->size < sizeof(ngx_radix_node_t)) {\n        tree->start = ngx_pmemalign(tree->pool, ngx_pagesize, ngx_pagesize);\n        if (tree->start == NULL) {\n            return NULL;\n        }\n\n        tree->size = ngx_pagesize;\n    }\n\n    p = (ngx_radix_node_t *) tree->start;\n    tree->start += sizeof(ngx_radix_node_t);\n    tree->size -= sizeof(ngx_radix_node_t);\n\n    return p;\n}\n"
  },
  {
    "path": "src/core/ngx_radix_tree.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_RADIX_TREE_H_INCLUDED_\n#define _NGX_RADIX_TREE_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\n#define NGX_RADIX_NO_VALUE   (uintptr_t) -1\n\ntypedef struct ngx_radix_node_s  ngx_radix_node_t;\n\nstruct ngx_radix_node_s {\n    ngx_radix_node_t  *right;\n    ngx_radix_node_t  *left;\n    ngx_radix_node_t  *parent;\n    uintptr_t          value;\n};\n\n\ntypedef struct {\n    ngx_radix_node_t  *root;\n    ngx_pool_t        *pool;\n    ngx_radix_node_t  *free;\n    char              *start;\n    size_t             size;\n} ngx_radix_tree_t;\n\n\nngx_radix_tree_t *ngx_radix_tree_create(ngx_pool_t *pool,\n    ngx_int_t preallocate);\n\nngx_int_t ngx_radix32tree_insert(ngx_radix_tree_t *tree,\n    uint32_t key, uint32_t mask, uintptr_t value);\nngx_int_t ngx_radix32tree_delete(ngx_radix_tree_t *tree,\n    uint32_t key, uint32_t mask);\nuintptr_t ngx_radix32tree_find(ngx_radix_tree_t *tree, uint32_t key);\n\n#if (NGX_HAVE_INET6)\nngx_int_t ngx_radix128tree_insert(ngx_radix_tree_t *tree,\n    u_char *key, u_char *mask, uintptr_t value);\nngx_int_t ngx_radix128tree_delete(ngx_radix_tree_t *tree,\n    u_char *key, u_char *mask);\nuintptr_t ngx_radix128tree_find(ngx_radix_tree_t *tree, u_char *key);\n#endif\n\n\n#endif /* _NGX_RADIX_TREE_H_INCLUDED_ */\n"
  },
  {
    "path": "src/core/ngx_rbtree.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\n/*\n * The red-black tree code is based on the algorithm described in\n * the \"Introduction to Algorithms\" by Cormen, Leiserson and Rivest.\n */\n\n\nstatic ngx_inline void ngx_rbtree_left_rotate(ngx_rbtree_node_t **root,\n    ngx_rbtree_node_t *sentinel, ngx_rbtree_node_t *node);\nstatic ngx_inline void ngx_rbtree_right_rotate(ngx_rbtree_node_t **root,\n    ngx_rbtree_node_t *sentinel, ngx_rbtree_node_t *node);\n\n\nvoid\nngx_rbtree_insert(ngx_rbtree_t *tree, ngx_rbtree_node_t *node)\n{\n    ngx_rbtree_node_t  **root, *temp, *sentinel;\n\n    /* a binary tree insert */\n\n    root = &tree->root;\n    sentinel = tree->sentinel;\n\n    if (*root == sentinel) {\n        node->parent = NULL;\n        node->left = sentinel;\n        node->right = sentinel;\n        ngx_rbt_black(node);\n        *root = node;\n\n        return;\n    }\n\n    tree->insert(*root, node, sentinel);\n\n    /* re-balance tree */\n\n    while (node != *root && ngx_rbt_is_red(node->parent)) {\n\n        if (node->parent == node->parent->parent->left) {\n            temp = node->parent->parent->right;\n\n            if (ngx_rbt_is_red(temp)) {\n                ngx_rbt_black(node->parent);\n                ngx_rbt_black(temp);\n                ngx_rbt_red(node->parent->parent);\n                node = node->parent->parent;\n\n            } else {\n                if (node == node->parent->right) {\n                    node = node->parent;\n                    ngx_rbtree_left_rotate(root, sentinel, node);\n                }\n\n                ngx_rbt_black(node->parent);\n                ngx_rbt_red(node->parent->parent);\n                ngx_rbtree_right_rotate(root, sentinel, node->parent->parent);\n            }\n\n        } else {\n            temp = node->parent->parent->left;\n\n            if (ngx_rbt_is_red(temp)) {\n                ngx_rbt_black(node->parent);\n                ngx_rbt_black(temp);\n                ngx_rbt_red(node->parent->parent);\n                node = node->parent->parent;\n\n            } else {\n                if (node == node->parent->left) {\n                    node = node->parent;\n                    ngx_rbtree_right_rotate(root, sentinel, node);\n                }\n\n                ngx_rbt_black(node->parent);\n                ngx_rbt_red(node->parent->parent);\n                ngx_rbtree_left_rotate(root, sentinel, node->parent->parent);\n            }\n        }\n    }\n\n    ngx_rbt_black(*root);\n}\n\n\nvoid\nngx_rbtree_insert_value(ngx_rbtree_node_t *temp, ngx_rbtree_node_t *node,\n    ngx_rbtree_node_t *sentinel)\n{\n    ngx_rbtree_node_t  **p;\n\n    for ( ;; ) {\n\n        p = (node->key < temp->key) ? &temp->left : &temp->right;\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\nvoid\nngx_rbtree_insert_timer_value(ngx_rbtree_node_t *temp, ngx_rbtree_node_t *node,\n    ngx_rbtree_node_t *sentinel)\n{\n    ngx_rbtree_node_t  **p;\n\n    for ( ;; ) {\n\n        /*\n         * Timer values\n         * 1) are spread in small range, usually several minutes,\n         * 2) and overflow each 49 days, if milliseconds are stored in 32 bits.\n         * The comparison takes into account that overflow.\n         */\n\n        /*  node->key < temp->key */\n\n        p = ((ngx_rbtree_key_int_t) (node->key - temp->key) < 0)\n            ? &temp->left : &temp->right;\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\nvoid\nngx_rbtree_delete(ngx_rbtree_t *tree, ngx_rbtree_node_t *node)\n{\n    ngx_uint_t           red;\n    ngx_rbtree_node_t  **root, *sentinel, *subst, *temp, *w;\n\n    /* a binary tree delete */\n\n    root = &tree->root;\n    sentinel = tree->sentinel;\n\n    if (node->left == sentinel) {\n        temp = node->right;\n        subst = node;\n\n    } else if (node->right == sentinel) {\n        temp = node->left;\n        subst = node;\n\n    } else {\n        subst = ngx_rbtree_min(node->right, sentinel);\n        temp = subst->right;\n    }\n\n    if (subst == *root) {\n        *root = temp;\n        ngx_rbt_black(temp);\n\n        /* DEBUG stuff */\n        node->left = NULL;\n        node->right = NULL;\n        node->parent = NULL;\n        node->key = 0;\n\n        return;\n    }\n\n    red = ngx_rbt_is_red(subst);\n\n    if (subst == subst->parent->left) {\n        subst->parent->left = temp;\n\n    } else {\n        subst->parent->right = temp;\n    }\n\n    if (subst == node) {\n\n        temp->parent = subst->parent;\n\n    } else {\n\n        if (subst->parent == node) {\n            temp->parent = subst;\n\n        } else {\n            temp->parent = subst->parent;\n        }\n\n        subst->left = node->left;\n        subst->right = node->right;\n        subst->parent = node->parent;\n        ngx_rbt_copy_color(subst, node);\n\n        if (node == *root) {\n            *root = subst;\n\n        } else {\n            if (node == node->parent->left) {\n                node->parent->left = subst;\n            } else {\n                node->parent->right = subst;\n            }\n        }\n\n        if (subst->left != sentinel) {\n            subst->left->parent = subst;\n        }\n\n        if (subst->right != sentinel) {\n            subst->right->parent = subst;\n        }\n    }\n\n    /* DEBUG stuff */\n    node->left = NULL;\n    node->right = NULL;\n    node->parent = NULL;\n    node->key = 0;\n\n    if (red) {\n        return;\n    }\n\n    /* a delete fixup */\n\n    while (temp != *root && ngx_rbt_is_black(temp)) {\n\n        if (temp == temp->parent->left) {\n            w = temp->parent->right;\n\n            if (ngx_rbt_is_red(w)) {\n                ngx_rbt_black(w);\n                ngx_rbt_red(temp->parent);\n                ngx_rbtree_left_rotate(root, sentinel, temp->parent);\n                w = temp->parent->right;\n            }\n\n            if (ngx_rbt_is_black(w->left) && ngx_rbt_is_black(w->right)) {\n                ngx_rbt_red(w);\n                temp = temp->parent;\n\n            } else {\n                if (ngx_rbt_is_black(w->right)) {\n                    ngx_rbt_black(w->left);\n                    ngx_rbt_red(w);\n                    ngx_rbtree_right_rotate(root, sentinel, w);\n                    w = temp->parent->right;\n                }\n\n                ngx_rbt_copy_color(w, temp->parent);\n                ngx_rbt_black(temp->parent);\n                ngx_rbt_black(w->right);\n                ngx_rbtree_left_rotate(root, sentinel, temp->parent);\n                temp = *root;\n            }\n\n        } else {\n            w = temp->parent->left;\n\n            if (ngx_rbt_is_red(w)) {\n                ngx_rbt_black(w);\n                ngx_rbt_red(temp->parent);\n                ngx_rbtree_right_rotate(root, sentinel, temp->parent);\n                w = temp->parent->left;\n            }\n\n            if (ngx_rbt_is_black(w->left) && ngx_rbt_is_black(w->right)) {\n                ngx_rbt_red(w);\n                temp = temp->parent;\n\n            } else {\n                if (ngx_rbt_is_black(w->left)) {\n                    ngx_rbt_black(w->right);\n                    ngx_rbt_red(w);\n                    ngx_rbtree_left_rotate(root, sentinel, w);\n                    w = temp->parent->left;\n                }\n\n                ngx_rbt_copy_color(w, temp->parent);\n                ngx_rbt_black(temp->parent);\n                ngx_rbt_black(w->left);\n                ngx_rbtree_right_rotate(root, sentinel, temp->parent);\n                temp = *root;\n            }\n        }\n    }\n\n    ngx_rbt_black(temp);\n}\n\n\nstatic ngx_inline void\nngx_rbtree_left_rotate(ngx_rbtree_node_t **root, ngx_rbtree_node_t *sentinel,\n    ngx_rbtree_node_t *node)\n{\n    ngx_rbtree_node_t  *temp;\n\n    temp = node->right;\n    node->right = temp->left;\n\n    if (temp->left != sentinel) {\n        temp->left->parent = node;\n    }\n\n    temp->parent = node->parent;\n\n    if (node == *root) {\n        *root = temp;\n\n    } else if (node == node->parent->left) {\n        node->parent->left = temp;\n\n    } else {\n        node->parent->right = temp;\n    }\n\n    temp->left = node;\n    node->parent = temp;\n}\n\n\nstatic ngx_inline void\nngx_rbtree_right_rotate(ngx_rbtree_node_t **root, ngx_rbtree_node_t *sentinel,\n    ngx_rbtree_node_t *node)\n{\n    ngx_rbtree_node_t  *temp;\n\n    temp = node->left;\n    node->left = temp->right;\n\n    if (temp->right != sentinel) {\n        temp->right->parent = node;\n    }\n\n    temp->parent = node->parent;\n\n    if (node == *root) {\n        *root = temp;\n\n    } else if (node == node->parent->right) {\n        node->parent->right = temp;\n\n    } else {\n        node->parent->left = temp;\n    }\n\n    temp->right = node;\n    node->parent = temp;\n}\n\n\nngx_rbtree_node_t *\nngx_rbtree_next(ngx_rbtree_t *tree, ngx_rbtree_node_t *node)\n{\n    ngx_rbtree_node_t  *root, *sentinel, *parent;\n\n    sentinel = tree->sentinel;\n\n    if (node->right != sentinel) {\n        return ngx_rbtree_min(node->right, sentinel);\n    }\n\n    root = tree->root;\n\n    for ( ;; ) {\n        parent = node->parent;\n\n        if (node == root) {\n            return NULL;\n        }\n\n        if (node == parent->left) {\n            return parent;\n        }\n\n        node = parent;\n    }\n}\n"
  },
  {
    "path": "src/core/ngx_rbtree.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_RBTREE_H_INCLUDED_\n#define _NGX_RBTREE_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\ntypedef ngx_uint_t  ngx_rbtree_key_t;\ntypedef ngx_int_t   ngx_rbtree_key_int_t;\n\n\ntypedef struct ngx_rbtree_node_s  ngx_rbtree_node_t;\n\nstruct ngx_rbtree_node_s {\n    ngx_rbtree_key_t       key;\n    ngx_rbtree_node_t     *left;\n    ngx_rbtree_node_t     *right;\n    ngx_rbtree_node_t     *parent;\n    u_char                 color;\n    u_char                 data;\n};\n\n\ntypedef struct ngx_rbtree_s  ngx_rbtree_t;\n\ntypedef void (*ngx_rbtree_insert_pt) (ngx_rbtree_node_t *root,\n    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);\n\nstruct ngx_rbtree_s {\n    ngx_rbtree_node_t     *root;\n    ngx_rbtree_node_t     *sentinel;\n    ngx_rbtree_insert_pt   insert;\n};\n\n\n#define ngx_rbtree_init(tree, s, i)                                           \\\n    ngx_rbtree_sentinel_init(s);                                              \\\n    (tree)->root = s;                                                         \\\n    (tree)->sentinel = s;                                                     \\\n    (tree)->insert = i\n\n#define ngx_rbtree_data(node, type, link)                                     \\\n    (type *) ((u_char *) (node) - offsetof(type, link))\n\n\nvoid ngx_rbtree_insert(ngx_rbtree_t *tree, ngx_rbtree_node_t *node);\nvoid ngx_rbtree_delete(ngx_rbtree_t *tree, ngx_rbtree_node_t *node);\nvoid ngx_rbtree_insert_value(ngx_rbtree_node_t *root, ngx_rbtree_node_t *node,\n    ngx_rbtree_node_t *sentinel);\nvoid ngx_rbtree_insert_timer_value(ngx_rbtree_node_t *root,\n    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);\nngx_rbtree_node_t *ngx_rbtree_next(ngx_rbtree_t *tree,\n    ngx_rbtree_node_t *node);\n\n\n#define ngx_rbt_red(node)               ((node)->color = 1)\n#define ngx_rbt_black(node)             ((node)->color = 0)\n#define ngx_rbt_is_red(node)            ((node)->color)\n#define ngx_rbt_is_black(node)          (!ngx_rbt_is_red(node))\n#define ngx_rbt_copy_color(n1, n2)      (n1->color = n2->color)\n\n\n/* a sentinel must be black */\n\n#define ngx_rbtree_sentinel_init(node)  ngx_rbt_black(node)\n\n\nstatic ngx_inline ngx_rbtree_node_t *\nngx_rbtree_min(ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)\n{\n    while (node->left != sentinel) {\n        node = node->left;\n    }\n\n    return node;\n}\n\n\n#endif /* _NGX_RBTREE_H_INCLUDED_ */\n"
  },
  {
    "path": "src/core/ngx_regex.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\ntypedef struct {\n    ngx_flag_t   pcre_jit;\n    ngx_list_t  *studies;\n} ngx_regex_conf_t;\n\n\nstatic ngx_inline void ngx_regex_malloc_init(ngx_pool_t *pool);\nstatic ngx_inline void ngx_regex_malloc_done(void);\n\n#if (NGX_PCRE2)\nstatic void * ngx_libc_cdecl ngx_regex_malloc(size_t size, void *data);\nstatic void ngx_libc_cdecl ngx_regex_free(void *p, void *data);\n#else\nstatic void * ngx_libc_cdecl ngx_regex_malloc(size_t size);\nstatic void ngx_libc_cdecl ngx_regex_free(void *p);\n#endif\nstatic void ngx_regex_cleanup(void *data);\n\nstatic ngx_int_t ngx_regex_module_init(ngx_cycle_t *cycle);\n\nstatic void *ngx_regex_create_conf(ngx_cycle_t *cycle);\nstatic char *ngx_regex_init_conf(ngx_cycle_t *cycle, void *conf);\n\nstatic char *ngx_regex_pcre_jit(ngx_conf_t *cf, void *post, void *data);\nstatic ngx_conf_post_t  ngx_regex_pcre_jit_post = { ngx_regex_pcre_jit };\n\n\nstatic ngx_command_t  ngx_regex_commands[] = {\n\n    { ngx_string(\"pcre_jit\"),\n      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      0,\n      offsetof(ngx_regex_conf_t, pcre_jit),\n      &ngx_regex_pcre_jit_post },\n\n      ngx_null_command\n};\n\n\nstatic ngx_core_module_t  ngx_regex_module_ctx = {\n    ngx_string(\"regex\"),\n    ngx_regex_create_conf,\n    ngx_regex_init_conf\n};\n\n\nngx_module_t  ngx_regex_module = {\n    NGX_MODULE_V1,\n    &ngx_regex_module_ctx,                 /* module context */\n    ngx_regex_commands,                    /* module directives */\n    NGX_CORE_MODULE,                       /* module type */\n    NULL,                                  /* init master */\n    ngx_regex_module_init,                 /* 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_pool_t             *ngx_regex_pool;\nstatic ngx_list_t             *ngx_regex_studies;\nstatic ngx_uint_t              ngx_regex_direct_alloc;\n\n#if (NGX_PCRE2)\nstatic pcre2_compile_context  *ngx_regex_compile_context;\nstatic pcre2_match_data       *ngx_regex_match_data;\nstatic ngx_uint_t              ngx_regex_match_data_size;\n#endif\n\n\nvoid\nngx_regex_init(void)\n{\n#if !(NGX_PCRE2)\n    pcre_malloc = ngx_regex_malloc;\n    pcre_free = ngx_regex_free;\n#endif\n}\n\n\nstatic ngx_inline void\nngx_regex_malloc_init(ngx_pool_t *pool)\n{\n    ngx_regex_pool = pool;\n    ngx_regex_direct_alloc = (pool == NULL) ? 1 : 0;\n}\n\n\nstatic ngx_inline void\nngx_regex_malloc_done(void)\n{\n    ngx_regex_pool = NULL;\n    ngx_regex_direct_alloc = 0;\n}\n\n\n#if (NGX_PCRE2)\n\nngx_int_t\nngx_regex_compile(ngx_regex_compile_t *rc)\n{\n    int                     n, errcode;\n    char                   *p;\n    u_char                  errstr[128];\n    size_t                  erroff;\n    uint32_t                options;\n    pcre2_code             *re;\n    ngx_regex_elt_t        *elt;\n    pcre2_general_context  *gctx;\n    pcre2_compile_context  *cctx;\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        ngx_regex_malloc_init(NULL);\n\n        gctx = pcre2_general_context_create(ngx_regex_malloc, ngx_regex_free,\n                                            NULL);\n        if (gctx == NULL) {\n            ngx_regex_malloc_done();\n            goto nomem;\n        }\n\n        cctx = pcre2_compile_context_create(gctx);\n        if (cctx == NULL) {\n            pcre2_general_context_free(gctx);\n            ngx_regex_malloc_done();\n            goto nomem;\n        }\n\n        ngx_regex_compile_context = cctx;\n\n        pcre2_general_context_free(gctx);\n        ngx_regex_malloc_done();\n    }\n\n    options = 0;\n\n    if (rc->options & NGX_REGEX_CASELESS) {\n        options |= PCRE2_CASELESS;\n    }\n\n    if (rc->options & NGX_REGEX_MULTILINE) {\n        options |= PCRE2_MULTILINE;\n    }\n\n    if (rc->options & ~(NGX_REGEX_CASELESS|NGX_REGEX_MULTILINE)) {\n        rc->err.len = ngx_snprintf(rc->err.data, rc->err.len,\n                            \"regex \\\"%V\\\" compilation failed: invalid options\",\n                            &rc->pattern)\n                      - rc->err.data;\n        return NGX_ERROR;\n    }\n\n    ngx_regex_malloc_init(rc->pool);\n\n    re = pcre2_compile(rc->pattern.data, rc->pattern.len, options,\n                       &errcode, &erroff, ngx_regex_compile_context);\n\n    /* ensure that there is no current pool */\n    ngx_regex_malloc_done();\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 \\\"%V\\\" at \\\"%s\\\"\",\n                               errstr, &rc->pattern, rc->pattern.data + erroff)\n                          - rc->err.data;\n        }\n\n        return NGX_ERROR;\n    }\n\n    rc->regex = re;\n\n    /* do not study at runtime */\n\n    if (ngx_regex_studies != NULL) {\n        elt = ngx_list_push(ngx_regex_studies);\n        if (elt == NULL) {\n            goto nomem;\n        }\n\n        elt->regex = rc->regex;\n        elt->name = rc->pattern.data;\n    }\n\n    n = pcre2_pattern_info(re, PCRE2_INFO_CAPTURECOUNT, &rc->captures);\n    if (n < 0) {\n        p = \"pcre2_pattern_info(\\\"%V\\\", PCRE2_INFO_CAPTURECOUNT) failed: %d\";\n        goto failed;\n    }\n\n    if (rc->captures == 0) {\n        return NGX_OK;\n    }\n\n    n = pcre2_pattern_info(re, PCRE2_INFO_NAMECOUNT, &rc->named_captures);\n    if (n < 0) {\n        p = \"pcre2_pattern_info(\\\"%V\\\", PCRE2_INFO_NAMECOUNT) failed: %d\";\n        goto failed;\n    }\n\n    if (rc->named_captures == 0) {\n        return NGX_OK;\n    }\n\n    n = pcre2_pattern_info(re, PCRE2_INFO_NAMEENTRYSIZE, &rc->name_size);\n    if (n < 0) {\n        p = \"pcre2_pattern_info(\\\"%V\\\", PCRE2_INFO_NAMEENTRYSIZE) failed: %d\";\n        goto failed;\n    }\n\n    n = pcre2_pattern_info(re, PCRE2_INFO_NAMETABLE, &rc->names);\n    if (n < 0) {\n        p = \"pcre2_pattern_info(\\\"%V\\\", PCRE2_INFO_NAMETABLE) failed: %d\";\n        goto failed;\n    }\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\nngx_int_t\nngx_regex_compile(ngx_regex_compile_t *rc)\n{\n    int               n, erroff;\n    char             *p;\n    pcre             *re;\n    const char       *errstr;\n    ngx_uint_t        options;\n    ngx_regex_elt_t  *elt;\n\n    options = 0;\n\n    if (rc->options & NGX_REGEX_CASELESS) {\n        options |= PCRE_CASELESS;\n    }\n\n    if (rc->options & NGX_REGEX_MULTILINE) {\n        options |= PCRE_MULTILINE;\n    }\n\n    if (rc->options & ~(NGX_REGEX_CASELESS|NGX_REGEX_MULTILINE)) {\n        rc->err.len = ngx_snprintf(rc->err.data, rc->err.len,\n                            \"regex \\\"%V\\\" compilation failed: invalid options\",\n                            &rc->pattern)\n                      - rc->err.data;\n        return NGX_ERROR;\n    }\n\n    ngx_regex_malloc_init(rc->pool);\n\n    re = pcre_compile((const char *) rc->pattern.data, (int) options,\n                      &errstr, &erroff, NULL);\n\n    /* ensure that there is no current pool */\n    ngx_regex_malloc_done();\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\\\" at \\\"%s\\\"\",\n                               errstr, &rc->pattern, rc->pattern.data + erroff)\n                         - rc->err.data;\n        }\n\n        return NGX_ERROR;\n    }\n\n    rc->regex = ngx_pcalloc(rc->pool, sizeof(ngx_regex_t));\n    if (rc->regex == NULL) {\n        goto nomem;\n    }\n\n    rc->regex->code = re;\n\n    /* do not study at runtime */\n\n    if (ngx_regex_studies != NULL) {\n        elt = ngx_list_push(ngx_regex_studies);\n        if (elt == NULL) {\n            goto nomem;\n        }\n\n        elt->regex = rc->regex;\n        elt->name = rc->pattern.data;\n    }\n\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\n    if (rc->captures == 0) {\n        return NGX_OK;\n    }\n\n    n = pcre_fullinfo(re, NULL, PCRE_INFO_NAMECOUNT, &rc->named_captures);\n    if (n < 0) {\n        p = \"pcre_fullinfo(\\\"%V\\\", PCRE_INFO_NAMECOUNT) failed: %d\";\n        goto failed;\n    }\n\n    if (rc->named_captures == 0) {\n        return NGX_OK;\n    }\n\n    n = pcre_fullinfo(re, NULL, PCRE_INFO_NAMEENTRYSIZE, &rc->name_size);\n    if (n < 0) {\n        p = \"pcre_fullinfo(\\\"%V\\\", PCRE_INFO_NAMEENTRYSIZE) failed: %d\";\n        goto failed;\n    }\n\n    n = pcre_fullinfo(re, NULL, PCRE_INFO_NAMETABLE, &rc->names);\n    if (n < 0) {\n        p = \"pcre_fullinfo(\\\"%V\\\", PCRE_INFO_NAMETABLE) failed: %d\";\n        goto failed;\n    }\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#endif\n\n\n#if (NGX_PCRE2)\n\nngx_int_t\nngx_regex_exec(ngx_regex_t *re, ngx_str_t *s, int *captures, ngx_uint_t size)\n{\n    size_t      *ov;\n    ngx_int_t    rc;\n    ngx_uint_t   n, i;\n\n    /*\n     * The pcre2_match() function might allocate memory for backtracking\n     * frames, typical allocations are from 40k and above.  So the allocator\n     * is configured to do direct allocations from heap during matching.\n     */\n\n    ngx_regex_malloc_init(NULL);\n\n    if (ngx_regex_match_data == NULL\n        || size > 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 = size;\n        ngx_regex_match_data = pcre2_match_data_create(size / 3, NULL);\n\n        if (ngx_regex_match_data == NULL) {\n            rc = PCRE2_ERROR_NOMEMORY;\n            goto failed;\n        }\n    }\n\n    rc = pcre2_match(re, s->data, s->len, 0, 0, ngx_regex_match_data, NULL);\n\n    if (rc < 0) {\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 (n > size / 3) {\n        n = size / 3;\n    }\n\n    for (i = 0; i < n; i++) {\n        captures[i * 2] = ov[i * 2];\n        captures[i * 2 + 1] = ov[i * 2 + 1];\n    }\n\nfailed:\n\n    ngx_regex_malloc_done();\n\n    return rc;\n}\n\n#else\n\nngx_int_t\nngx_regex_exec(ngx_regex_t *re, ngx_str_t *s, int *captures, ngx_uint_t size)\n{\n    return pcre_exec(re->code, re->extra, (const char *) s->data, s->len,\n                     0, 0, captures, size);\n}\n\n#endif\n\n\nngx_int_t\nngx_regex_exec_array(ngx_array_t *a, ngx_str_t *s, ngx_log_t *log)\n{\n    ngx_int_t         n;\n    ngx_uint_t        i;\n    ngx_regex_elt_t  *re;\n\n    re = a->elts;\n\n    for (i = 0; i < a->nelts; i++) {\n\n        n = ngx_regex_exec(re[i].regex, s, NULL, 0);\n\n        if (n == NGX_REGEX_NO_MATCHED) {\n            continue;\n        }\n\n        if (n < 0) {\n            ngx_log_error(NGX_LOG_ALERT, log, 0,\n                          ngx_regex_exec_n \" failed: %i on \\\"%V\\\" using \\\"%s\\\"\",\n                          n, s, re[i].name);\n            return NGX_ERROR;\n        }\n\n        /* match */\n\n        return NGX_OK;\n    }\n\n    return NGX_DECLINED;\n}\n\n\n#if (NGX_PCRE2)\n\nstatic void * ngx_libc_cdecl\nngx_regex_malloc(size_t size, void *data)\n{\n    if (ngx_regex_pool) {\n        return ngx_palloc(ngx_regex_pool, size);\n    }\n\n    if (ngx_regex_direct_alloc) {\n        return ngx_alloc(size, ngx_cycle->log);\n    }\n\n    return NULL;\n}\n\n\nstatic void ngx_libc_cdecl\nngx_regex_free(void *p, void *data)\n{\n    if (ngx_regex_direct_alloc) {\n        ngx_free(p);\n    }\n\n    return;\n}\n\n#else\n\nstatic void * ngx_libc_cdecl\nngx_regex_malloc(size_t size)\n{\n    if (ngx_regex_pool) {\n        return ngx_palloc(ngx_regex_pool, size);\n    }\n\n    return NULL;\n}\n\n\nstatic void ngx_libc_cdecl\nngx_regex_free(void *p)\n{\n    return;\n}\n\n#endif\n\n\nstatic void\nngx_regex_cleanup(void *data)\n{\n#if (NGX_PCRE2 || NGX_HAVE_PCRE_JIT)\n    ngx_regex_conf_t *rcf = data;\n\n    ngx_uint_t        i;\n    ngx_list_part_t  *part;\n    ngx_regex_elt_t  *elts;\n\n    part = &rcf->studies->part;\n    elts = 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            elts = part->elts;\n            i = 0;\n        }\n\n        /*\n         * The PCRE JIT compiler uses mmap for its executable codes, so we\n         * have to explicitly call the pcre_free_study() function to free\n         * this memory.  In PCRE2, we call the pcre2_code_free() function\n         * for the same reason.\n         */\n\n#if (NGX_PCRE2)\n        pcre2_code_free(elts[i].regex);\n#else\n        if (elts[i].regex->extra != NULL) {\n            pcre_free_study(elts[i].regex->extra);\n        }\n#endif\n    }\n#endif\n\n    /*\n     * On configuration parsing errors ngx_regex_module_init() will not\n     * be called.  Make sure ngx_regex_studies is properly cleared anyway.\n     */\n\n    ngx_regex_studies = NULL;\n\n#if (NGX_PCRE2)\n\n    /*\n     * Free compile context and match data.  If needed at runtime by\n     * the new cycle, these will be re-allocated.\n     */\n\n    if (ngx_regex_compile_context) {\n        pcre2_compile_context_free(ngx_regex_compile_context);\n        ngx_regex_compile_context = NULL;\n    }\n\n    if (ngx_regex_match_data) {\n        pcre2_match_data_free(ngx_regex_match_data);\n        ngx_regex_match_data = NULL;\n        ngx_regex_match_data_size = 0;\n    }\n\n#endif\n}\n\n\nstatic ngx_int_t\nngx_regex_module_init(ngx_cycle_t *cycle)\n{\n    int                opt;\n#if !(NGX_PCRE2)\n    const char        *errstr;\n#endif\n    ngx_uint_t         i;\n    ngx_list_part_t   *part;\n    ngx_regex_elt_t   *elts;\n    ngx_regex_conf_t  *rcf;\n\n    opt = 0;\n\n    rcf = (ngx_regex_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_regex_module);\n\n#if (NGX_PCRE2 || NGX_HAVE_PCRE_JIT)\n\n    if (rcf->pcre_jit) {\n#if (NGX_PCRE2)\n        opt = 1;\n#else\n        opt = PCRE_STUDY_JIT_COMPILE;\n#endif\n    }\n\n#endif\n\n    ngx_regex_malloc_init(cycle->pool);\n\n    part = &rcf->studies->part;\n    elts = 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            elts = part->elts;\n            i = 0;\n        }\n\n#if (NGX_PCRE2)\n\n        if (opt) {\n            int  n;\n\n            n = pcre2_jit_compile(elts[i].regex, PCRE2_JIT_COMPLETE);\n\n            if (n != 0) {\n                ngx_log_error(NGX_LOG_INFO, cycle->log, 0,\n                              \"pcre2_jit_compile() failed: %d in \\\"%s\\\", \"\n                              \"ignored\",\n                              n, elts[i].name);\n            }\n        }\n\n#else\n\n        elts[i].regex->extra = pcre_study(elts[i].regex->code, opt, &errstr);\n\n        if (errstr != NULL) {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,\n                          \"pcre_study() failed: %s in \\\"%s\\\"\",\n                          errstr, elts[i].name);\n        }\n\n#if (NGX_HAVE_PCRE_JIT)\n        if (opt & PCRE_STUDY_JIT_COMPILE) {\n            int jit, n;\n\n            jit = 0;\n            n = pcre_fullinfo(elts[i].regex->code, elts[i].regex->extra,\n                              PCRE_INFO_JIT, &jit);\n\n            if (n != 0 || jit != 1) {\n                ngx_log_error(NGX_LOG_INFO, cycle->log, 0,\n                              \"JIT compiler does not support pattern: \\\"%s\\\"\",\n                              elts[i].name);\n            }\n        }\n#endif\n#endif\n    }\n\n    ngx_regex_malloc_done();\n\n    ngx_regex_studies = NULL;\n#if (NGX_PCRE2)\n    ngx_regex_compile_context = NULL;\n#endif\n\n    return NGX_OK;\n}\n\n\nstatic void *\nngx_regex_create_conf(ngx_cycle_t *cycle)\n{\n    ngx_regex_conf_t    *rcf;\n    ngx_pool_cleanup_t  *cln;\n\n    rcf = ngx_pcalloc(cycle->pool, sizeof(ngx_regex_conf_t));\n    if (rcf == NULL) {\n        return NULL;\n    }\n\n    rcf->pcre_jit = NGX_CONF_UNSET;\n\n    cln = ngx_pool_cleanup_add(cycle->pool, 0);\n    if (cln == NULL) {\n        return NULL;\n    }\n\n    cln->handler = ngx_regex_cleanup;\n    cln->data = rcf;\n\n    rcf->studies = ngx_list_create(cycle->pool, 8, sizeof(ngx_regex_elt_t));\n    if (rcf->studies == NULL) {\n        return NULL;\n    }\n\n    ngx_regex_studies = rcf->studies;\n\n    return rcf;\n}\n\n\nstatic char *\nngx_regex_init_conf(ngx_cycle_t *cycle, void *conf)\n{\n    ngx_regex_conf_t *rcf = conf;\n\n    ngx_conf_init_value(rcf->pcre_jit, 0);\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_regex_pcre_jit(ngx_conf_t *cf, void *post, void *data)\n{\n    ngx_flag_t  *fp = data;\n\n    if (*fp == 0) {\n        return NGX_CONF_OK;\n    }\n\n#if (NGX_PCRE2)\n    {\n    int       r;\n    uint32_t  jit;\n\n    jit = 0;\n    r = pcre2_config(PCRE2_CONFIG_JIT, &jit);\n\n    if (r != 0 || jit != 1) {\n        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                           \"PCRE2 library does not support JIT\");\n        *fp = 0;\n    }\n    }\n#elif (NGX_HAVE_PCRE_JIT)\n    {\n    int  jit, r;\n\n    jit = 0;\n    r = pcre_config(PCRE_CONFIG_JIT, &jit);\n\n    if (r != 0 || jit != 1) {\n        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                           \"PCRE library does not support JIT\");\n        *fp = 0;\n    }\n    }\n#else\n    ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                       \"nginx was built without PCRE JIT support\");\n    *fp = 0;\n#endif\n\n    return NGX_CONF_OK;\n}\n"
  },
  {
    "path": "src/core/ngx_regex.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_REGEX_H_INCLUDED_\n#define _NGX_REGEX_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\n#if (NGX_PCRE2)\n\n#define PCRE2_CODE_UNIT_WIDTH  8\n#include <pcre2.h>\n\n#define NGX_REGEX_NO_MATCHED   PCRE2_ERROR_NOMATCH   /* -1 */\n\ntypedef pcre2_code  ngx_regex_t;\n\n#else\n\n#include <pcre.h>\n\n#define NGX_REGEX_NO_MATCHED   PCRE_ERROR_NOMATCH    /* -1 */\n\ntypedef struct {\n    pcre        *code;\n    pcre_extra  *extra;\n} ngx_regex_t;\n\n#endif\n\n\n#define NGX_REGEX_CASELESS     0x00000001\n#define NGX_REGEX_MULTILINE    0x00000002\n\n\ntypedef struct {\n    ngx_str_t     pattern;\n    ngx_pool_t   *pool;\n    ngx_uint_t    options;\n\n    ngx_regex_t  *regex;\n    int           captures;\n    int           named_captures;\n    int           name_size;\n    u_char       *names;\n    ngx_str_t     err;\n} ngx_regex_compile_t;\n\n\ntypedef struct {\n    ngx_regex_t  *regex;\n    u_char       *name;\n} ngx_regex_elt_t;\n\n\nvoid ngx_regex_init(void);\nngx_int_t ngx_regex_compile(ngx_regex_compile_t *rc);\n\nngx_int_t ngx_regex_exec(ngx_regex_t *re, ngx_str_t *s, int *captures,\n    ngx_uint_t size);\n\n#if (NGX_PCRE2)\n#define ngx_regex_exec_n       \"pcre2_match()\"\n#else\n#define ngx_regex_exec_n       \"pcre_exec()\"\n#endif\n\nngx_int_t ngx_regex_exec_array(ngx_array_t *a, ngx_str_t *s, ngx_log_t *log);\n\n\n#endif /* _NGX_REGEX_H_INCLUDED_ */\n"
  },
  {
    "path": "src/core/ngx_resolver.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n\n\n#define NGX_RESOLVER_UDP_SIZE   4096\n\n#define NGX_RESOLVER_TCP_RSIZE  (2 + 65535)\n#define NGX_RESOLVER_TCP_WSIZE  8192\n\n\ntypedef struct {\n    u_char  ident_hi;\n    u_char  ident_lo;\n    u_char  flags_hi;\n    u_char  flags_lo;\n    u_char  nqs_hi;\n    u_char  nqs_lo;\n    u_char  nan_hi;\n    u_char  nan_lo;\n    u_char  nns_hi;\n    u_char  nns_lo;\n    u_char  nar_hi;\n    u_char  nar_lo;\n} ngx_resolver_hdr_t;\n\n\ntypedef struct {\n    u_char  type_hi;\n    u_char  type_lo;\n    u_char  class_hi;\n    u_char  class_lo;\n} ngx_resolver_qs_t;\n\n\ntypedef struct {\n    u_char  type_hi;\n    u_char  type_lo;\n    u_char  class_hi;\n    u_char  class_lo;\n    u_char  ttl[4];\n    u_char  len_hi;\n    u_char  len_lo;\n} ngx_resolver_an_t;\n\n\n#define ngx_resolver_node(n)  ngx_rbtree_data(n, ngx_resolver_node_t, node)\n\n\nstatic ngx_int_t ngx_udp_connect(ngx_resolver_connection_t *rec);\nstatic ngx_int_t ngx_tcp_connect(ngx_resolver_connection_t *rec);\n\n\nstatic void ngx_resolver_cleanup(void *data);\nstatic void ngx_resolver_cleanup_tree(ngx_resolver_t *r, ngx_rbtree_t *tree);\nstatic ngx_int_t ngx_resolve_name_locked(ngx_resolver_t *r,\n    ngx_resolver_ctx_t *ctx, ngx_str_t *name);\nstatic void ngx_resolver_expire(ngx_resolver_t *r, ngx_rbtree_t *tree,\n    ngx_queue_t *queue);\nstatic ngx_int_t ngx_resolver_send_query(ngx_resolver_t *r,\n    ngx_resolver_node_t *rn);\nstatic ngx_int_t ngx_resolver_send_udp_query(ngx_resolver_t *r,\n    ngx_resolver_connection_t *rec, u_char *query, u_short qlen);\nstatic ngx_int_t ngx_resolver_send_tcp_query(ngx_resolver_t *r,\n    ngx_resolver_connection_t *rec, u_char *query, u_short qlen);\nstatic ngx_int_t ngx_resolver_create_name_query(ngx_resolver_t *r,\n    ngx_resolver_node_t *rn, ngx_str_t *name);\nstatic ngx_int_t ngx_resolver_create_srv_query(ngx_resolver_t *r,\n    ngx_resolver_node_t *rn, ngx_str_t *name);\nstatic ngx_int_t ngx_resolver_create_addr_query(ngx_resolver_t *r,\n    ngx_resolver_node_t *rn, ngx_resolver_addr_t *addr);\nstatic void ngx_resolver_resend_handler(ngx_event_t *ev);\nstatic time_t ngx_resolver_resend(ngx_resolver_t *r, ngx_rbtree_t *tree,\n    ngx_queue_t *queue);\nstatic ngx_uint_t ngx_resolver_resend_empty(ngx_resolver_t *r);\nstatic void ngx_resolver_udp_read(ngx_event_t *rev);\nstatic void ngx_resolver_tcp_write(ngx_event_t *wev);\nstatic void ngx_resolver_tcp_read(ngx_event_t *rev);\nstatic void ngx_resolver_process_response(ngx_resolver_t *r, u_char *buf,\n    size_t n, ngx_uint_t tcp);\nstatic void ngx_resolver_process_a(ngx_resolver_t *r, u_char *buf, size_t n,\n    ngx_uint_t ident, ngx_uint_t code, ngx_uint_t qtype,\n    ngx_uint_t nan, ngx_uint_t trunc, ngx_uint_t ans);\nstatic void ngx_resolver_process_srv(ngx_resolver_t *r, u_char *buf, size_t n,\n    ngx_uint_t ident, ngx_uint_t code, ngx_uint_t nan,\n    ngx_uint_t trunc, ngx_uint_t ans);\nstatic void ngx_resolver_process_ptr(ngx_resolver_t *r, u_char *buf, size_t n,\n    ngx_uint_t ident, ngx_uint_t code, ngx_uint_t nan);\nstatic ngx_resolver_node_t *ngx_resolver_lookup_name(ngx_resolver_t *r,\n    ngx_str_t *name, uint32_t hash);\nstatic ngx_resolver_node_t *ngx_resolver_lookup_srv(ngx_resolver_t *r,\n    ngx_str_t *name, uint32_t hash);\nstatic ngx_resolver_node_t *ngx_resolver_lookup_addr(ngx_resolver_t *r,\n    in_addr_t addr);\nstatic void ngx_resolver_rbtree_insert_value(ngx_rbtree_node_t *temp,\n    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);\nstatic ngx_int_t ngx_resolver_copy(ngx_resolver_t *r, ngx_str_t *name,\n    u_char *buf, u_char *src, u_char *last);\nstatic ngx_int_t ngx_resolver_set_timeout(ngx_resolver_t *r,\n    ngx_resolver_ctx_t *ctx);\nstatic void ngx_resolver_timeout_handler(ngx_event_t *ev);\nstatic void ngx_resolver_free_node(ngx_resolver_t *r, ngx_resolver_node_t *rn);\nstatic void *ngx_resolver_alloc(ngx_resolver_t *r, size_t size);\nstatic void *ngx_resolver_calloc(ngx_resolver_t *r, size_t size);\nstatic void ngx_resolver_free(ngx_resolver_t *r, void *p);\nstatic void ngx_resolver_free_locked(ngx_resolver_t *r, void *p);\nstatic void *ngx_resolver_dup(ngx_resolver_t *r, void *src, size_t size);\nstatic ngx_resolver_addr_t *ngx_resolver_export(ngx_resolver_t *r,\n    ngx_resolver_node_t *rn, ngx_uint_t rotate);\nstatic void ngx_resolver_report_srv(ngx_resolver_t *r, ngx_resolver_ctx_t *ctx);\nstatic u_char *ngx_resolver_log_error(ngx_log_t *log, u_char *buf, size_t len);\nstatic void ngx_resolver_resolve_srv_names(ngx_resolver_ctx_t *ctx,\n    ngx_resolver_node_t *rn);\nstatic void ngx_resolver_srv_names_handler(ngx_resolver_ctx_t *ctx);\nstatic ngx_int_t ngx_resolver_cmp_srvs(const void *one, const void *two);\n\n#if (NGX_HAVE_INET6)\nstatic void ngx_resolver_rbtree_insert_addr6_value(ngx_rbtree_node_t *temp,\n    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);\nstatic ngx_resolver_node_t *ngx_resolver_lookup_addr6(ngx_resolver_t *r,\n    struct in6_addr *addr, uint32_t hash);\n#endif\n\n\n#if (T_NGX_RESOLVER_FILE)\nstatic ngx_int_t\nngx_resolver_parse_resolv_address(ngx_conf_t *cf, ngx_file_t *file,\n    ngx_str_t **names, ngx_uint_t *num)\n {\n     off_t           file_size;\n     u_char         *buf, *p, *end, *line, *line_end;\n     ssize_t         n;\n     ngx_str_t      *address;\n     ngx_array_t     addrs;\n\n     if (ngx_array_init(&addrs, cf->pool, 2, sizeof(ngx_str_t)) != NGX_OK) {\n         return NGX_ERROR;\n     }\n\n     if (ngx_fd_info(file->fd, &file->info) == NGX_FILE_ERROR) {\n         ngx_log_error(NGX_LOG_EMERG, cf->log, ngx_errno,\n                       ngx_fd_info_n \" \\\"%s\\\" failed\", file->name.data);\n     }\n\n     file_size = ngx_file_size(&file->info);\n\n     buf = ngx_pnalloc(cf->pool, file_size + 1);\n     if (buf == NULL) {\n         return NGX_ERROR;\n     }\n\n     n = ngx_read_file(file, buf, file_size, 0);\n     if (n == NGX_ERROR) {\n         return NGX_ERROR;\n     }\n\n     if (n != file_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, file_size);\n         return NGX_ERROR;\n     }\n\n     p = buf;\n     end = buf + file_size;\n     line = p;\n\n     for (/* void */; p < end; p++) {\n         if (*p == CR || *p == LF || p == (end - 1) || *(p + 1) == '#') {\n\n             while (*line == ' ' || *line == '\\t') {\n                 line++;\n             }\n\n             if (ngx_strncmp(line, \"nameserver\", sizeof(\"nameserver\") - 1)\n                 == 0)\n             {\n                 line += sizeof(\"nameserver\") - 1;\n\n                 while (*line == ' ' || *line == '\\t') {\n                     line++;\n                 }\n\n                 line_end = p;\n\n                 while (*line_end == ' ' || *line_end == '\\t'\n                        || *line_end == CR || *line_end == LF)\n                 {\n                     line_end--;\n                 }\n                 /* put a null character for string parse */\n                 *++line_end = '\\0';\n\n                 address = ngx_array_push(&addrs);\n                 if (address == NULL) {\n                     return NGX_ERROR;\n                 }\n\n #if (NGX_HAVE_INET6)\n\n                 if (ngx_strlchr(line, line_end, ':')) {\n                     address->len = line_end - line + 2;\n                     address->data = ngx_palloc(cf->pool, address->len + 2);\n                     if (address->data == NULL) {\n                         return NGX_ERROR;\n                     }\n                     address->data[0] = '[';\n                     ngx_memcpy(address->data + 1, line, address->len - 2);\n                     address->data[address->len - 1] = ']';\n\n                 } else {\n #endif\n                     address->data = line;\n                     address->len = line_end - line;\n #if (NGX_HAVE_INET6)\n                 }\n #endif\n             }\n\n             line = p + 1;\n         }\n     }\n\n     *names = addrs.elts;\n     *num = addrs.nelts;\n\n     return NGX_OK;\n }\n\n\n ngx_int_t\n ngx_resolver_read_resolv_file(ngx_conf_t *cf, ngx_str_t *filename, ngx_str_t **names, ngx_uint_t *n)\n {\n     ngx_int_t       rc;\n     ngx_file_t      file;\n\n     ngx_memzero(&file, sizeof(ngx_file_t));\n\n     file.name.data = filename->data;\n     file.name.len = filename->len;\n     file.log = cf->log;\n\n     file.fd = ngx_open_file(file.name.data, NGX_FILE_RDONLY,\n                             NGX_FILE_OPEN, NGX_FILE_DEFAULT_ACCESS);\n\n     if (file.fd == NGX_INVALID_FILE) {\n         ngx_log_error(NGX_LOG_ERR, cf->log, ngx_errno,\n                       ngx_open_file_n \" \\\"%s\\\" failed\", file.name.data);\n         return NGX_ERROR;\n     }\n\n     rc = ngx_resolver_parse_resolv_address(cf, &file, names, n);\n\n     if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {\n         ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,\n                       ngx_close_file_n \" \\\"%s\\\" failed\", file.name.data);\n     }\n     return rc;\n}\n#endif\n\n\nngx_resolver_t *\nngx_resolver_create(ngx_conf_t *cf, ngx_str_t *names, ngx_uint_t n)\n{\n    ngx_str_t                   s;\n    ngx_url_t                   u;\n    ngx_uint_t                  i, j;\n    ngx_resolver_t             *r;\n    ngx_pool_cleanup_t         *cln;\n    ngx_resolver_connection_t  *rec;\n#if (T_NGX_RESOLVER_FILE)\n#if defined(NGX_RESOLVER_FILE)\n    ngx_str_t default_file = ngx_string(NGX_RESOLVER_FILE);\n#else\n    ngx_str_t default_file = ngx_null_string;\n#endif\n#endif\n\n    r = ngx_pcalloc(cf->pool, sizeof(ngx_resolver_t));\n    if (r == NULL) {\n        return NULL;\n    }\n\n    r->event = ngx_pcalloc(cf->pool, sizeof(ngx_event_t));\n    if (r->event == NULL) {\n        return NULL;\n    }\n\n    cln = ngx_pool_cleanup_add(cf->pool, 0);\n    if (cln == NULL) {\n        return NULL;\n    }\n\n    cln->handler = ngx_resolver_cleanup;\n    cln->data = r;\n\n    r->ipv4 = 1;\n\n    ngx_rbtree_init(&r->name_rbtree, &r->name_sentinel,\n                    ngx_resolver_rbtree_insert_value);\n\n    ngx_rbtree_init(&r->srv_rbtree, &r->srv_sentinel,\n                    ngx_resolver_rbtree_insert_value);\n\n    ngx_rbtree_init(&r->addr_rbtree, &r->addr_sentinel,\n                    ngx_rbtree_insert_value);\n\n    ngx_queue_init(&r->name_resend_queue);\n    ngx_queue_init(&r->srv_resend_queue);\n    ngx_queue_init(&r->addr_resend_queue);\n\n    ngx_queue_init(&r->name_expire_queue);\n    ngx_queue_init(&r->srv_expire_queue);\n    ngx_queue_init(&r->addr_expire_queue);\n\n#if (NGX_HAVE_INET6)\n    r->ipv6 = 1;\n\n    ngx_rbtree_init(&r->addr6_rbtree, &r->addr6_sentinel,\n                    ngx_resolver_rbtree_insert_addr6_value);\n\n    ngx_queue_init(&r->addr6_resend_queue);\n\n    ngx_queue_init(&r->addr6_expire_queue);\n#endif\n\n    r->event->handler = ngx_resolver_resend_handler;\n    r->event->data = r;\n    r->event->log = &cf->cycle->new_log;\n    r->event->cancelable = 1;\n    r->ident = -1;\n\n    r->resend_timeout = 5;\n    r->tcp_timeout = 5;\n    r->expire = 30;\n    r->valid = 0;\n\n    r->log = &cf->cycle->new_log;\n    r->log_level = NGX_LOG_ERR;\n\n#if (T_NGX_RESOLVER_FILE)\n    if (names == NULL) {\n        if (default_file.len != 0\n            && ngx_resolver_read_resolv_file(cf, &default_file, &names, &n) != NGX_OK)\n        {\n            return NULL;\n        }\n     }\n #endif\n\n    if (n) {\n        if (ngx_array_init(&r->connections, cf->pool, n,\n                           sizeof(ngx_resolver_connection_t))\n            != NGX_OK)\n        {\n            return NULL;\n        }\n    }\n\n    for (i = 0; i < n; i++) {\n        if (ngx_strncmp(names[i].data, \"valid=\", 6) == 0) {\n            s.len = names[i].len - 6;\n            s.data = names[i].data + 6;\n\n            r->valid = ngx_parse_time(&s, 1);\n\n            if (r->valid == (time_t) NGX_ERROR) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"invalid parameter: %V\", &names[i]);\n                return NULL;\n            }\n\n            continue;\n        }\n\n#if (NGX_HAVE_INET6)\n        if (ngx_strncmp(names[i].data, \"ipv4=\", 5) == 0) {\n\n            if (ngx_strcmp(&names[i].data[5], \"on\") == 0) {\n                r->ipv4 = 1;\n\n            } else if (ngx_strcmp(&names[i].data[5], \"off\") == 0) {\n                r->ipv4 = 0;\n\n            } else {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"invalid parameter: %V\", &names[i]);\n                return NULL;\n            }\n\n            continue;\n        }\n\n        if (ngx_strncmp(names[i].data, \"ipv6=\", 5) == 0) {\n\n            if (ngx_strcmp(&names[i].data[5], \"on\") == 0) {\n                r->ipv6 = 1;\n\n            } else if (ngx_strcmp(&names[i].data[5], \"off\") == 0) {\n                r->ipv6 = 0;\n\n            } else {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"invalid parameter: %V\", &names[i]);\n                return NULL;\n            }\n\n            continue;\n        }\n#endif\n\n        ngx_memzero(&u, sizeof(ngx_url_t));\n\n        u.url = names[i];\n        u.default_port = 53;\n\n        if (ngx_parse_url(cf->pool, &u) != NGX_OK) {\n            if (u.err) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"%s in resolver \\\"%V\\\"\",\n                                   u.err, &u.url);\n            }\n\n            return NULL;\n        }\n\n        rec = ngx_array_push_n(&r->connections, u.naddrs);\n        if (rec == NULL) {\n            return NULL;\n        }\n\n        ngx_memzero(rec, u.naddrs * sizeof(ngx_resolver_connection_t));\n\n        for (j = 0; j < u.naddrs; j++) {\n            rec[j].sockaddr = u.addrs[j].sockaddr;\n            rec[j].socklen = u.addrs[j].socklen;\n            rec[j].server = u.addrs[j].name;\n            rec[j].resolver = r;\n        }\n    }\n\n#if (NGX_HAVE_INET6)\n    if (r->ipv4 + r->ipv6 == 0) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"\\\"ipv4\\\" and \\\"ipv6\\\" cannot both be \\\"off\\\"\");\n        return NULL;\n    }\n#endif\n\n    if (n && r->connections.nelts == 0) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, \"no name servers defined\");\n        return NULL;\n    }\n\n    return r;\n}\n\n\nstatic void\nngx_resolver_cleanup(void *data)\n{\n    ngx_resolver_t  *r = data;\n\n    ngx_uint_t                  i;\n    ngx_resolver_connection_t  *rec;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, \"cleanup resolver\");\n\n    ngx_resolver_cleanup_tree(r, &r->name_rbtree);\n\n    ngx_resolver_cleanup_tree(r, &r->srv_rbtree);\n\n    ngx_resolver_cleanup_tree(r, &r->addr_rbtree);\n\n#if (NGX_HAVE_INET6)\n    ngx_resolver_cleanup_tree(r, &r->addr6_rbtree);\n#endif\n\n    if (r->event->timer_set) {\n        ngx_del_timer(r->event);\n    }\n\n    rec = r->connections.elts;\n\n    for (i = 0; i < r->connections.nelts; i++) {\n        if (rec[i].udp) {\n            ngx_close_connection(rec[i].udp);\n        }\n\n        if (rec[i].tcp) {\n            ngx_close_connection(rec[i].tcp);\n        }\n\n        if (rec[i].read_buf) {\n            ngx_resolver_free(r, rec[i].read_buf->start);\n            ngx_resolver_free(r, rec[i].read_buf);\n        }\n\n        if (rec[i].write_buf) {\n            ngx_resolver_free(r, rec[i].write_buf->start);\n            ngx_resolver_free(r, rec[i].write_buf);\n        }\n    }\n}\n\n\nstatic void\nngx_resolver_cleanup_tree(ngx_resolver_t *r, ngx_rbtree_t *tree)\n{\n    ngx_resolver_ctx_t   *ctx, *next;\n    ngx_resolver_node_t  *rn;\n\n    while (tree->root != tree->sentinel) {\n\n        rn = ngx_resolver_node(ngx_rbtree_min(tree->root, tree->sentinel));\n\n        ngx_queue_remove(&rn->queue);\n\n        for (ctx = rn->waiting; ctx; ctx = next) {\n            next = ctx->next;\n\n            if (ctx->event) {\n                if (ctx->event->timer_set) {\n                    ngx_del_timer(ctx->event);\n                }\n\n                ngx_resolver_free(r, ctx->event);\n            }\n\n            ngx_resolver_free(r, ctx);\n        }\n\n        ngx_rbtree_delete(tree, &rn->node);\n\n        ngx_resolver_free_node(r, rn);\n    }\n}\n\n\nngx_resolver_ctx_t *\nngx_resolve_start(ngx_resolver_t *r, ngx_resolver_ctx_t *temp)\n{\n    in_addr_t            addr;\n    ngx_resolver_ctx_t  *ctx;\n\n    if (temp) {\n        addr = ngx_inet_addr(temp->name.data, temp->name.len);\n\n        if (addr != INADDR_NONE) {\n            temp->resolver = r;\n            temp->state = NGX_OK;\n            temp->naddrs = 1;\n            temp->addrs = &temp->addr;\n            temp->addr.sockaddr = (struct sockaddr *) &temp->sin;\n            temp->addr.socklen = sizeof(struct sockaddr_in);\n            ngx_memzero(&temp->sin, sizeof(struct sockaddr_in));\n            temp->sin.sin_family = AF_INET;\n            temp->sin.sin_addr.s_addr = addr;\n            temp->quick = 1;\n\n            return temp;\n        }\n    }\n\n    if (r->connections.nelts == 0) {\n        return NGX_NO_RESOLVER;\n    }\n\n    ctx = ngx_resolver_calloc(r, sizeof(ngx_resolver_ctx_t));\n\n    if (ctx) {\n        ctx->resolver = r;\n    }\n\n    return ctx;\n}\n\n\nngx_int_t\nngx_resolve_name(ngx_resolver_ctx_t *ctx)\n{\n    size_t           slen;\n    ngx_int_t        rc;\n    ngx_str_t        name;\n    ngx_resolver_t  *r;\n\n    r = ctx->resolver;\n\n    if (ctx->name.len > 0 && ctx->name.data[ctx->name.len - 1] == '.') {\n        ctx->name.len--;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0,\n                   \"resolve: \\\"%V\\\"\", &ctx->name);\n\n    if (ctx->quick) {\n        ctx->handler(ctx);\n        return NGX_OK;\n    }\n\n    if (ctx->service.len) {\n        slen = ctx->service.len;\n\n        if (ngx_strlchr(ctx->service.data,\n                        ctx->service.data + ctx->service.len, '.')\n            == NULL)\n        {\n            slen += sizeof(\"_._tcp\") - 1;\n        }\n\n        name.len = slen + 1 + ctx->name.len;\n\n        name.data = ngx_resolver_alloc(r, name.len);\n        if (name.data == NULL) {\n            goto failed;\n        }\n\n        if (slen == ctx->service.len) {\n            ngx_sprintf(name.data, \"%V.%V\", &ctx->service, &ctx->name);\n\n        } else {\n            ngx_sprintf(name.data, \"_%V._tcp.%V\", &ctx->service, &ctx->name);\n        }\n\n        /* lock name mutex */\n\n        rc = ngx_resolve_name_locked(r, ctx, &name);\n\n        ngx_resolver_free(r, name.data);\n\n    } else {\n        /* lock name mutex */\n\n        rc = ngx_resolve_name_locked(r, ctx, &ctx->name);\n    }\n\n    if (rc == NGX_OK) {\n        return NGX_OK;\n    }\n\n    /* unlock name mutex */\n\n    if (rc == NGX_AGAIN) {\n        return NGX_OK;\n    }\n\n    /* NGX_ERROR */\n\n    if (ctx->event) {\n        ngx_resolver_free(r, ctx->event);\n    }\n\nfailed:\n\n    ngx_resolver_free(r, ctx);\n\n    return NGX_ERROR;\n}\n\n\nvoid\nngx_resolve_name_done(ngx_resolver_ctx_t *ctx)\n{\n    ngx_uint_t            i;\n    ngx_resolver_t       *r;\n    ngx_resolver_ctx_t   *w, **p;\n    ngx_resolver_node_t  *rn;\n\n    r = ctx->resolver;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0,\n                   \"resolve name done: %i\", ctx->state);\n\n    if (ctx->quick) {\n        return;\n    }\n\n    if (ctx->event && ctx->event->timer_set) {\n        ngx_del_timer(ctx->event);\n    }\n\n    /* lock name mutex */\n\n    if (ctx->nsrvs) {\n        for (i = 0; i < ctx->nsrvs; i++) {\n            if (ctx->srvs[i].ctx) {\n                ngx_resolve_name_done(ctx->srvs[i].ctx);\n            }\n\n            if (ctx->srvs[i].addrs) {\n                ngx_resolver_free(r, ctx->srvs[i].addrs->sockaddr);\n                ngx_resolver_free(r, ctx->srvs[i].addrs);\n            }\n\n            ngx_resolver_free(r, ctx->srvs[i].name.data);\n        }\n\n        ngx_resolver_free(r, ctx->srvs);\n    }\n\n    if (ctx->state == NGX_AGAIN || ctx->state == NGX_RESOLVE_TIMEDOUT) {\n\n        rn = ctx->node;\n\n        if (rn) {\n            p = &rn->waiting;\n            w = rn->waiting;\n\n            while (w) {\n                if (w == ctx) {\n                    *p = w->next;\n\n                    goto done;\n                }\n\n                p = &w->next;\n                w = w->next;\n            }\n\n            ngx_log_error(NGX_LOG_ALERT, r->log, 0,\n                          \"could not cancel %V resolving\", &ctx->name);\n        }\n    }\n\ndone:\n\n    if (ctx->service.len) {\n        ngx_resolver_expire(r, &r->srv_rbtree, &r->srv_expire_queue);\n\n    } else {\n        ngx_resolver_expire(r, &r->name_rbtree, &r->name_expire_queue);\n    }\n\n    /* unlock name mutex */\n\n    /* lock alloc mutex */\n\n    if (ctx->event) {\n        ngx_resolver_free_locked(r, ctx->event);\n    }\n\n    ngx_resolver_free_locked(r, ctx);\n\n    /* unlock alloc mutex */\n\n    if (r->event->timer_set && ngx_resolver_resend_empty(r)) {\n        ngx_del_timer(r->event);\n    }\n}\n\n\nstatic ngx_int_t\nngx_resolve_name_locked(ngx_resolver_t *r, ngx_resolver_ctx_t *ctx,\n    ngx_str_t *name)\n{\n    uint32_t              hash;\n    ngx_int_t             rc;\n    ngx_str_t             cname;\n    ngx_uint_t            i, naddrs;\n    ngx_queue_t          *resend_queue, *expire_queue;\n    ngx_rbtree_t         *tree;\n    ngx_resolver_ctx_t   *next, *last;\n    ngx_resolver_addr_t  *addrs;\n    ngx_resolver_node_t  *rn;\n\n    ngx_strlow(name->data, name->data, name->len);\n\n    hash = ngx_crc32_short(name->data, name->len);\n\n    if (ctx->service.len) {\n        rn = ngx_resolver_lookup_srv(r, name, hash);\n\n        tree = &r->srv_rbtree;\n        resend_queue = &r->srv_resend_queue;\n        expire_queue = &r->srv_expire_queue;\n\n    } else {\n        rn = ngx_resolver_lookup_name(r, name, hash);\n\n        tree = &r->name_rbtree;\n        resend_queue = &r->name_resend_queue;\n        expire_queue = &r->name_expire_queue;\n    }\n\n    if (rn) {\n\n        /* ctx can be a list after NGX_RESOLVE_CNAME */\n        for (last = ctx; last->next; last = last->next);\n\n        if (rn->valid >= ngx_time()) {\n\n            ngx_log_debug0(NGX_LOG_DEBUG_CORE, r->log, 0, \"resolve cached\");\n\n            ngx_queue_remove(&rn->queue);\n\n            rn->expire = ngx_time() + r->expire;\n\n            ngx_queue_insert_head(expire_queue, &rn->queue);\n\n            naddrs = (rn->naddrs == (u_short) -1) ? 0 : rn->naddrs;\n#if (NGX_HAVE_INET6)\n            naddrs += (rn->naddrs6 == (u_short) -1) ? 0 : rn->naddrs6;\n#endif\n\n            if (naddrs) {\n\n                if (naddrs == 1 && rn->naddrs == 1) {\n                    addrs = NULL;\n\n                } else {\n                    addrs = ngx_resolver_export(r, rn, 1);\n                    if (addrs == NULL) {\n                        return NGX_ERROR;\n                    }\n                }\n\n                last->next = rn->waiting;\n                rn->waiting = NULL;\n\n                /* unlock name mutex */\n\n                do {\n                    ctx->state = NGX_OK;\n                    ctx->valid = rn->valid;\n                    ctx->naddrs = naddrs;\n\n                    if (addrs == NULL) {\n                        ctx->addrs = &ctx->addr;\n                        ctx->addr.sockaddr = (struct sockaddr *) &ctx->sin;\n                        ctx->addr.socklen = sizeof(struct sockaddr_in);\n                        ngx_memzero(&ctx->sin, sizeof(struct sockaddr_in));\n                        ctx->sin.sin_family = AF_INET;\n                        ctx->sin.sin_addr.s_addr = rn->u.addr;\n\n                    } else {\n                        ctx->addrs = addrs;\n                    }\n\n                    next = ctx->next;\n\n                    ctx->handler(ctx);\n\n                    ctx = next;\n                } while (ctx);\n\n                if (addrs != NULL) {\n                    ngx_resolver_free(r, addrs->sockaddr);\n                    ngx_resolver_free(r, addrs);\n                }\n\n                return NGX_OK;\n            }\n\n            if (rn->nsrvs) {\n                last->next = rn->waiting;\n                rn->waiting = NULL;\n\n                /* unlock name mutex */\n\n                do {\n                    next = ctx->next;\n\n                    ngx_resolver_resolve_srv_names(ctx, rn);\n\n                    ctx = next;\n                } while (ctx);\n\n                return NGX_OK;\n            }\n\n            /* NGX_RESOLVE_CNAME */\n\n            if (ctx->recursion++ < NGX_RESOLVER_MAX_RECURSION) {\n\n                cname.len = rn->cnlen;\n                cname.data = rn->u.cname;\n\n                return ngx_resolve_name_locked(r, ctx, &cname);\n            }\n\n            last->next = rn->waiting;\n            rn->waiting = NULL;\n\n            /* unlock name mutex */\n\n            do {\n                ctx->state = NGX_RESOLVE_NXDOMAIN;\n                ctx->valid = ngx_time() + (r->valid ? r->valid : 10);\n                next = ctx->next;\n\n                ctx->handler(ctx);\n\n                ctx = next;\n            } while (ctx);\n\n            return NGX_OK;\n        }\n\n        if (rn->waiting) {\n            if (ngx_resolver_set_timeout(r, ctx) != NGX_OK) {\n                return NGX_ERROR;\n            }\n\n            last->next = rn->waiting;\n            rn->waiting = ctx;\n            ctx->state = NGX_AGAIN;\n            ctx->async = 1;\n\n            do {\n                ctx->node = rn;\n                ctx = ctx->next;\n            } while (ctx);\n\n            return NGX_AGAIN;\n        }\n\n        ngx_queue_remove(&rn->queue);\n\n        /* lock alloc mutex */\n\n        if (rn->query) {\n            ngx_resolver_free_locked(r, rn->query);\n            rn->query = NULL;\n#if (NGX_HAVE_INET6)\n            rn->query6 = NULL;\n#endif\n        }\n\n        if (rn->cnlen) {\n            ngx_resolver_free_locked(r, rn->u.cname);\n        }\n\n        if (rn->naddrs > 1 && rn->naddrs != (u_short) -1) {\n            ngx_resolver_free_locked(r, rn->u.addrs);\n        }\n\n#if (NGX_HAVE_INET6)\n        if (rn->naddrs6 > 1 && rn->naddrs6 != (u_short) -1) {\n            ngx_resolver_free_locked(r, rn->u6.addrs6);\n        }\n#endif\n\n        if (rn->nsrvs) {\n            for (i = 0; i < (ngx_uint_t) rn->nsrvs; i++) {\n                if (rn->u.srvs[i].name.data) {\n                    ngx_resolver_free_locked(r, rn->u.srvs[i].name.data);\n                }\n            }\n\n            ngx_resolver_free_locked(r, rn->u.srvs);\n        }\n\n        /* unlock alloc mutex */\n\n    } else {\n\n        rn = ngx_resolver_alloc(r, sizeof(ngx_resolver_node_t));\n        if (rn == NULL) {\n            return NGX_ERROR;\n        }\n\n        rn->name = ngx_resolver_dup(r, name->data, name->len);\n        if (rn->name == NULL) {\n            ngx_resolver_free(r, rn);\n            return NGX_ERROR;\n        }\n\n        rn->node.key = hash;\n        rn->nlen = (u_short) name->len;\n        rn->query = NULL;\n#if (NGX_HAVE_INET6)\n        rn->query6 = NULL;\n#endif\n\n        ngx_rbtree_insert(tree, &rn->node);\n    }\n\n    if (ctx->service.len) {\n        rc = ngx_resolver_create_srv_query(r, rn, name);\n\n    } else {\n        rc = ngx_resolver_create_name_query(r, rn, name);\n    }\n\n    if (rc == NGX_ERROR) {\n        goto failed;\n    }\n\n    if (rc == NGX_DECLINED) {\n        ngx_rbtree_delete(tree, &rn->node);\n\n        ngx_resolver_free(r, rn->query);\n        ngx_resolver_free(r, rn->name);\n        ngx_resolver_free(r, rn);\n\n        do {\n            ctx->state = NGX_RESOLVE_NXDOMAIN;\n            next = ctx->next;\n\n            ctx->handler(ctx);\n\n            ctx = next;\n        } while (ctx);\n\n        return NGX_OK;\n    }\n\n    rn->last_connection = r->last_connection++;\n    if (r->last_connection == r->connections.nelts) {\n        r->last_connection = 0;\n    }\n\n    rn->naddrs = r->ipv4 ? (u_short) -1 : 0;\n    rn->tcp = 0;\n#if (NGX_HAVE_INET6)\n    rn->naddrs6 = r->ipv6 ? (u_short) -1 : 0;\n    rn->tcp6 = 0;\n#endif\n    rn->nsrvs = 0;\n\n    if (ngx_resolver_send_query(r, rn) != NGX_OK) {\n\n        /* immediately retry once on failure */\n\n        rn->last_connection++;\n        if (rn->last_connection == r->connections.nelts) {\n            rn->last_connection = 0;\n        }\n\n        (void) ngx_resolver_send_query(r, rn);\n    }\n\n    if (ngx_resolver_set_timeout(r, ctx) != NGX_OK) {\n        goto failed;\n    }\n\n    if (ngx_resolver_resend_empty(r)) {\n        ngx_add_timer(r->event, (ngx_msec_t) (r->resend_timeout * 1000));\n    }\n\n    rn->expire = ngx_time() + r->resend_timeout;\n\n    ngx_queue_insert_head(resend_queue, &rn->queue);\n\n    rn->code = 0;\n    rn->cnlen = 0;\n    rn->valid = 0;\n    rn->ttl = NGX_MAX_UINT32_VALUE;\n    rn->waiting = ctx;\n\n    ctx->state = NGX_AGAIN;\n    ctx->async = 1;\n\n    do {\n        ctx->node = rn;\n        ctx = ctx->next;\n    } while (ctx);\n\n    return NGX_AGAIN;\n\nfailed:\n\n    ngx_rbtree_delete(tree, &rn->node);\n\n    if (rn->query) {\n        ngx_resolver_free(r, rn->query);\n    }\n\n    ngx_resolver_free(r, rn->name);\n\n    ngx_resolver_free(r, rn);\n\n    return NGX_ERROR;\n}\n\n\nngx_int_t\nngx_resolve_addr(ngx_resolver_ctx_t *ctx)\n{\n    u_char               *name;\n    in_addr_t             addr;\n    ngx_queue_t          *resend_queue, *expire_queue;\n    ngx_rbtree_t         *tree;\n    ngx_resolver_t       *r;\n    struct sockaddr_in   *sin;\n    ngx_resolver_node_t  *rn;\n#if (NGX_HAVE_INET6)\n    uint32_t              hash;\n    struct sockaddr_in6  *sin6;\n#endif\n\n#if (NGX_SUPPRESS_WARN)\n    addr = 0;\n#if (NGX_HAVE_INET6)\n    hash = 0;\n    sin6 = NULL;\n#endif\n#endif\n\n    r = ctx->resolver;\n\n    switch (ctx->addr.sockaddr->sa_family) {\n\n#if (NGX_HAVE_INET6)\n    case AF_INET6:\n        sin6 = (struct sockaddr_in6 *) ctx->addr.sockaddr;\n        hash = ngx_crc32_short(sin6->sin6_addr.s6_addr, 16);\n\n        /* lock addr mutex */\n\n        rn = ngx_resolver_lookup_addr6(r, &sin6->sin6_addr, hash);\n\n        tree = &r->addr6_rbtree;\n        resend_queue = &r->addr6_resend_queue;\n        expire_queue = &r->addr6_expire_queue;\n\n        break;\n#endif\n\n    default: /* AF_INET */\n        sin = (struct sockaddr_in *) ctx->addr.sockaddr;\n        addr = ntohl(sin->sin_addr.s_addr);\n\n        /* lock addr mutex */\n\n        rn = ngx_resolver_lookup_addr(r, addr);\n\n        tree = &r->addr_rbtree;\n        resend_queue = &r->addr_resend_queue;\n        expire_queue = &r->addr_expire_queue;\n    }\n\n    if (rn) {\n\n        if (rn->valid >= ngx_time()) {\n\n            ngx_log_debug0(NGX_LOG_DEBUG_CORE, r->log, 0, \"resolve cached\");\n\n            ngx_queue_remove(&rn->queue);\n\n            rn->expire = ngx_time() + r->expire;\n\n            ngx_queue_insert_head(expire_queue, &rn->queue);\n\n            name = ngx_resolver_dup(r, rn->name, rn->nlen);\n            if (name == NULL) {\n                ngx_resolver_free(r, ctx);\n                return NGX_ERROR;\n            }\n\n            ctx->name.len = rn->nlen;\n            ctx->name.data = name;\n\n            /* unlock addr mutex */\n\n            ctx->state = NGX_OK;\n            ctx->valid = rn->valid;\n\n            ctx->handler(ctx);\n\n            ngx_resolver_free(r, name);\n\n            return NGX_OK;\n        }\n\n        if (rn->waiting) {\n            if (ngx_resolver_set_timeout(r, ctx) != NGX_OK) {\n                return NGX_ERROR;\n            }\n\n            ctx->next = rn->waiting;\n            rn->waiting = ctx;\n            ctx->state = NGX_AGAIN;\n            ctx->async = 1;\n            ctx->node = rn;\n\n            /* unlock addr mutex */\n\n            return NGX_OK;\n        }\n\n        ngx_queue_remove(&rn->queue);\n\n        ngx_resolver_free(r, rn->query);\n        rn->query = NULL;\n#if (NGX_HAVE_INET6)\n        rn->query6 = NULL;\n#endif\n\n    } else {\n        rn = ngx_resolver_alloc(r, sizeof(ngx_resolver_node_t));\n        if (rn == NULL) {\n            goto failed;\n        }\n\n        switch (ctx->addr.sockaddr->sa_family) {\n\n#if (NGX_HAVE_INET6)\n        case AF_INET6:\n            rn->addr6 = sin6->sin6_addr;\n            rn->node.key = hash;\n            break;\n#endif\n\n        default: /* AF_INET */\n            rn->node.key = addr;\n        }\n\n        rn->query = NULL;\n#if (NGX_HAVE_INET6)\n        rn->query6 = NULL;\n#endif\n\n        ngx_rbtree_insert(tree, &rn->node);\n    }\n\n    if (ngx_resolver_create_addr_query(r, rn, &ctx->addr) != NGX_OK) {\n        goto failed;\n    }\n\n    rn->last_connection = r->last_connection++;\n    if (r->last_connection == r->connections.nelts) {\n        r->last_connection = 0;\n    }\n\n    rn->naddrs = (u_short) -1;\n    rn->tcp = 0;\n#if (NGX_HAVE_INET6)\n    rn->naddrs6 = (u_short) -1;\n    rn->tcp6 = 0;\n#endif\n    rn->nsrvs = 0;\n\n    if (ngx_resolver_send_query(r, rn) != NGX_OK) {\n\n        /* immediately retry once on failure */\n\n        rn->last_connection++;\n        if (rn->last_connection == r->connections.nelts) {\n            rn->last_connection = 0;\n        }\n\n        (void) ngx_resolver_send_query(r, rn);\n    }\n\n    if (ngx_resolver_set_timeout(r, ctx) != NGX_OK) {\n        goto failed;\n    }\n\n    if (ngx_resolver_resend_empty(r)) {\n        ngx_add_timer(r->event, (ngx_msec_t) (r->resend_timeout * 1000));\n    }\n\n    rn->expire = ngx_time() + r->resend_timeout;\n\n    ngx_queue_insert_head(resend_queue, &rn->queue);\n\n    rn->code = 0;\n    rn->cnlen = 0;\n    rn->name = NULL;\n    rn->nlen = 0;\n    rn->valid = 0;\n    rn->ttl = NGX_MAX_UINT32_VALUE;\n    rn->waiting = ctx;\n\n    /* unlock addr mutex */\n\n    ctx->state = NGX_AGAIN;\n    ctx->async = 1;\n    ctx->node = rn;\n\n    return NGX_OK;\n\nfailed:\n\n    if (rn) {\n        ngx_rbtree_delete(tree, &rn->node);\n\n        if (rn->query) {\n            ngx_resolver_free(r, rn->query);\n        }\n\n        ngx_resolver_free(r, rn);\n    }\n\n    /* unlock addr mutex */\n\n    if (ctx->event) {\n        ngx_resolver_free(r, ctx->event);\n    }\n\n    ngx_resolver_free(r, ctx);\n\n    return NGX_ERROR;\n}\n\n\nvoid\nngx_resolve_addr_done(ngx_resolver_ctx_t *ctx)\n{\n    ngx_queue_t          *expire_queue;\n    ngx_rbtree_t         *tree;\n    ngx_resolver_t       *r;\n    ngx_resolver_ctx_t   *w, **p;\n    ngx_resolver_node_t  *rn;\n\n    r = ctx->resolver;\n\n    switch (ctx->addr.sockaddr->sa_family) {\n\n#if (NGX_HAVE_INET6)\n    case AF_INET6:\n        tree = &r->addr6_rbtree;\n        expire_queue = &r->addr6_expire_queue;\n        break;\n#endif\n\n    default: /* AF_INET */\n        tree = &r->addr_rbtree;\n        expire_queue = &r->addr_expire_queue;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0,\n                   \"resolve addr done: %i\", ctx->state);\n\n    if (ctx->event && ctx->event->timer_set) {\n        ngx_del_timer(ctx->event);\n    }\n\n    /* lock addr mutex */\n\n    if (ctx->state == NGX_AGAIN || ctx->state == NGX_RESOLVE_TIMEDOUT) {\n\n        rn = ctx->node;\n\n        if (rn) {\n            p = &rn->waiting;\n            w = rn->waiting;\n\n            while (w) {\n                if (w == ctx) {\n                    *p = w->next;\n\n                    goto done;\n                }\n\n                p = &w->next;\n                w = w->next;\n            }\n        }\n\n        {\n            u_char     text[NGX_SOCKADDR_STRLEN];\n            ngx_str_t  addrtext;\n\n            addrtext.data = text;\n            addrtext.len = ngx_sock_ntop(ctx->addr.sockaddr, ctx->addr.socklen,\n                                         text, NGX_SOCKADDR_STRLEN, 0);\n\n            ngx_log_error(NGX_LOG_ALERT, r->log, 0,\n                          \"could not cancel %V resolving\", &addrtext);\n        }\n    }\n\ndone:\n\n    ngx_resolver_expire(r, tree, expire_queue);\n\n    /* unlock addr mutex */\n\n    /* lock alloc mutex */\n\n    if (ctx->event) {\n        ngx_resolver_free_locked(r, ctx->event);\n    }\n\n    ngx_resolver_free_locked(r, ctx);\n\n    /* unlock alloc mutex */\n\n    if (r->event->timer_set && ngx_resolver_resend_empty(r)) {\n        ngx_del_timer(r->event);\n    }\n}\n\n\nstatic void\nngx_resolver_expire(ngx_resolver_t *r, ngx_rbtree_t *tree, ngx_queue_t *queue)\n{\n    time_t                now;\n    ngx_uint_t            i;\n    ngx_queue_t          *q;\n    ngx_resolver_node_t  *rn;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_CORE, r->log, 0, \"resolver expire\");\n\n    now = ngx_time();\n\n    for (i = 0; i < 2; i++) {\n        if (ngx_queue_empty(queue)) {\n            return;\n        }\n\n        q = ngx_queue_last(queue);\n\n        rn = ngx_queue_data(q, ngx_resolver_node_t, queue);\n\n        if (now <= rn->expire) {\n            return;\n        }\n\n        ngx_log_debug2(NGX_LOG_DEBUG_CORE, r->log, 0,\n                       \"resolver expire \\\"%*s\\\"\", (size_t) rn->nlen, rn->name);\n\n        ngx_queue_remove(q);\n\n        ngx_rbtree_delete(tree, &rn->node);\n\n        ngx_resolver_free_node(r, rn);\n    }\n}\n\n\nstatic ngx_int_t\nngx_resolver_send_query(ngx_resolver_t *r, ngx_resolver_node_t *rn)\n{\n    ngx_int_t                   rc;\n    ngx_resolver_connection_t  *rec;\n\n    rec = r->connections.elts;\n    rec = &rec[rn->last_connection];\n\n    if (rec->log.handler == NULL) {\n        rec->log = *r->log;\n        rec->log.handler = ngx_resolver_log_error;\n        rec->log.data = rec;\n        rec->log.action = \"resolving\";\n    }\n\n    if (rn->query && rn->naddrs == (u_short) -1) {\n        rc = rn->tcp ? ngx_resolver_send_tcp_query(r, rec, rn->query, rn->qlen)\n                     : ngx_resolver_send_udp_query(r, rec, rn->query, rn->qlen);\n\n        if (rc != NGX_OK) {\n            return rc;\n        }\n    }\n\n#if (NGX_HAVE_INET6)\n\n    if (rn->query6 && rn->naddrs6 == (u_short) -1) {\n        rc = rn->tcp6\n                    ? ngx_resolver_send_tcp_query(r, rec, rn->query6, rn->qlen)\n                    : ngx_resolver_send_udp_query(r, rec, rn->query6, rn->qlen);\n\n        if (rc != NGX_OK) {\n            return rc;\n        }\n    }\n\n#endif\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_resolver_send_udp_query(ngx_resolver_t *r, ngx_resolver_connection_t  *rec,\n    u_char *query, u_short qlen)\n{\n    ssize_t  n;\n\n    if (rec->udp == NULL) {\n        if (ngx_udp_connect(rec) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        rec->udp->data = rec;\n        rec->udp->read->handler = ngx_resolver_udp_read;\n        rec->udp->read->resolver = 1;\n    }\n\n    n = ngx_send(rec->udp, query, qlen);\n\n    if (n == NGX_ERROR) {\n        goto failed;\n    }\n\n    if ((size_t) n != (size_t) qlen) {\n        ngx_log_error(NGX_LOG_CRIT, &rec->log, 0, \"send() incomplete\");\n        goto failed;\n    }\n\n    return NGX_OK;\n\nfailed:\n\n    ngx_close_connection(rec->udp);\n    rec->udp = NULL;\n\n    return NGX_ERROR;\n}\n\n\nstatic ngx_int_t\nngx_resolver_send_tcp_query(ngx_resolver_t *r, ngx_resolver_connection_t *rec,\n    u_char *query, u_short qlen)\n{\n    ngx_buf_t  *b;\n    ngx_int_t   rc;\n\n    rc = NGX_OK;\n\n    if (rec->tcp == NULL) {\n        b = rec->read_buf;\n\n        if (b == NULL) {\n            b = ngx_resolver_calloc(r, sizeof(ngx_buf_t));\n            if (b == NULL) {\n                return NGX_ERROR;\n            }\n\n            b->start = ngx_resolver_alloc(r, NGX_RESOLVER_TCP_RSIZE);\n            if (b->start == NULL) {\n                ngx_resolver_free(r, b);\n                return NGX_ERROR;\n            }\n\n            b->end = b->start + NGX_RESOLVER_TCP_RSIZE;\n\n            rec->read_buf = b;\n        }\n\n        b->pos = b->start;\n        b->last = b->start;\n\n        b = rec->write_buf;\n\n        if (b == NULL) {\n            b = ngx_resolver_calloc(r, sizeof(ngx_buf_t));\n            if (b == NULL) {\n                return NGX_ERROR;\n            }\n\n            b->start = ngx_resolver_alloc(r, NGX_RESOLVER_TCP_WSIZE);\n            if (b->start == NULL) {\n                ngx_resolver_free(r, b);\n                return NGX_ERROR;\n            }\n\n            b->end = b->start + NGX_RESOLVER_TCP_WSIZE;\n\n            rec->write_buf = b;\n        }\n\n        b->pos = b->start;\n        b->last = b->start;\n\n        rc = ngx_tcp_connect(rec);\n        if (rc == NGX_ERROR) {\n            return NGX_ERROR;\n        }\n\n        rec->tcp->data = rec;\n        rec->tcp->write->handler = ngx_resolver_tcp_write;\n        rec->tcp->write->cancelable = 1;\n        rec->tcp->read->handler = ngx_resolver_tcp_read;\n        rec->tcp->read->resolver = 1;\n\n        ngx_add_timer(rec->tcp->write, (ngx_msec_t) (r->tcp_timeout * 1000));\n    }\n\n    b = rec->write_buf;\n\n    if (b->end - b->last <  2 + qlen) {\n        ngx_log_error(NGX_LOG_CRIT, &rec->log, 0, \"buffer overflow\");\n        return NGX_ERROR;\n    }\n\n    *b->last++ = (u_char) (qlen >> 8);\n    *b->last++ = (u_char) qlen;\n    b->last = ngx_cpymem(b->last, query, qlen);\n\n    if (rc == NGX_OK) {\n        ngx_resolver_tcp_write(rec->tcp->write);\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_resolver_resend_handler(ngx_event_t *ev)\n{\n    time_t           timer, atimer, stimer, ntimer;\n#if (NGX_HAVE_INET6)\n    time_t           a6timer;\n#endif\n    ngx_resolver_t  *r;\n\n    r = ev->data;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_CORE, r->log, 0,\n                   \"resolver resend handler\");\n\n    /* lock name mutex */\n\n    ntimer = ngx_resolver_resend(r, &r->name_rbtree, &r->name_resend_queue);\n\n    stimer = ngx_resolver_resend(r, &r->srv_rbtree, &r->srv_resend_queue);\n\n    /* unlock name mutex */\n\n    /* lock addr mutex */\n\n    atimer = ngx_resolver_resend(r, &r->addr_rbtree, &r->addr_resend_queue);\n\n    /* unlock addr mutex */\n\n#if (NGX_HAVE_INET6)\n\n    /* lock addr6 mutex */\n\n    a6timer = ngx_resolver_resend(r, &r->addr6_rbtree, &r->addr6_resend_queue);\n\n    /* unlock addr6 mutex */\n\n#endif\n\n    timer = ntimer;\n\n    if (timer == 0) {\n        timer = atimer;\n\n    } else if (atimer) {\n        timer = ngx_min(timer, atimer);\n    }\n\n    if (timer == 0) {\n        timer = stimer;\n\n    } else if (stimer) {\n        timer = ngx_min(timer, stimer);\n    }\n\n#if (NGX_HAVE_INET6)\n\n    if (timer == 0) {\n        timer = a6timer;\n\n    } else if (a6timer) {\n        timer = ngx_min(timer, a6timer);\n    }\n\n#endif\n\n    if (timer) {\n        ngx_add_timer(r->event, (ngx_msec_t) (timer * 1000));\n    }\n}\n\n\nstatic time_t\nngx_resolver_resend(ngx_resolver_t *r, ngx_rbtree_t *tree, ngx_queue_t *queue)\n{\n    time_t                now;\n    ngx_queue_t          *q;\n    ngx_resolver_node_t  *rn;\n\n    now = ngx_time();\n\n    for ( ;; ) {\n        if (ngx_queue_empty(queue)) {\n            return 0;\n        }\n\n        q = ngx_queue_last(queue);\n\n        rn = ngx_queue_data(q, ngx_resolver_node_t, queue);\n\n        if (now < rn->expire) {\n            return rn->expire - now;\n        }\n\n        ngx_log_debug3(NGX_LOG_DEBUG_CORE, r->log, 0,\n                       \"resolver resend \\\"%*s\\\" %p\",\n                       (size_t) rn->nlen, rn->name, rn->waiting);\n\n        ngx_queue_remove(q);\n\n        if (rn->waiting) {\n\n            if (++rn->last_connection == r->connections.nelts) {\n                rn->last_connection = 0;\n            }\n\n            (void) ngx_resolver_send_query(r, rn);\n\n            rn->expire = now + r->resend_timeout;\n\n            ngx_queue_insert_head(queue, q);\n\n            continue;\n        }\n\n        ngx_rbtree_delete(tree, &rn->node);\n\n        ngx_resolver_free_node(r, rn);\n    }\n}\n\n\nstatic ngx_uint_t\nngx_resolver_resend_empty(ngx_resolver_t *r)\n{\n    return ngx_queue_empty(&r->name_resend_queue)\n           && ngx_queue_empty(&r->srv_resend_queue)\n#if (NGX_HAVE_INET6)\n           && ngx_queue_empty(&r->addr6_resend_queue)\n#endif\n           && ngx_queue_empty(&r->addr_resend_queue);\n}\n\n\nstatic void\nngx_resolver_udp_read(ngx_event_t *rev)\n{\n    ssize_t                     n;\n    ngx_connection_t           *c;\n    ngx_resolver_connection_t  *rec;\n    u_char                      buf[NGX_RESOLVER_UDP_SIZE];\n\n    c = rev->data;\n    rec = c->data;\n\n    do {\n        n = ngx_udp_recv(c, buf, NGX_RESOLVER_UDP_SIZE);\n\n        if (n == NGX_AGAIN) {\n            break;\n        }\n\n        if (n == NGX_ERROR) {\n            goto failed;\n        }\n\n        ngx_resolver_process_response(rec->resolver, buf, n, 0);\n\n    } while (rev->ready);\n\n    if (ngx_handle_read_event(rev, 0) != NGX_OK) {\n        goto failed;\n    }\n\n    return;\n\nfailed:\n\n    ngx_close_connection(rec->udp);\n    rec->udp = NULL;\n}\n\n\nstatic void\nngx_resolver_tcp_write(ngx_event_t *wev)\n{\n    off_t                       sent;\n    ssize_t                     n;\n    ngx_buf_t                  *b;\n    ngx_resolver_t             *r;\n    ngx_connection_t           *c;\n    ngx_resolver_connection_t  *rec;\n\n    c = wev->data;\n    rec = c->data;\n    b = rec->write_buf;\n    r = rec->resolver;\n\n    if (wev->timedout) {\n        goto failed;\n    }\n\n    sent = c->sent;\n\n    while (wev->ready && b->pos < b->last) {\n        n = ngx_send(c, b->pos, b->last - b->pos);\n\n        if (n == NGX_AGAIN) {\n            break;\n        }\n\n        if (n == NGX_ERROR) {\n            goto failed;\n        }\n\n        b->pos += n;\n    }\n\n    if (b->pos != b->start) {\n        b->last = ngx_movemem(b->start, b->pos, b->last - b->pos);\n        b->pos = b->start;\n    }\n\n    if (c->sent != sent) {\n        ngx_add_timer(wev, (ngx_msec_t) (r->tcp_timeout * 1000));\n    }\n\n    if (ngx_handle_write_event(wev, 0) != NGX_OK) {\n        goto failed;\n    }\n\n    return;\n\nfailed:\n\n    ngx_close_connection(c);\n    rec->tcp = NULL;\n}\n\n\nstatic void\nngx_resolver_tcp_read(ngx_event_t *rev)\n{\n    u_char                     *p;\n    size_t                      size;\n    ssize_t                     n;\n    u_short                     qlen;\n    ngx_buf_t                  *b;\n    ngx_resolver_t             *r;\n    ngx_connection_t           *c;\n    ngx_resolver_connection_t  *rec;\n\n    c = rev->data;\n    rec = c->data;\n    b = rec->read_buf;\n    r = rec->resolver;\n\n    while (rev->ready) {\n        n = ngx_recv(c, b->last, b->end - b->last);\n\n        if (n == NGX_AGAIN) {\n            break;\n        }\n\n        if (n == NGX_ERROR || n == 0) {\n            goto failed;\n        }\n\n        b->last += n;\n\n        for ( ;; ) {\n            p = b->pos;\n            size = b->last - p;\n\n            if (size < 2) {\n                break;\n            }\n\n            qlen = (u_short) *p++ << 8;\n            qlen += *p++;\n\n            if (size < (size_t) (2 + qlen)) {\n                break;\n            }\n\n            ngx_resolver_process_response(r, p, qlen, 1);\n\n            b->pos += 2 + qlen;\n        }\n\n        if (b->pos != b->start) {\n            b->last = ngx_movemem(b->start, b->pos, b->last - b->pos);\n            b->pos = b->start;\n        }\n    }\n\n    if (ngx_handle_read_event(rev, 0) != NGX_OK) {\n        goto failed;\n    }\n\n    return;\n\nfailed:\n\n    ngx_close_connection(c);\n    rec->tcp = NULL;\n}\n\n\nstatic void\nngx_resolver_process_response(ngx_resolver_t *r, u_char *buf, size_t n,\n    ngx_uint_t tcp)\n{\n    char                 *err;\n    ngx_uint_t            i, times, ident, qident, flags, code, nqs, nan, trunc,\n                          qtype, qclass;\n#if (NGX_HAVE_INET6)\n    ngx_uint_t            qident6;\n#endif\n    ngx_queue_t          *q;\n    ngx_resolver_qs_t    *qs;\n    ngx_resolver_hdr_t   *response;\n    ngx_resolver_node_t  *rn;\n\n    if (n < sizeof(ngx_resolver_hdr_t)) {\n        goto short_response;\n    }\n\n    response = (ngx_resolver_hdr_t *) buf;\n\n    ident = (response->ident_hi << 8) + response->ident_lo;\n    flags = (response->flags_hi << 8) + response->flags_lo;\n    nqs = (response->nqs_hi << 8) + response->nqs_lo;\n    nan = (response->nan_hi << 8) + response->nan_lo;\n    trunc = flags & 0x0200;\n\n    ngx_log_debug6(NGX_LOG_DEBUG_CORE, r->log, 0,\n                   \"resolver DNS response %ui fl:%04Xi %ui/%ui/%ud/%ud\",\n                   ident, flags, nqs, nan,\n                   (response->nns_hi << 8) + response->nns_lo,\n                   (response->nar_hi << 8) + response->nar_lo);\n\n    /* response to a standard query */\n    if ((flags & 0xf870) != 0x8000 || (trunc && tcp)) {\n        ngx_log_error(r->log_level, r->log, 0,\n                      \"invalid %s DNS response %ui fl:%04Xi\",\n                      tcp ? \"TCP\" : \"UDP\", ident, flags);\n        return;\n    }\n\n    code = flags & 0xf;\n\n    if (code == NGX_RESOLVE_FORMERR) {\n\n        times = 0;\n\n        for (q = ngx_queue_head(&r->name_resend_queue);\n             q != ngx_queue_sentinel(&r->name_resend_queue) && times++ < 100;\n             q = ngx_queue_next(q))\n        {\n            rn = ngx_queue_data(q, ngx_resolver_node_t, queue);\n\n            if (rn->query) {\n                qident = (rn->query[0] << 8) + rn->query[1];\n\n                if (qident == ident) {\n                    goto dns_error_name;\n                }\n            }\n\n#if (NGX_HAVE_INET6)\n            if (rn->query6) {\n                qident6 = (rn->query6[0] << 8) + rn->query6[1];\n\n                if (qident6 == ident) {\n                    goto dns_error_name;\n                }\n            }\n#endif\n        }\n\n        goto dns_error;\n    }\n\n    if (code > NGX_RESOLVE_REFUSED) {\n        goto dns_error;\n    }\n\n    if (nqs != 1) {\n        err = \"invalid number of questions in DNS response\";\n        goto done;\n    }\n\n    i = sizeof(ngx_resolver_hdr_t);\n\n    while (i < (ngx_uint_t) n) {\n\n        if (buf[i] & 0xc0) {\n            err = \"unexpected compression pointer in DNS response\";\n            goto done;\n        }\n\n        if (buf[i] == '\\0') {\n            goto found;\n        }\n\n        i += 1 + buf[i];\n    }\n\n    goto short_response;\n\nfound:\n\n    if (i++ == sizeof(ngx_resolver_hdr_t)) {\n        err = \"zero-length domain name in DNS response\";\n        goto done;\n    }\n\n    if (i + sizeof(ngx_resolver_qs_t) + nan * (2 + sizeof(ngx_resolver_an_t))\n        > (ngx_uint_t) n)\n    {\n        goto short_response;\n    }\n\n    qs = (ngx_resolver_qs_t *) &buf[i];\n\n    qtype = (qs->type_hi << 8) + qs->type_lo;\n    qclass = (qs->class_hi << 8) + qs->class_lo;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_CORE, r->log, 0,\n                   \"resolver DNS response qt:%ui cl:%ui\", qtype, qclass);\n\n    if (qclass != 1) {\n        ngx_log_error(r->log_level, r->log, 0,\n                      \"unknown query class %ui in DNS response\", qclass);\n        return;\n    }\n\n    switch (qtype) {\n\n    case NGX_RESOLVE_A:\n#if (NGX_HAVE_INET6)\n    case NGX_RESOLVE_AAAA:\n#endif\n\n        ngx_resolver_process_a(r, buf, n, ident, code, qtype, nan, trunc,\n                               i + sizeof(ngx_resolver_qs_t));\n\n        break;\n\n    case NGX_RESOLVE_SRV:\n\n        ngx_resolver_process_srv(r, buf, n, ident, code, nan, trunc,\n                                 i + sizeof(ngx_resolver_qs_t));\n\n        break;\n\n    case NGX_RESOLVE_PTR:\n\n        ngx_resolver_process_ptr(r, buf, n, ident, code, nan);\n\n        break;\n\n    default:\n        ngx_log_error(r->log_level, r->log, 0,\n                      \"unknown query type %ui in DNS response\", qtype);\n        return;\n    }\n\n    return;\n\nshort_response:\n\n    err = \"short DNS response\";\n\ndone:\n\n    ngx_log_error(r->log_level, r->log, 0, err);\n\n    return;\n\ndns_error_name:\n\n    ngx_log_error(r->log_level, r->log, 0,\n                  \"DNS error (%ui: %s), query id:%ui, name:\\\"%*s\\\"\",\n                  code, ngx_resolver_strerror(code), ident,\n                  (size_t) rn->nlen, rn->name);\n    return;\n\ndns_error:\n\n    ngx_log_error(r->log_level, r->log, 0,\n                  \"DNS error (%ui: %s), query id:%ui\",\n                  code, ngx_resolver_strerror(code), ident);\n    return;\n}\n\n\nstatic void\nngx_resolver_process_a(ngx_resolver_t *r, u_char *buf, size_t n,\n    ngx_uint_t ident, ngx_uint_t code, ngx_uint_t qtype,\n    ngx_uint_t nan, ngx_uint_t trunc, ngx_uint_t ans)\n{\n    char                       *err;\n    u_char                     *cname;\n    size_t                      len;\n    int32_t                     ttl;\n    uint32_t                    hash;\n    in_addr_t                  *addr;\n    ngx_str_t                   name;\n    ngx_uint_t                  type, class, qident, naddrs, a, i, j, start;\n#if (NGX_HAVE_INET6)\n    struct in6_addr            *addr6;\n#endif\n    ngx_resolver_an_t          *an;\n    ngx_resolver_ctx_t         *ctx, *next;\n    ngx_resolver_node_t        *rn;\n    ngx_resolver_addr_t        *addrs;\n    ngx_resolver_connection_t  *rec;\n\n    if (ngx_resolver_copy(r, &name, buf,\n                          buf + sizeof(ngx_resolver_hdr_t), buf + n)\n        != NGX_OK)\n    {\n        return;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0, \"resolver qs:%V\", &name);\n\n    hash = ngx_crc32_short(name.data, name.len);\n\n    /* lock name mutex */\n\n    rn = ngx_resolver_lookup_name(r, &name, hash);\n\n    if (rn == NULL) {\n        ngx_log_error(r->log_level, r->log, 0,\n                      \"unexpected DNS response for %V\", &name);\n        ngx_resolver_free(r, name.data);\n        goto failed;\n    }\n\n    switch (qtype) {\n\n#if (NGX_HAVE_INET6)\n    case NGX_RESOLVE_AAAA:\n\n        if (rn->query6 == NULL || rn->naddrs6 != (u_short) -1) {\n            ngx_log_error(r->log_level, r->log, 0,\n                          \"unexpected DNS response for %V\", &name);\n            ngx_resolver_free(r, name.data);\n            goto failed;\n        }\n\n        if (trunc && rn->tcp6) {\n            ngx_resolver_free(r, name.data);\n            goto failed;\n        }\n\n        qident = (rn->query6[0] << 8) + rn->query6[1];\n\n        break;\n#endif\n\n    default: /* NGX_RESOLVE_A */\n\n        if (rn->query == NULL || rn->naddrs != (u_short) -1) {\n            ngx_log_error(r->log_level, r->log, 0,\n                          \"unexpected DNS response for %V\", &name);\n            ngx_resolver_free(r, name.data);\n            goto failed;\n        }\n\n        if (trunc && rn->tcp) {\n            ngx_resolver_free(r, name.data);\n            goto failed;\n        }\n\n        qident = (rn->query[0] << 8) + rn->query[1];\n    }\n\n    if (ident != qident) {\n        ngx_log_error(r->log_level, r->log, 0,\n                      \"wrong ident %ui in DNS response for %V, expect %ui\",\n                      ident, &name, qident);\n        ngx_resolver_free(r, name.data);\n        goto failed;\n    }\n\n    ngx_resolver_free(r, name.data);\n\n    if (trunc) {\n\n        ngx_queue_remove(&rn->queue);\n\n        if (rn->waiting == NULL) {\n            ngx_rbtree_delete(&r->name_rbtree, &rn->node);\n            ngx_resolver_free_node(r, rn);\n            goto next;\n        }\n\n        rec = r->connections.elts;\n        rec = &rec[rn->last_connection];\n\n        switch (qtype) {\n\n#if (NGX_HAVE_INET6)\n        case NGX_RESOLVE_AAAA:\n\n            rn->tcp6 = 1;\n\n            (void) ngx_resolver_send_tcp_query(r, rec, rn->query6, rn->qlen);\n\n            break;\n#endif\n\n        default: /* NGX_RESOLVE_A */\n\n            rn->tcp = 1;\n\n            (void) ngx_resolver_send_tcp_query(r, rec, rn->query, rn->qlen);\n        }\n\n        rn->expire = ngx_time() + r->resend_timeout;\n\n        ngx_queue_insert_head(&r->name_resend_queue, &rn->queue);\n\n        goto next;\n    }\n\n    if (code == 0 && rn->code) {\n        code = rn->code;\n    }\n\n    if (code == 0 && nan == 0) {\n\n#if (NGX_HAVE_INET6)\n        switch (qtype) {\n\n        case NGX_RESOLVE_AAAA:\n\n            rn->naddrs6 = 0;\n\n            if (rn->naddrs == (u_short) -1) {\n                goto next;\n            }\n\n            if (rn->naddrs) {\n                goto export;\n            }\n\n            break;\n\n        default: /* NGX_RESOLVE_A */\n\n            rn->naddrs = 0;\n\n            if (rn->naddrs6 == (u_short) -1) {\n                goto next;\n            }\n\n            if (rn->naddrs6) {\n                goto export;\n            }\n        }\n#endif\n\n        code = NGX_RESOLVE_NXDOMAIN;\n    }\n\n    if (code) {\n\n#if (NGX_HAVE_INET6)\n        switch (qtype) {\n\n        case NGX_RESOLVE_AAAA:\n\n            rn->naddrs6 = 0;\n\n            if (rn->naddrs == (u_short) -1) {\n                rn->code = (u_char) code;\n                goto next;\n            }\n\n            break;\n\n        default: /* NGX_RESOLVE_A */\n\n            rn->naddrs = 0;\n\n            if (rn->naddrs6 == (u_short) -1) {\n                rn->code = (u_char) code;\n                goto next;\n            }\n        }\n#endif\n\n        next = rn->waiting;\n        rn->waiting = NULL;\n\n        ngx_queue_remove(&rn->queue);\n\n        ngx_rbtree_delete(&r->name_rbtree, &rn->node);\n\n        /* unlock name mutex */\n\n        while (next) {\n            ctx = next;\n            ctx->state = code;\n            ctx->valid = ngx_time() + (r->valid ? r->valid : 10);\n            next = ctx->next;\n\n            ctx->handler(ctx);\n        }\n\n        ngx_resolver_free_node(r, rn);\n\n        return;\n    }\n\n    i = ans;\n    naddrs = 0;\n    cname = NULL;\n\n    for (a = 0; a < nan; a++) {\n\n        start = i;\n\n        while (i < n) {\n\n            if (buf[i] & 0xc0) {\n                i += 2;\n                goto found;\n            }\n\n            if (buf[i] == 0) {\n                i++;\n                goto test_length;\n            }\n\n            i += 1 + buf[i];\n        }\n\n        goto short_response;\n\n    test_length:\n\n        if (i - start < 2) {\n            err = \"invalid name in DNS response\";\n            goto invalid;\n        }\n\n    found:\n\n        if (i + sizeof(ngx_resolver_an_t) >= n) {\n            goto short_response;\n        }\n\n        an = (ngx_resolver_an_t *) &buf[i];\n\n        type = (an->type_hi << 8) + an->type_lo;\n        class = (an->class_hi << 8) + an->class_lo;\n        len = (an->len_hi << 8) + an->len_lo;\n        ttl = (an->ttl[0] << 24) + (an->ttl[1] << 16)\n            + (an->ttl[2] << 8) + (an->ttl[3]);\n\n        if (class != 1) {\n            ngx_log_error(r->log_level, r->log, 0,\n                          \"unexpected RR class %ui in DNS response\", class);\n            goto failed;\n        }\n\n        if (ttl < 0) {\n            ttl = 0;\n        }\n\n        rn->ttl = ngx_min(rn->ttl, (uint32_t) ttl);\n\n        i += sizeof(ngx_resolver_an_t);\n\n        switch (type) {\n\n        case NGX_RESOLVE_A:\n\n            if (qtype != NGX_RESOLVE_A) {\n                err = \"unexpected A record in DNS response\";\n                goto invalid;\n            }\n\n            if (len != 4) {\n                err = \"invalid A record in DNS response\";\n                goto invalid;\n            }\n\n            if (i + 4 > n) {\n                goto short_response;\n            }\n\n            naddrs++;\n\n            break;\n\n#if (NGX_HAVE_INET6)\n        case NGX_RESOLVE_AAAA:\n\n            if (qtype != NGX_RESOLVE_AAAA) {\n                err = \"unexpected AAAA record in DNS response\";\n                goto invalid;\n            }\n\n            if (len != 16) {\n                err = \"invalid AAAA record in DNS response\";\n                goto invalid;\n            }\n\n            if (i + 16 > n) {\n                goto short_response;\n            }\n\n            naddrs++;\n\n            break;\n#endif\n\n        case NGX_RESOLVE_CNAME:\n\n            cname = &buf[i];\n\n            break;\n\n        case NGX_RESOLVE_DNAME:\n\n            break;\n\n        default:\n\n            ngx_log_error(r->log_level, r->log, 0,\n                          \"unexpected RR type %ui in DNS response\", type);\n        }\n\n        i += len;\n    }\n\n    ngx_log_debug3(NGX_LOG_DEBUG_CORE, r->log, 0,\n                   \"resolver naddrs:%ui cname:%p ttl:%uD\",\n                   naddrs, cname, rn->ttl);\n\n    if (naddrs) {\n\n        switch (qtype) {\n\n#if (NGX_HAVE_INET6)\n        case NGX_RESOLVE_AAAA:\n\n            if (naddrs == 1) {\n                addr6 = &rn->u6.addr6;\n                rn->naddrs6 = 1;\n\n            } else {\n                addr6 = ngx_resolver_alloc(r, naddrs * sizeof(struct in6_addr));\n                if (addr6 == NULL) {\n                    goto failed;\n                }\n\n                rn->u6.addrs6 = addr6;\n                rn->naddrs6 = (u_short) naddrs;\n            }\n\n#if (NGX_SUPPRESS_WARN)\n            addr = NULL;\n#endif\n\n            break;\n#endif\n\n        default: /* NGX_RESOLVE_A */\n\n            if (naddrs == 1) {\n                addr = &rn->u.addr;\n                rn->naddrs = 1;\n\n            } else {\n                addr = ngx_resolver_alloc(r, naddrs * sizeof(in_addr_t));\n                if (addr == NULL) {\n                    goto failed;\n                }\n\n                rn->u.addrs = addr;\n                rn->naddrs = (u_short) naddrs;\n            }\n\n#if (NGX_HAVE_INET6 && NGX_SUPPRESS_WARN)\n            addr6 = NULL;\n#endif\n        }\n\n        j = 0;\n        i = ans;\n\n        for (a = 0; a < nan; a++) {\n\n            for ( ;; ) {\n\n                if (buf[i] & 0xc0) {\n                    i += 2;\n                    break;\n                }\n\n                if (buf[i] == 0) {\n                    i++;\n                    break;\n                }\n\n                i += 1 + buf[i];\n            }\n\n            an = (ngx_resolver_an_t *) &buf[i];\n\n            type = (an->type_hi << 8) + an->type_lo;\n            len = (an->len_hi << 8) + an->len_lo;\n\n            i += sizeof(ngx_resolver_an_t);\n\n            if (type == NGX_RESOLVE_A) {\n\n                addr[j] = htonl((buf[i] << 24) + (buf[i + 1] << 16)\n                                + (buf[i + 2] << 8) + (buf[i + 3]));\n\n                if (++j == naddrs) {\n\n#if (NGX_HAVE_INET6)\n                    if (rn->naddrs6 == (u_short) -1) {\n                        goto next;\n                    }\n#endif\n\n                    break;\n                }\n            }\n\n#if (NGX_HAVE_INET6)\n            else if (type == NGX_RESOLVE_AAAA) {\n\n                ngx_memcpy(addr6[j].s6_addr, &buf[i], 16);\n\n                if (++j == naddrs) {\n\n                    if (rn->naddrs == (u_short) -1) {\n                        goto next;\n                    }\n\n                    break;\n                }\n            }\n#endif\n\n            i += len;\n        }\n    }\n\n    switch (qtype) {\n\n#if (NGX_HAVE_INET6)\n    case NGX_RESOLVE_AAAA:\n\n        if (rn->naddrs6 == (u_short) -1) {\n            rn->naddrs6 = 0;\n        }\n\n        break;\n#endif\n\n    default: /* NGX_RESOLVE_A */\n\n        if (rn->naddrs == (u_short) -1) {\n            rn->naddrs = 0;\n        }\n    }\n\n    if (rn->naddrs != (u_short) -1\n#if (NGX_HAVE_INET6)\n        && rn->naddrs6 != (u_short) -1\n#endif\n        && rn->naddrs\n#if (NGX_HAVE_INET6)\n           + rn->naddrs6\n#endif\n           > 0)\n    {\n\n#if (NGX_HAVE_INET6)\n    export:\n#endif\n\n        naddrs = rn->naddrs;\n#if (NGX_HAVE_INET6)\n        naddrs += rn->naddrs6;\n#endif\n\n        if (naddrs == 1 && rn->naddrs == 1) {\n            addrs = NULL;\n\n        } else {\n            addrs = ngx_resolver_export(r, rn, 0);\n            if (addrs == NULL) {\n                goto failed;\n            }\n        }\n\n        ngx_queue_remove(&rn->queue);\n\n        rn->valid = ngx_time() + (r->valid ? r->valid : (time_t) rn->ttl);\n        rn->expire = ngx_time() + r->expire;\n\n        ngx_queue_insert_head(&r->name_expire_queue, &rn->queue);\n\n        next = rn->waiting;\n        rn->waiting = NULL;\n\n        /* unlock name mutex */\n\n        while (next) {\n            ctx = next;\n            ctx->state = NGX_OK;\n            ctx->valid = rn->valid;\n            ctx->naddrs = naddrs;\n\n            if (addrs == NULL) {\n                ctx->addrs = &ctx->addr;\n                ctx->addr.sockaddr = (struct sockaddr *) &ctx->sin;\n                ctx->addr.socklen = sizeof(struct sockaddr_in);\n                ngx_memzero(&ctx->sin, sizeof(struct sockaddr_in));\n                ctx->sin.sin_family = AF_INET;\n                ctx->sin.sin_addr.s_addr = rn->u.addr;\n\n            } else {\n                ctx->addrs = addrs;\n            }\n\n            next = ctx->next;\n\n            ctx->handler(ctx);\n        }\n\n        if (addrs != NULL) {\n            ngx_resolver_free(r, addrs->sockaddr);\n            ngx_resolver_free(r, addrs);\n        }\n\n        ngx_resolver_free(r, rn->query);\n        rn->query = NULL;\n#if (NGX_HAVE_INET6)\n        rn->query6 = NULL;\n#endif\n\n        return;\n    }\n\n    if (cname) {\n\n        /* CNAME only */\n\n        if (rn->naddrs == (u_short) -1\n#if (NGX_HAVE_INET6)\n            || rn->naddrs6 == (u_short) -1\n#endif\n            )\n        {\n            goto next;\n        }\n\n        if (ngx_resolver_copy(r, &name, buf, cname, buf + n) != NGX_OK) {\n            goto failed;\n        }\n\n        ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0,\n                       \"resolver cname:\\\"%V\\\"\", &name);\n\n        ngx_queue_remove(&rn->queue);\n\n        rn->cnlen = (u_short) name.len;\n        rn->u.cname = name.data;\n\n        rn->valid = ngx_time() + (r->valid ? r->valid : (time_t) rn->ttl);\n        rn->expire = ngx_time() + r->expire;\n\n        ngx_queue_insert_head(&r->name_expire_queue, &rn->queue);\n\n        ngx_resolver_free(r, rn->query);\n        rn->query = NULL;\n#if (NGX_HAVE_INET6)\n        rn->query6 = NULL;\n#endif\n\n        ctx = rn->waiting;\n        rn->waiting = NULL;\n\n        if (ctx) {\n\n            if (ctx->recursion++ >= NGX_RESOLVER_MAX_RECURSION) {\n\n                /* unlock name mutex */\n\n                do {\n                    ctx->state = NGX_RESOLVE_NXDOMAIN;\n                    next = ctx->next;\n\n                    ctx->handler(ctx);\n\n                    ctx = next;\n                } while (ctx);\n\n                return;\n            }\n\n            for (next = ctx; next; next = next->next) {\n                next->node = NULL;\n            }\n\n            (void) ngx_resolve_name_locked(r, ctx, &name);\n        }\n\n        /* unlock name mutex */\n\n        return;\n    }\n\n    ngx_log_error(r->log_level, r->log, 0,\n                  \"no A or CNAME types in DNS response\");\n    return;\n\nshort_response:\n\n    err = \"short DNS response\";\n\ninvalid:\n\n    /* unlock name mutex */\n\n    ngx_log_error(r->log_level, r->log, 0, err);\n\n    return;\n\nfailed:\n\nnext:\n\n    /* unlock name mutex */\n\n    return;\n}\n\n\nstatic void\nngx_resolver_process_srv(ngx_resolver_t *r, u_char *buf, size_t n,\n    ngx_uint_t ident, ngx_uint_t code, ngx_uint_t nan,\n    ngx_uint_t trunc, ngx_uint_t ans)\n{\n    char                       *err;\n    u_char                     *cname;\n    size_t                      len;\n    int32_t                     ttl;\n    uint32_t                    hash;\n    ngx_str_t                   name;\n    ngx_uint_t                  type, qident, class, start, nsrvs, a, i, j;\n    ngx_resolver_an_t          *an;\n    ngx_resolver_ctx_t         *ctx, *next;\n    ngx_resolver_srv_t         *srvs;\n    ngx_resolver_node_t        *rn;\n    ngx_resolver_connection_t  *rec;\n\n    if (ngx_resolver_copy(r, &name, buf,\n                          buf + sizeof(ngx_resolver_hdr_t), buf + n)\n        != NGX_OK)\n    {\n        return;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0, \"resolver qs:%V\", &name);\n\n    hash = ngx_crc32_short(name.data, name.len);\n\n    rn = ngx_resolver_lookup_srv(r, &name, hash);\n\n    if (rn == NULL || rn->query == NULL) {\n        ngx_log_error(r->log_level, r->log, 0,\n                      \"unexpected DNS response for %V\", &name);\n        ngx_resolver_free(r, name.data);\n        goto failed;\n    }\n\n    if (trunc && rn->tcp) {\n        ngx_resolver_free(r, name.data);\n        goto failed;\n    }\n\n    qident = (rn->query[0] << 8) + rn->query[1];\n\n    if (ident != qident) {\n        ngx_log_error(r->log_level, r->log, 0,\n                      \"wrong ident %ui in DNS response for %V, expect %ui\",\n                      ident, &name, qident);\n        ngx_resolver_free(r, name.data);\n        goto failed;\n    }\n\n    ngx_resolver_free(r, name.data);\n\n    if (trunc) {\n\n        ngx_queue_remove(&rn->queue);\n\n        if (rn->waiting == NULL) {\n            ngx_rbtree_delete(&r->srv_rbtree, &rn->node);\n            ngx_resolver_free_node(r, rn);\n            return;\n        }\n\n        rec = r->connections.elts;\n        rec = &rec[rn->last_connection];\n\n        rn->tcp = 1;\n\n        (void) ngx_resolver_send_tcp_query(r, rec, rn->query, rn->qlen);\n\n        rn->expire = ngx_time() + r->resend_timeout;\n\n        ngx_queue_insert_head(&r->srv_resend_queue, &rn->queue);\n\n        return;\n    }\n\n    if (code == 0 && rn->code) {\n        code = rn->code;\n    }\n\n    if (code == 0 && nan == 0) {\n        code = NGX_RESOLVE_NXDOMAIN;\n    }\n\n    if (code) {\n        next = rn->waiting;\n        rn->waiting = NULL;\n\n        ngx_queue_remove(&rn->queue);\n\n        ngx_rbtree_delete(&r->srv_rbtree, &rn->node);\n\n        while (next) {\n            ctx = next;\n            ctx->state = code;\n            ctx->valid = ngx_time() + (r->valid ? r->valid : 10);\n            next = ctx->next;\n\n            ctx->handler(ctx);\n        }\n\n        ngx_resolver_free_node(r, rn);\n\n        return;\n    }\n\n    i = ans;\n    nsrvs = 0;\n    cname = NULL;\n\n    for (a = 0; a < nan; a++) {\n\n        start = i;\n\n        while (i < n) {\n\n            if (buf[i] & 0xc0) {\n                i += 2;\n                goto found;\n            }\n\n            if (buf[i] == 0) {\n                i++;\n                goto test_length;\n            }\n\n            i += 1 + buf[i];\n        }\n\n        goto short_response;\n\n    test_length:\n\n        if (i - start < 2) {\n            err = \"invalid name DNS response\";\n            goto invalid;\n        }\n\n    found:\n\n        if (i + sizeof(ngx_resolver_an_t) >= n) {\n            goto short_response;\n        }\n\n        an = (ngx_resolver_an_t *) &buf[i];\n\n        type = (an->type_hi << 8) + an->type_lo;\n        class = (an->class_hi << 8) + an->class_lo;\n        len = (an->len_hi << 8) + an->len_lo;\n        ttl = (an->ttl[0] << 24) + (an->ttl[1] << 16)\n            + (an->ttl[2] << 8) + (an->ttl[3]);\n\n        if (class != 1) {\n            ngx_log_error(r->log_level, r->log, 0,\n                          \"unexpected RR class %ui in DNS response\", class);\n            goto failed;\n        }\n\n        if (ttl < 0) {\n            ttl = 0;\n        }\n\n        rn->ttl = ngx_min(rn->ttl, (uint32_t) ttl);\n\n        i += sizeof(ngx_resolver_an_t);\n\n        switch (type) {\n\n        case NGX_RESOLVE_SRV:\n\n            if (i + 6 > n) {\n                goto short_response;\n            }\n\n            if (ngx_resolver_copy(r, NULL, buf, &buf[i + 6], buf + n)\n                != NGX_OK)\n            {\n                goto failed;\n            }\n\n            nsrvs++;\n\n            break;\n\n        case NGX_RESOLVE_CNAME:\n\n            cname = &buf[i];\n\n            break;\n\n        case NGX_RESOLVE_DNAME:\n\n            break;\n\n        default:\n\n            ngx_log_error(r->log_level, r->log, 0,\n                          \"unexpected RR type %ui in DNS response\", type);\n        }\n\n        i += len;\n    }\n\n    ngx_log_debug3(NGX_LOG_DEBUG_CORE, r->log, 0,\n                   \"resolver nsrvs:%ui cname:%p ttl:%uD\",\n                   nsrvs, cname, rn->ttl);\n\n    if (nsrvs) {\n\n        srvs = ngx_resolver_calloc(r, nsrvs * sizeof(ngx_resolver_srv_t));\n        if (srvs == NULL) {\n            goto failed;\n        }\n\n        rn->u.srvs = srvs;\n        rn->nsrvs = (u_short) nsrvs;\n\n        j = 0;\n        i = ans;\n\n        for (a = 0; a < nan; a++) {\n\n            for ( ;; ) {\n\n                if (buf[i] & 0xc0) {\n                    i += 2;\n                    break;\n                }\n\n                if (buf[i] == 0) {\n                    i++;\n                    break;\n                }\n\n                i += 1 + buf[i];\n            }\n\n            an = (ngx_resolver_an_t *) &buf[i];\n\n            type = (an->type_hi << 8) + an->type_lo;\n            len = (an->len_hi << 8) + an->len_lo;\n\n            i += sizeof(ngx_resolver_an_t);\n\n            if (type == NGX_RESOLVE_SRV) {\n\n                srvs[j].priority = (buf[i] << 8) + buf[i + 1];\n                srvs[j].weight = (buf[i + 2] << 8) + buf[i + 3];\n\n                if (srvs[j].weight == 0) {\n                    srvs[j].weight = 1;\n                }\n\n                srvs[j].port = (buf[i + 4] << 8) + buf[i + 5];\n\n                if (ngx_resolver_copy(r, &srvs[j].name, buf, &buf[i + 6],\n                                      buf + n)\n                    != NGX_OK)\n                {\n                    goto failed;\n                }\n\n                j++;\n            }\n\n            i += len;\n        }\n\n        ngx_sort(srvs, nsrvs, sizeof(ngx_resolver_srv_t),\n                 ngx_resolver_cmp_srvs);\n\n        ngx_resolver_free(r, rn->query);\n        rn->query = NULL;\n\n        ngx_queue_remove(&rn->queue);\n\n        rn->valid = ngx_time() + (r->valid ? r->valid : (time_t) rn->ttl);\n        rn->expire = ngx_time() + r->expire;\n\n        ngx_queue_insert_head(&r->srv_expire_queue, &rn->queue);\n\n        next = rn->waiting;\n        rn->waiting = NULL;\n\n        while (next) {\n            ctx = next;\n            next = ctx->next;\n\n            ngx_resolver_resolve_srv_names(ctx, rn);\n        }\n\n        return;\n    }\n\n    rn->nsrvs = 0;\n\n    if (cname) {\n\n        /* CNAME only */\n\n        if (ngx_resolver_copy(r, &name, buf, cname, buf + n) != NGX_OK) {\n            goto failed;\n        }\n\n        ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0,\n                       \"resolver cname:\\\"%V\\\"\", &name);\n\n        ngx_queue_remove(&rn->queue);\n\n        rn->cnlen = (u_short) name.len;\n        rn->u.cname = name.data;\n\n        rn->valid = ngx_time() + (r->valid ? r->valid : (time_t) rn->ttl);\n        rn->expire = ngx_time() + r->expire;\n\n        ngx_queue_insert_head(&r->srv_expire_queue, &rn->queue);\n\n        ngx_resolver_free(r, rn->query);\n        rn->query = NULL;\n#if (NGX_HAVE_INET6)\n        rn->query6 = NULL;\n#endif\n\n        ctx = rn->waiting;\n        rn->waiting = NULL;\n\n        if (ctx) {\n\n            if (ctx->recursion++ >= NGX_RESOLVER_MAX_RECURSION) {\n\n                /* unlock name mutex */\n\n                do {\n                    ctx->state = NGX_RESOLVE_NXDOMAIN;\n                    next = ctx->next;\n\n                    ctx->handler(ctx);\n\n                    ctx = next;\n                } while (ctx);\n\n                return;\n            }\n\n            for (next = ctx; next; next = next->next) {\n                next->node = NULL;\n            }\n\n            (void) ngx_resolve_name_locked(r, ctx, &name);\n        }\n\n        /* unlock name mutex */\n\n        return;\n    }\n\n    ngx_log_error(r->log_level, r->log, 0, \"no SRV type in DNS response\");\n\n    return;\n\nshort_response:\n\n    err = \"short DNS response\";\n\ninvalid:\n\n    /* unlock name mutex */\n\n    ngx_log_error(r->log_level, r->log, 0, err);\n\n    return;\n\nfailed:\n\n    /* unlock name mutex */\n\n    return;\n}\n\n\nstatic void\nngx_resolver_resolve_srv_names(ngx_resolver_ctx_t *ctx, ngx_resolver_node_t *rn)\n{\n    ngx_uint_t                i;\n    ngx_resolver_t           *r;\n    ngx_resolver_ctx_t       *cctx;\n    ngx_resolver_srv_name_t  *srvs;\n\n    r = ctx->resolver;\n\n    ctx->node = NULL;\n    ctx->state = NGX_OK;\n    ctx->valid = rn->valid;\n    ctx->count = rn->nsrvs;\n\n    srvs = ngx_resolver_calloc(r, rn->nsrvs * sizeof(ngx_resolver_srv_name_t));\n    if (srvs == NULL) {\n        goto failed;\n    }\n\n    ctx->srvs = srvs;\n    ctx->nsrvs = rn->nsrvs;\n\n    if (ctx->event && ctx->event->timer_set) {\n        ngx_del_timer(ctx->event);\n    }\n\n    for (i = 0; i < (ngx_uint_t) rn->nsrvs; i++) {\n        srvs[i].name.data = ngx_resolver_alloc(r, rn->u.srvs[i].name.len);\n        if (srvs[i].name.data == NULL) {\n            goto failed;\n        }\n\n        srvs[i].name.len = rn->u.srvs[i].name.len;\n        ngx_memcpy(srvs[i].name.data, rn->u.srvs[i].name.data,\n                   srvs[i].name.len);\n\n        cctx = ngx_resolve_start(r, NULL);\n        if (cctx == NULL) {\n            goto failed;\n        }\n\n        cctx->name = srvs[i].name;\n        cctx->handler = ngx_resolver_srv_names_handler;\n        cctx->data = ctx;\n        cctx->srvs = &srvs[i];\n        cctx->timeout = ctx->timeout;\n\n        srvs[i].priority = rn->u.srvs[i].priority;\n        srvs[i].weight = rn->u.srvs[i].weight;\n        srvs[i].port = rn->u.srvs[i].port;\n        srvs[i].ctx = cctx;\n\n        if (ngx_resolve_name(cctx) == NGX_ERROR) {\n            srvs[i].ctx = NULL;\n            goto failed;\n        }\n    }\n\n    return;\n\nfailed:\n\n    ctx->state = NGX_ERROR;\n    ctx->valid = ngx_time() + (r->valid ? r->valid : 10);\n\n    ctx->handler(ctx);\n}\n\n\nstatic void\nngx_resolver_srv_names_handler(ngx_resolver_ctx_t *cctx)\n{\n    ngx_uint_t                i;\n    ngx_addr_t               *addrs;\n    ngx_resolver_t           *r;\n    ngx_sockaddr_t           *sockaddr;\n    ngx_resolver_ctx_t       *ctx;\n    ngx_resolver_srv_name_t  *srv;\n\n    r = cctx->resolver;\n    ctx = cctx->data;\n    srv = cctx->srvs;\n\n    ctx->count--;\n    ctx->async |= cctx->async;\n\n    srv->ctx = NULL;\n    srv->state = cctx->state;\n\n    if (cctx->naddrs) {\n\n        ctx->valid = ngx_min(ctx->valid, cctx->valid);\n\n        addrs = ngx_resolver_calloc(r, cctx->naddrs * sizeof(ngx_addr_t));\n        if (addrs == NULL) {\n            srv->state = NGX_ERROR;\n            goto done;\n        }\n\n        sockaddr = ngx_resolver_alloc(r, cctx->naddrs * sizeof(ngx_sockaddr_t));\n        if (sockaddr == NULL) {\n            ngx_resolver_free(r, addrs);\n            srv->state = NGX_ERROR;\n            goto done;\n        }\n\n        for (i = 0; i < cctx->naddrs; i++) {\n            addrs[i].sockaddr = &sockaddr[i].sockaddr;\n            addrs[i].socklen = cctx->addrs[i].socklen;\n\n            ngx_memcpy(&sockaddr[i], cctx->addrs[i].sockaddr,\n                       addrs[i].socklen);\n\n            ngx_inet_set_port(addrs[i].sockaddr, srv->port);\n        }\n\n        srv->addrs = addrs;\n        srv->naddrs = cctx->naddrs;\n    }\n\ndone:\n\n    ngx_resolve_name_done(cctx);\n\n    if (ctx->count == 0) {\n        ngx_resolver_report_srv(r, ctx);\n    }\n}\n\n\nstatic void\nngx_resolver_process_ptr(ngx_resolver_t *r, u_char *buf, size_t n,\n    ngx_uint_t ident, ngx_uint_t code, ngx_uint_t nan)\n{\n    char                 *err;\n    size_t                len;\n    in_addr_t             addr;\n    int32_t               ttl;\n    ngx_int_t             octet;\n    ngx_str_t             name;\n    ngx_uint_t            mask, type, class, qident, a, i, start;\n    ngx_queue_t          *expire_queue;\n    ngx_rbtree_t         *tree;\n    ngx_resolver_an_t    *an;\n    ngx_resolver_ctx_t   *ctx, *next;\n    ngx_resolver_node_t  *rn;\n#if (NGX_HAVE_INET6)\n    uint32_t              hash;\n    ngx_int_t             digit;\n    struct in6_addr       addr6;\n#endif\n\n    if (ngx_resolver_copy(r, &name, buf,\n                          buf + sizeof(ngx_resolver_hdr_t), buf + n)\n        != NGX_OK)\n    {\n        return;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0, \"resolver qs:%V\", &name);\n\n    /* AF_INET */\n\n    addr = 0;\n    i = sizeof(ngx_resolver_hdr_t);\n\n    for (mask = 0; mask < 32; mask += 8) {\n        len = buf[i++];\n\n        octet = ngx_atoi(&buf[i], len);\n        if (octet == NGX_ERROR || octet > 255) {\n            goto invalid_in_addr_arpa;\n        }\n\n        addr += octet << mask;\n        i += len;\n    }\n\n    if (ngx_strcasecmp(&buf[i], (u_char *) \"\\7in-addr\\4arpa\") == 0) {\n        i += sizeof(\"\\7in-addr\\4arpa\");\n\n        /* lock addr mutex */\n\n        rn = ngx_resolver_lookup_addr(r, addr);\n\n        tree = &r->addr_rbtree;\n        expire_queue = &r->addr_expire_queue;\n\n        goto valid;\n    }\n\ninvalid_in_addr_arpa:\n\n#if (NGX_HAVE_INET6)\n\n    i = sizeof(ngx_resolver_hdr_t);\n\n    for (octet = 15; octet >= 0; octet--) {\n        if (buf[i++] != '\\1') {\n            goto invalid_ip6_arpa;\n        }\n\n        digit = ngx_hextoi(&buf[i++], 1);\n        if (digit == NGX_ERROR) {\n            goto invalid_ip6_arpa;\n        }\n\n        addr6.s6_addr[octet] = (u_char) digit;\n\n        if (buf[i++] != '\\1') {\n            goto invalid_ip6_arpa;\n        }\n\n        digit = ngx_hextoi(&buf[i++], 1);\n        if (digit == NGX_ERROR) {\n            goto invalid_ip6_arpa;\n        }\n\n        addr6.s6_addr[octet] += (u_char) (digit * 16);\n    }\n\n    if (ngx_strcasecmp(&buf[i], (u_char *) \"\\3ip6\\4arpa\") == 0) {\n        i += sizeof(\"\\3ip6\\4arpa\");\n\n        /* lock addr mutex */\n\n        hash = ngx_crc32_short(addr6.s6_addr, 16);\n        rn = ngx_resolver_lookup_addr6(r, &addr6, hash);\n\n        tree = &r->addr6_rbtree;\n        expire_queue = &r->addr6_expire_queue;\n\n        goto valid;\n    }\n\ninvalid_ip6_arpa:\n#endif\n\n    ngx_log_error(r->log_level, r->log, 0,\n                  \"invalid in-addr.arpa or ip6.arpa name in DNS response\");\n    ngx_resolver_free(r, name.data);\n    return;\n\nvalid:\n\n    if (rn == NULL || rn->query == NULL) {\n        ngx_log_error(r->log_level, r->log, 0,\n                      \"unexpected DNS response for %V\", &name);\n        ngx_resolver_free(r, name.data);\n        goto failed;\n    }\n\n    qident = (rn->query[0] << 8) + rn->query[1];\n\n    if (ident != qident) {\n        ngx_log_error(r->log_level, r->log, 0,\n                      \"wrong ident %ui in DNS response for %V, expect %ui\",\n                      ident, &name, qident);\n        ngx_resolver_free(r, name.data);\n        goto failed;\n    }\n\n    ngx_resolver_free(r, name.data);\n\n    if (code == 0 && nan == 0) {\n        code = NGX_RESOLVE_NXDOMAIN;\n    }\n\n    if (code) {\n        next = rn->waiting;\n        rn->waiting = NULL;\n\n        ngx_queue_remove(&rn->queue);\n\n        ngx_rbtree_delete(tree, &rn->node);\n\n        /* unlock addr mutex */\n\n        while (next) {\n            ctx = next;\n            ctx->state = code;\n            ctx->valid = ngx_time() + (r->valid ? r->valid : 10);\n            next = ctx->next;\n\n            ctx->handler(ctx);\n        }\n\n        ngx_resolver_free_node(r, rn);\n\n        return;\n    }\n\n    i += sizeof(ngx_resolver_qs_t);\n\n    for (a = 0; a < nan; a++) {\n\n        start = i;\n\n        while (i < n) {\n\n            if (buf[i] & 0xc0) {\n                i += 2;\n                goto found;\n            }\n\n            if (buf[i] == 0) {\n                i++;\n                goto test_length;\n            }\n\n            i += 1 + buf[i];\n        }\n\n        goto short_response;\n\n    test_length:\n\n        if (i - start < 2) {\n            err = \"invalid name in DNS response\";\n            goto invalid;\n        }\n\n    found:\n\n        if (i + sizeof(ngx_resolver_an_t) >= n) {\n            goto short_response;\n        }\n\n        an = (ngx_resolver_an_t *) &buf[i];\n\n        type = (an->type_hi << 8) + an->type_lo;\n        class = (an->class_hi << 8) + an->class_lo;\n        len = (an->len_hi << 8) + an->len_lo;\n        ttl = (an->ttl[0] << 24) + (an->ttl[1] << 16)\n            + (an->ttl[2] << 8) + (an->ttl[3]);\n\n        if (class != 1) {\n            ngx_log_error(r->log_level, r->log, 0,\n                          \"unexpected RR class %ui in DNS response\", class);\n            goto failed;\n        }\n\n        if (ttl < 0) {\n            ttl = 0;\n        }\n\n        ngx_log_debug3(NGX_LOG_DEBUG_CORE, r->log, 0,\n                      \"resolver qt:%ui cl:%ui len:%uz\",\n                      type, class, len);\n\n        i += sizeof(ngx_resolver_an_t);\n\n        switch (type) {\n\n        case NGX_RESOLVE_PTR:\n\n            goto ptr;\n\n        case NGX_RESOLVE_CNAME:\n\n            break;\n\n        default:\n\n            ngx_log_error(r->log_level, r->log, 0,\n                          \"unexpected RR type %ui in DNS response\", type);\n        }\n\n        i += len;\n    }\n\n    /* unlock addr mutex */\n\n    ngx_log_error(r->log_level, r->log, 0,\n                  \"no PTR type in DNS response\");\n    return;\n\nptr:\n\n    if (ngx_resolver_copy(r, &name, buf, buf + i, buf + n) != NGX_OK) {\n        goto failed;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0, \"resolver an:%V\", &name);\n\n    if (name.len != (size_t) rn->nlen\n        || ngx_strncmp(name.data, rn->name, name.len) != 0)\n    {\n        if (rn->nlen) {\n            ngx_resolver_free(r, rn->name);\n        }\n\n        rn->nlen = (u_short) name.len;\n        rn->name = name.data;\n\n        name.data = ngx_resolver_dup(r, rn->name, name.len);\n        if (name.data == NULL) {\n            goto failed;\n        }\n    }\n\n    ngx_queue_remove(&rn->queue);\n\n    rn->valid = ngx_time() + (r->valid ? r->valid : ttl);\n    rn->expire = ngx_time() + r->expire;\n\n    ngx_queue_insert_head(expire_queue, &rn->queue);\n\n    next = rn->waiting;\n    rn->waiting = NULL;\n\n    /* unlock addr mutex */\n\n    while (next) {\n        ctx = next;\n        ctx->state = NGX_OK;\n        ctx->valid = rn->valid;\n        ctx->name = name;\n        next = ctx->next;\n\n        ctx->handler(ctx);\n    }\n\n    ngx_resolver_free(r, name.data);\n\n    return;\n\nshort_response:\n\n    err = \"short DNS response\";\n\ninvalid:\n\n    /* unlock addr mutex */\n\n    ngx_log_error(r->log_level, r->log, 0, err);\n\n    return;\n\nfailed:\n\n    /* unlock addr mutex */\n\n    return;\n}\n\n\nstatic ngx_resolver_node_t *\nngx_resolver_lookup_name(ngx_resolver_t *r, ngx_str_t *name, uint32_t hash)\n{\n    ngx_int_t             rc;\n    ngx_rbtree_node_t    *node, *sentinel;\n    ngx_resolver_node_t  *rn;\n\n    node = r->name_rbtree.root;\n    sentinel = r->name_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        rn = ngx_resolver_node(node);\n\n        rc = ngx_memn2cmp(name->data, rn->name, name->len, rn->nlen);\n\n        if (rc == 0) {\n            return rn;\n        }\n\n        node = (rc < 0) ? node->left : node->right;\n    }\n\n    /* not found */\n\n    return NULL;\n}\n\n\nstatic ngx_resolver_node_t *\nngx_resolver_lookup_srv(ngx_resolver_t *r, ngx_str_t *name, uint32_t hash)\n{\n    ngx_int_t             rc;\n    ngx_rbtree_node_t    *node, *sentinel;\n    ngx_resolver_node_t  *rn;\n\n    node = r->srv_rbtree.root;\n    sentinel = r->srv_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        rn = ngx_resolver_node(node);\n\n        rc = ngx_memn2cmp(name->data, rn->name, name->len, rn->nlen);\n\n        if (rc == 0) {\n            return rn;\n        }\n\n        node = (rc < 0) ? node->left : node->right;\n    }\n\n    /* not found */\n\n    return NULL;\n}\n\n\nstatic ngx_resolver_node_t *\nngx_resolver_lookup_addr(ngx_resolver_t *r, in_addr_t addr)\n{\n    ngx_rbtree_node_t  *node, *sentinel;\n\n    node = r->addr_rbtree.root;\n    sentinel = r->addr_rbtree.sentinel;\n\n    while (node != sentinel) {\n\n        if (addr < node->key) {\n            node = node->left;\n            continue;\n        }\n\n        if (addr > node->key) {\n            node = node->right;\n            continue;\n        }\n\n        /* addr == node->key */\n\n        return ngx_resolver_node(node);\n    }\n\n    /* not found */\n\n    return NULL;\n}\n\n\n#if (NGX_HAVE_INET6)\n\nstatic ngx_resolver_node_t *\nngx_resolver_lookup_addr6(ngx_resolver_t *r, struct in6_addr *addr,\n    uint32_t hash)\n{\n    ngx_int_t             rc;\n    ngx_rbtree_node_t    *node, *sentinel;\n    ngx_resolver_node_t  *rn;\n\n    node = r->addr6_rbtree.root;\n    sentinel = r->addr6_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        rn = ngx_resolver_node(node);\n\n        rc = ngx_memcmp(addr, &rn->addr6, 16);\n\n        if (rc == 0) {\n            return rn;\n        }\n\n        node = (rc < 0) ? node->left : node->right;\n    }\n\n    /* not found */\n\n    return NULL;\n}\n\n#endif\n\n\nstatic void\nngx_resolver_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_resolver_node_t   *rn, *rn_temp;\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            rn = ngx_resolver_node(node);\n            rn_temp = ngx_resolver_node(temp);\n\n            p = (ngx_memn2cmp(rn->name, rn_temp->name, rn->nlen, rn_temp->nlen)\n                 < 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\n#if (NGX_HAVE_INET6)\n\nstatic void\nngx_resolver_rbtree_insert_addr6_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_resolver_node_t   *rn, *rn_temp;\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            rn = ngx_resolver_node(node);\n            rn_temp = ngx_resolver_node(temp);\n\n            p = (ngx_memcmp(&rn->addr6, &rn_temp->addr6, 16)\n                 < 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#endif\n\n\nstatic ngx_int_t\nngx_resolver_create_name_query(ngx_resolver_t *r, ngx_resolver_node_t *rn,\n    ngx_str_t *name)\n{\n    u_char              *p, *s;\n    size_t               len, nlen;\n    ngx_uint_t           ident;\n    ngx_resolver_qs_t   *qs;\n    ngx_resolver_hdr_t  *query;\n\n    nlen = name->len ? (1 + name->len + 1) : 1;\n\n    len = sizeof(ngx_resolver_hdr_t) + nlen + sizeof(ngx_resolver_qs_t);\n\n#if (NGX_HAVE_INET6)\n    p = ngx_resolver_alloc(r, len * (r->ipv4 + r->ipv6));\n#else\n    p = ngx_resolver_alloc(r, len);\n#endif\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    rn->qlen = (u_short) len;\n    rn->query = p;\n\n#if (NGX_HAVE_INET6)\n    if (r->ipv6) {\n        rn->query6 = r->ipv4 ? (p + len) : p;\n    }\n#endif\n\n    query = (ngx_resolver_hdr_t *) p;\n\n    if (r->ipv4) {\n        ident = ngx_random();\n\n        ngx_log_debug2(NGX_LOG_DEBUG_CORE, r->log, 0,\n                       \"resolve: \\\"%V\\\" A %i\", name, ident & 0xffff);\n\n        query->ident_hi = (u_char) ((ident >> 8) & 0xff);\n        query->ident_lo = (u_char) (ident & 0xff);\n    }\n\n    /* recursion query */\n    query->flags_hi = 1; query->flags_lo = 0;\n\n    /* one question */\n    query->nqs_hi = 0; query->nqs_lo = 1;\n    query->nan_hi = 0; query->nan_lo = 0;\n    query->nns_hi = 0; query->nns_lo = 0;\n    query->nar_hi = 0; query->nar_lo = 0;\n\n    p += sizeof(ngx_resolver_hdr_t) + nlen;\n\n    qs = (ngx_resolver_qs_t *) p;\n\n    /* query type */\n    qs->type_hi = 0; qs->type_lo = NGX_RESOLVE_A;\n\n    /* IN query class */\n    qs->class_hi = 0; qs->class_lo = 1;\n\n    /* convert \"www.example.com\" to \"\\3www\\7example\\3com\\0\" */\n\n    len = 0;\n    p--;\n    *p-- = '\\0';\n\n    if (name->len == 0)  {\n        return NGX_DECLINED;\n    }\n\n    for (s = name->data + name->len - 1; s >= name->data; s--) {\n        if (*s != '.') {\n            *p = *s;\n            len++;\n\n        } else {\n            if (len == 0 || len > 255) {\n                return NGX_DECLINED;\n            }\n\n            *p = (u_char) len;\n            len = 0;\n        }\n\n        p--;\n    }\n\n    if (len == 0 || len > 255) {\n        return NGX_DECLINED;\n    }\n\n    *p = (u_char) len;\n\n#if (NGX_HAVE_INET6)\n    if (!r->ipv6) {\n        return NGX_OK;\n    }\n\n    p = rn->query6;\n\n    if (r->ipv4) {\n        ngx_memcpy(p, rn->query, rn->qlen);\n    }\n\n    query = (ngx_resolver_hdr_t *) p;\n\n    ident = ngx_random();\n\n    ngx_log_debug2(NGX_LOG_DEBUG_CORE, r->log, 0,\n                   \"resolve: \\\"%V\\\" AAAA %i\", name, ident & 0xffff);\n\n    query->ident_hi = (u_char) ((ident >> 8) & 0xff);\n    query->ident_lo = (u_char) (ident & 0xff);\n\n    p += sizeof(ngx_resolver_hdr_t) + nlen;\n\n    qs = (ngx_resolver_qs_t *) p;\n\n    qs->type_lo = NGX_RESOLVE_AAAA;\n#endif\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_resolver_create_srv_query(ngx_resolver_t *r, ngx_resolver_node_t *rn,\n    ngx_str_t *name)\n{\n    u_char              *p, *s;\n    size_t               len, nlen;\n    ngx_uint_t           ident;\n    ngx_resolver_qs_t   *qs;\n    ngx_resolver_hdr_t  *query;\n\n    nlen = name->len ? (1 + name->len + 1) : 1;\n\n    len = sizeof(ngx_resolver_hdr_t) + nlen + sizeof(ngx_resolver_qs_t);\n\n    p = ngx_resolver_alloc(r, len);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    rn->qlen = (u_short) len;\n    rn->query = p;\n\n    query = (ngx_resolver_hdr_t *) p;\n\n    ident = ngx_random();\n\n    ngx_log_debug2(NGX_LOG_DEBUG_CORE, r->log, 0,\n                   \"resolve: \\\"%V\\\" SRV %i\", name, ident & 0xffff);\n\n    query->ident_hi = (u_char) ((ident >> 8) & 0xff);\n    query->ident_lo = (u_char) (ident & 0xff);\n\n    /* recursion query */\n    query->flags_hi = 1; query->flags_lo = 0;\n\n    /* one question */\n    query->nqs_hi = 0; query->nqs_lo = 1;\n    query->nan_hi = 0; query->nan_lo = 0;\n    query->nns_hi = 0; query->nns_lo = 0;\n    query->nar_hi = 0; query->nar_lo = 0;\n\n    p += sizeof(ngx_resolver_hdr_t) + nlen;\n\n    qs = (ngx_resolver_qs_t *) p;\n\n    /* query type */\n    qs->type_hi = 0; qs->type_lo = NGX_RESOLVE_SRV;\n\n    /* IN query class */\n    qs->class_hi = 0; qs->class_lo = 1;\n\n    /* converts \"www.example.com\" to \"\\3www\\7example\\3com\\0\" */\n\n    len = 0;\n    p--;\n    *p-- = '\\0';\n\n    if (name->len == 0)  {\n        return NGX_DECLINED;\n    }\n\n    for (s = name->data + name->len - 1; s >= name->data; s--) {\n        if (*s != '.') {\n            *p = *s;\n            len++;\n\n        } else {\n            if (len == 0 || len > 255) {\n                return NGX_DECLINED;\n            }\n\n            *p = (u_char) len;\n            len = 0;\n        }\n\n        p--;\n    }\n\n    if (len == 0 || len > 255) {\n        return NGX_DECLINED;\n    }\n\n    *p = (u_char) len;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_resolver_create_addr_query(ngx_resolver_t *r, ngx_resolver_node_t *rn,\n    ngx_resolver_addr_t *addr)\n{\n    u_char               *p, *d;\n    size_t                len;\n    in_addr_t             inaddr;\n    ngx_int_t             n;\n    ngx_uint_t            ident;\n    ngx_resolver_hdr_t   *query;\n    struct sockaddr_in   *sin;\n#if (NGX_HAVE_INET6)\n    struct sockaddr_in6  *sin6;\n#endif\n\n    switch (addr->sockaddr->sa_family) {\n\n#if (NGX_HAVE_INET6)\n    case AF_INET6:\n        len = sizeof(ngx_resolver_hdr_t)\n              + 64 + sizeof(\".ip6.arpa.\") - 1\n              + sizeof(ngx_resolver_qs_t);\n\n        break;\n#endif\n\n    default: /* AF_INET */\n        len = sizeof(ngx_resolver_hdr_t)\n              + sizeof(\".255.255.255.255.in-addr.arpa.\") - 1\n              + sizeof(ngx_resolver_qs_t);\n    }\n\n    p = ngx_resolver_alloc(r, len);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    rn->query = p;\n    query = (ngx_resolver_hdr_t *) p;\n\n    ident = ngx_random();\n\n    query->ident_hi = (u_char) ((ident >> 8) & 0xff);\n    query->ident_lo = (u_char) (ident & 0xff);\n\n    /* recursion query */\n    query->flags_hi = 1; query->flags_lo = 0;\n\n    /* one question */\n    query->nqs_hi = 0; query->nqs_lo = 1;\n    query->nan_hi = 0; query->nan_lo = 0;\n    query->nns_hi = 0; query->nns_lo = 0;\n    query->nar_hi = 0; query->nar_lo = 0;\n\n    p += sizeof(ngx_resolver_hdr_t);\n\n    switch (addr->sockaddr->sa_family) {\n\n#if (NGX_HAVE_INET6)\n    case AF_INET6:\n        sin6 = (struct sockaddr_in6 *) addr->sockaddr;\n\n        for (n = 15; n >= 0; n--) {\n            p = ngx_sprintf(p, \"\\1%xd\\1%xd\",\n                            sin6->sin6_addr.s6_addr[n] & 0xf,\n                            (sin6->sin6_addr.s6_addr[n] >> 4) & 0xf);\n        }\n\n        p = ngx_cpymem(p, \"\\3ip6\\4arpa\\0\", 10);\n\n        break;\n#endif\n\n    default: /* AF_INET */\n\n        sin = (struct sockaddr_in *) addr->sockaddr;\n        inaddr = ntohl(sin->sin_addr.s_addr);\n\n        for (n = 0; n < 32; n += 8) {\n            d = ngx_sprintf(&p[1], \"%ud\", (inaddr >> n) & 0xff);\n            *p = (u_char) (d - &p[1]);\n            p = d;\n        }\n\n        p = ngx_cpymem(p, \"\\7in-addr\\4arpa\\0\", 14);\n    }\n\n    /* query type \"PTR\", IN query class */\n    p = ngx_cpymem(p, \"\\0\\14\\0\\1\", 4);\n\n    rn->qlen = (u_short) (p - rn->query);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_resolver_copy(ngx_resolver_t *r, ngx_str_t *name, u_char *buf, u_char *src,\n    u_char *last)\n{\n    char        *err;\n    u_char      *p, *dst;\n    size_t       len;\n    ngx_uint_t   i, n;\n\n    p = src;\n    len = 0;\n\n    /*\n     * compression pointers allow to create endless loop, so we set limit;\n     * 128 pointers should be enough to store 255-byte name\n     */\n\n    for (i = 0; i < 128; i++) {\n        n = *p++;\n\n        if (n == 0) {\n            goto done;\n        }\n\n        if (n & 0xc0) {\n            if ((n & 0xc0) != 0xc0) {\n                err = \"invalid label type in DNS response\";\n                goto invalid;\n            }\n\n            if (p >= last) {\n                err = \"name is out of DNS response\";\n                goto invalid;\n            }\n\n            n = ((n & 0x3f) << 8) + *p;\n            p = &buf[n];\n\n        } else {\n            len += 1 + n;\n            p = &p[n];\n        }\n\n        if (p >= last) {\n            err = \"name is out of DNS response\";\n            goto invalid;\n        }\n    }\n\n    err = \"compression pointers loop in DNS response\";\n\ninvalid:\n\n    ngx_log_error(r->log_level, r->log, 0, err);\n\n    return NGX_ERROR;\n\ndone:\n\n    if (name == NULL) {\n        return NGX_OK;\n    }\n\n    if (len == 0) {\n        ngx_str_null(name);\n        return NGX_OK;\n    }\n\n    dst = ngx_resolver_alloc(r, len);\n    if (dst == NULL) {\n        return NGX_ERROR;\n    }\n\n    name->data = dst;\n\n    for ( ;; ) {\n        n = *src++;\n\n        if (n == 0) {\n            name->len = dst - name->data - 1;\n            return NGX_OK;\n        }\n\n        if (n & 0xc0) {\n            n = ((n & 0x3f) << 8) + *src;\n            src = &buf[n];\n\n        } else {\n            ngx_strlow(dst, src, n);\n            dst += n;\n            src += n;\n            *dst++ = '.';\n        }\n    }\n}\n\n\nstatic ngx_int_t\nngx_resolver_set_timeout(ngx_resolver_t *r, ngx_resolver_ctx_t *ctx)\n{\n    if (ctx->event || ctx->timeout == 0) {\n        return NGX_OK;\n    }\n\n    ctx->event = ngx_resolver_calloc(r, sizeof(ngx_event_t));\n    if (ctx->event == NULL) {\n        return NGX_ERROR;\n    }\n\n    ctx->event->handler = ngx_resolver_timeout_handler;\n    ctx->event->data = ctx;\n    ctx->event->log = r->log;\n    ctx->event->cancelable = ctx->cancelable;\n    ctx->ident = -1;\n\n    ngx_add_timer(ctx->event, ctx->timeout);\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_resolver_timeout_handler(ngx_event_t *ev)\n{\n    ngx_resolver_ctx_t  *ctx;\n\n    ctx = ev->data;\n\n    ctx->state = NGX_RESOLVE_TIMEDOUT;\n\n    ctx->handler(ctx);\n}\n\n\nstatic void\nngx_resolver_free_node(ngx_resolver_t *r, ngx_resolver_node_t *rn)\n{\n    ngx_uint_t  i;\n\n    /* lock alloc mutex */\n\n    if (rn->query) {\n        ngx_resolver_free_locked(r, rn->query);\n    }\n\n    if (rn->name) {\n        ngx_resolver_free_locked(r, rn->name);\n    }\n\n    if (rn->cnlen) {\n        ngx_resolver_free_locked(r, rn->u.cname);\n    }\n\n    if (rn->naddrs > 1 && rn->naddrs != (u_short) -1) {\n        ngx_resolver_free_locked(r, rn->u.addrs);\n    }\n\n#if (NGX_HAVE_INET6)\n    if (rn->naddrs6 > 1 && rn->naddrs6 != (u_short) -1) {\n        ngx_resolver_free_locked(r, rn->u6.addrs6);\n    }\n#endif\n\n    if (rn->nsrvs) {\n        for (i = 0; i < (ngx_uint_t) rn->nsrvs; i++) {\n            if (rn->u.srvs[i].name.data) {\n                ngx_resolver_free_locked(r, rn->u.srvs[i].name.data);\n            }\n        }\n\n        ngx_resolver_free_locked(r, rn->u.srvs);\n    }\n\n    ngx_resolver_free_locked(r, rn);\n\n    /* unlock alloc mutex */\n}\n\n\nstatic void *\nngx_resolver_alloc(ngx_resolver_t *r, size_t size)\n{\n    u_char  *p;\n\n    /* lock alloc mutex */\n\n    p = ngx_alloc(size, r->log);\n\n    /* unlock alloc mutex */\n\n    return p;\n}\n\n\nstatic void *\nngx_resolver_calloc(ngx_resolver_t *r, size_t size)\n{\n    u_char  *p;\n\n    p = ngx_resolver_alloc(r, size);\n\n    if (p) {\n        ngx_memzero(p, size);\n    }\n\n    return p;\n}\n\n\nstatic void\nngx_resolver_free(ngx_resolver_t *r, void *p)\n{\n    /* lock alloc mutex */\n\n    ngx_free(p);\n\n    /* unlock alloc mutex */\n}\n\n\nstatic void\nngx_resolver_free_locked(ngx_resolver_t *r, void *p)\n{\n    ngx_free(p);\n}\n\n\nstatic void *\nngx_resolver_dup(ngx_resolver_t *r, void *src, size_t size)\n{\n    void  *dst;\n\n    dst = ngx_resolver_alloc(r, size);\n\n    if (dst == NULL) {\n        return dst;\n    }\n\n    ngx_memcpy(dst, src, size);\n\n    return dst;\n}\n\n\nstatic ngx_resolver_addr_t *\nngx_resolver_export(ngx_resolver_t *r, ngx_resolver_node_t *rn,\n    ngx_uint_t rotate)\n{\n    ngx_uint_t            d, i, j, n;\n    in_addr_t            *addr;\n    ngx_sockaddr_t       *sockaddr;\n    struct sockaddr_in   *sin;\n    ngx_resolver_addr_t  *dst;\n#if (NGX_HAVE_INET6)\n    struct in6_addr      *addr6;\n    struct sockaddr_in6  *sin6;\n#endif\n\n    n = rn->naddrs;\n#if (NGX_HAVE_INET6)\n    n += rn->naddrs6;\n#endif\n\n    dst = ngx_resolver_calloc(r, n * sizeof(ngx_resolver_addr_t));\n    if (dst == NULL) {\n        return NULL;\n    }\n\n    sockaddr = ngx_resolver_calloc(r, n * sizeof(ngx_sockaddr_t));\n    if (sockaddr == NULL) {\n        ngx_resolver_free(r, dst);\n        return NULL;\n    }\n\n    i = 0;\n    d = rotate ? ngx_random() % n : 0;\n\n    if (rn->naddrs) {\n        j = rotate ? ngx_random() % rn->naddrs : 0;\n\n        addr = (rn->naddrs == 1) ? &rn->u.addr : rn->u.addrs;\n\n        do {\n            sin = &sockaddr[d].sockaddr_in;\n            sin->sin_family = AF_INET;\n            sin->sin_addr.s_addr = addr[j++];\n            dst[d].sockaddr = (struct sockaddr *) sin;\n            dst[d++].socklen = sizeof(struct sockaddr_in);\n\n            if (d == n) {\n                d = 0;\n            }\n\n            if (j == (ngx_uint_t) rn->naddrs) {\n                j = 0;\n            }\n        } while (++i < (ngx_uint_t) rn->naddrs);\n    }\n\n#if (NGX_HAVE_INET6)\n    if (rn->naddrs6) {\n        j = rotate ? ngx_random() % rn->naddrs6 : 0;\n\n        addr6 = (rn->naddrs6 == 1) ? &rn->u6.addr6 : rn->u6.addrs6;\n\n        do {\n            sin6 = &sockaddr[d].sockaddr_in6;\n            sin6->sin6_family = AF_INET6;\n            ngx_memcpy(sin6->sin6_addr.s6_addr, addr6[j++].s6_addr, 16);\n            dst[d].sockaddr = (struct sockaddr *) sin6;\n            dst[d++].socklen = sizeof(struct sockaddr_in6);\n\n            if (d == n) {\n                d = 0;\n            }\n\n            if (j == rn->naddrs6) {\n                j = 0;\n            }\n        } while (++i < n);\n    }\n#endif\n\n    return dst;\n}\n\n\nstatic void\nngx_resolver_report_srv(ngx_resolver_t *r, ngx_resolver_ctx_t *ctx)\n{\n    ngx_uint_t                naddrs, nsrvs, nw, i, j, k, l, m, n, w;\n    ngx_resolver_addr_t      *addrs;\n    ngx_resolver_srv_name_t  *srvs;\n\n    srvs = ctx->srvs;\n    nsrvs = ctx->nsrvs;\n\n    naddrs = 0;\n\n    for (i = 0; i < nsrvs; i++) {\n        if (srvs[i].state == NGX_ERROR) {\n            ctx->state = NGX_ERROR;\n            ctx->valid = ngx_time() + (r->valid ? r->valid : 10);\n\n            ctx->handler(ctx);\n            return;\n        }\n\n        naddrs += srvs[i].naddrs;\n    }\n\n    if (naddrs == 0) {\n        ctx->state = srvs[0].state;\n\n        for (i = 0; i < nsrvs; i++) {\n            if (srvs[i].state == NGX_RESOLVE_NXDOMAIN) {\n                ctx->state = NGX_RESOLVE_NXDOMAIN;\n                break;\n            }\n        }\n\n        ctx->valid = ngx_time() + (r->valid ? r->valid : 10);\n\n        ctx->handler(ctx);\n        return;\n    }\n\n    addrs = ngx_resolver_calloc(r, naddrs * sizeof(ngx_resolver_addr_t));\n    if (addrs == NULL) {\n        ctx->state = NGX_ERROR;\n        ctx->valid = ngx_time() + (r->valid ? r->valid : 10);\n\n        ctx->handler(ctx);\n        return;\n    }\n\n    i = 0;\n    n = 0;\n\n    do {\n        nw = 0;\n\n        for (j = i; j < nsrvs; j++) {\n            if (srvs[j].priority != srvs[i].priority) {\n                break;\n            }\n\n            nw += srvs[j].naddrs * srvs[j].weight;\n        }\n\n        if (nw == 0) {\n            goto next_srv;\n        }\n\n        w = ngx_random() % nw;\n\n        for (k = i; k < j; k++) {\n            if (w < srvs[k].naddrs * srvs[k].weight) {\n                break;\n            }\n\n            w -= srvs[k].naddrs * srvs[k].weight;\n        }\n\n        for (l = i; l < j; l++) {\n\n            for (m = 0; m < srvs[k].naddrs; m++) {\n                addrs[n].socklen = srvs[k].addrs[m].socklen;\n                addrs[n].sockaddr = srvs[k].addrs[m].sockaddr;\n                addrs[n].name = srvs[k].name;\n                addrs[n].priority = srvs[k].priority;\n                addrs[n].weight = srvs[k].weight;\n                n++;\n            }\n\n            if (++k == j) {\n                k = i;\n            }\n        }\n\nnext_srv:\n\n        i = j;\n\n    } while (i < ctx->nsrvs);\n\n    ctx->state = NGX_OK;\n    ctx->addrs = addrs;\n    ctx->naddrs = naddrs;\n\n    ctx->handler(ctx);\n\n    ngx_resolver_free(r, addrs);\n}\n\n\nchar *\nngx_resolver_strerror(ngx_int_t err)\n{\n    static char *errors[] = {\n        \"Format error\",     /* FORMERR */\n        \"Server failure\",   /* SERVFAIL */\n        \"Host not found\",   /* NXDOMAIN */\n        \"Unimplemented\",    /* NOTIMP */\n        \"Operation refused\" /* REFUSED */\n    };\n\n    if (err > 0 && err < 6) {\n        return errors[err - 1];\n    }\n\n    if (err == NGX_RESOLVE_TIMEDOUT) {\n        return \"Operation timed out\";\n    }\n\n    return \"Unknown error\";\n}\n\n\nstatic u_char *\nngx_resolver_log_error(ngx_log_t *log, u_char *buf, size_t len)\n{\n    u_char                     *p;\n    ngx_resolver_connection_t  *rec;\n\n    p = buf;\n\n    if (log->action) {\n        p = ngx_snprintf(buf, len, \" while %s\", log->action);\n        len -= p - buf;\n    }\n\n    rec = log->data;\n\n    if (rec) {\n        p = ngx_snprintf(p, len, \", resolver: %V\", &rec->server);\n    }\n\n    return p;\n}\n\n\nstatic ngx_int_t\nngx_udp_connect(ngx_resolver_connection_t *rec)\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(rec->sockaddr->sa_family, SOCK_DGRAM, 0);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, &rec->log, 0, \"UDP socket %d\", s);\n\n    if (s == (ngx_socket_t) -1) {\n        ngx_log_error(NGX_LOG_ALERT, &rec->log, ngx_socket_errno,\n                      ngx_socket_n \" failed\");\n        return NGX_ERROR;\n    }\n\n    c = ngx_get_connection(s, &rec->log);\n\n    if (c == NULL) {\n        if (ngx_close_socket(s) == -1) {\n            ngx_log_error(NGX_LOG_ALERT, &rec->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, &rec->log, ngx_socket_errno,\n                      ngx_nonblocking_n \" failed\");\n\n        goto failed;\n    }\n\n    rev = c->read;\n    wev = c->write;\n\n    rev->log = &rec->log;\n    wev->log = &rec->log;\n\n    rec->udp = c;\n\n    c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1);\n\n    c->start_time = ngx_current_msec;\n\n    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, &rec->log, 0,\n                   \"connect to %V, fd:%d #%uA\", &rec->server, s, c->number);\n\n    rc = connect(s, rec->sockaddr, rec->socklen);\n\n    /* TODO: iocp */\n\n    if (rc == -1) {\n        ngx_log_error(NGX_LOG_CRIT, &rec->log, ngx_socket_errno,\n                      \"connect() failed\");\n\n        goto failed;\n    }\n\n    /* UDP sockets are always ready to write */\n    wev->ready = 1;\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        goto failed;\n    }\n\n    return NGX_OK;\n\nfailed:\n\n    ngx_close_connection(c);\n    rec->udp = NULL;\n\n    return NGX_ERROR;\n}\n\n\nstatic ngx_int_t\nngx_tcp_connect(ngx_resolver_connection_t *rec)\n{\n    int                rc;\n    ngx_int_t          event;\n    ngx_err_t          err;\n    ngx_uint_t         level;\n    ngx_socket_t       s;\n    ngx_event_t       *rev, *wev;\n    ngx_connection_t  *c;\n\n    s = ngx_socket(rec->sockaddr->sa_family, SOCK_STREAM, 0);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, &rec->log, 0, \"TCP socket %d\", s);\n\n    if (s == (ngx_socket_t) -1) {\n        ngx_log_error(NGX_LOG_ALERT, &rec->log, ngx_socket_errno,\n                      ngx_socket_n \" failed\");\n        return NGX_ERROR;\n    }\n\n    c = ngx_get_connection(s, &rec->log);\n\n    if (c == NULL) {\n        if (ngx_close_socket(s) == -1) {\n            ngx_log_error(NGX_LOG_ALERT, &rec->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, &rec->log, ngx_socket_errno,\n                      ngx_nonblocking_n \" failed\");\n\n        goto failed;\n    }\n\n    rev = c->read;\n    wev = c->write;\n\n    rev->log = &rec->log;\n    wev->log = &rec->log;\n\n    rec->tcp = c;\n\n    c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1);\n\n    c->start_time = ngx_current_msec;\n\n    if (ngx_add_conn) {\n        if (ngx_add_conn(c) == NGX_ERROR) {\n            goto failed;\n        }\n    }\n\n    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, &rec->log, 0,\n                   \"connect to %V, fd:%d #%uA\", &rec->server, s, c->number);\n\n    rc = connect(s, rec->sockaddr, rec->socklen);\n\n    if (rc == -1) {\n        err = ngx_socket_errno;\n\n\n        if (err != NGX_EINPROGRESS\n#if (NGX_WIN32)\n            /* Winsock returns WSAEWOULDBLOCK (NGX_EAGAIN) */\n            && err != NGX_EAGAIN\n#endif\n            )\n        {\n            if (err == NGX_ECONNREFUSED\n#if (NGX_LINUX)\n                /*\n                 * Linux returns EAGAIN instead of ECONNREFUSED\n                 * for unix sockets if listen queue is full\n                 */\n                || err == NGX_EAGAIN\n#endif\n                || err == NGX_ECONNRESET\n                || err == NGX_ENETDOWN\n                || err == NGX_ENETUNREACH\n                || err == NGX_EHOSTDOWN\n                || err == NGX_EHOSTUNREACH)\n            {\n                level = NGX_LOG_ERR;\n\n            } else {\n                level = NGX_LOG_CRIT;\n            }\n\n            ngx_log_error(level, &rec->log, err, \"connect() to %V failed\",\n                          &rec->server);\n\n            ngx_close_connection(c);\n            rec->tcp = NULL;\n\n            return NGX_ERROR;\n        }\n    }\n\n    if (ngx_add_conn) {\n        if (rc == -1) {\n\n            /* NGX_EINPROGRESS */\n\n            return NGX_AGAIN;\n        }\n\n        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, &rec->log, 0, \"connected\");\n\n        wev->ready = 1;\n\n        return NGX_OK;\n    }\n\n    if (ngx_event_flags & NGX_USE_IOCP_EVENT) {\n\n        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, &rec->log, ngx_socket_errno,\n                       \"connect(): %d\", rc);\n\n        if (ngx_blocking(s) == -1) {\n            ngx_log_error(NGX_LOG_ALERT, &rec->log, ngx_socket_errno,\n                          ngx_blocking_n \" failed\");\n            goto failed;\n        }\n\n        /*\n         * FreeBSD's aio allows to post an operation on non-connected socket.\n         * NT does not support it.\n         *\n         * TODO: check in Win32, etc. As workaround we can use NGX_ONESHOT_EVENT\n         */\n\n        rev->ready = 1;\n        wev->ready = 1;\n\n        return NGX_OK;\n    }\n\n    if (ngx_event_flags & NGX_USE_CLEAR_EVENT) {\n\n        /* kqueue */\n\n        event = NGX_CLEAR_EVENT;\n\n    } else {\n\n        /* select, poll, /dev/poll */\n\n        event = NGX_LEVEL_EVENT;\n    }\n\n    if (ngx_add_event(rev, NGX_READ_EVENT, event) != NGX_OK) {\n        goto failed;\n    }\n\n    if (rc == -1) {\n\n        /* NGX_EINPROGRESS */\n\n        if (ngx_add_event(wev, NGX_WRITE_EVENT, event) != NGX_OK) {\n            goto failed;\n        }\n\n        return NGX_AGAIN;\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, &rec->log, 0, \"connected\");\n\n    wev->ready = 1;\n\n    return NGX_OK;\n\nfailed:\n\n    ngx_close_connection(c);\n    rec->tcp = NULL;\n\n    return NGX_ERROR;\n}\n\n\nstatic ngx_int_t\nngx_resolver_cmp_srvs(const void *one, const void *two)\n{\n    ngx_int_t            p1, p2;\n    ngx_resolver_srv_t  *first, *second;\n\n    first = (ngx_resolver_srv_t *) one;\n    second = (ngx_resolver_srv_t *) two;\n\n    p1 = first->priority;\n    p2 = second->priority;\n\n    return p1 - p2;\n}\n"
  },
  {
    "path": "src/core/ngx_resolver.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\n#ifndef _NGX_RESOLVER_H_INCLUDED_\n#define _NGX_RESOLVER_H_INCLUDED_\n\n\n#define NGX_RESOLVE_A         1\n#define NGX_RESOLVE_CNAME     5\n#define NGX_RESOLVE_PTR       12\n#define NGX_RESOLVE_MX        15\n#define NGX_RESOLVE_TXT       16\n#if (NGX_HAVE_INET6)\n#define NGX_RESOLVE_AAAA      28\n#endif\n#define NGX_RESOLVE_SRV       33\n#define NGX_RESOLVE_DNAME     39\n\n#define NGX_RESOLVE_FORMERR   1\n#define NGX_RESOLVE_SERVFAIL  2\n#define NGX_RESOLVE_NXDOMAIN  3\n#define NGX_RESOLVE_NOTIMP    4\n#define NGX_RESOLVE_REFUSED   5\n#define NGX_RESOLVE_TIMEDOUT  NGX_ETIMEDOUT\n\n\n#define NGX_NO_RESOLVER       (void *) -1\n\n#define NGX_RESOLVER_MAX_RECURSION    50\n\n\ntypedef struct ngx_resolver_s  ngx_resolver_t;\n\n\ntypedef struct {\n    ngx_connection_t         *udp;\n    ngx_connection_t         *tcp;\n    struct sockaddr          *sockaddr;\n    socklen_t                 socklen;\n    ngx_str_t                 server;\n    ngx_log_t                 log;\n    ngx_buf_t                *read_buf;\n    ngx_buf_t                *write_buf;\n    ngx_resolver_t           *resolver;\n} ngx_resolver_connection_t;\n\n\ntypedef struct ngx_resolver_ctx_s  ngx_resolver_ctx_t;\n\ntypedef void (*ngx_resolver_handler_pt)(ngx_resolver_ctx_t *ctx);\n\n\ntypedef struct {\n    struct sockaddr          *sockaddr;\n    socklen_t                 socklen;\n    ngx_str_t                 name;\n    u_short                   priority;\n    u_short                   weight;\n} ngx_resolver_addr_t;\n\n\ntypedef struct {\n    ngx_str_t                 name;\n    u_short                   priority;\n    u_short                   weight;\n    u_short                   port;\n} ngx_resolver_srv_t;\n\n\ntypedef struct {\n    ngx_str_t                 name;\n    u_short                   priority;\n    u_short                   weight;\n    u_short                   port;\n\n    ngx_resolver_ctx_t       *ctx;\n    ngx_int_t                 state;\n\n    ngx_uint_t                naddrs;\n    ngx_addr_t               *addrs;\n} ngx_resolver_srv_name_t;\n\n\ntypedef struct {\n    ngx_rbtree_node_t         node;\n    ngx_queue_t               queue;\n\n    /* PTR: resolved name, A: name to resolve */\n    u_char                   *name;\n\n#if (NGX_HAVE_INET6)\n    /* PTR: IPv6 address to resolve (IPv4 address is in rbtree node key) */\n    struct in6_addr           addr6;\n#endif\n\n    u_short                   nlen;\n    u_short                   qlen;\n\n    u_char                   *query;\n#if (NGX_HAVE_INET6)\n    u_char                   *query6;\n#endif\n\n    union {\n        in_addr_t             addr;\n        in_addr_t            *addrs;\n        u_char               *cname;\n        ngx_resolver_srv_t   *srvs;\n    } u;\n\n    u_char                    code;\n    u_short                   naddrs;\n    u_short                   nsrvs;\n    u_short                   cnlen;\n\n#if (NGX_HAVE_INET6)\n    union {\n        struct in6_addr       addr6;\n        struct in6_addr      *addrs6;\n    } u6;\n\n    u_short                   naddrs6;\n#endif\n\n    time_t                    expire;\n    time_t                    valid;\n    uint32_t                  ttl;\n\n    unsigned                  tcp:1;\n#if (NGX_HAVE_INET6)\n    unsigned                  tcp6:1;\n#endif\n\n    ngx_uint_t                last_connection;\n\n    ngx_resolver_ctx_t       *waiting;\n} ngx_resolver_node_t;\n\n\nstruct ngx_resolver_s {\n    /* has to be pointer because of \"incomplete type\" */\n    ngx_event_t              *event;\n    void                     *dummy;\n    ngx_log_t                *log;\n\n    /* event ident must be after 3 pointers as in ngx_connection_t */\n    ngx_int_t                 ident;\n\n    /* simple round robin DNS peers balancer */\n    ngx_array_t               connections;\n    ngx_uint_t                last_connection;\n\n    ngx_rbtree_t              name_rbtree;\n    ngx_rbtree_node_t         name_sentinel;\n\n    ngx_rbtree_t              srv_rbtree;\n    ngx_rbtree_node_t         srv_sentinel;\n\n    ngx_rbtree_t              addr_rbtree;\n    ngx_rbtree_node_t         addr_sentinel;\n\n    ngx_queue_t               name_resend_queue;\n    ngx_queue_t               srv_resend_queue;\n    ngx_queue_t               addr_resend_queue;\n\n    ngx_queue_t               name_expire_queue;\n    ngx_queue_t               srv_expire_queue;\n    ngx_queue_t               addr_expire_queue;\n\n    unsigned                  ipv4:1;\n\n#if (NGX_HAVE_INET6)\n    unsigned                  ipv6:1;\n    ngx_rbtree_t              addr6_rbtree;\n    ngx_rbtree_node_t         addr6_sentinel;\n    ngx_queue_t               addr6_resend_queue;\n    ngx_queue_t               addr6_expire_queue;\n#endif\n\n    time_t                    resend_timeout;\n    time_t                    tcp_timeout;\n    time_t                    expire;\n    time_t                    valid;\n\n    ngx_uint_t                log_level;\n};\n\n\nstruct ngx_resolver_ctx_s {\n    ngx_resolver_ctx_t       *next;\n    ngx_resolver_t           *resolver;\n    ngx_resolver_node_t      *node;\n\n    /* event ident must be after 3 pointers as in ngx_connection_t */\n    ngx_int_t                 ident;\n\n    ngx_int_t                 state;\n    ngx_str_t                 name;\n    ngx_str_t                 service;\n\n    time_t                    valid;\n    ngx_uint_t                naddrs;\n    ngx_resolver_addr_t      *addrs;\n    ngx_resolver_addr_t       addr;\n    struct sockaddr_in        sin;\n\n    ngx_uint_t                count;\n    ngx_uint_t                nsrvs;\n    ngx_resolver_srv_name_t  *srvs;\n\n    ngx_resolver_handler_pt   handler;\n    void                     *data;\n    ngx_msec_t                timeout;\n\n    unsigned                  quick:1;\n    unsigned                  async:1;\n    unsigned                  cancelable:1;\n    ngx_uint_t                recursion;\n    ngx_event_t              *event;\n};\n\n\n#if (T_NGX_RESOLVER_FILE)\nngx_int_t ngx_resolver_read_resolv_file(ngx_conf_t *cf, ngx_str_t *filename,\n    ngx_str_t **names, ngx_uint_t *n);\n#endif\nngx_resolver_t *ngx_resolver_create(ngx_conf_t *cf, ngx_str_t *names,\n    ngx_uint_t n);\nngx_resolver_ctx_t *ngx_resolve_start(ngx_resolver_t *r,\n    ngx_resolver_ctx_t *temp);\nngx_int_t ngx_resolve_name(ngx_resolver_ctx_t *ctx);\nvoid ngx_resolve_name_done(ngx_resolver_ctx_t *ctx);\nngx_int_t ngx_resolve_addr(ngx_resolver_ctx_t *ctx);\nvoid ngx_resolve_addr_done(ngx_resolver_ctx_t *ctx);\nchar *ngx_resolver_strerror(ngx_int_t err);\n\n\n#endif /* _NGX_RESOLVER_H_INCLUDED_ */\n"
  },
  {
    "path": "src/core/ngx_rwlock.c",
    "content": "\n/*\n * Copyright (C) Ruslan Ermilov\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\n#if (NGX_HAVE_ATOMIC_OPS)\n\n\n#define NGX_RWLOCK_SPIN   2048\n#define NGX_RWLOCK_WLOCK  ((ngx_atomic_uint_t) -1)\n\n\nvoid\nngx_rwlock_wlock(ngx_atomic_t *lock)\n{\n    ngx_uint_t  i, n;\n\n    for ( ;; ) {\n\n        if (*lock == 0 && ngx_atomic_cmp_set(lock, 0, NGX_RWLOCK_WLOCK)) {\n            return;\n        }\n\n        if (ngx_ncpu > 1) {\n\n            for (n = 1; n < NGX_RWLOCK_SPIN; n <<= 1) {\n\n                for (i = 0; i < n; i++) {\n                    ngx_cpu_pause();\n                }\n\n                if (*lock == 0\n                    && ngx_atomic_cmp_set(lock, 0, NGX_RWLOCK_WLOCK))\n                {\n                    return;\n                }\n            }\n        }\n\n        ngx_sched_yield();\n    }\n}\n\n\nvoid\nngx_rwlock_rlock(ngx_atomic_t *lock)\n{\n    ngx_uint_t         i, n;\n    ngx_atomic_uint_t  readers;\n\n    for ( ;; ) {\n        readers = *lock;\n\n        if (readers != NGX_RWLOCK_WLOCK\n            && ngx_atomic_cmp_set(lock, readers, readers + 1))\n        {\n            return;\n        }\n\n        if (ngx_ncpu > 1) {\n\n            for (n = 1; n < NGX_RWLOCK_SPIN; n <<= 1) {\n\n                for (i = 0; i < n; i++) {\n                    ngx_cpu_pause();\n                }\n\n                readers = *lock;\n\n                if (readers != NGX_RWLOCK_WLOCK\n                    && ngx_atomic_cmp_set(lock, readers, readers + 1))\n                {\n                    return;\n                }\n            }\n        }\n\n        ngx_sched_yield();\n    }\n}\n\n\nvoid\nngx_rwlock_unlock(ngx_atomic_t *lock)\n{\n    if (*lock == NGX_RWLOCK_WLOCK) {\n        (void) ngx_atomic_cmp_set(lock, NGX_RWLOCK_WLOCK, 0);\n    } else {\n        (void) ngx_atomic_fetch_add(lock, -1);\n    }\n}\n\n\nvoid\nngx_rwlock_downgrade(ngx_atomic_t *lock)\n{\n    if (*lock == NGX_RWLOCK_WLOCK) {\n        *lock = 1;\n    }\n}\n\n\n#else\n\n#if (NGX_HTTP_UPSTREAM_ZONE || NGX_STREAM_UPSTREAM_ZONE)\n\n#error ngx_atomic_cmp_set() is not defined!\n\n#endif\n\n#endif\n"
  },
  {
    "path": "src/core/ngx_rwlock.h",
    "content": "\n/*\n * Copyright (C) Ruslan Ermilov\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_RWLOCK_H_INCLUDED_\n#define _NGX_RWLOCK_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\nvoid ngx_rwlock_wlock(ngx_atomic_t *lock);\nvoid ngx_rwlock_rlock(ngx_atomic_t *lock);\nvoid ngx_rwlock_unlock(ngx_atomic_t *lock);\nvoid ngx_rwlock_downgrade(ngx_atomic_t *lock);\n\n\n#endif /* _NGX_RWLOCK_H_INCLUDED_ */\n"
  },
  {
    "path": "src/core/ngx_segment_tree.c",
    "content": "\n/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#include <ngx_core.h>\n#include <ngx_config.h>\n\n\n#define ngx_segment_node_copy(s,t)  \\\n    (s)->key = (t)->key;            \\\n    (s)->data = (t)->data\n\n\nstatic ngx_int_t\nngx_segment_tree_min(ngx_segment_node_t *one, ngx_segment_node_t *two)\n{\n    return two->key - one->key;\n}\n\n\nngx_int_t\nngx_segment_tree_init(ngx_segment_tree_t *tree, ngx_uint_t num,\n    ngx_pool_t *pool)\n{\n    tree->segments = ngx_pcalloc(pool,\n                                ((num + 1) << 2) * sizeof(ngx_segment_node_t));\n    if (tree->segments == NULL) {\n        return NGX_ERROR;\n    }\n\n    tree->extreme = NGX_MAX_UINT32_VALUE;\n    tree->pool = pool;\n    tree->num = num;\n\n    tree->cmp = ngx_segment_tree_min;\n    tree->build = ngx_segment_tree_build;\n    tree->insert = ngx_segment_tree_insert;\n    tree->query = ngx_segment_tree_query;\n    tree->del = ngx_segment_tree_delete;\n\n    tree->segments[0].key = tree->extreme;\n    return NGX_OK;\n}\n\n\nvoid\nngx_segment_tree_build(ngx_segment_tree_t *tree, ngx_int_t index, ngx_int_t l,\n    ngx_int_t r)\n{\n    ngx_int_t   child, mid;\n    if (l == r) {\n        tree->segments[index].key = l;\n        return;\n    }\n\n    child = index << 1;\n    mid = (l + r) >> 1;\n\n    ngx_segment_tree_build(tree, child, l, mid);\n    ngx_segment_tree_build(tree, child + 1, mid + 1, r);\n\n    if (tree->cmp(&tree->segments[child], &tree->segments[child + 1]) > 0) {\n        ngx_segment_node_copy(&tree->segments[index], &tree->segments[child]);\n\n    } else {\n        ngx_segment_node_copy(&tree->segments[index],\n                              &tree->segments[child + 1]);\n    }\n}\n\n\nvoid\nngx_segment_tree_insert(ngx_segment_tree_t *tree, ngx_int_t index, ngx_int_t l,\n    ngx_int_t r, ngx_int_t pos, ngx_segment_node_t *node)\n{\n    ngx_int_t   child, mid;\n    if (l == r && l == pos) {\n        ngx_segment_node_copy(&tree->segments[index], node);\n        return;\n    }\n\n    child = index << 1;\n    mid = (l + r) >> 1;\n\n    if (pos <= mid) {\n        ngx_segment_tree_insert(tree, child, l, mid, pos, node);\n\n    } else {\n        ngx_segment_tree_insert(tree, child + 1, mid + 1, r, pos, node);\n    }\n\n    if (tree->cmp(&tree->segments[child], &tree->segments[child + 1]) > 0) {\n        ngx_segment_node_copy(&tree->segments[index], &tree->segments[child]);\n\n    } else {\n        ngx_segment_node_copy(&tree->segments[index],\n                              &tree->segments[child + 1]);\n    }\n}\n\n\nngx_segment_node_t *\nngx_segment_tree_query(ngx_segment_tree_t *tree, ngx_int_t index, ngx_int_t l,\n    ngx_int_t r, ngx_int_t ll, ngx_int_t rr)\n{\n    ngx_int_t  child, mid;\n    ngx_segment_node_t *l_node, *r_node;\n\n    if (ll > rr) {\n        return &tree->segments[0];\n    }\n\n    if (l == ll && r == rr) {\n        return &tree->segments[index];\n    }\n\n    child = index << 1;\n    mid = (l + r) >> 1;\n\n    if (rr <= mid) {\n        return ngx_segment_tree_query(tree, child, l, mid, ll, rr);\n\n    } else if (ll > mid) {\n        return ngx_segment_tree_query(tree, child + 1, mid + 1, r, ll, rr);\n    }\n\n    l_node = ngx_segment_tree_query(tree, child, l, mid, ll, mid);\n    r_node = ngx_segment_tree_query(tree, child + 1, mid + 1, r, mid + 1, rr);\n\n    if (tree->cmp(l_node, r_node) > 0) {\n        return l_node;\n    }\n\n    return r_node;\n}\n\n\nvoid\nngx_segment_tree_delete(ngx_segment_tree_t *tree, ngx_int_t index,\n    ngx_int_t l, ngx_int_t r, ngx_int_t pos)\n{\n    ngx_int_t  child, mid;\n\n    if (l == r && l == pos) {\n        tree->segments[index].key = tree->extreme;\n        return;\n    }\n\n    child = index << 1;\n    mid = (l + r) >> 1;\n\n    if (pos <= mid) {\n        ngx_segment_tree_delete(tree, child, l, mid, pos);\n\n    } else {\n        ngx_segment_tree_delete(tree, child + 1, mid + 1, r, pos);\n    }\n\n    if (tree->cmp(&tree->segments[child], &tree->segments[child + 1]) > 0) {\n        ngx_segment_node_copy(&tree->segments[index], &tree->segments[child]);\n\n    } else {\n        ngx_segment_node_copy(&tree->segments[index],\n                              &tree->segments[child + 1]);\n    }\n}\n"
  },
  {
    "path": "src/core/ngx_segment_tree.h",
    "content": "\n/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#ifndef _NGX_SEGMENT_TREE_H_INCLUDE_\n#define _NGX_SEGMENT_TREE_H_INCLUDE_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\ntypedef struct ngx_segment_node_s ngx_segment_node_t;\ntypedef struct ngx_segment_tree_s ngx_segment_tree_t;\n\n\ntypedef ngx_int_t (*ngx_segment_cmp_pt)(ngx_segment_node_t *one,\n    ngx_segment_node_t *two);\ntypedef void (*ngx_segment_build_pt)(ngx_segment_tree_t *tree, ngx_int_t index,\n    ngx_int_t l, ngx_int_t r);\ntypedef void (*ngx_segment_insert_pt)(ngx_segment_tree_t *tree, ngx_int_t index,\n    ngx_int_t l, ngx_int_t r, ngx_int_t pos, ngx_segment_node_t *node);\ntypedef ngx_segment_node_t *(*ngx_segment_query_pt)(ngx_segment_tree_t *tree,\n    ngx_int_t index, ngx_int_t l, ngx_int_t r, ngx_int_t ll, ngx_int_t rr);\ntypedef void (*ngx_segment_delete_pt)(ngx_segment_tree_t *tree, ngx_int_t index,\n    ngx_int_t l, ngx_int_t r, ngx_int_t pos);\n\nstruct ngx_segment_node_s {\n    ngx_int_t             key;\n    void                 *data;\n};\n\nstruct ngx_segment_tree_s {\n    uint32_t              extreme;\n    ngx_pool_t           *pool;\n    ngx_uint_t            num;\n    ngx_segment_node_t   *segments;\n\n    ngx_segment_cmp_pt    cmp;\n    ngx_segment_build_pt  build;\n    ngx_segment_insert_pt insert;\n    ngx_segment_query_pt  query;\n    ngx_segment_delete_pt del;\n};\n\n\nvoid ngx_segment_tree_build(ngx_segment_tree_t *tree, ngx_int_t index,\n    ngx_int_t l, ngx_int_t r);\nvoid ngx_segment_tree_insert(ngx_segment_tree_t *tree, ngx_int_t index,\n    ngx_int_t l, ngx_int_t r, ngx_int_t pos, ngx_segment_node_t *node);\nngx_segment_node_t *ngx_segment_tree_query(ngx_segment_tree_t *tree,\n    ngx_int_t index, ngx_int_t l, ngx_int_t r, ngx_int_t ll, ngx_int_t rr);\nvoid ngx_segment_tree_delete(ngx_segment_tree_t *tree, ngx_int_t index,\n    ngx_int_t l, ngx_int_t r, ngx_int_t pos);\nngx_int_t ngx_segment_tree_init(ngx_segment_tree_t *tree, ngx_uint_t num,\n    ngx_pool_t *pool);\n\n\n#endif\n"
  },
  {
    "path": "src/core/ngx_sha1.c",
    "content": "\n/*\n * Copyright (C) Maxim Dounin\n * Copyright (C) Nginx, Inc.\n *\n * An internal SHA1 implementation.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_sha1.h>\n\n\nstatic const u_char *ngx_sha1_body(ngx_sha1_t *ctx, const u_char *data,\n    size_t size);\n\n\nvoid\nngx_sha1_init(ngx_sha1_t *ctx)\n{\n    ctx->a = 0x67452301;\n    ctx->b = 0xefcdab89;\n    ctx->c = 0x98badcfe;\n    ctx->d = 0x10325476;\n    ctx->e = 0xc3d2e1f0;\n\n    ctx->bytes = 0;\n}\n\n\nvoid\nngx_sha1_update(ngx_sha1_t *ctx, const void *data, size_t size)\n{\n    size_t  used, free;\n\n    used = (size_t) (ctx->bytes & 0x3f);\n    ctx->bytes += size;\n\n    if (used) {\n        free = 64 - used;\n\n        if (size < free) {\n            ngx_memcpy(&ctx->buffer[used], data, size);\n            return;\n        }\n\n        ngx_memcpy(&ctx->buffer[used], data, free);\n        data = (u_char *) data + free;\n        size -= free;\n        (void) ngx_sha1_body(ctx, ctx->buffer, 64);\n    }\n\n    if (size >= 64) {\n        data = ngx_sha1_body(ctx, data, size & ~(size_t) 0x3f);\n        size &= 0x3f;\n    }\n\n    ngx_memcpy(ctx->buffer, data, size);\n}\n\n\nvoid\nngx_sha1_final(u_char result[20], ngx_sha1_t *ctx)\n{\n    size_t  used, free;\n\n    used = (size_t) (ctx->bytes & 0x3f);\n\n    ctx->buffer[used++] = 0x80;\n\n    free = 64 - used;\n\n    if (free < 8) {\n        ngx_memzero(&ctx->buffer[used], free);\n        (void) ngx_sha1_body(ctx, ctx->buffer, 64);\n        used = 0;\n        free = 64;\n    }\n\n    ngx_memzero(&ctx->buffer[used], free - 8);\n\n    ctx->bytes <<= 3;\n    ctx->buffer[56] = (u_char) (ctx->bytes >> 56);\n    ctx->buffer[57] = (u_char) (ctx->bytes >> 48);\n    ctx->buffer[58] = (u_char) (ctx->bytes >> 40);\n    ctx->buffer[59] = (u_char) (ctx->bytes >> 32);\n    ctx->buffer[60] = (u_char) (ctx->bytes >> 24);\n    ctx->buffer[61] = (u_char) (ctx->bytes >> 16);\n    ctx->buffer[62] = (u_char) (ctx->bytes >> 8);\n    ctx->buffer[63] = (u_char) ctx->bytes;\n\n    (void) ngx_sha1_body(ctx, ctx->buffer, 64);\n\n    result[0] = (u_char) (ctx->a >> 24);\n    result[1] = (u_char) (ctx->a >> 16);\n    result[2] = (u_char) (ctx->a >> 8);\n    result[3] = (u_char) ctx->a;\n    result[4] = (u_char) (ctx->b >> 24);\n    result[5] = (u_char) (ctx->b >> 16);\n    result[6] = (u_char) (ctx->b >> 8);\n    result[7] = (u_char) ctx->b;\n    result[8] = (u_char) (ctx->c >> 24);\n    result[9] = (u_char) (ctx->c >> 16);\n    result[10] = (u_char) (ctx->c >> 8);\n    result[11] = (u_char) ctx->c;\n    result[12] = (u_char) (ctx->d >> 24);\n    result[13] = (u_char) (ctx->d >> 16);\n    result[14] = (u_char) (ctx->d >> 8);\n    result[15] = (u_char) ctx->d;\n    result[16] = (u_char) (ctx->e >> 24);\n    result[17] = (u_char) (ctx->e >> 16);\n    result[18] = (u_char) (ctx->e >> 8);\n    result[19] = (u_char) ctx->e;\n\n    ngx_memzero(ctx, sizeof(*ctx));\n}\n\n\n/*\n * Helper functions.\n */\n\n#define ROTATE(bits, word)  (((word) << (bits)) | ((word) >> (32 - (bits))))\n\n#define F1(b, c, d)  (((b) & (c)) | ((~(b)) & (d)))\n#define F2(b, c, d)  ((b) ^ (c) ^ (d))\n#define F3(b, c, d)  (((b) & (c)) | ((b) & (d)) | ((c) & (d)))\n\n#define STEP(f, a, b, c, d, e, w, t)                                          \\\n    temp = ROTATE(5, (a)) + f((b), (c), (d)) + (e) + (w) + (t);               \\\n    (e) = (d);                                                                \\\n    (d) = (c);                                                                \\\n    (c) = ROTATE(30, (b));                                                    \\\n    (b) = (a);                                                                \\\n    (a) = temp;\n\n\n/*\n * GET() reads 4 input bytes in big-endian byte order and returns\n * them as uint32_t.\n */\n\n#define GET(n)                                                                \\\n    ((uint32_t) p[n * 4 + 3] |                                                \\\n    ((uint32_t) p[n * 4 + 2] << 8) |                                          \\\n    ((uint32_t) p[n * 4 + 1] << 16) |                                         \\\n    ((uint32_t) p[n * 4] << 24))\n\n\n/*\n * This processes one or more 64-byte data blocks, but does not update\n * the bit counters.  There are no alignment requirements.\n */\n\nstatic const u_char *\nngx_sha1_body(ngx_sha1_t *ctx, const u_char *data, size_t size)\n{\n    uint32_t       a, b, c, d, e, temp;\n    uint32_t       saved_a, saved_b, saved_c, saved_d, saved_e;\n    uint32_t       words[80];\n    ngx_uint_t     i;\n    const u_char  *p;\n\n    p = data;\n\n    a = ctx->a;\n    b = ctx->b;\n    c = ctx->c;\n    d = ctx->d;\n    e = ctx->e;\n\n    do {\n        saved_a = a;\n        saved_b = b;\n        saved_c = c;\n        saved_d = d;\n        saved_e = e;\n\n        /* Load data block into the words array */\n\n        for (i = 0; i < 16; i++) {\n            words[i] = GET(i);\n        }\n\n        for (i = 16; i < 80; i++) {\n            words[i] = ROTATE(1, words[i - 3] ^ words[i - 8] ^ words[i - 14]\n                                 ^ words[i - 16]);\n        }\n\n        /* Transformations */\n\n        STEP(F1, a, b, c, d, e, words[0],  0x5a827999);\n        STEP(F1, a, b, c, d, e, words[1],  0x5a827999);\n        STEP(F1, a, b, c, d, e, words[2],  0x5a827999);\n        STEP(F1, a, b, c, d, e, words[3],  0x5a827999);\n        STEP(F1, a, b, c, d, e, words[4],  0x5a827999);\n        STEP(F1, a, b, c, d, e, words[5],  0x5a827999);\n        STEP(F1, a, b, c, d, e, words[6],  0x5a827999);\n        STEP(F1, a, b, c, d, e, words[7],  0x5a827999);\n        STEP(F1, a, b, c, d, e, words[8],  0x5a827999);\n        STEP(F1, a, b, c, d, e, words[9],  0x5a827999);\n        STEP(F1, a, b, c, d, e, words[10], 0x5a827999);\n        STEP(F1, a, b, c, d, e, words[11], 0x5a827999);\n        STEP(F1, a, b, c, d, e, words[12], 0x5a827999);\n        STEP(F1, a, b, c, d, e, words[13], 0x5a827999);\n        STEP(F1, a, b, c, d, e, words[14], 0x5a827999);\n        STEP(F1, a, b, c, d, e, words[15], 0x5a827999);\n        STEP(F1, a, b, c, d, e, words[16], 0x5a827999);\n        STEP(F1, a, b, c, d, e, words[17], 0x5a827999);\n        STEP(F1, a, b, c, d, e, words[18], 0x5a827999);\n        STEP(F1, a, b, c, d, e, words[19], 0x5a827999);\n\n        STEP(F2, a, b, c, d, e, words[20], 0x6ed9eba1);\n        STEP(F2, a, b, c, d, e, words[21], 0x6ed9eba1);\n        STEP(F2, a, b, c, d, e, words[22], 0x6ed9eba1);\n        STEP(F2, a, b, c, d, e, words[23], 0x6ed9eba1);\n        STEP(F2, a, b, c, d, e, words[24], 0x6ed9eba1);\n        STEP(F2, a, b, c, d, e, words[25], 0x6ed9eba1);\n        STEP(F2, a, b, c, d, e, words[26], 0x6ed9eba1);\n        STEP(F2, a, b, c, d, e, words[27], 0x6ed9eba1);\n        STEP(F2, a, b, c, d, e, words[28], 0x6ed9eba1);\n        STEP(F2, a, b, c, d, e, words[29], 0x6ed9eba1);\n        STEP(F2, a, b, c, d, e, words[30], 0x6ed9eba1);\n        STEP(F2, a, b, c, d, e, words[31], 0x6ed9eba1);\n        STEP(F2, a, b, c, d, e, words[32], 0x6ed9eba1);\n        STEP(F2, a, b, c, d, e, words[33], 0x6ed9eba1);\n        STEP(F2, a, b, c, d, e, words[34], 0x6ed9eba1);\n        STEP(F2, a, b, c, d, e, words[35], 0x6ed9eba1);\n        STEP(F2, a, b, c, d, e, words[36], 0x6ed9eba1);\n        STEP(F2, a, b, c, d, e, words[37], 0x6ed9eba1);\n        STEP(F2, a, b, c, d, e, words[38], 0x6ed9eba1);\n        STEP(F2, a, b, c, d, e, words[39], 0x6ed9eba1);\n\n        STEP(F3, a, b, c, d, e, words[40], 0x8f1bbcdc);\n        STEP(F3, a, b, c, d, e, words[41], 0x8f1bbcdc);\n        STEP(F3, a, b, c, d, e, words[42], 0x8f1bbcdc);\n        STEP(F3, a, b, c, d, e, words[43], 0x8f1bbcdc);\n        STEP(F3, a, b, c, d, e, words[44], 0x8f1bbcdc);\n        STEP(F3, a, b, c, d, e, words[45], 0x8f1bbcdc);\n        STEP(F3, a, b, c, d, e, words[46], 0x8f1bbcdc);\n        STEP(F3, a, b, c, d, e, words[47], 0x8f1bbcdc);\n        STEP(F3, a, b, c, d, e, words[48], 0x8f1bbcdc);\n        STEP(F3, a, b, c, d, e, words[49], 0x8f1bbcdc);\n        STEP(F3, a, b, c, d, e, words[50], 0x8f1bbcdc);\n        STEP(F3, a, b, c, d, e, words[51], 0x8f1bbcdc);\n        STEP(F3, a, b, c, d, e, words[52], 0x8f1bbcdc);\n        STEP(F3, a, b, c, d, e, words[53], 0x8f1bbcdc);\n        STEP(F3, a, b, c, d, e, words[54], 0x8f1bbcdc);\n        STEP(F3, a, b, c, d, e, words[55], 0x8f1bbcdc);\n        STEP(F3, a, b, c, d, e, words[56], 0x8f1bbcdc);\n        STEP(F3, a, b, c, d, e, words[57], 0x8f1bbcdc);\n        STEP(F3, a, b, c, d, e, words[58], 0x8f1bbcdc);\n        STEP(F3, a, b, c, d, e, words[59], 0x8f1bbcdc);\n\n        STEP(F2, a, b, c, d, e, words[60], 0xca62c1d6);\n        STEP(F2, a, b, c, d, e, words[61], 0xca62c1d6);\n        STEP(F2, a, b, c, d, e, words[62], 0xca62c1d6);\n        STEP(F2, a, b, c, d, e, words[63], 0xca62c1d6);\n        STEP(F2, a, b, c, d, e, words[64], 0xca62c1d6);\n        STEP(F2, a, b, c, d, e, words[65], 0xca62c1d6);\n        STEP(F2, a, b, c, d, e, words[66], 0xca62c1d6);\n        STEP(F2, a, b, c, d, e, words[67], 0xca62c1d6);\n        STEP(F2, a, b, c, d, e, words[68], 0xca62c1d6);\n        STEP(F2, a, b, c, d, e, words[69], 0xca62c1d6);\n        STEP(F2, a, b, c, d, e, words[70], 0xca62c1d6);\n        STEP(F2, a, b, c, d, e, words[71], 0xca62c1d6);\n        STEP(F2, a, b, c, d, e, words[72], 0xca62c1d6);\n        STEP(F2, a, b, c, d, e, words[73], 0xca62c1d6);\n        STEP(F2, a, b, c, d, e, words[74], 0xca62c1d6);\n        STEP(F2, a, b, c, d, e, words[75], 0xca62c1d6);\n        STEP(F2, a, b, c, d, e, words[76], 0xca62c1d6);\n        STEP(F2, a, b, c, d, e, words[77], 0xca62c1d6);\n        STEP(F2, a, b, c, d, e, words[78], 0xca62c1d6);\n        STEP(F2, a, b, c, d, e, words[79], 0xca62c1d6);\n\n        a += saved_a;\n        b += saved_b;\n        c += saved_c;\n        d += saved_d;\n        e += saved_e;\n\n        p += 64;\n\n    } while (size -= 64);\n\n    ctx->a = a;\n    ctx->b = b;\n    ctx->c = c;\n    ctx->d = d;\n    ctx->e = e;\n\n    return p;\n}\n"
  },
  {
    "path": "src/core/ngx_sha1.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_SHA1_H_INCLUDED_\n#define _NGX_SHA1_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\ntypedef struct {\n    uint64_t  bytes;\n    uint32_t  a, b, c, d, e, f;\n    u_char    buffer[64];\n} ngx_sha1_t;\n\n\nvoid ngx_sha1_init(ngx_sha1_t *ctx);\nvoid ngx_sha1_update(ngx_sha1_t *ctx, const void *data, size_t size);\nvoid ngx_sha1_final(u_char result[20], ngx_sha1_t *ctx);\n\n\n#endif /* _NGX_SHA1_H_INCLUDED_ */\n"
  },
  {
    "path": "src/core/ngx_shmtx.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\n#if (NGX_HAVE_ATOMIC_OPS)\n\n\nstatic void ngx_shmtx_wakeup(ngx_shmtx_t *mtx);\n\n\nngx_int_t\nngx_shmtx_create(ngx_shmtx_t *mtx, ngx_shmtx_sh_t *addr, u_char *name)\n{\n    mtx->lock = &addr->lock;\n\n    if (mtx->spin == (ngx_uint_t) -1) {\n        return NGX_OK;\n    }\n\n    mtx->spin = 2048;\n\n#if (NGX_HAVE_POSIX_SEM)\n\n    mtx->wait = &addr->wait;\n\n    if (sem_init(&mtx->sem, 1, 0) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_errno,\n                      \"sem_init() failed\");\n    } else {\n        mtx->semaphore = 1;\n    }\n\n#endif\n\n    return NGX_OK;\n}\n\n\nvoid\nngx_shmtx_destroy(ngx_shmtx_t *mtx)\n{\n#if (NGX_HAVE_POSIX_SEM)\n\n    if (mtx->semaphore) {\n        if (sem_destroy(&mtx->sem) == -1) {\n            ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_errno,\n                          \"sem_destroy() failed\");\n        }\n    }\n\n#endif\n}\n\n\nngx_uint_t\nngx_shmtx_trylock(ngx_shmtx_t *mtx)\n{\n    return (*mtx->lock == 0 && ngx_atomic_cmp_set(mtx->lock, 0, ngx_pid));\n}\n\n\nvoid\nngx_shmtx_lock(ngx_shmtx_t *mtx)\n{\n    ngx_uint_t         i, n;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, \"shmtx lock\");\n\n    for ( ;; ) {\n\n        if (*mtx->lock == 0 && ngx_atomic_cmp_set(mtx->lock, 0, ngx_pid)) {\n            return;\n        }\n\n        if (ngx_ncpu > 1) {\n\n            for (n = 1; n < mtx->spin; n <<= 1) {\n\n                for (i = 0; i < n; i++) {\n                    ngx_cpu_pause();\n                }\n\n                if (*mtx->lock == 0\n                    && ngx_atomic_cmp_set(mtx->lock, 0, ngx_pid))\n                {\n                    return;\n                }\n            }\n        }\n\n#if (NGX_HAVE_POSIX_SEM)\n\n        if (mtx->semaphore) {\n            (void) ngx_atomic_fetch_add(mtx->wait, 1);\n\n            if (*mtx->lock == 0 && ngx_atomic_cmp_set(mtx->lock, 0, ngx_pid)) {\n                (void) ngx_atomic_fetch_add(mtx->wait, -1);\n                return;\n            }\n\n            ngx_log_debug1(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0,\n                           \"shmtx wait %uA\", *mtx->wait);\n\n            while (sem_wait(&mtx->sem) == -1) {\n                ngx_err_t  err;\n\n                err = ngx_errno;\n\n                if (err != NGX_EINTR) {\n                    ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, err,\n                                  \"sem_wait() failed while waiting on shmtx\");\n                    break;\n                }\n            }\n\n            ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0,\n                           \"shmtx awoke\");\n\n            continue;\n        }\n\n#endif\n\n        ngx_sched_yield();\n    }\n}\n\n\nvoid\nngx_shmtx_unlock(ngx_shmtx_t *mtx)\n{\n    if (mtx->spin != (ngx_uint_t) -1) {\n        ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0, \"shmtx unlock\");\n    }\n\n    if (ngx_atomic_cmp_set(mtx->lock, ngx_pid, 0)) {\n        ngx_shmtx_wakeup(mtx);\n    }\n}\n\n\nngx_uint_t\nngx_shmtx_force_unlock(ngx_shmtx_t *mtx, ngx_pid_t pid)\n{\n    ngx_log_debug0(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0,\n                   \"shmtx forced unlock\");\n\n    if (ngx_atomic_cmp_set(mtx->lock, pid, 0)) {\n        ngx_shmtx_wakeup(mtx);\n        return 1;\n    }\n\n    return 0;\n}\n\n\nstatic void\nngx_shmtx_wakeup(ngx_shmtx_t *mtx)\n{\n#if (NGX_HAVE_POSIX_SEM)\n    ngx_atomic_uint_t  wait;\n\n    if (!mtx->semaphore) {\n        return;\n    }\n\n    for ( ;; ) {\n\n        wait = *mtx->wait;\n\n        if ((ngx_atomic_int_t) wait <= 0) {\n            return;\n        }\n\n        if (ngx_atomic_cmp_set(mtx->wait, wait, wait - 1)) {\n            break;\n        }\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0,\n                   \"shmtx wake %uA\", wait);\n\n    if (sem_post(&mtx->sem) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_errno,\n                      \"sem_post() failed while wake shmtx\");\n    }\n\n#endif\n}\n\n\n#else\n\n\nngx_int_t\nngx_shmtx_create(ngx_shmtx_t *mtx, ngx_shmtx_sh_t *addr, u_char *name)\n{\n    if (mtx->name) {\n\n        if (ngx_strcmp(name, mtx->name) == 0) {\n            mtx->name = name;\n            return NGX_OK;\n        }\n\n        ngx_shmtx_destroy(mtx);\n    }\n\n    mtx->fd = ngx_open_file(name, NGX_FILE_RDWR, NGX_FILE_CREATE_OR_OPEN,\n                            NGX_FILE_DEFAULT_ACCESS);\n\n    if (mtx->fd == NGX_INVALID_FILE) {\n        ngx_log_error(NGX_LOG_EMERG, ngx_cycle->log, ngx_errno,\n                      ngx_open_file_n \" \\\"%s\\\" failed\", name);\n        return NGX_ERROR;\n    }\n\n    if (ngx_delete_file(name) == NGX_FILE_ERROR) {\n        ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_errno,\n                      ngx_delete_file_n \" \\\"%s\\\" failed\", name);\n    }\n\n    mtx->name = name;\n\n    return NGX_OK;\n}\n\n\nvoid\nngx_shmtx_destroy(ngx_shmtx_t *mtx)\n{\n    if (ngx_close_file(mtx->fd) == NGX_FILE_ERROR) {\n        ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_errno,\n                      ngx_close_file_n \" \\\"%s\\\" failed\", mtx->name);\n    }\n}\n\n\nngx_uint_t\nngx_shmtx_trylock(ngx_shmtx_t *mtx)\n{\n    ngx_err_t  err;\n\n    err = ngx_trylock_fd(mtx->fd);\n\n    if (err == 0) {\n        return 1;\n    }\n\n    if (err == NGX_EAGAIN) {\n        return 0;\n    }\n\n#if __osf__ /* Tru64 UNIX */\n\n    if (err == NGX_EACCES) {\n        return 0;\n    }\n\n#endif\n\n    ngx_log_abort(err, ngx_trylock_fd_n \" %s failed\", mtx->name);\n\n    return 0;\n}\n\n\nvoid\nngx_shmtx_lock(ngx_shmtx_t *mtx)\n{\n    ngx_err_t  err;\n\n    err = ngx_lock_fd(mtx->fd);\n\n    if (err == 0) {\n        return;\n    }\n\n    ngx_log_abort(err, ngx_lock_fd_n \" %s failed\", mtx->name);\n}\n\n\nvoid\nngx_shmtx_unlock(ngx_shmtx_t *mtx)\n{\n    ngx_err_t  err;\n\n    err = ngx_unlock_fd(mtx->fd);\n\n    if (err == 0) {\n        return;\n    }\n\n    ngx_log_abort(err, ngx_unlock_fd_n \" %s failed\", mtx->name);\n}\n\n\nngx_uint_t\nngx_shmtx_force_unlock(ngx_shmtx_t *mtx, ngx_pid_t pid)\n{\n    return 0;\n}\n\n#endif\n"
  },
  {
    "path": "src/core/ngx_shmtx.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_SHMTX_H_INCLUDED_\n#define _NGX_SHMTX_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\ntypedef struct {\n    ngx_atomic_t   lock;\n#if (NGX_HAVE_POSIX_SEM)\n    ngx_atomic_t   wait;\n#endif\n} ngx_shmtx_sh_t;\n\n\ntypedef struct {\n#if (NGX_HAVE_ATOMIC_OPS)\n    ngx_atomic_t  *lock;\n#if (NGX_HAVE_POSIX_SEM)\n    ngx_atomic_t  *wait;\n    ngx_uint_t     semaphore;\n    sem_t          sem;\n#endif\n#else\n    ngx_fd_t       fd;\n    u_char        *name;\n#endif\n    ngx_uint_t     spin;\n} ngx_shmtx_t;\n\n\nngx_int_t ngx_shmtx_create(ngx_shmtx_t *mtx, ngx_shmtx_sh_t *addr,\n    u_char *name);\nvoid ngx_shmtx_destroy(ngx_shmtx_t *mtx);\nngx_uint_t ngx_shmtx_trylock(ngx_shmtx_t *mtx);\nvoid ngx_shmtx_lock(ngx_shmtx_t *mtx);\nvoid ngx_shmtx_unlock(ngx_shmtx_t *mtx);\nngx_uint_t ngx_shmtx_force_unlock(ngx_shmtx_t *mtx, ngx_pid_t pid);\n\n\n#endif /* _NGX_SHMTX_H_INCLUDED_ */\n"
  },
  {
    "path": "src/core/ngx_slab.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\n#define NGX_SLAB_PAGE_MASK   3\n#define NGX_SLAB_PAGE        0\n#define NGX_SLAB_BIG         1\n#define NGX_SLAB_EXACT       2\n#define NGX_SLAB_SMALL       3\n\n#if (NGX_PTR_SIZE == 4)\n\n#define NGX_SLAB_PAGE_FREE   0\n#define NGX_SLAB_PAGE_BUSY   0xffffffff\n#define NGX_SLAB_PAGE_START  0x80000000\n\n#define NGX_SLAB_SHIFT_MASK  0x0000000f\n#define NGX_SLAB_MAP_MASK    0xffff0000\n#define NGX_SLAB_MAP_SHIFT   16\n\n#define NGX_SLAB_BUSY        0xffffffff\n\n#else /* (NGX_PTR_SIZE == 8) */\n\n#define NGX_SLAB_PAGE_FREE   0\n#define NGX_SLAB_PAGE_BUSY   0xffffffffffffffff\n#define NGX_SLAB_PAGE_START  0x8000000000000000\n\n#define NGX_SLAB_SHIFT_MASK  0x000000000000000f\n#define NGX_SLAB_MAP_MASK    0xffffffff00000000\n#define NGX_SLAB_MAP_SHIFT   32\n\n#define NGX_SLAB_BUSY        0xffffffffffffffff\n\n#endif\n\n\n#define ngx_slab_slots(pool)                                                  \\\n    (ngx_slab_page_t *) ((u_char *) (pool) + sizeof(ngx_slab_pool_t))\n\n#define ngx_slab_page_type(page)   ((page)->prev & NGX_SLAB_PAGE_MASK)\n\n#define ngx_slab_page_prev(page)                                              \\\n    (ngx_slab_page_t *) ((page)->prev & ~NGX_SLAB_PAGE_MASK)\n\n#define ngx_slab_page_addr(pool, page)                                        \\\n    ((((page) - (pool)->pages) << ngx_pagesize_shift)                         \\\n     + (uintptr_t) (pool)->start)\n\n\n#if (NGX_DEBUG_MALLOC)\n\n#define ngx_slab_junk(p, size)     ngx_memset(p, 0xA5, size)\n\n#elif (NGX_HAVE_DEBUG_MALLOC)\n\n#define ngx_slab_junk(p, size)                                                \\\n    if (ngx_debug_malloc)          ngx_memset(p, 0xA5, size)\n\n#else\n\n#define ngx_slab_junk(p, size)\n\n#endif\n\nstatic ngx_slab_page_t *ngx_slab_alloc_pages(ngx_slab_pool_t *pool,\n    ngx_uint_t pages);\nstatic void ngx_slab_free_pages(ngx_slab_pool_t *pool, ngx_slab_page_t *page,\n    ngx_uint_t pages);\nstatic void ngx_slab_error(ngx_slab_pool_t *pool, ngx_uint_t level,\n    char *text);\n\n\nstatic ngx_uint_t  ngx_slab_max_size;\nstatic ngx_uint_t  ngx_slab_exact_size;\nstatic ngx_uint_t  ngx_slab_exact_shift;\n\n\nvoid\nngx_slab_sizes_init(void)\n{\n    ngx_uint_t  n;\n\n    ngx_slab_max_size = ngx_pagesize / 2;\n    ngx_slab_exact_size = ngx_pagesize / (8 * sizeof(uintptr_t));\n    for (n = ngx_slab_exact_size; n >>= 1; ngx_slab_exact_shift++) {\n        /* void */\n    }\n}\n\n\nvoid\nngx_slab_init(ngx_slab_pool_t *pool)\n{\n    u_char           *p;\n    size_t            size;\n    ngx_int_t         m;\n    ngx_uint_t        i, n, pages;\n    ngx_slab_page_t  *slots, *page;\n\n    pool->min_size = (size_t) 1 << pool->min_shift;\n\n    slots = ngx_slab_slots(pool);\n\n    p = (u_char *) slots;\n    size = pool->end - p;\n\n    ngx_slab_junk(p, size);\n\n    n = ngx_pagesize_shift - pool->min_shift;\n\n    for (i = 0; i < n; i++) {\n        /* only \"next\" is used in list head */\n        slots[i].slab = 0;\n        slots[i].next = &slots[i];\n        slots[i].prev = 0;\n    }\n\n    p += n * sizeof(ngx_slab_page_t);\n\n    pool->stats = (ngx_slab_stat_t *) p;\n    ngx_memzero(pool->stats, n * sizeof(ngx_slab_stat_t));\n\n    p += n * sizeof(ngx_slab_stat_t);\n\n    size -= n * (sizeof(ngx_slab_page_t) + sizeof(ngx_slab_stat_t));\n\n    pages = (ngx_uint_t) (size / (ngx_pagesize + sizeof(ngx_slab_page_t)));\n\n    pool->pages = (ngx_slab_page_t *) p;\n    ngx_memzero(pool->pages, pages * sizeof(ngx_slab_page_t));\n\n    page = pool->pages;\n\n    /* only \"next\" is used in list head */\n    pool->free.slab = 0;\n    pool->free.next = page;\n    pool->free.prev = 0;\n\n    page->slab = pages;\n    page->next = &pool->free;\n    page->prev = (uintptr_t) &pool->free;\n\n    pool->start = ngx_align_ptr(p + pages * sizeof(ngx_slab_page_t),\n                                ngx_pagesize);\n\n    m = pages - (pool->end - pool->start) / ngx_pagesize;\n    if (m > 0) {\n        pages -= m;\n        page->slab = pages;\n    }\n\n    pool->last = pool->pages + pages;\n    pool->pfree = pages;\n\n    pool->log_nomem = 1;\n    pool->log_ctx = &pool->zero;\n    pool->zero = '\\0';\n}\n\n\nvoid *\nngx_slab_alloc(ngx_slab_pool_t *pool, size_t size)\n{\n    void  *p;\n\n    ngx_shmtx_lock(&pool->mutex);\n\n    p = ngx_slab_alloc_locked(pool, size);\n\n    ngx_shmtx_unlock(&pool->mutex);\n\n    return p;\n}\n\n\nvoid *\nngx_slab_alloc_locked(ngx_slab_pool_t *pool, size_t size)\n{\n    size_t            s;\n    uintptr_t         p, m, mask, *bitmap;\n    ngx_uint_t        i, n, slot, shift, map;\n    ngx_slab_page_t  *page, *prev, *slots;\n\n    if (size > ngx_slab_max_size) {\n\n        ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, ngx_cycle->log, 0,\n                       \"slab alloc: %uz\", size);\n\n        page = ngx_slab_alloc_pages(pool, (size >> ngx_pagesize_shift)\n                                          + ((size % ngx_pagesize) ? 1 : 0));\n        if (page) {\n            p = ngx_slab_page_addr(pool, page);\n\n        } else {\n            p = 0;\n        }\n\n        goto done;\n    }\n\n    if (size > pool->min_size) {\n        shift = 1;\n        for (s = size - 1; s >>= 1; shift++) { /* void */ }\n        slot = shift - pool->min_shift;\n\n    } else {\n        shift = pool->min_shift;\n        slot = 0;\n    }\n\n    pool->stats[slot].reqs++;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, ngx_cycle->log, 0,\n                   \"slab alloc: %uz slot: %ui\", size, slot);\n\n    slots = ngx_slab_slots(pool);\n    page = slots[slot].next;\n\n    if (page->next != page) {\n\n        if (shift < ngx_slab_exact_shift) {\n\n            bitmap = (uintptr_t *) ngx_slab_page_addr(pool, page);\n\n            map = (ngx_pagesize >> shift) / (8 * sizeof(uintptr_t));\n\n            for (n = 0; n < map; n++) {\n\n                if (bitmap[n] != NGX_SLAB_BUSY) {\n\n                    for (m = 1, i = 0; m; m <<= 1, i++) {\n                        if (bitmap[n] & m) {\n                            continue;\n                        }\n\n                        bitmap[n] |= m;\n\n                        i = (n * 8 * sizeof(uintptr_t) + i) << shift;\n\n                        p = (uintptr_t) bitmap + i;\n\n                        pool->stats[slot].used++;\n\n                        if (bitmap[n] == NGX_SLAB_BUSY) {\n                            for (n = n + 1; n < map; n++) {\n                                if (bitmap[n] != NGX_SLAB_BUSY) {\n                                    goto done;\n                                }\n                            }\n\n                            prev = ngx_slab_page_prev(page);\n                            prev->next = page->next;\n                            page->next->prev = page->prev;\n\n                            page->next = NULL;\n                            page->prev = NGX_SLAB_SMALL;\n                        }\n\n                        goto done;\n                    }\n                }\n            }\n\n        } else if (shift == ngx_slab_exact_shift) {\n\n            for (m = 1, i = 0; m; m <<= 1, i++) {\n                if (page->slab & m) {\n                    continue;\n                }\n\n                page->slab |= m;\n\n                if (page->slab == NGX_SLAB_BUSY) {\n                    prev = ngx_slab_page_prev(page);\n                    prev->next = page->next;\n                    page->next->prev = page->prev;\n\n                    page->next = NULL;\n                    page->prev = NGX_SLAB_EXACT;\n                }\n\n                p = ngx_slab_page_addr(pool, page) + (i << shift);\n\n                pool->stats[slot].used++;\n\n                goto done;\n            }\n\n        } else { /* shift > ngx_slab_exact_shift */\n\n            mask = ((uintptr_t) 1 << (ngx_pagesize >> shift)) - 1;\n            mask <<= NGX_SLAB_MAP_SHIFT;\n\n            for (m = (uintptr_t) 1 << NGX_SLAB_MAP_SHIFT, i = 0;\n                 m & mask;\n                 m <<= 1, i++)\n            {\n                if (page->slab & m) {\n                    continue;\n                }\n\n                page->slab |= m;\n\n                if ((page->slab & NGX_SLAB_MAP_MASK) == mask) {\n                    prev = ngx_slab_page_prev(page);\n                    prev->next = page->next;\n                    page->next->prev = page->prev;\n\n                    page->next = NULL;\n                    page->prev = NGX_SLAB_BIG;\n                }\n\n                p = ngx_slab_page_addr(pool, page) + (i << shift);\n\n                pool->stats[slot].used++;\n\n                goto done;\n            }\n        }\n\n        ngx_slab_error(pool, NGX_LOG_ALERT, \"ngx_slab_alloc(): page is busy\");\n        ngx_debug_point();\n    }\n\n    page = ngx_slab_alloc_pages(pool, 1);\n\n    if (page) {\n        if (shift < ngx_slab_exact_shift) {\n            bitmap = (uintptr_t *) ngx_slab_page_addr(pool, page);\n\n            n = (ngx_pagesize >> shift) / ((1 << shift) * 8);\n\n            if (n == 0) {\n                n = 1;\n            }\n\n            /* \"n\" elements for bitmap, plus one requested */\n\n            for (i = 0; i < (n + 1) / (8 * sizeof(uintptr_t)); i++) {\n                bitmap[i] = NGX_SLAB_BUSY;\n            }\n\n            m = ((uintptr_t) 1 << ((n + 1) % (8 * sizeof(uintptr_t)))) - 1;\n            bitmap[i] = m;\n\n            map = (ngx_pagesize >> shift) / (8 * sizeof(uintptr_t));\n\n            for (i = i + 1; i < map; i++) {\n                bitmap[i] = 0;\n            }\n\n            page->slab = shift;\n            page->next = &slots[slot];\n            page->prev = (uintptr_t) &slots[slot] | NGX_SLAB_SMALL;\n\n            slots[slot].next = page;\n\n            pool->stats[slot].total += (ngx_pagesize >> shift) - n;\n\n            p = ngx_slab_page_addr(pool, page) + (n << shift);\n\n            pool->stats[slot].used++;\n\n            goto done;\n\n        } else if (shift == ngx_slab_exact_shift) {\n\n            page->slab = 1;\n            page->next = &slots[slot];\n            page->prev = (uintptr_t) &slots[slot] | NGX_SLAB_EXACT;\n\n            slots[slot].next = page;\n\n            pool->stats[slot].total += 8 * sizeof(uintptr_t);\n\n            p = ngx_slab_page_addr(pool, page);\n\n            pool->stats[slot].used++;\n\n            goto done;\n\n        } else { /* shift > ngx_slab_exact_shift */\n\n            page->slab = ((uintptr_t) 1 << NGX_SLAB_MAP_SHIFT) | shift;\n            page->next = &slots[slot];\n            page->prev = (uintptr_t) &slots[slot] | NGX_SLAB_BIG;\n\n            slots[slot].next = page;\n\n            pool->stats[slot].total += ngx_pagesize >> shift;\n\n            p = ngx_slab_page_addr(pool, page);\n\n            pool->stats[slot].used++;\n\n            goto done;\n        }\n    }\n\n    p = 0;\n\n    pool->stats[slot].fails++;\n\ndone:\n\n    ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, ngx_cycle->log, 0,\n                   \"slab alloc: %p\", (void *) p);\n\n    return (void *) p;\n}\n\n\nvoid *\nngx_slab_calloc(ngx_slab_pool_t *pool, size_t size)\n{\n    void  *p;\n\n    ngx_shmtx_lock(&pool->mutex);\n\n    p = ngx_slab_calloc_locked(pool, size);\n\n    ngx_shmtx_unlock(&pool->mutex);\n\n    return p;\n}\n\n\nvoid *\nngx_slab_calloc_locked(ngx_slab_pool_t *pool, size_t size)\n{\n    void  *p;\n\n    p = ngx_slab_alloc_locked(pool, size);\n    if (p) {\n        ngx_memzero(p, size);\n    }\n\n    return p;\n}\n\n\nvoid\nngx_slab_free(ngx_slab_pool_t *pool, void *p)\n{\n    ngx_shmtx_lock(&pool->mutex);\n\n    ngx_slab_free_locked(pool, p);\n\n    ngx_shmtx_unlock(&pool->mutex);\n}\n\n\nvoid\nngx_slab_free_locked(ngx_slab_pool_t *pool, void *p)\n{\n    size_t            size;\n    uintptr_t         slab, m, *bitmap;\n    ngx_uint_t        i, n, type, slot, shift, map;\n    ngx_slab_page_t  *slots, *page;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_ALLOC, ngx_cycle->log, 0, \"slab free: %p\", p);\n\n    if ((u_char *) p < pool->start || (u_char *) p > pool->end) {\n        ngx_slab_error(pool, NGX_LOG_ALERT, \"ngx_slab_free(): outside of pool\");\n        goto fail;\n    }\n\n    n = ((u_char *) p - pool->start) >> ngx_pagesize_shift;\n    page = &pool->pages[n];\n    slab = page->slab;\n    type = ngx_slab_page_type(page);\n\n    switch (type) {\n\n    case NGX_SLAB_SMALL:\n\n        shift = slab & NGX_SLAB_SHIFT_MASK;\n        size = (size_t) 1 << shift;\n\n        if ((uintptr_t) p & (size - 1)) {\n            goto wrong_chunk;\n        }\n\n        n = ((uintptr_t) p & (ngx_pagesize - 1)) >> shift;\n        m = (uintptr_t) 1 << (n % (8 * sizeof(uintptr_t)));\n        n /= 8 * sizeof(uintptr_t);\n        bitmap = (uintptr_t *)\n                             ((uintptr_t) p & ~((uintptr_t) ngx_pagesize - 1));\n\n        if (bitmap[n] & m) {\n            slot = shift - pool->min_shift;\n\n            if (page->next == NULL) {\n                slots = ngx_slab_slots(pool);\n\n                page->next = slots[slot].next;\n                slots[slot].next = page;\n\n                page->prev = (uintptr_t) &slots[slot] | NGX_SLAB_SMALL;\n                page->next->prev = (uintptr_t) page | NGX_SLAB_SMALL;\n            }\n\n            bitmap[n] &= ~m;\n\n            n = (ngx_pagesize >> shift) / ((1 << shift) * 8);\n\n            if (n == 0) {\n                n = 1;\n            }\n\n            i = n / (8 * sizeof(uintptr_t));\n            m = ((uintptr_t) 1 << (n % (8 * sizeof(uintptr_t)))) - 1;\n\n            if (bitmap[i] & ~m) {\n                goto done;\n            }\n\n            map = (ngx_pagesize >> shift) / (8 * sizeof(uintptr_t));\n\n            for (i = i + 1; i < map; i++) {\n                if (bitmap[i]) {\n                    goto done;\n                }\n            }\n\n            ngx_slab_free_pages(pool, page, 1);\n\n            pool->stats[slot].total -= (ngx_pagesize >> shift) - n;\n\n            goto done;\n        }\n\n        goto chunk_already_free;\n\n    case NGX_SLAB_EXACT:\n\n        m = (uintptr_t) 1 <<\n                (((uintptr_t) p & (ngx_pagesize - 1)) >> ngx_slab_exact_shift);\n        size = ngx_slab_exact_size;\n\n        if ((uintptr_t) p & (size - 1)) {\n            goto wrong_chunk;\n        }\n\n        if (slab & m) {\n            slot = ngx_slab_exact_shift - pool->min_shift;\n\n            if (slab == NGX_SLAB_BUSY) {\n                slots = ngx_slab_slots(pool);\n\n                page->next = slots[slot].next;\n                slots[slot].next = page;\n\n                page->prev = (uintptr_t) &slots[slot] | NGX_SLAB_EXACT;\n                page->next->prev = (uintptr_t) page | NGX_SLAB_EXACT;\n            }\n\n            page->slab &= ~m;\n\n            if (page->slab) {\n                goto done;\n            }\n\n            ngx_slab_free_pages(pool, page, 1);\n\n            pool->stats[slot].total -= 8 * sizeof(uintptr_t);\n\n            goto done;\n        }\n\n        goto chunk_already_free;\n\n    case NGX_SLAB_BIG:\n\n        shift = slab & NGX_SLAB_SHIFT_MASK;\n        size = (size_t) 1 << shift;\n\n        if ((uintptr_t) p & (size - 1)) {\n            goto wrong_chunk;\n        }\n\n        m = (uintptr_t) 1 << ((((uintptr_t) p & (ngx_pagesize - 1)) >> shift)\n                              + NGX_SLAB_MAP_SHIFT);\n\n        if (slab & m) {\n            slot = shift - pool->min_shift;\n\n            if (page->next == NULL) {\n                slots = ngx_slab_slots(pool);\n\n                page->next = slots[slot].next;\n                slots[slot].next = page;\n\n                page->prev = (uintptr_t) &slots[slot] | NGX_SLAB_BIG;\n                page->next->prev = (uintptr_t) page | NGX_SLAB_BIG;\n            }\n\n            page->slab &= ~m;\n\n            if (page->slab & NGX_SLAB_MAP_MASK) {\n                goto done;\n            }\n\n            ngx_slab_free_pages(pool, page, 1);\n\n            pool->stats[slot].total -= ngx_pagesize >> shift;\n\n            goto done;\n        }\n\n        goto chunk_already_free;\n\n    case NGX_SLAB_PAGE:\n\n        if ((uintptr_t) p & (ngx_pagesize - 1)) {\n            goto wrong_chunk;\n        }\n\n        if (!(slab & NGX_SLAB_PAGE_START)) {\n            ngx_slab_error(pool, NGX_LOG_ALERT,\n                           \"ngx_slab_free(): page is already free\");\n            goto fail;\n        }\n\n        if (slab == NGX_SLAB_PAGE_BUSY) {\n            ngx_slab_error(pool, NGX_LOG_ALERT,\n                           \"ngx_slab_free(): pointer to wrong page\");\n            goto fail;\n        }\n\n        size = slab & ~NGX_SLAB_PAGE_START;\n\n        ngx_slab_free_pages(pool, page, size);\n\n        ngx_slab_junk(p, size << ngx_pagesize_shift);\n\n        return;\n    }\n\n    /* not reached */\n\n    return;\n\ndone:\n\n    pool->stats[slot].used--;\n\n    ngx_slab_junk(p, size);\n\n    return;\n\nwrong_chunk:\n\n    ngx_slab_error(pool, NGX_LOG_ALERT,\n                   \"ngx_slab_free(): pointer to wrong chunk\");\n\n    goto fail;\n\nchunk_already_free:\n\n    ngx_slab_error(pool, NGX_LOG_ALERT,\n                   \"ngx_slab_free(): chunk is already free\");\n\nfail:\n\n    return;\n}\n\n\nstatic ngx_slab_page_t *\nngx_slab_alloc_pages(ngx_slab_pool_t *pool, ngx_uint_t pages)\n{\n    ngx_slab_page_t  *page, *p;\n\n    for (page = pool->free.next; page != &pool->free; page = page->next) {\n\n        if (page->slab >= pages) {\n\n            if (page->slab > pages) {\n                page[page->slab - 1].prev = (uintptr_t) &page[pages];\n\n                page[pages].slab = page->slab - pages;\n                page[pages].next = page->next;\n                page[pages].prev = page->prev;\n\n                p = (ngx_slab_page_t *) page->prev;\n                p->next = &page[pages];\n                page->next->prev = (uintptr_t) &page[pages];\n\n            } else {\n                p = (ngx_slab_page_t *) page->prev;\n                p->next = page->next;\n                page->next->prev = page->prev;\n            }\n\n            page->slab = pages | NGX_SLAB_PAGE_START;\n            page->next = NULL;\n            page->prev = NGX_SLAB_PAGE;\n\n            pool->pfree -= pages;\n\n            if (--pages == 0) {\n                return page;\n            }\n\n            for (p = page + 1; pages; pages--) {\n                p->slab = NGX_SLAB_PAGE_BUSY;\n                p->next = NULL;\n                p->prev = NGX_SLAB_PAGE;\n                p++;\n            }\n\n            return page;\n        }\n    }\n\n    if (pool->log_nomem) {\n        ngx_slab_error(pool, NGX_LOG_CRIT,\n                       \"ngx_slab_alloc() failed: no memory\");\n    }\n\n    return NULL;\n}\n\n\nstatic void\nngx_slab_free_pages(ngx_slab_pool_t *pool, ngx_slab_page_t *page,\n    ngx_uint_t pages)\n{\n    ngx_slab_page_t  *prev, *join;\n\n    pool->pfree += pages;\n\n    page->slab = pages--;\n\n    if (pages) {\n        ngx_memzero(&page[1], pages * sizeof(ngx_slab_page_t));\n    }\n\n    if (page->next) {\n        prev = ngx_slab_page_prev(page);\n        prev->next = page->next;\n        page->next->prev = page->prev;\n    }\n\n    join = page + page->slab;\n\n    if (join < pool->last) {\n\n        if (ngx_slab_page_type(join) == NGX_SLAB_PAGE) {\n\n            if (join->next != NULL) {\n                pages += join->slab;\n                page->slab += join->slab;\n\n                prev = ngx_slab_page_prev(join);\n                prev->next = join->next;\n                join->next->prev = join->prev;\n\n                join->slab = NGX_SLAB_PAGE_FREE;\n                join->next = NULL;\n                join->prev = NGX_SLAB_PAGE;\n            }\n        }\n    }\n\n    if (page > pool->pages) {\n        join = page - 1;\n\n        if (ngx_slab_page_type(join) == NGX_SLAB_PAGE) {\n\n            if (join->slab == NGX_SLAB_PAGE_FREE) {\n                join = ngx_slab_page_prev(join);\n            }\n\n            if (join->next != NULL) {\n                pages += join->slab;\n                join->slab += page->slab;\n\n                prev = ngx_slab_page_prev(join);\n                prev->next = join->next;\n                join->next->prev = join->prev;\n\n                page->slab = NGX_SLAB_PAGE_FREE;\n                page->next = NULL;\n                page->prev = NGX_SLAB_PAGE;\n\n                page = join;\n            }\n        }\n    }\n\n    if (pages) {\n        page[pages].prev = (uintptr_t) page;\n    }\n\n    page->prev = (uintptr_t) &pool->free;\n    page->next = pool->free.next;\n\n    page->next->prev = (uintptr_t) page;\n\n    pool->free.next = page;\n}\n\n\nstatic void\nngx_slab_error(ngx_slab_pool_t *pool, ngx_uint_t level, char *text)\n{\n    ngx_log_error(level, ngx_cycle->log, 0, \"%s%s\", text, pool->log_ctx);\n}\n"
  },
  {
    "path": "src/core/ngx_slab.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_SLAB_H_INCLUDED_\n#define _NGX_SLAB_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\ntypedef struct ngx_slab_page_s  ngx_slab_page_t;\n\nstruct ngx_slab_page_s {\n    uintptr_t         slab;\n    ngx_slab_page_t  *next;\n    uintptr_t         prev;\n};\n\n\ntypedef struct {\n    ngx_uint_t        total;\n    ngx_uint_t        used;\n\n    ngx_uint_t        reqs;\n    ngx_uint_t        fails;\n} ngx_slab_stat_t;\n\n\ntypedef struct {\n    ngx_shmtx_sh_t    lock;\n\n    size_t            min_size;\n    size_t            min_shift;\n\n    ngx_slab_page_t  *pages;\n    ngx_slab_page_t  *last;\n    ngx_slab_page_t   free;\n\n    ngx_slab_stat_t  *stats;\n    ngx_uint_t        pfree;\n\n    u_char           *start;\n    u_char           *end;\n\n    ngx_shmtx_t       mutex;\n\n    u_char           *log_ctx;\n    u_char            zero;\n\n    unsigned          log_nomem:1;\n\n    void             *data;\n    void             *addr;\n} ngx_slab_pool_t;\n\n\nvoid ngx_slab_sizes_init(void);\nvoid ngx_slab_init(ngx_slab_pool_t *pool);\nvoid *ngx_slab_alloc(ngx_slab_pool_t *pool, size_t size);\nvoid *ngx_slab_alloc_locked(ngx_slab_pool_t *pool, size_t size);\nvoid *ngx_slab_calloc(ngx_slab_pool_t *pool, size_t size);\nvoid *ngx_slab_calloc_locked(ngx_slab_pool_t *pool, size_t size);\nvoid ngx_slab_free(ngx_slab_pool_t *pool, void *p);\nvoid ngx_slab_free_locked(ngx_slab_pool_t *pool, void *p);\n\n\n#endif /* _NGX_SLAB_H_INCLUDED_ */\n"
  },
  {
    "path": "src/core/ngx_spinlock.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\nvoid\nngx_spinlock(ngx_atomic_t *lock, ngx_atomic_int_t value, ngx_uint_t spin)\n{\n\n#if (NGX_HAVE_ATOMIC_OPS)\n\n    ngx_uint_t  i, n;\n\n    for ( ;; ) {\n\n        if (*lock == 0 && ngx_atomic_cmp_set(lock, 0, value)) {\n            return;\n        }\n\n        if (ngx_ncpu > 1) {\n\n            for (n = 1; n < spin; n <<= 1) {\n\n                for (i = 0; i < n; i++) {\n                    ngx_cpu_pause();\n                }\n\n                if (*lock == 0 && ngx_atomic_cmp_set(lock, 0, value)) {\n                    return;\n                }\n            }\n        }\n\n        ngx_sched_yield();\n    }\n\n#else\n\n#if (NGX_THREADS)\n\n#error ngx_spinlock() or ngx_atomic_cmp_set() are not defined !\n\n#endif\n\n#endif\n\n}\n"
  },
  {
    "path": "src/core/ngx_string.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\nstatic u_char *ngx_sprintf_num(u_char *buf, u_char *last, uint64_t ui64,\n    u_char zero, ngx_uint_t hexadecimal, ngx_uint_t width);\nstatic u_char *ngx_sprintf_str(u_char *buf, u_char *last, u_char *src,\n    size_t len, ngx_uint_t hexadecimal);\nstatic void ngx_encode_base64_internal(ngx_str_t *dst, ngx_str_t *src,\n    const u_char *basis, ngx_uint_t padding);\nstatic ngx_int_t ngx_decode_base64_internal(ngx_str_t *dst, ngx_str_t *src,\n    const u_char *basis);\n\n\nvoid\nngx_strlow(u_char *dst, u_char *src, size_t n)\n{\n    while (n) {\n        *dst = ngx_tolower(*src);\n        dst++;\n        src++;\n        n--;\n    }\n}\n\n\nsize_t\nngx_strnlen(u_char *p, size_t n)\n{\n    size_t  i;\n\n    for (i = 0; i < n; i++) {\n\n        if (p[i] == '\\0') {\n            return i;\n        }\n    }\n\n    return n;\n}\n\n\nu_char *\nngx_cpystrn(u_char *dst, u_char *src, size_t n)\n{\n    if (n == 0) {\n        return dst;\n    }\n\n    while (--n) {\n        *dst = *src;\n\n        if (*dst == '\\0') {\n            return dst;\n        }\n\n        dst++;\n        src++;\n    }\n\n    *dst = '\\0';\n\n    return dst;\n}\n\n\nu_char *\nngx_pstrdup(ngx_pool_t *pool, ngx_str_t *src)\n{\n    u_char  *dst;\n\n    dst = ngx_pnalloc(pool, src->len);\n    if (dst == NULL) {\n        return NULL;\n    }\n\n    ngx_memcpy(dst, src->data, src->len);\n\n    return dst;\n}\n\n\n/*\n * supported formats:\n *    %[0][width][x][X]O        off_t\n *    %[0][width]T              time_t\n *    %[0][width][u][x|X]z      ssize_t/size_t\n *    %[0][width][u][x|X]d      int/u_int\n *    %[0][width][u][x|X]l      long\n *    %[0][width|m][u][x|X]i    ngx_int_t/ngx_uint_t\n *    %[0][width][u][x|X]D      int32_t/uint32_t\n *    %[0][width][u][x|X]L      int64_t/uint64_t\n *    %[0][width|m][u][x|X]A    ngx_atomic_int_t/ngx_atomic_uint_t\n *    %[0][width][.width]f      double, max valid number fits to %18.15f\n *    %P                        ngx_pid_t\n *    %M                        ngx_msec_t\n *    %r                        rlim_t\n *    %p                        void *\n *    %[x|X]V                   ngx_str_t *\n *    %[x|X]v                   ngx_variable_value_t *\n *    %[x|X]s                   null-terminated string\n *    %*[x|X]s                  length and string\n *    %Z                        '\\0'\n *    %N                        '\\n'\n *    %c                        char\n *    %%                        %\n *\n *  reserved:\n *    %t                        ptrdiff_t\n *    %S                        null-terminated wchar string\n *    %C                        wchar\n */\n\n\nu_char * ngx_cdecl\nngx_sprintf(u_char *buf, const char *fmt, ...)\n{\n    u_char   *p;\n    va_list   args;\n\n    va_start(args, fmt);\n    p = ngx_vslprintf(buf, (void *) -1, fmt, args);\n    va_end(args);\n\n    return p;\n}\n\n\nu_char * ngx_cdecl\nngx_snprintf(u_char *buf, size_t max, const char *fmt, ...)\n{\n    u_char   *p;\n    va_list   args;\n\n    va_start(args, fmt);\n    p = ngx_vslprintf(buf, buf + max, fmt, args);\n    va_end(args);\n\n    return p;\n}\n\n\nu_char * ngx_cdecl\nngx_slprintf(u_char *buf, u_char *last, const char *fmt, ...)\n{\n    u_char   *p;\n    va_list   args;\n\n    va_start(args, fmt);\n    p = ngx_vslprintf(buf, last, fmt, args);\n    va_end(args);\n\n    return p;\n}\n\n\nu_char *\nngx_vslprintf(u_char *buf, u_char *last, const char *fmt, va_list args)\n{\n    u_char                *p, zero;\n    int                    d;\n    double                 f;\n    size_t                 slen;\n    int64_t                i64;\n    uint64_t               ui64, frac;\n    ngx_msec_t             ms;\n    ngx_uint_t             width, sign, hex, max_width, frac_width, scale, n;\n    ngx_str_t             *v;\n    ngx_variable_value_t  *vv;\n\n    while (*fmt && buf < last) {\n\n        /*\n         * \"buf < last\" means that we could copy at least one character:\n         * the plain character, \"%%\", \"%c\", and minus without the checking\n         */\n\n        if (*fmt == '%') {\n\n            i64 = 0;\n            ui64 = 0;\n\n            zero = (u_char) ((*++fmt == '0') ? '0' : ' ');\n            width = 0;\n            sign = 1;\n            hex = 0;\n            max_width = 0;\n            frac_width = 0;\n            slen = (size_t) -1;\n\n            while (*fmt >= '0' && *fmt <= '9') {\n                width = width * 10 + (*fmt++ - '0');\n            }\n\n\n            for ( ;; ) {\n                switch (*fmt) {\n\n                case 'u':\n                    sign = 0;\n                    fmt++;\n                    continue;\n\n                case 'm':\n                    max_width = 1;\n                    fmt++;\n                    continue;\n\n                case 'X':\n                    hex = 2;\n                    sign = 0;\n                    fmt++;\n                    continue;\n\n                case 'x':\n                    hex = 1;\n                    sign = 0;\n                    fmt++;\n                    continue;\n\n                case '.':\n                    fmt++;\n\n                    while (*fmt >= '0' && *fmt <= '9') {\n                        frac_width = frac_width * 10 + (*fmt++ - '0');\n                    }\n\n                    break;\n\n                case '*':\n                    slen = va_arg(args, size_t);\n                    fmt++;\n                    continue;\n\n                default:\n                    break;\n                }\n\n                break;\n            }\n\n\n            switch (*fmt) {\n\n            case 'V':\n                v = va_arg(args, ngx_str_t *);\n\n                buf = ngx_sprintf_str(buf, last, v->data, v->len, hex);\n                fmt++;\n\n                continue;\n\n            case 'v':\n                vv = va_arg(args, ngx_variable_value_t *);\n\n                buf = ngx_sprintf_str(buf, last, vv->data, vv->len, hex);\n                fmt++;\n\n                continue;\n\n            case 's':\n                p = va_arg(args, u_char *);\n\n                buf = ngx_sprintf_str(buf, last, p, slen, hex);\n                fmt++;\n\n                continue;\n\n            case 'O':\n                i64 = (int64_t) va_arg(args, off_t);\n                sign = 1;\n                break;\n\n            case 'P':\n                i64 = (int64_t) va_arg(args, ngx_pid_t);\n                sign = 1;\n                break;\n\n            case 'T':\n                i64 = (int64_t) va_arg(args, time_t);\n                sign = 1;\n                break;\n\n            case 'M':\n                ms = (ngx_msec_t) va_arg(args, ngx_msec_t);\n                if ((ngx_msec_int_t) ms == -1) {\n                    sign = 1;\n                    i64 = -1;\n                } else {\n                    sign = 0;\n                    ui64 = (uint64_t) ms;\n                }\n                break;\n\n            case 'z':\n                if (sign) {\n                    i64 = (int64_t) va_arg(args, ssize_t);\n                } else {\n                    ui64 = (uint64_t) va_arg(args, size_t);\n                }\n                break;\n\n            case 'i':\n                if (sign) {\n                    i64 = (int64_t) va_arg(args, ngx_int_t);\n                } else {\n                    ui64 = (uint64_t) va_arg(args, ngx_uint_t);\n                }\n\n                if (max_width) {\n                    width = NGX_INT_T_LEN;\n                }\n\n                break;\n\n            case 'd':\n                if (sign) {\n                    i64 = (int64_t) va_arg(args, int);\n                } else {\n                    ui64 = (uint64_t) va_arg(args, u_int);\n                }\n                break;\n\n            case 'l':\n                if (sign) {\n                    i64 = (int64_t) va_arg(args, long);\n                } else {\n                    ui64 = (uint64_t) va_arg(args, u_long);\n                }\n                break;\n\n            case 'D':\n                if (sign) {\n                    i64 = (int64_t) va_arg(args, int32_t);\n                } else {\n                    ui64 = (uint64_t) va_arg(args, uint32_t);\n                }\n                break;\n\n            case 'L':\n                if (sign) {\n                    i64 = va_arg(args, int64_t);\n                } else {\n                    ui64 = va_arg(args, uint64_t);\n                }\n                break;\n\n            case 'A':\n                if (sign) {\n                    i64 = (int64_t) va_arg(args, ngx_atomic_int_t);\n                } else {\n                    ui64 = (uint64_t) va_arg(args, ngx_atomic_uint_t);\n                }\n\n                if (max_width) {\n                    width = NGX_ATOMIC_T_LEN;\n                }\n\n                break;\n\n            case 'f':\n                f = va_arg(args, double);\n\n                if (f < 0) {\n                    *buf++ = '-';\n                    f = -f;\n                }\n\n                ui64 = (int64_t) f;\n                frac = 0;\n\n                if (frac_width) {\n\n                    scale = 1;\n                    for (n = frac_width; n; n--) {\n                        scale *= 10;\n                    }\n\n                    frac = (uint64_t) ((f - (double) ui64) * scale + 0.5);\n\n                    if (frac == scale) {\n                        ui64++;\n                        frac = 0;\n                    }\n                }\n\n                buf = ngx_sprintf_num(buf, last, ui64, zero, 0, width);\n\n                if (frac_width) {\n                    if (buf < last) {\n                        *buf++ = '.';\n                    }\n\n                    buf = ngx_sprintf_num(buf, last, frac, '0', 0, frac_width);\n                }\n\n                fmt++;\n\n                continue;\n\n#if !(NGX_WIN32)\n            case 'r':\n                i64 = (int64_t) va_arg(args, rlim_t);\n                sign = 1;\n                break;\n#endif\n\n            case 'p':\n                ui64 = (uintptr_t) va_arg(args, void *);\n                hex = 2;\n                sign = 0;\n                zero = '0';\n                width = 2 * sizeof(void *);\n                break;\n\n            case 'c':\n                d = va_arg(args, int);\n                *buf++ = (u_char) (d & 0xff);\n                fmt++;\n\n                continue;\n\n            case 'Z':\n                *buf++ = '\\0';\n                fmt++;\n\n                continue;\n\n            case 'N':\n#if (NGX_WIN32)\n                *buf++ = CR;\n                if (buf < last) {\n                    *buf++ = LF;\n                }\n#else\n                *buf++ = LF;\n#endif\n                fmt++;\n\n                continue;\n\n            case '%':\n                *buf++ = '%';\n                fmt++;\n\n                continue;\n\n            default:\n                *buf++ = *fmt++;\n\n                continue;\n            }\n\n            if (sign) {\n                if (i64 < 0) {\n                    *buf++ = '-';\n                    ui64 = (uint64_t) -i64;\n\n                } else {\n                    ui64 = (uint64_t) i64;\n                }\n            }\n\n            buf = ngx_sprintf_num(buf, last, ui64, zero, hex, width);\n\n            fmt++;\n\n        } else {\n            *buf++ = *fmt++;\n        }\n    }\n\n    return buf;\n}\n\n\nstatic u_char *\nngx_sprintf_num(u_char *buf, u_char *last, uint64_t ui64, u_char zero,\n    ngx_uint_t hexadecimal, ngx_uint_t width)\n{\n    u_char         *p, temp[NGX_INT64_LEN + 1];\n                       /*\n                        * we need temp[NGX_INT64_LEN] only,\n                        * but icc issues the warning\n                        */\n    size_t          len;\n    uint32_t        ui32;\n    static u_char   hex[] = \"0123456789abcdef\";\n    static u_char   HEX[] = \"0123456789ABCDEF\";\n\n    p = temp + NGX_INT64_LEN;\n\n    if (hexadecimal == 0) {\n\n        if (ui64 <= (uint64_t) NGX_MAX_UINT32_VALUE) {\n\n            /*\n             * To divide 64-bit numbers and to find remainders\n             * on the x86 platform gcc and icc call the libc functions\n             * [u]divdi3() and [u]moddi3(), they call another function\n             * in its turn.  On FreeBSD it is the qdivrem() function,\n             * its source code is about 170 lines of the code.\n             * The glibc counterpart is about 150 lines of the code.\n             *\n             * For 32-bit numbers and some divisors gcc and icc use\n             * a inlined multiplication and shifts.  For example,\n             * unsigned \"i32 / 10\" is compiled to\n             *\n             *     (i32 * 0xCCCCCCCD) >> 35\n             */\n\n            ui32 = (uint32_t) ui64;\n\n            do {\n                *--p = (u_char) (ui32 % 10 + '0');\n            } while (ui32 /= 10);\n\n        } else {\n            do {\n                *--p = (u_char) (ui64 % 10 + '0');\n            } while (ui64 /= 10);\n        }\n\n    } else if (hexadecimal == 1) {\n\n        do {\n\n            /* the \"(uint32_t)\" cast disables the BCC's warning */\n            *--p = hex[(uint32_t) (ui64 & 0xf)];\n\n        } while (ui64 >>= 4);\n\n    } else { /* hexadecimal == 2 */\n\n        do {\n\n            /* the \"(uint32_t)\" cast disables the BCC's warning */\n            *--p = HEX[(uint32_t) (ui64 & 0xf)];\n\n        } while (ui64 >>= 4);\n    }\n\n    /* zero or space padding */\n\n    len = (temp + NGX_INT64_LEN) - p;\n\n    while (len++ < width && buf < last) {\n        *buf++ = zero;\n    }\n\n    /* number safe copy */\n\n    len = (temp + NGX_INT64_LEN) - p;\n\n    if (buf + len > last) {\n        len = last - buf;\n    }\n\n    return ngx_cpymem(buf, p, len);\n}\n\n\nstatic u_char *\nngx_sprintf_str(u_char *buf, u_char *last, u_char *src, size_t len,\n    ngx_uint_t hexadecimal)\n{\n    static u_char   hex[] = \"0123456789abcdef\";\n    static u_char   HEX[] = \"0123456789ABCDEF\";\n\n    if (hexadecimal == 0) {\n\n        if (len == (size_t) -1) {\n            while (*src && buf < last) {\n                *buf++ = *src++;\n            }\n\n        } else {\n            len = ngx_min((size_t) (last - buf), len);\n            buf = ngx_cpymem(buf, src, len);\n        }\n\n    } else if (hexadecimal == 1) {\n\n        if (len == (size_t) -1) {\n\n            while (*src && buf < last - 1) {\n                *buf++ = hex[*src >> 4];\n                *buf++ = hex[*src++ & 0xf];\n            }\n\n        } else {\n\n            while (len-- && buf < last - 1) {\n                *buf++ = hex[*src >> 4];\n                *buf++ = hex[*src++ & 0xf];\n            }\n        }\n\n    } else { /* hexadecimal == 2 */\n\n        if (len == (size_t) -1) {\n\n            while (*src && buf < last - 1) {\n                *buf++ = HEX[*src >> 4];\n                *buf++ = HEX[*src++ & 0xf];\n            }\n\n        } else {\n\n            while (len-- && buf < last - 1) {\n                *buf++ = HEX[*src >> 4];\n                *buf++ = HEX[*src++ & 0xf];\n            }\n        }\n    }\n\n    return buf;\n}\n\n\n/*\n * We use ngx_strcasecmp()/ngx_strncasecmp() for 7-bit ASCII strings only,\n * and implement our own ngx_strcasecmp()/ngx_strncasecmp()\n * to avoid libc locale overhead.  Besides, we use the ngx_uint_t's\n * instead of the u_char's, because they are slightly faster.\n */\n\nngx_int_t\nngx_strcasecmp(u_char *s1, u_char *s2)\n{\n    ngx_uint_t  c1, c2;\n\n    for ( ;; ) {\n        c1 = (ngx_uint_t) *s1++;\n        c2 = (ngx_uint_t) *s2++;\n\n        c1 = (c1 >= 'A' && c1 <= 'Z') ? (c1 | 0x20) : c1;\n        c2 = (c2 >= 'A' && c2 <= 'Z') ? (c2 | 0x20) : c2;\n\n        if (c1 == c2) {\n\n            if (c1) {\n                continue;\n            }\n\n            return 0;\n        }\n\n        return c1 - c2;\n    }\n}\n\n\nngx_int_t\nngx_strncasecmp(u_char *s1, u_char *s2, size_t n)\n{\n    ngx_uint_t  c1, c2;\n\n    while (n) {\n        c1 = (ngx_uint_t) *s1++;\n        c2 = (ngx_uint_t) *s2++;\n\n        c1 = (c1 >= 'A' && c1 <= 'Z') ? (c1 | 0x20) : c1;\n        c2 = (c2 >= 'A' && c2 <= 'Z') ? (c2 | 0x20) : c2;\n\n        if (c1 == c2) {\n\n            if (c1) {\n                n--;\n                continue;\n            }\n\n            return 0;\n        }\n\n        return c1 - c2;\n    }\n\n    return 0;\n}\n\n\nu_char *\nngx_strnstr(u_char *s1, char *s2, size_t len)\n{\n    u_char  c1, c2;\n    size_t  n;\n\n    c2 = *(u_char *) s2++;\n\n    n = ngx_strlen(s2);\n\n    do {\n        do {\n            if (len-- == 0) {\n                return NULL;\n            }\n\n            c1 = *s1++;\n\n            if (c1 == 0) {\n                return NULL;\n            }\n\n        } while (c1 != c2);\n\n        if (n > len) {\n            return NULL;\n        }\n\n    } while (ngx_strncmp(s1, (u_char *) s2, n) != 0);\n\n    return --s1;\n}\n\n\n/*\n * ngx_strstrn() and ngx_strcasestrn() are intended to search for static\n * substring with known length in null-terminated string. The argument n\n * must be length of the second substring - 1.\n */\n\nu_char *\nngx_strstrn(u_char *s1, char *s2, size_t n)\n{\n    u_char  c1, c2;\n\n    c2 = *(u_char *) s2++;\n\n    do {\n        do {\n            c1 = *s1++;\n\n            if (c1 == 0) {\n                return NULL;\n            }\n\n        } while (c1 != c2);\n\n    } while (ngx_strncmp(s1, (u_char *) s2, n) != 0);\n\n    return --s1;\n}\n\n\nu_char *\nngx_strcasestrn(u_char *s1, char *s2, size_t n)\n{\n    ngx_uint_t  c1, c2;\n\n    c2 = (ngx_uint_t) *s2++;\n    c2 = (c2 >= 'A' && c2 <= 'Z') ? (c2 | 0x20) : c2;\n\n    do {\n        do {\n            c1 = (ngx_uint_t) *s1++;\n\n            if (c1 == 0) {\n                return NULL;\n            }\n\n            c1 = (c1 >= 'A' && c1 <= 'Z') ? (c1 | 0x20) : c1;\n\n        } while (c1 != c2);\n\n    } while (ngx_strncasecmp(s1, (u_char *) s2, n) != 0);\n\n    return --s1;\n}\n\n\n/*\n * ngx_strlcasestrn() 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\nu_char *\nngx_strlcasestrn(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    c2 = (c2 >= 'A' && c2 <= 'Z') ? (c2 | 0x20) : c2;\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            c1 = (c1 >= 'A' && c1 <= 'Z') ? (c1 | 0x20) : c1;\n\n        } while (c1 != c2);\n\n    } while (ngx_strncasecmp(s1, s2, n) != 0);\n\n    return --s1;\n}\n\n\nngx_int_t\nngx_rstrncmp(u_char *s1, u_char *s2, size_t n)\n{\n    if (n == 0) {\n        return 0;\n    }\n\n    n--;\n\n    for ( ;; ) {\n        if (s1[n] != s2[n]) {\n            return s1[n] - s2[n];\n        }\n\n        if (n == 0) {\n            return 0;\n        }\n\n        n--;\n    }\n}\n\n\nngx_int_t\nngx_rstrncasecmp(u_char *s1, u_char *s2, size_t n)\n{\n    u_char  c1, c2;\n\n    if (n == 0) {\n        return 0;\n    }\n\n    n--;\n\n    for ( ;; ) {\n        c1 = s1[n];\n        if (c1 >= 'a' && c1 <= 'z') {\n            c1 -= 'a' - 'A';\n        }\n\n        c2 = s2[n];\n        if (c2 >= 'a' && c2 <= 'z') {\n            c2 -= 'a' - 'A';\n        }\n\n        if (c1 != c2) {\n            return c1 - c2;\n        }\n\n        if (n == 0) {\n            return 0;\n        }\n\n        n--;\n    }\n}\n\n\nngx_int_t\nngx_memn2cmp(u_char *s1, u_char *s2, size_t n1, size_t n2)\n{\n    size_t     n;\n    ngx_int_t  m, z;\n\n    if (n1 <= n2) {\n        n = n1;\n        z = -1;\n\n    } else {\n        n = n2;\n        z = 1;\n    }\n\n    m = ngx_memcmp(s1, s2, n);\n\n    if (m || n1 == n2) {\n        return m;\n    }\n\n    return z;\n}\n\n\nngx_int_t\nngx_dns_strcmp(u_char *s1, u_char *s2)\n{\n    ngx_uint_t  c1, c2;\n\n    for ( ;; ) {\n        c1 = (ngx_uint_t) *s1++;\n        c2 = (ngx_uint_t) *s2++;\n\n        c1 = (c1 >= 'A' && c1 <= 'Z') ? (c1 | 0x20) : c1;\n        c2 = (c2 >= 'A' && c2 <= 'Z') ? (c2 | 0x20) : c2;\n\n        if (c1 == c2) {\n\n            if (c1) {\n                continue;\n            }\n\n            return 0;\n        }\n\n        /* in ASCII '.' > '-', but we need '.' to be the lowest character */\n\n        c1 = (c1 == '.') ? ' ' : c1;\n        c2 = (c2 == '.') ? ' ' : c2;\n\n        return c1 - c2;\n    }\n}\n\n\nngx_int_t\nngx_filename_cmp(u_char *s1, u_char *s2, size_t n)\n{\n    ngx_uint_t  c1, c2;\n\n    while (n) {\n        c1 = (ngx_uint_t) *s1++;\n        c2 = (ngx_uint_t) *s2++;\n\n#if (NGX_HAVE_CASELESS_FILESYSTEM)\n        c1 = tolower(c1);\n        c2 = tolower(c2);\n#endif\n\n        if (c1 == c2) {\n\n            if (c1) {\n                n--;\n                continue;\n            }\n\n            return 0;\n        }\n\n        /* we need '/' to be the lowest character */\n\n        if (c1 == 0 || c2 == 0) {\n            return c1 - c2;\n        }\n\n        c1 = (c1 == '/') ? 0 : c1;\n        c2 = (c2 == '/') ? 0 : c2;\n\n        return c1 - c2;\n    }\n\n    return 0;\n}\n\n\nngx_int_t\nngx_atoi(u_char *line, size_t n)\n{\n    ngx_int_t  value, cutoff, cutlim;\n\n    if (n == 0) {\n        return NGX_ERROR;\n    }\n\n    cutoff = NGX_MAX_INT_T_VALUE / 10;\n    cutlim = NGX_MAX_INT_T_VALUE % 10;\n\n    for (value = 0; n--; line++) {\n        if (*line < '0' || *line > '9') {\n            return NGX_ERROR;\n        }\n\n        if (value >= cutoff && (value > cutoff || *line - '0' > cutlim)) {\n            return NGX_ERROR;\n        }\n\n        value = value * 10 + (*line - '0');\n    }\n\n    return value;\n}\n\n\n#if (T_NGX_HTTP_IMPROVED_IF)\nlong long\nngx_atoll(u_char *line, size_t n)\n{\n    long long value;\n\n    if (n == 0) {\n        return NGX_ERROR;\n    }\n\n    for (value = 0; n--; line++) {\n        if (*line < '0' || *line > '9') {\n            return NGX_ERROR;\n        }\n\n        value = value * 10 + (*line - '0');\n    }\n\n    if (value < 0) {\n        return NGX_ERROR;\n\n    } else {\n        return value;\n    }\n}\n#endif\n\n\n/* parse a fixed point number, e.g., ngx_atofp(\"10.5\", 4, 2) returns 1050 */\n\nngx_int_t\nngx_atofp(u_char *line, size_t n, size_t point)\n{\n    ngx_int_t   value, cutoff, cutlim;\n    ngx_uint_t  dot;\n\n    if (n == 0) {\n        return NGX_ERROR;\n    }\n\n    cutoff = NGX_MAX_INT_T_VALUE / 10;\n    cutlim = NGX_MAX_INT_T_VALUE % 10;\n\n    dot = 0;\n\n    for (value = 0; n--; line++) {\n\n        if (point == 0) {\n            return NGX_ERROR;\n        }\n\n        if (*line == '.') {\n            if (dot) {\n                return NGX_ERROR;\n            }\n\n            dot = 1;\n            continue;\n        }\n\n        if (*line < '0' || *line > '9') {\n            return NGX_ERROR;\n        }\n\n        if (value >= cutoff && (value > cutoff || *line - '0' > cutlim)) {\n            return NGX_ERROR;\n        }\n\n        value = value * 10 + (*line - '0');\n        point -= dot;\n    }\n\n    while (point--) {\n        if (value > cutoff) {\n            return NGX_ERROR;\n        }\n\n        value = value * 10;\n    }\n\n    return value;\n}\n\n\nssize_t\nngx_atosz(u_char *line, size_t n)\n{\n    ssize_t  value, cutoff, cutlim;\n\n    if (n == 0) {\n        return NGX_ERROR;\n    }\n\n    cutoff = NGX_MAX_SIZE_T_VALUE / 10;\n    cutlim = NGX_MAX_SIZE_T_VALUE % 10;\n\n    for (value = 0; n--; line++) {\n        if (*line < '0' || *line > '9') {\n            return NGX_ERROR;\n        }\n\n        if (value >= cutoff && (value > cutoff || *line - '0' > cutlim)) {\n            return NGX_ERROR;\n        }\n\n        value = value * 10 + (*line - '0');\n    }\n\n    return value;\n}\n\n\noff_t\nngx_atoof(u_char *line, size_t n)\n{\n    off_t  value, cutoff, cutlim;\n\n    if (n == 0) {\n        return NGX_ERROR;\n    }\n\n    cutoff = NGX_MAX_OFF_T_VALUE / 10;\n    cutlim = NGX_MAX_OFF_T_VALUE % 10;\n\n    for (value = 0; n--; line++) {\n        if (*line < '0' || *line > '9') {\n            return NGX_ERROR;\n        }\n\n        if (value >= cutoff && (value > cutoff || *line - '0' > cutlim)) {\n            return NGX_ERROR;\n        }\n\n        value = value * 10 + (*line - '0');\n    }\n\n    return value;\n}\n\n\ntime_t\nngx_atotm(u_char *line, size_t n)\n{\n    time_t  value, cutoff, cutlim;\n\n    if (n == 0) {\n        return NGX_ERROR;\n    }\n\n    cutoff = NGX_MAX_TIME_T_VALUE / 10;\n    cutlim = NGX_MAX_TIME_T_VALUE % 10;\n\n    for (value = 0; n--; line++) {\n        if (*line < '0' || *line > '9') {\n            return NGX_ERROR;\n        }\n\n        if (value >= cutoff && (value > cutoff || *line - '0' > cutlim)) {\n            return NGX_ERROR;\n        }\n\n        value = value * 10 + (*line - '0');\n    }\n\n    return value;\n}\n\n\nngx_int_t\nngx_hextoi(u_char *line, size_t n)\n{\n    u_char     c, ch;\n    ngx_int_t  value, cutoff;\n\n    if (n == 0) {\n        return NGX_ERROR;\n    }\n\n    cutoff = NGX_MAX_INT_T_VALUE / 16;\n\n    for (value = 0; n--; line++) {\n        if (value > cutoff) {\n            return NGX_ERROR;\n        }\n\n        ch = *line;\n\n        if (ch >= '0' && ch <= '9') {\n            value = value * 16 + (ch - '0');\n            continue;\n        }\n\n        c = (u_char) (ch | 0x20);\n\n        if (c >= 'a' && c <= 'f') {\n            value = value * 16 + (c - 'a' + 10);\n            continue;\n        }\n\n        return NGX_ERROR;\n    }\n\n    return value;\n}\n\n\nu_char *\nngx_hex_dump(u_char *dst, u_char *src, size_t len)\n{\n    static u_char  hex[] = \"0123456789abcdef\";\n\n    while (len--) {\n        *dst++ = hex[*src >> 4];\n        *dst++ = hex[*src++ & 0xf];\n    }\n\n    return dst;\n}\n\n\nvoid\nngx_encode_base64(ngx_str_t *dst, ngx_str_t *src)\n{\n    static u_char   basis64[] =\n            \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\n\n    ngx_encode_base64_internal(dst, src, basis64, 1);\n}\n\n\nvoid\nngx_encode_base64url(ngx_str_t *dst, ngx_str_t *src)\n{\n    static u_char   basis64[] =\n            \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_\";\n\n    ngx_encode_base64_internal(dst, src, basis64, 0);\n}\n\n\nstatic void\nngx_encode_base64_internal(ngx_str_t *dst, ngx_str_t *src, const u_char *basis,\n    ngx_uint_t padding)\n{\n    u_char         *d, *s;\n    size_t          len;\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 (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 (padding) {\n            *d++ = '=';\n        }\n    }\n\n    dst->len = d - dst->data;\n}\n\n\nngx_int_t\nngx_decode_base64(ngx_str_t *dst, ngx_str_t *src)\n{\n    static u_char   basis64[] = {\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    return ngx_decode_base64_internal(dst, src, basis64);\n}\n\n\nngx_int_t\nngx_decode_base64url(ngx_str_t *dst, ngx_str_t *src)\n{\n    static u_char   basis64[] = {\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, 62, 77, 77,\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, 63,\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    return ngx_decode_base64_internal(dst, src, basis64);\n}\n\n\nstatic ngx_int_t\nngx_decode_base64_internal(ngx_str_t *dst, ngx_str_t *src, const u_char *basis)\n{\n    size_t          len;\n    u_char         *d, *s;\n\n    for (len = 0; len < src->len; len++) {\n        if (src->data[len] == '=') {\n            break;\n        }\n\n        if (basis[src->data[len]] == 77) {\n            return NGX_ERROR;\n        }\n    }\n\n    if (len % 4 == 1) {\n        return NGX_ERROR;\n    }\n\n    s = src->data;\n    d = dst->data;\n\n    while (len > 3) {\n        *d++ = (u_char) (basis[s[0]] << 2 | basis[s[1]] >> 4);\n        *d++ = (u_char) (basis[s[1]] << 4 | basis[s[2]] >> 2);\n        *d++ = (u_char) (basis[s[2]] << 6 | basis[s[3]]);\n\n        s += 4;\n        len -= 4;\n    }\n\n    if (len > 1) {\n        *d++ = (u_char) (basis[s[0]] << 2 | basis[s[1]] >> 4);\n    }\n\n    if (len > 2) {\n        *d++ = (u_char) (basis[s[1]] << 4 | basis[s[2]] >> 2);\n    }\n\n    dst->len = d - dst->data;\n\n    return NGX_OK;\n}\n\n\n/*\n * ngx_utf8_decode() decodes two and more bytes UTF sequences only\n * the return values:\n *    0x80 - 0x10ffff         valid character\n *    0x110000 - 0xfffffffd   invalid sequence\n *    0xfffffffe              incomplete sequence\n *    0xffffffff              error\n */\n\nuint32_t\nngx_utf8_decode(u_char **p, size_t n)\n{\n    size_t    len;\n    uint32_t  u, i, valid;\n\n    u = **p;\n\n    if (u >= 0xf8) {\n\n        (*p)++;\n        return 0xffffffff;\n\n    } else if (u >= 0xf0) {\n\n        u &= 0x07;\n        valid = 0xffff;\n        len = 3;\n\n    } else if (u >= 0xe0) {\n\n        u &= 0x0f;\n        valid = 0x7ff;\n        len = 2;\n\n    } else if (u >= 0xc2) {\n\n        u &= 0x1f;\n        valid = 0x7f;\n        len = 1;\n\n    } else {\n        (*p)++;\n        return 0xffffffff;\n    }\n\n    if (n - 1 < len) {\n        return 0xfffffffe;\n    }\n\n    (*p)++;\n\n    while (len) {\n        i = *(*p)++;\n\n        if (i < 0x80) {\n            return 0xffffffff;\n        }\n\n        u = (u << 6) | (i & 0x3f);\n\n        len--;\n    }\n\n    if (u > valid) {\n        return u;\n    }\n\n    return 0xffffffff;\n}\n\n\nsize_t\nngx_utf8_length(u_char *p, size_t n)\n{\n    u_char  c, *last;\n    size_t  len;\n\n    last = p + n;\n\n    for (len = 0; p < last; len++) {\n\n        c = *p;\n\n        if (c < 0x80) {\n            p++;\n            continue;\n        }\n\n        if (ngx_utf8_decode(&p, last - p) > 0x10ffff) {\n            /* invalid UTF-8 */\n            return n;\n        }\n    }\n\n    return len;\n}\n\n\nu_char *\nngx_utf8_cpystrn(u_char *dst, u_char *src, size_t n, size_t len)\n{\n    u_char  c, *next;\n\n    if (n == 0) {\n        return dst;\n    }\n\n    while (--n) {\n\n        c = *src;\n        *dst = c;\n\n        if (c < 0x80) {\n\n            if (c != '\\0') {\n                dst++;\n                src++;\n                len--;\n\n                continue;\n            }\n\n            return dst;\n        }\n\n        next = src;\n\n        if (ngx_utf8_decode(&next, len) > 0x10ffff) {\n            /* invalid UTF-8 */\n            break;\n        }\n\n        while (src < next) {\n            *dst++ = *src++;\n            len--;\n        }\n    }\n\n    *dst = '\\0';\n\n    return dst;\n}\n\n\nuintptr_t\nngx_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    /*\n     * Per RFC 3986 only the following chars are allowed in URIs unescaped:\n     *\n     * unreserved    = ALPHA / DIGIT / \"-\" / \".\" / \"_\" / \"~\"\n     * gen-delims    = \":\" / \"/\" / \"?\" / \"#\" / \"[\" / \"]\" / \"@\"\n     * sub-delims    = \"!\" / \"$\" / \"&\" / \"'\" / \"(\" / \")\"\n     *               / \"*\" / \"+\" / \",\" / \";\" / \"=\"\n     *\n     * And \"%\" can appear as a part of escaping itself.  The following\n     * characters are not allowed and need to be escaped: %00-%1F, %7F-%FF,\n     * \" \", \"\"\", \"<\", \">\", \"\\\", \"^\", \"`\", \"{\", \"|\", \"}\".\n     */\n\n                    /* \" \", \"#\", \"%\", \"?\", not allowed */\n\n    static uint32_t   uri[] = {\n        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */\n\n                    /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #\"!  */\n        0xd000002d, /* 1101 0000 0000 0000  0000 0000 0010 1101 */\n\n                    /* _^]\\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */\n        0x50000000, /* 0101 0000 0000 0000  0000 0000 0000 0000 */\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                    /* \" \", \"#\", \"%\", \"&\", \"+\", \";\", \"?\", not allowed */\n\n    static uint32_t   args[] = {\n        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */\n\n                    /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #\"!  */\n        0xd800086d, /* 1101 1000 0000 0000  0000 1000 0110 1101 */\n\n                    /* _^]\\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */\n        0x50000000, /* 0101 0000 0000 0000  0000 0000 0000 0000 */\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                    /* 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        0xfc009fff, /* 1111 1100 0000 0000  1001 1111 1111 1111 */\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                    /* \" \", \"#\", \"\"\", \"%\", \"'\", not allowed */\n\n    static uint32_t   html[] = {\n        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */\n\n                    /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #\"!  */\n        0x500000ad, /* 0101 0000 0000 0000  0000 0000 1010 1101 */\n\n                    /* _^]\\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */\n        0x50000000, /* 0101 0000 0000 0000  0000 0000 0000 0000 */\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                    /* \" \", \"\"\", \"'\", not allowed */\n\n    static uint32_t   refresh[] = {\n        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */\n\n                    /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #\"!  */\n        0x50000085, /* 0101 0000 0000 0000  0000 0000 1000 0101 */\n\n                    /* _^]\\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */\n        0x50000000, /* 0101 0000 0000 0000  0000 0000 0000 0000 */\n\n                    /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */\n        0xd8000001, /* 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 */\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    static uint32_t  *map[] =\n        { uri, args, uri_component, html, refresh, memcached, memcached };\n\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] & (1U << (*src & 0x1f))) {\n                n++;\n            }\n            src++;\n            size--;\n        }\n\n        return (uintptr_t) n;\n    }\n\n    while (size) {\n        if (escape[*src >> 5] & (1U << (*src & 0x1f))) {\n            *dst++ = '%';\n            *dst++ = hex[*src >> 4];\n            *dst++ = hex[*src & 0xf];\n            src++;\n\n        } else {\n            *dst++ = *src++;\n        }\n        size--;\n    }\n\n    return (uintptr_t) dst;\n}\n\n\nvoid\nngx_unescape_uri(u_char **dst, u_char **src, size_t size, ngx_uint_t type)\n{\n    u_char  *d, *s, ch, c, decoded;\n    enum {\n        sw_usual = 0,\n        sw_quoted,\n        sw_quoted_second\n    } state;\n\n    d = *dst;\n    s = *src;\n\n    state = 0;\n    decoded = 0;\n\n    while (size--) {\n\n        ch = *s++;\n\n        switch (state) {\n        case sw_usual:\n            if (ch == '?'\n                && (type & (NGX_UNESCAPE_URI|NGX_UNESCAPE_REDIRECT)))\n            {\n                *d++ = ch;\n                goto done;\n            }\n\n            if (ch == '%') {\n                state = sw_quoted;\n                break;\n            }\n\n            *d++ = ch;\n            break;\n\n        case sw_quoted:\n\n            if (ch >= '0' && ch <= '9') {\n                decoded = (u_char) (ch - '0');\n                state = sw_quoted_second;\n                break;\n            }\n\n            c = (u_char) (ch | 0x20);\n            if (c >= 'a' && c <= 'f') {\n                decoded = (u_char) (c - 'a' + 10);\n                state = sw_quoted_second;\n                break;\n            }\n\n            /* the invalid quoted character */\n\n            state = sw_usual;\n\n            *d++ = ch;\n\n            break;\n\n        case sw_quoted_second:\n\n            state = sw_usual;\n\n            if (ch >= '0' && ch <= '9') {\n                ch = (u_char) ((decoded << 4) + (ch - '0'));\n\n                if (type & NGX_UNESCAPE_REDIRECT) {\n                    if (ch > '%' && ch < 0x7f) {\n                        *d++ = ch;\n                        break;\n                    }\n\n                    *d++ = '%'; *d++ = *(s - 2); *d++ = *(s - 1);\n\n                    break;\n                }\n\n                *d++ = ch;\n\n                break;\n            }\n\n            c = (u_char) (ch | 0x20);\n            if (c >= 'a' && c <= 'f') {\n                ch = (u_char) ((decoded << 4) + (c - 'a') + 10);\n\n                if (type & NGX_UNESCAPE_URI) {\n                    if (ch == '?') {\n                        *d++ = ch;\n                        goto done;\n                    }\n\n                    *d++ = ch;\n                    break;\n                }\n\n                if (type & NGX_UNESCAPE_REDIRECT) {\n                    if (ch == '?') {\n                        *d++ = ch;\n                        goto done;\n                    }\n\n                    if (ch > '%' && ch < 0x7f) {\n                        *d++ = ch;\n                        break;\n                    }\n\n                    *d++ = '%'; *d++ = *(s - 2); *d++ = *(s - 1);\n                    break;\n                }\n\n                *d++ = ch;\n\n                break;\n            }\n\n            /* the invalid quoted character */\n\n            break;\n        }\n    }\n\ndone:\n\n    *dst = d;\n    *src = s;\n}\n\n\nuintptr_t\nngx_escape_html(u_char *dst, u_char *src, size_t size)\n{\n    u_char      ch;\n    ngx_uint_t  len;\n\n    if (dst == NULL) {\n\n        len = 0;\n\n        while (size) {\n            switch (*src++) {\n\n            case '<':\n                len += sizeof(\"&lt;\") - 2;\n                break;\n\n            case '>':\n                len += sizeof(\"&gt;\") - 2;\n                break;\n\n            case '&':\n                len += sizeof(\"&amp;\") - 2;\n                break;\n\n            case '\"':\n                len += sizeof(\"&quot;\") - 2;\n                break;\n\n            default:\n                break;\n            }\n            size--;\n        }\n\n        return (uintptr_t) len;\n    }\n\n    while (size) {\n        ch = *src++;\n\n        switch (ch) {\n\n        case '<':\n            *dst++ = '&'; *dst++ = 'l'; *dst++ = 't'; *dst++ = ';';\n            break;\n\n        case '>':\n            *dst++ = '&'; *dst++ = 'g'; *dst++ = 't'; *dst++ = ';';\n            break;\n\n        case '&':\n            *dst++ = '&'; *dst++ = 'a'; *dst++ = 'm'; *dst++ = 'p';\n            *dst++ = ';';\n            break;\n\n        case '\"':\n            *dst++ = '&'; *dst++ = 'q'; *dst++ = 'u'; *dst++ = 'o';\n            *dst++ = 't'; *dst++ = ';';\n            break;\n\n        default:\n            *dst++ = ch;\n            break;\n        }\n        size--;\n    }\n\n    return (uintptr_t) dst;\n}\n\n\nuintptr_t\nngx_escape_json(u_char *dst, u_char *src, size_t size)\n{\n    u_char      ch;\n    ngx_uint_t  len;\n\n    if (dst == NULL) {\n        len = 0;\n\n        while (size) {\n            ch = *src++;\n\n            if (ch == '\\\\' || ch == '\"') {\n                len++;\n\n            } else if (ch <= 0x1f) {\n\n                switch (ch) {\n                case '\\n':\n                case '\\r':\n                case '\\t':\n                case '\\b':\n                case '\\f':\n                    len++;\n                    break;\n\n                default:\n                    len += sizeof(\"\\\\u001F\") - 2;\n                }\n            }\n\n            size--;\n        }\n\n        return (uintptr_t) len;\n    }\n\n    while (size) {\n        ch = *src++;\n\n        if (ch > 0x1f) {\n\n            if (ch == '\\\\' || ch == '\"') {\n                *dst++ = '\\\\';\n            }\n\n            *dst++ = ch;\n\n        } else {\n            *dst++ = '\\\\';\n\n            switch (ch) {\n            case '\\n':\n                *dst++ = 'n';\n                break;\n\n            case '\\r':\n                *dst++ = 'r';\n                break;\n\n            case '\\t':\n                *dst++ = 't';\n                break;\n\n            case '\\b':\n                *dst++ = 'b';\n                break;\n\n            case '\\f':\n                *dst++ = 'f';\n                break;\n\n            default:\n                *dst++ = 'u'; *dst++ = '0'; *dst++ = '0';\n                *dst++ = '0' + (ch >> 4);\n\n                ch &= 0xf;\n\n                *dst++ = (ch < 10) ? ('0' + ch) : ('A' + ch - 10);\n            }\n        }\n\n        size--;\n    }\n\n    return (uintptr_t) dst;\n}\n\n\nvoid\nngx_str_rbtree_insert_value(ngx_rbtree_node_t *temp,\n    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)\n{\n    ngx_str_node_t      *n, *t;\n    ngx_rbtree_node_t  **p;\n\n    for ( ;; ) {\n\n        n = (ngx_str_node_t *) node;\n        t = (ngx_str_node_t *) temp;\n\n        if (node->key != temp->key) {\n\n            p = (node->key < temp->key) ? &temp->left : &temp->right;\n\n        } else if (n->str.len != t->str.len) {\n\n            p = (n->str.len < t->str.len) ? &temp->left : &temp->right;\n\n        } else {\n            p = (ngx_memcmp(n->str.data, t->str.data, n->str.len) < 0)\n                 ? &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\nngx_str_node_t *\nngx_str_rbtree_lookup(ngx_rbtree_t *rbtree, ngx_str_t *val, uint32_t hash)\n{\n    ngx_int_t           rc;\n    ngx_str_node_t     *n;\n    ngx_rbtree_node_t  *node, *sentinel;\n\n    node = rbtree->root;\n    sentinel = rbtree->sentinel;\n\n    while (node != sentinel) {\n\n        n = (ngx_str_node_t *) node;\n\n        if (hash != node->key) {\n            node = (hash < node->key) ? node->left : node->right;\n            continue;\n        }\n\n        if (val->len != n->str.len) {\n            node = (val->len < n->str.len) ? node->left : node->right;\n            continue;\n        }\n\n        rc = ngx_memcmp(val->data, n->str.data, val->len);\n\n        if (rc < 0) {\n            node = node->left;\n            continue;\n        }\n\n        if (rc > 0) {\n            node = node->right;\n            continue;\n        }\n\n        return n;\n    }\n\n    return NULL;\n}\n\n\n/* ngx_sort() is implemented as insertion sort because we need stable sort */\n\nvoid\nngx_sort(void *base, size_t n, size_t size,\n    ngx_int_t (*cmp)(const void *, const void *))\n{\n    u_char  *p1, *p2, *p;\n\n    p = ngx_alloc(size, ngx_cycle->log);\n    if (p == NULL) {\n        return;\n    }\n\n    for (p1 = (u_char *) base + size;\n         p1 < (u_char *) base + n * size;\n         p1 += size)\n    {\n        ngx_memcpy(p, p1, size);\n\n        for (p2 = p1;\n             p2 > (u_char *) base && cmp(p2 - size, p) > 0;\n             p2 -= size)\n        {\n            ngx_memcpy(p2, p2 - size, size);\n        }\n\n        ngx_memcpy(p2, p, size);\n    }\n\n    ngx_free(p);\n}\n\n\nvoid\nngx_explicit_memzero(void *buf, size_t n)\n{\n    ngx_memzero(buf, n);\n    ngx_memory_barrier();\n}\n\n\n#if (NGX_MEMCPY_LIMIT)\n\nvoid *\nngx_memcpy(void *dst, const void *src, size_t n)\n{\n    if (n > NGX_MEMCPY_LIMIT) {\n        ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, \"memcpy %uz bytes\", n);\n        ngx_debug_point();\n    }\n\n    return memcpy(dst, src, n);\n}\n\n#endif\n"
  },
  {
    "path": "src/core/ngx_string.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_STRING_H_INCLUDED_\n#define _NGX_STRING_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\ntypedef struct {\n    size_t      len;\n    u_char     *data;\n} ngx_str_t;\n\n\ntypedef struct {\n    ngx_str_t   key;\n    ngx_str_t   value;\n} ngx_keyval_t;\n\n\ntypedef struct {\n    unsigned    len:28;\n\n    unsigned    valid:1;\n    unsigned    no_cacheable:1;\n    unsigned    not_found:1;\n    unsigned    escape:1;\n\n    u_char     *data;\n} ngx_variable_value_t;\n\n\n#define ngx_string(str)     { sizeof(str) - 1, (u_char *) str }\n#define ngx_null_string     { 0, NULL }\n#define ngx_str_set(str, text)                                               \\\n    (str)->len = sizeof(text) - 1; (str)->data = (u_char *) text\n#define ngx_str_null(str)   (str)->len = 0; (str)->data = NULL\n\n\n#define ngx_tolower(c)      (u_char) ((c >= 'A' && c <= 'Z') ? (c | 0x20) : c)\n#define ngx_toupper(c)      (u_char) ((c >= 'a' && c <= 'z') ? (c & ~0x20) : c)\n\nvoid ngx_strlow(u_char *dst, u_char *src, size_t n);\n\n\n#define ngx_strncmp(s1, s2, n)  strncmp((const char *) s1, (const char *) s2, n)\n\n\n/* msvc and icc7 compile strcmp() to inline loop */\n#define ngx_strcmp(s1, s2)  strcmp((const char *) s1, (const char *) s2)\n\n\n#define ngx_strstr(s1, s2)  strstr((const char *) s1, (const char *) s2)\n#define ngx_strlen(s)       strlen((const char *) s)\n\nsize_t ngx_strnlen(u_char *p, size_t n);\n\n#define ngx_strchr(s1, c)   strchr((const char *) s1, (int) c)\n\nstatic ngx_inline u_char *\nngx_strlchr(u_char *p, u_char *last, u_char c)\n{\n    while (p < last) {\n\n        if (*p == c) {\n            return p;\n        }\n\n        p++;\n    }\n\n    return NULL;\n}\n\n\n/*\n * msvc and icc7 compile memset() to the inline \"rep stos\"\n * while ZeroMemory() and bzero() are the calls.\n * icc7 may also inline several mov's of a zeroed register for small blocks.\n */\n#define ngx_memzero(buf, n)       (void) memset(buf, 0, n)\n#define ngx_memset(buf, c, n)     (void) memset(buf, c, n)\n\nvoid ngx_explicit_memzero(void *buf, size_t n);\n\n\n#if (NGX_MEMCPY_LIMIT)\n\nvoid *ngx_memcpy(void *dst, const void *src, size_t n);\n#define ngx_cpymem(dst, src, n)   (((u_char *) ngx_memcpy(dst, src, n)) + (n))\n\n#else\n\n/*\n * gcc3, msvc, and icc7 compile memcpy() to the inline \"rep movs\".\n * gcc3 compiles memcpy(d, s, 4) to the inline \"mov\"es.\n * icc8 compile memcpy(d, s, 4) to the inline \"mov\"es or XMM moves.\n */\n#define ngx_memcpy(dst, src, n)   (void) memcpy(dst, src, n)\n#define ngx_cpymem(dst, src, n)   (((u_char *) memcpy(dst, src, n)) + (n))\n\n#endif\n\n\n#if ( __INTEL_COMPILER >= 800 )\n\n/*\n * the simple inline cycle copies the variable length strings up to 16\n * bytes faster than icc8 autodetecting _intel_fast_memcpy()\n */\n\nstatic ngx_inline u_char *\nngx_copy(u_char *dst, u_char *src, size_t len)\n{\n    if (len < 17) {\n\n        while (len) {\n            *dst++ = *src++;\n            len--;\n        }\n\n        return dst;\n\n    } else {\n        return ngx_cpymem(dst, src, len);\n    }\n}\n\n#else\n\n#define ngx_copy                  ngx_cpymem\n\n#endif\n\n\n#define ngx_memmove(dst, src, n)  (void) memmove(dst, src, n)\n#define ngx_movemem(dst, src, n)  (((u_char *) memmove(dst, src, n)) + (n))\n\n\n/* msvc and icc7 compile memcmp() to the inline loop */\n#define ngx_memcmp(s1, s2, n)     memcmp(s1, s2, n)\n\n\nu_char *ngx_cpystrn(u_char *dst, u_char *src, size_t n);\nu_char *ngx_pstrdup(ngx_pool_t *pool, ngx_str_t *src);\nu_char * ngx_cdecl ngx_sprintf(u_char *buf, const char *fmt, ...);\nu_char * ngx_cdecl ngx_snprintf(u_char *buf, size_t max, const char *fmt, ...);\nu_char * ngx_cdecl ngx_slprintf(u_char *buf, u_char *last, const char *fmt,\n    ...);\nu_char *ngx_vslprintf(u_char *buf, u_char *last, const char *fmt, va_list args);\n#define ngx_vsnprintf(buf, max, fmt, args)                                   \\\n    ngx_vslprintf(buf, buf + (max), fmt, args)\n\nngx_int_t ngx_strcasecmp(u_char *s1, u_char *s2);\nngx_int_t ngx_strncasecmp(u_char *s1, u_char *s2, size_t n);\n\nu_char *ngx_strnstr(u_char *s1, char *s2, size_t n);\n\nu_char *ngx_strstrn(u_char *s1, char *s2, size_t n);\nu_char *ngx_strcasestrn(u_char *s1, char *s2, size_t n);\nu_char *ngx_strlcasestrn(u_char *s1, u_char *last, u_char *s2, size_t n);\n\nngx_int_t ngx_rstrncmp(u_char *s1, u_char *s2, size_t n);\nngx_int_t ngx_rstrncasecmp(u_char *s1, u_char *s2, size_t n);\nngx_int_t ngx_memn2cmp(u_char *s1, u_char *s2, size_t n1, size_t n2);\nngx_int_t ngx_dns_strcmp(u_char *s1, u_char *s2);\nngx_int_t ngx_filename_cmp(u_char *s1, u_char *s2, size_t n);\n\nngx_int_t ngx_atoi(u_char *line, size_t n);\n#if (T_NGX_HTTP_IMPROVED_IF)\nlong long ngx_atoll(u_char *line, size_t n);\n#endif\nngx_int_t ngx_atofp(u_char *line, size_t n, size_t point);\nssize_t ngx_atosz(u_char *line, size_t n);\noff_t ngx_atoof(u_char *line, size_t n);\ntime_t ngx_atotm(u_char *line, size_t n);\nngx_int_t ngx_hextoi(u_char *line, size_t n);\n\nu_char *ngx_hex_dump(u_char *dst, u_char *src, size_t len);\n\n\n#define ngx_base64_encoded_length(len)  (((len + 2) / 3) * 4)\n#define ngx_base64_decoded_length(len)  (((len + 3) / 4) * 3)\n\nvoid ngx_encode_base64(ngx_str_t *dst, ngx_str_t *src);\nvoid ngx_encode_base64url(ngx_str_t *dst, ngx_str_t *src);\nngx_int_t ngx_decode_base64(ngx_str_t *dst, ngx_str_t *src);\nngx_int_t ngx_decode_base64url(ngx_str_t *dst, ngx_str_t *src);\n\nuint32_t ngx_utf8_decode(u_char **p, size_t n);\nsize_t ngx_utf8_length(u_char *p, size_t n);\nu_char *ngx_utf8_cpystrn(u_char *dst, u_char *src, size_t n, size_t len);\n\n\n#define NGX_ESCAPE_URI            0\n#define NGX_ESCAPE_ARGS           1\n#define NGX_ESCAPE_URI_COMPONENT  2\n#define NGX_ESCAPE_HTML           3\n#define NGX_ESCAPE_REFRESH        4\n#define NGX_ESCAPE_MEMCACHED      5\n#define NGX_ESCAPE_MAIL_AUTH      6\n\n#define NGX_UNESCAPE_URI       1\n#define NGX_UNESCAPE_REDIRECT  2\n\nuintptr_t ngx_escape_uri(u_char *dst, u_char *src, size_t size,\n    ngx_uint_t type);\nvoid ngx_unescape_uri(u_char **dst, u_char **src, size_t size, ngx_uint_t type);\nuintptr_t ngx_escape_html(u_char *dst, u_char *src, size_t size);\nuintptr_t ngx_escape_json(u_char *dst, u_char *src, size_t size);\n\n\ntypedef struct {\n    ngx_rbtree_node_t         node;\n    ngx_str_t                 str;\n} ngx_str_node_t;\n\n\nvoid ngx_str_rbtree_insert_value(ngx_rbtree_node_t *temp,\n    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);\nngx_str_node_t *ngx_str_rbtree_lookup(ngx_rbtree_t *rbtree, ngx_str_t *name,\n    uint32_t hash);\n\n\nvoid ngx_sort(void *base, size_t n, size_t size,\n    ngx_int_t (*cmp)(const void *, const void *));\n#define ngx_qsort             qsort\n\n\n#define ngx_value_helper(n)   #n\n#define ngx_value(n)          ngx_value_helper(n)\n\n\n#endif /* _NGX_STRING_H_INCLUDED_ */\n"
  },
  {
    "path": "src/core/ngx_syslog.c",
    "content": "\n/*\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n\n\n#define NGX_SYSLOG_MAX_STR                                                    \\\n    NGX_MAX_ERROR_STR + sizeof(\"<255>Jan 01 00:00:00 \") - 1                   \\\n    + (NGX_MAXHOSTNAMELEN - 1) + 1 /* space */                                \\\n    + 32 /* tag */ + 2 /* colon, space */\n\n\nstatic char *ngx_syslog_parse_args(ngx_conf_t *cf, ngx_syslog_peer_t *peer);\nstatic ngx_int_t ngx_syslog_init_peer(ngx_syslog_peer_t *peer);\nstatic void ngx_syslog_cleanup(void *data);\nstatic u_char *ngx_syslog_log_error(ngx_log_t *log, u_char *buf, size_t len);\n\n\nstatic char  *facilities[] = {\n    \"kern\", \"user\", \"mail\", \"daemon\", \"auth\", \"intern\", \"lpr\", \"news\", \"uucp\",\n    \"clock\", \"authpriv\", \"ftp\", \"ntp\", \"audit\", \"alert\", \"cron\", \"local0\",\n    \"local1\", \"local2\", \"local3\", \"local4\", \"local5\", \"local6\", \"local7\",\n    NULL\n};\n\n/* note 'error/warn' like in nginx.conf, not 'err/warning' */\nstatic char  *severities[] = {\n    \"emerg\", \"alert\", \"crit\", \"error\", \"warn\", \"notice\", \"info\", \"debug\", NULL\n};\n\nstatic ngx_log_t    ngx_syslog_dummy_log;\nstatic ngx_event_t  ngx_syslog_dummy_event;\n\n\nchar *\nngx_syslog_process_conf(ngx_conf_t *cf, ngx_syslog_peer_t *peer)\n{\n    ngx_pool_cleanup_t  *cln;\n\n    peer->facility = NGX_CONF_UNSET_UINT;\n    peer->severity = NGX_CONF_UNSET_UINT;\n\n    if (ngx_syslog_parse_args(cf, peer) != NGX_CONF_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (peer->server.sockaddr == NULL) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"no syslog server specified\");\n        return NGX_CONF_ERROR;\n    }\n\n    if (peer->facility == NGX_CONF_UNSET_UINT) {\n        peer->facility = 23; /* local7 */\n    }\n\n    if (peer->severity == NGX_CONF_UNSET_UINT) {\n        peer->severity = 6; /* info */\n    }\n\n    if (peer->tag.data == NULL) {\n        ngx_str_set(&peer->tag, \"nginx\");\n    }\n\n    peer->hostname = &cf->cycle->hostname;\n    peer->logp = &cf->cycle->new_log;\n\n    peer->conn.fd = (ngx_socket_t) -1;\n\n    peer->conn.read = &ngx_syslog_dummy_event;\n    peer->conn.write = &ngx_syslog_dummy_event;\n\n    ngx_syslog_dummy_event.log = &ngx_syslog_dummy_log;\n\n    cln = ngx_pool_cleanup_add(cf->pool, 0);\n    if (cln == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    cln->data = peer;\n    cln->handler = ngx_syslog_cleanup;\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_syslog_parse_args(ngx_conf_t *cf, ngx_syslog_peer_t *peer)\n{\n    u_char      *p, *comma, c;\n    size_t       len;\n    ngx_str_t   *value;\n    ngx_url_t    u;\n    ngx_uint_t   i;\n\n    value = cf->args->elts;\n\n    p = value[1].data + sizeof(\"syslog:\") - 1;\n\n    for ( ;; ) {\n        comma = (u_char *) ngx_strchr(p, ',');\n\n        if (comma != NULL) {\n            len = comma - p;\n            *comma = '\\0';\n\n        } else {\n            len = value[1].data + value[1].len - p;\n        }\n\n        if (ngx_strncmp(p, \"server=\", 7) == 0) {\n\n            if (peer->server.sockaddr != NULL) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"duplicate syslog \\\"server\\\"\");\n                return NGX_CONF_ERROR;\n            }\n\n            ngx_memzero(&u, sizeof(ngx_url_t));\n\n            u.url.data = p + 7;\n            u.url.len = len - 7;\n            u.default_port = 514;\n\n            if (ngx_parse_url(cf->pool, &u) != NGX_OK) {\n                if (u.err) {\n                    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                       \"%s in syslog server \\\"%V\\\"\",\n                                       u.err, &u.url);\n                }\n\n                return NGX_CONF_ERROR;\n            }\n\n            peer->server = u.addrs[0];\n\n        } else if (ngx_strncmp(p, \"facility=\", 9) == 0) {\n\n            if (peer->facility != NGX_CONF_UNSET_UINT) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"duplicate syslog \\\"facility\\\"\");\n                return NGX_CONF_ERROR;\n            }\n\n            for (i = 0; facilities[i] != NULL; i++) {\n\n                if (ngx_strcmp(p + 9, facilities[i]) == 0) {\n                    peer->facility = i;\n                    goto next;\n                }\n            }\n\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"unknown syslog facility \\\"%s\\\"\", p + 9);\n            return NGX_CONF_ERROR;\n\n        } else if (ngx_strncmp(p, \"severity=\", 9) == 0) {\n\n            if (peer->severity != NGX_CONF_UNSET_UINT) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"duplicate syslog \\\"severity\\\"\");\n                return NGX_CONF_ERROR;\n            }\n\n            for (i = 0; severities[i] != NULL; i++) {\n\n                if (ngx_strcmp(p + 9, severities[i]) == 0) {\n                    peer->severity = i;\n                    goto next;\n                }\n            }\n\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"unknown syslog severity \\\"%s\\\"\", p + 9);\n            return NGX_CONF_ERROR;\n\n        } else if (ngx_strncmp(p, \"tag=\", 4) == 0) {\n\n            if (peer->tag.data != NULL) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"duplicate syslog \\\"tag\\\"\");\n                return NGX_CONF_ERROR;\n            }\n\n            /*\n             * RFC 3164: the TAG is a string of ABNF alphanumeric characters\n             * that MUST NOT exceed 32 characters.\n             */\n            if (len - 4 > 32) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"syslog tag length exceeds 32\");\n                return NGX_CONF_ERROR;\n            }\n\n            for (i = 4; i < len; i++) {\n                c = ngx_tolower(p[i]);\n\n                if (c < '0' || (c > '9' && c < 'a' && c != '_') || c > 'z') {\n                    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                       \"syslog \\\"tag\\\" only allows \"\n                                       \"alphanumeric characters \"\n                                       \"and underscore\");\n                    return NGX_CONF_ERROR;\n                }\n            }\n\n            peer->tag.data = p + 4;\n            peer->tag.len = len - 4;\n\n        } else if (len == 10 && ngx_strncmp(p, \"nohostname\", 10) == 0) {\n            peer->nohostname = 1;\n\n        } else {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"unknown syslog parameter \\\"%s\\\"\", p);\n            return NGX_CONF_ERROR;\n        }\n\n    next:\n\n        if (comma == NULL) {\n            break;\n        }\n\n        p = comma + 1;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nu_char *\nngx_syslog_add_header(ngx_syslog_peer_t *peer, u_char *buf)\n{\n    ngx_uint_t  pri;\n\n    pri = peer->facility * 8 + peer->severity;\n\n    if (peer->nohostname) {\n        return ngx_sprintf(buf, \"<%ui>%V %V: \", pri, &ngx_cached_syslog_time,\n                           &peer->tag);\n    }\n\n    return ngx_sprintf(buf, \"<%ui>%V %V %V: \", pri, &ngx_cached_syslog_time,\n                       peer->hostname, &peer->tag);\n}\n\n\nvoid\nngx_syslog_writer(ngx_log_t *log, ngx_uint_t level, u_char *buf,\n    size_t len)\n{\n    u_char             *p, msg[NGX_SYSLOG_MAX_STR];\n    ngx_uint_t          head_len;\n    ngx_syslog_peer_t  *peer;\n\n    peer = log->wdata;\n\n    if (peer->busy) {\n        return;\n    }\n\n    peer->busy = 1;\n    peer->severity = level - 1;\n\n    p = ngx_syslog_add_header(peer, msg);\n    head_len = p - msg;\n\n    len -= NGX_LINEFEED_SIZE;\n\n    if (len > NGX_SYSLOG_MAX_STR - head_len) {\n        len = NGX_SYSLOG_MAX_STR - head_len;\n    }\n\n    p = ngx_snprintf(p, len, \"%s\", buf);\n\n    (void) ngx_syslog_send(peer, msg, p - msg);\n\n    peer->busy = 0;\n}\n\n\nssize_t\nngx_syslog_send(ngx_syslog_peer_t *peer, u_char *buf, size_t len)\n{\n    ssize_t  n;\n\n    if (peer->log.handler == NULL) {\n        peer->log = *peer->logp;\n        peer->log.handler = ngx_syslog_log_error;\n        peer->log.data = peer;\n        peer->log.action = \"logging to syslog\";\n    }\n\n    if (peer->conn.fd == (ngx_socket_t) -1) {\n        if (ngx_syslog_init_peer(peer) != NGX_OK) {\n            return NGX_ERROR;\n        }\n    }\n\n    if (ngx_send) {\n        n = ngx_send(&peer->conn, buf, len);\n\n    } else {\n        /* event module has not yet set ngx_io */\n        n = ngx_os_io.send(&peer->conn, buf, len);\n    }\n\n    if (n == NGX_ERROR) {\n\n        if (ngx_close_socket(peer->conn.fd) == -1) {\n            ngx_log_error(NGX_LOG_ALERT, &peer->log, ngx_socket_errno,\n                          ngx_close_socket_n \" failed\");\n        }\n\n        peer->conn.fd = (ngx_socket_t) -1;\n    }\n\n    return n;\n}\n\n\nstatic ngx_int_t\nngx_syslog_init_peer(ngx_syslog_peer_t *peer)\n{\n    ngx_socket_t  fd;\n\n    fd = ngx_socket(peer->server.sockaddr->sa_family, SOCK_DGRAM, 0);\n    if (fd == (ngx_socket_t) -1) {\n        ngx_log_error(NGX_LOG_ALERT, &peer->log, ngx_socket_errno,\n                      ngx_socket_n \" failed\");\n        return NGX_ERROR;\n    }\n\n    if (ngx_nonblocking(fd) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, &peer->log, ngx_socket_errno,\n                      ngx_nonblocking_n \" failed\");\n        goto failed;\n    }\n\n    if (connect(fd, peer->server.sockaddr, peer->server.socklen) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, &peer->log, ngx_socket_errno,\n                      \"connect() failed\");\n        goto failed;\n    }\n\n    peer->conn.fd = fd;\n    peer->conn.log = &peer->log;\n\n    /* UDP sockets are always ready to write */\n    peer->conn.write->ready = 1;\n\n    return NGX_OK;\n\nfailed:\n\n    if (ngx_close_socket(fd) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, &peer->log, ngx_socket_errno,\n                      ngx_close_socket_n \" failed\");\n    }\n\n    return NGX_ERROR;\n}\n\n\nstatic void\nngx_syslog_cleanup(void *data)\n{\n    ngx_syslog_peer_t  *peer = data;\n\n    /* prevents further use of this peer */\n    peer->busy = 1;\n\n    if (peer->conn.fd == (ngx_socket_t) -1) {\n        return;\n    }\n\n    if (ngx_close_socket(peer->conn.fd) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, &peer->log, ngx_socket_errno,\n                      ngx_close_socket_n \" failed\");\n    }\n}\n\n\nstatic u_char *\nngx_syslog_log_error(ngx_log_t *log, u_char *buf, size_t len)\n{\n    u_char             *p;\n    ngx_syslog_peer_t  *peer;\n\n    p = buf;\n\n    if (log->action) {\n        p = ngx_snprintf(buf, len, \" while %s\", log->action);\n        len -= p - buf;\n    }\n\n    peer = log->data;\n\n    if (peer) {\n        p = ngx_snprintf(p, len, \", server: %V\", &peer->server.name);\n    }\n\n    return p;\n}\n"
  },
  {
    "path": "src/core/ngx_syslog.h",
    "content": "\n/*\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_SYSLOG_H_INCLUDED_\n#define _NGX_SYSLOG_H_INCLUDED_\n\n\ntypedef struct {\n    ngx_uint_t         facility;\n    ngx_uint_t         severity;\n    ngx_str_t          tag;\n\n    ngx_str_t         *hostname;\n\n    ngx_addr_t         server;\n    ngx_connection_t   conn;\n\n    ngx_log_t          log;\n    ngx_log_t         *logp;\n\n    unsigned           busy:1;\n    unsigned           nohostname:1;\n} ngx_syslog_peer_t;\n\n\nchar *ngx_syslog_process_conf(ngx_conf_t *cf, ngx_syslog_peer_t *peer);\nu_char *ngx_syslog_add_header(ngx_syslog_peer_t *peer, u_char *buf);\nvoid ngx_syslog_writer(ngx_log_t *log, ngx_uint_t level, u_char *buf,\n    size_t len);\nssize_t ngx_syslog_send(ngx_syslog_peer_t *peer, u_char *buf, size_t len);\n\n\n#endif /* _NGX_SYSLOG_H_INCLUDED_ */\n"
  },
  {
    "path": "src/core/ngx_thread_pool.c",
    "content": "\n/*\n * Copyright (C) Nginx, Inc.\n * Copyright (C) Valentin V. Bartenev\n * Copyright (C) Ruslan Ermilov\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_thread_pool.h>\n\n\ntypedef struct {\n    ngx_array_t               pools;\n} ngx_thread_pool_conf_t;\n\n\ntypedef struct {\n    ngx_thread_task_t        *first;\n    ngx_thread_task_t       **last;\n} ngx_thread_pool_queue_t;\n\n#define ngx_thread_pool_queue_init(q)                                         \\\n    (q)->first = NULL;                                                        \\\n    (q)->last = &(q)->first\n\n\nstruct ngx_thread_pool_s {\n    ngx_thread_mutex_t        mtx;\n    ngx_thread_pool_queue_t   queue;\n    ngx_int_t                 waiting;\n    ngx_thread_cond_t         cond;\n\n    ngx_log_t                *log;\n\n    ngx_str_t                 name;\n    ngx_uint_t                threads;\n    ngx_int_t                 max_queue;\n\n    u_char                   *file;\n    ngx_uint_t                line;\n};\n\n\nstatic ngx_int_t ngx_thread_pool_init(ngx_thread_pool_t *tp, ngx_log_t *log,\n    ngx_pool_t *pool);\nstatic void ngx_thread_pool_destroy(ngx_thread_pool_t *tp);\nstatic void ngx_thread_pool_exit_handler(void *data, ngx_log_t *log);\n\nstatic void *ngx_thread_pool_cycle(void *data);\nstatic void ngx_thread_pool_handler(ngx_event_t *ev);\n\nstatic char *ngx_thread_pool(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\n\nstatic void *ngx_thread_pool_create_conf(ngx_cycle_t *cycle);\nstatic char *ngx_thread_pool_init_conf(ngx_cycle_t *cycle, void *conf);\n\nstatic ngx_int_t ngx_thread_pool_init_worker(ngx_cycle_t *cycle);\nstatic void ngx_thread_pool_exit_worker(ngx_cycle_t *cycle);\n\n\nstatic ngx_command_t  ngx_thread_pool_commands[] = {\n\n    { ngx_string(\"thread_pool\"),\n      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE23,\n      ngx_thread_pool,\n      0,\n      0,\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_core_module_t  ngx_thread_pool_module_ctx = {\n    ngx_string(\"thread_pool\"),\n    ngx_thread_pool_create_conf,\n    ngx_thread_pool_init_conf\n};\n\n\nngx_module_t  ngx_thread_pool_module = {\n    NGX_MODULE_V1,\n    &ngx_thread_pool_module_ctx,           /* module context */\n    ngx_thread_pool_commands,              /* module directives */\n    NGX_CORE_MODULE,                       /* module type */\n    NULL,                                  /* init master */\n    NULL,                                  /* init module */\n    ngx_thread_pool_init_worker,           /* init process */\n    NULL,                                  /* init thread */\n    NULL,                                  /* exit thread */\n    ngx_thread_pool_exit_worker,           /* exit process */\n    NULL,                                  /* exit master */\n    NGX_MODULE_V1_PADDING\n};\n\n\nstatic ngx_str_t  ngx_thread_pool_default = ngx_string(\"default\");\n\nstatic ngx_uint_t               ngx_thread_pool_task_id;\nstatic ngx_atomic_t             ngx_thread_pool_done_lock;\nstatic ngx_thread_pool_queue_t  ngx_thread_pool_done;\n\n\nstatic ngx_int_t\nngx_thread_pool_init(ngx_thread_pool_t *tp, ngx_log_t *log, ngx_pool_t *pool)\n{\n    int             err;\n    pthread_t       tid;\n    ngx_uint_t      n;\n    pthread_attr_t  attr;\n\n    if (ngx_notify == NULL) {\n        ngx_log_error(NGX_LOG_ALERT, log, 0,\n               \"the configured event method cannot be used with thread pools\");\n        return NGX_ERROR;\n    }\n\n    ngx_thread_pool_queue_init(&tp->queue);\n\n    if (ngx_thread_mutex_create(&tp->mtx, log) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    if (ngx_thread_cond_create(&tp->cond, log) != NGX_OK) {\n        (void) ngx_thread_mutex_destroy(&tp->mtx, log);\n        return NGX_ERROR;\n    }\n\n    tp->log = log;\n\n    err = pthread_attr_init(&attr);\n    if (err) {\n        ngx_log_error(NGX_LOG_ALERT, log, err,\n                      \"pthread_attr_init() failed\");\n        return NGX_ERROR;\n    }\n\n    err = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);\n    if (err) {\n        ngx_log_error(NGX_LOG_ALERT, log, err,\n                      \"pthread_attr_setdetachstate() failed\");\n        return NGX_ERROR;\n    }\n\n#if 0\n    err = pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN);\n    if (err) {\n        ngx_log_error(NGX_LOG_ALERT, log, err,\n                      \"pthread_attr_setstacksize() failed\");\n        return NGX_ERROR;\n    }\n#endif\n\n    for (n = 0; n < tp->threads; n++) {\n        err = pthread_create(&tid, &attr, ngx_thread_pool_cycle, tp);\n        if (err) {\n            ngx_log_error(NGX_LOG_ALERT, log, err,\n                          \"pthread_create() failed\");\n            return NGX_ERROR;\n        }\n    }\n\n    (void) pthread_attr_destroy(&attr);\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_thread_pool_destroy(ngx_thread_pool_t *tp)\n{\n    ngx_uint_t           n;\n    ngx_thread_task_t    task;\n    volatile ngx_uint_t  lock;\n\n    ngx_memzero(&task, sizeof(ngx_thread_task_t));\n\n    task.handler = ngx_thread_pool_exit_handler;\n    task.ctx = (void *) &lock;\n\n    for (n = 0; n < tp->threads; n++) {\n        lock = 1;\n\n        if (ngx_thread_task_post(tp, &task) != NGX_OK) {\n            return;\n        }\n\n        while (lock) {\n            ngx_sched_yield();\n        }\n\n        task.event.active = 0;\n    }\n\n    (void) ngx_thread_cond_destroy(&tp->cond, tp->log);\n\n    (void) ngx_thread_mutex_destroy(&tp->mtx, tp->log);\n}\n\n\nstatic void\nngx_thread_pool_exit_handler(void *data, ngx_log_t *log)\n{\n    ngx_uint_t *lock = data;\n\n    *lock = 0;\n\n    pthread_exit(0);\n}\n\n\nngx_thread_task_t *\nngx_thread_task_alloc(ngx_pool_t *pool, size_t size)\n{\n    ngx_thread_task_t  *task;\n\n    task = ngx_pcalloc(pool, sizeof(ngx_thread_task_t) + size);\n    if (task == NULL) {\n        return NULL;\n    }\n\n    task->ctx = task + 1;\n\n    return task;\n}\n\n\nngx_int_t\nngx_thread_task_post(ngx_thread_pool_t *tp, ngx_thread_task_t *task)\n{\n    if (task->event.active) {\n        ngx_log_error(NGX_LOG_ALERT, tp->log, 0,\n                      \"task #%ui already active\", task->id);\n        return NGX_ERROR;\n    }\n\n    if (ngx_thread_mutex_lock(&tp->mtx, tp->log) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    if (tp->waiting >= tp->max_queue) {\n        (void) ngx_thread_mutex_unlock(&tp->mtx, tp->log);\n\n        ngx_log_error(NGX_LOG_ERR, tp->log, 0,\n                      \"thread pool \\\"%V\\\" queue overflow: %i tasks waiting\",\n                      &tp->name, tp->waiting);\n        return NGX_ERROR;\n    }\n\n    task->event.active = 1;\n\n    task->id = ngx_thread_pool_task_id++;\n    task->next = NULL;\n\n    if (ngx_thread_cond_signal(&tp->cond, tp->log) != NGX_OK) {\n        (void) ngx_thread_mutex_unlock(&tp->mtx, tp->log);\n        return NGX_ERROR;\n    }\n\n    *tp->queue.last = task;\n    tp->queue.last = &task->next;\n\n    tp->waiting++;\n\n    (void) ngx_thread_mutex_unlock(&tp->mtx, tp->log);\n\n    ngx_log_debug2(NGX_LOG_DEBUG_CORE, tp->log, 0,\n                   \"task #%ui added to thread pool \\\"%V\\\"\",\n                   task->id, &tp->name);\n\n    return NGX_OK;\n}\n\n\nstatic void *\nngx_thread_pool_cycle(void *data)\n{\n    ngx_thread_pool_t *tp = data;\n\n    int                 err;\n    sigset_t            set;\n    ngx_thread_task_t  *task;\n\n#if 0\n    ngx_time_update();\n#endif\n\n    ngx_log_debug1(NGX_LOG_DEBUG_CORE, tp->log, 0,\n                   \"thread in pool \\\"%V\\\" started\", &tp->name);\n\n    sigfillset(&set);\n\n    sigdelset(&set, SIGILL);\n    sigdelset(&set, SIGFPE);\n    sigdelset(&set, SIGSEGV);\n    sigdelset(&set, SIGBUS);\n\n    err = pthread_sigmask(SIG_BLOCK, &set, NULL);\n    if (err) {\n        ngx_log_error(NGX_LOG_ALERT, tp->log, err, \"pthread_sigmask() failed\");\n        return NULL;\n    }\n\n    for ( ;; ) {\n        if (ngx_thread_mutex_lock(&tp->mtx, tp->log) != NGX_OK) {\n            return NULL;\n        }\n\n        /* the number may become negative */\n        tp->waiting--;\n\n        while (tp->queue.first == NULL) {\n            if (ngx_thread_cond_wait(&tp->cond, &tp->mtx, tp->log)\n                != NGX_OK)\n            {\n                (void) ngx_thread_mutex_unlock(&tp->mtx, tp->log);\n                return NULL;\n            }\n        }\n\n        task = tp->queue.first;\n        tp->queue.first = task->next;\n\n        if (tp->queue.first == NULL) {\n            tp->queue.last = &tp->queue.first;\n        }\n\n        if (ngx_thread_mutex_unlock(&tp->mtx, tp->log) != NGX_OK) {\n            return NULL;\n        }\n\n#if 0\n        ngx_time_update();\n#endif\n\n        ngx_log_debug2(NGX_LOG_DEBUG_CORE, tp->log, 0,\n                       \"run task #%ui in thread pool \\\"%V\\\"\",\n                       task->id, &tp->name);\n\n        task->handler(task->ctx, tp->log);\n\n        ngx_log_debug2(NGX_LOG_DEBUG_CORE, tp->log, 0,\n                       \"complete task #%ui in thread pool \\\"%V\\\"\",\n                       task->id, &tp->name);\n\n        task->next = NULL;\n\n        ngx_spinlock(&ngx_thread_pool_done_lock, 1, 2048);\n\n        *ngx_thread_pool_done.last = task;\n        ngx_thread_pool_done.last = &task->next;\n\n        ngx_memory_barrier();\n\n        ngx_unlock(&ngx_thread_pool_done_lock);\n\n        (void) ngx_notify(ngx_thread_pool_handler);\n    }\n}\n\n\nstatic void\nngx_thread_pool_handler(ngx_event_t *ev)\n{\n    ngx_event_t        *event;\n    ngx_thread_task_t  *task;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_CORE, ev->log, 0, \"thread pool handler\");\n\n    ngx_spinlock(&ngx_thread_pool_done_lock, 1, 2048);\n\n    task = ngx_thread_pool_done.first;\n    ngx_thread_pool_done.first = NULL;\n    ngx_thread_pool_done.last = &ngx_thread_pool_done.first;\n\n    ngx_memory_barrier();\n\n    ngx_unlock(&ngx_thread_pool_done_lock);\n\n    while (task) {\n        ngx_log_debug1(NGX_LOG_DEBUG_CORE, ev->log, 0,\n                       \"run completion handler for task #%ui\", task->id);\n\n        event = &task->event;\n        task = task->next;\n\n        event->complete = 1;\n        event->active = 0;\n\n        event->handler(event);\n    }\n}\n\n\nstatic void *\nngx_thread_pool_create_conf(ngx_cycle_t *cycle)\n{\n    ngx_thread_pool_conf_t  *tcf;\n\n    tcf = ngx_pcalloc(cycle->pool, sizeof(ngx_thread_pool_conf_t));\n    if (tcf == NULL) {\n        return NULL;\n    }\n\n    if (ngx_array_init(&tcf->pools, cycle->pool, 4,\n                       sizeof(ngx_thread_pool_t *))\n        != NGX_OK)\n    {\n        return NULL;\n    }\n\n    return tcf;\n}\n\n\nstatic char *\nngx_thread_pool_init_conf(ngx_cycle_t *cycle, void *conf)\n{\n    ngx_thread_pool_conf_t *tcf = conf;\n\n    ngx_uint_t           i;\n    ngx_thread_pool_t  **tpp;\n\n    tpp = tcf->pools.elts;\n\n    for (i = 0; i < tcf->pools.nelts; i++) {\n\n        if (tpp[i]->threads) {\n            continue;\n        }\n\n        if (tpp[i]->name.len == ngx_thread_pool_default.len\n            && ngx_strncmp(tpp[i]->name.data, ngx_thread_pool_default.data,\n                           ngx_thread_pool_default.len)\n               == 0)\n        {\n            tpp[i]->threads = 32;\n            tpp[i]->max_queue = 65536;\n            continue;\n        }\n\n        ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,\n                      \"unknown thread pool \\\"%V\\\" in %s:%ui\",\n                      &tpp[i]->name, tpp[i]->file, tpp[i]->line);\n\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_thread_pool(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_str_t          *value;\n    ngx_uint_t          i;\n    ngx_thread_pool_t  *tp;\n\n    value = cf->args->elts;\n\n    tp = ngx_thread_pool_add(cf, &value[1]);\n\n    if (tp == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (tp->threads) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"duplicate thread pool \\\"%V\\\"\", &tp->name);\n        return NGX_CONF_ERROR;\n    }\n\n    tp->max_queue = 65536;\n\n    for (i = 2; i < cf->args->nelts; i++) {\n\n        if (ngx_strncmp(value[i].data, \"threads=\", 8) == 0) {\n\n            tp->threads = ngx_atoi(value[i].data + 8, value[i].len - 8);\n\n            if (tp->threads == (ngx_uint_t) NGX_ERROR || tp->threads == 0) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"invalid threads value \\\"%V\\\"\", &value[i]);\n                return NGX_CONF_ERROR;\n            }\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"max_queue=\", 10) == 0) {\n\n            tp->max_queue = ngx_atoi(value[i].data + 10, value[i].len - 10);\n\n            if (tp->max_queue == NGX_ERROR) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"invalid max_queue value \\\"%V\\\"\", &value[i]);\n                return NGX_CONF_ERROR;\n            }\n\n            continue;\n        }\n    }\n\n    if (tp->threads == 0) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"\\\"%V\\\" must have \\\"threads\\\" parameter\",\n                           &cmd->name);\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nngx_thread_pool_t *\nngx_thread_pool_add(ngx_conf_t *cf, ngx_str_t *name)\n{\n    ngx_thread_pool_t       *tp, **tpp;\n    ngx_thread_pool_conf_t  *tcf;\n\n    if (name == NULL) {\n        name = &ngx_thread_pool_default;\n    }\n\n    tp = ngx_thread_pool_get(cf->cycle, name);\n\n    if (tp) {\n        return tp;\n    }\n\n    tp = ngx_pcalloc(cf->pool, sizeof(ngx_thread_pool_t));\n    if (tp == NULL) {\n        return NULL;\n    }\n\n    tp->name = *name;\n    tp->file = cf->conf_file->file.name.data;\n    tp->line = cf->conf_file->line;\n\n    tcf = (ngx_thread_pool_conf_t *) ngx_get_conf(cf->cycle->conf_ctx,\n                                                  ngx_thread_pool_module);\n\n    tpp = ngx_array_push(&tcf->pools);\n    if (tpp == NULL) {\n        return NULL;\n    }\n\n    *tpp = tp;\n\n    return tp;\n}\n\n\nngx_thread_pool_t *\nngx_thread_pool_get(ngx_cycle_t *cycle, ngx_str_t *name)\n{\n    ngx_uint_t                i;\n    ngx_thread_pool_t       **tpp;\n    ngx_thread_pool_conf_t   *tcf;\n\n    tcf = (ngx_thread_pool_conf_t *) ngx_get_conf(cycle->conf_ctx,\n                                                  ngx_thread_pool_module);\n\n    tpp = tcf->pools.elts;\n\n    for (i = 0; i < tcf->pools.nelts; i++) {\n\n        if (tpp[i]->name.len == name->len\n            && ngx_strncmp(tpp[i]->name.data, name->data, name->len) == 0)\n        {\n            return tpp[i];\n        }\n    }\n\n    return NULL;\n}\n\n\nstatic ngx_int_t\nngx_thread_pool_init_worker(ngx_cycle_t *cycle)\n{\n    ngx_uint_t                i;\n    ngx_thread_pool_t       **tpp;\n    ngx_thread_pool_conf_t   *tcf;\n\n    if (ngx_process != NGX_PROCESS_WORKER\n        && ngx_process != NGX_PROCESS_SINGLE)\n    {\n        return NGX_OK;\n    }\n\n    tcf = (ngx_thread_pool_conf_t *) ngx_get_conf(cycle->conf_ctx,\n                                                  ngx_thread_pool_module);\n\n    if (tcf == NULL) {\n        return NGX_OK;\n    }\n\n    ngx_thread_pool_queue_init(&ngx_thread_pool_done);\n\n    tpp = tcf->pools.elts;\n\n    for (i = 0; i < tcf->pools.nelts; i++) {\n        if (ngx_thread_pool_init(tpp[i], cycle->log, cycle->pool) != NGX_OK) {\n            return NGX_ERROR;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_thread_pool_exit_worker(ngx_cycle_t *cycle)\n{\n    ngx_uint_t                i;\n    ngx_thread_pool_t       **tpp;\n    ngx_thread_pool_conf_t   *tcf;\n\n    if (ngx_process != NGX_PROCESS_WORKER\n        && ngx_process != NGX_PROCESS_SINGLE)\n    {\n        return;\n    }\n\n    tcf = (ngx_thread_pool_conf_t *) ngx_get_conf(cycle->conf_ctx,\n                                                  ngx_thread_pool_module);\n\n    if (tcf == NULL) {\n        return;\n    }\n\n    tpp = tcf->pools.elts;\n\n    for (i = 0; i < tcf->pools.nelts; i++) {\n        ngx_thread_pool_destroy(tpp[i]);\n    }\n}\n"
  },
  {
    "path": "src/core/ngx_thread_pool.h",
    "content": "\n/*\n * Copyright (C) Nginx, Inc.\n * Copyright (C) Valentin V. Bartenev\n */\n\n\n#ifndef _NGX_THREAD_POOL_H_INCLUDED_\n#define _NGX_THREAD_POOL_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n\n\nstruct ngx_thread_task_s {\n    ngx_thread_task_t   *next;\n    ngx_uint_t           id;\n    void                *ctx;\n    void               (*handler)(void *data, ngx_log_t *log);\n    ngx_event_t          event;\n};\n\n\ntypedef struct ngx_thread_pool_s  ngx_thread_pool_t;\n\n\nngx_thread_pool_t *ngx_thread_pool_add(ngx_conf_t *cf, ngx_str_t *name);\nngx_thread_pool_t *ngx_thread_pool_get(ngx_cycle_t *cycle, ngx_str_t *name);\n\nngx_thread_task_t *ngx_thread_task_alloc(ngx_pool_t *pool, size_t size);\nngx_int_t ngx_thread_task_post(ngx_thread_pool_t *tp, ngx_thread_task_t *task);\n\n\n#endif /* _NGX_THREAD_POOL_H_INCLUDED_ */\n"
  },
  {
    "path": "src/core/ngx_times.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\nstatic ngx_msec_t ngx_monotonic_time(time_t sec, ngx_uint_t msec);\n\n\n/*\n * The time may be updated by signal handler or by several threads.\n * The time update operations are rare and require to hold the ngx_time_lock.\n * The time read operations are frequent, so they are lock-free and get time\n * values and strings from the current slot.  Thus thread may get the corrupted\n * values only if it is preempted while copying and then it is not scheduled\n * to run more than NGX_TIME_SLOTS seconds.\n */\n\n#define NGX_TIME_SLOTS   64\n\nstatic ngx_uint_t        slot;\nstatic ngx_atomic_t      ngx_time_lock;\n\nvolatile ngx_msec_t      ngx_current_msec;\nvolatile ngx_time_t     *ngx_cached_time;\nvolatile ngx_str_t       ngx_cached_err_log_time;\n\n#if (T_NGX_XQUIC)\nvolatile ngx_str_t       ngx_cached_xquic_log_time;\n#endif\n\nvolatile ngx_str_t       ngx_cached_http_time;\nvolatile ngx_str_t       ngx_cached_http_log_time;\nvolatile ngx_str_t       ngx_cached_http_log_iso8601;\nvolatile ngx_str_t       ngx_cached_syslog_time;\n#if (T_NGX_RET_CACHE)\nvolatile ngx_tm_t       *ngx_cached_tm;\n#endif\n\n#if !(NGX_WIN32)\n\n/*\n * localtime() and localtime_r() are not Async-Signal-Safe functions, therefore,\n * they must not be called by a signal handler, so we use the cached\n * GMT offset value. Fortunately the value is changed only two times a year.\n */\n\nstatic ngx_int_t         cached_gmtoff;\n#endif\n\n#if (T_NGX_RET_CACHE)\nstatic ngx_tm_t          cached_http_log_tm[NGX_TIME_SLOTS];\n#endif\nstatic ngx_time_t        cached_time[NGX_TIME_SLOTS];\nstatic u_char            cached_err_log_time[NGX_TIME_SLOTS]\n                                    [sizeof(\"1970/09/28 12:00:00\")];\n#if (T_NGX_XQUIC)\nstatic u_char            cached_xquic_log_time[NGX_TIME_SLOTS]\n                                    [sizeof(\"1970/09/28 12:00:00.000|1426141364883\")];\n#endif\nstatic u_char            cached_http_time[NGX_TIME_SLOTS]\n                                    [sizeof(\"Mon, 28 Sep 1970 06:00:00 GMT\")];\nstatic u_char            cached_http_log_time[NGX_TIME_SLOTS]\n                                    [sizeof(\"28/Sep/1970:12:00:00 +0600\")];\nstatic u_char            cached_http_log_iso8601[NGX_TIME_SLOTS]\n                                    [sizeof(\"1970-09-28T12:00:00+06:00\")];\nstatic u_char            cached_syslog_time[NGX_TIME_SLOTS]\n                                    [sizeof(\"Sep 28 12:00:00\")];\n\n\nstatic char  *week[] = { \"Sun\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\" };\nstatic char  *months[] = { \"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\",\n                           \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\" };\n\nvoid\nngx_time_init(void)\n{\n    ngx_cached_err_log_time.len = sizeof(\"1970/09/28 12:00:00\") - 1;\n#if (T_NGX_XQUIC)\n    ngx_cached_xquic_log_time.len = sizeof(\"1970/09/28 12:00:00.000|1426141364883\") -1;\n#endif\n    ngx_cached_http_time.len = sizeof(\"Mon, 28 Sep 1970 06:00:00 GMT\") - 1;\n    ngx_cached_http_log_time.len = sizeof(\"28/Sep/1970:12:00:00 +0600\") - 1;\n    ngx_cached_http_log_iso8601.len = sizeof(\"1970-09-28T12:00:00+06:00\") - 1;\n    ngx_cached_syslog_time.len = sizeof(\"Sep 28 12:00:00\") - 1;\n\n    ngx_cached_time = &cached_time[0];\n#if (T_NGX_RET_CACHE)\n    ngx_cached_tm = &cached_http_log_tm[0];\n#endif\n\n    ngx_time_update();\n}\n\n\nvoid\nngx_time_update(void)\n{\n    u_char          *p0, *p1, *p2, *p3, *p4;\n#if (T_NGX_XQUIC)\n    u_char          *p5;\n#endif\n    ngx_tm_t         tm, gmt;\n    time_t           sec;\n    ngx_uint_t       msec;\n    ngx_time_t      *tp;\n    struct timeval   tv;\n#if (T_NGX_RET_CACHE)\n    ngx_uint_t       usec;\n#endif\n\n    if (!ngx_trylock(&ngx_time_lock)) {\n        return;\n    }\n\n    ngx_gettimeofday(&tv);\n\n    sec = tv.tv_sec;\n    msec = tv.tv_usec / 1000;\n#if (T_NGX_RET_CACHE)\n    usec = tv.tv_usec % 1000;\n#endif\n\n    ngx_current_msec = ngx_monotonic_time(sec, msec);\n\n    tp = &cached_time[slot];\n\n    if (tp->sec == sec) {\n        tp->msec = msec;\n#if (T_NGX_RET_CACHE)\n        tp->usec = usec;\n#endif\n        ngx_unlock(&ngx_time_lock);\n        return;\n    }\n\n    if (slot == NGX_TIME_SLOTS - 1) {\n        slot = 0;\n    } else {\n        slot++;\n    }\n\n    tp = &cached_time[slot];\n\n    tp->sec = sec;\n    tp->msec = msec;\n#if (T_NGX_RET_CACHE)\n    tp->usec = usec;\n#endif\n\n    ngx_gmtime(sec, &gmt);\n\n\n    p0 = &cached_http_time[slot][0];\n\n    (void) ngx_sprintf(p0, \"%s, %02d %s %4d %02d:%02d:%02d GMT\",\n                       week[gmt.ngx_tm_wday], gmt.ngx_tm_mday,\n                       months[gmt.ngx_tm_mon - 1], gmt.ngx_tm_year,\n                       gmt.ngx_tm_hour, gmt.ngx_tm_min, gmt.ngx_tm_sec);\n\n#if (NGX_HAVE_GETTIMEZONE)\n\n    tp->gmtoff = ngx_gettimezone();\n    ngx_gmtime(sec + tp->gmtoff * 60, &tm);\n\n#elif (NGX_HAVE_GMTOFF)\n\n    ngx_localtime(sec, &tm);\n    cached_gmtoff = (ngx_int_t) (tm.ngx_tm_gmtoff / 60);\n    tp->gmtoff = cached_gmtoff;\n\n#else\n\n    ngx_localtime(sec, &tm);\n    cached_gmtoff = ngx_timezone(tm.ngx_tm_isdst);\n    tp->gmtoff = cached_gmtoff;\n\n#endif\n\n#if (T_NGX_RET_CACHE)\n    cached_http_log_tm[slot] = tm;\n#endif\n\n    p1 = &cached_err_log_time[slot][0];\n\n    (void) ngx_sprintf(p1, \"%4d/%02d/%02d %02d:%02d:%02d\",\n                       tm.ngx_tm_year, tm.ngx_tm_mon,\n                       tm.ngx_tm_mday, tm.ngx_tm_hour,\n                       tm.ngx_tm_min, tm.ngx_tm_sec);\n\n\n    p2 = &cached_http_log_time[slot][0];\n\n    (void) ngx_sprintf(p2, \"%02d/%s/%d:%02d:%02d:%02d %c%02i%02i\",\n                       tm.ngx_tm_mday, months[tm.ngx_tm_mon - 1],\n                       tm.ngx_tm_year, tm.ngx_tm_hour,\n                       tm.ngx_tm_min, tm.ngx_tm_sec,\n                       tp->gmtoff < 0 ? '-' : '+',\n                       ngx_abs(tp->gmtoff / 60), ngx_abs(tp->gmtoff % 60));\n\n    p3 = &cached_http_log_iso8601[slot][0];\n\n    (void) ngx_sprintf(p3, \"%4d-%02d-%02dT%02d:%02d:%02d%c%02i:%02i\",\n                       tm.ngx_tm_year, tm.ngx_tm_mon,\n                       tm.ngx_tm_mday, tm.ngx_tm_hour,\n                       tm.ngx_tm_min, tm.ngx_tm_sec,\n                       tp->gmtoff < 0 ? '-' : '+',\n                       ngx_abs(tp->gmtoff / 60), ngx_abs(tp->gmtoff % 60));\n\n    p4 = &cached_syslog_time[slot][0];\n\n    (void) ngx_sprintf(p4, \"%s %2d %02d:%02d:%02d\",\n                       months[tm.ngx_tm_mon - 1], tm.ngx_tm_mday,\n                       tm.ngx_tm_hour, tm.ngx_tm_min, tm.ngx_tm_sec);\n\n#if (T_NGX_XQUIC)\n    p5 = &cached_xquic_log_time[slot][0];\n\n    (void) ngx_sprintf(p5, \"%4d/%02d/%02d %02d:%02d:%02d.%03d|%013ui\",\n                       tm.ngx_tm_year, tm.ngx_tm_mon,\n                       tm.ngx_tm_mday, tm.ngx_tm_hour,\n                       tm.ngx_tm_min, tm.ngx_tm_sec, msec, ngx_current_msec);\n#endif\n\n    ngx_memory_barrier();\n\n#if (T_NGX_RET_CACHE)\n    ngx_cached_tm = &cached_http_log_tm[slot];\n#endif\n    ngx_cached_time = tp;\n    ngx_cached_http_time.data = p0;\n    ngx_cached_err_log_time.data = p1;\n    ngx_cached_http_log_time.data = p2;\n    ngx_cached_http_log_iso8601.data = p3;\n    ngx_cached_syslog_time.data = p4;\n#if (T_NGX_XQUIC)\n    ngx_cached_xquic_log_time.data = p5;\n#endif\n    ngx_unlock(&ngx_time_lock);\n}\n\n\nstatic ngx_msec_t\nngx_monotonic_time(time_t sec, ngx_uint_t msec)\n{\n#if (NGX_HAVE_CLOCK_MONOTONIC)\n    struct timespec  ts;\n\n#if defined(CLOCK_MONOTONIC_FAST)\n    clock_gettime(CLOCK_MONOTONIC_FAST, &ts);\n#else\n    clock_gettime(CLOCK_MONOTONIC, &ts);\n#endif\n\n    sec = ts.tv_sec;\n    msec = ts.tv_nsec / 1000000;\n\n#endif\n\n    return (ngx_msec_t) sec * 1000 + msec;\n}\n\n\n#if !(NGX_WIN32)\n\nvoid\nngx_time_sigsafe_update(void)\n{\n    u_char          *p, *p2;\n    ngx_tm_t         tm;\n    time_t           sec;\n    ngx_time_t      *tp;\n    struct timeval   tv;\n\n    if (!ngx_trylock(&ngx_time_lock)) {\n        return;\n    }\n\n    ngx_gettimeofday(&tv);\n\n    sec = tv.tv_sec;\n\n    tp = &cached_time[slot];\n\n    if (tp->sec == sec) {\n        ngx_unlock(&ngx_time_lock);\n        return;\n    }\n\n    if (slot == NGX_TIME_SLOTS - 1) {\n        slot = 0;\n    } else {\n        slot++;\n    }\n\n    tp = &cached_time[slot];\n\n    tp->sec = 0;\n\n    ngx_gmtime(sec + cached_gmtoff * 60, &tm);\n\n    p = &cached_err_log_time[slot][0];\n\n    (void) ngx_sprintf(p, \"%4d/%02d/%02d %02d:%02d:%02d\",\n                       tm.ngx_tm_year, tm.ngx_tm_mon,\n                       tm.ngx_tm_mday, tm.ngx_tm_hour,\n                       tm.ngx_tm_min, tm.ngx_tm_sec);\n\n    p2 = &cached_syslog_time[slot][0];\n\n    (void) ngx_sprintf(p2, \"%s %2d %02d:%02d:%02d\",\n                       months[tm.ngx_tm_mon - 1], tm.ngx_tm_mday,\n                       tm.ngx_tm_hour, tm.ngx_tm_min, tm.ngx_tm_sec);\n\n    ngx_memory_barrier();\n\n    ngx_cached_err_log_time.data = p;\n    ngx_cached_syslog_time.data = p2;\n\n    ngx_unlock(&ngx_time_lock);\n}\n\n#endif\n\n\nu_char *\nngx_http_time(u_char *buf, time_t t)\n{\n    ngx_tm_t  tm;\n\n    ngx_gmtime(t, &tm);\n\n    return ngx_sprintf(buf, \"%s, %02d %s %4d %02d:%02d:%02d GMT\",\n                       week[tm.ngx_tm_wday],\n                       tm.ngx_tm_mday,\n                       months[tm.ngx_tm_mon - 1],\n                       tm.ngx_tm_year,\n                       tm.ngx_tm_hour,\n                       tm.ngx_tm_min,\n                       tm.ngx_tm_sec);\n}\n\n\nu_char *\nngx_http_cookie_time(u_char *buf, time_t t)\n{\n    ngx_tm_t  tm;\n\n    ngx_gmtime(t, &tm);\n\n    /*\n     * Netscape 3.x does not understand 4-digit years at all and\n     * 2-digit years more than \"37\"\n     */\n\n    return ngx_sprintf(buf,\n                       (tm.ngx_tm_year > 2037) ?\n                                         \"%s, %02d-%s-%d %02d:%02d:%02d GMT\":\n                                         \"%s, %02d-%s-%02d %02d:%02d:%02d GMT\",\n                       week[tm.ngx_tm_wday],\n                       tm.ngx_tm_mday,\n                       months[tm.ngx_tm_mon - 1],\n                       (tm.ngx_tm_year > 2037) ? tm.ngx_tm_year:\n                                                 tm.ngx_tm_year % 100,\n                       tm.ngx_tm_hour,\n                       tm.ngx_tm_min,\n                       tm.ngx_tm_sec);\n}\n\n\nvoid\nngx_gmtime(time_t t, ngx_tm_t *tp)\n{\n    ngx_int_t   yday;\n    ngx_uint_t  sec, min, hour, mday, mon, year, wday, days, leap;\n\n    /* the calculation is valid for positive time_t only */\n\n    if (t < 0) {\n        t = 0;\n    }\n\n    days = t / 86400;\n    sec = t % 86400;\n\n    /*\n     * no more than 4 year digits supported,\n     * truncate to December 31, 9999, 23:59:59\n     */\n\n    if (days > 2932896) {\n        days = 2932896;\n        sec = 86399;\n    }\n\n    /* January 1, 1970 was Thursday */\n\n    wday = (4 + days) % 7;\n\n    hour = sec / 3600;\n    sec %= 3600;\n    min = sec / 60;\n    sec %= 60;\n\n    /*\n     * the algorithm based on Gauss' formula,\n     * see src/core/ngx_parse_time.c\n     */\n\n    /* days since March 1, 1 BC */\n    days = days - (31 + 28) + 719527;\n\n    /*\n     * The \"days\" should be adjusted to 1 only, however, some March 1st's go\n     * to previous year, so we adjust them to 2.  This causes also shift of the\n     * last February days to next year, but we catch the case when \"yday\"\n     * becomes negative.\n     */\n\n    year = (days + 2) * 400 / (365 * 400 + 100 - 4 + 1);\n\n    yday = days - (365 * year + year / 4 - year / 100 + year / 400);\n\n    if (yday < 0) {\n        leap = (year % 4 == 0) && (year % 100 || (year % 400 == 0));\n        yday = 365 + leap + yday;\n        year--;\n    }\n\n    /*\n     * The empirical formula that maps \"yday\" to month.\n     * There are at least 10 variants, some of them are:\n     *     mon = (yday + 31) * 15 / 459\n     *     mon = (yday + 31) * 17 / 520\n     *     mon = (yday + 31) * 20 / 612\n     */\n\n    mon = (yday + 31) * 10 / 306;\n\n    /* the Gauss' formula that evaluates days before the month */\n\n    mday = yday - (367 * mon / 12 - 30) + 1;\n\n    if (yday >= 306) {\n\n        year++;\n        mon -= 10;\n\n        /*\n         * there is no \"yday\" in Win32 SYSTEMTIME\n         *\n         * yday -= 306;\n         */\n\n    } else {\n\n        mon += 2;\n\n        /*\n         * there is no \"yday\" in Win32 SYSTEMTIME\n         *\n         * yday += 31 + 28 + leap;\n         */\n    }\n\n    tp->ngx_tm_sec = (ngx_tm_sec_t) sec;\n    tp->ngx_tm_min = (ngx_tm_min_t) min;\n    tp->ngx_tm_hour = (ngx_tm_hour_t) hour;\n    tp->ngx_tm_mday = (ngx_tm_mday_t) mday;\n    tp->ngx_tm_mon = (ngx_tm_mon_t) mon;\n    tp->ngx_tm_year = (ngx_tm_year_t) year;\n    tp->ngx_tm_wday = (ngx_tm_wday_t) wday;\n}\n\n\ntime_t\nngx_next_time(time_t when)\n{\n    time_t     now, next;\n    struct tm  tm;\n\n    now = ngx_time();\n\n    ngx_libc_localtime(now, &tm);\n\n    tm.tm_hour = (int) (when / 3600);\n    when %= 3600;\n    tm.tm_min = (int) (when / 60);\n    tm.tm_sec = (int) (when % 60);\n\n    next = mktime(&tm);\n\n    if (next == -1) {\n        return -1;\n    }\n\n    if (next - now > 0) {\n        return next;\n    }\n\n    tm.tm_mday++;\n\n    /* mktime() should normalize a date (Jan 32, etc) */\n\n    next = mktime(&tm);\n\n    if (next != -1) {\n        return next;\n    }\n\n    return -1;\n}\n"
  },
  {
    "path": "src/core/ngx_times.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_TIMES_H_INCLUDED_\n#define _NGX_TIMES_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\ntypedef struct {\n    time_t      sec;\n    ngx_uint_t  msec;\n#if (T_NGX_RET_CACHE)\n    ngx_uint_t  usec;\n#endif\n    ngx_int_t   gmtoff;\n} ngx_time_t;\n\n\nvoid ngx_time_init(void);\nvoid ngx_time_update(void);\nvoid ngx_time_sigsafe_update(void);\nu_char *ngx_http_time(u_char *buf, time_t t);\nu_char *ngx_http_cookie_time(u_char *buf, time_t t);\nvoid ngx_gmtime(time_t t, ngx_tm_t *tp);\n\ntime_t ngx_next_time(time_t when);\n#define ngx_next_time_n      \"mktime()\"\n\n#if (T_NGX_RET_CACHE)\nextern volatile ngx_tm_t    *ngx_cached_tm;\n#endif\n\nextern volatile ngx_time_t  *ngx_cached_time;\n\n#define ngx_time()           ngx_cached_time->sec\n#define ngx_timeofday()      (ngx_time_t *) ngx_cached_time\n\n#if (T_NGX_XQUIC)\nextern volatile ngx_str_t    ngx_cached_xquic_log_time;\n#endif\n\nextern volatile ngx_str_t    ngx_cached_err_log_time;\nextern volatile ngx_str_t    ngx_cached_http_time;\nextern volatile ngx_str_t    ngx_cached_http_log_time;\nextern volatile ngx_str_t    ngx_cached_http_log_iso8601;\nextern volatile ngx_str_t    ngx_cached_syslog_time;\n\n/*\n * milliseconds elapsed since some unspecified point in the past\n * and truncated to ngx_msec_t, used in event timers\n */\nextern volatile ngx_msec_t  ngx_current_msec;\n\n\n#endif /* _NGX_TIMES_H_INCLUDED_ */\n"
  },
  {
    "path": "src/core/ngx_trie.c",
    "content": "\n/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\n#define NGX_TRIE_MAX_QUEUE_SIZE     300\n#define NGX_TRIE_KIND               256\n\n\nngx_trie_t *\nngx_trie_create(ngx_pool_t *pool)\n{\n    ngx_trie_t *trie;\n\n    trie = ngx_palloc(pool, sizeof(ngx_trie_t));\n    if (trie == NULL) {\n        return NULL;\n    }\n\n    trie->root = ngx_trie_node_create(pool);\n    if (trie->root == NULL) {\n        return NULL;\n    }\n\n    trie->pool = pool;\n    trie->insert = ngx_trie_insert;\n    trie->query = ngx_trie_query;\n    trie->build_clue = ngx_trie_build_clue;\n\n    return trie;\n}\n\n\nngx_trie_node_t *\nngx_trie_node_create(ngx_pool_t *pool)\n{\n    ngx_trie_node_t *node;\n\n    node = ngx_pcalloc(pool, sizeof(ngx_trie_node_t));\n    if (node == NULL) {\n        return NULL;\n    }\n\n    return node;\n}\n\n\nngx_trie_node_t *\nngx_trie_insert(ngx_trie_t *trie, ngx_str_t *str, ngx_uint_t mode)\n{\n    size_t           i;\n    ngx_int_t        pos, step, index;\n    ngx_trie_node_t *p, *root;\n\n    root = trie->root;\n    i = 0;\n\n    if (mode & NGX_TRIE_REVERSE) {\n        pos = str->len;\n        step = -1;\n    } else {\n        pos = -1;\n        step = 1;\n    }\n\n    p = root;\n\n    while (i < str->len) {\n        pos = pos + step;\n        index = str->data[pos];\n\n        if (index < 0 || index >= NGX_TRIE_KIND) {\n            continue;\n        }\n\n        if (p->next == NULL) {\n            p->next = ngx_pcalloc(trie->pool,\n                                  NGX_TRIE_KIND * sizeof(ngx_trie_node_t *));\n\n            if (p->next == NULL) {\n                return NULL;\n            }\n        }\n\n        if (p->next[index] == NULL) {\n            p->next[index] = ngx_trie_node_create(trie->pool);\n            if (p->next[index] == NULL) {\n                return NULL;\n            }\n        }\n\n        p = p->next[index];\n        i++;\n    }\n\n    p->key = str->len;\n    if (mode & NGX_TRIE_CONTINUE) {\n        p->greedy = 1;\n    }\n\n    return p;\n}\n\n\nngx_int_t\nngx_trie_build_clue(ngx_trie_t *trie)\n{\n    ngx_int_t        i, head, tail;\n    ngx_trie_node_t *q[NGX_TRIE_MAX_QUEUE_SIZE], *p, *t, *root;\n\n    head = tail = 0;\n    root = trie->root;\n    q[head++] = root;\n    root->search_clue = NULL;\n\n    while (head != tail) {\n        t = q[tail++];\n        tail %= NGX_TRIE_MAX_QUEUE_SIZE;\n\n        if (t->next == NULL) {\n            continue;\n        }\n\n        p = NULL;\n\n        for (i = 0; i< NGX_TRIE_KIND; i++) {\n            if (t->next[i] == NULL) {\n                continue;\n            }\n\n            if (t == root) {\n                t->next[i]->search_clue = root;\n\n                q[head++] = t->next[i];\n                head %= NGX_TRIE_MAX_QUEUE_SIZE;\n\n                continue;\n            }\n\n            p = t->search_clue;\n\n            while (p != NULL) {\n                if (p->next !=NULL && p->next[i] != NULL) {\n                    t->next[i]->search_clue = p->next[i];\n                    break;\n                }\n                p = p->search_clue;\n            }\n\n            if (p == NULL) {\n                t->next[i]->search_clue = root;\n            }\n\n            q[head++] = t->next[i];\n            head %= NGX_TRIE_MAX_QUEUE_SIZE;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nvoid *\nngx_trie_query(ngx_trie_t *trie, ngx_str_t *str, ngx_int_t *version_pos,\n    ngx_uint_t mode)\n{\n    void            *value;\n    size_t           i;\n    ngx_int_t        step, pos, index;\n    ngx_trie_node_t *p, *root;\n\n    value = NULL;\n    root = trie->root;\n    p = root;\n    i = 0;\n\n    if (mode & NGX_TRIE_REVERSE) {\n        pos = str->len;\n        step = -1;\n    } else {\n        pos = -1;\n        step = 1;\n    }\n\n    if (p->next == NULL) {\n        return NULL;\n    }\n\n    while (i < str->len) {\n        pos += step;\n        index = str->data[pos];\n        if (index < 0 || index >= NGX_TRIE_KIND) {\n            index = 0;\n        }\n\n        while (p->next[index] == NULL) {\n            if (p == root) {\n                break;\n            }\n            p = p->search_clue;\n        }\n\n        p = p->next[index];\n        p = p == NULL ? root : p;\n        if (p->key) {\n            value = p->value;\n            *version_pos = pos + p->key;\n            if (!p->greedy) {\n                return value;\n            }\n            p = root;\n        }\n\n        i++;\n    }\n\n    return value;\n}\n"
  },
  {
    "path": "src/core/ngx_trie.h",
    "content": "\n/*\n *  Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#ifndef _NGX_TRIE_H_INCLUDE_\n#define _NGX_TRIE_H_INCLUDE_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\n#define NGX_TRIE_REVERSE            1\n#define NGX_TRIE_CONTINUE           2\n\n\ntypedef struct ngx_trie_s           ngx_trie_t;\ntypedef struct ngx_trie_node_s      ngx_trie_node_t;\n\ntypedef ngx_trie_node_t *(*ngx_trie_insert_pt)(ngx_trie_t *trie,\n    ngx_str_t *str, ngx_uint_t mode);\ntypedef ngx_int_t (*ngx_trie_build_clue_pt)(ngx_trie_t *trie);\ntypedef void *(*ngx_trie_query_pt)(ngx_trie_t *trie,\n    ngx_str_t *str, ngx_int_t *pos, ngx_uint_t mode);\n\n\nstruct ngx_trie_node_s {\n    void                           *value;\n    ngx_trie_node_t                *search_clue;\n    ngx_trie_node_t               **next;\n\n    unsigned                        key:31;\n    unsigned                        greedy:1;\n};\n\n\nstruct ngx_trie_s {\n    ngx_trie_node_t                *root;\n    ngx_pool_t                     *pool;\n    ngx_trie_insert_pt              insert;\n    ngx_trie_query_pt               query;\n    ngx_trie_build_clue_pt          build_clue;\n};\n\n\nngx_trie_t *ngx_trie_create(ngx_pool_t *pool);\nngx_trie_node_t *ngx_trie_node_create(ngx_pool_t *pool);\nngx_trie_node_t *ngx_trie_insert(ngx_trie_t *trie, ngx_str_t *str,\n    ngx_uint_t mode);\nvoid *ngx_trie_query(ngx_trie_t *trie, ngx_str_t *str, ngx_int_t *pos,\n    ngx_uint_t mode);\nngx_int_t ngx_trie_build_clue(ngx_trie_t *trie);\n\n\n#endif /* _NGX_TRIE_H_INCLUDE_ */\n"
  },
  {
    "path": "src/event/modules/ngx_aio_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n\n\nextern ngx_event_module_t  ngx_kqueue_module_ctx;\n\n\nstatic ngx_int_t ngx_aio_init(ngx_cycle_t *cycle, ngx_msec_t timer);\nstatic void ngx_aio_done(ngx_cycle_t *cycle);\nstatic ngx_int_t ngx_aio_add_event(ngx_event_t *ev, ngx_int_t event,\n    ngx_uint_t flags);\nstatic ngx_int_t ngx_aio_del_event(ngx_event_t *ev, ngx_int_t event,\n    ngx_uint_t flags);\nstatic ngx_int_t ngx_aio_del_connection(ngx_connection_t *c, ngx_uint_t flags);\nstatic ngx_int_t ngx_aio_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,\n    ngx_uint_t flags);\n\n\nngx_os_io_t ngx_os_aio = {\n    ngx_aio_read,\n    ngx_aio_read_chain,\n    NULL,\n    ngx_aio_write,\n    ngx_aio_write_chain,\n    0\n};\n\n\nstatic ngx_str_t      aio_name = ngx_string(\"aio\");\n\nngx_event_module_t  ngx_aio_module_ctx = {\n    &aio_name,\n    NULL,                                  /* create configuration */\n    NULL,                                  /* init configuration */\n\n    {\n        ngx_aio_add_event,                 /* add an event */\n        ngx_aio_del_event,                 /* delete an event */\n        NULL,                              /* enable an event */\n        NULL,                              /* disable an event */\n        NULL,                              /* add an connection */\n        ngx_aio_del_connection,            /* delete an connection */\n        NULL,                              /* trigger a notify */\n        ngx_aio_process_events,            /* process the events */\n        ngx_aio_init,                      /* init the events */\n        ngx_aio_done                       /* done the events */\n    }\n\n};\n\nngx_module_t  ngx_aio_module = {\n    NGX_MODULE_V1,\n    &ngx_aio_module_ctx,                   /* module context */\n    NULL,                                  /* module directives */\n    NGX_EVENT_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#if (NGX_HAVE_KQUEUE)\n\nstatic ngx_int_t\nngx_aio_init(ngx_cycle_t *cycle, ngx_msec_t timer)\n{\n    if (ngx_kqueue_module_ctx.actions.init(cycle, timer) == NGX_ERROR) {\n        return NGX_ERROR;\n    }\n\n    ngx_io = ngx_os_aio;\n\n    ngx_event_flags = NGX_USE_AIO_EVENT;\n    ngx_event_actions = ngx_aio_module_ctx.actions;\n\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_aio_done(ngx_cycle_t *cycle)\n{\n    ngx_kqueue_module_ctx.actions.done(cycle);\n}\n\n\n/* the event adding and deleting are needed for the listening sockets */\n\nstatic ngx_int_t\nngx_aio_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)\n{\n    return ngx_kqueue_module_ctx.actions.add(ev, event, flags);\n}\n\n\nstatic ngx_int_t\nngx_aio_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)\n{\n    return ngx_kqueue_module_ctx.actions.del(ev, event, flags);\n}\n\n\nstatic ngx_int_t\nngx_aio_del_connection(ngx_connection_t *c, ngx_uint_t flags)\n{\n    int  rc;\n\n    if (c->read->active == 0 && c->write->active == 0) {\n        return NGX_OK;\n    }\n\n    if (flags & NGX_CLOSE_EVENT) {\n        return NGX_OK;\n    }\n\n    rc = aio_cancel(c->fd, NULL);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, \"aio_cancel: %d\", rc);\n\n    if (rc == AIO_CANCELED) {\n        c->read->active = 0;\n        c->write->active = 0;\n        return NGX_OK;\n    }\n\n    if (rc == AIO_ALLDONE) {\n        c->read->active = 0;\n        c->write->active = 0;\n        ngx_log_error(NGX_LOG_ALERT, c->log, 0,\n                      \"aio_cancel() returned AIO_ALLDONE\");\n        return NGX_OK;\n    }\n\n    if (rc == -1) {\n        ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,\n                      \"aio_cancel() failed\");\n        return NGX_ERROR;\n    }\n\n    if (rc == AIO_NOTCANCELED) {\n        ngx_log_error(NGX_LOG_ALERT, c->log, 0,\n                      \"aio_cancel() returned AIO_NOTCANCELED\");\n\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_aio_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags)\n{\n    return ngx_kqueue_module_ctx.actions.process_events(cycle, timer, flags);\n}\n\n#endif /* NGX_HAVE_KQUEUE */\n"
  },
  {
    "path": "src/event/modules/ngx_devpoll_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n\n\n#if (NGX_TEST_BUILD_DEVPOLL)\n\n/* Solaris declarations */\n\n#ifndef POLLREMOVE\n#define POLLREMOVE   0x0800\n#endif\n#define DP_POLL      0xD001\n#define DP_ISPOLLED  0xD002\n\nstruct dvpoll {\n    struct pollfd  *dp_fds;\n    int             dp_nfds;\n    int             dp_timeout;\n};\n\n#endif\n\n\ntypedef struct {\n    ngx_uint_t      changes;\n    ngx_uint_t      events;\n} ngx_devpoll_conf_t;\n\n\nstatic ngx_int_t ngx_devpoll_init(ngx_cycle_t *cycle, ngx_msec_t timer);\nstatic void ngx_devpoll_done(ngx_cycle_t *cycle);\nstatic ngx_int_t ngx_devpoll_add_event(ngx_event_t *ev, ngx_int_t event,\n    ngx_uint_t flags);\nstatic ngx_int_t ngx_devpoll_del_event(ngx_event_t *ev, ngx_int_t event,\n    ngx_uint_t flags);\nstatic ngx_int_t ngx_devpoll_set_event(ngx_event_t *ev, ngx_int_t event,\n    ngx_uint_t flags);\nstatic ngx_int_t ngx_devpoll_process_events(ngx_cycle_t *cycle,\n    ngx_msec_t timer, ngx_uint_t flags);\n\nstatic void *ngx_devpoll_create_conf(ngx_cycle_t *cycle);\nstatic char *ngx_devpoll_init_conf(ngx_cycle_t *cycle, void *conf);\n\nstatic int              dp = -1;\nstatic struct pollfd   *change_list, *event_list;\nstatic ngx_uint_t       nchanges, max_changes, nevents;\n\nstatic ngx_event_t    **change_index;\n\n\nstatic ngx_str_t      devpoll_name = ngx_string(\"/dev/poll\");\n\nstatic ngx_command_t  ngx_devpoll_commands[] = {\n\n    { ngx_string(\"devpoll_changes\"),\n      NGX_EVENT_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_num_slot,\n      0,\n      offsetof(ngx_devpoll_conf_t, changes),\n      NULL },\n\n    { ngx_string(\"devpoll_events\"),\n      NGX_EVENT_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_num_slot,\n      0,\n      offsetof(ngx_devpoll_conf_t, events),\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_event_module_t  ngx_devpoll_module_ctx = {\n    &devpoll_name,\n    ngx_devpoll_create_conf,               /* create configuration */\n    ngx_devpoll_init_conf,                 /* init configuration */\n\n    {\n        ngx_devpoll_add_event,             /* add an event */\n        ngx_devpoll_del_event,             /* delete an event */\n        ngx_devpoll_add_event,             /* enable an event */\n        ngx_devpoll_del_event,             /* disable an event */\n        NULL,                              /* add an connection */\n        NULL,                              /* delete an connection */\n        NULL,                              /* trigger a notify */\n        ngx_devpoll_process_events,        /* process the events */\n        ngx_devpoll_init,                  /* init the events */\n        ngx_devpoll_done,                  /* done the events */\n#if (NGX_SSL && NGX_SSL_ASYNC)\n        NULL,                              /* add an async conn */\n        NULL,                              /* del an async conn */\n#endif\n    }\n\n};\n\nngx_module_t  ngx_devpoll_module = {\n    NGX_MODULE_V1,\n    &ngx_devpoll_module_ctx,               /* module context */\n    ngx_devpoll_commands,                  /* module directives */\n    NGX_EVENT_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_devpoll_init(ngx_cycle_t *cycle, ngx_msec_t timer)\n{\n    size_t               n;\n    ngx_devpoll_conf_t  *dpcf;\n\n    dpcf = ngx_event_get_conf(cycle->conf_ctx, ngx_devpoll_module);\n\n    if (dp == -1) {\n        dp = open(\"/dev/poll\", O_RDWR);\n\n        if (dp == -1) {\n            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                          \"open(/dev/poll) failed\");\n            return NGX_ERROR;\n        }\n    }\n\n    if (max_changes < dpcf->changes) {\n        if (nchanges) {\n            n = nchanges * sizeof(struct pollfd);\n            if (write(dp, change_list, n) != (ssize_t) n) {\n                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                              \"write(/dev/poll) failed\");\n                return NGX_ERROR;\n            }\n\n            nchanges = 0;\n        }\n\n        if (change_list) {\n            ngx_free(change_list);\n        }\n\n        change_list = ngx_alloc(sizeof(struct pollfd) * dpcf->changes,\n                                cycle->log);\n        if (change_list == NULL) {\n            return NGX_ERROR;\n        }\n\n        if (change_index) {\n            ngx_free(change_index);\n        }\n\n        change_index = ngx_alloc(sizeof(ngx_event_t *) * dpcf->changes,\n                                 cycle->log);\n        if (change_index == NULL) {\n            return NGX_ERROR;\n        }\n    }\n\n    max_changes = dpcf->changes;\n\n    if (nevents < dpcf->events) {\n        if (event_list) {\n            ngx_free(event_list);\n        }\n\n        event_list = ngx_alloc(sizeof(struct pollfd) * dpcf->events,\n                               cycle->log);\n        if (event_list == NULL) {\n            return NGX_ERROR;\n        }\n    }\n\n    nevents = dpcf->events;\n\n    ngx_io = ngx_os_io;\n\n    ngx_event_actions = ngx_devpoll_module_ctx.actions;\n\n    ngx_event_flags = NGX_USE_LEVEL_EVENT|NGX_USE_FD_EVENT;\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_devpoll_done(ngx_cycle_t *cycle)\n{\n    if (close(dp) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                      \"close(/dev/poll) failed\");\n    }\n\n    dp = -1;\n\n    ngx_free(change_list);\n    ngx_free(event_list);\n    ngx_free(change_index);\n\n    change_list = NULL;\n    event_list = NULL;\n    change_index = NULL;\n    max_changes = 0;\n    nchanges = 0;\n    nevents = 0;\n}\n\n\nstatic ngx_int_t\nngx_devpoll_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)\n{\n#if (NGX_DEBUG)\n    ngx_connection_t *c;\n#endif\n\n#if (NGX_READ_EVENT != POLLIN)\n    event = (event == NGX_READ_EVENT) ? POLLIN : POLLOUT;\n#endif\n\n#if (NGX_DEBUG)\n    c = ev->data;\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,\n                   \"devpoll add event: fd:%d ev:%04Xi\", c->fd, event);\n#endif\n\n    ev->active = 1;\n\n    return ngx_devpoll_set_event(ev, event, 0);\n}\n\n\nstatic ngx_int_t\nngx_devpoll_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)\n{\n    ngx_event_t       *e;\n    ngx_connection_t  *c;\n\n    c = ev->data;\n\n#if (NGX_READ_EVENT != POLLIN)\n    event = (event == NGX_READ_EVENT) ? POLLIN : POLLOUT;\n#endif\n\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,\n                   \"devpoll del event: fd:%d ev:%04Xi\", c->fd, event);\n\n    if (ngx_devpoll_set_event(ev, POLLREMOVE, flags) == NGX_ERROR) {\n        return NGX_ERROR;\n    }\n\n    ev->active = 0;\n\n    if (flags & NGX_CLOSE_EVENT) {\n        e = (event == POLLIN) ? c->write : c->read;\n\n        if (e) {\n            e->active = 0;\n        }\n\n        return NGX_OK;\n    }\n\n    /* restore the pair event if it exists */\n\n    if (event == POLLIN) {\n        e = c->write;\n        event = POLLOUT;\n\n    } else {\n        e = c->read;\n        event = POLLIN;\n    }\n\n    if (e && e->active) {\n        return ngx_devpoll_set_event(e, event, 0);\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_devpoll_set_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)\n{\n    size_t             n;\n    ngx_connection_t  *c;\n\n    c = ev->data;\n\n    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ev->log, 0,\n                   \"devpoll fd:%d ev:%04Xi fl:%04Xi\", c->fd, event, flags);\n\n    if (nchanges >= max_changes) {\n        ngx_log_error(NGX_LOG_WARN, ev->log, 0,\n                      \"/dev/pool change list is filled up\");\n\n        n = nchanges * sizeof(struct pollfd);\n        if (write(dp, change_list, n) != (ssize_t) n) {\n            ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,\n                          \"write(/dev/poll) failed\");\n            return NGX_ERROR;\n        }\n\n        nchanges = 0;\n    }\n\n    change_list[nchanges].fd = c->fd;\n    change_list[nchanges].events = (short) event;\n    change_list[nchanges].revents = 0;\n\n    change_index[nchanges] = ev;\n    ev->index = nchanges;\n\n    nchanges++;\n\n    if (flags & NGX_CLOSE_EVENT) {\n        n = nchanges * sizeof(struct pollfd);\n        if (write(dp, change_list, n) != (ssize_t) n) {\n            ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,\n                          \"write(/dev/poll) failed\");\n            return NGX_ERROR;\n        }\n\n        nchanges = 0;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_devpoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,\n    ngx_uint_t flags)\n{\n    int                 events, revents, rc;\n    size_t              n;\n    ngx_fd_t            fd;\n    ngx_err_t           err;\n    ngx_int_t           i;\n    ngx_uint_t          level, instance;\n    ngx_event_t        *rev, *wev;\n    ngx_queue_t        *queue;\n    ngx_connection_t   *c;\n    struct pollfd       pfd;\n    struct dvpoll       dvp;\n\n    /* NGX_TIMER_INFINITE == INFTIM */\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                   \"devpoll timer: %M\", timer);\n\n    if (nchanges) {\n        n = nchanges * sizeof(struct pollfd);\n        if (write(dp, change_list, n) != (ssize_t) n) {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                          \"write(/dev/poll) failed\");\n            return NGX_ERROR;\n        }\n\n        nchanges = 0;\n    }\n\n    dvp.dp_fds = event_list;\n    dvp.dp_nfds = (int) nevents;\n    dvp.dp_timeout = timer;\n    events = ioctl(dp, DP_POLL, &dvp);\n\n    err = (events == -1) ? ngx_errno : 0;\n\n    if (flags & NGX_UPDATE_TIME || ngx_event_timer_alarm) {\n        ngx_time_update();\n    }\n\n    if (err) {\n        if (err == NGX_EINTR) {\n\n            if (ngx_event_timer_alarm) {\n                ngx_event_timer_alarm = 0;\n                return NGX_OK;\n            }\n\n            level = NGX_LOG_INFO;\n\n        } else {\n            level = NGX_LOG_ALERT;\n        }\n\n        ngx_log_error(level, cycle->log, err, \"ioctl(DP_POLL) failed\");\n        return NGX_ERROR;\n    }\n\n    if (events == 0) {\n        if (timer != NGX_TIMER_INFINITE) {\n            return NGX_OK;\n        }\n\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,\n                      \"ioctl(DP_POLL) returned no events without timeout\");\n        return NGX_ERROR;\n    }\n\n    for (i = 0; i < events; i++) {\n\n        fd = event_list[i].fd;\n        revents = event_list[i].revents;\n\n        c = ngx_cycle->files[fd];\n\n        if (c == NULL || c->fd == -1) {\n\n            pfd.fd = fd;\n            pfd.events = 0;\n            pfd.revents = 0;\n\n            rc = ioctl(dp, DP_ISPOLLED, &pfd);\n\n            switch (rc) {\n\n            case -1:\n                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                    \"ioctl(DP_ISPOLLED) failed for socket %d, event %04Xd\",\n                    fd, revents);\n                break;\n\n            case 0:\n                ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,\n                    \"phantom event %04Xd for closed and removed socket %d\",\n                    revents, fd);\n                break;\n\n            default:\n                ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,\n                    \"unexpected event %04Xd for closed and removed socket %d, \"\n                    \"ioctl(DP_ISPOLLED) returned rc:%d, fd:%d, event %04Xd\",\n                    revents, fd, rc, pfd.fd, pfd.revents);\n\n                pfd.fd = fd;\n                pfd.events = POLLREMOVE;\n                pfd.revents = 0;\n\n                if (write(dp, &pfd, sizeof(struct pollfd))\n                    != (ssize_t) sizeof(struct pollfd))\n                {\n                    ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                                  \"write(/dev/poll) for %d failed\", fd);\n                }\n\n                if (close(fd) == -1) {\n                    ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                                  \"close(%d) failed\", fd);\n                }\n\n                break;\n            }\n\n            continue;\n        }\n\n        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                       \"devpoll: fd:%d, ev:%04Xd, rev:%04Xd\",\n                       fd, event_list[i].events, revents);\n\n        if (revents & (POLLERR|POLLHUP|POLLNVAL)) {\n            ngx_log_debug3(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                          \"ioctl(DP_POLL) error fd:%d ev:%04Xd rev:%04Xd\",\n                          fd, event_list[i].events, revents);\n        }\n\n        if (revents & ~(POLLIN|POLLOUT|POLLERR|POLLHUP|POLLNVAL)) {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,\n                          \"strange ioctl(DP_POLL) events \"\n                          \"fd:%d ev:%04Xd rev:%04Xd\",\n                          fd, event_list[i].events, revents);\n        }\n\n        if (revents & (POLLERR|POLLHUP|POLLNVAL)) {\n\n            /*\n             * if the error events were returned, add POLLIN and POLLOUT\n             * to handle the events at least in one active handler\n             */\n\n            revents |= POLLIN|POLLOUT;\n        }\n\n        rev = c->read;\n\n        if ((revents & POLLIN) && rev->active) {\n            rev->ready = 1;\n            rev->available = -1;\n\n            if (flags & NGX_POST_EVENTS) {\n                queue = rev->accept ? &ngx_posted_accept_events\n                                    : &ngx_posted_events;\n\n                ngx_post_event(rev, queue);\n\n            } else {\n                instance = rev->instance;\n\n                rev->handler(rev);\n\n                if (c->fd == -1 || rev->instance != instance) {\n                    continue;\n                }\n            }\n        }\n\n        wev = c->write;\n\n        if ((revents & POLLOUT) && wev->active) {\n            wev->ready = 1;\n\n            if (flags & NGX_POST_EVENTS) {\n                ngx_post_event(wev, &ngx_posted_events);\n\n            } else {\n                wev->handler(wev);\n            }\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void *\nngx_devpoll_create_conf(ngx_cycle_t *cycle)\n{\n    ngx_devpoll_conf_t  *dpcf;\n\n    dpcf = ngx_palloc(cycle->pool, sizeof(ngx_devpoll_conf_t));\n    if (dpcf == NULL) {\n        return NULL;\n    }\n\n    dpcf->changes = NGX_CONF_UNSET;\n    dpcf->events = NGX_CONF_UNSET;\n\n    return dpcf;\n}\n\n\nstatic char *\nngx_devpoll_init_conf(ngx_cycle_t *cycle, void *conf)\n{\n    ngx_devpoll_conf_t *dpcf = conf;\n\n    ngx_conf_init_uint_value(dpcf->changes, 32);\n    ngx_conf_init_uint_value(dpcf->events, 32);\n\n    return NGX_CONF_OK;\n}\n"
  },
  {
    "path": "src/event/modules/ngx_epoll_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n\n\n#if (NGX_TEST_BUILD_EPOLL)\n\n/* epoll declarations */\n\n#define EPOLLIN        0x001\n#define EPOLLPRI       0x002\n#define EPOLLOUT       0x004\n#define EPOLLERR       0x008\n#define EPOLLHUP       0x010\n#define EPOLLRDNORM    0x040\n#define EPOLLRDBAND    0x080\n#define EPOLLWRNORM    0x100\n#define EPOLLWRBAND    0x200\n#define EPOLLMSG       0x400\n\n#define EPOLLRDHUP     0x2000\n\n#define EPOLLEXCLUSIVE 0x10000000\n#define EPOLLONESHOT   0x40000000\n#define EPOLLET        0x80000000\n\n#define EPOLL_CTL_ADD  1\n#define EPOLL_CTL_DEL  2\n#define EPOLL_CTL_MOD  3\n\ntypedef union epoll_data {\n    void         *ptr;\n    int           fd;\n    uint32_t      u32;\n    uint64_t      u64;\n} epoll_data_t;\n\nstruct epoll_event {\n    uint32_t      events;\n    epoll_data_t  data;\n};\n\n\nint epoll_create(int size);\n\nint epoll_create(int size)\n{\n    return -1;\n}\n\n\nint epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);\n\nint epoll_ctl(int epfd, int op, int fd, struct epoll_event *event)\n{\n    return -1;\n}\n\n\nint epoll_wait(int epfd, struct epoll_event *events, int nevents, int timeout);\n\nint epoll_wait(int epfd, struct epoll_event *events, int nevents, int timeout)\n{\n    return -1;\n}\n\n#if (NGX_HAVE_EVENTFD)\n#define SYS_eventfd       323\n#endif\n\n#if (NGX_HAVE_FILE_AIO)\n\n#define SYS_io_setup      245\n#define SYS_io_destroy    246\n#define SYS_io_getevents  247\n\ntypedef u_int  aio_context_t;\n\nstruct io_event {\n    uint64_t  data;  /* the data field from the iocb */\n    uint64_t  obj;   /* what iocb this event came from */\n    int64_t   res;   /* result code for this event */\n    int64_t   res2;  /* secondary result */\n};\n\n\n#endif\n#endif /* NGX_TEST_BUILD_EPOLL */\n\n\ntypedef struct {\n    ngx_uint_t  events;\n    ngx_uint_t  aio_requests;\n} ngx_epoll_conf_t;\n\n\nstatic ngx_int_t ngx_epoll_init(ngx_cycle_t *cycle, ngx_msec_t timer);\n#if (NGX_HAVE_EVENTFD)\nstatic ngx_int_t ngx_epoll_notify_init(ngx_log_t *log);\nstatic void ngx_epoll_notify_handler(ngx_event_t *ev);\n#endif\n#if (NGX_HAVE_EPOLLRDHUP)\nstatic void ngx_epoll_test_rdhup(ngx_cycle_t *cycle);\n#endif\nstatic void ngx_epoll_done(ngx_cycle_t *cycle);\nstatic ngx_int_t ngx_epoll_add_event(ngx_event_t *ev, ngx_int_t event,\n    ngx_uint_t flags);\nstatic ngx_int_t ngx_epoll_del_event(ngx_event_t *ev, ngx_int_t event,\n    ngx_uint_t flags);\nstatic ngx_int_t ngx_epoll_add_connection(ngx_connection_t *c);\nstatic ngx_int_t ngx_epoll_del_connection(ngx_connection_t *c,\n    ngx_uint_t flags);\n#if (NGX_HAVE_EVENTFD)\nstatic ngx_int_t ngx_epoll_notify(ngx_event_handler_pt handler);\n#endif\nstatic ngx_int_t ngx_epoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,\n    ngx_uint_t flags);\n#if (NGX_SSL && NGX_SSL_ASYNC)\nstatic ngx_int_t ngx_epoll_add_async_connection(ngx_connection_t *c);\nstatic ngx_int_t ngx_epoll_del_async_connection(ngx_connection_t *c,\n    ngx_uint_t flags);\n#endif\n\n#if (NGX_HAVE_FILE_AIO)\nstatic void ngx_epoll_eventfd_handler(ngx_event_t *ev);\n#endif\n\nstatic void *ngx_epoll_create_conf(ngx_cycle_t *cycle);\nstatic char *ngx_epoll_init_conf(ngx_cycle_t *cycle, void *conf);\n\nstatic int                  ep = -1;\nstatic struct epoll_event  *event_list;\nstatic ngx_uint_t           nevents;\n\n#if (NGX_HAVE_EVENTFD)\nstatic int                  notify_fd = -1;\nstatic ngx_event_t          notify_event;\nstatic ngx_connection_t     notify_conn;\n#endif\n\n#if (NGX_HAVE_FILE_AIO)\n\nint                         ngx_eventfd = -1;\naio_context_t               ngx_aio_ctx = 0;\n\nstatic ngx_event_t          ngx_eventfd_event;\nstatic ngx_connection_t     ngx_eventfd_conn;\n\n#endif\n\n#if (NGX_HAVE_EPOLLRDHUP)\nngx_uint_t                  ngx_use_epoll_rdhup;\n#endif\n\nstatic ngx_str_t      epoll_name = ngx_string(\"epoll\");\n\nstatic ngx_command_t  ngx_epoll_commands[] = {\n\n    { ngx_string(\"epoll_events\"),\n      NGX_EVENT_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_num_slot,\n      0,\n      offsetof(ngx_epoll_conf_t, events),\n      NULL },\n\n    { ngx_string(\"worker_aio_requests\"),\n      NGX_EVENT_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_num_slot,\n      0,\n      offsetof(ngx_epoll_conf_t, aio_requests),\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_event_module_t  ngx_epoll_module_ctx = {\n    &epoll_name,\n    ngx_epoll_create_conf,               /* create configuration */\n    ngx_epoll_init_conf,                 /* init configuration */\n\n    {\n        ngx_epoll_add_event,             /* add an event */\n        ngx_epoll_del_event,             /* delete an event */\n        ngx_epoll_add_event,             /* enable an event */\n        ngx_epoll_del_event,             /* disable an event */\n        ngx_epoll_add_connection,        /* add an connection */\n        ngx_epoll_del_connection,        /* delete an connection */\n#if (NGX_HAVE_EVENTFD)\n        ngx_epoll_notify,                /* trigger a notify */\n#else\n        NULL,                            /* trigger a notify */\n#endif\n        ngx_epoll_process_events,        /* process the events */\n        ngx_epoll_init,                  /* init the events */\n        ngx_epoll_done,                  /* done the events */\n#if (NGX_SSL && NGX_SSL_ASYNC)\n        ngx_epoll_add_async_connection,  /* add an async conn */\n        ngx_epoll_del_async_connection   /* del an async conn */\n#endif\n    }\n};\n\nngx_module_t  ngx_epoll_module = {\n    NGX_MODULE_V1,\n    &ngx_epoll_module_ctx,               /* module context */\n    ngx_epoll_commands,                  /* module directives */\n    NGX_EVENT_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#if (NGX_HAVE_FILE_AIO)\n\n/*\n * We call io_setup(), io_destroy() io_submit(), and io_getevents() directly\n * as syscalls instead of libaio usage, because the library header file\n * supports eventfd() since 0.3.107 version only.\n */\n\nstatic int\nio_setup(u_int nr_reqs, aio_context_t *ctx)\n{\n    return syscall(SYS_io_setup, nr_reqs, ctx);\n}\n\n\nstatic int\nio_destroy(aio_context_t ctx)\n{\n    return syscall(SYS_io_destroy, ctx);\n}\n\n\nstatic int\nio_getevents(aio_context_t ctx, long min_nr, long nr, struct io_event *events,\n    struct timespec *tmo)\n{\n    return syscall(SYS_io_getevents, ctx, min_nr, nr, events, tmo);\n}\n\n\nstatic void\nngx_epoll_aio_init(ngx_cycle_t *cycle, ngx_epoll_conf_t *epcf)\n{\n    int                 n;\n    struct epoll_event  ee;\n\n#if (NGX_HAVE_SYS_EVENTFD_H)\n    ngx_eventfd = eventfd(0, 0);\n#else\n    ngx_eventfd = syscall(SYS_eventfd, 0);\n#endif\n\n    if (ngx_eventfd == -1) {\n        ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                      \"eventfd() failed\");\n        ngx_file_aio = 0;\n        return;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                   \"eventfd: %d\", ngx_eventfd);\n\n    n = 1;\n\n    if (ioctl(ngx_eventfd, FIONBIO, &n) == -1) {\n        ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                      \"ioctl(eventfd, FIONBIO) failed\");\n        goto failed;\n    }\n\n    if (io_setup(epcf->aio_requests, &ngx_aio_ctx) == -1) {\n        ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                      \"io_setup() failed\");\n        goto failed;\n    }\n\n    ngx_eventfd_event.data = &ngx_eventfd_conn;\n    ngx_eventfd_event.handler = ngx_epoll_eventfd_handler;\n    ngx_eventfd_event.log = cycle->log;\n    ngx_eventfd_event.active = 1;\n    ngx_eventfd_conn.fd = ngx_eventfd;\n    ngx_eventfd_conn.read = &ngx_eventfd_event;\n    ngx_eventfd_conn.log = cycle->log;\n\n    ee.events = EPOLLIN|EPOLLET;\n    ee.data.ptr = &ngx_eventfd_conn;\n\n    if (epoll_ctl(ep, EPOLL_CTL_ADD, ngx_eventfd, &ee) != -1) {\n        return;\n    }\n\n    ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                  \"epoll_ctl(EPOLL_CTL_ADD, eventfd) failed\");\n\n    if (io_destroy(ngx_aio_ctx) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                      \"io_destroy() failed\");\n    }\n\nfailed:\n\n    if (close(ngx_eventfd) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                      \"eventfd close() failed\");\n    }\n\n    ngx_eventfd = -1;\n    ngx_aio_ctx = 0;\n    ngx_file_aio = 0;\n}\n\n#endif\n\n\nstatic ngx_int_t\nngx_epoll_init(ngx_cycle_t *cycle, ngx_msec_t timer)\n{\n    ngx_epoll_conf_t  *epcf;\n\n    epcf = ngx_event_get_conf(cycle->conf_ctx, ngx_epoll_module);\n\n    if (ep == -1) {\n        ep = epoll_create(cycle->connection_n / 2);\n\n        if (ep == -1) {\n            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                          \"epoll_create() failed\");\n            return NGX_ERROR;\n        }\n\n#if (NGX_HAVE_EVENTFD)\n        if (ngx_epoll_notify_init(cycle->log) != NGX_OK) {\n            ngx_epoll_module_ctx.actions.notify = NULL;\n        }\n#endif\n\n#if (NGX_HAVE_FILE_AIO)\n        ngx_epoll_aio_init(cycle, epcf);\n#endif\n\n#if (NGX_HAVE_EPOLLRDHUP)\n        ngx_epoll_test_rdhup(cycle);\n#endif\n    }\n\n    if (nevents < epcf->events) {\n        if (event_list) {\n            ngx_free(event_list);\n        }\n\n        event_list = ngx_alloc(sizeof(struct epoll_event) * epcf->events,\n                               cycle->log);\n        if (event_list == NULL) {\n            return NGX_ERROR;\n        }\n    }\n\n    nevents = epcf->events;\n\n    ngx_io = ngx_os_io;\n\n    ngx_event_actions = ngx_epoll_module_ctx.actions;\n\n#if (NGX_HAVE_CLEAR_EVENT)\n    ngx_event_flags = NGX_USE_CLEAR_EVENT\n#else\n    ngx_event_flags = NGX_USE_LEVEL_EVENT\n#endif\n                      |NGX_USE_GREEDY_EVENT\n                      |NGX_USE_EPOLL_EVENT;\n\n    return NGX_OK;\n}\n\n\n#if (NGX_HAVE_EVENTFD)\n\nstatic ngx_int_t\nngx_epoll_notify_init(ngx_log_t *log)\n{\n    struct epoll_event  ee;\n\n#if (NGX_HAVE_SYS_EVENTFD_H)\n    notify_fd = eventfd(0, 0);\n#else\n    notify_fd = syscall(SYS_eventfd, 0);\n#endif\n\n    if (notify_fd == -1) {\n        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, \"eventfd() failed\");\n        return NGX_ERROR;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, log, 0,\n                   \"notify eventfd: %d\", notify_fd);\n\n    notify_event.handler = ngx_epoll_notify_handler;\n    notify_event.log = log;\n    notify_event.active = 1;\n\n    notify_conn.fd = notify_fd;\n    notify_conn.read = &notify_event;\n    notify_conn.log = log;\n\n    ee.events = EPOLLIN|EPOLLET;\n    ee.data.ptr = &notify_conn;\n\n    if (epoll_ctl(ep, EPOLL_CTL_ADD, notify_fd, &ee) == -1) {\n        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,\n                      \"epoll_ctl(EPOLL_CTL_ADD, eventfd) failed\");\n\n        if (close(notify_fd) == -1) {\n            ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,\n                            \"eventfd close() failed\");\n        }\n\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_epoll_notify_handler(ngx_event_t *ev)\n{\n    ssize_t               n;\n    uint64_t              count;\n    ngx_err_t             err;\n    ngx_event_handler_pt  handler;\n\n    if (++ev->index == NGX_MAX_UINT32_VALUE) {\n        ev->index = 0;\n\n        n = read(notify_fd, &count, sizeof(uint64_t));\n\n        err = ngx_errno;\n\n        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ev->log, 0,\n                       \"read() eventfd %d: %z count:%uL\", notify_fd, n, count);\n\n        if ((size_t) n != sizeof(uint64_t)) {\n            ngx_log_error(NGX_LOG_ALERT, ev->log, err,\n                          \"read() eventfd %d failed\", notify_fd);\n        }\n    }\n\n    handler = ev->data;\n    handler(ev);\n}\n\n#endif\n\n\n#if (NGX_HAVE_EPOLLRDHUP)\n\nstatic void\nngx_epoll_test_rdhup(ngx_cycle_t *cycle)\n{\n    int                 s[2], events;\n    struct epoll_event  ee;\n\n    if (socketpair(AF_UNIX, SOCK_STREAM, 0, s) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                      \"socketpair() failed\");\n        return;\n    }\n\n    ee.events = EPOLLET|EPOLLIN|EPOLLRDHUP;\n\n    if (epoll_ctl(ep, EPOLL_CTL_ADD, s[0], &ee) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                      \"epoll_ctl() failed\");\n        goto failed;\n    }\n\n    if (close(s[1]) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                      \"close() failed\");\n        s[1] = -1;\n        goto failed;\n    }\n\n    s[1] = -1;\n\n    events = epoll_wait(ep, &ee, 1, 5000);\n\n    if (events == -1) {\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                      \"epoll_wait() failed\");\n        goto failed;\n    }\n\n    if (events) {\n        ngx_use_epoll_rdhup = ee.events & EPOLLRDHUP;\n\n    } else {\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, NGX_ETIMEDOUT,\n                      \"epoll_wait() timed out\");\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                   \"testing the EPOLLRDHUP flag: %s\",\n                   ngx_use_epoll_rdhup ? \"success\" : \"fail\");\n\nfailed:\n\n    if (s[1] != -1 && close(s[1]) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                      \"close() failed\");\n    }\n\n    if (close(s[0]) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                      \"close() failed\");\n    }\n}\n\n#endif\n\n\nstatic void\nngx_epoll_done(ngx_cycle_t *cycle)\n{\n    if (close(ep) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                      \"epoll close() failed\");\n    }\n\n    ep = -1;\n\n#if (NGX_HAVE_EVENTFD)\n\n    if (close(notify_fd) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                      \"eventfd close() failed\");\n    }\n\n    notify_fd = -1;\n\n#endif\n\n#if (NGX_HAVE_FILE_AIO)\n\n    if (ngx_eventfd != -1) {\n\n        if (io_destroy(ngx_aio_ctx) == -1) {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                          \"io_destroy() failed\");\n        }\n\n        if (close(ngx_eventfd) == -1) {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                          \"eventfd close() failed\");\n        }\n\n        ngx_eventfd = -1;\n    }\n\n    ngx_aio_ctx = 0;\n\n#endif\n\n    ngx_free(event_list);\n\n    event_list = NULL;\n    nevents = 0;\n}\n\n\nstatic ngx_int_t\nngx_epoll_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)\n{\n    int                  op;\n    uint32_t             events, prev;\n    ngx_event_t         *e;\n    ngx_connection_t    *c;\n    struct epoll_event   ee;\n\n    c = ev->data;\n\n    events = (uint32_t) event;\n\n    if (event == NGX_READ_EVENT) {\n        e = c->write;\n        prev = EPOLLOUT;\n#if (NGX_READ_EVENT != EPOLLIN|EPOLLRDHUP)\n        events = EPOLLIN|EPOLLRDHUP;\n#endif\n\n    } else {\n        e = c->read;\n        prev = EPOLLIN|EPOLLRDHUP;\n#if (NGX_WRITE_EVENT != EPOLLOUT)\n        events = EPOLLOUT;\n#endif\n    }\n\n    if (e->active) {\n        op = EPOLL_CTL_MOD;\n        events |= prev;\n\n    } else {\n        op = EPOLL_CTL_ADD;\n    }\n\n#if (NGX_HAVE_EPOLLEXCLUSIVE && NGX_HAVE_EPOLLRDHUP)\n    if (flags & NGX_EXCLUSIVE_EVENT) {\n        events &= ~EPOLLRDHUP;\n    }\n#endif\n\n    ee.events = events | (uint32_t) flags;\n    ee.data.ptr = (void *) ((uintptr_t) c | ev->instance);\n\n    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ev->log, 0,\n                   \"epoll add event: fd:%d op:%d ev:%08XD\",\n                   c->fd, op, ee.events);\n\n    if (epoll_ctl(ep, op, c->fd, &ee) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,\n                      \"epoll_ctl(%d, %d) failed\", op, c->fd);\n        return NGX_ERROR;\n    }\n\n    ev->active = 1;\n#if 0\n    ev->oneshot = (flags & NGX_ONESHOT_EVENT) ? 1 : 0;\n#endif\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_epoll_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)\n{\n    int                  op;\n    uint32_t             prev;\n    ngx_event_t         *e;\n    ngx_connection_t    *c;\n    struct epoll_event   ee;\n\n    /*\n     * when the file descriptor is closed, the epoll automatically deletes\n     * it from its queue, so we do not need to delete explicitly the event\n     * before the closing the file descriptor\n     */\n\n    if (flags & NGX_CLOSE_EVENT) {\n        ev->active = 0;\n        return NGX_OK;\n    }\n\n    c = ev->data;\n\n    if (event == NGX_READ_EVENT) {\n        e = c->write;\n        prev = EPOLLOUT;\n\n    } else {\n        e = c->read;\n        prev = EPOLLIN|EPOLLRDHUP;\n    }\n\n    if (e->active) {\n        op = EPOLL_CTL_MOD;\n        ee.events = prev | (uint32_t) flags;\n        ee.data.ptr = (void *) ((uintptr_t) c | ev->instance);\n\n    } else {\n        op = EPOLL_CTL_DEL;\n        ee.events = 0;\n        ee.data.ptr = NULL;\n    }\n\n    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ev->log, 0,\n                   \"epoll del event: fd:%d op:%d ev:%08XD\",\n                   c->fd, op, ee.events);\n\n    if (epoll_ctl(ep, op, c->fd, &ee) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,\n                      \"epoll_ctl(%d, %d) failed\", op, c->fd);\n        return NGX_ERROR;\n    }\n\n    ev->active = 0;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_epoll_add_connection(ngx_connection_t *c)\n{\n    struct epoll_event  ee;\n\n    ee.events = EPOLLIN|EPOLLOUT|EPOLLET|EPOLLRDHUP;\n    ee.data.ptr = (void *) ((uintptr_t) c | c->read->instance);\n\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"epoll add connection: fd:%d ev:%08XD\", c->fd, ee.events);\n\n    if (epoll_ctl(ep, EPOLL_CTL_ADD, c->fd, &ee) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,\n                      \"epoll_ctl(EPOLL_CTL_ADD, %d) failed\", c->fd);\n        return NGX_ERROR;\n    }\n\n    c->read->active = 1;\n    c->write->active = 1;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_epoll_del_connection(ngx_connection_t *c, ngx_uint_t flags)\n{\n    int                 op;\n    struct epoll_event  ee;\n\n    /*\n     * when the file descriptor is closed the epoll automatically deletes\n     * it from its queue so we do not need to delete explicitly the event\n     * before the closing the file descriptor\n     */\n\n    if (flags & NGX_CLOSE_EVENT) {\n        c->read->active = 0;\n        c->write->active = 0;\n        return NGX_OK;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"epoll del connection: fd:%d\", c->fd);\n\n    op = EPOLL_CTL_DEL;\n    ee.events = 0;\n    ee.data.ptr = NULL;\n\n    if (epoll_ctl(ep, op, c->fd, &ee) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,\n                      \"epoll_ctl(%d, %d) failed\", op, c->fd);\n        return NGX_ERROR;\n    }\n\n    c->read->active = 0;\n    c->write->active = 0;\n\n    return NGX_OK;\n}\n\n#if (NGX_SSL && NGX_SSL_ASYNC)\nstatic ngx_int_t\nngx_epoll_add_async_connection(ngx_connection_t *c)\n{\n    struct epoll_event  ee;\n\n    ee.events = EPOLLIN|EPOLLOUT|EPOLLET|EPOLLRDHUP;\n    ee.data.ptr = (void *) ((uintptr_t) c | (c->async->async << 1) | c->async->instance);\n\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"epoll add async connection: fd:%d ev:%08XD\", c->async_fd, ee.events);\n    if (epoll_ctl(ep, EPOLL_CTL_ADD, c->async_fd, &ee) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,\n                      \"async add conn epoll_ctl(EPOLL_CTL_ADD, %d) failed\", c->async_fd);\n        return NGX_ERROR;\n    }\n\n    c->async->active = 1;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_epoll_del_async_connection(ngx_connection_t *c, ngx_uint_t flags)\n{\n    int                 op;\n    struct epoll_event  ee;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"epoll del async connection: fd:%d\", c->async_fd);\n\n    op = EPOLL_CTL_DEL;\n    ee.events = 0;\n    ee.data.ptr = NULL;\n    if (epoll_ctl(ep, op, c->async_fd, &ee) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,\n                      \"async del conn epoll_ctl(%d, %d) failed\", op, c->async_fd);\n        c->async_fd = -1;\n        return NGX_ERROR;\n    }\n    c->async_fd = -1;\n    c->async->active = 0;\n\n    return NGX_OK;\n}\n#endif\n\n#if (NGX_HAVE_EVENTFD)\n\nstatic ngx_int_t\nngx_epoll_notify(ngx_event_handler_pt handler)\n{\n    static uint64_t inc = 1;\n\n    notify_event.data = handler;\n\n    if ((size_t) write(notify_fd, &inc, sizeof(uint64_t)) != sizeof(uint64_t)) {\n        ngx_log_error(NGX_LOG_ALERT, notify_event.log, ngx_errno,\n                      \"write() to eventfd %d failed\", notify_fd);\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n#endif\n\n\nstatic ngx_int_t\nngx_epoll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags)\n{\n    int                events;\n    uint32_t           revents;\n    ngx_int_t          instance, i;\n    ngx_uint_t         level;\n    ngx_err_t          err;\n    ngx_event_t       *rev, *wev;\n    ngx_queue_t       *queue;\n    ngx_connection_t  *c;\n#if (NGX_SSL && NGX_SSL_ASYNC)\n    ngx_int_t          async;\n    ngx_event_t       *aev;\n#endif\n\n    /* NGX_TIMER_INFINITE == INFTIM */\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                   \"epoll timer: %M\", timer);\n\n    events = epoll_wait(ep, event_list, (int) nevents, timer);\n\n    err = (events == -1) ? ngx_errno : 0;\n\n    if (flags & NGX_UPDATE_TIME || ngx_event_timer_alarm) {\n        ngx_time_update();\n    }\n\n    if (err) {\n        if (err == NGX_EINTR) {\n\n            if (ngx_event_timer_alarm) {\n                ngx_event_timer_alarm = 0;\n                return NGX_OK;\n            }\n\n            level = NGX_LOG_INFO;\n\n        } else {\n            level = NGX_LOG_ALERT;\n        }\n\n        ngx_log_error(level, cycle->log, err, \"epoll_wait() failed\");\n        return NGX_ERROR;\n    }\n\n    if (events == 0) {\n        if (timer != NGX_TIMER_INFINITE) {\n            return NGX_OK;\n        }\n\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,\n                      \"epoll_wait() returned no events without timeout\");\n        return NGX_ERROR;\n    }\n\n    for (i = 0; i < events; i++) {\n        c = event_list[i].data.ptr;\n\n        instance = (uintptr_t) c & 1;\n#if (NGX_SSL)\n#if (NGX_SSL_ASYNC)\n        async = ((uintptr_t) c & 2) >> 1;\n#endif\n        c = (ngx_connection_t *) ((uintptr_t) c & (uintptr_t) ~3);\n#else\n        c = (ngx_connection_t *) ((uintptr_t) c & (uintptr_t) ~1);\n#endif\n\n        rev = c->read;\n\n        if (c->fd == -1 || rev->instance != instance) {\n\n            /*\n             * the stale event from a file descriptor\n             * that was just closed in this iteration\n             */\n\n            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                           \"epoll: stale event %p\", c);\n            continue;\n        }\n\n        revents = event_list[i].events;\n\n        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                       \"epoll: fd:%d ev:%04XD d:%p\",\n                       c->fd, revents, event_list[i].data.ptr);\n\n        if (revents & (EPOLLERR|EPOLLHUP)) {\n            ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                           \"epoll_wait() error on fd:%d ev:%04XD\",\n                           c->fd, revents);\n\n            /*\n             * if the error events were returned, add EPOLLIN and EPOLLOUT\n             * to handle the events at least in one active handler\n             */\n\n            revents |= EPOLLIN|EPOLLOUT;\n        }\n\n#if 0\n        if (revents & ~(EPOLLIN|EPOLLOUT|EPOLLERR|EPOLLHUP)) {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,\n                          \"strange epoll_wait() events fd:%d ev:%04XD\",\n                          c->fd, revents);\n        }\n#endif\n\n#if (NGX_SSL && NGX_SSL_ASYNC)\n        if ((revents & EPOLLIN) && rev->active && !async) {\n#else\n        if ((revents & EPOLLIN) && rev->active) {\n#endif\n\n#if (NGX_HAVE_EPOLLRDHUP)\n            if (revents & EPOLLRDHUP) {\n                rev->pending_eof = 1;\n            }\n#endif\n\n            rev->ready = 1;\n            rev->available = -1;\n\n            if (flags & NGX_POST_EVENTS) {\n                queue = rev->accept ? &ngx_posted_accept_events\n                                    : &ngx_posted_events;\n\n                ngx_post_event(rev, queue);\n\n            } else {\n                rev->handler(rev);\n            }\n        }\n\n        wev = c->write;\n\n#if (NGX_SSL && NGX_SSL_ASYNC)\n        if ((revents & EPOLLOUT) && wev->active && !async) {\n#else\n        if ((revents & EPOLLOUT) && wev->active) {\n#endif\n\n            if (c->fd == -1 || wev->instance != instance) {\n\n                /*\n                 * the stale event from a file descriptor\n                 * that was just closed in this iteration\n                 */\n\n                ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                               \"epoll: stale event %p\", c);\n                continue;\n            }\n\n            wev->ready = 1;\n#if (NGX_THREADS)\n            wev->complete = 1;\n#endif\n\n            if (flags & NGX_POST_EVENTS) {\n                ngx_post_event(wev, &ngx_posted_events);\n\n            } else {\n                wev->handler(wev);\n            }\n        }\n\n#if (NGX_SSL && NGX_SSL_ASYNC)\n        aev = c->async;\n\n        if ((revents & EPOLLIN) && aev && aev->active && async) {\n\n            if (c->async_fd == -1 || aev->instance!= instance) {\n                /*\n                 * the stale event from a file descriptor\n                 * that was just closed in this iteration\n                 */\n\n                ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                               \"epoll: stale event %p\", c);\n                continue;\n            }\n\n            aev->ready = 1;\n\n            if (flags & NGX_POST_EVENTS) {\n                ngx_post_event(aev, &ngx_posted_events);\n\n            } else {\n                aev->handler(aev);\n            }\n        }\n#endif\n    }\n\n    return NGX_OK;\n}\n\n\n#if (NGX_HAVE_FILE_AIO)\n\nstatic void\nngx_epoll_eventfd_handler(ngx_event_t *ev)\n{\n    int               n, events;\n    long              i;\n    uint64_t          ready;\n    ngx_err_t         err;\n    ngx_event_t      *e;\n    ngx_event_aio_t  *aio;\n    struct io_event   event[64];\n    struct timespec   ts;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0, \"eventfd handler\");\n\n    n = read(ngx_eventfd, &ready, 8);\n\n    err = ngx_errno;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ev->log, 0, \"eventfd: %d\", n);\n\n    if (n != 8) {\n        if (n == -1) {\n            if (err == NGX_EAGAIN) {\n                return;\n            }\n\n            ngx_log_error(NGX_LOG_ALERT, ev->log, err, \"read(eventfd) failed\");\n            return;\n        }\n\n        ngx_log_error(NGX_LOG_ALERT, ev->log, 0,\n                      \"read(eventfd) returned only %d bytes\", n);\n        return;\n    }\n\n    ts.tv_sec = 0;\n    ts.tv_nsec = 0;\n\n    while (ready) {\n\n        events = io_getevents(ngx_aio_ctx, 1, 64, event, &ts);\n\n        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ev->log, 0,\n                       \"io_getevents: %d\", events);\n\n        if (events > 0) {\n            ready -= events;\n\n            for (i = 0; i < events; i++) {\n\n                ngx_log_debug4(NGX_LOG_DEBUG_EVENT, ev->log, 0,\n                               \"io_event: %XL %XL %L %L\",\n                                event[i].data, event[i].obj,\n                                event[i].res, event[i].res2);\n\n                e = (ngx_event_t *) (uintptr_t) event[i].data;\n\n                e->complete = 1;\n                e->active = 0;\n                e->ready = 1;\n\n                aio = e->data;\n                aio->res = event[i].res;\n\n                ngx_post_event(e, &ngx_posted_events);\n            }\n\n            continue;\n        }\n\n        if (events == 0) {\n            return;\n        }\n\n        /* events == -1 */\n        ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,\n                      \"io_getevents() failed\");\n        return;\n    }\n}\n\n#endif\n\n\nstatic void *\nngx_epoll_create_conf(ngx_cycle_t *cycle)\n{\n    ngx_epoll_conf_t  *epcf;\n\n    epcf = ngx_palloc(cycle->pool, sizeof(ngx_epoll_conf_t));\n    if (epcf == NULL) {\n        return NULL;\n    }\n\n    epcf->events = NGX_CONF_UNSET;\n    epcf->aio_requests = NGX_CONF_UNSET;\n\n    return epcf;\n}\n\n\nstatic char *\nngx_epoll_init_conf(ngx_cycle_t *cycle, void *conf)\n{\n    ngx_epoll_conf_t *epcf = conf;\n\n    ngx_conf_init_uint_value(epcf->events, 512);\n    ngx_conf_init_uint_value(epcf->aio_requests, 32);\n\n    return NGX_CONF_OK;\n}\n"
  },
  {
    "path": "src/event/modules/ngx_eventport_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n\n\n#if (NGX_TEST_BUILD_EVENTPORT)\n\n#define ushort_t  u_short\n#define uint_t    u_int\n\n#ifndef CLOCK_REALTIME\n#define CLOCK_REALTIME          0\ntypedef int     clockid_t;\ntypedef void *  timer_t;\n#elif (NGX_DARWIN)\ntypedef void *  timer_t;\n#endif\n\n/* Solaris declarations */\n\n#define PORT_SOURCE_AIO         1\n#define PORT_SOURCE_TIMER       2\n#define PORT_SOURCE_USER        3\n#define PORT_SOURCE_FD          4\n#define PORT_SOURCE_ALERT       5\n#define PORT_SOURCE_MQ          6\n\n#ifndef ETIME\n#define ETIME                   64\n#endif\n\n#define SIGEV_PORT              4\n\ntypedef struct {\n    int         portev_events;  /* event data is source specific */\n    ushort_t    portev_source;  /* event source */\n    ushort_t    portev_pad;     /* port internal use */\n    uintptr_t   portev_object;  /* source specific object */\n    void       *portev_user;    /* user cookie */\n} port_event_t;\n\ntypedef struct  port_notify {\n    int         portnfy_port;   /* bind request(s) to port */\n    void       *portnfy_user;   /* user defined */\n} port_notify_t;\n\n#if (__FreeBSD__ && __FreeBSD_version < 700005) || (NGX_DARWIN)\n\ntypedef struct itimerspec {     /* definition per POSIX.4 */\n    struct timespec it_interval;/* timer period */\n    struct timespec it_value;   /* timer expiration */\n} itimerspec_t;\n\n#endif\n\nint port_create(void);\n\nint port_create(void)\n{\n    return -1;\n}\n\n\nint port_associate(int port, int source, uintptr_t object, int events,\n    void *user);\n\nint port_associate(int port, int source, uintptr_t object, int events,\n    void *user)\n{\n    return -1;\n}\n\n\nint port_dissociate(int port, int source, uintptr_t object);\n\nint port_dissociate(int port, int source, uintptr_t object)\n{\n    return -1;\n}\n\n\nint port_getn(int port, port_event_t list[], uint_t max, uint_t *nget,\n    struct timespec *timeout);\n\nint port_getn(int port, port_event_t list[], uint_t max, uint_t *nget,\n    struct timespec *timeout)\n{\n    return -1;\n}\n\nint port_send(int port, int events, void *user);\n\nint port_send(int port, int events, void *user)\n{\n    return -1;\n}\n\n\nint timer_create(clockid_t clock_id, struct sigevent *evp, timer_t *timerid);\n\nint timer_create(clockid_t clock_id, struct sigevent *evp, timer_t *timerid)\n{\n    return -1;\n}\n\n\nint timer_settime(timer_t timerid, int flags, const struct itimerspec *value,\n    struct itimerspec *ovalue);\n\nint timer_settime(timer_t timerid, int flags, const struct itimerspec *value,\n    struct itimerspec *ovalue)\n{\n    return -1;\n}\n\n\nint timer_delete(timer_t timerid);\n\nint timer_delete(timer_t timerid)\n{\n    return -1;\n}\n\n#endif\n\n\ntypedef struct {\n    ngx_uint_t  events;\n} ngx_eventport_conf_t;\n\n\nstatic ngx_int_t ngx_eventport_init(ngx_cycle_t *cycle, ngx_msec_t timer);\nstatic void ngx_eventport_done(ngx_cycle_t *cycle);\nstatic ngx_int_t ngx_eventport_add_event(ngx_event_t *ev, ngx_int_t event,\n    ngx_uint_t flags);\nstatic ngx_int_t ngx_eventport_del_event(ngx_event_t *ev, ngx_int_t event,\n    ngx_uint_t flags);\nstatic ngx_int_t ngx_eventport_notify(ngx_event_handler_pt handler);\nstatic ngx_int_t ngx_eventport_process_events(ngx_cycle_t *cycle,\n    ngx_msec_t timer, ngx_uint_t flags);\n\nstatic void *ngx_eventport_create_conf(ngx_cycle_t *cycle);\nstatic char *ngx_eventport_init_conf(ngx_cycle_t *cycle, void *conf);\n\nstatic int            ep = -1;\nstatic port_event_t  *event_list;\nstatic ngx_uint_t     nevents;\nstatic timer_t        event_timer = (timer_t) -1;\nstatic ngx_event_t    notify_event;\n\nstatic ngx_str_t      eventport_name = ngx_string(\"eventport\");\n\n\nstatic ngx_command_t  ngx_eventport_commands[] = {\n\n    { ngx_string(\"eventport_events\"),\n      NGX_EVENT_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_num_slot,\n      0,\n      offsetof(ngx_eventport_conf_t, events),\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_event_module_t  ngx_eventport_module_ctx = {\n    &eventport_name,\n    ngx_eventport_create_conf,             /* create configuration */\n    ngx_eventport_init_conf,               /* init configuration */\n\n    {\n        ngx_eventport_add_event,           /* add an event */\n        ngx_eventport_del_event,           /* delete an event */\n        ngx_eventport_add_event,           /* enable an event */\n        ngx_eventport_del_event,           /* disable an event */\n        NULL,                              /* add an connection */\n        NULL,                              /* delete an connection */\n        ngx_eventport_notify,              /* trigger a notify */\n        ngx_eventport_process_events,      /* process the events */\n        ngx_eventport_init,                /* init the events */\n        ngx_eventport_done,                /* done the events */\n#if (NGX_SSL && NGX_SSL_ASYNC)\n        NULL,                              /* add an async conn */\n        NULL,                              /* del an async conn */\n#endif\n    }\n\n};\n\nngx_module_t  ngx_eventport_module = {\n    NGX_MODULE_V1,\n    &ngx_eventport_module_ctx,             /* module context */\n    ngx_eventport_commands,                /* module directives */\n    NGX_EVENT_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_eventport_init(ngx_cycle_t *cycle, ngx_msec_t timer)\n{\n    port_notify_t          pn;\n    struct itimerspec      its;\n    struct sigevent        sev;\n    ngx_eventport_conf_t  *epcf;\n\n    epcf = ngx_event_get_conf(cycle->conf_ctx, ngx_eventport_module);\n\n    if (ep == -1) {\n        ep = port_create();\n\n        if (ep == -1) {\n            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                          \"port_create() failed\");\n            return NGX_ERROR;\n        }\n\n        notify_event.active = 1;\n        notify_event.log = cycle->log;\n    }\n\n    if (nevents < epcf->events) {\n        if (event_list) {\n            ngx_free(event_list);\n        }\n\n        event_list = ngx_alloc(sizeof(port_event_t) * epcf->events,\n                               cycle->log);\n        if (event_list == NULL) {\n            return NGX_ERROR;\n        }\n    }\n\n    ngx_event_flags = NGX_USE_EVENTPORT_EVENT;\n\n    if (timer) {\n        ngx_memzero(&pn, sizeof(port_notify_t));\n        pn.portnfy_port = ep;\n\n        ngx_memzero(&sev, sizeof(struct sigevent));\n        sev.sigev_notify = SIGEV_PORT;\n        sev.sigev_value.sival_ptr = &pn;\n\n        if (timer_create(CLOCK_REALTIME, &sev, &event_timer) == -1) {\n            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                          \"timer_create() failed\");\n            return NGX_ERROR;\n        }\n\n        its.it_interval.tv_sec = timer / 1000;\n        its.it_interval.tv_nsec = (timer % 1000) * 1000000;\n        its.it_value.tv_sec = timer / 1000;\n        its.it_value.tv_nsec = (timer % 1000) * 1000000;\n\n        if (timer_settime(event_timer, 0, &its, NULL) == -1) {\n            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                          \"timer_settime() failed\");\n            return NGX_ERROR;\n        }\n\n        ngx_event_flags |= NGX_USE_TIMER_EVENT;\n    }\n\n    nevents = epcf->events;\n\n    ngx_io = ngx_os_io;\n\n    ngx_event_actions = ngx_eventport_module_ctx.actions;\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_eventport_done(ngx_cycle_t *cycle)\n{\n    if (event_timer != (timer_t) -1) {\n        if (timer_delete(event_timer) == -1) {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                          \"timer_delete() failed\");\n        }\n\n        event_timer = (timer_t) -1;\n    }\n\n    if (close(ep) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                      \"close() event port failed\");\n    }\n\n    ep = -1;\n\n    ngx_free(event_list);\n\n    event_list = NULL;\n    nevents = 0;\n}\n\n\nstatic ngx_int_t\nngx_eventport_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)\n{\n    ngx_int_t          events, prev;\n    ngx_event_t       *e;\n    ngx_connection_t  *c;\n\n    c = ev->data;\n\n    events = event;\n\n    if (event == NGX_READ_EVENT) {\n        e = c->write;\n        prev = POLLOUT;\n#if (NGX_READ_EVENT != POLLIN)\n        events = POLLIN;\n#endif\n\n    } else {\n        e = c->read;\n        prev = POLLIN;\n#if (NGX_WRITE_EVENT != POLLOUT)\n        events = POLLOUT;\n#endif\n    }\n\n    if (e->oneshot) {\n        events |= prev;\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,\n                   \"eventport add event: fd:%d ev:%04Xi\", c->fd, events);\n\n    if (port_associate(ep, PORT_SOURCE_FD, c->fd, events,\n                       (void *) ((uintptr_t) ev | ev->instance))\n        == -1)\n    {\n        ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,\n                      \"port_associate() failed\");\n        return NGX_ERROR;\n    }\n\n    ev->active = 1;\n    ev->oneshot = 1;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_eventport_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)\n{\n    ngx_event_t       *e;\n    ngx_connection_t  *c;\n\n    /*\n     * when the file descriptor is closed, the event port automatically\n     * dissociates it from the port, so we do not need to dissociate explicitly\n     * the event before the closing the file descriptor\n     */\n\n    if (flags & NGX_CLOSE_EVENT) {\n        ev->active = 0;\n        ev->oneshot = 0;\n        return NGX_OK;\n    }\n\n    c = ev->data;\n\n    if (event == NGX_READ_EVENT) {\n        e = c->write;\n        event = POLLOUT;\n\n    } else {\n        e = c->read;\n        event = POLLIN;\n    }\n\n    if (e->oneshot) {\n        ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,\n                       \"eventport change event: fd:%d ev:%04Xi\", c->fd, event);\n\n        if (port_associate(ep, PORT_SOURCE_FD, c->fd, event,\n                           (void *) ((uintptr_t) ev | ev->instance))\n            == -1)\n        {\n            ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,\n                          \"port_associate() failed\");\n            return NGX_ERROR;\n        }\n\n    } else if (ev->active) {\n        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ev->log, 0,\n                       \"eventport del event: fd:%d\", c->fd);\n\n        if (port_dissociate(ep, PORT_SOURCE_FD, c->fd) == -1) {\n            ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,\n                          \"port_dissociate() failed\");\n            return NGX_ERROR;\n        }\n    }\n\n    ev->active = 0;\n    ev->oneshot = 0;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_eventport_notify(ngx_event_handler_pt handler)\n{\n    notify_event.handler = handler;\n\n    if (port_send(ep, 0, &notify_event) != 0) {\n        ngx_log_error(NGX_LOG_ALERT, notify_event.log, ngx_errno,\n                      \"port_send() failed\");\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_eventport_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,\n    ngx_uint_t flags)\n{\n    int                 n, revents;\n    u_int               events;\n    ngx_err_t           err;\n    ngx_int_t           instance;\n    ngx_uint_t          i, level;\n    ngx_event_t        *ev, *rev, *wev;\n    ngx_queue_t        *queue;\n    ngx_connection_t   *c;\n    struct timespec     ts, *tp;\n\n    if (timer == NGX_TIMER_INFINITE) {\n        tp = NULL;\n\n    } else {\n        ts.tv_sec = timer / 1000;\n        ts.tv_nsec = (timer % 1000) * 1000000;\n        tp = &ts;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                   \"eventport timer: %M\", timer);\n\n    events = 1;\n\n    n = port_getn(ep, event_list, (u_int) nevents, &events, tp);\n\n    err = ngx_errno;\n\n    if (flags & NGX_UPDATE_TIME) {\n        ngx_time_update();\n    }\n\n    if (n == -1) {\n        if (err == ETIME) {\n            if (timer != NGX_TIMER_INFINITE) {\n                return NGX_OK;\n            }\n\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,\n                          \"port_getn() returned no events without timeout\");\n            return NGX_ERROR;\n        }\n\n        level = (err == NGX_EINTR) ? NGX_LOG_INFO : NGX_LOG_ALERT;\n        ngx_log_error(level, cycle->log, err, \"port_getn() failed\");\n        return NGX_ERROR;\n    }\n\n    if (events == 0) {\n        if (timer != NGX_TIMER_INFINITE) {\n            return NGX_OK;\n        }\n\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,\n                      \"port_getn() returned no events without timeout\");\n        return NGX_ERROR;\n    }\n\n    for (i = 0; i < events; i++) {\n\n        if (event_list[i].portev_source == PORT_SOURCE_TIMER) {\n            ngx_time_update();\n            continue;\n        }\n\n        ev = event_list[i].portev_user;\n\n        switch (event_list[i].portev_source) {\n\n        case PORT_SOURCE_FD:\n\n            instance = (uintptr_t) ev & 1;\n            ev = (ngx_event_t *) ((uintptr_t) ev & (uintptr_t) ~1);\n\n            if (ev->closed || ev->instance != instance) {\n\n                /*\n                 * the stale event from a file descriptor\n                 * that was just closed in this iteration\n                 */\n\n                ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                               \"eventport: stale event %p\", ev);\n                continue;\n            }\n\n            revents = event_list[i].portev_events;\n\n            ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                           \"eventport: fd:%d, ev:%04Xd\",\n                           (int) event_list[i].portev_object, revents);\n\n            if (revents & (POLLERR|POLLHUP|POLLNVAL)) {\n                ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                               \"port_getn() error fd:%d ev:%04Xd\",\n                               (int) event_list[i].portev_object, revents);\n            }\n\n            if (revents & ~(POLLIN|POLLOUT|POLLERR|POLLHUP|POLLNVAL)) {\n                ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,\n                              \"strange port_getn() events fd:%d ev:%04Xd\",\n                              (int) event_list[i].portev_object, revents);\n            }\n\n            if (revents & (POLLERR|POLLHUP|POLLNVAL)) {\n\n                /*\n                 * if the error events were returned, add POLLIN and POLLOUT\n                 * to handle the events at least in one active handler\n                 */\n\n                revents |= POLLIN|POLLOUT;\n            }\n\n            c = ev->data;\n            rev = c->read;\n            wev = c->write;\n\n            rev->active = 0;\n            wev->active = 0;\n\n            if (revents & POLLIN) {\n                rev->ready = 1;\n                rev->available = -1;\n\n                if (flags & NGX_POST_EVENTS) {\n                    queue = rev->accept ? &ngx_posted_accept_events\n                                        : &ngx_posted_events;\n\n                    ngx_post_event(rev, queue);\n\n                } else {\n                    rev->handler(rev);\n\n                    if (ev->closed || ev->instance != instance) {\n                        continue;\n                    }\n                }\n\n                if (rev->accept) {\n                    if (ngx_use_accept_mutex) {\n                        ngx_accept_events = 1;\n                        continue;\n                    }\n\n                    if (port_associate(ep, PORT_SOURCE_FD, c->fd, POLLIN,\n                                       (void *) ((uintptr_t) ev | ev->instance))\n                        == -1)\n                    {\n                        ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,\n                                      \"port_associate() failed\");\n                        return NGX_ERROR;\n                    }\n                }\n            }\n\n            if (revents & POLLOUT) {\n                wev->ready = 1;\n\n                if (flags & NGX_POST_EVENTS) {\n                    ngx_post_event(wev, &ngx_posted_events);\n\n                } else {\n                    wev->handler(wev);\n                }\n            }\n\n            continue;\n\n        case PORT_SOURCE_USER:\n\n            ev->handler(ev);\n\n            continue;\n\n        default:\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,\n                          \"unexpected eventport object %d\",\n                          (int) event_list[i].portev_object);\n            continue;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void *\nngx_eventport_create_conf(ngx_cycle_t *cycle)\n{\n    ngx_eventport_conf_t  *epcf;\n\n    epcf = ngx_palloc(cycle->pool, sizeof(ngx_eventport_conf_t));\n    if (epcf == NULL) {\n        return NULL;\n    }\n\n    epcf->events = NGX_CONF_UNSET;\n\n    return epcf;\n}\n\n\nstatic char *\nngx_eventport_init_conf(ngx_cycle_t *cycle, void *conf)\n{\n    ngx_eventport_conf_t *epcf = conf;\n\n    ngx_conf_init_uint_value(epcf->events, 32);\n\n    return NGX_CONF_OK;\n}\n"
  },
  {
    "path": "src/event/modules/ngx_iocp_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n#include <ngx_iocp_module.h>\n\n\nstatic ngx_int_t ngx_iocp_init(ngx_cycle_t *cycle, ngx_msec_t timer);\nstatic ngx_thread_value_t __stdcall ngx_iocp_timer(void *data);\nstatic void ngx_iocp_done(ngx_cycle_t *cycle);\nstatic ngx_int_t ngx_iocp_add_event(ngx_event_t *ev, ngx_int_t event,\n    ngx_uint_t key);\nstatic ngx_int_t ngx_iocp_del_connection(ngx_connection_t *c, ngx_uint_t flags);\nstatic ngx_int_t ngx_iocp_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,\n    ngx_uint_t flags);\nstatic void *ngx_iocp_create_conf(ngx_cycle_t *cycle);\nstatic char *ngx_iocp_init_conf(ngx_cycle_t *cycle, void *conf);\n\n\nstatic ngx_str_t      iocp_name = ngx_string(\"iocp\");\n\nstatic ngx_command_t  ngx_iocp_commands[] = {\n\n    { ngx_string(\"iocp_threads\"),\n      NGX_EVENT_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_num_slot,\n      0,\n      offsetof(ngx_iocp_conf_t, threads),\n      NULL },\n\n    { ngx_string(\"post_acceptex\"),\n      NGX_EVENT_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_num_slot,\n      0,\n      offsetof(ngx_iocp_conf_t, post_acceptex),\n      NULL },\n\n    { ngx_string(\"acceptex_read\"),\n      NGX_EVENT_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      0,\n      offsetof(ngx_iocp_conf_t, acceptex_read),\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_event_module_t  ngx_iocp_module_ctx = {\n    &iocp_name,\n    ngx_iocp_create_conf,                  /* create configuration */\n    ngx_iocp_init_conf,                    /* init configuration */\n\n    {\n        ngx_iocp_add_event,                /* add an event */\n        NULL,                              /* delete an event */\n        NULL,                              /* enable an event */\n        NULL,                              /* disable an event */\n        NULL,                              /* add an connection */\n        ngx_iocp_del_connection,           /* delete an connection */\n        NULL,                              /* trigger a notify */\n        ngx_iocp_process_events,           /* process the events */\n        ngx_iocp_init,                     /* init the events */\n        ngx_iocp_done                      /* done the events */\n    }\n\n};\n\nngx_module_t  ngx_iocp_module = {\n    NGX_MODULE_V1,\n    &ngx_iocp_module_ctx,                  /* module context */\n    ngx_iocp_commands,                     /* module directives */\n    NGX_EVENT_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\nngx_os_io_t ngx_iocp_io = {\n    ngx_overlapped_wsarecv,\n    NULL,\n    ngx_udp_overlapped_wsarecv,\n    NULL,\n    NULL,\n    NULL,\n    ngx_overlapped_wsasend_chain,\n    0\n};\n\n\nstatic HANDLE      iocp;\nstatic ngx_tid_t   timer_thread;\nstatic ngx_msec_t  msec;\n\n\nstatic ngx_int_t\nngx_iocp_init(ngx_cycle_t *cycle, ngx_msec_t timer)\n{\n    ngx_iocp_conf_t  *cf;\n\n    cf = ngx_event_get_conf(cycle->conf_ctx, ngx_iocp_module);\n\n    if (iocp == NULL) {\n        iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0,\n                                      cf->threads);\n    }\n\n    if (iocp == NULL) {\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                      \"CreateIoCompletionPort() failed\");\n        return NGX_ERROR;\n    }\n\n    ngx_io = ngx_iocp_io;\n\n    ngx_event_actions = ngx_iocp_module_ctx.actions;\n\n    ngx_event_flags = NGX_USE_IOCP_EVENT;\n\n    if (timer == 0) {\n        return NGX_OK;\n    }\n\n    /*\n     * The waitable timer could not be used, because\n     * GetQueuedCompletionStatus() does not set a thread to alertable state\n     */\n\n    if (timer_thread == NULL) {\n\n        msec = timer;\n\n        if (ngx_create_thread(&timer_thread, ngx_iocp_timer, &msec, cycle->log)\n            != 0)\n        {\n            return NGX_ERROR;\n        }\n    }\n\n    ngx_event_flags |= NGX_USE_TIMER_EVENT;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_thread_value_t __stdcall\nngx_iocp_timer(void *data)\n{\n    ngx_msec_t  timer = *(ngx_msec_t *) data;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ngx_cycle->log, 0,\n                   \"THREAD %p %p\", &msec, data);\n\n    for ( ;; ) {\n        Sleep(timer);\n\n        ngx_time_update();\n#if 1\n        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ngx_cycle->log, 0, \"timer\");\n#endif\n    }\n\n#if defined(__WATCOMC__) || defined(__GNUC__)\n    return 0;\n#endif\n}\n\n\nstatic void\nngx_iocp_done(ngx_cycle_t *cycle)\n{\n    if (CloseHandle(iocp) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                      \"iocp CloseHandle() failed\");\n    }\n\n    iocp = NULL;\n}\n\n\nstatic ngx_int_t\nngx_iocp_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t key)\n{\n    ngx_connection_t  *c;\n\n    c = (ngx_connection_t *) ev->data;\n\n    c->read->active = 1;\n    c->write->active = 1;\n\n    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ev->log, 0,\n                   \"iocp add: fd:%d k:%ui ov:%p\", c->fd, key, &ev->ovlp);\n\n    if (CreateIoCompletionPort((HANDLE) c->fd, iocp, key, 0) == NULL) {\n        ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,\n                      \"CreateIoCompletionPort() failed\");\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_iocp_del_connection(ngx_connection_t *c, ngx_uint_t flags)\n{\n#if 0\n    if (flags & NGX_CLOSE_EVENT) {\n        return NGX_OK;\n    }\n\n    if (CancelIo((HANDLE) c->fd) == 0) {\n        ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno, \"CancelIo() failed\");\n        return NGX_ERROR;\n    }\n#endif\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_iocp_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags)\n{\n    int                rc;\n    u_int              key;\n    u_long             bytes;\n    ngx_err_t          err;\n    ngx_msec_t         delta;\n    ngx_event_t       *ev;\n    ngx_event_ovlp_t  *ovlp;\n\n    if (timer == NGX_TIMER_INFINITE) {\n        timer = INFINITE;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, \"iocp timer: %M\", timer);\n\n    rc = GetQueuedCompletionStatus(iocp, &bytes, (PULONG_PTR) &key,\n                                   (LPOVERLAPPED *) &ovlp, (u_long) timer);\n\n    if (rc == 0) {\n        err = ngx_errno;\n    } else {\n        err = 0;\n    }\n\n    delta = ngx_current_msec;\n\n    if (flags & NGX_UPDATE_TIME) {\n        ngx_time_update();\n    }\n\n    ngx_log_debug4(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                   \"iocp: %d b:%d k:%d ov:%p\", rc, bytes, key, ovlp);\n\n    if (timer != INFINITE) {\n        delta = ngx_current_msec - delta;\n\n        ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                       \"iocp timer: %M, delta: %M\", timer, delta);\n    }\n\n    if (err) {\n        if (ovlp == NULL) {\n            if (err != WAIT_TIMEOUT) {\n                ngx_log_error(NGX_LOG_ALERT, cycle->log, err,\n                              \"GetQueuedCompletionStatus() failed\");\n\n                return NGX_ERROR;\n            }\n\n            return NGX_OK;\n        }\n\n        ovlp->error = err;\n    }\n\n    if (ovlp == NULL) {\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,\n                      \"GetQueuedCompletionStatus() returned no operation\");\n        return NGX_ERROR;\n    }\n\n\n    ev = ovlp->event;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, err, \"iocp event:%p\", ev);\n\n\n    if (err == ERROR_NETNAME_DELETED /* the socket was closed */\n        || err == ERROR_OPERATION_ABORTED /* the operation was canceled */)\n    {\n\n        /*\n         * the WSA_OPERATION_ABORTED completion notification\n         * for a file descriptor that was closed\n         */\n\n        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, err,\n                       \"iocp: aborted event %p\", ev);\n\n        return NGX_OK;\n    }\n\n    if (err) {\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, err,\n                      \"GetQueuedCompletionStatus() returned operation error\");\n    }\n\n    switch (key) {\n\n    case NGX_IOCP_ACCEPT:\n        if (bytes) {\n            ev->ready = 1;\n        }\n        break;\n\n    case NGX_IOCP_IO:\n        ev->complete = 1;\n        ev->ready = 1;\n        break;\n\n    case NGX_IOCP_CONNECT:\n        ev->ready = 1;\n    }\n\n    ev->available = bytes;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                   \"iocp event handler: %p\", ev->handler);\n\n    ev->handler(ev);\n\n    return NGX_OK;\n}\n\n\nstatic void *\nngx_iocp_create_conf(ngx_cycle_t *cycle)\n{\n    ngx_iocp_conf_t  *cf;\n\n    cf = ngx_palloc(cycle->pool, sizeof(ngx_iocp_conf_t));\n    if (cf == NULL) {\n        return NULL;\n    }\n\n    cf->threads = NGX_CONF_UNSET;\n    cf->post_acceptex = NGX_CONF_UNSET;\n    cf->acceptex_read = NGX_CONF_UNSET;\n\n    return cf;\n}\n\n\nstatic char *\nngx_iocp_init_conf(ngx_cycle_t *cycle, void *conf)\n{\n    ngx_iocp_conf_t *cf = conf;\n\n    ngx_conf_init_value(cf->threads, 0);\n    ngx_conf_init_value(cf->post_acceptex, 10);\n    ngx_conf_init_value(cf->acceptex_read, 1);\n\n    return NGX_CONF_OK;\n}\n"
  },
  {
    "path": "src/event/modules/ngx_iocp_module.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_IOCP_MODULE_H_INCLUDED_\n#define _NGX_IOCP_MODULE_H_INCLUDED_\n\n\ntypedef struct {\n    int  threads;\n    int  post_acceptex;\n    int  acceptex_read;\n} ngx_iocp_conf_t;\n\n\nextern ngx_module_t  ngx_iocp_module;\n\n\n#endif /* _NGX_IOCP_MODULE_H_INCLUDED_ */\n"
  },
  {
    "path": "src/event/modules/ngx_kqueue_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n\n\ntypedef struct {\n    ngx_uint_t  changes;\n    ngx_uint_t  events;\n} ngx_kqueue_conf_t;\n\n\nstatic ngx_int_t ngx_kqueue_init(ngx_cycle_t *cycle, ngx_msec_t timer);\n#ifdef EVFILT_USER\nstatic ngx_int_t ngx_kqueue_notify_init(ngx_log_t *log);\n#endif\nstatic void ngx_kqueue_done(ngx_cycle_t *cycle);\nstatic ngx_int_t ngx_kqueue_add_event(ngx_event_t *ev, ngx_int_t event,\n    ngx_uint_t flags);\nstatic ngx_int_t ngx_kqueue_del_event(ngx_event_t *ev, ngx_int_t event,\n    ngx_uint_t flags);\nstatic ngx_int_t ngx_kqueue_set_event(ngx_event_t *ev, ngx_int_t filter,\n    ngx_uint_t flags);\n#ifdef EVFILT_USER\nstatic ngx_int_t ngx_kqueue_notify(ngx_event_handler_pt handler);\n#endif\nstatic ngx_int_t ngx_kqueue_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,\n    ngx_uint_t flags);\nstatic ngx_inline void ngx_kqueue_dump_event(ngx_log_t *log,\n    struct kevent *kev);\n\nstatic void *ngx_kqueue_create_conf(ngx_cycle_t *cycle);\nstatic char *ngx_kqueue_init_conf(ngx_cycle_t *cycle, void *conf);\n\n\nint                    ngx_kqueue = -1;\n\nstatic struct kevent  *change_list;\nstatic struct kevent  *event_list;\nstatic ngx_uint_t      max_changes, nchanges, nevents;\n\n#ifdef EVFILT_USER\nstatic ngx_event_t     notify_event;\nstatic struct kevent   notify_kev;\n#endif\n\n\nstatic ngx_str_t      kqueue_name = ngx_string(\"kqueue\");\n\nstatic ngx_command_t  ngx_kqueue_commands[] = {\n\n    { ngx_string(\"kqueue_changes\"),\n      NGX_EVENT_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_num_slot,\n      0,\n      offsetof(ngx_kqueue_conf_t, changes),\n      NULL },\n\n    { ngx_string(\"kqueue_events\"),\n      NGX_EVENT_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_num_slot,\n      0,\n      offsetof(ngx_kqueue_conf_t, events),\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_event_module_t  ngx_kqueue_module_ctx = {\n    &kqueue_name,\n    ngx_kqueue_create_conf,                /* create configuration */\n    ngx_kqueue_init_conf,                  /* init configuration */\n\n    {\n        ngx_kqueue_add_event,              /* add an event */\n        ngx_kqueue_del_event,              /* delete an event */\n        ngx_kqueue_add_event,              /* enable an event */\n        ngx_kqueue_del_event,              /* disable an event */\n        NULL,                              /* add an connection */\n        NULL,                              /* delete an connection */\n#ifdef EVFILT_USER\n        ngx_kqueue_notify,                 /* trigger a notify */\n#else\n        NULL,                              /* trigger a notify */\n#endif\n        ngx_kqueue_process_events,         /* process the events */\n        ngx_kqueue_init,                   /* init the events */\n        ngx_kqueue_done,                   /* done the events */\n#if (NGX_SSL && NGX_SSL_ASYNC)        \n        NULL,                              /* add an async conn */\n        NULL,                              /* del an async conn */\n#endif\n    }\n\n};\n\nngx_module_t  ngx_kqueue_module = {\n    NGX_MODULE_V1,\n    &ngx_kqueue_module_ctx,                /* module context */\n    ngx_kqueue_commands,                   /* module directives */\n    NGX_EVENT_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_kqueue_init(ngx_cycle_t *cycle, ngx_msec_t timer)\n{\n    ngx_kqueue_conf_t  *kcf;\n    struct timespec     ts;\n#if (NGX_HAVE_TIMER_EVENT)\n    struct kevent       kev;\n#endif\n\n    kcf = ngx_event_get_conf(cycle->conf_ctx, ngx_kqueue_module);\n\n    if (ngx_kqueue == -1) {\n        ngx_kqueue = kqueue();\n\n        if (ngx_kqueue == -1) {\n            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                          \"kqueue() failed\");\n            return NGX_ERROR;\n        }\n\n#ifdef EVFILT_USER\n        if (ngx_kqueue_notify_init(cycle->log) != NGX_OK) {\n            return NGX_ERROR;\n        }\n#endif\n    }\n\n    if (max_changes < kcf->changes) {\n        if (nchanges) {\n            ts.tv_sec = 0;\n            ts.tv_nsec = 0;\n\n            if (kevent(ngx_kqueue, change_list, (int) nchanges, NULL, 0, &ts)\n                == -1)\n            {\n                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                              \"kevent() failed\");\n                return NGX_ERROR;\n            }\n            nchanges = 0;\n        }\n\n        if (change_list) {\n            ngx_free(change_list);\n        }\n\n        change_list = ngx_alloc(kcf->changes * sizeof(struct kevent),\n                                cycle->log);\n        if (change_list == NULL) {\n            return NGX_ERROR;\n        }\n    }\n\n    max_changes = kcf->changes;\n\n    if (nevents < kcf->events) {\n        if (event_list) {\n            ngx_free(event_list);\n        }\n\n        event_list = ngx_alloc(kcf->events * sizeof(struct kevent), cycle->log);\n        if (event_list == NULL) {\n            return NGX_ERROR;\n        }\n    }\n\n    ngx_event_flags = NGX_USE_ONESHOT_EVENT\n                      |NGX_USE_KQUEUE_EVENT\n                      |NGX_USE_VNODE_EVENT;\n\n#if (NGX_HAVE_TIMER_EVENT)\n\n    if (timer) {\n        kev.ident = 0;\n        kev.filter = EVFILT_TIMER;\n        kev.flags = EV_ADD|EV_ENABLE;\n        kev.fflags = 0;\n        kev.data = timer;\n        kev.udata = 0;\n\n        ts.tv_sec = 0;\n        ts.tv_nsec = 0;\n\n        if (kevent(ngx_kqueue, &kev, 1, NULL, 0, &ts) == -1) {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                          \"kevent(EVFILT_TIMER) failed\");\n            return NGX_ERROR;\n        }\n\n        ngx_event_flags |= NGX_USE_TIMER_EVENT;\n    }\n\n#endif\n\n#if (NGX_HAVE_CLEAR_EVENT)\n    ngx_event_flags |= NGX_USE_CLEAR_EVENT;\n#else\n    ngx_event_flags |= NGX_USE_LEVEL_EVENT;\n#endif\n\n#if (NGX_HAVE_LOWAT_EVENT)\n    ngx_event_flags |= NGX_USE_LOWAT_EVENT;\n#endif\n\n    nevents = kcf->events;\n\n    ngx_io = ngx_os_io;\n\n    ngx_event_actions = ngx_kqueue_module_ctx.actions;\n\n    return NGX_OK;\n}\n\n\n#ifdef EVFILT_USER\n\nstatic ngx_int_t\nngx_kqueue_notify_init(ngx_log_t *log)\n{\n    notify_kev.ident = 0;\n    notify_kev.filter = EVFILT_USER;\n    notify_kev.data = 0;\n    notify_kev.flags = EV_ADD|EV_CLEAR;\n    notify_kev.fflags = 0;\n    notify_kev.udata = 0;\n\n    if (kevent(ngx_kqueue, &notify_kev, 1, NULL, 0, NULL) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,\n                      \"kevent(EVFILT_USER, EV_ADD) failed\");\n        return NGX_ERROR;\n    }\n\n    notify_event.active = 1;\n    notify_event.log = log;\n\n    notify_kev.flags = 0;\n    notify_kev.fflags = NOTE_TRIGGER;\n    notify_kev.udata = NGX_KQUEUE_UDATA_T ((uintptr_t) &notify_event);\n\n    return NGX_OK;\n}\n\n#endif\n\n\nstatic void\nngx_kqueue_done(ngx_cycle_t *cycle)\n{\n    if (close(ngx_kqueue) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                      \"kqueue close() failed\");\n    }\n\n    ngx_kqueue = -1;\n\n    ngx_free(change_list);\n    ngx_free(event_list);\n\n    change_list = NULL;\n    event_list = NULL;\n    max_changes = 0;\n    nchanges = 0;\n    nevents = 0;\n}\n\n\nstatic ngx_int_t\nngx_kqueue_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)\n{\n    ngx_int_t          rc;\n#if 0\n    ngx_event_t       *e;\n    ngx_connection_t  *c;\n#endif\n\n    ev->active = 1;\n    ev->disabled = 0;\n    ev->oneshot = (flags & NGX_ONESHOT_EVENT) ? 1 : 0;\n\n#if 0\n\n    if (ev->index < nchanges\n        && ((uintptr_t) change_list[ev->index].udata & (uintptr_t) ~1)\n            == (uintptr_t) ev)\n    {\n        if (change_list[ev->index].flags == EV_DISABLE) {\n\n            /*\n             * if the EV_DISABLE is still not passed to a kernel\n             * we will not pass it\n             */\n\n            ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,\n                           \"kevent activated: %d: ft:%i\",\n                           ngx_event_ident(ev->data), event);\n\n            if (ev->index < --nchanges) {\n                e = (ngx_event_t *)\n                    ((uintptr_t) change_list[nchanges].udata & (uintptr_t) ~1);\n                change_list[ev->index] = change_list[nchanges];\n                e->index = ev->index;\n            }\n\n            return NGX_OK;\n        }\n\n        c = ev->data;\n\n        ngx_log_error(NGX_LOG_ALERT, ev->log, 0,\n                      \"previous event on #%d were not passed in kernel\", c->fd);\n\n        return NGX_ERROR;\n    }\n\n#endif\n\n    rc = ngx_kqueue_set_event(ev, event, EV_ADD|EV_ENABLE|flags);\n\n    return rc;\n}\n\n\nstatic ngx_int_t\nngx_kqueue_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)\n{\n    ngx_int_t     rc;\n    ngx_event_t  *e;\n\n    ev->active = 0;\n    ev->disabled = 0;\n\n    if (ev->index < nchanges\n        && ((uintptr_t) change_list[ev->index].udata & (uintptr_t) ~1)\n            == (uintptr_t) ev)\n    {\n        ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,\n                       \"kevent deleted: %d: ft:%i\",\n                       ngx_event_ident(ev->data), event);\n\n        /* if the event is still not passed to a kernel we will not pass it */\n\n        nchanges--;\n\n        if (ev->index < nchanges) {\n            e = (ngx_event_t *)\n                    ((uintptr_t) change_list[nchanges].udata & (uintptr_t) ~1);\n            change_list[ev->index] = change_list[nchanges];\n            e->index = ev->index;\n        }\n\n        return NGX_OK;\n    }\n\n    /*\n     * when the file descriptor is closed the kqueue automatically deletes\n     * its filters so we do not need to delete explicitly the event\n     * before the closing the file descriptor.\n     */\n\n    if (flags & NGX_CLOSE_EVENT) {\n        return NGX_OK;\n    }\n\n    if (flags & NGX_DISABLE_EVENT) {\n        ev->disabled = 1;\n\n    } else {\n        flags |= EV_DELETE;\n    }\n\n    rc = ngx_kqueue_set_event(ev, event, flags);\n\n    return rc;\n}\n\n\nstatic ngx_int_t\nngx_kqueue_set_event(ngx_event_t *ev, ngx_int_t filter, ngx_uint_t flags)\n{\n    struct kevent     *kev;\n    struct timespec    ts;\n    ngx_connection_t  *c;\n\n    c = ev->data;\n\n    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ev->log, 0,\n                   \"kevent set event: %d: ft:%i fl:%04Xi\",\n                   c->fd, filter, flags);\n\n    if (nchanges >= max_changes) {\n        ngx_log_error(NGX_LOG_WARN, ev->log, 0,\n                      \"kqueue change list is filled up\");\n\n        ts.tv_sec = 0;\n        ts.tv_nsec = 0;\n\n        if (kevent(ngx_kqueue, change_list, (int) nchanges, NULL, 0, &ts)\n            == -1)\n        {\n            ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno, \"kevent() failed\");\n            return NGX_ERROR;\n        }\n\n        nchanges = 0;\n    }\n\n    kev = &change_list[nchanges];\n\n    kev->ident = c->fd;\n    kev->filter = (short) filter;\n    kev->flags = (u_short) flags;\n    kev->udata = NGX_KQUEUE_UDATA_T ((uintptr_t) ev | ev->instance);\n\n    if (filter == EVFILT_VNODE) {\n        kev->fflags = NOTE_DELETE|NOTE_WRITE|NOTE_EXTEND\n                                 |NOTE_ATTRIB|NOTE_RENAME\n#if (__FreeBSD__ == 4 && __FreeBSD_version >= 430000) \\\n    || __FreeBSD_version >= 500018\n                                 |NOTE_REVOKE\n#endif\n                      ;\n        kev->data = 0;\n\n    } else {\n#if (NGX_HAVE_LOWAT_EVENT)\n        if (flags & NGX_LOWAT_EVENT) {\n            kev->fflags = NOTE_LOWAT;\n            kev->data = ev->available;\n\n        } else {\n            kev->fflags = 0;\n            kev->data = 0;\n        }\n#else\n        kev->fflags = 0;\n        kev->data = 0;\n#endif\n    }\n\n    ev->index = nchanges;\n    nchanges++;\n\n    if (flags & NGX_FLUSH_EVENT) {\n        ts.tv_sec = 0;\n        ts.tv_nsec = 0;\n\n        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0, \"kevent flush\");\n\n        if (kevent(ngx_kqueue, change_list, (int) nchanges, NULL, 0, &ts)\n            == -1)\n        {\n            ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno, \"kevent() failed\");\n            return NGX_ERROR;\n        }\n\n        nchanges = 0;\n    }\n\n    return NGX_OK;\n}\n\n\n#ifdef EVFILT_USER\n\nstatic ngx_int_t\nngx_kqueue_notify(ngx_event_handler_pt handler)\n{\n    notify_event.handler = handler;\n\n    if (kevent(ngx_kqueue, &notify_kev, 1, NULL, 0, NULL) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, notify_event.log, ngx_errno,\n                      \"kevent(EVFILT_USER, NOTE_TRIGGER) failed\");\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n#endif\n\n\nstatic ngx_int_t\nngx_kqueue_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,\n    ngx_uint_t flags)\n{\n    int               events, n;\n    ngx_int_t         i, instance;\n    ngx_uint_t        level;\n    ngx_err_t         err;\n    ngx_event_t      *ev;\n    ngx_queue_t      *queue;\n    struct timespec   ts, *tp;\n\n    n = (int) nchanges;\n    nchanges = 0;\n\n    if (timer == NGX_TIMER_INFINITE) {\n        tp = NULL;\n\n    } else {\n\n        ts.tv_sec = timer / 1000;\n        ts.tv_nsec = (timer % 1000) * 1000000;\n\n        /*\n         * 64-bit Darwin kernel has the bug: kernel level ts.tv_nsec is\n         * the int32_t while user level ts.tv_nsec is the long (64-bit),\n         * so on the big endian PowerPC all nanoseconds are lost.\n         */\n\n#if (NGX_DARWIN_KEVENT_BUG)\n        ts.tv_nsec <<= 32;\n#endif\n\n        tp = &ts;\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                   \"kevent timer: %M, changes: %d\", timer, n);\n\n    events = kevent(ngx_kqueue, change_list, n, event_list, (int) nevents, tp);\n\n    err = (events == -1) ? ngx_errno : 0;\n\n    if (flags & NGX_UPDATE_TIME || ngx_event_timer_alarm) {\n        ngx_time_update();\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                   \"kevent events: %d\", events);\n\n    if (err) {\n        if (err == NGX_EINTR) {\n\n            if (ngx_event_timer_alarm) {\n                ngx_event_timer_alarm = 0;\n                return NGX_OK;\n            }\n\n            level = NGX_LOG_INFO;\n\n        } else {\n            level = NGX_LOG_ALERT;\n        }\n\n        ngx_log_error(level, cycle->log, err, \"kevent() failed\");\n        return NGX_ERROR;\n    }\n\n    if (events == 0) {\n        if (timer != NGX_TIMER_INFINITE) {\n            return NGX_OK;\n        }\n\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,\n                      \"kevent() returned no events without timeout\");\n        return NGX_ERROR;\n    }\n\n    for (i = 0; i < events; i++) {\n\n        ngx_kqueue_dump_event(cycle->log, &event_list[i]);\n\n        if (event_list[i].flags & EV_ERROR) {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, event_list[i].data,\n                          \"kevent() error on %d filter:%d flags:%04Xd\",\n                          (int) event_list[i].ident, event_list[i].filter,\n                          event_list[i].flags);\n            continue;\n        }\n\n#if (NGX_HAVE_TIMER_EVENT)\n\n        if (event_list[i].filter == EVFILT_TIMER) {\n            ngx_time_update();\n            continue;\n        }\n\n#endif\n\n        ev = (ngx_event_t *) event_list[i].udata;\n\n        switch (event_list[i].filter) {\n\n        case EVFILT_READ:\n        case EVFILT_WRITE:\n\n            instance = (uintptr_t) ev & 1;\n            ev = (ngx_event_t *) ((uintptr_t) ev & (uintptr_t) ~1);\n\n            if (ev->closed || ev->instance != instance) {\n\n                /*\n                 * the stale event from a file descriptor\n                 * that was just closed in this iteration\n                 */\n\n                ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                               \"kevent: stale event %p\", ev);\n                continue;\n            }\n\n            if (ev->log && (ev->log->log_level & NGX_LOG_DEBUG_CONNECTION)) {\n                ngx_kqueue_dump_event(ev->log, &event_list[i]);\n            }\n\n            if (ev->oneshot) {\n                ev->active = 0;\n            }\n\n            ev->available = event_list[i].data;\n\n            if (event_list[i].flags & EV_EOF) {\n                ev->pending_eof = 1;\n                ev->kq_errno = event_list[i].fflags;\n            }\n\n            ev->ready = 1;\n\n            break;\n\n        case EVFILT_VNODE:\n            ev->kq_vnode = 1;\n\n            break;\n\n        case EVFILT_AIO:\n            ev->complete = 1;\n            ev->ready = 1;\n\n            break;\n\n#ifdef EVFILT_USER\n        case EVFILT_USER:\n            break;\n#endif\n\n        default:\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,\n                          \"unexpected kevent() filter %d\",\n                          event_list[i].filter);\n            continue;\n        }\n\n        if (flags & NGX_POST_EVENTS) {\n            queue = ev->accept ? &ngx_posted_accept_events\n                               : &ngx_posted_events;\n\n            ngx_post_event(ev, queue);\n\n            continue;\n        }\n\n        ev->handler(ev);\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_inline void\nngx_kqueue_dump_event(ngx_log_t *log, struct kevent *kev)\n{\n    if (kev->ident > 0x8000000 && kev->ident != (unsigned) -1) {\n        ngx_log_debug6(NGX_LOG_DEBUG_EVENT, log, 0,\n                       \"kevent: %p: ft:%d fl:%04Xd ff:%08Xd d:%d ud:%p\",\n                       (void *) kev->ident, kev->filter,\n                       kev->flags, kev->fflags,\n                       (int) kev->data, kev->udata);\n\n    } else {\n        ngx_log_debug6(NGX_LOG_DEBUG_EVENT, log, 0,\n                       \"kevent: %d: ft:%d fl:%04Xd ff:%08Xd d:%d ud:%p\",\n                       (int) kev->ident, kev->filter,\n                       kev->flags, kev->fflags,\n                       (int) kev->data, kev->udata);\n    }\n}\n\n\nstatic void *\nngx_kqueue_create_conf(ngx_cycle_t *cycle)\n{\n    ngx_kqueue_conf_t  *kcf;\n\n    kcf = ngx_palloc(cycle->pool, sizeof(ngx_kqueue_conf_t));\n    if (kcf == NULL) {\n        return NULL;\n    }\n\n    kcf->changes = NGX_CONF_UNSET;\n    kcf->events = NGX_CONF_UNSET;\n\n    return kcf;\n}\n\n\nstatic char *\nngx_kqueue_init_conf(ngx_cycle_t *cycle, void *conf)\n{\n    ngx_kqueue_conf_t *kcf = conf;\n\n    ngx_conf_init_uint_value(kcf->changes, 512);\n    ngx_conf_init_uint_value(kcf->events, 512);\n\n    return NGX_CONF_OK;\n}\n"
  },
  {
    "path": "src/event/modules/ngx_poll_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n\n\nstatic ngx_int_t ngx_poll_init(ngx_cycle_t *cycle, ngx_msec_t timer);\nstatic void ngx_poll_done(ngx_cycle_t *cycle);\nstatic ngx_int_t ngx_poll_add_event(ngx_event_t *ev, ngx_int_t event,\n    ngx_uint_t flags);\nstatic ngx_int_t ngx_poll_del_event(ngx_event_t *ev, ngx_int_t event,\n    ngx_uint_t flags);\nstatic ngx_int_t ngx_poll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,\n    ngx_uint_t flags);\nstatic char *ngx_poll_init_conf(ngx_cycle_t *cycle, void *conf);\n\n\nstatic struct pollfd  *event_list;\nstatic ngx_uint_t      nevents;\n\n\nstatic ngx_str_t           poll_name = ngx_string(\"poll\");\n\nstatic ngx_event_module_t  ngx_poll_module_ctx = {\n    &poll_name,\n    NULL,                                  /* create configuration */\n    ngx_poll_init_conf,                    /* init configuration */\n\n    {\n        ngx_poll_add_event,                /* add an event */\n        ngx_poll_del_event,                /* delete an event */\n        ngx_poll_add_event,                /* enable an event */\n        ngx_poll_del_event,                /* disable an event */\n        NULL,                              /* add an connection */\n        NULL,                              /* delete an connection */\n        NULL,                              /* trigger a notify */\n        ngx_poll_process_events,           /* process the events */\n        ngx_poll_init,                     /* init the events */\n        ngx_poll_done,                     /* done the events */\n#if (NGX_SSL && NGX_SSL_ASYNC)\n        NULL,                              /* add an async conn */\n        NULL,                              /* del an async conn */\n#endif\n    }\n\n};\n\nngx_module_t  ngx_poll_module = {\n    NGX_MODULE_V1,\n    &ngx_poll_module_ctx,                  /* module context */\n    NULL,                                  /* module directives */\n    NGX_EVENT_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\nstatic ngx_int_t\nngx_poll_init(ngx_cycle_t *cycle, ngx_msec_t timer)\n{\n    struct pollfd   *list;\n\n    if (event_list == NULL) {\n        nevents = 0;\n    }\n\n    if (ngx_process >= NGX_PROCESS_WORKER\n        || cycle->old_cycle == NULL\n        || cycle->old_cycle->connection_n < cycle->connection_n)\n    {\n        list = ngx_alloc(sizeof(struct pollfd) * cycle->connection_n,\n                         cycle->log);\n        if (list == NULL) {\n            return NGX_ERROR;\n        }\n\n        if (event_list) {\n            ngx_memcpy(list, event_list, sizeof(struct pollfd) * nevents);\n            ngx_free(event_list);\n        }\n\n        event_list = list;\n    }\n\n    ngx_io = ngx_os_io;\n\n    ngx_event_actions = ngx_poll_module_ctx.actions;\n\n    ngx_event_flags = NGX_USE_LEVEL_EVENT|NGX_USE_FD_EVENT;\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_poll_done(ngx_cycle_t *cycle)\n{\n    ngx_free(event_list);\n\n    event_list = NULL;\n}\n\n\nstatic ngx_int_t\nngx_poll_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)\n{\n    ngx_event_t       *e;\n    ngx_connection_t  *c;\n\n    c = ev->data;\n\n    ev->active = 1;\n\n    if (ev->index != NGX_INVALID_INDEX) {\n        ngx_log_error(NGX_LOG_ALERT, ev->log, 0,\n                      \"poll event fd:%d ev:%i is already set\", c->fd, event);\n        return NGX_OK;\n    }\n\n    if (event == NGX_READ_EVENT) {\n        e = c->write;\n#if (NGX_READ_EVENT != POLLIN)\n        event = POLLIN;\n#endif\n\n    } else {\n        e = c->read;\n#if (NGX_WRITE_EVENT != POLLOUT)\n        event = POLLOUT;\n#endif\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,\n                   \"poll add event: fd:%d ev:%i\", c->fd, event);\n\n    if (e == NULL || e->index == NGX_INVALID_INDEX) {\n        event_list[nevents].fd = c->fd;\n        event_list[nevents].events = (short) event;\n        event_list[nevents].revents = 0;\n\n        ev->index = nevents;\n        nevents++;\n\n    } else {\n        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ev->log, 0,\n                       \"poll add index: %i\", e->index);\n\n        event_list[e->index].events |= (short) event;\n        ev->index = e->index;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_poll_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)\n{\n    ngx_event_t       *e;\n    ngx_connection_t  *c;\n\n    c = ev->data;\n\n    ev->active = 0;\n\n    if (ev->index == NGX_INVALID_INDEX) {\n        ngx_log_error(NGX_LOG_ALERT, ev->log, 0,\n                      \"poll event fd:%d ev:%i is already deleted\",\n                      c->fd, event);\n        return NGX_OK;\n    }\n\n    if (event == NGX_READ_EVENT) {\n        e = c->write;\n#if (NGX_READ_EVENT != POLLIN)\n        event = POLLIN;\n#endif\n\n    } else {\n        e = c->read;\n#if (NGX_WRITE_EVENT != POLLOUT)\n        event = POLLOUT;\n#endif\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,\n                   \"poll del event: fd:%d ev:%i\", c->fd, event);\n\n    if (e == NULL || e->index == NGX_INVALID_INDEX) {\n        nevents--;\n\n        if (ev->index < nevents) {\n\n            ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,\n                           \"index: copy event %ui to %i\", nevents, ev->index);\n\n            event_list[ev->index] = event_list[nevents];\n\n            c = ngx_cycle->files[event_list[nevents].fd];\n\n            if (c->fd == -1) {\n                ngx_log_error(NGX_LOG_ALERT, ev->log, 0,\n                              \"unexpected last event\");\n\n            } else {\n                if (c->read->index == nevents) {\n                    c->read->index = ev->index;\n                }\n\n                if (c->write->index == nevents) {\n                    c->write->index = ev->index;\n                }\n            }\n        }\n\n    } else {\n        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ev->log, 0,\n                       \"poll del index: %i\", e->index);\n\n        event_list[e->index].events &= (short) ~event;\n    }\n\n    ev->index = NGX_INVALID_INDEX;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_poll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags)\n{\n    int                 ready, revents;\n    ngx_err_t           err;\n    ngx_uint_t          i, found, level;\n    ngx_event_t        *ev;\n    ngx_queue_t        *queue;\n    ngx_connection_t   *c;\n\n    /* NGX_TIMER_INFINITE == INFTIM */\n\n#if (NGX_DEBUG0)\n    if (cycle->log->log_level & NGX_LOG_DEBUG_ALL) {\n        for (i = 0; i < nevents; i++) {\n            ngx_log_debug3(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                           \"poll: %ui: fd:%d ev:%04Xd\",\n                           i, event_list[i].fd, event_list[i].events);\n        }\n    }\n#endif\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, \"poll timer: %M\", timer);\n\n    ready = poll(event_list, (u_int) nevents, (int) timer);\n\n    err = (ready == -1) ? ngx_errno : 0;\n\n    if (flags & NGX_UPDATE_TIME || ngx_event_timer_alarm) {\n        ngx_time_update();\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                   \"poll ready %d of %ui\", ready, nevents);\n\n    if (err) {\n        if (err == NGX_EINTR) {\n\n            if (ngx_event_timer_alarm) {\n                ngx_event_timer_alarm = 0;\n                return NGX_OK;\n            }\n\n            level = NGX_LOG_INFO;\n\n        } else {\n            level = NGX_LOG_ALERT;\n        }\n\n        ngx_log_error(level, cycle->log, err, \"poll() failed\");\n        return NGX_ERROR;\n    }\n\n    if (ready == 0) {\n        if (timer != NGX_TIMER_INFINITE) {\n            return NGX_OK;\n        }\n\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,\n                      \"poll() returned no events without timeout\");\n        return NGX_ERROR;\n    }\n\n    for (i = 0; i < nevents && ready; i++) {\n\n        revents = event_list[i].revents;\n\n#if 1\n        ngx_log_debug4(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                       \"poll: %ui: fd:%d ev:%04Xd rev:%04Xd\",\n                       i, event_list[i].fd, event_list[i].events, revents);\n#else\n        if (revents) {\n            ngx_log_debug4(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                           \"poll: %ui: fd:%d ev:%04Xd rev:%04Xd\",\n                           i, event_list[i].fd, event_list[i].events, revents);\n        }\n#endif\n\n        if (revents & POLLNVAL) {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,\n                          \"poll() error fd:%d ev:%04Xd rev:%04Xd\",\n                          event_list[i].fd, event_list[i].events, revents);\n        }\n\n        if (revents & ~(POLLIN|POLLOUT|POLLERR|POLLHUP|POLLNVAL)) {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,\n                          \"strange poll() events fd:%d ev:%04Xd rev:%04Xd\",\n                          event_list[i].fd, event_list[i].events, revents);\n        }\n\n        if (event_list[i].fd == -1) {\n            /*\n             * the disabled event, a workaround for our possible bug,\n             * see the comment below\n             */\n            continue;\n        }\n\n        c = ngx_cycle->files[event_list[i].fd];\n\n        if (c->fd == -1) {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, \"unexpected event\");\n\n            /*\n             * it is certainly our fault and it should be investigated,\n             * in the meantime we disable this event to avoid a CPU spinning\n             */\n\n            if (i == nevents - 1) {\n                nevents--;\n            } else {\n                event_list[i].fd = -1;\n            }\n\n            continue;\n        }\n\n        if (revents & (POLLERR|POLLHUP|POLLNVAL)) {\n\n            /*\n             * if the error events were returned, add POLLIN and POLLOUT\n             * to handle the events at least in one active handler\n             */\n\n            revents |= POLLIN|POLLOUT;\n        }\n\n        found = 0;\n\n        if ((revents & POLLIN) && c->read->active) {\n            found = 1;\n\n            ev = c->read;\n            ev->ready = 1;\n            ev->available = -1;\n\n            queue = ev->accept ? &ngx_posted_accept_events\n                               : &ngx_posted_events;\n\n            ngx_post_event(ev, queue);\n        }\n\n        if ((revents & POLLOUT) && c->write->active) {\n            found = 1;\n\n            ev = c->write;\n            ev->ready = 1;\n\n            ngx_post_event(ev, &ngx_posted_events);\n        }\n\n        if (found) {\n            ready--;\n            continue;\n        }\n    }\n\n    if (ready != 0) {\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, \"poll ready != events\");\n    }\n\n    return NGX_OK;\n}\n\n\nstatic char *\nngx_poll_init_conf(ngx_cycle_t *cycle, void *conf)\n{\n    ngx_event_conf_t  *ecf;\n\n    ecf = ngx_event_get_conf(cycle->conf_ctx, ngx_event_core_module);\n\n    if (ecf->use != ngx_poll_module.ctx_index) {\n        return NGX_CONF_OK;\n    }\n\n    return NGX_CONF_OK;\n}\n"
  },
  {
    "path": "src/event/modules/ngx_rtsig_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n\n\n#if (NGX_TEST_BUILD_RTSIG)\n\n#if (NGX_DARWIN)\n\n#define SIGRTMIN       33\n#define si_fd          __pad[0]\n\n#else\n\n#ifdef  SIGRTMIN\n#define si_fd          _reason.__spare__.__spare2__[0]\n#else\n#define SIGRTMIN       33\n#define si_fd          __spare__[0]\n#endif\n\n#endif\n\n#define F_SETSIG       10\n#define KERN_RTSIGNR   30\n#define KERN_RTSIGMAX  31\n\nint sigtimedwait(const sigset_t *set, siginfo_t *info,\n                 const struct timespec *timeout);\n\nint sigtimedwait(const sigset_t *set, siginfo_t *info,\n                 const struct timespec *timeout)\n{\n    return -1;\n}\n\nint ngx_linux_rtsig_max;\n\n#endif\n\n\ntypedef struct {\n    ngx_uint_t  signo;\n    ngx_uint_t  overflow_events;\n    ngx_uint_t  overflow_test;\n    ngx_uint_t  overflow_threshold;\n} ngx_rtsig_conf_t;\n\n\nextern ngx_event_module_t  ngx_poll_module_ctx;\n\nstatic ngx_int_t ngx_rtsig_init(ngx_cycle_t *cycle, ngx_msec_t timer);\nstatic void ngx_rtsig_done(ngx_cycle_t *cycle);\nstatic ngx_int_t ngx_rtsig_add_connection(ngx_connection_t *c);\nstatic ngx_int_t ngx_rtsig_del_connection(ngx_connection_t *c,\n    ngx_uint_t flags);\nstatic ngx_int_t ngx_rtsig_process_events(ngx_cycle_t *cycle,\n    ngx_msec_t timer, ngx_uint_t flags);\nstatic ngx_int_t ngx_rtsig_process_overflow(ngx_cycle_t *cycle,\n    ngx_msec_t timer, ngx_uint_t flags);\n\nstatic void *ngx_rtsig_create_conf(ngx_cycle_t *cycle);\nstatic char *ngx_rtsig_init_conf(ngx_cycle_t *cycle, void *conf);\nstatic char *ngx_check_ngx_overflow_threshold_bounds(ngx_conf_t *cf,\n    void *post, void *data);\n\n\nstatic sigset_t        set;\nstatic ngx_uint_t      overflow, overflow_current;\nstatic struct pollfd  *overflow_list;\n\n\nstatic ngx_str_t      rtsig_name = ngx_string(\"rtsig\");\n\nstatic ngx_conf_num_bounds_t  ngx_overflow_threshold_bounds = {\n    ngx_check_ngx_overflow_threshold_bounds, 2, 10\n};\n\n\nstatic ngx_command_t  ngx_rtsig_commands[] = {\n\n    { ngx_string(\"rtsig_signo\"),\n      NGX_EVENT_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_num_slot,\n      0,\n      offsetof(ngx_rtsig_conf_t, signo),\n      NULL },\n\n    { ngx_string(\"rtsig_overflow_events\"),\n      NGX_EVENT_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_num_slot,\n      0,\n      offsetof(ngx_rtsig_conf_t, overflow_events),\n      NULL },\n\n    { ngx_string(\"rtsig_overflow_test\"),\n      NGX_EVENT_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_num_slot,\n      0,\n      offsetof(ngx_rtsig_conf_t, overflow_test),\n      NULL },\n\n    { ngx_string(\"rtsig_overflow_threshold\"),\n      NGX_EVENT_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_num_slot,\n      0,\n      offsetof(ngx_rtsig_conf_t, overflow_threshold),\n      &ngx_overflow_threshold_bounds },\n\n      ngx_null_command\n};\n\n\nngx_event_module_t  ngx_rtsig_module_ctx = {\n    &rtsig_name,\n    ngx_rtsig_create_conf,               /* create configuration */\n    ngx_rtsig_init_conf,                 /* init configuration */\n\n    {\n        NULL,                            /* add an event */\n        NULL,                            /* delete an event */\n        NULL,                            /* enable an event */\n        NULL,                            /* disable an event */\n        ngx_rtsig_add_connection,        /* add an connection */\n        ngx_rtsig_del_connection,        /* delete an connection */\n        NULL,                            /* trigger a notify */\n        ngx_rtsig_process_events,        /* process the events */\n        ngx_rtsig_init,                  /* init the events */\n        ngx_rtsig_done,                  /* done the events */\n    }\n\n};\n\nngx_module_t  ngx_rtsig_module = {\n    NGX_MODULE_V1,\n    &ngx_rtsig_module_ctx,               /* module context */\n    ngx_rtsig_commands,                  /* module directives */\n    NGX_EVENT_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_rtsig_init(ngx_cycle_t *cycle, ngx_msec_t timer)\n{\n    ngx_rtsig_conf_t  *rtscf;\n\n    rtscf = ngx_event_get_conf(cycle->conf_ctx, ngx_rtsig_module);\n\n    sigemptyset(&set);\n    sigaddset(&set, (int) rtscf->signo);\n    sigaddset(&set, (int) rtscf->signo + 1);\n    sigaddset(&set, SIGIO);\n    sigaddset(&set, SIGALRM);\n\n    if (sigprocmask(SIG_BLOCK, &set, NULL) == -1) {\n        ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                      \"sigprocmask() failed\");\n        return NGX_ERROR;\n    }\n\n    if (overflow_list) {\n        ngx_free(overflow_list);\n    }\n\n    overflow_list = ngx_alloc(sizeof(struct pollfd) * rtscf->overflow_events,\n                              cycle->log);\n    if (overflow_list == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_io = ngx_os_io;\n\n    ngx_event_actions = ngx_rtsig_module_ctx.actions;\n\n    ngx_event_flags = NGX_USE_RTSIG_EVENT\n                      |NGX_USE_GREEDY_EVENT\n                      |NGX_USE_FD_EVENT;\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_rtsig_done(ngx_cycle_t *cycle)\n{\n    ngx_free(overflow_list);\n\n    overflow_list = NULL;\n}\n\n\nstatic ngx_int_t\nngx_rtsig_add_connection(ngx_connection_t *c)\n{\n    ngx_uint_t         signo;\n    ngx_rtsig_conf_t  *rtscf;\n\n    if (c->read->accept && c->read->disabled) {\n\n        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"rtsig enable connection: fd:%d\", c->fd);\n\n        if (fcntl(c->fd, F_SETOWN, ngx_pid) == -1) {\n            ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,\n                          \"fcntl(F_SETOWN) failed\");\n            return NGX_ERROR;\n        }\n\n        c->read->active = 1;\n        c->read->disabled = 0;\n    }\n\n    rtscf = ngx_event_get_conf(ngx_cycle->conf_ctx, ngx_rtsig_module);\n\n    signo = rtscf->signo + c->read->instance;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"rtsig add connection: fd:%d signo:%ui\", c->fd, signo);\n\n    if (fcntl(c->fd, F_SETFL, O_RDWR|O_NONBLOCK|O_ASYNC) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,\n                      \"fcntl(O_RDWR|O_NONBLOCK|O_ASYNC) failed\");\n        return NGX_ERROR;\n    }\n\n    if (fcntl(c->fd, F_SETSIG, (int) signo) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,\n                      \"fcntl(F_SETSIG) failed\");\n        return NGX_ERROR;\n    }\n\n    if (fcntl(c->fd, F_SETOWN, ngx_pid) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,\n                      \"fcntl(F_SETOWN) failed\");\n        return NGX_ERROR;\n    }\n\n#if (NGX_HAVE_ONESIGFD)\n    if (fcntl(c->fd, F_SETAUXFL, O_ONESIGFD) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,\n                      \"fcntl(F_SETAUXFL) failed\");\n        return NGX_ERROR;\n    }\n#endif\n\n    c->read->active = 1;\n    c->write->active = 1;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_rtsig_del_connection(ngx_connection_t *c, ngx_uint_t flags)\n{\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"rtsig del connection: fd:%d\", c->fd);\n\n    if ((flags & NGX_DISABLE_EVENT) && c->read->accept) {\n\n        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"rtsig disable connection: fd:%d\", c->fd);\n\n        c->read->active = 0;\n        c->read->disabled = 1;\n        return NGX_OK;\n    }\n\n    if (flags & NGX_CLOSE_EVENT) {\n        c->read->active = 0;\n        c->write->active = 0;\n        return NGX_OK;\n    }\n\n    if (fcntl(c->fd, F_SETFL, O_RDWR|O_NONBLOCK) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,\n                      \"fcntl(O_RDWR|O_NONBLOCK) failed\");\n        return NGX_ERROR;\n    }\n\n    c->read->active = 0;\n    c->write->active = 0;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_rtsig_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags)\n{\n    int                 signo;\n    ngx_int_t           instance;\n    ngx_err_t           err;\n    siginfo_t           si;\n    ngx_event_t        *rev, *wev;\n    ngx_queue_t        *queue;\n    struct timespec     ts, *tp;\n    struct sigaction    sa;\n    ngx_connection_t   *c;\n    ngx_rtsig_conf_t   *rtscf;\n\n    if (timer == NGX_TIMER_INFINITE) {\n        tp = NULL;\n\n    } else {\n        ts.tv_sec = timer / 1000;\n        ts.tv_nsec = (timer % 1000) * 1000000;\n        tp = &ts;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                   \"rtsig timer: %M\", timer);\n\n    /* Linux's sigwaitinfo() is sigtimedwait() with the NULL timeout pointer */\n\n    signo = sigtimedwait(&set, &si, tp);\n\n    if (signo == -1) {\n        err = ngx_errno;\n\n        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, err,\n                       \"rtsig signo:%d\", signo);\n\n        if (flags & NGX_UPDATE_TIME) {\n            ngx_time_update();\n        }\n\n        if (err == NGX_EAGAIN) {\n\n            /* timeout */\n\n            if (timer != NGX_TIMER_INFINITE) {\n                return NGX_AGAIN;\n            }\n\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, err,\n                          \"sigtimedwait() returned EAGAIN without timeout\");\n            return NGX_ERROR;\n        }\n\n        ngx_log_error((err == NGX_EINTR) ? NGX_LOG_INFO : NGX_LOG_ALERT,\n                      cycle->log, err, \"sigtimedwait() failed\");\n        return NGX_ERROR;\n    }\n\n    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                   \"rtsig signo:%d fd:%d band:%04Xd\",\n                   signo, si.si_fd, si.si_band);\n\n    if (flags & NGX_UPDATE_TIME) {\n        ngx_time_update();\n    }\n\n    rtscf = ngx_event_get_conf(ngx_cycle->conf_ctx, ngx_rtsig_module);\n\n    if (signo == (int) rtscf->signo || signo == (int) rtscf->signo + 1) {\n\n        if (overflow && (ngx_uint_t) si.si_fd > overflow_current) {\n            return NGX_OK;\n        }\n\n        c = ngx_cycle->files[si.si_fd];\n\n        if (c == NULL) {\n\n            /* the stale event */\n\n            return NGX_OK;\n        }\n\n        instance = signo - (int) rtscf->signo;\n\n        rev = c->read;\n\n        if (rev->instance != instance) {\n\n            /*\n             * the stale event from a file descriptor\n             * that was just closed in this iteration\n             */\n\n            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                           \"rtsig: stale event %p\", c);\n\n            return NGX_OK;\n        }\n\n        if ((si.si_band & (POLLIN|POLLHUP|POLLERR)) && rev->active) {\n\n            rev->ready = 1;\n\n            if (flags & NGX_POST_EVENTS) {\n                queue = rev->accept ? &ngx_posted_accept_events\n                                    : &ngx_posted_events;\n\n                ngx_post_event(rev, queue);\n\n            } else {\n                rev->handler(rev);\n            }\n        }\n\n        wev = c->write;\n\n        if ((si.si_band & (POLLOUT|POLLHUP|POLLERR)) && wev->active) {\n\n            wev->ready = 1;\n\n            if (flags & NGX_POST_EVENTS) {\n                ngx_post_event(wev, &ngx_posted_events);\n\n            } else {\n                wev->handler(wev);\n            }\n        }\n\n        return NGX_OK;\n\n    } else if (signo == SIGALRM) {\n\n        ngx_time_update();\n\n        return NGX_OK;\n\n    } else if (signo == SIGIO) {\n\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,\n                      \"rt signal queue overflowed\");\n\n        /* flush the RT signal queue */\n\n        ngx_memzero(&sa, sizeof(struct sigaction));\n        sa.sa_handler = SIG_DFL;\n        sigemptyset(&sa.sa_mask);\n\n        if (sigaction(rtscf->signo, &sa, NULL) == -1) {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                          \"sigaction(%d, SIG_DFL) failed\", rtscf->signo);\n        }\n\n        if (sigaction(rtscf->signo + 1, &sa, NULL) == -1) {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                          \"sigaction(%d, SIG_DFL) failed\", rtscf->signo + 1);\n        }\n\n        overflow = 1;\n        overflow_current = 0;\n        ngx_event_actions.process_events = ngx_rtsig_process_overflow;\n\n        return NGX_ERROR;\n\n    }\n\n    ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,\n                  \"sigtimedwait() returned unexpected signal: %d\", signo);\n\n    return NGX_ERROR;\n}\n\n\nstatic ngx_int_t\nngx_rtsig_process_overflow(ngx_cycle_t *cycle, ngx_msec_t timer,\n    ngx_uint_t flags)\n{\n    int                name[2], rtsig_max, rtsig_nr, events, ready;\n    size_t             len;\n    ngx_err_t          err;\n    ngx_uint_t         tested, n, i;\n    ngx_event_t       *rev, *wev;\n    ngx_queue_t       *queue;\n    ngx_connection_t  *c;\n    ngx_rtsig_conf_t  *rtscf;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                   \"rtsig process overflow\");\n\n    rtscf = ngx_event_get_conf(ngx_cycle->conf_ctx, ngx_rtsig_module);\n\n    tested = 0;\n\n    for ( ;; ) {\n\n        n = 0;\n        while (n < rtscf->overflow_events) {\n\n            if (overflow_current == cycle->connection_n) {\n                break;\n            }\n\n            c = cycle->files[overflow_current++];\n\n            if (c == NULL || c->fd == -1) {\n                continue;\n            }\n\n            events = 0;\n\n            if (c->read->active && c->read->handler) {\n                events |= POLLIN;\n            }\n\n            if (c->write->active && c->write->handler) {\n                events |= POLLOUT;\n            }\n\n            if (events == 0) {\n                continue;\n            }\n\n            overflow_list[n].fd = c->fd;\n            overflow_list[n].events = events;\n            overflow_list[n].revents = 0;\n            n++;\n        }\n\n        if (n == 0) {\n            break;\n        }\n\n        for ( ;; ) {\n            ready = poll(overflow_list, n, 0);\n\n            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                           \"rtsig overflow poll:%d\", ready);\n\n            if (ready == -1) {\n                err = ngx_errno;\n                ngx_log_error((err == NGX_EINTR) ? NGX_LOG_INFO : NGX_LOG_ALERT,\n                              cycle->log, 0,\n                              \"poll() failed while the overflow recover\");\n\n                if (err == NGX_EINTR) {\n                    continue;\n                }\n            }\n\n            break;\n        }\n\n        if (ready <= 0) {\n            continue;\n        }\n\n        for (i = 0; i < n; i++) {\n            c = cycle->files[overflow_list[i].fd];\n\n            if (c == NULL) {\n                continue;\n            }\n\n            rev = c->read;\n\n            if (rev->active\n                && !rev->closed\n                && rev->handler\n                && (overflow_list[i].revents\n                                          & (POLLIN|POLLERR|POLLHUP|POLLNVAL)))\n            {\n                tested++;\n\n                rev->ready = 1;\n\n                if (flags & NGX_POST_EVENTS) {\n                    queue = rev->accept ? &ngx_posted_accept_events\n                                        : &ngx_posted_events;\n\n                    ngx_post_event(rev, queue);\n\n                } else {\n                    rev->handler(rev);\n                }\n            }\n\n            wev = c->write;\n\n            if (wev->active\n                && !wev->closed\n                && wev->handler\n                && (overflow_list[i].revents\n                                         & (POLLOUT|POLLERR|POLLHUP|POLLNVAL)))\n            {\n                tested++;\n\n                wev->ready = 1;\n\n                if (flags & NGX_POST_EVENTS) {\n                    ngx_post_event(wev, &ngx_posted_events);\n\n                } else {\n                    wev->handler(wev);\n                }\n            }\n        }\n\n        if (tested >= rtscf->overflow_test) {\n\n            if (ngx_linux_rtsig_max) {\n\n                /*\n                 * Check the current rt queue length to prevent\n                 * the new overflow.\n                 *\n                 * learn the \"/proc/sys/kernel/rtsig-max\" value because\n                 * it can be changed since the last checking\n                 */\n\n                name[0] = CTL_KERN;\n                name[1] = KERN_RTSIGMAX;\n                len = sizeof(rtsig_max);\n\n                if (sysctl(name, 2, &rtsig_max, &len, NULL, 0) == -1) {\n                    ngx_log_error(NGX_LOG_ALERT, cycle->log, errno,\n                                  \"sysctl(KERN_RTSIGMAX) failed\");\n                    return NGX_ERROR;\n                }\n\n                /* name[0] = CTL_KERN; */\n                name[1] = KERN_RTSIGNR;\n                len = sizeof(rtsig_nr);\n\n                if (sysctl(name, 2, &rtsig_nr, &len, NULL, 0) == -1) {\n                    ngx_log_error(NGX_LOG_ALERT, cycle->log, errno,\n                                  \"sysctl(KERN_RTSIGNR) failed\");\n                    return NGX_ERROR;\n                }\n\n                /*\n                 * drain the rt signal queue if the /\"proc/sys/kernel/rtsig-nr\"\n                 * is bigger than\n                 *    \"/proc/sys/kernel/rtsig-max\" / \"rtsig_overflow_threshold\"\n                 */\n\n                if (rtsig_max / (int) rtscf->overflow_threshold < rtsig_nr) {\n                    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                                   \"rtsig queue state: %d/%d\",\n                                   rtsig_nr, rtsig_max);\n                    while (ngx_rtsig_process_events(cycle, 0, flags) == NGX_OK)\n                    {\n                        /* void */\n                    }\n                }\n\n            } else {\n\n                /*\n                 * Linux has not KERN_RTSIGMAX since 2.6.6-mm2\n                 * so drain the rt signal queue unconditionally\n                 */\n\n                while (ngx_rtsig_process_events(cycle, 0, flags) == NGX_OK) {\n                    /* void */\n                }\n            }\n\n            tested = 0;\n        }\n    }\n\n    if (flags & NGX_UPDATE_TIME) {\n        ngx_time_update();\n    }\n\n    ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,\n                  \"rt signal queue overflow recovered\");\n\n    overflow = 0;\n    ngx_event_actions.process_events = ngx_rtsig_process_events;\n\n    return NGX_OK;\n}\n\n\nstatic void *\nngx_rtsig_create_conf(ngx_cycle_t *cycle)\n{\n    ngx_rtsig_conf_t  *rtscf;\n\n    rtscf = ngx_palloc(cycle->pool, sizeof(ngx_rtsig_conf_t));\n    if (rtscf == NULL) {\n        return NULL;\n    }\n\n    rtscf->signo = NGX_CONF_UNSET;\n    rtscf->overflow_events = NGX_CONF_UNSET;\n    rtscf->overflow_test = NGX_CONF_UNSET;\n    rtscf->overflow_threshold = NGX_CONF_UNSET;\n\n    return rtscf;\n}\n\n\nstatic char *\nngx_rtsig_init_conf(ngx_cycle_t *cycle, void *conf)\n{\n    ngx_rtsig_conf_t  *rtscf = conf;\n\n    /* LinuxThreads use the first 3 RT signals */\n    ngx_conf_init_uint_value(rtscf->signo, SIGRTMIN + 10);\n\n    ngx_conf_init_uint_value(rtscf->overflow_events, 16);\n    ngx_conf_init_uint_value(rtscf->overflow_test, 32);\n    ngx_conf_init_uint_value(rtscf->overflow_threshold, 10);\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_check_ngx_overflow_threshold_bounds(ngx_conf_t *cf, void *post, void *data)\n{\n    if (ngx_linux_rtsig_max) {\n        return ngx_conf_check_num_bounds(cf, post, data);\n    }\n\n    ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                       \"\\\"rtsig_overflow_threshold\\\" is not supported \"\n                       \"since Linux 2.6.6-mm2, ignored\");\n\n    return NGX_CONF_OK;\n}\n"
  },
  {
    "path": "src/event/modules/ngx_select_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n\n\nstatic ngx_int_t ngx_select_init(ngx_cycle_t *cycle, ngx_msec_t timer);\nstatic void ngx_select_done(ngx_cycle_t *cycle);\nstatic ngx_int_t ngx_select_add_event(ngx_event_t *ev, ngx_int_t event,\n    ngx_uint_t flags);\nstatic ngx_int_t ngx_select_del_event(ngx_event_t *ev, ngx_int_t event,\n    ngx_uint_t flags);\nstatic ngx_int_t ngx_select_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,\n    ngx_uint_t flags);\nstatic void ngx_select_repair_fd_sets(ngx_cycle_t *cycle);\nstatic char *ngx_select_init_conf(ngx_cycle_t *cycle, void *conf);\n\n\nstatic fd_set         master_read_fd_set;\nstatic fd_set         master_write_fd_set;\nstatic fd_set         work_read_fd_set;\nstatic fd_set         work_write_fd_set;\n\nstatic ngx_int_t      max_fd;\nstatic ngx_uint_t     nevents;\n\nstatic ngx_event_t  **event_index;\n\n\nstatic ngx_str_t           select_name = ngx_string(\"select\");\n\nstatic ngx_event_module_t  ngx_select_module_ctx = {\n    &select_name,\n    NULL,                                  /* create configuration */\n    ngx_select_init_conf,                  /* init configuration */\n\n    {\n        ngx_select_add_event,              /* add an event */\n        ngx_select_del_event,              /* delete an event */\n        ngx_select_add_event,              /* enable an event */\n        ngx_select_del_event,              /* disable an event */\n        NULL,                              /* add an connection */\n        NULL,                              /* delete an connection */\n        NULL,                              /* trigger a notify */\n        ngx_select_process_events,         /* process the events */\n        ngx_select_init,                   /* init the events */\n        ngx_select_done,                   /* done the events */\n#if (NGX_SSL && NGX_SSL_ASYNC)\n        NULL,                              /* add an async conn */\n        NULL,                              /* del an async conn */\n#endif\n    }\n\n};\n\nngx_module_t  ngx_select_module = {\n    NGX_MODULE_V1,\n    &ngx_select_module_ctx,                /* module context */\n    NULL,                                  /* module directives */\n    NGX_EVENT_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_select_init(ngx_cycle_t *cycle, ngx_msec_t timer)\n{\n    ngx_event_t  **index;\n\n    if (event_index == NULL) {\n        FD_ZERO(&master_read_fd_set);\n        FD_ZERO(&master_write_fd_set);\n        nevents = 0;\n    }\n\n    if (ngx_process >= NGX_PROCESS_WORKER\n        || cycle->old_cycle == NULL\n        || cycle->old_cycle->connection_n < cycle->connection_n)\n    {\n        index = ngx_alloc(sizeof(ngx_event_t *) * 2 * cycle->connection_n,\n                          cycle->log);\n        if (index == NULL) {\n            return NGX_ERROR;\n        }\n\n        if (event_index) {\n            ngx_memcpy(index, event_index, sizeof(ngx_event_t *) * nevents);\n            ngx_free(event_index);\n        }\n\n        event_index = index;\n    }\n\n    ngx_io = ngx_os_io;\n\n    ngx_event_actions = ngx_select_module_ctx.actions;\n\n    ngx_event_flags = NGX_USE_LEVEL_EVENT;\n\n    max_fd = -1;\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_select_done(ngx_cycle_t *cycle)\n{\n    ngx_free(event_index);\n\n    event_index = NULL;\n}\n\n\nstatic ngx_int_t\nngx_select_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)\n{\n    ngx_connection_t  *c;\n\n    c = ev->data;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,\n                   \"select add event fd:%d ev:%i\", c->fd, event);\n\n    if (ev->index != NGX_INVALID_INDEX) {\n        ngx_log_error(NGX_LOG_ALERT, ev->log, 0,\n                      \"select event fd:%d ev:%i is already set\", c->fd, event);\n        return NGX_OK;\n    }\n\n    if ((event == NGX_READ_EVENT && ev->write)\n        || (event == NGX_WRITE_EVENT && !ev->write))\n    {\n        ngx_log_error(NGX_LOG_ALERT, ev->log, 0,\n                      \"invalid select %s event fd:%d ev:%i\",\n                      ev->write ? \"write\" : \"read\", c->fd, event);\n        return NGX_ERROR;\n    }\n\n    if (event == NGX_READ_EVENT) {\n        FD_SET(c->fd, &master_read_fd_set);\n\n    } else if (event == NGX_WRITE_EVENT) {\n        FD_SET(c->fd, &master_write_fd_set);\n    }\n\n    if (max_fd != -1 && max_fd < c->fd) {\n        max_fd = c->fd;\n    }\n\n    ev->active = 1;\n\n    event_index[nevents] = ev;\n    ev->index = nevents;\n    nevents++;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_select_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)\n{\n    ngx_event_t       *e;\n    ngx_connection_t  *c;\n\n    c = ev->data;\n\n    ev->active = 0;\n\n    if (ev->index == NGX_INVALID_INDEX) {\n        return NGX_OK;\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,\n                   \"select del event fd:%d ev:%i\", c->fd, event);\n\n    if (event == NGX_READ_EVENT) {\n        FD_CLR(c->fd, &master_read_fd_set);\n\n    } else if (event == NGX_WRITE_EVENT) {\n        FD_CLR(c->fd, &master_write_fd_set);\n    }\n\n    if (max_fd == c->fd) {\n        max_fd = -1;\n    }\n\n    if (ev->index < --nevents) {\n        e = event_index[nevents];\n        event_index[ev->index] = e;\n        e->index = ev->index;\n    }\n\n    ev->index = NGX_INVALID_INDEX;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_select_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,\n    ngx_uint_t flags)\n{\n    int                ready, nready;\n    ngx_err_t          err;\n    ngx_uint_t         i, found;\n    ngx_event_t       *ev;\n    ngx_queue_t       *queue;\n    struct timeval     tv, *tp;\n    ngx_connection_t  *c;\n\n    if (max_fd == -1) {\n        for (i = 0; i < nevents; i++) {\n            c = event_index[i]->data;\n            if (max_fd < c->fd) {\n                max_fd = c->fd;\n            }\n        }\n\n        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                       \"change max_fd: %i\", max_fd);\n    }\n\n#if (NGX_DEBUG)\n    if (cycle->log->log_level & NGX_LOG_DEBUG_ALL) {\n        for (i = 0; i < nevents; i++) {\n            ev = event_index[i];\n            c = ev->data;\n            ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                           \"select event: fd:%d wr:%d\", c->fd, ev->write);\n        }\n\n        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                       \"max_fd: %i\", max_fd);\n    }\n#endif\n\n    if (timer == NGX_TIMER_INFINITE) {\n        tp = NULL;\n\n    } else {\n        tv.tv_sec = (long) (timer / 1000);\n        tv.tv_usec = (long) ((timer % 1000) * 1000);\n        tp = &tv;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                   \"select timer: %M\", timer);\n\n    work_read_fd_set = master_read_fd_set;\n    work_write_fd_set = master_write_fd_set;\n\n    ready = select(max_fd + 1, &work_read_fd_set, &work_write_fd_set, NULL, tp);\n\n    err = (ready == -1) ? ngx_errno : 0;\n\n    if (flags & NGX_UPDATE_TIME || ngx_event_timer_alarm) {\n        ngx_time_update();\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                   \"select ready %d\", ready);\n\n    if (err) {\n        ngx_uint_t  level;\n\n        if (err == NGX_EINTR) {\n\n            if (ngx_event_timer_alarm) {\n                ngx_event_timer_alarm = 0;\n                return NGX_OK;\n            }\n\n            level = NGX_LOG_INFO;\n\n        } else {\n            level = NGX_LOG_ALERT;\n        }\n\n        ngx_log_error(level, cycle->log, err, \"select() failed\");\n\n        if (err == NGX_EBADF) {\n            ngx_select_repair_fd_sets(cycle);\n        }\n\n        return NGX_ERROR;\n    }\n\n    if (ready == 0) {\n        if (timer != NGX_TIMER_INFINITE) {\n            return NGX_OK;\n        }\n\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,\n                      \"select() returned no events without timeout\");\n        return NGX_ERROR;\n    }\n\n    nready = 0;\n\n    for (i = 0; i < nevents; i++) {\n        ev = event_index[i];\n        c = ev->data;\n        found = 0;\n\n        if (ev->write) {\n            if (FD_ISSET(c->fd, &work_write_fd_set)) {\n                found = 1;\n                ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                               \"select write %d\", c->fd);\n            }\n\n        } else {\n            if (FD_ISSET(c->fd, &work_read_fd_set)) {\n                found = 1;\n                ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                               \"select read %d\", c->fd);\n            }\n        }\n\n        if (found) {\n            ev->ready = 1;\n            ev->available = -1;\n\n            queue = ev->accept ? &ngx_posted_accept_events\n                               : &ngx_posted_events;\n\n            ngx_post_event(ev, queue);\n\n            nready++;\n        }\n    }\n\n    if (ready != nready) {\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,\n                      \"select ready != events: %d:%d\", ready, nready);\n\n        ngx_select_repair_fd_sets(cycle);\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_select_repair_fd_sets(ngx_cycle_t *cycle)\n{\n    int           n;\n    socklen_t     len;\n    ngx_err_t     err;\n    ngx_socket_t  s;\n\n    for (s = 0; s <= max_fd; s++) {\n\n        if (FD_ISSET(s, &master_read_fd_set) == 0) {\n            continue;\n        }\n\n        len = sizeof(int);\n\n        if (getsockopt(s, SOL_SOCKET, SO_TYPE, &n, &len) == -1) {\n            err = ngx_socket_errno;\n\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, err,\n                          \"invalid descriptor #%d in read fd_set\", s);\n\n            FD_CLR(s, &master_read_fd_set);\n        }\n    }\n\n    for (s = 0; s <= max_fd; s++) {\n\n        if (FD_ISSET(s, &master_write_fd_set) == 0) {\n            continue;\n        }\n\n        len = sizeof(int);\n\n        if (getsockopt(s, SOL_SOCKET, SO_TYPE, &n, &len) == -1) {\n            err = ngx_socket_errno;\n\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, err,\n                          \"invalid descriptor #%d in write fd_set\", s);\n\n            FD_CLR(s, &master_write_fd_set);\n        }\n    }\n\n    max_fd = -1;\n}\n\n\nstatic char *\nngx_select_init_conf(ngx_cycle_t *cycle, void *conf)\n{\n    ngx_event_conf_t  *ecf;\n\n    ecf = ngx_event_get_conf(cycle->conf_ctx, ngx_event_core_module);\n\n    if (ecf->use != ngx_select_module.ctx_index) {\n        return NGX_CONF_OK;\n    }\n\n    /* disable warning: the default FD_SETSIZE is 1024U in FreeBSD 5.x */\n\n    if (cycle->connection_n > FD_SETSIZE) {\n        ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,\n                      \"the maximum number of files \"\n                      \"supported by select() is %ud\", FD_SETSIZE);\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n"
  },
  {
    "path": "src/event/modules/ngx_win32_poll_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Maxim Dounin\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n\n\nstatic ngx_int_t ngx_poll_init(ngx_cycle_t *cycle, ngx_msec_t timer);\nstatic void ngx_poll_done(ngx_cycle_t *cycle);\nstatic ngx_int_t ngx_poll_add_event(ngx_event_t *ev, ngx_int_t event,\n    ngx_uint_t flags);\nstatic ngx_int_t ngx_poll_del_event(ngx_event_t *ev, ngx_int_t event,\n    ngx_uint_t flags);\nstatic ngx_int_t ngx_poll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,\n    ngx_uint_t flags);\nstatic char *ngx_poll_init_conf(ngx_cycle_t *cycle, void *conf);\n\n\nstatic struct pollfd      *event_list;\nstatic ngx_connection_t  **event_index;\nstatic ngx_uint_t          nevents;\n\n\nstatic ngx_str_t           poll_name = ngx_string(\"poll\");\n\nstatic ngx_event_module_t  ngx_poll_module_ctx = {\n    &poll_name,\n    NULL,                                  /* create configuration */\n    ngx_poll_init_conf,                    /* init configuration */\n\n    {\n        ngx_poll_add_event,                /* add an event */\n        ngx_poll_del_event,                /* delete an event */\n        ngx_poll_add_event,                /* enable an event */\n        ngx_poll_del_event,                /* disable an event */\n        NULL,                              /* add an connection */\n        NULL,                              /* delete an connection */\n        NULL,                              /* trigger a notify */\n        ngx_poll_process_events,           /* process the events */\n        ngx_poll_init,                     /* init the events */\n        ngx_poll_done                      /* done the events */\n    }\n\n};\n\nngx_module_t  ngx_poll_module = {\n    NGX_MODULE_V1,\n    &ngx_poll_module_ctx,                  /* module context */\n    NULL,                                  /* module directives */\n    NGX_EVENT_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\nstatic ngx_int_t\nngx_poll_init(ngx_cycle_t *cycle, ngx_msec_t timer)\n{\n    struct pollfd      *list;\n    ngx_connection_t  **index;\n\n    if (event_list == NULL) {\n        nevents = 0;\n    }\n\n    if (ngx_process >= NGX_PROCESS_WORKER\n        || cycle->old_cycle == NULL\n        || cycle->old_cycle->connection_n < cycle->connection_n)\n    {\n        list = ngx_alloc(sizeof(struct pollfd) * cycle->connection_n,\n                         cycle->log);\n        if (list == NULL) {\n            return NGX_ERROR;\n        }\n\n        if (event_list) {\n            ngx_memcpy(list, event_list, sizeof(struct pollfd) * nevents);\n            ngx_free(event_list);\n        }\n\n        event_list = list;\n\n        index = ngx_alloc(sizeof(ngx_connection_t *) * cycle->connection_n,\n                          cycle->log);\n        if (index == NULL) {\n            return NGX_ERROR;\n        }\n\n        if (event_index) {\n            ngx_memcpy(index, event_index,\n                       sizeof(ngx_connection_t *) * nevents);\n            ngx_free(event_index);\n        }\n\n        event_index = index;\n    }\n\n    ngx_io = ngx_os_io;\n\n    ngx_event_actions = ngx_poll_module_ctx.actions;\n\n    ngx_event_flags = NGX_USE_LEVEL_EVENT;\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_poll_done(ngx_cycle_t *cycle)\n{\n    ngx_free(event_list);\n    ngx_free(event_index);\n\n    event_list = NULL;\n    event_index = NULL;\n}\n\n\nstatic ngx_int_t\nngx_poll_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)\n{\n    ngx_event_t       *e;\n    ngx_connection_t  *c;\n\n    c = ev->data;\n\n    ev->active = 1;\n\n    if (ev->index != NGX_INVALID_INDEX) {\n        ngx_log_error(NGX_LOG_ALERT, ev->log, 0,\n                      \"poll event fd:%d ev:%i is already set\", c->fd, event);\n        return NGX_OK;\n    }\n\n    if (event == NGX_READ_EVENT) {\n        e = c->write;\n#if (NGX_READ_EVENT != POLLIN)\n        event = POLLIN;\n#endif\n\n    } else {\n        e = c->read;\n#if (NGX_WRITE_EVENT != POLLOUT)\n        event = POLLOUT;\n#endif\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,\n                   \"poll add event: fd:%d ev:%i\", c->fd, event);\n\n    if (e == NULL || e->index == NGX_INVALID_INDEX) {\n\n        event_list[nevents].fd = c->fd;\n        event_list[nevents].events = (short) event;\n        event_list[nevents].revents = 0;\n\n        event_index[nevents] = c;\n\n        ev->index = nevents;\n        nevents++;\n\n    } else {\n        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ev->log, 0,\n                       \"poll add index: %i\", e->index);\n\n        event_list[e->index].events |= (short) event;\n        ev->index = e->index;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_poll_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)\n{\n    ngx_event_t       *e;\n    ngx_connection_t  *c;\n\n    c = ev->data;\n\n    ev->active = 0;\n\n    if (ev->index == NGX_INVALID_INDEX) {\n        ngx_log_error(NGX_LOG_ALERT, ev->log, 0,\n                      \"poll event fd:%d ev:%i is already deleted\",\n                      c->fd, event);\n        return NGX_OK;\n    }\n\n    if (event == NGX_READ_EVENT) {\n        e = c->write;\n#if (NGX_READ_EVENT != POLLIN)\n        event = POLLIN;\n#endif\n\n    } else {\n        e = c->read;\n#if (NGX_WRITE_EVENT != POLLOUT)\n        event = POLLOUT;\n#endif\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,\n                   \"poll del event: fd:%d ev:%i\", c->fd, event);\n\n    if (e == NULL || e->index == NGX_INVALID_INDEX) {\n        nevents--;\n\n        if (ev->index < nevents) {\n\n            ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,\n                           \"index: copy event %ui to %i\", nevents, ev->index);\n\n            event_list[ev->index] = event_list[nevents];\n            event_index[ev->index] = event_index[nevents];\n\n            c = event_index[ev->index];\n\n            if (c->fd == (ngx_socket_t) -1) {\n                ngx_log_error(NGX_LOG_ALERT, ev->log, 0,\n                              \"unexpected last event\");\n\n            } else {\n                if (c->read->index == nevents) {\n                    c->read->index = ev->index;\n                }\n\n                if (c->write->index == nevents) {\n                    c->write->index = ev->index;\n                }\n            }\n        }\n\n    } else {\n        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ev->log, 0,\n                       \"poll del index: %i\", e->index);\n\n        event_list[e->index].events &= (short) ~event;\n    }\n\n    ev->index = NGX_INVALID_INDEX;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_poll_process_events(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags)\n{\n    int                 ready, revents;\n    ngx_err_t           err;\n    ngx_uint_t          i, found;\n    ngx_event_t        *ev;\n    ngx_queue_t        *queue;\n    ngx_connection_t   *c;\n\n    /* NGX_TIMER_INFINITE == INFTIM */\n\n#if (NGX_DEBUG0)\n    if (cycle->log->log_level & NGX_LOG_DEBUG_ALL) {\n        for (i = 0; i < nevents; i++) {\n            ngx_log_debug3(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                           \"poll: %ui: fd:%d ev:%04Xd\",\n                           i, event_list[i].fd, event_list[i].events);\n        }\n    }\n#endif\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0, \"poll timer: %M\", timer);\n\n    ready = WSAPoll(event_list, (u_int) nevents, (int) timer);\n\n    err = (ready == -1) ? ngx_errno : 0;\n\n    if (flags & NGX_UPDATE_TIME || ngx_event_timer_alarm) {\n        ngx_time_update();\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                   \"poll ready %d of %ui\", ready, nevents);\n\n    if (err) {\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, err, \"WSAPoll() failed\");\n        return NGX_ERROR;\n    }\n\n    if (ready == 0) {\n        if (timer != NGX_TIMER_INFINITE) {\n            return NGX_OK;\n        }\n\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,\n                      \"WSAPoll() returned no events without timeout\");\n        return NGX_ERROR;\n    }\n\n    for (i = 0; i < nevents && ready; i++) {\n\n        revents = event_list[i].revents;\n\n#if 1\n        ngx_log_debug4(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                       \"poll: %ui: fd:%d ev:%04Xd rev:%04Xd\",\n                       i, event_list[i].fd, event_list[i].events, revents);\n#else\n        if (revents) {\n            ngx_log_debug4(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                           \"poll: %ui: fd:%d ev:%04Xd rev:%04Xd\",\n                           i, event_list[i].fd, event_list[i].events, revents);\n        }\n#endif\n\n        if (revents & POLLNVAL) {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,\n                          \"poll() error fd:%d ev:%04Xd rev:%04Xd\",\n                          event_list[i].fd, event_list[i].events, revents);\n        }\n\n        if (revents & ~(POLLIN|POLLOUT|POLLERR|POLLHUP|POLLNVAL)) {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,\n                          \"strange poll() events fd:%d ev:%04Xd rev:%04Xd\",\n                          event_list[i].fd, event_list[i].events, revents);\n        }\n\n        if (event_list[i].fd == (ngx_socket_t) -1) {\n            /*\n             * the disabled event, a workaround for our possible bug,\n             * see the comment below\n             */\n            continue;\n        }\n\n        c = event_index[i];\n\n        if (c->fd == (ngx_socket_t) -1) {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, \"unexpected event\");\n\n            /*\n             * it is certainly our fault and it should be investigated,\n             * in the meantime we disable this event to avoid a CPU spinning\n             */\n\n            if (i == nevents - 1) {\n                nevents--;\n            } else {\n                event_list[i].fd = (ngx_socket_t) -1;\n            }\n\n            continue;\n        }\n\n        if (revents & (POLLERR|POLLHUP|POLLNVAL)) {\n\n            /*\n             * if the error events were returned, add POLLIN and POLLOUT\n             * to handle the events at least in one active handler\n             */\n\n            revents |= POLLIN|POLLOUT;\n        }\n\n        found = 0;\n\n        if ((revents & POLLIN) && c->read->active) {\n            found = 1;\n\n            ev = c->read;\n            ev->ready = 1;\n            ev->available = -1;\n\n            queue = ev->accept ? &ngx_posted_accept_events\n                               : &ngx_posted_events;\n\n            ngx_post_event(ev, queue);\n        }\n\n        if ((revents & POLLOUT) && c->write->active) {\n            found = 1;\n\n            ev = c->write;\n            ev->ready = 1;\n\n            ngx_post_event(ev, &ngx_posted_events);\n        }\n\n        if (found) {\n            ready--;\n            continue;\n        }\n    }\n\n    if (ready != 0) {\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, \"poll ready != events\");\n    }\n\n    return NGX_OK;\n}\n\n\nstatic char *\nngx_poll_init_conf(ngx_cycle_t *cycle, void *conf)\n{\n    ngx_event_conf_t  *ecf;\n\n    ecf = ngx_event_get_conf(cycle->conf_ctx, ngx_event_core_module);\n\n    if (ecf->use != ngx_poll_module.ctx_index) {\n        return NGX_CONF_OK;\n    }\n\n#if (NGX_LOAD_WSAPOLL)\n\n    if (!ngx_have_wsapoll) {\n        ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,\n                      \"poll is not available on this platform\");\n        return NGX_CONF_ERROR;\n    }\n\n#endif\n\n    return NGX_CONF_OK;\n}\n"
  },
  {
    "path": "src/event/modules/ngx_win32_select_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n\n\nstatic ngx_int_t ngx_select_init(ngx_cycle_t *cycle, ngx_msec_t timer);\nstatic void ngx_select_done(ngx_cycle_t *cycle);\nstatic ngx_int_t ngx_select_add_event(ngx_event_t *ev, ngx_int_t event,\n    ngx_uint_t flags);\nstatic ngx_int_t ngx_select_del_event(ngx_event_t *ev, ngx_int_t event,\n    ngx_uint_t flags);\nstatic ngx_int_t ngx_select_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,\n    ngx_uint_t flags);\nstatic void ngx_select_repair_fd_sets(ngx_cycle_t *cycle);\nstatic char *ngx_select_init_conf(ngx_cycle_t *cycle, void *conf);\n\n\nstatic fd_set         master_read_fd_set;\nstatic fd_set         master_write_fd_set;\nstatic fd_set         work_read_fd_set;\nstatic fd_set         work_write_fd_set;\nstatic fd_set         work_except_fd_set;\n\nstatic ngx_uint_t     max_read;\nstatic ngx_uint_t     max_write;\nstatic ngx_uint_t     nevents;\n\nstatic ngx_event_t  **event_index;\n\n\nstatic ngx_str_t           select_name = ngx_string(\"select\");\n\nstatic ngx_event_module_t  ngx_select_module_ctx = {\n    &select_name,\n    NULL,                                  /* create configuration */\n    ngx_select_init_conf,                  /* init configuration */\n\n    {\n        ngx_select_add_event,              /* add an event */\n        ngx_select_del_event,              /* delete an event */\n        ngx_select_add_event,              /* enable an event */\n        ngx_select_del_event,              /* disable an event */\n        NULL,                              /* add an connection */\n        NULL,                              /* delete an connection */\n        NULL,                              /* trigger a notify */\n        ngx_select_process_events,         /* process the events */\n        ngx_select_init,                   /* init the events */\n        ngx_select_done,                   /* done the events */\n#if (NGX_SSL && NGX_SSL_ASYNC)\n        NULL,                              /* add an async conn */\n        NULL,                              /* del an async conn */\n#endif\n    }\n\n};\n\nngx_module_t  ngx_select_module = {\n    NGX_MODULE_V1,\n    &ngx_select_module_ctx,                /* module context */\n    NULL,                                  /* module directives */\n    NGX_EVENT_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_select_init(ngx_cycle_t *cycle, ngx_msec_t timer)\n{\n    ngx_event_t  **index;\n\n    if (event_index == NULL) {\n        FD_ZERO(&master_read_fd_set);\n        FD_ZERO(&master_write_fd_set);\n        nevents = 0;\n    }\n\n    if (ngx_process >= NGX_PROCESS_WORKER\n        || cycle->old_cycle == NULL\n        || cycle->old_cycle->connection_n < cycle->connection_n)\n    {\n        index = ngx_alloc(sizeof(ngx_event_t *) * 2 * cycle->connection_n,\n                          cycle->log);\n        if (index == NULL) {\n            return NGX_ERROR;\n        }\n\n        if (event_index) {\n            ngx_memcpy(index, event_index, sizeof(ngx_event_t *) * nevents);\n            ngx_free(event_index);\n        }\n\n        event_index = index;\n    }\n\n    ngx_io = ngx_os_io;\n\n    ngx_event_actions = ngx_select_module_ctx.actions;\n\n    ngx_event_flags = NGX_USE_LEVEL_EVENT;\n\n    max_read = 0;\n    max_write = 0;\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_select_done(ngx_cycle_t *cycle)\n{\n    ngx_free(event_index);\n\n    event_index = NULL;\n}\n\n\nstatic ngx_int_t\nngx_select_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)\n{\n    ngx_connection_t  *c;\n\n    c = ev->data;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,\n                   \"select add event fd:%d ev:%i\", c->fd, event);\n\n    if (ev->index != NGX_INVALID_INDEX) {\n        ngx_log_error(NGX_LOG_ALERT, ev->log, 0,\n                      \"select event fd:%d ev:%i is already set\", c->fd, event);\n        return NGX_OK;\n    }\n\n    if ((event == NGX_READ_EVENT && ev->write)\n        || (event == NGX_WRITE_EVENT && !ev->write))\n    {\n        ngx_log_error(NGX_LOG_ALERT, ev->log, 0,\n                      \"invalid select %s event fd:%d ev:%i\",\n                      ev->write ? \"write\" : \"read\", c->fd, event);\n        return NGX_ERROR;\n    }\n\n    if ((event == NGX_READ_EVENT && max_read >= FD_SETSIZE)\n        || (event == NGX_WRITE_EVENT && max_write >= FD_SETSIZE))\n    {\n        ngx_log_error(NGX_LOG_ERR, ev->log, 0,\n                      \"maximum number of descriptors \"\n                      \"supported by select() is %d\", FD_SETSIZE);\n        return NGX_ERROR;\n    }\n\n    if (event == NGX_READ_EVENT) {\n        FD_SET(c->fd, &master_read_fd_set);\n        max_read++;\n\n    } else if (event == NGX_WRITE_EVENT) {\n        FD_SET(c->fd, &master_write_fd_set);\n        max_write++;\n    }\n\n    ev->active = 1;\n\n    event_index[nevents] = ev;\n    ev->index = nevents;\n    nevents++;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_select_del_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)\n{\n    ngx_event_t       *e;\n    ngx_connection_t  *c;\n\n    c = ev->data;\n\n    ev->active = 0;\n\n    if (ev->index == NGX_INVALID_INDEX) {\n        return NGX_OK;\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,\n                   \"select del event fd:%d ev:%i\", c->fd, event);\n\n    if (event == NGX_READ_EVENT) {\n        FD_CLR(c->fd, &master_read_fd_set);\n        max_read--;\n\n    } else if (event == NGX_WRITE_EVENT) {\n        FD_CLR(c->fd, &master_write_fd_set);\n        max_write--;\n    }\n\n    if (ev->index < --nevents) {\n        e = event_index[nevents];\n        event_index[ev->index] = e;\n        e->index = ev->index;\n    }\n\n    ev->index = NGX_INVALID_INDEX;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_select_process_events(ngx_cycle_t *cycle, ngx_msec_t timer,\n    ngx_uint_t flags)\n{\n    int                ready, nready;\n    ngx_err_t          err;\n    ngx_uint_t         i, found;\n    ngx_event_t       *ev;\n    ngx_queue_t       *queue;\n    struct timeval     tv, *tp;\n    ngx_connection_t  *c;\n\n#if (NGX_DEBUG)\n    if (cycle->log->log_level & NGX_LOG_DEBUG_ALL) {\n        for (i = 0; i < nevents; i++) {\n            ev = event_index[i];\n            c = ev->data;\n            ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                           \"select event: fd:%d wr:%d\", c->fd, ev->write);\n        }\n    }\n#endif\n\n    if (timer == NGX_TIMER_INFINITE) {\n        tp = NULL;\n\n    } else {\n        tv.tv_sec = (long) (timer / 1000);\n        tv.tv_usec = (long) ((timer % 1000) * 1000);\n        tp = &tv;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                   \"select timer: %M\", timer);\n\n    work_read_fd_set = master_read_fd_set;\n    work_write_fd_set = master_write_fd_set;\n    work_except_fd_set = master_write_fd_set;\n\n    if (max_read || max_write) {\n        ready = select(0, &work_read_fd_set, &work_write_fd_set,\n                       &work_except_fd_set, tp);\n\n    } else {\n\n        /*\n         * Winsock select() requires that at least one descriptor set must be\n         * be non-null, and any non-null descriptor set must contain at least\n         * one handle to a socket.  Otherwise select() returns WSAEINVAL.\n         */\n\n        ngx_msleep(timer);\n\n        ready = 0;\n    }\n\n    err = (ready == -1) ? ngx_socket_errno : 0;\n\n    if (flags & NGX_UPDATE_TIME) {\n        ngx_time_update();\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                   \"select ready %d\", ready);\n\n    if (err) {\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, err, \"select() failed\");\n\n        if (err == WSAENOTSOCK) {\n            ngx_select_repair_fd_sets(cycle);\n        }\n\n        return NGX_ERROR;\n    }\n\n    if (ready == 0) {\n        if (timer != NGX_TIMER_INFINITE) {\n            return NGX_OK;\n        }\n\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,\n                      \"select() returned no events without timeout\");\n        return NGX_ERROR;\n    }\n\n    nready = 0;\n\n    for (i = 0; i < nevents; i++) {\n        ev = event_index[i];\n        c = ev->data;\n        found = 0;\n\n        if (ev->write) {\n            if (FD_ISSET(c->fd, &work_write_fd_set)) {\n                found++;\n                ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                               \"select write %d\", c->fd);\n            }\n\n            if (FD_ISSET(c->fd, &work_except_fd_set)) {\n                found++;\n                ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                               \"select except %d\", c->fd);\n            }\n\n        } else {\n            if (FD_ISSET(c->fd, &work_read_fd_set)) {\n                found++;\n                ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                               \"select read %d\", c->fd);\n            }\n        }\n\n        if (found) {\n            ev->ready = 1;\n            ev->available = -1;\n\n            queue = ev->accept ? &ngx_posted_accept_events\n                               : &ngx_posted_events;\n\n            ngx_post_event(ev, queue);\n\n            nready += found;\n        }\n    }\n\n    if (ready != nready) {\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,\n                      \"select ready != events: %d:%d\", ready, nready);\n\n        ngx_select_repair_fd_sets(cycle);\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_select_repair_fd_sets(ngx_cycle_t *cycle)\n{\n    int           n;\n    u_int         i;\n    socklen_t     len;\n    ngx_err_t     err;\n    ngx_socket_t  s;\n\n    for (i = 0; i < master_read_fd_set.fd_count; i++) {\n\n        s = master_read_fd_set.fd_array[i];\n        len = sizeof(int);\n\n        if (getsockopt(s, SOL_SOCKET, SO_TYPE, (char *) &n, &len) == -1) {\n            err = ngx_socket_errno;\n\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, err,\n                          \"invalid descriptor #%d in read fd_set\", s);\n\n            FD_CLR(s, &master_read_fd_set);\n        }\n    }\n\n    for (i = 0; i < master_write_fd_set.fd_count; i++) {\n\n        s = master_write_fd_set.fd_array[i];\n        len = sizeof(int);\n\n        if (getsockopt(s, SOL_SOCKET, SO_TYPE, (char *) &n, &len) == -1) {\n            err = ngx_socket_errno;\n\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, err,\n                          \"invalid descriptor #%d in write fd_set\", s);\n\n            FD_CLR(s, &master_write_fd_set);\n        }\n    }\n}\n\n\nstatic char *\nngx_select_init_conf(ngx_cycle_t *cycle, void *conf)\n{\n    ngx_event_conf_t  *ecf;\n\n    ecf = ngx_event_get_conf(cycle->conf_ctx, ngx_event_core_module);\n\n    if (ecf->use != ngx_select_module.ctx_index) {\n        return NGX_CONF_OK;\n    }\n\n    return NGX_CONF_OK;\n}\n"
  },
  {
    "path": "src/event/ngx_dlopen.h",
    "content": "\n/*\n * Copyright (C) Maxim Dounin\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_DLOPEN_H_INCLUDED_\n#define _NGX_DLOPEN_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\n#define ngx_dlopen(path)           dlopen((char *) path, RTLD_NOW | RTLD_GLOBAL)\n#define ngx_dlopen_n               \"dlopen()\"\n\n#define ngx_dlsym(handle, symbol)  dlsym(handle, symbol)\n#define ngx_dlsym_n                \"dlsym()\"\n\n#define ngx_dlclose(handle)        dlclose(handle)\n#define ngx_dlclose_n              \"dlclose()\"\n\n\n#if (NGX_HAVE_DLOPEN)\nchar *ngx_dlerror(void);\n#endif\n\n\n#endif /* _NGX_DLOPEN_H_INCLUDED_ */\n"
  },
  {
    "path": "src/event/ngx_event.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n\n\n#define DEFAULT_CONNECTIONS  512\n\n\nextern ngx_module_t ngx_kqueue_module;\nextern ngx_module_t ngx_eventport_module;\nextern ngx_module_t ngx_devpoll_module;\nextern ngx_module_t ngx_epoll_module;\nextern ngx_module_t ngx_select_module;\n\n\nstatic char *ngx_event_init_conf(ngx_cycle_t *cycle, void *conf);\nstatic ngx_int_t ngx_event_module_init(ngx_cycle_t *cycle);\nstatic ngx_int_t ngx_event_process_init(ngx_cycle_t *cycle);\nstatic char *ngx_events_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\n\nstatic char *ngx_event_connections(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_event_use(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\nstatic char *ngx_event_debug_connection(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n\nstatic void *ngx_event_core_create_conf(ngx_cycle_t *cycle);\nstatic char *ngx_event_core_init_conf(ngx_cycle_t *cycle, void *conf);\n\n#if (T_NGX_ACCEPT_FILTER)\nstatic ngx_int_t ngx_event_dummy_accept_filter(ngx_connection_t *c);\nngx_int_t  (*ngx_event_top_accept_filter) (ngx_connection_t *c);\n#endif\n\n\nstatic ngx_uint_t     ngx_timer_resolution;\nsig_atomic_t          ngx_event_timer_alarm;\n\nstatic ngx_uint_t     ngx_event_max_module;\n\nngx_uint_t            ngx_event_flags;\nngx_event_actions_t   ngx_event_actions;\n\n\nstatic ngx_atomic_t   connection_counter = 1;\nngx_atomic_t         *ngx_connection_counter = &connection_counter;\n\n\nngx_atomic_t         *ngx_accept_mutex_ptr;\nngx_shmtx_t           ngx_accept_mutex;\nngx_uint_t            ngx_use_accept_mutex;\nngx_uint_t            ngx_accept_events;\nngx_uint_t            ngx_accept_mutex_held;\nngx_msec_t            ngx_accept_mutex_delay;\nngx_int_t             ngx_accept_disabled;\nngx_uint_t            ngx_use_exclusive_accept;\n\n\n#if (NGX_STAT_STUB)\n\nstatic ngx_atomic_t   ngx_stat_accepted0;\nngx_atomic_t         *ngx_stat_accepted = &ngx_stat_accepted0;\nstatic ngx_atomic_t   ngx_stat_handled0;\nngx_atomic_t         *ngx_stat_handled = &ngx_stat_handled0;\nstatic ngx_atomic_t   ngx_stat_requests0;\nngx_atomic_t         *ngx_stat_requests = &ngx_stat_requests0;\nstatic ngx_atomic_t   ngx_stat_active0;\nngx_atomic_t         *ngx_stat_active = &ngx_stat_active0;\nstatic ngx_atomic_t   ngx_stat_reading0;\nngx_atomic_t         *ngx_stat_reading = &ngx_stat_reading0;\nstatic ngx_atomic_t   ngx_stat_writing0;\nngx_atomic_t         *ngx_stat_writing = &ngx_stat_writing0;\nstatic ngx_atomic_t   ngx_stat_waiting0;\nngx_atomic_t         *ngx_stat_waiting = &ngx_stat_waiting0;\n\n#if (T_NGX_HTTP_STUB_STATUS)\nstatic ngx_atomic_t   ngx_stat_request_time0;\nngx_atomic_t         *ngx_stat_request_time = &ngx_stat_request_time0;\n#endif\n\n#endif\n\n#if (T_NGX_XQUIC)\n\nngx_atomic_t   ngx_stat_quic_conns0;\nngx_atomic_t  *ngx_stat_quic_conns = &ngx_stat_quic_conns0;\nngx_atomic_t   ngx_stat_quic_cps_nexttime0;\nngx_atomic_t  *ngx_stat_quic_cps_nexttime = &ngx_stat_quic_cps_nexttime0;\nngx_atomic_t   ngx_stat_quic_cps0;\nngx_atomic_t  *ngx_stat_quic_cps = &ngx_stat_quic_cps0;\nngx_atomic_t   ngx_stat_quic_conns_refused0;\nngx_atomic_t  *ngx_stat_quic_conns_refused = &ngx_stat_quic_conns_refused0;\n\nngx_atomic_t   ngx_stat_quic_queries0;\nngx_atomic_t  *ngx_stat_quic_queries = &ngx_stat_quic_queries0;\nngx_atomic_t   ngx_stat_quic_qps_nexttime0;\nngx_atomic_t  *ngx_stat_quic_qps_nexttime = &ngx_stat_quic_qps_nexttime0;\nngx_atomic_t   ngx_stat_quic_qps0;\nngx_atomic_t  *ngx_stat_quic_qps = &ngx_stat_quic_qps0;\nngx_atomic_t   ngx_stat_quic_queries_refused0;\nngx_atomic_t  *ngx_stat_quic_queries_refused = &ngx_stat_quic_queries_refused0;\n\nngx_atomic_t   ngx_stat_quic_concurrent_conns0;\nngx_atomic_t  *ngx_stat_quic_concurrent_conns = &ngx_stat_quic_concurrent_conns0;\n\n#endif\n\nstatic ngx_command_t  ngx_events_commands[] = {\n\n    { ngx_string(\"events\"),\n      NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,\n      ngx_events_block,\n      0,\n      0,\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_core_module_t  ngx_events_module_ctx = {\n    ngx_string(\"events\"),\n    NULL,\n    ngx_event_init_conf\n};\n\n\nngx_module_t  ngx_events_module = {\n    NGX_MODULE_V1,\n    &ngx_events_module_ctx,                /* module context */\n    ngx_events_commands,                   /* module directives */\n    NGX_CORE_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_str_t  event_core_name = ngx_string(\"event_core\");\n\n\nstatic ngx_command_t  ngx_event_core_commands[] = {\n\n    { ngx_string(\"worker_connections\"),\n      NGX_EVENT_CONF|NGX_CONF_TAKE1,\n      ngx_event_connections,\n      0,\n      0,\n      NULL },\n\n    { ngx_string(\"use\"),\n      NGX_EVENT_CONF|NGX_CONF_TAKE1,\n      ngx_event_use,\n      0,\n      0,\n      NULL },\n\n    { ngx_string(\"multi_accept\"),\n      NGX_EVENT_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      0,\n      offsetof(ngx_event_conf_t, multi_accept),\n      NULL },\n\n    { ngx_string(\"accept_mutex\"),\n      NGX_EVENT_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      0,\n      offsetof(ngx_event_conf_t, accept_mutex),\n      NULL },\n\n    { ngx_string(\"accept_mutex_delay\"),\n      NGX_EVENT_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      0,\n      offsetof(ngx_event_conf_t, accept_mutex_delay),\n      NULL },\n\n    { ngx_string(\"debug_connection\"),\n      NGX_EVENT_CONF|NGX_CONF_TAKE1,\n      ngx_event_debug_connection,\n      0,\n      0,\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_event_module_t  ngx_event_core_module_ctx = {\n    &event_core_name,\n    ngx_event_core_create_conf,            /* create configuration */\n    ngx_event_core_init_conf,              /* init configuration */\n#if (NGX_SSL && NGX_SSL_ASYNC)\n    { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }\n#else\n    { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }\n#endif\n};\n\n\nngx_module_t  ngx_event_core_module = {\n    NGX_MODULE_V1,\n    &ngx_event_core_module_ctx,            /* module context */\n    ngx_event_core_commands,               /* module directives */\n    NGX_EVENT_MODULE,                      /* module type */\n    NULL,                                  /* init master */\n    ngx_event_module_init,                 /* init module */\n    ngx_event_process_init,                /* 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\nvoid\nngx_process_events_and_timers(ngx_cycle_t *cycle)\n{\n    ngx_uint_t  flags;\n    ngx_msec_t  timer, delta;\n\n    if (ngx_timer_resolution) {\n        timer = NGX_TIMER_INFINITE;\n        flags = 0;\n\n    } else {\n        timer = ngx_event_find_timer();\n        flags = NGX_UPDATE_TIME;\n\n#if (NGX_WIN32)\n\n        /* handle signals from master in case of network inactivity */\n\n        if (timer == NGX_TIMER_INFINITE || timer > 500) {\n            timer = 500;\n        }\n\n#endif\n    }\n\n    if (ngx_use_accept_mutex) {\n        if (ngx_accept_disabled > 0) {\n            ngx_accept_disabled--;\n\n        } else {\n            if (ngx_trylock_accept_mutex(cycle) == NGX_ERROR) {\n                return;\n            }\n\n            if (ngx_accept_mutex_held) {\n                flags |= NGX_POST_EVENTS;\n\n            } else {\n                if (timer == NGX_TIMER_INFINITE\n                    || timer > ngx_accept_mutex_delay)\n                {\n                    timer = ngx_accept_mutex_delay;\n                }\n            }\n        }\n    }\n\n    if (!ngx_queue_empty(&ngx_posted_next_events)) {\n        ngx_event_move_posted_next(cycle);\n        timer = 0;\n    }\n\n    delta = ngx_current_msec;\n\n    (void) ngx_process_events(cycle, timer, flags);\n\n    delta = ngx_current_msec - delta;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                   \"timer delta: %M\", delta);\n\n    ngx_event_process_posted(cycle, &ngx_posted_accept_events);\n\n    if (ngx_accept_mutex_held) {\n        ngx_shmtx_unlock(&ngx_accept_mutex);\n    }\n\n    ngx_event_expire_timers();\n\n    ngx_event_process_posted(cycle, &ngx_posted_events);\n#if (T_NGX_HAVE_XUDP)\n    ngx_event_process_posted(cycle, &ngx_posted_commit);\n#endif\n}\n\n\nngx_int_t\nngx_handle_read_event(ngx_event_t *rev, ngx_uint_t flags)\n{\n    if (ngx_event_flags & NGX_USE_CLEAR_EVENT) {\n\n        /* kqueue, epoll */\n\n        if (!rev->active && !rev->ready) {\n            if (ngx_add_event(rev, NGX_READ_EVENT, NGX_CLEAR_EVENT)\n                == NGX_ERROR)\n            {\n                return NGX_ERROR;\n            }\n        }\n\n        return NGX_OK;\n\n    } else if (ngx_event_flags & NGX_USE_LEVEL_EVENT) {\n\n        /* select, poll, /dev/poll */\n\n        if (!rev->active && !rev->ready) {\n            if (ngx_add_event(rev, NGX_READ_EVENT, NGX_LEVEL_EVENT)\n                == NGX_ERROR)\n            {\n                return NGX_ERROR;\n            }\n\n            return NGX_OK;\n        }\n\n        if (rev->active && (rev->ready || (flags & NGX_CLOSE_EVENT))) {\n            if (ngx_del_event(rev, NGX_READ_EVENT, NGX_LEVEL_EVENT | flags)\n                == NGX_ERROR)\n            {\n                return NGX_ERROR;\n            }\n\n            return NGX_OK;\n        }\n\n    } else if (ngx_event_flags & NGX_USE_EVENTPORT_EVENT) {\n\n        /* event ports */\n\n        if (!rev->active && !rev->ready) {\n            if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {\n                return NGX_ERROR;\n            }\n\n            return NGX_OK;\n        }\n\n        if (rev->oneshot && rev->ready) {\n            if (ngx_del_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {\n                return NGX_ERROR;\n            }\n\n            return NGX_OK;\n        }\n    }\n\n    /* iocp */\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_handle_write_event(ngx_event_t *wev, size_t lowat)\n{\n    ngx_connection_t  *c;\n\n    if (lowat) {\n        c = wev->data;\n\n        if (ngx_send_lowat(c, lowat) == NGX_ERROR) {\n            return NGX_ERROR;\n        }\n    }\n\n    if (ngx_event_flags & NGX_USE_CLEAR_EVENT) {\n\n        /* kqueue, epoll */\n\n        if (!wev->active && !wev->ready) {\n            if (ngx_add_event(wev, NGX_WRITE_EVENT,\n                              NGX_CLEAR_EVENT | (lowat ? NGX_LOWAT_EVENT : 0))\n                == NGX_ERROR)\n            {\n                return NGX_ERROR;\n            }\n        }\n\n        return NGX_OK;\n\n    } else if (ngx_event_flags & NGX_USE_LEVEL_EVENT) {\n\n        /* select, poll, /dev/poll */\n\n        if (!wev->active && !wev->ready) {\n            if (ngx_add_event(wev, NGX_WRITE_EVENT, NGX_LEVEL_EVENT)\n                == NGX_ERROR)\n            {\n                return NGX_ERROR;\n            }\n\n            return NGX_OK;\n        }\n\n        if (wev->active && wev->ready) {\n            if (ngx_del_event(wev, NGX_WRITE_EVENT, NGX_LEVEL_EVENT)\n                == NGX_ERROR)\n            {\n                return NGX_ERROR;\n            }\n\n            return NGX_OK;\n        }\n\n    } else if (ngx_event_flags & NGX_USE_EVENTPORT_EVENT) {\n\n        /* event ports */\n\n        if (!wev->active && !wev->ready) {\n            if (ngx_add_event(wev, NGX_WRITE_EVENT, 0) == NGX_ERROR) {\n                return NGX_ERROR;\n            }\n\n            return NGX_OK;\n        }\n\n        if (wev->oneshot && wev->ready) {\n            if (ngx_del_event(wev, NGX_WRITE_EVENT, 0) == NGX_ERROR) {\n                return NGX_ERROR;\n            }\n\n            return NGX_OK;\n        }\n    }\n\n    /* iocp */\n\n    return NGX_OK;\n}\n\n\nstatic char *\nngx_event_init_conf(ngx_cycle_t *cycle, void *conf)\n{\n#if (NGX_HAVE_REUSEPORT)\n    ngx_uint_t        i;\n    ngx_core_conf_t  *ccf;\n    ngx_listening_t  *ls;\n#endif\n\n    if (ngx_get_conf(cycle->conf_ctx, ngx_events_module) == NULL) {\n        ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,\n                      \"no \\\"events\\\" section in configuration\");\n        return NGX_CONF_ERROR;\n    }\n\n    if (cycle->connection_n < cycle->listening.nelts + 1) {\n\n        /*\n         * there should be at least one connection for each listening\n         * socket, plus an additional connection for channel\n         */\n\n        ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,\n                      \"%ui worker_connections are not enough \"\n                      \"for %ui listening sockets\",\n                      cycle->connection_n, cycle->listening.nelts);\n\n        return NGX_CONF_ERROR;\n    }\n\n#if (NGX_HAVE_REUSEPORT)\n\n    ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);\n\n    if (!ngx_test_config && ccf->master) {\n\n        ls = cycle->listening.elts;\n        for (i = 0; i < cycle->listening.nelts; i++) {\n\n            if (!ls[i].reuseport || ls[i].worker != 0) {\n                continue;\n            }\n\n            if (ngx_clone_listening(cycle, &ls[i]) != NGX_OK) {\n                return NGX_CONF_ERROR;\n            }\n\n            /* cloning may change cycle->listening.elts */\n\n            ls = cycle->listening.elts;\n        }\n    }\n\n#endif\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_event_module_init(ngx_cycle_t *cycle)\n{\n    void              ***cf;\n    u_char              *shared;\n    size_t               size, cl;\n    ngx_shm_t            shm;\n    ngx_time_t          *tp;\n    ngx_core_conf_t     *ccf;\n    ngx_event_conf_t    *ecf;\n\n    cf = ngx_get_conf(cycle->conf_ctx, ngx_events_module);\n    ecf = (*cf)[ngx_event_core_module.ctx_index];\n\n    if (!ngx_test_config && ngx_process <= NGX_PROCESS_MASTER) {\n        ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0,\n                      \"using the \\\"%s\\\" event method\", ecf->name);\n    }\n\n    ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);\n\n    ngx_timer_resolution = ccf->timer_resolution;\n\n#if !(NGX_WIN32)\n    {\n    ngx_int_t      limit;\n    struct rlimit  rlmt;\n\n    if (getrlimit(RLIMIT_NOFILE, &rlmt) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                      \"getrlimit(RLIMIT_NOFILE) failed, ignored\");\n\n    } else {\n        if (ecf->connections > (ngx_uint_t) rlmt.rlim_cur\n            && (ccf->rlimit_nofile == NGX_CONF_UNSET\n                || ecf->connections > (ngx_uint_t) ccf->rlimit_nofile))\n        {\n            limit = (ccf->rlimit_nofile == NGX_CONF_UNSET) ?\n                         (ngx_int_t) rlmt.rlim_cur : ccf->rlimit_nofile;\n\n            ngx_log_error(NGX_LOG_WARN, cycle->log, 0,\n                          \"%ui worker_connections exceed \"\n                          \"open file resource limit: %i\",\n                          ecf->connections, limit);\n        }\n    }\n    }\n#endif /* !(NGX_WIN32) */\n\n\n    if (ccf->master == 0) {\n        return NGX_OK;\n    }\n\n    if (ngx_accept_mutex_ptr) {\n        return NGX_OK;\n    }\n\n\n    /* cl should be equal to or greater than cache line size */\n\n    cl = 128;\n\n    size = cl            /* ngx_accept_mutex */\n           + cl          /* ngx_connection_counter */\n           + cl;         /* ngx_temp_number */\n\n#if (NGX_STAT_STUB)\n\n    size += cl           /* ngx_stat_accepted */\n           + cl          /* ngx_stat_handled */\n           + cl          /* ngx_stat_requests */\n           + cl          /* ngx_stat_active */\n           + cl          /* ngx_stat_reading */\n           + cl          /* ngx_stat_writing */\n           + cl          /* ngx_stat_waiting */\n#if (T_NGX_HTTP_STUB_STATUS)\n           + cl         /* ngx_stat_request_time */\n#endif\n           ;\n\n#endif\n\n#if (T_NGX_XQUIC)\n\n    size += cl          /* ngx_stat_quic_conns */\n            + cl        /* ngx_stat_quic_cps_nexttime */\n            + cl        /* ngx_stat_quic_cps */\n            + cl        /* ngx_stat_quic_conns_refused */\n            + cl        /* ngx_stat_quic_queries */\n            + cl        /* ngx_stat_quic_qps_nexttime */\n            + cl        /* ngx_stat_quic_qps */\n            + cl        /* ngx_stat_quic_queries_refused */\n            + cl;       /* ngx_stat_quic_concurrent_conns */\n\n#endif\n\n    shm.size = size;\n    ngx_str_set(&shm.name, \"nginx_shared_zone\");\n    shm.log = cycle->log;\n\n    if (ngx_shm_alloc(&shm) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    shared = shm.addr;\n\n    ngx_accept_mutex_ptr = (ngx_atomic_t *) shared;\n    ngx_accept_mutex.spin = (ngx_uint_t) -1;\n\n    if (ngx_shmtx_create(&ngx_accept_mutex, (ngx_shmtx_sh_t *) shared,\n                         cycle->lock_file.data)\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    ngx_connection_counter = (ngx_atomic_t *) (shared + 1 * cl);\n\n    (void) ngx_atomic_cmp_set(ngx_connection_counter, 0, 1);\n\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                   \"counter: %p, %uA\",\n                   ngx_connection_counter, *ngx_connection_counter);\n\n    ngx_temp_number = (ngx_atomic_t *) (shared + 2 * cl);\n\n    size_t n = 2;\n\n    tp = ngx_timeofday();\n\n    ngx_random_number = (tp->msec << 16) + ngx_pid;\n\n#if (NGX_STAT_STUB)\n\n    ngx_stat_accepted = (ngx_atomic_t *) (shared + 3 * cl);\n    ngx_stat_handled = (ngx_atomic_t *) (shared + 4 * cl);\n    ngx_stat_requests = (ngx_atomic_t *) (shared + 5 * cl);\n    ngx_stat_active = (ngx_atomic_t *) (shared + 6 * cl);\n    ngx_stat_reading = (ngx_atomic_t *) (shared + 7 * cl);\n    ngx_stat_writing = (ngx_atomic_t *) (shared + 8 * cl);\n    ngx_stat_waiting = (ngx_atomic_t *) (shared + 9 * cl);\n\n    n += 7;\n#endif\n\n#if (T_NGX_HTTP_STUB_STATUS)\n    ngx_stat_request_time = (ngx_atomic_t *) (shared + (n + 1) * cl);\n\t\n\tn += 1;\n#endif\n\n#if (T_NGX_XQUIC)\n\n    ngx_stat_quic_conns = (ngx_atomic_t *) (shared + (n + 1) * cl);\n    ngx_stat_quic_cps_nexttime = (ngx_atomic_t *) (shared + (n + 2) * cl);\n    ngx_stat_quic_cps = (ngx_atomic_t *) (shared + (n + 3) * cl);\n    ngx_stat_quic_conns_refused = (ngx_atomic_t *) (shared + (n + 4) * cl);\n    ngx_stat_quic_queries = (ngx_atomic_t *) (shared + (n + 5) * cl);\n    ngx_stat_quic_qps_nexttime = (ngx_atomic_t *) (shared + (n + 6) * cl);\n    ngx_stat_quic_qps = (ngx_atomic_t *) (shared + (n + 7) * cl);\n    ngx_stat_quic_queries_refused = (ngx_atomic_t *) (shared + (n + 8) * cl);\n    ngx_stat_quic_concurrent_conns = (ngx_atomic_t * ) (shared + (n + 9) * cl);\n\n    n += 9;\n#endif\n\n    return NGX_OK;\n}\n\n\n#if !(NGX_WIN32)\n\nstatic void\nngx_timer_signal_handler(int signo)\n{\n    ngx_event_timer_alarm = 1;\n\n#if 1\n    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ngx_cycle->log, 0, \"timer signal\");\n#endif\n}\n\n#endif\n\n\nstatic ngx_int_t\nngx_event_process_init(ngx_cycle_t *cycle)\n{\n    ngx_uint_t           m, i;\n    ngx_event_t         *rev, *wev;\n#if (NGX_SSL && NGX_SSL_ASYNC)\n    ngx_event_t         *aev;\n#endif\n    ngx_listening_t     *ls;\n    ngx_connection_t    *c, *next, *old;\n    ngx_core_conf_t     *ccf;\n    ngx_event_conf_t    *ecf;\n    ngx_event_module_t  *module;\n\n    ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);\n    ecf = ngx_event_get_conf(cycle->conf_ctx, ngx_event_core_module);\n\n    if (ccf->master && ccf->worker_processes > 1 && ecf->accept_mutex) {\n        ngx_use_accept_mutex = 1;\n        ngx_accept_mutex_held = 0;\n        ngx_accept_mutex_delay = ecf->accept_mutex_delay;\n\n    } else {\n        ngx_use_accept_mutex = 0;\n    }\n\n#if (NGX_WIN32)\n\n    /*\n     * disable accept mutex on win32 as it may cause deadlock if\n     * grabbed by a process which can't accept connections\n     */\n\n    ngx_use_accept_mutex = 0;\n\n#endif\n\n    ngx_use_exclusive_accept = 0;\n\n    ngx_queue_init(&ngx_posted_accept_events);\n    ngx_queue_init(&ngx_posted_next_events);\n    ngx_queue_init(&ngx_posted_events);\n#if (T_NGX_HAVE_XUDP)\n    ngx_queue_init(&ngx_posted_commit);\n#endif\n#if (T_NGX_UDPV2)\n    ngx_queue_init(&ngx_udpv2_posted_event);\n#endif\n\n    if (ngx_event_timer_init(cycle->log) == NGX_ERROR) {\n        return NGX_ERROR;\n    }\n\n    for (m = 0; cycle->modules[m]; m++) {\n        if (cycle->modules[m]->type != NGX_EVENT_MODULE) {\n            continue;\n        }\n\n        if (cycle->modules[m]->ctx_index != ecf->use) {\n            continue;\n        }\n\n        module = cycle->modules[m]->ctx;\n\n        if (module->actions.init(cycle, ngx_timer_resolution) != NGX_OK) {\n            /* fatal */\n            exit(2);\n        }\n\n        break;\n    }\n\n#if !(NGX_WIN32)\n\n    if (ngx_timer_resolution && !(ngx_event_flags & NGX_USE_TIMER_EVENT)) {\n        struct sigaction  sa;\n        struct itimerval  itv;\n\n        ngx_memzero(&sa, sizeof(struct sigaction));\n        sa.sa_handler = ngx_timer_signal_handler;\n        sigemptyset(&sa.sa_mask);\n\n        if (sigaction(SIGALRM, &sa, NULL) == -1) {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                          \"sigaction(SIGALRM) failed\");\n            return NGX_ERROR;\n        }\n\n        itv.it_interval.tv_sec = ngx_timer_resolution / 1000;\n        itv.it_interval.tv_usec = (ngx_timer_resolution % 1000) * 1000;\n        itv.it_value.tv_sec = ngx_timer_resolution / 1000;\n        itv.it_value.tv_usec = (ngx_timer_resolution % 1000 ) * 1000;\n\n        if (setitimer(ITIMER_REAL, &itv, NULL) == -1) {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                          \"setitimer() failed\");\n        }\n    }\n\n    if (ngx_event_flags & NGX_USE_FD_EVENT) {\n        struct rlimit  rlmt;\n\n        if (getrlimit(RLIMIT_NOFILE, &rlmt) == -1) {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                          \"getrlimit(RLIMIT_NOFILE) failed\");\n            return NGX_ERROR;\n        }\n\n        cycle->files_n = (ngx_uint_t) rlmt.rlim_cur;\n\n        cycle->files = ngx_calloc(sizeof(ngx_connection_t *) * cycle->files_n,\n                                  cycle->log);\n        if (cycle->files == NULL) {\n            return NGX_ERROR;\n        }\n    }\n\n#else\n\n    if (ngx_timer_resolution && !(ngx_event_flags & NGX_USE_TIMER_EVENT)) {\n        ngx_log_error(NGX_LOG_WARN, cycle->log, 0,\n                      \"the \\\"timer_resolution\\\" directive is not supported \"\n                      \"with the configured event method, ignored\");\n        ngx_timer_resolution = 0;\n    }\n\n#endif\n\n    cycle->connections =\n        ngx_alloc(sizeof(ngx_connection_t) * cycle->connection_n, cycle->log);\n    if (cycle->connections == NULL) {\n        return NGX_ERROR;\n    }\n\n    c = cycle->connections;\n\n    cycle->read_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n,\n                                   cycle->log);\n    if (cycle->read_events == NULL) {\n        return NGX_ERROR;\n    }\n\n    rev = cycle->read_events;\n    for (i = 0; i < cycle->connection_n; i++) {\n        rev[i].closed = 1;\n        rev[i].instance = 1;\n    }\n\n    cycle->write_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n,\n                                    cycle->log);\n    if (cycle->write_events == NULL) {\n        return NGX_ERROR;\n    }\n\n    wev = cycle->write_events;\n    for (i = 0; i < cycle->connection_n; i++) {\n        wev[i].closed = 1;\n    }\n\n#if (NGX_SSL && NGX_SSL_ASYNC)\n    cycle->async_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n,\n                                    cycle->log);\n    if (cycle->async_events == NULL) {\n        return NGX_ERROR;\n    }\n\n    aev = cycle->async_events;\n    for (i = 0; i < cycle->connection_n; i++) {\n        aev[i].closed = 1;\n        aev[i].instance = 1;\n    }\n#endif\n\n    i = cycle->connection_n;\n    next = NULL;\n\n    do {\n        i--;\n\n        c[i].data = next;\n        c[i].read = &cycle->read_events[i];\n        c[i].write = &cycle->write_events[i];\n        c[i].fd = (ngx_socket_t) -1;\n#if (NGX_SSL && NGX_SSL_ASYNC)\n        c[i].async = &cycle->async_events[i];\n        c[i].async_fd = (ngx_socket_t) -1;\n#endif\n\n        next = &c[i];\n    } while (i);\n\n    cycle->free_connections = next;\n    cycle->free_connection_n = cycle->connection_n;\n\n    /* for each listening socket */\n\n    ls = cycle->listening.elts;\n    for (i = 0; i < cycle->listening.nelts; i++) {\n\n#if (T_NGX_HAVE_XUDP)\n        if (ls[i].for_xudp && ls[i].worker != ngx_worker) {\n            continue;\n        }\n#endif\n\n#if (NGX_HAVE_REUSEPORT)\n        if (ls[i].reuseport && ls[i].worker != ngx_worker) {\n            continue;\n        }\n#endif\n\n        c = ngx_get_connection(ls[i].fd, cycle->log);\n\n        if (c == NULL) {\n            return NGX_ERROR;\n        }\n\n#if (T_NGX_UDPV2)\n        if (ls[i].type == SOCK_DGRAM) {\n            ngx_queue_init(&ls[i].writable_queue);\n            ngx_event_udpv2_init_listening(&ls[i]);\n        }\n#endif\n\n        c->type = ls[i].type;\n        c->log = &ls[i].log;\n\n        c->listening = &ls[i];\n        ls[i].connection = c;\n\n        rev = c->read;\n\n        rev->log = c->log;\n        rev->accept = 1;\n\n#if (NGX_HAVE_DEFERRED_ACCEPT)\n        rev->deferred_accept = ls[i].deferred_accept;\n#endif\n\n        if (!(ngx_event_flags & NGX_USE_IOCP_EVENT)\n            && cycle->old_cycle)\n        {\n            if (ls[i].previous) {\n\n                /*\n                 * delete the old accept events that were bound to\n                 * the old cycle read events array\n                 */\n\n                old = ls[i].previous->connection;\n\n                if (ngx_del_event(old->read, NGX_READ_EVENT, NGX_CLOSE_EVENT)\n                    == NGX_ERROR)\n                {\n                    return NGX_ERROR;\n                }\n\n                old->fd = (ngx_socket_t) -1;\n            }\n        }\n\n#if (NGX_WIN32)\n\n        if (ngx_event_flags & NGX_USE_IOCP_EVENT) {\n            ngx_iocp_conf_t  *iocpcf;\n\n            rev->handler = ngx_event_acceptex;\n\n            if (ngx_use_accept_mutex) {\n                continue;\n            }\n\n            if (ngx_add_event(rev, 0, NGX_IOCP_ACCEPT) == NGX_ERROR) {\n                return NGX_ERROR;\n            }\n\n            ls[i].log.handler = ngx_acceptex_log_error;\n\n            iocpcf = ngx_event_get_conf(cycle->conf_ctx, ngx_iocp_module);\n            if (ngx_event_post_acceptex(&ls[i], iocpcf->post_acceptex)\n                == NGX_ERROR)\n            {\n                return NGX_ERROR;\n            }\n\n        } else {\n            rev->handler = ngx_event_accept;\n\n            if (ngx_use_accept_mutex) {\n                continue;\n            }\n\n            if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {\n                return NGX_ERROR;\n            }\n        }\n\n#else\n\n        rev->handler = (c->type == SOCK_STREAM) ? ngx_event_accept\n                                                : ngx_event_recvmsg;\n\n#if (NGX_HAVE_REUSEPORT)\n\n        if (ls[i].reuseport) {\n            if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {\n                return NGX_ERROR;\n            }\n\n            continue;\n        }\n\n#endif\n\n        if (ngx_use_accept_mutex) {\n            continue;\n        }\n\n#if (NGX_HAVE_EPOLLEXCLUSIVE)\n\n        if ((ngx_event_flags & NGX_USE_EPOLL_EVENT)\n            && ccf->worker_processes > 1)\n        {\n            ngx_use_exclusive_accept = 1;\n\n            if (ngx_add_event(rev, NGX_READ_EVENT, NGX_EXCLUSIVE_EVENT)\n                == NGX_ERROR)\n            {\n                return NGX_ERROR;\n            }\n\n            continue;\n        }\n\n#endif\n\n        if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {\n            return NGX_ERROR;\n        }\n\n#endif\n\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_send_lowat(ngx_connection_t *c, size_t lowat)\n{\n    int  sndlowat;\n\n#if (NGX_HAVE_LOWAT_EVENT)\n\n    if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {\n        c->write->available = lowat;\n        return NGX_OK;\n    }\n\n#endif\n\n    if (lowat == 0 || c->sndlowat) {\n        return NGX_OK;\n    }\n\n    sndlowat = (int) lowat;\n\n    if (setsockopt(c->fd, SOL_SOCKET, SO_SNDLOWAT,\n                   (const void *) &sndlowat, sizeof(int))\n        == -1)\n    {\n        ngx_connection_error(c, ngx_socket_errno,\n                             \"setsockopt(SO_SNDLOWAT) failed\");\n        return NGX_ERROR;\n    }\n\n    c->sndlowat = 1;\n\n    return NGX_OK;\n}\n\n\nstatic char *\nngx_events_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    char                 *rv;\n    void               ***ctx;\n    ngx_uint_t            i;\n    ngx_conf_t            pcf;\n    ngx_event_module_t   *m;\n\n    if (*(void **) conf) {\n        return \"is duplicate\";\n    }\n\n    /* count the number of the event modules and set up their indices */\n\n    ngx_event_max_module = ngx_count_modules(cf->cycle, NGX_EVENT_MODULE);\n\n    ctx = ngx_pcalloc(cf->pool, sizeof(void *));\n    if (ctx == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    *ctx = ngx_pcalloc(cf->pool, ngx_event_max_module * sizeof(void *));\n    if (*ctx == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    *(void **) conf = ctx;\n\n    for (i = 0; cf->cycle->modules[i]; i++) {\n        if (cf->cycle->modules[i]->type != NGX_EVENT_MODULE) {\n            continue;\n        }\n\n        m = cf->cycle->modules[i]->ctx;\n\n        if (m->create_conf) {\n            (*ctx)[cf->cycle->modules[i]->ctx_index] =\n                                                     m->create_conf(cf->cycle);\n            if ((*ctx)[cf->cycle->modules[i]->ctx_index] == NULL) {\n                return NGX_CONF_ERROR;\n            }\n        }\n    }\n\n#if (T_NGX_ACCEPT_FILTER)\n    ngx_event_top_accept_filter = ngx_event_dummy_accept_filter;\n#endif\n\n    pcf = *cf;\n    cf->ctx = ctx;\n    cf->module_type = NGX_EVENT_MODULE;\n    cf->cmd_type = NGX_EVENT_CONF;\n\n    rv = ngx_conf_parse(cf, NULL);\n\n    *cf = pcf;\n\n    if (rv != NGX_CONF_OK) {\n        return rv;\n    }\n\n    for (i = 0; cf->cycle->modules[i]; i++) {\n        if (cf->cycle->modules[i]->type != NGX_EVENT_MODULE) {\n            continue;\n        }\n\n        m = cf->cycle->modules[i]->ctx;\n\n        if (m->init_conf) {\n            rv = m->init_conf(cf->cycle,\n                              (*ctx)[cf->cycle->modules[i]->ctx_index]);\n            if (rv != NGX_CONF_OK) {\n                return rv;\n            }\n        }\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_event_connections(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_event_conf_t  *ecf = conf;\n\n    ngx_str_t  *value;\n\n    if (ecf->connections != NGX_CONF_UNSET_UINT) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n    ecf->connections = ngx_atoi(value[1].data, value[1].len);\n    if (ecf->connections == (ngx_uint_t) NGX_ERROR) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid number \\\"%V\\\"\", &value[1]);\n\n        return NGX_CONF_ERROR;\n    }\n\n    cf->cycle->connection_n = ecf->connections;\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_event_use(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_event_conf_t  *ecf = conf;\n\n    ngx_int_t             m;\n    ngx_str_t            *value;\n    ngx_event_conf_t     *old_ecf;\n    ngx_event_module_t   *module;\n\n    if (ecf->use != NGX_CONF_UNSET_UINT) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    if (cf->cycle->old_cycle->conf_ctx) {\n        old_ecf = ngx_event_get_conf(cf->cycle->old_cycle->conf_ctx,\n                                     ngx_event_core_module);\n    } else {\n        old_ecf = NULL;\n    }\n\n\n    for (m = 0; cf->cycle->modules[m]; m++) {\n        if (cf->cycle->modules[m]->type != NGX_EVENT_MODULE) {\n            continue;\n        }\n\n        module = cf->cycle->modules[m]->ctx;\n        if (module->name->len == value[1].len) {\n            if (ngx_strcmp(module->name->data, value[1].data) == 0) {\n                ecf->use = cf->cycle->modules[m]->ctx_index;\n                ecf->name = module->name->data;\n\n                if (ngx_process == NGX_PROCESS_SINGLE\n                    && old_ecf\n                    && old_ecf->use != ecf->use)\n                {\n                    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"when the server runs without a master process \"\n                               \"the \\\"%V\\\" event type must be the same as \"\n                               \"in previous configuration - \\\"%s\\\" \"\n                               \"and it cannot be changed on the fly, \"\n                               \"to change it you need to stop server \"\n                               \"and start it again\",\n                               &value[1], old_ecf->name);\n\n                    return NGX_CONF_ERROR;\n                }\n\n                return NGX_CONF_OK;\n            }\n        }\n    }\n\n    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                       \"invalid event type \\\"%V\\\"\", &value[1]);\n\n    return NGX_CONF_ERROR;\n}\n\n\nstatic char *\nngx_event_debug_connection(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n#if (NGX_DEBUG)\n    ngx_event_conf_t  *ecf = conf;\n\n    ngx_int_t             rc;\n    ngx_str_t            *value;\n    ngx_url_t             u;\n    ngx_cidr_t            c, *cidr;\n    ngx_uint_t            i;\n    struct sockaddr_in   *sin;\n#if (NGX_HAVE_INET6)\n    struct sockaddr_in6  *sin6;\n#endif\n\n    value = cf->args->elts;\n\n#if (NGX_HAVE_UNIX_DOMAIN)\n\n    if (ngx_strcmp(value[1].data, \"unix:\") == 0) {\n        cidr = ngx_array_push(&ecf->debug_connection);\n        if (cidr == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        cidr->family = AF_UNIX;\n        return NGX_CONF_OK;\n    }\n\n#endif\n\n    rc = ngx_ptocidr(&value[1], &c);\n\n    if (rc != NGX_ERROR) {\n        if (rc == NGX_DONE) {\n            ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                               \"low address bits of %V are meaningless\",\n                               &value[1]);\n        }\n\n        cidr = ngx_array_push(&ecf->debug_connection);\n        if (cidr == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        *cidr = c;\n\n        return NGX_CONF_OK;\n    }\n\n    ngx_memzero(&u, sizeof(ngx_url_t));\n    u.host = value[1];\n\n    if (ngx_inet_resolve_host(cf->pool, &u) != NGX_OK) {\n        if (u.err) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"%s in debug_connection \\\"%V\\\"\",\n                               u.err, &u.host);\n        }\n\n        return NGX_CONF_ERROR;\n    }\n\n    cidr = ngx_array_push_n(&ecf->debug_connection, u.naddrs);\n    if (cidr == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_memzero(cidr, u.naddrs * sizeof(ngx_cidr_t));\n\n    for (i = 0; i < u.naddrs; i++) {\n        cidr[i].family = u.addrs[i].sockaddr->sa_family;\n\n        switch (cidr[i].family) {\n\n#if (NGX_HAVE_INET6)\n        case AF_INET6:\n            sin6 = (struct sockaddr_in6 *) u.addrs[i].sockaddr;\n            cidr[i].u.in6.addr = sin6->sin6_addr;\n            ngx_memset(cidr[i].u.in6.mask.s6_addr, 0xff, 16);\n            break;\n#endif\n\n        default: /* AF_INET */\n            sin = (struct sockaddr_in *) u.addrs[i].sockaddr;\n            cidr[i].u.in.addr = sin->sin_addr.s_addr;\n            cidr[i].u.in.mask = 0xffffffff;\n            break;\n        }\n    }\n\n#else\n\n    ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                       \"\\\"debug_connection\\\" is ignored, you need to rebuild \"\n                       \"nginx using --with-debug option to enable it\");\n\n#endif\n\n    return NGX_CONF_OK;\n}\n\n\nstatic void *\nngx_event_core_create_conf(ngx_cycle_t *cycle)\n{\n    ngx_event_conf_t  *ecf;\n\n    ecf = ngx_palloc(cycle->pool, sizeof(ngx_event_conf_t));\n    if (ecf == NULL) {\n        return NULL;\n    }\n\n    ecf->connections = NGX_CONF_UNSET_UINT;\n    ecf->use = NGX_CONF_UNSET_UINT;\n    ecf->multi_accept = NGX_CONF_UNSET;\n    ecf->accept_mutex = NGX_CONF_UNSET;\n    ecf->accept_mutex_delay = NGX_CONF_UNSET_MSEC;\n    ecf->name = (void *) NGX_CONF_UNSET;\n\n#if (NGX_DEBUG)\n\n    if (ngx_array_init(&ecf->debug_connection, cycle->pool, 4,\n                       sizeof(ngx_cidr_t)) == NGX_ERROR)\n    {\n        return NULL;\n    }\n\n#endif\n\n    return ecf;\n}\n\n\nstatic char *\nngx_event_core_init_conf(ngx_cycle_t *cycle, void *conf)\n{\n    ngx_event_conf_t  *ecf = conf;\n\n#if (NGX_HAVE_EPOLL) && !(NGX_TEST_BUILD_EPOLL)\n    int                  fd;\n#endif\n    ngx_int_t            i;\n    ngx_module_t        *module;\n    ngx_event_module_t  *event_module;\n\n    module = NULL;\n\n#if (NGX_HAVE_EPOLL) && !(NGX_TEST_BUILD_EPOLL)\n\n    fd = epoll_create(100);\n\n    if (fd != -1) {\n        (void) close(fd);\n        module = &ngx_epoll_module;\n\n    } else if (ngx_errno != NGX_ENOSYS) {\n        module = &ngx_epoll_module;\n    }\n\n#endif\n\n#if (NGX_HAVE_DEVPOLL) && !(NGX_TEST_BUILD_DEVPOLL)\n\n    module = &ngx_devpoll_module;\n\n#endif\n\n#if (NGX_HAVE_KQUEUE)\n\n    module = &ngx_kqueue_module;\n\n#endif\n\n#if (NGX_HAVE_SELECT)\n\n    if (module == NULL) {\n        module = &ngx_select_module;\n    }\n\n#endif\n\n    if (module == NULL) {\n        for (i = 0; cycle->modules[i]; i++) {\n\n            if (cycle->modules[i]->type != NGX_EVENT_MODULE) {\n                continue;\n            }\n\n            event_module = cycle->modules[i]->ctx;\n\n            if (ngx_strcmp(event_module->name->data, event_core_name.data) == 0)\n            {\n                continue;\n            }\n\n            module = cycle->modules[i];\n            break;\n        }\n    }\n\n    if (module == NULL) {\n        ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, \"no events module found\");\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_conf_init_uint_value(ecf->connections, DEFAULT_CONNECTIONS);\n    cycle->connection_n = ecf->connections;\n\n    ngx_conf_init_uint_value(ecf->use, module->ctx_index);\n\n    event_module = module->ctx;\n    ngx_conf_init_ptr_value(ecf->name, event_module->name->data);\n\n    ngx_conf_init_value(ecf->multi_accept, 0);\n    ngx_conf_init_value(ecf->accept_mutex, 0);\n    ngx_conf_init_msec_value(ecf->accept_mutex_delay,\n#if (T_NGX_MODIFY_DEFAULT_VALUE)\n                            100);\n#else\n                            500);\n#endif\n\n    return NGX_CONF_OK;\n}\n\n\n#if (T_NGX_ACCEPT_FILTER)\nstatic ngx_int_t\nngx_event_dummy_accept_filter(ngx_connection_t *c)\n{\n    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, \"event dummy accept filter\");\n    return NGX_OK;\n}\n#endif\n"
  },
  {
    "path": "src/event/ngx_event.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_EVENT_H_INCLUDED_\n#define _NGX_EVENT_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\n#define NGX_INVALID_INDEX  0xd0d0d0d0\n\n\n#if (NGX_HAVE_IOCP)\n\ntypedef struct {\n    WSAOVERLAPPED    ovlp;\n    ngx_event_t     *event;\n    int              error;\n} ngx_event_ovlp_t;\n\n#endif\n\n#if (T_NGX_XQUIC)\n\nextern ngx_atomic_t  *ngx_stat_quic_conns;\nextern ngx_atomic_t  *ngx_stat_quic_cps_nexttime;\nextern ngx_atomic_t  *ngx_stat_quic_cps;\nextern ngx_atomic_t  *ngx_stat_quic_conns_refused;\n\nextern ngx_atomic_t  *ngx_stat_quic_queries;\nextern ngx_atomic_t  *ngx_stat_quic_qps_nexttime;\nextern ngx_atomic_t  *ngx_stat_quic_qps;\nextern ngx_atomic_t  *ngx_stat_quic_queries_refused;\n\nextern ngx_atomic_t  *ngx_stat_quic_concurrent_conns;\n\n#endif\n\nstruct ngx_event_s {\n    void            *data;\n\n    unsigned         write:1;\n\n#if (NGX_SSL && NGX_SSL_ASYNC)\n    unsigned         async:1;\n#endif\n\n    unsigned         accept:1;\n\n    /* used to detect the stale events in kqueue and epoll */\n    unsigned         instance:1;\n\n    /*\n     * the event was passed or would be passed to a kernel;\n     * in aio mode - operation was posted.\n     */\n    unsigned         active:1;\n\n    unsigned         disabled:1;\n\n    /* the ready event; in aio mode 0 means that no operation can be posted */\n    unsigned         ready:1;\n\n    unsigned         oneshot:1;\n\n    /* aio operation is complete */\n    unsigned         complete:1;\n\n    unsigned         eof:1;\n    unsigned         error:1;\n\n    unsigned         timedout:1;\n    unsigned         timer_set:1;\n\n    unsigned         delayed:1;\n\n    unsigned         deferred_accept:1;\n\n    /* the pending eof reported by kqueue, epoll or in aio chain operation */\n    unsigned         pending_eof:1;\n\n    unsigned         posted:1;\n\n    unsigned         closed:1;\n\n    /* to test on worker exit */\n    unsigned         channel:1;\n    unsigned         resolver:1;\n\n    unsigned         cancelable:1;\n\n#if (NGX_HAVE_KQUEUE)\n    unsigned         kq_vnode:1;\n\n    /* the pending errno reported by kqueue */\n    int              kq_errno;\n#endif\n\n    /*\n     * kqueue only:\n     *   accept:     number of sockets that wait to be accepted\n     *   read:       bytes to read when event is ready\n     *               or lowat when event is set with NGX_LOWAT_EVENT flag\n     *   write:      available space in buffer when event is ready\n     *               or lowat when event is set with NGX_LOWAT_EVENT flag\n     *\n     * iocp: TODO\n     *\n     * otherwise:\n     *   accept:     1 if accept many, 0 otherwise\n     *   read:       bytes to read when event is ready, -1 if not known\n     */\n\n    int              available;\n\n    ngx_event_handler_pt  handler;\n#if (NGX_SSL && NGX_SSL_ASYNC)\n    ngx_event_handler_pt  saved_handler;\n#endif\n\n\n\n#if (NGX_HAVE_IOCP)\n    ngx_event_ovlp_t ovlp;\n#endif\n\n    ngx_uint_t       index;\n\n    ngx_log_t       *log;\n\n    ngx_rbtree_node_t   timer;\n\n    /* the posted queue */\n    ngx_queue_t      queue;\n\n#if 0\n\n    /* the threads support */\n\n    /*\n     * the event thread context, we store it here\n     * if $(CC) does not understand __thread declaration\n     * and pthread_getspecific() is too costly\n     */\n\n    void            *thr_ctx;\n\n#if (NGX_EVENT_T_PADDING)\n\n    /* event should not cross cache line in SMP */\n\n    uint32_t         padding[NGX_EVENT_T_PADDING];\n#endif\n#endif\n};\n\n\n#if (NGX_HAVE_FILE_AIO)\n\nstruct ngx_event_aio_s {\n    void                      *data;\n    ngx_event_handler_pt       handler;\n    ngx_file_t                *file;\n\n    ngx_fd_t                   fd;\n\n#if (NGX_HAVE_EVENTFD)\n    int64_t                    res;\n#endif\n\n#if !(NGX_HAVE_EVENTFD) || (NGX_TEST_BUILD_EPOLL)\n    ngx_err_t                  err;\n    size_t                     nbytes;\n#endif\n\n    ngx_aiocb_t                aiocb;\n    ngx_event_t                event;\n};\n\n#endif\n\n\ntypedef struct {\n    ngx_int_t  (*add)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);\n    ngx_int_t  (*del)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);\n\n    ngx_int_t  (*enable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);\n    ngx_int_t  (*disable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);\n\n    ngx_int_t  (*add_conn)(ngx_connection_t *c);\n    ngx_int_t  (*del_conn)(ngx_connection_t *c, ngx_uint_t flags);\n\n    ngx_int_t  (*notify)(ngx_event_handler_pt handler);\n\n    ngx_int_t  (*process_events)(ngx_cycle_t *cycle, ngx_msec_t timer,\n                                 ngx_uint_t flags);\n\n    ngx_int_t  (*init)(ngx_cycle_t *cycle, ngx_msec_t timer);\n    void       (*done)(ngx_cycle_t *cycle);\n\n#if (NGX_SSL && NGX_SSL_ASYNC)\n    ngx_int_t  (*add_async_conn)(ngx_connection_t *c);\n    ngx_int_t  (*del_async_conn)(ngx_connection_t *c, ngx_uint_t flags);\n#endif\n} ngx_event_actions_t;\n\n\nextern ngx_event_actions_t   ngx_event_actions;\n#if (NGX_HAVE_EPOLLRDHUP)\nextern ngx_uint_t            ngx_use_epoll_rdhup;\n#endif\n#if (T_NGX_ACCEPT_FILTER)\ntypedef ngx_int_t (*ngx_event_accept_filter_pt) (ngx_connection_t *c);\nextern ngx_event_accept_filter_pt ngx_event_top_accept_filter;\n#endif\n\n\n/*\n * The event filter requires to read/write the whole data:\n * select, poll, /dev/poll, kqueue, epoll.\n */\n#define NGX_USE_LEVEL_EVENT      0x00000001\n\n/*\n * The event filter is deleted after a notification without an additional\n * syscall: kqueue, epoll.\n */\n#define NGX_USE_ONESHOT_EVENT    0x00000002\n\n/*\n * The event filter notifies only the changes and an initial level:\n * kqueue, epoll.\n */\n#define NGX_USE_CLEAR_EVENT      0x00000004\n\n/*\n * The event filter has kqueue features: the eof flag, errno,\n * available data, etc.\n */\n#define NGX_USE_KQUEUE_EVENT     0x00000008\n\n/*\n * The event filter supports low water mark: kqueue's NOTE_LOWAT.\n * kqueue in FreeBSD 4.1-4.2 has no NOTE_LOWAT so we need a separate flag.\n */\n#define NGX_USE_LOWAT_EVENT      0x00000010\n\n/*\n * The event filter requires to do i/o operation until EAGAIN: epoll.\n */\n#define NGX_USE_GREEDY_EVENT     0x00000020\n\n/*\n * The event filter is epoll.\n */\n#define NGX_USE_EPOLL_EVENT      0x00000040\n\n/*\n * Obsolete.\n */\n#define NGX_USE_RTSIG_EVENT      0x00000080\n\n/*\n * Obsolete.\n */\n#define NGX_USE_AIO_EVENT        0x00000100\n\n/*\n * Need to add socket or handle only once: i/o completion port.\n */\n#define NGX_USE_IOCP_EVENT       0x00000200\n\n/*\n * The event filter has no opaque data and requires file descriptors table:\n * poll, /dev/poll.\n */\n#define NGX_USE_FD_EVENT         0x00000400\n\n/*\n * The event module handles periodic or absolute timer event by itself:\n * kqueue in FreeBSD 4.4, NetBSD 2.0, and MacOSX 10.4, Solaris 10's event ports.\n */\n#define NGX_USE_TIMER_EVENT      0x00000800\n\n/*\n * All event filters on file descriptor are deleted after a notification:\n * Solaris 10's event ports.\n */\n#define NGX_USE_EVENTPORT_EVENT  0x00001000\n\n/*\n * The event filter support vnode notifications: kqueue.\n */\n#define NGX_USE_VNODE_EVENT      0x00002000\n\n\n/*\n * The event filter is deleted just before the closing file.\n * Has no meaning for select and poll.\n * kqueue, epoll, eventport:         allows to avoid explicit delete,\n *                                   because filter automatically is deleted\n *                                   on file close,\n *\n * /dev/poll:                        we need to flush POLLREMOVE event\n *                                   before closing file.\n */\n#define NGX_CLOSE_EVENT    1\n\n/*\n * disable temporarily event filter, this may avoid locks\n * in kernel malloc()/free(): kqueue.\n */\n#define NGX_DISABLE_EVENT  2\n\n/*\n * event must be passed to kernel right now, do not wait until batch processing.\n */\n#define NGX_FLUSH_EVENT    4\n\n\n/* these flags have a meaning only for kqueue */\n#define NGX_LOWAT_EVENT    0\n#define NGX_VNODE_EVENT    0\n\n\n#if (NGX_HAVE_EPOLL) && !(NGX_HAVE_EPOLLRDHUP)\n#define EPOLLRDHUP         0\n#endif\n\n\n#if (NGX_HAVE_KQUEUE)\n\n#define NGX_READ_EVENT     EVFILT_READ\n#define NGX_WRITE_EVENT    EVFILT_WRITE\n\n#undef  NGX_VNODE_EVENT\n#define NGX_VNODE_EVENT    EVFILT_VNODE\n\n/*\n * NGX_CLOSE_EVENT, NGX_LOWAT_EVENT, and NGX_FLUSH_EVENT are the module flags\n * and they must not go into a kernel so we need to choose the value\n * that must not interfere with any existent and future kqueue flags.\n * kqueue has such values - EV_FLAG1, EV_EOF, and EV_ERROR:\n * they are reserved and cleared on a kernel entrance.\n */\n#undef  NGX_CLOSE_EVENT\n#define NGX_CLOSE_EVENT    EV_EOF\n\n#undef  NGX_LOWAT_EVENT\n#define NGX_LOWAT_EVENT    EV_FLAG1\n\n#undef  NGX_FLUSH_EVENT\n#define NGX_FLUSH_EVENT    EV_ERROR\n\n#define NGX_LEVEL_EVENT    0\n#define NGX_ONESHOT_EVENT  EV_ONESHOT\n#define NGX_CLEAR_EVENT    EV_CLEAR\n\n#undef  NGX_DISABLE_EVENT\n#define NGX_DISABLE_EVENT  EV_DISABLE\n\n\n#elif (NGX_HAVE_DEVPOLL && !(NGX_TEST_BUILD_DEVPOLL)) \\\n      || (NGX_HAVE_EVENTPORT && !(NGX_TEST_BUILD_EVENTPORT))\n\n#define NGX_READ_EVENT     POLLIN\n#define NGX_WRITE_EVENT    POLLOUT\n\n#define NGX_LEVEL_EVENT    0\n#define NGX_ONESHOT_EVENT  1\n\n\n#elif (NGX_HAVE_EPOLL) && !(NGX_TEST_BUILD_EPOLL)\n\n#define NGX_READ_EVENT     (EPOLLIN|EPOLLRDHUP)\n#define NGX_WRITE_EVENT    EPOLLOUT\n\n#define NGX_LEVEL_EVENT    0\n#define NGX_CLEAR_EVENT    EPOLLET\n#define NGX_ONESHOT_EVENT  0x70000000\n#if 0\n#define NGX_ONESHOT_EVENT  EPOLLONESHOT\n#endif\n\n#if (NGX_HAVE_EPOLLEXCLUSIVE)\n#define NGX_EXCLUSIVE_EVENT  EPOLLEXCLUSIVE\n#endif\n\n#elif (NGX_HAVE_POLL)\n\n#define NGX_READ_EVENT     POLLIN\n#define NGX_WRITE_EVENT    POLLOUT\n\n#define NGX_LEVEL_EVENT    0\n#define NGX_ONESHOT_EVENT  1\n\n\n#else /* select */\n\n#define NGX_READ_EVENT     0\n#define NGX_WRITE_EVENT    1\n\n#define NGX_LEVEL_EVENT    0\n#define NGX_ONESHOT_EVENT  1\n\n#endif /* NGX_HAVE_KQUEUE */\n\n\n#if (NGX_HAVE_IOCP)\n#define NGX_IOCP_ACCEPT      0\n#define NGX_IOCP_IO          1\n#define NGX_IOCP_CONNECT     2\n#endif\n\n\n#if (NGX_TEST_BUILD_EPOLL)\n#define NGX_EXCLUSIVE_EVENT  0\n#endif\n\n\n#ifndef NGX_CLEAR_EVENT\n#define NGX_CLEAR_EVENT    0    /* dummy declaration */\n#endif\n\n\n#define ngx_process_events   ngx_event_actions.process_events\n#define ngx_done_events      ngx_event_actions.done\n\n#define ngx_add_event        ngx_event_actions.add\n#define ngx_del_event        ngx_event_actions.del\n#define ngx_add_conn         ngx_event_actions.add_conn\n#define ngx_del_conn         ngx_event_actions.del_conn\n\n#if (NGX_SSL && NGX_SSL_ASYNC)\n#define ngx_add_async_conn   ngx_event_actions.add_async_conn\n#define ngx_del_async_conn   ngx_event_actions.del_async_conn\n#endif\n\n#define ngx_notify           ngx_event_actions.notify\n\n#define ngx_add_timer        ngx_event_add_timer\n#define ngx_del_timer        ngx_event_del_timer\n\n\nextern ngx_os_io_t  ngx_io;\n\n#define ngx_recv             ngx_io.recv\n#define ngx_recv_chain       ngx_io.recv_chain\n#define ngx_udp_recv         ngx_io.udp_recv\n#define ngx_send             ngx_io.send\n#define ngx_send_chain       ngx_io.send_chain\n#define ngx_udp_send         ngx_io.udp_send\n#define ngx_udp_send_chain   ngx_io.udp_send_chain\n\n\n#define NGX_EVENT_MODULE      0x544E5645  /* \"EVNT\" */\n#define NGX_EVENT_CONF        0x02000000\n\n\ntypedef struct {\n    ngx_uint_t    connections;\n    ngx_uint_t    use;\n\n    ngx_flag_t    multi_accept;\n    ngx_flag_t    accept_mutex;\n\n    ngx_msec_t    accept_mutex_delay;\n\n    u_char       *name;\n\n#if (NGX_DEBUG)\n    ngx_array_t   debug_connection;\n#endif\n} ngx_event_conf_t;\n\n\ntypedef struct {\n    ngx_str_t              *name;\n\n    void                 *(*create_conf)(ngx_cycle_t *cycle);\n    char                 *(*init_conf)(ngx_cycle_t *cycle, void *conf);\n\n    ngx_event_actions_t     actions;\n} ngx_event_module_t;\n\n\nextern ngx_atomic_t          *ngx_connection_counter;\n\nextern ngx_atomic_t          *ngx_accept_mutex_ptr;\nextern ngx_shmtx_t            ngx_accept_mutex;\nextern ngx_uint_t             ngx_use_accept_mutex;\nextern ngx_uint_t             ngx_accept_events;\nextern ngx_uint_t             ngx_accept_mutex_held;\nextern ngx_msec_t             ngx_accept_mutex_delay;\nextern ngx_int_t              ngx_accept_disabled;\nextern ngx_uint_t             ngx_use_exclusive_accept;\n\n\n#if (NGX_STAT_STUB)\n\nextern ngx_atomic_t  *ngx_stat_accepted;\nextern ngx_atomic_t  *ngx_stat_handled;\nextern ngx_atomic_t  *ngx_stat_requests;\nextern ngx_atomic_t  *ngx_stat_active;\nextern ngx_atomic_t  *ngx_stat_reading;\nextern ngx_atomic_t  *ngx_stat_writing;\nextern ngx_atomic_t  *ngx_stat_waiting;\n#if (T_NGX_HTTP_STUB_STATUS)\nextern ngx_atomic_t  *ngx_stat_request_time;\n#endif\n#endif\n\n\n#define NGX_UPDATE_TIME         1\n#define NGX_POST_EVENTS         2\n\n\nextern sig_atomic_t           ngx_event_timer_alarm;\nextern ngx_uint_t             ngx_event_flags;\nextern ngx_module_t           ngx_events_module;\nextern ngx_module_t           ngx_event_core_module;\n\n\n#define ngx_event_get_conf(conf_ctx, module)                                  \\\n             (*(ngx_get_conf(conf_ctx, ngx_events_module))) [module.ctx_index]\n\n\n\nvoid ngx_event_accept(ngx_event_t *ev);\nngx_int_t ngx_trylock_accept_mutex(ngx_cycle_t *cycle);\nngx_int_t ngx_enable_accept_events(ngx_cycle_t *cycle);\nu_char *ngx_accept_log_error(ngx_log_t *log, u_char *buf, size_t len);\n#if (NGX_DEBUG)\nvoid ngx_debug_accepted_connection(ngx_event_conf_t *ecf, ngx_connection_t *c);\n#endif\n\n\nvoid ngx_process_events_and_timers(ngx_cycle_t *cycle);\nngx_int_t ngx_handle_read_event(ngx_event_t *rev, ngx_uint_t flags);\nngx_int_t ngx_handle_write_event(ngx_event_t *wev, size_t lowat);\n\n\n#if (NGX_WIN32)\nvoid ngx_event_acceptex(ngx_event_t *ev);\nngx_int_t ngx_event_post_acceptex(ngx_listening_t *ls, ngx_uint_t n);\nu_char *ngx_acceptex_log_error(ngx_log_t *log, u_char *buf, size_t len);\n#endif\n\n\nngx_int_t ngx_send_lowat(ngx_connection_t *c, size_t lowat);\n\n\n/* used in ngx_log_debugX() */\n#define ngx_event_ident(p)  ((ngx_connection_t *) (p))->fd\n\n\n#include <ngx_event_timer.h>\n#include <ngx_event_posted.h>\n#include <ngx_event_udp.h>\n\n#if (NGX_WIN32)\n#include <ngx_iocp_module.h>\n#endif\n\n#if (T_NGX_UDPV2)\n#include <ngx_event_udpv2.h>\n#endif\n\n#endif /* _NGX_EVENT_H_INCLUDED_ */\n"
  },
  {
    "path": "src/event/ngx_event_accept.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n\n\nstatic ngx_int_t ngx_disable_accept_events(ngx_cycle_t *cycle, ngx_uint_t all);\n#if (NGX_HAVE_EPOLLEXCLUSIVE)\nstatic void ngx_reorder_accept_events(ngx_listening_t *ls);\n#endif\nstatic void ngx_close_accepted_connection(ngx_connection_t *c);\n\n\nvoid\nngx_event_accept(ngx_event_t *ev)\n{\n    socklen_t          socklen;\n    ngx_err_t          err;\n    ngx_log_t         *log;\n    ngx_uint_t         level;\n    ngx_socket_t       s;\n    ngx_event_t       *rev, *wev;\n    ngx_sockaddr_t     sa;\n    ngx_listening_t   *ls;\n    ngx_connection_t  *c, *lc;\n    ngx_event_conf_t  *ecf;\n#if (NGX_HAVE_ACCEPT4)\n    static ngx_uint_t  use_accept4 = 1;\n#endif\n\n    if (ev->timedout) {\n        if (ngx_enable_accept_events((ngx_cycle_t *) ngx_cycle) != NGX_OK) {\n            return;\n        }\n\n        ev->timedout = 0;\n    }\n\n    ecf = ngx_event_get_conf(ngx_cycle->conf_ctx, ngx_event_core_module);\n\n    if (!(ngx_event_flags & NGX_USE_KQUEUE_EVENT)) {\n        ev->available = ecf->multi_accept;\n    }\n\n    lc = ev->data;\n    ls = lc->listening;\n    ev->ready = 0;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,\n                   \"accept on %V, ready: %d\", &ls->addr_text, ev->available);\n\n    do {\n        socklen = sizeof(ngx_sockaddr_t);\n\n#if (NGX_HAVE_ACCEPT4)\n        if (use_accept4) {\n            s = accept4(lc->fd, &sa.sockaddr, &socklen, SOCK_NONBLOCK);\n        } else {\n            s = accept(lc->fd, &sa.sockaddr, &socklen);\n        }\n#else\n        s = accept(lc->fd, &sa.sockaddr, &socklen);\n#endif\n\n        if (s == (ngx_socket_t) -1) {\n            err = ngx_socket_errno;\n\n            if (err == NGX_EAGAIN) {\n                ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, err,\n                               \"accept() not ready\");\n                return;\n            }\n\n            level = NGX_LOG_ALERT;\n\n            if (err == NGX_ECONNABORTED) {\n                level = NGX_LOG_ERR;\n\n            } else if (err == NGX_EMFILE || err == NGX_ENFILE) {\n                level = NGX_LOG_CRIT;\n            }\n\n#if (NGX_HAVE_ACCEPT4)\n            ngx_log_error(level, ev->log, err,\n                          use_accept4 ? \"accept4() failed\" : \"accept() failed\");\n\n            if (use_accept4 && err == NGX_ENOSYS) {\n                use_accept4 = 0;\n                ngx_inherited_nonblocking = 0;\n                continue;\n            }\n#else\n            ngx_log_error(level, ev->log, err, \"accept() failed\");\n#endif\n\n            if (err == NGX_ECONNABORTED) {\n                if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {\n                    ev->available--;\n                }\n\n                if (ev->available) {\n                    continue;\n                }\n            }\n\n            if (err == NGX_EMFILE || err == NGX_ENFILE) {\n                if (ngx_disable_accept_events((ngx_cycle_t *) ngx_cycle, 1)\n                    != NGX_OK)\n                {\n                    return;\n                }\n\n                if (ngx_use_accept_mutex) {\n                    if (ngx_accept_mutex_held) {\n                        ngx_shmtx_unlock(&ngx_accept_mutex);\n                        ngx_accept_mutex_held = 0;\n                    }\n\n                    ngx_accept_disabled = 1;\n\n                } else {\n                    ngx_add_timer(ev, ecf->accept_mutex_delay);\n                }\n            }\n\n            return;\n        }\n\n#if (NGX_STAT_STUB)\n        (void) ngx_atomic_fetch_add(ngx_stat_accepted, 1);\n#endif\n\n        ngx_accept_disabled = ngx_cycle->connection_n / 8\n                              - ngx_cycle->free_connection_n;\n\n        c = ngx_get_connection(s, ev->log);\n\n        if (c == NULL) {\n            if (ngx_close_socket(s) == -1) {\n                ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_socket_errno,\n                              ngx_close_socket_n \" failed\");\n            }\n\n            return;\n        }\n\n        c->type = SOCK_STREAM;\n\n#if (NGX_STAT_STUB)\n        (void) ngx_atomic_fetch_add(ngx_stat_active, 1);\n#endif\n\n        c->pool = ngx_create_pool(ls->pool_size, ev->log);\n        if (c->pool == NULL) {\n            ngx_close_accepted_connection(c);\n            return;\n        }\n\n        if (socklen > (socklen_t) sizeof(ngx_sockaddr_t)) {\n            socklen = sizeof(ngx_sockaddr_t);\n        }\n\n        c->sockaddr = ngx_palloc(c->pool, socklen);\n        if (c->sockaddr == NULL) {\n            ngx_close_accepted_connection(c);\n            return;\n        }\n\n        ngx_memcpy(c->sockaddr, &sa, socklen);\n\n        log = ngx_palloc(c->pool, sizeof(ngx_log_t));\n        if (log == NULL) {\n            ngx_close_accepted_connection(c);\n            return;\n        }\n\n        /* set a blocking mode for iocp and non-blocking mode for others */\n\n        if (ngx_inherited_nonblocking) {\n            if (ngx_event_flags & NGX_USE_IOCP_EVENT) {\n                if (ngx_blocking(s) == -1) {\n                    ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_socket_errno,\n                                  ngx_blocking_n \" failed\");\n                    ngx_close_accepted_connection(c);\n                    return;\n                }\n            }\n\n        } else {\n            if (!(ngx_event_flags & NGX_USE_IOCP_EVENT)) {\n                if (ngx_nonblocking(s) == -1) {\n                    ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_socket_errno,\n                                  ngx_nonblocking_n \" failed\");\n                    ngx_close_accepted_connection(c);\n                    return;\n                }\n            }\n        }\n\n        *log = ls->log;\n\n        c->recv = ngx_recv;\n        c->send = ngx_send;\n        c->recv_chain = ngx_recv_chain;\n        c->send_chain = ngx_send_chain;\n\n        c->log = log;\n        c->pool->log = log;\n\n        c->socklen = socklen;\n        c->listening = ls;\n        c->local_sockaddr = ls->sockaddr;\n        c->local_socklen = ls->socklen;\n\n#if (NGX_HAVE_UNIX_DOMAIN)\n        if (c->sockaddr->sa_family == AF_UNIX) {\n            c->tcp_nopush = NGX_TCP_NOPUSH_DISABLED;\n            c->tcp_nodelay = NGX_TCP_NODELAY_DISABLED;\n#if (NGX_SOLARIS)\n            /* Solaris's sendfilev() supports AF_NCA, AF_INET, and AF_INET6 */\n            c->sendfile = 0;\n#endif\n        }\n#endif\n\n        rev = c->read;\n        wev = c->write;\n\n        wev->ready = 1;\n\n        if (ngx_event_flags & NGX_USE_IOCP_EVENT) {\n            rev->ready = 1;\n        }\n\n        if (ev->deferred_accept) {\n            rev->ready = 1;\n#if (NGX_HAVE_KQUEUE || NGX_HAVE_EPOLLRDHUP)\n            rev->available = 1;\n#endif\n        }\n\n        rev->log = log;\n        wev->log = log;\n#if (NGX_SSL && NGX_SSL_ASYNC)\n        c->async->log = log;\n#endif\n\n        /*\n         * TODO: MT: - ngx_atomic_fetch_add()\n         *             or protection by critical section or light mutex\n         *\n         * TODO: MP: - allocated in a shared memory\n         *           - ngx_atomic_fetch_add()\n         *             or protection by critical section or light mutex\n         */\n\n        c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1);\n\n        c->start_time = ngx_current_msec;\n\n#if (NGX_STAT_STUB)\n        (void) ngx_atomic_fetch_add(ngx_stat_handled, 1);\n#endif\n\n        if (ls->addr_ntop) {\n            c->addr_text.data = ngx_pnalloc(c->pool, ls->addr_text_max_len);\n            if (c->addr_text.data == NULL) {\n                ngx_close_accepted_connection(c);\n                return;\n            }\n\n            c->addr_text.len = ngx_sock_ntop(c->sockaddr, c->socklen,\n                                             c->addr_text.data,\n                                             ls->addr_text_max_len, 0);\n            if (c->addr_text.len == 0) {\n                ngx_close_accepted_connection(c);\n                return;\n            }\n        }\n\n#if (NGX_DEBUG)\n        {\n        ngx_str_t  addr;\n        u_char     text[NGX_SOCKADDR_STRLEN];\n\n        ngx_debug_accepted_connection(ecf, c);\n\n        if (log->log_level & NGX_LOG_DEBUG_EVENT) {\n            addr.data = text;\n            addr.len = ngx_sock_ntop(c->sockaddr, c->socklen, text,\n                                     NGX_SOCKADDR_STRLEN, 1);\n\n            ngx_log_debug3(NGX_LOG_DEBUG_EVENT, log, 0,\n                           \"*%uA accept: %V fd:%d\", c->number, &addr, s);\n        }\n\n        }\n#endif\n\n        if (ngx_add_conn && (ngx_event_flags & NGX_USE_EPOLL_EVENT) == 0) {\n            if (ngx_add_conn(c) == NGX_ERROR) {\n                ngx_close_accepted_connection(c);\n                return;\n            }\n        }\n\n        log->data = NULL;\n        log->handler = NULL;\n\n#if (T_NGX_ACCEPT_FILTER)\n        /* accept filter */\n        ngx_int_t          rc;\n        rc = ngx_event_top_accept_filter(c);\n        if (rc == NGX_ERROR) {\n            ngx_close_accepted_connection(c);\n            return;\n        }\n        if (rc == NGX_DECLINED) {\n            if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {\n                ev->available--;\n            }\n            continue;\n        }\n#endif\n\n        ls->handler(c);\n\n        if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {\n            ev->available--;\n        }\n\n    } while (ev->available);\n\n#if (NGX_HAVE_EPOLLEXCLUSIVE)\n    ngx_reorder_accept_events(ls);\n#endif\n}\n\n\nngx_int_t\nngx_trylock_accept_mutex(ngx_cycle_t *cycle)\n{\n    if (ngx_shmtx_trylock(&ngx_accept_mutex)) {\n\n        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                       \"accept mutex locked\");\n\n        if (ngx_accept_mutex_held && ngx_accept_events == 0) {\n            return NGX_OK;\n        }\n\n        if (ngx_enable_accept_events(cycle) == NGX_ERROR) {\n            ngx_shmtx_unlock(&ngx_accept_mutex);\n            return NGX_ERROR;\n        }\n\n        ngx_accept_events = 0;\n        ngx_accept_mutex_held = 1;\n\n        return NGX_OK;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                   \"accept mutex lock failed: %ui\", ngx_accept_mutex_held);\n\n    if (ngx_accept_mutex_held) {\n        if (ngx_disable_accept_events(cycle, 0) == NGX_ERROR) {\n            return NGX_ERROR;\n        }\n\n        ngx_accept_mutex_held = 0;\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_enable_accept_events(ngx_cycle_t *cycle)\n{\n    ngx_uint_t         i;\n    ngx_listening_t   *ls;\n    ngx_connection_t  *c;\n\n    ls = cycle->listening.elts;\n    for (i = 0; i < cycle->listening.nelts; i++) {\n\n        c = ls[i].connection;\n\n        if (c == NULL || c->read->active) {\n            continue;\n        }\n\n        if (ngx_add_event(c->read, NGX_READ_EVENT, 0) == NGX_ERROR) {\n            return NGX_ERROR;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_disable_accept_events(ngx_cycle_t *cycle, ngx_uint_t all)\n{\n    ngx_uint_t         i;\n    ngx_listening_t   *ls;\n    ngx_connection_t  *c;\n\n    ls = cycle->listening.elts;\n    for (i = 0; i < cycle->listening.nelts; i++) {\n\n#if (T_NGX_HAVE_XUDP)\n        if (ls[i].for_xudp) {\n            /*\n             * do not disable xudp sockets\n             * closes the xudp sockets, xudp will not process data anymore\n             */\n            continue;\n        }\n#endif\n\n        c = ls[i].connection;\n\n        if (c == NULL || !c->read->active) {\n            continue;\n        }\n\n#if (NGX_HAVE_REUSEPORT)\n\n        /*\n         * do not disable accept on worker's own sockets\n         * when disabling accept events due to accept mutex\n         */\n\n        if (ls[i].reuseport && !all) {\n            continue;\n        }\n\n#endif\n\n#if (NGX_SSL && NGX_SSL_ASYNC)\n        if (c->async_enable && ngx_del_async_conn) {\n            if (c->num_async_fds) {\n                ngx_del_async_conn(c, NGX_DISABLE_EVENT);\n                c->num_async_fds--;\n            }\n        }\n#endif\n        if (ngx_del_event(c->read, NGX_READ_EVENT, NGX_DISABLE_EVENT)\n            == NGX_ERROR)\n        {\n            return NGX_ERROR;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\n#if (NGX_HAVE_EPOLLEXCLUSIVE)\n\nstatic void\nngx_reorder_accept_events(ngx_listening_t *ls)\n{\n    ngx_connection_t  *c;\n\n    /*\n     * Linux with EPOLLEXCLUSIVE usually notifies only the process which\n     * was first to add the listening socket to the epoll instance.  As\n     * a result most of the connections are handled by the first worker\n     * process.  To fix this, we re-add the socket periodically, so other\n     * workers will get a chance to accept connections.\n     */\n\n    if (!ngx_use_exclusive_accept) {\n        return;\n    }\n\n#if (NGX_HAVE_REUSEPORT)\n\n    if (ls->reuseport) {\n        return;\n    }\n\n#endif\n\n    c = ls->connection;\n\n    if (c->requests++ % 16 != 0\n        && ngx_accept_disabled <= 0)\n    {\n        return;\n    }\n\n    if (ngx_del_event(c->read, NGX_READ_EVENT, NGX_DISABLE_EVENT)\n        == NGX_ERROR)\n    {\n        return;\n    }\n\n    if (ngx_add_event(c->read, NGX_READ_EVENT, NGX_EXCLUSIVE_EVENT)\n        == NGX_ERROR)\n    {\n        return;\n    }\n}\n\n#endif\n\n\nstatic void\nngx_close_accepted_connection(ngx_connection_t *c)\n{\n    ngx_socket_t  fd;\n\n    ngx_free_connection(c);\n\n    fd = c->fd;\n    c->fd = (ngx_socket_t) -1;\n\n    if (ngx_close_socket(fd) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, c->log, ngx_socket_errno,\n                      ngx_close_socket_n \" failed\");\n    }\n\n    if (c->pool) {\n        ngx_destroy_pool(c->pool);\n    }\n\n#if (NGX_STAT_STUB)\n    (void) ngx_atomic_fetch_add(ngx_stat_active, -1);\n#endif\n}\n\n\nu_char *\nngx_accept_log_error(ngx_log_t *log, u_char *buf, size_t len)\n{\n    return ngx_snprintf(buf, len, \" while accepting new connection on %V\",\n                        log->data);\n}\n\n\n#if (NGX_DEBUG)\n\nvoid\nngx_debug_accepted_connection(ngx_event_conf_t *ecf, ngx_connection_t *c)\n{\n    struct sockaddr_in   *sin;\n    ngx_cidr_t           *cidr;\n    ngx_uint_t            i;\n#if (NGX_HAVE_INET6)\n    struct sockaddr_in6  *sin6;\n    ngx_uint_t            n;\n#endif\n\n    cidr = ecf->debug_connection.elts;\n    for (i = 0; i < ecf->debug_connection.nelts; i++) {\n        if (cidr[i].family != (ngx_uint_t) c->sockaddr->sa_family) {\n            goto next;\n        }\n\n        switch (cidr[i].family) {\n\n#if (NGX_HAVE_INET6)\n        case AF_INET6:\n            sin6 = (struct sockaddr_in6 *) c->sockaddr;\n            for (n = 0; n < 16; n++) {\n                if ((sin6->sin6_addr.s6_addr[n]\n                    & cidr[i].u.in6.mask.s6_addr[n])\n                    != cidr[i].u.in6.addr.s6_addr[n])\n                {\n                    goto next;\n                }\n            }\n            break;\n#endif\n\n#if (NGX_HAVE_UNIX_DOMAIN)\n        case AF_UNIX:\n            break;\n#endif\n\n        default: /* AF_INET */\n            sin = (struct sockaddr_in *) c->sockaddr;\n            if ((sin->sin_addr.s_addr & cidr[i].u.in.mask)\n                != cidr[i].u.in.addr)\n            {\n                goto next;\n            }\n            break;\n        }\n\n        c->log->log_level = NGX_LOG_DEBUG_CONNECTION|NGX_LOG_DEBUG_ALL;\n        break;\n\n    next:\n        continue;\n    }\n}\n\n#endif\n"
  },
  {
    "path": "src/event/ngx_event_acceptex.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n\n\nstatic void ngx_close_posted_connection(ngx_connection_t *c);\n\n\nvoid\nngx_event_acceptex(ngx_event_t *rev)\n{\n    ngx_listening_t   *ls;\n    ngx_connection_t  *c;\n\n    c = rev->data;\n    ls = c->listening;\n\n    c->log->handler = ngx_accept_log_error;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, \"AcceptEx: %d\", c->fd);\n\n    if (rev->ovlp.error) {\n        ngx_log_error(NGX_LOG_CRIT, c->log, rev->ovlp.error,\n                      \"AcceptEx() %V failed\", &ls->addr_text);\n        return;\n    }\n\n    /* SO_UPDATE_ACCEPT_CONTEXT is required for shutdown() to work */\n\n    if (setsockopt(c->fd, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT,\n                   (char *) &ls->fd, sizeof(ngx_socket_t))\n        == -1)\n    {\n        ngx_log_error(NGX_LOG_CRIT, c->log, ngx_socket_errno,\n                      \"setsockopt(SO_UPDATE_ACCEPT_CONTEXT) failed for %V\",\n                      &c->addr_text);\n        /* TODO: close socket */\n        return;\n    }\n\n    ngx_getacceptexsockaddrs(c->buffer->pos,\n                             ls->post_accept_buffer_size,\n                             ls->socklen + 16,\n                             ls->socklen + 16,\n                             &c->local_sockaddr, &c->local_socklen,\n                             &c->sockaddr, &c->socklen);\n\n    if (ls->post_accept_buffer_size) {\n        c->buffer->last += rev->available;\n        c->buffer->end = c->buffer->start + ls->post_accept_buffer_size;\n\n    } else {\n        c->buffer = NULL;\n    }\n\n    if (ls->addr_ntop) {\n        c->addr_text.data = ngx_pnalloc(c->pool, ls->addr_text_max_len);\n        if (c->addr_text.data == NULL) {\n            /* TODO: close socket */\n            return;\n        }\n\n        c->addr_text.len = ngx_sock_ntop(c->sockaddr, c->socklen,\n                                         c->addr_text.data,\n                                         ls->addr_text_max_len, 0);\n        if (c->addr_text.len == 0) {\n            /* TODO: close socket */\n            return;\n        }\n    }\n\n    ngx_event_post_acceptex(ls, 1);\n\n    c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1);\n\n    c->start_time = ngx_current_msec;\n\n    ls->handler(c);\n\n    return;\n\n}\n\n\nngx_int_t\nngx_event_post_acceptex(ngx_listening_t *ls, ngx_uint_t n)\n{\n    u_long             rcvd;\n    ngx_err_t          err;\n    ngx_log_t         *log;\n    ngx_uint_t         i;\n    ngx_event_t       *rev, *wev;\n    ngx_socket_t       s;\n    ngx_connection_t  *c;\n\n    for (i = 0; i < n; i++) {\n\n        /* TODO: look up reused sockets */\n\n        s = ngx_socket(ls->sockaddr->sa_family, ls->type, 0);\n\n        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, &ls->log, 0,\n                       ngx_socket_n \" s:%d\", s);\n\n        if (s == (ngx_socket_t) -1) {\n            ngx_log_error(NGX_LOG_ALERT, &ls->log, ngx_socket_errno,\n                          ngx_socket_n \" failed\");\n\n            return NGX_ERROR;\n        }\n\n        c = ngx_get_connection(s, &ls->log);\n\n        if (c == NULL) {\n            return NGX_ERROR;\n        }\n\n        c->pool = ngx_create_pool(ls->pool_size, &ls->log);\n        if (c->pool == NULL) {\n            ngx_close_posted_connection(c);\n            return NGX_ERROR;\n        }\n\n        log = ngx_palloc(c->pool, sizeof(ngx_log_t));\n        if (log == NULL) {\n            ngx_close_posted_connection(c);\n            return NGX_ERROR;\n        }\n\n        c->buffer = ngx_create_temp_buf(c->pool, ls->post_accept_buffer_size\n                                                 + 2 * (ls->socklen + 16));\n        if (c->buffer == NULL) {\n            ngx_close_posted_connection(c);\n            return NGX_ERROR;\n        }\n\n        c->local_sockaddr = ngx_palloc(c->pool, ls->socklen);\n        if (c->local_sockaddr == NULL) {\n            ngx_close_posted_connection(c);\n            return NGX_ERROR;\n        }\n\n        c->sockaddr = ngx_palloc(c->pool, ls->socklen);\n        if (c->sockaddr == NULL) {\n            ngx_close_posted_connection(c);\n            return NGX_ERROR;\n        }\n\n        *log = ls->log;\n        c->log = log;\n\n        c->recv = ngx_recv;\n        c->send = ngx_send;\n        c->recv_chain = ngx_recv_chain;\n        c->send_chain = ngx_send_chain;\n\n        c->listening = ls;\n\n        rev = c->read;\n        wev = c->write;\n\n        rev->ovlp.event = rev;\n        wev->ovlp.event = wev;\n        rev->handler = ngx_event_acceptex;\n\n        rev->ready = 1;\n        wev->ready = 1;\n\n        rev->log = c->log;\n        wev->log = c->log;\n\n        if (ngx_add_event(rev, 0, NGX_IOCP_IO) == NGX_ERROR) {\n            ngx_close_posted_connection(c);\n            return NGX_ERROR;\n        }\n\n        if (ngx_acceptex(ls->fd, s, c->buffer->pos, ls->post_accept_buffer_size,\n                         ls->socklen + 16, ls->socklen + 16,\n                         &rcvd, (LPOVERLAPPED) &rev->ovlp)\n            == 0)\n        {\n            err = ngx_socket_errno;\n            if (err != WSA_IO_PENDING) {\n                ngx_log_error(NGX_LOG_ALERT, &ls->log, err,\n                              \"AcceptEx() %V failed\", &ls->addr_text);\n\n                ngx_close_posted_connection(c);\n                return NGX_ERROR;\n            }\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_close_posted_connection(ngx_connection_t *c)\n{\n    ngx_socket_t  fd;\n\n    ngx_free_connection(c);\n\n    fd = c->fd;\n    c->fd = (ngx_socket_t) -1;\n\n    if (ngx_close_socket(fd) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, c->log, ngx_socket_errno,\n                      ngx_close_socket_n \" failed\");\n    }\n\n    if (c->pool) {\n        ngx_destroy_pool(c->pool);\n    }\n}\n\n\nu_char *\nngx_acceptex_log_error(ngx_log_t *log, u_char *buf, size_t len)\n{\n    return ngx_snprintf(buf, len, \" while posting AcceptEx() on %V\", log->data);\n}\n"
  },
  {
    "path": "src/event/ngx_event_connect.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n#include <ngx_event_connect.h>\n\n\n#if (NGX_HAVE_TRANSPARENT_PROXY)\nstatic ngx_int_t ngx_event_connect_set_transparent(ngx_peer_connection_t *pc,\n    ngx_socket_t s);\n#endif\n\n\nngx_int_t\nngx_event_connect_peer(ngx_peer_connection_t *pc)\n{\n    int                rc, type, value;\n#if (NGX_HAVE_IP_BIND_ADDRESS_NO_PORT || NGX_LINUX)\n    in_port_t          port;\n#endif\n    ngx_int_t          event;\n    ngx_err_t          err;\n    ngx_uint_t         level;\n    ngx_socket_t       s;\n    ngx_event_t       *rev, *wev;\n    ngx_connection_t  *c;\n\n    rc = pc->get(pc, pc->data);\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    type = (pc->type ? pc->type : SOCK_STREAM);\n\n    s = ngx_socket(pc->sockaddr->sa_family, type, 0);\n\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, pc->log, 0, \"%s socket %d\",\n                   (type == SOCK_STREAM) ? \"stream\" : \"dgram\", s);\n\n    if (s == (ngx_socket_t) -1) {\n        ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,\n                      ngx_socket_n \" failed\");\n        return NGX_ERROR;\n    }\n\n\n    c = ngx_get_connection(s, pc->log);\n\n    if (c == NULL) {\n        if (ngx_close_socket(s) == -1) {\n            ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,\n                          ngx_close_socket_n \" failed\");\n        }\n\n        return NGX_ERROR;\n    }\n\n    c->type = type;\n\n    if (pc->rcvbuf) {\n        if (setsockopt(s, SOL_SOCKET, SO_RCVBUF,\n                       (const void *) &pc->rcvbuf, sizeof(int)) == -1)\n        {\n            ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,\n                          \"setsockopt(SO_RCVBUF) failed\");\n            goto failed;\n        }\n    }\n#if (T_NGX_SOCKET_BUFFER)\n    if (pc->sndbuf) {\n        if (setsockopt(s, SOL_SOCKET, SO_SNDBUF,\n                       (const void *) &pc->sndbuf, sizeof(int)) == -1)\n        {\n            ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,\n                          \"setsockopt(SO_SNDBUF) failed\");\n            goto failed;\n        }\n    }\n#endif\n\n    if (pc->so_keepalive) {\n        value = 1;\n\n        if (setsockopt(s, SOL_SOCKET, SO_KEEPALIVE,\n                       (const void *) &value, sizeof(int))\n            == -1)\n        {\n            ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,\n                          \"setsockopt(SO_KEEPALIVE) failed, ignored\");\n        }\n    }\n\n    if (ngx_nonblocking(s) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,\n                      ngx_nonblocking_n \" failed\");\n\n        goto failed;\n    }\n\n    if (pc->local) {\n\n#if (NGX_HAVE_TRANSPARENT_PROXY)\n        if (pc->transparent) {\n            if (ngx_event_connect_set_transparent(pc, s) != NGX_OK) {\n                goto failed;\n            }\n        }\n#endif\n\n#if (NGX_HAVE_IP_BIND_ADDRESS_NO_PORT || NGX_LINUX)\n        port = ngx_inet_get_port(pc->local->sockaddr);\n#endif\n\n#if (NGX_HAVE_IP_BIND_ADDRESS_NO_PORT)\n\n        if (pc->sockaddr->sa_family != AF_UNIX && port == 0) {\n            static int  bind_address_no_port = 1;\n\n            if (bind_address_no_port) {\n                if (setsockopt(s, IPPROTO_IP, IP_BIND_ADDRESS_NO_PORT,\n                               (const void *) &bind_address_no_port,\n                               sizeof(int)) == -1)\n                {\n                    err = ngx_socket_errno;\n\n                    if (err != NGX_EOPNOTSUPP && err != NGX_ENOPROTOOPT) {\n                        ngx_log_error(NGX_LOG_ALERT, pc->log, err,\n                                      \"setsockopt(IP_BIND_ADDRESS_NO_PORT) \"\n                                      \"failed, ignored\");\n\n                    } else {\n                        bind_address_no_port = 0;\n                    }\n                }\n            }\n        }\n\n#endif\n\n#if (NGX_LINUX)\n\n        if (pc->type == SOCK_DGRAM && port != 0) {\n            int  reuse_addr = 1;\n\n            if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,\n                           (const void *) &reuse_addr, sizeof(int))\n                 == -1)\n            {\n                ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,\n                              \"setsockopt(SO_REUSEADDR) failed\");\n                goto failed;\n            }\n        }\n\n#endif\n\n        if (bind(s, pc->local->sockaddr, pc->local->socklen) == -1) {\n            ngx_log_error(NGX_LOG_CRIT, pc->log, ngx_socket_errno,\n                          \"bind(%V) failed\", &pc->local->name);\n\n            goto failed;\n        }\n    }\n\n    if (type == SOCK_STREAM) {\n        c->recv = ngx_recv;\n        c->send = ngx_send;\n        c->recv_chain = ngx_recv_chain;\n        c->send_chain = ngx_send_chain;\n\n        c->sendfile = 1;\n\n        if (pc->sockaddr->sa_family == AF_UNIX) {\n            c->tcp_nopush = NGX_TCP_NOPUSH_DISABLED;\n            c->tcp_nodelay = NGX_TCP_NODELAY_DISABLED;\n\n#if (NGX_SOLARIS)\n            /* Solaris's sendfilev() supports AF_NCA, AF_INET, and AF_INET6 */\n            c->sendfile = 0;\n#endif\n        }\n\n    } else { /* type == SOCK_DGRAM */\n        c->recv = ngx_udp_recv;\n        c->send = ngx_send;\n        c->send_chain = ngx_udp_send_chain;\n\n        c->need_flush_buf = 1;\n    }\n\n    c->log_error = pc->log_error;\n\n    rev = c->read;\n    wev = c->write;\n\n    rev->log = pc->log;\n    wev->log = pc->log;\n\n    pc->connection = c;\n\n    c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1);\n\n    c->start_time = ngx_current_msec;\n\n    if (ngx_add_conn) {\n        if (ngx_add_conn(c) == NGX_ERROR) {\n            goto failed;\n        }\n    }\n\n    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, pc->log, 0,\n                   \"connect to %V, fd:%d #%uA\", pc->name, s, c->number);\n\n    rc = connect(s, pc->sockaddr, pc->socklen);\n\n    if (rc == -1) {\n        err = ngx_socket_errno;\n\n\n        if (err != NGX_EINPROGRESS\n#if (NGX_WIN32)\n            /* Winsock returns WSAEWOULDBLOCK (NGX_EAGAIN) */\n            && err != NGX_EAGAIN\n#endif\n            )\n        {\n            if (err == NGX_ECONNREFUSED\n#if (NGX_LINUX)\n                /*\n                 * Linux returns EAGAIN instead of ECONNREFUSED\n                 * for unix sockets if listen queue is full\n                 */\n                || err == NGX_EAGAIN\n#endif\n                || err == NGX_ECONNRESET\n                || err == NGX_ENETDOWN\n                || err == NGX_ENETUNREACH\n                || err == NGX_EHOSTDOWN\n                || err == NGX_EHOSTUNREACH)\n            {\n                level = NGX_LOG_ERR;\n\n            } else {\n                level = NGX_LOG_CRIT;\n            }\n\n            ngx_log_error(level, c->log, err, \"connect() to %V failed\",\n                          pc->name);\n\n            ngx_close_connection(c);\n            pc->connection = NULL;\n\n            return NGX_DECLINED;\n        }\n    }\n\n    if (ngx_add_conn) {\n        if (rc == -1) {\n\n            /* NGX_EINPROGRESS */\n\n            return NGX_AGAIN;\n        }\n\n        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, pc->log, 0, \"connected\");\n\n        wev->ready = 1;\n\n        return NGX_OK;\n    }\n\n    if (ngx_event_flags & NGX_USE_IOCP_EVENT) {\n\n        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, pc->log, ngx_socket_errno,\n                       \"connect(): %d\", rc);\n\n        if (ngx_blocking(s) == -1) {\n            ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,\n                          ngx_blocking_n \" failed\");\n            goto failed;\n        }\n\n        /*\n         * FreeBSD's aio allows to post an operation on non-connected socket.\n         * NT does not support it.\n         *\n         * TODO: check in Win32, etc. As workaround we can use NGX_ONESHOT_EVENT\n         */\n\n        rev->ready = 1;\n        wev->ready = 1;\n\n        return NGX_OK;\n    }\n\n    if (ngx_event_flags & NGX_USE_CLEAR_EVENT) {\n\n        /* kqueue */\n\n        event = NGX_CLEAR_EVENT;\n\n    } else {\n\n        /* select, poll, /dev/poll */\n\n        event = NGX_LEVEL_EVENT;\n    }\n\n    if (ngx_add_event(rev, NGX_READ_EVENT, event) != NGX_OK) {\n        goto failed;\n    }\n\n    if (rc == -1) {\n\n        /* NGX_EINPROGRESS */\n\n        if (ngx_add_event(wev, NGX_WRITE_EVENT, event) != NGX_OK) {\n            goto failed;\n        }\n\n        return NGX_AGAIN;\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, pc->log, 0, \"connected\");\n\n    wev->ready = 1;\n\n    return NGX_OK;\n\nfailed:\n\n    ngx_close_connection(c);\n    pc->connection = NULL;\n\n    return NGX_ERROR;\n}\n\n\n#if (NGX_HAVE_TRANSPARENT_PROXY)\n\nstatic ngx_int_t\nngx_event_connect_set_transparent(ngx_peer_connection_t *pc, ngx_socket_t s)\n{\n    int  value;\n\n    value = 1;\n\n#if defined(SO_BINDANY)\n\n    if (setsockopt(s, SOL_SOCKET, SO_BINDANY,\n                   (const void *) &value, sizeof(int)) == -1)\n    {\n        ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,\n                      \"setsockopt(SO_BINDANY) failed\");\n        return NGX_ERROR;\n    }\n\n#else\n\n    switch (pc->local->sockaddr->sa_family) {\n\n    case AF_INET:\n\n#if defined(IP_TRANSPARENT)\n\n        if (setsockopt(s, IPPROTO_IP, IP_TRANSPARENT,\n                       (const void *) &value, sizeof(int)) == -1)\n        {\n            ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,\n                          \"setsockopt(IP_TRANSPARENT) failed\");\n            return NGX_ERROR;\n        }\n\n#elif defined(IP_BINDANY)\n\n        if (setsockopt(s, IPPROTO_IP, IP_BINDANY,\n                       (const void *) &value, sizeof(int)) == -1)\n        {\n            ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,\n                          \"setsockopt(IP_BINDANY) failed\");\n            return NGX_ERROR;\n        }\n\n#endif\n\n        break;\n\n#if (NGX_HAVE_INET6)\n\n    case AF_INET6:\n\n#if defined(IPV6_TRANSPARENT)\n\n        if (setsockopt(s, IPPROTO_IPV6, IPV6_TRANSPARENT,\n                       (const void *) &value, sizeof(int)) == -1)\n        {\n            ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,\n                          \"setsockopt(IPV6_TRANSPARENT) failed\");\n            return NGX_ERROR;\n        }\n\n#elif defined(IPV6_BINDANY)\n\n        if (setsockopt(s, IPPROTO_IPV6, IPV6_BINDANY,\n                       (const void *) &value, sizeof(int)) == -1)\n        {\n            ngx_log_error(NGX_LOG_ALERT, pc->log, ngx_socket_errno,\n                          \"setsockopt(IPV6_BINDANY) failed\");\n            return NGX_ERROR;\n        }\n\n#else\n\n        ngx_log_error(NGX_LOG_ALERT, pc->log, 0,\n                      \"could not enable transparent proxying for IPv6 \"\n                      \"on this platform\");\n\n        return NGX_ERROR;\n\n#endif\n\n        break;\n\n#endif /* NGX_HAVE_INET6 */\n\n    }\n\n#endif /* SO_BINDANY */\n\n    return NGX_OK;\n}\n\n#endif\n\n\nngx_int_t\nngx_event_get_peer(ngx_peer_connection_t *pc, void *data)\n{\n    return NGX_OK;\n}\n"
  },
  {
    "path": "src/event/ngx_event_connect.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_EVENT_CONNECT_H_INCLUDED_\n#define _NGX_EVENT_CONNECT_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n\n\n#define NGX_PEER_KEEPALIVE           1\n#define NGX_PEER_NEXT                2\n#define NGX_PEER_FAILED              4\n\n\ntypedef struct ngx_peer_connection_s  ngx_peer_connection_t;\n\ntypedef ngx_int_t (*ngx_event_get_peer_pt)(ngx_peer_connection_t *pc,\n    void *data);\ntypedef void (*ngx_event_free_peer_pt)(ngx_peer_connection_t *pc, void *data,\n    ngx_uint_t state);\ntypedef void (*ngx_event_notify_peer_pt)(ngx_peer_connection_t *pc,\n    void *data, ngx_uint_t type);\ntypedef ngx_int_t (*ngx_event_set_peer_session_pt)(ngx_peer_connection_t *pc,\n    void *data);\ntypedef void (*ngx_event_save_peer_session_pt)(ngx_peer_connection_t *pc,\n    void *data);\n\n\nstruct ngx_peer_connection_s {\n    ngx_connection_t                *connection;\n\n    struct sockaddr                 *sockaddr;\n    socklen_t                        socklen;\n    ngx_str_t                       *name;\n#if (T_NGX_HTTP_DYNAMIC_RESOLVE)    \n    ngx_str_t                       *host;\n#endif\n\n    ngx_uint_t                       tries;\n    ngx_msec_t                       start_time;\n\n    ngx_event_get_peer_pt            get;\n    ngx_event_free_peer_pt           free;\n    ngx_event_notify_peer_pt         notify;\n    void                            *data;\n\n#if (NGX_SSL || NGX_COMPAT)\n    ngx_event_set_peer_session_pt    set_session;\n    ngx_event_save_peer_session_pt   save_session;\n#endif\n\n    ngx_addr_t                      *local;\n\n    int                              type;\n    int                              rcvbuf;\n#if (T_NGX_SOCKET_BUFFER)\n    int                              sndbuf;\n#endif\n\n    ngx_log_t                       *log;\n\n    unsigned                         cached:1;\n    unsigned                         transparent:1;\n    unsigned                         so_keepalive:1;\n    unsigned                         down:1;\n\n#if (T_NGX_HTTP_DYNAMIC_RESOLVE)    \n    unsigned                         resolved:2;\n#endif\n\n                                     /* ngx_connection_log_error_e */\n    unsigned                         log_error:2;\n\n    NGX_COMPAT_BEGIN(2)\n    NGX_COMPAT_END\n};\n\n\nngx_int_t ngx_event_connect_peer(ngx_peer_connection_t *pc);\nngx_int_t ngx_event_get_peer(ngx_peer_connection_t *pc, void *data);\n\n\n#endif /* _NGX_EVENT_CONNECT_H_INCLUDED_ */\n"
  },
  {
    "path": "src/event/ngx_event_connectex.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n\n\n#define NGX_MAX_PENDING_CONN  10\n\n\nstatic CRITICAL_SECTION  connect_lock;\nstatic int               nconnects;\nstatic ngx_connection_t  pending_connects[NGX_MAX_PENDING_CONN];\n\nstatic HANDLE            pending_connect_event;\n\n__declspec(thread) int                nevents = 0;\n__declspec(thread) WSAEVENT           events[WSA_MAXIMUM_WAIT_EVENTS + 1];\n__declspec(thread) ngx_connection_t  *conn[WSA_MAXIMUM_WAIT_EVENTS + 1];\n\n\n\nint ngx_iocp_wait_connect(ngx_connection_t *c)\n{\n    for ( ;; ) {\n        EnterCriticalSection(&connect_lock);\n\n        if (nconnects < NGX_MAX_PENDING_CONN) {\n            pending_connects[--nconnects] = c;\n            LeaveCriticalSection(&connect_lock);\n\n            if (SetEvent(pending_connect_event) == 0) {\n                ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,\n                              \"SetEvent() failed\");\n                return NGX_ERROR;\n\n            break;\n        }\n\n        LeaveCriticalSection(&connect_lock);\n        ngx_log_error(NGX_LOG_NOTICE, c->log, 0,\n                      \"max number of pending connect()s is %d\",\n                      NGX_MAX_PENDING_CONN);\n        msleep(100);\n    }\n\n    if (!started) {\n        if (ngx_iocp_new_thread(1) == NGX_ERROR) {\n            return NGX_ERROR;\n        }\n        started = 1;\n    }\n\n    return NGX_OK;\n}\n\n\nint ngx_iocp_new_thread(int main)\n{\n    u_int  id;\n\n    if (main) {\n        pending_connect_event = CreateEvent(NULL, 0, 1, NULL);\n        if (pending_connect_event == INVALID_HANDLE_VALUE) {\n            ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,\n                          \"CreateThread() failed\");\n            return NGX_ERROR;\n        }\n    }\n\n    if (CreateThread(NULL, 0, ngx_iocp_wait_events, main, 0, &id)\n                                                       == INVALID_HANDLE_VALUE)\n    {\n        ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,\n                      \"CreateThread() failed\");\n        return NGX_ERROR;\n    }\n\n    SetEvent(event) {\n        ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,\n                      \"SetEvent() failed\");\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nint ngx_iocp_new_connect()\n{\n    EnterCriticalSection(&connect_lock);\n    c = pending_connects[--nconnects];\n    LeaveCriticalSection(&connect_lock);\n\n    conn[nevents] = c;\n\n    events[nevents] = WSACreateEvent();\n    if (events[nevents] == INVALID_HANDLE_VALUE) {\n        ngx_log_error(NGX_LOG_ALERT, c->log, ngx_socket_errno,\n                      \"WSACreateEvent() failed\");\n        return NGX_ERROR;\n    }\n\n    if (WSAEventSelect(c->fd, events[nevents], FD_CONNECT) == -1)\n        ngx_log_error(NGX_LOG_ALERT, c->log, ngx_socket_errno,\n                      \"WSAEventSelect() failed\");\n        return NGX_ERROR;\n    }\n\n    nevents++;\n\n    return NGX_OK;\n}\n\n\nvoid ngx_iocp_wait_events(int main)\n{\n    WSANETWORKEVENTS  ne;\n\n    nevents = 1;\n    events[0] = pending_connect_event;\n    conn[0] = NULL;\n\n    for ( ;; ) {\n        offset = (nevents == WSA_MAXIMUM_WAIT_EVENTS + 1) ? 1 : 0;\n        timeout = (nevents == 1 && !first) ? 60000 : INFINITE;\n\n        n = WSAWaitForMultipleEvents(nevents - offset, events[offset],\n                                     0, timeout, 0);\n        if (n == WAIT_FAILED) {\n            ngx_log_error(NGX_LOG_ALERT, log, ngx_socket_errno,\n                          \"WSAWaitForMultipleEvents() failed\");\n            continue;\n        }\n\n        if (n == WAIT_TIMEOUT) {\n            if (nevents == 2 && !main) {\n                ExitThread(0);\n            }\n\n            ngx_log_error(NGX_LOG_ALERT, log, 0,\n                          \"WSAWaitForMultipleEvents() \"\n                          \"returned unexpected WAIT_TIMEOUT\");\n            continue;\n        }\n\n        n -= WSA_WAIT_EVENT_0;\n\n        if (events[n] == NULL) {\n\n            /* the pending_connect_event */\n\n            if (nevents == WSA_MAXIMUM_WAIT_EVENTS) {\n                ngx_iocp_new_thread(0);\n            } else {\n                ngx_iocp_new_connect();\n            }\n\n            continue;\n        }\n\n        if (WSAEnumNetworkEvents(c[n].fd, events[n], &ne) == -1) {\n            ngx_log_error(NGX_LOG_ALERT, log, ngx_socket_errno,\n                          \"WSAEnumNetworkEvents() failed\");\n            continue;\n        }\n\n        if (ne.lNetworkEvents & FD_CONNECT) {\n            conn[n].write->ovlp.error = ne.iErrorCode[FD_CONNECT_BIT];\n\n            if (PostQueuedCompletionStatus(iocp, 0, NGX_IOCP_CONNECT,\n                                           &conn[n].write->ovlp) == 0)\n            {\n                ngx_log_error(NGX_LOG_ALERT, log, ngx_socket_errno,\n                              \"PostQueuedCompletionStatus() failed\");\n                continue;\n            }\n\n            if (n < nevents) {\n                conn[n] = conn[nevents];\n                events[n] = events[nevents];\n            }\n\n            nevents--;\n            continue;\n        }\n\n        if (ne.lNetworkEvents & FD_ACCEPT) {\n\n            /* CHECK ERROR ??? */\n\n            ngx_event_post_acceptex(conn[n].listening, 1);\n            continue;\n        }\n\n        ngx_log_error(NGX_LOG_ALERT, c[n].log, 0,\n                      \"WSAWaitForMultipleEvents() \"\n                      \"returned unexpected network event %ul\",\n                      ne.lNetworkEvents);\n    }\n}\n"
  },
  {
    "path": "src/event/ngx_event_openssl.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n\n\n#define NGX_SSL_PASSWORD_BUFFER_SIZE  4096\n\n\ntypedef struct {\n    ngx_uint_t  engine;   /* unsigned  engine:1; */\n} ngx_openssl_conf_t;\n\n\nstatic X509 *ngx_ssl_load_certificate(ngx_pool_t *pool, char **err,\n    ngx_str_t *cert, STACK_OF(X509) **chain);\nstatic EVP_PKEY *ngx_ssl_load_certificate_key(ngx_pool_t *pool, char **err,\n    ngx_str_t *key, ngx_array_t *passwords);\nstatic int ngx_ssl_password_callback(char *buf, int size, int rwflag,\n    void *userdata);\nstatic int ngx_ssl_verify_callback(int ok, X509_STORE_CTX *x509_store);\nstatic void ngx_ssl_info_callback(const ngx_ssl_conn_t *ssl_conn, int where,\n    int ret);\nstatic void ngx_ssl_passwords_cleanup(void *data);\nstatic int ngx_ssl_new_client_session(ngx_ssl_conn_t *ssl_conn,\n    ngx_ssl_session_t *sess);\n#ifdef SSL_READ_EARLY_DATA_SUCCESS\nstatic ngx_int_t ngx_ssl_try_early_data(ngx_connection_t *c);\n#endif\n#if (NGX_DEBUG)\nstatic void ngx_ssl_handshake_log(ngx_connection_t *c);\n#endif\nstatic void ngx_ssl_handshake_handler(ngx_event_t *ev);\n#ifdef SSL_READ_EARLY_DATA_SUCCESS\nstatic ssize_t ngx_ssl_recv_early(ngx_connection_t *c, u_char *buf,\n    size_t size);\n#endif\nstatic ngx_int_t ngx_ssl_handle_recv(ngx_connection_t *c, int n);\nstatic void ngx_ssl_write_handler(ngx_event_t *wev);\n#ifdef SSL_READ_EARLY_DATA_SUCCESS\nstatic ssize_t ngx_ssl_write_early(ngx_connection_t *c, u_char *data,\n    size_t size);\n#endif\nstatic ssize_t ngx_ssl_sendfile(ngx_connection_t *c, ngx_buf_t *file,\n    size_t size);\nstatic void ngx_ssl_read_handler(ngx_event_t *rev);\nstatic void ngx_ssl_shutdown_handler(ngx_event_t *ev);\nstatic void ngx_ssl_connection_error(ngx_connection_t *c, int sslerr,\n    ngx_err_t err, char *text);\nstatic void ngx_ssl_clear_error(ngx_log_t *log);\n\nstatic ngx_int_t ngx_ssl_session_id_context(ngx_ssl_t *ssl,\n    ngx_str_t *sess_ctx, ngx_array_t *certificates);\nstatic int ngx_ssl_new_session(ngx_ssl_conn_t *ssl_conn,\n    ngx_ssl_session_t *sess);\nstatic ngx_ssl_session_t *ngx_ssl_get_cached_session(ngx_ssl_conn_t *ssl_conn,\n#if OPENSSL_VERSION_NUMBER >= 0x10100003L\n    const\n#endif\n    u_char *id, int len, int *copy);\nstatic void ngx_ssl_remove_session(SSL_CTX *ssl, ngx_ssl_session_t *sess);\nstatic void ngx_ssl_expire_sessions(ngx_ssl_session_cache_t *cache,\n    ngx_slab_pool_t *shpool, ngx_uint_t n);\nstatic void ngx_ssl_session_rbtree_insert_value(ngx_rbtree_node_t *temp,\n    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);\n\n#ifdef SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB\nstatic int ngx_ssl_ticket_key_callback(ngx_ssl_conn_t *ssl_conn,\n    unsigned char *name, unsigned char *iv, EVP_CIPHER_CTX *ectx,\n    HMAC_CTX *hctx, int enc);\nstatic ngx_int_t ngx_ssl_rotate_ticket_keys(SSL_CTX *ssl_ctx, ngx_log_t *log);\nstatic void ngx_ssl_ticket_keys_cleanup(void *data);\n#endif\n\n#ifndef X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT\nstatic ngx_int_t ngx_ssl_check_name(ngx_str_t *name, ASN1_STRING *str);\n#endif\n\nstatic time_t ngx_ssl_parse_time(\n#if OPENSSL_VERSION_NUMBER > 0x10100000L\n    const\n#endif\n    ASN1_TIME *asn1time, ngx_log_t *log);\n\nstatic void *ngx_openssl_create_conf(ngx_cycle_t *cycle);\nstatic char *ngx_openssl_engine(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\nstatic void ngx_openssl_exit(ngx_cycle_t *cycle);\n\n#if (NGX_SSL && NGX_SSL_ASYNC)\nstatic void ngx_ssl_handshake_async_handler(ngx_event_t * aev);\nstatic void ngx_ssl_read_async_handler(ngx_event_t * aev);\nstatic void ngx_ssl_write_async_handler(ngx_event_t * aev);\nstatic void ngx_ssl_shutdown_async_handler(ngx_event_t *aev);\n#endif\n\n#if defined(T_NGX_HAVE_DTLS)\n#define NGX_DTLS_MAX_RETRANSMIT_TIME 3\n\nstatic void ngx_dtls_retransmit(ngx_event_t *ev);\nstatic ngx_int_t ngx_dtls_handshake(ngx_connection_t *c);\n\nstatic int ngx_dtls_client_hmac(SSL *ssl, u_char res[EVP_MAX_MD_SIZE],\n    unsigned int *rlen);\n\nstatic int ngx_dtls_generate_cookie_cb(SSL *ssl, unsigned char *cookie,\n    unsigned int *cookie_len);\n\nstatic int ngx_dtls_verify_cookie_cb(SSL *ssl,\n#if OPENSSL_VERSION_NUMBER >= 0x10100000L\n    const\n#endif\nunsigned char *cookie, unsigned int cookie_len);\n\n#endif\nstatic ngx_command_t  ngx_openssl_commands[] = {\n\n    { ngx_string(\"ssl_engine\"),\n      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,\n      ngx_openssl_engine,\n      0,\n      0,\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_core_module_t  ngx_openssl_module_ctx = {\n    ngx_string(\"openssl\"),\n    ngx_openssl_create_conf,\n    NULL\n};\n\n\nngx_module_t  ngx_openssl_module = {\n    NGX_MODULE_V1,\n    &ngx_openssl_module_ctx,               /* module context */\n    ngx_openssl_commands,                  /* module directives */\n    NGX_CORE_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    ngx_openssl_exit,                      /* exit master */\n    NGX_MODULE_V1_PADDING\n};\n\n\nint  ngx_ssl_connection_index;\nint  ngx_ssl_server_conf_index;\nint  ngx_ssl_session_cache_index;\nint  ngx_ssl_ticket_keys_index;\nint  ngx_ssl_ocsp_index;\nint  ngx_ssl_certificate_index;\nint  ngx_ssl_next_certificate_index;\nint  ngx_ssl_certificate_name_index;\nint  ngx_ssl_stapling_index;\n\n\n#if (NGX_SSL && NGX_SSL_ASYNC)\nstatic void\nngx_ssl_empty_handler(ngx_event_t *aev)\n{\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, aev->log, 0, \"ssl empty handler\");\n\n    return;\n}\n\nngx_int_t\nngx_ssl_async_process_fds(ngx_connection_t *c)\n{\n    OSSL_ASYNC_FD *add_fds = NULL;\n    OSSL_ASYNC_FD *del_fds = NULL;\n    size_t        num_add_fds = 0;\n    size_t        num_del_fds = 0;\n    unsigned      loop = 0;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"ngx_ssl_async_process_fds called\");\n\n    if (!ngx_del_async_conn || !ngx_add_async_conn) {\n        ngx_ssl_error(NGX_LOG_ALERT, c->log, 0,\n                      \"Async notifications not supported\");\n        return NGX_ERROR;\n    }\n\n    SSL_get_changed_async_fds(c->ssl->connection, NULL, &num_add_fds,\n                              NULL, &num_del_fds);\n\n    if (num_add_fds) {\n        add_fds = ngx_alloc(num_add_fds * sizeof(OSSL_ASYNC_FD), c->log);\n        if (add_fds == NULL) {\n            ngx_ssl_error(NGX_LOG_ALERT, c->log, 0,\n                          \"Memory Allocation Error\");\n            return NGX_ERROR;\n        }\n    }\n\n    if (num_del_fds) {\n        del_fds = ngx_alloc(num_del_fds * sizeof(OSSL_ASYNC_FD), c->log);\n        if (del_fds == NULL) {\n            ngx_ssl_error(NGX_LOG_ALERT, c->log, 0,\n                          \"Memory Allocation Error\");\n            if (add_fds)\n                ngx_free(add_fds);\n            return NGX_ERROR;\n        }\n    }\n\n    SSL_get_changed_async_fds(c->ssl->connection, add_fds, &num_add_fds,\n                              del_fds, &num_del_fds);\n\n    if (num_del_fds) {\n        for (loop = 0; loop < num_del_fds; loop++) {\n            c->async_fd = del_fds[loop];\n            if (c->num_async_fds) {\n                ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, \"%s: deleting fd = %d\", __func__, c->async_fd);\n                ngx_del_async_conn(c, NGX_DISABLE_EVENT);\n                c->num_async_fds--;\n            }\n        }\n    }\n    if (num_add_fds) {\n        for (loop = 0; loop < num_add_fds; loop++) {\n            if (c->num_async_fds == 0) {\n                c->num_async_fds++;\n                c->async_fd = add_fds[loop];\n                ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, \"%s: adding fd = %d\", __func__, c->async_fd);\n                ngx_add_async_conn(c);\n            }\n        }\n    }\n\n    if (add_fds)\n        ngx_free(add_fds);\n    if (del_fds)\n        ngx_free(del_fds);\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_ssl_handshake_async_handler(ngx_event_t *aev)\n{\n    ngx_connection_t  *c;\n\n    c = aev->data;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"SSL handshake async handler\");\n\n    aev->ready = 0;\n    aev->handler = ngx_ssl_empty_handler;\n    c->read->handler = c->read->saved_handler;\n\n    if (ngx_ssl_handshake(c) == NGX_AGAIN) {\n        return;\n    }\n\n    c->ssl->handler(c);\n}\n\n\nstatic void\nngx_ssl_read_async_handler(ngx_event_t *aev)\n{\n    ngx_connection_t  *c;\n\n    c = aev->data;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"SSL read async handler\");\n\n    aev->ready = 0;\n    aev->handler = ngx_ssl_empty_handler;\n    c->read->handler = c->read->saved_handler;\n\n    c->read->handler(c->read);\n}\n\n\nstatic void\nngx_ssl_shutdown_async_handler(ngx_event_t *aev)\n{\n    ngx_connection_t           *c;\n    ngx_connection_handler_pt   handler;\n\n    c = aev->data;\n    handler = c->ssl->handler;\n\n    if (aev->timedout) {\n        c->timedout = 1;\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, aev->log, 0,\n                   \"SSL shutdown async handler\");\n\n    aev->ready = 0;\n    aev->handler = ngx_ssl_empty_handler;\n    c->read->handler = c->read->saved_handler;\n\n    if (ngx_ssl_shutdown(c) == NGX_AGAIN) {\n        return;\n    }\n\n    handler(c);\n}\n\n\nstatic void\nngx_ssl_write_async_handler(ngx_event_t *aev)\n{\n    ngx_connection_t  *c;\n\n    c = aev->data;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"SSL write async handler\");\n\n    aev->ready = 0;\n    aev->handler = ngx_ssl_empty_handler;\n    c->read->handler = c->read->saved_handler;\n\n    c->write->handler(c->write);\n}\n#endif\n\nngx_int_t\nngx_ssl_init(ngx_log_t *log)\n{\n#if OPENSSL_VERSION_NUMBER >= 0x10100003L\n\n    if (OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG, NULL) == 0) {\n        ngx_ssl_error(NGX_LOG_ALERT, log, 0, \"OPENSSL_init_ssl() failed\");\n        return NGX_ERROR;\n    }\n\n    /*\n     * OPENSSL_init_ssl() may leave errors in the error queue\n     * while returning success\n     */\n\n    ERR_clear_error();\n\n#else\n\n    OPENSSL_config(NULL);\n\n    SSL_library_init();\n    SSL_load_error_strings();\n\n    OpenSSL_add_all_algorithms();\n\n#endif\n\n#ifndef SSL_OP_NO_COMPRESSION\n    {\n    /*\n     * Disable gzip compression in OpenSSL prior to 1.0.0 version,\n     * this saves about 522K per connection.\n     */\n    int                  n;\n    STACK_OF(SSL_COMP)  *ssl_comp_methods;\n\n    ssl_comp_methods = SSL_COMP_get_compression_methods();\n    n = sk_SSL_COMP_num(ssl_comp_methods);\n\n    while (n--) {\n        (void) sk_SSL_COMP_pop(ssl_comp_methods);\n    }\n    }\n#endif\n\n    ngx_ssl_connection_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);\n\n    if (ngx_ssl_connection_index == -1) {\n        ngx_ssl_error(NGX_LOG_ALERT, log, 0, \"SSL_get_ex_new_index() failed\");\n        return NGX_ERROR;\n    }\n\n    ngx_ssl_server_conf_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL,\n                                                         NULL);\n    if (ngx_ssl_server_conf_index == -1) {\n        ngx_ssl_error(NGX_LOG_ALERT, log, 0,\n                      \"SSL_CTX_get_ex_new_index() failed\");\n        return NGX_ERROR;\n    }\n\n    ngx_ssl_session_cache_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL,\n                                                           NULL);\n    if (ngx_ssl_session_cache_index == -1) {\n        ngx_ssl_error(NGX_LOG_ALERT, log, 0,\n                      \"SSL_CTX_get_ex_new_index() failed\");\n        return NGX_ERROR;\n    }\n\n    ngx_ssl_ticket_keys_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL,\n                                                         NULL);\n    if (ngx_ssl_ticket_keys_index == -1) {\n        ngx_ssl_error(NGX_LOG_ALERT, log, 0,\n                      \"SSL_CTX_get_ex_new_index() failed\");\n        return NGX_ERROR;\n    }\n\n    ngx_ssl_ocsp_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, NULL);\n    if (ngx_ssl_ocsp_index == -1) {\n        ngx_ssl_error(NGX_LOG_ALERT, log, 0,\n                      \"SSL_CTX_get_ex_new_index() failed\");\n        return NGX_ERROR;\n    }\n\n    ngx_ssl_certificate_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL,\n                                                         NULL);\n    if (ngx_ssl_certificate_index == -1) {\n        ngx_ssl_error(NGX_LOG_ALERT, log, 0,\n                      \"SSL_CTX_get_ex_new_index() failed\");\n        return NGX_ERROR;\n    }\n\n    ngx_ssl_next_certificate_index = X509_get_ex_new_index(0, NULL, NULL, NULL,\n                                                           NULL);\n    if (ngx_ssl_next_certificate_index == -1) {\n        ngx_ssl_error(NGX_LOG_ALERT, log, 0, \"X509_get_ex_new_index() failed\");\n        return NGX_ERROR;\n    }\n\n    ngx_ssl_certificate_name_index = X509_get_ex_new_index(0, NULL, NULL, NULL,\n                                                           NULL);\n\n    if (ngx_ssl_certificate_name_index == -1) {\n        ngx_ssl_error(NGX_LOG_ALERT, log, 0, \"X509_get_ex_new_index() failed\");\n        return NGX_ERROR;\n    }\n\n    ngx_ssl_stapling_index = X509_get_ex_new_index(0, NULL, NULL, NULL, NULL);\n\n    if (ngx_ssl_stapling_index == -1) {\n        ngx_ssl_error(NGX_LOG_ALERT, log, 0, \"X509_get_ex_new_index() failed\");\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_t protocols, void *data)\n{\n#if defined(T_NGX_HAVE_DTLS)\n    if (protocols & NGX_SSL_DTLSv1 || protocols & NGX_SSL_DTLSv1_2) {\n\n\n#if OPENSSL_VERSION_NUMBER < 0x10100000L\n\n        if (protocols & NGX_SSL_DTLSv1_2) {\n\n            /* DTLS 1.2 is only supported since 1.0.2 */\n\n            /* DTLSv1_x_method()  functions are deprecated in 1.1.0 */\n\n#if OPENSSL_VERSION_NUMBER < 0x10002000L\n\n            /* ancient ... 1.0.2 */\n            ngx_log_error(NGX_LOG_EMERG, ssl->log, 0,\n                          \"DTLSv1.2 is not supported by \"\n                          \"the used version of OpenSSL\");\n            return NGX_ERROR;\n\n#else\n            /* 1.0.2 ... 1.1 */\n            ssl->ctx = SSL_CTX_new(DTLSv1_2_method());\n#endif\n        }\n\n        /* note: either 1.2 or 1.1 methods may be initialized, not both,\n         * preferred is 1.2 if both specified in ssl_protocols\n         */\n\n        if (protocols & NGX_SSL_DTLSv1 && ssl->ctx == NULL) {\n            ssl->ctx = SSL_CTX_new(DTLSv1_method());\n        }\n#else\n        ssl->ctx = SSL_CTX_new(DTLS_method());\n#endif\n\n    } else {\n        ssl->ctx = SSL_CTX_new(SSLv23_method());\n    }\n#else\n\n    ssl->ctx = SSL_CTX_new(SSLv23_method());\n#endif\n\n    if (ssl->ctx == NULL) {\n        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, \"SSL_CTX_new() failed\");\n        return NGX_ERROR;\n    }\n\n#if defined(T_NGX_HAVE_DTLS)\n    if (protocols & NGX_SSL_DTLSv1\n        || protocols & NGX_SSL_DTLSv1_2) {\n\n        SSL_CTX_set_cookie_generate_cb(ssl->ctx, ngx_dtls_generate_cookie_cb);\n        SSL_CTX_set_cookie_verify_cb(ssl->ctx, ngx_dtls_verify_cookie_cb);\n    }\n#endif\n\n    if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_server_conf_index, data) == 0) {\n        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,\n                      \"SSL_CTX_set_ex_data() failed\");\n        return NGX_ERROR;\n    }\n\n    if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_certificate_index, NULL) == 0) {\n        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,\n                      \"SSL_CTX_set_ex_data() failed\");\n        return NGX_ERROR;\n    }\n\n    ssl->buffer_size = NGX_SSL_BUFSIZE;\n\n    /* client side options */\n\n#ifdef SSL_OP_MICROSOFT_SESS_ID_BUG\n    SSL_CTX_set_options(ssl->ctx, SSL_OP_MICROSOFT_SESS_ID_BUG);\n#endif\n\n#ifdef SSL_OP_NETSCAPE_CHALLENGE_BUG\n    SSL_CTX_set_options(ssl->ctx, SSL_OP_NETSCAPE_CHALLENGE_BUG);\n#endif\n\n    /* server side options */\n\n#ifdef SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG\n    SSL_CTX_set_options(ssl->ctx, SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG);\n#endif\n\n#ifdef SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER\n    SSL_CTX_set_options(ssl->ctx, SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER);\n#endif\n\n#ifdef SSL_OP_SSLEAY_080_CLIENT_DH_BUG\n    SSL_CTX_set_options(ssl->ctx, SSL_OP_SSLEAY_080_CLIENT_DH_BUG);\n#endif\n\n#ifdef SSL_OP_TLS_D5_BUG\n    SSL_CTX_set_options(ssl->ctx, SSL_OP_TLS_D5_BUG);\n#endif\n\n#ifdef SSL_OP_TLS_BLOCK_PADDING_BUG\n    SSL_CTX_set_options(ssl->ctx, SSL_OP_TLS_BLOCK_PADDING_BUG);\n#endif\n\n#ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS\n    SSL_CTX_set_options(ssl->ctx, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS);\n#endif\n\n    SSL_CTX_set_options(ssl->ctx, SSL_OP_SINGLE_DH_USE);\n\n#if OPENSSL_VERSION_NUMBER >= 0x009080dfL\n    /* only in 0.9.8m+ */\n    SSL_CTX_clear_options(ssl->ctx,\n                          SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_TLSv1);\n#endif\n\n    if (!(protocols & NGX_SSL_SSLv2)) {\n        SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_SSLv2);\n    }\n    if (!(protocols & NGX_SSL_SSLv3)) {\n        SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_SSLv3);\n    }\n    if (!(protocols & NGX_SSL_TLSv1)) {\n        SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_TLSv1);\n    }\n#ifdef SSL_OP_NO_TLSv1_1\n    SSL_CTX_clear_options(ssl->ctx, SSL_OP_NO_TLSv1_1);\n    if (!(protocols & NGX_SSL_TLSv1_1)) {\n        SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_TLSv1_1);\n    }\n#endif\n#ifdef SSL_OP_NO_TLSv1_2\n    SSL_CTX_clear_options(ssl->ctx, SSL_OP_NO_TLSv1_2);\n    if (!(protocols & NGX_SSL_TLSv1_2)) {\n        SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_TLSv1_2);\n    }\n#endif\n#ifdef SSL_OP_NO_TLSv1_3\n    SSL_CTX_clear_options(ssl->ctx, SSL_OP_NO_TLSv1_3);\n    if (!(protocols & NGX_SSL_TLSv1_3)) {\n        SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_TLSv1_3);\n    }\n#endif\n\n#ifdef SSL_CTX_set_min_proto_version\n    SSL_CTX_set_min_proto_version(ssl->ctx, 0);\n    SSL_CTX_set_max_proto_version(ssl->ctx, TLS1_2_VERSION);\n#endif\n\n#ifdef TLS1_3_VERSION\n    SSL_CTX_set_min_proto_version(ssl->ctx, 0);\n    SSL_CTX_set_max_proto_version(ssl->ctx, TLS1_3_VERSION);\n#endif\n\n#ifdef SSL_OP_NO_COMPRESSION\n    SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_COMPRESSION);\n#endif\n\n#ifdef SSL_OP_NO_ANTI_REPLAY\n    SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_ANTI_REPLAY);\n#endif\n\n#ifdef SSL_OP_NO_CLIENT_RENEGOTIATION\n    SSL_CTX_set_options(ssl->ctx, SSL_OP_NO_CLIENT_RENEGOTIATION);\n#endif\n\n#ifdef SSL_OP_IGNORE_UNEXPECTED_EOF\n    SSL_CTX_set_options(ssl->ctx, SSL_OP_IGNORE_UNEXPECTED_EOF);\n#endif\n\n#ifdef SSL_MODE_RELEASE_BUFFERS\n    SSL_CTX_set_mode(ssl->ctx, SSL_MODE_RELEASE_BUFFERS);\n#endif\n\n#ifdef SSL_MODE_NO_AUTO_CHAIN\n    SSL_CTX_set_mode(ssl->ctx, SSL_MODE_NO_AUTO_CHAIN);\n#endif\n\n#if (NGX_SSL && NGX_SSL_ASYNC)\n    if (ssl->async_enable) {\n        SSL_CTX_set_mode(ssl->ctx, SSL_MODE_ASYNC);\n    }\n#endif\n\n    SSL_CTX_set_read_ahead(ssl->ctx, 1);\n\n    SSL_CTX_set_info_callback(ssl->ctx, ngx_ssl_info_callback);\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_ssl_certificates(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_array_t *certs,\n    ngx_array_t *keys, ngx_array_t *passwords)\n{\n    ngx_str_t   *cert, *key;\n    ngx_uint_t   i;\n\n#if (T_NGX_SSL_NTLS)\n    if (certs == NULL || keys == NULL)\n        return NGX_OK;\n#endif\n    cert = certs->elts;\n    key = keys->elts;\n\n    for (i = 0; i < certs->nelts; i++) {\n\n#if (T_NGX_SSL_NTLS)\n        if (ngx_ssl_certificate(cf, ssl, &cert[i], &key[i], passwords,\n                                SSL_NORMAL_CERT)\n#else\n        if (ngx_ssl_certificate(cf, ssl, &cert[i], &key[i], passwords)\n#endif\n            != NGX_OK)\n        {\n            return NGX_ERROR;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\n#if (T_NGX_SSL_NTLS)\nngx_int_t\nngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,\n    ngx_str_t *key, ngx_array_t *passwords, ngx_flag_t cert_tag)\n#else\nngx_int_t\nngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,\n    ngx_str_t *key, ngx_array_t *passwords)\n#endif\n{\n    char            *err;\n    X509            *x509;\n    EVP_PKEY        *pkey;\n    STACK_OF(X509)  *chain;\n\n    x509 = ngx_ssl_load_certificate(cf->pool, &err, cert, &chain);\n    if (x509 == NULL) {\n        if (err != NULL) {\n            ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,\n                          \"cannot load certificate \\\"%s\\\": %s\",\n                          cert->data, err);\n        }\n\n        return NGX_ERROR;\n    }\n\n#if (T_NGX_SSL_NTLS)\n    if (cert_tag == SSL_ENC_CERT) {\n        if (SSL_CTX_use_enc_certificate(ssl->ctx, x509) == 0) {\n            ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,\n                          \"SSL_CTX_use_enc_certificate(\\\"%s\\\") failed\",\n                          cert->data);\n            X509_free(x509);\n            sk_X509_pop_free(chain, X509_free);\n            return NGX_ERROR;\n        }\n    } else if (cert_tag == SSL_SIGN_CERT) {\n        if (SSL_CTX_use_sign_certificate(ssl->ctx, x509) == 0) {\n            ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,\n                          \"SSL_CTX_use_sign_certificate(\\\"%s\\\") failed\",\n                          cert->data);\n            X509_free(x509);\n            sk_X509_pop_free(chain, X509_free);\n            return NGX_ERROR;\n        }\n    } else\n#endif\n    if (SSL_CTX_use_certificate(ssl->ctx, x509) == 0) {\n        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,\n                      \"SSL_CTX_use_certificate(\\\"%s\\\") failed\", cert->data);\n        X509_free(x509);\n        sk_X509_pop_free(chain, X509_free);\n        return NGX_ERROR;\n    }\n\n    if (X509_set_ex_data(x509, ngx_ssl_certificate_name_index, cert->data)\n        == 0)\n    {\n        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, \"X509_set_ex_data() failed\");\n        X509_free(x509);\n        sk_X509_pop_free(chain, X509_free);\n        return NGX_ERROR;\n    }\n\n    if (X509_set_ex_data(x509, ngx_ssl_next_certificate_index,\n                      SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index))\n        == 0)\n    {\n        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, \"X509_set_ex_data() failed\");\n        X509_free(x509);\n        sk_X509_pop_free(chain, X509_free);\n        return NGX_ERROR;\n    }\n\n    if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_certificate_index, x509) == 0) {\n        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,\n                      \"SSL_CTX_set_ex_data() failed\");\n        X509_free(x509);\n        sk_X509_pop_free(chain, X509_free);\n        return NGX_ERROR;\n    }\n\n    /*\n     * Note that x509 is not freed here, but will be instead freed in\n     * ngx_ssl_cleanup_ctx().  This is because we need to preserve all\n     * certificates to be able to iterate all of them through exdata\n     * (ngx_ssl_certificate_index, ngx_ssl_next_certificate_index),\n     * while OpenSSL can free a certificate if it is replaced with another\n     * certificate of the same type.\n     */\n\n#ifdef SSL_CTX_set0_chain\n\n    if (SSL_CTX_set0_chain(ssl->ctx, chain) == 0) {\n        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,\n                      \"SSL_CTX_set0_chain(\\\"%s\\\") failed\", cert->data);\n        sk_X509_pop_free(chain, X509_free);\n        return NGX_ERROR;\n    }\n\n#else\n    {\n    int  n;\n\n    /* SSL_CTX_set0_chain() is only available in OpenSSL 1.0.2+ */\n\n    n = sk_X509_num(chain);\n\n    while (n--) {\n        x509 = sk_X509_shift(chain);\n\n        if (SSL_CTX_add_extra_chain_cert(ssl->ctx, x509) == 0) {\n            ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,\n                          \"SSL_CTX_add_extra_chain_cert(\\\"%s\\\") failed\",\n                          cert->data);\n            sk_X509_pop_free(chain, X509_free);\n            return NGX_ERROR;\n        }\n    }\n\n    sk_X509_free(chain);\n    }\n#endif\n\n    pkey = ngx_ssl_load_certificate_key(cf->pool, &err, key, passwords);\n    if (pkey == NULL) {\n        if (err != NULL) {\n            ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,\n                          \"cannot load certificate key \\\"%s\\\": %s\",\n                          key->data, err);\n        }\n\n        return NGX_ERROR;\n    }\n\n#if (T_NGX_SSL_NTLS)\n    if (cert_tag == SSL_ENC_CERT) {\n        if (SSL_CTX_use_enc_PrivateKey(ssl->ctx, pkey) == 0) {\n            ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,\n                          \"SSL_CTX_use_enc_PrivateKey(\\\"%s\\\") failed\",\n                          key->data);\n            EVP_PKEY_free(pkey);\n            return NGX_ERROR;\n        }\n    } else if (cert_tag == SSL_SIGN_CERT) {\n        if (SSL_CTX_use_sign_PrivateKey(ssl->ctx, pkey) == 0) {\n            ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,\n                          \"SSL_CTX_use_sign_PrivateKey(\\\"%s\\\") failed\",\n                          key->data);\n            EVP_PKEY_free(pkey);\n            return NGX_ERROR;\n        }\n    } else\n#endif\n    if (SSL_CTX_use_PrivateKey(ssl->ctx, pkey) == 0) {\n        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,\n                      \"SSL_CTX_use_PrivateKey(\\\"%s\\\") failed\", key->data);\n        EVP_PKEY_free(pkey);\n        return NGX_ERROR;\n    }\n\n    EVP_PKEY_free(pkey);\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_ssl_connection_certificate(ngx_connection_t *c, ngx_pool_t *pool,\n    ngx_str_t *cert, ngx_str_t *key, ngx_array_t *passwords)\n{\n    char            *err;\n    X509            *x509;\n    EVP_PKEY        *pkey;\n    STACK_OF(X509)  *chain;\n\n    x509 = ngx_ssl_load_certificate(pool, &err, cert, &chain);\n    if (x509 == NULL) {\n        if (err != NULL) {\n            ngx_ssl_error(NGX_LOG_ERR, c->log, 0,\n                          \"cannot load certificate \\\"%s\\\": %s\",\n                          cert->data, err);\n        }\n\n        return NGX_ERROR;\n    }\n\n    if (SSL_use_certificate(c->ssl->connection, x509) == 0) {\n        ngx_ssl_error(NGX_LOG_ERR, c->log, 0,\n                      \"SSL_use_certificate(\\\"%s\\\") failed\", cert->data);\n        X509_free(x509);\n        sk_X509_pop_free(chain, X509_free);\n        return NGX_ERROR;\n    }\n\n    X509_free(x509);\n\n#ifdef SSL_set0_chain\n\n    /*\n     * SSL_set0_chain() is only available in OpenSSL 1.0.2+,\n     * but this function is only called via certificate callback,\n     * which is only available in OpenSSL 1.0.2+ as well\n     */\n\n    if (SSL_set0_chain(c->ssl->connection, chain) == 0) {\n        ngx_ssl_error(NGX_LOG_ERR, c->log, 0,\n                      \"SSL_set0_chain(\\\"%s\\\") failed\", cert->data);\n        sk_X509_pop_free(chain, X509_free);\n        return NGX_ERROR;\n    }\n\n#endif\n\n    pkey = ngx_ssl_load_certificate_key(pool, &err, key, passwords);\n    if (pkey == NULL) {\n        if (err != NULL) {\n            ngx_ssl_error(NGX_LOG_ERR, c->log, 0,\n                          \"cannot load certificate key \\\"%s\\\": %s\",\n                          key->data, err);\n        }\n\n        return NGX_ERROR;\n    }\n\n    if (SSL_use_PrivateKey(c->ssl->connection, pkey) == 0) {\n        ngx_ssl_error(NGX_LOG_ERR, c->log, 0,\n                      \"SSL_use_PrivateKey(\\\"%s\\\") failed\", key->data);\n        EVP_PKEY_free(pkey);\n        return NGX_ERROR;\n    }\n\n    EVP_PKEY_free(pkey);\n\n    return NGX_OK;\n}\n\n\nstatic X509 *\nngx_ssl_load_certificate(ngx_pool_t *pool, char **err, ngx_str_t *cert,\n    STACK_OF(X509) **chain)\n{\n    BIO     *bio;\n    X509    *x509, *temp;\n    u_long   n;\n\n    if (ngx_strncmp(cert->data, \"data:\", sizeof(\"data:\") - 1) == 0) {\n\n        bio = BIO_new_mem_buf(cert->data + sizeof(\"data:\") - 1,\n                              cert->len - (sizeof(\"data:\") - 1));\n        if (bio == NULL) {\n            *err = \"BIO_new_mem_buf() failed\";\n            return NULL;\n        }\n\n    } else {\n\n        if (ngx_get_full_name(pool, (ngx_str_t *) &ngx_cycle->conf_prefix, cert)\n            != NGX_OK)\n        {\n            *err = NULL;\n            return NULL;\n        }\n\n        bio = BIO_new_file((char *) cert->data, \"r\");\n        if (bio == NULL) {\n            *err = \"BIO_new_file() failed\";\n            return NULL;\n        }\n    }\n\n    /* certificate itself */\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        return NULL;\n    }\n\n    /* rest of the chain */\n\n    *chain = sk_X509_new_null();\n    if (*chain == NULL) {\n        *err = \"sk_X509_new_null() failed\";\n        BIO_free(bio);\n        X509_free(x509);\n        return NULL;\n    }\n\n    for ( ;; ) {\n\n        temp = PEM_read_bio_X509(bio, NULL, NULL, NULL);\n        if (temp == 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            X509_free(x509);\n            sk_X509_pop_free(*chain, X509_free);\n            return NULL;\n        }\n\n        if (sk_X509_push(*chain, temp) == 0) {\n            *err = \"sk_X509_push() failed\";\n            BIO_free(bio);\n            X509_free(x509);\n            sk_X509_pop_free(*chain, X509_free);\n            return NULL;\n        }\n    }\n\n    BIO_free(bio);\n\n    return x509;\n}\n\n\nstatic EVP_PKEY *\nngx_ssl_load_certificate_key(ngx_pool_t *pool, char **err,\n    ngx_str_t *key, ngx_array_t *passwords)\n{\n    BIO              *bio;\n    EVP_PKEY         *pkey;\n    ngx_str_t        *pwd;\n    ngx_uint_t        tries;\n    pem_password_cb  *cb;\n\n    if (ngx_strncmp(key->data, \"engine:\", sizeof(\"engine:\") - 1) == 0) {\n\n#ifndef OPENSSL_NO_ENGINE\n\n        u_char  *p, *last;\n        ENGINE  *engine;\n\n        p = key->data + sizeof(\"engine:\") - 1;\n        last = (u_char *) ngx_strchr(p, ':');\n\n        if (last == NULL) {\n            *err = \"invalid syntax\";\n            return NULL;\n        }\n\n        *last = '\\0';\n\n        engine = ENGINE_by_id((char *) p);\n\n        if (engine == NULL) {\n            *err = \"ENGINE_by_id() failed\";\n            return NULL;\n        }\n\n        *last++ = ':';\n\n        pkey = ENGINE_load_private_key(engine, (char *) last, 0, 0);\n\n        if (pkey == NULL) {\n            *err = \"ENGINE_load_private_key() failed\";\n            ENGINE_free(engine);\n            return NULL;\n        }\n\n        ENGINE_free(engine);\n\n        return pkey;\n\n#else\n\n        *err = \"loading \\\"engine:...\\\" certificate keys is not supported\";\n        return NULL;\n\n#endif\n    }\n\n    if (ngx_strncmp(key->data, \"data:\", sizeof(\"data:\") - 1) == 0) {\n\n        bio = BIO_new_mem_buf(key->data + sizeof(\"data:\") - 1,\n                              key->len - (sizeof(\"data:\") - 1));\n        if (bio == NULL) {\n            *err = \"BIO_new_mem_buf() failed\";\n            return NULL;\n        }\n\n    } else {\n\n        if (ngx_get_full_name(pool, (ngx_str_t *) &ngx_cycle->conf_prefix, key)\n            != NGX_OK)\n        {\n            *err = NULL;\n            return NULL;\n        }\n\n        bio = BIO_new_file((char *) key->data, \"r\");\n        if (bio == NULL) {\n            *err = \"BIO_new_file() failed\";\n            return NULL;\n        }\n    }\n\n    if (passwords) {\n        tries = passwords->nelts;\n        pwd = passwords->elts;\n        cb = ngx_ssl_password_callback;\n\n    } else {\n        tries = 1;\n        pwd = NULL;\n        cb = NULL;\n    }\n\n    for ( ;; ) {\n\n        pkey = PEM_read_bio_PrivateKey(bio, NULL, cb, pwd);\n        if (pkey != NULL) {\n            break;\n        }\n\n        if (tries-- > 1) {\n            ERR_clear_error();\n            (void) BIO_reset(bio);\n            pwd++;\n            continue;\n        }\n\n        *err = \"PEM_read_bio_PrivateKey() failed\";\n        BIO_free(bio);\n        return NULL;\n    }\n\n    BIO_free(bio);\n\n    return pkey;\n}\n\n\nstatic int\nngx_ssl_password_callback(char *buf, int size, int rwflag, void *userdata)\n{\n    ngx_str_t *pwd = userdata;\n\n    if (rwflag) {\n        ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,\n                      \"ngx_ssl_password_callback() is called for encryption\");\n        return 0;\n    }\n\n    if (pwd == NULL) {\n        return 0;\n    }\n\n    if (pwd->len > (size_t) size) {\n        ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0,\n                      \"password is truncated to %d bytes\", size);\n    } else {\n        size = pwd->len;\n    }\n\n    ngx_memcpy(buf, pwd->data, size);\n\n    return size;\n}\n\n\nngx_int_t\nngx_ssl_ciphers(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *ciphers,\n    ngx_uint_t prefer_server_ciphers)\n{\n    if (SSL_CTX_set_cipher_list(ssl->ctx, (char *) ciphers->data) == 0) {\n        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,\n                      \"SSL_CTX_set_cipher_list(\\\"%V\\\") failed\",\n                      ciphers);\n        return NGX_ERROR;\n    }\n\n    if (prefer_server_ciphers) {\n        SSL_CTX_set_options(ssl->ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_ssl_client_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,\n    ngx_int_t depth)\n{\n    STACK_OF(X509_NAME)  *list;\n\n    SSL_CTX_set_verify(ssl->ctx, SSL_VERIFY_PEER, ngx_ssl_verify_callback);\n\n    SSL_CTX_set_verify_depth(ssl->ctx, depth);\n\n    if (cert->len == 0) {\n        return NGX_OK;\n    }\n\n    if (ngx_conf_full_name(cf->cycle, cert, 1) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    if (SSL_CTX_load_verify_locations(ssl->ctx, (char *) cert->data, NULL)\n        == 0)\n    {\n        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,\n                      \"SSL_CTX_load_verify_locations(\\\"%s\\\") failed\",\n                      cert->data);\n        return NGX_ERROR;\n    }\n\n    /*\n     * SSL_CTX_load_verify_locations() may leave errors in the error queue\n     * while returning success\n     */\n\n    ERR_clear_error();\n\n    list = SSL_load_client_CA_file((char *) cert->data);\n\n    if (list == NULL) {\n        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,\n                      \"SSL_load_client_CA_file(\\\"%s\\\") failed\", cert->data);\n        return NGX_ERROR;\n    }\n\n    SSL_CTX_set_client_CA_list(ssl->ctx, list);\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_ssl_trusted_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *cert,\n    ngx_int_t depth)\n{\n    SSL_CTX_set_verify(ssl->ctx, SSL_CTX_get_verify_mode(ssl->ctx),\n                       ngx_ssl_verify_callback);\n\n    SSL_CTX_set_verify_depth(ssl->ctx, depth);\n\n    if (cert->len == 0) {\n        return NGX_OK;\n    }\n\n    if (ngx_conf_full_name(cf->cycle, cert, 1) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    if (SSL_CTX_load_verify_locations(ssl->ctx, (char *) cert->data, NULL)\n        == 0)\n    {\n        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,\n                      \"SSL_CTX_load_verify_locations(\\\"%s\\\") failed\",\n                      cert->data);\n        return NGX_ERROR;\n    }\n\n    /*\n     * SSL_CTX_load_verify_locations() may leave errors in the error queue\n     * while returning success\n     */\n\n    ERR_clear_error();\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_ssl_crl(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *crl)\n{\n    X509_STORE   *store;\n    X509_LOOKUP  *lookup;\n\n    if (crl->len == 0) {\n        return NGX_OK;\n    }\n\n    if (ngx_conf_full_name(cf->cycle, crl, 1) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    store = SSL_CTX_get_cert_store(ssl->ctx);\n\n    if (store == NULL) {\n        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,\n                      \"SSL_CTX_get_cert_store() failed\");\n        return NGX_ERROR;\n    }\n\n    lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file());\n\n    if (lookup == NULL) {\n        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,\n                      \"X509_STORE_add_lookup() failed\");\n        return NGX_ERROR;\n    }\n\n    if (X509_LOOKUP_load_file(lookup, (char *) crl->data, X509_FILETYPE_PEM)\n        == 0)\n    {\n        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,\n                      \"X509_LOOKUP_load_file(\\\"%s\\\") failed\", crl->data);\n        return NGX_ERROR;\n    }\n\n    X509_STORE_set_flags(store,\n                         X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL);\n\n    return NGX_OK;\n}\n\n\nstatic int\nngx_ssl_verify_callback(int ok, X509_STORE_CTX *x509_store)\n{\n#if (NGX_DEBUG)\n    char              *subject, *issuer;\n    int                err, depth;\n    X509              *cert;\n    X509_NAME         *sname, *iname;\n    ngx_connection_t  *c;\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);\n\n    if (!(c->log->log_level & NGX_LOG_DEBUG_EVENT)) {\n        return 1;\n    }\n\n    cert = X509_STORE_CTX_get_current_cert(x509_store);\n    err = X509_STORE_CTX_get_error(x509_store);\n    depth = X509_STORE_CTX_get_error_depth(x509_store);\n\n    sname = X509_get_subject_name(cert);\n\n    if (sname) {\n        subject = X509_NAME_oneline(sname, NULL, 0);\n        if (subject == NULL) {\n            ngx_ssl_error(NGX_LOG_ALERT, c->log, 0,\n                          \"X509_NAME_oneline() failed\");\n        }\n\n    } else {\n        subject = NULL;\n    }\n\n    iname = X509_get_issuer_name(cert);\n\n    if (iname) {\n        issuer = X509_NAME_oneline(iname, NULL, 0);\n        if (issuer == NULL) {\n            ngx_ssl_error(NGX_LOG_ALERT, c->log, 0,\n                          \"X509_NAME_oneline() failed\");\n        }\n\n    } else {\n        issuer = NULL;\n    }\n\n    ngx_log_debug5(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"verify:%d, error:%d, depth:%d, \"\n                   \"subject:\\\"%s\\\", issuer:\\\"%s\\\"\",\n                   ok, err, depth,\n                   subject ? subject : \"(none)\",\n                   issuer ? issuer : \"(none)\");\n\n    if (subject) {\n        OPENSSL_free(subject);\n    }\n\n    if (issuer) {\n        OPENSSL_free(issuer);\n    }\n#endif\n\n    return 1;\n}\n\n\nstatic void\nngx_ssl_info_callback(const ngx_ssl_conn_t *ssl_conn, int where, int ret)\n{\n    BIO               *rbio, *wbio;\n    ngx_connection_t  *c;\n\n#ifndef SSL_OP_NO_RENEGOTIATION\n\n    if ((where & SSL_CB_HANDSHAKE_START)\n        && SSL_is_server((ngx_ssl_conn_t *) ssl_conn))\n    {\n        c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);\n\n        if (c->ssl->handshaked) {\n            c->ssl->renegotiation = 1;\n            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, \"SSL renegotiation\");\n        }\n    }\n\n#endif\n\n#ifdef TLS1_3_VERSION\n\n    if ((where & SSL_CB_ACCEPT_LOOP) == SSL_CB_ACCEPT_LOOP\n        && SSL_version(ssl_conn) == TLS1_3_VERSION)\n    {\n        time_t        now, time, timeout, conf_timeout;\n        SSL_SESSION  *sess;\n\n        /*\n         * OpenSSL with TLSv1.3 updates the session creation time on\n         * session resumption and keeps the session timeout unmodified,\n         * making it possible to maintain the session forever, bypassing\n         * client certificate expiration and revocation.  To make sure\n         * session timeouts are actually used, we now update the session\n         * creation time and reduce the session timeout accordingly.\n         *\n         * BoringSSL with TLSv1.3 ignores configured session timeouts\n         * and uses a hardcoded timeout instead, 7 days.  So we update\n         * session timeout to the configured value as soon as a session\n         * is created.\n         */\n\n        c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);\n        sess = SSL_get0_session(ssl_conn);\n\n        if (!c->ssl->session_timeout_set && sess) {\n            c->ssl->session_timeout_set = 1;\n\n            now = ngx_time();\n            time = SSL_SESSION_get_time(sess);\n            timeout = SSL_SESSION_get_timeout(sess);\n            conf_timeout = SSL_CTX_get_timeout(c->ssl->session_ctx);\n\n            timeout = ngx_min(timeout, conf_timeout);\n\n            if (now - time >= timeout) {\n                SSL_SESSION_set1_id_context(sess, (unsigned char *) \"\", 0);\n\n            } else {\n                SSL_SESSION_set_time(sess, now);\n                SSL_SESSION_set_timeout(sess, timeout - (now - time));\n            }\n        }\n    }\n\n#endif\n\n    if ((where & SSL_CB_ACCEPT_LOOP) == SSL_CB_ACCEPT_LOOP) {\n        c = ngx_ssl_get_connection((ngx_ssl_conn_t *) ssl_conn);\n\n        if (!c->ssl->handshake_buffer_set) {\n            /*\n             * By default OpenSSL uses 4k buffer during a handshake,\n             * which is too low for long certificate chains and might\n             * result in extra round-trips.\n             *\n             * To adjust a buffer size we detect that buffering was added\n             * to write side of the connection by comparing rbio and wbio.\n             * If they are different, we assume that it's due to buffering\n             * added to wbio, and set buffer size.\n             */\n\n            rbio = SSL_get_rbio(ssl_conn);\n            wbio = SSL_get_wbio(ssl_conn);\n\n            if (rbio != wbio) {\n                (void) BIO_set_write_buffer_size(wbio, NGX_SSL_BUFSIZE);\n                c->ssl->handshake_buffer_set = 1;\n            }\n        }\n    }\n}\n\n\nngx_array_t *\nngx_ssl_read_password_file(ngx_conf_t *cf, ngx_str_t *file)\n{\n    u_char              *p, *last, *end;\n    size_t               len;\n    ssize_t              n;\n    ngx_fd_t             fd;\n    ngx_str_t           *pwd;\n    ngx_array_t         *passwords;\n    ngx_pool_cleanup_t  *cln;\n    u_char               buf[NGX_SSL_PASSWORD_BUFFER_SIZE];\n\n    if (ngx_conf_full_name(cf->cycle, file, 1) != NGX_OK) {\n        return NULL;\n    }\n\n    passwords = ngx_array_create(cf->temp_pool, 4, sizeof(ngx_str_t));\n    if (passwords == NULL) {\n        return NULL;\n    }\n\n    cln = ngx_pool_cleanup_add(cf->temp_pool, 0);\n    if (cln == NULL) {\n        return NULL;\n    }\n\n    cln->handler = ngx_ssl_passwords_cleanup;\n    cln->data = passwords;\n\n    fd = ngx_open_file(file->data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);\n\n    if (fd == NGX_INVALID_FILE) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno,\n                           ngx_open_file_n \" \\\"%s\\\" failed\", file->data);\n        return NULL;\n    }\n\n    len = 0;\n    last = buf;\n\n    do {\n        n = ngx_read_fd(fd, last, NGX_SSL_PASSWORD_BUFFER_SIZE - len);\n\n        if (n == -1) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno,\n                               ngx_read_fd_n \" \\\"%s\\\" failed\", file->data);\n            passwords = NULL;\n            goto cleanup;\n        }\n\n        end = last + n;\n\n        if (len && n == 0) {\n            *end++ = LF;\n        }\n\n        p = buf;\n\n        for ( ;; ) {\n            last = ngx_strlchr(last, end, LF);\n\n            if (last == NULL) {\n                break;\n            }\n\n            len = last++ - p;\n\n            if (len && p[len - 1] == CR) {\n                len--;\n            }\n\n            if (len) {\n                pwd = ngx_array_push(passwords);\n                if (pwd == NULL) {\n                    passwords = NULL;\n                    goto cleanup;\n                }\n\n                pwd->len = len;\n                pwd->data = ngx_pnalloc(cf->temp_pool, len);\n\n                if (pwd->data == NULL) {\n                    passwords->nelts--;\n                    passwords = NULL;\n                    goto cleanup;\n                }\n\n                ngx_memcpy(pwd->data, p, len);\n            }\n\n            p = last;\n        }\n\n        len = end - p;\n\n        if (len == NGX_SSL_PASSWORD_BUFFER_SIZE) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"too long line in \\\"%s\\\"\", file->data);\n            passwords = NULL;\n            goto cleanup;\n        }\n\n        ngx_memmove(buf, p, len);\n        last = buf + len;\n\n    } while (n != 0);\n\n    if (passwords->nelts == 0) {\n        pwd = ngx_array_push(passwords);\n        if (pwd == NULL) {\n            passwords = NULL;\n            goto cleanup;\n        }\n\n        ngx_memzero(pwd, sizeof(ngx_str_t));\n    }\n\ncleanup:\n\n    if (ngx_close_file(fd) == NGX_FILE_ERROR) {\n        ngx_conf_log_error(NGX_LOG_ALERT, cf, ngx_errno,\n                           ngx_close_file_n \" \\\"%s\\\" failed\", file->data);\n    }\n\n    ngx_explicit_memzero(buf, NGX_SSL_PASSWORD_BUFFER_SIZE);\n\n    return passwords;\n}\n\n\nngx_array_t *\nngx_ssl_preserve_passwords(ngx_conf_t *cf, ngx_array_t *passwords)\n{\n    ngx_str_t           *opwd, *pwd;\n    ngx_uint_t           i;\n    ngx_array_t         *pwds;\n    ngx_pool_cleanup_t  *cln;\n    static ngx_array_t   empty_passwords;\n\n    if (passwords == NULL) {\n\n        /*\n         * If there are no passwords, an empty array is used\n         * to make sure OpenSSL's default password callback\n         * won't block on reading from stdin.\n         */\n\n        return &empty_passwords;\n    }\n\n    /*\n     * Passwords are normally allocated from the temporary pool\n     * and cleared after parsing configuration.  To be used at\n     * runtime they have to be copied to the configuration pool.\n     */\n\n    pwds = ngx_array_create(cf->pool, passwords->nelts, sizeof(ngx_str_t));\n    if (pwds == NULL) {\n        return NULL;\n    }\n\n    cln = ngx_pool_cleanup_add(cf->pool, 0);\n    if (cln == NULL) {\n        return NULL;\n    }\n\n    cln->handler = ngx_ssl_passwords_cleanup;\n    cln->data = pwds;\n\n    opwd = passwords->elts;\n\n    for (i = 0; i < passwords->nelts; i++) {\n\n        pwd = ngx_array_push(pwds);\n        if (pwd == NULL) {\n            return NULL;\n        }\n\n        pwd->len = opwd[i].len;\n        pwd->data = ngx_pnalloc(cf->pool, pwd->len);\n\n        if (pwd->data == NULL) {\n            pwds->nelts--;\n            return NULL;\n        }\n\n        ngx_memcpy(pwd->data, opwd[i].data, opwd[i].len);\n    }\n\n    return pwds;\n}\n\n\nstatic void\nngx_ssl_passwords_cleanup(void *data)\n{\n    ngx_array_t *passwords = data;\n\n    ngx_str_t   *pwd;\n    ngx_uint_t   i;\n\n    pwd = passwords->elts;\n\n    for (i = 0; i < passwords->nelts; i++) {\n        ngx_explicit_memzero(pwd[i].data, pwd[i].len);\n    }\n}\n\n\nngx_int_t\nngx_ssl_dhparam(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file)\n{\n    BIO  *bio;\n\n    if (file->len == 0) {\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    bio = BIO_new_file((char *) file->data, \"r\");\n    if (bio == NULL) {\n        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,\n                      \"BIO_new_file(\\\"%s\\\") failed\", file->data);\n        return NGX_ERROR;\n    }\n\n#ifdef SSL_CTX_set_tmp_dh\n    {\n    DH  *dh;\n\n    dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);\n    if (dh == NULL) {\n        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,\n                      \"PEM_read_bio_DHparams(\\\"%s\\\") failed\", file->data);\n        BIO_free(bio);\n        return NGX_ERROR;\n    }\n\n    if (SSL_CTX_set_tmp_dh(ssl->ctx, dh) != 1) {\n        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,\n                      \"SSL_CTX_set_tmp_dh(\\\"%s\\\") failed\", file->data);\n        DH_free(dh);\n        BIO_free(bio);\n        return NGX_ERROR;\n    }\n\n    DH_free(dh);\n    }\n#else\n    {\n    EVP_PKEY  *dh;\n\n    /*\n     * PEM_read_bio_DHparams() and SSL_CTX_set_tmp_dh()\n     * are deprecated in OpenSSL 3.0\n     */\n\n    dh = PEM_read_bio_Parameters(bio, NULL);\n    if (dh == NULL) {\n        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,\n                      \"PEM_read_bio_Parameters(\\\"%s\\\") failed\", file->data);\n        BIO_free(bio);\n        return NGX_ERROR;\n    }\n\n    if (SSL_CTX_set0_tmp_dh_pkey(ssl->ctx, dh) != 1) {\n        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,\n                      \"SSL_CTX_set0_tmp_dh_pkey(\\%s\\\") failed\", file->data);\n#if (OPENSSL_VERSION_NUMBER >= 0x3000001fL)\n        EVP_PKEY_free(dh);\n#endif\n        BIO_free(bio);\n        return NGX_ERROR;\n    }\n    }\n#endif\n\n    BIO_free(bio);\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_ssl_ecdh_curve(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *name)\n{\n#ifndef OPENSSL_NO_ECDH\n\n    /*\n     * Elliptic-Curve Diffie-Hellman parameters are either \"named curves\"\n     * from RFC 4492 section 5.1.1, or explicitly described curves over\n     * binary fields.  OpenSSL only supports the \"named curves\", which provide\n     * maximum interoperability.\n     */\n\n#if (defined SSL_CTX_set1_curves_list || defined SSL_CTRL_SET_CURVES_LIST)\n\n    /*\n     * OpenSSL 1.0.2+ allows configuring a curve list instead of a single\n     * curve previously supported.  By default an internal list is used,\n     * with prime256v1 being preferred by server in OpenSSL 1.0.2b+\n     * and X25519 in OpenSSL 1.1.0+.\n     *\n     * By default a curve preferred by the client will be used for\n     * key exchange.  The SSL_OP_CIPHER_SERVER_PREFERENCE option can\n     * be used to prefer server curves instead, similar to what it\n     * does for ciphers.\n     */\n\n    SSL_CTX_set_options(ssl->ctx, SSL_OP_SINGLE_ECDH_USE);\n\n#ifdef SSL_CTRL_SET_ECDH_AUTO\n    /* not needed in OpenSSL 1.1.0+ */\n    (void) SSL_CTX_set_ecdh_auto(ssl->ctx, 1);\n#endif\n\n    if (ngx_strcmp(name->data, \"auto\") == 0) {\n        return NGX_OK;\n    }\n\n    if (SSL_CTX_set1_curves_list(ssl->ctx, (char *) name->data) == 0) {\n        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,\n                      \"SSL_CTX_set1_curves_list(\\\"%s\\\") failed\", name->data);\n        return NGX_ERROR;\n    }\n\n#else\n\n    int      nid;\n    char    *curve;\n    EC_KEY  *ecdh;\n\n    if (ngx_strcmp(name->data, \"auto\") == 0) {\n        curve = \"prime256v1\";\n\n    } else {\n        curve = (char *) name->data;\n    }\n\n    nid = OBJ_sn2nid(curve);\n    if (nid == 0) {\n        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,\n                      \"OBJ_sn2nid(\\\"%s\\\") failed: unknown curve\", curve);\n        return NGX_ERROR;\n    }\n\n    ecdh = EC_KEY_new_by_curve_name(nid);\n    if (ecdh == NULL) {\n        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,\n                      \"EC_KEY_new_by_curve_name(\\\"%s\\\") failed\", curve);\n        return NGX_ERROR;\n    }\n\n    SSL_CTX_set_options(ssl->ctx, SSL_OP_SINGLE_ECDH_USE);\n\n    SSL_CTX_set_tmp_ecdh(ssl->ctx, ecdh);\n\n    EC_KEY_free(ecdh);\n#endif\n#endif\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_ssl_early_data(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_uint_t enable)\n{\n    if (!enable) {\n        return NGX_OK;\n    }\n\n#ifdef SSL_ERROR_EARLY_DATA_REJECTED\n\n    /* BoringSSL */\n\n    SSL_CTX_set_early_data_enabled(ssl->ctx, 1);\n\n#elif defined SSL_READ_EARLY_DATA_SUCCESS\n\n    /* OpenSSL */\n\n    SSL_CTX_set_max_early_data(ssl->ctx, NGX_SSL_BUFSIZE);\n\n#else\n    ngx_log_error(NGX_LOG_WARN, ssl->log, 0,\n                  \"\\\"ssl_early_data\\\" is not supported on this platform, \"\n                  \"ignored\");\n#endif\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_ssl_conf_commands(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_array_t *commands)\n{\n    if (commands == NULL) {\n        return NGX_OK;\n    }\n\n#ifdef SSL_CONF_FLAG_FILE\n    {\n    int            type;\n    u_char        *key, *value;\n    ngx_uint_t     i;\n    ngx_keyval_t  *cmd;\n    SSL_CONF_CTX  *cctx;\n\n    cctx = SSL_CONF_CTX_new();\n    if (cctx == NULL) {\n        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,\n                      \"SSL_CONF_CTX_new() failed\");\n        return NGX_ERROR;\n    }\n\n    SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_FILE);\n    SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_SERVER);\n    SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_CLIENT);\n    SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_CERTIFICATE);\n    SSL_CONF_CTX_set_flags(cctx, SSL_CONF_FLAG_SHOW_ERRORS);\n\n    SSL_CONF_CTX_set_ssl_ctx(cctx, ssl->ctx);\n\n    cmd = commands->elts;\n    for (i = 0; i < commands->nelts; i++) {\n\n        key = cmd[i].key.data;\n        type = SSL_CONF_cmd_value_type(cctx, (char *) key);\n\n        if (type == SSL_CONF_TYPE_FILE || type == SSL_CONF_TYPE_DIR) {\n            if (ngx_conf_full_name(cf->cycle, &cmd[i].value, 1) != NGX_OK) {\n                SSL_CONF_CTX_free(cctx);\n                return NGX_ERROR;\n            }\n        }\n\n        value = cmd[i].value.data;\n\n        if (SSL_CONF_cmd(cctx, (char *) key, (char *) value) <= 0) {\n            ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,\n                          \"SSL_CONF_cmd(\\\"%s\\\", \\\"%s\\\") failed\", key, value);\n            SSL_CONF_CTX_free(cctx);\n            return NGX_ERROR;\n        }\n    }\n\n    if (SSL_CONF_CTX_finish(cctx) != 1) {\n        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,\n                      \"SSL_CONF_finish() failed\");\n        SSL_CONF_CTX_free(cctx);\n        return NGX_ERROR;\n    }\n\n    SSL_CONF_CTX_free(cctx);\n\n    return NGX_OK;\n    }\n#else\n    ngx_log_error(NGX_LOG_EMERG, ssl->log, 0,\n                  \"SSL_CONF_cmd() is not available on this platform\");\n    return NGX_ERROR;\n#endif\n}\n\n\nngx_int_t\nngx_ssl_client_session_cache(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_uint_t enable)\n{\n    if (!enable) {\n        return NGX_OK;\n    }\n\n    SSL_CTX_set_session_cache_mode(ssl->ctx,\n                                   SSL_SESS_CACHE_CLIENT\n                                   |SSL_SESS_CACHE_NO_INTERNAL);\n\n    SSL_CTX_sess_set_new_cb(ssl->ctx, ngx_ssl_new_client_session);\n\n    return NGX_OK;\n}\n\n\nstatic int\nngx_ssl_new_client_session(ngx_ssl_conn_t *ssl_conn, ngx_ssl_session_t *sess)\n{\n    ngx_connection_t  *c;\n\n    c = ngx_ssl_get_connection(ssl_conn);\n\n    if (c->ssl->save_session) {\n        c->ssl->session = sess;\n\n        c->ssl->save_session(c);\n\n        c->ssl->session = NULL;\n    }\n\n    return 0;\n}\n\n\nngx_int_t\nngx_ssl_create_connection(ngx_ssl_t *ssl, ngx_connection_t *c, ngx_uint_t flags)\n{\n    ngx_ssl_connection_t  *sc;\n\n    sc = ngx_pcalloc(c->pool, sizeof(ngx_ssl_connection_t));\n    if (sc == NULL) {\n        return NGX_ERROR;\n    }\n\n    sc->buffer = ((flags & NGX_SSL_BUFFER) != 0);\n    sc->buffer_size = ssl->buffer_size;\n\n    sc->session_ctx = ssl->ctx;\n\n#ifdef SSL_READ_EARLY_DATA_SUCCESS\n    if (SSL_CTX_get_max_early_data(ssl->ctx)) {\n        sc->try_early_data = 1;\n    }\n#endif\n\n    sc->connection = SSL_new(ssl->ctx);\n\n    if (sc->connection == NULL) {\n        ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, \"SSL_new() failed\");\n        return NGX_ERROR;\n    }\n\n    if (SSL_set_fd(sc->connection, c->fd) == 0) {\n        ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, \"SSL_set_fd() failed\");\n        return NGX_ERROR;\n    }\n\n    if (flags & NGX_SSL_CLIENT) {\n        SSL_set_connect_state(sc->connection);\n\n    } else {\n        SSL_set_accept_state(sc->connection);\n\n#ifdef SSL_OP_NO_RENEGOTIATION\n        SSL_set_options(sc->connection, SSL_OP_NO_RENEGOTIATION);\n#endif\n    }\n\n    if (SSL_set_ex_data(sc->connection, ngx_ssl_connection_index, c) == 0) {\n        ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, \"SSL_set_ex_data() failed\");\n        return NGX_ERROR;\n    }\n\n    c->ssl = sc;\n#if (NGX_SSL && NGX_SSL_ASYNC)\n    c->async_enable = ssl->async_enable;\n#endif\n\n    return NGX_OK;\n}\n\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\n\nngx_ssl_session_t *\nngx_ssl_get0_session(ngx_connection_t *c)\n{\n    if (c->ssl->session) {\n        return c->ssl->session;\n    }\n\n    return SSL_get0_session(c->ssl->connection);\n}\n\n\nngx_int_t\nngx_ssl_set_session(ngx_connection_t *c, ngx_ssl_session_t *session)\n{\n    if (session) {\n        if (SSL_set_session(c->ssl->connection, session) == 0) {\n            ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, \"SSL_set_session() failed\");\n            return NGX_ERROR;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_ssl_handshake(ngx_connection_t *c)\n{\n    int        n, sslerr;\n    ngx_err_t  err;\n    ngx_int_t  rc;\n\n#ifdef SSL_READ_EARLY_DATA_SUCCESS\n    if (c->ssl->try_early_data) {\n        return ngx_ssl_try_early_data(c);\n    }\n#endif\n\n#if (T_NGX_HAVE_DTLS)\n    struct timeval timeout;\n\n    if (c->type == SOCK_DGRAM\n        && !c->ssl->client\n        && !c->ssl->bio_changed)\n    {\n        rc = ngx_dtls_handshake(c);\n        if (rc != NGX_OK) {\n            return rc;\n        }\n    }\n#endif\n\n    if (c->ssl->in_ocsp) {\n        return ngx_ssl_ocsp_validate(c);\n    }\n\n    ngx_ssl_clear_error(c->log);\n\n    n = SSL_do_handshake(c->ssl->connection);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, \"SSL_do_handshake: %d\", n);\n\n    if (n == 1) {\n\n#if (NGX_SSL && NGX_SSL_ASYNC)\n        if (c->async_enable && ngx_ssl_async_process_fds(c) == NGX_ERROR) {\n            return NGX_ERROR;\n        }\n#endif\n\n        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        if (ngx_handle_write_event(c->write, 0) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n#if (T_NGX_HAVE_DTLS)\n        if (c->ssl->retrans &&\n            c->ssl->retrans->timer_set) {\n            ngx_del_timer(c->ssl->retrans);\n        }\n#endif\n\n#if (NGX_DEBUG)\n        ngx_ssl_handshake_log(c);\n#endif\n#if (T_NGX_SSL_HANDSHAKE_TIME)\n        ngx_time_t *tp;\n        tp = ngx_timeofday();\n        c->ssl->handshake_end_msec = tp->sec * 1000 + tp->msec;\n#endif\n\n        c->recv = ngx_ssl_recv;\n        c->send = ngx_ssl_write;\n        c->recv_chain = ngx_ssl_recv_chain;\n        c->send_chain = ngx_ssl_send_chain;\n\n        c->read->ready = 1;\n        c->write->ready = 1;\n\n#ifndef SSL_OP_NO_RENEGOTIATION\n#if OPENSSL_VERSION_NUMBER < 0x10100000L\n#ifdef SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS\n\n        /* initial handshake done, disable renegotiation (CVE-2009-3555) */\n        if (c->ssl->connection->s3 && SSL_is_server(c->ssl->connection)) {\n            c->ssl->connection->s3->flags |= SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS;\n        }\n\n#endif\n#endif\n#endif\n\n#if (defined BIO_get_ktls_send && !NGX_WIN32)\n\n        if (BIO_get_ktls_send(SSL_get_wbio(c->ssl->connection)) == 1) {\n            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                           \"BIO_get_ktls_send(): 1\");\n            c->ssl->sendfile = 1;\n        }\n\n#endif\n\n        rc = ngx_ssl_ocsp_validate(c);\n\n        if (rc == NGX_ERROR) {\n            return NGX_ERROR;\n        }\n\n        if (rc == NGX_AGAIN) {\n            c->read->handler = ngx_ssl_handshake_handler;\n            c->write->handler = ngx_ssl_handshake_handler;\n            return NGX_AGAIN;\n        }\n\n        c->ssl->handshaked = 1;\n\n        return NGX_OK;\n    }\n\n    sslerr = SSL_get_error(c->ssl->connection, n);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, \"SSL_get_error: %d\", sslerr);\n\n    if (sslerr == SSL_ERROR_WANT_READ) {\n\n#if (NGX_SSL && NGX_SSL_ASYNC)\n        if (c->async_enable && ngx_ssl_async_process_fds(c) == NGX_ERROR) {\n            return NGX_ERROR;\n        }\n#endif\n\n        c->read->ready = 0;\n        c->read->handler = ngx_ssl_handshake_handler;\n        c->write->handler = ngx_ssl_handshake_handler;\n\n        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        if (ngx_handle_write_event(c->write, 0) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n#if (T_NGX_HAVE_DTLS)\n        if (c->type == SOCK_DGRAM) {\n\n        /*At least on packet should be sent(SH or HR)*/\n            if (!c->ssl->dtls_send) {\n                ngx_ssl_error(NGX_LOG_ERR, c->log, 0,\n                              \"unexcepted message of dtls session\");\n                return NGX_ERROR;\n            }\n\n            if (DTLSv1_get_timeout(c->ssl->connection, &timeout)) {\n                ngx_msec_t timer = timeout.tv_sec * 1000 + timeout.tv_usec/1000;\n\n                if (timer) {\n                    c->ssl->retrans->handler = ngx_dtls_retransmit;\n                    ngx_add_timer(c->ssl->retrans, timer);\n                }\n\n            } else {\n                /*No need to retransmit, delete timer*/\n                if (c->ssl->retrans &&\n                    c->ssl->retrans->timer_set) {\n                    ngx_del_timer(c->ssl->retrans);\n                }\n            }\n        }\n#endif\n\n        return NGX_AGAIN;\n    }\n\n    if (sslerr == SSL_ERROR_WANT_WRITE) {\n\n#if (NGX_SSL && NGX_SSL_ASYNC)\n        if (c->async_enable && ngx_ssl_async_process_fds(c) == NGX_ERROR) {\n            return NGX_ERROR;\n        }\n#endif\n\n        c->write->ready = 0;\n        c->read->handler = ngx_ssl_handshake_handler;\n        c->write->handler = ngx_ssl_handshake_handler;\n\n        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        if (ngx_handle_write_event(c->write, 0) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        return NGX_AGAIN;\n    }\n\n#if (NGX_SSL && NGX_SSL_ASYNC)\n    if (c->async_enable && sslerr == SSL_ERROR_WANT_ASYNC)\n    {\n        c->async->handler = ngx_ssl_handshake_async_handler;\n        c->read->saved_handler = c->read->handler;\n        c->read->handler = ngx_ssl_empty_handler;\n\n        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"SSL ASYNC WANT recieved: \\\"%s\\\"\", __func__);\n\n        if (ngx_ssl_async_process_fds(c) == NGX_ERROR) {\n            return NGX_ERROR;\n        }\n\n        return NGX_AGAIN;\n    }\n#endif\n\n#ifdef T_INGRESS_SHARED_MEMORY_PB\nif (0\n#if OPENSSL_VERSION_NUMBER >= 0x10101000L\n            || sslerr == SSL_ERROR_WANT_CLIENT_HELLO_CB\n#endif\n   )\n    {\n        c->read->handler = ngx_ssl_handshake_handler;\n        c->write->handler = ngx_ssl_handshake_handler;\n\n        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        if (ngx_handle_write_event(c->write, 0) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        return NGX_AGAIN;\n    }\n#endif\n\n    err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0;\n\n    c->ssl->no_wait_shutdown = 1;\n    c->ssl->no_send_shutdown = 1;\n    c->read->eof = 1;\n\n    if (sslerr == SSL_ERROR_ZERO_RETURN || ERR_peek_error() == 0) {\n        ngx_connection_error(c, err,\n                             \"peer closed connection in SSL handshake\");\n\n        return NGX_ERROR;\n    }\n\n    if (c->ssl->handshake_rejected) {\n        ngx_connection_error(c, err, \"handshake rejected\");\n        ERR_clear_error();\n\n        return NGX_ERROR;\n    }\n\n    c->read->error = 1;\n\n    ngx_ssl_connection_error(c, sslerr, err, \"SSL_do_handshake() failed\");\n\n    return NGX_ERROR;\n}\n\n\n#ifdef SSL_READ_EARLY_DATA_SUCCESS\n\nstatic ngx_int_t\nngx_ssl_try_early_data(ngx_connection_t *c)\n{\n    int        n, sslerr;\n    u_char     buf;\n    size_t     readbytes;\n    ngx_err_t  err;\n    ngx_int_t  rc;\n\n    ngx_ssl_clear_error(c->log);\n\n    readbytes = 0;\n\n    n = SSL_read_early_data(c->ssl->connection, &buf, 1, &readbytes);\n\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"SSL_read_early_data: %d, %uz\", n, readbytes);\n\n    if (n == SSL_READ_EARLY_DATA_FINISH) {\n        c->ssl->try_early_data = 0;\n        return ngx_ssl_handshake(c);\n    }\n\n    if (n == SSL_READ_EARLY_DATA_SUCCESS) {\n\n        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        if (ngx_handle_write_event(c->write, 0) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n#if (NGX_DEBUG)\n        ngx_ssl_handshake_log(c);\n#endif\n\n        c->ssl->try_early_data = 0;\n\n        c->ssl->early_buf = buf;\n        c->ssl->early_preread = 1;\n\n        c->ssl->in_early = 1;\n\n        c->recv = ngx_ssl_recv;\n        c->send = ngx_ssl_write;\n        c->recv_chain = ngx_ssl_recv_chain;\n        c->send_chain = ngx_ssl_send_chain;\n\n        c->read->ready = 1;\n        c->write->ready = 1;\n\n#if (defined BIO_get_ktls_send && !NGX_WIN32)\n\n        if (BIO_get_ktls_send(SSL_get_wbio(c->ssl->connection)) == 1) {\n            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                           \"BIO_get_ktls_send(): 1\");\n            c->ssl->sendfile = 1;\n        }\n\n#endif\n\n        rc = ngx_ssl_ocsp_validate(c);\n\n        if (rc == NGX_ERROR) {\n            return NGX_ERROR;\n        }\n\n        if (rc == NGX_AGAIN) {\n            c->read->handler = ngx_ssl_handshake_handler;\n            c->write->handler = ngx_ssl_handshake_handler;\n            return NGX_AGAIN;\n        }\n\n        c->ssl->handshaked = 1;\n\n        return NGX_OK;\n    }\n\n    /* SSL_READ_EARLY_DATA_ERROR */\n\n    sslerr = SSL_get_error(c->ssl->connection, n);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, \"SSL_get_error: %d\", sslerr);\n\n    if (sslerr == SSL_ERROR_WANT_READ) {\n        c->read->ready = 0;\n        c->read->handler = ngx_ssl_handshake_handler;\n        c->write->handler = ngx_ssl_handshake_handler;\n\n        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        if (ngx_handle_write_event(c->write, 0) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        return NGX_AGAIN;\n    }\n\n    if (sslerr == SSL_ERROR_WANT_WRITE) {\n        c->write->ready = 0;\n        c->read->handler = ngx_ssl_handshake_handler;\n        c->write->handler = ngx_ssl_handshake_handler;\n\n        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        if (ngx_handle_write_event(c->write, 0) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        return NGX_AGAIN;\n    }\n\n#if (NGX_SSL && NGX_SSL_ASYNC)\n    if (c->async_enable && sslerr == SSL_ERROR_WANT_ASYNC)\n    {\n        c->async->handler = ngx_ssl_handshake_async_handler;\n        c->read->saved_handler = c->read->handler;\n        c->read->handler = ngx_ssl_empty_handler;\n\n        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"SSL ASYNC WANT recieved: \\\"%s\\\"\", __func__);\n\n        if (ngx_ssl_async_process_fds(c) == NGX_ERROR) {\n            return NGX_ERROR;\n        }\n\n        return NGX_AGAIN;\n    }\n#endif\n\n    err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0;\n\n    c->ssl->no_wait_shutdown = 1;\n    c->ssl->no_send_shutdown = 1;\n    c->read->eof = 1;\n\n    if (sslerr == SSL_ERROR_ZERO_RETURN || ERR_peek_error() == 0) {\n        ngx_connection_error(c, err,\n                             \"peer closed connection in SSL handshake\");\n\n        return NGX_ERROR;\n    }\n\n    c->read->error = 1;\n\n    ngx_ssl_connection_error(c, sslerr, err, \"SSL_read_early_data() failed\");\n\n    return NGX_ERROR;\n}\n\n#endif\n\n\n#if (NGX_DEBUG)\n\nstatic void\nngx_ssl_handshake_log(ngx_connection_t *c)\n{\n    char         buf[129], *s, *d;\n#if OPENSSL_VERSION_NUMBER >= 0x10000000L\n    const\n#endif\n    SSL_CIPHER  *cipher;\n\n    if (!(c->log->log_level & NGX_LOG_DEBUG_EVENT)) {\n        return;\n    }\n\n    cipher = SSL_get_current_cipher(c->ssl->connection);\n\n    if (cipher) {\n        SSL_CIPHER_description(cipher, &buf[1], 128);\n\n        for (s = &buf[1], d = buf; *s; s++) {\n            if (*s == ' ' && *d == ' ') {\n                continue;\n            }\n\n            if (*s == LF || *s == CR) {\n                continue;\n            }\n\n            *++d = *s;\n        }\n\n        if (*d != ' ') {\n            d++;\n        }\n\n        *d = '\\0';\n\n        ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"SSL: %s, cipher: \\\"%s\\\"\",\n                       SSL_get_version(c->ssl->connection), &buf[1]);\n\n        if (SSL_session_reused(c->ssl->connection)) {\n            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                           \"SSL reused session\");\n        }\n\n    } else {\n        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"SSL no shared ciphers\");\n    }\n}\n\n#endif\n\n\nstatic void\nngx_ssl_handshake_handler(ngx_event_t *ev)\n{\n    ngx_connection_t  *c;\n\n    c = ev->data;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"SSL handshake handler: %d\", ev->write);\n\n    if (ev->timedout) {\n        c->ssl->handler(c);\n        return;\n    }\n\n    if (ngx_ssl_handshake(c) == NGX_AGAIN) {\n        return;\n    }\n\n#if (NGX_SSL && NGX_SSL_ASYNC)\n    /*\n     * empty the handler of async event to avoid\n     * going back to previous ssl handshake state\n     */\n    c->async->handler = ngx_ssl_empty_handler;\n#endif\n    c->ssl->handler(c);\n}\n\n\nssize_t\nngx_ssl_recv_chain(ngx_connection_t *c, ngx_chain_t *cl, off_t limit)\n{\n    u_char     *last;\n    ssize_t     n, bytes, size;\n    ngx_buf_t  *b;\n\n    bytes = 0;\n\n    b = cl->buf;\n    last = b->last;\n\n    for ( ;; ) {\n        size = b->end - last;\n\n        if (limit) {\n            if (bytes >= limit) {\n                return bytes;\n            }\n\n            if (bytes + size > limit) {\n                size = (ssize_t) (limit - bytes);\n            }\n        }\n\n        n = ngx_ssl_recv(c, last, size);\n\n        if (n > 0) {\n            last += n;\n            bytes += n;\n\n            if (!c->read->ready) {\n                return bytes;\n            }\n\n            if (last == b->end) {\n                cl = cl->next;\n\n                if (cl == NULL) {\n                    return bytes;\n                }\n\n                b = cl->buf;\n                last = b->last;\n            }\n\n            continue;\n        }\n\n        if (bytes) {\n\n            if (n == 0 || n == NGX_ERROR) {\n                c->read->ready = 1;\n            }\n\n            return bytes;\n        }\n\n        return n;\n    }\n}\n\n\nssize_t\nngx_ssl_recv(ngx_connection_t *c, u_char *buf, size_t size)\n{\n    int  n, bytes;\n\n#ifdef SSL_READ_EARLY_DATA_SUCCESS\n    if (c->ssl->in_early) {\n        return ngx_ssl_recv_early(c, buf, size);\n    }\n#endif\n\n    if (c->ssl->last == NGX_ERROR) {\n        c->read->ready = 0;\n        c->read->error = 1;\n        return NGX_ERROR;\n    }\n\n    if (c->ssl->last == NGX_DONE) {\n        c->read->ready = 0;\n        c->read->eof = 1;\n        return 0;\n    }\n\n    bytes = 0;\n\n    ngx_ssl_clear_error(c->log);\n\n    /*\n     * SSL_read() may return data in parts, so try to read\n     * until SSL_read() would return no data\n     */\n\n    for ( ;; ) {\n\n        n = SSL_read(c->ssl->connection, buf, size);\n\n        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, \"SSL_read: %d\", n);\n\n        if (n > 0) {\n            bytes += n;\n        }\n\n        c->ssl->last = ngx_ssl_handle_recv(c, n);\n\n        if (c->ssl->last == NGX_OK) {\n\n            size -= n;\n\n            if (size == 0) {\n                c->read->ready = 1;\n\n                if (c->read->available >= 0) {\n                    c->read->available -= bytes;\n\n                    /*\n                     * there can be data buffered at SSL layer,\n                     * so we post an event to continue reading on the next\n                     * iteration of the event loop\n                     */\n\n                    if (c->read->available < 0) {\n                        c->read->available = 0;\n                        c->read->ready = 0;\n\n                        if (c->read->posted) {\n                            ngx_delete_posted_event(c->read);\n                        }\n\n                        ngx_post_event(c->read, &ngx_posted_next_events);\n                    }\n\n                    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                                   \"SSL_read: avail:%d\", c->read->available);\n\n                } else {\n\n#if (NGX_HAVE_FIONREAD)\n\n                    if (ngx_socket_nread(c->fd, &c->read->available) == -1) {\n                        c->read->ready = 0;\n                        c->read->error = 1;\n                        ngx_connection_error(c, ngx_socket_errno,\n                                             ngx_socket_nread_n \" failed\");\n                        return NGX_ERROR;\n                    }\n\n                    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                                   \"SSL_read: avail:%d\", c->read->available);\n\n#endif\n                }\n\n                return bytes;\n            }\n\n            buf += n;\n\n            continue;\n        }\n\n        if (bytes) {\n            if (c->ssl->last != NGX_AGAIN) {\n                c->read->ready = 1;\n            }\n\n            return bytes;\n        }\n\n        switch (c->ssl->last) {\n\n        case NGX_DONE:\n            c->read->ready = 0;\n            c->read->eof = 1;\n            return 0;\n\n        case NGX_ERROR:\n            c->read->ready = 0;\n            c->read->error = 1;\n\n            /* fall through */\n\n        case NGX_AGAIN:\n            return c->ssl->last;\n        }\n    }\n}\n\n\n#ifdef SSL_READ_EARLY_DATA_SUCCESS\n\nstatic ssize_t\nngx_ssl_recv_early(ngx_connection_t *c, u_char *buf, size_t size)\n{\n    int        n, bytes;\n    size_t     readbytes;\n\n    if (c->ssl->last == NGX_ERROR) {\n        c->read->ready = 0;\n        c->read->error = 1;\n        return NGX_ERROR;\n    }\n\n    if (c->ssl->last == NGX_DONE) {\n        c->read->ready = 0;\n        c->read->eof = 1;\n        return 0;\n    }\n\n    bytes = 0;\n\n    ngx_ssl_clear_error(c->log);\n\n    if (c->ssl->early_preread) {\n\n        if (size == 0) {\n            c->read->ready = 0;\n            c->read->eof = 1;\n            return 0;\n        }\n\n        *buf = c->ssl->early_buf;\n\n        c->ssl->early_preread = 0;\n\n        bytes = 1;\n        size -= 1;\n        buf += 1;\n    }\n\n    if (c->ssl->write_blocked) {\n        return NGX_AGAIN;\n    }\n\n    /*\n     * SSL_read_early_data() may return data in parts, so try to read\n     * until SSL_read_early_data() would return no data\n     */\n\n    for ( ;; ) {\n\n        readbytes = 0;\n\n        n = SSL_read_early_data(c->ssl->connection, buf, size, &readbytes);\n\n        ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"SSL_read_early_data: %d, %uz\", n, readbytes);\n\n        if (n == SSL_READ_EARLY_DATA_SUCCESS) {\n\n            c->ssl->last = ngx_ssl_handle_recv(c, 1);\n\n            bytes += readbytes;\n            size -= readbytes;\n\n            if (size == 0) {\n                c->read->ready = 1;\n                return bytes;\n            }\n\n            buf += readbytes;\n\n            continue;\n        }\n\n        if (n == SSL_READ_EARLY_DATA_FINISH) {\n\n            c->ssl->last = ngx_ssl_handle_recv(c, 1);\n            c->ssl->in_early = 0;\n\n            if (bytes) {\n                c->read->ready = 1;\n                return bytes;\n            }\n\n            return ngx_ssl_recv(c, buf, size);\n        }\n\n        /* SSL_READ_EARLY_DATA_ERROR */\n\n        c->ssl->last = ngx_ssl_handle_recv(c, 0);\n\n        if (bytes) {\n            if (c->ssl->last != NGX_AGAIN) {\n                c->read->ready = 1;\n            }\n\n            return bytes;\n        }\n\n        switch (c->ssl->last) {\n\n        case NGX_DONE:\n            c->read->ready = 0;\n            c->read->eof = 1;\n            return 0;\n\n        case NGX_ERROR:\n            c->read->ready = 0;\n            c->read->error = 1;\n\n            /* fall through */\n\n        case NGX_AGAIN:\n            return c->ssl->last;\n        }\n    }\n}\n\n#endif\n\n\nstatic ngx_int_t\nngx_ssl_handle_recv(ngx_connection_t *c, int n)\n{\n    int        sslerr;\n    ngx_err_t  err;\n\n#ifndef SSL_OP_NO_RENEGOTIATION\n\n    if (c->ssl->renegotiation) {\n        /*\n         * disable renegotiation (CVE-2009-3555):\n         * OpenSSL (at least up to 0.9.8l) does not handle disabled\n         * renegotiation gracefully, so drop connection here\n         */\n\n        ngx_log_error(NGX_LOG_NOTICE, c->log, 0, \"SSL renegotiation disabled\");\n\n        while (ERR_peek_error()) {\n            ngx_ssl_error(NGX_LOG_DEBUG, c->log, 0,\n                          \"ignoring stale global SSL error\");\n        }\n\n        ERR_clear_error();\n\n        c->ssl->no_wait_shutdown = 1;\n        c->ssl->no_send_shutdown = 1;\n\n        return NGX_ERROR;\n    }\n\n#endif\n\n    if (n > 0) {\n\n#if (NGX_SSL && NGX_SSL_ASYNC)\n        if (c->async_enable && ngx_ssl_async_process_fds(c) == NGX_ERROR) {\n            return NGX_ERROR;\n        }\n#endif\n\n        if (c->ssl->saved_write_handler) {\n\n            c->write->handler = c->ssl->saved_write_handler;\n            c->ssl->saved_write_handler = NULL;\n            c->write->ready = 1;\n\n            if (ngx_handle_write_event(c->write, 0) != NGX_OK) {\n                return NGX_ERROR;\n            }\n\n            ngx_post_event(c->write, &ngx_posted_events);\n        }\n\n        return NGX_OK;\n    }\n\n    sslerr = SSL_get_error(c->ssl->connection, n);\n\n    err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, \"SSL_get_error: %d\", sslerr);\n\n    if (sslerr == SSL_ERROR_WANT_READ) {\n#if (NGX_SSL && NGX_SSL_ASYNC)\n        if (c->async_enable && ngx_ssl_async_process_fds(c) == NGX_ERROR) {\n            return NGX_ERROR;\n        }\n#endif\n\n        if (c->ssl->saved_write_handler) {\n\n            c->write->handler = c->ssl->saved_write_handler;\n            c->ssl->saved_write_handler = NULL;\n            c->write->ready = 1;\n\n            if (ngx_handle_write_event(c->write, 0) != NGX_OK) {\n                return NGX_ERROR;\n            }\n\n            ngx_post_event(c->write, &ngx_posted_events);\n        }\n\n        c->read->ready = 0;\n        return NGX_AGAIN;\n    }\n\n    if (sslerr == SSL_ERROR_WANT_WRITE) {\n\n        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"SSL_read: want write\");\n\n#if (NGX_SSL && NGX_SSL_ASYNC)\n        if (c->async_enable && ngx_ssl_async_process_fds(c) == NGX_ERROR) {\n            return NGX_ERROR;\n        }\n#endif\n\n        c->write->ready = 0;\n\n        if (ngx_handle_write_event(c->write, 0) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        /*\n         * we do not set the timer because there is already the read event timer\n         */\n\n        if (c->ssl->saved_write_handler == NULL) {\n            c->ssl->saved_write_handler = c->write->handler;\n            c->write->handler = ngx_ssl_write_handler;\n        }\n\n        return NGX_AGAIN;\n    }\n\n#if (NGX_SSL && NGX_SSL_ASYNC)\n    if (c->async_enable && sslerr == SSL_ERROR_WANT_ASYNC) {\n        c->async->handler = ngx_ssl_read_async_handler;\n        c->read->saved_handler = c->read->handler;\n        c->read->handler = ngx_ssl_empty_handler;\n\n        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"SSL ASYNC WANT recieved: \\\"%s\\\"\", __func__);\n\n        if (ngx_ssl_async_process_fds(c) == NGX_ERROR) {\n            return NGX_ERROR;\n        }\n\n        return NGX_AGAIN;\n    }\n#endif\n    c->ssl->no_wait_shutdown = 1;\n    c->ssl->no_send_shutdown = 1;\n\n    if (sslerr == SSL_ERROR_ZERO_RETURN || ERR_peek_error() == 0) {\n        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"peer shutdown SSL cleanly\");\n        return NGX_DONE;\n    }\n\n    ngx_ssl_connection_error(c, sslerr, err, \"SSL_read() failed\");\n\n    return NGX_ERROR;\n}\n\n\nstatic void\nngx_ssl_write_handler(ngx_event_t *wev)\n{\n    ngx_connection_t  *c;\n\n    c = wev->data;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, \"SSL write handler\");\n\n    c->read->handler(c->read);\n}\n\n\n/*\n * OpenSSL has no SSL_writev() so we copy several bufs into our 16K buffer\n * before the SSL_write() call to decrease a SSL overhead.\n *\n * Besides for protocols such as HTTP it is possible to always buffer\n * the output to decrease a SSL overhead some more.\n */\n\nngx_chain_t *\nngx_ssl_send_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)\n{\n    int           n;\n    ngx_uint_t    flush;\n    ssize_t       send, size, file_size;\n    ngx_buf_t    *buf;\n    ngx_chain_t  *cl;\n\n    if (!c->ssl->buffer) {\n\n        while (in) {\n            if (ngx_buf_special(in->buf)) {\n                in = in->next;\n                continue;\n            }\n\n            n = ngx_ssl_write(c, in->buf->pos, in->buf->last - in->buf->pos);\n\n            if (n == NGX_ERROR) {\n                return NGX_CHAIN_ERROR;\n            }\n\n            if (n == NGX_AGAIN) {\n                return in;\n            }\n\n            in->buf->pos += n;\n\n            if (in->buf->pos == in->buf->last) {\n                in = in->next;\n            }\n        }\n\n        return in;\n    }\n\n\n    /* the maximum limit size is the maximum int32_t value - the page size */\n\n    if (limit == 0 || limit > (off_t) (NGX_MAX_INT32_VALUE - ngx_pagesize)) {\n        limit = NGX_MAX_INT32_VALUE - ngx_pagesize;\n    }\n\n    buf = c->ssl->buf;\n\n    if (buf == NULL) {\n        buf = ngx_create_temp_buf(c->pool, c->ssl->buffer_size);\n        if (buf == NULL) {\n            return NGX_CHAIN_ERROR;\n        }\n\n        c->ssl->buf = buf;\n    }\n\n    if (buf->start == NULL) {\n        buf->start = ngx_palloc(c->pool, c->ssl->buffer_size);\n        if (buf->start == NULL) {\n            return NGX_CHAIN_ERROR;\n        }\n\n        buf->pos = buf->start;\n        buf->last = buf->start;\n        buf->end = buf->start + c->ssl->buffer_size;\n    }\n\n    send = buf->last - buf->pos;\n    flush = (in == NULL) ? 1 : buf->flush;\n\n    for ( ;; ) {\n\n        while (in && buf->last < buf->end && send < limit) {\n            if (in->buf->last_buf || in->buf->flush) {\n                flush = 1;\n            }\n\n            if (ngx_buf_special(in->buf)) {\n                in = in->next;\n                continue;\n            }\n\n            if (in->buf->in_file && c->ssl->sendfile) {\n                flush = 1;\n                break;\n            }\n\n            size = in->buf->last - in->buf->pos;\n\n            if (size > buf->end - buf->last) {\n                size = buf->end - buf->last;\n            }\n\n            if (send + size > limit) {\n                size = (ssize_t) (limit - send);\n            }\n\n            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                           \"SSL buf copy: %z\", size);\n\n            ngx_memcpy(buf->last, in->buf->pos, size);\n\n            buf->last += size;\n            in->buf->pos += size;\n            send += size;\n\n            if (in->buf->pos == in->buf->last) {\n                in = in->next;\n            }\n        }\n\n        if (!flush && send < limit && buf->last < buf->end) {\n            break;\n        }\n\n        size = buf->last - buf->pos;\n\n        if (size == 0) {\n\n            if (in && in->buf->in_file && send < limit) {\n\n                /* coalesce the neighbouring file bufs */\n\n                cl = in;\n                file_size = (size_t) ngx_chain_coalesce_file(&cl, limit - send);\n\n                n = ngx_ssl_sendfile(c, in->buf, file_size);\n\n                if (n == NGX_ERROR) {\n                    return NGX_CHAIN_ERROR;\n                }\n\n                if (n == NGX_AGAIN) {\n                    break;\n                }\n\n                in = ngx_chain_update_sent(in, n);\n\n                send += n;\n                flush = 0;\n\n                continue;\n            }\n\n            buf->flush = 0;\n            c->buffered &= ~NGX_SSL_BUFFERED;\n\n            return in;\n        }\n\n        n = ngx_ssl_write(c, buf->pos, size);\n\n        if (n == NGX_ERROR) {\n            return NGX_CHAIN_ERROR;\n        }\n\n        if (n == NGX_AGAIN) {\n            break;\n        }\n\n        buf->pos += n;\n\n        if (n < size) {\n            break;\n        }\n\n        flush = 0;\n\n        buf->pos = buf->start;\n        buf->last = buf->start;\n\n        if (in == NULL || send >= limit) {\n            break;\n        }\n    }\n\n    buf->flush = flush;\n\n    if (buf->pos < buf->last) {\n        c->buffered |= NGX_SSL_BUFFERED;\n\n    } else {\n        c->buffered &= ~NGX_SSL_BUFFERED;\n    }\n\n    return in;\n}\n\n\nssize_t\nngx_ssl_write(ngx_connection_t *c, u_char *data, size_t size)\n{\n    int        n, sslerr;\n    ngx_err_t  err;\n\n#ifdef SSL_READ_EARLY_DATA_SUCCESS\n    if (c->ssl->in_early) {\n        return ngx_ssl_write_early(c, data, size);\n    }\n#endif\n\n    ngx_ssl_clear_error(c->log);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, \"SSL to write: %uz\", size);\n\n    n = SSL_write(c->ssl->connection, data, size);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, \"SSL_write: %d\", n);\n\n    if (n > 0) {\n\n#if (NGX_SSL && NGX_SSL_ASYNC)\n        if (c->async_enable && ngx_ssl_async_process_fds(c) == NGX_ERROR) {\n            return NGX_ERROR;\n        }\n#endif\n\n        if (c->ssl->saved_read_handler) {\n\n            c->read->handler = c->ssl->saved_read_handler;\n            c->ssl->saved_read_handler = NULL;\n            c->read->ready = 1;\n\n            if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n                return NGX_ERROR;\n            }\n\n            ngx_post_event(c->read, &ngx_posted_events);\n        }\n\n        c->sent += n;\n\n        return n;\n    }\n\n    sslerr = SSL_get_error(c->ssl->connection, n);\n\n    if (sslerr == SSL_ERROR_ZERO_RETURN) {\n\n        /*\n         * OpenSSL 1.1.1 fails to return SSL_ERROR_SYSCALL if an error\n         * happens during SSL_write() after close_notify alert from the\n         * peer, and returns SSL_ERROR_ZERO_RETURN instead,\n         * https://git.openssl.org/?p=openssl.git;a=commitdiff;h=8051ab2\n         */\n\n        sslerr = SSL_ERROR_SYSCALL;\n    }\n\n    err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, \"SSL_get_error: %d\", sslerr);\n\n    if (sslerr == SSL_ERROR_WANT_WRITE) {\n#if (NGX_SSL && NGX_SSL_ASYNC)\n        if (c->async_enable && ngx_ssl_async_process_fds(c) == NGX_ERROR) {\n            return NGX_ERROR;\n        }\n#endif\n\n        if (c->ssl->saved_read_handler) {\n\n            c->read->handler = c->ssl->saved_read_handler;\n            c->ssl->saved_read_handler = NULL;\n            c->read->ready = 1;\n\n            if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n                return NGX_ERROR;\n            }\n\n            ngx_post_event(c->read, &ngx_posted_events);\n        }\n\n        c->write->ready = 0;\n        return NGX_AGAIN;\n    }\n\n    if (sslerr == SSL_ERROR_WANT_READ) {\n\n        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"SSL_write: want read\");\n\n#if (NGX_SSL && NGX_SSL_ASYNC)\n        if (c->async_enable && ngx_ssl_async_process_fds(c) == NGX_ERROR) {\n            return NGX_ERROR;\n        }\n#endif\n        c->read->ready = 0;\n\n        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        /*\n         * we do not set the timer because there is already\n         * the write event timer\n         */\n\n        if (c->ssl->saved_read_handler == NULL) {\n            c->ssl->saved_read_handler = c->read->handler;\n            c->read->handler = ngx_ssl_read_handler;\n        }\n\n        return NGX_AGAIN;\n    }\n\n#if (NGX_SSL && NGX_SSL_ASYNC)\n    if (c->async_enable && sslerr == SSL_ERROR_WANT_ASYNC) {\n        c->async->handler = ngx_ssl_write_async_handler;\n        c->read->saved_handler = c->read->handler;\n        c->read->handler = ngx_ssl_empty_handler;\n\n        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"SSL ASYNC WANT recieved: \\\"%s\\\"\", __func__);\n\n        if (ngx_ssl_async_process_fds(c) == NGX_ERROR) {\n            return NGX_ERROR;\n        }\n\n        return NGX_AGAIN;\n    }\n#endif\n    c->ssl->no_wait_shutdown = 1;\n    c->ssl->no_send_shutdown = 1;\n    c->write->error = 1;\n\n    ngx_ssl_connection_error(c, sslerr, err, \"SSL_write() failed\");\n\n    return NGX_ERROR;\n}\n\n\n#ifdef SSL_READ_EARLY_DATA_SUCCESS\n\nstatic ssize_t\nngx_ssl_write_early(ngx_connection_t *c, u_char *data, size_t size)\n{\n    int        n, sslerr;\n    size_t     written;\n    ngx_err_t  err;\n\n    ngx_ssl_clear_error(c->log);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, \"SSL to write: %uz\", size);\n\n    written = 0;\n\n    n = SSL_write_early_data(c->ssl->connection, data, size, &written);\n\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"SSL_write_early_data: %d, %uz\", n, written);\n\n    if (n > 0) {\n\n        if (c->ssl->saved_read_handler) {\n\n            c->read->handler = c->ssl->saved_read_handler;\n            c->ssl->saved_read_handler = NULL;\n            c->read->ready = 1;\n\n            if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n                return NGX_ERROR;\n            }\n\n            ngx_post_event(c->read, &ngx_posted_events);\n        }\n\n        if (c->ssl->write_blocked) {\n            c->ssl->write_blocked = 0;\n            ngx_post_event(c->read, &ngx_posted_events);\n        }\n\n        c->sent += written;\n\n        return written;\n    }\n\n    sslerr = SSL_get_error(c->ssl->connection, n);\n\n    err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, \"SSL_get_error: %d\", sslerr);\n\n    if (sslerr == SSL_ERROR_WANT_WRITE) {\n\n        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"SSL_write_early_data: want write\");\n\n        if (c->ssl->saved_read_handler) {\n\n            c->read->handler = c->ssl->saved_read_handler;\n            c->ssl->saved_read_handler = NULL;\n            c->read->ready = 1;\n\n            if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n                return NGX_ERROR;\n            }\n\n            ngx_post_event(c->read, &ngx_posted_events);\n        }\n\n        /*\n         * OpenSSL 1.1.1a fails to handle SSL_read_early_data()\n         * if an SSL_write_early_data() call blocked on writing,\n         * see https://github.com/openssl/openssl/issues/7757\n         */\n\n        c->ssl->write_blocked = 1;\n\n        c->write->ready = 0;\n        return NGX_AGAIN;\n    }\n\n    if (sslerr == SSL_ERROR_WANT_READ) {\n\n        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"SSL_write_early_data: want read\");\n\n        c->read->ready = 0;\n\n        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        /*\n         * we do not set the timer because there is already\n         * the write event timer\n         */\n\n        if (c->ssl->saved_read_handler == NULL) {\n            c->ssl->saved_read_handler = c->read->handler;\n            c->read->handler = ngx_ssl_read_handler;\n        }\n\n        return NGX_AGAIN;\n    }\n\n    c->ssl->no_wait_shutdown = 1;\n    c->ssl->no_send_shutdown = 1;\n    c->write->error = 1;\n\n    ngx_ssl_connection_error(c, sslerr, err, \"SSL_write_early_data() failed\");\n\n    return NGX_ERROR;\n}\n\n#endif\n\n\nstatic ssize_t\nngx_ssl_sendfile(ngx_connection_t *c, ngx_buf_t *file, size_t size)\n{\n#if (defined BIO_get_ktls_send && !NGX_WIN32)\n\n    int        sslerr, flags;\n    ssize_t    n;\n    ngx_err_t  err;\n\n    ngx_ssl_clear_error(c->log);\n\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"SSL to sendfile: @%O %uz\",\n                   file->file_pos, size);\n\n    ngx_set_errno(0);\n\n#if (NGX_HAVE_SENDFILE_NODISKIO)\n\n    flags = (c->busy_count <= 2) ? SF_NODISKIO : 0;\n\n    if (file->file->directio) {\n        flags |= SF_NOCACHE;\n    }\n\n#else\n    flags = 0;\n#endif\n\n    n = SSL_sendfile(c->ssl->connection, file->file->fd, file->file_pos,\n                     size, flags);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, \"SSL_sendfile: %z\", n);\n\n    if (n > 0) {\n\n        if (c->ssl->saved_read_handler) {\n\n            c->read->handler = c->ssl->saved_read_handler;\n            c->ssl->saved_read_handler = NULL;\n            c->read->ready = 1;\n\n            if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n                return NGX_ERROR;\n            }\n\n            ngx_post_event(c->read, &ngx_posted_events);\n        }\n\n#if (NGX_HAVE_SENDFILE_NODISKIO)\n        c->busy_count = 0;\n#endif\n\n        c->sent += n;\n\n        return n;\n    }\n\n    if (n == 0) {\n\n        /*\n         * if sendfile returns zero, then someone has truncated the file,\n         * so the offset became beyond the end of the file\n         */\n\n        ngx_log_error(NGX_LOG_ALERT, c->log, 0,\n                      \"SSL_sendfile() reported that \\\"%s\\\" was truncated at %O\",\n                      file->file->name.data, file->file_pos);\n\n        return NGX_ERROR;\n    }\n\n    sslerr = SSL_get_error(c->ssl->connection, n);\n\n    if (sslerr == SSL_ERROR_ZERO_RETURN) {\n\n        /*\n         * OpenSSL fails to return SSL_ERROR_SYSCALL if an error\n         * happens during writing after close_notify alert from the\n         * peer, and returns SSL_ERROR_ZERO_RETURN instead\n         */\n\n        sslerr = SSL_ERROR_SYSCALL;\n    }\n\n    if (sslerr == SSL_ERROR_SSL\n        && ERR_GET_REASON(ERR_peek_error()) == SSL_R_UNINITIALIZED\n        && ngx_errno != 0)\n    {\n        /*\n         * OpenSSL fails to return SSL_ERROR_SYSCALL if an error\n         * happens in sendfile(), and returns SSL_ERROR_SSL with\n         * SSL_R_UNINITIALIZED reason instead\n         */\n\n        sslerr = SSL_ERROR_SYSCALL;\n    }\n\n    err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, \"SSL_get_error: %d\", sslerr);\n\n    if (sslerr == SSL_ERROR_WANT_WRITE) {\n\n        if (c->ssl->saved_read_handler) {\n\n            c->read->handler = c->ssl->saved_read_handler;\n            c->ssl->saved_read_handler = NULL;\n            c->read->ready = 1;\n\n            if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n                return NGX_ERROR;\n            }\n\n            ngx_post_event(c->read, &ngx_posted_events);\n        }\n\n#if (NGX_HAVE_SENDFILE_NODISKIO)\n\n        if (ngx_errno == EBUSY) {\n            c->busy_count++;\n\n            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                           \"SSL_sendfile() busy, count:%d\", c->busy_count);\n\n            if (c->write->posted) {\n                ngx_delete_posted_event(c->write);\n            }\n\n            ngx_post_event(c->write, &ngx_posted_next_events);\n        }\n\n#endif\n\n        c->write->ready = 0;\n        return NGX_AGAIN;\n    }\n\n    if (sslerr == SSL_ERROR_WANT_READ) {\n\n        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"SSL_sendfile: want read\");\n\n        c->read->ready = 0;\n\n        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        /*\n         * we do not set the timer because there is already\n         * the write event timer\n         */\n\n        if (c->ssl->saved_read_handler == NULL) {\n            c->ssl->saved_read_handler = c->read->handler;\n            c->read->handler = ngx_ssl_read_handler;\n        }\n\n        return NGX_AGAIN;\n    }\n\n    c->ssl->no_wait_shutdown = 1;\n    c->ssl->no_send_shutdown = 1;\n    c->write->error = 1;\n\n    ngx_ssl_connection_error(c, sslerr, err, \"SSL_sendfile() failed\");\n\n#else\n    ngx_log_error(NGX_LOG_ALERT, c->log, 0,\n                  \"SSL_sendfile() not available\");\n#endif\n\n    return NGX_ERROR;\n}\n\n\nstatic void\nngx_ssl_read_handler(ngx_event_t *rev)\n{\n    ngx_connection_t  *c;\n\n    c = rev->data;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, \"SSL read handler\");\n\n    c->write->handler(c->write);\n}\n\n\nvoid\nngx_ssl_free_buffer(ngx_connection_t *c)\n{\n    if (c->ssl->buf && c->ssl->buf->start) {\n        if (ngx_pfree(c->pool, c->ssl->buf->start) == NGX_OK) {\n            c->ssl->buf->start = NULL;\n        }\n    }\n}\n\n\nngx_int_t\nngx_ssl_shutdown(ngx_connection_t *c)\n{\n    int         n, sslerr, mode;\n    ngx_int_t   rc;\n    ngx_err_t   err;\n    ngx_uint_t  tries;\n\n#if (T_NGX_HAVE_DTLS)\n    if (c->ssl->retrans && c->ssl->retrans->timer_set) {\n        ngx_del_timer(c->ssl->retrans);\n    }\n#endif\n\n    rc = NGX_OK;\n\n    ngx_ssl_ocsp_cleanup(c);\n\n    if (SSL_in_init(c->ssl->connection)) {\n        /*\n         * OpenSSL 1.0.2f complains if SSL_shutdown() is called during\n         * an SSL handshake, while previous versions always return 0.\n         * Avoid calling SSL_shutdown() if handshake wasn't completed.\n         */\n\n#if (NGX_SSL && NGX_SSL_ASYNC)\n        if (c->async_enable) {\n            /* Check if there is inflight request */\n            if (SSL_want_async(c->ssl->connection) && !c->timedout) {\n                c->async->handler = ngx_ssl_shutdown_async_handler;\n                ngx_ssl_async_process_fds(c);\n                ngx_add_timer(c->async, 300);\n                return NGX_AGAIN;\n            }\n\n            /* Ignore errors from ngx_ssl_async_process_fds as\n               we want to carry on and close the SSL connection\n               anyway. */\n            ngx_ssl_async_process_fds(c);\n            if (ngx_del_async_conn) {\n                if (c->num_async_fds) {\n                    ngx_del_async_conn(c, NGX_DISABLE_EVENT);\n                    c->num_async_fds--;\n                }\n            }\n            ngx_del_conn(c, NGX_DISABLE_EVENT);\n        }\n#endif\n\n        goto done;\n    }\n\n    if (c->timedout || c->error || c->buffered) {\n        mode = SSL_RECEIVED_SHUTDOWN|SSL_SENT_SHUTDOWN;\n        SSL_set_quiet_shutdown(c->ssl->connection, 1);\n\n    } else {\n        mode = SSL_get_shutdown(c->ssl->connection);\n\n        if (c->ssl->no_wait_shutdown) {\n            mode |= SSL_RECEIVED_SHUTDOWN;\n        }\n\n        if (c->ssl->no_send_shutdown) {\n            mode |= SSL_SENT_SHUTDOWN;\n        }\n\n        if (c->ssl->no_wait_shutdown && c->ssl->no_send_shutdown) {\n            SSL_set_quiet_shutdown(c->ssl->connection, 1);\n        }\n    }\n\n    SSL_set_shutdown(c->ssl->connection, mode);\n\n    ngx_ssl_clear_error(c->log);\n\n    tries = 2;\n\n    for ( ;; ) {\n\n        /*\n         * For bidirectional shutdown, SSL_shutdown() needs to be called\n         * twice: first call sends the \"close notify\" alert and returns 0,\n         * second call waits for the peer's \"close notify\" alert.\n         */\n\n        n = SSL_shutdown(c->ssl->connection);\n\n        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, \"SSL_shutdown: %d\", n);\n\n        if (n == 1) {\n#if (NGX_SSL && NGX_SSL_ASYNC)\n            if (c->async_enable) {\n                /* Ignore errors from ngx_ssl_async_process_fds as\n                    we want to carry on and close the SSL connection\n                    anyway. */\n                ngx_ssl_async_process_fds(c);\n                if (ngx_del_async_conn) {\n                    if (c->num_async_fds) {\n                        ngx_del_async_conn(c, NGX_DISABLE_EVENT);\n                        c->num_async_fds--;\n                    }\n                }\n                ngx_del_conn(c, NGX_DISABLE_EVENT);\n            }\n#endif\n            goto done;\n        }\n\n        if (n == 0 && tries-- > 1) {\n            continue;\n        }\n\n        /* before 0.9.8m SSL_shutdown() returned 0 instead of -1 on errors */\n\n#if (NGX_SSL && NGX_SSL_ASYNC)\n        if (c->async_enable && ngx_ssl_async_process_fds(c) == NGX_ERROR) {\n            return NGX_ERROR;\n        }\n#endif\n        sslerr = SSL_get_error(c->ssl->connection, n);\n\n        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"SSL_get_error: %d\", sslerr);\n\n        if (sslerr == SSL_ERROR_WANT_READ || sslerr == SSL_ERROR_WANT_WRITE) {\n#if (NGX_SSL && NGX_SSL_ASYNC)\n            if (c->async_enable && ngx_ssl_async_process_fds(c) == NGX_ERROR) {\n                return NGX_ERROR;\n            }\n#endif\n            c->read->handler = ngx_ssl_shutdown_handler;\n            c->write->handler = ngx_ssl_shutdown_handler;\n\n            if (sslerr == SSL_ERROR_WANT_READ) {\n                c->read->ready = 0;\n\n            } else {\n                c->write->ready = 0;\n            }\n\n            if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n                goto failed;\n            }\n\n            if (ngx_handle_write_event(c->write, 0) != NGX_OK) {\n                goto failed;\n            }\n\n            ngx_add_timer(c->read, 3000);\n\n            return NGX_AGAIN;\n        }\n\n#if (NGX_SSL && NGX_SSL_ASYNC)\n    if (c->async_enable) {\n        if (sslerr == SSL_ERROR_WANT_ASYNC) {\n            c->async->handler = ngx_ssl_shutdown_async_handler;\n            c->read->saved_handler = ngx_ssl_shutdown_handler;\n            c->read->handler = ngx_ssl_empty_handler;\n            c->write->handler = ngx_ssl_shutdown_handler;\n\n            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                           \"SSL ASYNC WANT recieved: \\\"%s\\\"\", __func__);\n\n            /* Ignore errors from ngx_ssl_async_process_fds as\n               we want to carry on anyway */\n            ngx_ssl_async_process_fds(c);\n            return NGX_AGAIN;\n        }\n\n        /* Ignore errors from ngx_ssl_async_process_fds as\n           we want to carry on and close the SSL connection\n           anyway. */\n        ngx_ssl_async_process_fds(c);\n        if (ngx_del_async_conn) {\n            if (c->num_async_fds) {\n                ngx_del_async_conn(c, NGX_DISABLE_EVENT);\n                c->num_async_fds--;\n            }\n        }\n        ngx_del_conn(c, NGX_DISABLE_EVENT);\n    }\n#endif\n\n        if (sslerr == SSL_ERROR_ZERO_RETURN || ERR_peek_error() == 0) {\n            goto done;\n        }\n\n        err = (sslerr == SSL_ERROR_SYSCALL) ? ngx_errno : 0;\n\n        ngx_ssl_connection_error(c, sslerr, err, \"SSL_shutdown() failed\");\n\n        break;\n    }\n\nfailed:\n\n    rc = NGX_ERROR;\n\ndone:\n\n    if (c->ssl->shutdown_without_free) {\n        c->ssl->shutdown_without_free = 0;\n        c->recv = ngx_recv;\n        return rc;\n    }\n\n#if (T_NGX_XQUIC)\n\tif (!c->xquic_conn) {\n\t\tSSL_free(c->ssl->connection);\n\t}\n#else\n    SSL_free(c->ssl->connection);\n#endif\n    c->ssl = NULL;\n    c->recv = ngx_recv;\n\n    return rc;\n}\n\n\nstatic void\nngx_ssl_shutdown_handler(ngx_event_t *ev)\n{\n    ngx_connection_t           *c;\n    ngx_connection_handler_pt   handler;\n\n    c = ev->data;\n    handler = c->ssl->handler;\n\n    if (ev->timedout) {\n        c->timedout = 1;\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0, \"SSL shutdown handler\");\n\n    if (ngx_ssl_shutdown(c) == NGX_AGAIN) {\n        return;\n    }\n\n#if (NGX_SSL && NGX_SSL_ASYNC)\n    /*\n     * empty the handler of async event to avoid\n     * going back to previous ssl shutdown state\n     */\n    c->async->handler = ngx_ssl_empty_handler;\n#endif\n    handler(c);\n}\n\n\nstatic void\nngx_ssl_connection_error(ngx_connection_t *c, int sslerr, ngx_err_t err,\n    char *text)\n{\n    int         n;\n    ngx_uint_t  level;\n\n    level = NGX_LOG_CRIT;\n\n    if (sslerr == SSL_ERROR_SYSCALL) {\n\n        if (err == NGX_ECONNRESET\n#if (NGX_WIN32)\n            || err == NGX_ECONNABORTED\n#endif\n            || err == NGX_EPIPE\n            || err == NGX_ENOTCONN\n            || err == NGX_ETIMEDOUT\n            || err == NGX_ECONNREFUSED\n            || err == NGX_ENETDOWN\n            || err == NGX_ENETUNREACH\n            || err == NGX_EHOSTDOWN\n            || err == NGX_EHOSTUNREACH)\n        {\n            switch (c->log_error) {\n\n            case NGX_ERROR_IGNORE_ECONNRESET:\n            case NGX_ERROR_INFO:\n                level = NGX_LOG_INFO;\n                break;\n\n            case NGX_ERROR_ERR:\n                level = NGX_LOG_ERR;\n                break;\n\n            default:\n                break;\n            }\n        }\n\n    } else if (sslerr == SSL_ERROR_SSL) {\n\n        n = ERR_GET_REASON(ERR_peek_last_error());\n\n            /* handshake failures */\n        if (n == SSL_R_BAD_CHANGE_CIPHER_SPEC                        /*  103 */\n#ifdef SSL_R_NO_SUITABLE_KEY_SHARE\n            || n == SSL_R_NO_SUITABLE_KEY_SHARE                      /*  101 */\n#endif\n#ifdef SSL_R_BAD_ALERT\n            || n == SSL_R_BAD_ALERT                                  /*  102 */\n#endif\n#ifdef SSL_R_BAD_KEY_SHARE\n            || n == SSL_R_BAD_KEY_SHARE                              /*  108 */\n#endif\n#ifdef SSL_R_BAD_EXTENSION\n            || n == SSL_R_BAD_EXTENSION                              /*  110 */\n#endif\n            || n == SSL_R_BAD_DIGEST_LENGTH                          /*  111 */\n#ifdef SSL_R_MISSING_SIGALGS_EXTENSION\n            || n == SSL_R_MISSING_SIGALGS_EXTENSION                  /*  112 */\n#endif\n            || n == SSL_R_BAD_PACKET_LENGTH                          /*  115 */\n#ifdef SSL_R_NO_SUITABLE_SIGNATURE_ALGORITHM\n            || n == SSL_R_NO_SUITABLE_SIGNATURE_ALGORITHM            /*  118 */\n#endif\n#ifdef SSL_R_BAD_KEY_UPDATE\n            || n == SSL_R_BAD_KEY_UPDATE                             /*  122 */\n#endif\n            || n == SSL_R_BLOCK_CIPHER_PAD_IS_WRONG                  /*  129 */\n            || n == SSL_R_CCS_RECEIVED_EARLY                         /*  133 */\n#ifdef SSL_R_DECODE_ERROR\n            || n == SSL_R_DECODE_ERROR                               /*  137 */\n#endif\n#ifdef SSL_R_DATA_BETWEEN_CCS_AND_FINISHED\n            || n == SSL_R_DATA_BETWEEN_CCS_AND_FINISHED              /*  145 */\n#endif\n            || n == SSL_R_DATA_LENGTH_TOO_LONG                       /*  146 */\n            || n == SSL_R_DIGEST_CHECK_FAILED                        /*  149 */\n            || n == SSL_R_ENCRYPTED_LENGTH_TOO_LONG                  /*  150 */\n            || n == SSL_R_ERROR_IN_RECEIVED_CIPHER_LIST              /*  151 */\n            || n == SSL_R_EXCESSIVE_MESSAGE_SIZE                     /*  152 */\n#ifdef SSL_R_GOT_A_FIN_BEFORE_A_CCS\n            || n == SSL_R_GOT_A_FIN_BEFORE_A_CCS                     /*  154 */\n#endif\n            || n == SSL_R_HTTPS_PROXY_REQUEST                        /*  155 */\n            || n == SSL_R_HTTP_REQUEST                               /*  156 */\n            || n == SSL_R_LENGTH_MISMATCH                            /*  159 */\n#ifdef SSL_R_LENGTH_TOO_SHORT\n            || n == SSL_R_LENGTH_TOO_SHORT                           /*  160 */\n#endif\n#ifdef SSL_R_NO_RENEGOTIATION\n            || n == SSL_R_NO_RENEGOTIATION                           /*  182 */\n#endif\n#ifdef SSL_R_NO_CIPHERS_PASSED\n            || n == SSL_R_NO_CIPHERS_PASSED                          /*  182 */\n#endif\n            || n == SSL_R_NO_CIPHERS_SPECIFIED                       /*  183 */\n#ifdef SSL_R_BAD_CIPHER\n            || n == SSL_R_BAD_CIPHER                                 /*  186 */\n#endif\n            || n == SSL_R_NO_COMPRESSION_SPECIFIED                   /*  187 */\n            || n == SSL_R_NO_SHARED_CIPHER                           /*  193 */\n#ifdef SSL_R_PACKET_LENGTH_TOO_LONG\n            || n == SSL_R_PACKET_LENGTH_TOO_LONG                     /*  198 */\n#endif\n            || n == SSL_R_RECORD_LENGTH_MISMATCH                     /*  213 */\n#ifdef SSL_R_TOO_MANY_WARNING_ALERTS\n            || n == SSL_R_TOO_MANY_WARNING_ALERTS                    /*  220 */\n#endif\n#ifdef SSL_R_CLIENTHELLO_TLSEXT\n            || n == SSL_R_CLIENTHELLO_TLSEXT                         /*  226 */\n#endif\n#ifdef SSL_R_PARSE_TLSEXT\n            || n == SSL_R_PARSE_TLSEXT                               /*  227 */\n#endif\n#ifdef SSL_R_CALLBACK_FAILED\n            || n == SSL_R_CALLBACK_FAILED                            /*  234 */\n#endif\n#ifdef SSL_R_TLS_RSA_ENCRYPTED_VALUE_LENGTH_IS_WRONG\n            || n == SSL_R_TLS_RSA_ENCRYPTED_VALUE_LENGTH_IS_WRONG    /*  234 */\n#endif\n#ifdef SSL_R_NO_APPLICATION_PROTOCOL\n            || n == SSL_R_NO_APPLICATION_PROTOCOL                    /*  235 */\n#endif\n            || n == SSL_R_UNEXPECTED_MESSAGE                         /*  244 */\n            || n == SSL_R_UNEXPECTED_RECORD                          /*  245 */\n            || n == SSL_R_UNKNOWN_ALERT_TYPE                         /*  246 */\n            || n == SSL_R_UNKNOWN_PROTOCOL                           /*  252 */\n#ifdef SSL_R_NO_COMMON_SIGNATURE_ALGORITHMS\n            || n == SSL_R_NO_COMMON_SIGNATURE_ALGORITHMS             /*  253 */\n#endif\n#ifdef SSL_R_INVALID_COMPRESSION_LIST\n            || n == SSL_R_INVALID_COMPRESSION_LIST                   /*  256 */\n#endif\n#ifdef SSL_R_MISSING_KEY_SHARE\n            || n == SSL_R_MISSING_KEY_SHARE                          /*  258 */\n#endif\n            || n == SSL_R_UNSUPPORTED_PROTOCOL                       /*  258 */\n#ifdef SSL_R_NO_SHARED_GROUP\n            || n == SSL_R_NO_SHARED_GROUP                            /*  266 */\n#endif\n            || n == SSL_R_WRONG_VERSION_NUMBER                       /*  267 */\n#ifdef SSL_R_TOO_MUCH_SKIPPED_EARLY_DATA\n            || n == SSL_R_TOO_MUCH_SKIPPED_EARLY_DATA                /*  270 */\n#endif\n            || n == SSL_R_BAD_LENGTH                                 /*  271 */\n            || n == SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC        /*  281 */\n#ifdef SSL_R_APPLICATION_DATA_AFTER_CLOSE_NOTIFY\n            || n == SSL_R_APPLICATION_DATA_AFTER_CLOSE_NOTIFY        /*  291 */\n#endif\n#ifdef SSL_R_APPLICATION_DATA_ON_SHUTDOWN\n            || n == SSL_R_APPLICATION_DATA_ON_SHUTDOWN               /*  291 */\n#endif\n#ifdef SSL_R_BAD_LEGACY_VERSION\n            || n == SSL_R_BAD_LEGACY_VERSION                         /*  292 */\n#endif\n#ifdef SSL_R_MIXED_HANDSHAKE_AND_NON_HANDSHAKE_DATA\n            || n == SSL_R_MIXED_HANDSHAKE_AND_NON_HANDSHAKE_DATA     /*  293 */\n#endif\n#ifdef SSL_R_RECORD_TOO_SMALL\n            || n == SSL_R_RECORD_TOO_SMALL                           /*  298 */\n#endif\n#ifdef SSL_R_SSL3_SESSION_ID_TOO_LONG\n            || n == SSL_R_SSL3_SESSION_ID_TOO_LONG                   /*  300 */\n#endif\n#ifdef SSL_R_BAD_ECPOINT\n            || n == SSL_R_BAD_ECPOINT                                /*  306 */\n#endif\n#ifdef SSL_R_RENEGOTIATE_EXT_TOO_LONG\n            || n == SSL_R_RENEGOTIATE_EXT_TOO_LONG                   /*  335 */\n            || n == SSL_R_RENEGOTIATION_ENCODING_ERR                 /*  336 */\n            || n == SSL_R_RENEGOTIATION_MISMATCH                     /*  337 */\n#endif\n#ifdef SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED\n            || n == SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED       /*  338 */\n#endif\n#ifdef SSL_R_SCSV_RECEIVED_WHEN_RENEGOTIATING\n            || n == SSL_R_SCSV_RECEIVED_WHEN_RENEGOTIATING           /*  345 */\n#endif\n#ifdef SSL_R_INAPPROPRIATE_FALLBACK\n            || n == SSL_R_INAPPROPRIATE_FALLBACK                     /*  373 */\n#endif\n#ifdef SSL_R_NO_SHARED_SIGNATURE_ALGORITHMS\n            || n == SSL_R_NO_SHARED_SIGNATURE_ALGORITHMS             /*  376 */\n#endif\n#ifdef SSL_R_NO_SHARED_SIGATURE_ALGORITHMS\n            || n == SSL_R_NO_SHARED_SIGATURE_ALGORITHMS              /*  376 */\n#endif\n#ifdef SSL_R_CERT_CB_ERROR\n            || n == SSL_R_CERT_CB_ERROR                              /*  377 */\n#endif\n#ifdef SSL_R_VERSION_TOO_LOW\n            || n == SSL_R_VERSION_TOO_LOW                            /*  396 */\n#endif\n#ifdef SSL_R_TOO_MANY_WARN_ALERTS\n            || n == SSL_R_TOO_MANY_WARN_ALERTS                       /*  409 */\n#endif\n#ifdef SSL_R_BAD_RECORD_TYPE\n            || n == SSL_R_BAD_RECORD_TYPE                            /*  443 */\n#endif\n            || n == 1000 /* SSL_R_SSLV3_ALERT_CLOSE_NOTIFY */\n#ifdef SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE\n            || n == SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE             /* 1010 */\n            || n == SSL_R_SSLV3_ALERT_BAD_RECORD_MAC                 /* 1020 */\n            || n == SSL_R_TLSV1_ALERT_DECRYPTION_FAILED              /* 1021 */\n            || n == SSL_R_TLSV1_ALERT_RECORD_OVERFLOW                /* 1022 */\n            || n == SSL_R_SSLV3_ALERT_DECOMPRESSION_FAILURE          /* 1030 */\n            || n == SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE              /* 1040 */\n            || n == SSL_R_SSLV3_ALERT_NO_CERTIFICATE                 /* 1041 */\n            || n == SSL_R_SSLV3_ALERT_BAD_CERTIFICATE                /* 1042 */\n            || n == SSL_R_SSLV3_ALERT_UNSUPPORTED_CERTIFICATE        /* 1043 */\n            || n == SSL_R_SSLV3_ALERT_CERTIFICATE_REVOKED            /* 1044 */\n            || n == SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED            /* 1045 */\n            || n == SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN            /* 1046 */\n            || n == SSL_R_SSLV3_ALERT_ILLEGAL_PARAMETER              /* 1047 */\n            || n == SSL_R_TLSV1_ALERT_UNKNOWN_CA                     /* 1048 */\n            || n == SSL_R_TLSV1_ALERT_ACCESS_DENIED                  /* 1049 */\n            || n == SSL_R_TLSV1_ALERT_DECODE_ERROR                   /* 1050 */\n            || n == SSL_R_TLSV1_ALERT_DECRYPT_ERROR                  /* 1051 */\n            || n == SSL_R_TLSV1_ALERT_EXPORT_RESTRICTION             /* 1060 */\n            || n == SSL_R_TLSV1_ALERT_PROTOCOL_VERSION               /* 1070 */\n            || n == SSL_R_TLSV1_ALERT_INSUFFICIENT_SECURITY          /* 1071 */\n            || n == SSL_R_TLSV1_ALERT_INTERNAL_ERROR                 /* 1080 */\n            || n == SSL_R_TLSV1_ALERT_USER_CANCELLED                 /* 1090 */\n            || n == SSL_R_TLSV1_ALERT_NO_RENEGOTIATION               /* 1100 */\n#endif\n            )\n        {\n            switch (c->log_error) {\n\n            case NGX_ERROR_IGNORE_ECONNRESET:\n            case NGX_ERROR_INFO:\n                level = NGX_LOG_INFO;\n                break;\n\n            case NGX_ERROR_ERR:\n                level = NGX_LOG_ERR;\n                break;\n\n            default:\n                break;\n            }\n        }\n    }\n\n    ngx_ssl_error(level, c->log, err, text);\n}\n\n\nstatic void\nngx_ssl_clear_error(ngx_log_t *log)\n{\n    while (ERR_peek_error()) {\n        ngx_ssl_error(NGX_LOG_ALERT, log, 0, \"ignoring stale global SSL error\");\n    }\n\n    ERR_clear_error();\n}\n\n\nvoid ngx_cdecl\nngx_ssl_error(ngx_uint_t level, ngx_log_t *log, ngx_err_t err, char *fmt, ...)\n{\n    int          flags;\n    u_long       n;\n    va_list      args;\n    u_char      *p, *last;\n    u_char       errstr[NGX_MAX_CONF_ERRSTR];\n    const char  *data;\n\n    last = errstr + NGX_MAX_CONF_ERRSTR;\n\n    va_start(args, fmt);\n    p = ngx_vslprintf(errstr, last - 1, fmt, args);\n    va_end(args);\n\n    if (ERR_peek_error()) {\n        p = ngx_cpystrn(p, (u_char *) \" (SSL:\", last - p);\n\n        for ( ;; ) {\n\n            n = ERR_peek_error_data(&data, &flags);\n\n            if (n == 0) {\n                break;\n            }\n\n            /* ERR_error_string_n() requires at least one byte */\n\n            if (p >= last - 1) {\n                goto next;\n            }\n\n            *p++ = ' ';\n\n            ERR_error_string_n(n, (char *) p, last - p);\n\n            while (p < last && *p) {\n                p++;\n            }\n\n            if (p < last && *data && (flags & ERR_TXT_STRING)) {\n                *p++ = ':';\n                p = ngx_cpystrn(p, (u_char *) data, last - p);\n            }\n\n        next:\n\n            (void) ERR_get_error();\n        }\n\n        if (p < last) {\n            *p++ = ')';\n        }\n    }\n\n    ngx_log_error(level, log, err, \"%*s\", p - errstr, errstr);\n}\n\n\nngx_int_t\nngx_ssl_session_cache(ngx_ssl_t *ssl, ngx_str_t *sess_ctx,\n    ngx_array_t *certificates, ssize_t builtin_session_cache,\n    ngx_shm_zone_t *shm_zone, time_t timeout)\n{\n    long  cache_mode;\n\n    SSL_CTX_set_timeout(ssl->ctx, (long) timeout);\n\n    if (ngx_ssl_session_id_context(ssl, sess_ctx, certificates) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    if (builtin_session_cache == NGX_SSL_NO_SCACHE) {\n        SSL_CTX_set_session_cache_mode(ssl->ctx, SSL_SESS_CACHE_OFF);\n        return NGX_OK;\n    }\n\n    if (builtin_session_cache == NGX_SSL_NONE_SCACHE) {\n\n        /*\n         * If the server explicitly says that it does not support\n         * session reuse (see SSL_SESS_CACHE_OFF above), then\n         * Outlook Express fails to upload a sent email to\n         * the Sent Items folder on the IMAP server via a separate IMAP\n         * connection in the background.  Therefore we have a special\n         * mode (SSL_SESS_CACHE_SERVER|SSL_SESS_CACHE_NO_INTERNAL_STORE)\n         * where the server pretends that it supports session reuse,\n         * but it does not actually store any session.\n         */\n\n        SSL_CTX_set_session_cache_mode(ssl->ctx,\n                                       SSL_SESS_CACHE_SERVER\n                                       |SSL_SESS_CACHE_NO_AUTO_CLEAR\n                                       |SSL_SESS_CACHE_NO_INTERNAL_STORE);\n\n        SSL_CTX_sess_set_cache_size(ssl->ctx, 1);\n\n        return NGX_OK;\n    }\n\n    cache_mode = SSL_SESS_CACHE_SERVER;\n\n    if (shm_zone && builtin_session_cache == NGX_SSL_NO_BUILTIN_SCACHE) {\n        cache_mode |= SSL_SESS_CACHE_NO_INTERNAL;\n    }\n\n    SSL_CTX_set_session_cache_mode(ssl->ctx, cache_mode);\n\n    if (builtin_session_cache != NGX_SSL_NO_BUILTIN_SCACHE) {\n\n        if (builtin_session_cache != NGX_SSL_DFLT_BUILTIN_SCACHE) {\n            SSL_CTX_sess_set_cache_size(ssl->ctx, builtin_session_cache);\n        }\n    }\n\n    if (shm_zone) {\n        SSL_CTX_sess_set_new_cb(ssl->ctx, ngx_ssl_new_session);\n        SSL_CTX_sess_set_get_cb(ssl->ctx, ngx_ssl_get_cached_session);\n        SSL_CTX_sess_set_remove_cb(ssl->ctx, ngx_ssl_remove_session);\n\n        if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_session_cache_index, shm_zone)\n            == 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\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_ssl_session_id_context(ngx_ssl_t *ssl, ngx_str_t *sess_ctx,\n    ngx_array_t *certificates)\n{\n    int                   n, i;\n    X509                 *cert;\n    X509_NAME            *name;\n    ngx_str_t            *certs;\n    ngx_uint_t            k;\n    EVP_MD_CTX           *md;\n    unsigned int          len;\n    STACK_OF(X509_NAME)  *list;\n    u_char                buf[EVP_MAX_MD_SIZE];\n\n    /*\n     * Session ID context is set based on the string provided,\n     * the server certificates, and the client CA list.\n     */\n\n    md = EVP_MD_CTX_create();\n    if (md == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (EVP_DigestInit_ex(md, EVP_sha1(), NULL) == 0) {\n        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,\n                      \"EVP_DigestInit_ex() failed\");\n        goto failed;\n    }\n\n    if (EVP_DigestUpdate(md, sess_ctx->data, sess_ctx->len) == 0) {\n        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,\n                      \"EVP_DigestUpdate() failed\");\n        goto failed;\n    }\n\n    for (cert = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index);\n         cert;\n         cert = X509_get_ex_data(cert, ngx_ssl_next_certificate_index))\n    {\n        if (X509_digest(cert, EVP_sha1(), buf, &len) == 0) {\n            ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,\n                          \"X509_digest() failed\");\n            goto failed;\n        }\n\n        if (EVP_DigestUpdate(md, buf, len) == 0) {\n            ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,\n                          \"EVP_DigestUpdate() failed\");\n            goto failed;\n        }\n    }\n\n    if (SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index) == NULL\n        && certificates != NULL)\n    {\n        /*\n         * If certificates are loaded dynamically, we use certificate\n         * names as specified in the configuration (with variables).\n         */\n\n        certs = certificates->elts;\n        for (k = 0; k < certificates->nelts; k++) {\n\n            if (EVP_DigestUpdate(md, certs[k].data, certs[k].len) == 0) {\n                ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,\n                              \"EVP_DigestUpdate() failed\");\n                goto failed;\n            }\n        }\n    }\n\n    list = SSL_CTX_get_client_CA_list(ssl->ctx);\n\n    if (list != NULL) {\n        n = sk_X509_NAME_num(list);\n\n        for (i = 0; i < n; i++) {\n            name = sk_X509_NAME_value(list, i);\n\n            if (X509_NAME_digest(name, EVP_sha1(), buf, &len) == 0) {\n                ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,\n                              \"X509_NAME_digest() failed\");\n                goto failed;\n            }\n\n            if (EVP_DigestUpdate(md, buf, len) == 0) {\n                ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,\n                              \"EVP_DigestUpdate() failed\");\n                goto failed;\n            }\n        }\n    }\n\n    if (EVP_DigestFinal_ex(md, buf, &len) == 0) {\n        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,\n                      \"EVP_DigestFinal_ex() failed\");\n        goto failed;\n    }\n\n    EVP_MD_CTX_destroy(md);\n\n    if (SSL_CTX_set_session_id_context(ssl->ctx, buf, len) == 0) {\n        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,\n                      \"SSL_CTX_set_session_id_context() failed\");\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n\nfailed:\n\n    EVP_MD_CTX_destroy(md);\n\n    return NGX_ERROR;\n}\n\n\nngx_int_t\nngx_ssl_session_cache_init(ngx_shm_zone_t *shm_zone, void *data)\n{\n    size_t                    len;\n    ngx_slab_pool_t          *shpool;\n    ngx_ssl_session_cache_t  *cache;\n\n    if (data) {\n        shm_zone->data = data;\n        return NGX_OK;\n    }\n\n    shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;\n\n    if (shm_zone->shm.exists) {\n        shm_zone->data = shpool->data;\n        return NGX_OK;\n    }\n\n    cache = ngx_slab_alloc(shpool, sizeof(ngx_ssl_session_cache_t));\n    if (cache == NULL) {\n        return NGX_ERROR;\n    }\n\n    shpool->data = cache;\n    shm_zone->data = cache;\n\n    ngx_rbtree_init(&cache->session_rbtree, &cache->sentinel,\n                    ngx_ssl_session_rbtree_insert_value);\n\n    ngx_queue_init(&cache->expire_queue);\n\n    cache->ticket_keys[0].expire = 0;\n    cache->ticket_keys[1].expire = 0;\n    cache->ticket_keys[2].expire = 0;\n\n    cache->fail_time = 0;\n\n    len = sizeof(\" in SSL session shared cache \\\"\\\"\") + shm_zone->shm.name.len;\n\n    shpool->log_ctx = ngx_slab_alloc(shpool, len);\n    if (shpool->log_ctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_sprintf(shpool->log_ctx, \" in SSL session shared cache \\\"%V\\\"%Z\",\n                &shm_zone->shm.name);\n\n    shpool->log_nomem = 0;\n\n    return NGX_OK;\n}\n\n\n/*\n * The length of the session id is 16 bytes for SSLv2 sessions and\n * between 1 and 32 bytes for SSLv3 and TLS, typically 32 bytes.\n * Typical length of the external ASN1 representation of a session\n * is about 150 bytes plus SNI server name.\n *\n * On 32-bit platforms we allocate an rbtree node, a session id, and\n * an ASN1 representation in a single allocation, it typically takes\n * 256 bytes.\n *\n * On 64-bit platforms we allocate separately an rbtree node + session_id,\n * and an ASN1 representation, they take accordingly 128 and 256 bytes.\n *\n * OpenSSL's i2d_SSL_SESSION() and d2i_SSL_SESSION are slow,\n * so they are outside the code locked by shared pool mutex\n */\n\nstatic int\nngx_ssl_new_session(ngx_ssl_conn_t *ssl_conn, ngx_ssl_session_t *sess)\n{\n    int                       len;\n    u_char                   *p, *session_id;\n    size_t                    n;\n    uint32_t                  hash;\n    SSL_CTX                  *ssl_ctx;\n    unsigned int              session_id_length;\n    ngx_shm_zone_t           *shm_zone;\n    ngx_connection_t         *c;\n    ngx_slab_pool_t          *shpool;\n    ngx_ssl_sess_id_t        *sess_id;\n    ngx_ssl_session_cache_t  *cache;\n    u_char                    buf[NGX_SSL_MAX_SESSION_SIZE];\n\n#ifdef TLS1_3_VERSION\n\n    /*\n     * OpenSSL tries to save TLSv1.3 sessions into session cache\n     * even when using tickets for stateless session resumption,\n     * \"because some applications just want to know about the creation\n     * of a session\"; do not cache such sessions\n     */\n\n    if (SSL_version(ssl_conn) == TLS1_3_VERSION\n        && (SSL_get_options(ssl_conn) & SSL_OP_NO_TICKET) == 0)\n    {\n        return 0;\n    }\n\n#endif\n\n    len = i2d_SSL_SESSION(sess, NULL);\n\n    /* do not cache too big session */\n\n    if (len > NGX_SSL_MAX_SESSION_SIZE) {\n        return 0;\n    }\n\n    p = buf;\n    i2d_SSL_SESSION(sess, &p);\n\n    session_id = (u_char *) SSL_SESSION_get_id(sess, &session_id_length);\n\n    /* do not cache sessions with too long session id */\n\n    if (session_id_length > 32) {\n        return 0;\n    }\n\n    c = ngx_ssl_get_connection(ssl_conn);\n\n    ssl_ctx = c->ssl->session_ctx;\n    shm_zone = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_session_cache_index);\n\n    cache = shm_zone->data;\n    shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;\n\n    ngx_shmtx_lock(&shpool->mutex);\n\n    /* drop one or two expired sessions */\n    ngx_ssl_expire_sessions(cache, shpool, 1);\n\n#if (NGX_PTR_SIZE == 8)\n    n = sizeof(ngx_ssl_sess_id_t);\n#else\n    n = offsetof(ngx_ssl_sess_id_t, session) + len;\n#endif\n\n    sess_id = ngx_slab_alloc_locked(shpool, n);\n\n    if (sess_id == NULL) {\n\n        /* drop the oldest non-expired session and try once more */\n\n        ngx_ssl_expire_sessions(cache, shpool, 0);\n\n        sess_id = ngx_slab_alloc_locked(shpool, n);\n\n        if (sess_id == NULL) {\n            goto failed;\n        }\n    }\n\n#if (NGX_PTR_SIZE == 8)\n\n    sess_id->session = ngx_slab_alloc_locked(shpool, len);\n\n    if (sess_id->session == NULL) {\n\n        /* drop the oldest non-expired session and try once more */\n\n        ngx_ssl_expire_sessions(cache, shpool, 0);\n\n        sess_id->session = ngx_slab_alloc_locked(shpool, len);\n\n        if (sess_id->session == NULL) {\n            goto failed;\n        }\n    }\n\n#endif\n\n    ngx_memcpy(sess_id->session, buf, len);\n    ngx_memcpy(sess_id->id, session_id, session_id_length);\n\n    hash = ngx_crc32_short(session_id, session_id_length);\n\n    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"ssl new session: %08XD:%ud:%d\",\n                   hash, session_id_length, len);\n\n    sess_id->node.key = hash;\n    sess_id->node.data = (u_char) session_id_length;\n    sess_id->len = len;\n\n    sess_id->expire = ngx_time() + SSL_CTX_get_timeout(ssl_ctx);\n\n    ngx_queue_insert_head(&cache->expire_queue, &sess_id->queue);\n\n    ngx_rbtree_insert(&cache->session_rbtree, &sess_id->node);\n\n    ngx_shmtx_unlock(&shpool->mutex);\n\n    return 0;\n\nfailed:\n\n    if (sess_id) {\n        ngx_slab_free_locked(shpool, sess_id);\n    }\n\n    ngx_shmtx_unlock(&shpool->mutex);\n\n    if (cache->fail_time != ngx_time()) {\n        cache->fail_time = ngx_time();\n        ngx_log_error(NGX_LOG_WARN, c->log, 0,\n                      \"could not allocate new session%s\", shpool->log_ctx);\n    }\n\n    return 0;\n}\n\n\nstatic ngx_ssl_session_t *\nngx_ssl_get_cached_session(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    size_t                    slen;\n    uint32_t                  hash;\n    ngx_int_t                 rc;\n    const u_char             *p;\n    ngx_shm_zone_t           *shm_zone;\n    ngx_slab_pool_t          *shpool;\n    ngx_rbtree_node_t        *node, *sentinel;\n    ngx_ssl_session_t        *sess;\n    ngx_ssl_sess_id_t        *sess_id;\n    ngx_ssl_session_cache_t  *cache;\n    u_char                    buf[NGX_SSL_MAX_SESSION_SIZE];\n    ngx_connection_t         *c;\n\n    hash = ngx_crc32_short((u_char *) (uintptr_t) id, (size_t) len);\n    *copy = 0;\n\n    c = ngx_ssl_get_connection(ssl_conn);\n\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"ssl get session: %08XD:%d\", hash, len);\n\n    shm_zone = SSL_CTX_get_ex_data(c->ssl->session_ctx,\n                                   ngx_ssl_session_cache_index);\n\n    cache = shm_zone->data;\n\n    sess = NULL;\n\n    shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;\n\n    ngx_shmtx_lock(&shpool->mutex);\n\n    node = cache->session_rbtree.root;\n    sentinel = cache->session_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        sess_id = (ngx_ssl_sess_id_t *) node;\n\n        rc = ngx_memn2cmp((u_char *) (uintptr_t) id, sess_id->id,\n                          (size_t) len, (size_t) node->data);\n\n        if (rc == 0) {\n\n            if (sess_id->expire > ngx_time()) {\n                slen = sess_id->len;\n\n                ngx_memcpy(buf, sess_id->session, slen);\n\n                ngx_shmtx_unlock(&shpool->mutex);\n\n                p = buf;\n                sess = d2i_SSL_SESSION(NULL, &p, slen);\n\n                return sess;\n            }\n\n            ngx_queue_remove(&sess_id->queue);\n\n            ngx_rbtree_delete(&cache->session_rbtree, node);\n\n            ngx_explicit_memzero(sess_id->session, sess_id->len);\n\n#if (NGX_PTR_SIZE == 8)\n            ngx_slab_free_locked(shpool, sess_id->session);\n#endif\n            ngx_slab_free_locked(shpool, sess_id);\n\n            sess = NULL;\n\n            goto done;\n        }\n\n        node = (rc < 0) ? node->left : node->right;\n    }\n\ndone:\n\n    ngx_shmtx_unlock(&shpool->mutex);\n\n    return sess;\n}\n\n\nvoid\nngx_ssl_remove_cached_session(SSL_CTX *ssl, ngx_ssl_session_t *sess)\n{\n    SSL_CTX_remove_session(ssl, sess);\n\n    ngx_ssl_remove_session(ssl, sess);\n}\n\n\nstatic void\nngx_ssl_remove_session(SSL_CTX *ssl, ngx_ssl_session_t *sess)\n{\n    u_char                   *id;\n    uint32_t                  hash;\n    ngx_int_t                 rc;\n    unsigned int              len;\n    ngx_shm_zone_t           *shm_zone;\n    ngx_slab_pool_t          *shpool;\n    ngx_rbtree_node_t        *node, *sentinel;\n    ngx_ssl_sess_id_t        *sess_id;\n    ngx_ssl_session_cache_t  *cache;\n\n    shm_zone = SSL_CTX_get_ex_data(ssl, ngx_ssl_session_cache_index);\n\n    if (shm_zone == NULL) {\n        return;\n    }\n\n    cache = shm_zone->data;\n\n    id = (u_char *) SSL_SESSION_get_id(sess, &len);\n\n    hash = ngx_crc32_short(id, len);\n\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ngx_cycle->log, 0,\n                   \"ssl remove session: %08XD:%ud\", hash, len);\n\n    shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;\n\n    ngx_shmtx_lock(&shpool->mutex);\n\n    node = cache->session_rbtree.root;\n    sentinel = cache->session_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        sess_id = (ngx_ssl_sess_id_t *) node;\n\n        rc = ngx_memn2cmp(id, sess_id->id, len, (size_t) node->data);\n\n        if (rc == 0) {\n\n            ngx_queue_remove(&sess_id->queue);\n\n            ngx_rbtree_delete(&cache->session_rbtree, node);\n\n            ngx_explicit_memzero(sess_id->session, sess_id->len);\n\n#if (NGX_PTR_SIZE == 8)\n            ngx_slab_free_locked(shpool, sess_id->session);\n#endif\n            ngx_slab_free_locked(shpool, sess_id);\n\n            goto done;\n        }\n\n        node = (rc < 0) ? node->left : node->right;\n    }\n\ndone:\n\n    ngx_shmtx_unlock(&shpool->mutex);\n}\n\n\nstatic void\nngx_ssl_expire_sessions(ngx_ssl_session_cache_t *cache,\n    ngx_slab_pool_t *shpool, ngx_uint_t n)\n{\n    time_t              now;\n    ngx_queue_t        *q;\n    ngx_ssl_sess_id_t  *sess_id;\n\n    now = ngx_time();\n\n    while (n < 3) {\n\n        if (ngx_queue_empty(&cache->expire_queue)) {\n            return;\n        }\n\n        q = ngx_queue_last(&cache->expire_queue);\n\n        sess_id = ngx_queue_data(q, ngx_ssl_sess_id_t, queue);\n\n        if (n++ != 0 && sess_id->expire > now) {\n            return;\n        }\n\n        ngx_queue_remove(q);\n\n        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ngx_cycle->log, 0,\n                       \"expire session: %08Xi\", sess_id->node.key);\n\n        ngx_rbtree_delete(&cache->session_rbtree, &sess_id->node);\n\n        ngx_explicit_memzero(sess_id->session, sess_id->len);\n\n#if (NGX_PTR_SIZE == 8)\n        ngx_slab_free_locked(shpool, sess_id->session);\n#endif\n        ngx_slab_free_locked(shpool, sess_id);\n    }\n}\n\n\nstatic void\nngx_ssl_session_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_ssl_sess_id_t   *sess_id, *sess_id_temp;\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            sess_id = (ngx_ssl_sess_id_t *) node;\n            sess_id_temp = (ngx_ssl_sess_id_t *) temp;\n\n            p = (ngx_memn2cmp(sess_id->id, sess_id_temp->id,\n                              (size_t) node->data, (size_t) temp->data)\n                 < 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\n#ifdef SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB\n\nngx_int_t\nngx_ssl_session_ticket_keys(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_array_t *paths)\n{\n    u_char                 buf[80];\n    size_t                 size;\n    ssize_t                n;\n    ngx_str_t             *path;\n    ngx_file_t             file;\n    ngx_uint_t             i;\n    ngx_array_t           *keys;\n    ngx_file_info_t        fi;\n    ngx_pool_cleanup_t    *cln;\n    ngx_ssl_ticket_key_t  *key;\n\n    if (paths == NULL\n        && SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_session_cache_index) == NULL)\n    {\n        return NGX_OK;\n    }\n\n    keys = ngx_array_create(cf->pool, paths ? paths->nelts : 3,\n                            sizeof(ngx_ssl_ticket_key_t));\n    if (keys == NULL) {\n        return NGX_ERROR;\n    }\n\n    cln = ngx_pool_cleanup_add(cf->pool, 0);\n    if (cln == NULL) {\n        return NGX_ERROR;\n    }\n\n    cln->handler = ngx_ssl_ticket_keys_cleanup;\n    cln->data = keys;\n\n    if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_ticket_keys_index, keys) == 0) {\n        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,\n                      \"SSL_CTX_set_ex_data() failed\");\n        return NGX_ERROR;\n    }\n\n    if (SSL_CTX_set_tlsext_ticket_key_cb(ssl->ctx, ngx_ssl_ticket_key_callback)\n        == 0)\n    {\n        ngx_log_error(NGX_LOG_WARN, cf->log, 0,\n                      \"nginx was built with Session Tickets support, however, \"\n                      \"now it is linked dynamically to an OpenSSL library \"\n                      \"which has no tlsext support, therefore Session Tickets \"\n                      \"are not available\");\n        return NGX_OK;\n    }\n\n    if (paths == NULL) {\n\n        /* placeholder for keys in shared memory */\n\n        key = ngx_array_push_n(keys, 3);\n        key[0].shared = 1;\n        key[0].expire = 0;\n        key[1].shared = 1;\n        key[1].expire = 0;\n        key[2].shared = 1;\n        key[2].expire = 0;\n\n        return NGX_OK;\n    }\n\n    path = paths->elts;\n    for (i = 0; i < paths->nelts; i++) {\n\n        if (ngx_conf_full_name(cf->cycle, &path[i], 1) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        ngx_memzero(&file, sizeof(ngx_file_t));\n        file.name = path[i];\n        file.log = cf->log;\n\n        file.fd = ngx_open_file(file.name.data, NGX_FILE_RDONLY,\n                                NGX_FILE_OPEN, 0);\n\n        if (file.fd == NGX_INVALID_FILE) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno,\n                               ngx_open_file_n \" \\\"%V\\\" failed\", &file.name);\n            return NGX_ERROR;\n        }\n\n        if (ngx_fd_info(file.fd, &fi) == NGX_FILE_ERROR) {\n            ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,\n                               ngx_fd_info_n \" \\\"%V\\\" failed\", &file.name);\n            goto failed;\n        }\n\n        size = ngx_file_size(&fi);\n\n        if (size != 48 && size != 80) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"\\\"%V\\\" must be 48 or 80 bytes\", &file.name);\n            goto failed;\n        }\n\n        n = ngx_read_file(&file, buf, size, 0);\n\n        if (n == NGX_ERROR) {\n            ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,\n                               ngx_read_file_n \" \\\"%V\\\" failed\", &file.name);\n            goto failed;\n        }\n\n        if ((size_t) n != size) {\n            ngx_conf_log_error(NGX_LOG_CRIT, cf, 0,\n                               ngx_read_file_n \" \\\"%V\\\" returned only \"\n                               \"%z bytes instead of %uz\", &file.name, n, size);\n            goto failed;\n        }\n\n        key = ngx_array_push(keys);\n        if (key == NULL) {\n            goto failed;\n        }\n\n        key->shared = 0;\n        key->expire = 1;\n\n        if (size == 48) {\n            key->size = 48;\n            ngx_memcpy(key->name, buf, 16);\n            ngx_memcpy(key->aes_key, buf + 16, 16);\n            ngx_memcpy(key->hmac_key, buf + 32, 16);\n\n        } else {\n            key->size = 80;\n            ngx_memcpy(key->name, buf, 16);\n            ngx_memcpy(key->hmac_key, buf + 16, 32);\n            ngx_memcpy(key->aes_key, buf + 48, 32);\n        }\n\n        if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {\n            ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,\n                          ngx_close_file_n \" \\\"%V\\\" failed\", &file.name);\n        }\n\n        ngx_explicit_memzero(&buf, 80);\n    }\n\n    return NGX_OK;\n\nfailed:\n\n    if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {\n        ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,\n                      ngx_close_file_n \" \\\"%V\\\" failed\", &file.name);\n    }\n\n    ngx_explicit_memzero(&buf, 80);\n\n    return NGX_ERROR;\n}\n\n\nstatic int\nngx_ssl_ticket_key_callback(ngx_ssl_conn_t *ssl_conn,\n    unsigned char *name, unsigned char *iv, EVP_CIPHER_CTX *ectx,\n    HMAC_CTX *hctx, int enc)\n{\n    size_t                 size;\n    SSL_CTX               *ssl_ctx;\n    ngx_uint_t             i;\n    ngx_array_t           *keys;\n    ngx_connection_t      *c;\n    ngx_ssl_ticket_key_t  *key;\n    const EVP_MD          *digest;\n    const EVP_CIPHER      *cipher;\n\n    c = ngx_ssl_get_connection(ssl_conn);\n    ssl_ctx = c->ssl->session_ctx;\n\n    if (ngx_ssl_rotate_ticket_keys(ssl_ctx, c->log) != NGX_OK) {\n        return -1;\n    }\n\n#ifdef OPENSSL_NO_SHA256\n    digest = EVP_sha1();\n#else\n    digest = EVP_sha256();\n#endif\n\n    keys = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_ticket_keys_index);\n    if (keys == NULL) {\n        return -1;\n    }\n\n    key = keys->elts;\n\n    if (enc == 1) {\n        /* encrypt session ticket */\n\n        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"ssl ticket encrypt, key: \\\"%*xs\\\" (%s session)\",\n                       (size_t) 16, key[0].name,\n                       SSL_session_reused(ssl_conn) ? \"reused\" : \"new\");\n\n        if (key[0].size == 48) {\n            cipher = EVP_aes_128_cbc();\n            size = 16;\n\n        } else {\n            cipher = EVP_aes_256_cbc();\n            size = 32;\n        }\n\n        if (RAND_bytes(iv, EVP_CIPHER_iv_length(cipher)) != 1) {\n            ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, \"RAND_bytes() failed\");\n            return -1;\n        }\n\n        if (EVP_EncryptInit_ex(ectx, cipher, NULL, key[0].aes_key, iv) != 1) {\n            ngx_ssl_error(NGX_LOG_ALERT, c->log, 0,\n                          \"EVP_EncryptInit_ex() failed\");\n            return -1;\n        }\n\n#if OPENSSL_VERSION_NUMBER >= 0x10000000L\n        if (HMAC_Init_ex(hctx, key[0].hmac_key, size, digest, NULL) != 1) {\n            ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, \"HMAC_Init_ex() failed\");\n            return -1;\n        }\n#else\n        HMAC_Init_ex(hctx, key[0].hmac_key, size, digest, NULL);\n#endif\n\n        ngx_memcpy(name, key[0].name, 16);\n\n        return 1;\n\n    } else {\n        /* decrypt session ticket */\n\n        for (i = 0; i < keys->nelts; i++) {\n            if (ngx_memcmp(name, key[i].name, 16) == 0) {\n                goto found;\n            }\n        }\n\n        ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"ssl ticket decrypt, key: \\\"%*xs\\\" not found\",\n                       (size_t) 16, name);\n\n        return 0;\n\n    found:\n\n        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"ssl ticket decrypt, key: \\\"%*xs\\\"%s\",\n                       (size_t) 16, key[i].name, (i == 0) ? \" (default)\" : \"\");\n\n        if (key[i].size == 48) {\n            cipher = EVP_aes_128_cbc();\n            size = 16;\n\n        } else {\n            cipher = EVP_aes_256_cbc();\n            size = 32;\n        }\n\n#if OPENSSL_VERSION_NUMBER >= 0x10000000L\n        if (HMAC_Init_ex(hctx, key[i].hmac_key, size, digest, NULL) != 1) {\n            ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, \"HMAC_Init_ex() failed\");\n            return -1;\n        }\n#else\n        HMAC_Init_ex(hctx, key[i].hmac_key, size, digest, NULL);\n#endif\n\n        if (EVP_DecryptInit_ex(ectx, cipher, NULL, key[i].aes_key, iv) != 1) {\n            ngx_ssl_error(NGX_LOG_ALERT, c->log, 0,\n                          \"EVP_DecryptInit_ex() failed\");\n            return -1;\n        }\n\n        /* renew if TLSv1.3 */\n\n#ifdef TLS1_3_VERSION\n        if (SSL_version(ssl_conn) == TLS1_3_VERSION) {\n            return 2;\n        }\n#endif\n\n        /* renew if non-default key */\n\n        if (i != 0 && key[i].expire) {\n            return 2;\n        }\n\n        return 1;\n    }\n}\n\n\nstatic ngx_int_t\nngx_ssl_rotate_ticket_keys(SSL_CTX *ssl_ctx, ngx_log_t *log)\n{\n    time_t                    now, expire;\n    ngx_array_t              *keys;\n    ngx_shm_zone_t           *shm_zone;\n    ngx_slab_pool_t          *shpool;\n    ngx_ssl_ticket_key_t     *key;\n    ngx_ssl_session_cache_t  *cache;\n    u_char                    buf[80];\n\n    keys = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_ticket_keys_index);\n    if (keys == NULL) {\n        return NGX_OK;\n    }\n\n    key = keys->elts;\n\n    if (!key[0].shared) {\n        return NGX_OK;\n    }\n\n    /*\n     * if we don't need to update expiration of the current key\n     * and the previous key is still needed, don't sync with shared\n     * memory to save some work; in the worst case other worker process\n     * will switch to the next key, but this process will still be able\n     * to decrypt tickets encrypted with it\n     */\n\n    now = ngx_time();\n    expire = now + SSL_CTX_get_timeout(ssl_ctx);\n\n    if (key[0].expire >= expire && key[1].expire >= now) {\n        return NGX_OK;\n    }\n\n    shm_zone = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_session_cache_index);\n\n    cache = shm_zone->data;\n    shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;\n\n    ngx_shmtx_lock(&shpool->mutex);\n\n    key = cache->ticket_keys;\n\n    if (key[0].expire == 0) {\n\n        /* initialize the current key */\n\n        if (RAND_bytes(buf, 80) != 1) {\n            ngx_ssl_error(NGX_LOG_ALERT, log, 0, \"RAND_bytes() failed\");\n            ngx_shmtx_unlock(&shpool->mutex);\n            return NGX_ERROR;\n        }\n\n        key[0].shared = 1;\n        key[0].expire = expire;\n        key[0].size = 80;\n        ngx_memcpy(key[0].name, buf, 16);\n        ngx_memcpy(key[0].hmac_key, buf + 16, 32);\n        ngx_memcpy(key[0].aes_key, buf + 48, 32);\n\n        ngx_explicit_memzero(&buf, 80);\n\n        ngx_log_debug2(NGX_LOG_DEBUG_EVENT, log, 0,\n                       \"ssl ticket key: \\\"%*xs\\\"\",\n                       (size_t) 16, key[0].name);\n\n        /*\n         * copy the current key to the next key, as initialization of\n         * the previous key will replace the current key with the next\n         * key\n         */\n\n        key[2] = key[0];\n    }\n\n    if (key[1].expire < now) {\n\n        /*\n         * if the previous key is no longer needed (or not initialized),\n         * replace it with the current key, replace the current key with\n         * the next key, and generate new next key\n         */\n\n        key[1] = key[0];\n        key[0] = key[2];\n\n        if (RAND_bytes(buf, 80) != 1) {\n            ngx_ssl_error(NGX_LOG_ALERT, log, 0, \"RAND_bytes() failed\");\n            ngx_shmtx_unlock(&shpool->mutex);\n            return NGX_ERROR;\n        }\n\n        key[2].shared = 1;\n        key[2].expire = 0;\n        key[2].size = 80;\n        ngx_memcpy(key[2].name, buf, 16);\n        ngx_memcpy(key[2].hmac_key, buf + 16, 32);\n        ngx_memcpy(key[2].aes_key, buf + 48, 32);\n\n        ngx_explicit_memzero(&buf, 80);\n\n        ngx_log_debug2(NGX_LOG_DEBUG_EVENT, log, 0,\n                       \"ssl ticket key: \\\"%*xs\\\"\",\n                       (size_t) 16, key[2].name);\n    }\n\n    /*\n     * update expiration of the current key: it is going to be needed\n     * at least till the session being created expires\n     */\n\n    if (expire > key[0].expire) {\n        key[0].expire = expire;\n    }\n\n    /* sync keys to the worker process memory */\n\n    ngx_memcpy(keys->elts, cache->ticket_keys,\n               2 * sizeof(ngx_ssl_ticket_key_t));\n\n    ngx_shmtx_unlock(&shpool->mutex);\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_ssl_ticket_keys_cleanup(void *data)\n{\n    ngx_array_t  *keys = data;\n\n    ngx_explicit_memzero(keys->elts,\n                         keys->nelts * sizeof(ngx_ssl_ticket_key_t));\n}\n\n#else\n\nngx_int_t\nngx_ssl_session_ticket_keys(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_array_t *paths)\n{\n    if (paths) {\n        ngx_log_error(NGX_LOG_WARN, ssl->log, 0,\n                      \"\\\"ssl_session_ticket_key\\\" ignored, not supported\");\n    }\n\n    return NGX_OK;\n}\n\n#endif\n\n\nvoid\nngx_ssl_cleanup_ctx(void *data)\n{\n    ngx_ssl_t  *ssl = data;\n\n    X509  *cert, *next;\n\n    cert = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index);\n\n    while (cert) {\n        next = X509_get_ex_data(cert, ngx_ssl_next_certificate_index);\n        X509_free(cert);\n        cert = next;\n    }\n\n    SSL_CTX_free(ssl->ctx);\n}\n\n\nngx_int_t\nngx_ssl_check_host(ngx_connection_t *c, ngx_str_t *name)\n{\n    X509   *cert;\n\n    cert = SSL_get_peer_certificate(c->ssl->connection);\n    if (cert == NULL) {\n        return NGX_ERROR;\n    }\n\n#ifdef X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT\n\n    /* X509_check_host() is only available in OpenSSL 1.0.2+ */\n\n    if (name->len == 0) {\n        goto failed;\n    }\n\n    if (X509_check_host(cert, (char *) name->data, name->len, 0, NULL) != 1) {\n        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"X509_check_host(): no match\");\n        goto failed;\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"X509_check_host(): match\");\n\n    goto found;\n\n#else\n    {\n    int                      n, i;\n    X509_NAME               *sname;\n    ASN1_STRING             *str;\n    X509_NAME_ENTRY         *entry;\n    GENERAL_NAME            *altname;\n    STACK_OF(GENERAL_NAME)  *altnames;\n\n    /*\n     * As per RFC6125 and RFC2818, we check subjectAltName extension,\n     * and if it's not present - commonName in Subject is checked.\n     */\n\n    altnames = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);\n\n    if (altnames) {\n        n = sk_GENERAL_NAME_num(altnames);\n\n        for (i = 0; i < n; i++) {\n            altname = sk_GENERAL_NAME_value(altnames, i);\n\n            if (altname->type != GEN_DNS) {\n                continue;\n            }\n\n            str = altname->d.dNSName;\n\n            ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                           \"SSL subjectAltName: \\\"%*s\\\"\",\n                           ASN1_STRING_length(str), ASN1_STRING_data(str));\n\n            if (ngx_ssl_check_name(name, str) == NGX_OK) {\n                ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                               \"SSL subjectAltName: match\");\n                GENERAL_NAMES_free(altnames);\n                goto found;\n            }\n        }\n\n        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"SSL subjectAltName: no match\");\n\n        GENERAL_NAMES_free(altnames);\n        goto failed;\n    }\n\n    /*\n     * If there is no subjectAltName extension, check commonName\n     * in Subject.  While RFC2818 requires to only check \"most specific\"\n     * CN, both Apache and OpenSSL check all CNs, and so do we.\n     */\n\n    sname = X509_get_subject_name(cert);\n\n    if (sname == NULL) {\n        goto failed;\n    }\n\n    i = -1;\n    for ( ;; ) {\n        i = X509_NAME_get_index_by_NID(sname, NID_commonName, i);\n\n        if (i < 0) {\n            break;\n        }\n\n        entry = X509_NAME_get_entry(sname, i);\n        str = X509_NAME_ENTRY_get_data(entry);\n\n        ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"SSL commonName: \\\"%*s\\\"\",\n                       ASN1_STRING_length(str), ASN1_STRING_data(str));\n\n        if (ngx_ssl_check_name(name, str) == NGX_OK) {\n            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                           \"SSL commonName: match\");\n            goto found;\n        }\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"SSL commonName: no match\");\n    }\n#endif\n\nfailed:\n\n    X509_free(cert);\n    return NGX_ERROR;\n\nfound:\n\n    X509_free(cert);\n    return NGX_OK;\n}\n\n\n#ifndef X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT\n\nstatic ngx_int_t\nngx_ssl_check_name(ngx_str_t *name, ASN1_STRING *pattern)\n{\n    u_char  *s, *p, *end;\n    size_t   slen, plen;\n\n    s = name->data;\n    slen = name->len;\n\n    p = ASN1_STRING_data(pattern);\n    plen = ASN1_STRING_length(pattern);\n\n    if (slen == plen && ngx_strncasecmp(s, p, plen) == 0) {\n        return NGX_OK;\n    }\n\n    if (plen > 2 && p[0] == '*' && p[1] == '.') {\n        plen -= 1;\n        p += 1;\n\n        end = s + slen;\n        s = ngx_strlchr(s, end, '.');\n\n        if (s == NULL) {\n            return NGX_ERROR;\n        }\n\n        slen = end - s;\n\n        if (plen == slen && ngx_strncasecmp(s, p, plen) == 0) {\n            return NGX_OK;\n        }\n    }\n\n    return NGX_ERROR;\n}\n\n#endif\n\n\nngx_int_t\nngx_ssl_get_protocol(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)\n{\n    s->data = (u_char *) SSL_get_version(c->ssl->connection);\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_ssl_get_cipher_name(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)\n{\n    s->data = (u_char *) SSL_get_cipher_name(c->ssl->connection);\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_ssl_get_ciphers(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)\n{\n#ifdef SSL_CTRL_GET_RAW_CIPHERLIST\n\n    int                n, i, bytes;\n    size_t             len;\n    u_char            *ciphers, *p;\n    const SSL_CIPHER  *cipher;\n\n    bytes = SSL_get0_raw_cipherlist(c->ssl->connection, NULL);\n    n = SSL_get0_raw_cipherlist(c->ssl->connection, &ciphers);\n\n    if (n <= 0) {\n        s->len = 0;\n        return NGX_OK;\n    }\n\n    len = 0;\n    n /= bytes;\n\n    for (i = 0; i < n; i++) {\n        cipher = SSL_CIPHER_find(c->ssl->connection, ciphers + i * bytes);\n\n        if (cipher) {\n            len += ngx_strlen(SSL_CIPHER_get_name(cipher));\n\n        } else {\n            len += sizeof(\"0x\") - 1 + bytes * (sizeof(\"00\") - 1);\n        }\n\n        len += sizeof(\":\") - 1;\n    }\n\n    s->data = ngx_pnalloc(pool, len);\n    if (s->data == NULL) {\n        return NGX_ERROR;\n    }\n\n    p = s->data;\n\n    for (i = 0; i < n; i++) {\n        cipher = SSL_CIPHER_find(c->ssl->connection, ciphers + i * bytes);\n\n        if (cipher) {\n            p = ngx_sprintf(p, \"%s\", SSL_CIPHER_get_name(cipher));\n\n        } else {\n            p = ngx_sprintf(p, \"0x\");\n            p = ngx_hex_dump(p, ciphers + i * bytes, bytes);\n        }\n\n        *p++ = ':';\n    }\n\n    p--;\n\n    s->len = p - s->data;\n\n#else\n\n    u_char  buf[4096];\n\n    if (SSL_get_shared_ciphers(c->ssl->connection, (char *) buf, 4096)\n        == NULL)\n    {\n        s->len = 0;\n        return NGX_OK;\n    }\n\n    s->len = ngx_strlen(buf);\n    s->data = ngx_pnalloc(pool, s->len);\n    if (s->data == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_memcpy(s->data, buf, s->len);\n\n#endif\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_ssl_get_curve(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)\n{\n#ifdef SSL_get_negotiated_group\n\n    int  nid;\n\n    nid = SSL_get_negotiated_group(c->ssl->connection);\n\n    if (nid != NID_undef) {\n\n        if ((nid & TLSEXT_nid_unknown) == 0) {\n            s->len = ngx_strlen(OBJ_nid2sn(nid));\n            s->data = (u_char *) OBJ_nid2sn(nid);\n            return NGX_OK;\n        }\n\n        s->len = sizeof(\"0x0000\") - 1;\n\n        s->data = ngx_pnalloc(pool, s->len);\n        if (s->data == NULL) {\n            return NGX_ERROR;\n        }\n\n        ngx_sprintf(s->data, \"0x%04xd\", nid & 0xffff);\n\n        return NGX_OK;\n    }\n\n#endif\n\n    s->len = 0;\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_ssl_get_curves(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)\n{\n#ifdef SSL_CTRL_GET_CURVES\n\n    int         *curves, n, i, nid;\n    u_char      *p;\n    size_t       len;\n\n    n = SSL_get1_curves(c->ssl->connection, NULL);\n\n    if (n <= 0) {\n        s->len = 0;\n        return NGX_OK;\n    }\n\n    curves = ngx_palloc(pool, n * sizeof(int));\n\n    n = SSL_get1_curves(c->ssl->connection, curves);\n    len = 0;\n\n    for (i = 0; i < n; i++) {\n        nid = curves[i];\n\n        if (nid & TLSEXT_nid_unknown) {\n            len += sizeof(\"0x0000\") - 1;\n\n        } else {\n            len += ngx_strlen(OBJ_nid2sn(nid));\n        }\n\n        len += sizeof(\":\") - 1;\n    }\n\n    s->data = ngx_pnalloc(pool, len);\n    if (s->data == NULL) {\n        return NGX_ERROR;\n    }\n\n    p = s->data;\n\n    for (i = 0; i < n; i++) {\n        nid = curves[i];\n\n        if (nid & TLSEXT_nid_unknown) {\n            p = ngx_sprintf(p, \"0x%04xd\", nid & 0xffff);\n\n        } else {\n            p = ngx_sprintf(p, \"%s\", OBJ_nid2sn(nid));\n        }\n\n        *p++ = ':';\n    }\n\n    p--;\n\n    s->len = p - s->data;\n\n#else\n\n    s->len = 0;\n\n#endif\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_ssl_get_session_id(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)\n{\n    u_char        *buf;\n    SSL_SESSION   *sess;\n    unsigned int   len;\n\n    sess = SSL_get0_session(c->ssl->connection);\n    if (sess == NULL) {\n        s->len = 0;\n        return NGX_OK;\n    }\n\n    buf = (u_char *) SSL_SESSION_get_id(sess, &len);\n\n    s->len = 2 * len;\n    s->data = ngx_pnalloc(pool, 2 * len);\n    if (s->data == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_hex_dump(s->data, buf, len);\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_ssl_get_session_reused(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)\n{\n    if (SSL_session_reused(c->ssl->connection)) {\n        ngx_str_set(s, \"r\");\n\n    } else {\n        ngx_str_set(s, \".\");\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_ssl_get_early_data(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)\n{\n    s->len = 0;\n\n#ifdef SSL_ERROR_EARLY_DATA_REJECTED\n\n    /* BoringSSL */\n\n    if (SSL_in_early_data(c->ssl->connection)) {\n        ngx_str_set(s, \"1\");\n    }\n\n#elif defined SSL_READ_EARLY_DATA_SUCCESS\n\n    /* OpenSSL */\n\n    if (!SSL_is_init_finished(c->ssl->connection)) {\n        ngx_str_set(s, \"1\");\n    }\n\n#endif\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_ssl_get_server_name(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)\n{\n#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME\n\n    size_t       len;\n    const char  *name;\n\n    name = SSL_get_servername(c->ssl->connection, TLSEXT_NAMETYPE_host_name);\n\n    if (name) {\n        len = ngx_strlen(name);\n\n        s->len = len;\n        s->data = ngx_pnalloc(pool, len);\n        if (s->data == NULL) {\n            return NGX_ERROR;\n        }\n\n        ngx_memcpy(s->data, name, len);\n\n        return NGX_OK;\n    }\n\n#endif\n\n    s->len = 0;\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_ssl_get_alpn_protocol(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)\n{\n#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation\n\n    unsigned int          len;\n    const unsigned char  *data;\n\n    SSL_get0_alpn_selected(c->ssl->connection, &data, &len);\n\n    if (len > 0) {\n\n        s->data = ngx_pnalloc(pool, len);\n        if (s->data == NULL) {\n            return NGX_ERROR;\n        }\n\n        ngx_memcpy(s->data, data, len);\n        s->len = len;\n\n        return NGX_OK;\n    }\n\n#endif\n\n    s->len = 0;\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_ssl_get_raw_certificate(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)\n{\n    size_t   len;\n    BIO     *bio;\n    X509    *cert;\n\n    s->len = 0;\n\n    cert = SSL_get_peer_certificate(c->ssl->connection);\n    if (cert == NULL) {\n        return NGX_OK;\n    }\n\n    bio = BIO_new(BIO_s_mem());\n    if (bio == NULL) {\n        ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, \"BIO_new() failed\");\n        X509_free(cert);\n        return NGX_ERROR;\n    }\n\n    if (PEM_write_bio_X509(bio, cert) == 0) {\n        ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, \"PEM_write_bio_X509() failed\");\n        goto failed;\n    }\n\n    len = BIO_pending(bio);\n    s->len = len;\n\n    s->data = ngx_pnalloc(pool, len);\n    if (s->data == NULL) {\n        goto failed;\n    }\n\n    BIO_read(bio, s->data, len);\n\n    BIO_free(bio);\n    X509_free(cert);\n\n    return NGX_OK;\n\nfailed:\n\n    BIO_free(bio);\n    X509_free(cert);\n\n    return NGX_ERROR;\n}\n\n\nngx_int_t\nngx_ssl_get_certificate(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)\n{\n    u_char      *p;\n    size_t       len;\n    ngx_uint_t   i;\n    ngx_str_t    cert;\n\n    if (ngx_ssl_get_raw_certificate(c, pool, &cert) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    if (cert.len == 0) {\n        s->len = 0;\n        return NGX_OK;\n    }\n\n    len = cert.len - 1;\n\n    for (i = 0; i < cert.len - 1; i++) {\n        if (cert.data[i] == LF) {\n            len++;\n        }\n    }\n\n    s->len = len;\n    s->data = ngx_pnalloc(pool, len);\n    if (s->data == NULL) {\n        return NGX_ERROR;\n    }\n\n    p = s->data;\n\n    for (i = 0; i < cert.len - 1; i++) {\n        *p++ = cert.data[i];\n        if (cert.data[i] == LF) {\n            *p++ = '\\t';\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_ssl_get_escaped_certificate(ngx_connection_t *c, ngx_pool_t *pool,\n    ngx_str_t *s)\n{\n    ngx_str_t  cert;\n    uintptr_t  n;\n\n    if (ngx_ssl_get_raw_certificate(c, pool, &cert) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    if (cert.len == 0) {\n        s->len = 0;\n        return NGX_OK;\n    }\n\n    n = ngx_escape_uri(NULL, cert.data, cert.len, NGX_ESCAPE_URI_COMPONENT);\n\n    s->len = cert.len + n * 2;\n    s->data = ngx_pnalloc(pool, s->len);\n    if (s->data == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_escape_uri(s->data, cert.data, cert.len, NGX_ESCAPE_URI_COMPONENT);\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_ssl_get_subject_dn(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)\n{\n    BIO        *bio;\n    X509       *cert;\n    X509_NAME  *name;\n\n    s->len = 0;\n\n    cert = SSL_get_peer_certificate(c->ssl->connection);\n    if (cert == NULL) {\n        return NGX_OK;\n    }\n\n    name = X509_get_subject_name(cert);\n    if (name == NULL) {\n        X509_free(cert);\n        return NGX_ERROR;\n    }\n\n    bio = BIO_new(BIO_s_mem());\n    if (bio == NULL) {\n        ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, \"BIO_new() failed\");\n        X509_free(cert);\n        return NGX_ERROR;\n    }\n\n    if (X509_NAME_print_ex(bio, name, 0, XN_FLAG_RFC2253) < 0) {\n        ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, \"X509_NAME_print_ex() failed\");\n        goto failed;\n    }\n\n    s->len = BIO_pending(bio);\n    s->data = ngx_pnalloc(pool, s->len);\n    if (s->data == NULL) {\n        goto failed;\n    }\n\n    BIO_read(bio, s->data, s->len);\n\n    BIO_free(bio);\n    X509_free(cert);\n\n    return NGX_OK;\n\nfailed:\n\n    BIO_free(bio);\n    X509_free(cert);\n\n    return NGX_ERROR;\n}\n\n\nngx_int_t\nngx_ssl_get_issuer_dn(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)\n{\n    BIO        *bio;\n    X509       *cert;\n    X509_NAME  *name;\n\n    s->len = 0;\n\n    cert = SSL_get_peer_certificate(c->ssl->connection);\n    if (cert == NULL) {\n        return NGX_OK;\n    }\n\n    name = X509_get_issuer_name(cert);\n    if (name == NULL) {\n        X509_free(cert);\n        return NGX_ERROR;\n    }\n\n    bio = BIO_new(BIO_s_mem());\n    if (bio == NULL) {\n        ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, \"BIO_new() failed\");\n        X509_free(cert);\n        return NGX_ERROR;\n    }\n\n    if (X509_NAME_print_ex(bio, name, 0, XN_FLAG_RFC2253) < 0) {\n        ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, \"X509_NAME_print_ex() failed\");\n        goto failed;\n    }\n\n    s->len = BIO_pending(bio);\n    s->data = ngx_pnalloc(pool, s->len);\n    if (s->data == NULL) {\n        goto failed;\n    }\n\n    BIO_read(bio, s->data, s->len);\n\n    BIO_free(bio);\n    X509_free(cert);\n\n    return NGX_OK;\n\nfailed:\n\n    BIO_free(bio);\n    X509_free(cert);\n\n    return NGX_ERROR;\n}\n\n\nngx_int_t\nngx_ssl_get_subject_dn_legacy(ngx_connection_t *c, ngx_pool_t *pool,\n    ngx_str_t *s)\n{\n    char       *p;\n    size_t      len;\n    X509       *cert;\n    X509_NAME  *name;\n\n    s->len = 0;\n\n    cert = SSL_get_peer_certificate(c->ssl->connection);\n    if (cert == NULL) {\n        return NGX_OK;\n    }\n\n    name = X509_get_subject_name(cert);\n    if (name == NULL) {\n        X509_free(cert);\n        return NGX_ERROR;\n    }\n\n    p = X509_NAME_oneline(name, NULL, 0);\n    if (p == NULL) {\n        ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, \"X509_NAME_oneline() failed\");\n        X509_free(cert);\n        return NGX_ERROR;\n    }\n\n    for (len = 0; p[len]; len++) { /* void */ }\n\n    s->len = len;\n    s->data = ngx_pnalloc(pool, len);\n    if (s->data == NULL) {\n        OPENSSL_free(p);\n        X509_free(cert);\n        return NGX_ERROR;\n    }\n\n    ngx_memcpy(s->data, p, len);\n\n    OPENSSL_free(p);\n    X509_free(cert);\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_ssl_get_issuer_dn_legacy(ngx_connection_t *c, ngx_pool_t *pool,\n    ngx_str_t *s)\n{\n    char       *p;\n    size_t      len;\n    X509       *cert;\n    X509_NAME  *name;\n\n    s->len = 0;\n\n    cert = SSL_get_peer_certificate(c->ssl->connection);\n    if (cert == NULL) {\n        return NGX_OK;\n    }\n\n    name = X509_get_issuer_name(cert);\n    if (name == NULL) {\n        X509_free(cert);\n        return NGX_ERROR;\n    }\n\n    p = X509_NAME_oneline(name, NULL, 0);\n    if (p == NULL) {\n        ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, \"X509_NAME_oneline() failed\");\n        X509_free(cert);\n        return NGX_ERROR;\n    }\n\n    for (len = 0; p[len]; len++) { /* void */ }\n\n    s->len = len;\n    s->data = ngx_pnalloc(pool, len);\n    if (s->data == NULL) {\n        OPENSSL_free(p);\n        X509_free(cert);\n        return NGX_ERROR;\n    }\n\n    ngx_memcpy(s->data, p, len);\n\n    OPENSSL_free(p);\n    X509_free(cert);\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_ssl_get_serial_number(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)\n{\n    size_t   len;\n    X509    *cert;\n    BIO     *bio;\n\n    s->len = 0;\n\n    cert = SSL_get_peer_certificate(c->ssl->connection);\n    if (cert == NULL) {\n        return NGX_OK;\n    }\n\n    bio = BIO_new(BIO_s_mem());\n    if (bio == NULL) {\n        ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, \"BIO_new() failed\");\n        X509_free(cert);\n        return NGX_ERROR;\n    }\n\n    i2a_ASN1_INTEGER(bio, X509_get_serialNumber(cert));\n    len = BIO_pending(bio);\n\n    s->len = len;\n    s->data = ngx_pnalloc(pool, len);\n    if (s->data == NULL) {\n        BIO_free(bio);\n        X509_free(cert);\n        return NGX_ERROR;\n    }\n\n    BIO_read(bio, s->data, len);\n    BIO_free(bio);\n    X509_free(cert);\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_ssl_get_fingerprint(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)\n{\n    X509          *cert;\n    unsigned int   len;\n    u_char         buf[EVP_MAX_MD_SIZE];\n\n    s->len = 0;\n\n    cert = SSL_get_peer_certificate(c->ssl->connection);\n    if (cert == NULL) {\n        return NGX_OK;\n    }\n\n    if (!X509_digest(cert, EVP_sha1(), buf, &len)) {\n        ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, \"X509_digest() failed\");\n        X509_free(cert);\n        return NGX_ERROR;\n    }\n\n    s->len = 2 * len;\n    s->data = ngx_pnalloc(pool, 2 * len);\n    if (s->data == NULL) {\n        X509_free(cert);\n        return NGX_ERROR;\n    }\n\n    ngx_hex_dump(s->data, buf, len);\n\n    X509_free(cert);\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_ssl_get_client_verify(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)\n{\n    X509        *cert;\n    long         rc;\n    const char  *str;\n\n    cert = SSL_get_peer_certificate(c->ssl->connection);\n    if (cert == NULL) {\n        ngx_str_set(s, \"NONE\");\n        return NGX_OK;\n    }\n\n    X509_free(cert);\n\n    rc = SSL_get_verify_result(c->ssl->connection);\n\n    if (rc == X509_V_OK) {\n        if (ngx_ssl_ocsp_get_status(c, &str) == NGX_OK) {\n            ngx_str_set(s, \"SUCCESS\");\n            return NGX_OK;\n        }\n\n    } else {\n        str = X509_verify_cert_error_string(rc);\n    }\n\n    s->data = ngx_pnalloc(pool, sizeof(\"FAILED:\") - 1 + ngx_strlen(str));\n    if (s->data == NULL) {\n        return NGX_ERROR;\n    }\n\n    s->len = ngx_sprintf(s->data, \"FAILED:%s\", str) - s->data;\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_ssl_get_client_v_start(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)\n{\n    BIO     *bio;\n    X509    *cert;\n    size_t   len;\n\n    s->len = 0;\n\n    cert = SSL_get_peer_certificate(c->ssl->connection);\n    if (cert == NULL) {\n        return NGX_OK;\n    }\n\n    bio = BIO_new(BIO_s_mem());\n    if (bio == NULL) {\n        ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, \"BIO_new() failed\");\n        X509_free(cert);\n        return NGX_ERROR;\n    }\n\n#if OPENSSL_VERSION_NUMBER > 0x10100000L\n    ASN1_TIME_print(bio, X509_get0_notBefore(cert));\n#else\n    ASN1_TIME_print(bio, X509_get_notBefore(cert));\n#endif\n\n    len = BIO_pending(bio);\n\n    s->len = len;\n    s->data = ngx_pnalloc(pool, len);\n    if (s->data == NULL) {\n        BIO_free(bio);\n        X509_free(cert);\n        return NGX_ERROR;\n    }\n\n    BIO_read(bio, s->data, len);\n    BIO_free(bio);\n    X509_free(cert);\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_ssl_get_client_v_end(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)\n{\n    BIO     *bio;\n    X509    *cert;\n    size_t   len;\n\n    s->len = 0;\n\n    cert = SSL_get_peer_certificate(c->ssl->connection);\n    if (cert == NULL) {\n        return NGX_OK;\n    }\n\n    bio = BIO_new(BIO_s_mem());\n    if (bio == NULL) {\n        ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, \"BIO_new() failed\");\n        X509_free(cert);\n        return NGX_ERROR;\n    }\n\n#if OPENSSL_VERSION_NUMBER > 0x10100000L\n    ASN1_TIME_print(bio, X509_get0_notAfter(cert));\n#else\n    ASN1_TIME_print(bio, X509_get_notAfter(cert));\n#endif\n\n    len = BIO_pending(bio);\n\n    s->len = len;\n    s->data = ngx_pnalloc(pool, len);\n    if (s->data == NULL) {\n        BIO_free(bio);\n        X509_free(cert);\n        return NGX_ERROR;\n    }\n\n    BIO_read(bio, s->data, len);\n    BIO_free(bio);\n    X509_free(cert);\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_ssl_get_client_v_remain(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)\n{\n    X509    *cert;\n    time_t   now, end;\n\n    s->len = 0;\n\n    cert = SSL_get_peer_certificate(c->ssl->connection);\n    if (cert == NULL) {\n        return NGX_OK;\n    }\n\n#if OPENSSL_VERSION_NUMBER > 0x10100000L\n    end = ngx_ssl_parse_time(X509_get0_notAfter(cert), c->log);\n#else\n    end = ngx_ssl_parse_time(X509_get_notAfter(cert), c->log);\n#endif\n\n    if (end == (time_t) NGX_ERROR) {\n        X509_free(cert);\n        return NGX_OK;\n    }\n\n    now = ngx_time();\n\n    if (end < now + 86400) {\n        ngx_str_set(s, \"0\");\n        X509_free(cert);\n        return NGX_OK;\n    }\n\n    s->data = ngx_pnalloc(pool, NGX_TIME_T_LEN);\n    if (s->data == NULL) {\n        X509_free(cert);\n        return NGX_ERROR;\n    }\n\n    s->len = ngx_sprintf(s->data, \"%T\", (end - now) / 86400) - s->data;\n\n    X509_free(cert);\n\n    return NGX_OK;\n}\n\n\n#if (T_NGX_SSL_HANDSHAKE_TIME)\nngx_int_t\nngx_ssl_get_handshake_time(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)\n{\n    ngx_msec_int_t   ms;\n    u_char          *p;\n    ngx_time_t      *tp;\n\n    if (c->ssl == NULL) {\n        ngx_str_null(s);\n\n        return NGX_OK;\n    }\n\n    tp = ngx_timeofday();\n\n    if (c->ssl->handshake_end_msec == 0) {\n        ms = tp->sec * 1000 + tp->msec - c->ssl->handshake_start_msec;\n\n    } else {\n        ms = c->ssl->handshake_end_msec - c->ssl->handshake_start_msec;\n    }\n\n    ms = ngx_max(ms, 0);\n\n    p = ngx_pnalloc(pool, NGX_TIME_T_LEN + 4);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    s->len = ngx_sprintf(p, \"%T.%03M\", (time_t) ms / 1000, ms % 1000) - p;\n    s->data = p;\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_ssl_get_handshake_time_msec(ngx_connection_t *c, ngx_pool_t *pool, ngx_str_t *s)\n{\n    ngx_msec_int_t   ms;\n    u_char          *p;\n    ngx_time_t      *tp;\n\n    if (c->ssl == NULL) {\n        ngx_str_null(s);\n\n        return NGX_OK;\n    }\n\n    tp = ngx_timeofday();\n\n    if (c->ssl->handshake_end_msec == 0) {\n        ms = tp->sec * 1000 + tp->msec - c->ssl->handshake_start_msec;\n\n    } else {\n        ms = c->ssl->handshake_end_msec - c->ssl->handshake_start_msec;\n    }\n\n    ms = ngx_max(ms, 0);\n\n    p = ngx_pnalloc(pool, NGX_TIME_T_LEN);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    s->len = ngx_sprintf(p, \"%i\", ms) - p;\n    s->data = p;\n\n    return NGX_OK;\n}\n#endif\n\n\nstatic time_t\nngx_ssl_parse_time(\n#if OPENSSL_VERSION_NUMBER > 0x10100000L\n    const\n#endif\n    ASN1_TIME *asn1time, ngx_log_t *log)\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_TIME\n     * into time_t.  To do this, we use ASN1_TIME_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        ngx_ssl_error(NGX_LOG_ALERT, log, 0, \"BIO_new() failed\");\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_TIME_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\n\nstatic void *\nngx_openssl_create_conf(ngx_cycle_t *cycle)\n{\n    ngx_openssl_conf_t  *oscf;\n\n    oscf = ngx_pcalloc(cycle->pool, sizeof(ngx_openssl_conf_t));\n    if (oscf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc():\n     *\n     *     oscf->engine = 0;\n     */\n\n    return oscf;\n}\n\n\nstatic char *\nngx_openssl_engine(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n#ifndef OPENSSL_NO_ENGINE\n\n    ngx_openssl_conf_t *oscf = conf;\n\n    ENGINE     *engine;\n    ngx_str_t  *value;\n\n    if (oscf->engine) {\n        return \"is duplicate\";\n    }\n\n#if (NGX_SSL && NGX_SSL_ASYNC)\n    if (cf->no_ssl_init) {\n        return NGX_CONF_OK;\n    }\n#endif\n\n    oscf->engine = 1;\n\n    value = cf->args->elts;\n\n    engine = ENGINE_by_id((char *) value[1].data);\n\n    if (engine == NULL) {\n        ngx_ssl_error(NGX_LOG_EMERG, cf->log, 0,\n                      \"ENGINE_by_id(\\\"%V\\\") failed\", &value[1]);\n        return NGX_CONF_ERROR;\n    }\n\n    if (ENGINE_set_default(engine, ENGINE_METHOD_ALL) == 0) {\n        ngx_ssl_error(NGX_LOG_EMERG, cf->log, 0,\n                      \"ENGINE_set_default(\\\"%V\\\", ENGINE_METHOD_ALL) failed\",\n                      &value[1]);\n\n        ENGINE_free(engine);\n\n        return NGX_CONF_ERROR;\n    }\n\n    ENGINE_free(engine);\n\n    return NGX_CONF_OK;\n\n#else\n\n    return \"is not supported\";\n\n#endif\n}\n\n\nstatic void\nngx_openssl_exit(ngx_cycle_t *cycle)\n{\n#if OPENSSL_VERSION_NUMBER < 0x10100003L\n\n    EVP_cleanup();\n#ifndef OPENSSL_NO_ENGINE\n    ENGINE_cleanup();\n#endif\n\n#endif\n}\n\n#if defined(T_NGX_HAVE_DTLS)\nvoid ngx_dtls_retransmit(ngx_event_t *ev)\n{\n    ngx_connection_t *c = ev->data;\n\n    ngx_ssl_error(NGX_LOG_DEBUG, c->log, 0,\n                  \"ngx_dtls_retransmit %i\", (ngx_int_t)c->ssl->retrans_times);\n\n    if (c->ssl->retrans_times > NGX_DTLS_MAX_RETRANSMIT_TIME) {\n        c->ssl->handler(c);\n        return;\n    }\n\n    c->ssl->retrans_times++;\n    if (ngx_ssl_handshake(c) == NGX_AGAIN) {\n        return;\n    }\n\n    c->ssl->handler(c);\n}\n\n/*\n * RFC 6347, 4.2.1:\n *\n * When responding to a HelloVerifyRequest, the client MUST use the same\n * parameter values (version, random, session_id, cipher_suites,\n * compression_method) as it did in the original ClientHello.  The\n * server SHOULD use those values to generate its cookie and verify that\n * they are correct upon cookie receipt.\n */\n\nstatic int\nngx_dtls_client_hmac(SSL *ssl, u_char res[EVP_MAX_MD_SIZE], unsigned int *rlen)\n{\n    u_char            *p;\n    size_t             len;\n    ngx_connection_t  *c;\n\n    u_char             buffer[64];\n\n    c = ngx_ssl_get_connection(ssl);\n\n    p = buffer;\n\n    p = ngx_cpymem(p, c->addr_text.data, c->addr_text.len);\n    p = ngx_sprintf(p, \"%d\", ngx_inet_get_port(c->sockaddr));\n\n    len = p - buffer;\n\n    HMAC(EVP_sha1(), (const void*) c->ssl->dtls_cookie_secret,\n         sizeof(c->ssl->dtls_cookie_secret), (const u_char*) buffer, len, res, rlen);\n\n    return NGX_OK;\n}\n\n\nstatic int\nngx_dtls_generate_cookie_cb(SSL *ssl, unsigned char *cookie,\n    unsigned int *cookie_len)\n{\n    unsigned int  rlen;\n    u_char        res[EVP_MAX_MD_SIZE];\n\n    if (ngx_dtls_client_hmac(ssl, res, &rlen) != NGX_OK) {\n        return 0;\n    }\n\n    ngx_memcpy(cookie, res, rlen);\n    *cookie_len = rlen;\n\n    return 1;\n}\n\n\nstatic int\nngx_dtls_verify_cookie_cb(SSL *ssl,\n#if OPENSSL_VERSION_NUMBER >= 0x10100000L\n    const\n#endif\n    unsigned char *cookie, unsigned int cookie_len)\n{\n    unsigned int  rlen;\n    u_char        res[EVP_MAX_MD_SIZE];\n\n    if (ngx_dtls_client_hmac(ssl, res, &rlen) != NGX_OK) {\n        return 0;\n    }\n\n    if (cookie_len == rlen && ngx_memcmp(res, cookie, rlen) == 0) {\n        return 1;\n    }\n\n    return 0;\n}\n\nstatic int\nngx_dtls_new(BIO *bi)\n{\n\n#if OPENSSL_VERSION_NUMBER >= 0x10100000L\n    BIO_set_init(bi, 0);\n    BIO_set_data(bi, NULL);\n    BIO_clear_flags(bi, ~0);\n#else\n    bi->init = 0;\n    bi->num = 0;\n    bi->ptr = NULL;\n    bi->flags = 0;\n#endif\n    return (1);\n}\n\nstatic int\nngx_dtls_free(BIO *a)\n{\n    int shutdown, init, num;\n\n    if (a == NULL)\n        return (0);\n\n#if OPENSSL_VERSION_NUMBER >= 0x10100000L\n    ngx_connection_t *c;\n    shutdown = BIO_get_shutdown(a);\n    init = BIO_get_init(a);\n    c = BIO_get_data(a);\n    num = c->fd;\n#else\n    shutdown = a->shutdown;\n    init = a->init;\n    num = a->num;\n#endif\n    //\n    if (shutdown) {\n        //BIO_get_init\n        if (init) {\n            ngx_close_socket(num);\n        }\n#if OPENSSL_VERSION_NUMBER >= 0x10100000L\n        BIO_set_data(a, NULL);\n        BIO_set_init(a, 0);\n#else\n        a->init = 0;\n        a->flags = 0;\n#endif\n    }\n\n    return (1);\n}\n\nstatic int\nngx_dtls_read(BIO *b, char *out, int outl)\n{\n    ssize_t ret = 0, n = 0;\n\n#if OPENSSL_VERSION_NUMBER >= 0x10100000L\n    ngx_connection_t *c = BIO_get_data(b);\n#else\n    ngx_connection_t *c = (ngx_connection_t *)b->ptr;\n#endif\n\n    if (out != NULL) {\n\n        if (c->buffer && c->buffer->pos < c->buffer->last) {\n            n = c->buffer->last - c->buffer->pos;\n\n            if (n > (ssize_t)outl) {\n                n = outl;\n            }\n            ngx_memcpy(out, c->buffer->pos, n);\n            c->buffer->pos += n;\n        }\n\n        /*ngx_udp_shared_recv*/\n        ret = c->recv(c, (u_char*)out, (size_t)outl);\n\n        BIO_clear_retry_flags(b);\n\n        if (ret == NGX_AGAIN) {\n            BIO_set_retry_read(b);\n\n        } else {\n            n += ret;\n        }\n\n        if (n == 0) {\n            n = -1;\n        }\n    }\n\n    return n;\n}\n\nstatic int\nngx_dtls_write(BIO *b, const char *in, int inl)\n{\n    ssize_t ret;\n\n#if OPENSSL_VERSION_NUMBER >= 0x10100000L\n    ngx_connection_t *c = BIO_get_data(b);\n#else\n    ngx_connection_t *c = (ngx_connection_t *)b->ptr;\n#endif\n\n    c->ssl->dtls_send = 1;\n    ret = ngx_udp_send(c, (u_char *)in, (size_t)inl);\n\n    BIO_clear_retry_flags(b);\n\n    if (ret <= 0) {\n        if (ret == NGX_AGAIN) {\n            BIO_set_retry_write(b);\n        }\n    }\n\n    return (ret);\n}\n\nstatic int\nngx_dtls_puts(BIO *bp, const char *str)\n{\n    return ngx_dtls_write(bp, str, strlen(str));\n}\n\nstatic long\nngx_dtls_ctrl(BIO *b, int cmd, long num, void *ptr)\n{\n    long ret = 1;\n    ngx_connection_t *c;\n\n    /*BIO_get_data*/\n\n    switch (cmd) {\n    case BIO_C_SET_CONNECT:\n#if OPENSSL_VERSION_NUMBER >= 0x10100000L\n        BIO_set_data(b, ptr);\n        BIO_set_init(b, 1);\n        BIO_set_shutdown(b, num);\n        (void)c;\n#else\n        b->ptr = ptr;\n        c = (ngx_connection_t *)ptr;\n        b->num = c->fd;\n        b->init = 1;\n        b->shutdown = (int)num;\n#endif\n        break;\n    case BIO_CTRL_GET_CLOSE:\n#if OPENSSL_VERSION_NUMBER >= 0x10100000L\n        ret = BIO_get_shutdown(b);\n#else\n        ret = b->shutdown;\n#endif\n        break;\n    case BIO_CTRL_SET_CLOSE:\n#if OPENSSL_VERSION_NUMBER >= 0x10100000L\n        BIO_set_shutdown(b, num);\n#else\n        b->shutdown = (int)num;\n#endif\n        break;\n    case BIO_CTRL_DUP:\n    case BIO_CTRL_FLUSH:\n        ret = 1;\n        break;\n    default:\n        ret = 0;\n        break;\n    }\n    return (ret);\n}\n\n#if OPENSSL_VERSION_NUMBER >= 0x10100000L\nstatic BIO_METHOD *methods_ngx_dtls = NULL;\n#else\nstatic BIO_METHOD methods_ngx_dtls = {\n    BIO_TYPE_SOCKET,\n    \"ngx dtls\",\n    ngx_dtls_write,\n    ngx_dtls_read,\n    ngx_dtls_puts,\n    NULL,\n    ngx_dtls_ctrl,\n    ngx_dtls_new,\n    ngx_dtls_free,\n    NULL,\n};\n\n#endif\nstatic ngx_int_t\nngx_dtls_handshake(ngx_connection_t *c)\n{\n    BIO *rbio;\n\n#if OPENSSL_VERSION_NUMBER >= 0x10100000L\n    if (methods_ngx_dtls == NULL) {\n        methods_ngx_dtls = BIO_meth_new(BIO_TYPE_SOCKET, \"ngx dtls\");\n        if (!methods_ngx_dtls\n            || !BIO_meth_set_create(methods_ngx_dtls, ngx_dtls_new)\n            || !BIO_meth_set_destroy(methods_ngx_dtls, ngx_dtls_free)\n            || !BIO_meth_set_write(methods_ngx_dtls, ngx_dtls_write)\n            || !BIO_meth_set_read(methods_ngx_dtls, ngx_dtls_read)\n            || !BIO_meth_set_puts(methods_ngx_dtls, ngx_dtls_puts)\n            || !BIO_meth_set_ctrl(methods_ngx_dtls, ngx_dtls_ctrl)) {\n\n            return NGX_ERROR;\n        }\n    }\n    rbio = BIO_new(methods_ngx_dtls);\n#else\n    /*Need to replace rbio*/\n    rbio = BIO_new(&methods_ngx_dtls);\n#endif\n\n    if (rbio == NULL) {\n        return NGX_ERROR;\n    }\n\n    BIO_ctrl(rbio, BIO_C_SET_CONNECT, BIO_NOCLOSE, (char *)c);\n\n    SSL_set_bio(c->ssl->connection, rbio, rbio);\n    /*TODO: directive*/\n    SSL_set_options(c->ssl->connection, SSL_OP_COOKIE_EXCHANGE);\n\n    c->ssl->retrans = ngx_pcalloc(c->pool, sizeof(ngx_event_t));\n    if (!c->ssl->retrans) {\n        return NGX_ERROR;\n    }\n\n    c->ssl->retrans->log = c->log;\n    c->ssl->retrans->data = c;\n\n    c->ssl->bio_changed = 1;\n\n    if (!RAND_bytes(c->ssl->dtls_cookie_secret, sizeof(c->ssl->dtls_cookie_secret))) {\n        return NGX_ERROR;\n    }\n    return NGX_OK;\n}\n\n#endif\n\n"
  },
  {
    "path": "src/event/ngx_event_openssl.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_EVENT_OPENSSL_H_INCLUDED_\n#define _NGX_EVENT_OPENSSL_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n#define OPENSSL_SUPPRESS_DEPRECATED\n\n#include <openssl/ssl.h>\n#include <openssl/err.h>\n#include <openssl/bn.h>\n#include <openssl/conf.h>\n#include <openssl/crypto.h>\n#include <openssl/dh.h>\n#ifndef OPENSSL_NO_ENGINE\n#include <openssl/engine.h>\n#endif\n#include <openssl/evp.h>\n#include <openssl/hmac.h>\n#ifndef OPENSSL_NO_OCSP\n#include <openssl/ocsp.h>\n#endif\n#include <openssl/rand.h>\n#include <openssl/x509.h>\n#include <openssl/x509v3.h>\n\n#define NGX_SSL_NAME     \"OpenSSL\"\n\n\n#if (defined LIBRESSL_VERSION_NUMBER && OPENSSL_VERSION_NUMBER == 0x20000000L)\n#undef OPENSSL_VERSION_NUMBER\n#if (LIBRESSL_VERSION_NUMBER >= 0x2080000fL)\n#define OPENSSL_VERSION_NUMBER  0x1010000fL\n#else\n#define OPENSSL_VERSION_NUMBER  0x1000107fL\n#endif\n#endif\n\n\n#if (OPENSSL_VERSION_NUMBER >= 0x10100001L)\n\n#define ngx_ssl_version()       OpenSSL_version(OPENSSL_VERSION)\n\n#else\n\n#define ngx_ssl_version()       SSLeay_version(SSLEAY_VERSION)\n\n#endif\n\n\n#define ngx_ssl_session_t       SSL_SESSION\n#define ngx_ssl_conn_t          SSL\n\n\n#if (OPENSSL_VERSION_NUMBER < 0x10002000L)\n#define SSL_is_server(s)        (s)->server\n#endif\n\n\n#if (OPENSSL_VERSION_NUMBER >= 0x30000000L && !defined SSL_get_peer_certificate)\n#define SSL_get_peer_certificate(s)  SSL_get1_peer_certificate(s)\n#endif\n\n\n#if (OPENSSL_VERSION_NUMBER < 0x30000000L && !defined ERR_peek_error_data)\n#define ERR_peek_error_data(d, f)    ERR_peek_error_line_data(NULL, NULL, d, f)\n#endif\n\n\ntypedef struct ngx_ssl_ocsp_s  ngx_ssl_ocsp_t;\n\n\nstruct ngx_ssl_s {\n    SSL_CTX                    *ctx;\n    ngx_log_t                  *log;\n    size_t                      buffer_size;\n#if (NGX_SSL && NGX_SSL_ASYNC)\n    ngx_flag_t                  async_enable;\n#endif\n};\n\n\nstruct ngx_ssl_connection_s {\n    ngx_ssl_conn_t             *connection;\n    SSL_CTX                    *session_ctx;\n\n    ngx_int_t                   last;\n    ngx_buf_t                  *buf;\n    size_t                      buffer_size;\n\n    ngx_connection_handler_pt   handler;\n\n    ngx_ssl_session_t          *session;\n    ngx_connection_handler_pt   save_session;\n\n    ngx_event_handler_pt        saved_read_handler;\n    ngx_event_handler_pt        saved_write_handler;\n\n    ngx_ssl_ocsp_t             *ocsp;\n\n    u_char                      early_buf;\n\n#if (T_NGX_SSL_HANDSHAKE_TIME)\n    ngx_msec_t                  handshake_start_msec;\n    ngx_msec_t                  handshake_end_msec;\n#endif\n\n    unsigned                    handshaked:1;\n    unsigned                    handshake_rejected:1;\n    unsigned                    renegotiation:1;\n    unsigned                    buffer:1;\n    unsigned                    sendfile:1;\n    unsigned                    no_wait_shutdown:1;\n    unsigned                    no_send_shutdown:1;\n    unsigned                    shutdown_without_free:1;\n    unsigned                    handshake_buffer_set:1;\n    unsigned                    session_timeout_set:1;\n    unsigned                    try_early_data:1;\n    unsigned                    in_early:1;\n    unsigned                    in_ocsp:1;\n    unsigned                    early_preread:1;\n    unsigned                    write_blocked:1;\n\n#if defined(T_INGRESS_SHARED_MEMORY_PB) && OPENSSL_VERSION_NUMBER >= 0x10101000L\n    unsigned                    client_hello_retry:1;\n#endif\n\n#if (T_NGX_HAVE_DTLS)\n    unsigned                    bio_changed:1;\n    unsigned                    dtls_send:1;\n    unsigned                    client:1;\n    unsigned                    retrans_times;\n    ngx_event_t                *retrans;\n    u_char                      dtls_cookie_secret[32];\n#endif\n};\n\n\n#define NGX_SSL_NO_SCACHE            -2\n#define NGX_SSL_NONE_SCACHE          -3\n#define NGX_SSL_NO_BUILTIN_SCACHE    -4\n#define NGX_SSL_DFLT_BUILTIN_SCACHE  -5\n\n\n#define NGX_SSL_MAX_SESSION_SIZE  4096\n\ntypedef struct ngx_ssl_sess_id_s  ngx_ssl_sess_id_t;\n\nstruct ngx_ssl_sess_id_s {\n    ngx_rbtree_node_t           node;\n    size_t                      len;\n    ngx_queue_t                 queue;\n    time_t                      expire;\n    u_char                      id[32];\n#if (NGX_PTR_SIZE == 8)\n    u_char                     *session;\n#else\n    u_char                      session[1];\n#endif\n};\n\n\ntypedef struct {\n    u_char                      name[16];\n    u_char                      hmac_key[32];\n    u_char                      aes_key[32];\n    time_t                      expire;\n    unsigned                    size:8;\n    unsigned                    shared:1;\n} ngx_ssl_ticket_key_t;\n\n\ntypedef struct {\n    ngx_rbtree_t                session_rbtree;\n    ngx_rbtree_node_t           sentinel;\n    ngx_queue_t                 expire_queue;\n    ngx_ssl_ticket_key_t        ticket_keys[3];\n    time_t                      fail_time;\n} ngx_ssl_session_cache_t;\n\n\n#define NGX_SSL_SSLv2    0x0002\n#define NGX_SSL_SSLv3    0x0004\n#define NGX_SSL_TLSv1    0x0008\n#define NGX_SSL_TLSv1_1  0x0010\n#define NGX_SSL_TLSv1_2  0x0020\n#define NGX_SSL_TLSv1_3  0x0040\n\n#if (T_NGX_HAVE_DTLS)\n#define NGX_SSL_DTLSv1   0x0080\n#define NGX_SSL_DTLSv1_2 0x0200\n#endif\n\n#define NGX_SSL_BUFFER   1\n#define NGX_SSL_CLIENT   2\n\n#define NGX_SSL_BUFSIZE  16384\n\n\nngx_int_t ngx_ssl_init(ngx_log_t *log);\nngx_int_t ngx_ssl_create(ngx_ssl_t *ssl, ngx_uint_t protocols, void *data);\n\nngx_int_t ngx_ssl_certificates(ngx_conf_t *cf, ngx_ssl_t *ssl,\n    ngx_array_t *certs, ngx_array_t *keys, ngx_array_t *passwords);\n#if (T_NGX_SSL_NTLS)\nngx_int_t ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl,\n    ngx_str_t *cert, ngx_str_t *key, ngx_array_t *passwords,\n    ngx_flag_t cert_tag);\n#else\nngx_int_t ngx_ssl_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl,\n    ngx_str_t *cert, ngx_str_t *key, ngx_array_t *passwords);\n#endif\nngx_int_t ngx_ssl_connection_certificate(ngx_connection_t *c, ngx_pool_t *pool,\n    ngx_str_t *cert, ngx_str_t *key, ngx_array_t *passwords);\n\nngx_int_t ngx_ssl_ciphers(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *ciphers,\n    ngx_uint_t prefer_server_ciphers);\nngx_int_t ngx_ssl_client_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl,\n    ngx_str_t *cert, ngx_int_t depth);\nngx_int_t ngx_ssl_trusted_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl,\n    ngx_str_t *cert, ngx_int_t depth);\nngx_int_t ngx_ssl_crl(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *crl);\nngx_int_t ngx_ssl_stapling(ngx_conf_t *cf, ngx_ssl_t *ssl,\n    ngx_str_t *file, ngx_str_t *responder, ngx_uint_t verify);\nngx_int_t ngx_ssl_stapling_resolver(ngx_conf_t *cf, ngx_ssl_t *ssl,\n    ngx_resolver_t *resolver, ngx_msec_t resolver_timeout);\nngx_int_t ngx_ssl_ocsp(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *responder,\n    ngx_uint_t depth, ngx_shm_zone_t *shm_zone);\nngx_int_t ngx_ssl_ocsp_resolver(ngx_conf_t *cf, ngx_ssl_t *ssl,\n    ngx_resolver_t *resolver, ngx_msec_t resolver_timeout);\n\nngx_int_t ngx_ssl_ocsp_validate(ngx_connection_t *c);\nngx_int_t ngx_ssl_ocsp_get_status(ngx_connection_t *c, const char **s);\nvoid ngx_ssl_ocsp_cleanup(ngx_connection_t *c);\nngx_int_t ngx_ssl_ocsp_cache_init(ngx_shm_zone_t *shm_zone, void *data);\n\nngx_array_t *ngx_ssl_read_password_file(ngx_conf_t *cf, ngx_str_t *file);\nngx_array_t *ngx_ssl_preserve_passwords(ngx_conf_t *cf,\n    ngx_array_t *passwords);\nngx_int_t ngx_ssl_dhparam(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file);\nngx_int_t ngx_ssl_ecdh_curve(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *name);\nngx_int_t ngx_ssl_early_data(ngx_conf_t *cf, ngx_ssl_t *ssl,\n    ngx_uint_t enable);\nngx_int_t ngx_ssl_conf_commands(ngx_conf_t *cf, ngx_ssl_t *ssl,\n    ngx_array_t *commands);\n\nngx_int_t ngx_ssl_client_session_cache(ngx_conf_t *cf, ngx_ssl_t *ssl,\n    ngx_uint_t enable);\nngx_int_t ngx_ssl_session_cache(ngx_ssl_t *ssl, ngx_str_t *sess_ctx,\n    ngx_array_t *certificates, ssize_t builtin_session_cache,\n    ngx_shm_zone_t *shm_zone, time_t timeout);\nngx_int_t ngx_ssl_session_ticket_keys(ngx_conf_t *cf, ngx_ssl_t *ssl,\n    ngx_array_t *paths);\nngx_int_t ngx_ssl_session_cache_init(ngx_shm_zone_t *shm_zone, void *data);\n\nngx_int_t ngx_ssl_create_connection(ngx_ssl_t *ssl, ngx_connection_t *c,\n    ngx_uint_t flags);\n\nvoid ngx_ssl_remove_cached_session(SSL_CTX *ssl, ngx_ssl_session_t *sess);\nngx_int_t ngx_ssl_set_session(ngx_connection_t *c, ngx_ssl_session_t *session);\nngx_ssl_session_t *ngx_ssl_get_session(ngx_connection_t *c);\nngx_ssl_session_t *ngx_ssl_get0_session(ngx_connection_t *c);\n#define ngx_ssl_free_session        SSL_SESSION_free\n#define ngx_ssl_get_connection(ssl_conn)                                      \\\n    SSL_get_ex_data(ssl_conn, ngx_ssl_connection_index)\n#define ngx_ssl_get_server_conf(ssl_ctx)                                      \\\n    SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_server_conf_index)\n\n#define ngx_ssl_verify_error_optional(n)                                      \\\n    (n == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT                              \\\n     || n == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN                             \\\n     || n == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY                     \\\n     || n == X509_V_ERR_CERT_UNTRUSTED                                        \\\n     || n == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE)\n\nngx_int_t ngx_ssl_check_host(ngx_connection_t *c, ngx_str_t *name);\n\n#if (NGX_SSL && NGX_SSL_ASYNC)\n#define ngx_ssl_waiting_for_async(c) SSL_waiting_for_async(c->ssl->connection)\n#endif\n\nngx_int_t ngx_ssl_get_protocol(ngx_connection_t *c, ngx_pool_t *pool,\n    ngx_str_t *s);\nngx_int_t ngx_ssl_get_cipher_name(ngx_connection_t *c, ngx_pool_t *pool,\n    ngx_str_t *s);\nngx_int_t ngx_ssl_get_ciphers(ngx_connection_t *c, ngx_pool_t *pool,\n    ngx_str_t *s);\nngx_int_t ngx_ssl_get_curve(ngx_connection_t *c, ngx_pool_t *pool,\n    ngx_str_t *s);\nngx_int_t ngx_ssl_get_curves(ngx_connection_t *c, ngx_pool_t *pool,\n    ngx_str_t *s);\nngx_int_t ngx_ssl_get_session_id(ngx_connection_t *c, ngx_pool_t *pool,\n    ngx_str_t *s);\nngx_int_t ngx_ssl_get_session_reused(ngx_connection_t *c, ngx_pool_t *pool,\n    ngx_str_t *s);\nngx_int_t ngx_ssl_get_early_data(ngx_connection_t *c, ngx_pool_t *pool,\n    ngx_str_t *s);\nngx_int_t ngx_ssl_get_server_name(ngx_connection_t *c, ngx_pool_t *pool,\n    ngx_str_t *s);\nngx_int_t ngx_ssl_get_alpn_protocol(ngx_connection_t *c, ngx_pool_t *pool,\n    ngx_str_t *s);\nngx_int_t ngx_ssl_get_raw_certificate(ngx_connection_t *c, ngx_pool_t *pool,\n    ngx_str_t *s);\nngx_int_t ngx_ssl_get_certificate(ngx_connection_t *c, ngx_pool_t *pool,\n    ngx_str_t *s);\nngx_int_t ngx_ssl_get_escaped_certificate(ngx_connection_t *c, ngx_pool_t *pool,\n    ngx_str_t *s);\nngx_int_t ngx_ssl_get_subject_dn(ngx_connection_t *c, ngx_pool_t *pool,\n    ngx_str_t *s);\nngx_int_t ngx_ssl_get_issuer_dn(ngx_connection_t *c, ngx_pool_t *pool,\n    ngx_str_t *s);\nngx_int_t ngx_ssl_get_subject_dn_legacy(ngx_connection_t *c, ngx_pool_t *pool,\n    ngx_str_t *s);\nngx_int_t ngx_ssl_get_issuer_dn_legacy(ngx_connection_t *c, ngx_pool_t *pool,\n    ngx_str_t *s);\nngx_int_t ngx_ssl_get_serial_number(ngx_connection_t *c, ngx_pool_t *pool,\n    ngx_str_t *s);\nngx_int_t ngx_ssl_get_fingerprint(ngx_connection_t *c, ngx_pool_t *pool,\n    ngx_str_t *s);\nngx_int_t ngx_ssl_get_client_verify(ngx_connection_t *c, ngx_pool_t *pool,\n    ngx_str_t *s);\nngx_int_t ngx_ssl_get_client_v_start(ngx_connection_t *c, ngx_pool_t *pool,\n    ngx_str_t *s);\nngx_int_t ngx_ssl_get_client_v_end(ngx_connection_t *c, ngx_pool_t *pool,\n    ngx_str_t *s);\nngx_int_t ngx_ssl_get_client_v_remain(ngx_connection_t *c, ngx_pool_t *pool,\n    ngx_str_t *s);\n#if (T_NGX_SSL_HANDSHAKE_TIME)\nngx_int_t ngx_ssl_get_handshake_time(ngx_connection_t *c, ngx_pool_t *pool,\n    ngx_str_t *s);\nngx_int_t ngx_ssl_get_handshake_time_msec(ngx_connection_t *c, ngx_pool_t *pool,\n    ngx_str_t *s);\n#endif\n\n\nngx_int_t ngx_ssl_handshake(ngx_connection_t *c);\nssize_t ngx_ssl_recv(ngx_connection_t *c, u_char *buf, size_t size);\nssize_t ngx_ssl_write(ngx_connection_t *c, u_char *data, size_t size);\nssize_t ngx_ssl_recv_chain(ngx_connection_t *c, ngx_chain_t *cl, off_t limit);\nngx_chain_t *ngx_ssl_send_chain(ngx_connection_t *c, ngx_chain_t *in,\n    off_t limit);\nvoid ngx_ssl_free_buffer(ngx_connection_t *c);\nngx_int_t ngx_ssl_shutdown(ngx_connection_t *c);\nvoid ngx_cdecl ngx_ssl_error(ngx_uint_t level, ngx_log_t *log, ngx_err_t err,\n    char *fmt, ...);\nvoid ngx_ssl_cleanup_ctx(void *data);\n\n#if (NGX_SSL && NGX_SSL_ASYNC)\nngx_int_t ngx_ssl_async_process_fds(ngx_connection_t *c) ;\n#endif\n\nextern int  ngx_ssl_connection_index;\nextern int  ngx_ssl_server_conf_index;\nextern int  ngx_ssl_session_cache_index;\nextern int  ngx_ssl_ticket_keys_index;\nextern int  ngx_ssl_ocsp_index;\nextern int  ngx_ssl_certificate_index;\nextern int  ngx_ssl_next_certificate_index;\nextern int  ngx_ssl_certificate_name_index;\nextern int  ngx_ssl_stapling_index;\n\n\n#endif /* _NGX_EVENT_OPENSSL_H_INCLUDED_ */\n"
  },
  {
    "path": "src/event/ngx_event_openssl_stapling.c",
    "content": "\n/*\n * Copyright (C) Maxim Dounin\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n#include <ngx_event_connect.h>\n\n\n#if (!defined OPENSSL_NO_OCSP && defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB)\n\n\ntypedef struct {\n    ngx_str_t                    staple;\n    ngx_msec_t                   timeout;\n\n    ngx_resolver_t              *resolver;\n    ngx_msec_t                   resolver_timeout;\n\n    ngx_addr_t                  *addrs;\n    ngx_uint_t                   naddrs;\n    ngx_str_t                    host;\n    ngx_str_t                    uri;\n    in_port_t                    port;\n\n    SSL_CTX                     *ssl_ctx;\n\n    X509                        *cert;\n    X509                        *issuer;\n    STACK_OF(X509)              *chain;\n\n    u_char                      *name;\n\n    time_t                       valid;\n    time_t                       refresh;\n\n    unsigned                     verify:1;\n    unsigned                     loading:1;\n} ngx_ssl_stapling_t;\n\n\ntypedef struct {\n    ngx_addr_t                  *addrs;\n    ngx_uint_t                   naddrs;\n\n    ngx_str_t                    host;\n    ngx_str_t                    uri;\n    in_port_t                    port;\n    ngx_uint_t                   depth;\n\n    ngx_shm_zone_t              *shm_zone;\n\n    ngx_resolver_t              *resolver;\n    ngx_msec_t                   resolver_timeout;\n} ngx_ssl_ocsp_conf_t;\n\n\ntypedef struct {\n    ngx_rbtree_t                 rbtree;\n    ngx_rbtree_node_t            sentinel;\n    ngx_queue_t                  expire_queue;\n} ngx_ssl_ocsp_cache_t;\n\n\ntypedef struct {\n    ngx_str_node_t               node;\n    ngx_queue_t                  queue;\n    int                          status;\n    time_t                       valid;\n} ngx_ssl_ocsp_cache_node_t;\n\n\ntypedef struct ngx_ssl_ocsp_ctx_s  ngx_ssl_ocsp_ctx_t;\n\n\nstruct ngx_ssl_ocsp_s {\n    STACK_OF(X509)              *certs;\n    ngx_uint_t                   ncert;\n\n    int                          cert_status;\n    ngx_int_t                    status;\n\n    ngx_ssl_ocsp_conf_t         *conf;\n    ngx_ssl_ocsp_ctx_t          *ctx;\n};\n\n\nstruct ngx_ssl_ocsp_ctx_s {\n    SSL_CTX                     *ssl_ctx;\n\n    X509                        *cert;\n    X509                        *issuer;\n    STACK_OF(X509)              *chain;\n\n    int                          status;\n    time_t                       valid;\n\n    u_char                      *name;\n\n    ngx_uint_t                   naddrs;\n    ngx_uint_t                   naddr;\n\n    ngx_addr_t                  *addrs;\n    ngx_str_t                    host;\n    ngx_str_t                    uri;\n    in_port_t                    port;\n\n    ngx_resolver_t              *resolver;\n    ngx_msec_t                   resolver_timeout;\n\n    ngx_msec_t                   timeout;\n\n    void                       (*handler)(ngx_ssl_ocsp_ctx_t *ctx);\n    void                        *data;\n\n    ngx_str_t                    key;\n    ngx_buf_t                   *request;\n    ngx_buf_t                   *response;\n    ngx_peer_connection_t        peer;\n\n    ngx_shm_zone_t              *shm_zone;\n\n    ngx_int_t                  (*process)(ngx_ssl_ocsp_ctx_t *ctx);\n\n    ngx_uint_t                   state;\n\n    ngx_uint_t                   code;\n    ngx_uint_t                   count;\n    ngx_uint_t                   flags;\n    ngx_uint_t                   done;\n\n    u_char                      *header_name_start;\n    u_char                      *header_name_end;\n    u_char                      *header_start;\n    u_char                      *header_end;\n\n    ngx_pool_t                  *pool;\n    ngx_log_t                   *log;\n};\n\n\nstatic ngx_int_t ngx_ssl_stapling_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl,\n    X509 *cert, ngx_str_t *file, ngx_str_t *responder, ngx_uint_t verify);\nstatic ngx_int_t ngx_ssl_stapling_file(ngx_conf_t *cf, ngx_ssl_t *ssl,\n    ngx_ssl_stapling_t *staple, ngx_str_t *file);\nstatic ngx_int_t ngx_ssl_stapling_issuer(ngx_conf_t *cf, ngx_ssl_t *ssl,\n    ngx_ssl_stapling_t *staple);\nstatic ngx_int_t ngx_ssl_stapling_responder(ngx_conf_t *cf, ngx_ssl_t *ssl,\n    ngx_ssl_stapling_t *staple, ngx_str_t *responder);\n\nstatic int ngx_ssl_certificate_status_callback(ngx_ssl_conn_t *ssl_conn,\n    void *data);\nstatic void ngx_ssl_stapling_update(ngx_ssl_stapling_t *staple);\nstatic void ngx_ssl_stapling_ocsp_handler(ngx_ssl_ocsp_ctx_t *ctx);\n\nstatic time_t ngx_ssl_stapling_time(ASN1_GENERALIZEDTIME *asn1time);\n\nstatic void ngx_ssl_stapling_cleanup(void *data);\n\nstatic void ngx_ssl_ocsp_validate_next(ngx_connection_t *c);\nstatic void ngx_ssl_ocsp_handler(ngx_ssl_ocsp_ctx_t *ctx);\nstatic ngx_int_t ngx_ssl_ocsp_responder(ngx_connection_t *c,\n    ngx_ssl_ocsp_ctx_t *ctx);\n\nstatic ngx_ssl_ocsp_ctx_t *ngx_ssl_ocsp_start(ngx_log_t *log);\nstatic void ngx_ssl_ocsp_done(ngx_ssl_ocsp_ctx_t *ctx);\nstatic void ngx_ssl_ocsp_next(ngx_ssl_ocsp_ctx_t *ctx);\nstatic void ngx_ssl_ocsp_request(ngx_ssl_ocsp_ctx_t *ctx);\nstatic void ngx_ssl_ocsp_resolve_handler(ngx_resolver_ctx_t *resolve);\nstatic void ngx_ssl_ocsp_connect(ngx_ssl_ocsp_ctx_t *ctx);\nstatic void ngx_ssl_ocsp_write_handler(ngx_event_t *wev);\nstatic void ngx_ssl_ocsp_read_handler(ngx_event_t *rev);\nstatic void ngx_ssl_ocsp_dummy_handler(ngx_event_t *ev);\n\nstatic ngx_int_t ngx_ssl_ocsp_create_request(ngx_ssl_ocsp_ctx_t *ctx);\nstatic ngx_int_t ngx_ssl_ocsp_process_status_line(ngx_ssl_ocsp_ctx_t *ctx);\nstatic ngx_int_t ngx_ssl_ocsp_parse_status_line(ngx_ssl_ocsp_ctx_t *ctx);\nstatic ngx_int_t ngx_ssl_ocsp_process_headers(ngx_ssl_ocsp_ctx_t *ctx);\nstatic ngx_int_t ngx_ssl_ocsp_parse_header_line(ngx_ssl_ocsp_ctx_t *ctx);\nstatic ngx_int_t ngx_ssl_ocsp_process_body(ngx_ssl_ocsp_ctx_t *ctx);\nstatic ngx_int_t ngx_ssl_ocsp_verify(ngx_ssl_ocsp_ctx_t *ctx);\n\nstatic ngx_int_t ngx_ssl_ocsp_cache_lookup(ngx_ssl_ocsp_ctx_t *ctx);\nstatic ngx_int_t ngx_ssl_ocsp_cache_store(ngx_ssl_ocsp_ctx_t *ctx);\nstatic ngx_int_t ngx_ssl_ocsp_create_key(ngx_ssl_ocsp_ctx_t *ctx);\n\nstatic u_char *ngx_ssl_ocsp_log_error(ngx_log_t *log, u_char *buf, size_t len);\n\n\nngx_int_t\nngx_ssl_stapling(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file,\n    ngx_str_t *responder, ngx_uint_t verify)\n{\n    X509  *cert;\n\n    for (cert = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index);\n         cert;\n         cert = X509_get_ex_data(cert, ngx_ssl_next_certificate_index))\n    {\n        if (ngx_ssl_stapling_certificate(cf, ssl, cert, file, responder, verify)\n            != NGX_OK)\n        {\n            return NGX_ERROR;\n        }\n    }\n\n    SSL_CTX_set_tlsext_status_cb(ssl->ctx, ngx_ssl_certificate_status_callback);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_ssl_stapling_certificate(ngx_conf_t *cf, ngx_ssl_t *ssl, X509 *cert,\n    ngx_str_t *file, ngx_str_t *responder, ngx_uint_t verify)\n{\n    ngx_int_t            rc;\n    ngx_pool_cleanup_t  *cln;\n    ngx_ssl_stapling_t  *staple;\n\n    staple = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_stapling_t));\n    if (staple == NULL) {\n        return NGX_ERROR;\n    }\n\n    cln = ngx_pool_cleanup_add(cf->pool, 0);\n    if (cln == NULL) {\n        return NGX_ERROR;\n    }\n\n    cln->handler = ngx_ssl_stapling_cleanup;\n    cln->data = staple;\n\n    if (X509_set_ex_data(cert, ngx_ssl_stapling_index, staple) == 0) {\n        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0, \"X509_set_ex_data() failed\");\n        return NGX_ERROR;\n    }\n\n#ifdef SSL_CTRL_SELECT_CURRENT_CERT\n    /* OpenSSL 1.0.2+ */\n    SSL_CTX_select_current_cert(ssl->ctx, cert);\n#endif\n\n#ifdef SSL_CTRL_GET_EXTRA_CHAIN_CERTS\n    /* OpenSSL 1.0.1+ */\n    SSL_CTX_get_extra_chain_certs(ssl->ctx, &staple->chain);\n#else\n    staple->chain = ssl->ctx->extra_certs;\n#endif\n\n    staple->ssl_ctx = ssl->ctx;\n    staple->timeout = 60000;\n    staple->verify = verify;\n    staple->cert = cert;\n    staple->name = X509_get_ex_data(staple->cert,\n                                    ngx_ssl_certificate_name_index);\n\n    if (file->len) {\n        /* use OCSP response from the file */\n\n        if (ngx_ssl_stapling_file(cf, ssl, staple, file) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        return NGX_OK;\n    }\n\n    rc = ngx_ssl_stapling_issuer(cf, ssl, staple);\n\n    if (rc == NGX_DECLINED) {\n        return NGX_OK;\n    }\n\n    if (rc != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    rc = ngx_ssl_stapling_responder(cf, ssl, staple, responder);\n\n    if (rc == NGX_DECLINED) {\n        return NGX_OK;\n    }\n\n    if (rc != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_ssl_stapling_file(ngx_conf_t *cf, ngx_ssl_t *ssl,\n    ngx_ssl_stapling_t *staple, ngx_str_t *file)\n{\n    BIO            *bio;\n    int             len;\n    u_char         *p, *buf;\n    OCSP_RESPONSE  *response;\n\n    if (ngx_conf_full_name(cf->cycle, file, 1) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    bio = BIO_new_file((char *) file->data, \"rb\");\n    if (bio == NULL) {\n        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,\n                      \"BIO_new_file(\\\"%s\\\") failed\", file->data);\n        return NGX_ERROR;\n    }\n\n    response = d2i_OCSP_RESPONSE_bio(bio, NULL);\n    if (response == NULL) {\n        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,\n                      \"d2i_OCSP_RESPONSE_bio(\\\"%s\\\") failed\", file->data);\n        BIO_free(bio);\n        return NGX_ERROR;\n    }\n\n    len = i2d_OCSP_RESPONSE(response, NULL);\n    if (len <= 0) {\n        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,\n                      \"i2d_OCSP_RESPONSE(\\\"%s\\\") failed\", file->data);\n        goto failed;\n    }\n\n    buf = ngx_alloc(len, ssl->log);\n    if (buf == NULL) {\n        goto failed;\n    }\n\n    p = buf;\n    len = i2d_OCSP_RESPONSE(response, &p);\n    if (len <= 0) {\n        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,\n                      \"i2d_OCSP_RESPONSE(\\\"%s\\\") failed\", file->data);\n        ngx_free(buf);\n        goto failed;\n    }\n\n    OCSP_RESPONSE_free(response);\n    BIO_free(bio);\n\n    staple->staple.data = buf;\n    staple->staple.len = len;\n    staple->valid = NGX_MAX_TIME_T_VALUE;\n\n    return NGX_OK;\n\nfailed:\n\n    OCSP_RESPONSE_free(response);\n    BIO_free(bio);\n\n    return NGX_ERROR;\n}\n\n\nstatic ngx_int_t\nngx_ssl_stapling_issuer(ngx_conf_t *cf, ngx_ssl_t *ssl,\n    ngx_ssl_stapling_t *staple)\n{\n    int              i, n, rc;\n    X509            *cert, *issuer;\n    X509_STORE      *store;\n    X509_STORE_CTX  *store_ctx;\n\n    cert = staple->cert;\n\n    n = sk_X509_num(staple->chain);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ssl->log, 0,\n                   \"SSL get issuer: %d extra certs\", n);\n\n    for (i = 0; i < n; i++) {\n        issuer = sk_X509_value(staple->chain, i);\n        if (X509_check_issued(issuer, cert) == X509_V_OK) {\n#if OPENSSL_VERSION_NUMBER >= 0x10100001L\n            X509_up_ref(issuer);\n#else\n            CRYPTO_add(&issuer->references, 1, CRYPTO_LOCK_X509);\n#endif\n\n            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ssl->log, 0,\n                           \"SSL get issuer: found %p in extra certs\", issuer);\n\n            staple->issuer = issuer;\n\n            return NGX_OK;\n        }\n    }\n\n    store = SSL_CTX_get_cert_store(ssl->ctx);\n    if (store == NULL) {\n        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,\n                      \"SSL_CTX_get_cert_store() failed\");\n        return NGX_ERROR;\n    }\n\n    store_ctx = X509_STORE_CTX_new();\n    if (store_ctx == NULL) {\n        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,\n                      \"X509_STORE_CTX_new() failed\");\n        return NGX_ERROR;\n    }\n\n    if (X509_STORE_CTX_init(store_ctx, store, NULL, NULL) == 0) {\n        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,\n                      \"X509_STORE_CTX_init() failed\");\n        X509_STORE_CTX_free(store_ctx);\n        return NGX_ERROR;\n    }\n\n    rc = X509_STORE_CTX_get1_issuer(&issuer, store_ctx, cert);\n\n    if (rc == -1) {\n        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,\n                      \"X509_STORE_CTX_get1_issuer() failed\");\n        X509_STORE_CTX_free(store_ctx);\n        return NGX_ERROR;\n    }\n\n    if (rc == 0) {\n        ngx_log_error(NGX_LOG_WARN, ssl->log, 0,\n                      \"\\\"ssl_stapling\\\" ignored, \"\n                      \"issuer certificate not found for certificate \\\"%s\\\"\",\n                      staple->name);\n        X509_STORE_CTX_free(store_ctx);\n        return NGX_DECLINED;\n    }\n\n    X509_STORE_CTX_free(store_ctx);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ssl->log, 0,\n                   \"SSL get issuer: found %p in cert store\", issuer);\n\n    staple->issuer = issuer;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_ssl_stapling_responder(ngx_conf_t *cf, ngx_ssl_t *ssl,\n    ngx_ssl_stapling_t *staple, ngx_str_t *responder)\n{\n    char                      *s;\n    ngx_str_t                  rsp;\n    ngx_url_t                  u;\n    STACK_OF(OPENSSL_STRING)  *aia;\n\n    if (responder->len == 0) {\n\n        /* extract OCSP responder URL from certificate */\n\n        aia = X509_get1_ocsp(staple->cert);\n        if (aia == NULL) {\n            ngx_log_error(NGX_LOG_WARN, ssl->log, 0,\n                          \"\\\"ssl_stapling\\\" ignored, \"\n                          \"no OCSP responder URL in the certificate \\\"%s\\\"\",\n                          staple->name);\n            return NGX_DECLINED;\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            ngx_log_error(NGX_LOG_WARN, ssl->log, 0,\n                          \"\\\"ssl_stapling\\\" ignored, \"\n                          \"no OCSP responder URL in the certificate \\\"%s\\\"\",\n                          staple->name);\n            X509_email_free(aia);\n            return NGX_DECLINED;\n        }\n\n        responder = &rsp;\n\n        responder->len = ngx_strlen(s);\n        responder->data = ngx_palloc(cf->pool, responder->len);\n        if (responder->data == NULL) {\n            X509_email_free(aia);\n            return NGX_ERROR;\n        }\n\n        ngx_memcpy(responder->data, s, responder->len);\n        X509_email_free(aia);\n    }\n\n    ngx_memzero(&u, sizeof(ngx_url_t));\n\n    u.url = *responder;\n    u.default_port = 80;\n    u.uri_part = 1;\n\n    if (u.url.len > 7\n        && ngx_strncasecmp(u.url.data, (u_char *) \"http://\", 7) == 0)\n    {\n        u.url.len -= 7;\n        u.url.data += 7;\n\n    } else {\n        ngx_log_error(NGX_LOG_WARN, ssl->log, 0,\n                      \"\\\"ssl_stapling\\\" ignored, \"\n                      \"invalid URL prefix in OCSP responder \\\"%V\\\" \"\n                      \"in the certificate \\\"%s\\\"\",\n                      &u.url, staple->name);\n        return NGX_DECLINED;\n    }\n\n    if (ngx_parse_url(cf->pool, &u) != NGX_OK) {\n        if (u.err) {\n            ngx_log_error(NGX_LOG_WARN, ssl->log, 0,\n                          \"\\\"ssl_stapling\\\" ignored, \"\n                          \"%s in OCSP responder \\\"%V\\\" \"\n                          \"in the certificate \\\"%s\\\"\",\n                          u.err, &u.url, staple->name);\n            return NGX_DECLINED;\n        }\n\n        return NGX_ERROR;\n    }\n\n    staple->addrs = u.addrs;\n    staple->naddrs = u.naddrs;\n    staple->host = u.host;\n    staple->uri = u.uri;\n    staple->port = u.port;\n\n    if (staple->uri.len == 0) {\n        ngx_str_set(&staple->uri, \"/\");\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_ssl_stapling_resolver(ngx_conf_t *cf, ngx_ssl_t *ssl,\n    ngx_resolver_t *resolver, ngx_msec_t resolver_timeout)\n{\n    X509                *cert;\n    ngx_ssl_stapling_t  *staple;\n\n    for (cert = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_certificate_index);\n         cert;\n         cert = X509_get_ex_data(cert, ngx_ssl_next_certificate_index))\n    {\n        staple = X509_get_ex_data(cert, ngx_ssl_stapling_index);\n        staple->resolver = resolver;\n        staple->resolver_timeout = resolver_timeout;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic int\nngx_ssl_certificate_status_callback(ngx_ssl_conn_t *ssl_conn, void *data)\n{\n    int                  rc;\n    X509                *cert;\n    u_char              *p;\n    ngx_connection_t    *c;\n    ngx_ssl_stapling_t  *staple;\n\n    c = ngx_ssl_get_connection(ssl_conn);\n\n    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"SSL certificate status callback\");\n\n    rc = SSL_TLSEXT_ERR_NOACK;\n\n    cert = SSL_get_certificate(ssl_conn);\n\n    if (cert == NULL) {\n        return rc;\n    }\n\n    staple = X509_get_ex_data(cert, ngx_ssl_stapling_index);\n\n    if (staple == NULL) {\n        return rc;\n    }\n\n    if (staple->staple.len\n        && staple->valid >= ngx_time())\n    {\n        /* we have to copy ocsp response as OpenSSL will free it by itself */\n\n        p = OPENSSL_malloc(staple->staple.len);\n        if (p == NULL) {\n            ngx_ssl_error(NGX_LOG_ALERT, c->log, 0, \"OPENSSL_malloc() failed\");\n            return SSL_TLSEXT_ERR_NOACK;\n        }\n\n        ngx_memcpy(p, staple->staple.data, staple->staple.len);\n\n        SSL_set_tlsext_status_ocsp_resp(ssl_conn, p, staple->staple.len);\n\n        rc = SSL_TLSEXT_ERR_OK;\n    }\n\n    ngx_ssl_stapling_update(staple);\n\n    return rc;\n}\n\n\nstatic void\nngx_ssl_stapling_update(ngx_ssl_stapling_t *staple)\n{\n    ngx_ssl_ocsp_ctx_t  *ctx;\n\n    if (staple->host.len == 0\n        || staple->loading || staple->refresh >= ngx_time())\n    {\n        return;\n    }\n\n    staple->loading = 1;\n\n    ctx = ngx_ssl_ocsp_start(ngx_cycle->log);\n    if (ctx == NULL) {\n        return;\n    }\n\n    ctx->ssl_ctx = staple->ssl_ctx;\n    ctx->cert = staple->cert;\n    ctx->issuer = staple->issuer;\n    ctx->chain = staple->chain;\n    ctx->name = staple->name;\n    ctx->flags = (staple->verify ? OCSP_TRUSTOTHER : OCSP_NOVERIFY);\n\n    ctx->addrs = staple->addrs;\n    ctx->naddrs = staple->naddrs;\n    ctx->host = staple->host;\n    ctx->uri = staple->uri;\n    ctx->port = staple->port;\n    ctx->timeout = staple->timeout;\n\n    ctx->resolver = staple->resolver;\n    ctx->resolver_timeout = staple->resolver_timeout;\n\n    ctx->handler = ngx_ssl_stapling_ocsp_handler;\n    ctx->data = staple;\n\n    ngx_ssl_ocsp_request(ctx);\n\n    return;\n}\n\n\nstatic void\nngx_ssl_stapling_ocsp_handler(ngx_ssl_ocsp_ctx_t *ctx)\n{\n    time_t               now;\n    ngx_str_t            response;\n    ngx_ssl_stapling_t  *staple;\n\n    staple = ctx->data;\n    now = ngx_time();\n\n    if (ngx_ssl_ocsp_verify(ctx) != NGX_OK) {\n        goto error;\n    }\n\n    if (ctx->status != V_OCSP_CERTSTATUS_GOOD) {\n        ngx_log_error(NGX_LOG_ERR, ctx->log, 0,\n                      \"certificate status \\\"%s\\\" in the OCSP response\",\n                      OCSP_cert_status_str(ctx->status));\n        goto error;\n    }\n\n    /* copy the response to memory not in ctx->pool */\n\n    response.len = ctx->response->last - ctx->response->pos;\n    response.data = ngx_alloc(response.len, ctx->log);\n\n    if (response.data == NULL) {\n        goto error;\n    }\n\n    ngx_memcpy(response.data, ctx->response->pos, response.len);\n\n    if (staple->staple.data) {\n        ngx_free(staple->staple.data);\n    }\n\n    staple->staple = response;\n    staple->valid = ctx->valid;\n\n    /*\n     * refresh before the response expires,\n     * but not earlier than in 5 minutes, and at least in an hour\n     */\n\n    staple->loading = 0;\n    staple->refresh = ngx_max(ngx_min(ctx->valid - 300, now + 3600), now + 300);\n\n    ngx_ssl_ocsp_done(ctx);\n    return;\n\nerror:\n\n    staple->loading = 0;\n    staple->refresh = now + 300;\n\n    ngx_ssl_ocsp_done(ctx);\n}\n\n\nstatic time_t\nngx_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 time_t.  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\n\nstatic void\nngx_ssl_stapling_cleanup(void *data)\n{\n    ngx_ssl_stapling_t  *staple = data;\n\n    if (staple->issuer) {\n        X509_free(staple->issuer);\n    }\n\n    if (staple->staple.data) {\n        ngx_free(staple->staple.data);\n    }\n}\n\n\nngx_int_t\nngx_ssl_ocsp(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *responder,\n    ngx_uint_t depth, ngx_shm_zone_t *shm_zone)\n{\n    ngx_url_t             u;\n    ngx_ssl_ocsp_conf_t  *ocf;\n\n    ocf = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_ocsp_conf_t));\n    if (ocf == NULL) {\n        return NGX_ERROR;\n    }\n\n    ocf->depth = depth;\n    ocf->shm_zone = shm_zone;\n\n    if (responder->len) {\n        ngx_memzero(&u, sizeof(ngx_url_t));\n\n        u.url = *responder;\n        u.default_port = 80;\n        u.uri_part = 1;\n\n        if (u.url.len > 7\n            && ngx_strncasecmp(u.url.data, (u_char *) \"http://\", 7) == 0)\n        {\n            u.url.len -= 7;\n            u.url.data += 7;\n\n        } else {\n            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                          \"invalid URL prefix in OCSP responder \\\"%V\\\" \"\n                          \"in \\\"ssl_ocsp_responder\\\"\", &u.url);\n            return NGX_ERROR;\n        }\n\n        if (ngx_parse_url(cf->pool, &u) != NGX_OK) {\n            if (u.err) {\n                ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                              \"%s in OCSP responder \\\"%V\\\" \"\n                              \"in \\\"ssl_ocsp_responder\\\"\", u.err, &u.url);\n            }\n\n            return NGX_ERROR;\n        }\n\n        ocf->addrs = u.addrs;\n        ocf->naddrs = u.naddrs;\n        ocf->host = u.host;\n        ocf->uri = u.uri;\n        ocf->port = u.port;\n    }\n\n    if (SSL_CTX_set_ex_data(ssl->ctx, ngx_ssl_ocsp_index, ocf) == 0) {\n        ngx_ssl_error(NGX_LOG_EMERG, ssl->log, 0,\n                      \"SSL_CTX_set_ex_data() failed\");\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_ssl_ocsp_resolver(ngx_conf_t *cf, ngx_ssl_t *ssl,\n    ngx_resolver_t *resolver, ngx_msec_t resolver_timeout)\n{\n    ngx_ssl_ocsp_conf_t  *ocf;\n\n    ocf = SSL_CTX_get_ex_data(ssl->ctx, ngx_ssl_ocsp_index);\n    ocf->resolver = resolver;\n    ocf->resolver_timeout = resolver_timeout;\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_ssl_ocsp_validate(ngx_connection_t *c)\n{\n    X509                 *cert;\n    SSL_CTX              *ssl_ctx;\n    ngx_int_t             rc;\n    X509_STORE           *store;\n    X509_STORE_CTX       *store_ctx;\n    STACK_OF(X509)       *chain;\n    ngx_ssl_ocsp_t       *ocsp;\n    ngx_ssl_ocsp_conf_t  *ocf;\n\n    if (c->ssl->in_ocsp) {\n        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        if (ngx_handle_write_event(c->write, 0) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        return NGX_AGAIN;\n    }\n\n    ssl_ctx = SSL_get_SSL_CTX(c->ssl->connection);\n\n    ocf = SSL_CTX_get_ex_data(ssl_ctx, ngx_ssl_ocsp_index);\n    if (ocf == NULL) {\n        return NGX_OK;\n    }\n\n    if (SSL_get_verify_result(c->ssl->connection) != X509_V_OK) {\n        return NGX_OK;\n    }\n\n    cert = SSL_get_peer_certificate(c->ssl->connection);\n    if (cert == NULL) {\n        return NGX_OK;\n    }\n\n    ocsp = ngx_pcalloc(c->pool, sizeof(ngx_ssl_ocsp_t));\n    if (ocsp == NULL) {\n        X509_free(cert);\n        return NGX_ERROR;\n    }\n\n    c->ssl->ocsp = ocsp;\n\n    ocsp->status = NGX_AGAIN;\n    ocsp->cert_status = V_OCSP_CERTSTATUS_GOOD;\n    ocsp->conf = ocf;\n\n#if (OPENSSL_VERSION_NUMBER >= 0x10100000L && !defined LIBRESSL_VERSION_NUMBER)\n\n    ocsp->certs = SSL_get0_verified_chain(c->ssl->connection);\n\n    if (ocsp->certs) {\n        ocsp->certs = X509_chain_up_ref(ocsp->certs);\n        if (ocsp->certs == NULL) {\n            X509_free(cert);\n            return NGX_ERROR;\n        }\n    }\n\n#endif\n\n    if (ocsp->certs == NULL) {\n        store = SSL_CTX_get_cert_store(ssl_ctx);\n        if (store == NULL) {\n            ngx_ssl_error(NGX_LOG_ERR, c->log, 0,\n                          \"SSL_CTX_get_cert_store() failed\");\n            X509_free(cert);\n            return NGX_ERROR;\n        }\n\n        store_ctx = X509_STORE_CTX_new();\n        if (store_ctx == NULL) {\n            ngx_ssl_error(NGX_LOG_ERR, c->log, 0,\n                          \"X509_STORE_CTX_new() failed\");\n            X509_free(cert);\n            return NGX_ERROR;\n        }\n\n        chain = SSL_get_peer_cert_chain(c->ssl->connection);\n\n        if (X509_STORE_CTX_init(store_ctx, store, cert, chain) == 0) {\n            ngx_ssl_error(NGX_LOG_ERR, c->log, 0,\n                          \"X509_STORE_CTX_init() failed\");\n            X509_STORE_CTX_free(store_ctx);\n            X509_free(cert);\n            return NGX_ERROR;\n        }\n\n        rc = X509_verify_cert(store_ctx);\n        if (rc <= 0) {\n            ngx_ssl_error(NGX_LOG_ERR, c->log, 0, \"X509_verify_cert() failed\");\n            X509_STORE_CTX_free(store_ctx);\n            X509_free(cert);\n            return NGX_ERROR;\n        }\n\n        ocsp->certs = X509_STORE_CTX_get1_chain(store_ctx);\n        if (ocsp->certs == NULL) {\n            ngx_ssl_error(NGX_LOG_ERR, c->log, 0,\n                          \"X509_STORE_CTX_get1_chain() failed\");\n            X509_STORE_CTX_free(store_ctx);\n            X509_free(cert);\n            return NGX_ERROR;\n        }\n\n        X509_STORE_CTX_free(store_ctx);\n    }\n\n    X509_free(cert);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"ssl ocsp validate, certs:%d\", sk_X509_num(ocsp->certs));\n\n    ngx_ssl_ocsp_validate_next(c);\n\n    if (ocsp->status == NGX_AGAIN) {\n        c->ssl->in_ocsp = 1;\n        return NGX_AGAIN;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_ssl_ocsp_validate_next(ngx_connection_t *c)\n{\n    ngx_int_t             rc;\n    ngx_uint_t            n;\n    ngx_ssl_ocsp_t       *ocsp;\n    ngx_ssl_ocsp_ctx_t   *ctx;\n    ngx_ssl_ocsp_conf_t  *ocf;\n\n    ocsp = c->ssl->ocsp;\n    ocf = ocsp->conf;\n\n    n = sk_X509_num(ocsp->certs);\n\n    for ( ;; ) {\n\n        if (ocsp->ncert == n - 1 || (ocf->depth == 2 && ocsp->ncert == 1)) {\n            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                           \"ssl ocsp validated, certs:%ui\", ocsp->ncert);\n            rc = NGX_OK;\n            goto done;\n        }\n\n        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"ssl ocsp validate cert:%ui\", ocsp->ncert);\n\n        ctx = ngx_ssl_ocsp_start(c->log);\n        if (ctx == NULL) {\n            rc = NGX_ERROR;\n            goto done;\n        }\n\n        ocsp->ctx = ctx;\n\n        ctx->ssl_ctx = SSL_get_SSL_CTX(c->ssl->connection);\n        ctx->cert = sk_X509_value(ocsp->certs, ocsp->ncert);\n        ctx->issuer = sk_X509_value(ocsp->certs, ocsp->ncert + 1);\n        ctx->chain = ocsp->certs;\n\n        ctx->resolver = ocf->resolver;\n        ctx->resolver_timeout = ocf->resolver_timeout;\n\n        ctx->handler = ngx_ssl_ocsp_handler;\n        ctx->data = c;\n\n        ctx->shm_zone = ocf->shm_zone;\n\n        ctx->addrs = ocf->addrs;\n        ctx->naddrs = ocf->naddrs;\n        ctx->host = ocf->host;\n        ctx->uri = ocf->uri;\n        ctx->port = ocf->port;\n\n        rc = ngx_ssl_ocsp_responder(c, ctx);\n        if (rc != NGX_OK) {\n            goto done;\n        }\n\n        if (ctx->uri.len == 0) {\n            ngx_str_set(&ctx->uri, \"/\");\n        }\n\n        ocsp->ncert++;\n\n        rc = ngx_ssl_ocsp_cache_lookup(ctx);\n\n        if (rc == NGX_ERROR) {\n            goto done;\n        }\n\n        if (rc == NGX_DECLINED) {\n            break;\n        }\n\n        /* rc == NGX_OK */\n\n        if (ctx->status != V_OCSP_CERTSTATUS_GOOD) {\n            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ctx->log, 0,\n                           \"ssl ocsp cached status \\\"%s\\\"\",\n                           OCSP_cert_status_str(ctx->status));\n            ocsp->cert_status = ctx->status;\n            goto done;\n        }\n\n        ocsp->ctx = NULL;\n        ngx_ssl_ocsp_done(ctx);\n    }\n\n    ngx_ssl_ocsp_request(ctx);\n    return;\n\ndone:\n\n    ocsp->status = rc;\n\n    if (c->ssl->in_ocsp) {\n        c->ssl->handshaked = 1;\n        c->ssl->handler(c);\n    }\n}\n\n\nstatic void\nngx_ssl_ocsp_handler(ngx_ssl_ocsp_ctx_t *ctx)\n{\n    ngx_int_t          rc;\n    ngx_ssl_ocsp_t    *ocsp;\n    ngx_connection_t  *c;\n\n    c = ctx->data;\n    ocsp = c->ssl->ocsp;\n    ocsp->ctx = NULL;\n\n    rc = ngx_ssl_ocsp_verify(ctx);\n    if (rc != NGX_OK) {\n        goto done;\n    }\n\n    rc = ngx_ssl_ocsp_cache_store(ctx);\n    if (rc != NGX_OK) {\n        goto done;\n    }\n\n    if (ctx->status != V_OCSP_CERTSTATUS_GOOD) {\n        ocsp->cert_status = ctx->status;\n        goto done;\n    }\n\n    ngx_ssl_ocsp_done(ctx);\n\n    ngx_ssl_ocsp_validate_next(c);\n\n    return;\n\ndone:\n\n    ocsp->status = rc;\n    ngx_ssl_ocsp_done(ctx);\n\n    if (c->ssl->in_ocsp) {\n        c->ssl->handshaked = 1;\n        c->ssl->handler(c);\n    }\n}\n\n\nstatic ngx_int_t\nngx_ssl_ocsp_responder(ngx_connection_t *c, ngx_ssl_ocsp_ctx_t *ctx)\n{\n    char                      *s;\n    ngx_str_t                  responder;\n    ngx_url_t                  u;\n    STACK_OF(OPENSSL_STRING)  *aia;\n\n    if (ctx->host.len) {\n        return NGX_OK;\n    }\n\n    /* extract OCSP responder URL from certificate */\n\n    aia = X509_get1_ocsp(ctx->cert);\n    if (aia == NULL) {\n        ngx_log_error(NGX_LOG_ERR, c->log, 0,\n                      \"no OCSP responder URL in certificate\");\n        return NGX_ERROR;\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        ngx_log_error(NGX_LOG_ERR, c->log, 0,\n                      \"no OCSP responder URL in certificate\");\n        X509_email_free(aia);\n        return NGX_ERROR;\n    }\n\n    responder.len = ngx_strlen(s);\n    responder.data = ngx_palloc(ctx->pool, responder.len);\n    if (responder.data == NULL) {\n        X509_email_free(aia);\n        return NGX_ERROR;\n    }\n\n    ngx_memcpy(responder.data, s, responder.len);\n    X509_email_free(aia);\n\n    ngx_memzero(&u, sizeof(ngx_url_t));\n\n    u.url = responder;\n    u.default_port = 80;\n    u.uri_part = 1;\n    u.no_resolve = 1;\n\n    if (u.url.len > 7\n        && ngx_strncasecmp(u.url.data, (u_char *) \"http://\", 7) == 0)\n    {\n        u.url.len -= 7;\n        u.url.data += 7;\n\n    } else {\n        ngx_log_error(NGX_LOG_ERR, c->log, 0,\n                      \"invalid URL prefix in OCSP responder \\\"%V\\\" \"\n                      \"in certificate\", &u.url);\n        return NGX_ERROR;\n    }\n\n    if (ngx_parse_url(ctx->pool, &u) != NGX_OK) {\n        if (u.err) {\n            ngx_log_error(NGX_LOG_ERR, c->log, 0,\n                          \"%s in OCSP responder \\\"%V\\\" in certificate\",\n                          u.err, &u.url);\n        }\n\n        return NGX_ERROR;\n    }\n\n    if (u.host.len == 0) {\n        ngx_log_error(NGX_LOG_ERR, c->log, 0,\n                      \"empty host in OCSP responder in certificate\");\n        return NGX_ERROR;\n    }\n\n    ctx->addrs = u.addrs;\n    ctx->naddrs = u.naddrs;\n    ctx->host = u.host;\n    ctx->uri = u.uri;\n    ctx->port = u.port;\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_ssl_ocsp_get_status(ngx_connection_t *c, const char **s)\n{\n    ngx_ssl_ocsp_t  *ocsp;\n\n    ocsp = c->ssl->ocsp;\n    if (ocsp == NULL) {\n        return NGX_OK;\n    }\n\n    if (ocsp->status == NGX_ERROR) {\n        *s = \"certificate status request failed\";\n        return NGX_DECLINED;\n    }\n\n    switch (ocsp->cert_status) {\n\n    case V_OCSP_CERTSTATUS_GOOD:\n        return NGX_OK;\n\n    case V_OCSP_CERTSTATUS_REVOKED:\n        *s = \"certificate revoked\";\n        break;\n\n    default: /* V_OCSP_CERTSTATUS_UNKNOWN */\n        *s = \"certificate status unknown\";\n    }\n\n    return NGX_DECLINED;\n}\n\n\nvoid\nngx_ssl_ocsp_cleanup(ngx_connection_t *c)\n{\n    ngx_ssl_ocsp_t  *ocsp;\n\n    ocsp = c->ssl->ocsp;\n    if (ocsp == NULL) {\n        return;\n    }\n\n    if (ocsp->ctx) {\n        ngx_ssl_ocsp_done(ocsp->ctx);\n        ocsp->ctx = NULL;\n    }\n\n    if (ocsp->certs) {\n        sk_X509_pop_free(ocsp->certs, X509_free);\n        ocsp->certs = NULL;\n    }\n}\n\n\nstatic ngx_ssl_ocsp_ctx_t *\nngx_ssl_ocsp_start(ngx_log_t *log)\n{\n    ngx_pool_t          *pool;\n    ngx_ssl_ocsp_ctx_t  *ctx;\n\n    pool = ngx_create_pool(2048, log);\n    if (pool == NULL) {\n        return NULL;\n    }\n\n    ctx = ngx_pcalloc(pool, sizeof(ngx_ssl_ocsp_ctx_t));\n    if (ctx == NULL) {\n        ngx_destroy_pool(pool);\n        return NULL;\n    }\n\n    log = ngx_palloc(pool, sizeof(ngx_log_t));\n    if (log == NULL) {\n        ngx_destroy_pool(pool);\n        return NULL;\n    }\n\n    ctx->pool = pool;\n\n    *log = *ctx->pool->log;\n\n    ctx->pool->log = log;\n    ctx->log = log;\n\n    log->handler = ngx_ssl_ocsp_log_error;\n    log->data = ctx;\n    log->action = \"requesting certificate status\";\n\n    return ctx;\n}\n\n\nstatic void\nngx_ssl_ocsp_done(ngx_ssl_ocsp_ctx_t *ctx)\n{\n    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,\n                   \"ssl ocsp done\");\n\n    if (ctx->peer.connection) {\n        ngx_close_connection(ctx->peer.connection);\n    }\n\n    ngx_destroy_pool(ctx->pool);\n}\n\n\nstatic void\nngx_ssl_ocsp_error(ngx_ssl_ocsp_ctx_t *ctx)\n{\n    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,\n                   \"ssl ocsp error\");\n\n    ctx->code = 0;\n    ctx->handler(ctx);\n}\n\n\nstatic void\nngx_ssl_ocsp_next(ngx_ssl_ocsp_ctx_t *ctx)\n{\n    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,\n                   \"ssl ocsp next\");\n\n    if (++ctx->naddr >= ctx->naddrs) {\n        ngx_ssl_ocsp_error(ctx);\n        return;\n    }\n\n    ctx->request->pos = ctx->request->start;\n\n    if (ctx->response) {\n        ctx->response->last = ctx->response->pos;\n    }\n\n    if (ctx->peer.connection) {\n        ngx_close_connection(ctx->peer.connection);\n        ctx->peer.connection = NULL;\n    }\n\n    ctx->state = 0;\n    ctx->count = 0;\n    ctx->done = 0;\n\n    ngx_ssl_ocsp_connect(ctx);\n}\n\n\nstatic void\nngx_ssl_ocsp_request(ngx_ssl_ocsp_ctx_t *ctx)\n{\n    ngx_resolver_ctx_t  *resolve, temp;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,\n                   \"ssl ocsp request\");\n\n    if (ngx_ssl_ocsp_create_request(ctx) != NGX_OK) {\n        ngx_ssl_ocsp_error(ctx);\n        return;\n    }\n\n    if (ctx->resolver) {\n        /* resolve OCSP responder hostname */\n\n        temp.name = ctx->host;\n\n        resolve = ngx_resolve_start(ctx->resolver, &temp);\n        if (resolve == NULL) {\n            ngx_ssl_ocsp_error(ctx);\n            return;\n        }\n\n        if (resolve == NGX_NO_RESOLVER) {\n            if (ctx->naddrs == 0) {\n                ngx_log_error(NGX_LOG_ERR, ctx->log, 0,\n                              \"no resolver defined to resolve %V\", &ctx->host);\n\n                ngx_ssl_ocsp_error(ctx);\n                return;\n            }\n\n            ngx_log_error(NGX_LOG_WARN, ctx->log, 0,\n                          \"no resolver defined to resolve %V\", &ctx->host);\n            goto connect;\n        }\n\n        resolve->name = ctx->host;\n        resolve->handler = ngx_ssl_ocsp_resolve_handler;\n        resolve->data = ctx;\n        resolve->timeout = ctx->resolver_timeout;\n\n        if (ngx_resolve_name(resolve) != NGX_OK) {\n            ngx_ssl_ocsp_error(ctx);\n            return;\n        }\n\n        return;\n    }\n\nconnect:\n\n    ngx_ssl_ocsp_connect(ctx);\n}\n\n\nstatic void\nngx_ssl_ocsp_resolve_handler(ngx_resolver_ctx_t *resolve)\n{\n    ngx_ssl_ocsp_ctx_t *ctx = resolve->data;\n\n    u_char           *p;\n    size_t            len;\n    socklen_t         socklen;\n    ngx_uint_t        i;\n    struct sockaddr  *sockaddr;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,\n                   \"ssl ocsp resolve handler\");\n\n    if (resolve->state) {\n        ngx_log_error(NGX_LOG_ERR, ctx->log, 0,\n                      \"%V could not be resolved (%i: %s)\",\n                      &resolve->name, resolve->state,\n                      ngx_resolver_strerror(resolve->state));\n        goto failed;\n    }\n\n#if (NGX_DEBUG)\n    {\n    u_char     text[NGX_SOCKADDR_STRLEN];\n    ngx_str_t  addr;\n\n    addr.data = text;\n\n    for (i = 0; i < resolve->naddrs; i++) {\n        addr.len = ngx_sock_ntop(resolve->addrs[i].sockaddr,\n                                 resolve->addrs[i].socklen,\n                                 text, NGX_SOCKADDR_STRLEN, 0);\n\n        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ctx->log, 0,\n                       \"name was resolved to %V\", &addr);\n\n    }\n    }\n#endif\n\n    ctx->naddrs = resolve->naddrs;\n    ctx->addrs = ngx_pcalloc(ctx->pool, ctx->naddrs * sizeof(ngx_addr_t));\n\n    if (ctx->addrs == NULL) {\n        goto failed;\n    }\n\n    for (i = 0; i < resolve->naddrs; i++) {\n\n        socklen = resolve->addrs[i].socklen;\n\n        sockaddr = ngx_palloc(ctx->pool, socklen);\n        if (sockaddr == NULL) {\n            goto failed;\n        }\n\n        ngx_memcpy(sockaddr, resolve->addrs[i].sockaddr, socklen);\n        ngx_inet_set_port(sockaddr, ctx->port);\n\n        ctx->addrs[i].sockaddr = sockaddr;\n        ctx->addrs[i].socklen = socklen;\n\n        p = ngx_pnalloc(ctx->pool, NGX_SOCKADDR_STRLEN);\n        if (p == NULL) {\n            goto failed;\n        }\n\n        len = ngx_sock_ntop(sockaddr, socklen, p, NGX_SOCKADDR_STRLEN, 1);\n\n        ctx->addrs[i].name.len = len;\n        ctx->addrs[i].name.data = p;\n    }\n\n    ngx_resolve_name_done(resolve);\n\n    ngx_ssl_ocsp_connect(ctx);\n    return;\n\nfailed:\n\n    ngx_resolve_name_done(resolve);\n    ngx_ssl_ocsp_error(ctx);\n}\n\n\nstatic void\nngx_ssl_ocsp_connect(ngx_ssl_ocsp_ctx_t *ctx)\n{\n    ngx_int_t    rc;\n    ngx_addr_t  *addr;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ctx->log, 0,\n                   \"ssl ocsp connect %ui/%ui\", ctx->naddr, ctx->naddrs);\n\n    addr = &ctx->addrs[ctx->naddr];\n\n    ctx->peer.sockaddr = addr->sockaddr;\n    ctx->peer.socklen = addr->socklen;\n    ctx->peer.name = &addr->name;\n    ctx->peer.get = ngx_event_get_peer;\n    ctx->peer.log = ctx->log;\n    ctx->peer.log_error = NGX_ERROR_ERR;\n\n    rc = ngx_event_connect_peer(&ctx->peer);\n\n    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,\n                   \"ssl ocsp connect peer done\");\n\n    if (rc == NGX_ERROR) {\n        ngx_ssl_ocsp_error(ctx);\n        return;\n    }\n\n    if (rc == NGX_BUSY || rc == NGX_DECLINED) {\n        ngx_ssl_ocsp_next(ctx);\n        return;\n    }\n\n    ctx->peer.connection->data = ctx;\n    ctx->peer.connection->pool = ctx->pool;\n\n    ctx->peer.connection->read->handler = ngx_ssl_ocsp_read_handler;\n    ctx->peer.connection->write->handler = ngx_ssl_ocsp_write_handler;\n\n    ctx->process = ngx_ssl_ocsp_process_status_line;\n\n    if (ctx->timeout) {\n        ngx_add_timer(ctx->peer.connection->read, ctx->timeout);\n        ngx_add_timer(ctx->peer.connection->write, ctx->timeout);\n    }\n\n    if (rc == NGX_OK) {\n        ngx_ssl_ocsp_write_handler(ctx->peer.connection->write);\n        return;\n    }\n}\n\n\nstatic void\nngx_ssl_ocsp_write_handler(ngx_event_t *wev)\n{\n    ssize_t              n, size;\n    ngx_connection_t    *c;\n    ngx_ssl_ocsp_ctx_t  *ctx;\n\n    c = wev->data;\n    ctx = c->data;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, wev->log, 0,\n                   \"ssl ocsp write handler\");\n\n    if (wev->timedout) {\n        ngx_log_error(NGX_LOG_ERR, wev->log, NGX_ETIMEDOUT,\n                      \"OCSP responder timed out\");\n        ngx_ssl_ocsp_next(ctx);\n        return;\n    }\n\n    size = ctx->request->last - ctx->request->pos;\n\n    n = ngx_send(c, ctx->request->pos, size);\n\n    if (n == NGX_ERROR) {\n        ngx_ssl_ocsp_next(ctx);\n        return;\n    }\n\n    if (n > 0) {\n        ctx->request->pos += n;\n\n        if (n == size) {\n            wev->handler = ngx_ssl_ocsp_dummy_handler;\n\n            if (wev->timer_set) {\n                ngx_del_timer(wev);\n            }\n\n            if (ngx_handle_write_event(wev, 0) != NGX_OK) {\n                ngx_ssl_ocsp_error(ctx);\n            }\n\n            return;\n        }\n    }\n\n    if (!wev->timer_set && ctx->timeout) {\n        ngx_add_timer(wev, ctx->timeout);\n    }\n}\n\n\nstatic void\nngx_ssl_ocsp_read_handler(ngx_event_t *rev)\n{\n    ssize_t              n, size;\n    ngx_int_t            rc;\n    ngx_connection_t    *c;\n    ngx_ssl_ocsp_ctx_t  *ctx;\n\n    c = rev->data;\n    ctx = c->data;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, rev->log, 0,\n                   \"ssl ocsp read handler\");\n\n    if (rev->timedout) {\n        ngx_log_error(NGX_LOG_ERR, rev->log, NGX_ETIMEDOUT,\n                      \"OCSP responder timed out\");\n        ngx_ssl_ocsp_next(ctx);\n        return;\n    }\n\n    if (ctx->response == NULL) {\n        ctx->response = ngx_create_temp_buf(ctx->pool, 16384);\n        if (ctx->response == NULL) {\n            ngx_ssl_ocsp_error(ctx);\n            return;\n        }\n    }\n\n    for ( ;; ) {\n\n        size = ctx->response->end - ctx->response->last;\n\n        n = ngx_recv(c, ctx->response->last, size);\n\n        if (n > 0) {\n            ctx->response->last += n;\n\n            rc = ctx->process(ctx);\n\n            if (rc == NGX_ERROR) {\n                ngx_ssl_ocsp_next(ctx);\n                return;\n            }\n\n            continue;\n        }\n\n        if (n == NGX_AGAIN) {\n\n            if (ngx_handle_read_event(rev, 0) != NGX_OK) {\n                ngx_ssl_ocsp_error(ctx);\n            }\n\n            return;\n        }\n\n        break;\n    }\n\n    ctx->done = 1;\n\n    rc = ctx->process(ctx);\n\n    if (rc == NGX_DONE) {\n        /* ctx->handler() was called */\n        return;\n    }\n\n    ngx_log_error(NGX_LOG_ERR, ctx->log, 0,\n                  \"OCSP responder prematurely closed connection\");\n\n    ngx_ssl_ocsp_next(ctx);\n}\n\n\nstatic void\nngx_ssl_ocsp_dummy_handler(ngx_event_t *ev)\n{\n    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0,\n                   \"ssl ocsp dummy handler\");\n}\n\n\nstatic ngx_int_t\nngx_ssl_ocsp_create_request(ngx_ssl_ocsp_ctx_t *ctx)\n{\n    int            len;\n    u_char        *p;\n    uintptr_t      escape;\n    ngx_str_t      binary, base64;\n    ngx_buf_t     *b;\n    OCSP_CERTID   *id;\n    OCSP_REQUEST  *ocsp;\n\n    ocsp = OCSP_REQUEST_new();\n    if (ocsp == NULL) {\n        ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0,\n                      \"OCSP_REQUEST_new() failed\");\n        return NGX_ERROR;\n    }\n\n    id = OCSP_cert_to_id(NULL, ctx->cert, ctx->issuer);\n    if (id == NULL) {\n        ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0,\n                      \"OCSP_cert_to_id() failed\");\n        goto failed;\n    }\n\n    if (OCSP_request_add0_id(ocsp, id) == NULL) {\n        ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0,\n                      \"OCSP_request_add0_id() failed\");\n        OCSP_CERTID_free(id);\n        goto failed;\n    }\n\n    len = i2d_OCSP_REQUEST(ocsp, NULL);\n    if (len <= 0) {\n        ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0,\n                      \"i2d_OCSP_REQUEST() failed\");\n        goto failed;\n    }\n\n    binary.len = len;\n    binary.data = ngx_palloc(ctx->pool, len);\n    if (binary.data == NULL) {\n        goto failed;\n    }\n\n    p = binary.data;\n    len = i2d_OCSP_REQUEST(ocsp, &p);\n    if (len <= 0) {\n        ngx_ssl_error(NGX_LOG_EMERG, ctx->log, 0,\n                      \"i2d_OCSP_REQUEST() failed\");\n        goto failed;\n    }\n\n    base64.len = ngx_base64_encoded_length(binary.len);\n    base64.data = ngx_palloc(ctx->pool, base64.len);\n    if (base64.data == NULL) {\n        goto failed;\n    }\n\n    ngx_encode_base64(&base64, &binary);\n\n    escape = ngx_escape_uri(NULL, base64.data, base64.len,\n                            NGX_ESCAPE_URI_COMPONENT);\n\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ctx->log, 0,\n                   \"ssl ocsp request length %z, escape %d\",\n                   base64.len, (int) escape);\n\n    len = sizeof(\"GET \") - 1 + ctx->uri.len + sizeof(\"/\") - 1\n          + base64.len + 2 * escape + sizeof(\" HTTP/1.0\" CRLF) - 1\n          + sizeof(\"Host: \") - 1 + ctx->host.len + sizeof(CRLF) - 1\n          + sizeof(CRLF) - 1;\n\n    b = ngx_create_temp_buf(ctx->pool, len);\n    if (b == NULL) {\n        goto failed;\n    }\n\n    p = b->last;\n\n    p = ngx_cpymem(p, \"GET \", sizeof(\"GET \") - 1);\n    p = ngx_cpymem(p, ctx->uri.data, ctx->uri.len);\n\n    if (ctx->uri.data[ctx->uri.len - 1] != '/') {\n        *p++ = '/';\n    }\n\n    if (escape == 0) {\n        p = ngx_cpymem(p, base64.data, base64.len);\n\n    } else {\n        p = (u_char *) ngx_escape_uri(p, base64.data, base64.len,\n                                      NGX_ESCAPE_URI_COMPONENT);\n    }\n\n    p = ngx_cpymem(p, \" HTTP/1.0\" CRLF, sizeof(\" HTTP/1.0\" CRLF) - 1);\n    p = ngx_cpymem(p, \"Host: \", sizeof(\"Host: \") - 1);\n    p = ngx_cpymem(p, ctx->host.data, ctx->host.len);\n    *p++ = CR; *p++ = LF;\n\n    /* add \"\\r\\n\" at the header end */\n    *p++ = CR; *p++ = LF;\n\n    b->last = p;\n    ctx->request = b;\n\n    OCSP_REQUEST_free(ocsp);\n\n    return NGX_OK;\n\nfailed:\n\n    OCSP_REQUEST_free(ocsp);\n\n    return NGX_ERROR;\n}\n\n\nstatic ngx_int_t\nngx_ssl_ocsp_process_status_line(ngx_ssl_ocsp_ctx_t *ctx)\n{\n    ngx_int_t  rc;\n\n    rc = ngx_ssl_ocsp_parse_status_line(ctx);\n\n    if (rc == NGX_OK) {\n        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ctx->log, 0,\n                       \"ssl ocsp status %ui \\\"%*s\\\"\",\n                       ctx->code,\n                       ctx->header_end - ctx->header_start,\n                       ctx->header_start);\n\n        ctx->process = ngx_ssl_ocsp_process_headers;\n        return ctx->process(ctx);\n    }\n\n    if (rc == NGX_AGAIN) {\n        return NGX_AGAIN;\n    }\n\n    /* rc == NGX_ERROR */\n\n    ngx_log_error(NGX_LOG_ERR, ctx->log, 0,\n                  \"OCSP responder sent invalid response\");\n\n    return NGX_ERROR;\n}\n\n\nstatic ngx_int_t\nngx_ssl_ocsp_parse_status_line(ngx_ssl_ocsp_ctx_t *ctx)\n{\n    u_char      ch;\n    u_char     *p;\n    ngx_buf_t  *b;\n    enum {\n        sw_start = 0,\n        sw_H,\n        sw_HT,\n        sw_HTT,\n        sw_HTTP,\n        sw_first_major_digit,\n        sw_major_digit,\n        sw_first_minor_digit,\n        sw_minor_digit,\n        sw_status,\n        sw_space_after_status,\n        sw_status_text,\n        sw_almost_done\n    } state;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,\n                   \"ssl ocsp process status line\");\n\n    state = ctx->state;\n    b = ctx->response;\n\n    for (p = b->pos; p < b->last; p++) {\n        ch = *p;\n\n        switch (state) {\n\n        /* \"HTTP/\" */\n        case sw_start:\n            switch (ch) {\n            case 'H':\n                state = sw_H;\n                break;\n            default:\n                return NGX_ERROR;\n            }\n            break;\n\n        case sw_H:\n            switch (ch) {\n            case 'T':\n                state = sw_HT;\n                break;\n            default:\n                return NGX_ERROR;\n            }\n            break;\n\n        case sw_HT:\n            switch (ch) {\n            case 'T':\n                state = sw_HTT;\n                break;\n            default:\n                return NGX_ERROR;\n            }\n            break;\n\n        case sw_HTT:\n            switch (ch) {\n            case 'P':\n                state = sw_HTTP;\n                break;\n            default:\n                return NGX_ERROR;\n            }\n            break;\n\n        case sw_HTTP:\n            switch (ch) {\n            case '/':\n                state = sw_first_major_digit;\n                break;\n            default:\n                return NGX_ERROR;\n            }\n            break;\n\n        /* the first digit of major HTTP version */\n        case sw_first_major_digit:\n            if (ch < '1' || ch > '9') {\n                return NGX_ERROR;\n            }\n\n            state = sw_major_digit;\n            break;\n\n        /* the major HTTP version or dot */\n        case sw_major_digit:\n            if (ch == '.') {\n                state = sw_first_minor_digit;\n                break;\n            }\n\n            if (ch < '0' || ch > '9') {\n                return NGX_ERROR;\n            }\n\n            break;\n\n        /* the first digit of minor HTTP version */\n        case sw_first_minor_digit:\n            if (ch < '0' || ch > '9') {\n                return NGX_ERROR;\n            }\n\n            state = sw_minor_digit;\n            break;\n\n        /* the minor HTTP version or the end of the request line */\n        case sw_minor_digit:\n            if (ch == ' ') {\n                state = sw_status;\n                break;\n            }\n\n            if (ch < '0' || ch > '9') {\n                return NGX_ERROR;\n            }\n\n            break;\n\n        /* HTTP status code */\n        case sw_status:\n            if (ch == ' ') {\n                break;\n            }\n\n            if (ch < '0' || ch > '9') {\n                return NGX_ERROR;\n            }\n\n            ctx->code = ctx->code * 10 + (ch - '0');\n\n            if (++ctx->count == 3) {\n                state = sw_space_after_status;\n                ctx->header_start = p - 2;\n            }\n\n            break;\n\n        /* space or end of line */\n        case sw_space_after_status:\n            switch (ch) {\n            case ' ':\n                state = sw_status_text;\n                break;\n            case '.':                    /* IIS may send 403.1, 403.2, etc */\n                state = sw_status_text;\n                break;\n            case CR:\n                state = sw_almost_done;\n                break;\n            case LF:\n                ctx->header_end = p;\n                goto done;\n            default:\n                return NGX_ERROR;\n            }\n            break;\n\n        /* any text until end of line */\n        case sw_status_text:\n            switch (ch) {\n            case CR:\n                state = sw_almost_done;\n                break;\n            case LF:\n                ctx->header_end = p;\n                goto done;\n            }\n            break;\n\n        /* end of status line */\n        case sw_almost_done:\n            switch (ch) {\n            case LF:\n                ctx->header_end = p - 1;\n                goto done;\n            default:\n                return NGX_ERROR;\n            }\n        }\n    }\n\n    b->pos = p;\n    ctx->state = state;\n\n    return NGX_AGAIN;\n\ndone:\n\n    b->pos = p + 1;\n    ctx->state = sw_start;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_ssl_ocsp_process_headers(ngx_ssl_ocsp_ctx_t *ctx)\n{\n    size_t     len;\n    ngx_int_t  rc;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,\n                   \"ssl ocsp process headers\");\n\n    for ( ;; ) {\n        rc = ngx_ssl_ocsp_parse_header_line(ctx);\n\n        if (rc == NGX_OK) {\n\n            ngx_log_debug4(NGX_LOG_DEBUG_EVENT, ctx->log, 0,\n                           \"ssl ocsp header \\\"%*s: %*s\\\"\",\n                           ctx->header_name_end - ctx->header_name_start,\n                           ctx->header_name_start,\n                           ctx->header_end - ctx->header_start,\n                           ctx->header_start);\n\n            len = ctx->header_name_end - ctx->header_name_start;\n\n            if (len == sizeof(\"Content-Type\") - 1\n                && ngx_strncasecmp(ctx->header_name_start,\n                                   (u_char *) \"Content-Type\",\n                                   sizeof(\"Content-Type\") - 1)\n                   == 0)\n            {\n                len = ctx->header_end - ctx->header_start;\n\n                if (len != sizeof(\"application/ocsp-response\") - 1\n                    || ngx_strncasecmp(ctx->header_start,\n                                       (u_char *) \"application/ocsp-response\",\n                                       sizeof(\"application/ocsp-response\") - 1)\n                       != 0)\n                {\n                    ngx_log_error(NGX_LOG_ERR, ctx->log, 0,\n                                  \"OCSP responder sent invalid \"\n                                  \"\\\"Content-Type\\\" header: \\\"%*s\\\"\",\n                                  ctx->header_end - ctx->header_start,\n                                  ctx->header_start);\n                    return NGX_ERROR;\n                }\n\n                continue;\n            }\n\n            /* TODO: honor Content-Length */\n\n            continue;\n        }\n\n        if (rc == NGX_DONE) {\n            break;\n        }\n\n        if (rc == NGX_AGAIN) {\n            return NGX_AGAIN;\n        }\n\n        /* rc == NGX_ERROR */\n\n        ngx_log_error(NGX_LOG_ERR, ctx->log, 0,\n                      \"OCSP responder sent invalid response\");\n\n        return NGX_ERROR;\n    }\n\n    ctx->process = ngx_ssl_ocsp_process_body;\n    return ctx->process(ctx);\n}\n\n\nstatic ngx_int_t\nngx_ssl_ocsp_parse_header_line(ngx_ssl_ocsp_ctx_t *ctx)\n{\n    u_char  c, ch, *p;\n    enum {\n        sw_start = 0,\n        sw_name,\n        sw_space_before_value,\n        sw_value,\n        sw_space_after_value,\n        sw_almost_done,\n        sw_header_almost_done\n    } state;\n\n    state = ctx->state;\n\n    for (p = ctx->response->pos; p < ctx->response->last; p++) {\n        ch = *p;\n\n#if 0\n        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ctx->log, 0,\n                       \"s:%d in:'%02Xd:%c'\", state, ch, ch);\n#endif\n\n        switch (state) {\n\n        /* first char */\n        case sw_start:\n\n            switch (ch) {\n            case CR:\n                ctx->header_end = p;\n                state = sw_header_almost_done;\n                break;\n            case LF:\n                ctx->header_end = p;\n                goto header_done;\n            default:\n                state = sw_name;\n                ctx->header_name_start = p;\n\n                c = (u_char) (ch | 0x20);\n                if (c >= 'a' && c <= 'z') {\n                    break;\n                }\n\n                if (ch >= '0' && ch <= '9') {\n                    break;\n                }\n\n                return NGX_ERROR;\n            }\n            break;\n\n        /* header name */\n        case sw_name:\n            c = (u_char) (ch | 0x20);\n            if (c >= 'a' && c <= 'z') {\n                break;\n            }\n\n            if (ch == ':') {\n                ctx->header_name_end = p;\n                state = sw_space_before_value;\n                break;\n            }\n\n            if (ch == '-') {\n                break;\n            }\n\n            if (ch >= '0' && ch <= '9') {\n                break;\n            }\n\n            if (ch == CR) {\n                ctx->header_name_end = p;\n                ctx->header_start = p;\n                ctx->header_end = p;\n                state = sw_almost_done;\n                break;\n            }\n\n            if (ch == LF) {\n                ctx->header_name_end = p;\n                ctx->header_start = p;\n                ctx->header_end = p;\n                goto done;\n            }\n\n            return NGX_ERROR;\n\n        /* space* before header value */\n        case sw_space_before_value:\n            switch (ch) {\n            case ' ':\n                break;\n            case CR:\n                ctx->header_start = p;\n                ctx->header_end = p;\n                state = sw_almost_done;\n                break;\n            case LF:\n                ctx->header_start = p;\n                ctx->header_end = p;\n                goto done;\n            default:\n                ctx->header_start = p;\n                state = sw_value;\n                break;\n            }\n            break;\n\n        /* header value */\n        case sw_value:\n            switch (ch) {\n            case ' ':\n                ctx->header_end = p;\n                state = sw_space_after_value;\n                break;\n            case CR:\n                ctx->header_end = p;\n                state = sw_almost_done;\n                break;\n            case LF:\n                ctx->header_end = p;\n                goto done;\n            }\n            break;\n\n        /* space* before end of header line */\n        case sw_space_after_value:\n            switch (ch) {\n            case ' ':\n                break;\n            case CR:\n                state = sw_almost_done;\n                break;\n            case LF:\n                goto done;\n            default:\n                state = sw_value;\n                break;\n            }\n            break;\n\n        /* end of header line */\n        case sw_almost_done:\n            switch (ch) {\n            case LF:\n                goto done;\n            default:\n                return NGX_ERROR;\n            }\n\n        /* end of header */\n        case sw_header_almost_done:\n            switch (ch) {\n            case LF:\n                goto header_done;\n            default:\n                return NGX_ERROR;\n            }\n        }\n    }\n\n    ctx->response->pos = p;\n    ctx->state = state;\n\n    return NGX_AGAIN;\n\ndone:\n\n    ctx->response->pos = p + 1;\n    ctx->state = sw_start;\n\n    return NGX_OK;\n\nheader_done:\n\n    ctx->response->pos = p + 1;\n    ctx->state = sw_start;\n\n    return NGX_DONE;\n}\n\n\nstatic ngx_int_t\nngx_ssl_ocsp_process_body(ngx_ssl_ocsp_ctx_t *ctx)\n{\n    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,\n                   \"ssl ocsp process body\");\n\n    if (ctx->done) {\n        ctx->handler(ctx);\n        return NGX_DONE;\n    }\n\n    return NGX_AGAIN;\n}\n\n\nstatic ngx_int_t\nngx_ssl_ocsp_verify(ngx_ssl_ocsp_ctx_t *ctx)\n{\n    int                    n;\n    size_t                 len;\n    X509_STORE            *store;\n    const u_char          *p;\n    OCSP_CERTID           *id;\n    OCSP_RESPONSE         *ocsp;\n    OCSP_BASICRESP        *basic;\n    ASN1_GENERALIZEDTIME  *thisupdate, *nextupdate;\n\n    ocsp = NULL;\n    basic = NULL;\n    id = NULL;\n\n    if (ctx->code != 200) {\n        goto error;\n    }\n\n    /* check the response */\n\n    len = ctx->response->last - ctx->response->pos;\n    p = ctx->response->pos;\n\n    ocsp = d2i_OCSP_RESPONSE(NULL, &p, len);\n    if (ocsp == NULL) {\n        ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0,\n                      \"d2i_OCSP_RESPONSE() failed\");\n        goto error;\n    }\n\n    n = OCSP_response_status(ocsp);\n\n    if (n != OCSP_RESPONSE_STATUS_SUCCESSFUL) {\n        ngx_log_error(NGX_LOG_ERR, ctx->log, 0,\n                      \"OCSP response not successful (%d: %s)\",\n                      n, OCSP_response_status_str(n));\n        goto error;\n    }\n\n    basic = OCSP_response_get1_basic(ocsp);\n    if (basic == NULL) {\n        ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0,\n                      \"OCSP_response_get1_basic() failed\");\n        goto error;\n    }\n\n    store = SSL_CTX_get_cert_store(ctx->ssl_ctx);\n    if (store == NULL) {\n        ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0,\n                      \"SSL_CTX_get_cert_store() failed\");\n        goto error;\n    }\n\n    if (OCSP_basic_verify(basic, ctx->chain, store, ctx->flags) != 1) {\n        ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0,\n                      \"OCSP_basic_verify() failed\");\n        goto error;\n    }\n\n    id = OCSP_cert_to_id(NULL, ctx->cert, ctx->issuer);\n    if (id == NULL) {\n        ngx_ssl_error(NGX_LOG_CRIT, ctx->log, 0,\n                      \"OCSP_cert_to_id() failed\");\n        goto error;\n    }\n\n    if (OCSP_resp_find_status(basic, id, &ctx->status, NULL, NULL,\n                              &thisupdate, &nextupdate)\n        != 1)\n    {\n        ngx_log_error(NGX_LOG_ERR, ctx->log, 0,\n                      \"certificate status not found in the OCSP response\");\n        goto error;\n    }\n\n    if (OCSP_check_validity(thisupdate, nextupdate, 300, -1) != 1) {\n        ngx_ssl_error(NGX_LOG_ERR, ctx->log, 0,\n                      \"OCSP_check_validity() failed\");\n        goto error;\n    }\n\n    if (nextupdate) {\n        ctx->valid = ngx_ssl_stapling_time(nextupdate);\n        if (ctx->valid == (time_t) NGX_ERROR) {\n            ngx_log_error(NGX_LOG_ERR, ctx->log, 0,\n                          \"invalid nextUpdate time in certificate status\");\n            goto error;\n        }\n\n    } else {\n        ctx->valid = NGX_MAX_TIME_T_VALUE;\n    }\n\n    OCSP_CERTID_free(id);\n    OCSP_BASICRESP_free(basic);\n    OCSP_RESPONSE_free(ocsp);\n\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ctx->log, 0,\n                   \"ssl ocsp response, %s, %uz\",\n                   OCSP_cert_status_str(ctx->status), len);\n\n    return NGX_OK;\n\nerror:\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    return NGX_ERROR;\n}\n\n\nngx_int_t\nngx_ssl_ocsp_cache_init(ngx_shm_zone_t *shm_zone, void *data)\n{\n    size_t                 len;\n    ngx_slab_pool_t       *shpool;\n    ngx_ssl_ocsp_cache_t  *cache;\n\n    if (data) {\n        shm_zone->data = data;\n        return NGX_OK;\n    }\n\n    shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;\n\n    if (shm_zone->shm.exists) {\n        shm_zone->data = shpool->data;\n        return NGX_OK;\n    }\n\n    cache = ngx_slab_alloc(shpool, sizeof(ngx_ssl_ocsp_cache_t));\n    if (cache == NULL) {\n        return NGX_ERROR;\n    }\n\n    shpool->data = cache;\n    shm_zone->data = cache;\n\n    ngx_rbtree_init(&cache->rbtree, &cache->sentinel,\n                    ngx_str_rbtree_insert_value);\n\n    ngx_queue_init(&cache->expire_queue);\n\n    len = sizeof(\" in OCSP cache \\\"\\\"\") + shm_zone->shm.name.len;\n\n    shpool->log_ctx = ngx_slab_alloc(shpool, len);\n    if (shpool->log_ctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_sprintf(shpool->log_ctx, \" in OCSP cache \\\"%V\\\"%Z\",\n                &shm_zone->shm.name);\n\n    shpool->log_nomem = 0;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_ssl_ocsp_cache_lookup(ngx_ssl_ocsp_ctx_t *ctx)\n{\n    uint32_t                    hash;\n    ngx_shm_zone_t             *shm_zone;\n    ngx_slab_pool_t            *shpool;\n    ngx_ssl_ocsp_cache_t       *cache;\n    ngx_ssl_ocsp_cache_node_t  *node;\n\n    shm_zone = ctx->shm_zone;\n\n    if (shm_zone == NULL) {\n        return NGX_DECLINED;\n    }\n\n    if (ngx_ssl_ocsp_create_key(ctx) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0, \"ssl ocsp cache lookup\");\n\n    cache = shm_zone->data;\n    shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;\n    hash = ngx_hash_key(ctx->key.data, ctx->key.len);\n\n    ngx_shmtx_lock(&shpool->mutex);\n\n    node = (ngx_ssl_ocsp_cache_node_t *)\n               ngx_str_rbtree_lookup(&cache->rbtree, &ctx->key, hash);\n\n    if (node) {\n        if (node->valid > ngx_time()) {\n            ctx->status = node->status;\n            ngx_shmtx_unlock(&shpool->mutex);\n\n            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ctx->log, 0,\n                           \"ssl ocsp cache hit, %s\",\n                           OCSP_cert_status_str(ctx->status));\n\n            return NGX_OK;\n        }\n\n        ngx_queue_remove(&node->queue);\n        ngx_rbtree_delete(&cache->rbtree, &node->node.node);\n        ngx_slab_free_locked(shpool, node);\n\n        ngx_shmtx_unlock(&shpool->mutex);\n\n        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0,\n                       \"ssl ocsp cache expired\");\n\n        return NGX_DECLINED;\n    }\n\n    ngx_shmtx_unlock(&shpool->mutex);\n\n    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ctx->log, 0, \"ssl ocsp cache miss\");\n\n    return NGX_DECLINED;\n}\n\n\nstatic ngx_int_t\nngx_ssl_ocsp_cache_store(ngx_ssl_ocsp_ctx_t *ctx)\n{\n    time_t                      now, valid;\n    uint32_t                    hash;\n    ngx_queue_t                *q;\n    ngx_shm_zone_t             *shm_zone;\n    ngx_slab_pool_t            *shpool;\n    ngx_ssl_ocsp_cache_t       *cache;\n    ngx_ssl_ocsp_cache_node_t  *node;\n\n    shm_zone = ctx->shm_zone;\n\n    if (shm_zone == NULL) {\n        return NGX_OK;\n    }\n\n    valid = ctx->valid;\n\n    now = ngx_time();\n\n    if (valid < now) {\n        return NGX_OK;\n    }\n\n    if (valid == NGX_MAX_TIME_T_VALUE) {\n        valid = now + 3600;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ctx->log, 0,\n                   \"ssl ocsp cache store, valid:%T\", valid - now);\n\n    cache = shm_zone->data;\n    shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;\n    hash = ngx_hash_key(ctx->key.data, ctx->key.len);\n\n    ngx_shmtx_lock(&shpool->mutex);\n\n    node = ngx_slab_calloc_locked(shpool,\n                             sizeof(ngx_ssl_ocsp_cache_node_t) + ctx->key.len);\n    if (node == NULL) {\n\n        if (!ngx_queue_empty(&cache->expire_queue)) {\n            q = ngx_queue_last(&cache->expire_queue);\n            node = ngx_queue_data(q, ngx_ssl_ocsp_cache_node_t, queue);\n\n            ngx_rbtree_delete(&cache->rbtree, &node->node.node);\n            ngx_queue_remove(q);\n            ngx_slab_free_locked(shpool, node);\n\n            node = ngx_slab_alloc_locked(shpool,\n                             sizeof(ngx_ssl_ocsp_cache_node_t) + ctx->key.len);\n        }\n\n        if (node == NULL) {\n            ngx_shmtx_unlock(&shpool->mutex);\n            ngx_log_error(NGX_LOG_ALERT, ctx->log, 0,\n                          \"could not allocate new entry%s\", shpool->log_ctx);\n            return NGX_ERROR;\n        }\n    }\n\n    node->node.str.len = ctx->key.len;\n    node->node.str.data = (u_char *) node + sizeof(ngx_ssl_ocsp_cache_node_t);\n    ngx_memcpy(node->node.str.data, ctx->key.data, ctx->key.len);\n    node->node.node.key = hash;\n    node->status = ctx->status;\n    node->valid = valid;\n\n    ngx_rbtree_insert(&cache->rbtree, &node->node.node);\n    ngx_queue_insert_head(&cache->expire_queue, &node->queue);\n\n    ngx_shmtx_unlock(&shpool->mutex);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_ssl_ocsp_create_key(ngx_ssl_ocsp_ctx_t *ctx)\n{\n    u_char        *p;\n    X509_NAME     *name;\n    ASN1_INTEGER  *serial;\n\n    p = ngx_pnalloc(ctx->pool, 60);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    ctx->key.data = p;\n    ctx->key.len = 60;\n\n    name = X509_get_subject_name(ctx->issuer);\n    if (X509_NAME_digest(name, EVP_sha1(), p, NULL) == 0) {\n        return NGX_ERROR;\n    }\n\n    p += 20;\n\n    if (X509_pubkey_digest(ctx->issuer, EVP_sha1(), p, NULL) == 0) {\n        return NGX_ERROR;\n    }\n\n    p += 20;\n\n    serial = X509_get_serialNumber(ctx->cert);\n    if (serial->length > 20) {\n        return NGX_ERROR;\n    }\n\n    p = ngx_cpymem(p, serial->data, serial->length);\n    ngx_memzero(p, 20 - serial->length);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, ctx->log, 0,\n                   \"ssl ocsp key %xV\", &ctx->key);\n\n    return NGX_OK;\n}\n\n\nstatic u_char *\nngx_ssl_ocsp_log_error(ngx_log_t *log, u_char *buf, size_t len)\n{\n    u_char              *p;\n    ngx_ssl_ocsp_ctx_t  *ctx;\n\n    p = buf;\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    ctx = log->data;\n\n    if (ctx) {\n        p = ngx_snprintf(buf, len, \", responder: %V\", &ctx->host);\n        len -= p - buf;\n        buf = p;\n    }\n\n    if (ctx && ctx->peer.name) {\n        p = ngx_snprintf(buf, len, \", peer: %V\", ctx->peer.name);\n        len -= p - buf;\n        buf = p;\n    }\n\n    if (ctx && ctx->name) {\n        p = ngx_snprintf(buf, len, \", certificate: \\\"%s\\\"\", ctx->name);\n        len -= p - buf;\n        buf = p;\n    }\n\n    return p;\n}\n\n\n#else\n\n\nngx_int_t\nngx_ssl_stapling(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *file,\n    ngx_str_t *responder, ngx_uint_t verify)\n{\n    ngx_log_error(NGX_LOG_WARN, ssl->log, 0,\n                  \"\\\"ssl_stapling\\\" ignored, not supported\");\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_ssl_stapling_resolver(ngx_conf_t *cf, ngx_ssl_t *ssl,\n    ngx_resolver_t *resolver, ngx_msec_t resolver_timeout)\n{\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_ssl_ocsp(ngx_conf_t *cf, ngx_ssl_t *ssl, ngx_str_t *responder,\n    ngx_uint_t depth, ngx_shm_zone_t *shm_zone)\n{\n    ngx_log_error(NGX_LOG_EMERG, ssl->log, 0,\n                  \"\\\"ssl_ocsp\\\" is not supported on this platform\");\n\n    return NGX_ERROR;\n}\n\n\nngx_int_t\nngx_ssl_ocsp_resolver(ngx_conf_t *cf, ngx_ssl_t *ssl,\n    ngx_resolver_t *resolver, ngx_msec_t resolver_timeout)\n{\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_ssl_ocsp_validate(ngx_connection_t *c)\n{\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_ssl_ocsp_get_status(ngx_connection_t *c, const char **s)\n{\n    return NGX_OK;\n}\n\n\nvoid\nngx_ssl_ocsp_cleanup(ngx_connection_t *c)\n{\n}\n\n\nngx_int_t\nngx_ssl_ocsp_cache_init(ngx_shm_zone_t *shm_zone, void *data)\n{\n    return NGX_OK;\n}\n\n\n#endif\n"
  },
  {
    "path": "src/event/ngx_event_pipe.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n#include <ngx_event_pipe.h>\n\n\nstatic ngx_int_t ngx_event_pipe_read_upstream(ngx_event_pipe_t *p);\nstatic ngx_int_t ngx_event_pipe_write_to_downstream(ngx_event_pipe_t *p);\n\nstatic ngx_int_t ngx_event_pipe_write_chain_to_temp_file(ngx_event_pipe_t *p);\nstatic ngx_inline void ngx_event_pipe_remove_shadow_links(ngx_buf_t *buf);\nstatic ngx_int_t ngx_event_pipe_drain_chains(ngx_event_pipe_t *p);\n\n\nngx_int_t\nngx_event_pipe(ngx_event_pipe_t *p, ngx_int_t do_write)\n{\n    ngx_int_t     rc;\n    ngx_uint_t    flags;\n    ngx_event_t  *rev, *wev;\n\n    for ( ;; ) {\n        if (do_write) {\n            p->log->action = \"sending to client\";\n\n            rc = ngx_event_pipe_write_to_downstream(p);\n\n            if (rc == NGX_ABORT) {\n                return NGX_ABORT;\n            }\n\n            if (rc == NGX_BUSY) {\n                return NGX_OK;\n            }\n        }\n\n        p->read = 0;\n        p->upstream_blocked = 0;\n\n        p->log->action = \"reading upstream\";\n\n        if (ngx_event_pipe_read_upstream(p) == NGX_ABORT) {\n            return NGX_ABORT;\n        }\n\n        if (!p->read && !p->upstream_blocked) {\n            break;\n        }\n\n        do_write = 1;\n    }\n\n    if (p->upstream->fd != (ngx_socket_t) -1) {\n        rev = p->upstream->read;\n\n        flags = (rev->eof || rev->error) ? NGX_CLOSE_EVENT : 0;\n\n        if (ngx_handle_read_event(rev, flags) != NGX_OK) {\n            return NGX_ABORT;\n        }\n\n        if (!rev->delayed) {\n            if (rev->active && !rev->ready) {\n                ngx_add_timer(rev, p->read_timeout);\n\n            } else if (rev->timer_set) {\n                ngx_del_timer(rev);\n            }\n        }\n    }\n\n    if (p->downstream->fd != (ngx_socket_t) -1\n        && p->downstream->data == p->output_ctx)\n    {\n        wev = p->downstream->write;\n        if (ngx_handle_write_event(wev, p->send_lowat) != NGX_OK) {\n            return NGX_ABORT;\n        }\n\n        if (!wev->delayed) {\n            if (wev->active && !wev->ready) {\n                ngx_add_timer(wev, p->send_timeout);\n\n            } else if (wev->timer_set) {\n                ngx_del_timer(wev);\n            }\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_event_pipe_read_upstream(ngx_event_pipe_t *p)\n{\n    off_t         limit;\n    ssize_t       n, size;\n    ngx_int_t     rc;\n    ngx_buf_t    *b;\n    ngx_msec_t    delay;\n    ngx_chain_t  *chain, *cl, *ln;\n\n    if (p->upstream_eof || p->upstream_error || p->upstream_done) {\n        return NGX_OK;\n    }\n\n#if (NGX_THREADS)\n\n    if (p->aio) {\n        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, p->log, 0,\n                       \"pipe read upstream: aio\");\n        return NGX_AGAIN;\n    }\n\n    if (p->writing) {\n        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, p->log, 0,\n                       \"pipe read upstream: writing\");\n\n        rc = ngx_event_pipe_write_chain_to_temp_file(p);\n\n        if (rc != NGX_OK) {\n            return rc;\n        }\n    }\n\n#endif\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0,\n                   \"pipe read upstream: %d\", p->upstream->read->ready);\n\n    for ( ;; ) {\n\n        if (p->upstream_eof || p->upstream_error || p->upstream_done) {\n            break;\n        }\n\n        if (p->preread_bufs == NULL && !p->upstream->read->ready) {\n            break;\n        }\n\n        if (p->preread_bufs) {\n\n            /* use the pre-read bufs if they exist */\n\n            chain = p->preread_bufs;\n            p->preread_bufs = NULL;\n            n = p->preread_size;\n\n            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0,\n                           \"pipe preread: %z\", n);\n\n            if (n) {\n                p->read = 1;\n            }\n\n        } else {\n\n#if (NGX_HAVE_KQUEUE)\n\n            /*\n             * kqueue notifies about the end of file or a pending error.\n             * This test allows not to allocate a buf on these conditions\n             * and not to call c->recv_chain().\n             */\n\n            if (p->upstream->read->available == 0\n                && p->upstream->read->pending_eof\n#if (NGX_SSL)\n                && !p->upstream->ssl\n#endif\n                )\n            {\n                p->upstream->read->ready = 0;\n                p->upstream->read->eof = 1;\n                p->upstream_eof = 1;\n                p->read = 1;\n\n                if (p->upstream->read->kq_errno) {\n                    p->upstream->read->error = 1;\n                    p->upstream_error = 1;\n                    p->upstream_eof = 0;\n\n                    ngx_log_error(NGX_LOG_ERR, p->log,\n                                  p->upstream->read->kq_errno,\n                                  \"kevent() reported that upstream \"\n                                  \"closed connection\");\n                }\n\n                break;\n            }\n#endif\n\n            if (p->limit_rate) {\n                if (p->upstream->read->delayed) {\n                    break;\n                }\n\n                limit = (off_t) p->limit_rate * (ngx_time() - p->start_sec + 1)\n                        - p->read_length;\n\n                if (limit <= 0) {\n                    p->upstream->read->delayed = 1;\n                    delay = (ngx_msec_t) (- limit * 1000 / p->limit_rate + 1);\n                    ngx_add_timer(p->upstream->read, delay);\n                    break;\n                }\n\n            } else {\n                limit = 0;\n            }\n\n            if (p->free_raw_bufs) {\n\n                /* use the free bufs if they exist */\n\n                chain = p->free_raw_bufs;\n                if (p->single_buf) {\n                    p->free_raw_bufs = p->free_raw_bufs->next;\n                    chain->next = NULL;\n                } else {\n                    p->free_raw_bufs = NULL;\n                }\n\n            } else if (p->allocated < p->bufs.num) {\n\n                /* allocate a new buf if it's still allowed */\n\n                b = ngx_create_temp_buf(p->pool, p->bufs.size);\n                if (b == NULL) {\n                    return NGX_ABORT;\n                }\n\n                p->allocated++;\n\n                chain = ngx_alloc_chain_link(p->pool);\n                if (chain == NULL) {\n                    return NGX_ABORT;\n                }\n\n                chain->buf = b;\n                chain->next = NULL;\n\n            } else if (!p->cacheable\n                       && p->downstream->data == p->output_ctx\n                       && p->downstream->write->ready\n                       && !p->downstream->write->delayed)\n            {\n                /*\n                 * if the bufs are not needed to be saved in a cache and\n                 * a downstream is ready then write the bufs to a downstream\n                 */\n\n                p->upstream_blocked = 1;\n\n                ngx_log_debug0(NGX_LOG_DEBUG_EVENT, p->log, 0,\n                               \"pipe downstream ready\");\n\n                break;\n\n            } else if (p->cacheable\n                       || p->temp_file->offset < p->max_temp_file_size)\n            {\n\n                /*\n                 * if it is allowed, then save some bufs from p->in\n                 * to a temporary file, and add them to a p->out chain\n                 */\n\n                rc = ngx_event_pipe_write_chain_to_temp_file(p);\n\n                ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0,\n                               \"pipe temp offset: %O\", p->temp_file->offset);\n\n                if (rc == NGX_BUSY) {\n                    break;\n                }\n\n                if (rc != NGX_OK) {\n                    return rc;\n                }\n\n                chain = p->free_raw_bufs;\n                if (p->single_buf) {\n                    p->free_raw_bufs = p->free_raw_bufs->next;\n                    chain->next = NULL;\n                } else {\n                    p->free_raw_bufs = NULL;\n                }\n\n            } else {\n\n                /* there are no bufs to read in */\n\n                ngx_log_debug0(NGX_LOG_DEBUG_EVENT, p->log, 0,\n                               \"no pipe bufs to read in\");\n\n                break;\n            }\n\n            n = p->upstream->recv_chain(p->upstream, chain, limit);\n\n            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0,\n                           \"pipe recv chain: %z\", n);\n\n            if (p->free_raw_bufs) {\n                chain->next = p->free_raw_bufs;\n            }\n            p->free_raw_bufs = chain;\n\n            if (n == NGX_ERROR) {\n                p->upstream_error = 1;\n                break;\n            }\n\n            if (n == NGX_AGAIN) {\n                if (p->single_buf) {\n                    ngx_event_pipe_remove_shadow_links(chain->buf);\n                }\n\n                break;\n            }\n\n            p->read = 1;\n\n            if (n == 0) {\n                p->upstream_eof = 1;\n                break;\n            }\n        }\n\n        delay = p->limit_rate ? (ngx_msec_t) n * 1000 / p->limit_rate : 0;\n\n        p->read_length += n;\n        cl = chain;\n        p->free_raw_bufs = NULL;\n\n        while (cl && n > 0) {\n\n            ngx_event_pipe_remove_shadow_links(cl->buf);\n\n            size = cl->buf->end - cl->buf->last;\n\n            if (n >= size) {\n                cl->buf->last = cl->buf->end;\n\n                /* STUB */ cl->buf->num = p->num++;\n\n                if (p->input_filter(p, cl->buf) == NGX_ERROR) {\n                    return NGX_ABORT;\n                }\n\n                n -= size;\n                ln = cl;\n                cl = cl->next;\n                ngx_free_chain(p->pool, ln);\n\n            } else {\n                cl->buf->last += n;\n                n = 0;\n            }\n        }\n\n        if (cl) {\n            for (ln = cl; ln->next; ln = ln->next) { /* void */ }\n\n            ln->next = p->free_raw_bufs;\n            p->free_raw_bufs = cl;\n        }\n\n        if (delay > 0) {\n            p->upstream->read->delayed = 1;\n            ngx_add_timer(p->upstream->read, delay);\n            break;\n        }\n    }\n\n#if (NGX_DEBUG)\n\n    for (cl = p->busy; cl; cl = cl->next) {\n        ngx_log_debug8(NGX_LOG_DEBUG_EVENT, p->log, 0,\n                       \"pipe buf busy s:%d t:%d f:%d \"\n                       \"%p, pos %p, size: %z \"\n                       \"file: %O, size: %O\",\n                       (cl->buf->shadow ? 1 : 0),\n                       cl->buf->temporary, cl->buf->in_file,\n                       cl->buf->start, cl->buf->pos,\n                       cl->buf->last - cl->buf->pos,\n                       cl->buf->file_pos,\n                       cl->buf->file_last - cl->buf->file_pos);\n    }\n\n    for (cl = p->out; cl; cl = cl->next) {\n        ngx_log_debug8(NGX_LOG_DEBUG_EVENT, p->log, 0,\n                       \"pipe buf out  s:%d t:%d f:%d \"\n                       \"%p, pos %p, size: %z \"\n                       \"file: %O, size: %O\",\n                       (cl->buf->shadow ? 1 : 0),\n                       cl->buf->temporary, cl->buf->in_file,\n                       cl->buf->start, cl->buf->pos,\n                       cl->buf->last - cl->buf->pos,\n                       cl->buf->file_pos,\n                       cl->buf->file_last - cl->buf->file_pos);\n    }\n\n    for (cl = p->in; cl; cl = cl->next) {\n        ngx_log_debug8(NGX_LOG_DEBUG_EVENT, p->log, 0,\n                       \"pipe buf in   s:%d t:%d f:%d \"\n                       \"%p, pos %p, size: %z \"\n                       \"file: %O, size: %O\",\n                       (cl->buf->shadow ? 1 : 0),\n                       cl->buf->temporary, cl->buf->in_file,\n                       cl->buf->start, cl->buf->pos,\n                       cl->buf->last - cl->buf->pos,\n                       cl->buf->file_pos,\n                       cl->buf->file_last - cl->buf->file_pos);\n    }\n\n    for (cl = p->free_raw_bufs; cl; cl = cl->next) {\n        ngx_log_debug8(NGX_LOG_DEBUG_EVENT, p->log, 0,\n                       \"pipe buf free s:%d t:%d f:%d \"\n                       \"%p, pos %p, size: %z \"\n                       \"file: %O, size: %O\",\n                       (cl->buf->shadow ? 1 : 0),\n                       cl->buf->temporary, cl->buf->in_file,\n                       cl->buf->start, cl->buf->pos,\n                       cl->buf->last - cl->buf->pos,\n                       cl->buf->file_pos,\n                       cl->buf->file_last - cl->buf->file_pos);\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0,\n                   \"pipe length: %O\", p->length);\n\n#endif\n\n    if (p->free_raw_bufs && p->length != -1) {\n        cl = p->free_raw_bufs;\n\n        if (cl->buf->last - cl->buf->pos >= p->length) {\n\n            p->free_raw_bufs = cl->next;\n\n            /* STUB */ cl->buf->num = p->num++;\n\n            if (p->input_filter(p, cl->buf) == NGX_ERROR) {\n                return NGX_ABORT;\n            }\n\n            ngx_free_chain(p->pool, cl);\n        }\n    }\n\n    if (p->length == 0) {\n        p->upstream_done = 1;\n        p->read = 1;\n    }\n\n    if ((p->upstream_eof || p->upstream_error) && p->free_raw_bufs) {\n\n        /* STUB */ p->free_raw_bufs->buf->num = p->num++;\n\n        if (p->input_filter(p, p->free_raw_bufs->buf) == NGX_ERROR) {\n            return NGX_ABORT;\n        }\n\n        p->free_raw_bufs = p->free_raw_bufs->next;\n\n        if (p->free_bufs && p->buf_to_file == NULL) {\n            for (cl = p->free_raw_bufs; cl; cl = cl->next) {\n                if (cl->buf->shadow == NULL) {\n                    ngx_pfree(p->pool, cl->buf->start);\n                }\n            }\n        }\n    }\n\n    if (p->cacheable && (p->in || p->buf_to_file)) {\n\n        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, p->log, 0,\n                       \"pipe write chain\");\n\n        rc = ngx_event_pipe_write_chain_to_temp_file(p);\n\n        if (rc != NGX_OK) {\n            return rc;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_event_pipe_write_to_downstream(ngx_event_pipe_t *p)\n{\n    u_char            *prev;\n    size_t             bsize;\n    ngx_int_t          rc;\n    ngx_uint_t         flush, flushed, prev_last_shadow;\n    ngx_chain_t       *out, **ll, *cl;\n    ngx_connection_t  *downstream;\n\n    downstream = p->downstream;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0,\n                   \"pipe write downstream: %d\", downstream->write->ready);\n\n#if (NGX_THREADS)\n\n    if (p->writing) {\n        rc = ngx_event_pipe_write_chain_to_temp_file(p);\n\n        if (rc == NGX_ABORT) {\n            return NGX_ABORT;\n        }\n    }\n\n#endif\n\n    flushed = 0;\n\n    for ( ;; ) {\n        if (p->downstream_error) {\n            return ngx_event_pipe_drain_chains(p);\n        }\n\n        if (p->upstream_eof || p->upstream_error || p->upstream_done) {\n\n            /* pass the p->out and p->in chains to the output filter */\n\n            for (cl = p->busy; cl; cl = cl->next) {\n                cl->buf->recycled = 0;\n            }\n\n            if (p->out) {\n                ngx_log_debug0(NGX_LOG_DEBUG_EVENT, p->log, 0,\n                               \"pipe write downstream flush out\");\n\n                for (cl = p->out; cl; cl = cl->next) {\n                    cl->buf->recycled = 0;\n                }\n\n                rc = p->output_filter(p->output_ctx, p->out);\n\n                if (rc == NGX_ERROR) {\n                    p->downstream_error = 1;\n                    return ngx_event_pipe_drain_chains(p);\n                }\n\n                p->out = NULL;\n            }\n\n            if (p->writing) {\n                break;\n            }\n\n            if (p->in) {\n                ngx_log_debug0(NGX_LOG_DEBUG_EVENT, p->log, 0,\n                               \"pipe write downstream flush in\");\n\n                for (cl = p->in; cl; cl = cl->next) {\n                    cl->buf->recycled = 0;\n                }\n\n                rc = p->output_filter(p->output_ctx, p->in);\n\n                if (rc == NGX_ERROR) {\n                    p->downstream_error = 1;\n                    return ngx_event_pipe_drain_chains(p);\n                }\n\n                p->in = NULL;\n            }\n\n            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, p->log, 0,\n                           \"pipe write downstream done\");\n\n            /* TODO: free unused bufs */\n\n            p->downstream_done = 1;\n            break;\n        }\n\n        if (downstream->data != p->output_ctx\n            || !downstream->write->ready\n            || downstream->write->delayed)\n        {\n            break;\n        }\n\n        /* bsize is the size of the busy recycled bufs */\n\n        prev = NULL;\n        bsize = 0;\n\n        for (cl = p->busy; cl; cl = cl->next) {\n\n            if (cl->buf->recycled) {\n                if (prev == cl->buf->start) {\n                    continue;\n                }\n\n                bsize += cl->buf->end - cl->buf->start;\n                prev = cl->buf->start;\n            }\n        }\n\n        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0,\n                       \"pipe write busy: %uz\", bsize);\n\n        out = NULL;\n\n        if (bsize >= (size_t) p->busy_size) {\n            flush = 1;\n            goto flush;\n        }\n\n        flush = 0;\n        ll = NULL;\n        prev_last_shadow = 1;\n\n        for ( ;; ) {\n            if (p->out) {\n                cl = p->out;\n\n                if (cl->buf->recycled) {\n                    ngx_log_error(NGX_LOG_ALERT, p->log, 0,\n                                  \"recycled buffer in pipe out chain\");\n                }\n\n                p->out = p->out->next;\n\n            } else if (!p->cacheable && !p->writing && p->in) {\n                cl = p->in;\n\n                ngx_log_debug3(NGX_LOG_DEBUG_EVENT, p->log, 0,\n                               \"pipe write buf ls:%d %p %z\",\n                               cl->buf->last_shadow,\n                               cl->buf->pos,\n                               cl->buf->last - cl->buf->pos);\n\n                if (cl->buf->recycled && prev_last_shadow) {\n                    if (bsize + cl->buf->end - cl->buf->start > p->busy_size) {\n                        flush = 1;\n                        break;\n                    }\n\n                    bsize += cl->buf->end - cl->buf->start;\n                }\n\n                prev_last_shadow = cl->buf->last_shadow;\n\n                p->in = p->in->next;\n\n            } else {\n                break;\n            }\n\n            cl->next = NULL;\n\n            if (out) {\n                *ll = cl;\n            } else {\n                out = cl;\n            }\n            ll = &cl->next;\n        }\n\n    flush:\n\n        ngx_log_debug2(NGX_LOG_DEBUG_EVENT, p->log, 0,\n                       \"pipe write: out:%p, f:%ui\", out, flush);\n\n        if (out == NULL) {\n\n            if (!flush) {\n                break;\n            }\n\n            /* a workaround for AIO */\n            if (flushed++ > 10) {\n                return NGX_BUSY;\n            }\n        }\n\n        rc = p->output_filter(p->output_ctx, out);\n\n        ngx_chain_update_chains(p->pool, &p->free, &p->busy, &out, p->tag);\n\n        if (rc == NGX_ERROR) {\n            p->downstream_error = 1;\n            return ngx_event_pipe_drain_chains(p);\n        }\n\n        for (cl = p->free; cl; cl = cl->next) {\n\n            if (cl->buf->temp_file) {\n                if (p->cacheable || !p->cyclic_temp_file) {\n                    continue;\n                }\n\n                /* reset p->temp_offset if all bufs had been sent */\n\n                if (cl->buf->file_last == p->temp_file->offset) {\n                    p->temp_file->offset = 0;\n                }\n            }\n\n            /* TODO: free buf if p->free_bufs && upstream done */\n\n            /* add the free shadow raw buf to p->free_raw_bufs */\n\n            if (cl->buf->last_shadow) {\n                if (ngx_event_pipe_add_free_buf(p, cl->buf->shadow) != NGX_OK) {\n                    return NGX_ABORT;\n                }\n\n                cl->buf->last_shadow = 0;\n            }\n\n            cl->buf->shadow = NULL;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_event_pipe_write_chain_to_temp_file(ngx_event_pipe_t *p)\n{\n    ssize_t       size, bsize, n;\n    ngx_buf_t    *b;\n    ngx_uint_t    prev_last_shadow;\n    ngx_chain_t  *cl, *tl, *next, *out, **ll, **last_out, **last_free;\n\n#if (NGX_THREADS)\n\n    if (p->writing) {\n\n        if (p->aio) {\n            return NGX_AGAIN;\n        }\n\n        out = p->writing;\n        p->writing = NULL;\n\n        n = ngx_write_chain_to_temp_file(p->temp_file, NULL);\n\n        if (n == NGX_ERROR) {\n            return NGX_ABORT;\n        }\n\n        goto done;\n    }\n\n#endif\n\n    if (p->buf_to_file) {\n        out = ngx_alloc_chain_link(p->pool);\n        if (out == NULL) {\n            return NGX_ABORT;\n        }\n\n        out->buf = p->buf_to_file;\n        out->next = p->in;\n\n    } else {\n        out = p->in;\n    }\n\n    if (!p->cacheable) {\n\n        size = 0;\n        cl = out;\n        ll = NULL;\n        prev_last_shadow = 1;\n\n        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0,\n                       \"pipe offset: %O\", p->temp_file->offset);\n\n        do {\n            bsize = cl->buf->last - cl->buf->pos;\n\n            ngx_log_debug4(NGX_LOG_DEBUG_EVENT, p->log, 0,\n                           \"pipe buf ls:%d %p, pos %p, size: %z\",\n                           cl->buf->last_shadow, cl->buf->start,\n                           cl->buf->pos, bsize);\n\n            if (prev_last_shadow\n                && ((size + bsize > p->temp_file_write_size)\n                    || (p->temp_file->offset + size + bsize\n                        > p->max_temp_file_size)))\n            {\n                break;\n            }\n\n            prev_last_shadow = cl->buf->last_shadow;\n\n            size += bsize;\n            ll = &cl->next;\n            cl = cl->next;\n\n        } while (cl);\n\n        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0, \"size: %z\", size);\n\n        if (ll == NULL) {\n            return NGX_BUSY;\n        }\n\n        if (cl) {\n            p->in = cl;\n            *ll = NULL;\n\n        } else {\n            p->in = NULL;\n            p->last_in = &p->in;\n        }\n\n    } else {\n        p->in = NULL;\n        p->last_in = &p->in;\n    }\n\n#if (NGX_THREADS)\n    if (p->thread_handler) {\n        p->temp_file->thread_write = 1;\n        p->temp_file->file.thread_task = p->thread_task;\n        p->temp_file->file.thread_handler = p->thread_handler;\n        p->temp_file->file.thread_ctx = p->thread_ctx;\n    }\n#endif\n\n    n = ngx_write_chain_to_temp_file(p->temp_file, out);\n\n    if (n == NGX_ERROR) {\n        return NGX_ABORT;\n    }\n\n#if (NGX_THREADS)\n\n    if (n == NGX_AGAIN) {\n        p->writing = out;\n        p->thread_task = p->temp_file->file.thread_task;\n        return NGX_AGAIN;\n    }\n\ndone:\n\n#endif\n\n    if (p->buf_to_file) {\n        p->temp_file->offset = p->buf_to_file->last - p->buf_to_file->pos;\n        n -= p->buf_to_file->last - p->buf_to_file->pos;\n        p->buf_to_file = NULL;\n        out = out->next;\n    }\n\n    if (n > 0) {\n        /* update previous buffer or add new buffer */\n\n        if (p->out) {\n            for (cl = p->out; cl->next; cl = cl->next) { /* void */ }\n\n            b = cl->buf;\n\n            if (b->file_last == p->temp_file->offset) {\n                p->temp_file->offset += n;\n                b->file_last = p->temp_file->offset;\n                goto free;\n            }\n\n            last_out = &cl->next;\n\n        } else {\n            last_out = &p->out;\n        }\n\n        cl = ngx_chain_get_free_buf(p->pool, &p->free);\n        if (cl == NULL) {\n            return NGX_ABORT;\n        }\n\n        b = cl->buf;\n\n        ngx_memzero(b, sizeof(ngx_buf_t));\n\n        b->tag = p->tag;\n\n        b->file = &p->temp_file->file;\n        b->file_pos = p->temp_file->offset;\n        p->temp_file->offset += n;\n        b->file_last = p->temp_file->offset;\n\n        b->in_file = 1;\n        b->temp_file = 1;\n\n        *last_out = cl;\n    }\n\nfree:\n\n    for (last_free = &p->free_raw_bufs;\n         *last_free != NULL;\n         last_free = &(*last_free)->next)\n    {\n        /* void */\n    }\n\n    for (cl = out; cl; cl = next) {\n        next = cl->next;\n\n        cl->next = p->free;\n        p->free = cl;\n\n        b = cl->buf;\n\n        if (b->last_shadow) {\n\n            tl = ngx_alloc_chain_link(p->pool);\n            if (tl == NULL) {\n                return NGX_ABORT;\n            }\n\n            tl->buf = b->shadow;\n            tl->next = NULL;\n\n            *last_free = tl;\n            last_free = &tl->next;\n\n            b->shadow->pos = b->shadow->start;\n            b->shadow->last = b->shadow->start;\n\n            ngx_event_pipe_remove_shadow_links(b->shadow);\n        }\n    }\n\n    return NGX_OK;\n}\n\n\n/* the copy input filter */\n\nngx_int_t\nngx_event_pipe_copy_input_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)\n{\n    ngx_buf_t    *b;\n    ngx_chain_t  *cl;\n\n    if (buf->pos == buf->last) {\n        return NGX_OK;\n    }\n\n    if (p->upstream_done) {\n        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, p->log, 0,\n                       \"input data after close\");\n        return NGX_OK;\n    }\n\n    if (p->length == 0) {\n        p->upstream_done = 1;\n\n        ngx_log_error(NGX_LOG_WARN, p->log, 0,\n                      \"upstream sent more data than specified in \"\n                      \"\\\"Content-Length\\\" header\");\n\n        return NGX_OK;\n    }\n\n    cl = ngx_chain_get_free_buf(p->pool, &p->free);\n    if (cl == NULL) {\n        return NGX_ERROR;\n    }\n\n    b = cl->buf;\n\n    ngx_memcpy(b, buf, sizeof(ngx_buf_t));\n    b->shadow = buf;\n    b->tag = p->tag;\n    b->last_shadow = 1;\n    b->recycled = 1;\n    buf->shadow = b;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0, \"input buf #%d\", b->num);\n\n    if (p->in) {\n        *p->last_in = cl;\n    } else {\n        p->in = cl;\n    }\n    p->last_in = &cl->next;\n\n    if (p->length == -1) {\n        return NGX_OK;\n    }\n\n    if (b->last - b->pos > p->length) {\n\n        ngx_log_error(NGX_LOG_WARN, p->log, 0,\n                      \"upstream sent more data than specified in \"\n                      \"\\\"Content-Length\\\" header\");\n\n        b->last = b->pos + p->length;\n        p->upstream_done = 1;\n\n        return NGX_OK;\n    }\n\n    p->length -= b->last - b->pos;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_inline void\nngx_event_pipe_remove_shadow_links(ngx_buf_t *buf)\n{\n    ngx_buf_t  *b, *next;\n\n    b = buf->shadow;\n\n    if (b == NULL) {\n        return;\n    }\n\n    while (!b->last_shadow) {\n        next = b->shadow;\n\n        b->temporary = 0;\n        b->recycled = 0;\n\n        b->shadow = NULL;\n        b = next;\n    }\n\n    b->temporary = 0;\n    b->recycled = 0;\n    b->last_shadow = 0;\n\n    b->shadow = NULL;\n\n    buf->shadow = NULL;\n}\n\n\nngx_int_t\nngx_event_pipe_add_free_buf(ngx_event_pipe_t *p, ngx_buf_t *b)\n{\n    ngx_chain_t  *cl;\n\n    cl = ngx_alloc_chain_link(p->pool);\n    if (cl == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (p->buf_to_file && b->start == p->buf_to_file->start) {\n        b->pos = p->buf_to_file->last;\n        b->last = p->buf_to_file->last;\n\n    } else {\n        b->pos = b->start;\n        b->last = b->start;\n    }\n\n    b->shadow = NULL;\n\n    cl->buf = b;\n\n    if (p->free_raw_bufs == NULL) {\n        p->free_raw_bufs = cl;\n        cl->next = NULL;\n\n        return NGX_OK;\n    }\n\n    if (p->free_raw_bufs->buf->pos == p->free_raw_bufs->buf->last) {\n\n        /* add the free buf to the list start */\n\n        cl->next = p->free_raw_bufs;\n        p->free_raw_bufs = cl;\n\n        return NGX_OK;\n    }\n\n    /* the first free buf is partially filled, thus add the free buf after it */\n\n    cl->next = p->free_raw_bufs->next;\n    p->free_raw_bufs->next = cl;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_event_pipe_drain_chains(ngx_event_pipe_t *p)\n{\n    ngx_chain_t  *cl, *tl;\n\n    for ( ;; ) {\n        if (p->busy) {\n            cl = p->busy;\n            p->busy = NULL;\n\n        } else if (p->out) {\n            cl = p->out;\n            p->out = NULL;\n\n        } else if (p->in) {\n            cl = p->in;\n            p->in = NULL;\n\n        } else {\n            return NGX_OK;\n        }\n\n        while (cl) {\n            if (cl->buf->last_shadow) {\n                if (ngx_event_pipe_add_free_buf(p, cl->buf->shadow) != NGX_OK) {\n                    return NGX_ABORT;\n                }\n\n                cl->buf->last_shadow = 0;\n            }\n\n            cl->buf->shadow = NULL;\n            tl = cl->next;\n            cl->next = p->free;\n            p->free = cl;\n            cl = tl;\n        }\n    }\n}\n"
  },
  {
    "path": "src/event/ngx_event_pipe.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_EVENT_PIPE_H_INCLUDED_\n#define _NGX_EVENT_PIPE_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n\n\ntypedef struct ngx_event_pipe_s  ngx_event_pipe_t;\n\ntypedef ngx_int_t (*ngx_event_pipe_input_filter_pt)(ngx_event_pipe_t *p,\n                                                    ngx_buf_t *buf);\ntypedef ngx_int_t (*ngx_event_pipe_output_filter_pt)(void *data,\n                                                     ngx_chain_t *chain);\n\n\nstruct ngx_event_pipe_s {\n    ngx_connection_t  *upstream;\n    ngx_connection_t  *downstream;\n\n    ngx_chain_t       *free_raw_bufs;\n    ngx_chain_t       *in;\n    ngx_chain_t      **last_in;\n\n    ngx_chain_t       *writing;\n\n    ngx_chain_t       *out;\n    ngx_chain_t       *free;\n    ngx_chain_t       *busy;\n\n    /*\n     * the input filter i.e. that moves HTTP/1.1 chunks\n     * from the raw bufs to an incoming chain\n     */\n\n    ngx_event_pipe_input_filter_pt    input_filter;\n    void                             *input_ctx;\n\n    ngx_event_pipe_output_filter_pt   output_filter;\n    void                             *output_ctx;\n\n#if (NGX_THREADS || NGX_COMPAT)\n    ngx_int_t                       (*thread_handler)(ngx_thread_task_t *task,\n                                                      ngx_file_t *file);\n    void                             *thread_ctx;\n    ngx_thread_task_t                *thread_task;\n#endif\n\n    unsigned           read:1;\n    unsigned           cacheable:1;\n    unsigned           single_buf:1;\n    unsigned           free_bufs:1;\n    unsigned           upstream_done:1;\n    unsigned           upstream_error:1;\n    unsigned           upstream_eof:1;\n    unsigned           upstream_blocked:1;\n    unsigned           downstream_done:1;\n    unsigned           downstream_error:1;\n    unsigned           cyclic_temp_file:1;\n    unsigned           aio:1;\n\n    ngx_int_t          allocated;\n    ngx_bufs_t         bufs;\n    ngx_buf_tag_t      tag;\n\n    ssize_t            busy_size;\n\n    off_t              read_length;\n    off_t              length;\n\n    off_t              max_temp_file_size;\n    ssize_t            temp_file_write_size;\n\n    ngx_msec_t         read_timeout;\n    ngx_msec_t         send_timeout;\n    ssize_t            send_lowat;\n\n    ngx_pool_t        *pool;\n    ngx_log_t         *log;\n\n    ngx_chain_t       *preread_bufs;\n    size_t             preread_size;\n    ngx_buf_t         *buf_to_file;\n\n    size_t             limit_rate;\n    time_t             start_sec;\n\n    ngx_temp_file_t   *temp_file;\n\n    /* STUB */ int     num;\n};\n\n\nngx_int_t ngx_event_pipe(ngx_event_pipe_t *p, ngx_int_t do_write);\nngx_int_t ngx_event_pipe_copy_input_filter(ngx_event_pipe_t *p, ngx_buf_t *buf);\nngx_int_t ngx_event_pipe_add_free_buf(ngx_event_pipe_t *p, ngx_buf_t *b);\n\n\n#endif /* _NGX_EVENT_PIPE_H_INCLUDED_ */\n"
  },
  {
    "path": "src/event/ngx_event_posted.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n\n\nngx_queue_t  ngx_posted_accept_events;\nngx_queue_t  ngx_posted_next_events;\nngx_queue_t  ngx_posted_events;\n\n#if (T_NGX_HAVE_XUDP)\nngx_queue_t  ngx_posted_commit;\n#endif\n\nvoid\nngx_event_process_posted(ngx_cycle_t *cycle, ngx_queue_t *posted)\n{\n    ngx_queue_t  *q;\n    ngx_event_t  *ev;\n\n    while (!ngx_queue_empty(posted)) {\n\n        q = ngx_queue_head(posted);\n        ev = ngx_queue_data(q, ngx_event_t, queue);\n\n        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                      \"posted event %p\", ev);\n\n        ngx_delete_posted_event(ev);\n\n        ev->handler(ev);\n    }\n}\n\n\nvoid\nngx_event_move_posted_next(ngx_cycle_t *cycle)\n{\n    ngx_queue_t  *q;\n    ngx_event_t  *ev;\n\n    for (q = ngx_queue_head(&ngx_posted_next_events);\n         q != ngx_queue_sentinel(&ngx_posted_next_events);\n         q = ngx_queue_next(q))\n    {\n        ev = ngx_queue_data(q, ngx_event_t, queue);\n\n        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                      \"posted next event %p\", ev);\n\n        ev->ready = 1;\n        ev->available = -1;\n    }\n\n    ngx_queue_add(&ngx_posted_events, &ngx_posted_next_events);\n    ngx_queue_init(&ngx_posted_next_events);\n}\n"
  },
  {
    "path": "src/event/ngx_event_posted.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_EVENT_POSTED_H_INCLUDED_\n#define _NGX_EVENT_POSTED_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n\n\n#define ngx_post_event(ev, q)                                                 \\\n                                                                              \\\n    if (!(ev)->posted) {                                                      \\\n        (ev)->posted = 1;                                                     \\\n        ngx_queue_insert_tail(q, &(ev)->queue);                               \\\n                                                                              \\\n        ngx_log_debug1(NGX_LOG_DEBUG_CORE, (ev)->log, 0, \"post event %p\", ev);\\\n                                                                              \\\n    } else  {                                                                 \\\n        ngx_log_debug1(NGX_LOG_DEBUG_CORE, (ev)->log, 0,                      \\\n                       \"update posted event %p\", ev);                         \\\n    }\n\n\n#define ngx_delete_posted_event(ev)                                           \\\n                                                                              \\\n    (ev)->posted = 0;                                                         \\\n    ngx_queue_remove(&(ev)->queue);                                           \\\n                                                                              \\\n    ngx_log_debug1(NGX_LOG_DEBUG_CORE, (ev)->log, 0,                          \\\n                   \"delete posted event %p\", ev);\n\n\n\nvoid ngx_event_process_posted(ngx_cycle_t *cycle, ngx_queue_t *posted);\nvoid ngx_event_move_posted_next(ngx_cycle_t *cycle);\n\n\nextern ngx_queue_t  ngx_posted_accept_events;\nextern ngx_queue_t  ngx_posted_next_events;\nextern ngx_queue_t  ngx_posted_events;\n#if (T_NGX_HAVE_XUDP)\nextern ngx_queue_t  ngx_posted_commit;\n#endif\n\n#endif /* _NGX_EVENT_POSTED_H_INCLUDED_ */\n"
  },
  {
    "path": "src/event/ngx_event_timer.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n\n\nngx_rbtree_t              ngx_event_timer_rbtree;\nstatic ngx_rbtree_node_t  ngx_event_timer_sentinel;\n\n/*\n * the event timer rbtree may contain the duplicate keys, however,\n * it should not be a problem, because we use the rbtree to find\n * a minimum timer value only\n */\n\nngx_int_t\nngx_event_timer_init(ngx_log_t *log)\n{\n    ngx_rbtree_init(&ngx_event_timer_rbtree, &ngx_event_timer_sentinel,\n                    ngx_rbtree_insert_timer_value);\n\n    return NGX_OK;\n}\n\n\nngx_msec_t\nngx_event_find_timer(void)\n{\n    ngx_msec_int_t      timer;\n    ngx_rbtree_node_t  *node, *root, *sentinel;\n\n    if (ngx_event_timer_rbtree.root == &ngx_event_timer_sentinel) {\n        return NGX_TIMER_INFINITE;\n    }\n\n    root = ngx_event_timer_rbtree.root;\n    sentinel = ngx_event_timer_rbtree.sentinel;\n\n    node = ngx_rbtree_min(root, sentinel);\n\n    timer = (ngx_msec_int_t) (node->key - ngx_current_msec);\n\n    return (ngx_msec_t) (timer > 0 ? timer : 0);\n}\n\n\nvoid\nngx_event_expire_timers(void)\n{\n    ngx_event_t        *ev;\n    ngx_rbtree_node_t  *node, *root, *sentinel;\n\n    sentinel = ngx_event_timer_rbtree.sentinel;\n\n    for ( ;; ) {\n        root = ngx_event_timer_rbtree.root;\n\n        if (root == sentinel) {\n            return;\n        }\n\n        node = ngx_rbtree_min(root, sentinel);\n\n        /* node->key > ngx_current_msec */\n\n        if ((ngx_msec_int_t) (node->key - ngx_current_msec) > 0) {\n            return;\n        }\n\n        ev = ngx_rbtree_data(node, ngx_event_t, timer);\n\n        ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,\n                       \"event timer del: %d: %M\",\n                       ngx_event_ident(ev->data), ev->timer.key);\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        ev->handler(ev);\n    }\n}\n\n\nngx_int_t\nngx_event_no_timers_left(void)\n{\n    ngx_event_t        *ev;\n    ngx_rbtree_node_t  *node, *root, *sentinel;\n\n    sentinel = ngx_event_timer_rbtree.sentinel;\n    root = ngx_event_timer_rbtree.root;\n\n    if (root == sentinel) {\n        return NGX_OK;\n    }\n\n    for (node = ngx_rbtree_min(root, sentinel);\n         node;\n         node = ngx_rbtree_next(&ngx_event_timer_rbtree, node))\n    {\n        ev = ngx_rbtree_data(node, ngx_event_t, timer);\n\n        if (!ev->cancelable) {\n            return NGX_AGAIN;\n        }\n    }\n\n    /* only cancelable timers left */\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "src/event/ngx_event_timer.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_EVENT_TIMER_H_INCLUDED_\n#define _NGX_EVENT_TIMER_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n\n\n#define NGX_TIMER_INFINITE  (ngx_msec_t) -1\n\n#define NGX_TIMER_LAZY_DELAY  300\n\n\nngx_int_t ngx_event_timer_init(ngx_log_t *log);\nngx_msec_t ngx_event_find_timer(void);\nvoid ngx_event_expire_timers(void);\nngx_int_t ngx_event_no_timers_left(void);\n\n\nextern ngx_rbtree_t  ngx_event_timer_rbtree;\n\n\nstatic ngx_inline void\nngx_event_del_timer(ngx_event_t *ev)\n{\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,\n                   \"event timer del: %d: %M\",\n                    ngx_event_ident(ev->data), ev->timer.key);\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\n\nstatic ngx_inline void\nngx_event_add_timer(ngx_event_t *ev, ngx_msec_t timer)\n{\n    ngx_msec_t      key;\n    ngx_msec_int_t  diff;\n\n    key = ngx_current_msec + timer;\n\n    if (ev->timer_set) {\n\n        /*\n         * Use a previous timer value if difference between it and a new\n         * value is less than NGX_TIMER_LAZY_DELAY milliseconds: this allows\n         * to minimize the rbtree operations for fast connections.\n         */\n\n        diff = (ngx_msec_int_t) (key - ev->timer.key);\n\n        if (ngx_abs(diff) < NGX_TIMER_LAZY_DELAY) {\n            ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ev->log, 0,\n                           \"event timer: %d, old: %M, new: %M\",\n                            ngx_event_ident(ev->data), ev->timer.key, key);\n            return;\n        }\n\n        ngx_del_timer(ev);\n    }\n\n    ev->timer.key = key;\n\n    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, ev->log, 0,\n                   \"event timer add: %d: %M:%M\",\n                    ngx_event_ident(ev->data), timer, ev->timer.key);\n\n    ngx_rbtree_insert(&ngx_event_timer_rbtree, &ev->timer);\n\n    ev->timer_set = 1;\n}\n\n\n#endif /* _NGX_EVENT_TIMER_H_INCLUDED_ */\n"
  },
  {
    "path": "src/event/ngx_event_udp.c",
    "content": "\n/*\n * Copyright (C) Roman Arutyunyan\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n\n\n#if !(NGX_WIN32)\n\nstruct ngx_udp_connection_s {\n    ngx_rbtree_node_t   node;\n    ngx_connection_t   *connection;\n    ngx_buf_t          *buffer;\n};\n\n\nstatic void ngx_close_accepted_udp_connection(ngx_connection_t *c);\nstatic ssize_t ngx_udp_shared_recv(ngx_connection_t *c, u_char *buf,\n    size_t size);\nstatic ngx_int_t ngx_insert_udp_connection(ngx_connection_t *c);\nstatic ngx_connection_t *ngx_lookup_udp_connection(ngx_listening_t *ls,\n    struct sockaddr *sockaddr, socklen_t socklen,\n    struct sockaddr *local_sockaddr, socklen_t local_socklen);\n\n\nvoid\nngx_event_recvmsg(ngx_event_t *ev)\n{\n    ssize_t            n;\n    ngx_buf_t          buf;\n    ngx_log_t         *log;\n    ngx_err_t          err;\n    socklen_t          socklen, local_socklen;\n    ngx_event_t       *rev, *wev;\n    struct iovec       iov[1];\n    struct msghdr      msg;\n    ngx_sockaddr_t     sa, lsa;\n    struct sockaddr   *sockaddr, *local_sockaddr;\n    ngx_listening_t   *ls;\n    ngx_event_conf_t  *ecf;\n    ngx_connection_t  *c, *lc;\n    static u_char      buffer[65535];\n\n#if (NGX_HAVE_ADDRINFO_CMSG)\n    u_char             msg_control[CMSG_SPACE(sizeof(ngx_addrinfo_t))];\n#endif\n\n    if (ev->timedout) {\n        if (ngx_enable_accept_events((ngx_cycle_t *) ngx_cycle) != NGX_OK) {\n            return;\n        }\n\n        ev->timedout = 0;\n    }\n\n    ecf = ngx_event_get_conf(ngx_cycle->conf_ctx, ngx_event_core_module);\n\n    if (!(ngx_event_flags & NGX_USE_KQUEUE_EVENT)) {\n        ev->available = ecf->multi_accept;\n    }\n\n    lc = ev->data;\n    ls = lc->listening;\n    ev->ready = 0;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,\n                   \"recvmsg on %V, ready: %d\", &ls->addr_text, ev->available);\n\n    do {\n        ngx_memzero(&msg, sizeof(struct msghdr));\n\n        iov[0].iov_base = (void *) buffer;\n        iov[0].iov_len = sizeof(buffer);\n\n        msg.msg_name = &sa;\n        msg.msg_namelen = sizeof(ngx_sockaddr_t);\n        msg.msg_iov = iov;\n        msg.msg_iovlen = 1;\n\n#if (NGX_HAVE_ADDRINFO_CMSG)\n        if (ls->wildcard) {\n            msg.msg_control = &msg_control;\n            msg.msg_controllen = sizeof(msg_control);\n\n            ngx_memzero(&msg_control, sizeof(msg_control));\n        }\n#endif\n\n        n = recvmsg(lc->fd, &msg, 0);\n\n        if (n == -1) {\n            err = ngx_socket_errno;\n\n            if (err == NGX_EAGAIN) {\n                ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, err,\n                               \"recvmsg() not ready\");\n                return;\n            }\n\n            ngx_log_error(NGX_LOG_ALERT, ev->log, err, \"recvmsg() failed\");\n\n            return;\n        }\n\n#if (NGX_HAVE_ADDRINFO_CMSG)\n        if (msg.msg_flags & (MSG_TRUNC|MSG_CTRUNC)) {\n            ngx_log_error(NGX_LOG_ALERT, ev->log, 0,\n                          \"recvmsg() truncated data\");\n            continue;\n        }\n#endif\n\n        sockaddr = msg.msg_name;\n        socklen = msg.msg_namelen;\n\n        if (socklen > (socklen_t) sizeof(ngx_sockaddr_t)) {\n            socklen = sizeof(ngx_sockaddr_t);\n        }\n\n        if (socklen == 0) {\n\n            /*\n             * on Linux recvmsg() returns zero msg_namelen\n             * when receiving packets from unbound AF_UNIX sockets\n             */\n\n            socklen = sizeof(struct sockaddr);\n            ngx_memzero(&sa, sizeof(struct sockaddr));\n            sa.sockaddr.sa_family = ls->sockaddr->sa_family;\n        }\n\n        local_sockaddr = ls->sockaddr;\n        local_socklen = ls->socklen;\n\n#if (NGX_HAVE_ADDRINFO_CMSG)\n\n        if (ls->wildcard) {\n            struct cmsghdr  *cmsg;\n\n            ngx_memcpy(&lsa, local_sockaddr, local_socklen);\n            local_sockaddr = &lsa.sockaddr;\n\n            for (cmsg = CMSG_FIRSTHDR(&msg);\n                 cmsg != NULL;\n                 cmsg = CMSG_NXTHDR(&msg, cmsg))\n            {\n                if (ngx_get_srcaddr_cmsg(cmsg, local_sockaddr) == NGX_OK) {\n                    break;\n                }\n            }\n        }\n\n#endif\n\n        c = ngx_lookup_udp_connection(ls, sockaddr, socklen, local_sockaddr,\n                                      local_socklen);\n\n        if (c) {\n\n#if (NGX_DEBUG)\n            if (c->log->log_level & NGX_LOG_DEBUG_EVENT) {\n                ngx_log_handler_pt  handler;\n\n                handler = c->log->handler;\n                c->log->handler = NULL;\n\n                ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                               \"recvmsg: fd:%d n:%z\", c->fd, n);\n\n                c->log->handler = handler;\n            }\n#endif\n\n            ngx_memzero(&buf, sizeof(ngx_buf_t));\n\n            buf.pos = buffer;\n            buf.last = buffer + n;\n\n            rev = c->read;\n\n            c->udp->buffer = &buf;\n\n            rev->ready = 1;\n            rev->active = 0;\n\n            rev->handler(rev);\n\n            if (c->udp) {\n                c->udp->buffer = NULL;\n            }\n\n            rev->ready = 0;\n            rev->active = 1;\n\n            goto next;\n        }\n\n#if (NGX_STAT_STUB)\n        (void) ngx_atomic_fetch_add(ngx_stat_accepted, 1);\n#endif\n\n        ngx_accept_disabled = ngx_cycle->connection_n / 8\n                              - ngx_cycle->free_connection_n;\n\n        c = ngx_get_connection(lc->fd, ev->log);\n        if (c == NULL) {\n            return;\n        }\n\n        c->shared = 1;\n        c->type = SOCK_DGRAM;\n        c->socklen = socklen;\n\n#if (NGX_STAT_STUB)\n        (void) ngx_atomic_fetch_add(ngx_stat_active, 1);\n#endif\n\n        c->pool = ngx_create_pool(ls->pool_size, ev->log);\n        if (c->pool == NULL) {\n            ngx_close_accepted_udp_connection(c);\n            return;\n        }\n\n        c->sockaddr = ngx_palloc(c->pool, socklen);\n        if (c->sockaddr == NULL) {\n            ngx_close_accepted_udp_connection(c);\n            return;\n        }\n\n        ngx_memcpy(c->sockaddr, sockaddr, socklen);\n\n        log = ngx_palloc(c->pool, sizeof(ngx_log_t));\n        if (log == NULL) {\n            ngx_close_accepted_udp_connection(c);\n            return;\n        }\n\n        *log = ls->log;\n\n        c->recv = ngx_udp_shared_recv;\n        c->send = ngx_udp_send;\n        c->send_chain = ngx_udp_send_chain;\n\n        c->need_flush_buf = 1;\n\n        c->log = log;\n        c->pool->log = log;\n        c->listening = ls;\n\n        if (local_sockaddr == &lsa.sockaddr) {\n            local_sockaddr = ngx_palloc(c->pool, local_socklen);\n            if (local_sockaddr == NULL) {\n                ngx_close_accepted_udp_connection(c);\n                return;\n            }\n\n            ngx_memcpy(local_sockaddr, &lsa, local_socklen);\n        }\n\n        c->local_sockaddr = local_sockaddr;\n        c->local_socklen = local_socklen;\n\n        c->buffer = ngx_create_temp_buf(c->pool, n);\n        if (c->buffer == NULL) {\n            ngx_close_accepted_udp_connection(c);\n            return;\n        }\n\n        c->buffer->last = ngx_cpymem(c->buffer->last, buffer, n);\n\n        rev = c->read;\n        wev = c->write;\n\n        rev->active = 1;\n        wev->ready = 1;\n\n        rev->log = log;\n        wev->log = log;\n\n        /*\n         * TODO: MT: - ngx_atomic_fetch_add()\n         *             or protection by critical section or light mutex\n         *\n         * TODO: MP: - allocated in a shared memory\n         *           - ngx_atomic_fetch_add()\n         *             or protection by critical section or light mutex\n         */\n\n        c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1);\n\n        c->start_time = ngx_current_msec;\n\n#if (NGX_STAT_STUB)\n        (void) ngx_atomic_fetch_add(ngx_stat_handled, 1);\n#endif\n\n        if (ls->addr_ntop) {\n            c->addr_text.data = ngx_pnalloc(c->pool, ls->addr_text_max_len);\n            if (c->addr_text.data == NULL) {\n                ngx_close_accepted_udp_connection(c);\n                return;\n            }\n\n            c->addr_text.len = ngx_sock_ntop(c->sockaddr, c->socklen,\n                                             c->addr_text.data,\n                                             ls->addr_text_max_len, 0);\n            if (c->addr_text.len == 0) {\n                ngx_close_accepted_udp_connection(c);\n                return;\n            }\n        }\n\n#if (NGX_DEBUG)\n        {\n        ngx_str_t  addr;\n        u_char     text[NGX_SOCKADDR_STRLEN];\n\n        ngx_debug_accepted_connection(ecf, c);\n\n        if (log->log_level & NGX_LOG_DEBUG_EVENT) {\n            addr.data = text;\n            addr.len = ngx_sock_ntop(c->sockaddr, c->socklen, text,\n                                     NGX_SOCKADDR_STRLEN, 1);\n\n            ngx_log_debug4(NGX_LOG_DEBUG_EVENT, log, 0,\n                           \"*%uA recvmsg: %V fd:%d n:%z\",\n                           c->number, &addr, c->fd, n);\n        }\n\n        }\n#endif\n\n        if (ngx_insert_udp_connection(c) != NGX_OK) {\n            ngx_close_accepted_udp_connection(c);\n            return;\n        }\n\n        log->data = NULL;\n        log->handler = NULL;\n\n        ls->handler(c);\n\n    next:\n\n        if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {\n            ev->available -= n;\n        }\n\n    } while (ev->available);\n}\n\n\nstatic void\nngx_close_accepted_udp_connection(ngx_connection_t *c)\n{\n    ngx_free_connection(c);\n\n    c->fd = (ngx_socket_t) -1;\n\n    if (c->pool) {\n        ngx_destroy_pool(c->pool);\n    }\n\n#if (NGX_STAT_STUB)\n    (void) ngx_atomic_fetch_add(ngx_stat_active, -1);\n#endif\n}\n\n\nstatic ssize_t\nngx_udp_shared_recv(ngx_connection_t *c, u_char *buf, size_t size)\n{\n    ssize_t     n;\n    ngx_buf_t  *b;\n\n    if (c->udp == NULL || c->udp->buffer == NULL) {\n        return NGX_AGAIN;\n    }\n\n    b = c->udp->buffer;\n\n    n = ngx_min(b->last - b->pos, (ssize_t) size);\n\n    ngx_memcpy(buf, b->pos, n);\n\n    c->udp->buffer = NULL;\n\n    c->read->ready = 0;\n    c->read->active = 1;\n\n    return n;\n}\n\n\nvoid\nngx_udp_rbtree_insert_value(ngx_rbtree_node_t *temp,\n    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)\n{\n    ngx_int_t               rc;\n    ngx_connection_t       *c, *ct;\n    ngx_rbtree_node_t     **p;\n    ngx_udp_connection_t   *udp, *udpt;\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            udp = (ngx_udp_connection_t *) node;\n            c = udp->connection;\n\n            udpt = (ngx_udp_connection_t *) temp;\n            ct = udpt->connection;\n\n            rc = ngx_cmp_sockaddr(c->sockaddr, c->socklen,\n                                  ct->sockaddr, ct->socklen, 1);\n\n            if (rc == 0 && c->listening->wildcard) {\n                rc = ngx_cmp_sockaddr(c->local_sockaddr, c->local_socklen,\n                                      ct->local_sockaddr, ct->local_socklen, 1);\n            }\n\n            p = (rc < 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_insert_udp_connection(ngx_connection_t *c)\n{\n    uint32_t               hash;\n    ngx_pool_cleanup_t    *cln;\n    ngx_udp_connection_t  *udp;\n\n    if (c->udp) {\n        return NGX_OK;\n    }\n\n    udp = ngx_pcalloc(c->pool, sizeof(ngx_udp_connection_t));\n    if (udp == NULL) {\n        return NGX_ERROR;\n    }\n\n    udp->connection = c;\n\n    ngx_crc32_init(hash);\n    ngx_crc32_update(&hash, (u_char *) c->sockaddr, c->socklen);\n\n    if (c->listening->wildcard) {\n        ngx_crc32_update(&hash, (u_char *) c->local_sockaddr, c->local_socklen);\n    }\n\n    ngx_crc32_final(hash);\n\n    udp->node.key = hash;\n\n    cln = ngx_pool_cleanup_add(c->pool, 0);\n    if (cln == NULL) {\n        return NGX_ERROR;\n    }\n\n    cln->data = c;\n    cln->handler = ngx_delete_udp_connection;\n\n    ngx_rbtree_insert(&c->listening->rbtree, &udp->node);\n\n    c->udp = udp;\n\n    return NGX_OK;\n}\n\n\nvoid\nngx_delete_udp_connection(void *data)\n{\n    ngx_connection_t  *c = data;\n\n    if (c->udp == NULL) {\n        return;\n    }\n\n    ngx_rbtree_delete(&c->listening->rbtree, &c->udp->node);\n\n    c->udp = NULL;\n}\n\n\nstatic ngx_connection_t *\nngx_lookup_udp_connection(ngx_listening_t *ls, struct sockaddr *sockaddr,\n    socklen_t socklen, struct sockaddr *local_sockaddr, socklen_t local_socklen)\n{\n    uint32_t               hash;\n    ngx_int_t              rc;\n    ngx_connection_t      *c;\n    ngx_rbtree_node_t     *node, *sentinel;\n    ngx_udp_connection_t  *udp;\n\n#if (NGX_HAVE_UNIX_DOMAIN)\n\n    if (sockaddr->sa_family == AF_UNIX) {\n        struct sockaddr_un *saun = (struct sockaddr_un *) sockaddr;\n\n        if (socklen <= (socklen_t) offsetof(struct sockaddr_un, sun_path)\n            || saun->sun_path[0] == '\\0')\n        {\n            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ngx_cycle->log, 0,\n                           \"unbound unix socket\");\n            return NULL;\n        }\n    }\n\n#endif\n\n    node = ls->rbtree.root;\n    sentinel = ls->rbtree.sentinel;\n\n    ngx_crc32_init(hash);\n    ngx_crc32_update(&hash, (u_char *) sockaddr, socklen);\n\n    if (ls->wildcard) {\n        ngx_crc32_update(&hash, (u_char *) local_sockaddr, local_socklen);\n    }\n\n    ngx_crc32_final(hash);\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        udp = (ngx_udp_connection_t *) node;\n\n        c = udp->connection;\n\n        rc = ngx_cmp_sockaddr(sockaddr, socklen,\n                              c->sockaddr, c->socklen, 1);\n\n        if (rc == 0 && ls->wildcard) {\n            rc = ngx_cmp_sockaddr(local_sockaddr, local_socklen,\n                                  c->local_sockaddr, c->local_socklen, 1);\n        }\n\n        if (rc == 0) {\n            return c;\n        }\n\n        node = (rc < 0) ? node->left : node->right;\n    }\n\n    return NULL;\n}\n\n#else\n\nvoid\nngx_delete_udp_connection(void *data)\n{\n    return;\n}\n\n#endif\n"
  },
  {
    "path": "src/event/ngx_event_udp.h",
    "content": "\n/*\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_EVENT_UDP_H_INCLUDED_\n#define _NGX_EVENT_UDP_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\n#if !(NGX_WIN32)\n\n#if ((NGX_HAVE_MSGHDR_MSG_CONTROL)                                            \\\n     && (NGX_HAVE_IP_SENDSRCADDR || NGX_HAVE_IP_RECVDSTADDR                   \\\n         || NGX_HAVE_IP_PKTINFO                                               \\\n         || (NGX_HAVE_INET6 && NGX_HAVE_IPV6_RECVPKTINFO)))\n#define NGX_HAVE_ADDRINFO_CMSG  1\n\n#endif\n\n\n#if (NGX_HAVE_ADDRINFO_CMSG)\n\ntypedef union {\n#if (NGX_HAVE_IP_SENDSRCADDR || NGX_HAVE_IP_RECVDSTADDR)\n    struct in_addr        addr;\n#endif\n\n#if (NGX_HAVE_IP_PKTINFO)\n    struct in_pktinfo     pkt;\n#endif\n\n#if (NGX_HAVE_INET6 && NGX_HAVE_IPV6_RECVPKTINFO)\n    struct in6_pktinfo    pkt6;\n#endif\n} ngx_addrinfo_t;\n\nsize_t ngx_set_srcaddr_cmsg(struct cmsghdr *cmsg,\n    struct sockaddr *local_sockaddr);\nngx_int_t ngx_get_srcaddr_cmsg(struct cmsghdr *cmsg,\n    struct sockaddr *local_sockaddr);\n\n#endif\n\nvoid ngx_event_recvmsg(ngx_event_t *ev);\nssize_t ngx_sendmsg(ngx_connection_t *c, struct msghdr *msg, int flags);\nvoid ngx_udp_rbtree_insert_value(ngx_rbtree_node_t *temp,\n    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);\n#endif\n\nvoid ngx_delete_udp_connection(void *data);\n\n\n#endif /* _NGX_EVENT_UDP_H_INCLUDED_ */\n"
  },
  {
    "path": "src/event/ngx_event_udpv2.c",
    "content": "#include <ngx_event.h>\n#include <ngx_event_udpv2.h>\n#if (T_NGX_UDPV2)\n/**\n * for udpv2 posted event\n * */\nngx_queue_t         ngx_udpv2_posted_event;\n\ntypedef struct ngx_udpv2_msg_control_st ngx_udpv2_msg_control_t;\n\nstruct ngx_udpv2_msg_control_st\n{\n    union {\n#if (NGX_HAVE_MSGHDR_MSG_CONTROL)\n        #if (NGX_HAVE_IP_RECVDSTADDR)\n        u_char             msg_control[CMSG_SPACE(sizeof(struct in_addr))];\n#elif (NGX_HAVE_IP_PKTINFO)\n        u_char             msg_control[CMSG_SPACE(sizeof(struct in_pktinfo))];\n#endif // NGX_HAVE_IP_RECVDSTADDR\n\n#if (NGX_HAVE_INET6 && NGX_HAVE_IPV6_RECVPKTINFO)\n        u_char             msg_control6[CMSG_SPACE(sizeof(struct in6_pktinfo))];\n#endif // NGX_HAVE_INET6 && NGX_HAVE_IPV6_RECVPKTINFO\n\n#endif // NGX_HAVE_MSGHDR_MSG_CONTROL\n    } __ipinfo;\n\n    u_char                 ts[CMSG_SPACE(sizeof(struct timespec))]  ;\n};\n\nvoid\nngx_event_udpv2_init_listening(ngx_listening_t *ls) {\n\n    ls->udpv2_current_processing = NULL;\n    ngx_queue_init(&ls->udpv2_filter);\n\n    ls->udpv2_traffic_filter.func = NULL;\n    ngx_udpv2_add_dispatch_filter(ls, &ls->udpv2_traffic_filter);\n}\n\nvoid\nngx_udpv2_write_handler_mainlogic(ngx_event_t *wev)\n{\n    ngx_listening_t *ls;\n    ngx_connection_t *lc;\n\n    lc  = (ngx_connection_t*)(wev->data) ;\n    ls = lc->listening ;\n\n    /* 不管是何种触发方式，删除写事件 */\n    ngx_del_event(wev, NGX_WRITE_EVENT, 0);\n\n    /* 处理posted的写事件 */\n    ngx_event_process_posted((ngx_cycle_t *) ngx_cycle, ngx_udpv2_writable_queue(ls));\n}\n\nngx_inline ngx_queue_t *\nngx_udpv2_writable_queue(ngx_listening_t *ls)\n{\n    return &ls->writable_queue;\n}\n\nngx_queue_t *\nngx_udpv2_active_writable_queue(ngx_listening_t *ls)\n{\n    ngx_event_t         *wev;\n    ngx_connection_t    *c;\n\n    if (!ls) {\n        return NULL;\n    }\n\n    c   = ls->connection;\n    wev = c->write;\n\n    if (!wev->active) {\n        // active writable event\n        ngx_handle_write_event(wev, 0);\n    }\n\n    return ngx_udpv2_writable_queue(ls);\n}\n\nint\nngx_udpv2_push_dispatch_filter(ngx_cycle_t *cycle, ngx_listening_t *ls, ngx_udpv2_traffic_filter_handler func)\n{\n    ngx_udpv2_traffic_filter_t  *tf;\n    tf = ngx_pcalloc(cycle->pool, sizeof(*tf));\n    if (!tf) {\n        return NGX_ERROR;\n    }\n    tf->func = func;\n    ngx_udpv2_add_dispatch_filter(ls, tf);\n    return NGX_OK;\n}\n\n\nngx_listening_t*\nngx_udpv2_reset_dispatch_filter(ngx_listening_t *ls)\n{\n    ngx_queue_init(&ls->udpv2_filter);\n    return ls;\n}\n\nngx_listening_t*\nngx_udpv2_add_dispatch_filter(ngx_listening_t *ls,ngx_udpv2_traffic_filter_t *filter)\n{\n    ngx_queue_insert_tail(&ls->udpv2_filter,&filter->sk);\n    return ls;\n}\n\nngx_inline void\nngx_udpv2_process_posted_traffic() {\n    /* trigger posted event if nessary */\n    ngx_event_process_posted((ngx_cycle_t *) ngx_cycle, &ngx_udpv2_posted_event);\n}\n\nngx_inline void\nngx_udpv2_dispatch_traffic(ngx_udpv2_packets_hdr_t *uhdr)\n{\n    ngx_listening_t *ls ;\n    ngx_udpv2_traffic_filter_t *filter;\n    ngx_queue_t *head ;\n    ngx_queue_t  *f;\n    ngx_queue_t  *p, *n;\n    size_t i;\n    ngx_udpv2_packet_t *upkt , *prev;\n\n    /* validate */\n    if (uhdr == NULL || uhdr->ls == NULL) {\n        return;\n    }\n\n    size_t sz = uhdr->npkts;\n\n    ls      = uhdr->ls;\n    head    = &(uhdr->ls->udpv2_filter);\n    prev    = ls->udpv2_current_processing;\n\n    for(f = ngx_queue_head(head); sz > 0 && f != head ; f = ngx_queue_next(f)) {\n\n        filter = ngx_queue_data(f, ngx_udpv2_traffic_filter_t, sk);\n        if (!filter->func) {\n            continue;\n        }\n\n        for(i = 0 , p = ngx_queue_head(&uhdr->pkts) ; i < sz ; i++ ){\n\n            upkt = ngx_queue_data(p, ngx_udpv2_packet_t, pkt_list);\n            n = ngx_queue_next(p);\n\n            ls->udpv2_current_processing = upkt;\n            ngx_memory_barrier();\n\n            switch(filter->func(ls, upkt)) {\n                case NGX_UDPV2_DONE:\n                case NGX_UDPV2_DROP:\n                {\n                    if (--uhdr->npkts > 0) {\n                        ngx_queue_remove(p);\n                        ngx_queue_insert_tail(&uhdr->pkts, p);\n                    }\n                }\n                default:\n                    break;\n            }\n\n            p = n ;\n        }\n\n        /* update sz */\n        sz = uhdr->npkts ;\n    }\n\n    /* reset current processing */\n    ls->udpv2_current_processing = prev;\n}\n\nvoid\nngx_udpv2_dispatch_packet(ngx_listening_t* ls, ngx_udpv2_packet_t *upkt, ngx_uint_t flags)\n{\n    ngx_udpv2_packets_hdr_t uhdr = NGX_UDPV2_PACKETS_HDR_INIT(uhdr);\n\n    uhdr.ls = ls;\n    NGX_UDPV2_PACKETS_HDR_ADD_PACKET(&uhdr, upkt);\n    uhdr.npkts = 1;\n\n    ngx_udpv2_dispatch_traffic(&uhdr);\n    ngx_udpv2_process_posted_traffic();\n}\n\n#endif"
  },
  {
    "path": "src/event/ngx_event_udpv2.h",
    "content": "#ifndef _NGX_EVENT_UDP_V2_H_INCLUDED_\n#define _NGX_EVENT_UDP_V2_H_INCLUDED_\n#if (T_NGX_UDPV2)\n/**\n * @david.sw\n * udpv2 is extensible\n * it is based on nginx udp\n * */\n\n#ifndef _NGX_EVENT_H_INCLUDED_\n#error \"do not include ngx_event_udpv2 directly, include ngx_event.h. \"\n#endif\n\n#include <ngx_core.h>\n\n/**\n * declare for posted event\n * */\nextern ngx_queue_t ngx_udpv2_posted_event;\n\n/**\n * marcos\n * */\n#define NGX_UDPV2_ALIGN(n)          __attribute__((aligned(n)))\n#define NGX_UDPV2_PACK(name,n)      unsigned char name[0] NGX_UDPV2_ALIGN(n)\n\n/**\n * flags for dispatch\n * */\n#define NGX_UDPV2_F_DELAY_POSTED    0x1\n\n/**\n * udp packet\n * */\nstruct ngx_udpv2_packet_st\n{\n    /* offset to payload   */\n    unsigned char*          pkt_payload;\n    /* payload size         */\n    size_t                  pkt_sz;\n    /* microsecond of udp packet generation */\n    uint64_t                pkt_micrs;\n    /* local sockaddr of udp packet */\n    ngx_sockaddr_t          pkt_local_sockaddr;\n    /* length of local sockaddr for udp packet */\n    socklen_t               pkt_local_socklen;\n    /* remote sockaddr of udp packet */\n    ngx_sockaddr_t          pkt_sockaddr;\n    /* length of remote sockaddr for udp packet */\n    socklen_t               pkt_socklen;\n    NGX_UDPV2_PACK(property, NGX_CPU_CACHE_LINE);\n    /* list node for packet */\n    ngx_queue_t             pkt_list;\n\n} NGX_UDPV2_ALIGN(NGX_CPU_CACHE_LINE);\n\n/**\n * udp packets hdr\n * */\nstruct ngx_udpv2_packets_hdr_st\n{\n    /**\n     * list for pkts\n     * */\n    ngx_queue_t         pkts;\n\n    /**\n    * from where\n     * */\n    ngx_listening_t    *ls;\n\n    /**\n     * capability of pkts\n     * */\n    size_t              pkts_capability ;\n\n    /**\n     * number of pkts ready\n     * */\n    ssize_t             npkts;\n\n    /**\n     * flags for pkts\n     * */\n    int                 flags ;\n\n};\n\n#define NGX_UDPV2_PACKETS_HDR_INIT(name,...)  {     \\\n                                                    \\\n    {&(name.pkts),&(name.pkts)},                    \\\n    NULL,                                           \\\n    0,                                              \\\n    0,                                              \\\n    0,                                              \\\n    __VA_ARGS__                                     \\\n}\n\n#define NGX_UDPV2_PACKETS_HDR_FIRST_PACKET(uhdr) ({                                 \\\n    ngx_udpv2_packets_hdr_t *__inner_hdr = (ngx_udpv2_packets_hdr_t*) (uhdr);       \\\n    ngx_queue_data(ngx_queue_head(&__inner_hdr->pkts),ngx_udpv2_packet_t,pkt_list); \\\n})\n\n#define NGX_UDPV2_PACKETS_HDR_ADD_PACKET(uhdr, upkt)           do {                 \\\n    ngx_udpv2_packets_hdr_t *__inner_hdr = (ngx_udpv2_packets_hdr_t*) (uhdr);       \\\n    ngx_queue_insert_tail(&__inner_hdr->pkts, &((upkt)->pkt_list));                 \\\n    __inner_hdr->pkts_capability++;                                                 \\\n}while(0)\n\n\n/**\n *  初始化udpv2所需的数据结构\n * */\n\nvoid ngx_event_udpv2_init_listening(ngx_listening_t *ls);\n\n/**\n *  分发流量到处理流量的连接\n *  当未找到合适的处理连接时，执行ls->udp_accept_handler(urp) or  ngx_event_udpv2_create_udp_connection\n * */\n\nvoid ngx_udpv2_dispatch_traffic(ngx_udpv2_packets_hdr_t *uhdr);\n\n/**\n * 通知所有udpv2批处理数据结束。\n * */\nvoid ngx_udpv2_process_posted_traffic(void);\n\n/**\n *  分发单个报文到处理流量的连接\n *  当未找到合适的处理连接时，执行ls->udp_accept_handler(urp) or  ngx_event_udpv2_create_udp_connection\n * */\nvoid ngx_udpv2_dispatch_packet(ngx_listening_t *ls, ngx_udpv2_packet_t *upkt, ngx_uint_t flags);\n\n/**\n * Get writable queue\n * */\nngx_queue_t * ngx_udpv2_writable_queue(ngx_listening_t *ls);\n\n/**\n * Get writable queue  and try to active it\n * */\nngx_queue_t * ngx_udpv2_active_writable_queue(ngx_listening_t *ls);\n\n\n/**\n * 在accept的udp模型中，模拟可写事件的核心逻辑。\n * */\nvoid ngx_udpv2_write_handler_mainlogic(ngx_event_t *);\n\n/**\n * add filter\n * */\nint ngx_udpv2_push_dispatch_filter(ngx_cycle_t *cycle, ngx_listening_t *ls, ngx_udpv2_traffic_filter_handler func);\n\n/**\n *  reset all traffic filter\n * */\nngx_listening_t* ngx_udpv2_reset_dispatch_filter(ngx_listening_t *ls);\n\n/**\n * push new traffic filter as stack\n * */\nngx_listening_t* ngx_udpv2_add_dispatch_filter(ngx_listening_t *ls, ngx_udpv2_traffic_filter_t *filter);\n\n#endif\n#endif // _NGX_EVENT_UDP_V2_H_INCLUDED_`"
  },
  {
    "path": "src/http/modules/ngx_http_access_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\ntypedef struct {\n    in_addr_t         mask;\n    in_addr_t         addr;\n    ngx_uint_t        deny;      /* unsigned  deny:1; */\n} ngx_http_access_rule_t;\n\n#if (NGX_HAVE_INET6)\n\ntypedef struct {\n    struct in6_addr   addr;\n    struct in6_addr   mask;\n    ngx_uint_t        deny;      /* unsigned  deny:1; */\n} ngx_http_access_rule6_t;\n\n#endif\n\n#if (NGX_HAVE_UNIX_DOMAIN)\n\ntypedef struct {\n    ngx_uint_t        deny;      /* unsigned  deny:1; */\n} ngx_http_access_rule_un_t;\n\n#endif\n\ntypedef struct {\n    ngx_array_t      *rules;     /* array of ngx_http_access_rule_t */\n#if (NGX_HAVE_INET6)\n    ngx_array_t      *rules6;    /* array of ngx_http_access_rule6_t */\n#endif\n#if (NGX_HAVE_UNIX_DOMAIN)\n    ngx_array_t      *rules_un;  /* array of ngx_http_access_rule_un_t */\n#endif\n} ngx_http_access_loc_conf_t;\n\n\nstatic ngx_int_t ngx_http_access_handler(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_access_inet(ngx_http_request_t *r,\n    ngx_http_access_loc_conf_t *alcf, in_addr_t addr);\n#if (NGX_HAVE_INET6)\nstatic ngx_int_t ngx_http_access_inet6(ngx_http_request_t *r,\n    ngx_http_access_loc_conf_t *alcf, u_char *p);\n#endif\n#if (NGX_HAVE_UNIX_DOMAIN)\nstatic ngx_int_t ngx_http_access_unix(ngx_http_request_t *r,\n    ngx_http_access_loc_conf_t *alcf);\n#endif\nstatic ngx_int_t ngx_http_access_found(ngx_http_request_t *r, ngx_uint_t deny);\nstatic char *ngx_http_access_rule(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic void *ngx_http_access_create_loc_conf(ngx_conf_t *cf);\nstatic char *ngx_http_access_merge_loc_conf(ngx_conf_t *cf,\n    void *parent, void *child);\nstatic ngx_int_t ngx_http_access_init(ngx_conf_t *cf);\n\n\nstatic ngx_command_t  ngx_http_access_commands[] = {\n\n    { ngx_string(\"allow\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF\n                        |NGX_CONF_TAKE1,\n      ngx_http_access_rule,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"deny\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF\n                        |NGX_CONF_TAKE1,\n      ngx_http_access_rule,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n      ngx_null_command\n};\n\n\n\nstatic ngx_http_module_t  ngx_http_access_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    ngx_http_access_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    ngx_http_access_create_loc_conf,       /* create location configuration */\n    ngx_http_access_merge_loc_conf         /* merge location configuration */\n};\n\n\nngx_module_t  ngx_http_access_module = {\n    NGX_MODULE_V1,\n    &ngx_http_access_module_ctx,           /* module context */\n    ngx_http_access_commands,              /* 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_access_handler(ngx_http_request_t *r)\n{\n    struct sockaddr_in          *sin;\n    ngx_http_access_loc_conf_t  *alcf;\n#if (NGX_HAVE_INET6)\n    u_char                      *p;\n    in_addr_t                    addr;\n    struct sockaddr_in6         *sin6;\n#endif\n\n    alcf = ngx_http_get_module_loc_conf(r, ngx_http_access_module);\n\n    switch (r->connection->sockaddr->sa_family) {\n\n    case AF_INET:\n        if (alcf->rules) {\n            sin = (struct sockaddr_in *) r->connection->sockaddr;\n            return ngx_http_access_inet(r, alcf, sin->sin_addr.s_addr);\n        }\n        break;\n\n#if (NGX_HAVE_INET6)\n\n    case AF_INET6:\n        sin6 = (struct sockaddr_in6 *) r->connection->sockaddr;\n        p = sin6->sin6_addr.s6_addr;\n\n        if (alcf->rules && IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {\n            addr = p[12] << 24;\n            addr += p[13] << 16;\n            addr += p[14] << 8;\n            addr += p[15];\n            return ngx_http_access_inet(r, alcf, htonl(addr));\n        }\n\n        if (alcf->rules6) {\n            return ngx_http_access_inet6(r, alcf, p);\n        }\n\n        break;\n\n#endif\n\n#if (NGX_HAVE_UNIX_DOMAIN)\n\n    case AF_UNIX:\n        if (alcf->rules_un) {\n            return ngx_http_access_unix(r, alcf);\n        }\n\n        break;\n\n#endif\n    }\n\n    return NGX_DECLINED;\n}\n\n\nstatic ngx_int_t\nngx_http_access_inet(ngx_http_request_t *r, ngx_http_access_loc_conf_t *alcf,\n    in_addr_t addr)\n{\n    ngx_uint_t               i;\n    ngx_http_access_rule_t  *rule;\n\n    rule = alcf->rules->elts;\n    for (i = 0; i < alcf->rules->nelts; i++) {\n\n        ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"access: %08XD %08XD %08XD\",\n                       addr, rule[i].mask, rule[i].addr);\n\n        if ((addr & rule[i].mask) == rule[i].addr) {\n            return ngx_http_access_found(r, rule[i].deny);\n        }\n    }\n\n    return NGX_DECLINED;\n}\n\n\n#if (NGX_HAVE_INET6)\n\nstatic ngx_int_t\nngx_http_access_inet6(ngx_http_request_t *r, ngx_http_access_loc_conf_t *alcf,\n    u_char *p)\n{\n    ngx_uint_t                n;\n    ngx_uint_t                i;\n    ngx_http_access_rule6_t  *rule6;\n\n    rule6 = alcf->rules6->elts;\n    for (i = 0; i < alcf->rules6->nelts; i++) {\n\n#if (NGX_DEBUG)\n        {\n        size_t  cl, ml, al;\n        u_char  ct[NGX_INET6_ADDRSTRLEN];\n        u_char  mt[NGX_INET6_ADDRSTRLEN];\n        u_char  at[NGX_INET6_ADDRSTRLEN];\n\n        cl = ngx_inet6_ntop(p, ct, NGX_INET6_ADDRSTRLEN);\n        ml = ngx_inet6_ntop(rule6[i].mask.s6_addr, mt, NGX_INET6_ADDRSTRLEN);\n        al = ngx_inet6_ntop(rule6[i].addr.s6_addr, at, NGX_INET6_ADDRSTRLEN);\n\n        ngx_log_debug6(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"access: %*s %*s %*s\", cl, ct, ml, mt, al, at);\n        }\n#endif\n\n        for (n = 0; n < 16; n++) {\n            if ((p[n] & rule6[i].mask.s6_addr[n]) != rule6[i].addr.s6_addr[n]) {\n                goto next;\n            }\n        }\n\n        return ngx_http_access_found(r, rule6[i].deny);\n\n    next:\n        continue;\n    }\n\n    return NGX_DECLINED;\n}\n\n#endif\n\n\n#if (NGX_HAVE_UNIX_DOMAIN)\n\nstatic ngx_int_t\nngx_http_access_unix(ngx_http_request_t *r, ngx_http_access_loc_conf_t *alcf)\n{\n    ngx_uint_t                  i;\n    ngx_http_access_rule_un_t  *rule_un;\n\n    rule_un = alcf->rules_un->elts;\n    for (i = 0; i < alcf->rules_un->nelts; i++) {\n\n        /* TODO: check path */\n        if (1) {\n            return ngx_http_access_found(r, rule_un[i].deny);\n        }\n    }\n\n    return NGX_DECLINED;\n}\n\n#endif\n\n\nstatic ngx_int_t\nngx_http_access_found(ngx_http_request_t *r, ngx_uint_t deny)\n{\n    ngx_http_core_loc_conf_t  *clcf;\n\n    if (deny) {\n        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n        if (clcf->satisfy == NGX_HTTP_SATISFY_ALL) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"access forbidden by rule\");\n        }\n\n        return NGX_HTTP_FORBIDDEN;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic char *\nngx_http_access_rule(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_access_loc_conf_t *alcf = conf;\n\n    ngx_int_t                   rc;\n    ngx_uint_t                  all;\n    ngx_str_t                  *value;\n    ngx_cidr_t                  cidr;\n    ngx_http_access_rule_t     *rule;\n#if (NGX_HAVE_INET6)\n    ngx_http_access_rule6_t    *rule6;\n#endif\n#if (NGX_HAVE_UNIX_DOMAIN)\n    ngx_http_access_rule_un_t  *rule_un;\n#endif\n\n    all = 0;\n    ngx_memzero(&cidr, sizeof(ngx_cidr_t));\n\n    value = cf->args->elts;\n\n    if (value[1].len == 3 && ngx_strcmp(value[1].data, \"all\") == 0) {\n        all = 1;\n\n#if (NGX_HAVE_UNIX_DOMAIN)\n    } else if (value[1].len == 5 && ngx_strcmp(value[1].data, \"unix:\") == 0) {\n        cidr.family = AF_UNIX;\n#endif\n\n    } else {\n        rc = ngx_ptocidr(&value[1], &cidr);\n\n        if (rc == NGX_ERROR) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                         \"invalid parameter \\\"%V\\\"\", &value[1]);\n            return NGX_CONF_ERROR;\n        }\n\n        if (rc == NGX_DONE) {\n            ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                         \"low address bits of %V are meaningless\", &value[1]);\n        }\n    }\n\n    if (cidr.family == AF_INET || all) {\n\n        if (alcf->rules == NULL) {\n            alcf->rules = ngx_array_create(cf->pool, 4,\n                                           sizeof(ngx_http_access_rule_t));\n            if (alcf->rules == NULL) {\n                return NGX_CONF_ERROR;\n            }\n        }\n\n        rule = ngx_array_push(alcf->rules);\n        if (rule == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        rule->mask = cidr.u.in.mask;\n        rule->addr = cidr.u.in.addr;\n        rule->deny = (value[0].data[0] == 'd') ? 1 : 0;\n    }\n\n#if (NGX_HAVE_INET6)\n    if (cidr.family == AF_INET6 || all) {\n\n        if (alcf->rules6 == NULL) {\n            alcf->rules6 = ngx_array_create(cf->pool, 4,\n                                            sizeof(ngx_http_access_rule6_t));\n            if (alcf->rules6 == NULL) {\n                return NGX_CONF_ERROR;\n            }\n        }\n\n        rule6 = ngx_array_push(alcf->rules6);\n        if (rule6 == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        rule6->mask = cidr.u.in6.mask;\n        rule6->addr = cidr.u.in6.addr;\n        rule6->deny = (value[0].data[0] == 'd') ? 1 : 0;\n    }\n#endif\n\n#if (NGX_HAVE_UNIX_DOMAIN)\n    if (cidr.family == AF_UNIX || all) {\n\n        if (alcf->rules_un == NULL) {\n            alcf->rules_un = ngx_array_create(cf->pool, 1,\n                                            sizeof(ngx_http_access_rule_un_t));\n            if (alcf->rules_un == NULL) {\n                return NGX_CONF_ERROR;\n            }\n        }\n\n        rule_un = ngx_array_push(alcf->rules_un);\n        if (rule_un == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        rule_un->deny = (value[0].data[0] == 'd') ? 1 : 0;\n    }\n#endif\n\n    return NGX_CONF_OK;\n}\n\n\nstatic void *\nngx_http_access_create_loc_conf(ngx_conf_t *cf)\n{\n    ngx_http_access_loc_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_access_loc_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    return conf;\n}\n\n\nstatic char *\nngx_http_access_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_http_access_loc_conf_t  *prev = parent;\n    ngx_http_access_loc_conf_t  *conf = child;\n\n    if (conf->rules == NULL\n#if (NGX_HAVE_INET6)\n        && conf->rules6 == NULL\n#endif\n#if (NGX_HAVE_UNIX_DOMAIN)\n        && conf->rules_un == NULL\n#endif\n    ) {\n        conf->rules = prev->rules;\n#if (NGX_HAVE_INET6)\n        conf->rules6 = prev->rules6;\n#endif\n#if (NGX_HAVE_UNIX_DOMAIN)\n        conf->rules_un = prev->rules_un;\n#endif\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_access_init(ngx_conf_t *cf)\n{\n    ngx_http_handler_pt        *h;\n    ngx_http_core_main_conf_t  *cmcf;\n\n    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);\n\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_access_handler;\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "src/http/modules/ngx_http_addition_filter_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\ntypedef struct {\n    ngx_str_t     before_body;\n    ngx_str_t     after_body;\n\n    ngx_hash_t    types;\n    ngx_array_t  *types_keys;\n} ngx_http_addition_conf_t;\n\n\ntypedef struct {\n    ngx_uint_t    before_body_sent;\n} ngx_http_addition_ctx_t;\n\n\nstatic void *ngx_http_addition_create_conf(ngx_conf_t *cf);\nstatic char *ngx_http_addition_merge_conf(ngx_conf_t *cf, void *parent,\n    void *child);\nstatic ngx_int_t ngx_http_addition_filter_init(ngx_conf_t *cf);\n\n\nstatic ngx_command_t  ngx_http_addition_commands[] = {\n\n    { ngx_string(\"add_before_body\"),\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_addition_conf_t, before_body),\n      NULL },\n\n    { ngx_string(\"add_after_body\"),\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_addition_conf_t, after_body),\n      NULL },\n\n    { ngx_string(\"addition_types\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,\n      ngx_http_types_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_addition_conf_t, types_keys),\n      &ngx_http_html_default_types[0] },\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_addition_filter_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    ngx_http_addition_filter_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    ngx_http_addition_create_conf,         /* create location configuration */\n    ngx_http_addition_merge_conf           /* merge location configuration */\n};\n\n\nngx_module_t  ngx_http_addition_filter_module = {\n    NGX_MODULE_V1,\n    &ngx_http_addition_filter_module_ctx,  /* module context */\n    ngx_http_addition_commands,            /* 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_http_output_header_filter_pt  ngx_http_next_header_filter;\nstatic ngx_http_output_body_filter_pt    ngx_http_next_body_filter;\n\n\nstatic ngx_int_t\nngx_http_addition_header_filter(ngx_http_request_t *r)\n{\n    ngx_http_addition_ctx_t   *ctx;\n    ngx_http_addition_conf_t  *conf;\n\n    if (r->headers_out.status != NGX_HTTP_OK || r != r->main) {\n        return ngx_http_next_header_filter(r);\n    }\n\n    conf = ngx_http_get_module_loc_conf(r, ngx_http_addition_filter_module);\n\n    if (conf->before_body.len == 0 && conf->after_body.len == 0) {\n        return ngx_http_next_header_filter(r);\n    }\n\n    if (ngx_http_test_content_type(r, &conf->types) == NULL) {\n        return ngx_http_next_header_filter(r);\n    }\n\n    ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_addition_ctx_t));\n    if (ctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_http_set_ctx(r, ctx, ngx_http_addition_filter_module);\n\n    ngx_http_clear_content_length(r);\n    ngx_http_clear_accept_ranges(r);\n    ngx_http_weak_etag(r);\n\n    r->preserve_body = 1;\n\n    return ngx_http_next_header_filter(r);\n}\n\n\nstatic ngx_int_t\nngx_http_addition_body_filter(ngx_http_request_t *r, ngx_chain_t *in)\n{\n    ngx_int_t                  rc;\n    ngx_uint_t                 last;\n    ngx_chain_t               *cl;\n    ngx_http_request_t        *sr;\n    ngx_http_addition_ctx_t   *ctx;\n    ngx_http_addition_conf_t  *conf;\n\n    if (in == NULL || r->header_only) {\n        return ngx_http_next_body_filter(r, in);\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_addition_filter_module);\n\n    if (ctx == NULL) {\n        return ngx_http_next_body_filter(r, in);\n    }\n\n    conf = ngx_http_get_module_loc_conf(r, ngx_http_addition_filter_module);\n\n    if (!ctx->before_body_sent) {\n        ctx->before_body_sent = 1;\n\n        if (conf->before_body.len) {\n            if (ngx_http_subrequest(r, &conf->before_body, NULL, &sr, NULL, 0)\n                != NGX_OK)\n            {\n                return NGX_ERROR;\n            }\n        }\n    }\n\n    if (conf->after_body.len == 0) {\n        ngx_http_set_ctx(r, NULL, ngx_http_addition_filter_module);\n        return ngx_http_next_body_filter(r, in);\n    }\n\n    last = 0;\n\n    for (cl = in; cl; cl = cl->next) {\n        if (cl->buf->last_buf) {\n            cl->buf->last_buf = 0;\n            cl->buf->last_in_chain = 1;\n            cl->buf->sync = 1;\n            last = 1;\n        }\n    }\n\n    rc = ngx_http_next_body_filter(r, in);\n\n    if (rc == NGX_ERROR || !last || conf->after_body.len == 0) {\n        return rc;\n    }\n\n    if (ngx_http_subrequest(r, &conf->after_body, NULL, &sr, NULL, 0)\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    ngx_http_set_ctx(r, NULL, ngx_http_addition_filter_module);\n\n    return ngx_http_send_special(r, NGX_HTTP_LAST);\n}\n\n\nstatic ngx_int_t\nngx_http_addition_filter_init(ngx_conf_t *cf)\n{\n    ngx_http_next_header_filter = ngx_http_top_header_filter;\n    ngx_http_top_header_filter = ngx_http_addition_header_filter;\n\n    ngx_http_next_body_filter = ngx_http_top_body_filter;\n    ngx_http_top_body_filter = ngx_http_addition_body_filter;\n\n    return NGX_OK;\n}\n\n\nstatic void *\nngx_http_addition_create_conf(ngx_conf_t *cf)\n{\n    ngx_http_addition_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_addition_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc():\n     *\n     *     conf->before_body = { 0, NULL };\n     *     conf->after_body = { 0, NULL };\n     *     conf->types = { NULL };\n     *     conf->types_keys = NULL;\n     */\n\n    return conf;\n}\n\n\nstatic char *\nngx_http_addition_merge_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_http_addition_conf_t *prev = parent;\n    ngx_http_addition_conf_t *conf = child;\n\n    ngx_conf_merge_str_value(conf->before_body, prev->before_body, \"\");\n    ngx_conf_merge_str_value(conf->after_body, prev->after_body, \"\");\n\n    if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,\n                             &prev->types_keys, &prev->types,\n                             ngx_http_html_default_types)\n        != NGX_OK)\n    {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n"
  },
  {
    "path": "src/http/modules/ngx_http_auth_basic_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n#include <ngx_crypt.h>\n\n\n#define NGX_HTTP_AUTH_BUF_SIZE  2048\n\n\ntypedef struct {\n    ngx_http_complex_value_t  *realm;\n    ngx_http_complex_value_t  *user_file;\n} ngx_http_auth_basic_loc_conf_t;\n\n\nstatic ngx_int_t ngx_http_auth_basic_handler(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_auth_basic_crypt_handler(ngx_http_request_t *r,\n    ngx_str_t *passwd, ngx_str_t *realm);\nstatic ngx_int_t ngx_http_auth_basic_set_realm(ngx_http_request_t *r,\n    ngx_str_t *realm);\nstatic void *ngx_http_auth_basic_create_loc_conf(ngx_conf_t *cf);\nstatic char *ngx_http_auth_basic_merge_loc_conf(ngx_conf_t *cf,\n    void *parent, void *child);\nstatic ngx_int_t ngx_http_auth_basic_init(ngx_conf_t *cf);\nstatic char *ngx_http_auth_basic_user_file(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n\n\nstatic ngx_command_t  ngx_http_auth_basic_commands[] = {\n\n    { ngx_string(\"auth_basic\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF\n                        |NGX_CONF_TAKE1,\n      ngx_http_set_complex_value_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_auth_basic_loc_conf_t, realm),\n      NULL },\n\n    { ngx_string(\"auth_basic_user_file\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF\n                        |NGX_CONF_TAKE1,\n      ngx_http_auth_basic_user_file,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_auth_basic_loc_conf_t, user_file),\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_auth_basic_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    ngx_http_auth_basic_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    ngx_http_auth_basic_create_loc_conf,   /* create location configuration */\n    ngx_http_auth_basic_merge_loc_conf     /* merge location configuration */\n};\n\n\nngx_module_t  ngx_http_auth_basic_module = {\n    NGX_MODULE_V1,\n    &ngx_http_auth_basic_module_ctx,       /* module context */\n    ngx_http_auth_basic_commands,          /* 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_auth_basic_handler(ngx_http_request_t *r)\n{\n    off_t                            offset;\n    ssize_t                          n;\n    ngx_fd_t                         fd;\n    ngx_int_t                        rc;\n    ngx_err_t                        err;\n    ngx_str_t                        pwd, realm, user_file;\n    ngx_uint_t                       i, level, login, left, passwd;\n    ngx_file_t                       file;\n    ngx_http_auth_basic_loc_conf_t  *alcf;\n    u_char                           buf[NGX_HTTP_AUTH_BUF_SIZE];\n    enum {\n        sw_login,\n        sw_passwd,\n        sw_skip\n    } state;\n\n    alcf = ngx_http_get_module_loc_conf(r, ngx_http_auth_basic_module);\n\n    if (alcf->realm == NULL || alcf->user_file == NULL) {\n        return NGX_DECLINED;\n    }\n\n    if (ngx_http_complex_value(r, alcf->realm, &realm) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    if (realm.len == 3 && ngx_strncmp(realm.data, \"off\", 3) == 0) {\n        return NGX_DECLINED;\n    }\n\n    rc = ngx_http_auth_basic_user(r);\n\n    if (rc == NGX_DECLINED) {\n\n        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                      \"no user/password was provided for basic authentication\");\n\n        return ngx_http_auth_basic_set_realm(r, &realm);\n    }\n\n    if (rc == NGX_ERROR) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    if (ngx_http_complex_value(r, alcf->user_file, &user_file) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    fd = ngx_open_file(user_file.data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);\n\n    if (fd == NGX_INVALID_FILE) {\n        err = ngx_errno;\n\n        if (err == NGX_ENOENT) {\n            level = NGX_LOG_ERR;\n            rc = NGX_HTTP_FORBIDDEN;\n\n        } else {\n            level = NGX_LOG_CRIT;\n            rc = NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n\n        ngx_log_error(level, r->connection->log, err,\n                      ngx_open_file_n \" \\\"%s\\\" failed\", user_file.data);\n\n        return rc;\n    }\n\n    ngx_memzero(&file, sizeof(ngx_file_t));\n\n    file.fd = fd;\n    file.name = user_file;\n    file.log = r->connection->log;\n\n    state = sw_login;\n    passwd = 0;\n    login = 0;\n    left = 0;\n    offset = 0;\n\n    for ( ;; ) {\n        i = left;\n\n        n = ngx_read_file(&file, buf + left, NGX_HTTP_AUTH_BUF_SIZE - left,\n                          offset);\n\n        if (n == NGX_ERROR) {\n            rc = NGX_HTTP_INTERNAL_SERVER_ERROR;\n            goto cleanup;\n        }\n\n        if (n == 0) {\n            break;\n        }\n\n        for (i = left; i < left + n; i++) {\n            switch (state) {\n\n            case sw_login:\n                if (login == 0) {\n\n                    if (buf[i] == '#' || buf[i] == CR) {\n                        state = sw_skip;\n                        break;\n                    }\n\n                    if (buf[i] == LF) {\n                        break;\n                    }\n                }\n\n                if (buf[i] != r->headers_in.user.data[login]) {\n                    state = sw_skip;\n                    break;\n                }\n\n                if (login == r->headers_in.user.len) {\n                    state = sw_passwd;\n                    passwd = i + 1;\n                }\n\n                login++;\n\n                break;\n\n            case sw_passwd:\n                if (buf[i] == LF || buf[i] == CR || buf[i] == ':') {\n                    buf[i] = '\\0';\n\n                    pwd.len = i - passwd;\n                    pwd.data = &buf[passwd];\n\n                    rc = ngx_http_auth_basic_crypt_handler(r, &pwd, &realm);\n                    goto cleanup;\n                }\n\n                break;\n\n            case sw_skip:\n                if (buf[i] == LF) {\n                    state = sw_login;\n                    login = 0;\n                }\n\n                break;\n            }\n        }\n\n        if (state == sw_passwd) {\n            left = left + n - passwd;\n            ngx_memmove(buf, &buf[passwd], left);\n            passwd = 0;\n\n        } else {\n            left = 0;\n        }\n\n        offset += n;\n    }\n\n    if (state == sw_passwd) {\n        pwd.len = i - passwd;\n        pwd.data = ngx_pnalloc(r->pool, pwd.len + 1);\n        if (pwd.data == NULL) {\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n\n        ngx_cpystrn(pwd.data, &buf[passwd], pwd.len + 1);\n\n        rc = ngx_http_auth_basic_crypt_handler(r, &pwd, &realm);\n        goto cleanup;\n    }\n\n    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                  \"user \\\"%V\\\" was not found in \\\"%s\\\"\",\n                  &r->headers_in.user, user_file.data);\n\n    rc = ngx_http_auth_basic_set_realm(r, &realm);\n\ncleanup:\n\n    if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {\n        ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,\n                      ngx_close_file_n \" \\\"%s\\\" failed\", user_file.data);\n    }\n\n    ngx_explicit_memzero(buf, NGX_HTTP_AUTH_BUF_SIZE);\n\n    return rc;\n}\n\n\nstatic ngx_int_t\nngx_http_auth_basic_crypt_handler(ngx_http_request_t *r, ngx_str_t *passwd,\n    ngx_str_t *realm)\n{\n    ngx_int_t   rc;\n    u_char     *encrypted;\n\n    rc = ngx_crypt(r->pool, r->headers_in.passwd.data, passwd->data,\n                   &encrypted);\n\n    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"rc: %i user: \\\"%V\\\" salt: \\\"%s\\\"\",\n                   rc, &r->headers_in.user, passwd->data);\n\n    if (rc != NGX_OK) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    if (ngx_strcmp(encrypted, passwd->data) == 0) {\n        return NGX_OK;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"encrypted: \\\"%s\\\"\", encrypted);\n\n    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                  \"user \\\"%V\\\": password mismatch\",\n                  &r->headers_in.user);\n\n    return ngx_http_auth_basic_set_realm(r, realm);\n}\n\n\nstatic ngx_int_t\nngx_http_auth_basic_set_realm(ngx_http_request_t *r, ngx_str_t *realm)\n{\n    size_t   len;\n    u_char  *basic, *p;\n\n    r->headers_out.www_authenticate = ngx_list_push(&r->headers_out.headers);\n    if (r->headers_out.www_authenticate == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    len = sizeof(\"Basic realm=\\\"\\\"\") - 1 + realm->len;\n\n    basic = ngx_pnalloc(r->pool, len);\n    if (basic == NULL) {\n        r->headers_out.www_authenticate->hash = 0;\n        r->headers_out.www_authenticate = NULL;\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    p = ngx_cpymem(basic, \"Basic realm=\\\"\", sizeof(\"Basic realm=\\\"\") - 1);\n    p = ngx_cpymem(p, realm->data, realm->len);\n    *p = '\"';\n\n    r->headers_out.www_authenticate->hash = 1;\n    r->headers_out.www_authenticate->next = NULL;\n    ngx_str_set(&r->headers_out.www_authenticate->key, \"WWW-Authenticate\");\n    r->headers_out.www_authenticate->value.data = basic;\n    r->headers_out.www_authenticate->value.len = len;\n\n    return NGX_HTTP_UNAUTHORIZED;\n}\n\n\nstatic void *\nngx_http_auth_basic_create_loc_conf(ngx_conf_t *cf)\n{\n    ngx_http_auth_basic_loc_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_auth_basic_loc_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    conf->realm = NGX_CONF_UNSET_PTR;\n    conf->user_file = NGX_CONF_UNSET_PTR;\n\n    return conf;\n}\n\n\nstatic char *\nngx_http_auth_basic_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_http_auth_basic_loc_conf_t  *prev = parent;\n    ngx_http_auth_basic_loc_conf_t  *conf = child;\n\n    ngx_conf_merge_ptr_value(conf->realm, prev->realm, NULL);\n    ngx_conf_merge_ptr_value(conf->user_file, prev->user_file, NULL);\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_auth_basic_init(ngx_conf_t *cf)\n{\n    ngx_http_handler_pt        *h;\n    ngx_http_core_main_conf_t  *cmcf;\n\n    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);\n\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_auth_basic_handler;\n\n    return NGX_OK;\n}\n\n\nstatic char *\nngx_http_auth_basic_user_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_auth_basic_loc_conf_t *alcf = conf;\n\n    ngx_str_t                         *value;\n    ngx_http_compile_complex_value_t   ccv;\n\n    if (alcf->user_file != NGX_CONF_UNSET_PTR) {\n        return \"is duplicate\";\n    }\n\n    alcf->user_file = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));\n    if (alcf->user_file == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    value = cf->args->elts;\n\n    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n\n    ccv.cf = cf;\n    ccv.value = &value[1];\n    ccv.complex_value = alcf->user_file;\n    ccv.zero = 1;\n    ccv.conf_prefix = 1;\n\n    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n"
  },
  {
    "path": "src/http/modules/ngx_http_auth_request_module.c",
    "content": "\n/*\n * Copyright (C) Maxim Dounin\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\ntypedef struct {\n    ngx_str_t                 uri;\n    ngx_array_t              *vars;\n} ngx_http_auth_request_conf_t;\n\n\ntypedef struct {\n    ngx_uint_t                done;\n    ngx_uint_t                status;\n    ngx_http_request_t       *subrequest;\n} ngx_http_auth_request_ctx_t;\n\n\ntypedef struct {\n    ngx_int_t                 index;\n    ngx_http_complex_value_t  value;\n    ngx_http_set_variable_pt  set_handler;\n} ngx_http_auth_request_variable_t;\n\n\nstatic ngx_int_t ngx_http_auth_request_handler(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_auth_request_done(ngx_http_request_t *r,\n    void *data, ngx_int_t rc);\nstatic ngx_int_t ngx_http_auth_request_set_variables(ngx_http_request_t *r,\n    ngx_http_auth_request_conf_t *arcf, ngx_http_auth_request_ctx_t *ctx);\nstatic ngx_int_t ngx_http_auth_request_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic void *ngx_http_auth_request_create_conf(ngx_conf_t *cf);\nstatic char *ngx_http_auth_request_merge_conf(ngx_conf_t *cf,\n    void *parent, void *child);\nstatic ngx_int_t ngx_http_auth_request_init(ngx_conf_t *cf);\nstatic char *ngx_http_auth_request(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_http_auth_request_set(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n\n\nstatic ngx_command_t  ngx_http_auth_request_commands[] = {\n\n    { ngx_string(\"auth_request\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_http_auth_request,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"auth_request_set\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,\n      ngx_http_auth_request_set,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_auth_request_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    ngx_http_auth_request_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    ngx_http_auth_request_create_conf,     /* create location configuration */\n    ngx_http_auth_request_merge_conf       /* merge location configuration */\n};\n\n\nngx_module_t  ngx_http_auth_request_module = {\n    NGX_MODULE_V1,\n    &ngx_http_auth_request_module_ctx,     /* module context */\n    ngx_http_auth_request_commands,        /* 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_auth_request_handler(ngx_http_request_t *r)\n{\n    ngx_table_elt_t               *h, *ho, **ph;\n    ngx_http_request_t            *sr;\n    ngx_http_post_subrequest_t    *ps;\n    ngx_http_auth_request_ctx_t   *ctx;\n    ngx_http_auth_request_conf_t  *arcf;\n\n    arcf = ngx_http_get_module_loc_conf(r, ngx_http_auth_request_module);\n\n    if (arcf->uri.len == 0) {\n        return NGX_DECLINED;\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"auth request handler\");\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_auth_request_module);\n\n    if (ctx != NULL) {\n        if (!ctx->done) {\n            return NGX_AGAIN;\n        }\n\n        /*\n         * as soon as we are done - explicitly set variables to make\n         * sure they will be available after internal redirects\n         */\n\n        if (ngx_http_auth_request_set_variables(r, arcf, ctx) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        /* return appropriate status */\n\n        if (ctx->status == NGX_HTTP_FORBIDDEN) {\n            return ctx->status;\n        }\n\n        if (ctx->status == NGX_HTTP_UNAUTHORIZED) {\n            sr = ctx->subrequest;\n\n            h = sr->headers_out.www_authenticate;\n\n            if (!h && sr->upstream) {\n                h = sr->upstream->headers_in.www_authenticate;\n            }\n\n            ph = &r->headers_out.www_authenticate;\n\n            while (h) {\n                ho = ngx_list_push(&r->headers_out.headers);\n                if (ho == NULL) {\n                    return NGX_ERROR;\n                }\n\n                *ho = *h;\n                ho->next = NULL;\n\n                *ph = ho;\n                ph = &ho->next;\n\n                h = h->next;\n            }\n\n            return ctx->status;\n        }\n\n        if (ctx->status >= NGX_HTTP_OK\n            && ctx->status < NGX_HTTP_SPECIAL_RESPONSE)\n        {\n            return NGX_OK;\n        }\n\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"auth request unexpected status: %ui\", ctx->status);\n\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_auth_request_ctx_t));\n    if (ctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    ps = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t));\n    if (ps == NULL) {\n        return NGX_ERROR;\n    }\n\n    ps->handler = ngx_http_auth_request_done;\n    ps->data = ctx;\n\n    if (ngx_http_subrequest(r, &arcf->uri, NULL, &sr, ps,\n                            NGX_HTTP_SUBREQUEST_WAITED)\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    /*\n     * allocate fake request body to avoid attempts to read it and to make\n     * sure real body file (if already read) won't be closed by upstream\n     */\n\n    sr->request_body = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t));\n    if (sr->request_body == NULL) {\n        return NGX_ERROR;\n    }\n\n    sr->header_only = 1;\n\n    ctx->subrequest = sr;\n\n    ngx_http_set_ctx(r, ctx, ngx_http_auth_request_module);\n\n    return NGX_AGAIN;\n}\n\n\nstatic ngx_int_t\nngx_http_auth_request_done(ngx_http_request_t *r, void *data, ngx_int_t rc)\n{\n    ngx_http_auth_request_ctx_t   *ctx = data;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"auth request done s:%ui\", r->headers_out.status);\n\n    ctx->done = 1;\n    ctx->status = r->headers_out.status;\n\n    return rc;\n}\n\n\nstatic ngx_int_t\nngx_http_auth_request_set_variables(ngx_http_request_t *r,\n    ngx_http_auth_request_conf_t *arcf, ngx_http_auth_request_ctx_t *ctx)\n{\n    ngx_str_t                          val;\n    ngx_http_variable_t               *v;\n    ngx_http_variable_value_t         *vv;\n    ngx_http_auth_request_variable_t  *av, *last;\n    ngx_http_core_main_conf_t         *cmcf;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"auth request set variables\");\n\n    if (arcf->vars == NULL) {\n        return NGX_OK;\n    }\n\n    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);\n    v = cmcf->variables.elts;\n\n    av = arcf->vars->elts;\n    last = av + arcf->vars->nelts;\n\n    while (av < last) {\n        /*\n         * explicitly set new value to make sure it will be available after\n         * internal redirects\n         */\n\n        vv = &r->variables[av->index];\n\n        if (ngx_http_complex_value(ctx->subrequest, &av->value, &val)\n            != NGX_OK)\n        {\n            return NGX_ERROR;\n        }\n\n        vv->valid = 1;\n        vv->not_found = 0;\n        vv->data = val.data;\n        vv->len = val.len;\n\n        if (av->set_handler) {\n            /*\n             * set_handler only available in cmcf->variables_keys, so we store\n             * it explicitly\n             */\n\n            av->set_handler(r, vv, v[av->index].data);\n        }\n\n        av++;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_auth_request_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"auth request variable\");\n\n    v->not_found = 1;\n\n    return NGX_OK;\n}\n\n\nstatic void *\nngx_http_auth_request_create_conf(ngx_conf_t *cf)\n{\n    ngx_http_auth_request_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_auth_request_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc():\n     *\n     *     conf->uri = { 0, NULL };\n     */\n\n    conf->vars = NGX_CONF_UNSET_PTR;\n\n    return conf;\n}\n\n\nstatic char *\nngx_http_auth_request_merge_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_http_auth_request_conf_t *prev = parent;\n    ngx_http_auth_request_conf_t *conf = child;\n\n    ngx_conf_merge_str_value(conf->uri, prev->uri, \"\");\n    ngx_conf_merge_ptr_value(conf->vars, prev->vars, NULL);\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_auth_request_init(ngx_conf_t *cf)\n{\n    ngx_http_handler_pt        *h;\n    ngx_http_core_main_conf_t  *cmcf;\n\n    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);\n\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_auth_request_handler;\n\n    return NGX_OK;\n}\n\n\nstatic char *\nngx_http_auth_request(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_auth_request_conf_t *arcf = conf;\n\n    ngx_str_t        *value;\n\n    if (arcf->uri.data != NULL) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    if (ngx_strcmp(value[1].data, \"off\") == 0) {\n        arcf->uri.len = 0;\n        arcf->uri.data = (u_char *) \"\";\n\n        return NGX_CONF_OK;\n    }\n\n    arcf->uri = value[1];\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_auth_request_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_auth_request_conf_t *arcf = conf;\n\n    ngx_str_t                         *value;\n    ngx_http_variable_t               *v;\n    ngx_http_auth_request_variable_t  *av;\n    ngx_http_compile_complex_value_t   ccv;\n\n    value = cf->args->elts;\n\n    if (value[1].data[0] != '$') {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid variable name \\\"%V\\\"\", &value[1]);\n        return NGX_CONF_ERROR;\n    }\n\n    value[1].len--;\n    value[1].data++;\n\n    if (arcf->vars == NGX_CONF_UNSET_PTR) {\n        arcf->vars = ngx_array_create(cf->pool, 1,\n                                      sizeof(ngx_http_auth_request_variable_t));\n        if (arcf->vars == NULL) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    av = ngx_array_push(arcf->vars);\n    if (av == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    v = ngx_http_add_variable(cf, &value[1], NGX_HTTP_VAR_CHANGEABLE);\n    if (v == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    av->index = ngx_http_get_variable_index(cf, &value[1]);\n    if (av->index == NGX_ERROR) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (v->get_handler == NULL) {\n        v->get_handler = ngx_http_auth_request_variable;\n        v->data = (uintptr_t) av;\n    }\n\n    av->set_handler = v->set_handler;\n\n    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n\n    ccv.cf = cf;\n    ccv.value = &value[2];\n    ccv.complex_value = &av->value;\n\n    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n"
  },
  {
    "path": "src/http/modules/ngx_http_autoindex_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\n#if 0\n\ntypedef struct {\n    ngx_buf_t     *buf;\n    size_t         size;\n    ngx_pool_t    *pool;\n    size_t         alloc_size;\n    ngx_chain_t  **last_out;\n} ngx_http_autoindex_ctx_t;\n\n#endif\n\n\ntypedef struct {\n    ngx_str_t      name;\n    size_t         utf_len;\n    size_t         escape;\n    size_t         escape_html;\n\n    unsigned       dir:1;\n    unsigned       file:1;\n\n    time_t         mtime;\n    off_t          size;\n} ngx_http_autoindex_entry_t;\n\n\ntypedef struct {\n    ngx_flag_t     enable;\n    ngx_uint_t     format;\n    ngx_flag_t     localtime;\n    ngx_flag_t     exact_size;\n} ngx_http_autoindex_loc_conf_t;\n\n\n#define NGX_HTTP_AUTOINDEX_HTML         0\n#define NGX_HTTP_AUTOINDEX_JSON         1\n#define NGX_HTTP_AUTOINDEX_JSONP        2\n#define NGX_HTTP_AUTOINDEX_XML          3\n\n#define NGX_HTTP_AUTOINDEX_PREALLOCATE  50\n\n#define NGX_HTTP_AUTOINDEX_NAME_LEN     50\n\n\nstatic ngx_buf_t *ngx_http_autoindex_html(ngx_http_request_t *r,\n    ngx_array_t *entries);\nstatic ngx_buf_t *ngx_http_autoindex_json(ngx_http_request_t *r,\n    ngx_array_t *entries, ngx_str_t *callback);\nstatic ngx_int_t ngx_http_autoindex_jsonp_callback(ngx_http_request_t *r,\n    ngx_str_t *callback);\nstatic ngx_buf_t *ngx_http_autoindex_xml(ngx_http_request_t *r,\n    ngx_array_t *entries);\n\nstatic int ngx_libc_cdecl ngx_http_autoindex_cmp_entries(const void *one,\n    const void *two);\nstatic ngx_int_t ngx_http_autoindex_error(ngx_http_request_t *r,\n    ngx_dir_t *dir, ngx_str_t *name);\n\nstatic ngx_int_t ngx_http_autoindex_init(ngx_conf_t *cf);\nstatic void *ngx_http_autoindex_create_loc_conf(ngx_conf_t *cf);\nstatic char *ngx_http_autoindex_merge_loc_conf(ngx_conf_t *cf,\n    void *parent, void *child);\n\n\nstatic ngx_conf_enum_t  ngx_http_autoindex_format[] = {\n    { ngx_string(\"html\"), NGX_HTTP_AUTOINDEX_HTML },\n    { ngx_string(\"json\"), NGX_HTTP_AUTOINDEX_JSON },\n    { ngx_string(\"jsonp\"), NGX_HTTP_AUTOINDEX_JSONP },\n    { ngx_string(\"xml\"), NGX_HTTP_AUTOINDEX_XML },\n    { ngx_null_string, 0 }\n};\n\n\nstatic ngx_command_t  ngx_http_autoindex_commands[] = {\n\n    { ngx_string(\"autoindex\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_autoindex_loc_conf_t, enable),\n      NULL },\n\n    { ngx_string(\"autoindex_format\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_enum_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_autoindex_loc_conf_t, format),\n      &ngx_http_autoindex_format },\n\n    { ngx_string(\"autoindex_localtime\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_autoindex_loc_conf_t, localtime),\n      NULL },\n\n    { ngx_string(\"autoindex_exact_size\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_autoindex_loc_conf_t, exact_size),\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_autoindex_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    ngx_http_autoindex_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    ngx_http_autoindex_create_loc_conf,    /* create location configuration */\n    ngx_http_autoindex_merge_loc_conf      /* merge location configuration */\n};\n\n\nngx_module_t  ngx_http_autoindex_module = {\n    NGX_MODULE_V1,\n    &ngx_http_autoindex_module_ctx,        /* module context */\n    ngx_http_autoindex_commands,           /* 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_autoindex_handler(ngx_http_request_t *r)\n{\n    u_char                         *last, *filename;\n    size_t                          len, allocated, root;\n    ngx_err_t                       err;\n    ngx_buf_t                      *b;\n    ngx_int_t                       rc;\n    ngx_str_t                       path, callback;\n    ngx_dir_t                       dir;\n    ngx_uint_t                      level, format;\n    ngx_pool_t                     *pool;\n    ngx_chain_t                     out;\n    ngx_array_t                     entries;\n    ngx_http_autoindex_entry_t     *entry;\n    ngx_http_autoindex_loc_conf_t  *alcf;\n\n    if (r->uri.data[r->uri.len - 1] != '/') {\n        return NGX_DECLINED;\n    }\n\n    if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {\n        return NGX_DECLINED;\n    }\n\n    alcf = ngx_http_get_module_loc_conf(r, ngx_http_autoindex_module);\n\n    if (!alcf->enable) {\n        return NGX_DECLINED;\n    }\n\n    rc = ngx_http_discard_request_body(r);\n\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    last = ngx_http_map_uri_to_path(r, &path, &root,\n                                    NGX_HTTP_AUTOINDEX_PREALLOCATE);\n    if (last == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    allocated = path.len;\n    path.len = last - path.data;\n    if (path.len > 1) {\n        path.len--;\n    }\n    path.data[path.len] = '\\0';\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http autoindex: \\\"%s\\\"\", path.data);\n\n    format = alcf->format;\n\n    if (format == NGX_HTTP_AUTOINDEX_JSONP) {\n        if (ngx_http_autoindex_jsonp_callback(r, &callback) != NGX_OK) {\n            return NGX_HTTP_BAD_REQUEST;\n        }\n\n        if (callback.len == 0) {\n            format = NGX_HTTP_AUTOINDEX_JSON;\n        }\n    }\n\n    if (ngx_open_dir(&path, &dir) == NGX_ERROR) {\n        err = ngx_errno;\n\n        if (err == NGX_ENOENT\n            || err == NGX_ENOTDIR\n            || err == NGX_ENAMETOOLONG)\n        {\n            level = NGX_LOG_ERR;\n            rc = NGX_HTTP_NOT_FOUND;\n\n        } else if (err == NGX_EACCES) {\n            level = NGX_LOG_ERR;\n            rc = NGX_HTTP_FORBIDDEN;\n\n        } else {\n            level = NGX_LOG_CRIT;\n            rc = NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n\n        ngx_log_error(level, r->connection->log, err,\n                      ngx_open_dir_n \" \\\"%s\\\" failed\", path.data);\n\n        return rc;\n    }\n\n#if (NGX_SUPPRESS_WARN)\n\n    /* MSVC thinks 'entries' may be used without having been initialized */\n    ngx_memzero(&entries, sizeof(ngx_array_t));\n\n#endif\n\n    /* TODO: pool should be temporary pool */\n    pool = r->pool;\n\n    if (ngx_array_init(&entries, pool, 40, sizeof(ngx_http_autoindex_entry_t))\n        != NGX_OK)\n    {\n        return ngx_http_autoindex_error(r, &dir, &path);\n    }\n\n    r->headers_out.status = NGX_HTTP_OK;\n\n    switch (format) {\n\n    case NGX_HTTP_AUTOINDEX_JSON:\n        ngx_str_set(&r->headers_out.content_type, \"application/json\");\n        break;\n\n    case NGX_HTTP_AUTOINDEX_JSONP:\n        ngx_str_set(&r->headers_out.content_type, \"application/javascript\");\n        break;\n\n    case NGX_HTTP_AUTOINDEX_XML:\n        ngx_str_set(&r->headers_out.content_type, \"text/xml\");\n        ngx_str_set(&r->headers_out.charset, \"utf-8\");\n        break;\n\n    default: /* NGX_HTTP_AUTOINDEX_HTML */\n        ngx_str_set(&r->headers_out.content_type, \"text/html\");\n        break;\n    }\n\n    r->headers_out.content_type_len = r->headers_out.content_type.len;\n    r->headers_out.content_type_lowcase = NULL;\n\n    rc = ngx_http_send_header(r);\n\n    if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {\n        if (ngx_close_dir(&dir) == NGX_ERROR) {\n            ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,\n                          ngx_close_dir_n \" \\\"%V\\\" failed\", &path);\n        }\n\n        return rc;\n    }\n\n    filename = path.data;\n    filename[path.len] = '/';\n\n    for ( ;; ) {\n        ngx_set_errno(0);\n\n        if (ngx_read_dir(&dir) == NGX_ERROR) {\n            err = ngx_errno;\n\n            if (err != NGX_ENOMOREFILES) {\n                ngx_log_error(NGX_LOG_CRIT, r->connection->log, err,\n                              ngx_read_dir_n \" \\\"%V\\\" failed\", &path);\n                return ngx_http_autoindex_error(r, &dir, &path);\n            }\n\n            break;\n        }\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"http autoindex file: \\\"%s\\\"\", ngx_de_name(&dir));\n\n        len = ngx_de_namelen(&dir);\n\n        if (ngx_de_name(&dir)[0] == '.') {\n            continue;\n        }\n\n        if (!dir.valid_info) {\n\n            /* 1 byte for '/' and 1 byte for terminating '\\0' */\n\n            if (path.len + 1 + len + 1 > allocated) {\n                allocated = path.len + 1 + len + 1\n                                     + NGX_HTTP_AUTOINDEX_PREALLOCATE;\n\n                filename = ngx_pnalloc(pool, allocated);\n                if (filename == NULL) {\n                    return ngx_http_autoindex_error(r, &dir, &path);\n                }\n\n                last = ngx_cpystrn(filename, path.data, path.len + 1);\n                *last++ = '/';\n            }\n\n            ngx_cpystrn(last, ngx_de_name(&dir), len + 1);\n\n            if (ngx_de_info(filename, &dir) == NGX_FILE_ERROR) {\n                err = ngx_errno;\n\n                if (err != NGX_ENOENT && err != NGX_ELOOP) {\n                    ngx_log_error(NGX_LOG_CRIT, r->connection->log, err,\n                                  ngx_de_info_n \" \\\"%s\\\" failed\", filename);\n\n                    if (err == NGX_EACCES) {\n                        continue;\n                    }\n\n                    return ngx_http_autoindex_error(r, &dir, &path);\n                }\n\n                if (ngx_de_link_info(filename, &dir) == NGX_FILE_ERROR) {\n                    ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,\n                                  ngx_de_link_info_n \" \\\"%s\\\" failed\",\n                                  filename);\n                    return ngx_http_autoindex_error(r, &dir, &path);\n                }\n            }\n        }\n\n        entry = ngx_array_push(&entries);\n        if (entry == NULL) {\n            return ngx_http_autoindex_error(r, &dir, &path);\n        }\n\n        entry->name.len = len;\n\n        entry->name.data = ngx_pnalloc(pool, len + 1);\n        if (entry->name.data == NULL) {\n            return ngx_http_autoindex_error(r, &dir, &path);\n        }\n\n        ngx_cpystrn(entry->name.data, ngx_de_name(&dir), len + 1);\n\n        entry->dir = ngx_de_is_dir(&dir);\n        entry->file = ngx_de_is_file(&dir);\n        entry->mtime = ngx_de_mtime(&dir);\n        entry->size = ngx_de_size(&dir);\n    }\n\n    if (ngx_close_dir(&dir) == NGX_ERROR) {\n        ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,\n                      ngx_close_dir_n \" \\\"%V\\\" failed\", &path);\n    }\n\n    if (entries.nelts > 1) {\n        ngx_qsort(entries.elts, (size_t) entries.nelts,\n                  sizeof(ngx_http_autoindex_entry_t),\n                  ngx_http_autoindex_cmp_entries);\n    }\n\n    switch (format) {\n\n    case NGX_HTTP_AUTOINDEX_JSON:\n        b = ngx_http_autoindex_json(r, &entries, NULL);\n        break;\n\n    case NGX_HTTP_AUTOINDEX_JSONP:\n        b = ngx_http_autoindex_json(r, &entries, &callback);\n        break;\n\n    case NGX_HTTP_AUTOINDEX_XML:\n        b = ngx_http_autoindex_xml(r, &entries);\n        break;\n\n    default: /* NGX_HTTP_AUTOINDEX_HTML */\n        b = ngx_http_autoindex_html(r, &entries);\n        break;\n    }\n\n    if (b == NULL) {\n        return NGX_ERROR;\n    }\n\n    /* TODO: free temporary pool */\n\n    if (r == r->main) {\n        b->last_buf = 1;\n    }\n\n    b->last_in_chain = 1;\n\n    out.buf = b;\n    out.next = NULL;\n\n    return ngx_http_output_filter(r, &out);\n}\n\n\nstatic ngx_buf_t *\nngx_http_autoindex_html(ngx_http_request_t *r, ngx_array_t *entries)\n{\n    u_char                         *last, scale;\n    off_t                           length;\n    size_t                          len, entry_len, char_len, escape_html;\n    ngx_tm_t                        tm;\n    ngx_buf_t                      *b;\n    ngx_int_t                       size;\n    ngx_uint_t                      i, utf8;\n    ngx_time_t                     *tp;\n    ngx_http_autoindex_entry_t     *entry;\n    ngx_http_autoindex_loc_conf_t  *alcf;\n\n    static u_char  title[] =\n        \"<html>\" CRLF\n        \"<head><title>Index of \"\n    ;\n\n    static u_char  header[] =\n        \"</title></head>\" CRLF\n        \"<body>\" CRLF\n        \"<h1>Index of \"\n    ;\n\n    static u_char  tail[] =\n        \"</body>\" CRLF\n        \"</html>\" CRLF\n    ;\n\n    static char  *months[] = { \"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\",\n                               \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\" };\n\n    if (r->headers_out.charset.len == 5\n        && ngx_strncasecmp(r->headers_out.charset.data, (u_char *) \"utf-8\", 5)\n           == 0)\n    {\n        utf8 = 1;\n\n    } else {\n        utf8 = 0;\n    }\n\n    escape_html = ngx_escape_html(NULL, r->uri.data, r->uri.len);\n\n    len = sizeof(title) - 1\n          + r->uri.len + escape_html\n          + sizeof(header) - 1\n          + r->uri.len + escape_html\n          + sizeof(\"</h1>\") - 1\n          + sizeof(\"<hr><pre><a href=\\\"../\\\">../</a>\" CRLF) - 1\n          + sizeof(\"</pre><hr>\") - 1\n          + sizeof(tail) - 1;\n\n    entry = entries->elts;\n    for (i = 0; i < entries->nelts; i++) {\n        entry[i].escape = 2 * ngx_escape_uri(NULL, entry[i].name.data,\n                                             entry[i].name.len,\n                                             NGX_ESCAPE_URI_COMPONENT);\n\n        entry[i].escape_html = ngx_escape_html(NULL, entry[i].name.data,\n                                               entry[i].name.len);\n\n        if (utf8) {\n            entry[i].utf_len = ngx_utf8_length(entry[i].name.data,\n                                               entry[i].name.len);\n        } else {\n            entry[i].utf_len = entry[i].name.len;\n        }\n\n        entry_len = sizeof(\"<a href=\\\"\") - 1\n                  + entry[i].name.len + entry[i].escape\n                  + 1                                    /* 1 is for \"/\" */\n                  + sizeof(\"\\\">\") - 1\n                  + entry[i].name.len - entry[i].utf_len\n                  + entry[i].escape_html\n                  + NGX_HTTP_AUTOINDEX_NAME_LEN + sizeof(\"&gt;\") - 2\n                  + sizeof(\"</a>\") - 1\n                  + sizeof(\" 28-Sep-1970 12:00 \") - 1\n                  + 20                                   /* the file size */\n                  + 2;\n\n        if (len > NGX_MAX_SIZE_T_VALUE - entry_len) {\n            return NULL;\n        }\n\n        len += entry_len;\n    }\n\n    b = ngx_create_temp_buf(r->pool, len);\n    if (b == NULL) {\n        return NULL;\n    }\n\n    b->last = ngx_cpymem(b->last, title, sizeof(title) - 1);\n\n    if (escape_html) {\n        b->last = (u_char *) ngx_escape_html(b->last, r->uri.data, r->uri.len);\n        b->last = ngx_cpymem(b->last, header, sizeof(header) - 1);\n        b->last = (u_char *) ngx_escape_html(b->last, r->uri.data, r->uri.len);\n\n    } else {\n        b->last = ngx_cpymem(b->last, r->uri.data, r->uri.len);\n        b->last = ngx_cpymem(b->last, header, sizeof(header) - 1);\n        b->last = ngx_cpymem(b->last, r->uri.data, r->uri.len);\n    }\n\n    b->last = ngx_cpymem(b->last, \"</h1>\", sizeof(\"</h1>\") - 1);\n\n    b->last = ngx_cpymem(b->last, \"<hr><pre><a href=\\\"../\\\">../</a>\" CRLF,\n                         sizeof(\"<hr><pre><a href=\\\"../\\\">../</a>\" CRLF) - 1);\n\n    alcf = ngx_http_get_module_loc_conf(r, ngx_http_autoindex_module);\n    tp = ngx_timeofday();\n\n    for (i = 0; i < entries->nelts; i++) {\n        b->last = ngx_cpymem(b->last, \"<a href=\\\"\", sizeof(\"<a href=\\\"\") - 1);\n\n        if (entry[i].escape) {\n            ngx_escape_uri(b->last, entry[i].name.data, entry[i].name.len,\n                           NGX_ESCAPE_URI_COMPONENT);\n\n            b->last += entry[i].name.len + entry[i].escape;\n\n        } else {\n            b->last = ngx_cpymem(b->last, entry[i].name.data,\n                                 entry[i].name.len);\n        }\n\n        if (entry[i].dir) {\n            *b->last++ = '/';\n        }\n\n        *b->last++ = '\"';\n        *b->last++ = '>';\n\n        len = entry[i].utf_len;\n\n        if (entry[i].name.len != len) {\n            if (len > NGX_HTTP_AUTOINDEX_NAME_LEN) {\n                char_len = NGX_HTTP_AUTOINDEX_NAME_LEN - 3 + 1;\n\n            } else {\n                char_len = NGX_HTTP_AUTOINDEX_NAME_LEN + 1;\n            }\n\n            last = b->last;\n            b->last = ngx_utf8_cpystrn(b->last, entry[i].name.data,\n                                       char_len, entry[i].name.len + 1);\n\n            if (entry[i].escape_html) {\n                b->last = (u_char *) ngx_escape_html(last, entry[i].name.data,\n                                                     b->last - last);\n            }\n\n            last = b->last;\n\n        } else {\n            if (entry[i].escape_html) {\n                if (len > NGX_HTTP_AUTOINDEX_NAME_LEN) {\n                    char_len = NGX_HTTP_AUTOINDEX_NAME_LEN - 3;\n\n                } else {\n                    char_len = len;\n                }\n\n                b->last = (u_char *) ngx_escape_html(b->last,\n                                                  entry[i].name.data, char_len);\n                last = b->last;\n\n            } else {\n                b->last = ngx_cpystrn(b->last, entry[i].name.data,\n                                      NGX_HTTP_AUTOINDEX_NAME_LEN + 1);\n                last = b->last - 3;\n            }\n        }\n\n        if (len > NGX_HTTP_AUTOINDEX_NAME_LEN) {\n            b->last = ngx_cpymem(last, \"..&gt;</a>\", sizeof(\"..&gt;</a>\") - 1);\n\n        } else {\n            if (entry[i].dir && NGX_HTTP_AUTOINDEX_NAME_LEN - len > 0) {\n                *b->last++ = '/';\n                len++;\n            }\n\n            b->last = ngx_cpymem(b->last, \"</a>\", sizeof(\"</a>\") - 1);\n\n            if (NGX_HTTP_AUTOINDEX_NAME_LEN - len > 0) {\n                ngx_memset(b->last, ' ', NGX_HTTP_AUTOINDEX_NAME_LEN - len);\n                b->last += NGX_HTTP_AUTOINDEX_NAME_LEN - len;\n            }\n        }\n\n        *b->last++ = ' ';\n\n        ngx_gmtime(entry[i].mtime + tp->gmtoff * 60 * alcf->localtime, &tm);\n\n        b->last = ngx_sprintf(b->last, \"%02d-%s-%d %02d:%02d \",\n                              tm.ngx_tm_mday,\n                              months[tm.ngx_tm_mon - 1],\n                              tm.ngx_tm_year,\n                              tm.ngx_tm_hour,\n                              tm.ngx_tm_min);\n\n        if (alcf->exact_size) {\n            if (entry[i].dir) {\n                b->last = ngx_cpymem(b->last,  \"                  -\",\n                                     sizeof(\"                  -\") - 1);\n            } else {\n                b->last = ngx_sprintf(b->last, \"%19O\", entry[i].size);\n            }\n\n        } else {\n            if (entry[i].dir) {\n                b->last = ngx_cpymem(b->last,  \"      -\",\n                                     sizeof(\"      -\") - 1);\n\n            } else {\n                length = entry[i].size;\n\n                if (length > 1024 * 1024 * 1024 - 1) {\n                    size = (ngx_int_t) (length / (1024 * 1024 * 1024));\n                    if ((length % (1024 * 1024 * 1024))\n                                                > (1024 * 1024 * 1024 / 2 - 1))\n                    {\n                        size++;\n                    }\n                    scale = 'G';\n\n                } else if (length > 1024 * 1024 - 1) {\n                    size = (ngx_int_t) (length / (1024 * 1024));\n                    if ((length % (1024 * 1024)) > (1024 * 1024 / 2 - 1)) {\n                        size++;\n                    }\n                    scale = 'M';\n\n                } else if (length > 9999) {\n                    size = (ngx_int_t) (length / 1024);\n                    if (length % 1024 > 511) {\n                        size++;\n                    }\n                    scale = 'K';\n\n                } else {\n                    size = (ngx_int_t) length;\n                    scale = '\\0';\n                }\n\n                if (scale) {\n                    b->last = ngx_sprintf(b->last, \"%6i%c\", size, scale);\n\n                } else {\n                    b->last = ngx_sprintf(b->last, \" %6i\", size);\n                }\n            }\n        }\n\n        *b->last++ = CR;\n        *b->last++ = LF;\n    }\n\n    b->last = ngx_cpymem(b->last, \"</pre><hr>\", sizeof(\"</pre><hr>\") - 1);\n\n    b->last = ngx_cpymem(b->last, tail, sizeof(tail) - 1);\n\n    return b;\n}\n\n\nstatic ngx_buf_t *\nngx_http_autoindex_json(ngx_http_request_t *r, ngx_array_t *entries,\n    ngx_str_t *callback)\n{\n    size_t                       len, entry_len;\n    ngx_buf_t                   *b;\n    ngx_uint_t                   i;\n    ngx_http_autoindex_entry_t  *entry;\n\n    len = sizeof(\"[\" CRLF CRLF \"]\") - 1;\n\n    if (callback) {\n        len += sizeof(\"/* callback */\" CRLF \"();\") - 1 + callback->len;\n    }\n\n    entry = entries->elts;\n\n    for (i = 0; i < entries->nelts; i++) {\n        entry[i].escape = ngx_escape_json(NULL, entry[i].name.data,\n                                          entry[i].name.len);\n\n        entry_len = sizeof(\"{  },\" CRLF) - 1\n                  + sizeof(\"\\\"name\\\":\\\"\\\"\") - 1\n                  + entry[i].name.len + entry[i].escape\n                  + sizeof(\", \\\"type\\\":\\\"directory\\\"\") - 1\n                  + sizeof(\", \\\"mtime\\\":\\\"Wed, 31 Dec 1986 10:00:00 GMT\\\"\") - 1;\n\n        if (entry[i].file) {\n            entry_len += sizeof(\", \\\"size\\\":\") - 1 + NGX_OFF_T_LEN;\n        }\n\n        if (len > NGX_MAX_SIZE_T_VALUE - entry_len) {\n            return NULL;\n        }\n\n        len += entry_len;\n    }\n\n    b = ngx_create_temp_buf(r->pool, len);\n    if (b == NULL) {\n        return NULL;\n    }\n\n    if (callback) {\n        b->last = ngx_cpymem(b->last, \"/* callback */\" CRLF,\n                             sizeof(\"/* callback */\" CRLF) - 1);\n\n        b->last = ngx_cpymem(b->last, callback->data, callback->len);\n\n        *b->last++ = '(';\n    }\n\n    *b->last++ = '[';\n\n    for (i = 0; i < entries->nelts; i++) {\n        b->last = ngx_cpymem(b->last, CRLF \"{ \\\"name\\\":\\\"\",\n                             sizeof(CRLF \"{ \\\"name\\\":\\\"\") - 1);\n\n        if (entry[i].escape) {\n            b->last = (u_char *) ngx_escape_json(b->last, entry[i].name.data,\n                                                 entry[i].name.len);\n        } else {\n            b->last = ngx_cpymem(b->last, entry[i].name.data,\n                                 entry[i].name.len);\n        }\n\n        b->last = ngx_cpymem(b->last, \"\\\", \\\"type\\\":\\\"\",\n                             sizeof(\"\\\", \\\"type\\\":\\\"\") - 1);\n\n        if (entry[i].dir) {\n            b->last = ngx_cpymem(b->last, \"directory\", sizeof(\"directory\") - 1);\n\n        } else if (entry[i].file) {\n            b->last = ngx_cpymem(b->last, \"file\", sizeof(\"file\") - 1);\n\n        } else {\n            b->last = ngx_cpymem(b->last, \"other\", sizeof(\"other\") - 1);\n        }\n\n        b->last = ngx_cpymem(b->last, \"\\\", \\\"mtime\\\":\\\"\",\n                             sizeof(\"\\\", \\\"mtime\\\":\\\"\") - 1);\n\n        b->last = ngx_http_time(b->last, entry[i].mtime);\n\n        if (entry[i].file) {\n            b->last = ngx_cpymem(b->last, \"\\\", \\\"size\\\":\",\n                                 sizeof(\"\\\", \\\"size\\\":\") - 1);\n            b->last = ngx_sprintf(b->last, \"%O\", entry[i].size);\n\n        } else {\n            *b->last++ = '\"';\n        }\n\n        b->last = ngx_cpymem(b->last, \" },\", sizeof(\" },\") - 1);\n    }\n\n    if (i > 0) {\n        b->last--;  /* strip last comma */\n    }\n\n    b->last = ngx_cpymem(b->last, CRLF \"]\", sizeof(CRLF \"]\") - 1);\n\n    if (callback) {\n        *b->last++ = ')'; *b->last++ = ';';\n    }\n\n    return b;\n}\n\n\nstatic ngx_int_t\nngx_http_autoindex_jsonp_callback(ngx_http_request_t *r, ngx_str_t *callback)\n{\n    u_char      *p, c, ch;\n    ngx_uint_t   i;\n\n    if (ngx_http_arg(r, (u_char *) \"callback\", 8, callback) != NGX_OK) {\n        callback->len = 0;\n        return NGX_OK;\n    }\n\n    if (callback->len > 128) {\n        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                      \"client sent too long callback name: \\\"%V\\\"\", callback);\n        return NGX_DECLINED;\n    }\n\n    p = callback->data;\n\n    for (i = 0; i < callback->len; i++) {\n        ch = p[i];\n\n        c = (u_char) (ch | 0x20);\n        if (c >= 'a' && c <= 'z') {\n            continue;\n        }\n\n        if ((ch >= '0' && ch <= '9') || ch == '_' || ch == '.') {\n            continue;\n        }\n\n        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                      \"client sent invalid callback name: \\\"%V\\\"\", callback);\n\n        return NGX_DECLINED;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_buf_t *\nngx_http_autoindex_xml(ngx_http_request_t *r, ngx_array_t *entries)\n{\n    size_t                          len, entry_len;\n    ngx_tm_t                        tm;\n    ngx_buf_t                      *b;\n    ngx_str_t                       type;\n    ngx_uint_t                      i;\n    ngx_http_autoindex_entry_t     *entry;\n\n    static u_char  head[] = \"<?xml version=\\\"1.0\\\"?>\" CRLF \"<list>\" CRLF;\n    static u_char  tail[] = \"</list>\" CRLF;\n\n    len = sizeof(head) - 1 + sizeof(tail) - 1;\n\n    entry = entries->elts;\n\n    for (i = 0; i < entries->nelts; i++) {\n        entry[i].escape = ngx_escape_html(NULL, entry[i].name.data,\n                                          entry[i].name.len);\n\n        entry_len = sizeof(\"<directory></directory>\" CRLF) - 1\n                  + entry[i].name.len + entry[i].escape\n                  + sizeof(\" mtime=\\\"1986-12-31T10:00:00Z\\\"\") - 1;\n\n        if (entry[i].file) {\n            entry_len += sizeof(\" size=\\\"\\\"\") - 1 + NGX_OFF_T_LEN;\n        }\n\n        if (len > NGX_MAX_SIZE_T_VALUE - entry_len) {\n            return NULL;\n        }\n\n        len += entry_len;\n    }\n\n    b = ngx_create_temp_buf(r->pool, len);\n    if (b == NULL) {\n        return NULL;\n    }\n\n    b->last = ngx_cpymem(b->last, head, sizeof(head) - 1);\n\n    for (i = 0; i < entries->nelts; i++) {\n        *b->last++ = '<';\n\n        if (entry[i].dir) {\n            ngx_str_set(&type, \"directory\");\n\n        } else if (entry[i].file) {\n            ngx_str_set(&type, \"file\");\n\n        } else {\n            ngx_str_set(&type, \"other\");\n        }\n\n        b->last = ngx_cpymem(b->last, type.data, type.len);\n\n        b->last = ngx_cpymem(b->last, \" mtime=\\\"\", sizeof(\" mtime=\\\"\") - 1);\n\n        ngx_gmtime(entry[i].mtime, &tm);\n\n        b->last = ngx_sprintf(b->last, \"%4d-%02d-%02dT%02d:%02d:%02dZ\",\n                              tm.ngx_tm_year, tm.ngx_tm_mon,\n                              tm.ngx_tm_mday, tm.ngx_tm_hour,\n                              tm.ngx_tm_min, tm.ngx_tm_sec);\n\n        if (entry[i].file) {\n            b->last = ngx_cpymem(b->last, \"\\\" size=\\\"\",\n                                 sizeof(\"\\\" size=\\\"\") - 1);\n            b->last = ngx_sprintf(b->last, \"%O\", entry[i].size);\n        }\n\n        *b->last++ = '\"'; *b->last++ = '>';\n\n        if (entry[i].escape) {\n            b->last = (u_char *) ngx_escape_html(b->last, entry[i].name.data,\n                                                 entry[i].name.len);\n        } else {\n            b->last = ngx_cpymem(b->last, entry[i].name.data,\n                                 entry[i].name.len);\n        }\n\n        *b->last++ = '<'; *b->last++ = '/';\n\n        b->last = ngx_cpymem(b->last, type.data, type.len);\n\n        *b->last++ = '>';\n\n        *b->last++ = CR; *b->last++ = LF;\n    }\n\n    b->last = ngx_cpymem(b->last, tail, sizeof(tail) - 1);\n\n    return b;\n}\n\n\nstatic int ngx_libc_cdecl\nngx_http_autoindex_cmp_entries(const void *one, const void *two)\n{\n    ngx_http_autoindex_entry_t *first = (ngx_http_autoindex_entry_t *) one;\n    ngx_http_autoindex_entry_t *second = (ngx_http_autoindex_entry_t *) two;\n\n    if (first->dir && !second->dir) {\n        /* move the directories to the start */\n        return -1;\n    }\n\n    if (!first->dir && second->dir) {\n        /* move the directories to the start */\n        return 1;\n    }\n\n    return (int) ngx_strcmp(first->name.data, second->name.data);\n}\n\n\n#if 0\n\nstatic ngx_buf_t *\nngx_http_autoindex_alloc(ngx_http_autoindex_ctx_t *ctx, size_t size)\n{\n    ngx_chain_t  *cl;\n\n    if (ctx->buf) {\n\n        if ((size_t) (ctx->buf->end - ctx->buf->last) >= size) {\n            return ctx->buf;\n        }\n\n        ctx->size += ctx->buf->last - ctx->buf->pos;\n    }\n\n    ctx->buf = ngx_create_temp_buf(ctx->pool, ctx->alloc_size);\n    if (ctx->buf == NULL) {\n        return NULL;\n    }\n\n    cl = ngx_alloc_chain_link(ctx->pool);\n    if (cl == NULL) {\n        return NULL;\n    }\n\n    cl->buf = ctx->buf;\n    cl->next = NULL;\n\n    *ctx->last_out = cl;\n    ctx->last_out = &cl->next;\n\n    return ctx->buf;\n}\n\n#endif\n\n\nstatic ngx_int_t\nngx_http_autoindex_error(ngx_http_request_t *r, ngx_dir_t *dir, ngx_str_t *name)\n{\n    if (ngx_close_dir(dir) == NGX_ERROR) {\n        ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,\n                      ngx_close_dir_n \" \\\"%V\\\" failed\", name);\n    }\n\n    return r->header_sent ? NGX_ERROR : NGX_HTTP_INTERNAL_SERVER_ERROR;\n}\n\n\nstatic void *\nngx_http_autoindex_create_loc_conf(ngx_conf_t *cf)\n{\n    ngx_http_autoindex_loc_conf_t  *conf;\n\n    conf = ngx_palloc(cf->pool, sizeof(ngx_http_autoindex_loc_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    conf->enable = NGX_CONF_UNSET;\n    conf->format = NGX_CONF_UNSET_UINT;\n    conf->localtime = NGX_CONF_UNSET;\n    conf->exact_size = NGX_CONF_UNSET;\n\n    return conf;\n}\n\n\nstatic char *\nngx_http_autoindex_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_http_autoindex_loc_conf_t *prev = parent;\n    ngx_http_autoindex_loc_conf_t *conf = child;\n\n    ngx_conf_merge_value(conf->enable, prev->enable, 0);\n    ngx_conf_merge_uint_value(conf->format, prev->format,\n                              NGX_HTTP_AUTOINDEX_HTML);\n    ngx_conf_merge_value(conf->localtime, prev->localtime, 0);\n    ngx_conf_merge_value(conf->exact_size, prev->exact_size, 1);\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_autoindex_init(ngx_conf_t *cf)\n{\n    ngx_http_handler_pt        *h;\n    ngx_http_core_main_conf_t  *cmcf;\n\n    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);\n\n    h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);\n    if (h == NULL) {\n        return NGX_ERROR;\n    }\n\n    *h = ngx_http_autoindex_handler;\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "src/http/modules/ngx_http_browser_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\n/*\n * The module can check browser versions conforming to the following formats:\n * X, X.X, X.X.X, and X.X.X.X.  The maximum values of each format may be\n * 4000, 4000.99, 4000.99.99, and 4000.99.99.99.\n */\n\n\n#define  NGX_HTTP_MODERN_BROWSER   0\n#define  NGX_HTTP_ANCIENT_BROWSER  1\n\n\ntypedef struct {\n    u_char                      browser[12];\n    size_t                      skip;\n    size_t                      add;\n    u_char                      name[12];\n} ngx_http_modern_browser_mask_t;\n\n\ntypedef struct {\n    ngx_uint_t                  version;\n    size_t                      skip;\n    size_t                      add;\n    u_char                      name[12];\n} ngx_http_modern_browser_t;\n\n\ntypedef struct {\n    ngx_array_t                *modern_browsers;\n    ngx_array_t                *ancient_browsers;\n    ngx_http_variable_value_t  *modern_browser_value;\n    ngx_http_variable_value_t  *ancient_browser_value;\n\n    unsigned                    modern_unlisted_browsers:1;\n    unsigned                    netscape4:1;\n} ngx_http_browser_conf_t;\n\n\nstatic ngx_int_t ngx_http_msie_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_browser_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\n\nstatic ngx_uint_t ngx_http_browser(ngx_http_request_t *r,\n    ngx_http_browser_conf_t *cf);\n\nstatic ngx_int_t ngx_http_browser_add_variables(ngx_conf_t *cf);\nstatic void *ngx_http_browser_create_conf(ngx_conf_t *cf);\nstatic char *ngx_http_browser_merge_conf(ngx_conf_t *cf, void *parent,\n    void *child);\nstatic int ngx_libc_cdecl ngx_http_modern_browser_sort(const void *one,\n    const void *two);\nstatic char *ngx_http_modern_browser(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_http_ancient_browser(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_http_modern_browser_value(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_http_ancient_browser_value(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n\n\nstatic ngx_command_t  ngx_http_browser_commands[] = {\n\n    { ngx_string(\"modern_browser\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,\n      ngx_http_modern_browser,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"ancient_browser\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,\n      ngx_http_ancient_browser,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"modern_browser_value\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_http_modern_browser_value,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"ancient_browser_value\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_http_ancient_browser_value,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_browser_module_ctx = {\n    ngx_http_browser_add_variables,        /* preconfiguration */\n    NULL,                                  /* 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    ngx_http_browser_create_conf,          /* create location configuration */\n    ngx_http_browser_merge_conf            /* merge location configuration */\n};\n\n\nngx_module_t  ngx_http_browser_module = {\n    NGX_MODULE_V1,\n    &ngx_http_browser_module_ctx,          /* module context */\n    ngx_http_browser_commands,             /* 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_http_modern_browser_mask_t  ngx_http_modern_browser_masks[] = {\n\n    /* Opera must be the first browser to check */\n\n    /*\n     * \"Opera/7.50 (X11; FreeBSD i386; U)  [en]\"\n     * \"Mozilla/5.0 (X11; FreeBSD i386; U) Opera 7.50  [en]\"\n     * \"Mozilla/4.0 (compatible; MSIE 6.0; X11; FreeBSD i386) Opera 7.50  [en]\"\n     * \"Opera/8.0 (Windows NT 5.1; U; ru)\"\n     * \"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0; en) Opera 8.0\"\n     * \"Opera/9.01 (X11; FreeBSD 6 i386; U; en)\"\n     */\n\n    { \"opera\",\n      0,\n      sizeof(\"Opera \") - 1,\n      \"Opera\"},\n\n    /* \"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)\" */\n\n    { \"msie\",\n      sizeof(\"Mozilla/4.0 (compatible; \") - 1,\n      sizeof(\"MSIE \") - 1,\n      \"MSIE \"},\n\n    /*\n     * \"Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.0.0) Gecko/20020610\"\n     * \"Mozilla/5.0 (Windows; U; Windows NT 5.1; ru-RU; rv:1.5) Gecko/20031006\"\n     * \"Mozilla/5.0 (Windows; U; Windows NT 5.1; ru-RU; rv:1.6) Gecko/20040206\n     *              Firefox/0.8\"\n     * \"Mozilla/5.0 (Windows; U; Windows NT 5.1; ru-RU; rv:1.7.8)\n     *              Gecko/20050511 Firefox/1.0.4\"\n     * \"Mozilla/5.0 (X11; U; FreeBSD i386; en-US; rv:1.8.0.5) Gecko/20060729\n     *              Firefox/1.5.0.5\"\n     */\n\n    { \"gecko\",\n      sizeof(\"Mozilla/5.0 (\") - 1,\n      sizeof(\"rv:\") - 1,\n      \"rv:\"},\n\n    /*\n     * \"Mozilla/5.0 (Macintosh; U; PPC Mac OS X; ru-ru) AppleWebKit/125.2\n     *              (KHTML, like Gecko) Safari/125.7\"\n     * \"Mozilla/5.0 (SymbianOS/9.1; U; en-us) AppleWebKit/413\n     *              (KHTML, like Gecko) Safari/413\"\n     * \"Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/418\n     *              (KHTML, like Gecko) Safari/417.9.3\"\n     * \"Mozilla/5.0 (Macintosh; U; PPC Mac OS X; ru-ru) AppleWebKit/418.8\n     *              (KHTML, like Gecko) Safari/419.3\"\n     */\n\n    { \"safari\",\n      sizeof(\"Mozilla/5.0 (\") - 1,\n      sizeof(\"Safari/\") - 1,\n      \"Safari/\"},\n\n    /*\n     * \"Mozilla/5.0 (compatible; Konqueror/3.1; Linux)\"\n     * \"Mozilla/5.0 (compatible; Konqueror/3.4; Linux) KHTML/3.4.2 (like Gecko)\"\n     * \"Mozilla/5.0 (compatible; Konqueror/3.5; FreeBSD) KHTML/3.5.1\n     *              (like Gecko)\"\n     */\n\n    { \"konqueror\",\n      sizeof(\"Mozilla/5.0 (compatible; \") - 1,\n      sizeof(\"Konqueror/\") - 1,\n      \"Konqueror/\"},\n\n    { \"\", 0, 0, \"\" }\n\n};\n\n\nstatic ngx_http_variable_t  ngx_http_browser_vars[] = {\n\n    { ngx_string(\"msie\"), NULL, ngx_http_msie_variable,\n      0, NGX_HTTP_VAR_CHANGEABLE, 0 },\n\n    { ngx_string(\"modern_browser\"), NULL, ngx_http_browser_variable,\n      NGX_HTTP_MODERN_BROWSER, NGX_HTTP_VAR_CHANGEABLE, 0 },\n\n    { ngx_string(\"ancient_browser\"), NULL, ngx_http_browser_variable,\n      NGX_HTTP_ANCIENT_BROWSER, NGX_HTTP_VAR_CHANGEABLE, 0 },\n\n      ngx_http_null_variable\n};\n\n\nstatic ngx_int_t\nngx_http_browser_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,\n    uintptr_t data)\n{\n    ngx_uint_t                rc;\n    ngx_http_browser_conf_t  *cf;\n\n    cf = ngx_http_get_module_loc_conf(r, ngx_http_browser_module);\n\n    rc = ngx_http_browser(r, cf);\n\n    if (data == NGX_HTTP_MODERN_BROWSER && rc == NGX_HTTP_MODERN_BROWSER) {\n        *v = *cf->modern_browser_value;\n        return NGX_OK;\n    }\n\n    if (data == NGX_HTTP_ANCIENT_BROWSER && rc == NGX_HTTP_ANCIENT_BROWSER) {\n        *v = *cf->ancient_browser_value;\n        return NGX_OK;\n    }\n\n    *v = ngx_http_variable_null_value;\n    return NGX_OK;\n}\n\n\nstatic ngx_uint_t\nngx_http_browser(ngx_http_request_t *r, ngx_http_browser_conf_t *cf)\n{\n    size_t                      len;\n    u_char                     *name, *ua, *last, c;\n    ngx_str_t                  *ancient;\n    ngx_uint_t                  i, version, ver, scale;\n    ngx_http_modern_browser_t  *modern;\n\n    if (r->headers_in.user_agent == NULL) {\n        if (cf->modern_unlisted_browsers) {\n            return NGX_HTTP_MODERN_BROWSER;\n        }\n\n        return NGX_HTTP_ANCIENT_BROWSER;\n    }\n\n    ua = r->headers_in.user_agent->value.data;\n    len = r->headers_in.user_agent->value.len;\n    last = ua + len;\n\n    if (cf->modern_browsers) {\n        modern = cf->modern_browsers->elts;\n\n        for (i = 0; i < cf->modern_browsers->nelts; i++) {\n            name = ua + modern[i].skip;\n\n            if (name >= last) {\n                continue;\n            }\n\n            name = (u_char *) ngx_strstr(name, modern[i].name);\n\n            if (name == NULL) {\n                continue;\n            }\n\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"browser: \\\"%s\\\"\", name);\n\n            name += modern[i].add;\n\n            if (name >= last) {\n                continue;\n            }\n\n            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"version: \\\"%ui\\\" \\\"%s\\\"\", modern[i].version, name);\n\n            version = 0;\n            ver = 0;\n            scale = 1000000;\n\n            while (name < last) {\n\n                c = *name++;\n\n                if (c >= '0' && c <= '9') {\n                    ver = ver * 10 + (c - '0');\n                    continue;\n                }\n\n                if (c == '.') {\n                    version += ver * scale;\n\n                    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                                   \"version: \\\"%ui\\\" \\\"%ui\\\"\",\n                                   modern[i].version, version);\n\n                    if (version > modern[i].version) {\n                        return NGX_HTTP_MODERN_BROWSER;\n                    }\n\n                    ver = 0;\n                    scale /= 100;\n                    continue;\n                }\n\n                break;\n            }\n\n            version += ver * scale;\n\n            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"version: \\\"%ui\\\" \\\"%ui\\\"\",\n                           modern[i].version, version);\n\n            if (version >= modern[i].version) {\n                return NGX_HTTP_MODERN_BROWSER;\n            }\n\n            return NGX_HTTP_ANCIENT_BROWSER;\n        }\n\n        if (!cf->modern_unlisted_browsers) {\n            return NGX_HTTP_ANCIENT_BROWSER;\n        }\n    }\n\n    if (cf->netscape4) {\n        if (len > sizeof(\"Mozilla/4.72 \") - 1\n            && ngx_strncmp(ua, \"Mozilla/\", sizeof(\"Mozilla/\") - 1) == 0\n            && ua[8] > '0' && ua[8] < '5')\n        {\n            return NGX_HTTP_ANCIENT_BROWSER;\n        }\n    }\n\n    if (cf->ancient_browsers) {\n        ancient = cf->ancient_browsers->elts;\n\n        for (i = 0; i < cf->ancient_browsers->nelts; i++) {\n            if (len >= ancient[i].len\n                && ngx_strstr(ua, ancient[i].data) != NULL)\n            {\n                return NGX_HTTP_ANCIENT_BROWSER;\n            }\n        }\n    }\n\n    if (cf->modern_unlisted_browsers) {\n        return NGX_HTTP_MODERN_BROWSER;\n    }\n\n    return NGX_HTTP_ANCIENT_BROWSER;\n}\n\n\nstatic ngx_int_t\nngx_http_msie_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,\n    uintptr_t data)\n{\n    if (r->headers_in.msie) {\n        *v = ngx_http_variable_true_value;\n        return NGX_OK;\n    }\n\n    *v = ngx_http_variable_null_value;\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_browser_add_variables(ngx_conf_t *cf)\n{\n    ngx_http_variable_t  *var, *v;\n\n    for (v = ngx_http_browser_vars; v->name.len; v++) {\n\n        var = ngx_http_add_variable(cf, &v->name, v->flags);\n        if (var == NULL) {\n            return NGX_ERROR;\n        }\n\n        var->get_handler = v->get_handler;\n        var->data = v->data;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void *\nngx_http_browser_create_conf(ngx_conf_t *cf)\n{\n    ngx_http_browser_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_browser_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc():\n     *\n     *     conf->modern_browsers = NULL;\n     *     conf->ancient_browsers = NULL;\n     *     conf->modern_browser_value = NULL;\n     *     conf->ancient_browser_value = NULL;\n     *\n     *     conf->modern_unlisted_browsers = 0;\n     *     conf->netscape4 = 0;\n     */\n\n    return conf;\n}\n\n\nstatic char *\nngx_http_browser_merge_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_http_browser_conf_t *prev = parent;\n    ngx_http_browser_conf_t *conf = child;\n\n    ngx_uint_t                  i, n;\n    ngx_http_modern_browser_t  *browsers, *opera;\n\n    /*\n     * At the merge the skip field is used to store the browser slot,\n     * it will be used in sorting and then will overwritten\n     * with a real skip value.  The zero value means Opera.\n     */\n\n    if (conf->modern_browsers == NULL && conf->modern_unlisted_browsers == 0) {\n        conf->modern_browsers = prev->modern_browsers;\n        conf->modern_unlisted_browsers = prev->modern_unlisted_browsers;\n\n    } else if (conf->modern_browsers != NULL) {\n        browsers = conf->modern_browsers->elts;\n\n        for (i = 0; i < conf->modern_browsers->nelts; i++) {\n            if (browsers[i].skip == 0) {\n                goto found;\n            }\n        }\n\n        /*\n         * Opera may contain MSIE string, so if Opera was not enumerated\n         * as modern browsers, then add it and set a unreachable version\n         */\n\n        opera = ngx_array_push(conf->modern_browsers);\n        if (opera == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        opera->skip = 0;\n        opera->version = 4001000000U;\n\n        browsers = conf->modern_browsers->elts;\n\nfound:\n\n        ngx_qsort(browsers, (size_t) conf->modern_browsers->nelts,\n                  sizeof(ngx_http_modern_browser_t),\n                  ngx_http_modern_browser_sort);\n\n        for (i = 0; i < conf->modern_browsers->nelts; i++) {\n             n = browsers[i].skip;\n\n             browsers[i].skip = ngx_http_modern_browser_masks[n].skip;\n             browsers[i].add = ngx_http_modern_browser_masks[n].add;\n             (void) ngx_cpystrn(browsers[i].name,\n                                ngx_http_modern_browser_masks[n].name, 12);\n        }\n    }\n\n    if (conf->ancient_browsers == NULL && conf->netscape4 == 0) {\n        conf->ancient_browsers = prev->ancient_browsers;\n        conf->netscape4 = prev->netscape4;\n    }\n\n    if (conf->modern_browser_value == NULL) {\n        conf->modern_browser_value = prev->modern_browser_value;\n    }\n\n    if (conf->modern_browser_value == NULL) {\n        conf->modern_browser_value = &ngx_http_variable_true_value;\n    }\n\n    if (conf->ancient_browser_value == NULL) {\n        conf->ancient_browser_value = prev->ancient_browser_value;\n    }\n\n    if (conf->ancient_browser_value == NULL) {\n        conf->ancient_browser_value = &ngx_http_variable_true_value;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic int ngx_libc_cdecl\nngx_http_modern_browser_sort(const void *one, const void *two)\n{\n    ngx_http_modern_browser_t *first = (ngx_http_modern_browser_t *) one;\n    ngx_http_modern_browser_t *second = (ngx_http_modern_browser_t *) two;\n\n    return (first->skip - second->skip);\n}\n\n\nstatic char *\nngx_http_modern_browser(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_browser_conf_t *bcf = conf;\n\n    u_char                           c;\n    ngx_str_t                       *value;\n    ngx_uint_t                       i, n, version, ver, scale;\n    ngx_http_modern_browser_t       *browser;\n    ngx_http_modern_browser_mask_t  *mask;\n\n    value = cf->args->elts;\n\n    if (cf->args->nelts == 2) {\n        if (ngx_strcmp(value[1].data, \"unlisted\") == 0) {\n            bcf->modern_unlisted_browsers = 1;\n            return NGX_CONF_OK;\n        }\n\n        return NGX_CONF_ERROR;\n    }\n\n    if (bcf->modern_browsers == NULL) {\n        bcf->modern_browsers = ngx_array_create(cf->pool, 5,\n                                            sizeof(ngx_http_modern_browser_t));\n        if (bcf->modern_browsers == NULL) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    browser = ngx_array_push(bcf->modern_browsers);\n    if (browser == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    mask = ngx_http_modern_browser_masks;\n\n    for (n = 0; mask[n].browser[0] != '\\0'; n++) {\n        if (ngx_strcasecmp(mask[n].browser, value[1].data) == 0) {\n            goto found;\n        }\n    }\n\n    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                       \"unknown browser name \\\"%V\\\"\", &value[1]);\n\n    return NGX_CONF_ERROR;\n\nfound:\n\n    /*\n     * at this stage the skip field is used to store the browser slot,\n     * it will be used in sorting in merge stage and then will overwritten\n     * with a real value\n     */\n\n    browser->skip = n;\n\n    version = 0;\n    ver = 0;\n    scale = 1000000;\n\n    for (i = 0; i < value[2].len; i++) {\n\n        c = value[2].data[i];\n\n        if (c >= '0' && c <= '9') {\n            ver = ver * 10 + (c - '0');\n            continue;\n        }\n\n        if (c == '.') {\n            version += ver * scale;\n            ver = 0;\n            scale /= 100;\n            continue;\n        }\n\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid browser version \\\"%V\\\"\", &value[2]);\n\n        return NGX_CONF_ERROR;\n    }\n\n    version += ver * scale;\n\n    browser->version = version;\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_ancient_browser(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_browser_conf_t *bcf = conf;\n\n    ngx_str_t   *value, *browser;\n    ngx_uint_t   i;\n\n    value = cf->args->elts;\n\n    for (i = 1; i < cf->args->nelts; i++) {\n        if (ngx_strcmp(value[i].data, \"netscape4\") == 0) {\n            bcf->netscape4 = 1;\n            continue;\n        }\n\n        if (bcf->ancient_browsers == NULL) {\n            bcf->ancient_browsers = ngx_array_create(cf->pool, 4,\n                                                     sizeof(ngx_str_t));\n            if (bcf->ancient_browsers == NULL) {\n                return NGX_CONF_ERROR;\n            }\n        }\n\n        browser = ngx_array_push(bcf->ancient_browsers);\n        if (browser == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        *browser = value[i];\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_modern_browser_value(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_browser_conf_t *bcf = conf;\n\n    ngx_str_t  *value;\n\n    bcf->modern_browser_value = ngx_palloc(cf->pool,\n                                           sizeof(ngx_http_variable_value_t));\n    if (bcf->modern_browser_value == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    value = cf->args->elts;\n\n    bcf->modern_browser_value->len = value[1].len;\n    bcf->modern_browser_value->valid = 1;\n    bcf->modern_browser_value->no_cacheable = 0;\n    bcf->modern_browser_value->not_found = 0;\n    bcf->modern_browser_value->data = value[1].data;\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_ancient_browser_value(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_browser_conf_t *bcf = conf;\n\n    ngx_str_t  *value;\n\n    bcf->ancient_browser_value = ngx_palloc(cf->pool,\n                                            sizeof(ngx_http_variable_value_t));\n    if (bcf->ancient_browser_value == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    value = cf->args->elts;\n\n    bcf->ancient_browser_value->len = value[1].len;\n    bcf->ancient_browser_value->valid = 1;\n    bcf->ancient_browser_value->no_cacheable = 0;\n    bcf->ancient_browser_value->not_found = 0;\n    bcf->ancient_browser_value->data = value[1].data;\n\n    return NGX_CONF_OK;\n}\n"
  },
  {
    "path": "src/http/modules/ngx_http_charset_filter_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\n#define NGX_HTTP_CHARSET_OFF    -2\n#define NGX_HTTP_NO_CHARSET     -3\n#define NGX_HTTP_CHARSET_VAR    0x10000\n\n/* 1 byte length and up to 3 bytes for the UTF-8 encoding of the UCS-2 */\n#define NGX_UTF_LEN             4\n\n#define NGX_HTML_ENTITY_LEN     (sizeof(\"&#1114111;\") - 1)\n\n\ntypedef struct {\n    u_char                    **tables;\n    ngx_str_t                   name;\n\n    unsigned                    length:16;\n    unsigned                    utf8:1;\n} ngx_http_charset_t;\n\n\ntypedef struct {\n    ngx_int_t                   src;\n    ngx_int_t                   dst;\n} ngx_http_charset_recode_t;\n\n\ntypedef struct {\n    ngx_int_t                   src;\n    ngx_int_t                   dst;\n    u_char                     *src2dst;\n    u_char                     *dst2src;\n} ngx_http_charset_tables_t;\n\n\ntypedef struct {\n    ngx_array_t                 charsets;       /* ngx_http_charset_t */\n    ngx_array_t                 tables;         /* ngx_http_charset_tables_t */\n    ngx_array_t                 recodes;        /* ngx_http_charset_recode_t */\n} ngx_http_charset_main_conf_t;\n\n\ntypedef struct {\n    ngx_int_t                   charset;\n    ngx_int_t                   source_charset;\n    ngx_flag_t                  override_charset;\n\n    ngx_hash_t                  types;\n    ngx_array_t                *types_keys;\n} ngx_http_charset_loc_conf_t;\n\n\ntypedef struct {\n    u_char                     *table;\n    ngx_int_t                   charset;\n    ngx_str_t                   charset_name;\n\n    ngx_chain_t                *busy;\n    ngx_chain_t                *free_bufs;\n    ngx_chain_t                *free_buffers;\n\n    size_t                      saved_len;\n    u_char                      saved[NGX_UTF_LEN];\n\n    unsigned                    length:16;\n    unsigned                    from_utf8:1;\n    unsigned                    to_utf8:1;\n} ngx_http_charset_ctx_t;\n\n\ntypedef struct {\n    ngx_http_charset_tables_t  *table;\n    ngx_http_charset_t         *charset;\n    ngx_uint_t                  characters;\n} ngx_http_charset_conf_ctx_t;\n\n\nstatic ngx_int_t ngx_http_destination_charset(ngx_http_request_t *r,\n    ngx_str_t *name);\nstatic ngx_int_t ngx_http_main_request_charset(ngx_http_request_t *r,\n    ngx_str_t *name);\nstatic ngx_int_t ngx_http_source_charset(ngx_http_request_t *r,\n    ngx_str_t *name);\nstatic ngx_int_t ngx_http_get_charset(ngx_http_request_t *r, ngx_str_t *name);\nstatic ngx_inline void ngx_http_set_charset(ngx_http_request_t *r,\n    ngx_str_t *charset);\nstatic ngx_int_t ngx_http_charset_ctx(ngx_http_request_t *r,\n    ngx_http_charset_t *charsets, ngx_int_t charset, ngx_int_t source_charset);\nstatic ngx_uint_t ngx_http_charset_recode(ngx_buf_t *b, u_char *table);\nstatic ngx_chain_t *ngx_http_charset_recode_from_utf8(ngx_pool_t *pool,\n    ngx_buf_t *buf, ngx_http_charset_ctx_t *ctx);\nstatic ngx_chain_t *ngx_http_charset_recode_to_utf8(ngx_pool_t *pool,\n    ngx_buf_t *buf, ngx_http_charset_ctx_t *ctx);\n\nstatic ngx_chain_t *ngx_http_charset_get_buf(ngx_pool_t *pool,\n    ngx_http_charset_ctx_t *ctx);\nstatic ngx_chain_t *ngx_http_charset_get_buffer(ngx_pool_t *pool,\n    ngx_http_charset_ctx_t *ctx, size_t size);\n\nstatic char *ngx_http_charset_map_block(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_http_charset_map(ngx_conf_t *cf, ngx_command_t *dummy,\n    void *conf);\n\nstatic char *ngx_http_set_charset_slot(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic ngx_int_t ngx_http_add_charset(ngx_array_t *charsets, ngx_str_t *name);\n\nstatic void *ngx_http_charset_create_main_conf(ngx_conf_t *cf);\nstatic void *ngx_http_charset_create_loc_conf(ngx_conf_t *cf);\nstatic char *ngx_http_charset_merge_loc_conf(ngx_conf_t *cf,\n    void *parent, void *child);\nstatic ngx_int_t ngx_http_charset_postconfiguration(ngx_conf_t *cf);\n\n\nstatic ngx_str_t  ngx_http_charset_default_types[] = {\n    ngx_string(\"text/html\"),\n    ngx_string(\"text/xml\"),\n    ngx_string(\"text/plain\"),\n    ngx_string(\"text/vnd.wap.wml\"),\n    ngx_string(\"application/javascript\"),\n    ngx_string(\"application/rss+xml\"),\n    ngx_null_string\n};\n\n\nstatic ngx_command_t  ngx_http_charset_filter_commands[] = {\n\n    { ngx_string(\"charset\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF\n                        |NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,\n      ngx_http_set_charset_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_charset_loc_conf_t, charset),\n      NULL },\n\n    { ngx_string(\"source_charset\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF\n                        |NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,\n      ngx_http_set_charset_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_charset_loc_conf_t, source_charset),\n      NULL },\n\n    { ngx_string(\"override_charset\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF\n                        |NGX_HTTP_LIF_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_charset_loc_conf_t, override_charset),\n      NULL },\n\n    { ngx_string(\"charset_types\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,\n      ngx_http_types_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_charset_loc_conf_t, types_keys),\n      &ngx_http_charset_default_types[0] },\n\n    { ngx_string(\"charset_map\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE2,\n      ngx_http_charset_map_block,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      0,\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_charset_filter_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    ngx_http_charset_postconfiguration,    /* postconfiguration */\n\n    ngx_http_charset_create_main_conf,     /* create main configuration */\n    NULL,                                  /* init main configuration */\n\n    NULL,                                  /* create server configuration */\n    NULL,                                  /* merge server configuration */\n\n    ngx_http_charset_create_loc_conf,      /* create location configuration */\n    ngx_http_charset_merge_loc_conf        /* merge location configuration */\n};\n\n\nngx_module_t  ngx_http_charset_filter_module = {\n    NGX_MODULE_V1,\n    &ngx_http_charset_filter_module_ctx,   /* module context */\n    ngx_http_charset_filter_commands,      /* 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_http_output_header_filter_pt  ngx_http_next_header_filter;\nstatic ngx_http_output_body_filter_pt    ngx_http_next_body_filter;\n\n\nstatic ngx_int_t\nngx_http_charset_header_filter(ngx_http_request_t *r)\n{\n    ngx_int_t                      charset, source_charset;\n    ngx_str_t                      dst, src;\n    ngx_http_charset_t            *charsets;\n    ngx_http_charset_main_conf_t  *mcf;\n\n    if (r == r->main) {\n        charset = ngx_http_destination_charset(r, &dst);\n\n    } else {\n        charset = ngx_http_main_request_charset(r, &dst);\n    }\n\n    if (charset == NGX_ERROR) {\n        return NGX_ERROR;\n    }\n\n    if (charset == NGX_DECLINED) {\n        return ngx_http_next_header_filter(r);\n    }\n\n    /* charset: charset index or NGX_HTTP_NO_CHARSET */\n\n    source_charset = ngx_http_source_charset(r, &src);\n\n    if (source_charset == NGX_ERROR) {\n        return NGX_ERROR;\n    }\n\n    /*\n     * source_charset: charset index, NGX_HTTP_NO_CHARSET,\n     *                 or NGX_HTTP_CHARSET_OFF\n     */\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"charset: \\\"%V\\\" > \\\"%V\\\"\", &src, &dst);\n\n    if (source_charset == NGX_HTTP_CHARSET_OFF) {\n        ngx_http_set_charset(r, &dst);\n\n        return ngx_http_next_header_filter(r);\n    }\n\n    if (charset == NGX_HTTP_NO_CHARSET\n        || source_charset == NGX_HTTP_NO_CHARSET)\n    {\n        if (source_charset != charset\n            || ngx_strncasecmp(dst.data, src.data, dst.len) != 0)\n        {\n            goto no_charset_map;\n        }\n\n        ngx_http_set_charset(r, &dst);\n\n        return ngx_http_next_header_filter(r);\n    }\n\n    if (source_charset == charset) {\n        r->headers_out.content_type.len = r->headers_out.content_type_len;\n\n        ngx_http_set_charset(r, &dst);\n\n        return ngx_http_next_header_filter(r);\n    }\n\n    /* source_charset != charset */\n\n    if (r->headers_out.content_encoding\n        && r->headers_out.content_encoding->value.len)\n    {\n        return ngx_http_next_header_filter(r);\n    }\n\n    mcf = ngx_http_get_module_main_conf(r, ngx_http_charset_filter_module);\n    charsets = mcf->charsets.elts;\n\n    if (charsets[source_charset].tables == NULL\n        || charsets[source_charset].tables[charset] == NULL)\n    {\n        goto no_charset_map;\n    }\n\n    r->headers_out.content_type.len = r->headers_out.content_type_len;\n\n    ngx_http_set_charset(r, &dst);\n\n    return ngx_http_charset_ctx(r, charsets, charset, source_charset);\n\nno_charset_map:\n\n    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                  \"no \\\"charset_map\\\" between the charsets \\\"%V\\\" and \\\"%V\\\"\",\n                  &src, &dst);\n\n    return ngx_http_next_header_filter(r);\n}\n\n\nstatic ngx_int_t\nngx_http_destination_charset(ngx_http_request_t *r, ngx_str_t *name)\n{\n    ngx_int_t                      charset;\n    ngx_http_charset_t            *charsets;\n    ngx_http_variable_value_t     *vv;\n    ngx_http_charset_loc_conf_t   *mlcf;\n    ngx_http_charset_main_conf_t  *mcf;\n\n    if (r->headers_out.content_type.len == 0) {\n        return NGX_DECLINED;\n    }\n\n    if (r->headers_out.override_charset\n        && r->headers_out.override_charset->len)\n    {\n        *name = *r->headers_out.override_charset;\n\n        charset = ngx_http_get_charset(r, name);\n\n        if (charset != NGX_HTTP_NO_CHARSET) {\n            return charset;\n        }\n\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"unknown charset \\\"%V\\\" to override\", name);\n\n        return NGX_DECLINED;\n    }\n\n    mlcf = ngx_http_get_module_loc_conf(r, ngx_http_charset_filter_module);\n    charset = mlcf->charset;\n\n    if (charset == NGX_HTTP_CHARSET_OFF) {\n        return NGX_DECLINED;\n    }\n\n    if (r->headers_out.charset.len) {\n        if (mlcf->override_charset == 0) {\n            return NGX_DECLINED;\n        }\n\n    } else {\n        if (ngx_http_test_content_type(r, &mlcf->types) == NULL) {\n            return NGX_DECLINED;\n        }\n    }\n\n    if (charset < NGX_HTTP_CHARSET_VAR) {\n        mcf = ngx_http_get_module_main_conf(r, ngx_http_charset_filter_module);\n        charsets = mcf->charsets.elts;\n        *name = charsets[charset].name;\n        return charset;\n    }\n\n    vv = ngx_http_get_indexed_variable(r, charset - NGX_HTTP_CHARSET_VAR);\n\n    if (vv == NULL || vv->not_found) {\n        return NGX_ERROR;\n    }\n\n    name->len = vv->len;\n    name->data = vv->data;\n\n    return ngx_http_get_charset(r, name);\n}\n\n\nstatic ngx_int_t\nngx_http_main_request_charset(ngx_http_request_t *r, ngx_str_t *src)\n{\n    ngx_int_t                charset;\n    ngx_str_t               *main_charset;\n    ngx_http_charset_ctx_t  *ctx;\n\n    ctx = ngx_http_get_module_ctx(r->main, ngx_http_charset_filter_module);\n\n    if (ctx) {\n        *src = ctx->charset_name;\n        return ctx->charset;\n    }\n\n    main_charset = &r->main->headers_out.charset;\n\n    if (main_charset->len == 0) {\n        return NGX_DECLINED;\n    }\n\n    ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_charset_ctx_t));\n    if (ctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_http_set_ctx(r->main, ctx, ngx_http_charset_filter_module);\n\n    charset = ngx_http_get_charset(r, main_charset);\n\n    ctx->charset = charset;\n    ctx->charset_name = *main_charset;\n    *src = *main_charset;\n\n    return charset;\n}\n\n\nstatic ngx_int_t\nngx_http_source_charset(ngx_http_request_t *r, ngx_str_t *name)\n{\n    ngx_int_t                      charset;\n    ngx_http_charset_t            *charsets;\n    ngx_http_variable_value_t     *vv;\n    ngx_http_charset_loc_conf_t   *lcf;\n    ngx_http_charset_main_conf_t  *mcf;\n\n    if (r->headers_out.charset.len) {\n        *name = r->headers_out.charset;\n        return ngx_http_get_charset(r, name);\n    }\n\n    lcf = ngx_http_get_module_loc_conf(r, ngx_http_charset_filter_module);\n\n    charset = lcf->source_charset;\n\n    if (charset == NGX_HTTP_CHARSET_OFF) {\n        name->len = 0;\n        return charset;\n    }\n\n    if (charset < NGX_HTTP_CHARSET_VAR) {\n        mcf = ngx_http_get_module_main_conf(r, ngx_http_charset_filter_module);\n        charsets = mcf->charsets.elts;\n        *name = charsets[charset].name;\n        return charset;\n    }\n\n    vv = ngx_http_get_indexed_variable(r, charset - NGX_HTTP_CHARSET_VAR);\n\n    if (vv == NULL || vv->not_found) {\n        return NGX_ERROR;\n    }\n\n    name->len = vv->len;\n    name->data = vv->data;\n\n    return ngx_http_get_charset(r, name);\n}\n\n\nstatic ngx_int_t\nngx_http_get_charset(ngx_http_request_t *r, ngx_str_t *name)\n{\n    ngx_uint_t                     i, n;\n    ngx_http_charset_t            *charset;\n    ngx_http_charset_main_conf_t  *mcf;\n\n    mcf = ngx_http_get_module_main_conf(r, ngx_http_charset_filter_module);\n\n    charset = mcf->charsets.elts;\n    n = mcf->charsets.nelts;\n\n    for (i = 0; i < n; i++) {\n        if (charset[i].name.len != name->len) {\n            continue;\n        }\n\n        if (ngx_strncasecmp(charset[i].name.data, name->data, name->len) == 0) {\n            return i;\n        }\n    }\n\n    return NGX_HTTP_NO_CHARSET;\n}\n\n\nstatic ngx_inline void\nngx_http_set_charset(ngx_http_request_t *r, ngx_str_t *charset)\n{\n    if (r != r->main) {\n        return;\n    }\n\n    if (r->headers_out.status == NGX_HTTP_MOVED_PERMANENTLY\n        || r->headers_out.status == NGX_HTTP_MOVED_TEMPORARILY)\n    {\n        /*\n         * do not set charset for the redirect because NN 4.x\n         * use this charset instead of the next page charset\n         */\n\n        r->headers_out.charset.len = 0;\n        return;\n    }\n\n    r->headers_out.charset = *charset;\n}\n\n\nstatic ngx_int_t\nngx_http_charset_ctx(ngx_http_request_t *r, ngx_http_charset_t *charsets,\n    ngx_int_t charset, ngx_int_t source_charset)\n{\n    ngx_http_charset_ctx_t  *ctx;\n\n    ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_charset_ctx_t));\n    if (ctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_http_set_ctx(r, ctx, ngx_http_charset_filter_module);\n\n    ctx->table = charsets[source_charset].tables[charset];\n    ctx->charset = charset;\n    ctx->charset_name = charsets[charset].name;\n    ctx->length = charsets[charset].length;\n    ctx->from_utf8 = charsets[source_charset].utf8;\n    ctx->to_utf8 = charsets[charset].utf8;\n\n    r->filter_need_in_memory = 1;\n\n    if ((ctx->to_utf8 || ctx->from_utf8) && r == r->main) {\n        ngx_http_clear_content_length(r);\n\n    } else {\n        r->filter_need_temporary = 1;\n    }\n\n    return ngx_http_next_header_filter(r);\n}\n\n\nstatic ngx_int_t\nngx_http_charset_body_filter(ngx_http_request_t *r, ngx_chain_t *in)\n{\n    ngx_int_t                rc;\n    ngx_buf_t               *b;\n    ngx_chain_t             *cl, *out, **ll;\n    ngx_http_charset_ctx_t  *ctx;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_charset_filter_module);\n\n    if (ctx == NULL || ctx->table == NULL) {\n        return ngx_http_next_body_filter(r, in);\n    }\n\n    if ((ctx->to_utf8 || ctx->from_utf8) || ctx->busy) {\n\n        out = NULL;\n        ll = &out;\n\n        for (cl = in; cl; cl = cl->next) {\n            b = cl->buf;\n\n            if (ngx_buf_size(b) == 0) {\n\n                *ll = ngx_alloc_chain_link(r->pool);\n                if (*ll == NULL) {\n                    return NGX_ERROR;\n                }\n\n                (*ll)->buf = b;\n                (*ll)->next = NULL;\n\n                ll = &(*ll)->next;\n\n                continue;\n            }\n\n            if (ctx->to_utf8) {\n                *ll = ngx_http_charset_recode_to_utf8(r->pool, b, ctx);\n\n            } else {\n                *ll = ngx_http_charset_recode_from_utf8(r->pool, b, ctx);\n            }\n\n            if (*ll == NULL) {\n                return NGX_ERROR;\n            }\n\n            while (*ll) {\n                ll = &(*ll)->next;\n            }\n        }\n\n        rc = ngx_http_next_body_filter(r, out);\n\n        if (out) {\n            if (ctx->busy == NULL) {\n                ctx->busy = out;\n\n            } else {\n                for (cl = ctx->busy; cl->next; cl = cl->next) { /* void */ }\n                cl->next = out;\n            }\n        }\n\n        while (ctx->busy) {\n\n            cl = ctx->busy;\n            b = cl->buf;\n\n            if (ngx_buf_size(b) != 0) {\n                break;\n            }\n\n            ctx->busy = cl->next;\n\n            if (b->tag != (ngx_buf_tag_t) &ngx_http_charset_filter_module) {\n                continue;\n            }\n\n            if (b->shadow) {\n                b->shadow->pos = b->shadow->last;\n            }\n\n            if (b->pos) {\n                cl->next = ctx->free_buffers;\n                ctx->free_buffers = cl;\n                continue;\n            }\n\n            cl->next = ctx->free_bufs;\n            ctx->free_bufs = cl;\n        }\n\n        return rc;\n    }\n\n    for (cl = in; cl; cl = cl->next) {\n        (void) ngx_http_charset_recode(cl->buf, ctx->table);\n    }\n\n    return ngx_http_next_body_filter(r, in);\n}\n\n\nstatic ngx_uint_t\nngx_http_charset_recode(ngx_buf_t *b, u_char *table)\n{\n    u_char  *p, *last;\n\n    last = b->last;\n\n    for (p = b->pos; p < last; p++) {\n\n        if (*p != table[*p]) {\n            goto recode;\n        }\n    }\n\n    return 0;\n\nrecode:\n\n    do {\n        if (*p != table[*p]) {\n            *p = table[*p];\n        }\n\n        p++;\n\n    } while (p < last);\n\n    b->in_file = 0;\n\n    return 1;\n}\n\n\nstatic ngx_chain_t *\nngx_http_charset_recode_from_utf8(ngx_pool_t *pool, ngx_buf_t *buf,\n    ngx_http_charset_ctx_t *ctx)\n{\n    size_t        len, size;\n    u_char        c, *p, *src, *dst, *saved, **table;\n    uint32_t      n;\n    ngx_buf_t    *b;\n    ngx_uint_t    i;\n    ngx_chain_t  *out, *cl, **ll;\n\n    src = buf->pos;\n\n    if (ctx->saved_len == 0) {\n\n        for ( /* void */ ; src < buf->last; src++) {\n\n            if (*src < 0x80) {\n                continue;\n            }\n\n            len = src - buf->pos;\n\n            if (len > 512) {\n                out = ngx_http_charset_get_buf(pool, ctx);\n                if (out == NULL) {\n                    return NULL;\n                }\n\n                b = out->buf;\n\n                b->temporary = buf->temporary;\n                b->memory = buf->memory;\n                b->mmap = buf->mmap;\n                b->flush = buf->flush;\n\n                b->pos = buf->pos;\n                b->last = src;\n\n                out->buf = b;\n                out->next = NULL;\n\n                size = buf->last - src;\n\n                saved = src;\n                n = ngx_utf8_decode(&saved, size);\n\n                if (n == 0xfffffffe) {\n                    /* incomplete UTF-8 symbol */\n\n                    ngx_memcpy(ctx->saved, src, size);\n                    ctx->saved_len = size;\n\n                    b->shadow = buf;\n\n                    return out;\n                }\n\n            } else {\n                out = NULL;\n                size = len + buf->last - src;\n                src = buf->pos;\n            }\n\n            if (size < NGX_HTML_ENTITY_LEN) {\n                size += NGX_HTML_ENTITY_LEN;\n            }\n\n            cl = ngx_http_charset_get_buffer(pool, ctx, size);\n            if (cl == NULL) {\n                return NULL;\n            }\n\n            if (out) {\n                out->next = cl;\n\n            } else {\n                out = cl;\n            }\n\n            b = cl->buf;\n            dst = b->pos;\n\n            goto recode;\n        }\n\n        out = ngx_alloc_chain_link(pool);\n        if (out == NULL) {\n            return NULL;\n        }\n\n        out->buf = buf;\n        out->next = NULL;\n\n        return out;\n    }\n\n    /* process incomplete UTF sequence from previous buffer */\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pool->log, 0,\n                   \"http charset utf saved: %z\", ctx->saved_len);\n\n    p = src;\n\n    for (i = ctx->saved_len; i < NGX_UTF_LEN; i++) {\n        ctx->saved[i] = *p++;\n\n        if (p == buf->last) {\n            break;\n        }\n    }\n\n    saved = ctx->saved;\n    n = ngx_utf8_decode(&saved, i);\n\n    c = '\\0';\n\n    if (n < 0x10000) {\n        table = (u_char **) ctx->table;\n        p = table[n >> 8];\n\n        if (p) {\n            c = p[n & 0xff];\n        }\n\n    } else if (n == 0xfffffffe) {\n\n        /* incomplete UTF-8 symbol */\n\n        if (i < NGX_UTF_LEN) {\n            out = ngx_http_charset_get_buf(pool, ctx);\n            if (out == NULL) {\n                return NULL;\n            }\n\n            b = out->buf;\n\n            b->pos = buf->pos;\n            b->last = buf->last;\n            b->sync = 1;\n            b->shadow = buf;\n\n            ngx_memcpy(&ctx->saved[ctx->saved_len], src, i);\n            ctx->saved_len += i;\n\n            return out;\n        }\n    }\n\n    size = buf->last - buf->pos;\n\n    if (size < NGX_HTML_ENTITY_LEN) {\n        size += NGX_HTML_ENTITY_LEN;\n    }\n\n    cl = ngx_http_charset_get_buffer(pool, ctx, size);\n    if (cl == NULL) {\n        return NULL;\n    }\n\n    out = cl;\n\n    b = cl->buf;\n    dst = b->pos;\n\n    if (c) {\n        *dst++ = c;\n\n    } else if (n == 0xfffffffe) {\n        *dst++ = '?';\n\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pool->log, 0,\n                       \"http charset invalid utf 0\");\n\n        saved = &ctx->saved[NGX_UTF_LEN];\n\n    } else if (n > 0x10ffff) {\n        *dst++ = '?';\n\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pool->log, 0,\n                       \"http charset invalid utf 1\");\n\n    } else {\n        dst = ngx_sprintf(dst, \"&#%uD;\", n);\n    }\n\n    src += (saved - ctx->saved) - ctx->saved_len;\n    ctx->saved_len = 0;\n\nrecode:\n\n    ll = &cl->next;\n\n    table = (u_char **) ctx->table;\n\n    while (src < buf->last) {\n\n        if ((size_t) (b->end - dst) < NGX_HTML_ENTITY_LEN) {\n            b->last = dst;\n\n            size = buf->last - src + NGX_HTML_ENTITY_LEN;\n\n            cl = ngx_http_charset_get_buffer(pool, ctx, size);\n            if (cl == NULL) {\n                return NULL;\n            }\n\n            *ll = cl;\n            ll = &cl->next;\n\n            b = cl->buf;\n            dst = b->pos;\n        }\n\n        if (*src < 0x80) {\n            *dst++ = *src++;\n            continue;\n        }\n\n        len = buf->last - src;\n\n        n = ngx_utf8_decode(&src, len);\n\n        if (n < 0x10000) {\n\n            p = table[n >> 8];\n\n            if (p) {\n                c = p[n & 0xff];\n\n                if (c) {\n                    *dst++ = c;\n                    continue;\n                }\n            }\n\n            dst = ngx_sprintf(dst, \"&#%uD;\", n);\n\n            continue;\n        }\n\n        if (n == 0xfffffffe) {\n            /* incomplete UTF-8 symbol */\n\n            ngx_memcpy(ctx->saved, src, len);\n            ctx->saved_len = len;\n\n            if (b->pos == dst) {\n                b->sync = 1;\n                b->temporary = 0;\n            }\n\n            break;\n        }\n\n        if (n > 0x10ffff) {\n            *dst++ = '?';\n\n            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pool->log, 0,\n                           \"http charset invalid utf 2\");\n\n            continue;\n        }\n\n        /* n > 0xffff */\n\n        dst = ngx_sprintf(dst, \"&#%uD;\", n);\n    }\n\n    b->last = dst;\n\n    b->last_buf = buf->last_buf;\n    b->last_in_chain = buf->last_in_chain;\n    b->flush = buf->flush;\n\n    b->shadow = buf;\n\n    return out;\n}\n\n\nstatic ngx_chain_t *\nngx_http_charset_recode_to_utf8(ngx_pool_t *pool, ngx_buf_t *buf,\n    ngx_http_charset_ctx_t *ctx)\n{\n    size_t        len, size;\n    u_char       *p, *src, *dst, *table;\n    ngx_buf_t    *b;\n    ngx_chain_t  *out, *cl, **ll;\n\n    table = ctx->table;\n\n    for (src = buf->pos; src < buf->last; src++) {\n        if (table[*src * NGX_UTF_LEN] == '\\1') {\n            continue;\n        }\n\n        goto recode;\n    }\n\n    out = ngx_alloc_chain_link(pool);\n    if (out == NULL) {\n        return NULL;\n    }\n\n    out->buf = buf;\n    out->next = NULL;\n\n    return out;\n\nrecode:\n\n    /*\n     * we assume that there are about half of characters to be recoded,\n     * so we preallocate \"size / 2 + size / 2 * ctx->length\"\n     */\n\n    len = src - buf->pos;\n\n    if (len > 512) {\n        out = ngx_http_charset_get_buf(pool, ctx);\n        if (out == NULL) {\n            return NULL;\n        }\n\n        b = out->buf;\n\n        b->temporary = buf->temporary;\n        b->memory = buf->memory;\n        b->mmap = buf->mmap;\n        b->flush = buf->flush;\n\n        b->pos = buf->pos;\n        b->last = src;\n\n        out->buf = b;\n        out->next = NULL;\n\n        size = buf->last - src;\n        size = size / 2 + size / 2 * ctx->length;\n\n    } else {\n        out = NULL;\n\n        size = buf->last - src;\n        size = len + size / 2 + size / 2 * ctx->length;\n\n        src = buf->pos;\n    }\n\n    cl = ngx_http_charset_get_buffer(pool, ctx, size);\n    if (cl == NULL) {\n        return NULL;\n    }\n\n    if (out) {\n        out->next = cl;\n\n    } else {\n        out = cl;\n    }\n\n    ll = &cl->next;\n\n    b = cl->buf;\n    dst = b->pos;\n\n    while (src < buf->last) {\n\n        p = &table[*src++ * NGX_UTF_LEN];\n        len = *p++;\n\n        if ((size_t) (b->end - dst) < len) {\n            b->last = dst;\n\n            size = buf->last - src;\n            size = len + size / 2 + size / 2 * ctx->length;\n\n            cl = ngx_http_charset_get_buffer(pool, ctx, size);\n            if (cl == NULL) {\n                return NULL;\n            }\n\n            *ll = cl;\n            ll = &cl->next;\n\n            b = cl->buf;\n            dst = b->pos;\n        }\n\n        while (len) {\n            *dst++ = *p++;\n            len--;\n        }\n    }\n\n    b->last = dst;\n\n    b->last_buf = buf->last_buf;\n    b->last_in_chain = buf->last_in_chain;\n    b->flush = buf->flush;\n\n    b->shadow = buf;\n\n    return out;\n}\n\n\nstatic ngx_chain_t *\nngx_http_charset_get_buf(ngx_pool_t *pool, ngx_http_charset_ctx_t *ctx)\n{\n    ngx_chain_t  *cl;\n\n    cl = ctx->free_bufs;\n\n    if (cl) {\n        ctx->free_bufs = cl->next;\n\n        cl->buf->shadow = NULL;\n        cl->next = NULL;\n\n        return cl;\n    }\n\n    cl = ngx_alloc_chain_link(pool);\n    if (cl == NULL) {\n        return NULL;\n    }\n\n    cl->buf = ngx_calloc_buf(pool);\n    if (cl->buf == NULL) {\n        return NULL;\n    }\n\n    cl->next = NULL;\n\n    cl->buf->tag = (ngx_buf_tag_t) &ngx_http_charset_filter_module;\n\n    return cl;\n}\n\n\nstatic ngx_chain_t *\nngx_http_charset_get_buffer(ngx_pool_t *pool, ngx_http_charset_ctx_t *ctx,\n    size_t size)\n{\n    ngx_buf_t    *b;\n    ngx_chain_t  *cl, **ll;\n\n    for (ll = &ctx->free_buffers, cl = ctx->free_buffers;\n         cl;\n         ll = &cl->next, cl = cl->next)\n    {\n        b = cl->buf;\n\n        if ((size_t) (b->end - b->start) >= size) {\n            *ll = cl->next;\n            cl->next = NULL;\n\n            b->pos = b->start;\n            b->temporary = 1;\n            b->shadow = NULL;\n\n            return cl;\n        }\n    }\n\n    cl = ngx_alloc_chain_link(pool);\n    if (cl == NULL) {\n        return NULL;\n    }\n\n    cl->buf = ngx_create_temp_buf(pool, size);\n    if (cl->buf == NULL) {\n        return NULL;\n    }\n\n    cl->next = NULL;\n\n    cl->buf->temporary = 1;\n    cl->buf->tag = (ngx_buf_tag_t) &ngx_http_charset_filter_module;\n\n    return cl;\n}\n\n\nstatic char *\nngx_http_charset_map_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_charset_main_conf_t  *mcf = conf;\n\n    char                         *rv;\n    u_char                       *p, *dst2src, **pp;\n    ngx_int_t                     src, dst;\n    ngx_uint_t                    i, n;\n    ngx_str_t                    *value;\n    ngx_conf_t                    pvcf;\n    ngx_http_charset_t           *charset;\n    ngx_http_charset_tables_t    *table;\n    ngx_http_charset_conf_ctx_t   ctx;\n\n    value = cf->args->elts;\n\n    src = ngx_http_add_charset(&mcf->charsets, &value[1]);\n    if (src == NGX_ERROR) {\n        return NGX_CONF_ERROR;\n    }\n\n    dst = ngx_http_add_charset(&mcf->charsets, &value[2]);\n    if (dst == NGX_ERROR) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (src == dst) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"\\\"charset_map\\\" between the same charsets \"\n                           \"\\\"%V\\\" and \\\"%V\\\"\", &value[1], &value[2]);\n        return NGX_CONF_ERROR;\n    }\n\n    table = mcf->tables.elts;\n    for (i = 0; i < mcf->tables.nelts; i++) {\n        if ((src == table->src && dst == table->dst)\n             || (src == table->dst && dst == table->src))\n        {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"duplicate \\\"charset_map\\\" between \"\n                               \"\\\"%V\\\" and \\\"%V\\\"\", &value[1], &value[2]);\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    table = ngx_array_push(&mcf->tables);\n    if (table == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    table->src = src;\n    table->dst = dst;\n\n    if (ngx_strcasecmp(value[2].data, (u_char *) \"utf-8\") == 0) {\n        table->src2dst = ngx_pcalloc(cf->pool, 256 * NGX_UTF_LEN);\n        if (table->src2dst == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        table->dst2src = ngx_pcalloc(cf->pool, 256 * sizeof(void *));\n        if (table->dst2src == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        dst2src = ngx_pcalloc(cf->pool, 256);\n        if (dst2src == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        pp = (u_char **) &table->dst2src[0];\n        pp[0] = dst2src;\n\n        for (i = 0; i < 128; i++) {\n            p = &table->src2dst[i * NGX_UTF_LEN];\n            p[0] = '\\1';\n            p[1] = (u_char) i;\n            dst2src[i] = (u_char) i;\n        }\n\n        for (/* void */; i < 256; i++) {\n            p = &table->src2dst[i * NGX_UTF_LEN];\n            p[0] = '\\1';\n            p[1] = '?';\n        }\n\n    } else {\n        table->src2dst = ngx_palloc(cf->pool, 256);\n        if (table->src2dst == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        table->dst2src = ngx_palloc(cf->pool, 256);\n        if (table->dst2src == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        for (i = 0; i < 128; i++) {\n            table->src2dst[i] = (u_char) i;\n            table->dst2src[i] = (u_char) i;\n        }\n\n        for (/* void */; i < 256; i++) {\n            table->src2dst[i] = '?';\n            table->dst2src[i] = '?';\n        }\n    }\n\n    charset = mcf->charsets.elts;\n\n    ctx.table = table;\n    ctx.charset = &charset[dst];\n    ctx.characters = 0;\n\n    pvcf = *cf;\n    cf->ctx = &ctx;\n    cf->handler = ngx_http_charset_map;\n    cf->handler_conf = conf;\n\n    rv = ngx_conf_parse(cf, NULL);\n\n    *cf = pvcf;\n\n    if (ctx.characters) {\n        n = ctx.charset->length;\n        ctx.charset->length /= ctx.characters;\n\n        if (((n * 10) / ctx.characters) % 10 > 4) {\n            ctx.charset->length++;\n        }\n    }\n\n    return rv;\n}\n\n\nstatic char *\nngx_http_charset_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)\n{\n    u_char                       *p, *dst2src, **pp;\n    uint32_t                      n;\n    ngx_int_t                     src, dst;\n    ngx_str_t                    *value;\n    ngx_uint_t                    i;\n    ngx_http_charset_tables_t    *table;\n    ngx_http_charset_conf_ctx_t  *ctx;\n\n    if (cf->args->nelts != 2) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, \"invalid parameters number\");\n        return NGX_CONF_ERROR;\n    }\n\n    value = cf->args->elts;\n\n    src = ngx_hextoi(value[0].data, value[0].len);\n    if (src == NGX_ERROR || src > 255) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid value \\\"%V\\\"\", &value[0]);\n        return NGX_CONF_ERROR;\n    }\n\n    ctx = cf->ctx;\n    table = ctx->table;\n\n    if (ctx->charset->utf8) {\n        p = &table->src2dst[src * NGX_UTF_LEN];\n\n        *p++ = (u_char) (value[1].len / 2);\n\n        for (i = 0; i < value[1].len; i += 2) {\n            dst = ngx_hextoi(&value[1].data[i], 2);\n            if (dst == NGX_ERROR || dst > 255) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"invalid value \\\"%V\\\"\", &value[1]);\n                return NGX_CONF_ERROR;\n            }\n\n            *p++ = (u_char) dst;\n        }\n\n        i /= 2;\n\n        ctx->charset->length += i;\n        ctx->characters++;\n\n        p = &table->src2dst[src * NGX_UTF_LEN] + 1;\n\n        n = ngx_utf8_decode(&p, i);\n\n        if (n > 0xffff) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"invalid value \\\"%V\\\"\", &value[1]);\n            return NGX_CONF_ERROR;\n        }\n\n        pp = (u_char **) &table->dst2src[0];\n\n        dst2src = pp[n >> 8];\n\n        if (dst2src == NULL) {\n            dst2src = ngx_pcalloc(cf->pool, 256);\n            if (dst2src == NULL) {\n                return NGX_CONF_ERROR;\n            }\n\n            pp[n >> 8] = dst2src;\n        }\n\n        dst2src[n & 0xff] = (u_char) src;\n\n    } else {\n        dst = ngx_hextoi(value[1].data, value[1].len);\n        if (dst == NGX_ERROR || dst > 255) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"invalid value \\\"%V\\\"\", &value[1]);\n            return NGX_CONF_ERROR;\n        }\n\n        table->src2dst[src] = (u_char) dst;\n        table->dst2src[dst] = (u_char) src;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_set_charset_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    char  *p = conf;\n\n    ngx_int_t                     *cp;\n    ngx_str_t                     *value, var;\n    ngx_http_charset_main_conf_t  *mcf;\n\n    cp = (ngx_int_t *) (p + cmd->offset);\n\n    if (*cp != NGX_CONF_UNSET) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    if (cmd->offset == offsetof(ngx_http_charset_loc_conf_t, charset)\n        && ngx_strcmp(value[1].data, \"off\") == 0)\n    {\n        *cp = NGX_HTTP_CHARSET_OFF;\n        return NGX_CONF_OK;\n    }\n\n\n    if (value[1].data[0] == '$') {\n        var.len = value[1].len - 1;\n        var.data = value[1].data + 1;\n\n        *cp = ngx_http_get_variable_index(cf, &var);\n\n        if (*cp == NGX_ERROR) {\n            return NGX_CONF_ERROR;\n        }\n\n        *cp += NGX_HTTP_CHARSET_VAR;\n\n        return NGX_CONF_OK;\n    }\n\n    mcf = ngx_http_conf_get_module_main_conf(cf,\n                                             ngx_http_charset_filter_module);\n\n    *cp = ngx_http_add_charset(&mcf->charsets, &value[1]);\n    if (*cp == NGX_ERROR) {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_add_charset(ngx_array_t *charsets, ngx_str_t *name)\n{\n    ngx_uint_t           i;\n    ngx_http_charset_t  *c;\n\n    c = charsets->elts;\n    for (i = 0; i < charsets->nelts; i++) {\n        if (name->len != c[i].name.len) {\n            continue;\n        }\n\n        if (ngx_strcasecmp(name->data, c[i].name.data) == 0) {\n            break;\n        }\n    }\n\n    if (i < charsets->nelts) {\n        return i;\n    }\n\n    c = ngx_array_push(charsets);\n    if (c == NULL) {\n        return NGX_ERROR;\n    }\n\n    c->tables = NULL;\n    c->name = *name;\n    c->length = 0;\n\n    if (ngx_strcasecmp(name->data, (u_char *) \"utf-8\") == 0) {\n        c->utf8 = 1;\n\n    } else {\n        c->utf8 = 0;\n    }\n\n    return i;\n}\n\n\nstatic void *\nngx_http_charset_create_main_conf(ngx_conf_t *cf)\n{\n    ngx_http_charset_main_conf_t  *mcf;\n\n    mcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_charset_main_conf_t));\n    if (mcf == NULL) {\n        return NULL;\n    }\n\n    if (ngx_array_init(&mcf->charsets, cf->pool, 2, sizeof(ngx_http_charset_t))\n        != NGX_OK)\n    {\n        return NULL;\n    }\n\n    if (ngx_array_init(&mcf->tables, cf->pool, 1,\n                       sizeof(ngx_http_charset_tables_t))\n        != NGX_OK)\n    {\n        return NULL;\n    }\n\n    if (ngx_array_init(&mcf->recodes, cf->pool, 2,\n                       sizeof(ngx_http_charset_recode_t))\n        != NGX_OK)\n    {\n        return NULL;\n    }\n\n    return mcf;\n}\n\n\nstatic void *\nngx_http_charset_create_loc_conf(ngx_conf_t *cf)\n{\n    ngx_http_charset_loc_conf_t  *lcf;\n\n    lcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_charset_loc_conf_t));\n    if (lcf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc():\n     *\n     *     lcf->types = { NULL };\n     *     lcf->types_keys = NULL;\n     */\n\n    lcf->charset = NGX_CONF_UNSET;\n    lcf->source_charset = NGX_CONF_UNSET;\n    lcf->override_charset = NGX_CONF_UNSET;\n\n    return lcf;\n}\n\n\nstatic char *\nngx_http_charset_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_http_charset_loc_conf_t *prev = parent;\n    ngx_http_charset_loc_conf_t *conf = child;\n\n    ngx_uint_t                     i;\n    ngx_http_charset_recode_t     *recode;\n    ngx_http_charset_main_conf_t  *mcf;\n\n    if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,\n                             &prev->types_keys, &prev->types,\n                             ngx_http_charset_default_types)\n        != NGX_OK)\n    {\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_conf_merge_value(conf->override_charset, prev->override_charset, 0);\n    ngx_conf_merge_value(conf->charset, prev->charset, NGX_HTTP_CHARSET_OFF);\n    ngx_conf_merge_value(conf->source_charset, prev->source_charset,\n                         NGX_HTTP_CHARSET_OFF);\n\n    if (conf->charset == NGX_HTTP_CHARSET_OFF\n        || conf->source_charset == NGX_HTTP_CHARSET_OFF\n        || conf->charset == conf->source_charset)\n    {\n        return NGX_CONF_OK;\n    }\n\n    if (conf->source_charset >= NGX_HTTP_CHARSET_VAR\n        || conf->charset >= NGX_HTTP_CHARSET_VAR)\n    {\n        return NGX_CONF_OK;\n    }\n\n    mcf = ngx_http_conf_get_module_main_conf(cf,\n                                             ngx_http_charset_filter_module);\n    recode = mcf->recodes.elts;\n    for (i = 0; i < mcf->recodes.nelts; i++) {\n        if (conf->source_charset == recode[i].src\n            && conf->charset == recode[i].dst)\n        {\n            return NGX_CONF_OK;\n        }\n    }\n\n    recode = ngx_array_push(&mcf->recodes);\n    if (recode == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    recode->src = conf->source_charset;\n    recode->dst = conf->charset;\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_charset_postconfiguration(ngx_conf_t *cf)\n{\n    u_char                       **src, **dst;\n    ngx_int_t                      c;\n    ngx_uint_t                     i, t;\n    ngx_http_charset_t            *charset;\n    ngx_http_charset_recode_t     *recode;\n    ngx_http_charset_tables_t     *tables;\n    ngx_http_charset_main_conf_t  *mcf;\n\n    mcf = ngx_http_conf_get_module_main_conf(cf,\n                                             ngx_http_charset_filter_module);\n\n    recode = mcf->recodes.elts;\n    tables = mcf->tables.elts;\n    charset = mcf->charsets.elts;\n\n    for (i = 0; i < mcf->recodes.nelts; i++) {\n\n        c = recode[i].src;\n\n        for (t = 0; t < mcf->tables.nelts; t++) {\n\n            if (c == tables[t].src && recode[i].dst == tables[t].dst) {\n                goto next;\n            }\n\n            if (c == tables[t].dst && recode[i].dst == tables[t].src) {\n                goto next;\n            }\n        }\n\n        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                   \"no \\\"charset_map\\\" between the charsets \\\"%V\\\" and \\\"%V\\\"\",\n                   &charset[c].name, &charset[recode[i].dst].name);\n        return NGX_ERROR;\n\n    next:\n        continue;\n    }\n\n\n    for (t = 0; t < mcf->tables.nelts; t++) {\n\n        src = charset[tables[t].src].tables;\n\n        if (src == NULL) {\n            src = ngx_pcalloc(cf->pool, sizeof(u_char *) * mcf->charsets.nelts);\n            if (src == NULL) {\n                return NGX_ERROR;\n            }\n\n            charset[tables[t].src].tables = src;\n        }\n\n        dst = charset[tables[t].dst].tables;\n\n        if (dst == NULL) {\n            dst = ngx_pcalloc(cf->pool, sizeof(u_char *) * mcf->charsets.nelts);\n            if (dst == NULL) {\n                return NGX_ERROR;\n            }\n\n            charset[tables[t].dst].tables = dst;\n        }\n\n        src[tables[t].dst] = tables[t].src2dst;\n        dst[tables[t].src] = tables[t].dst2src;\n    }\n\n    ngx_http_next_header_filter = ngx_http_top_header_filter;\n    ngx_http_top_header_filter = ngx_http_charset_header_filter;\n\n    ngx_http_next_body_filter = ngx_http_top_body_filter;\n    ngx_http_top_body_filter = ngx_http_charset_body_filter;\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "src/http/modules/ngx_http_chunked_filter_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\ntypedef struct {\n    ngx_chain_t         *free;\n    ngx_chain_t         *busy;\n} ngx_http_chunked_filter_ctx_t;\n\n\nstatic ngx_int_t ngx_http_chunked_filter_init(ngx_conf_t *cf);\nstatic ngx_chain_t *ngx_http_chunked_create_trailers(ngx_http_request_t *r,\n    ngx_http_chunked_filter_ctx_t *ctx);\n\n\nstatic ngx_http_module_t  ngx_http_chunked_filter_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    ngx_http_chunked_filter_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\nngx_module_t  ngx_http_chunked_filter_module = {\n    NGX_MODULE_V1,\n    &ngx_http_chunked_filter_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_http_output_header_filter_pt  ngx_http_next_header_filter;\nstatic ngx_http_output_body_filter_pt    ngx_http_next_body_filter;\n\n\nstatic ngx_int_t\nngx_http_chunked_header_filter(ngx_http_request_t *r)\n{\n    ngx_http_core_loc_conf_t       *clcf;\n    ngx_http_chunked_filter_ctx_t  *ctx;\n\n    if (r->headers_out.status == NGX_HTTP_NOT_MODIFIED\n        || r->headers_out.status == NGX_HTTP_NO_CONTENT\n        || r->headers_out.status < NGX_HTTP_OK\n        || r != r->main\n        || r->method == NGX_HTTP_HEAD)\n    {\n        return ngx_http_next_header_filter(r);\n    }\n\n    if (r->headers_out.content_length_n == -1\n        || r->expect_trailers)\n    {\n        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n        if (r->http_version >= NGX_HTTP_VERSION_11\n            && clcf->chunked_transfer_encoding)\n        {\n            if (r->expect_trailers) {\n                ngx_http_clear_content_length(r);\n            }\n\n            r->chunked = 1;\n\n            ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_chunked_filter_ctx_t));\n            if (ctx == NULL) {\n                return NGX_ERROR;\n            }\n\n            ngx_http_set_ctx(r, ctx, ngx_http_chunked_filter_module);\n\n        } else if (r->headers_out.content_length_n == -1) {\n            r->keepalive = 0;\n        }\n    }\n\n    return ngx_http_next_header_filter(r);\n}\n\n\nstatic ngx_int_t\nngx_http_chunked_body_filter(ngx_http_request_t *r, ngx_chain_t *in)\n{\n    u_char                         *chunk;\n    off_t                           size;\n    ngx_int_t                       rc;\n    ngx_buf_t                      *b;\n    ngx_chain_t                    *out, *cl, *tl, **ll;\n    ngx_http_chunked_filter_ctx_t  *ctx;\n\n    if (in == NULL || !r->chunked || r->header_only) {\n        return ngx_http_next_body_filter(r, in);\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_chunked_filter_module);\n\n    out = NULL;\n    ll = &out;\n\n    size = 0;\n    cl = in;\n\n    for ( ;; ) {\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"http chunk: %O\", ngx_buf_size(cl->buf));\n\n        size += ngx_buf_size(cl->buf);\n\n        if (cl->buf->flush\n            || cl->buf->sync\n            || ngx_buf_in_memory(cl->buf)\n            || cl->buf->in_file)\n        {\n            tl = ngx_alloc_chain_link(r->pool);\n            if (tl == NULL) {\n                return NGX_ERROR;\n            }\n\n            tl->buf = cl->buf;\n            *ll = tl;\n            ll = &tl->next;\n        }\n\n        if (cl->next == NULL) {\n            break;\n        }\n\n        cl = cl->next;\n    }\n\n    if (size) {\n        tl = ngx_chain_get_free_buf(r->pool, &ctx->free);\n        if (tl == NULL) {\n            return NGX_ERROR;\n        }\n\n        b = tl->buf;\n        chunk = b->start;\n\n        if (chunk == NULL) {\n            /* the \"0000000000000000\" is 64-bit hexadecimal string */\n\n            chunk = ngx_palloc(r->pool, sizeof(\"0000000000000000\" CRLF) - 1);\n            if (chunk == NULL) {\n                return NGX_ERROR;\n            }\n\n            b->start = chunk;\n            b->end = chunk + sizeof(\"0000000000000000\" CRLF) - 1;\n        }\n\n        b->tag = (ngx_buf_tag_t) &ngx_http_chunked_filter_module;\n        b->memory = 0;\n        b->temporary = 1;\n        b->pos = chunk;\n        b->last = ngx_sprintf(chunk, \"%xO\" CRLF, size);\n\n        tl->next = out;\n        out = tl;\n    }\n\n    if (cl->buf->last_buf) {\n        tl = ngx_http_chunked_create_trailers(r, ctx);\n        if (tl == NULL) {\n            return NGX_ERROR;\n        }\n\n        cl->buf->last_buf = 0;\n\n        *ll = tl;\n\n        if (size == 0) {\n            tl->buf->pos += 2;\n        }\n\n    } else if (size > 0) {\n        tl = ngx_chain_get_free_buf(r->pool, &ctx->free);\n        if (tl == NULL) {\n            return NGX_ERROR;\n        }\n\n        b = tl->buf;\n\n        b->tag = (ngx_buf_tag_t) &ngx_http_chunked_filter_module;\n        b->temporary = 0;\n        b->memory = 1;\n        b->pos = (u_char *) CRLF;\n        b->last = b->pos + 2;\n\n        *ll = tl;\n\n    } else {\n        *ll = NULL;\n    }\n\n    rc = ngx_http_next_body_filter(r, out);\n\n    ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &out,\n                            (ngx_buf_tag_t) &ngx_http_chunked_filter_module);\n\n    return rc;\n}\n\n\nstatic ngx_chain_t *\nngx_http_chunked_create_trailers(ngx_http_request_t *r,\n    ngx_http_chunked_filter_ctx_t *ctx)\n{\n    size_t            len;\n    ngx_buf_t        *b;\n    ngx_uint_t        i;\n    ngx_chain_t      *cl;\n    ngx_list_part_t  *part;\n    ngx_table_elt_t  *header;\n\n    len = 0;\n\n    part = &r->headers_out.trailers.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 (header[i].hash == 0) {\n            continue;\n        }\n\n        len += header[i].key.len + sizeof(\": \") - 1\n               + header[i].value.len + sizeof(CRLF) - 1;\n    }\n\n    cl = ngx_chain_get_free_buf(r->pool, &ctx->free);\n    if (cl == NULL) {\n        return NULL;\n    }\n\n    b = cl->buf;\n\n    b->tag = (ngx_buf_tag_t) &ngx_http_chunked_filter_module;\n    b->temporary = 0;\n    b->memory = 1;\n    b->last_buf = 1;\n\n    if (len == 0) {\n        b->pos = (u_char *) CRLF \"0\" CRLF CRLF;\n        b->last = b->pos + sizeof(CRLF \"0\" CRLF CRLF) - 1;\n        return cl;\n    }\n\n    len += sizeof(CRLF \"0\" CRLF CRLF) - 1;\n\n    b->pos = ngx_palloc(r->pool, len);\n    if (b->pos == NULL) {\n        return NULL;\n    }\n\n    b->last = b->pos;\n\n    *b->last++ = CR; *b->last++ = LF;\n    *b->last++ = '0';\n    *b->last++ = CR; *b->last++ = LF;\n\n    part = &r->headers_out.trailers.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 (header[i].hash == 0) {\n            continue;\n        }\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"http trailer: \\\"%V: %V\\\"\",\n                       &header[i].key, &header[i].value);\n\n        b->last = ngx_copy(b->last, header[i].key.data, header[i].key.len);\n        *b->last++ = ':'; *b->last++ = ' ';\n\n        b->last = ngx_copy(b->last, header[i].value.data, header[i].value.len);\n        *b->last++ = CR; *b->last++ = LF;\n    }\n\n    *b->last++ = CR; *b->last++ = LF;\n\n    return cl;\n}\n\n\nstatic ngx_int_t\nngx_http_chunked_filter_init(ngx_conf_t *cf)\n{\n    ngx_http_next_header_filter = ngx_http_top_header_filter;\n    ngx_http_top_header_filter = ngx_http_chunked_header_filter;\n\n    ngx_http_next_body_filter = ngx_http_top_body_filter;\n    ngx_http_top_body_filter = ngx_http_chunked_body_filter;\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "src/http/modules/ngx_http_dav_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\n#define NGX_HTTP_DAV_OFF             2\n\n\n#define NGX_HTTP_DAV_NO_DEPTH        -3\n#define NGX_HTTP_DAV_INVALID_DEPTH   -2\n#define NGX_HTTP_DAV_INFINITY_DEPTH  -1\n\n\ntypedef struct {\n    ngx_uint_t  methods;\n    ngx_uint_t  access;\n    ngx_uint_t  min_delete_depth;\n    ngx_flag_t  create_full_put_path;\n} ngx_http_dav_loc_conf_t;\n\n\ntypedef struct {\n    ngx_str_t   path;\n    size_t      len;\n} ngx_http_dav_copy_ctx_t;\n\n\nstatic ngx_int_t ngx_http_dav_handler(ngx_http_request_t *r);\n\nstatic void ngx_http_dav_put_handler(ngx_http_request_t *r);\n\nstatic ngx_int_t ngx_http_dav_delete_handler(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_dav_delete_path(ngx_http_request_t *r,\n    ngx_str_t *path, ngx_uint_t dir);\nstatic ngx_int_t ngx_http_dav_delete_dir(ngx_tree_ctx_t *ctx, ngx_str_t *path);\nstatic ngx_int_t ngx_http_dav_delete_file(ngx_tree_ctx_t *ctx, ngx_str_t *path);\nstatic ngx_int_t ngx_http_dav_noop(ngx_tree_ctx_t *ctx, ngx_str_t *path);\n\nstatic ngx_int_t ngx_http_dav_mkcol_handler(ngx_http_request_t *r,\n    ngx_http_dav_loc_conf_t *dlcf);\n\nstatic ngx_int_t ngx_http_dav_copy_move_handler(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_dav_copy_dir(ngx_tree_ctx_t *ctx, ngx_str_t *path);\nstatic ngx_int_t ngx_http_dav_copy_dir_time(ngx_tree_ctx_t *ctx,\n    ngx_str_t *path);\nstatic ngx_int_t ngx_http_dav_copy_tree_file(ngx_tree_ctx_t *ctx,\n    ngx_str_t *path);\n\nstatic ngx_int_t ngx_http_dav_depth(ngx_http_request_t *r, ngx_int_t dflt);\nstatic ngx_int_t ngx_http_dav_error(ngx_log_t *log, ngx_err_t err,\n    ngx_int_t not_found, char *failed, u_char *path);\nstatic ngx_int_t ngx_http_dav_location(ngx_http_request_t *r);\nstatic void *ngx_http_dav_create_loc_conf(ngx_conf_t *cf);\nstatic char *ngx_http_dav_merge_loc_conf(ngx_conf_t *cf,\n    void *parent, void *child);\nstatic ngx_int_t ngx_http_dav_init(ngx_conf_t *cf);\n\n\nstatic ngx_conf_bitmask_t  ngx_http_dav_methods_mask[] = {\n    { ngx_string(\"off\"), NGX_HTTP_DAV_OFF },\n    { ngx_string(\"put\"), NGX_HTTP_PUT },\n    { ngx_string(\"delete\"), NGX_HTTP_DELETE },\n    { ngx_string(\"mkcol\"), NGX_HTTP_MKCOL },\n    { ngx_string(\"copy\"), NGX_HTTP_COPY },\n    { ngx_string(\"move\"), NGX_HTTP_MOVE },\n    { ngx_null_string, 0 }\n};\n\n\nstatic ngx_command_t  ngx_http_dav_commands[] = {\n\n    { ngx_string(\"dav_methods\"),\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_dav_loc_conf_t, methods),\n      &ngx_http_dav_methods_mask },\n\n    { ngx_string(\"create_full_put_path\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_dav_loc_conf_t, create_full_put_path),\n      NULL },\n\n    { ngx_string(\"min_delete_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_dav_loc_conf_t, min_delete_depth),\n      NULL },\n\n    { ngx_string(\"dav_access\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,\n      ngx_conf_set_access_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_dav_loc_conf_t, access),\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_dav_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    ngx_http_dav_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    ngx_http_dav_create_loc_conf,          /* create location configuration */\n    ngx_http_dav_merge_loc_conf            /* merge location configuration */\n};\n\n\nngx_module_t  ngx_http_dav_module = {\n    NGX_MODULE_V1,\n    &ngx_http_dav_module_ctx,              /* module context */\n    ngx_http_dav_commands,                 /* 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_dav_handler(ngx_http_request_t *r)\n{\n    ngx_int_t                 rc;\n    ngx_http_dav_loc_conf_t  *dlcf;\n\n    dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);\n\n    if (!(r->method & dlcf->methods)) {\n        return NGX_DECLINED;\n    }\n\n    switch (r->method) {\n\n    case NGX_HTTP_PUT:\n\n        if (r->uri.data[r->uri.len - 1] == '/') {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"cannot PUT to a collection\");\n            return NGX_HTTP_CONFLICT;\n        }\n\n        if (r->headers_in.content_range) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"PUT with range is unsupported\");\n            return NGX_HTTP_NOT_IMPLEMENTED;\n        }\n\n        r->request_body_in_file_only = 1;\n        r->request_body_in_persistent_file = 1;\n        r->request_body_in_clean_file = 1;\n        r->request_body_file_group_access = 1;\n        r->request_body_file_log_level = 0;\n\n        rc = ngx_http_read_client_request_body(r, ngx_http_dav_put_handler);\n\n        if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {\n            return rc;\n        }\n\n        return NGX_DONE;\n\n    case NGX_HTTP_DELETE:\n\n        return ngx_http_dav_delete_handler(r);\n\n    case NGX_HTTP_MKCOL:\n\n        return ngx_http_dav_mkcol_handler(r, dlcf);\n\n    case NGX_HTTP_COPY:\n\n        return ngx_http_dav_copy_move_handler(r);\n\n    case NGX_HTTP_MOVE:\n\n        return ngx_http_dav_copy_move_handler(r);\n    }\n\n    return NGX_DECLINED;\n}\n\n\nstatic void\nngx_http_dav_put_handler(ngx_http_request_t *r)\n{\n    size_t                    root;\n    time_t                    date;\n    ngx_str_t                *temp, path;\n    ngx_uint_t                status;\n    ngx_file_info_t           fi;\n    ngx_ext_rename_file_t     ext;\n    ngx_http_dav_loc_conf_t  *dlcf;\n\n    if (r->request_body == NULL) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"PUT request body is unavailable\");\n        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return;\n    }\n\n    if (r->request_body->temp_file == NULL) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"PUT request body must be in a file\");\n        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return;\n    }\n\n    if (ngx_http_map_uri_to_path(r, &path, &root, 0) == NULL) {\n        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return;\n    }\n\n    path.len--;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http put filename: \\\"%s\\\"\", path.data);\n\n    temp = &r->request_body->temp_file->file.name;\n\n    if (ngx_file_info(path.data, &fi) == NGX_FILE_ERROR) {\n        status = NGX_HTTP_CREATED;\n\n    } else {\n        status = NGX_HTTP_NO_CONTENT;\n\n        if (ngx_is_dir(&fi)) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, NGX_EISDIR,\n                          \"\\\"%s\\\" could not be created\", path.data);\n\n            if (ngx_delete_file(temp->data) == NGX_FILE_ERROR) {\n                ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,\n                              ngx_delete_file_n \" \\\"%s\\\" failed\",\n                              temp->data);\n            }\n\n            ngx_http_finalize_request(r, NGX_HTTP_CONFLICT);\n            return;\n        }\n    }\n\n    dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);\n\n    ext.access = dlcf->access;\n    ext.path_access = dlcf->access;\n    ext.time = -1;\n    ext.create_path = dlcf->create_full_put_path;\n    ext.delete_file = 1;\n    ext.log = r->connection->log;\n\n    if (r->headers_in.date) {\n        date = ngx_parse_http_time(r->headers_in.date->value.data,\n                                   r->headers_in.date->value.len);\n\n        if (date != NGX_ERROR) {\n            ext.time = date;\n            ext.fd = r->request_body->temp_file->file.fd;\n        }\n    }\n\n    if (ngx_ext_rename_file(temp, &path, &ext) != NGX_OK) {\n        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return;\n    }\n\n    if (status == NGX_HTTP_CREATED) {\n        if (ngx_http_dav_location(r) != NGX_OK) {\n            ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n            return;\n        }\n\n        r->headers_out.content_length_n = 0;\n    }\n\n    r->headers_out.status = status;\n    r->header_only = 1;\n\n    ngx_http_finalize_request(r, ngx_http_send_header(r));\n    return;\n}\n\n\nstatic ngx_int_t\nngx_http_dav_delete_handler(ngx_http_request_t *r)\n{\n    size_t                    root;\n    ngx_err_t                 err;\n    ngx_int_t                 rc, depth;\n    ngx_uint_t                i, d, dir;\n    ngx_str_t                 path;\n    ngx_file_info_t           fi;\n    ngx_http_dav_loc_conf_t  *dlcf;\n\n    if (r->headers_in.content_length_n > 0 || r->headers_in.chunked) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"DELETE with body is unsupported\");\n        return NGX_HTTP_UNSUPPORTED_MEDIA_TYPE;\n    }\n\n    dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);\n\n    if (dlcf->min_delete_depth) {\n        d = 0;\n\n        for (i = 0; i < r->uri.len; /* void */) {\n            if (r->uri.data[i++] == '/') {\n                if (++d >= dlcf->min_delete_depth && i < r->uri.len) {\n                    goto ok;\n                }\n            }\n        }\n\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"insufficient URI depth:%i to DELETE\", d);\n        return NGX_HTTP_CONFLICT;\n    }\n\nok:\n\n    if (ngx_http_map_uri_to_path(r, &path, &root, 0) == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http delete filename: \\\"%s\\\"\", path.data);\n\n    if (ngx_link_info(path.data, &fi) == NGX_FILE_ERROR) {\n        err = ngx_errno;\n\n        rc = (err == NGX_ENOTDIR) ? NGX_HTTP_CONFLICT : NGX_HTTP_NOT_FOUND;\n\n        return ngx_http_dav_error(r->connection->log, err,\n                                  rc, ngx_link_info_n, path.data);\n    }\n\n    if (ngx_is_dir(&fi)) {\n\n        if (r->uri.data[r->uri.len - 1] != '/') {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, NGX_EISDIR,\n                          \"DELETE \\\"%s\\\" failed\", path.data);\n            return NGX_HTTP_CONFLICT;\n        }\n\n        depth = ngx_http_dav_depth(r, NGX_HTTP_DAV_INFINITY_DEPTH);\n\n        if (depth != NGX_HTTP_DAV_INFINITY_DEPTH) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"\\\"Depth\\\" header must be infinity\");\n            return NGX_HTTP_BAD_REQUEST;\n        }\n\n        path.len -= 2;  /* omit \"/\\0\" */\n\n        dir = 1;\n\n    } else {\n\n        /*\n         * we do not need to test (r->uri.data[r->uri.len - 1] == '/')\n         * because ngx_link_info(\"/file/\") returned NGX_ENOTDIR above\n         */\n\n        depth = ngx_http_dav_depth(r, 0);\n\n        if (depth != 0 && depth != NGX_HTTP_DAV_INFINITY_DEPTH) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"\\\"Depth\\\" header must be 0 or infinity\");\n            return NGX_HTTP_BAD_REQUEST;\n        }\n\n        dir = 0;\n    }\n\n    rc = ngx_http_dav_delete_path(r, &path, dir);\n\n    if (rc == NGX_OK) {\n        return NGX_HTTP_NO_CONTENT;\n    }\n\n    return rc;\n}\n\n\nstatic ngx_int_t\nngx_http_dav_delete_path(ngx_http_request_t *r, ngx_str_t *path, ngx_uint_t dir)\n{\n    char            *failed;\n    ngx_tree_ctx_t   tree;\n\n    if (dir) {\n\n        tree.init_handler = NULL;\n        tree.file_handler = ngx_http_dav_delete_file;\n        tree.pre_tree_handler = ngx_http_dav_noop;\n        tree.post_tree_handler = ngx_http_dav_delete_dir;\n        tree.spec_handler = ngx_http_dav_delete_file;\n        tree.data = NULL;\n        tree.alloc = 0;\n        tree.log = r->connection->log;\n\n        /* TODO: 207 */\n\n        if (ngx_walk_tree(&tree, path) != NGX_OK) {\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n\n        if (ngx_delete_dir(path->data) != NGX_FILE_ERROR) {\n            return NGX_OK;\n        }\n\n        failed = ngx_delete_dir_n;\n\n    } else {\n\n        if (ngx_delete_file(path->data) != NGX_FILE_ERROR) {\n            return NGX_OK;\n        }\n\n        failed = ngx_delete_file_n;\n    }\n\n    return ngx_http_dav_error(r->connection->log, ngx_errno,\n                              NGX_HTTP_NOT_FOUND, failed, path->data);\n}\n\n\nstatic ngx_int_t\nngx_http_dav_delete_dir(ngx_tree_ctx_t *ctx, ngx_str_t *path)\n{\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,\n                   \"http delete dir: \\\"%s\\\"\", path->data);\n\n    if (ngx_delete_dir(path->data) == NGX_FILE_ERROR) {\n\n        /* TODO: add to 207 */\n\n        (void) ngx_http_dav_error(ctx->log, ngx_errno, 0, ngx_delete_dir_n,\n                                  path->data);\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_dav_delete_file(ngx_tree_ctx_t *ctx, ngx_str_t *path)\n{\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,\n                   \"http delete file: \\\"%s\\\"\", path->data);\n\n    if (ngx_delete_file(path->data) == NGX_FILE_ERROR) {\n\n        /* TODO: add to 207 */\n\n        (void) ngx_http_dav_error(ctx->log, ngx_errno, 0, ngx_delete_file_n,\n                                  path->data);\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_dav_noop(ngx_tree_ctx_t *ctx, ngx_str_t *path)\n{\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_dav_mkcol_handler(ngx_http_request_t *r, ngx_http_dav_loc_conf_t *dlcf)\n{\n    u_char    *p;\n    size_t     root;\n    ngx_str_t  path;\n\n    if (r->headers_in.content_length_n > 0 || r->headers_in.chunked) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"MKCOL with body is unsupported\");\n        return NGX_HTTP_UNSUPPORTED_MEDIA_TYPE;\n    }\n\n    if (r->uri.data[r->uri.len - 1] != '/') {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"MKCOL can create a collection only\");\n        return NGX_HTTP_CONFLICT;\n    }\n\n    p = ngx_http_map_uri_to_path(r, &path, &root, 0);\n    if (p == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    *(p - 1) = '\\0';\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http mkcol path: \\\"%s\\\"\", path.data);\n\n    if (ngx_create_dir(path.data, ngx_dir_access(dlcf->access))\n        != NGX_FILE_ERROR)\n    {\n        if (ngx_http_dav_location(r) != NGX_OK) {\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n\n        return NGX_HTTP_CREATED;\n    }\n\n    return ngx_http_dav_error(r->connection->log, ngx_errno,\n                              NGX_HTTP_CONFLICT, ngx_create_dir_n, path.data);\n}\n\n\nstatic ngx_int_t\nngx_http_dav_copy_move_handler(ngx_http_request_t *r)\n{\n    u_char                   *p, *host, *last, ch;\n    size_t                    len, root;\n    ngx_err_t                 err;\n    ngx_int_t                 rc, depth;\n    ngx_uint_t                overwrite, slash, dir, flags;\n    ngx_str_t                 path, uri, duri, args;\n    ngx_tree_ctx_t            tree;\n    ngx_copy_file_t           cf;\n    ngx_file_info_t           fi;\n    ngx_table_elt_t          *dest, *over;\n    ngx_ext_rename_file_t     ext;\n    ngx_http_dav_copy_ctx_t   copy;\n    ngx_http_dav_loc_conf_t  *dlcf;\n\n    if (r->headers_in.content_length_n > 0 || r->headers_in.chunked) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"COPY and MOVE with body are unsupported\");\n        return NGX_HTTP_UNSUPPORTED_MEDIA_TYPE;\n    }\n\n    dest = r->headers_in.destination;\n\n    if (dest == NULL) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"client sent no \\\"Destination\\\" header\");\n        return NGX_HTTP_BAD_REQUEST;\n    }\n\n    p = dest->value.data;\n    /* there is always '\\0' even after empty header value */\n    if (p[0] == '/') {\n        last = p + dest->value.len;\n        goto destination_done;\n    }\n\n    len = r->headers_in.server.len;\n\n    if (len == 0) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"client sent no \\\"Host\\\" header\");\n        return NGX_HTTP_BAD_REQUEST;\n    }\n\n#if (NGX_HTTP_SSL)\n\n    if (r->connection->ssl) {\n        if (ngx_strncmp(dest->value.data, \"https://\", sizeof(\"https://\") - 1)\n            != 0)\n        {\n            goto invalid_destination;\n        }\n\n        host = dest->value.data + sizeof(\"https://\") - 1;\n\n    } else\n#endif\n    {\n        if (ngx_strncmp(dest->value.data, \"http://\", sizeof(\"http://\") - 1)\n            != 0)\n        {\n            goto invalid_destination;\n        }\n\n        host = dest->value.data + sizeof(\"http://\") - 1;\n    }\n\n    if (ngx_strncmp(host, r->headers_in.server.data, len) != 0) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"\\\"Destination\\\" URI \\\"%V\\\" is handled by \"\n                      \"different repository than the source URI\",\n                      &dest->value);\n        return NGX_HTTP_BAD_REQUEST;\n    }\n\n    last = dest->value.data + dest->value.len;\n\n    for (p = host + len; p < last; p++) {\n        if (*p == '/') {\n            goto destination_done;\n        }\n    }\n\ninvalid_destination:\n\n    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                  \"client sent invalid \\\"Destination\\\" header: \\\"%V\\\"\",\n                  &dest->value);\n    return NGX_HTTP_BAD_REQUEST;\n\ndestination_done:\n\n    duri.len = last - p;\n    duri.data = p;\n    flags = NGX_HTTP_LOG_UNSAFE;\n\n    if (ngx_http_parse_unsafe_uri(r, &duri, &args, &flags) != NGX_OK) {\n        goto invalid_destination;\n    }\n\n    if ((r->uri.data[r->uri.len - 1] == '/' && *(last - 1) != '/')\n        || (r->uri.data[r->uri.len - 1] != '/' && *(last - 1) == '/'))\n    {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"both URI \\\"%V\\\" and \\\"Destination\\\" URI \\\"%V\\\" \"\n                      \"should be either collections or non-collections\",\n                      &r->uri, &dest->value);\n        return NGX_HTTP_CONFLICT;\n    }\n\n    depth = ngx_http_dav_depth(r, NGX_HTTP_DAV_INFINITY_DEPTH);\n\n    if (depth != NGX_HTTP_DAV_INFINITY_DEPTH) {\n\n        if (r->method == NGX_HTTP_COPY) {\n            if (depth != 0) {\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                              \"\\\"Depth\\\" header must be 0 or infinity\");\n                return NGX_HTTP_BAD_REQUEST;\n            }\n\n        } else {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"\\\"Depth\\\" header must be infinity\");\n            return NGX_HTTP_BAD_REQUEST;\n        }\n    }\n\n    over = r->headers_in.overwrite;\n\n    if (over) {\n        if (over->value.len == 1) {\n            ch = over->value.data[0];\n\n            if (ch == 'T' || ch == 't') {\n                overwrite = 1;\n                goto overwrite_done;\n            }\n\n            if (ch == 'F' || ch == 'f') {\n                overwrite = 0;\n                goto overwrite_done;\n            }\n\n        }\n\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"client sent invalid \\\"Overwrite\\\" header: \\\"%V\\\"\",\n                      &over->value);\n        return NGX_HTTP_BAD_REQUEST;\n    }\n\n    overwrite = 1;\n\noverwrite_done:\n\n    if (ngx_http_map_uri_to_path(r, &path, &root, 0) == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http copy from: \\\"%s\\\"\", path.data);\n\n    uri = r->uri;\n    r->uri = duri;\n\n    if (ngx_http_map_uri_to_path(r, &copy.path, &root, 0) == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    r->uri = uri;\n\n    copy.path.len--;  /* omit \"\\0\" */\n\n    if (copy.path.data[copy.path.len - 1] == '/') {\n        slash = 1;\n        copy.path.len--;\n        copy.path.data[copy.path.len] = '\\0';\n\n    } else {\n        slash = 0;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http copy to: \\\"%s\\\"\", copy.path.data);\n\n    if (ngx_link_info(copy.path.data, &fi) == NGX_FILE_ERROR) {\n        err = ngx_errno;\n\n        if (err != NGX_ENOENT) {\n            return ngx_http_dav_error(r->connection->log, err,\n                                      NGX_HTTP_NOT_FOUND, ngx_link_info_n,\n                                      copy.path.data);\n        }\n\n        /* destination does not exist */\n\n        overwrite = 0;\n        dir = 0;\n\n    } else {\n\n        /* destination exists */\n\n        if (ngx_is_dir(&fi) && !slash) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"\\\"%V\\\" could not be %Ved to collection \\\"%V\\\"\",\n                          &r->uri, &r->method_name, &dest->value);\n            return NGX_HTTP_CONFLICT;\n        }\n\n        if (!overwrite) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, NGX_EEXIST,\n                          \"\\\"%s\\\" could not be created\", copy.path.data);\n            return NGX_HTTP_PRECONDITION_FAILED;\n        }\n\n        dir = ngx_is_dir(&fi);\n    }\n\n    if (ngx_link_info(path.data, &fi) == NGX_FILE_ERROR) {\n        return ngx_http_dav_error(r->connection->log, ngx_errno,\n                                  NGX_HTTP_NOT_FOUND, ngx_link_info_n,\n                                  path.data);\n    }\n\n    if (ngx_is_dir(&fi)) {\n\n        if (r->uri.data[r->uri.len - 1] != '/') {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"\\\"%V\\\" is collection\", &r->uri);\n            return NGX_HTTP_BAD_REQUEST;\n        }\n\n        if (overwrite) {\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"http delete: \\\"%s\\\"\", copy.path.data);\n\n            rc = ngx_http_dav_delete_path(r, &copy.path, dir);\n\n            if (rc != NGX_OK) {\n                return rc;\n            }\n        }\n    }\n\n    if (ngx_is_dir(&fi)) {\n\n        path.len -= 2;  /* omit \"/\\0\" */\n\n        if (r->method == NGX_HTTP_MOVE) {\n            if (ngx_rename_file(path.data, copy.path.data) != NGX_FILE_ERROR) {\n                return NGX_HTTP_CREATED;\n            }\n        }\n\n        if (ngx_create_dir(copy.path.data, ngx_file_access(&fi))\n            == NGX_FILE_ERROR)\n        {\n            return ngx_http_dav_error(r->connection->log, ngx_errno,\n                                      NGX_HTTP_NOT_FOUND,\n                                      ngx_create_dir_n, copy.path.data);\n        }\n\n        copy.len = path.len;\n\n        tree.init_handler = NULL;\n        tree.file_handler = ngx_http_dav_copy_tree_file;\n        tree.pre_tree_handler = ngx_http_dav_copy_dir;\n        tree.post_tree_handler = ngx_http_dav_copy_dir_time;\n        tree.spec_handler = ngx_http_dav_noop;\n        tree.data = &copy;\n        tree.alloc = 0;\n        tree.log = r->connection->log;\n\n        if (ngx_walk_tree(&tree, &path) == NGX_OK) {\n\n            if (r->method == NGX_HTTP_MOVE) {\n                rc = ngx_http_dav_delete_path(r, &path, 1);\n\n                if (rc != NGX_OK) {\n                    return rc;\n                }\n            }\n\n            return NGX_HTTP_CREATED;\n        }\n\n    } else {\n\n        if (r->method == NGX_HTTP_MOVE) {\n\n            dlcf = ngx_http_get_module_loc_conf(r, ngx_http_dav_module);\n\n            ext.access = 0;\n            ext.path_access = dlcf->access;\n            ext.time = -1;\n            ext.create_path = 1;\n            ext.delete_file = 0;\n            ext.log = r->connection->log;\n\n            if (ngx_ext_rename_file(&path, &copy.path, &ext) == NGX_OK) {\n                return NGX_HTTP_NO_CONTENT;\n            }\n\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n\n        cf.size = ngx_file_size(&fi);\n        cf.buf_size = 0;\n        cf.access = ngx_file_access(&fi);\n        cf.time = ngx_file_mtime(&fi);\n        cf.log = r->connection->log;\n\n        if (ngx_copy_file(path.data, copy.path.data, &cf) == NGX_OK) {\n            return NGX_HTTP_NO_CONTENT;\n        }\n    }\n\n    return NGX_HTTP_INTERNAL_SERVER_ERROR;\n}\n\n\nstatic ngx_int_t\nngx_http_dav_copy_dir(ngx_tree_ctx_t *ctx, ngx_str_t *path)\n{\n    u_char                   *p, *dir;\n    size_t                    len;\n    ngx_http_dav_copy_ctx_t  *copy;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,\n                   \"http copy dir: \\\"%s\\\"\", path->data);\n\n    copy = ctx->data;\n\n    len = copy->path.len + path->len;\n\n    dir = ngx_alloc(len + 1, ctx->log);\n    if (dir == NULL) {\n        return NGX_ABORT;\n    }\n\n    p = ngx_cpymem(dir, copy->path.data, copy->path.len);\n    (void) ngx_cpystrn(p, path->data + copy->len, path->len - copy->len + 1);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,\n                   \"http copy dir to: \\\"%s\\\"\", dir);\n\n    if (ngx_create_dir(dir, ngx_dir_access(ctx->access)) == NGX_FILE_ERROR) {\n        (void) ngx_http_dav_error(ctx->log, ngx_errno, 0, ngx_create_dir_n,\n                                  dir);\n    }\n\n    ngx_free(dir);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_dav_copy_dir_time(ngx_tree_ctx_t *ctx, ngx_str_t *path)\n{\n    u_char                   *p, *dir;\n    size_t                    len;\n    ngx_http_dav_copy_ctx_t  *copy;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,\n                   \"http copy dir time: \\\"%s\\\"\", path->data);\n\n    copy = ctx->data;\n\n    len = copy->path.len + path->len;\n\n    dir = ngx_alloc(len + 1, ctx->log);\n    if (dir == NULL) {\n        return NGX_ABORT;\n    }\n\n    p = ngx_cpymem(dir, copy->path.data, copy->path.len);\n    (void) ngx_cpystrn(p, path->data + copy->len, path->len - copy->len + 1);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,\n                   \"http copy dir time to: \\\"%s\\\"\", dir);\n\n#if (NGX_WIN32)\n    {\n    ngx_fd_t  fd;\n\n    fd = ngx_open_file(dir, NGX_FILE_RDWR, NGX_FILE_OPEN, 0);\n\n    if (fd == NGX_INVALID_FILE) {\n        (void) ngx_http_dav_error(ctx->log, ngx_errno, 0, ngx_open_file_n, dir);\n        goto failed;\n    }\n\n    if (ngx_set_file_time(NULL, fd, ctx->mtime) != NGX_OK) {\n        ngx_log_error(NGX_LOG_ALERT, ctx->log, ngx_errno,\n                      ngx_set_file_time_n \" \\\"%s\\\" failed\", dir);\n    }\n\n    if (ngx_close_file(fd) == NGX_FILE_ERROR) {\n        ngx_log_error(NGX_LOG_ALERT, ctx->log, ngx_errno,\n                      ngx_close_file_n \" \\\"%s\\\" failed\", dir);\n    }\n    }\n\nfailed:\n\n#else\n\n    if (ngx_set_file_time(dir, 0, ctx->mtime) != NGX_OK) {\n        ngx_log_error(NGX_LOG_ALERT, ctx->log, ngx_errno,\n                      ngx_set_file_time_n \" \\\"%s\\\" failed\", dir);\n    }\n\n#endif\n\n    ngx_free(dir);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_dav_copy_tree_file(ngx_tree_ctx_t *ctx, ngx_str_t *path)\n{\n    u_char                   *p, *file;\n    size_t                    len;\n    ngx_copy_file_t           cf;\n    ngx_http_dav_copy_ctx_t  *copy;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,\n                   \"http copy file: \\\"%s\\\"\", path->data);\n\n    copy = ctx->data;\n\n    len = copy->path.len + path->len;\n\n    file = ngx_alloc(len + 1, ctx->log);\n    if (file == NULL) {\n        return NGX_ABORT;\n    }\n\n    p = ngx_cpymem(file, copy->path.data, copy->path.len);\n    (void) ngx_cpystrn(p, path->data + copy->len, path->len - copy->len + 1);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,\n                   \"http copy file to: \\\"%s\\\"\", file);\n\n    cf.size = ctx->size;\n    cf.buf_size = 0;\n    cf.access = ctx->access;\n    cf.time = ctx->mtime;\n    cf.log = ctx->log;\n\n    (void) ngx_copy_file(path->data, file, &cf);\n\n    ngx_free(file);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_dav_depth(ngx_http_request_t *r, ngx_int_t dflt)\n{\n    ngx_table_elt_t  *depth;\n\n    depth = r->headers_in.depth;\n\n    if (depth == NULL) {\n        return dflt;\n    }\n\n    if (depth->value.len == 1) {\n\n        if (depth->value.data[0] == '0') {\n            return 0;\n        }\n\n        if (depth->value.data[0] == '1') {\n            return 1;\n        }\n\n    } else {\n\n        if (depth->value.len == sizeof(\"infinity\") - 1\n            && ngx_strcmp(depth->value.data, \"infinity\") == 0)\n        {\n            return NGX_HTTP_DAV_INFINITY_DEPTH;\n        }\n    }\n\n    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                  \"client sent invalid \\\"Depth\\\" header: \\\"%V\\\"\",\n                  &depth->value);\n\n    return NGX_HTTP_DAV_INVALID_DEPTH;\n}\n\n\nstatic ngx_int_t\nngx_http_dav_error(ngx_log_t *log, ngx_err_t err, ngx_int_t not_found,\n    char *failed, u_char *path)\n{\n    ngx_int_t   rc;\n    ngx_uint_t  level;\n\n    if (err == NGX_ENOENT || err == NGX_ENOTDIR || err == NGX_ENAMETOOLONG) {\n        level = NGX_LOG_ERR;\n        rc = not_found;\n\n    } else if (err == NGX_EACCES || err == NGX_EPERM) {\n        level = NGX_LOG_ERR;\n        rc = NGX_HTTP_FORBIDDEN;\n\n    } else if (err == NGX_EEXIST) {\n        level = NGX_LOG_ERR;\n        rc = NGX_HTTP_NOT_ALLOWED;\n\n    } else if (err == NGX_ENOSPC) {\n        level = NGX_LOG_CRIT;\n        rc = NGX_HTTP_INSUFFICIENT_STORAGE;\n\n    } else {\n        level = NGX_LOG_CRIT;\n        rc = NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    ngx_log_error(level, log, err, \"%s \\\"%s\\\" failed\", failed, path);\n\n    return rc;\n}\n\n\nstatic ngx_int_t\nngx_http_dav_location(ngx_http_request_t *r)\n{\n    u_char     *p;\n    size_t      len;\n    uintptr_t   escape;\n\n    r->headers_out.location = ngx_list_push(&r->headers_out.headers);\n    if (r->headers_out.location == NULL) {\n        return NGX_ERROR;\n    }\n\n    r->headers_out.location->hash = 1;\n    r->headers_out.location->next = NULL;\n    ngx_str_set(&r->headers_out.location->key, \"Location\");\n\n    escape = 2 * ngx_escape_uri(NULL, r->uri.data, r->uri.len, NGX_ESCAPE_URI);\n\n    if (escape) {\n        len = r->uri.len + escape;\n\n        p = ngx_pnalloc(r->pool, len);\n        if (p == NULL) {\n            ngx_http_clear_location(r);\n            return NGX_ERROR;\n        }\n\n        r->headers_out.location->value.len = len;\n        r->headers_out.location->value.data = p;\n\n        ngx_escape_uri(p, r->uri.data, r->uri.len, NGX_ESCAPE_URI);\n\n    } else {\n        r->headers_out.location->value = r->uri;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void *\nngx_http_dav_create_loc_conf(ngx_conf_t *cf)\n{\n    ngx_http_dav_loc_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_dav_loc_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc():\n     *\n     *     conf->methods = 0;\n     */\n\n    conf->min_delete_depth = NGX_CONF_UNSET_UINT;\n    conf->access = NGX_CONF_UNSET_UINT;\n    conf->create_full_put_path = NGX_CONF_UNSET;\n\n    return conf;\n}\n\n\nstatic char *\nngx_http_dav_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_http_dav_loc_conf_t  *prev = parent;\n    ngx_http_dav_loc_conf_t  *conf = child;\n\n    ngx_conf_merge_bitmask_value(conf->methods, prev->methods,\n                         (NGX_CONF_BITMASK_SET|NGX_HTTP_DAV_OFF));\n\n    ngx_conf_merge_uint_value(conf->min_delete_depth,\n                         prev->min_delete_depth, 0);\n\n    ngx_conf_merge_uint_value(conf->access, prev->access, 0600);\n\n    ngx_conf_merge_value(conf->create_full_put_path,\n                         prev->create_full_put_path, 0);\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_dav_init(ngx_conf_t *cf)\n{\n    ngx_http_handler_pt        *h;\n    ngx_http_core_main_conf_t  *cmcf;\n\n    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);\n\n    h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);\n    if (h == NULL) {\n        return NGX_ERROR;\n    }\n\n    *h = ngx_http_dav_handler;\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "src/http/modules/ngx_http_degradation_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\ntypedef struct {\n    size_t      sbrk_size;\n} ngx_http_degradation_main_conf_t;\n\n\ntypedef struct {\n    ngx_uint_t  degrade;\n} ngx_http_degradation_loc_conf_t;\n\n\nstatic ngx_conf_enum_t  ngx_http_degrade[] = {\n    { ngx_string(\"204\"), 204 },\n    { ngx_string(\"444\"), 444 },\n    { ngx_null_string, 0 }\n};\n\n\nstatic void *ngx_http_degradation_create_main_conf(ngx_conf_t *cf);\nstatic void *ngx_http_degradation_create_loc_conf(ngx_conf_t *cf);\nstatic char *ngx_http_degradation_merge_loc_conf(ngx_conf_t *cf, void *parent,\n    void *child);\nstatic char *ngx_http_degradation(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic ngx_int_t ngx_http_degradation_init(ngx_conf_t *cf);\n\n\nstatic ngx_command_t  ngx_http_degradation_commands[] = {\n\n    { ngx_string(\"degradation\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,\n      ngx_http_degradation,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"degrade\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_enum_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_degradation_loc_conf_t, degrade),\n      &ngx_http_degrade },\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_degradation_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    ngx_http_degradation_init,             /* postconfiguration */\n\n    ngx_http_degradation_create_main_conf, /* create main configuration */\n    NULL,                                  /* init main configuration */\n\n    NULL,                                  /* create server configuration */\n    NULL,                                  /* merge server configuration */\n\n    ngx_http_degradation_create_loc_conf,  /* create location configuration */\n    ngx_http_degradation_merge_loc_conf    /* merge location configuration */\n};\n\n\nngx_module_t  ngx_http_degradation_module = {\n    NGX_MODULE_V1,\n    &ngx_http_degradation_module_ctx,      /* module context */\n    ngx_http_degradation_commands,         /* 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_degradation_handler(ngx_http_request_t *r)\n{\n    ngx_http_degradation_loc_conf_t  *dlcf;\n\n    dlcf = ngx_http_get_module_loc_conf(r, ngx_http_degradation_module);\n\n    if (dlcf->degrade && ngx_http_degraded(r)) {\n        return dlcf->degrade;\n    }\n\n    return NGX_DECLINED;\n}\n\n\nngx_uint_t\nngx_http_degraded(ngx_http_request_t *r)\n{\n    time_t                             now;\n    ngx_uint_t                         log;\n    static size_t                      sbrk_size;\n    static time_t                      sbrk_time;\n    ngx_http_degradation_main_conf_t  *dmcf;\n\n    dmcf = ngx_http_get_module_main_conf(r, ngx_http_degradation_module);\n\n    if (dmcf->sbrk_size) {\n\n        log = 0;\n        now = ngx_time();\n\n        /* lock mutex */\n\n        if (now != sbrk_time) {\n\n            /*\n             * ELF/i386 is loaded at 0x08000000, 128M\n             * ELF/amd64 is loaded at 0x00400000, 4M\n             *\n             * use a function address to subtract the loading address\n             */\n\n            sbrk_size = (size_t) sbrk(0) - ((uintptr_t) ngx_palloc & ~0x3FFFFF);\n            sbrk_time = now;\n            log = 1;\n        }\n\n        /* unlock mutex */\n\n        if (sbrk_size >= dmcf->sbrk_size) {\n            if (log) {\n                ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0,\n                              \"degradation sbrk:%uzM\",\n                              sbrk_size / (1024 * 1024));\n            }\n\n            return 1;\n        }\n    }\n\n    return 0;\n}\n\n\nstatic void *\nngx_http_degradation_create_main_conf(ngx_conf_t *cf)\n{\n    ngx_http_degradation_main_conf_t  *dmcf;\n\n    dmcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_degradation_main_conf_t));\n    if (dmcf == NULL) {\n        return NULL;\n    }\n\n    return dmcf;\n}\n\n\nstatic void *\nngx_http_degradation_create_loc_conf(ngx_conf_t *cf)\n{\n    ngx_http_degradation_loc_conf_t  *conf;\n\n    conf = ngx_palloc(cf->pool, sizeof(ngx_http_degradation_loc_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    conf->degrade = NGX_CONF_UNSET_UINT;\n\n    return conf;\n}\n\n\nstatic char *\nngx_http_degradation_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_http_degradation_loc_conf_t  *prev = parent;\n    ngx_http_degradation_loc_conf_t  *conf = child;\n\n    ngx_conf_merge_uint_value(conf->degrade, prev->degrade, 0);\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_degradation(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_degradation_main_conf_t  *dmcf = conf;\n\n    ngx_str_t  *value, s;\n\n    value = cf->args->elts;\n\n    if (ngx_strncmp(value[1].data, \"sbrk=\", 5) == 0) {\n\n        s.len = value[1].len - 5;\n        s.data = value[1].data + 5;\n\n        dmcf->sbrk_size = ngx_parse_size(&s);\n        if (dmcf->sbrk_size == (size_t) NGX_ERROR) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"invalid sbrk size \\\"%V\\\"\", &value[1]);\n            return NGX_CONF_ERROR;\n        }\n\n        return NGX_CONF_OK;\n    }\n\n    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                       \"invalid parameter \\\"%V\\\"\", &value[1]);\n\n    return NGX_CONF_ERROR;\n}\n\n\nstatic ngx_int_t\nngx_http_degradation_init(ngx_conf_t *cf)\n{\n    ngx_http_handler_pt        *h;\n    ngx_http_core_main_conf_t  *cmcf;\n\n    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);\n\n    h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers);\n    if (h == NULL) {\n        return NGX_ERROR;\n    }\n\n    *h = ngx_http_degradation_handler;\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "src/http/modules/ngx_http_empty_gif_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\nstatic char *ngx_http_empty_gif(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n\nstatic ngx_command_t  ngx_http_empty_gif_commands[] = {\n\n    { ngx_string(\"empty_gif\"),\n      NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,\n      ngx_http_empty_gif,\n      0,\n      0,\n      NULL },\n\n      ngx_null_command\n};\n\n\n/* the minimal single pixel transparent GIF, 43 bytes */\n\nstatic u_char  ngx_empty_gif[] = {\n\n    'G', 'I', 'F', '8', '9', 'a',  /* header                                 */\n\n                                   /* logical screen descriptor              */\n    0x01, 0x00,                    /* logical screen width                   */\n    0x01, 0x00,                    /* logical screen height                  */\n    0x80,                          /* global 1-bit color table               */\n    0x01,                          /* background color #1                    */\n    0x00,                          /* no aspect ratio                        */\n\n                                   /* global color table                     */\n    0x00, 0x00, 0x00,              /* #0: black                              */\n    0xff, 0xff, 0xff,              /* #1: white                              */\n\n                                   /* graphic control extension              */\n    0x21,                          /* extension introducer                   */\n    0xf9,                          /* graphic control label                  */\n    0x04,                          /* block size                             */\n    0x01,                          /* transparent color is given,            */\n                                   /*     no disposal specified,             */\n                                   /*     user input is not expected         */\n    0x00, 0x00,                    /* delay time                             */\n    0x01,                          /* transparent color #1                   */\n    0x00,                          /* block terminator                       */\n\n                                   /* image descriptor                       */\n    0x2c,                          /* image separator                        */\n    0x00, 0x00,                    /* image left position                    */\n    0x00, 0x00,                    /* image top position                     */\n    0x01, 0x00,                    /* image width                            */\n    0x01, 0x00,                    /* image height                           */\n    0x00,                          /* no local color table, no interlaced    */\n\n                                   /* table based image data                 */\n    0x02,                          /* LZW minimum code size,                 */\n                                   /*     must be at least 2-bit             */\n    0x02,                          /* block size                             */\n    0x4c, 0x01,                    /* compressed bytes 01_001_100, 0000000_1 */\n                                   /* 100: clear code                        */\n                                   /* 001: 1                                 */\n                                   /* 101: end of information code           */\n    0x00,                          /* block terminator                       */\n\n    0x3B                           /* trailer                                */\n};\n\n\nstatic ngx_http_module_t  ngx_http_empty_gif_module_ctx = {\n    NULL,                          /* preconfiguration */\n    NULL,                          /* 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\nngx_module_t  ngx_http_empty_gif_module = {\n    NGX_MODULE_V1,\n    &ngx_http_empty_gif_module_ctx, /* module context */\n    ngx_http_empty_gif_commands,   /* 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_str_t  ngx_http_gif_type = ngx_string(\"image/gif\");\n\n\nstatic ngx_int_t\nngx_http_empty_gif_handler(ngx_http_request_t *r)\n{\n    ngx_http_complex_value_t  cv;\n\n    if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {\n        return NGX_HTTP_NOT_ALLOWED;\n    }\n\n    ngx_memzero(&cv, sizeof(ngx_http_complex_value_t));\n\n    cv.value.len = sizeof(ngx_empty_gif);\n    cv.value.data = ngx_empty_gif;\n    r->headers_out.last_modified_time = 23349600;\n\n    return ngx_http_send_response(r, NGX_HTTP_OK, &ngx_http_gif_type, &cv);\n}\n\n\nstatic char *\nngx_http_empty_gif(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_core_loc_conf_t  *clcf;\n\n    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);\n    clcf->handler = ngx_http_empty_gif_handler;\n\n    return NGX_CONF_OK;\n}\n"
  },
  {
    "path": "src/http/modules/ngx_http_fastcgi_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\ntypedef struct {\n    ngx_array_t                    caches;  /* ngx_http_file_cache_t * */\n} ngx_http_fastcgi_main_conf_t;\n\n\ntypedef struct {\n    ngx_array_t                   *flushes;\n    ngx_array_t                   *lengths;\n    ngx_array_t                   *values;\n    ngx_uint_t                     number;\n    ngx_hash_t                     hash;\n} ngx_http_fastcgi_params_t;\n\n\ntypedef struct {\n    ngx_http_upstream_conf_t       upstream;\n\n    ngx_str_t                      index;\n\n    ngx_http_fastcgi_params_t      params;\n#if (NGX_HTTP_CACHE)\n    ngx_http_fastcgi_params_t      params_cache;\n#endif\n\n    ngx_array_t                   *params_source;\n    ngx_array_t                   *catch_stderr;\n\n    ngx_array_t                   *fastcgi_lengths;\n    ngx_array_t                   *fastcgi_values;\n\n    ngx_flag_t                     keep_conn;\n\n#if (T_NGX_SOCKET_BUFFER)\n    size_t                         sndbuf;\n    size_t                         rcvbuf;\n#endif\n\n#if (NGX_HTTP_CACHE)\n    ngx_http_complex_value_t       cache_key;\n#endif\n\n#if (NGX_PCRE)\n    ngx_regex_t                   *split_regex;\n    ngx_str_t                      split_name;\n#endif\n} ngx_http_fastcgi_loc_conf_t;\n\n\ntypedef enum {\n    ngx_http_fastcgi_st_version = 0,\n    ngx_http_fastcgi_st_type,\n    ngx_http_fastcgi_st_request_id_hi,\n    ngx_http_fastcgi_st_request_id_lo,\n    ngx_http_fastcgi_st_content_length_hi,\n    ngx_http_fastcgi_st_content_length_lo,\n    ngx_http_fastcgi_st_padding_length,\n    ngx_http_fastcgi_st_reserved,\n    ngx_http_fastcgi_st_data,\n    ngx_http_fastcgi_st_padding\n} ngx_http_fastcgi_state_e;\n\n\ntypedef struct {\n    u_char                        *start;\n    u_char                        *end;\n} ngx_http_fastcgi_split_part_t;\n\n\ntypedef struct {\n    ngx_http_fastcgi_state_e       state;\n    u_char                        *pos;\n    u_char                        *last;\n    ngx_uint_t                     type;\n    size_t                         length;\n    size_t                         padding;\n\n    off_t                          rest;\n\n    ngx_chain_t                   *free;\n    ngx_chain_t                   *busy;\n\n    unsigned                       fastcgi_stdout:1;\n    unsigned                       large_stderr:1;\n    unsigned                       header_sent:1;\n    unsigned                       closed:1;\n\n    ngx_array_t                   *split_parts;\n\n    ngx_str_t                      script_name;\n    ngx_str_t                      path_info;\n} ngx_http_fastcgi_ctx_t;\n\n\n#define NGX_HTTP_FASTCGI_RESPONDER      1\n\n#define NGX_HTTP_FASTCGI_KEEP_CONN      1\n\n#define NGX_HTTP_FASTCGI_BEGIN_REQUEST  1\n#define NGX_HTTP_FASTCGI_ABORT_REQUEST  2\n#define NGX_HTTP_FASTCGI_END_REQUEST    3\n#define NGX_HTTP_FASTCGI_PARAMS         4\n#define NGX_HTTP_FASTCGI_STDIN          5\n#define NGX_HTTP_FASTCGI_STDOUT         6\n#define NGX_HTTP_FASTCGI_STDERR         7\n#define NGX_HTTP_FASTCGI_DATA           8\n\n\ntypedef struct {\n    u_char  version;\n    u_char  type;\n    u_char  request_id_hi;\n    u_char  request_id_lo;\n    u_char  content_length_hi;\n    u_char  content_length_lo;\n    u_char  padding_length;\n    u_char  reserved;\n} ngx_http_fastcgi_header_t;\n\n\ntypedef struct {\n    u_char  role_hi;\n    u_char  role_lo;\n    u_char  flags;\n    u_char  reserved[5];\n} ngx_http_fastcgi_begin_request_t;\n\n\ntypedef struct {\n    u_char  version;\n    u_char  type;\n    u_char  request_id_hi;\n    u_char  request_id_lo;\n} ngx_http_fastcgi_header_small_t;\n\n\ntypedef struct {\n    ngx_http_fastcgi_header_t         h0;\n    ngx_http_fastcgi_begin_request_t  br;\n    ngx_http_fastcgi_header_small_t   h1;\n} ngx_http_fastcgi_request_start_t;\n\n\nstatic ngx_int_t ngx_http_fastcgi_eval(ngx_http_request_t *r,\n    ngx_http_fastcgi_loc_conf_t *flcf);\n#if (NGX_HTTP_CACHE)\nstatic ngx_int_t ngx_http_fastcgi_create_key(ngx_http_request_t *r);\n#endif\nstatic ngx_int_t ngx_http_fastcgi_create_request(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_fastcgi_reinit_request(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_fastcgi_body_output_filter(void *data,\n    ngx_chain_t *in);\nstatic ngx_int_t ngx_http_fastcgi_process_header(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_fastcgi_input_filter_init(void *data);\nstatic ngx_int_t ngx_http_fastcgi_input_filter(ngx_event_pipe_t *p,\n    ngx_buf_t *buf);\nstatic ngx_int_t ngx_http_fastcgi_non_buffered_filter(void *data,\n    ssize_t bytes);\nstatic ngx_int_t ngx_http_fastcgi_process_record(ngx_http_request_t *r,\n    ngx_http_fastcgi_ctx_t *f);\nstatic void ngx_http_fastcgi_abort_request(ngx_http_request_t *r);\nstatic void ngx_http_fastcgi_finalize_request(ngx_http_request_t *r,\n    ngx_int_t rc);\n\nstatic ngx_int_t ngx_http_fastcgi_add_variables(ngx_conf_t *cf);\nstatic void *ngx_http_fastcgi_create_main_conf(ngx_conf_t *cf);\nstatic void *ngx_http_fastcgi_create_loc_conf(ngx_conf_t *cf);\nstatic char *ngx_http_fastcgi_merge_loc_conf(ngx_conf_t *cf,\n    void *parent, void *child);\nstatic ngx_int_t ngx_http_fastcgi_init_params(ngx_conf_t *cf,\n    ngx_http_fastcgi_loc_conf_t *conf, ngx_http_fastcgi_params_t *params,\n    ngx_keyval_t *default_params);\n\nstatic ngx_int_t ngx_http_fastcgi_script_name_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_fastcgi_path_info_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_http_fastcgi_ctx_t *ngx_http_fastcgi_split(ngx_http_request_t *r,\n    ngx_http_fastcgi_loc_conf_t *flcf);\n\nstatic char *ngx_http_fastcgi_pass(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_http_fastcgi_split_path_info(ngx_conf_t *cf,\n    ngx_command_t *cmd, void *conf);\nstatic char *ngx_http_fastcgi_store(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n#if (NGX_HTTP_CACHE)\nstatic char *ngx_http_fastcgi_cache(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_http_fastcgi_cache_key(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n#endif\n\nstatic char *ngx_http_fastcgi_lowat_check(ngx_conf_t *cf, void *post,\n    void *data);\n\n\nstatic ngx_conf_post_t  ngx_http_fastcgi_lowat_post =\n    { ngx_http_fastcgi_lowat_check };\n\n\nstatic ngx_conf_bitmask_t  ngx_http_fastcgi_next_upstream_masks[] = {\n    { ngx_string(\"error\"), NGX_HTTP_UPSTREAM_FT_ERROR },\n    { ngx_string(\"timeout\"), NGX_HTTP_UPSTREAM_FT_TIMEOUT },\n    { ngx_string(\"invalid_header\"), NGX_HTTP_UPSTREAM_FT_INVALID_HEADER },\n    { ngx_string(\"non_idempotent\"), NGX_HTTP_UPSTREAM_FT_NON_IDEMPOTENT },\n    { ngx_string(\"http_500\"), NGX_HTTP_UPSTREAM_FT_HTTP_500 },\n    { ngx_string(\"http_503\"), NGX_HTTP_UPSTREAM_FT_HTTP_503 },\n    { ngx_string(\"http_403\"), NGX_HTTP_UPSTREAM_FT_HTTP_403 },\n    { ngx_string(\"http_404\"), NGX_HTTP_UPSTREAM_FT_HTTP_404 },\n    { ngx_string(\"http_429\"), NGX_HTTP_UPSTREAM_FT_HTTP_429 },\n    { ngx_string(\"updating\"), NGX_HTTP_UPSTREAM_FT_UPDATING },\n    { ngx_string(\"off\"), NGX_HTTP_UPSTREAM_FT_OFF },\n    { ngx_null_string, 0 }\n};\n\n\nngx_module_t  ngx_http_fastcgi_module;\n\n\nstatic ngx_command_t  ngx_http_fastcgi_commands[] = {\n\n    { ngx_string(\"fastcgi_pass\"),\n      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,\n      ngx_http_fastcgi_pass,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"fastcgi_index\"),\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_fastcgi_loc_conf_t, index),\n      NULL },\n\n    { ngx_string(\"fastcgi_split_path_info\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_http_fastcgi_split_path_info,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"fastcgi_store\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_http_fastcgi_store,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"fastcgi_store_access\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,\n      ngx_conf_set_access_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.store_access),\n      NULL },\n\n    { ngx_string(\"fastcgi_buffering\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.buffering),\n      NULL },\n\n    { ngx_string(\"fastcgi_request_buffering\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.request_buffering),\n      NULL },\n\n    { ngx_string(\"fastcgi_ignore_client_abort\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.ignore_client_abort),\n      NULL },\n\n    { ngx_string(\"fastcgi_bind\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,\n      ngx_http_upstream_bind_set_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.local),\n      NULL },\n\n    { ngx_string(\"fastcgi_socket_keepalive\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.socket_keepalive),\n      NULL },\n\n    { ngx_string(\"fastcgi_connect_timeout\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.connect_timeout),\n      NULL },\n\n    { ngx_string(\"fastcgi_send_timeout\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.send_timeout),\n      NULL },\n\n    { ngx_string(\"fastcgi_send_lowat\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.send_lowat),\n      &ngx_http_fastcgi_lowat_post },\n\n    { ngx_string(\"fastcgi_buffer_size\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.buffer_size),\n      NULL },\n\n    { ngx_string(\"fastcgi_pass_request_headers\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.pass_request_headers),\n      NULL },\n\n    { ngx_string(\"fastcgi_pass_request_body\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.pass_request_body),\n      NULL },\n\n    { ngx_string(\"fastcgi_intercept_errors\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.intercept_errors),\n      NULL },\n\n    { ngx_string(\"fastcgi_read_timeout\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.read_timeout),\n      NULL },\n\n    { ngx_string(\"fastcgi_buffers\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,\n      ngx_conf_set_bufs_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.bufs),\n      NULL },\n\n    { ngx_string(\"fastcgi_busy_buffers_size\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.busy_buffers_size_conf),\n      NULL },\n\n    { ngx_string(\"fastcgi_force_ranges\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.force_ranges),\n      NULL },\n\n    { ngx_string(\"fastcgi_limit_rate\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.limit_rate),\n      NULL },\n\n#if (NGX_HTTP_CACHE)\n\n    { ngx_string(\"fastcgi_cache\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_http_fastcgi_cache,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"fastcgi_cache_key\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_http_fastcgi_cache_key,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"fastcgi_cache_path\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_2MORE,\n      ngx_http_file_cache_set_slot,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      offsetof(ngx_http_fastcgi_main_conf_t, caches),\n      &ngx_http_fastcgi_module },\n\n    { ngx_string(\"fastcgi_cache_bypass\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,\n      ngx_http_set_predicate_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_bypass),\n      NULL },\n\n    { ngx_string(\"fastcgi_no_cache\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,\n      ngx_http_set_predicate_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.no_cache),\n      NULL },\n\n    { ngx_string(\"fastcgi_cache_valid\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,\n      ngx_http_file_cache_valid_set_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_valid),\n      NULL },\n\n    { ngx_string(\"fastcgi_cache_min_uses\"),\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_fastcgi_loc_conf_t, upstream.cache_min_uses),\n      NULL },\n\n    { ngx_string(\"fastcgi_cache_max_range_offset\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_off_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_max_range_offset),\n      NULL },\n\n    { ngx_string(\"fastcgi_cache_use_stale\"),\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_fastcgi_loc_conf_t, upstream.cache_use_stale),\n      &ngx_http_fastcgi_next_upstream_masks },\n\n    { ngx_string(\"fastcgi_cache_methods\"),\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_fastcgi_loc_conf_t, upstream.cache_methods),\n      &ngx_http_upstream_cache_method_mask },\n\n    { ngx_string(\"fastcgi_cache_lock\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_lock),\n      NULL },\n\n    { ngx_string(\"fastcgi_cache_lock_timeout\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_lock_timeout),\n      NULL },\n\n    { ngx_string(\"fastcgi_cache_lock_age\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_lock_age),\n      NULL },\n\n    { ngx_string(\"fastcgi_cache_revalidate\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_revalidate),\n      NULL },\n\n    { ngx_string(\"fastcgi_cache_background_update\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.cache_background_update),\n      NULL },\n\n#endif\n\n    { ngx_string(\"fastcgi_temp_path\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1234,\n      ngx_conf_set_path_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.temp_path),\n      NULL },\n\n    { ngx_string(\"fastcgi_max_temp_file_size\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.max_temp_file_size_conf),\n      NULL },\n\n    { ngx_string(\"fastcgi_temp_file_write_size\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.temp_file_write_size_conf),\n      NULL },\n\n    { ngx_string(\"fastcgi_next_upstream\"),\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_fastcgi_loc_conf_t, upstream.next_upstream),\n      &ngx_http_fastcgi_next_upstream_masks },\n\n    { ngx_string(\"fastcgi_next_upstream_tries\"),\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_fastcgi_loc_conf_t, upstream.next_upstream_tries),\n      NULL },\n\n#if (T_UPSTREAM_TRIES)\n    { ngx_string(\"fastcgi_upstream_tries\"),\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_fastcgi_loc_conf_t, upstream.next_upstream_tries),\n      NULL },\n#endif\n\n    { ngx_string(\"fastcgi_next_upstream_timeout\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_fastcgi_loc_conf_t, upstream.next_upstream_timeout),\n      NULL },\n\n    { ngx_string(\"fastcgi_param\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE23,\n      ngx_http_upstream_param_set_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_fastcgi_loc_conf_t, params_source),\n      NULL },\n\n    { ngx_string(\"fastcgi_pass_header\"),\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_fastcgi_loc_conf_t, upstream.pass_headers),\n      NULL },\n\n    { ngx_string(\"fastcgi_hide_header\"),\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_fastcgi_loc_conf_t, upstream.hide_headers),\n      NULL },\n\n    { ngx_string(\"fastcgi_ignore_headers\"),\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_fastcgi_loc_conf_t, upstream.ignore_headers),\n      &ngx_http_upstream_ignore_headers_masks },\n\n    { ngx_string(\"fastcgi_catch_stderr\"),\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_fastcgi_loc_conf_t, catch_stderr),\n      NULL },\n\n    { ngx_string(\"fastcgi_keep_conn\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_fastcgi_loc_conf_t, keep_conn),\n      NULL },\n\n#if (T_NGX_SOCKET_BUFFER)\n    { ngx_string(\"fastcgi_sndbuf_size\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_fastcgi_loc_conf_t, sndbuf),\n      NULL },\n\n    { ngx_string(\"fastcgi_rcvbuf_size\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_fastcgi_loc_conf_t, rcvbuf),\n      NULL },\n#endif\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_fastcgi_module_ctx = {\n    ngx_http_fastcgi_add_variables,        /* preconfiguration */\n    NULL,                                  /* postconfiguration */\n\n    ngx_http_fastcgi_create_main_conf,     /* create main configuration */\n    NULL,                                  /* init main configuration */\n\n    NULL,                                  /* create server configuration */\n    NULL,                                  /* merge server configuration */\n\n    ngx_http_fastcgi_create_loc_conf,      /* create location configuration */\n    ngx_http_fastcgi_merge_loc_conf        /* merge location configuration */\n};\n\n\nngx_module_t  ngx_http_fastcgi_module = {\n    NGX_MODULE_V1,\n    &ngx_http_fastcgi_module_ctx,          /* module context */\n    ngx_http_fastcgi_commands,             /* 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_http_fastcgi_request_start_t  ngx_http_fastcgi_request_start = {\n    { 1,                                               /* version */\n      NGX_HTTP_FASTCGI_BEGIN_REQUEST,                  /* type */\n      0,                                               /* request_id_hi */\n      1,                                               /* request_id_lo */\n      0,                                               /* content_length_hi */\n      sizeof(ngx_http_fastcgi_begin_request_t),        /* content_length_lo */\n      0,                                               /* padding_length */\n      0 },                                             /* reserved */\n\n    { 0,                                               /* role_hi */\n      NGX_HTTP_FASTCGI_RESPONDER,                      /* role_lo */\n      0, /* NGX_HTTP_FASTCGI_KEEP_CONN */              /* flags */\n      { 0, 0, 0, 0, 0 } },                             /* reserved[5] */\n\n    { 1,                                               /* version */\n      NGX_HTTP_FASTCGI_PARAMS,                         /* type */\n      0,                                               /* request_id_hi */\n      1 },                                             /* request_id_lo */\n\n};\n\n\nstatic ngx_http_variable_t  ngx_http_fastcgi_vars[] = {\n\n    { ngx_string(\"fastcgi_script_name\"), NULL,\n      ngx_http_fastcgi_script_name_variable, 0,\n      NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },\n\n    { ngx_string(\"fastcgi_path_info\"), NULL,\n      ngx_http_fastcgi_path_info_variable, 0,\n      NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },\n\n      ngx_http_null_variable\n};\n\n\nstatic ngx_str_t  ngx_http_fastcgi_hide_headers[] = {\n    ngx_string(\"Status\"),\n    ngx_string(\"X-Accel-Expires\"),\n    ngx_string(\"X-Accel-Redirect\"),\n    ngx_string(\"X-Accel-Limit-Rate\"),\n    ngx_string(\"X-Accel-Buffering\"),\n    ngx_string(\"X-Accel-Charset\"),\n    ngx_null_string\n};\n\n\n#if (NGX_HTTP_CACHE)\n\nstatic ngx_keyval_t  ngx_http_fastcgi_cache_headers[] = {\n    { ngx_string(\"HTTP_IF_MODIFIED_SINCE\"),\n      ngx_string(\"$upstream_cache_last_modified\") },\n    { ngx_string(\"HTTP_IF_UNMODIFIED_SINCE\"), ngx_string(\"\") },\n    { ngx_string(\"HTTP_IF_NONE_MATCH\"), ngx_string(\"$upstream_cache_etag\") },\n    { ngx_string(\"HTTP_IF_MATCH\"), ngx_string(\"\") },\n    { ngx_string(\"HTTP_RANGE\"), ngx_string(\"\") },\n    { ngx_string(\"HTTP_IF_RANGE\"), ngx_string(\"\") },\n    { ngx_null_string, ngx_null_string }\n};\n\n#endif\n\n\nstatic ngx_path_init_t  ngx_http_fastcgi_temp_path = {\n    ngx_string(NGX_HTTP_FASTCGI_TEMP_PATH), { 1, 2, 0 }\n};\n\n\nstatic ngx_int_t\nngx_http_fastcgi_handler(ngx_http_request_t *r)\n{\n    ngx_int_t                      rc;\n    ngx_http_upstream_t           *u;\n    ngx_http_fastcgi_ctx_t        *f;\n    ngx_http_fastcgi_loc_conf_t   *flcf;\n#if (NGX_HTTP_CACHE)\n    ngx_http_fastcgi_main_conf_t  *fmcf;\n#endif\n\n    if (ngx_http_upstream_create(r) != NGX_OK) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    f = ngx_pcalloc(r->pool, sizeof(ngx_http_fastcgi_ctx_t));\n    if (f == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    ngx_http_set_ctx(r, f, ngx_http_fastcgi_module);\n\n    flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);\n\n    if (flcf->fastcgi_lengths) {\n        if (ngx_http_fastcgi_eval(r, flcf) != NGX_OK) {\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n    }\n\n    u = r->upstream;\n\n    ngx_str_set(&u->schema, \"fastcgi://\");\n    u->output.tag = (ngx_buf_tag_t) &ngx_http_fastcgi_module;\n\n    u->conf = &flcf->upstream;\n\n#if (NGX_HTTP_CACHE)\n    fmcf = ngx_http_get_module_main_conf(r, ngx_http_fastcgi_module);\n\n    u->caches = &fmcf->caches;\n    u->create_key = ngx_http_fastcgi_create_key;\n#endif\n\n    u->create_request = ngx_http_fastcgi_create_request;\n    u->reinit_request = ngx_http_fastcgi_reinit_request;\n    u->process_header = ngx_http_fastcgi_process_header;\n    u->abort_request = ngx_http_fastcgi_abort_request;\n    u->finalize_request = ngx_http_fastcgi_finalize_request;\n    r->state = 0;\n\n#if (T_NGX_SOCKET_BUFFER)\n    u->peer.sndbuf = flcf->sndbuf;\n    u->peer.rcvbuf = flcf->rcvbuf;\n#endif\n\n    u->buffering = flcf->upstream.buffering;\n\n    u->pipe = ngx_pcalloc(r->pool, sizeof(ngx_event_pipe_t));\n    if (u->pipe == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    u->pipe->input_filter = ngx_http_fastcgi_input_filter;\n    u->pipe->input_ctx = r;\n\n    u->input_filter_init = ngx_http_fastcgi_input_filter_init;\n    u->input_filter = ngx_http_fastcgi_non_buffered_filter;\n    u->input_filter_ctx = r;\n\n    if (!flcf->upstream.request_buffering\n        && flcf->upstream.pass_request_body)\n    {\n        r->request_body_no_buffering = 1;\n    }\n\n    rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init);\n\n    if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {\n        return rc;\n    }\n\n    return NGX_DONE;\n}\n\n\nstatic ngx_int_t\nngx_http_fastcgi_eval(ngx_http_request_t *r, ngx_http_fastcgi_loc_conf_t *flcf)\n{\n    ngx_url_t             url;\n    ngx_http_upstream_t  *u;\n\n    ngx_memzero(&url, sizeof(ngx_url_t));\n\n    if (ngx_http_script_run(r, &url.url, flcf->fastcgi_lengths->elts, 0,\n                            flcf->fastcgi_values->elts)\n        == NULL)\n    {\n        return NGX_ERROR;\n    }\n\n    url.no_resolve = 1;\n\n    if (ngx_parse_url(r->pool, &url) != NGX_OK) {\n        if (url.err) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"%s in upstream \\\"%V\\\"\", url.err, &url.url);\n        }\n\n        return NGX_ERROR;\n    }\n\n    u = r->upstream;\n\n    u->resolved = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_resolved_t));\n    if (u->resolved == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (url.addrs) {\n        u->resolved->sockaddr = url.addrs[0].sockaddr;\n        u->resolved->socklen = url.addrs[0].socklen;\n        u->resolved->name = url.addrs[0].name;\n        u->resolved->naddrs = 1;\n    }\n\n    u->resolved->host = url.host;\n    u->resolved->port = url.port;\n    u->resolved->no_port = url.no_port;\n\n    return NGX_OK;\n}\n\n\n#if (NGX_HTTP_CACHE)\n\nstatic ngx_int_t\nngx_http_fastcgi_create_key(ngx_http_request_t *r)\n{\n    ngx_str_t                    *key;\n    ngx_http_fastcgi_loc_conf_t  *flcf;\n\n    key = ngx_array_push(&r->cache->keys);\n    if (key == NULL) {\n        return NGX_ERROR;\n    }\n\n    flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);\n\n    if (ngx_http_complex_value(r, &flcf->cache_key, key) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n#endif\n\n\nstatic ngx_int_t\nngx_http_fastcgi_create_request(ngx_http_request_t *r)\n{\n    off_t                         file_pos;\n    u_char                        ch, sep, *pos, *lowcase_key;\n    size_t                        size, len, key_len, val_len, padding,\n                                  allocated;\n    ngx_uint_t                    i, n, next, hash, skip_empty, header_params;\n    ngx_buf_t                    *b;\n    ngx_chain_t                  *cl, *body;\n    ngx_list_part_t              *part;\n    ngx_table_elt_t              *header, *hn, **ignored;\n    ngx_http_upstream_t          *u;\n    ngx_http_script_code_pt       code;\n    ngx_http_script_engine_t      e, le;\n    ngx_http_fastcgi_header_t    *h;\n    ngx_http_fastcgi_params_t    *params;\n    ngx_http_fastcgi_loc_conf_t  *flcf;\n    ngx_http_script_len_code_pt   lcode;\n\n    len = 0;\n    header_params = 0;\n    ignored = NULL;\n\n    u = r->upstream;\n\n    flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);\n\n#if (NGX_HTTP_CACHE)\n    params = u->cacheable ? &flcf->params_cache : &flcf->params;\n#else\n    params = &flcf->params;\n#endif\n\n    if (params->lengths) {\n        ngx_memzero(&le, sizeof(ngx_http_script_engine_t));\n\n        ngx_http_script_flush_no_cacheable_variables(r, params->flushes);\n        le.flushed = 1;\n\n        le.ip = params->lengths->elts;\n        le.request = r;\n\n        while (*(uintptr_t *) le.ip) {\n\n            lcode = *(ngx_http_script_len_code_pt *) le.ip;\n            key_len = lcode(&le);\n\n            lcode = *(ngx_http_script_len_code_pt *) le.ip;\n            skip_empty = lcode(&le);\n\n            for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) {\n                lcode = *(ngx_http_script_len_code_pt *) le.ip;\n            }\n            le.ip += sizeof(uintptr_t);\n\n            if (skip_empty && val_len == 0) {\n                continue;\n            }\n\n            len += 1 + key_len + ((val_len > 127) ? 4 : 1) + val_len;\n        }\n    }\n\n    if (flcf->upstream.pass_request_headers) {\n\n        allocated = 0;\n        lowcase_key = NULL;\n\n        if (ngx_http_link_multi_headers(r) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        if (params->number || r->headers_in.multi) {\n            n = 0;\n            part = &r->headers_in.headers.part;\n\n            while (part) {\n                n += part->nelts;\n                part = part->next;\n            }\n\n            ignored = ngx_palloc(r->pool, n * sizeof(void *));\n            if (ignored == NULL) {\n                return NGX_ERROR;\n            }\n        }\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            for (n = 0; n < header_params; n++) {\n                if (&header[i] == ignored[n]) {\n                    goto next_length;\n                }\n            }\n\n            if (params->number) {\n                if (allocated < header[i].key.len) {\n                    allocated = header[i].key.len + 16;\n                    lowcase_key = ngx_pnalloc(r->pool, allocated);\n                    if (lowcase_key == NULL) {\n                        return NGX_ERROR;\n                    }\n                }\n\n                hash = 0;\n\n                for (n = 0; n < header[i].key.len; n++) {\n                    ch = header[i].key.data[n];\n\n                    if (ch >= 'A' && ch <= 'Z') {\n                        ch |= 0x20;\n\n                    } else if (ch == '-') {\n                        ch = '_';\n                    }\n\n                    hash = ngx_hash(hash, ch);\n                    lowcase_key[n] = ch;\n                }\n\n                if (ngx_hash_find(&params->hash, hash, lowcase_key, n)) {\n                    ignored[header_params++] = &header[i];\n                    continue;\n                }\n            }\n\n            key_len = sizeof(\"HTTP_\") - 1 + header[i].key.len;\n\n            val_len = header[i].value.len;\n\n            for (hn = header[i].next; hn; hn = hn->next) {\n                val_len += hn->value.len + 2;\n                ignored[header_params++] = hn;\n            }\n\n            len += ((key_len > 127) ? 4 : 1) + key_len\n                   + ((val_len > 127) ? 4 : 1) + val_len;\n\n        next_length:\n\n            continue;\n        }\n    }\n\n\n    if (len > 65535) {\n        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,\n                      \"fastcgi request record is too big: %uz\", len);\n        return NGX_ERROR;\n    }\n\n\n    padding = 8 - len % 8;\n    padding = (padding == 8) ? 0 : padding;\n\n\n    size = sizeof(ngx_http_fastcgi_header_t)\n           + sizeof(ngx_http_fastcgi_begin_request_t)\n\n           + sizeof(ngx_http_fastcgi_header_t)  /* NGX_HTTP_FASTCGI_PARAMS */\n           + len + padding\n           + sizeof(ngx_http_fastcgi_header_t)  /* NGX_HTTP_FASTCGI_PARAMS */\n\n           + sizeof(ngx_http_fastcgi_header_t); /* NGX_HTTP_FASTCGI_STDIN */\n\n\n    b = ngx_create_temp_buf(r->pool, size);\n    if (b == NULL) {\n        return NGX_ERROR;\n    }\n\n    cl = ngx_alloc_chain_link(r->pool);\n    if (cl == NULL) {\n        return NGX_ERROR;\n    }\n\n    cl->buf = b;\n\n    ngx_http_fastcgi_request_start.br.flags =\n        flcf->keep_conn ? NGX_HTTP_FASTCGI_KEEP_CONN : 0;\n\n    ngx_memcpy(b->pos, &ngx_http_fastcgi_request_start,\n               sizeof(ngx_http_fastcgi_request_start_t));\n\n    h = (ngx_http_fastcgi_header_t *)\n             (b->pos + sizeof(ngx_http_fastcgi_header_t)\n                     + sizeof(ngx_http_fastcgi_begin_request_t));\n\n    h->content_length_hi = (u_char) ((len >> 8) & 0xff);\n    h->content_length_lo = (u_char) (len & 0xff);\n    h->padding_length = (u_char) padding;\n    h->reserved = 0;\n\n    b->last = b->pos + sizeof(ngx_http_fastcgi_header_t)\n                     + sizeof(ngx_http_fastcgi_begin_request_t)\n                     + sizeof(ngx_http_fastcgi_header_t);\n\n\n    if (params->lengths) {\n        ngx_memzero(&e, sizeof(ngx_http_script_engine_t));\n\n        e.ip = params->values->elts;\n        e.pos = b->last;\n        e.request = r;\n        e.flushed = 1;\n\n        le.ip = params->lengths->elts;\n\n        while (*(uintptr_t *) le.ip) {\n\n            lcode = *(ngx_http_script_len_code_pt *) le.ip;\n            key_len = (u_char) lcode(&le);\n\n            lcode = *(ngx_http_script_len_code_pt *) le.ip;\n            skip_empty = lcode(&le);\n\n            for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) {\n                lcode = *(ngx_http_script_len_code_pt *) le.ip;\n            }\n            le.ip += sizeof(uintptr_t);\n\n            if (skip_empty && val_len == 0) {\n                e.skip = 1;\n\n                while (*(uintptr_t *) e.ip) {\n                    code = *(ngx_http_script_code_pt *) e.ip;\n                    code((ngx_http_script_engine_t *) &e);\n                }\n                e.ip += sizeof(uintptr_t);\n\n                e.skip = 0;\n\n                continue;\n            }\n\n            *e.pos++ = (u_char) key_len;\n\n            if (val_len > 127) {\n                *e.pos++ = (u_char) (((val_len >> 24) & 0x7f) | 0x80);\n                *e.pos++ = (u_char) ((val_len >> 16) & 0xff);\n                *e.pos++ = (u_char) ((val_len >> 8) & 0xff);\n                *e.pos++ = (u_char) (val_len & 0xff);\n\n            } else {\n                *e.pos++ = (u_char) val_len;\n            }\n\n            while (*(uintptr_t *) e.ip) {\n                code = *(ngx_http_script_code_pt *) e.ip;\n                code((ngx_http_script_engine_t *) &e);\n            }\n            e.ip += sizeof(uintptr_t);\n\n            ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"fastcgi param: \\\"%*s: %*s\\\"\",\n                           key_len, e.pos - (key_len + val_len),\n                           val_len, e.pos - val_len);\n        }\n\n        b->last = e.pos;\n    }\n\n\n    if (flcf->upstream.pass_request_headers) {\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            for (n = 0; n < header_params; n++) {\n                if (&header[i] == ignored[n]) {\n                    goto next_value;\n                }\n            }\n\n            key_len = sizeof(\"HTTP_\") - 1 + header[i].key.len;\n            if (key_len > 127) {\n                *b->last++ = (u_char) (((key_len >> 24) & 0x7f) | 0x80);\n                *b->last++ = (u_char) ((key_len >> 16) & 0xff);\n                *b->last++ = (u_char) ((key_len >> 8) & 0xff);\n                *b->last++ = (u_char) (key_len & 0xff);\n\n            } else {\n                *b->last++ = (u_char) key_len;\n            }\n\n            val_len = header[i].value.len;\n\n            for (hn = header[i].next; hn; hn = hn->next) {\n                val_len += hn->value.len + 2;\n            }\n\n            if (val_len > 127) {\n                *b->last++ = (u_char) (((val_len >> 24) & 0x7f) | 0x80);\n                *b->last++ = (u_char) ((val_len >> 16) & 0xff);\n                *b->last++ = (u_char) ((val_len >> 8) & 0xff);\n                *b->last++ = (u_char) (val_len & 0xff);\n\n            } else {\n                *b->last++ = (u_char) val_len;\n            }\n\n            b->last = ngx_cpymem(b->last, \"HTTP_\", sizeof(\"HTTP_\") - 1);\n\n            for (n = 0; n < header[i].key.len; n++) {\n                ch = header[i].key.data[n];\n\n                if (ch >= 'a' && ch <= 'z') {\n                    ch &= ~0x20;\n\n                } else if (ch == '-') {\n                    ch = '_';\n                }\n\n                *b->last++ = ch;\n            }\n\n            b->last = ngx_copy(b->last, header[i].value.data,\n                               header[i].value.len);\n\n            if (header[i].next) {\n\n                if (header[i].key.len == sizeof(\"Cookie\") - 1\n                    && ngx_strncasecmp(header[i].key.data, (u_char *) \"Cookie\",\n                                       sizeof(\"Cookie\") - 1)\n                       == 0)\n                {\n                    sep = ';';\n\n                } else {\n                    sep = ',';\n                }\n\n                for (hn = header[i].next; hn; hn = hn->next) {\n                    *b->last++ = sep;\n                    *b->last++ = ' ';\n                    b->last = ngx_copy(b->last, hn->value.data, hn->value.len);\n                }\n            }\n\n            ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"fastcgi param: \\\"%*s: %*s\\\"\",\n                           key_len, b->last - (key_len + val_len),\n                           val_len, b->last - val_len);\n        next_value:\n\n            continue;\n        }\n    }\n\n\n    if (padding) {\n        ngx_memzero(b->last, padding);\n        b->last += padding;\n    }\n\n\n    h = (ngx_http_fastcgi_header_t *) b->last;\n    b->last += sizeof(ngx_http_fastcgi_header_t);\n\n    h->version = 1;\n    h->type = NGX_HTTP_FASTCGI_PARAMS;\n    h->request_id_hi = 0;\n    h->request_id_lo = 1;\n    h->content_length_hi = 0;\n    h->content_length_lo = 0;\n    h->padding_length = 0;\n    h->reserved = 0;\n\n    if (r->request_body_no_buffering) {\n\n        u->request_bufs = cl;\n\n        u->output.output_filter = ngx_http_fastcgi_body_output_filter;\n        u->output.filter_ctx = r;\n\n    } else if (flcf->upstream.pass_request_body) {\n\n        body = u->request_bufs;\n        u->request_bufs = cl;\n\n#if (NGX_SUPPRESS_WARN)\n        file_pos = 0;\n        pos = NULL;\n#endif\n\n        while (body) {\n\n            if (ngx_buf_special(body->buf)) {\n                body = body->next;\n                continue;\n            }\n\n            if (body->buf->in_file) {\n                file_pos = body->buf->file_pos;\n\n            } else {\n                pos = body->buf->pos;\n            }\n\n            next = 0;\n\n            do {\n                b = ngx_alloc_buf(r->pool);\n                if (b == NULL) {\n                    return NGX_ERROR;\n                }\n\n                ngx_memcpy(b, body->buf, sizeof(ngx_buf_t));\n\n                if (body->buf->in_file) {\n                    b->file_pos = file_pos;\n                    file_pos += 32 * 1024;\n\n                    if (file_pos >= body->buf->file_last) {\n                        file_pos = body->buf->file_last;\n                        next = 1;\n                    }\n\n                    b->file_last = file_pos;\n                    len = (ngx_uint_t) (file_pos - b->file_pos);\n\n                } else {\n                    b->pos = pos;\n                    b->start = pos;\n                    pos += 32 * 1024;\n\n                    if (pos >= body->buf->last) {\n                        pos = body->buf->last;\n                        next = 1;\n                    }\n\n                    b->last = pos;\n                    len = (ngx_uint_t) (pos - b->pos);\n                }\n\n                padding = 8 - len % 8;\n                padding = (padding == 8) ? 0 : padding;\n\n                h = (ngx_http_fastcgi_header_t *) cl->buf->last;\n                cl->buf->last += sizeof(ngx_http_fastcgi_header_t);\n\n                h->version = 1;\n                h->type = NGX_HTTP_FASTCGI_STDIN;\n                h->request_id_hi = 0;\n                h->request_id_lo = 1;\n                h->content_length_hi = (u_char) ((len >> 8) & 0xff);\n                h->content_length_lo = (u_char) (len & 0xff);\n                h->padding_length = (u_char) padding;\n                h->reserved = 0;\n\n                cl->next = ngx_alloc_chain_link(r->pool);\n                if (cl->next == NULL) {\n                    return NGX_ERROR;\n                }\n\n                cl = cl->next;\n                cl->buf = b;\n\n                b = ngx_create_temp_buf(r->pool,\n                                        sizeof(ngx_http_fastcgi_header_t)\n                                        + padding);\n                if (b == NULL) {\n                    return NGX_ERROR;\n                }\n\n                if (padding) {\n                    ngx_memzero(b->last, padding);\n                    b->last += padding;\n                }\n\n                cl->next = ngx_alloc_chain_link(r->pool);\n                if (cl->next == NULL) {\n                    return NGX_ERROR;\n                }\n\n                cl = cl->next;\n                cl->buf = b;\n\n            } while (!next);\n\n            body = body->next;\n        }\n\n    } else {\n        u->request_bufs = cl;\n    }\n\n    if (!r->request_body_no_buffering) {\n        h = (ngx_http_fastcgi_header_t *) cl->buf->last;\n        cl->buf->last += sizeof(ngx_http_fastcgi_header_t);\n\n        h->version = 1;\n        h->type = NGX_HTTP_FASTCGI_STDIN;\n        h->request_id_hi = 0;\n        h->request_id_lo = 1;\n        h->content_length_hi = 0;\n        h->content_length_lo = 0;\n        h->padding_length = 0;\n        h->reserved = 0;\n    }\n\n    cl->next = NULL;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_fastcgi_reinit_request(ngx_http_request_t *r)\n{\n    ngx_http_fastcgi_ctx_t  *f;\n\n    f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);\n\n    if (f == NULL) {\n        return NGX_OK;\n    }\n\n    f->state = ngx_http_fastcgi_st_version;\n    f->fastcgi_stdout = 0;\n    f->large_stderr = 0;\n\n    if (f->split_parts) {\n        f->split_parts->nelts = 0;\n    }\n\n    r->state = 0;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_fastcgi_body_output_filter(void *data, ngx_chain_t *in)\n{\n    ngx_http_request_t  *r = data;\n\n    off_t                       file_pos;\n    u_char                     *pos, *start;\n    size_t                      len, padding;\n    ngx_buf_t                  *b;\n    ngx_int_t                   rc;\n    ngx_uint_t                  next, last;\n    ngx_chain_t                *cl, *tl, *out, **ll;\n    ngx_http_fastcgi_ctx_t     *f;\n    ngx_http_fastcgi_header_t  *h;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"fastcgi output filter\");\n\n    f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);\n\n    if (in == NULL) {\n        out = in;\n        goto out;\n    }\n\n    out = NULL;\n    ll = &out;\n\n    if (!f->header_sent) {\n        /* first buffer contains headers, pass it unmodified */\n\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"fastcgi output header\");\n\n        f->header_sent = 1;\n\n        tl = ngx_alloc_chain_link(r->pool);\n        if (tl == NULL) {\n            return NGX_ERROR;\n        }\n\n        tl->buf = in->buf;\n        *ll = tl;\n        ll = &tl->next;\n\n        in = in->next;\n\n        if (in == NULL) {\n            tl->next = NULL;\n            goto out;\n        }\n    }\n\n    cl = ngx_chain_get_free_buf(r->pool, &f->free);\n    if (cl == NULL) {\n        return NGX_ERROR;\n    }\n\n    b = cl->buf;\n\n    b->tag = (ngx_buf_tag_t) &ngx_http_fastcgi_body_output_filter;\n    b->temporary = 1;\n\n    if (b->start == NULL) {\n        /* reserve space for maximum possible padding, 7 bytes */\n\n        b->start = ngx_palloc(r->pool,\n                              sizeof(ngx_http_fastcgi_header_t) + 7);\n        if (b->start == NULL) {\n            return NGX_ERROR;\n        }\n\n        b->pos = b->start;\n        b->last = b->start;\n\n        b->end = b->start + sizeof(ngx_http_fastcgi_header_t) + 7;\n    }\n\n    *ll = cl;\n\n    last = 0;\n    padding = 0;\n\n#if (NGX_SUPPRESS_WARN)\n    file_pos = 0;\n    pos = NULL;\n#endif\n\n    while (in) {\n\n        ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,\n                       \"fastcgi output in  l:%d f:%d %p, pos %p, size: %z \"\n                       \"file: %O, size: %O\",\n                       in->buf->last_buf,\n                       in->buf->in_file,\n                       in->buf->start, in->buf->pos,\n                       in->buf->last - in->buf->pos,\n                       in->buf->file_pos,\n                       in->buf->file_last - in->buf->file_pos);\n\n        if (in->buf->last_buf) {\n            last = 1;\n        }\n\n        if (ngx_buf_special(in->buf)) {\n            in = in->next;\n            continue;\n        }\n\n        if (in->buf->in_file) {\n            file_pos = in->buf->file_pos;\n\n        } else {\n            pos = in->buf->pos;\n        }\n\n        next = 0;\n\n        do {\n            tl = ngx_chain_get_free_buf(r->pool, &f->free);\n            if (tl == NULL) {\n                return NGX_ERROR;\n            }\n\n            b = tl->buf;\n            start = b->start;\n\n            ngx_memcpy(b, in->buf, sizeof(ngx_buf_t));\n\n            /*\n             * restore b->start to preserve memory allocated in the buffer,\n             * to reuse it later for headers and padding\n             */\n\n            b->start = start;\n\n            if (in->buf->in_file) {\n                b->file_pos = file_pos;\n                file_pos += 32 * 1024;\n\n                if (file_pos >= in->buf->file_last) {\n                    file_pos = in->buf->file_last;\n                    next = 1;\n                }\n\n                b->file_last = file_pos;\n                len = (ngx_uint_t) (file_pos - b->file_pos);\n\n            } else {\n                b->pos = pos;\n                pos += 32 * 1024;\n\n                if (pos >= in->buf->last) {\n                    pos = in->buf->last;\n                    next = 1;\n                }\n\n                b->last = pos;\n                len = (ngx_uint_t) (pos - b->pos);\n            }\n\n            b->tag = (ngx_buf_tag_t) &ngx_http_fastcgi_body_output_filter;\n            b->shadow = in->buf;\n            b->last_shadow = next;\n\n            b->last_buf = 0;\n            b->last_in_chain = 0;\n\n            padding = 8 - len % 8;\n            padding = (padding == 8) ? 0 : padding;\n\n            h = (ngx_http_fastcgi_header_t *) cl->buf->last;\n            cl->buf->last += sizeof(ngx_http_fastcgi_header_t);\n\n            h->version = 1;\n            h->type = NGX_HTTP_FASTCGI_STDIN;\n            h->request_id_hi = 0;\n            h->request_id_lo = 1;\n            h->content_length_hi = (u_char) ((len >> 8) & 0xff);\n            h->content_length_lo = (u_char) (len & 0xff);\n            h->padding_length = (u_char) padding;\n            h->reserved = 0;\n\n            cl->next = tl;\n            cl = tl;\n\n            tl = ngx_chain_get_free_buf(r->pool, &f->free);\n            if (tl == NULL) {\n                return NGX_ERROR;\n            }\n\n            b = tl->buf;\n\n            b->tag = (ngx_buf_tag_t) &ngx_http_fastcgi_body_output_filter;\n            b->temporary = 1;\n\n            if (b->start == NULL) {\n                /* reserve space for maximum possible padding, 7 bytes */\n\n                b->start = ngx_palloc(r->pool,\n                                      sizeof(ngx_http_fastcgi_header_t) + 7);\n                if (b->start == NULL) {\n                    return NGX_ERROR;\n                }\n\n                b->pos = b->start;\n                b->last = b->start;\n\n                b->end = b->start + sizeof(ngx_http_fastcgi_header_t) + 7;\n            }\n\n            if (padding) {\n                ngx_memzero(b->last, padding);\n                b->last += padding;\n            }\n\n            cl->next = tl;\n            cl = tl;\n\n        } while (!next);\n\n        in = in->next;\n    }\n\n    if (last) {\n        h = (ngx_http_fastcgi_header_t *) cl->buf->last;\n        cl->buf->last += sizeof(ngx_http_fastcgi_header_t);\n\n        h->version = 1;\n        h->type = NGX_HTTP_FASTCGI_STDIN;\n        h->request_id_hi = 0;\n        h->request_id_lo = 1;\n        h->content_length_hi = 0;\n        h->content_length_lo = 0;\n        h->padding_length = 0;\n        h->reserved = 0;\n\n        cl->buf->last_buf = 1;\n\n    } else if (padding == 0) {\n        /* TODO: do not allocate buffers instead */\n        cl->buf->temporary = 0;\n        cl->buf->sync = 1;\n    }\n\n    cl->next = NULL;\n\nout:\n\n#if (NGX_DEBUG)\n\n    for (cl = out; cl; cl = cl->next) {\n        ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,\n                       \"fastcgi output out l:%d f:%d %p, pos %p, size: %z \"\n                       \"file: %O, size: %O\",\n                       cl->buf->last_buf,\n                       cl->buf->in_file,\n                       cl->buf->start, cl->buf->pos,\n                       cl->buf->last - cl->buf->pos,\n                       cl->buf->file_pos,\n                       cl->buf->file_last - cl->buf->file_pos);\n    }\n\n#endif\n\n    rc = ngx_chain_writer(&r->upstream->writer, out);\n\n    ngx_chain_update_chains(r->pool, &f->free, &f->busy, &out,\n                         (ngx_buf_tag_t) &ngx_http_fastcgi_body_output_filter);\n\n    for (cl = f->free; cl; cl = cl->next) {\n\n        /* mark original buffers as sent */\n\n        if (cl->buf->shadow) {\n            if (cl->buf->last_shadow) {\n                b = cl->buf->shadow;\n                b->pos = b->last;\n            }\n\n            cl->buf->shadow = NULL;\n        }\n    }\n\n    return rc;\n}\n\n\nstatic ngx_int_t\nngx_http_fastcgi_process_header(ngx_http_request_t *r)\n{\n    u_char                         *p, *msg, *start, *last,\n                                   *part_start, *part_end;\n    size_t                          size;\n    ngx_str_t                      *status_line, *pattern;\n    ngx_int_t                       rc, status;\n    ngx_buf_t                       buf;\n    ngx_uint_t                      i;\n    ngx_table_elt_t                *h;\n    ngx_http_upstream_t            *u;\n    ngx_http_fastcgi_ctx_t         *f;\n    ngx_http_upstream_header_t     *hh;\n    ngx_http_fastcgi_loc_conf_t    *flcf;\n    ngx_http_fastcgi_split_part_t  *part;\n    ngx_http_upstream_main_conf_t  *umcf;\n\n    f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);\n\n    umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);\n\n    u = r->upstream;\n\n    for ( ;; ) {\n\n        if (f->state < ngx_http_fastcgi_st_data) {\n\n            f->pos = u->buffer.pos;\n            f->last = u->buffer.last;\n\n            rc = ngx_http_fastcgi_process_record(r, f);\n\n            u->buffer.pos = f->pos;\n            u->buffer.last = f->last;\n\n            if (rc == NGX_AGAIN) {\n                return NGX_AGAIN;\n            }\n\n            if (rc == NGX_ERROR) {\n                return NGX_HTTP_UPSTREAM_INVALID_HEADER;\n            }\n\n            if (f->type != NGX_HTTP_FASTCGI_STDOUT\n                && f->type != NGX_HTTP_FASTCGI_STDERR)\n            {\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                              \"upstream sent unexpected FastCGI record: %ui\",\n                              f->type);\n\n                return NGX_HTTP_UPSTREAM_INVALID_HEADER;\n            }\n\n            if (f->type == NGX_HTTP_FASTCGI_STDOUT && f->length == 0) {\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                              \"upstream prematurely closed FastCGI stdout\");\n\n                return NGX_HTTP_UPSTREAM_INVALID_HEADER;\n            }\n        }\n\n        if (f->state == ngx_http_fastcgi_st_padding) {\n\n            if (u->buffer.pos + f->padding < u->buffer.last) {\n                f->state = ngx_http_fastcgi_st_version;\n                u->buffer.pos += f->padding;\n\n                continue;\n            }\n\n            if (u->buffer.pos + f->padding == u->buffer.last) {\n                f->state = ngx_http_fastcgi_st_version;\n                u->buffer.pos = u->buffer.last;\n\n                return NGX_AGAIN;\n            }\n\n            f->padding -= u->buffer.last - u->buffer.pos;\n            u->buffer.pos = u->buffer.last;\n\n            return NGX_AGAIN;\n        }\n\n\n        /* f->state == ngx_http_fastcgi_st_data */\n\n        if (f->type == NGX_HTTP_FASTCGI_STDERR) {\n\n            if (f->length) {\n                msg = u->buffer.pos;\n\n                if (u->buffer.pos + f->length <= u->buffer.last) {\n                    u->buffer.pos += f->length;\n                    f->length = 0;\n                    f->state = ngx_http_fastcgi_st_padding;\n\n                } else {\n                    f->length -= u->buffer.last - u->buffer.pos;\n                    u->buffer.pos = u->buffer.last;\n                }\n\n                for (p = u->buffer.pos - 1; msg < p; p--) {\n                    if (*p != LF && *p != CR && *p != '.' && *p != ' ') {\n                        break;\n                    }\n                }\n\n                p++;\n\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                              \"FastCGI sent in stderr: \\\"%*s\\\"\", p - msg, msg);\n\n                flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);\n\n                if (flcf->catch_stderr) {\n                    pattern = flcf->catch_stderr->elts;\n\n                    for (i = 0; i < flcf->catch_stderr->nelts; i++) {\n                        if (ngx_strnstr(msg, (char *) pattern[i].data,\n                                        p - msg)\n                            != NULL)\n                        {\n                            return NGX_HTTP_UPSTREAM_INVALID_HEADER;\n                        }\n                    }\n                }\n\n                if (u->buffer.pos == u->buffer.last) {\n\n                    if (!f->fastcgi_stdout) {\n\n                        /*\n                         * the special handling the large number\n                         * of the PHP warnings to not allocate memory\n                         */\n\n#if (NGX_HTTP_CACHE)\n                        if (r->cache) {\n                            u->buffer.pos = u->buffer.start\n                                                     + r->cache->header_start;\n                        } else {\n                            u->buffer.pos = u->buffer.start;\n                        }\n#else\n                        u->buffer.pos = u->buffer.start;\n#endif\n                        u->buffer.last = u->buffer.pos;\n                        f->large_stderr = 1;\n                    }\n\n                    return NGX_AGAIN;\n                }\n\n            } else {\n                f->state = ngx_http_fastcgi_st_padding;\n            }\n\n            continue;\n        }\n\n\n        /* f->type == NGX_HTTP_FASTCGI_STDOUT */\n\n#if (NGX_HTTP_CACHE)\n\n        if (f->large_stderr && r->cache) {\n            ssize_t                     len;\n            ngx_http_fastcgi_header_t  *fh;\n\n            start = u->buffer.start + r->cache->header_start;\n\n            len = u->buffer.pos - start - 2 * sizeof(ngx_http_fastcgi_header_t);\n\n            /*\n             * A tail of large stderr output before HTTP header is placed\n             * in a cache file without a FastCGI record header.\n             * To workaround it we put a dummy FastCGI record header at the\n             * start of the stderr output or update r->cache_header_start,\n             * if there is no enough place for the record header.\n             */\n\n            if (len >= 0) {\n                fh = (ngx_http_fastcgi_header_t *) start;\n                fh->version = 1;\n                fh->type = NGX_HTTP_FASTCGI_STDERR;\n                fh->request_id_hi = 0;\n                fh->request_id_lo = 1;\n                fh->content_length_hi = (u_char) ((len >> 8) & 0xff);\n                fh->content_length_lo = (u_char) (len & 0xff);\n                fh->padding_length = 0;\n                fh->reserved = 0;\n\n            } else {\n                r->cache->header_start += u->buffer.pos - start\n                                          - sizeof(ngx_http_fastcgi_header_t);\n            }\n\n            f->large_stderr = 0;\n        }\n\n#endif\n\n        f->fastcgi_stdout = 1;\n\n        start = u->buffer.pos;\n\n        if (u->buffer.pos + f->length < u->buffer.last) {\n\n            /*\n             * set u->buffer.last to the end of the FastCGI record data\n             * for ngx_http_parse_header_line()\n             */\n\n            last = u->buffer.last;\n            u->buffer.last = u->buffer.pos + f->length;\n\n        } else {\n            last = NULL;\n        }\n\n        for ( ;; ) {\n\n            part_start = u->buffer.pos;\n            part_end = u->buffer.last;\n\n            rc = ngx_http_parse_header_line(r, &u->buffer, 1);\n\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"http fastcgi parser: %i\", rc);\n\n            if (rc == NGX_AGAIN) {\n                break;\n            }\n\n            if (rc == NGX_OK) {\n\n                /* a header line has been parsed successfully */\n\n                h = ngx_list_push(&u->headers_in.headers);\n                if (h == NULL) {\n                    return NGX_ERROR;\n                }\n\n                if (f->split_parts && f->split_parts->nelts) {\n\n                    part = f->split_parts->elts;\n                    size = u->buffer.pos - part_start;\n\n                    for (i = 0; i < f->split_parts->nelts; i++) {\n                        size += part[i].end - part[i].start;\n                    }\n\n                    p = ngx_pnalloc(r->pool, size);\n                    if (p == NULL) {\n                        h->hash = 0;\n                        return NGX_ERROR;\n                    }\n\n                    buf.pos = p;\n\n                    for (i = 0; i < f->split_parts->nelts; i++) {\n                        p = ngx_cpymem(p, part[i].start,\n                                       part[i].end - part[i].start);\n                    }\n\n                    p = ngx_cpymem(p, part_start, u->buffer.pos - part_start);\n\n                    buf.last = p;\n\n                    f->split_parts->nelts = 0;\n\n                    rc = ngx_http_parse_header_line(r, &buf, 1);\n\n                    if (rc != NGX_OK) {\n                        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,\n                                      \"invalid header after joining \"\n                                      \"FastCGI records\");\n                        h->hash = 0;\n                        return NGX_ERROR;\n                    }\n\n                    h->key.len = r->header_name_end - r->header_name_start;\n                    h->key.data = r->header_name_start;\n                    h->key.data[h->key.len] = '\\0';\n\n                    h->value.len = r->header_end - r->header_start;\n                    h->value.data = r->header_start;\n                    h->value.data[h->value.len] = '\\0';\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                } else {\n\n                    h->key.len = r->header_name_end - r->header_name_start;\n                    h->value.len = r->header_end - r->header_start;\n\n                    h->key.data = ngx_pnalloc(r->pool,\n                                              h->key.len + 1 + h->value.len + 1\n                                              + h->key.len);\n                    if (h->key.data == NULL) {\n                        h->hash = 0;\n                        return NGX_ERROR;\n                    }\n\n                    h->value.data = h->key.data + h->key.len + 1;\n                    h->lowcase_key = h->key.data + h->key.len + 1\n                                     + h->value.len + 1;\n\n                    ngx_memcpy(h->key.data, r->header_name_start, h->key.len);\n                    h->key.data[h->key.len] = '\\0';\n                    ngx_memcpy(h->value.data, r->header_start, h->value.len);\n                    h->value.data[h->value.len] = '\\0';\n                }\n\n                h->hash = r->header_hash;\n\n                if (h->key.len == r->lowcase_index) {\n                    ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len);\n\n                } else {\n                    ngx_strlow(h->lowcase_key, h->key.data, h->key.len);\n                }\n\n                hh = ngx_hash_find(&umcf->headers_in_hash, h->hash,\n                                   h->lowcase_key, h->key.len);\n\n                if (hh) {\n                    rc = hh->handler(r, h, hh->offset);\n\n                    if (rc != NGX_OK) {\n                        return rc;\n                    }\n                }\n\n                ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                               \"http fastcgi header: \\\"%V: %V\\\"\",\n                               &h->key, &h->value);\n\n                if (u->buffer.pos < u->buffer.last) {\n                    continue;\n                }\n\n                /* the end of the FastCGI record */\n\n                break;\n            }\n\n            if (rc == NGX_HTTP_PARSE_HEADER_DONE) {\n\n                /* a whole header has been parsed successfully */\n\n                ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                               \"http fastcgi header done\");\n\n                if (u->headers_in.status) {\n                    status_line = &u->headers_in.status->value;\n\n                    status = ngx_atoi(status_line->data, 3);\n\n                    if (status == NGX_ERROR) {\n                        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                                      \"upstream sent invalid status \\\"%V\\\"\",\n                                      status_line);\n                        return NGX_HTTP_UPSTREAM_INVALID_HEADER;\n                    }\n\n                    u->headers_in.status_n = status;\n                    u->headers_in.status_line = *status_line;\n\n                } else if (u->headers_in.location) {\n                    u->headers_in.status_n = 302;\n                    ngx_str_set(&u->headers_in.status_line,\n                                \"302 Moved Temporarily\");\n\n                } else {\n                    u->headers_in.status_n = 200;\n                    ngx_str_set(&u->headers_in.status_line, \"200 OK\");\n                }\n\n                if (u->state && u->state->status == 0) {\n                    u->state->status = u->headers_in.status_n;\n                }\n\n                break;\n            }\n\n            /* rc == NGX_HTTP_PARSE_INVALID_HEADER */\n\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"upstream sent invalid header: \\\"%*s\\\\x%02xd...\\\"\",\n                          r->header_end - r->header_name_start,\n                          r->header_name_start, *r->header_end);\n\n            return NGX_HTTP_UPSTREAM_INVALID_HEADER;\n        }\n\n        if (last) {\n            u->buffer.last = last;\n        }\n\n        f->length -= u->buffer.pos - start;\n\n        if (f->length == 0) {\n            f->state = ngx_http_fastcgi_st_padding;\n        }\n\n        if (rc == NGX_HTTP_PARSE_HEADER_DONE) {\n            return NGX_OK;\n        }\n\n        if (rc == NGX_OK) {\n            continue;\n        }\n\n        /* rc == NGX_AGAIN */\n\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"upstream split a header line in FastCGI records\");\n\n        if (f->split_parts == NULL) {\n            f->split_parts = ngx_array_create(r->pool, 1,\n                                        sizeof(ngx_http_fastcgi_split_part_t));\n            if (f->split_parts == NULL) {\n                return NGX_ERROR;\n            }\n        }\n\n        part = ngx_array_push(f->split_parts);\n        if (part == NULL) {\n            return NGX_ERROR;\n        }\n\n        part->start = part_start;\n        part->end = part_end;\n\n        if (u->buffer.pos < u->buffer.last) {\n            continue;\n        }\n\n        return NGX_AGAIN;\n    }\n}\n\n\nstatic ngx_int_t\nngx_http_fastcgi_input_filter_init(void *data)\n{\n    ngx_http_request_t  *r = data;\n\n    ngx_http_upstream_t          *u;\n    ngx_http_fastcgi_ctx_t       *f;\n    ngx_http_fastcgi_loc_conf_t  *flcf;\n\n    u = r->upstream;\n\n    f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);\n    flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);\n\n    u->pipe->length = flcf->keep_conn ?\n                      (off_t) sizeof(ngx_http_fastcgi_header_t) : -1;\n\n    if (u->headers_in.status_n == NGX_HTTP_NO_CONTENT\n        || u->headers_in.status_n == NGX_HTTP_NOT_MODIFIED)\n    {\n        f->rest = 0;\n\n    } else if (r->method == NGX_HTTP_HEAD) {\n        f->rest = -2;\n\n    } else {\n        f->rest = u->headers_in.content_length_n;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_fastcgi_input_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)\n{\n    u_char                       *m, *msg;\n    ngx_int_t                     rc;\n    ngx_buf_t                    *b, **prev;\n    ngx_chain_t                  *cl;\n    ngx_http_request_t           *r;\n    ngx_http_fastcgi_ctx_t       *f;\n    ngx_http_fastcgi_loc_conf_t  *flcf;\n\n    if (buf->pos == buf->last) {\n        return NGX_OK;\n    }\n\n    r = p->input_ctx;\n    f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);\n    flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);\n\n    if (p->upstream_done || f->closed) {\n        r->upstream->keepalive = 0;\n\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, p->log, 0,\n                       \"http fastcgi data after close\");\n\n        return NGX_OK;\n    }\n\n    b = NULL;\n    prev = &buf->shadow;\n\n    f->pos = buf->pos;\n    f->last = buf->last;\n\n    for ( ;; ) {\n        if (f->state < ngx_http_fastcgi_st_data) {\n\n            rc = ngx_http_fastcgi_process_record(r, f);\n\n            if (rc == NGX_AGAIN) {\n                break;\n            }\n\n            if (rc == NGX_ERROR) {\n                return NGX_ERROR;\n            }\n\n            if (f->type == NGX_HTTP_FASTCGI_STDOUT && f->length == 0) {\n                f->state = ngx_http_fastcgi_st_padding;\n\n                ngx_log_debug0(NGX_LOG_DEBUG_HTTP, p->log, 0,\n                               \"http fastcgi closed stdout\");\n\n                if (f->rest > 0) {\n                    ngx_log_error(NGX_LOG_ERR, p->log, 0,\n                                  \"upstream prematurely closed \"\n                                  \"FastCGI stdout\");\n\n                    p->upstream_error = 1;\n                    p->upstream_eof = 0;\n                    f->closed = 1;\n\n                    break;\n                }\n\n                if (!flcf->keep_conn) {\n                    p->upstream_done = 1;\n                }\n\n                continue;\n            }\n\n            if (f->type == NGX_HTTP_FASTCGI_END_REQUEST) {\n\n                ngx_log_debug0(NGX_LOG_DEBUG_HTTP, p->log, 0,\n                               \"http fastcgi sent end request\");\n\n                if (f->rest > 0) {\n                    ngx_log_error(NGX_LOG_ERR, p->log, 0,\n                                  \"upstream prematurely closed \"\n                                  \"FastCGI request\");\n\n                    p->upstream_error = 1;\n                    p->upstream_eof = 0;\n                    f->closed = 1;\n\n                    break;\n                }\n\n                if (!flcf->keep_conn) {\n                    p->upstream_done = 1;\n                    break;\n                }\n\n                continue;\n            }\n        }\n\n\n        if (f->state == ngx_http_fastcgi_st_padding) {\n\n            if (f->type == NGX_HTTP_FASTCGI_END_REQUEST) {\n\n                if (f->pos + f->padding < f->last) {\n                    p->upstream_done = 1;\n                    break;\n                }\n\n                if (f->pos + f->padding == f->last) {\n                    p->upstream_done = 1;\n                    r->upstream->keepalive = 1;\n                    break;\n                }\n\n                f->padding -= f->last - f->pos;\n\n                break;\n            }\n\n            if (f->pos + f->padding < f->last) {\n                f->state = ngx_http_fastcgi_st_version;\n                f->pos += f->padding;\n\n                continue;\n            }\n\n            if (f->pos + f->padding == f->last) {\n                f->state = ngx_http_fastcgi_st_version;\n\n                break;\n            }\n\n            f->padding -= f->last - f->pos;\n\n            break;\n        }\n\n\n        /* f->state == ngx_http_fastcgi_st_data */\n\n        if (f->type == NGX_HTTP_FASTCGI_STDERR) {\n\n            if (f->length) {\n\n                if (f->pos == f->last) {\n                    break;\n                }\n\n                msg = f->pos;\n\n                if (f->pos + f->length <= f->last) {\n                    f->pos += f->length;\n                    f->length = 0;\n                    f->state = ngx_http_fastcgi_st_padding;\n\n                } else {\n                    f->length -= f->last - f->pos;\n                    f->pos = f->last;\n                }\n\n                for (m = f->pos - 1; msg < m; m--) {\n                    if (*m != LF && *m != CR && *m != '.' && *m != ' ') {\n                        break;\n                    }\n                }\n\n                ngx_log_error(NGX_LOG_ERR, p->log, 0,\n                              \"FastCGI sent in stderr: \\\"%*s\\\"\",\n                              m + 1 - msg, msg);\n\n            } else {\n                f->state = ngx_http_fastcgi_st_padding;\n            }\n\n            continue;\n        }\n\n        if (f->type == NGX_HTTP_FASTCGI_END_REQUEST) {\n\n            if (f->pos + f->length <= f->last) {\n                f->state = ngx_http_fastcgi_st_padding;\n                f->pos += f->length;\n\n                continue;\n            }\n\n            f->length -= f->last - f->pos;\n\n            break;\n        }\n\n\n        /* f->type == NGX_HTTP_FASTCGI_STDOUT */\n\n        if (f->pos == f->last) {\n            break;\n        }\n\n        if (f->rest == -2) {\n            f->rest = r->upstream->headers_in.content_length_n;\n        }\n\n        if (f->rest == 0) {\n            ngx_log_error(NGX_LOG_WARN, p->log, 0,\n                          \"upstream sent more data than specified in \"\n                          \"\\\"Content-Length\\\" header\");\n            p->upstream_done = 1;\n            break;\n        }\n\n        cl = ngx_chain_get_free_buf(p->pool, &p->free);\n        if (cl == NULL) {\n            return NGX_ERROR;\n        }\n\n        b = cl->buf;\n\n        ngx_memzero(b, sizeof(ngx_buf_t));\n\n        b->pos = f->pos;\n        b->start = buf->start;\n        b->end = buf->end;\n        b->tag = p->tag;\n        b->temporary = 1;\n        b->recycled = 1;\n\n        *prev = b;\n        prev = &b->shadow;\n\n        if (p->in) {\n            *p->last_in = cl;\n        } else {\n            p->in = cl;\n        }\n        p->last_in = &cl->next;\n\n\n        /* STUB */ b->num = buf->num;\n\n        ngx_log_debug2(NGX_LOG_DEBUG_EVENT, p->log, 0,\n                       \"input buf #%d %p\", b->num, b->pos);\n\n        if (f->pos + f->length <= f->last) {\n            f->state = ngx_http_fastcgi_st_padding;\n            f->pos += f->length;\n            b->last = f->pos;\n\n        } else {\n            f->length -= f->last - f->pos;\n            f->pos = f->last;\n            b->last = f->last;\n        }\n\n        if (f->rest > 0) {\n\n            if (b->last - b->pos > f->rest) {\n                ngx_log_error(NGX_LOG_WARN, p->log, 0,\n                              \"upstream sent more data than specified in \"\n                              \"\\\"Content-Length\\\" header\");\n\n                b->last = b->pos + f->rest;\n                p->upstream_done = 1;\n\n                break;\n            }\n\n            f->rest -= b->last - b->pos;\n        }\n    }\n\n    if (flcf->keep_conn) {\n\n        /* set p->length, minimal amount of data we want to see */\n\n        if (f->state < ngx_http_fastcgi_st_data) {\n            p->length = 1;\n\n        } else if (f->state == ngx_http_fastcgi_st_padding) {\n            p->length = f->padding;\n\n        } else {\n            /* ngx_http_fastcgi_st_data */\n\n            p->length = f->length;\n        }\n    }\n\n    if (b) {\n        b->shadow = buf;\n        b->last_shadow = 1;\n\n        ngx_log_debug2(NGX_LOG_DEBUG_EVENT, p->log, 0,\n                       \"input buf %p %z\", b->pos, b->last - b->pos);\n\n        return NGX_OK;\n    }\n\n    /* there is no data record in the buf, add it to free chain */\n\n    if (ngx_event_pipe_add_free_buf(p, buf) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_fastcgi_non_buffered_filter(void *data, ssize_t bytes)\n{\n    u_char                  *m, *msg;\n    ngx_int_t                rc;\n    ngx_buf_t               *b, *buf;\n    ngx_chain_t             *cl, **ll;\n    ngx_http_request_t      *r;\n    ngx_http_upstream_t     *u;\n    ngx_http_fastcgi_ctx_t  *f;\n\n    r = data;\n    f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);\n\n    u = r->upstream;\n    buf = &u->buffer;\n\n    buf->pos = buf->last;\n    buf->last += bytes;\n\n    for (cl = u->out_bufs, ll = &u->out_bufs; cl; cl = cl->next) {\n        ll = &cl->next;\n    }\n\n    f->pos = buf->pos;\n    f->last = buf->last;\n\n    for ( ;; ) {\n        if (f->state < ngx_http_fastcgi_st_data) {\n\n            rc = ngx_http_fastcgi_process_record(r, f);\n\n            if (rc == NGX_AGAIN) {\n                break;\n            }\n\n            if (rc == NGX_ERROR) {\n                return NGX_ERROR;\n            }\n\n            if (f->type == NGX_HTTP_FASTCGI_STDOUT && f->length == 0) {\n                f->state = ngx_http_fastcgi_st_padding;\n\n                ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                               \"http fastcgi closed stdout\");\n\n                continue;\n            }\n        }\n\n        if (f->state == ngx_http_fastcgi_st_padding) {\n\n            if (f->type == NGX_HTTP_FASTCGI_END_REQUEST) {\n\n                if (f->rest > 0) {\n                    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                                  \"upstream prematurely closed \"\n                                  \"FastCGI request\");\n                    u->error = 1;\n                    break;\n                }\n\n                if (f->pos + f->padding < f->last) {\n                    u->length = 0;\n                    break;\n                }\n\n                if (f->pos + f->padding == f->last) {\n                    u->length = 0;\n                    u->keepalive = 1;\n                    break;\n                }\n\n                f->padding -= f->last - f->pos;\n\n                break;\n            }\n\n            if (f->pos + f->padding < f->last) {\n                f->state = ngx_http_fastcgi_st_version;\n                f->pos += f->padding;\n\n                continue;\n            }\n\n            if (f->pos + f->padding == f->last) {\n                f->state = ngx_http_fastcgi_st_version;\n\n                break;\n            }\n\n            f->padding -= f->last - f->pos;\n\n            break;\n        }\n\n\n        /* f->state == ngx_http_fastcgi_st_data */\n\n        if (f->type == NGX_HTTP_FASTCGI_STDERR) {\n\n            if (f->length) {\n\n                if (f->pos == f->last) {\n                    break;\n                }\n\n                msg = f->pos;\n\n                if (f->pos + f->length <= f->last) {\n                    f->pos += f->length;\n                    f->length = 0;\n                    f->state = ngx_http_fastcgi_st_padding;\n\n                } else {\n                    f->length -= f->last - f->pos;\n                    f->pos = f->last;\n                }\n\n                for (m = f->pos - 1; msg < m; m--) {\n                    if (*m != LF && *m != CR && *m != '.' && *m != ' ') {\n                        break;\n                    }\n                }\n\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                              \"FastCGI sent in stderr: \\\"%*s\\\"\",\n                              m + 1 - msg, msg);\n\n            } else {\n                f->state = ngx_http_fastcgi_st_padding;\n            }\n\n            continue;\n        }\n\n        if (f->type == NGX_HTTP_FASTCGI_END_REQUEST) {\n\n            if (f->pos + f->length <= f->last) {\n                f->state = ngx_http_fastcgi_st_padding;\n                f->pos += f->length;\n\n                continue;\n            }\n\n            f->length -= f->last - f->pos;\n\n            break;\n        }\n\n\n        /* f->type == NGX_HTTP_FASTCGI_STDOUT */\n\n        if (f->pos == f->last) {\n            break;\n        }\n\n        if (f->rest == 0) {\n            ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,\n                          \"upstream sent more data than specified in \"\n                          \"\\\"Content-Length\\\" header\");\n            u->length = 0;\n            break;\n        }\n\n        cl = ngx_chain_get_free_buf(r->pool, &u->free_bufs);\n        if (cl == NULL) {\n            return NGX_ERROR;\n        }\n\n        *ll = cl;\n        ll = &cl->next;\n\n        b = cl->buf;\n\n        b->flush = 1;\n        b->memory = 1;\n\n        b->pos = f->pos;\n        b->tag = u->output.tag;\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"http fastcgi output buf %p\", b->pos);\n\n        if (f->pos + f->length <= f->last) {\n            f->state = ngx_http_fastcgi_st_padding;\n            f->pos += f->length;\n            b->last = f->pos;\n\n        } else {\n            f->length -= f->last - f->pos;\n            f->pos = f->last;\n            b->last = f->last;\n        }\n\n        if (f->rest > 0) {\n\n            if (b->last - b->pos > f->rest) {\n                ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,\n                              \"upstream sent more data than specified in \"\n                              \"\\\"Content-Length\\\" header\");\n\n                b->last = b->pos + f->rest;\n                u->length = 0;\n\n                break;\n            }\n\n            f->rest -= b->last - b->pos;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_fastcgi_process_record(ngx_http_request_t *r,\n    ngx_http_fastcgi_ctx_t *f)\n{\n    u_char                     ch, *p;\n    ngx_http_fastcgi_state_e   state;\n\n    state = f->state;\n\n    for (p = f->pos; p < f->last; p++) {\n\n        ch = *p;\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"http fastcgi record byte: %02Xd\", ch);\n\n        switch (state) {\n\n        case ngx_http_fastcgi_st_version:\n            if (ch != 1) {\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                              \"upstream sent unsupported FastCGI \"\n                              \"protocol version: %d\", ch);\n                return NGX_ERROR;\n            }\n            state = ngx_http_fastcgi_st_type;\n            break;\n\n        case ngx_http_fastcgi_st_type:\n            switch (ch) {\n            case NGX_HTTP_FASTCGI_STDOUT:\n            case NGX_HTTP_FASTCGI_STDERR:\n            case NGX_HTTP_FASTCGI_END_REQUEST:\n                f->type = (ngx_uint_t) ch;\n                break;\n            default:\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                              \"upstream sent invalid FastCGI \"\n                              \"record type: %d\", ch);\n                return NGX_ERROR;\n\n            }\n            state = ngx_http_fastcgi_st_request_id_hi;\n            break;\n\n        /* we support the single request per connection */\n\n        case ngx_http_fastcgi_st_request_id_hi:\n            if (ch != 0) {\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                              \"upstream sent unexpected FastCGI \"\n                              \"request id high byte: %d\", ch);\n                return NGX_ERROR;\n            }\n            state = ngx_http_fastcgi_st_request_id_lo;\n            break;\n\n        case ngx_http_fastcgi_st_request_id_lo:\n            if (ch != 1) {\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                              \"upstream sent unexpected FastCGI \"\n                              \"request id low byte: %d\", ch);\n                return NGX_ERROR;\n            }\n            state = ngx_http_fastcgi_st_content_length_hi;\n            break;\n\n        case ngx_http_fastcgi_st_content_length_hi:\n            f->length = ch << 8;\n            state = ngx_http_fastcgi_st_content_length_lo;\n            break;\n\n        case ngx_http_fastcgi_st_content_length_lo:\n            f->length |= (size_t) ch;\n            state = ngx_http_fastcgi_st_padding_length;\n            break;\n\n        case ngx_http_fastcgi_st_padding_length:\n            f->padding = (size_t) ch;\n            state = ngx_http_fastcgi_st_reserved;\n            break;\n\n        case ngx_http_fastcgi_st_reserved:\n            state = ngx_http_fastcgi_st_data;\n\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"http fastcgi record length: %z\", f->length);\n\n            f->pos = p + 1;\n            f->state = state;\n\n            return NGX_OK;\n\n        /* suppress warning */\n        case ngx_http_fastcgi_st_data:\n        case ngx_http_fastcgi_st_padding:\n            break;\n        }\n    }\n\n    f->pos = p;\n    f->state = state;\n\n    return NGX_AGAIN;\n}\n\n\nstatic void\nngx_http_fastcgi_abort_request(ngx_http_request_t *r)\n{\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"abort http fastcgi request\");\n\n    return;\n}\n\n\nstatic void\nngx_http_fastcgi_finalize_request(ngx_http_request_t *r, ngx_int_t rc)\n{\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"finalize http fastcgi request\");\n\n    return;\n}\n\n\nstatic ngx_int_t\nngx_http_fastcgi_add_variables(ngx_conf_t *cf)\n{\n    ngx_http_variable_t  *var, *v;\n\n    for (v = ngx_http_fastcgi_vars; v->name.len; v++) {\n        var = ngx_http_add_variable(cf, &v->name, v->flags);\n        if (var == NULL) {\n            return NGX_ERROR;\n        }\n\n        var->get_handler = v->get_handler;\n        var->data = v->data;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void *\nngx_http_fastcgi_create_main_conf(ngx_conf_t *cf)\n{\n    ngx_http_fastcgi_main_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_fastcgi_main_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n#if (NGX_HTTP_CACHE)\n    if (ngx_array_init(&conf->caches, cf->pool, 4,\n                       sizeof(ngx_http_file_cache_t *))\n        != NGX_OK)\n    {\n        return NULL;\n    }\n#endif\n\n    return conf;\n}\n\n\nstatic void *\nngx_http_fastcgi_create_loc_conf(ngx_conf_t *cf)\n{\n    ngx_http_fastcgi_loc_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_fastcgi_loc_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc():\n     *\n     *     conf->upstream.bufs.num = 0;\n     *     conf->upstream.ignore_headers = 0;\n     *     conf->upstream.next_upstream = 0;\n     *     conf->upstream.cache_zone = NULL;\n     *     conf->upstream.cache_use_stale = 0;\n     *     conf->upstream.cache_methods = 0;\n     *     conf->upstream.temp_path = NULL;\n     *     conf->upstream.hide_headers_hash = { NULL, 0 };\n     *     conf->upstream.store_lengths = NULL;\n     *     conf->upstream.store_values = NULL;\n     *\n     *     conf->index.len = { 0, NULL };\n     */\n\n    conf->upstream.store = NGX_CONF_UNSET;\n    conf->upstream.store_access = NGX_CONF_UNSET_UINT;\n    conf->upstream.next_upstream_tries = NGX_CONF_UNSET_UINT;\n    conf->upstream.buffering = NGX_CONF_UNSET;\n    conf->upstream.request_buffering = NGX_CONF_UNSET;\n    conf->upstream.ignore_client_abort = NGX_CONF_UNSET;\n    conf->upstream.force_ranges = NGX_CONF_UNSET;\n\n    conf->upstream.local = NGX_CONF_UNSET_PTR;\n    conf->upstream.socket_keepalive = NGX_CONF_UNSET;\n\n    conf->upstream.connect_timeout = NGX_CONF_UNSET_MSEC;\n    conf->upstream.send_timeout = NGX_CONF_UNSET_MSEC;\n    conf->upstream.read_timeout = NGX_CONF_UNSET_MSEC;\n    conf->upstream.next_upstream_timeout = NGX_CONF_UNSET_MSEC;\n\n    conf->upstream.send_lowat = NGX_CONF_UNSET_SIZE;\n    conf->upstream.buffer_size = NGX_CONF_UNSET_SIZE;\n    conf->upstream.limit_rate = NGX_CONF_UNSET_SIZE;\n\n    conf->upstream.busy_buffers_size_conf = NGX_CONF_UNSET_SIZE;\n    conf->upstream.max_temp_file_size_conf = NGX_CONF_UNSET_SIZE;\n    conf->upstream.temp_file_write_size_conf = NGX_CONF_UNSET_SIZE;\n\n    conf->upstream.pass_request_headers = NGX_CONF_UNSET;\n    conf->upstream.pass_request_body = NGX_CONF_UNSET;\n\n#if (NGX_HTTP_CACHE)\n    conf->upstream.cache = NGX_CONF_UNSET;\n    conf->upstream.cache_min_uses = NGX_CONF_UNSET_UINT;\n    conf->upstream.cache_max_range_offset = NGX_CONF_UNSET;\n    conf->upstream.cache_bypass = NGX_CONF_UNSET_PTR;\n    conf->upstream.no_cache = NGX_CONF_UNSET_PTR;\n    conf->upstream.cache_valid = NGX_CONF_UNSET_PTR;\n    conf->upstream.cache_lock = NGX_CONF_UNSET;\n    conf->upstream.cache_lock_timeout = NGX_CONF_UNSET_MSEC;\n    conf->upstream.cache_lock_age = NGX_CONF_UNSET_MSEC;\n    conf->upstream.cache_revalidate = NGX_CONF_UNSET;\n    conf->upstream.cache_background_update = NGX_CONF_UNSET;\n#endif\n\n    conf->upstream.hide_headers = NGX_CONF_UNSET_PTR;\n    conf->upstream.pass_headers = NGX_CONF_UNSET_PTR;\n\n    conf->upstream.intercept_errors = NGX_CONF_UNSET;\n\n    /* \"fastcgi_cyclic_temp_file\" is disabled */\n    conf->upstream.cyclic_temp_file = 0;\n\n    conf->upstream.change_buffering = 1;\n\n    conf->catch_stderr = NGX_CONF_UNSET_PTR;\n\n    conf->keep_conn = NGX_CONF_UNSET;\n\n    ngx_str_set(&conf->upstream.module, \"fastcgi\");\n\n#if (T_NGX_SOCKET_BUFFER)\n    conf->sndbuf = NGX_CONF_UNSET_SIZE;\n    conf->rcvbuf = NGX_CONF_UNSET_SIZE;\n#endif\n\n    return conf;\n}\n\n\nstatic char *\nngx_http_fastcgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_http_fastcgi_loc_conf_t *prev = parent;\n    ngx_http_fastcgi_loc_conf_t *conf = child;\n\n    size_t                        size;\n    ngx_int_t                     rc;\n    ngx_hash_init_t               hash;\n    ngx_http_core_loc_conf_t     *clcf;\n\n#if (NGX_HTTP_CACHE)\n\n    if (conf->upstream.store > 0) {\n        conf->upstream.cache = 0;\n    }\n\n    if (conf->upstream.cache > 0) {\n        conf->upstream.store = 0;\n    }\n\n#endif\n\n    if (conf->upstream.store == NGX_CONF_UNSET) {\n        ngx_conf_merge_value(conf->upstream.store,\n                              prev->upstream.store, 0);\n\n        conf->upstream.store_lengths = prev->upstream.store_lengths;\n        conf->upstream.store_values = prev->upstream.store_values;\n    }\n\n    ngx_conf_merge_uint_value(conf->upstream.store_access,\n                              prev->upstream.store_access, 0600);\n\n    ngx_conf_merge_uint_value(conf->upstream.next_upstream_tries,\n                              prev->upstream.next_upstream_tries, 0);\n\n    ngx_conf_merge_value(conf->upstream.buffering,\n                              prev->upstream.buffering, 1);\n\n    ngx_conf_merge_value(conf->upstream.request_buffering,\n                              prev->upstream.request_buffering, 1);\n\n    ngx_conf_merge_value(conf->upstream.ignore_client_abort,\n                              prev->upstream.ignore_client_abort, 0);\n\n    ngx_conf_merge_value(conf->upstream.force_ranges,\n                              prev->upstream.force_ranges, 0);\n\n    ngx_conf_merge_ptr_value(conf->upstream.local,\n                              prev->upstream.local, NULL);\n\n    ngx_conf_merge_value(conf->upstream.socket_keepalive,\n                              prev->upstream.socket_keepalive, 0);\n\n    ngx_conf_merge_msec_value(conf->upstream.connect_timeout,\n                              prev->upstream.connect_timeout, 60000);\n\n    ngx_conf_merge_msec_value(conf->upstream.send_timeout,\n                              prev->upstream.send_timeout, 60000);\n\n    ngx_conf_merge_msec_value(conf->upstream.read_timeout,\n                              prev->upstream.read_timeout, 60000);\n\n    ngx_conf_merge_msec_value(conf->upstream.next_upstream_timeout,\n                              prev->upstream.next_upstream_timeout, 0);\n\n    ngx_conf_merge_size_value(conf->upstream.send_lowat,\n                              prev->upstream.send_lowat, 0);\n\n    ngx_conf_merge_size_value(conf->upstream.buffer_size,\n                              prev->upstream.buffer_size,\n                              (size_t) ngx_pagesize);\n\n    ngx_conf_merge_size_value(conf->upstream.limit_rate,\n                              prev->upstream.limit_rate, 0);\n\n#if (T_NGX_SOCKET_BUFFER)\n    ngx_conf_merge_size_value(conf->sndbuf, prev->sndbuf, (size_t) 0);\n    ngx_conf_merge_size_value(conf->rcvbuf, prev->rcvbuf, (size_t) 0);\n#endif\n\n    ngx_conf_merge_bufs_value(conf->upstream.bufs, prev->upstream.bufs,\n                              8, ngx_pagesize);\n\n    if (conf->upstream.bufs.num < 2) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"there must be at least 2 \\\"fastcgi_buffers\\\"\");\n        return NGX_CONF_ERROR;\n    }\n\n\n    size = conf->upstream.buffer_size;\n    if (size < conf->upstream.bufs.size) {\n        size = conf->upstream.bufs.size;\n    }\n\n\n    ngx_conf_merge_size_value(conf->upstream.busy_buffers_size_conf,\n                              prev->upstream.busy_buffers_size_conf,\n                              NGX_CONF_UNSET_SIZE);\n\n    if (conf->upstream.busy_buffers_size_conf == NGX_CONF_UNSET_SIZE) {\n        conf->upstream.busy_buffers_size = 2 * size;\n    } else {\n        conf->upstream.busy_buffers_size =\n                                         conf->upstream.busy_buffers_size_conf;\n    }\n\n    if (conf->upstream.busy_buffers_size < size) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n             \"\\\"fastcgi_busy_buffers_size\\\" must be equal to or greater than \"\n             \"the maximum of the value of \\\"fastcgi_buffer_size\\\" and \"\n             \"one of the \\\"fastcgi_buffers\\\"\");\n\n        return NGX_CONF_ERROR;\n    }\n\n    if (conf->upstream.busy_buffers_size\n        > (conf->upstream.bufs.num - 1) * conf->upstream.bufs.size)\n    {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n             \"\\\"fastcgi_busy_buffers_size\\\" must be less than \"\n             \"the size of all \\\"fastcgi_buffers\\\" minus one buffer\");\n\n        return NGX_CONF_ERROR;\n    }\n\n\n    ngx_conf_merge_size_value(conf->upstream.temp_file_write_size_conf,\n                              prev->upstream.temp_file_write_size_conf,\n                              NGX_CONF_UNSET_SIZE);\n\n    if (conf->upstream.temp_file_write_size_conf == NGX_CONF_UNSET_SIZE) {\n        conf->upstream.temp_file_write_size = 2 * size;\n    } else {\n        conf->upstream.temp_file_write_size =\n                                      conf->upstream.temp_file_write_size_conf;\n    }\n\n    if (conf->upstream.temp_file_write_size < size) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n             \"\\\"fastcgi_temp_file_write_size\\\" must be equal to or greater \"\n             \"than the maximum of the value of \\\"fastcgi_buffer_size\\\" and \"\n             \"one of the \\\"fastcgi_buffers\\\"\");\n\n        return NGX_CONF_ERROR;\n    }\n\n\n    ngx_conf_merge_size_value(conf->upstream.max_temp_file_size_conf,\n                              prev->upstream.max_temp_file_size_conf,\n                              NGX_CONF_UNSET_SIZE);\n\n    if (conf->upstream.max_temp_file_size_conf == NGX_CONF_UNSET_SIZE) {\n        conf->upstream.max_temp_file_size = 1024 * 1024 * 1024;\n    } else {\n        conf->upstream.max_temp_file_size =\n                                        conf->upstream.max_temp_file_size_conf;\n    }\n\n    if (conf->upstream.max_temp_file_size != 0\n        && conf->upstream.max_temp_file_size < size)\n    {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n             \"\\\"fastcgi_max_temp_file_size\\\" must be equal to zero to disable \"\n             \"temporary files usage or must be equal to or greater than \"\n             \"the maximum of the value of \\\"fastcgi_buffer_size\\\" and \"\n             \"one of the \\\"fastcgi_buffers\\\"\");\n\n        return NGX_CONF_ERROR;\n    }\n\n\n    ngx_conf_merge_bitmask_value(conf->upstream.ignore_headers,\n                              prev->upstream.ignore_headers,\n                              NGX_CONF_BITMASK_SET);\n\n\n    ngx_conf_merge_bitmask_value(conf->upstream.next_upstream,\n                              prev->upstream.next_upstream,\n                              (NGX_CONF_BITMASK_SET\n                               |NGX_HTTP_UPSTREAM_FT_ERROR\n                               |NGX_HTTP_UPSTREAM_FT_TIMEOUT));\n\n    if (conf->upstream.next_upstream & NGX_HTTP_UPSTREAM_FT_OFF) {\n        conf->upstream.next_upstream = NGX_CONF_BITMASK_SET\n                                       |NGX_HTTP_UPSTREAM_FT_OFF;\n    }\n\n    if (ngx_conf_merge_path_value(cf, &conf->upstream.temp_path,\n                              prev->upstream.temp_path,\n                              &ngx_http_fastcgi_temp_path)\n        != NGX_OK)\n    {\n        return NGX_CONF_ERROR;\n    }\n\n#if (NGX_HTTP_CACHE)\n\n    if (conf->upstream.cache == NGX_CONF_UNSET) {\n        ngx_conf_merge_value(conf->upstream.cache,\n                              prev->upstream.cache, 0);\n\n        conf->upstream.cache_zone = prev->upstream.cache_zone;\n        conf->upstream.cache_value = prev->upstream.cache_value;\n    }\n\n    if (conf->upstream.cache_zone && conf->upstream.cache_zone->data == NULL) {\n        ngx_shm_zone_t  *shm_zone;\n\n        shm_zone = conf->upstream.cache_zone;\n\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"\\\"fastcgi_cache\\\" zone \\\"%V\\\" is unknown\",\n                           &shm_zone->shm.name);\n\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_conf_merge_uint_value(conf->upstream.cache_min_uses,\n                              prev->upstream.cache_min_uses, 1);\n\n    ngx_conf_merge_off_value(conf->upstream.cache_max_range_offset,\n                              prev->upstream.cache_max_range_offset,\n                              NGX_MAX_OFF_T_VALUE);\n\n    ngx_conf_merge_bitmask_value(conf->upstream.cache_use_stale,\n                              prev->upstream.cache_use_stale,\n                              (NGX_CONF_BITMASK_SET\n                               |NGX_HTTP_UPSTREAM_FT_OFF));\n\n    if (conf->upstream.cache_use_stale & NGX_HTTP_UPSTREAM_FT_OFF) {\n        conf->upstream.cache_use_stale = NGX_CONF_BITMASK_SET\n                                         |NGX_HTTP_UPSTREAM_FT_OFF;\n    }\n\n    if (conf->upstream.cache_use_stale & NGX_HTTP_UPSTREAM_FT_ERROR) {\n        conf->upstream.cache_use_stale |= NGX_HTTP_UPSTREAM_FT_NOLIVE;\n    }\n\n    if (conf->upstream.cache_methods == 0) {\n        conf->upstream.cache_methods = prev->upstream.cache_methods;\n    }\n\n    conf->upstream.cache_methods |= NGX_HTTP_GET|NGX_HTTP_HEAD;\n\n    ngx_conf_merge_ptr_value(conf->upstream.cache_bypass,\n                             prev->upstream.cache_bypass, NULL);\n\n    ngx_conf_merge_ptr_value(conf->upstream.no_cache,\n                             prev->upstream.no_cache, NULL);\n\n    ngx_conf_merge_ptr_value(conf->upstream.cache_valid,\n                             prev->upstream.cache_valid, NULL);\n\n    if (conf->cache_key.value.data == NULL) {\n        conf->cache_key = prev->cache_key;\n    }\n\n    if (conf->upstream.cache && conf->cache_key.value.data == NULL) {\n        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                           \"no \\\"fastcgi_cache_key\\\" for \\\"fastcgi_cache\\\"\");\n    }\n\n    ngx_conf_merge_value(conf->upstream.cache_lock,\n                              prev->upstream.cache_lock, 0);\n\n    ngx_conf_merge_msec_value(conf->upstream.cache_lock_timeout,\n                              prev->upstream.cache_lock_timeout, 5000);\n\n    ngx_conf_merge_msec_value(conf->upstream.cache_lock_age,\n                              prev->upstream.cache_lock_age, 5000);\n\n    ngx_conf_merge_value(conf->upstream.cache_revalidate,\n                              prev->upstream.cache_revalidate, 0);\n\n    ngx_conf_merge_value(conf->upstream.cache_background_update,\n                              prev->upstream.cache_background_update, 0);\n\n#endif\n\n    ngx_conf_merge_value(conf->upstream.pass_request_headers,\n                              prev->upstream.pass_request_headers, 1);\n    ngx_conf_merge_value(conf->upstream.pass_request_body,\n                              prev->upstream.pass_request_body, 1);\n\n    ngx_conf_merge_value(conf->upstream.intercept_errors,\n                              prev->upstream.intercept_errors, 0);\n\n    ngx_conf_merge_ptr_value(conf->catch_stderr, prev->catch_stderr, NULL);\n\n    ngx_conf_merge_value(conf->keep_conn, prev->keep_conn, 0);\n\n\n    ngx_conf_merge_str_value(conf->index, prev->index, \"\");\n\n    hash.max_size = 512;\n    hash.bucket_size = ngx_align(64, ngx_cacheline_size);\n    hash.name = \"fastcgi_hide_headers_hash\";\n\n    if (ngx_http_upstream_hide_headers_hash(cf, &conf->upstream,\n             &prev->upstream, ngx_http_fastcgi_hide_headers, &hash)\n        != NGX_OK)\n    {\n        return NGX_CONF_ERROR;\n    }\n\n    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);\n\n    if (clcf->noname\n        && conf->upstream.upstream == NULL && conf->fastcgi_lengths == NULL)\n    {\n        conf->upstream.upstream = prev->upstream.upstream;\n        conf->fastcgi_lengths = prev->fastcgi_lengths;\n        conf->fastcgi_values = prev->fastcgi_values;\n    }\n\n    if (clcf->lmt_excpt && clcf->handler == NULL\n        && (conf->upstream.upstream || conf->fastcgi_lengths))\n    {\n        clcf->handler = ngx_http_fastcgi_handler;\n    }\n\n#if (NGX_PCRE)\n    if (conf->split_regex == NULL) {\n        conf->split_regex = prev->split_regex;\n        conf->split_name = prev->split_name;\n    }\n#endif\n\n    if (conf->params_source == NULL) {\n        conf->params = prev->params;\n#if (NGX_HTTP_CACHE)\n        conf->params_cache = prev->params_cache;\n#endif\n        conf->params_source = prev->params_source;\n    }\n\n    rc = ngx_http_fastcgi_init_params(cf, conf, &conf->params, NULL);\n    if (rc != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n#if (NGX_HTTP_CACHE)\n\n    if (conf->upstream.cache) {\n        rc = ngx_http_fastcgi_init_params(cf, conf, &conf->params_cache,\n                                          ngx_http_fastcgi_cache_headers);\n        if (rc != NGX_OK) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n#endif\n\n    /*\n     * special handling to preserve conf->params in the \"http\" section\n     * to inherit it to all servers\n     */\n\n    if (prev->params.hash.buckets == NULL\n        && conf->params_source == prev->params_source)\n    {\n        prev->params = conf->params;\n#if (NGX_HTTP_CACHE)\n        prev->params_cache = conf->params_cache;\n#endif\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_fastcgi_init_params(ngx_conf_t *cf, ngx_http_fastcgi_loc_conf_t *conf,\n    ngx_http_fastcgi_params_t *params, ngx_keyval_t *default_params)\n{\n    u_char                       *p;\n    size_t                        size;\n    uintptr_t                    *code;\n    ngx_uint_t                    i, nsrc;\n    ngx_array_t                   headers_names, params_merged;\n    ngx_keyval_t                 *h;\n    ngx_hash_key_t               *hk;\n    ngx_hash_init_t               hash;\n    ngx_http_upstream_param_t    *src, *s;\n    ngx_http_script_compile_t     sc;\n    ngx_http_script_copy_code_t  *copy;\n\n    if (params->hash.buckets) {\n        return NGX_OK;\n    }\n\n    if (conf->params_source == NULL && default_params == NULL) {\n        params->hash.buckets = (void *) 1;\n        return NGX_OK;\n    }\n\n    params->lengths = ngx_array_create(cf->pool, 64, 1);\n    if (params->lengths == NULL) {\n        return NGX_ERROR;\n    }\n\n    params->values = ngx_array_create(cf->pool, 512, 1);\n    if (params->values == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (ngx_array_init(&headers_names, cf->temp_pool, 4, sizeof(ngx_hash_key_t))\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    if (conf->params_source) {\n        src = conf->params_source->elts;\n        nsrc = conf->params_source->nelts;\n\n    } else {\n        src = NULL;\n        nsrc = 0;\n    }\n\n    if (default_params) {\n        if (ngx_array_init(&params_merged, cf->temp_pool, 4,\n                           sizeof(ngx_http_upstream_param_t))\n            != NGX_OK)\n        {\n            return NGX_ERROR;\n        }\n\n        for (i = 0; i < nsrc; i++) {\n\n            s = ngx_array_push(&params_merged);\n            if (s == NULL) {\n                return NGX_ERROR;\n            }\n\n            *s = src[i];\n        }\n\n        h = default_params;\n\n        while (h->key.len) {\n\n            src = params_merged.elts;\n            nsrc = params_merged.nelts;\n\n            for (i = 0; i < nsrc; i++) {\n                if (ngx_strcasecmp(h->key.data, src[i].key.data) == 0) {\n                    goto next;\n                }\n            }\n\n            s = ngx_array_push(&params_merged);\n            if (s == NULL) {\n                return NGX_ERROR;\n            }\n\n            s->key = h->key;\n            s->value = h->value;\n            s->skip_empty = 1;\n\n        next:\n\n            h++;\n        }\n\n        src = params_merged.elts;\n        nsrc = params_merged.nelts;\n    }\n\n    for (i = 0; i < nsrc; i++) {\n\n        if (src[i].key.len > sizeof(\"HTTP_\") - 1\n            && ngx_strncmp(src[i].key.data, \"HTTP_\", sizeof(\"HTTP_\") - 1) == 0)\n        {\n            hk = ngx_array_push(&headers_names);\n            if (hk == NULL) {\n                return NGX_ERROR;\n            }\n\n            hk->key.len = src[i].key.len - 5;\n            hk->key.data = src[i].key.data + 5;\n            hk->key_hash = ngx_hash_key_lc(hk->key.data, hk->key.len);\n            hk->value = (void *) 1;\n\n            if (src[i].value.len == 0) {\n                continue;\n            }\n        }\n\n        copy = ngx_array_push_n(params->lengths,\n                                sizeof(ngx_http_script_copy_code_t));\n        if (copy == NULL) {\n            return NGX_ERROR;\n        }\n\n        copy->code = (ngx_http_script_code_pt) (void *)\n                                                 ngx_http_script_copy_len_code;\n        copy->len = src[i].key.len;\n\n        copy = ngx_array_push_n(params->lengths,\n                                sizeof(ngx_http_script_copy_code_t));\n        if (copy == NULL) {\n            return NGX_ERROR;\n        }\n\n        copy->code = (ngx_http_script_code_pt) (void *)\n                                                 ngx_http_script_copy_len_code;\n        copy->len = src[i].skip_empty;\n\n\n        size = (sizeof(ngx_http_script_copy_code_t)\n                + src[i].key.len + sizeof(uintptr_t) - 1)\n               & ~(sizeof(uintptr_t) - 1);\n\n        copy = ngx_array_push_n(params->values, size);\n        if (copy == NULL) {\n            return NGX_ERROR;\n        }\n\n        copy->code = ngx_http_script_copy_code;\n        copy->len = src[i].key.len;\n\n        p = (u_char *) copy + sizeof(ngx_http_script_copy_code_t);\n        ngx_memcpy(p, src[i].key.data, src[i].key.len);\n\n\n        ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));\n\n        sc.cf = cf;\n        sc.source = &src[i].value;\n        sc.flushes = &params->flushes;\n        sc.lengths = &params->lengths;\n        sc.values = &params->values;\n\n        if (ngx_http_script_compile(&sc) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        code = ngx_array_push_n(params->lengths, sizeof(uintptr_t));\n        if (code == NULL) {\n            return NGX_ERROR;\n        }\n\n        *code = (uintptr_t) NULL;\n\n\n        code = ngx_array_push_n(params->values, sizeof(uintptr_t));\n        if (code == NULL) {\n            return NGX_ERROR;\n        }\n\n        *code = (uintptr_t) NULL;\n    }\n\n    code = ngx_array_push_n(params->lengths, sizeof(uintptr_t));\n    if (code == NULL) {\n        return NGX_ERROR;\n    }\n\n    *code = (uintptr_t) NULL;\n\n    params->number = headers_names.nelts;\n\n    hash.hash = &params->hash;\n    hash.key = ngx_hash_key_lc;\n    hash.max_size = 512;\n    hash.bucket_size = 64;\n    hash.name = \"fastcgi_params_hash\";\n    hash.pool = cf->pool;\n    hash.temp_pool = NULL;\n\n    return ngx_hash_init(&hash, headers_names.elts, headers_names.nelts);\n}\n\n\nstatic ngx_int_t\nngx_http_fastcgi_script_name_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    u_char                       *p;\n    ngx_http_fastcgi_ctx_t       *f;\n    ngx_http_fastcgi_loc_conf_t  *flcf;\n\n    flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);\n\n    f = ngx_http_fastcgi_split(r, flcf);\n\n    if (f == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (f->script_name.len == 0\n        || f->script_name.data[f->script_name.len - 1] != '/')\n    {\n        v->len = f->script_name.len;\n        v->valid = 1;\n        v->no_cacheable = 0;\n        v->not_found = 0;\n        v->data = f->script_name.data;\n\n        return NGX_OK;\n    }\n\n    v->len = f->script_name.len + flcf->index.len;\n\n    v->data = ngx_pnalloc(r->pool, v->len);\n    if (v->data == NULL) {\n        return NGX_ERROR;\n    }\n\n    p = ngx_copy(v->data, f->script_name.data, f->script_name.len);\n    ngx_memcpy(p, flcf->index.data, flcf->index.len);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_fastcgi_path_info_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    ngx_http_fastcgi_ctx_t       *f;\n    ngx_http_fastcgi_loc_conf_t  *flcf;\n\n    flcf = ngx_http_get_module_loc_conf(r, ngx_http_fastcgi_module);\n\n    f = ngx_http_fastcgi_split(r, flcf);\n\n    if (f == NULL) {\n        return NGX_ERROR;\n    }\n\n    v->len = f->path_info.len;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = f->path_info.data;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_http_fastcgi_ctx_t *\nngx_http_fastcgi_split(ngx_http_request_t *r, ngx_http_fastcgi_loc_conf_t *flcf)\n{\n    ngx_http_fastcgi_ctx_t       *f;\n#if (NGX_PCRE)\n    ngx_int_t                     n;\n    int                           captures[(1 + 2) * 3];\n\n    f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);\n\n    if (f == NULL) {\n        f = ngx_pcalloc(r->pool, sizeof(ngx_http_fastcgi_ctx_t));\n        if (f == NULL) {\n            return NULL;\n        }\n\n        ngx_http_set_ctx(r, f, ngx_http_fastcgi_module);\n    }\n\n    if (f->script_name.len) {\n        return f;\n    }\n\n    if (flcf->split_regex == NULL) {\n        f->script_name = r->uri;\n        return f;\n    }\n\n    n = ngx_regex_exec(flcf->split_regex, &r->uri, captures, (1 + 2) * 3);\n\n    if (n >= 0) { /* match */\n        f->script_name.len = captures[3] - captures[2];\n        f->script_name.data = r->uri.data + captures[2];\n\n        f->path_info.len = captures[5] - captures[4];\n        f->path_info.data = r->uri.data + captures[4];\n\n        return f;\n    }\n\n    if (n == NGX_REGEX_NO_MATCHED) {\n        f->script_name = r->uri;\n        return f;\n    }\n\n    ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,\n                  ngx_regex_exec_n \" failed: %i on \\\"%V\\\" using \\\"%V\\\"\",\n                  n, &r->uri, &flcf->split_name);\n    return NULL;\n\n#else\n\n    f = ngx_http_get_module_ctx(r, ngx_http_fastcgi_module);\n\n    if (f == NULL) {\n        f = ngx_pcalloc(r->pool, sizeof(ngx_http_fastcgi_ctx_t));\n        if (f == NULL) {\n            return NULL;\n        }\n\n        ngx_http_set_ctx(r, f, ngx_http_fastcgi_module);\n    }\n\n    f->script_name = r->uri;\n\n    return f;\n\n#endif\n}\n\n\nstatic char *\nngx_http_fastcgi_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_fastcgi_loc_conf_t *flcf = conf;\n\n    ngx_url_t                   u;\n    ngx_str_t                  *value, *url;\n    ngx_uint_t                  n;\n    ngx_http_core_loc_conf_t   *clcf;\n    ngx_http_script_compile_t   sc;\n\n    if (flcf->upstream.upstream || flcf->fastcgi_lengths) {\n        return \"is duplicate\";\n    }\n\n    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);\n\n    clcf->handler = ngx_http_fastcgi_handler;\n\n    if (clcf->name.len && clcf->name.data[clcf->name.len - 1] == '/') {\n        clcf->auto_redirect = 1;\n    }\n\n    value = cf->args->elts;\n\n    url = &value[1];\n\n    n = ngx_http_script_variables_count(url);\n\n    if (n) {\n\n        ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));\n\n        sc.cf = cf;\n        sc.source = url;\n        sc.lengths = &flcf->fastcgi_lengths;\n        sc.values = &flcf->fastcgi_values;\n        sc.variables = n;\n        sc.complete_lengths = 1;\n        sc.complete_values = 1;\n\n        if (ngx_http_script_compile(&sc) != NGX_OK) {\n            return NGX_CONF_ERROR;\n        }\n\n        return NGX_CONF_OK;\n    }\n\n    ngx_memzero(&u, sizeof(ngx_url_t));\n\n    u.url = value[1];\n    u.no_resolve = 1;\n\n    flcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0);\n    if (flcf->upstream.upstream == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_fastcgi_split_path_info(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n#if (NGX_PCRE)\n    ngx_http_fastcgi_loc_conf_t *flcf = conf;\n\n    ngx_str_t            *value;\n    ngx_regex_compile_t   rc;\n    u_char                errstr[NGX_MAX_CONF_ERRSTR];\n\n    value = cf->args->elts;\n\n    flcf->split_name = value[1];\n\n    ngx_memzero(&rc, sizeof(ngx_regex_compile_t));\n\n    rc.pattern = value[1];\n    rc.pool = cf->pool;\n    rc.err.len = NGX_MAX_CONF_ERRSTR;\n    rc.err.data = errstr;\n\n    if (ngx_regex_compile(&rc) != NGX_OK) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, \"%V\", &rc.err);\n        return NGX_CONF_ERROR;\n    }\n\n    if (rc.captures != 2) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"pattern \\\"%V\\\" must have 2 captures\", &value[1]);\n        return NGX_CONF_ERROR;\n    }\n\n    flcf->split_regex = rc.regex;\n\n    return NGX_CONF_OK;\n\n#else\n\n    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                       \"\\\"%V\\\" requires PCRE library\", &cmd->name);\n    return NGX_CONF_ERROR;\n\n#endif\n}\n\n\nstatic char *\nngx_http_fastcgi_store(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_fastcgi_loc_conf_t *flcf = conf;\n\n    ngx_str_t                  *value;\n    ngx_http_script_compile_t   sc;\n\n    if (flcf->upstream.store != NGX_CONF_UNSET) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    if (ngx_strcmp(value[1].data, \"off\") == 0) {\n        flcf->upstream.store = 0;\n        return NGX_CONF_OK;\n    }\n\n#if (NGX_HTTP_CACHE)\n    if (flcf->upstream.cache > 0) {\n        return \"is incompatible with \\\"fastcgi_cache\\\"\";\n    }\n#endif\n\n    flcf->upstream.store = 1;\n\n    if (ngx_strcmp(value[1].data, \"on\") == 0) {\n        return NGX_CONF_OK;\n    }\n\n    /* include the terminating '\\0' into script */\n    value[1].len++;\n\n    ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));\n\n    sc.cf = cf;\n    sc.source = &value[1];\n    sc.lengths = &flcf->upstream.store_lengths;\n    sc.values = &flcf->upstream.store_values;\n    sc.variables = ngx_http_script_variables_count(&value[1]);\n    sc.complete_lengths = 1;\n    sc.complete_values = 1;\n\n    if (ngx_http_script_compile(&sc) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\n#if (NGX_HTTP_CACHE)\n\nstatic char *\nngx_http_fastcgi_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_fastcgi_loc_conf_t *flcf = conf;\n\n    ngx_str_t                         *value;\n    ngx_http_complex_value_t           cv;\n    ngx_http_compile_complex_value_t   ccv;\n\n    value = cf->args->elts;\n\n    if (flcf->upstream.cache != NGX_CONF_UNSET) {\n        return \"is duplicate\";\n    }\n\n    if (ngx_strcmp(value[1].data, \"off\") == 0) {\n        flcf->upstream.cache = 0;\n        return NGX_CONF_OK;\n    }\n\n    if (flcf->upstream.store > 0) {\n        return \"is incompatible with \\\"fastcgi_store\\\"\";\n    }\n\n    flcf->upstream.cache = 1;\n\n    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n\n    ccv.cf = cf;\n    ccv.value = &value[1];\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\n        flcf->upstream.cache_value = ngx_palloc(cf->pool,\n                                             sizeof(ngx_http_complex_value_t));\n        if (flcf->upstream.cache_value == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        *flcf->upstream.cache_value = cv;\n\n        return NGX_CONF_OK;\n    }\n\n    flcf->upstream.cache_zone = ngx_shared_memory_add(cf, &value[1], 0,\n                                                      &ngx_http_fastcgi_module);\n    if (flcf->upstream.cache_zone == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_fastcgi_cache_key(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_fastcgi_loc_conf_t *flcf = conf;\n\n    ngx_str_t                         *value;\n    ngx_http_compile_complex_value_t   ccv;\n\n    value = cf->args->elts;\n\n    if (flcf->cache_key.value.data) {\n        return \"is duplicate\";\n    }\n\n    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n\n    ccv.cf = cf;\n    ccv.value = &value[1];\n    ccv.complex_value = &flcf->cache_key;\n\n    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n#endif\n\n\nstatic char *\nngx_http_fastcgi_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                           \"\\\"fastcgi_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                       \"\\\"fastcgi_send_lowat\\\" is not supported, ignored\");\n\n    *np = 0;\n\n#endif\n\n    return NGX_CONF_OK;\n}\n"
  },
  {
    "path": "src/http/modules/ngx_http_flv_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\nstatic char *ngx_http_flv(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\n\nstatic ngx_command_t  ngx_http_flv_commands[] = {\n\n    { ngx_string(\"flv\"),\n      NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,\n      ngx_http_flv,\n      0,\n      0,\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic u_char  ngx_flv_header[] = \"FLV\\x1\\x5\\0\\0\\0\\x9\\0\\0\\0\\0\";\n\n\nstatic ngx_http_module_t  ngx_http_flv_module_ctx = {\n    NULL,                          /* preconfiguration */\n    NULL,                          /* 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\nngx_module_t  ngx_http_flv_module = {\n    NGX_MODULE_V1,\n    &ngx_http_flv_module_ctx,      /* module context */\n    ngx_http_flv_commands,         /* 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_flv_handler(ngx_http_request_t *r)\n{\n    u_char                    *last;\n    off_t                      start, len;\n    size_t                     root;\n    ngx_int_t                  rc;\n    ngx_uint_t                 level, i;\n    ngx_str_t                  path, value;\n    ngx_log_t                 *log;\n    ngx_buf_t                 *b;\n    ngx_chain_t                out[2];\n    ngx_open_file_info_t       of;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {\n        return NGX_HTTP_NOT_ALLOWED;\n    }\n\n    if (r->uri.data[r->uri.len - 1] == '/') {\n        return NGX_DECLINED;\n    }\n\n    rc = ngx_http_discard_request_body(r);\n\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    last = ngx_http_map_uri_to_path(r, &path, &root, 0);\n    if (last == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    log = r->connection->log;\n\n    path.len = last - path.data;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,\n                   \"http flv filename: \\\"%V\\\"\", &path);\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    ngx_memzero(&of, sizeof(ngx_open_file_info_t));\n\n    of.read_ahead = clcf->read_ahead;\n    of.directio = clcf->directio;\n    of.valid = clcf->open_file_cache_valid;\n    of.min_uses = clcf->open_file_cache_min_uses;\n    of.errors = clcf->open_file_cache_errors;\n    of.events = clcf->open_file_cache_events;\n\n    if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)\n        != NGX_OK)\n    {\n        switch (of.err) {\n\n        case 0:\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n\n        case NGX_ENOENT:\n        case NGX_ENOTDIR:\n        case NGX_ENAMETOOLONG:\n\n            level = NGX_LOG_ERR;\n            rc = NGX_HTTP_NOT_FOUND;\n            break;\n\n        case NGX_EACCES:\n#if (NGX_HAVE_OPENAT)\n        case NGX_EMLINK:\n        case NGX_ELOOP:\n#endif\n\n            level = NGX_LOG_ERR;\n            rc = NGX_HTTP_FORBIDDEN;\n            break;\n\n        default:\n\n            level = NGX_LOG_CRIT;\n            rc = NGX_HTTP_INTERNAL_SERVER_ERROR;\n            break;\n        }\n\n        if (rc != NGX_HTTP_NOT_FOUND || clcf->log_not_found) {\n            ngx_log_error(level, log, of.err,\n                          \"%s \\\"%s\\\" failed\", of.failed, path.data);\n        }\n\n        return rc;\n    }\n\n    if (!of.is_file) {\n        return NGX_DECLINED;\n    }\n\n    r->root_tested = !r->error_page;\n\n    start = 0;\n    len = of.size;\n    i = 1;\n\n    if (r->args.len) {\n\n        if (ngx_http_arg(r, (u_char *) \"start\", 5, &value) == NGX_OK) {\n\n            start = ngx_atoof(value.data, value.len);\n\n            if (start == NGX_ERROR || start >= len) {\n                start = 0;\n            }\n\n            if (start) {\n                len = sizeof(ngx_flv_header) - 1 + len - start;\n                i = 0;\n            }\n        }\n    }\n\n    log->action = \"sending flv to client\";\n\n    r->headers_out.status = NGX_HTTP_OK;\n    r->headers_out.content_length_n = len;\n    r->headers_out.last_modified_time = of.mtime;\n\n    if (ngx_http_set_etag(r) != NGX_OK) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    if (ngx_http_set_content_type(r) != NGX_OK) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    if (i == 0) {\n        b = ngx_calloc_buf(r->pool);\n        if (b == NULL) {\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n\n        b->pos = ngx_flv_header;\n        b->last = ngx_flv_header + sizeof(ngx_flv_header) - 1;\n        b->memory = 1;\n\n        out[0].buf = b;\n        out[0].next = &out[1];\n    }\n\n\n    b = ngx_calloc_buf(r->pool);\n    if (b == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t));\n    if (b->file == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    r->allow_ranges = 1;\n\n    rc = ngx_http_send_header(r);\n\n    if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {\n        return rc;\n    }\n\n    b->file_pos = start;\n    b->file_last = of.size;\n\n    b->in_file = b->file_last ? 1 : 0;\n    b->last_buf = (r == r->main) ? 1 : 0;\n    b->last_in_chain = 1;\n    b->sync = (b->last_buf || b->in_file) ? 0 : 1;\n\n    b->file->fd = of.fd;\n    b->file->name = path;\n    b->file->log = log;\n    b->file->directio = of.is_directio;\n\n    out[1].buf = b;\n    out[1].next = NULL;\n\n    return ngx_http_output_filter(r, &out[i]);\n}\n\n\nstatic char *\nngx_http_flv(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_core_loc_conf_t  *clcf;\n\n    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);\n    clcf->handler = ngx_http_flv_handler;\n\n    return NGX_CONF_OK;\n}\n"
  },
  {
    "path": "src/http/modules/ngx_http_geo_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\ntypedef struct {\n    ngx_http_variable_value_t       *value;\n    u_short                          start;\n    u_short                          end;\n} ngx_http_geo_range_t;\n\n\ntypedef struct {\n    ngx_radix_tree_t                *tree;\n#if (NGX_HAVE_INET6)\n    ngx_radix_tree_t                *tree6;\n#endif\n} ngx_http_geo_trees_t;\n\n\ntypedef struct {\n    ngx_http_geo_range_t           **low;\n    ngx_http_variable_value_t       *default_value;\n} ngx_http_geo_high_ranges_t;\n\n\ntypedef struct {\n    ngx_str_node_t                   sn;\n    ngx_http_variable_value_t       *value;\n    size_t                           offset;\n} ngx_http_geo_variable_value_node_t;\n\n\ntypedef struct {\n    ngx_http_variable_value_t       *value;\n    ngx_str_t                       *net;\n    ngx_http_geo_high_ranges_t       high;\n    ngx_radix_tree_t                *tree;\n#if (NGX_HAVE_INET6)\n    ngx_radix_tree_t                *tree6;\n#endif\n    ngx_rbtree_t                     rbtree;\n    ngx_rbtree_node_t                sentinel;\n    ngx_array_t                     *proxies;\n    ngx_pool_t                      *pool;\n    ngx_pool_t                      *temp_pool;\n\n    size_t                           data_size;\n\n    ngx_str_t                        include_name;\n    ngx_uint_t                       includes;\n    ngx_uint_t                       entries;\n\n    unsigned                         ranges:1;\n    unsigned                         outside_entries:1;\n    unsigned                         allow_binary_include:1;\n    unsigned                         binary_include:1;\n    unsigned                         proxy_recursive:1;\n} ngx_http_geo_conf_ctx_t;\n\n\ntypedef struct {\n    union {\n        ngx_http_geo_trees_t         trees;\n        ngx_http_geo_high_ranges_t   high;\n    } u;\n\n    ngx_array_t                     *proxies;\n    unsigned                         proxy_recursive:1;\n\n    ngx_int_t                        index;\n} ngx_http_geo_ctx_t;\n\n\nstatic ngx_int_t ngx_http_geo_addr(ngx_http_request_t *r,\n    ngx_http_geo_ctx_t *ctx, ngx_addr_t *addr);\nstatic ngx_int_t ngx_http_geo_real_addr(ngx_http_request_t *r,\n    ngx_http_geo_ctx_t *ctx, ngx_addr_t *addr);\nstatic char *ngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\nstatic char *ngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf);\nstatic char *ngx_http_geo_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,\n    ngx_str_t *value);\nstatic char *ngx_http_geo_add_range(ngx_conf_t *cf,\n    ngx_http_geo_conf_ctx_t *ctx, in_addr_t start, in_addr_t end);\nstatic ngx_uint_t ngx_http_geo_delete_range(ngx_conf_t *cf,\n    ngx_http_geo_conf_ctx_t *ctx, in_addr_t start, in_addr_t end);\nstatic char *ngx_http_geo_cidr(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,\n    ngx_str_t *value);\nstatic char *ngx_http_geo_cidr_add(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,\n    ngx_cidr_t *cidr, ngx_str_t *value, ngx_str_t *net);\nstatic ngx_http_variable_value_t *ngx_http_geo_value(ngx_conf_t *cf,\n    ngx_http_geo_conf_ctx_t *ctx, ngx_str_t *value);\nstatic char *ngx_http_geo_add_proxy(ngx_conf_t *cf,\n    ngx_http_geo_conf_ctx_t *ctx, ngx_cidr_t *cidr);\nstatic ngx_int_t ngx_http_geo_cidr_value(ngx_conf_t *cf, ngx_str_t *net,\n    ngx_cidr_t *cidr);\nstatic char *ngx_http_geo_include(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,\n    ngx_str_t *name);\nstatic ngx_int_t ngx_http_geo_include_binary_base(ngx_conf_t *cf,\n    ngx_http_geo_conf_ctx_t *ctx, ngx_str_t *name);\nstatic void ngx_http_geo_create_binary_base(ngx_http_geo_conf_ctx_t *ctx);\nstatic u_char *ngx_http_geo_copy_values(u_char *base, u_char *p,\n    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);\n\n\nstatic ngx_command_t  ngx_http_geo_commands[] = {\n\n    { ngx_string(\"geo\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE12,\n      ngx_http_geo_block,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      0,\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_geo_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    NULL,                                  /* 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\nngx_module_t  ngx_http_geo_module = {\n    NGX_MODULE_V1,\n    &ngx_http_geo_module_ctx,              /* module context */\n    ngx_http_geo_commands,                 /* 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    u_char    GEORNG[6];\n    u_char    version;\n    u_char    ptr_size;\n    uint32_t  endianness;\n    uint32_t  crc32;\n} ngx_http_geo_header_t;\n\n\nstatic ngx_http_geo_header_t  ngx_http_geo_header = {\n    { 'G', 'E', 'O', 'R', 'N', 'G' }, 0, sizeof(void *), 0x12345678, 0\n};\n\n\n/* geo range is AF_INET only */\n\nstatic ngx_int_t\nngx_http_geo_cidr_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,\n    uintptr_t data)\n{\n    ngx_http_geo_ctx_t *ctx = (ngx_http_geo_ctx_t *) data;\n\n    in_addr_t                   inaddr;\n    ngx_addr_t                  addr;\n    struct sockaddr_in         *sin;\n    ngx_http_variable_value_t  *vv;\n#if (NGX_HAVE_INET6)\n    u_char                     *p;\n    struct in6_addr            *inaddr6;\n#endif\n\n    if (ngx_http_geo_addr(r, ctx, &addr) != NGX_OK) {\n        vv = (ngx_http_variable_value_t *)\n                  ngx_radix32tree_find(ctx->u.trees.tree, INADDR_NONE);\n        goto done;\n    }\n\n    switch (addr.sockaddr->sa_family) {\n\n#if (NGX_HAVE_INET6)\n    case AF_INET6:\n        inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr;\n        p = inaddr6->s6_addr;\n\n        if (IN6_IS_ADDR_V4MAPPED(inaddr6)) {\n            inaddr = p[12] << 24;\n            inaddr += p[13] << 16;\n            inaddr += p[14] << 8;\n            inaddr += p[15];\n\n            vv = (ngx_http_variable_value_t *)\n                      ngx_radix32tree_find(ctx->u.trees.tree, inaddr);\n\n        } else {\n            vv = (ngx_http_variable_value_t *)\n                      ngx_radix128tree_find(ctx->u.trees.tree6, p);\n        }\n\n        break;\n#endif\n\n#if (NGX_HAVE_UNIX_DOMAIN)\n    case AF_UNIX:\n        vv = (ngx_http_variable_value_t *)\n                  ngx_radix32tree_find(ctx->u.trees.tree, INADDR_NONE);\n        break;\n#endif\n\n    default: /* AF_INET */\n        sin = (struct sockaddr_in *) addr.sockaddr;\n        inaddr = ntohl(sin->sin_addr.s_addr);\n\n        vv = (ngx_http_variable_value_t *)\n                  ngx_radix32tree_find(ctx->u.trees.tree, inaddr);\n\n        break;\n    }\n\ndone:\n\n    *v = *vv;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http geo: %v\", v);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_geo_range_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,\n    uintptr_t data)\n{\n    ngx_http_geo_ctx_t *ctx = (ngx_http_geo_ctx_t *) data;\n\n    in_addr_t              inaddr;\n    ngx_addr_t             addr;\n    ngx_uint_t             n;\n    struct sockaddr_in    *sin;\n    ngx_http_geo_range_t  *range;\n#if (NGX_HAVE_INET6)\n    u_char                *p;\n    struct in6_addr       *inaddr6;\n#endif\n\n    *v = *ctx->u.high.default_value;\n\n    if (ngx_http_geo_addr(r, ctx, &addr) == NGX_OK) {\n\n        switch (addr.sockaddr->sa_family) {\n\n#if (NGX_HAVE_INET6)\n        case AF_INET6:\n            inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr;\n\n            if (IN6_IS_ADDR_V4MAPPED(inaddr6)) {\n                p = inaddr6->s6_addr;\n\n                inaddr = p[12] << 24;\n                inaddr += p[13] << 16;\n                inaddr += p[14] << 8;\n                inaddr += p[15];\n\n            } else {\n                inaddr = INADDR_NONE;\n            }\n\n            break;\n#endif\n\n#if (NGX_HAVE_UNIX_DOMAIN)\n        case AF_UNIX:\n            inaddr = INADDR_NONE;\n            break;\n#endif\n\n        default: /* AF_INET */\n            sin = (struct sockaddr_in *) addr.sockaddr;\n            inaddr = ntohl(sin->sin_addr.s_addr);\n            break;\n        }\n\n    } else {\n        inaddr = INADDR_NONE;\n    }\n\n    if (ctx->u.high.low) {\n        range = ctx->u.high.low[inaddr >> 16];\n\n        if (range) {\n            n = inaddr & 0xffff;\n            do {\n                if (n >= (ngx_uint_t) range->start\n                    && n <= (ngx_uint_t) range->end)\n                {\n                    *v = *range->value;\n                    break;\n                }\n            } while ((++range)->value);\n        }\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http geo: %v\", v);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_geo_addr(ngx_http_request_t *r, ngx_http_geo_ctx_t *ctx,\n    ngx_addr_t *addr)\n{\n    ngx_table_elt_t  *xfwd;\n\n    if (ngx_http_geo_real_addr(r, ctx, addr) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    xfwd = r->headers_in.x_forwarded_for;\n\n    if (xfwd != NULL && ctx->proxies != NULL) {\n        (void) ngx_http_get_forwarded_addr(r, addr, xfwd, NULL,\n                                           ctx->proxies, ctx->proxy_recursive);\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_geo_real_addr(ngx_http_request_t *r, ngx_http_geo_ctx_t *ctx,\n    ngx_addr_t *addr)\n{\n    ngx_http_variable_value_t  *v;\n\n    if (ctx->index == -1) {\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"http geo started: %V\", &r->connection->addr_text);\n\n        addr->sockaddr = r->connection->sockaddr;\n        addr->socklen = r->connection->socklen;\n        /* addr->name = r->connection->addr_text; */\n\n        return NGX_OK;\n    }\n\n    v = ngx_http_get_flushed_variable(r, ctx->index);\n\n    if (v == NULL || v->not_found) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"http geo not found\");\n\n        return NGX_ERROR;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http geo started: %v\", v);\n\n    if (ngx_parse_addr(r->pool, addr, v->data, v->len) == NGX_OK) {\n        return NGX_OK;\n    }\n\n    return NGX_ERROR;\n}\n\n\nstatic char *\nngx_http_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    char                     *rv;\n    size_t                    len;\n    ngx_str_t                *value, name;\n    ngx_uint_t                i;\n    ngx_conf_t                save;\n    ngx_pool_t               *pool;\n    ngx_array_t              *a;\n    ngx_http_variable_t      *var;\n    ngx_http_geo_ctx_t       *geo;\n    ngx_http_geo_conf_ctx_t   ctx;\n#if (NGX_HAVE_INET6)\n    static struct in6_addr    zero;\n#endif\n\n    value = cf->args->elts;\n\n    geo = ngx_palloc(cf->pool, sizeof(ngx_http_geo_ctx_t));\n    if (geo == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    name = value[1];\n\n    if (name.data[0] != '$') {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid variable name \\\"%V\\\"\", &name);\n        return NGX_CONF_ERROR;\n    }\n\n    name.len--;\n    name.data++;\n\n    if (cf->args->nelts == 3) {\n\n        geo->index = ngx_http_get_variable_index(cf, &name);\n        if (geo->index == NGX_ERROR) {\n            return NGX_CONF_ERROR;\n        }\n\n        name = value[2];\n\n        if (name.data[0] != '$') {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"invalid variable name \\\"%V\\\"\", &name);\n            return NGX_CONF_ERROR;\n        }\n\n        name.len--;\n        name.data++;\n\n    } else {\n        geo->index = -1;\n    }\n\n    var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE);\n    if (var == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, cf->log);\n    if (pool == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_memzero(&ctx, sizeof(ngx_http_geo_conf_ctx_t));\n\n    ctx.temp_pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, cf->log);\n    if (ctx.temp_pool == NULL) {\n        ngx_destroy_pool(pool);\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_rbtree_init(&ctx.rbtree, &ctx.sentinel, ngx_str_rbtree_insert_value);\n\n    ctx.pool = cf->pool;\n    ctx.data_size = sizeof(ngx_http_geo_header_t)\n                  + sizeof(ngx_http_variable_value_t)\n                  + 0x10000 * sizeof(ngx_http_geo_range_t *);\n    ctx.allow_binary_include = 1;\n\n    save = *cf;\n    cf->pool = pool;\n    cf->ctx = &ctx;\n    cf->handler = ngx_http_geo;\n    cf->handler_conf = conf;\n\n    rv = ngx_conf_parse(cf, NULL);\n\n    *cf = save;\n\n    if (rv != NGX_CONF_OK) {\n        goto failed;\n    }\n\n    geo->proxies = ctx.proxies;\n    geo->proxy_recursive = ctx.proxy_recursive;\n\n    if (ctx.ranges) {\n\n        if (ctx.high.low && !ctx.binary_include) {\n            for (i = 0; i < 0x10000; i++) {\n                a = (ngx_array_t *) ctx.high.low[i];\n\n                if (a == NULL) {\n                    continue;\n                }\n\n                if (a->nelts == 0) {\n                    ctx.high.low[i] = NULL;\n                    continue;\n                }\n\n                len = a->nelts * sizeof(ngx_http_geo_range_t);\n\n                ctx.high.low[i] = ngx_palloc(cf->pool, len + sizeof(void *));\n                if (ctx.high.low[i] == NULL) {\n                    goto failed;\n                }\n\n                ngx_memcpy(ctx.high.low[i], a->elts, len);\n                ctx.high.low[i][a->nelts].value = NULL;\n                ctx.data_size += len + sizeof(void *);\n            }\n\n            if (ctx.allow_binary_include\n                && !ctx.outside_entries\n                && ctx.entries > 100000\n                && ctx.includes == 1)\n            {\n                ngx_http_geo_create_binary_base(&ctx);\n            }\n        }\n\n        if (ctx.high.default_value == NULL) {\n            ctx.high.default_value = &ngx_http_variable_null_value;\n        }\n\n        geo->u.high = ctx.high;\n\n        var->get_handler = ngx_http_geo_range_variable;\n        var->data = (uintptr_t) geo;\n\n    } else {\n        if (ctx.tree == NULL) {\n            ctx.tree = ngx_radix_tree_create(cf->pool, -1);\n            if (ctx.tree == NULL) {\n                goto failed;\n            }\n        }\n\n        geo->u.trees.tree = ctx.tree;\n\n#if (NGX_HAVE_INET6)\n        if (ctx.tree6 == NULL) {\n            ctx.tree6 = ngx_radix_tree_create(cf->pool, -1);\n            if (ctx.tree6 == NULL) {\n                goto failed;\n            }\n        }\n\n        geo->u.trees.tree6 = ctx.tree6;\n#endif\n\n        var->get_handler = ngx_http_geo_cidr_variable;\n        var->data = (uintptr_t) geo;\n\n        if (ngx_radix32tree_insert(ctx.tree, 0, 0,\n                                   (uintptr_t) &ngx_http_variable_null_value)\n            == NGX_ERROR)\n        {\n            goto failed;\n        }\n\n        /* NGX_BUSY is okay (default was set explicitly) */\n\n#if (NGX_HAVE_INET6)\n        if (ngx_radix128tree_insert(ctx.tree6, zero.s6_addr, zero.s6_addr,\n                                    (uintptr_t) &ngx_http_variable_null_value)\n            == NGX_ERROR)\n        {\n            goto failed;\n        }\n#endif\n    }\n\n    ngx_destroy_pool(ctx.temp_pool);\n    ngx_destroy_pool(pool);\n\n    return NGX_CONF_OK;\n\nfailed:\n\n    ngx_destroy_pool(ctx.temp_pool);\n    ngx_destroy_pool(pool);\n\n    return NGX_CONF_ERROR;\n}\n\n\nstatic char *\nngx_http_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)\n{\n    char                     *rv;\n    ngx_str_t                *value;\n    ngx_cidr_t                cidr;\n    ngx_http_geo_conf_ctx_t  *ctx;\n\n    ctx = cf->ctx;\n\n    value = cf->args->elts;\n\n    if (cf->args->nelts == 1) {\n\n        if (ngx_strcmp(value[0].data, \"ranges\") == 0) {\n\n            if (ctx->tree\n#if (NGX_HAVE_INET6)\n                || ctx->tree6\n#endif\n               )\n            {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"the \\\"ranges\\\" directive must be \"\n                                   \"the first directive inside \\\"geo\\\" block\");\n                goto failed;\n            }\n\n            ctx->ranges = 1;\n\n            rv = NGX_CONF_OK;\n\n            goto done;\n        }\n\n        else if (ngx_strcmp(value[0].data, \"proxy_recursive\") == 0) {\n            ctx->proxy_recursive = 1;\n            rv = NGX_CONF_OK;\n            goto done;\n        }\n    }\n\n    if (cf->args->nelts != 2) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid number of the geo parameters\");\n        goto failed;\n    }\n\n    if (ngx_strcmp(value[0].data, \"include\") == 0) {\n\n        rv = ngx_http_geo_include(cf, ctx, &value[1]);\n\n        goto done;\n\n    } else if (ngx_strcmp(value[0].data, \"proxy\") == 0) {\n\n        if (ngx_http_geo_cidr_value(cf, &value[1], &cidr) != NGX_OK) {\n            goto failed;\n        }\n\n        rv = ngx_http_geo_add_proxy(cf, ctx, &cidr);\n\n        goto done;\n    }\n\n    if (ctx->ranges) {\n        rv = ngx_http_geo_range(cf, ctx, value);\n\n    } else {\n        rv = ngx_http_geo_cidr(cf, ctx, value);\n    }\n\ndone:\n\n    ngx_reset_pool(cf->pool);\n\n    return rv;\n\nfailed:\n\n    ngx_reset_pool(cf->pool);\n\n    return NGX_CONF_ERROR;\n}\n\n\nstatic char *\nngx_http_geo_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,\n    ngx_str_t *value)\n{\n    u_char      *p, *last;\n    in_addr_t    start, end;\n    ngx_str_t   *net;\n    ngx_uint_t   del;\n\n    if (ngx_strcmp(value[0].data, \"default\") == 0) {\n\n        if (ctx->high.default_value) {\n            ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                \"duplicate default geo range value: \\\"%V\\\", old value: \\\"%v\\\"\",\n                &value[1], ctx->high.default_value);\n        }\n\n        ctx->high.default_value = ngx_http_geo_value(cf, ctx, &value[1]);\n        if (ctx->high.default_value == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        return NGX_CONF_OK;\n    }\n\n    if (ctx->binary_include) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n            \"binary geo range base \\\"%s\\\" cannot be mixed with usual entries\",\n            ctx->include_name.data);\n        return NGX_CONF_ERROR;\n    }\n\n    if (ctx->high.low == NULL) {\n        ctx->high.low = ngx_pcalloc(ctx->pool,\n                                    0x10000 * sizeof(ngx_http_geo_range_t *));\n        if (ctx->high.low == NULL) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    ctx->entries++;\n    ctx->outside_entries = 1;\n\n    if (ngx_strcmp(value[0].data, \"delete\") == 0) {\n        net = &value[1];\n        del = 1;\n\n    } else {\n        net = &value[0];\n        del = 0;\n    }\n\n    last = net->data + net->len;\n\n    p = ngx_strlchr(net->data, last, '-');\n\n    if (p == NULL) {\n        goto invalid;\n    }\n\n    start = ngx_inet_addr(net->data, p - net->data);\n\n    if (start == INADDR_NONE) {\n        goto invalid;\n    }\n\n    start = ntohl(start);\n\n    p++;\n\n    end = ngx_inet_addr(p, last - p);\n\n    if (end == INADDR_NONE) {\n        goto invalid;\n    }\n\n    end = ntohl(end);\n\n    if (start > end) {\n        goto invalid;\n    }\n\n    if (del) {\n        if (ngx_http_geo_delete_range(cf, ctx, start, end)) {\n            ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                               \"no address range \\\"%V\\\" to delete\", net);\n        }\n\n        return NGX_CONF_OK;\n    }\n\n    ctx->value = ngx_http_geo_value(cf, ctx, &value[1]);\n\n    if (ctx->value == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    ctx->net = net;\n\n    return ngx_http_geo_add_range(cf, ctx, start, end);\n\ninvalid:\n\n    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, \"invalid range \\\"%V\\\"\", net);\n\n    return NGX_CONF_ERROR;\n}\n\n\n/* the add procedure is optimized to add a growing up sequence */\n\nstatic char *\nngx_http_geo_add_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,\n    in_addr_t start, in_addr_t end)\n{\n    in_addr_t              n;\n    ngx_uint_t             h, i, s, e;\n    ngx_array_t           *a;\n    ngx_http_geo_range_t  *range;\n\n    for (n = start; n <= end; n = (n + 0x10000) & 0xffff0000) {\n\n        h = n >> 16;\n\n        if (n == start) {\n            s = n & 0xffff;\n        } else {\n            s = 0;\n        }\n\n        if ((n | 0xffff) > end) {\n            e = end & 0xffff;\n\n        } else {\n            e = 0xffff;\n        }\n\n        a = (ngx_array_t *) ctx->high.low[h];\n\n        if (a == NULL) {\n            a = ngx_array_create(ctx->temp_pool, 64,\n                                 sizeof(ngx_http_geo_range_t));\n            if (a == NULL) {\n                return NGX_CONF_ERROR;\n            }\n\n            ctx->high.low[h] = (ngx_http_geo_range_t *) a;\n        }\n\n        i = a->nelts;\n        range = a->elts;\n\n        while (i) {\n\n            i--;\n\n            if (e < (ngx_uint_t) range[i].start) {\n                continue;\n            }\n\n            if (s > (ngx_uint_t) range[i].end) {\n\n                /* add after the range */\n\n                range = ngx_array_push(a);\n                if (range == NULL) {\n                    return NGX_CONF_ERROR;\n                }\n\n                range = a->elts;\n\n                ngx_memmove(&range[i + 2], &range[i + 1],\n                            (a->nelts - 2 - i) * sizeof(ngx_http_geo_range_t));\n\n                range[i + 1].start = (u_short) s;\n                range[i + 1].end = (u_short) e;\n                range[i + 1].value = ctx->value;\n\n                goto next;\n            }\n\n            if (s == (ngx_uint_t) range[i].start\n                && e == (ngx_uint_t) range[i].end)\n            {\n                ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                    \"duplicate range \\\"%V\\\", value: \\\"%v\\\", old value: \\\"%v\\\"\",\n                    ctx->net, ctx->value, range[i].value);\n\n                range[i].value = ctx->value;\n\n                goto next;\n            }\n\n            if (s > (ngx_uint_t) range[i].start\n                && e < (ngx_uint_t) range[i].end)\n            {\n                /* split the range and insert the new one */\n\n                range = ngx_array_push(a);\n                if (range == NULL) {\n                    return NGX_CONF_ERROR;\n                }\n\n                range = ngx_array_push(a);\n                if (range == NULL) {\n                    return NGX_CONF_ERROR;\n                }\n\n                range = a->elts;\n\n                ngx_memmove(&range[i + 3], &range[i + 1],\n                            (a->nelts - 3 - i) * sizeof(ngx_http_geo_range_t));\n\n                range[i + 2].start = (u_short) (e + 1);\n                range[i + 2].end = range[i].end;\n                range[i + 2].value = range[i].value;\n\n                range[i + 1].start = (u_short) s;\n                range[i + 1].end = (u_short) e;\n                range[i + 1].value = ctx->value;\n\n                range[i].end = (u_short) (s - 1);\n\n                goto next;\n            }\n\n            if (s == (ngx_uint_t) range[i].start\n                && e < (ngx_uint_t) range[i].end)\n            {\n                /* shift the range start and insert the new range */\n\n                range = ngx_array_push(a);\n                if (range == NULL) {\n                    return NGX_CONF_ERROR;\n                }\n\n                range = a->elts;\n\n                ngx_memmove(&range[i + 1], &range[i],\n                            (a->nelts - 1 - i) * sizeof(ngx_http_geo_range_t));\n\n                range[i + 1].start = (u_short) (e + 1);\n\n                range[i].start = (u_short) s;\n                range[i].end = (u_short) e;\n                range[i].value = ctx->value;\n\n                goto next;\n            }\n\n            if (s > (ngx_uint_t) range[i].start\n                && e == (ngx_uint_t) range[i].end)\n            {\n                /* shift the range end and insert the new range */\n\n                range = ngx_array_push(a);\n                if (range == NULL) {\n                    return NGX_CONF_ERROR;\n                }\n\n                range = a->elts;\n\n                ngx_memmove(&range[i + 2], &range[i + 1],\n                            (a->nelts - 2 - i) * sizeof(ngx_http_geo_range_t));\n\n                range[i + 1].start = (u_short) s;\n                range[i + 1].end = (u_short) e;\n                range[i + 1].value = ctx->value;\n\n                range[i].end = (u_short) (s - 1);\n\n                goto next;\n            }\n\n            s = (ngx_uint_t) range[i].start;\n            e = (ngx_uint_t) range[i].end;\n\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                         \"range \\\"%V\\\" overlaps \\\"%d.%d.%d.%d-%d.%d.%d.%d\\\"\",\n                         ctx->net,\n                         h >> 8, h & 0xff, s >> 8, s & 0xff,\n                         h >> 8, h & 0xff, e >> 8, e & 0xff);\n\n            return NGX_CONF_ERROR;\n        }\n\n        /* add the first range */\n\n        range = ngx_array_push(a);\n        if (range == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        range = a->elts;\n\n        ngx_memmove(&range[1], &range[0],\n                    (a->nelts - 1) * sizeof(ngx_http_geo_range_t));\n\n        range[0].start = (u_short) s;\n        range[0].end = (u_short) e;\n        range[0].value = ctx->value;\n\n    next:\n\n        if (h == 0xffff) {\n            break;\n        }\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_uint_t\nngx_http_geo_delete_range(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,\n    in_addr_t start, in_addr_t end)\n{\n    in_addr_t              n;\n    ngx_uint_t             h, i, s, e, warn;\n    ngx_array_t           *a;\n    ngx_http_geo_range_t  *range;\n\n    warn = 0;\n\n    for (n = start; n <= end; n = (n + 0x10000) & 0xffff0000) {\n\n        h = n >> 16;\n\n        if (n == start) {\n            s = n & 0xffff;\n        } else {\n            s = 0;\n        }\n\n        if ((n | 0xffff) > end) {\n            e = end & 0xffff;\n\n        } else {\n            e = 0xffff;\n        }\n\n        a = (ngx_array_t *) ctx->high.low[h];\n\n        if (a == NULL || a->nelts == 0) {\n            warn = 1;\n            goto next;\n        }\n\n        range = a->elts;\n        for (i = 0; i < a->nelts; i++) {\n\n            if (s == (ngx_uint_t) range[i].start\n                && e == (ngx_uint_t) range[i].end)\n            {\n                ngx_memmove(&range[i], &range[i + 1],\n                            (a->nelts - 1 - i) * sizeof(ngx_http_geo_range_t));\n\n                a->nelts--;\n\n                break;\n            }\n\n            if (i == a->nelts - 1) {\n                warn = 1;\n            }\n        }\n\n    next:\n\n        if (h == 0xffff) {\n            break;\n        }\n    }\n\n    return warn;\n}\n\n\nstatic char *\nngx_http_geo_cidr(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,\n    ngx_str_t *value)\n{\n    char        *rv;\n    ngx_int_t    rc, del;\n    ngx_str_t   *net;\n    ngx_cidr_t   cidr;\n\n    if (ctx->tree == NULL) {\n        ctx->tree = ngx_radix_tree_create(ctx->pool, -1);\n        if (ctx->tree == NULL) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n#if (NGX_HAVE_INET6)\n    if (ctx->tree6 == NULL) {\n        ctx->tree6 = ngx_radix_tree_create(ctx->pool, -1);\n        if (ctx->tree6 == NULL) {\n            return NGX_CONF_ERROR;\n        }\n    }\n#endif\n\n    if (ngx_strcmp(value[0].data, \"default\") == 0) {\n        cidr.family = AF_INET;\n        cidr.u.in.addr = 0;\n        cidr.u.in.mask = 0;\n\n        rv = ngx_http_geo_cidr_add(cf, ctx, &cidr, &value[1], &value[0]);\n\n        if (rv != NGX_CONF_OK) {\n            return rv;\n        }\n\n#if (NGX_HAVE_INET6)\n        cidr.family = AF_INET6;\n        ngx_memzero(&cidr.u.in6, sizeof(ngx_in6_cidr_t));\n\n        rv = ngx_http_geo_cidr_add(cf, ctx, &cidr, &value[1], &value[0]);\n\n        if (rv != NGX_CONF_OK) {\n            return rv;\n        }\n#endif\n\n        return NGX_CONF_OK;\n    }\n\n    if (ngx_strcmp(value[0].data, \"delete\") == 0) {\n        net = &value[1];\n        del = 1;\n\n    } else {\n        net = &value[0];\n        del = 0;\n    }\n\n    if (ngx_http_geo_cidr_value(cf, net, &cidr) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (cidr.family == AF_INET) {\n        cidr.u.in.addr = ntohl(cidr.u.in.addr);\n        cidr.u.in.mask = ntohl(cidr.u.in.mask);\n    }\n\n    if (del) {\n        switch (cidr.family) {\n\n#if (NGX_HAVE_INET6)\n        case AF_INET6:\n            rc = ngx_radix128tree_delete(ctx->tree6,\n                                         cidr.u.in6.addr.s6_addr,\n                                         cidr.u.in6.mask.s6_addr);\n            break;\n#endif\n\n        default: /* AF_INET */\n            rc = ngx_radix32tree_delete(ctx->tree, cidr.u.in.addr,\n                                        cidr.u.in.mask);\n            break;\n        }\n\n        if (rc != NGX_OK) {\n            ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                               \"no network \\\"%V\\\" to delete\", net);\n        }\n\n        return NGX_CONF_OK;\n    }\n\n    return ngx_http_geo_cidr_add(cf, ctx, &cidr, &value[1], net);\n}\n\n\nstatic char *\nngx_http_geo_cidr_add(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,\n    ngx_cidr_t *cidr, ngx_str_t *value, ngx_str_t *net)\n{\n    ngx_int_t                   rc;\n    ngx_http_variable_value_t  *val, *old;\n\n    val = ngx_http_geo_value(cf, ctx, value);\n\n    if (val == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    switch (cidr->family) {\n\n#if (NGX_HAVE_INET6)\n    case AF_INET6:\n        rc = ngx_radix128tree_insert(ctx->tree6, cidr->u.in6.addr.s6_addr,\n                                     cidr->u.in6.mask.s6_addr,\n                                     (uintptr_t) val);\n\n        if (rc == NGX_OK) {\n            return NGX_CONF_OK;\n        }\n\n        if (rc == NGX_ERROR) {\n            return NGX_CONF_ERROR;\n        }\n\n        /* rc == NGX_BUSY */\n\n        old = (ngx_http_variable_value_t *)\n                   ngx_radix128tree_find(ctx->tree6,\n                                         cidr->u.in6.addr.s6_addr);\n\n        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n              \"duplicate network \\\"%V\\\", value: \\\"%v\\\", old value: \\\"%v\\\"\",\n              net, val, old);\n\n        rc = ngx_radix128tree_delete(ctx->tree6,\n                                     cidr->u.in6.addr.s6_addr,\n                                     cidr->u.in6.mask.s6_addr);\n\n        if (rc == NGX_ERROR) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, \"invalid radix tree\");\n            return NGX_CONF_ERROR;\n        }\n\n        rc = ngx_radix128tree_insert(ctx->tree6, cidr->u.in6.addr.s6_addr,\n                                     cidr->u.in6.mask.s6_addr,\n                                     (uintptr_t) val);\n\n        break;\n#endif\n\n    default: /* AF_INET */\n        rc = ngx_radix32tree_insert(ctx->tree, cidr->u.in.addr,\n                                    cidr->u.in.mask, (uintptr_t) val);\n\n        if (rc == NGX_OK) {\n            return NGX_CONF_OK;\n        }\n\n        if (rc == NGX_ERROR) {\n            return NGX_CONF_ERROR;\n        }\n\n        /* rc == NGX_BUSY */\n\n        old = (ngx_http_variable_value_t *)\n                   ngx_radix32tree_find(ctx->tree, cidr->u.in.addr);\n\n        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n              \"duplicate network \\\"%V\\\", value: \\\"%v\\\", old value: \\\"%v\\\"\",\n              net, val, old);\n\n        rc = ngx_radix32tree_delete(ctx->tree,\n                                    cidr->u.in.addr, cidr->u.in.mask);\n\n        if (rc == NGX_ERROR) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, \"invalid radix tree\");\n            return NGX_CONF_ERROR;\n        }\n\n        rc = ngx_radix32tree_insert(ctx->tree, cidr->u.in.addr,\n                                    cidr->u.in.mask, (uintptr_t) val);\n\n        break;\n    }\n\n    if (rc == NGX_OK) {\n        return NGX_CONF_OK;\n    }\n\n    return NGX_CONF_ERROR;\n}\n\n\nstatic ngx_http_variable_value_t *\nngx_http_geo_value(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,\n    ngx_str_t *value)\n{\n    uint32_t                             hash;\n    ngx_http_variable_value_t           *val;\n    ngx_http_geo_variable_value_node_t  *gvvn;\n\n    hash = ngx_crc32_long(value->data, value->len);\n\n    gvvn = (ngx_http_geo_variable_value_node_t *)\n               ngx_str_rbtree_lookup(&ctx->rbtree, value, hash);\n\n    if (gvvn) {\n        return gvvn->value;\n    }\n\n    val = ngx_palloc(ctx->pool, sizeof(ngx_http_variable_value_t));\n    if (val == NULL) {\n        return NULL;\n    }\n\n    val->len = value->len;\n    val->data = ngx_pstrdup(ctx->pool, value);\n    if (val->data == NULL) {\n        return NULL;\n    }\n\n    val->valid = 1;\n    val->no_cacheable = 0;\n    val->not_found = 0;\n\n    gvvn = ngx_palloc(ctx->temp_pool,\n                      sizeof(ngx_http_geo_variable_value_node_t));\n    if (gvvn == NULL) {\n        return NULL;\n    }\n\n    gvvn->sn.node.key = hash;\n    gvvn->sn.str.len = val->len;\n    gvvn->sn.str.data = val->data;\n    gvvn->value = val;\n    gvvn->offset = 0;\n\n    ngx_rbtree_insert(&ctx->rbtree, &gvvn->sn.node);\n\n    ctx->data_size += ngx_align(sizeof(ngx_http_variable_value_t) + value->len,\n                                sizeof(void *));\n\n    return val;\n}\n\n\nstatic char *\nngx_http_geo_add_proxy(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,\n    ngx_cidr_t *cidr)\n{\n    ngx_cidr_t  *c;\n\n    if (ctx->proxies == NULL) {\n        ctx->proxies = ngx_array_create(ctx->pool, 4, sizeof(ngx_cidr_t));\n        if (ctx->proxies == NULL) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    c = ngx_array_push(ctx->proxies);\n    if (c == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    *c = *cidr;\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_geo_cidr_value(ngx_conf_t *cf, ngx_str_t *net, ngx_cidr_t *cidr)\n{\n    ngx_int_t  rc;\n\n    if (ngx_strcmp(net->data, \"255.255.255.255\") == 0) {\n        cidr->family = AF_INET;\n        cidr->u.in.addr = 0xffffffff;\n        cidr->u.in.mask = 0xffffffff;\n\n        return NGX_OK;\n    }\n\n    rc = ngx_ptocidr(net, cidr);\n\n    if (rc == NGX_ERROR) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, \"invalid network \\\"%V\\\"\", net);\n        return NGX_ERROR;\n    }\n\n    if (rc == NGX_DONE) {\n        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                           \"low address bits of %V are meaningless\", net);\n    }\n\n    return NGX_OK;\n}\n\n\nstatic char *\nngx_http_geo_include(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,\n    ngx_str_t *name)\n{\n    char       *rv;\n    ngx_str_t   file;\n\n    file.len = name->len + 4;\n    file.data = ngx_pnalloc(ctx->temp_pool, name->len + 5);\n    if (file.data == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_sprintf(file.data, \"%V.bin%Z\", name);\n\n    if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (ctx->ranges) {\n        ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, \"include %s\", file.data);\n\n        switch (ngx_http_geo_include_binary_base(cf, ctx, &file)) {\n        case NGX_OK:\n            return NGX_CONF_OK;\n        case NGX_ERROR:\n            return NGX_CONF_ERROR;\n        default:\n            break;\n        }\n    }\n\n    file.len -= 4;\n    file.data[file.len] = '\\0';\n\n    ctx->include_name = file;\n\n    if (ctx->outside_entries) {\n        ctx->allow_binary_include = 0;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, \"include %s\", file.data);\n\n    rv = ngx_conf_parse(cf, &file);\n\n    ctx->includes++;\n    ctx->outside_entries = 0;\n\n    return rv;\n}\n\n\nstatic ngx_int_t\nngx_http_geo_include_binary_base(ngx_conf_t *cf, ngx_http_geo_conf_ctx_t *ctx,\n    ngx_str_t *name)\n{\n    u_char                     *base, ch;\n    time_t                      mtime;\n    size_t                      size, len;\n    ssize_t                     n;\n    uint32_t                    crc32;\n    ngx_err_t                   err;\n    ngx_int_t                   rc;\n    ngx_uint_t                  i;\n    ngx_file_t                  file;\n    ngx_file_info_t             fi;\n    ngx_http_geo_range_t       *range, **ranges;\n    ngx_http_geo_header_t      *header;\n    ngx_http_variable_value_t  *vv;\n\n    ngx_memzero(&file, sizeof(ngx_file_t));\n    file.name = *name;\n    file.log = cf->log;\n\n    file.fd = ngx_open_file(name->data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);\n\n    if (file.fd == NGX_INVALID_FILE) {\n        err = ngx_errno;\n        if (err != NGX_ENOENT) {\n            ngx_conf_log_error(NGX_LOG_CRIT, cf, err,\n                               ngx_open_file_n \" \\\"%s\\\" failed\", name->data);\n        }\n        return NGX_DECLINED;\n    }\n\n    if (ctx->outside_entries) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n            \"binary geo range base \\\"%s\\\" cannot be mixed with usual entries\",\n            name->data);\n        rc = NGX_ERROR;\n        goto done;\n    }\n\n    if (ctx->binary_include) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n            \"second binary geo range base \\\"%s\\\" cannot be mixed with \\\"%s\\\"\",\n            name->data, ctx->include_name.data);\n        rc = NGX_ERROR;\n        goto done;\n    }\n\n    if (ngx_fd_info(file.fd, &fi) == NGX_FILE_ERROR) {\n        ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,\n                           ngx_fd_info_n \" \\\"%s\\\" failed\", name->data);\n        goto failed;\n    }\n\n    size = (size_t) ngx_file_size(&fi);\n    mtime = ngx_file_mtime(&fi);\n\n    ch = name->data[name->len - 4];\n    name->data[name->len - 4] = '\\0';\n\n    if (ngx_file_info(name->data, &fi) == NGX_FILE_ERROR) {\n        ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,\n                           ngx_file_info_n \" \\\"%s\\\" failed\", name->data);\n        goto failed;\n    }\n\n    name->data[name->len - 4] = ch;\n\n    if (mtime < ngx_file_mtime(&fi)) {\n        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                           \"stale binary geo range base \\\"%s\\\"\", name->data);\n        goto failed;\n    }\n\n    base = ngx_palloc(ctx->pool, size);\n    if (base == NULL) {\n        goto failed;\n    }\n\n    n = ngx_read_file(&file, base, size, 0);\n\n    if (n == NGX_ERROR) {\n        ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,\n                           ngx_read_file_n \" \\\"%s\\\" failed\", name->data);\n        goto failed;\n    }\n\n    if ((size_t) n != size) {\n        ngx_conf_log_error(NGX_LOG_CRIT, cf, 0,\n            ngx_read_file_n \" \\\"%s\\\" returned only %z bytes instead of %z\",\n            name->data, n, size);\n        goto failed;\n    }\n\n    header = (ngx_http_geo_header_t *) base;\n\n    if (size < 16 || ngx_memcmp(&ngx_http_geo_header, header, 12) != 0) {\n        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n             \"incompatible binary geo range base \\\"%s\\\"\", name->data);\n        goto failed;\n    }\n\n    ngx_crc32_init(crc32);\n\n    vv = (ngx_http_variable_value_t *) (base + sizeof(ngx_http_geo_header_t));\n\n    while (vv->data) {\n        len = ngx_align(sizeof(ngx_http_variable_value_t) + vv->len,\n                        sizeof(void *));\n        ngx_crc32_update(&crc32, (u_char *) vv, len);\n        vv->data += (size_t) base;\n        vv = (ngx_http_variable_value_t *) ((u_char *) vv + len);\n    }\n    ngx_crc32_update(&crc32, (u_char *) vv, sizeof(ngx_http_variable_value_t));\n    vv++;\n\n    ranges = (ngx_http_geo_range_t **) vv;\n\n    for (i = 0; i < 0x10000; i++) {\n        ngx_crc32_update(&crc32, (u_char *) &ranges[i], sizeof(void *));\n        if (ranges[i]) {\n            ranges[i] = (ngx_http_geo_range_t *)\n                            ((u_char *) ranges[i] + (size_t) base);\n        }\n    }\n\n    range = (ngx_http_geo_range_t *) &ranges[0x10000];\n\n    while ((u_char *) range < base + size) {\n        while (range->value) {\n            ngx_crc32_update(&crc32, (u_char *) range,\n                             sizeof(ngx_http_geo_range_t));\n            range->value = (ngx_http_variable_value_t *)\n                               ((u_char *) range->value + (size_t) base);\n            range++;\n        }\n        ngx_crc32_update(&crc32, (u_char *) range, sizeof(void *));\n        range = (ngx_http_geo_range_t *) ((u_char *) range + sizeof(void *));\n    }\n\n    ngx_crc32_final(crc32);\n\n    if (crc32 != header->crc32) {\n        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                  \"CRC32 mismatch in binary geo range base \\\"%s\\\"\", name->data);\n        goto failed;\n    }\n\n    ngx_conf_log_error(NGX_LOG_NOTICE, cf, 0,\n                       \"using binary geo range base \\\"%s\\\"\", name->data);\n\n    ctx->include_name = *name;\n    ctx->binary_include = 1;\n    ctx->high.low = ranges;\n    rc = NGX_OK;\n\n    goto done;\n\nfailed:\n\n    rc = NGX_DECLINED;\n\ndone:\n\n    if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {\n        ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,\n                      ngx_close_file_n \" \\\"%s\\\" failed\", name->data);\n    }\n\n    return rc;\n}\n\n\nstatic void\nngx_http_geo_create_binary_base(ngx_http_geo_conf_ctx_t *ctx)\n{\n    u_char                              *p;\n    uint32_t                             hash;\n    ngx_str_t                            s;\n    ngx_uint_t                           i;\n    ngx_file_mapping_t                   fm;\n    ngx_http_geo_range_t                *r, *range, **ranges;\n    ngx_http_geo_header_t               *header;\n    ngx_http_geo_variable_value_node_t  *gvvn;\n\n    fm.name = ngx_pnalloc(ctx->temp_pool, ctx->include_name.len + 5);\n    if (fm.name == NULL) {\n        return;\n    }\n\n    ngx_sprintf(fm.name, \"%V.bin%Z\", &ctx->include_name);\n\n    fm.size = ctx->data_size;\n    fm.log = ctx->pool->log;\n\n    ngx_log_error(NGX_LOG_NOTICE, fm.log, 0,\n                  \"creating binary geo range base \\\"%s\\\"\", fm.name);\n\n    if (ngx_create_file_mapping(&fm) != NGX_OK) {\n        return;\n    }\n\n    p = ngx_cpymem(fm.addr, &ngx_http_geo_header,\n                   sizeof(ngx_http_geo_header_t));\n\n    p = ngx_http_geo_copy_values(fm.addr, p, ctx->rbtree.root,\n                                 ctx->rbtree.sentinel);\n\n    p += sizeof(ngx_http_variable_value_t);\n\n    ranges = (ngx_http_geo_range_t **) p;\n\n    p += 0x10000 * sizeof(ngx_http_geo_range_t *);\n\n    for (i = 0; i < 0x10000; i++) {\n        r = ctx->high.low[i];\n        if (r == NULL) {\n            continue;\n        }\n\n        range = (ngx_http_geo_range_t *) p;\n        ranges[i] = (ngx_http_geo_range_t *) (p - (u_char *) fm.addr);\n\n        do {\n            s.len = r->value->len;\n            s.data = r->value->data;\n            hash = ngx_crc32_long(s.data, s.len);\n            gvvn = (ngx_http_geo_variable_value_node_t *)\n                        ngx_str_rbtree_lookup(&ctx->rbtree, &s, hash);\n\n            range->value = (ngx_http_variable_value_t *) gvvn->offset;\n            range->start = r->start;\n            range->end = r->end;\n            range++;\n\n        } while ((++r)->value);\n\n        range->value = NULL;\n\n        p = (u_char *) range + sizeof(void *);\n    }\n\n    header = fm.addr;\n    header->crc32 = ngx_crc32_long((u_char *) fm.addr\n                                       + sizeof(ngx_http_geo_header_t),\n                                   fm.size - sizeof(ngx_http_geo_header_t));\n\n    ngx_close_file_mapping(&fm);\n}\n\n\nstatic u_char *\nngx_http_geo_copy_values(u_char *base, u_char *p, ngx_rbtree_node_t *node,\n    ngx_rbtree_node_t *sentinel)\n{\n    ngx_http_variable_value_t           *vv;\n    ngx_http_geo_variable_value_node_t  *gvvn;\n\n    if (node == sentinel) {\n        return p;\n    }\n\n    gvvn = (ngx_http_geo_variable_value_node_t *) node;\n    gvvn->offset = p - base;\n\n    vv = (ngx_http_variable_value_t *) p;\n    *vv = *gvvn->value;\n    p += sizeof(ngx_http_variable_value_t);\n    vv->data = (u_char *) (p - base);\n\n    p = ngx_cpymem(p, gvvn->sn.str.data, gvvn->sn.str.len);\n\n    p = ngx_align_ptr(p, sizeof(void *));\n\n    p = ngx_http_geo_copy_values(base, p, node->left, sentinel);\n\n    return ngx_http_geo_copy_values(base, p, node->right, sentinel);\n}\n"
  },
  {
    "path": "src/http/modules/ngx_http_geoip_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n#include <GeoIP.h>\n#include <GeoIPCity.h>\n\n\n#define NGX_GEOIP_COUNTRY_CODE   0\n#define NGX_GEOIP_COUNTRY_CODE3  1\n#define NGX_GEOIP_COUNTRY_NAME   2\n\n\ntypedef struct {\n    GeoIP        *country;\n#if (T_GEO)\n    GeoIP        *region;\n#endif\n    GeoIP        *org;\n    GeoIP        *city;\n    ngx_array_t  *proxies;    /* array of ngx_cidr_t */\n    ngx_flag_t    proxy_recursive;\n#if (NGX_HAVE_GEOIP_V6)\n    unsigned      country_v6:1;\n    unsigned      org_v6:1;\n    unsigned      city_v6:1;\n#endif\n} ngx_http_geoip_conf_t;\n\n\ntypedef struct {\n    ngx_str_t    *name;\n    uintptr_t     data;\n} ngx_http_geoip_var_t;\n\n\ntypedef const char *(*ngx_http_geoip_variable_handler_pt)(GeoIP *,\n    u_long addr);\n\n\nngx_http_geoip_variable_handler_pt ngx_http_geoip_country_functions[] = {\n    GeoIP_country_code_by_ipnum,\n    GeoIP_country_code3_by_ipnum,\n    GeoIP_country_name_by_ipnum,\n};\n\n\n#if (NGX_HAVE_GEOIP_V6)\n\ntypedef const char *(*ngx_http_geoip_variable_handler_v6_pt)(GeoIP *,\n    geoipv6_t addr);\n\n\nngx_http_geoip_variable_handler_v6_pt ngx_http_geoip_country_v6_functions[] = {\n    GeoIP_country_code_by_ipnum_v6,\n    GeoIP_country_code3_by_ipnum_v6,\n    GeoIP_country_name_by_ipnum_v6,\n};\n\n#endif\n\n\nstatic ngx_int_t ngx_http_geoip_country_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\n#if (T_GEO)\nstatic ngx_int_t ngx_http_geoip_region_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\n#endif\nstatic ngx_int_t ngx_http_geoip_org_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_geoip_city_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_geoip_region_name_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_geoip_city_float_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_geoip_city_int_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic GeoIPRecord *ngx_http_geoip_get_city_record(ngx_http_request_t *r);\n\nstatic ngx_int_t ngx_http_geoip_add_variables(ngx_conf_t *cf);\nstatic void *ngx_http_geoip_create_conf(ngx_conf_t *cf);\nstatic char *ngx_http_geoip_init_conf(ngx_conf_t *cf, void *conf);\nstatic char *ngx_http_geoip_country(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n#if (T_GEO)\nstatic char *ngx_http_geoip_region(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n#endif\nstatic char *ngx_http_geoip_org(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_http_geoip_city(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_http_geoip_proxy(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic ngx_int_t ngx_http_geoip_cidr_value(ngx_conf_t *cf, ngx_str_t *net,\n    ngx_cidr_t *cidr);\nstatic void ngx_http_geoip_cleanup(void *data);\n\n\nstatic ngx_command_t  ngx_http_geoip_commands[] = {\n\n    { ngx_string(\"geoip_country\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE12,\n      ngx_http_geoip_country,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      0,\n      NULL },\n\n#if (T_GEO)\n    { ngx_string(\"geoip_region\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE12,\n      ngx_http_geoip_region,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      0,\n      NULL },\n#endif\n\n    { ngx_string(\"geoip_org\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE12,\n      ngx_http_geoip_org,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"geoip_city\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE12,\n      ngx_http_geoip_city,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"geoip_proxy\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,\n      ngx_http_geoip_proxy,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"geoip_proxy_recursive\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      offsetof(ngx_http_geoip_conf_t, proxy_recursive),\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_geoip_module_ctx = {\n    ngx_http_geoip_add_variables,          /* preconfiguration */\n    NULL,                                  /* postconfiguration */\n\n    ngx_http_geoip_create_conf,            /* create main configuration */\n    ngx_http_geoip_init_conf,              /* 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_geoip_module = {\n    NGX_MODULE_V1,\n    &ngx_http_geoip_module_ctx,            /* module context */\n    ngx_http_geoip_commands,               /* 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_http_variable_t  ngx_http_geoip_vars[] = {\n\n    { ngx_string(\"geoip_country_code\"), NULL,\n      ngx_http_geoip_country_variable,\n      NGX_GEOIP_COUNTRY_CODE, 0, 0 },\n\n    { ngx_string(\"geoip_country_code3\"), NULL,\n      ngx_http_geoip_country_variable,\n      NGX_GEOIP_COUNTRY_CODE3, 0, 0 },\n\n    { ngx_string(\"geoip_country_name\"), NULL,\n      ngx_http_geoip_country_variable,\n      NGX_GEOIP_COUNTRY_NAME, 0, 0 },\n\n#if (T_GEO)\n    { ngx_string(\"geoip_region_country_code\"), NULL,\n      ngx_http_geoip_region_variable,\n      offsetof(GeoIPRegion, country_code), 0, 0 },\n\n    { ngx_string(\"geoip_region_region\"), NULL,\n      ngx_http_geoip_region_variable,\n      offsetof(GeoIPRegion, region), 0, 0 },\n#endif\n\n    { ngx_string(\"geoip_org\"), NULL,\n      ngx_http_geoip_org_variable,\n      0, 0, 0 },\n\n    { ngx_string(\"geoip_city_continent_code\"), NULL,\n      ngx_http_geoip_city_variable,\n      offsetof(GeoIPRecord, continent_code), 0, 0 },\n\n    { ngx_string(\"geoip_city_country_code\"), NULL,\n      ngx_http_geoip_city_variable,\n      offsetof(GeoIPRecord, country_code), 0, 0 },\n\n    { ngx_string(\"geoip_city_country_code3\"), NULL,\n      ngx_http_geoip_city_variable,\n      offsetof(GeoIPRecord, country_code3), 0, 0 },\n\n    { ngx_string(\"geoip_city_country_name\"), NULL,\n      ngx_http_geoip_city_variable,\n      offsetof(GeoIPRecord, country_name), 0, 0 },\n\n    { ngx_string(\"geoip_region\"), NULL,\n      ngx_http_geoip_city_variable,\n      offsetof(GeoIPRecord, region), 0, 0 },\n\n    { ngx_string(\"geoip_region_name\"), NULL,\n      ngx_http_geoip_region_name_variable,\n      0, 0, 0 },\n\n    { ngx_string(\"geoip_city\"), NULL,\n      ngx_http_geoip_city_variable,\n      offsetof(GeoIPRecord, city), 0, 0 },\n\n    { ngx_string(\"geoip_postal_code\"), NULL,\n      ngx_http_geoip_city_variable,\n      offsetof(GeoIPRecord, postal_code), 0, 0 },\n\n    { ngx_string(\"geoip_latitude\"), NULL,\n      ngx_http_geoip_city_float_variable,\n      offsetof(GeoIPRecord, latitude), 0, 0 },\n\n    { ngx_string(\"geoip_longitude\"), NULL,\n      ngx_http_geoip_city_float_variable,\n      offsetof(GeoIPRecord, longitude), 0, 0 },\n\n    { ngx_string(\"geoip_dma_code\"), NULL,\n      ngx_http_geoip_city_int_variable,\n      offsetof(GeoIPRecord, dma_code), 0, 0 },\n\n    { ngx_string(\"geoip_area_code\"), NULL,\n      ngx_http_geoip_city_int_variable,\n      offsetof(GeoIPRecord, area_code), 0, 0 },\n\n      ngx_http_null_variable\n};\n\n\nstatic u_long\nngx_http_geoip_addr(ngx_http_request_t *r, ngx_http_geoip_conf_t *gcf)\n{\n    ngx_addr_t           addr;\n    ngx_table_elt_t     *xfwd;\n    struct sockaddr_in  *sin;\n\n    addr.sockaddr = r->connection->sockaddr;\n    addr.socklen = r->connection->socklen;\n    /* addr.name = r->connection->addr_text; */\n\n    xfwd = r->headers_in.x_forwarded_for;\n\n    if (xfwd != NULL && gcf->proxies != NULL) {\n        (void) ngx_http_get_forwarded_addr(r, &addr, xfwd, NULL,\n                                           gcf->proxies, gcf->proxy_recursive);\n    }\n\n#if (NGX_HAVE_INET6)\n\n    if (addr.sockaddr->sa_family == AF_INET6) {\n        u_char           *p;\n        in_addr_t         inaddr;\n        struct in6_addr  *inaddr6;\n\n        inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr;\n\n        if (IN6_IS_ADDR_V4MAPPED(inaddr6)) {\n            p = inaddr6->s6_addr;\n\n            inaddr = p[12] << 24;\n            inaddr += p[13] << 16;\n            inaddr += p[14] << 8;\n            inaddr += p[15];\n\n            return inaddr;\n        }\n    }\n\n#endif\n\n    if (addr.sockaddr->sa_family != AF_INET) {\n        return INADDR_NONE;\n    }\n\n    sin = (struct sockaddr_in *) addr.sockaddr;\n    return ntohl(sin->sin_addr.s_addr);\n}\n\n\n#if (NGX_HAVE_GEOIP_V6)\n\nstatic geoipv6_t\nngx_http_geoip_addr_v6(ngx_http_request_t *r, ngx_http_geoip_conf_t *gcf)\n{\n    ngx_addr_t            addr;\n    ngx_table_elt_t      *xfwd;\n    in_addr_t             addr4;\n    struct in6_addr       addr6;\n    struct sockaddr_in   *sin;\n    struct sockaddr_in6  *sin6;\n\n    addr.sockaddr = r->connection->sockaddr;\n    addr.socklen = r->connection->socklen;\n    /* addr.name = r->connection->addr_text; */\n\n    xfwd = r->headers_in.x_forwarded_for;\n\n    if (xfwd != NULL && gcf->proxies != NULL) {\n        (void) ngx_http_get_forwarded_addr(r, &addr, xfwd, NULL,\n                                           gcf->proxies, gcf->proxy_recursive);\n    }\n\n    switch (addr.sockaddr->sa_family) {\n\n    case AF_INET:\n        /* Produce IPv4-mapped IPv6 address. */\n        sin = (struct sockaddr_in *) addr.sockaddr;\n        addr4 = ntohl(sin->sin_addr.s_addr);\n\n        ngx_memzero(&addr6, sizeof(struct in6_addr));\n        addr6.s6_addr[10] = 0xff;\n        addr6.s6_addr[11] = 0xff;\n        addr6.s6_addr[12] = addr4 >> 24;\n        addr6.s6_addr[13] = addr4 >> 16;\n        addr6.s6_addr[14] = addr4 >> 8;\n        addr6.s6_addr[15] = addr4;\n        return addr6;\n\n    case AF_INET6:\n        sin6 = (struct sockaddr_in6 *) addr.sockaddr;\n        return sin6->sin6_addr;\n\n    default:\n        return in6addr_any;\n    }\n}\n\n#endif\n\n\nstatic ngx_int_t\nngx_http_geoip_country_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    ngx_http_geoip_variable_handler_pt     handler =\n        ngx_http_geoip_country_functions[data];\n#if (NGX_HAVE_GEOIP_V6)\n    ngx_http_geoip_variable_handler_v6_pt  handler_v6 =\n        ngx_http_geoip_country_v6_functions[data];\n#endif\n\n    const char             *val;\n    ngx_http_geoip_conf_t  *gcf;\n\n    gcf = ngx_http_get_module_main_conf(r, ngx_http_geoip_module);\n\n    if (gcf->country == NULL) {\n        goto not_found;\n    }\n\n#if (NGX_HAVE_GEOIP_V6)\n    val = gcf->country_v6\n              ? handler_v6(gcf->country, ngx_http_geoip_addr_v6(r, gcf))\n              : handler(gcf->country, ngx_http_geoip_addr(r, gcf));\n#else\n    val = handler(gcf->country, ngx_http_geoip_addr(r, gcf));\n#endif\n\n    if (val == NULL) {\n        goto not_found;\n    }\n\n    v->len = ngx_strlen(val);\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = (u_char *) val;\n\n    return NGX_OK;\n\nnot_found:\n\n    v->not_found = 1;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_geoip_org_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    size_t                  len;\n    char                   *val;\n    ngx_http_geoip_conf_t  *gcf;\n\n    gcf = ngx_http_get_module_main_conf(r, ngx_http_geoip_module);\n\n    if (gcf->org == NULL) {\n        goto not_found;\n    }\n\n#if (NGX_HAVE_GEOIP_V6)\n    val = gcf->org_v6\n              ? GeoIP_name_by_ipnum_v6(gcf->org,\n                                       ngx_http_geoip_addr_v6(r, gcf))\n              : GeoIP_name_by_ipnum(gcf->org,\n                                    ngx_http_geoip_addr(r, gcf));\n#else\n    val = GeoIP_name_by_ipnum(gcf->org, ngx_http_geoip_addr(r, gcf));\n#endif\n\n    if (val == NULL) {\n        goto not_found;\n    }\n\n    len = ngx_strlen(val);\n    v->data = ngx_pnalloc(r->pool, len);\n    if (v->data == NULL) {\n        ngx_free(val);\n        return NGX_ERROR;\n    }\n\n    ngx_memcpy(v->data, val, len);\n\n    v->len = len;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    ngx_free(val);\n\n    return NGX_OK;\n\nnot_found:\n\n    v->not_found = 1;\n\n    return NGX_OK;\n}\n\n\n#if (T_GEO)\nstatic ngx_int_t\nngx_http_geoip_region_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    char         *val;\n    size_t        len;\n    GeoIPRegion  *gr;\n\n    ngx_http_geoip_conf_t  *gcf;\n\n    gcf = ngx_http_get_module_main_conf(r, ngx_http_geoip_module);\n\n    if (gcf->region == NULL) {\n        goto not_found;\n    }\n\n    gr = GeoIP_region_by_ipnum(gcf->region, ngx_http_geoip_addr(r, gcf));\n\n    if (gr == NULL) {\n        goto not_found;\n    }\n\n    val = (char *) gr + data;\n    if (val == NULL) {\n        goto no_value;\n    }\n\n    len = ngx_strlen(val);\n    v->data = ngx_pnalloc(r->pool, len);\n    if (v->data == NULL) {\n        GeoIPRegion_delete(gr);\n        return NGX_ERROR;\n    }\n\n    ngx_memcpy(v->data, val, len);\n\n    v->len = len;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    GeoIPRegion_delete(gr);\n\n    return NGX_OK;\n\nno_value:\n\n    GeoIPRegion_delete(gr);\n\nnot_found:\n\n    v->not_found = 1;\n\n    return NGX_OK;\n}\n#endif\n\n\nstatic ngx_int_t\nngx_http_geoip_city_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    char         *val;\n    size_t        len;\n    GeoIPRecord  *gr;\n\n    gr = ngx_http_geoip_get_city_record(r);\n    if (gr == NULL) {\n        goto not_found;\n    }\n\n    val = *(char **) ((char *) gr + data);\n    if (val == NULL) {\n        goto no_value;\n    }\n\n    len = ngx_strlen(val);\n    v->data = ngx_pnalloc(r->pool, len);\n    if (v->data == NULL) {\n        GeoIPRecord_delete(gr);\n        return NGX_ERROR;\n    }\n\n    ngx_memcpy(v->data, val, len);\n\n    v->len = len;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    GeoIPRecord_delete(gr);\n\n    return NGX_OK;\n\nno_value:\n\n    GeoIPRecord_delete(gr);\n\nnot_found:\n\n    v->not_found = 1;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_geoip_region_name_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    size_t        len;\n    const char   *val;\n    GeoIPRecord  *gr;\n\n    gr = ngx_http_geoip_get_city_record(r);\n    if (gr == NULL) {\n        goto not_found;\n    }\n\n    val = GeoIP_region_name_by_code(gr->country_code, gr->region);\n\n    GeoIPRecord_delete(gr);\n\n    if (val == NULL) {\n        goto not_found;\n    }\n\n    len = ngx_strlen(val);\n    v->data = ngx_pnalloc(r->pool, len);\n    if (v->data == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_memcpy(v->data, val, len);\n\n    v->len = len;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    return NGX_OK;\n\nnot_found:\n\n    v->not_found = 1;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_geoip_city_float_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    float         val;\n    GeoIPRecord  *gr;\n\n    gr = ngx_http_geoip_get_city_record(r);\n    if (gr == NULL) {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    v->data = ngx_pnalloc(r->pool, NGX_INT64_LEN + 5);\n    if (v->data == NULL) {\n        GeoIPRecord_delete(gr);\n        return NGX_ERROR;\n    }\n\n    val = *(float *) ((char *) gr + data);\n\n    v->len = ngx_sprintf(v->data, \"%.4f\", val) - v->data;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    GeoIPRecord_delete(gr);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_geoip_city_int_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    int           val;\n    GeoIPRecord  *gr;\n\n    gr = ngx_http_geoip_get_city_record(r);\n    if (gr == NULL) {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    v->data = ngx_pnalloc(r->pool, NGX_INT64_LEN);\n    if (v->data == NULL) {\n        GeoIPRecord_delete(gr);\n        return NGX_ERROR;\n    }\n\n    val = *(int *) ((char *) gr + data);\n\n    v->len = ngx_sprintf(v->data, \"%d\", val) - v->data;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    GeoIPRecord_delete(gr);\n\n    return NGX_OK;\n}\n\n\nstatic GeoIPRecord *\nngx_http_geoip_get_city_record(ngx_http_request_t *r)\n{\n    ngx_http_geoip_conf_t  *gcf;\n\n    gcf = ngx_http_get_module_main_conf(r, ngx_http_geoip_module);\n\n    if (gcf->city) {\n#if (NGX_HAVE_GEOIP_V6)\n        return gcf->city_v6\n                   ? GeoIP_record_by_ipnum_v6(gcf->city,\n                                              ngx_http_geoip_addr_v6(r, gcf))\n                   : GeoIP_record_by_ipnum(gcf->city,\n                                           ngx_http_geoip_addr(r, gcf));\n#else\n        return GeoIP_record_by_ipnum(gcf->city, ngx_http_geoip_addr(r, gcf));\n#endif\n    }\n\n    return NULL;\n}\n\n\nstatic ngx_int_t\nngx_http_geoip_add_variables(ngx_conf_t *cf)\n{\n    ngx_http_variable_t  *var, *v;\n\n    for (v = ngx_http_geoip_vars; v->name.len; v++) {\n        var = ngx_http_add_variable(cf, &v->name, v->flags);\n        if (var == NULL) {\n            return NGX_ERROR;\n        }\n\n        var->get_handler = v->get_handler;\n        var->data = v->data;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void *\nngx_http_geoip_create_conf(ngx_conf_t *cf)\n{\n    ngx_pool_cleanup_t     *cln;\n    ngx_http_geoip_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_geoip_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    conf->proxy_recursive = NGX_CONF_UNSET;\n\n    cln = ngx_pool_cleanup_add(cf->pool, 0);\n    if (cln == NULL) {\n        return NULL;\n    }\n\n    cln->handler = ngx_http_geoip_cleanup;\n    cln->data = conf;\n\n    return conf;\n}\n\n\nstatic char *\nngx_http_geoip_init_conf(ngx_conf_t *cf, void *conf)\n{\n    ngx_http_geoip_conf_t  *gcf = conf;\n\n    ngx_conf_init_value(gcf->proxy_recursive, 0);\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_geoip_country(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_geoip_conf_t  *gcf = conf;\n\n    ngx_str_t  *value;\n\n    if (gcf->country) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    gcf->country = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE);\n\n    if (gcf->country == NULL) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"GeoIP_open(\\\"%V\\\") failed\", &value[1]);\n\n        return NGX_CONF_ERROR;\n    }\n\n    if (cf->args->nelts == 3) {\n        if (ngx_strcmp(value[2].data, \"utf8\") == 0) {\n            GeoIP_set_charset(gcf->country, GEOIP_CHARSET_UTF8);\n\n        } else {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"invalid parameter \\\"%V\\\"\", &value[2]);\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    switch (gcf->country->databaseType) {\n\n    case GEOIP_COUNTRY_EDITION:\n\n        return NGX_CONF_OK;\n\n#if (NGX_HAVE_GEOIP_V6)\n    case GEOIP_COUNTRY_EDITION_V6:\n\n        gcf->country_v6 = 1;\n        return NGX_CONF_OK;\n#endif\n\n    default:\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid GeoIP database \\\"%V\\\" type:%d\",\n                           &value[1], gcf->country->databaseType);\n        return NGX_CONF_ERROR;\n    }\n}\n\n\n#if (T_GEO)\nstatic char *\nngx_http_geoip_region(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_geoip_conf_t  *gcf = conf;\n\n    ngx_str_t  *value;\n\n    if (gcf->region) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    gcf->region = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE);\n\n    if (gcf->region == NULL) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"GeoIP_open(\\\"%V\\\") failed\", &value[1]);\n\n        return NGX_CONF_ERROR;\n    }\n\n    if (cf->args->nelts == 3) {\n        if (ngx_strcmp(value[2].data, \"utf8\") == 0) {\n            GeoIP_set_charset (gcf->region, GEOIP_CHARSET_UTF8);\n\n        } else {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"invalid parameter \\\"%V\\\"\", &value[2]);\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    switch (gcf->region->databaseType) {\n        case GEOIP_REGION_EDITION_REV0:\n        case GEOIP_REGION_EDITION_REV1:\n            return NGX_CONF_OK;\n    default:\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid GeoIP database \\\"%V\\\" type:%d\",\n                           &value[1], gcf->region->databaseType);\n        return NGX_CONF_ERROR;\n    }\n}\n#endif\n\n\nstatic char *\nngx_http_geoip_org(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_geoip_conf_t  *gcf = conf;\n\n    ngx_str_t  *value;\n\n    if (gcf->org) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    gcf->org = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE);\n\n    if (gcf->org == NULL) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"GeoIP_open(\\\"%V\\\") failed\", &value[1]);\n\n        return NGX_CONF_ERROR;\n    }\n\n    if (cf->args->nelts == 3) {\n        if (ngx_strcmp(value[2].data, \"utf8\") == 0) {\n            GeoIP_set_charset(gcf->org, GEOIP_CHARSET_UTF8);\n\n        } else {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"invalid parameter \\\"%V\\\"\", &value[2]);\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    switch (gcf->org->databaseType) {\n\n    case GEOIP_ISP_EDITION:\n    case GEOIP_ORG_EDITION:\n    case GEOIP_DOMAIN_EDITION:\n    case GEOIP_ASNUM_EDITION:\n\n        return NGX_CONF_OK;\n\n#if (NGX_HAVE_GEOIP_V6)\n    case GEOIP_ISP_EDITION_V6:\n    case GEOIP_ORG_EDITION_V6:\n    case GEOIP_DOMAIN_EDITION_V6:\n    case GEOIP_ASNUM_EDITION_V6:\n\n        gcf->org_v6 = 1;\n        return NGX_CONF_OK;\n#endif\n\n    default:\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid GeoIP database \\\"%V\\\" type:%d\",\n                           &value[1], gcf->org->databaseType);\n        return NGX_CONF_ERROR;\n    }\n}\n\n\nstatic char *\nngx_http_geoip_city(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_geoip_conf_t  *gcf = conf;\n\n    ngx_str_t  *value;\n\n    if (gcf->city) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    gcf->city = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE);\n\n    if (gcf->city == NULL) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"GeoIP_open(\\\"%V\\\") failed\", &value[1]);\n\n        return NGX_CONF_ERROR;\n    }\n\n    if (cf->args->nelts == 3) {\n        if (ngx_strcmp(value[2].data, \"utf8\") == 0) {\n            GeoIP_set_charset(gcf->city, GEOIP_CHARSET_UTF8);\n\n        } else {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"invalid parameter \\\"%V\\\"\", &value[2]);\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    switch (gcf->city->databaseType) {\n\n    case GEOIP_CITY_EDITION_REV0:\n    case GEOIP_CITY_EDITION_REV1:\n\n        return NGX_CONF_OK;\n\n#if (NGX_HAVE_GEOIP_V6)\n    case GEOIP_CITY_EDITION_REV0_V6:\n    case GEOIP_CITY_EDITION_REV1_V6:\n\n        gcf->city_v6 = 1;\n        return NGX_CONF_OK;\n#endif\n\n    default:\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid GeoIP City database \\\"%V\\\" type:%d\",\n                           &value[1], gcf->city->databaseType);\n        return NGX_CONF_ERROR;\n    }\n}\n\n\nstatic char *\nngx_http_geoip_proxy(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_geoip_conf_t  *gcf = conf;\n\n    ngx_str_t   *value;\n    ngx_cidr_t  cidr, *c;\n\n    value = cf->args->elts;\n\n    if (ngx_http_geoip_cidr_value(cf, &value[1], &cidr) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (gcf->proxies == NULL) {\n        gcf->proxies = ngx_array_create(cf->pool, 4, sizeof(ngx_cidr_t));\n        if (gcf->proxies == NULL) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    c = ngx_array_push(gcf->proxies);\n    if (c == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    *c = cidr;\n\n    return NGX_CONF_OK;\n}\n\nstatic ngx_int_t\nngx_http_geoip_cidr_value(ngx_conf_t *cf, ngx_str_t *net, ngx_cidr_t *cidr)\n{\n    ngx_int_t  rc;\n\n    if (ngx_strcmp(net->data, \"255.255.255.255\") == 0) {\n        cidr->family = AF_INET;\n        cidr->u.in.addr = 0xffffffff;\n        cidr->u.in.mask = 0xffffffff;\n\n        return NGX_OK;\n    }\n\n    rc = ngx_ptocidr(net, cidr);\n\n    if (rc == NGX_ERROR) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, \"invalid network \\\"%V\\\"\", net);\n        return NGX_ERROR;\n    }\n\n    if (rc == NGX_DONE) {\n        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                           \"low address bits of %V are meaningless\", net);\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_geoip_cleanup(void *data)\n{\n    ngx_http_geoip_conf_t  *gcf = data;\n\n    if (gcf->country) {\n        GeoIP_delete(gcf->country);\n    }\n\n#if (T_GEO)\n    if (gcf->region) {\n        GeoIP_delete(gcf->region);\n    }\n#endif\n\n    if (gcf->org) {\n        GeoIP_delete(gcf->org);\n    }\n\n    if (gcf->city) {\n        GeoIP_delete(gcf->city);\n    }\n}\n"
  },
  {
    "path": "src/http/modules/ngx_http_grpc_module.c",
    "content": "\n/*\n * Copyright (C) Maxim Dounin\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\ntypedef struct {\n    ngx_array_t               *flushes;\n    ngx_array_t               *lengths;\n    ngx_array_t               *values;\n    ngx_hash_t                 hash;\n} ngx_http_grpc_headers_t;\n\n\ntypedef struct {\n    ngx_http_upstream_conf_t   upstream;\n\n    ngx_http_grpc_headers_t    headers;\n    ngx_array_t               *headers_source;\n\n    ngx_str_t                  host;\n    ngx_uint_t                 host_set;\n\n    ngx_array_t               *grpc_lengths;\n    ngx_array_t               *grpc_values;\n\n#if (NGX_HTTP_SSL)\n    ngx_uint_t                 ssl;\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_array_t               *ssl_conf_commands;\n#endif\n} ngx_http_grpc_loc_conf_t;\n\n\ntypedef enum {\n    ngx_http_grpc_st_start = 0,\n    ngx_http_grpc_st_length_2,\n    ngx_http_grpc_st_length_3,\n    ngx_http_grpc_st_type,\n    ngx_http_grpc_st_flags,\n    ngx_http_grpc_st_stream_id,\n    ngx_http_grpc_st_stream_id_2,\n    ngx_http_grpc_st_stream_id_3,\n    ngx_http_grpc_st_stream_id_4,\n    ngx_http_grpc_st_payload,\n    ngx_http_grpc_st_padding\n} ngx_http_grpc_state_e;\n\n\ntypedef struct {\n    size_t                     init_window;\n    size_t                     send_window;\n    size_t                     recv_window;\n    ngx_uint_t                 last_stream_id;\n} ngx_http_grpc_conn_t;\n\n\ntypedef struct {\n    ngx_http_grpc_state_e      state;\n    ngx_uint_t                 frame_state;\n    ngx_uint_t                 fragment_state;\n\n    ngx_chain_t               *in;\n    ngx_chain_t               *out;\n    ngx_chain_t               *free;\n    ngx_chain_t               *busy;\n\n    ngx_http_grpc_conn_t      *connection;\n\n    ngx_uint_t                 id;\n\n    ngx_uint_t                 pings;\n    ngx_uint_t                 settings;\n\n    off_t                      length;\n\n    ssize_t                    send_window;\n    size_t                     recv_window;\n\n    size_t                     rest;\n    ngx_uint_t                 stream_id;\n    u_char                     type;\n    u_char                     flags;\n    u_char                     padding;\n\n    ngx_uint_t                 error;\n    ngx_uint_t                 window_update;\n\n    ngx_uint_t                 setting_id;\n    ngx_uint_t                 setting_value;\n\n    u_char                     ping_data[8];\n\n    ngx_uint_t                 index;\n    ngx_str_t                  name;\n    ngx_str_t                  value;\n\n    u_char                    *field_end;\n    size_t                     field_length;\n    size_t                     field_rest;\n    u_char                     field_state;\n\n    unsigned                   literal:1;\n    unsigned                   field_huffman:1;\n\n    unsigned                   header_sent:1;\n    unsigned                   output_closed:1;\n    unsigned                   output_blocked:1;\n    unsigned                   parsing_headers:1;\n    unsigned                   end_stream:1;\n    unsigned                   done:1;\n    unsigned                   status:1;\n    unsigned                   rst:1;\n    unsigned                   goaway:1;\n\n    ngx_http_request_t        *request;\n\n    ngx_str_t                  host;\n} ngx_http_grpc_ctx_t;\n\n\ntypedef struct {\n    u_char                     length_0;\n    u_char                     length_1;\n    u_char                     length_2;\n    u_char                     type;\n    u_char                     flags;\n    u_char                     stream_id_0;\n    u_char                     stream_id_1;\n    u_char                     stream_id_2;\n    u_char                     stream_id_3;\n} ngx_http_grpc_frame_t;\n\n\nstatic ngx_int_t ngx_http_grpc_eval(ngx_http_request_t *r,\n    ngx_http_grpc_ctx_t *ctx, ngx_http_grpc_loc_conf_t *glcf);\nstatic ngx_int_t ngx_http_grpc_create_request(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_grpc_reinit_request(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_grpc_body_output_filter(void *data, ngx_chain_t *in);\nstatic ngx_int_t ngx_http_grpc_process_header(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_grpc_filter_init(void *data);\nstatic ngx_int_t ngx_http_grpc_filter(void *data, ssize_t bytes);\n\nstatic ngx_int_t ngx_http_grpc_parse_frame(ngx_http_request_t *r,\n    ngx_http_grpc_ctx_t *ctx, ngx_buf_t *b);\nstatic ngx_int_t ngx_http_grpc_parse_header(ngx_http_request_t *r,\n    ngx_http_grpc_ctx_t *ctx, ngx_buf_t *b);\nstatic ngx_int_t ngx_http_grpc_parse_fragment(ngx_http_request_t *r,\n    ngx_http_grpc_ctx_t *ctx, ngx_buf_t *b);\nstatic ngx_int_t ngx_http_grpc_validate_header_name(ngx_http_request_t *r,\n    ngx_str_t *s);\nstatic ngx_int_t ngx_http_grpc_validate_header_value(ngx_http_request_t *r,\n    ngx_str_t *s);\nstatic ngx_int_t ngx_http_grpc_parse_rst_stream(ngx_http_request_t *r,\n    ngx_http_grpc_ctx_t *ctx, ngx_buf_t *b);\nstatic ngx_int_t ngx_http_grpc_parse_goaway(ngx_http_request_t *r,\n    ngx_http_grpc_ctx_t *ctx, ngx_buf_t *b);\nstatic ngx_int_t ngx_http_grpc_parse_window_update(ngx_http_request_t *r,\n    ngx_http_grpc_ctx_t *ctx, ngx_buf_t *b);\nstatic ngx_int_t ngx_http_grpc_parse_settings(ngx_http_request_t *r,\n    ngx_http_grpc_ctx_t *ctx, ngx_buf_t *b);\nstatic ngx_int_t ngx_http_grpc_parse_ping(ngx_http_request_t *r,\n    ngx_http_grpc_ctx_t *ctx, ngx_buf_t *b);\n\nstatic ngx_int_t ngx_http_grpc_send_settings_ack(ngx_http_request_t *r,\n    ngx_http_grpc_ctx_t *ctx);\nstatic ngx_int_t ngx_http_grpc_send_ping_ack(ngx_http_request_t *r,\n    ngx_http_grpc_ctx_t *ctx);\nstatic ngx_int_t ngx_http_grpc_send_window_update(ngx_http_request_t *r,\n    ngx_http_grpc_ctx_t *ctx);\n\nstatic ngx_chain_t *ngx_http_grpc_get_buf(ngx_http_request_t *r,\n    ngx_http_grpc_ctx_t *ctx);\nstatic ngx_http_grpc_ctx_t *ngx_http_grpc_get_ctx(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_grpc_get_connection_data(ngx_http_request_t *r,\n    ngx_http_grpc_ctx_t *ctx, ngx_peer_connection_t *pc);\nstatic void ngx_http_grpc_cleanup(void *data);\n\nstatic void ngx_http_grpc_abort_request(ngx_http_request_t *r);\nstatic void ngx_http_grpc_finalize_request(ngx_http_request_t *r,\n    ngx_int_t rc);\n\nstatic ngx_int_t ngx_http_grpc_internal_trailers_variable(\n    ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data);\n\nstatic ngx_int_t ngx_http_grpc_add_variables(ngx_conf_t *cf);\nstatic void *ngx_http_grpc_create_loc_conf(ngx_conf_t *cf);\nstatic char *ngx_http_grpc_merge_loc_conf(ngx_conf_t *cf,\n    void *parent, void *child);\nstatic ngx_int_t ngx_http_grpc_init_headers(ngx_conf_t *cf,\n    ngx_http_grpc_loc_conf_t *conf, ngx_http_grpc_headers_t *headers,\n    ngx_keyval_t *default_headers);\n\nstatic char *ngx_http_grpc_pass(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n\n#if (NGX_HTTP_SSL)\nstatic char *ngx_http_grpc_ssl_password_file(ngx_conf_t *cf,\n    ngx_command_t *cmd, void *conf);\nstatic char *ngx_http_grpc_ssl_conf_command_check(ngx_conf_t *cf, void *post,\n    void *data);\nstatic ngx_int_t ngx_http_grpc_merge_ssl(ngx_conf_t *cf,\n    ngx_http_grpc_loc_conf_t *conf, ngx_http_grpc_loc_conf_t *prev);\nstatic ngx_int_t ngx_http_grpc_set_ssl(ngx_conf_t *cf,\n    ngx_http_grpc_loc_conf_t *glcf);\n#endif\n\n\nstatic ngx_conf_bitmask_t  ngx_http_grpc_next_upstream_masks[] = {\n    { ngx_string(\"error\"), NGX_HTTP_UPSTREAM_FT_ERROR },\n    { ngx_string(\"timeout\"), NGX_HTTP_UPSTREAM_FT_TIMEOUT },\n    { ngx_string(\"invalid_header\"), NGX_HTTP_UPSTREAM_FT_INVALID_HEADER },\n    { ngx_string(\"non_idempotent\"), NGX_HTTP_UPSTREAM_FT_NON_IDEMPOTENT },\n    { ngx_string(\"http_500\"), NGX_HTTP_UPSTREAM_FT_HTTP_500 },\n    { ngx_string(\"http_502\"), NGX_HTTP_UPSTREAM_FT_HTTP_502 },\n    { ngx_string(\"http_503\"), NGX_HTTP_UPSTREAM_FT_HTTP_503 },\n    { ngx_string(\"http_504\"), NGX_HTTP_UPSTREAM_FT_HTTP_504 },\n    { ngx_string(\"http_403\"), NGX_HTTP_UPSTREAM_FT_HTTP_403 },\n    { ngx_string(\"http_404\"), NGX_HTTP_UPSTREAM_FT_HTTP_404 },\n    { ngx_string(\"http_429\"), NGX_HTTP_UPSTREAM_FT_HTTP_429 },\n    { ngx_string(\"off\"), NGX_HTTP_UPSTREAM_FT_OFF },\n    { ngx_null_string, 0 }\n};\n\n\n#if (NGX_HTTP_SSL)\n\nstatic ngx_conf_bitmask_t  ngx_http_grpc_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    { ngx_string(\"TLSv1.3\"), NGX_SSL_TLSv1_3 },\n    { ngx_null_string, 0 }\n};\n\nstatic ngx_conf_post_t  ngx_http_grpc_ssl_conf_command_post =\n    { ngx_http_grpc_ssl_conf_command_check };\n\n#endif\n\n\nstatic ngx_command_t  ngx_http_grpc_commands[] = {\n\n    { ngx_string(\"grpc_pass\"),\n      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,\n      ngx_http_grpc_pass,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"grpc_bind\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,\n      ngx_http_upstream_bind_set_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_grpc_loc_conf_t, upstream.local),\n      NULL },\n\n    { ngx_string(\"grpc_socket_keepalive\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_grpc_loc_conf_t, upstream.socket_keepalive),\n      NULL },\n\n    { ngx_string(\"grpc_connect_timeout\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_grpc_loc_conf_t, upstream.connect_timeout),\n      NULL },\n\n    { ngx_string(\"grpc_send_timeout\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_grpc_loc_conf_t, upstream.send_timeout),\n      NULL },\n\n    { ngx_string(\"grpc_intercept_errors\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_grpc_loc_conf_t, upstream.intercept_errors),\n      NULL },\n\n    { ngx_string(\"grpc_buffer_size\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_grpc_loc_conf_t, upstream.buffer_size),\n      NULL },\n\n    { ngx_string(\"grpc_read_timeout\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_grpc_loc_conf_t, upstream.read_timeout),\n      NULL },\n\n    { ngx_string(\"grpc_next_upstream\"),\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_grpc_loc_conf_t, upstream.next_upstream),\n      &ngx_http_grpc_next_upstream_masks },\n\n    { ngx_string(\"grpc_next_upstream_tries\"),\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_grpc_loc_conf_t, upstream.next_upstream_tries),\n      NULL },\n\n    { ngx_string(\"grpc_next_upstream_timeout\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_grpc_loc_conf_t, upstream.next_upstream_timeout),\n      NULL },\n\n    { ngx_string(\"grpc_set_header\"),\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_grpc_loc_conf_t, headers_source),\n      NULL },\n\n    { ngx_string(\"grpc_pass_header\"),\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_grpc_loc_conf_t, upstream.pass_headers),\n      NULL },\n\n    { ngx_string(\"grpc_hide_header\"),\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_grpc_loc_conf_t, upstream.hide_headers),\n      NULL },\n\n    { ngx_string(\"grpc_ignore_headers\"),\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_grpc_loc_conf_t, upstream.ignore_headers),\n      &ngx_http_upstream_ignore_headers_masks },\n\n#if (NGX_HTTP_SSL)\n\n    { ngx_string(\"grpc_ssl_session_reuse\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_grpc_loc_conf_t, upstream.ssl_session_reuse),\n      NULL },\n\n    { ngx_string(\"grpc_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_grpc_loc_conf_t, ssl_protocols),\n      &ngx_http_grpc_ssl_protocols },\n\n    { ngx_string(\"grpc_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_grpc_loc_conf_t, ssl_ciphers),\n      NULL },\n\n    { ngx_string(\"grpc_ssl_name\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_http_set_complex_value_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_grpc_loc_conf_t, upstream.ssl_name),\n      NULL },\n\n    { ngx_string(\"grpc_ssl_server_name\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_grpc_loc_conf_t, upstream.ssl_server_name),\n      NULL },\n\n    { ngx_string(\"grpc_ssl_verify\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_grpc_loc_conf_t, upstream.ssl_verify),\n      NULL },\n\n    { ngx_string(\"grpc_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_grpc_loc_conf_t, ssl_verify_depth),\n      NULL },\n\n    { ngx_string(\"grpc_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_grpc_loc_conf_t, ssl_trusted_certificate),\n      NULL },\n\n    { ngx_string(\"grpc_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_grpc_loc_conf_t, ssl_crl),\n      NULL },\n\n    { ngx_string(\"grpc_ssl_certificate\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_http_set_complex_value_zero_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_grpc_loc_conf_t, upstream.ssl_certificate),\n      NULL },\n\n    { ngx_string(\"grpc_ssl_certificate_key\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_http_set_complex_value_zero_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_grpc_loc_conf_t, upstream.ssl_certificate_key),\n      NULL },\n\n    { ngx_string(\"grpc_ssl_password_file\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_http_grpc_ssl_password_file,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"grpc_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_grpc_loc_conf_t, ssl_conf_commands),\n      &ngx_http_grpc_ssl_conf_command_post },\n\n#if (T_NGX_SSL_NTLS)\n    { ngx_string(\"grpc_enable_ntls\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_http_set_complex_value_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_grpc_loc_conf_t, upstream.enable_ntls),\n      NULL },\n\n    { ngx_string(\"grpc_ssl_enc_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_grpc_loc_conf_t, upstream.enc_certificate),\n      NULL },\n\n    { ngx_string(\"grpc_ssl_enc_certificate_key\"),\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_grpc_loc_conf_t, upstream.enc_certificate_key),\n      NULL },\n\n    { ngx_string(\"grpc_ssl_sign_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_grpc_loc_conf_t, upstream.sign_certificate),\n      NULL },\n\n    { ngx_string(\"grpc_ssl_sign_certificate_key\"),\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_grpc_loc_conf_t, upstream.sign_certificate_key),\n      NULL },\n\n#endif\n#endif\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_grpc_module_ctx = {\n    ngx_http_grpc_add_variables,           /* preconfiguration */\n    NULL,                                  /* 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    ngx_http_grpc_create_loc_conf,         /* create location configuration */\n    ngx_http_grpc_merge_loc_conf           /* merge location configuration */\n};\n\n\nngx_module_t  ngx_http_grpc_module = {\n    NGX_MODULE_V1,\n    &ngx_http_grpc_module_ctx,             /* module context */\n    ngx_http_grpc_commands,                /* 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 u_char  ngx_http_grpc_connection_start[] =\n    \"PRI * HTTP/2.0\\r\\n\\r\\nSM\\r\\n\\r\\n\"         /* connection preface */\n\n    \"\\x00\\x00\\x12\\x04\\x00\\x00\\x00\\x00\\x00\"     /* settings frame */\n    \"\\x00\\x01\\x00\\x00\\x00\\x00\"                 /* header table size */\n    \"\\x00\\x02\\x00\\x00\\x00\\x00\"                 /* disable push */\n    \"\\x00\\x04\\x7f\\xff\\xff\\xff\"                 /* initial window */\n\n    \"\\x00\\x00\\x04\\x08\\x00\\x00\\x00\\x00\\x00\"     /* window update frame */\n    \"\\x7f\\xff\\x00\\x00\";\n\n\nstatic ngx_keyval_t  ngx_http_grpc_headers[] = {\n    { ngx_string(\"Content-Length\"), ngx_string(\"$content_length\") },\n    { ngx_string(\"TE\"), ngx_string(\"$grpc_internal_trailers\") },\n    { ngx_string(\"Host\"), ngx_string(\"\") },\n    { ngx_string(\"Connection\"), ngx_string(\"\") },\n    { ngx_string(\"Transfer-Encoding\"), ngx_string(\"\") },\n    { ngx_string(\"Keep-Alive\"), ngx_string(\"\") },\n    { ngx_string(\"Expect\"), ngx_string(\"\") },\n    { ngx_string(\"Upgrade\"), ngx_string(\"\") },\n    { ngx_null_string, ngx_null_string }\n};\n\n\nstatic ngx_str_t  ngx_http_grpc_hide_headers[] = {\n    ngx_string(\"Date\"),\n    ngx_string(\"Server\"),\n    ngx_string(\"X-Accel-Expires\"),\n    ngx_string(\"X-Accel-Redirect\"),\n    ngx_string(\"X-Accel-Limit-Rate\"),\n    ngx_string(\"X-Accel-Buffering\"),\n    ngx_string(\"X-Accel-Charset\"),\n    ngx_null_string\n};\n\n\nstatic ngx_http_variable_t  ngx_http_grpc_vars[] = {\n\n    { ngx_string(\"grpc_internal_trailers\"), NULL,\n      ngx_http_grpc_internal_trailers_variable, 0,\n      NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },\n\n      ngx_http_null_variable\n};\n\n\nstatic ngx_int_t\nngx_http_grpc_handler(ngx_http_request_t *r)\n{\n    ngx_int_t                  rc;\n    ngx_http_upstream_t       *u;\n    ngx_http_grpc_ctx_t       *ctx;\n    ngx_http_grpc_loc_conf_t  *glcf;\n\n    if (ngx_http_upstream_create(r) != NGX_OK) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_grpc_ctx_t));\n    if (ctx == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    ctx->request = r;\n\n    ngx_http_set_ctx(r, ctx, ngx_http_grpc_module);\n\n    glcf = ngx_http_get_module_loc_conf(r, ngx_http_grpc_module);\n\n    u = r->upstream;\n\n    if (glcf->grpc_lengths == NULL) {\n        ctx->host = glcf->host;\n\n#if (NGX_HTTP_SSL)\n        u->ssl = glcf->ssl;\n\n        if (u->ssl) {\n            ngx_str_set(&u->schema, \"grpcs://\");\n\n        } else {\n            ngx_str_set(&u->schema, \"grpc://\");\n        }\n#else\n        ngx_str_set(&u->schema, \"grpc://\");\n#endif\n\n    } else {\n        if (ngx_http_grpc_eval(r, ctx, glcf) != NGX_OK) {\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n    }\n\n    u->output.tag = (ngx_buf_tag_t) &ngx_http_grpc_module;\n\n    u->conf = &glcf->upstream;\n\n    u->create_request = ngx_http_grpc_create_request;\n    u->reinit_request = ngx_http_grpc_reinit_request;\n    u->process_header = ngx_http_grpc_process_header;\n    u->abort_request = ngx_http_grpc_abort_request;\n    u->finalize_request = ngx_http_grpc_finalize_request;\n\n    u->input_filter_init = ngx_http_grpc_filter_init;\n    u->input_filter = ngx_http_grpc_filter;\n    u->input_filter_ctx = ctx;\n\n    r->request_body_no_buffering = 1;\n\n    rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init);\n\n    if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {\n        return rc;\n    }\n\n    return NGX_DONE;\n}\n\n\nstatic ngx_int_t\nngx_http_grpc_eval(ngx_http_request_t *r, ngx_http_grpc_ctx_t *ctx,\n    ngx_http_grpc_loc_conf_t *glcf)\n{\n    size_t                add;\n    ngx_url_t             url;\n    ngx_http_upstream_t  *u;\n\n    ngx_memzero(&url, sizeof(ngx_url_t));\n\n    if (ngx_http_script_run(r, &url.url, glcf->grpc_lengths->elts, 0,\n                            glcf->grpc_values->elts)\n        == NULL)\n    {\n        return NGX_ERROR;\n    }\n\n    if (url.url.len > 7\n        && ngx_strncasecmp(url.url.data, (u_char *) \"grpc://\", 7) == 0)\n    {\n        add = 7;\n\n    } else if (url.url.len > 8\n               && ngx_strncasecmp(url.url.data, (u_char *) \"grpcs://\", 8) == 0)\n    {\n\n#if (NGX_HTTP_SSL)\n        add = 8;\n        r->upstream->ssl = 1;\n#else\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"grpcs protocol requires SSL support\");\n        return NGX_ERROR;\n#endif\n\n    } else {\n        add = 0;\n    }\n\n    u = r->upstream;\n\n    if (add) {\n        u->schema.len = add;\n        u->schema.data = url.url.data;\n\n        url.url.data += add;\n        url.url.len -= add;\n\n    } else {\n        ngx_str_set(&u->schema, \"grpc://\");\n    }\n\n    url.no_resolve = 1;\n\n    if (ngx_parse_url(r->pool, &url) != NGX_OK) {\n        if (url.err) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"%s in upstream \\\"%V\\\"\", url.err, &url.url);\n        }\n\n        return NGX_ERROR;\n    }\n\n    u->resolved = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_resolved_t));\n    if (u->resolved == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (url.addrs) {\n        u->resolved->sockaddr = url.addrs[0].sockaddr;\n        u->resolved->socklen = url.addrs[0].socklen;\n        u->resolved->name = url.addrs[0].name;\n        u->resolved->naddrs = 1;\n    }\n\n    u->resolved->host = url.host;\n    u->resolved->port = url.port;\n    u->resolved->no_port = url.no_port;\n\n    if (url.family != AF_UNIX) {\n\n        if (url.no_port) {\n            ctx->host = url.host;\n\n        } else {\n            ctx->host.len = url.host.len + 1 + url.port_text.len;\n            ctx->host.data = url.host.data;\n        }\n\n    } else {\n        ngx_str_set(&ctx->host, \"localhost\");\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_grpc_create_request(ngx_http_request_t *r)\n{\n    u_char                       *p, *tmp, *key_tmp, *val_tmp, *headers_frame;\n    size_t                        len, tmp_len, key_len, val_len, uri_len;\n    uintptr_t                     escape;\n    ngx_buf_t                    *b;\n    ngx_uint_t                    i, next;\n    ngx_chain_t                  *cl, *body;\n    ngx_list_part_t              *part;\n    ngx_table_elt_t              *header;\n    ngx_http_grpc_ctx_t          *ctx;\n    ngx_http_upstream_t          *u;\n    ngx_http_grpc_frame_t        *f;\n    ngx_http_script_code_pt       code;\n    ngx_http_grpc_loc_conf_t     *glcf;\n    ngx_http_script_engine_t      e, le;\n    ngx_http_script_len_code_pt   lcode;\n\n    u = r->upstream;\n\n    glcf = ngx_http_get_module_loc_conf(r, ngx_http_grpc_module);\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_grpc_module);\n\n    len = sizeof(ngx_http_grpc_connection_start) - 1\n          + sizeof(ngx_http_grpc_frame_t);             /* headers frame */\n\n    /* :method header */\n\n    if (r->method == NGX_HTTP_GET || r->method == NGX_HTTP_POST) {\n        len += 1;\n        tmp_len = 0;\n\n    } else {\n        len += 1 + NGX_HTTP_V2_INT_OCTETS + r->method_name.len;\n        tmp_len = r->method_name.len;\n    }\n\n    /* :scheme header */\n\n    len += 1;\n\n    /* :path header */\n\n    if (r->valid_unparsed_uri) {\n        escape = 0;\n        uri_len = r->unparsed_uri.len;\n\n    } else {\n        escape = 2 * ngx_escape_uri(NULL, r->uri.data, r->uri.len,\n                                    NGX_ESCAPE_URI);\n        uri_len = r->uri.len + escape + sizeof(\"?\") - 1 + r->args.len;\n    }\n\n    len += 1 + NGX_HTTP_V2_INT_OCTETS + uri_len;\n\n    if (tmp_len < uri_len) {\n        tmp_len = uri_len;\n    }\n\n    /* :authority header */\n\n    if (!glcf->host_set) {\n        len += 1 + NGX_HTTP_V2_INT_OCTETS + ctx->host.len;\n\n        if (tmp_len < ctx->host.len) {\n            tmp_len = ctx->host.len;\n        }\n    }\n\n    /* other headers */\n\n    ngx_http_script_flush_no_cacheable_variables(r, glcf->headers.flushes);\n    ngx_memzero(&le, sizeof(ngx_http_script_engine_t));\n\n    le.ip = glcf->headers.lengths->elts;\n    le.request = r;\n    le.flushed = 1;\n\n    while (*(uintptr_t *) le.ip) {\n\n        lcode = *(ngx_http_script_len_code_pt *) le.ip;\n        key_len = lcode(&le);\n\n        for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) {\n            lcode = *(ngx_http_script_len_code_pt *) le.ip;\n        }\n        le.ip += sizeof(uintptr_t);\n\n        if (val_len == 0) {\n            continue;\n        }\n\n        len += 1 + NGX_HTTP_V2_INT_OCTETS + key_len\n                 + NGX_HTTP_V2_INT_OCTETS + val_len;\n\n        if (tmp_len < key_len) {\n            tmp_len = key_len;\n        }\n\n        if (tmp_len < val_len) {\n            tmp_len = val_len;\n        }\n    }\n\n    if (glcf->upstream.pass_request_headers) {\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_hash_find(&glcf->headers.hash, header[i].hash,\n                              header[i].lowcase_key, header[i].key.len))\n            {\n                continue;\n            }\n\n            len += 1 + NGX_HTTP_V2_INT_OCTETS + header[i].key.len\n                     + NGX_HTTP_V2_INT_OCTETS + header[i].value.len;\n\n            if (tmp_len < header[i].key.len) {\n                tmp_len = header[i].key.len;\n            }\n\n            if (tmp_len < header[i].value.len) {\n                tmp_len = header[i].value.len;\n            }\n        }\n    }\n\n    /* continuation frames */\n\n    len += sizeof(ngx_http_grpc_frame_t)\n           * (len / NGX_HTTP_V2_DEFAULT_FRAME_SIZE);\n\n\n    b = ngx_create_temp_buf(r->pool, len);\n    if (b == NULL) {\n        return NGX_ERROR;\n    }\n\n    cl = ngx_alloc_chain_link(r->pool);\n    if (cl == NULL) {\n        return NGX_ERROR;\n    }\n\n    cl->buf = b;\n    cl->next = NULL;\n\n    tmp = ngx_palloc(r->pool, tmp_len * 3);\n    if (tmp == NULL) {\n        return NGX_ERROR;\n    }\n\n    key_tmp = tmp + tmp_len;\n    val_tmp = tmp + 2 * tmp_len;\n\n    /* connection preface */\n\n    b->last = ngx_copy(b->last, ngx_http_grpc_connection_start,\n                       sizeof(ngx_http_grpc_connection_start) - 1);\n\n    /* headers frame */\n\n    headers_frame = b->last;\n\n    f = (ngx_http_grpc_frame_t *) b->last;\n    b->last += sizeof(ngx_http_grpc_frame_t);\n\n    f->length_0 = 0;\n    f->length_1 = 0;\n    f->length_2 = 0;\n    f->type = NGX_HTTP_V2_HEADERS_FRAME;\n    f->flags = 0;\n    f->stream_id_0 = 0;\n    f->stream_id_1 = 0;\n    f->stream_id_2 = 0;\n    f->stream_id_3 = 1;\n\n    if (r->method == NGX_HTTP_GET) {\n        *b->last++ = ngx_http_v2_indexed(NGX_HTTP_V2_METHOD_GET_INDEX);\n\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"grpc header: \\\":method: GET\\\"\");\n\n    } else if (r->method == NGX_HTTP_POST) {\n        *b->last++ = ngx_http_v2_indexed(NGX_HTTP_V2_METHOD_POST_INDEX);\n\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"grpc header: \\\":method: POST\\\"\");\n\n    } else {\n        *b->last++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_METHOD_INDEX);\n        b->last = ngx_http_v2_write_value(b->last, r->method_name.data,\n                                          r->method_name.len, tmp);\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"grpc header: \\\":method: %V\\\"\", &r->method_name);\n    }\n\n#if (NGX_HTTP_SSL)\n    if (u->ssl) {\n        *b->last++ = ngx_http_v2_indexed(NGX_HTTP_V2_SCHEME_HTTPS_INDEX);\n\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"grpc header: \\\":scheme: https\\\"\");\n    } else\n#endif\n    {\n        *b->last++ = ngx_http_v2_indexed(NGX_HTTP_V2_SCHEME_HTTP_INDEX);\n\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"grpc header: \\\":scheme: http\\\"\");\n    }\n\n    if (r->valid_unparsed_uri) {\n\n        if (r->unparsed_uri.len == 1 && r->unparsed_uri.data[0] == '/') {\n            *b->last++ = ngx_http_v2_indexed(NGX_HTTP_V2_PATH_ROOT_INDEX);\n\n        } else {\n            *b->last++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_PATH_INDEX);\n            b->last = ngx_http_v2_write_value(b->last, r->unparsed_uri.data,\n                                              r->unparsed_uri.len, tmp);\n        }\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"grpc header: \\\":path: %V\\\"\", &r->unparsed_uri);\n\n    } else if (escape || r->args.len > 0) {\n        p = val_tmp;\n\n        if (escape) {\n            p = (u_char *) ngx_escape_uri(p, r->uri.data, r->uri.len,\n                                          NGX_ESCAPE_URI);\n\n        } else {\n            p = ngx_copy(p, r->uri.data, r->uri.len);\n        }\n\n        if (r->args.len > 0) {\n            *p++ = '?';\n            p = ngx_copy(p, r->args.data, r->args.len);\n        }\n\n        *b->last++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_PATH_INDEX);\n        b->last = ngx_http_v2_write_value(b->last, val_tmp, p - val_tmp, tmp);\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"grpc header: \\\":path: %*s\\\"\", p - val_tmp, val_tmp);\n\n    } else {\n        *b->last++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_PATH_INDEX);\n        b->last = ngx_http_v2_write_value(b->last, r->uri.data,\n                                          r->uri.len, tmp);\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"grpc header: \\\":path: %V\\\"\", &r->uri);\n    }\n\n    if (!glcf->host_set) {\n        *b->last++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_AUTHORITY_INDEX);\n        b->last = ngx_http_v2_write_value(b->last, ctx->host.data,\n                                          ctx->host.len, tmp);\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"grpc header: \\\":authority: %V\\\"\", &ctx->host);\n    }\n\n    ngx_memzero(&e, sizeof(ngx_http_script_engine_t));\n\n    e.ip = glcf->headers.values->elts;\n    e.request = r;\n    e.flushed = 1;\n\n    le.ip = glcf->headers.lengths->elts;\n\n    while (*(uintptr_t *) le.ip) {\n\n        lcode = *(ngx_http_script_len_code_pt *) le.ip;\n        key_len = lcode(&le);\n\n        for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) {\n            lcode = *(ngx_http_script_len_code_pt *) le.ip;\n        }\n        le.ip += sizeof(uintptr_t);\n\n        if (val_len == 0) {\n            e.skip = 1;\n\n            while (*(uintptr_t *) e.ip) {\n                code = *(ngx_http_script_code_pt *) e.ip;\n                code((ngx_http_script_engine_t *) &e);\n            }\n            e.ip += sizeof(uintptr_t);\n\n            e.skip = 0;\n\n            continue;\n        }\n\n        *b->last++ = 0;\n\n        e.pos = key_tmp;\n\n        code = *(ngx_http_script_code_pt *) e.ip;\n        code((ngx_http_script_engine_t *) &e);\n\n        b->last = ngx_http_v2_write_name(b->last, key_tmp, key_len, tmp);\n\n        e.pos = val_tmp;\n\n        while (*(uintptr_t *) e.ip) {\n            code = *(ngx_http_script_code_pt *) e.ip;\n            code((ngx_http_script_engine_t *) &e);\n        }\n        e.ip += sizeof(uintptr_t);\n\n        b->last = ngx_http_v2_write_value(b->last, val_tmp, val_len, tmp);\n\n#if (NGX_DEBUG)\n        if (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP) {\n            ngx_strlow(key_tmp, key_tmp, key_len);\n\n            ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"grpc header: \\\"%*s: %*s\\\"\",\n                           key_len, key_tmp, val_len, val_tmp);\n        }\n#endif\n    }\n\n    if (glcf->upstream.pass_request_headers) {\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_hash_find(&glcf->headers.hash, header[i].hash,\n                              header[i].lowcase_key, header[i].key.len))\n            {\n                continue;\n            }\n\n            *b->last++ = 0;\n\n            b->last = ngx_http_v2_write_name(b->last, header[i].key.data,\n                                             header[i].key.len, tmp);\n\n            b->last = ngx_http_v2_write_value(b->last, header[i].value.data,\n                                              header[i].value.len, tmp);\n\n#if (NGX_DEBUG)\n            if (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP) {\n                ngx_strlow(tmp, header[i].key.data, header[i].key.len);\n\n                ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                               \"grpc header: \\\"%*s: %V\\\"\",\n                               header[i].key.len, tmp, &header[i].value);\n            }\n#endif\n        }\n    }\n\n    /* update headers frame length */\n\n    len = b->last - headers_frame - sizeof(ngx_http_grpc_frame_t);\n\n    if (len > NGX_HTTP_V2_DEFAULT_FRAME_SIZE) {\n        len = NGX_HTTP_V2_DEFAULT_FRAME_SIZE;\n        next = 1;\n\n    } else {\n        next = 0;\n    }\n\n    f = (ngx_http_grpc_frame_t *) headers_frame;\n\n    f->length_0 = (u_char) ((len >> 16) & 0xff);\n    f->length_1 = (u_char) ((len >> 8) & 0xff);\n    f->length_2 = (u_char) (len & 0xff);\n\n    /* create additional continuation frames */\n\n    p = headers_frame;\n\n    while (next) {\n        p += sizeof(ngx_http_grpc_frame_t) + NGX_HTTP_V2_DEFAULT_FRAME_SIZE;\n        len = b->last - p;\n\n        ngx_memmove(p + sizeof(ngx_http_grpc_frame_t), p, len);\n        b->last += sizeof(ngx_http_grpc_frame_t);\n\n        if (len > NGX_HTTP_V2_DEFAULT_FRAME_SIZE) {\n            len = NGX_HTTP_V2_DEFAULT_FRAME_SIZE;\n            next = 1;\n\n        } else {\n            next = 0;\n        }\n\n        f = (ngx_http_grpc_frame_t *) p;\n\n        f->length_0 = (u_char) ((len >> 16) & 0xff);\n        f->length_1 = (u_char) ((len >> 8) & 0xff);\n        f->length_2 = (u_char) (len & 0xff);\n        f->type = NGX_HTTP_V2_CONTINUATION_FRAME;\n        f->flags = 0;\n        f->stream_id_0 = 0;\n        f->stream_id_1 = 0;\n        f->stream_id_2 = 0;\n        f->stream_id_3 = 1;\n    }\n\n    f->flags |= NGX_HTTP_V2_END_HEADERS_FLAG;\n\n    ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"grpc header: %*xs%s, len: %uz\",\n                   (size_t) ngx_min(b->last - b->pos, 256), b->pos,\n                   b->last - b->pos > 256 ? \"...\" : \"\",\n                   b->last - b->pos);\n\n    if (r->request_body_no_buffering) {\n\n        u->request_bufs = cl;\n\n    } else {\n\n        body = u->request_bufs;\n        u->request_bufs = cl;\n\n        if (body == NULL) {\n            f = (ngx_http_grpc_frame_t *) headers_frame;\n            f->flags |= NGX_HTTP_V2_END_STREAM_FLAG;\n        }\n\n        while (body) {\n            b = ngx_alloc_buf(r->pool);\n            if (b == NULL) {\n                return NGX_ERROR;\n            }\n\n            ngx_memcpy(b, body->buf, sizeof(ngx_buf_t));\n\n            cl->next = ngx_alloc_chain_link(r->pool);\n            if (cl->next == NULL) {\n                return NGX_ERROR;\n            }\n\n            cl = cl->next;\n            cl->buf = b;\n\n            body = body->next;\n        }\n\n        b->last_buf = 1;\n    }\n\n    u->output.output_filter = ngx_http_grpc_body_output_filter;\n    u->output.filter_ctx = r;\n\n    b->flush = 1;\n    cl->next = NULL;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_grpc_reinit_request(ngx_http_request_t *r)\n{\n    ngx_http_grpc_ctx_t  *ctx;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_grpc_module);\n\n    if (ctx == NULL) {\n        return NGX_OK;\n    }\n\n    ctx->state = 0;\n    ctx->header_sent = 0;\n    ctx->output_closed = 0;\n    ctx->output_blocked = 0;\n    ctx->parsing_headers = 0;\n    ctx->end_stream = 0;\n    ctx->done = 0;\n    ctx->status = 0;\n    ctx->rst = 0;\n    ctx->goaway = 0;\n    ctx->connection = NULL;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_grpc_body_output_filter(void *data, ngx_chain_t *in)\n{\n    ngx_http_request_t  *r = data;\n\n    off_t                   file_pos;\n    u_char                 *p, *pos, *start;\n    size_t                  len, limit;\n    ngx_buf_t              *b;\n    ngx_int_t               rc;\n    ngx_uint_t              next, last;\n    ngx_chain_t            *cl, *out, **ll;\n    ngx_http_upstream_t    *u;\n    ngx_http_grpc_ctx_t    *ctx;\n    ngx_http_grpc_frame_t  *f;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"grpc output filter\");\n\n    ctx = ngx_http_grpc_get_ctx(r);\n\n    if (ctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (in) {\n        if (ngx_chain_add_copy(r->pool, &ctx->in, in) != NGX_OK) {\n            return NGX_ERROR;\n        }\n    }\n\n    out = NULL;\n    ll = &out;\n\n    if (!ctx->header_sent) {\n        /* first buffer contains headers */\n\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"grpc output header\");\n\n        ctx->header_sent = 1;\n\n        if (ctx->id != 1) {\n            /*\n             * keepalive connection: skip connection preface,\n             * update stream identifiers\n             */\n\n            b = ctx->in->buf;\n            b->pos += sizeof(ngx_http_grpc_connection_start) - 1;\n\n            p = b->pos;\n\n            while (p < b->last) {\n                f = (ngx_http_grpc_frame_t *) p;\n                p += sizeof(ngx_http_grpc_frame_t);\n\n                f->stream_id_0 = (u_char) ((ctx->id >> 24) & 0xff);\n                f->stream_id_1 = (u_char) ((ctx->id >> 16) & 0xff);\n                f->stream_id_2 = (u_char) ((ctx->id >> 8) & 0xff);\n                f->stream_id_3 = (u_char) (ctx->id & 0xff);\n\n                p += (f->length_0 << 16) + (f->length_1 << 8) + f->length_2;\n            }\n        }\n\n        if (ctx->in->buf->last_buf) {\n            ctx->output_closed = 1;\n        }\n\n        *ll = ctx->in;\n        ll = &ctx->in->next;\n\n        ctx->in = ctx->in->next;\n    }\n\n    if (ctx->out) {\n        /* queued control frames */\n\n        *ll = ctx->out;\n\n        for (cl = ctx->out, ll = &cl->next; cl; cl = cl->next) {\n            ll = &cl->next;\n        }\n\n        ctx->out = NULL;\n    }\n\n    f = NULL;\n    last = 0;\n\n    limit = ngx_max(0, ctx->send_window);\n\n    if (limit > ctx->connection->send_window) {\n        limit = ctx->connection->send_window;\n    }\n\n    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"grpc output limit: %uz w:%z:%uz\",\n                   limit, ctx->send_window, ctx->connection->send_window);\n\n#if (NGX_SUPPRESS_WARN)\n    file_pos = 0;\n    pos = NULL;\n    cl = NULL;\n#endif\n\n    in = ctx->in;\n\n    while (in && limit > 0) {\n\n        ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,\n                       \"grpc output in  l:%d f:%d %p, pos %p, size: %z \"\n                       \"file: %O, size: %O\",\n                       in->buf->last_buf,\n                       in->buf->in_file,\n                       in->buf->start, in->buf->pos,\n                       in->buf->last - in->buf->pos,\n                       in->buf->file_pos,\n                       in->buf->file_last - in->buf->file_pos);\n\n        if (ngx_buf_special(in->buf)) {\n            goto next;\n        }\n\n        if (in->buf->in_file) {\n            file_pos = in->buf->file_pos;\n\n        } else {\n            pos = in->buf->pos;\n        }\n\n        next = 0;\n\n        do {\n\n            cl = ngx_http_grpc_get_buf(r, ctx);\n            if (cl == NULL) {\n                return NGX_ERROR;\n            }\n\n            b = cl->buf;\n\n            f = (ngx_http_grpc_frame_t *) b->last;\n            b->last += sizeof(ngx_http_grpc_frame_t);\n\n            *ll = cl;\n            ll = &cl->next;\n\n            cl = ngx_chain_get_free_buf(r->pool, &ctx->free);\n            if (cl == NULL) {\n                return NGX_ERROR;\n            }\n\n            b = cl->buf;\n            start = b->start;\n\n            ngx_memcpy(b, in->buf, sizeof(ngx_buf_t));\n\n            /*\n             * restore b->start to preserve memory allocated in the buffer,\n             * to reuse it later for headers and control frames\n             */\n\n            b->start = start;\n\n            if (in->buf->in_file) {\n                b->file_pos = file_pos;\n                file_pos += ngx_min(NGX_HTTP_V2_DEFAULT_FRAME_SIZE, limit);\n\n                if (file_pos >= in->buf->file_last) {\n                    file_pos = in->buf->file_last;\n                    next = 1;\n                }\n\n                b->file_last = file_pos;\n                len = (ngx_uint_t) (file_pos - b->file_pos);\n\n            } else {\n                b->pos = pos;\n                pos += ngx_min(NGX_HTTP_V2_DEFAULT_FRAME_SIZE, limit);\n\n                if (pos >= in->buf->last) {\n                    pos = in->buf->last;\n                    next = 1;\n                }\n\n                b->last = pos;\n                len = (ngx_uint_t) (pos - b->pos);\n            }\n\n            b->tag = (ngx_buf_tag_t) &ngx_http_grpc_body_output_filter;\n            b->shadow = in->buf;\n            b->last_shadow = next;\n\n            b->last_buf = 0;\n            b->last_in_chain = 0;\n\n            *ll = cl;\n            ll = &cl->next;\n\n            f->length_0 = (u_char) ((len >> 16) & 0xff);\n            f->length_1 = (u_char) ((len >> 8) & 0xff);\n            f->length_2 = (u_char) (len & 0xff);\n            f->type = NGX_HTTP_V2_DATA_FRAME;\n            f->flags = 0;\n            f->stream_id_0 = (u_char) ((ctx->id >> 24) & 0xff);\n            f->stream_id_1 = (u_char) ((ctx->id >> 16) & 0xff);\n            f->stream_id_2 = (u_char) ((ctx->id >> 8) & 0xff);\n            f->stream_id_3 = (u_char) (ctx->id & 0xff);\n\n            limit -= len;\n            ctx->send_window -= len;\n            ctx->connection->send_window -= len;\n\n        } while (!next && limit > 0);\n\n        if (!next) {\n            /*\n             * if the buffer wasn't fully sent due to flow control limits,\n             * preserve position for future use\n             */\n\n            if (in->buf->in_file) {\n                in->buf->file_pos = file_pos;\n\n            } else {\n                in->buf->pos = pos;\n            }\n\n            break;\n        }\n\n    next:\n\n        if (in->buf->last_buf) {\n            last = 1;\n        }\n\n        in = in->next;\n    }\n\n    ctx->in = in;\n\n    if (last) {\n\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"grpc output last\");\n\n        ctx->output_closed = 1;\n\n        if (f) {\n            f->flags |= NGX_HTTP_V2_END_STREAM_FLAG;\n\n        } else {\n            cl = ngx_http_grpc_get_buf(r, ctx);\n            if (cl == NULL) {\n                return NGX_ERROR;\n            }\n\n            b = cl->buf;\n\n            f = (ngx_http_grpc_frame_t *) b->last;\n            b->last += sizeof(ngx_http_grpc_frame_t);\n\n            f->length_0 = 0;\n            f->length_1 = 0;\n            f->length_2 = 0;\n            f->type = NGX_HTTP_V2_DATA_FRAME;\n            f->flags = NGX_HTTP_V2_END_STREAM_FLAG;\n            f->stream_id_0 = (u_char) ((ctx->id >> 24) & 0xff);\n            f->stream_id_1 = (u_char) ((ctx->id >> 16) & 0xff);\n            f->stream_id_2 = (u_char) ((ctx->id >> 8) & 0xff);\n            f->stream_id_3 = (u_char) (ctx->id & 0xff);\n\n            *ll = cl;\n            ll = &cl->next;\n        }\n\n        cl->buf->last_buf = 1;\n    }\n\n    *ll = NULL;\n\n#if (NGX_DEBUG)\n\n    for (cl = out; cl; cl = cl->next) {\n        ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,\n                       \"grpc output out l:%d f:%d %p, pos %p, size: %z \"\n                       \"file: %O, size: %O\",\n                       cl->buf->last_buf,\n                       cl->buf->in_file,\n                       cl->buf->start, cl->buf->pos,\n                       cl->buf->last - cl->buf->pos,\n                       cl->buf->file_pos,\n                       cl->buf->file_last - cl->buf->file_pos);\n    }\n\n    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"grpc output limit: %uz w:%z:%uz\",\n                   limit, ctx->send_window, ctx->connection->send_window);\n\n#endif\n\n    rc = ngx_chain_writer(&r->upstream->writer, out);\n\n    ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &out,\n                            (ngx_buf_tag_t) &ngx_http_grpc_body_output_filter);\n\n    for (cl = ctx->free; cl; cl = cl->next) {\n\n        /* mark original buffers as sent */\n\n        if (cl->buf->shadow) {\n            if (cl->buf->last_shadow) {\n                b = cl->buf->shadow;\n                b->pos = b->last;\n            }\n\n            cl->buf->shadow = NULL;\n        }\n    }\n\n    if (rc == NGX_OK && ctx->in) {\n        rc = NGX_AGAIN;\n    }\n\n    if (rc == NGX_AGAIN) {\n        ctx->output_blocked = 1;\n\n    } else {\n        ctx->output_blocked = 0;\n    }\n\n    if (ctx->done) {\n\n        /*\n         * We have already got the response and were sending some additional\n         * control frames.  Even if there is still something unsent, stop\n         * here anyway.\n         */\n\n        u = r->upstream;\n        u->length = 0;\n\n        if (ctx->in == NULL\n            && ctx->out == NULL\n            && ctx->output_closed\n            && !ctx->output_blocked\n            && !ctx->goaway\n            && ctx->state == ngx_http_grpc_st_start)\n        {\n            u->keepalive = 1;\n        }\n\n        ngx_post_event(u->peer.connection->read, &ngx_posted_events);\n    }\n\n    return rc;\n}\n\n\nstatic ngx_int_t\nngx_http_grpc_process_header(ngx_http_request_t *r)\n{\n    ngx_str_t                      *status_line;\n    ngx_int_t                       rc, status;\n    ngx_buf_t                      *b;\n    ngx_table_elt_t                *h;\n    ngx_http_upstream_t            *u;\n    ngx_http_grpc_ctx_t            *ctx;\n    ngx_http_upstream_header_t     *hh;\n    ngx_http_upstream_main_conf_t  *umcf;\n\n    u = r->upstream;\n    b = &u->buffer;\n\n    ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"grpc response: %*xs%s, len: %uz\",\n                   (size_t) ngx_min(b->last - b->pos, 256),\n                   b->pos, b->last - b->pos > 256 ? \"...\" : \"\",\n                   b->last - b->pos);\n\n    ctx = ngx_http_grpc_get_ctx(r);\n\n    if (ctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);\n\n    for ( ;; ) {\n\n        if (ctx->state < ngx_http_grpc_st_payload) {\n\n            rc = ngx_http_grpc_parse_frame(r, ctx, b);\n\n            if (rc == NGX_AGAIN) {\n\n                /*\n                 * there can be a lot of window update frames,\n                 * so we reset buffer if it is empty and we haven't\n                 * started parsing headers yet\n                 */\n\n                if (!ctx->parsing_headers) {\n                    b->pos = b->start;\n                    b->last = b->pos;\n                }\n\n                return NGX_AGAIN;\n            }\n\n            if (rc == NGX_ERROR) {\n                return NGX_HTTP_UPSTREAM_INVALID_HEADER;\n            }\n\n            /*\n             * RFC 7540 says that implementations MUST discard frames\n             * that have unknown or unsupported types.  However, extension\n             * frames that appear in the middle of a header block are\n             * not permitted.  Also, for obvious reasons CONTINUATION frames\n             * cannot appear before headers, and DATA frames are not expected\n             * to appear before all headers are parsed.\n             */\n\n            if (ctx->type == NGX_HTTP_V2_DATA_FRAME\n                || (ctx->type == NGX_HTTP_V2_CONTINUATION_FRAME\n                    && !ctx->parsing_headers)\n                || (ctx->type != NGX_HTTP_V2_CONTINUATION_FRAME\n                    && ctx->parsing_headers))\n            {\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                              \"upstream sent unexpected http2 frame: %d\",\n                              ctx->type);\n                return NGX_HTTP_UPSTREAM_INVALID_HEADER;\n            }\n\n            if (ctx->stream_id && ctx->stream_id != ctx->id) {\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                              \"upstream sent frame for unknown stream %ui\",\n                              ctx->stream_id);\n                return NGX_HTTP_UPSTREAM_INVALID_HEADER;\n            }\n        }\n\n        /* frame payload */\n\n        if (ctx->type == NGX_HTTP_V2_RST_STREAM_FRAME) {\n\n            rc = ngx_http_grpc_parse_rst_stream(r, ctx, b);\n\n            if (rc == NGX_AGAIN) {\n                return NGX_AGAIN;\n            }\n\n            if (rc == NGX_ERROR) {\n                return NGX_HTTP_UPSTREAM_INVALID_HEADER;\n            }\n\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"upstream rejected request with error %ui\",\n                          ctx->error);\n\n            return NGX_HTTP_UPSTREAM_INVALID_HEADER;\n        }\n\n        if (ctx->type == NGX_HTTP_V2_GOAWAY_FRAME) {\n\n            rc = ngx_http_grpc_parse_goaway(r, ctx, b);\n\n            if (rc == NGX_AGAIN) {\n                return NGX_AGAIN;\n            }\n\n            if (rc == NGX_ERROR) {\n                return NGX_HTTP_UPSTREAM_INVALID_HEADER;\n            }\n\n            /*\n             * If stream_id is lower than one we use, our\n             * request won't be processed and needs to be retried.\n             * If stream_id is greater or equal to the one we use,\n             * we can continue normally (except we can't use this\n             * connection for additional requests).  If there is\n             * a real error, the connection will be closed.\n             */\n\n            if (ctx->stream_id < ctx->id) {\n\n                /* TODO: we can retry non-idempotent requests */\n\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                              \"upstream sent goaway with error %ui\",\n                              ctx->error);\n\n                return NGX_HTTP_UPSTREAM_INVALID_HEADER;\n            }\n\n            ctx->goaway = 1;\n\n            continue;\n        }\n\n        if (ctx->type == NGX_HTTP_V2_WINDOW_UPDATE_FRAME) {\n\n            rc = ngx_http_grpc_parse_window_update(r, ctx, b);\n\n            if (rc == NGX_AGAIN) {\n                return NGX_AGAIN;\n            }\n\n            if (rc == NGX_ERROR) {\n                return NGX_HTTP_UPSTREAM_INVALID_HEADER;\n            }\n\n            if (ctx->in) {\n                ngx_post_event(u->peer.connection->write, &ngx_posted_events);\n            }\n\n            continue;\n        }\n\n        if (ctx->type == NGX_HTTP_V2_SETTINGS_FRAME) {\n\n            rc = ngx_http_grpc_parse_settings(r, ctx, b);\n\n            if (rc == NGX_AGAIN) {\n                return NGX_AGAIN;\n            }\n\n            if (rc == NGX_ERROR) {\n                return NGX_HTTP_UPSTREAM_INVALID_HEADER;\n            }\n\n            if (ctx->in) {\n                ngx_post_event(u->peer.connection->write, &ngx_posted_events);\n            }\n\n            continue;\n        }\n\n        if (ctx->type == NGX_HTTP_V2_PING_FRAME) {\n\n            rc = ngx_http_grpc_parse_ping(r, ctx, b);\n\n            if (rc == NGX_AGAIN) {\n                return NGX_AGAIN;\n            }\n\n            if (rc == NGX_ERROR) {\n                return NGX_HTTP_UPSTREAM_INVALID_HEADER;\n            }\n\n            ngx_post_event(u->peer.connection->write, &ngx_posted_events);\n            continue;\n        }\n\n        if (ctx->type == NGX_HTTP_V2_PUSH_PROMISE_FRAME) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"upstream sent unexpected push promise frame\");\n            return NGX_HTTP_UPSTREAM_INVALID_HEADER;\n        }\n\n        if (ctx->type != NGX_HTTP_V2_HEADERS_FRAME\n            && ctx->type != NGX_HTTP_V2_CONTINUATION_FRAME)\n        {\n            /* priority, unknown frames */\n\n            if (b->last - b->pos < (ssize_t) ctx->rest) {\n                ctx->rest -= b->last - b->pos;\n                b->pos = b->last;\n                return NGX_AGAIN;\n            }\n\n            b->pos += ctx->rest;\n            ctx->rest = 0;\n            ctx->state = ngx_http_grpc_st_start;\n\n            continue;\n        }\n\n        /* headers */\n\n        for ( ;; ) {\n\n            rc = ngx_http_grpc_parse_header(r, ctx, b);\n\n            if (rc == NGX_AGAIN) {\n                break;\n            }\n\n            if (rc == NGX_OK) {\n\n                /* a header line has been parsed successfully */\n\n                ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                               \"grpc header: \\\"%V: %V\\\"\",\n                               &ctx->name, &ctx->value);\n\n                if (ctx->name.len && ctx->name.data[0] == ':') {\n\n                    if (ctx->name.len != sizeof(\":status\") - 1\n                        || ngx_strncmp(ctx->name.data, \":status\",\n                                       sizeof(\":status\") - 1)\n                           != 0)\n                    {\n                        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                                      \"upstream sent invalid header \\\"%V: %V\\\"\",\n                                      &ctx->name, &ctx->value);\n                        return NGX_HTTP_UPSTREAM_INVALID_HEADER;\n                    }\n\n                    if (ctx->status) {\n                        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                                      \"upstream sent duplicate :status header\");\n                        return NGX_HTTP_UPSTREAM_INVALID_HEADER;\n                    }\n\n                    status_line = &ctx->value;\n\n                    if (status_line->len != 3) {\n                        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                                      \"upstream sent invalid :status \\\"%V\\\"\",\n                                      status_line);\n                        return NGX_HTTP_UPSTREAM_INVALID_HEADER;\n                    }\n\n                    status = ngx_atoi(status_line->data, 3);\n\n                    if (status == NGX_ERROR) {\n                        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                                      \"upstream sent invalid :status \\\"%V\\\"\",\n                                      status_line);\n                        return NGX_HTTP_UPSTREAM_INVALID_HEADER;\n                    }\n\n                    if (status < NGX_HTTP_OK) {\n                        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                                      \"upstream sent unexpected :status \\\"%V\\\"\",\n                                      status_line);\n                        return NGX_HTTP_UPSTREAM_INVALID_HEADER;\n                    }\n\n                    u->headers_in.status_n = status;\n\n                    if (u->state && u->state->status == 0) {\n                        u->state->status = status;\n                    }\n\n                    ctx->status = 1;\n\n                    continue;\n\n                } else if (!ctx->status) {\n                    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                                  \"upstream sent no :status header\");\n                    return NGX_HTTP_UPSTREAM_INVALID_HEADER;\n                }\n\n                h = ngx_list_push(&u->headers_in.headers);\n                if (h == NULL) {\n                    return NGX_ERROR;\n                }\n\n                h->key = ctx->name;\n                h->value = ctx->value;\n                h->lowcase_key = h->key.data;\n                h->hash = ngx_hash_key(h->key.data, h->key.len);\n\n                hh = ngx_hash_find(&umcf->headers_in_hash, h->hash,\n                                   h->lowcase_key, h->key.len);\n\n                if (hh) {\n                    rc = hh->handler(r, h, hh->offset);\n\n                    if (rc != NGX_OK) {\n                        return rc;\n                    }\n                }\n\n                continue;\n            }\n\n            if (rc == NGX_HTTP_PARSE_HEADER_DONE) {\n\n                /* a whole header has been parsed successfully */\n\n                ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                               \"grpc header done\");\n\n                if (ctx->end_stream) {\n                    u->headers_in.content_length_n = 0;\n\n                    if (ctx->in == NULL\n                        && ctx->out == NULL\n                        && ctx->output_closed\n                        && !ctx->output_blocked\n                        && !ctx->goaway\n                        && b->last == b->pos)\n                    {\n                        u->keepalive = 1;\n                    }\n                }\n\n                return NGX_OK;\n            }\n\n            /* there was error while a header line parsing */\n\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"upstream sent invalid header\");\n\n            return NGX_HTTP_UPSTREAM_INVALID_HEADER;\n        }\n\n        /* rc == NGX_AGAIN */\n\n        if (ctx->rest == 0) {\n            ctx->state = ngx_http_grpc_st_start;\n            continue;\n        }\n\n        return NGX_AGAIN;\n    }\n}\n\n\nstatic ngx_int_t\nngx_http_grpc_filter_init(void *data)\n{\n    ngx_http_grpc_ctx_t  *ctx = data;\n\n    ngx_http_request_t   *r;\n    ngx_http_upstream_t  *u;\n\n    r = ctx->request;\n    u = r->upstream;\n\n    if (u->headers_in.status_n == NGX_HTTP_NO_CONTENT\n        || u->headers_in.status_n == NGX_HTTP_NOT_MODIFIED\n        || r->method == NGX_HTTP_HEAD)\n    {\n        ctx->length = 0;\n\n    } else {\n        ctx->length = u->headers_in.content_length_n;\n    }\n\n    if (ctx->end_stream) {\n\n        if (ctx->length > 0) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"upstream prematurely closed stream\");\n            return NGX_ERROR;\n        }\n\n        u->length = 0;\n        ctx->done = 1;\n\n    } else {\n        u->length = 1;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_grpc_filter(void *data, ssize_t bytes)\n{\n    ngx_http_grpc_ctx_t  *ctx = data;\n\n    ngx_int_t             rc;\n    ngx_buf_t            *b, *buf;\n    ngx_chain_t          *cl, **ll;\n    ngx_table_elt_t      *h;\n    ngx_http_request_t   *r;\n    ngx_http_upstream_t  *u;\n\n    r = ctx->request;\n    u = r->upstream;\n    b = &u->buffer;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"grpc filter bytes:%z\", bytes);\n\n    b->pos = b->last;\n    b->last += bytes;\n\n    for (cl = u->out_bufs, ll = &u->out_bufs; cl; cl = cl->next) {\n        ll = &cl->next;\n    }\n\n    for ( ;; ) {\n\n        if (ctx->state < ngx_http_grpc_st_payload) {\n\n            rc = ngx_http_grpc_parse_frame(r, ctx, b);\n\n            if (rc == NGX_AGAIN) {\n\n                if (ctx->done) {\n\n                    if (ctx->length > 0) {\n                        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                                      \"upstream prematurely closed stream\");\n                        return NGX_ERROR;\n                    }\n\n                    /*\n                     * We have finished parsing the response and the\n                     * remaining control frames.  If there are unsent\n                     * control frames, post a write event to send them.\n                     */\n\n                    if (ctx->out) {\n                        ngx_post_event(u->peer.connection->write,\n                                       &ngx_posted_events);\n                        return NGX_AGAIN;\n                    }\n\n                    u->length = 0;\n\n                    if (ctx->in == NULL\n                        && ctx->output_closed\n                        && !ctx->output_blocked\n                        && !ctx->goaway\n                        && ctx->state == ngx_http_grpc_st_start)\n                    {\n                        u->keepalive = 1;\n                    }\n\n                    break;\n                }\n\n                return NGX_AGAIN;\n            }\n\n            if (rc == NGX_ERROR) {\n                return NGX_ERROR;\n            }\n\n            if ((ctx->type == NGX_HTTP_V2_CONTINUATION_FRAME\n                 && !ctx->parsing_headers)\n                || (ctx->type != NGX_HTTP_V2_CONTINUATION_FRAME\n                    && ctx->parsing_headers))\n            {\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                              \"upstream sent unexpected http2 frame: %d\",\n                              ctx->type);\n                return NGX_ERROR;\n            }\n\n            if (ctx->type == NGX_HTTP_V2_DATA_FRAME) {\n\n                if (ctx->stream_id != ctx->id) {\n                    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                                  \"upstream sent data frame \"\n                                  \"for unknown stream %ui\",\n                                  ctx->stream_id);\n                    return NGX_ERROR;\n                }\n\n                if (ctx->rest > ctx->recv_window) {\n                    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                                  \"upstream violated stream flow control, \"\n                                  \"received %uz data frame with window %uz\",\n                                  ctx->rest, ctx->recv_window);\n                    return NGX_ERROR;\n                }\n\n                if (ctx->rest > ctx->connection->recv_window) {\n                    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                                  \"upstream violated connection flow control, \"\n                                  \"received %uz data frame with window %uz\",\n                                  ctx->rest, ctx->connection->recv_window);\n                    return NGX_ERROR;\n                }\n\n                ctx->recv_window -= ctx->rest;\n                ctx->connection->recv_window -= ctx->rest;\n\n                if (ctx->connection->recv_window < NGX_HTTP_V2_MAX_WINDOW / 4\n                    || ctx->recv_window < NGX_HTTP_V2_MAX_WINDOW / 4)\n                {\n                    if (ngx_http_grpc_send_window_update(r, ctx) != NGX_OK) {\n                        return NGX_ERROR;\n                    }\n\n                    ngx_post_event(u->peer.connection->write,\n                                   &ngx_posted_events);\n                }\n            }\n\n            if (ctx->stream_id && ctx->stream_id != ctx->id) {\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                              \"upstream sent frame for unknown stream %ui\",\n                              ctx->stream_id);\n                return NGX_ERROR;\n            }\n\n            if (ctx->stream_id && ctx->done\n                && ctx->type != NGX_HTTP_V2_RST_STREAM_FRAME\n                && ctx->type != NGX_HTTP_V2_WINDOW_UPDATE_FRAME)\n            {\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                              \"upstream sent frame for closed stream %ui\",\n                              ctx->stream_id);\n                return NGX_ERROR;\n            }\n\n            ctx->padding = 0;\n        }\n\n        if (ctx->state == ngx_http_grpc_st_padding) {\n\n            if (b->last - b->pos < (ssize_t) ctx->rest) {\n                ctx->rest -= b->last - b->pos;\n                b->pos = b->last;\n                return NGX_AGAIN;\n            }\n\n            b->pos += ctx->rest;\n            ctx->rest = 0;\n            ctx->state = ngx_http_grpc_st_start;\n\n            if (ctx->flags & NGX_HTTP_V2_END_STREAM_FLAG) {\n                ctx->done = 1;\n            }\n\n            continue;\n        }\n\n        /* frame payload */\n\n        if (ctx->type == NGX_HTTP_V2_RST_STREAM_FRAME) {\n\n            rc = ngx_http_grpc_parse_rst_stream(r, ctx, b);\n\n            if (rc == NGX_AGAIN) {\n                return NGX_AGAIN;\n            }\n\n            if (rc == NGX_ERROR) {\n                return NGX_ERROR;\n            }\n\n            if (ctx->error || !ctx->done) {\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                              \"upstream rejected request with error %ui\",\n                              ctx->error);\n                return NGX_ERROR;\n            }\n\n            if (ctx->rst) {\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                              \"upstream sent frame for closed stream %ui\",\n                              ctx->stream_id);\n                return NGX_ERROR;\n            }\n\n            ctx->rst = 1;\n\n            continue;\n        }\n\n        if (ctx->type == NGX_HTTP_V2_GOAWAY_FRAME) {\n\n            rc = ngx_http_grpc_parse_goaway(r, ctx, b);\n\n            if (rc == NGX_AGAIN) {\n                return NGX_AGAIN;\n            }\n\n            if (rc == NGX_ERROR) {\n                return NGX_ERROR;\n            }\n\n            /*\n             * If stream_id is lower than one we use, our\n             * request won't be processed and needs to be retried.\n             * If stream_id is greater or equal to the one we use,\n             * we can continue normally (except we can't use this\n             * connection for additional requests).  If there is\n             * a real error, the connection will be closed.\n             */\n\n            if (ctx->stream_id < ctx->id) {\n\n                /* TODO: we can retry non-idempotent requests */\n\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                              \"upstream sent goaway with error %ui\",\n                              ctx->error);\n\n                return NGX_ERROR;\n            }\n\n            ctx->goaway = 1;\n\n            continue;\n        }\n\n        if (ctx->type == NGX_HTTP_V2_WINDOW_UPDATE_FRAME) {\n\n            rc = ngx_http_grpc_parse_window_update(r, ctx, b);\n\n            if (rc == NGX_AGAIN) {\n                return NGX_AGAIN;\n            }\n\n            if (rc == NGX_ERROR) {\n                return NGX_ERROR;\n            }\n\n            if (ctx->in) {\n                ngx_post_event(u->peer.connection->write, &ngx_posted_events);\n            }\n\n            continue;\n        }\n\n        if (ctx->type == NGX_HTTP_V2_SETTINGS_FRAME) {\n\n            rc = ngx_http_grpc_parse_settings(r, ctx, b);\n\n            if (rc == NGX_AGAIN) {\n                return NGX_AGAIN;\n            }\n\n            if (rc == NGX_ERROR) {\n                return NGX_ERROR;\n            }\n\n            if (ctx->in) {\n                ngx_post_event(u->peer.connection->write, &ngx_posted_events);\n            }\n\n            continue;\n        }\n\n        if (ctx->type == NGX_HTTP_V2_PING_FRAME) {\n\n            rc = ngx_http_grpc_parse_ping(r, ctx, b);\n\n            if (rc == NGX_AGAIN) {\n                return NGX_AGAIN;\n            }\n\n            if (rc == NGX_ERROR) {\n                return NGX_ERROR;\n            }\n\n            ngx_post_event(u->peer.connection->write, &ngx_posted_events);\n            continue;\n        }\n\n        if (ctx->type == NGX_HTTP_V2_PUSH_PROMISE_FRAME) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"upstream sent unexpected push promise frame\");\n            return NGX_ERROR;\n        }\n\n        if (ctx->type == NGX_HTTP_V2_HEADERS_FRAME\n            || ctx->type == NGX_HTTP_V2_CONTINUATION_FRAME)\n        {\n            for ( ;; ) {\n\n                rc = ngx_http_grpc_parse_header(r, ctx, b);\n\n                if (rc == NGX_AGAIN) {\n                    break;\n                }\n\n                if (rc == NGX_OK) {\n\n                    /* a header line has been parsed successfully */\n\n                    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                                   \"grpc trailer: \\\"%V: %V\\\"\",\n                                   &ctx->name, &ctx->value);\n\n                    if (ctx->name.len && ctx->name.data[0] == ':') {\n                        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                                      \"upstream sent invalid \"\n                                      \"trailer \\\"%V: %V\\\"\",\n                                      &ctx->name, &ctx->value);\n                        return NGX_ERROR;\n                    }\n\n                    h = ngx_list_push(&u->headers_in.trailers);\n                    if (h == NULL) {\n                        return NGX_ERROR;\n                    }\n\n                    h->key = ctx->name;\n                    h->value = ctx->value;\n                    h->lowcase_key = h->key.data;\n                    h->hash = ngx_hash_key(h->key.data, h->key.len);\n\n                    continue;\n                }\n\n                if (rc == NGX_HTTP_PARSE_HEADER_DONE) {\n\n                    /* a whole header has been parsed successfully */\n\n                    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                                   \"grpc trailer done\");\n\n                    if (ctx->end_stream) {\n                        ctx->done = 1;\n                        break;\n                    }\n\n                    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                                  \"upstream sent trailer without \"\n                                  \"end stream flag\");\n                    return NGX_ERROR;\n                }\n\n                /* there was error while a header line parsing */\n\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                              \"upstream sent invalid trailer\");\n\n                return NGX_ERROR;\n            }\n\n            if (rc == NGX_HTTP_PARSE_HEADER_DONE) {\n                continue;\n            }\n\n            /* rc == NGX_AGAIN */\n\n            if (ctx->rest == 0) {\n                ctx->state = ngx_http_grpc_st_start;\n                continue;\n            }\n\n            return NGX_AGAIN;\n        }\n\n        if (ctx->type != NGX_HTTP_V2_DATA_FRAME) {\n\n            /* priority, unknown frames */\n\n            if (b->last - b->pos < (ssize_t) ctx->rest) {\n                ctx->rest -= b->last - b->pos;\n                b->pos = b->last;\n                return NGX_AGAIN;\n            }\n\n            b->pos += ctx->rest;\n            ctx->rest = 0;\n            ctx->state = ngx_http_grpc_st_start;\n\n            continue;\n        }\n\n        /*\n         * data frame:\n         *\n         * +---------------+\n         * |Pad Length? (8)|\n         * +---------------+-----------------------------------------------+\n         * |                            Data (*)                         ...\n         * +---------------------------------------------------------------+\n         * |                           Padding (*)                       ...\n         * +---------------------------------------------------------------+\n         */\n\n        if (ctx->flags & NGX_HTTP_V2_PADDED_FLAG) {\n\n            if (ctx->rest == 0) {\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                              \"upstream sent too short http2 frame\");\n                return NGX_ERROR;\n            }\n\n            if (b->pos == b->last) {\n                return NGX_AGAIN;\n            }\n\n            ctx->flags &= ~NGX_HTTP_V2_PADDED_FLAG;\n            ctx->padding = *b->pos++;\n            ctx->rest -= 1;\n\n            if (ctx->padding > ctx->rest) {\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                              \"upstream sent http2 frame with too long \"\n                              \"padding: %d in frame %uz\",\n                              ctx->padding, ctx->rest);\n                return NGX_ERROR;\n            }\n\n            continue;\n        }\n\n        if (ctx->rest == ctx->padding) {\n            goto done;\n        }\n\n        if (b->pos == b->last) {\n            return NGX_AGAIN;\n        }\n\n        cl = ngx_chain_get_free_buf(r->pool, &u->free_bufs);\n        if (cl == NULL) {\n            return NGX_ERROR;\n        }\n\n        *ll = cl;\n        ll = &cl->next;\n\n        buf = cl->buf;\n\n        buf->flush = 1;\n        buf->memory = 1;\n\n        buf->pos = b->pos;\n        buf->tag = u->output.tag;\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"grpc output buf %p\", buf->pos);\n\n        if (b->last - b->pos < (ssize_t) ctx->rest - ctx->padding) {\n\n            ctx->rest -= b->last - b->pos;\n            b->pos = b->last;\n            buf->last = b->pos;\n\n            if (ctx->length != -1) {\n\n                if (buf->last - buf->pos > ctx->length) {\n                    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                                  \"upstream sent response body larger \"\n                                  \"than indicated content length\");\n                    return NGX_ERROR;\n                }\n\n                ctx->length -= buf->last - buf->pos;\n            }\n\n            return NGX_AGAIN;\n        }\n\n        b->pos += ctx->rest - ctx->padding;\n        buf->last = b->pos;\n        ctx->rest = ctx->padding;\n\n        if (ctx->length != -1) {\n\n            if (buf->last - buf->pos > ctx->length) {\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                              \"upstream sent response body larger \"\n                              \"than indicated content length\");\n                return NGX_ERROR;\n            }\n\n            ctx->length -= buf->last - buf->pos;\n        }\n\n    done:\n\n        if (ctx->padding) {\n            ctx->state = ngx_http_grpc_st_padding;\n            continue;\n        }\n\n        ctx->state = ngx_http_grpc_st_start;\n\n        if (ctx->flags & NGX_HTTP_V2_END_STREAM_FLAG) {\n            ctx->done = 1;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_grpc_parse_frame(ngx_http_request_t *r, ngx_http_grpc_ctx_t *ctx,\n    ngx_buf_t *b)\n{\n    u_char                 ch, *p;\n    ngx_http_grpc_state_e  state;\n\n    state = ctx->state;\n\n    for (p = b->pos; p < b->last; p++) {\n        ch = *p;\n\n#if 0\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"grpc frame byte: %02Xd, s:%d\", ch, state);\n#endif\n\n        switch (state) {\n\n        case ngx_http_grpc_st_start:\n            ctx->rest = ch << 16;\n            state = ngx_http_grpc_st_length_2;\n            break;\n\n        case ngx_http_grpc_st_length_2:\n            ctx->rest |= ch << 8;\n            state = ngx_http_grpc_st_length_3;\n            break;\n\n        case ngx_http_grpc_st_length_3:\n            ctx->rest |= ch;\n\n            if (ctx->rest > NGX_HTTP_V2_DEFAULT_FRAME_SIZE) {\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                              \"upstream sent too large http2 frame: %uz\",\n                              ctx->rest);\n                return NGX_ERROR;\n            }\n\n            state = ngx_http_grpc_st_type;\n            break;\n\n        case ngx_http_grpc_st_type:\n            ctx->type = ch;\n            state = ngx_http_grpc_st_flags;\n            break;\n\n        case ngx_http_grpc_st_flags:\n            ctx->flags = ch;\n            state = ngx_http_grpc_st_stream_id;\n            break;\n\n        case ngx_http_grpc_st_stream_id:\n            ctx->stream_id = (ch & 0x7f) << 24;\n            state = ngx_http_grpc_st_stream_id_2;\n            break;\n\n        case ngx_http_grpc_st_stream_id_2:\n            ctx->stream_id |= ch << 16;\n            state = ngx_http_grpc_st_stream_id_3;\n            break;\n\n        case ngx_http_grpc_st_stream_id_3:\n            ctx->stream_id |= ch << 8;\n            state = ngx_http_grpc_st_stream_id_4;\n            break;\n\n        case ngx_http_grpc_st_stream_id_4:\n            ctx->stream_id |= ch;\n\n            ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"grpc frame: %d, len: %uz, f:%d, i:%ui\",\n                           ctx->type, ctx->rest, ctx->flags, ctx->stream_id);\n\n            b->pos = p + 1;\n\n            ctx->state = ngx_http_grpc_st_payload;\n            ctx->frame_state = 0;\n\n            return NGX_OK;\n\n        /* suppress warning */\n        case ngx_http_grpc_st_payload:\n        case ngx_http_grpc_st_padding:\n            break;\n        }\n    }\n\n    b->pos = p;\n    ctx->state = state;\n\n    return NGX_AGAIN;\n}\n\n\nstatic ngx_int_t\nngx_http_grpc_parse_header(ngx_http_request_t *r, ngx_http_grpc_ctx_t *ctx,\n    ngx_buf_t *b)\n{\n    u_char     ch, *p, *last;\n    size_t     min;\n    ngx_int_t  rc;\n    enum {\n        sw_start = 0,\n        sw_padding_length,\n        sw_dependency,\n        sw_dependency_2,\n        sw_dependency_3,\n        sw_dependency_4,\n        sw_weight,\n        sw_fragment,\n        sw_padding\n    } state;\n\n    state = ctx->frame_state;\n\n    if (state == sw_start) {\n\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"grpc parse header: start\");\n\n        if (ctx->type == NGX_HTTP_V2_HEADERS_FRAME) {\n            ctx->parsing_headers = 1;\n            ctx->fragment_state = 0;\n\n            min = (ctx->flags & NGX_HTTP_V2_PADDED_FLAG ? 1 : 0)\n                  + (ctx->flags & NGX_HTTP_V2_PRIORITY_FLAG ? 5 : 0);\n\n            if (ctx->rest < min) {\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                              \"upstream sent headers frame \"\n                              \"with invalid length: %uz\",\n                              ctx->rest);\n                return NGX_ERROR;\n            }\n\n            if (ctx->flags & NGX_HTTP_V2_END_STREAM_FLAG) {\n                ctx->end_stream = 1;\n            }\n\n            if (ctx->flags & NGX_HTTP_V2_PADDED_FLAG) {\n                state = sw_padding_length;\n\n            } else if (ctx->flags & NGX_HTTP_V2_PRIORITY_FLAG) {\n                state = sw_dependency;\n\n            } else {\n                state = sw_fragment;\n            }\n\n        } else if (ctx->type == NGX_HTTP_V2_CONTINUATION_FRAME) {\n            state = sw_fragment;\n        }\n\n        ctx->padding = 0;\n        ctx->frame_state = state;\n    }\n\n    if (state < sw_fragment) {\n\n        if (b->last - b->pos < (ssize_t) ctx->rest) {\n            last = b->last;\n\n        } else {\n            last = b->pos + ctx->rest;\n        }\n\n        for (p = b->pos; p < last; p++) {\n            ch = *p;\n\n#if 0\n            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"grpc header byte: %02Xd s:%d\", ch, state);\n#endif\n\n            /*\n             * headers frame:\n             *\n             * +---------------+\n             * |Pad Length? (8)|\n             * +-+-------------+----------------------------------------------+\n             * |E|                 Stream Dependency? (31)                    |\n             * +-+-------------+----------------------------------------------+\n             * |  Weight? (8)  |\n             * +-+-------------+----------------------------------------------+\n             * |                   Header Block Fragment (*)                ...\n             * +--------------------------------------------------------------+\n             * |                           Padding (*)                      ...\n             * +--------------------------------------------------------------+\n             */\n\n            switch (state) {\n\n            case sw_padding_length:\n\n                ctx->padding = ch;\n\n                if (ctx->flags & NGX_HTTP_V2_PRIORITY_FLAG) {\n                    state = sw_dependency;\n                    break;\n                }\n\n                goto fragment;\n\n            case sw_dependency:\n                state = sw_dependency_2;\n                break;\n\n            case sw_dependency_2:\n                state = sw_dependency_3;\n                break;\n\n            case sw_dependency_3:\n                state = sw_dependency_4;\n                break;\n\n            case sw_dependency_4:\n                state = sw_weight;\n                break;\n\n            case sw_weight:\n                goto fragment;\n\n            /* suppress warning */\n            case sw_start:\n            case sw_fragment:\n            case sw_padding:\n                break;\n            }\n        }\n\n        ctx->rest -= p - b->pos;\n        b->pos = p;\n\n        ctx->frame_state = state;\n        return NGX_AGAIN;\n\n    fragment:\n\n        p++;\n        ctx->rest -= p - b->pos;\n        b->pos = p;\n\n        if (ctx->padding > ctx->rest) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"upstream sent http2 frame with too long \"\n                          \"padding: %d in frame %uz\",\n                          ctx->padding, ctx->rest);\n            return NGX_ERROR;\n        }\n\n        state = sw_fragment;\n        ctx->frame_state = state;\n    }\n\n    if (state == sw_fragment) {\n\n        rc = ngx_http_grpc_parse_fragment(r, ctx, b);\n\n        if (rc == NGX_AGAIN) {\n            return NGX_AGAIN;\n        }\n\n        if (rc == NGX_ERROR) {\n            return NGX_ERROR;\n        }\n\n        if (rc == NGX_OK) {\n            return NGX_OK;\n        }\n\n        /* rc == NGX_DONE */\n\n        state = sw_padding;\n        ctx->frame_state = state;\n    }\n\n    if (state == sw_padding) {\n\n        if (b->last - b->pos < (ssize_t) ctx->rest) {\n\n            ctx->rest -= b->last - b->pos;\n            b->pos = b->last;\n\n            return NGX_AGAIN;\n        }\n\n        b->pos += ctx->rest;\n        ctx->rest = 0;\n\n        ctx->state = ngx_http_grpc_st_start;\n\n        if (ctx->flags & NGX_HTTP_V2_END_HEADERS_FLAG) {\n\n            if (ctx->fragment_state) {\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                              \"upstream sent truncated http2 header\");\n                return NGX_ERROR;\n            }\n\n            ctx->parsing_headers = 0;\n\n            return NGX_HTTP_PARSE_HEADER_DONE;\n        }\n\n        return NGX_AGAIN;\n    }\n\n    /* unreachable */\n\n    return NGX_ERROR;\n}\n\n\nstatic ngx_int_t\nngx_http_grpc_parse_fragment(ngx_http_request_t *r, ngx_http_grpc_ctx_t *ctx,\n    ngx_buf_t *b)\n{\n    u_char      ch, *p, *last;\n    size_t      size;\n    ngx_uint_t  index, size_update;\n    enum {\n        sw_start = 0,\n        sw_index,\n        sw_name_length,\n        sw_name_length_2,\n        sw_name_length_3,\n        sw_name_length_4,\n        sw_name,\n        sw_name_bytes,\n        sw_value_length,\n        sw_value_length_2,\n        sw_value_length_3,\n        sw_value_length_4,\n        sw_value,\n        sw_value_bytes\n    } state;\n\n    /* header block fragment */\n\n#if 0\n    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"grpc header fragment %p:%p rest:%uz\",\n                   b->pos, b->last, ctx->rest);\n#endif\n\n    if (b->last - b->pos < (ssize_t) ctx->rest - ctx->padding) {\n        last = b->last;\n\n    } else {\n        last = b->pos + ctx->rest - ctx->padding;\n    }\n\n    state = ctx->fragment_state;\n\n    for (p = b->pos; p < last; p++) {\n        ch = *p;\n\n#if 0\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"grpc header byte: %02Xd s:%d\", ch, state);\n#endif\n\n        switch (state) {\n\n        case sw_start:\n            ctx->index = 0;\n\n            if ((ch & 0x80) == 0x80) {\n                /*\n                 * indexed header:\n                 *\n                 *   0   1   2   3   4   5   6   7\n                 * +---+---+---+---+---+---+---+---+\n                 * | 1 |        Index (7+)         |\n                 * +---+---------------------------+\n                 */\n\n                index = ch & ~0x80;\n\n                if (index == 0 || index > 61) {\n                    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                                  \"upstream sent invalid http2 \"\n                                  \"table index: %ui\", index);\n                    return NGX_ERROR;\n                }\n\n                ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                               \"grpc indexed header: %ui\", index);\n\n                ctx->index = index;\n                ctx->literal = 0;\n\n                goto done;\n\n            } else if ((ch & 0xc0) == 0x40) {\n                /*\n                 * literal header with incremental indexing:\n                 *\n                 *   0   1   2   3   4   5   6   7\n                 * +---+---+---+---+---+---+---+---+\n                 * | 0 | 1 |      Index (6+)       |\n                 * +---+---+-----------------------+\n                 * | H |     Value Length (7+)     |\n                 * +---+---------------------------+\n                 * | Value String (Length octets)  |\n                 * +-------------------------------+\n                 *\n                 *   0   1   2   3   4   5   6   7\n                 * +---+---+---+---+---+---+---+---+\n                 * | 0 | 1 |           0           |\n                 * +---+---+-----------------------+\n                 * | H |     Name Length (7+)      |\n                 * +---+---------------------------+\n                 * |  Name String (Length octets)  |\n                 * +---+---------------------------+\n                 * | H |     Value Length (7+)     |\n                 * +---+---------------------------+\n                 * | Value String (Length octets)  |\n                 * +-------------------------------+\n                 */\n\n                index = ch & ~0xc0;\n\n                if (index > 61) {\n                    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                                  \"upstream sent invalid http2 \"\n                                  \"table index: %ui\", index);\n                    return NGX_ERROR;\n                }\n\n                ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                               \"grpc literal header: %ui\", index);\n\n                if (index == 0) {\n                    state = sw_name_length;\n                    break;\n                }\n\n                ctx->index = index;\n                ctx->literal = 1;\n\n                state = sw_value_length;\n                break;\n\n            } else if ((ch & 0xe0) == 0x20) {\n                /*\n                 * dynamic table size update:\n                 *\n                 *   0   1   2   3   4   5   6   7\n                 * +---+---+---+---+---+---+---+---+\n                 * | 0 | 0 | 1 |   Max size (5+)   |\n                 * +---+---------------------------+\n                 */\n\n                size_update = ch & ~0xe0;\n\n                if (size_update > 0) {\n                    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                                  \"upstream sent invalid http2 \"\n                                  \"dynamic table size update: %ui\",\n                                  size_update);\n                    return NGX_ERROR;\n                }\n\n                ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                               \"grpc table size update: %ui\", size_update);\n\n                break;\n\n            } else if ((ch & 0xf0) == 0x10) {\n                /*\n                 *  literal header field never indexed:\n                 *\n                 *   0   1   2   3   4   5   6   7\n                 * +---+---+---+---+---+---+---+---+\n                 * | 0 | 0 | 0 | 1 |  Index (4+)   |\n                 * +---+---+-----------------------+\n                 * | H |     Value Length (7+)     |\n                 * +---+---------------------------+\n                 * | Value String (Length octets)  |\n                 * +-------------------------------+\n                 *\n                 *   0   1   2   3   4   5   6   7\n                 * +---+---+---+---+---+---+---+---+\n                 * | 0 | 0 | 0 | 1 |       0       |\n                 * +---+---+-----------------------+\n                 * | H |     Name Length (7+)      |\n                 * +---+---------------------------+\n                 * |  Name String (Length octets)  |\n                 * +---+---------------------------+\n                 * | H |     Value Length (7+)     |\n                 * +---+---------------------------+\n                 * | Value String (Length octets)  |\n                 * +-------------------------------+\n                 */\n\n                index = ch & ~0xf0;\n\n                if (index == 0x0f) {\n                    ctx->index = index;\n                    ctx->literal = 1;\n                    state = sw_index;\n                    break;\n                }\n\n                if (index == 0) {\n                    state = sw_name_length;\n                    break;\n                }\n\n                ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                               \"grpc literal header never indexed: %ui\",\n                               index);\n\n                ctx->index = index;\n                ctx->literal = 1;\n\n                state = sw_value_length;\n                break;\n\n            } else if ((ch & 0xf0) == 0x00) {\n                /*\n                 * literal header field without indexing:\n                 *\n                 *   0   1   2   3   4   5   6   7\n                 * +---+---+---+---+---+---+---+---+\n                 * | 0 | 0 | 0 | 0 |  Index (4+)   |\n                 * +---+---+-----------------------+\n                 * | H |     Value Length (7+)     |\n                 * +---+---------------------------+\n                 * | Value String (Length octets)  |\n                 * +-------------------------------+\n                 *\n                 *   0   1   2   3   4   5   6   7\n                 * +---+---+---+---+---+---+---+---+\n                 * | 0 | 0 | 0 | 0 |       0       |\n                 * +---+---+-----------------------+\n                 * | H |     Name Length (7+)      |\n                 * +---+---------------------------+\n                 * |  Name String (Length octets)  |\n                 * +---+---------------------------+\n                 * | H |     Value Length (7+)     |\n                 * +---+---------------------------+\n                 * | Value String (Length octets)  |\n                 * +-------------------------------+\n                 */\n\n                index = ch & ~0xf0;\n\n                if (index == 0x0f) {\n                    ctx->index = index;\n                    ctx->literal = 1;\n                    state = sw_index;\n                    break;\n                }\n\n                if (index == 0) {\n                    state = sw_name_length;\n                    break;\n                }\n\n                ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                               \"grpc literal header without indexing: %ui\",\n                               index);\n\n                ctx->index = index;\n                ctx->literal = 1;\n\n                state = sw_value_length;\n                break;\n            }\n\n            /* not reached */\n\n            return NGX_ERROR;\n\n        case sw_index:\n            ctx->index = ctx->index + (ch & ~0x80);\n\n            if (ch & 0x80) {\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                              \"upstream sent http2 table index \"\n                              \"with continuation flag\");\n                return NGX_ERROR;\n            }\n\n            if (ctx->index > 61) {\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                              \"upstream sent invalid http2 \"\n                              \"table index: %ui\", ctx->index);\n                return NGX_ERROR;\n            }\n\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"grpc header index: %ui\", ctx->index);\n\n            state = sw_value_length;\n            break;\n\n        case sw_name_length:\n            ctx->field_huffman = ch & 0x80 ? 1 : 0;\n            ctx->field_length = ch & ~0x80;\n\n            if (ctx->field_length == 0x7f) {\n                state = sw_name_length_2;\n                break;\n            }\n\n            if (ctx->field_length == 0) {\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                              \"upstream sent zero http2 \"\n                              \"header name length\");\n                return NGX_ERROR;\n            }\n\n            state = sw_name;\n            break;\n\n        case sw_name_length_2:\n            ctx->field_length += ch & ~0x80;\n\n            if (ch & 0x80) {\n                state = sw_name_length_3;\n                break;\n            }\n\n            state = sw_name;\n            break;\n\n        case sw_name_length_3:\n            ctx->field_length += (ch & ~0x80) << 7;\n\n            if (ch & 0x80) {\n                state = sw_name_length_4;\n                break;\n            }\n\n            state = sw_name;\n            break;\n\n        case sw_name_length_4:\n            ctx->field_length += (ch & ~0x80) << 14;\n\n            if (ch & 0x80) {\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                              \"upstream sent too large http2 \"\n                              \"header name length\");\n                return NGX_ERROR;\n            }\n\n            state = sw_name;\n            break;\n\n        case sw_name:\n            ctx->name.len = ctx->field_huffman ?\n                            ctx->field_length * 8 / 5 : ctx->field_length;\n\n            ctx->name.data = ngx_pnalloc(r->pool, ctx->name.len + 1);\n            if (ctx->name.data == NULL) {\n                return NGX_ERROR;\n            }\n\n            ctx->field_end = ctx->name.data;\n            ctx->field_rest = ctx->field_length;\n            ctx->field_state = 0;\n\n            state = sw_name_bytes;\n\n            /* fall through */\n\n        case sw_name_bytes:\n\n            ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"grpc name: len:%uz h:%d last:%uz, rest:%uz\",\n                           ctx->field_length,\n                           ctx->field_huffman,\n                           last - p,\n                           ctx->rest - (p - b->pos));\n\n            size = ngx_min(last - p, (ssize_t) ctx->field_rest);\n            ctx->field_rest -= size;\n\n            if (ctx->field_huffman) {\n                if (ngx_http_huff_decode(&ctx->field_state, p, size,\n                                         &ctx->field_end,\n                                         ctx->field_rest == 0,\n                                         r->connection->log)\n                    != NGX_OK)\n                {\n                    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                                  \"upstream sent invalid encoded header\");\n                    return NGX_ERROR;\n                }\n\n                ctx->name.len = ctx->field_end - ctx->name.data;\n                ctx->name.data[ctx->name.len] = '\\0';\n\n            } else {\n                ctx->field_end = ngx_cpymem(ctx->field_end, p, size);\n                ctx->name.data[ctx->name.len] = '\\0';\n            }\n\n            p += size - 1;\n\n            if (ctx->field_rest == 0) {\n                state = sw_value_length;\n            }\n\n            break;\n\n        case sw_value_length:\n            ctx->field_huffman = ch & 0x80 ? 1 : 0;\n            ctx->field_length = ch & ~0x80;\n\n            if (ctx->field_length == 0x7f) {\n                state = sw_value_length_2;\n                break;\n            }\n\n            if (ctx->field_length == 0) {\n                ngx_str_set(&ctx->value, \"\");\n                goto done;\n            }\n\n            state = sw_value;\n            break;\n\n        case sw_value_length_2:\n            ctx->field_length += ch & ~0x80;\n\n            if (ch & 0x80) {\n                state = sw_value_length_3;\n                break;\n            }\n\n            state = sw_value;\n            break;\n\n        case sw_value_length_3:\n            ctx->field_length += (ch & ~0x80) << 7;\n\n            if (ch & 0x80) {\n                state = sw_value_length_4;\n                break;\n            }\n\n            state = sw_value;\n            break;\n\n        case sw_value_length_4:\n            ctx->field_length += (ch & ~0x80) << 14;\n\n            if (ch & 0x80) {\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                              \"upstream sent too large http2 \"\n                              \"header value length\");\n                return NGX_ERROR;\n            }\n\n            state = sw_value;\n            break;\n\n        case sw_value:\n            ctx->value.len = ctx->field_huffman ?\n                             ctx->field_length * 8 / 5 : ctx->field_length;\n\n            ctx->value.data = ngx_pnalloc(r->pool, ctx->value.len + 1);\n            if (ctx->value.data == NULL) {\n                return NGX_ERROR;\n            }\n\n            ctx->field_end = ctx->value.data;\n            ctx->field_rest = ctx->field_length;\n            ctx->field_state = 0;\n\n            state = sw_value_bytes;\n\n            /* fall through */\n\n        case sw_value_bytes:\n\n            ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"grpc value: len:%uz h:%d last:%uz, rest:%uz\",\n                           ctx->field_length,\n                           ctx->field_huffman,\n                           last - p,\n                           ctx->rest - (p - b->pos));\n\n            size = ngx_min(last - p, (ssize_t) ctx->field_rest);\n            ctx->field_rest -= size;\n\n            if (ctx->field_huffman) {\n                if (ngx_http_huff_decode(&ctx->field_state, p, size,\n                                         &ctx->field_end,\n                                         ctx->field_rest == 0,\n                                         r->connection->log)\n                    != NGX_OK)\n                {\n                    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                                  \"upstream sent invalid encoded header\");\n                    return NGX_ERROR;\n                }\n\n                ctx->value.len = ctx->field_end - ctx->value.data;\n                ctx->value.data[ctx->value.len] = '\\0';\n\n            } else {\n                ctx->field_end = ngx_cpymem(ctx->field_end, p, size);\n                ctx->value.data[ctx->value.len] = '\\0';\n            }\n\n            p += size - 1;\n\n            if (ctx->field_rest == 0) {\n                goto done;\n            }\n\n            break;\n        }\n\n        continue;\n\n    done:\n\n        p++;\n        ctx->rest -= p - b->pos;\n        ctx->fragment_state = sw_start;\n        b->pos = p;\n\n        if (ctx->index) {\n            ctx->name = *ngx_http_v2_get_static_name(ctx->index);\n        }\n\n        if (ctx->index && !ctx->literal) {\n            ctx->value = *ngx_http_v2_get_static_value(ctx->index);\n        }\n\n        if (!ctx->index) {\n            if (ngx_http_grpc_validate_header_name(r, &ctx->name) != NGX_OK) {\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                              \"upstream sent invalid header: \\\"%V: %V\\\"\",\n                              &ctx->name, &ctx->value);\n                return NGX_ERROR;\n            }\n        }\n\n        if (!ctx->index || ctx->literal) {\n            if (ngx_http_grpc_validate_header_value(r, &ctx->value) != NGX_OK) {\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                              \"upstream sent invalid header: \\\"%V: %V\\\"\",\n                              &ctx->name, &ctx->value);\n                return NGX_ERROR;\n            }\n        }\n\n        return NGX_OK;\n    }\n\n    ctx->rest -= p - b->pos;\n    ctx->fragment_state = state;\n    b->pos = p;\n\n    if (ctx->rest > ctx->padding) {\n        return NGX_AGAIN;\n    }\n\n    return NGX_DONE;\n}\n\n\nstatic ngx_int_t\nngx_http_grpc_validate_header_name(ngx_http_request_t *r, ngx_str_t *s)\n{\n    u_char      ch;\n    ngx_uint_t  i;\n\n    for (i = 0; i < s->len; i++) {\n        ch = s->data[i];\n\n        if (ch == ':' && i > 0) {\n            return NGX_ERROR;\n        }\n\n        if (ch >= 'A' && ch <= 'Z') {\n            return NGX_ERROR;\n        }\n\n        if (ch <= 0x20 || ch == 0x7f) {\n            return NGX_ERROR;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_grpc_validate_header_value(ngx_http_request_t *r, ngx_str_t *s)\n{\n    u_char      ch;\n    ngx_uint_t  i;\n\n    for (i = 0; i < s->len; i++) {\n        ch = s->data[i];\n\n        if (ch == '\\0' || ch == CR || ch == LF) {\n            return NGX_ERROR;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_grpc_parse_rst_stream(ngx_http_request_t *r, ngx_http_grpc_ctx_t *ctx,\n    ngx_buf_t *b)\n{\n    u_char  ch, *p, *last;\n    enum {\n        sw_start = 0,\n        sw_error_2,\n        sw_error_3,\n        sw_error_4\n    } state;\n\n    if (b->last - b->pos < (ssize_t) ctx->rest) {\n        last = b->last;\n\n    } else {\n        last = b->pos + ctx->rest;\n    }\n\n    state = ctx->frame_state;\n\n    if (state == sw_start) {\n        if (ctx->rest != 4) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"upstream sent rst stream frame \"\n                          \"with invalid length: %uz\",\n                          ctx->rest);\n            return NGX_ERROR;\n        }\n    }\n\n    for (p = b->pos; p < last; p++) {\n        ch = *p;\n\n#if 0\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"grpc rst byte: %02Xd s:%d\", ch, state);\n#endif\n\n        switch (state) {\n\n        case sw_start:\n            ctx->error = (ngx_uint_t) ch << 24;\n            state = sw_error_2;\n            break;\n\n        case sw_error_2:\n            ctx->error |= ch << 16;\n            state = sw_error_3;\n            break;\n\n        case sw_error_3:\n            ctx->error |= ch << 8;\n            state = sw_error_4;\n            break;\n\n        case sw_error_4:\n            ctx->error |= ch;\n            state = sw_start;\n\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"grpc error: %ui\", ctx->error);\n\n            break;\n        }\n    }\n\n    ctx->rest -= p - b->pos;\n    ctx->frame_state = state;\n    b->pos = p;\n\n    if (ctx->rest > 0) {\n        return NGX_AGAIN;\n    }\n\n    ctx->state = ngx_http_grpc_st_start;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_grpc_parse_goaway(ngx_http_request_t *r, ngx_http_grpc_ctx_t *ctx,\n    ngx_buf_t *b)\n{\n    u_char  ch, *p, *last;\n    enum {\n        sw_start = 0,\n        sw_last_stream_id_2,\n        sw_last_stream_id_3,\n        sw_last_stream_id_4,\n        sw_error,\n        sw_error_2,\n        sw_error_3,\n        sw_error_4,\n        sw_debug\n    } state;\n\n    if (b->last - b->pos < (ssize_t) ctx->rest) {\n        last = b->last;\n\n    } else {\n        last = b->pos + ctx->rest;\n    }\n\n    state = ctx->frame_state;\n\n    if (state == sw_start) {\n\n        if (ctx->stream_id) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"upstream sent goaway frame \"\n                          \"with non-zero stream id: %ui\",\n                          ctx->stream_id);\n            return NGX_ERROR;\n        }\n\n        if (ctx->rest < 8) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"upstream sent goaway frame \"\n                          \"with invalid length: %uz\",\n                          ctx->rest);\n            return NGX_ERROR;\n        }\n    }\n\n    for (p = b->pos; p < last; p++) {\n        ch = *p;\n\n#if 0\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"grpc goaway byte: %02Xd s:%d\", ch, state);\n#endif\n\n        switch (state) {\n\n        case sw_start:\n            ctx->stream_id = (ch & 0x7f) << 24;\n            state = sw_last_stream_id_2;\n            break;\n\n        case sw_last_stream_id_2:\n            ctx->stream_id |= ch << 16;\n            state = sw_last_stream_id_3;\n            break;\n\n        case sw_last_stream_id_3:\n            ctx->stream_id |= ch << 8;\n            state = sw_last_stream_id_4;\n            break;\n\n        case sw_last_stream_id_4:\n            ctx->stream_id |= ch;\n            state = sw_error;\n            break;\n\n        case sw_error:\n            ctx->error = (ngx_uint_t) ch << 24;\n            state = sw_error_2;\n            break;\n\n        case sw_error_2:\n            ctx->error |= ch << 16;\n            state = sw_error_3;\n            break;\n\n        case sw_error_3:\n            ctx->error |= ch << 8;\n            state = sw_error_4;\n            break;\n\n        case sw_error_4:\n            ctx->error |= ch;\n            state = sw_debug;\n            break;\n\n        case sw_debug:\n            break;\n        }\n    }\n\n    ctx->rest -= p - b->pos;\n    ctx->frame_state = state;\n    b->pos = p;\n\n    if (ctx->rest > 0) {\n        return NGX_AGAIN;\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"grpc goaway: %ui, stream %ui\",\n                   ctx->error, ctx->stream_id);\n\n    ctx->state = ngx_http_grpc_st_start;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_grpc_parse_window_update(ngx_http_request_t *r,\n    ngx_http_grpc_ctx_t *ctx, ngx_buf_t *b)\n{\n    u_char  ch, *p, *last;\n    enum {\n        sw_start = 0,\n        sw_size_2,\n        sw_size_3,\n        sw_size_4\n    } state;\n\n    if (b->last - b->pos < (ssize_t) ctx->rest) {\n        last = b->last;\n\n    } else {\n        last = b->pos + ctx->rest;\n    }\n\n    state = ctx->frame_state;\n\n    if (state == sw_start) {\n        if (ctx->rest != 4) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"upstream sent window update frame \"\n                          \"with invalid length: %uz\",\n                          ctx->rest);\n            return NGX_ERROR;\n        }\n    }\n\n    for (p = b->pos; p < last; p++) {\n        ch = *p;\n\n#if 0\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"grpc window update byte: %02Xd s:%d\", ch, state);\n#endif\n\n        switch (state) {\n\n        case sw_start:\n            ctx->window_update = (ch & 0x7f) << 24;\n            state = sw_size_2;\n            break;\n\n        case sw_size_2:\n            ctx->window_update |= ch << 16;\n            state = sw_size_3;\n            break;\n\n        case sw_size_3:\n            ctx->window_update |= ch << 8;\n            state = sw_size_4;\n            break;\n\n        case sw_size_4:\n            ctx->window_update |= ch;\n            state = sw_start;\n            break;\n        }\n    }\n\n    ctx->rest -= p - b->pos;\n    ctx->frame_state = state;\n    b->pos = p;\n\n    if (ctx->rest > 0) {\n        return NGX_AGAIN;\n    }\n\n    ctx->state = ngx_http_grpc_st_start;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"grpc window update: %ui\", ctx->window_update);\n\n    if (ctx->stream_id) {\n\n        if (ctx->window_update > (size_t) NGX_HTTP_V2_MAX_WINDOW\n                                 - ctx->send_window)\n        {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"upstream sent too large window update\");\n            return NGX_ERROR;\n        }\n\n        ctx->send_window += ctx->window_update;\n\n    } else {\n\n        if (ctx->window_update > NGX_HTTP_V2_MAX_WINDOW\n                                 - ctx->connection->send_window)\n        {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"upstream sent too large window update\");\n            return NGX_ERROR;\n        }\n\n        ctx->connection->send_window += ctx->window_update;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_grpc_parse_settings(ngx_http_request_t *r, ngx_http_grpc_ctx_t *ctx,\n    ngx_buf_t *b)\n{\n    u_char   ch, *p, *last;\n    ssize_t  window_update;\n    enum {\n        sw_start = 0,\n        sw_id,\n        sw_id_2,\n        sw_value,\n        sw_value_2,\n        sw_value_3,\n        sw_value_4\n    } state;\n\n    if (b->last - b->pos < (ssize_t) ctx->rest) {\n        last = b->last;\n\n    } else {\n        last = b->pos + ctx->rest;\n    }\n\n    state = ctx->frame_state;\n\n    if (state == sw_start) {\n\n        if (ctx->stream_id) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"upstream sent settings frame \"\n                          \"with non-zero stream id: %ui\",\n                          ctx->stream_id);\n            return NGX_ERROR;\n        }\n\n        if (ctx->flags & NGX_HTTP_V2_ACK_FLAG) {\n            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"grpc settings ack\");\n\n            if (ctx->rest != 0) {\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                              \"upstream sent settings frame \"\n                              \"with ack flag and non-zero length: %uz\",\n                              ctx->rest);\n                return NGX_ERROR;\n            }\n\n            ctx->state = ngx_http_grpc_st_start;\n\n            return NGX_OK;\n        }\n\n        if (ctx->rest % 6 != 0) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"upstream sent settings frame \"\n                          \"with invalid length: %uz\",\n                          ctx->rest);\n            return NGX_ERROR;\n        }\n\n        if (ctx->free == NULL && ctx->settings++ > 1000) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"upstream sent too many settings frames\");\n            return NGX_ERROR;\n        }\n    }\n\n    for (p = b->pos; p < last; p++) {\n        ch = *p;\n\n#if 0\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"grpc settings byte: %02Xd s:%d\", ch, state);\n#endif\n\n        switch (state) {\n\n        case sw_start:\n        case sw_id:\n            ctx->setting_id = ch << 8;\n            state = sw_id_2;\n            break;\n\n        case sw_id_2:\n            ctx->setting_id |= ch;\n            state = sw_value;\n            break;\n\n        case sw_value:\n            ctx->setting_value = (ngx_uint_t) ch << 24;\n            state = sw_value_2;\n            break;\n\n        case sw_value_2:\n            ctx->setting_value |= ch << 16;\n            state = sw_value_3;\n            break;\n\n        case sw_value_3:\n            ctx->setting_value |= ch << 8;\n            state = sw_value_4;\n            break;\n\n        case sw_value_4:\n            ctx->setting_value |= ch;\n            state = sw_id;\n\n            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"grpc setting: %ui %ui\",\n                           ctx->setting_id, ctx->setting_value);\n\n            /*\n             * The following settings are defined by the protocol:\n             *\n             * SETTINGS_HEADER_TABLE_SIZE, SETTINGS_ENABLE_PUSH,\n             * SETTINGS_MAX_CONCURRENT_STREAMS, SETTINGS_INITIAL_WINDOW_SIZE,\n             * SETTINGS_MAX_FRAME_SIZE, SETTINGS_MAX_HEADER_LIST_SIZE\n             *\n             * Only SETTINGS_INITIAL_WINDOW_SIZE seems to be needed in\n             * a simple client.\n             */\n\n            if (ctx->setting_id == 0x04) {\n                /* SETTINGS_INITIAL_WINDOW_SIZE */\n\n                if (ctx->setting_value > NGX_HTTP_V2_MAX_WINDOW) {\n                    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                                  \"upstream sent settings frame \"\n                                  \"with too large initial window size: %ui\",\n                                  ctx->setting_value);\n                    return NGX_ERROR;\n                }\n\n                window_update = ctx->setting_value\n                                - ctx->connection->init_window;\n                ctx->connection->init_window = ctx->setting_value;\n\n                if (ctx->send_window > 0\n                    && window_update > (ssize_t) NGX_HTTP_V2_MAX_WINDOW\n                                       - ctx->send_window)\n                {\n                    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                                  \"upstream sent settings frame \"\n                                  \"with too large initial window size: %ui\",\n                                  ctx->setting_value);\n                    return NGX_ERROR;\n                }\n\n                ctx->send_window += window_update;\n            }\n\n            break;\n        }\n    }\n\n    ctx->rest -= p - b->pos;\n    ctx->frame_state = state;\n    b->pos = p;\n\n    if (ctx->rest > 0) {\n        return NGX_AGAIN;\n    }\n\n    ctx->state = ngx_http_grpc_st_start;\n\n    return ngx_http_grpc_send_settings_ack(r, ctx);\n}\n\n\nstatic ngx_int_t\nngx_http_grpc_parse_ping(ngx_http_request_t *r,\n    ngx_http_grpc_ctx_t *ctx, ngx_buf_t *b)\n{\n    u_char  ch, *p, *last;\n    enum {\n        sw_start = 0,\n        sw_data_2,\n        sw_data_3,\n        sw_data_4,\n        sw_data_5,\n        sw_data_6,\n        sw_data_7,\n        sw_data_8\n    } state;\n\n    if (b->last - b->pos < (ssize_t) ctx->rest) {\n        last = b->last;\n\n    } else {\n        last = b->pos + ctx->rest;\n    }\n\n    state = ctx->frame_state;\n\n    if (state == sw_start) {\n\n        if (ctx->stream_id) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"upstream sent ping frame \"\n                          \"with non-zero stream id: %ui\",\n                          ctx->stream_id);\n            return NGX_ERROR;\n        }\n\n        if (ctx->rest != 8) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"upstream sent ping frame \"\n                          \"with invalid length: %uz\",\n                          ctx->rest);\n            return NGX_ERROR;\n        }\n\n        if (ctx->flags & NGX_HTTP_V2_ACK_FLAG) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"upstream sent ping frame with ack flag\");\n            return NGX_ERROR;\n        }\n\n        if (ctx->free == NULL && ctx->pings++ > 1000) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"upstream sent too many ping frames\");\n            return NGX_ERROR;\n        }\n    }\n\n    for (p = b->pos; p < last; p++) {\n        ch = *p;\n\n#if 0\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"grpc ping byte: %02Xd s:%d\", ch, state);\n#endif\n\n        if (state < sw_data_8) {\n            ctx->ping_data[state] = ch;\n            state++;\n\n        } else {\n            ctx->ping_data[7] = ch;\n            state = sw_start;\n\n            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"grpc ping\");\n        }\n    }\n\n    ctx->rest -= p - b->pos;\n    ctx->frame_state = state;\n    b->pos = p;\n\n    if (ctx->rest > 0) {\n        return NGX_AGAIN;\n    }\n\n    ctx->state = ngx_http_grpc_st_start;\n\n    return ngx_http_grpc_send_ping_ack(r, ctx);\n}\n\n\nstatic ngx_int_t\nngx_http_grpc_send_settings_ack(ngx_http_request_t *r, ngx_http_grpc_ctx_t *ctx)\n{\n    ngx_chain_t            *cl, **ll;\n    ngx_http_grpc_frame_t  *f;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"grpc send settings ack\");\n\n    for (cl = ctx->out, ll = &ctx->out; cl; cl = cl->next) {\n        ll = &cl->next;\n    }\n\n    cl = ngx_http_grpc_get_buf(r, ctx);\n    if (cl == NULL) {\n        return NGX_ERROR;\n    }\n\n    f = (ngx_http_grpc_frame_t *) cl->buf->last;\n    cl->buf->last += sizeof(ngx_http_grpc_frame_t);\n\n    f->length_0 = 0;\n    f->length_1 = 0;\n    f->length_2 = 0;\n    f->type = NGX_HTTP_V2_SETTINGS_FRAME;\n    f->flags = NGX_HTTP_V2_ACK_FLAG;\n    f->stream_id_0 = 0;\n    f->stream_id_1 = 0;\n    f->stream_id_2 = 0;\n    f->stream_id_3 = 0;\n\n    *ll = cl;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_grpc_send_ping_ack(ngx_http_request_t *r, ngx_http_grpc_ctx_t *ctx)\n{\n    ngx_chain_t            *cl, **ll;\n    ngx_http_grpc_frame_t  *f;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"grpc send ping ack\");\n\n    for (cl = ctx->out, ll = &ctx->out; cl; cl = cl->next) {\n        ll = &cl->next;\n    }\n\n    cl = ngx_http_grpc_get_buf(r, ctx);\n    if (cl == NULL) {\n        return NGX_ERROR;\n    }\n\n    f = (ngx_http_grpc_frame_t *) cl->buf->last;\n    cl->buf->last += sizeof(ngx_http_grpc_frame_t);\n\n    f->length_0 = 0;\n    f->length_1 = 0;\n    f->length_2 = 8;\n    f->type = NGX_HTTP_V2_PING_FRAME;\n    f->flags = NGX_HTTP_V2_ACK_FLAG;\n    f->stream_id_0 = 0;\n    f->stream_id_1 = 0;\n    f->stream_id_2 = 0;\n    f->stream_id_3 = 0;\n\n    cl->buf->last = ngx_copy(cl->buf->last, ctx->ping_data, 8);\n\n    *ll = cl;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_grpc_send_window_update(ngx_http_request_t *r,\n    ngx_http_grpc_ctx_t *ctx)\n{\n    size_t                  n;\n    ngx_chain_t            *cl, **ll;\n    ngx_http_grpc_frame_t  *f;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"grpc send window update: %uz %uz\",\n                   ctx->connection->recv_window, ctx->recv_window);\n\n    for (cl = ctx->out, ll = &ctx->out; cl; cl = cl->next) {\n        ll = &cl->next;\n    }\n\n    cl = ngx_http_grpc_get_buf(r, ctx);\n    if (cl == NULL) {\n        return NGX_ERROR;\n    }\n\n    f = (ngx_http_grpc_frame_t *) cl->buf->last;\n    cl->buf->last += sizeof(ngx_http_grpc_frame_t);\n\n    f->length_0 = 0;\n    f->length_1 = 0;\n    f->length_2 = 4;\n    f->type = NGX_HTTP_V2_WINDOW_UPDATE_FRAME;\n    f->flags = 0;\n    f->stream_id_0 = 0;\n    f->stream_id_1 = 0;\n    f->stream_id_2 = 0;\n    f->stream_id_3 = 0;\n\n    n = NGX_HTTP_V2_MAX_WINDOW - ctx->connection->recv_window;\n    ctx->connection->recv_window = NGX_HTTP_V2_MAX_WINDOW;\n\n    *cl->buf->last++ = (u_char) ((n >> 24) & 0xff);\n    *cl->buf->last++ = (u_char) ((n >> 16) & 0xff);\n    *cl->buf->last++ = (u_char) ((n >> 8) & 0xff);\n    *cl->buf->last++ = (u_char) (n & 0xff);\n\n    f = (ngx_http_grpc_frame_t *) cl->buf->last;\n    cl->buf->last += sizeof(ngx_http_grpc_frame_t);\n\n    f->length_0 = 0;\n    f->length_1 = 0;\n    f->length_2 = 4;\n    f->type = NGX_HTTP_V2_WINDOW_UPDATE_FRAME;\n    f->flags = 0;\n    f->stream_id_0 = (u_char) ((ctx->id >> 24) & 0xff);\n    f->stream_id_1 = (u_char) ((ctx->id >> 16) & 0xff);\n    f->stream_id_2 = (u_char) ((ctx->id >> 8) & 0xff);\n    f->stream_id_3 = (u_char) (ctx->id & 0xff);\n\n    n = NGX_HTTP_V2_MAX_WINDOW - ctx->recv_window;\n    ctx->recv_window = NGX_HTTP_V2_MAX_WINDOW;\n\n    *cl->buf->last++ = (u_char) ((n >> 24) & 0xff);\n    *cl->buf->last++ = (u_char) ((n >> 16) & 0xff);\n    *cl->buf->last++ = (u_char) ((n >> 8) & 0xff);\n    *cl->buf->last++ = (u_char) (n & 0xff);\n\n    *ll = cl;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_chain_t *\nngx_http_grpc_get_buf(ngx_http_request_t *r, ngx_http_grpc_ctx_t *ctx)\n{\n    u_char       *start;\n    ngx_buf_t    *b;\n    ngx_chain_t  *cl;\n\n    cl = ngx_chain_get_free_buf(r->pool, &ctx->free);\n    if (cl == NULL) {\n        return NULL;\n    }\n\n    b = cl->buf;\n    start = b->start;\n\n    if (start == NULL) {\n\n        /*\n         * each buffer is large enough to hold two window update\n         * frames in a row\n         */\n\n        start = ngx_palloc(r->pool, 2 * sizeof(ngx_http_grpc_frame_t) + 8);\n        if (start == NULL) {\n            return NULL;\n        }\n\n    }\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 = start + 2 * sizeof(ngx_http_grpc_frame_t) + 8;\n\n    b->tag = (ngx_buf_tag_t) &ngx_http_grpc_body_output_filter;\n    b->temporary = 1;\n    b->flush = 1;\n\n    return cl;\n}\n\n\nstatic ngx_http_grpc_ctx_t *\nngx_http_grpc_get_ctx(ngx_http_request_t *r)\n{\n    ngx_http_grpc_ctx_t  *ctx;\n    ngx_http_upstream_t  *u;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_grpc_module);\n\n    if (ctx->connection == NULL) {\n        u = r->upstream;\n\n        if (ngx_http_grpc_get_connection_data(r, ctx, &u->peer) != NGX_OK) {\n            return NULL;\n        }\n    }\n\n    return ctx;\n}\n\n\nstatic ngx_int_t\nngx_http_grpc_get_connection_data(ngx_http_request_t *r,\n    ngx_http_grpc_ctx_t *ctx, ngx_peer_connection_t *pc)\n{\n    ngx_connection_t    *c;\n    ngx_pool_cleanup_t  *cln;\n\n    c = pc->connection;\n\n    if (pc->cached) {\n\n        /*\n         * for cached connections, connection data can be found\n         * in the cleanup handler\n         */\n\n        for (cln = c->pool->cleanup; cln; cln = cln->next) {\n            if (cln->handler == ngx_http_grpc_cleanup) {\n                ctx->connection = cln->data;\n                break;\n            }\n        }\n\n        if (ctx->connection == NULL) {\n            ngx_log_error(NGX_LOG_ERR, c->log, 0,\n                          \"no connection data found for \"\n                          \"keepalive http2 connection\");\n            return NGX_ERROR;\n        }\n\n        ctx->send_window = ctx->connection->init_window;\n        ctx->recv_window = NGX_HTTP_V2_MAX_WINDOW;\n\n        ctx->connection->last_stream_id += 2;\n        ctx->id = ctx->connection->last_stream_id;\n\n        return NGX_OK;\n    }\n\n    cln = ngx_pool_cleanup_add(c->pool, sizeof(ngx_http_grpc_conn_t));\n    if (cln == NULL) {\n        return NGX_ERROR;\n    }\n\n    cln->handler = ngx_http_grpc_cleanup;\n    ctx->connection = cln->data;\n\n    ctx->connection->init_window = NGX_HTTP_V2_DEFAULT_WINDOW;\n    ctx->connection->send_window = NGX_HTTP_V2_DEFAULT_WINDOW;\n    ctx->connection->recv_window = NGX_HTTP_V2_MAX_WINDOW;\n\n    ctx->send_window = NGX_HTTP_V2_DEFAULT_WINDOW;\n    ctx->recv_window = NGX_HTTP_V2_MAX_WINDOW;\n\n    ctx->id = 1;\n    ctx->connection->last_stream_id = 1;\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_grpc_cleanup(void *data)\n{\n#if 0\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"grpc cleanup\");\n#endif\n    return;\n}\n\n\nstatic void\nngx_http_grpc_abort_request(ngx_http_request_t *r)\n{\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"abort grpc request\");\n    return;\n}\n\n\nstatic void\nngx_http_grpc_finalize_request(ngx_http_request_t *r, ngx_int_t rc)\n{\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"finalize grpc request\");\n    return;\n}\n\n\nstatic ngx_int_t\nngx_http_grpc_internal_trailers_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    ngx_table_elt_t  *te;\n\n    te = r->headers_in.te;\n\n    if (te == NULL) {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    if (ngx_strlcasestrn(te->value.data, te->value.data + te->value.len,\n                         (u_char *) \"trailers\", 8 - 1)\n        == NULL)\n    {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    v->data = (u_char *) \"trailers\";\n    v->len = sizeof(\"trailers\") - 1;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_grpc_add_variables(ngx_conf_t *cf)\n{\n    ngx_http_variable_t  *var, *v;\n\n    for (v = ngx_http_grpc_vars; v->name.len; v++) {\n        var = ngx_http_add_variable(cf, &v->name, v->flags);\n        if (var == NULL) {\n            return NGX_ERROR;\n        }\n\n        var->get_handler = v->get_handler;\n        var->data = v->data;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void *\nngx_http_grpc_create_loc_conf(ngx_conf_t *cf)\n{\n    ngx_http_grpc_loc_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_grpc_loc_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc():\n     *\n     *     conf->upstream.ignore_headers = 0;\n     *     conf->upstream.next_upstream = 0;\n     *     conf->upstream.hide_headers_hash = { NULL, 0 };\n     *\n     *     conf->headers.lengths = NULL;\n     *     conf->headers.values = NULL;\n     *     conf->headers.hash = { NULL, 0 };\n     *     conf->host = { 0, NULL };\n     *     conf->host_set = 0;\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     */\n\n    conf->upstream.local = NGX_CONF_UNSET_PTR;\n    conf->upstream.socket_keepalive = NGX_CONF_UNSET;\n    conf->upstream.next_upstream_tries = NGX_CONF_UNSET_UINT;\n    conf->upstream.connect_timeout = NGX_CONF_UNSET_MSEC;\n    conf->upstream.send_timeout = NGX_CONF_UNSET_MSEC;\n    conf->upstream.read_timeout = NGX_CONF_UNSET_MSEC;\n    conf->upstream.next_upstream_timeout = NGX_CONF_UNSET_MSEC;\n\n    conf->upstream.buffer_size = NGX_CONF_UNSET_SIZE;\n\n    conf->upstream.hide_headers = NGX_CONF_UNSET_PTR;\n    conf->upstream.pass_headers = NGX_CONF_UNSET_PTR;\n\n    conf->upstream.intercept_errors = NGX_CONF_UNSET;\n\n#if (NGX_HTTP_SSL)\n    conf->upstream.ssl_session_reuse = NGX_CONF_UNSET;\n    conf->upstream.ssl_name = NGX_CONF_UNSET_PTR;\n    conf->upstream.ssl_server_name = NGX_CONF_UNSET;\n    conf->upstream.ssl_verify = NGX_CONF_UNSET;\n    conf->ssl_verify_depth = NGX_CONF_UNSET_UINT;\n    conf->upstream.ssl_certificate = NGX_CONF_UNSET_PTR;\n    conf->upstream.ssl_certificate_key = NGX_CONF_UNSET_PTR;\n    conf->upstream.ssl_passwords = NGX_CONF_UNSET_PTR;\n    conf->ssl_conf_commands = NGX_CONF_UNSET_PTR;\n\n#if (T_NGX_SSL_NTLS)\n    conf->upstream.tls_method = NULL;\n    conf->upstream.enable_ntls = NULL;\n#endif\n#endif\n\n    /* the hardcoded values */\n    conf->upstream.cyclic_temp_file = 0;\n    conf->upstream.buffering = 0;\n    conf->upstream.ignore_client_abort = 0;\n    conf->upstream.send_lowat = 0;\n    conf->upstream.bufs.num = 0;\n    conf->upstream.busy_buffers_size = 0;\n    conf->upstream.max_temp_file_size = 0;\n    conf->upstream.temp_file_write_size = 0;\n    conf->upstream.pass_request_headers = 1;\n    conf->upstream.pass_request_body = 1;\n    conf->upstream.force_ranges = 0;\n    conf->upstream.pass_trailers = 1;\n    conf->upstream.preserve_output = 1;\n\n    conf->headers_source = NGX_CONF_UNSET_PTR;\n\n    ngx_str_set(&conf->upstream.module, \"grpc\");\n\n    return conf;\n}\n\n\nstatic char *\nngx_http_grpc_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_http_grpc_loc_conf_t *prev = parent;\n    ngx_http_grpc_loc_conf_t *conf = child;\n\n    ngx_int_t                  rc;\n    ngx_hash_init_t            hash;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    ngx_conf_merge_ptr_value(conf->upstream.local,\n                              prev->upstream.local, NULL);\n\n    ngx_conf_merge_value(conf->upstream.socket_keepalive,\n                              prev->upstream.socket_keepalive, 0);\n\n    ngx_conf_merge_uint_value(conf->upstream.next_upstream_tries,\n                              prev->upstream.next_upstream_tries, 0);\n\n    ngx_conf_merge_msec_value(conf->upstream.connect_timeout,\n                              prev->upstream.connect_timeout, 60000);\n\n    ngx_conf_merge_msec_value(conf->upstream.send_timeout,\n                              prev->upstream.send_timeout, 60000);\n\n    ngx_conf_merge_msec_value(conf->upstream.read_timeout,\n                              prev->upstream.read_timeout, 60000);\n\n    ngx_conf_merge_msec_value(conf->upstream.next_upstream_timeout,\n                              prev->upstream.next_upstream_timeout, 0);\n\n    ngx_conf_merge_size_value(conf->upstream.buffer_size,\n                              prev->upstream.buffer_size,\n                              (size_t) ngx_pagesize);\n\n    ngx_conf_merge_bitmask_value(conf->upstream.ignore_headers,\n                              prev->upstream.ignore_headers,\n                              NGX_CONF_BITMASK_SET);\n\n    ngx_conf_merge_bitmask_value(conf->upstream.next_upstream,\n                              prev->upstream.next_upstream,\n                              (NGX_CONF_BITMASK_SET\n                               |NGX_HTTP_UPSTREAM_FT_ERROR\n                               |NGX_HTTP_UPSTREAM_FT_TIMEOUT));\n\n    if (conf->upstream.next_upstream & NGX_HTTP_UPSTREAM_FT_OFF) {\n        conf->upstream.next_upstream = NGX_CONF_BITMASK_SET\n                                       |NGX_HTTP_UPSTREAM_FT_OFF;\n    }\n\n    ngx_conf_merge_value(conf->upstream.intercept_errors,\n                              prev->upstream.intercept_errors, 0);\n\n#if (NGX_HTTP_SSL)\n\n    if (ngx_http_grpc_merge_ssl(cf, conf, prev) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_conf_merge_value(conf->upstream.ssl_session_reuse,\n                              prev->upstream.ssl_session_reuse, 1);\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|NGX_SSL_TLSv1_3));\n\n    ngx_conf_merge_str_value(conf->ssl_ciphers, prev->ssl_ciphers,\n                             \"DEFAULT\");\n\n    ngx_conf_merge_ptr_value(conf->upstream.ssl_name,\n                              prev->upstream.ssl_name, NULL);\n    ngx_conf_merge_value(conf->upstream.ssl_server_name,\n                              prev->upstream.ssl_server_name, 0);\n    ngx_conf_merge_value(conf->upstream.ssl_verify,\n                              prev->upstream.ssl_verify, 0);\n    ngx_conf_merge_uint_value(conf->ssl_verify_depth,\n                              prev->ssl_verify_depth, 1);\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\n    ngx_conf_merge_ptr_value(conf->upstream.ssl_certificate,\n                              prev->upstream.ssl_certificate, NULL);\n    ngx_conf_merge_ptr_value(conf->upstream.ssl_certificate_key,\n                              prev->upstream.ssl_certificate_key, NULL);\n    ngx_conf_merge_ptr_value(conf->upstream.ssl_passwords,\n                              prev->upstream.ssl_passwords, NULL);\n\n    ngx_conf_merge_ptr_value(conf->ssl_conf_commands,\n                              prev->ssl_conf_commands, NULL);\n\n    if (conf->ssl && ngx_http_grpc_set_ssl(cf, conf) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n#if (T_NGX_SSL_NTLS)\n    if (conf->upstream.enable_ntls == NULL) {\n        conf->upstream.enable_ntls = prev->upstream.enable_ntls;\n    }\n    ngx_conf_merge_str_value(conf->upstream.enc_certificate,\n                             prev->upstream.enc_certificate, \"\");\n    ngx_conf_merge_str_value(conf->upstream.enc_certificate_key,\n                             prev->upstream.enc_certificate_key, \"\");\n    ngx_conf_merge_str_value(conf->upstream.sign_certificate,\n                             prev->upstream.sign_certificate, \"\");\n    ngx_conf_merge_str_value(conf->upstream.sign_certificate_key,\n                             prev->upstream.sign_certificate_key, \"\");\n    conf->upstream.ssl_ciphers = conf->ssl_ciphers;\n#endif\n#endif\n\n    hash.max_size = 512;\n    hash.bucket_size = ngx_align(64, ngx_cacheline_size);\n    hash.name = \"grpc_headers_hash\";\n\n    if (ngx_http_upstream_hide_headers_hash(cf, &conf->upstream,\n            &prev->upstream, ngx_http_grpc_hide_headers, &hash)\n        != NGX_OK)\n    {\n        return NGX_CONF_ERROR;\n    }\n\n    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);\n\n    if (clcf->noname\n        && conf->upstream.upstream == NULL && conf->grpc_lengths == NULL)\n    {\n        conf->upstream.upstream = prev->upstream.upstream;\n        conf->host = prev->host;\n\n        conf->grpc_lengths = prev->grpc_lengths;\n        conf->grpc_values = prev->grpc_values;\n\n#if (NGX_HTTP_SSL)\n        conf->ssl = prev->ssl;\n#endif\n    }\n\n    if (clcf->lmt_excpt && clcf->handler == NULL\n        && (conf->upstream.upstream || conf->grpc_lengths))\n    {\n        clcf->handler = ngx_http_grpc_handler;\n    }\n\n    ngx_conf_merge_ptr_value(conf->headers_source, prev->headers_source, NULL);\n\n    if (conf->headers_source == prev->headers_source) {\n        conf->headers = prev->headers;\n        conf->host_set = prev->host_set;\n    }\n\n    rc = ngx_http_grpc_init_headers(cf, conf, &conf->headers,\n                                    ngx_http_grpc_headers);\n    if (rc != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    /*\n     * special handling to preserve conf->headers in the \"http\" section\n     * to inherit it to all servers\n     */\n\n    if (prev->headers.hash.buckets == NULL\n        && conf->headers_source == prev->headers_source)\n    {\n        prev->headers = conf->headers;\n        prev->host_set = conf->host_set;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_grpc_init_headers(ngx_conf_t *cf, ngx_http_grpc_loc_conf_t *conf,\n    ngx_http_grpc_headers_t *headers, ngx_keyval_t *default_headers)\n{\n    u_char                       *p;\n    size_t                        size;\n    uintptr_t                    *code;\n    ngx_uint_t                    i;\n    ngx_array_t                   headers_names, headers_merged;\n    ngx_keyval_t                 *src, *s, *h;\n    ngx_hash_key_t               *hk;\n    ngx_hash_init_t               hash;\n    ngx_http_script_compile_t     sc;\n    ngx_http_script_copy_code_t  *copy;\n\n    if (headers->hash.buckets) {\n        return NGX_OK;\n    }\n\n    if (ngx_array_init(&headers_names, cf->temp_pool, 4, sizeof(ngx_hash_key_t))\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    if (ngx_array_init(&headers_merged, cf->temp_pool, 4, sizeof(ngx_keyval_t))\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    headers->lengths = ngx_array_create(cf->pool, 64, 1);\n    if (headers->lengths == NULL) {\n        return NGX_ERROR;\n    }\n\n    headers->values = ngx_array_create(cf->pool, 512, 1);\n    if (headers->values == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (conf->headers_source) {\n\n        src = conf->headers_source->elts;\n        for (i = 0; i < conf->headers_source->nelts; i++) {\n\n            if (src[i].key.len == 4\n                && ngx_strncasecmp(src[i].key.data, (u_char *) \"Host\", 4) == 0)\n            {\n                conf->host_set = 1;\n            }\n\n            s = ngx_array_push(&headers_merged);\n            if (s == NULL) {\n                return NGX_ERROR;\n            }\n\n            *s = src[i];\n        }\n    }\n\n    h = default_headers;\n\n    while (h->key.len) {\n\n        src = headers_merged.elts;\n        for (i = 0; i < headers_merged.nelts; i++) {\n            if (ngx_strcasecmp(h->key.data, src[i].key.data) == 0) {\n                goto next;\n            }\n        }\n\n        s = ngx_array_push(&headers_merged);\n        if (s == NULL) {\n            return NGX_ERROR;\n        }\n\n        *s = *h;\n\n    next:\n\n        h++;\n    }\n\n\n    src = headers_merged.elts;\n    for (i = 0; i < headers_merged.nelts; i++) {\n\n        hk = ngx_array_push(&headers_names);\n        if (hk == NULL) {\n            return NGX_ERROR;\n        }\n\n        hk->key = src[i].key;\n        hk->key_hash = ngx_hash_key_lc(src[i].key.data, src[i].key.len);\n        hk->value = (void *) 1;\n\n        if (src[i].value.len == 0) {\n            continue;\n        }\n\n        copy = ngx_array_push_n(headers->lengths,\n                                sizeof(ngx_http_script_copy_code_t));\n        if (copy == NULL) {\n            return NGX_ERROR;\n        }\n\n        copy->code = (ngx_http_script_code_pt) (void *)\n                                                 ngx_http_script_copy_len_code;\n        copy->len = src[i].key.len;\n\n        size = (sizeof(ngx_http_script_copy_code_t)\n                + src[i].key.len + sizeof(uintptr_t) - 1)\n               & ~(sizeof(uintptr_t) - 1);\n\n        copy = ngx_array_push_n(headers->values, size);\n        if (copy == NULL) {\n            return NGX_ERROR;\n        }\n\n        copy->code = ngx_http_script_copy_code;\n        copy->len = src[i].key.len;\n\n        p = (u_char *) copy + sizeof(ngx_http_script_copy_code_t);\n        ngx_memcpy(p, src[i].key.data, src[i].key.len);\n\n        ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));\n\n        sc.cf = cf;\n        sc.source = &src[i].value;\n        sc.flushes = &headers->flushes;\n        sc.lengths = &headers->lengths;\n        sc.values = &headers->values;\n\n        if (ngx_http_script_compile(&sc) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        code = ngx_array_push_n(headers->lengths, sizeof(uintptr_t));\n        if (code == NULL) {\n            return NGX_ERROR;\n        }\n\n        *code = (uintptr_t) NULL;\n\n        code = ngx_array_push_n(headers->values, sizeof(uintptr_t));\n        if (code == NULL) {\n            return NGX_ERROR;\n        }\n\n        *code = (uintptr_t) NULL;\n    }\n\n    code = ngx_array_push_n(headers->lengths, sizeof(uintptr_t));\n    if (code == NULL) {\n        return NGX_ERROR;\n    }\n\n    *code = (uintptr_t) NULL;\n\n\n    hash.hash = &headers->hash;\n    hash.key = ngx_hash_key_lc;\n    hash.max_size = 512;\n    hash.bucket_size = 64;\n    hash.name = \"grpc_headers_hash\";\n    hash.pool = cf->pool;\n    hash.temp_pool = NULL;\n\n    return ngx_hash_init(&hash, headers_names.elts, headers_names.nelts);\n}\n\n\nstatic char *\nngx_http_grpc_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_grpc_loc_conf_t *glcf = conf;\n\n    size_t                      add;\n    ngx_str_t                  *value, *url;\n    ngx_url_t                   u;\n    ngx_uint_t                  n;\n    ngx_http_core_loc_conf_t   *clcf;\n    ngx_http_script_compile_t   sc;\n\n    if (glcf->upstream.upstream || glcf->grpc_lengths) {\n        return \"is duplicate\";\n    }\n\n    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);\n\n    clcf->handler = ngx_http_grpc_handler;\n\n    if (clcf->name.len && clcf->name.data[clcf->name.len - 1] == '/') {\n        clcf->auto_redirect = 1;\n    }\n\n    value = cf->args->elts;\n\n    url = &value[1];\n\n    n = ngx_http_script_variables_count(url);\n\n    if (n) {\n\n        ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));\n\n        sc.cf = cf;\n        sc.source = url;\n        sc.lengths = &glcf->grpc_lengths;\n        sc.values = &glcf->grpc_values;\n        sc.variables = n;\n        sc.complete_lengths = 1;\n        sc.complete_values = 1;\n\n        if (ngx_http_script_compile(&sc) != NGX_OK) {\n            return NGX_CONF_ERROR;\n        }\n\n#if (NGX_HTTP_SSL)\n        glcf->ssl = 1;\n#endif\n\n        return NGX_CONF_OK;\n    }\n\n    if (ngx_strncasecmp(url->data, (u_char *) \"grpc://\", 7) == 0) {\n        add = 7;\n\n    } else if (ngx_strncasecmp(url->data, (u_char *) \"grpcs://\", 8) == 0) {\n\n#if (NGX_HTTP_SSL)\n        glcf->ssl = 1;\n\n        add = 8;\n#else\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"grpcs protocol requires SSL support\");\n        return NGX_CONF_ERROR;\n#endif\n\n    } else {\n        add = 0;\n    }\n\n    ngx_memzero(&u, sizeof(ngx_url_t));\n\n    u.url.len = url->len - add;\n    u.url.data = url->data + add;\n    u.no_resolve = 1;\n\n    glcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0);\n    if (glcf->upstream.upstream == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (u.family != AF_UNIX) {\n\n        if (u.no_port) {\n            glcf->host = u.host;\n\n        } else {\n            glcf->host.len = u.host.len + 1 + u.port_text.len;\n            glcf->host.data = u.host.data;\n        }\n\n    } else {\n        ngx_str_set(&glcf->host, \"localhost\");\n    }\n\n    return NGX_CONF_OK;\n}\n\n\n#if (NGX_HTTP_SSL)\n\nstatic char *\nngx_http_grpc_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_grpc_loc_conf_t *glcf = conf;\n\n    ngx_str_t  *value;\n\n    if (glcf->upstream.ssl_passwords != NGX_CONF_UNSET_PTR) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    glcf->upstream.ssl_passwords = ngx_ssl_read_password_file(cf, &value[1]);\n\n    if (glcf->upstream.ssl_passwords == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_grpc_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#else\n    return NGX_CONF_OK;\n#endif\n}\n\n\nstatic ngx_int_t\nngx_http_grpc_merge_ssl(ngx_conf_t *cf, ngx_http_grpc_loc_conf_t *conf,\n    ngx_http_grpc_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->upstream.ssl_certificate == NGX_CONF_UNSET_PTR\n        && conf->upstream.ssl_certificate_key == NGX_CONF_UNSET_PTR\n        && conf->upstream.ssl_passwords == NGX_CONF_UNSET_PTR\n        && conf->upstream.ssl_verify == NGX_CONF_UNSET\n        && conf->ssl_verify_depth == NGX_CONF_UNSET_UINT\n        && conf->ssl_trusted_certificate.data == NULL\n        && conf->ssl_crl.data == NULL\n        && conf->upstream.ssl_session_reuse == NGX_CONF_UNSET\n        && conf->ssl_conf_commands == NGX_CONF_UNSET_PTR)\n    {\n        if (prev->upstream.ssl) {\n            conf->upstream.ssl = prev->upstream.ssl;\n            return NGX_OK;\n        }\n\n        preserve = 1;\n\n    } else {\n        preserve = 0;\n    }\n\n    conf->upstream.ssl = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_t));\n    if (conf->upstream.ssl == NULL) {\n        return NGX_ERROR;\n    }\n\n    conf->upstream.ssl->log = cf->log;\n\n    /*\n     * special handling to preserve conf->upstream.ssl\n     * in the \"http\" section to inherit it to all servers\n     */\n\n    if (preserve) {\n        prev->upstream.ssl = conf->upstream.ssl;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_grpc_set_ssl(ngx_conf_t *cf, ngx_http_grpc_loc_conf_t *glcf)\n{\n    ngx_pool_cleanup_t  *cln;\n\n    if (glcf->upstream.ssl->ctx) {\n        return NGX_OK;\n    }\n\n    if (ngx_ssl_create(glcf->upstream.ssl, glcf->ssl_protocols, NULL)\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    cln = ngx_pool_cleanup_add(cf->pool, 0);\n    if (cln == NULL) {\n        ngx_ssl_cleanup_ctx(glcf->upstream.ssl);\n        return NGX_ERROR;\n    }\n\n    cln->handler = ngx_ssl_cleanup_ctx;\n    cln->data = glcf->upstream.ssl;\n\n    if (ngx_ssl_ciphers(cf, glcf->upstream.ssl, &glcf->ssl_ciphers, 0)\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    if (glcf->upstream.ssl_certificate\n        && glcf->upstream.ssl_certificate->value.len)\n    {\n        if (glcf->upstream.ssl_certificate_key == NULL) {\n            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                          \"no \\\"grpc_ssl_certificate_key\\\" is defined \"\n                          \"for certificate \\\"%V\\\"\",\n                          &glcf->upstream.ssl_certificate->value);\n            return NGX_ERROR;\n        }\n\n        if (glcf->upstream.ssl_certificate->lengths\n            || glcf->upstream.ssl_certificate_key->lengths)\n        {\n            glcf->upstream.ssl_passwords =\n                  ngx_ssl_preserve_passwords(cf, glcf->upstream.ssl_passwords);\n            if (glcf->upstream.ssl_passwords == NULL) {\n                return NGX_ERROR;\n            }\n\n        } else {\n#if (T_NGX_SSL_NTLS)\n            if (ngx_ssl_certificate(cf, glcf->upstream.ssl,\n                                    &glcf->upstream.ssl_certificate->value,\n                                    &glcf->upstream.ssl_certificate_key->value,\n                                    glcf->upstream.ssl_passwords,\n                                    SSL_NORMAL_CERT)\n#else\n            if (ngx_ssl_certificate(cf, glcf->upstream.ssl,\n                                    &glcf->upstream.ssl_certificate->value,\n                                    &glcf->upstream.ssl_certificate_key->value,\n                                    glcf->upstream.ssl_passwords)\n#endif\n                != NGX_OK)\n            {\n                return NGX_ERROR;\n            }\n        }\n    }\n\n#if (T_NGX_SSL_NTLS)\n    glcf->upstream.tls_method = SSL_CTX_get_ssl_method(glcf->upstream.ssl->ctx);\n    if (glcf->upstream.enc_certificate.len) {\n\n        if (glcf->upstream.enc_certificate_key.len == 0) {\n            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                          \"no \\\"grpc_ssl_enc_certificate_key\\\" is defined \"\n                          \"for certificate \\\"%V\\\"\",\n                          &glcf->upstream.enc_certificate);\n            return NGX_ERROR;\n        }\n\n        if (ngx_ssl_certificate(cf, glcf->upstream.ssl,\n                                &glcf->upstream.enc_certificate,\n                                &glcf->upstream.enc_certificate_key,\n                                glcf->upstream.ssl_passwords,\n                                SSL_ENC_CERT)\n            != NGX_OK)\n        {\n            return NGX_ERROR;\n        }\n    }\n\n    if (glcf->upstream.sign_certificate.len) {\n\n        if (glcf->upstream.sign_certificate_key.len == 0) {\n            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                          \"no \\\"grpc_ssl_sign_certificate_key\\\" is defined \"\n                          \"for certificate \\\"%V\\\"\",\n                          &glcf->upstream.sign_certificate);\n            return NGX_ERROR;\n        }\n\n        if (ngx_ssl_certificate(cf, glcf->upstream.ssl,\n                                &glcf->upstream.sign_certificate,\n                                &glcf->upstream.sign_certificate_key,\n                                glcf->upstream.ssl_passwords,\n                                SSL_SIGN_CERT)\n            != NGX_OK)\n        {\n            return NGX_ERROR;\n        }\n    }\n#endif\n\n    if (glcf->upstream.ssl_verify) {\n        if (glcf->ssl_trusted_certificate.len == 0) {\n            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                      \"no grpc_ssl_trusted_certificate for grpc_ssl_verify\");\n            return NGX_ERROR;\n        }\n\n        if (ngx_ssl_trusted_certificate(cf, glcf->upstream.ssl,\n                                        &glcf->ssl_trusted_certificate,\n                                        glcf->ssl_verify_depth)\n            != NGX_OK)\n        {\n            return NGX_ERROR;\n        }\n\n        if (ngx_ssl_crl(cf, glcf->upstream.ssl, &glcf->ssl_crl) != NGX_OK) {\n            return NGX_ERROR;\n        }\n    }\n\n    if (ngx_ssl_client_session_cache(cf, glcf->upstream.ssl,\n                                     glcf->upstream.ssl_session_reuse)\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation\n\n    if (SSL_CTX_set_alpn_protos(glcf->upstream.ssl->ctx,\n                                (u_char *) \"\\x02h2\", 3)\n        != 0)\n    {\n        ngx_ssl_error(NGX_LOG_EMERG, cf->log, 0,\n                      \"SSL_CTX_set_alpn_protos() failed\");\n        return NGX_ERROR;\n    }\n\n#endif\n\n    if (ngx_ssl_conf_commands(cf, glcf->upstream.ssl, glcf->ssl_conf_commands)\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n#endif\n"
  },
  {
    "path": "src/http/modules/ngx_http_gunzip_filter_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Maxim Dounin\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n#include <zlib.h>\n\n\ntypedef struct {\n    ngx_flag_t           enable;\n    ngx_bufs_t           bufs;\n} ngx_http_gunzip_conf_t;\n\n\ntypedef struct {\n    ngx_chain_t         *in;\n    ngx_chain_t         *free;\n    ngx_chain_t         *busy;\n    ngx_chain_t         *out;\n    ngx_chain_t        **last_out;\n\n    ngx_buf_t           *in_buf;\n    ngx_buf_t           *out_buf;\n    ngx_int_t            bufs;\n\n    unsigned             started:1;\n    unsigned             flush:4;\n    unsigned             redo:1;\n    unsigned             done:1;\n    unsigned             nomem:1;\n\n    z_stream             zstream;\n    ngx_http_request_t  *request;\n} ngx_http_gunzip_ctx_t;\n\n\nstatic ngx_int_t ngx_http_gunzip_filter_inflate_start(ngx_http_request_t *r,\n    ngx_http_gunzip_ctx_t *ctx);\nstatic ngx_int_t ngx_http_gunzip_filter_add_data(ngx_http_request_t *r,\n    ngx_http_gunzip_ctx_t *ctx);\nstatic ngx_int_t ngx_http_gunzip_filter_get_buf(ngx_http_request_t *r,\n    ngx_http_gunzip_ctx_t *ctx);\nstatic ngx_int_t ngx_http_gunzip_filter_inflate(ngx_http_request_t *r,\n    ngx_http_gunzip_ctx_t *ctx);\nstatic ngx_int_t ngx_http_gunzip_filter_inflate_end(ngx_http_request_t *r,\n    ngx_http_gunzip_ctx_t *ctx);\n\nstatic void *ngx_http_gunzip_filter_alloc(void *opaque, u_int items,\n    u_int size);\nstatic void ngx_http_gunzip_filter_free(void *opaque, void *address);\n\nstatic ngx_int_t ngx_http_gunzip_filter_init(ngx_conf_t *cf);\nstatic void *ngx_http_gunzip_create_conf(ngx_conf_t *cf);\nstatic char *ngx_http_gunzip_merge_conf(ngx_conf_t *cf,\n    void *parent, void *child);\n\n\nstatic ngx_command_t  ngx_http_gunzip_filter_commands[] = {\n\n    { ngx_string(\"gunzip\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_gunzip_conf_t, enable),\n      NULL },\n\n    { ngx_string(\"gunzip_buffers\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,\n      ngx_conf_set_bufs_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_gunzip_conf_t, bufs),\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_gunzip_filter_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    ngx_http_gunzip_filter_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    ngx_http_gunzip_create_conf,           /* create location configuration */\n    ngx_http_gunzip_merge_conf             /* merge location configuration */\n};\n\n\nngx_module_t  ngx_http_gunzip_filter_module = {\n    NGX_MODULE_V1,\n    &ngx_http_gunzip_filter_module_ctx,    /* module context */\n    ngx_http_gunzip_filter_commands,       /* 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_http_output_header_filter_pt  ngx_http_next_header_filter;\nstatic ngx_http_output_body_filter_pt    ngx_http_next_body_filter;\n\n\nstatic ngx_int_t\nngx_http_gunzip_header_filter(ngx_http_request_t *r)\n{\n    ngx_http_gunzip_ctx_t   *ctx;\n    ngx_http_gunzip_conf_t  *conf;\n\n    conf = ngx_http_get_module_loc_conf(r, ngx_http_gunzip_filter_module);\n\n    /* TODO support multiple content-codings */\n    /* TODO always gunzip - due to configuration or module request */\n    /* TODO ignore content encoding? */\n\n    if (!conf->enable\n        || r->headers_out.content_encoding == NULL\n        || r->headers_out.content_encoding->value.len != 4\n        || ngx_strncasecmp(r->headers_out.content_encoding->value.data,\n                           (u_char *) \"gzip\", 4) != 0)\n    {\n        return ngx_http_next_header_filter(r);\n    }\n\n    r->gzip_vary = 1;\n\n    if (!r->gzip_tested) {\n        if (ngx_http_gzip_ok(r) == NGX_OK) {\n            return ngx_http_next_header_filter(r);\n        }\n\n    } else if (r->gzip_ok) {\n        return ngx_http_next_header_filter(r);\n    }\n\n    ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_gunzip_ctx_t));\n    if (ctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_http_set_ctx(r, ctx, ngx_http_gunzip_filter_module);\n\n    ctx->request = r;\n\n    r->filter_need_in_memory = 1;\n\n    r->headers_out.content_encoding->hash = 0;\n    r->headers_out.content_encoding = NULL;\n\n    ngx_http_clear_content_length(r);\n    ngx_http_clear_accept_ranges(r);\n    ngx_http_weak_etag(r);\n\n    return ngx_http_next_header_filter(r);\n}\n\n\nstatic ngx_int_t\nngx_http_gunzip_body_filter(ngx_http_request_t *r, ngx_chain_t *in)\n{\n    int                     rc;\n    ngx_uint_t              flush;\n    ngx_chain_t            *cl;\n    ngx_http_gunzip_ctx_t  *ctx;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_gunzip_filter_module);\n\n    if (ctx == NULL || ctx->done) {\n        return ngx_http_next_body_filter(r, in);\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http gunzip filter\");\n\n    if (!ctx->started) {\n        if (ngx_http_gunzip_filter_inflate_start(r, ctx) != NGX_OK) {\n            goto failed;\n        }\n    }\n\n    if (in) {\n        if (ngx_chain_add_copy(r->pool, &ctx->in, in) != NGX_OK) {\n            goto failed;\n        }\n    }\n\n    if (ctx->nomem) {\n\n        /* flush busy buffers */\n\n        if (ngx_http_next_body_filter(r, NULL) == NGX_ERROR) {\n            goto failed;\n        }\n\n        cl = NULL;\n\n        ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &cl,\n                                (ngx_buf_tag_t) &ngx_http_gunzip_filter_module);\n        ctx->nomem = 0;\n        flush = 0;\n\n    } else {\n        flush = ctx->busy ? 1 : 0;\n    }\n\n    for ( ;; ) {\n\n        /* cycle while we can write to a client */\n\n        for ( ;; ) {\n\n            /* cycle while there is data to feed zlib and ... */\n\n            rc = ngx_http_gunzip_filter_add_data(r, ctx);\n\n            if (rc == NGX_DECLINED) {\n                break;\n            }\n\n            if (rc == NGX_AGAIN) {\n                continue;\n            }\n\n\n            /* ... there are buffers to write zlib output */\n\n            rc = ngx_http_gunzip_filter_get_buf(r, ctx);\n\n            if (rc == NGX_DECLINED) {\n                break;\n            }\n\n            if (rc == NGX_ERROR) {\n                goto failed;\n            }\n\n            rc = ngx_http_gunzip_filter_inflate(r, ctx);\n\n            if (rc == NGX_OK) {\n                break;\n            }\n\n            if (rc == NGX_ERROR) {\n                goto failed;\n            }\n\n            /* rc == NGX_AGAIN */\n        }\n\n        if (ctx->out == NULL && !flush) {\n            return ctx->busy ? NGX_AGAIN : NGX_OK;\n        }\n\n        rc = ngx_http_next_body_filter(r, ctx->out);\n\n        if (rc == NGX_ERROR) {\n            goto failed;\n        }\n\n        ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &ctx->out,\n                                (ngx_buf_tag_t) &ngx_http_gunzip_filter_module);\n        ctx->last_out = &ctx->out;\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"gunzip out: %p\", ctx->out);\n\n        ctx->nomem = 0;\n        flush = 0;\n\n        if (ctx->done) {\n            return rc;\n        }\n    }\n\n    /* unreachable */\n\nfailed:\n\n    ctx->done = 1;\n\n    return NGX_ERROR;\n}\n\n\nstatic ngx_int_t\nngx_http_gunzip_filter_inflate_start(ngx_http_request_t *r,\n    ngx_http_gunzip_ctx_t *ctx)\n{\n    int  rc;\n\n    ctx->zstream.next_in = Z_NULL;\n    ctx->zstream.avail_in = 0;\n\n    ctx->zstream.zalloc = ngx_http_gunzip_filter_alloc;\n    ctx->zstream.zfree = ngx_http_gunzip_filter_free;\n    ctx->zstream.opaque = ctx;\n\n    /* windowBits +16 to decode gzip, zlib 1.2.0.4+ */\n    rc = inflateInit2(&ctx->zstream, MAX_WBITS + 16);\n\n    if (rc != Z_OK) {\n        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,\n                      \"inflateInit2() failed: %d\", rc);\n        return NGX_ERROR;\n    }\n\n    ctx->started = 1;\n\n    ctx->last_out = &ctx->out;\n    ctx->flush = Z_NO_FLUSH;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_gunzip_filter_add_data(ngx_http_request_t *r,\n    ngx_http_gunzip_ctx_t *ctx)\n{\n    if (ctx->zstream.avail_in || ctx->flush != Z_NO_FLUSH || ctx->redo) {\n        return NGX_OK;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"gunzip in: %p\", ctx->in);\n\n    if (ctx->in == NULL) {\n        return NGX_DECLINED;\n    }\n\n    ctx->in_buf = ctx->in->buf;\n    ctx->in = ctx->in->next;\n\n    ctx->zstream.next_in = ctx->in_buf->pos;\n    ctx->zstream.avail_in = ctx->in_buf->last - ctx->in_buf->pos;\n\n    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"gunzip in_buf:%p ni:%p ai:%ud\",\n                   ctx->in_buf,\n                   ctx->zstream.next_in, ctx->zstream.avail_in);\n\n    if (ctx->in_buf->last_buf || ctx->in_buf->last_in_chain) {\n        ctx->flush = Z_FINISH;\n\n    } else if (ctx->in_buf->flush) {\n        ctx->flush = Z_SYNC_FLUSH;\n\n    } else if (ctx->zstream.avail_in == 0) {\n        /* ctx->flush == Z_NO_FLUSH */\n        return NGX_AGAIN;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_gunzip_filter_get_buf(ngx_http_request_t *r,\n    ngx_http_gunzip_ctx_t *ctx)\n{\n    ngx_http_gunzip_conf_t  *conf;\n\n    if (ctx->zstream.avail_out) {\n        return NGX_OK;\n    }\n\n    conf = ngx_http_get_module_loc_conf(r, ngx_http_gunzip_filter_module);\n\n    if (ctx->free) {\n        ctx->out_buf = ctx->free->buf;\n        ctx->free = ctx->free->next;\n\n        ctx->out_buf->flush = 0;\n\n    } else if (ctx->bufs < conf->bufs.num) {\n\n        ctx->out_buf = ngx_create_temp_buf(r->pool, conf->bufs.size);\n        if (ctx->out_buf == NULL) {\n            return NGX_ERROR;\n        }\n\n        ctx->out_buf->tag = (ngx_buf_tag_t) &ngx_http_gunzip_filter_module;\n        ctx->out_buf->recycled = 1;\n        ctx->bufs++;\n\n    } else {\n        ctx->nomem = 1;\n        return NGX_DECLINED;\n    }\n\n    ctx->zstream.next_out = ctx->out_buf->pos;\n    ctx->zstream.avail_out = conf->bufs.size;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_gunzip_filter_inflate(ngx_http_request_t *r,\n    ngx_http_gunzip_ctx_t *ctx)\n{\n    int           rc;\n    ngx_buf_t    *b;\n    ngx_chain_t  *cl;\n\n    ngx_log_debug6(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"inflate in: ni:%p no:%p ai:%ud ao:%ud fl:%d redo:%d\",\n                   ctx->zstream.next_in, ctx->zstream.next_out,\n                   ctx->zstream.avail_in, ctx->zstream.avail_out,\n                   ctx->flush, ctx->redo);\n\n    rc = inflate(&ctx->zstream, ctx->flush);\n\n    if (rc != Z_OK && rc != Z_STREAM_END && rc != Z_BUF_ERROR) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"inflate() failed: %d, %d\", ctx->flush, rc);\n        return NGX_ERROR;\n    }\n\n    ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"inflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d\",\n                   ctx->zstream.next_in, ctx->zstream.next_out,\n                   ctx->zstream.avail_in, ctx->zstream.avail_out,\n                   rc);\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"gunzip in_buf:%p pos:%p\",\n                   ctx->in_buf, ctx->in_buf->pos);\n\n    if (ctx->zstream.next_in) {\n        ctx->in_buf->pos = ctx->zstream.next_in;\n\n        if (ctx->zstream.avail_in == 0) {\n            ctx->zstream.next_in = NULL;\n        }\n    }\n\n    ctx->out_buf->last = ctx->zstream.next_out;\n\n    if (ctx->zstream.avail_out == 0) {\n\n        /* zlib wants to output some more data */\n\n        cl = ngx_alloc_chain_link(r->pool);\n        if (cl == NULL) {\n            return NGX_ERROR;\n        }\n\n        cl->buf = ctx->out_buf;\n        cl->next = NULL;\n        *ctx->last_out = cl;\n        ctx->last_out = &cl->next;\n\n        ctx->redo = 1;\n\n        return NGX_AGAIN;\n    }\n\n    ctx->redo = 0;\n\n    if (ctx->flush == Z_SYNC_FLUSH) {\n\n        ctx->flush = Z_NO_FLUSH;\n\n        cl = ngx_alloc_chain_link(r->pool);\n        if (cl == NULL) {\n            return NGX_ERROR;\n        }\n\n        b = ctx->out_buf;\n\n        if (ngx_buf_size(b) == 0) {\n\n            b = ngx_calloc_buf(ctx->request->pool);\n            if (b == NULL) {\n                return NGX_ERROR;\n            }\n\n        } else {\n            ctx->zstream.avail_out = 0;\n        }\n\n        b->flush = 1;\n\n        cl->buf = b;\n        cl->next = NULL;\n        *ctx->last_out = cl;\n        ctx->last_out = &cl->next;\n\n        return NGX_OK;\n    }\n\n    if (ctx->flush == Z_FINISH && ctx->zstream.avail_in == 0) {\n\n        if (rc != Z_STREAM_END) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"inflate() returned %d on response end\", rc);\n            return NGX_ERROR;\n        }\n\n        if (ngx_http_gunzip_filter_inflate_end(r, ctx) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        return NGX_OK;\n    }\n\n    if (rc == Z_STREAM_END && ctx->zstream.avail_in > 0) {\n\n        rc = inflateReset(&ctx->zstream);\n\n        if (rc != Z_OK) {\n            ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,\n                          \"inflateReset() failed: %d\", rc);\n            return NGX_ERROR;\n        }\n\n        ctx->redo = 1;\n\n        return NGX_AGAIN;\n    }\n\n    if (ctx->in == NULL) {\n\n        b = ctx->out_buf;\n\n        if (ngx_buf_size(b) == 0) {\n            return NGX_OK;\n        }\n\n        cl = ngx_alloc_chain_link(r->pool);\n        if (cl == NULL) {\n            return NGX_ERROR;\n        }\n\n        ctx->zstream.avail_out = 0;\n\n        cl->buf = b;\n        cl->next = NULL;\n        *ctx->last_out = cl;\n        ctx->last_out = &cl->next;\n\n        return NGX_OK;\n    }\n\n    return NGX_AGAIN;\n}\n\n\nstatic ngx_int_t\nngx_http_gunzip_filter_inflate_end(ngx_http_request_t *r,\n    ngx_http_gunzip_ctx_t *ctx)\n{\n    int           rc;\n    ngx_buf_t    *b;\n    ngx_chain_t  *cl;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"gunzip inflate end\");\n\n    rc = inflateEnd(&ctx->zstream);\n\n    if (rc != Z_OK) {\n        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,\n                      \"inflateEnd() failed: %d\", rc);\n        return NGX_ERROR;\n    }\n\n    b = ctx->out_buf;\n\n    if (ngx_buf_size(b) == 0) {\n\n        b = ngx_calloc_buf(ctx->request->pool);\n        if (b == NULL) {\n            return NGX_ERROR;\n        }\n    }\n\n    cl = ngx_alloc_chain_link(r->pool);\n    if (cl == NULL) {\n        return NGX_ERROR;\n    }\n\n    cl->buf = b;\n    cl->next = NULL;\n    *ctx->last_out = cl;\n    ctx->last_out = &cl->next;\n\n    b->last_buf = (r == r->main) ? 1 : 0;\n    b->last_in_chain = 1;\n    b->sync = 1;\n\n    ctx->done = 1;\n\n    return NGX_OK;\n}\n\n\nstatic void *\nngx_http_gunzip_filter_alloc(void *opaque, u_int items, u_int size)\n{\n    ngx_http_gunzip_ctx_t *ctx = opaque;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ctx->request->connection->log, 0,\n                   \"gunzip alloc: n:%ud s:%ud\",\n                   items, size);\n\n    return ngx_palloc(ctx->request->pool, items * size);\n}\n\n\nstatic void\nngx_http_gunzip_filter_free(void *opaque, void *address)\n{\n#if 0\n    ngx_http_gunzip_ctx_t *ctx = opaque;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->request->connection->log, 0,\n                   \"gunzip free: %p\", address);\n#endif\n}\n\n\nstatic void *\nngx_http_gunzip_create_conf(ngx_conf_t *cf)\n{\n    ngx_http_gunzip_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_gunzip_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc():\n     *\n     *     conf->bufs.num = 0;\n     */\n\n    conf->enable = NGX_CONF_UNSET;\n\n    return conf;\n}\n\n\nstatic char *\nngx_http_gunzip_merge_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_http_gunzip_conf_t *prev = parent;\n    ngx_http_gunzip_conf_t *conf = child;\n\n    ngx_conf_merge_value(conf->enable, prev->enable, 0);\n\n    ngx_conf_merge_bufs_value(conf->bufs, prev->bufs,\n                              (128 * 1024) / ngx_pagesize, ngx_pagesize);\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_gunzip_filter_init(ngx_conf_t *cf)\n{\n    ngx_http_next_header_filter = ngx_http_top_header_filter;\n    ngx_http_top_header_filter = ngx_http_gunzip_header_filter;\n\n    ngx_http_next_body_filter = ngx_http_top_body_filter;\n    ngx_http_top_body_filter = ngx_http_gunzip_body_filter;\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "src/http/modules/ngx_http_gzip_filter_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n#include <zlib.h>\n\n\ntypedef struct {\n    ngx_flag_t           enable;\n    ngx_flag_t           no_buffer;\n#if (T_NGX_GZIP_CLEAR_ETAG)\n    ngx_flag_t           clear_etag;\n#endif\n\n    ngx_hash_t           types;\n\n    ngx_bufs_t           bufs;\n\n    size_t               postpone_gzipping;\n    ngx_int_t            level;\n    size_t               wbits;\n    size_t               memlevel;\n    ssize_t              min_length;\n\n    ngx_array_t         *types_keys;\n} ngx_http_gzip_conf_t;\n\n\ntypedef struct {\n    ngx_chain_t         *in;\n    ngx_chain_t         *free;\n    ngx_chain_t         *busy;\n    ngx_chain_t         *out;\n    ngx_chain_t        **last_out;\n\n    ngx_chain_t         *copied;\n    ngx_chain_t         *copy_buf;\n\n    ngx_buf_t           *in_buf;\n    ngx_buf_t           *out_buf;\n    ngx_int_t            bufs;\n\n    void                *preallocated;\n    char                *free_mem;\n    ngx_uint_t           allocated;\n\n    int                  wbits;\n    int                  memlevel;\n\n    unsigned             flush:4;\n    unsigned             redo:1;\n    unsigned             done:1;\n    unsigned             nomem:1;\n    unsigned             buffering:1;\n    unsigned             zlib_ng:1;\n    unsigned             state_allocated:1;\n\n    size_t               zin;\n    size_t               zout;\n\n    z_stream             zstream;\n    ngx_http_request_t  *request;\n} ngx_http_gzip_ctx_t;\n\n\nstatic void ngx_http_gzip_filter_memory(ngx_http_request_t *r,\n    ngx_http_gzip_ctx_t *ctx);\nstatic ngx_int_t ngx_http_gzip_filter_buffer(ngx_http_gzip_ctx_t *ctx,\n    ngx_chain_t *in);\nstatic ngx_int_t ngx_http_gzip_filter_deflate_start(ngx_http_request_t *r,\n    ngx_http_gzip_ctx_t *ctx);\nstatic ngx_int_t ngx_http_gzip_filter_add_data(ngx_http_request_t *r,\n    ngx_http_gzip_ctx_t *ctx);\nstatic ngx_int_t ngx_http_gzip_filter_get_buf(ngx_http_request_t *r,\n    ngx_http_gzip_ctx_t *ctx);\nstatic ngx_int_t ngx_http_gzip_filter_deflate(ngx_http_request_t *r,\n    ngx_http_gzip_ctx_t *ctx);\nstatic ngx_int_t ngx_http_gzip_filter_deflate_end(ngx_http_request_t *r,\n    ngx_http_gzip_ctx_t *ctx);\n\nstatic void *ngx_http_gzip_filter_alloc(void *opaque, u_int items,\n    u_int size);\nstatic void ngx_http_gzip_filter_free(void *opaque, void *address);\nstatic void ngx_http_gzip_filter_free_copy_buf(ngx_http_request_t *r,\n    ngx_http_gzip_ctx_t *ctx);\n\nstatic ngx_int_t ngx_http_gzip_add_variables(ngx_conf_t *cf);\nstatic ngx_int_t ngx_http_gzip_ratio_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\n\nstatic ngx_int_t ngx_http_gzip_filter_init(ngx_conf_t *cf);\nstatic void *ngx_http_gzip_create_conf(ngx_conf_t *cf);\nstatic char *ngx_http_gzip_merge_conf(ngx_conf_t *cf,\n    void *parent, void *child);\nstatic char *ngx_http_gzip_window(ngx_conf_t *cf, void *post, void *data);\nstatic char *ngx_http_gzip_hash(ngx_conf_t *cf, void *post, void *data);\n\n\nstatic ngx_conf_num_bounds_t  ngx_http_gzip_comp_level_bounds = {\n    ngx_conf_check_num_bounds, 1, 9\n};\n\nstatic ngx_conf_post_handler_pt  ngx_http_gzip_window_p = ngx_http_gzip_window;\nstatic ngx_conf_post_handler_pt  ngx_http_gzip_hash_p = ngx_http_gzip_hash;\n\n\nstatic ngx_command_t  ngx_http_gzip_filter_commands[] = {\n\n    { ngx_string(\"gzip\"),\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_gzip_conf_t, enable),\n      NULL },\n\n    { ngx_string(\"gzip_buffers\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,\n      ngx_conf_set_bufs_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_gzip_conf_t, bufs),\n      NULL },\n\n    { ngx_string(\"gzip_types\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,\n      ngx_http_types_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_gzip_conf_t, types_keys),\n      &ngx_http_html_default_types[0] },\n\n    { ngx_string(\"gzip_comp_level\"),\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_gzip_conf_t, level),\n      &ngx_http_gzip_comp_level_bounds },\n\n    { ngx_string(\"gzip_window\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_gzip_conf_t, wbits),\n      &ngx_http_gzip_window_p },\n\n    { ngx_string(\"gzip_hash\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_gzip_conf_t, memlevel),\n      &ngx_http_gzip_hash_p },\n\n    { ngx_string(\"postpone_gzipping\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_gzip_conf_t, postpone_gzipping),\n      NULL },\n\n    { ngx_string(\"gzip_no_buffer\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_gzip_conf_t, no_buffer),\n      NULL },\n\n    { ngx_string(\"gzip_min_length\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_gzip_conf_t, min_length),\n      NULL },\n\n#if (T_NGX_GZIP_CLEAR_ETAG)\n    { ngx_string(\"gzip_clear_etag\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_gzip_conf_t, clear_etag),\n      NULL },\n#endif\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_gzip_filter_module_ctx = {\n    ngx_http_gzip_add_variables,           /* preconfiguration */\n    ngx_http_gzip_filter_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    ngx_http_gzip_create_conf,             /* create location configuration */\n    ngx_http_gzip_merge_conf               /* merge location configuration */\n};\n\n\nngx_module_t  ngx_http_gzip_filter_module = {\n    NGX_MODULE_V1,\n    &ngx_http_gzip_filter_module_ctx,      /* module context */\n    ngx_http_gzip_filter_commands,         /* 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_str_t  ngx_http_gzip_ratio = ngx_string(\"gzip_ratio\");\n\nstatic ngx_http_output_header_filter_pt  ngx_http_next_header_filter;\nstatic ngx_http_output_body_filter_pt    ngx_http_next_body_filter;\n\nstatic ngx_uint_t  ngx_http_gzip_assume_zlib_ng;\n\n\nstatic ngx_int_t\nngx_http_gzip_header_filter(ngx_http_request_t *r)\n{\n    ngx_table_elt_t       *h;\n    ngx_http_gzip_ctx_t   *ctx;\n    ngx_http_gzip_conf_t  *conf;\n\n    conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);\n\n    if (!conf->enable\n        || (r->headers_out.status != NGX_HTTP_OK\n            && r->headers_out.status != NGX_HTTP_FORBIDDEN\n            && r->headers_out.status != NGX_HTTP_NOT_FOUND)\n        || (r->headers_out.content_encoding\n            && r->headers_out.content_encoding->value.len)\n        || (r->headers_out.content_length_n != -1\n            && r->headers_out.content_length_n < conf->min_length)\n        || ngx_http_test_content_type(r, &conf->types) == NULL\n        || r->header_only)\n    {\n        return ngx_http_next_header_filter(r);\n    }\n\n    r->gzip_vary = 1;\n\n#if (NGX_HTTP_DEGRADATION)\n    {\n    ngx_http_core_loc_conf_t  *clcf;\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    if (clcf->gzip_disable_degradation && ngx_http_degraded(r)) {\n        return ngx_http_next_header_filter(r);\n    }\n    }\n#endif\n\n    if (!r->gzip_tested) {\n        if (ngx_http_gzip_ok(r) != NGX_OK) {\n            return ngx_http_next_header_filter(r);\n        }\n\n    } else if (!r->gzip_ok) {\n        return ngx_http_next_header_filter(r);\n    }\n\n    ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_gzip_ctx_t));\n    if (ctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_http_set_ctx(r, ctx, ngx_http_gzip_filter_module);\n\n    ctx->request = r;\n    ctx->buffering = (conf->postpone_gzipping != 0);\n\n    ngx_http_gzip_filter_memory(r, ctx);\n\n    h = ngx_list_push(&r->headers_out.headers);\n    if (h == NULL) {\n        return NGX_ERROR;\n    }\n\n    h->hash = 1;\n    h->next = NULL;\n    ngx_str_set(&h->key, \"Content-Encoding\");\n    ngx_str_set(&h->value, \"gzip\");\n    r->headers_out.content_encoding = h;\n\n    r->main_filter_need_in_memory = 1;\n\n    ngx_http_clear_content_length(r);\n    ngx_http_clear_accept_ranges(r);\n#if (T_NGX_GZIP_CLEAR_ETAG)\n    if (conf->clear_etag) {\n        ngx_http_clear_etag(r);\n    } else\n#endif\n    ngx_http_weak_etag(r);\n\n    return ngx_http_next_header_filter(r);\n}\n\n\nstatic ngx_int_t\nngx_http_gzip_body_filter(ngx_http_request_t *r, ngx_chain_t *in)\n{\n    int                   rc;\n    ngx_uint_t            flush;\n    ngx_chain_t          *cl;\n    ngx_http_gzip_ctx_t  *ctx;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_gzip_filter_module);\n\n    if (ctx == NULL || ctx->done || r->header_only) {\n        return ngx_http_next_body_filter(r, in);\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http gzip filter\");\n\n    if (ctx->buffering) {\n\n        /*\n         * With default memory settings zlib starts to output gzipped data\n         * only after it has got about 90K, so it makes sense to allocate\n         * zlib memory (200-400K) only after we have enough data to compress.\n         * Although we copy buffers, nevertheless for not big responses\n         * this allows to allocate zlib memory, to compress and to output\n         * the response in one step using hot CPU cache.\n         */\n\n        if (in) {\n            switch (ngx_http_gzip_filter_buffer(ctx, in)) {\n\n            case NGX_OK:\n                return NGX_OK;\n\n            case NGX_DONE:\n                in = NULL;\n                break;\n\n            default:  /* NGX_ERROR */\n                goto failed;\n            }\n\n        } else {\n            ctx->buffering = 0;\n        }\n    }\n\n    if (ctx->preallocated == NULL) {\n        if (ngx_http_gzip_filter_deflate_start(r, ctx) != NGX_OK) {\n            goto failed;\n        }\n    }\n\n    if (in) {\n        if (ngx_chain_add_copy(r->pool, &ctx->in, in) != NGX_OK) {\n            goto failed;\n        }\n\n        r->connection->buffered |= NGX_HTTP_GZIP_BUFFERED;\n    }\n\n    if (ctx->nomem) {\n\n        /* flush busy buffers */\n\n        if (ngx_http_next_body_filter(r, NULL) == NGX_ERROR) {\n            goto failed;\n        }\n\n        cl = NULL;\n\n        ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &cl,\n                                (ngx_buf_tag_t) &ngx_http_gzip_filter_module);\n        ctx->nomem = 0;\n        flush = 0;\n\n    } else {\n        flush = ctx->busy ? 1 : 0;\n    }\n\n    for ( ;; ) {\n\n        /* cycle while we can write to a client */\n\n        for ( ;; ) {\n\n            /* cycle while there is data to feed zlib and ... */\n\n            rc = ngx_http_gzip_filter_add_data(r, ctx);\n\n            if (rc == NGX_DECLINED) {\n                break;\n            }\n\n            if (rc == NGX_AGAIN) {\n                continue;\n            }\n\n\n            /* ... there are buffers to write zlib output */\n\n            rc = ngx_http_gzip_filter_get_buf(r, ctx);\n\n            if (rc == NGX_DECLINED) {\n                break;\n            }\n\n            if (rc == NGX_ERROR) {\n                goto failed;\n            }\n\n\n            rc = ngx_http_gzip_filter_deflate(r, ctx);\n\n            if (rc == NGX_OK) {\n                break;\n            }\n\n            if (rc == NGX_ERROR) {\n                goto failed;\n            }\n\n            /* rc == NGX_AGAIN */\n        }\n\n        if (ctx->out == NULL && !flush) {\n            ngx_http_gzip_filter_free_copy_buf(r, ctx);\n\n            return ctx->busy ? NGX_AGAIN : NGX_OK;\n        }\n\n        rc = ngx_http_next_body_filter(r, ctx->out);\n\n        if (rc == NGX_ERROR) {\n            goto failed;\n        }\n\n        ngx_http_gzip_filter_free_copy_buf(r, ctx);\n\n        ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &ctx->out,\n                                (ngx_buf_tag_t) &ngx_http_gzip_filter_module);\n        ctx->last_out = &ctx->out;\n\n        ctx->nomem = 0;\n        flush = 0;\n\n        if (ctx->done) {\n            return rc;\n        }\n    }\n\n    /* unreachable */\n\nfailed:\n\n    ctx->done = 1;\n\n    if (ctx->preallocated) {\n        deflateEnd(&ctx->zstream);\n\n        ngx_pfree(r->pool, ctx->preallocated);\n    }\n\n    ngx_http_gzip_filter_free_copy_buf(r, ctx);\n\n    return NGX_ERROR;\n}\n\n\nstatic void\nngx_http_gzip_filter_memory(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx)\n{\n    int                    wbits, memlevel;\n    ngx_http_gzip_conf_t  *conf;\n\n    conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);\n\n    wbits = conf->wbits;\n    memlevel = conf->memlevel;\n\n    if (r->headers_out.content_length_n > 0) {\n\n        /* the actual zlib window size is smaller by 262 bytes */\n\n        while (r->headers_out.content_length_n < ((1 << (wbits - 1)) - 262)) {\n            wbits--;\n            memlevel--;\n        }\n\n        if (memlevel < 1) {\n            memlevel = 1;\n        }\n    }\n\n    ctx->wbits = wbits;\n    ctx->memlevel = memlevel;\n\n    /*\n     * We preallocate a memory for zlib in one buffer (200K-400K), this\n     * decreases a number of malloc() and free() calls and also probably\n     * decreases a number of syscalls (sbrk()/mmap() and so on).\n     * Besides we free the memory as soon as a gzipping will complete\n     * and do not wait while a whole response will be sent to a client.\n     *\n     * 8K is for zlib deflate_state, it takes\n     *  *) 5816 bytes on i386 and sparc64 (32-bit mode)\n     *  *) 5920 bytes on amd64 and sparc64\n     *\n     * A zlib variant from Intel (https://github.com/jtkukunas/zlib)\n     * uses additional 16-byte padding in one of window-sized buffers.\n     */\n\n    if (!ngx_http_gzip_assume_zlib_ng) {\n        ctx->allocated = 8192 + 16 + (1 << (wbits + 2))\n                         + (1 << (memlevel + 9));\n\n    } else {\n        /*\n         * Another zlib variant, https://github.com/zlib-ng/zlib-ng.\n         * It used to force window bits to 13 for fast compression level,\n         * uses (64 + sizeof(void*)) additional space on all allocations\n         * for alignment, 16-byte padding in one of window-sized buffers,\n         * and 128K hash.\n         */\n\n        if (conf->level == 1) {\n            wbits = ngx_max(wbits, 13);\n        }\n\n        ctx->allocated = 8192 + 16 + (1 << (wbits + 2))\n                         + 131072 + (1 << (memlevel + 8))\n                         + 4 * (64 + sizeof(void*));\n        ctx->zlib_ng = 1;\n    }\n}\n\n\nstatic ngx_int_t\nngx_http_gzip_filter_buffer(ngx_http_gzip_ctx_t *ctx, ngx_chain_t *in)\n{\n    size_t                 size, buffered;\n    ngx_buf_t             *b, *buf;\n    ngx_chain_t           *cl, **ll;\n    ngx_http_request_t    *r;\n    ngx_http_gzip_conf_t  *conf;\n\n    r = ctx->request;\n\n    r->connection->buffered |= NGX_HTTP_GZIP_BUFFERED;\n\n    buffered = 0;\n    ll = &ctx->in;\n\n    for (cl = ctx->in; cl; cl = cl->next) {\n        buffered += cl->buf->last - cl->buf->pos;\n        ll = &cl->next;\n    }\n\n    conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);\n\n    while (in) {\n        cl = ngx_alloc_chain_link(r->pool);\n        if (cl == NULL) {\n            return NGX_ERROR;\n        }\n\n        b = in->buf;\n\n        size = b->last - b->pos;\n        buffered += size;\n\n        if (b->flush || b->last_buf || buffered > conf->postpone_gzipping) {\n            ctx->buffering = 0;\n        }\n\n        if (ctx->buffering && size) {\n\n            buf = ngx_create_temp_buf(r->pool, size);\n            if (buf == NULL) {\n                return NGX_ERROR;\n            }\n\n            buf->last = ngx_cpymem(buf->pos, b->pos, size);\n            b->pos = b->last;\n\n            buf->last_buf = b->last_buf;\n            buf->tag = (ngx_buf_tag_t) &ngx_http_gzip_filter_module;\n\n            cl->buf = buf;\n\n        } else {\n            cl->buf = b;\n        }\n\n        *ll = cl;\n        ll = &cl->next;\n        in = in->next;\n    }\n\n    *ll = NULL;\n\n    return ctx->buffering ? NGX_OK : NGX_DONE;\n}\n\n\nstatic ngx_int_t\nngx_http_gzip_filter_deflate_start(ngx_http_request_t *r,\n    ngx_http_gzip_ctx_t *ctx)\n{\n    int                    rc;\n    ngx_http_gzip_conf_t  *conf;\n\n    conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);\n\n    ctx->preallocated = ngx_palloc(r->pool, ctx->allocated);\n    if (ctx->preallocated == NULL) {\n        return NGX_ERROR;\n    }\n\n    ctx->free_mem = ctx->preallocated;\n\n    ctx->zstream.zalloc = ngx_http_gzip_filter_alloc;\n    ctx->zstream.zfree = ngx_http_gzip_filter_free;\n    ctx->zstream.opaque = ctx;\n\n    rc = deflateInit2(&ctx->zstream, (int) conf->level, Z_DEFLATED,\n                      ctx->wbits + 16, ctx->memlevel, Z_DEFAULT_STRATEGY);\n\n    if (rc != Z_OK) {\n        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,\n                      \"deflateInit2() failed: %d\", rc);\n        return NGX_ERROR;\n    }\n\n    ctx->last_out = &ctx->out;\n    ctx->flush = Z_NO_FLUSH;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_gzip_filter_add_data(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx)\n{\n    ngx_chain_t  *cl;\n\n    if (ctx->zstream.avail_in || ctx->flush != Z_NO_FLUSH || ctx->redo) {\n        return NGX_OK;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"gzip in: %p\", ctx->in);\n\n    if (ctx->in == NULL) {\n        return NGX_DECLINED;\n    }\n\n    if (ctx->copy_buf) {\n\n        /*\n         * to avoid CPU cache trashing we do not free() just quit buf,\n         * but postpone free()ing after zlib compressing and data output\n         */\n\n        ctx->copy_buf->next = ctx->copied;\n        ctx->copied = ctx->copy_buf;\n        ctx->copy_buf = NULL;\n    }\n\n    cl = ctx->in;\n    ctx->in_buf = cl->buf;\n    ctx->in = cl->next;\n\n    if (ctx->in_buf->tag == (ngx_buf_tag_t) &ngx_http_gzip_filter_module) {\n        ctx->copy_buf = cl;\n\n    } else {\n        ngx_free_chain(r->pool, cl);\n    }\n\n    ctx->zstream.next_in = ctx->in_buf->pos;\n    ctx->zstream.avail_in = ctx->in_buf->last - ctx->in_buf->pos;\n\n    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"gzip in_buf:%p ni:%p ai:%ud\",\n                   ctx->in_buf,\n                   ctx->zstream.next_in, ctx->zstream.avail_in);\n\n    if (ctx->in_buf->last_buf) {\n        ctx->flush = Z_FINISH;\n\n    } else if (ctx->in_buf->flush) {\n        ctx->flush = Z_SYNC_FLUSH;\n\n    } else if (ctx->zstream.avail_in == 0) {\n        /* ctx->flush == Z_NO_FLUSH */\n        return NGX_AGAIN;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_gzip_filter_get_buf(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx)\n{\n    ngx_chain_t           *cl;\n    ngx_http_gzip_conf_t  *conf;\n\n    if (ctx->zstream.avail_out) {\n        return NGX_OK;\n    }\n\n    conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);\n\n    if (ctx->free) {\n\n        cl = ctx->free;\n        ctx->out_buf = cl->buf;\n        ctx->free = cl->next;\n\n        ngx_free_chain(r->pool, cl);\n\n    } else if (ctx->bufs < conf->bufs.num) {\n\n        ctx->out_buf = ngx_create_temp_buf(r->pool, conf->bufs.size);\n        if (ctx->out_buf == NULL) {\n            return NGX_ERROR;\n        }\n\n        ctx->out_buf->tag = (ngx_buf_tag_t) &ngx_http_gzip_filter_module;\n        ctx->out_buf->recycled = 1;\n        ctx->bufs++;\n\n    } else {\n        ctx->nomem = 1;\n        return NGX_DECLINED;\n    }\n\n    ctx->zstream.next_out = ctx->out_buf->pos;\n    ctx->zstream.avail_out = conf->bufs.size;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_gzip_filter_deflate(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx)\n{\n    int                    rc;\n    ngx_buf_t             *b;\n    ngx_chain_t           *cl;\n    ngx_http_gzip_conf_t  *conf;\n\n    ngx_log_debug6(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                 \"deflate in: ni:%p no:%p ai:%ud ao:%ud fl:%d redo:%d\",\n                 ctx->zstream.next_in, ctx->zstream.next_out,\n                 ctx->zstream.avail_in, ctx->zstream.avail_out,\n                 ctx->flush, ctx->redo);\n\n    rc = deflate(&ctx->zstream, ctx->flush);\n\n    if (rc != Z_OK && rc != Z_STREAM_END && rc != Z_BUF_ERROR) {\n        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,\n                      \"deflate() failed: %d, %d\", ctx->flush, rc);\n        return NGX_ERROR;\n    }\n\n    ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"deflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d\",\n                   ctx->zstream.next_in, ctx->zstream.next_out,\n                   ctx->zstream.avail_in, ctx->zstream.avail_out,\n                   rc);\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"gzip in_buf:%p pos:%p\",\n                   ctx->in_buf, ctx->in_buf->pos);\n\n    if (ctx->zstream.next_in) {\n        ctx->in_buf->pos = ctx->zstream.next_in;\n\n        if (ctx->zstream.avail_in == 0) {\n            ctx->zstream.next_in = NULL;\n        }\n    }\n\n    ctx->out_buf->last = ctx->zstream.next_out;\n\n    if (ctx->zstream.avail_out == 0 && rc != Z_STREAM_END) {\n\n        /* zlib wants to output some more gzipped data */\n\n        cl = ngx_alloc_chain_link(r->pool);\n        if (cl == NULL) {\n            return NGX_ERROR;\n        }\n\n        cl->buf = ctx->out_buf;\n        cl->next = NULL;\n        *ctx->last_out = cl;\n        ctx->last_out = &cl->next;\n\n        ctx->redo = 1;\n\n        return NGX_AGAIN;\n    }\n\n    ctx->redo = 0;\n\n    if (ctx->flush == Z_SYNC_FLUSH) {\n\n        ctx->flush = Z_NO_FLUSH;\n\n        cl = ngx_alloc_chain_link(r->pool);\n        if (cl == NULL) {\n            return NGX_ERROR;\n        }\n\n        b = ctx->out_buf;\n\n        if (ngx_buf_size(b) == 0) {\n\n            b = ngx_calloc_buf(ctx->request->pool);\n            if (b == NULL) {\n                return NGX_ERROR;\n            }\n\n        } else {\n            ctx->zstream.avail_out = 0;\n        }\n\n        b->flush = 1;\n\n        cl->buf = b;\n        cl->next = NULL;\n        *ctx->last_out = cl;\n        ctx->last_out = &cl->next;\n\n        r->connection->buffered &= ~NGX_HTTP_GZIP_BUFFERED;\n\n        return NGX_OK;\n    }\n\n    if (rc == Z_STREAM_END) {\n\n        if (ngx_http_gzip_filter_deflate_end(r, ctx) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        return NGX_OK;\n    }\n\n    conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);\n\n    if (conf->no_buffer && ctx->in == NULL) {\n\n        cl = ngx_alloc_chain_link(r->pool);\n        if (cl == NULL) {\n            return NGX_ERROR;\n        }\n\n        cl->buf = ctx->out_buf;\n        cl->next = NULL;\n        *ctx->last_out = cl;\n        ctx->last_out = &cl->next;\n\n        return NGX_OK;\n    }\n\n    return NGX_AGAIN;\n}\n\n\nstatic ngx_int_t\nngx_http_gzip_filter_deflate_end(ngx_http_request_t *r,\n    ngx_http_gzip_ctx_t *ctx)\n{\n    int           rc;\n    ngx_buf_t    *b;\n    ngx_chain_t  *cl;\n\n    ctx->zin = ctx->zstream.total_in;\n    ctx->zout = ctx->zstream.total_out;\n\n    rc = deflateEnd(&ctx->zstream);\n\n    if (rc != Z_OK) {\n        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,\n                      \"deflateEnd() failed: %d\", rc);\n        return NGX_ERROR;\n    }\n\n    ngx_pfree(r->pool, ctx->preallocated);\n\n    cl = ngx_alloc_chain_link(r->pool);\n    if (cl == NULL) {\n        return NGX_ERROR;\n    }\n\n    b = ctx->out_buf;\n\n    if (ngx_buf_size(b) == 0) {\n        b->temporary = 0;\n    }\n\n    b->last_buf = 1;\n\n    cl->buf = b;\n    cl->next = NULL;\n    *ctx->last_out = cl;\n    ctx->last_out = &cl->next;\n\n    ctx->zstream.avail_in = 0;\n    ctx->zstream.avail_out = 0;\n\n    ctx->done = 1;\n\n    r->connection->buffered &= ~NGX_HTTP_GZIP_BUFFERED;\n\n    return NGX_OK;\n}\n\n\nstatic void *\nngx_http_gzip_filter_alloc(void *opaque, u_int items, u_int size)\n{\n    ngx_http_gzip_ctx_t *ctx = opaque;\n\n    void        *p;\n    ngx_uint_t   alloc;\n\n    alloc = items * size;\n\n    if (items == 1 && alloc % 512 != 0 && alloc < 8192\n        && !ctx->state_allocated)\n    {\n        /*\n         * The zlib deflate_state allocation, it takes about 6K,\n         * we allocate 8K.  Other allocations are divisible by 512.\n         */\n\n        ctx->state_allocated = 1;\n\n        alloc = 8192;\n    }\n\n    if (alloc <= ctx->allocated) {\n        p = ctx->free_mem;\n        ctx->free_mem += alloc;\n        ctx->allocated -= alloc;\n\n        ngx_log_debug4(NGX_LOG_DEBUG_HTTP, ctx->request->connection->log, 0,\n                       \"gzip alloc: n:%ud s:%ud a:%ui p:%p\",\n                       items, size, alloc, p);\n\n        return p;\n    }\n\n    if (ctx->zlib_ng) {\n        ngx_log_error(NGX_LOG_ALERT, ctx->request->connection->log, 0,\n                      \"gzip filter failed to use preallocated memory: \"\n                      \"%ud of %ui\", items * size, ctx->allocated);\n\n    } else {\n        ngx_http_gzip_assume_zlib_ng = 1;\n    }\n\n    p = ngx_palloc(ctx->request->pool, items * size);\n\n    return p;\n}\n\n\nstatic void\nngx_http_gzip_filter_free(void *opaque, void *address)\n{\n#if 0\n    ngx_http_gzip_ctx_t *ctx = opaque;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->request->connection->log, 0,\n                   \"gzip free: %p\", address);\n#endif\n}\n\n\nstatic void\nngx_http_gzip_filter_free_copy_buf(ngx_http_request_t *r,\n    ngx_http_gzip_ctx_t *ctx)\n{\n    ngx_chain_t  *cl;\n\n    for (cl = ctx->copied; cl; cl = cl->next) {\n        ngx_pfree(r->pool, cl->buf->start);\n    }\n\n    ctx->copied = NULL;\n}\n\n\nstatic ngx_int_t\nngx_http_gzip_add_variables(ngx_conf_t *cf)\n{\n    ngx_http_variable_t  *var;\n\n    var = ngx_http_add_variable(cf, &ngx_http_gzip_ratio, NGX_HTTP_VAR_NOHASH);\n    if (var == NULL) {\n        return NGX_ERROR;\n    }\n\n    var->get_handler = ngx_http_gzip_ratio_variable;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_gzip_ratio_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    ngx_uint_t            zint, zfrac;\n    ngx_http_gzip_ctx_t  *ctx;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_gzip_filter_module);\n\n    if (ctx == NULL || ctx->zout == 0) {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    v->data = ngx_pnalloc(r->pool, NGX_INT32_LEN + 3);\n    if (v->data == NULL) {\n        return NGX_ERROR;\n    }\n\n    zint = (ngx_uint_t) (ctx->zin / ctx->zout);\n    zfrac = (ngx_uint_t) ((ctx->zin * 100 / ctx->zout) % 100);\n\n    if ((ctx->zin * 1000 / ctx->zout) % 10 > 4) {\n\n        /* the rounding, e.g., 2.125 to 2.13 */\n\n        zfrac++;\n\n        if (zfrac > 99) {\n            zint++;\n            zfrac = 0;\n        }\n    }\n\n    v->len = ngx_sprintf(v->data, \"%ui.%02ui\", zint, zfrac) - v->data;\n\n    return NGX_OK;\n}\n\n\nstatic void *\nngx_http_gzip_create_conf(ngx_conf_t *cf)\n{\n    ngx_http_gzip_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_gzip_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc():\n     *\n     *     conf->bufs.num = 0;\n     *     conf->types = { NULL };\n     *     conf->types_keys = NULL;\n     */\n\n    conf->enable = NGX_CONF_UNSET;\n    conf->no_buffer = NGX_CONF_UNSET;\n#if (T_NGX_GZIP_CLEAR_ETAG)\n    conf->clear_etag = NGX_CONF_UNSET;\n#endif\n\n    conf->postpone_gzipping = NGX_CONF_UNSET_SIZE;\n    conf->level = NGX_CONF_UNSET;\n    conf->wbits = NGX_CONF_UNSET_SIZE;\n    conf->memlevel = NGX_CONF_UNSET_SIZE;\n    conf->min_length = NGX_CONF_UNSET;\n\n    return conf;\n}\n\n\nstatic char *\nngx_http_gzip_merge_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_http_gzip_conf_t *prev = parent;\n    ngx_http_gzip_conf_t *conf = child;\n\n    ngx_conf_merge_value(conf->enable, prev->enable, 0);\n    ngx_conf_merge_value(conf->no_buffer, prev->no_buffer, 0);\n#if (T_NGX_GZIP_CLEAR_ETAG)\n    ngx_conf_merge_value(conf->clear_etag, prev->clear_etag, 0);\n#endif\n\n    ngx_conf_merge_bufs_value(conf->bufs, prev->bufs,\n                              (128 * 1024) / ngx_pagesize, ngx_pagesize);\n\n    ngx_conf_merge_size_value(conf->postpone_gzipping, prev->postpone_gzipping,\n                              0);\n    ngx_conf_merge_value(conf->level, prev->level, 1);\n    ngx_conf_merge_size_value(conf->wbits, prev->wbits, MAX_WBITS);\n    ngx_conf_merge_size_value(conf->memlevel, prev->memlevel,\n                              MAX_MEM_LEVEL - 1);\n    ngx_conf_merge_value(conf->min_length, prev->min_length, 20);\n\n    if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,\n                             &prev->types_keys, &prev->types,\n                             ngx_http_html_default_types)\n        != NGX_OK)\n    {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_gzip_filter_init(ngx_conf_t *cf)\n{\n    ngx_http_next_header_filter = ngx_http_top_header_filter;\n    ngx_http_top_header_filter = ngx_http_gzip_header_filter;\n\n    ngx_http_next_body_filter = ngx_http_top_body_filter;\n    ngx_http_top_body_filter = ngx_http_gzip_body_filter;\n\n    return NGX_OK;\n}\n\n\nstatic char *\nngx_http_gzip_window(ngx_conf_t *cf, void *post, void *data)\n{\n    size_t *np = data;\n\n    size_t  wbits, wsize;\n\n    wbits = 15;\n\n    for (wsize = 32 * 1024; wsize > 256; wsize >>= 1) {\n\n        if (wsize == *np) {\n            *np = wbits;\n\n            return NGX_CONF_OK;\n        }\n\n        wbits--;\n    }\n\n    return \"must be 512, 1k, 2k, 4k, 8k, 16k, or 32k\";\n}\n\n\nstatic char *\nngx_http_gzip_hash(ngx_conf_t *cf, void *post, void *data)\n{\n    size_t *np = data;\n\n    size_t  memlevel, hsize;\n\n    memlevel = 9;\n\n    for (hsize = 128 * 1024; hsize > 256; hsize >>= 1) {\n\n        if (hsize == *np) {\n            *np = memlevel;\n\n            return NGX_CONF_OK;\n        }\n\n        memlevel--;\n    }\n\n    return \"must be 512, 1k, 2k, 4k, 8k, 16k, 32k, 64k, or 128k\";\n}\n"
  },
  {
    "path": "src/http/modules/ngx_http_gzip_static_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\n#define NGX_HTTP_GZIP_STATIC_OFF     0\n#define NGX_HTTP_GZIP_STATIC_ON      1\n#define NGX_HTTP_GZIP_STATIC_ALWAYS  2\n\n\ntypedef struct {\n    ngx_uint_t  enable;\n} ngx_http_gzip_static_conf_t;\n\n\nstatic ngx_int_t ngx_http_gzip_static_handler(ngx_http_request_t *r);\nstatic void *ngx_http_gzip_static_create_conf(ngx_conf_t *cf);\nstatic char *ngx_http_gzip_static_merge_conf(ngx_conf_t *cf, void *parent,\n    void *child);\nstatic ngx_int_t ngx_http_gzip_static_init(ngx_conf_t *cf);\n\n\nstatic ngx_conf_enum_t  ngx_http_gzip_static[] = {\n    { ngx_string(\"off\"), NGX_HTTP_GZIP_STATIC_OFF },\n    { ngx_string(\"on\"), NGX_HTTP_GZIP_STATIC_ON },\n    { ngx_string(\"always\"), NGX_HTTP_GZIP_STATIC_ALWAYS },\n    { ngx_null_string, 0 }\n};\n\n\nstatic ngx_command_t  ngx_http_gzip_static_commands[] = {\n\n    { ngx_string(\"gzip_static\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_enum_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_gzip_static_conf_t, enable),\n      &ngx_http_gzip_static },\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_gzip_static_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    ngx_http_gzip_static_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    ngx_http_gzip_static_create_conf,      /* create location configuration */\n    ngx_http_gzip_static_merge_conf        /* merge location configuration */\n};\n\n\nngx_module_t  ngx_http_gzip_static_module = {\n    NGX_MODULE_V1,\n    &ngx_http_gzip_static_module_ctx,      /* module context */\n    ngx_http_gzip_static_commands,         /* 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_gzip_static_handler(ngx_http_request_t *r)\n{\n    u_char                       *p;\n    size_t                        root;\n    ngx_str_t                     path;\n    ngx_int_t                     rc;\n    ngx_uint_t                    level;\n    ngx_log_t                    *log;\n    ngx_buf_t                    *b;\n    ngx_chain_t                   out;\n    ngx_table_elt_t              *h;\n    ngx_open_file_info_t          of;\n    ngx_http_core_loc_conf_t     *clcf;\n    ngx_http_gzip_static_conf_t  *gzcf;\n\n    if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {\n        return NGX_DECLINED;\n    }\n\n    if (r->uri.data[r->uri.len - 1] == '/') {\n        return NGX_DECLINED;\n    }\n\n    gzcf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_static_module);\n\n    if (gzcf->enable == NGX_HTTP_GZIP_STATIC_OFF) {\n        return NGX_DECLINED;\n    }\n\n    if (gzcf->enable == NGX_HTTP_GZIP_STATIC_ON) {\n        rc = ngx_http_gzip_ok(r);\n\n    } else {\n        /* always */\n        rc = NGX_OK;\n    }\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    if (!clcf->gzip_vary && rc != NGX_OK) {\n        return NGX_DECLINED;\n    }\n\n    log = r->connection->log;\n\n    p = ngx_http_map_uri_to_path(r, &path, &root, sizeof(\".gz\") - 1);\n    if (p == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    *p++ = '.';\n    *p++ = 'g';\n    *p++ = 'z';\n    *p = '\\0';\n\n    path.len = p - path.data;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,\n                   \"http filename: \\\"%s\\\"\", path.data);\n\n    ngx_memzero(&of, sizeof(ngx_open_file_info_t));\n\n    of.read_ahead = clcf->read_ahead;\n    of.directio = clcf->directio;\n    of.valid = clcf->open_file_cache_valid;\n    of.min_uses = clcf->open_file_cache_min_uses;\n    of.errors = clcf->open_file_cache_errors;\n    of.events = clcf->open_file_cache_events;\n\n    if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)\n        != NGX_OK)\n    {\n        switch (of.err) {\n\n        case 0:\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n\n        case NGX_ENOENT:\n        case NGX_ENOTDIR:\n        case NGX_ENAMETOOLONG:\n\n            return NGX_DECLINED;\n\n        case NGX_EACCES:\n#if (NGX_HAVE_OPENAT)\n        case NGX_EMLINK:\n        case NGX_ELOOP:\n#endif\n\n            level = NGX_LOG_ERR;\n            break;\n\n        default:\n\n            level = NGX_LOG_CRIT;\n            break;\n        }\n\n        ngx_log_error(level, log, of.err,\n                      \"%s \\\"%s\\\" failed\", of.failed, path.data);\n\n        return NGX_DECLINED;\n    }\n\n    if (gzcf->enable == NGX_HTTP_GZIP_STATIC_ON) {\n        r->gzip_vary = 1;\n\n        if (rc != NGX_OK) {\n            return NGX_DECLINED;\n        }\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, \"http static fd: %d\", of.fd);\n\n    if (of.is_dir) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, log, 0, \"http dir\");\n        return NGX_DECLINED;\n    }\n\n#if !(NGX_WIN32) /* the not regular files are probably Unix specific */\n\n    if (!of.is_file) {\n        ngx_log_error(NGX_LOG_CRIT, log, 0,\n                      \"\\\"%s\\\" is not a regular file\", path.data);\n\n        return NGX_HTTP_NOT_FOUND;\n    }\n\n#endif\n\n    r->root_tested = !r->error_page;\n\n    rc = ngx_http_discard_request_body(r);\n\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    log->action = \"sending response to client\";\n\n    r->headers_out.status = NGX_HTTP_OK;\n    r->headers_out.content_length_n = of.size;\n    r->headers_out.last_modified_time = of.mtime;\n\n    if (ngx_http_set_etag(r) != NGX_OK) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    if (ngx_http_set_content_type(r) != NGX_OK) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    h = ngx_list_push(&r->headers_out.headers);\n    if (h == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    h->hash = 1;\n    h->next = NULL;\n    ngx_str_set(&h->key, \"Content-Encoding\");\n    ngx_str_set(&h->value, \"gzip\");\n    r->headers_out.content_encoding = h;\n\n    r->allow_ranges = 1;\n\n    /* we need to allocate all before the header would be sent */\n\n    b = ngx_calloc_buf(r->pool);\n    if (b == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t));\n    if (b->file == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    rc = ngx_http_send_header(r);\n\n    if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {\n        return rc;\n    }\n\n    b->file_pos = 0;\n    b->file_last = of.size;\n\n    b->in_file = b->file_last ? 1 : 0;\n    b->last_buf = (r == r->main) ? 1 : 0;\n    b->last_in_chain = 1;\n    b->sync = (b->last_buf || b->in_file) ? 0 : 1;\n\n    b->file->fd = of.fd;\n    b->file->name = path;\n    b->file->log = log;\n    b->file->directio = of.is_directio;\n\n    out.buf = b;\n    out.next = NULL;\n\n    return ngx_http_output_filter(r, &out);\n}\n\n\nstatic void *\nngx_http_gzip_static_create_conf(ngx_conf_t *cf)\n{\n    ngx_http_gzip_static_conf_t  *conf;\n\n    conf = ngx_palloc(cf->pool, sizeof(ngx_http_gzip_static_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    conf->enable = NGX_CONF_UNSET_UINT;\n\n    return conf;\n}\n\n\nstatic char *\nngx_http_gzip_static_merge_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_http_gzip_static_conf_t *prev = parent;\n    ngx_http_gzip_static_conf_t *conf = child;\n\n    ngx_conf_merge_uint_value(conf->enable, prev->enable,\n                              NGX_HTTP_GZIP_STATIC_OFF);\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_gzip_static_init(ngx_conf_t *cf)\n{\n    ngx_http_handler_pt        *h;\n    ngx_http_core_main_conf_t  *cmcf;\n\n    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);\n\n    h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);\n    if (h == NULL) {\n        return NGX_ERROR;\n    }\n\n    *h = ngx_http_gzip_static_handler;\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "src/http/modules/ngx_http_headers_filter_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\ntypedef struct ngx_http_header_val_s  ngx_http_header_val_t;\n\ntypedef ngx_int_t (*ngx_http_set_header_pt)(ngx_http_request_t *r,\n    ngx_http_header_val_t *hv, ngx_str_t *value);\n\n\ntypedef struct {\n    ngx_str_t                  name;\n    ngx_uint_t                 offset;\n    ngx_http_set_header_pt     handler;\n} ngx_http_set_header_t;\n\n\nstruct ngx_http_header_val_s {\n    ngx_http_complex_value_t   value;\n    ngx_str_t                  key;\n    ngx_http_set_header_pt     handler;\n    ngx_uint_t                 offset;\n    ngx_uint_t                 always;  /* unsigned  always:1 */\n};\n\n\ntypedef enum {\n    NGX_HTTP_EXPIRES_OFF,\n    NGX_HTTP_EXPIRES_EPOCH,\n    NGX_HTTP_EXPIRES_MAX,\n    NGX_HTTP_EXPIRES_ACCESS,\n    NGX_HTTP_EXPIRES_MODIFIED,\n    NGX_HTTP_EXPIRES_DAILY,\n    NGX_HTTP_EXPIRES_UNSET\n} ngx_http_expires_t;\n\n\ntypedef struct {\n    ngx_http_expires_t         expires;\n    time_t                     expires_time;\n    ngx_http_complex_value_t  *expires_value;\n    ngx_array_t               *headers;\n    ngx_array_t               *trailers;\n} ngx_http_headers_conf_t;\n\n\nstatic ngx_int_t ngx_http_set_expires(ngx_http_request_t *r,\n    ngx_http_headers_conf_t *conf);\nstatic ngx_int_t ngx_http_parse_expires(ngx_str_t *value,\n    ngx_http_expires_t *expires, time_t *expires_time, char **err);\nstatic ngx_int_t ngx_http_add_multi_header_lines(ngx_http_request_t *r,\n    ngx_http_header_val_t *hv, ngx_str_t *value);\nstatic ngx_int_t ngx_http_add_header(ngx_http_request_t *r,\n    ngx_http_header_val_t *hv, ngx_str_t *value);\nstatic ngx_int_t ngx_http_set_last_modified(ngx_http_request_t *r,\n    ngx_http_header_val_t *hv, ngx_str_t *value);\nstatic ngx_int_t ngx_http_set_response_header(ngx_http_request_t *r,\n    ngx_http_header_val_t *hv, ngx_str_t *value);\n\nstatic void *ngx_http_headers_create_conf(ngx_conf_t *cf);\nstatic char *ngx_http_headers_merge_conf(ngx_conf_t *cf,\n    void *parent, void *child);\nstatic ngx_int_t ngx_http_headers_filter_init(ngx_conf_t *cf);\nstatic char *ngx_http_headers_expires(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_http_headers_add(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n\n\nstatic ngx_http_set_header_t  ngx_http_set_headers[] = {\n\n    { ngx_string(\"Cache-Control\"),\n                 offsetof(ngx_http_headers_out_t, cache_control),\n                 ngx_http_add_multi_header_lines },\n\n    { ngx_string(\"Link\"),\n                 offsetof(ngx_http_headers_out_t, link),\n                 ngx_http_add_multi_header_lines },\n\n    { ngx_string(\"Last-Modified\"),\n                 offsetof(ngx_http_headers_out_t, last_modified),\n                 ngx_http_set_last_modified },\n\n    { ngx_string(\"ETag\"),\n                 offsetof(ngx_http_headers_out_t, etag),\n                 ngx_http_set_response_header },\n\n    { ngx_null_string, 0, NULL }\n};\n\n\nstatic ngx_command_t  ngx_http_headers_filter_commands[] = {\n\n    { ngx_string(\"expires\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF\n                        |NGX_CONF_TAKE12,\n      ngx_http_headers_expires,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"add_header\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF\n                        |NGX_CONF_TAKE23,\n      ngx_http_headers_add,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_headers_conf_t, headers),\n      NULL },\n\n    { ngx_string(\"add_trailer\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF\n                        |NGX_CONF_TAKE23,\n      ngx_http_headers_add,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_headers_conf_t, trailers),\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_headers_filter_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    ngx_http_headers_filter_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    ngx_http_headers_create_conf,          /* create location configuration */\n    ngx_http_headers_merge_conf            /* merge location configuration */\n};\n\n\nngx_module_t  ngx_http_headers_filter_module = {\n    NGX_MODULE_V1,\n    &ngx_http_headers_filter_module_ctx,   /* module context */\n    ngx_http_headers_filter_commands,      /* 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_http_output_header_filter_pt  ngx_http_next_header_filter;\nstatic ngx_http_output_body_filter_pt    ngx_http_next_body_filter;\n\n\nstatic ngx_int_t\nngx_http_headers_filter(ngx_http_request_t *r)\n{\n    ngx_str_t                 value;\n    ngx_uint_t                i, safe_status;\n    ngx_http_header_val_t    *h;\n    ngx_http_headers_conf_t  *conf;\n\n    if (r != r->main) {\n        return ngx_http_next_header_filter(r);\n    }\n\n    conf = ngx_http_get_module_loc_conf(r, ngx_http_headers_filter_module);\n\n    if (conf->expires == NGX_HTTP_EXPIRES_OFF\n        && conf->headers == NULL\n        && conf->trailers == NULL)\n    {\n        return ngx_http_next_header_filter(r);\n    }\n\n    switch (r->headers_out.status) {\n\n    case NGX_HTTP_OK:\n    case NGX_HTTP_CREATED:\n    case NGX_HTTP_NO_CONTENT:\n    case NGX_HTTP_PARTIAL_CONTENT:\n    case NGX_HTTP_MOVED_PERMANENTLY:\n    case NGX_HTTP_MOVED_TEMPORARILY:\n    case NGX_HTTP_SEE_OTHER:\n    case NGX_HTTP_NOT_MODIFIED:\n    case NGX_HTTP_TEMPORARY_REDIRECT:\n    case NGX_HTTP_PERMANENT_REDIRECT:\n        safe_status = 1;\n        break;\n\n    default:\n        safe_status = 0;\n        break;\n    }\n\n    if (conf->expires != NGX_HTTP_EXPIRES_OFF && safe_status) {\n        if (ngx_http_set_expires(r, conf) != NGX_OK) {\n            return NGX_ERROR;\n        }\n    }\n\n    if (conf->headers) {\n        h = conf->headers->elts;\n        for (i = 0; i < conf->headers->nelts; i++) {\n\n            if (!safe_status && !h[i].always) {\n                continue;\n            }\n\n            if (ngx_http_complex_value(r, &h[i].value, &value) != NGX_OK) {\n                return NGX_ERROR;\n            }\n\n            if (h[i].handler(r, &h[i], &value) != NGX_OK) {\n                return NGX_ERROR;\n            }\n        }\n    }\n\n    if (conf->trailers) {\n        h = conf->trailers->elts;\n        for (i = 0; i < conf->trailers->nelts; i++) {\n\n            if (!safe_status && !h[i].always) {\n                continue;\n            }\n\n            r->expect_trailers = 1;\n            break;\n        }\n    }\n\n    return ngx_http_next_header_filter(r);\n}\n\n\nstatic ngx_int_t\nngx_http_trailers_filter(ngx_http_request_t *r, ngx_chain_t *in)\n{\n    ngx_str_t                 value;\n    ngx_uint_t                i, safe_status;\n    ngx_chain_t              *cl;\n    ngx_table_elt_t          *t;\n    ngx_http_header_val_t    *h;\n    ngx_http_headers_conf_t  *conf;\n\n    conf = ngx_http_get_module_loc_conf(r, ngx_http_headers_filter_module);\n\n    if (in == NULL\n        || conf->trailers == NULL\n        || !r->expect_trailers\n        || r->header_only)\n    {\n        return ngx_http_next_body_filter(r, in);\n    }\n\n    for (cl = in; cl; cl = cl->next) {\n        if (cl->buf->last_buf) {\n            break;\n        }\n    }\n\n    if (cl == NULL) {\n        return ngx_http_next_body_filter(r, in);\n    }\n\n    switch (r->headers_out.status) {\n\n    case NGX_HTTP_OK:\n    case NGX_HTTP_CREATED:\n    case NGX_HTTP_NO_CONTENT:\n    case NGX_HTTP_PARTIAL_CONTENT:\n    case NGX_HTTP_MOVED_PERMANENTLY:\n    case NGX_HTTP_MOVED_TEMPORARILY:\n    case NGX_HTTP_SEE_OTHER:\n    case NGX_HTTP_NOT_MODIFIED:\n    case NGX_HTTP_TEMPORARY_REDIRECT:\n    case NGX_HTTP_PERMANENT_REDIRECT:\n        safe_status = 1;\n        break;\n\n    default:\n        safe_status = 0;\n        break;\n    }\n\n    h = conf->trailers->elts;\n    for (i = 0; i < conf->trailers->nelts; i++) {\n\n        if (!safe_status && !h[i].always) {\n            continue;\n        }\n\n        if (ngx_http_complex_value(r, &h[i].value, &value) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        if (value.len) {\n            t = ngx_list_push(&r->headers_out.trailers);\n            if (t == NULL) {\n                return NGX_ERROR;\n            }\n\n            t->key = h[i].key;\n            t->value = value;\n            t->hash = 1;\n        }\n    }\n\n    return ngx_http_next_body_filter(r, in);\n}\n\n\nstatic ngx_int_t\nngx_http_set_expires(ngx_http_request_t *r, ngx_http_headers_conf_t *conf)\n{\n    char                *err;\n    size_t               len;\n    time_t               now, expires_time, max_age;\n    ngx_str_t            value;\n    ngx_int_t            rc;\n    ngx_table_elt_t     *e, *cc;\n    ngx_http_expires_t   expires;\n\n    expires = conf->expires;\n    expires_time = conf->expires_time;\n\n    if (conf->expires_value != NULL) {\n\n        if (ngx_http_complex_value(r, conf->expires_value, &value) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        rc = ngx_http_parse_expires(&value, &expires, &expires_time, &err);\n\n        if (rc != NGX_OK) {\n            return NGX_OK;\n        }\n\n        if (expires == NGX_HTTP_EXPIRES_OFF) {\n            return NGX_OK;\n        }\n    }\n\n    e = r->headers_out.expires;\n\n    if (e == NULL) {\n\n        e = ngx_list_push(&r->headers_out.headers);\n        if (e == NULL) {\n            return NGX_ERROR;\n        }\n\n        r->headers_out.expires = e;\n        e->next = NULL;\n\n        e->hash = 1;\n        ngx_str_set(&e->key, \"Expires\");\n    }\n\n    len = sizeof(\"Mon, 28 Sep 1970 06:00:00 GMT\");\n    e->value.len = len - 1;\n\n    cc = r->headers_out.cache_control;\n\n    if (cc == NULL) {\n\n        cc = ngx_list_push(&r->headers_out.headers);\n        if (cc == NULL) {\n            e->hash = 0;\n            return NGX_ERROR;\n        }\n\n        r->headers_out.cache_control = cc;\n        cc->next = NULL;\n\n        cc->hash = 1;\n        ngx_str_set(&cc->key, \"Cache-Control\");\n\n    } else {\n        for (cc = cc->next; cc; cc = cc->next) {\n            cc->hash = 0;\n        }\n\n        cc = r->headers_out.cache_control;\n        cc->next = NULL;\n    }\n\n    if (expires == NGX_HTTP_EXPIRES_EPOCH) {\n        e->value.data = (u_char *) \"Thu, 01 Jan 1970 00:00:01 GMT\";\n        ngx_str_set(&cc->value, \"no-cache\");\n        return NGX_OK;\n    }\n\n    if (expires == NGX_HTTP_EXPIRES_MAX) {\n        e->value.data = (u_char *) \"Thu, 31 Dec 2037 23:55:55 GMT\";\n        /* 10 years */\n        ngx_str_set(&cc->value, \"max-age=315360000\");\n        return NGX_OK;\n    }\n\n    e->value.data = ngx_pnalloc(r->pool, len);\n    if (e->value.data == NULL) {\n        e->hash = 0;\n        cc->hash = 0;\n        return NGX_ERROR;\n    }\n\n    if (expires_time == 0 && expires != NGX_HTTP_EXPIRES_DAILY) {\n        ngx_memcpy(e->value.data, ngx_cached_http_time.data,\n                   ngx_cached_http_time.len + 1);\n        ngx_str_set(&cc->value, \"max-age=0\");\n        return NGX_OK;\n    }\n\n    now = ngx_time();\n\n    if (expires == NGX_HTTP_EXPIRES_DAILY) {\n        expires_time = ngx_next_time(expires_time);\n        max_age = expires_time - now;\n\n    } else if (expires == NGX_HTTP_EXPIRES_ACCESS\n               || r->headers_out.last_modified_time == -1)\n    {\n        max_age = expires_time;\n        expires_time += now;\n\n    } else {\n        expires_time += r->headers_out.last_modified_time;\n        max_age = expires_time - now;\n    }\n\n    ngx_http_time(e->value.data, expires_time);\n\n    if (conf->expires_time < 0 || max_age < 0) {\n        ngx_str_set(&cc->value, \"no-cache\");\n        return NGX_OK;\n    }\n\n    cc->value.data = ngx_pnalloc(r->pool,\n                                 sizeof(\"max-age=\") + NGX_TIME_T_LEN + 1);\n    if (cc->value.data == NULL) {\n        cc->hash = 0;\n        return NGX_ERROR;\n    }\n\n    cc->value.len = ngx_sprintf(cc->value.data, \"max-age=%T\", max_age)\n                    - cc->value.data;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_parse_expires(ngx_str_t *value, ngx_http_expires_t *expires,\n    time_t *expires_time, char **err)\n{\n    ngx_uint_t  minus;\n\n    if (*expires != NGX_HTTP_EXPIRES_MODIFIED) {\n\n        if (value->len == 5 && ngx_strncmp(value->data, \"epoch\", 5) == 0) {\n            *expires = NGX_HTTP_EXPIRES_EPOCH;\n            return NGX_OK;\n        }\n\n        if (value->len == 3 && ngx_strncmp(value->data, \"max\", 3) == 0) {\n            *expires = NGX_HTTP_EXPIRES_MAX;\n            return NGX_OK;\n        }\n\n        if (value->len == 3 && ngx_strncmp(value->data, \"off\", 3) == 0) {\n            *expires = NGX_HTTP_EXPIRES_OFF;\n            return NGX_OK;\n        }\n    }\n\n    if (value->len && value->data[0] == '@') {\n        value->data++;\n        value->len--;\n        minus = 0;\n\n        if (*expires == NGX_HTTP_EXPIRES_MODIFIED) {\n            *err = \"daily time cannot be used with \\\"modified\\\" parameter\";\n            return NGX_ERROR;\n        }\n\n        *expires = NGX_HTTP_EXPIRES_DAILY;\n\n    } else if (value->len && value->data[0] == '+') {\n        value->data++;\n        value->len--;\n        minus = 0;\n\n    } else if (value->len && value->data[0] == '-') {\n        value->data++;\n        value->len--;\n        minus = 1;\n\n    } else {\n        minus = 0;\n    }\n\n    *expires_time = ngx_parse_time(value, 1);\n\n    if (*expires_time == (time_t) NGX_ERROR) {\n        *err = \"invalid value\";\n        return NGX_ERROR;\n    }\n\n    if (*expires == NGX_HTTP_EXPIRES_DAILY\n        && *expires_time > 24 * 60 * 60)\n    {\n        *err = \"daily time value must be less than 24 hours\";\n        return NGX_ERROR;\n    }\n\n    if (minus) {\n        *expires_time = - *expires_time;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_add_header(ngx_http_request_t *r, ngx_http_header_val_t *hv,\n    ngx_str_t *value)\n{\n    ngx_table_elt_t  *h;\n\n    if (value->len) {\n        h = ngx_list_push(&r->headers_out.headers);\n        if (h == NULL) {\n            return NGX_ERROR;\n        }\n\n        h->hash = 1;\n        h->key = hv->key;\n        h->value = *value;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_add_multi_header_lines(ngx_http_request_t *r,\n    ngx_http_header_val_t *hv, ngx_str_t *value)\n{\n    ngx_table_elt_t  *h, **ph;\n\n    if (value->len == 0) {\n        return NGX_OK;\n    }\n\n    h = ngx_list_push(&r->headers_out.headers);\n    if (h == NULL) {\n        return NGX_ERROR;\n    }\n\n    h->hash = 1;\n    h->key = hv->key;\n    h->value = *value;\n\n    ph = (ngx_table_elt_t **) ((char *) &r->headers_out + hv->offset);\n\n    while (*ph) { ph = &(*ph)->next; }\n\n    *ph = h;\n    h->next = NULL;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_set_last_modified(ngx_http_request_t *r, ngx_http_header_val_t *hv,\n    ngx_str_t *value)\n{\n    if (ngx_http_set_response_header(r, hv, value) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    r->headers_out.last_modified_time =\n        (value->len) ? ngx_parse_http_time(value->data, value->len) : -1;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_set_response_header(ngx_http_request_t *r, ngx_http_header_val_t *hv,\n    ngx_str_t *value)\n{\n    ngx_table_elt_t  *h, **old;\n\n    old = (ngx_table_elt_t **) ((char *) &r->headers_out + hv->offset);\n\n    if (value->len == 0) {\n        if (*old) {\n            (*old)->hash = 0;\n            *old = NULL;\n        }\n\n        return NGX_OK;\n    }\n\n    if (*old) {\n        h = *old;\n\n    } else {\n        h = ngx_list_push(&r->headers_out.headers);\n        if (h == NULL) {\n            return NGX_ERROR;\n        }\n\n        *old = h;\n        h->next = NULL;\n    }\n\n    h->hash = 1;\n    h->key = hv->key;\n    h->value = *value;\n\n    return NGX_OK;\n}\n\n\nstatic void *\nngx_http_headers_create_conf(ngx_conf_t *cf)\n{\n    ngx_http_headers_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_headers_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc():\n     *\n     *     conf->headers = NULL;\n     *     conf->trailers = NULL;\n     *     conf->expires_time = 0;\n     *     conf->expires_value = NULL;\n     */\n\n    conf->expires = NGX_HTTP_EXPIRES_UNSET;\n\n    return conf;\n}\n\n\nstatic char *\nngx_http_headers_merge_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_http_headers_conf_t *prev = parent;\n    ngx_http_headers_conf_t *conf = child;\n\n    if (conf->expires == NGX_HTTP_EXPIRES_UNSET) {\n        conf->expires = prev->expires;\n        conf->expires_time = prev->expires_time;\n        conf->expires_value = prev->expires_value;\n\n        if (conf->expires == NGX_HTTP_EXPIRES_UNSET) {\n            conf->expires = NGX_HTTP_EXPIRES_OFF;\n        }\n    }\n\n    if (conf->headers == NULL) {\n        conf->headers = prev->headers;\n    }\n\n    if (conf->trailers == NULL) {\n        conf->trailers = prev->trailers;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_headers_filter_init(ngx_conf_t *cf)\n{\n    ngx_http_next_header_filter = ngx_http_top_header_filter;\n    ngx_http_top_header_filter = ngx_http_headers_filter;\n\n    ngx_http_next_body_filter = ngx_http_top_body_filter;\n    ngx_http_top_body_filter = ngx_http_trailers_filter;\n\n    return NGX_OK;\n}\n\n\nstatic char *\nngx_http_headers_expires(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_headers_conf_t *hcf = conf;\n\n    char                              *err;\n    ngx_str_t                         *value;\n    ngx_int_t                          rc;\n    ngx_uint_t                         n;\n    ngx_http_complex_value_t           cv;\n    ngx_http_compile_complex_value_t   ccv;\n\n    if (hcf->expires != NGX_HTTP_EXPIRES_UNSET) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    if (cf->args->nelts == 2) {\n\n        hcf->expires = NGX_HTTP_EXPIRES_ACCESS;\n\n        n = 1;\n\n    } else { /* cf->args->nelts == 3 */\n\n        if (ngx_strcmp(value[1].data, \"modified\") != 0) {\n            return \"invalid value\";\n        }\n\n        hcf->expires = NGX_HTTP_EXPIRES_MODIFIED;\n\n        n = 2;\n    }\n\n    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n\n    ccv.cf = cf;\n    ccv.value = &value[n];\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\n        hcf->expires_value = ngx_palloc(cf->pool,\n                                        sizeof(ngx_http_complex_value_t));\n        if (hcf->expires_value == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        *hcf->expires_value = cv;\n\n        return NGX_CONF_OK;\n    }\n\n    rc = ngx_http_parse_expires(&value[n], &hcf->expires, &hcf->expires_time,\n                                &err);\n    if (rc != NGX_OK) {\n        return err;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_headers_add(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_headers_conf_t *hcf = conf;\n\n    ngx_str_t                          *value;\n    ngx_uint_t                          i;\n    ngx_array_t                       **headers;\n    ngx_http_header_val_t              *hv;\n    ngx_http_set_header_t              *set;\n    ngx_http_compile_complex_value_t    ccv;\n\n    value = cf->args->elts;\n\n    headers = (ngx_array_t **) ((char *) hcf + cmd->offset);\n\n    if (*headers == NULL) {\n        *headers = ngx_array_create(cf->pool, 1,\n                                    sizeof(ngx_http_header_val_t));\n        if (*headers == NULL) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    hv = ngx_array_push(*headers);\n    if (hv == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    hv->key = value[1];\n    hv->handler = NULL;\n    hv->offset = 0;\n    hv->always = 0;\n\n    if (headers == &hcf->headers) {\n        hv->handler = ngx_http_add_header;\n\n        set = ngx_http_set_headers;\n        for (i = 0; set[i].name.len; i++) {\n            if (ngx_strcasecmp(value[1].data, set[i].name.data) != 0) {\n                continue;\n            }\n\n            hv->offset = set[i].offset;\n            hv->handler = set[i].handler;\n\n            break;\n        }\n    }\n\n    if (value[2].len == 0) {\n        ngx_memzero(&hv->value, sizeof(ngx_http_complex_value_t));\n\n    } else {\n        ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n\n        ccv.cf = cf;\n        ccv.value = &value[2];\n        ccv.complex_value = &hv->value;\n\n        if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    if (cf->args->nelts == 3) {\n        return NGX_CONF_OK;\n    }\n\n    if (ngx_strcmp(value[3].data, \"always\") != 0) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid parameter \\\"%V\\\"\", &value[3]);\n        return NGX_CONF_ERROR;\n    }\n\n    hv->always = 1;\n\n    return NGX_CONF_OK;\n}\n"
  },
  {
    "path": "src/http/modules/ngx_http_image_filter_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n#include <gd.h>\n\n\n#define NGX_HTTP_IMAGE_OFF       0\n#define NGX_HTTP_IMAGE_TEST      1\n#define NGX_HTTP_IMAGE_SIZE      2\n#define NGX_HTTP_IMAGE_RESIZE    3\n#define NGX_HTTP_IMAGE_CROP      4\n#define NGX_HTTP_IMAGE_ROTATE    5\n\n#if (T_NGX_HTTP_IMAGE_FILTER)\n#define NGX_HTTP_IMAGE_CROP_KEEPX       6\n#define NGX_HTTP_IMAGE_CROP_KEEPY       7\n#endif\n\n\n#define NGX_HTTP_IMAGE_START     0\n#define NGX_HTTP_IMAGE_READ      1\n#define NGX_HTTP_IMAGE_PROCESS   2\n#define NGX_HTTP_IMAGE_PASS      3\n#define NGX_HTTP_IMAGE_DONE      4\n\n\n#define NGX_HTTP_IMAGE_NONE      0\n#define NGX_HTTP_IMAGE_JPEG      1\n#define NGX_HTTP_IMAGE_GIF       2\n#define NGX_HTTP_IMAGE_PNG       3\n#define NGX_HTTP_IMAGE_WEBP      4\n\n#if (T_NGX_HTTP_IMAGE_FILTER)\n#define NGX_HTTP_IMAGE_OFFSET_CENTER    0\n#define NGX_HTTP_IMAGE_OFFSET_LEFT      1\n#define NGX_HTTP_IMAGE_OFFSET_RIGHT     2\n#define NGX_HTTP_IMAGE_OFFSET_TOP       3\n#define NGX_HTTP_IMAGE_OFFSET_BOTTOM    4\n#endif\n\n#define NGX_HTTP_IMAGE_BUFFERED  0x08\n\n\ntypedef struct {\n    ngx_uint_t                   filter;\n    ngx_uint_t                   width;\n    ngx_uint_t                   height;\n    ngx_uint_t                   angle;\n    ngx_uint_t                   jpeg_quality;\n    ngx_uint_t                   webp_quality;\n    ngx_uint_t                   sharpen;\n\n#if (T_NGX_HTTP_IMAGE_FILTER)\n    ngx_uint_t                   offset_x;\n    ngx_uint_t                   offset_y;\n#endif\n\n    ngx_flag_t                   transparency;\n    ngx_flag_t                   interlace;\n\n    ngx_http_complex_value_t    *wcv;\n    ngx_http_complex_value_t    *hcv;\n    ngx_http_complex_value_t    *acv;\n    ngx_http_complex_value_t    *jqcv;\n    ngx_http_complex_value_t    *wqcv;\n    ngx_http_complex_value_t    *shcv;\n\n#if (T_NGX_HTTP_IMAGE_FILTER)\n    ngx_http_complex_value_t    *oxcv;\n    ngx_http_complex_value_t    *oycv;\n#endif\n\n    size_t                       buffer_size;\n} ngx_http_image_filter_conf_t;\n\n\ntypedef struct {\n    u_char                      *image;\n    u_char                      *last;\n\n    size_t                       length;\n\n    ngx_uint_t                   width;\n    ngx_uint_t                   height;\n    ngx_uint_t                   max_width;\n    ngx_uint_t                   max_height;\n    ngx_uint_t                   angle;\n\n#if (T_NGX_HTTP_IMAGE_FILTER)\n    ngx_uint_t                   offset_x;\n    ngx_uint_t                   offset_y;\n#endif\n\n    ngx_uint_t                   phase;\n    ngx_uint_t                   type;\n    ngx_uint_t                   force;\n} ngx_http_image_filter_ctx_t;\n\n\nstatic ngx_int_t ngx_http_image_send(ngx_http_request_t *r,\n    ngx_http_image_filter_ctx_t *ctx, ngx_chain_t *in);\nstatic ngx_uint_t ngx_http_image_test(ngx_http_request_t *r, ngx_chain_t *in);\nstatic ngx_int_t ngx_http_image_read(ngx_http_request_t *r, ngx_chain_t *in);\nstatic ngx_buf_t *ngx_http_image_process(ngx_http_request_t *r);\nstatic ngx_buf_t *ngx_http_image_json(ngx_http_request_t *r,\n    ngx_http_image_filter_ctx_t *ctx);\nstatic ngx_buf_t *ngx_http_image_asis(ngx_http_request_t *r,\n    ngx_http_image_filter_ctx_t *ctx);\nstatic void ngx_http_image_length(ngx_http_request_t *r, ngx_buf_t *b);\nstatic ngx_int_t ngx_http_image_size(ngx_http_request_t *r,\n    ngx_http_image_filter_ctx_t *ctx);\n\nstatic ngx_buf_t *ngx_http_image_resize(ngx_http_request_t *r,\n    ngx_http_image_filter_ctx_t *ctx);\nstatic gdImagePtr ngx_http_image_source(ngx_http_request_t *r,\n    ngx_http_image_filter_ctx_t *ctx);\nstatic gdImagePtr ngx_http_image_new(ngx_http_request_t *r, int w, int h,\n    int colors);\nstatic u_char *ngx_http_image_out(ngx_http_request_t *r, ngx_uint_t type,\n    gdImagePtr img, int *size);\nstatic void ngx_http_image_cleanup(void *data);\nstatic ngx_uint_t ngx_http_image_filter_get_value(ngx_http_request_t *r,\n    ngx_http_complex_value_t *cv, ngx_uint_t v);\nstatic ngx_uint_t ngx_http_image_filter_value(ngx_str_t *value);\n\n\nstatic void *ngx_http_image_filter_create_conf(ngx_conf_t *cf);\nstatic char *ngx_http_image_filter_merge_conf(ngx_conf_t *cf, void *parent,\n    void *child);\nstatic char *ngx_http_image_filter(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_http_image_filter_jpeg_quality(ngx_conf_t *cf,\n    ngx_command_t *cmd, void *conf);\nstatic char *ngx_http_image_filter_webp_quality(ngx_conf_t *cf,\n    ngx_command_t *cmd, void *conf);\nstatic char *ngx_http_image_filter_sharpen(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n#if (T_NGX_HTTP_IMAGE_FILTER)\nstatic char *ngx_http_image_filter_offset(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n#endif\nstatic ngx_int_t ngx_http_image_filter_init(ngx_conf_t *cf);\n\n\nstatic ngx_command_t  ngx_http_image_filter_commands[] = {\n\n    { ngx_string(\"image_filter\"),\n      NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,\n      ngx_http_image_filter,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"image_filter_jpeg_quality\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_http_image_filter_jpeg_quality,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"image_filter_webp_quality\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_http_image_filter_webp_quality,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"image_filter_sharpen\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_http_image_filter_sharpen,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"image_filter_transparency\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_image_filter_conf_t, transparency),\n      NULL },\n\n    { ngx_string(\"image_filter_interlace\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_image_filter_conf_t, interlace),\n      NULL },\n\n    { ngx_string(\"image_filter_buffer\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_image_filter_conf_t, buffer_size),\n      NULL },\n\n#if (T_NGX_HTTP_IMAGE_FILTER)\n    { ngx_string(\"image_filter_crop_offset\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,\n      ngx_http_image_filter_offset,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n#endif\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_image_filter_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    ngx_http_image_filter_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    ngx_http_image_filter_create_conf,     /* create location configuration */\n    ngx_http_image_filter_merge_conf       /* merge location configuration */\n};\n\n\nngx_module_t  ngx_http_image_filter_module = {\n    NGX_MODULE_V1,\n    &ngx_http_image_filter_module_ctx,     /* module context */\n    ngx_http_image_filter_commands,        /* 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_http_output_header_filter_pt  ngx_http_next_header_filter;\nstatic ngx_http_output_body_filter_pt    ngx_http_next_body_filter;\n\n\nstatic ngx_str_t  ngx_http_image_types[] = {\n    ngx_string(\"image/jpeg\"),\n    ngx_string(\"image/gif\"),\n    ngx_string(\"image/png\"),\n    ngx_string(\"image/webp\")\n};\n\n\nstatic ngx_int_t\nngx_http_image_header_filter(ngx_http_request_t *r)\n{\n    off_t                          len;\n    ngx_http_image_filter_ctx_t   *ctx;\n    ngx_http_image_filter_conf_t  *conf;\n\n    if (r->headers_out.status == NGX_HTTP_NOT_MODIFIED) {\n        return ngx_http_next_header_filter(r);\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_image_filter_module);\n\n    if (ctx) {\n        ngx_http_set_ctx(r, NULL, ngx_http_image_filter_module);\n        return ngx_http_next_header_filter(r);\n    }\n\n    conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module);\n\n    if (conf->filter == NGX_HTTP_IMAGE_OFF) {\n        return ngx_http_next_header_filter(r);\n    }\n\n    if (r->headers_out.content_type.len\n            >= sizeof(\"multipart/x-mixed-replace\") - 1\n        && ngx_strncasecmp(r->headers_out.content_type.data,\n                           (u_char *) \"multipart/x-mixed-replace\",\n                           sizeof(\"multipart/x-mixed-replace\") - 1)\n           == 0)\n    {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"image filter: multipart/x-mixed-replace response\");\n\n        return NGX_ERROR;\n    }\n\n    ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_image_filter_ctx_t));\n    if (ctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_http_set_ctx(r, ctx, ngx_http_image_filter_module);\n\n    len = r->headers_out.content_length_n;\n\n    if (len != -1 && len > (off_t) conf->buffer_size) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"image filter: too big response: %O\", len);\n\n        return NGX_HTTP_UNSUPPORTED_MEDIA_TYPE;\n    }\n\n    if (len == -1) {\n        ctx->length = conf->buffer_size;\n\n    } else {\n        ctx->length = (size_t) len;\n    }\n\n    if (r->headers_out.refresh) {\n        r->headers_out.refresh->hash = 0;\n    }\n\n    r->main_filter_need_in_memory = 1;\n    r->allow_ranges = 0;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_image_body_filter(ngx_http_request_t *r, ngx_chain_t *in)\n{\n    ngx_int_t                      rc;\n    ngx_str_t                     *ct;\n    ngx_chain_t                    out;\n    ngx_http_image_filter_ctx_t   *ctx;\n    ngx_http_image_filter_conf_t  *conf;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, \"image filter\");\n\n    if (in == NULL) {\n        return ngx_http_next_body_filter(r, in);\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_image_filter_module);\n\n    if (ctx == NULL) {\n        return ngx_http_next_body_filter(r, in);\n    }\n\n    switch (ctx->phase) {\n\n    case NGX_HTTP_IMAGE_START:\n\n        ctx->type = ngx_http_image_test(r, in);\n\n        conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module);\n\n        if (ctx->type == NGX_HTTP_IMAGE_NONE) {\n\n            if (conf->filter == NGX_HTTP_IMAGE_SIZE) {\n                out.buf = ngx_http_image_json(r, NULL);\n\n                if (out.buf) {\n                    out.next = NULL;\n                    ctx->phase = NGX_HTTP_IMAGE_DONE;\n\n                    return ngx_http_image_send(r, ctx, &out);\n                }\n            }\n\n            return ngx_http_filter_finalize_request(r,\n                                              &ngx_http_image_filter_module,\n                                              NGX_HTTP_UNSUPPORTED_MEDIA_TYPE);\n        }\n\n        /* override content type */\n\n        ct = &ngx_http_image_types[ctx->type - 1];\n        r->headers_out.content_type_len = ct->len;\n        r->headers_out.content_type = *ct;\n        r->headers_out.content_type_lowcase = NULL;\n\n        if (conf->filter == NGX_HTTP_IMAGE_TEST) {\n            ctx->phase = NGX_HTTP_IMAGE_PASS;\n\n            return ngx_http_image_send(r, ctx, in);\n        }\n\n        ctx->phase = NGX_HTTP_IMAGE_READ;\n\n        /* fall through */\n\n    case NGX_HTTP_IMAGE_READ:\n\n        rc = ngx_http_image_read(r, in);\n\n        if (rc == NGX_AGAIN) {\n            return NGX_OK;\n        }\n\n        if (rc == NGX_ERROR) {\n            return ngx_http_filter_finalize_request(r,\n                                              &ngx_http_image_filter_module,\n                                              NGX_HTTP_UNSUPPORTED_MEDIA_TYPE);\n        }\n\n        /* fall through */\n\n    case NGX_HTTP_IMAGE_PROCESS:\n\n        out.buf = ngx_http_image_process(r);\n\n        if (out.buf == NULL) {\n            return ngx_http_filter_finalize_request(r,\n                                              &ngx_http_image_filter_module,\n                                              NGX_HTTP_UNSUPPORTED_MEDIA_TYPE);\n        }\n\n        out.next = NULL;\n        ctx->phase = NGX_HTTP_IMAGE_PASS;\n\n        return ngx_http_image_send(r, ctx, &out);\n\n    case NGX_HTTP_IMAGE_PASS:\n\n        return ngx_http_next_body_filter(r, in);\n\n    default: /* NGX_HTTP_IMAGE_DONE */\n\n        rc = ngx_http_next_body_filter(r, NULL);\n\n        /* NGX_ERROR resets any pending data */\n        return (rc == NGX_OK) ? NGX_ERROR : rc;\n    }\n}\n\n\nstatic ngx_int_t\nngx_http_image_send(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx,\n    ngx_chain_t *in)\n{\n    ngx_int_t  rc;\n\n    rc = ngx_http_next_header_filter(r);\n\n    if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {\n        return NGX_ERROR;\n    }\n\n    rc = ngx_http_next_body_filter(r, in);\n\n    if (ctx->phase == NGX_HTTP_IMAGE_DONE) {\n        /* NGX_ERROR resets any pending data */\n        return (rc == NGX_OK) ? NGX_ERROR : rc;\n    }\n\n    return rc;\n}\n\n\nstatic ngx_uint_t\nngx_http_image_test(ngx_http_request_t *r, ngx_chain_t *in)\n{\n    u_char  *p;\n\n    p = in->buf->pos;\n\n    if (in->buf->last - p < 16) {\n        return NGX_HTTP_IMAGE_NONE;\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"image filter: \\\"%c%c\\\"\", p[0], p[1]);\n\n    if (p[0] == 0xff && p[1] == 0xd8) {\n\n        /* JPEG */\n\n        return NGX_HTTP_IMAGE_JPEG;\n\n    } else if (p[0] == 'G' && p[1] == 'I' && p[2] == 'F' && p[3] == '8'\n               && p[5] == 'a')\n    {\n        if (p[4] == '9' || p[4] == '7') {\n            /* GIF */\n            return NGX_HTTP_IMAGE_GIF;\n        }\n\n    } else if (p[0] == 0x89 && p[1] == 'P' && p[2] == 'N' && p[3] == 'G'\n               && p[4] == 0x0d && p[5] == 0x0a && p[6] == 0x1a && p[7] == 0x0a)\n    {\n        /* PNG */\n\n        return NGX_HTTP_IMAGE_PNG;\n\n    } else if (p[0] == 'R' && p[1] == 'I' && p[2] == 'F' && p[3] == 'F'\n               && p[8] == 'W' && p[9] == 'E' && p[10] == 'B' && p[11] == 'P')\n    {\n        /* WebP */\n\n        return NGX_HTTP_IMAGE_WEBP;\n    }\n\n    return NGX_HTTP_IMAGE_NONE;\n}\n\n\nstatic ngx_int_t\nngx_http_image_read(ngx_http_request_t *r, ngx_chain_t *in)\n{\n    u_char                       *p;\n    size_t                        size, rest;\n    ngx_buf_t                    *b;\n    ngx_chain_t                  *cl;\n    ngx_http_image_filter_ctx_t  *ctx;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_image_filter_module);\n\n    if (ctx->image == NULL) {\n        ctx->image = ngx_palloc(r->pool, ctx->length);\n        if (ctx->image == NULL) {\n            return NGX_ERROR;\n        }\n\n        ctx->last = ctx->image;\n    }\n\n    p = ctx->last;\n\n    for (cl = in; cl; cl = cl->next) {\n\n        b = cl->buf;\n        size = b->last - b->pos;\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"image buf: %uz\", size);\n\n        rest = ctx->image + ctx->length - p;\n\n        if (size > rest) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"image filter: too big response\");\n            return NGX_ERROR;\n        }\n\n        p = ngx_cpymem(p, b->pos, size);\n        b->pos += size;\n\n        if (b->last_buf) {\n            ctx->last = p;\n            return NGX_OK;\n        }\n    }\n\n    ctx->last = p;\n    r->connection->buffered |= NGX_HTTP_IMAGE_BUFFERED;\n\n    return NGX_AGAIN;\n}\n\n\nstatic ngx_buf_t *\nngx_http_image_process(ngx_http_request_t *r)\n{\n    ngx_int_t                      rc;\n    ngx_http_image_filter_ctx_t   *ctx;\n    ngx_http_image_filter_conf_t  *conf;\n\n    r->connection->buffered &= ~NGX_HTTP_IMAGE_BUFFERED;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_image_filter_module);\n\n    rc = ngx_http_image_size(r, ctx);\n\n    conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module);\n\n    if (conf->filter == NGX_HTTP_IMAGE_SIZE) {\n        return ngx_http_image_json(r, rc == NGX_OK ? ctx : NULL);\n    }\n\n    ctx->angle = ngx_http_image_filter_get_value(r, conf->acv, conf->angle);\n\n    if (conf->filter == NGX_HTTP_IMAGE_ROTATE) {\n\n        if (ctx->angle != 90 && ctx->angle != 180 && ctx->angle != 270) {\n            return NULL;\n        }\n\n        return ngx_http_image_resize(r, ctx);\n    }\n\n    ctx->max_width = ngx_http_image_filter_get_value(r, conf->wcv, conf->width);\n    if (ctx->max_width == 0) {\n        return NULL;\n    }\n\n    ctx->max_height = ngx_http_image_filter_get_value(r, conf->hcv,\n                                                      conf->height);\n    if (ctx->max_height == 0) {\n        return NULL;\n    }\n\n    if (rc == NGX_OK\n        && ctx->width <= ctx->max_width\n        && ctx->height <= ctx->max_height\n        && ctx->angle == 0\n        && !ctx->force)\n    {\n        return ngx_http_image_asis(r, ctx);\n    }\n\n    return ngx_http_image_resize(r, ctx);\n}\n\n\nstatic ngx_buf_t *\nngx_http_image_json(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx)\n{\n    size_t      len;\n    ngx_buf_t  *b;\n\n    b = ngx_calloc_buf(r->pool);\n    if (b == NULL) {\n        return NULL;\n    }\n\n    b->memory = 1;\n    b->last_buf = 1;\n\n    ngx_http_clean_header(r);\n\n    r->headers_out.status = NGX_HTTP_OK;\n    r->headers_out.content_type_len = sizeof(\"application/json\") - 1;\n    ngx_str_set(&r->headers_out.content_type, \"application/json\");\n    r->headers_out.content_type_lowcase = NULL;\n\n    if (ctx == NULL) {\n        b->pos = (u_char *) \"{}\" CRLF;\n        b->last = b->pos + sizeof(\"{}\" CRLF) - 1;\n\n        ngx_http_image_length(r, b);\n\n        return b;\n    }\n\n    len = sizeof(\"{ \\\"img\\\" : \"\n                 \"{ \\\"width\\\": , \\\"height\\\": , \\\"type\\\": \\\"jpeg\\\" } }\" CRLF) - 1\n          + 2 * NGX_SIZE_T_LEN;\n\n    b->pos = ngx_pnalloc(r->pool, len);\n    if (b->pos == NULL) {\n        return NULL;\n    }\n\n    b->last = ngx_sprintf(b->pos,\n                          \"{ \\\"img\\\" : \"\n                                       \"{ \\\"width\\\": %uz,\"\n                                        \" \\\"height\\\": %uz,\"\n                                        \" \\\"type\\\": \\\"%s\\\" } }\" CRLF,\n                          ctx->width, ctx->height,\n                          ngx_http_image_types[ctx->type - 1].data + 6);\n\n    ngx_http_image_length(r, b);\n\n    return b;\n}\n\n\nstatic ngx_buf_t *\nngx_http_image_asis(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx)\n{\n    ngx_buf_t  *b;\n\n    b = ngx_calloc_buf(r->pool);\n    if (b == NULL) {\n        return NULL;\n    }\n\n    b->pos = ctx->image;\n    b->last = ctx->last;\n    b->memory = 1;\n    b->last_buf = 1;\n\n    ngx_http_image_length(r, b);\n\n    return b;\n}\n\n\nstatic void\nngx_http_image_length(ngx_http_request_t *r, ngx_buf_t *b)\n{\n    r->headers_out.content_length_n = b->last - b->pos;\n\n    if (r->headers_out.content_length) {\n        r->headers_out.content_length->hash = 0;\n    }\n\n    r->headers_out.content_length = NULL;\n}\n\n\nstatic ngx_int_t\nngx_http_image_size(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx)\n{\n    u_char      *p, *last;\n    size_t       len, app;\n    ngx_uint_t   width, height;\n\n    p = ctx->image;\n\n    switch (ctx->type) {\n\n    case NGX_HTTP_IMAGE_JPEG:\n\n        p += 2;\n        last = ctx->image + ctx->length - 10;\n        width = 0;\n        height = 0;\n        app = 0;\n\n        while (p < last) {\n\n            if (p[0] == 0xff && p[1] != 0xff) {\n\n                ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                               \"JPEG: %02xd %02xd\", p[0], p[1]);\n\n                p++;\n\n                if ((*p == 0xc0 || *p == 0xc1 || *p == 0xc2 || *p == 0xc3\n                     || *p == 0xc9 || *p == 0xca || *p == 0xcb)\n                    && (width == 0 || height == 0))\n                {\n                    width = p[6] * 256 + p[7];\n                    height = p[4] * 256 + p[5];\n                }\n\n                ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                               \"JPEG: %02xd %02xd\", p[1], p[2]);\n\n                len = p[1] * 256 + p[2];\n\n                if (*p >= 0xe1 && *p <= 0xef) {\n                    /* application data, e.g., EXIF, Adobe XMP, etc. */\n                    app += len;\n                }\n\n                p += len;\n\n                continue;\n            }\n\n            p++;\n        }\n\n        if (width == 0 || height == 0) {\n            return NGX_DECLINED;\n        }\n\n        if (ctx->length / 20 < app) {\n            /* force conversion if application data consume more than 5% */\n            ctx->force = 1;\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"app data size: %uz\", app);\n        }\n\n        break;\n\n    case NGX_HTTP_IMAGE_GIF:\n\n        if (ctx->length < 10) {\n            return NGX_DECLINED;\n        }\n\n        width = p[7] * 256 + p[6];\n        height = p[9] * 256 + p[8];\n\n        break;\n\n    case NGX_HTTP_IMAGE_PNG:\n\n        if (ctx->length < 24) {\n            return NGX_DECLINED;\n        }\n\n        width = p[18] * 256 + p[19];\n        height = p[22] * 256 + p[23];\n\n        break;\n\n    case NGX_HTTP_IMAGE_WEBP:\n\n        if (ctx->length < 30) {\n            return NGX_DECLINED;\n        }\n\n        if (p[12] != 'V' || p[13] != 'P' || p[14] != '8') {\n            return NGX_DECLINED;\n        }\n\n        switch (p[15]) {\n\n        case ' ':\n            if (p[20] & 1) {\n                /* not a key frame */\n                return NGX_DECLINED;\n            }\n\n            if (p[23] != 0x9d || p[24] != 0x01 || p[25] != 0x2a) {\n                /* invalid start code */\n                return NGX_DECLINED;\n            }\n\n            width = (p[26] | p[27] << 8) & 0x3fff;\n            height = (p[28] | p[29] << 8) & 0x3fff;\n\n            break;\n\n        case 'L':\n            if (p[20] != 0x2f) {\n                /* invalid signature */\n                return NGX_DECLINED;\n            }\n\n            width = ((p[21] | p[22] << 8) & 0x3fff) + 1;\n            height = ((p[22] >> 6 | p[23] << 2 | p[24] << 10) & 0x3fff) + 1;\n\n            break;\n\n        case 'X':\n            width = (p[24] | p[25] << 8 | p[26] << 16) + 1;\n            height = (p[27] | p[28] << 8 | p[29] << 16) + 1;\n            break;\n\n        default:\n            return NGX_DECLINED;\n        }\n\n        break;\n\n    default:\n\n        return NGX_DECLINED;\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"image size: %d x %d\", (int) width, (int) height);\n\n    ctx->width = width;\n    ctx->height = height;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_buf_t *\nngx_http_image_resize(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx)\n{\n    int                            sx, sy, dx, dy, ox, oy, ax, ay, size,\n                                   colors, palette, transparent, sharpen,\n                                   red, green, blue, t;\n#if (T_NGX_HTTP_IMAGE_FILTER)\n    int                            offset_x, offset_y;\n#endif\n    u_char                        *out;\n    ngx_buf_t                     *b;\n    ngx_uint_t                     resize;\n    gdImagePtr                     src, dst;\n    ngx_pool_cleanup_t            *cln;\n    ngx_http_image_filter_conf_t  *conf;\n\n    src = ngx_http_image_source(r, ctx);\n\n    if (src == NULL) {\n        return NULL;\n    }\n\n    sx = gdImageSX(src);\n    sy = gdImageSY(src);\n\n    conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module);\n\n    if (!ctx->force\n        && ctx->angle == 0\n        && (ngx_uint_t) sx <= ctx->max_width\n        && (ngx_uint_t) sy <= ctx->max_height)\n    {\n        gdImageDestroy(src);\n        return ngx_http_image_asis(r, ctx);\n    }\n\n    colors = gdImageColorsTotal(src);\n\n    if (colors && conf->transparency) {\n        transparent = gdImageGetTransparent(src);\n\n        if (transparent != -1) {\n            palette = colors;\n            red = gdImageRed(src, transparent);\n            green = gdImageGreen(src, transparent);\n            blue = gdImageBlue(src, transparent);\n\n            goto transparent;\n        }\n    }\n\n    palette = 0;\n    transparent = -1;\n    red = 0;\n    green = 0;\n    blue = 0;\n\ntransparent:\n\n    gdImageColorTransparent(src, -1);\n\n    dx = sx;\n    dy = sy;\n\n    if (conf->filter == NGX_HTTP_IMAGE_RESIZE) {\n\n        if ((ngx_uint_t) dx > ctx->max_width) {\n            dy = dy * ctx->max_width / dx;\n            dy = dy ? dy : 1;\n            dx = ctx->max_width;\n        }\n\n        if ((ngx_uint_t) dy > ctx->max_height) {\n            dx = dx * ctx->max_height / dy;\n            dx = dx ? dx : 1;\n            dy = ctx->max_height;\n        }\n\n        resize = 1;\n\n    } else if (conf->filter == NGX_HTTP_IMAGE_ROTATE) {\n\n        resize = 0;\n\n    } else { /* NGX_HTTP_IMAGE_CROP */\n\n        resize = 0;\n\n#if (T_NGX_HTTP_IMAGE_FILTER)\n        if (conf->filter == NGX_HTTP_IMAGE_CROP_KEEPX) {\n            if ((ngx_uint_t) dx > ctx->max_width) {\n                dy = dy * ctx->max_width / dx;\n                dy = dy ? dy : 1;\n                dx = ctx->max_width;\n                resize = 1;\n            }\n\n        } else if (conf->filter == NGX_HTTP_IMAGE_CROP_KEEPY) {\n            if ((ngx_uint_t) dy > ctx->max_height) {\n                dx = dx * ctx->max_height / dy;\n                dx = dx ? dx : 1;\n                dy = ctx->max_height;\n                resize = 1;\n            }\n\n        } else\n#endif\n\n        if ((double) dx / dy < (double) ctx->max_width / ctx->max_height) {\n            if ((ngx_uint_t) dx > ctx->max_width) {\n                dy = dy * ctx->max_width / dx;\n                dy = dy ? dy : 1;\n                dx = ctx->max_width;\n                resize = 1;\n            }\n\n        } else {\n            if ((ngx_uint_t) dy > ctx->max_height) {\n                dx = dx * ctx->max_height / dy;\n                dx = dx ? dx : 1;\n                dy = ctx->max_height;\n                resize = 1;\n            }\n        }\n    }\n\n    if (resize) {\n        dst = ngx_http_image_new(r, dx, dy, palette);\n        if (dst == NULL) {\n            gdImageDestroy(src);\n            return NULL;\n        }\n\n        if (colors == 0) {\n            gdImageSaveAlpha(dst, 1);\n            gdImageAlphaBlending(dst, 0);\n        }\n\n        gdImageCopyResampled(dst, src, 0, 0, 0, 0, dx, dy, sx, sy);\n\n        if (colors) {\n            gdImageTrueColorToPalette(dst, 1, 256);\n        }\n\n        gdImageDestroy(src);\n\n    } else {\n        dst = src;\n    }\n\n    if (ctx->angle) {\n        src = dst;\n\n        ax = (dx % 2 == 0) ? 1 : 0;\n        ay = (dy % 2 == 0) ? 1 : 0;\n\n        switch (ctx->angle) {\n\n        case 90:\n        case 270:\n            dst = ngx_http_image_new(r, dy, dx, palette);\n            if (dst == NULL) {\n                gdImageDestroy(src);\n                return NULL;\n            }\n            if (ctx->angle == 90) {\n                ox = dy / 2 + ay;\n                oy = dx / 2 - ax;\n\n            } else {\n                ox = dy / 2 - ay;\n                oy = dx / 2 + ax;\n            }\n\n            gdImageCopyRotated(dst, src, ox, oy, 0, 0,\n                               dx + ax, dy + ay, ctx->angle);\n            gdImageDestroy(src);\n\n            t = dx;\n            dx = dy;\n            dy = t;\n            break;\n\n        case 180:\n            dst = ngx_http_image_new(r, dx, dy, palette);\n            if (dst == NULL) {\n                gdImageDestroy(src);\n                return NULL;\n            }\n            gdImageCopyRotated(dst, src, dx / 2 - ax, dy / 2 - ay, 0, 0,\n                               dx + ax, dy + ay, ctx->angle);\n            gdImageDestroy(src);\n            break;\n        }\n    }\n\n#if (T_NGX_HTTP_IMAGE_FILTER)\n    if (conf->filter == NGX_HTTP_IMAGE_CROP\n        || conf->filter == NGX_HTTP_IMAGE_CROP_KEEPX\n        || conf->filter == NGX_HTTP_IMAGE_CROP_KEEPY) {\n#else\n    if (conf->filter == NGX_HTTP_IMAGE_CROP) {\n#endif\n\n        src = dst;\n\n        if ((ngx_uint_t) dx > ctx->max_width) {\n            ox = dx - ctx->max_width;\n\n        } else {\n            ox = 0;\n        }\n\n        if ((ngx_uint_t) dy > ctx->max_height) {\n            oy = dy - ctx->max_height;\n\n        } else {\n            oy = 0;\n        }\n\n        if (ox || oy) {\n\n            dst = ngx_http_image_new(r, dx - ox, dy - oy, colors);\n\n            if (dst == NULL) {\n                gdImageDestroy(src);\n                return NULL;\n            }\n\n#if (T_NGX_HTTP_IMAGE_FILTER)\n            offset_x = ngx_http_image_filter_get_value(r, conf->oxcv,\n                                                       conf->offset_x);\n            offset_y = ngx_http_image_filter_get_value(r, conf->oycv,\n                                                       conf->offset_y);\n\n            if (offset_x == NGX_HTTP_IMAGE_OFFSET_LEFT) {\n                ox = 0;\n\n            } else if (offset_x == NGX_HTTP_IMAGE_OFFSET_CENTER) {\n                ox /= 2;\n            }\n\n            if (offset_y == NGX_HTTP_IMAGE_OFFSET_TOP) {\n                oy = 0;\n\n            } else if (offset_y == NGX_HTTP_IMAGE_OFFSET_CENTER) {\n                oy /= 2;\n            }\n#else\n            ox /= 2;\n            oy /= 2;\n#endif\n\n            ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"image crop: %d x %d @ %d x %d\",\n                           dx, dy, ox, oy);\n\n            if (colors == 0) {\n                gdImageSaveAlpha(dst, 1);\n                gdImageAlphaBlending(dst, 0);\n            }\n\n            gdImageCopy(dst, src, 0, 0, ox, oy, dx - ox, dy - oy);\n\n            if (colors) {\n                gdImageTrueColorToPalette(dst, 1, 256);\n            }\n\n            gdImageDestroy(src);\n        }\n    }\n\n    if (transparent != -1 && colors) {\n        gdImageColorTransparent(dst, gdImageColorExact(dst, red, green, blue));\n    }\n\n    sharpen = ngx_http_image_filter_get_value(r, conf->shcv, conf->sharpen);\n    if (sharpen > 0) {\n        gdImageSharpen(dst, sharpen);\n    }\n\n    gdImageInterlace(dst, (int) conf->interlace);\n\n    out = ngx_http_image_out(r, ctx->type, dst, &size);\n\n    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"image: %d x %d %d\", sx, sy, colors);\n\n    gdImageDestroy(dst);\n    ngx_pfree(r->pool, ctx->image);\n\n    if (out == NULL) {\n        return NULL;\n    }\n\n    cln = ngx_pool_cleanup_add(r->pool, 0);\n    if (cln == NULL) {\n        gdFree(out);\n        return NULL;\n    }\n\n    b = ngx_calloc_buf(r->pool);\n    if (b == NULL) {\n        gdFree(out);\n        return NULL;\n    }\n\n    cln->handler = ngx_http_image_cleanup;\n    cln->data = out;\n\n    b->pos = out;\n    b->last = out + size;\n    b->memory = 1;\n    b->last_buf = 1;\n\n    ngx_http_image_length(r, b);\n    ngx_http_weak_etag(r);\n\n    return b;\n}\n\n\nstatic gdImagePtr\nngx_http_image_source(ngx_http_request_t *r, ngx_http_image_filter_ctx_t *ctx)\n{\n    char        *failed;\n    gdImagePtr   img;\n\n    img = NULL;\n\n    switch (ctx->type) {\n\n    case NGX_HTTP_IMAGE_JPEG:\n        img = gdImageCreateFromJpegPtr(ctx->length, ctx->image);\n        failed = \"gdImageCreateFromJpegPtr() failed\";\n        break;\n\n    case NGX_HTTP_IMAGE_GIF:\n        img = gdImageCreateFromGifPtr(ctx->length, ctx->image);\n        failed = \"gdImageCreateFromGifPtr() failed\";\n        break;\n\n    case NGX_HTTP_IMAGE_PNG:\n        img = gdImageCreateFromPngPtr(ctx->length, ctx->image);\n        failed = \"gdImageCreateFromPngPtr() failed\";\n        break;\n\n    case NGX_HTTP_IMAGE_WEBP:\n#if (NGX_HAVE_GD_WEBP)\n        img = gdImageCreateFromWebpPtr(ctx->length, ctx->image);\n        failed = \"gdImageCreateFromWebpPtr() failed\";\n#else\n        failed = \"nginx was built without GD WebP support\";\n#endif\n        break;\n\n    default:\n        failed = \"unknown image type\";\n        break;\n    }\n\n    if (img == NULL) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, failed);\n    }\n\n    return img;\n}\n\n\nstatic gdImagePtr\nngx_http_image_new(ngx_http_request_t *r, int w, int h, int colors)\n{\n    gdImagePtr  img;\n\n    if (colors == 0) {\n        img = gdImageCreateTrueColor(w, h);\n\n        if (img == NULL) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"gdImageCreateTrueColor() failed\");\n            return NULL;\n        }\n\n    } else {\n        img = gdImageCreate(w, h);\n\n        if (img == NULL) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"gdImageCreate() failed\");\n            return NULL;\n        }\n    }\n\n    return img;\n}\n\n\nstatic u_char *\nngx_http_image_out(ngx_http_request_t *r, ngx_uint_t type, gdImagePtr img,\n    int *size)\n{\n    char                          *failed;\n    u_char                        *out;\n    ngx_int_t                      q;\n    ngx_http_image_filter_conf_t  *conf;\n\n    out = NULL;\n\n    switch (type) {\n\n    case NGX_HTTP_IMAGE_JPEG:\n        conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module);\n\n        q = ngx_http_image_filter_get_value(r, conf->jqcv, conf->jpeg_quality);\n        if (q <= 0) {\n            return NULL;\n        }\n\n        out = gdImageJpegPtr(img, size, q);\n        failed = \"gdImageJpegPtr() failed\";\n        break;\n\n    case NGX_HTTP_IMAGE_GIF:\n        out = gdImageGifPtr(img, size);\n        failed = \"gdImageGifPtr() failed\";\n        break;\n\n    case NGX_HTTP_IMAGE_PNG:\n        out = gdImagePngPtr(img, size);\n        failed = \"gdImagePngPtr() failed\";\n        break;\n\n    case NGX_HTTP_IMAGE_WEBP:\n#if (NGX_HAVE_GD_WEBP)\n        conf = ngx_http_get_module_loc_conf(r, ngx_http_image_filter_module);\n\n        q = ngx_http_image_filter_get_value(r, conf->wqcv, conf->webp_quality);\n        if (q <= 0) {\n            return NULL;\n        }\n\n        out = gdImageWebpPtrEx(img, size, q);\n        failed = \"gdImageWebpPtrEx() failed\";\n#else\n        failed = \"nginx was built without GD WebP support\";\n#endif\n        break;\n\n    default:\n        failed = \"unknown image type\";\n        break;\n    }\n\n    if (out == NULL) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, failed);\n    }\n\n    return out;\n}\n\n\nstatic void\nngx_http_image_cleanup(void *data)\n{\n    gdFree(data);\n}\n\n\nstatic ngx_uint_t\nngx_http_image_filter_get_value(ngx_http_request_t *r,\n    ngx_http_complex_value_t *cv, ngx_uint_t v)\n{\n    ngx_str_t  val;\n\n    if (cv == NULL) {\n        return v;\n    }\n\n    if (ngx_http_complex_value(r, cv, &val) != NGX_OK) {\n        return 0;\n    }\n\n    return ngx_http_image_filter_value(&val);\n}\n\n\nstatic ngx_uint_t\nngx_http_image_filter_value(ngx_str_t *value)\n{\n    ngx_int_t  n;\n\n    if (value->len == 1 && value->data[0] == '-') {\n        return (ngx_uint_t) -1;\n    }\n\n    n = ngx_atoi(value->data, value->len);\n\n    if (n > 0) {\n        return (ngx_uint_t) n;\n    }\n\n#if (T_NGX_HTTP_IMAGE_FILTER)\n    if (n == NGX_ERROR) {\n        if (value->len == sizeof(\"left\") - 1\n            && ngx_strncmp(value->data, \"left\", value->len) == 0)\n        {\n            return NGX_HTTP_IMAGE_OFFSET_LEFT;\n        } else if (value->len == sizeof(\"right\") - 1\n                   && ngx_strncmp(value->data, \"right\", sizeof(\"right\") - 1) == 0)\n        {\n            return NGX_HTTP_IMAGE_OFFSET_RIGHT;\n        } else if (value->len == sizeof(\"top\") - 1\n                   && ngx_strncmp(value->data, \"top\", sizeof(\"top\") - 1) == 0)\n        {\n            return NGX_HTTP_IMAGE_OFFSET_TOP;\n        } else if (value->len == sizeof(\"bottom\") - 1\n                   && ngx_strncmp(value->data, \"bottom\", sizeof(\"bottom\") - 1) == 0)\n        {\n            return NGX_HTTP_IMAGE_OFFSET_BOTTOM;\n        }\n    }\n#endif\n\n    return 0;\n}\n\n\nstatic void *\nngx_http_image_filter_create_conf(ngx_conf_t *cf)\n{\n    ngx_http_image_filter_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_image_filter_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc():\n     *\n     *     conf->width = 0;\n     *     conf->height = 0;\n     *     conf->angle = 0;\n     *     conf->wcv = NULL;\n     *     conf->hcv = NULL;\n     *     conf->acv = NULL;\n     *     conf->jqcv = NULL;\n     *     conf->wqcv = NULL;\n     *     conf->shcv = NULL;\n     */\n\n    conf->filter = NGX_CONF_UNSET_UINT;\n    conf->jpeg_quality = NGX_CONF_UNSET_UINT;\n    conf->webp_quality = NGX_CONF_UNSET_UINT;\n    conf->sharpen = NGX_CONF_UNSET_UINT;\n    conf->transparency = NGX_CONF_UNSET;\n    conf->interlace = NGX_CONF_UNSET;\n    conf->buffer_size = NGX_CONF_UNSET_SIZE;\n\n#if (T_NGX_HTTP_IMAGE_FILTER)\n    conf->angle = NGX_CONF_UNSET_UINT;\n    conf->offset_x = NGX_CONF_UNSET_UINT;\n    conf->offset_y = NGX_CONF_UNSET_UINT;\n#endif\n\n    return conf;\n}\n\n\nstatic char *\nngx_http_image_filter_merge_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_http_image_filter_conf_t *prev = parent;\n    ngx_http_image_filter_conf_t *conf = child;\n\n    if (conf->filter == NGX_CONF_UNSET_UINT) {\n\n        if (prev->filter == NGX_CONF_UNSET_UINT) {\n            conf->filter = NGX_HTTP_IMAGE_OFF;\n\n        } else {\n            conf->filter = prev->filter;\n            conf->width = prev->width;\n            conf->height = prev->height;\n            conf->angle = prev->angle;\n            conf->wcv = prev->wcv;\n            conf->hcv = prev->hcv;\n            conf->acv = prev->acv;\n        }\n    }\n\n    if (conf->jpeg_quality == NGX_CONF_UNSET_UINT) {\n\n        /* 75 is libjpeg default quality */\n        ngx_conf_merge_uint_value(conf->jpeg_quality, prev->jpeg_quality, 75);\n\n        if (conf->jqcv == NULL) {\n            conf->jqcv = prev->jqcv;\n        }\n    }\n\n    if (conf->webp_quality == NGX_CONF_UNSET_UINT) {\n\n        /* 80 is libwebp default quality */\n        ngx_conf_merge_uint_value(conf->webp_quality, prev->webp_quality, 80);\n\n        if (conf->wqcv == NULL) {\n            conf->wqcv = prev->wqcv;\n        }\n    }\n\n    if (conf->sharpen == NGX_CONF_UNSET_UINT) {\n        ngx_conf_merge_uint_value(conf->sharpen, prev->sharpen, 0);\n\n        if (conf->shcv == NULL) {\n            conf->shcv = prev->shcv;\n        }\n    }\n\n#if (T_NGX_HTTP_IMAGE_FILTER)\n    if (conf->angle == NGX_CONF_UNSET_UINT) {\n        ngx_conf_merge_uint_value(conf->angle, prev->angle, 0);\n\n        if (conf->acv == NULL) {\n            conf->acv = prev->acv;\n        }\n    }\n#endif\n\n    ngx_conf_merge_value(conf->transparency, prev->transparency, 1);\n\n    ngx_conf_merge_value(conf->interlace, prev->interlace, 0);\n\n    ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size,\n                              1 * 1024 * 1024);\n\n#if (T_NGX_HTTP_IMAGE_FILTER)\n    if (conf->offset_x == NGX_CONF_UNSET_UINT) {\n        ngx_conf_merge_uint_value(conf->offset_x, prev->offset_x,\n                                  NGX_HTTP_IMAGE_OFFSET_CENTER);\n\n        if (conf->oxcv == NULL) {\n            conf->oxcv = prev->oxcv;\n        }\n    }\n\n    if (conf->offset_y == NGX_CONF_UNSET_UINT) {\n        ngx_conf_merge_uint_value(conf->offset_y, prev->offset_y,\n                                  NGX_HTTP_IMAGE_OFFSET_CENTER);\n\n        if (conf->oycv == NULL) {\n            conf->oycv = prev->oycv;\n        }\n    }\n#endif\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_image_filter(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_image_filter_conf_t *imcf = conf;\n\n    ngx_str_t                         *value;\n    ngx_int_t                          n;\n    ngx_uint_t                         i;\n    ngx_http_complex_value_t           cv;\n    ngx_http_compile_complex_value_t   ccv;\n\n    value = cf->args->elts;\n\n    i = 1;\n\n    if (cf->args->nelts == 2) {\n        if (ngx_strcmp(value[i].data, \"off\") == 0) {\n            imcf->filter = NGX_HTTP_IMAGE_OFF;\n\n        } else if (ngx_strcmp(value[i].data, \"test\") == 0) {\n            imcf->filter = NGX_HTTP_IMAGE_TEST;\n\n        } else if (ngx_strcmp(value[i].data, \"size\") == 0) {\n            imcf->filter = NGX_HTTP_IMAGE_SIZE;\n\n        } else {\n            goto failed;\n        }\n\n        return NGX_CONF_OK;\n\n    } else if (cf->args->nelts == 3) {\n\n        if (ngx_strcmp(value[i].data, \"rotate\") == 0) {\n            if (imcf->filter != NGX_HTTP_IMAGE_RESIZE\n                && imcf->filter != NGX_HTTP_IMAGE_CROP)\n            {\n                imcf->filter = NGX_HTTP_IMAGE_ROTATE;\n            }\n\n            ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n\n            ccv.cf = cf;\n            ccv.value = &value[++i];\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                n = ngx_http_image_filter_value(&value[i]);\n\n                if (n != 90 && n != 180 && n != 270) {\n                    goto failed;\n                }\n\n                imcf->angle = (ngx_uint_t) n;\n\n            } else {\n                imcf->acv = ngx_palloc(cf->pool,\n                                       sizeof(ngx_http_complex_value_t));\n                if (imcf->acv == NULL) {\n                    return NGX_CONF_ERROR;\n                }\n\n                *imcf->acv = cv;\n            }\n\n            return NGX_CONF_OK;\n\n        } else {\n            goto failed;\n        }\n    }\n\n    if (ngx_strcmp(value[i].data, \"resize\") == 0) {\n        imcf->filter = NGX_HTTP_IMAGE_RESIZE;\n\n    } else if (ngx_strcmp(value[i].data, \"crop\") == 0) {\n        imcf->filter = NGX_HTTP_IMAGE_CROP;\n\n#if (T_NGX_HTTP_IMAGE_FILTER)\n    } else if (ngx_strcmp(value[i].data, \"crop_keepx\") == 0) {\n        imcf->filter = NGX_HTTP_IMAGE_CROP_KEEPX;\n\n    } else if (ngx_strcmp(value[i].data, \"crop_keepy\") == 0) {\n        imcf->filter = NGX_HTTP_IMAGE_CROP_KEEPY;\n\n#endif\n    } else {\n        goto failed;\n    }\n\n    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n\n    ccv.cf = cf;\n    ccv.value = &value[++i];\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        n = ngx_http_image_filter_value(&value[i]);\n\n        if (n == 0) {\n            goto failed;\n        }\n\n        imcf->width = (ngx_uint_t) n;\n\n    } else {\n        imcf->wcv = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));\n        if (imcf->wcv == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        *imcf->wcv = cv;\n    }\n\n    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n\n    ccv.cf = cf;\n    ccv.value = &value[++i];\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        n = ngx_http_image_filter_value(&value[i]);\n\n        if (n == 0) {\n            goto failed;\n        }\n\n        imcf->height = (ngx_uint_t) n;\n\n    } else {\n        imcf->hcv = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));\n        if (imcf->hcv == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        *imcf->hcv = cv;\n    }\n\n    return NGX_CONF_OK;\n\nfailed:\n\n    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, \"invalid parameter \\\"%V\\\"\",\n                       &value[i]);\n\n    return NGX_CONF_ERROR;\n}\n\n\nstatic char *\nngx_http_image_filter_jpeg_quality(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n    ngx_http_image_filter_conf_t *imcf = conf;\n\n    ngx_str_t                         *value;\n    ngx_int_t                          n;\n    ngx_http_complex_value_t           cv;\n    ngx_http_compile_complex_value_t   ccv;\n\n    value = cf->args->elts;\n\n    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n\n    ccv.cf = cf;\n    ccv.value = &value[1];\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        n = ngx_http_image_filter_value(&value[1]);\n\n        if (n <= 0) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"invalid value \\\"%V\\\"\", &value[1]);\n            return NGX_CONF_ERROR;\n        }\n\n        imcf->jpeg_quality = (ngx_uint_t) n;\n\n    } else {\n        imcf->jqcv = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));\n        if (imcf->jqcv == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        *imcf->jqcv = cv;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_image_filter_webp_quality(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n    ngx_http_image_filter_conf_t *imcf = conf;\n\n    ngx_str_t                         *value;\n    ngx_int_t                          n;\n    ngx_http_complex_value_t           cv;\n    ngx_http_compile_complex_value_t   ccv;\n\n    value = cf->args->elts;\n\n    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n\n    ccv.cf = cf;\n    ccv.value = &value[1];\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        n = ngx_http_image_filter_value(&value[1]);\n\n        if (n <= 0) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"invalid value \\\"%V\\\"\", &value[1]);\n            return NGX_CONF_ERROR;\n        }\n\n        imcf->webp_quality = (ngx_uint_t) n;\n\n    } else {\n        imcf->wqcv = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));\n        if (imcf->wqcv == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        *imcf->wqcv = cv;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_image_filter_sharpen(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n    ngx_http_image_filter_conf_t *imcf = conf;\n\n    ngx_str_t                         *value;\n    ngx_int_t                          n;\n    ngx_http_complex_value_t           cv;\n    ngx_http_compile_complex_value_t   ccv;\n\n    value = cf->args->elts;\n\n    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n\n    ccv.cf = cf;\n    ccv.value = &value[1];\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        n = ngx_http_image_filter_value(&value[1]);\n\n        if (n < 0) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"invalid value \\\"%V\\\"\", &value[1]);\n            return NGX_CONF_ERROR;\n        }\n\n        imcf->sharpen = (ngx_uint_t) n;\n\n    } else {\n        imcf->shcv = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));\n        if (imcf->shcv == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        *imcf->shcv = cv;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\n#if (T_NGX_HTTP_IMAGE_FILTER)\nstatic char *\nngx_http_image_filter_offset(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n    ngx_http_image_filter_conf_t *imcf = conf;\n\n    ngx_str_t                         *value;\n    ngx_http_complex_value_t           cv;\n    ngx_http_compile_complex_value_t   ccv;\n\n    value = cf->args->elts;\n\n    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n\n    ccv.cf = cf;\n    ccv.value = &value[1];\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        imcf->offset_x = ngx_http_image_filter_value(&value[1]);\n\n    } else {\n        imcf->oxcv = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));\n        if (imcf->oxcv == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        *imcf->oxcv = cv;\n    }\n\n    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n\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        imcf->offset_y = ngx_http_image_filter_value(&value[2]);\n\n    } else {\n        imcf->oycv = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));\n        if (imcf->oycv == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        *imcf->oycv = cv;\n    }\n\n    return NGX_CONF_OK;\n}\n#endif\n\n\nstatic ngx_int_t\nngx_http_image_filter_init(ngx_conf_t *cf)\n{\n    ngx_http_next_header_filter = ngx_http_top_header_filter;\n    ngx_http_top_header_filter = ngx_http_image_header_filter;\n\n    ngx_http_next_body_filter = ngx_http_top_body_filter;\n    ngx_http_top_body_filter = ngx_http_image_body_filter;\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "src/http/modules/ngx_http_index_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\ntypedef struct {\n    ngx_str_t                name;\n    ngx_array_t             *lengths;\n    ngx_array_t             *values;\n} ngx_http_index_t;\n\n\ntypedef struct {\n    ngx_array_t             *indices;    /* array of ngx_http_index_t */\n    size_t                   max_index_len;\n} ngx_http_index_loc_conf_t;\n\n\n#define NGX_HTTP_DEFAULT_INDEX   \"index.html\"\n\n\nstatic ngx_int_t ngx_http_index_test_dir(ngx_http_request_t *r,\n    ngx_http_core_loc_conf_t *clcf, u_char *path, u_char *last);\nstatic ngx_int_t ngx_http_index_error(ngx_http_request_t *r,\n    ngx_http_core_loc_conf_t *clcf, u_char *file, ngx_err_t err);\n\nstatic ngx_int_t ngx_http_index_init(ngx_conf_t *cf);\nstatic void *ngx_http_index_create_loc_conf(ngx_conf_t *cf);\nstatic char *ngx_http_index_merge_loc_conf(ngx_conf_t *cf,\n    void *parent, void *child);\nstatic char *ngx_http_index_set_index(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n\n\nstatic ngx_command_t  ngx_http_index_commands[] = {\n\n    { ngx_string(\"index\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,\n      ngx_http_index_set_index,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_index_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    ngx_http_index_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    ngx_http_index_create_loc_conf,        /* create location configuration */\n    ngx_http_index_merge_loc_conf          /* merge location configuration */\n};\n\n\nngx_module_t  ngx_http_index_module = {\n    NGX_MODULE_V1,\n    &ngx_http_index_module_ctx,            /* module context */\n    ngx_http_index_commands,               /* 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/*\n * Try to open/test the first index file before the test of directory\n * existence because valid requests should prevail over invalid ones.\n * If open()/stat() of a file will fail then stat() of a directory\n * should be faster because kernel may have already cached some data.\n * Besides, Win32 may return ERROR_PATH_NOT_FOUND (NGX_ENOTDIR) at once.\n * Unix has ENOTDIR error; however, it's less helpful than Win32's one:\n * it only indicates that path points to a regular file, not a directory.\n */\n\nstatic ngx_int_t\nngx_http_index_handler(ngx_http_request_t *r)\n{\n    u_char                       *p, *name;\n    size_t                        len, root, reserve, allocated;\n    ngx_int_t                     rc;\n    ngx_str_t                     path, uri;\n    ngx_uint_t                    i, dir_tested;\n    ngx_http_index_t             *index;\n    ngx_open_file_info_t          of;\n    ngx_http_script_code_pt       code;\n    ngx_http_script_engine_t      e;\n    ngx_http_core_loc_conf_t     *clcf;\n    ngx_http_index_loc_conf_t    *ilcf;\n    ngx_http_script_len_code_pt   lcode;\n\n    if (r->uri.data[r->uri.len - 1] != '/') {\n        return NGX_DECLINED;\n    }\n\n    if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD|NGX_HTTP_POST))) {\n        return NGX_DECLINED;\n    }\n\n    ilcf = ngx_http_get_module_loc_conf(r, ngx_http_index_module);\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    allocated = 0;\n    root = 0;\n    dir_tested = 0;\n    name = NULL;\n    /* suppress MSVC warning */\n    path.data = NULL;\n\n    index = ilcf->indices->elts;\n    for (i = 0; i < ilcf->indices->nelts; i++) {\n\n        if (index[i].lengths == NULL) {\n\n            if (index[i].name.data[0] == '/') {\n                return ngx_http_internal_redirect(r, &index[i].name, &r->args);\n            }\n\n            reserve = ilcf->max_index_len;\n            len = index[i].name.len;\n\n        } else {\n            ngx_memzero(&e, sizeof(ngx_http_script_engine_t));\n\n            e.ip = index[i].lengths->elts;\n            e.request = r;\n            e.flushed = 1;\n\n            /* 1 is for terminating '\\0' as in static names */\n            len = 1;\n\n            while (*(uintptr_t *) e.ip) {\n                lcode = *(ngx_http_script_len_code_pt *) e.ip;\n                len += lcode(&e);\n            }\n\n            /* 16 bytes are preallocation */\n\n            reserve = len + 16;\n        }\n\n        if (reserve > allocated) {\n\n            name = ngx_http_map_uri_to_path(r, &path, &root, reserve);\n            if (name == NULL) {\n                return NGX_HTTP_INTERNAL_SERVER_ERROR;\n            }\n\n            allocated = path.data + path.len - name;\n        }\n\n        if (index[i].values == NULL) {\n\n            /* index[i].name.len includes the terminating '\\0' */\n\n            ngx_memcpy(name, index[i].name.data, index[i].name.len);\n\n            path.len = (name + index[i].name.len - 1) - path.data;\n\n        } else {\n            e.ip = index[i].values->elts;\n            e.pos = name;\n\n            while (*(uintptr_t *) e.ip) {\n                code = *(ngx_http_script_code_pt *) e.ip;\n                code((ngx_http_script_engine_t *) &e);\n            }\n\n            if (*name == '/') {\n                uri.len = len - 1;\n                uri.data = name;\n                return ngx_http_internal_redirect(r, &uri, &r->args);\n            }\n\n            path.len = e.pos - path.data;\n\n            *e.pos = '\\0';\n        }\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"open index \\\"%V\\\"\", &path);\n\n        ngx_memzero(&of, sizeof(ngx_open_file_info_t));\n\n        of.read_ahead = clcf->read_ahead;\n        of.directio = clcf->directio;\n        of.valid = clcf->open_file_cache_valid;\n        of.min_uses = clcf->open_file_cache_min_uses;\n        of.test_only = 1;\n        of.errors = clcf->open_file_cache_errors;\n        of.events = clcf->open_file_cache_events;\n\n        if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) {\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n\n        if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)\n            != NGX_OK)\n        {\n            if (of.err == 0) {\n                return NGX_HTTP_INTERNAL_SERVER_ERROR;\n            }\n\n            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, of.err,\n                           \"%s \\\"%s\\\" failed\", of.failed, path.data);\n\n#if (NGX_HAVE_OPENAT)\n            if (of.err == NGX_EMLINK\n                || of.err == NGX_ELOOP)\n            {\n                return NGX_HTTP_FORBIDDEN;\n            }\n#endif\n\n            if (of.err == NGX_ENOTDIR\n                || of.err == NGX_ENAMETOOLONG\n                || of.err == NGX_EACCES)\n            {\n                return ngx_http_index_error(r, clcf, path.data, of.err);\n            }\n\n            if (!dir_tested) {\n                rc = ngx_http_index_test_dir(r, clcf, path.data, name - 1);\n\n                if (rc != NGX_OK) {\n                    return rc;\n                }\n\n                dir_tested = 1;\n            }\n\n            if (of.err == NGX_ENOENT) {\n                continue;\n            }\n\n            ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err,\n                          \"%s \\\"%s\\\" failed\", of.failed, path.data);\n\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n\n        uri.len = r->uri.len + len - 1;\n\n        if (!clcf->alias) {\n            uri.data = path.data + root;\n\n        } else {\n            uri.data = ngx_pnalloc(r->pool, uri.len);\n            if (uri.data == NULL) {\n                return NGX_HTTP_INTERNAL_SERVER_ERROR;\n            }\n\n            p = ngx_copy(uri.data, r->uri.data, r->uri.len);\n            ngx_memcpy(p, name, len - 1);\n        }\n\n        return ngx_http_internal_redirect(r, &uri, &r->args);\n    }\n\n    return NGX_DECLINED;\n}\n\n\nstatic ngx_int_t\nngx_http_index_test_dir(ngx_http_request_t *r, ngx_http_core_loc_conf_t *clcf,\n    u_char *path, u_char *last)\n{\n    u_char                c;\n    ngx_str_t             dir;\n    ngx_open_file_info_t  of;\n\n    c = *last;\n    if (c != '/' || path == last) {\n        /* \"alias\" without trailing slash */\n        c = *(++last);\n    }\n    *last = '\\0';\n\n    dir.len = last - path;\n    dir.data = path;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http index check dir: \\\"%V\\\"\", &dir);\n\n    ngx_memzero(&of, sizeof(ngx_open_file_info_t));\n\n    of.test_dir = 1;\n    of.test_only = 1;\n    of.valid = clcf->open_file_cache_valid;\n    of.errors = clcf->open_file_cache_errors;\n\n    if (ngx_http_set_disable_symlinks(r, clcf, &dir, &of) != NGX_OK) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    if (ngx_open_cached_file(clcf->open_file_cache, &dir, &of, r->pool)\n        != NGX_OK)\n    {\n        if (of.err) {\n\n#if (NGX_HAVE_OPENAT)\n            if (of.err == NGX_EMLINK\n                || of.err == NGX_ELOOP)\n            {\n                return NGX_HTTP_FORBIDDEN;\n            }\n#endif\n\n            if (of.err == NGX_ENOENT) {\n                *last = c;\n                return ngx_http_index_error(r, clcf, dir.data, NGX_ENOENT);\n            }\n\n            if (of.err == NGX_EACCES) {\n\n                *last = c;\n\n                /*\n                 * ngx_http_index_test_dir() is called after the first index\n                 * file testing has returned an error distinct from NGX_EACCES.\n                 * This means that directory searching is allowed.\n                 */\n\n                return NGX_OK;\n            }\n\n            ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err,\n                          \"%s \\\"%s\\\" failed\", of.failed, dir.data);\n        }\n\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    *last = c;\n\n    if (of.is_dir) {\n        return NGX_OK;\n    }\n\n    ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,\n                  \"\\\"%s\\\" is not a directory\", dir.data);\n\n    return NGX_HTTP_INTERNAL_SERVER_ERROR;\n}\n\n\nstatic ngx_int_t\nngx_http_index_error(ngx_http_request_t *r, ngx_http_core_loc_conf_t  *clcf,\n    u_char *file, ngx_err_t err)\n{\n    if (err == NGX_EACCES) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, err,\n                      \"\\\"%s\\\" is forbidden\", file);\n\n        return NGX_HTTP_FORBIDDEN;\n    }\n\n    if (clcf->log_not_found) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, err,\n                      \"\\\"%s\\\" is not found\", file);\n    }\n\n    return NGX_HTTP_NOT_FOUND;\n}\n\n\nstatic void *\nngx_http_index_create_loc_conf(ngx_conf_t *cf)\n{\n    ngx_http_index_loc_conf_t  *conf;\n\n    conf = ngx_palloc(cf->pool, sizeof(ngx_http_index_loc_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    conf->indices = NULL;\n    conf->max_index_len = 0;\n\n    return conf;\n}\n\n\nstatic char *\nngx_http_index_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_http_index_loc_conf_t  *prev = parent;\n    ngx_http_index_loc_conf_t  *conf = child;\n\n    ngx_http_index_t  *index;\n\n    if (conf->indices == NULL) {\n        conf->indices = prev->indices;\n        conf->max_index_len = prev->max_index_len;\n    }\n\n    if (conf->indices == NULL) {\n        conf->indices = ngx_array_create(cf->pool, 1, sizeof(ngx_http_index_t));\n        if (conf->indices == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        index = ngx_array_push(conf->indices);\n        if (index == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        index->name.len = sizeof(NGX_HTTP_DEFAULT_INDEX);\n        index->name.data = (u_char *) NGX_HTTP_DEFAULT_INDEX;\n        index->lengths = NULL;\n        index->values = NULL;\n\n        conf->max_index_len = sizeof(NGX_HTTP_DEFAULT_INDEX);\n\n        return NGX_CONF_OK;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_index_init(ngx_conf_t *cf)\n{\n    ngx_http_handler_pt        *h;\n    ngx_http_core_main_conf_t  *cmcf;\n\n    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);\n\n    h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);\n    if (h == NULL) {\n        return NGX_ERROR;\n    }\n\n    *h = ngx_http_index_handler;\n\n    return NGX_OK;\n}\n\n\n/* TODO: warn about duplicate indices */\n\nstatic char *\nngx_http_index_set_index(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_index_loc_conf_t *ilcf = conf;\n\n    ngx_str_t                  *value;\n    ngx_uint_t                  i, n;\n    ngx_http_index_t           *index;\n    ngx_http_script_compile_t   sc;\n\n    if (ilcf->indices == NULL) {\n        ilcf->indices = ngx_array_create(cf->pool, 2, sizeof(ngx_http_index_t));\n        if (ilcf->indices == NULL) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    value = cf->args->elts;\n\n    for (i = 1; i < cf->args->nelts; i++) {\n\n        if (value[i].data[0] == '/' && i != cf->args->nelts - 1) {\n            ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                               \"only the last index in \\\"index\\\" directive \"\n                               \"should be absolute\");\n        }\n\n        if (value[i].len == 0) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"index \\\"%V\\\" in \\\"index\\\" directive is invalid\",\n                               &value[1]);\n            return NGX_CONF_ERROR;\n        }\n\n        index = ngx_array_push(ilcf->indices);\n        if (index == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        index->name.len = value[i].len;\n        index->name.data = value[i].data;\n        index->lengths = NULL;\n        index->values = NULL;\n\n        n = ngx_http_script_variables_count(&value[i]);\n\n        if (n == 0) {\n            if (ilcf->max_index_len < index->name.len) {\n                ilcf->max_index_len = index->name.len;\n            }\n\n            if (index->name.data[0] == '/') {\n                continue;\n            }\n\n            /* include the terminating '\\0' to the length to use ngx_memcpy() */\n            index->name.len++;\n\n            continue;\n        }\n\n        ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));\n\n        sc.cf = cf;\n        sc.source = &value[i];\n        sc.lengths = &index->lengths;\n        sc.values = &index->values;\n        sc.variables = n;\n        sc.complete_lengths = 1;\n        sc.complete_values = 1;\n\n        if (ngx_http_script_compile(&sc) != NGX_OK) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    return NGX_CONF_OK;\n}\n"
  },
  {
    "path": "src/http/modules/ngx_http_limit_conn_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\n#define NGX_HTTP_LIMIT_CONN_PASSED            1\n#define NGX_HTTP_LIMIT_CONN_REJECTED          2\n#define NGX_HTTP_LIMIT_CONN_REJECTED_DRY_RUN  3\n\n\ntypedef struct {\n    u_char                        color;\n    u_char                        len;\n    u_short                       conn;\n    u_char                        data[1];\n} ngx_http_limit_conn_node_t;\n\n\ntypedef struct {\n    ngx_shm_zone_t               *shm_zone;\n    ngx_rbtree_node_t            *node;\n} ngx_http_limit_conn_cleanup_t;\n\n\ntypedef struct {\n    ngx_rbtree_t                  rbtree;\n    ngx_rbtree_node_t             sentinel;\n} ngx_http_limit_conn_shctx_t;\n\n\ntypedef struct {\n    ngx_http_limit_conn_shctx_t  *sh;\n    ngx_slab_pool_t              *shpool;\n    ngx_http_complex_value_t      key;\n} ngx_http_limit_conn_ctx_t;\n\n\ntypedef struct {\n    ngx_shm_zone_t               *shm_zone;\n    ngx_uint_t                    conn;\n} ngx_http_limit_conn_limit_t;\n\n\ntypedef struct {\n    ngx_array_t                   limits;\n    ngx_uint_t                    log_level;\n    ngx_uint_t                    status_code;\n    ngx_flag_t                    dry_run;\n} ngx_http_limit_conn_conf_t;\n\n\nstatic ngx_rbtree_node_t *ngx_http_limit_conn_lookup(ngx_rbtree_t *rbtree,\n    ngx_str_t *key, uint32_t hash);\nstatic void ngx_http_limit_conn_cleanup(void *data);\nstatic ngx_inline void ngx_http_limit_conn_cleanup_all(ngx_pool_t *pool);\n\nstatic ngx_int_t ngx_http_limit_conn_status_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic void *ngx_http_limit_conn_create_conf(ngx_conf_t *cf);\nstatic char *ngx_http_limit_conn_merge_conf(ngx_conf_t *cf, void *parent,\n    void *child);\nstatic char *ngx_http_limit_conn_zone(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_http_limit_conn(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic ngx_int_t ngx_http_limit_conn_add_variables(ngx_conf_t *cf);\nstatic ngx_int_t ngx_http_limit_conn_init(ngx_conf_t *cf);\n\n\nstatic ngx_conf_enum_t  ngx_http_limit_conn_log_levels[] = {\n    { ngx_string(\"info\"), NGX_LOG_INFO },\n    { ngx_string(\"notice\"), NGX_LOG_NOTICE },\n    { ngx_string(\"warn\"), NGX_LOG_WARN },\n    { ngx_string(\"error\"), NGX_LOG_ERR },\n    { ngx_null_string, 0 }\n};\n\n\nstatic ngx_conf_num_bounds_t  ngx_http_limit_conn_status_bounds = {\n    ngx_conf_check_num_bounds, 400, 599\n};\n\n\nstatic ngx_command_t  ngx_http_limit_conn_commands[] = {\n\n    { ngx_string(\"limit_conn_zone\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE2,\n      ngx_http_limit_conn_zone,\n      0,\n      0,\n      NULL },\n\n    { ngx_string(\"limit_conn\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,\n      ngx_http_limit_conn,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"limit_conn_log_level\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_enum_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_limit_conn_conf_t, log_level),\n      &ngx_http_limit_conn_log_levels },\n\n    { ngx_string(\"limit_conn_status\"),\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_limit_conn_conf_t, status_code),\n      &ngx_http_limit_conn_status_bounds },\n\n    { ngx_string(\"limit_conn_dry_run\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_limit_conn_conf_t, dry_run),\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_limit_conn_module_ctx = {\n    ngx_http_limit_conn_add_variables,     /* preconfiguration */\n    ngx_http_limit_conn_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    ngx_http_limit_conn_create_conf,       /* create location configuration */\n    ngx_http_limit_conn_merge_conf         /* merge location configuration */\n};\n\n\nngx_module_t  ngx_http_limit_conn_module = {\n    NGX_MODULE_V1,\n    &ngx_http_limit_conn_module_ctx,       /* module context */\n    ngx_http_limit_conn_commands,          /* 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_http_variable_t  ngx_http_limit_conn_vars[] = {\n\n    { ngx_string(\"limit_conn_status\"), NULL,\n      ngx_http_limit_conn_status_variable, 0, NGX_HTTP_VAR_NOCACHEABLE, 0 },\n\n      ngx_http_null_variable\n};\n\n\nstatic ngx_str_t  ngx_http_limit_conn_status[] = {\n    ngx_string(\"PASSED\"),\n    ngx_string(\"REJECTED\"),\n    ngx_string(\"REJECTED_DRY_RUN\")\n};\n\n\nstatic ngx_int_t\nngx_http_limit_conn_handler(ngx_http_request_t *r)\n{\n    size_t                          n;\n    uint32_t                        hash;\n    ngx_str_t                       key;\n    ngx_uint_t                      i;\n    ngx_rbtree_node_t              *node;\n    ngx_pool_cleanup_t             *cln;\n    ngx_http_limit_conn_ctx_t      *ctx;\n    ngx_http_limit_conn_node_t     *lc;\n    ngx_http_limit_conn_conf_t     *lccf;\n    ngx_http_limit_conn_limit_t    *limits;\n    ngx_http_limit_conn_cleanup_t  *lccln;\n\n    if (r->main->limit_conn_status) {\n        return NGX_DECLINED;\n    }\n\n    lccf = ngx_http_get_module_loc_conf(r, ngx_http_limit_conn_module);\n    limits = lccf->limits.elts;\n\n    for (i = 0; i < lccf->limits.nelts; i++) {\n        ctx = limits[i].shm_zone->data;\n\n        if (ngx_http_complex_value(r, &ctx->key, &key) != NGX_OK) {\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n\n        if (key.len == 0) {\n            continue;\n        }\n\n        if (key.len > 255) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"the value of the \\\"%V\\\" key \"\n                          \"is more than 255 bytes: \\\"%V\\\"\",\n                          &ctx->key.value, &key);\n            continue;\n        }\n\n        r->main->limit_conn_status = NGX_HTTP_LIMIT_CONN_PASSED;\n\n        hash = ngx_crc32_short(key.data, key.len);\n\n        ngx_shmtx_lock(&ctx->shpool->mutex);\n\n        node = ngx_http_limit_conn_lookup(&ctx->sh->rbtree, &key, hash);\n\n        if (node == NULL) {\n\n            n = offsetof(ngx_rbtree_node_t, color)\n                + offsetof(ngx_http_limit_conn_node_t, data)\n                + key.len;\n\n            node = ngx_slab_alloc_locked(ctx->shpool, n);\n\n            if (node == NULL) {\n                ngx_shmtx_unlock(&ctx->shpool->mutex);\n                ngx_http_limit_conn_cleanup_all(r->pool);\n\n                if (lccf->dry_run) {\n                    r->main->limit_conn_status =\n                                          NGX_HTTP_LIMIT_CONN_REJECTED_DRY_RUN;\n                    return NGX_DECLINED;\n                }\n\n                r->main->limit_conn_status = NGX_HTTP_LIMIT_CONN_REJECTED;\n\n                return lccf->status_code;\n            }\n\n            lc = (ngx_http_limit_conn_node_t *) &node->color;\n\n            node->key = hash;\n            lc->len = (u_char) key.len;\n            lc->conn = 1;\n            ngx_memcpy(lc->data, key.data, key.len);\n\n            ngx_rbtree_insert(&ctx->sh->rbtree, node);\n\n        } else {\n\n            lc = (ngx_http_limit_conn_node_t *) &node->color;\n\n            if ((ngx_uint_t) lc->conn >= limits[i].conn) {\n\n                ngx_shmtx_unlock(&ctx->shpool->mutex);\n\n                ngx_log_error(lccf->log_level, r->connection->log, 0,\n                              \"limiting connections%s by zone \\\"%V\\\"\",\n                              lccf->dry_run ? \", dry run,\" : \"\",\n                              &limits[i].shm_zone->shm.name);\n\n                ngx_http_limit_conn_cleanup_all(r->pool);\n\n                if (lccf->dry_run) {\n                    r->main->limit_conn_status =\n                                          NGX_HTTP_LIMIT_CONN_REJECTED_DRY_RUN;\n                    return NGX_DECLINED;\n                }\n\n                r->main->limit_conn_status = NGX_HTTP_LIMIT_CONN_REJECTED;\n\n                return lccf->status_code;\n            }\n\n            lc->conn++;\n        }\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"limit conn: %08Xi %d\", node->key, lc->conn);\n\n        ngx_shmtx_unlock(&ctx->shpool->mutex);\n\n        cln = ngx_pool_cleanup_add(r->pool,\n                                   sizeof(ngx_http_limit_conn_cleanup_t));\n        if (cln == NULL) {\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n\n        cln->handler = ngx_http_limit_conn_cleanup;\n        lccln = cln->data;\n\n        lccln->shm_zone = limits[i].shm_zone;\n        lccln->node = node;\n    }\n\n    return NGX_DECLINED;\n}\n\n\nstatic void\nngx_http_limit_conn_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_limit_conn_node_t   *lcn, *lcnt;\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            lcn = (ngx_http_limit_conn_node_t *) &node->color;\n            lcnt = (ngx_http_limit_conn_node_t *) &temp->color;\n\n            p = (ngx_memn2cmp(lcn->data, lcnt->data, lcn->len, lcnt->len) < 0)\n                ? &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_rbtree_node_t *\nngx_http_limit_conn_lookup(ngx_rbtree_t *rbtree, ngx_str_t *key, uint32_t hash)\n{\n    ngx_int_t                    rc;\n    ngx_rbtree_node_t           *node, *sentinel;\n    ngx_http_limit_conn_node_t  *lcn;\n\n    node = rbtree->root;\n    sentinel = 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        lcn = (ngx_http_limit_conn_node_t *) &node->color;\n\n        rc = ngx_memn2cmp(key->data, lcn->data, key->len, (size_t) lcn->len);\n\n        if (rc == 0) {\n            return node;\n        }\n\n        node = (rc < 0) ? node->left : node->right;\n    }\n\n    return NULL;\n}\n\n\nstatic void\nngx_http_limit_conn_cleanup(void *data)\n{\n    ngx_http_limit_conn_cleanup_t  *lccln = data;\n\n    ngx_rbtree_node_t           *node;\n    ngx_http_limit_conn_ctx_t   *ctx;\n    ngx_http_limit_conn_node_t  *lc;\n\n    ctx = lccln->shm_zone->data;\n    node = lccln->node;\n    lc = (ngx_http_limit_conn_node_t *) &node->color;\n\n    ngx_shmtx_lock(&ctx->shpool->mutex);\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, lccln->shm_zone->shm.log, 0,\n                   \"limit conn cleanup: %08Xi %d\", node->key, lc->conn);\n\n    lc->conn--;\n\n    if (lc->conn == 0) {\n        ngx_rbtree_delete(&ctx->sh->rbtree, node);\n        ngx_slab_free_locked(ctx->shpool, node);\n    }\n\n    ngx_shmtx_unlock(&ctx->shpool->mutex);\n}\n\n\nstatic ngx_inline void\nngx_http_limit_conn_cleanup_all(ngx_pool_t *pool)\n{\n    ngx_pool_cleanup_t  *cln;\n\n    cln = pool->cleanup;\n\n    while (cln && cln->handler == ngx_http_limit_conn_cleanup) {\n        ngx_http_limit_conn_cleanup(cln->data);\n        cln = cln->next;\n    }\n\n    pool->cleanup = cln;\n}\n\n\nstatic ngx_int_t\nngx_http_limit_conn_init_zone(ngx_shm_zone_t *shm_zone, void *data)\n{\n    ngx_http_limit_conn_ctx_t  *octx = data;\n\n    size_t                      len;\n    ngx_http_limit_conn_ctx_t  *ctx;\n\n    ctx = shm_zone->data;\n\n    if (octx) {\n        if (ctx->key.value.len != octx->key.value.len\n            || ngx_strncmp(ctx->key.value.data, octx->key.value.data,\n                           ctx->key.value.len)\n               != 0)\n        {\n            ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0,\n                          \"limit_conn_zone \\\"%V\\\" uses the \\\"%V\\\" key \"\n                          \"while previously it used the \\\"%V\\\" key\",\n                          &shm_zone->shm.name, &ctx->key.value,\n                          &octx->key.value);\n            return NGX_ERROR;\n        }\n\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_limit_conn_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_limit_conn_rbtree_insert_value);\n\n    len = sizeof(\" in limit_conn_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 limit_conn_zone \\\"%V\\\"%Z\",\n                &shm_zone->shm.name);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_limit_conn_status_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    if (r->main->limit_conn_status == 0) {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->len = ngx_http_limit_conn_status[r->main->limit_conn_status - 1].len;\n    v->data = ngx_http_limit_conn_status[r->main->limit_conn_status - 1].data;\n\n    return NGX_OK;\n}\n\n\nstatic void *\nngx_http_limit_conn_create_conf(ngx_conf_t *cf)\n{\n    ngx_http_limit_conn_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_conn_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc():\n     *\n     *     conf->limits.elts = NULL;\n     */\n\n    conf->log_level = NGX_CONF_UNSET_UINT;\n    conf->status_code = NGX_CONF_UNSET_UINT;\n    conf->dry_run = NGX_CONF_UNSET;\n\n    return conf;\n}\n\n\nstatic char *\nngx_http_limit_conn_merge_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_http_limit_conn_conf_t *prev = parent;\n    ngx_http_limit_conn_conf_t *conf = child;\n\n    if (conf->limits.elts == NULL) {\n        conf->limits = prev->limits;\n    }\n\n    ngx_conf_merge_uint_value(conf->log_level, prev->log_level, NGX_LOG_ERR);\n    ngx_conf_merge_uint_value(conf->status_code, prev->status_code,\n                              NGX_HTTP_SERVICE_UNAVAILABLE);\n\n    ngx_conf_merge_value(conf->dry_run, prev->dry_run, 0);\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_limit_conn_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    u_char                            *p;\n    ssize_t                            size;\n    ngx_str_t                         *value, name, s;\n    ngx_uint_t                         i;\n    ngx_shm_zone_t                    *shm_zone;\n    ngx_http_limit_conn_ctx_t         *ctx;\n    ngx_http_compile_complex_value_t   ccv;\n\n    value = cf->args->elts;\n\n    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_conn_ctx_t));\n    if (ctx == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n\n    ccv.cf = cf;\n    ccv.value = &value[1];\n    ccv.complex_value = &ctx->key;\n\n    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    size = 0;\n    name.len = 0;\n\n    for (i = 2; i < cf->args->nelts; i++) {\n\n        if (ngx_strncmp(value[i].data, \"zone=\", 5) == 0) {\n\n            name.data = value[i].data + 5;\n\n            p = (u_char *) ngx_strchr(name.data, ':');\n\n            if (p == NULL) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"invalid zone size \\\"%V\\\"\", &value[i]);\n                return NGX_CONF_ERROR;\n            }\n\n            name.len = p - name.data;\n\n            s.data = p + 1;\n            s.len = value[i].data + value[i].len - s.data;\n\n            size = ngx_parse_size(&s);\n\n            if (size == NGX_ERROR) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"invalid zone size \\\"%V\\\"\", &value[i]);\n                return NGX_CONF_ERROR;\n            }\n\n            if (size < (ssize_t) (8 * ngx_pagesize)) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"zone \\\"%V\\\" is too small\", &value[i]);\n                return NGX_CONF_ERROR;\n            }\n\n            continue;\n        }\n\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid parameter \\\"%V\\\"\", &value[i]);\n        return NGX_CONF_ERROR;\n    }\n\n    if (name.len == 0) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"\\\"%V\\\" must have \\\"zone\\\" parameter\",\n                           &cmd->name);\n        return NGX_CONF_ERROR;\n    }\n\n    shm_zone = ngx_shared_memory_add(cf, &name, size,\n                                     &ngx_http_limit_conn_module);\n    if (shm_zone == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (shm_zone->data) {\n        ctx = shm_zone->data;\n\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"%V \\\"%V\\\" is already bound to key \\\"%V\\\"\",\n                           &cmd->name, &name, &ctx->key.value);\n        return NGX_CONF_ERROR;\n    }\n\n    shm_zone->init = ngx_http_limit_conn_init_zone;\n    shm_zone->data = ctx;\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_limit_conn(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_shm_zone_t               *shm_zone;\n    ngx_http_limit_conn_conf_t   *lccf = conf;\n    ngx_http_limit_conn_limit_t  *limit, *limits;\n\n    ngx_str_t  *value;\n    ngx_int_t   n;\n    ngx_uint_t  i;\n\n    value = cf->args->elts;\n\n    shm_zone = ngx_shared_memory_add(cf, &value[1], 0,\n                                     &ngx_http_limit_conn_module);\n    if (shm_zone == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    limits = lccf->limits.elts;\n\n    if (limits == NULL) {\n        if (ngx_array_init(&lccf->limits, cf->pool, 1,\n                           sizeof(ngx_http_limit_conn_limit_t))\n            != NGX_OK)\n        {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    for (i = 0; i < lccf->limits.nelts; i++) {\n        if (shm_zone == limits[i].shm_zone) {\n            return \"is duplicate\";\n        }\n    }\n\n    n = ngx_atoi(value[2].data, value[2].len);\n    if (n <= 0) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid number of connections \\\"%V\\\"\", &value[2]);\n        return NGX_CONF_ERROR;\n    }\n\n    if (n > 65535) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"connection limit must be less 65536\");\n        return NGX_CONF_ERROR;\n    }\n\n    limit = ngx_array_push(&lccf->limits);\n    if (limit == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    limit->conn = n;\n    limit->shm_zone = shm_zone;\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_limit_conn_add_variables(ngx_conf_t *cf)\n{\n    ngx_http_variable_t  *var, *v;\n\n    for (v = ngx_http_limit_conn_vars; v->name.len; v++) {\n        var = ngx_http_add_variable(cf, &v->name, v->flags);\n        if (var == NULL) {\n            return NGX_ERROR;\n        }\n\n        var->get_handler = v->get_handler;\n        var->data = v->data;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_limit_conn_init(ngx_conf_t *cf)\n{\n    ngx_http_handler_pt        *h;\n    ngx_http_core_main_conf_t  *cmcf;\n\n    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);\n\n    h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers);\n    if (h == NULL) {\n        return NGX_ERROR;\n    }\n\n    *h = ngx_http_limit_conn_handler;\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "src/http/modules/ngx_http_limit_req_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\n#define NGX_HTTP_LIMIT_REQ_PASSED            1\n#define NGX_HTTP_LIMIT_REQ_DELAYED           2\n#define NGX_HTTP_LIMIT_REQ_REJECTED          3\n#define NGX_HTTP_LIMIT_REQ_DELAYED_DRY_RUN   4\n#define NGX_HTTP_LIMIT_REQ_REJECTED_DRY_RUN  5\n\n#if (T_LIMIT_REQ)\ntypedef struct {\n    ngx_int_t                    index;\n    ngx_str_t                    var;\n} ngx_http_limit_req_variable_t;\n#endif\n\ntypedef struct {\n    u_char                       color;\n    u_char                       dummy;\n    u_short                      len;\n    ngx_queue_t                  queue;\n    ngx_msec_t                   last;\n    /* integer value, 1 corresponds to 0.001 r/s */\n    ngx_uint_t                   excess;\n    ngx_uint_t                   count;\n    u_char                       data[1];\n} ngx_http_limit_req_node_t;\n\n\ntypedef struct {\n    ngx_rbtree_t                  rbtree;\n    ngx_rbtree_node_t             sentinel;\n    ngx_queue_t                   queue;\n} ngx_http_limit_req_shctx_t;\n\n\ntypedef struct {\n    ngx_http_limit_req_shctx_t  *sh;\n    ngx_slab_pool_t             *shpool;\n    /* integer value, 1 corresponds to 0.001 r/s */\n    ngx_uint_t                   rate;\n    ngx_http_complex_value_t     key;\n    ngx_http_limit_req_node_t   *node;\n#if (T_LIMIT_REQ_RATE_VAR)\n    ngx_http_limit_req_variable_t rate_var;\n#endif\n} ngx_http_limit_req_ctx_t;\n\n\ntypedef struct {\n    ngx_shm_zone_t              *shm_zone;\n    /* integer value, 1 corresponds to 0.001 r/s */\n    ngx_uint_t                   burst;\n    ngx_uint_t                   delay;\n#if (T_LIMIT_REQ)\n    ngx_str_t                    forbid_action;\n#endif\n} ngx_http_limit_req_limit_t;\n\n\ntypedef struct {\n#if (T_LIMIT_REQ)\n    ngx_flag_t                   enable;\n\n    ngx_str_t                    geo_var_name;\n    ngx_int_t                    geo_var_index;\n    ngx_str_t                    geo_var_value;\n#endif\n    ngx_array_t                  limits;\n    ngx_uint_t                   limit_log_level;\n    ngx_uint_t                   delay_log_level;\n    ngx_uint_t                   status_code;\n    ngx_flag_t                   dry_run;\n} ngx_http_limit_req_conf_t;\n\n\nstatic void ngx_http_limit_req_delay(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_limit_req_lookup(ngx_http_limit_req_limit_t *limit,\n    ngx_uint_t hash, ngx_str_t *key, ngx_uint_t *ep, ngx_uint_t account);\nstatic ngx_msec_t ngx_http_limit_req_account(ngx_http_limit_req_limit_t *limits,\n    ngx_uint_t n, ngx_uint_t *ep, ngx_http_limit_req_limit_t **limit);\nstatic void ngx_http_limit_req_unlock(ngx_http_limit_req_limit_t *limits,\n    ngx_uint_t n);\nstatic void ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx,\n    ngx_uint_t n);\n\nstatic ngx_int_t ngx_http_limit_req_status_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic void *ngx_http_limit_req_create_conf(ngx_conf_t *cf);\nstatic char *ngx_http_limit_req_merge_conf(ngx_conf_t *cf, void *parent,\n    void *child);\nstatic char *ngx_http_limit_req_zone(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_http_limit_req(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic ngx_int_t ngx_http_limit_req_add_variables(ngx_conf_t *cf);\n#if (T_LIMIT_REQ)\nstatic char *ngx_http_limit_req_whitelist(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n#endif\nstatic ngx_int_t ngx_http_limit_req_init(ngx_conf_t *cf);\n\n\nstatic ngx_conf_enum_t  ngx_http_limit_req_log_levels[] = {\n    { ngx_string(\"info\"), NGX_LOG_INFO },\n    { ngx_string(\"notice\"), NGX_LOG_NOTICE },\n    { ngx_string(\"warn\"), NGX_LOG_WARN },\n    { ngx_string(\"error\"), NGX_LOG_ERR },\n    { ngx_null_string, 0 }\n};\n\n\nstatic ngx_conf_num_bounds_t  ngx_http_limit_req_status_bounds = {\n    ngx_conf_check_num_bounds, 400, 599\n};\n\n\nstatic ngx_command_t  ngx_http_limit_req_commands[] = {\n\n    { ngx_string(\"limit_req_zone\"),\n#if (T_LIMIT_REQ)\n      NGX_HTTP_MAIN_CONF|NGX_CONF_2MORE,\n#else\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE3,\n#endif\n      ngx_http_limit_req_zone,\n      0,\n      0,\n      NULL },\n\n    { ngx_string(\"limit_req\"),\n#if (T_LIMIT_REQ)\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1234,\n#else\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,\n#endif\n      ngx_http_limit_req,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"limit_req_log_level\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_enum_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_limit_req_conf_t, limit_log_level),\n      &ngx_http_limit_req_log_levels },\n\n    { ngx_string(\"limit_req_status\"),\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_limit_req_conf_t, status_code),\n      &ngx_http_limit_req_status_bounds },\n\n    { ngx_string(\"limit_req_dry_run\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_limit_req_conf_t, dry_run),\n      NULL },\n\n#if (T_LIMIT_REQ)\n    { ngx_string(\"limit_req_whitelist\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,\n      ngx_http_limit_req_whitelist,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n#endif\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_limit_req_module_ctx = {\n    ngx_http_limit_req_add_variables,      /* preconfiguration */\n    ngx_http_limit_req_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    ngx_http_limit_req_create_conf,        /* create location configuration */\n    ngx_http_limit_req_merge_conf          /* merge location configuration */\n};\n\n\nngx_module_t  ngx_http_limit_req_module = {\n    NGX_MODULE_V1,\n    &ngx_http_limit_req_module_ctx,        /* module context */\n    ngx_http_limit_req_commands,           /* 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_http_variable_t  ngx_http_limit_req_vars[] = {\n\n    { ngx_string(\"limit_req_status\"), NULL,\n      ngx_http_limit_req_status_variable, 0, NGX_HTTP_VAR_NOCACHEABLE, 0 },\n\n      ngx_http_null_variable\n};\n\n\nstatic ngx_str_t  ngx_http_limit_req_status[] = {\n    ngx_string(\"PASSED\"),\n    ngx_string(\"DELAYED\"),\n    ngx_string(\"REJECTED\"),\n    ngx_string(\"DELAYED_DRY_RUN\"),\n    ngx_string(\"REJECTED_DRY_RUN\")\n};\n\n\n#if (T_LIMIT_REQ)\nstatic inline ngx_int_t\nngx_http_limit_req_ip_filter(ngx_http_request_t *r,\n    ngx_http_limit_req_conf_t *lrcf)\n{\n    ngx_http_variable_value_t    *vv;\n\n    if (lrcf->geo_var_index != NGX_CONF_UNSET) {\n        vv = ngx_http_get_indexed_variable(r, lrcf->geo_var_index);\n\n        if (vv == NULL || vv->not_found) {\n            return NGX_DECLINED;\n        }\n\n        if ((vv->len == lrcf->geo_var_value.len)\n             && (ngx_memcmp(vv->data, lrcf->geo_var_value.data, vv->len) == 0))\n        {\n            return NGX_OK;\n        }\n    }\n\n    return NGX_DECLINED;\n}\n#endif\n\n\n#if (T_LIMIT_REQ_RATE_VAR)\nstatic ngx_int_t\nngx_http_limit_req_rate_value(ngx_http_request_t *r,\n    ngx_http_limit_req_ctx_t *ctx)\n{\n    u_char                        *p;\n    size_t                         len;\n    ngx_uint_t                     rate, scale;\n    ngx_http_variable_value_t     *vv;\n\n    rate = 1;\n    scale = 1;\n    p = NULL;\n\n    if (ctx->rate_var.var.len != 0) {\n        vv = ngx_http_get_indexed_variable(r, ctx->rate_var.index);\n        if (vv == NULL || vv->not_found || vv->len <= 3) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"the value of the \\\"%V\\\" variable \"\n                          \"for limit rate is wrong\",\n                          &ctx->rate_var.var);\n            return NGX_ERROR;\n        }\n        if (vv->len > 65535) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"the value of the \\\"%V\\\" variable \"\n                          \"is more than 65535 bytes: \\\"%V\\\"\",\n                          &ctx->rate_var.var, vv);\n            return NGX_ERROR;\n        }\n        len = vv->len;\n        p = vv->data + len - 3;\n        if (ngx_strncmp(p, \"r/s\", 3) == 0) {\n            scale = 1;\n        } else if (ngx_strncmp(p, \"r/m\", 3) == 0) {\n            scale = 60;\n        }\n        rate = ngx_atoi(vv->data, vv->len - 3);\n        if (rate <= 0) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"the value of rate is wrong\",\n                          &ctx->rate_var.var);\n            return NGX_ERROR;\n        }\n         ctx->rate = rate * 1000 / scale;\n    }\n     p = NULL;\n\n    return NGX_OK;\n}\n#endif\n\n\nstatic ngx_int_t\nngx_http_limit_req_handler(ngx_http_request_t *r)\n{\n    uint32_t                     hash;\n    ngx_str_t                    key;\n    ngx_int_t                    rc;\n    ngx_uint_t                   n, excess;\n    ngx_msec_t                   delay;\n    ngx_http_limit_req_ctx_t    *ctx;\n    ngx_http_limit_req_conf_t   *lrcf;\n    ngx_http_limit_req_limit_t  *limit, *limits;\n\n    if (r->main->limit_req_status) {\n        return NGX_DECLINED;\n    }\n\n    lrcf = ngx_http_get_module_loc_conf(r, ngx_http_limit_req_module);\n    limits = lrcf->limits.elts;\n\n#if (T_LIMIT_REQ)\n    if (!lrcf->enable) {\n        return NGX_DECLINED;\n    }\n\n    /* filter whitelist */\n    if (ngx_http_limit_req_ip_filter(r, lrcf) == NGX_OK) {\n        return NGX_DECLINED;\n    }\n#endif\n\n    excess = 0;\n\n    rc = NGX_DECLINED;\n\n#if (NGX_SUPPRESS_WARN)\n    limit = NULL;\n#endif\n\n    for (n = 0; n < lrcf->limits.nelts; n++) {\n\n        limit = &limits[n];\n\n        ctx = limit->shm_zone->data;\n\n#if (T_LIMIT_REQ_RATE_VAR)\n        if (ngx_http_limit_req_rate_value(r, ctx) != NGX_OK) {\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n#endif\n\n        if (ngx_http_complex_value(r, &ctx->key, &key) != NGX_OK) {\n            ngx_http_limit_req_unlock(limits, n);\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n\n        if (key.len == 0) {\n            continue;\n        }\n\n        if (key.len > 65535) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"the value of the \\\"%V\\\" key \"\n                          \"is more than 65535 bytes: \\\"%V\\\"\",\n                          &ctx->key.value, &key);\n            continue;\n        }\n\n        hash = ngx_crc32_short(key.data, key.len);\n\n        ngx_shmtx_lock(&ctx->shpool->mutex);\n\n        rc = ngx_http_limit_req_lookup(limit, hash, &key, &excess,\n                                       (n == lrcf->limits.nelts - 1));\n\n        ngx_shmtx_unlock(&ctx->shpool->mutex);\n\n        ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"limit_req[%ui]: %i %ui.%03ui\",\n                       n, rc, excess / 1000, excess % 1000);\n\n        if (rc != NGX_AGAIN) {\n            break;\n        }\n    }\n\n    if (rc == NGX_DECLINED) {\n        return NGX_DECLINED;\n    }\n\n    if (rc == NGX_BUSY || rc == NGX_ERROR) {\n\n        if (rc == NGX_BUSY) {\n            ngx_log_error(lrcf->limit_log_level, r->connection->log, 0,\n                        \"limiting requests%s, excess: %ui.%03ui by zone \\\"%V\\\"\",\n                        lrcf->dry_run ? \", dry run\" : \"\",\n                        excess / 1000, excess % 1000,\n                        &limit->shm_zone->shm.name);\n        }\n\n        ngx_http_limit_req_unlock(limits, n);\n\n        if (lrcf->dry_run) {\n            r->main->limit_req_status = NGX_HTTP_LIMIT_REQ_REJECTED_DRY_RUN;\n            return NGX_DECLINED;\n        }\n\n#if (T_LIMIT_REQ)\n        if (rc == NGX_ERROR || limit->forbid_action.len == 0) {\n            r->main->limit_req_status = NGX_HTTP_LIMIT_REQ_REJECTED;\n\n            return lrcf->status_code;\n\n        } else if (limit->forbid_action.data[0] == '@') {\n\n            ngx_log_error(lrcf->limit_log_level, r->connection->log, 0,\n                          \"limiting requests, forbid_action is %V\",\n                          &limit->forbid_action);\n            (void) ngx_http_named_location(r, &limit->forbid_action);\n\n        } else {\n\n            ngx_log_error(lrcf->limit_log_level, r->connection->log, 0,\n                          \"limiting requests, forbid_action is %V\",\n                          &limit->forbid_action);\n            (void) ngx_http_internal_redirect(r,\n                                              &limit->forbid_action,\n                                              &r->args);\n        }\n\n        ngx_http_finalize_request(r, NGX_DONE);\n        return NGX_DONE;\n#else\n        r->main->limit_req_status = NGX_HTTP_LIMIT_REQ_REJECTED;\n\n        return lrcf->status_code;\n#endif\n    }\n\n    /* rc == NGX_AGAIN || rc == NGX_OK */\n\n    if (rc == NGX_AGAIN) {\n        excess = 0;\n    }\n\n    delay = ngx_http_limit_req_account(limits, n, &excess, &limit);\n\n    if (!delay) {\n        r->main->limit_req_status = NGX_HTTP_LIMIT_REQ_PASSED;\n        return NGX_DECLINED;\n    }\n\n    ngx_log_error(lrcf->delay_log_level, r->connection->log, 0,\n                  \"delaying request%s, excess: %ui.%03ui, by zone \\\"%V\\\"\",\n                  lrcf->dry_run ? \", dry run\" : \"\",\n                  excess / 1000, excess % 1000, &limit->shm_zone->shm.name);\n\n    if (lrcf->dry_run) {\n        r->main->limit_req_status = NGX_HTTP_LIMIT_REQ_DELAYED_DRY_RUN;\n        return NGX_DECLINED;\n    }\n\n    r->main->limit_req_status = NGX_HTTP_LIMIT_REQ_DELAYED;\n\n    if (r->connection->read->ready) {\n        ngx_post_event(r->connection->read, &ngx_posted_events);\n\n    } else {\n        if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) {\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n    }\n\n    r->read_event_handler = ngx_http_test_reading;\n    r->write_event_handler = ngx_http_limit_req_delay;\n\n    r->connection->write->delayed = 1;\n    ngx_add_timer(r->connection->write, delay);\n\n    return NGX_AGAIN;\n}\n\n\nstatic void\nngx_http_limit_req_delay(ngx_http_request_t *r)\n{\n    ngx_event_t  *wev;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"limit_req delay\");\n\n    wev = r->connection->write;\n\n    if (wev->delayed) {\n\n        if (ngx_handle_write_event(wev, 0) != NGX_OK) {\n            ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n        }\n\n        return;\n    }\n\n    if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) {\n        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return;\n    }\n\n    r->read_event_handler = ngx_http_block_reading;\n    r->write_event_handler = ngx_http_core_run_phases;\n\n    ngx_http_core_run_phases(r);\n}\n\n\nstatic void\nngx_http_limit_req_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_limit_req_node_t   *lrn, *lrnt;\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            lrn = (ngx_http_limit_req_node_t *) &node->color;\n            lrnt = (ngx_http_limit_req_node_t *) &temp->color;\n\n            p = (ngx_memn2cmp(lrn->data, lrnt->data, lrn->len, lrnt->len) < 0)\n                ? &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_limit_req_lookup(ngx_http_limit_req_limit_t *limit, ngx_uint_t hash,\n    ngx_str_t *key, ngx_uint_t *ep, ngx_uint_t account)\n{\n    size_t                      size;\n    ngx_int_t                   rc, excess;\n    ngx_msec_t                  now;\n    ngx_msec_int_t              ms;\n    ngx_rbtree_node_t          *node, *sentinel;\n    ngx_http_limit_req_ctx_t   *ctx;\n    ngx_http_limit_req_node_t  *lr;\n\n    now = ngx_current_msec;\n\n    ctx = limit->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        lr = (ngx_http_limit_req_node_t *) &node->color;\n\n        rc = ngx_memn2cmp(key->data, lr->data, key->len, (size_t) lr->len);\n\n        if (rc == 0) {\n            ngx_queue_remove(&lr->queue);\n            ngx_queue_insert_head(&ctx->sh->queue, &lr->queue);\n\n            ms = (ngx_msec_int_t) (now - lr->last);\n\n            if (ms < -60000) {\n                ms = 1;\n\n            } else if (ms < 0) {\n                ms = 0;\n            }\n\n            excess = lr->excess - ctx->rate * ms / 1000 + 1000;\n\n            if (excess < 0) {\n                excess = 0;\n            }\n\n            *ep = excess;\n\n            if ((ngx_uint_t) excess > limit->burst) {\n                return NGX_BUSY;\n            }\n\n            if (account) {\n                lr->excess = excess;\n\n                if (ms) {\n                    lr->last = now;\n                }\n\n                return NGX_OK;\n            }\n\n            lr->count++;\n\n            ctx->node = lr;\n\n            return NGX_AGAIN;\n        }\n\n        node = (rc < 0) ? node->left : node->right;\n    }\n\n    *ep = 0;\n\n    size = offsetof(ngx_rbtree_node_t, color)\n           + offsetof(ngx_http_limit_req_node_t, data)\n           + key->len;\n\n    ngx_http_limit_req_expire(ctx, 1);\n\n    node = ngx_slab_alloc_locked(ctx->shpool, size);\n\n    if (node == NULL) {\n        ngx_http_limit_req_expire(ctx, 0);\n\n        node = ngx_slab_alloc_locked(ctx->shpool, size);\n        if (node == NULL) {\n            ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,\n                          \"could not allocate node%s\", ctx->shpool->log_ctx);\n            return NGX_ERROR;\n        }\n    }\n\n    node->key = hash;\n\n    lr = (ngx_http_limit_req_node_t *) &node->color;\n\n    lr->len = (u_short) key->len;\n    lr->excess = 0;\n\n    ngx_memcpy(lr->data, key->data, key->len);\n\n    ngx_rbtree_insert(&ctx->sh->rbtree, node);\n\n    ngx_queue_insert_head(&ctx->sh->queue, &lr->queue);\n\n    if (account) {\n        lr->last = now;\n        lr->count = 0;\n        return NGX_OK;\n    }\n\n    lr->last = 0;\n    lr->count = 1;\n\n    ctx->node = lr;\n\n    return NGX_AGAIN;\n}\n\n\nstatic ngx_msec_t\nngx_http_limit_req_account(ngx_http_limit_req_limit_t *limits, ngx_uint_t n,\n    ngx_uint_t *ep, ngx_http_limit_req_limit_t **limit)\n{\n    ngx_int_t                   excess;\n    ngx_msec_t                  now, delay, max_delay;\n    ngx_msec_int_t              ms;\n    ngx_http_limit_req_ctx_t   *ctx;\n    ngx_http_limit_req_node_t  *lr;\n\n    excess = *ep;\n\n    if ((ngx_uint_t) excess <= (*limit)->delay) {\n        max_delay = 0;\n\n    } else {\n        ctx = (*limit)->shm_zone->data;\n        max_delay = (excess - (*limit)->delay) * 1000 / ctx->rate;\n    }\n\n    while (n--) {\n        ctx = limits[n].shm_zone->data;\n        lr = ctx->node;\n\n        if (lr == NULL) {\n            continue;\n        }\n\n        ngx_shmtx_lock(&ctx->shpool->mutex);\n\n        now = ngx_current_msec;\n        ms = (ngx_msec_int_t) (now - lr->last);\n\n        if (ms < -60000) {\n            ms = 1;\n\n        } else if (ms < 0) {\n            ms = 0;\n        }\n\n        excess = lr->excess - ctx->rate * ms / 1000 + 1000;\n\n        if (excess < 0) {\n            excess = 0;\n        }\n\n        if (ms) {\n            lr->last = now;\n        }\n\n        lr->excess = excess;\n        lr->count--;\n\n        ngx_shmtx_unlock(&ctx->shpool->mutex);\n\n        ctx->node = NULL;\n\n        if ((ngx_uint_t) excess <= limits[n].delay) {\n            continue;\n        }\n\n        delay = (excess - limits[n].delay) * 1000 / ctx->rate;\n\n        if (delay > max_delay) {\n            max_delay = delay;\n            *ep = excess;\n            *limit = &limits[n];\n        }\n    }\n\n    return max_delay;\n}\n\n\nstatic void\nngx_http_limit_req_unlock(ngx_http_limit_req_limit_t *limits, ngx_uint_t n)\n{\n    ngx_http_limit_req_ctx_t  *ctx;\n\n    while (n--) {\n        ctx = limits[n].shm_zone->data;\n\n        if (ctx->node == NULL) {\n            continue;\n        }\n\n        ngx_shmtx_lock(&ctx->shpool->mutex);\n\n        ctx->node->count--;\n\n        ngx_shmtx_unlock(&ctx->shpool->mutex);\n\n        ctx->node = NULL;\n    }\n}\n\n\nstatic void\nngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx, ngx_uint_t n)\n{\n    ngx_int_t                   excess;\n    ngx_msec_t                  now;\n    ngx_queue_t                *q;\n    ngx_msec_int_t              ms;\n    ngx_rbtree_node_t          *node;\n    ngx_http_limit_req_node_t  *lr;\n\n    now = ngx_current_msec;\n\n    /*\n     * n == 1 deletes one or two zero rate 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->queue)) {\n            return;\n        }\n\n        q = ngx_queue_last(&ctx->sh->queue);\n\n        lr = ngx_queue_data(q, ngx_http_limit_req_node_t, queue);\n\n        if (lr->count) {\n\n            /*\n             * There is not much sense in looking further,\n             * because we bump nodes on the lookup stage.\n             */\n\n            return;\n        }\n\n        if (n++ != 0) {\n\n            ms = (ngx_msec_int_t) (now - lr->last);\n            ms = ngx_abs(ms);\n\n            if (ms < 60000) {\n                return;\n            }\n\n            excess = lr->excess - ctx->rate * ms / 1000;\n\n            if (excess > 0) {\n                return;\n            }\n        }\n\n        ngx_queue_remove(q);\n\n        node = (ngx_rbtree_node_t *)\n                   ((u_char *) lr - 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\n\nstatic ngx_int_t\nngx_http_limit_req_init_zone(ngx_shm_zone_t *shm_zone, void *data)\n{\n    ngx_http_limit_req_ctx_t  *octx = data;\n\n    size_t                     len;\n    ngx_http_limit_req_ctx_t  *ctx;\n\n    ctx = shm_zone->data;\n\n    if (octx) {\n        if (ctx->key.value.len != octx->key.value.len\n            || ngx_strncmp(ctx->key.value.data, octx->key.value.data,\n                           ctx->key.value.len)\n               != 0)\n        {\n            ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0,\n                          \"limit_req \\\"%V\\\" uses the \\\"%V\\\" key \"\n                          \"while previously it used the \\\"%V\\\" key\",\n                          &shm_zone->shm.name, &ctx->key.value,\n                          &octx->key.value);\n            return NGX_ERROR;\n        }\n\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_limit_req_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_limit_req_rbtree_insert_value);\n\n    ngx_queue_init(&ctx->sh->queue);\n\n    len = sizeof(\" in limit_req 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 limit_req zone \\\"%V\\\"%Z\",\n                &shm_zone->shm.name);\n\n    ctx->shpool->log_nomem = 0;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_limit_req_status_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    if (r->main->limit_req_status == 0) {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->len = ngx_http_limit_req_status[r->main->limit_req_status - 1].len;\n    v->data = ngx_http_limit_req_status[r->main->limit_req_status - 1].data;\n\n    return NGX_OK;\n}\n\n\nstatic void *\nngx_http_limit_req_create_conf(ngx_conf_t *cf)\n{\n    ngx_http_limit_req_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_req_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc():\n     *\n     *     conf->limits.elts = NULL;\n     */\n\n    conf->limit_log_level = NGX_CONF_UNSET_UINT;\n    conf->status_code = NGX_CONF_UNSET_UINT;\n    conf->dry_run = NGX_CONF_UNSET;\n\n#if (T_LIMIT_REQ)\n    conf->enable = NGX_CONF_UNSET;\n    conf->geo_var_index = NGX_CONF_UNSET;\n#endif\n\n    return conf;\n}\n\n\nstatic char *\nngx_http_limit_req_merge_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_http_limit_req_conf_t *prev = parent;\n    ngx_http_limit_req_conf_t *conf = child;\n\n    if (conf->limits.elts == NULL) {\n        conf->limits = prev->limits;\n    }\n\n    ngx_conf_merge_uint_value(conf->limit_log_level, prev->limit_log_level,\n                              NGX_LOG_ERR);\n\n    conf->delay_log_level = (conf->limit_log_level == NGX_LOG_INFO) ?\n                                NGX_LOG_INFO : conf->limit_log_level + 1;\n\n    ngx_conf_merge_uint_value(conf->status_code, prev->status_code,\n                              NGX_HTTP_SERVICE_UNAVAILABLE);\n\n    ngx_conf_merge_value(conf->dry_run, prev->dry_run, 0);\n#if (T_LIMIT_REQ)\n    ngx_conf_merge_value(conf->enable, prev->enable, 0);\n\n    ngx_conf_merge_value(conf->geo_var_index, prev->geo_var_index,\n                         NGX_CONF_UNSET);\n\n    ngx_conf_merge_str_value(conf->geo_var_value, prev->geo_var_value, \"\");\n#endif\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_limit_req_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    u_char                            *p;\n    size_t                             len;\n    ssize_t                            size;\n    ngx_str_t                         *value, name, s;\n    ngx_int_t                          rate, scale;\n    ngx_uint_t                         i;\n    ngx_shm_zone_t                    *shm_zone;\n    ngx_http_limit_req_ctx_t          *ctx;\n    ngx_http_compile_complex_value_t   ccv;\n#if (T_LIMIT_REQ_RATE_VAR)\n    ngx_http_limit_req_variable_t      rate_var;\n#endif\n\n    value = cf->args->elts;\n\n    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_limit_req_ctx_t));\n    if (ctx == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n#if (T_LIMIT_REQ)\n    {\n    u_char      *p;\n    size_t      len;\n    ngx_str_t   v1;\n\n    len = 0;\n    for (i = 1; i < cf->args->nelts; i++) {\n        if (i == 1 || value[i].data[0] == '$') {\n            len += value[i].len;\n\n        } else {\n            break;\n\t}\n    }\n\n    p = ngx_palloc(cf->pool, len);\n    if (p == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    v1.len = len;\n    v1.data = p;\n\n    for (i = 1; i < cf->args->nelts; i++) {\n        if (i == 1 || value[i].data[0] == '$') {\n            p = ngx_cpymem(p, value[i].data, value[i].len);\n\n        } else {\n            break;\n\t}\n\n    }\n\n    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n\n    ccv.cf = cf;\n    ccv.value = &v1;\n    ccv.complex_value = &ctx->key;\n\n    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n    }\n#else\n\n    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n\n    ccv.cf = cf;\n    ccv.value = &value[1];\n    ccv.complex_value = &ctx->key;\n\n    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n#endif\n\n    size = 0;\n    rate = 1;\n    scale = 1;\n    name.len = 0;\n\n#if (T_LIMIT_REQ_RATE_VAR)\n    memset(&rate_var, 0x0, sizeof(rate_var));\n#endif\n\n    for (i = 2; i < cf->args->nelts; i++) {\n\n        if (ngx_strncmp(value[i].data, \"zone=\", 5) == 0) {\n\n            name.data = value[i].data + 5;\n\n            p = (u_char *) ngx_strchr(name.data, ':');\n\n            if (p == NULL) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"invalid zone size \\\"%V\\\"\", &value[i]);\n                return NGX_CONF_ERROR;\n            }\n\n            name.len = p - name.data;\n\n            s.data = p + 1;\n            s.len = value[i].data + value[i].len - s.data;\n\n            size = ngx_parse_size(&s);\n\n            if (size == NGX_ERROR) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"invalid zone size \\\"%V\\\"\", &value[i]);\n                return NGX_CONF_ERROR;\n            }\n\n            if (size < (ssize_t) (8 * ngx_pagesize)) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"zone \\\"%V\\\" is too small\", &value[i]);\n                return NGX_CONF_ERROR;\n            }\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"rate=\", 5) == 0) {\n#if (T_LIMIT_REQ_RATE_VAR)\n            if (value[i].data[5] == '$') {\n\n                value[i].data += 6;\n                value[i].len -= 6;\n                rate_var.index = ngx_http_get_variable_index(cf, &value[i]);\n\n                if (rate_var.index == NGX_ERROR) {\n                    return NGX_CONF_ERROR;\n                }\n\n                rate_var.var = value[i];\n\n                continue;\n            }\n#endif\n\n            len = value[i].len;\n            p = value[i].data + len - 3;\n\n            if (ngx_strncmp(p, \"r/s\", 3) == 0) {\n                scale = 1;\n                len -= 3;\n\n            } else if (ngx_strncmp(p, \"r/m\", 3) == 0) {\n                scale = 60;\n                len -= 3;\n            }\n\n            rate = ngx_atoi(value[i].data + 5, len - 5);\n            if (rate <= 0) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"invalid rate \\\"%V\\\"\", &value[i]);\n                return NGX_CONF_ERROR;\n            }\n\n            continue;\n        }\n\n#if (T_LIMIT_REQ)\n        if (value[i].data[0] == '$') {\n            continue;\n        }\n#endif\n\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid parameter \\\"%V\\\"\", &value[i]);\n        return NGX_CONF_ERROR;\n    }\n\n    if (name.len == 0) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"\\\"%V\\\" must have \\\"zone\\\" parameter\",\n                           &cmd->name);\n        return NGX_CONF_ERROR;\n    }\n\n    ctx->rate = rate * 1000 / scale;\n\n#if (T_LIMIT_REQ_RATE_VAR)\n    ctx->rate_var = rate_var;\n#endif\n\n    shm_zone = ngx_shared_memory_add(cf, &name, size,\n                                     &ngx_http_limit_req_module);\n    if (shm_zone == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (shm_zone->data) {\n        ctx = shm_zone->data;\n\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"%V \\\"%V\\\" is already bound to key \\\"%V\\\"\",\n                           &cmd->name, &name, &ctx->key.value);\n        return NGX_CONF_ERROR;\n    }\n\n    shm_zone->init = ngx_http_limit_req_init_zone;\n    shm_zone->data = ctx;\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_limit_req(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_limit_req_conf_t  *lrcf = conf;\n\n    ngx_int_t                    burst, delay;\n    ngx_str_t                   *value, s;\n    ngx_uint_t                   i;\n    ngx_shm_zone_t              *shm_zone;\n    ngx_http_limit_req_limit_t  *limit, *limits;\n#if (T_LIMIT_REQ)\n    ngx_str_t                    forbid_action;\n#endif\n\n    value = cf->args->elts;\n\n#if (T_LIMIT_REQ)\n    if (cf->args->nelts == 2) {\n        if (ngx_strncmp(value[1].data, \"off\", 3) == 0) {\n            lrcf->enable = 0;\n            return NGX_CONF_OK;\n        }\n    }\n\n    lrcf->enable = 1;\n    ngx_str_null(&forbid_action);\n#endif\n\n    shm_zone = NULL;\n    burst = 0;\n    delay = 0;\n\n    for (i = 1; i < cf->args->nelts; i++) {\n\n        if (ngx_strncmp(value[i].data, \"zone=\", 5) == 0) {\n\n            s.len = value[i].len - 5;\n            s.data = value[i].data + 5;\n\n            shm_zone = ngx_shared_memory_add(cf, &s, 0,\n                                             &ngx_http_limit_req_module);\n            if (shm_zone == NULL) {\n                return NGX_CONF_ERROR;\n            }\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"burst=\", 6) == 0) {\n\n            burst = ngx_atoi(value[i].data + 6, value[i].len - 6);\n            if (burst <= 0) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"invalid burst value \\\"%V\\\"\", &value[i]);\n                return NGX_CONF_ERROR;\n            }\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"delay=\", 6) == 0) {\n\n            delay = ngx_atoi(value[i].data + 6, value[i].len - 6);\n            if (delay <= 0) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"invalid delay value \\\"%V\\\"\", &value[i]);\n                return NGX_CONF_ERROR;\n            }\n\n            continue;\n        }\n\n        if (ngx_strcmp(value[i].data, \"nodelay\") == 0) {\n            delay = NGX_MAX_INT_T_VALUE / 1000;\n            continue;\n        }\n\n#if (T_LIMIT_REQ)\n        if (ngx_strncmp(value[i].data, \"forbid_action=\", 14) == 0) {\n\n            s.len = value[i].len - 14;\n            s.data = value[i].data + 14;\n\n            if (s.len < 2 || (s.data[0] != '@' && s.data[0] != '/')) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"invalid forbid_action \\\"%V\\\"\", &value[i]);\n                return NGX_CONF_ERROR;\n            }\n\n            forbid_action = s;\n\n            continue;\n        }\n#endif\n\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid parameter \\\"%V\\\"\", &value[i]);\n        return NGX_CONF_ERROR;\n    }\n\n    if (shm_zone == NULL) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"\\\"%V\\\" must have \\\"zone\\\" parameter\",\n                           &cmd->name);\n        return NGX_CONF_ERROR;\n    }\n\n    limits = lrcf->limits.elts;\n\n    if (limits == NULL) {\n        if (ngx_array_init(&lrcf->limits, cf->pool, 1,\n                           sizeof(ngx_http_limit_req_limit_t))\n            != NGX_OK)\n        {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    for (i = 0; i < lrcf->limits.nelts; i++) {\n        if (shm_zone == limits[i].shm_zone) {\n            return \"is duplicate\";\n        }\n    }\n\n    limit = ngx_array_push(&lrcf->limits);\n    if (limit == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    limit->shm_zone = shm_zone;\n    limit->burst = burst * 1000;\n    limit->delay = delay * 1000;\n#if (T_LIMIT_REQ)\n    limit->forbid_action = forbid_action;\n#endif\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_limit_req_add_variables(ngx_conf_t *cf)\n{\n    ngx_http_variable_t  *var, *v;\n\n    for (v = ngx_http_limit_req_vars; v->name.len; v++) {\n        var = ngx_http_add_variable(cf, &v->name, v->flags);\n        if (var == NULL) {\n            return NGX_ERROR;\n        }\n\n        var->get_handler = v->get_handler;\n        var->data = v->data;\n    }\n\n    return NGX_OK;\n}\n\n\n#if (T_LIMIT_REQ)\nstatic char *\nngx_http_limit_req_whitelist(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n    ngx_http_limit_req_conf_t  *lrcf = conf;\n\n    ngx_str_t              *value, s;\n    ngx_uint_t              i;\n\n    value = cf->args->elts;\n\n    for (i = 1; i < cf->args->nelts; i++) {\n        if (ngx_strncmp(value[i].data, \"geo_var_name=\", 13) == 0) {\n\n            s.len = value[i].len - 13;\n            s.data = value[i].data + 13;\n\n            lrcf->geo_var_name = s;\n\n            lrcf->geo_var_index = ngx_http_get_variable_index(cf,\n                &lrcf->geo_var_name);\n\n            if (lrcf->geo_var_index == NGX_ERROR) {\n                return NGX_CONF_ERROR;\n            }\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"geo_var_value=\", 14) == 0) {\n\n            s.len = value[i].len - 14;\n            s.data = value[i].data + 14;\n\n            lrcf->geo_var_value = s;\n\n            continue;\n        }\n\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid parameter \\\"%V\\\"\", &value[i]);\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n#endif\n\n\nstatic ngx_int_t\nngx_http_limit_req_init(ngx_conf_t *cf)\n{\n    ngx_http_handler_pt        *h;\n    ngx_http_core_main_conf_t  *cmcf;\n\n    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);\n\n    h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers);\n    if (h == NULL) {\n        return NGX_ERROR;\n    }\n\n    *h = ngx_http_limit_req_handler;\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "src/http/modules/ngx_http_log_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n#if (NGX_ZLIB)\n#include <zlib.h>\n#endif\n\n\ntypedef struct ngx_http_log_op_s  ngx_http_log_op_t;\n\ntypedef u_char *(*ngx_http_log_op_run_pt) (ngx_http_request_t *r, u_char *buf,\n    ngx_http_log_op_t *op);\n\ntypedef size_t (*ngx_http_log_op_getlen_pt) (ngx_http_request_t *r,\n    uintptr_t data);\n\n\nstruct ngx_http_log_op_s {\n    size_t                      len;\n    ngx_http_log_op_getlen_pt   getlen;\n    ngx_http_log_op_run_pt      run;\n    uintptr_t                   data;\n};\n\n\ntypedef struct {\n    ngx_str_t                   name;\n    ngx_array_t                *flushes;\n    ngx_array_t                *ops;        /* array of ngx_http_log_op_t */\n} ngx_http_log_fmt_t;\n\n\ntypedef struct {\n    ngx_array_t                 formats;    /* array of ngx_http_log_fmt_t */\n    ngx_uint_t                  combined_used; /* unsigned  combined_used:1 */\n} ngx_http_log_main_conf_t;\n\n\ntypedef struct {\n    u_char                     *start;\n    u_char                     *pos;\n    u_char                     *last;\n\n    ngx_event_t                *event;\n    ngx_msec_t                  flush;\n    ngx_int_t                   gzip;\n} ngx_http_log_buf_t;\n\n\ntypedef struct {\n    ngx_array_t                *lengths;\n    ngx_array_t                *values;\n} ngx_http_log_script_t;\n\n\ntypedef struct {\n    ngx_open_file_t            *file;\n    ngx_http_log_script_t      *script;\n    time_t                      disk_full_time;\n    time_t                      error_log_time;\n    ngx_syslog_peer_t          *syslog_peer;\n    ngx_http_log_fmt_t         *format;\n    ngx_http_complex_value_t   *filter;\n} ngx_http_log_t;\n\n\ntypedef struct {\n    ngx_array_t                *logs;       /* array of ngx_http_log_t */\n\n    ngx_open_file_cache_t      *open_file_cache;\n    time_t                      open_file_cache_valid;\n    ngx_uint_t                  open_file_cache_min_uses;\n\n    ngx_uint_t                  off;        /* unsigned  off:1 */\n} ngx_http_log_loc_conf_t;\n\n\ntypedef struct {\n    ngx_str_t                   name;\n    size_t                      len;\n    ngx_http_log_op_run_pt      run;\n} ngx_http_log_var_t;\n\n\n#define NGX_HTTP_LOG_ESCAPE_DEFAULT  0\n#define NGX_HTTP_LOG_ESCAPE_JSON     1\n#define NGX_HTTP_LOG_ESCAPE_NONE     2\n\n\nstatic void ngx_http_log_write(ngx_http_request_t *r, ngx_http_log_t *log,\n    u_char *buf, size_t len);\nstatic ssize_t ngx_http_log_script_write(ngx_http_request_t *r,\n    ngx_http_log_script_t *script, u_char **name, u_char *buf, size_t len);\n\n#if (NGX_ZLIB)\nstatic ssize_t ngx_http_log_gzip(ngx_fd_t fd, u_char *buf, size_t len,\n    ngx_int_t level, ngx_log_t *log);\n\nstatic void *ngx_http_log_gzip_alloc(void *opaque, u_int items, u_int size);\nstatic void ngx_http_log_gzip_free(void *opaque, void *address);\n#endif\n\nstatic void ngx_http_log_flush(ngx_open_file_t *file, ngx_log_t *log);\nstatic void ngx_http_log_flush_handler(ngx_event_t *ev);\n\nstatic u_char *ngx_http_log_pipe(ngx_http_request_t *r, u_char *buf,\n    ngx_http_log_op_t *op);\nstatic u_char *ngx_http_log_time(ngx_http_request_t *r, u_char *buf,\n    ngx_http_log_op_t *op);\nstatic u_char *ngx_http_log_iso8601(ngx_http_request_t *r, u_char *buf,\n    ngx_http_log_op_t *op);\nstatic u_char *ngx_http_log_msec(ngx_http_request_t *r, u_char *buf,\n    ngx_http_log_op_t *op);\nstatic u_char *ngx_http_log_request_time(ngx_http_request_t *r, u_char *buf,\n    ngx_http_log_op_t *op);\n#if (T_NGX_VARS)\nstatic u_char *ngx_http_log_request_time_msec(ngx_http_request_t *r,\n    u_char *buf, ngx_http_log_op_t *op);\nstatic u_char *ngx_http_log_request_time_usec(ngx_http_request_t *r,\n    u_char *buf, ngx_http_log_op_t *op);\n#endif\nstatic u_char *ngx_http_log_status(ngx_http_request_t *r, u_char *buf,\n    ngx_http_log_op_t *op);\nstatic u_char *ngx_http_log_bytes_sent(ngx_http_request_t *r, u_char *buf,\n    ngx_http_log_op_t *op);\nstatic u_char *ngx_http_log_body_bytes_sent(ngx_http_request_t *r,\n    u_char *buf, ngx_http_log_op_t *op);\nstatic u_char *ngx_http_log_request_length(ngx_http_request_t *r, u_char *buf,\n    ngx_http_log_op_t *op);\n\nstatic ngx_int_t ngx_http_log_variable_compile(ngx_conf_t *cf,\n    ngx_http_log_op_t *op, ngx_str_t *value, ngx_uint_t escape);\nstatic size_t ngx_http_log_variable_getlen(ngx_http_request_t *r,\n    uintptr_t data);\nstatic u_char *ngx_http_log_variable(ngx_http_request_t *r, u_char *buf,\n    ngx_http_log_op_t *op);\nstatic uintptr_t ngx_http_log_escape(u_char *dst, u_char *src, size_t size);\nstatic size_t ngx_http_log_json_variable_getlen(ngx_http_request_t *r,\n    uintptr_t data);\nstatic u_char *ngx_http_log_json_variable(ngx_http_request_t *r, u_char *buf,\n    ngx_http_log_op_t *op);\nstatic size_t ngx_http_log_unescaped_variable_getlen(ngx_http_request_t *r,\n    uintptr_t data);\nstatic u_char *ngx_http_log_unescaped_variable(ngx_http_request_t *r,\n    u_char *buf, ngx_http_log_op_t *op);\n\n\nstatic void *ngx_http_log_create_main_conf(ngx_conf_t *cf);\nstatic void *ngx_http_log_create_loc_conf(ngx_conf_t *cf);\nstatic char *ngx_http_log_merge_loc_conf(ngx_conf_t *cf, void *parent,\n    void *child);\nstatic char *ngx_http_log_set_log(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_http_log_set_format(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_http_log_compile_format(ngx_conf_t *cf,\n    ngx_array_t *flushes, ngx_array_t *ops, ngx_array_t *args, ngx_uint_t s);\nstatic char *ngx_http_log_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic ngx_int_t ngx_http_log_init(ngx_conf_t *cf);\n\n\nstatic ngx_command_t  ngx_http_log_commands[] = {\n\n    { ngx_string(\"log_format\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_2MORE,\n      ngx_http_log_set_format,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"access_log\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF\n                        |NGX_HTTP_LMT_CONF|NGX_CONF_1MORE,\n      ngx_http_log_set_log,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"open_log_file_cache\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1234,\n      ngx_http_log_open_file_cache,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_log_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    ngx_http_log_init,                     /* postconfiguration */\n\n    ngx_http_log_create_main_conf,         /* create main configuration */\n    NULL,                                  /* init main configuration */\n\n    NULL,                                  /* create server configuration */\n    NULL,                                  /* merge server configuration */\n\n    ngx_http_log_create_loc_conf,          /* create location configuration */\n    ngx_http_log_merge_loc_conf            /* merge location configuration */\n};\n\n\nngx_module_t  ngx_http_log_module = {\n    NGX_MODULE_V1,\n    &ngx_http_log_module_ctx,              /* module context */\n    ngx_http_log_commands,                 /* 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_str_t  ngx_http_access_log = ngx_string(NGX_HTTP_LOG_PATH);\n\n\nstatic ngx_str_t  ngx_http_combined_fmt =\n    ngx_string(\"$remote_addr - $remote_user [$time_local] \"\n               \"\\\"$request\\\" $status $body_bytes_sent \"\n               \"\\\"$http_referer\\\" \\\"$http_user_agent\\\"\");\n\n\nstatic ngx_http_log_var_t  ngx_http_log_vars[] = {\n    { ngx_string(\"pipe\"), 1, ngx_http_log_pipe },\n    { ngx_string(\"time_local\"), sizeof(\"28/Sep/1970:12:00:00 +0600\") - 1,\n                          ngx_http_log_time },\n    { ngx_string(\"time_iso8601\"), sizeof(\"1970-09-28T12:00:00+06:00\") - 1,\n                          ngx_http_log_iso8601 },\n    { ngx_string(\"msec\"), NGX_TIME_T_LEN + 4, ngx_http_log_msec },\n    { ngx_string(\"request_time\"), NGX_TIME_T_LEN + 4,\n                          ngx_http_log_request_time },\n#if (T_NGX_VARS)\n    { ngx_string(\"request_time_msec\"), NGX_TIME_T_LEN,\n                          ngx_http_log_request_time_msec },\n    { ngx_string(\"request_time_usec\"), NGX_TIME_T_LEN,\n                          ngx_http_log_request_time_usec },\n#endif\n    { ngx_string(\"status\"), NGX_INT_T_LEN, ngx_http_log_status },\n    { ngx_string(\"bytes_sent\"), NGX_OFF_T_LEN, ngx_http_log_bytes_sent },\n    { ngx_string(\"body_bytes_sent\"), NGX_OFF_T_LEN,\n                          ngx_http_log_body_bytes_sent },\n    { ngx_string(\"request_length\"), NGX_SIZE_T_LEN,\n                          ngx_http_log_request_length },\n\n    { ngx_null_string, 0, NULL }\n};\n\n\nstatic ngx_int_t\nngx_http_log_handler(ngx_http_request_t *r)\n{\n    u_char                   *line, *p;\n    size_t                    len, size;\n    ssize_t                   n;\n    ngx_str_t                 val;\n    ngx_uint_t                i, l;\n    ngx_http_log_t           *log;\n    ngx_http_log_op_t        *op;\n    ngx_http_log_buf_t       *buffer;\n    ngx_http_log_loc_conf_t  *lcf;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http log handler\");\n\n    lcf = ngx_http_get_module_loc_conf(r, ngx_http_log_module);\n\n    if (lcf->off) {\n        return NGX_OK;\n    }\n\n    log = lcf->logs->elts;\n    for (l = 0; l < lcf->logs->nelts; l++) {\n\n        if (log[l].filter) {\n            if (ngx_http_complex_value(r, log[l].filter, &val) != NGX_OK) {\n                return NGX_ERROR;\n            }\n\n            if (val.len == 0 || (val.len == 1 && val.data[0] == '0')) {\n                continue;\n            }\n        }\n\n        if (ngx_time() == log[l].disk_full_time) {\n\n            /*\n             * on FreeBSD writing to a full filesystem with enabled softupdates\n             * may block process for much longer time than writing to non-full\n             * filesystem, so we skip writing to a log for one second\n             */\n\n            continue;\n        }\n\n        ngx_http_script_flush_no_cacheable_variables(r, log[l].format->flushes);\n\n        len = 0;\n        op = log[l].format->ops->elts;\n        for (i = 0; i < log[l].format->ops->nelts; i++) {\n            if (op[i].len == 0) {\n                len += op[i].getlen(r, op[i].data);\n\n            } else {\n                len += op[i].len;\n            }\n        }\n\n        if (log[l].syslog_peer) {\n\n            /* length of syslog's PRI and HEADER message parts */\n            len += sizeof(\"<255>Jan 01 00:00:00 \") - 1\n                   + ngx_cycle->hostname.len + 1\n                   + log[l].syslog_peer->tag.len + 2;\n\n            goto alloc_line;\n        }\n\n        len += NGX_LINEFEED_SIZE;\n\n        buffer = log[l].file ? log[l].file->data : NULL;\n\n        if (buffer) {\n\n            if (len > (size_t) (buffer->last - buffer->pos)) {\n\n                ngx_http_log_write(r, &log[l], buffer->start,\n                                   buffer->pos - buffer->start);\n\n                buffer->pos = buffer->start;\n            }\n\n            if (len <= (size_t) (buffer->last - buffer->pos)) {\n\n                p = buffer->pos;\n\n                if (buffer->event && p == buffer->start) {\n                    ngx_add_timer(buffer->event, buffer->flush);\n                }\n\n                for (i = 0; i < log[l].format->ops->nelts; i++) {\n                    p = op[i].run(r, p, &op[i]);\n                }\n\n                ngx_linefeed(p);\n\n                buffer->pos = p;\n\n                continue;\n            }\n\n            if (buffer->event && buffer->event->timer_set) {\n                ngx_del_timer(buffer->event);\n            }\n        }\n\n    alloc_line:\n\n        line = ngx_pnalloc(r->pool, len);\n        if (line == NULL) {\n            return NGX_ERROR;\n        }\n\n        p = line;\n\n        if (log[l].syslog_peer) {\n            p = ngx_syslog_add_header(log[l].syslog_peer, line);\n        }\n\n        for (i = 0; i < log[l].format->ops->nelts; i++) {\n            p = op[i].run(r, p, &op[i]);\n        }\n\n        if (log[l].syslog_peer) {\n\n            size = p - line;\n\n            n = ngx_syslog_send(log[l].syslog_peer, line, size);\n\n            if (n < 0) {\n                ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,\n                              \"send() to syslog failed\");\n\n            } else if ((size_t) n != size) {\n                ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,\n                              \"send() to syslog has written only %z of %uz\",\n                              n, size);\n            }\n\n            continue;\n        }\n\n        ngx_linefeed(p);\n\n        ngx_http_log_write(r, &log[l], line, p - line);\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_log_write(ngx_http_request_t *r, ngx_http_log_t *log, u_char *buf,\n    size_t len)\n{\n    u_char              *name;\n    time_t               now;\n    ssize_t              n;\n    ngx_err_t            err;\n#if (NGX_ZLIB)\n    ngx_http_log_buf_t  *buffer;\n#endif\n\n    if (log->script == NULL) {\n        name = log->file->name.data;\n\n#if (NGX_ZLIB)\n        buffer = log->file->data;\n\n        if (buffer && buffer->gzip) {\n            n = ngx_http_log_gzip(log->file->fd, buf, len, buffer->gzip,\n                                  r->connection->log);\n        } else {\n            n = ngx_write_fd(log->file->fd, buf, len);\n        }\n#else\n        n = ngx_write_fd(log->file->fd, buf, len);\n#endif\n\n    } else {\n        name = NULL;\n        n = ngx_http_log_script_write(r, log->script, &name, buf, len);\n    }\n\n    if (n == (ssize_t) len) {\n        return;\n    }\n\n    now = ngx_time();\n\n#if (T_PIPES)\n    if (name == NULL) {\n        name = (u_char *) \"log file\";\n    }\n#endif\n\n    if (n == -1) {\n        err = ngx_errno;\n\n        if (err == NGX_ENOSPC) {\n            log->disk_full_time = now;\n        }\n\n        if (now - log->error_log_time > 59) {\n            ngx_log_error(NGX_LOG_ALERT, r->connection->log, err,\n                          ngx_write_fd_n \" to \\\"%s\\\" failed\", name);\n\n            log->error_log_time = now;\n        }\n\n        return;\n    }\n\n    if (now - log->error_log_time > 59) {\n        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,\n                      ngx_write_fd_n \" to \\\"%s\\\" was incomplete: %z of %uz\",\n                      name, n, len);\n\n        log->error_log_time = now;\n    }\n}\n\n\nstatic ssize_t\nngx_http_log_script_write(ngx_http_request_t *r, ngx_http_log_script_t *script,\n    u_char **name, u_char *buf, size_t len)\n{\n    size_t                     root;\n    ssize_t                    n;\n    ngx_str_t                  log, path;\n    ngx_open_file_info_t       of;\n    ngx_http_log_loc_conf_t   *llcf;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    if (!r->root_tested) {\n\n        /* test root directory existence */\n\n        if (ngx_http_map_uri_to_path(r, &path, &root, 0) == NULL) {\n            /* simulate successful logging */\n            return len;\n        }\n\n        path.data[root] = '\\0';\n\n        ngx_memzero(&of, sizeof(ngx_open_file_info_t));\n\n        of.valid = clcf->open_file_cache_valid;\n        of.min_uses = clcf->open_file_cache_min_uses;\n        of.test_dir = 1;\n        of.test_only = 1;\n        of.errors = clcf->open_file_cache_errors;\n        of.events = clcf->open_file_cache_events;\n\n        if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) {\n            /* simulate successful logging */\n            return len;\n        }\n\n        if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)\n            != NGX_OK)\n        {\n            if (of.err == 0) {\n                /* simulate successful logging */\n                return len;\n            }\n\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, of.err,\n                          \"testing \\\"%s\\\" existence failed\", path.data);\n\n            /* simulate successful logging */\n            return len;\n        }\n\n        if (!of.is_dir) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, NGX_ENOTDIR,\n                          \"testing \\\"%s\\\" existence failed\", path.data);\n\n            /* simulate successful logging */\n            return len;\n        }\n    }\n\n    if (ngx_http_script_run(r, &log, script->lengths->elts, 1,\n                            script->values->elts)\n        == NULL)\n    {\n        /* simulate successful logging */\n        return len;\n    }\n\n    log.data[log.len - 1] = '\\0';\n    *name = log.data;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http log \\\"%s\\\"\", log.data);\n\n    llcf = ngx_http_get_module_loc_conf(r, ngx_http_log_module);\n\n    ngx_memzero(&of, sizeof(ngx_open_file_info_t));\n\n    of.log = 1;\n    of.valid = llcf->open_file_cache_valid;\n    of.min_uses = llcf->open_file_cache_min_uses;\n    of.directio = NGX_OPEN_FILE_DIRECTIO_OFF;\n\n    if (ngx_http_set_disable_symlinks(r, clcf, &log, &of) != NGX_OK) {\n        /* simulate successful logging */\n        return len;\n    }\n\n    if (ngx_open_cached_file(llcf->open_file_cache, &log, &of, r->pool)\n        != NGX_OK)\n    {\n        if (of.err == 0) {\n            /* simulate successful logging */\n            return len;\n        }\n\n        ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,\n                      \"%s \\\"%s\\\" failed\", of.failed, log.data);\n        /* simulate successful logging */\n        return len;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http log #%d\", of.fd);\n\n    n = ngx_write_fd(of.fd, buf, len);\n\n    return n;\n}\n\n\n#if (NGX_ZLIB)\n\nstatic ssize_t\nngx_http_log_gzip(ngx_fd_t fd, u_char *buf, size_t len, ngx_int_t level,\n    ngx_log_t *log)\n{\n    int          rc, wbits, memlevel;\n    u_char      *out;\n    size_t       size;\n    ssize_t      n;\n    z_stream     zstream;\n    ngx_err_t    err;\n    ngx_pool_t  *pool;\n\n    wbits = MAX_WBITS;\n    memlevel = MAX_MEM_LEVEL - 1;\n\n    while ((ssize_t) len < ((1 << (wbits - 1)) - 262)) {\n        wbits--;\n        memlevel--;\n    }\n\n    /*\n     * This is a formula from deflateBound() for conservative upper bound of\n     * compressed data plus 18 bytes of gzip wrapper.\n     */\n\n    size = len + ((len + 7) >> 3) + ((len + 63) >> 6) + 5 + 18;\n\n    ngx_memzero(&zstream, sizeof(z_stream));\n\n    pool = ngx_create_pool(256, log);\n    if (pool == NULL) {\n        /* simulate successful logging */\n        return len;\n    }\n\n    pool->log = log;\n\n    zstream.zalloc = ngx_http_log_gzip_alloc;\n    zstream.zfree = ngx_http_log_gzip_free;\n    zstream.opaque = pool;\n\n    out = ngx_pnalloc(pool, size);\n    if (out == NULL) {\n        goto done;\n    }\n\n    zstream.next_in = buf;\n    zstream.avail_in = len;\n    zstream.next_out = out;\n    zstream.avail_out = size;\n\n    rc = deflateInit2(&zstream, (int) level, Z_DEFLATED, wbits + 16, memlevel,\n                      Z_DEFAULT_STRATEGY);\n\n    if (rc != Z_OK) {\n        ngx_log_error(NGX_LOG_ALERT, log, 0, \"deflateInit2() failed: %d\", rc);\n        goto done;\n    }\n\n    ngx_log_debug4(NGX_LOG_DEBUG_HTTP, log, 0,\n                   \"deflate in: ni:%p no:%p ai:%ud ao:%ud\",\n                   zstream.next_in, zstream.next_out,\n                   zstream.avail_in, zstream.avail_out);\n\n    rc = deflate(&zstream, Z_FINISH);\n\n    if (rc != Z_STREAM_END) {\n        ngx_log_error(NGX_LOG_ALERT, log, 0,\n                      \"deflate(Z_FINISH) failed: %d\", rc);\n        goto done;\n    }\n\n    ngx_log_debug5(NGX_LOG_DEBUG_HTTP, log, 0,\n                   \"deflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d\",\n                   zstream.next_in, zstream.next_out,\n                   zstream.avail_in, zstream.avail_out,\n                   rc);\n\n    size -= zstream.avail_out;\n\n    rc = deflateEnd(&zstream);\n\n    if (rc != Z_OK) {\n        ngx_log_error(NGX_LOG_ALERT, log, 0, \"deflateEnd() failed: %d\", rc);\n        goto done;\n    }\n\n    n = ngx_write_fd(fd, out, size);\n\n    if (n != (ssize_t) size) {\n        err = (n == -1) ? ngx_errno : 0;\n\n        ngx_destroy_pool(pool);\n\n        ngx_set_errno(err);\n        return -1;\n    }\n\ndone:\n\n    ngx_destroy_pool(pool);\n\n    /* simulate successful logging */\n    return len;\n}\n\n\nstatic void *\nngx_http_log_gzip_alloc(void *opaque, u_int items, u_int size)\n{\n    ngx_pool_t *pool = opaque;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pool->log, 0,\n                   \"gzip alloc: n:%ud s:%ud\", items, size);\n\n    return ngx_palloc(pool, items * size);\n}\n\n\nstatic void\nngx_http_log_gzip_free(void *opaque, void *address)\n{\n#if 0\n    ngx_pool_t *pool = opaque;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pool->log, 0, \"gzip free: %p\", address);\n#endif\n}\n\n#endif\n\n\nstatic void\nngx_http_log_flush(ngx_open_file_t *file, ngx_log_t *log)\n{\n    size_t               len;\n    ssize_t              n;\n    ngx_http_log_buf_t  *buffer;\n\n    buffer = file->data;\n\n    len = buffer->pos - buffer->start;\n\n    if (len == 0) {\n        return;\n    }\n\n#if (NGX_ZLIB)\n    if (buffer->gzip) {\n        n = ngx_http_log_gzip(file->fd, buffer->start, len, buffer->gzip, log);\n    } else {\n        n = ngx_write_fd(file->fd, buffer->start, len);\n    }\n#else\n    n = ngx_write_fd(file->fd, buffer->start, len);\n#endif\n\n    if (n == -1) {\n        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,\n                      ngx_write_fd_n \" to \\\"%s\\\" failed\",\n                      file->name.data);\n\n    } else if ((size_t) n != len) {\n        ngx_log_error(NGX_LOG_ALERT, log, 0,\n                      ngx_write_fd_n \" to \\\"%s\\\" was incomplete: %z of %uz\",\n                      file->name.data, n, len);\n    }\n\n    buffer->pos = buffer->start;\n\n    if (buffer->event && buffer->event->timer_set) {\n        ngx_del_timer(buffer->event);\n    }\n}\n\n\nstatic void\nngx_http_log_flush_handler(ngx_event_t *ev)\n{\n    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0,\n                   \"http log buffer flush handler\");\n\n    ngx_http_log_flush(ev->data, ev->log);\n}\n\n\nstatic u_char *\nngx_http_log_copy_short(ngx_http_request_t *r, u_char *buf,\n    ngx_http_log_op_t *op)\n{\n    size_t     len;\n    uintptr_t  data;\n\n    len = op->len;\n    data = op->data;\n\n    while (len--) {\n        *buf++ = (u_char) (data & 0xff);\n        data >>= 8;\n    }\n\n    return buf;\n}\n\n\nstatic u_char *\nngx_http_log_copy_long(ngx_http_request_t *r, u_char *buf,\n    ngx_http_log_op_t *op)\n{\n    return ngx_cpymem(buf, (u_char *) op->data, op->len);\n}\n\n\nstatic u_char *\nngx_http_log_pipe(ngx_http_request_t *r, u_char *buf, ngx_http_log_op_t *op)\n{\n    if (r->pipeline) {\n        *buf = 'p';\n    } else {\n        *buf = '.';\n    }\n\n    return buf + 1;\n}\n\n\nstatic u_char *\nngx_http_log_time(ngx_http_request_t *r, u_char *buf, ngx_http_log_op_t *op)\n{\n    return ngx_cpymem(buf, ngx_cached_http_log_time.data,\n                      ngx_cached_http_log_time.len);\n}\n\nstatic u_char *\nngx_http_log_iso8601(ngx_http_request_t *r, u_char *buf, ngx_http_log_op_t *op)\n{\n    return ngx_cpymem(buf, ngx_cached_http_log_iso8601.data,\n                      ngx_cached_http_log_iso8601.len);\n}\n\nstatic u_char *\nngx_http_log_msec(ngx_http_request_t *r, u_char *buf, ngx_http_log_op_t *op)\n{\n    ngx_time_t  *tp;\n\n    tp = ngx_timeofday();\n\n    return ngx_sprintf(buf, \"%T.%03M\", tp->sec, tp->msec);\n}\n\n\nstatic u_char *\nngx_http_log_request_time(ngx_http_request_t *r, u_char *buf,\n    ngx_http_log_op_t *op)\n{\n    ngx_time_t      *tp;\n    ngx_msec_int_t   ms;\n\n#if (T_NGX_RET_CACHE)\n    struct timeval             tv;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n    if (clcf->request_time_cache) {\n        tp = ngx_timeofday();\n        ms = (ngx_msec_int_t)\n                 ((tp->sec - r->start_sec) * 1000 + (tp->msec - r->start_msec));\n\n    } else {\n        ngx_gettimeofday(&tv);\n        ms = (ngx_msec_int_t)\n             ((tv.tv_sec - r->start_sec) * 1000\n              + (tv.tv_usec / 1000 - r->start_msec));\n    }\n\n#else\n    tp = ngx_timeofday();\n\n    ms = (ngx_msec_int_t)\n             ((tp->sec - r->start_sec) * 1000 + (tp->msec - r->start_msec));\n#endif\n\n    ms = ngx_max(ms, 0);\n\n    return ngx_sprintf(buf, \"%T.%03M\", (time_t) ms / 1000, ms % 1000);\n}\n\n\n#if (T_NGX_VARS)\nstatic u_char *\nngx_http_log_request_time_msec(ngx_http_request_t *r, u_char *buf,\n    ngx_http_log_op_t *op)\n{\n    ngx_time_t                *tp;\n    ngx_msec_int_t             ms;\n#if (T_NGX_RET_CACHE)\n    struct timeval             tv;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n    if (clcf->request_time_cache) {\n        tp = ngx_timeofday();\n        ms = (ngx_msec_int_t)\n                 ((tp->sec - r->start_sec) * 1000 + (tp->msec - r->start_msec));\n    } else {\n        ngx_gettimeofday(&tv);\n        ms = (ngx_msec_int_t) ((tv.tv_sec - r->start_sec) * 1000\n              + (tv.tv_usec / 1000 - r->start_msec));\n    }\n\n#else\n    tp = ngx_timeofday();\n    ms = (ngx_msec_int_t)\n             ((tp->sec - r->start_sec) * 1000 + (tp->msec - r->start_msec));    \n#endif\n\n    ms = ngx_max(ms, 0);\n\n    return ngx_sprintf(buf, \"%T\", (time_t) ms);\n}\n\n\nstatic u_char *\nngx_http_log_request_time_usec(ngx_http_request_t *r, u_char *buf,\n    ngx_http_log_op_t *op)\n{\n    ngx_time_t                *tp;\n    ngx_usec_int_t             us;\n#if (T_NGX_RET_CACHE)\n    struct timeval             tv;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n    if (clcf->request_time_cache) {\n        tp = ngx_timeofday();\n        us = (ngx_usec_int_t) (1000 *\n                 ((tp->sec - r->start_sec) * 1000 + (tp->msec - r->start_msec)))\n                 + tp->usec - r->start_usec;\n    } else {\n        ngx_gettimeofday(&tv);\n        us = (ngx_usec_int_t) (1000 * ((tv.tv_sec - r->start_sec) * 1000\n                 + (tv.tv_usec / 1000 - r->start_msec)))\n                 + tv.tv_usec % 1000 - r->start_usec;\n    }\n\n#else\n    tp = ngx_timeofday();\n    us = (ngx_usec_int_t) (1000 *\n             ((tp->sec - r->start_sec) * 1000 + (tp->msec - r->start_msec)));\n#endif\n\n    us = ngx_max(us, 0);\n\n    return ngx_sprintf(buf, \"%T\", (time_t) us);\n}\n#endif\n\n\nstatic u_char *\nngx_http_log_status(ngx_http_request_t *r, u_char *buf, ngx_http_log_op_t *op)\n{\n    ngx_uint_t  status;\n\n    if (r->err_status) {\n        status = r->err_status;\n\n    } else if (r->headers_out.status) {\n        status = r->headers_out.status;\n\n    } else if (r->http_version == NGX_HTTP_VERSION_9) {\n        status = 9;\n\n    } else {\n        status = 0;\n    }\n\n    return ngx_sprintf(buf, \"%03ui\", status);\n}\n\n\nstatic u_char *\nngx_http_log_bytes_sent(ngx_http_request_t *r, u_char *buf,\n    ngx_http_log_op_t *op)\n{\n    return ngx_sprintf(buf, \"%O\", r->connection->sent);\n}\n\n\n/*\n * although there is a real $body_bytes_sent variable,\n * this log operation code function is more optimized for logging\n */\n\nstatic u_char *\nngx_http_log_body_bytes_sent(ngx_http_request_t *r, u_char *buf,\n    ngx_http_log_op_t *op)\n{\n    off_t  length;\n\n    length = r->connection->sent - r->header_size;\n\n    if (length > 0) {\n        return ngx_sprintf(buf, \"%O\", length);\n    }\n\n    *buf = '0';\n\n    return buf + 1;\n}\n\n\nstatic u_char *\nngx_http_log_request_length(ngx_http_request_t *r, u_char *buf,\n    ngx_http_log_op_t *op)\n{\n    return ngx_sprintf(buf, \"%O\", r->request_length);\n}\n\n\nstatic ngx_int_t\nngx_http_log_variable_compile(ngx_conf_t *cf, ngx_http_log_op_t *op,\n    ngx_str_t *value, ngx_uint_t escape)\n{\n    ngx_int_t  index;\n\n    index = ngx_http_get_variable_index(cf, value);\n    if (index == NGX_ERROR) {\n        return NGX_ERROR;\n    }\n\n    op->len = 0;\n\n    switch (escape) {\n    case NGX_HTTP_LOG_ESCAPE_JSON:\n        op->getlen = ngx_http_log_json_variable_getlen;\n        op->run = ngx_http_log_json_variable;\n        break;\n\n    case NGX_HTTP_LOG_ESCAPE_NONE:\n        op->getlen = ngx_http_log_unescaped_variable_getlen;\n        op->run = ngx_http_log_unescaped_variable;\n        break;\n\n    default: /* NGX_HTTP_LOG_ESCAPE_DEFAULT */\n        op->getlen = ngx_http_log_variable_getlen;\n        op->run = ngx_http_log_variable;\n    }\n\n    op->data = index;\n\n    return NGX_OK;\n}\n\n\nstatic size_t\nngx_http_log_variable_getlen(ngx_http_request_t *r, uintptr_t data)\n{\n    uintptr_t                   len;\n    ngx_http_variable_value_t  *value;\n\n    value = ngx_http_get_indexed_variable(r, data);\n\n    if (value == NULL || value->not_found) {\n        return 1;\n    }\n\n    len = ngx_http_log_escape(NULL, value->data, value->len);\n\n    value->escape = len ? 1 : 0;\n\n    return value->len + len * 3;\n}\n\n\nstatic u_char *\nngx_http_log_variable(ngx_http_request_t *r, u_char *buf, ngx_http_log_op_t *op)\n{\n    ngx_http_variable_value_t  *value;\n\n    value = ngx_http_get_indexed_variable(r, op->data);\n\n    if (value == NULL || value->not_found) {\n        *buf = '-';\n        return buf + 1;\n    }\n\n    if (value->escape == 0) {\n        return ngx_cpymem(buf, value->data, value->len);\n\n    } else {\n        return (u_char *) ngx_http_log_escape(buf, value->data, value->len);\n    }\n}\n\n\nstatic uintptr_t\nngx_http_log_escape(u_char *dst, u_char *src, size_t size)\n{\n    ngx_uint_t      n;\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\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] & (1U << (*src & 0x1f))) {\n                n++;\n            }\n            src++;\n            size--;\n        }\n\n        return (uintptr_t) n;\n    }\n\n    while (size) {\n        if (escape[*src >> 5] & (1U << (*src & 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        size--;\n    }\n\n    return (uintptr_t) dst;\n}\n\n\nstatic size_t\nngx_http_log_json_variable_getlen(ngx_http_request_t *r, uintptr_t data)\n{\n    uintptr_t                   len;\n    ngx_http_variable_value_t  *value;\n\n    value = ngx_http_get_indexed_variable(r, data);\n\n    if (value == NULL || value->not_found) {\n        return 0;\n    }\n\n    len = ngx_escape_json(NULL, value->data, value->len);\n\n    value->escape = len ? 1 : 0;\n\n    return value->len + len;\n}\n\n\nstatic u_char *\nngx_http_log_json_variable(ngx_http_request_t *r, u_char *buf,\n    ngx_http_log_op_t *op)\n{\n    ngx_http_variable_value_t  *value;\n\n    value = ngx_http_get_indexed_variable(r, op->data);\n\n    if (value == NULL || value->not_found) {\n        return buf;\n    }\n\n    if (value->escape == 0) {\n        return ngx_cpymem(buf, value->data, value->len);\n\n    } else {\n        return (u_char *) ngx_escape_json(buf, value->data, value->len);\n    }\n}\n\n\nstatic size_t\nngx_http_log_unescaped_variable_getlen(ngx_http_request_t *r, uintptr_t data)\n{\n    ngx_http_variable_value_t  *value;\n\n    value = ngx_http_get_indexed_variable(r, data);\n\n    if (value == NULL || value->not_found) {\n        return 0;\n    }\n\n    value->escape = 0;\n\n    return value->len;\n}\n\n\nstatic u_char *\nngx_http_log_unescaped_variable(ngx_http_request_t *r, u_char *buf,\n    ngx_http_log_op_t *op)\n{\n    ngx_http_variable_value_t  *value;\n\n    value = ngx_http_get_indexed_variable(r, op->data);\n\n    if (value == NULL || value->not_found) {\n        return buf;\n    }\n\n    return ngx_cpymem(buf, value->data, value->len);\n}\n\n\nstatic void *\nngx_http_log_create_main_conf(ngx_conf_t *cf)\n{\n    ngx_http_log_main_conf_t  *conf;\n\n    ngx_http_log_fmt_t  *fmt;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_log_main_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    if (ngx_array_init(&conf->formats, cf->pool, 4, sizeof(ngx_http_log_fmt_t))\n        != NGX_OK)\n    {\n        return NULL;\n    }\n\n    fmt = ngx_array_push(&conf->formats);\n    if (fmt == NULL) {\n        return NULL;\n    }\n\n    ngx_str_set(&fmt->name, \"combined\");\n\n    fmt->flushes = NULL;\n\n    fmt->ops = ngx_array_create(cf->pool, 16, sizeof(ngx_http_log_op_t));\n    if (fmt->ops == NULL) {\n        return NULL;\n    }\n\n    return conf;\n}\n\n\nstatic void *\nngx_http_log_create_loc_conf(ngx_conf_t *cf)\n{\n    ngx_http_log_loc_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_log_loc_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    conf->open_file_cache = NGX_CONF_UNSET_PTR;\n\n    return conf;\n}\n\n\nstatic char *\nngx_http_log_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_http_log_loc_conf_t *prev = parent;\n    ngx_http_log_loc_conf_t *conf = child;\n\n    ngx_http_log_t            *log;\n    ngx_http_log_fmt_t        *fmt;\n    ngx_http_log_main_conf_t  *lmcf;\n\n    if (conf->open_file_cache == NGX_CONF_UNSET_PTR) {\n\n        conf->open_file_cache = prev->open_file_cache;\n        conf->open_file_cache_valid = prev->open_file_cache_valid;\n        conf->open_file_cache_min_uses = prev->open_file_cache_min_uses;\n\n        if (conf->open_file_cache == NGX_CONF_UNSET_PTR) {\n            conf->open_file_cache = NULL;\n        }\n    }\n\n    if (conf->logs || conf->off) {\n        return NGX_CONF_OK;\n    }\n\n    conf->logs = prev->logs;\n    conf->off = prev->off;\n\n    if (conf->logs || conf->off) {\n        return NGX_CONF_OK;\n    }\n\n    conf->logs = ngx_array_create(cf->pool, 2, sizeof(ngx_http_log_t));\n    if (conf->logs == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    log = ngx_array_push(conf->logs);\n    if (log == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_memzero(log, sizeof(ngx_http_log_t));\n\n    log->file = ngx_conf_open_file(cf->cycle, &ngx_http_access_log);\n    if (log->file == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_log_module);\n    fmt = lmcf->formats.elts;\n\n    /* the default \"combined\" format */\n    log->format = &fmt[0];\n    lmcf->combined_used = 1;\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_log_set_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_log_loc_conf_t *llcf = conf;\n\n    ssize_t                            size;\n    ngx_int_t                          gzip;\n    ngx_uint_t                         i, n;\n    ngx_msec_t                         flush;\n    ngx_str_t                         *value, name, s;\n    ngx_http_log_t                    *log;\n    ngx_syslog_peer_t                 *peer;\n    ngx_http_log_buf_t                *buffer;\n    ngx_http_log_fmt_t                *fmt;\n    ngx_http_log_main_conf_t          *lmcf;\n    ngx_http_script_compile_t          sc;\n    ngx_http_compile_complex_value_t   ccv;\n\n    value = cf->args->elts;\n\n    if (ngx_strcmp(value[1].data, \"off\") == 0) {\n        llcf->off = 1;\n        if (cf->args->nelts == 2) {\n            return NGX_CONF_OK;\n        }\n\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid parameter \\\"%V\\\"\", &value[2]);\n        return NGX_CONF_ERROR;\n    }\n\n    if (llcf->logs == NULL) {\n        llcf->logs = ngx_array_create(cf->pool, 2, sizeof(ngx_http_log_t));\n        if (llcf->logs == NULL) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_log_module);\n\n    log = ngx_array_push(llcf->logs);\n    if (log == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_memzero(log, sizeof(ngx_http_log_t));\n\n\n    if (ngx_strncmp(value[1].data, \"syslog:\", 7) == 0) {\n\n        peer = ngx_pcalloc(cf->pool, sizeof(ngx_syslog_peer_t));\n        if (peer == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        if (ngx_syslog_process_conf(cf, peer) != NGX_CONF_OK) {\n            return NGX_CONF_ERROR;\n        }\n\n        log->syslog_peer = peer;\n\n        goto process_formats;\n#if (T_PIPES) && !(NGX_WIN32)\n    } else if (ngx_strncmp(value[1].data, \"pipe:\", 5) == 0) {\n        if (value[1].len == 5) {\n            return NGX_CONF_ERROR;\n        }\n\n        value[1].len -= 5;\n        value[1].data += 5;\n\n        ngx_open_pipe_t *pipe_conf = ngx_conf_open_pipe(cf->cycle, &value[1], \"w\");\n        if (pipe_conf == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        log->file = pipe_conf->open_fd;\n#ifdef LOG_PIPE_NEED_BACKUP\n        if (log->file != NULL) {\n            name = ngx_log_access_backup;\n            if (ngx_conf_full_name(cf->cycle, &name, 0) != NGX_OK) {\n                return \"fail to set bakup\";\n            }\n\n            log->file->name = name;\n        }\n#endif\n\n        goto process_formats;\n#endif\n    }\n\n    n = ngx_http_script_variables_count(&value[1]);\n\n    if (n == 0) {\n        log->file = ngx_conf_open_file(cf->cycle, &value[1]);\n        if (log->file == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n    } else {\n        if (ngx_conf_full_name(cf->cycle, &value[1], 0) != NGX_OK) {\n            return NGX_CONF_ERROR;\n        }\n\n        log->script = ngx_pcalloc(cf->pool, sizeof(ngx_http_log_script_t));\n        if (log->script == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));\n\n        sc.cf = cf;\n        sc.source = &value[1];\n        sc.lengths = &log->script->lengths;\n        sc.values = &log->script->values;\n        sc.variables = n;\n        sc.complete_lengths = 1;\n        sc.complete_values = 1;\n\n        if (ngx_http_script_compile(&sc) != NGX_OK) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\nprocess_formats:\n\n    if (cf->args->nelts >= 3) {\n        name = value[2];\n\n        if (ngx_strcmp(name.data, \"combined\") == 0) {\n            lmcf->combined_used = 1;\n        }\n\n    } else {\n        ngx_str_set(&name, \"combined\");\n        lmcf->combined_used = 1;\n    }\n\n    fmt = lmcf->formats.elts;\n    for (i = 0; i < lmcf->formats.nelts; i++) {\n        if (fmt[i].name.len == name.len\n            && ngx_strcasecmp(fmt[i].name.data, name.data) == 0)\n        {\n            log->format = &fmt[i];\n            break;\n        }\n    }\n\n    if (log->format == NULL) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"unknown log format \\\"%V\\\"\", &name);\n        return NGX_CONF_ERROR;\n    }\n\n    size = 0;\n    flush = 0;\n    gzip = 0;\n\n    for (i = 3; i < cf->args->nelts; i++) {\n\n        if (ngx_strncmp(value[i].data, \"buffer=\", 7) == 0) {\n            s.len = value[i].len - 7;\n            s.data = value[i].data + 7;\n\n            size = ngx_parse_size(&s);\n\n            if (size == NGX_ERROR || size == 0) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"invalid buffer size \\\"%V\\\"\", &s);\n                return NGX_CONF_ERROR;\n            }\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"flush=\", 6) == 0) {\n            s.len = value[i].len - 6;\n            s.data = value[i].data + 6;\n\n            flush = ngx_parse_time(&s, 0);\n\n            if (flush == (ngx_msec_t) NGX_ERROR || flush == 0) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"invalid flush time \\\"%V\\\"\", &s);\n                return NGX_CONF_ERROR;\n            }\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"gzip\", 4) == 0\n            && (value[i].len == 4 || value[i].data[4] == '='))\n        {\n#if (NGX_ZLIB)\n            if (size == 0) {\n                size = 64 * 1024;\n            }\n\n            if (value[i].len == 4) {\n                gzip = Z_BEST_SPEED;\n                continue;\n            }\n\n            s.len = value[i].len - 5;\n            s.data = value[i].data + 5;\n\n            gzip = ngx_atoi(s.data, s.len);\n\n            if (gzip < 1 || gzip > 9) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"invalid compression level \\\"%V\\\"\", &s);\n                return NGX_CONF_ERROR;\n            }\n\n            continue;\n\n#else\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"nginx was built without zlib support\");\n            return NGX_CONF_ERROR;\n#endif\n        }\n\n        if (ngx_strncmp(value[i].data, \"if=\", 3) == 0) {\n            s.len = value[i].len - 3;\n            s.data = value[i].data + 3;\n\n            ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n\n            ccv.cf = cf;\n            ccv.value = &s;\n            ccv.complex_value = ngx_palloc(cf->pool,\n                                           sizeof(ngx_http_complex_value_t));\n            if (ccv.complex_value == NULL) {\n                return NGX_CONF_ERROR;\n            }\n\n            if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {\n                return NGX_CONF_ERROR;\n            }\n\n            log->filter = ccv.complex_value;\n\n            continue;\n        }\n\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid parameter \\\"%V\\\"\", &value[i]);\n        return NGX_CONF_ERROR;\n    }\n\n    if (flush && size == 0) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"no buffer is defined for access_log \\\"%V\\\"\",\n                           &value[1]);\n        return NGX_CONF_ERROR;\n    }\n\n    if (size) {\n\n        if (log->script) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"buffered logs cannot have variables in name\");\n            return NGX_CONF_ERROR;\n        }\n\n        if (log->syslog_peer) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"logs to syslog cannot be buffered\");\n            return NGX_CONF_ERROR;\n        }\n\n        if (log->file->data) {\n            buffer = log->file->data;\n\n            if (buffer->last - buffer->start != size\n                || buffer->flush != flush\n                || buffer->gzip != gzip)\n            {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"access_log \\\"%V\\\" already defined \"\n                                   \"with conflicting parameters\",\n                                   &value[1]);\n                return NGX_CONF_ERROR;\n            }\n\n            return NGX_CONF_OK;\n        }\n\n        buffer = ngx_pcalloc(cf->pool, sizeof(ngx_http_log_buf_t));\n        if (buffer == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        buffer->start = ngx_pnalloc(cf->pool, size);\n        if (buffer->start == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        buffer->pos = buffer->start;\n        buffer->last = buffer->start + size;\n\n        if (flush) {\n            buffer->event = ngx_pcalloc(cf->pool, sizeof(ngx_event_t));\n            if (buffer->event == NULL) {\n                return NGX_CONF_ERROR;\n            }\n\n            buffer->event->data = log->file;\n            buffer->event->handler = ngx_http_log_flush_handler;\n            buffer->event->log = &cf->cycle->new_log;\n            buffer->event->cancelable = 1;\n\n            buffer->flush = flush;\n        }\n\n        buffer->gzip = gzip;\n\n        log->file->flush = ngx_http_log_flush;\n        log->file->data = buffer;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_log_set_format(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_log_main_conf_t *lmcf = conf;\n\n    ngx_str_t           *value;\n    ngx_uint_t           i;\n    ngx_http_log_fmt_t  *fmt;\n\n    value = cf->args->elts;\n\n    fmt = lmcf->formats.elts;\n    for (i = 0; i < lmcf->formats.nelts; i++) {\n        if (fmt[i].name.len == value[1].len\n            && ngx_strcmp(fmt[i].name.data, value[1].data) == 0)\n        {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"duplicate \\\"log_format\\\" name \\\"%V\\\"\",\n                               &value[1]);\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    fmt = ngx_array_push(&lmcf->formats);\n    if (fmt == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    fmt->name = value[1];\n\n    fmt->flushes = ngx_array_create(cf->pool, 4, sizeof(ngx_int_t));\n    if (fmt->flushes == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    fmt->ops = ngx_array_create(cf->pool, 16, sizeof(ngx_http_log_op_t));\n    if (fmt->ops == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    return ngx_http_log_compile_format(cf, fmt->flushes, fmt->ops, cf->args, 2);\n}\n\n\nstatic char *\nngx_http_log_compile_format(ngx_conf_t *cf, ngx_array_t *flushes,\n    ngx_array_t *ops, ngx_array_t *args, ngx_uint_t s)\n{\n    u_char              *data, *p, ch;\n    size_t               i, len;\n    ngx_str_t           *value, var;\n    ngx_int_t           *flush;\n    ngx_uint_t           bracket, escape;\n    ngx_http_log_op_t   *op;\n    ngx_http_log_var_t  *v;\n\n    escape = NGX_HTTP_LOG_ESCAPE_DEFAULT;\n    value = args->elts;\n\n    if (s < args->nelts && ngx_strncmp(value[s].data, \"escape=\", 7) == 0) {\n        data = value[s].data + 7;\n\n        if (ngx_strcmp(data, \"json\") == 0) {\n            escape = NGX_HTTP_LOG_ESCAPE_JSON;\n\n        } else if (ngx_strcmp(data, \"none\") == 0) {\n            escape = NGX_HTTP_LOG_ESCAPE_NONE;\n\n        } else if (ngx_strcmp(data, \"default\") != 0) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"unknown log format escaping \\\"%s\\\"\", data);\n            return NGX_CONF_ERROR;\n        }\n\n        s++;\n    }\n\n    for ( /* void */ ; s < args->nelts; s++) {\n\n        i = 0;\n\n        while (i < value[s].len) {\n\n            op = ngx_array_push(ops);\n            if (op == NULL) {\n                return NGX_CONF_ERROR;\n            }\n\n            data = &value[s].data[i];\n\n            if (value[s].data[i] == '$') {\n\n                if (++i == value[s].len) {\n                    goto invalid;\n                }\n\n                if (value[s].data[i] == '{') {\n                    bracket = 1;\n\n                    if (++i == value[s].len) {\n                        goto invalid;\n                    }\n\n                    var.data = &value[s].data[i];\n\n                } else {\n                    bracket = 0;\n                    var.data = &value[s].data[i];\n                }\n\n                for (var.len = 0; i < value[s].len; i++, var.len++) {\n                    ch = value[s].data[i];\n\n                    if (ch == '}' && bracket) {\n                        i++;\n                        bracket = 0;\n                        break;\n                    }\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_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                       \"the closing bracket in \\\"%V\\\" \"\n                                       \"variable is missing\", &var);\n                    return NGX_CONF_ERROR;\n                }\n\n                if (var.len == 0) {\n                    goto invalid;\n                }\n\n                for (v = ngx_http_log_vars; v->name.len; v++) {\n\n                    if (v->name.len == var.len\n                        && ngx_strncmp(v->name.data, var.data, var.len) == 0)\n                    {\n                        op->len = v->len;\n                        op->getlen = NULL;\n                        op->run = v->run;\n                        op->data = 0;\n\n                        goto found;\n                    }\n                }\n\n                if (ngx_http_log_variable_compile(cf, op, &var, escape)\n                    != NGX_OK)\n                {\n                    return NGX_CONF_ERROR;\n                }\n\n                if (flushes) {\n\n                    flush = ngx_array_push(flushes);\n                    if (flush == NULL) {\n                        return NGX_CONF_ERROR;\n                    }\n\n                    *flush = op->data; /* variable index */\n                }\n\n            found:\n\n                continue;\n            }\n\n            i++;\n\n            while (i < value[s].len && value[s].data[i] != '$') {\n                i++;\n            }\n\n            len = &value[s].data[i] - data;\n\n            if (len) {\n\n                op->len = len;\n                op->getlen = NULL;\n\n                if (len <= sizeof(uintptr_t)) {\n                    op->run = ngx_http_log_copy_short;\n                    op->data = 0;\n\n                    while (len--) {\n                        op->data <<= 8;\n                        op->data |= data[len];\n                    }\n\n                } else {\n                    op->run = ngx_http_log_copy_long;\n\n                    p = ngx_pnalloc(cf->pool, len);\n                    if (p == NULL) {\n                        return NGX_CONF_ERROR;\n                    }\n\n                    ngx_memcpy(p, data, len);\n                    op->data = (uintptr_t) p;\n                }\n            }\n        }\n    }\n\n    return NGX_CONF_OK;\n\ninvalid:\n\n    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, \"invalid parameter \\\"%s\\\"\", data);\n\n    return NGX_CONF_ERROR;\n}\n\n\nstatic char *\nngx_http_log_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_log_loc_conf_t *llcf = conf;\n\n    time_t       inactive, valid;\n    ngx_str_t   *value, s;\n    ngx_int_t    max, min_uses;\n    ngx_uint_t   i;\n\n    if (llcf->open_file_cache != NGX_CONF_UNSET_PTR) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    max = 0;\n    inactive = 10;\n    valid = 60;\n    min_uses = 1;\n\n    for (i = 1; i < cf->args->nelts; i++) {\n\n        if (ngx_strncmp(value[i].data, \"max=\", 4) == 0) {\n\n            max = ngx_atoi(value[i].data + 4, value[i].len - 4);\n            if (max == NGX_ERROR) {\n                goto failed;\n            }\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"inactive=\", 9) == 0) {\n\n            s.len = value[i].len - 9;\n            s.data = value[i].data + 9;\n\n            inactive = ngx_parse_time(&s, 1);\n            if (inactive == (time_t) NGX_ERROR) {\n                goto failed;\n            }\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"min_uses=\", 9) == 0) {\n\n            min_uses = ngx_atoi(value[i].data + 9, value[i].len - 9);\n            if (min_uses == NGX_ERROR) {\n                goto failed;\n            }\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"valid=\", 6) == 0) {\n\n            s.len = value[i].len - 6;\n            s.data = value[i].data + 6;\n\n            valid = ngx_parse_time(&s, 1);\n            if (valid == (time_t) NGX_ERROR) {\n                goto failed;\n            }\n\n            continue;\n        }\n\n        if (ngx_strcmp(value[i].data, \"off\") == 0) {\n\n            llcf->open_file_cache = NULL;\n\n            continue;\n        }\n\n    failed:\n\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid \\\"open_log_file_cache\\\" parameter \\\"%V\\\"\",\n                           &value[i]);\n        return NGX_CONF_ERROR;\n    }\n\n    if (llcf->open_file_cache == NULL) {\n        return NGX_CONF_OK;\n    }\n\n    if (max == 0) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                        \"\\\"open_log_file_cache\\\" must have \\\"max\\\" parameter\");\n        return NGX_CONF_ERROR;\n    }\n\n    llcf->open_file_cache = ngx_open_file_cache_init(cf->pool, max, inactive);\n\n    if (llcf->open_file_cache) {\n\n        llcf->open_file_cache_valid = valid;\n        llcf->open_file_cache_min_uses = min_uses;\n\n        return NGX_CONF_OK;\n    }\n\n    return NGX_CONF_ERROR;\n}\n\n\nstatic ngx_int_t\nngx_http_log_init(ngx_conf_t *cf)\n{\n    ngx_str_t                  *value;\n    ngx_array_t                 a;\n    ngx_http_handler_pt        *h;\n    ngx_http_log_fmt_t         *fmt;\n    ngx_http_log_main_conf_t   *lmcf;\n    ngx_http_core_main_conf_t  *cmcf;\n\n    lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_log_module);\n\n    if (lmcf->combined_used) {\n        if (ngx_array_init(&a, cf->pool, 1, sizeof(ngx_str_t)) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        value = ngx_array_push(&a);\n        if (value == NULL) {\n            return NGX_ERROR;\n        }\n\n        *value = ngx_http_combined_fmt;\n        fmt = lmcf->formats.elts;\n\n        if (ngx_http_log_compile_format(cf, NULL, fmt->ops, &a, 0)\n            != NGX_CONF_OK)\n        {\n            return NGX_ERROR;\n        }\n    }\n\n    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);\n\n    h = ngx_array_push(&cmcf->phases[NGX_HTTP_LOG_PHASE].handlers);\n    if (h == NULL) {\n        return NGX_ERROR;\n    }\n\n    *h = ngx_http_log_handler;\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "src/http/modules/ngx_http_map_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\ntypedef struct {\n    ngx_uint_t                  hash_max_size;\n    ngx_uint_t                  hash_bucket_size;\n} ngx_http_map_conf_t;\n\n\ntypedef struct {\n    ngx_hash_keys_arrays_t      keys;\n\n    ngx_array_t                *values_hash;\n#if (NGX_PCRE)\n    ngx_array_t                 regexes;\n#endif\n\n    ngx_http_variable_value_t  *default_value;\n    ngx_conf_t                 *cf;\n    unsigned                    hostnames:1;\n    unsigned                    no_cacheable:1;\n} ngx_http_map_conf_ctx_t;\n\n\ntypedef struct {\n    ngx_http_map_t              map;\n    ngx_http_complex_value_t    value;\n    ngx_http_variable_value_t  *default_value;\n    ngx_uint_t                  hostnames;      /* unsigned  hostnames:1 */\n} ngx_http_map_ctx_t;\n\n\nstatic int ngx_libc_cdecl ngx_http_map_cmp_dns_wildcards(const void *one,\n    const void *two);\nstatic void *ngx_http_map_create_conf(ngx_conf_t *cf);\nstatic char *ngx_http_map_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\nstatic char *ngx_http_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf);\n\n\nstatic ngx_command_t  ngx_http_map_commands[] = {\n\n    { ngx_string(\"map\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE2,\n      ngx_http_map_block,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"map_hash_max_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_map_conf_t, hash_max_size),\n      NULL },\n\n    { ngx_string(\"map_hash_bucket_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_map_conf_t, hash_bucket_size),\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_map_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    NULL,                                  /* postconfiguration */\n\n    ngx_http_map_create_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_map_module = {\n    NGX_MODULE_V1,\n    &ngx_http_map_module_ctx,              /* module context */\n    ngx_http_map_commands,                 /* 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_map_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,\n    uintptr_t data)\n{\n    ngx_http_map_ctx_t  *map = (ngx_http_map_ctx_t *) data;\n\n    ngx_str_t                   val, str;\n    ngx_http_complex_value_t   *cv;\n    ngx_http_variable_value_t  *value;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http map started\");\n\n    if (ngx_http_complex_value(r, &map->value, &val) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    if (map->hostnames && val.len > 0 && val.data[val.len - 1] == '.') {\n        val.len--;\n    }\n\n    value = ngx_http_map_find(r, &map->map, &val);\n\n    if (value == NULL) {\n        value = map->default_value;\n    }\n\n    if (!value->valid) {\n        cv = (ngx_http_complex_value_t *) value->data;\n\n        if (ngx_http_complex_value(r, cv, &str) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        v->valid = 1;\n        v->no_cacheable = 0;\n        v->not_found = 0;\n        v->len = str.len;\n        v->data = str.data;\n\n    } else {\n        *v = *value;\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http map: \\\"%V\\\" \\\"%v\\\"\", &val, v);\n\n    return NGX_OK;\n}\n\n\nstatic void *\nngx_http_map_create_conf(ngx_conf_t *cf)\n{\n    ngx_http_map_conf_t  *mcf;\n\n    mcf = ngx_palloc(cf->pool, sizeof(ngx_http_map_conf_t));\n    if (mcf == NULL) {\n        return NULL;\n    }\n\n    mcf->hash_max_size = NGX_CONF_UNSET_UINT;\n    mcf->hash_bucket_size = NGX_CONF_UNSET_UINT;\n\n    return mcf;\n}\n\n\nstatic char *\nngx_http_map_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_map_conf_t  *mcf = conf;\n\n    char                              *rv;\n    ngx_str_t                         *value, name;\n    ngx_conf_t                         save;\n    ngx_pool_t                        *pool;\n    ngx_hash_init_t                    hash;\n    ngx_http_map_ctx_t                *map;\n    ngx_http_variable_t               *var;\n    ngx_http_map_conf_ctx_t            ctx;\n    ngx_http_compile_complex_value_t   ccv;\n\n    if (mcf->hash_max_size == NGX_CONF_UNSET_UINT) {\n        mcf->hash_max_size = 2048;\n    }\n\n    if (mcf->hash_bucket_size == NGX_CONF_UNSET_UINT) {\n        mcf->hash_bucket_size = ngx_cacheline_size;\n\n    } else {\n        mcf->hash_bucket_size = ngx_align(mcf->hash_bucket_size,\n                                          ngx_cacheline_size);\n    }\n\n    map = ngx_pcalloc(cf->pool, sizeof(ngx_http_map_ctx_t));\n    if (map == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    value = cf->args->elts;\n\n    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n\n    ccv.cf = cf;\n    ccv.value = &value[1];\n    ccv.complex_value = &map->value;\n\n    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    name = value[2];\n\n    if (name.data[0] != '$') {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid variable name \\\"%V\\\"\", &name);\n        return NGX_CONF_ERROR;\n    }\n\n    name.len--;\n    name.data++;\n\n    var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE);\n    if (var == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    var->get_handler = ngx_http_map_variable;\n    var->data = (uintptr_t) map;\n\n    pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, cf->log);\n    if (pool == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    ctx.keys.pool = cf->pool;\n    ctx.keys.temp_pool = pool;\n\n    if (ngx_hash_keys_array_init(&ctx.keys, NGX_HASH_LARGE) != NGX_OK) {\n        ngx_destroy_pool(pool);\n        return NGX_CONF_ERROR;\n    }\n\n    ctx.values_hash = ngx_pcalloc(pool, sizeof(ngx_array_t) * ctx.keys.hsize);\n    if (ctx.values_hash == NULL) {\n        ngx_destroy_pool(pool);\n        return NGX_CONF_ERROR;\n    }\n\n#if (NGX_PCRE)\n    if (ngx_array_init(&ctx.regexes, cf->pool, 2, sizeof(ngx_http_map_regex_t))\n        != NGX_OK)\n    {\n        ngx_destroy_pool(pool);\n        return NGX_CONF_ERROR;\n    }\n#endif\n\n    ctx.default_value = NULL;\n    ctx.cf = &save;\n    ctx.hostnames = 0;\n    ctx.no_cacheable = 0;\n\n    save = *cf;\n    cf->pool = pool;\n    cf->ctx = &ctx;\n    cf->handler = ngx_http_map;\n    cf->handler_conf = conf;\n\n    rv = ngx_conf_parse(cf, NULL);\n\n    *cf = save;\n\n    if (rv != NGX_CONF_OK) {\n        ngx_destroy_pool(pool);\n        return rv;\n    }\n\n    if (ctx.no_cacheable) {\n        var->flags |= NGX_HTTP_VAR_NOCACHEABLE;\n    }\n\n    map->default_value = ctx.default_value ? ctx.default_value:\n                                             &ngx_http_variable_null_value;\n\n    map->hostnames = ctx.hostnames;\n\n    hash.key = ngx_hash_key_lc;\n    hash.max_size = mcf->hash_max_size;\n    hash.bucket_size = mcf->hash_bucket_size;\n    hash.name = \"map_hash\";\n    hash.pool = cf->pool;\n\n    if (ctx.keys.keys.nelts) {\n        hash.hash = &map->map.hash.hash;\n        hash.temp_pool = NULL;\n\n        if (ngx_hash_init(&hash, ctx.keys.keys.elts, ctx.keys.keys.nelts)\n            != NGX_OK)\n        {\n            ngx_destroy_pool(pool);\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    if (ctx.keys.dns_wc_head.nelts) {\n\n        ngx_qsort(ctx.keys.dns_wc_head.elts,\n                  (size_t) ctx.keys.dns_wc_head.nelts,\n                  sizeof(ngx_hash_key_t), ngx_http_map_cmp_dns_wildcards);\n\n        hash.hash = NULL;\n        hash.temp_pool = pool;\n\n        if (ngx_hash_wildcard_init(&hash, ctx.keys.dns_wc_head.elts,\n                                   ctx.keys.dns_wc_head.nelts)\n            != NGX_OK)\n        {\n            ngx_destroy_pool(pool);\n            return NGX_CONF_ERROR;\n        }\n\n        map->map.hash.wc_head = (ngx_hash_wildcard_t *) hash.hash;\n    }\n\n    if (ctx.keys.dns_wc_tail.nelts) {\n\n        ngx_qsort(ctx.keys.dns_wc_tail.elts,\n                  (size_t) ctx.keys.dns_wc_tail.nelts,\n                  sizeof(ngx_hash_key_t), ngx_http_map_cmp_dns_wildcards);\n\n        hash.hash = NULL;\n        hash.temp_pool = pool;\n\n        if (ngx_hash_wildcard_init(&hash, ctx.keys.dns_wc_tail.elts,\n                                   ctx.keys.dns_wc_tail.nelts)\n            != NGX_OK)\n        {\n            ngx_destroy_pool(pool);\n            return NGX_CONF_ERROR;\n        }\n\n        map->map.hash.wc_tail = (ngx_hash_wildcard_t *) hash.hash;\n    }\n\n#if (NGX_PCRE)\n\n    if (ctx.regexes.nelts) {\n        map->map.regex = ctx.regexes.elts;\n        map->map.nregex = ctx.regexes.nelts;\n    }\n\n#endif\n\n    ngx_destroy_pool(pool);\n\n    return rv;\n}\n\n\nstatic int ngx_libc_cdecl\nngx_http_map_cmp_dns_wildcards(const void *one, const void *two)\n{\n    ngx_hash_key_t  *first, *second;\n\n    first = (ngx_hash_key_t *) one;\n    second = (ngx_hash_key_t *) two;\n\n    return ngx_dns_strcmp(first->key.data, second->key.data);\n}\n\n\nstatic char *\nngx_http_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)\n{\n    u_char                            *data;\n    size_t                             len;\n    ngx_int_t                          rv;\n    ngx_str_t                         *value, v;\n    ngx_uint_t                         i, key;\n    ngx_http_map_conf_ctx_t           *ctx;\n    ngx_http_complex_value_t           cv, *cvp;\n    ngx_http_variable_value_t         *var, **vp;\n    ngx_http_compile_complex_value_t   ccv;\n\n    ctx = cf->ctx;\n\n    value = cf->args->elts;\n\n    if (cf->args->nelts == 1\n        && ngx_strcmp(value[0].data, \"hostnames\") == 0)\n    {\n        ctx->hostnames = 1;\n        return NGX_CONF_OK;\n    }\n\n    if (cf->args->nelts == 1\n        && ngx_strcmp(value[0].data, \"volatile\") == 0)\n    {\n        ctx->no_cacheable = 1;\n        return NGX_CONF_OK;\n    }\n\n    if (cf->args->nelts != 2) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid number of the map parameters\");\n        return NGX_CONF_ERROR;\n    }\n\n    if (ngx_strcmp(value[0].data, \"include\") == 0) {\n        return ngx_conf_include(cf, dummy, conf);\n    }\n\n    key = 0;\n\n    for (i = 0; i < value[1].len; i++) {\n        key = ngx_hash(key, value[1].data[i]);\n    }\n\n    key %= ctx->keys.hsize;\n\n    vp = ctx->values_hash[key].elts;\n\n    if (vp) {\n        for (i = 0; i < ctx->values_hash[key].nelts; i++) {\n\n            if (vp[i]->valid) {\n                data = vp[i]->data;\n                len = vp[i]->len;\n\n            } else {\n                cvp = (ngx_http_complex_value_t *) vp[i]->data;\n                data = cvp->value.data;\n                len = cvp->value.len;\n            }\n\n            if (value[1].len != len) {\n                continue;\n            }\n\n            if (ngx_strncmp(value[1].data, data, len) == 0) {\n                var = vp[i];\n                goto found;\n            }\n        }\n\n    } else {\n        if (ngx_array_init(&ctx->values_hash[key], cf->pool, 4,\n                           sizeof(ngx_http_variable_value_t *))\n            != NGX_OK)\n        {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    var = ngx_palloc(ctx->keys.pool, sizeof(ngx_http_variable_value_t));\n    if (var == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    v.len = value[1].len;\n    v.data = ngx_pstrdup(ctx->keys.pool, &value[1]);\n    if (v.data == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n\n    ccv.cf = ctx->cf;\n    ccv.value = &v;\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        cvp = ngx_palloc(ctx->keys.pool, sizeof(ngx_http_complex_value_t));\n        if (cvp == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        *cvp = cv;\n\n        var->len = 0;\n        var->data = (u_char *) cvp;\n        var->valid = 0;\n\n    } else {\n        var->len = v.len;\n        var->data = v.data;\n        var->valid = 1;\n    }\n\n    var->no_cacheable = 0;\n    var->not_found = 0;\n\n    vp = ngx_array_push(&ctx->values_hash[key]);\n    if (vp == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    *vp = var;\n\nfound:\n\n    if (ngx_strcmp(value[0].data, \"default\") == 0) {\n\n        if (ctx->default_value) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"duplicate default map parameter\");\n            return NGX_CONF_ERROR;\n        }\n\n        ctx->default_value = var;\n\n        return NGX_CONF_OK;\n    }\n\n#if (NGX_PCRE)\n\n    if (value[0].len && value[0].data[0] == '~') {\n        ngx_regex_compile_t    rc;\n        ngx_http_map_regex_t  *regex;\n        u_char                 errstr[NGX_MAX_CONF_ERRSTR];\n\n        regex = ngx_array_push(&ctx->regexes);\n        if (regex == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        value[0].len--;\n        value[0].data++;\n\n        ngx_memzero(&rc, sizeof(ngx_regex_compile_t));\n\n        if (value[0].data[0] == '*') {\n            value[0].len--;\n            value[0].data++;\n            rc.options = NGX_REGEX_CASELESS;\n        }\n\n        rc.pattern = value[0];\n        rc.err.len = NGX_MAX_CONF_ERRSTR;\n        rc.err.data = errstr;\n\n        regex->regex = ngx_http_regex_compile(ctx->cf, &rc);\n        if (regex->regex == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        regex->value = var;\n\n        return NGX_CONF_OK;\n    }\n\n#endif\n\n    if (value[0].len && value[0].data[0] == '\\\\') {\n        value[0].len--;\n        value[0].data++;\n    }\n\n    rv = ngx_hash_add_key(&ctx->keys, &value[0], var,\n                          (ctx->hostnames) ? NGX_HASH_WILDCARD_KEY : 0);\n\n    if (rv == NGX_OK) {\n        return NGX_CONF_OK;\n    }\n\n    if (rv == NGX_DECLINED) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid hostname or wildcard \\\"%V\\\"\", &value[0]);\n    }\n\n    if (rv == NGX_BUSY) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"conflicting parameter \\\"%V\\\"\", &value[0]);\n    }\n\n    return NGX_CONF_ERROR;\n}\n"
  },
  {
    "path": "src/http/modules/ngx_http_memcached_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\ntypedef struct {\n    ngx_http_upstream_conf_t   upstream;\n    ngx_int_t                  index;\n    ngx_uint_t                 gzip_flag;\n} ngx_http_memcached_loc_conf_t;\n\n\ntypedef struct {\n    size_t                     rest;\n    ngx_http_request_t        *request;\n    ngx_str_t                  key;\n} ngx_http_memcached_ctx_t;\n\n\nstatic ngx_int_t ngx_http_memcached_create_request(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_memcached_reinit_request(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_memcached_process_header(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_memcached_filter_init(void *data);\nstatic ngx_int_t ngx_http_memcached_filter(void *data, ssize_t bytes);\nstatic void ngx_http_memcached_abort_request(ngx_http_request_t *r);\nstatic void ngx_http_memcached_finalize_request(ngx_http_request_t *r,\n    ngx_int_t rc);\n\nstatic void *ngx_http_memcached_create_loc_conf(ngx_conf_t *cf);\nstatic char *ngx_http_memcached_merge_loc_conf(ngx_conf_t *cf,\n    void *parent, void *child);\n\nstatic char *ngx_http_memcached_pass(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n\n\nstatic ngx_conf_bitmask_t  ngx_http_memcached_next_upstream_masks[] = {\n    { ngx_string(\"error\"), NGX_HTTP_UPSTREAM_FT_ERROR },\n    { ngx_string(\"timeout\"), NGX_HTTP_UPSTREAM_FT_TIMEOUT },\n    { ngx_string(\"invalid_response\"), NGX_HTTP_UPSTREAM_FT_INVALID_HEADER },\n    { ngx_string(\"not_found\"), NGX_HTTP_UPSTREAM_FT_HTTP_404 },\n    { ngx_string(\"off\"), NGX_HTTP_UPSTREAM_FT_OFF },\n    { ngx_null_string, 0 }\n};\n\n\nstatic ngx_command_t  ngx_http_memcached_commands[] = {\n\n    { ngx_string(\"memcached_pass\"),\n      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,\n      ngx_http_memcached_pass,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"memcached_bind\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,\n      ngx_http_upstream_bind_set_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_memcached_loc_conf_t, upstream.local),\n      NULL },\n\n    { ngx_string(\"memcached_socket_keepalive\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_memcached_loc_conf_t, upstream.socket_keepalive),\n      NULL },\n\n    { ngx_string(\"memcached_connect_timeout\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_memcached_loc_conf_t, upstream.connect_timeout),\n      NULL },\n\n    { ngx_string(\"memcached_send_timeout\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_memcached_loc_conf_t, upstream.send_timeout),\n      NULL },\n\n    { ngx_string(\"memcached_buffer_size\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_memcached_loc_conf_t, upstream.buffer_size),\n      NULL },\n\n    { ngx_string(\"memcached_read_timeout\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_memcached_loc_conf_t, upstream.read_timeout),\n      NULL },\n\n    { ngx_string(\"memcached_next_upstream\"),\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_memcached_loc_conf_t, upstream.next_upstream),\n      &ngx_http_memcached_next_upstream_masks },\n\n    { ngx_string(\"memcached_next_upstream_tries\"),\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_memcached_loc_conf_t, upstream.next_upstream_tries),\n      NULL },\n\n    { ngx_string(\"memcached_next_upstream_timeout\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_memcached_loc_conf_t, upstream.next_upstream_timeout),\n      NULL },\n\n    { ngx_string(\"memcached_gzip_flag\"),\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_memcached_loc_conf_t, gzip_flag),\n      NULL },\n\n#if (T_UPSTREAM_TRIES)\n    { ngx_string(\"memcached_upstream_tries\"),\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_memcached_loc_conf_t, upstream.next_upstream_tries),\n      NULL },\n#endif\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_memcached_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    NULL,                                  /* 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    ngx_http_memcached_create_loc_conf,    /* create location configuration */\n    ngx_http_memcached_merge_loc_conf      /* merge location configuration */\n};\n\n\nngx_module_t  ngx_http_memcached_module = {\n    NGX_MODULE_V1,\n    &ngx_http_memcached_module_ctx,        /* module context */\n    ngx_http_memcached_commands,           /* 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_str_t  ngx_http_memcached_key = ngx_string(\"memcached_key\");\n\n\n#define NGX_HTTP_MEMCACHED_END   (sizeof(ngx_http_memcached_end) - 1)\nstatic u_char  ngx_http_memcached_end[] = CRLF \"END\" CRLF;\n\n\nstatic ngx_int_t\nngx_http_memcached_handler(ngx_http_request_t *r)\n{\n    ngx_int_t                       rc;\n    ngx_http_upstream_t            *u;\n    ngx_http_memcached_ctx_t       *ctx;\n    ngx_http_memcached_loc_conf_t  *mlcf;\n\n    if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {\n        return NGX_HTTP_NOT_ALLOWED;\n    }\n\n    rc = ngx_http_discard_request_body(r);\n\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    if (ngx_http_set_content_type(r) != NGX_OK) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    if (ngx_http_upstream_create(r) != NGX_OK) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    u = r->upstream;\n\n    ngx_str_set(&u->schema, \"memcached://\");\n    u->output.tag = (ngx_buf_tag_t) &ngx_http_memcached_module;\n\n    mlcf = ngx_http_get_module_loc_conf(r, ngx_http_memcached_module);\n\n    u->conf = &mlcf->upstream;\n\n    u->create_request = ngx_http_memcached_create_request;\n    u->reinit_request = ngx_http_memcached_reinit_request;\n    u->process_header = ngx_http_memcached_process_header;\n    u->abort_request = ngx_http_memcached_abort_request;\n    u->finalize_request = ngx_http_memcached_finalize_request;\n\n    ctx = ngx_palloc(r->pool, sizeof(ngx_http_memcached_ctx_t));\n    if (ctx == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    ctx->request = r;\n\n    ngx_http_set_ctx(r, ctx, ngx_http_memcached_module);\n\n    u->input_filter_init = ngx_http_memcached_filter_init;\n    u->input_filter = ngx_http_memcached_filter;\n    u->input_filter_ctx = ctx;\n\n    r->main->count++;\n\n    ngx_http_upstream_init(r);\n\n    return NGX_DONE;\n}\n\n\nstatic ngx_int_t\nngx_http_memcached_create_request(ngx_http_request_t *r)\n{\n    size_t                          len;\n    uintptr_t                       escape;\n    ngx_buf_t                      *b;\n    ngx_chain_t                    *cl;\n    ngx_http_memcached_ctx_t       *ctx;\n    ngx_http_variable_value_t      *vv;\n    ngx_http_memcached_loc_conf_t  *mlcf;\n\n    mlcf = ngx_http_get_module_loc_conf(r, ngx_http_memcached_module);\n\n    vv = ngx_http_get_indexed_variable(r, mlcf->index);\n\n    if (vv == NULL || vv->not_found || vv->len == 0) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"the \\\"$memcached_key\\\" variable is not set\");\n        return NGX_ERROR;\n    }\n\n    escape = 2 * ngx_escape_uri(NULL, vv->data, vv->len, NGX_ESCAPE_MEMCACHED);\n\n    len = sizeof(\"get \") - 1 + vv->len + escape + sizeof(CRLF) - 1;\n\n    b = ngx_create_temp_buf(r->pool, len);\n    if (b == NULL) {\n        return NGX_ERROR;\n    }\n\n    cl = ngx_alloc_chain_link(r->pool);\n    if (cl == NULL) {\n        return NGX_ERROR;\n    }\n\n    cl->buf = b;\n    cl->next = NULL;\n\n    r->upstream->request_bufs = cl;\n\n    *b->last++ = 'g'; *b->last++ = 'e'; *b->last++ = 't'; *b->last++ = ' ';\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_memcached_module);\n\n    ctx->key.data = b->last;\n\n    if (escape == 0) {\n        b->last = ngx_copy(b->last, vv->data, vv->len);\n\n    } else {\n        b->last = (u_char *) ngx_escape_uri(b->last, vv->data, vv->len,\n                                            NGX_ESCAPE_MEMCACHED);\n    }\n\n    ctx->key.len = b->last - ctx->key.data;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http memcached request: \\\"%V\\\"\", &ctx->key);\n\n    *b->last++ = CR; *b->last++ = LF;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_memcached_reinit_request(ngx_http_request_t *r)\n{\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_memcached_process_header(ngx_http_request_t *r)\n{\n    u_char                         *p, *start;\n    ngx_str_t                       line;\n    ngx_uint_t                      flags;\n    ngx_table_elt_t                *h;\n    ngx_http_upstream_t            *u;\n    ngx_http_memcached_ctx_t       *ctx;\n    ngx_http_memcached_loc_conf_t  *mlcf;\n\n    u = r->upstream;\n\n    for (p = u->buffer.pos; p < u->buffer.last; p++) {\n        if (*p == LF) {\n            goto found;\n        }\n    }\n\n    return NGX_AGAIN;\n\nfound:\n\n    line.data = u->buffer.pos;\n    line.len = p - u->buffer.pos;\n\n    if (line.len == 0 || *(p - 1) != CR) {\n        goto no_valid;\n    }\n\n    *p = '\\0';\n    line.len--;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"memcached: \\\"%V\\\"\", &line);\n\n    p = u->buffer.pos;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_memcached_module);\n    mlcf = ngx_http_get_module_loc_conf(r, ngx_http_memcached_module);\n\n    if (ngx_strncmp(p, \"VALUE \", sizeof(\"VALUE \") - 1) == 0) {\n\n        p += sizeof(\"VALUE \") - 1;\n\n        if (ngx_strncmp(p, ctx->key.data, ctx->key.len) != 0) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"memcached sent invalid key in response \\\"%V\\\" \"\n                          \"for key \\\"%V\\\"\",\n                          &line, &ctx->key);\n\n            return NGX_HTTP_UPSTREAM_INVALID_HEADER;\n        }\n\n        p += ctx->key.len;\n\n        if (*p++ != ' ') {\n            goto no_valid;\n        }\n\n        /* flags */\n\n        start = p;\n\n        while (*p) {\n            if (*p++ == ' ') {\n                if (mlcf->gzip_flag) {\n                    goto flags;\n                } else {\n                    goto length;\n                }\n            }\n        }\n\n        goto no_valid;\n\n    flags:\n\n        flags = ngx_atoi(start, p - start - 1);\n\n        if (flags == (ngx_uint_t) NGX_ERROR) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"memcached sent invalid flags in response \\\"%V\\\" \"\n                          \"for key \\\"%V\\\"\",\n                          &line, &ctx->key);\n            return NGX_HTTP_UPSTREAM_INVALID_HEADER;\n        }\n\n        if (flags & mlcf->gzip_flag) {\n            h = ngx_list_push(&r->headers_out.headers);\n            if (h == NULL) {\n                return NGX_ERROR;\n            }\n\n            h->hash = 1;\n            h->next = NULL;\n            ngx_str_set(&h->key, \"Content-Encoding\");\n            ngx_str_set(&h->value, \"gzip\");\n            r->headers_out.content_encoding = h;\n        }\n\n    length:\n\n        start = p;\n        p = line.data + line.len;\n\n        u->headers_in.content_length_n = ngx_atoof(start, p - start);\n        if (u->headers_in.content_length_n == NGX_ERROR) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"memcached sent invalid length in response \\\"%V\\\" \"\n                          \"for key \\\"%V\\\"\",\n                          &line, &ctx->key);\n            return NGX_HTTP_UPSTREAM_INVALID_HEADER;\n        }\n\n        u->headers_in.status_n = 200;\n        u->state->status = 200;\n        u->buffer.pos = p + sizeof(CRLF) - 1;\n\n        return NGX_OK;\n    }\n\n    if (ngx_strcmp(p, \"END\\x0d\") == 0) {\n        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                      \"key: \\\"%V\\\" was not found by memcached\", &ctx->key);\n\n        u->headers_in.content_length_n = 0;\n        u->headers_in.status_n = 404;\n        u->state->status = 404;\n        u->buffer.pos = p + sizeof(\"END\" CRLF) - 1;\n        u->keepalive = 1;\n\n        return NGX_OK;\n    }\n\nno_valid:\n\n    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                  \"memcached sent invalid response: \\\"%V\\\"\", &line);\n\n    return NGX_HTTP_UPSTREAM_INVALID_HEADER;\n}\n\n\nstatic ngx_int_t\nngx_http_memcached_filter_init(void *data)\n{\n    ngx_http_memcached_ctx_t  *ctx = data;\n\n    ngx_http_upstream_t  *u;\n\n    u = ctx->request->upstream;\n\n    if (u->headers_in.status_n != 404) {\n        u->length = u->headers_in.content_length_n + NGX_HTTP_MEMCACHED_END;\n        ctx->rest = NGX_HTTP_MEMCACHED_END;\n\n    } else {\n        u->length = 0;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_memcached_filter(void *data, ssize_t bytes)\n{\n    ngx_http_memcached_ctx_t  *ctx = data;\n\n    u_char               *last;\n    ngx_buf_t            *b;\n    ngx_chain_t          *cl, **ll;\n    ngx_http_upstream_t  *u;\n\n    u = ctx->request->upstream;\n    b = &u->buffer;\n\n    if (u->length == (ssize_t) ctx->rest) {\n\n        if (bytes > u->length\n            || ngx_strncmp(b->last,\n                   ngx_http_memcached_end + NGX_HTTP_MEMCACHED_END - ctx->rest,\n                   bytes)\n               != 0)\n        {\n            ngx_log_error(NGX_LOG_ERR, ctx->request->connection->log, 0,\n                          \"memcached sent invalid trailer\");\n\n            u->length = 0;\n            ctx->rest = 0;\n\n            return NGX_OK;\n        }\n\n        u->length -= bytes;\n        ctx->rest -= bytes;\n\n        if (u->length == 0) {\n            u->keepalive = 1;\n        }\n\n        return NGX_OK;\n    }\n\n    for (cl = u->out_bufs, ll = &u->out_bufs; cl; cl = cl->next) {\n        ll = &cl->next;\n    }\n\n    cl = ngx_chain_get_free_buf(ctx->request->pool, &u->free_bufs);\n    if (cl == NULL) {\n        return NGX_ERROR;\n    }\n\n    cl->buf->flush = 1;\n    cl->buf->memory = 1;\n\n    *ll = cl;\n\n    last = b->last;\n    cl->buf->pos = last;\n    b->last += bytes;\n    cl->buf->last = b->last;\n    cl->buf->tag = u->output.tag;\n\n    ngx_log_debug4(NGX_LOG_DEBUG_HTTP, ctx->request->connection->log, 0,\n                   \"memcached filter bytes:%z size:%z length:%O rest:%z\",\n                   bytes, b->last - b->pos, u->length, ctx->rest);\n\n    if (bytes <= (ssize_t) (u->length - NGX_HTTP_MEMCACHED_END)) {\n        u->length -= bytes;\n        return NGX_OK;\n    }\n\n    last += (size_t) (u->length - NGX_HTTP_MEMCACHED_END);\n\n    if (bytes > u->length\n        || ngx_strncmp(last, ngx_http_memcached_end, b->last - last) != 0)\n    {\n        ngx_log_error(NGX_LOG_ERR, ctx->request->connection->log, 0,\n                      \"memcached sent invalid trailer\");\n\n        b->last = last;\n        cl->buf->last = last;\n        u->length = 0;\n        ctx->rest = 0;\n\n        return NGX_OK;\n    }\n\n    ctx->rest -= b->last - last;\n    b->last = last;\n    cl->buf->last = last;\n    u->length = ctx->rest;\n\n    if (u->length == 0) {\n        u->keepalive = 1;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_memcached_abort_request(ngx_http_request_t *r)\n{\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"abort http memcached request\");\n    return;\n}\n\n\nstatic void\nngx_http_memcached_finalize_request(ngx_http_request_t *r, ngx_int_t rc)\n{\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"finalize http memcached request\");\n    return;\n}\n\n\nstatic void *\nngx_http_memcached_create_loc_conf(ngx_conf_t *cf)\n{\n    ngx_http_memcached_loc_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_memcached_loc_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc():\n     *\n     *     conf->upstream.bufs.num = 0;\n     *     conf->upstream.next_upstream = 0;\n     *     conf->upstream.temp_path = NULL;\n     */\n\n    conf->upstream.local = NGX_CONF_UNSET_PTR;\n    conf->upstream.socket_keepalive = NGX_CONF_UNSET;\n    conf->upstream.next_upstream_tries = NGX_CONF_UNSET_UINT;\n    conf->upstream.connect_timeout = NGX_CONF_UNSET_MSEC;\n    conf->upstream.send_timeout = NGX_CONF_UNSET_MSEC;\n    conf->upstream.read_timeout = NGX_CONF_UNSET_MSEC;\n    conf->upstream.next_upstream_timeout = NGX_CONF_UNSET_MSEC;\n\n    conf->upstream.buffer_size = NGX_CONF_UNSET_SIZE;\n\n    /* the hardcoded values */\n    conf->upstream.cyclic_temp_file = 0;\n    conf->upstream.buffering = 0;\n    conf->upstream.ignore_client_abort = 0;\n    conf->upstream.send_lowat = 0;\n    conf->upstream.bufs.num = 0;\n    conf->upstream.busy_buffers_size = 0;\n    conf->upstream.max_temp_file_size = 0;\n    conf->upstream.temp_file_write_size = 0;\n    conf->upstream.intercept_errors = 1;\n    conf->upstream.intercept_404 = 1;\n    conf->upstream.pass_request_headers = 0;\n    conf->upstream.pass_request_body = 0;\n    conf->upstream.force_ranges = 1;\n\n    conf->index = NGX_CONF_UNSET;\n    conf->gzip_flag = NGX_CONF_UNSET_UINT;\n\n    return conf;\n}\n\n\nstatic char *\nngx_http_memcached_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_http_memcached_loc_conf_t *prev = parent;\n    ngx_http_memcached_loc_conf_t *conf = child;\n\n    ngx_conf_merge_ptr_value(conf->upstream.local,\n                              prev->upstream.local, NULL);\n\n    ngx_conf_merge_value(conf->upstream.socket_keepalive,\n                              prev->upstream.socket_keepalive, 0);\n\n    ngx_conf_merge_uint_value(conf->upstream.next_upstream_tries,\n                              prev->upstream.next_upstream_tries, 0);\n\n    ngx_conf_merge_msec_value(conf->upstream.connect_timeout,\n                              prev->upstream.connect_timeout, 60000);\n\n    ngx_conf_merge_msec_value(conf->upstream.send_timeout,\n                              prev->upstream.send_timeout, 60000);\n\n    ngx_conf_merge_msec_value(conf->upstream.read_timeout,\n                              prev->upstream.read_timeout, 60000);\n\n    ngx_conf_merge_msec_value(conf->upstream.next_upstream_timeout,\n                              prev->upstream.next_upstream_timeout, 0);\n\n    ngx_conf_merge_size_value(conf->upstream.buffer_size,\n                              prev->upstream.buffer_size,\n                              (size_t) ngx_pagesize);\n\n    ngx_conf_merge_bitmask_value(conf->upstream.next_upstream,\n                              prev->upstream.next_upstream,\n                              (NGX_CONF_BITMASK_SET\n                               |NGX_HTTP_UPSTREAM_FT_ERROR\n                               |NGX_HTTP_UPSTREAM_FT_TIMEOUT));\n\n    if (conf->upstream.next_upstream & NGX_HTTP_UPSTREAM_FT_OFF) {\n        conf->upstream.next_upstream = NGX_CONF_BITMASK_SET\n                                       |NGX_HTTP_UPSTREAM_FT_OFF;\n    }\n\n    if (conf->upstream.upstream == NULL) {\n        conf->upstream.upstream = prev->upstream.upstream;\n    }\n\n    if (conf->index == NGX_CONF_UNSET) {\n        conf->index = prev->index;\n    }\n\n    ngx_conf_merge_uint_value(conf->gzip_flag, prev->gzip_flag, 0);\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_memcached_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_memcached_loc_conf_t *mlcf = conf;\n\n    ngx_str_t                 *value;\n    ngx_url_t                  u;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    if (mlcf->upstream.upstream) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    ngx_memzero(&u, sizeof(ngx_url_t));\n\n    u.url = value[1];\n    u.no_resolve = 1;\n\n    mlcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0);\n    if (mlcf->upstream.upstream == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);\n\n    clcf->handler = ngx_http_memcached_handler;\n\n    if (clcf->name.len && clcf->name.data[clcf->name.len - 1] == '/') {\n        clcf->auto_redirect = 1;\n    }\n\n    mlcf->index = ngx_http_get_variable_index(cf, &ngx_http_memcached_key);\n\n    if (mlcf->index == NGX_ERROR) {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n"
  },
  {
    "path": "src/http/modules/ngx_http_mirror_module.c",
    "content": "\n/*\n * Copyright (C) Roman Arutyunyan\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\ntypedef struct {\n    ngx_array_t  *mirror;\n    ngx_flag_t    request_body;\n} ngx_http_mirror_loc_conf_t;\n\n\ntypedef struct {\n    ngx_int_t     status;\n} ngx_http_mirror_ctx_t;\n\n\nstatic ngx_int_t ngx_http_mirror_handler(ngx_http_request_t *r);\nstatic void ngx_http_mirror_body_handler(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_mirror_handler_internal(ngx_http_request_t *r);\nstatic void *ngx_http_mirror_create_loc_conf(ngx_conf_t *cf);\nstatic char *ngx_http_mirror_merge_loc_conf(ngx_conf_t *cf, void *parent,\n    void *child);\nstatic char *ngx_http_mirror(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\nstatic ngx_int_t ngx_http_mirror_init(ngx_conf_t *cf);\n\n\nstatic ngx_command_t  ngx_http_mirror_commands[] = {\n\n    { ngx_string(\"mirror\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_http_mirror,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"mirror_request_body\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_mirror_loc_conf_t, request_body),\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_mirror_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    ngx_http_mirror_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    ngx_http_mirror_create_loc_conf,       /* create location configuration */\n    ngx_http_mirror_merge_loc_conf         /* merge location configuration */\n};\n\n\nngx_module_t  ngx_http_mirror_module = {\n    NGX_MODULE_V1,\n    &ngx_http_mirror_module_ctx,           /* module context */\n    ngx_http_mirror_commands,              /* 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_mirror_handler(ngx_http_request_t *r)\n{\n    ngx_int_t                    rc;\n    ngx_http_mirror_ctx_t       *ctx;\n    ngx_http_mirror_loc_conf_t  *mlcf;\n\n    if (r != r->main) {\n        return NGX_DECLINED;\n    }\n\n    mlcf = ngx_http_get_module_loc_conf(r, ngx_http_mirror_module);\n\n    if (mlcf->mirror == NULL) {\n        return NGX_DECLINED;\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, \"mirror handler\");\n\n    if (mlcf->request_body) {\n        ctx = ngx_http_get_module_ctx(r, ngx_http_mirror_module);\n\n        if (ctx) {\n            return ctx->status;\n        }\n\n        ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_mirror_ctx_t));\n        if (ctx == NULL) {\n            return NGX_ERROR;\n        }\n\n        ctx->status = NGX_DONE;\n\n        ngx_http_set_ctx(r, ctx, ngx_http_mirror_module);\n\n        rc = ngx_http_read_client_request_body(r, ngx_http_mirror_body_handler);\n        if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {\n            return rc;\n        }\n\n        ngx_http_finalize_request(r, NGX_DONE);\n        return NGX_DONE;\n    }\n\n    return ngx_http_mirror_handler_internal(r);\n}\n\n\nstatic void\nngx_http_mirror_body_handler(ngx_http_request_t *r)\n{\n    ngx_http_mirror_ctx_t  *ctx;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_mirror_module);\n\n    ctx->status = ngx_http_mirror_handler_internal(r);\n\n    r->preserve_body = 1;\n\n    r->write_event_handler = ngx_http_core_run_phases;\n    ngx_http_core_run_phases(r);\n}\n\n\nstatic ngx_int_t\nngx_http_mirror_handler_internal(ngx_http_request_t *r)\n{\n    ngx_str_t                   *name;\n    ngx_uint_t                   i;\n    ngx_http_request_t          *sr;\n    ngx_http_mirror_loc_conf_t  *mlcf;\n\n    mlcf = ngx_http_get_module_loc_conf(r, ngx_http_mirror_module);\n\n    name = mlcf->mirror->elts;\n\n    for (i = 0; i < mlcf->mirror->nelts; i++) {\n        if (ngx_http_subrequest(r, &name[i], &r->args, &sr, NULL,\n                                NGX_HTTP_SUBREQUEST_BACKGROUND)\n            != NGX_OK)\n        {\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n\n        sr->header_only = 1;\n        sr->method = r->method;\n        sr->method_name = r->method_name;\n    }\n\n    return NGX_DECLINED;\n}\n\n\nstatic void *\nngx_http_mirror_create_loc_conf(ngx_conf_t *cf)\n{\n    ngx_http_mirror_loc_conf_t  *mlcf;\n\n    mlcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_mirror_loc_conf_t));\n    if (mlcf == NULL) {\n        return NULL;\n    }\n\n    mlcf->mirror = NGX_CONF_UNSET_PTR;\n    mlcf->request_body = NGX_CONF_UNSET;\n\n    return mlcf;\n}\n\n\nstatic char *\nngx_http_mirror_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_http_mirror_loc_conf_t *prev = parent;\n    ngx_http_mirror_loc_conf_t *conf = child;\n\n    ngx_conf_merge_ptr_value(conf->mirror, prev->mirror, NULL);\n    ngx_conf_merge_value(conf->request_body, prev->request_body, 1);\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_mirror(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_mirror_loc_conf_t *mlcf = conf;\n\n    ngx_str_t  *value, *s;\n\n    value = cf->args->elts;\n\n    if (ngx_strcmp(value[1].data, \"off\") == 0) {\n        if (mlcf->mirror != NGX_CONF_UNSET_PTR) {\n            return \"is duplicate\";\n        }\n\n        mlcf->mirror = NULL;\n        return NGX_CONF_OK;\n    }\n\n    if (mlcf->mirror == NULL) {\n        return \"is duplicate\";\n    }\n\n    if (mlcf->mirror == NGX_CONF_UNSET_PTR) {\n        mlcf->mirror = ngx_array_create(cf->pool, 4, sizeof(ngx_str_t));\n        if (mlcf->mirror == NULL) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    s = ngx_array_push(mlcf->mirror);\n    if (s == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    *s = value[1];\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_mirror_init(ngx_conf_t *cf)\n{\n    ngx_http_handler_pt        *h;\n    ngx_http_core_main_conf_t  *cmcf;\n\n    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);\n\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_mirror_handler;\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "src/http/modules/ngx_http_mp4_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\n#define NGX_HTTP_MP4_TRAK_ATOM     0\n#define NGX_HTTP_MP4_TKHD_ATOM     1\n#define NGX_HTTP_MP4_EDTS_ATOM     2\n#define NGX_HTTP_MP4_ELST_ATOM     3\n#define NGX_HTTP_MP4_MDIA_ATOM     4\n#define NGX_HTTP_MP4_MDHD_ATOM     5\n#define NGX_HTTP_MP4_HDLR_ATOM     6\n#define NGX_HTTP_MP4_MINF_ATOM     7\n#define NGX_HTTP_MP4_VMHD_ATOM     8\n#define NGX_HTTP_MP4_SMHD_ATOM     9\n#define NGX_HTTP_MP4_DINF_ATOM    10\n#define NGX_HTTP_MP4_STBL_ATOM    11\n#define NGX_HTTP_MP4_STSD_ATOM    12\n#define NGX_HTTP_MP4_STTS_ATOM    13\n#define NGX_HTTP_MP4_STTS_DATA    14\n#define NGX_HTTP_MP4_STSS_ATOM    15\n#define NGX_HTTP_MP4_STSS_DATA    16\n#define NGX_HTTP_MP4_CTTS_ATOM    17\n#define NGX_HTTP_MP4_CTTS_DATA    18\n#define NGX_HTTP_MP4_STSC_ATOM    19\n#define NGX_HTTP_MP4_STSC_START   20\n#define NGX_HTTP_MP4_STSC_DATA    21\n#define NGX_HTTP_MP4_STSC_END     22\n#define NGX_HTTP_MP4_STSZ_ATOM    23\n#define NGX_HTTP_MP4_STSZ_DATA    24\n#define NGX_HTTP_MP4_STCO_ATOM    25\n#define NGX_HTTP_MP4_STCO_DATA    26\n#define NGX_HTTP_MP4_CO64_ATOM    27\n#define NGX_HTTP_MP4_CO64_DATA    28\n\n#define NGX_HTTP_MP4_LAST_ATOM    NGX_HTTP_MP4_CO64_DATA\n\n\ntypedef struct {\n    size_t                buffer_size;\n    size_t                max_buffer_size;\n    ngx_flag_t            start_key_frame;\n} ngx_http_mp4_conf_t;\n\n\ntypedef struct {\n    u_char                chunk[4];\n    u_char                samples[4];\n    u_char                id[4];\n} ngx_mp4_stsc_entry_t;\n\n\ntypedef struct {\n    u_char                size[4];\n    u_char                name[4];\n} ngx_mp4_edts_atom_t;\n\n\ntypedef struct {\n    u_char                size[4];\n    u_char                name[4];\n    u_char                version[1];\n    u_char                flags[3];\n    u_char                entries[4];\n    u_char                duration[8];\n    u_char                media_time[8];\n    u_char                media_rate[2];\n    u_char                reserved[2];\n} ngx_mp4_elst_atom_t;\n\n\ntypedef struct {\n    uint32_t              timescale;\n    uint32_t              time_to_sample_entries;\n    uint32_t              sample_to_chunk_entries;\n    uint32_t              sync_samples_entries;\n    uint32_t              composition_offset_entries;\n    uint32_t              sample_sizes_entries;\n    uint32_t              chunks;\n\n    ngx_uint_t            start_sample;\n    ngx_uint_t            end_sample;\n    ngx_uint_t            start_chunk;\n    ngx_uint_t            end_chunk;\n    ngx_uint_t            start_chunk_samples;\n    ngx_uint_t            end_chunk_samples;\n    uint64_t              start_chunk_samples_size;\n    uint64_t              end_chunk_samples_size;\n    uint64_t              duration;\n    uint64_t              prefix;\n    uint64_t              movie_duration;\n    off_t                 start_offset;\n    off_t                 end_offset;\n\n    size_t                tkhd_size;\n    size_t                mdhd_size;\n    size_t                hdlr_size;\n    size_t                vmhd_size;\n    size_t                smhd_size;\n    size_t                dinf_size;\n    size_t                size;\n\n    ngx_chain_t           out[NGX_HTTP_MP4_LAST_ATOM + 1];\n\n    ngx_buf_t             trak_atom_buf;\n    ngx_buf_t             tkhd_atom_buf;\n    ngx_buf_t             edts_atom_buf;\n    ngx_buf_t             elst_atom_buf;\n    ngx_buf_t             mdia_atom_buf;\n    ngx_buf_t             mdhd_atom_buf;\n    ngx_buf_t             hdlr_atom_buf;\n    ngx_buf_t             minf_atom_buf;\n    ngx_buf_t             vmhd_atom_buf;\n    ngx_buf_t             smhd_atom_buf;\n    ngx_buf_t             dinf_atom_buf;\n    ngx_buf_t             stbl_atom_buf;\n    ngx_buf_t             stsd_atom_buf;\n    ngx_buf_t             stts_atom_buf;\n    ngx_buf_t             stts_data_buf;\n    ngx_buf_t             stss_atom_buf;\n    ngx_buf_t             stss_data_buf;\n    ngx_buf_t             ctts_atom_buf;\n    ngx_buf_t             ctts_data_buf;\n    ngx_buf_t             stsc_atom_buf;\n    ngx_buf_t             stsc_start_chunk_buf;\n    ngx_buf_t             stsc_end_chunk_buf;\n    ngx_buf_t             stsc_data_buf;\n    ngx_buf_t             stsz_atom_buf;\n    ngx_buf_t             stsz_data_buf;\n    ngx_buf_t             stco_atom_buf;\n    ngx_buf_t             stco_data_buf;\n    ngx_buf_t             co64_atom_buf;\n    ngx_buf_t             co64_data_buf;\n\n    ngx_mp4_edts_atom_t   edts_atom;\n    ngx_mp4_elst_atom_t   elst_atom;\n    ngx_mp4_stsc_entry_t  stsc_start_chunk_entry;\n    ngx_mp4_stsc_entry_t  stsc_end_chunk_entry;\n} ngx_http_mp4_trak_t;\n\n\ntypedef struct {\n    ngx_file_t            file;\n\n    u_char               *buffer;\n    u_char               *buffer_start;\n    u_char               *buffer_pos;\n    u_char               *buffer_end;\n    size_t                buffer_size;\n\n    off_t                 offset;\n    off_t                 end;\n    off_t                 content_length;\n    ngx_uint_t            start;\n    ngx_uint_t            length;\n    uint32_t              timescale;\n    ngx_http_request_t   *request;\n    ngx_array_t           trak;\n    ngx_http_mp4_trak_t   traks[2];\n\n    size_t                ftyp_size;\n    size_t                moov_size;\n\n    ngx_chain_t          *out;\n    ngx_chain_t           ftyp_atom;\n    ngx_chain_t           moov_atom;\n    ngx_chain_t           mvhd_atom;\n    ngx_chain_t           mdat_atom;\n    ngx_chain_t           mdat_data;\n\n    ngx_buf_t             ftyp_atom_buf;\n    ngx_buf_t             moov_atom_buf;\n    ngx_buf_t             mvhd_atom_buf;\n    ngx_buf_t             mdat_atom_buf;\n    ngx_buf_t             mdat_data_buf;\n\n    u_char                moov_atom_header[8];\n    u_char                mdat_atom_header[16];\n} ngx_http_mp4_file_t;\n\n\ntypedef struct {\n    char                 *name;\n    ngx_int_t           (*handler)(ngx_http_mp4_file_t *mp4,\n                                   uint64_t atom_data_size);\n} ngx_http_mp4_atom_handler_t;\n\n\n#define ngx_mp4_atom_header(mp4)   (mp4->buffer_pos - 8)\n#define ngx_mp4_atom_data(mp4)     mp4->buffer_pos\n#define ngx_mp4_atom_data_size(t)  (uint64_t) (sizeof(t) - 8)\n\n\n#define ngx_mp4_atom_next(mp4, n)                                             \\\n                                                                              \\\n    if (n > (size_t) (mp4->buffer_end - mp4->buffer_pos)) {                   \\\n        mp4->buffer_pos = mp4->buffer_end;                                    \\\n                                                                              \\\n    } else {                                                                  \\\n        mp4->buffer_pos += (size_t) n;                                        \\\n    }                                                                         \\\n                                                                              \\\n    mp4->offset += n\n\n\n#define ngx_mp4_set_atom_name(p, n1, n2, n3, n4)                              \\\n    ((u_char *) (p))[4] = n1;                                                 \\\n    ((u_char *) (p))[5] = n2;                                                 \\\n    ((u_char *) (p))[6] = n3;                                                 \\\n    ((u_char *) (p))[7] = n4\n\n#define ngx_mp4_get_16value(p)                                                \\\n    ( ((uint16_t) ((u_char *) (p))[0] << 8)                                   \\\n    + (           ((u_char *) (p))[1]) )\n\n#define ngx_mp4_set_16value(p, n)                                             \\\n    ((u_char *) (p))[0] = (u_char) ((n) >> 8);                                \\\n    ((u_char *) (p))[1] = (u_char)  (n)\n\n#define ngx_mp4_get_32value(p)                                                \\\n    ( ((uint32_t) ((u_char *) (p))[0] << 24)                                  \\\n    + (           ((u_char *) (p))[1] << 16)                                  \\\n    + (           ((u_char *) (p))[2] << 8)                                   \\\n    + (           ((u_char *) (p))[3]) )\n\n#define ngx_mp4_set_32value(p, n)                                             \\\n    ((u_char *) (p))[0] = (u_char) ((n) >> 24);                               \\\n    ((u_char *) (p))[1] = (u_char) ((n) >> 16);                               \\\n    ((u_char *) (p))[2] = (u_char) ((n) >> 8);                                \\\n    ((u_char *) (p))[3] = (u_char)  (n)\n\n#define ngx_mp4_get_64value(p)                                                \\\n    ( ((uint64_t) ((u_char *) (p))[0] << 56)                                  \\\n    + ((uint64_t) ((u_char *) (p))[1] << 48)                                  \\\n    + ((uint64_t) ((u_char *) (p))[2] << 40)                                  \\\n    + ((uint64_t) ((u_char *) (p))[3] << 32)                                  \\\n    + ((uint64_t) ((u_char *) (p))[4] << 24)                                  \\\n    + (           ((u_char *) (p))[5] << 16)                                  \\\n    + (           ((u_char *) (p))[6] << 8)                                   \\\n    + (           ((u_char *) (p))[7]) )\n\n#define ngx_mp4_set_64value(p, n)                                             \\\n    ((u_char *) (p))[0] = (u_char) ((uint64_t) (n) >> 56);                    \\\n    ((u_char *) (p))[1] = (u_char) ((uint64_t) (n) >> 48);                    \\\n    ((u_char *) (p))[2] = (u_char) ((uint64_t) (n) >> 40);                    \\\n    ((u_char *) (p))[3] = (u_char) ((uint64_t) (n) >> 32);                    \\\n    ((u_char *) (p))[4] = (u_char) (           (n) >> 24);                    \\\n    ((u_char *) (p))[5] = (u_char) (           (n) >> 16);                    \\\n    ((u_char *) (p))[6] = (u_char) (           (n) >> 8);                     \\\n    ((u_char *) (p))[7] = (u_char)             (n)\n\n#define ngx_mp4_last_trak(mp4)                                                \\\n    &((ngx_http_mp4_trak_t *) mp4->trak.elts)[mp4->trak.nelts - 1]\n\n\nstatic ngx_int_t ngx_http_mp4_handler(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_mp4_atofp(u_char *line, size_t n, size_t point);\n\nstatic ngx_int_t ngx_http_mp4_process(ngx_http_mp4_file_t *mp4);\nstatic ngx_int_t ngx_http_mp4_read_atom(ngx_http_mp4_file_t *mp4,\n    ngx_http_mp4_atom_handler_t *atom, uint64_t atom_data_size);\nstatic ngx_int_t ngx_http_mp4_read(ngx_http_mp4_file_t *mp4, size_t size);\nstatic ngx_int_t ngx_http_mp4_read_ftyp_atom(ngx_http_mp4_file_t *mp4,\n    uint64_t atom_data_size);\nstatic ngx_int_t ngx_http_mp4_read_moov_atom(ngx_http_mp4_file_t *mp4,\n    uint64_t atom_data_size);\nstatic ngx_int_t ngx_http_mp4_read_mdat_atom(ngx_http_mp4_file_t *mp4,\n    uint64_t atom_data_size);\nstatic size_t ngx_http_mp4_update_mdat_atom(ngx_http_mp4_file_t *mp4,\n    off_t start_offset, off_t end_offset);\nstatic ngx_int_t ngx_http_mp4_read_mvhd_atom(ngx_http_mp4_file_t *mp4,\n    uint64_t atom_data_size);\nstatic ngx_int_t ngx_http_mp4_read_trak_atom(ngx_http_mp4_file_t *mp4,\n    uint64_t atom_data_size);\nstatic void ngx_http_mp4_update_trak_atom(ngx_http_mp4_file_t *mp4,\n    ngx_http_mp4_trak_t *trak);\nstatic ngx_int_t ngx_http_mp4_read_cmov_atom(ngx_http_mp4_file_t *mp4,\n    uint64_t atom_data_size);\nstatic ngx_int_t ngx_http_mp4_read_tkhd_atom(ngx_http_mp4_file_t *mp4,\n    uint64_t atom_data_size);\nstatic ngx_int_t ngx_http_mp4_read_mdia_atom(ngx_http_mp4_file_t *mp4,\n    uint64_t atom_data_size);\nstatic void ngx_http_mp4_update_mdia_atom(ngx_http_mp4_file_t *mp4,\n    ngx_http_mp4_trak_t *trak);\nstatic ngx_int_t ngx_http_mp4_read_mdhd_atom(ngx_http_mp4_file_t *mp4,\n    uint64_t atom_data_size);\nstatic void ngx_http_mp4_update_mdhd_atom(ngx_http_mp4_file_t *mp4,\n    ngx_http_mp4_trak_t *trak);\nstatic ngx_int_t ngx_http_mp4_read_hdlr_atom(ngx_http_mp4_file_t *mp4,\n    uint64_t atom_data_size);\nstatic ngx_int_t ngx_http_mp4_read_minf_atom(ngx_http_mp4_file_t *mp4,\n    uint64_t atom_data_size);\nstatic void ngx_http_mp4_update_minf_atom(ngx_http_mp4_file_t *mp4,\n    ngx_http_mp4_trak_t *trak);\nstatic ngx_int_t ngx_http_mp4_read_dinf_atom(ngx_http_mp4_file_t *mp4,\n    uint64_t atom_data_size);\nstatic ngx_int_t ngx_http_mp4_read_vmhd_atom(ngx_http_mp4_file_t *mp4,\n    uint64_t atom_data_size);\nstatic ngx_int_t ngx_http_mp4_read_smhd_atom(ngx_http_mp4_file_t *mp4,\n    uint64_t atom_data_size);\nstatic ngx_int_t ngx_http_mp4_read_stbl_atom(ngx_http_mp4_file_t *mp4,\n    uint64_t atom_data_size);\nstatic void ngx_http_mp4_update_edts_atom(ngx_http_mp4_file_t *mp4,\n    ngx_http_mp4_trak_t *trak);\nstatic void ngx_http_mp4_update_stbl_atom(ngx_http_mp4_file_t *mp4,\n    ngx_http_mp4_trak_t *trak);\nstatic ngx_int_t ngx_http_mp4_read_stsd_atom(ngx_http_mp4_file_t *mp4,\n    uint64_t atom_data_size);\nstatic ngx_int_t ngx_http_mp4_read_stts_atom(ngx_http_mp4_file_t *mp4,\n    uint64_t atom_data_size);\nstatic ngx_int_t ngx_http_mp4_update_stts_atom(ngx_http_mp4_file_t *mp4,\n    ngx_http_mp4_trak_t *trak);\nstatic ngx_int_t ngx_http_mp4_crop_stts_data(ngx_http_mp4_file_t *mp4,\n    ngx_http_mp4_trak_t *trak, ngx_uint_t start);\nstatic uint32_t ngx_http_mp4_seek_key_frame(ngx_http_mp4_file_t *mp4,\n    ngx_http_mp4_trak_t *trak, uint32_t start_sample);\nstatic ngx_int_t ngx_http_mp4_read_stss_atom(ngx_http_mp4_file_t *mp4,\n    uint64_t atom_data_size);\nstatic ngx_int_t ngx_http_mp4_update_stss_atom(ngx_http_mp4_file_t *mp4,\n    ngx_http_mp4_trak_t *trak);\nstatic void ngx_http_mp4_crop_stss_data(ngx_http_mp4_file_t *mp4,\n    ngx_http_mp4_trak_t *trak, ngx_uint_t start);\nstatic ngx_int_t ngx_http_mp4_read_ctts_atom(ngx_http_mp4_file_t *mp4,\n    uint64_t atom_data_size);\nstatic void ngx_http_mp4_update_ctts_atom(ngx_http_mp4_file_t *mp4,\n    ngx_http_mp4_trak_t *trak);\nstatic void ngx_http_mp4_crop_ctts_data(ngx_http_mp4_file_t *mp4,\n    ngx_http_mp4_trak_t *trak, ngx_uint_t start);\nstatic ngx_int_t ngx_http_mp4_read_stsc_atom(ngx_http_mp4_file_t *mp4,\n    uint64_t atom_data_size);\nstatic ngx_int_t ngx_http_mp4_update_stsc_atom(ngx_http_mp4_file_t *mp4,\n    ngx_http_mp4_trak_t *trak);\nstatic ngx_int_t ngx_http_mp4_crop_stsc_data(ngx_http_mp4_file_t *mp4,\n    ngx_http_mp4_trak_t *trak, ngx_uint_t start);\nstatic ngx_int_t ngx_http_mp4_read_stsz_atom(ngx_http_mp4_file_t *mp4,\n    uint64_t atom_data_size);\nstatic ngx_int_t ngx_http_mp4_update_stsz_atom(ngx_http_mp4_file_t *mp4,\n    ngx_http_mp4_trak_t *trak);\nstatic ngx_int_t ngx_http_mp4_read_stco_atom(ngx_http_mp4_file_t *mp4,\n    uint64_t atom_data_size);\nstatic ngx_int_t ngx_http_mp4_update_stco_atom(ngx_http_mp4_file_t *mp4,\n    ngx_http_mp4_trak_t *trak);\nstatic void ngx_http_mp4_adjust_stco_atom(ngx_http_mp4_file_t *mp4,\n    ngx_http_mp4_trak_t *trak, int32_t adjustment);\nstatic ngx_int_t ngx_http_mp4_read_co64_atom(ngx_http_mp4_file_t *mp4,\n    uint64_t atom_data_size);\nstatic ngx_int_t ngx_http_mp4_update_co64_atom(ngx_http_mp4_file_t *mp4,\n    ngx_http_mp4_trak_t *trak);\nstatic void ngx_http_mp4_adjust_co64_atom(ngx_http_mp4_file_t *mp4,\n    ngx_http_mp4_trak_t *trak, off_t adjustment);\n\nstatic char *ngx_http_mp4(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\nstatic void *ngx_http_mp4_create_conf(ngx_conf_t *cf);\nstatic char *ngx_http_mp4_merge_conf(ngx_conf_t *cf, void *parent, void *child);\n\n\nstatic ngx_command_t  ngx_http_mp4_commands[] = {\n\n    { ngx_string(\"mp4\"),\n      NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,\n      ngx_http_mp4,\n      0,\n      0,\n      NULL },\n\n    { ngx_string(\"mp4_buffer_size\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_mp4_conf_t, buffer_size),\n      NULL },\n\n    { ngx_string(\"mp4_max_buffer_size\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_mp4_conf_t, max_buffer_size),\n      NULL },\n\n    { ngx_string(\"mp4_start_key_frame\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_mp4_conf_t, start_key_frame),\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_mp4_module_ctx = {\n    NULL,                          /* preconfiguration */\n    NULL,                          /* 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    ngx_http_mp4_create_conf,      /* create location configuration */\n    ngx_http_mp4_merge_conf        /* merge location configuration */\n};\n\n\nngx_module_t  ngx_http_mp4_module = {\n    NGX_MODULE_V1,\n    &ngx_http_mp4_module_ctx,      /* module context */\n    ngx_http_mp4_commands,         /* 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_http_mp4_atom_handler_t  ngx_http_mp4_atoms[] = {\n    { \"ftyp\", ngx_http_mp4_read_ftyp_atom },\n    { \"moov\", ngx_http_mp4_read_moov_atom },\n    { \"mdat\", ngx_http_mp4_read_mdat_atom },\n    { NULL, NULL }\n};\n\nstatic ngx_http_mp4_atom_handler_t  ngx_http_mp4_moov_atoms[] = {\n    { \"mvhd\", ngx_http_mp4_read_mvhd_atom },\n    { \"trak\", ngx_http_mp4_read_trak_atom },\n    { \"cmov\", ngx_http_mp4_read_cmov_atom },\n    { NULL, NULL }\n};\n\nstatic ngx_http_mp4_atom_handler_t  ngx_http_mp4_trak_atoms[] = {\n    { \"tkhd\", ngx_http_mp4_read_tkhd_atom },\n    { \"mdia\", ngx_http_mp4_read_mdia_atom },\n    { NULL, NULL }\n};\n\nstatic ngx_http_mp4_atom_handler_t  ngx_http_mp4_mdia_atoms[] = {\n    { \"mdhd\", ngx_http_mp4_read_mdhd_atom },\n    { \"hdlr\", ngx_http_mp4_read_hdlr_atom },\n    { \"minf\", ngx_http_mp4_read_minf_atom },\n    { NULL, NULL }\n};\n\nstatic ngx_http_mp4_atom_handler_t  ngx_http_mp4_minf_atoms[] = {\n    { \"vmhd\", ngx_http_mp4_read_vmhd_atom },\n    { \"smhd\", ngx_http_mp4_read_smhd_atom },\n    { \"dinf\", ngx_http_mp4_read_dinf_atom },\n    { \"stbl\", ngx_http_mp4_read_stbl_atom },\n    { NULL, NULL }\n};\n\nstatic ngx_http_mp4_atom_handler_t  ngx_http_mp4_stbl_atoms[] = {\n    { \"stsd\", ngx_http_mp4_read_stsd_atom },\n    { \"stts\", ngx_http_mp4_read_stts_atom },\n    { \"stss\", ngx_http_mp4_read_stss_atom },\n    { \"ctts\", ngx_http_mp4_read_ctts_atom },\n    { \"stsc\", ngx_http_mp4_read_stsc_atom },\n    { \"stsz\", ngx_http_mp4_read_stsz_atom },\n    { \"stco\", ngx_http_mp4_read_stco_atom },\n    { \"co64\", ngx_http_mp4_read_co64_atom },\n    { NULL, NULL }\n};\n\n\nstatic ngx_int_t\nngx_http_mp4_handler(ngx_http_request_t *r)\n{\n    u_char                    *last;\n    size_t                     root;\n    ngx_int_t                  rc, start, end;\n    ngx_uint_t                 level, length;\n    ngx_str_t                  path, value;\n    ngx_log_t                 *log;\n    ngx_buf_t                 *b;\n    ngx_chain_t                out;\n    ngx_http_mp4_file_t       *mp4;\n    ngx_open_file_info_t       of;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {\n        return NGX_HTTP_NOT_ALLOWED;\n    }\n\n    if (r->uri.data[r->uri.len - 1] == '/') {\n        return NGX_DECLINED;\n    }\n\n    rc = ngx_http_discard_request_body(r);\n\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    last = ngx_http_map_uri_to_path(r, &path, &root, 0);\n    if (last == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    log = r->connection->log;\n\n    path.len = last - path.data;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,\n                   \"http mp4 filename: \\\"%V\\\"\", &path);\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    ngx_memzero(&of, sizeof(ngx_open_file_info_t));\n\n    of.read_ahead = clcf->read_ahead;\n    of.directio = NGX_MAX_OFF_T_VALUE;\n    of.valid = clcf->open_file_cache_valid;\n    of.min_uses = clcf->open_file_cache_min_uses;\n    of.errors = clcf->open_file_cache_errors;\n    of.events = clcf->open_file_cache_events;\n\n    if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)\n        != NGX_OK)\n    {\n        switch (of.err) {\n\n        case 0:\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n\n        case NGX_ENOENT:\n        case NGX_ENOTDIR:\n        case NGX_ENAMETOOLONG:\n\n            level = NGX_LOG_ERR;\n            rc = NGX_HTTP_NOT_FOUND;\n            break;\n\n        case NGX_EACCES:\n#if (NGX_HAVE_OPENAT)\n        case NGX_EMLINK:\n        case NGX_ELOOP:\n#endif\n\n            level = NGX_LOG_ERR;\n            rc = NGX_HTTP_FORBIDDEN;\n            break;\n\n        default:\n\n            level = NGX_LOG_CRIT;\n            rc = NGX_HTTP_INTERNAL_SERVER_ERROR;\n            break;\n        }\n\n        if (rc != NGX_HTTP_NOT_FOUND || clcf->log_not_found) {\n            ngx_log_error(level, log, of.err,\n                          \"%s \\\"%s\\\" failed\", of.failed, path.data);\n        }\n\n        return rc;\n    }\n\n    if (!of.is_file) {\n        return NGX_DECLINED;\n    }\n\n    r->root_tested = !r->error_page;\n    r->allow_ranges = 1;\n\n    start = -1;\n    length = 0;\n    r->headers_out.content_length_n = of.size;\n    mp4 = NULL;\n    b = NULL;\n\n    if (r->args.len) {\n\n        if (ngx_http_arg(r, (u_char *) \"start\", 5, &value) == NGX_OK) {\n\n            /*\n             * A Flash player may send start value with a lot of digits\n             * after dot so a custom function is used instead of ngx_atofp().\n             */\n\n            start = ngx_http_mp4_atofp(value.data, value.len, 3);\n        }\n\n        if (ngx_http_arg(r, (u_char *) \"end\", 3, &value) == NGX_OK) {\n\n            end = ngx_http_mp4_atofp(value.data, value.len, 3);\n\n            if (end > 0) {\n                if (start < 0) {\n                    start = 0;\n                }\n\n                if (end > start) {\n                    length = end - start;\n                }\n            }\n        }\n    }\n\n    if (start >= 0) {\n        r->single_range = 1;\n\n        mp4 = ngx_pcalloc(r->pool, sizeof(ngx_http_mp4_file_t));\n        if (mp4 == NULL) {\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n\n        mp4->file.fd = of.fd;\n        mp4->file.name = path;\n        mp4->file.log = r->connection->log;\n        mp4->end = of.size;\n        mp4->start = (ngx_uint_t) start;\n        mp4->length = length;\n        mp4->request = r;\n\n        switch (ngx_http_mp4_process(mp4)) {\n\n        case NGX_DECLINED:\n            if (mp4->buffer) {\n                ngx_pfree(r->pool, mp4->buffer);\n            }\n\n            ngx_pfree(r->pool, mp4);\n            mp4 = NULL;\n\n            break;\n\n        case NGX_OK:\n            r->headers_out.content_length_n = mp4->content_length;\n            break;\n\n        default: /* NGX_ERROR */\n            if (mp4->buffer) {\n                ngx_pfree(r->pool, mp4->buffer);\n            }\n\n            ngx_pfree(r->pool, mp4);\n\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n    }\n\n    log->action = \"sending mp4 to client\";\n\n    if (clcf->directio <= of.size) {\n\n        /*\n         * DIRECTIO is set on transfer only\n         * to allow kernel to cache \"moov\" atom\n         */\n\n        if (ngx_directio_on(of.fd) == NGX_FILE_ERROR) {\n            ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,\n                          ngx_directio_on_n \" \\\"%s\\\" failed\", path.data);\n        }\n\n        of.is_directio = 1;\n\n        if (mp4) {\n            mp4->file.directio = 1;\n        }\n    }\n\n    r->headers_out.status = NGX_HTTP_OK;\n    r->headers_out.last_modified_time = of.mtime;\n\n    if (ngx_http_set_etag(r) != NGX_OK) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    if (ngx_http_set_content_type(r) != NGX_OK) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    if (mp4 == NULL) {\n        b = ngx_calloc_buf(r->pool);\n        if (b == NULL) {\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n\n        b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t));\n        if (b->file == NULL) {\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n    }\n\n    rc = ngx_http_send_header(r);\n\n    if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {\n        return rc;\n    }\n\n    if (mp4) {\n        return ngx_http_output_filter(r, mp4->out);\n    }\n\n    b->file_pos = 0;\n    b->file_last = of.size;\n\n    b->in_file = b->file_last ? 1 : 0;\n    b->last_buf = (r == r->main) ? 1 : 0;\n    b->last_in_chain = 1;\n    b->sync = (b->last_buf || b->in_file) ? 0 : 1;\n\n    b->file->fd = of.fd;\n    b->file->name = path;\n    b->file->log = log;\n    b->file->directio = of.is_directio;\n\n    out.buf = b;\n    out.next = NULL;\n\n    return ngx_http_output_filter(r, &out);\n}\n\n\nstatic ngx_int_t\nngx_http_mp4_atofp(u_char *line, size_t n, size_t point)\n{\n    ngx_int_t   value, cutoff, cutlim;\n    ngx_uint_t  dot;\n\n    /* same as ngx_atofp(), but allows additional digits */\n\n    if (n == 0) {\n        return NGX_ERROR;\n    }\n\n    cutoff = NGX_MAX_INT_T_VALUE / 10;\n    cutlim = NGX_MAX_INT_T_VALUE % 10;\n\n    dot = 0;\n\n    for (value = 0; n--; line++) {\n\n        if (*line == '.') {\n            if (dot) {\n                return NGX_ERROR;\n            }\n\n            dot = 1;\n            continue;\n        }\n\n        if (*line < '0' || *line > '9') {\n            return NGX_ERROR;\n        }\n\n        if (point == 0) {\n            continue;\n        }\n\n        if (value >= cutoff && (value > cutoff || *line - '0' > cutlim)) {\n            return NGX_ERROR;\n        }\n\n        value = value * 10 + (*line - '0');\n        point -= dot;\n    }\n\n    while (point--) {\n        if (value > cutoff) {\n            return NGX_ERROR;\n        }\n\n        value = value * 10;\n    }\n\n    return value;\n}\n\n\nstatic ngx_int_t\nngx_http_mp4_process(ngx_http_mp4_file_t *mp4)\n{\n    off_t                  start_offset, end_offset, adjustment;\n    ngx_int_t              rc;\n    ngx_uint_t             i, j;\n    ngx_chain_t          **prev;\n    ngx_http_mp4_trak_t   *trak;\n    ngx_http_mp4_conf_t   *conf;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                   \"mp4 start:%ui, length:%ui\", mp4->start, mp4->length);\n\n    conf = ngx_http_get_module_loc_conf(mp4->request, ngx_http_mp4_module);\n\n    mp4->buffer_size = conf->buffer_size;\n\n    rc = ngx_http_mp4_read_atom(mp4, ngx_http_mp4_atoms, mp4->end);\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    if (mp4->trak.nelts == 0) {\n        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                      \"no mp4 trak atoms were found in \\\"%s\\\"\",\n                      mp4->file.name.data);\n        return NGX_ERROR;\n    }\n\n    if (mp4->mdat_atom.buf == NULL) {\n        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                      \"no mp4 mdat atom was found in \\\"%s\\\"\",\n                      mp4->file.name.data);\n        return NGX_ERROR;\n    }\n\n    prev = &mp4->out;\n\n    if (mp4->ftyp_atom.buf) {\n        *prev = &mp4->ftyp_atom;\n        prev = &mp4->ftyp_atom.next;\n    }\n\n    *prev = &mp4->moov_atom;\n    prev = &mp4->moov_atom.next;\n\n    if (mp4->mvhd_atom.buf) {\n        mp4->moov_size += mp4->mvhd_atom_buf.last - mp4->mvhd_atom_buf.pos;\n        *prev = &mp4->mvhd_atom;\n        prev = &mp4->mvhd_atom.next;\n    }\n\n    start_offset = mp4->end;\n    end_offset = 0;\n    trak = mp4->trak.elts;\n\n    for (i = 0; i < mp4->trak.nelts; i++) {\n\n        if (ngx_http_mp4_update_stts_atom(mp4, &trak[i]) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        if (ngx_http_mp4_update_stss_atom(mp4, &trak[i]) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        ngx_http_mp4_update_ctts_atom(mp4, &trak[i]);\n\n        if (ngx_http_mp4_update_stsc_atom(mp4, &trak[i]) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        if (ngx_http_mp4_update_stsz_atom(mp4, &trak[i]) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        if (trak[i].out[NGX_HTTP_MP4_CO64_DATA].buf) {\n            if (ngx_http_mp4_update_co64_atom(mp4, &trak[i]) != NGX_OK) {\n                return NGX_ERROR;\n            }\n\n        } else {\n            if (ngx_http_mp4_update_stco_atom(mp4, &trak[i]) != NGX_OK) {\n                return NGX_ERROR;\n            }\n        }\n\n        ngx_http_mp4_update_stbl_atom(mp4, &trak[i]);\n        ngx_http_mp4_update_minf_atom(mp4, &trak[i]);\n        ngx_http_mp4_update_mdhd_atom(mp4, &trak[i]);\n        trak[i].size += trak[i].hdlr_size;\n        ngx_http_mp4_update_mdia_atom(mp4, &trak[i]);\n        trak[i].size += trak[i].tkhd_size;\n        ngx_http_mp4_update_edts_atom(mp4, &trak[i]);\n        ngx_http_mp4_update_trak_atom(mp4, &trak[i]);\n\n        mp4->moov_size += trak[i].size;\n\n        if (start_offset > trak[i].start_offset) {\n            start_offset = trak[i].start_offset;\n        }\n\n        if (end_offset < trak[i].end_offset) {\n            end_offset = trak[i].end_offset;\n        }\n\n        *prev = &trak[i].out[NGX_HTTP_MP4_TRAK_ATOM];\n        prev = &trak[i].out[NGX_HTTP_MP4_TRAK_ATOM].next;\n\n        for (j = 0; j < NGX_HTTP_MP4_LAST_ATOM + 1; j++) {\n            if (trak[i].out[j].buf) {\n                *prev = &trak[i].out[j];\n                prev = &trak[i].out[j].next;\n            }\n        }\n    }\n\n    if (end_offset < start_offset) {\n        end_offset = start_offset;\n    }\n\n    mp4->moov_size += 8;\n\n    ngx_mp4_set_32value(mp4->moov_atom_header, mp4->moov_size);\n    ngx_mp4_set_atom_name(mp4->moov_atom_header, 'm', 'o', 'o', 'v');\n    mp4->content_length += mp4->moov_size;\n\n    *prev = &mp4->mdat_atom;\n\n    if (start_offset > mp4->mdat_data.buf->file_last) {\n        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                      \"start time is out mp4 mdat atom in \\\"%s\\\"\",\n                      mp4->file.name.data);\n        return NGX_ERROR;\n    }\n\n    adjustment = mp4->ftyp_size + mp4->moov_size\n                 + ngx_http_mp4_update_mdat_atom(mp4, start_offset, end_offset)\n                 - start_offset;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                   \"mp4 adjustment:%O\", adjustment);\n\n    for (i = 0; i < mp4->trak.nelts; i++) {\n        if (trak[i].out[NGX_HTTP_MP4_CO64_DATA].buf) {\n            ngx_http_mp4_adjust_co64_atom(mp4, &trak[i], adjustment);\n        } else {\n            ngx_http_mp4_adjust_stco_atom(mp4, &trak[i], (int32_t) adjustment);\n        }\n    }\n\n    return NGX_OK;\n}\n\n\ntypedef struct {\n    u_char    size[4];\n    u_char    name[4];\n} ngx_mp4_atom_header_t;\n\ntypedef struct {\n    u_char    size[4];\n    u_char    name[4];\n    u_char    size64[8];\n} ngx_mp4_atom_header64_t;\n\n\nstatic ngx_int_t\nngx_http_mp4_read_atom(ngx_http_mp4_file_t *mp4,\n    ngx_http_mp4_atom_handler_t *atom, uint64_t atom_data_size)\n{\n    off_t        end;\n    size_t       atom_header_size;\n    u_char      *atom_header, *atom_name;\n    uint64_t     atom_size;\n    ngx_int_t    rc;\n    ngx_uint_t   n;\n\n    end = mp4->offset + atom_data_size;\n\n    while (mp4->offset < end) {\n\n        if (ngx_http_mp4_read(mp4, sizeof(uint32_t)) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        atom_header = mp4->buffer_pos;\n        atom_size = ngx_mp4_get_32value(atom_header);\n        atom_header_size = sizeof(ngx_mp4_atom_header_t);\n\n        if (atom_size == 0) {\n            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                           \"mp4 atom end\");\n            return NGX_OK;\n        }\n\n        if (atom_size < sizeof(ngx_mp4_atom_header_t)) {\n\n            if (atom_size == 1) {\n\n                if (ngx_http_mp4_read(mp4, sizeof(ngx_mp4_atom_header64_t))\n                    != NGX_OK)\n                {\n                    return NGX_ERROR;\n                }\n\n                /* 64-bit atom size */\n                atom_header = mp4->buffer_pos;\n                atom_size = ngx_mp4_get_64value(atom_header + 8);\n                atom_header_size = sizeof(ngx_mp4_atom_header64_t);\n\n                if (atom_size < sizeof(ngx_mp4_atom_header64_t)) {\n                    ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                                  \"\\\"%s\\\" mp4 atom is too small:%uL\",\n                                  mp4->file.name.data, atom_size);\n                    return NGX_ERROR;\n                }\n\n            } else {\n                ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                              \"\\\"%s\\\" mp4 atom is too small:%uL\",\n                              mp4->file.name.data, atom_size);\n                return NGX_ERROR;\n            }\n        }\n\n        if (ngx_http_mp4_read(mp4, sizeof(ngx_mp4_atom_header_t)) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        atom_header = mp4->buffer_pos;\n        atom_name = atom_header + sizeof(uint32_t);\n\n        ngx_log_debug4(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                       \"mp4 atom: %*s @%O:%uL\",\n                       (size_t) 4, atom_name, mp4->offset, atom_size);\n\n        if (atom_size > (uint64_t) (NGX_MAX_OFF_T_VALUE - mp4->offset)\n            || mp4->offset + (off_t) atom_size > end)\n        {\n            ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                          \"\\\"%s\\\" mp4 atom too large:%uL\",\n                          mp4->file.name.data, atom_size);\n            return NGX_ERROR;\n        }\n\n        for (n = 0; atom[n].name; n++) {\n\n            if (ngx_strncmp(atom_name, atom[n].name, 4) == 0) {\n\n                ngx_mp4_atom_next(mp4, atom_header_size);\n\n                rc = atom[n].handler(mp4, atom_size - atom_header_size);\n                if (rc != NGX_OK) {\n                    return rc;\n                }\n\n                goto next;\n            }\n        }\n\n        ngx_mp4_atom_next(mp4, atom_size);\n\n    next:\n        continue;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_mp4_read(ngx_http_mp4_file_t *mp4, size_t size)\n{\n    ssize_t  n;\n\n    if (mp4->buffer_pos + size <= mp4->buffer_end) {\n        return NGX_OK;\n    }\n\n    if (mp4->offset + (off_t) mp4->buffer_size > mp4->end) {\n        mp4->buffer_size = (size_t) (mp4->end - mp4->offset);\n    }\n\n    if (mp4->buffer_size < size) {\n        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                      \"\\\"%s\\\" mp4 file truncated\", mp4->file.name.data);\n        return NGX_ERROR;\n    }\n\n    if (mp4->buffer == NULL) {\n        mp4->buffer = ngx_palloc(mp4->request->pool, mp4->buffer_size);\n        if (mp4->buffer == NULL) {\n            return NGX_ERROR;\n        }\n\n        mp4->buffer_start = mp4->buffer;\n    }\n\n    n = ngx_read_file(&mp4->file, mp4->buffer_start, mp4->buffer_size,\n                      mp4->offset);\n\n    if (n == NGX_ERROR) {\n        return NGX_ERROR;\n    }\n\n    if ((size_t) n != mp4->buffer_size) {\n        ngx_log_error(NGX_LOG_CRIT, mp4->file.log, 0,\n                      ngx_read_file_n \" read only %z of %z from \\\"%s\\\"\",\n                      n, mp4->buffer_size, mp4->file.name.data);\n        return NGX_ERROR;\n    }\n\n    mp4->buffer_pos = mp4->buffer_start;\n    mp4->buffer_end = mp4->buffer_start + mp4->buffer_size;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_mp4_read_ftyp_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)\n{\n    u_char     *ftyp_atom;\n    size_t      atom_size;\n    ngx_buf_t  *atom;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, \"mp4 ftyp atom\");\n\n    if (atom_data_size > 1024\n        || ngx_mp4_atom_data(mp4) + (size_t) atom_data_size > mp4->buffer_end)\n    {\n        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                      \"\\\"%s\\\" mp4 ftyp atom is too large:%uL\",\n                      mp4->file.name.data, atom_data_size);\n        return NGX_ERROR;\n    }\n\n    if (mp4->ftyp_atom.buf) {\n        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                      \"duplicate mp4 ftyp atom in \\\"%s\\\"\", mp4->file.name.data);\n        return NGX_ERROR;\n    }\n\n    atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;\n\n    ftyp_atom = ngx_palloc(mp4->request->pool, atom_size);\n    if (ftyp_atom == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_mp4_set_32value(ftyp_atom, atom_size);\n    ngx_mp4_set_atom_name(ftyp_atom, 'f', 't', 'y', 'p');\n\n    /*\n     * only moov atom content is guaranteed to be in mp4->buffer\n     * during sending response, so ftyp atom content should be copied\n     */\n    ngx_memcpy(ftyp_atom + sizeof(ngx_mp4_atom_header_t),\n               ngx_mp4_atom_data(mp4), (size_t) atom_data_size);\n\n    atom = &mp4->ftyp_atom_buf;\n    atom->temporary = 1;\n    atom->pos = ftyp_atom;\n    atom->last = ftyp_atom + atom_size;\n\n    mp4->ftyp_atom.buf = atom;\n    mp4->ftyp_size = atom_size;\n    mp4->content_length = atom_size;\n\n    ngx_mp4_atom_next(mp4, atom_data_size);\n\n    return NGX_OK;\n}\n\n\n/*\n * Small excess buffer to process atoms after moov atom, mp4->buffer_start\n * will be set to this buffer part after moov atom processing.\n */\n#define NGX_HTTP_MP4_MOOV_BUFFER_EXCESS  (4 * 1024)\n\nstatic ngx_int_t\nngx_http_mp4_read_moov_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)\n{\n    ngx_int_t             rc;\n    ngx_uint_t            no_mdat;\n    ngx_buf_t            *atom;\n    ngx_http_mp4_conf_t  *conf;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, \"mp4 moov atom\");\n\n    no_mdat = (mp4->mdat_atom.buf == NULL);\n\n    if (no_mdat && mp4->start == 0 && mp4->length == 0) {\n        /*\n         * send original file if moov atom resides before\n         * mdat atom and client requests integral file\n         */\n        return NGX_DECLINED;\n    }\n\n    if (mp4->moov_atom.buf) {\n        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                      \"duplicate mp4 moov atom in \\\"%s\\\"\", mp4->file.name.data);\n        return NGX_ERROR;\n    }\n\n    conf = ngx_http_get_module_loc_conf(mp4->request, ngx_http_mp4_module);\n\n    if (atom_data_size > mp4->buffer_size) {\n\n        if (atom_data_size > conf->max_buffer_size) {\n            ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                          \"\\\"%s\\\" mp4 moov atom is too large:%uL, \"\n                          \"you may want to increase mp4_max_buffer_size\",\n                          mp4->file.name.data, atom_data_size);\n            return NGX_ERROR;\n        }\n\n        ngx_pfree(mp4->request->pool, mp4->buffer);\n        mp4->buffer = NULL;\n        mp4->buffer_pos = NULL;\n        mp4->buffer_end = NULL;\n\n        mp4->buffer_size = (size_t) atom_data_size\n                         + NGX_HTTP_MP4_MOOV_BUFFER_EXCESS * no_mdat;\n    }\n\n    if (ngx_http_mp4_read(mp4, (size_t) atom_data_size) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    mp4->trak.elts = &mp4->traks;\n    mp4->trak.size = sizeof(ngx_http_mp4_trak_t);\n    mp4->trak.nalloc = 2;\n    mp4->trak.pool = mp4->request->pool;\n\n    atom = &mp4->moov_atom_buf;\n    atom->temporary = 1;\n    atom->pos = mp4->moov_atom_header;\n    atom->last = mp4->moov_atom_header + 8;\n\n    mp4->moov_atom.buf = &mp4->moov_atom_buf;\n\n    rc = ngx_http_mp4_read_atom(mp4, ngx_http_mp4_moov_atoms, atom_data_size);\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, \"mp4 moov atom done\");\n\n    if (no_mdat) {\n        mp4->buffer_start = mp4->buffer_pos;\n        mp4->buffer_size = NGX_HTTP_MP4_MOOV_BUFFER_EXCESS;\n\n        if (mp4->buffer_start + mp4->buffer_size > mp4->buffer_end) {\n            mp4->buffer = NULL;\n            mp4->buffer_pos = NULL;\n            mp4->buffer_end = NULL;\n        }\n\n    } else {\n        /* skip atoms after moov atom */\n        mp4->offset = mp4->end;\n    }\n\n    return rc;\n}\n\n\nstatic ngx_int_t\nngx_http_mp4_read_mdat_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)\n{\n    ngx_buf_t  *data;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, \"mp4 mdat atom\");\n\n    if (mp4->mdat_atom.buf) {\n        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                      \"duplicate mp4 mdat atom in \\\"%s\\\"\", mp4->file.name.data);\n        return NGX_ERROR;\n    }\n\n    data = &mp4->mdat_data_buf;\n    data->file = &mp4->file;\n    data->in_file = 1;\n    data->last_buf = (mp4->request == mp4->request->main) ? 1 : 0;\n    data->last_in_chain = 1;\n    data->file_last = mp4->offset + atom_data_size;\n\n    mp4->mdat_atom.buf = &mp4->mdat_atom_buf;\n    mp4->mdat_atom.next = &mp4->mdat_data;\n    mp4->mdat_data.buf = data;\n\n    if (mp4->trak.nelts) {\n        /* skip atoms after mdat atom */\n        mp4->offset = mp4->end;\n\n    } else {\n        ngx_mp4_atom_next(mp4, atom_data_size);\n    }\n\n    return NGX_OK;\n}\n\n\nstatic size_t\nngx_http_mp4_update_mdat_atom(ngx_http_mp4_file_t *mp4, off_t start_offset,\n    off_t end_offset)\n{\n    off_t       atom_data_size;\n    u_char     *atom_header;\n    uint32_t    atom_header_size;\n    uint64_t    atom_size;\n    ngx_buf_t  *atom;\n\n    atom_data_size = end_offset - start_offset;\n    mp4->mdat_data.buf->file_pos = start_offset;\n    mp4->mdat_data.buf->file_last = end_offset;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                   \"mdat new offset @%O:%O\", start_offset, atom_data_size);\n\n    atom_header = mp4->mdat_atom_header;\n\n    if ((uint64_t) atom_data_size\n        > (uint64_t) 0xffffffff - sizeof(ngx_mp4_atom_header_t))\n    {\n        atom_size = 1;\n        atom_header_size = sizeof(ngx_mp4_atom_header64_t);\n        ngx_mp4_set_64value(atom_header + sizeof(ngx_mp4_atom_header_t),\n                            sizeof(ngx_mp4_atom_header64_t) + atom_data_size);\n    } else {\n        atom_size = sizeof(ngx_mp4_atom_header_t) + atom_data_size;\n        atom_header_size = sizeof(ngx_mp4_atom_header_t);\n    }\n\n    mp4->content_length += atom_header_size + atom_data_size;\n\n    ngx_mp4_set_32value(atom_header, atom_size);\n    ngx_mp4_set_atom_name(atom_header, 'm', 'd', 'a', 't');\n\n    atom = &mp4->mdat_atom_buf;\n    atom->temporary = 1;\n    atom->pos = atom_header;\n    atom->last = atom_header + atom_header_size;\n\n    return atom_header_size;\n}\n\n\ntypedef struct {\n    u_char    size[4];\n    u_char    name[4];\n    u_char    version[1];\n    u_char    flags[3];\n    u_char    creation_time[4];\n    u_char    modification_time[4];\n    u_char    timescale[4];\n    u_char    duration[4];\n    u_char    rate[4];\n    u_char    volume[2];\n    u_char    reserved[10];\n    u_char    matrix[36];\n    u_char    preview_time[4];\n    u_char    preview_duration[4];\n    u_char    poster_time[4];\n    u_char    selection_time[4];\n    u_char    selection_duration[4];\n    u_char    current_time[4];\n    u_char    next_track_id[4];\n} ngx_mp4_mvhd_atom_t;\n\ntypedef struct {\n    u_char    size[4];\n    u_char    name[4];\n    u_char    version[1];\n    u_char    flags[3];\n    u_char    creation_time[8];\n    u_char    modification_time[8];\n    u_char    timescale[4];\n    u_char    duration[8];\n    u_char    rate[4];\n    u_char    volume[2];\n    u_char    reserved[10];\n    u_char    matrix[36];\n    u_char    preview_time[4];\n    u_char    preview_duration[4];\n    u_char    poster_time[4];\n    u_char    selection_time[4];\n    u_char    selection_duration[4];\n    u_char    current_time[4];\n    u_char    next_track_id[4];\n} ngx_mp4_mvhd64_atom_t;\n\n\nstatic ngx_int_t\nngx_http_mp4_read_mvhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)\n{\n    u_char                 *atom_header;\n    size_t                  atom_size;\n    uint32_t                timescale;\n    uint64_t                duration, start_time, length_time;\n    ngx_buf_t              *atom;\n    ngx_mp4_mvhd_atom_t    *mvhd_atom;\n    ngx_mp4_mvhd64_atom_t  *mvhd64_atom;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, \"mp4 mvhd atom\");\n\n    if (mp4->mvhd_atom.buf) {\n        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                      \"duplicate mp4 mvhd atom in \\\"%s\\\"\", mp4->file.name.data);\n        return NGX_ERROR;\n    }\n\n    atom_header = ngx_mp4_atom_header(mp4);\n    mvhd_atom = (ngx_mp4_mvhd_atom_t *) atom_header;\n    mvhd64_atom = (ngx_mp4_mvhd64_atom_t *) atom_header;\n    ngx_mp4_set_atom_name(atom_header, 'm', 'v', 'h', 'd');\n\n    if (ngx_mp4_atom_data_size(ngx_mp4_mvhd_atom_t) > atom_data_size) {\n        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                      \"\\\"%s\\\" mp4 mvhd atom too small\", mp4->file.name.data);\n        return NGX_ERROR;\n    }\n\n    if (mvhd_atom->version[0] == 0) {\n        /* version 0: 32-bit duration */\n        timescale = ngx_mp4_get_32value(mvhd_atom->timescale);\n        duration = ngx_mp4_get_32value(mvhd_atom->duration);\n\n    } else {\n        /* version 1: 64-bit duration */\n\n        if (ngx_mp4_atom_data_size(ngx_mp4_mvhd64_atom_t) > atom_data_size) {\n            ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                          \"\\\"%s\\\" mp4 mvhd atom too small\",\n                          mp4->file.name.data);\n            return NGX_ERROR;\n        }\n\n        timescale = ngx_mp4_get_32value(mvhd64_atom->timescale);\n        duration = ngx_mp4_get_64value(mvhd64_atom->duration);\n    }\n\n    mp4->timescale = timescale;\n\n    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                   \"mvhd timescale:%uD, duration:%uL, time:%.3fs\",\n                   timescale, duration, (double) duration / timescale);\n\n    start_time = (uint64_t) mp4->start * timescale / 1000;\n\n    if (duration < start_time) {\n        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                      \"\\\"%s\\\" mp4 start time exceeds file duration\",\n                      mp4->file.name.data);\n        return NGX_ERROR;\n    }\n\n    duration -= start_time;\n\n    if (mp4->length) {\n        length_time = (uint64_t) mp4->length * timescale / 1000;\n\n        if (duration > length_time) {\n            duration = length_time;\n        }\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                   \"mvhd new duration:%uL, time:%.3fs\",\n                   duration, (double) duration / timescale);\n\n    atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;\n    ngx_mp4_set_32value(mvhd_atom->size, atom_size);\n\n    if (mvhd_atom->version[0] == 0) {\n        ngx_mp4_set_32value(mvhd_atom->duration, duration);\n\n    } else {\n        ngx_mp4_set_64value(mvhd64_atom->duration, duration);\n    }\n\n    atom = &mp4->mvhd_atom_buf;\n    atom->temporary = 1;\n    atom->pos = atom_header;\n    atom->last = atom_header + atom_size;\n\n    mp4->mvhd_atom.buf = atom;\n\n    ngx_mp4_atom_next(mp4, atom_data_size);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_mp4_read_trak_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)\n{\n    u_char               *atom_header, *atom_end;\n    off_t                 atom_file_end;\n    ngx_int_t             rc;\n    ngx_buf_t            *atom;\n    ngx_http_mp4_trak_t  *trak;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, \"mp4 trak atom\");\n\n    trak = ngx_array_push(&mp4->trak);\n    if (trak == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_memzero(trak, sizeof(ngx_http_mp4_trak_t));\n\n    atom_header = ngx_mp4_atom_header(mp4);\n    ngx_mp4_set_atom_name(atom_header, 't', 'r', 'a', 'k');\n\n    atom = &trak->trak_atom_buf;\n    atom->temporary = 1;\n    atom->pos = atom_header;\n    atom->last = atom_header + sizeof(ngx_mp4_atom_header_t);\n\n    trak->out[NGX_HTTP_MP4_TRAK_ATOM].buf = atom;\n\n    atom_end = mp4->buffer_pos + (size_t) atom_data_size;\n    atom_file_end = mp4->offset + atom_data_size;\n\n    rc = ngx_http_mp4_read_atom(mp4, ngx_http_mp4_trak_atoms, atom_data_size);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                   \"mp4 trak atom: %i\", rc);\n\n    if (rc == NGX_DECLINED) {\n        /* skip this trak */\n        ngx_memzero(trak, sizeof(ngx_http_mp4_trak_t));\n        mp4->trak.nelts--;\n        mp4->buffer_pos = atom_end;\n        mp4->offset = atom_file_end;\n        return NGX_OK;\n    }\n\n    return rc;\n}\n\n\nstatic void\nngx_http_mp4_update_trak_atom(ngx_http_mp4_file_t *mp4,\n    ngx_http_mp4_trak_t *trak)\n{\n    ngx_buf_t  *atom;\n\n    trak->size += sizeof(ngx_mp4_atom_header_t);\n    atom = &trak->trak_atom_buf;\n    ngx_mp4_set_32value(atom->pos, trak->size);\n}\n\n\nstatic ngx_int_t\nngx_http_mp4_read_cmov_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)\n{\n    ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                  \"\\\"%s\\\" mp4 compressed moov atom (cmov) is not supported\",\n                  mp4->file.name.data);\n\n    return NGX_ERROR;\n}\n\n\ntypedef struct {\n    u_char    size[4];\n    u_char    name[4];\n    u_char    version[1];\n    u_char    flags[3];\n    u_char    creation_time[4];\n    u_char    modification_time[4];\n    u_char    track_id[4];\n    u_char    reserved1[4];\n    u_char    duration[4];\n    u_char    reserved2[8];\n    u_char    layer[2];\n    u_char    group[2];\n    u_char    volume[2];\n    u_char    reserved3[2];\n    u_char    matrix[36];\n    u_char    width[4];\n    u_char    height[4];\n} ngx_mp4_tkhd_atom_t;\n\ntypedef struct {\n    u_char    size[4];\n    u_char    name[4];\n    u_char    version[1];\n    u_char    flags[3];\n    u_char    creation_time[8];\n    u_char    modification_time[8];\n    u_char    track_id[4];\n    u_char    reserved1[4];\n    u_char    duration[8];\n    u_char    reserved2[8];\n    u_char    layer[2];\n    u_char    group[2];\n    u_char    volume[2];\n    u_char    reserved3[2];\n    u_char    matrix[36];\n    u_char    width[4];\n    u_char    height[4];\n} ngx_mp4_tkhd64_atom_t;\n\n\nstatic ngx_int_t\nngx_http_mp4_read_tkhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)\n{\n    u_char                 *atom_header;\n    size_t                  atom_size;\n    uint64_t                duration, start_time, length_time;\n    ngx_buf_t              *atom;\n    ngx_http_mp4_trak_t    *trak;\n    ngx_mp4_tkhd_atom_t    *tkhd_atom;\n    ngx_mp4_tkhd64_atom_t  *tkhd64_atom;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, \"mp4 tkhd atom\");\n\n    atom_header = ngx_mp4_atom_header(mp4);\n    tkhd_atom = (ngx_mp4_tkhd_atom_t *) atom_header;\n    tkhd64_atom = (ngx_mp4_tkhd64_atom_t *) atom_header;\n    ngx_mp4_set_atom_name(tkhd_atom, 't', 'k', 'h', 'd');\n\n    if (ngx_mp4_atom_data_size(ngx_mp4_tkhd_atom_t) > atom_data_size) {\n        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                      \"\\\"%s\\\" mp4 tkhd atom too small\", mp4->file.name.data);\n        return NGX_ERROR;\n    }\n\n    if (tkhd_atom->version[0] == 0) {\n        /* version 0: 32-bit duration */\n        duration = ngx_mp4_get_32value(tkhd_atom->duration);\n\n    } else {\n        /* version 1: 64-bit duration */\n\n        if (ngx_mp4_atom_data_size(ngx_mp4_tkhd64_atom_t) > atom_data_size) {\n            ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                          \"\\\"%s\\\" mp4 tkhd atom too small\",\n                          mp4->file.name.data);\n            return NGX_ERROR;\n        }\n\n        duration = ngx_mp4_get_64value(tkhd64_atom->duration);\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                   \"tkhd duration:%uL, time:%.3fs\",\n                   duration, (double) duration / mp4->timescale);\n\n    start_time = (uint64_t) mp4->start * mp4->timescale / 1000;\n\n    if (duration <= start_time) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                       \"tkhd duration is less than start time\");\n        return NGX_DECLINED;\n    }\n\n    duration -= start_time;\n\n    if (mp4->length) {\n        length_time = (uint64_t) mp4->length * mp4->timescale / 1000;\n\n        if (duration > length_time) {\n            duration = length_time;\n        }\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                   \"tkhd new duration:%uL, time:%.3fs\",\n                   duration, (double) duration / mp4->timescale);\n\n    atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;\n\n    trak = ngx_mp4_last_trak(mp4);\n\n    if (trak->out[NGX_HTTP_MP4_TKHD_ATOM].buf) {\n        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                      \"duplicate mp4 tkhd atom in \\\"%s\\\"\", mp4->file.name.data);\n        return NGX_ERROR;\n    }\n\n    trak->tkhd_size = atom_size;\n    trak->movie_duration = duration;\n\n    ngx_mp4_set_32value(tkhd_atom->size, atom_size);\n\n    if (tkhd_atom->version[0] == 0) {\n        ngx_mp4_set_32value(tkhd_atom->duration, duration);\n\n    } else {\n        ngx_mp4_set_64value(tkhd64_atom->duration, duration);\n    }\n\n    atom = &trak->tkhd_atom_buf;\n    atom->temporary = 1;\n    atom->pos = atom_header;\n    atom->last = atom_header + atom_size;\n\n    trak->out[NGX_HTTP_MP4_TKHD_ATOM].buf = atom;\n\n    ngx_mp4_atom_next(mp4, atom_data_size);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_mp4_read_mdia_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)\n{\n    u_char               *atom_header;\n    ngx_buf_t            *atom;\n    ngx_http_mp4_trak_t  *trak;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, \"process mdia atom\");\n\n    atom_header = ngx_mp4_atom_header(mp4);\n    ngx_mp4_set_atom_name(atom_header, 'm', 'd', 'i', 'a');\n\n    trak = ngx_mp4_last_trak(mp4);\n\n    if (trak->out[NGX_HTTP_MP4_MDIA_ATOM].buf) {\n        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                      \"duplicate mp4 mdia atom in \\\"%s\\\"\", mp4->file.name.data);\n        return NGX_ERROR;\n    }\n\n    atom = &trak->mdia_atom_buf;\n    atom->temporary = 1;\n    atom->pos = atom_header;\n    atom->last = atom_header + sizeof(ngx_mp4_atom_header_t);\n\n    trak->out[NGX_HTTP_MP4_MDIA_ATOM].buf = atom;\n\n    return ngx_http_mp4_read_atom(mp4, ngx_http_mp4_mdia_atoms, atom_data_size);\n}\n\n\nstatic void\nngx_http_mp4_update_mdia_atom(ngx_http_mp4_file_t *mp4,\n    ngx_http_mp4_trak_t *trak)\n{\n    ngx_buf_t  *atom;\n\n    trak->size += sizeof(ngx_mp4_atom_header_t);\n    atom = &trak->mdia_atom_buf;\n    ngx_mp4_set_32value(atom->pos, trak->size);\n}\n\n\ntypedef struct {\n    u_char    size[4];\n    u_char    name[4];\n    u_char    version[1];\n    u_char    flags[3];\n    u_char    creation_time[4];\n    u_char    modification_time[4];\n    u_char    timescale[4];\n    u_char    duration[4];\n    u_char    language[2];\n    u_char    quality[2];\n} ngx_mp4_mdhd_atom_t;\n\ntypedef struct {\n    u_char    size[4];\n    u_char    name[4];\n    u_char    version[1];\n    u_char    flags[3];\n    u_char    creation_time[8];\n    u_char    modification_time[8];\n    u_char    timescale[4];\n    u_char    duration[8];\n    u_char    language[2];\n    u_char    quality[2];\n} ngx_mp4_mdhd64_atom_t;\n\n\nstatic ngx_int_t\nngx_http_mp4_read_mdhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)\n{\n    u_char                 *atom_header;\n    size_t                  atom_size;\n    uint32_t                timescale;\n    uint64_t                duration, start_time, length_time;\n    ngx_buf_t              *atom;\n    ngx_http_mp4_trak_t    *trak;\n    ngx_mp4_mdhd_atom_t    *mdhd_atom;\n    ngx_mp4_mdhd64_atom_t  *mdhd64_atom;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, \"mp4 mdhd atom\");\n\n    atom_header = ngx_mp4_atom_header(mp4);\n    mdhd_atom = (ngx_mp4_mdhd_atom_t *) atom_header;\n    mdhd64_atom = (ngx_mp4_mdhd64_atom_t *) atom_header;\n    ngx_mp4_set_atom_name(mdhd_atom, 'm', 'd', 'h', 'd');\n\n    if (ngx_mp4_atom_data_size(ngx_mp4_mdhd_atom_t) > atom_data_size) {\n        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                      \"\\\"%s\\\" mp4 mdhd atom too small\", mp4->file.name.data);\n        return NGX_ERROR;\n    }\n\n    if (mdhd_atom->version[0] == 0) {\n        /* version 0: everything is 32-bit */\n        timescale = ngx_mp4_get_32value(mdhd_atom->timescale);\n        duration = ngx_mp4_get_32value(mdhd_atom->duration);\n\n    } else {\n        /* version 1: 64-bit duration and 32-bit timescale */\n\n        if (ngx_mp4_atom_data_size(ngx_mp4_mdhd64_atom_t) > atom_data_size) {\n            ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                          \"\\\"%s\\\" mp4 mdhd atom too small\",\n                          mp4->file.name.data);\n            return NGX_ERROR;\n        }\n\n        timescale = ngx_mp4_get_32value(mdhd64_atom->timescale);\n        duration = ngx_mp4_get_64value(mdhd64_atom->duration);\n    }\n\n    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                   \"mdhd timescale:%uD, duration:%uL, time:%.3fs\",\n                   timescale, duration, (double) duration / timescale);\n\n    start_time = (uint64_t) mp4->start * timescale / 1000;\n\n    if (duration <= start_time) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                       \"mdhd duration is less than start time\");\n        return NGX_DECLINED;\n    }\n\n    duration -= start_time;\n\n    if (mp4->length) {\n        length_time = (uint64_t) mp4->length * timescale / 1000;\n\n        if (duration > length_time) {\n            duration = length_time;\n        }\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                   \"mdhd new duration:%uL, time:%.3fs\",\n                   duration, (double) duration / timescale);\n\n    atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;\n\n    trak = ngx_mp4_last_trak(mp4);\n\n    if (trak->out[NGX_HTTP_MP4_MDHD_ATOM].buf) {\n        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                      \"duplicate mp4 mdhd atom in \\\"%s\\\"\", mp4->file.name.data);\n        return NGX_ERROR;\n    }\n\n    trak->mdhd_size = atom_size;\n    trak->timescale = timescale;\n    trak->duration = duration;\n\n    ngx_mp4_set_32value(mdhd_atom->size, atom_size);\n\n    atom = &trak->mdhd_atom_buf;\n    atom->temporary = 1;\n    atom->pos = atom_header;\n    atom->last = atom_header + atom_size;\n\n    trak->out[NGX_HTTP_MP4_MDHD_ATOM].buf = atom;\n\n    ngx_mp4_atom_next(mp4, atom_data_size);\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_mp4_update_mdhd_atom(ngx_http_mp4_file_t *mp4,\n            ngx_http_mp4_trak_t *trak)\n{\n    ngx_buf_t              *atom;\n    ngx_mp4_mdhd_atom_t    *mdhd_atom;\n    ngx_mp4_mdhd64_atom_t  *mdhd64_atom;\n\n    atom = trak->out[NGX_HTTP_MP4_MDHD_ATOM].buf;\n    if (atom == NULL) {\n        return;\n    }\n\n    mdhd_atom = (ngx_mp4_mdhd_atom_t *) atom->pos;\n    mdhd64_atom = (ngx_mp4_mdhd64_atom_t *) atom->pos;\n\n    if (mdhd_atom->version[0] == 0) {\n        ngx_mp4_set_32value(mdhd_atom->duration, trak->duration);\n\n    } else {\n        ngx_mp4_set_64value(mdhd64_atom->duration, trak->duration);\n    }\n\n    trak->size += trak->mdhd_size;\n}\n\n\nstatic ngx_int_t\nngx_http_mp4_read_hdlr_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)\n{\n    u_char              *atom_header;\n    size_t               atom_size;\n    ngx_buf_t            *atom;\n    ngx_http_mp4_trak_t  *trak;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, \"mp4 hdlr atom\");\n\n    atom_header = ngx_mp4_atom_header(mp4);\n    atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;\n    ngx_mp4_set_32value(atom_header, atom_size);\n    ngx_mp4_set_atom_name(atom_header, 'h', 'd', 'l', 'r');\n\n    trak = ngx_mp4_last_trak(mp4);\n\n    if (trak->out[NGX_HTTP_MP4_HDLR_ATOM].buf) {\n        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                      \"duplicate mp4 hdlr atom in \\\"%s\\\"\", mp4->file.name.data);\n        return NGX_ERROR;\n    }\n\n    atom = &trak->hdlr_atom_buf;\n    atom->temporary = 1;\n    atom->pos = atom_header;\n    atom->last = atom_header + atom_size;\n\n    trak->hdlr_size = atom_size;\n    trak->out[NGX_HTTP_MP4_HDLR_ATOM].buf = atom;\n\n    ngx_mp4_atom_next(mp4, atom_data_size);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_mp4_read_minf_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)\n{\n    u_char               *atom_header;\n    ngx_buf_t            *atom;\n    ngx_http_mp4_trak_t  *trak;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, \"process minf atom\");\n\n    atom_header = ngx_mp4_atom_header(mp4);\n    ngx_mp4_set_atom_name(atom_header, 'm', 'i', 'n', 'f');\n\n    trak = ngx_mp4_last_trak(mp4);\n\n    if (trak->out[NGX_HTTP_MP4_MINF_ATOM].buf) {\n        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                      \"duplicate mp4 minf atom in \\\"%s\\\"\", mp4->file.name.data);\n        return NGX_ERROR;\n    }\n\n    atom = &trak->minf_atom_buf;\n    atom->temporary = 1;\n    atom->pos = atom_header;\n    atom->last = atom_header + sizeof(ngx_mp4_atom_header_t);\n\n    trak->out[NGX_HTTP_MP4_MINF_ATOM].buf = atom;\n\n    return ngx_http_mp4_read_atom(mp4, ngx_http_mp4_minf_atoms, atom_data_size);\n}\n\n\nstatic void\nngx_http_mp4_update_minf_atom(ngx_http_mp4_file_t *mp4,\n    ngx_http_mp4_trak_t *trak)\n{\n    ngx_buf_t  *atom;\n\n    trak->size += sizeof(ngx_mp4_atom_header_t)\n               + trak->vmhd_size\n               + trak->smhd_size\n               + trak->dinf_size;\n    atom = &trak->minf_atom_buf;\n    ngx_mp4_set_32value(atom->pos, trak->size);\n}\n\n\nstatic ngx_int_t\nngx_http_mp4_read_vmhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)\n{\n    u_char              *atom_header;\n    size_t               atom_size;\n    ngx_buf_t            *atom;\n    ngx_http_mp4_trak_t  *trak;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, \"mp4 vmhd atom\");\n\n    atom_header = ngx_mp4_atom_header(mp4);\n    atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;\n    ngx_mp4_set_32value(atom_header, atom_size);\n    ngx_mp4_set_atom_name(atom_header, 'v', 'm', 'h', 'd');\n\n    trak = ngx_mp4_last_trak(mp4);\n\n    if (trak->out[NGX_HTTP_MP4_VMHD_ATOM].buf\n        || trak->out[NGX_HTTP_MP4_SMHD_ATOM].buf)\n    {\n        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                      \"duplicate mp4 vmhd/smhd atom in \\\"%s\\\"\",\n                      mp4->file.name.data);\n        return NGX_ERROR;\n    }\n\n    atom = &trak->vmhd_atom_buf;\n    atom->temporary = 1;\n    atom->pos = atom_header;\n    atom->last = atom_header + atom_size;\n\n    trak->vmhd_size += atom_size;\n    trak->out[NGX_HTTP_MP4_VMHD_ATOM].buf = atom;\n\n    ngx_mp4_atom_next(mp4, atom_data_size);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_mp4_read_smhd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)\n{\n    u_char              *atom_header;\n    size_t               atom_size;\n    ngx_buf_t            *atom;\n    ngx_http_mp4_trak_t  *trak;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, \"mp4 smhd atom\");\n\n    atom_header = ngx_mp4_atom_header(mp4);\n    atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;\n    ngx_mp4_set_32value(atom_header, atom_size);\n    ngx_mp4_set_atom_name(atom_header, 's', 'm', 'h', 'd');\n\n    trak = ngx_mp4_last_trak(mp4);\n\n    if (trak->out[NGX_HTTP_MP4_VMHD_ATOM].buf\n        || trak->out[NGX_HTTP_MP4_SMHD_ATOM].buf)\n    {\n        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                      \"duplicate mp4 vmhd/smhd atom in \\\"%s\\\"\",\n                      mp4->file.name.data);\n        return NGX_ERROR;\n    }\n\n    atom = &trak->smhd_atom_buf;\n    atom->temporary = 1;\n    atom->pos = atom_header;\n    atom->last = atom_header + atom_size;\n\n    trak->smhd_size += atom_size;\n    trak->out[NGX_HTTP_MP4_SMHD_ATOM].buf = atom;\n\n    ngx_mp4_atom_next(mp4, atom_data_size);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_mp4_read_dinf_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)\n{\n    u_char              *atom_header;\n    size_t               atom_size;\n    ngx_buf_t            *atom;\n    ngx_http_mp4_trak_t  *trak;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, \"mp4 dinf atom\");\n\n    atom_header = ngx_mp4_atom_header(mp4);\n    atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;\n    ngx_mp4_set_32value(atom_header, atom_size);\n    ngx_mp4_set_atom_name(atom_header, 'd', 'i', 'n', 'f');\n\n    trak = ngx_mp4_last_trak(mp4);\n\n    if (trak->out[NGX_HTTP_MP4_DINF_ATOM].buf) {\n        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                      \"duplicate mp4 dinf atom in \\\"%s\\\"\", mp4->file.name.data);\n        return NGX_ERROR;\n    }\n\n    atom = &trak->dinf_atom_buf;\n    atom->temporary = 1;\n    atom->pos = atom_header;\n    atom->last = atom_header + atom_size;\n\n    trak->dinf_size += atom_size;\n    trak->out[NGX_HTTP_MP4_DINF_ATOM].buf = atom;\n\n    ngx_mp4_atom_next(mp4, atom_data_size);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_mp4_read_stbl_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)\n{\n    u_char               *atom_header;\n    ngx_buf_t            *atom;\n    ngx_http_mp4_trak_t  *trak;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, \"process stbl atom\");\n\n    atom_header = ngx_mp4_atom_header(mp4);\n    ngx_mp4_set_atom_name(atom_header, 's', 't', 'b', 'l');\n\n    trak = ngx_mp4_last_trak(mp4);\n\n    if (trak->out[NGX_HTTP_MP4_STBL_ATOM].buf) {\n        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                      \"duplicate mp4 stbl atom in \\\"%s\\\"\", mp4->file.name.data);\n        return NGX_ERROR;\n    }\n\n    atom = &trak->stbl_atom_buf;\n    atom->temporary = 1;\n    atom->pos = atom_header;\n    atom->last = atom_header + sizeof(ngx_mp4_atom_header_t);\n\n    trak->out[NGX_HTTP_MP4_STBL_ATOM].buf = atom;\n\n    return ngx_http_mp4_read_atom(mp4, ngx_http_mp4_stbl_atoms, atom_data_size);\n}\n\n\nstatic void\nngx_http_mp4_update_edts_atom(ngx_http_mp4_file_t *mp4,\n    ngx_http_mp4_trak_t *trak)\n{\n    ngx_buf_t            *atom;\n    ngx_mp4_elst_atom_t  *elst_atom;\n    ngx_mp4_edts_atom_t  *edts_atom;\n\n    if (trak->prefix == 0) {\n        return;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                   \"mp4 edts atom update prefix:%uL\", trak->prefix);\n\n    edts_atom = &trak->edts_atom;\n    ngx_mp4_set_32value(edts_atom->size, sizeof(ngx_mp4_edts_atom_t)\n                                         + sizeof(ngx_mp4_elst_atom_t));\n    ngx_mp4_set_atom_name(edts_atom, 'e', 'd', 't', 's');\n\n    atom = &trak->edts_atom_buf;\n    atom->temporary = 1;\n    atom->pos = (u_char *) edts_atom;\n    atom->last = (u_char *) edts_atom + sizeof(ngx_mp4_edts_atom_t);\n\n    trak->out[NGX_HTTP_MP4_EDTS_ATOM].buf = atom;\n\n    elst_atom = &trak->elst_atom;\n    ngx_mp4_set_32value(elst_atom->size, sizeof(ngx_mp4_elst_atom_t));\n    ngx_mp4_set_atom_name(elst_atom, 'e', 'l', 's', 't');\n\n    elst_atom->version[0] = 1;\n    elst_atom->flags[0] = 0;\n    elst_atom->flags[1] = 0;\n    elst_atom->flags[2] = 0;\n\n    ngx_mp4_set_32value(elst_atom->entries, 1);\n    ngx_mp4_set_64value(elst_atom->duration, trak->movie_duration);\n    ngx_mp4_set_64value(elst_atom->media_time, trak->prefix);\n    ngx_mp4_set_16value(elst_atom->media_rate, 1);\n    ngx_mp4_set_16value(elst_atom->reserved, 0);\n\n    atom = &trak->elst_atom_buf;\n    atom->temporary = 1;\n    atom->pos = (u_char *) elst_atom;\n    atom->last = (u_char *) elst_atom + sizeof(ngx_mp4_elst_atom_t);\n\n    trak->out[NGX_HTTP_MP4_ELST_ATOM].buf = atom;\n\n    trak->size += sizeof(ngx_mp4_edts_atom_t) + sizeof(ngx_mp4_elst_atom_t);\n}\n\n\nstatic void\nngx_http_mp4_update_stbl_atom(ngx_http_mp4_file_t *mp4,\n    ngx_http_mp4_trak_t *trak)\n{\n    ngx_buf_t  *atom;\n\n    trak->size += sizeof(ngx_mp4_atom_header_t);\n    atom = &trak->stbl_atom_buf;\n    ngx_mp4_set_32value(atom->pos, trak->size);\n}\n\n\ntypedef struct {\n    u_char    size[4];\n    u_char    name[4];\n    u_char    version[1];\n    u_char    flags[3];\n    u_char    entries[4];\n\n    u_char    media_size[4];\n    u_char    media_name[4];\n} ngx_mp4_stsd_atom_t;\n\n\nstatic ngx_int_t\nngx_http_mp4_read_stsd_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)\n{\n    u_char               *atom_header, *atom_table;\n    size_t                atom_size;\n    ngx_buf_t            *atom;\n    ngx_mp4_stsd_atom_t  *stsd_atom;\n    ngx_http_mp4_trak_t  *trak;\n\n    /* sample description atom */\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, \"mp4 stsd atom\");\n\n    atom_header = ngx_mp4_atom_header(mp4);\n    stsd_atom = (ngx_mp4_stsd_atom_t *) atom_header;\n    atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;\n    atom_table = atom_header + atom_size;\n    ngx_mp4_set_32value(stsd_atom->size, atom_size);\n    ngx_mp4_set_atom_name(stsd_atom, 's', 't', 's', 'd');\n\n    if (ngx_mp4_atom_data_size(ngx_mp4_stsd_atom_t) > atom_data_size) {\n        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                      \"\\\"%s\\\" mp4 stsd atom too small\", mp4->file.name.data);\n        return NGX_ERROR;\n    }\n\n    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                   \"stsd entries:%uD, media:%*s\",\n                   ngx_mp4_get_32value(stsd_atom->entries),\n                   (size_t) 4, stsd_atom->media_name);\n\n    trak = ngx_mp4_last_trak(mp4);\n\n    if (trak->out[NGX_HTTP_MP4_STSD_ATOM].buf) {\n        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                      \"duplicate mp4 stsd atom in \\\"%s\\\"\", mp4->file.name.data);\n        return NGX_ERROR;\n    }\n\n    atom = &trak->stsd_atom_buf;\n    atom->temporary = 1;\n    atom->pos = atom_header;\n    atom->last = atom_table;\n\n    trak->out[NGX_HTTP_MP4_STSD_ATOM].buf = atom;\n    trak->size += atom_size;\n\n    ngx_mp4_atom_next(mp4, atom_data_size);\n\n    return NGX_OK;\n}\n\n\ntypedef struct {\n    u_char    size[4];\n    u_char    name[4];\n    u_char    version[1];\n    u_char    flags[3];\n    u_char    entries[4];\n} ngx_mp4_stts_atom_t;\n\ntypedef struct {\n    u_char    count[4];\n    u_char    duration[4];\n} ngx_mp4_stts_entry_t;\n\n\nstatic ngx_int_t\nngx_http_mp4_read_stts_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)\n{\n    u_char               *atom_header, *atom_table, *atom_end;\n    uint32_t              entries;\n    ngx_buf_t            *atom, *data;\n    ngx_mp4_stts_atom_t  *stts_atom;\n    ngx_http_mp4_trak_t  *trak;\n\n    /* time-to-sample atom */\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, \"mp4 stts atom\");\n\n    atom_header = ngx_mp4_atom_header(mp4);\n    stts_atom = (ngx_mp4_stts_atom_t *) atom_header;\n    ngx_mp4_set_atom_name(stts_atom, 's', 't', 't', 's');\n\n    if (ngx_mp4_atom_data_size(ngx_mp4_stts_atom_t) > atom_data_size) {\n        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                      \"\\\"%s\\\" mp4 stts atom too small\", mp4->file.name.data);\n        return NGX_ERROR;\n    }\n\n    entries = ngx_mp4_get_32value(stts_atom->entries);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                   \"mp4 time-to-sample entries:%uD\", entries);\n\n    if (ngx_mp4_atom_data_size(ngx_mp4_stts_atom_t)\n        + entries * sizeof(ngx_mp4_stts_entry_t) > atom_data_size)\n    {\n        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                      \"\\\"%s\\\" mp4 stts atom too small\", mp4->file.name.data);\n        return NGX_ERROR;\n    }\n\n    atom_table = atom_header + sizeof(ngx_mp4_stts_atom_t);\n    atom_end = atom_table + entries * sizeof(ngx_mp4_stts_entry_t);\n\n    trak = ngx_mp4_last_trak(mp4);\n\n    if (trak->out[NGX_HTTP_MP4_STTS_ATOM].buf) {\n        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                      \"duplicate mp4 stts atom in \\\"%s\\\"\", mp4->file.name.data);\n        return NGX_ERROR;\n    }\n\n    trak->time_to_sample_entries = entries;\n\n    atom = &trak->stts_atom_buf;\n    atom->temporary = 1;\n    atom->pos = atom_header;\n    atom->last = atom_table;\n\n    data = &trak->stts_data_buf;\n    data->temporary = 1;\n    data->pos = atom_table;\n    data->last = atom_end;\n\n    trak->out[NGX_HTTP_MP4_STTS_ATOM].buf = atom;\n    trak->out[NGX_HTTP_MP4_STTS_DATA].buf = data;\n\n    ngx_mp4_atom_next(mp4, atom_data_size);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_mp4_update_stts_atom(ngx_http_mp4_file_t *mp4,\n    ngx_http_mp4_trak_t *trak)\n{\n    size_t                atom_size;\n    ngx_buf_t            *atom, *data;\n    ngx_mp4_stts_atom_t  *stts_atom;\n\n    /*\n     * mdia.minf.stbl.stts updating requires trak->timescale\n     * from mdia.mdhd atom which may reside after mdia.minf\n     */\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                   \"mp4 stts atom update\");\n\n    data = trak->out[NGX_HTTP_MP4_STTS_DATA].buf;\n\n    if (data == NULL) {\n        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                      \"no mp4 stts atoms were found in \\\"%s\\\"\",\n                      mp4->file.name.data);\n        return NGX_ERROR;\n    }\n\n    if (ngx_http_mp4_crop_stts_data(mp4, trak, 1) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    if (ngx_http_mp4_crop_stts_data(mp4, trak, 0) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                   \"time-to-sample entries:%uD\", trak->time_to_sample_entries);\n\n    atom_size = sizeof(ngx_mp4_stts_atom_t) + (data->last - data->pos);\n    trak->size += atom_size;\n\n    atom = trak->out[NGX_HTTP_MP4_STTS_ATOM].buf;\n    stts_atom = (ngx_mp4_stts_atom_t *) atom->pos;\n    ngx_mp4_set_32value(stts_atom->size, atom_size);\n    ngx_mp4_set_32value(stts_atom->entries, trak->time_to_sample_entries);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_mp4_crop_stts_data(ngx_http_mp4_file_t *mp4,\n    ngx_http_mp4_trak_t *trak, ngx_uint_t start)\n{\n    uint32_t               count, duration, rest, key_prefix;\n    uint64_t               start_time;\n    ngx_buf_t             *data;\n    ngx_uint_t             start_sample, entries, start_sec;\n    ngx_mp4_stts_entry_t  *entry, *end;\n\n    if (start) {\n        start_sec = mp4->start;\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                       \"mp4 stts crop start_time:%ui\", start_sec);\n\n    } else if (mp4->length) {\n        start_sec = mp4->length;\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                       \"mp4 stts crop end_time:%ui\", start_sec);\n\n    } else {\n        return NGX_OK;\n    }\n\n    data = trak->out[NGX_HTTP_MP4_STTS_DATA].buf;\n\n    start_time = (uint64_t) start_sec * trak->timescale / 1000 + trak->prefix;\n\n    entries = trak->time_to_sample_entries;\n    start_sample = 0;\n    entry = (ngx_mp4_stts_entry_t *) data->pos;\n    end = (ngx_mp4_stts_entry_t *) data->last;\n\n    while (entry < end) {\n        count = ngx_mp4_get_32value(entry->count);\n        duration = ngx_mp4_get_32value(entry->duration);\n\n        ngx_log_debug3(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                       \"time:%uL, count:%uD, duration:%uD\",\n                       start_time, count, duration);\n\n        if (start_time < (uint64_t) count * duration) {\n            start_sample += (ngx_uint_t) (start_time / duration);\n            rest = (uint32_t) (start_time / duration);\n            goto found;\n        }\n\n        start_sample += count;\n        start_time -= (uint64_t) count * duration;\n        entries--;\n        entry++;\n    }\n\n    if (start) {\n        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                      \"start time is out mp4 stts samples in \\\"%s\\\"\",\n                      mp4->file.name.data);\n\n        return NGX_ERROR;\n\n    } else {\n        trak->end_sample = trak->start_sample + start_sample;\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                       \"end_sample:%ui\", trak->end_sample);\n\n        return NGX_OK;\n    }\n\nfound:\n\n    if (start) {\n        key_prefix = ngx_http_mp4_seek_key_frame(mp4, trak, start_sample);\n\n        start_sample -= key_prefix;\n\n        while (rest < key_prefix) {\n            trak->prefix += rest * duration;\n            key_prefix -= rest;\n\n            entry--;\n            entries++;\n\n            count = ngx_mp4_get_32value(entry->count);\n            duration = ngx_mp4_get_32value(entry->duration);\n            rest = count;\n        }\n\n        trak->prefix += key_prefix * duration;\n        trak->duration += trak->prefix;\n        rest -= key_prefix;\n\n        ngx_mp4_set_32value(entry->count, count - rest);\n        data->pos = (u_char *) entry;\n        trak->time_to_sample_entries = entries;\n        trak->start_sample = start_sample;\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                       \"start_sample:%ui, new count:%uD\",\n                       trak->start_sample, count - rest);\n\n    } else {\n        ngx_mp4_set_32value(entry->count, rest);\n        data->last = (u_char *) (entry + 1);\n        trak->time_to_sample_entries -= entries - 1;\n        trak->end_sample = trak->start_sample + start_sample;\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                       \"end_sample:%ui, new count:%uD\",\n                       trak->end_sample, rest);\n    }\n\n    return NGX_OK;\n}\n\n\nstatic uint32_t\nngx_http_mp4_seek_key_frame(ngx_http_mp4_file_t *mp4, ngx_http_mp4_trak_t *trak,\n    uint32_t start_sample)\n{\n    uint32_t              key_prefix, sample, *entry, *end;\n    ngx_buf_t            *data;\n    ngx_http_mp4_conf_t  *conf;\n\n    conf = ngx_http_get_module_loc_conf(mp4->request, ngx_http_mp4_module);\n    if (!conf->start_key_frame) {\n        return 0;\n    }\n\n    data = trak->out[NGX_HTTP_MP4_STSS_DATA].buf;\n    if (data == NULL) {\n        return 0;\n    }\n\n    entry = (uint32_t *) data->pos;\n    end = (uint32_t *) data->last;\n\n    /* sync samples starts from 1 */\n    start_sample++;\n\n    key_prefix = 0;\n\n    while (entry < end) {\n        sample = ngx_mp4_get_32value(entry);\n        if (sample > start_sample) {\n            break;\n        }\n\n        key_prefix = start_sample - sample;\n        entry++;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                   \"mp4 key frame prefix:%uD\", key_prefix);\n\n    return key_prefix;\n}\n\n\ntypedef struct {\n    u_char    size[4];\n    u_char    name[4];\n    u_char    version[1];\n    u_char    flags[3];\n    u_char    entries[4];\n} ngx_http_mp4_stss_atom_t;\n\n\nstatic ngx_int_t\nngx_http_mp4_read_stss_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)\n{\n    u_char                    *atom_header, *atom_table, *atom_end;\n    uint32_t                   entries;\n    ngx_buf_t                 *atom, *data;\n    ngx_http_mp4_trak_t       *trak;\n    ngx_http_mp4_stss_atom_t  *stss_atom;\n\n    /* sync samples atom */\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, \"mp4 stss atom\");\n\n    atom_header = ngx_mp4_atom_header(mp4);\n    stss_atom = (ngx_http_mp4_stss_atom_t *) atom_header;\n    ngx_mp4_set_atom_name(stss_atom, 's', 't', 's', 's');\n\n    if (ngx_mp4_atom_data_size(ngx_http_mp4_stss_atom_t) > atom_data_size) {\n        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                      \"\\\"%s\\\" mp4 stss atom too small\", mp4->file.name.data);\n        return NGX_ERROR;\n    }\n\n    entries = ngx_mp4_get_32value(stss_atom->entries);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                   \"sync sample entries:%uD\", entries);\n\n    trak = ngx_mp4_last_trak(mp4);\n\n    if (trak->out[NGX_HTTP_MP4_STSS_ATOM].buf) {\n        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                      \"duplicate mp4 stss atom in \\\"%s\\\"\", mp4->file.name.data);\n        return NGX_ERROR;\n    }\n\n    trak->sync_samples_entries = entries;\n\n    atom_table = atom_header + sizeof(ngx_http_mp4_stss_atom_t);\n\n    atom = &trak->stss_atom_buf;\n    atom->temporary = 1;\n    atom->pos = atom_header;\n    atom->last = atom_table;\n\n    if (ngx_mp4_atom_data_size(ngx_http_mp4_stss_atom_t)\n        + entries * sizeof(uint32_t) > atom_data_size)\n    {\n        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                      \"\\\"%s\\\" mp4 stss atom too small\", mp4->file.name.data);\n        return NGX_ERROR;\n    }\n\n    atom_end = atom_table + entries * sizeof(uint32_t);\n\n    data = &trak->stss_data_buf;\n    data->temporary = 1;\n    data->pos = atom_table;\n    data->last = atom_end;\n\n    trak->out[NGX_HTTP_MP4_STSS_ATOM].buf = atom;\n    trak->out[NGX_HTTP_MP4_STSS_DATA].buf = data;\n\n    ngx_mp4_atom_next(mp4, atom_data_size);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_mp4_update_stss_atom(ngx_http_mp4_file_t *mp4,\n    ngx_http_mp4_trak_t *trak)\n{\n    size_t                     atom_size;\n    uint32_t                   sample, start_sample, *entry, *end;\n    ngx_buf_t                 *atom, *data;\n    ngx_http_mp4_stss_atom_t  *stss_atom;\n\n    /*\n     * mdia.minf.stbl.stss updating requires trak->start_sample\n     * from mdia.minf.stbl.stts which depends on value from mdia.mdhd\n     * atom which may reside after mdia.minf\n     */\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                   \"mp4 stss atom update\");\n\n    data = trak->out[NGX_HTTP_MP4_STSS_DATA].buf;\n\n    if (data == NULL) {\n        return NGX_OK;\n    }\n\n    ngx_http_mp4_crop_stss_data(mp4, trak, 1);\n    ngx_http_mp4_crop_stss_data(mp4, trak, 0);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                   \"sync sample entries:%uD\", trak->sync_samples_entries);\n\n    if (trak->sync_samples_entries) {\n        entry = (uint32_t *) data->pos;\n        end = (uint32_t *) data->last;\n\n        start_sample = trak->start_sample;\n\n        while (entry < end) {\n            sample = ngx_mp4_get_32value(entry);\n            sample -= start_sample;\n            ngx_mp4_set_32value(entry, sample);\n            entry++;\n        }\n\n    } else {\n        trak->out[NGX_HTTP_MP4_STSS_DATA].buf = NULL;\n    }\n\n    atom_size = sizeof(ngx_http_mp4_stss_atom_t) + (data->last - data->pos);\n    trak->size += atom_size;\n\n    atom = trak->out[NGX_HTTP_MP4_STSS_ATOM].buf;\n    stss_atom = (ngx_http_mp4_stss_atom_t *) atom->pos;\n\n    ngx_mp4_set_32value(stss_atom->size, atom_size);\n    ngx_mp4_set_32value(stss_atom->entries, trak->sync_samples_entries);\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_mp4_crop_stss_data(ngx_http_mp4_file_t *mp4,\n    ngx_http_mp4_trak_t *trak, ngx_uint_t start)\n{\n    uint32_t     sample, start_sample, *entry, *end;\n    ngx_buf_t   *data;\n    ngx_uint_t   entries;\n\n    /* sync samples starts from 1 */\n\n    if (start) {\n        start_sample = trak->start_sample + 1;\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                       \"mp4 stss crop start_sample:%uD\", start_sample);\n\n    } else if (mp4->length) {\n        start_sample = trak->end_sample + 1;\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                       \"mp4 stss crop end_sample:%uD\", start_sample);\n\n    } else {\n        return;\n    }\n\n    data = trak->out[NGX_HTTP_MP4_STSS_DATA].buf;\n\n    entries = trak->sync_samples_entries;\n    entry = (uint32_t *) data->pos;\n    end = (uint32_t *) data->last;\n\n    while (entry < end) {\n        sample = ngx_mp4_get_32value(entry);\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                       \"sync:%uD\", sample);\n\n        if (sample >= start_sample) {\n            goto found;\n        }\n\n        entries--;\n        entry++;\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                   \"sample is out of mp4 stss atom\");\n\nfound:\n\n    if (start) {\n        data->pos = (u_char *) entry;\n        trak->sync_samples_entries = entries;\n\n    } else {\n        data->last = (u_char *) entry;\n        trak->sync_samples_entries -= entries;\n    }\n}\n\n\ntypedef struct {\n    u_char    size[4];\n    u_char    name[4];\n    u_char    version[1];\n    u_char    flags[3];\n    u_char    entries[4];\n} ngx_mp4_ctts_atom_t;\n\ntypedef struct {\n    u_char    count[4];\n    u_char    offset[4];\n} ngx_mp4_ctts_entry_t;\n\n\nstatic ngx_int_t\nngx_http_mp4_read_ctts_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)\n{\n    u_char               *atom_header, *atom_table, *atom_end;\n    uint32_t              entries;\n    ngx_buf_t            *atom, *data;\n    ngx_mp4_ctts_atom_t  *ctts_atom;\n    ngx_http_mp4_trak_t  *trak;\n\n    /* composition offsets atom */\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, \"mp4 ctts atom\");\n\n    atom_header = ngx_mp4_atom_header(mp4);\n    ctts_atom = (ngx_mp4_ctts_atom_t *) atom_header;\n    ngx_mp4_set_atom_name(ctts_atom, 'c', 't', 't', 's');\n\n    if (ngx_mp4_atom_data_size(ngx_mp4_ctts_atom_t) > atom_data_size) {\n        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                      \"\\\"%s\\\" mp4 ctts atom too small\", mp4->file.name.data);\n        return NGX_ERROR;\n    }\n\n    entries = ngx_mp4_get_32value(ctts_atom->entries);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                   \"composition offset entries:%uD\", entries);\n\n    trak = ngx_mp4_last_trak(mp4);\n\n    if (trak->out[NGX_HTTP_MP4_CTTS_ATOM].buf) {\n        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                      \"duplicate mp4 ctts atom in \\\"%s\\\"\", mp4->file.name.data);\n        return NGX_ERROR;\n    }\n\n    trak->composition_offset_entries = entries;\n\n    atom_table = atom_header + sizeof(ngx_mp4_ctts_atom_t);\n\n    atom = &trak->ctts_atom_buf;\n    atom->temporary = 1;\n    atom->pos = atom_header;\n    atom->last = atom_table;\n\n    if (ngx_mp4_atom_data_size(ngx_mp4_ctts_atom_t)\n        + entries * sizeof(ngx_mp4_ctts_entry_t) > atom_data_size)\n    {\n        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                      \"\\\"%s\\\" mp4 ctts atom too small\", mp4->file.name.data);\n        return NGX_ERROR;\n    }\n\n    atom_end = atom_table + entries * sizeof(ngx_mp4_ctts_entry_t);\n\n    data = &trak->ctts_data_buf;\n    data->temporary = 1;\n    data->pos = atom_table;\n    data->last = atom_end;\n\n    trak->out[NGX_HTTP_MP4_CTTS_ATOM].buf = atom;\n    trak->out[NGX_HTTP_MP4_CTTS_DATA].buf = data;\n\n    ngx_mp4_atom_next(mp4, atom_data_size);\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_mp4_update_ctts_atom(ngx_http_mp4_file_t *mp4,\n    ngx_http_mp4_trak_t *trak)\n{\n    size_t                atom_size;\n    ngx_buf_t            *atom, *data;\n    ngx_mp4_ctts_atom_t  *ctts_atom;\n\n    /*\n     * mdia.minf.stbl.ctts updating requires trak->start_sample\n     * from mdia.minf.stbl.stts which depends on value from mdia.mdhd\n     * atom which may reside after mdia.minf\n     */\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                   \"mp4 ctts atom update\");\n\n    data = trak->out[NGX_HTTP_MP4_CTTS_DATA].buf;\n\n    if (data == NULL) {\n        return;\n    }\n\n    ngx_http_mp4_crop_ctts_data(mp4, trak, 1);\n    ngx_http_mp4_crop_ctts_data(mp4, trak, 0);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                   \"composition offset entries:%uD\",\n                   trak->composition_offset_entries);\n\n    if (trak->composition_offset_entries == 0) {\n        trak->out[NGX_HTTP_MP4_CTTS_ATOM].buf = NULL;\n        trak->out[NGX_HTTP_MP4_CTTS_DATA].buf = NULL;\n        return;\n    }\n\n    atom_size = sizeof(ngx_mp4_ctts_atom_t) + (data->last - data->pos);\n    trak->size += atom_size;\n\n    atom = trak->out[NGX_HTTP_MP4_CTTS_ATOM].buf;\n    ctts_atom = (ngx_mp4_ctts_atom_t *) atom->pos;\n\n    ngx_mp4_set_32value(ctts_atom->size, atom_size);\n    ngx_mp4_set_32value(ctts_atom->entries, trak->composition_offset_entries);\n\n    return;\n}\n\n\nstatic void\nngx_http_mp4_crop_ctts_data(ngx_http_mp4_file_t *mp4,\n    ngx_http_mp4_trak_t *trak, ngx_uint_t start)\n{\n    uint32_t               count, start_sample, rest;\n    ngx_buf_t             *data;\n    ngx_uint_t             entries;\n    ngx_mp4_ctts_entry_t  *entry, *end;\n\n    /* sync samples starts from 1 */\n\n    if (start) {\n        start_sample = trak->start_sample + 1;\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                       \"mp4 ctts crop start_sample:%uD\", start_sample);\n\n    } else if (mp4->length) {\n        start_sample = trak->end_sample - trak->start_sample + 1;\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                       \"mp4 ctts crop end_sample:%uD\", start_sample);\n\n    } else {\n        return;\n    }\n\n    data = trak->out[NGX_HTTP_MP4_CTTS_DATA].buf;\n\n    entries = trak->composition_offset_entries;\n    entry = (ngx_mp4_ctts_entry_t *) data->pos;\n    end = (ngx_mp4_ctts_entry_t *) data->last;\n\n    while (entry < end) {\n        count = ngx_mp4_get_32value(entry->count);\n\n        ngx_log_debug3(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                       \"sample:%uD, count:%uD, offset:%uD\",\n                       start_sample, count, ngx_mp4_get_32value(entry->offset));\n\n        if (start_sample <= count) {\n            rest = start_sample - 1;\n            goto found;\n        }\n\n        start_sample -= count;\n        entries--;\n        entry++;\n    }\n\n    if (start) {\n        data->pos = (u_char *) end;\n        trak->composition_offset_entries = 0;\n    }\n\n    return;\n\nfound:\n\n    if (start) {\n        ngx_mp4_set_32value(entry->count, count - rest);\n        data->pos = (u_char *) entry;\n        trak->composition_offset_entries = entries;\n\n    } else {\n        ngx_mp4_set_32value(entry->count, rest);\n        data->last = (u_char *) (entry + 1);\n        trak->composition_offset_entries -= entries - 1;\n    }\n}\n\n\ntypedef struct {\n    u_char    size[4];\n    u_char    name[4];\n    u_char    version[1];\n    u_char    flags[3];\n    u_char    entries[4];\n} ngx_mp4_stsc_atom_t;\n\n\nstatic ngx_int_t\nngx_http_mp4_read_stsc_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)\n{\n    u_char               *atom_header, *atom_table, *atom_end;\n    uint32_t              entries;\n    ngx_buf_t            *atom, *data;\n    ngx_mp4_stsc_atom_t  *stsc_atom;\n    ngx_http_mp4_trak_t  *trak;\n\n    /* sample-to-chunk atom */\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, \"mp4 stsc atom\");\n\n    atom_header = ngx_mp4_atom_header(mp4);\n    stsc_atom = (ngx_mp4_stsc_atom_t *) atom_header;\n    ngx_mp4_set_atom_name(stsc_atom, 's', 't', 's', 'c');\n\n    if (ngx_mp4_atom_data_size(ngx_mp4_stsc_atom_t) > atom_data_size) {\n        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                      \"\\\"%s\\\" mp4 stsc atom too small\", mp4->file.name.data);\n        return NGX_ERROR;\n    }\n\n    entries = ngx_mp4_get_32value(stsc_atom->entries);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                   \"sample-to-chunk entries:%uD\", entries);\n\n    if (ngx_mp4_atom_data_size(ngx_mp4_stsc_atom_t)\n        + entries * sizeof(ngx_mp4_stsc_entry_t) > atom_data_size)\n    {\n        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                      \"\\\"%s\\\" mp4 stsc atom too small\", mp4->file.name.data);\n        return NGX_ERROR;\n    }\n\n    atom_table = atom_header + sizeof(ngx_mp4_stsc_atom_t);\n    atom_end = atom_table + entries * sizeof(ngx_mp4_stsc_entry_t);\n\n    trak = ngx_mp4_last_trak(mp4);\n\n    if (trak->out[NGX_HTTP_MP4_STSC_ATOM].buf) {\n        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                      \"duplicate mp4 stsc atom in \\\"%s\\\"\", mp4->file.name.data);\n        return NGX_ERROR;\n    }\n\n    trak->sample_to_chunk_entries = entries;\n\n    atom = &trak->stsc_atom_buf;\n    atom->temporary = 1;\n    atom->pos = atom_header;\n    atom->last = atom_table;\n\n    data = &trak->stsc_data_buf;\n    data->temporary = 1;\n    data->pos = atom_table;\n    data->last = atom_end;\n\n    trak->out[NGX_HTTP_MP4_STSC_ATOM].buf = atom;\n    trak->out[NGX_HTTP_MP4_STSC_DATA].buf = data;\n\n    ngx_mp4_atom_next(mp4, atom_data_size);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_mp4_update_stsc_atom(ngx_http_mp4_file_t *mp4,\n    ngx_http_mp4_trak_t *trak)\n{\n    size_t                 atom_size;\n    uint32_t               chunk;\n    ngx_buf_t             *atom, *data;\n    ngx_mp4_stsc_atom_t   *stsc_atom;\n    ngx_mp4_stsc_entry_t  *entry, *end;\n\n    /*\n     * mdia.minf.stbl.stsc updating requires trak->start_sample\n     * from mdia.minf.stbl.stts which depends on value from mdia.mdhd\n     * atom which may reside after mdia.minf\n     */\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                   \"mp4 stsc atom update\");\n\n    data = trak->out[NGX_HTTP_MP4_STSC_DATA].buf;\n\n    if (data == NULL) {\n        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                      \"no mp4 stsc atoms were found in \\\"%s\\\"\",\n                      mp4->file.name.data);\n        return NGX_ERROR;\n    }\n\n    if (trak->sample_to_chunk_entries == 0) {\n        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                      \"zero number of entries in stsc atom in \\\"%s\\\"\",\n                      mp4->file.name.data);\n        return NGX_ERROR;\n    }\n\n    if (ngx_http_mp4_crop_stsc_data(mp4, trak, 1) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    if (ngx_http_mp4_crop_stsc_data(mp4, trak, 0) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                   \"sample-to-chunk entries:%uD\",\n                   trak->sample_to_chunk_entries);\n\n    entry = (ngx_mp4_stsc_entry_t *) data->pos;\n    end = (ngx_mp4_stsc_entry_t *) data->last;\n\n    while (entry < end) {\n        chunk = ngx_mp4_get_32value(entry->chunk);\n        chunk -= trak->start_chunk;\n        ngx_mp4_set_32value(entry->chunk, chunk);\n        entry++;\n    }\n\n    atom_size = sizeof(ngx_mp4_stsc_atom_t)\n                + trak->sample_to_chunk_entries * sizeof(ngx_mp4_stsc_entry_t);\n\n    trak->size += atom_size;\n\n    atom = trak->out[NGX_HTTP_MP4_STSC_ATOM].buf;\n    stsc_atom = (ngx_mp4_stsc_atom_t *) atom->pos;\n\n    ngx_mp4_set_32value(stsc_atom->size, atom_size);\n    ngx_mp4_set_32value(stsc_atom->entries, trak->sample_to_chunk_entries);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_mp4_crop_stsc_data(ngx_http_mp4_file_t *mp4,\n    ngx_http_mp4_trak_t *trak, ngx_uint_t start)\n{\n    uint32_t               start_sample, chunk, samples, id, next_chunk, n,\n                           prev_samples;\n    ngx_buf_t             *data, *buf;\n    ngx_uint_t             entries, target_chunk, chunk_samples;\n    ngx_mp4_stsc_entry_t  *entry, *end, *first;\n\n    entries = trak->sample_to_chunk_entries - 1;\n\n    if (start) {\n        start_sample = (uint32_t) trak->start_sample;\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                       \"mp4 stsc crop start_sample:%uD\", start_sample);\n\n    } else if (mp4->length) {\n        start_sample = (uint32_t) (trak->end_sample - trak->start_sample);\n        samples = 0;\n\n        data = trak->out[NGX_HTTP_MP4_STSC_START].buf;\n\n        if (data) {\n            entry = (ngx_mp4_stsc_entry_t *) data->pos;\n            samples = ngx_mp4_get_32value(entry->samples);\n            entries--;\n\n            if (samples > start_sample) {\n                samples = start_sample;\n                ngx_mp4_set_32value(entry->samples, samples);\n            }\n\n            start_sample -= samples;\n        }\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                       \"mp4 stsc crop end_sample:%uD, ext_samples:%uD\",\n                       start_sample, samples);\n\n    } else {\n        return NGX_OK;\n    }\n\n    data = trak->out[NGX_HTTP_MP4_STSC_DATA].buf;\n\n    entry = (ngx_mp4_stsc_entry_t *) data->pos;\n    end = (ngx_mp4_stsc_entry_t *) data->last;\n\n    chunk = ngx_mp4_get_32value(entry->chunk);\n    samples = ngx_mp4_get_32value(entry->samples);\n    id = ngx_mp4_get_32value(entry->id);\n    prev_samples = 0;\n    entry++;\n\n    while (entry < end) {\n\n        next_chunk = ngx_mp4_get_32value(entry->chunk);\n\n        ngx_log_debug5(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                       \"sample:%uD, chunk:%uD, chunks:%uD, \"\n                       \"samples:%uD, id:%uD\",\n                       start_sample, chunk, next_chunk - chunk, samples, id);\n\n        n = (next_chunk - chunk) * samples;\n\n        if (start_sample < n) {\n            goto found;\n        }\n\n        start_sample -= n;\n\n        prev_samples = samples;\n        chunk = next_chunk;\n        samples = ngx_mp4_get_32value(entry->samples);\n        id = ngx_mp4_get_32value(entry->id);\n        entries--;\n        entry++;\n    }\n\n    next_chunk = trak->chunks + 1;\n\n    ngx_log_debug4(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                   \"sample:%uD, chunk:%uD, chunks:%uD, samples:%uD\",\n                   start_sample, chunk, next_chunk - chunk, samples);\n\n    n = (next_chunk - chunk) * samples;\n\n    if (start_sample > n) {\n        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                      \"%s time is out mp4 stsc chunks in \\\"%s\\\"\",\n                      start ? \"start\" : \"end\", mp4->file.name.data);\n        return NGX_ERROR;\n    }\n\nfound:\n\n    entries++;\n    entry--;\n\n    if (samples == 0) {\n        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                      \"zero number of samples in \\\"%s\\\"\",\n                      mp4->file.name.data);\n        return NGX_ERROR;\n    }\n\n    target_chunk = chunk - 1;\n    target_chunk += start_sample / samples;\n    chunk_samples = start_sample % samples;\n\n    if (start) {\n        data->pos = (u_char *) entry;\n\n        trak->sample_to_chunk_entries = entries;\n        trak->start_chunk = target_chunk;\n        trak->start_chunk_samples = chunk_samples;\n\n        ngx_mp4_set_32value(entry->chunk, trak->start_chunk + 1);\n\n        samples -= chunk_samples;\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                       \"start_chunk:%ui, start_chunk_samples:%ui\",\n                       trak->start_chunk, trak->start_chunk_samples);\n\n    } else {\n        if (start_sample) {\n            data->last = (u_char *) (entry + 1);\n            trak->sample_to_chunk_entries -= entries - 1;\n            trak->end_chunk_samples = samples;\n\n        } else {\n            data->last = (u_char *) entry;\n            trak->sample_to_chunk_entries -= entries;\n            trak->end_chunk_samples = prev_samples;\n        }\n\n        if (chunk_samples) {\n            trak->end_chunk = target_chunk + 1;\n            trak->end_chunk_samples = chunk_samples;\n\n        } else {\n            trak->end_chunk = target_chunk;\n        }\n\n        samples = chunk_samples;\n        next_chunk = chunk + 1;\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                       \"end_chunk:%ui, end_chunk_samples:%ui\",\n                       trak->end_chunk, trak->end_chunk_samples);\n    }\n\n    if (chunk_samples && next_chunk - target_chunk == 2) {\n\n        ngx_mp4_set_32value(entry->samples, samples);\n\n    } else if (chunk_samples && start) {\n\n        first = &trak->stsc_start_chunk_entry;\n        ngx_mp4_set_32value(first->chunk, 1);\n        ngx_mp4_set_32value(first->samples, samples);\n        ngx_mp4_set_32value(first->id, id);\n\n        buf = &trak->stsc_start_chunk_buf;\n        buf->temporary = 1;\n        buf->pos = (u_char *) first;\n        buf->last = (u_char *) first + sizeof(ngx_mp4_stsc_entry_t);\n\n        trak->out[NGX_HTTP_MP4_STSC_START].buf = buf;\n\n        ngx_mp4_set_32value(entry->chunk, trak->start_chunk + 2);\n\n        trak->sample_to_chunk_entries++;\n\n    } else if (chunk_samples) {\n\n        first = &trak->stsc_end_chunk_entry;\n        ngx_mp4_set_32value(first->chunk, trak->end_chunk - trak->start_chunk);\n        ngx_mp4_set_32value(first->samples, samples);\n        ngx_mp4_set_32value(first->id, id);\n\n        buf = &trak->stsc_end_chunk_buf;\n        buf->temporary = 1;\n        buf->pos = (u_char *) first;\n        buf->last = (u_char *) first + sizeof(ngx_mp4_stsc_entry_t);\n\n        trak->out[NGX_HTTP_MP4_STSC_END].buf = buf;\n\n        trak->sample_to_chunk_entries++;\n    }\n\n    return NGX_OK;\n}\n\n\ntypedef struct {\n    u_char    size[4];\n    u_char    name[4];\n    u_char    version[1];\n    u_char    flags[3];\n    u_char    uniform_size[4];\n    u_char    entries[4];\n} ngx_mp4_stsz_atom_t;\n\n\nstatic ngx_int_t\nngx_http_mp4_read_stsz_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)\n{\n    u_char               *atom_header, *atom_table, *atom_end;\n    size_t                atom_size;\n    uint32_t              entries, size;\n    ngx_buf_t            *atom, *data;\n    ngx_mp4_stsz_atom_t  *stsz_atom;\n    ngx_http_mp4_trak_t  *trak;\n\n    /* sample sizes atom */\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, \"mp4 stsz atom\");\n\n    atom_header = ngx_mp4_atom_header(mp4);\n    stsz_atom = (ngx_mp4_stsz_atom_t *) atom_header;\n    ngx_mp4_set_atom_name(stsz_atom, 's', 't', 's', 'z');\n\n    if (ngx_mp4_atom_data_size(ngx_mp4_stsz_atom_t) > atom_data_size) {\n        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                      \"\\\"%s\\\" mp4 stsz atom too small\", mp4->file.name.data);\n        return NGX_ERROR;\n    }\n\n    size = ngx_mp4_get_32value(stsz_atom->uniform_size);\n    entries = ngx_mp4_get_32value(stsz_atom->entries);\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                   \"sample uniform size:%uD, entries:%uD\", size, entries);\n\n    trak = ngx_mp4_last_trak(mp4);\n\n    if (trak->out[NGX_HTTP_MP4_STSZ_ATOM].buf) {\n        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                      \"duplicate mp4 stsz atom in \\\"%s\\\"\", mp4->file.name.data);\n        return NGX_ERROR;\n    }\n\n    trak->sample_sizes_entries = entries;\n\n    atom_table = atom_header + sizeof(ngx_mp4_stsz_atom_t);\n\n    atom = &trak->stsz_atom_buf;\n    atom->temporary = 1;\n    atom->pos = atom_header;\n    atom->last = atom_table;\n\n    trak->out[NGX_HTTP_MP4_STSZ_ATOM].buf = atom;\n\n    if (size == 0) {\n        if (ngx_mp4_atom_data_size(ngx_mp4_stsz_atom_t)\n            + entries * sizeof(uint32_t) > atom_data_size)\n        {\n            ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                          \"\\\"%s\\\" mp4 stsz atom too small\",\n                          mp4->file.name.data);\n            return NGX_ERROR;\n        }\n\n        atom_end = atom_table + entries * sizeof(uint32_t);\n\n        data = &trak->stsz_data_buf;\n        data->temporary = 1;\n        data->pos = atom_table;\n        data->last = atom_end;\n\n        trak->out[NGX_HTTP_MP4_STSZ_DATA].buf = data;\n\n    } else {\n        /* if size != 0 then all samples are the same size */\n        /* TODO : chunk samples */\n        atom_size = sizeof(ngx_mp4_atom_header_t) + (size_t) atom_data_size;\n        ngx_mp4_set_32value(atom_header, atom_size);\n        trak->size += atom_size;\n    }\n\n    ngx_mp4_atom_next(mp4, atom_data_size);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_mp4_update_stsz_atom(ngx_http_mp4_file_t *mp4,\n    ngx_http_mp4_trak_t *trak)\n{\n    size_t                atom_size;\n    uint32_t             *pos, *end, entries;\n    ngx_buf_t            *atom, *data;\n    ngx_mp4_stsz_atom_t  *stsz_atom;\n\n    /*\n     * mdia.minf.stbl.stsz updating requires trak->start_sample\n     * from mdia.minf.stbl.stts which depends on value from mdia.mdhd\n     * atom which may reside after mdia.minf\n     */\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                   \"mp4 stsz atom update\");\n\n    data = trak->out[NGX_HTTP_MP4_STSZ_DATA].buf;\n\n    if (data) {\n        entries = trak->sample_sizes_entries;\n\n        if (trak->start_sample > entries) {\n            ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                          \"start time is out mp4 stsz samples in \\\"%s\\\"\",\n                          mp4->file.name.data);\n            return NGX_ERROR;\n        }\n\n        entries -= trak->start_sample;\n        data->pos += trak->start_sample * sizeof(uint32_t);\n        end = (uint32_t *) data->pos;\n\n        for (pos = end - trak->start_chunk_samples; pos < end; pos++) {\n            trak->start_chunk_samples_size += ngx_mp4_get_32value(pos);\n        }\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                       \"chunk samples sizes:%uL\",\n                       trak->start_chunk_samples_size);\n\n        if (trak->start_chunk_samples_size > (uint64_t) mp4->end) {\n            ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                          \"too large mp4 start samples size in \\\"%s\\\"\",\n                          mp4->file.name.data);\n            return NGX_ERROR;\n        }\n\n        if (mp4->length) {\n            if (trak->end_sample - trak->start_sample > entries) {\n                ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                              \"end time is out mp4 stsz samples in \\\"%s\\\"\",\n                              mp4->file.name.data);\n                return NGX_ERROR;\n            }\n\n            entries = trak->end_sample - trak->start_sample;\n            data->last = data->pos + entries * sizeof(uint32_t);\n            end = (uint32_t *) data->last;\n\n            for (pos = end - trak->end_chunk_samples; pos < end; pos++) {\n                trak->end_chunk_samples_size += ngx_mp4_get_32value(pos);\n            }\n\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                           \"mp4 stsz end_chunk_samples_size:%uL\",\n                           trak->end_chunk_samples_size);\n\n            if (trak->end_chunk_samples_size > (uint64_t) mp4->end) {\n                ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                              \"too large mp4 end samples size in \\\"%s\\\"\",\n                              mp4->file.name.data);\n                return NGX_ERROR;\n            }\n        }\n\n        atom_size = sizeof(ngx_mp4_stsz_atom_t) + (data->last - data->pos);\n        trak->size += atom_size;\n\n        atom = trak->out[NGX_HTTP_MP4_STSZ_ATOM].buf;\n        stsz_atom = (ngx_mp4_stsz_atom_t *) atom->pos;\n\n        ngx_mp4_set_32value(stsz_atom->size, atom_size);\n        ngx_mp4_set_32value(stsz_atom->entries, entries);\n    }\n\n    return NGX_OK;\n}\n\n\ntypedef struct {\n    u_char    size[4];\n    u_char    name[4];\n    u_char    version[1];\n    u_char    flags[3];\n    u_char    entries[4];\n} ngx_mp4_stco_atom_t;\n\n\nstatic ngx_int_t\nngx_http_mp4_read_stco_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)\n{\n    u_char               *atom_header, *atom_table, *atom_end;\n    uint32_t              entries;\n    ngx_buf_t            *atom, *data;\n    ngx_mp4_stco_atom_t  *stco_atom;\n    ngx_http_mp4_trak_t  *trak;\n\n    /* chunk offsets atom */\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, \"mp4 stco atom\");\n\n    atom_header = ngx_mp4_atom_header(mp4);\n    stco_atom = (ngx_mp4_stco_atom_t *) atom_header;\n    ngx_mp4_set_atom_name(stco_atom, 's', 't', 'c', 'o');\n\n    if (ngx_mp4_atom_data_size(ngx_mp4_stco_atom_t) > atom_data_size) {\n        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                      \"\\\"%s\\\" mp4 stco atom too small\", mp4->file.name.data);\n        return NGX_ERROR;\n    }\n\n    entries = ngx_mp4_get_32value(stco_atom->entries);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, \"chunks:%uD\", entries);\n\n    if (ngx_mp4_atom_data_size(ngx_mp4_stco_atom_t)\n        + entries * sizeof(uint32_t) > atom_data_size)\n    {\n        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                      \"\\\"%s\\\" mp4 stco atom too small\", mp4->file.name.data);\n        return NGX_ERROR;\n    }\n\n    atom_table = atom_header + sizeof(ngx_mp4_stco_atom_t);\n    atom_end = atom_table + entries * sizeof(uint32_t);\n\n    trak = ngx_mp4_last_trak(mp4);\n\n    if (trak->out[NGX_HTTP_MP4_STCO_ATOM].buf\n        || trak->out[NGX_HTTP_MP4_CO64_ATOM].buf)\n    {\n        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                      \"duplicate mp4 stco/co64 atom in \\\"%s\\\"\",\n                      mp4->file.name.data);\n        return NGX_ERROR;\n    }\n\n    trak->chunks = entries;\n\n    atom = &trak->stco_atom_buf;\n    atom->temporary = 1;\n    atom->pos = atom_header;\n    atom->last = atom_table;\n\n    data = &trak->stco_data_buf;\n    data->temporary = 1;\n    data->pos = atom_table;\n    data->last = atom_end;\n\n    trak->out[NGX_HTTP_MP4_STCO_ATOM].buf = atom;\n    trak->out[NGX_HTTP_MP4_STCO_DATA].buf = data;\n\n    ngx_mp4_atom_next(mp4, atom_data_size);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_mp4_update_stco_atom(ngx_http_mp4_file_t *mp4,\n    ngx_http_mp4_trak_t *trak)\n{\n    size_t                atom_size;\n    uint32_t              entries;\n    uint64_t              chunk_offset, samples_size;\n    ngx_buf_t            *atom, *data;\n    ngx_mp4_stco_atom_t  *stco_atom;\n\n    /*\n     * mdia.minf.stbl.stco updating requires trak->start_chunk\n     * from mdia.minf.stbl.stsc which depends on value from mdia.mdhd\n     * atom which may reside after mdia.minf\n     */\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                   \"mp4 stco atom update\");\n\n    data = trak->out[NGX_HTTP_MP4_STCO_DATA].buf;\n\n    if (data == NULL) {\n        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                      \"no mp4 stco atoms were found in \\\"%s\\\"\",\n                      mp4->file.name.data);\n        return NGX_ERROR;\n    }\n\n    if (trak->start_chunk > trak->chunks) {\n        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                      \"start time is out mp4 stco chunks in \\\"%s\\\"\",\n                      mp4->file.name.data);\n        return NGX_ERROR;\n    }\n\n    data->pos += trak->start_chunk * sizeof(uint32_t);\n\n    chunk_offset = ngx_mp4_get_32value(data->pos);\n    samples_size = trak->start_chunk_samples_size;\n\n    if (chunk_offset > (uint64_t) mp4->end - samples_size\n        || chunk_offset + samples_size > NGX_MAX_UINT32_VALUE)\n    {\n        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                      \"too large chunk offset in \\\"%s\\\"\",\n                      mp4->file.name.data);\n        return NGX_ERROR;\n    }\n\n    trak->start_offset = chunk_offset + samples_size;\n    ngx_mp4_set_32value(data->pos, trak->start_offset);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                   \"start chunk offset:%O\", trak->start_offset);\n\n    if (mp4->length) {\n\n        if (trak->end_chunk > trak->chunks) {\n            ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                          \"end time is out mp4 stco chunks in \\\"%s\\\"\",\n                          mp4->file.name.data);\n            return NGX_ERROR;\n        }\n\n        entries = trak->end_chunk - trak->start_chunk;\n        data->last = data->pos + entries * sizeof(uint32_t);\n\n        if (entries) {\n            chunk_offset = ngx_mp4_get_32value(data->last - sizeof(uint32_t));\n            samples_size = trak->end_chunk_samples_size;\n\n            if (chunk_offset > (uint64_t) mp4->end - samples_size\n                || chunk_offset + samples_size > NGX_MAX_UINT32_VALUE)\n            {\n                ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                              \"too large chunk offset in \\\"%s\\\"\",\n                              mp4->file.name.data);\n                return NGX_ERROR;\n            }\n\n            trak->end_offset = chunk_offset + samples_size;\n\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                           \"end chunk offset:%O\", trak->end_offset);\n        }\n\n    } else {\n        entries = trak->chunks - trak->start_chunk;\n        trak->end_offset = mp4->mdat_data.buf->file_last;\n    }\n\n    if (entries == 0) {\n        trak->start_offset = mp4->end;\n        trak->end_offset = 0;\n    }\n\n    atom_size = sizeof(ngx_mp4_stco_atom_t) + (data->last - data->pos);\n    trak->size += atom_size;\n\n    atom = trak->out[NGX_HTTP_MP4_STCO_ATOM].buf;\n    stco_atom = (ngx_mp4_stco_atom_t *) atom->pos;\n\n    ngx_mp4_set_32value(stco_atom->size, atom_size);\n    ngx_mp4_set_32value(stco_atom->entries, entries);\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_mp4_adjust_stco_atom(ngx_http_mp4_file_t *mp4,\n    ngx_http_mp4_trak_t *trak, int32_t adjustment)\n{\n    uint32_t    offset, *entry, *end;\n    ngx_buf_t  *data;\n\n    /*\n     * moov.trak.mdia.minf.stbl.stco adjustment requires\n     * minimal start offset of all traks and new moov atom size\n     */\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                   \"mp4 stco atom adjustment\");\n\n    data = trak->out[NGX_HTTP_MP4_STCO_DATA].buf;\n    entry = (uint32_t *) data->pos;\n    end = (uint32_t *) data->last;\n\n    while (entry < end) {\n        offset = ngx_mp4_get_32value(entry);\n        offset += adjustment;\n        ngx_mp4_set_32value(entry, offset);\n        entry++;\n    }\n}\n\n\ntypedef struct {\n    u_char    size[4];\n    u_char    name[4];\n    u_char    version[1];\n    u_char    flags[3];\n    u_char    entries[4];\n} ngx_mp4_co64_atom_t;\n\n\nstatic ngx_int_t\nngx_http_mp4_read_co64_atom(ngx_http_mp4_file_t *mp4, uint64_t atom_data_size)\n{\n    u_char               *atom_header, *atom_table, *atom_end;\n    uint32_t              entries;\n    ngx_buf_t            *atom, *data;\n    ngx_mp4_co64_atom_t  *co64_atom;\n    ngx_http_mp4_trak_t  *trak;\n\n    /* chunk offsets atom */\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, \"mp4 co64 atom\");\n\n    atom_header = ngx_mp4_atom_header(mp4);\n    co64_atom = (ngx_mp4_co64_atom_t *) atom_header;\n    ngx_mp4_set_atom_name(co64_atom, 'c', 'o', '6', '4');\n\n    if (ngx_mp4_atom_data_size(ngx_mp4_co64_atom_t) > atom_data_size) {\n        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                      \"\\\"%s\\\" mp4 co64 atom too small\", mp4->file.name.data);\n        return NGX_ERROR;\n    }\n\n    entries = ngx_mp4_get_32value(co64_atom->entries);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0, \"chunks:%uD\", entries);\n\n    if (ngx_mp4_atom_data_size(ngx_mp4_co64_atom_t)\n        + entries * sizeof(uint64_t) > atom_data_size)\n    {\n        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                      \"\\\"%s\\\" mp4 co64 atom too small\", mp4->file.name.data);\n        return NGX_ERROR;\n    }\n\n    atom_table = atom_header + sizeof(ngx_mp4_co64_atom_t);\n    atom_end = atom_table + entries * sizeof(uint64_t);\n\n    trak = ngx_mp4_last_trak(mp4);\n\n    if (trak->out[NGX_HTTP_MP4_STCO_ATOM].buf\n        || trak->out[NGX_HTTP_MP4_CO64_ATOM].buf)\n    {\n        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                      \"duplicate mp4 stco/co64 atom in \\\"%s\\\"\",\n                      mp4->file.name.data);\n        return NGX_ERROR;\n    }\n\n    trak->chunks = entries;\n\n    atom = &trak->co64_atom_buf;\n    atom->temporary = 1;\n    atom->pos = atom_header;\n    atom->last = atom_table;\n\n    data = &trak->co64_data_buf;\n    data->temporary = 1;\n    data->pos = atom_table;\n    data->last = atom_end;\n\n    trak->out[NGX_HTTP_MP4_CO64_ATOM].buf = atom;\n    trak->out[NGX_HTTP_MP4_CO64_DATA].buf = data;\n\n    ngx_mp4_atom_next(mp4, atom_data_size);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_mp4_update_co64_atom(ngx_http_mp4_file_t *mp4,\n    ngx_http_mp4_trak_t *trak)\n{\n    size_t                atom_size;\n    uint64_t              entries, chunk_offset, samples_size;\n    ngx_buf_t            *atom, *data;\n    ngx_mp4_co64_atom_t  *co64_atom;\n\n    /*\n     * mdia.minf.stbl.co64 updating requires trak->start_chunk\n     * from mdia.minf.stbl.stsc which depends on value from mdia.mdhd\n     * atom which may reside after mdia.minf\n     */\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                   \"mp4 co64 atom update\");\n\n    data = trak->out[NGX_HTTP_MP4_CO64_DATA].buf;\n\n    if (data == NULL) {\n        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                      \"no mp4 co64 atoms were found in \\\"%s\\\"\",\n                      mp4->file.name.data);\n        return NGX_ERROR;\n    }\n\n    if (trak->start_chunk > trak->chunks) {\n        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                      \"start time is out mp4 co64 chunks in \\\"%s\\\"\",\n                      mp4->file.name.data);\n        return NGX_ERROR;\n    }\n\n    data->pos += trak->start_chunk * sizeof(uint64_t);\n\n    chunk_offset = ngx_mp4_get_64value(data->pos);\n    samples_size = trak->start_chunk_samples_size;\n\n    if (chunk_offset > (uint64_t) mp4->end - samples_size) {\n        ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                      \"too large chunk offset in \\\"%s\\\"\",\n                      mp4->file.name.data);\n        return NGX_ERROR;\n    }\n\n    trak->start_offset = chunk_offset + samples_size;\n    ngx_mp4_set_64value(data->pos, trak->start_offset);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                   \"start chunk offset:%O\", trak->start_offset);\n\n    if (mp4->length) {\n\n        if (trak->end_chunk > trak->chunks) {\n            ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                          \"end time is out mp4 co64 chunks in \\\"%s\\\"\",\n                          mp4->file.name.data);\n            return NGX_ERROR;\n        }\n\n        entries = trak->end_chunk - trak->start_chunk;\n        data->last = data->pos + entries * sizeof(uint64_t);\n\n        if (entries) {\n            chunk_offset = ngx_mp4_get_64value(data->last - sizeof(uint64_t));\n            samples_size = trak->end_chunk_samples_size;\n\n            if (chunk_offset > (uint64_t) mp4->end - samples_size) {\n                ngx_log_error(NGX_LOG_ERR, mp4->file.log, 0,\n                              \"too large chunk offset in \\\"%s\\\"\",\n                              mp4->file.name.data);\n                return NGX_ERROR;\n            }\n\n            trak->end_offset = chunk_offset + samples_size;\n\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                           \"end chunk offset:%O\", trak->end_offset);\n        }\n\n    } else {\n        entries = trak->chunks - trak->start_chunk;\n        trak->end_offset = mp4->mdat_data.buf->file_last;\n    }\n\n    if (entries == 0) {\n        trak->start_offset = mp4->end;\n        trak->end_offset = 0;\n    }\n\n    atom_size = sizeof(ngx_mp4_co64_atom_t) + (data->last - data->pos);\n    trak->size += atom_size;\n\n    atom = trak->out[NGX_HTTP_MP4_CO64_ATOM].buf;\n    co64_atom = (ngx_mp4_co64_atom_t *) atom->pos;\n\n    ngx_mp4_set_32value(co64_atom->size, atom_size);\n    ngx_mp4_set_32value(co64_atom->entries, entries);\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_mp4_adjust_co64_atom(ngx_http_mp4_file_t *mp4,\n    ngx_http_mp4_trak_t *trak, off_t adjustment)\n{\n    uint64_t    offset, *entry, *end;\n    ngx_buf_t  *data;\n\n    /*\n     * moov.trak.mdia.minf.stbl.co64 adjustment requires\n     * minimal start offset of all traks and new moov atom size\n     */\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, mp4->file.log, 0,\n                   \"mp4 co64 atom adjustment\");\n\n    data = trak->out[NGX_HTTP_MP4_CO64_DATA].buf;\n    entry = (uint64_t *) data->pos;\n    end = (uint64_t *) data->last;\n\n    while (entry < end) {\n        offset = ngx_mp4_get_64value(entry);\n        offset += adjustment;\n        ngx_mp4_set_64value(entry, offset);\n        entry++;\n    }\n}\n\n\nstatic char *\nngx_http_mp4(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_core_loc_conf_t  *clcf;\n\n    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);\n    clcf->handler = ngx_http_mp4_handler;\n\n    return NGX_CONF_OK;\n}\n\n\nstatic void *\nngx_http_mp4_create_conf(ngx_conf_t *cf)\n{\n    ngx_http_mp4_conf_t  *conf;\n\n    conf = ngx_palloc(cf->pool, sizeof(ngx_http_mp4_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    conf->buffer_size = NGX_CONF_UNSET_SIZE;\n    conf->max_buffer_size = NGX_CONF_UNSET_SIZE;\n    conf->start_key_frame = NGX_CONF_UNSET;\n\n    return conf;\n}\n\n\nstatic char *\nngx_http_mp4_merge_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_http_mp4_conf_t *prev = parent;\n    ngx_http_mp4_conf_t *conf = child;\n\n    ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size, 512 * 1024);\n    ngx_conf_merge_size_value(conf->max_buffer_size, prev->max_buffer_size,\n                              10 * 1024 * 1024);\n    ngx_conf_merge_value(conf->start_key_frame, prev->start_key_frame, 0);\n\n    return NGX_CONF_OK;\n}\n"
  },
  {
    "path": "src/http/modules/ngx_http_not_modified_filter_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\nstatic ngx_uint_t ngx_http_test_if_unmodified(ngx_http_request_t *r);\nstatic ngx_uint_t ngx_http_test_if_modified(ngx_http_request_t *r);\nstatic ngx_uint_t ngx_http_test_if_match(ngx_http_request_t *r,\n    ngx_table_elt_t *header, ngx_uint_t weak);\nstatic ngx_int_t ngx_http_not_modified_filter_init(ngx_conf_t *cf);\n\n\nstatic ngx_http_module_t  ngx_http_not_modified_filter_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    ngx_http_not_modified_filter_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\nngx_module_t  ngx_http_not_modified_filter_module = {\n    NGX_MODULE_V1,\n    &ngx_http_not_modified_filter_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_http_output_header_filter_pt  ngx_http_next_header_filter;\n\n\nstatic ngx_int_t\nngx_http_not_modified_header_filter(ngx_http_request_t *r)\n{\n    if (r->headers_out.status != NGX_HTTP_OK\n        || r != r->main\n        || r->disable_not_modified)\n    {\n        return ngx_http_next_header_filter(r);\n    }\n\n    if (r->headers_in.if_unmodified_since\n        && !ngx_http_test_if_unmodified(r))\n    {\n        return ngx_http_filter_finalize_request(r, NULL,\n                                                NGX_HTTP_PRECONDITION_FAILED);\n    }\n\n    if (r->headers_in.if_match\n        && !ngx_http_test_if_match(r, r->headers_in.if_match, 0))\n    {\n        return ngx_http_filter_finalize_request(r, NULL,\n                                                NGX_HTTP_PRECONDITION_FAILED);\n    }\n\n    if (r->headers_in.if_modified_since || r->headers_in.if_none_match) {\n\n        if (r->headers_in.if_modified_since\n            && ngx_http_test_if_modified(r))\n        {\n            return ngx_http_next_header_filter(r);\n        }\n\n        if (r->headers_in.if_none_match\n            && !ngx_http_test_if_match(r, r->headers_in.if_none_match, 1))\n        {\n            return ngx_http_next_header_filter(r);\n        }\n\n        /* not modified */\n\n        r->headers_out.status = NGX_HTTP_NOT_MODIFIED;\n        r->headers_out.status_line.len = 0;\n        r->headers_out.content_type.len = 0;\n        ngx_http_clear_content_length(r);\n        ngx_http_clear_accept_ranges(r);\n\n        if (r->headers_out.content_encoding) {\n            r->headers_out.content_encoding->hash = 0;\n            r->headers_out.content_encoding = NULL;\n        }\n\n        return ngx_http_next_header_filter(r);\n    }\n\n    return ngx_http_next_header_filter(r);\n}\n\n\nstatic ngx_uint_t\nngx_http_test_if_unmodified(ngx_http_request_t *r)\n{\n    time_t  iums;\n\n    if (r->headers_out.last_modified_time == (time_t) -1) {\n        return 0;\n    }\n\n    iums = ngx_parse_http_time(r->headers_in.if_unmodified_since->value.data,\n                               r->headers_in.if_unmodified_since->value.len);\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                 \"http iums:%T lm:%T\", iums, r->headers_out.last_modified_time);\n\n    if (iums >= r->headers_out.last_modified_time) {\n        return 1;\n    }\n\n    return 0;\n}\n\n\nstatic ngx_uint_t\nngx_http_test_if_modified(ngx_http_request_t *r)\n{\n    time_t                     ims;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    if (r->headers_out.last_modified_time == (time_t) -1) {\n        return 1;\n    }\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    if (clcf->if_modified_since == NGX_HTTP_IMS_OFF) {\n        return 1;\n    }\n\n    ims = ngx_parse_http_time(r->headers_in.if_modified_since->value.data,\n                              r->headers_in.if_modified_since->value.len);\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http ims:%T lm:%T\", ims, r->headers_out.last_modified_time);\n\n    if (ims == r->headers_out.last_modified_time) {\n        return 0;\n    }\n\n    if (clcf->if_modified_since == NGX_HTTP_IMS_EXACT\n        || ims < r->headers_out.last_modified_time)\n    {\n        return 1;\n    }\n\n    return 0;\n}\n\n\nstatic ngx_uint_t\nngx_http_test_if_match(ngx_http_request_t *r, ngx_table_elt_t *header,\n    ngx_uint_t weak)\n{\n    u_char     *start, *end, ch;\n    ngx_str_t   etag, *list;\n\n    list = &header->value;\n\n    if (list->len == 1 && list->data[0] == '*') {\n        return 1;\n    }\n\n    if (r->headers_out.etag == NULL) {\n        return 0;\n    }\n\n    etag = r->headers_out.etag->value;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http im:\\\"%V\\\" etag:%V\", list, &etag);\n\n    if (weak\n        && etag.len > 2\n        && etag.data[0] == 'W'\n        && etag.data[1] == '/')\n    {\n        etag.len -= 2;\n        etag.data += 2;\n    }\n\n    start = list->data;\n    end = list->data + list->len;\n\n    while (start < end) {\n\n        if (weak\n            && end - start > 2\n            && start[0] == 'W'\n            && start[1] == '/')\n        {\n            start += 2;\n        }\n\n        if (etag.len > (size_t) (end - start)) {\n            return 0;\n        }\n\n        if (ngx_strncmp(start, etag.data, etag.len) != 0) {\n            goto skip;\n        }\n\n        start += etag.len;\n\n        while (start < end) {\n            ch = *start;\n\n            if (ch == ' ' || ch == '\\t') {\n                start++;\n                continue;\n            }\n\n            break;\n        }\n\n        if (start == end || *start == ',') {\n            return 1;\n        }\n\n    skip:\n\n        while (start < end && *start != ',') { start++; }\n        while (start < end) {\n            ch = *start;\n\n            if (ch == ' ' || ch == '\\t' || ch == ',') {\n                start++;\n                continue;\n            }\n\n            break;\n        }\n    }\n\n    return 0;\n}\n\n\nstatic ngx_int_t\nngx_http_not_modified_filter_init(ngx_conf_t *cf)\n{\n    ngx_http_next_header_filter = ngx_http_top_header_filter;\n    ngx_http_top_header_filter = ngx_http_not_modified_header_filter;\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "src/http/modules/ngx_http_proxy_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\n#define  NGX_HTTP_PROXY_COOKIE_SECURE           0x0001\n#define  NGX_HTTP_PROXY_COOKIE_SECURE_ON        0x0002\n#define  NGX_HTTP_PROXY_COOKIE_SECURE_OFF       0x0004\n#define  NGX_HTTP_PROXY_COOKIE_HTTPONLY         0x0008\n#define  NGX_HTTP_PROXY_COOKIE_HTTPONLY_ON      0x0010\n#define  NGX_HTTP_PROXY_COOKIE_HTTPONLY_OFF     0x0020\n#define  NGX_HTTP_PROXY_COOKIE_SAMESITE         0x0040\n#define  NGX_HTTP_PROXY_COOKIE_SAMESITE_STRICT  0x0080\n#define  NGX_HTTP_PROXY_COOKIE_SAMESITE_LAX     0x0100\n#define  NGX_HTTP_PROXY_COOKIE_SAMESITE_NONE    0x0200\n#define  NGX_HTTP_PROXY_COOKIE_SAMESITE_OFF     0x0400\n\n\ntypedef struct {\n    ngx_array_t                    caches;  /* ngx_http_file_cache_t * */\n} ngx_http_proxy_main_conf_t;\n\n\ntypedef struct ngx_http_proxy_rewrite_s  ngx_http_proxy_rewrite_t;\n\ntypedef ngx_int_t (*ngx_http_proxy_rewrite_pt)(ngx_http_request_t *r,\n    ngx_str_t *value, size_t prefix, size_t len,\n    ngx_http_proxy_rewrite_t *pr);\n\nstruct ngx_http_proxy_rewrite_s {\n    ngx_http_proxy_rewrite_pt      handler;\n\n    union {\n        ngx_http_complex_value_t   complex;\n#if (NGX_PCRE)\n        ngx_http_regex_t          *regex;\n#endif\n    } pattern;\n\n    ngx_http_complex_value_t       replacement;\n};\n\n\ntypedef struct {\n    union {\n        ngx_http_complex_value_t   complex;\n#if (NGX_PCRE)\n        ngx_http_regex_t          *regex;\n#endif\n    } cookie;\n\n    ngx_array_t                    flags_values;\n    ngx_uint_t                     regex;\n} ngx_http_proxy_cookie_flags_t;\n\n\ntypedef struct {\n    ngx_str_t                      key_start;\n    ngx_str_t                      schema;\n    ngx_str_t                      host_header;\n    ngx_str_t                      port;\n    ngx_str_t                      uri;\n} ngx_http_proxy_vars_t;\n\n\ntypedef struct {\n    ngx_array_t                   *flushes;\n    ngx_array_t                   *lengths;\n    ngx_array_t                   *values;\n    ngx_hash_t                     hash;\n} ngx_http_proxy_headers_t;\n\n\ntypedef struct {\n    ngx_http_upstream_conf_t       upstream;\n\n    ngx_array_t                   *body_flushes;\n    ngx_array_t                   *body_lengths;\n    ngx_array_t                   *body_values;\n    ngx_str_t                      body_source;\n\n    ngx_http_proxy_headers_t       headers;\n#if (NGX_HTTP_CACHE)\n    ngx_http_proxy_headers_t       headers_cache;\n#endif\n    ngx_array_t                   *headers_source;\n\n    ngx_array_t                   *proxy_lengths;\n    ngx_array_t                   *proxy_values;\n\n    ngx_array_t                   *redirects;\n    ngx_array_t                   *cookie_domains;\n    ngx_array_t                   *cookie_paths;\n    ngx_array_t                   *cookie_flags;\n\n    ngx_http_complex_value_t      *method;\n    ngx_str_t                      location;\n    ngx_str_t                      url;\n\n#if (NGX_HTTP_CACHE)\n    ngx_http_complex_value_t       cache_key;\n#endif\n\n    ngx_http_proxy_vars_t          vars;\n\n    ngx_flag_t                     redirect;\n#if (T_NGX_SOCKET_BUFFER)\n    size_t                         sndbuf;\n    size_t                         rcvbuf;\n#endif\n\n    ngx_uint_t                     http_version;\n\n    ngx_uint_t                     headers_hash_max_size;\n    ngx_uint_t                     headers_hash_bucket_size;\n\n#if (NGX_HTTP_SSL)\n    ngx_uint_t                     ssl;\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_array_t                   *ssl_conf_commands;\n#endif\n} ngx_http_proxy_loc_conf_t;\n\n\ntypedef struct {\n    ngx_http_status_t              status;\n    ngx_http_chunked_t             chunked;\n    ngx_http_proxy_vars_t          vars;\n    off_t                          internal_body_length;\n\n    ngx_chain_t                   *free;\n    ngx_chain_t                   *busy;\n\n    unsigned                       head:1;\n    unsigned                       internal_chunked:1;\n    unsigned                       header_sent:1;\n} ngx_http_proxy_ctx_t;\n\n\nstatic ngx_int_t ngx_http_proxy_eval(ngx_http_request_t *r,\n    ngx_http_proxy_ctx_t *ctx, ngx_http_proxy_loc_conf_t *plcf);\n#if (NGX_HTTP_CACHE)\nstatic ngx_int_t ngx_http_proxy_create_key(ngx_http_request_t *r);\n#endif\nstatic ngx_int_t ngx_http_proxy_create_request(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_proxy_reinit_request(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_proxy_body_output_filter(void *data, ngx_chain_t *in);\nstatic ngx_int_t ngx_http_proxy_process_status_line(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_proxy_process_header(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_proxy_input_filter_init(void *data);\nstatic ngx_int_t ngx_http_proxy_copy_filter(ngx_event_pipe_t *p,\n    ngx_buf_t *buf);\nstatic ngx_int_t ngx_http_proxy_chunked_filter(ngx_event_pipe_t *p,\n    ngx_buf_t *buf);\nstatic ngx_int_t ngx_http_proxy_non_buffered_copy_filter(void *data,\n    ssize_t bytes);\nstatic ngx_int_t ngx_http_proxy_non_buffered_chunked_filter(void *data,\n    ssize_t bytes);\nstatic void ngx_http_proxy_abort_request(ngx_http_request_t *r);\nstatic void ngx_http_proxy_finalize_request(ngx_http_request_t *r,\n    ngx_int_t rc);\n\nstatic ngx_int_t ngx_http_proxy_host_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_proxy_port_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t\n    ngx_http_proxy_add_x_forwarded_for_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t\n    ngx_http_proxy_internal_body_length_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_proxy_internal_chunked_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_proxy_rewrite_redirect(ngx_http_request_t *r,\n    ngx_table_elt_t *h, size_t prefix);\nstatic ngx_int_t ngx_http_proxy_rewrite_cookie(ngx_http_request_t *r,\n    ngx_table_elt_t *h);\nstatic ngx_int_t ngx_http_proxy_parse_cookie(ngx_str_t *value,\n    ngx_array_t *attrs);\nstatic ngx_int_t ngx_http_proxy_rewrite_cookie_value(ngx_http_request_t *r,\n    ngx_str_t *value, ngx_array_t *rewrites);\nstatic ngx_int_t ngx_http_proxy_rewrite_cookie_flags(ngx_http_request_t *r,\n    ngx_array_t *attrs, ngx_array_t *flags);\nstatic ngx_int_t ngx_http_proxy_edit_cookie_flags(ngx_http_request_t *r,\n    ngx_array_t *attrs, ngx_uint_t flags);\nstatic ngx_int_t ngx_http_proxy_rewrite(ngx_http_request_t *r,\n    ngx_str_t *value, size_t prefix, size_t len, ngx_str_t *replacement);\n\nstatic ngx_int_t ngx_http_proxy_add_variables(ngx_conf_t *cf);\nstatic void *ngx_http_proxy_create_main_conf(ngx_conf_t *cf);\nstatic void *ngx_http_proxy_create_loc_conf(ngx_conf_t *cf);\nstatic char *ngx_http_proxy_merge_loc_conf(ngx_conf_t *cf,\n    void *parent, void *child);\nstatic ngx_int_t ngx_http_proxy_init_headers(ngx_conf_t *cf,\n    ngx_http_proxy_loc_conf_t *conf, ngx_http_proxy_headers_t *headers,\n    ngx_keyval_t *default_headers);\n\nstatic char *ngx_http_proxy_pass(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_http_proxy_redirect(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_http_proxy_cookie_domain(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_http_proxy_cookie_path(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_http_proxy_cookie_flags(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_http_proxy_store(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n#if (NGX_HTTP_CACHE)\nstatic char *ngx_http_proxy_cache(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_http_proxy_cache_key(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n#endif\n#if (NGX_HTTP_SSL)\nstatic char *ngx_http_proxy_ssl_password_file(ngx_conf_t *cf,\n    ngx_command_t *cmd, void *conf);\n#endif\n\nstatic char *ngx_http_proxy_lowat_check(ngx_conf_t *cf, void *post, void *data);\n#if (NGX_HTTP_SSL)\nstatic char *ngx_http_proxy_ssl_conf_command_check(ngx_conf_t *cf, void *post,\n    void *data);\n#endif\n\nstatic ngx_int_t ngx_http_proxy_rewrite_regex(ngx_conf_t *cf,\n    ngx_http_proxy_rewrite_t *pr, ngx_str_t *regex, ngx_uint_t caseless);\n\n#if (NGX_HTTP_SSL)\nstatic ngx_int_t ngx_http_proxy_merge_ssl(ngx_conf_t *cf,\n    ngx_http_proxy_loc_conf_t *conf, ngx_http_proxy_loc_conf_t *prev);\nstatic ngx_int_t ngx_http_proxy_set_ssl(ngx_conf_t *cf,\n    ngx_http_proxy_loc_conf_t *plcf);\n#endif\nstatic void ngx_http_proxy_set_vars(ngx_url_t *u, ngx_http_proxy_vars_t *v);\n\n\nstatic ngx_conf_post_t  ngx_http_proxy_lowat_post =\n    { ngx_http_proxy_lowat_check };\n\n\nstatic ngx_conf_bitmask_t  ngx_http_proxy_next_upstream_masks[] = {\n    { ngx_string(\"error\"), NGX_HTTP_UPSTREAM_FT_ERROR },\n    { ngx_string(\"timeout\"), NGX_HTTP_UPSTREAM_FT_TIMEOUT },\n    { ngx_string(\"invalid_header\"), NGX_HTTP_UPSTREAM_FT_INVALID_HEADER },\n    { ngx_string(\"non_idempotent\"), NGX_HTTP_UPSTREAM_FT_NON_IDEMPOTENT },\n    { ngx_string(\"http_500\"), NGX_HTTP_UPSTREAM_FT_HTTP_500 },\n    { ngx_string(\"http_502\"), NGX_HTTP_UPSTREAM_FT_HTTP_502 },\n    { ngx_string(\"http_503\"), NGX_HTTP_UPSTREAM_FT_HTTP_503 },\n    { ngx_string(\"http_504\"), NGX_HTTP_UPSTREAM_FT_HTTP_504 },\n    { ngx_string(\"http_403\"), NGX_HTTP_UPSTREAM_FT_HTTP_403 },\n    { ngx_string(\"http_404\"), NGX_HTTP_UPSTREAM_FT_HTTP_404 },\n    { ngx_string(\"http_429\"), NGX_HTTP_UPSTREAM_FT_HTTP_429 },\n    { ngx_string(\"updating\"), NGX_HTTP_UPSTREAM_FT_UPDATING },\n    { ngx_string(\"off\"), NGX_HTTP_UPSTREAM_FT_OFF },\n    { ngx_null_string, 0 }\n};\n\n\n#if (NGX_HTTP_SSL)\n\nstatic ngx_conf_bitmask_t  ngx_http_proxy_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    { ngx_string(\"TLSv1.3\"), NGX_SSL_TLSv1_3 },\n    { ngx_null_string, 0 }\n};\n\nstatic ngx_conf_post_t  ngx_http_proxy_ssl_conf_command_post =\n    { ngx_http_proxy_ssl_conf_command_check };\n\n#endif\n\n\nstatic ngx_conf_enum_t  ngx_http_proxy_http_version[] = {\n    { ngx_string(\"1.0\"), NGX_HTTP_VERSION_10 },\n    { ngx_string(\"1.1\"), NGX_HTTP_VERSION_11 },\n    { ngx_null_string, 0 }\n};\n\n\nngx_module_t  ngx_http_proxy_module;\n\n\nstatic ngx_command_t  ngx_http_proxy_commands[] = {\n\n    { ngx_string(\"proxy_pass\"),\n      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_TAKE1,\n      ngx_http_proxy_pass,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"proxy_redirect\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,\n      ngx_http_proxy_redirect,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"proxy_cookie_domain\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,\n      ngx_http_proxy_cookie_domain,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"proxy_cookie_path\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,\n      ngx_http_proxy_cookie_path,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"proxy_cookie_flags\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1234,\n      ngx_http_proxy_cookie_flags,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"proxy_store\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_http_proxy_store,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"proxy_store_access\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,\n      ngx_conf_set_access_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_proxy_loc_conf_t, upstream.store_access),\n      NULL },\n\n    { ngx_string(\"proxy_buffering\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_proxy_loc_conf_t, upstream.buffering),\n      NULL },\n\n    { ngx_string(\"proxy_request_buffering\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_proxy_loc_conf_t, upstream.request_buffering),\n      NULL },\n\n    { ngx_string(\"proxy_ignore_client_abort\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_proxy_loc_conf_t, upstream.ignore_client_abort),\n      NULL },\n\n    { ngx_string(\"proxy_bind\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,\n      ngx_http_upstream_bind_set_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_proxy_loc_conf_t, upstream.local),\n      NULL },\n\n    { ngx_string(\"proxy_socket_keepalive\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_proxy_loc_conf_t, upstream.socket_keepalive),\n      NULL },\n\n    { ngx_string(\"proxy_connect_timeout\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_proxy_loc_conf_t, upstream.connect_timeout),\n      NULL },\n\n    { ngx_string(\"proxy_send_timeout\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_proxy_loc_conf_t, upstream.send_timeout),\n      NULL },\n\n    { ngx_string(\"proxy_send_lowat\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_proxy_loc_conf_t, upstream.send_lowat),\n      &ngx_http_proxy_lowat_post },\n\n    { ngx_string(\"proxy_intercept_errors\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_proxy_loc_conf_t, upstream.intercept_errors),\n      NULL },\n\n    { ngx_string(\"proxy_set_header\"),\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_proxy_loc_conf_t, headers_source),\n      NULL },\n\n    { ngx_string(\"proxy_headers_hash_max_size\"),\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_proxy_loc_conf_t, headers_hash_max_size),\n      NULL },\n\n    { ngx_string(\"proxy_headers_hash_bucket_size\"),\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_proxy_loc_conf_t, headers_hash_bucket_size),\n      NULL },\n\n    { ngx_string(\"proxy_set_body\"),\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_proxy_loc_conf_t, body_source),\n      NULL },\n\n    { ngx_string(\"proxy_method\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_http_set_complex_value_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_proxy_loc_conf_t, method),\n      NULL },\n\n    { ngx_string(\"proxy_pass_request_headers\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_proxy_loc_conf_t, upstream.pass_request_headers),\n      NULL },\n\n    { ngx_string(\"proxy_pass_request_body\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_proxy_loc_conf_t, upstream.pass_request_body),\n      NULL },\n\n    { ngx_string(\"proxy_buffer_size\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_proxy_loc_conf_t, upstream.buffer_size),\n      NULL },\n\n    { ngx_string(\"proxy_read_timeout\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_proxy_loc_conf_t, upstream.read_timeout),\n      NULL },\n\n    { ngx_string(\"proxy_buffers\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,\n      ngx_conf_set_bufs_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_proxy_loc_conf_t, upstream.bufs),\n      NULL },\n\n    { ngx_string(\"proxy_busy_buffers_size\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_proxy_loc_conf_t, upstream.busy_buffers_size_conf),\n      NULL },\n\n    { ngx_string(\"proxy_force_ranges\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_proxy_loc_conf_t, upstream.force_ranges),\n      NULL },\n\n    { ngx_string(\"proxy_limit_rate\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_proxy_loc_conf_t, upstream.limit_rate),\n      NULL },\n\n#if (NGX_HTTP_CACHE)\n\n    { ngx_string(\"proxy_cache\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_http_proxy_cache,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"proxy_cache_key\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_http_proxy_cache_key,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"proxy_cache_path\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_2MORE,\n      ngx_http_file_cache_set_slot,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      offsetof(ngx_http_proxy_main_conf_t, caches),\n      &ngx_http_proxy_module },\n\n    { ngx_string(\"proxy_cache_bypass\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,\n      ngx_http_set_predicate_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_proxy_loc_conf_t, upstream.cache_bypass),\n      NULL },\n\n    { ngx_string(\"proxy_no_cache\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,\n      ngx_http_set_predicate_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_proxy_loc_conf_t, upstream.no_cache),\n      NULL },\n\n    { ngx_string(\"proxy_cache_valid\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,\n      ngx_http_file_cache_valid_set_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_proxy_loc_conf_t, upstream.cache_valid),\n      NULL },\n\n    { ngx_string(\"proxy_cache_min_uses\"),\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_proxy_loc_conf_t, upstream.cache_min_uses),\n      NULL },\n\n    { ngx_string(\"proxy_cache_max_range_offset\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_off_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_proxy_loc_conf_t, upstream.cache_max_range_offset),\n      NULL },\n\n    { ngx_string(\"proxy_cache_use_stale\"),\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_proxy_loc_conf_t, upstream.cache_use_stale),\n      &ngx_http_proxy_next_upstream_masks },\n\n    { ngx_string(\"proxy_cache_methods\"),\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_proxy_loc_conf_t, upstream.cache_methods),\n      &ngx_http_upstream_cache_method_mask },\n\n    { ngx_string(\"proxy_cache_lock\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_proxy_loc_conf_t, upstream.cache_lock),\n      NULL },\n\n    { ngx_string(\"proxy_cache_lock_timeout\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_proxy_loc_conf_t, upstream.cache_lock_timeout),\n      NULL },\n\n    { ngx_string(\"proxy_cache_lock_age\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_proxy_loc_conf_t, upstream.cache_lock_age),\n      NULL },\n\n    { ngx_string(\"proxy_cache_revalidate\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_proxy_loc_conf_t, upstream.cache_revalidate),\n      NULL },\n\n    { ngx_string(\"proxy_cache_convert_head\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_proxy_loc_conf_t, upstream.cache_convert_head),\n      NULL },\n\n    { ngx_string(\"proxy_cache_background_update\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_proxy_loc_conf_t, upstream.cache_background_update),\n      NULL },\n\n#endif\n\n    { ngx_string(\"proxy_temp_path\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1234,\n      ngx_conf_set_path_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_proxy_loc_conf_t, upstream.temp_path),\n      NULL },\n\n    { ngx_string(\"proxy_max_temp_file_size\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_proxy_loc_conf_t, upstream.max_temp_file_size_conf),\n      NULL },\n\n    { ngx_string(\"proxy_temp_file_write_size\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_proxy_loc_conf_t, upstream.temp_file_write_size_conf),\n      NULL },\n\n    { ngx_string(\"proxy_next_upstream\"),\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_proxy_loc_conf_t, upstream.next_upstream),\n      &ngx_http_proxy_next_upstream_masks },\n\n    { ngx_string(\"proxy_next_upstream_tries\"),\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_proxy_loc_conf_t, upstream.next_upstream_tries),\n      NULL },\n\n#if (T_UPSTREAM_TRIES)\n    { ngx_string(\"proxy_upstream_tries\"),\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_proxy_loc_conf_t, upstream.next_upstream_tries),\n      NULL },\n#endif\n\n    { ngx_string(\"proxy_next_upstream_timeout\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_proxy_loc_conf_t, upstream.next_upstream_timeout),\n      NULL },\n\n    { ngx_string(\"proxy_pass_header\"),\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_proxy_loc_conf_t, upstream.pass_headers),\n      NULL },\n\n    { ngx_string(\"proxy_hide_header\"),\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_proxy_loc_conf_t, upstream.hide_headers),\n      NULL },\n\n    { ngx_string(\"proxy_ignore_headers\"),\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_proxy_loc_conf_t, upstream.ignore_headers),\n      &ngx_http_upstream_ignore_headers_masks },\n\n    { ngx_string(\"proxy_http_version\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_enum_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_proxy_loc_conf_t, http_version),\n      &ngx_http_proxy_http_version },\n\n#if (NGX_HTTP_SSL)\n\n    { ngx_string(\"proxy_ssl_session_reuse\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_proxy_loc_conf_t, upstream.ssl_session_reuse),\n      NULL },\n\n    { ngx_string(\"proxy_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_proxy_loc_conf_t, ssl_protocols),\n      &ngx_http_proxy_ssl_protocols },\n\n    { ngx_string(\"proxy_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_proxy_loc_conf_t, ssl_ciphers),\n      NULL },\n\n    { ngx_string(\"proxy_ssl_name\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_http_set_complex_value_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_proxy_loc_conf_t, upstream.ssl_name),\n      NULL },\n\n    { ngx_string(\"proxy_ssl_server_name\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_proxy_loc_conf_t, upstream.ssl_server_name),\n      NULL },\n\n    { ngx_string(\"proxy_ssl_verify\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_proxy_loc_conf_t, upstream.ssl_verify),\n      NULL },\n\n    { ngx_string(\"proxy_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_proxy_loc_conf_t, ssl_verify_depth),\n      NULL },\n\n    { ngx_string(\"proxy_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_proxy_loc_conf_t, ssl_trusted_certificate),\n      NULL },\n\n    { ngx_string(\"proxy_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_proxy_loc_conf_t, ssl_crl),\n      NULL },\n\n    { ngx_string(\"proxy_ssl_certificate\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_http_set_complex_value_zero_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_proxy_loc_conf_t, upstream.ssl_certificate),\n      NULL },\n\n    { ngx_string(\"proxy_ssl_certificate_key\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_http_set_complex_value_zero_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_proxy_loc_conf_t, upstream.ssl_certificate_key),\n      NULL },\n\n    { ngx_string(\"proxy_ssl_password_file\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_http_proxy_ssl_password_file,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n#if (T_NGX_SOCKET_BUFFER)\n    { ngx_string(\"proxy_sndbuf_size\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_proxy_loc_conf_t, sndbuf),\n      NULL },\n\n    { ngx_string(\"proxy_rcvbuf_size\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_proxy_loc_conf_t, rcvbuf),\n      NULL },\n#endif\n\n    { ngx_string(\"proxy_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_proxy_loc_conf_t, ssl_conf_commands),\n      &ngx_http_proxy_ssl_conf_command_post },\n\n#if (T_NGX_SSL_NTLS)\n    { ngx_string(\"proxy_enable_ntls\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_http_set_complex_value_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_proxy_loc_conf_t, upstream.enable_ntls),\n      NULL },\n\n    { ngx_string(\"proxy_ssl_enc_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_proxy_loc_conf_t, upstream.enc_certificate),\n      NULL },\n\n    { ngx_string(\"proxy_ssl_enc_certificate_key\"),\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_proxy_loc_conf_t, upstream.enc_certificate_key),\n      NULL },\n\n    { ngx_string(\"proxy_ssl_sign_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_proxy_loc_conf_t, upstream.sign_certificate),\n      NULL },\n\n    { ngx_string(\"proxy_ssl_sign_certificate_key\"),\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_proxy_loc_conf_t, upstream.sign_certificate_key),\n      NULL },\n#endif\n#endif\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_proxy_module_ctx = {\n    ngx_http_proxy_add_variables,          /* preconfiguration */\n    NULL,                                  /* postconfiguration */\n\n    ngx_http_proxy_create_main_conf,       /* create main configuration */\n    NULL,                                  /* init main configuration */\n\n    NULL,                                  /* create server configuration */\n    NULL,                                  /* merge server configuration */\n\n    ngx_http_proxy_create_loc_conf,        /* create location configuration */\n    ngx_http_proxy_merge_loc_conf          /* merge location configuration */\n};\n\n\nngx_module_t  ngx_http_proxy_module = {\n    NGX_MODULE_V1,\n    &ngx_http_proxy_module_ctx,            /* module context */\n    ngx_http_proxy_commands,               /* 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 char  ngx_http_proxy_version[] = \" HTTP/1.0\" CRLF;\nstatic char  ngx_http_proxy_version_11[] = \" HTTP/1.1\" CRLF;\n\n\nstatic ngx_keyval_t  ngx_http_proxy_headers[] = {\n    { ngx_string(\"Host\"), ngx_string(\"$proxy_host\") },\n    { ngx_string(\"Connection\"), ngx_string(\"close\") },\n    { ngx_string(\"Content-Length\"), ngx_string(\"$proxy_internal_body_length\") },\n    { ngx_string(\"Transfer-Encoding\"), ngx_string(\"$proxy_internal_chunked\") },\n    { ngx_string(\"TE\"), ngx_string(\"\") },\n    { ngx_string(\"Keep-Alive\"), ngx_string(\"\") },\n    { ngx_string(\"Expect\"), ngx_string(\"\") },\n    { ngx_string(\"Upgrade\"), ngx_string(\"\") },\n    { ngx_null_string, ngx_null_string }\n};\n\n\nstatic ngx_str_t  ngx_http_proxy_hide_headers[] = {\n    ngx_string(\"Date\"),\n    ngx_string(\"Server\"),\n    ngx_string(\"X-Pad\"),\n    ngx_string(\"X-Accel-Expires\"),\n    ngx_string(\"X-Accel-Redirect\"),\n    ngx_string(\"X-Accel-Limit-Rate\"),\n    ngx_string(\"X-Accel-Buffering\"),\n    ngx_string(\"X-Accel-Charset\"),\n    ngx_null_string\n};\n\n\n#if (NGX_HTTP_CACHE)\n\nstatic ngx_keyval_t  ngx_http_proxy_cache_headers[] = {\n    { ngx_string(\"Host\"), ngx_string(\"$proxy_host\") },\n    { ngx_string(\"Connection\"), ngx_string(\"close\") },\n    { ngx_string(\"Content-Length\"), ngx_string(\"$proxy_internal_body_length\") },\n    { ngx_string(\"Transfer-Encoding\"), ngx_string(\"$proxy_internal_chunked\") },\n    { ngx_string(\"TE\"), ngx_string(\"\") },\n    { ngx_string(\"Keep-Alive\"), ngx_string(\"\") },\n    { ngx_string(\"Expect\"), ngx_string(\"\") },\n    { ngx_string(\"Upgrade\"), ngx_string(\"\") },\n    { ngx_string(\"If-Modified-Since\"),\n      ngx_string(\"$upstream_cache_last_modified\") },\n    { ngx_string(\"If-Unmodified-Since\"), ngx_string(\"\") },\n    { ngx_string(\"If-None-Match\"), ngx_string(\"$upstream_cache_etag\") },\n    { ngx_string(\"If-Match\"), ngx_string(\"\") },\n    { ngx_string(\"Range\"), ngx_string(\"\") },\n    { ngx_string(\"If-Range\"), ngx_string(\"\") },\n    { ngx_null_string, ngx_null_string }\n};\n\n#endif\n\n\nstatic ngx_http_variable_t  ngx_http_proxy_vars[] = {\n\n    { ngx_string(\"proxy_host\"), NULL, ngx_http_proxy_host_variable, 0,\n      NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },\n\n    { ngx_string(\"proxy_port\"), NULL, ngx_http_proxy_port_variable, 0,\n      NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },\n\n    { ngx_string(\"proxy_add_x_forwarded_for\"), NULL,\n      ngx_http_proxy_add_x_forwarded_for_variable, 0, NGX_HTTP_VAR_NOHASH, 0 },\n\n#if 0\n    { ngx_string(\"proxy_add_via\"), NULL, NULL, 0, NGX_HTTP_VAR_NOHASH, 0 },\n#endif\n\n    { ngx_string(\"proxy_internal_body_length\"), NULL,\n      ngx_http_proxy_internal_body_length_variable, 0,\n      NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },\n\n    { ngx_string(\"proxy_internal_chunked\"), NULL,\n      ngx_http_proxy_internal_chunked_variable, 0,\n      NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },\n\n      ngx_http_null_variable\n};\n\n\nstatic ngx_path_init_t  ngx_http_proxy_temp_path = {\n    ngx_string(NGX_HTTP_PROXY_TEMP_PATH), { 1, 2, 0 }\n};\n\n\nstatic ngx_conf_bitmask_t  ngx_http_proxy_cookie_flags_masks[] = {\n\n    { ngx_string(\"secure\"),\n      NGX_HTTP_PROXY_COOKIE_SECURE|NGX_HTTP_PROXY_COOKIE_SECURE_ON },\n\n    { ngx_string(\"nosecure\"),\n      NGX_HTTP_PROXY_COOKIE_SECURE|NGX_HTTP_PROXY_COOKIE_SECURE_OFF },\n\n    { ngx_string(\"httponly\"),\n      NGX_HTTP_PROXY_COOKIE_HTTPONLY|NGX_HTTP_PROXY_COOKIE_HTTPONLY_ON },\n\n    { ngx_string(\"nohttponly\"),\n      NGX_HTTP_PROXY_COOKIE_HTTPONLY|NGX_HTTP_PROXY_COOKIE_HTTPONLY_OFF },\n\n    { ngx_string(\"samesite=strict\"),\n      NGX_HTTP_PROXY_COOKIE_SAMESITE|NGX_HTTP_PROXY_COOKIE_SAMESITE_STRICT },\n\n    { ngx_string(\"samesite=lax\"),\n      NGX_HTTP_PROXY_COOKIE_SAMESITE|NGX_HTTP_PROXY_COOKIE_SAMESITE_LAX },\n\n    { ngx_string(\"samesite=none\"),\n      NGX_HTTP_PROXY_COOKIE_SAMESITE|NGX_HTTP_PROXY_COOKIE_SAMESITE_NONE },\n\n    { ngx_string(\"nosamesite\"),\n      NGX_HTTP_PROXY_COOKIE_SAMESITE|NGX_HTTP_PROXY_COOKIE_SAMESITE_OFF },\n\n    { ngx_null_string, 0 }\n};\n\n\nstatic ngx_int_t\nngx_http_proxy_handler(ngx_http_request_t *r)\n{\n    ngx_int_t                    rc;\n    ngx_http_upstream_t         *u;\n    ngx_http_proxy_ctx_t        *ctx;\n    ngx_http_proxy_loc_conf_t   *plcf;\n#if (NGX_HTTP_CACHE)\n    ngx_http_proxy_main_conf_t  *pmcf;\n#endif\n\n    if (ngx_http_upstream_create(r) != NGX_OK) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_proxy_ctx_t));\n    if (ctx == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    ngx_http_set_ctx(r, ctx, ngx_http_proxy_module);\n\n    plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module);\n\n    u = r->upstream;\n\n    if (plcf->proxy_lengths == NULL) {\n        ctx->vars = plcf->vars;\n        u->schema = plcf->vars.schema;\n#if (NGX_HTTP_SSL)\n        u->ssl = plcf->ssl;\n#endif\n\n    } else {\n        if (ngx_http_proxy_eval(r, ctx, plcf) != NGX_OK) {\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n    }\n\n    u->output.tag = (ngx_buf_tag_t) &ngx_http_proxy_module;\n\n    u->conf = &plcf->upstream;\n\n#if (NGX_HTTP_CACHE)\n    pmcf = ngx_http_get_module_main_conf(r, ngx_http_proxy_module);\n\n    u->caches = &pmcf->caches;\n    u->create_key = ngx_http_proxy_create_key;\n#endif\n\n    u->create_request = ngx_http_proxy_create_request;\n    u->reinit_request = ngx_http_proxy_reinit_request;\n    u->process_header = ngx_http_proxy_process_status_line;\n    u->abort_request = ngx_http_proxy_abort_request;\n    u->finalize_request = ngx_http_proxy_finalize_request;\n    r->state = 0;\n\n    if (plcf->redirects) {\n        u->rewrite_redirect = ngx_http_proxy_rewrite_redirect;\n    }\n\n    if (plcf->cookie_domains || plcf->cookie_paths || plcf->cookie_flags) {\n        u->rewrite_cookie = ngx_http_proxy_rewrite_cookie;\n    }\n\n    u->buffering = plcf->upstream.buffering;\n\n#if (T_NGX_SOCKET_BUFFER)\n    u->peer.sndbuf = plcf->sndbuf;\n    u->peer.rcvbuf = plcf->rcvbuf;\n#endif\n\n    u->pipe = ngx_pcalloc(r->pool, sizeof(ngx_event_pipe_t));\n    if (u->pipe == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    u->pipe->input_filter = ngx_http_proxy_copy_filter;\n    u->pipe->input_ctx = r;\n\n    u->input_filter_init = ngx_http_proxy_input_filter_init;\n    u->input_filter = ngx_http_proxy_non_buffered_copy_filter;\n    u->input_filter_ctx = r;\n\n    u->accel = 1;\n\n    if (!plcf->upstream.request_buffering\n        && plcf->body_values == NULL && plcf->upstream.pass_request_body\n        && (!r->headers_in.chunked\n            || plcf->http_version == NGX_HTTP_VERSION_11))\n    {\n        r->request_body_no_buffering = 1;\n    }\n\n    rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init);\n\n    if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {\n        return rc;\n    }\n\n    return NGX_DONE;\n}\n\n\nstatic ngx_int_t\nngx_http_proxy_eval(ngx_http_request_t *r, ngx_http_proxy_ctx_t *ctx,\n    ngx_http_proxy_loc_conf_t *plcf)\n{\n    u_char               *p;\n    size_t                add;\n    u_short               port;\n    ngx_str_t             proxy;\n    ngx_url_t             url;\n    ngx_http_upstream_t  *u;\n\n    if (ngx_http_script_run(r, &proxy, plcf->proxy_lengths->elts, 0,\n                            plcf->proxy_values->elts)\n        == NULL)\n    {\n        return NGX_ERROR;\n    }\n\n    if (proxy.len > 7\n        && ngx_strncasecmp(proxy.data, (u_char *) \"http://\", 7) == 0)\n    {\n        add = 7;\n        port = 80;\n\n#if (NGX_HTTP_SSL)\n\n    } else if (proxy.len > 8\n               && ngx_strncasecmp(proxy.data, (u_char *) \"https://\", 8) == 0)\n    {\n        add = 8;\n        port = 443;\n        r->upstream->ssl = 1;\n\n#endif\n\n    } else {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"invalid URL prefix in \\\"%V\\\"\", &proxy);\n        return NGX_ERROR;\n    }\n\n    u = r->upstream;\n\n    u->schema.len = add;\n    u->schema.data = proxy.data;\n\n    ngx_memzero(&url, sizeof(ngx_url_t));\n\n    url.url.len = proxy.len - add;\n    url.url.data = proxy.data + add;\n    url.default_port = port;\n    url.uri_part = 1;\n    url.no_resolve = 1;\n\n    if (ngx_parse_url(r->pool, &url) != NGX_OK) {\n        if (url.err) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"%s in upstream \\\"%V\\\"\", url.err, &url.url);\n        }\n\n        return NGX_ERROR;\n    }\n\n    if (url.uri.len) {\n        if (url.uri.data[0] == '?') {\n            p = ngx_pnalloc(r->pool, url.uri.len + 1);\n            if (p == NULL) {\n                return NGX_ERROR;\n            }\n\n            *p++ = '/';\n            ngx_memcpy(p, url.uri.data, url.uri.len);\n\n            url.uri.len++;\n            url.uri.data = p - 1;\n        }\n    }\n\n    ctx->vars.key_start = u->schema;\n\n    ngx_http_proxy_set_vars(&url, &ctx->vars);\n\n    u->resolved = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_resolved_t));\n    if (u->resolved == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (url.addrs) {\n        u->resolved->sockaddr = url.addrs[0].sockaddr;\n        u->resolved->socklen = url.addrs[0].socklen;\n        u->resolved->name = url.addrs[0].name;\n        u->resolved->naddrs = 1;\n    }\n\n    u->resolved->host = url.host;\n    u->resolved->port = (in_port_t) (url.no_port ? port : url.port);\n    u->resolved->no_port = url.no_port;\n\n    return NGX_OK;\n}\n\n\n#if (NGX_HTTP_CACHE)\n\nstatic ngx_int_t\nngx_http_proxy_create_key(ngx_http_request_t *r)\n{\n    size_t                      len, loc_len;\n    u_char                     *p;\n    uintptr_t                   escape;\n    ngx_str_t                  *key;\n    ngx_http_upstream_t        *u;\n    ngx_http_proxy_ctx_t       *ctx;\n    ngx_http_proxy_loc_conf_t  *plcf;\n\n    u = r->upstream;\n\n    plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module);\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);\n\n    key = ngx_array_push(&r->cache->keys);\n    if (key == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (plcf->cache_key.value.data) {\n\n        if (ngx_http_complex_value(r, &plcf->cache_key, key) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        return NGX_OK;\n    }\n\n    *key = ctx->vars.key_start;\n\n    key = ngx_array_push(&r->cache->keys);\n    if (key == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (plcf->proxy_lengths && ctx->vars.uri.len) {\n\n        *key = ctx->vars.uri;\n        u->uri = ctx->vars.uri;\n\n        return NGX_OK;\n\n    } else if (ctx->vars.uri.len == 0 && r->valid_unparsed_uri) {\n        *key = r->unparsed_uri;\n        u->uri = r->unparsed_uri;\n\n        return NGX_OK;\n    }\n\n    loc_len = (r->valid_location && ctx->vars.uri.len) ? plcf->location.len : 0;\n\n    if (r->quoted_uri || r->internal) {\n        escape = 2 * ngx_escape_uri(NULL, r->uri.data + loc_len,\n                                    r->uri.len - loc_len, NGX_ESCAPE_URI);\n    } else {\n        escape = 0;\n    }\n\n    len = ctx->vars.uri.len + r->uri.len - loc_len + escape\n          + sizeof(\"?\") - 1 + r->args.len;\n\n    p = ngx_pnalloc(r->pool, len);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    key->data = p;\n\n    if (r->valid_location) {\n        p = ngx_copy(p, ctx->vars.uri.data, ctx->vars.uri.len);\n    }\n\n    if (escape) {\n        ngx_escape_uri(p, r->uri.data + loc_len,\n                       r->uri.len - loc_len, NGX_ESCAPE_URI);\n        p += r->uri.len - loc_len + escape;\n\n    } else {\n        p = ngx_copy(p, r->uri.data + loc_len, r->uri.len - loc_len);\n    }\n\n    if (r->args.len > 0) {\n        *p++ = '?';\n        p = ngx_copy(p, r->args.data, r->args.len);\n    }\n\n    key->len = p - key->data;\n    u->uri = *key;\n\n    return NGX_OK;\n}\n\n#endif\n\n\nstatic ngx_int_t\nngx_http_proxy_create_request(ngx_http_request_t *r)\n{\n    size_t                        len, uri_len, loc_len, body_len,\n                                  key_len, val_len;\n    uintptr_t                     escape;\n    ngx_buf_t                    *b;\n    ngx_str_t                     method;\n    ngx_uint_t                    i, unparsed_uri;\n    ngx_chain_t                  *cl, *body;\n    ngx_list_part_t              *part;\n    ngx_table_elt_t              *header;\n    ngx_http_upstream_t          *u;\n    ngx_http_proxy_ctx_t         *ctx;\n    ngx_http_script_code_pt       code;\n    ngx_http_proxy_headers_t     *headers;\n    ngx_http_script_engine_t      e, le;\n    ngx_http_proxy_loc_conf_t    *plcf;\n    ngx_http_script_len_code_pt   lcode;\n\n    u = r->upstream;\n\n    plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module);\n\n#if (NGX_HTTP_CACHE)\n    headers = u->cacheable ? &plcf->headers_cache : &plcf->headers;\n#else\n    headers = &plcf->headers;\n#endif\n\n    if (u->method.len) {\n        /* HEAD was changed to GET to cache response */\n        method = u->method;\n\n    } else if (plcf->method) {\n        if (ngx_http_complex_value(r, plcf->method, &method) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n    } else {\n        method = r->method_name;\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);\n\n    if (method.len == 4\n        && ngx_strncasecmp(method.data, (u_char *) \"HEAD\", 4) == 0)\n    {\n        ctx->head = 1;\n    }\n\n    len = method.len + 1 + sizeof(ngx_http_proxy_version) - 1\n          + sizeof(CRLF) - 1;\n\n    escape = 0;\n    loc_len = 0;\n    unparsed_uri = 0;\n\n    if (plcf->proxy_lengths && ctx->vars.uri.len) {\n        uri_len = ctx->vars.uri.len;\n\n    } else if (ctx->vars.uri.len == 0 && r->valid_unparsed_uri) {\n        unparsed_uri = 1;\n        uri_len = r->unparsed_uri.len;\n\n    } else {\n        loc_len = (r->valid_location && ctx->vars.uri.len) ?\n                      plcf->location.len : 0;\n\n        if (r->quoted_uri || r->internal) {\n            escape = 2 * ngx_escape_uri(NULL, r->uri.data + loc_len,\n                                        r->uri.len - loc_len, NGX_ESCAPE_URI);\n        }\n\n        uri_len = ctx->vars.uri.len + r->uri.len - loc_len + escape\n                  + sizeof(\"?\") - 1 + r->args.len;\n    }\n\n    if (uri_len == 0) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"zero length URI to proxy\");\n        return NGX_ERROR;\n    }\n\n    len += uri_len;\n\n    ngx_memzero(&le, sizeof(ngx_http_script_engine_t));\n\n    ngx_http_script_flush_no_cacheable_variables(r, plcf->body_flushes);\n    ngx_http_script_flush_no_cacheable_variables(r, headers->flushes);\n\n    if (plcf->body_lengths) {\n        le.ip = plcf->body_lengths->elts;\n        le.request = r;\n        le.flushed = 1;\n        body_len = 0;\n\n        while (*(uintptr_t *) le.ip) {\n            lcode = *(ngx_http_script_len_code_pt *) le.ip;\n            body_len += lcode(&le);\n        }\n\n        ctx->internal_body_length = body_len;\n        len += body_len;\n\n    } else if (r->headers_in.chunked && r->reading_body) {\n        ctx->internal_body_length = -1;\n        ctx->internal_chunked = 1;\n\n    } else {\n        ctx->internal_body_length = r->headers_in.content_length_n;\n    }\n\n    le.ip = headers->lengths->elts;\n    le.request = r;\n    le.flushed = 1;\n\n    while (*(uintptr_t *) le.ip) {\n\n        lcode = *(ngx_http_script_len_code_pt *) le.ip;\n        key_len = lcode(&le);\n\n        for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) {\n            lcode = *(ngx_http_script_len_code_pt *) le.ip;\n        }\n        le.ip += sizeof(uintptr_t);\n\n        if (val_len == 0) {\n            continue;\n        }\n\n        len += key_len + sizeof(\": \") - 1 + val_len + sizeof(CRLF) - 1;\n    }\n\n\n    if (plcf->upstream.pass_request_headers) {\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_hash_find(&headers->hash, header[i].hash,\n                              header[i].lowcase_key, header[i].key.len))\n            {\n                continue;\n            }\n\n            len += header[i].key.len + sizeof(\": \") - 1\n                + header[i].value.len + sizeof(CRLF) - 1;\n        }\n    }\n\n\n    b = ngx_create_temp_buf(r->pool, len);\n    if (b == NULL) {\n        return NGX_ERROR;\n    }\n\n    cl = ngx_alloc_chain_link(r->pool);\n    if (cl == NULL) {\n        return NGX_ERROR;\n    }\n\n    cl->buf = b;\n\n\n    /* the request line */\n\n    b->last = ngx_copy(b->last, method.data, method.len);\n    *b->last++ = ' ';\n\n    u->uri.data = b->last;\n\n    if (plcf->proxy_lengths && ctx->vars.uri.len) {\n        b->last = ngx_copy(b->last, ctx->vars.uri.data, ctx->vars.uri.len);\n\n    } else if (unparsed_uri) {\n        b->last = ngx_copy(b->last, r->unparsed_uri.data, r->unparsed_uri.len);\n\n    } else {\n        if (r->valid_location) {\n            b->last = ngx_copy(b->last, ctx->vars.uri.data, ctx->vars.uri.len);\n        }\n\n        if (escape) {\n            ngx_escape_uri(b->last, r->uri.data + loc_len,\n                           r->uri.len - loc_len, NGX_ESCAPE_URI);\n            b->last += r->uri.len - loc_len + escape;\n\n        } else {\n            b->last = ngx_copy(b->last, r->uri.data + loc_len,\n                               r->uri.len - loc_len);\n        }\n\n        if (r->args.len > 0) {\n            *b->last++ = '?';\n            b->last = ngx_copy(b->last, r->args.data, r->args.len);\n        }\n    }\n\n    u->uri.len = b->last - u->uri.data;\n\n    if (plcf->http_version == NGX_HTTP_VERSION_11) {\n        b->last = ngx_cpymem(b->last, ngx_http_proxy_version_11,\n                             sizeof(ngx_http_proxy_version_11) - 1);\n\n    } else {\n        b->last = ngx_cpymem(b->last, ngx_http_proxy_version,\n                             sizeof(ngx_http_proxy_version) - 1);\n    }\n\n    ngx_memzero(&e, sizeof(ngx_http_script_engine_t));\n\n    e.ip = headers->values->elts;\n    e.pos = b->last;\n    e.request = r;\n    e.flushed = 1;\n\n    le.ip = headers->lengths->elts;\n\n    while (*(uintptr_t *) le.ip) {\n\n        lcode = *(ngx_http_script_len_code_pt *) le.ip;\n        (void) lcode(&le);\n\n        for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) {\n            lcode = *(ngx_http_script_len_code_pt *) le.ip;\n        }\n        le.ip += sizeof(uintptr_t);\n\n        if (val_len == 0) {\n            e.skip = 1;\n\n            while (*(uintptr_t *) e.ip) {\n                code = *(ngx_http_script_code_pt *) e.ip;\n                code((ngx_http_script_engine_t *) &e);\n            }\n            e.ip += sizeof(uintptr_t);\n\n            e.skip = 0;\n\n            continue;\n        }\n\n        code = *(ngx_http_script_code_pt *) e.ip;\n        code((ngx_http_script_engine_t *) &e);\n\n        *e.pos++ = ':'; *e.pos++ = ' ';\n\n        while (*(uintptr_t *) e.ip) {\n            code = *(ngx_http_script_code_pt *) e.ip;\n            code((ngx_http_script_engine_t *) &e);\n        }\n        e.ip += sizeof(uintptr_t);\n\n        *e.pos++ = CR; *e.pos++ = LF;\n    }\n\n    b->last = e.pos;\n\n\n    if (plcf->upstream.pass_request_headers) {\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_hash_find(&headers->hash, header[i].hash,\n                              header[i].lowcase_key, header[i].key.len))\n            {\n                continue;\n            }\n\n            b->last = ngx_copy(b->last, header[i].key.data, header[i].key.len);\n\n            *b->last++ = ':'; *b->last++ = ' ';\n\n            b->last = ngx_copy(b->last, header[i].value.data,\n                               header[i].value.len);\n\n            *b->last++ = CR; *b->last++ = LF;\n\n            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"http proxy header: \\\"%V: %V\\\"\",\n                           &header[i].key, &header[i].value);\n        }\n    }\n\n\n    /* add \"\\r\\n\" at the header end */\n    *b->last++ = CR; *b->last++ = LF;\n\n    if (plcf->body_values) {\n        e.ip = plcf->body_values->elts;\n        e.pos = b->last;\n        e.skip = 0;\n\n        while (*(uintptr_t *) e.ip) {\n            code = *(ngx_http_script_code_pt *) e.ip;\n            code((ngx_http_script_engine_t *) &e);\n        }\n\n        b->last = e.pos;\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http proxy header:%N\\\"%*s\\\"\",\n                   (size_t) (b->last - b->pos), b->pos);\n\n    if (r->request_body_no_buffering) {\n\n        u->request_bufs = cl;\n\n        if (ctx->internal_chunked) {\n            u->output.output_filter = ngx_http_proxy_body_output_filter;\n            u->output.filter_ctx = r;\n        }\n\n    } else if (plcf->body_values == NULL && plcf->upstream.pass_request_body) {\n\n        body = u->request_bufs;\n        u->request_bufs = cl;\n\n        while (body) {\n            b = ngx_alloc_buf(r->pool);\n            if (b == NULL) {\n                return NGX_ERROR;\n            }\n\n            ngx_memcpy(b, body->buf, sizeof(ngx_buf_t));\n\n            cl->next = ngx_alloc_chain_link(r->pool);\n            if (cl->next == NULL) {\n                return NGX_ERROR;\n            }\n\n            cl = cl->next;\n            cl->buf = b;\n\n            body = body->next;\n        }\n\n    } else {\n        u->request_bufs = cl;\n    }\n\n    b->flush = 1;\n    cl->next = NULL;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_proxy_reinit_request(ngx_http_request_t *r)\n{\n    ngx_http_proxy_ctx_t  *ctx;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);\n\n    if (ctx == NULL) {\n        return NGX_OK;\n    }\n\n    ctx->status.code = 0;\n    ctx->status.count = 0;\n    ctx->status.start = NULL;\n    ctx->status.end = NULL;\n    ctx->chunked.state = 0;\n\n    r->upstream->process_header = ngx_http_proxy_process_status_line;\n    r->upstream->pipe->input_filter = ngx_http_proxy_copy_filter;\n    r->upstream->input_filter = ngx_http_proxy_non_buffered_copy_filter;\n    r->state = 0;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_proxy_body_output_filter(void *data, ngx_chain_t *in)\n{\n    ngx_http_request_t  *r = data;\n\n    off_t                  size;\n    u_char                *chunk;\n    ngx_int_t              rc;\n    ngx_buf_t             *b;\n    ngx_chain_t           *out, *cl, *tl, **ll, **fl;\n    ngx_http_proxy_ctx_t  *ctx;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"proxy output filter\");\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);\n\n    if (in == NULL) {\n        out = in;\n        goto out;\n    }\n\n    out = NULL;\n    ll = &out;\n\n    if (!ctx->header_sent) {\n        /* first buffer contains headers, pass it unmodified */\n\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"proxy output header\");\n\n        ctx->header_sent = 1;\n\n        tl = ngx_alloc_chain_link(r->pool);\n        if (tl == NULL) {\n            return NGX_ERROR;\n        }\n\n        tl->buf = in->buf;\n        *ll = tl;\n        ll = &tl->next;\n\n        in = in->next;\n\n        if (in == NULL) {\n            tl->next = NULL;\n            goto out;\n        }\n    }\n\n    size = 0;\n    cl = in;\n    fl = ll;\n\n    for ( ;; ) {\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"proxy output chunk: %O\", ngx_buf_size(cl->buf));\n\n        size += ngx_buf_size(cl->buf);\n\n        if (cl->buf->flush\n            || cl->buf->sync\n            || ngx_buf_in_memory(cl->buf)\n            || cl->buf->in_file)\n        {\n            tl = ngx_alloc_chain_link(r->pool);\n            if (tl == NULL) {\n                return NGX_ERROR;\n            }\n\n            tl->buf = cl->buf;\n            *ll = tl;\n            ll = &tl->next;\n        }\n\n        if (cl->next == NULL) {\n            break;\n        }\n\n        cl = cl->next;\n    }\n\n    if (size) {\n        tl = ngx_chain_get_free_buf(r->pool, &ctx->free);\n        if (tl == NULL) {\n            return NGX_ERROR;\n        }\n\n        b = tl->buf;\n        chunk = b->start;\n\n        if (chunk == NULL) {\n            /* the \"0000000000000000\" is 64-bit hexadecimal string */\n\n            chunk = ngx_palloc(r->pool, sizeof(\"0000000000000000\" CRLF) - 1);\n            if (chunk == NULL) {\n                return NGX_ERROR;\n            }\n\n            b->start = chunk;\n            b->end = chunk + sizeof(\"0000000000000000\" CRLF) - 1;\n        }\n\n        b->tag = (ngx_buf_tag_t) &ngx_http_proxy_body_output_filter;\n        b->memory = 0;\n        b->temporary = 1;\n        b->pos = chunk;\n        b->last = ngx_sprintf(chunk, \"%xO\" CRLF, size);\n\n        tl->next = *fl;\n        *fl = tl;\n    }\n\n    if (cl->buf->last_buf) {\n        tl = ngx_chain_get_free_buf(r->pool, &ctx->free);\n        if (tl == NULL) {\n            return NGX_ERROR;\n        }\n\n        b = tl->buf;\n\n        b->tag = (ngx_buf_tag_t) &ngx_http_proxy_body_output_filter;\n        b->temporary = 0;\n        b->memory = 1;\n        b->last_buf = 1;\n        b->pos = (u_char *) CRLF \"0\" CRLF CRLF;\n        b->last = b->pos + 7;\n\n        cl->buf->last_buf = 0;\n\n        *ll = tl;\n\n        if (size == 0) {\n            b->pos += 2;\n        }\n\n    } else if (size > 0) {\n        tl = ngx_chain_get_free_buf(r->pool, &ctx->free);\n        if (tl == NULL) {\n            return NGX_ERROR;\n        }\n\n        b = tl->buf;\n\n        b->tag = (ngx_buf_tag_t) &ngx_http_proxy_body_output_filter;\n        b->temporary = 0;\n        b->memory = 1;\n        b->pos = (u_char *) CRLF;\n        b->last = b->pos + 2;\n\n        *ll = tl;\n\n    } else {\n        *ll = NULL;\n    }\n\nout:\n\n    rc = ngx_chain_writer(&r->upstream->writer, out);\n\n    ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &out,\n                            (ngx_buf_tag_t) &ngx_http_proxy_body_output_filter);\n\n    return rc;\n}\n\n\nstatic ngx_int_t\nngx_http_proxy_process_status_line(ngx_http_request_t *r)\n{\n    size_t                 len;\n    ngx_int_t              rc;\n    ngx_http_upstream_t   *u;\n    ngx_http_proxy_ctx_t  *ctx;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);\n\n    if (ctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    u = r->upstream;\n\n    rc = ngx_http_parse_status_line(r, &u->buffer, &ctx->status);\n\n    if (rc == NGX_AGAIN) {\n        return rc;\n    }\n\n    if (rc == NGX_ERROR) {\n\n#if (NGX_HTTP_CACHE)\n\n        if (r->cache) {\n            r->http_version = NGX_HTTP_VERSION_9;\n            return NGX_OK;\n        }\n\n#endif\n\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"upstream sent no valid HTTP/1.0 header\");\n\n#if 0\n        if (u->accel) {\n            return NGX_HTTP_UPSTREAM_INVALID_HEADER;\n        }\n#endif\n\n        r->http_version = NGX_HTTP_VERSION_9;\n        u->state->status = NGX_HTTP_OK;\n        u->headers_in.connection_close = 1;\n\n        return NGX_OK;\n    }\n\n    if (u->state && u->state->status == 0) {\n        u->state->status = ctx->status.code;\n    }\n\n    u->headers_in.status_n = ctx->status.code;\n\n    len = ctx->status.end - ctx->status.start;\n    u->headers_in.status_line.len = len;\n\n    u->headers_in.status_line.data = ngx_pnalloc(r->pool, len);\n    if (u->headers_in.status_line.data == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_memcpy(u->headers_in.status_line.data, ctx->status.start, len);\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http proxy status %ui \\\"%V\\\"\",\n                   u->headers_in.status_n, &u->headers_in.status_line);\n\n    if (ctx->status.http_version < NGX_HTTP_VERSION_11) {\n        u->headers_in.connection_close = 1;\n    }\n\n    u->process_header = ngx_http_proxy_process_header;\n\n    return ngx_http_proxy_process_header(r);\n}\n\n\nstatic ngx_int_t\nngx_http_proxy_process_header(ngx_http_request_t *r)\n{\n    ngx_int_t                       rc;\n    ngx_table_elt_t                *h;\n    ngx_http_upstream_t            *u;\n    ngx_http_proxy_ctx_t           *ctx;\n    ngx_http_upstream_header_t     *hh;\n    ngx_http_upstream_main_conf_t  *umcf;\n\n    umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);\n\n    for ( ;; ) {\n\n        rc = ngx_http_parse_header_line(r, &r->upstream->buffer, 1);\n\n        if (rc == NGX_OK) {\n\n            /* a header line has been parsed successfully */\n\n            h = ngx_list_push(&r->upstream->headers_in.headers);\n            if (h == NULL) {\n                return NGX_ERROR;\n            }\n\n            h->hash = r->header_hash;\n\n            h->key.len = r->header_name_end - r->header_name_start;\n            h->value.len = r->header_end - r->header_start;\n\n            h->key.data = ngx_pnalloc(r->pool,\n                               h->key.len + 1 + h->value.len + 1 + h->key.len);\n            if (h->key.data == NULL) {\n                h->hash = 0;\n                return NGX_ERROR;\n            }\n\n            h->value.data = h->key.data + h->key.len + 1;\n            h->lowcase_key = h->key.data + h->key.len + 1 + h->value.len + 1;\n\n            ngx_memcpy(h->key.data, r->header_name_start, h->key.len);\n            h->key.data[h->key.len] = '\\0';\n            ngx_memcpy(h->value.data, r->header_start, h->value.len);\n            h->value.data[h->value.len] = '\\0';\n\n            if (h->key.len == r->lowcase_index) {\n                ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len);\n\n            } else {\n                ngx_strlow(h->lowcase_key, h->key.data, h->key.len);\n            }\n\n            hh = ngx_hash_find(&umcf->headers_in_hash, h->hash,\n                               h->lowcase_key, h->key.len);\n\n            if (hh) {\n                rc = hh->handler(r, h, hh->offset);\n\n                if (rc != NGX_OK) {\n                    return rc;\n                }\n            }\n\n            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"http proxy header: \\\"%V: %V\\\"\",\n                           &h->key, &h->value);\n\n            continue;\n        }\n\n        if (rc == NGX_HTTP_PARSE_HEADER_DONE) {\n\n            /* a whole header has been parsed successfully */\n\n            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"http proxy header done\");\n\n            /*\n             * if no \"Server\" and \"Date\" in header line,\n             * then add the special empty headers\n             */\n\n            if (r->upstream->headers_in.server == NULL) {\n                h = ngx_list_push(&r->upstream->headers_in.headers);\n                if (h == NULL) {\n                    return NGX_ERROR;\n                }\n\n                h->hash = ngx_hash(ngx_hash(ngx_hash(ngx_hash(\n                                    ngx_hash('s', 'e'), 'r'), 'v'), 'e'), 'r');\n\n                ngx_str_set(&h->key, \"Server\");\n                ngx_str_null(&h->value);\n                h->lowcase_key = (u_char *) \"server\";\n                h->next = NULL;\n            }\n\n            if (r->upstream->headers_in.date == NULL) {\n                h = ngx_list_push(&r->upstream->headers_in.headers);\n                if (h == NULL) {\n                    return NGX_ERROR;\n                }\n\n                h->hash = ngx_hash(ngx_hash(ngx_hash('d', 'a'), 't'), 'e');\n\n                ngx_str_set(&h->key, \"Date\");\n                ngx_str_null(&h->value);\n                h->lowcase_key = (u_char *) \"date\";\n                h->next = NULL;\n            }\n\n            /* clear content length if response is chunked */\n\n            u = r->upstream;\n\n            if (u->headers_in.chunked) {\n                u->headers_in.content_length_n = -1;\n            }\n\n            /*\n             * set u->keepalive if response has no body; this allows to keep\n             * connections alive in case of r->header_only or X-Accel-Redirect\n             */\n\n            ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);\n\n            if (u->headers_in.status_n == NGX_HTTP_NO_CONTENT\n                || u->headers_in.status_n == NGX_HTTP_NOT_MODIFIED\n                || ctx->head\n                || (!u->headers_in.chunked\n                    && u->headers_in.content_length_n == 0))\n            {\n                u->keepalive = !u->headers_in.connection_close;\n            }\n\n            if (u->headers_in.status_n == NGX_HTTP_SWITCHING_PROTOCOLS) {\n                u->keepalive = 0;\n\n                if (r->headers_in.upgrade) {\n                    u->upgrade = 1;\n                }\n            }\n\n            return NGX_OK;\n        }\n\n        if (rc == NGX_AGAIN) {\n            return NGX_AGAIN;\n        }\n\n        /* rc == NGX_HTTP_PARSE_INVALID_HEADER */\n\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"upstream sent invalid header: \\\"%*s\\\\x%02xd...\\\"\",\n                      r->header_end - r->header_name_start,\n                      r->header_name_start, *r->header_end);\n\n        return NGX_HTTP_UPSTREAM_INVALID_HEADER;\n    }\n}\n\n\nstatic ngx_int_t\nngx_http_proxy_input_filter_init(void *data)\n{\n    ngx_http_request_t    *r = data;\n    ngx_http_upstream_t   *u;\n    ngx_http_proxy_ctx_t  *ctx;\n\n    u = r->upstream;\n    ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);\n\n    if (ctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http proxy filter init s:%ui h:%d c:%d l:%O\",\n                   u->headers_in.status_n, ctx->head, u->headers_in.chunked,\n                   u->headers_in.content_length_n);\n\n    /* as per RFC2616, 4.4 Message Length */\n\n    if (u->headers_in.status_n == NGX_HTTP_NO_CONTENT\n        || u->headers_in.status_n == NGX_HTTP_NOT_MODIFIED\n        || ctx->head)\n    {\n        /* 1xx, 204, and 304 and replies to HEAD requests */\n        /* no 1xx since we don't send Expect and Upgrade */\n\n        u->pipe->length = 0;\n        u->length = 0;\n        u->keepalive = !u->headers_in.connection_close;\n\n    } else if (u->headers_in.chunked) {\n        /* chunked */\n\n        u->pipe->input_filter = ngx_http_proxy_chunked_filter;\n        u->pipe->length = 3; /* \"0\" LF LF */\n\n        u->input_filter = ngx_http_proxy_non_buffered_chunked_filter;\n        u->length = 1;\n\n    } else if (u->headers_in.content_length_n == 0) {\n        /* empty body: special case as filter won't be called */\n\n        u->pipe->length = 0;\n        u->length = 0;\n        u->keepalive = !u->headers_in.connection_close;\n\n    } else {\n        /* content length or connection close */\n\n        u->pipe->length = u->headers_in.content_length_n;\n        u->length = u->headers_in.content_length_n;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_proxy_copy_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)\n{\n    ngx_buf_t           *b;\n    ngx_chain_t         *cl;\n    ngx_http_request_t  *r;\n\n    if (buf->pos == buf->last) {\n        return NGX_OK;\n    }\n\n    if (p->upstream_done) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, p->log, 0,\n                       \"http proxy data after close\");\n        return NGX_OK;\n    }\n\n    if (p->length == 0) {\n\n        ngx_log_error(NGX_LOG_WARN, p->log, 0,\n                      \"upstream sent more data than specified in \"\n                      \"\\\"Content-Length\\\" header\");\n\n        r = p->input_ctx;\n        r->upstream->keepalive = 0;\n        p->upstream_done = 1;\n\n        return NGX_OK;\n    }\n\n    cl = ngx_chain_get_free_buf(p->pool, &p->free);\n    if (cl == NULL) {\n        return NGX_ERROR;\n    }\n\n    b = cl->buf;\n\n    ngx_memcpy(b, buf, sizeof(ngx_buf_t));\n    b->shadow = buf;\n    b->tag = p->tag;\n    b->last_shadow = 1;\n    b->recycled = 1;\n    buf->shadow = b;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, p->log, 0, \"input buf #%d\", b->num);\n\n    if (p->in) {\n        *p->last_in = cl;\n    } else {\n        p->in = cl;\n    }\n    p->last_in = &cl->next;\n\n    if (p->length == -1) {\n        return NGX_OK;\n    }\n\n    if (b->last - b->pos > p->length) {\n\n        ngx_log_error(NGX_LOG_WARN, p->log, 0,\n                      \"upstream sent more data than specified in \"\n                      \"\\\"Content-Length\\\" header\");\n\n        b->last = b->pos + p->length;\n        p->upstream_done = 1;\n\n        return NGX_OK;\n    }\n\n    p->length -= b->last - b->pos;\n\n    if (p->length == 0) {\n        r = p->input_ctx;\n        r->upstream->keepalive = !r->upstream->headers_in.connection_close;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_proxy_chunked_filter(ngx_event_pipe_t *p, ngx_buf_t *buf)\n{\n    ngx_int_t              rc;\n    ngx_buf_t             *b, **prev;\n    ngx_chain_t           *cl;\n    ngx_http_request_t    *r;\n    ngx_http_proxy_ctx_t  *ctx;\n\n    if (buf->pos == buf->last) {\n        return NGX_OK;\n    }\n\n    r = p->input_ctx;\n    ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);\n\n    if (ctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (p->upstream_done) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, p->log, 0,\n                       \"http proxy data after close\");\n        return NGX_OK;\n    }\n\n    if (p->length == 0) {\n\n        ngx_log_error(NGX_LOG_WARN, p->log, 0,\n                      \"upstream sent data after final chunk\");\n\n        r->upstream->keepalive = 0;\n        p->upstream_done = 1;\n\n        return NGX_OK;\n    }\n\n    b = NULL;\n    prev = &buf->shadow;\n\n    for ( ;; ) {\n\n        rc = ngx_http_parse_chunked(r, buf, &ctx->chunked);\n\n        if (rc == NGX_OK) {\n\n            /* a chunk has been parsed successfully */\n\n            cl = ngx_chain_get_free_buf(p->pool, &p->free);\n            if (cl == NULL) {\n                return NGX_ERROR;\n            }\n\n            b = cl->buf;\n\n            ngx_memzero(b, sizeof(ngx_buf_t));\n\n            b->pos = buf->pos;\n            b->start = buf->start;\n            b->end = buf->end;\n            b->tag = p->tag;\n            b->temporary = 1;\n            b->recycled = 1;\n\n            *prev = b;\n            prev = &b->shadow;\n\n            if (p->in) {\n                *p->last_in = cl;\n            } else {\n                p->in = cl;\n            }\n            p->last_in = &cl->next;\n\n            /* STUB */ b->num = buf->num;\n\n            ngx_log_debug2(NGX_LOG_DEBUG_EVENT, p->log, 0,\n                           \"input buf #%d %p\", b->num, b->pos);\n\n            if (buf->last - buf->pos >= ctx->chunked.size) {\n\n                buf->pos += (size_t) ctx->chunked.size;\n                b->last = buf->pos;\n                ctx->chunked.size = 0;\n\n                continue;\n            }\n\n            ctx->chunked.size -= buf->last - buf->pos;\n            buf->pos = buf->last;\n            b->last = buf->last;\n\n            continue;\n        }\n\n        if (rc == NGX_DONE) {\n\n            /* a whole response has been parsed successfully */\n\n            p->length = 0;\n            r->upstream->keepalive = !r->upstream->headers_in.connection_close;\n\n            if (buf->pos != buf->last) {\n                ngx_log_error(NGX_LOG_WARN, p->log, 0,\n                              \"upstream sent data after final chunk\");\n                r->upstream->keepalive = 0;\n            }\n\n            break;\n        }\n\n        if (rc == NGX_AGAIN) {\n\n            /* set p->length, minimal amount of data we want to see */\n\n            p->length = ctx->chunked.length;\n\n            break;\n        }\n\n        /* invalid response */\n\n        ngx_log_error(NGX_LOG_ERR, p->log, 0,\n                      \"upstream sent invalid chunked response\");\n\n        return NGX_ERROR;\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, p->log, 0,\n                   \"http proxy chunked state %ui, length %O\",\n                   ctx->chunked.state, p->length);\n\n    if (b) {\n        b->shadow = buf;\n        b->last_shadow = 1;\n\n        ngx_log_debug2(NGX_LOG_DEBUG_EVENT, p->log, 0,\n                       \"input buf %p %z\", b->pos, b->last - b->pos);\n\n        return NGX_OK;\n    }\n\n    /* there is no data record in the buf, add it to free chain */\n\n    if (ngx_event_pipe_add_free_buf(p, buf) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_proxy_non_buffered_copy_filter(void *data, ssize_t bytes)\n{\n    ngx_http_request_t   *r = data;\n\n    ngx_buf_t            *b;\n    ngx_chain_t          *cl, **ll;\n    ngx_http_upstream_t  *u;\n\n    u = r->upstream;\n\n    if (u->length == 0) {\n        ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,\n                      \"upstream sent more data than specified in \"\n                      \"\\\"Content-Length\\\" header\");\n        u->keepalive = 0;\n        return NGX_OK;\n    }\n\n    for (cl = u->out_bufs, ll = &u->out_bufs; cl; cl = cl->next) {\n        ll = &cl->next;\n    }\n\n    cl = ngx_chain_get_free_buf(r->pool, &u->free_bufs);\n    if (cl == NULL) {\n        return NGX_ERROR;\n    }\n\n    *ll = cl;\n\n    cl->buf->flush = 1;\n    cl->buf->memory = 1;\n\n    b = &u->buffer;\n\n    cl->buf->pos = b->last;\n    b->last += bytes;\n    cl->buf->last = b->last;\n    cl->buf->tag = u->output.tag;\n\n    if (u->length == -1) {\n        return NGX_OK;\n    }\n\n    if (bytes > u->length) {\n\n        ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,\n                      \"upstream sent more data than specified in \"\n                      \"\\\"Content-Length\\\" header\");\n\n        cl->buf->last = cl->buf->pos + u->length;\n        u->length = 0;\n\n        return NGX_OK;\n    }\n\n    u->length -= bytes;\n\n    if (u->length == 0) {\n        u->keepalive = !u->headers_in.connection_close;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_proxy_non_buffered_chunked_filter(void *data, ssize_t bytes)\n{\n    ngx_http_request_t   *r = data;\n\n    ngx_int_t              rc;\n    ngx_buf_t             *b, *buf;\n    ngx_chain_t           *cl, **ll;\n    ngx_http_upstream_t   *u;\n    ngx_http_proxy_ctx_t  *ctx;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);\n\n    if (ctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    u = r->upstream;\n    buf = &u->buffer;\n\n    buf->pos = buf->last;\n    buf->last += bytes;\n\n    for (cl = u->out_bufs, ll = &u->out_bufs; cl; cl = cl->next) {\n        ll = &cl->next;\n    }\n\n    for ( ;; ) {\n\n        rc = ngx_http_parse_chunked(r, buf, &ctx->chunked);\n\n        if (rc == NGX_OK) {\n\n            /* a chunk has been parsed successfully */\n\n            cl = ngx_chain_get_free_buf(r->pool, &u->free_bufs);\n            if (cl == NULL) {\n                return NGX_ERROR;\n            }\n\n            *ll = cl;\n            ll = &cl->next;\n\n            b = cl->buf;\n\n            b->flush = 1;\n            b->memory = 1;\n\n            b->pos = buf->pos;\n            b->tag = u->output.tag;\n\n            if (buf->last - buf->pos >= ctx->chunked.size) {\n                buf->pos += (size_t) ctx->chunked.size;\n                b->last = buf->pos;\n                ctx->chunked.size = 0;\n\n            } else {\n                ctx->chunked.size -= buf->last - buf->pos;\n                buf->pos = buf->last;\n                b->last = buf->last;\n            }\n\n            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"http proxy out buf %p %z\",\n                           b->pos, b->last - b->pos);\n\n            continue;\n        }\n\n        if (rc == NGX_DONE) {\n\n            /* a whole response has been parsed successfully */\n\n            u->keepalive = !u->headers_in.connection_close;\n            u->length = 0;\n\n            if (buf->pos != buf->last) {\n                ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,\n                              \"upstream sent data after final chunk\");\n                u->keepalive = 0;\n            }\n\n            break;\n        }\n\n        if (rc == NGX_AGAIN) {\n            break;\n        }\n\n        /* invalid response */\n\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"upstream sent invalid chunked response\");\n\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_proxy_abort_request(ngx_http_request_t *r)\n{\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"abort http proxy request\");\n\n    return;\n}\n\n\nstatic void\nngx_http_proxy_finalize_request(ngx_http_request_t *r, ngx_int_t rc)\n{\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"finalize http proxy request\");\n\n    return;\n}\n\n\nstatic ngx_int_t\nngx_http_proxy_host_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    ngx_http_proxy_ctx_t  *ctx;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);\n\n    if (ctx == NULL) {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    v->len = ctx->vars.host_header.len;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = ctx->vars.host_header.data;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_proxy_port_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    ngx_http_proxy_ctx_t  *ctx;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);\n\n    if (ctx == NULL) {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    v->len = ctx->vars.port.len;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = ctx->vars.port.data;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_proxy_add_x_forwarded_for_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    size_t            len;\n    u_char           *p;\n    ngx_table_elt_t  *h, *xfwd;\n\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    xfwd = r->headers_in.x_forwarded_for;\n\n    len = 0;\n\n    for (h = xfwd; h; h = h->next) {\n        len += h->value.len + sizeof(\", \") - 1;\n    }\n\n    if (len == 0) {\n        v->len = r->connection->addr_text.len;\n        v->data = r->connection->addr_text.data;\n        return NGX_OK;\n    }\n\n    len += r->connection->addr_text.len;\n\n    p = ngx_pnalloc(r->pool, len);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    v->len = len;\n    v->data = p;\n\n    for (h = xfwd; h; h = h->next) {\n        p = ngx_copy(p, h->value.data, h->value.len);\n        *p++ = ','; *p++ = ' ';\n    }\n\n    ngx_memcpy(p, r->connection->addr_text.data, r->connection->addr_text.len);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_proxy_internal_body_length_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    ngx_http_proxy_ctx_t  *ctx;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);\n\n    if (ctx == NULL || ctx->internal_body_length < 0) {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    v->data = ngx_pnalloc(r->pool, NGX_OFF_T_LEN);\n\n    if (v->data == NULL) {\n        return NGX_ERROR;\n    }\n\n    v->len = ngx_sprintf(v->data, \"%O\", ctx->internal_body_length) - v->data;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_proxy_internal_chunked_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    ngx_http_proxy_ctx_t  *ctx;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_proxy_module);\n\n    if (ctx == NULL || !ctx->internal_chunked) {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    v->data = (u_char *) \"chunked\";\n    v->len = sizeof(\"chunked\") - 1;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_proxy_rewrite_redirect(ngx_http_request_t *r, ngx_table_elt_t *h,\n    size_t prefix)\n{\n    size_t                      len;\n    ngx_int_t                   rc;\n    ngx_uint_t                  i;\n    ngx_http_proxy_rewrite_t   *pr;\n    ngx_http_proxy_loc_conf_t  *plcf;\n\n    plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module);\n\n    pr = plcf->redirects->elts;\n\n    if (pr == NULL) {\n        return NGX_DECLINED;\n    }\n\n    len = h->value.len - prefix;\n\n    for (i = 0; i < plcf->redirects->nelts; i++) {\n        rc = pr[i].handler(r, &h->value, prefix, len, &pr[i]);\n\n        if (rc != NGX_DECLINED) {\n            return rc;\n        }\n    }\n\n    return NGX_DECLINED;\n}\n\n\nstatic ngx_int_t\nngx_http_proxy_rewrite_cookie(ngx_http_request_t *r, ngx_table_elt_t *h)\n{\n    u_char                     *p;\n    size_t                      len;\n    ngx_int_t                   rc, rv;\n    ngx_str_t                  *key, *value;\n    ngx_uint_t                  i;\n    ngx_array_t                 attrs;\n    ngx_keyval_t               *attr;\n    ngx_http_proxy_loc_conf_t  *plcf;\n\n    if (ngx_array_init(&attrs, r->pool, 2, sizeof(ngx_keyval_t)) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    if (ngx_http_proxy_parse_cookie(&h->value, &attrs) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    attr = attrs.elts;\n\n    if (attr[0].value.data == NULL) {\n        return NGX_DECLINED;\n    }\n\n    rv = NGX_DECLINED;\n\n    plcf = ngx_http_get_module_loc_conf(r, ngx_http_proxy_module);\n\n    for (i = 1; i < attrs.nelts; i++) {\n\n        key = &attr[i].key;\n        value = &attr[i].value;\n\n        if (plcf->cookie_domains && key->len == 6\n            && ngx_strncasecmp(key->data, (u_char *) \"domain\", 6) == 0\n            && value->data)\n        {\n            rc = ngx_http_proxy_rewrite_cookie_value(r, value,\n                                                     plcf->cookie_domains);\n            if (rc == NGX_ERROR) {\n                return NGX_ERROR;\n            }\n\n            if (rc != NGX_DECLINED) {\n                rv = rc;\n            }\n        }\n\n        if (plcf->cookie_paths && key->len == 4\n            && ngx_strncasecmp(key->data, (u_char *) \"path\", 4) == 0\n            && value->data)\n        {\n            rc = ngx_http_proxy_rewrite_cookie_value(r, value,\n                                                     plcf->cookie_paths);\n            if (rc == NGX_ERROR) {\n                return NGX_ERROR;\n            }\n\n            if (rc != NGX_DECLINED) {\n                rv = rc;\n            }\n        }\n    }\n\n    if (plcf->cookie_flags) {\n        rc = ngx_http_proxy_rewrite_cookie_flags(r, &attrs,\n                                                 plcf->cookie_flags);\n        if (rc == NGX_ERROR) {\n            return NGX_ERROR;\n        }\n\n        if (rc != NGX_DECLINED) {\n            rv = rc;\n        }\n\n        attr = attrs.elts;\n    }\n\n    if (rv != NGX_OK) {\n        return rv;\n    }\n\n    len = 0;\n\n    for (i = 0; i < attrs.nelts; i++) {\n\n        if (attr[i].key.data == NULL) {\n            continue;\n        }\n\n        if (i > 0) {\n            len += 2;\n        }\n\n        len += attr[i].key.len;\n\n        if (attr[i].value.data) {\n            len += 1 + attr[i].value.len;\n        }\n    }\n\n    p = ngx_pnalloc(r->pool, len + 1);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    h->value.data = p;\n    h->value.len = len;\n\n    for (i = 0; i < attrs.nelts; i++) {\n\n        if (attr[i].key.data == NULL) {\n            continue;\n        }\n\n        if (i > 0) {\n            *p++ = ';';\n            *p++ = ' ';\n        }\n\n        p = ngx_cpymem(p, attr[i].key.data, attr[i].key.len);\n\n        if (attr[i].value.data) {\n            *p++ = '=';\n            p = ngx_cpymem(p, attr[i].value.data, attr[i].value.len);\n        }\n    }\n\n    *p = '\\0';\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_proxy_parse_cookie(ngx_str_t *value, ngx_array_t *attrs)\n{\n    u_char        *start, *end, *p, *last;\n    ngx_str_t      name, val;\n    ngx_keyval_t  *attr;\n\n    start = value->data;\n    end = value->data + value->len;\n\n    for ( ;; ) {\n\n        last = (u_char *) ngx_strchr(start, ';');\n\n        if (last == NULL) {\n            last = end;\n        }\n\n        while (start < last && *start == ' ') { start++; }\n\n        for (p = start; p < last && *p != '='; p++) { /* void */ }\n\n        name.data = start;\n        name.len = p - start;\n\n        while (name.len && name.data[name.len - 1] == ' ') {\n            name.len--;\n        }\n\n        if (p < last) {\n\n            p++;\n\n            while (p < last && *p == ' ') { p++; }\n\n            val.data = p;\n            val.len = last - val.data;\n\n            while (val.len && val.data[val.len - 1] == ' ') {\n                val.len--;\n            }\n\n        } else {\n            ngx_str_null(&val);\n        }\n\n        attr = ngx_array_push(attrs);\n        if (attr == NULL) {\n            return NGX_ERROR;\n        }\n\n        attr->key = name;\n        attr->value = val;\n\n        if (last == end) {\n            break;\n        }\n\n        start = last + 1;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_proxy_rewrite_cookie_value(ngx_http_request_t *r, ngx_str_t *value,\n    ngx_array_t *rewrites)\n{\n    ngx_int_t                  rc;\n    ngx_uint_t                 i;\n    ngx_http_proxy_rewrite_t  *pr;\n\n    pr = rewrites->elts;\n\n    for (i = 0; i < rewrites->nelts; i++) {\n        rc = pr[i].handler(r, value, 0, value->len, &pr[i]);\n\n        if (rc != NGX_DECLINED) {\n            return rc;\n        }\n    }\n\n    return NGX_DECLINED;\n}\n\n\nstatic ngx_int_t\nngx_http_proxy_rewrite_cookie_flags(ngx_http_request_t *r, ngx_array_t *attrs,\n    ngx_array_t *flags)\n{\n    ngx_str_t                       pattern, value;\n#if (NGX_PCRE)\n    ngx_int_t                       rc;\n#endif\n    ngx_uint_t                      i, m, f, nelts;\n    ngx_keyval_t                   *attr;\n    ngx_conf_bitmask_t             *mask;\n    ngx_http_complex_value_t       *flags_values;\n    ngx_http_proxy_cookie_flags_t  *pcf;\n\n    attr = attrs->elts;\n    pcf = flags->elts;\n\n    for (i = 0; i < flags->nelts; i++) {\n\n#if (NGX_PCRE)\n        if (pcf[i].regex) {\n            rc = ngx_http_regex_exec(r, pcf[i].cookie.regex, &attr[0].key);\n\n            if (rc == NGX_ERROR) {\n                return NGX_ERROR;\n            }\n\n            if (rc == NGX_OK) {\n                break;\n            }\n\n            /* NGX_DECLINED */\n\n            continue;\n        }\n#endif\n\n        if (ngx_http_complex_value(r, &pcf[i].cookie.complex, &pattern)\n            != NGX_OK)\n        {\n            return NGX_ERROR;\n        }\n\n        if (pattern.len == attr[0].key.len\n            && ngx_strncasecmp(attr[0].key.data, pattern.data, pattern.len)\n               == 0)\n        {\n            break;\n        }\n    }\n\n    if (i == flags->nelts) {\n        return NGX_DECLINED;\n    }\n\n    nelts = pcf[i].flags_values.nelts;\n    flags_values = pcf[i].flags_values.elts;\n\n    mask = ngx_http_proxy_cookie_flags_masks;\n    f = 0;\n\n    for (i = 0; i < nelts; i++) {\n\n        if (ngx_http_complex_value(r, &flags_values[i], &value) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        if (value.len == 0) {\n            continue;\n        }\n\n        for (m = 0; mask[m].name.len != 0; m++) {\n\n            if (mask[m].name.len != value.len\n                || ngx_strncasecmp(mask[m].name.data, value.data, value.len)\n                   != 0)\n            {\n                continue;\n            }\n\n            f |= mask[m].mask;\n\n            break;\n        }\n\n        if (mask[m].name.len == 0) {\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"invalid proxy_cookie_flags flag \\\"%V\\\"\", &value);\n        }\n    }\n\n    if (f == 0) {\n        return NGX_DECLINED;\n    }\n\n    return ngx_http_proxy_edit_cookie_flags(r, attrs, f);\n}\n\n\nstatic ngx_int_t\nngx_http_proxy_edit_cookie_flags(ngx_http_request_t *r, ngx_array_t *attrs,\n    ngx_uint_t flags)\n{\n    ngx_str_t     *key, *value;\n    ngx_uint_t     i;\n    ngx_keyval_t  *attr;\n\n    attr = attrs->elts;\n\n    for (i = 1; i < attrs->nelts; i++) {\n        key = &attr[i].key;\n\n        if (key->len == 6\n            && ngx_strncasecmp(key->data, (u_char *) \"secure\", 6) == 0)\n        {\n            if (flags & NGX_HTTP_PROXY_COOKIE_SECURE_ON) {\n                flags &= ~NGX_HTTP_PROXY_COOKIE_SECURE_ON;\n\n            } else if (flags & NGX_HTTP_PROXY_COOKIE_SECURE_OFF) {\n                key->data = NULL;\n            }\n\n            continue;\n        }\n\n        if (key->len == 8\n            && ngx_strncasecmp(key->data, (u_char *) \"httponly\", 8) == 0)\n        {\n            if (flags & NGX_HTTP_PROXY_COOKIE_HTTPONLY_ON) {\n                flags &= ~NGX_HTTP_PROXY_COOKIE_HTTPONLY_ON;\n\n            } else if (flags & NGX_HTTP_PROXY_COOKIE_HTTPONLY_OFF) {\n                key->data = NULL;\n            }\n\n            continue;\n        }\n\n        if (key->len == 8\n            && ngx_strncasecmp(key->data, (u_char *) \"samesite\", 8) == 0)\n        {\n            value = &attr[i].value;\n\n            if (flags & NGX_HTTP_PROXY_COOKIE_SAMESITE_STRICT) {\n                flags &= ~NGX_HTTP_PROXY_COOKIE_SAMESITE_STRICT;\n\n                if (value->len != 6\n                    || ngx_strncasecmp(value->data, (u_char *) \"strict\", 6)\n                       != 0)\n                {\n                    ngx_str_set(key, \"SameSite\");\n                    ngx_str_set(value, \"Strict\");\n                }\n\n            } else if (flags & NGX_HTTP_PROXY_COOKIE_SAMESITE_LAX) {\n                flags &= ~NGX_HTTP_PROXY_COOKIE_SAMESITE_LAX;\n\n                if (value->len != 3\n                    || ngx_strncasecmp(value->data, (u_char *) \"lax\", 3) != 0)\n                {\n                    ngx_str_set(key, \"SameSite\");\n                    ngx_str_set(value, \"Lax\");\n                }\n\n            } else if (flags & NGX_HTTP_PROXY_COOKIE_SAMESITE_NONE) {\n                flags &= ~NGX_HTTP_PROXY_COOKIE_SAMESITE_NONE;\n\n                if (value->len != 4\n                    || ngx_strncasecmp(value->data, (u_char *) \"none\", 4) != 0)\n                {\n                    ngx_str_set(key, \"SameSite\");\n                    ngx_str_set(value, \"None\");\n                }\n\n            } else if (flags & NGX_HTTP_PROXY_COOKIE_SAMESITE_OFF) {\n                key->data = NULL;\n            }\n\n            continue;\n        }\n    }\n\n    if (flags & NGX_HTTP_PROXY_COOKIE_SECURE_ON) {\n        attr = ngx_array_push(attrs);\n        if (attr == NULL) {\n            return NGX_ERROR;\n        }\n\n        ngx_str_set(&attr->key, \"Secure\");\n        ngx_str_null(&attr->value);\n    }\n\n    if (flags & NGX_HTTP_PROXY_COOKIE_HTTPONLY_ON) {\n        attr = ngx_array_push(attrs);\n        if (attr == NULL) {\n            return NGX_ERROR;\n        }\n\n        ngx_str_set(&attr->key, \"HttpOnly\");\n        ngx_str_null(&attr->value);\n    }\n\n    if (flags & (NGX_HTTP_PROXY_COOKIE_SAMESITE_STRICT\n                 |NGX_HTTP_PROXY_COOKIE_SAMESITE_LAX\n                 |NGX_HTTP_PROXY_COOKIE_SAMESITE_NONE))\n    {\n        attr = ngx_array_push(attrs);\n        if (attr == NULL) {\n            return NGX_ERROR;\n        }\n\n        ngx_str_set(&attr->key, \"SameSite\");\n\n        if (flags & NGX_HTTP_PROXY_COOKIE_SAMESITE_STRICT) {\n            ngx_str_set(&attr->value, \"Strict\");\n\n        } else if (flags & NGX_HTTP_PROXY_COOKIE_SAMESITE_LAX) {\n            ngx_str_set(&attr->value, \"Lax\");\n\n        } else {\n            ngx_str_set(&attr->value, \"None\");\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_proxy_rewrite_complex_handler(ngx_http_request_t *r, ngx_str_t *value,\n    size_t prefix, size_t len, ngx_http_proxy_rewrite_t *pr)\n{\n    ngx_str_t  pattern, replacement;\n\n    if (ngx_http_complex_value(r, &pr->pattern.complex, &pattern) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    if (pattern.len > len\n        || ngx_rstrncmp(value->data + prefix, pattern.data, pattern.len) != 0)\n    {\n        return NGX_DECLINED;\n    }\n\n    if (ngx_http_complex_value(r, &pr->replacement, &replacement) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    return ngx_http_proxy_rewrite(r, value, prefix, pattern.len, &replacement);\n}\n\n\n#if (NGX_PCRE)\n\nstatic ngx_int_t\nngx_http_proxy_rewrite_regex_handler(ngx_http_request_t *r, ngx_str_t *value,\n    size_t prefix, size_t len, ngx_http_proxy_rewrite_t *pr)\n{\n    ngx_str_t  pattern, replacement;\n\n    pattern.len = len;\n    pattern.data = value->data + prefix;\n\n    if (ngx_http_regex_exec(r, pr->pattern.regex, &pattern) != NGX_OK) {\n        return NGX_DECLINED;\n    }\n\n    if (ngx_http_complex_value(r, &pr->replacement, &replacement) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    return ngx_http_proxy_rewrite(r, value, prefix, len, &replacement);\n}\n\n#endif\n\n\nstatic ngx_int_t\nngx_http_proxy_rewrite_domain_handler(ngx_http_request_t *r, ngx_str_t *value,\n    size_t prefix, size_t len, ngx_http_proxy_rewrite_t *pr)\n{\n    u_char     *p;\n    ngx_str_t   pattern, replacement;\n\n    if (ngx_http_complex_value(r, &pr->pattern.complex, &pattern) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    p = value->data + prefix;\n\n    if (len && p[0] == '.') {\n        p++;\n        prefix++;\n        len--;\n    }\n\n    if (pattern.len != len || ngx_rstrncasecmp(pattern.data, p, len) != 0) {\n        return NGX_DECLINED;\n    }\n\n    if (ngx_http_complex_value(r, &pr->replacement, &replacement) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    return ngx_http_proxy_rewrite(r, value, prefix, len, &replacement);\n}\n\n\nstatic ngx_int_t\nngx_http_proxy_rewrite(ngx_http_request_t *r, ngx_str_t *value, size_t prefix,\n    size_t len, ngx_str_t *replacement)\n{\n    u_char  *p, *data;\n    size_t   new_len;\n\n    if (len == value->len) {\n        *value = *replacement;\n        return NGX_OK;\n    }\n\n    new_len = replacement->len + value->len - len;\n\n    if (replacement->len > len) {\n\n        data = ngx_pnalloc(r->pool, new_len + 1);\n        if (data == NULL) {\n            return NGX_ERROR;\n        }\n\n        p = ngx_copy(data, value->data, prefix);\n        p = ngx_copy(p, replacement->data, replacement->len);\n\n        ngx_memcpy(p, value->data + prefix + len,\n                   value->len - len - prefix + 1);\n\n        value->data = data;\n\n    } else {\n        p = ngx_copy(value->data + prefix, replacement->data, replacement->len);\n\n        ngx_memmove(p, value->data + prefix + len,\n                    value->len - len - prefix + 1);\n    }\n\n    value->len = new_len;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_proxy_add_variables(ngx_conf_t *cf)\n{\n    ngx_http_variable_t  *var, *v;\n\n    for (v = ngx_http_proxy_vars; v->name.len; v++) {\n        var = ngx_http_add_variable(cf, &v->name, v->flags);\n        if (var == NULL) {\n            return NGX_ERROR;\n        }\n\n        var->get_handler = v->get_handler;\n        var->data = v->data;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void *\nngx_http_proxy_create_main_conf(ngx_conf_t *cf)\n{\n    ngx_http_proxy_main_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_proxy_main_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n#if (NGX_HTTP_CACHE)\n    if (ngx_array_init(&conf->caches, cf->pool, 4,\n                       sizeof(ngx_http_file_cache_t *))\n        != NGX_OK)\n    {\n        return NULL;\n    }\n#endif\n\n    return conf;\n}\n\n\nstatic void *\nngx_http_proxy_create_loc_conf(ngx_conf_t *cf)\n{\n    ngx_http_proxy_loc_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_proxy_loc_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc():\n     *\n     *     conf->upstream.bufs.num = 0;\n     *     conf->upstream.ignore_headers = 0;\n     *     conf->upstream.next_upstream = 0;\n     *     conf->upstream.cache_zone = NULL;\n     *     conf->upstream.cache_use_stale = 0;\n     *     conf->upstream.cache_methods = 0;\n     *     conf->upstream.temp_path = NULL;\n     *     conf->upstream.hide_headers_hash = { NULL, 0 };\n     *     conf->upstream.store_lengths = NULL;\n     *     conf->upstream.store_values = NULL;\n     *\n     *     conf->location = NULL;\n     *     conf->url = { 0, NULL };\n     *     conf->headers.lengths = NULL;\n     *     conf->headers.values = NULL;\n     *     conf->headers.hash = { NULL, 0 };\n     *     conf->headers_cache.lengths = NULL;\n     *     conf->headers_cache.values = NULL;\n     *     conf->headers_cache.hash = { NULL, 0 };\n     *     conf->body_lengths = NULL;\n     *     conf->body_values = NULL;\n     *     conf->body_source = { 0, NULL };\n     *     conf->redirects = NULL;\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     */\n\n    conf->upstream.store = NGX_CONF_UNSET;\n    conf->upstream.store_access = NGX_CONF_UNSET_UINT;\n    conf->upstream.next_upstream_tries = NGX_CONF_UNSET_UINT;\n    conf->upstream.buffering = NGX_CONF_UNSET;\n    conf->upstream.request_buffering = NGX_CONF_UNSET;\n    conf->upstream.ignore_client_abort = NGX_CONF_UNSET;\n    conf->upstream.force_ranges = NGX_CONF_UNSET;\n\n    conf->upstream.local = NGX_CONF_UNSET_PTR;\n    conf->upstream.socket_keepalive = NGX_CONF_UNSET;\n\n    conf->upstream.connect_timeout = NGX_CONF_UNSET_MSEC;\n    conf->upstream.send_timeout = NGX_CONF_UNSET_MSEC;\n    conf->upstream.read_timeout = NGX_CONF_UNSET_MSEC;\n    conf->upstream.next_upstream_timeout = NGX_CONF_UNSET_MSEC;\n\n    conf->upstream.send_lowat = NGX_CONF_UNSET_SIZE;\n    conf->upstream.buffer_size = NGX_CONF_UNSET_SIZE;\n    conf->upstream.limit_rate = NGX_CONF_UNSET_SIZE;\n\n    conf->upstream.busy_buffers_size_conf = NGX_CONF_UNSET_SIZE;\n    conf->upstream.max_temp_file_size_conf = NGX_CONF_UNSET_SIZE;\n    conf->upstream.temp_file_write_size_conf = NGX_CONF_UNSET_SIZE;\n\n    conf->upstream.pass_request_headers = NGX_CONF_UNSET;\n    conf->upstream.pass_request_body = NGX_CONF_UNSET;\n\n#if (NGX_HTTP_CACHE)\n    conf->upstream.cache = NGX_CONF_UNSET;\n    conf->upstream.cache_min_uses = NGX_CONF_UNSET_UINT;\n    conf->upstream.cache_max_range_offset = NGX_CONF_UNSET;\n    conf->upstream.cache_bypass = NGX_CONF_UNSET_PTR;\n    conf->upstream.no_cache = NGX_CONF_UNSET_PTR;\n    conf->upstream.cache_valid = NGX_CONF_UNSET_PTR;\n    conf->upstream.cache_lock = NGX_CONF_UNSET;\n    conf->upstream.cache_lock_timeout = NGX_CONF_UNSET_MSEC;\n    conf->upstream.cache_lock_age = NGX_CONF_UNSET_MSEC;\n    conf->upstream.cache_revalidate = NGX_CONF_UNSET;\n    conf->upstream.cache_convert_head = NGX_CONF_UNSET;\n    conf->upstream.cache_background_update = NGX_CONF_UNSET;\n#endif\n\n    conf->upstream.hide_headers = NGX_CONF_UNSET_PTR;\n    conf->upstream.pass_headers = NGX_CONF_UNSET_PTR;\n\n    conf->upstream.intercept_errors = NGX_CONF_UNSET;\n\n#if (NGX_HTTP_SSL)\n    conf->upstream.ssl_session_reuse = NGX_CONF_UNSET;\n    conf->upstream.ssl_name = NGX_CONF_UNSET_PTR;\n    conf->upstream.ssl_server_name = NGX_CONF_UNSET;\n    conf->upstream.ssl_verify = NGX_CONF_UNSET;\n    conf->upstream.ssl_certificate = NGX_CONF_UNSET_PTR;\n    conf->upstream.ssl_certificate_key = NGX_CONF_UNSET_PTR;\n    conf->upstream.ssl_passwords = NGX_CONF_UNSET_PTR;\n    conf->ssl_verify_depth = NGX_CONF_UNSET_UINT;\n    conf->ssl_conf_commands = NGX_CONF_UNSET_PTR;\n\n#if (T_NGX_SSL_NTLS)\n    conf->upstream.tls_method = NULL;\n    conf->upstream.enable_ntls = NULL;\n#endif\n#endif\n\n    /* \"proxy_cyclic_temp_file\" is disabled */\n    conf->upstream.cyclic_temp_file = 0;\n\n    conf->upstream.change_buffering = 1;\n\n    conf->headers_source = NGX_CONF_UNSET_PTR;\n\n    conf->method = NGX_CONF_UNSET_PTR;\n\n    conf->redirect = NGX_CONF_UNSET;\n\n    conf->cookie_domains = NGX_CONF_UNSET_PTR;\n    conf->cookie_paths = NGX_CONF_UNSET_PTR;\n    conf->cookie_flags = NGX_CONF_UNSET_PTR;\n\n    conf->http_version = NGX_CONF_UNSET_UINT;\n\n    conf->headers_hash_max_size = NGX_CONF_UNSET_UINT;\n    conf->headers_hash_bucket_size = NGX_CONF_UNSET_UINT;\n\n    ngx_str_set(&conf->upstream.module, \"proxy\");\n\n#if (T_NGX_SOCKET_BUFFER)\n    conf->sndbuf = NGX_CONF_UNSET_SIZE;\n    conf->rcvbuf = NGX_CONF_UNSET_SIZE;\n#endif\n\n    return conf;\n}\n\n\nstatic char *\nngx_http_proxy_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_http_proxy_loc_conf_t *prev = parent;\n    ngx_http_proxy_loc_conf_t *conf = child;\n\n    u_char                     *p;\n    size_t                      size;\n    ngx_int_t                   rc;\n    ngx_hash_init_t             hash;\n    ngx_http_core_loc_conf_t   *clcf;\n    ngx_http_proxy_rewrite_t   *pr;\n    ngx_http_script_compile_t   sc;\n\n#if (NGX_HTTP_CACHE)\n\n    if (conf->upstream.store > 0) {\n        conf->upstream.cache = 0;\n    }\n\n    if (conf->upstream.cache > 0) {\n        conf->upstream.store = 0;\n    }\n\n#endif\n\n    if (conf->upstream.store == NGX_CONF_UNSET) {\n        ngx_conf_merge_value(conf->upstream.store,\n                              prev->upstream.store, 0);\n\n        conf->upstream.store_lengths = prev->upstream.store_lengths;\n        conf->upstream.store_values = prev->upstream.store_values;\n    }\n\n    ngx_conf_merge_uint_value(conf->upstream.store_access,\n                              prev->upstream.store_access, 0600);\n\n    ngx_conf_merge_uint_value(conf->upstream.next_upstream_tries,\n                              prev->upstream.next_upstream_tries, 0);\n\n    ngx_conf_merge_value(conf->upstream.buffering,\n                              prev->upstream.buffering, 1);\n\n    ngx_conf_merge_value(conf->upstream.request_buffering,\n                              prev->upstream.request_buffering, 1);\n\n    ngx_conf_merge_value(conf->upstream.ignore_client_abort,\n                              prev->upstream.ignore_client_abort, 0);\n\n    ngx_conf_merge_value(conf->upstream.force_ranges,\n                              prev->upstream.force_ranges, 0);\n\n    ngx_conf_merge_ptr_value(conf->upstream.local,\n                              prev->upstream.local, NULL);\n\n    ngx_conf_merge_value(conf->upstream.socket_keepalive,\n                              prev->upstream.socket_keepalive, 0);\n\n    ngx_conf_merge_msec_value(conf->upstream.connect_timeout,\n                              prev->upstream.connect_timeout, 60000);\n\n    ngx_conf_merge_msec_value(conf->upstream.send_timeout,\n                              prev->upstream.send_timeout, 60000);\n\n    ngx_conf_merge_msec_value(conf->upstream.read_timeout,\n                              prev->upstream.read_timeout, 60000);\n\n    ngx_conf_merge_msec_value(conf->upstream.next_upstream_timeout,\n                              prev->upstream.next_upstream_timeout, 0);\n\n    ngx_conf_merge_size_value(conf->upstream.send_lowat,\n                              prev->upstream.send_lowat, 0);\n\n    ngx_conf_merge_size_value(conf->upstream.buffer_size,\n                              prev->upstream.buffer_size,\n                              (size_t) ngx_pagesize);\n\n    ngx_conf_merge_size_value(conf->upstream.limit_rate,\n                              prev->upstream.limit_rate, 0);\n\n#if (T_NGX_SOCKET_BUFFER)\n    ngx_conf_merge_size_value(conf->sndbuf, prev->sndbuf, (size_t) 0);\n    ngx_conf_merge_size_value(conf->rcvbuf, prev->rcvbuf, (size_t) 0);\n#endif\n\n    ngx_conf_merge_bufs_value(conf->upstream.bufs, prev->upstream.bufs,\n                              8, ngx_pagesize);\n\n    if (conf->upstream.bufs.num < 2) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"there must be at least 2 \\\"proxy_buffers\\\"\");\n        return NGX_CONF_ERROR;\n    }\n\n\n    size = conf->upstream.buffer_size;\n    if (size < conf->upstream.bufs.size) {\n        size = conf->upstream.bufs.size;\n    }\n\n\n    ngx_conf_merge_size_value(conf->upstream.busy_buffers_size_conf,\n                              prev->upstream.busy_buffers_size_conf,\n                              NGX_CONF_UNSET_SIZE);\n\n    if (conf->upstream.busy_buffers_size_conf == NGX_CONF_UNSET_SIZE) {\n        conf->upstream.busy_buffers_size = 2 * size;\n    } else {\n        conf->upstream.busy_buffers_size =\n                                         conf->upstream.busy_buffers_size_conf;\n    }\n\n    if (conf->upstream.busy_buffers_size < size) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n             \"\\\"proxy_busy_buffers_size\\\" must be equal to or greater than \"\n             \"the maximum of the value of \\\"proxy_buffer_size\\\" and \"\n             \"one of the \\\"proxy_buffers\\\"\");\n\n        return NGX_CONF_ERROR;\n    }\n\n    if (conf->upstream.busy_buffers_size\n        > (conf->upstream.bufs.num - 1) * conf->upstream.bufs.size)\n    {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n             \"\\\"proxy_busy_buffers_size\\\" must be less than \"\n             \"the size of all \\\"proxy_buffers\\\" minus one buffer\");\n\n        return NGX_CONF_ERROR;\n    }\n\n\n    ngx_conf_merge_size_value(conf->upstream.temp_file_write_size_conf,\n                              prev->upstream.temp_file_write_size_conf,\n                              NGX_CONF_UNSET_SIZE);\n\n    if (conf->upstream.temp_file_write_size_conf == NGX_CONF_UNSET_SIZE) {\n        conf->upstream.temp_file_write_size = 2 * size;\n    } else {\n        conf->upstream.temp_file_write_size =\n                                      conf->upstream.temp_file_write_size_conf;\n    }\n\n    if (conf->upstream.temp_file_write_size < size) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n             \"\\\"proxy_temp_file_write_size\\\" must be equal to or greater \"\n             \"than the maximum of the value of \\\"proxy_buffer_size\\\" and \"\n             \"one of the \\\"proxy_buffers\\\"\");\n\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_conf_merge_size_value(conf->upstream.max_temp_file_size_conf,\n                              prev->upstream.max_temp_file_size_conf,\n                              NGX_CONF_UNSET_SIZE);\n\n    if (conf->upstream.max_temp_file_size_conf == NGX_CONF_UNSET_SIZE) {\n        conf->upstream.max_temp_file_size = 1024 * 1024 * 1024;\n    } else {\n        conf->upstream.max_temp_file_size =\n                                        conf->upstream.max_temp_file_size_conf;\n    }\n\n    if (conf->upstream.max_temp_file_size != 0\n        && conf->upstream.max_temp_file_size < size)\n    {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n             \"\\\"proxy_max_temp_file_size\\\" must be equal to zero to disable \"\n             \"temporary files usage or must be equal to or greater than \"\n             \"the maximum of the value of \\\"proxy_buffer_size\\\" and \"\n             \"one of the \\\"proxy_buffers\\\"\");\n\n        return NGX_CONF_ERROR;\n    }\n\n\n    ngx_conf_merge_bitmask_value(conf->upstream.ignore_headers,\n                              prev->upstream.ignore_headers,\n                              NGX_CONF_BITMASK_SET);\n\n\n    ngx_conf_merge_bitmask_value(conf->upstream.next_upstream,\n                              prev->upstream.next_upstream,\n                              (NGX_CONF_BITMASK_SET\n                               |NGX_HTTP_UPSTREAM_FT_ERROR\n                               |NGX_HTTP_UPSTREAM_FT_TIMEOUT));\n\n    if (conf->upstream.next_upstream & NGX_HTTP_UPSTREAM_FT_OFF) {\n        conf->upstream.next_upstream = NGX_CONF_BITMASK_SET\n                                       |NGX_HTTP_UPSTREAM_FT_OFF;\n    }\n\n    if (ngx_conf_merge_path_value(cf, &conf->upstream.temp_path,\n                              prev->upstream.temp_path,\n                              &ngx_http_proxy_temp_path)\n        != NGX_OK)\n    {\n        return NGX_CONF_ERROR;\n    }\n\n\n#if (NGX_HTTP_CACHE)\n\n    if (conf->upstream.cache == NGX_CONF_UNSET) {\n        ngx_conf_merge_value(conf->upstream.cache,\n                              prev->upstream.cache, 0);\n\n        conf->upstream.cache_zone = prev->upstream.cache_zone;\n        conf->upstream.cache_value = prev->upstream.cache_value;\n    }\n\n    if (conf->upstream.cache_zone && conf->upstream.cache_zone->data == NULL) {\n        ngx_shm_zone_t  *shm_zone;\n\n        shm_zone = conf->upstream.cache_zone;\n\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"\\\"proxy_cache\\\" zone \\\"%V\\\" is unknown\",\n                           &shm_zone->shm.name);\n\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_conf_merge_uint_value(conf->upstream.cache_min_uses,\n                              prev->upstream.cache_min_uses, 1);\n\n    ngx_conf_merge_off_value(conf->upstream.cache_max_range_offset,\n                              prev->upstream.cache_max_range_offset,\n                              NGX_MAX_OFF_T_VALUE);\n\n    ngx_conf_merge_bitmask_value(conf->upstream.cache_use_stale,\n                              prev->upstream.cache_use_stale,\n                              (NGX_CONF_BITMASK_SET\n                               |NGX_HTTP_UPSTREAM_FT_OFF));\n\n    if (conf->upstream.cache_use_stale & NGX_HTTP_UPSTREAM_FT_OFF) {\n        conf->upstream.cache_use_stale = NGX_CONF_BITMASK_SET\n                                         |NGX_HTTP_UPSTREAM_FT_OFF;\n    }\n\n    if (conf->upstream.cache_use_stale & NGX_HTTP_UPSTREAM_FT_ERROR) {\n        conf->upstream.cache_use_stale |= NGX_HTTP_UPSTREAM_FT_NOLIVE;\n    }\n\n    if (conf->upstream.cache_methods == 0) {\n        conf->upstream.cache_methods = prev->upstream.cache_methods;\n    }\n\n    conf->upstream.cache_methods |= NGX_HTTP_GET|NGX_HTTP_HEAD;\n\n    ngx_conf_merge_ptr_value(conf->upstream.cache_bypass,\n                             prev->upstream.cache_bypass, NULL);\n\n    ngx_conf_merge_ptr_value(conf->upstream.no_cache,\n                             prev->upstream.no_cache, NULL);\n\n    ngx_conf_merge_ptr_value(conf->upstream.cache_valid,\n                             prev->upstream.cache_valid, NULL);\n\n    if (conf->cache_key.value.data == NULL) {\n        conf->cache_key = prev->cache_key;\n    }\n\n    ngx_conf_merge_value(conf->upstream.cache_lock,\n                              prev->upstream.cache_lock, 0);\n\n    ngx_conf_merge_msec_value(conf->upstream.cache_lock_timeout,\n                              prev->upstream.cache_lock_timeout, 5000);\n\n    ngx_conf_merge_msec_value(conf->upstream.cache_lock_age,\n                              prev->upstream.cache_lock_age, 5000);\n\n    ngx_conf_merge_value(conf->upstream.cache_revalidate,\n                              prev->upstream.cache_revalidate, 0);\n\n    ngx_conf_merge_value(conf->upstream.cache_convert_head,\n                              prev->upstream.cache_convert_head, 1);\n\n    ngx_conf_merge_value(conf->upstream.cache_background_update,\n                              prev->upstream.cache_background_update, 0);\n\n#endif\n\n    ngx_conf_merge_value(conf->upstream.pass_request_headers,\n                              prev->upstream.pass_request_headers, 1);\n    ngx_conf_merge_value(conf->upstream.pass_request_body,\n                              prev->upstream.pass_request_body, 1);\n\n    ngx_conf_merge_value(conf->upstream.intercept_errors,\n                              prev->upstream.intercept_errors, 0);\n\n#if (NGX_HTTP_SSL)\n\n    if (ngx_http_proxy_merge_ssl(cf, conf, prev) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_conf_merge_value(conf->upstream.ssl_session_reuse,\n                              prev->upstream.ssl_session_reuse, 1);\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|NGX_SSL_TLSv1_3));\n\n    ngx_conf_merge_str_value(conf->ssl_ciphers, prev->ssl_ciphers,\n                             \"DEFAULT\");\n\n    ngx_conf_merge_ptr_value(conf->upstream.ssl_name,\n                              prev->upstream.ssl_name, NULL);\n    ngx_conf_merge_value(conf->upstream.ssl_server_name,\n                              prev->upstream.ssl_server_name, 0);\n    ngx_conf_merge_value(conf->upstream.ssl_verify,\n                              prev->upstream.ssl_verify, 0);\n    ngx_conf_merge_uint_value(conf->ssl_verify_depth,\n                              prev->ssl_verify_depth, 1);\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\n    ngx_conf_merge_ptr_value(conf->upstream.ssl_certificate,\n                              prev->upstream.ssl_certificate, NULL);\n    ngx_conf_merge_ptr_value(conf->upstream.ssl_certificate_key,\n                              prev->upstream.ssl_certificate_key, NULL);\n    ngx_conf_merge_ptr_value(conf->upstream.ssl_passwords,\n                              prev->upstream.ssl_passwords, NULL);\n\n    ngx_conf_merge_ptr_value(conf->ssl_conf_commands,\n                              prev->ssl_conf_commands, NULL);\n\n    if (conf->ssl && ngx_http_proxy_set_ssl(cf, conf) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n#if (T_NGX_SSL_NTLS)\n    if (conf->upstream.enable_ntls == NULL) {\n        conf->upstream.enable_ntls = prev->upstream.enable_ntls;\n    }\n    ngx_conf_merge_str_value(conf->upstream.enc_certificate,\n                             prev->upstream.enc_certificate, \"\");\n    ngx_conf_merge_str_value(conf->upstream.enc_certificate_key,\n                             prev->upstream.enc_certificate_key, \"\");\n    ngx_conf_merge_str_value(conf->upstream.sign_certificate,\n                             prev->upstream.sign_certificate, \"\");\n    ngx_conf_merge_str_value(conf->upstream.sign_certificate_key,\n                             prev->upstream.sign_certificate_key, \"\");\n    conf->upstream.ssl_ciphers = conf->ssl_ciphers;\n#endif\n#endif\n\n    ngx_conf_merge_ptr_value(conf->method, prev->method, NULL);\n\n    ngx_conf_merge_value(conf->redirect, prev->redirect, 1);\n\n    if (conf->redirect) {\n\n        if (conf->redirects == NULL) {\n            conf->redirects = prev->redirects;\n        }\n\n        if (conf->redirects == NULL && conf->url.data) {\n\n            conf->redirects = ngx_array_create(cf->pool, 1,\n                                             sizeof(ngx_http_proxy_rewrite_t));\n            if (conf->redirects == NULL) {\n                return NGX_CONF_ERROR;\n            }\n\n            pr = ngx_array_push(conf->redirects);\n            if (pr == NULL) {\n                return NGX_CONF_ERROR;\n            }\n\n            ngx_memzero(&pr->pattern.complex,\n                        sizeof(ngx_http_complex_value_t));\n\n            ngx_memzero(&pr->replacement, sizeof(ngx_http_complex_value_t));\n\n            pr->handler = ngx_http_proxy_rewrite_complex_handler;\n\n            if (conf->vars.uri.len) {\n                pr->pattern.complex.value = conf->url;\n                pr->replacement.value = conf->location;\n\n            } else {\n                pr->pattern.complex.value.len = conf->url.len\n                                                + sizeof(\"/\") - 1;\n\n                p = ngx_pnalloc(cf->pool, pr->pattern.complex.value.len);\n                if (p == NULL) {\n                    return NGX_CONF_ERROR;\n                }\n\n                pr->pattern.complex.value.data = p;\n\n                p = ngx_cpymem(p, conf->url.data, conf->url.len);\n                *p = '/';\n\n                ngx_str_set(&pr->replacement.value, \"/\");\n            }\n        }\n    }\n\n    ngx_conf_merge_ptr_value(conf->cookie_domains, prev->cookie_domains, NULL);\n\n    ngx_conf_merge_ptr_value(conf->cookie_paths, prev->cookie_paths, NULL);\n\n    ngx_conf_merge_ptr_value(conf->cookie_flags, prev->cookie_flags, NULL);\n\n    ngx_conf_merge_uint_value(conf->http_version, prev->http_version,\n                              NGX_HTTP_VERSION_10);\n\n    ngx_conf_merge_uint_value(conf->headers_hash_max_size,\n                              prev->headers_hash_max_size, 512);\n\n    ngx_conf_merge_uint_value(conf->headers_hash_bucket_size,\n                              prev->headers_hash_bucket_size, 64);\n\n    conf->headers_hash_bucket_size = ngx_align(conf->headers_hash_bucket_size,\n                                               ngx_cacheline_size);\n\n    hash.max_size = conf->headers_hash_max_size;\n    hash.bucket_size = conf->headers_hash_bucket_size;\n    hash.name = \"proxy_headers_hash\";\n\n    if (ngx_http_upstream_hide_headers_hash(cf, &conf->upstream,\n            &prev->upstream, ngx_http_proxy_hide_headers, &hash)\n        != NGX_OK)\n    {\n        return NGX_CONF_ERROR;\n    }\n\n    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);\n\n    if (clcf->noname\n        && conf->upstream.upstream == NULL && conf->proxy_lengths == NULL)\n    {\n        conf->upstream.upstream = prev->upstream.upstream;\n        conf->location = prev->location;\n        conf->vars = prev->vars;\n\n        conf->proxy_lengths = prev->proxy_lengths;\n        conf->proxy_values = prev->proxy_values;\n\n#if (NGX_HTTP_SSL)\n        conf->ssl = prev->ssl;\n#endif\n    }\n\n    if (clcf->lmt_excpt && clcf->handler == NULL\n        && (conf->upstream.upstream || conf->proxy_lengths))\n    {\n        clcf->handler = ngx_http_proxy_handler;\n    }\n\n    if (conf->body_source.data == NULL) {\n        conf->body_flushes = prev->body_flushes;\n        conf->body_source = prev->body_source;\n        conf->body_lengths = prev->body_lengths;\n        conf->body_values = prev->body_values;\n    }\n\n    if (conf->body_source.data && conf->body_lengths == NULL) {\n\n        ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));\n\n        sc.cf = cf;\n        sc.source = &conf->body_source;\n        sc.flushes = &conf->body_flushes;\n        sc.lengths = &conf->body_lengths;\n        sc.values = &conf->body_values;\n        sc.complete_lengths = 1;\n        sc.complete_values = 1;\n\n        if (ngx_http_script_compile(&sc) != NGX_OK) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    ngx_conf_merge_ptr_value(conf->headers_source, prev->headers_source, NULL);\n\n    if (conf->headers_source == prev->headers_source) {\n        conf->headers = prev->headers;\n#if (NGX_HTTP_CACHE)\n        conf->headers_cache = prev->headers_cache;\n#endif\n    }\n\n    rc = ngx_http_proxy_init_headers(cf, conf, &conf->headers,\n                                     ngx_http_proxy_headers);\n    if (rc != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n#if (NGX_HTTP_CACHE)\n\n    if (conf->upstream.cache) {\n        rc = ngx_http_proxy_init_headers(cf, conf, &conf->headers_cache,\n                                         ngx_http_proxy_cache_headers);\n        if (rc != NGX_OK) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n#endif\n\n    /*\n     * special handling to preserve conf->headers in the \"http\" section\n     * to inherit it to all servers\n     */\n\n    if (prev->headers.hash.buckets == NULL\n        && conf->headers_source == prev->headers_source)\n    {\n        prev->headers = conf->headers;\n#if (NGX_HTTP_CACHE)\n        prev->headers_cache = conf->headers_cache;\n#endif\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_proxy_init_headers(ngx_conf_t *cf, ngx_http_proxy_loc_conf_t *conf,\n    ngx_http_proxy_headers_t *headers, ngx_keyval_t *default_headers)\n{\n    u_char                       *p;\n    size_t                        size;\n    uintptr_t                    *code;\n    ngx_uint_t                    i;\n    ngx_array_t                   headers_names, headers_merged;\n    ngx_keyval_t                 *src, *s, *h;\n    ngx_hash_key_t               *hk;\n    ngx_hash_init_t               hash;\n    ngx_http_script_compile_t     sc;\n    ngx_http_script_copy_code_t  *copy;\n\n    if (headers->hash.buckets) {\n        return NGX_OK;\n    }\n\n    if (ngx_array_init(&headers_names, cf->temp_pool, 4, sizeof(ngx_hash_key_t))\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    if (ngx_array_init(&headers_merged, cf->temp_pool, 4, sizeof(ngx_keyval_t))\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    headers->lengths = ngx_array_create(cf->pool, 64, 1);\n    if (headers->lengths == NULL) {\n        return NGX_ERROR;\n    }\n\n    headers->values = ngx_array_create(cf->pool, 512, 1);\n    if (headers->values == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (conf->headers_source) {\n\n        src = conf->headers_source->elts;\n        for (i = 0; i < conf->headers_source->nelts; i++) {\n\n            s = ngx_array_push(&headers_merged);\n            if (s == NULL) {\n                return NGX_ERROR;\n            }\n\n            *s = src[i];\n        }\n    }\n\n    h = default_headers;\n\n    while (h->key.len) {\n\n        src = headers_merged.elts;\n        for (i = 0; i < headers_merged.nelts; i++) {\n            if (ngx_strcasecmp(h->key.data, src[i].key.data) == 0) {\n                goto next;\n            }\n        }\n\n        s = ngx_array_push(&headers_merged);\n        if (s == NULL) {\n            return NGX_ERROR;\n        }\n\n        *s = *h;\n\n    next:\n\n        h++;\n    }\n\n\n    src = headers_merged.elts;\n    for (i = 0; i < headers_merged.nelts; i++) {\n\n        hk = ngx_array_push(&headers_names);\n        if (hk == NULL) {\n            return NGX_ERROR;\n        }\n\n        hk->key = src[i].key;\n        hk->key_hash = ngx_hash_key_lc(src[i].key.data, src[i].key.len);\n        hk->value = (void *) 1;\n\n        if (src[i].value.len == 0) {\n            continue;\n        }\n\n        copy = ngx_array_push_n(headers->lengths,\n                                sizeof(ngx_http_script_copy_code_t));\n        if (copy == NULL) {\n            return NGX_ERROR;\n        }\n\n        copy->code = (ngx_http_script_code_pt) (void *)\n                                                 ngx_http_script_copy_len_code;\n        copy->len = src[i].key.len;\n\n        size = (sizeof(ngx_http_script_copy_code_t)\n                + src[i].key.len + sizeof(uintptr_t) - 1)\n               & ~(sizeof(uintptr_t) - 1);\n\n        copy = ngx_array_push_n(headers->values, size);\n        if (copy == NULL) {\n            return NGX_ERROR;\n        }\n\n        copy->code = ngx_http_script_copy_code;\n        copy->len = src[i].key.len;\n\n        p = (u_char *) copy + sizeof(ngx_http_script_copy_code_t);\n        ngx_memcpy(p, src[i].key.data, src[i].key.len);\n\n        ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));\n\n        sc.cf = cf;\n        sc.source = &src[i].value;\n        sc.flushes = &headers->flushes;\n        sc.lengths = &headers->lengths;\n        sc.values = &headers->values;\n\n        if (ngx_http_script_compile(&sc) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        code = ngx_array_push_n(headers->lengths, sizeof(uintptr_t));\n        if (code == NULL) {\n            return NGX_ERROR;\n        }\n\n        *code = (uintptr_t) NULL;\n\n        code = ngx_array_push_n(headers->values, sizeof(uintptr_t));\n        if (code == NULL) {\n            return NGX_ERROR;\n        }\n\n        *code = (uintptr_t) NULL;\n    }\n\n    code = ngx_array_push_n(headers->lengths, sizeof(uintptr_t));\n    if (code == NULL) {\n        return NGX_ERROR;\n    }\n\n    *code = (uintptr_t) NULL;\n\n\n    hash.hash = &headers->hash;\n    hash.key = ngx_hash_key_lc;\n    hash.max_size = conf->headers_hash_max_size;\n    hash.bucket_size = conf->headers_hash_bucket_size;\n    hash.name = \"proxy_headers_hash\";\n    hash.pool = cf->pool;\n    hash.temp_pool = NULL;\n\n    return ngx_hash_init(&hash, headers_names.elts, headers_names.nelts);\n}\n\n\nstatic char *\nngx_http_proxy_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_proxy_loc_conf_t *plcf = conf;\n\n    size_t                      add;\n    u_short                     port;\n    ngx_str_t                  *value, *url;\n    ngx_url_t                   u;\n    ngx_uint_t                  n;\n    ngx_http_core_loc_conf_t   *clcf;\n    ngx_http_script_compile_t   sc;\n\n    if (plcf->upstream.upstream || plcf->proxy_lengths) {\n        return \"is duplicate\";\n    }\n\n    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);\n\n    clcf->handler = ngx_http_proxy_handler;\n\n    if (clcf->name.len && clcf->name.data[clcf->name.len - 1] == '/') {\n        clcf->auto_redirect = 1;\n    }\n\n    value = cf->args->elts;\n\n    url = &value[1];\n\n    n = ngx_http_script_variables_count(url);\n\n    if (n) {\n\n        ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));\n\n        sc.cf = cf;\n        sc.source = url;\n        sc.lengths = &plcf->proxy_lengths;\n        sc.values = &plcf->proxy_values;\n        sc.variables = n;\n        sc.complete_lengths = 1;\n        sc.complete_values = 1;\n\n        if (ngx_http_script_compile(&sc) != NGX_OK) {\n            return NGX_CONF_ERROR;\n        }\n\n#if (NGX_HTTP_SSL)\n        plcf->ssl = 1;\n#endif\n\n        return NGX_CONF_OK;\n    }\n\n    if (ngx_strncasecmp(url->data, (u_char *) \"http://\", 7) == 0) {\n        add = 7;\n        port = 80;\n\n    } else if (ngx_strncasecmp(url->data, (u_char *) \"https://\", 8) == 0) {\n\n#if (NGX_HTTP_SSL)\n        plcf->ssl = 1;\n\n        add = 8;\n        port = 443;\n#else\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"https protocol requires SSL support\");\n        return NGX_CONF_ERROR;\n#endif\n\n    } else {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, \"invalid URL prefix\");\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_memzero(&u, sizeof(ngx_url_t));\n\n    u.url.len = url->len - add;\n    u.url.data = url->data + add;\n    u.default_port = port;\n    u.uri_part = 1;\n    u.no_resolve = 1;\n\n    plcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0);\n    if (plcf->upstream.upstream == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    plcf->vars.schema.len = add;\n    plcf->vars.schema.data = url->data;\n    plcf->vars.key_start = plcf->vars.schema;\n\n    ngx_http_proxy_set_vars(&u, &plcf->vars);\n\n    plcf->location = clcf->name;\n\n    if (clcf->named\n#if (NGX_PCRE)\n        || clcf->regex\n#endif\n        || clcf->noname)\n    {\n        if (plcf->vars.uri.len) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"\\\"proxy_pass\\\" cannot have URI part in \"\n                               \"location given by regular expression, \"\n                               \"or inside named location, \"\n                               \"or inside \\\"if\\\" statement, \"\n                               \"or inside \\\"limit_except\\\" block\");\n            return NGX_CONF_ERROR;\n        }\n\n        plcf->location.len = 0;\n    }\n\n    plcf->url = *url;\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_proxy_redirect(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_proxy_loc_conf_t *plcf = conf;\n\n    u_char                            *p;\n    ngx_str_t                         *value;\n    ngx_http_proxy_rewrite_t          *pr;\n    ngx_http_compile_complex_value_t   ccv;\n\n    if (plcf->redirect == 0) {\n        return \"is duplicate\";\n    }\n\n    plcf->redirect = 1;\n\n    value = cf->args->elts;\n\n    if (cf->args->nelts == 2) {\n        if (ngx_strcmp(value[1].data, \"off\") == 0) {\n\n            if (plcf->redirects) {\n                return \"is duplicate\";\n            }\n\n            plcf->redirect = 0;\n            return NGX_CONF_OK;\n        }\n\n        if (ngx_strcmp(value[1].data, \"default\") != 0) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"invalid parameter \\\"%V\\\"\", &value[1]);\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    if (plcf->redirects == NULL) {\n        plcf->redirects = ngx_array_create(cf->pool, 1,\n                                           sizeof(ngx_http_proxy_rewrite_t));\n        if (plcf->redirects == NULL) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    pr = ngx_array_push(plcf->redirects);\n    if (pr == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (cf->args->nelts == 2\n        && ngx_strcmp(value[1].data, \"default\") == 0)\n    {\n        if (plcf->proxy_lengths) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"\\\"proxy_redirect default\\\" cannot be used \"\n                               \"with \\\"proxy_pass\\\" directive with variables\");\n            return NGX_CONF_ERROR;\n        }\n\n        if (plcf->url.data == NULL) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"\\\"proxy_redirect default\\\" should be placed \"\n                               \"after the \\\"proxy_pass\\\" directive\");\n            return NGX_CONF_ERROR;\n        }\n\n        pr->handler = ngx_http_proxy_rewrite_complex_handler;\n\n        ngx_memzero(&pr->pattern.complex, sizeof(ngx_http_complex_value_t));\n\n        ngx_memzero(&pr->replacement, sizeof(ngx_http_complex_value_t));\n\n        if (plcf->vars.uri.len) {\n            pr->pattern.complex.value = plcf->url;\n            pr->replacement.value = plcf->location;\n\n        } else {\n            pr->pattern.complex.value.len = plcf->url.len + sizeof(\"/\") - 1;\n\n            p = ngx_pnalloc(cf->pool, pr->pattern.complex.value.len);\n            if (p == NULL) {\n                return NGX_CONF_ERROR;\n            }\n\n            pr->pattern.complex.value.data = p;\n\n            p = ngx_cpymem(p, plcf->url.data, plcf->url.len);\n            *p = '/';\n\n            ngx_str_set(&pr->replacement.value, \"/\");\n        }\n\n        return NGX_CONF_OK;\n    }\n\n\n    if (value[1].data[0] == '~') {\n        value[1].len--;\n        value[1].data++;\n\n        if (value[1].data[0] == '*') {\n            value[1].len--;\n            value[1].data++;\n\n            if (ngx_http_proxy_rewrite_regex(cf, pr, &value[1], 1) != NGX_OK) {\n                return NGX_CONF_ERROR;\n            }\n\n        } else {\n            if (ngx_http_proxy_rewrite_regex(cf, pr, &value[1], 0) != NGX_OK) {\n                return NGX_CONF_ERROR;\n            }\n        }\n\n    } else {\n\n        ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n\n        ccv.cf = cf;\n        ccv.value = &value[1];\n        ccv.complex_value = &pr->pattern.complex;\n\n        if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {\n            return NGX_CONF_ERROR;\n        }\n\n        pr->handler = ngx_http_proxy_rewrite_complex_handler;\n    }\n\n\n    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n\n    ccv.cf = cf;\n    ccv.value = &value[2];\n    ccv.complex_value = &pr->replacement;\n\n    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_proxy_cookie_domain(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_proxy_loc_conf_t *plcf = conf;\n\n    ngx_str_t                         *value;\n    ngx_http_proxy_rewrite_t          *pr;\n    ngx_http_compile_complex_value_t   ccv;\n\n    if (plcf->cookie_domains == NULL) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    if (cf->args->nelts == 2) {\n\n        if (ngx_strcmp(value[1].data, \"off\") == 0) {\n\n            if (plcf->cookie_domains != NGX_CONF_UNSET_PTR) {\n                return \"is duplicate\";\n            }\n\n            plcf->cookie_domains = NULL;\n            return NGX_CONF_OK;\n        }\n\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid parameter \\\"%V\\\"\", &value[1]);\n        return NGX_CONF_ERROR;\n    }\n\n    if (plcf->cookie_domains == NGX_CONF_UNSET_PTR) {\n        plcf->cookie_domains = ngx_array_create(cf->pool, 1,\n                                     sizeof(ngx_http_proxy_rewrite_t));\n        if (plcf->cookie_domains == NULL) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    pr = ngx_array_push(plcf->cookie_domains);\n    if (pr == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (value[1].data[0] == '~') {\n        value[1].len--;\n        value[1].data++;\n\n        if (ngx_http_proxy_rewrite_regex(cf, pr, &value[1], 1) != NGX_OK) {\n            return NGX_CONF_ERROR;\n        }\n\n    } else {\n\n        if (value[1].data[0] == '.') {\n            value[1].len--;\n            value[1].data++;\n        }\n\n        ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n\n        ccv.cf = cf;\n        ccv.value = &value[1];\n        ccv.complex_value = &pr->pattern.complex;\n\n        if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {\n            return NGX_CONF_ERROR;\n        }\n\n        pr->handler = ngx_http_proxy_rewrite_domain_handler;\n\n        if (value[2].data[0] == '.') {\n            value[2].len--;\n            value[2].data++;\n        }\n    }\n\n    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n\n    ccv.cf = cf;\n    ccv.value = &value[2];\n    ccv.complex_value = &pr->replacement;\n\n    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_proxy_cookie_path(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_proxy_loc_conf_t *plcf = conf;\n\n    ngx_str_t                         *value;\n    ngx_http_proxy_rewrite_t          *pr;\n    ngx_http_compile_complex_value_t   ccv;\n\n    if (plcf->cookie_paths == NULL) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    if (cf->args->nelts == 2) {\n\n        if (ngx_strcmp(value[1].data, \"off\") == 0) {\n\n            if (plcf->cookie_paths != NGX_CONF_UNSET_PTR) {\n                return \"is duplicate\";\n            }\n\n            plcf->cookie_paths = NULL;\n            return NGX_CONF_OK;\n        }\n\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid parameter \\\"%V\\\"\", &value[1]);\n        return NGX_CONF_ERROR;\n    }\n\n    if (plcf->cookie_paths == NGX_CONF_UNSET_PTR) {\n        plcf->cookie_paths = ngx_array_create(cf->pool, 1,\n                                     sizeof(ngx_http_proxy_rewrite_t));\n        if (plcf->cookie_paths == NULL) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    pr = ngx_array_push(plcf->cookie_paths);\n    if (pr == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (value[1].data[0] == '~') {\n        value[1].len--;\n        value[1].data++;\n\n        if (value[1].data[0] == '*') {\n            value[1].len--;\n            value[1].data++;\n\n            if (ngx_http_proxy_rewrite_regex(cf, pr, &value[1], 1) != NGX_OK) {\n                return NGX_CONF_ERROR;\n            }\n\n        } else {\n            if (ngx_http_proxy_rewrite_regex(cf, pr, &value[1], 0) != NGX_OK) {\n                return NGX_CONF_ERROR;\n            }\n        }\n\n    } else {\n\n        ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n\n        ccv.cf = cf;\n        ccv.value = &value[1];\n        ccv.complex_value = &pr->pattern.complex;\n\n        if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {\n            return NGX_CONF_ERROR;\n        }\n\n        pr->handler = ngx_http_proxy_rewrite_complex_handler;\n    }\n\n    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n\n    ccv.cf = cf;\n    ccv.value = &value[2];\n    ccv.complex_value = &pr->replacement;\n\n    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_proxy_cookie_flags(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_proxy_loc_conf_t *plcf = conf;\n\n    ngx_str_t                         *value;\n    ngx_uint_t                         i;\n    ngx_http_complex_value_t          *cv;\n    ngx_http_proxy_cookie_flags_t     *pcf;\n    ngx_http_compile_complex_value_t   ccv;\n#if (NGX_PCRE)\n    ngx_regex_compile_t                rc;\n    u_char                             errstr[NGX_MAX_CONF_ERRSTR];\n#endif\n\n    if (plcf->cookie_flags == NULL) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    if (cf->args->nelts == 2) {\n\n        if (ngx_strcmp(value[1].data, \"off\") == 0) {\n\n            if (plcf->cookie_flags != NGX_CONF_UNSET_PTR) {\n                return \"is duplicate\";\n            }\n\n            plcf->cookie_flags = NULL;\n            return NGX_CONF_OK;\n        }\n\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid parameter \\\"%V\\\"\", &value[1]);\n        return NGX_CONF_ERROR;\n    }\n\n    if (plcf->cookie_flags == NGX_CONF_UNSET_PTR) {\n        plcf->cookie_flags = ngx_array_create(cf->pool, 1,\n                                        sizeof(ngx_http_proxy_cookie_flags_t));\n        if (plcf->cookie_flags == NULL) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    pcf = ngx_array_push(plcf->cookie_flags);\n    if (pcf == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    pcf->regex = 0;\n\n    if (value[1].data[0] == '~') {\n        value[1].len--;\n        value[1].data++;\n\n#if (NGX_PCRE)\n        ngx_memzero(&rc, sizeof(ngx_regex_compile_t));\n\n        rc.pattern = value[1];\n        rc.err.len = NGX_MAX_CONF_ERRSTR;\n        rc.err.data = errstr;\n        rc.options = NGX_REGEX_CASELESS;\n\n        pcf->cookie.regex = ngx_http_regex_compile(cf, &rc);\n        if (pcf->cookie.regex == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        pcf->regex = 1;\n#else\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"using regex \\\"%V\\\" requires PCRE library\",\n                           &value[1]);\n        return NGX_CONF_ERROR;\n#endif\n\n    } else {\n\n        ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n\n        ccv.cf = cf;\n        ccv.value = &value[1];\n        ccv.complex_value = &pcf->cookie.complex;\n\n        if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    if (ngx_array_init(&pcf->flags_values, cf->pool, cf->args->nelts - 2,\n                       sizeof(ngx_http_complex_value_t))\n        != NGX_OK)\n    {\n        return NGX_CONF_ERROR;\n    }\n\n    for (i = 2; i < cf->args->nelts; i++) {\n\n        cv = ngx_array_push(&pcf->flags_values);\n        if (cv == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n\n        ccv.cf = cf;\n        ccv.value = &value[i];\n        ccv.complex_value = cv;\n\n        if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_proxy_rewrite_regex(ngx_conf_t *cf, ngx_http_proxy_rewrite_t *pr,\n    ngx_str_t *regex, ngx_uint_t caseless)\n{\n#if (NGX_PCRE)\n    u_char               errstr[NGX_MAX_CONF_ERRSTR];\n    ngx_regex_compile_t  rc;\n\n    ngx_memzero(&rc, sizeof(ngx_regex_compile_t));\n\n    rc.pattern = *regex;\n    rc.err.len = NGX_MAX_CONF_ERRSTR;\n    rc.err.data = errstr;\n\n    if (caseless) {\n        rc.options = NGX_REGEX_CASELESS;\n    }\n\n    pr->pattern.regex = ngx_http_regex_compile(cf, &rc);\n    if (pr->pattern.regex == NULL) {\n        return NGX_ERROR;\n    }\n\n    pr->handler = ngx_http_proxy_rewrite_regex_handler;\n\n    return NGX_OK;\n\n#else\n\n    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                       \"using regex \\\"%V\\\" requires PCRE library\", regex);\n    return NGX_ERROR;\n\n#endif\n}\n\n\nstatic char *\nngx_http_proxy_store(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_proxy_loc_conf_t *plcf = conf;\n\n    ngx_str_t                  *value;\n    ngx_http_script_compile_t   sc;\n\n    if (plcf->upstream.store != NGX_CONF_UNSET) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    if (ngx_strcmp(value[1].data, \"off\") == 0) {\n        plcf->upstream.store = 0;\n        return NGX_CONF_OK;\n    }\n\n#if (NGX_HTTP_CACHE)\n    if (plcf->upstream.cache > 0) {\n        return \"is incompatible with \\\"proxy_cache\\\"\";\n    }\n#endif\n\n    plcf->upstream.store = 1;\n\n    if (ngx_strcmp(value[1].data, \"on\") == 0) {\n        return NGX_CONF_OK;\n    }\n\n    /* include the terminating '\\0' into script */\n    value[1].len++;\n\n    ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));\n\n    sc.cf = cf;\n    sc.source = &value[1];\n    sc.lengths = &plcf->upstream.store_lengths;\n    sc.values = &plcf->upstream.store_values;\n    sc.variables = ngx_http_script_variables_count(&value[1]);\n    sc.complete_lengths = 1;\n    sc.complete_values = 1;\n\n    if (ngx_http_script_compile(&sc) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\n#if (NGX_HTTP_CACHE)\n\nstatic char *\nngx_http_proxy_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_proxy_loc_conf_t *plcf = conf;\n\n    ngx_str_t                         *value;\n    ngx_http_complex_value_t           cv;\n    ngx_http_compile_complex_value_t   ccv;\n\n    value = cf->args->elts;\n\n    if (plcf->upstream.cache != NGX_CONF_UNSET) {\n        return \"is duplicate\";\n    }\n\n    if (ngx_strcmp(value[1].data, \"off\") == 0) {\n        plcf->upstream.cache = 0;\n        return NGX_CONF_OK;\n    }\n\n    if (plcf->upstream.store > 0) {\n        return \"is incompatible with \\\"proxy_store\\\"\";\n    }\n\n    plcf->upstream.cache = 1;\n\n    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n\n    ccv.cf = cf;\n    ccv.value = &value[1];\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\n        plcf->upstream.cache_value = ngx_palloc(cf->pool,\n                                             sizeof(ngx_http_complex_value_t));\n        if (plcf->upstream.cache_value == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        *plcf->upstream.cache_value = cv;\n\n        return NGX_CONF_OK;\n    }\n\n    plcf->upstream.cache_zone = ngx_shared_memory_add(cf, &value[1], 0,\n                                                      &ngx_http_proxy_module);\n    if (plcf->upstream.cache_zone == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_proxy_cache_key(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_proxy_loc_conf_t *plcf = conf;\n\n    ngx_str_t                         *value;\n    ngx_http_compile_complex_value_t   ccv;\n\n    value = cf->args->elts;\n\n    if (plcf->cache_key.value.data) {\n        return \"is duplicate\";\n    }\n\n    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n\n    ccv.cf = cf;\n    ccv.value = &value[1];\n    ccv.complex_value = &plcf->cache_key;\n\n    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n#endif\n\n\n#if (NGX_HTTP_SSL)\n\nstatic char *\nngx_http_proxy_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_proxy_loc_conf_t *plcf = conf;\n\n    ngx_str_t  *value;\n\n    if (plcf->upstream.ssl_passwords != NGX_CONF_UNSET_PTR) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    plcf->upstream.ssl_passwords = ngx_ssl_read_password_file(cf, &value[1]);\n\n    if (plcf->upstream.ssl_passwords == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n#endif\n\n\nstatic char *\nngx_http_proxy_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                           \"\\\"proxy_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                       \"\\\"proxy_send_lowat\\\" is not supported, ignored\");\n\n    *np = 0;\n\n#endif\n\n    return NGX_CONF_OK;\n}\n\n\n#if (NGX_HTTP_SSL)\n\nstatic char *\nngx_http_proxy_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#else\n    return NGX_CONF_OK;\n#endif\n}\n\n\nstatic ngx_int_t\nngx_http_proxy_merge_ssl(ngx_conf_t *cf, ngx_http_proxy_loc_conf_t *conf,\n    ngx_http_proxy_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->upstream.ssl_certificate == NGX_CONF_UNSET_PTR\n        && conf->upstream.ssl_certificate_key == NGX_CONF_UNSET_PTR\n        && conf->upstream.ssl_passwords == NGX_CONF_UNSET_PTR\n        && conf->upstream.ssl_verify == NGX_CONF_UNSET\n        && conf->ssl_verify_depth == NGX_CONF_UNSET_UINT\n        && conf->ssl_trusted_certificate.data == NULL\n        && conf->ssl_crl.data == NULL\n        && conf->upstream.ssl_session_reuse == NGX_CONF_UNSET\n        && conf->ssl_conf_commands == NGX_CONF_UNSET_PTR)\n    {\n        if (prev->upstream.ssl) {\n            conf->upstream.ssl = prev->upstream.ssl;\n            return NGX_OK;\n        }\n\n        preserve = 1;\n\n    } else {\n        preserve = 0;\n    }\n\n    conf->upstream.ssl = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_t));\n    if (conf->upstream.ssl == NULL) {\n        return NGX_ERROR;\n    }\n\n    conf->upstream.ssl->log = cf->log;\n\n    /*\n     * special handling to preserve conf->upstream.ssl\n     * in the \"http\" section to inherit it to all servers\n     */\n\n    if (preserve) {\n        prev->upstream.ssl = conf->upstream.ssl;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_proxy_set_ssl(ngx_conf_t *cf, ngx_http_proxy_loc_conf_t *plcf)\n{\n    ngx_pool_cleanup_t  *cln;\n\n    if (plcf->upstream.ssl->ctx) {\n        return NGX_OK;\n    }\n\n    if (ngx_ssl_create(plcf->upstream.ssl, plcf->ssl_protocols, NULL)\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    cln = ngx_pool_cleanup_add(cf->pool, 0);\n    if (cln == NULL) {\n        ngx_ssl_cleanup_ctx(plcf->upstream.ssl);\n        return NGX_ERROR;\n    }\n\n    cln->handler = ngx_ssl_cleanup_ctx;\n    cln->data = plcf->upstream.ssl;\n\n    if (ngx_ssl_ciphers(cf, plcf->upstream.ssl, &plcf->ssl_ciphers, 0)\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    if (plcf->upstream.ssl_certificate\n        && plcf->upstream.ssl_certificate->value.len)\n    {\n        if (plcf->upstream.ssl_certificate_key == NULL) {\n            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                          \"no \\\"proxy_ssl_certificate_key\\\" is defined \"\n                          \"for certificate \\\"%V\\\"\",\n                          &plcf->upstream.ssl_certificate->value);\n            return NGX_ERROR;\n        }\n\n        if (plcf->upstream.ssl_certificate->lengths\n            || plcf->upstream.ssl_certificate_key->lengths)\n        {\n            plcf->upstream.ssl_passwords =\n                  ngx_ssl_preserve_passwords(cf, plcf->upstream.ssl_passwords);\n            if (plcf->upstream.ssl_passwords == NULL) {\n                return NGX_ERROR;\n            }\n\n        } else {\n#if (T_NGX_SSL_NTLS)\n            if (ngx_ssl_certificate(cf, plcf->upstream.ssl,\n                                    &plcf->upstream.ssl_certificate->value,\n                                    &plcf->upstream.ssl_certificate_key->value,\n                                    plcf->upstream.ssl_passwords,\n                                    SSL_NORMAL_CERT)\n#else\n            if (ngx_ssl_certificate(cf, plcf->upstream.ssl,\n                                    &plcf->upstream.ssl_certificate->value,\n                                    &plcf->upstream.ssl_certificate_key->value,\n                                    plcf->upstream.ssl_passwords)\n#endif\n                != NGX_OK)\n            {\n                return NGX_ERROR;\n            }\n        }\n    }\n\n#if (T_NGX_SSL_NTLS)\n    plcf->upstream.tls_method = SSL_CTX_get_ssl_method(plcf->upstream.ssl->ctx);\n    if (plcf->upstream.enc_certificate.len) {\n\n        if (plcf->upstream.enc_certificate_key.len == 0) {\n            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                          \"no \\\"proxy_ssl_enc_certificate_key\\\" is defined \"\n                          \"for certificate \\\"%V\\\"\",\n                          &plcf->upstream.enc_certificate);\n            return NGX_ERROR;\n        }\n\n        if (ngx_ssl_certificate(cf, plcf->upstream.ssl,\n                                &plcf->upstream.enc_certificate,\n                                &plcf->upstream.enc_certificate_key,\n                                plcf->upstream.ssl_passwords,\n                                SSL_ENC_CERT)\n            != NGX_OK)\n        {\n            return NGX_ERROR;\n        }\n    }\n\n    if (plcf->upstream.sign_certificate.len) {\n\n        if (plcf->upstream.sign_certificate_key.len == 0) {\n            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                          \"no \\\"proxy_ssl_sign_certificate_key\\\" is defined \"\n                          \"for certificate \\\"%V\\\"\",\n                          &plcf->upstream.sign_certificate);\n            return NGX_ERROR;\n        }\n\n        if (ngx_ssl_certificate(cf, plcf->upstream.ssl,\n                                &plcf->upstream.sign_certificate,\n                                &plcf->upstream.sign_certificate_key,\n                                plcf->upstream.ssl_passwords,\n                                SSL_SIGN_CERT)\n            != NGX_OK)\n        {\n            return NGX_ERROR;\n        }\n    }\n#endif\n\n    if (plcf->upstream.ssl_verify) {\n        if (plcf->ssl_trusted_certificate.len == 0) {\n            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                      \"no proxy_ssl_trusted_certificate for proxy_ssl_verify\");\n            return NGX_ERROR;\n        }\n\n        if (ngx_ssl_trusted_certificate(cf, plcf->upstream.ssl,\n                                        &plcf->ssl_trusted_certificate,\n                                        plcf->ssl_verify_depth)\n            != NGX_OK)\n        {\n            return NGX_ERROR;\n        }\n\n        if (ngx_ssl_crl(cf, plcf->upstream.ssl, &plcf->ssl_crl) != NGX_OK) {\n            return NGX_ERROR;\n        }\n    }\n\n    if (ngx_ssl_client_session_cache(cf, plcf->upstream.ssl,\n                                     plcf->upstream.ssl_session_reuse)\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    if (ngx_ssl_conf_commands(cf, plcf->upstream.ssl, plcf->ssl_conf_commands)\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n#endif\n\n\nstatic void\nngx_http_proxy_set_vars(ngx_url_t *u, ngx_http_proxy_vars_t *v)\n{\n    if (u->family != AF_UNIX) {\n\n        if (u->no_port || u->port == u->default_port) {\n\n            v->host_header = u->host;\n\n            if (u->default_port == 80) {\n                ngx_str_set(&v->port, \"80\");\n\n            } else {\n                ngx_str_set(&v->port, \"443\");\n            }\n\n        } else {\n            v->host_header.len = u->host.len + 1 + u->port_text.len;\n            v->host_header.data = u->host.data;\n            v->port = u->port_text;\n        }\n\n        v->key_start.len += v->host_header.len;\n\n    } else {\n        ngx_str_set(&v->host_header, \"localhost\");\n        ngx_str_null(&v->port);\n        v->key_start.len += sizeof(\"unix:\") - 1 + u->host.len + 1;\n    }\n\n    v->uri = u->uri;\n}\n"
  },
  {
    "path": "src/http/modules/ngx_http_random_index_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\ntypedef struct {\n    ngx_flag_t  enable;\n} ngx_http_random_index_loc_conf_t;\n\n\n#define NGX_HTTP_RANDOM_INDEX_PREALLOCATE  50\n\n\nstatic ngx_int_t ngx_http_random_index_error(ngx_http_request_t *r,\n    ngx_dir_t *dir, ngx_str_t *name);\nstatic ngx_int_t ngx_http_random_index_init(ngx_conf_t *cf);\nstatic void *ngx_http_random_index_create_loc_conf(ngx_conf_t *cf);\nstatic char *ngx_http_random_index_merge_loc_conf(ngx_conf_t *cf,\n    void *parent, void *child);\n\n\nstatic ngx_command_t  ngx_http_random_index_commands[] = {\n\n    { ngx_string(\"random_index\"),\n      NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_random_index_loc_conf_t, enable),\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_random_index_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    ngx_http_random_index_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    ngx_http_random_index_create_loc_conf, /* create location configuration */\n    ngx_http_random_index_merge_loc_conf   /* merge location configuration */\n};\n\n\nngx_module_t  ngx_http_random_index_module = {\n    NGX_MODULE_V1,\n    &ngx_http_random_index_module_ctx,     /* module context */\n    ngx_http_random_index_commands,        /* 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_random_index_handler(ngx_http_request_t *r)\n{\n    u_char                            *last, *filename;\n    size_t                             len, allocated, root;\n    ngx_err_t                          err;\n    ngx_int_t                          rc;\n    ngx_str_t                          path, uri, *name;\n    ngx_dir_t                          dir;\n    ngx_uint_t                         n, level;\n    ngx_array_t                        names;\n    ngx_http_random_index_loc_conf_t  *rlcf;\n\n    if (r->uri.data[r->uri.len - 1] != '/') {\n        return NGX_DECLINED;\n    }\n\n    if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD|NGX_HTTP_POST))) {\n        return NGX_DECLINED;\n    }\n\n    rlcf = ngx_http_get_module_loc_conf(r, ngx_http_random_index_module);\n\n    if (!rlcf->enable) {\n        return NGX_DECLINED;\n    }\n\n#if (NGX_HAVE_D_TYPE)\n    len = 0;\n#else\n    len = NGX_HTTP_RANDOM_INDEX_PREALLOCATE;\n#endif\n\n    last = ngx_http_map_uri_to_path(r, &path, &root, len);\n    if (last == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    allocated = path.len;\n\n    path.len = last - path.data - 1;\n    path.data[path.len] = '\\0';\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http random index: \\\"%s\\\"\", path.data);\n\n    if (ngx_open_dir(&path, &dir) == NGX_ERROR) {\n        err = ngx_errno;\n\n        if (err == NGX_ENOENT\n            || err == NGX_ENOTDIR\n            || err == NGX_ENAMETOOLONG)\n        {\n            level = NGX_LOG_ERR;\n            rc = NGX_HTTP_NOT_FOUND;\n\n        } else if (err == NGX_EACCES) {\n            level = NGX_LOG_ERR;\n            rc = NGX_HTTP_FORBIDDEN;\n\n        } else {\n            level = NGX_LOG_CRIT;\n            rc = NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n\n        ngx_log_error(level, r->connection->log, err,\n                      ngx_open_dir_n \" \\\"%s\\\" failed\", path.data);\n\n        return rc;\n    }\n\n    if (ngx_array_init(&names, r->pool, 32, sizeof(ngx_str_t)) != NGX_OK) {\n        return ngx_http_random_index_error(r, &dir, &path);\n    }\n\n    filename = path.data;\n    filename[path.len] = '/';\n\n    for ( ;; ) {\n        ngx_set_errno(0);\n\n        if (ngx_read_dir(&dir) == NGX_ERROR) {\n            err = ngx_errno;\n\n            if (err != NGX_ENOMOREFILES) {\n                ngx_log_error(NGX_LOG_CRIT, r->connection->log, err,\n                              ngx_read_dir_n \" \\\"%V\\\" failed\", &path);\n                return ngx_http_random_index_error(r, &dir, &path);\n            }\n\n            break;\n        }\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"http random index file: \\\"%s\\\"\", ngx_de_name(&dir));\n\n        if (ngx_de_name(&dir)[0] == '.') {\n            continue;\n        }\n\n        len = ngx_de_namelen(&dir);\n\n        if (dir.type == 0 || ngx_de_is_link(&dir)) {\n\n            /* 1 byte for '/' and 1 byte for terminating '\\0' */\n\n            if (path.len + 1 + len + 1 > allocated) {\n                allocated = path.len + 1 + len + 1\n                                     + NGX_HTTP_RANDOM_INDEX_PREALLOCATE;\n\n                filename = ngx_pnalloc(r->pool, allocated);\n                if (filename == NULL) {\n                    return ngx_http_random_index_error(r, &dir, &path);\n                }\n\n                last = ngx_cpystrn(filename, path.data, path.len + 1);\n                *last++ = '/';\n            }\n\n            ngx_cpystrn(last, ngx_de_name(&dir), len + 1);\n\n            if (ngx_de_info(filename, &dir) == 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_de_info_n \" \\\"%s\\\" failed\", filename);\n                    return ngx_http_random_index_error(r, &dir, &path);\n                }\n\n                if (ngx_de_link_info(filename, &dir) == NGX_FILE_ERROR) {\n                    ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,\n                                  ngx_de_link_info_n \" \\\"%s\\\" failed\",\n                                  filename);\n                    return ngx_http_random_index_error(r, &dir, &path);\n                }\n            }\n        }\n\n        if (!ngx_de_is_file(&dir)) {\n            continue;\n        }\n\n        name = ngx_array_push(&names);\n        if (name == NULL) {\n            return ngx_http_random_index_error(r, &dir, &path);\n        }\n\n        name->len = len;\n\n        name->data = ngx_pnalloc(r->pool, len);\n        if (name->data == NULL) {\n            return ngx_http_random_index_error(r, &dir, &path);\n        }\n\n        ngx_memcpy(name->data, ngx_de_name(&dir), len);\n    }\n\n    if (ngx_close_dir(&dir) == NGX_ERROR) {\n        ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,\n                      ngx_close_dir_n \" \\\"%V\\\" failed\", &path);\n    }\n\n    n = names.nelts;\n\n    if (n == 0) {\n        return NGX_DECLINED;\n    }\n\n    name = names.elts;\n\n    n = (ngx_uint_t) (((uint64_t) ngx_random() * n) / 0x80000000);\n\n    uri.len = r->uri.len + name[n].len;\n\n    uri.data = ngx_pnalloc(r->pool, uri.len);\n    if (uri.data == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    last = ngx_copy(uri.data, r->uri.data, r->uri.len);\n    ngx_memcpy(last, name[n].data, name[n].len);\n\n    return ngx_http_internal_redirect(r, &uri, &r->args);\n}\n\n\nstatic ngx_int_t\nngx_http_random_index_error(ngx_http_request_t *r, ngx_dir_t *dir,\n    ngx_str_t *name)\n{\n    if (ngx_close_dir(dir) == NGX_ERROR) {\n        ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,\n                      ngx_close_dir_n \" \\\"%V\\\" failed\", name);\n    }\n\n    return NGX_HTTP_INTERNAL_SERVER_ERROR;\n}\n\n\nstatic void *\nngx_http_random_index_create_loc_conf(ngx_conf_t *cf)\n{\n    ngx_http_random_index_loc_conf_t  *conf;\n\n    conf = ngx_palloc(cf->pool, sizeof(ngx_http_random_index_loc_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    conf->enable = NGX_CONF_UNSET;\n\n    return conf;\n}\n\n\nstatic char *\nngx_http_random_index_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_http_random_index_loc_conf_t *prev = parent;\n    ngx_http_random_index_loc_conf_t *conf = child;\n\n    ngx_conf_merge_value(conf->enable, prev->enable, 0);\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_random_index_init(ngx_conf_t *cf)\n{\n    ngx_http_handler_pt        *h;\n    ngx_http_core_main_conf_t  *cmcf;\n\n    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);\n\n    h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);\n    if (h == NULL) {\n        return NGX_ERROR;\n    }\n\n    *h = ngx_http_random_index_handler;\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "src/http/modules/ngx_http_range_filter_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\n/*\n * the single part format:\n *\n * \"HTTP/1.0 206 Partial Content\" CRLF\n * ... header ...\n * \"Content-Type: image/jpeg\" CRLF\n * \"Content-Length: SIZE\" CRLF\n * \"Content-Range: bytes START-END/SIZE\" CRLF\n * CRLF\n * ... data ...\n *\n *\n * the multipart format:\n *\n * \"HTTP/1.0 206 Partial Content\" CRLF\n * ... header ...\n * \"Content-Type: multipart/byteranges; boundary=0123456789\" CRLF\n * CRLF\n * CRLF\n * \"--0123456789\" CRLF\n * \"Content-Type: image/jpeg\" CRLF\n * \"Content-Range: bytes START0-END0/SIZE\" CRLF\n * CRLF\n * ... data ...\n * CRLF\n * \"--0123456789\" CRLF\n * \"Content-Type: image/jpeg\" CRLF\n * \"Content-Range: bytes START1-END1/SIZE\" CRLF\n * CRLF\n * ... data ...\n * CRLF\n * \"--0123456789--\" CRLF\n */\n\n\ntypedef struct {\n    off_t        start;\n    off_t        end;\n    ngx_str_t    content_range;\n} ngx_http_range_t;\n\n\ntypedef struct {\n    off_t        offset;\n    ngx_str_t    boundary_header;\n    ngx_array_t  ranges;\n} ngx_http_range_filter_ctx_t;\n\n\nstatic ngx_int_t ngx_http_range_parse(ngx_http_request_t *r,\n    ngx_http_range_filter_ctx_t *ctx, ngx_uint_t ranges);\nstatic ngx_int_t ngx_http_range_singlepart_header(ngx_http_request_t *r,\n    ngx_http_range_filter_ctx_t *ctx);\nstatic ngx_int_t ngx_http_range_multipart_header(ngx_http_request_t *r,\n    ngx_http_range_filter_ctx_t *ctx);\nstatic ngx_int_t ngx_http_range_not_satisfiable(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_range_test_overlapped(ngx_http_request_t *r,\n    ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in);\nstatic ngx_int_t ngx_http_range_singlepart_body(ngx_http_request_t *r,\n    ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in);\nstatic ngx_int_t ngx_http_range_multipart_body(ngx_http_request_t *r,\n    ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in);\n\nstatic ngx_int_t ngx_http_range_header_filter_init(ngx_conf_t *cf);\nstatic ngx_int_t ngx_http_range_body_filter_init(ngx_conf_t *cf);\n\n\nstatic ngx_http_module_t  ngx_http_range_header_filter_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    ngx_http_range_header_filter_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\nngx_module_t  ngx_http_range_header_filter_module = {\n    NGX_MODULE_V1,\n    &ngx_http_range_header_filter_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_http_module_t  ngx_http_range_body_filter_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    ngx_http_range_body_filter_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\nngx_module_t  ngx_http_range_body_filter_module = {\n    NGX_MODULE_V1,\n    &ngx_http_range_body_filter_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_http_output_header_filter_pt  ngx_http_next_header_filter;\nstatic ngx_http_output_body_filter_pt    ngx_http_next_body_filter;\n\n\nstatic ngx_int_t\nngx_http_range_header_filter(ngx_http_request_t *r)\n{\n    time_t                        if_range_time;\n    ngx_str_t                    *if_range, *etag;\n    ngx_uint_t                    ranges;\n    ngx_http_core_loc_conf_t     *clcf;\n    ngx_http_range_filter_ctx_t  *ctx;\n\n    if (r->http_version < NGX_HTTP_VERSION_10\n        || r->headers_out.status != NGX_HTTP_OK\n        || (r != r->main && !r->subrequest_ranges)\n        || r->headers_out.content_length_n == -1\n        || !r->allow_ranges)\n    {\n        return ngx_http_next_header_filter(r);\n    }\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    if (clcf->max_ranges == 0) {\n        return ngx_http_next_header_filter(r);\n    }\n\n    if (r->headers_in.range == NULL\n        || r->headers_in.range->value.len < 7\n        || ngx_strncasecmp(r->headers_in.range->value.data,\n                           (u_char *) \"bytes=\", 6)\n           != 0)\n    {\n        goto next_filter;\n    }\n\n    if (r->headers_in.if_range) {\n\n        if_range = &r->headers_in.if_range->value;\n\n        if (if_range->len >= 2 && if_range->data[if_range->len - 1] == '\"') {\n\n            if (r->headers_out.etag == NULL) {\n                goto next_filter;\n            }\n\n            etag = &r->headers_out.etag->value;\n\n            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"http ir:%V etag:%V\", if_range, etag);\n\n            if (if_range->len != etag->len\n                || ngx_strncmp(if_range->data, etag->data, etag->len) != 0)\n            {\n                goto next_filter;\n            }\n\n            goto parse;\n        }\n\n        if (r->headers_out.last_modified_time == (time_t) -1) {\n            goto next_filter;\n        }\n\n        if_range_time = ngx_parse_http_time(if_range->data, if_range->len);\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"http ir:%T lm:%T\",\n                       if_range_time, r->headers_out.last_modified_time);\n\n        if (if_range_time != r->headers_out.last_modified_time) {\n            goto next_filter;\n        }\n    }\n\nparse:\n\n    ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_range_filter_ctx_t));\n    if (ctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    ctx->offset = r->headers_out.content_offset;\n\n    ranges = r->single_range ? 1 : clcf->max_ranges;\n\n    switch (ngx_http_range_parse(r, ctx, ranges)) {\n\n    case NGX_OK:\n        ngx_http_set_ctx(r, ctx, ngx_http_range_body_filter_module);\n\n        r->headers_out.status = NGX_HTTP_PARTIAL_CONTENT;\n        r->headers_out.status_line.len = 0;\n\n        if (ctx->ranges.nelts == 1) {\n            return ngx_http_range_singlepart_header(r, ctx);\n        }\n\n        return ngx_http_range_multipart_header(r, ctx);\n\n    case NGX_HTTP_RANGE_NOT_SATISFIABLE:\n        return ngx_http_range_not_satisfiable(r);\n\n    case NGX_ERROR:\n        return NGX_ERROR;\n\n    default: /* NGX_DECLINED */\n        break;\n    }\n\nnext_filter:\n\n    r->headers_out.accept_ranges = ngx_list_push(&r->headers_out.headers);\n    if (r->headers_out.accept_ranges == NULL) {\n        return NGX_ERROR;\n    }\n\n    r->headers_out.accept_ranges->hash = 1;\n    r->headers_out.accept_ranges->next = NULL;\n    ngx_str_set(&r->headers_out.accept_ranges->key, \"Accept-Ranges\");\n    ngx_str_set(&r->headers_out.accept_ranges->value, \"bytes\");\n\n    return ngx_http_next_header_filter(r);\n}\n\n\nstatic ngx_int_t\nngx_http_range_parse(ngx_http_request_t *r, ngx_http_range_filter_ctx_t *ctx,\n    ngx_uint_t ranges)\n{\n    u_char                       *p;\n    off_t                         start, end, size, content_length, cutoff,\n                                  cutlim;\n    ngx_uint_t                    suffix;\n    ngx_http_range_t             *range;\n    ngx_http_range_filter_ctx_t  *mctx;\n\n    if (r != r->main) {\n        mctx = ngx_http_get_module_ctx(r->main,\n                                       ngx_http_range_body_filter_module);\n        if (mctx) {\n            ctx->ranges = mctx->ranges;\n            return NGX_OK;\n        }\n    }\n\n    if (ngx_array_init(&ctx->ranges, r->pool, 1, sizeof(ngx_http_range_t))\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    p = r->headers_in.range->value.data + 6;\n    size = 0;\n    content_length = r->headers_out.content_length_n;\n\n    cutoff = NGX_MAX_OFF_T_VALUE / 10;\n    cutlim = NGX_MAX_OFF_T_VALUE % 10;\n\n    for ( ;; ) {\n        start = 0;\n        end = 0;\n        suffix = 0;\n\n        while (*p == ' ') { p++; }\n\n        if (*p != '-') {\n            if (*p < '0' || *p > '9') {\n                return NGX_HTTP_RANGE_NOT_SATISFIABLE;\n            }\n\n            while (*p >= '0' && *p <= '9') {\n                if (start >= cutoff && (start > cutoff || *p - '0' > cutlim)) {\n                    return NGX_HTTP_RANGE_NOT_SATISFIABLE;\n                }\n\n                start = start * 10 + (*p++ - '0');\n            }\n\n            while (*p == ' ') { p++; }\n\n            if (*p++ != '-') {\n                return NGX_HTTP_RANGE_NOT_SATISFIABLE;\n            }\n\n            while (*p == ' ') { p++; }\n\n            if (*p == ',' || *p == '\\0') {\n                end = content_length;\n                goto found;\n            }\n\n        } else {\n            suffix = 1;\n            p++;\n        }\n\n        if (*p < '0' || *p > '9') {\n            return NGX_HTTP_RANGE_NOT_SATISFIABLE;\n        }\n\n        while (*p >= '0' && *p <= '9') {\n            if (end >= cutoff && (end > cutoff || *p - '0' > cutlim)) {\n                return NGX_HTTP_RANGE_NOT_SATISFIABLE;\n            }\n\n            end = end * 10 + (*p++ - '0');\n        }\n\n        while (*p == ' ') { p++; }\n\n        if (*p != ',' && *p != '\\0') {\n            return NGX_HTTP_RANGE_NOT_SATISFIABLE;\n        }\n\n        if (suffix) {\n            start = (end < content_length) ? content_length - end : 0;\n            end = content_length - 1;\n        }\n\n        if (end >= content_length) {\n            end = content_length;\n\n        } else {\n            end++;\n        }\n\n    found:\n\n        if (start < end) {\n            range = ngx_array_push(&ctx->ranges);\n            if (range == NULL) {\n                return NGX_ERROR;\n            }\n\n            range->start = start;\n            range->end = end;\n\n            if (size > NGX_MAX_OFF_T_VALUE - (end - start)) {\n                return NGX_HTTP_RANGE_NOT_SATISFIABLE;\n            }\n\n            size += end - start;\n\n            if (ranges-- == 0) {\n                return NGX_DECLINED;\n            }\n\n        } else if (start == 0) {\n            return NGX_DECLINED;\n        }\n\n        if (*p++ != ',') {\n            break;\n        }\n    }\n\n    if (ctx->ranges.nelts == 0) {\n        return NGX_HTTP_RANGE_NOT_SATISFIABLE;\n    }\n\n    if (size > content_length) {\n        return NGX_DECLINED;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_range_singlepart_header(ngx_http_request_t *r,\n    ngx_http_range_filter_ctx_t *ctx)\n{\n    ngx_table_elt_t   *content_range;\n    ngx_http_range_t  *range;\n\n    if (r != r->main) {\n        return ngx_http_next_header_filter(r);\n    }\n\n    content_range = ngx_list_push(&r->headers_out.headers);\n    if (content_range == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (r->headers_out.content_range) {\n        r->headers_out.content_range->hash = 0;\n    }\n\n    r->headers_out.content_range = content_range;\n\n    content_range->hash = 1;\n    content_range->next = NULL;\n    ngx_str_set(&content_range->key, \"Content-Range\");\n\n    content_range->value.data = ngx_pnalloc(r->pool,\n                                    sizeof(\"bytes -/\") - 1 + 3 * NGX_OFF_T_LEN);\n    if (content_range->value.data == NULL) {\n        content_range->hash = 0;\n        r->headers_out.content_range = NULL;\n        return NGX_ERROR;\n    }\n\n    /* \"Content-Range: bytes SSSS-EEEE/TTTT\" header */\n\n    range = ctx->ranges.elts;\n\n    content_range->value.len = ngx_sprintf(content_range->value.data,\n                                           \"bytes %O-%O/%O\",\n                                           range->start, range->end - 1,\n                                           r->headers_out.content_length_n)\n                               - content_range->value.data;\n\n    r->headers_out.content_length_n = range->end - range->start;\n    r->headers_out.content_offset = range->start;\n\n    if (r->headers_out.content_length) {\n        r->headers_out.content_length->hash = 0;\n        r->headers_out.content_length = NULL;\n    }\n\n    return ngx_http_next_header_filter(r);\n}\n\n\nstatic ngx_int_t\nngx_http_range_multipart_header(ngx_http_request_t *r,\n    ngx_http_range_filter_ctx_t *ctx)\n{\n    off_t               len;\n    size_t              size;\n    ngx_uint_t          i;\n    ngx_http_range_t   *range;\n    ngx_atomic_uint_t   boundary;\n\n    size = sizeof(CRLF \"--\") - 1 + NGX_ATOMIC_T_LEN\n           + sizeof(CRLF \"Content-Type: \") - 1\n           + r->headers_out.content_type.len\n           + sizeof(CRLF \"Content-Range: bytes \") - 1;\n\n    if (r->headers_out.content_type_len == r->headers_out.content_type.len\n        && r->headers_out.charset.len)\n    {\n        size += sizeof(\"; charset=\") - 1 + r->headers_out.charset.len;\n    }\n\n    ctx->boundary_header.data = ngx_pnalloc(r->pool, size);\n    if (ctx->boundary_header.data == NULL) {\n        return NGX_ERROR;\n    }\n\n    boundary = ngx_next_temp_number(0);\n\n    /*\n     * The boundary header of the range:\n     * CRLF\n     * \"--0123456789\" CRLF\n     * \"Content-Type: image/jpeg\" CRLF\n     * \"Content-Range: bytes \"\n     */\n\n    if (r->headers_out.content_type_len == r->headers_out.content_type.len\n        && r->headers_out.charset.len)\n    {\n        ctx->boundary_header.len = ngx_sprintf(ctx->boundary_header.data,\n                                           CRLF \"--%0muA\" CRLF\n                                           \"Content-Type: %V; charset=%V\" CRLF\n                                           \"Content-Range: bytes \",\n                                           boundary,\n                                           &r->headers_out.content_type,\n                                           &r->headers_out.charset)\n                                   - ctx->boundary_header.data;\n\n    } else if (r->headers_out.content_type.len) {\n        ctx->boundary_header.len = ngx_sprintf(ctx->boundary_header.data,\n                                           CRLF \"--%0muA\" CRLF\n                                           \"Content-Type: %V\" CRLF\n                                           \"Content-Range: bytes \",\n                                           boundary,\n                                           &r->headers_out.content_type)\n                                   - ctx->boundary_header.data;\n\n    } else {\n        ctx->boundary_header.len = ngx_sprintf(ctx->boundary_header.data,\n                                           CRLF \"--%0muA\" CRLF\n                                           \"Content-Range: bytes \",\n                                           boundary)\n                                   - ctx->boundary_header.data;\n    }\n\n    r->headers_out.content_type.data =\n        ngx_pnalloc(r->pool,\n                    sizeof(\"Content-Type: multipart/byteranges; boundary=\") - 1\n                    + NGX_ATOMIC_T_LEN);\n\n    if (r->headers_out.content_type.data == NULL) {\n        return NGX_ERROR;\n    }\n\n    r->headers_out.content_type_lowcase = NULL;\n\n    /* \"Content-Type: multipart/byteranges; boundary=0123456789\" */\n\n    r->headers_out.content_type.len =\n                           ngx_sprintf(r->headers_out.content_type.data,\n                                       \"multipart/byteranges; boundary=%0muA\",\n                                       boundary)\n                           - r->headers_out.content_type.data;\n\n    r->headers_out.content_type_len = r->headers_out.content_type.len;\n\n    r->headers_out.charset.len = 0;\n\n    /* the size of the last boundary CRLF \"--0123456789--\" CRLF */\n\n    len = sizeof(CRLF \"--\") - 1 + NGX_ATOMIC_T_LEN + sizeof(\"--\" CRLF) - 1;\n\n    range = ctx->ranges.elts;\n    for (i = 0; i < ctx->ranges.nelts; i++) {\n\n        /* the size of the range: \"SSSS-EEEE/TTTT\" CRLF CRLF */\n\n        range[i].content_range.data =\n                               ngx_pnalloc(r->pool, 3 * NGX_OFF_T_LEN + 2 + 4);\n\n        if (range[i].content_range.data == NULL) {\n            return NGX_ERROR;\n        }\n\n        range[i].content_range.len = ngx_sprintf(range[i].content_range.data,\n                                               \"%O-%O/%O\" CRLF CRLF,\n                                               range[i].start, range[i].end - 1,\n                                               r->headers_out.content_length_n)\n                                     - range[i].content_range.data;\n\n        len += ctx->boundary_header.len + range[i].content_range.len\n                                             + (range[i].end - range[i].start);\n    }\n\n    r->headers_out.content_length_n = len;\n\n    if (r->headers_out.content_length) {\n        r->headers_out.content_length->hash = 0;\n        r->headers_out.content_length = NULL;\n    }\n\n    if (r->headers_out.content_range) {\n        r->headers_out.content_range->hash = 0;\n        r->headers_out.content_range = NULL;\n    }\n\n    return ngx_http_next_header_filter(r);\n}\n\n\nstatic ngx_int_t\nngx_http_range_not_satisfiable(ngx_http_request_t *r)\n{\n    ngx_table_elt_t  *content_range;\n\n    r->headers_out.status = NGX_HTTP_RANGE_NOT_SATISFIABLE;\n\n    content_range = ngx_list_push(&r->headers_out.headers);\n    if (content_range == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (r->headers_out.content_range) {\n        r->headers_out.content_range->hash = 0;\n    }\n\n    r->headers_out.content_range = content_range;\n\n    content_range->hash = 1;\n    content_range->next = NULL;\n    ngx_str_set(&content_range->key, \"Content-Range\");\n\n    content_range->value.data = ngx_pnalloc(r->pool,\n                                       sizeof(\"bytes */\") - 1 + NGX_OFF_T_LEN);\n    if (content_range->value.data == NULL) {\n        content_range->hash = 0;\n        r->headers_out.content_range = NULL;\n        return NGX_ERROR;\n    }\n\n    content_range->value.len = ngx_sprintf(content_range->value.data,\n                                           \"bytes */%O\",\n                                           r->headers_out.content_length_n)\n                               - content_range->value.data;\n\n    ngx_http_clear_content_length(r);\n\n    return NGX_HTTP_RANGE_NOT_SATISFIABLE;\n}\n\n\nstatic ngx_int_t\nngx_http_range_body_filter(ngx_http_request_t *r, ngx_chain_t *in)\n{\n    ngx_http_range_filter_ctx_t  *ctx;\n\n    if (in == NULL) {\n        return ngx_http_next_body_filter(r, in);\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_range_body_filter_module);\n\n    if (ctx == NULL) {\n        return ngx_http_next_body_filter(r, in);\n    }\n\n    if (ctx->ranges.nelts == 1) {\n        return ngx_http_range_singlepart_body(r, ctx, in);\n    }\n\n    /*\n     * multipart ranges are supported only if whole body is in a single buffer\n     */\n\n    if (ngx_buf_special(in->buf)) {\n        return ngx_http_next_body_filter(r, in);\n    }\n\n    if (ngx_http_range_test_overlapped(r, ctx, in) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    return ngx_http_range_multipart_body(r, ctx, in);\n}\n\n\nstatic ngx_int_t\nngx_http_range_test_overlapped(ngx_http_request_t *r,\n    ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in)\n{\n    off_t              start, last;\n    ngx_buf_t         *buf;\n    ngx_uint_t         i;\n    ngx_http_range_t  *range;\n\n    if (ctx->offset) {\n        goto overlapped;\n    }\n\n    buf = in->buf;\n\n    if (!buf->last_buf) {\n        start = ctx->offset;\n        last = ctx->offset + ngx_buf_size(buf);\n\n        range = ctx->ranges.elts;\n        for (i = 0; i < ctx->ranges.nelts; i++) {\n            if (start > range[i].start || last < range[i].end) {\n                goto overlapped;\n            }\n        }\n    }\n\n    ctx->offset = ngx_buf_size(buf);\n\n    return NGX_OK;\n\noverlapped:\n\n    ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,\n                  \"range in overlapped buffers\");\n\n    return NGX_ERROR;\n}\n\n\nstatic ngx_int_t\nngx_http_range_singlepart_body(ngx_http_request_t *r,\n    ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in)\n{\n    off_t              start, last;\n    ngx_int_t          rc;\n    ngx_buf_t         *buf;\n    ngx_chain_t       *out, *cl, *tl, **ll;\n    ngx_http_range_t  *range;\n\n    out = NULL;\n    ll = &out;\n    range = ctx->ranges.elts;\n\n    for (cl = in; cl; cl = cl->next) {\n\n        buf = cl->buf;\n\n        start = ctx->offset;\n        last = ctx->offset + ngx_buf_size(buf);\n\n        ctx->offset = last;\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"http range body buf: %O-%O\", start, last);\n\n        if (ngx_buf_special(buf)) {\n\n            if (range->end <= start) {\n                continue;\n            }\n\n            tl = ngx_alloc_chain_link(r->pool);\n            if (tl == NULL) {\n                return NGX_ERROR;\n            }\n\n            tl->buf = buf;\n            tl->next = NULL;\n\n            *ll = tl;\n            ll = &tl->next;\n\n            continue;\n        }\n\n        if (range->end <= start || range->start >= last) {\n\n            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"http range body skip\");\n\n            if (buf->in_file) {\n                buf->file_pos = buf->file_last;\n            }\n\n            buf->pos = buf->last;\n            buf->sync = 1;\n\n            continue;\n        }\n\n        if (range->start > start) {\n\n            if (buf->in_file) {\n                buf->file_pos += range->start - start;\n            }\n\n            if (ngx_buf_in_memory(buf)) {\n                buf->pos += (size_t) (range->start - start);\n            }\n        }\n\n        if (range->end <= last) {\n\n            if (buf->in_file) {\n                buf->file_last -= last - range->end;\n            }\n\n            if (ngx_buf_in_memory(buf)) {\n                buf->last -= (size_t) (last - range->end);\n            }\n\n            buf->last_buf = (r == r->main) ? 1 : 0;\n            buf->last_in_chain = 1;\n\n            tl = ngx_alloc_chain_link(r->pool);\n            if (tl == NULL) {\n                return NGX_ERROR;\n            }\n\n            tl->buf = buf;\n            tl->next = NULL;\n\n            *ll = tl;\n            ll = &tl->next;\n\n            continue;\n        }\n\n        tl = ngx_alloc_chain_link(r->pool);\n        if (tl == NULL) {\n            return NGX_ERROR;\n        }\n\n        tl->buf = buf;\n        tl->next = NULL;\n\n        *ll = tl;\n        ll = &tl->next;\n    }\n\n    rc = ngx_http_next_body_filter(r, out);\n\n    while (out) {\n        cl = out;\n        out = out->next;\n        ngx_free_chain(r->pool, cl);\n    }\n\n    return rc;\n}\n\n\nstatic ngx_int_t\nngx_http_range_multipart_body(ngx_http_request_t *r,\n    ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in)\n{\n    ngx_buf_t         *b, *buf;\n    ngx_uint_t         i;\n    ngx_chain_t       *out, *hcl, *rcl, *dcl, **ll;\n    ngx_http_range_t  *range;\n\n    ll = &out;\n    buf = in->buf;\n    range = ctx->ranges.elts;\n\n    for (i = 0; i < ctx->ranges.nelts; i++) {\n\n        /*\n         * The boundary header of the range:\n         * CRLF\n         * \"--0123456789\" CRLF\n         * \"Content-Type: image/jpeg\" CRLF\n         * \"Content-Range: bytes \"\n         */\n\n        b = ngx_calloc_buf(r->pool);\n        if (b == NULL) {\n            return NGX_ERROR;\n        }\n\n        b->memory = 1;\n        b->pos = ctx->boundary_header.data;\n        b->last = ctx->boundary_header.data + ctx->boundary_header.len;\n\n        hcl = ngx_alloc_chain_link(r->pool);\n        if (hcl == NULL) {\n            return NGX_ERROR;\n        }\n\n        hcl->buf = b;\n\n\n        /* \"SSSS-EEEE/TTTT\" CRLF CRLF */\n\n        b = ngx_calloc_buf(r->pool);\n        if (b == NULL) {\n            return NGX_ERROR;\n        }\n\n        b->temporary = 1;\n        b->pos = range[i].content_range.data;\n        b->last = range[i].content_range.data + range[i].content_range.len;\n\n        rcl = ngx_alloc_chain_link(r->pool);\n        if (rcl == NULL) {\n            return NGX_ERROR;\n        }\n\n        rcl->buf = b;\n\n\n        /* the range data */\n\n        b = ngx_calloc_buf(r->pool);\n        if (b == NULL) {\n            return NGX_ERROR;\n        }\n\n        b->in_file = buf->in_file;\n        b->temporary = buf->temporary;\n        b->memory = buf->memory;\n        b->mmap = buf->mmap;\n        b->file = buf->file;\n\n        if (buf->in_file) {\n            b->file_pos = buf->file_pos + range[i].start;\n            b->file_last = buf->file_pos + range[i].end;\n        }\n\n        if (ngx_buf_in_memory(buf)) {\n            b->pos = buf->pos + (size_t) range[i].start;\n            b->last = buf->pos + (size_t) range[i].end;\n        }\n\n        dcl = ngx_alloc_chain_link(r->pool);\n        if (dcl == NULL) {\n            return NGX_ERROR;\n        }\n\n        dcl->buf = b;\n\n        *ll = hcl;\n        hcl->next = rcl;\n        rcl->next = dcl;\n        ll = &dcl->next;\n    }\n\n    /* the last boundary CRLF \"--0123456789--\" CRLF  */\n\n    b = ngx_calloc_buf(r->pool);\n    if (b == NULL) {\n        return NGX_ERROR;\n    }\n\n    b->temporary = 1;\n    b->last_buf = 1;\n\n    b->pos = ngx_pnalloc(r->pool, sizeof(CRLF \"--\") - 1 + NGX_ATOMIC_T_LEN\n                                  + sizeof(\"--\" CRLF) - 1);\n    if (b->pos == NULL) {\n        return NGX_ERROR;\n    }\n\n    b->last = ngx_cpymem(b->pos, ctx->boundary_header.data,\n                         sizeof(CRLF \"--\") - 1 + NGX_ATOMIC_T_LEN);\n    *b->last++ = '-'; *b->last++ = '-';\n    *b->last++ = CR; *b->last++ = LF;\n\n    hcl = ngx_alloc_chain_link(r->pool);\n    if (hcl == NULL) {\n        return NGX_ERROR;\n    }\n\n    hcl->buf = b;\n    hcl->next = NULL;\n\n    *ll = hcl;\n\n    return ngx_http_next_body_filter(r, out);\n}\n\n\nstatic ngx_int_t\nngx_http_range_header_filter_init(ngx_conf_t *cf)\n{\n    ngx_http_next_header_filter = ngx_http_top_header_filter;\n    ngx_http_top_header_filter = ngx_http_range_header_filter;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_range_body_filter_init(ngx_conf_t *cf)\n{\n    ngx_http_next_body_filter = ngx_http_top_body_filter;\n    ngx_http_top_body_filter = ngx_http_range_body_filter;\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "src/http/modules/ngx_http_realip_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\n#define NGX_HTTP_REALIP_XREALIP  0\n#define NGX_HTTP_REALIP_XFWD     1\n#define NGX_HTTP_REALIP_HEADER   2\n#define NGX_HTTP_REALIP_PROXY    3\n\n\ntypedef struct {\n    ngx_array_t       *from;     /* array of ngx_cidr_t */\n    ngx_uint_t         type;\n    ngx_uint_t         hash;\n    ngx_str_t          header;\n    ngx_flag_t         recursive;\n} ngx_http_realip_loc_conf_t;\n\n\ntypedef struct {\n    ngx_connection_t  *connection;\n    struct sockaddr   *sockaddr;\n    socklen_t          socklen;\n    ngx_str_t          addr_text;\n} ngx_http_realip_ctx_t;\n\n\nstatic ngx_int_t ngx_http_realip_handler(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_realip_set_addr(ngx_http_request_t *r,\n    ngx_addr_t *addr);\nstatic void ngx_http_realip_cleanup(void *data);\nstatic char *ngx_http_realip_from(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_http_realip(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\nstatic void *ngx_http_realip_create_loc_conf(ngx_conf_t *cf);\nstatic char *ngx_http_realip_merge_loc_conf(ngx_conf_t *cf,\n    void *parent, void *child);\nstatic ngx_int_t ngx_http_realip_add_variables(ngx_conf_t *cf);\nstatic ngx_int_t ngx_http_realip_init(ngx_conf_t *cf);\nstatic ngx_http_realip_ctx_t *ngx_http_realip_get_module_ctx(\n    ngx_http_request_t *r);\n\n\nstatic ngx_int_t ngx_http_realip_remote_addr_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_realip_remote_port_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\n\n\nstatic ngx_command_t  ngx_http_realip_commands[] = {\n\n    { ngx_string(\"set_real_ip_from\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_http_realip_from,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"real_ip_header\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_http_realip,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"real_ip_recursive\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_realip_loc_conf_t, recursive),\n      NULL },\n\n      ngx_null_command\n};\n\n\n\nstatic ngx_http_module_t  ngx_http_realip_module_ctx = {\n    ngx_http_realip_add_variables,         /* preconfiguration */\n    ngx_http_realip_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    ngx_http_realip_create_loc_conf,       /* create location configuration */\n    ngx_http_realip_merge_loc_conf         /* merge location configuration */\n};\n\n\nngx_module_t  ngx_http_realip_module = {\n    NGX_MODULE_V1,\n    &ngx_http_realip_module_ctx,           /* module context */\n    ngx_http_realip_commands,              /* 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_http_variable_t  ngx_http_realip_vars[] = {\n\n    { ngx_string(\"realip_remote_addr\"), NULL,\n      ngx_http_realip_remote_addr_variable, 0, 0, 0 },\n\n    { ngx_string(\"realip_remote_port\"), NULL,\n      ngx_http_realip_remote_port_variable, 0, 0, 0 },\n\n      ngx_http_null_variable\n};\n\n\nstatic ngx_int_t\nngx_http_realip_handler(ngx_http_request_t *r)\n{\n    u_char                      *p;\n    size_t                       len;\n    ngx_str_t                   *value;\n    ngx_uint_t                   i, hash;\n    ngx_addr_t                   addr;\n    ngx_list_part_t             *part;\n    ngx_table_elt_t             *header, *xfwd;\n    ngx_connection_t            *c;\n    ngx_http_realip_ctx_t       *ctx;\n    ngx_http_realip_loc_conf_t  *rlcf;\n\n    rlcf = ngx_http_get_module_loc_conf(r, ngx_http_realip_module);\n\n    if (rlcf->from == NULL) {\n        return NGX_DECLINED;\n    }\n\n    ctx = ngx_http_realip_get_module_ctx(r);\n\n    if (ctx) {\n        return NGX_DECLINED;\n    }\n\n    switch (rlcf->type) {\n\n    case NGX_HTTP_REALIP_XREALIP:\n\n        if (r->headers_in.x_real_ip == NULL) {\n            return NGX_DECLINED;\n        }\n\n        value = &r->headers_in.x_real_ip->value;\n        xfwd = NULL;\n\n        break;\n\n    case NGX_HTTP_REALIP_XFWD:\n\n        xfwd = r->headers_in.x_forwarded_for;\n\n        if (xfwd == NULL) {\n            return NGX_DECLINED;\n        }\n\n        value = NULL;\n\n        break;\n\n    case NGX_HTTP_REALIP_PROXY:\n\n        if (r->connection->proxy_protocol == NULL) {\n            return NGX_DECLINED;\n        }\n\n        value = &r->connection->proxy_protocol->src_addr;\n        xfwd = NULL;\n\n        break;\n\n    default: /* NGX_HTTP_REALIP_HEADER */\n\n        part = &r->headers_in.headers.part;\n        header = part->elts;\n\n        hash = rlcf->hash;\n        len = rlcf->header.len;\n        p = rlcf->header.data;\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 (hash == header[i].hash\n                && len == header[i].key.len\n                && ngx_strncmp(p, header[i].lowcase_key, len) == 0)\n            {\n                value = &header[i].value;\n                xfwd = NULL;\n\n                goto found;\n            }\n        }\n\n        return NGX_DECLINED;\n    }\n\nfound:\n\n    c = r->connection;\n\n    addr.sockaddr = c->sockaddr;\n    addr.socklen = c->socklen;\n    /* addr.name = c->addr_text; */\n\n    if (ngx_http_get_forwarded_addr(r, &addr, xfwd, value, rlcf->from,\n                                    rlcf->recursive)\n        != NGX_DECLINED)\n    {\n        if (rlcf->type == NGX_HTTP_REALIP_PROXY) {\n            ngx_inet_set_port(addr.sockaddr, c->proxy_protocol->src_port);\n        }\n\n        return ngx_http_realip_set_addr(r, &addr);\n    }\n\n    return NGX_DECLINED;\n}\n\n\nstatic ngx_int_t\nngx_http_realip_set_addr(ngx_http_request_t *r, ngx_addr_t *addr)\n{\n    size_t                  len;\n    u_char                 *p;\n    u_char                  text[NGX_SOCKADDR_STRLEN];\n    ngx_connection_t       *c;\n    ngx_pool_cleanup_t     *cln;\n    ngx_http_realip_ctx_t  *ctx;\n\n    cln = ngx_pool_cleanup_add(r->pool, sizeof(ngx_http_realip_ctx_t));\n    if (cln == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    ctx = cln->data;\n\n    c = r->connection;\n\n    len = ngx_sock_ntop(addr->sockaddr, addr->socklen, text,\n                        NGX_SOCKADDR_STRLEN, 0);\n    if (len == 0) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    p = ngx_pnalloc(c->pool, len);\n    if (p == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    ngx_memcpy(p, text, len);\n\n    cln->handler = ngx_http_realip_cleanup;\n    ngx_http_set_ctx(r, ctx, ngx_http_realip_module);\n\n    ctx->connection = c;\n    ctx->sockaddr = c->sockaddr;\n    ctx->socklen = c->socklen;\n    ctx->addr_text = c->addr_text;\n\n    c->sockaddr = addr->sockaddr;\n    c->socklen = addr->socklen;\n    c->addr_text.len = len;\n    c->addr_text.data = p;\n\n    return NGX_DECLINED;\n}\n\n\nstatic void\nngx_http_realip_cleanup(void *data)\n{\n    ngx_http_realip_ctx_t *ctx = data;\n\n    ngx_connection_t  *c;\n\n    c = ctx->connection;\n\n    c->sockaddr = ctx->sockaddr;\n    c->socklen = ctx->socklen;\n    c->addr_text = ctx->addr_text;\n}\n\n\nstatic char *\nngx_http_realip_from(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_realip_loc_conf_t *rlcf = conf;\n\n    ngx_int_t             rc;\n    ngx_str_t            *value;\n    ngx_url_t             u;\n    ngx_cidr_t            c, *cidr;\n    ngx_uint_t            i;\n    struct sockaddr_in   *sin;\n#if (NGX_HAVE_INET6)\n    struct sockaddr_in6  *sin6;\n#endif\n\n    value = cf->args->elts;\n\n    if (rlcf->from == NULL) {\n        rlcf->from = ngx_array_create(cf->pool, 2,\n                                      sizeof(ngx_cidr_t));\n        if (rlcf->from == NULL) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n#if (NGX_HAVE_UNIX_DOMAIN)\n\n    if (ngx_strcmp(value[1].data, \"unix:\") == 0) {\n        cidr = ngx_array_push(rlcf->from);\n        if (cidr == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        cidr->family = AF_UNIX;\n        return NGX_CONF_OK;\n    }\n\n#endif\n\n    rc = ngx_ptocidr(&value[1], &c);\n\n    if (rc != NGX_ERROR) {\n        if (rc == NGX_DONE) {\n            ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                               \"low address bits of %V are meaningless\",\n                               &value[1]);\n        }\n\n        cidr = ngx_array_push(rlcf->from);\n        if (cidr == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        *cidr = c;\n\n        return NGX_CONF_OK;\n    }\n\n    ngx_memzero(&u, sizeof(ngx_url_t));\n    u.host = value[1];\n\n    if (ngx_inet_resolve_host(cf->pool, &u) != NGX_OK) {\n        if (u.err) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"%s in set_real_ip_from \\\"%V\\\"\",\n                               u.err, &u.host);\n        }\n\n        return NGX_CONF_ERROR;\n    }\n\n    cidr = ngx_array_push_n(rlcf->from, u.naddrs);\n    if (cidr == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_memzero(cidr, u.naddrs * sizeof(ngx_cidr_t));\n\n    for (i = 0; i < u.naddrs; i++) {\n        cidr[i].family = u.addrs[i].sockaddr->sa_family;\n\n        switch (cidr[i].family) {\n\n#if (NGX_HAVE_INET6)\n        case AF_INET6:\n            sin6 = (struct sockaddr_in6 *) u.addrs[i].sockaddr;\n            cidr[i].u.in6.addr = sin6->sin6_addr;\n            ngx_memset(cidr[i].u.in6.mask.s6_addr, 0xff, 16);\n            break;\n#endif\n\n        default: /* AF_INET */\n            sin = (struct sockaddr_in *) u.addrs[i].sockaddr;\n            cidr[i].u.in.addr = sin->sin_addr.s_addr;\n            cidr[i].u.in.mask = 0xffffffff;\n            break;\n        }\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_realip(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_realip_loc_conf_t *rlcf = conf;\n\n    ngx_str_t  *value;\n\n    if (rlcf->type != NGX_CONF_UNSET_UINT) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    if (ngx_strcmp(value[1].data, \"X-Real-IP\") == 0) {\n        rlcf->type = NGX_HTTP_REALIP_XREALIP;\n        return NGX_CONF_OK;\n    }\n\n    if (ngx_strcmp(value[1].data, \"X-Forwarded-For\") == 0) {\n        rlcf->type = NGX_HTTP_REALIP_XFWD;\n        return NGX_CONF_OK;\n    }\n\n    if (ngx_strcmp(value[1].data, \"proxy_protocol\") == 0) {\n        rlcf->type = NGX_HTTP_REALIP_PROXY;\n        return NGX_CONF_OK;\n    }\n\n    rlcf->type = NGX_HTTP_REALIP_HEADER;\n    rlcf->hash = ngx_hash_strlow(value[1].data, value[1].data, value[1].len);\n    rlcf->header = value[1];\n\n    return NGX_CONF_OK;\n}\n\n\nstatic void *\nngx_http_realip_create_loc_conf(ngx_conf_t *cf)\n{\n    ngx_http_realip_loc_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_realip_loc_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc():\n     *\n     *     conf->from = NULL;\n     *     conf->hash = 0;\n     *     conf->header = { 0, NULL };\n     */\n\n    conf->type = NGX_CONF_UNSET_UINT;\n    conf->recursive = NGX_CONF_UNSET;\n\n    return conf;\n}\n\n\nstatic char *\nngx_http_realip_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_http_realip_loc_conf_t  *prev = parent;\n    ngx_http_realip_loc_conf_t  *conf = child;\n\n    if (conf->from == NULL) {\n        conf->from = prev->from;\n    }\n\n    ngx_conf_merge_uint_value(conf->type, prev->type, NGX_HTTP_REALIP_XREALIP);\n    ngx_conf_merge_value(conf->recursive, prev->recursive, 0);\n\n    if (conf->header.len == 0) {\n        conf->hash = prev->hash;\n        conf->header = prev->header;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_realip_add_variables(ngx_conf_t *cf)\n{\n    ngx_http_variable_t  *var, *v;\n\n    for (v = ngx_http_realip_vars; v->name.len; v++) {\n        var = ngx_http_add_variable(cf, &v->name, v->flags);\n        if (var == NULL) {\n            return NGX_ERROR;\n        }\n\n        var->get_handler = v->get_handler;\n        var->data = v->data;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_realip_init(ngx_conf_t *cf)\n{\n    ngx_http_handler_pt        *h;\n    ngx_http_core_main_conf_t  *cmcf;\n\n    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);\n\n    h = ngx_array_push(&cmcf->phases[NGX_HTTP_POST_READ_PHASE].handlers);\n    if (h == NULL) {\n        return NGX_ERROR;\n    }\n\n    *h = ngx_http_realip_handler;\n\n    h = ngx_array_push(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers);\n    if (h == NULL) {\n        return NGX_ERROR;\n    }\n\n    *h = ngx_http_realip_handler;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_http_realip_ctx_t *\nngx_http_realip_get_module_ctx(ngx_http_request_t *r)\n{\n    ngx_pool_cleanup_t     *cln;\n    ngx_http_realip_ctx_t  *ctx;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_realip_module);\n\n    if (ctx == NULL && (r->internal || r->filter_finalize)) {\n\n        /*\n         * if module context was reset, the original address\n         * can still be found in the cleanup handler\n         */\n\n        for (cln = r->pool->cleanup; cln; cln = cln->next) {\n            if (cln->handler == ngx_http_realip_cleanup) {\n                ctx = cln->data;\n                break;\n            }\n        }\n    }\n\n    return ctx;\n}\n\n\nstatic ngx_int_t\nngx_http_realip_remote_addr_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    ngx_str_t              *addr_text;\n    ngx_http_realip_ctx_t  *ctx;\n\n    ctx = ngx_http_realip_get_module_ctx(r);\n\n    addr_text = ctx ? &ctx->addr_text : &r->connection->addr_text;\n\n    v->len = addr_text->len;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = addr_text->data;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_realip_remote_port_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    ngx_uint_t              port;\n    struct sockaddr        *sa;\n    ngx_http_realip_ctx_t  *ctx;\n\n    ctx = ngx_http_realip_get_module_ctx(r);\n\n    sa = ctx ? ctx->sockaddr : r->connection->sockaddr;\n\n    v->len = 0;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    v->data = ngx_pnalloc(r->pool, sizeof(\"65535\") - 1);\n    if (v->data == NULL) {\n        return NGX_ERROR;\n    }\n\n    port = ngx_inet_get_port(sa);\n\n    if (port > 0 && port < 65536) {\n        v->len = ngx_sprintf(v->data, \"%ui\", port) - v->data;\n    }\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "src/http/modules/ngx_http_referer_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\n#define NGX_HTTP_REFERER_NO_URI_PART  ((void *) 4)\n\n\ntypedef struct {\n    ngx_hash_combined_t      hash;\n\n#if (NGX_PCRE)\n    ngx_array_t             *regex;\n    ngx_array_t             *server_name_regex;\n#endif\n\n    ngx_flag_t               no_referer;\n    ngx_flag_t               blocked_referer;\n    ngx_flag_t               server_names;\n\n    ngx_hash_keys_arrays_t  *keys;\n\n    ngx_uint_t               referer_hash_max_size;\n    ngx_uint_t               referer_hash_bucket_size;\n} ngx_http_referer_conf_t;\n\n\nstatic ngx_int_t ngx_http_referer_add_variables(ngx_conf_t *cf);\nstatic void * ngx_http_referer_create_conf(ngx_conf_t *cf);\nstatic char * ngx_http_referer_merge_conf(ngx_conf_t *cf, void *parent,\n    void *child);\nstatic char *ngx_http_valid_referers(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic ngx_int_t ngx_http_add_referer(ngx_conf_t *cf,\n    ngx_hash_keys_arrays_t *keys, ngx_str_t *value, ngx_str_t *uri);\nstatic ngx_int_t ngx_http_add_regex_referer(ngx_conf_t *cf,\n    ngx_http_referer_conf_t *rlcf, ngx_str_t *name);\n#if (NGX_PCRE)\nstatic ngx_int_t ngx_http_add_regex_server_name(ngx_conf_t *cf,\n    ngx_http_referer_conf_t *rlcf, ngx_http_regex_t *regex);\n#endif\nstatic int ngx_libc_cdecl ngx_http_cmp_referer_wildcards(const void *one,\n    const void *two);\n\n\nstatic ngx_command_t  ngx_http_referer_commands[] = {\n\n    { ngx_string(\"valid_referers\"),\n      NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,\n      ngx_http_valid_referers,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"referer_hash_max_size\"),\n      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_referer_conf_t, referer_hash_max_size),\n      NULL },\n\n    { ngx_string(\"referer_hash_bucket_size\"),\n      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_referer_conf_t, referer_hash_bucket_size),\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_referer_module_ctx = {\n    ngx_http_referer_add_variables,        /* preconfiguration */\n    NULL,                                  /* 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    ngx_http_referer_create_conf,          /* create location configuration */\n    ngx_http_referer_merge_conf            /* merge location configuration */\n};\n\n\nngx_module_t  ngx_http_referer_module = {\n    NGX_MODULE_V1,\n    &ngx_http_referer_module_ctx,          /* module context */\n    ngx_http_referer_commands,             /* 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_str_t  ngx_http_invalid_referer_name = ngx_string(\"invalid_referer\");\n\n\nstatic ngx_int_t\nngx_http_referer_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,\n    uintptr_t data)\n{\n    u_char                    *p, *ref, *last;\n    size_t                     len;\n    ngx_str_t                 *uri;\n    ngx_uint_t                 i, key;\n    ngx_http_referer_conf_t   *rlcf;\n    u_char                     buf[256];\n#if (NGX_PCRE)\n    ngx_int_t                  rc;\n    ngx_str_t                  referer;\n#endif\n\n    rlcf = ngx_http_get_module_loc_conf(r, ngx_http_referer_module);\n\n    if (rlcf->hash.hash.buckets == NULL\n        && rlcf->hash.wc_head == NULL\n        && rlcf->hash.wc_tail == NULL\n#if (NGX_PCRE)\n        && rlcf->regex == NULL\n        && rlcf->server_name_regex == NULL\n#endif\n       )\n    {\n        goto valid;\n    }\n\n    if (r->headers_in.referer == NULL) {\n        if (rlcf->no_referer) {\n            goto valid;\n        }\n\n        goto invalid;\n    }\n\n    len = r->headers_in.referer->value.len;\n    ref = r->headers_in.referer->value.data;\n\n    if (len >= sizeof(\"http://i.ru\") - 1) {\n        last = ref + len;\n\n        if (ngx_strncasecmp(ref, (u_char *) \"http://\", 7) == 0) {\n            ref += 7;\n            len -= 7;\n            goto valid_scheme;\n\n        } else if (ngx_strncasecmp(ref, (u_char *) \"https://\", 8) == 0) {\n            ref += 8;\n            len -= 8;\n            goto valid_scheme;\n        }\n    }\n\n    if (rlcf->blocked_referer) {\n        goto valid;\n    }\n\n    goto invalid;\n\nvalid_scheme:\n\n    i = 0;\n    key = 0;\n\n    for (p = ref; p < last; p++) {\n        if (*p == '/' || *p == ':') {\n            break;\n        }\n\n        if (i == 256) {\n            goto invalid;\n        }\n\n        buf[i] = ngx_tolower(*p);\n        key = ngx_hash(key, buf[i++]);\n    }\n\n    uri = ngx_hash_find_combined(&rlcf->hash, key, buf, p - ref);\n\n    if (uri) {\n        goto uri;\n    }\n\n#if (NGX_PCRE)\n\n    if (rlcf->server_name_regex) {\n        referer.len = p - ref;\n        referer.data = buf;\n\n        rc = ngx_regex_exec_array(rlcf->server_name_regex, &referer,\n                                  r->connection->log);\n\n        if (rc == NGX_OK) {\n            goto valid;\n        }\n\n        if (rc == NGX_ERROR) {\n            return rc;\n        }\n\n        /* NGX_DECLINED */\n    }\n\n    if (rlcf->regex) {\n        referer.len = len;\n        referer.data = ref;\n\n        rc = ngx_regex_exec_array(rlcf->regex, &referer, r->connection->log);\n\n        if (rc == NGX_OK) {\n            goto valid;\n        }\n\n        if (rc == NGX_ERROR) {\n            return rc;\n        }\n\n        /* NGX_DECLINED */\n    }\n\n#endif\n\ninvalid:\n\n    *v = ngx_http_variable_true_value;\n\n    return NGX_OK;\n\nuri:\n\n    for ( /* void */ ; p < last; p++) {\n        if (*p == '/') {\n            break;\n        }\n    }\n\n    len = last - p;\n\n    if (uri == NGX_HTTP_REFERER_NO_URI_PART) {\n        goto valid;\n    }\n\n    if (len < uri->len || ngx_strncmp(uri->data, p, uri->len) != 0) {\n        goto invalid;\n    }\n\nvalid:\n\n    *v = ngx_http_variable_null_value;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_referer_add_variables(ngx_conf_t *cf)\n{\n    ngx_http_variable_t  *var;\n\n    var = ngx_http_add_variable(cf, &ngx_http_invalid_referer_name,\n                                NGX_HTTP_VAR_CHANGEABLE);\n    if (var == NULL) {\n        return NGX_ERROR;\n    }\n\n    var->get_handler = ngx_http_referer_variable;\n\n    return NGX_OK;\n}\n\n\nstatic void *\nngx_http_referer_create_conf(ngx_conf_t *cf)\n{\n    ngx_http_referer_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_referer_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc():\n     *\n     *     conf->hash = { NULL };\n     *     conf->server_names = 0;\n     *     conf->keys = NULL;\n     */\n\n#if (NGX_PCRE)\n    conf->regex = NGX_CONF_UNSET_PTR;\n    conf->server_name_regex = NGX_CONF_UNSET_PTR;\n#endif\n\n    conf->no_referer = NGX_CONF_UNSET;\n    conf->blocked_referer = NGX_CONF_UNSET;\n    conf->referer_hash_max_size = NGX_CONF_UNSET_UINT;\n    conf->referer_hash_bucket_size = NGX_CONF_UNSET_UINT;\n\n    return conf;\n}\n\n\nstatic char *\nngx_http_referer_merge_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_http_referer_conf_t *prev = parent;\n    ngx_http_referer_conf_t *conf = child;\n\n    ngx_uint_t                 n;\n    ngx_hash_init_t            hash;\n    ngx_http_server_name_t    *sn;\n    ngx_http_core_srv_conf_t  *cscf;\n\n    if (conf->keys == NULL) {\n        conf->hash = prev->hash;\n\n#if (NGX_PCRE)\n        ngx_conf_merge_ptr_value(conf->regex, prev->regex, NULL);\n        ngx_conf_merge_ptr_value(conf->server_name_regex,\n                                 prev->server_name_regex, NULL);\n#endif\n        ngx_conf_merge_value(conf->no_referer, prev->no_referer, 0);\n        ngx_conf_merge_value(conf->blocked_referer, prev->blocked_referer, 0);\n        ngx_conf_merge_uint_value(conf->referer_hash_max_size,\n                                  prev->referer_hash_max_size, 2048);\n        ngx_conf_merge_uint_value(conf->referer_hash_bucket_size,\n                                  prev->referer_hash_bucket_size, 64);\n\n        return NGX_CONF_OK;\n    }\n\n    if (conf->server_names == 1) {\n        cscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_core_module);\n\n        sn = cscf->server_names.elts;\n        for (n = 0; n < cscf->server_names.nelts; n++) {\n\n#if (NGX_PCRE)\n            if (sn[n].regex) {\n\n                if (ngx_http_add_regex_server_name(cf, conf, sn[n].regex)\n                    != NGX_OK)\n                {\n                    return NGX_CONF_ERROR;\n                }\n\n                continue;\n            }\n#endif\n\n            if (ngx_http_add_referer(cf, conf->keys, &sn[n].name, NULL)\n                != NGX_OK)\n            {\n                return NGX_CONF_ERROR;\n            }\n        }\n    }\n\n    if ((conf->no_referer == 1 || conf->blocked_referer == 1)\n        && conf->keys->keys.nelts == 0\n        && conf->keys->dns_wc_head.nelts == 0\n        && conf->keys->dns_wc_tail.nelts == 0)\n    {\n        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                      \"the \\\"none\\\" or \\\"blocked\\\" referers are specified \"\n                      \"in the \\\"valid_referers\\\" directive \"\n                      \"without any valid referer\");\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_conf_merge_uint_value(conf->referer_hash_max_size,\n                              prev->referer_hash_max_size, 2048);\n    ngx_conf_merge_uint_value(conf->referer_hash_bucket_size,\n                              prev->referer_hash_bucket_size, 64);\n    conf->referer_hash_bucket_size = ngx_align(conf->referer_hash_bucket_size,\n                                               ngx_cacheline_size);\n\n    hash.key = ngx_hash_key_lc;\n    hash.max_size = conf->referer_hash_max_size;\n    hash.bucket_size = conf->referer_hash_bucket_size;\n    hash.name = \"referer_hash\";\n    hash.pool = cf->pool;\n\n    if (conf->keys->keys.nelts) {\n        hash.hash = &conf->hash.hash;\n        hash.temp_pool = NULL;\n\n        if (ngx_hash_init(&hash, conf->keys->keys.elts, conf->keys->keys.nelts)\n            != NGX_OK)\n        {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    if (conf->keys->dns_wc_head.nelts) {\n\n        ngx_qsort(conf->keys->dns_wc_head.elts,\n                  (size_t) conf->keys->dns_wc_head.nelts,\n                  sizeof(ngx_hash_key_t),\n                  ngx_http_cmp_referer_wildcards);\n\n        hash.hash = NULL;\n        hash.temp_pool = cf->temp_pool;\n\n        if (ngx_hash_wildcard_init(&hash, conf->keys->dns_wc_head.elts,\n                                   conf->keys->dns_wc_head.nelts)\n            != NGX_OK)\n        {\n            return NGX_CONF_ERROR;\n        }\n\n        conf->hash.wc_head = (ngx_hash_wildcard_t *) hash.hash;\n    }\n\n    if (conf->keys->dns_wc_tail.nelts) {\n\n        ngx_qsort(conf->keys->dns_wc_tail.elts,\n                  (size_t) conf->keys->dns_wc_tail.nelts,\n                  sizeof(ngx_hash_key_t),\n                  ngx_http_cmp_referer_wildcards);\n\n        hash.hash = NULL;\n        hash.temp_pool = cf->temp_pool;\n\n        if (ngx_hash_wildcard_init(&hash, conf->keys->dns_wc_tail.elts,\n                                   conf->keys->dns_wc_tail.nelts)\n            != NGX_OK)\n        {\n            return NGX_CONF_ERROR;\n        }\n\n        conf->hash.wc_tail = (ngx_hash_wildcard_t *) hash.hash;\n    }\n\n#if (NGX_PCRE)\n    ngx_conf_merge_ptr_value(conf->regex, prev->regex, NULL);\n    ngx_conf_merge_ptr_value(conf->server_name_regex, prev->server_name_regex,\n                             NULL);\n#endif\n\n    if (conf->no_referer == NGX_CONF_UNSET) {\n        conf->no_referer = 0;\n    }\n\n    if (conf->blocked_referer == NGX_CONF_UNSET) {\n        conf->blocked_referer = 0;\n    }\n\n    conf->keys = NULL;\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_valid_referers(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_referer_conf_t  *rlcf = conf;\n\n    u_char      *p;\n    ngx_str_t   *value, uri;\n    ngx_uint_t   i;\n\n    if (rlcf->keys == NULL) {\n        rlcf->keys = ngx_pcalloc(cf->temp_pool, sizeof(ngx_hash_keys_arrays_t));\n        if (rlcf->keys == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        rlcf->keys->pool = cf->pool;\n        rlcf->keys->temp_pool = cf->pool;\n\n        if (ngx_hash_keys_array_init(rlcf->keys, NGX_HASH_SMALL) != NGX_OK) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    value = cf->args->elts;\n\n    for (i = 1; i < cf->args->nelts; i++) {\n        if (value[i].len == 0) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"invalid referer \\\"%V\\\"\", &value[i]);\n            return NGX_CONF_ERROR;\n        }\n\n        if (ngx_strcmp(value[i].data, \"none\") == 0) {\n            rlcf->no_referer = 1;\n            continue;\n        }\n\n        if (ngx_strcmp(value[i].data, \"blocked\") == 0) {\n            rlcf->blocked_referer = 1;\n            continue;\n        }\n\n        if (ngx_strcmp(value[i].data, \"server_names\") == 0) {\n            rlcf->server_names = 1;\n            continue;\n        }\n\n        if (value[i].data[0] == '~') {\n            if (ngx_http_add_regex_referer(cf, rlcf, &value[i]) != NGX_OK) {\n                return NGX_CONF_ERROR;\n            }\n\n            continue;\n        }\n\n        ngx_str_null(&uri);\n\n        p = (u_char *) ngx_strchr(value[i].data, '/');\n\n        if (p) {\n            uri.len = (value[i].data + value[i].len) - p;\n            uri.data = p;\n            value[i].len = p - value[i].data;\n        }\n\n        if (ngx_http_add_referer(cf, rlcf->keys, &value[i], &uri) != NGX_OK) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_add_referer(ngx_conf_t *cf, ngx_hash_keys_arrays_t *keys,\n    ngx_str_t *value, ngx_str_t *uri)\n{\n    ngx_int_t   rc;\n    ngx_str_t  *u;\n\n    if (uri == NULL || uri->len == 0) {\n        u = NGX_HTTP_REFERER_NO_URI_PART;\n\n    } else {\n        u = ngx_palloc(cf->pool, sizeof(ngx_str_t));\n        if (u == NULL) {\n            return NGX_ERROR;\n        }\n\n        *u = *uri;\n    }\n\n    rc = ngx_hash_add_key(keys, value, u, NGX_HASH_WILDCARD_KEY);\n\n    if (rc == NGX_OK) {\n        return NGX_OK;\n    }\n\n    if (rc == NGX_DECLINED) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid hostname or wildcard \\\"%V\\\"\", value);\n    }\n\n    if (rc == NGX_BUSY) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"conflicting parameter \\\"%V\\\"\", value);\n    }\n\n    return NGX_ERROR;\n}\n\n\nstatic ngx_int_t\nngx_http_add_regex_referer(ngx_conf_t *cf, ngx_http_referer_conf_t *rlcf,\n    ngx_str_t *name)\n{\n#if (NGX_PCRE)\n    ngx_regex_elt_t      *re;\n    ngx_regex_compile_t   rc;\n    u_char                errstr[NGX_MAX_CONF_ERRSTR];\n\n    if (name->len == 1) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, \"empty regex in \\\"%V\\\"\", name);\n        return NGX_ERROR;\n    }\n\n    if (rlcf->regex == NGX_CONF_UNSET_PTR) {\n        rlcf->regex = ngx_array_create(cf->pool, 2, sizeof(ngx_regex_elt_t));\n        if (rlcf->regex == NULL) {\n            return NGX_ERROR;\n        }\n    }\n\n    re = ngx_array_push(rlcf->regex);\n    if (re == NULL) {\n        return NGX_ERROR;\n    }\n\n    name->len--;\n    name->data++;\n\n    ngx_memzero(&rc, sizeof(ngx_regex_compile_t));\n\n    rc.pattern = *name;\n    rc.pool = cf->pool;\n    rc.options = NGX_REGEX_CASELESS;\n    rc.err.len = NGX_MAX_CONF_ERRSTR;\n    rc.err.data = errstr;\n\n    if (ngx_regex_compile(&rc) != NGX_OK) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, \"%V\", &rc.err);\n        return NGX_ERROR;\n    }\n\n    re->regex = rc.regex;\n    re->name = name->data;\n\n    return NGX_OK;\n\n#else\n\n    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                       \"the using of the regex \\\"%V\\\" requires PCRE library\",\n                       name);\n\n    return NGX_ERROR;\n\n#endif\n}\n\n\n#if (NGX_PCRE)\n\nstatic ngx_int_t\nngx_http_add_regex_server_name(ngx_conf_t *cf, ngx_http_referer_conf_t *rlcf,\n    ngx_http_regex_t *regex)\n{\n    ngx_regex_elt_t  *re;\n\n    if (rlcf->server_name_regex == NGX_CONF_UNSET_PTR) {\n        rlcf->server_name_regex = ngx_array_create(cf->pool, 2,\n                                                   sizeof(ngx_regex_elt_t));\n        if (rlcf->server_name_regex == NULL) {\n            return NGX_ERROR;\n        }\n    }\n\n    re = ngx_array_push(rlcf->server_name_regex);\n    if (re == NULL) {\n        return NGX_ERROR;\n    }\n\n    re->regex = regex->regex;\n    re->name = regex->name.data;\n\n    return NGX_OK;\n}\n\n#endif\n\n\nstatic int ngx_libc_cdecl\nngx_http_cmp_referer_wildcards(const void *one, const void *two)\n{\n    ngx_hash_key_t  *first, *second;\n\n    first = (ngx_hash_key_t *) one;\n    second = (ngx_hash_key_t *) two;\n\n    return ngx_dns_strcmp(first->key.data, second->key.data);\n}\n"
  },
  {
    "path": "src/http/modules/ngx_http_rewrite_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\ntypedef struct {\n    ngx_array_t  *codes;        /* uintptr_t */\n\n    ngx_uint_t    stack_size;\n\n    ngx_flag_t    log;\n    ngx_flag_t    uninitialized_variable_warn;\n} ngx_http_rewrite_loc_conf_t;\n\n\nstatic void *ngx_http_rewrite_create_loc_conf(ngx_conf_t *cf);\nstatic char *ngx_http_rewrite_merge_loc_conf(ngx_conf_t *cf,\n    void *parent, void *child);\nstatic ngx_int_t ngx_http_rewrite_init(ngx_conf_t *cf);\nstatic char *ngx_http_rewrite(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\nstatic char *ngx_http_rewrite_return(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_http_rewrite_break(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_http_rewrite_if(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char * ngx_http_rewrite_if_condition(ngx_conf_t *cf,\n    ngx_http_rewrite_loc_conf_t *lcf);\nstatic char *ngx_http_rewrite_variable(ngx_conf_t *cf,\n    ngx_http_rewrite_loc_conf_t *lcf, ngx_str_t *value);\nstatic char *ngx_http_rewrite_set(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char * ngx_http_rewrite_value(ngx_conf_t *cf,\n    ngx_http_rewrite_loc_conf_t *lcf, ngx_str_t *value);\n\n\nstatic ngx_command_t  ngx_http_rewrite_commands[] = {\n\n    { ngx_string(\"rewrite\"),\n      NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF\n                       |NGX_CONF_TAKE23,\n      ngx_http_rewrite,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"return\"),\n      NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF\n                       |NGX_CONF_TAKE12,\n      ngx_http_rewrite_return,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"break\"),\n      NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF\n                       |NGX_CONF_NOARGS,\n      ngx_http_rewrite_break,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"if\"),\n      NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_BLOCK|NGX_CONF_1MORE,\n      ngx_http_rewrite_if,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"set\"),\n      NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF\n                       |NGX_CONF_TAKE2,\n      ngx_http_rewrite_set,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"rewrite_log\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF|NGX_HTTP_LOC_CONF\n                        |NGX_HTTP_LIF_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_rewrite_loc_conf_t, log),\n      NULL },\n\n    { ngx_string(\"uninitialized_variable_warn\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF|NGX_HTTP_LOC_CONF\n                        |NGX_HTTP_LIF_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_rewrite_loc_conf_t, uninitialized_variable_warn),\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_rewrite_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    ngx_http_rewrite_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    ngx_http_rewrite_create_loc_conf,      /* create location configuration */\n    ngx_http_rewrite_merge_loc_conf        /* merge location configuration */\n};\n\n\nngx_module_t  ngx_http_rewrite_module = {\n    NGX_MODULE_V1,\n    &ngx_http_rewrite_module_ctx,          /* module context */\n    ngx_http_rewrite_commands,             /* 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_rewrite_handler(ngx_http_request_t *r)\n{\n    ngx_int_t                     index;\n    ngx_http_script_code_pt       code;\n    ngx_http_script_engine_t     *e;\n    ngx_http_core_srv_conf_t     *cscf;\n    ngx_http_core_main_conf_t    *cmcf;\n    ngx_http_rewrite_loc_conf_t  *rlcf;\n\n    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);\n    cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);\n    index = cmcf->phase_engine.location_rewrite_index;\n\n    if (r->phase_handler == index && r->loc_conf == cscf->ctx->loc_conf) {\n        /* skipping location rewrite phase for server null location */\n        return NGX_DECLINED;\n    }\n\n    rlcf = ngx_http_get_module_loc_conf(r, ngx_http_rewrite_module);\n\n    if (rlcf->codes == NULL) {\n        return NGX_DECLINED;\n    }\n\n    e = ngx_pcalloc(r->pool, sizeof(ngx_http_script_engine_t));\n    if (e == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    e->sp = ngx_pcalloc(r->pool,\n                        rlcf->stack_size * sizeof(ngx_http_variable_value_t));\n    if (e->sp == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    e->ip = rlcf->codes->elts;\n    e->request = r;\n    e->quote = 1;\n    e->log = rlcf->log;\n    e->status = NGX_DECLINED;\n\n    while (*(uintptr_t *) e->ip) {\n        code = *(ngx_http_script_code_pt *) e->ip;\n        code(e);\n    }\n\n    return e->status;\n}\n\n\nstatic ngx_int_t\nngx_http_rewrite_var(ngx_http_request_t *r, ngx_http_variable_value_t *v,\n    uintptr_t data)\n{\n    ngx_http_variable_t          *var;\n    ngx_http_core_main_conf_t    *cmcf;\n    ngx_http_rewrite_loc_conf_t  *rlcf;\n\n    rlcf = ngx_http_get_module_loc_conf(r, ngx_http_rewrite_module);\n\n    if (rlcf->uninitialized_variable_warn == 0) {\n        *v = ngx_http_variable_null_value;\n        return NGX_OK;\n    }\n\n    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);\n\n    var = cmcf->variables.elts;\n\n    /*\n     * the ngx_http_rewrite_module sets variables directly in r->variables,\n     * and they should be handled by ngx_http_get_indexed_variable(),\n     * so the handler is called only if the variable is not initialized\n     */\n\n    ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,\n                  \"using uninitialized \\\"%V\\\" variable\", &var[data].name);\n\n    *v = ngx_http_variable_null_value;\n\n    return NGX_OK;\n}\n\n\nstatic void *\nngx_http_rewrite_create_loc_conf(ngx_conf_t *cf)\n{\n    ngx_http_rewrite_loc_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_rewrite_loc_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    conf->stack_size = NGX_CONF_UNSET_UINT;\n    conf->log = NGX_CONF_UNSET;\n    conf->uninitialized_variable_warn = NGX_CONF_UNSET;\n\n    return conf;\n}\n\n\nstatic char *\nngx_http_rewrite_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_http_rewrite_loc_conf_t *prev = parent;\n    ngx_http_rewrite_loc_conf_t *conf = child;\n\n    uintptr_t  *code;\n\n    ngx_conf_merge_value(conf->log, prev->log, 0);\n    ngx_conf_merge_value(conf->uninitialized_variable_warn,\n                         prev->uninitialized_variable_warn, 1);\n    ngx_conf_merge_uint_value(conf->stack_size, prev->stack_size, 10);\n\n    if (conf->codes == NULL) {\n        return NGX_CONF_OK;\n    }\n\n    if (conf->codes == prev->codes) {\n        return NGX_CONF_OK;\n    }\n\n    code = ngx_array_push_n(conf->codes, sizeof(uintptr_t));\n    if (code == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    *code = (uintptr_t) NULL;\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_rewrite_init(ngx_conf_t *cf)\n{\n    ngx_http_handler_pt        *h;\n    ngx_http_core_main_conf_t  *cmcf;\n\n    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);\n\n    h = ngx_array_push(&cmcf->phases[NGX_HTTP_SERVER_REWRITE_PHASE].handlers);\n    if (h == NULL) {\n        return NGX_ERROR;\n    }\n\n    *h = ngx_http_rewrite_handler;\n\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_rewrite_handler;\n\n    return NGX_OK;\n}\n\n\nstatic char *\nngx_http_rewrite(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_rewrite_loc_conf_t  *lcf = conf;\n\n    ngx_str_t                         *value;\n    ngx_uint_t                         last;\n    ngx_regex_compile_t                rc;\n    ngx_http_script_code_pt           *code;\n    ngx_http_script_compile_t          sc;\n    ngx_http_script_regex_code_t      *regex;\n    ngx_http_script_regex_end_code_t  *regex_end;\n    u_char                             errstr[NGX_MAX_CONF_ERRSTR];\n\n    regex = ngx_http_script_start_code(cf->pool, &lcf->codes,\n                                       sizeof(ngx_http_script_regex_code_t));\n    if (regex == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_memzero(regex, sizeof(ngx_http_script_regex_code_t));\n\n    value = cf->args->elts;\n\n    if (value[2].len == 0) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, \"empty replacement\");\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_memzero(&rc, sizeof(ngx_regex_compile_t));\n\n    rc.pattern = value[1];\n    rc.err.len = NGX_MAX_CONF_ERRSTR;\n    rc.err.data = errstr;\n\n    /* TODO: NGX_REGEX_CASELESS */\n\n    regex->regex = ngx_http_regex_compile(cf, &rc);\n    if (regex->regex == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    regex->code = ngx_http_script_regex_start_code;\n    regex->uri = 1;\n    regex->name = value[1];\n\n    if (value[2].data[value[2].len - 1] == '?') {\n\n        /* the last \"?\" drops the original arguments */\n        value[2].len--;\n\n    } else {\n        regex->add_args = 1;\n    }\n\n    last = 0;\n\n    if (ngx_strncmp(value[2].data, \"http://\", sizeof(\"http://\") - 1) == 0\n        || ngx_strncmp(value[2].data, \"https://\", sizeof(\"https://\") - 1) == 0\n        || ngx_strncmp(value[2].data, \"$scheme\", sizeof(\"$scheme\") - 1) == 0)\n    {\n        regex->status = NGX_HTTP_MOVED_TEMPORARILY;\n        regex->redirect = 1;\n        last = 1;\n    }\n\n    if (cf->args->nelts == 4) {\n        if (ngx_strcmp(value[3].data, \"last\") == 0) {\n            last = 1;\n\n        } else if (ngx_strcmp(value[3].data, \"break\") == 0) {\n            regex->break_cycle = 1;\n            last = 1;\n\n        } else if (ngx_strcmp(value[3].data, \"redirect\") == 0) {\n            regex->status = NGX_HTTP_MOVED_TEMPORARILY;\n            regex->redirect = 1;\n            last = 1;\n\n        } else if (ngx_strcmp(value[3].data, \"permanent\") == 0) {\n            regex->status = NGX_HTTP_MOVED_PERMANENTLY;\n            regex->redirect = 1;\n            last = 1;\n\n        } else {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"invalid parameter \\\"%V\\\"\", &value[3]);\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));\n\n    sc.cf = cf;\n    sc.source = &value[2];\n    sc.lengths = &regex->lengths;\n    sc.values = &lcf->codes;\n    sc.variables = ngx_http_script_variables_count(&value[2]);\n    sc.main = regex;\n    sc.complete_lengths = 1;\n    sc.compile_args = !regex->redirect;\n\n    if (ngx_http_script_compile(&sc) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    regex = sc.main;\n\n    regex->size = sc.size;\n    regex->args = sc.args;\n\n    if (sc.variables == 0 && !sc.dup_capture) {\n        regex->lengths = NULL;\n    }\n\n    regex_end = ngx_http_script_add_code(lcf->codes,\n                                      sizeof(ngx_http_script_regex_end_code_t),\n                                      &regex);\n    if (regex_end == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    regex_end->code = ngx_http_script_regex_end_code;\n    regex_end->uri = regex->uri;\n    regex_end->args = regex->args;\n    regex_end->add_args = regex->add_args;\n    regex_end->redirect = regex->redirect;\n\n    if (last) {\n        code = ngx_http_script_add_code(lcf->codes, sizeof(uintptr_t), &regex);\n        if (code == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        *code = NULL;\n    }\n\n    regex->next = (u_char *) lcf->codes->elts + lcf->codes->nelts\n                                              - (u_char *) regex;\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_rewrite_return(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_rewrite_loc_conf_t  *lcf = conf;\n\n    u_char                            *p;\n    ngx_str_t                         *value, *v;\n    ngx_http_script_return_code_t     *ret;\n    ngx_http_compile_complex_value_t   ccv;\n\n    ret = ngx_http_script_start_code(cf->pool, &lcf->codes,\n                                     sizeof(ngx_http_script_return_code_t));\n    if (ret == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    value = cf->args->elts;\n\n    ngx_memzero(ret, sizeof(ngx_http_script_return_code_t));\n\n    ret->code = ngx_http_script_return_code;\n\n    p = value[1].data;\n\n    ret->status = ngx_atoi(p, value[1].len);\n\n    if (ret->status == (uintptr_t) NGX_ERROR) {\n\n        if (cf->args->nelts == 2\n            && (ngx_strncmp(p, \"http://\", sizeof(\"http://\") - 1) == 0\n                || ngx_strncmp(p, \"https://\", sizeof(\"https://\") - 1) == 0\n                || ngx_strncmp(p, \"$scheme\", sizeof(\"$scheme\") - 1) == 0))\n        {\n            ret->status = NGX_HTTP_MOVED_TEMPORARILY;\n            v = &value[1];\n\n        } else {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"invalid return code \\\"%V\\\"\", &value[1]);\n            return NGX_CONF_ERROR;\n        }\n\n    } else {\n\n        if (ret->status > 999) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"invalid return code \\\"%V\\\"\", &value[1]);\n            return NGX_CONF_ERROR;\n        }\n\n        if (cf->args->nelts == 2) {\n            return NGX_CONF_OK;\n        }\n\n        v = &value[2];\n    }\n\n    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n\n    ccv.cf = cf;\n    ccv.value = v;\n    ccv.complex_value = &ret->text;\n\n    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_rewrite_break(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_rewrite_loc_conf_t *lcf = conf;\n\n    ngx_http_script_code_pt  *code;\n\n    code = ngx_http_script_start_code(cf->pool, &lcf->codes, sizeof(uintptr_t));\n    if (code == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    *code = ngx_http_script_break_code;\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_rewrite_if(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_rewrite_loc_conf_t  *lcf = conf;\n\n    void                         *mconf;\n    char                         *rv;\n    u_char                       *elts;\n    ngx_uint_t                    i;\n    ngx_conf_t                    save;\n    ngx_http_module_t            *module;\n    ngx_http_conf_ctx_t          *ctx, *pctx;\n    ngx_http_core_loc_conf_t     *clcf, *pclcf;\n    ngx_http_script_if_code_t    *if_code;\n    ngx_http_rewrite_loc_conf_t  *nlcf;\n\n    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));\n    if (ctx == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    pctx = cf->ctx;\n    ctx->main_conf = pctx->main_conf;\n    ctx->srv_conf = pctx->srv_conf;\n\n    ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);\n    if (ctx->loc_conf == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    for (i = 0; cf->cycle->modules[i]; i++) {\n        if (cf->cycle->modules[i]->type != NGX_HTTP_MODULE) {\n            continue;\n        }\n\n        module = cf->cycle->modules[i]->ctx;\n\n        if (module->create_loc_conf) {\n\n            mconf = module->create_loc_conf(cf);\n            if (mconf == NULL) {\n                return NGX_CONF_ERROR;\n            }\n\n            ctx->loc_conf[cf->cycle->modules[i]->ctx_index] = mconf;\n        }\n    }\n\n    pclcf = pctx->loc_conf[ngx_http_core_module.ctx_index];\n\n    clcf = ctx->loc_conf[ngx_http_core_module.ctx_index];\n    clcf->loc_conf = ctx->loc_conf;\n    clcf->name = pclcf->name;\n    clcf->noname = 1;\n\n    if (ngx_http_add_location(cf, &pclcf->locations, clcf) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (ngx_http_rewrite_if_condition(cf, lcf) != NGX_CONF_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    if_code = ngx_array_push_n(lcf->codes, sizeof(ngx_http_script_if_code_t));\n    if (if_code == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    if_code->code = ngx_http_script_if_code;\n\n    elts = lcf->codes->elts;\n\n\n    /* the inner directives must be compiled to the same code array */\n\n    nlcf = ctx->loc_conf[ngx_http_rewrite_module.ctx_index];\n    nlcf->codes = lcf->codes;\n\n\n    save = *cf;\n    cf->ctx = ctx;\n\n    if (cf->cmd_type == NGX_HTTP_SRV_CONF) {\n        if_code->loc_conf = NULL;\n        cf->cmd_type = NGX_HTTP_SIF_CONF;\n\n    } else {\n        if_code->loc_conf = ctx->loc_conf;\n        cf->cmd_type = NGX_HTTP_LIF_CONF;\n    }\n\n    rv = ngx_conf_parse(cf, NULL);\n\n    *cf = save;\n\n    if (rv != NGX_CONF_OK) {\n        return rv;\n    }\n\n\n    if (elts != lcf->codes->elts) {\n        if_code = (ngx_http_script_if_code_t *)\n                   ((u_char *) if_code + ((u_char *) lcf->codes->elts - elts));\n    }\n\n    if_code->next = (u_char *) lcf->codes->elts + lcf->codes->nelts\n                                                - (u_char *) if_code;\n\n    /* the code array belong to parent block */\n\n    nlcf->codes = NULL;\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_rewrite_if_condition(ngx_conf_t *cf, ngx_http_rewrite_loc_conf_t *lcf)\n{\n    u_char                        *p;\n    size_t                         len;\n    ngx_str_t                     *value;\n    ngx_uint_t                     cur, last;\n    ngx_regex_compile_t            rc;\n    ngx_http_script_code_pt       *code;\n    ngx_http_script_file_code_t   *fop;\n    ngx_http_script_regex_code_t  *regex;\n    u_char                         errstr[NGX_MAX_CONF_ERRSTR];\n\n    value = cf->args->elts;\n    last = cf->args->nelts - 1;\n\n    if (value[1].len < 1 || value[1].data[0] != '(') {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid condition \\\"%V\\\"\", &value[1]);\n        return NGX_CONF_ERROR;\n    }\n\n    if (value[1].len == 1) {\n        cur = 2;\n\n    } else {\n        cur = 1;\n        value[1].len--;\n        value[1].data++;\n    }\n\n    if (value[last].len < 1 || value[last].data[value[last].len - 1] != ')') {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid condition \\\"%V\\\"\", &value[last]);\n        return NGX_CONF_ERROR;\n    }\n\n    if (value[last].len == 1) {\n        last--;\n\n    } else {\n        value[last].len--;\n        value[last].data[value[last].len] = '\\0';\n    }\n\n    len = value[cur].len;\n    p = value[cur].data;\n\n    if (len > 1 && p[0] == '$') {\n\n        if (cur != last && cur + 2 != last) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"invalid condition \\\"%V\\\"\", &value[cur]);\n            return NGX_CONF_ERROR;\n        }\n\n        if (ngx_http_rewrite_variable(cf, lcf, &value[cur]) != NGX_CONF_OK) {\n            return NGX_CONF_ERROR;\n        }\n\n        if (cur == last) {\n            return NGX_CONF_OK;\n        }\n\n        cur++;\n\n        len = value[cur].len;\n        p = value[cur].data;\n\n        if (len == 1 && p[0] == '=') {\n\n            if (ngx_http_rewrite_value(cf, lcf, &value[last]) != NGX_CONF_OK) {\n                return NGX_CONF_ERROR;\n            }\n\n            code = ngx_http_script_start_code(cf->pool, &lcf->codes,\n                                              sizeof(uintptr_t));\n            if (code == NULL) {\n                return NGX_CONF_ERROR;\n            }\n\n            *code = ngx_http_script_equal_code;\n\n            return NGX_CONF_OK;\n        }\n\n#if (T_NGX_HTTP_IMPROVED_IF)\n        if (len == 1 && p[0] == '>') {\n\n            if (ngx_http_rewrite_value(cf, lcf, &value[last]) != NGX_CONF_OK) {\n                return NGX_CONF_ERROR;\n            }\n\n            code = ngx_http_script_start_code(cf->pool, &lcf->codes,\n                                              sizeof(uintptr_t));\n            if (code == NULL) {\n                return NGX_CONF_ERROR;\n            }\n\n            *code = ngx_http_script_greater_code;\n\n            return NGX_CONF_OK;\n        }\n\n        if (len == 1 && p[0] == '<') {\n\n            if (ngx_http_rewrite_value(cf, lcf, &value[last]) != NGX_CONF_OK) {\n                return NGX_CONF_ERROR;\n            }\n\n            code = ngx_http_script_start_code(cf->pool, &lcf->codes,\n                                              sizeof(uintptr_t));\n            if (code == NULL) {\n                return NGX_CONF_ERROR;\n            }\n\n            *code = ngx_http_script_less_code;\n\n            return NGX_CONF_OK;\n        }\n\n        if (len == 2 && p[0] == '>' && p[1] == '=') {\n\n            if (ngx_http_rewrite_value(cf, lcf, &value[last]) != NGX_CONF_OK) {\n                return NGX_CONF_ERROR;\n            }\n\n            code = ngx_http_script_start_code(cf->pool, &lcf->codes,\n                                              sizeof(uintptr_t));\n            if (code == NULL) {\n                return NGX_CONF_ERROR;\n            }\n\n            *code = ngx_http_script_greater_or_equal_code;\n            return NGX_CONF_OK;\n\n        }\n\n        if (len == 2 && p[0] == '<' && p[1] == '=') {\n\n            if (ngx_http_rewrite_value(cf, lcf, &value[last]) != NGX_CONF_OK) {\n                return NGX_CONF_ERROR;\n            }\n\n            code = ngx_http_script_start_code(cf->pool, &lcf->codes,\n                                              sizeof(uintptr_t));\n            if (code == NULL) {\n                return NGX_CONF_ERROR;\n            }\n\n            *code = ngx_http_script_less_or_equal_code;\n            return NGX_CONF_OK;\n\n        }\n#endif\n\n        if (len == 2 && p[0] == '!' && p[1] == '=') {\n\n            if (ngx_http_rewrite_value(cf, lcf, &value[last]) != NGX_CONF_OK) {\n                return NGX_CONF_ERROR;\n            }\n\n            code = ngx_http_script_start_code(cf->pool, &lcf->codes,\n                                              sizeof(uintptr_t));\n            if (code == NULL) {\n                return NGX_CONF_ERROR;\n            }\n\n            *code = ngx_http_script_not_equal_code;\n            return NGX_CONF_OK;\n        }\n\n        if ((len == 1 && p[0] == '~')\n            || (len == 2 && p[0] == '~' && p[1] == '*')\n            || (len == 2 && p[0] == '!' && p[1] == '~')\n            || (len == 3 && p[0] == '!' && p[1] == '~' && p[2] == '*'))\n        {\n            regex = ngx_http_script_start_code(cf->pool, &lcf->codes,\n                                         sizeof(ngx_http_script_regex_code_t));\n            if (regex == NULL) {\n                return NGX_CONF_ERROR;\n            }\n\n            ngx_memzero(regex, sizeof(ngx_http_script_regex_code_t));\n\n            ngx_memzero(&rc, sizeof(ngx_regex_compile_t));\n\n            rc.pattern = value[last];\n            rc.options = (p[len - 1] == '*') ? NGX_REGEX_CASELESS : 0;\n            rc.err.len = NGX_MAX_CONF_ERRSTR;\n            rc.err.data = errstr;\n\n            regex->regex = ngx_http_regex_compile(cf, &rc);\n            if (regex->regex == NULL) {\n                return NGX_CONF_ERROR;\n            }\n\n            regex->code = ngx_http_script_regex_start_code;\n            regex->next = sizeof(ngx_http_script_regex_code_t);\n            regex->test = 1;\n            if (p[0] == '!') {\n                regex->negative_test = 1;\n            }\n            regex->name = value[last];\n\n            return NGX_CONF_OK;\n        }\n\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"unexpected \\\"%V\\\" in condition\", &value[cur]);\n        return NGX_CONF_ERROR;\n\n    } else if ((len == 2 && p[0] == '-')\n               || (len == 3 && p[0] == '!' && p[1] == '-'))\n    {\n        if (cur + 1 != last) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"invalid condition \\\"%V\\\"\", &value[cur]);\n            return NGX_CONF_ERROR;\n        }\n\n        value[last].data[value[last].len] = '\\0';\n        value[last].len++;\n\n        if (ngx_http_rewrite_value(cf, lcf, &value[last]) != NGX_CONF_OK) {\n            return NGX_CONF_ERROR;\n        }\n\n        fop = ngx_http_script_start_code(cf->pool, &lcf->codes,\n                                          sizeof(ngx_http_script_file_code_t));\n        if (fop == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        fop->code = ngx_http_script_file_code;\n\n        if (p[1] == 'f') {\n            fop->op = ngx_http_script_file_plain;\n            return NGX_CONF_OK;\n        }\n\n        if (p[1] == 'd') {\n            fop->op = ngx_http_script_file_dir;\n            return NGX_CONF_OK;\n        }\n\n        if (p[1] == 'e') {\n            fop->op = ngx_http_script_file_exists;\n            return NGX_CONF_OK;\n        }\n\n        if (p[1] == 'x') {\n            fop->op = ngx_http_script_file_exec;\n            return NGX_CONF_OK;\n        }\n\n        if (p[0] == '!') {\n            if (p[2] == 'f') {\n                fop->op = ngx_http_script_file_not_plain;\n                return NGX_CONF_OK;\n            }\n\n            if (p[2] == 'd') {\n                fop->op = ngx_http_script_file_not_dir;\n                return NGX_CONF_OK;\n            }\n\n            if (p[2] == 'e') {\n                fop->op = ngx_http_script_file_not_exists;\n                return NGX_CONF_OK;\n            }\n\n            if (p[2] == 'x') {\n                fop->op = ngx_http_script_file_not_exec;\n                return NGX_CONF_OK;\n            }\n        }\n\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid condition \\\"%V\\\"\", &value[cur]);\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                       \"invalid condition \\\"%V\\\"\", &value[cur]);\n\n    return NGX_CONF_ERROR;\n}\n\n\nstatic char *\nngx_http_rewrite_variable(ngx_conf_t *cf, ngx_http_rewrite_loc_conf_t *lcf,\n    ngx_str_t *value)\n{\n    ngx_int_t                    index;\n    ngx_http_script_var_code_t  *var_code;\n\n    value->len--;\n    value->data++;\n\n    index = ngx_http_get_variable_index(cf, value);\n\n    if (index == NGX_ERROR) {\n        return NGX_CONF_ERROR;\n    }\n\n    var_code = ngx_http_script_start_code(cf->pool, &lcf->codes,\n                                          sizeof(ngx_http_script_var_code_t));\n    if (var_code == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    var_code->code = ngx_http_script_var_code;\n    var_code->index = index;\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_rewrite_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_rewrite_loc_conf_t  *lcf = conf;\n\n    ngx_int_t                            index;\n    ngx_str_t                           *value;\n    ngx_http_variable_t                 *v;\n    ngx_http_script_var_code_t          *vcode;\n    ngx_http_script_var_handler_code_t  *vhcode;\n\n    value = cf->args->elts;\n\n    if (value[1].data[0] != '$') {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid variable name \\\"%V\\\"\", &value[1]);\n        return NGX_CONF_ERROR;\n    }\n\n    value[1].len--;\n    value[1].data++;\n\n    v = ngx_http_add_variable(cf, &value[1],\n                              NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_WEAK);\n    if (v == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    index = ngx_http_get_variable_index(cf, &value[1]);\n    if (index == NGX_ERROR) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (v->get_handler == NULL) {\n        v->get_handler = ngx_http_rewrite_var;\n        v->data = index;\n    }\n\n    if (ngx_http_rewrite_value(cf, lcf, &value[2]) != NGX_CONF_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (v->set_handler) {\n        vhcode = ngx_http_script_start_code(cf->pool, &lcf->codes,\n                                   sizeof(ngx_http_script_var_handler_code_t));\n        if (vhcode == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        vhcode->code = ngx_http_script_var_set_handler_code;\n        vhcode->handler = v->set_handler;\n        vhcode->data = v->data;\n\n        return NGX_CONF_OK;\n    }\n\n    vcode = ngx_http_script_start_code(cf->pool, &lcf->codes,\n                                       sizeof(ngx_http_script_var_code_t));\n    if (vcode == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    vcode->code = ngx_http_script_set_var_code;\n    vcode->index = (uintptr_t) index;\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_rewrite_value(ngx_conf_t *cf, ngx_http_rewrite_loc_conf_t *lcf,\n    ngx_str_t *value)\n{\n    ngx_int_t                              n;\n    ngx_http_script_compile_t              sc;\n    ngx_http_script_value_code_t          *val;\n    ngx_http_script_complex_value_code_t  *complex;\n\n    n = ngx_http_script_variables_count(value);\n\n    if (n == 0) {\n        val = ngx_http_script_start_code(cf->pool, &lcf->codes,\n                                         sizeof(ngx_http_script_value_code_t));\n        if (val == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        n = ngx_atoi(value->data, value->len);\n\n        if (n == NGX_ERROR) {\n            n = 0;\n        }\n\n        val->code = ngx_http_script_value_code;\n        val->value = (uintptr_t) n;\n        val->text_len = (uintptr_t) value->len;\n        val->text_data = (uintptr_t) value->data;\n\n        return NGX_CONF_OK;\n    }\n\n    complex = ngx_http_script_start_code(cf->pool, &lcf->codes,\n                                 sizeof(ngx_http_script_complex_value_code_t));\n    if (complex == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    complex->code = ngx_http_script_complex_value_code;\n    complex->lengths = NULL;\n\n    ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));\n\n    sc.cf = cf;\n    sc.source = value;\n    sc.lengths = &complex->lengths;\n    sc.values = &lcf->codes;\n    sc.variables = n;\n    sc.complete_lengths = 1;\n\n    if (ngx_http_script_compile(&sc) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n"
  },
  {
    "path": "src/http/modules/ngx_http_scgi_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n * Copyright (C) Manlio Perillo (manlio.perillo@gmail.com)\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\ntypedef struct {\n    ngx_array_t                caches;  /* ngx_http_file_cache_t * */\n} ngx_http_scgi_main_conf_t;\n\n\ntypedef struct {\n    ngx_array_t               *flushes;\n    ngx_array_t               *lengths;\n    ngx_array_t               *values;\n    ngx_uint_t                 number;\n    ngx_hash_t                 hash;\n} ngx_http_scgi_params_t;\n\n\ntypedef struct {\n    ngx_http_upstream_conf_t   upstream;\n\n    ngx_http_scgi_params_t     params;\n#if (NGX_HTTP_CACHE)\n    ngx_http_scgi_params_t     params_cache;\n#endif\n    ngx_array_t               *params_source;\n\n    ngx_array_t               *scgi_lengths;\n    ngx_array_t               *scgi_values;\n\n#if (NGX_HTTP_CACHE)\n    ngx_http_complex_value_t   cache_key;\n#endif\n} ngx_http_scgi_loc_conf_t;\n\n\nstatic ngx_int_t ngx_http_scgi_eval(ngx_http_request_t *r,\n    ngx_http_scgi_loc_conf_t *scf);\nstatic ngx_int_t ngx_http_scgi_create_request(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_scgi_reinit_request(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_scgi_process_status_line(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_scgi_process_header(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_scgi_input_filter_init(void *data);\nstatic void ngx_http_scgi_abort_request(ngx_http_request_t *r);\nstatic void ngx_http_scgi_finalize_request(ngx_http_request_t *r, ngx_int_t rc);\n\nstatic void *ngx_http_scgi_create_main_conf(ngx_conf_t *cf);\nstatic void *ngx_http_scgi_create_loc_conf(ngx_conf_t *cf);\nstatic char *ngx_http_scgi_merge_loc_conf(ngx_conf_t *cf, void *parent,\n    void *child);\nstatic ngx_int_t ngx_http_scgi_init_params(ngx_conf_t *cf,\n    ngx_http_scgi_loc_conf_t *conf, ngx_http_scgi_params_t *params,\n    ngx_keyval_t *default_params);\n\nstatic char *ngx_http_scgi_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\nstatic char *ngx_http_scgi_store(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n\n#if (NGX_HTTP_CACHE)\nstatic ngx_int_t ngx_http_scgi_create_key(ngx_http_request_t *r);\nstatic char *ngx_http_scgi_cache(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_http_scgi_cache_key(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n#endif\n\n\nstatic ngx_conf_bitmask_t ngx_http_scgi_next_upstream_masks[] = {\n    { ngx_string(\"error\"), NGX_HTTP_UPSTREAM_FT_ERROR },\n    { ngx_string(\"timeout\"), NGX_HTTP_UPSTREAM_FT_TIMEOUT },\n    { ngx_string(\"invalid_header\"), NGX_HTTP_UPSTREAM_FT_INVALID_HEADER },\n    { ngx_string(\"non_idempotent\"), NGX_HTTP_UPSTREAM_FT_NON_IDEMPOTENT },\n    { ngx_string(\"http_500\"), NGX_HTTP_UPSTREAM_FT_HTTP_500 },\n    { ngx_string(\"http_503\"), NGX_HTTP_UPSTREAM_FT_HTTP_503 },\n    { ngx_string(\"http_403\"), NGX_HTTP_UPSTREAM_FT_HTTP_403 },\n    { ngx_string(\"http_404\"), NGX_HTTP_UPSTREAM_FT_HTTP_404 },\n    { ngx_string(\"http_429\"), NGX_HTTP_UPSTREAM_FT_HTTP_429 },\n    { ngx_string(\"updating\"), NGX_HTTP_UPSTREAM_FT_UPDATING },\n    { ngx_string(\"off\"), NGX_HTTP_UPSTREAM_FT_OFF },\n    { ngx_null_string, 0 }\n};\n\n\nngx_module_t  ngx_http_scgi_module;\n\n\nstatic ngx_command_t ngx_http_scgi_commands[] = {\n\n    { ngx_string(\"scgi_pass\"),\n      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,\n      ngx_http_scgi_pass,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"scgi_store\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_http_scgi_store,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"scgi_store_access\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,\n      ngx_conf_set_access_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_scgi_loc_conf_t, upstream.store_access),\n      NULL },\n\n    { ngx_string(\"scgi_buffering\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_scgi_loc_conf_t, upstream.buffering),\n      NULL },\n\n    { ngx_string(\"scgi_request_buffering\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_scgi_loc_conf_t, upstream.request_buffering),\n      NULL },\n\n    { ngx_string(\"scgi_ignore_client_abort\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_scgi_loc_conf_t, upstream.ignore_client_abort),\n      NULL },\n\n    { ngx_string(\"scgi_bind\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,\n      ngx_http_upstream_bind_set_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_scgi_loc_conf_t, upstream.local),\n      NULL },\n\n    { ngx_string(\"scgi_socket_keepalive\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_scgi_loc_conf_t, upstream.socket_keepalive),\n      NULL },\n\n    { ngx_string(\"scgi_connect_timeout\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_scgi_loc_conf_t, upstream.connect_timeout),\n      NULL },\n\n    { ngx_string(\"scgi_send_timeout\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_scgi_loc_conf_t, upstream.send_timeout),\n      NULL },\n\n    { ngx_string(\"scgi_buffer_size\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_scgi_loc_conf_t, upstream.buffer_size),\n      NULL },\n\n    { ngx_string(\"scgi_pass_request_headers\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_scgi_loc_conf_t, upstream.pass_request_headers),\n      NULL },\n\n    { ngx_string(\"scgi_pass_request_body\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_scgi_loc_conf_t, upstream.pass_request_body),\n      NULL },\n\n    { ngx_string(\"scgi_intercept_errors\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_scgi_loc_conf_t, upstream.intercept_errors),\n      NULL },\n\n    { ngx_string(\"scgi_read_timeout\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_scgi_loc_conf_t, upstream.read_timeout),\n      NULL },\n\n    { ngx_string(\"scgi_buffers\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,\n      ngx_conf_set_bufs_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_scgi_loc_conf_t, upstream.bufs),\n      NULL },\n\n    { ngx_string(\"scgi_busy_buffers_size\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_scgi_loc_conf_t, upstream.busy_buffers_size_conf),\n      NULL },\n\n    { ngx_string(\"scgi_force_ranges\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_scgi_loc_conf_t, upstream.force_ranges),\n      NULL },\n\n    { ngx_string(\"scgi_limit_rate\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_scgi_loc_conf_t, upstream.limit_rate),\n      NULL },\n\n#if (NGX_HTTP_CACHE)\n\n    { ngx_string(\"scgi_cache\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_http_scgi_cache,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"scgi_cache_key\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_http_scgi_cache_key,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"scgi_cache_path\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_2MORE,\n      ngx_http_file_cache_set_slot,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      offsetof(ngx_http_scgi_main_conf_t, caches),\n      &ngx_http_scgi_module },\n\n    { ngx_string(\"scgi_cache_bypass\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,\n      ngx_http_set_predicate_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_scgi_loc_conf_t, upstream.cache_bypass),\n      NULL },\n\n    { ngx_string(\"scgi_no_cache\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,\n      ngx_http_set_predicate_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_scgi_loc_conf_t, upstream.no_cache),\n      NULL },\n\n    { ngx_string(\"scgi_cache_valid\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,\n      ngx_http_file_cache_valid_set_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_scgi_loc_conf_t, upstream.cache_valid),\n      NULL },\n\n    { ngx_string(\"scgi_cache_min_uses\"),\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_scgi_loc_conf_t, upstream.cache_min_uses),\n      NULL },\n\n    { ngx_string(\"scgi_cache_max_range_offset\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_off_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_scgi_loc_conf_t, upstream.cache_max_range_offset),\n      NULL },\n\n    { ngx_string(\"scgi_cache_use_stale\"),\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_scgi_loc_conf_t, upstream.cache_use_stale),\n      &ngx_http_scgi_next_upstream_masks },\n\n    { ngx_string(\"scgi_cache_methods\"),\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_scgi_loc_conf_t, upstream.cache_methods),\n      &ngx_http_upstream_cache_method_mask },\n\n    { ngx_string(\"scgi_cache_lock\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_scgi_loc_conf_t, upstream.cache_lock),\n      NULL },\n\n    { ngx_string(\"scgi_cache_lock_timeout\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_scgi_loc_conf_t, upstream.cache_lock_timeout),\n      NULL },\n\n    { ngx_string(\"scgi_cache_lock_age\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_scgi_loc_conf_t, upstream.cache_lock_age),\n      NULL },\n\n    { ngx_string(\"scgi_cache_revalidate\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_scgi_loc_conf_t, upstream.cache_revalidate),\n      NULL },\n\n    { ngx_string(\"scgi_cache_background_update\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_scgi_loc_conf_t, upstream.cache_background_update),\n      NULL },\n\n#endif\n\n    { ngx_string(\"scgi_temp_path\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1234,\n      ngx_conf_set_path_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_scgi_loc_conf_t, upstream.temp_path),\n      NULL },\n\n    { ngx_string(\"scgi_max_temp_file_size\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_scgi_loc_conf_t, upstream.max_temp_file_size_conf),\n      NULL },\n\n    { ngx_string(\"scgi_temp_file_write_size\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_scgi_loc_conf_t, upstream.temp_file_write_size_conf),\n      NULL },\n\n    { ngx_string(\"scgi_next_upstream\"),\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_scgi_loc_conf_t, upstream.next_upstream),\n      &ngx_http_scgi_next_upstream_masks },\n\n#if (T_UPSTREAM_TRIES)\n    { ngx_string(\"scgi_upstream_tries\"),\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_scgi_loc_conf_t, upstream.next_upstream_tries),\n      NULL },\n#endif\n\n    { ngx_string(\"scgi_next_upstream_tries\"),\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_scgi_loc_conf_t, upstream.next_upstream_tries),\n      NULL },\n\n    { ngx_string(\"scgi_next_upstream_timeout\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_scgi_loc_conf_t, upstream.next_upstream_timeout),\n      NULL },\n\n    { ngx_string(\"scgi_param\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE23,\n      ngx_http_upstream_param_set_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_scgi_loc_conf_t, params_source),\n      NULL },\n\n    { ngx_string(\"scgi_pass_header\"),\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_scgi_loc_conf_t, upstream.pass_headers),\n      NULL },\n\n    { ngx_string(\"scgi_hide_header\"),\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_scgi_loc_conf_t, upstream.hide_headers),\n      NULL },\n\n    { ngx_string(\"scgi_ignore_headers\"),\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_scgi_loc_conf_t, upstream.ignore_headers),\n      &ngx_http_upstream_ignore_headers_masks },\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t ngx_http_scgi_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    NULL,                                  /* postconfiguration */\n\n    ngx_http_scgi_create_main_conf,        /* create main configuration */\n    NULL,                                  /* init main configuration */\n\n    NULL,                                  /* create server configuration */\n    NULL,                                  /* merge server configuration */\n\n    ngx_http_scgi_create_loc_conf,         /* create location configuration */\n    ngx_http_scgi_merge_loc_conf           /* merge location configuration */\n};\n\n\nngx_module_t ngx_http_scgi_module = {\n    NGX_MODULE_V1,\n    &ngx_http_scgi_module_ctx,             /* module context */\n    ngx_http_scgi_commands,                /* 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_str_t ngx_http_scgi_hide_headers[] = {\n    ngx_string(\"Status\"),\n    ngx_string(\"X-Accel-Expires\"),\n    ngx_string(\"X-Accel-Redirect\"),\n    ngx_string(\"X-Accel-Limit-Rate\"),\n    ngx_string(\"X-Accel-Buffering\"),\n    ngx_string(\"X-Accel-Charset\"),\n    ngx_null_string\n};\n\n\n#if (NGX_HTTP_CACHE)\n\nstatic ngx_keyval_t  ngx_http_scgi_cache_headers[] = {\n    { ngx_string(\"HTTP_IF_MODIFIED_SINCE\"),\n      ngx_string(\"$upstream_cache_last_modified\") },\n    { ngx_string(\"HTTP_IF_UNMODIFIED_SINCE\"), ngx_string(\"\") },\n    { ngx_string(\"HTTP_IF_NONE_MATCH\"), ngx_string(\"$upstream_cache_etag\") },\n    { ngx_string(\"HTTP_IF_MATCH\"), ngx_string(\"\") },\n    { ngx_string(\"HTTP_RANGE\"), ngx_string(\"\") },\n    { ngx_string(\"HTTP_IF_RANGE\"), ngx_string(\"\") },\n    { ngx_null_string, ngx_null_string }\n};\n\n#endif\n\n\nstatic ngx_path_init_t ngx_http_scgi_temp_path = {\n    ngx_string(NGX_HTTP_SCGI_TEMP_PATH), { 1, 2, 0 }\n};\n\n\nstatic ngx_int_t\nngx_http_scgi_handler(ngx_http_request_t *r)\n{\n    ngx_int_t                   rc;\n    ngx_http_status_t          *status;\n    ngx_http_upstream_t        *u;\n    ngx_http_scgi_loc_conf_t   *scf;\n#if (NGX_HTTP_CACHE)\n    ngx_http_scgi_main_conf_t  *smcf;\n#endif\n\n    if (ngx_http_upstream_create(r) != NGX_OK) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    status = ngx_pcalloc(r->pool, sizeof(ngx_http_status_t));\n    if (status == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    ngx_http_set_ctx(r, status, ngx_http_scgi_module);\n\n    scf = ngx_http_get_module_loc_conf(r, ngx_http_scgi_module);\n\n    if (scf->scgi_lengths) {\n        if (ngx_http_scgi_eval(r, scf) != NGX_OK) {\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n    }\n\n    u = r->upstream;\n\n    ngx_str_set(&u->schema, \"scgi://\");\n    u->output.tag = (ngx_buf_tag_t) &ngx_http_scgi_module;\n\n    u->conf = &scf->upstream;\n\n#if (NGX_HTTP_CACHE)\n    smcf = ngx_http_get_module_main_conf(r, ngx_http_scgi_module);\n\n    u->caches = &smcf->caches;\n    u->create_key = ngx_http_scgi_create_key;\n#endif\n\n    u->create_request = ngx_http_scgi_create_request;\n    u->reinit_request = ngx_http_scgi_reinit_request;\n    u->process_header = ngx_http_scgi_process_status_line;\n    u->abort_request = ngx_http_scgi_abort_request;\n    u->finalize_request = ngx_http_scgi_finalize_request;\n    r->state = 0;\n\n    u->buffering = scf->upstream.buffering;\n\n    u->pipe = ngx_pcalloc(r->pool, sizeof(ngx_event_pipe_t));\n    if (u->pipe == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    u->pipe->input_filter = ngx_event_pipe_copy_input_filter;\n    u->pipe->input_ctx = r;\n\n    u->input_filter_init = ngx_http_scgi_input_filter_init;\n    u->input_filter = ngx_http_upstream_non_buffered_filter;\n    u->input_filter_ctx = r;\n\n    if (!scf->upstream.request_buffering\n        && scf->upstream.pass_request_body\n        && !r->headers_in.chunked)\n    {\n        r->request_body_no_buffering = 1;\n    }\n\n    rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init);\n\n    if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {\n        return rc;\n    }\n\n    return NGX_DONE;\n}\n\n\nstatic ngx_int_t\nngx_http_scgi_eval(ngx_http_request_t *r, ngx_http_scgi_loc_conf_t * scf)\n{\n    ngx_url_t             url;\n    ngx_http_upstream_t  *u;\n\n    ngx_memzero(&url, sizeof(ngx_url_t));\n\n    if (ngx_http_script_run(r, &url.url, scf->scgi_lengths->elts, 0,\n                            scf->scgi_values->elts)\n        == NULL)\n    {\n        return NGX_ERROR;\n    }\n\n    url.no_resolve = 1;\n\n    if (ngx_parse_url(r->pool, &url) != NGX_OK) {\n        if (url.err) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"%s in upstream \\\"%V\\\"\", url.err, &url.url);\n        }\n\n        return NGX_ERROR;\n    }\n\n    u = r->upstream;\n\n    u->resolved = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_resolved_t));\n    if (u->resolved == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (url.addrs) {\n        u->resolved->sockaddr = url.addrs[0].sockaddr;\n        u->resolved->socklen = url.addrs[0].socklen;\n        u->resolved->name = url.addrs[0].name;\n        u->resolved->naddrs = 1;\n    }\n\n    u->resolved->host = url.host;\n    u->resolved->port = url.port;\n    u->resolved->no_port = url.no_port;\n\n    return NGX_OK;\n}\n\n\n#if (NGX_HTTP_CACHE)\n\nstatic ngx_int_t\nngx_http_scgi_create_key(ngx_http_request_t *r)\n{\n    ngx_str_t                 *key;\n    ngx_http_scgi_loc_conf_t  *scf;\n\n    key = ngx_array_push(&r->cache->keys);\n    if (key == NULL) {\n        return NGX_ERROR;\n    }\n\n    scf = ngx_http_get_module_loc_conf(r, ngx_http_scgi_module);\n\n    if (ngx_http_complex_value(r, &scf->cache_key, key) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n#endif\n\n\nstatic ngx_int_t\nngx_http_scgi_create_request(ngx_http_request_t *r)\n{\n    off_t                         content_length_n;\n    u_char                        ch, sep, *key, *val, *lowcase_key;\n    size_t                        len, key_len, val_len, allocated;\n    ngx_buf_t                    *b;\n    ngx_str_t                     content_length;\n    ngx_uint_t                    i, n, hash, skip_empty, header_params;\n    ngx_chain_t                  *cl, *body;\n    ngx_list_part_t              *part;\n    ngx_table_elt_t              *header, *hn, **ignored;\n    ngx_http_scgi_params_t       *params;\n    ngx_http_script_code_pt       code;\n    ngx_http_script_engine_t      e, le;\n    ngx_http_scgi_loc_conf_t     *scf;\n    ngx_http_script_len_code_pt   lcode;\n    u_char                        buffer[NGX_OFF_T_LEN];\n\n    content_length_n = 0;\n    body = r->upstream->request_bufs;\n\n    while (body) {\n        content_length_n += ngx_buf_size(body->buf);\n        body = body->next;\n    }\n\n    content_length.data = buffer;\n    content_length.len = ngx_sprintf(buffer, \"%O\", content_length_n) - buffer;\n\n    len = sizeof(\"CONTENT_LENGTH\") + content_length.len + 1;\n\n    header_params = 0;\n    ignored = NULL;\n\n    scf = ngx_http_get_module_loc_conf(r, ngx_http_scgi_module);\n\n#if (NGX_HTTP_CACHE)\n    params = r->upstream->cacheable ? &scf->params_cache : &scf->params;\n#else\n    params = &scf->params;\n#endif\n\n    if (params->lengths) {\n        ngx_memzero(&le, sizeof(ngx_http_script_engine_t));\n\n        ngx_http_script_flush_no_cacheable_variables(r, params->flushes);\n        le.flushed = 1;\n\n        le.ip = params->lengths->elts;\n        le.request = r;\n\n        while (*(uintptr_t *) le.ip) {\n\n            lcode = *(ngx_http_script_len_code_pt *) le.ip;\n            key_len = lcode(&le);\n\n            lcode = *(ngx_http_script_len_code_pt *) le.ip;\n            skip_empty = lcode(&le);\n\n            for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) {\n                lcode = *(ngx_http_script_len_code_pt *) le.ip;\n            }\n            le.ip += sizeof(uintptr_t);\n\n            if (skip_empty && val_len == 0) {\n                continue;\n            }\n\n            len += key_len + val_len + 1;\n        }\n    }\n\n    if (scf->upstream.pass_request_headers) {\n\n        allocated = 0;\n        lowcase_key = NULL;\n\n        if (ngx_http_link_multi_headers(r) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        if (params->number || r->headers_in.multi) {\n            n = 0;\n            part = &r->headers_in.headers.part;\n\n            while (part) {\n                n += part->nelts;\n                part = part->next;\n            }\n\n            ignored = ngx_palloc(r->pool, n * sizeof(void *));\n            if (ignored == NULL) {\n                return NGX_ERROR;\n            }\n        }\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            for (n = 0; n < header_params; n++) {\n                if (&header[i] == ignored[n]) {\n                    goto next_length;\n                }\n            }\n\n            if (params->number) {\n                if (allocated < header[i].key.len) {\n                    allocated = header[i].key.len + 16;\n                    lowcase_key = ngx_pnalloc(r->pool, allocated);\n                    if (lowcase_key == NULL) {\n                        return NGX_ERROR;\n                    }\n                }\n\n                hash = 0;\n\n                for (n = 0; n < header[i].key.len; n++) {\n                    ch = header[i].key.data[n];\n\n                    if (ch >= 'A' && ch <= 'Z') {\n                        ch |= 0x20;\n\n                    } else if (ch == '-') {\n                        ch = '_';\n                    }\n\n                    hash = ngx_hash(hash, ch);\n                    lowcase_key[n] = ch;\n                }\n\n                if (ngx_hash_find(&params->hash, hash, lowcase_key, n)) {\n                    ignored[header_params++] = &header[i];\n                    continue;\n                }\n            }\n\n            len += sizeof(\"HTTP_\") - 1 + header[i].key.len + 1\n                + header[i].value.len + 1;\n\n            for (hn = header[i].next; hn; hn = hn->next) {\n                len += hn->value.len + 2;\n                ignored[header_params++] = hn;\n            }\n\n        next_length:\n\n            continue;\n        }\n    }\n\n    /* netstring: \"length:\" + packet + \",\" */\n\n    b = ngx_create_temp_buf(r->pool, NGX_SIZE_T_LEN + 1 + len + 1);\n    if (b == NULL) {\n        return NGX_ERROR;\n    }\n\n    cl = ngx_alloc_chain_link(r->pool);\n    if (cl == NULL) {\n        return NGX_ERROR;\n    }\n\n    cl->buf = b;\n\n    b->last = ngx_sprintf(b->last, \"%ui:CONTENT_LENGTH%Z%V%Z\",\n                          len, &content_length);\n\n    if (params->lengths) {\n        ngx_memzero(&e, sizeof(ngx_http_script_engine_t));\n\n        e.ip = params->values->elts;\n        e.pos = b->last;\n        e.request = r;\n        e.flushed = 1;\n\n        le.ip = params->lengths->elts;\n\n        while (*(uintptr_t *) le.ip) {\n\n            lcode = *(ngx_http_script_len_code_pt *) le.ip;\n            lcode(&le); /* key length */\n\n            lcode = *(ngx_http_script_len_code_pt *) le.ip;\n            skip_empty = lcode(&le);\n\n            for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) {\n                lcode = *(ngx_http_script_len_code_pt *) le.ip;\n            }\n            le.ip += sizeof(uintptr_t);\n\n            if (skip_empty && val_len == 0) {\n                e.skip = 1;\n\n                while (*(uintptr_t *) e.ip) {\n                    code = *(ngx_http_script_code_pt *) e.ip;\n                    code((ngx_http_script_engine_t *) &e);\n                }\n                e.ip += sizeof(uintptr_t);\n\n                e.skip = 0;\n\n                continue;\n            }\n\n#if (NGX_DEBUG)\n            key = e.pos;\n#endif\n            code = *(ngx_http_script_code_pt *) e.ip;\n            code((ngx_http_script_engine_t *) &e);\n\n#if (NGX_DEBUG)\n            val = e.pos;\n#endif\n            while (*(uintptr_t *) e.ip) {\n                code = *(ngx_http_script_code_pt *) e.ip;\n                code((ngx_http_script_engine_t *) &e);\n            }\n            *e.pos++ = '\\0';\n            e.ip += sizeof(uintptr_t);\n\n            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"scgi param: \\\"%s: %s\\\"\", key, val);\n        }\n\n        b->last = e.pos;\n    }\n\n    if (scf->upstream.pass_request_headers) {\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            for (n = 0; n < header_params; n++) {\n                if (&header[i] == ignored[n]) {\n                    goto next_value;\n                }\n            }\n\n            key = b->last;\n            b->last = ngx_cpymem(key, \"HTTP_\", sizeof(\"HTTP_\") - 1);\n\n            for (n = 0; n < header[i].key.len; n++) {\n                ch = header[i].key.data[n];\n\n                if (ch >= 'a' && ch <= 'z') {\n                    ch &= ~0x20;\n\n                } else if (ch == '-') {\n                    ch = '_';\n                }\n\n                *b->last++ = ch;\n            }\n\n            *b->last++ = (u_char) 0;\n\n            val = b->last;\n            b->last = ngx_copy(val, header[i].value.data, header[i].value.len);\n\n            if (header[i].next) {\n\n                if (header[i].key.len == sizeof(\"Cookie\") - 1\n                    && ngx_strncasecmp(header[i].key.data, (u_char *) \"Cookie\",\n                                       sizeof(\"Cookie\") - 1)\n                       == 0)\n                {\n                    sep = ';';\n\n                } else {\n                    sep = ',';\n                }\n\n                for (hn = header[i].next; hn; hn = hn->next) {\n                    *b->last++ = sep;\n                    *b->last++ = ' ';\n                    b->last = ngx_copy(b->last, hn->value.data, hn->value.len);\n                }\n            }\n\n            *b->last++ = (u_char) 0;\n\n            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"scgi param: \\\"%s: %s\\\"\", key, val);\n\n        next_value:\n\n            continue;\n        }\n    }\n\n    *b->last++ = (u_char) ',';\n\n    if (r->request_body_no_buffering) {\n        r->upstream->request_bufs = cl;\n\n    } else if (scf->upstream.pass_request_body) {\n        body = r->upstream->request_bufs;\n        r->upstream->request_bufs = cl;\n\n        while (body) {\n            b = ngx_alloc_buf(r->pool);\n            if (b == NULL) {\n                return NGX_ERROR;\n            }\n\n            ngx_memcpy(b, body->buf, sizeof(ngx_buf_t));\n\n            cl->next = ngx_alloc_chain_link(r->pool);\n            if (cl->next == NULL) {\n                return NGX_ERROR;\n            }\n\n            cl = cl->next;\n            cl->buf = b;\n\n            body = body->next;\n        }\n\n    } else {\n        r->upstream->request_bufs = cl;\n    }\n\n    cl->next = NULL;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_scgi_reinit_request(ngx_http_request_t *r)\n{\n    ngx_http_status_t  *status;\n\n    status = ngx_http_get_module_ctx(r, ngx_http_scgi_module);\n\n    if (status == NULL) {\n        return NGX_OK;\n    }\n\n    status->code = 0;\n    status->count = 0;\n    status->start = NULL;\n    status->end = NULL;\n\n    r->upstream->process_header = ngx_http_scgi_process_status_line;\n    r->state = 0;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_scgi_process_status_line(ngx_http_request_t *r)\n{\n    size_t                len;\n    ngx_int_t             rc;\n    ngx_http_status_t    *status;\n    ngx_http_upstream_t  *u;\n\n    status = ngx_http_get_module_ctx(r, ngx_http_scgi_module);\n\n    if (status == NULL) {\n        return NGX_ERROR;\n    }\n\n    u = r->upstream;\n\n    rc = ngx_http_parse_status_line(r, &u->buffer, status);\n\n    if (rc == NGX_AGAIN) {\n        return rc;\n    }\n\n    if (rc == NGX_ERROR) {\n        u->process_header = ngx_http_scgi_process_header;\n        return ngx_http_scgi_process_header(r);\n    }\n\n    if (u->state && u->state->status == 0) {\n        u->state->status = status->code;\n    }\n\n    u->headers_in.status_n = status->code;\n\n    len = status->end - status->start;\n    u->headers_in.status_line.len = len;\n\n    u->headers_in.status_line.data = ngx_pnalloc(r->pool, len);\n    if (u->headers_in.status_line.data == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_memcpy(u->headers_in.status_line.data, status->start, len);\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http scgi status %ui \\\"%V\\\"\",\n                   u->headers_in.status_n, &u->headers_in.status_line);\n\n    u->process_header = ngx_http_scgi_process_header;\n\n    return ngx_http_scgi_process_header(r);\n}\n\n\nstatic ngx_int_t\nngx_http_scgi_process_header(ngx_http_request_t *r)\n{\n    ngx_str_t                      *status_line;\n    ngx_int_t                       rc, status;\n    ngx_table_elt_t                *h;\n    ngx_http_upstream_t            *u;\n    ngx_http_upstream_header_t     *hh;\n    ngx_http_upstream_main_conf_t  *umcf;\n\n    umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);\n\n    for ( ;; ) {\n\n        rc = ngx_http_parse_header_line(r, &r->upstream->buffer, 1);\n\n        if (rc == NGX_OK) {\n\n            /* a header line has been parsed successfully */\n\n            h = ngx_list_push(&r->upstream->headers_in.headers);\n            if (h == NULL) {\n                return NGX_ERROR;\n            }\n\n            h->hash = r->header_hash;\n\n            h->key.len = r->header_name_end - r->header_name_start;\n            h->value.len = r->header_end - r->header_start;\n\n            h->key.data = ngx_pnalloc(r->pool,\n                                      h->key.len + 1 + h->value.len + 1\n                                      + h->key.len);\n            if (h->key.data == NULL) {\n                h->hash = 0;\n                return NGX_ERROR;\n            }\n\n            h->value.data = h->key.data + h->key.len + 1;\n            h->lowcase_key = h->key.data + h->key.len + 1 + h->value.len + 1;\n\n            ngx_memcpy(h->key.data, r->header_name_start, h->key.len);\n            h->key.data[h->key.len] = '\\0';\n            ngx_memcpy(h->value.data, r->header_start, h->value.len);\n            h->value.data[h->value.len] = '\\0';\n\n            if (h->key.len == r->lowcase_index) {\n                ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len);\n\n            } else {\n                ngx_strlow(h->lowcase_key, h->key.data, h->key.len);\n            }\n\n            hh = ngx_hash_find(&umcf->headers_in_hash, h->hash,\n                               h->lowcase_key, h->key.len);\n\n            if (hh) {\n                rc = hh->handler(r, h, hh->offset);\n\n                if (rc != NGX_OK) {\n                    return rc;\n                }\n            }\n\n            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"http scgi header: \\\"%V: %V\\\"\", &h->key, &h->value);\n\n            continue;\n        }\n\n        if (rc == NGX_HTTP_PARSE_HEADER_DONE) {\n\n            /* a whole header has been parsed successfully */\n\n            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"http scgi header done\");\n\n            u = r->upstream;\n\n            if (u->headers_in.status_n) {\n                goto done;\n            }\n\n            if (u->headers_in.status) {\n                status_line = &u->headers_in.status->value;\n\n                status = ngx_atoi(status_line->data, 3);\n                if (status == NGX_ERROR) {\n                    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                                  \"upstream sent invalid status \\\"%V\\\"\",\n                                  status_line);\n                    return NGX_HTTP_UPSTREAM_INVALID_HEADER;\n                }\n\n                u->headers_in.status_n = status;\n                u->headers_in.status_line = *status_line;\n\n            } else if (u->headers_in.location) {\n                u->headers_in.status_n = 302;\n                ngx_str_set(&u->headers_in.status_line,\n                            \"302 Moved Temporarily\");\n\n            } else {\n                u->headers_in.status_n = 200;\n                ngx_str_set(&u->headers_in.status_line, \"200 OK\");\n            }\n\n            if (u->state && u->state->status == 0) {\n                u->state->status = u->headers_in.status_n;\n            }\n\n        done:\n\n            if (u->headers_in.status_n == NGX_HTTP_SWITCHING_PROTOCOLS\n                && r->headers_in.upgrade)\n            {\n                u->upgrade = 1;\n            }\n\n            return NGX_OK;\n        }\n\n        if (rc == NGX_AGAIN) {\n            return NGX_AGAIN;\n        }\n\n        /* rc == NGX_HTTP_PARSE_INVALID_HEADER */\n\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"upstream sent invalid header: \\\"%*s\\\\x%02xd...\\\"\",\n                      r->header_end - r->header_name_start,\n                      r->header_name_start, *r->header_end);\n\n        return NGX_HTTP_UPSTREAM_INVALID_HEADER;\n    }\n}\n\n\nstatic ngx_int_t\nngx_http_scgi_input_filter_init(void *data)\n{\n    ngx_http_request_t   *r = data;\n    ngx_http_upstream_t  *u;\n\n    u = r->upstream;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http scgi filter init s:%ui l:%O\",\n                   u->headers_in.status_n, u->headers_in.content_length_n);\n\n    if (u->headers_in.status_n == NGX_HTTP_NO_CONTENT\n        || u->headers_in.status_n == NGX_HTTP_NOT_MODIFIED)\n    {\n        u->pipe->length = 0;\n        u->length = 0;\n\n    } else if (r->method == NGX_HTTP_HEAD) {\n        u->pipe->length = -1;\n        u->length = -1;\n\n    } else {\n        u->pipe->length = u->headers_in.content_length_n;\n        u->length = u->headers_in.content_length_n;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_scgi_abort_request(ngx_http_request_t *r)\n{\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"abort http scgi request\");\n\n    return;\n}\n\n\nstatic void\nngx_http_scgi_finalize_request(ngx_http_request_t *r, ngx_int_t rc)\n{\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"finalize http scgi request\");\n\n    return;\n}\n\n\nstatic void *\nngx_http_scgi_create_main_conf(ngx_conf_t *cf)\n{\n    ngx_http_scgi_main_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_scgi_main_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n#if (NGX_HTTP_CACHE)\n    if (ngx_array_init(&conf->caches, cf->pool, 4,\n                       sizeof(ngx_http_file_cache_t *))\n        != NGX_OK)\n    {\n        return NULL;\n    }\n#endif\n\n    return conf;\n}\n\n\nstatic void *\nngx_http_scgi_create_loc_conf(ngx_conf_t *cf)\n{\n    ngx_http_scgi_loc_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_scgi_loc_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    conf->upstream.store = NGX_CONF_UNSET;\n    conf->upstream.store_access = NGX_CONF_UNSET_UINT;\n    conf->upstream.next_upstream_tries = NGX_CONF_UNSET_UINT;\n    conf->upstream.buffering = NGX_CONF_UNSET;\n    conf->upstream.request_buffering = NGX_CONF_UNSET;\n    conf->upstream.ignore_client_abort = NGX_CONF_UNSET;\n    conf->upstream.force_ranges = NGX_CONF_UNSET;\n\n    conf->upstream.local = NGX_CONF_UNSET_PTR;\n    conf->upstream.socket_keepalive = NGX_CONF_UNSET;\n\n    conf->upstream.connect_timeout = NGX_CONF_UNSET_MSEC;\n    conf->upstream.send_timeout = NGX_CONF_UNSET_MSEC;\n    conf->upstream.read_timeout = NGX_CONF_UNSET_MSEC;\n    conf->upstream.next_upstream_timeout = NGX_CONF_UNSET_MSEC;\n\n    conf->upstream.send_lowat = NGX_CONF_UNSET_SIZE;\n    conf->upstream.buffer_size = NGX_CONF_UNSET_SIZE;\n    conf->upstream.limit_rate = NGX_CONF_UNSET_SIZE;\n\n    conf->upstream.busy_buffers_size_conf = NGX_CONF_UNSET_SIZE;\n    conf->upstream.max_temp_file_size_conf = NGX_CONF_UNSET_SIZE;\n    conf->upstream.temp_file_write_size_conf = NGX_CONF_UNSET_SIZE;\n\n    conf->upstream.pass_request_headers = NGX_CONF_UNSET;\n    conf->upstream.pass_request_body = NGX_CONF_UNSET;\n\n#if (NGX_HTTP_CACHE)\n    conf->upstream.cache = NGX_CONF_UNSET;\n    conf->upstream.cache_min_uses = NGX_CONF_UNSET_UINT;\n    conf->upstream.cache_max_range_offset = NGX_CONF_UNSET;\n    conf->upstream.cache_bypass = NGX_CONF_UNSET_PTR;\n    conf->upstream.no_cache = NGX_CONF_UNSET_PTR;\n    conf->upstream.cache_valid = NGX_CONF_UNSET_PTR;\n    conf->upstream.cache_lock = NGX_CONF_UNSET;\n    conf->upstream.cache_lock_timeout = NGX_CONF_UNSET_MSEC;\n    conf->upstream.cache_lock_age = NGX_CONF_UNSET_MSEC;\n    conf->upstream.cache_revalidate = NGX_CONF_UNSET;\n    conf->upstream.cache_background_update = NGX_CONF_UNSET;\n#endif\n\n    conf->upstream.hide_headers = NGX_CONF_UNSET_PTR;\n    conf->upstream.pass_headers = NGX_CONF_UNSET_PTR;\n\n    conf->upstream.intercept_errors = NGX_CONF_UNSET;\n\n    /* \"scgi_cyclic_temp_file\" is disabled */\n    conf->upstream.cyclic_temp_file = 0;\n\n    conf->upstream.change_buffering = 1;\n\n    ngx_str_set(&conf->upstream.module, \"scgi\");\n\n    return conf;\n}\n\n\nstatic char *\nngx_http_scgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_http_scgi_loc_conf_t *prev = parent;\n    ngx_http_scgi_loc_conf_t *conf = child;\n\n    size_t                        size;\n    ngx_int_t                     rc;\n    ngx_hash_init_t               hash;\n    ngx_http_core_loc_conf_t     *clcf;\n\n#if (NGX_HTTP_CACHE)\n\n    if (conf->upstream.store > 0) {\n        conf->upstream.cache = 0;\n    }\n\n    if (conf->upstream.cache > 0) {\n        conf->upstream.store = 0;\n    }\n\n#endif\n\n    if (conf->upstream.store == NGX_CONF_UNSET) {\n        ngx_conf_merge_value(conf->upstream.store, prev->upstream.store, 0);\n\n        conf->upstream.store_lengths = prev->upstream.store_lengths;\n        conf->upstream.store_values = prev->upstream.store_values;\n    }\n\n    ngx_conf_merge_uint_value(conf->upstream.store_access,\n                              prev->upstream.store_access, 0600);\n\n    ngx_conf_merge_uint_value(conf->upstream.next_upstream_tries,\n                              prev->upstream.next_upstream_tries, 0);\n\n    ngx_conf_merge_value(conf->upstream.buffering,\n                              prev->upstream.buffering, 1);\n\n    ngx_conf_merge_value(conf->upstream.request_buffering,\n                              prev->upstream.request_buffering, 1);\n\n    ngx_conf_merge_value(conf->upstream.ignore_client_abort,\n                              prev->upstream.ignore_client_abort, 0);\n\n    ngx_conf_merge_value(conf->upstream.force_ranges,\n                              prev->upstream.force_ranges, 0);\n\n    ngx_conf_merge_ptr_value(conf->upstream.local,\n                              prev->upstream.local, NULL);\n\n    ngx_conf_merge_value(conf->upstream.socket_keepalive,\n                              prev->upstream.socket_keepalive, 0);\n\n    ngx_conf_merge_msec_value(conf->upstream.connect_timeout,\n                              prev->upstream.connect_timeout, 60000);\n\n    ngx_conf_merge_msec_value(conf->upstream.send_timeout,\n                              prev->upstream.send_timeout, 60000);\n\n    ngx_conf_merge_msec_value(conf->upstream.read_timeout,\n                              prev->upstream.read_timeout, 60000);\n\n    ngx_conf_merge_msec_value(conf->upstream.next_upstream_timeout,\n                              prev->upstream.next_upstream_timeout, 0);\n\n    ngx_conf_merge_size_value(conf->upstream.send_lowat,\n                              prev->upstream.send_lowat, 0);\n\n    ngx_conf_merge_size_value(conf->upstream.buffer_size,\n                              prev->upstream.buffer_size,\n                              (size_t) ngx_pagesize);\n\n    ngx_conf_merge_size_value(conf->upstream.limit_rate,\n                              prev->upstream.limit_rate, 0);\n\n\n    ngx_conf_merge_bufs_value(conf->upstream.bufs, prev->upstream.bufs,\n                              8, ngx_pagesize);\n\n    if (conf->upstream.bufs.num < 2) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"there must be at least 2 \\\"scgi_buffers\\\"\");\n        return NGX_CONF_ERROR;\n    }\n\n\n    size = conf->upstream.buffer_size;\n    if (size < conf->upstream.bufs.size) {\n        size = conf->upstream.bufs.size;\n    }\n\n\n    ngx_conf_merge_size_value(conf->upstream.busy_buffers_size_conf,\n                              prev->upstream.busy_buffers_size_conf,\n                              NGX_CONF_UNSET_SIZE);\n\n    if (conf->upstream.busy_buffers_size_conf == NGX_CONF_UNSET_SIZE) {\n        conf->upstream.busy_buffers_size = 2 * size;\n    } else {\n        conf->upstream.busy_buffers_size =\n            conf->upstream.busy_buffers_size_conf;\n    }\n\n    if (conf->upstream.busy_buffers_size < size) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n            \"\\\"scgi_busy_buffers_size\\\" must be equal to or greater \"\n            \"than the maximum of the value of \\\"scgi_buffer_size\\\" and \"\n            \"one of the \\\"scgi_buffers\\\"\");\n\n        return NGX_CONF_ERROR;\n    }\n\n    if (conf->upstream.busy_buffers_size\n        > (conf->upstream.bufs.num - 1) * conf->upstream.bufs.size)\n    {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n            \"\\\"scgi_busy_buffers_size\\\" must be less than \"\n            \"the size of all \\\"scgi_buffers\\\" minus one buffer\");\n\n        return NGX_CONF_ERROR;\n    }\n\n\n    ngx_conf_merge_size_value(conf->upstream.temp_file_write_size_conf,\n                              prev->upstream.temp_file_write_size_conf,\n                              NGX_CONF_UNSET_SIZE);\n\n    if (conf->upstream.temp_file_write_size_conf == NGX_CONF_UNSET_SIZE) {\n        conf->upstream.temp_file_write_size = 2 * size;\n    } else {\n        conf->upstream.temp_file_write_size =\n            conf->upstream.temp_file_write_size_conf;\n    }\n\n    if (conf->upstream.temp_file_write_size < size) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n            \"\\\"scgi_temp_file_write_size\\\" must be equal to or greater than \"\n            \"the maximum of the value of \\\"scgi_buffer_size\\\" and \"\n            \"one of the \\\"scgi_buffers\\\"\");\n\n        return NGX_CONF_ERROR;\n    }\n\n\n    ngx_conf_merge_size_value(conf->upstream.max_temp_file_size_conf,\n                              prev->upstream.max_temp_file_size_conf,\n                              NGX_CONF_UNSET_SIZE);\n\n    if (conf->upstream.max_temp_file_size_conf == NGX_CONF_UNSET_SIZE) {\n        conf->upstream.max_temp_file_size = 1024 * 1024 * 1024;\n    } else {\n        conf->upstream.max_temp_file_size =\n            conf->upstream.max_temp_file_size_conf;\n    }\n\n    if (conf->upstream.max_temp_file_size != 0\n        && conf->upstream.max_temp_file_size < size)\n    {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n            \"\\\"scgi_max_temp_file_size\\\" must be equal to zero to disable \"\n            \"temporary files usage or must be equal to or greater than \"\n            \"the maximum of the value of \\\"scgi_buffer_size\\\" and \"\n            \"one of the \\\"scgi_buffers\\\"\");\n\n        return NGX_CONF_ERROR;\n    }\n\n\n    ngx_conf_merge_bitmask_value(conf->upstream.ignore_headers,\n                                 prev->upstream.ignore_headers,\n                                 NGX_CONF_BITMASK_SET);\n\n\n    ngx_conf_merge_bitmask_value(conf->upstream.next_upstream,\n                                 prev->upstream.next_upstream,\n                                 (NGX_CONF_BITMASK_SET\n                                  |NGX_HTTP_UPSTREAM_FT_ERROR\n                                  |NGX_HTTP_UPSTREAM_FT_TIMEOUT));\n\n    if (conf->upstream.next_upstream & NGX_HTTP_UPSTREAM_FT_OFF) {\n        conf->upstream.next_upstream = NGX_CONF_BITMASK_SET\n                                       |NGX_HTTP_UPSTREAM_FT_OFF;\n    }\n\n    if (ngx_conf_merge_path_value(cf, &conf->upstream.temp_path,\n                                  prev->upstream.temp_path,\n                                  &ngx_http_scgi_temp_path)\n        != NGX_OK)\n    {\n        return NGX_CONF_ERROR;\n    }\n\n#if (NGX_HTTP_CACHE)\n\n    if (conf->upstream.cache == NGX_CONF_UNSET) {\n        ngx_conf_merge_value(conf->upstream.cache,\n                              prev->upstream.cache, 0);\n\n        conf->upstream.cache_zone = prev->upstream.cache_zone;\n        conf->upstream.cache_value = prev->upstream.cache_value;\n    }\n\n    if (conf->upstream.cache_zone && conf->upstream.cache_zone->data == NULL) {\n        ngx_shm_zone_t  *shm_zone;\n\n        shm_zone = conf->upstream.cache_zone;\n\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"\\\"scgi_cache\\\" zone \\\"%V\\\" is unknown\",\n                           &shm_zone->shm.name);\n\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_conf_merge_uint_value(conf->upstream.cache_min_uses,\n                              prev->upstream.cache_min_uses, 1);\n\n    ngx_conf_merge_off_value(conf->upstream.cache_max_range_offset,\n                              prev->upstream.cache_max_range_offset,\n                              NGX_MAX_OFF_T_VALUE);\n\n    ngx_conf_merge_bitmask_value(conf->upstream.cache_use_stale,\n                              prev->upstream.cache_use_stale,\n                              (NGX_CONF_BITMASK_SET\n                               |NGX_HTTP_UPSTREAM_FT_OFF));\n\n    if (conf->upstream.cache_use_stale & NGX_HTTP_UPSTREAM_FT_OFF) {\n        conf->upstream.cache_use_stale = NGX_CONF_BITMASK_SET\n                                         |NGX_HTTP_UPSTREAM_FT_OFF;\n    }\n\n    if (conf->upstream.cache_use_stale & NGX_HTTP_UPSTREAM_FT_ERROR) {\n        conf->upstream.cache_use_stale |= NGX_HTTP_UPSTREAM_FT_NOLIVE;\n    }\n\n    if (conf->upstream.cache_methods == 0) {\n        conf->upstream.cache_methods = prev->upstream.cache_methods;\n    }\n\n    conf->upstream.cache_methods |= NGX_HTTP_GET|NGX_HTTP_HEAD;\n\n    ngx_conf_merge_ptr_value(conf->upstream.cache_bypass,\n                             prev->upstream.cache_bypass, NULL);\n\n    ngx_conf_merge_ptr_value(conf->upstream.no_cache,\n                             prev->upstream.no_cache, NULL);\n\n    ngx_conf_merge_ptr_value(conf->upstream.cache_valid,\n                             prev->upstream.cache_valid, NULL);\n\n    if (conf->cache_key.value.data == NULL) {\n        conf->cache_key = prev->cache_key;\n    }\n\n    if (conf->upstream.cache && conf->cache_key.value.data == NULL) {\n        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                           \"no \\\"scgi_cache_key\\\" for \\\"scgi_cache\\\"\");\n    }\n\n    ngx_conf_merge_value(conf->upstream.cache_lock,\n                              prev->upstream.cache_lock, 0);\n\n    ngx_conf_merge_msec_value(conf->upstream.cache_lock_timeout,\n                              prev->upstream.cache_lock_timeout, 5000);\n\n    ngx_conf_merge_msec_value(conf->upstream.cache_lock_age,\n                              prev->upstream.cache_lock_age, 5000);\n\n    ngx_conf_merge_value(conf->upstream.cache_revalidate,\n                              prev->upstream.cache_revalidate, 0);\n\n    ngx_conf_merge_value(conf->upstream.cache_background_update,\n                              prev->upstream.cache_background_update, 0);\n\n#endif\n\n    ngx_conf_merge_value(conf->upstream.pass_request_headers,\n                         prev->upstream.pass_request_headers, 1);\n    ngx_conf_merge_value(conf->upstream.pass_request_body,\n                         prev->upstream.pass_request_body, 1);\n\n    ngx_conf_merge_value(conf->upstream.intercept_errors,\n                         prev->upstream.intercept_errors, 0);\n\n    hash.max_size = 512;\n    hash.bucket_size = ngx_align(64, ngx_cacheline_size);\n    hash.name = \"scgi_hide_headers_hash\";\n\n    if (ngx_http_upstream_hide_headers_hash(cf, &conf->upstream,\n            &prev->upstream, ngx_http_scgi_hide_headers, &hash)\n        != NGX_OK)\n    {\n        return NGX_CONF_ERROR;\n    }\n\n    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);\n\n    if (clcf->noname\n        && conf->upstream.upstream == NULL && conf->scgi_lengths == NULL)\n    {\n        conf->upstream.upstream = prev->upstream.upstream;\n        conf->scgi_lengths = prev->scgi_lengths;\n        conf->scgi_values = prev->scgi_values;\n    }\n\n    if (clcf->lmt_excpt && clcf->handler == NULL\n        && (conf->upstream.upstream || conf->scgi_lengths))\n    {\n        clcf->handler = ngx_http_scgi_handler;\n    }\n\n    if (conf->params_source == NULL) {\n        conf->params = prev->params;\n#if (NGX_HTTP_CACHE)\n        conf->params_cache = prev->params_cache;\n#endif\n        conf->params_source = prev->params_source;\n    }\n\n    rc = ngx_http_scgi_init_params(cf, conf, &conf->params, NULL);\n    if (rc != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n#if (NGX_HTTP_CACHE)\n\n    if (conf->upstream.cache) {\n        rc = ngx_http_scgi_init_params(cf, conf, &conf->params_cache,\n                                       ngx_http_scgi_cache_headers);\n        if (rc != NGX_OK) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n#endif\n\n    /*\n     * special handling to preserve conf->params in the \"http\" section\n     * to inherit it to all servers\n     */\n\n    if (prev->params.hash.buckets == NULL\n        && conf->params_source == prev->params_source)\n    {\n        prev->params = conf->params;\n#if (NGX_HTTP_CACHE)\n        prev->params_cache = conf->params_cache;\n#endif\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_scgi_init_params(ngx_conf_t *cf, ngx_http_scgi_loc_conf_t *conf,\n    ngx_http_scgi_params_t *params, ngx_keyval_t *default_params)\n{\n    u_char                       *p;\n    size_t                        size;\n    uintptr_t                    *code;\n    ngx_uint_t                    i, nsrc;\n    ngx_array_t                   headers_names, params_merged;\n    ngx_keyval_t                 *h;\n    ngx_hash_key_t               *hk;\n    ngx_hash_init_t               hash;\n    ngx_http_upstream_param_t    *src, *s;\n    ngx_http_script_compile_t     sc;\n    ngx_http_script_copy_code_t  *copy;\n\n    if (params->hash.buckets) {\n        return NGX_OK;\n    }\n\n    if (conf->params_source == NULL && default_params == NULL) {\n        params->hash.buckets = (void *) 1;\n        return NGX_OK;\n    }\n\n    params->lengths = ngx_array_create(cf->pool, 64, 1);\n    if (params->lengths == NULL) {\n        return NGX_ERROR;\n    }\n\n    params->values = ngx_array_create(cf->pool, 512, 1);\n    if (params->values == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (ngx_array_init(&headers_names, cf->temp_pool, 4, sizeof(ngx_hash_key_t))\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    if (conf->params_source) {\n        src = conf->params_source->elts;\n        nsrc = conf->params_source->nelts;\n\n    } else {\n        src = NULL;\n        nsrc = 0;\n    }\n\n    if (default_params) {\n        if (ngx_array_init(&params_merged, cf->temp_pool, 4,\n                           sizeof(ngx_http_upstream_param_t))\n            != NGX_OK)\n        {\n            return NGX_ERROR;\n        }\n\n        for (i = 0; i < nsrc; i++) {\n\n            s = ngx_array_push(&params_merged);\n            if (s == NULL) {\n                return NGX_ERROR;\n            }\n\n            *s = src[i];\n        }\n\n        h = default_params;\n\n        while (h->key.len) {\n\n            src = params_merged.elts;\n            nsrc = params_merged.nelts;\n\n            for (i = 0; i < nsrc; i++) {\n                if (ngx_strcasecmp(h->key.data, src[i].key.data) == 0) {\n                    goto next;\n                }\n            }\n\n            s = ngx_array_push(&params_merged);\n            if (s == NULL) {\n                return NGX_ERROR;\n            }\n\n            s->key = h->key;\n            s->value = h->value;\n            s->skip_empty = 1;\n\n        next:\n\n            h++;\n        }\n\n        src = params_merged.elts;\n        nsrc = params_merged.nelts;\n    }\n\n    for (i = 0; i < nsrc; i++) {\n\n        if (src[i].key.len > sizeof(\"HTTP_\") - 1\n            && ngx_strncmp(src[i].key.data, \"HTTP_\", sizeof(\"HTTP_\") - 1) == 0)\n        {\n            hk = ngx_array_push(&headers_names);\n            if (hk == NULL) {\n                return NGX_ERROR;\n            }\n\n            hk->key.len = src[i].key.len - 5;\n            hk->key.data = src[i].key.data + 5;\n            hk->key_hash = ngx_hash_key_lc(hk->key.data, hk->key.len);\n            hk->value = (void *) 1;\n\n            if (src[i].value.len == 0) {\n                continue;\n            }\n        }\n\n        copy = ngx_array_push_n(params->lengths,\n                                sizeof(ngx_http_script_copy_code_t));\n        if (copy == NULL) {\n            return NGX_ERROR;\n        }\n\n        copy->code = (ngx_http_script_code_pt) (void *)\n                                                 ngx_http_script_copy_len_code;\n        copy->len = src[i].key.len + 1;\n\n        copy = ngx_array_push_n(params->lengths,\n                                sizeof(ngx_http_script_copy_code_t));\n        if (copy == NULL) {\n            return NGX_ERROR;\n        }\n\n        copy->code = (ngx_http_script_code_pt) (void *)\n                                                 ngx_http_script_copy_len_code;\n        copy->len = src[i].skip_empty;\n\n\n        size = (sizeof(ngx_http_script_copy_code_t)\n                + src[i].key.len + 1 + sizeof(uintptr_t) - 1)\n               & ~(sizeof(uintptr_t) - 1);\n\n        copy = ngx_array_push_n(params->values, size);\n        if (copy == NULL) {\n            return NGX_ERROR;\n        }\n\n        copy->code = ngx_http_script_copy_code;\n        copy->len = src[i].key.len + 1;\n\n        p = (u_char *) copy + sizeof(ngx_http_script_copy_code_t);\n        (void) ngx_cpystrn(p, src[i].key.data, src[i].key.len + 1);\n\n\n        ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));\n\n        sc.cf = cf;\n        sc.source = &src[i].value;\n        sc.flushes = &params->flushes;\n        sc.lengths = &params->lengths;\n        sc.values = &params->values;\n\n        if (ngx_http_script_compile(&sc) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        code = ngx_array_push_n(params->lengths, sizeof(uintptr_t));\n        if (code == NULL) {\n            return NGX_ERROR;\n        }\n\n        *code = (uintptr_t) NULL;\n\n\n        code = ngx_array_push_n(params->values, sizeof(uintptr_t));\n        if (code == NULL) {\n            return NGX_ERROR;\n        }\n\n        *code = (uintptr_t) NULL;\n    }\n\n    code = ngx_array_push_n(params->lengths, sizeof(uintptr_t));\n    if (code == NULL) {\n        return NGX_ERROR;\n    }\n\n    *code = (uintptr_t) NULL;\n\n    params->number = headers_names.nelts;\n\n    hash.hash = &params->hash;\n    hash.key = ngx_hash_key_lc;\n    hash.max_size = 512;\n    hash.bucket_size = 64;\n    hash.name = \"scgi_params_hash\";\n    hash.pool = cf->pool;\n    hash.temp_pool = NULL;\n\n    return ngx_hash_init(&hash, headers_names.elts, headers_names.nelts);\n}\n\n\nstatic char *\nngx_http_scgi_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_scgi_loc_conf_t *scf = conf;\n\n    ngx_url_t                   u;\n    ngx_str_t                  *value, *url;\n    ngx_uint_t                  n;\n    ngx_http_core_loc_conf_t   *clcf;\n    ngx_http_script_compile_t   sc;\n\n    if (scf->upstream.upstream || scf->scgi_lengths) {\n        return \"is duplicate\";\n    }\n\n    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);\n    clcf->handler = ngx_http_scgi_handler;\n\n    value = cf->args->elts;\n\n    url = &value[1];\n\n    n = ngx_http_script_variables_count(url);\n\n    if (n) {\n\n        ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));\n\n        sc.cf = cf;\n        sc.source = url;\n        sc.lengths = &scf->scgi_lengths;\n        sc.values = &scf->scgi_values;\n        sc.variables = n;\n        sc.complete_lengths = 1;\n        sc.complete_values = 1;\n\n        if (ngx_http_script_compile(&sc) != NGX_OK) {\n            return NGX_CONF_ERROR;\n        }\n\n        return NGX_CONF_OK;\n    }\n\n    ngx_memzero(&u, sizeof(ngx_url_t));\n\n    u.url = value[1];\n    u.no_resolve = 1;\n\n    scf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0);\n    if (scf->upstream.upstream == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (clcf->name.len && clcf->name.data[clcf->name.len - 1] == '/') {\n        clcf->auto_redirect = 1;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_scgi_store(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_scgi_loc_conf_t *scf = conf;\n\n    ngx_str_t                  *value;\n    ngx_http_script_compile_t   sc;\n\n    if (scf->upstream.store != NGX_CONF_UNSET) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    if (ngx_strcmp(value[1].data, \"off\") == 0) {\n        scf->upstream.store = 0;\n        return NGX_CONF_OK;\n    }\n\n#if (NGX_HTTP_CACHE)\n    if (scf->upstream.cache > 0) {\n        return \"is incompatible with \\\"scgi_cache\\\"\";\n    }\n#endif\n\n    scf->upstream.store = 1;\n\n    if (ngx_strcmp(value[1].data, \"on\") == 0) {\n        return NGX_CONF_OK;\n    }\n\n    /* include the terminating '\\0' into script */\n    value[1].len++;\n\n    ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));\n\n    sc.cf = cf;\n    sc.source = &value[1];\n    sc.lengths = &scf->upstream.store_lengths;\n    sc.values = &scf->upstream.store_values;\n    sc.variables = ngx_http_script_variables_count(&value[1]);\n    sc.complete_lengths = 1;\n    sc.complete_values = 1;\n\n    if (ngx_http_script_compile(&sc) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\n#if (NGX_HTTP_CACHE)\n\nstatic char *\nngx_http_scgi_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_scgi_loc_conf_t *scf = conf;\n\n    ngx_str_t                         *value;\n    ngx_http_complex_value_t           cv;\n    ngx_http_compile_complex_value_t   ccv;\n\n    value = cf->args->elts;\n\n    if (scf->upstream.cache != NGX_CONF_UNSET) {\n        return \"is duplicate\";\n    }\n\n    if (ngx_strcmp(value[1].data, \"off\") == 0) {\n        scf->upstream.cache = 0;\n        return NGX_CONF_OK;\n    }\n\n    if (scf->upstream.store > 0) {\n        return \"is incompatible with \\\"scgi_store\\\"\";\n    }\n\n    scf->upstream.cache = 1;\n\n    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n\n    ccv.cf = cf;\n    ccv.value = &value[1];\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\n        scf->upstream.cache_value = ngx_palloc(cf->pool,\n                                             sizeof(ngx_http_complex_value_t));\n        if (scf->upstream.cache_value == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        *scf->upstream.cache_value = cv;\n\n        return NGX_CONF_OK;\n    }\n\n    scf->upstream.cache_zone = ngx_shared_memory_add(cf, &value[1], 0,\n                                                     &ngx_http_scgi_module);\n    if (scf->upstream.cache_zone == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_scgi_cache_key(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_scgi_loc_conf_t *scf = conf;\n\n    ngx_str_t                         *value;\n    ngx_http_compile_complex_value_t   ccv;\n\n    value = cf->args->elts;\n\n    if (scf->cache_key.value.data) {\n        return \"is duplicate\";\n    }\n\n    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n\n    ccv.cf = cf;\n    ccv.value = &value[1];\n    ccv.complex_value = &scf->cache_key;\n\n    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n#endif\n"
  },
  {
    "path": "src/http/modules/ngx_http_secure_link_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n#include <ngx_md5.h>\n\n\ntypedef struct {\n    ngx_http_complex_value_t  *variable;\n    ngx_http_complex_value_t  *md5;\n    ngx_str_t                  secret;\n} ngx_http_secure_link_conf_t;\n\n\ntypedef struct {\n    ngx_str_t                  expires;\n} ngx_http_secure_link_ctx_t;\n\n\nstatic ngx_int_t ngx_http_secure_link_old_variable(ngx_http_request_t *r,\n    ngx_http_secure_link_conf_t *conf, ngx_http_variable_value_t *v,\n    uintptr_t data);\nstatic ngx_int_t ngx_http_secure_link_expires_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic void *ngx_http_secure_link_create_conf(ngx_conf_t *cf);\nstatic char *ngx_http_secure_link_merge_conf(ngx_conf_t *cf, void *parent,\n    void *child);\nstatic ngx_int_t ngx_http_secure_link_add_variables(ngx_conf_t *cf);\n\n\nstatic ngx_command_t  ngx_http_secure_link_commands[] = {\n\n    { ngx_string(\"secure_link\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_http_set_complex_value_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_secure_link_conf_t, variable),\n      NULL },\n\n    { ngx_string(\"secure_link_md5\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_http_set_complex_value_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_secure_link_conf_t, md5),\n      NULL },\n\n    { ngx_string(\"secure_link_secret\"),\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_secure_link_conf_t, secret),\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_secure_link_module_ctx = {\n    ngx_http_secure_link_add_variables,    /* preconfiguration */\n    NULL,                                  /* 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    ngx_http_secure_link_create_conf,      /* create location configuration */\n    ngx_http_secure_link_merge_conf        /* merge location configuration */\n};\n\n\nngx_module_t  ngx_http_secure_link_module = {\n    NGX_MODULE_V1,\n    &ngx_http_secure_link_module_ctx,      /* module context */\n    ngx_http_secure_link_commands,         /* 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_str_t  ngx_http_secure_link_name = ngx_string(\"secure_link\");\nstatic ngx_str_t  ngx_http_secure_link_expires_name =\n    ngx_string(\"secure_link_expires\");\n\n\nstatic ngx_int_t\nngx_http_secure_link_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    u_char                       *p, *last;\n    ngx_str_t                     val, hash;\n    time_t                        expires;\n    ngx_md5_t                     md5;\n    ngx_http_secure_link_ctx_t   *ctx;\n    ngx_http_secure_link_conf_t  *conf;\n    u_char                        hash_buf[18], md5_buf[16];\n\n    conf = ngx_http_get_module_loc_conf(r, ngx_http_secure_link_module);\n\n    if (conf->secret.data) {\n        return ngx_http_secure_link_old_variable(r, conf, v, data);\n    }\n\n    if (conf->variable == NULL || conf->md5 == NULL) {\n        goto not_found;\n    }\n\n    if (ngx_http_complex_value(r, conf->variable, &val) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"secure link: \\\"%V\\\"\", &val);\n\n    last = val.data + val.len;\n\n    p = ngx_strlchr(val.data, last, ',');\n    expires = 0;\n\n    if (p) {\n        val.len = p++ - val.data;\n\n        expires = ngx_atotm(p, last - p);\n        if (expires <= 0) {\n            goto not_found;\n        }\n\n        ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_secure_link_ctx_t));\n        if (ctx == NULL) {\n            return NGX_ERROR;\n        }\n\n        ngx_http_set_ctx(r, ctx, ngx_http_secure_link_module);\n\n        ctx->expires.len = last - p;\n        ctx->expires.data = p;\n    }\n\n    if (val.len > 24) {\n        goto not_found;\n    }\n\n    hash.data = hash_buf;\n\n    if (ngx_decode_base64url(&hash, &val) != NGX_OK) {\n        goto not_found;\n    }\n\n    if (hash.len != 16) {\n        goto not_found;\n    }\n\n    if (ngx_http_complex_value(r, conf->md5, &val) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"secure link md5: \\\"%V\\\"\", &val);\n\n    ngx_md5_init(&md5);\n    ngx_md5_update(&md5, val.data, val.len);\n    ngx_md5_final(md5_buf, &md5);\n\n    if (ngx_memcmp(hash_buf, md5_buf, 16) != 0) {\n        goto not_found;\n    }\n\n    v->data = (u_char *) ((expires && expires < ngx_time()) ? \"0\" : \"1\");\n    v->len = 1;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    return NGX_OK;\n\nnot_found:\n\n    v->not_found = 1;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_secure_link_old_variable(ngx_http_request_t *r,\n    ngx_http_secure_link_conf_t *conf, ngx_http_variable_value_t *v,\n    uintptr_t data)\n{\n    u_char      *p, *start, *end, *last;\n    size_t       len;\n    ngx_int_t    n;\n    ngx_uint_t   i;\n    ngx_md5_t    md5;\n    u_char       hash[16];\n\n    p = &r->unparsed_uri.data[1];\n    last = r->unparsed_uri.data + r->unparsed_uri.len;\n\n    while (p < last) {\n        if (*p++ == '/') {\n            start = p;\n            goto md5_start;\n        }\n    }\n\n    goto not_found;\n\nmd5_start:\n\n    while (p < last) {\n        if (*p++ == '/') {\n            end = p - 1;\n            goto url_start;\n        }\n    }\n\n    goto not_found;\n\nurl_start:\n\n    len = last - p;\n\n    if (end - start != 32 || len == 0) {\n        goto not_found;\n    }\n\n    ngx_md5_init(&md5);\n    ngx_md5_update(&md5, p, len);\n    ngx_md5_update(&md5, conf->secret.data, conf->secret.len);\n    ngx_md5_final(hash, &md5);\n\n    for (i = 0; i < 16; i++) {\n        n = ngx_hextoi(&start[2 * i], 2);\n        if (n == NGX_ERROR || n != hash[i]) {\n            goto not_found;\n        }\n    }\n\n    v->len = len;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = p;\n\n    return NGX_OK;\n\nnot_found:\n\n    v->not_found = 1;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_secure_link_expires_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    ngx_http_secure_link_ctx_t  *ctx;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_secure_link_module);\n\n    if (ctx) {\n        v->len = ctx->expires.len;\n        v->valid = 1;\n        v->no_cacheable = 0;\n        v->not_found = 0;\n        v->data = ctx->expires.data;\n\n    } else {\n        v->not_found = 1;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void *\nngx_http_secure_link_create_conf(ngx_conf_t *cf)\n{\n    ngx_http_secure_link_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_secure_link_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc():\n     *\n     *     conf->secret = { 0, NULL };\n     */\n\n    conf->variable = NGX_CONF_UNSET_PTR;\n    conf->md5 = NGX_CONF_UNSET_PTR;\n\n    return conf;\n}\n\n\nstatic char *\nngx_http_secure_link_merge_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_http_secure_link_conf_t *prev = parent;\n    ngx_http_secure_link_conf_t *conf = child;\n\n    if (conf->secret.data) {\n        ngx_conf_init_ptr_value(conf->variable, NULL);\n        ngx_conf_init_ptr_value(conf->md5, NULL);\n\n        if (conf->variable || conf->md5) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"\\\"secure_link_secret\\\" cannot be mixed with \"\n                               \"\\\"secure_link\\\" and \\\"secure_link_md5\\\"\");\n            return NGX_CONF_ERROR;\n        }\n\n        return NGX_CONF_OK;\n    }\n\n    ngx_conf_merge_ptr_value(conf->variable, prev->variable, NULL);\n    ngx_conf_merge_ptr_value(conf->md5, prev->md5, NULL);\n\n    if (conf->variable == NULL && conf->md5 == NULL) {\n        conf->secret = prev->secret;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_secure_link_add_variables(ngx_conf_t *cf)\n{\n    ngx_http_variable_t  *var;\n\n    var = ngx_http_add_variable(cf, &ngx_http_secure_link_name, 0);\n    if (var == NULL) {\n        return NGX_ERROR;\n    }\n\n    var->get_handler = ngx_http_secure_link_variable;\n\n    var = ngx_http_add_variable(cf, &ngx_http_secure_link_expires_name, 0);\n    if (var == NULL) {\n        return NGX_ERROR;\n    }\n\n    var->get_handler = ngx_http_secure_link_expires_variable;\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "src/http/modules/ngx_http_slice_filter_module.c",
    "content": "\n/*\n * Copyright (C) Roman Arutyunyan\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\ntypedef struct {\n    size_t               size;\n} ngx_http_slice_loc_conf_t;\n\n\ntypedef struct {\n    off_t                start;\n    off_t                end;\n    ngx_str_t            range;\n    ngx_str_t            etag;\n    unsigned             last:1;\n    unsigned             active:1;\n    ngx_http_request_t  *sr;\n} ngx_http_slice_ctx_t;\n\n\ntypedef struct {\n    off_t                start;\n    off_t                end;\n    off_t                complete_length;\n} ngx_http_slice_content_range_t;\n\n\nstatic ngx_int_t ngx_http_slice_header_filter(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_slice_body_filter(ngx_http_request_t *r,\n    ngx_chain_t *in);\nstatic ngx_int_t ngx_http_slice_parse_content_range(ngx_http_request_t *r,\n    ngx_http_slice_content_range_t *cr);\nstatic ngx_int_t ngx_http_slice_range_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic off_t ngx_http_slice_get_start(ngx_http_request_t *r);\nstatic void *ngx_http_slice_create_loc_conf(ngx_conf_t *cf);\nstatic char *ngx_http_slice_merge_loc_conf(ngx_conf_t *cf, void *parent,\n    void *child);\nstatic ngx_int_t ngx_http_slice_add_variables(ngx_conf_t *cf);\nstatic ngx_int_t ngx_http_slice_init(ngx_conf_t *cf);\n\n\nstatic ngx_command_t  ngx_http_slice_filter_commands[] = {\n\n    { ngx_string(\"slice\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_slice_loc_conf_t, size),\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_slice_filter_module_ctx = {\n    ngx_http_slice_add_variables,          /* preconfiguration */\n    ngx_http_slice_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    ngx_http_slice_create_loc_conf,        /* create location configuration */\n    ngx_http_slice_merge_loc_conf          /* merge location configuration */\n};\n\n\nngx_module_t  ngx_http_slice_filter_module = {\n    NGX_MODULE_V1,\n    &ngx_http_slice_filter_module_ctx,     /* module context */\n    ngx_http_slice_filter_commands,        /* 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_str_t  ngx_http_slice_range_name = ngx_string(\"slice_range\");\n\nstatic ngx_http_output_header_filter_pt  ngx_http_next_header_filter;\nstatic ngx_http_output_body_filter_pt    ngx_http_next_body_filter;\n\n\nstatic ngx_int_t\nngx_http_slice_header_filter(ngx_http_request_t *r)\n{\n    off_t                            end;\n    ngx_int_t                        rc;\n    ngx_table_elt_t                 *h;\n    ngx_http_slice_ctx_t            *ctx;\n    ngx_http_slice_loc_conf_t       *slcf;\n    ngx_http_slice_content_range_t   cr;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_slice_filter_module);\n    if (ctx == NULL) {\n        return ngx_http_next_header_filter(r);\n    }\n\n    if (r->headers_out.status != NGX_HTTP_PARTIAL_CONTENT) {\n        if (r == r->main) {\n            ngx_http_set_ctx(r, NULL, ngx_http_slice_filter_module);\n            return ngx_http_next_header_filter(r);\n        }\n\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"unexpected status code %ui in slice response\",\n                      r->headers_out.status);\n        return NGX_ERROR;\n    }\n\n    h = r->headers_out.etag;\n\n    if (ctx->etag.len) {\n        if (h == NULL\n            || h->value.len != ctx->etag.len\n            || ngx_strncmp(h->value.data, ctx->etag.data, ctx->etag.len)\n               != 0)\n        {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"etag mismatch in slice response\");\n            return NGX_ERROR;\n        }\n    }\n\n    if (h) {\n        ctx->etag = h->value;\n    }\n\n    if (ngx_http_slice_parse_content_range(r, &cr) != NGX_OK) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"invalid range in slice response\");\n        return NGX_ERROR;\n    }\n\n    if (cr.complete_length == -1) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"no complete length in slice response\");\n        return NGX_ERROR;\n    }\n\n    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http slice response range: %O-%O/%O\",\n                   cr.start, cr.end, cr.complete_length);\n\n    slcf = ngx_http_get_module_loc_conf(r, ngx_http_slice_filter_module);\n\n    end = ngx_min(cr.start + (off_t) slcf->size, cr.complete_length);\n\n    if (cr.start != ctx->start || cr.end != end) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"unexpected range in slice response: %O-%O\",\n                      cr.start, cr.end);\n        return NGX_ERROR;\n    }\n\n    ctx->start = end;\n    ctx->active = 1;\n\n    r->headers_out.status = NGX_HTTP_OK;\n    r->headers_out.status_line.len = 0;\n    r->headers_out.content_length_n = cr.complete_length;\n    r->headers_out.content_offset = cr.start;\n    r->headers_out.content_range->hash = 0;\n    r->headers_out.content_range = NULL;\n\n    if (r->headers_out.accept_ranges) {\n        r->headers_out.accept_ranges->hash = 0;\n        r->headers_out.accept_ranges = NULL;\n    }\n\n    r->allow_ranges = 1;\n    r->subrequest_ranges = 1;\n    r->single_range = 1;\n\n    rc = ngx_http_next_header_filter(r);\n\n    if (r != r->main) {\n        return rc;\n    }\n\n    r->preserve_body = 1;\n\n    if (r->headers_out.status == NGX_HTTP_PARTIAL_CONTENT) {\n        if (ctx->start + (off_t) slcf->size <= r->headers_out.content_offset) {\n            ctx->start = slcf->size\n                         * (r->headers_out.content_offset / slcf->size);\n        }\n\n        ctx->end = r->headers_out.content_offset\n                   + r->headers_out.content_length_n;\n\n    } else {\n        ctx->end = cr.complete_length;\n    }\n\n    return rc;\n}\n\n\nstatic ngx_int_t\nngx_http_slice_body_filter(ngx_http_request_t *r, ngx_chain_t *in)\n{\n    ngx_int_t                   rc;\n    ngx_chain_t                *cl;\n    ngx_http_slice_ctx_t       *ctx;\n    ngx_http_slice_loc_conf_t  *slcf;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_slice_filter_module);\n\n    if (ctx == NULL || r != r->main) {\n        return ngx_http_next_body_filter(r, in);\n    }\n\n    for (cl = in; cl; cl = cl->next) {\n        if (cl->buf->last_buf) {\n            cl->buf->last_buf = 0;\n            cl->buf->last_in_chain = 1;\n            cl->buf->sync = 1;\n            ctx->last = 1;\n        }\n    }\n\n    rc = ngx_http_next_body_filter(r, in);\n\n    if (rc == NGX_ERROR || !ctx->last) {\n        return rc;\n    }\n\n    if (ctx->sr && !ctx->sr->done) {\n        return rc;\n    }\n\n    if (!ctx->active) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"missing slice response\");\n        return NGX_ERROR;\n    }\n\n    if (ctx->start >= ctx->end) {\n        ngx_http_set_ctx(r, NULL, ngx_http_slice_filter_module);\n        ngx_http_send_special(r, NGX_HTTP_LAST);\n        return rc;\n    }\n\n    if (r->buffered) {\n        return rc;\n    }\n\n    if (ngx_http_subrequest(r, &r->uri, &r->args, &ctx->sr, NULL,\n                            NGX_HTTP_SUBREQUEST_CLONE)\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    ngx_http_set_ctx(ctx->sr, ctx, ngx_http_slice_filter_module);\n\n    slcf = ngx_http_get_module_loc_conf(r, ngx_http_slice_filter_module);\n\n    ctx->range.len = ngx_sprintf(ctx->range.data, \"bytes=%O-%O\", ctx->start,\n                                 ctx->start + (off_t) slcf->size - 1)\n                     - ctx->range.data;\n\n    ctx->active = 0;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http slice subrequest: \\\"%V\\\"\", &ctx->range);\n\n    return rc;\n}\n\n\nstatic ngx_int_t\nngx_http_slice_parse_content_range(ngx_http_request_t *r,\n    ngx_http_slice_content_range_t *cr)\n{\n    off_t             start, end, complete_length, cutoff, cutlim;\n    u_char           *p;\n    ngx_table_elt_t  *h;\n\n    h = r->headers_out.content_range;\n\n    if (h == NULL\n        || h->value.len < 7\n        || ngx_strncmp(h->value.data, \"bytes \", 6) != 0)\n    {\n        return NGX_ERROR;\n    }\n\n    p = h->value.data + 6;\n\n    cutoff = NGX_MAX_OFF_T_VALUE / 10;\n    cutlim = NGX_MAX_OFF_T_VALUE % 10;\n\n    start = 0;\n    end = 0;\n    complete_length = 0;\n\n    while (*p == ' ') { p++; }\n\n    if (*p < '0' || *p > '9') {\n        return NGX_ERROR;\n    }\n\n    while (*p >= '0' && *p <= '9') {\n        if (start >= cutoff && (start > cutoff || *p - '0' > cutlim)) {\n            return NGX_ERROR;\n        }\n\n        start = start * 10 + (*p++ - '0');\n    }\n\n    while (*p == ' ') { p++; }\n\n    if (*p++ != '-') {\n        return NGX_ERROR;\n    }\n\n    while (*p == ' ') { p++; }\n\n    if (*p < '0' || *p > '9') {\n        return NGX_ERROR;\n    }\n\n    while (*p >= '0' && *p <= '9') {\n        if (end >= cutoff && (end > cutoff || *p - '0' > cutlim)) {\n            return NGX_ERROR;\n        }\n\n        end = end * 10 + (*p++ - '0');\n    }\n\n    end++;\n\n    while (*p == ' ') { p++; }\n\n    if (*p++ != '/') {\n        return NGX_ERROR;\n    }\n\n    while (*p == ' ') { p++; }\n\n    if (*p != '*') {\n        if (*p < '0' || *p > '9') {\n            return NGX_ERROR;\n        }\n\n        while (*p >= '0' && *p <= '9') {\n            if (complete_length >= cutoff\n                && (complete_length > cutoff || *p - '0' > cutlim))\n            {\n                return NGX_ERROR;\n            }\n\n            complete_length = complete_length * 10 + (*p++ - '0');\n        }\n\n    } else {\n        complete_length = -1;\n        p++;\n    }\n\n    while (*p == ' ') { p++; }\n\n    if (*p != '\\0') {\n        return NGX_ERROR;\n    }\n\n    cr->start = start;\n    cr->end = end;\n    cr->complete_length = complete_length;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_slice_range_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    u_char                     *p;\n    ngx_http_slice_ctx_t       *ctx;\n    ngx_http_slice_loc_conf_t  *slcf;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_slice_filter_module);\n\n    if (ctx == NULL) {\n        if (r != r->main || r->headers_out.status) {\n            v->not_found = 1;\n            return NGX_OK;\n        }\n\n        slcf = ngx_http_get_module_loc_conf(r, ngx_http_slice_filter_module);\n\n        if (slcf->size == 0) {\n            v->not_found = 1;\n            return NGX_OK;\n        }\n\n        ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_slice_ctx_t));\n        if (ctx == NULL) {\n            return NGX_ERROR;\n        }\n\n        ngx_http_set_ctx(r, ctx, ngx_http_slice_filter_module);\n\n        p = ngx_pnalloc(r->pool, sizeof(\"bytes=-\") - 1 + 2 * NGX_OFF_T_LEN);\n        if (p == NULL) {\n            return NGX_ERROR;\n        }\n\n        ctx->start = slcf->size * (ngx_http_slice_get_start(r) / slcf->size);\n\n        ctx->range.data = p;\n        ctx->range.len = ngx_sprintf(p, \"bytes=%O-%O\", ctx->start,\n                                     ctx->start + (off_t) slcf->size - 1)\n                         - p;\n    }\n\n    v->data = ctx->range.data;\n    v->valid = 1;\n    v->not_found = 0;\n    v->no_cacheable = 1;\n    v->len = ctx->range.len;\n\n    return NGX_OK;\n}\n\n\nstatic off_t\nngx_http_slice_get_start(ngx_http_request_t *r)\n{\n    off_t             start, cutoff, cutlim;\n    u_char           *p;\n    ngx_table_elt_t  *h;\n\n    if (r->headers_in.if_range) {\n        return 0;\n    }\n\n    h = r->headers_in.range;\n\n    if (h == NULL\n        || h->value.len < 7\n        || ngx_strncasecmp(h->value.data, (u_char *) \"bytes=\", 6) != 0)\n    {\n        return 0;\n    }\n\n    p = h->value.data + 6;\n\n    if (ngx_strchr(p, ',')) {\n        return 0;\n    }\n\n    while (*p == ' ') { p++; }\n\n    if (*p == '-') {\n        return 0;\n    }\n\n    cutoff = NGX_MAX_OFF_T_VALUE / 10;\n    cutlim = NGX_MAX_OFF_T_VALUE % 10;\n\n    start = 0;\n\n    while (*p >= '0' && *p <= '9') {\n        if (start >= cutoff && (start > cutoff || *p - '0' > cutlim)) {\n            return 0;\n        }\n\n        start = start * 10 + (*p++ - '0');\n    }\n\n    return start;\n}\n\n\nstatic void *\nngx_http_slice_create_loc_conf(ngx_conf_t *cf)\n{\n    ngx_http_slice_loc_conf_t  *slcf;\n\n    slcf = ngx_palloc(cf->pool, sizeof(ngx_http_slice_loc_conf_t));\n    if (slcf == NULL) {\n        return NULL;\n    }\n\n    slcf->size = NGX_CONF_UNSET_SIZE;\n\n    return slcf;\n}\n\n\nstatic char *\nngx_http_slice_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_http_slice_loc_conf_t *prev = parent;\n    ngx_http_slice_loc_conf_t *conf = child;\n\n    ngx_conf_merge_size_value(conf->size, prev->size, 0);\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_slice_add_variables(ngx_conf_t *cf)\n{\n    ngx_http_variable_t  *var;\n\n    var = ngx_http_add_variable(cf, &ngx_http_slice_range_name, 0);\n    if (var == NULL) {\n        return NGX_ERROR;\n    }\n\n    var->get_handler = ngx_http_slice_range_variable;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_slice_init(ngx_conf_t *cf)\n{\n    ngx_http_next_header_filter = ngx_http_top_header_filter;\n    ngx_http_top_header_filter = ngx_http_slice_header_filter;\n\n    ngx_http_next_body_filter = ngx_http_top_body_filter;\n    ngx_http_top_body_filter = ngx_http_slice_body_filter;\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "src/http/modules/ngx_http_split_clients_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\ntypedef struct {\n    uint32_t                    percent;\n    ngx_http_variable_value_t   value;\n} ngx_http_split_clients_part_t;\n\n\ntypedef struct {\n    ngx_http_complex_value_t    value;\n    ngx_array_t                 parts;\n} ngx_http_split_clients_ctx_t;\n\n\nstatic char *ngx_conf_split_clients_block(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_http_split_clients(ngx_conf_t *cf, ngx_command_t *dummy,\n    void *conf);\n\nstatic ngx_command_t  ngx_http_split_clients_commands[] = {\n\n    { ngx_string(\"split_clients\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE2,\n      ngx_conf_split_clients_block,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      0,\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_split_clients_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    NULL,                                  /* 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\nngx_module_t  ngx_http_split_clients_module = {\n    NGX_MODULE_V1,\n    &ngx_http_split_clients_module_ctx,    /* module context */\n    ngx_http_split_clients_commands,       /* 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_split_clients_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    ngx_http_split_clients_ctx_t *ctx = (ngx_http_split_clients_ctx_t *) data;\n\n    uint32_t                        hash;\n    ngx_str_t                       val;\n    ngx_uint_t                      i;\n    ngx_http_split_clients_part_t  *part;\n\n    *v = ngx_http_variable_null_value;\n\n    if (ngx_http_complex_value(r, &ctx->value, &val) != NGX_OK) {\n        return NGX_OK;\n    }\n\n    hash = ngx_murmur_hash2(val.data, val.len);\n\n    part = ctx->parts.elts;\n\n    for (i = 0; i < ctx->parts.nelts; i++) {\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"http split: %uD %uD\", hash, part[i].percent);\n\n        if (hash < part[i].percent || part[i].percent == 0) {\n            *v = part[i].value;\n            return NGX_OK;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nstatic char *\nngx_conf_split_clients_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    char                                *rv;\n    uint32_t                             sum, last;\n    ngx_str_t                           *value, name;\n    ngx_uint_t                           i;\n    ngx_conf_t                           save;\n    ngx_http_variable_t                 *var;\n    ngx_http_split_clients_ctx_t        *ctx;\n    ngx_http_split_clients_part_t       *part;\n    ngx_http_compile_complex_value_t     ccv;\n\n    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_split_clients_ctx_t));\n    if (ctx == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    value = cf->args->elts;\n\n    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n\n    ccv.cf = cf;\n    ccv.value = &value[1];\n    ccv.complex_value = &ctx->value;\n\n    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    name = value[2];\n\n    if (name.data[0] != '$') {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid variable name \\\"%V\\\"\", &name);\n        return NGX_CONF_ERROR;\n    }\n\n    name.len--;\n    name.data++;\n\n    var = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE);\n    if (var == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    var->get_handler = ngx_http_split_clients_variable;\n    var->data = (uintptr_t) ctx;\n\n    if (ngx_array_init(&ctx->parts, cf->pool, 2,\n                       sizeof(ngx_http_split_clients_part_t))\n        != NGX_OK)\n    {\n        return NGX_CONF_ERROR;\n    }\n\n    save = *cf;\n    cf->ctx = ctx;\n    cf->handler = ngx_http_split_clients;\n    cf->handler_conf = conf;\n\n    rv = ngx_conf_parse(cf, NULL);\n\n    *cf = save;\n\n    if (rv != NGX_CONF_OK) {\n        return rv;\n    }\n\n    sum = 0;\n    last = 0;\n    part = ctx->parts.elts;\n\n    for (i = 0; i < ctx->parts.nelts; i++) {\n        sum = part[i].percent ? sum + part[i].percent : 10000;\n        if (sum > 10000) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"percent total is greater than 100%%\");\n            return NGX_CONF_ERROR;\n        }\n\n        if (part[i].percent) {\n            last += part[i].percent * (uint64_t) 0xffffffff / 10000;\n            part[i].percent = last;\n        }\n    }\n\n    return rv;\n}\n\n\nstatic char *\nngx_http_split_clients(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)\n{\n    ngx_int_t                       n;\n    ngx_str_t                      *value;\n    ngx_http_split_clients_ctx_t   *ctx;\n    ngx_http_split_clients_part_t  *part;\n\n    ctx = cf->ctx;\n    value = cf->args->elts;\n\n    part = ngx_array_push(&ctx->parts);\n    if (part == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (value[0].len == 1 && value[0].data[0] == '*') {\n        part->percent = 0;\n\n    } else {\n        if (value[0].len == 0 || value[0].data[value[0].len - 1] != '%') {\n            goto invalid;\n        }\n\n        n = ngx_atofp(value[0].data, value[0].len - 1, 2);\n        if (n == NGX_ERROR || n == 0) {\n            goto invalid;\n        }\n\n        part->percent = (uint32_t) n;\n    }\n\n    part->value.len = value[1].len;\n    part->value.valid = 1;\n    part->value.no_cacheable = 0;\n    part->value.not_found = 0;\n    part->value.data = value[1].data;\n\n    return NGX_CONF_OK;\n\ninvalid:\n\n    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                       \"invalid percent value \\\"%V\\\"\", &value[0]);\n    return NGX_CONF_ERROR;\n}\n"
  },
  {
    "path": "src/http/modules/ngx_http_ssi_filter_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n#define NGX_HTTP_SSI_ERROR          1\n\n#define NGX_HTTP_SSI_DATE_LEN       2048\n\n#define NGX_HTTP_SSI_ADD_PREFIX     1\n#define NGX_HTTP_SSI_ADD_ZERO       2\n\n\ntypedef struct {\n    ngx_flag_t    enable;\n    ngx_flag_t    silent_errors;\n    ngx_flag_t    ignore_recycled_buffers;\n    ngx_flag_t    last_modified;\n\n    ngx_hash_t    types;\n\n    size_t        min_file_chunk;\n    size_t        value_len;\n\n    ngx_array_t  *types_keys;\n} ngx_http_ssi_loc_conf_t;\n\n\ntypedef struct {\n    ngx_str_t     name;\n    ngx_uint_t    key;\n    ngx_str_t     value;\n} ngx_http_ssi_var_t;\n\n\ntypedef struct {\n    ngx_str_t     name;\n    ngx_chain_t  *bufs;\n    ngx_uint_t    count;\n} ngx_http_ssi_block_t;\n\n\ntypedef enum {\n    ssi_start_state = 0,\n    ssi_tag_state,\n    ssi_comment0_state,\n    ssi_comment1_state,\n    ssi_sharp_state,\n    ssi_precommand_state,\n    ssi_command_state,\n    ssi_preparam_state,\n    ssi_param_state,\n    ssi_preequal_state,\n    ssi_prevalue_state,\n    ssi_double_quoted_value_state,\n    ssi_quoted_value_state,\n    ssi_quoted_symbol_state,\n    ssi_postparam_state,\n    ssi_comment_end0_state,\n    ssi_comment_end1_state,\n    ssi_error_state,\n    ssi_error_end0_state,\n    ssi_error_end1_state\n} ngx_http_ssi_state_e;\n\n\nstatic ngx_int_t ngx_http_ssi_output(ngx_http_request_t *r,\n    ngx_http_ssi_ctx_t *ctx);\nstatic void ngx_http_ssi_buffered(ngx_http_request_t *r,\n    ngx_http_ssi_ctx_t *ctx);\nstatic ngx_int_t ngx_http_ssi_parse(ngx_http_request_t *r,\n    ngx_http_ssi_ctx_t *ctx);\nstatic ngx_str_t *ngx_http_ssi_get_variable(ngx_http_request_t *r,\n    ngx_str_t *name, ngx_uint_t key);\nstatic ngx_int_t ngx_http_ssi_evaluate_string(ngx_http_request_t *r,\n    ngx_http_ssi_ctx_t *ctx, ngx_str_t *text, ngx_uint_t flags);\nstatic ngx_int_t ngx_http_ssi_regex_match(ngx_http_request_t *r,\n    ngx_str_t *pattern, ngx_str_t *str);\n\nstatic ngx_int_t ngx_http_ssi_include(ngx_http_request_t *r,\n    ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);\nstatic ngx_int_t ngx_http_ssi_stub_output(ngx_http_request_t *r, void *data,\n    ngx_int_t rc);\nstatic ngx_int_t ngx_http_ssi_set_variable(ngx_http_request_t *r, void *data,\n    ngx_int_t rc);\nstatic ngx_int_t ngx_http_ssi_echo(ngx_http_request_t *r,\n    ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);\nstatic ngx_int_t ngx_http_ssi_config(ngx_http_request_t *r,\n    ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);\nstatic ngx_int_t ngx_http_ssi_set(ngx_http_request_t *r,\n    ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);\nstatic ngx_int_t ngx_http_ssi_if(ngx_http_request_t *r,\n    ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);\nstatic ngx_int_t ngx_http_ssi_else(ngx_http_request_t *r,\n    ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);\nstatic ngx_int_t ngx_http_ssi_endif(ngx_http_request_t *r,\n    ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);\nstatic ngx_int_t ngx_http_ssi_block(ngx_http_request_t *r,\n    ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);\nstatic ngx_int_t ngx_http_ssi_endblock(ngx_http_request_t *r,\n    ngx_http_ssi_ctx_t *ctx, ngx_str_t **params);\n\nstatic ngx_int_t ngx_http_ssi_date_gmt_local_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t gmt);\n\nstatic ngx_int_t ngx_http_ssi_preconfiguration(ngx_conf_t *cf);\nstatic void *ngx_http_ssi_create_main_conf(ngx_conf_t *cf);\nstatic char *ngx_http_ssi_init_main_conf(ngx_conf_t *cf, void *conf);\nstatic void *ngx_http_ssi_create_loc_conf(ngx_conf_t *cf);\nstatic char *ngx_http_ssi_merge_loc_conf(ngx_conf_t *cf,\n    void *parent, void *child);\nstatic ngx_int_t ngx_http_ssi_filter_init(ngx_conf_t *cf);\n\n\nstatic ngx_command_t  ngx_http_ssi_filter_commands[] = {\n\n    { ngx_string(\"ssi\"),\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_ssi_loc_conf_t, enable),\n      NULL },\n\n    { ngx_string(\"ssi_silent_errors\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_ssi_loc_conf_t, silent_errors),\n      NULL },\n\n    { ngx_string(\"ssi_ignore_recycled_buffers\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_ssi_loc_conf_t, ignore_recycled_buffers),\n      NULL },\n\n    { ngx_string(\"ssi_min_file_chunk\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_ssi_loc_conf_t, min_file_chunk),\n      NULL },\n\n    { ngx_string(\"ssi_value_length\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_ssi_loc_conf_t, value_len),\n      NULL },\n\n    { ngx_string(\"ssi_types\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,\n      ngx_http_types_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_ssi_loc_conf_t, types_keys),\n      &ngx_http_html_default_types[0] },\n\n    { ngx_string(\"ssi_last_modified\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_ssi_loc_conf_t, last_modified),\n      NULL },\n\n      ngx_null_command\n};\n\n\n\nstatic ngx_http_module_t  ngx_http_ssi_filter_module_ctx = {\n    ngx_http_ssi_preconfiguration,         /* preconfiguration */\n    ngx_http_ssi_filter_init,              /* postconfiguration */\n\n    ngx_http_ssi_create_main_conf,         /* create main configuration */\n    ngx_http_ssi_init_main_conf,           /* init main configuration */\n\n    NULL,                                  /* create server configuration */\n    NULL,                                  /* merge server configuration */\n\n    ngx_http_ssi_create_loc_conf,          /* create location configuration */\n    ngx_http_ssi_merge_loc_conf            /* merge location configuration */\n};\n\n\nngx_module_t  ngx_http_ssi_filter_module = {\n    NGX_MODULE_V1,\n    &ngx_http_ssi_filter_module_ctx,       /* module context */\n    ngx_http_ssi_filter_commands,          /* 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_http_output_header_filter_pt  ngx_http_next_header_filter;\nstatic ngx_http_output_body_filter_pt    ngx_http_next_body_filter;\n\n\nstatic u_char ngx_http_ssi_string[] = \"<!--\";\n\nstatic ngx_str_t ngx_http_ssi_none = ngx_string(\"(none)\");\nstatic ngx_str_t ngx_http_ssi_timefmt = ngx_string(\"%A, %d-%b-%Y %H:%M:%S %Z\");\nstatic ngx_str_t ngx_http_ssi_null_string = ngx_null_string;\n\n\n#define  NGX_HTTP_SSI_INCLUDE_VIRTUAL  0\n#define  NGX_HTTP_SSI_INCLUDE_FILE     1\n#define  NGX_HTTP_SSI_INCLUDE_WAIT     2\n#define  NGX_HTTP_SSI_INCLUDE_SET      3\n#define  NGX_HTTP_SSI_INCLUDE_STUB     4\n\n#define  NGX_HTTP_SSI_ECHO_VAR         0\n#define  NGX_HTTP_SSI_ECHO_DEFAULT     1\n#define  NGX_HTTP_SSI_ECHO_ENCODING    2\n\n#define  NGX_HTTP_SSI_CONFIG_ERRMSG    0\n#define  NGX_HTTP_SSI_CONFIG_TIMEFMT   1\n\n#define  NGX_HTTP_SSI_SET_VAR          0\n#define  NGX_HTTP_SSI_SET_VALUE        1\n\n#define  NGX_HTTP_SSI_IF_EXPR          0\n\n#define  NGX_HTTP_SSI_BLOCK_NAME       0\n\n\nstatic ngx_http_ssi_param_t  ngx_http_ssi_include_params[] = {\n    { ngx_string(\"virtual\"), NGX_HTTP_SSI_INCLUDE_VIRTUAL, 0, 0 },\n    { ngx_string(\"file\"), NGX_HTTP_SSI_INCLUDE_FILE, 0, 0 },\n    { ngx_string(\"wait\"), NGX_HTTP_SSI_INCLUDE_WAIT, 0, 0 },\n    { ngx_string(\"set\"), NGX_HTTP_SSI_INCLUDE_SET, 0, 0 },\n    { ngx_string(\"stub\"), NGX_HTTP_SSI_INCLUDE_STUB, 0, 0 },\n    { ngx_null_string, 0, 0, 0 }\n};\n\n\nstatic ngx_http_ssi_param_t  ngx_http_ssi_echo_params[] = {\n    { ngx_string(\"var\"), NGX_HTTP_SSI_ECHO_VAR, 1, 0 },\n    { ngx_string(\"default\"), NGX_HTTP_SSI_ECHO_DEFAULT, 0, 0 },\n    { ngx_string(\"encoding\"), NGX_HTTP_SSI_ECHO_ENCODING, 0, 0 },\n    { ngx_null_string, 0, 0, 0 }\n};\n\n\nstatic ngx_http_ssi_param_t  ngx_http_ssi_config_params[] = {\n    { ngx_string(\"errmsg\"), NGX_HTTP_SSI_CONFIG_ERRMSG, 0, 0 },\n    { ngx_string(\"timefmt\"), NGX_HTTP_SSI_CONFIG_TIMEFMT, 0, 0 },\n    { ngx_null_string, 0, 0, 0 }\n};\n\n\nstatic ngx_http_ssi_param_t  ngx_http_ssi_set_params[] = {\n    { ngx_string(\"var\"), NGX_HTTP_SSI_SET_VAR, 1, 0 },\n    { ngx_string(\"value\"), NGX_HTTP_SSI_SET_VALUE, 1, 0 },\n    { ngx_null_string, 0, 0, 0 }\n};\n\n\nstatic ngx_http_ssi_param_t  ngx_http_ssi_if_params[] = {\n    { ngx_string(\"expr\"), NGX_HTTP_SSI_IF_EXPR, 1, 0 },\n    { ngx_null_string, 0, 0, 0 }\n};\n\n\nstatic ngx_http_ssi_param_t  ngx_http_ssi_block_params[] = {\n    { ngx_string(\"name\"), NGX_HTTP_SSI_BLOCK_NAME, 1, 0 },\n    { ngx_null_string, 0, 0, 0 }\n};\n\n\nstatic ngx_http_ssi_param_t  ngx_http_ssi_no_params[] = {\n    { ngx_null_string, 0, 0, 0 }\n};\n\n\nstatic ngx_http_ssi_command_t  ngx_http_ssi_commands[] = {\n    { ngx_string(\"include\"), ngx_http_ssi_include,\n                       ngx_http_ssi_include_params, 0, 0, 1 },\n    { ngx_string(\"echo\"), ngx_http_ssi_echo,\n                       ngx_http_ssi_echo_params, 0, 0, 0 },\n    { ngx_string(\"config\"), ngx_http_ssi_config,\n                       ngx_http_ssi_config_params, 0, 0, 0 },\n    { ngx_string(\"set\"), ngx_http_ssi_set, ngx_http_ssi_set_params, 0, 0, 0 },\n\n    { ngx_string(\"if\"), ngx_http_ssi_if, ngx_http_ssi_if_params, 0, 0, 0 },\n    { ngx_string(\"elif\"), ngx_http_ssi_if, ngx_http_ssi_if_params,\n                       NGX_HTTP_SSI_COND_IF, 0, 0 },\n    { ngx_string(\"else\"), ngx_http_ssi_else, ngx_http_ssi_no_params,\n                       NGX_HTTP_SSI_COND_IF, 0, 0 },\n    { ngx_string(\"endif\"), ngx_http_ssi_endif, ngx_http_ssi_no_params,\n                       NGX_HTTP_SSI_COND_ELSE, 0, 0 },\n\n    { ngx_string(\"block\"), ngx_http_ssi_block,\n                       ngx_http_ssi_block_params, 0, 0, 0 },\n    { ngx_string(\"endblock\"), ngx_http_ssi_endblock,\n                       ngx_http_ssi_no_params, 0, 1, 0 },\n\n    { ngx_null_string, NULL, NULL, 0, 0, 0 }\n};\n\n\nstatic ngx_http_variable_t  ngx_http_ssi_vars[] = {\n\n    { ngx_string(\"date_local\"), NULL, ngx_http_ssi_date_gmt_local_variable, 0,\n      NGX_HTTP_VAR_NOCACHEABLE, 0 },\n\n    { ngx_string(\"date_gmt\"), NULL, ngx_http_ssi_date_gmt_local_variable, 1,\n      NGX_HTTP_VAR_NOCACHEABLE, 0 },\n\n      ngx_http_null_variable\n};\n\n\n\nstatic ngx_int_t\nngx_http_ssi_header_filter(ngx_http_request_t *r)\n{\n    ngx_http_ssi_ctx_t       *ctx, *mctx;\n    ngx_http_ssi_loc_conf_t  *slcf;\n\n    slcf = ngx_http_get_module_loc_conf(r, ngx_http_ssi_filter_module);\n\n    if (!slcf->enable\n        || r->headers_out.content_length_n == 0\n        || ngx_http_test_content_type(r, &slcf->types) == NULL)\n    {\n        return ngx_http_next_header_filter(r);\n    }\n\n    mctx = ngx_http_get_module_ctx(r->main, ngx_http_ssi_filter_module);\n\n    ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_ssi_ctx_t));\n    if (ctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_http_set_ctx(r, ctx, ngx_http_ssi_filter_module);\n\n\n    ctx->value_len = slcf->value_len;\n    ctx->last_out = &ctx->out;\n\n    ctx->encoding = NGX_HTTP_SSI_ENTITY_ENCODING;\n    ctx->output = 1;\n\n    ctx->params.elts = ctx->params_array;\n    ctx->params.size = sizeof(ngx_table_elt_t);\n    ctx->params.nalloc = NGX_HTTP_SSI_PARAMS_N;\n    ctx->params.pool = r->pool;\n\n    ctx->timefmt = ngx_http_ssi_timefmt;\n    ngx_str_set(&ctx->errmsg,\n                \"[an error occurred while processing the directive]\");\n\n    r->filter_need_in_memory = 1;\n\n    if (r == r->main) {\n\n        if (mctx) {\n\n            /*\n             * if there was a shared context previously used as main,\n             * copy variables and blocks\n             */\n\n            ctx->variables = mctx->variables;\n            ctx->blocks = mctx->blocks;\n\n#if (NGX_PCRE)\n            ctx->ncaptures = mctx->ncaptures;\n            ctx->captures = mctx->captures;\n            ctx->captures_data = mctx->captures_data;\n#endif\n\n            mctx->shared = 0;\n        }\n\n        ngx_http_clear_content_length(r);\n        ngx_http_clear_accept_ranges(r);\n\n        r->preserve_body = 1;\n\n        if (!slcf->last_modified) {\n            ngx_http_clear_last_modified(r);\n            ngx_http_clear_etag(r);\n\n        } else {\n            ngx_http_weak_etag(r);\n        }\n\n    } else if (mctx == NULL) {\n        ngx_http_set_ctx(r->main, ctx, ngx_http_ssi_filter_module);\n        ctx->shared = 1;\n    }\n\n    return ngx_http_next_header_filter(r);\n}\n\n\nstatic ngx_int_t\nngx_http_ssi_body_filter(ngx_http_request_t *r, ngx_chain_t *in)\n{\n    size_t                     len;\n    ngx_int_t                  rc;\n    ngx_buf_t                 *b;\n    ngx_uint_t                 i, index;\n    ngx_chain_t               *cl, **ll;\n    ngx_table_elt_t           *param;\n    ngx_http_ssi_ctx_t        *ctx, *mctx;\n    ngx_http_ssi_block_t      *bl;\n    ngx_http_ssi_param_t      *prm;\n    ngx_http_ssi_command_t    *cmd;\n    ngx_http_ssi_loc_conf_t   *slcf;\n    ngx_http_ssi_main_conf_t  *smcf;\n    ngx_str_t                 *params[NGX_HTTP_SSI_MAX_PARAMS + 1];\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_ssi_filter_module);\n\n    if (ctx == NULL\n        || (ctx->shared && r == r->main)\n        || (in == NULL\n            && ctx->buf == NULL\n            && ctx->in == NULL\n            && ctx->busy == NULL))\n    {\n        return ngx_http_next_body_filter(r, in);\n    }\n\n    /* add the incoming chain to the chain ctx->in */\n\n    if (in) {\n        if (ngx_chain_add_copy(r->pool, &ctx->in, in) != NGX_OK) {\n            return NGX_ERROR;\n        }\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http ssi filter \\\"%V?%V\\\"\", &r->uri, &r->args);\n\n    if (ctx->wait) {\n\n        if (r != r->connection->data) {\n            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"http ssi filter wait \\\"%V?%V\\\" non-active\",\n                           &ctx->wait->uri, &ctx->wait->args);\n\n            return NGX_AGAIN;\n        }\n\n        if (ctx->wait->done) {\n            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"http ssi filter wait \\\"%V?%V\\\" done\",\n                           &ctx->wait->uri, &ctx->wait->args);\n\n            ctx->wait = NULL;\n\n        } else {\n            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"http ssi filter wait \\\"%V?%V\\\"\",\n                           &ctx->wait->uri, &ctx->wait->args);\n\n            return ngx_http_next_body_filter(r, NULL);\n        }\n    }\n\n    slcf = ngx_http_get_module_loc_conf(r, ngx_http_ssi_filter_module);\n\n    while (ctx->in || ctx->buf) {\n\n        if (ctx->buf == NULL) {\n            ctx->buf = ctx->in->buf;\n            ctx->in = ctx->in->next;\n            ctx->pos = ctx->buf->pos;\n        }\n\n        if (ctx->state == ssi_start_state) {\n            ctx->copy_start = ctx->pos;\n            ctx->copy_end = ctx->pos;\n        }\n\n        b = NULL;\n\n        while (ctx->pos < ctx->buf->last) {\n\n            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"saved: %uz state: %ui\", ctx->saved, ctx->state);\n\n            rc = ngx_http_ssi_parse(r, ctx);\n\n            ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"parse: %i, looked: %uz %p-%p\",\n                           rc, ctx->looked, ctx->copy_start, ctx->copy_end);\n\n            if (rc == NGX_ERROR) {\n                return rc;\n            }\n\n            if (ctx->copy_start != ctx->copy_end) {\n\n                if (ctx->output) {\n\n                    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                                   \"saved: %uz\", ctx->saved);\n\n                    if (ctx->saved) {\n\n                        if (ctx->free) {\n                            cl = ctx->free;\n                            ctx->free = ctx->free->next;\n                            b = cl->buf;\n                            ngx_memzero(b, sizeof(ngx_buf_t));\n\n                        } else {\n                            b = ngx_calloc_buf(r->pool);\n                            if (b == NULL) {\n                                return NGX_ERROR;\n                            }\n\n                            cl = ngx_alloc_chain_link(r->pool);\n                            if (cl == NULL) {\n                                return NGX_ERROR;\n                            }\n\n                            cl->buf = b;\n                        }\n\n                        b->memory = 1;\n                        b->pos = ngx_http_ssi_string;\n                        b->last = ngx_http_ssi_string + ctx->saved;\n\n                        *ctx->last_out = cl;\n                        ctx->last_out = &cl->next;\n\n                        ctx->saved = 0;\n                    }\n\n                    if (ctx->free) {\n                        cl = ctx->free;\n                        ctx->free = ctx->free->next;\n                        b = cl->buf;\n\n                    } else {\n                        b = ngx_alloc_buf(r->pool);\n                        if (b == NULL) {\n                            return NGX_ERROR;\n                        }\n\n                        cl = ngx_alloc_chain_link(r->pool);\n                        if (cl == NULL) {\n                            return NGX_ERROR;\n                        }\n\n                        cl->buf = b;\n                    }\n\n                    ngx_memcpy(b, ctx->buf, sizeof(ngx_buf_t));\n\n                    b->pos = ctx->copy_start;\n                    b->last = ctx->copy_end;\n                    b->shadow = NULL;\n                    b->last_buf = 0;\n                    b->recycled = 0;\n\n                    if (b->in_file) {\n                        if (slcf->min_file_chunk < (size_t) (b->last - b->pos))\n                        {\n                            b->file_last = b->file_pos\n                                                   + (b->last - ctx->buf->pos);\n                            b->file_pos += b->pos - ctx->buf->pos;\n\n                        } else {\n                            b->in_file = 0;\n                        }\n                    }\n\n                    cl->next = NULL;\n                    *ctx->last_out = cl;\n                    ctx->last_out = &cl->next;\n\n                } else {\n                    if (ctx->block\n                        && ctx->saved + (ctx->copy_end - ctx->copy_start))\n                    {\n                        b = ngx_create_temp_buf(r->pool,\n                               ctx->saved + (ctx->copy_end - ctx->copy_start));\n\n                        if (b == NULL) {\n                            return NGX_ERROR;\n                        }\n\n                        if (ctx->saved) {\n                            b->last = ngx_cpymem(b->pos, ngx_http_ssi_string,\n                                                 ctx->saved);\n                        }\n\n                        b->last = ngx_cpymem(b->last, ctx->copy_start,\n                                             ctx->copy_end - ctx->copy_start);\n\n                        cl = ngx_alloc_chain_link(r->pool);\n                        if (cl == NULL) {\n                            return NGX_ERROR;\n                        }\n\n                        cl->buf = b;\n                        cl->next = NULL;\n\n                        b = NULL;\n\n                        mctx = ngx_http_get_module_ctx(r->main,\n                                                   ngx_http_ssi_filter_module);\n                        bl = mctx->blocks->elts;\n                        for (ll = &bl[mctx->blocks->nelts - 1].bufs;\n                             *ll;\n                             ll = &(*ll)->next)\n                        {\n                            /* void */\n                        }\n\n                        *ll = cl;\n                    }\n\n                    ctx->saved = 0;\n                }\n            }\n\n            if (ctx->state == ssi_start_state) {\n                ctx->copy_start = ctx->pos;\n                ctx->copy_end = ctx->pos;\n\n            } else {\n                ctx->copy_start = NULL;\n                ctx->copy_end = NULL;\n            }\n\n            if (rc == NGX_AGAIN) {\n                continue;\n            }\n\n\n            b = NULL;\n\n            if (rc == NGX_OK) {\n\n                smcf = ngx_http_get_module_main_conf(r,\n                                                   ngx_http_ssi_filter_module);\n\n                cmd = ngx_hash_find(&smcf->hash, ctx->key, ctx->command.data,\n                                    ctx->command.len);\n\n                if (cmd == NULL) {\n                    if (ctx->output) {\n                        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                                      \"invalid SSI command: \\\"%V\\\"\",\n                                      &ctx->command);\n                        goto ssi_error;\n                    }\n\n                    continue;\n                }\n\n                if (!ctx->output && !cmd->block) {\n\n                    if (ctx->block) {\n\n                        /* reconstruct the SSI command text */\n\n                        len = 5 + ctx->command.len + 4;\n\n                        param = ctx->params.elts;\n                        for (i = 0; i < ctx->params.nelts; i++) {\n                            len += 1 + param[i].key.len + 2\n                                + param[i].value.len + 1;\n                        }\n\n                        b = ngx_create_temp_buf(r->pool, len);\n\n                        if (b == NULL) {\n                            return NGX_ERROR;\n                        }\n\n                        cl = ngx_alloc_chain_link(r->pool);\n                        if (cl == NULL) {\n                            return NGX_ERROR;\n                        }\n\n                        cl->buf = b;\n                        cl->next = NULL;\n\n                        *b->last++ = '<';\n                        *b->last++ = '!';\n                        *b->last++ = '-';\n                        *b->last++ = '-';\n                        *b->last++ = '#';\n\n                        b->last = ngx_cpymem(b->last, ctx->command.data,\n                                             ctx->command.len);\n\n                        for (i = 0; i < ctx->params.nelts; i++) {\n                            *b->last++ = ' ';\n                            b->last = ngx_cpymem(b->last, param[i].key.data,\n                                                 param[i].key.len);\n                            *b->last++ = '=';\n                            *b->last++ = '\"';\n                            b->last = ngx_cpymem(b->last, param[i].value.data,\n                                                 param[i].value.len);\n                            *b->last++ = '\"';\n                        }\n\n                        *b->last++ = ' ';\n                        *b->last++ = '-';\n                        *b->last++ = '-';\n                        *b->last++ = '>';\n\n                        mctx = ngx_http_get_module_ctx(r->main,\n                                                   ngx_http_ssi_filter_module);\n                        bl = mctx->blocks->elts;\n                        for (ll = &bl[mctx->blocks->nelts - 1].bufs;\n                             *ll;\n                             ll = &(*ll)->next)\n                        {\n                            /* void */\n                        }\n\n                        *ll = cl;\n\n                        b = NULL;\n\n                        continue;\n                    }\n\n                    if (cmd->conditional == 0) {\n                        continue;\n                    }\n                }\n\n                if (cmd->conditional\n                    && (ctx->conditional == 0\n                        || ctx->conditional > cmd->conditional))\n                {\n                    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                                  \"invalid context of SSI command: \\\"%V\\\"\",\n                                  &ctx->command);\n                    goto ssi_error;\n                }\n\n                if (ctx->params.nelts > NGX_HTTP_SSI_MAX_PARAMS) {\n                    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                                  \"too many SSI command parameters: \\\"%V\\\"\",\n                                  &ctx->command);\n                    goto ssi_error;\n                }\n\n                ngx_memzero(params,\n                           (NGX_HTTP_SSI_MAX_PARAMS + 1) * sizeof(ngx_str_t *));\n\n                param = ctx->params.elts;\n\n                for (i = 0; i < ctx->params.nelts; i++) {\n\n                    for (prm = cmd->params; prm->name.len; prm++) {\n\n                        if (param[i].key.len != prm->name.len\n                            || ngx_strncmp(param[i].key.data, prm->name.data,\n                                           prm->name.len) != 0)\n                        {\n                            continue;\n                        }\n\n                        if (!prm->multiple) {\n                            if (params[prm->index]) {\n                                ngx_log_error(NGX_LOG_ERR,\n                                              r->connection->log, 0,\n                                              \"duplicate \\\"%V\\\" parameter \"\n                                              \"in \\\"%V\\\" SSI command\",\n                                              &param[i].key, &ctx->command);\n\n                                goto ssi_error;\n                            }\n\n                            params[prm->index] = &param[i].value;\n\n                            break;\n                        }\n\n                        for (index = prm->index; params[index]; index++) {\n                            /* void */\n                        }\n\n                        params[index] = &param[i].value;\n\n                        break;\n                    }\n\n                    if (prm->name.len == 0) {\n                        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                                      \"invalid parameter name: \\\"%V\\\" \"\n                                      \"in \\\"%V\\\" SSI command\",\n                                      &param[i].key, &ctx->command);\n\n                        goto ssi_error;\n                    }\n                }\n\n                for (prm = cmd->params; prm->name.len; prm++) {\n                    if (prm->mandatory && params[prm->index] == 0) {\n                        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                                      \"mandatory \\\"%V\\\" parameter is absent \"\n                                      \"in \\\"%V\\\" SSI command\",\n                                      &prm->name, &ctx->command);\n\n                        goto ssi_error;\n                    }\n                }\n\n                if (cmd->flush && ctx->out) {\n\n                    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                                   \"ssi flush\");\n\n                    if (ngx_http_ssi_output(r, ctx) == NGX_ERROR) {\n                        return NGX_ERROR;\n                    }\n                }\n\n                rc = cmd->handler(r, ctx, params);\n\n                if (rc == NGX_OK) {\n                    continue;\n                }\n\n                if (rc == NGX_DONE || rc == NGX_AGAIN || rc == NGX_ERROR) {\n                    ngx_http_ssi_buffered(r, ctx);\n                    return rc;\n                }\n            }\n\n\n            /* rc == NGX_HTTP_SSI_ERROR */\n\n    ssi_error:\n\n            if (slcf->silent_errors) {\n                continue;\n            }\n\n            if (ctx->free) {\n                cl = ctx->free;\n                ctx->free = ctx->free->next;\n                b = cl->buf;\n                ngx_memzero(b, sizeof(ngx_buf_t));\n\n            } else {\n                b = ngx_calloc_buf(r->pool);\n                if (b == NULL) {\n                    return NGX_ERROR;\n                }\n\n                cl = ngx_alloc_chain_link(r->pool);\n                if (cl == NULL) {\n                    return NGX_ERROR;\n                }\n\n                cl->buf = b;\n            }\n\n            b->memory = 1;\n            b->pos = ctx->errmsg.data;\n            b->last = ctx->errmsg.data + ctx->errmsg.len;\n\n            cl->next = NULL;\n            *ctx->last_out = cl;\n            ctx->last_out = &cl->next;\n\n            continue;\n        }\n\n        if (ctx->buf->last_buf || ngx_buf_in_memory(ctx->buf)) {\n            if (b == NULL) {\n                if (ctx->free) {\n                    cl = ctx->free;\n                    ctx->free = ctx->free->next;\n                    b = cl->buf;\n                    ngx_memzero(b, sizeof(ngx_buf_t));\n\n                } else {\n                    b = ngx_calloc_buf(r->pool);\n                    if (b == NULL) {\n                        return NGX_ERROR;\n                    }\n\n                    cl = ngx_alloc_chain_link(r->pool);\n                    if (cl == NULL) {\n                        return NGX_ERROR;\n                    }\n\n                    cl->buf = b;\n                }\n\n                b->sync = 1;\n\n                cl->next = NULL;\n                *ctx->last_out = cl;\n                ctx->last_out = &cl->next;\n            }\n\n            b->last_buf = ctx->buf->last_buf;\n            b->shadow = ctx->buf;\n\n            if (slcf->ignore_recycled_buffers == 0)  {\n                b->recycled = ctx->buf->recycled;\n            }\n        }\n\n        ctx->buf = NULL;\n\n        ctx->saved = ctx->looked;\n    }\n\n    if (ctx->out == NULL && ctx->busy == NULL) {\n        return NGX_OK;\n    }\n\n    return ngx_http_ssi_output(r, ctx);\n}\n\n\nstatic ngx_int_t\nngx_http_ssi_output(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx)\n{\n    ngx_int_t     rc;\n    ngx_buf_t    *b;\n    ngx_chain_t  *cl;\n\n#if 1\n    b = NULL;\n    for (cl = ctx->out; cl; cl = cl->next) {\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"ssi out: %p %p\", cl->buf, cl->buf->pos);\n        if (cl->buf == b) {\n            ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,\n                          \"the same buf was used in ssi\");\n            ngx_debug_point();\n            return NGX_ERROR;\n        }\n        b = cl->buf;\n    }\n#endif\n\n    rc = ngx_http_next_body_filter(r, ctx->out);\n\n    if (ctx->busy == NULL) {\n        ctx->busy = ctx->out;\n\n    } else {\n        for (cl = ctx->busy; cl->next; cl = cl->next) { /* void */ }\n        cl->next = ctx->out;\n    }\n\n    ctx->out = NULL;\n    ctx->last_out = &ctx->out;\n\n    while (ctx->busy) {\n\n        cl = ctx->busy;\n        b = cl->buf;\n\n        if (ngx_buf_size(b) != 0) {\n            break;\n        }\n\n        if (b->shadow) {\n            b->shadow->pos = b->shadow->last;\n        }\n\n        ctx->busy = cl->next;\n\n        if (ngx_buf_in_memory(b) || b->in_file) {\n            /* add data bufs only to the free buf chain */\n\n            cl->next = ctx->free;\n            ctx->free = cl;\n        }\n    }\n\n    ngx_http_ssi_buffered(r, ctx);\n\n    return rc;\n}\n\n\nstatic void\nngx_http_ssi_buffered(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx)\n{\n    if (ctx->in || ctx->buf) {\n        r->buffered |= NGX_HTTP_SSI_BUFFERED;\n\n    } else {\n        r->buffered &= ~NGX_HTTP_SSI_BUFFERED;\n    }\n}\n\n\nstatic ngx_int_t\nngx_http_ssi_parse(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx)\n{\n    u_char                *p, *value, *last, *copy_end, ch;\n    size_t                 looked;\n    ngx_http_ssi_state_e   state;\n\n    state = ctx->state;\n    looked = ctx->looked;\n    last = ctx->buf->last;\n    copy_end = ctx->copy_end;\n\n    for (p = ctx->pos; p < last; p++) {\n\n        ch = *p;\n\n        if (state == ssi_start_state) {\n\n            /* the tight loop */\n\n            for ( ;; ) {\n                if (ch == '<') {\n                    copy_end = p;\n                    looked = 1;\n                    state = ssi_tag_state;\n\n                    goto tag_started;\n                }\n\n                if (++p == last) {\n                    break;\n                }\n\n                ch = *p;\n            }\n\n            ctx->state = state;\n            ctx->pos = p;\n            ctx->looked = looked;\n            ctx->copy_end = p;\n\n            if (ctx->copy_start == NULL) {\n                ctx->copy_start = ctx->buf->pos;\n            }\n\n            return NGX_AGAIN;\n\n        tag_started:\n\n            continue;\n        }\n\n        switch (state) {\n\n        case ssi_start_state:\n            /* not reached */\n            break;\n\n        case ssi_tag_state:\n            switch (ch) {\n            case '!':\n                looked = 2;\n                state = ssi_comment0_state;\n                break;\n\n            case '<':\n                copy_end = p;\n                break;\n\n            default:\n                copy_end = p;\n                looked = 0;\n                state = ssi_start_state;\n                break;\n            }\n\n            break;\n\n        case ssi_comment0_state:\n            switch (ch) {\n            case '-':\n                looked = 3;\n                state = ssi_comment1_state;\n                break;\n\n            case '<':\n                copy_end = p;\n                looked = 1;\n                state = ssi_tag_state;\n                break;\n\n            default:\n                copy_end = p;\n                looked = 0;\n                state = ssi_start_state;\n                break;\n            }\n\n            break;\n\n        case ssi_comment1_state:\n            switch (ch) {\n            case '-':\n                looked = 4;\n                state = ssi_sharp_state;\n                break;\n\n            case '<':\n                copy_end = p;\n                looked = 1;\n                state = ssi_tag_state;\n                break;\n\n            default:\n                copy_end = p;\n                looked = 0;\n                state = ssi_start_state;\n                break;\n            }\n\n            break;\n\n        case ssi_sharp_state:\n            switch (ch) {\n            case '#':\n                if (p - ctx->pos < 4) {\n                    ctx->saved = 0;\n                }\n                looked = 0;\n                state = ssi_precommand_state;\n                break;\n\n            case '<':\n                copy_end = p;\n                looked = 1;\n                state = ssi_tag_state;\n                break;\n\n            default:\n                copy_end = p;\n                looked = 0;\n                state = ssi_start_state;\n                break;\n            }\n\n            break;\n\n        case ssi_precommand_state:\n            switch (ch) {\n            case ' ':\n            case CR:\n            case LF:\n            case '\\t':\n                break;\n\n            default:\n                ctx->command.len = 1;\n                ctx->command.data = ngx_pnalloc(r->pool,\n                                                NGX_HTTP_SSI_COMMAND_LEN);\n                if (ctx->command.data == NULL) {\n                    return NGX_ERROR;\n                }\n\n                ctx->command.data[0] = ch;\n\n                ctx->key = 0;\n                ctx->key = ngx_hash(ctx->key, ch);\n\n                ctx->params.nelts = 0;\n\n                state = ssi_command_state;\n                break;\n            }\n\n            break;\n\n        case ssi_command_state:\n            switch (ch) {\n            case ' ':\n            case CR:\n            case LF:\n            case '\\t':\n                state = ssi_preparam_state;\n                break;\n\n            case '-':\n                state = ssi_comment_end0_state;\n                break;\n\n            default:\n                if (ctx->command.len == NGX_HTTP_SSI_COMMAND_LEN) {\n                    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                                  \"the \\\"%V%c...\\\" SSI command is too long\",\n                                  &ctx->command, ch);\n\n                    state = ssi_error_state;\n                    break;\n                }\n\n                ctx->command.data[ctx->command.len++] = ch;\n                ctx->key = ngx_hash(ctx->key, ch);\n            }\n\n            break;\n\n        case ssi_preparam_state:\n            switch (ch) {\n            case ' ':\n            case CR:\n            case LF:\n            case '\\t':\n                break;\n\n            case '-':\n                state = ssi_comment_end0_state;\n                break;\n\n            default:\n                ctx->param = ngx_array_push(&ctx->params);\n                if (ctx->param == NULL) {\n                    return NGX_ERROR;\n                }\n\n                ctx->param->key.len = 1;\n                ctx->param->key.data = ngx_pnalloc(r->pool,\n                                                   NGX_HTTP_SSI_PARAM_LEN);\n                if (ctx->param->key.data == NULL) {\n                    return NGX_ERROR;\n                }\n\n                ctx->param->key.data[0] = ch;\n\n                ctx->param->value.len = 0;\n\n                if (ctx->value_buf == NULL) {\n                    ctx->param->value.data = ngx_pnalloc(r->pool,\n                                                         ctx->value_len + 1);\n                    if (ctx->param->value.data == NULL) {\n                        return NGX_ERROR;\n                    }\n\n                } else {\n                    ctx->param->value.data = ctx->value_buf;\n                }\n\n                state = ssi_param_state;\n                break;\n            }\n\n            break;\n\n        case ssi_param_state:\n            switch (ch) {\n            case ' ':\n            case CR:\n            case LF:\n            case '\\t':\n                state = ssi_preequal_state;\n                break;\n\n            case '=':\n                state = ssi_prevalue_state;\n                break;\n\n            case '-':\n                state = ssi_error_end0_state;\n\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                              \"unexpected \\\"-\\\" symbol after \\\"%V\\\" \"\n                              \"parameter in \\\"%V\\\" SSI command\",\n                              &ctx->param->key, &ctx->command);\n                break;\n\n            default:\n                if (ctx->param->key.len == NGX_HTTP_SSI_PARAM_LEN) {\n                    state = ssi_error_state;\n                    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                                  \"too long \\\"%V%c...\\\" parameter in \"\n                                  \"\\\"%V\\\" SSI command\",\n                                  &ctx->param->key, ch, &ctx->command);\n                    break;\n                }\n\n                ctx->param->key.data[ctx->param->key.len++] = ch;\n            }\n\n            break;\n\n        case ssi_preequal_state:\n            switch (ch) {\n            case ' ':\n            case CR:\n            case LF:\n            case '\\t':\n                break;\n\n            case '=':\n                state = ssi_prevalue_state;\n                break;\n\n            default:\n                if (ch == '-') {\n                    state = ssi_error_end0_state;\n                } else {\n                    state = ssi_error_state;\n                }\n\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                              \"unexpected \\\"%c\\\" symbol after \\\"%V\\\" \"\n                              \"parameter in \\\"%V\\\" SSI command\",\n                              ch, &ctx->param->key, &ctx->command);\n                break;\n            }\n\n            break;\n\n        case ssi_prevalue_state:\n            switch (ch) {\n            case ' ':\n            case CR:\n            case LF:\n            case '\\t':\n                break;\n\n            case '\"':\n                state = ssi_double_quoted_value_state;\n                break;\n\n            case '\\'':\n                state = ssi_quoted_value_state;\n                break;\n\n            default:\n                if (ch == '-') {\n                    state = ssi_error_end0_state;\n                } else {\n                    state = ssi_error_state;\n                }\n\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                              \"unexpected \\\"%c\\\" symbol before value of \"\n                              \"\\\"%V\\\" parameter in \\\"%V\\\" SSI command\",\n                              ch, &ctx->param->key, &ctx->command);\n                break;\n            }\n\n            break;\n\n        case ssi_double_quoted_value_state:\n            switch (ch) {\n            case '\"':\n                state = ssi_postparam_state;\n                break;\n\n            case '\\\\':\n                ctx->saved_state = ssi_double_quoted_value_state;\n                state = ssi_quoted_symbol_state;\n\n                /* fall through */\n\n            default:\n                if (ctx->param->value.len == ctx->value_len) {\n                    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                                  \"too long \\\"%V%c...\\\" value of \\\"%V\\\" \"\n                                  \"parameter in \\\"%V\\\" SSI command\",\n                                  &ctx->param->value, ch, &ctx->param->key,\n                                  &ctx->command);\n                    state = ssi_error_state;\n                    break;\n                }\n\n                ctx->param->value.data[ctx->param->value.len++] = ch;\n            }\n\n            break;\n\n        case ssi_quoted_value_state:\n            switch (ch) {\n            case '\\'':\n                state = ssi_postparam_state;\n                break;\n\n            case '\\\\':\n                ctx->saved_state = ssi_quoted_value_state;\n                state = ssi_quoted_symbol_state;\n\n                /* fall through */\n\n            default:\n                if (ctx->param->value.len == ctx->value_len) {\n                    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                                  \"too long \\\"%V%c...\\\" value of \\\"%V\\\" \"\n                                  \"parameter in \\\"%V\\\" SSI command\",\n                                  &ctx->param->value, ch, &ctx->param->key,\n                                  &ctx->command);\n                    state = ssi_error_state;\n                    break;\n                }\n\n                ctx->param->value.data[ctx->param->value.len++] = ch;\n            }\n\n            break;\n\n        case ssi_quoted_symbol_state:\n            state = ctx->saved_state;\n\n            if (ctx->param->value.len == ctx->value_len) {\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                              \"too long \\\"%V%c...\\\" value of \\\"%V\\\" \"\n                              \"parameter in \\\"%V\\\" SSI command\",\n                              &ctx->param->value, ch, &ctx->param->key,\n                              &ctx->command);\n                state = ssi_error_state;\n                break;\n            }\n\n            ctx->param->value.data[ctx->param->value.len++] = ch;\n\n            break;\n\n        case ssi_postparam_state:\n\n            if (ctx->param->value.len + 1 < ctx->value_len / 2) {\n                value = ngx_pnalloc(r->pool, ctx->param->value.len + 1);\n                if (value == NULL) {\n                    return NGX_ERROR;\n                }\n\n                ngx_memcpy(value, ctx->param->value.data,\n                           ctx->param->value.len);\n\n                ctx->value_buf = ctx->param->value.data;\n                ctx->param->value.data = value;\n\n            } else {\n                ctx->value_buf = NULL;\n            }\n\n            switch (ch) {\n            case ' ':\n            case CR:\n            case LF:\n            case '\\t':\n                state = ssi_preparam_state;\n                break;\n\n            case '-':\n                state = ssi_comment_end0_state;\n                break;\n\n            default:\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                              \"unexpected \\\"%c\\\" symbol after \\\"%V\\\" value \"\n                              \"of \\\"%V\\\" parameter in \\\"%V\\\" SSI command\",\n                              ch, &ctx->param->value, &ctx->param->key,\n                              &ctx->command);\n                state = ssi_error_state;\n                break;\n            }\n\n            break;\n\n        case ssi_comment_end0_state:\n            switch (ch) {\n            case '-':\n                state = ssi_comment_end1_state;\n                break;\n\n            default:\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                              \"unexpected \\\"%c\\\" symbol in \\\"%V\\\" SSI command\",\n                              ch, &ctx->command);\n                state = ssi_error_state;\n                break;\n            }\n\n            break;\n\n        case ssi_comment_end1_state:\n            switch (ch) {\n            case '>':\n                ctx->state = ssi_start_state;\n                ctx->pos = p + 1;\n                ctx->looked = looked;\n                ctx->copy_end = copy_end;\n\n                if (ctx->copy_start == NULL && copy_end) {\n                    ctx->copy_start = ctx->buf->pos;\n                }\n\n                return NGX_OK;\n\n            default:\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                              \"unexpected \\\"%c\\\" symbol in \\\"%V\\\" SSI command\",\n                              ch, &ctx->command);\n                state = ssi_error_state;\n                break;\n            }\n\n            break;\n\n        case ssi_error_state:\n            switch (ch) {\n            case '-':\n                state = ssi_error_end0_state;\n                break;\n\n            default:\n                break;\n            }\n\n            break;\n\n        case ssi_error_end0_state:\n            switch (ch) {\n            case '-':\n                state = ssi_error_end1_state;\n                break;\n\n            default:\n                state = ssi_error_state;\n                break;\n            }\n\n            break;\n\n        case ssi_error_end1_state:\n            switch (ch) {\n            case '>':\n                ctx->state = ssi_start_state;\n                ctx->pos = p + 1;\n                ctx->looked = looked;\n                ctx->copy_end = copy_end;\n\n                if (ctx->copy_start == NULL && copy_end) {\n                    ctx->copy_start = ctx->buf->pos;\n                }\n\n                return NGX_HTTP_SSI_ERROR;\n\n            default:\n                state = ssi_error_state;\n                break;\n            }\n\n            break;\n        }\n    }\n\n    ctx->state = state;\n    ctx->pos = p;\n    ctx->looked = looked;\n\n    ctx->copy_end = (state == ssi_start_state) ? p : copy_end;\n\n    if (ctx->copy_start == NULL && ctx->copy_end) {\n        ctx->copy_start = ctx->buf->pos;\n    }\n\n    return NGX_AGAIN;\n}\n\n\nstatic ngx_str_t *\nngx_http_ssi_get_variable(ngx_http_request_t *r, ngx_str_t *name,\n    ngx_uint_t key)\n{\n    ngx_uint_t           i;\n    ngx_list_part_t     *part;\n    ngx_http_ssi_var_t  *var;\n    ngx_http_ssi_ctx_t  *ctx;\n\n    ctx = ngx_http_get_module_ctx(r->main, ngx_http_ssi_filter_module);\n\n#if (NGX_PCRE)\n    {\n    ngx_str_t  *value;\n\n    if (key >= '0' && key <= '9') {\n        i = key - '0';\n\n        if (i < ctx->ncaptures) {\n            value = ngx_palloc(r->pool, sizeof(ngx_str_t));\n            if (value == NULL) {\n                return NULL;\n            }\n\n            i *= 2;\n\n            value->data = ctx->captures_data + ctx->captures[i];\n            value->len = ctx->captures[i + 1] - ctx->captures[i];\n\n            return value;\n        }\n    }\n    }\n#endif\n\n    if (ctx->variables == NULL) {\n        return NULL;\n    }\n\n    part = &ctx->variables->part;\n    var = 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            var = part->elts;\n            i = 0;\n        }\n\n        if (name->len != var[i].name.len) {\n            continue;\n        }\n\n        if (key != var[i].key) {\n            continue;\n        }\n\n        if (ngx_strncmp(name->data, var[i].name.data, name->len) == 0) {\n            return &var[i].value;\n        }\n    }\n\n    return NULL;\n}\n\n\nstatic ngx_int_t\nngx_http_ssi_evaluate_string(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,\n    ngx_str_t *text, ngx_uint_t flags)\n{\n    u_char                      ch, *p, **value, *data, *part_data;\n    size_t                     *size, len, prefix, part_len;\n    ngx_str_t                   var, *val;\n    ngx_uint_t                  i, n, bracket, quoted, key;\n    ngx_array_t                 lengths, values;\n    ngx_http_variable_value_t  *vv;\n\n    n = ngx_http_script_variables_count(text);\n\n    if (n == 0) {\n\n        data = text->data;\n        p = data;\n\n        if ((flags & NGX_HTTP_SSI_ADD_PREFIX) && text->data[0] != '/') {\n\n            for (prefix = r->uri.len; prefix; prefix--) {\n                if (r->uri.data[prefix - 1] == '/') {\n                    break;\n                }\n            }\n\n            if (prefix) {\n                len = prefix + text->len;\n\n                data = ngx_pnalloc(r->pool, len);\n                if (data == NULL) {\n                    return NGX_ERROR;\n                }\n\n                p = ngx_copy(data, r->uri.data, prefix);\n            }\n        }\n\n        quoted = 0;\n\n        for (i = 0; i < text->len; i++) {\n            ch = text->data[i];\n\n            if (!quoted) {\n\n                if (ch == '\\\\') {\n                    quoted = 1;\n                    continue;\n                }\n\n            } else {\n                quoted = 0;\n\n                if (ch != '\\\\' && ch != '\\'' && ch != '\"' && ch != '$') {\n                    *p++ = '\\\\';\n                }\n            }\n\n            *p++ = ch;\n        }\n\n        text->len = p - data;\n        text->data = data;\n\n        return NGX_OK;\n    }\n\n    if (ngx_array_init(&lengths, r->pool, 8, sizeof(size_t *)) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    if (ngx_array_init(&values, r->pool, 8, sizeof(u_char *)) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    len = 0;\n    i = 0;\n\n    while (i < text->len) {\n\n        if (text->data[i] == '$') {\n\n            var.len = 0;\n\n            if (++i == text->len) {\n                goto invalid_variable;\n            }\n\n            if (text->data[i] == '{') {\n                bracket = 1;\n\n                if (++i == text->len) {\n                    goto invalid_variable;\n                }\n\n                var.data = &text->data[i];\n\n            } else {\n                bracket = 0;\n                var.data = &text->data[i];\n            }\n\n            for ( /* void */ ; i < text->len; i++, var.len++) {\n                ch = text->data[i];\n\n                if (ch == '}' && bracket) {\n                    i++;\n                    bracket = 0;\n                    break;\n                }\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, r->connection->log, 0,\n                              \"the closing bracket in \\\"%V\\\" \"\n                              \"variable is missing\", &var);\n                return NGX_HTTP_SSI_ERROR;\n            }\n\n            if (var.len == 0) {\n                goto invalid_variable;\n            }\n\n            key = ngx_hash_strlow(var.data, var.data, var.len);\n\n            val = ngx_http_ssi_get_variable(r, &var, key);\n\n            if (val == NULL) {\n                vv = ngx_http_get_variable(r, &var, key);\n                if (vv == NULL) {\n                    return NGX_ERROR;\n                }\n\n                if (vv->not_found) {\n                    continue;\n                }\n\n                part_data = vv->data;\n                part_len = vv->len;\n\n            } else {\n                part_data = val->data;\n                part_len = val->len;\n            }\n\n        } else {\n            part_data = &text->data[i];\n            quoted = 0;\n\n            for (p = part_data; i < text->len; i++) {\n                ch = text->data[i];\n\n                if (!quoted) {\n\n                    if (ch == '\\\\') {\n                        quoted = 1;\n                        continue;\n                    }\n\n                    if (ch == '$') {\n                        break;\n                    }\n\n                } else {\n                    quoted = 0;\n\n                    if (ch != '\\\\' && ch != '\\'' && ch != '\"' && ch != '$') {\n                        *p++ = '\\\\';\n                    }\n                }\n\n                *p++ = ch;\n            }\n\n            part_len = p - part_data;\n        }\n\n        len += part_len;\n\n        size = ngx_array_push(&lengths);\n        if (size == NULL) {\n            return NGX_ERROR;\n        }\n\n        *size = part_len;\n\n        value = ngx_array_push(&values);\n        if (value == NULL) {\n            return NGX_ERROR;\n        }\n\n        *value = part_data;\n    }\n\n    prefix = 0;\n\n    size = lengths.elts;\n    value = values.elts;\n\n    if (flags & NGX_HTTP_SSI_ADD_PREFIX) {\n        for (i = 0; i < values.nelts; i++) {\n            if (size[i] != 0) {\n                if (*value[i] != '/') {\n                    for (prefix = r->uri.len; prefix; prefix--) {\n                        if (r->uri.data[prefix - 1] == '/') {\n                            len += prefix;\n                            break;\n                        }\n                    }\n                }\n\n                break;\n            }\n        }\n    }\n\n    p = ngx_pnalloc(r->pool, len + ((flags & NGX_HTTP_SSI_ADD_ZERO) ? 1 : 0));\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    text->len = len;\n    text->data = p;\n\n    p = ngx_copy(p, r->uri.data, prefix);\n\n    for (i = 0; i < values.nelts; i++) {\n        p = ngx_copy(p, value[i], size[i]);\n    }\n\n    return NGX_OK;\n\ninvalid_variable:\n\n    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                  \"invalid variable name in \\\"%V\\\"\", text);\n\n    return NGX_HTTP_SSI_ERROR;\n}\n\n\nstatic ngx_int_t\nngx_http_ssi_regex_match(ngx_http_request_t *r, ngx_str_t *pattern,\n    ngx_str_t *str)\n{\n#if (NGX_PCRE)\n    int                   rc, *captures;\n    u_char               *p, errstr[NGX_MAX_CONF_ERRSTR];\n    size_t                size;\n    ngx_str_t            *vv, name, value;\n    ngx_uint_t            i, n, key;\n    ngx_http_ssi_ctx_t   *ctx;\n    ngx_http_ssi_var_t   *var;\n    ngx_regex_compile_t   rgc;\n\n    ngx_memzero(&rgc, sizeof(ngx_regex_compile_t));\n\n    rgc.pattern = *pattern;\n    rgc.pool = r->pool;\n    rgc.err.len = NGX_MAX_CONF_ERRSTR;\n    rgc.err.data = errstr;\n\n    if (ngx_regex_compile(&rgc) != NGX_OK) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, \"%V\", &rgc.err);\n        return NGX_HTTP_SSI_ERROR;\n    }\n\n    n = (rgc.captures + 1) * 3;\n\n    captures = ngx_palloc(r->pool, n * sizeof(int));\n    if (captures == NULL) {\n        return NGX_ERROR;\n    }\n\n    rc = ngx_regex_exec(rgc.regex, str, captures, n);\n\n    if (rc < NGX_REGEX_NO_MATCHED) {\n        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,\n                      ngx_regex_exec_n \" failed: %d on \\\"%V\\\" using \\\"%V\\\"\",\n                      rc, str, pattern);\n        return NGX_HTTP_SSI_ERROR;\n    }\n\n    if (rc == NGX_REGEX_NO_MATCHED) {\n        return NGX_DECLINED;\n    }\n\n    ctx = ngx_http_get_module_ctx(r->main, ngx_http_ssi_filter_module);\n\n    ctx->ncaptures = rc;\n    ctx->captures = captures;\n    ctx->captures_data = str->data;\n\n    if (rgc.named_captures > 0) {\n\n        if (ctx->variables == NULL) {\n            ctx->variables = ngx_list_create(r->pool, 4,\n                                             sizeof(ngx_http_ssi_var_t));\n            if (ctx->variables == NULL) {\n                return NGX_ERROR;\n            }\n        }\n\n        size = rgc.name_size;\n        p = rgc.names;\n\n        for (i = 0; i < (ngx_uint_t) rgc.named_captures; i++, p += size) {\n\n            name.data = &p[2];\n            name.len = ngx_strlen(name.data);\n\n            n = 2 * ((p[0] << 8) + p[1]);\n\n            value.data = &str->data[captures[n]];\n            value.len = captures[n + 1] - captures[n];\n\n            key = ngx_hash_strlow(name.data, name.data, name.len);\n\n            vv = ngx_http_ssi_get_variable(r, &name, key);\n\n            if (vv) {\n                *vv = value;\n                continue;\n            }\n\n            var = ngx_list_push(ctx->variables);\n            if (var == NULL) {\n                return NGX_ERROR;\n            }\n\n            var->name = name;\n            var->key = key;\n            var->value = value;\n        }\n    }\n\n    return NGX_OK;\n\n#else\n\n    ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,\n                  \"the using of the regex \\\"%V\\\" in SSI requires PCRE library\",\n                  pattern);\n    return NGX_HTTP_SSI_ERROR;\n\n#endif\n}\n\n\nstatic ngx_int_t\nngx_http_ssi_include(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,\n    ngx_str_t **params)\n{\n    ngx_int_t                    rc;\n    ngx_str_t                   *uri, *file, *wait, *set, *stub, args;\n    ngx_buf_t                   *b;\n    ngx_uint_t                   flags, i, key;\n    ngx_chain_t                 *cl, *tl, **ll, *out;\n    ngx_http_request_t          *sr;\n    ngx_http_ssi_var_t          *var;\n    ngx_http_ssi_ctx_t          *mctx;\n    ngx_http_ssi_block_t        *bl;\n    ngx_http_post_subrequest_t  *psr;\n\n    uri = params[NGX_HTTP_SSI_INCLUDE_VIRTUAL];\n    file = params[NGX_HTTP_SSI_INCLUDE_FILE];\n    wait = params[NGX_HTTP_SSI_INCLUDE_WAIT];\n    set = params[NGX_HTTP_SSI_INCLUDE_SET];\n    stub = params[NGX_HTTP_SSI_INCLUDE_STUB];\n\n    if (uri && file) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"inclusion may be either virtual=\\\"%V\\\" or file=\\\"%V\\\"\",\n                      uri, file);\n        return NGX_HTTP_SSI_ERROR;\n    }\n\n    if (uri == NULL && file == NULL) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"no parameter in \\\"include\\\" SSI command\");\n        return NGX_HTTP_SSI_ERROR;\n    }\n\n    if (set && stub) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"\\\"set\\\" and \\\"stub\\\" cannot be used together \"\n                      \"in \\\"include\\\" SSI command\");\n        return NGX_HTTP_SSI_ERROR;\n    }\n\n    if (wait) {\n        if (uri == NULL) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"\\\"wait\\\" cannot be used with file=\\\"%V\\\"\", file);\n            return NGX_HTTP_SSI_ERROR;\n        }\n\n        if (wait->len == 2\n            && ngx_strncasecmp(wait->data, (u_char *) \"no\", 2) == 0)\n        {\n            wait = NULL;\n\n        } else if (wait->len != 3\n                   || ngx_strncasecmp(wait->data, (u_char *) \"yes\", 3) != 0)\n        {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"invalid value \\\"%V\\\" in the \\\"wait\\\" parameter\",\n                          wait);\n            return NGX_HTTP_SSI_ERROR;\n        }\n    }\n\n    if (uri == NULL) {\n        uri = file;\n        wait = (ngx_str_t *) -1;\n    }\n\n    rc = ngx_http_ssi_evaluate_string(r, ctx, uri, NGX_HTTP_SSI_ADD_PREFIX);\n\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"ssi include: \\\"%V\\\"\", uri);\n\n    ngx_str_null(&args);\n    flags = NGX_HTTP_LOG_UNSAFE;\n\n    if (ngx_http_parse_unsafe_uri(r, uri, &args, &flags) != NGX_OK) {\n        return NGX_HTTP_SSI_ERROR;\n    }\n\n    psr = NULL;\n\n    mctx = ngx_http_get_module_ctx(r->main, ngx_http_ssi_filter_module);\n\n    if (stub) {\n        if (mctx->blocks) {\n            bl = mctx->blocks->elts;\n            for (i = 0; i < mctx->blocks->nelts; i++) {\n                if (stub->len == bl[i].name.len\n                    && ngx_strncmp(stub->data, bl[i].name.data, stub->len) == 0)\n                {\n                    goto found;\n                }\n            }\n        }\n\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"\\\"stub\\\"=\\\"%V\\\" for \\\"include\\\" not found\", stub);\n        return NGX_HTTP_SSI_ERROR;\n\n    found:\n\n        psr = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t));\n        if (psr == NULL) {\n            return NGX_ERROR;\n        }\n\n        psr->handler = ngx_http_ssi_stub_output;\n\n        if (bl[i].count++) {\n\n            out = NULL;\n            ll = &out;\n\n            for (tl = bl[i].bufs; tl; tl = tl->next) {\n\n                if (ctx->free) {\n                    cl = ctx->free;\n                    ctx->free = ctx->free->next;\n                    b = cl->buf;\n\n                } else {\n                    b = ngx_alloc_buf(r->pool);\n                    if (b == NULL) {\n                        return NGX_ERROR;\n                    }\n\n                    cl = ngx_alloc_chain_link(r->pool);\n                    if (cl == NULL) {\n                        return NGX_ERROR;\n                    }\n\n                    cl->buf = b;\n                }\n\n                ngx_memcpy(b, tl->buf, sizeof(ngx_buf_t));\n\n                b->pos = b->start;\n\n                *ll = cl;\n                cl->next = NULL;\n                ll = &cl->next;\n            }\n\n            psr->data = out;\n\n        } else {\n            psr->data = bl[i].bufs;\n        }\n    }\n\n    if (wait) {\n        flags |= NGX_HTTP_SUBREQUEST_WAITED;\n    }\n\n    if (set) {\n        key = ngx_hash_strlow(set->data, set->data, set->len);\n\n        psr = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t));\n        if (psr == NULL) {\n            return NGX_ERROR;\n        }\n\n        psr->handler = ngx_http_ssi_set_variable;\n        psr->data = ngx_http_ssi_get_variable(r, set, key);\n\n        if (psr->data == NULL) {\n\n            if (mctx->variables == NULL) {\n                mctx->variables = ngx_list_create(r->pool, 4,\n                                                  sizeof(ngx_http_ssi_var_t));\n                if (mctx->variables == NULL) {\n                    return NGX_ERROR;\n                }\n            }\n\n            var = ngx_list_push(mctx->variables);\n            if (var == NULL) {\n                return NGX_ERROR;\n            }\n\n            var->name = *set;\n            var->key = key;\n            var->value = ngx_http_ssi_null_string;\n            psr->data = &var->value;\n        }\n\n        flags |= NGX_HTTP_SUBREQUEST_IN_MEMORY|NGX_HTTP_SUBREQUEST_WAITED;\n    }\n\n    if (ngx_http_subrequest(r, uri, &args, &sr, psr, flags) != NGX_OK) {\n        return NGX_HTTP_SSI_ERROR;\n    }\n\n    if (wait == NULL && set == NULL) {\n        return NGX_OK;\n    }\n\n    if (ctx->wait == NULL) {\n        ctx->wait = sr;\n\n        return NGX_AGAIN;\n\n    } else {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"can only wait for one subrequest at a time\");\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_ssi_stub_output(ngx_http_request_t *r, void *data, ngx_int_t rc)\n{\n    ngx_chain_t  *out;\n\n    if (rc == NGX_ERROR || r->connection->error || r->request_output) {\n        return rc;\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"ssi stub output: \\\"%V?%V\\\"\", &r->uri, &r->args);\n\n    out = data;\n\n    if (!r->header_sent) {\n        r->headers_out.content_type_len =\n                                      r->parent->headers_out.content_type_len;\n        r->headers_out.content_type = r->parent->headers_out.content_type;\n\n        if (ngx_http_send_header(r) == NGX_ERROR) {\n            return NGX_ERROR;\n        }\n    }\n\n    return ngx_http_output_filter(r, out);\n}\n\n\nstatic ngx_int_t\nngx_http_ssi_set_variable(ngx_http_request_t *r, void *data, ngx_int_t rc)\n{\n    ngx_str_t  *value = data;\n\n    if (r->headers_out.status < NGX_HTTP_SPECIAL_RESPONSE\n        && r->out && r->out->buf)\n    {\n        value->len = r->out->buf->last - r->out->buf->pos;\n        value->data = r->out->buf->pos;\n    }\n\n    return rc;\n}\n\n\nstatic ngx_int_t\nngx_http_ssi_echo(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,\n    ngx_str_t **params)\n{\n    u_char                     *p;\n    uintptr_t                   len;\n    ngx_buf_t                  *b;\n    ngx_str_t                  *var, *value, *enc, text;\n    ngx_uint_t                  key;\n    ngx_chain_t                *cl;\n    ngx_http_variable_value_t  *vv;\n\n    var = params[NGX_HTTP_SSI_ECHO_VAR];\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"ssi echo \\\"%V\\\"\", var);\n\n    key = ngx_hash_strlow(var->data, var->data, var->len);\n\n    value = ngx_http_ssi_get_variable(r, var, key);\n\n    if (value == NULL) {\n        vv = ngx_http_get_variable(r, var, key);\n\n        if (vv == NULL) {\n            return NGX_HTTP_SSI_ERROR;\n        }\n\n        if (!vv->not_found) {\n            text.data = vv->data;\n            text.len = vv->len;\n            value = &text;\n        }\n    }\n\n    if (value == NULL) {\n        value = params[NGX_HTTP_SSI_ECHO_DEFAULT];\n\n        if (value == NULL) {\n            value = &ngx_http_ssi_none;\n\n        } else if (value->len == 0) {\n            return NGX_OK;\n        }\n\n    } else {\n        if (value->len == 0) {\n            return NGX_OK;\n        }\n    }\n\n    enc = params[NGX_HTTP_SSI_ECHO_ENCODING];\n\n    if (enc) {\n        if (enc->len == 4 && ngx_strncmp(enc->data, \"none\", 4) == 0) {\n\n            ctx->encoding = NGX_HTTP_SSI_NO_ENCODING;\n\n        } else if (enc->len == 3 && ngx_strncmp(enc->data, \"url\", 3) == 0) {\n\n            ctx->encoding = NGX_HTTP_SSI_URL_ENCODING;\n\n        } else if (enc->len == 6 && ngx_strncmp(enc->data, \"entity\", 6) == 0) {\n\n            ctx->encoding = NGX_HTTP_SSI_ENTITY_ENCODING;\n\n        } else {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"unknown encoding \\\"%V\\\" in the \\\"echo\\\" command\",\n                          enc);\n        }\n    }\n\n    p = value->data;\n\n    switch (ctx->encoding) {\n\n    case NGX_HTTP_SSI_URL_ENCODING:\n        len = 2 * ngx_escape_uri(NULL, value->data, value->len,\n                                 NGX_ESCAPE_HTML);\n\n        if (len) {\n            p = ngx_pnalloc(r->pool, value->len + len);\n            if (p == NULL) {\n                return NGX_HTTP_SSI_ERROR;\n            }\n\n            (void) ngx_escape_uri(p, value->data, value->len, NGX_ESCAPE_HTML);\n        }\n\n        len += value->len;\n        break;\n\n    case NGX_HTTP_SSI_ENTITY_ENCODING:\n        len = ngx_escape_html(NULL, value->data, value->len);\n\n        if (len) {\n            p = ngx_pnalloc(r->pool, value->len + len);\n            if (p == NULL) {\n                return NGX_HTTP_SSI_ERROR;\n            }\n\n            (void) ngx_escape_html(p, value->data, value->len);\n        }\n\n        len += value->len;\n        break;\n\n    default: /* NGX_HTTP_SSI_NO_ENCODING */\n        len = value->len;\n        break;\n    }\n\n    b = ngx_calloc_buf(r->pool);\n    if (b == NULL) {\n        return NGX_HTTP_SSI_ERROR;\n    }\n\n    cl = ngx_alloc_chain_link(r->pool);\n    if (cl == NULL) {\n        return NGX_HTTP_SSI_ERROR;\n    }\n\n    b->memory = 1;\n    b->pos = p;\n    b->last = p + len;\n\n    cl->buf = b;\n    cl->next = NULL;\n    *ctx->last_out = cl;\n    ctx->last_out = &cl->next;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_ssi_config(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,\n    ngx_str_t **params)\n{\n    ngx_str_t  *value;\n\n    value = params[NGX_HTTP_SSI_CONFIG_TIMEFMT];\n\n    if (value) {\n        ctx->timefmt.len = value->len;\n        ctx->timefmt.data = ngx_pnalloc(r->pool, value->len + 1);\n        if (ctx->timefmt.data == NULL) {\n            return NGX_ERROR;\n        }\n\n        ngx_cpystrn(ctx->timefmt.data, value->data, value->len + 1);\n    }\n\n    value = params[NGX_HTTP_SSI_CONFIG_ERRMSG];\n\n    if (value) {\n        ctx->errmsg = *value;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_ssi_set(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,\n    ngx_str_t **params)\n{\n    ngx_int_t            rc;\n    ngx_str_t           *name, *value, *vv;\n    ngx_uint_t           key;\n    ngx_http_ssi_var_t  *var;\n    ngx_http_ssi_ctx_t  *mctx;\n\n    mctx = ngx_http_get_module_ctx(r->main, ngx_http_ssi_filter_module);\n\n    if (mctx->variables == NULL) {\n        mctx->variables = ngx_list_create(r->pool, 4,\n                                          sizeof(ngx_http_ssi_var_t));\n        if (mctx->variables == NULL) {\n            return NGX_ERROR;\n        }\n    }\n\n    name = params[NGX_HTTP_SSI_SET_VAR];\n    value = params[NGX_HTTP_SSI_SET_VALUE];\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"ssi set \\\"%V\\\" \\\"%V\\\"\", name, value);\n\n    rc = ngx_http_ssi_evaluate_string(r, ctx, value, 0);\n\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    key = ngx_hash_strlow(name->data, name->data, name->len);\n\n    vv = ngx_http_ssi_get_variable(r, name, key);\n\n    if (vv) {\n        *vv = *value;\n        return NGX_OK;\n    }\n\n    var = ngx_list_push(mctx->variables);\n    if (var == NULL) {\n        return NGX_ERROR;\n    }\n\n    var->name = *name;\n    var->key = key;\n    var->value = *value;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"set: \\\"%V\\\"=\\\"%V\\\"\", name, value);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_ssi_if(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,\n    ngx_str_t **params)\n{\n    u_char       *p, *last;\n    ngx_str_t    *expr, left, right;\n    ngx_int_t     rc;\n    ngx_uint_t    negative, noregex, flags;\n\n    if (ctx->command.len == 2) {\n        if (ctx->conditional) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"the \\\"if\\\" command inside the \\\"if\\\" command\");\n            return NGX_HTTP_SSI_ERROR;\n        }\n    }\n\n    if (ctx->output_chosen) {\n        ctx->output = 0;\n        return NGX_OK;\n    }\n\n    expr = params[NGX_HTTP_SSI_IF_EXPR];\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"ssi if expr=\\\"%V\\\"\", expr);\n\n    left.data = expr->data;\n    last = expr->data + expr->len;\n\n    for (p = left.data; p < last; p++) {\n        if (*p >= 'A' && *p <= 'Z') {\n            *p |= 0x20;\n            continue;\n        }\n\n        if ((*p >= 'a' && *p <= 'z')\n             || (*p >= '0' && *p <= '9')\n             || *p == '$' || *p == '{' || *p == '}' || *p == '_'\n             || *p == '\"' || *p == '\\'')\n        {\n            continue;\n        }\n\n        break;\n    }\n\n    left.len = p - left.data;\n\n    while (p < last && *p == ' ') {\n        p++;\n    }\n\n    flags = 0;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"left: \\\"%V\\\"\", &left);\n\n    rc = ngx_http_ssi_evaluate_string(r, ctx, &left, flags);\n\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"evaluated left: \\\"%V\\\"\", &left);\n\n    if (p == last) {\n        if (left.len) {\n            ctx->output = 1;\n            ctx->output_chosen = 1;\n\n        } else {\n            ctx->output = 0;\n        }\n\n        ctx->conditional = NGX_HTTP_SSI_COND_IF;\n\n        return NGX_OK;\n    }\n\n    if (p < last && *p == '=') {\n        negative = 0;\n        p++;\n\n    } else if (p + 1 < last && *p == '!' && *(p + 1) == '=') {\n        negative = 1;\n        p += 2;\n\n    } else {\n        goto invalid_expression;\n    }\n\n    while (p < last && *p == ' ') {\n        p++;\n    }\n\n    if (p < last - 1 && *p == '/') {\n        if (*(last - 1) != '/') {\n            goto invalid_expression;\n        }\n\n        noregex = 0;\n        flags = NGX_HTTP_SSI_ADD_ZERO;\n        last--;\n        p++;\n\n    } else {\n        noregex = 1;\n        flags = 0;\n\n        if (p < last - 1 && p[0] == '\\\\' && p[1] == '/') {\n            p++;\n        }\n    }\n\n    right.len = last - p;\n    right.data = p;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"right: \\\"%V\\\"\", &right);\n\n    rc = ngx_http_ssi_evaluate_string(r, ctx, &right, flags);\n\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"evaluated right: \\\"%V\\\"\", &right);\n\n    if (noregex) {\n        if (left.len != right.len) {\n            rc = -1;\n\n        } else {\n            rc = ngx_strncmp(left.data, right.data, right.len);\n        }\n\n    } else {\n        right.data[right.len] = '\\0';\n\n        rc = ngx_http_ssi_regex_match(r, &right, &left);\n\n        if (rc == NGX_OK) {\n            rc = 0;\n        } else if (rc == NGX_DECLINED) {\n            rc = -1;\n        } else {\n            return rc;\n        }\n    }\n\n    if ((rc == 0 && !negative) || (rc != 0 && negative)) {\n        ctx->output = 1;\n        ctx->output_chosen = 1;\n\n    } else {\n        ctx->output = 0;\n    }\n\n    ctx->conditional = NGX_HTTP_SSI_COND_IF;\n\n    return NGX_OK;\n\ninvalid_expression:\n\n    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                  \"invalid expression in \\\"%V\\\"\", expr);\n\n    return NGX_HTTP_SSI_ERROR;\n}\n\n\nstatic ngx_int_t\nngx_http_ssi_else(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,\n    ngx_str_t **params)\n{\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"ssi else\");\n\n    if (ctx->output_chosen) {\n        ctx->output = 0;\n    } else {\n        ctx->output = 1;\n    }\n\n    ctx->conditional = NGX_HTTP_SSI_COND_ELSE;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_ssi_endif(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,\n    ngx_str_t **params)\n{\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"ssi endif\");\n\n    ctx->output = 1;\n    ctx->output_chosen = 0;\n    ctx->conditional = 0;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_ssi_block(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,\n    ngx_str_t **params)\n{\n    ngx_http_ssi_ctx_t    *mctx;\n    ngx_http_ssi_block_t  *bl;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"ssi block\");\n\n    mctx = ngx_http_get_module_ctx(r->main, ngx_http_ssi_filter_module);\n\n    if (mctx->blocks == NULL) {\n        mctx->blocks = ngx_array_create(r->pool, 4,\n                                        sizeof(ngx_http_ssi_block_t));\n        if (mctx->blocks == NULL) {\n            return NGX_HTTP_SSI_ERROR;\n        }\n    }\n\n    bl = ngx_array_push(mctx->blocks);\n    if (bl == NULL) {\n        return NGX_HTTP_SSI_ERROR;\n    }\n\n    bl->name = *params[NGX_HTTP_SSI_BLOCK_NAME];\n    bl->bufs = NULL;\n    bl->count = 0;\n\n    ctx->output = 0;\n    ctx->block = 1;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_ssi_endblock(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ctx,\n    ngx_str_t **params)\n{\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"ssi endblock\");\n\n    ctx->output = 1;\n    ctx->block = 0;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_ssi_date_gmt_local_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t gmt)\n{\n    time_t               now;\n    ngx_http_ssi_ctx_t  *ctx;\n    ngx_str_t           *timefmt;\n    struct tm            tm;\n    char                 buf[NGX_HTTP_SSI_DATE_LEN];\n\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    now = ngx_time();\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_ssi_filter_module);\n\n    timefmt = ctx ? &ctx->timefmt : &ngx_http_ssi_timefmt;\n\n    if (timefmt->len == sizeof(\"%s\") - 1\n        && timefmt->data[0] == '%' && timefmt->data[1] == 's')\n    {\n        v->data = ngx_pnalloc(r->pool, NGX_TIME_T_LEN);\n        if (v->data == NULL) {\n            return NGX_ERROR;\n        }\n\n        v->len = ngx_sprintf(v->data, \"%T\", now) - v->data;\n\n        return NGX_OK;\n    }\n\n    if (gmt) {\n        ngx_libc_gmtime(now, &tm);\n    } else {\n        ngx_libc_localtime(now, &tm);\n    }\n\n    v->len = strftime(buf, NGX_HTTP_SSI_DATE_LEN,\n                      (char *) timefmt->data, &tm);\n    if (v->len == 0) {\n        return NGX_ERROR;\n    }\n\n    v->data = ngx_pnalloc(r->pool, v->len);\n    if (v->data == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_memcpy(v->data, buf, v->len);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_ssi_preconfiguration(ngx_conf_t *cf)\n{\n    ngx_int_t                  rc;\n    ngx_http_variable_t       *var, *v;\n    ngx_http_ssi_command_t    *cmd;\n    ngx_http_ssi_main_conf_t  *smcf;\n\n    for (v = ngx_http_ssi_vars; v->name.len; v++) {\n        var = ngx_http_add_variable(cf, &v->name, v->flags);\n        if (var == NULL) {\n            return NGX_ERROR;\n        }\n\n        var->get_handler = v->get_handler;\n        var->data = v->data;\n    }\n\n    smcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_ssi_filter_module);\n\n    for (cmd = ngx_http_ssi_commands; cmd->name.len; cmd++) {\n        rc = ngx_hash_add_key(&smcf->commands, &cmd->name, cmd,\n                              NGX_HASH_READONLY_KEY);\n\n        if (rc == NGX_OK) {\n            continue;\n        }\n\n        if (rc == NGX_BUSY) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"conflicting SSI command \\\"%V\\\"\", &cmd->name);\n        }\n\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void *\nngx_http_ssi_create_main_conf(ngx_conf_t *cf)\n{\n    ngx_http_ssi_main_conf_t  *smcf;\n\n    smcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_ssi_main_conf_t));\n    if (smcf == NULL) {\n        return NULL;\n    }\n\n    smcf->commands.pool = cf->pool;\n    smcf->commands.temp_pool = cf->temp_pool;\n\n    if (ngx_hash_keys_array_init(&smcf->commands, NGX_HASH_SMALL) != NGX_OK) {\n        return NULL;\n    }\n\n    return smcf;\n}\n\n\nstatic char *\nngx_http_ssi_init_main_conf(ngx_conf_t *cf, void *conf)\n{\n    ngx_http_ssi_main_conf_t *smcf = conf;\n\n    ngx_hash_init_t  hash;\n\n    hash.hash = &smcf->hash;\n    hash.key = ngx_hash_key;\n    hash.max_size = 1024;\n    hash.bucket_size = ngx_cacheline_size;\n    hash.name = \"ssi_command_hash\";\n    hash.pool = cf->pool;\n    hash.temp_pool = NULL;\n\n    if (ngx_hash_init(&hash, smcf->commands.keys.elts,\n                      smcf->commands.keys.nelts)\n        != NGX_OK)\n    {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic void *\nngx_http_ssi_create_loc_conf(ngx_conf_t *cf)\n{\n    ngx_http_ssi_loc_conf_t  *slcf;\n\n    slcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_ssi_loc_conf_t));\n    if (slcf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc():\n     *\n     *     conf->types = { NULL };\n     *     conf->types_keys = NULL;\n     */\n\n    slcf->enable = NGX_CONF_UNSET;\n    slcf->silent_errors = NGX_CONF_UNSET;\n    slcf->ignore_recycled_buffers = NGX_CONF_UNSET;\n    slcf->last_modified = NGX_CONF_UNSET;\n\n    slcf->min_file_chunk = NGX_CONF_UNSET_SIZE;\n    slcf->value_len = NGX_CONF_UNSET_SIZE;\n\n    return slcf;\n}\n\n\nstatic char *\nngx_http_ssi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_http_ssi_loc_conf_t *prev = parent;\n    ngx_http_ssi_loc_conf_t *conf = child;\n\n    ngx_conf_merge_value(conf->enable, prev->enable, 0);\n    ngx_conf_merge_value(conf->silent_errors, prev->silent_errors, 0);\n    ngx_conf_merge_value(conf->ignore_recycled_buffers,\n                         prev->ignore_recycled_buffers, 0);\n    ngx_conf_merge_value(conf->last_modified, prev->last_modified, 0);\n\n    ngx_conf_merge_size_value(conf->min_file_chunk, prev->min_file_chunk, 1024);\n    ngx_conf_merge_size_value(conf->value_len, prev->value_len, 255);\n\n    if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,\n                             &prev->types_keys, &prev->types,\n                             ngx_http_html_default_types)\n        != NGX_OK)\n    {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_ssi_filter_init(ngx_conf_t *cf)\n{\n    ngx_http_next_header_filter = ngx_http_top_header_filter;\n    ngx_http_top_header_filter = ngx_http_ssi_header_filter;\n\n    ngx_http_next_body_filter = ngx_http_top_body_filter;\n    ngx_http_top_body_filter = ngx_http_ssi_body_filter;\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "src/http/modules/ngx_http_ssi_filter_module.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_HTTP_SSI_FILTER_H_INCLUDED_\n#define _NGX_HTTP_SSI_FILTER_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\n#define NGX_HTTP_SSI_MAX_PARAMS       16\n\n#define NGX_HTTP_SSI_COMMAND_LEN      32\n#define NGX_HTTP_SSI_PARAM_LEN        32\n#define NGX_HTTP_SSI_PARAMS_N         4\n\n\n#define NGX_HTTP_SSI_COND_IF          1\n#define NGX_HTTP_SSI_COND_ELSE        2\n\n\n#define NGX_HTTP_SSI_NO_ENCODING      0\n#define NGX_HTTP_SSI_URL_ENCODING     1\n#define NGX_HTTP_SSI_ENTITY_ENCODING  2\n\n\ntypedef struct {\n    ngx_hash_t                hash;\n    ngx_hash_keys_arrays_t    commands;\n} ngx_http_ssi_main_conf_t;\n\n\ntypedef struct {\n    ngx_buf_t                *buf;\n\n    u_char                   *pos;\n    u_char                   *copy_start;\n    u_char                   *copy_end;\n\n    ngx_uint_t                key;\n    ngx_str_t                 command;\n    ngx_array_t               params;\n    ngx_table_elt_t          *param;\n    ngx_table_elt_t           params_array[NGX_HTTP_SSI_PARAMS_N];\n\n    ngx_chain_t              *in;\n    ngx_chain_t              *out;\n    ngx_chain_t             **last_out;\n    ngx_chain_t              *busy;\n    ngx_chain_t              *free;\n\n    ngx_uint_t                state;\n    ngx_uint_t                saved_state;\n    size_t                    saved;\n    size_t                    looked;\n\n    size_t                    value_len;\n\n    ngx_list_t               *variables;\n    ngx_array_t              *blocks;\n\n#if (NGX_PCRE)\n    ngx_uint_t                ncaptures;\n    int                      *captures;\n    u_char                   *captures_data;\n#endif\n\n    unsigned                  shared:1;\n    unsigned                  conditional:2;\n    unsigned                  encoding:2;\n    unsigned                  block:1;\n    unsigned                  output:1;\n    unsigned                  output_chosen:1;\n\n    ngx_http_request_t       *wait;\n    void                     *value_buf;\n    ngx_str_t                 timefmt;\n    ngx_str_t                 errmsg;\n} ngx_http_ssi_ctx_t;\n\n\ntypedef ngx_int_t (*ngx_http_ssi_command_pt) (ngx_http_request_t *r,\n    ngx_http_ssi_ctx_t *ctx, ngx_str_t **);\n\n\ntypedef struct {\n    ngx_str_t                 name;\n    ngx_uint_t                index;\n\n    unsigned                  mandatory:1;\n    unsigned                  multiple:1;\n} ngx_http_ssi_param_t;\n\n\ntypedef struct {\n    ngx_str_t                 name;\n    ngx_http_ssi_command_pt   handler;\n    ngx_http_ssi_param_t     *params;\n\n    unsigned                  conditional:2;\n    unsigned                  block:1;\n    unsigned                  flush:1;\n} ngx_http_ssi_command_t;\n\n\nextern ngx_module_t  ngx_http_ssi_filter_module;\n\n\n#endif /* _NGX_HTTP_SSI_FILTER_H_INCLUDED_ */\n"
  },
  {
    "path": "src/http/modules/ngx_http_ssl_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n#if (NGX_HTTP_V2 && T_NGX_HTTP2_SRV_ENABLE)\n#include <ngx_http_v2_module.h>\n#endif\n\ntypedef ngx_int_t (*ngx_ssl_variable_handler_pt)(ngx_connection_t *c,\n    ngx_pool_t *pool, ngx_str_t *s);\n\n\n#define NGX_DEFAULT_CIPHERS     \"HIGH:!aNULL:!MD5\"\n#define NGX_DEFAULT_ECDH_CURVE  \"auto\"\n\n#define NGX_HTTP_ALPN_PROTOS    \"\\x08http/1.1\\x08http/1.0\\x08http/0.9\"\n\n\n#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation\nstatic int ngx_http_ssl_alpn_select(ngx_ssl_conn_t *ssl_conn,\n    const unsigned char **out, unsigned char *outlen,\n    const unsigned char *in, unsigned int inlen, void *arg);\n#endif\n\nstatic ngx_int_t ngx_http_ssl_static_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_ssl_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\n\nstatic ngx_int_t ngx_http_ssl_add_variables(ngx_conf_t *cf);\nstatic void *ngx_http_ssl_create_srv_conf(ngx_conf_t *cf);\n#if (T_NGX_HTTP_SSL_VCE)\nstatic void *ngx_http_ssl_create_loc_conf(ngx_conf_t *cf);\n#endif\nstatic char *ngx_http_ssl_merge_srv_conf(ngx_conf_t *cf,\n    void *parent, void *child);\n\nstatic ngx_int_t ngx_http_ssl_compile_certificates(ngx_conf_t *cf,\n    ngx_http_ssl_srv_conf_t *conf);\n\nstatic char *ngx_http_ssl_enable(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n#if (NGX_HTTP_SSL && NGX_SSL_ASYNC)\nstatic char *ngx_http_ssl_enable_async(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n#endif\nstatic char *ngx_http_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_http_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_http_ssl_ocsp_cache(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n\nstatic char *ngx_http_ssl_conf_command_check(ngx_conf_t *cf, void *post,\n    void *data);\n\nstatic ngx_int_t ngx_http_ssl_init(ngx_conf_t *cf);\n\n\nstatic ngx_conf_bitmask_t  ngx_http_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    { ngx_string(\"TLSv1.3\"), NGX_SSL_TLSv1_3 },\n    { ngx_null_string, 0 }\n};\n\n\nstatic ngx_conf_enum_t  ngx_http_ssl_verify[] = {\n    { ngx_string(\"off\"), 0 },\n    { ngx_string(\"on\"), 1 },\n    { ngx_string(\"optional\"), 2 },\n    { ngx_string(\"optional_no_ca\"), 3 },\n    { ngx_null_string, 0 }\n};\n\n\nstatic ngx_conf_enum_t  ngx_http_ssl_ocsp[] = {\n    { ngx_string(\"off\"), 0 },\n    { ngx_string(\"on\"), 1 },\n    { ngx_string(\"leaf\"), 2 },\n    { ngx_null_string, 0 }\n};\n\n\nstatic ngx_conf_deprecated_t  ngx_http_ssl_deprecated = {\n    ngx_conf_deprecated, \"ssl\", \"listen ... ssl\"\n};\n\n\nstatic ngx_conf_post_t  ngx_http_ssl_conf_command_post =\n    { ngx_http_ssl_conf_command_check };\n\n\nstatic ngx_command_t  ngx_http_ssl_commands[] = {\n\n    { ngx_string(\"ssl\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,\n      ngx_http_ssl_enable,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      offsetof(ngx_http_ssl_srv_conf_t, enable),\n      &ngx_http_ssl_deprecated },\n\n#if (NGX_HTTP_SSL && NGX_SSL_ASYNC)\n    { ngx_string(\"ssl_async\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,\n      ngx_http_ssl_enable_async,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      offsetof(ngx_http_ssl_srv_conf_t, async_enable),\n      NULL },\n#endif\n\n    { ngx_string(\"ssl_certificate\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_array_slot,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      offsetof(ngx_http_ssl_srv_conf_t, certificates),\n      NULL },\n\n    { ngx_string(\"ssl_certificate_key\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_array_slot,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      offsetof(ngx_http_ssl_srv_conf_t, certificate_keys),\n      NULL },\n\n    { ngx_string(\"ssl_password_file\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_http_ssl_password_file,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      0,\n      NULL },\n\n#if (T_NGX_SSL_NTLS)\n    { ngx_string(\"enable_ntls\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      offsetof(ngx_http_ssl_srv_conf_t, enable_ntls),\n      NULL },\n\n    { ngx_string(\"ssl_enc_certificate\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_slot,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      offsetof(ngx_http_ssl_srv_conf_t, enc_certificate),\n      NULL },\n\n    { ngx_string(\"ssl_enc_certificate_key\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_slot,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      offsetof(ngx_http_ssl_srv_conf_t, enc_certificate_key),\n      NULL },\n\n    { ngx_string(\"ssl_sign_certificate\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_slot,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      offsetof(ngx_http_ssl_srv_conf_t, sign_certificate),\n      NULL },\n\n    { ngx_string(\"ssl_sign_certificate_key\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_slot,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      offsetof(ngx_http_ssl_srv_conf_t, sign_certificate_key),\n      NULL },\n#endif\n\n    { ngx_string(\"ssl_dhparam\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_slot,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      offsetof(ngx_http_ssl_srv_conf_t, dhparam),\n      NULL },\n\n    { ngx_string(\"ssl_ecdh_curve\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_slot,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      offsetof(ngx_http_ssl_srv_conf_t, ecdh_curve),\n      NULL },\n\n    { ngx_string(\"ssl_protocols\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_1MORE,\n      ngx_conf_set_bitmask_slot,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      offsetof(ngx_http_ssl_srv_conf_t, protocols),\n      &ngx_http_ssl_protocols },\n\n    { ngx_string(\"ssl_ciphers\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_slot,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      offsetof(ngx_http_ssl_srv_conf_t, ciphers),\n      NULL },\n\n    { ngx_string(\"ssl_buffer_size\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      offsetof(ngx_http_ssl_srv_conf_t, buffer_size),\n      NULL },\n\n    { ngx_string(\"ssl_verify_client\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_enum_slot,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      offsetof(ngx_http_ssl_srv_conf_t, verify),\n      &ngx_http_ssl_verify },\n\n#if (T_NGX_HTTP_SSL_VCE)\n    { ngx_string(\"ssl_verify_client_exception\"),\n      NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_ssl_loc_conf_t, verify_exception),\n      NULL },\n#endif\n\n    { ngx_string(\"ssl_verify_depth\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_num_slot,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      offsetof(ngx_http_ssl_srv_conf_t, verify_depth),\n      NULL },\n\n    { ngx_string(\"ssl_client_certificate\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_slot,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      offsetof(ngx_http_ssl_srv_conf_t, client_certificate),\n      NULL },\n\n    { ngx_string(\"ssl_trusted_certificate\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_slot,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      offsetof(ngx_http_ssl_srv_conf_t, trusted_certificate),\n      NULL },\n\n    { ngx_string(\"ssl_prefer_server_ciphers\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      offsetof(ngx_http_ssl_srv_conf_t, prefer_server_ciphers),\n      NULL },\n\n    { ngx_string(\"ssl_session_cache\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE12,\n      ngx_http_ssl_session_cache,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"ssl_session_tickets\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      offsetof(ngx_http_ssl_srv_conf_t, session_tickets),\n      NULL },\n\n    { ngx_string(\"ssl_session_ticket_key\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_array_slot,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      offsetof(ngx_http_ssl_srv_conf_t, session_ticket_keys),\n      NULL },\n\n    { ngx_string(\"ssl_session_timeout\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_sec_slot,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      offsetof(ngx_http_ssl_srv_conf_t, session_timeout),\n      NULL },\n\n    { ngx_string(\"ssl_crl\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_slot,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      offsetof(ngx_http_ssl_srv_conf_t, crl),\n      NULL },\n\n    { ngx_string(\"ssl_ocsp\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_enum_slot,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      offsetof(ngx_http_ssl_srv_conf_t, ocsp),\n      &ngx_http_ssl_ocsp },\n\n    { ngx_string(\"ssl_ocsp_responder\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_slot,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      offsetof(ngx_http_ssl_srv_conf_t, ocsp_responder),\n      NULL },\n\n    { ngx_string(\"ssl_ocsp_cache\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_http_ssl_ocsp_cache,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"ssl_stapling\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      offsetof(ngx_http_ssl_srv_conf_t, stapling),\n      NULL },\n\n    { ngx_string(\"ssl_stapling_file\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_slot,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      offsetof(ngx_http_ssl_srv_conf_t, stapling_file),\n      NULL },\n\n    { ngx_string(\"ssl_stapling_responder\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_slot,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      offsetof(ngx_http_ssl_srv_conf_t, stapling_responder),\n      NULL },\n\n    { ngx_string(\"ssl_stapling_verify\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      offsetof(ngx_http_ssl_srv_conf_t, stapling_verify),\n      NULL },\n\n    { ngx_string(\"ssl_early_data\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      offsetof(ngx_http_ssl_srv_conf_t, early_data),\n      NULL },\n\n    { ngx_string(\"ssl_conf_command\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE2,\n      ngx_conf_set_keyval_slot,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      offsetof(ngx_http_ssl_srv_conf_t, conf_commands),\n      &ngx_http_ssl_conf_command_post },\n\n    { ngx_string(\"ssl_reject_handshake\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      offsetof(ngx_http_ssl_srv_conf_t, reject_handshake),\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_ssl_module_ctx = {\n    ngx_http_ssl_add_variables,            /* preconfiguration */\n    ngx_http_ssl_init,                     /* postconfiguration */\n\n    NULL,                                  /* create main configuration */\n    NULL,                                  /* init main configuration */\n\n    ngx_http_ssl_create_srv_conf,          /* create server configuration */\n    ngx_http_ssl_merge_srv_conf,           /* merge server configuration */\n\n#if (T_NGX_HTTP_SSL_VCE)\n    ngx_http_ssl_create_loc_conf,          /* create location configuration */\n#else\n    NULL,                                  /* create location configuration */\n#endif\n    NULL                                   /* merge location configuration */\n};\n\n\nngx_module_t  ngx_http_ssl_module = {\n    NGX_MODULE_V1,\n    &ngx_http_ssl_module_ctx,              /* module context */\n    ngx_http_ssl_commands,                 /* 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_http_variable_t  ngx_http_ssl_vars[] = {\n\n    { ngx_string(\"ssl_protocol\"), NULL, ngx_http_ssl_static_variable,\n      (uintptr_t) ngx_ssl_get_protocol, NGX_HTTP_VAR_CHANGEABLE, 0 },\n\n    { ngx_string(\"ssl_cipher\"), NULL, ngx_http_ssl_static_variable,\n      (uintptr_t) ngx_ssl_get_cipher_name, NGX_HTTP_VAR_CHANGEABLE, 0 },\n\n    { ngx_string(\"ssl_ciphers\"), NULL, ngx_http_ssl_variable,\n      (uintptr_t) ngx_ssl_get_ciphers, NGX_HTTP_VAR_CHANGEABLE, 0 },\n\n    { ngx_string(\"ssl_curve\"), NULL, ngx_http_ssl_variable,\n      (uintptr_t) ngx_ssl_get_curve, NGX_HTTP_VAR_CHANGEABLE, 0 },\n\n    { ngx_string(\"ssl_curves\"), NULL, ngx_http_ssl_variable,\n      (uintptr_t) ngx_ssl_get_curves, NGX_HTTP_VAR_CHANGEABLE, 0 },\n\n    { ngx_string(\"ssl_session_id\"), NULL, ngx_http_ssl_variable,\n      (uintptr_t) ngx_ssl_get_session_id, NGX_HTTP_VAR_CHANGEABLE, 0 },\n\n    { ngx_string(\"ssl_session_reused\"), NULL, ngx_http_ssl_variable,\n      (uintptr_t) ngx_ssl_get_session_reused, NGX_HTTP_VAR_CHANGEABLE, 0 },\n\n    { ngx_string(\"ssl_early_data\"), NULL, ngx_http_ssl_variable,\n      (uintptr_t) ngx_ssl_get_early_data,\n      NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE, 0 },\n\n    { ngx_string(\"ssl_server_name\"), NULL, ngx_http_ssl_variable,\n      (uintptr_t) ngx_ssl_get_server_name, NGX_HTTP_VAR_CHANGEABLE, 0 },\n\n    { ngx_string(\"ssl_alpn_protocol\"), NULL, ngx_http_ssl_variable,\n      (uintptr_t) ngx_ssl_get_alpn_protocol, NGX_HTTP_VAR_CHANGEABLE, 0 },\n\n    { ngx_string(\"ssl_client_cert\"), NULL, ngx_http_ssl_variable,\n      (uintptr_t) ngx_ssl_get_certificate, NGX_HTTP_VAR_CHANGEABLE, 0 },\n\n    { ngx_string(\"ssl_client_raw_cert\"), NULL, ngx_http_ssl_variable,\n      (uintptr_t) ngx_ssl_get_raw_certificate,\n      NGX_HTTP_VAR_CHANGEABLE, 0 },\n\n    { ngx_string(\"ssl_client_escaped_cert\"), NULL, ngx_http_ssl_variable,\n      (uintptr_t) ngx_ssl_get_escaped_certificate,\n      NGX_HTTP_VAR_CHANGEABLE, 0 },\n\n    { ngx_string(\"ssl_client_s_dn\"), NULL, ngx_http_ssl_variable,\n      (uintptr_t) ngx_ssl_get_subject_dn, NGX_HTTP_VAR_CHANGEABLE, 0 },\n\n    { ngx_string(\"ssl_client_i_dn\"), NULL, ngx_http_ssl_variable,\n      (uintptr_t) ngx_ssl_get_issuer_dn, NGX_HTTP_VAR_CHANGEABLE, 0 },\n\n    { ngx_string(\"ssl_client_s_dn_legacy\"), NULL, ngx_http_ssl_variable,\n      (uintptr_t) ngx_ssl_get_subject_dn_legacy, NGX_HTTP_VAR_CHANGEABLE, 0 },\n\n    { ngx_string(\"ssl_client_i_dn_legacy\"), NULL, ngx_http_ssl_variable,\n      (uintptr_t) ngx_ssl_get_issuer_dn_legacy, NGX_HTTP_VAR_CHANGEABLE, 0 },\n\n    { ngx_string(\"ssl_client_serial\"), NULL, ngx_http_ssl_variable,\n      (uintptr_t) ngx_ssl_get_serial_number, NGX_HTTP_VAR_CHANGEABLE, 0 },\n\n    { ngx_string(\"ssl_client_fingerprint\"), NULL, ngx_http_ssl_variable,\n      (uintptr_t) ngx_ssl_get_fingerprint, NGX_HTTP_VAR_CHANGEABLE, 0 },\n\n    { ngx_string(\"ssl_client_verify\"), NULL, ngx_http_ssl_variable,\n      (uintptr_t) ngx_ssl_get_client_verify, NGX_HTTP_VAR_CHANGEABLE, 0 },\n\n    { ngx_string(\"ssl_client_v_start\"), NULL, ngx_http_ssl_variable,\n      (uintptr_t) ngx_ssl_get_client_v_start, NGX_HTTP_VAR_CHANGEABLE, 0 },\n\n    { ngx_string(\"ssl_client_v_end\"), NULL, ngx_http_ssl_variable,\n      (uintptr_t) ngx_ssl_get_client_v_end, NGX_HTTP_VAR_CHANGEABLE, 0 },\n\n    { ngx_string(\"ssl_client_v_remain\"), NULL, ngx_http_ssl_variable,\n      (uintptr_t) ngx_ssl_get_client_v_remain, NGX_HTTP_VAR_CHANGEABLE, 0 },\n\n#if (T_NGX_SSL_HANDSHAKE_TIME)\n    /* $ssl_shandshakd_time deprecated and will be removed in the next release */\n    { ngx_string(\"ssl_handshakd_time\"), NULL, ngx_http_ssl_variable,\n      (uintptr_t) ngx_ssl_get_handshake_time, NGX_HTTP_VAR_CHANGEABLE, 0 },\n\n    { ngx_string(\"ssl_handshake_time\"), NULL, ngx_http_ssl_variable,\n      (uintptr_t) ngx_ssl_get_handshake_time, NGX_HTTP_VAR_CHANGEABLE, 0 },\n\n    { ngx_string(\"ssl_handshake_time_msec\"), NULL, ngx_http_ssl_variable,\n      (uintptr_t) ngx_ssl_get_handshake_time_msec, NGX_HTTP_VAR_CHANGEABLE, 0 },\n#endif\n\n      ngx_http_null_variable\n};\n\n\nstatic ngx_str_t ngx_http_ssl_sess_id_ctx = ngx_string(\"HTTP\");\n\n\n#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation\n\nstatic int\nngx_http_ssl_alpn_select(ngx_ssl_conn_t *ssl_conn, const unsigned char **out,\n    unsigned char *outlen, const unsigned char *in, unsigned int inlen,\n    void *arg)\n{\n    unsigned int            srvlen;\n    unsigned char          *srv;\n#if (NGX_DEBUG)\n    unsigned int            i;\n#endif\n#if (NGX_HTTP_V2)\n    ngx_http_connection_t  *hc;\n#if (T_NGX_HTTP2_SRV_ENABLE)\n    ngx_http_v2_srv_conf_t *h2scf;\n#endif\n#endif\n#if (NGX_HTTP_V2 || NGX_DEBUG)\n    ngx_connection_t       *c;\n\n    c = ngx_ssl_get_connection(ssl_conn);\n#endif\n\n#if (NGX_DEBUG)\n    for (i = 0; i < inlen; i += in[i] + 1) {\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"SSL ALPN supported by client: %*s\",\n                       (size_t) in[i], &in[i + 1]);\n    }\n#endif\n\n#if (NGX_HTTP_V2)\n    hc = c->data;\n\n#if (T_NGX_HTTP2_SRV_ENABLE)\n    h2scf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_v2_module);\n#endif\n\n    if (\n#if (T_NGX_HTTP2_SRV_ENABLE)\n        (\n#endif\n        hc->addr_conf->http2\n#if (T_NGX_HTTP2_SRV_ENABLE)\n        && h2scf->enable != 0) || h2scf->enable == 1\n#endif\n        )\n    {\n        srv = (unsigned char *) NGX_HTTP_V2_ALPN_PROTO NGX_HTTP_ALPN_PROTOS;\n        srvlen = sizeof(NGX_HTTP_V2_ALPN_PROTO NGX_HTTP_ALPN_PROTOS) - 1;\n    } else\n#endif\n    {\n        srv = (unsigned char *) NGX_HTTP_ALPN_PROTOS;\n        srvlen = sizeof(NGX_HTTP_ALPN_PROTOS) - 1;\n    }\n\n    if (SSL_select_next_proto((unsigned char **) out, outlen, srv, srvlen,\n                              in, inlen)\n        != OPENSSL_NPN_NEGOTIATED)\n    {\n        return SSL_TLSEXT_ERR_ALERT_FATAL;\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"SSL ALPN selected: %*s\", (size_t) *outlen, *out);\n\n    return SSL_TLSEXT_ERR_OK;\n}\n\n#endif\n\n\nstatic ngx_int_t\nngx_http_ssl_static_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    ngx_ssl_variable_handler_pt  handler = (ngx_ssl_variable_handler_pt) data;\n\n    size_t     len;\n    ngx_str_t  s;\n\n    if (r->connection->ssl) {\n\n        (void) handler(r->connection, NULL, &s);\n\n        v->data = s.data;\n\n        for (len = 0; v->data[len]; len++) { /* void */ }\n\n        v->len = len;\n        v->valid = 1;\n        v->no_cacheable = 0;\n        v->not_found = 0;\n\n        return NGX_OK;\n    }\n\n    v->not_found = 1;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_ssl_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,\n    uintptr_t data)\n{\n    ngx_ssl_variable_handler_pt  handler = (ngx_ssl_variable_handler_pt) data;\n\n    ngx_str_t  s;\n\n    if (r->connection->ssl) {\n\n        if (handler(r->connection, r->pool, &s) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        v->len = s.len;\n        v->data = s.data;\n\n        if (v->len) {\n            v->valid = 1;\n            v->no_cacheable = 0;\n            v->not_found = 0;\n\n            return NGX_OK;\n        }\n    }\n\n    v->not_found = 1;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_ssl_add_variables(ngx_conf_t *cf)\n{\n    ngx_http_variable_t  *var, *v;\n\n    for (v = ngx_http_ssl_vars; v->name.len; v++) {\n        var = ngx_http_add_variable(cf, &v->name, v->flags);\n        if (var == NULL) {\n            return NGX_ERROR;\n        }\n\n        var->get_handler = v->get_handler;\n        var->data = v->data;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void *\nngx_http_ssl_create_srv_conf(ngx_conf_t *cf)\n{\n    ngx_http_ssl_srv_conf_t  *sscf;\n\n    sscf = ngx_pcalloc(cf->pool, sizeof(ngx_http_ssl_srv_conf_t));\n    if (sscf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc():\n     *\n     *     sscf->protocols = 0;\n     *     sscf->certificate_values = NULL;\n     *     sscf->dhparam = { 0, NULL };\n     *     sscf->ecdh_curve = { 0, NULL };\n     *     sscf->client_certificate = { 0, NULL };\n     *     sscf->trusted_certificate = { 0, NULL };\n     *     sscf->crl = { 0, NULL };\n     *     sscf->ciphers = { 0, NULL };\n     *     sscf->shm_zone = NULL;\n     *     sscf->ocsp_responder = { 0, NULL };\n     *     sscf->stapling_file = { 0, NULL };\n     *     sscf->stapling_responder = { 0, NULL };\n     */\n\n    sscf->enable = NGX_CONF_UNSET;\n#if (NGX_HTTP_SSL && NGX_SSL_ASYNC)\n    sscf->async_enable = NGX_CONF_UNSET;\n#endif\n#if (T_NGX_SSL_NTLS)\n    sscf->enable_ntls = NGX_CONF_UNSET;\n#endif\n    sscf->prefer_server_ciphers = NGX_CONF_UNSET;\n    sscf->early_data = NGX_CONF_UNSET;\n    sscf->reject_handshake = NGX_CONF_UNSET;\n    sscf->buffer_size = NGX_CONF_UNSET_SIZE;\n    sscf->verify = NGX_CONF_UNSET_UINT;\n    sscf->verify_depth = NGX_CONF_UNSET_UINT;\n    sscf->certificates = NGX_CONF_UNSET_PTR;\n    sscf->certificate_keys = NGX_CONF_UNSET_PTR;\n    sscf->passwords = NGX_CONF_UNSET_PTR;\n    sscf->conf_commands = NGX_CONF_UNSET_PTR;\n    sscf->builtin_session_cache = NGX_CONF_UNSET;\n    sscf->session_timeout = NGX_CONF_UNSET;\n    sscf->session_tickets = NGX_CONF_UNSET;\n    sscf->session_ticket_keys = NGX_CONF_UNSET_PTR;\n    sscf->ocsp = NGX_CONF_UNSET_UINT;\n    sscf->ocsp_cache_zone = NGX_CONF_UNSET_PTR;\n    sscf->stapling = NGX_CONF_UNSET;\n    sscf->stapling_verify = NGX_CONF_UNSET;\n\n    return sscf;\n}\n\n\n#if (T_NGX_HTTP_SSL_VCE)\nstatic void *\nngx_http_ssl_create_loc_conf(ngx_conf_t *cf)\n{\n    ngx_http_ssl_loc_conf_t *slcf;\n\n    slcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_ssl_loc_conf_t));\n    if (slcf == NULL) {\n        return NULL;\n    }\n\n    slcf->verify_exception = NGX_CONF_UNSET;\n\n    return slcf;\n}\n#endif\n\n\nstatic char *\nngx_http_ssl_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_http_ssl_srv_conf_t *prev = parent;\n    ngx_http_ssl_srv_conf_t *conf = child;\n\n    ngx_pool_cleanup_t  *cln;\n\n    if (conf->enable == NGX_CONF_UNSET) {\n        if (prev->enable == NGX_CONF_UNSET) {\n            conf->enable = 0;\n\n        } else {\n            conf->enable = prev->enable;\n            conf->file = prev->file;\n            conf->line = prev->line;\n        }\n    }\n\n#if (NGX_HTTP_SSL && NGX_SSL_ASYNC)\n    if (conf->async_enable == NGX_CONF_UNSET) {\n        if (prev->async_enable == NGX_CONF_UNSET) {\n            conf->async_enable = 0;\n\n        } else {\n            conf->async_enable = prev->async_enable;\n            conf->file = prev->file;\n            conf->line = prev->line;\n        }\n    }\n#endif\n    ngx_conf_merge_value(conf->session_timeout,\n                         prev->session_timeout, 300);\n\n    ngx_conf_merge_value(conf->prefer_server_ciphers,\n                         prev->prefer_server_ciphers, 0);\n\n    ngx_conf_merge_value(conf->early_data, prev->early_data, 0);\n    ngx_conf_merge_value(conf->reject_handshake, prev->reject_handshake, 0);\n\n    ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols,\n                         (NGX_CONF_BITMASK_SET\n                          |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1\n                          |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3));\n\n    ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size,\n                         NGX_SSL_BUFSIZE);\n\n    ngx_conf_merge_uint_value(conf->verify, prev->verify, 0);\n    ngx_conf_merge_uint_value(conf->verify_depth, prev->verify_depth, 1);\n\n    ngx_conf_merge_ptr_value(conf->certificates, prev->certificates, NULL);\n    ngx_conf_merge_ptr_value(conf->certificate_keys, prev->certificate_keys,\n                         NULL);\n\n    ngx_conf_merge_ptr_value(conf->passwords, prev->passwords, NULL);\n\n#if (T_NGX_SSL_NTLS)\n    ngx_conf_merge_value(conf->enable_ntls, prev->enable_ntls, 0);\n    ngx_conf_merge_str_value(conf->enc_certificate, prev->enc_certificate, \"\");\n    ngx_conf_merge_str_value(conf->enc_certificate_key,\n                         prev->enc_certificate_key, \"\");\n    ngx_conf_merge_str_value(conf->sign_certificate, prev->sign_certificate,\n                         \"\");\n    ngx_conf_merge_str_value(conf->sign_certificate_key,\n                         prev->sign_certificate_key, \"\");\n#endif\n\n    ngx_conf_merge_str_value(conf->dhparam, prev->dhparam, \"\");\n\n    ngx_conf_merge_str_value(conf->client_certificate, prev->client_certificate,\n                         \"\");\n    ngx_conf_merge_str_value(conf->trusted_certificate,\n                         prev->trusted_certificate, \"\");\n    ngx_conf_merge_str_value(conf->crl, prev->crl, \"\");\n\n    ngx_conf_merge_str_value(conf->ecdh_curve, prev->ecdh_curve,\n                         NGX_DEFAULT_ECDH_CURVE);\n\n    ngx_conf_merge_str_value(conf->ciphers, prev->ciphers, NGX_DEFAULT_CIPHERS);\n\n    ngx_conf_merge_ptr_value(conf->conf_commands, prev->conf_commands, NULL);\n\n    ngx_conf_merge_uint_value(conf->ocsp, prev->ocsp, 0);\n    ngx_conf_merge_str_value(conf->ocsp_responder, prev->ocsp_responder, \"\");\n    ngx_conf_merge_ptr_value(conf->ocsp_cache_zone,\n                         prev->ocsp_cache_zone, NULL);\n\n    ngx_conf_merge_value(conf->stapling, prev->stapling, 0);\n    ngx_conf_merge_value(conf->stapling_verify, prev->stapling_verify, 0);\n    ngx_conf_merge_str_value(conf->stapling_file, prev->stapling_file, \"\");\n    ngx_conf_merge_str_value(conf->stapling_responder,\n                         prev->stapling_responder, \"\");\n\n    conf->ssl.log = cf->log;\n\n    if (conf->enable) {\n\n        if (conf->certificates) {\n            if (conf->certificate_keys == NULL) {\n                ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                              \"no \\\"ssl_certificate_key\\\" is defined for \"\n                              \"the \\\"ssl\\\" directive in %s:%ui\",\n                              conf->file, conf->line);\n                return NGX_CONF_ERROR;\n            }\n#if (NGX_HTTP_SSL && NGX_SSL_ASYNC)\n            conf->ssl.async_enable = conf->async_enable;\n#endif\n\n            if (conf->certificate_keys->nelts < conf->certificates->nelts) {\n                ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                              \"no \\\"ssl_certificate_key\\\" is defined \"\n                              \"for certificate \\\"%V\\\" and \"\n                              \"the \\\"ssl\\\" directive in %s:%ui\",\n                              ((ngx_str_t *) conf->certificates->elts)\n                              + conf->certificates->nelts - 1,\n                              conf->file, conf->line);\n                return NGX_CONF_ERROR;\n            }\n#if (T_NGX_SSL_NTLS)\n        } else if (conf->enc_certificate.len != 0 || conf->sign_certificate.len != 0) {\n            if (conf->enc_certificate.len == 0) {\n                ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                              \"no \\\"ssl_enc_certificate\\\" is defined for \"\n                              \"the \\\"ssl\\\" directive in %s:%ui\",\n                              conf->file, conf->line);\n                return NGX_CONF_ERROR;\n            }\n\n            if (conf->sign_certificate.len == 0) {\n                ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                              \"no \\\"ssl_sign_certificate\\\" is defined for \"\n                              \"the \\\"ssl\\\" directive in %s:%ui\",\n                              conf->file, conf->line);\n                return NGX_CONF_ERROR;\n            }\n\n            if (conf->enc_certificate_key.len == 0) {\n                ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                              \"no \\\"ssl_enc_certificate_key\\\" is defined for \"\n                              \"the \\\"ssl\\\" directive in %s:%ui\",\n                              conf->file, conf->line);\n                return NGX_CONF_ERROR;\n            }\n\n            if (conf->sign_certificate_key.len == 0) {\n                ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                              \"no \\\"ssl_sign_certificate_key\\\" is defined for \"\n                              \"the \\\"ssl\\\" directive in %s:%ui\",\n                              conf->file, conf->line);\n                return NGX_CONF_ERROR;\n            }\n#endif\n        } else if (!conf->reject_handshake) {\n            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                          \"no \\\"ssl_certificate\\\" is defined for \"\n                          \"the \\\"ssl\\\" directive in %s:%ui\",\n                          conf->file, conf->line);\n            return NGX_CONF_ERROR;\n        }\n\n    } else if (conf->certificates) {\n\n        if (conf->certificate_keys == NULL\n            || conf->certificate_keys->nelts < conf->certificates->nelts)\n        {\n            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                          \"no \\\"ssl_certificate_key\\\" is defined \"\n                          \"for certificate \\\"%V\\\"\",\n                          ((ngx_str_t *) conf->certificates->elts)\n                          + conf->certificates->nelts - 1);\n            return NGX_CONF_ERROR;\n        }\n#if (T_NGX_SSL_NTLS)\n    } else if (conf->enc_certificate.len != 0 || conf->sign_certificate.len != 0) {\n        if (conf->enc_certificate.len == 0) {\n            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                          \"no \\\"ssl_enc_certificate\\\" is defined for \"\n                          \"the \\\"ssl\\\" directive in %s:%ui\",\n                          conf->file, conf->line);\n            return NGX_CONF_ERROR;\n        }\n\n        if (conf->sign_certificate.len == 0) {\n            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                          \"no \\\"ssl_sign_certificate\\\" is defined for \"\n                          \"the \\\"ssl\\\" directive in %s:%ui\",\n                          conf->file, conf->line);\n            return NGX_CONF_ERROR;\n        }\n\n        if (conf->enc_certificate_key.len == 0) {\n            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                          \"no \\\"ssl_enc_certificate_key\\\" is defined for \"\n                          \"the \\\"ssl\\\" directive in %s:%ui\",\n                          conf->file, conf->line);\n            return NGX_CONF_ERROR;\n        }\n\n        if (conf->sign_certificate_key.len == 0) {\n            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                          \"no \\\"ssl_sign_certificate_key\\\" is defined for \"\n                          \"the \\\"ssl\\\" directive in %s:%ui\",\n                          conf->file, conf->line);\n            return NGX_CONF_ERROR;\n        }\n#endif\n    } else if (!conf->reject_handshake) {\n        return NGX_CONF_OK;\n    }\n\n    if (ngx_ssl_create(&conf->ssl, conf->protocols, conf) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    cln = ngx_pool_cleanup_add(cf->pool, 0);\n    if (cln == NULL) {\n        ngx_ssl_cleanup_ctx(&conf->ssl);\n        return NGX_CONF_ERROR;\n    }\n\n    cln->handler = ngx_ssl_cleanup_ctx;\n    cln->data = &conf->ssl;\n\n#if defined(T_INGRESS_SHARED_MEMORY_PB) && OPENSSL_VERSION_NUMBER >= 0x10101000L\n    SSL_CTX_set_client_hello_cb(conf->ssl.ctx,\n                                ngx_http_ssl_client_hello_callback, NULL);\n#endif\n\n#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME\n\n    if (SSL_CTX_set_tlsext_servername_callback(conf->ssl.ctx,\n                                               ngx_http_ssl_servername)\n        == 0)\n    {\n        ngx_log_error(NGX_LOG_WARN, cf->log, 0,\n            \"nginx was built with SNI support, however, now it is linked \"\n            \"dynamically to an OpenSSL library which has no tlsext support, \"\n            \"therefore SNI is not available\");\n    }\n\n#endif\n\n#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation\n    SSL_CTX_set_alpn_select_cb(conf->ssl.ctx, ngx_http_ssl_alpn_select, NULL);\n#endif\n\n    if (ngx_ssl_ciphers(cf, &conf->ssl, &conf->ciphers,\n                        conf->prefer_server_ciphers)\n        != NGX_OK)\n    {\n        return NGX_CONF_ERROR;\n    }\n\n    if (ngx_http_ssl_compile_certificates(cf, conf) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (conf->certificate_values) {\n\n#ifdef SSL_R_CERT_CB_ERROR\n\n        /* install callback to lookup certificates */\n\n        SSL_CTX_set_cert_cb(conf->ssl.ctx, ngx_http_ssl_certificate, conf);\n\n#else\n        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                      \"variables in \"\n                      \"\\\"ssl_certificate\\\" and \\\"ssl_certificate_key\\\" \"\n                      \"directives are not supported on this platform\");\n        return NGX_CONF_ERROR;\n#endif\n\n    } else if (conf->certificates) {\n\n        /* configure certificates */\n\n        if (ngx_ssl_certificates(cf, &conf->ssl, conf->certificates,\n                                 conf->certificate_keys, conf->passwords)\n            != NGX_OK)\n        {\n            return NGX_CONF_ERROR;\n        }\n    }\n#if (T_NGX_SSL_NTLS)\n    if (conf->enc_certificate.len != 0) {\n        if (ngx_ssl_certificate(cf, &conf->ssl, &conf->enc_certificate,\n                                &conf->enc_certificate_key, conf->passwords,\n                                SSL_ENC_CERT)\n            != NGX_OK)\n        {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    if (conf->sign_certificate.len != 0) {\n        if (ngx_ssl_certificate(cf, &conf->ssl, &conf->sign_certificate,\n                                &conf->sign_certificate_key, conf->passwords,\n                                SSL_SIGN_CERT)\n            != NGX_OK)\n        {\n            return NGX_CONF_ERROR;\n        }\n    }\n#endif\n\n    conf->ssl.buffer_size = conf->buffer_size;\n\n    if (conf->verify) {\n\n        if (conf->client_certificate.len == 0 && conf->verify != 3) {\n            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                          \"no ssl_client_certificate for ssl_verify_client\");\n            return NGX_CONF_ERROR;\n        }\n\n        if (ngx_ssl_client_certificate(cf, &conf->ssl,\n                                       &conf->client_certificate,\n                                       conf->verify_depth)\n            != NGX_OK)\n        {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    if (ngx_ssl_trusted_certificate(cf, &conf->ssl,\n                                    &conf->trusted_certificate,\n                                    conf->verify_depth)\n        != NGX_OK)\n    {\n        return NGX_CONF_ERROR;\n    }\n\n    if (ngx_ssl_crl(cf, &conf->ssl, &conf->crl) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (conf->ocsp) {\n\n        if (conf->verify == 3) {\n            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                          \"\\\"ssl_ocsp\\\" is incompatible with \"\n                          \"\\\"ssl_verify_client optional_no_ca\\\"\");\n            return NGX_CONF_ERROR;\n        }\n\n        if (ngx_ssl_ocsp(cf, &conf->ssl, &conf->ocsp_responder, conf->ocsp,\n                         conf->ocsp_cache_zone)\n            != NGX_OK)\n        {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    if (ngx_ssl_dhparam(cf, &conf->ssl, &conf->dhparam) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (ngx_ssl_ecdh_curve(cf, &conf->ssl, &conf->ecdh_curve) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_conf_merge_value(conf->builtin_session_cache,\n                         prev->builtin_session_cache, NGX_SSL_NONE_SCACHE);\n\n    if (conf->shm_zone == NULL) {\n        conf->shm_zone = prev->shm_zone;\n    }\n\n    if (ngx_ssl_session_cache(&conf->ssl, &ngx_http_ssl_sess_id_ctx,\n                              conf->certificates, conf->builtin_session_cache,\n                              conf->shm_zone, conf->session_timeout)\n        != NGX_OK)\n    {\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_conf_merge_value(conf->session_tickets, prev->session_tickets, 1);\n\n#ifdef SSL_OP_NO_TICKET\n    if (!conf->session_tickets) {\n        SSL_CTX_set_options(conf->ssl.ctx, SSL_OP_NO_TICKET);\n    }\n#endif\n\n    ngx_conf_merge_ptr_value(conf->session_ticket_keys,\n                         prev->session_ticket_keys, NULL);\n\n    if (ngx_ssl_session_ticket_keys(cf, &conf->ssl, conf->session_ticket_keys)\n        != NGX_OK)\n    {\n        return NGX_CONF_ERROR;\n    }\n\n    if (conf->stapling) {\n\n        if (ngx_ssl_stapling(cf, &conf->ssl, &conf->stapling_file,\n                             &conf->stapling_responder, conf->stapling_verify)\n            != NGX_OK)\n        {\n            return NGX_CONF_ERROR;\n        }\n\n    }\n\n    if (ngx_ssl_early_data(cf, &conf->ssl, conf->early_data) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (ngx_ssl_conf_commands(cf, &conf->ssl, conf->conf_commands) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_ssl_compile_certificates(ngx_conf_t *cf,\n    ngx_http_ssl_srv_conf_t *conf)\n{\n    ngx_str_t                         *cert, *key;\n    ngx_uint_t                         i, nelts;\n    ngx_http_complex_value_t          *cv;\n    ngx_http_compile_complex_value_t   ccv;\n\n    if (conf->certificates == NULL) {\n        return NGX_OK;\n    }\n\n    cert = conf->certificates->elts;\n    key = conf->certificate_keys->elts;\n    nelts = conf->certificates->nelts;\n\n    for (i = 0; i < nelts; i++) {\n\n        if (ngx_http_script_variables_count(&cert[i])) {\n            goto found;\n        }\n\n        if (ngx_http_script_variables_count(&key[i])) {\n            goto found;\n        }\n    }\n\n    return NGX_OK;\n\nfound:\n\n    conf->certificate_values = ngx_array_create(cf->pool, nelts,\n                                             sizeof(ngx_http_complex_value_t));\n    if (conf->certificate_values == NULL) {\n        return NGX_ERROR;\n    }\n\n    conf->certificate_key_values = ngx_array_create(cf->pool, nelts,\n                                             sizeof(ngx_http_complex_value_t));\n    if (conf->certificate_key_values == NULL) {\n        return NGX_ERROR;\n    }\n\n    for (i = 0; i < nelts; i++) {\n\n        cv = ngx_array_push(conf->certificate_values);\n        if (cv == NULL) {\n            return NGX_ERROR;\n        }\n\n        ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n\n        ccv.cf = cf;\n        ccv.value = &cert[i];\n        ccv.complex_value = cv;\n        ccv.zero = 1;\n\n        if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        cv = ngx_array_push(conf->certificate_key_values);\n        if (cv == NULL) {\n            return NGX_ERROR;\n        }\n\n        ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n\n        ccv.cf = cf;\n        ccv.value = &key[i];\n        ccv.complex_value = cv;\n        ccv.zero = 1;\n\n        if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {\n            return NGX_ERROR;\n        }\n    }\n\n    conf->passwords = ngx_ssl_preserve_passwords(cf, conf->passwords);\n    if (conf->passwords == NULL) {\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic char *\nngx_http_ssl_enable(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_ssl_srv_conf_t *sscf = conf;\n\n    char  *rv;\n\n    rv = ngx_conf_set_flag_slot(cf, cmd, conf);\n\n    if (rv != NGX_CONF_OK) {\n        return rv;\n    }\n\n    sscf->file = cf->conf_file->file.name.data;\n    sscf->line = cf->conf_file->line;\n\n    return NGX_CONF_OK;\n}\n\n#if (NGX_HTTP_SSL && NGX_SSL_ASYNC)\nstatic char *\nngx_http_ssl_enable_async(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_ssl_srv_conf_t *sscf = conf;\n    char                    *rv;\n    ngx_flag_t              *pssl, *pssl_async;\n\n    rv = ngx_conf_set_flag_slot(cf, cmd, conf);\n\n    if (rv != NGX_CONF_OK) {\n        return rv;\n    }\n\n    /* If ssl_async on is configured, then ssl on is configured by default\n     * This will align 'ssl_async on;' and 'listen port ssl' diretives\n     * */\n    pssl = (ngx_flag_t *) ((char *)conf + offsetof(ngx_http_ssl_srv_conf_t, enable));\n    pssl_async = (ngx_flag_t *) ((char *)conf + cmd->offset);\n\n    if(*pssl_async && *pssl != 1) {\n        *pssl = *pssl_async;\n    }\n\n    sscf->file = cf->conf_file->file.name.data;\n    sscf->line = cf->conf_file->line;\n\n    return NGX_CONF_OK;\n}\n#endif\n\nstatic char *\nngx_http_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_ssl_srv_conf_t *sscf = conf;\n\n    ngx_str_t  *value;\n\n    if (sscf->passwords != NGX_CONF_UNSET_PTR) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    sscf->passwords = ngx_ssl_read_password_file(cf, &value[1]);\n\n    if (sscf->passwords == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_ssl_srv_conf_t *sscf = conf;\n\n    size_t       len;\n    ngx_str_t   *value, name, size;\n    ngx_int_t    n;\n    ngx_uint_t   i, j;\n\n    value = cf->args->elts;\n\n    for (i = 1; i < cf->args->nelts; i++) {\n\n        if (ngx_strcmp(value[i].data, \"off\") == 0) {\n            sscf->builtin_session_cache = NGX_SSL_NO_SCACHE;\n            continue;\n        }\n\n        if (ngx_strcmp(value[i].data, \"none\") == 0) {\n            sscf->builtin_session_cache = NGX_SSL_NONE_SCACHE;\n            continue;\n        }\n\n        if (ngx_strcmp(value[i].data, \"builtin\") == 0) {\n            sscf->builtin_session_cache = NGX_SSL_DFLT_BUILTIN_SCACHE;\n            continue;\n        }\n\n        if (value[i].len > sizeof(\"builtin:\") - 1\n            && ngx_strncmp(value[i].data, \"builtin:\", sizeof(\"builtin:\") - 1)\n               == 0)\n        {\n            n = ngx_atoi(value[i].data + sizeof(\"builtin:\") - 1,\n                         value[i].len - (sizeof(\"builtin:\") - 1));\n\n            if (n == NGX_ERROR) {\n                goto invalid;\n            }\n\n            sscf->builtin_session_cache = n;\n\n            continue;\n        }\n\n        if (value[i].len > sizeof(\"shared:\") - 1\n            && ngx_strncmp(value[i].data, \"shared:\", sizeof(\"shared:\") - 1)\n               == 0)\n        {\n            len = 0;\n\n            for (j = sizeof(\"shared:\") - 1; j < value[i].len; j++) {\n                if (value[i].data[j] == ':') {\n                    break;\n                }\n\n                len++;\n            }\n\n            if (len == 0 || j == value[i].len) {\n                goto invalid;\n            }\n\n            name.len = len;\n            name.data = value[i].data + sizeof(\"shared:\") - 1;\n\n            size.len = value[i].len - j - 1;\n            size.data = name.data + len + 1;\n\n            n = ngx_parse_size(&size);\n\n            if (n == NGX_ERROR) {\n                goto invalid;\n            }\n\n            if (n < (ngx_int_t) (8 * ngx_pagesize)) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"session cache \\\"%V\\\" is too small\",\n                                   &value[i]);\n\n                return NGX_CONF_ERROR;\n            }\n\n            sscf->shm_zone = ngx_shared_memory_add(cf, &name, n,\n                                                   &ngx_http_ssl_module);\n            if (sscf->shm_zone == NULL) {\n                return NGX_CONF_ERROR;\n            }\n\n            sscf->shm_zone->init = ngx_ssl_session_cache_init;\n\n            continue;\n        }\n\n        goto invalid;\n    }\n\n    if (sscf->shm_zone && sscf->builtin_session_cache == NGX_CONF_UNSET) {\n        sscf->builtin_session_cache = NGX_SSL_NO_BUILTIN_SCACHE;\n    }\n\n    return NGX_CONF_OK;\n\ninvalid:\n\n    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                       \"invalid session cache \\\"%V\\\"\", &value[i]);\n\n    return NGX_CONF_ERROR;\n}\n\n\nstatic char *\nngx_http_ssl_ocsp_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_ssl_srv_conf_t *sscf = conf;\n\n    size_t       len;\n    ngx_int_t    n;\n    ngx_str_t   *value, name, size;\n    ngx_uint_t   j;\n\n    if (sscf->ocsp_cache_zone != NGX_CONF_UNSET_PTR) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    if (ngx_strcmp(value[1].data, \"off\") == 0) {\n        sscf->ocsp_cache_zone = NULL;\n        return NGX_CONF_OK;\n    }\n\n    if (value[1].len <= sizeof(\"shared:\") - 1\n        || ngx_strncmp(value[1].data, \"shared:\", sizeof(\"shared:\") - 1) != 0)\n    {\n        goto invalid;\n    }\n\n    len = 0;\n\n    for (j = sizeof(\"shared:\") - 1; j < value[1].len; j++) {\n        if (value[1].data[j] == ':') {\n            break;\n        }\n\n        len++;\n    }\n\n    if (len == 0 || j == value[1].len) {\n        goto invalid;\n    }\n\n    name.len = len;\n    name.data = value[1].data + sizeof(\"shared:\") - 1;\n\n    size.len = value[1].len - j - 1;\n    size.data = name.data + len + 1;\n\n    n = ngx_parse_size(&size);\n\n    if (n == NGX_ERROR) {\n        goto invalid;\n    }\n\n    if (n < (ngx_int_t) (8 * ngx_pagesize)) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"OCSP cache \\\"%V\\\" is too small\", &value[1]);\n\n        return NGX_CONF_ERROR;\n    }\n\n    sscf->ocsp_cache_zone = ngx_shared_memory_add(cf, &name, n,\n                                                  &ngx_http_ssl_module_ctx);\n    if (sscf->ocsp_cache_zone == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    sscf->ocsp_cache_zone->init = ngx_ssl_ocsp_cache_init;\n\n    return NGX_CONF_OK;\n\ninvalid:\n\n    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                       \"invalid OCSP cache \\\"%V\\\"\", &value[1]);\n\n    return NGX_CONF_ERROR;\n}\n\n\nstatic char *\nngx_http_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#else\n    return NGX_CONF_OK;\n#endif\n}\n\n\nstatic ngx_int_t\nngx_http_ssl_init(ngx_conf_t *cf)\n{\n    ngx_uint_t                   a, p, s;\n    ngx_http_conf_addr_t        *addr;\n    ngx_http_conf_port_t        *port;\n    ngx_http_ssl_srv_conf_t     *sscf;\n    ngx_http_core_loc_conf_t    *clcf;\n    ngx_http_core_srv_conf_t   **cscfp, *cscf;\n    ngx_http_core_main_conf_t   *cmcf;\n\n    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);\n    cscfp = cmcf->servers.elts;\n\n    for (s = 0; s < cmcf->servers.nelts; s++) {\n\n        sscf = cscfp[s]->ctx->srv_conf[ngx_http_ssl_module.ctx_index];\n\n        if (sscf->ssl.ctx == NULL) {\n            continue;\n        }\n\n        clcf = cscfp[s]->ctx->loc_conf[ngx_http_core_module.ctx_index];\n\n        if (sscf->stapling) {\n            if (ngx_ssl_stapling_resolver(cf, &sscf->ssl, clcf->resolver,\n                                          clcf->resolver_timeout)\n                != NGX_OK)\n            {\n                return NGX_ERROR;\n            }\n        }\n\n        if (sscf->ocsp) {\n            if (ngx_ssl_ocsp_resolver(cf, &sscf->ssl, clcf->resolver,\n                                      clcf->resolver_timeout)\n                != NGX_OK)\n            {\n                return NGX_ERROR;\n            }\n        }\n    }\n\n    if (cmcf->ports == NULL) {\n        return NGX_OK;\n    }\n\n    port = cmcf->ports->elts;\n    for (p = 0; p < cmcf->ports->nelts; p++) {\n\n        addr = port[p].addrs.elts;\n        for (a = 0; a < port[p].addrs.nelts; a++) {\n\n            if (!addr[a].opt.ssl) {\n                continue;\n            }\n\n            cscf = addr[a].default_server;\n            sscf = cscf->ctx->srv_conf[ngx_http_ssl_module.ctx_index];\n\n            if (sscf->certificates) {\n                continue;\n            }\n\n#if (T_NGX_SSL_NTLS)\n            if (sscf->sign_certificate.len > 0 || sscf->enc_certificate.len > 0) {\n                continue;\n            }\n#endif\n            if (!sscf->reject_handshake) {\n                ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n#if (T_NGX_SSL_NTLS)\n                              \"no \\\"ssl_certificate\\\", \\\"ssl_enc_certificate\\\" \"\n                              \"or \\\"ssl_sign_certificate\\\" is defined for \"\n#else\n                              \"no \\\"ssl_certificate\\\" is defined for \"\n#endif\n                              \"the \\\"listen ... ssl\\\" directive in %s:%ui\",\n                              cscf->file_name, cscf->line);\n                return NGX_ERROR;\n            }\n\n            /*\n             * if no certificates are defined in the default server,\n             * check all non-default server blocks\n             */\n\n            cscfp = addr[a].servers.elts;\n            for (s = 0; s < addr[a].servers.nelts; s++) {\n\n                cscf = cscfp[s];\n                sscf = cscf->ctx->srv_conf[ngx_http_ssl_module.ctx_index];\n\n                if (sscf->certificates || sscf->reject_handshake) {\n                    continue;\n                }\n\n#if (T_NGX_SSL_NTLS)\n                if (sscf->sign_certificate.len > 0 || sscf->enc_certificate.len > 0) {\n                    continue;\n                }\n#endif\n                ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n#if (T_NGX_SSL_NTLS)\n                              \"no \\\"ssl_certificate\\\", \\\"ssl_enc_certificate\\\" \"\n                              \"or \\\"ssl_sign_certificate\\\" is defined for \"\n#else\n                              \"no \\\"ssl_certificate\\\" is defined for \"\n#endif\n                              \"the \\\"listen ... ssl\\\" directive in %s:%ui\",\n                              cscf->file_name, cscf->line);\n                return NGX_ERROR;\n            }\n        }\n    }\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "src/http/modules/ngx_http_ssl_module.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_HTTP_SSL_H_INCLUDED_\n#define _NGX_HTTP_SSL_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\ntypedef struct {\n    ngx_flag_t                      enable;\n\n#if (NGX_HTTP_SSL && NGX_SSL_ASYNC)\n    ngx_flag_t                      async_enable;\n#endif\n\n    ngx_ssl_t                       ssl;\n\n    ngx_flag_t                      prefer_server_ciphers;\n    ngx_flag_t                      early_data;\n    ngx_flag_t                      reject_handshake;\n\n    ngx_uint_t                      protocols;\n\n    ngx_uint_t                      verify;\n    ngx_uint_t                      verify_depth;\n\n    size_t                          buffer_size;\n\n    ssize_t                         builtin_session_cache;\n\n    time_t                          session_timeout;\n\n    ngx_array_t                    *certificates;\n    ngx_array_t                    *certificate_keys;\n\n    ngx_array_t                    *certificate_values;\n    ngx_array_t                    *certificate_key_values;\n\n    ngx_str_t                       dhparam;\n    ngx_str_t                       ecdh_curve;\n    ngx_str_t                       client_certificate;\n    ngx_str_t                       trusted_certificate;\n    ngx_str_t                       crl;\n\n    ngx_str_t                       ciphers;\n\n    ngx_array_t                    *passwords;\n    ngx_array_t                    *conf_commands;\n\n    ngx_shm_zone_t                 *shm_zone;\n\n    ngx_flag_t                      session_tickets;\n    ngx_array_t                    *session_ticket_keys;\n\n    ngx_uint_t                      ocsp;\n    ngx_str_t                       ocsp_responder;\n    ngx_shm_zone_t                 *ocsp_cache_zone;\n\n    ngx_flag_t                      stapling;\n    ngx_flag_t                      stapling_verify;\n    ngx_str_t                       stapling_file;\n    ngx_str_t                       stapling_responder;\n\n    u_char                         *file;\n    ngx_uint_t                      line;\n\n#if (T_NGX_SSL_NTLS)\n    ngx_flag_t                      enable_ntls;\n    ngx_str_t                       enc_certificate;\n    ngx_str_t                       enc_certificate_key;\n    ngx_str_t                       sign_certificate;\n    ngx_str_t                       sign_certificate_key;\n#endif\n} ngx_http_ssl_srv_conf_t;\n\n#if (T_NGX_HTTP_SSL_VCE)\ntypedef struct {\n    ngx_flag_t                      verify_exception;\n} ngx_http_ssl_loc_conf_t;\n#endif\n\n\nextern ngx_module_t  ngx_http_ssl_module;\n\n\n#endif /* _NGX_HTTP_SSL_H_INCLUDED_ */\n"
  },
  {
    "path": "src/http/modules/ngx_http_static_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\nstatic ngx_int_t ngx_http_static_handler(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_static_init(ngx_conf_t *cf);\n\n\nstatic ngx_http_module_t  ngx_http_static_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    ngx_http_static_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\nngx_module_t  ngx_http_static_module = {\n    NGX_MODULE_V1,\n    &ngx_http_static_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_static_handler(ngx_http_request_t *r)\n{\n    u_char                    *last, *location;\n    size_t                     root, len;\n    uintptr_t                  escape;\n    ngx_str_t                  path;\n    ngx_int_t                  rc;\n    ngx_uint_t                 level;\n    ngx_log_t                 *log;\n    ngx_buf_t                 *b;\n    ngx_chain_t                out;\n    ngx_open_file_info_t       of;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD|NGX_HTTP_POST))) {\n        return NGX_HTTP_NOT_ALLOWED;\n    }\n\n    if (r->uri.data[r->uri.len - 1] == '/') {\n        return NGX_DECLINED;\n    }\n\n    log = r->connection->log;\n\n    /*\n     * ngx_http_map_uri_to_path() allocates memory for terminating '\\0'\n     * so we do not need to reserve memory for '/' for possible redirect\n     */\n\n    last = ngx_http_map_uri_to_path(r, &path, &root, 0);\n    if (last == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    path.len = last - path.data;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,\n                   \"http filename: \\\"%s\\\"\", path.data);\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    ngx_memzero(&of, sizeof(ngx_open_file_info_t));\n\n    of.read_ahead = clcf->read_ahead;\n    of.directio = clcf->directio;\n    of.valid = clcf->open_file_cache_valid;\n    of.min_uses = clcf->open_file_cache_min_uses;\n    of.errors = clcf->open_file_cache_errors;\n    of.events = clcf->open_file_cache_events;\n\n    if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)\n        != NGX_OK)\n    {\n        switch (of.err) {\n\n        case 0:\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n\n        case NGX_ENOENT:\n        case NGX_ENOTDIR:\n        case NGX_ENAMETOOLONG:\n\n            level = NGX_LOG_ERR;\n            rc = NGX_HTTP_NOT_FOUND;\n            break;\n\n        case NGX_EACCES:\n#if (NGX_HAVE_OPENAT)\n        case NGX_EMLINK:\n        case NGX_ELOOP:\n#endif\n\n            level = NGX_LOG_ERR;\n            rc = NGX_HTTP_FORBIDDEN;\n            break;\n\n        default:\n\n            level = NGX_LOG_CRIT;\n            rc = NGX_HTTP_INTERNAL_SERVER_ERROR;\n            break;\n        }\n\n        if (rc != NGX_HTTP_NOT_FOUND || clcf->log_not_found) {\n            ngx_log_error(level, log, of.err,\n                          \"%s \\\"%s\\\" failed\", of.failed, path.data);\n        }\n\n        return rc;\n    }\n\n    r->root_tested = !r->error_page;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, \"http static fd: %d\", of.fd);\n\n    if (of.is_dir) {\n\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, log, 0, \"http dir\");\n\n        ngx_http_clear_location(r);\n\n        r->headers_out.location = ngx_list_push(&r->headers_out.headers);\n        if (r->headers_out.location == NULL) {\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n\n        escape = 2 * ngx_escape_uri(NULL, r->uri.data, r->uri.len,\n                                    NGX_ESCAPE_URI);\n\n        if (!clcf->alias && r->args.len == 0 && escape == 0) {\n            len = r->uri.len + 1;\n            location = path.data + root;\n\n            *last = '/';\n\n        } else {\n            len = r->uri.len + escape + 1;\n\n            if (r->args.len) {\n                len += r->args.len + 1;\n            }\n\n            location = ngx_pnalloc(r->pool, len);\n            if (location == NULL) {\n                ngx_http_clear_location(r);\n                return NGX_HTTP_INTERNAL_SERVER_ERROR;\n            }\n\n            if (escape) {\n                last = (u_char *) ngx_escape_uri(location, r->uri.data,\n                                                 r->uri.len, NGX_ESCAPE_URI);\n\n            } else {\n                last = ngx_copy(location, r->uri.data, r->uri.len);\n            }\n\n            *last = '/';\n\n            if (r->args.len) {\n                *++last = '?';\n                ngx_memcpy(++last, r->args.data, r->args.len);\n            }\n        }\n\n        r->headers_out.location->hash = 1;\n        r->headers_out.location->next = NULL;\n        ngx_str_set(&r->headers_out.location->key, \"Location\");\n        r->headers_out.location->value.len = len;\n        r->headers_out.location->value.data = location;\n\n        return NGX_HTTP_MOVED_PERMANENTLY;\n    }\n\n#if !(NGX_WIN32) /* the not regular files are probably Unix specific */\n\n    if (!of.is_file) {\n        ngx_log_error(NGX_LOG_CRIT, log, 0,\n                      \"\\\"%s\\\" is not a regular file\", path.data);\n\n        return NGX_HTTP_NOT_FOUND;\n    }\n\n#endif\n\n    if (r->method == NGX_HTTP_POST) {\n        return NGX_HTTP_NOT_ALLOWED;\n    }\n\n    rc = ngx_http_discard_request_body(r);\n\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    log->action = \"sending response to client\";\n\n    r->headers_out.status = NGX_HTTP_OK;\n    r->headers_out.content_length_n = of.size;\n    r->headers_out.last_modified_time = of.mtime;\n\n    if (ngx_http_set_etag(r) != NGX_OK) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    if (ngx_http_set_content_type(r) != NGX_OK) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    r->allow_ranges = 1;\n\n    /* we need to allocate all before the header would be sent */\n\n    b = ngx_calloc_buf(r->pool);\n    if (b == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t));\n    if (b->file == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    rc = ngx_http_send_header(r);\n\n    if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {\n        return rc;\n    }\n\n    b->file_pos = 0;\n    b->file_last = of.size;\n\n    b->in_file = b->file_last ? 1 : 0;\n    b->last_buf = (r == r->main) ? 1 : 0;\n    b->last_in_chain = 1;\n    b->sync = (b->last_buf || b->in_file) ? 0 : 1;\n\n    b->file->fd = of.fd;\n    b->file->name = path;\n    b->file->log = log;\n    b->file->directio = of.is_directio;\n\n    out.buf = b;\n    out.next = NULL;\n\n    return ngx_http_output_filter(r, &out);\n}\n\n\nstatic ngx_int_t\nngx_http_static_init(ngx_conf_t *cf)\n{\n    ngx_http_handler_pt        *h;\n    ngx_http_core_main_conf_t  *cmcf;\n\n    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);\n\n    h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);\n    if (h == NULL) {\n        return NGX_ERROR;\n    }\n\n    *h = ngx_http_static_handler;\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "src/http/modules/ngx_http_stub_status_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\nstatic ngx_int_t ngx_http_stub_status_handler(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_stub_status_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_stub_status_add_variables(ngx_conf_t *cf);\nstatic char *ngx_http_set_stub_status(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n#if (T_NGX_HTTP_STUB_STATUS)\nstatic ngx_int_t ngx_http_stub_status_init(ngx_conf_t *cf);\n#endif\n\n\nstatic ngx_command_t  ngx_http_status_commands[] = {\n\n    { ngx_string(\"stub_status\"),\n      NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS|NGX_CONF_TAKE1,\n      ngx_http_set_stub_status,\n      0,\n      0,\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_stub_status_module_ctx = {\n    ngx_http_stub_status_add_variables,    /* preconfiguration */\n#if (T_NGX_HTTP_STUB_STATUS)\n    ngx_http_stub_status_init,             /* postconfiguration */\n#else\n    NULL,                                  /* postconfiguration */\n#endif\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\nngx_module_t  ngx_http_stub_status_module = {\n    NGX_MODULE_V1,\n    &ngx_http_stub_status_module_ctx,      /* module context */\n    ngx_http_status_commands,              /* 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_http_variable_t  ngx_http_stub_status_vars[] = {\n\n    { ngx_string(\"connections_active\"), NULL, ngx_http_stub_status_variable,\n      0, NGX_HTTP_VAR_NOCACHEABLE, 0 },\n\n    { ngx_string(\"connections_reading\"), NULL, ngx_http_stub_status_variable,\n      1, NGX_HTTP_VAR_NOCACHEABLE, 0 },\n\n    { ngx_string(\"connections_writing\"), NULL, ngx_http_stub_status_variable,\n      2, NGX_HTTP_VAR_NOCACHEABLE, 0 },\n\n    { ngx_string(\"connections_waiting\"), NULL, ngx_http_stub_status_variable,\n      3, NGX_HTTP_VAR_NOCACHEABLE, 0 },\n\n      ngx_http_null_variable\n};\n\n\nstatic ngx_int_t\nngx_http_stub_status_handler(ngx_http_request_t *r)\n{\n    size_t             size;\n    ngx_int_t          rc;\n    ngx_buf_t         *b;\n    ngx_chain_t        out;\n    ngx_atomic_int_t   ap, hn, ac, rq, rd, wr, wa;\n\n#if (T_NGX_HTTP_STUB_STATUS)\n    ngx_atomic_int_t   rt;\n#endif\n\n    if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {\n        return NGX_HTTP_NOT_ALLOWED;\n    }\n\n    rc = ngx_http_discard_request_body(r);\n\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    r->headers_out.content_type_len = sizeof(\"text/plain\") - 1;\n    ngx_str_set(&r->headers_out.content_type, \"text/plain\");\n    r->headers_out.content_type_lowcase = NULL;\n\n    size = sizeof(\"Active connections:  \\n\") + NGX_ATOMIC_T_LEN\n#if (T_NGX_HTTP_STUB_STATUS)\n           + sizeof(\"server accepts handled requests request_time\\n\") - 1\n           + 6 + 4 * NGX_ATOMIC_T_LEN\n#else\n           + sizeof(\"server accepts handled requests\\n\") - 1\n           + 6 + 3 * NGX_ATOMIC_T_LEN\n#endif\n           + sizeof(\"Reading:  Writing:  Waiting:  \\n\") + 3 * NGX_ATOMIC_T_LEN;\n\n    b = ngx_create_temp_buf(r->pool, size);\n    if (b == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    out.buf = b;\n    out.next = NULL;\n\n    ap = *ngx_stat_accepted;\n    hn = *ngx_stat_handled;\n    ac = *ngx_stat_active;\n    rq = *ngx_stat_requests;\n    rd = *ngx_stat_reading;\n    wr = *ngx_stat_writing;\n    wa = *ngx_stat_waiting;\n#if (T_NGX_HTTP_STUB_STATUS)\n    rt = *ngx_stat_request_time;\n#endif\n\n    b->last = ngx_sprintf(b->last, \"Active connections: %uA \\n\", ac);\n\n#if (T_NGX_HTTP_STUB_STATUS)\n    b->last = ngx_cpymem(b->last,\n        \"server accepts handled requests request_time\\n\",\n        sizeof(\"server accepts handled requests request_time\\n\") - 1);\n\n    b->last = ngx_sprintf(b->last, \" %uA %uA %uA %uA\\n\", ap, hn, rq, rt);\n#else\n    b->last = ngx_cpymem(b->last, \"server accepts handled requests\\n\",\n                         sizeof(\"server accepts handled requests\\n\") - 1);\n\n    b->last = ngx_sprintf(b->last, \" %uA %uA %uA \\n\", ap, hn, rq);\n#endif\n\n    b->last = ngx_sprintf(b->last, \"Reading: %uA Writing: %uA Waiting: %uA \\n\",\n                          rd, wr, wa);\n\n    r->headers_out.status = NGX_HTTP_OK;\n    r->headers_out.content_length_n = b->last - b->pos;\n\n    b->last_buf = (r == r->main) ? 1 : 0;\n    b->last_in_chain = 1;\n\n    rc = ngx_http_send_header(r);\n\n    if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {\n        return rc;\n    }\n\n    return ngx_http_output_filter(r, &out);\n}\n\n\nstatic ngx_int_t\nngx_http_stub_status_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    u_char            *p;\n    ngx_atomic_int_t   value;\n\n    p = ngx_pnalloc(r->pool, NGX_ATOMIC_T_LEN);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    switch (data) {\n    case 0:\n        value = *ngx_stat_active;\n        break;\n\n    case 1:\n        value = *ngx_stat_reading;\n        break;\n\n    case 2:\n        value = *ngx_stat_writing;\n        break;\n\n    case 3:\n        value = *ngx_stat_waiting;\n        break;\n\n    /* suppress warning */\n    default:\n        value = 0;\n        break;\n    }\n\n    v->len = ngx_sprintf(p, \"%uA\", value) - p;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = p;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_stub_status_add_variables(ngx_conf_t *cf)\n{\n    ngx_http_variable_t  *var, *v;\n\n    for (v = ngx_http_stub_status_vars; v->name.len; v++) {\n        var = ngx_http_add_variable(cf, &v->name, v->flags);\n        if (var == NULL) {\n            return NGX_ERROR;\n        }\n\n        var->get_handler = v->get_handler;\n        var->data = v->data;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic char *\nngx_http_set_stub_status(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_core_loc_conf_t  *clcf;\n\n    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);\n    clcf->handler = ngx_http_stub_status_handler;\n\n    return NGX_CONF_OK;\n}\n\n\n#if (T_NGX_HTTP_STUB_STATUS)\nstatic ngx_int_t\nngx_http_status_log_handler(ngx_http_request_t *r)\n{\n    ngx_time_t                *tp;\n    ngx_msec_int_t             ms;\n#if (T_NGX_RET_CACHE)\n    struct timeval             tv;\n    ngx_http_core_loc_conf_t  *clcf;\n#endif\n\n    if (r != r->main) {\n        return NGX_OK;\n    }\n\n#if (T_NGX_RET_CACHE)\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n    if (clcf->request_time_cache) {\n        tp = ngx_timeofday();\n\n        ms = (ngx_msec_int_t)\n                 ((tp->sec - r->start_sec) * 1000 + (tp->msec - r->start_msec));\n    } else {\n        ngx_gettimeofday(&tv);\n\n        ms = (ngx_msec_int_t) ((tv.tv_sec - r->start_sec) * 1000\n                 + (tv.tv_usec / 1000 - r->start_msec));\n    }\n\n#else \n    tp = ngx_timeofday();\n    ms = (ngx_msec_int_t)\n             ((tp->sec - r->start_sec) * 1000 + (tp->msec - r->start_msec));    \n#endif\n\n    ms = ngx_max(ms, 0);\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http status: request_time %M\", ms);\n\n    (void) ngx_atomic_fetch_add(ngx_stat_request_time, ms);\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_stub_status_init(ngx_conf_t *cf)\n{\n    ngx_http_handler_pt        *h;\n    ngx_http_core_main_conf_t  *cmcf;\n\n    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);\n\n    h = ngx_array_push(&cmcf->phases[NGX_HTTP_LOG_PHASE].handlers);\n    if (h == NULL) {\n        return NGX_ERROR;\n    }\n\n    *h = ngx_http_status_log_handler;\n\n    return NGX_OK;\n}\n#endif\n\n"
  },
  {
    "path": "src/http/modules/ngx_http_sub_filter_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\ntypedef struct {\n    ngx_http_complex_value_t   match;\n    ngx_http_complex_value_t   value;\n} ngx_http_sub_pair_t;\n\n\ntypedef struct {\n    ngx_str_t                  match;\n    ngx_http_complex_value_t  *value;\n} ngx_http_sub_match_t;\n\n\ntypedef struct {\n    ngx_uint_t                 min_match_len;\n    ngx_uint_t                 max_match_len;\n\n    u_char                     index[257];\n    u_char                     shift[256];\n} ngx_http_sub_tables_t;\n\n\ntypedef struct {\n    ngx_uint_t                 dynamic; /* unsigned dynamic:1; */\n\n    ngx_array_t               *pairs;\n\n    ngx_http_sub_tables_t     *tables;\n\n    ngx_hash_t                 types;\n\n    ngx_flag_t                 once;\n    ngx_flag_t                 last_modified;\n\n    ngx_array_t               *types_keys;\n    ngx_array_t               *matches;\n} ngx_http_sub_loc_conf_t;\n\n\ntypedef struct {\n    ngx_str_t                  saved;\n    ngx_str_t                  looked;\n\n    ngx_uint_t                 once;   /* unsigned  once:1 */\n\n    ngx_buf_t                 *buf;\n\n    u_char                    *pos;\n    u_char                    *copy_start;\n    u_char                    *copy_end;\n\n    ngx_chain_t               *in;\n    ngx_chain_t               *out;\n    ngx_chain_t              **last_out;\n    ngx_chain_t               *busy;\n    ngx_chain_t               *free;\n\n    ngx_str_t                 *sub;\n    ngx_uint_t                 applied;\n\n    ngx_int_t                  offset;\n    ngx_uint_t                 index;\n\n    ngx_http_sub_tables_t     *tables;\n    ngx_array_t               *matches;\n} ngx_http_sub_ctx_t;\n\n\nstatic ngx_uint_t ngx_http_sub_cmp_index;\n\n\nstatic ngx_int_t ngx_http_sub_output(ngx_http_request_t *r,\n    ngx_http_sub_ctx_t *ctx);\nstatic ngx_int_t ngx_http_sub_parse(ngx_http_request_t *r,\n    ngx_http_sub_ctx_t *ctx, ngx_uint_t flush);\nstatic ngx_int_t ngx_http_sub_match(ngx_http_sub_ctx_t *ctx, ngx_int_t start,\n    ngx_str_t *m);\n\nstatic char * ngx_http_sub_filter(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic void *ngx_http_sub_create_conf(ngx_conf_t *cf);\nstatic char *ngx_http_sub_merge_conf(ngx_conf_t *cf,\n    void *parent, void *child);\nstatic void ngx_http_sub_init_tables(ngx_http_sub_tables_t *tables,\n    ngx_http_sub_match_t *match, ngx_uint_t n);\nstatic ngx_int_t ngx_http_sub_cmp_matches(const void *one, const void *two);\nstatic ngx_int_t ngx_http_sub_filter_init(ngx_conf_t *cf);\n\n\nstatic ngx_command_t  ngx_http_sub_filter_commands[] = {\n\n    { ngx_string(\"sub_filter\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,\n      ngx_http_sub_filter,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"sub_filter_types\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,\n      ngx_http_types_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_sub_loc_conf_t, types_keys),\n      &ngx_http_html_default_types[0] },\n\n    { ngx_string(\"sub_filter_once\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_sub_loc_conf_t, once),\n      NULL },\n\n    { ngx_string(\"sub_filter_last_modified\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_sub_loc_conf_t, last_modified),\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_sub_filter_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    ngx_http_sub_filter_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    ngx_http_sub_create_conf,              /* create location configuration */\n    ngx_http_sub_merge_conf                /* merge location configuration */\n};\n\n\nngx_module_t  ngx_http_sub_filter_module = {\n    NGX_MODULE_V1,\n    &ngx_http_sub_filter_module_ctx,       /* module context */\n    ngx_http_sub_filter_commands,          /* 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_http_output_header_filter_pt  ngx_http_next_header_filter;\nstatic ngx_http_output_body_filter_pt    ngx_http_next_body_filter;\n\n\nstatic ngx_int_t\nngx_http_sub_header_filter(ngx_http_request_t *r)\n{\n    ngx_str_t                *m;\n    ngx_uint_t                i, j, n;\n    ngx_http_sub_ctx_t       *ctx;\n    ngx_http_sub_pair_t      *pairs;\n    ngx_http_sub_match_t     *matches;\n    ngx_http_sub_loc_conf_t  *slcf;\n\n    slcf = ngx_http_get_module_loc_conf(r, ngx_http_sub_filter_module);\n\n    if (slcf->pairs == NULL\n        || r->headers_out.content_length_n == 0\n        || ngx_http_test_content_type(r, &slcf->types) == NULL)\n    {\n        return ngx_http_next_header_filter(r);\n    }\n\n    ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_sub_ctx_t));\n    if (ctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (slcf->dynamic == 0) {\n        ctx->tables = slcf->tables;\n        ctx->matches = slcf->matches;\n\n    } else {\n        pairs = slcf->pairs->elts;\n        n = slcf->pairs->nelts;\n\n        matches = ngx_pcalloc(r->pool, sizeof(ngx_http_sub_match_t) * n);\n        if (matches == NULL) {\n            return NGX_ERROR;\n        }\n\n        j = 0;\n        for (i = 0; i < n; i++) {\n            matches[j].value = &pairs[i].value;\n\n            if (pairs[i].match.lengths == NULL) {\n                matches[j].match = pairs[i].match.value;\n                j++;\n                continue;\n            }\n\n            m = &matches[j].match;\n            if (ngx_http_complex_value(r, &pairs[i].match, m) != NGX_OK) {\n                return NGX_ERROR;\n            }\n\n            if (m->len == 0) {\n                continue;\n            }\n\n            ngx_strlow(m->data, m->data, m->len);\n            j++;\n        }\n\n        if (j == 0) {\n            return ngx_http_next_header_filter(r);\n        }\n\n        ctx->matches = ngx_palloc(r->pool, sizeof(ngx_array_t));\n        if (ctx->matches == NULL) {\n            return NGX_ERROR;\n        }\n\n        ctx->matches->elts = matches;\n        ctx->matches->nelts = j;\n\n        ctx->tables = ngx_palloc(r->pool, sizeof(ngx_http_sub_tables_t));\n        if (ctx->tables == NULL) {\n            return NGX_ERROR;\n        }\n\n        ngx_http_sub_init_tables(ctx->tables, ctx->matches->elts,\n                                 ctx->matches->nelts);\n    }\n\n    ctx->saved.data = ngx_pnalloc(r->pool, ctx->tables->max_match_len - 1);\n    if (ctx->saved.data == NULL) {\n        return NGX_ERROR;\n    }\n\n    ctx->looked.data = ngx_pnalloc(r->pool, ctx->tables->max_match_len - 1);\n    if (ctx->looked.data == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_http_set_ctx(r, ctx, ngx_http_sub_filter_module);\n\n    ctx->offset = ctx->tables->min_match_len - 1;\n    ctx->last_out = &ctx->out;\n\n    r->filter_need_in_memory = 1;\n\n    if (r == r->main) {\n        ngx_http_clear_content_length(r);\n\n        if (!slcf->last_modified) {\n            ngx_http_clear_last_modified(r);\n            ngx_http_clear_etag(r);\n\n        } else {\n            ngx_http_weak_etag(r);\n        }\n    }\n\n    return ngx_http_next_header_filter(r);\n}\n\n\nstatic ngx_int_t\nngx_http_sub_body_filter(ngx_http_request_t *r, ngx_chain_t *in)\n{\n    ngx_int_t                  rc;\n    ngx_buf_t                 *b;\n    ngx_str_t                 *sub;\n    ngx_uint_t                 flush, last;\n    ngx_chain_t               *cl;\n    ngx_http_sub_ctx_t        *ctx;\n    ngx_http_sub_match_t      *match;\n    ngx_http_sub_loc_conf_t   *slcf;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_sub_filter_module);\n\n    if (ctx == NULL) {\n        return ngx_http_next_body_filter(r, in);\n    }\n\n    if ((in == NULL\n         && ctx->buf == NULL\n         && ctx->in == NULL\n         && ctx->busy == NULL))\n    {\n        return ngx_http_next_body_filter(r, in);\n    }\n\n    if (ctx->once && (ctx->buf == NULL || ctx->in == NULL)) {\n\n        if (ctx->busy) {\n            if (ngx_http_sub_output(r, ctx) == NGX_ERROR) {\n                return NGX_ERROR;\n            }\n        }\n\n        return ngx_http_next_body_filter(r, in);\n    }\n\n    /* add the incoming chain to the chain ctx->in */\n\n    if (in) {\n        if (ngx_chain_add_copy(r->pool, &ctx->in, in) != NGX_OK) {\n            return NGX_ERROR;\n        }\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http sub filter \\\"%V\\\"\", &r->uri);\n\n    flush = 0;\n    last = 0;\n\n    while (ctx->in || ctx->buf) {\n\n        if (ctx->buf == NULL) {\n            ctx->buf = ctx->in->buf;\n            ctx->in = ctx->in->next;\n            ctx->pos = ctx->buf->pos;\n        }\n\n        if (ctx->buf->flush || ctx->buf->recycled) {\n            flush = 1;\n        }\n\n        if (ctx->in == NULL) {\n            last = flush;\n        }\n\n        b = NULL;\n\n        while (ctx->pos < ctx->buf->last) {\n\n            rc = ngx_http_sub_parse(r, ctx, last);\n\n            ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"parse: %i, looked: \\\"%V\\\" %p-%p\",\n                           rc, &ctx->looked, ctx->copy_start, ctx->copy_end);\n\n            if (rc == NGX_ERROR) {\n                return rc;\n            }\n\n            if (ctx->saved.len) {\n\n                ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                               \"saved: \\\"%V\\\"\", &ctx->saved);\n\n                cl = ngx_chain_get_free_buf(r->pool, &ctx->free);\n                if (cl == NULL) {\n                    return NGX_ERROR;\n                }\n\n                b = cl->buf;\n\n                ngx_memzero(b, sizeof(ngx_buf_t));\n\n                b->pos = ngx_pnalloc(r->pool, ctx->saved.len);\n                if (b->pos == NULL) {\n                    return NGX_ERROR;\n                }\n\n                ngx_memcpy(b->pos, ctx->saved.data, ctx->saved.len);\n                b->last = b->pos + ctx->saved.len;\n                b->memory = 1;\n\n                *ctx->last_out = cl;\n                ctx->last_out = &cl->next;\n\n                ctx->saved.len = 0;\n            }\n\n            if (ctx->copy_start != ctx->copy_end) {\n\n                cl = ngx_chain_get_free_buf(r->pool, &ctx->free);\n                if (cl == NULL) {\n                    return NGX_ERROR;\n                }\n\n                b = cl->buf;\n\n                ngx_memcpy(b, ctx->buf, sizeof(ngx_buf_t));\n\n                b->pos = ctx->copy_start;\n                b->last = ctx->copy_end;\n                b->shadow = NULL;\n                b->last_buf = 0;\n                b->last_in_chain = 0;\n                b->recycled = 0;\n\n                if (b->in_file) {\n                    b->file_last = b->file_pos + (b->last - ctx->buf->pos);\n                    b->file_pos += b->pos - ctx->buf->pos;\n                }\n\n                *ctx->last_out = cl;\n                ctx->last_out = &cl->next;\n            }\n\n            if (rc == NGX_AGAIN) {\n                continue;\n            }\n\n\n            /* rc == NGX_OK */\n\n            cl = ngx_chain_get_free_buf(r->pool, &ctx->free);\n            if (cl == NULL) {\n                return NGX_ERROR;\n            }\n\n            b = cl->buf;\n\n            ngx_memzero(b, sizeof(ngx_buf_t));\n\n            slcf = ngx_http_get_module_loc_conf(r, ngx_http_sub_filter_module);\n\n            if (ctx->sub == NULL) {\n                ctx->sub = ngx_pcalloc(r->pool, sizeof(ngx_str_t)\n                                                * ctx->matches->nelts);\n                if (ctx->sub == NULL) {\n                    return NGX_ERROR;\n                }\n            }\n\n            sub = &ctx->sub[ctx->index];\n\n            if (sub->data == NULL) {\n                match = ctx->matches->elts;\n\n                if (ngx_http_complex_value(r, match[ctx->index].value, sub)\n                    != NGX_OK)\n                {\n                    return NGX_ERROR;\n                }\n            }\n\n            if (sub->len) {\n                b->memory = 1;\n                b->pos = sub->data;\n                b->last = sub->data + sub->len;\n\n            } else {\n                b->sync = 1;\n            }\n\n            *ctx->last_out = cl;\n            ctx->last_out = &cl->next;\n\n            ctx->index = 0;\n            ctx->once = slcf->once && (++ctx->applied == ctx->matches->nelts);\n\n            continue;\n        }\n\n        if (ctx->looked.len\n            && (ctx->buf->last_buf || ctx->buf->last_in_chain))\n        {\n            cl = ngx_chain_get_free_buf(r->pool, &ctx->free);\n            if (cl == NULL) {\n                return NGX_ERROR;\n            }\n\n            b = cl->buf;\n\n            ngx_memzero(b, sizeof(ngx_buf_t));\n\n            b->pos = ctx->looked.data;\n            b->last = b->pos + ctx->looked.len;\n            b->memory = 1;\n\n            *ctx->last_out = cl;\n            ctx->last_out = &cl->next;\n\n            ctx->looked.len = 0;\n        }\n\n        if (ctx->buf->last_buf || ctx->buf->flush || ctx->buf->sync\n            || ngx_buf_in_memory(ctx->buf))\n        {\n            if (b == NULL) {\n                cl = ngx_chain_get_free_buf(r->pool, &ctx->free);\n                if (cl == NULL) {\n                    return NGX_ERROR;\n                }\n\n                b = cl->buf;\n\n                ngx_memzero(b, sizeof(ngx_buf_t));\n\n                b->sync = 1;\n\n                *ctx->last_out = cl;\n                ctx->last_out = &cl->next;\n            }\n\n            b->last_buf = ctx->buf->last_buf;\n            b->last_in_chain = ctx->buf->last_in_chain;\n            b->flush = ctx->buf->flush;\n            b->shadow = ctx->buf;\n\n            b->recycled = ctx->buf->recycled;\n        }\n\n        ctx->buf = NULL;\n    }\n\n    if (ctx->out == NULL && ctx->busy == NULL) {\n        return NGX_OK;\n    }\n\n    return ngx_http_sub_output(r, ctx);\n}\n\n\nstatic ngx_int_t\nngx_http_sub_output(ngx_http_request_t *r, ngx_http_sub_ctx_t *ctx)\n{\n    ngx_int_t     rc;\n    ngx_buf_t    *b;\n    ngx_chain_t  *cl;\n\n#if 1\n    b = NULL;\n    for (cl = ctx->out; cl; cl = cl->next) {\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"sub out: %p %p\", cl->buf, cl->buf->pos);\n        if (cl->buf == b) {\n            ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,\n                          \"the same buf was used in sub\");\n            ngx_debug_point();\n            return NGX_ERROR;\n        }\n        b = cl->buf;\n    }\n#endif\n\n    rc = ngx_http_next_body_filter(r, ctx->out);\n\n    if (ctx->busy == NULL) {\n        ctx->busy = ctx->out;\n\n    } else {\n        for (cl = ctx->busy; cl->next; cl = cl->next) { /* void */ }\n        cl->next = ctx->out;\n    }\n\n    ctx->out = NULL;\n    ctx->last_out = &ctx->out;\n\n    while (ctx->busy) {\n\n        cl = ctx->busy;\n        b = cl->buf;\n\n        if (ngx_buf_size(b) != 0) {\n            break;\n        }\n\n        if (b->shadow) {\n            b->shadow->pos = b->shadow->last;\n        }\n\n        ctx->busy = cl->next;\n\n        if (ngx_buf_in_memory(b) || b->in_file) {\n            /* add data bufs only to the free buf chain */\n\n            cl->next = ctx->free;\n            ctx->free = cl;\n        }\n    }\n\n    if (ctx->in || ctx->buf) {\n        r->buffered |= NGX_HTTP_SUB_BUFFERED;\n\n    } else {\n        r->buffered &= ~NGX_HTTP_SUB_BUFFERED;\n    }\n\n    return rc;\n}\n\n\nstatic ngx_int_t\nngx_http_sub_parse(ngx_http_request_t *r, ngx_http_sub_ctx_t *ctx,\n    ngx_uint_t flush)\n{\n    u_char                   *p, c;\n    ngx_str_t                *m;\n    ngx_int_t                 offset, start, next, end, len, rc;\n    ngx_uint_t                shift, i, j;\n    ngx_http_sub_match_t     *match;\n    ngx_http_sub_tables_t    *tables;\n    ngx_http_sub_loc_conf_t  *slcf;\n\n    slcf = ngx_http_get_module_loc_conf(r, ngx_http_sub_filter_module);\n    tables = ctx->tables;\n    match = ctx->matches->elts;\n\n    offset = ctx->offset;\n    end = ctx->buf->last - ctx->pos;\n\n    if (ctx->once) {\n        /* sets start and next to end */\n        offset = end + (ngx_int_t) tables->min_match_len - 1;\n        goto again;\n    }\n\n    while (offset < end) {\n\n        c = offset < 0 ? ctx->looked.data[ctx->looked.len + offset]\n                       : ctx->pos[offset];\n\n        c = ngx_tolower(c);\n\n        shift = tables->shift[c];\n        if (shift > 0) {\n            offset += shift;\n            continue;\n        }\n\n        /* a potential match */\n\n        start = offset - (ngx_int_t) tables->min_match_len + 1;\n\n        i = ngx_max((ngx_uint_t) tables->index[c], ctx->index);\n        j = tables->index[c + 1];\n\n        while (i != j) {\n\n            if (slcf->once && ctx->sub && ctx->sub[i].data) {\n                goto next;\n            }\n\n            m = &match[i].match;\n\n            rc = ngx_http_sub_match(ctx, start, m);\n\n            if (rc == NGX_DECLINED) {\n                goto next;\n            }\n\n            ctx->index = i;\n\n            if (rc == NGX_AGAIN) {\n                goto again;\n            }\n\n            ctx->offset = offset + (ngx_int_t) m->len;\n            next = start + (ngx_int_t) m->len;\n            end = ngx_max(next, 0);\n            rc = NGX_OK;\n\n            goto done;\n\n        next:\n\n            i++;\n        }\n\n        offset++;\n        ctx->index = 0;\n    }\n\n    if (flush) {\n        for ( ;; ) {\n            start = offset - (ngx_int_t) tables->min_match_len + 1;\n\n            if (start >= end) {\n                break;\n            }\n\n            for (i = 0; i < ctx->matches->nelts; i++) {\n                m = &match[i].match;\n\n                if (ngx_http_sub_match(ctx, start, m) == NGX_AGAIN) {\n                    goto again;\n                }\n            }\n\n            offset++;\n        }\n    }\n\nagain:\n\n    ctx->offset = offset;\n    start = offset - (ngx_int_t) tables->min_match_len + 1;\n    next = start;\n    rc = NGX_AGAIN;\n\ndone:\n\n    /* send [ - looked.len, start ] to client */\n\n    ctx->saved.len = ctx->looked.len + ngx_min(start, 0);\n    ngx_memcpy(ctx->saved.data, ctx->looked.data, ctx->saved.len);\n\n    ctx->copy_start = ctx->pos;\n    ctx->copy_end = ctx->pos + ngx_max(start, 0);\n\n    /* save [ next, end ] in looked */\n\n    len = ngx_min(next, 0);\n    p = ctx->looked.data;\n    p = ngx_movemem(p, p + ctx->looked.len + len, - len);\n\n    len = ngx_max(next, 0);\n    p = ngx_cpymem(p, ctx->pos + len, end - len);\n    ctx->looked.len = p - ctx->looked.data;\n\n    /* update position */\n\n    ctx->pos += end;\n    ctx->offset -= end;\n\n    return rc;\n}\n\n\nstatic ngx_int_t\nngx_http_sub_match(ngx_http_sub_ctx_t *ctx, ngx_int_t start, ngx_str_t *m)\n{\n    u_char  *p, *last, *pat, *pat_end;\n\n    pat = m->data;\n    pat_end = m->data + m->len;\n\n    if (start >= 0) {\n        p = ctx->pos + start;\n\n    } else {\n        last = ctx->looked.data + ctx->looked.len;\n        p = last + start;\n\n        while (p < last && pat < pat_end) {\n            if (ngx_tolower(*p) != *pat) {\n                return NGX_DECLINED;\n            }\n\n            p++;\n            pat++;\n        }\n\n        p = ctx->pos;\n    }\n\n    while (p < ctx->buf->last && pat < pat_end) {\n        if (ngx_tolower(*p) != *pat) {\n            return NGX_DECLINED;\n        }\n\n        p++;\n        pat++;\n    }\n\n    if (pat != pat_end) {\n        /* partial match */\n        return NGX_AGAIN;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic char *\nngx_http_sub_filter(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_sub_loc_conf_t *slcf = conf;\n\n    ngx_str_t                         *value;\n    ngx_http_sub_pair_t               *pair;\n    ngx_http_compile_complex_value_t   ccv;\n\n    value = cf->args->elts;\n\n    if (value[1].len == 0) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, \"empty search pattern\");\n        return NGX_CONF_ERROR;\n    }\n\n    if (slcf->pairs == NULL) {\n        slcf->pairs = ngx_array_create(cf->pool, 1,\n                                       sizeof(ngx_http_sub_pair_t));\n        if (slcf->pairs == NULL) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    if (slcf->pairs->nelts == 255) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"number of search patterns exceeds 255\");\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_strlow(value[1].data, value[1].data, value[1].len);\n\n    pair = ngx_array_push(slcf->pairs);\n    if (pair == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n\n    ccv.cf = cf;\n    ccv.value = &value[1];\n    ccv.complex_value = &pair->match;\n\n    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (ccv.complex_value->lengths != NULL) {\n        slcf->dynamic = 1;\n\n    } else {\n        ngx_strlow(pair->match.value.data, pair->match.value.data,\n                   pair->match.value.len);\n    }\n\n    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n\n    ccv.cf = cf;\n    ccv.value = &value[2];\n    ccv.complex_value = &pair->value;\n\n    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic void *\nngx_http_sub_create_conf(ngx_conf_t *cf)\n{\n    ngx_http_sub_loc_conf_t  *slcf;\n\n    slcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_sub_loc_conf_t));\n    if (slcf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc():\n     *\n     *     conf->dynamic = 0;\n     *     conf->pairs = NULL;\n     *     conf->tables = NULL;\n     *     conf->types = { NULL };\n     *     conf->types_keys = NULL;\n     *     conf->matches = NULL;\n     */\n\n    slcf->once = NGX_CONF_UNSET;\n    slcf->last_modified = NGX_CONF_UNSET;\n\n    return slcf;\n}\n\n\nstatic char *\nngx_http_sub_merge_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_uint_t                i, n;\n    ngx_http_sub_pair_t      *pairs;\n    ngx_http_sub_match_t     *matches;\n    ngx_http_sub_loc_conf_t  *prev = parent;\n    ngx_http_sub_loc_conf_t  *conf = child;\n\n    ngx_conf_merge_value(conf->once, prev->once, 1);\n    ngx_conf_merge_value(conf->last_modified, prev->last_modified, 0);\n\n    if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,\n                             &prev->types_keys, &prev->types,\n                             ngx_http_html_default_types)\n        != NGX_OK)\n    {\n        return NGX_CONF_ERROR;\n    }\n\n    if (conf->pairs == NULL) {\n        conf->dynamic = prev->dynamic;\n        conf->pairs = prev->pairs;\n        conf->matches = prev->matches;\n        conf->tables = prev->tables;\n    }\n\n    if (conf->pairs && conf->dynamic == 0 && conf->tables == NULL) {\n        pairs = conf->pairs->elts;\n        n = conf->pairs->nelts;\n\n        matches = ngx_palloc(cf->pool, sizeof(ngx_http_sub_match_t) * n);\n        if (matches == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        for (i = 0; i < n; i++) {\n            matches[i].match = pairs[i].match.value;\n            matches[i].value = &pairs[i].value;\n        }\n\n        conf->matches = ngx_palloc(cf->pool, sizeof(ngx_array_t));\n        if (conf->matches == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        conf->matches->elts = matches;\n        conf->matches->nelts = n;\n\n        conf->tables = ngx_palloc(cf->pool, sizeof(ngx_http_sub_tables_t));\n        if (conf->tables == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        ngx_http_sub_init_tables(conf->tables, conf->matches->elts,\n                                 conf->matches->nelts);\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic void\nngx_http_sub_init_tables(ngx_http_sub_tables_t *tables,\n    ngx_http_sub_match_t *match, ngx_uint_t n)\n{\n    u_char      c;\n    ngx_uint_t  i, j, min, max, ch;\n\n    min = match[0].match.len;\n    max = match[0].match.len;\n\n    for (i = 1; i < n; i++) {\n        min = ngx_min(min, match[i].match.len);\n        max = ngx_max(max, match[i].match.len);\n    }\n\n    tables->min_match_len = min;\n    tables->max_match_len = max;\n\n    ngx_http_sub_cmp_index = tables->min_match_len - 1;\n    ngx_sort(match, n, sizeof(ngx_http_sub_match_t), ngx_http_sub_cmp_matches);\n\n    min = ngx_min(min, 255);\n    ngx_memset(tables->shift, min, 256);\n\n    ch = 0;\n\n    for (i = 0; i < n; i++) {\n\n        for (j = 0; j < min; j++) {\n            c = match[i].match.data[tables->min_match_len - 1 - j];\n            tables->shift[c] = ngx_min(tables->shift[c], (u_char) j);\n        }\n\n        c = match[i].match.data[tables->min_match_len - 1];\n        while (ch <= (ngx_uint_t) c) {\n            tables->index[ch++] = (u_char) i;\n        }\n    }\n\n    while (ch < 257) {\n        tables->index[ch++] = (u_char) n;\n    }\n}\n\n\nstatic ngx_int_t\nngx_http_sub_cmp_matches(const void *one, const void *two)\n{\n    ngx_int_t              c1, c2;\n    ngx_http_sub_match_t  *first, *second;\n\n    first = (ngx_http_sub_match_t *) one;\n    second = (ngx_http_sub_match_t *) two;\n\n    c1 = first->match.data[ngx_http_sub_cmp_index];\n    c2 = second->match.data[ngx_http_sub_cmp_index];\n\n    return c1 - c2;\n}\n\n\nstatic ngx_int_t\nngx_http_sub_filter_init(ngx_conf_t *cf)\n{\n    ngx_http_next_header_filter = ngx_http_top_header_filter;\n    ngx_http_top_header_filter = ngx_http_sub_header_filter;\n\n    ngx_http_next_body_filter = ngx_http_top_body_filter;\n    ngx_http_top_body_filter = ngx_http_sub_body_filter;\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "src/http/modules/ngx_http_try_files_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\ntypedef struct {\n    ngx_array_t           *lengths;\n    ngx_array_t           *values;\n    ngx_str_t              name;\n\n    unsigned               code:10;\n    unsigned               test_dir:1;\n} ngx_http_try_file_t;\n\n\ntypedef struct {\n    ngx_http_try_file_t   *try_files;\n} ngx_http_try_files_loc_conf_t;\n\n\nstatic ngx_int_t ngx_http_try_files_handler(ngx_http_request_t *r);\nstatic char *ngx_http_try_files(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\nstatic void *ngx_http_try_files_create_loc_conf(ngx_conf_t *cf);\nstatic ngx_int_t ngx_http_try_files_init(ngx_conf_t *cf);\n\n\nstatic ngx_command_t  ngx_http_try_files_commands[] = {\n\n    { ngx_string(\"try_files\"),\n      NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_2MORE,\n      ngx_http_try_files,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_try_files_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    ngx_http_try_files_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    ngx_http_try_files_create_loc_conf,    /* create location configuration */\n    NULL                                   /* merge location configuration */\n};\n\n\nngx_module_t  ngx_http_try_files_module = {\n    NGX_MODULE_V1,\n    &ngx_http_try_files_module_ctx,        /* module context */\n    ngx_http_try_files_commands,           /* 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_try_files_handler(ngx_http_request_t *r)\n{\n    size_t                          len, root, alias, reserve, allocated;\n    u_char                         *p, *name;\n    ngx_str_t                       path, args;\n    ngx_uint_t                      test_dir;\n    ngx_http_try_file_t            *tf;\n    ngx_open_file_info_t            of;\n    ngx_http_script_code_pt         code;\n    ngx_http_script_engine_t        e;\n    ngx_http_core_loc_conf_t       *clcf;\n    ngx_http_script_len_code_pt     lcode;\n    ngx_http_try_files_loc_conf_t  *tlcf;\n\n    tlcf = ngx_http_get_module_loc_conf(r, ngx_http_try_files_module);\n\n    if (tlcf->try_files == NULL) {\n        return NGX_DECLINED;\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"try files handler\");\n\n    allocated = 0;\n    root = 0;\n    name = NULL;\n    /* suppress MSVC warning */\n    path.data = NULL;\n\n    tf = tlcf->try_files;\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    alias = clcf->alias;\n\n    for ( ;; ) {\n\n        if (tf->lengths) {\n            ngx_memzero(&e, sizeof(ngx_http_script_engine_t));\n\n            e.ip = tf->lengths->elts;\n            e.request = r;\n\n            /* 1 is for terminating '\\0' as in static names */\n            len = 1;\n\n            while (*(uintptr_t *) e.ip) {\n                lcode = *(ngx_http_script_len_code_pt *) e.ip;\n                len += lcode(&e);\n            }\n\n        } else {\n            len = tf->name.len;\n        }\n\n        if (!alias) {\n            reserve = len > r->uri.len ? len - r->uri.len : 0;\n\n        } else if (alias == NGX_MAX_SIZE_T_VALUE) {\n            reserve = len;\n\n        } else {\n            reserve = len > r->uri.len - alias ? len - (r->uri.len - alias) : 0;\n        }\n\n        if (reserve > allocated || !allocated) {\n\n            /* 16 bytes are preallocation */\n            allocated = reserve + 16;\n\n            if (ngx_http_map_uri_to_path(r, &path, &root, allocated) == NULL) {\n                return NGX_HTTP_INTERNAL_SERVER_ERROR;\n            }\n\n            name = path.data + root;\n        }\n\n        if (tf->values == NULL) {\n\n            /* tf->name.len includes the terminating '\\0' */\n\n            ngx_memcpy(name, tf->name.data, tf->name.len);\n\n            path.len = (name + tf->name.len - 1) - path.data;\n\n        } else {\n            e.ip = tf->values->elts;\n            e.pos = name;\n            e.flushed = 1;\n\n            while (*(uintptr_t *) e.ip) {\n                code = *(ngx_http_script_code_pt *) e.ip;\n                code((ngx_http_script_engine_t *) &e);\n            }\n\n            path.len = e.pos - path.data;\n\n            *e.pos = '\\0';\n\n            if (alias && alias != NGX_MAX_SIZE_T_VALUE\n                && ngx_strncmp(name, r->uri.data, alias) == 0)\n            {\n                ngx_memmove(name, name + alias, len - alias);\n                path.len -= alias;\n            }\n        }\n\n        test_dir = tf->test_dir;\n\n        tf++;\n\n        ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"trying to use %s: \\\"%s\\\" \\\"%s\\\"\",\n                       test_dir ? \"dir\" : \"file\", name, path.data);\n\n        if (tf->lengths == NULL && tf->name.len == 0) {\n\n            if (tf->code) {\n                return tf->code;\n            }\n\n            path.len -= root;\n            path.data += root;\n\n            if (path.data[0] == '@') {\n                (void) ngx_http_named_location(r, &path);\n\n            } else {\n                ngx_http_split_args(r, &path, &args);\n\n                (void) ngx_http_internal_redirect(r, &path, &args);\n            }\n\n            ngx_http_finalize_request(r, NGX_DONE);\n            return NGX_DONE;\n        }\n\n        ngx_memzero(&of, sizeof(ngx_open_file_info_t));\n\n        of.read_ahead = clcf->read_ahead;\n        of.directio = clcf->directio;\n        of.valid = clcf->open_file_cache_valid;\n        of.min_uses = clcf->open_file_cache_min_uses;\n        of.test_only = 1;\n        of.errors = clcf->open_file_cache_errors;\n        of.events = clcf->open_file_cache_events;\n\n        if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) {\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n\n        if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)\n            != NGX_OK)\n        {\n            if (of.err == 0) {\n                return NGX_HTTP_INTERNAL_SERVER_ERROR;\n            }\n\n            if (of.err != NGX_ENOENT\n                && of.err != NGX_ENOTDIR\n                && of.err != NGX_ENAMETOOLONG)\n            {\n                ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err,\n                              \"%s \\\"%s\\\" failed\", of.failed, path.data);\n            }\n\n            continue;\n        }\n\n        if (of.is_dir != test_dir) {\n            continue;\n        }\n\n        path.len -= root;\n        path.data += root;\n\n        if (!alias) {\n            r->uri = path;\n\n        } else if (alias == NGX_MAX_SIZE_T_VALUE) {\n            if (!test_dir) {\n                r->uri = path;\n                r->add_uri_to_alias = 1;\n            }\n\n        } else {\n            name = r->uri.data;\n\n            r->uri.len = alias + path.len;\n            r->uri.data = ngx_pnalloc(r->pool, r->uri.len);\n            if (r->uri.data == NULL) {\n                r->uri.len = 0;\n                return NGX_HTTP_INTERNAL_SERVER_ERROR;\n            }\n\n            p = ngx_copy(r->uri.data, name, alias);\n            ngx_memcpy(p, path.data, path.len);\n        }\n\n        ngx_http_set_exten(r);\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"try file uri: \\\"%V\\\"\", &r->uri);\n\n        return NGX_DECLINED;\n    }\n\n    /* not reached */\n}\n\n\nstatic char *\nngx_http_try_files(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_try_files_loc_conf_t *tlcf = conf;\n\n    ngx_str_t                  *value;\n    ngx_int_t                   code;\n    ngx_uint_t                  i, n;\n    ngx_http_try_file_t        *tf;\n    ngx_http_script_compile_t   sc;\n\n    if (tlcf->try_files) {\n        return \"is duplicate\";\n    }\n\n    tf = ngx_pcalloc(cf->pool, cf->args->nelts * sizeof(ngx_http_try_file_t));\n    if (tf == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    tlcf->try_files = tf;\n\n    value = cf->args->elts;\n\n    for (i = 0; i < cf->args->nelts - 1; i++) {\n\n        tf[i].name = value[i + 1];\n\n        if (tf[i].name.len > 0\n            && tf[i].name.data[tf[i].name.len - 1] == '/'\n            && i + 2 < cf->args->nelts)\n        {\n            tf[i].test_dir = 1;\n            tf[i].name.len--;\n            tf[i].name.data[tf[i].name.len] = '\\0';\n        }\n\n        n = ngx_http_script_variables_count(&tf[i].name);\n\n        if (n) {\n            ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));\n\n            sc.cf = cf;\n            sc.source = &tf[i].name;\n            sc.lengths = &tf[i].lengths;\n            sc.values = &tf[i].values;\n            sc.variables = n;\n            sc.complete_lengths = 1;\n            sc.complete_values = 1;\n\n            if (ngx_http_script_compile(&sc) != NGX_OK) {\n                return NGX_CONF_ERROR;\n            }\n\n        } else {\n            /* add trailing '\\0' to length */\n            tf[i].name.len++;\n        }\n    }\n\n    if (tf[i - 1].name.data[0] == '=') {\n\n        code = ngx_atoi(tf[i - 1].name.data + 1, tf[i - 1].name.len - 2);\n\n        if (code == NGX_ERROR || code > 999) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"invalid code \\\"%*s\\\"\",\n                               tf[i - 1].name.len - 1, tf[i - 1].name.data);\n            return NGX_CONF_ERROR;\n        }\n\n        tf[i].code = code;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic void *\nngx_http_try_files_create_loc_conf(ngx_conf_t *cf)\n{\n    ngx_http_try_files_loc_conf_t  *tlcf;\n\n    tlcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_try_files_loc_conf_t));\n    if (tlcf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc():\n     *\n     *     tlcf->try_files = NULL;\n     */\n\n    return tlcf;\n}\n\n\nstatic ngx_int_t\nngx_http_try_files_init(ngx_conf_t *cf)\n{\n    ngx_http_handler_pt        *h;\n    ngx_http_core_main_conf_t  *cmcf;\n\n    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);\n\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_try_files_handler;\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "src/http/modules/ngx_http_upstream_hash_module.c",
    "content": "\n/*\n * Copyright (C) Roman Arutyunyan\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n#if (NGX_HTTP_UPSTREAM_CHECK)\n#include \"ngx_http_upstream_check_module.h\"\n#endif\n\ntypedef struct {\n    uint32_t                            hash;\n    ngx_str_t                          *server;\n} ngx_http_upstream_chash_point_t;\n\n\ntypedef struct {\n    ngx_uint_t                          number;\n    ngx_http_upstream_chash_point_t     point[1];\n} ngx_http_upstream_chash_points_t;\n\n\ntypedef struct {\n    ngx_http_complex_value_t            key;\n    ngx_http_upstream_chash_points_t   *points;\n} ngx_http_upstream_hash_srv_conf_t;\n\n\ntypedef struct {\n    /* the round robin data must be first */\n    ngx_http_upstream_rr_peer_data_t    rrp;\n    ngx_http_upstream_hash_srv_conf_t  *conf;\n    ngx_str_t                           key;\n    ngx_uint_t                          tries;\n    ngx_uint_t                          rehash;\n    uint32_t                            hash;\n    ngx_event_get_peer_pt               get_rr_peer;\n} ngx_http_upstream_hash_peer_data_t;\n\n\nstatic ngx_int_t ngx_http_upstream_init_hash(ngx_conf_t *cf,\n    ngx_http_upstream_srv_conf_t *us);\nstatic ngx_int_t ngx_http_upstream_init_hash_peer(ngx_http_request_t *r,\n    ngx_http_upstream_srv_conf_t *us);\nstatic ngx_int_t ngx_http_upstream_get_hash_peer(ngx_peer_connection_t *pc,\n    void *data);\n\nstatic ngx_int_t ngx_http_upstream_init_chash(ngx_conf_t *cf,\n    ngx_http_upstream_srv_conf_t *us);\nstatic int ngx_libc_cdecl\n    ngx_http_upstream_chash_cmp_points(const void *one, const void *two);\nstatic ngx_uint_t ngx_http_upstream_find_chash_point(\n    ngx_http_upstream_chash_points_t *points, uint32_t hash);\nstatic ngx_int_t ngx_http_upstream_init_chash_peer(ngx_http_request_t *r,\n    ngx_http_upstream_srv_conf_t *us);\nstatic ngx_int_t ngx_http_upstream_get_chash_peer(ngx_peer_connection_t *pc,\n    void *data);\n\nstatic void *ngx_http_upstream_hash_create_conf(ngx_conf_t *cf);\nstatic char *ngx_http_upstream_hash(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n\n\nstatic ngx_command_t  ngx_http_upstream_hash_commands[] = {\n\n    { ngx_string(\"hash\"),\n      NGX_HTTP_UPS_CONF|NGX_CONF_TAKE12,\n      ngx_http_upstream_hash,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      0,\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_upstream_hash_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    NULL,                                  /* postconfiguration */\n\n    NULL,                                  /* create main configuration */\n    NULL,                                  /* init main configuration */\n\n    ngx_http_upstream_hash_create_conf,    /* 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_upstream_hash_module = {\n    NGX_MODULE_V1,\n    &ngx_http_upstream_hash_module_ctx,    /* module context */\n    ngx_http_upstream_hash_commands,       /* 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_upstream_init_hash(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us)\n{\n    if (ngx_http_upstream_init_round_robin(cf, us) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    us->peer.init = ngx_http_upstream_init_hash_peer;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_init_hash_peer(ngx_http_request_t *r,\n    ngx_http_upstream_srv_conf_t *us)\n{\n    ngx_http_upstream_hash_srv_conf_t   *hcf;\n    ngx_http_upstream_hash_peer_data_t  *hp;\n\n    hp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_hash_peer_data_t));\n    if (hp == NULL) {\n        return NGX_ERROR;\n    }\n\n    r->upstream->peer.data = &hp->rrp;\n\n    if (ngx_http_upstream_init_round_robin_peer(r, us) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    r->upstream->peer.get = ngx_http_upstream_get_hash_peer;\n\n    hcf = ngx_http_conf_upstream_srv_conf(us, ngx_http_upstream_hash_module);\n\n    if (ngx_http_complex_value(r, &hcf->key, &hp->key) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"upstream hash key:\\\"%V\\\"\", &hp->key);\n\n    hp->conf = hcf;\n    hp->tries = 0;\n    hp->rehash = 0;\n    hp->hash = 0;\n    hp->get_rr_peer = ngx_http_upstream_get_round_robin_peer;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_get_hash_peer(ngx_peer_connection_t *pc, void *data)\n{\n    ngx_http_upstream_hash_peer_data_t  *hp = data;\n\n    time_t                        now;\n    u_char                        buf[NGX_INT_T_LEN];\n    size_t                        size;\n    uint32_t                      hash;\n    ngx_int_t                     w;\n    uintptr_t                     m;\n    ngx_uint_t                    n, p;\n    ngx_http_upstream_rr_peer_t  *peer;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                   \"get hash peer, try: %ui\", pc->tries);\n\n    ngx_http_upstream_rr_peers_rlock(hp->rrp.peers);\n\n    if (hp->tries > 20 || hp->rrp.peers->single || hp->key.len == 0) {\n        ngx_http_upstream_rr_peers_unlock(hp->rrp.peers);\n        return hp->get_rr_peer(pc, &hp->rrp);\n    }\n\n    now = ngx_time();\n\n    pc->cached = 0;\n    pc->connection = NULL;\n\n    for ( ;; ) {\n\n        /*\n         * Hash expression is compatible with Cache::Memcached:\n         * ((crc32([REHASH] KEY) >> 16) & 0x7fff) + PREV_HASH\n         * with REHASH omitted at the first iteration.\n         */\n\n        ngx_crc32_init(hash);\n\n        if (hp->rehash > 0) {\n            size = ngx_sprintf(buf, \"%ui\", hp->rehash) - buf;\n            ngx_crc32_update(&hash, buf, size);\n        }\n\n        ngx_crc32_update(&hash, hp->key.data, hp->key.len);\n        ngx_crc32_final(hash);\n\n        hash = (hash >> 16) & 0x7fff;\n\n        hp->hash += hash;\n        hp->rehash++;\n\n        w = hp->hash % hp->rrp.peers->total_weight;\n        peer = hp->rrp.peers->peer;\n        p = 0;\n\n        while (w >= peer->weight) {\n            w -= peer->weight;\n            peer = peer->next;\n            p++;\n        }\n\n        n = p / (8 * sizeof(uintptr_t));\n        m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t));\n\n        if (hp->rrp.tried[n] & m) {\n            goto next;\n        }\n\n        ngx_http_upstream_rr_peer_lock(hp->rrp.peers, peer);\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                       \"get hash peer, value:%uD, peer:%ui\", hp->hash, p);\n\n        if (peer->down) {\n            ngx_http_upstream_rr_peer_unlock(hp->rrp.peers, peer);\n            goto next;\n        }\n\n#if (NGX_HTTP_UPSTREAM_CHECK)\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n            \"get hash peer, check_index: %ui\",\n             peer->check_index);\n        if (ngx_http_upstream_check_peer_down(peer->check_index)) {\n            ngx_http_upstream_rr_peer_unlock(hp->rrp.peers, peer);\n            goto next;\n        }\n#endif\n\n        if (peer->max_fails\n            && peer->fails >= peer->max_fails\n            && now - peer->checked <= peer->fail_timeout)\n        {\n            ngx_http_upstream_rr_peer_unlock(hp->rrp.peers, peer);\n            goto next;\n        }\n\n        if (peer->max_conns && peer->conns >= peer->max_conns) {\n            ngx_http_upstream_rr_peer_unlock(hp->rrp.peers, peer);\n            goto next;\n        }\n\n        break;\n\n    next:\n\n        if (++hp->tries > 20) {\n            ngx_http_upstream_rr_peers_unlock(hp->rrp.peers);\n            return hp->get_rr_peer(pc, &hp->rrp);\n        }\n    }\n\n    hp->rrp.current = peer;\n\n    pc->sockaddr = peer->sockaddr;\n    pc->socklen = peer->socklen;\n    pc->name = &peer->name;\n\n    peer->conns++;\n\n    if (now - peer->checked > peer->fail_timeout) {\n        peer->checked = now;\n    }\n\n    ngx_http_upstream_rr_peer_unlock(hp->rrp.peers, peer);\n    ngx_http_upstream_rr_peers_unlock(hp->rrp.peers);\n\n    hp->rrp.tried[n] |= m;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_init_chash(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us)\n{\n    u_char                             *host, *port, c;\n    size_t                              host_len, port_len, size;\n    uint32_t                            hash, base_hash;\n    ngx_str_t                          *server;\n    ngx_uint_t                          npoints, i, j;\n    ngx_http_upstream_rr_peer_t        *peer;\n    ngx_http_upstream_rr_peers_t       *peers;\n    ngx_http_upstream_chash_points_t   *points;\n    ngx_http_upstream_hash_srv_conf_t  *hcf;\n    union {\n        uint32_t                        value;\n        u_char                          byte[4];\n    } prev_hash;\n\n    if (ngx_http_upstream_init_round_robin(cf, us) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    us->peer.init = ngx_http_upstream_init_chash_peer;\n\n    peers = us->peer.data;\n    npoints = peers->total_weight * 160;\n\n    size = sizeof(ngx_http_upstream_chash_points_t)\n           + sizeof(ngx_http_upstream_chash_point_t) * (npoints - 1);\n\n    points = ngx_palloc(cf->pool, size);\n    if (points == NULL) {\n        return NGX_ERROR;\n    }\n\n    points->number = 0;\n\n    for (peer = peers->peer; peer; peer = peer->next) {\n        server = &peer->server;\n\n        /*\n         * Hash expression is compatible with Cache::Memcached::Fast:\n         * crc32(HOST \\0 PORT PREV_HASH).\n         */\n\n        if (server->len >= 5\n            && ngx_strncasecmp(server->data, (u_char *) \"unix:\", 5) == 0)\n        {\n            host = server->data + 5;\n            host_len = server->len - 5;\n            port = NULL;\n            port_len = 0;\n            goto done;\n        }\n\n        for (j = 0; j < server->len; j++) {\n            c = server->data[server->len - j - 1];\n\n            if (c == ':') {\n                host = server->data;\n                host_len = server->len - j - 1;\n                port = server->data + server->len - j;\n                port_len = j;\n                goto done;\n            }\n\n            if (c < '0' || c > '9') {\n                break;\n            }\n        }\n\n        host = server->data;\n        host_len = server->len;\n        port = NULL;\n        port_len = 0;\n\n    done:\n\n        ngx_crc32_init(base_hash);\n        ngx_crc32_update(&base_hash, host, host_len);\n        ngx_crc32_update(&base_hash, (u_char *) \"\", 1);\n        ngx_crc32_update(&base_hash, port, port_len);\n\n        prev_hash.value = 0;\n        npoints = peer->weight * 160;\n\n        for (j = 0; j < npoints; j++) {\n            hash = base_hash;\n\n            ngx_crc32_update(&hash, prev_hash.byte, 4);\n            ngx_crc32_final(hash);\n\n            points->point[points->number].hash = hash;\n            points->point[points->number].server = server;\n            points->number++;\n\n#if (NGX_HAVE_LITTLE_ENDIAN)\n            prev_hash.value = hash;\n#else\n            prev_hash.byte[0] = (u_char) (hash & 0xff);\n            prev_hash.byte[1] = (u_char) ((hash >> 8) & 0xff);\n            prev_hash.byte[2] = (u_char) ((hash >> 16) & 0xff);\n            prev_hash.byte[3] = (u_char) ((hash >> 24) & 0xff);\n#endif\n        }\n    }\n\n    ngx_qsort(points->point,\n              points->number,\n              sizeof(ngx_http_upstream_chash_point_t),\n              ngx_http_upstream_chash_cmp_points);\n\n    for (i = 0, j = 1; j < points->number; j++) {\n        if (points->point[i].hash != points->point[j].hash) {\n            points->point[++i] = points->point[j];\n        }\n    }\n\n    points->number = i + 1;\n\n    hcf = ngx_http_conf_upstream_srv_conf(us, ngx_http_upstream_hash_module);\n    hcf->points = points;\n\n    return NGX_OK;\n}\n\n\nstatic int ngx_libc_cdecl\nngx_http_upstream_chash_cmp_points(const void *one, const void *two)\n{\n    ngx_http_upstream_chash_point_t *first =\n                                       (ngx_http_upstream_chash_point_t *) one;\n    ngx_http_upstream_chash_point_t *second =\n                                       (ngx_http_upstream_chash_point_t *) two;\n\n    if (first->hash < second->hash) {\n        return -1;\n\n    } else if (first->hash > second->hash) {\n        return 1;\n\n    } else {\n        return 0;\n    }\n}\n\n\nstatic ngx_uint_t\nngx_http_upstream_find_chash_point(ngx_http_upstream_chash_points_t *points,\n    uint32_t hash)\n{\n    ngx_uint_t                        i, j, k;\n    ngx_http_upstream_chash_point_t  *point;\n\n    /* find first point >= hash */\n\n    point = &points->point[0];\n\n    i = 0;\n    j = points->number;\n\n    while (i < j) {\n        k = (i + j) / 2;\n\n        if (hash > point[k].hash) {\n            i = k + 1;\n\n        } else if (hash < point[k].hash) {\n            j = k;\n\n        } else {\n            return k;\n        }\n    }\n\n    return i;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_init_chash_peer(ngx_http_request_t *r,\n    ngx_http_upstream_srv_conf_t *us)\n{\n    uint32_t                             hash;\n    ngx_http_upstream_hash_srv_conf_t   *hcf;\n    ngx_http_upstream_hash_peer_data_t  *hp;\n\n    if (ngx_http_upstream_init_hash_peer(r, us) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    r->upstream->peer.get = ngx_http_upstream_get_chash_peer;\n\n    hp = r->upstream->peer.data;\n    hcf = ngx_http_conf_upstream_srv_conf(us, ngx_http_upstream_hash_module);\n\n    hash = ngx_crc32_long(hp->key.data, hp->key.len);\n\n    ngx_http_upstream_rr_peers_rlock(hp->rrp.peers);\n\n    hp->hash = ngx_http_upstream_find_chash_point(hcf->points, hash);\n\n    ngx_http_upstream_rr_peers_unlock(hp->rrp.peers);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_get_chash_peer(ngx_peer_connection_t *pc, void *data)\n{\n    ngx_http_upstream_hash_peer_data_t  *hp = data;\n\n    time_t                              now;\n    intptr_t                            m;\n    ngx_str_t                          *server;\n    ngx_int_t                           total;\n    ngx_uint_t                          i, n, best_i;\n    ngx_http_upstream_rr_peer_t        *peer, *best;\n    ngx_http_upstream_chash_point_t    *point;\n    ngx_http_upstream_chash_points_t   *points;\n    ngx_http_upstream_hash_srv_conf_t  *hcf;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                   \"get consistent hash peer, try: %ui\", pc->tries);\n\n    ngx_http_upstream_rr_peers_wlock(hp->rrp.peers);\n\n    if (hp->tries > 20 || hp->rrp.peers->single || hp->key.len == 0) {\n        ngx_http_upstream_rr_peers_unlock(hp->rrp.peers);\n        return hp->get_rr_peer(pc, &hp->rrp);\n    }\n\n    pc->cached = 0;\n    pc->connection = NULL;\n\n    now = ngx_time();\n    hcf = hp->conf;\n\n    points = hcf->points;\n    point = &points->point[0];\n\n    for ( ;; ) {\n        server = point[hp->hash % points->number].server;\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                       \"consistent hash peer:%uD, server:\\\"%V\\\"\",\n                       hp->hash, server);\n\n        best = NULL;\n        best_i = 0;\n        total = 0;\n\n        for (peer = hp->rrp.peers->peer, i = 0;\n             peer;\n             peer = peer->next, i++)\n        {\n            n = i / (8 * sizeof(uintptr_t));\n            m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));\n\n            if (hp->rrp.tried[n] & m) {\n                continue;\n            }\n\n            if (peer->down) {\n                continue;\n            }\n\n            if (peer->max_fails\n                && peer->fails >= peer->max_fails\n                && now - peer->checked <= peer->fail_timeout)\n            {\n                continue;\n            }\n\n            if (peer->max_conns && peer->conns >= peer->max_conns) {\n                continue;\n            }\n\n            if (peer->server.len != server->len\n                || ngx_strncmp(peer->server.data, server->data, server->len)\n                   != 0)\n            {\n                continue;\n            }\n\n            peer->current_weight += peer->effective_weight;\n            total += peer->effective_weight;\n\n            if (peer->effective_weight < peer->weight) {\n                peer->effective_weight++;\n            }\n\n            if (best == NULL || peer->current_weight > best->current_weight) {\n                best = peer;\n                best_i = i;\n            }\n        }\n\n        if (best) {\n            best->current_weight -= total;\n            goto found;\n        }\n\n        hp->hash++;\n        hp->tries++;\n\n        if (hp->tries > 20) {\n            ngx_http_upstream_rr_peers_unlock(hp->rrp.peers);\n            return hp->get_rr_peer(pc, &hp->rrp);\n        }\n    }\n\nfound:\n\n    hp->rrp.current = best;\n\n    pc->sockaddr = best->sockaddr;\n    pc->socklen = best->socklen;\n    pc->name = &best->name;\n\n    best->conns++;\n\n    if (now - best->checked > best->fail_timeout) {\n        best->checked = now;\n    }\n\n    ngx_http_upstream_rr_peers_unlock(hp->rrp.peers);\n\n    n = best_i / (8 * sizeof(uintptr_t));\n    m = (uintptr_t) 1 << best_i % (8 * sizeof(uintptr_t));\n\n    hp->rrp.tried[n] |= m;\n\n    return NGX_OK;\n}\n\n\nstatic void *\nngx_http_upstream_hash_create_conf(ngx_conf_t *cf)\n{\n    ngx_http_upstream_hash_srv_conf_t  *conf;\n\n    conf = ngx_palloc(cf->pool, sizeof(ngx_http_upstream_hash_srv_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    conf->points = NULL;\n\n    return conf;\n}\n\n\nstatic char *\nngx_http_upstream_hash(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_upstream_hash_srv_conf_t  *hcf = conf;\n\n    ngx_str_t                         *value;\n    ngx_http_upstream_srv_conf_t      *uscf;\n    ngx_http_compile_complex_value_t   ccv;\n\n    value = cf->args->elts;\n\n    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n\n    ccv.cf = cf;\n    ccv.value = &value[1];\n    ccv.complex_value = &hcf->key;\n\n    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);\n\n    if (uscf->peer.init_upstream) {\n        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                           \"load balancing method redefined\");\n    }\n\n    uscf->flags = NGX_HTTP_UPSTREAM_CREATE\n                  |NGX_HTTP_UPSTREAM_WEIGHT\n                  |NGX_HTTP_UPSTREAM_MAX_CONNS\n                  |NGX_HTTP_UPSTREAM_MAX_FAILS\n                  |NGX_HTTP_UPSTREAM_FAIL_TIMEOUT\n                  |NGX_HTTP_UPSTREAM_DOWN;\n\n    if (cf->args->nelts == 2) {\n        uscf->peer.init_upstream = ngx_http_upstream_init_hash;\n\n    } else if (ngx_strcmp(value[2].data, \"consistent\") == 0) {\n        uscf->peer.init_upstream = ngx_http_upstream_init_chash;\n\n    } else {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid parameter \\\"%V\\\"\", &value[2]);\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n"
  },
  {
    "path": "src/http/modules/ngx_http_upstream_ip_hash_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n#if (NGX_HTTP_UPSTREAM_CHECK)\n#include \"ngx_http_upstream_check_module.h\"\n#endif\n\ntypedef struct {\n    /* the round robin data must be first */\n    ngx_http_upstream_rr_peer_data_t   rrp;\n\n    ngx_uint_t                         hash;\n\n    u_char                             addrlen;\n    u_char                            *addr;\n\n    u_char                             tries;\n\n    ngx_event_get_peer_pt              get_rr_peer;\n} ngx_http_upstream_ip_hash_peer_data_t;\n\n\nstatic ngx_int_t ngx_http_upstream_init_ip_hash_peer(ngx_http_request_t *r,\n    ngx_http_upstream_srv_conf_t *us);\nstatic ngx_int_t ngx_http_upstream_get_ip_hash_peer(ngx_peer_connection_t *pc,\n    void *data);\nstatic char *ngx_http_upstream_ip_hash(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n\n\nstatic ngx_command_t  ngx_http_upstream_ip_hash_commands[] = {\n\n    { ngx_string(\"ip_hash\"),\n      NGX_HTTP_UPS_CONF|NGX_CONF_NOARGS,\n      ngx_http_upstream_ip_hash,\n      0,\n      0,\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_upstream_ip_hash_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    NULL,                                  /* 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\nngx_module_t  ngx_http_upstream_ip_hash_module = {\n    NGX_MODULE_V1,\n    &ngx_http_upstream_ip_hash_module_ctx, /* module context */\n    ngx_http_upstream_ip_hash_commands,    /* 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 u_char ngx_http_upstream_ip_hash_pseudo_addr[3];\n\n\nstatic ngx_int_t\nngx_http_upstream_init_ip_hash(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us)\n{\n    if (ngx_http_upstream_init_round_robin(cf, us) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    us->peer.init = ngx_http_upstream_init_ip_hash_peer;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_init_ip_hash_peer(ngx_http_request_t *r,\n    ngx_http_upstream_srv_conf_t *us)\n{\n    struct sockaddr_in                     *sin;\n#if (NGX_HAVE_INET6)\n    struct sockaddr_in6                    *sin6;\n#endif\n    ngx_http_upstream_ip_hash_peer_data_t  *iphp;\n\n    iphp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_ip_hash_peer_data_t));\n    if (iphp == NULL) {\n        return NGX_ERROR;\n    }\n\n    r->upstream->peer.data = &iphp->rrp;\n\n    if (ngx_http_upstream_init_round_robin_peer(r, us) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    r->upstream->peer.get = ngx_http_upstream_get_ip_hash_peer;\n\n    switch (r->connection->sockaddr->sa_family) {\n\n    case AF_INET:\n        sin = (struct sockaddr_in *) r->connection->sockaddr;\n        iphp->addr = (u_char *) &sin->sin_addr.s_addr;\n        iphp->addrlen = 3;\n        break;\n\n#if (NGX_HAVE_INET6)\n    case AF_INET6:\n        sin6 = (struct sockaddr_in6 *) r->connection->sockaddr;\n        iphp->addr = (u_char *) &sin6->sin6_addr.s6_addr;\n        iphp->addrlen = 16;\n        break;\n#endif\n\n    default:\n        iphp->addr = ngx_http_upstream_ip_hash_pseudo_addr;\n        iphp->addrlen = 3;\n    }\n\n    iphp->hash = 89;\n    iphp->tries = 0;\n    iphp->get_rr_peer = ngx_http_upstream_get_round_robin_peer;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_get_ip_hash_peer(ngx_peer_connection_t *pc, void *data)\n{\n    ngx_http_upstream_ip_hash_peer_data_t  *iphp = data;\n\n    time_t                        now;\n    ngx_int_t                     w;\n    uintptr_t                     m;\n    ngx_uint_t                    i, n, p, hash;\n    ngx_http_upstream_rr_peer_t  *peer;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                   \"get ip hash peer, try: %ui\", pc->tries);\n\n    /* TODO: cached */\n\n    ngx_http_upstream_rr_peers_rlock(iphp->rrp.peers);\n\n    if (iphp->tries > 20 || iphp->rrp.peers->single) {\n        ngx_http_upstream_rr_peers_unlock(iphp->rrp.peers);\n        return iphp->get_rr_peer(pc, &iphp->rrp);\n    }\n\n    now = ngx_time();\n\n    pc->cached = 0;\n    pc->connection = NULL;\n\n    hash = iphp->hash;\n\n    for ( ;; ) {\n\n        for (i = 0; i < (ngx_uint_t) iphp->addrlen; i++) {\n            hash = (hash * 113 + iphp->addr[i]) % 6271;\n        }\n\n        w = hash % iphp->rrp.peers->total_weight;\n        peer = iphp->rrp.peers->peer;\n        p = 0;\n\n        while (w >= peer->weight) {\n            w -= peer->weight;\n            peer = peer->next;\n            p++;\n        }\n\n        n = p / (8 * sizeof(uintptr_t));\n        m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t));\n\n        if (iphp->rrp.tried[n] & m) {\n            goto next;\n        }\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                       \"get ip hash peer, hash: %ui %04XL\", p, (uint64_t) m);\n\n        ngx_http_upstream_rr_peer_lock(iphp->rrp.peers, peer);\n\n        if (peer->down) {\n            ngx_http_upstream_rr_peer_unlock(iphp->rrp.peers, peer);\n            goto next;\n        }\n\n#if (NGX_HTTP_UPSTREAM_CHECK)\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                       \"get ip_hash peer, check_index: %ui\",\n                       peer->check_index);\n\n        if (ngx_http_upstream_check_peer_down(peer->check_index)) {\n            ngx_http_upstream_rr_peer_unlock(iphp->rrp.peers, peer);\n            goto next;\n        }\n#endif\n\n        if (peer->max_fails\n            && peer->fails >= peer->max_fails\n            && now - peer->checked <= peer->fail_timeout)\n        {\n            ngx_http_upstream_rr_peer_unlock(iphp->rrp.peers, peer);\n            goto next;\n        }\n\n        if (peer->max_conns && peer->conns >= peer->max_conns) {\n            ngx_http_upstream_rr_peer_unlock(iphp->rrp.peers, peer);\n            goto next;\n        }\n\n        break;\n\n    next:\n\n        if (++iphp->tries > 20) {\n            ngx_http_upstream_rr_peers_unlock(iphp->rrp.peers);\n            return iphp->get_rr_peer(pc, &iphp->rrp);\n        }\n    }\n\n    iphp->rrp.current = peer;\n\n    pc->sockaddr = peer->sockaddr;\n    pc->socklen = peer->socklen;\n    pc->name = &peer->name;\n#if (T_NGX_HTTP_DYNAMIC_RESOLVE) \n    pc->host = &peer->host;\n#endif\n    peer->conns++;\n\n    if (now - peer->checked > peer->fail_timeout) {\n        peer->checked = now;\n    }\n\n    ngx_http_upstream_rr_peer_unlock(iphp->rrp.peers, peer);\n    ngx_http_upstream_rr_peers_unlock(iphp->rrp.peers);\n\n    iphp->rrp.tried[n] |= m;\n    iphp->hash = hash;\n\n    return NGX_OK;\n}\n\n\nstatic char *\nngx_http_upstream_ip_hash(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_upstream_srv_conf_t  *uscf;\n\n    uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);\n\n    if (uscf->peer.init_upstream) {\n        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                           \"load balancing method redefined\");\n    }\n\n    uscf->peer.init_upstream = ngx_http_upstream_init_ip_hash;\n\n    uscf->flags = NGX_HTTP_UPSTREAM_CREATE\n                  |NGX_HTTP_UPSTREAM_WEIGHT\n                  |NGX_HTTP_UPSTREAM_MAX_CONNS\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"
  },
  {
    "path": "src/http/modules/ngx_http_upstream_keepalive_module.c",
    "content": "\n/*\n * Copyright (C) Maxim Dounin\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\ntypedef struct {\n    ngx_uint_t                         max_cached;\n    ngx_uint_t                         requests;\n    ngx_msec_t                         time;\n    ngx_msec_t                         timeout;\n\n    ngx_queue_t                        cache;\n    ngx_queue_t                        free;\n\n    ngx_http_upstream_init_pt          original_init_upstream;\n    ngx_http_upstream_init_peer_pt     original_init_peer;\n\n} ngx_http_upstream_keepalive_srv_conf_t;\n\n\ntypedef struct {\n    ngx_http_upstream_keepalive_srv_conf_t  *conf;\n\n    ngx_queue_t                        queue;\n    ngx_connection_t                  *connection;\n\n    socklen_t                          socklen;\n    ngx_sockaddr_t                     sockaddr;\n\n} ngx_http_upstream_keepalive_cache_t;\n\n\ntypedef struct {\n    ngx_http_upstream_keepalive_srv_conf_t  *conf;\n\n    ngx_http_upstream_t               *upstream;\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_upstream_keepalive_peer_data_t;\n\n\nstatic ngx_int_t ngx_http_upstream_init_keepalive_peer(ngx_http_request_t *r,\n    ngx_http_upstream_srv_conf_t *us);\nstatic ngx_int_t ngx_http_upstream_get_keepalive_peer(ngx_peer_connection_t *pc,\n    void *data);\nstatic void ngx_http_upstream_free_keepalive_peer(ngx_peer_connection_t *pc,\n    void *data, ngx_uint_t state);\n\nstatic void ngx_http_upstream_keepalive_dummy_handler(ngx_event_t *ev);\nstatic void ngx_http_upstream_keepalive_close_handler(ngx_event_t *ev);\nstatic void ngx_http_upstream_keepalive_close(ngx_connection_t *c);\n\n#if (NGX_HTTP_SSL)\nstatic ngx_int_t ngx_http_upstream_keepalive_set_session(\n    ngx_peer_connection_t *pc, void *data);\nstatic void ngx_http_upstream_keepalive_save_session(ngx_peer_connection_t *pc,\n    void *data);\n#endif\n\nstatic void *ngx_http_upstream_keepalive_create_conf(ngx_conf_t *cf);\nstatic char *ngx_http_upstream_keepalive(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n\n\nstatic ngx_command_t  ngx_http_upstream_keepalive_commands[] = {\n\n    { ngx_string(\"keepalive\"),\n      NGX_HTTP_UPS_CONF|NGX_CONF_TAKE1,\n      ngx_http_upstream_keepalive,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"keepalive_time\"),\n      NGX_HTTP_UPS_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      offsetof(ngx_http_upstream_keepalive_srv_conf_t, time),\n      NULL },\n\n    { ngx_string(\"keepalive_timeout\"),\n      NGX_HTTP_UPS_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      offsetof(ngx_http_upstream_keepalive_srv_conf_t, timeout),\n      NULL },\n\n    { ngx_string(\"keepalive_requests\"),\n      NGX_HTTP_UPS_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_num_slot,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      offsetof(ngx_http_upstream_keepalive_srv_conf_t, requests),\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_upstream_keepalive_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    NULL,                                  /* postconfiguration */\n\n    NULL,                                  /* create main configuration */\n    NULL,                                  /* init main configuration */\n\n    ngx_http_upstream_keepalive_create_conf, /* 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_upstream_keepalive_module = {\n    NGX_MODULE_V1,\n    &ngx_http_upstream_keepalive_module_ctx, /* module context */\n    ngx_http_upstream_keepalive_commands,    /* 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_upstream_init_keepalive(ngx_conf_t *cf,\n    ngx_http_upstream_srv_conf_t *us)\n{\n    ngx_uint_t                               i;\n    ngx_http_upstream_keepalive_srv_conf_t  *kcf;\n    ngx_http_upstream_keepalive_cache_t     *cached;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cf->log, 0,\n                   \"init keepalive\");\n\n    kcf = ngx_http_conf_upstream_srv_conf(us,\n                                          ngx_http_upstream_keepalive_module);\n\n    ngx_conf_init_msec_value(kcf->time, 3600000);\n    ngx_conf_init_msec_value(kcf->timeout, 60000);\n    ngx_conf_init_uint_value(kcf->requests, 1000);\n\n    if (kcf->original_init_upstream(cf, us) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    kcf->original_init_peer = us->peer.init;\n\n    us->peer.init = ngx_http_upstream_init_keepalive_peer;\n\n    /* allocate cache items and add to free queue */\n\n    cached = ngx_pcalloc(cf->pool,\n                sizeof(ngx_http_upstream_keepalive_cache_t) * kcf->max_cached);\n    if (cached == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_queue_init(&kcf->cache);\n    ngx_queue_init(&kcf->free);\n\n    for (i = 0; i < kcf->max_cached; i++) {\n        ngx_queue_insert_head(&kcf->free, &cached[i].queue);\n        cached[i].conf = kcf;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_init_keepalive_peer(ngx_http_request_t *r,\n    ngx_http_upstream_srv_conf_t *us)\n{\n    ngx_http_upstream_keepalive_peer_data_t  *kp;\n    ngx_http_upstream_keepalive_srv_conf_t   *kcf;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"init keepalive peer\");\n\n    kcf = ngx_http_conf_upstream_srv_conf(us,\n                                          ngx_http_upstream_keepalive_module);\n\n    kp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_keepalive_peer_data_t));\n    if (kp == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (kcf->original_init_peer(r, us) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    kp->conf = kcf;\n    kp->upstream = r->upstream;\n    kp->data = r->upstream->peer.data;\n    kp->original_get_peer = r->upstream->peer.get;\n    kp->original_free_peer = r->upstream->peer.free;\n\n    r->upstream->peer.data = kp;\n    r->upstream->peer.get = ngx_http_upstream_get_keepalive_peer;\n    r->upstream->peer.free = ngx_http_upstream_free_keepalive_peer;\n\n#if (NGX_HTTP_SSL)\n    kp->original_set_session = r->upstream->peer.set_session;\n    kp->original_save_session = r->upstream->peer.save_session;\n    r->upstream->peer.set_session = ngx_http_upstream_keepalive_set_session;\n    r->upstream->peer.save_session = ngx_http_upstream_keepalive_save_session;\n#endif\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_get_keepalive_peer(ngx_peer_connection_t *pc, void *data)\n{\n    ngx_http_upstream_keepalive_peer_data_t  *kp = data;\n    ngx_http_upstream_keepalive_cache_t      *item;\n\n    ngx_int_t          rc;\n    ngx_queue_t       *q, *cache;\n    ngx_connection_t  *c;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                   \"get keepalive peer\");\n\n    /* ask balancer */\n\n    rc = kp->original_get_peer(pc, kp->data);\n\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    /* search cache for suitable connection */\n\n    cache = &kp->conf->cache;\n\n    for (q = ngx_queue_head(cache);\n         q != ngx_queue_sentinel(cache);\n         q = ngx_queue_next(q))\n    {\n        item = ngx_queue_data(q, ngx_http_upstream_keepalive_cache_t, queue);\n        c = item->connection;\n\n        if (ngx_memn2cmp((u_char *) &item->sockaddr, (u_char *) pc->sockaddr,\n                         item->socklen, pc->socklen)\n            == 0)\n        {\n            ngx_queue_remove(q);\n            ngx_queue_insert_head(&kp->conf->free, q);\n\n            goto found;\n        }\n    }\n\n    return NGX_OK;\n\nfound:\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                   \"get keepalive peer: using connection %p\", c);\n\n    c->idle = 0;\n    c->sent = 0;\n    c->data = NULL;\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->connection = c;\n    pc->cached = 1;\n\n    return NGX_DONE;\n}\n\n\nstatic void\nngx_http_upstream_free_keepalive_peer(ngx_peer_connection_t *pc, void *data,\n    ngx_uint_t state)\n{\n    ngx_http_upstream_keepalive_peer_data_t  *kp = data;\n    ngx_http_upstream_keepalive_cache_t      *item;\n\n    ngx_queue_t          *q;\n    ngx_connection_t     *c;\n    ngx_http_upstream_t  *u;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                   \"free keepalive peer\");\n\n    /* cache valid connections */\n\n    u = kp->upstream;\n    c = pc->connection;\n\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 (c->requests >= kp->conf->requests) {\n        goto invalid;\n    }\n\n    if (ngx_current_msec - c->start_time > kp->conf->time) {\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    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                   \"free keepalive peer: saving connection %p\", c);\n\n    if (ngx_queue_empty(&kp->conf->free)) {\n\n        q = ngx_queue_last(&kp->conf->cache);\n        ngx_queue_remove(q);\n\n        item = ngx_queue_data(q, ngx_http_upstream_keepalive_cache_t, queue);\n\n        ngx_http_upstream_keepalive_close(item->connection);\n\n    } else {\n        q = ngx_queue_head(&kp->conf->free);\n        ngx_queue_remove(q);\n\n        item = ngx_queue_data(q, ngx_http_upstream_keepalive_cache_t, queue);\n    }\n\n    ngx_queue_insert_head(&kp->conf->cache, q);\n\n    item->connection = c;\n\n    pc->connection = NULL;\n\n    c->read->delayed = 0;\n    ngx_add_timer(c->read, kp->conf->timeout);\n\n    if (c->write->timer_set) {\n        ngx_del_timer(c->write);\n    }\n\n    c->write->handler = ngx_http_upstream_keepalive_dummy_handler;\n    c->read->handler = ngx_http_upstream_keepalive_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\n    if (c->read->ready) {\n        ngx_http_upstream_keepalive_close_handler(c->read);\n    }\n\ninvalid:\n\n    kp->original_free_peer(pc, kp->data, state);\n}\n\n\nstatic void\nngx_http_upstream_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_upstream_keepalive_close_handler(ngx_event_t *ev)\n{\n    ngx_http_upstream_keepalive_srv_conf_t  *conf;\n    ngx_http_upstream_keepalive_cache_t     *item;\n\n    int                n;\n    char               buf[1];\n    ngx_connection_t  *c;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0,\n                   \"keepalive close handler\");\n\n    c = ev->data;\n\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    conf = item->conf;\n\n    ngx_http_upstream_keepalive_close(c);\n\n    ngx_queue_remove(&item->queue);\n    ngx_queue_insert_head(&conf->free, &item->queue);\n}\n\n\nstatic void\nngx_http_upstream_keepalive_close(ngx_connection_t *c)\n{\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        if (ngx_ssl_shutdown(c) == NGX_AGAIN) {\n            c->ssl->handler = ngx_http_upstream_keepalive_close;\n            return;\n        }\n    }\n\n#endif\n\n    ngx_destroy_pool(c->pool);\n    ngx_close_connection(c);\n}\n\n\n#if (NGX_HTTP_SSL)\n\nstatic ngx_int_t\nngx_http_upstream_keepalive_set_session(ngx_peer_connection_t *pc, void *data)\n{\n    ngx_http_upstream_keepalive_peer_data_t  *kp = data;\n\n    return kp->original_set_session(pc, kp->data);\n}\n\n\nstatic void\nngx_http_upstream_keepalive_save_session(ngx_peer_connection_t *pc, void *data)\n{\n    ngx_http_upstream_keepalive_peer_data_t  *kp = data;\n\n    kp->original_save_session(pc, kp->data);\n    return;\n}\n\n#endif\n\n\nstatic void *\nngx_http_upstream_keepalive_create_conf(ngx_conf_t *cf)\n{\n    ngx_http_upstream_keepalive_srv_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool,\n                       sizeof(ngx_http_upstream_keepalive_srv_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc():\n     *\n     *     conf->original_init_upstream = NULL;\n     *     conf->original_init_peer = NULL;\n     *     conf->max_cached = 0;\n     */\n\n    conf->time = NGX_CONF_UNSET_MSEC;\n    conf->timeout = NGX_CONF_UNSET_MSEC;\n    conf->requests = NGX_CONF_UNSET_UINT;\n\n    return conf;\n}\n\n\nstatic char *\nngx_http_upstream_keepalive(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_upstream_srv_conf_t            *uscf;\n    ngx_http_upstream_keepalive_srv_conf_t  *kcf = conf;\n\n    ngx_int_t    n;\n    ngx_str_t   *value;\n\n    if (kcf->max_cached) {\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    kcf->max_cached = n;\n\n    /* init upstream handler */\n\n    uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);\n\n    kcf->original_init_upstream = uscf->peer.init_upstream\n                                  ? uscf->peer.init_upstream\n                                  : ngx_http_upstream_init_round_robin;\n\n    uscf->peer.init_upstream = ngx_http_upstream_init_keepalive;\n\n    return NGX_CONF_OK;\n}\n"
  },
  {
    "path": "src/http/modules/ngx_http_upstream_least_conn_module.c",
    "content": "\n/*\n * Copyright (C) Maxim Dounin\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n#if (NGX_HTTP_UPSTREAM_CHECK)\n\n#include \"ngx_http_upstream_check_module.h\"\n\n#endif\n\nstatic ngx_int_t ngx_http_upstream_init_least_conn_peer(ngx_http_request_t *r,\n    ngx_http_upstream_srv_conf_t *us);\nstatic ngx_int_t ngx_http_upstream_get_least_conn_peer(\n    ngx_peer_connection_t *pc, void *data);\nstatic char *ngx_http_upstream_least_conn(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n\n\nstatic ngx_command_t  ngx_http_upstream_least_conn_commands[] = {\n\n    { ngx_string(\"least_conn\"),\n      NGX_HTTP_UPS_CONF|NGX_CONF_NOARGS,\n      ngx_http_upstream_least_conn,\n      0,\n      0,\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_upstream_least_conn_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    NULL,                                  /* 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\nngx_module_t  ngx_http_upstream_least_conn_module = {\n    NGX_MODULE_V1,\n    &ngx_http_upstream_least_conn_module_ctx, /* module context */\n    ngx_http_upstream_least_conn_commands, /* 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_upstream_init_least_conn(ngx_conf_t *cf,\n    ngx_http_upstream_srv_conf_t *us)\n{\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cf->log, 0,\n                   \"init least conn\");\n\n    if (ngx_http_upstream_init_round_robin(cf, us) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    us->peer.init = ngx_http_upstream_init_least_conn_peer;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_init_least_conn_peer(ngx_http_request_t *r,\n    ngx_http_upstream_srv_conf_t *us)\n{\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"init least conn peer\");\n\n    if (ngx_http_upstream_init_round_robin_peer(r, us) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    r->upstream->peer.get = ngx_http_upstream_get_least_conn_peer;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_get_least_conn_peer(ngx_peer_connection_t *pc, void *data)\n{\n    ngx_http_upstream_rr_peer_data_t  *rrp = data;\n\n    time_t                         now;\n    uintptr_t                      m;\n    ngx_int_t                      rc, total;\n    ngx_uint_t                     i, n, p, many;\n    ngx_http_upstream_rr_peer_t   *peer, *best;\n    ngx_http_upstream_rr_peers_t  *peers;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                   \"get least conn peer, try: %ui\", pc->tries);\n\n    if (rrp->peers->single) {\n        return ngx_http_upstream_get_round_robin_peer(pc, rrp);\n    }\n\n    pc->cached = 0;\n    pc->connection = NULL;\n\n    now = ngx_time();\n\n    peers = rrp->peers;\n\n    ngx_http_upstream_rr_peers_wlock(peers);\n\n    best = NULL;\n    total = 0;\n\n#if (NGX_SUPPRESS_WARN)\n    many = 0;\n    p = 0;\n#endif\n\n    for (peer = peers->peer, i = 0;\n         peer;\n         peer = peer->next, i++)\n    {\n        n = i / (8 * sizeof(uintptr_t));\n        m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));\n\n        if (rrp->tried[n] & m) {\n            continue;\n        }\n\n        if (peer->down) {\n            continue;\n        }\n\n#if (NGX_HTTP_UPSTREAM_CHECK)\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                       \"get least_conn peer, check_index: %ui\",\n                       peer->check_index);\n\n        if (ngx_http_upstream_check_peer_down(peer->check_index)) {\n            continue;\n        }\n#endif\n\n        if (peer->max_fails\n            && peer->fails >= peer->max_fails\n            && now - peer->checked <= peer->fail_timeout)\n        {\n            continue;\n        }\n\n        if (peer->max_conns && peer->conns >= peer->max_conns) {\n            continue;\n        }\n\n        /*\n         * select peer with least number of connections; if there are\n         * multiple peers with the same number of connections, select\n         * based on round-robin\n         */\n\n        if (best == NULL\n            || peer->conns * best->weight < best->conns * peer->weight)\n        {\n            best = peer;\n            many = 0;\n            p = i;\n\n        } else if (peer->conns * best->weight == best->conns * peer->weight) {\n            many = 1;\n        }\n    }\n\n    if (best == NULL) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                       \"get least conn peer, no peer found\");\n\n        goto failed;\n    }\n\n    if (many) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                       \"get least conn peer, many\");\n\n        for (peer = best, i = p;\n             peer;\n             peer = peer->next, i++)\n        {\n            n = i / (8 * sizeof(uintptr_t));\n            m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));\n\n            if (rrp->tried[n] & m) {\n                continue;\n            }\n\n            if (peer->down) {\n                continue;\n            }\n\n#if (NGX_HTTP_UPSTREAM_CHECK)\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                           \"get least_conn peer, check_index: %ui\",\n                           peer->check_index);\n\n            if (ngx_http_upstream_check_peer_down(peer->check_index)) {\n                continue;\n            }\n#endif\n\n            if (peer->conns * best->weight != best->conns * peer->weight) {\n                continue;\n            }\n\n            if (peer->max_fails\n                && peer->fails >= peer->max_fails\n                && now - peer->checked <= peer->fail_timeout)\n            {\n                continue;\n            }\n\n            if (peer->max_conns && peer->conns >= peer->max_conns) {\n                continue;\n            }\n\n            peer->current_weight += peer->effective_weight;\n            total += peer->effective_weight;\n\n            if (peer->effective_weight < peer->weight) {\n                peer->effective_weight++;\n            }\n\n            if (peer->current_weight > best->current_weight) {\n                best = peer;\n                p = i;\n            }\n        }\n    }\n\n    best->current_weight -= total;\n\n    if (now - best->checked > best->fail_timeout) {\n        best->checked = now;\n    }\n\n    pc->sockaddr = best->sockaddr;\n    pc->socklen = best->socklen;\n    pc->name = &best->name;\n#if (T_NGX_HTTP_DYNAMIC_RESOLVE) \n    pc->host = &best->host;\n#endif\n\n    best->conns++;\n\n    rrp->current = best;\n\n    n = p / (8 * sizeof(uintptr_t));\n    m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t));\n\n    rrp->tried[n] |= m;\n\n    ngx_http_upstream_rr_peers_unlock(peers);\n\n    return NGX_OK;\n\nfailed:\n\n    if (peers->next) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                       \"get least conn peer, backup servers\");\n\n        rrp->peers = peers->next;\n\n        n = (rrp->peers->number + (8 * sizeof(uintptr_t) - 1))\n                / (8 * sizeof(uintptr_t));\n\n        for (i = 0; i < n; i++) {\n            rrp->tried[i] = 0;\n        }\n\n        ngx_http_upstream_rr_peers_unlock(peers);\n\n        rc = ngx_http_upstream_get_least_conn_peer(pc, rrp);\n\n        if (rc != NGX_BUSY) {\n            return rc;\n        }\n\n        ngx_http_upstream_rr_peers_wlock(peers);\n    }\n\n    ngx_http_upstream_rr_peers_unlock(peers);\n\n    pc->name = peers->name;\n\n    return NGX_BUSY;\n}\n\n\nstatic char *\nngx_http_upstream_least_conn(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_upstream_srv_conf_t  *uscf;\n\n    uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);\n\n    if (uscf->peer.init_upstream) {\n        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                           \"load balancing method redefined\");\n    }\n\n    uscf->peer.init_upstream = ngx_http_upstream_init_least_conn;\n\n    uscf->flags = NGX_HTTP_UPSTREAM_CREATE\n                  |NGX_HTTP_UPSTREAM_WEIGHT\n                  |NGX_HTTP_UPSTREAM_MAX_CONNS\n                  |NGX_HTTP_UPSTREAM_MAX_FAILS\n                  |NGX_HTTP_UPSTREAM_FAIL_TIMEOUT\n                  |NGX_HTTP_UPSTREAM_DOWN\n                  |NGX_HTTP_UPSTREAM_BACKUP;\n\n    return NGX_CONF_OK;\n}\n"
  },
  {
    "path": "src/http/modules/ngx_http_upstream_random_module.c",
    "content": "\n/*\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\ntypedef struct {\n    ngx_http_upstream_rr_peer_t          *peer;\n    ngx_uint_t                            range;\n} ngx_http_upstream_random_range_t;\n\n\ntypedef struct {\n    ngx_uint_t                            two;\n    ngx_http_upstream_random_range_t     *ranges;\n} ngx_http_upstream_random_srv_conf_t;\n\n\ntypedef struct {\n    /* the round robin data must be first */\n    ngx_http_upstream_rr_peer_data_t      rrp;\n\n    ngx_http_upstream_random_srv_conf_t  *conf;\n    u_char                                tries;\n} ngx_http_upstream_random_peer_data_t;\n\n\nstatic ngx_int_t ngx_http_upstream_init_random(ngx_conf_t *cf,\n    ngx_http_upstream_srv_conf_t *us);\nstatic ngx_int_t ngx_http_upstream_update_random(ngx_pool_t *pool,\n    ngx_http_upstream_srv_conf_t *us);\n\nstatic ngx_int_t ngx_http_upstream_init_random_peer(ngx_http_request_t *r,\n    ngx_http_upstream_srv_conf_t *us);\nstatic ngx_int_t ngx_http_upstream_get_random_peer(ngx_peer_connection_t *pc,\n    void *data);\nstatic ngx_int_t ngx_http_upstream_get_random2_peer(ngx_peer_connection_t *pc,\n    void *data);\nstatic ngx_uint_t ngx_http_upstream_peek_random_peer(\n    ngx_http_upstream_rr_peers_t *peers,\n    ngx_http_upstream_random_peer_data_t *rp);\nstatic void *ngx_http_upstream_random_create_conf(ngx_conf_t *cf);\nstatic char *ngx_http_upstream_random(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n\n\nstatic ngx_command_t  ngx_http_upstream_random_commands[] = {\n\n    { ngx_string(\"random\"),\n      NGX_HTTP_UPS_CONF|NGX_CONF_NOARGS|NGX_CONF_TAKE12,\n      ngx_http_upstream_random,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      0,\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_upstream_random_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    NULL,                                  /* postconfiguration */\n\n    NULL,                                  /* create main configuration */\n    NULL,                                  /* init main configuration */\n\n    ngx_http_upstream_random_create_conf,  /* 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_upstream_random_module = {\n    NGX_MODULE_V1,\n    &ngx_http_upstream_random_module_ctx,  /* module context */\n    ngx_http_upstream_random_commands,     /* 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_upstream_init_random(ngx_conf_t *cf, ngx_http_upstream_srv_conf_t *us)\n{\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cf->log, 0, \"init random\");\n\n    if (ngx_http_upstream_init_round_robin(cf, us) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    us->peer.init = ngx_http_upstream_init_random_peer;\n\n#if (NGX_HTTP_UPSTREAM_ZONE)\n    if (us->shm_zone) {\n        return NGX_OK;\n    }\n#endif\n\n    return ngx_http_upstream_update_random(cf->pool, us);\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_update_random(ngx_pool_t *pool,\n    ngx_http_upstream_srv_conf_t *us)\n{\n    size_t                                size;\n    ngx_uint_t                            i, total_weight;\n    ngx_http_upstream_rr_peer_t          *peer;\n    ngx_http_upstream_rr_peers_t         *peers;\n    ngx_http_upstream_random_range_t     *ranges;\n    ngx_http_upstream_random_srv_conf_t  *rcf;\n\n    rcf = ngx_http_conf_upstream_srv_conf(us, ngx_http_upstream_random_module);\n\n    peers = us->peer.data;\n\n    size = peers->number * sizeof(ngx_http_upstream_random_range_t);\n\n    ranges = pool ? ngx_palloc(pool, size) : ngx_alloc(size, ngx_cycle->log);\n    if (ranges == NULL) {\n        return NGX_ERROR;\n    }\n\n    total_weight = 0;\n\n    for (peer = peers->peer, i = 0; peer; peer = peer->next, i++) {\n        ranges[i].peer = peer;\n        ranges[i].range = total_weight;\n        total_weight += peer->weight;\n    }\n\n    rcf->ranges = ranges;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_init_random_peer(ngx_http_request_t *r,\n    ngx_http_upstream_srv_conf_t *us)\n{\n    ngx_http_upstream_random_srv_conf_t   *rcf;\n    ngx_http_upstream_random_peer_data_t  *rp;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"init random peer\");\n\n    rcf = ngx_http_conf_upstream_srv_conf(us, ngx_http_upstream_random_module);\n\n    rp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_random_peer_data_t));\n    if (rp == NULL) {\n        return NGX_ERROR;\n    }\n\n    r->upstream->peer.data = &rp->rrp;\n\n    if (ngx_http_upstream_init_round_robin_peer(r, us) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    if (rcf->two) {\n        r->upstream->peer.get = ngx_http_upstream_get_random2_peer;\n\n    } else {\n        r->upstream->peer.get = ngx_http_upstream_get_random_peer;\n    }\n\n    rp->conf = rcf;\n    rp->tries = 0;\n\n    ngx_http_upstream_rr_peers_rlock(rp->rrp.peers);\n\n#if (NGX_HTTP_UPSTREAM_ZONE)\n    if (rp->rrp.peers->shpool && rcf->ranges == NULL) {\n        if (ngx_http_upstream_update_random(NULL, us) != NGX_OK) {\n            ngx_http_upstream_rr_peers_unlock(rp->rrp.peers);\n            return NGX_ERROR;\n        }\n    }\n#endif\n\n    ngx_http_upstream_rr_peers_unlock(rp->rrp.peers);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_get_random_peer(ngx_peer_connection_t *pc, void *data)\n{\n    ngx_http_upstream_random_peer_data_t  *rp = data;\n\n    time_t                             now;\n    uintptr_t                          m;\n    ngx_uint_t                         i, n;\n    ngx_http_upstream_rr_peer_t       *peer;\n    ngx_http_upstream_rr_peers_t      *peers;\n    ngx_http_upstream_rr_peer_data_t  *rrp;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                   \"get random peer, try: %ui\", pc->tries);\n\n    rrp = &rp->rrp;\n    peers = rrp->peers;\n\n    ngx_http_upstream_rr_peers_rlock(peers);\n\n    if (rp->tries > 20 || peers->single) {\n        ngx_http_upstream_rr_peers_unlock(peers);\n        return ngx_http_upstream_get_round_robin_peer(pc, rrp);\n    }\n\n    pc->cached = 0;\n    pc->connection = NULL;\n\n    now = ngx_time();\n\n    for ( ;; ) {\n\n        i = ngx_http_upstream_peek_random_peer(peers, rp);\n\n        peer = rp->conf->ranges[i].peer;\n\n        n = i / (8 * sizeof(uintptr_t));\n        m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));\n\n        if (rrp->tried[n] & m) {\n            goto next;\n        }\n\n        ngx_http_upstream_rr_peer_lock(peers, peer);\n\n        if (peer->down) {\n            ngx_http_upstream_rr_peer_unlock(peers, peer);\n            goto next;\n        }\n\n        if (peer->max_fails\n            && peer->fails >= peer->max_fails\n            && now - peer->checked <= peer->fail_timeout)\n        {\n            ngx_http_upstream_rr_peer_unlock(peers, peer);\n            goto next;\n        }\n\n        if (peer->max_conns && peer->conns >= peer->max_conns) {\n            ngx_http_upstream_rr_peer_unlock(peers, peer);\n            goto next;\n        }\n\n        break;\n\n    next:\n\n        if (++rp->tries > 20) {\n            ngx_http_upstream_rr_peers_unlock(peers);\n            return ngx_http_upstream_get_round_robin_peer(pc, rrp);\n        }\n    }\n\n    rrp->current = peer;\n\n    if (now - peer->checked > peer->fail_timeout) {\n        peer->checked = now;\n    }\n\n    pc->sockaddr = peer->sockaddr;\n    pc->socklen = peer->socklen;\n    pc->name = &peer->name;\n\n    peer->conns++;\n\n    ngx_http_upstream_rr_peer_unlock(peers, peer);\n    ngx_http_upstream_rr_peers_unlock(peers);\n\n    rrp->tried[n] |= m;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_get_random2_peer(ngx_peer_connection_t *pc, void *data)\n{\n    ngx_http_upstream_random_peer_data_t  *rp = data;\n\n    time_t                             now;\n    uintptr_t                          m;\n    ngx_uint_t                         i, n, p;\n    ngx_http_upstream_rr_peer_t       *peer, *prev;\n    ngx_http_upstream_rr_peers_t      *peers;\n    ngx_http_upstream_rr_peer_data_t  *rrp;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                   \"get random2 peer, try: %ui\", pc->tries);\n\n    rrp = &rp->rrp;\n    peers = rrp->peers;\n\n    ngx_http_upstream_rr_peers_wlock(peers);\n\n    if (rp->tries > 20 || peers->single) {\n        ngx_http_upstream_rr_peers_unlock(peers);\n        return ngx_http_upstream_get_round_robin_peer(pc, rrp);\n    }\n\n    pc->cached = 0;\n    pc->connection = NULL;\n\n    now = ngx_time();\n\n    prev = NULL;\n\n#if (NGX_SUPPRESS_WARN)\n    p = 0;\n#endif\n\n    for ( ;; ) {\n\n        i = ngx_http_upstream_peek_random_peer(peers, rp);\n\n        peer = rp->conf->ranges[i].peer;\n\n        if (peer == prev) {\n            goto next;\n        }\n\n        n = i / (8 * sizeof(uintptr_t));\n        m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));\n\n        if (rrp->tried[n] & m) {\n            goto next;\n        }\n\n        if (peer->down) {\n            goto next;\n        }\n\n        if (peer->max_fails\n            && peer->fails >= peer->max_fails\n            && now - peer->checked <= peer->fail_timeout)\n        {\n            goto next;\n        }\n\n        if (peer->max_conns && peer->conns >= peer->max_conns) {\n            goto next;\n        }\n\n        if (prev) {\n            if (peer->conns * prev->weight > prev->conns * peer->weight) {\n                peer = prev;\n                n = p / (8 * sizeof(uintptr_t));\n                m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t));\n            }\n\n            break;\n        }\n\n        prev = peer;\n        p = i;\n\n    next:\n\n        if (++rp->tries > 20) {\n            ngx_http_upstream_rr_peers_unlock(peers);\n            return ngx_http_upstream_get_round_robin_peer(pc, rrp);\n        }\n    }\n\n    rrp->current = peer;\n\n    if (now - peer->checked > peer->fail_timeout) {\n        peer->checked = now;\n    }\n\n    pc->sockaddr = peer->sockaddr;\n    pc->socklen = peer->socklen;\n    pc->name = &peer->name;\n\n    peer->conns++;\n\n    ngx_http_upstream_rr_peers_unlock(peers);\n\n    rrp->tried[n] |= m;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_uint_t\nngx_http_upstream_peek_random_peer(ngx_http_upstream_rr_peers_t *peers,\n    ngx_http_upstream_random_peer_data_t *rp)\n{\n    ngx_uint_t  i, j, k, x;\n\n    x = ngx_random() % peers->total_weight;\n\n    i = 0;\n    j = peers->number;\n\n    while (j - i > 1) {\n        k = (i + j) / 2;\n\n        if (x < rp->conf->ranges[k].range) {\n            j = k;\n\n        } else {\n            i = k;\n        }\n    }\n\n    return i;\n}\n\n\nstatic void *\nngx_http_upstream_random_create_conf(ngx_conf_t *cf)\n{\n    ngx_http_upstream_random_srv_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_random_srv_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc():\n     *\n     *     conf->two = 0;\n     */\n\n    return conf;\n}\n\n\nstatic char *\nngx_http_upstream_random(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_upstream_random_srv_conf_t  *rcf = conf;\n\n    ngx_str_t                     *value;\n    ngx_http_upstream_srv_conf_t  *uscf;\n\n    uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);\n\n    if (uscf->peer.init_upstream) {\n        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                           \"load balancing method redefined\");\n    }\n\n    uscf->peer.init_upstream = ngx_http_upstream_init_random;\n\n    uscf->flags = NGX_HTTP_UPSTREAM_CREATE\n                  |NGX_HTTP_UPSTREAM_WEIGHT\n                  |NGX_HTTP_UPSTREAM_MAX_CONNS\n                  |NGX_HTTP_UPSTREAM_MAX_FAILS\n                  |NGX_HTTP_UPSTREAM_FAIL_TIMEOUT\n                  |NGX_HTTP_UPSTREAM_DOWN;\n\n    if (cf->args->nelts == 1) {\n        return NGX_CONF_OK;\n    }\n\n    value = cf->args->elts;\n\n    if (ngx_strcmp(value[1].data, \"two\") == 0) {\n        rcf->two = 1;\n\n    } else {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid parameter \\\"%V\\\"\", &value[1]);\n        return NGX_CONF_ERROR;\n    }\n\n    if (cf->args->nelts == 2) {\n        return NGX_CONF_OK;\n    }\n\n    if (ngx_strcmp(value[2].data, \"least_conn\") != 0) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid parameter \\\"%V\\\"\", &value[2]);\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n"
  },
  {
    "path": "src/http/modules/ngx_http_upstream_zone_module.c",
    "content": "\n/*\n * Copyright (C) Ruslan Ermilov\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\nstatic char *ngx_http_upstream_zone(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic ngx_int_t ngx_http_upstream_init_zone(ngx_shm_zone_t *shm_zone,\n    void *data);\nstatic ngx_http_upstream_rr_peers_t *ngx_http_upstream_zone_copy_peers(\n    ngx_slab_pool_t *shpool, ngx_http_upstream_srv_conf_t *uscf);\nstatic ngx_http_upstream_rr_peer_t *ngx_http_upstream_zone_copy_peer(\n    ngx_http_upstream_rr_peers_t *peers, ngx_http_upstream_rr_peer_t *src);\n\n\nstatic ngx_command_t  ngx_http_upstream_zone_commands[] = {\n\n    { ngx_string(\"zone\"),\n      NGX_HTTP_UPS_CONF|NGX_CONF_TAKE12,\n      ngx_http_upstream_zone,\n      0,\n      0,\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_upstream_zone_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    NULL,                                  /* 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\nngx_module_t  ngx_http_upstream_zone_module = {\n    NGX_MODULE_V1,\n    &ngx_http_upstream_zone_module_ctx,    /* module context */\n    ngx_http_upstream_zone_commands,       /* 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 char *\nngx_http_upstream_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ssize_t                         size;\n    ngx_str_t                      *value;\n    ngx_http_upstream_srv_conf_t   *uscf;\n    ngx_http_upstream_main_conf_t  *umcf;\n\n    uscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_upstream_module);\n    umcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_upstream_module);\n\n    value = cf->args->elts;\n\n    if (!value[1].len) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid zone name \\\"%V\\\"\", &value[1]);\n        return NGX_CONF_ERROR;\n    }\n\n    if (cf->args->nelts == 3) {\n        size = ngx_parse_size(&value[2]);\n\n        if (size == NGX_ERROR) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"invalid zone size \\\"%V\\\"\", &value[2]);\n            return NGX_CONF_ERROR;\n        }\n\n        if (size < (ssize_t) (8 * ngx_pagesize)) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"zone \\\"%V\\\" is too small\", &value[1]);\n            return NGX_CONF_ERROR;\n        }\n\n    } else {\n        size = 0;\n    }\n\n    uscf->shm_zone = ngx_shared_memory_add(cf, &value[1], size,\n                                           &ngx_http_upstream_module);\n    if (uscf->shm_zone == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    uscf->shm_zone->init = ngx_http_upstream_init_zone;\n    uscf->shm_zone->data = umcf;\n\n    uscf->shm_zone->noreuse = 1;\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_init_zone(ngx_shm_zone_t *shm_zone, void *data)\n{\n    size_t                          len;\n    ngx_uint_t                      i;\n    ngx_slab_pool_t                *shpool;\n    ngx_http_upstream_rr_peers_t   *peers, **peersp;\n    ngx_http_upstream_srv_conf_t   *uscf, **uscfp;\n    ngx_http_upstream_main_conf_t  *umcf;\n\n    shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;\n    umcf = shm_zone->data;\n    uscfp = umcf->upstreams.elts;\n\n    if (shm_zone->shm.exists) {\n        peers = shpool->data;\n\n        for (i = 0; i < umcf->upstreams.nelts; i++) {\n            uscf = uscfp[i];\n\n            if (uscf->shm_zone != shm_zone) {\n                continue;\n            }\n\n            uscf->peer.data = peers;\n            peers = peers->zone_next;\n        }\n\n        return NGX_OK;\n    }\n\n    len = sizeof(\" in upstream zone \\\"\\\"\") + shm_zone->shm.name.len;\n\n    shpool->log_ctx = ngx_slab_alloc(shpool, len);\n    if (shpool->log_ctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_sprintf(shpool->log_ctx, \" in upstream zone \\\"%V\\\"%Z\",\n                &shm_zone->shm.name);\n\n\n    /* copy peers to shared memory */\n\n    peersp = (ngx_http_upstream_rr_peers_t **) (void *) &shpool->data;\n\n    for (i = 0; i < umcf->upstreams.nelts; i++) {\n        uscf = uscfp[i];\n\n        if (uscf->shm_zone != shm_zone) {\n            continue;\n        }\n\n        peers = ngx_http_upstream_zone_copy_peers(shpool, uscf);\n        if (peers == NULL) {\n            return NGX_ERROR;\n        }\n\n        *peersp = peers;\n        peersp = &peers->zone_next;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_http_upstream_rr_peers_t *\nngx_http_upstream_zone_copy_peers(ngx_slab_pool_t *shpool,\n    ngx_http_upstream_srv_conf_t *uscf)\n{\n    ngx_str_t                     *name;\n    ngx_http_upstream_rr_peer_t   *peer, **peerp;\n    ngx_http_upstream_rr_peers_t  *peers, *backup;\n\n    peers = ngx_slab_alloc(shpool, sizeof(ngx_http_upstream_rr_peers_t));\n    if (peers == NULL) {\n        return NULL;\n    }\n\n    ngx_memcpy(peers, uscf->peer.data, sizeof(ngx_http_upstream_rr_peers_t));\n\n    name = ngx_slab_alloc(shpool, sizeof(ngx_str_t));\n    if (name == NULL) {\n        return NULL;\n    }\n\n    name->data = ngx_slab_alloc(shpool, peers->name->len);\n    if (name->data == NULL) {\n        return NULL;\n    }\n\n    ngx_memcpy(name->data, peers->name->data, peers->name->len);\n    name->len = peers->name->len;\n\n    peers->name = name;\n\n    peers->shpool = shpool;\n\n    for (peerp = &peers->peer; *peerp; peerp = &peer->next) {\n        /* pool is unlocked */\n        peer = ngx_http_upstream_zone_copy_peer(peers, *peerp);\n        if (peer == NULL) {\n            return NULL;\n        }\n\n        *peerp = peer;\n    }\n\n    if (peers->next == NULL) {\n        goto done;\n    }\n\n    backup = ngx_slab_alloc(shpool, sizeof(ngx_http_upstream_rr_peers_t));\n    if (backup == NULL) {\n        return NULL;\n    }\n\n    ngx_memcpy(backup, peers->next, sizeof(ngx_http_upstream_rr_peers_t));\n\n    backup->name = name;\n\n    backup->shpool = shpool;\n\n    for (peerp = &backup->peer; *peerp; peerp = &peer->next) {\n        /* pool is unlocked */\n        peer = ngx_http_upstream_zone_copy_peer(backup, *peerp);\n        if (peer == NULL) {\n            return NULL;\n        }\n\n        *peerp = peer;\n    }\n\n    peers->next = backup;\n\ndone:\n\n    uscf->peer.data = peers;\n\n    return peers;\n}\n\n\nstatic ngx_http_upstream_rr_peer_t *\nngx_http_upstream_zone_copy_peer(ngx_http_upstream_rr_peers_t *peers,\n    ngx_http_upstream_rr_peer_t *src)\n{\n    ngx_slab_pool_t              *pool;\n    ngx_http_upstream_rr_peer_t  *dst;\n\n    pool = peers->shpool;\n\n    dst = ngx_slab_calloc_locked(pool, sizeof(ngx_http_upstream_rr_peer_t));\n    if (dst == NULL) {\n        return NULL;\n    }\n\n    if (src) {\n        ngx_memcpy(dst, src, sizeof(ngx_http_upstream_rr_peer_t));\n        dst->sockaddr = NULL;\n        dst->name.data = NULL;\n        dst->server.data = NULL;\n    }\n\n    dst->sockaddr = ngx_slab_calloc_locked(pool, sizeof(ngx_sockaddr_t));\n    if (dst->sockaddr == NULL) {\n        goto failed;\n    }\n\n    dst->name.data = ngx_slab_calloc_locked(pool, NGX_SOCKADDR_STRLEN);\n    if (dst->name.data == NULL) {\n        goto failed;\n    }\n\n    if (src) {\n        ngx_memcpy(dst->sockaddr, src->sockaddr, src->socklen);\n        ngx_memcpy(dst->name.data, src->name.data, src->name.len);\n\n        dst->server.data = ngx_slab_alloc_locked(pool, src->server.len);\n        if (dst->server.data == NULL) {\n            goto failed;\n        }\n\n        ngx_memcpy(dst->server.data, src->server.data, src->server.len);\n    }\n\n    return dst;\n\nfailed:\n\n    if (dst->server.data) {\n        ngx_slab_free_locked(pool, dst->server.data);\n    }\n\n    if (dst->name.data) {\n        ngx_slab_free_locked(pool, dst->name.data);\n    }\n\n    if (dst->sockaddr) {\n        ngx_slab_free_locked(pool, dst->sockaddr);\n    }\n\n    ngx_slab_free_locked(pool, dst);\n\n    return NULL;\n}\n"
  },
  {
    "path": "src/http/modules/ngx_http_userid_filter_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\n#define NGX_HTTP_USERID_OFF   0\n#define NGX_HTTP_USERID_LOG   1\n#define NGX_HTTP_USERID_V1    2\n#define NGX_HTTP_USERID_ON    3\n\n#define NGX_HTTP_USERID_COOKIE_OFF              0x0002\n#define NGX_HTTP_USERID_COOKIE_SECURE           0x0004\n#define NGX_HTTP_USERID_COOKIE_HTTPONLY         0x0008\n#define NGX_HTTP_USERID_COOKIE_SAMESITE         0x0010\n#define NGX_HTTP_USERID_COOKIE_SAMESITE_STRICT  0x0020\n#define NGX_HTTP_USERID_COOKIE_SAMESITE_LAX     0x0040\n#define NGX_HTTP_USERID_COOKIE_SAMESITE_NONE    0x0080\n\n/* 31 Dec 2037 23:55:55 GMT */\n#define NGX_HTTP_USERID_MAX_EXPIRES  2145916555\n\n\ntypedef struct {\n    ngx_uint_t  enable;\n    ngx_uint_t  flags;\n\n    ngx_int_t   service;\n\n    ngx_str_t   name;\n    ngx_str_t   domain;\n    ngx_str_t   path;\n    ngx_str_t   p3p;\n\n    time_t      expires;\n\n    u_char      mark;\n} ngx_http_userid_conf_t;\n\n\ntypedef struct {\n    uint32_t    uid_got[4];\n    uint32_t    uid_set[4];\n    ngx_str_t   cookie;\n    ngx_uint_t  reset;\n} ngx_http_userid_ctx_t;\n\n\nstatic ngx_http_userid_ctx_t *ngx_http_userid_get_uid(ngx_http_request_t *r,\n    ngx_http_userid_conf_t *conf);\nstatic ngx_int_t ngx_http_userid_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, ngx_str_t *name, uint32_t *uid);\nstatic ngx_int_t ngx_http_userid_set_uid(ngx_http_request_t *r,\n    ngx_http_userid_ctx_t *ctx, ngx_http_userid_conf_t *conf);\nstatic ngx_int_t ngx_http_userid_create_uid(ngx_http_request_t *r,\n    ngx_http_userid_ctx_t *ctx, ngx_http_userid_conf_t *conf);\n\nstatic ngx_int_t ngx_http_userid_add_variables(ngx_conf_t *cf);\nstatic ngx_int_t ngx_http_userid_init(ngx_conf_t *cf);\nstatic void *ngx_http_userid_create_conf(ngx_conf_t *cf);\nstatic char *ngx_http_userid_merge_conf(ngx_conf_t *cf, void *parent,\n    void *child);\nstatic char *ngx_http_userid_domain(ngx_conf_t *cf, void *post, void *data);\nstatic char *ngx_http_userid_path(ngx_conf_t *cf, void *post, void *data);\nstatic char *ngx_http_userid_expires(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_http_userid_p3p(ngx_conf_t *cf, void *post, void *data);\nstatic char *ngx_http_userid_mark(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic ngx_int_t ngx_http_userid_init_worker(ngx_cycle_t *cycle);\n\n\n\nstatic uint32_t  start_value;\nstatic uint32_t  sequencer_v1 = 1;\nstatic uint32_t  sequencer_v2 = 0x03030302;\n\n\nstatic u_char expires[] = \"; expires=Thu, 31-Dec-37 23:55:55 GMT\";\n\n\nstatic ngx_http_output_header_filter_pt  ngx_http_next_header_filter;\n\n\nstatic ngx_conf_enum_t  ngx_http_userid_state[] = {\n    { ngx_string(\"off\"), NGX_HTTP_USERID_OFF },\n    { ngx_string(\"log\"), NGX_HTTP_USERID_LOG },\n    { ngx_string(\"v1\"), NGX_HTTP_USERID_V1 },\n    { ngx_string(\"on\"), NGX_HTTP_USERID_ON },\n    { ngx_null_string, 0 }\n};\n\n\nstatic ngx_conf_bitmask_t  ngx_http_userid_flags[] = {\n    { ngx_string(\"off\"), NGX_HTTP_USERID_COOKIE_OFF },\n    { ngx_string(\"secure\"), NGX_HTTP_USERID_COOKIE_SECURE },\n    { ngx_string(\"httponly\"), NGX_HTTP_USERID_COOKIE_HTTPONLY },\n    { ngx_string(\"samesite=strict\"),\n      NGX_HTTP_USERID_COOKIE_SAMESITE|NGX_HTTP_USERID_COOKIE_SAMESITE_STRICT },\n    { ngx_string(\"samesite=lax\"),\n      NGX_HTTP_USERID_COOKIE_SAMESITE|NGX_HTTP_USERID_COOKIE_SAMESITE_LAX },\n    { ngx_string(\"samesite=none\"),\n      NGX_HTTP_USERID_COOKIE_SAMESITE|NGX_HTTP_USERID_COOKIE_SAMESITE_NONE },\n    { ngx_null_string, 0 }\n};\n\n\nstatic ngx_conf_post_handler_pt  ngx_http_userid_domain_p =\n    ngx_http_userid_domain;\nstatic ngx_conf_post_handler_pt  ngx_http_userid_path_p = ngx_http_userid_path;\nstatic ngx_conf_post_handler_pt  ngx_http_userid_p3p_p = ngx_http_userid_p3p;\n\n\nstatic ngx_command_t  ngx_http_userid_commands[] = {\n\n    { ngx_string(\"userid\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_enum_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_userid_conf_t, enable),\n      ngx_http_userid_state },\n\n    { ngx_string(\"userid_service\"),\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_userid_conf_t, service),\n      NULL },\n\n    { ngx_string(\"userid_name\"),\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_userid_conf_t, name),\n      NULL },\n\n    { ngx_string(\"userid_domain\"),\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_userid_conf_t, domain),\n      &ngx_http_userid_domain_p },\n\n    { ngx_string(\"userid_path\"),\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_userid_conf_t, path),\n      &ngx_http_userid_path_p },\n\n    { ngx_string(\"userid_expires\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_http_userid_expires,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"userid_flags\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,\n      ngx_conf_set_bitmask_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_userid_conf_t, flags),\n      &ngx_http_userid_flags },\n\n    { ngx_string(\"userid_p3p\"),\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_userid_conf_t, p3p),\n      &ngx_http_userid_p3p_p },\n\n    { ngx_string(\"userid_mark\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_http_userid_mark,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_userid_filter_module_ctx = {\n    ngx_http_userid_add_variables,         /* preconfiguration */\n    ngx_http_userid_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    ngx_http_userid_create_conf,           /* create location configuration */\n    ngx_http_userid_merge_conf             /* merge location configuration */\n};\n\n\nngx_module_t  ngx_http_userid_filter_module = {\n    NGX_MODULE_V1,\n    &ngx_http_userid_filter_module_ctx,    /* module context */\n    ngx_http_userid_commands,              /* module directives */\n    NGX_HTTP_MODULE,                       /* module type */\n    NULL,                                  /* init master */\n    NULL,                                  /* init module */\n    ngx_http_userid_init_worker,           /* 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_str_t   ngx_http_userid_got = ngx_string(\"uid_got\");\nstatic ngx_str_t   ngx_http_userid_set = ngx_string(\"uid_set\");\nstatic ngx_str_t   ngx_http_userid_reset = ngx_string(\"uid_reset\");\nstatic ngx_uint_t  ngx_http_userid_reset_index;\n\n\nstatic ngx_int_t\nngx_http_userid_filter(ngx_http_request_t *r)\n{\n    ngx_http_userid_ctx_t   *ctx;\n    ngx_http_userid_conf_t  *conf;\n\n    if (r != r->main) {\n        return ngx_http_next_header_filter(r);\n    }\n\n    conf = ngx_http_get_module_loc_conf(r, ngx_http_userid_filter_module);\n\n    if (conf->enable < NGX_HTTP_USERID_V1) {\n        return ngx_http_next_header_filter(r);\n    }\n\n    ctx = ngx_http_userid_get_uid(r, conf);\n\n    if (ctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (ngx_http_userid_set_uid(r, ctx, conf) == NGX_OK) {\n        return ngx_http_next_header_filter(r);\n    }\n\n    return NGX_ERROR;\n}\n\n\nstatic ngx_int_t\nngx_http_userid_got_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    ngx_http_userid_ctx_t   *ctx;\n    ngx_http_userid_conf_t  *conf;\n\n    conf = ngx_http_get_module_loc_conf(r->main, ngx_http_userid_filter_module);\n\n    if (conf->enable == NGX_HTTP_USERID_OFF) {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    ctx = ngx_http_userid_get_uid(r->main, conf);\n\n    if (ctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (ctx->uid_got[3] != 0) {\n        return ngx_http_userid_variable(r->main, v, &conf->name, ctx->uid_got);\n    }\n\n    v->not_found = 1;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_userid_set_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    ngx_http_userid_ctx_t   *ctx;\n    ngx_http_userid_conf_t  *conf;\n\n    conf = ngx_http_get_module_loc_conf(r->main, ngx_http_userid_filter_module);\n\n    if (conf->enable < NGX_HTTP_USERID_V1) {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    ctx = ngx_http_userid_get_uid(r->main, conf);\n\n    if (ctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (ngx_http_userid_create_uid(r->main, ctx, conf) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    if (ctx->uid_set[3] == 0) {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    return ngx_http_userid_variable(r->main, v, &conf->name, ctx->uid_set);\n}\n\n\nstatic ngx_http_userid_ctx_t *\nngx_http_userid_get_uid(ngx_http_request_t *r, ngx_http_userid_conf_t *conf)\n{\n    ngx_str_t               src, dst;\n    ngx_table_elt_t        *cookie;\n    ngx_http_userid_ctx_t  *ctx;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_userid_filter_module);\n\n    if (ctx) {\n        return ctx;\n    }\n\n    if (ctx == NULL) {\n        ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_userid_ctx_t));\n        if (ctx == NULL) {\n            return NULL;\n        }\n\n        ngx_http_set_ctx(r, ctx, ngx_http_userid_filter_module);\n    }\n\n    cookie = ngx_http_parse_multi_header_lines(r, r->headers_in.cookie,\n                                               &conf->name, &ctx->cookie);\n    if (cookie == NULL) {\n        return ctx;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"uid cookie: \\\"%V\\\"\", &ctx->cookie);\n\n    if (ctx->cookie.len < 22) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"client sent too short userid cookie \\\"%V\\\"\",\n                      &cookie->value);\n        return ctx;\n    }\n\n    src = ctx->cookie;\n\n    /*\n     * we have to limit the encoded string to 22 characters because\n     *  1) cookie may be marked by \"userid_mark\",\n     *  2) and there are already the millions cookies with a garbage\n     *     instead of the correct base64 trail \"==\"\n     */\n\n    src.len = 22;\n\n    dst.data = (u_char *) ctx->uid_got;\n\n    if (ngx_decode_base64(&dst, &src) == NGX_ERROR) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"client sent invalid userid cookie \\\"%V\\\"\",\n                      &cookie->value);\n        return ctx;\n    }\n\n    ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"uid: %08XD%08XD%08XD%08XD\",\n                   ctx->uid_got[0], ctx->uid_got[1],\n                   ctx->uid_got[2], ctx->uid_got[3]);\n\n    return ctx;\n}\n\n\nstatic ngx_int_t\nngx_http_userid_set_uid(ngx_http_request_t *r, ngx_http_userid_ctx_t *ctx,\n    ngx_http_userid_conf_t *conf)\n{\n    u_char           *cookie, *p;\n    size_t            len;\n    ngx_str_t         src, dst;\n    ngx_table_elt_t  *set_cookie, *p3p;\n\n    if (ngx_http_userid_create_uid(r, ctx, conf) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    if (ctx->uid_set[3] == 0) {\n        return NGX_OK;\n    }\n\n    len = conf->name.len + 1 + ngx_base64_encoded_length(16) + conf->path.len;\n\n    if (conf->expires) {\n        len += sizeof(expires) - 1 + 2;\n    }\n\n    if (conf->domain.len) {\n        len += conf->domain.len;\n    }\n\n    if (conf->flags & NGX_HTTP_USERID_COOKIE_SECURE) {\n        len += sizeof(\"; secure\") - 1;\n    }\n\n    if (conf->flags & NGX_HTTP_USERID_COOKIE_HTTPONLY) {\n        len += sizeof(\"; httponly\") - 1;\n    }\n\n    if (conf->flags & NGX_HTTP_USERID_COOKIE_SAMESITE_STRICT) {\n        len += sizeof(\"; samesite=strict\") - 1;\n    }\n\n    if (conf->flags & NGX_HTTP_USERID_COOKIE_SAMESITE_LAX) {\n        len += sizeof(\"; samesite=lax\") - 1;\n    }\n\n    if (conf->flags & NGX_HTTP_USERID_COOKIE_SAMESITE_NONE) {\n        len += sizeof(\"; samesite=none\") - 1;\n    }\n\n    cookie = ngx_pnalloc(r->pool, len);\n    if (cookie == NULL) {\n        return NGX_ERROR;\n    }\n\n    p = ngx_copy(cookie, conf->name.data, conf->name.len);\n    *p++ = '=';\n\n    if (ctx->uid_got[3] == 0 || ctx->reset) {\n        src.len = 16;\n        src.data = (u_char *) ctx->uid_set;\n        dst.data = p;\n\n        ngx_encode_base64(&dst, &src);\n\n        p += dst.len;\n\n        if (conf->mark) {\n            *(p - 2) = conf->mark;\n        }\n\n    } else {\n        p = ngx_cpymem(p, ctx->cookie.data, 22);\n        *p++ = conf->mark;\n        *p++ = '=';\n    }\n\n    if (conf->expires == NGX_HTTP_USERID_MAX_EXPIRES) {\n        p = ngx_cpymem(p, expires, sizeof(expires) - 1);\n\n    } else if (conf->expires) {\n        p = ngx_cpymem(p, expires, sizeof(\"; expires=\") - 1);\n        p = ngx_http_cookie_time(p, ngx_time() + conf->expires);\n    }\n\n    p = ngx_copy(p, conf->domain.data, conf->domain.len);\n\n    p = ngx_copy(p, conf->path.data, conf->path.len);\n\n    if (conf->flags & NGX_HTTP_USERID_COOKIE_SECURE) {\n        p = ngx_cpymem(p, \"; secure\", sizeof(\"; secure\") - 1);\n    }\n\n    if (conf->flags & NGX_HTTP_USERID_COOKIE_HTTPONLY) {\n        p = ngx_cpymem(p, \"; httponly\", sizeof(\"; httponly\") - 1);\n    }\n\n    if (conf->flags & NGX_HTTP_USERID_COOKIE_SAMESITE_STRICT) {\n        p = ngx_cpymem(p, \"; samesite=strict\", sizeof(\"; samesite=strict\") - 1);\n    }\n\n    if (conf->flags & NGX_HTTP_USERID_COOKIE_SAMESITE_LAX) {\n        p = ngx_cpymem(p, \"; samesite=lax\", sizeof(\"; samesite=lax\") - 1);\n    }\n\n    if (conf->flags & NGX_HTTP_USERID_COOKIE_SAMESITE_NONE) {\n        p = ngx_cpymem(p, \"; samesite=none\", sizeof(\"; samesite=none\") - 1);\n    }\n\n    set_cookie = ngx_list_push(&r->headers_out.headers);\n    if (set_cookie == NULL) {\n        return NGX_ERROR;\n    }\n\n    set_cookie->hash = 1;\n    ngx_str_set(&set_cookie->key, \"Set-Cookie\");\n    set_cookie->value.len = p - cookie;\n    set_cookie->value.data = cookie;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"uid cookie: \\\"%V\\\"\", &set_cookie->value);\n\n    if (conf->p3p.len == 0) {\n        return NGX_OK;\n    }\n\n    p3p = ngx_list_push(&r->headers_out.headers);\n    if (p3p == NULL) {\n        return NGX_ERROR;\n    }\n\n    p3p->hash = 1;\n    ngx_str_set(&p3p->key, \"P3P\");\n    p3p->value = conf->p3p;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_userid_create_uid(ngx_http_request_t *r, ngx_http_userid_ctx_t *ctx,\n    ngx_http_userid_conf_t *conf)\n{\n    ngx_connection_t           *c;\n    struct sockaddr_in         *sin;\n    ngx_http_variable_value_t  *vv;\n#if (NGX_HAVE_INET6)\n    u_char                     *p;\n    struct sockaddr_in6        *sin6;\n#endif\n\n    if (ctx->uid_set[3] != 0) {\n        return NGX_OK;\n    }\n\n    if (ctx->uid_got[3] != 0) {\n\n        vv = ngx_http_get_indexed_variable(r, ngx_http_userid_reset_index);\n\n        if (vv == NULL || vv->not_found) {\n            return NGX_ERROR;\n        }\n\n        if (vv->len == 0 || (vv->len == 1 && vv->data[0] == '0')) {\n\n            if (conf->mark == '\\0'\n                || (ctx->cookie.len > 23\n                    && ctx->cookie.data[22] == conf->mark\n                    && ctx->cookie.data[23] == '='))\n            {\n                return NGX_OK;\n            }\n\n            ctx->uid_set[0] = ctx->uid_got[0];\n            ctx->uid_set[1] = ctx->uid_got[1];\n            ctx->uid_set[2] = ctx->uid_got[2];\n            ctx->uid_set[3] = ctx->uid_got[3];\n\n            return NGX_OK;\n\n        } else {\n            ctx->reset = 1;\n\n            if (vv->len == 3 && ngx_strncmp(vv->data, \"log\", 3) == 0) {\n                ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0,\n                        \"userid cookie \\\"%V=%08XD%08XD%08XD%08XD\\\" was reset\",\n                        &conf->name, ctx->uid_got[0], ctx->uid_got[1],\n                        ctx->uid_got[2], ctx->uid_got[3]);\n            }\n        }\n    }\n\n    /*\n     * TODO: in the threaded mode the sequencers should be in TLS and their\n     * ranges should be divided between threads\n     */\n\n    if (conf->enable == NGX_HTTP_USERID_V1) {\n        if (conf->service == NGX_CONF_UNSET) {\n            ctx->uid_set[0] = 0;\n        } else {\n            ctx->uid_set[0] = conf->service;\n        }\n        ctx->uid_set[1] = (uint32_t) ngx_time();\n        ctx->uid_set[2] = start_value;\n        ctx->uid_set[3] = sequencer_v1;\n        sequencer_v1 += 0x100;\n\n    } else {\n        if (conf->service == NGX_CONF_UNSET) {\n\n            c = r->connection;\n\n            if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) {\n                return NGX_ERROR;\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\n                p = (u_char *) &ctx->uid_set[0];\n\n                *p++ = sin6->sin6_addr.s6_addr[12];\n                *p++ = sin6->sin6_addr.s6_addr[13];\n                *p++ = sin6->sin6_addr.s6_addr[14];\n                *p = sin6->sin6_addr.s6_addr[15];\n\n                break;\n#endif\n\n#if (NGX_HAVE_UNIX_DOMAIN)\n            case AF_UNIX:\n                ctx->uid_set[0] = 0;\n                break;\n#endif\n\n            default: /* AF_INET */\n                sin = (struct sockaddr_in *) c->local_sockaddr;\n                ctx->uid_set[0] = sin->sin_addr.s_addr;\n                break;\n            }\n\n        } else {\n            ctx->uid_set[0] = htonl(conf->service);\n        }\n\n        ctx->uid_set[1] = htonl((uint32_t) ngx_time());\n        ctx->uid_set[2] = htonl(start_value);\n        ctx->uid_set[3] = htonl(sequencer_v2);\n        sequencer_v2 += 0x100;\n        if (sequencer_v2 < 0x03030302) {\n            sequencer_v2 = 0x03030302;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_userid_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,\n    ngx_str_t *name, uint32_t *uid)\n{\n    v->len = name->len + sizeof(\"=00001111222233334444555566667777\") - 1;\n    v->data = ngx_pnalloc(r->pool, v->len);\n    if (v->data == NULL) {\n        return NGX_ERROR;\n    }\n\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    ngx_sprintf(v->data, \"%V=%08XD%08XD%08XD%08XD\",\n                name, uid[0], uid[1], uid[2], uid[3]);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_userid_reset_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    *v = ngx_http_variable_null_value;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_userid_add_variables(ngx_conf_t *cf)\n{\n    ngx_int_t             n;\n    ngx_http_variable_t  *var;\n\n    var = ngx_http_add_variable(cf, &ngx_http_userid_got, 0);\n    if (var == NULL) {\n        return NGX_ERROR;\n    }\n\n    var->get_handler = ngx_http_userid_got_variable;\n\n    var = ngx_http_add_variable(cf, &ngx_http_userid_set, 0);\n    if (var == NULL) {\n        return NGX_ERROR;\n    }\n\n    var->get_handler = ngx_http_userid_set_variable;\n\n    var = ngx_http_add_variable(cf, &ngx_http_userid_reset,\n                                NGX_HTTP_VAR_CHANGEABLE);\n    if (var == NULL) {\n        return NGX_ERROR;\n    }\n\n    var->get_handler = ngx_http_userid_reset_variable;\n\n    n = ngx_http_get_variable_index(cf, &ngx_http_userid_reset);\n    if (n == NGX_ERROR) {\n        return NGX_ERROR;\n    }\n\n    ngx_http_userid_reset_index = n;\n\n    return NGX_OK;\n}\n\n\nstatic void *\nngx_http_userid_create_conf(ngx_conf_t *cf)\n{\n    ngx_http_userid_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_userid_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc():\n     *\n     *     conf->flags = 0;\n     *     conf->name = { 0, NULL };\n     *     conf->domain = { 0, NULL };\n     *     conf->path = { 0, NULL };\n     *     conf->p3p = { 0, NULL };\n     */\n\n    conf->enable = NGX_CONF_UNSET_UINT;\n    conf->service = NGX_CONF_UNSET;\n    conf->expires = NGX_CONF_UNSET;\n    conf->mark = (u_char) '\\xFF';\n\n    return conf;\n}\n\n\nstatic char *\nngx_http_userid_merge_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_http_userid_conf_t *prev = parent;\n    ngx_http_userid_conf_t *conf = child;\n\n    ngx_conf_merge_uint_value(conf->enable, prev->enable,\n                              NGX_HTTP_USERID_OFF);\n\n    ngx_conf_merge_bitmask_value(conf->flags, prev->flags,\n                            (NGX_CONF_BITMASK_SET|NGX_HTTP_USERID_COOKIE_OFF));\n\n    ngx_conf_merge_str_value(conf->name, prev->name, \"uid\");\n    ngx_conf_merge_str_value(conf->domain, prev->domain, \"\");\n    ngx_conf_merge_str_value(conf->path, prev->path, \"; path=/\");\n    ngx_conf_merge_str_value(conf->p3p, prev->p3p, \"\");\n\n    ngx_conf_merge_value(conf->service, prev->service, NGX_CONF_UNSET);\n    ngx_conf_merge_sec_value(conf->expires, prev->expires, 0);\n\n    if (conf->mark == (u_char) '\\xFF') {\n        if (prev->mark == (u_char) '\\xFF') {\n            conf->mark = '\\0';\n        } else {\n            conf->mark = prev->mark;\n        }\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_userid_init(ngx_conf_t *cf)\n{\n    ngx_http_next_header_filter = ngx_http_top_header_filter;\n    ngx_http_top_header_filter = ngx_http_userid_filter;\n\n    return NGX_OK;\n}\n\n\nstatic char *\nngx_http_userid_domain(ngx_conf_t *cf, void *post, void *data)\n{\n    ngx_str_t  *domain = data;\n\n    u_char  *p, *new;\n\n    if (ngx_strcmp(domain->data, \"none\") == 0) {\n        ngx_str_set(domain, \"\");\n        return NGX_CONF_OK;\n    }\n\n    new = ngx_pnalloc(cf->pool, sizeof(\"; domain=\") - 1 + domain->len);\n    if (new == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    p = ngx_cpymem(new, \"; domain=\", sizeof(\"; domain=\") - 1);\n    ngx_memcpy(p, domain->data, domain->len);\n\n    domain->len += sizeof(\"; domain=\") - 1;\n    domain->data = new;\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_userid_path(ngx_conf_t *cf, void *post, void *data)\n{\n    ngx_str_t  *path = data;\n\n    u_char  *p, *new;\n\n    new = ngx_pnalloc(cf->pool, sizeof(\"; path=\") - 1 + path->len);\n    if (new == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    p = ngx_cpymem(new, \"; path=\", sizeof(\"; path=\") - 1);\n    ngx_memcpy(p, path->data, path->len);\n\n    path->len += sizeof(\"; path=\") - 1;\n    path->data = new;\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_userid_expires(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_userid_conf_t *ucf = conf;\n\n    ngx_str_t  *value;\n\n    if (ucf->expires != NGX_CONF_UNSET) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    if (ngx_strcmp(value[1].data, \"max\") == 0) {\n        ucf->expires = NGX_HTTP_USERID_MAX_EXPIRES;\n        return NGX_CONF_OK;\n    }\n\n    if (ngx_strcmp(value[1].data, \"off\") == 0) {\n        ucf->expires = 0;\n        return NGX_CONF_OK;\n    }\n\n    ucf->expires = ngx_parse_time(&value[1], 1);\n    if (ucf->expires == (time_t) NGX_ERROR) {\n        return \"invalid value\";\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_userid_p3p(ngx_conf_t *cf, void *post, void *data)\n{\n    ngx_str_t  *p3p = data;\n\n    if (ngx_strcmp(p3p->data, \"none\") == 0) {\n        ngx_str_set(p3p, \"\");\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_userid_mark(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_userid_conf_t *ucf = conf;\n\n    ngx_str_t  *value;\n\n    if (ucf->mark != (u_char) '\\xFF') {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    if (ngx_strcmp(value[1].data, \"off\") == 0) {\n        ucf->mark = '\\0';\n        return NGX_CONF_OK;\n    }\n\n    if (value[1].len != 1\n        || !((value[1].data[0] >= '0' && value[1].data[0] <= '9')\n              || (value[1].data[0] >= 'A' && value[1].data[0] <= 'Z')\n              || (value[1].data[0] >= 'a' && value[1].data[0] <= 'z')\n              || value[1].data[0] == '='))\n    {\n        return \"value must be \\\"off\\\" or a single letter, digit or \\\"=\\\"\";\n    }\n\n    ucf->mark = value[1].data[0];\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_userid_init_worker(ngx_cycle_t *cycle)\n{\n    struct timeval  tp;\n\n    ngx_gettimeofday(&tp);\n\n    /* use the most significant usec part that fits to 16 bits */\n    start_value = (((uint32_t) tp.tv_usec / 20) << 16) | ngx_pid;\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "src/http/modules/ngx_http_uwsgi_module.c",
    "content": "\n/*\n * Copyright (C) Unbit S.a.s. 2009-2010\n * Copyright (C) 2008 Manlio Perillo (manlio.perillo@gmail.com)\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\ntypedef struct {\n    ngx_array_t                caches;  /* ngx_http_file_cache_t * */\n} ngx_http_uwsgi_main_conf_t;\n\n\ntypedef struct {\n    ngx_array_t               *flushes;\n    ngx_array_t               *lengths;\n    ngx_array_t               *values;\n    ngx_uint_t                 number;\n    ngx_hash_t                 hash;\n} ngx_http_uwsgi_params_t;\n\n\ntypedef struct {\n    ngx_http_upstream_conf_t   upstream;\n\n    ngx_http_uwsgi_params_t    params;\n#if (NGX_HTTP_CACHE)\n    ngx_http_uwsgi_params_t    params_cache;\n#endif\n    ngx_array_t               *params_source;\n\n    ngx_array_t               *uwsgi_lengths;\n    ngx_array_t               *uwsgi_values;\n\n#if (NGX_HTTP_CACHE)\n    ngx_http_complex_value_t   cache_key;\n#endif\n\n    ngx_str_t                  uwsgi_string;\n\n    ngx_uint_t                 modifier1;\n    ngx_uint_t                 modifier2;\n\n#if (NGX_HTTP_SSL)\n    ngx_uint_t                 ssl;\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_array_t               *ssl_conf_commands;\n#endif\n} ngx_http_uwsgi_loc_conf_t;\n\n\nstatic ngx_int_t ngx_http_uwsgi_eval(ngx_http_request_t *r,\n    ngx_http_uwsgi_loc_conf_t *uwcf);\nstatic ngx_int_t ngx_http_uwsgi_create_request(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_uwsgi_reinit_request(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_uwsgi_process_status_line(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_uwsgi_process_header(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_uwsgi_input_filter_init(void *data);\nstatic void ngx_http_uwsgi_abort_request(ngx_http_request_t *r);\nstatic void ngx_http_uwsgi_finalize_request(ngx_http_request_t *r,\n    ngx_int_t rc);\n\nstatic void *ngx_http_uwsgi_create_main_conf(ngx_conf_t *cf);\nstatic void *ngx_http_uwsgi_create_loc_conf(ngx_conf_t *cf);\nstatic char *ngx_http_uwsgi_merge_loc_conf(ngx_conf_t *cf, void *parent,\n    void *child);\nstatic ngx_int_t ngx_http_uwsgi_init_params(ngx_conf_t *cf,\n    ngx_http_uwsgi_loc_conf_t *conf, ngx_http_uwsgi_params_t *params,\n    ngx_keyval_t *default_params);\n\nstatic char *ngx_http_uwsgi_pass(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_http_uwsgi_store(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n\n#if (NGX_HTTP_CACHE)\nstatic ngx_int_t ngx_http_uwsgi_create_key(ngx_http_request_t *r);\nstatic char *ngx_http_uwsgi_cache(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_http_uwsgi_cache_key(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n#endif\n\n#if (NGX_HTTP_SSL)\nstatic char *ngx_http_uwsgi_ssl_password_file(ngx_conf_t *cf,\n    ngx_command_t *cmd, void *conf);\nstatic char *ngx_http_uwsgi_ssl_conf_command_check(ngx_conf_t *cf, void *post,\n    void *data);\nstatic ngx_int_t ngx_http_uwsgi_merge_ssl(ngx_conf_t *cf,\n    ngx_http_uwsgi_loc_conf_t *conf, ngx_http_uwsgi_loc_conf_t *prev);\nstatic ngx_int_t ngx_http_uwsgi_set_ssl(ngx_conf_t *cf,\n    ngx_http_uwsgi_loc_conf_t *uwcf);\n#endif\n\n\nstatic ngx_conf_num_bounds_t  ngx_http_uwsgi_modifier_bounds = {\n    ngx_conf_check_num_bounds, 0, 255\n};\n\n\nstatic ngx_conf_bitmask_t ngx_http_uwsgi_next_upstream_masks[] = {\n    { ngx_string(\"error\"), NGX_HTTP_UPSTREAM_FT_ERROR },\n    { ngx_string(\"timeout\"), NGX_HTTP_UPSTREAM_FT_TIMEOUT },\n    { ngx_string(\"invalid_header\"), NGX_HTTP_UPSTREAM_FT_INVALID_HEADER },\n    { ngx_string(\"non_idempotent\"), NGX_HTTP_UPSTREAM_FT_NON_IDEMPOTENT },\n    { ngx_string(\"http_500\"), NGX_HTTP_UPSTREAM_FT_HTTP_500 },\n    { ngx_string(\"http_503\"), NGX_HTTP_UPSTREAM_FT_HTTP_503 },\n    { ngx_string(\"http_403\"), NGX_HTTP_UPSTREAM_FT_HTTP_403 },\n    { ngx_string(\"http_404\"), NGX_HTTP_UPSTREAM_FT_HTTP_404 },\n    { ngx_string(\"http_429\"), NGX_HTTP_UPSTREAM_FT_HTTP_429 },\n    { ngx_string(\"updating\"), NGX_HTTP_UPSTREAM_FT_UPDATING },\n    { ngx_string(\"off\"), NGX_HTTP_UPSTREAM_FT_OFF },\n    { ngx_null_string, 0 }\n};\n\n\n#if (NGX_HTTP_SSL)\n\nstatic ngx_conf_bitmask_t  ngx_http_uwsgi_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    { ngx_string(\"TLSv1.3\"), NGX_SSL_TLSv1_3 },\n    { ngx_null_string, 0 }\n};\n\nstatic ngx_conf_post_t  ngx_http_uwsgi_ssl_conf_command_post =\n    { ngx_http_uwsgi_ssl_conf_command_check };\n\n#endif\n\n\nngx_module_t  ngx_http_uwsgi_module;\n\n\nstatic ngx_command_t ngx_http_uwsgi_commands[] = {\n\n    { ngx_string(\"uwsgi_pass\"),\n      NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,\n      ngx_http_uwsgi_pass,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"uwsgi_modifier1\"),\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_uwsgi_loc_conf_t, modifier1),\n      &ngx_http_uwsgi_modifier_bounds },\n\n    { ngx_string(\"uwsgi_modifier2\"),\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_uwsgi_loc_conf_t, modifier2),\n      &ngx_http_uwsgi_modifier_bounds },\n\n    { ngx_string(\"uwsgi_store\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_http_uwsgi_store,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"uwsgi_store_access\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123,\n      ngx_conf_set_access_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.store_access),\n      NULL },\n\n    { ngx_string(\"uwsgi_buffering\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.buffering),\n      NULL },\n\n    { ngx_string(\"uwsgi_request_buffering\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.request_buffering),\n      NULL },\n\n    { ngx_string(\"uwsgi_ignore_client_abort\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.ignore_client_abort),\n      NULL },\n\n    { ngx_string(\"uwsgi_bind\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,\n      ngx_http_upstream_bind_set_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.local),\n      NULL },\n\n    { ngx_string(\"uwsgi_socket_keepalive\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.socket_keepalive),\n      NULL },\n\n    { ngx_string(\"uwsgi_connect_timeout\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.connect_timeout),\n      NULL },\n\n    { ngx_string(\"uwsgi_send_timeout\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.send_timeout),\n      NULL },\n\n    { ngx_string(\"uwsgi_buffer_size\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.buffer_size),\n      NULL },\n\n    { ngx_string(\"uwsgi_pass_request_headers\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.pass_request_headers),\n      NULL },\n\n    { ngx_string(\"uwsgi_pass_request_body\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.pass_request_body),\n      NULL },\n\n    { ngx_string(\"uwsgi_intercept_errors\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.intercept_errors),\n      NULL },\n\n    { ngx_string(\"uwsgi_read_timeout\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.read_timeout),\n      NULL },\n\n    { ngx_string(\"uwsgi_buffers\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,\n      ngx_conf_set_bufs_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.bufs),\n      NULL },\n\n    { ngx_string(\"uwsgi_busy_buffers_size\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.busy_buffers_size_conf),\n      NULL },\n\n    { ngx_string(\"uwsgi_force_ranges\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.force_ranges),\n      NULL },\n\n    { ngx_string(\"uwsgi_limit_rate\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.limit_rate),\n      NULL },\n\n#if (NGX_HTTP_CACHE)\n\n    { ngx_string(\"uwsgi_cache\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_http_uwsgi_cache,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"uwsgi_cache_key\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_http_uwsgi_cache_key,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"uwsgi_cache_path\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_2MORE,\n      ngx_http_file_cache_set_slot,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      offsetof(ngx_http_uwsgi_main_conf_t, caches),\n      &ngx_http_uwsgi_module },\n\n    { ngx_string(\"uwsgi_cache_bypass\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,\n      ngx_http_set_predicate_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.cache_bypass),\n      NULL },\n\n    { ngx_string(\"uwsgi_no_cache\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,\n      ngx_http_set_predicate_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.no_cache),\n      NULL },\n\n    { ngx_string(\"uwsgi_cache_valid\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,\n      ngx_http_file_cache_valid_set_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.cache_valid),\n      NULL },\n\n    { ngx_string(\"uwsgi_cache_min_uses\"),\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_uwsgi_loc_conf_t, upstream.cache_min_uses),\n      NULL },\n\n    { ngx_string(\"uwsgi_cache_max_range_offset\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_off_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.cache_max_range_offset),\n      NULL },\n\n    { ngx_string(\"uwsgi_cache_use_stale\"),\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_uwsgi_loc_conf_t, upstream.cache_use_stale),\n      &ngx_http_uwsgi_next_upstream_masks },\n\n    { ngx_string(\"uwsgi_cache_methods\"),\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_uwsgi_loc_conf_t, upstream.cache_methods),\n      &ngx_http_upstream_cache_method_mask },\n\n    { ngx_string(\"uwsgi_cache_lock\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.cache_lock),\n      NULL },\n\n    { ngx_string(\"uwsgi_cache_lock_timeout\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.cache_lock_timeout),\n      NULL },\n\n    { ngx_string(\"uwsgi_cache_lock_age\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.cache_lock_age),\n      NULL },\n\n    { ngx_string(\"uwsgi_cache_revalidate\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.cache_revalidate),\n      NULL },\n\n    { ngx_string(\"uwsgi_cache_background_update\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.cache_background_update),\n      NULL },\n\n#endif\n\n    { ngx_string(\"uwsgi_temp_path\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1234,\n      ngx_conf_set_path_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.temp_path),\n      NULL },\n\n    { ngx_string(\"uwsgi_max_temp_file_size\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.max_temp_file_size_conf),\n      NULL },\n\n    { ngx_string(\"uwsgi_temp_file_write_size\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.temp_file_write_size_conf),\n      NULL },\n\n    { ngx_string(\"uwsgi_next_upstream\"),\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_uwsgi_loc_conf_t, upstream.next_upstream),\n      &ngx_http_uwsgi_next_upstream_masks },\n\n    { ngx_string(\"uwsgi_next_upstream_tries\"),\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_uwsgi_loc_conf_t, upstream.next_upstream_tries),\n      NULL },\n\n    { ngx_string(\"uwsgi_next_upstream_timeout\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.next_upstream_timeout),\n      NULL },\n\n#if (T_UPSTREAM_TRIES)\n    { ngx_string(\"uwsgi_upstream_tries\"),\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_uwsgi_loc_conf_t, upstream.next_upstream_tries),\n      NULL },\n#endif\n\n    { ngx_string(\"uwsgi_param\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE23,\n      ngx_http_upstream_param_set_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_uwsgi_loc_conf_t, params_source),\n      NULL },\n\n    { ngx_string(\"uwsgi_string\"),\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_uwsgi_loc_conf_t, uwsgi_string),\n      NULL },\n\n    { ngx_string(\"uwsgi_pass_header\"),\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_uwsgi_loc_conf_t, upstream.pass_headers),\n      NULL },\n\n    { ngx_string(\"uwsgi_hide_header\"),\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_uwsgi_loc_conf_t, upstream.hide_headers),\n      NULL },\n\n    { ngx_string(\"uwsgi_ignore_headers\"),\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_uwsgi_loc_conf_t, upstream.ignore_headers),\n      &ngx_http_upstream_ignore_headers_masks },\n\n#if (NGX_HTTP_SSL)\n\n    { ngx_string(\"uwsgi_ssl_session_reuse\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.ssl_session_reuse),\n      NULL },\n\n    { ngx_string(\"uwsgi_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_uwsgi_loc_conf_t, ssl_protocols),\n      &ngx_http_uwsgi_ssl_protocols },\n\n    { ngx_string(\"uwsgi_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_uwsgi_loc_conf_t, ssl_ciphers),\n      NULL },\n\n    { ngx_string(\"uwsgi_ssl_name\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_http_set_complex_value_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.ssl_name),\n      NULL },\n\n    { ngx_string(\"uwsgi_ssl_server_name\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.ssl_server_name),\n      NULL },\n\n    { ngx_string(\"uwsgi_ssl_verify\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.ssl_verify),\n      NULL },\n\n    { ngx_string(\"uwsgi_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_uwsgi_loc_conf_t, ssl_verify_depth),\n      NULL },\n\n    { ngx_string(\"uwsgi_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_uwsgi_loc_conf_t, ssl_trusted_certificate),\n      NULL },\n\n    { ngx_string(\"uwsgi_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_uwsgi_loc_conf_t, ssl_crl),\n      NULL },\n\n    { ngx_string(\"uwsgi_ssl_certificate\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_http_set_complex_value_zero_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.ssl_certificate),\n      NULL },\n\n    { ngx_string(\"uwsgi_ssl_certificate_key\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_http_set_complex_value_zero_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.ssl_certificate_key),\n      NULL },\n\n    { ngx_string(\"uwsgi_ssl_password_file\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_http_uwsgi_ssl_password_file,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"uwsgi_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_uwsgi_loc_conf_t, ssl_conf_commands),\n      &ngx_http_uwsgi_ssl_conf_command_post },\n\n#if (T_NGX_SSL_NTLS)\n    { ngx_string(\"uwsgi_enable_ntls\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_http_set_complex_value_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_uwsgi_loc_conf_t, upstream.enable_ntls),\n      NULL },\n\n    { ngx_string(\"uwsgi_ssl_enc_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_uwsgi_loc_conf_t, upstream.enc_certificate),\n      NULL },\n\n    { ngx_string(\"uwsgi_ssl_enc_certificate_key\"),\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_uwsgi_loc_conf_t, upstream.enc_certificate_key),\n      NULL },\n\n    { ngx_string(\"uwsgi_ssl_sign_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_uwsgi_loc_conf_t, upstream.sign_certificate),\n      NULL },\n\n    { ngx_string(\"uwsgi_ssl_sign_certificate_key\"),\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_uwsgi_loc_conf_t, upstream.sign_certificate_key),\n      NULL },\n#endif\n#endif\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t ngx_http_uwsgi_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    NULL,                                  /* postconfiguration */\n\n    ngx_http_uwsgi_create_main_conf,       /* create main configuration */\n    NULL,                                  /* init main configuration */\n\n    NULL,                                  /* create server configuration */\n    NULL,                                  /* merge server configuration */\n\n    ngx_http_uwsgi_create_loc_conf,        /* create location configuration */\n    ngx_http_uwsgi_merge_loc_conf          /* merge location configuration */\n};\n\n\nngx_module_t ngx_http_uwsgi_module = {\n    NGX_MODULE_V1,\n    &ngx_http_uwsgi_module_ctx,            /* module context */\n    ngx_http_uwsgi_commands,               /* 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_str_t ngx_http_uwsgi_hide_headers[] = {\n    ngx_string(\"X-Accel-Expires\"),\n    ngx_string(\"X-Accel-Redirect\"),\n    ngx_string(\"X-Accel-Limit-Rate\"),\n    ngx_string(\"X-Accel-Buffering\"),\n    ngx_string(\"X-Accel-Charset\"),\n    ngx_null_string\n};\n\n\n#if (NGX_HTTP_CACHE)\n\nstatic ngx_keyval_t  ngx_http_uwsgi_cache_headers[] = {\n    { ngx_string(\"HTTP_IF_MODIFIED_SINCE\"),\n      ngx_string(\"$upstream_cache_last_modified\") },\n    { ngx_string(\"HTTP_IF_UNMODIFIED_SINCE\"), ngx_string(\"\") },\n    { ngx_string(\"HTTP_IF_NONE_MATCH\"), ngx_string(\"$upstream_cache_etag\") },\n    { ngx_string(\"HTTP_IF_MATCH\"), ngx_string(\"\") },\n    { ngx_string(\"HTTP_RANGE\"), ngx_string(\"\") },\n    { ngx_string(\"HTTP_IF_RANGE\"), ngx_string(\"\") },\n    { ngx_null_string, ngx_null_string }\n};\n\n#endif\n\n\nstatic ngx_path_init_t ngx_http_uwsgi_temp_path = {\n    ngx_string(NGX_HTTP_UWSGI_TEMP_PATH), { 1, 2, 0 }\n};\n\n\nstatic ngx_int_t\nngx_http_uwsgi_handler(ngx_http_request_t *r)\n{\n    ngx_int_t                    rc;\n    ngx_http_status_t           *status;\n    ngx_http_upstream_t         *u;\n    ngx_http_uwsgi_loc_conf_t   *uwcf;\n#if (NGX_HTTP_CACHE)\n    ngx_http_uwsgi_main_conf_t  *uwmcf;\n#endif\n\n    if (ngx_http_upstream_create(r) != NGX_OK) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    status = ngx_pcalloc(r->pool, sizeof(ngx_http_status_t));\n    if (status == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    ngx_http_set_ctx(r, status, ngx_http_uwsgi_module);\n\n    uwcf = ngx_http_get_module_loc_conf(r, ngx_http_uwsgi_module);\n\n    u = r->upstream;\n\n    if (uwcf->uwsgi_lengths == NULL) {\n\n#if (NGX_HTTP_SSL)\n        u->ssl = uwcf->ssl;\n\n        if (u->ssl) {\n            ngx_str_set(&u->schema, \"suwsgi://\");\n\n        } else {\n            ngx_str_set(&u->schema, \"uwsgi://\");\n        }\n#else\n        ngx_str_set(&u->schema, \"uwsgi://\");\n#endif\n\n    } else {\n        if (ngx_http_uwsgi_eval(r, uwcf) != NGX_OK) {\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n    }\n\n    u->output.tag = (ngx_buf_tag_t) &ngx_http_uwsgi_module;\n\n    u->conf = &uwcf->upstream;\n\n#if (NGX_HTTP_CACHE)\n    uwmcf = ngx_http_get_module_main_conf(r, ngx_http_uwsgi_module);\n\n    u->caches = &uwmcf->caches;\n    u->create_key = ngx_http_uwsgi_create_key;\n#endif\n\n    u->create_request = ngx_http_uwsgi_create_request;\n    u->reinit_request = ngx_http_uwsgi_reinit_request;\n    u->process_header = ngx_http_uwsgi_process_status_line;\n    u->abort_request = ngx_http_uwsgi_abort_request;\n    u->finalize_request = ngx_http_uwsgi_finalize_request;\n    r->state = 0;\n\n    u->buffering = uwcf->upstream.buffering;\n\n    u->pipe = ngx_pcalloc(r->pool, sizeof(ngx_event_pipe_t));\n    if (u->pipe == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    u->pipe->input_filter = ngx_event_pipe_copy_input_filter;\n    u->pipe->input_ctx = r;\n\n    u->input_filter_init = ngx_http_uwsgi_input_filter_init;\n    u->input_filter = ngx_http_upstream_non_buffered_filter;\n    u->input_filter_ctx = r;\n\n    if (!uwcf->upstream.request_buffering\n        && uwcf->upstream.pass_request_body\n        && !r->headers_in.chunked)\n    {\n        r->request_body_no_buffering = 1;\n    }\n\n    rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init);\n\n    if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {\n        return rc;\n    }\n\n    return NGX_DONE;\n}\n\n\nstatic ngx_int_t\nngx_http_uwsgi_eval(ngx_http_request_t *r, ngx_http_uwsgi_loc_conf_t * uwcf)\n{\n    size_t                add;\n    ngx_url_t             url;\n    ngx_http_upstream_t  *u;\n\n    ngx_memzero(&url, sizeof(ngx_url_t));\n\n    if (ngx_http_script_run(r, &url.url, uwcf->uwsgi_lengths->elts, 0,\n                            uwcf->uwsgi_values->elts)\n        == NULL)\n    {\n        return NGX_ERROR;\n    }\n\n    if (url.url.len > 8\n        && ngx_strncasecmp(url.url.data, (u_char *) \"uwsgi://\", 8) == 0)\n    {\n        add = 8;\n\n    } else if (url.url.len > 9\n               && ngx_strncasecmp(url.url.data, (u_char *) \"suwsgi://\", 9) == 0)\n    {\n\n#if (NGX_HTTP_SSL)\n        add = 9;\n        r->upstream->ssl = 1;\n#else\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"suwsgi protocol requires SSL support\");\n        return NGX_ERROR;\n#endif\n\n    } else {\n        add = 0;\n    }\n\n    u = r->upstream;\n\n    if (add) {\n        u->schema.len = add;\n        u->schema.data = url.url.data;\n\n        url.url.data += add;\n        url.url.len -= add;\n\n    } else {\n        ngx_str_set(&u->schema, \"uwsgi://\");\n    }\n\n    url.no_resolve = 1;\n\n    if (ngx_parse_url(r->pool, &url) != NGX_OK) {\n        if (url.err) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"%s in upstream \\\"%V\\\"\", url.err, &url.url);\n        }\n\n        return NGX_ERROR;\n    }\n\n    u->resolved = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_resolved_t));\n    if (u->resolved == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (url.addrs) {\n        u->resolved->sockaddr = url.addrs[0].sockaddr;\n        u->resolved->socklen = url.addrs[0].socklen;\n        u->resolved->name = url.addrs[0].name;\n        u->resolved->naddrs = 1;\n    }\n\n    u->resolved->host = url.host;\n    u->resolved->port = url.port;\n    u->resolved->no_port = url.no_port;\n\n    return NGX_OK;\n}\n\n\n#if (NGX_HTTP_CACHE)\n\nstatic ngx_int_t\nngx_http_uwsgi_create_key(ngx_http_request_t *r)\n{\n    ngx_str_t                  *key;\n    ngx_http_uwsgi_loc_conf_t  *uwcf;\n\n    key = ngx_array_push(&r->cache->keys);\n    if (key == NULL) {\n        return NGX_ERROR;\n    }\n\n    uwcf = ngx_http_get_module_loc_conf(r, ngx_http_uwsgi_module);\n\n    if (ngx_http_complex_value(r, &uwcf->cache_key, key) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n#endif\n\n\nstatic ngx_int_t\nngx_http_uwsgi_create_request(ngx_http_request_t *r)\n{\n    u_char                        ch, sep, *lowcase_key;\n    size_t                        key_len, val_len, len, allocated;\n    ngx_uint_t                    i, n, hash, skip_empty, header_params;\n    ngx_buf_t                    *b;\n    ngx_chain_t                  *cl, *body;\n    ngx_list_part_t              *part;\n    ngx_table_elt_t              *header, *hn, **ignored;\n    ngx_http_uwsgi_params_t      *params;\n    ngx_http_script_code_pt       code;\n    ngx_http_script_engine_t      e, le;\n    ngx_http_uwsgi_loc_conf_t    *uwcf;\n    ngx_http_script_len_code_pt   lcode;\n\n    len = 0;\n    header_params = 0;\n    ignored = NULL;\n\n    uwcf = ngx_http_get_module_loc_conf(r, ngx_http_uwsgi_module);\n\n#if (NGX_HTTP_CACHE)\n    params = r->upstream->cacheable ? &uwcf->params_cache : &uwcf->params;\n#else\n    params = &uwcf->params;\n#endif\n\n    if (params->lengths) {\n        ngx_memzero(&le, sizeof(ngx_http_script_engine_t));\n\n        ngx_http_script_flush_no_cacheable_variables(r, params->flushes);\n        le.flushed = 1;\n\n        le.ip = params->lengths->elts;\n        le.request = r;\n\n        while (*(uintptr_t *) le.ip) {\n\n            lcode = *(ngx_http_script_len_code_pt *) le.ip;\n            key_len = lcode(&le);\n\n            lcode = *(ngx_http_script_len_code_pt *) le.ip;\n            skip_empty = lcode(&le);\n\n            for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) {\n                lcode = *(ngx_http_script_len_code_pt *) le.ip;\n            }\n            le.ip += sizeof(uintptr_t);\n\n            if (skip_empty && val_len == 0) {\n                continue;\n            }\n\n            len += 2 + key_len + 2 + val_len;\n        }\n    }\n\n    if (uwcf->upstream.pass_request_headers) {\n\n        allocated = 0;\n        lowcase_key = NULL;\n\n        if (ngx_http_link_multi_headers(r) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        if (params->number || r->headers_in.multi) {\n            n = 0;\n            part = &r->headers_in.headers.part;\n\n            while (part) {\n                n += part->nelts;\n                part = part->next;\n            }\n\n            ignored = ngx_palloc(r->pool, n * sizeof(void *));\n            if (ignored == NULL) {\n                return NGX_ERROR;\n            }\n        }\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            for (n = 0; n < header_params; n++) {\n                if (&header[i] == ignored[n]) {\n                    goto next_length;\n                }\n            }\n\n            if (params->number) {\n                if (allocated < header[i].key.len) {\n                    allocated = header[i].key.len + 16;\n                    lowcase_key = ngx_pnalloc(r->pool, allocated);\n                    if (lowcase_key == NULL) {\n                        return NGX_ERROR;\n                    }\n                }\n\n                hash = 0;\n\n                for (n = 0; n < header[i].key.len; n++) {\n                    ch = header[i].key.data[n];\n\n                    if (ch >= 'A' && ch <= 'Z') {\n                        ch |= 0x20;\n\n                    } else if (ch == '-') {\n                        ch = '_';\n                    }\n\n                    hash = ngx_hash(hash, ch);\n                    lowcase_key[n] = ch;\n                }\n\n                if (ngx_hash_find(&params->hash, hash, lowcase_key, n)) {\n                    ignored[header_params++] = &header[i];\n                    continue;\n                }\n            }\n\n            len += 2 + sizeof(\"HTTP_\") - 1 + header[i].key.len\n                 + 2 + header[i].value.len;\n\n            for (hn = header[i].next; hn; hn = hn->next) {\n                len += hn->value.len + 2;\n                ignored[header_params++] = hn;\n            }\n\n        next_length:\n\n            continue;\n        }\n    }\n\n    len += uwcf->uwsgi_string.len;\n\n#if 0\n    /* allow custom uwsgi packet */\n    if (len > 0 && len < 2) {\n        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,\n                      \"uwsgi request is too little: %uz\", len);\n        return NGX_ERROR;\n    }\n#endif\n\n    if (len > 65535) {\n        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,\n                      \"uwsgi request is too big: %uz\", len);\n        return NGX_ERROR;\n    }\n\n    b = ngx_create_temp_buf(r->pool, len + 4);\n    if (b == NULL) {\n        return NGX_ERROR;\n    }\n\n    cl = ngx_alloc_chain_link(r->pool);\n    if (cl == NULL) {\n        return NGX_ERROR;\n    }\n\n    cl->buf = b;\n\n    *b->last++ = (u_char) uwcf->modifier1;\n    *b->last++ = (u_char) (len & 0xff);\n    *b->last++ = (u_char) ((len >> 8) & 0xff);\n    *b->last++ = (u_char) uwcf->modifier2;\n\n    if (params->lengths) {\n        ngx_memzero(&e, sizeof(ngx_http_script_engine_t));\n\n        e.ip = params->values->elts;\n        e.pos = b->last;\n        e.request = r;\n        e.flushed = 1;\n\n        le.ip = params->lengths->elts;\n\n        while (*(uintptr_t *) le.ip) {\n\n            lcode = *(ngx_http_script_len_code_pt *) le.ip;\n            key_len = (u_char) lcode(&le);\n\n            lcode = *(ngx_http_script_len_code_pt *) le.ip;\n            skip_empty = lcode(&le);\n\n            for (val_len = 0; *(uintptr_t *) le.ip; val_len += lcode(&le)) {\n                lcode = *(ngx_http_script_len_code_pt *) le.ip;\n            }\n            le.ip += sizeof(uintptr_t);\n\n            if (skip_empty && val_len == 0) {\n                e.skip = 1;\n\n                while (*(uintptr_t *) e.ip) {\n                    code = *(ngx_http_script_code_pt *) e.ip;\n                    code((ngx_http_script_engine_t *) &e);\n                }\n                e.ip += sizeof(uintptr_t);\n\n                e.skip = 0;\n\n                continue;\n            }\n\n            *e.pos++ = (u_char) (key_len & 0xff);\n            *e.pos++ = (u_char) ((key_len >> 8) & 0xff);\n\n            code = *(ngx_http_script_code_pt *) e.ip;\n            code((ngx_http_script_engine_t *) &e);\n\n            *e.pos++ = (u_char) (val_len & 0xff);\n            *e.pos++ = (u_char) ((val_len >> 8) & 0xff);\n\n            while (*(uintptr_t *) e.ip) {\n                code = *(ngx_http_script_code_pt *) e.ip;\n                code((ngx_http_script_engine_t *) &e);\n            }\n\n            e.ip += sizeof(uintptr_t);\n\n            ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"uwsgi param: \\\"%*s: %*s\\\"\",\n                           key_len, e.pos - (key_len + 2 + val_len),\n                           val_len, e.pos - val_len);\n        }\n\n        b->last = e.pos;\n    }\n\n    if (uwcf->upstream.pass_request_headers) {\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            for (n = 0; n < header_params; n++) {\n                if (&header[i] == ignored[n]) {\n                    goto next_value;\n                }\n            }\n\n            key_len = sizeof(\"HTTP_\") - 1 + header[i].key.len;\n            *b->last++ = (u_char) (key_len & 0xff);\n            *b->last++ = (u_char) ((key_len >> 8) & 0xff);\n\n            b->last = ngx_cpymem(b->last, \"HTTP_\", sizeof(\"HTTP_\") - 1);\n            for (n = 0; n < header[i].key.len; n++) {\n                ch = header[i].key.data[n];\n\n                if (ch >= 'a' && ch <= 'z') {\n                    ch &= ~0x20;\n\n                } else if (ch == '-') {\n                    ch = '_';\n                }\n\n                *b->last++ = ch;\n            }\n\n            val_len = header[i].value.len;\n\n            for (hn = header[i].next; hn; hn = hn->next) {\n                val_len += hn->value.len + 2;\n            }\n\n            *b->last++ = (u_char) (val_len & 0xff);\n            *b->last++ = (u_char) ((val_len >> 8) & 0xff);\n            b->last = ngx_copy(b->last, header[i].value.data,\n                               header[i].value.len);\n\n            if (header[i].next) {\n\n                if (header[i].key.len == sizeof(\"Cookie\") - 1\n                    && ngx_strncasecmp(header[i].key.data, (u_char *) \"Cookie\",\n                                       sizeof(\"Cookie\") - 1)\n                       == 0)\n                {\n                    sep = ';';\n\n                } else {\n                    sep = ',';\n                }\n\n                for (hn = header[i].next; hn; hn = hn->next) {\n                    *b->last++ = sep;\n                    *b->last++ = ' ';\n                    b->last = ngx_copy(b->last, hn->value.data, hn->value.len);\n                }\n            }\n\n            ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"uwsgi param: \\\"%*s: %*s\\\"\",\n                           key_len, b->last - (key_len + 2 + val_len),\n                           val_len, b->last - val_len);\n        next_value:\n\n            continue;\n        }\n    }\n\n    b->last = ngx_copy(b->last, uwcf->uwsgi_string.data,\n                       uwcf->uwsgi_string.len);\n\n    if (r->request_body_no_buffering) {\n        r->upstream->request_bufs = cl;\n\n    } else if (uwcf->upstream.pass_request_body) {\n        body = r->upstream->request_bufs;\n        r->upstream->request_bufs = cl;\n\n        while (body) {\n            b = ngx_alloc_buf(r->pool);\n            if (b == NULL) {\n                return NGX_ERROR;\n            }\n\n            ngx_memcpy(b, body->buf, sizeof(ngx_buf_t));\n\n            cl->next = ngx_alloc_chain_link(r->pool);\n            if (cl->next == NULL) {\n                return NGX_ERROR;\n            }\n\n            cl = cl->next;\n            cl->buf = b;\n\n            body = body->next;\n        }\n\n    } else {\n        r->upstream->request_bufs = cl;\n    }\n\n    b->flush = 1;\n    cl->next = NULL;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_uwsgi_reinit_request(ngx_http_request_t *r)\n{\n    ngx_http_status_t  *status;\n\n    status = ngx_http_get_module_ctx(r, ngx_http_uwsgi_module);\n\n    if (status == NULL) {\n        return NGX_OK;\n    }\n\n    status->code = 0;\n    status->count = 0;\n    status->start = NULL;\n    status->end = NULL;\n\n    r->upstream->process_header = ngx_http_uwsgi_process_status_line;\n    r->state = 0;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_uwsgi_process_status_line(ngx_http_request_t *r)\n{\n    size_t                 len;\n    ngx_int_t              rc;\n    ngx_http_status_t     *status;\n    ngx_http_upstream_t   *u;\n\n    status = ngx_http_get_module_ctx(r, ngx_http_uwsgi_module);\n\n    if (status == NULL) {\n        return NGX_ERROR;\n    }\n\n    u = r->upstream;\n\n    rc = ngx_http_parse_status_line(r, &u->buffer, status);\n\n    if (rc == NGX_AGAIN) {\n        return rc;\n    }\n\n    if (rc == NGX_ERROR) {\n        u->process_header = ngx_http_uwsgi_process_header;\n        return ngx_http_uwsgi_process_header(r);\n    }\n\n    if (u->state && u->state->status == 0) {\n        u->state->status = status->code;\n    }\n\n    u->headers_in.status_n = status->code;\n\n    len = status->end - status->start;\n    u->headers_in.status_line.len = len;\n\n    u->headers_in.status_line.data = ngx_pnalloc(r->pool, len);\n    if (u->headers_in.status_line.data == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_memcpy(u->headers_in.status_line.data, status->start, len);\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http uwsgi status %ui \\\"%V\\\"\",\n                   u->headers_in.status_n, &u->headers_in.status_line);\n\n    u->process_header = ngx_http_uwsgi_process_header;\n\n    return ngx_http_uwsgi_process_header(r);\n}\n\n\nstatic ngx_int_t\nngx_http_uwsgi_process_header(ngx_http_request_t *r)\n{\n    ngx_str_t                      *status_line;\n    ngx_int_t                       rc, status;\n    ngx_table_elt_t                *h;\n    ngx_http_upstream_t            *u;\n    ngx_http_upstream_header_t     *hh;\n    ngx_http_upstream_main_conf_t  *umcf;\n\n    umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);\n\n    for ( ;; ) {\n\n        rc = ngx_http_parse_header_line(r, &r->upstream->buffer, 1);\n\n        if (rc == NGX_OK) {\n\n            /* a header line has been parsed successfully */\n\n            h = ngx_list_push(&r->upstream->headers_in.headers);\n            if (h == NULL) {\n                return NGX_ERROR;\n            }\n\n            h->hash = r->header_hash;\n\n            h->key.len = r->header_name_end - r->header_name_start;\n            h->value.len = r->header_end - r->header_start;\n\n            h->key.data = ngx_pnalloc(r->pool,\n                                      h->key.len + 1 + h->value.len + 1\n                                      + h->key.len);\n            if (h->key.data == NULL) {\n                h->hash = 0;\n                return NGX_ERROR;\n            }\n\n            h->value.data = h->key.data + h->key.len + 1;\n            h->lowcase_key = h->key.data + h->key.len + 1 + h->value.len + 1;\n\n            ngx_memcpy(h->key.data, r->header_name_start, h->key.len);\n            h->key.data[h->key.len] = '\\0';\n            ngx_memcpy(h->value.data, r->header_start, h->value.len);\n            h->value.data[h->value.len] = '\\0';\n\n            if (h->key.len == r->lowcase_index) {\n                ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len);\n\n            } else {\n                ngx_strlow(h->lowcase_key, h->key.data, h->key.len);\n            }\n\n            hh = ngx_hash_find(&umcf->headers_in_hash, h->hash,\n                               h->lowcase_key, h->key.len);\n\n            if (hh) {\n                rc = hh->handler(r, h, hh->offset);\n\n                if (rc != NGX_OK) {\n                    return rc;\n                }\n            }\n\n            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"http uwsgi header: \\\"%V: %V\\\"\", &h->key, &h->value);\n\n            continue;\n        }\n\n        if (rc == NGX_HTTP_PARSE_HEADER_DONE) {\n\n            /* a whole header has been parsed successfully */\n\n            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"http uwsgi header done\");\n\n            u = r->upstream;\n\n            if (u->headers_in.status_n) {\n                goto done;\n            }\n\n            if (u->headers_in.status) {\n                status_line = &u->headers_in.status->value;\n\n                status = ngx_atoi(status_line->data, 3);\n                if (status == NGX_ERROR) {\n                    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                                  \"upstream sent invalid status \\\"%V\\\"\",\n                                  status_line);\n                    return NGX_HTTP_UPSTREAM_INVALID_HEADER;\n                }\n\n                u->headers_in.status_n = status;\n                u->headers_in.status_line = *status_line;\n\n            } else if (u->headers_in.location) {\n                u->headers_in.status_n = 302;\n                ngx_str_set(&u->headers_in.status_line,\n                            \"302 Moved Temporarily\");\n\n            } else {\n                u->headers_in.status_n = 200;\n                ngx_str_set(&u->headers_in.status_line, \"200 OK\");\n            }\n\n            if (u->state && u->state->status == 0) {\n                u->state->status = u->headers_in.status_n;\n            }\n\n        done:\n\n            if (u->headers_in.status_n == NGX_HTTP_SWITCHING_PROTOCOLS\n                && r->headers_in.upgrade)\n            {\n                u->upgrade = 1;\n            }\n\n            return NGX_OK;\n        }\n\n        if (rc == NGX_AGAIN) {\n            return NGX_AGAIN;\n        }\n\n        /* rc == NGX_HTTP_PARSE_INVALID_HEADER */\n\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"upstream sent invalid header: \\\"%*s\\\\x%02xd...\\\"\",\n                      r->header_end - r->header_name_start,\n                      r->header_name_start, *r->header_end);\n\n        return NGX_HTTP_UPSTREAM_INVALID_HEADER;\n    }\n}\n\n\nstatic ngx_int_t\nngx_http_uwsgi_input_filter_init(void *data)\n{\n    ngx_http_request_t   *r = data;\n    ngx_http_upstream_t  *u;\n\n    u = r->upstream;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http uwsgi filter init s:%ui l:%O\",\n                   u->headers_in.status_n, u->headers_in.content_length_n);\n\n    if (u->headers_in.status_n == NGX_HTTP_NO_CONTENT\n        || u->headers_in.status_n == NGX_HTTP_NOT_MODIFIED)\n    {\n        u->pipe->length = 0;\n        u->length = 0;\n\n    } else if (r->method == NGX_HTTP_HEAD) {\n        u->pipe->length = -1;\n        u->length = -1;\n\n    } else {\n        u->pipe->length = u->headers_in.content_length_n;\n        u->length = u->headers_in.content_length_n;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_uwsgi_abort_request(ngx_http_request_t *r)\n{\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"abort http uwsgi request\");\n\n    return;\n}\n\n\nstatic void\nngx_http_uwsgi_finalize_request(ngx_http_request_t *r, ngx_int_t rc)\n{\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"finalize http uwsgi request\");\n\n    return;\n}\n\n\nstatic void *\nngx_http_uwsgi_create_main_conf(ngx_conf_t *cf)\n{\n    ngx_http_uwsgi_main_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_uwsgi_main_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n#if (NGX_HTTP_CACHE)\n    if (ngx_array_init(&conf->caches, cf->pool, 4,\n                       sizeof(ngx_http_file_cache_t *))\n        != NGX_OK)\n    {\n        return NULL;\n    }\n#endif\n\n    return conf;\n}\n\n\nstatic void *\nngx_http_uwsgi_create_loc_conf(ngx_conf_t *cf)\n{\n    ngx_http_uwsgi_loc_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_uwsgi_loc_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    conf->modifier1 = NGX_CONF_UNSET_UINT;\n    conf->modifier2 = NGX_CONF_UNSET_UINT;\n\n    conf->upstream.store = NGX_CONF_UNSET;\n    conf->upstream.store_access = NGX_CONF_UNSET_UINT;\n    conf->upstream.next_upstream_tries = NGX_CONF_UNSET_UINT;\n    conf->upstream.buffering = NGX_CONF_UNSET;\n    conf->upstream.request_buffering = NGX_CONF_UNSET;\n    conf->upstream.ignore_client_abort = NGX_CONF_UNSET;\n    conf->upstream.force_ranges = NGX_CONF_UNSET;\n\n    conf->upstream.local = NGX_CONF_UNSET_PTR;\n    conf->upstream.socket_keepalive = NGX_CONF_UNSET;\n\n    conf->upstream.connect_timeout = NGX_CONF_UNSET_MSEC;\n    conf->upstream.send_timeout = NGX_CONF_UNSET_MSEC;\n    conf->upstream.read_timeout = NGX_CONF_UNSET_MSEC;\n    conf->upstream.next_upstream_timeout = NGX_CONF_UNSET_MSEC;\n\n    conf->upstream.send_lowat = NGX_CONF_UNSET_SIZE;\n    conf->upstream.buffer_size = NGX_CONF_UNSET_SIZE;\n    conf->upstream.limit_rate = NGX_CONF_UNSET_SIZE;\n\n    conf->upstream.busy_buffers_size_conf = NGX_CONF_UNSET_SIZE;\n    conf->upstream.max_temp_file_size_conf = NGX_CONF_UNSET_SIZE;\n    conf->upstream.temp_file_write_size_conf = NGX_CONF_UNSET_SIZE;\n\n    conf->upstream.pass_request_headers = NGX_CONF_UNSET;\n    conf->upstream.pass_request_body = NGX_CONF_UNSET;\n\n#if (NGX_HTTP_CACHE)\n    conf->upstream.cache = NGX_CONF_UNSET;\n    conf->upstream.cache_min_uses = NGX_CONF_UNSET_UINT;\n    conf->upstream.cache_max_range_offset = NGX_CONF_UNSET;\n    conf->upstream.cache_bypass = NGX_CONF_UNSET_PTR;\n    conf->upstream.no_cache = NGX_CONF_UNSET_PTR;\n    conf->upstream.cache_valid = NGX_CONF_UNSET_PTR;\n    conf->upstream.cache_lock = NGX_CONF_UNSET;\n    conf->upstream.cache_lock_timeout = NGX_CONF_UNSET_MSEC;\n    conf->upstream.cache_lock_age = NGX_CONF_UNSET_MSEC;\n    conf->upstream.cache_revalidate = NGX_CONF_UNSET;\n    conf->upstream.cache_background_update = NGX_CONF_UNSET;\n#endif\n\n    conf->upstream.hide_headers = NGX_CONF_UNSET_PTR;\n    conf->upstream.pass_headers = NGX_CONF_UNSET_PTR;\n\n    conf->upstream.intercept_errors = NGX_CONF_UNSET;\n\n#if (NGX_HTTP_SSL)\n    conf->upstream.ssl_session_reuse = NGX_CONF_UNSET;\n    conf->upstream.ssl_name = NGX_CONF_UNSET_PTR;\n    conf->upstream.ssl_server_name = NGX_CONF_UNSET;\n    conf->upstream.ssl_verify = NGX_CONF_UNSET;\n    conf->ssl_verify_depth = NGX_CONF_UNSET_UINT;\n    conf->upstream.ssl_certificate = NGX_CONF_UNSET_PTR;\n    conf->upstream.ssl_certificate_key = NGX_CONF_UNSET_PTR;\n    conf->upstream.ssl_passwords = NGX_CONF_UNSET_PTR;\n    conf->ssl_conf_commands = NGX_CONF_UNSET_PTR;\n\n#if (T_NGX_SSL_NTLS)\n    conf->upstream.tls_method = NULL;\n    conf->upstream.enable_ntls = NULL;\n#endif\n#endif\n\n    /* \"uwsgi_cyclic_temp_file\" is disabled */\n    conf->upstream.cyclic_temp_file = 0;\n\n    conf->upstream.change_buffering = 1;\n\n    ngx_str_set(&conf->upstream.module, \"uwsgi\");\n\n    return conf;\n}\n\n\nstatic char *\nngx_http_uwsgi_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_http_uwsgi_loc_conf_t *prev = parent;\n    ngx_http_uwsgi_loc_conf_t *conf = child;\n\n    size_t                        size;\n    ngx_int_t                     rc;\n    ngx_hash_init_t               hash;\n    ngx_http_core_loc_conf_t     *clcf;\n\n#if (NGX_HTTP_CACHE)\n\n    if (conf->upstream.store > 0) {\n        conf->upstream.cache = 0;\n    }\n\n    if (conf->upstream.cache > 0) {\n        conf->upstream.store = 0;\n    }\n\n#endif\n\n    if (conf->upstream.store == NGX_CONF_UNSET) {\n        ngx_conf_merge_value(conf->upstream.store, prev->upstream.store, 0);\n\n        conf->upstream.store_lengths = prev->upstream.store_lengths;\n        conf->upstream.store_values = prev->upstream.store_values;\n    }\n\n    ngx_conf_merge_uint_value(conf->upstream.store_access,\n                              prev->upstream.store_access, 0600);\n\n    ngx_conf_merge_uint_value(conf->upstream.next_upstream_tries,\n                              prev->upstream.next_upstream_tries, 0);\n\n    ngx_conf_merge_value(conf->upstream.buffering,\n                              prev->upstream.buffering, 1);\n\n    ngx_conf_merge_value(conf->upstream.request_buffering,\n                              prev->upstream.request_buffering, 1);\n\n    ngx_conf_merge_value(conf->upstream.ignore_client_abort,\n                              prev->upstream.ignore_client_abort, 0);\n\n    ngx_conf_merge_value(conf->upstream.force_ranges,\n                              prev->upstream.force_ranges, 0);\n\n    ngx_conf_merge_ptr_value(conf->upstream.local,\n                              prev->upstream.local, NULL);\n\n    ngx_conf_merge_value(conf->upstream.socket_keepalive,\n                              prev->upstream.socket_keepalive, 0);\n\n    ngx_conf_merge_msec_value(conf->upstream.connect_timeout,\n                              prev->upstream.connect_timeout, 60000);\n\n    ngx_conf_merge_msec_value(conf->upstream.send_timeout,\n                              prev->upstream.send_timeout, 60000);\n\n    ngx_conf_merge_msec_value(conf->upstream.read_timeout,\n                              prev->upstream.read_timeout, 60000);\n\n    ngx_conf_merge_msec_value(conf->upstream.next_upstream_timeout,\n                              prev->upstream.next_upstream_timeout, 0);\n\n    ngx_conf_merge_size_value(conf->upstream.send_lowat,\n                              prev->upstream.send_lowat, 0);\n\n    ngx_conf_merge_size_value(conf->upstream.buffer_size,\n                              prev->upstream.buffer_size,\n                              (size_t) ngx_pagesize);\n\n    ngx_conf_merge_size_value(conf->upstream.limit_rate,\n                              prev->upstream.limit_rate, 0);\n\n\n    ngx_conf_merge_bufs_value(conf->upstream.bufs, prev->upstream.bufs,\n                              8, ngx_pagesize);\n\n    if (conf->upstream.bufs.num < 2) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"there must be at least 2 \\\"uwsgi_buffers\\\"\");\n        return NGX_CONF_ERROR;\n    }\n\n\n    size = conf->upstream.buffer_size;\n    if (size < conf->upstream.bufs.size) {\n        size = conf->upstream.bufs.size;\n    }\n\n\n    ngx_conf_merge_size_value(conf->upstream.busy_buffers_size_conf,\n                              prev->upstream.busy_buffers_size_conf,\n                              NGX_CONF_UNSET_SIZE);\n\n    if (conf->upstream.busy_buffers_size_conf == NGX_CONF_UNSET_SIZE) {\n        conf->upstream.busy_buffers_size = 2 * size;\n    } else {\n        conf->upstream.busy_buffers_size =\n            conf->upstream.busy_buffers_size_conf;\n    }\n\n    if (conf->upstream.busy_buffers_size < size) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n            \"\\\"uwsgi_busy_buffers_size\\\" must be equal to or greater \"\n            \"than the maximum of the value of \\\"uwsgi_buffer_size\\\" and \"\n            \"one of the \\\"uwsgi_buffers\\\"\");\n\n        return NGX_CONF_ERROR;\n    }\n\n    if (conf->upstream.busy_buffers_size\n        > (conf->upstream.bufs.num - 1) * conf->upstream.bufs.size)\n    {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n            \"\\\"uwsgi_busy_buffers_size\\\" must be less than \"\n            \"the size of all \\\"uwsgi_buffers\\\" minus one buffer\");\n\n        return NGX_CONF_ERROR;\n    }\n\n\n    ngx_conf_merge_size_value(conf->upstream.temp_file_write_size_conf,\n                              prev->upstream.temp_file_write_size_conf,\n                              NGX_CONF_UNSET_SIZE);\n\n    if (conf->upstream.temp_file_write_size_conf == NGX_CONF_UNSET_SIZE) {\n        conf->upstream.temp_file_write_size = 2 * size;\n    } else {\n        conf->upstream.temp_file_write_size =\n            conf->upstream.temp_file_write_size_conf;\n    }\n\n    if (conf->upstream.temp_file_write_size < size) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n            \"\\\"uwsgi_temp_file_write_size\\\" must be equal to or greater than \"\n            \"the maximum of the value of \\\"uwsgi_buffer_size\\\" and \"\n            \"one of the \\\"uwsgi_buffers\\\"\");\n\n        return NGX_CONF_ERROR;\n    }\n\n\n    ngx_conf_merge_size_value(conf->upstream.max_temp_file_size_conf,\n                              prev->upstream.max_temp_file_size_conf,\n                              NGX_CONF_UNSET_SIZE);\n\n    if (conf->upstream.max_temp_file_size_conf == NGX_CONF_UNSET_SIZE) {\n        conf->upstream.max_temp_file_size = 1024 * 1024 * 1024;\n    } else {\n        conf->upstream.max_temp_file_size =\n            conf->upstream.max_temp_file_size_conf;\n    }\n\n    if (conf->upstream.max_temp_file_size != 0\n        && conf->upstream.max_temp_file_size < size)\n    {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n            \"\\\"uwsgi_max_temp_file_size\\\" must be equal to zero to disable \"\n            \"temporary files usage or must be equal to or greater than \"\n            \"the maximum of the value of \\\"uwsgi_buffer_size\\\" and \"\n            \"one of the \\\"uwsgi_buffers\\\"\");\n\n        return NGX_CONF_ERROR;\n    }\n\n\n    ngx_conf_merge_bitmask_value(conf->upstream.ignore_headers,\n                                 prev->upstream.ignore_headers,\n                                 NGX_CONF_BITMASK_SET);\n\n\n    ngx_conf_merge_bitmask_value(conf->upstream.next_upstream,\n                                 prev->upstream.next_upstream,\n                                 (NGX_CONF_BITMASK_SET\n                                  |NGX_HTTP_UPSTREAM_FT_ERROR\n                                  |NGX_HTTP_UPSTREAM_FT_TIMEOUT));\n\n    if (conf->upstream.next_upstream & NGX_HTTP_UPSTREAM_FT_OFF) {\n        conf->upstream.next_upstream = NGX_CONF_BITMASK_SET\n                                       |NGX_HTTP_UPSTREAM_FT_OFF;\n    }\n\n    if (ngx_conf_merge_path_value(cf, &conf->upstream.temp_path,\n                                  prev->upstream.temp_path,\n                                  &ngx_http_uwsgi_temp_path)\n        != NGX_OK)\n    {\n        return NGX_CONF_ERROR;\n    }\n\n#if (NGX_HTTP_CACHE)\n\n    if (conf->upstream.cache == NGX_CONF_UNSET) {\n        ngx_conf_merge_value(conf->upstream.cache,\n                              prev->upstream.cache, 0);\n\n        conf->upstream.cache_zone = prev->upstream.cache_zone;\n        conf->upstream.cache_value = prev->upstream.cache_value;\n    }\n\n    if (conf->upstream.cache_zone && conf->upstream.cache_zone->data == NULL) {\n        ngx_shm_zone_t  *shm_zone;\n\n        shm_zone = conf->upstream.cache_zone;\n\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"\\\"uwsgi_cache\\\" zone \\\"%V\\\" is unknown\",\n                           &shm_zone->shm.name);\n\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_conf_merge_uint_value(conf->upstream.cache_min_uses,\n                              prev->upstream.cache_min_uses, 1);\n\n    ngx_conf_merge_off_value(conf->upstream.cache_max_range_offset,\n                              prev->upstream.cache_max_range_offset,\n                              NGX_MAX_OFF_T_VALUE);\n\n    ngx_conf_merge_bitmask_value(conf->upstream.cache_use_stale,\n                              prev->upstream.cache_use_stale,\n                              (NGX_CONF_BITMASK_SET\n                               |NGX_HTTP_UPSTREAM_FT_OFF));\n\n    if (conf->upstream.cache_use_stale & NGX_HTTP_UPSTREAM_FT_OFF) {\n        conf->upstream.cache_use_stale = NGX_CONF_BITMASK_SET\n                                         |NGX_HTTP_UPSTREAM_FT_OFF;\n    }\n\n    if (conf->upstream.cache_use_stale & NGX_HTTP_UPSTREAM_FT_ERROR) {\n        conf->upstream.cache_use_stale |= NGX_HTTP_UPSTREAM_FT_NOLIVE;\n    }\n\n    if (conf->upstream.cache_methods == 0) {\n        conf->upstream.cache_methods = prev->upstream.cache_methods;\n    }\n\n    conf->upstream.cache_methods |= NGX_HTTP_GET|NGX_HTTP_HEAD;\n\n    ngx_conf_merge_ptr_value(conf->upstream.cache_bypass,\n                             prev->upstream.cache_bypass, NULL);\n\n    ngx_conf_merge_ptr_value(conf->upstream.no_cache,\n                             prev->upstream.no_cache, NULL);\n\n    ngx_conf_merge_ptr_value(conf->upstream.cache_valid,\n                             prev->upstream.cache_valid, NULL);\n\n    if (conf->cache_key.value.data == NULL) {\n        conf->cache_key = prev->cache_key;\n    }\n\n    if (conf->upstream.cache && conf->cache_key.value.data == NULL) {\n        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                           \"no \\\"uwsgi_cache_key\\\" for \\\"uwsgi_cache\\\"\");\n    }\n\n    ngx_conf_merge_value(conf->upstream.cache_lock,\n                              prev->upstream.cache_lock, 0);\n\n    ngx_conf_merge_msec_value(conf->upstream.cache_lock_timeout,\n                              prev->upstream.cache_lock_timeout, 5000);\n\n    ngx_conf_merge_msec_value(conf->upstream.cache_lock_age,\n                              prev->upstream.cache_lock_age, 5000);\n\n    ngx_conf_merge_value(conf->upstream.cache_revalidate,\n                              prev->upstream.cache_revalidate, 0);\n\n    ngx_conf_merge_value(conf->upstream.cache_background_update,\n                              prev->upstream.cache_background_update, 0);\n\n#endif\n\n    ngx_conf_merge_value(conf->upstream.pass_request_headers,\n                         prev->upstream.pass_request_headers, 1);\n    ngx_conf_merge_value(conf->upstream.pass_request_body,\n                         prev->upstream.pass_request_body, 1);\n\n    ngx_conf_merge_value(conf->upstream.intercept_errors,\n                         prev->upstream.intercept_errors, 0);\n\n#if (NGX_HTTP_SSL)\n\n    if (ngx_http_uwsgi_merge_ssl(cf, conf, prev) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_conf_merge_value(conf->upstream.ssl_session_reuse,\n                              prev->upstream.ssl_session_reuse, 1);\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|NGX_SSL_TLSv1_3));\n\n    ngx_conf_merge_str_value(conf->ssl_ciphers, prev->ssl_ciphers,\n                             \"DEFAULT\");\n\n    ngx_conf_merge_ptr_value(conf->upstream.ssl_name,\n                              prev->upstream.ssl_name, NULL);\n    ngx_conf_merge_value(conf->upstream.ssl_server_name,\n                              prev->upstream.ssl_server_name, 0);\n    ngx_conf_merge_value(conf->upstream.ssl_verify,\n                              prev->upstream.ssl_verify, 0);\n    ngx_conf_merge_uint_value(conf->ssl_verify_depth,\n                              prev->ssl_verify_depth, 1);\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\n    ngx_conf_merge_ptr_value(conf->upstream.ssl_certificate,\n                              prev->upstream.ssl_certificate, NULL);\n    ngx_conf_merge_ptr_value(conf->upstream.ssl_certificate_key,\n                              prev->upstream.ssl_certificate_key, NULL);\n    ngx_conf_merge_ptr_value(conf->upstream.ssl_passwords,\n                              prev->upstream.ssl_passwords, NULL);\n\n    ngx_conf_merge_ptr_value(conf->ssl_conf_commands,\n                              prev->ssl_conf_commands, NULL);\n\n    if (conf->ssl && ngx_http_uwsgi_set_ssl(cf, conf) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n#if (T_NGX_SSL_NTLS)\n    if (conf->upstream.enable_ntls == NULL) {\n        conf->upstream.enable_ntls = prev->upstream.enable_ntls;\n    }\n    ngx_conf_merge_str_value(conf->upstream.enc_certificate,\n                             prev->upstream.enc_certificate, \"\");\n    ngx_conf_merge_str_value(conf->upstream.enc_certificate_key,\n                             prev->upstream.enc_certificate_key, \"\");\n    ngx_conf_merge_str_value(conf->upstream.sign_certificate,\n                             prev->upstream.sign_certificate, \"\");\n    ngx_conf_merge_str_value(conf->upstream.sign_certificate_key,\n                             prev->upstream.sign_certificate_key, \"\");\n    conf->upstream.ssl_ciphers = conf->ssl_ciphers;\n#endif\n#endif\n\n    ngx_conf_merge_str_value(conf->uwsgi_string, prev->uwsgi_string, \"\");\n\n    hash.max_size = 512;\n    hash.bucket_size = ngx_align(64, ngx_cacheline_size);\n    hash.name = \"uwsgi_hide_headers_hash\";\n\n    if (ngx_http_upstream_hide_headers_hash(cf, &conf->upstream,\n            &prev->upstream, ngx_http_uwsgi_hide_headers, &hash)\n        != NGX_OK)\n    {\n        return NGX_CONF_ERROR;\n    }\n\n    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);\n\n    if (clcf->noname\n        && conf->upstream.upstream == NULL && conf->uwsgi_lengths == NULL)\n    {\n        conf->upstream.upstream = prev->upstream.upstream;\n\n        conf->uwsgi_lengths = prev->uwsgi_lengths;\n        conf->uwsgi_values = prev->uwsgi_values;\n\n#if (NGX_HTTP_SSL)\n        conf->ssl = prev->ssl;\n#endif\n    }\n\n    if (clcf->lmt_excpt && clcf->handler == NULL\n        && (conf->upstream.upstream || conf->uwsgi_lengths))\n    {\n        clcf->handler = ngx_http_uwsgi_handler;\n    }\n\n    ngx_conf_merge_uint_value(conf->modifier1, prev->modifier1, 0);\n    ngx_conf_merge_uint_value(conf->modifier2, prev->modifier2, 0);\n\n    if (conf->params_source == NULL) {\n        conf->params = prev->params;\n#if (NGX_HTTP_CACHE)\n        conf->params_cache = prev->params_cache;\n#endif\n        conf->params_source = prev->params_source;\n    }\n\n    rc = ngx_http_uwsgi_init_params(cf, conf, &conf->params, NULL);\n    if (rc != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n#if (NGX_HTTP_CACHE)\n\n    if (conf->upstream.cache) {\n        rc = ngx_http_uwsgi_init_params(cf, conf, &conf->params_cache,\n                                        ngx_http_uwsgi_cache_headers);\n        if (rc != NGX_OK) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n#endif\n\n    /*\n     * special handling to preserve conf->params in the \"http\" section\n     * to inherit it to all servers\n     */\n\n    if (prev->params.hash.buckets == NULL\n        && conf->params_source == prev->params_source)\n    {\n        prev->params = conf->params;\n#if (NGX_HTTP_CACHE)\n        prev->params_cache = conf->params_cache;\n#endif\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_uwsgi_init_params(ngx_conf_t *cf, ngx_http_uwsgi_loc_conf_t *conf,\n    ngx_http_uwsgi_params_t *params, ngx_keyval_t *default_params)\n{\n    u_char                       *p;\n    size_t                        size;\n    uintptr_t                    *code;\n    ngx_uint_t                    i, nsrc;\n    ngx_array_t                   headers_names, params_merged;\n    ngx_keyval_t                 *h;\n    ngx_hash_key_t               *hk;\n    ngx_hash_init_t               hash;\n    ngx_http_upstream_param_t    *src, *s;\n    ngx_http_script_compile_t     sc;\n    ngx_http_script_copy_code_t  *copy;\n\n    if (params->hash.buckets) {\n        return NGX_OK;\n    }\n\n    if (conf->params_source == NULL && default_params == NULL) {\n        params->hash.buckets = (void *) 1;\n        return NGX_OK;\n    }\n\n    params->lengths = ngx_array_create(cf->pool, 64, 1);\n    if (params->lengths == NULL) {\n        return NGX_ERROR;\n    }\n\n    params->values = ngx_array_create(cf->pool, 512, 1);\n    if (params->values == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (ngx_array_init(&headers_names, cf->temp_pool, 4, sizeof(ngx_hash_key_t))\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    if (conf->params_source) {\n        src = conf->params_source->elts;\n        nsrc = conf->params_source->nelts;\n\n    } else {\n        src = NULL;\n        nsrc = 0;\n    }\n\n    if (default_params) {\n        if (ngx_array_init(&params_merged, cf->temp_pool, 4,\n                           sizeof(ngx_http_upstream_param_t))\n            != NGX_OK)\n        {\n            return NGX_ERROR;\n        }\n\n        for (i = 0; i < nsrc; i++) {\n\n            s = ngx_array_push(&params_merged);\n            if (s == NULL) {\n                return NGX_ERROR;\n            }\n\n            *s = src[i];\n        }\n\n        h = default_params;\n\n        while (h->key.len) {\n\n            src = params_merged.elts;\n            nsrc = params_merged.nelts;\n\n            for (i = 0; i < nsrc; i++) {\n                if (ngx_strcasecmp(h->key.data, src[i].key.data) == 0) {\n                    goto next;\n                }\n            }\n\n            s = ngx_array_push(&params_merged);\n            if (s == NULL) {\n                return NGX_ERROR;\n            }\n\n            s->key = h->key;\n            s->value = h->value;\n            s->skip_empty = 1;\n\n        next:\n\n            h++;\n        }\n\n        src = params_merged.elts;\n        nsrc = params_merged.nelts;\n    }\n\n    for (i = 0; i < nsrc; i++) {\n\n        if (src[i].key.len > sizeof(\"HTTP_\") - 1\n            && ngx_strncmp(src[i].key.data, \"HTTP_\", sizeof(\"HTTP_\") - 1) == 0)\n        {\n            hk = ngx_array_push(&headers_names);\n            if (hk == NULL) {\n                return NGX_ERROR;\n            }\n\n            hk->key.len = src[i].key.len - 5;\n            hk->key.data = src[i].key.data + 5;\n            hk->key_hash = ngx_hash_key_lc(hk->key.data, hk->key.len);\n            hk->value = (void *) 1;\n\n            if (src[i].value.len == 0) {\n                continue;\n            }\n        }\n\n        copy = ngx_array_push_n(params->lengths,\n                                sizeof(ngx_http_script_copy_code_t));\n        if (copy == NULL) {\n            return NGX_ERROR;\n        }\n\n        copy->code = (ngx_http_script_code_pt) (void *)\n                                                 ngx_http_script_copy_len_code;\n        copy->len = src[i].key.len;\n\n        copy = ngx_array_push_n(params->lengths,\n                                sizeof(ngx_http_script_copy_code_t));\n        if (copy == NULL) {\n            return NGX_ERROR;\n        }\n\n        copy->code = (ngx_http_script_code_pt) (void *)\n                                                 ngx_http_script_copy_len_code;\n        copy->len = src[i].skip_empty;\n\n\n        size = (sizeof(ngx_http_script_copy_code_t)\n                + src[i].key.len + sizeof(uintptr_t) - 1)\n               & ~(sizeof(uintptr_t) - 1);\n\n        copy = ngx_array_push_n(params->values, size);\n        if (copy == NULL) {\n            return NGX_ERROR;\n        }\n\n        copy->code = ngx_http_script_copy_code;\n        copy->len = src[i].key.len;\n\n        p = (u_char *) copy + sizeof(ngx_http_script_copy_code_t);\n        ngx_memcpy(p, src[i].key.data, src[i].key.len);\n\n\n        ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));\n\n        sc.cf = cf;\n        sc.source = &src[i].value;\n        sc.flushes = &params->flushes;\n        sc.lengths = &params->lengths;\n        sc.values = &params->values;\n\n        if (ngx_http_script_compile(&sc) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        code = ngx_array_push_n(params->lengths, sizeof(uintptr_t));\n        if (code == NULL) {\n            return NGX_ERROR;\n        }\n\n        *code = (uintptr_t) NULL;\n\n\n        code = ngx_array_push_n(params->values, sizeof(uintptr_t));\n        if (code == NULL) {\n            return NGX_ERROR;\n        }\n\n        *code = (uintptr_t) NULL;\n    }\n\n    code = ngx_array_push_n(params->lengths, sizeof(uintptr_t));\n    if (code == NULL) {\n        return NGX_ERROR;\n    }\n\n    *code = (uintptr_t) NULL;\n\n    params->number = headers_names.nelts;\n\n    hash.hash = &params->hash;\n    hash.key = ngx_hash_key_lc;\n    hash.max_size = 512;\n    hash.bucket_size = 64;\n    hash.name = \"uwsgi_params_hash\";\n    hash.pool = cf->pool;\n    hash.temp_pool = NULL;\n\n    return ngx_hash_init(&hash, headers_names.elts, headers_names.nelts);\n}\n\n\nstatic char *\nngx_http_uwsgi_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_uwsgi_loc_conf_t *uwcf = conf;\n\n    size_t                      add;\n    ngx_url_t                   u;\n    ngx_str_t                  *value, *url;\n    ngx_uint_t                  n;\n    ngx_http_core_loc_conf_t   *clcf;\n    ngx_http_script_compile_t   sc;\n\n    if (uwcf->upstream.upstream || uwcf->uwsgi_lengths) {\n        return \"is duplicate\";\n    }\n\n    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);\n    clcf->handler = ngx_http_uwsgi_handler;\n\n    value = cf->args->elts;\n\n    url = &value[1];\n\n    n = ngx_http_script_variables_count(url);\n\n    if (n) {\n\n        ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));\n\n        sc.cf = cf;\n        sc.source = url;\n        sc.lengths = &uwcf->uwsgi_lengths;\n        sc.values = &uwcf->uwsgi_values;\n        sc.variables = n;\n        sc.complete_lengths = 1;\n        sc.complete_values = 1;\n\n        if (ngx_http_script_compile(&sc) != NGX_OK) {\n            return NGX_CONF_ERROR;\n        }\n\n#if (NGX_HTTP_SSL)\n        uwcf->ssl = 1;\n#endif\n\n        return NGX_CONF_OK;\n    }\n\n    if (ngx_strncasecmp(url->data, (u_char *) \"uwsgi://\", 8) == 0) {\n        add = 8;\n\n    } else if (ngx_strncasecmp(url->data, (u_char *) \"suwsgi://\", 9) == 0) {\n\n#if (NGX_HTTP_SSL)\n        add = 9;\n        uwcf->ssl = 1;\n#else\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"suwsgi protocol requires SSL support\");\n        return NGX_CONF_ERROR;\n#endif\n\n    } else {\n        add = 0;\n    }\n\n    ngx_memzero(&u, sizeof(ngx_url_t));\n\n    u.url.len = url->len - add;\n    u.url.data = url->data + add;\n    u.no_resolve = 1;\n\n    uwcf->upstream.upstream = ngx_http_upstream_add(cf, &u, 0);\n    if (uwcf->upstream.upstream == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (clcf->name.len && clcf->name.data[clcf->name.len - 1] == '/') {\n        clcf->auto_redirect = 1;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_uwsgi_store(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_uwsgi_loc_conf_t *uwcf = conf;\n\n    ngx_str_t                  *value;\n    ngx_http_script_compile_t   sc;\n\n    if (uwcf->upstream.store != NGX_CONF_UNSET) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    if (ngx_strcmp(value[1].data, \"off\") == 0) {\n        uwcf->upstream.store = 0;\n        return NGX_CONF_OK;\n    }\n\n#if (NGX_HTTP_CACHE)\n\n    if (uwcf->upstream.cache > 0) {\n        return \"is incompatible with \\\"uwsgi_cache\\\"\";\n    }\n\n#endif\n\n    uwcf->upstream.store = 1;\n\n    if (ngx_strcmp(value[1].data, \"on\") == 0) {\n        return NGX_CONF_OK;\n    }\n\n    /* include the terminating '\\0' into script */\n    value[1].len++;\n\n    ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));\n\n    sc.cf = cf;\n    sc.source = &value[1];\n    sc.lengths = &uwcf->upstream.store_lengths;\n    sc.values = &uwcf->upstream.store_values;\n    sc.variables = ngx_http_script_variables_count(&value[1]);\n    sc.complete_lengths = 1;\n    sc.complete_values = 1;\n\n    if (ngx_http_script_compile(&sc) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\n#if (NGX_HTTP_CACHE)\n\nstatic char *\nngx_http_uwsgi_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_uwsgi_loc_conf_t *uwcf = conf;\n\n    ngx_str_t                         *value;\n    ngx_http_complex_value_t           cv;\n    ngx_http_compile_complex_value_t   ccv;\n\n    value = cf->args->elts;\n\n    if (uwcf->upstream.cache != NGX_CONF_UNSET) {\n        return \"is duplicate\";\n    }\n\n    if (ngx_strcmp(value[1].data, \"off\") == 0) {\n        uwcf->upstream.cache = 0;\n        return NGX_CONF_OK;\n    }\n\n    if (uwcf->upstream.store > 0) {\n        return \"is incompatible with \\\"uwsgi_store\\\"\";\n    }\n\n    uwcf->upstream.cache = 1;\n\n    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n\n    ccv.cf = cf;\n    ccv.value = &value[1];\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\n        uwcf->upstream.cache_value = ngx_palloc(cf->pool,\n                                             sizeof(ngx_http_complex_value_t));\n        if (uwcf->upstream.cache_value == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        *uwcf->upstream.cache_value = cv;\n\n        return NGX_CONF_OK;\n    }\n\n    uwcf->upstream.cache_zone = ngx_shared_memory_add(cf, &value[1], 0,\n                                                      &ngx_http_uwsgi_module);\n    if (uwcf->upstream.cache_zone == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_uwsgi_cache_key(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_uwsgi_loc_conf_t *uwcf = conf;\n\n    ngx_str_t                         *value;\n    ngx_http_compile_complex_value_t   ccv;\n\n    value = cf->args->elts;\n\n    if (uwcf->cache_key.value.data) {\n        return \"is duplicate\";\n    }\n\n    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n\n    ccv.cf = cf;\n    ccv.value = &value[1];\n    ccv.complex_value = &uwcf->cache_key;\n\n    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n#endif\n\n\n#if (NGX_HTTP_SSL)\n\nstatic char *\nngx_http_uwsgi_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_uwsgi_loc_conf_t *uwcf = conf;\n\n    ngx_str_t  *value;\n\n    if (uwcf->upstream.ssl_passwords != NGX_CONF_UNSET_PTR) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    uwcf->upstream.ssl_passwords = ngx_ssl_read_password_file(cf, &value[1]);\n\n    if (uwcf->upstream.ssl_passwords == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_uwsgi_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#else\n    return NGX_CONF_OK;\n#endif\n}\n\n\nstatic ngx_int_t\nngx_http_uwsgi_merge_ssl(ngx_conf_t *cf, ngx_http_uwsgi_loc_conf_t *conf,\n    ngx_http_uwsgi_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->upstream.ssl_certificate == NGX_CONF_UNSET_PTR\n        && conf->upstream.ssl_certificate_key == NGX_CONF_UNSET_PTR\n        && conf->upstream.ssl_passwords == NGX_CONF_UNSET_PTR\n        && conf->upstream.ssl_verify == NGX_CONF_UNSET\n        && conf->ssl_verify_depth == NGX_CONF_UNSET_UINT\n        && conf->ssl_trusted_certificate.data == NULL\n        && conf->ssl_crl.data == NULL\n        && conf->upstream.ssl_session_reuse == NGX_CONF_UNSET\n        && conf->ssl_conf_commands == NGX_CONF_UNSET_PTR)\n    {\n        if (prev->upstream.ssl) {\n            conf->upstream.ssl = prev->upstream.ssl;\n            return NGX_OK;\n        }\n\n        preserve = 1;\n\n    } else {\n        preserve = 0;\n    }\n\n    conf->upstream.ssl = ngx_pcalloc(cf->pool, sizeof(ngx_ssl_t));\n    if (conf->upstream.ssl == NULL) {\n        return NGX_ERROR;\n    }\n\n    conf->upstream.ssl->log = cf->log;\n\n    /*\n     * special handling to preserve conf->upstream.ssl\n     * in the \"http\" section to inherit it to all servers\n     */\n\n    if (preserve) {\n        prev->upstream.ssl = conf->upstream.ssl;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_uwsgi_set_ssl(ngx_conf_t *cf, ngx_http_uwsgi_loc_conf_t *uwcf)\n{\n    ngx_pool_cleanup_t  *cln;\n\n    if (uwcf->upstream.ssl->ctx) {\n        return NGX_OK;\n    }\n\n    if (ngx_ssl_create(uwcf->upstream.ssl, uwcf->ssl_protocols, NULL)\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    cln = ngx_pool_cleanup_add(cf->pool, 0);\n    if (cln == NULL) {\n        ngx_ssl_cleanup_ctx(uwcf->upstream.ssl);\n        return NGX_ERROR;\n    }\n\n    cln->handler = ngx_ssl_cleanup_ctx;\n    cln->data = uwcf->upstream.ssl;\n\n    if (ngx_ssl_ciphers(cf, uwcf->upstream.ssl, &uwcf->ssl_ciphers, 0)\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    if (uwcf->upstream.ssl_certificate\n        && uwcf->upstream.ssl_certificate->value.len)\n    {\n        if (uwcf->upstream.ssl_certificate_key == NULL) {\n            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                          \"no \\\"uwsgi_ssl_certificate_key\\\" is defined \"\n                          \"for certificate \\\"%V\\\"\",\n                          &uwcf->upstream.ssl_certificate->value);\n            return NGX_ERROR;\n        }\n\n        if (uwcf->upstream.ssl_certificate->lengths\n            || uwcf->upstream.ssl_certificate_key->lengths)\n        {\n            uwcf->upstream.ssl_passwords =\n                  ngx_ssl_preserve_passwords(cf, uwcf->upstream.ssl_passwords);\n            if (uwcf->upstream.ssl_passwords == NULL) {\n                return NGX_ERROR;\n            }\n\n        } else {\n#if (T_NGX_SSL_NTLS)\n            if (ngx_ssl_certificate(cf, uwcf->upstream.ssl,\n                                    &uwcf->upstream.ssl_certificate->value,\n                                    &uwcf->upstream.ssl_certificate_key->value,\n                                    uwcf->upstream.ssl_passwords,\n                                    SSL_NORMAL_CERT)\n#else\n            if (ngx_ssl_certificate(cf, uwcf->upstream.ssl,\n                                    &uwcf->upstream.ssl_certificate->value,\n                                    &uwcf->upstream.ssl_certificate_key->value,\n                                    uwcf->upstream.ssl_passwords)\n#endif\n                != NGX_OK)\n            {\n                return NGX_ERROR;\n            }\n        }\n    }\n\n#if (T_NGX_SSL_NTLS)\n    uwcf->upstream.tls_method = SSL_CTX_get_ssl_method(uwcf->upstream.ssl->ctx);\n    if (uwcf->upstream.enc_certificate.len) {\n\n        if (uwcf->upstream.enc_certificate_key.len == 0) {\n            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                          \"no \\\"uwsgi_ssl_enc_certificate_key\\\" is defined \"\n                          \"for certificate \\\"%V\\\"\",\n                          &uwcf->upstream.enc_certificate);\n            return NGX_ERROR;\n        }\n\n        if (ngx_ssl_certificate(cf, uwcf->upstream.ssl,\n                                &uwcf->upstream.enc_certificate,\n                                &uwcf->upstream.enc_certificate_key,\n                                uwcf->upstream.ssl_passwords,\n                                SSL_ENC_CERT)\n            != NGX_OK)\n        {\n            return NGX_ERROR;\n        }\n    }\n\n    if (uwcf->upstream.sign_certificate.len) {\n\n        if (uwcf->upstream.sign_certificate_key.len == 0) {\n            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                          \"no \\\"uwsgi_ssl_sign_certificate_key\\\" is defined \"\n                          \"for certificate \\\"%V\\\"\",\n                          &uwcf->upstream.sign_certificate);\n            return NGX_ERROR;\n        }\n\n        if (ngx_ssl_certificate(cf, uwcf->upstream.ssl,\n                                &uwcf->upstream.sign_certificate,\n                                &uwcf->upstream.sign_certificate_key,\n                                uwcf->upstream.ssl_passwords,\n                                SSL_SIGN_CERT)\n            != NGX_OK)\n        {\n            return NGX_ERROR;\n        }\n    }\n#endif\n\n    if (uwcf->upstream.ssl_verify) {\n        if (uwcf->ssl_trusted_certificate.len == 0) {\n            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                      \"no uwsgi_ssl_trusted_certificate for uwsgi_ssl_verify\");\n            return NGX_ERROR;\n        }\n\n        if (ngx_ssl_trusted_certificate(cf, uwcf->upstream.ssl,\n                                        &uwcf->ssl_trusted_certificate,\n                                        uwcf->ssl_verify_depth)\n            != NGX_OK)\n        {\n            return NGX_ERROR;\n        }\n\n        if (ngx_ssl_crl(cf, uwcf->upstream.ssl, &uwcf->ssl_crl) != NGX_OK) {\n            return NGX_ERROR;\n        }\n    }\n\n    if (ngx_ssl_client_session_cache(cf, uwcf->upstream.ssl,\n                                     uwcf->upstream.ssl_session_reuse)\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    if (ngx_ssl_conf_commands(cf, uwcf->upstream.ssl, uwcf->ssl_conf_commands)\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n#endif\n"
  },
  {
    "path": "src/http/modules/ngx_http_xslt_filter_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n#include <libxml/parser.h>\n#include <libxml/tree.h>\n#include <libxslt/xslt.h>\n#include <libxslt/xsltInternals.h>\n#include <libxslt/transform.h>\n#include <libxslt/variables.h>\n#include <libxslt/xsltutils.h>\n\n#if (NGX_HAVE_EXSLT)\n#include <libexslt/exslt.h>\n#endif\n\n\n#ifndef NGX_HTTP_XSLT_REUSE_DTD\n#define NGX_HTTP_XSLT_REUSE_DTD  1\n#endif\n\n\ntypedef struct {\n    u_char                    *name;\n    void                      *data;\n} ngx_http_xslt_file_t;\n\n\ntypedef struct {\n    ngx_array_t                dtd_files;    /* ngx_http_xslt_file_t */\n    ngx_array_t                sheet_files;  /* ngx_http_xslt_file_t */\n} ngx_http_xslt_filter_main_conf_t;\n\n\ntypedef struct {\n    u_char                    *name;\n    ngx_http_complex_value_t   value;\n    ngx_uint_t                 quote;        /* unsigned  quote:1; */\n} ngx_http_xslt_param_t;\n\n\ntypedef struct {\n    xsltStylesheetPtr          stylesheet;\n    ngx_array_t                params;       /* ngx_http_xslt_param_t */\n} ngx_http_xslt_sheet_t;\n\n\ntypedef struct {\n    xmlDtdPtr                  dtd;\n    ngx_array_t                sheets;       /* ngx_http_xslt_sheet_t */\n    ngx_hash_t                 types;\n    ngx_array_t               *types_keys;\n    ngx_array_t               *params;       /* ngx_http_xslt_param_t */\n    ngx_flag_t                 last_modified;\n} ngx_http_xslt_filter_loc_conf_t;\n\n\ntypedef struct {\n    xmlDocPtr                  doc;\n    xmlParserCtxtPtr           ctxt;\n    xsltTransformContextPtr    transform;\n    ngx_http_request_t        *request;\n    ngx_array_t                params;\n\n    ngx_uint_t                 done;         /* unsigned  done:1; */\n} ngx_http_xslt_filter_ctx_t;\n\n\nstatic ngx_int_t ngx_http_xslt_send(ngx_http_request_t *r,\n    ngx_http_xslt_filter_ctx_t *ctx, ngx_buf_t *b);\nstatic ngx_int_t ngx_http_xslt_add_chunk(ngx_http_request_t *r,\n    ngx_http_xslt_filter_ctx_t *ctx, ngx_buf_t *b);\n\n\nstatic void ngx_http_xslt_sax_external_subset(void *data, const xmlChar *name,\n    const xmlChar *externalId, const xmlChar *systemId);\nstatic void ngx_cdecl ngx_http_xslt_sax_error(void *data, const char *msg, ...);\n\n\nstatic ngx_buf_t *ngx_http_xslt_apply_stylesheet(ngx_http_request_t *r,\n    ngx_http_xslt_filter_ctx_t *ctx);\nstatic ngx_int_t ngx_http_xslt_params(ngx_http_request_t *r,\n    ngx_http_xslt_filter_ctx_t *ctx, ngx_array_t *params, ngx_uint_t final);\nstatic u_char *ngx_http_xslt_content_type(xsltStylesheetPtr s);\nstatic u_char *ngx_http_xslt_encoding(xsltStylesheetPtr s);\nstatic void ngx_http_xslt_cleanup(void *data);\n\nstatic char *ngx_http_xslt_entities(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_http_xslt_stylesheet(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_http_xslt_param(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic void ngx_http_xslt_cleanup_dtd(void *data);\nstatic void ngx_http_xslt_cleanup_stylesheet(void *data);\nstatic void *ngx_http_xslt_filter_create_main_conf(ngx_conf_t *cf);\nstatic void *ngx_http_xslt_filter_create_conf(ngx_conf_t *cf);\nstatic char *ngx_http_xslt_filter_merge_conf(ngx_conf_t *cf, void *parent,\n    void *child);\nstatic ngx_int_t ngx_http_xslt_filter_preconfiguration(ngx_conf_t *cf);\nstatic ngx_int_t ngx_http_xslt_filter_init(ngx_conf_t *cf);\nstatic void ngx_http_xslt_filter_exit(ngx_cycle_t *cycle);\n\n\nstatic ngx_str_t  ngx_http_xslt_default_types[] = {\n    ngx_string(\"text/xml\"),\n    ngx_null_string\n};\n\n\nstatic ngx_command_t  ngx_http_xslt_filter_commands[] = {\n\n    { ngx_string(\"xml_entities\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_http_xslt_entities,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"xslt_stylesheet\"),\n      NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,\n      ngx_http_xslt_stylesheet,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"xslt_param\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,\n      ngx_http_xslt_param,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"xslt_string_param\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,\n      ngx_http_xslt_param,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      (void *) 1 },\n\n    { ngx_string(\"xslt_types\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,\n      ngx_http_types_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_xslt_filter_loc_conf_t, types_keys),\n      &ngx_http_xslt_default_types[0] },\n\n    { ngx_string(\"xslt_last_modified\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_xslt_filter_loc_conf_t, last_modified),\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_xslt_filter_module_ctx = {\n    ngx_http_xslt_filter_preconfiguration, /* preconfiguration */\n    ngx_http_xslt_filter_init,             /* postconfiguration */\n\n    ngx_http_xslt_filter_create_main_conf, /* create main configuration */\n    NULL,                                  /* init main configuration */\n\n    NULL,                                  /* create server configuration */\n    NULL,                                  /* merge server configuration */\n\n    ngx_http_xslt_filter_create_conf,      /* create location configuration */\n    ngx_http_xslt_filter_merge_conf        /* merge location configuration */\n};\n\n\nngx_module_t  ngx_http_xslt_filter_module = {\n    NGX_MODULE_V1,\n    &ngx_http_xslt_filter_module_ctx,      /* module context */\n    ngx_http_xslt_filter_commands,         /* 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    ngx_http_xslt_filter_exit,             /* exit process */\n    ngx_http_xslt_filter_exit,             /* exit master */\n    NGX_MODULE_V1_PADDING\n};\n\n\nstatic ngx_http_output_header_filter_pt  ngx_http_next_header_filter;\nstatic ngx_http_output_body_filter_pt    ngx_http_next_body_filter;\n\n\nstatic ngx_int_t\nngx_http_xslt_header_filter(ngx_http_request_t *r)\n{\n    ngx_http_xslt_filter_ctx_t       *ctx;\n    ngx_http_xslt_filter_loc_conf_t  *conf;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"xslt filter header\");\n\n    if (r->headers_out.status == NGX_HTTP_NOT_MODIFIED) {\n        return ngx_http_next_header_filter(r);\n    }\n\n    conf = ngx_http_get_module_loc_conf(r, ngx_http_xslt_filter_module);\n\n    if (conf->sheets.nelts == 0\n        || ngx_http_test_content_type(r, &conf->types) == NULL)\n    {\n        return ngx_http_next_header_filter(r);\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_xslt_filter_module);\n\n    if (ctx) {\n        return ngx_http_next_header_filter(r);\n    }\n\n    ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_xslt_filter_ctx_t));\n    if (ctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_http_set_ctx(r, ctx, ngx_http_xslt_filter_module);\n\n    r->main_filter_need_in_memory = 1;\n    r->allow_ranges = 0;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_xslt_body_filter(ngx_http_request_t *r, ngx_chain_t *in)\n{\n    int                          wellFormed;\n    ngx_chain_t                 *cl;\n    ngx_http_xslt_filter_ctx_t  *ctx;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"xslt filter body\");\n\n    if (in == NULL) {\n        return ngx_http_next_body_filter(r, in);\n    }\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_xslt_filter_module);\n\n    if (ctx == NULL || ctx->done) {\n        return ngx_http_next_body_filter(r, in);\n    }\n\n    for (cl = in; cl; cl = cl->next) {\n\n        if (ngx_http_xslt_add_chunk(r, ctx, cl->buf) != NGX_OK) {\n\n            if (ctx->ctxt->myDoc) {\n\n#if (NGX_HTTP_XSLT_REUSE_DTD)\n                ctx->ctxt->myDoc->extSubset = NULL;\n#endif\n                xmlFreeDoc(ctx->ctxt->myDoc);\n            }\n\n            xmlFreeParserCtxt(ctx->ctxt);\n\n            return ngx_http_xslt_send(r, ctx, NULL);\n        }\n\n        if (cl->buf->last_buf || cl->buf->last_in_chain) {\n\n            ctx->doc = ctx->ctxt->myDoc;\n\n#if (NGX_HTTP_XSLT_REUSE_DTD)\n            ctx->doc->extSubset = NULL;\n#endif\n\n            wellFormed = ctx->ctxt->wellFormed;\n\n            xmlFreeParserCtxt(ctx->ctxt);\n\n            if (wellFormed) {\n                return ngx_http_xslt_send(r, ctx,\n                                       ngx_http_xslt_apply_stylesheet(r, ctx));\n            }\n\n            xmlFreeDoc(ctx->doc);\n\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"not well formed XML document\");\n\n            return ngx_http_xslt_send(r, ctx, NULL);\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_xslt_send(ngx_http_request_t *r, ngx_http_xslt_filter_ctx_t *ctx,\n    ngx_buf_t *b)\n{\n    ngx_int_t                         rc;\n    ngx_chain_t                       out;\n    ngx_pool_cleanup_t               *cln;\n    ngx_http_xslt_filter_loc_conf_t  *conf;\n\n    ctx->done = 1;\n\n    if (b == NULL) {\n        return ngx_http_filter_finalize_request(r, &ngx_http_xslt_filter_module,\n                                               NGX_HTTP_INTERNAL_SERVER_ERROR);\n    }\n\n    cln = ngx_pool_cleanup_add(r->pool, 0);\n\n    if (cln == NULL) {\n        ngx_free(b->pos);\n        return ngx_http_filter_finalize_request(r, &ngx_http_xslt_filter_module,\n                                               NGX_HTTP_INTERNAL_SERVER_ERROR);\n    }\n\n    if (r == r->main) {\n        r->headers_out.content_length_n = b->last - b->pos;\n\n        if (r->headers_out.content_length) {\n            r->headers_out.content_length->hash = 0;\n            r->headers_out.content_length = NULL;\n        }\n\n        conf = ngx_http_get_module_loc_conf(r, ngx_http_xslt_filter_module);\n\n        if (!conf->last_modified) {\n            ngx_http_clear_last_modified(r);\n            ngx_http_clear_etag(r);\n\n        } else {\n            ngx_http_weak_etag(r);\n        }\n    }\n\n    rc = ngx_http_next_header_filter(r);\n\n    if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {\n        ngx_free(b->pos);\n        return rc;\n    }\n\n    cln->handler = ngx_http_xslt_cleanup;\n    cln->data = b->pos;\n\n    out.buf = b;\n    out.next = NULL;\n\n    return ngx_http_next_body_filter(r, &out);\n}\n\n\nstatic ngx_int_t\nngx_http_xslt_add_chunk(ngx_http_request_t *r, ngx_http_xslt_filter_ctx_t *ctx,\n    ngx_buf_t *b)\n{\n    int               err;\n    xmlParserCtxtPtr  ctxt;\n\n    if (ctx->ctxt == NULL) {\n\n        ctxt = xmlCreatePushParserCtxt(NULL, NULL, NULL, 0, NULL);\n        if (ctxt == NULL) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"xmlCreatePushParserCtxt() failed\");\n            return NGX_ERROR;\n        }\n        xmlCtxtUseOptions(ctxt, XML_PARSE_NOENT|XML_PARSE_DTDLOAD\n                                               |XML_PARSE_NOWARNING);\n\n        ctxt->sax->externalSubset = ngx_http_xslt_sax_external_subset;\n        ctxt->sax->setDocumentLocator = NULL;\n        ctxt->sax->error = ngx_http_xslt_sax_error;\n        ctxt->sax->fatalError = ngx_http_xslt_sax_error;\n        ctxt->sax->_private = ctx;\n\n        ctx->ctxt = ctxt;\n        ctx->request = r;\n    }\n\n    err = xmlParseChunk(ctx->ctxt, (char *) b->pos, (int) (b->last - b->pos),\n                        (b->last_buf) || (b->last_in_chain));\n\n    if (err == 0) {\n        b->pos = b->last;\n        return NGX_OK;\n    }\n\n    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                  \"xmlParseChunk() failed, error:%d\", err);\n\n    return NGX_ERROR;\n}\n\n\nstatic void\nngx_http_xslt_sax_external_subset(void *data, const xmlChar *name,\n    const xmlChar *externalId, const xmlChar *systemId)\n{\n    xmlParserCtxtPtr ctxt = data;\n\n    xmlDocPtr                         doc;\n    xmlDtdPtr                         dtd;\n    ngx_http_request_t               *r;\n    ngx_http_xslt_filter_ctx_t       *ctx;\n    ngx_http_xslt_filter_loc_conf_t  *conf;\n\n    ctx = ctxt->sax->_private;\n    r = ctx->request;\n\n    conf = ngx_http_get_module_loc_conf(r, ngx_http_xslt_filter_module);\n\n    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"xslt filter extSubset: \\\"%s\\\" \\\"%s\\\" \\\"%s\\\"\",\n                   name ? name : (xmlChar *) \"\",\n                   externalId ? externalId : (xmlChar *) \"\",\n                   systemId ? systemId : (xmlChar *) \"\");\n\n    doc = ctxt->myDoc;\n\n#if (NGX_HTTP_XSLT_REUSE_DTD)\n\n    dtd = conf->dtd;\n\n#else\n\n    dtd = xmlCopyDtd(conf->dtd);\n    if (dtd == NULL) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"xmlCopyDtd() failed\");\n        return;\n    }\n\n    if (doc->children == NULL) {\n        xmlAddChild((xmlNodePtr) doc, (xmlNodePtr) dtd);\n\n    } else {\n        xmlAddPrevSibling(doc->children, (xmlNodePtr) dtd);\n    }\n\n#endif\n\n    doc->extSubset = dtd;\n}\n\n\nstatic void ngx_cdecl\nngx_http_xslt_sax_error(void *data, const char *msg, ...)\n{\n    xmlParserCtxtPtr ctxt = data;\n\n    size_t                       n;\n    va_list                      args;\n    ngx_http_xslt_filter_ctx_t  *ctx;\n    u_char                       buf[NGX_MAX_ERROR_STR];\n\n    ctx = ctxt->sax->_private;\n\n    buf[0] = '\\0';\n\n    va_start(args, msg);\n    n = (size_t) vsnprintf((char *) buf, NGX_MAX_ERROR_STR, msg, args);\n    va_end(args);\n\n    while (--n && (buf[n] == CR || buf[n] == LF)) { /* void */ }\n\n    ngx_log_error(NGX_LOG_ERR, ctx->request->connection->log, 0,\n                  \"libxml2 error: \\\"%*s\\\"\", n + 1, buf);\n}\n\n\nstatic ngx_buf_t *\nngx_http_xslt_apply_stylesheet(ngx_http_request_t *r,\n    ngx_http_xslt_filter_ctx_t *ctx)\n{\n    int                               len, rc, doc_type;\n    u_char                           *type, *encoding;\n    ngx_buf_t                        *b;\n    ngx_uint_t                        i;\n    xmlChar                          *buf;\n    xmlDocPtr                         doc, res;\n    ngx_http_xslt_sheet_t            *sheet;\n    ngx_http_xslt_filter_loc_conf_t  *conf;\n\n    conf = ngx_http_get_module_loc_conf(r, ngx_http_xslt_filter_module);\n    sheet = conf->sheets.elts;\n    doc = ctx->doc;\n\n    /* preallocate array for 4 params */\n\n    if (ngx_array_init(&ctx->params, r->pool, 4 * 2 + 1, sizeof(char *))\n        != NGX_OK)\n    {\n        xmlFreeDoc(doc);\n        return NULL;\n    }\n\n    for (i = 0; i < conf->sheets.nelts; i++) {\n\n        ctx->transform = xsltNewTransformContext(sheet[i].stylesheet, doc);\n        if (ctx->transform == NULL) {\n            xmlFreeDoc(doc);\n            return NULL;\n        }\n\n        if (conf->params\n            && ngx_http_xslt_params(r, ctx, conf->params, 0) != NGX_OK)\n        {\n            xsltFreeTransformContext(ctx->transform);\n            xmlFreeDoc(doc);\n            return NULL;\n        }\n\n        if (ngx_http_xslt_params(r, ctx, &sheet[i].params, 1) != NGX_OK) {\n            xsltFreeTransformContext(ctx->transform);\n            xmlFreeDoc(doc);\n            return NULL;\n        }\n\n        res = xsltApplyStylesheetUser(sheet[i].stylesheet, doc,\n                                      ctx->params.elts, NULL, NULL,\n                                      ctx->transform);\n\n        xsltFreeTransformContext(ctx->transform);\n        xmlFreeDoc(doc);\n\n        if (res == NULL) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"xsltApplyStylesheet() failed\");\n            return NULL;\n        }\n\n        doc = res;\n\n        /* reset array elements */\n        ctx->params.nelts = 0;\n    }\n\n    /* there must be at least one stylesheet */\n\n    if (r == r->main) {\n        type = ngx_http_xslt_content_type(sheet[i - 1].stylesheet);\n\n    } else {\n        type = NULL;\n    }\n\n    encoding = ngx_http_xslt_encoding(sheet[i - 1].stylesheet);\n    doc_type = doc->type;\n\n    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"xslt filter type: %d t:%s e:%s\",\n                   doc_type, type ? type : (u_char *) \"(null)\",\n                   encoding ? encoding : (u_char *) \"(null)\");\n\n    rc = xsltSaveResultToString(&buf, &len, doc, sheet[i - 1].stylesheet);\n\n    xmlFreeDoc(doc);\n\n    if (rc != 0) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"xsltSaveResultToString() failed\");\n        return NULL;\n    }\n\n    if (len == 0) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"xsltSaveResultToString() returned zero-length result\");\n        return NULL;\n    }\n\n    b = ngx_calloc_buf(r->pool);\n    if (b == NULL) {\n        ngx_free(buf);\n        return NULL;\n    }\n\n    b->pos = buf;\n    b->last = buf + len;\n    b->memory = 1;\n\n    if (encoding) {\n        r->headers_out.charset.len = ngx_strlen(encoding);\n        r->headers_out.charset.data = encoding;\n    }\n\n    if (r != r->main) {\n        return b;\n    }\n\n    b->last_buf = 1;\n\n    if (type) {\n        len = ngx_strlen(type);\n\n        r->headers_out.content_type_len = len;\n        r->headers_out.content_type.len = len;\n        r->headers_out.content_type.data = type;\n\n    } else if (doc_type == XML_HTML_DOCUMENT_NODE) {\n\n        r->headers_out.content_type_len = sizeof(\"text/html\") - 1;\n        ngx_str_set(&r->headers_out.content_type, \"text/html\");\n    }\n\n    r->headers_out.content_type_lowcase = NULL;\n\n    return b;\n}\n\n\nstatic ngx_int_t\nngx_http_xslt_params(ngx_http_request_t *r, ngx_http_xslt_filter_ctx_t *ctx,\n    ngx_array_t *params, ngx_uint_t final)\n{\n    u_char                 *p, *value, *dst, *src, **s;\n    size_t                  len;\n    ngx_uint_t              i;\n    ngx_str_t               string;\n    ngx_http_xslt_param_t  *param;\n\n    param = params->elts;\n\n    for (i = 0; i < params->nelts; i++) {\n\n        if (ngx_http_complex_value(r, &param[i].value, &string) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"xslt filter param: \\\"%s\\\"\", string.data);\n\n        if (param[i].name) {\n\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"xslt filter param name: \\\"%s\\\"\", param[i].name);\n\n            if (param[i].quote) {\n                if (xsltQuoteOneUserParam(ctx->transform, param[i].name,\n                                          string.data)\n                    != 0)\n                {\n                    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                                \"xsltQuoteOneUserParam(\\\"%s\\\", \\\"%s\\\") failed\",\n                                param[i].name, string.data);\n                    return NGX_ERROR;\n                }\n\n                continue;\n            }\n\n            s = ngx_array_push(&ctx->params);\n            if (s == NULL) {\n                return NGX_ERROR;\n            }\n\n            *s = param[i].name;\n\n            s = ngx_array_push(&ctx->params);\n            if (s == NULL) {\n                return NGX_ERROR;\n            }\n\n            *s = string.data;\n\n            continue;\n        }\n\n        /*\n         * parse param1=value1:param2=value2 syntax as used by parameters\n         * specified in xslt_stylesheet directives\n         */\n\n        if (param[i].value.lengths) {\n            p = string.data;\n\n        } else {\n            p = ngx_pnalloc(r->pool, string.len + 1);\n            if (p == NULL) {\n                return NGX_ERROR;\n            }\n\n            ngx_memcpy(p, string.data, string.len + 1);\n        }\n\n        while (p && *p) {\n\n            value = p;\n            p = (u_char *) ngx_strchr(p, '=');\n            if (p == NULL) {\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                                \"invalid libxslt parameter \\\"%s\\\"\", value);\n                return NGX_ERROR;\n            }\n            *p++ = '\\0';\n\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"xslt filter param name: \\\"%s\\\"\", value);\n\n            s = ngx_array_push(&ctx->params);\n            if (s == NULL) {\n                return NGX_ERROR;\n            }\n\n            *s = value;\n\n            value = p;\n            p = (u_char *) ngx_strchr(p, ':');\n\n            if (p) {\n                len = p - value;\n                *p++ = '\\0';\n\n            } else {\n                len = ngx_strlen(value);\n            }\n\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"xslt filter param value: \\\"%s\\\"\", value);\n\n            dst = value;\n            src = value;\n\n            ngx_unescape_uri(&dst, &src, len, 0);\n\n            *dst = '\\0';\n\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"xslt filter param unescaped: \\\"%s\\\"\", value);\n\n            s = ngx_array_push(&ctx->params);\n            if (s == NULL) {\n                return NGX_ERROR;\n            }\n\n            *s = value;\n        }\n    }\n\n    if (final) {\n        s = ngx_array_push(&ctx->params);\n        if (s == NULL) {\n            return NGX_ERROR;\n        }\n\n        *s = NULL;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic u_char *\nngx_http_xslt_content_type(xsltStylesheetPtr s)\n{\n    u_char  *type;\n\n    if (s->mediaType) {\n        return s->mediaType;\n    }\n\n    for (s = s->imports; s; s = s->next) {\n\n        type = ngx_http_xslt_content_type(s);\n\n        if (type) {\n            return type;\n        }\n    }\n\n    return NULL;\n}\n\n\nstatic u_char *\nngx_http_xslt_encoding(xsltStylesheetPtr s)\n{\n    u_char  *encoding;\n\n    if (s->encoding) {\n        return s->encoding;\n    }\n\n    for (s = s->imports; s; s = s->next) {\n\n        encoding = ngx_http_xslt_encoding(s);\n\n        if (encoding) {\n            return encoding;\n        }\n    }\n\n    return NULL;\n}\n\n\nstatic void\nngx_http_xslt_cleanup(void *data)\n{\n    ngx_free(data);\n}\n\n\nstatic char *\nngx_http_xslt_entities(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_xslt_filter_loc_conf_t *xlcf = conf;\n\n    ngx_str_t                         *value;\n    ngx_uint_t                         i;\n    ngx_pool_cleanup_t                *cln;\n    ngx_http_xslt_file_t              *file;\n    ngx_http_xslt_filter_main_conf_t  *xmcf;\n\n    if (xlcf->dtd) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    xmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_xslt_filter_module);\n\n    file = xmcf->dtd_files.elts;\n    for (i = 0; i < xmcf->dtd_files.nelts; i++) {\n        if (ngx_strcmp(file[i].name, value[1].data) == 0) {\n            xlcf->dtd = file[i].data;\n            return NGX_CONF_OK;\n        }\n    }\n\n    cln = ngx_pool_cleanup_add(cf->pool, 0);\n    if (cln == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    xlcf->dtd = xmlParseDTD(NULL, (xmlChar *) value[1].data);\n\n    if (xlcf->dtd == NULL) {\n        ngx_conf_log_error(NGX_LOG_ERR, cf, 0, \"xmlParseDTD() failed\");\n        return NGX_CONF_ERROR;\n    }\n\n    cln->handler = ngx_http_xslt_cleanup_dtd;\n    cln->data = xlcf->dtd;\n\n    file = ngx_array_push(&xmcf->dtd_files);\n    if (file == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    file->name = value[1].data;\n    file->data = xlcf->dtd;\n\n    return NGX_CONF_OK;\n}\n\n\n\nstatic char *\nngx_http_xslt_stylesheet(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_xslt_filter_loc_conf_t *xlcf = conf;\n\n    ngx_str_t                         *value;\n    ngx_uint_t                         i, n;\n    ngx_pool_cleanup_t                *cln;\n    ngx_http_xslt_file_t              *file;\n    ngx_http_xslt_sheet_t             *sheet;\n    ngx_http_xslt_param_t             *param;\n    ngx_http_compile_complex_value_t   ccv;\n    ngx_http_xslt_filter_main_conf_t  *xmcf;\n\n    value = cf->args->elts;\n\n    if (xlcf->sheets.elts == NULL) {\n        if (ngx_array_init(&xlcf->sheets, cf->pool, 1,\n                           sizeof(ngx_http_xslt_sheet_t))\n            != NGX_OK)\n        {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    sheet = ngx_array_push(&xlcf->sheets);\n    if (sheet == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_memzero(sheet, sizeof(ngx_http_xslt_sheet_t));\n\n    if (ngx_conf_full_name(cf->cycle, &value[1], 0) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    xmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_xslt_filter_module);\n\n    file = xmcf->sheet_files.elts;\n    for (i = 0; i < xmcf->sheet_files.nelts; i++) {\n        if (ngx_strcmp(file[i].name, value[1].data) == 0) {\n            sheet->stylesheet = file[i].data;\n            goto found;\n        }\n    }\n\n    cln = ngx_pool_cleanup_add(cf->pool, 0);\n    if (cln == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    sheet->stylesheet = xsltParseStylesheetFile(value[1].data);\n    if (sheet->stylesheet == NULL) {\n        ngx_conf_log_error(NGX_LOG_ERR, cf, 0,\n                           \"xsltParseStylesheetFile(\\\"%s\\\") failed\",\n                           value[1].data);\n        return NGX_CONF_ERROR;\n    }\n\n    cln->handler = ngx_http_xslt_cleanup_stylesheet;\n    cln->data = sheet->stylesheet;\n\n    file = ngx_array_push(&xmcf->sheet_files);\n    if (file == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    file->name = value[1].data;\n    file->data = sheet->stylesheet;\n\nfound:\n\n    n = cf->args->nelts;\n\n    if (n == 2) {\n        return NGX_CONF_OK;\n    }\n\n    if (ngx_array_init(&sheet->params, cf->pool, n - 2,\n                       sizeof(ngx_http_xslt_param_t))\n        != NGX_OK)\n    {\n        return NGX_CONF_ERROR;\n    }\n\n    for (i = 2; i < n; i++) {\n\n        param = ngx_array_push(&sheet->params);\n        if (param == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        ngx_memzero(param, sizeof(ngx_http_xslt_param_t));\n        ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n\n        ccv.cf = cf;\n        ccv.value = &value[i];\n        ccv.complex_value = &param->value;\n        ccv.zero = 1;\n\n        if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_xslt_param(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_xslt_filter_loc_conf_t  *xlcf = conf;\n\n    ngx_http_xslt_param_t            *param;\n    ngx_http_compile_complex_value_t  ccv;\n    ngx_str_t                        *value;\n\n    value = cf->args->elts;\n\n    if (xlcf->params == NULL) {\n        xlcf->params = ngx_array_create(cf->pool, 2,\n                                        sizeof(ngx_http_xslt_param_t));\n        if (xlcf->params == NULL) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    param = ngx_array_push(xlcf->params);\n    if (param == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    param->name = value[1].data;\n    param->quote = (cmd->post == NULL) ? 0 : 1;\n\n    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n\n    ccv.cf = cf;\n    ccv.value = &value[2];\n    ccv.complex_value = &param->value;\n    ccv.zero = 1;\n\n    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic void\nngx_http_xslt_cleanup_dtd(void *data)\n{\n    xmlFreeDtd(data);\n}\n\n\nstatic void\nngx_http_xslt_cleanup_stylesheet(void *data)\n{\n    xsltFreeStylesheet(data);\n}\n\n\nstatic void *\nngx_http_xslt_filter_create_main_conf(ngx_conf_t *cf)\n{\n    ngx_http_xslt_filter_main_conf_t  *conf;\n\n    conf = ngx_palloc(cf->pool, sizeof(ngx_http_xslt_filter_main_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    if (ngx_array_init(&conf->dtd_files, cf->pool, 1,\n                       sizeof(ngx_http_xslt_file_t))\n        != NGX_OK)\n    {\n        return NULL;\n    }\n\n    if (ngx_array_init(&conf->sheet_files, cf->pool, 1,\n                       sizeof(ngx_http_xslt_file_t))\n        != NGX_OK)\n    {\n        return NULL;\n    }\n\n    return conf;\n}\n\n\nstatic void *\nngx_http_xslt_filter_create_conf(ngx_conf_t *cf)\n{\n    ngx_http_xslt_filter_loc_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_xslt_filter_loc_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc():\n     *\n     *     conf->dtd = NULL;\n     *     conf->sheets = { NULL };\n     *     conf->types = { NULL };\n     *     conf->types_keys = NULL;\n     *     conf->params = NULL;\n     */\n\n    conf->last_modified = NGX_CONF_UNSET;\n\n    return conf;\n}\n\n\nstatic char *\nngx_http_xslt_filter_merge_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_http_xslt_filter_loc_conf_t *prev = parent;\n    ngx_http_xslt_filter_loc_conf_t *conf = child;\n\n    if (conf->dtd == NULL) {\n        conf->dtd = prev->dtd;\n    }\n\n    if (conf->sheets.nelts == 0) {\n        conf->sheets = prev->sheets;\n    }\n\n    if (conf->params == NULL) {\n        conf->params = prev->params;\n    }\n\n    if (ngx_http_merge_types(cf, &conf->types_keys, &conf->types,\n                             &prev->types_keys, &prev->types,\n                             ngx_http_xslt_default_types)\n        != NGX_OK)\n    {\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_conf_merge_value(conf->last_modified, prev->last_modified, 0);\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_xslt_filter_preconfiguration(ngx_conf_t *cf)\n{\n    xmlInitParser();\n\n#if (NGX_HAVE_EXSLT)\n    exsltRegisterAll();\n#endif\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_xslt_filter_init(ngx_conf_t *cf)\n{\n    ngx_http_next_header_filter = ngx_http_top_header_filter;\n    ngx_http_top_header_filter = ngx_http_xslt_header_filter;\n\n    ngx_http_next_body_filter = ngx_http_top_body_filter;\n    ngx_http_top_body_filter = ngx_http_xslt_body_filter;\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_xslt_filter_exit(ngx_cycle_t *cycle)\n{\n    xsltCleanupGlobals();\n    xmlCleanupParser();\n}\n"
  },
  {
    "path": "src/http/modules/perl/Makefile.PL",
    "content": "\n# Copyright (C) Igor Sysoev\n# Copyright (C) Nginx, Inc.\n\nuse 5.006001;\nuse ExtUtils::MakeMaker;\n\nWriteMakefile(\n    NAME              => 'nginx',\n    VERSION_FROM      => 'nginx.pm',     # finds $VERSION\n    PREREQ_PM         => {},             # e.g., Module::Name => 1.1\n\n    ABSTRACT_FROM     => 'nginx.pm',     # retrieve abstract from module\n    AUTHOR            => 'Igor Sysoev',\n\n    CCFLAGS           => \"$ENV{NGX_PM_CFLAGS}\",\n    OPTIMIZE          => '-O',\n\n    LDDLFLAGS         => \"$ENV{NGX_PM_LDFLAGS}\",\n\n    INC               => join(\" \", map {\n                             m#^/# ? \"-I $_\" : \"-I ../../../../../$_\"\n                         } (split /\\s+/, $ENV{NGX_INCS})),\n\n    depend => {\n        'nginx.c'     => join(\" \", map {\n                             m#^/# ? $_ : \"../../../../../$_\"\n                         } (split(/\\s+/, $ENV{NGX_DEPS}),\n                            \"src/http/modules/perl/ngx_http_perl_module.h\"))\n    },\n\n    PM => {\n        'nginx.pm'    => '$(INST_LIBDIR)/nginx.pm'\n    }\n);\n"
  },
  {
    "path": "src/http/modules/perl/nginx.pm",
    "content": "package nginx;\n\nuse 5.006001;\nuse strict;\nuse warnings;\n\nrequire Exporter;\n\nour @ISA = qw(Exporter);\n\nour @EXPORT = qw(\n    OK\n    DECLINED\n\n    HTTP_OK\n    HTTP_CREATED\n    HTTP_ACCEPTED\n    HTTP_NO_CONTENT\n    HTTP_PARTIAL_CONTENT\n\n    HTTP_MOVED_PERMANENTLY\n    HTTP_MOVED_TEMPORARILY\n    HTTP_REDIRECT\n    HTTP_SEE_OTHER\n    HTTP_NOT_MODIFIED\n    HTTP_TEMPORARY_REDIRECT\n    HTTP_PERMANENT_REDIRECT\n\n    HTTP_BAD_REQUEST\n    HTTP_UNAUTHORIZED\n    HTTP_PAYMENT_REQUIRED\n    HTTP_FORBIDDEN\n    HTTP_NOT_FOUND\n    HTTP_NOT_ALLOWED\n    HTTP_NOT_ACCEPTABLE\n    HTTP_REQUEST_TIME_OUT\n    HTTP_CONFLICT\n    HTTP_GONE\n    HTTP_LENGTH_REQUIRED\n    HTTP_REQUEST_ENTITY_TOO_LARGE\n    HTTP_REQUEST_URI_TOO_LARGE\n    HTTP_UNSUPPORTED_MEDIA_TYPE\n    HTTP_RANGE_NOT_SATISFIABLE\n\n    HTTP_INTERNAL_SERVER_ERROR\n    HTTP_SERVER_ERROR\n    HTTP_NOT_IMPLEMENTED\n    HTTP_BAD_GATEWAY\n    HTTP_SERVICE_UNAVAILABLE\n    HTTP_GATEWAY_TIME_OUT\n    HTTP_INSUFFICIENT_STORAGE\n);\n\nour $VERSION = '%%VERSION%%';\n\nrequire XSLoader;\nXSLoader::load('nginx', $VERSION);\n\n# Preloaded methods go here.\n\nuse constant OK                             => 0;\nuse constant DECLINED                       => -5;\n\nuse constant HTTP_OK                        => 200;\nuse constant HTTP_CREATED                   => 201;\nuse constant HTTP_ACCEPTED                  => 202;\nuse constant HTTP_NO_CONTENT                => 204;\nuse constant HTTP_PARTIAL_CONTENT           => 206;\n\nuse constant HTTP_MOVED_PERMANENTLY         => 301;\nuse constant HTTP_MOVED_TEMPORARILY         => 302;\nuse constant HTTP_REDIRECT                  => 302;\nuse constant HTTP_SEE_OTHER                 => 303;\nuse constant HTTP_NOT_MODIFIED              => 304;\nuse constant HTTP_TEMPORARY_REDIRECT        => 307;\nuse constant HTTP_PERMANENT_REDIRECT        => 308;\n\nuse constant HTTP_BAD_REQUEST               => 400;\nuse constant HTTP_UNAUTHORIZED              => 401;\nuse constant HTTP_PAYMENT_REQUIRED          => 402;\nuse constant HTTP_FORBIDDEN                 => 403;\nuse constant HTTP_NOT_FOUND                 => 404;\nuse constant HTTP_NOT_ALLOWED               => 405;\nuse constant HTTP_NOT_ACCEPTABLE            => 406;\nuse constant HTTP_REQUEST_TIME_OUT          => 408;\nuse constant HTTP_CONFLICT                  => 409;\nuse constant HTTP_GONE                      => 410;\nuse constant HTTP_LENGTH_REQUIRED           => 411;\nuse constant HTTP_REQUEST_ENTITY_TOO_LARGE  => 413;\nuse constant HTTP_REQUEST_URI_TOO_LARGE     => 414;\nuse constant HTTP_UNSUPPORTED_MEDIA_TYPE    => 415;\nuse constant HTTP_RANGE_NOT_SATISFIABLE     => 416;\n\nuse constant HTTP_INTERNAL_SERVER_ERROR     => 500;\nuse constant HTTP_SERVER_ERROR              => 500;\nuse constant HTTP_NOT_IMPLEMENTED           => 501;\nuse constant HTTP_BAD_GATEWAY               => 502;\nuse constant HTTP_SERVICE_UNAVAILABLE       => 503;\nuse constant HTTP_GATEWAY_TIME_OUT          => 504;\nuse constant HTTP_INSUFFICIENT_STORAGE      => 507;\n\n\nsub rflush {\n    my $r = shift;\n\n    $r->flush;\n}\n\n\n1;\n__END__\n\n=head1 NAME\n\nnginx - Perl interface to the nginx HTTP server API\n\n=head1 SYNOPSIS\n\n  use nginx;\n\n=head1 DESCRIPTION\n\nThis module provides a Perl interface to the nginx HTTP server API.\n\n\n=head1 SEE ALSO\n\nhttp://nginx.org/en/docs/http/ngx_http_perl_module.html\n\n=head1 AUTHOR\n\nIgor Sysoev\n\n=head1 COPYRIGHT AND LICENSE\n\nCopyright (C) Igor Sysoev\nCopyright (C) Nginx, Inc.\n\n\n=cut\n"
  },
  {
    "path": "src/http/modules/perl/nginx.xs",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#define PERL_NO_GET_CONTEXT\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n#include <ngx_http_perl_module.h>\n\n#include \"XSUB.h\"\n\n\n#define ngx_http_perl_set_request(r, ctx)                                     \\\n                                                                              \\\n    ctx = INT2PTR(ngx_http_perl_ctx_t *, SvIV((SV *) SvRV(ST(0))));           \\\n    r = ctx->request\n\n\n#define ngx_http_perl_set_targ(p, len)                                        \\\n                                                                              \\\n    SvUPGRADE(TARG, SVt_PV);                                                  \\\n    SvPOK_on(TARG);                                                           \\\n    sv_setpvn(TARG, (char *) p, len)\n\n\nstatic ngx_int_t\nngx_http_perl_sv2str(pTHX_ ngx_http_request_t *r, ngx_str_t *s, SV *sv)\n{\n    u_char  *p;\n    STRLEN   len;\n\n    if (SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PV) {\n        sv = SvRV(sv);\n    }\n\n    p = (u_char *) SvPV(sv, len);\n\n    s->len = len;\n\n    if (SvREADONLY(sv) && SvPOK(sv)) {\n        s->data = p;\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"perl sv2str: %08XD \\\"%V\\\"\", sv->sv_flags, s);\n\n        return NGX_OK;\n    }\n\n    s->data = ngx_pnalloc(r->pool, len);\n    if (s->data == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_memcpy(s->data, p, len);\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"perl sv2str: %08XD \\\"%V\\\"\", sv->sv_flags, s);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_perl_output(ngx_http_request_t *r, ngx_http_perl_ctx_t *ctx,\n    ngx_buf_t *b)\n{\n    ngx_chain_t   out;\n#if (NGX_HTTP_SSI)\n    ngx_chain_t  *cl;\n\n    if (ctx->ssi) {\n        cl = ngx_alloc_chain_link(r->pool);\n        if (cl == NULL) {\n            return NGX_ERROR;\n        }\n\n        cl->buf = b;\n        cl->next = NULL;\n        *ctx->ssi->last_out = cl;\n        ctx->ssi->last_out = &cl->next;\n\n        return NGX_OK;\n    }\n#endif\n\n    out.buf = b;\n    out.next = NULL;\n\n    return ngx_http_output_filter(r, &out);\n}\n\n\nMODULE = nginx    PACKAGE = nginx\n\n\nPROTOTYPES: DISABLE\n\n\nvoid\nstatus(r, code)\n    CODE:\n\n    ngx_http_request_t   *r;\n    ngx_http_perl_ctx_t  *ctx;\n\n    ngx_http_perl_set_request(r, ctx);\n\n    if (ctx->variable) {\n        croak(\"status(): cannot be used in variable handler\");\n    }\n\n    r->headers_out.status = SvIV(ST(1));\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"perl status: %d\", r->headers_out.status);\n\n    XSRETURN_UNDEF;\n\n\nvoid\nsend_http_header(r, ...)\n    CODE:\n\n    ngx_http_request_t   *r;\n    ngx_http_perl_ctx_t  *ctx;\n    SV                   *sv;\n    ngx_int_t             rc;\n\n    ngx_http_perl_set_request(r, ctx);\n\n    if (ctx->error) {\n        croak(\"send_http_header(): called after error\");\n    }\n\n    if (ctx->variable) {\n        croak(\"send_http_header(): cannot be used in variable handler\");\n    }\n\n    if (ctx->header_sent) {\n        croak(\"send_http_header(): header already sent\");\n    }\n\n    if (ctx->redirect_uri.len) {\n        croak(\"send_http_header(): cannot be used with internal_redirect()\");\n    }\n\n    if (r->headers_out.status == 0) {\n        r->headers_out.status = NGX_HTTP_OK;\n    }\n\n    if (items != 1) {\n        sv = ST(1);\n\n        if (ngx_http_perl_sv2str(aTHX_ r, &r->headers_out.content_type, sv)\n            != NGX_OK)\n        {\n            ctx->error = 1;\n            croak(\"ngx_http_perl_sv2str() failed\");\n        }\n\n        r->headers_out.content_type_len = r->headers_out.content_type.len;\n\n    } else {\n        if (ngx_http_set_content_type(r) != NGX_OK) {\n            ctx->error = 1;\n            croak(\"ngx_http_set_content_type() failed\");\n        }\n    }\n\n    ctx->header_sent = 1;\n\n    r->disable_not_modified = 1;\n\n    rc = ngx_http_send_header(r);\n\n    if (rc == NGX_ERROR || rc > NGX_OK) {\n        ctx->error = 1;\n        ctx->status = rc;\n        croak(\"ngx_http_send_header() failed\");\n    }\n\n\nvoid\nheader_only(r)\n    CODE:\n\n    dXSTARG;\n    ngx_http_request_t   *r;\n    ngx_http_perl_ctx_t  *ctx;\n\n    ngx_http_perl_set_request(r, ctx);\n\n    sv_upgrade(TARG, SVt_IV);\n    sv_setiv(TARG, r->header_only);\n\n    ST(0) = TARG;\n\n\nvoid\nuri(r)\n    CODE:\n\n    dXSTARG;\n    ngx_http_request_t   *r;\n    ngx_http_perl_ctx_t  *ctx;\n\n    ngx_http_perl_set_request(r, ctx);\n    ngx_http_perl_set_targ(r->uri.data, r->uri.len);\n\n    ST(0) = TARG;\n\n\nvoid\nargs(r)\n    CODE:\n\n    dXSTARG;\n    ngx_http_request_t   *r;\n    ngx_http_perl_ctx_t  *ctx;\n\n    ngx_http_perl_set_request(r, ctx);\n    ngx_http_perl_set_targ(r->args.data, r->args.len);\n\n    ST(0) = TARG;\n\n\nvoid\nrequest_method(r)\n    CODE:\n\n    dXSTARG;\n    ngx_http_request_t   *r;\n    ngx_http_perl_ctx_t  *ctx;\n\n    ngx_http_perl_set_request(r, ctx);\n    ngx_http_perl_set_targ(r->method_name.data, r->method_name.len);\n\n    ST(0) = TARG;\n\n\nvoid\nremote_addr(r)\n    CODE:\n\n    dXSTARG;\n    ngx_http_request_t   *r;\n    ngx_http_perl_ctx_t  *ctx;\n\n    ngx_http_perl_set_request(r, ctx);\n    ngx_http_perl_set_targ(r->connection->addr_text.data,\n                           r->connection->addr_text.len);\n\n    ST(0) = TARG;\n\n\nvoid\nheader_in(r, key)\n    CODE:\n\n    dXSTARG;\n    ngx_http_request_t         *r;\n    ngx_http_perl_ctx_t        *ctx;\n    SV                         *key;\n    u_char                     *p, *lowcase_key, *value, sep;\n    STRLEN                      len;\n    ssize_t                     size;\n    ngx_uint_t                  i, hash;\n    ngx_list_part_t            *part;\n    ngx_table_elt_t            *h, *header, **ph;\n    ngx_http_header_t          *hh;\n    ngx_http_core_main_conf_t  *cmcf;\n\n    ngx_http_perl_set_request(r, ctx);\n\n    key = ST(1);\n\n    if (SvROK(key) && SvTYPE(SvRV(key)) == SVt_PV) {\n        key = SvRV(key);\n    }\n\n    p = (u_char *) SvPV(key, len);\n\n    /* look up hashed headers */\n\n    lowcase_key = ngx_pnalloc(r->pool, len);\n    if (lowcase_key == NULL) {\n        ctx->error = 1;\n        croak(\"ngx_pnalloc() failed\");\n    }\n\n    hash = ngx_hash_strlow(lowcase_key, p, len);\n\n    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);\n\n    hh = ngx_hash_find(&cmcf->headers_in_hash, hash, lowcase_key, len);\n\n    if (hh) {\n\n        if (hh->offset == offsetof(ngx_http_headers_in_t, cookie)) {\n            sep = ';';\n\n        } else {\n            sep = ',';\n        }\n\n        ph = (ngx_table_elt_t **) ((char *) &r->headers_in + hh->offset);\n\n        goto found;\n    }\n\n    /* iterate over all headers */\n\n    sep = ',';\n    ph = &header;\n\n    part = &r->headers_in.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 (len != h[i].key.len\n            || ngx_strcasecmp(p, h[i].key.data) != 0)\n        {\n            continue;\n        }\n\n        *ph = &h[i];\n        ph = &h[i].next;\n    }\n\n    *ph = NULL;\n    ph = &header;\n\n    found:\n\n    if (*ph == NULL) {\n        XSRETURN_UNDEF;\n    }\n\n    if ((*ph)->next == NULL) {\n        ngx_http_perl_set_targ((*ph)->value.data, (*ph)->value.len);\n        goto done;\n    }\n\n    size = - (ssize_t) (sizeof(\"; \") - 1);\n\n    for (h = *ph; h; h = h->next) {\n        size += h->value.len + sizeof(\"; \") - 1;\n    }\n\n    value = ngx_pnalloc(r->pool, size);\n    if (value == NULL) {\n        ctx->error = 1;\n        croak(\"ngx_pnalloc() failed\");\n    }\n\n    p = value;\n\n    for (h = *ph; h; h = h->next) {\n        p = ngx_copy(p, h->value.data, h->value.len);\n\n        if (h->next == NULL) {\n            break;\n        }\n\n        *p++ = sep; *p++ = ' ';\n    }\n\n    ngx_http_perl_set_targ(value, size);\n\n    done:\n\n    ST(0) = TARG;\n\n\nvoid\nhas_request_body(r, next)\n    CODE:\n\n    dXSTARG;\n    ngx_http_request_t   *r;\n    ngx_http_perl_ctx_t  *ctx;\n    ngx_int_t             rc;\n\n    ngx_http_perl_set_request(r, ctx);\n\n    if (ctx->variable) {\n        croak(\"has_request_body(): cannot be used in variable handler\");\n    }\n\n    if (ctx->next) {\n        croak(\"has_request_body(): another handler active\");\n    }\n\n    if (r->headers_in.content_length_n <= 0 && !r->headers_in.chunked) {\n        XSRETURN_UNDEF;\n    }\n\n    ctx->next = SvRV(ST(1));\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 (r->request_body_in_file_only) {\n        r->request_body_file_log_level = 0;\n    }\n\n    rc = ngx_http_read_client_request_body(r, ngx_http_perl_handle_request);\n\n    if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {\n        ctx->error = 1;\n        ctx->status = rc;\n        ctx->next = NULL;\n        croak(\"ngx_http_read_client_request_body() failed\");\n    }\n\n    sv_upgrade(TARG, SVt_IV);\n    sv_setiv(TARG, 1);\n\n    ST(0) = TARG;\n\n\nvoid\nrequest_body(r)\n    CODE:\n\n    dXSTARG;\n    ngx_http_request_t   *r;\n    ngx_http_perl_ctx_t  *ctx;\n    u_char               *p, *data;\n    size_t                len;\n    ngx_buf_t            *buf;\n    ngx_chain_t          *cl;\n\n    ngx_http_perl_set_request(r, ctx);\n\n    if (r->request_body == NULL\n        || r->request_body->temp_file\n        || r->request_body->bufs == NULL)\n    {\n        XSRETURN_UNDEF;\n    }\n\n    cl = r->request_body->bufs;\n    buf = cl->buf;\n\n    if (cl->next == NULL) {\n        len = buf->last - buf->pos;\n        data = buf->pos;\n        goto done;\n    }\n\n    len = buf->last - buf->pos;\n    cl = cl->next;\n\n    for ( /* void */ ; cl; cl = cl->next) {\n        buf = cl->buf;\n        len += buf->last - buf->pos;\n    }\n\n    p = ngx_pnalloc(r->pool, len);\n    if (p == NULL) {\n        ctx->error = 1;\n        croak(\"ngx_pnalloc() failed\");\n    }\n\n    data = p;\n    cl = r->request_body->bufs;\n\n    for ( /* void */ ; cl; cl = cl->next) {\n        buf = cl->buf;\n        p = ngx_cpymem(p, buf->pos, buf->last - buf->pos);\n    }\n\n    done:\n\n    if (len == 0) {\n        XSRETURN_UNDEF;\n    }\n\n    ngx_http_perl_set_targ(data, len);\n\n    ST(0) = TARG;\n\n\nvoid\nrequest_body_file(r)\n    CODE:\n\n    dXSTARG;\n    ngx_http_request_t   *r;\n    ngx_http_perl_ctx_t  *ctx;\n\n    ngx_http_perl_set_request(r, ctx);\n\n    if (r->request_body == NULL || r->request_body->temp_file == NULL) {\n        XSRETURN_UNDEF;\n    }\n\n    ngx_http_perl_set_targ(r->request_body->temp_file->file.name.data,\n                           r->request_body->temp_file->file.name.len);\n\n    ST(0) = TARG;\n\n\nvoid\ndiscard_request_body(r)\n    CODE:\n\n    ngx_http_request_t   *r;\n    ngx_http_perl_ctx_t  *ctx;\n    ngx_int_t             rc;\n\n    ngx_http_perl_set_request(r, ctx);\n\n    if (ctx->variable) {\n        croak(\"discard_request_body(): cannot be used in variable handler\");\n    }\n\n    rc = ngx_http_discard_request_body(r);\n\n    if (rc != NGX_OK) {\n        ctx->error = 1;\n        ctx->status = rc;\n        croak(\"ngx_http_discard_request_body() failed\");\n    }\n\n\nvoid\nheader_out(r, key, value)\n    CODE:\n\n    ngx_http_request_t   *r;\n    ngx_http_perl_ctx_t  *ctx;\n    SV                   *key;\n    SV                   *value;\n    ngx_table_elt_t      *header;\n\n    ngx_http_perl_set_request(r, ctx);\n\n    if (ctx->error) {\n        croak(\"header_out(): called after error\");\n    }\n\n    if (ctx->variable) {\n        croak(\"header_out(): cannot be used in variable handler\");\n    }\n\n    key = ST(1);\n    value = ST(2);\n\n    header = ngx_list_push(&r->headers_out.headers);\n    if (header == NULL) {\n        ctx->error = 1;\n        croak(\"ngx_list_push() failed\");\n    }\n\n    header->hash = 1;\n    header->next = NULL;\n\n    if (ngx_http_perl_sv2str(aTHX_ r, &header->key, key) != NGX_OK) {\n        header->hash = 0;\n        ctx->error = 1;\n        croak(\"ngx_http_perl_sv2str() failed\");\n    }\n\n    if (ngx_http_perl_sv2str(aTHX_ r, &header->value, value) != NGX_OK) {\n        header->hash = 0;\n        ctx->error = 1;\n        croak(\"ngx_http_perl_sv2str() failed\");\n    }\n\n    if (header->key.len == sizeof(\"Content-Length\") - 1\n        && ngx_strncasecmp(header->key.data, (u_char *) \"Content-Length\",\n                           sizeof(\"Content-Length\") - 1) == 0)\n    {\n        r->headers_out.content_length_n = (off_t) SvIV(value);\n        r->headers_out.content_length = header;\n    }\n\n    if (header->key.len == sizeof(\"Content-Encoding\") - 1\n        && ngx_strncasecmp(header->key.data, (u_char *) \"Content-Encoding\",\n                           sizeof(\"Content-Encoding\") - 1) == 0)\n    {\n        r->headers_out.content_encoding = header;\n    }\n\n\nvoid\nfilename(r)\n    CODE:\n\n    dXSTARG;\n    ngx_http_request_t   *r;\n    ngx_http_perl_ctx_t  *ctx;\n    size_t                root;\n\n    ngx_http_perl_set_request(r, ctx);\n\n    if (ctx->filename.data) {\n        goto done;\n    }\n\n    if (ngx_http_map_uri_to_path(r, &ctx->filename, &root, 0) == NULL) {\n        ctx->error = 1;\n        croak(\"ngx_http_map_uri_to_path() failed\");\n    }\n\n    ctx->filename.len--;\n    sv_setpv(PL_statname, (char *) ctx->filename.data);\n\n    done:\n\n    ngx_http_perl_set_targ(ctx->filename.data, ctx->filename.len);\n\n    ST(0) = TARG;\n\n\nvoid\nprint(r, ...)\n    CODE:\n\n    ngx_http_request_t   *r;\n    ngx_http_perl_ctx_t  *ctx;\n    SV                   *sv;\n    int                   i;\n    u_char               *p;\n    size_t                size;\n    STRLEN                len;\n    ngx_int_t             rc;\n    ngx_buf_t            *b;\n\n    ngx_http_perl_set_request(r, ctx);\n\n    if (ctx->error) {\n        croak(\"print(): called after error\");\n    }\n\n    if (ctx->variable) {\n        croak(\"print(): cannot be used in variable handler\");\n    }\n\n    if (!ctx->header_sent) {\n        croak(\"print(): header not sent\");\n    }\n\n    if (items == 2) {\n\n        /*\n         * do zero copy for prolate single read-only SV:\n         *     $r->print(\"some text\\n\");\n         */\n\n        sv = ST(1);\n\n        if (SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PV) {\n            sv = SvRV(sv);\n        }\n\n        if (SvREADONLY(sv) && SvPOK(sv)) {\n\n            p = (u_char *) SvPV(sv, len);\n\n            if (len == 0) {\n                XSRETURN_EMPTY;\n            }\n\n            b = ngx_calloc_buf(r->pool);\n            if (b == NULL) {\n                ctx->error = 1;\n                croak(\"ngx_calloc_buf() failed\");\n            }\n\n            b->memory = 1;\n            b->pos = p;\n            b->last = p + len;\n            b->start = p;\n            b->end = b->last;\n\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"$r->print: read-only SV: %z\", len);\n\n            goto out;\n        }\n    }\n\n    size = 0;\n\n    for (i = 1; i < items; i++) {\n\n        sv = ST(i);\n\n        if (SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PV) {\n            sv = SvRV(sv);\n        }\n\n        (void) SvPV(sv, len);\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"$r->print: copy SV: %z\", len);\n\n        size += len;\n    }\n\n    if (size == 0) {\n        XSRETURN_EMPTY;\n    }\n\n    b = ngx_create_temp_buf(r->pool, size);\n    if (b == NULL) {\n        ctx->error = 1;\n        croak(\"ngx_create_temp_buf() failed\");\n    }\n\n    for (i = 1; i < items; i++) {\n        sv = ST(i);\n\n        if (SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PV) {\n            sv = SvRV(sv);\n        }\n\n        p = (u_char *) SvPV(sv, len);\n        b->last = ngx_cpymem(b->last, p, len);\n    }\n\n    out:\n\n    rc = ngx_http_perl_output(r, ctx, b);\n\n    if (rc == NGX_ERROR) {\n        ctx->error = 1;\n        croak(\"ngx_http_perl_output() failed\");\n    }\n\n\nvoid\nsendfile(r, filename, offset = -1, bytes = 0)\n    CODE:\n\n    ngx_http_request_t        *r;\n    ngx_http_perl_ctx_t       *ctx;\n    char                      *filename;\n    off_t                      offset;\n    size_t                     bytes;\n    ngx_int_t                  rc;\n    ngx_str_t                  path;\n    ngx_buf_t                 *b;\n    ngx_open_file_info_t       of;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    ngx_http_perl_set_request(r, ctx);\n\n    if (ctx->error) {\n        croak(\"sendfile(): called after error\");\n    }\n\n    if (ctx->variable) {\n        croak(\"sendfile(): cannot be used in variable handler\");\n    }\n\n    if (!ctx->header_sent) {\n        croak(\"sendfile(): header not sent\");\n    }\n\n    filename = SvPV_nolen(ST(1));\n\n    if (filename == NULL) {\n        croak(\"sendfile(): NULL filename\");\n    }\n\n    offset = items < 3 ? -1 : SvIV(ST(2));\n    bytes = items < 4 ? 0 : SvIV(ST(3));\n\n    b = ngx_calloc_buf(r->pool);\n    if (b == NULL) {\n        ctx->error = 1;\n        croak(\"ngx_calloc_buf() failed\");\n    }\n\n    b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t));\n    if (b->file == NULL) {\n        ctx->error = 1;\n        croak(\"ngx_pcalloc() failed\");\n    }\n\n    path.len = ngx_strlen(filename);\n\n    path.data = ngx_pnalloc(r->pool, path.len + 1);\n    if (path.data == NULL) {\n        ctx->error = 1;\n        croak(\"ngx_pnalloc() failed\");\n    }\n\n    (void) ngx_cpystrn(path.data, (u_char *) filename, path.len + 1);\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    ngx_memzero(&of, sizeof(ngx_open_file_info_t));\n\n    of.read_ahead = clcf->read_ahead;\n    of.directio = clcf->directio;\n    of.valid = clcf->open_file_cache_valid;\n    of.min_uses = clcf->open_file_cache_min_uses;\n    of.errors = clcf->open_file_cache_errors;\n    of.events = clcf->open_file_cache_events;\n\n    if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) {\n        ctx->error = 1;\n        croak(\"ngx_http_set_disable_symlinks() failed\");\n    }\n\n    if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)\n        != NGX_OK)\n    {\n        if (of.err == 0) {\n            ctx->error = 1;\n            croak(\"ngx_open_cached_file() failed\");\n        }\n\n        ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,\n                      \"%s \\\"%s\\\" failed\", of.failed, filename);\n\n        ctx->error = 1;\n        croak(\"ngx_open_cached_file() failed\");\n    }\n\n    if (offset == -1) {\n        offset = 0;\n    }\n\n    if (bytes == 0) {\n        bytes = of.size - offset;\n    }\n\n    b->in_file = 1;\n\n    b->file_pos = offset;\n    b->file_last = offset + bytes;\n\n    b->file->fd = of.fd;\n    b->file->log = r->connection->log;\n    b->file->directio = of.is_directio;\n\n    rc = ngx_http_perl_output(r, ctx, b);\n\n    if (rc == NGX_ERROR) {\n        ctx->error = 1;\n        croak(\"ngx_http_perl_output() failed\");\n    }\n\n\nvoid\nflush(r)\n    CODE:\n\n    ngx_http_request_t   *r;\n    ngx_http_perl_ctx_t  *ctx;\n    ngx_int_t             rc;\n    ngx_buf_t            *b;\n\n    ngx_http_perl_set_request(r, ctx);\n\n    if (ctx->error) {\n        croak(\"flush(): called after error\");\n    }\n\n    if (ctx->variable) {\n        croak(\"flush(): cannot be used in variable handler\");\n    }\n\n    if (!ctx->header_sent) {\n        croak(\"flush(): header not sent\");\n    }\n\n    b = ngx_calloc_buf(r->pool);\n    if (b == NULL) {\n        ctx->error = 1;\n        croak(\"ngx_calloc_buf() failed\");\n    }\n\n    b->flush = 1;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, \"$r->flush\");\n\n    rc = ngx_http_perl_output(r, ctx, b);\n\n    if (rc == NGX_ERROR) {\n        ctx->error = 1;\n        croak(\"ngx_http_perl_output() failed\");\n    }\n\n    XSRETURN_EMPTY;\n\n\nvoid\ninternal_redirect(r, uri)\n    CODE:\n\n    ngx_http_request_t   *r;\n    ngx_http_perl_ctx_t  *ctx;\n    SV                   *uri;\n\n    ngx_http_perl_set_request(r, ctx);\n\n    if (ctx->variable) {\n        croak(\"internal_redirect(): cannot be used in variable handler\");\n    }\n\n    if (ctx->header_sent) {\n        croak(\"internal_redirect(): header already sent\");\n    }\n\n    uri = ST(1);\n\n    if (ngx_http_perl_sv2str(aTHX_ r, &ctx->redirect_uri, uri) != NGX_OK) {\n        ctx->error = 1;\n        croak(\"ngx_http_perl_sv2str() failed\");\n    }\n\n\nvoid\nallow_ranges(r)\n    CODE:\n\n    ngx_http_request_t   *r;\n    ngx_http_perl_ctx_t  *ctx;\n\n    ngx_http_perl_set_request(r, ctx);\n\n    if (ctx->variable) {\n        croak(\"allow_ranges(): cannot be used in variable handler\");\n    }\n\n    r->allow_ranges = 1;\n\n\nvoid\nunescape(r, text, type = 0)\n    CODE:\n\n    dXSTARG;\n    ngx_http_request_t   *r;\n    ngx_http_perl_ctx_t  *ctx;\n    SV                   *text;\n    int                   type;\n    u_char               *p, *dst, *src;\n    STRLEN                len;\n\n    ngx_http_perl_set_request(r, ctx);\n\n    text = ST(1);\n\n    src = (u_char *) SvPV(text, len);\n\n    p = ngx_pnalloc(r->pool, len + 1);\n    if (p == NULL) {\n        ctx->error = 1;\n        croak(\"ngx_pnalloc() failed\");\n    }\n\n    dst = p;\n\n    type = items < 3 ? 0 : SvIV(ST(2));\n\n    ngx_unescape_uri(&dst, &src, len, (ngx_uint_t) type);\n    *dst = '\\0';\n\n    ngx_http_perl_set_targ(p, dst - p);\n\n    ST(0) = TARG;\n\n\nvoid\nvariable(r, name, value = NULL)\n    CODE:\n\n    dXSTARG;\n    ngx_http_request_t         *r;\n    ngx_http_perl_ctx_t        *ctx;\n    SV                         *name, *value;\n    u_char                     *p, *lowcase;\n    STRLEN                      len;\n    ngx_str_t                   var, val;\n    ngx_uint_t                  i, hash;\n    ngx_http_perl_var_t        *v;\n    ngx_http_variable_value_t  *vv;\n\n    ngx_http_perl_set_request(r, ctx);\n\n    name = ST(1);\n\n    if (SvROK(name) && SvTYPE(SvRV(name)) == SVt_PV) {\n        name = SvRV(name);\n    }\n\n    if (items == 2) {\n        value = NULL;\n\n    } else {\n        value = ST(2);\n\n        if (SvROK(value) && SvTYPE(SvRV(value)) == SVt_PV) {\n            value = SvRV(value);\n        }\n\n        if (ngx_http_perl_sv2str(aTHX_ r, &val, value) != NGX_OK) {\n            ctx->error = 1;\n            croak(\"ngx_http_perl_sv2str() failed\");\n        }\n    }\n\n    p = (u_char *) SvPV(name, len);\n\n    lowcase = ngx_pnalloc(r->pool, len);\n    if (lowcase == NULL) {\n        ctx->error = 1;\n        croak(\"ngx_pnalloc() failed\");\n    }\n\n    hash = ngx_hash_strlow(lowcase, p, len);\n\n    var.len = len;\n    var.data = lowcase;\n#if (NGX_DEBUG)\n\n    if (value) {\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"perl variable: \\\"%V\\\"=\\\"%V\\\"\", &var, &val);\n    } else {\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"perl variable: \\\"%V\\\"\", &var);\n    }\n#endif\n\n    vv = ngx_http_get_variable(r, &var, hash);\n    if (vv == NULL) {\n        ctx->error = 1;\n        croak(\"ngx_http_get_variable() failed\");\n    }\n\n    if (vv->not_found) {\n\n        if (ctx->variables) {\n\n            v = ctx->variables->elts;\n            for (i = 0; i < ctx->variables->nelts; i++) {\n\n                if (hash != v[i].hash\n                    || len != v[i].name.len\n                    || ngx_strncmp(lowcase, v[i].name.data, len) != 0)\n                {\n                    continue;\n                }\n\n                if (value) {\n                    v[i].value = val;\n                    XSRETURN_UNDEF;\n                }\n\n                ngx_http_perl_set_targ(v[i].value.data, v[i].value.len);\n\n                goto done;\n            }\n        }\n\n        if (value) {\n            if (ctx->variables == NULL) {\n                ctx->variables = ngx_array_create(r->pool, 1,\n                                                  sizeof(ngx_http_perl_var_t));\n                if (ctx->variables == NULL) {\n                    ctx->error = 1;\n                    croak(\"ngx_array_create() failed\");\n                }\n            }\n\n            v = ngx_array_push(ctx->variables);\n            if (v == NULL) {\n                ctx->error = 1;\n                croak(\"ngx_array_push() failed\");\n            }\n\n            v->hash = hash;\n            v->name.len = len;\n            v->name.data = lowcase;\n            v->value = val;\n\n            XSRETURN_UNDEF;\n        }\n\n        XSRETURN_UNDEF;\n    }\n\n    if (value) {\n        vv->len = val.len;\n        vv->valid = 1;\n        vv->no_cacheable = 0;\n        vv->not_found = 0;\n        vv->data = val.data;\n\n        XSRETURN_UNDEF;\n    }\n\n    ngx_http_perl_set_targ(vv->data, vv->len);\n\n    done:\n\n    ST(0) = TARG;\n\n\nvoid\nsleep(r, sleep, next)\n    CODE:\n\n    ngx_http_request_t   *r;\n    ngx_http_perl_ctx_t  *ctx;\n    ngx_msec_t            sleep;\n\n    ngx_http_perl_set_request(r, ctx);\n\n    if (ctx->variable) {\n        croak(\"sleep(): cannot be used in variable handler\");\n    }\n\n    if (ctx->next) {\n        croak(\"sleep(): another handler active\");\n    }\n\n    sleep = (ngx_msec_t) SvIV(ST(1));\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"perl sleep: %M\", sleep);\n\n    ctx->next = SvRV(ST(2));\n\n    r->connection->write->delayed = 1;\n    ngx_add_timer(r->connection->write, sleep);\n\n    r->write_event_handler = ngx_http_perl_sleep_handler;\n    r->main->count++;\n\n\nvoid\nlog_error(r, err, msg)\n    CODE:\n\n    ngx_http_request_t   *r;\n    ngx_http_perl_ctx_t  *ctx;\n    SV                   *err, *msg;\n    u_char               *p;\n    STRLEN                len;\n    ngx_err_t             e;\n\n    ngx_http_perl_set_request(r, ctx);\n\n    err = ST(1);\n\n    if (SvROK(err) && SvTYPE(SvRV(err)) == SVt_PV) {\n        err = SvRV(err);\n    }\n\n    e = SvIV(err);\n\n    msg = ST(2);\n\n    if (SvROK(msg) && SvTYPE(SvRV(msg)) == SVt_PV) {\n        msg = SvRV(msg);\n    }\n\n    p = (u_char *) SvPV(msg, len);\n\n    ngx_log_error(NGX_LOG_ERR, r->connection->log, e, \"perl: %s\", p);\n"
  },
  {
    "path": "src/http/modules/perl/ngx_http_perl_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n#include <ngx_http_perl_module.h>\n\n\ntypedef struct {\n    PerlInterpreter   *perl;\n    HV                *nginx;\n    ngx_array_t       *modules;\n    ngx_array_t       *requires;\n} ngx_http_perl_main_conf_t;\n\n\ntypedef struct {\n    SV                *sub;\n    ngx_str_t          handler;\n} ngx_http_perl_loc_conf_t;\n\n\ntypedef struct {\n    SV                *sub;\n    ngx_str_t          handler;\n} ngx_http_perl_variable_t;\n\n\n#if (NGX_HTTP_SSI)\nstatic ngx_int_t ngx_http_perl_ssi(ngx_http_request_t *r,\n    ngx_http_ssi_ctx_t *ssi_ctx, ngx_str_t **params);\n#endif\n\nstatic char *ngx_http_perl_init_interpreter(ngx_conf_t *cf,\n    ngx_http_perl_main_conf_t *pmcf);\nstatic PerlInterpreter *ngx_http_perl_create_interpreter(ngx_conf_t *cf,\n    ngx_http_perl_main_conf_t *pmcf);\nstatic ngx_int_t ngx_http_perl_run_requires(pTHX_ ngx_array_t *requires,\n    ngx_log_t *log);\nstatic ngx_int_t ngx_http_perl_call_handler(pTHX_ ngx_http_request_t *r,\n    ngx_http_perl_ctx_t *ctx, HV *nginx, SV *sub, SV **args,\n    ngx_str_t *handler, ngx_str_t *rv);\nstatic void ngx_http_perl_eval_anon_sub(pTHX_ ngx_str_t *handler, SV **sv);\n\nstatic ngx_int_t ngx_http_perl_preconfiguration(ngx_conf_t *cf);\nstatic void *ngx_http_perl_create_main_conf(ngx_conf_t *cf);\nstatic char *ngx_http_perl_init_main_conf(ngx_conf_t *cf, void *conf);\nstatic void *ngx_http_perl_create_loc_conf(ngx_conf_t *cf);\nstatic char *ngx_http_perl_merge_loc_conf(ngx_conf_t *cf, void *parent,\n    void *child);\nstatic char *ngx_http_perl(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\nstatic char *ngx_http_perl_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\n\n#if (NGX_HAVE_PERL_MULTIPLICITY)\nstatic void ngx_http_perl_cleanup_perl(void *data);\n#endif\n\nstatic ngx_int_t ngx_http_perl_init_worker(ngx_cycle_t *cycle);\nstatic void ngx_http_perl_exit(ngx_cycle_t *cycle);\n\n\nstatic ngx_command_t  ngx_http_perl_commands[] = {\n\n    { ngx_string(\"perl_modules\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_array_slot,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      offsetof(ngx_http_perl_main_conf_t, modules),\n      NULL },\n\n    { ngx_string(\"perl_require\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_array_slot,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      offsetof(ngx_http_perl_main_conf_t, requires),\n      NULL },\n\n    { ngx_string(\"perl\"),\n      NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_TAKE1,\n      ngx_http_perl,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"perl_set\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE2,\n      ngx_http_perl_set,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_perl_module_ctx = {\n    ngx_http_perl_preconfiguration,        /* preconfiguration */\n    NULL,                                  /* postconfiguration */\n\n    ngx_http_perl_create_main_conf,        /* create main configuration */\n    ngx_http_perl_init_main_conf,          /* init main configuration */\n\n    NULL,                                  /* create server configuration */\n    NULL,                                  /* merge server configuration */\n\n    ngx_http_perl_create_loc_conf,         /* create location configuration */\n    ngx_http_perl_merge_loc_conf           /* merge location configuration */\n};\n\n\nngx_module_t  ngx_http_perl_module = {\n    NGX_MODULE_V1,\n    &ngx_http_perl_module_ctx,             /* module context */\n    ngx_http_perl_commands,                /* module directives */\n    NGX_HTTP_MODULE,                       /* module type */\n    NULL,                                  /* init master */\n    NULL,                                  /* init module */\n    ngx_http_perl_init_worker,             /* init process */\n    NULL,                                  /* init thread */\n    NULL,                                  /* exit thread */\n    NULL,                                  /* exit process */\n    ngx_http_perl_exit,                    /* exit master */\n    NGX_MODULE_V1_PADDING\n};\n\n\n#if (NGX_HTTP_SSI)\n\n#define NGX_HTTP_PERL_SSI_SUB  0\n#define NGX_HTTP_PERL_SSI_ARG  1\n\n\nstatic ngx_http_ssi_param_t  ngx_http_perl_ssi_params[] = {\n    { ngx_string(\"sub\"), NGX_HTTP_PERL_SSI_SUB, 1, 0 },\n    { ngx_string(\"arg\"), NGX_HTTP_PERL_SSI_ARG, 0, 1 },\n    { ngx_null_string, 0, 0, 0 }\n};\n\nstatic ngx_http_ssi_command_t  ngx_http_perl_ssi_command = {\n    ngx_string(\"perl\"), ngx_http_perl_ssi, ngx_http_perl_ssi_params, 0, 0, 1\n};\n\n#endif\n\n\nstatic ngx_str_t         ngx_null_name = ngx_null_string;\nstatic HV               *nginx_stash;\n\n#if (NGX_HAVE_PERL_MULTIPLICITY)\nstatic ngx_uint_t        ngx_perl_term;\n#else\nstatic PerlInterpreter  *perl;\n#endif\n\n\nstatic void\nngx_http_perl_xs_init(pTHX)\n{\n    newXS(\"DynaLoader::boot_DynaLoader\", boot_DynaLoader, __FILE__);\n\n    nginx_stash = gv_stashpv(\"nginx\", TRUE);\n}\n\n\nstatic ngx_int_t\nngx_http_perl_handler(ngx_http_request_t *r)\n{\n    r->main->count++;\n\n    ngx_http_perl_handle_request(r);\n\n    return NGX_DONE;\n}\n\n\nvoid\nngx_http_perl_handle_request(ngx_http_request_t *r)\n{\n    SV                         *sub;\n    ngx_int_t                   rc;\n    ngx_str_t                   uri, args, *handler;\n    ngx_uint_t                  flags;\n    ngx_http_perl_ctx_t        *ctx;\n    ngx_http_perl_loc_conf_t   *plcf;\n    ngx_http_perl_main_conf_t  *pmcf;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, \"perl handler\");\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_perl_module);\n\n    if (ctx == NULL) {\n        ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_perl_ctx_t));\n        if (ctx == NULL) {\n            ngx_http_finalize_request(r, NGX_ERROR);\n            return;\n        }\n\n        ngx_http_set_ctx(r, ctx, ngx_http_perl_module);\n\n        ctx->request = r;\n    }\n\n    pmcf = ngx_http_get_module_main_conf(r, ngx_http_perl_module);\n\n    {\n\n    dTHXa(pmcf->perl);\n    PERL_SET_CONTEXT(pmcf->perl);\n    PERL_SET_INTERP(pmcf->perl);\n\n    if (ctx->next == NULL) {\n        plcf = ngx_http_get_module_loc_conf(r, ngx_http_perl_module);\n        sub = plcf->sub;\n        handler = &plcf->handler;\n\n    } else {\n        sub = ctx->next;\n        handler = &ngx_null_name;\n        ctx->next = NULL;\n    }\n\n    rc = ngx_http_perl_call_handler(aTHX_ r, ctx, pmcf->nginx, sub, NULL,\n                                    handler, NULL);\n\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"perl handler done: %i\", rc);\n\n    if (rc > 600) {\n        rc = NGX_OK;\n    }\n\n    if (ctx->redirect_uri.len) {\n        uri = ctx->redirect_uri;\n\n    } else {\n        uri.len = 0;\n    }\n\n    ctx->filename.data = NULL;\n    ctx->redirect_uri.len = 0;\n\n    if (rc == NGX_ERROR) {\n        ngx_http_finalize_request(r, rc);\n        return;\n    }\n\n    if (ctx->done || ctx->next) {\n        ngx_http_finalize_request(r, NGX_DONE);\n        return;\n    }\n\n    if (uri.len) {\n        if (uri.data[0] == '@') {\n            ngx_http_named_location(r, &uri);\n\n        } else {\n            ngx_str_null(&args);\n            flags = NGX_HTTP_LOG_UNSAFE;\n\n            if (ngx_http_parse_unsafe_uri(r, &uri, &args, &flags) != NGX_OK) {\n                ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n                return;\n            }\n\n            ngx_http_internal_redirect(r, &uri, &args);\n        }\n\n        ngx_http_finalize_request(r, NGX_DONE);\n        return;\n    }\n\n    if (rc == NGX_OK || rc == NGX_HTTP_OK) {\n        ngx_http_send_special(r, NGX_HTTP_LAST);\n        ctx->done = 1;\n    }\n\n    ngx_http_finalize_request(r, rc);\n}\n\n\nvoid\nngx_http_perl_sleep_handler(ngx_http_request_t *r)\n{\n    ngx_event_t  *wev;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"perl sleep handler\");\n\n    wev = r->connection->write;\n\n    if (wev->delayed) {\n\n        if (ngx_handle_write_event(wev, 0) != NGX_OK) {\n            ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n        }\n\n        return;\n    }\n\n    ngx_http_perl_handle_request(r);\n}\n\n\nstatic ngx_int_t\nngx_http_perl_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v,\n    uintptr_t data)\n{\n    ngx_http_perl_variable_t *pv = (ngx_http_perl_variable_t *) data;\n\n    ngx_int_t                   rc;\n    ngx_str_t                   value;\n    ngx_uint_t                  saved;\n    ngx_http_perl_ctx_t        *ctx;\n    ngx_http_perl_main_conf_t  *pmcf;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"perl variable handler\");\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_perl_module);\n\n    if (ctx == NULL) {\n        ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_perl_ctx_t));\n        if (ctx == NULL) {\n            return NGX_ERROR;\n        }\n\n        ngx_http_set_ctx(r, ctx, ngx_http_perl_module);\n\n        ctx->request = r;\n    }\n\n    saved = ctx->variable;\n    ctx->variable = 1;\n\n    pmcf = ngx_http_get_module_main_conf(r, ngx_http_perl_module);\n\n    value.data = NULL;\n\n    {\n\n    dTHXa(pmcf->perl);\n    PERL_SET_CONTEXT(pmcf->perl);\n    PERL_SET_INTERP(pmcf->perl);\n\n    rc = ngx_http_perl_call_handler(aTHX_ r, ctx, pmcf->nginx, pv->sub, NULL,\n                                    &pv->handler, &value);\n\n    }\n\n    if (value.data) {\n        v->len = value.len;\n        v->valid = 1;\n        v->no_cacheable = 0;\n        v->not_found = 0;\n        v->data = value.data;\n\n    } else {\n        v->not_found = 1;\n    }\n\n    ctx->variable = saved;\n    ctx->filename.data = NULL;\n    ctx->redirect_uri.len = 0;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"perl variable done\");\n\n    return rc;\n}\n\n\n#if (NGX_HTTP_SSI)\n\nstatic ngx_int_t\nngx_http_perl_ssi(ngx_http_request_t *r, ngx_http_ssi_ctx_t *ssi_ctx,\n    ngx_str_t **params)\n{\n    SV                         *sv, **asv;\n    ngx_int_t                   rc;\n    ngx_str_t                  *handler, **args;\n    ngx_uint_t                  i;\n    ngx_http_perl_ctx_t        *ctx;\n    ngx_http_perl_main_conf_t  *pmcf;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"perl ssi handler\");\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_perl_module);\n\n    if (ctx == NULL) {\n        ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_perl_ctx_t));\n        if (ctx == NULL) {\n            return NGX_ERROR;\n        }\n\n        ngx_http_set_ctx(r, ctx, ngx_http_perl_module);\n\n        ctx->request = r;\n    }\n\n    pmcf = ngx_http_get_module_main_conf(r, ngx_http_perl_module);\n\n    ctx->ssi = ssi_ctx;\n    ctx->header_sent = 1;\n\n    handler = params[NGX_HTTP_PERL_SSI_SUB];\n    handler->data[handler->len] = '\\0';\n\n    {\n\n    dTHXa(pmcf->perl);\n    PERL_SET_CONTEXT(pmcf->perl);\n    PERL_SET_INTERP(pmcf->perl);\n\n#if 0\n\n    /* the code is disabled to force the precompiled perl code using only */\n\n    ngx_http_perl_eval_anon_sub(aTHX_ handler, &sv);\n\n    if (sv == &PL_sv_undef) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"eval_pv(\\\"%V\\\") failed\", handler);\n        return NGX_ERROR;\n    }\n\n    if (sv == NULL) {\n        sv = newSVpvn((char *) handler->data, handler->len);\n    }\n\n#endif\n\n    sv = newSVpvn((char *) handler->data, handler->len);\n\n    args = &params[NGX_HTTP_PERL_SSI_ARG];\n\n    if (args[0]) {\n\n        for (i = 0; args[i]; i++) { /* void */ }\n\n        asv = ngx_pcalloc(r->pool, (i + 1) * sizeof(SV *));\n\n        if (asv == NULL) {\n            SvREFCNT_dec(sv);\n            return NGX_ERROR;\n        }\n\n        asv[0] = (SV *) (uintptr_t) i;\n\n        for (i = 0; args[i]; i++) {\n            asv[i + 1] = newSVpvn((char *) args[i]->data, args[i]->len);\n        }\n\n    } else {\n        asv = NULL;\n    }\n\n    rc = ngx_http_perl_call_handler(aTHX_ r, ctx, pmcf->nginx, sv, asv,\n                                    handler, NULL);\n\n    SvREFCNT_dec(sv);\n\n    }\n\n    ctx->filename.data = NULL;\n    ctx->redirect_uri.len = 0;\n    ctx->ssi = NULL;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, \"perl ssi done\");\n\n    return rc;\n}\n\n#endif\n\n\nstatic char *\nngx_http_perl_init_interpreter(ngx_conf_t *cf, ngx_http_perl_main_conf_t *pmcf)\n{\n    ngx_str_t           *m;\n    ngx_uint_t           i;\n#if (NGX_HAVE_PERL_MULTIPLICITY)\n    ngx_pool_cleanup_t  *cln;\n\n    cln = ngx_pool_cleanup_add(cf->pool, 0);\n    if (cln == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n#endif\n\n#ifdef NGX_PERL_MODULES\n    if (pmcf->modules == NGX_CONF_UNSET_PTR) {\n\n        pmcf->modules = ngx_array_create(cf->pool, 1, sizeof(ngx_str_t));\n        if (pmcf->modules == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        m = ngx_array_push(pmcf->modules);\n        if (m == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        ngx_str_set(m, NGX_PERL_MODULES);\n    }\n#endif\n\n    if (pmcf->modules != NGX_CONF_UNSET_PTR) {\n        m = pmcf->modules->elts;\n        for (i = 0; i < pmcf->modules->nelts; i++) {\n            if (ngx_conf_full_name(cf->cycle, &m[i], 0) != NGX_OK) {\n                return NGX_CONF_ERROR;\n            }\n        }\n    }\n\n#if !(NGX_HAVE_PERL_MULTIPLICITY)\n\n    if (perl) {\n\n        if (ngx_set_environment(cf->cycle, NULL) == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        if (ngx_http_perl_run_requires(aTHX_ pmcf->requires, cf->log)\n            != NGX_OK)\n        {\n            return NGX_CONF_ERROR;\n        }\n\n        pmcf->perl = perl;\n        pmcf->nginx = nginx_stash;\n\n        return NGX_CONF_OK;\n    }\n\n#endif\n\n    if (nginx_stash == NULL) {\n        PERL_SYS_INIT(&ngx_argc, &ngx_argv);\n    }\n\n    pmcf->perl = ngx_http_perl_create_interpreter(cf, pmcf);\n\n    if (pmcf->perl == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    pmcf->nginx = nginx_stash;\n\n#if (NGX_HAVE_PERL_MULTIPLICITY)\n\n    cln->handler = ngx_http_perl_cleanup_perl;\n    cln->data = pmcf->perl;\n\n#else\n\n    perl = pmcf->perl;\n\n#endif\n\n    return NGX_CONF_OK;\n}\n\n\nstatic PerlInterpreter *\nngx_http_perl_create_interpreter(ngx_conf_t *cf,\n    ngx_http_perl_main_conf_t *pmcf)\n{\n    int                n;\n    STRLEN             len;\n    SV                *sv;\n    char              *ver, **embedding;\n    ngx_str_t         *m;\n    ngx_uint_t         i;\n    PerlInterpreter   *perl;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cf->log, 0, \"create perl interpreter\");\n\n    if (ngx_set_environment(cf->cycle, NULL) == NULL) {\n        return NULL;\n    }\n\n    perl = perl_alloc();\n    if (perl == NULL) {\n        ngx_log_error(NGX_LOG_ALERT, cf->log, 0, \"perl_alloc() failed\");\n        return NULL;\n    }\n\n    {\n\n    dTHXa(perl);\n    PERL_SET_CONTEXT(perl);\n    PERL_SET_INTERP(perl);\n\n    perl_construct(perl);\n\n#ifdef PERL_EXIT_DESTRUCT_END\n    PL_exit_flags |= PERL_EXIT_DESTRUCT_END;\n#endif\n\n    n = (pmcf->modules != NGX_CONF_UNSET_PTR) ? pmcf->modules->nelts * 2 : 0;\n\n    embedding = ngx_palloc(cf->pool, (5 + n) * sizeof(char *));\n    if (embedding == NULL) {\n        goto fail;\n    }\n\n    embedding[0] = \"\";\n\n    if (n++) {\n        m = pmcf->modules->elts;\n        for (i = 0; i < pmcf->modules->nelts; i++) {\n            embedding[2 * i + 1] = \"-I\";\n            embedding[2 * i + 2] = (char *) m[i].data;\n        }\n    }\n\n    embedding[n++] = \"-Mnginx\";\n    embedding[n++] = \"-e\";\n    embedding[n++] = \"0\";\n    embedding[n] = NULL;\n\n    n = perl_parse(perl, ngx_http_perl_xs_init, n, embedding, NULL);\n\n    if (n != 0) {\n        ngx_log_error(NGX_LOG_ALERT, cf->log, 0, \"perl_parse() failed: %d\", n);\n        goto fail;\n    }\n\n    sv = get_sv(\"nginx::VERSION\", FALSE);\n    ver = SvPV(sv, len);\n\n    if (ngx_strcmp(ver, NGINX_VERSION) != 0) {\n        ngx_log_error(NGX_LOG_ALERT, cf->log, 0,\n                      \"version \" NGINX_VERSION \" of nginx.pm is required, \"\n                      \"but %s was found\", ver);\n        goto fail;\n    }\n\n    if (ngx_http_perl_run_requires(aTHX_ pmcf->requires, cf->log) != NGX_OK) {\n        goto fail;\n    }\n\n    }\n\n    return perl;\n\nfail:\n\n    (void) perl_destruct(perl);\n\n    perl_free(perl);\n\n    return NULL;\n}\n\n\nstatic ngx_int_t\nngx_http_perl_run_requires(pTHX_ ngx_array_t *requires, ngx_log_t *log)\n{\n    u_char      *err;\n    STRLEN       len;\n    ngx_str_t   *script;\n    ngx_uint_t   i;\n\n    if (requires == NGX_CONF_UNSET_PTR) {\n        return NGX_OK;\n    }\n\n    script = requires->elts;\n    for (i = 0; i < requires->nelts; i++) {\n\n        require_pv((char *) script[i].data);\n\n        if (SvTRUE(ERRSV)) {\n\n            err = (u_char *) SvPV(ERRSV, len);\n            while (--len && (err[len] == CR || err[len] == LF)) { /* void */ }\n\n            ngx_log_error(NGX_LOG_EMERG, log, 0,\n                          \"require_pv(\\\"%s\\\") failed: \\\"%*s\\\"\",\n                          script[i].data, len + 1, err);\n\n            return NGX_ERROR;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_perl_call_handler(pTHX_ ngx_http_request_t *r,\n    ngx_http_perl_ctx_t *ctx, HV *nginx, SV *sub, SV **args,\n    ngx_str_t *handler, ngx_str_t *rv)\n{\n    SV                *sv;\n    int                n, status;\n    char              *line;\n    u_char            *err;\n    STRLEN             len, n_a;\n    ngx_uint_t         i;\n    ngx_connection_t  *c;\n\n    dSP;\n\n    status = 0;\n\n    ctx->error = 0;\n    ctx->status = NGX_OK;\n\n    ENTER;\n    SAVETMPS;\n\n    PUSHMARK(sp);\n\n    sv = sv_2mortal(sv_bless(newRV_noinc(newSViv(PTR2IV(ctx))), nginx));\n    XPUSHs(sv);\n\n    if (args) {\n        EXTEND(sp, (intptr_t) args[0]);\n\n        for (i = 1; i <= (uintptr_t) args[0]; i++) {\n            PUSHs(sv_2mortal(args[i]));\n        }\n    }\n\n    PUTBACK;\n\n    c = r->connection;\n\n    n = call_sv(sub, G_EVAL);\n\n    SPAGAIN;\n\n    if (n) {\n        if (rv == NULL) {\n            status = POPi;\n\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                           \"call_sv: %d\", status);\n\n        } else {\n            line = SvPVx(POPs, n_a);\n            rv->len = n_a;\n\n            rv->data = ngx_pnalloc(r->pool, n_a);\n            if (rv->data == NULL) {\n                return NGX_ERROR;\n            }\n\n            ngx_memcpy(rv->data, line, n_a);\n        }\n    }\n\n    PUTBACK;\n\n    FREETMPS;\n    LEAVE;\n\n    if (ctx->error) {\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"call_sv: error, %d\", ctx->status);\n\n        if (ctx->status != NGX_OK) {\n            return ctx->status;\n        }\n\n        return NGX_ERROR;\n    }\n\n    /* check $@ */\n\n    if (SvTRUE(ERRSV)) {\n\n        err = (u_char *) SvPV(ERRSV, len);\n        while (--len && (err[len] == CR || err[len] == LF)) { /* void */ }\n\n        ngx_log_error(NGX_LOG_ERR, c->log, 0,\n                      \"call_sv(\\\"%V\\\") failed: \\\"%*s\\\"\", handler, len + 1, err);\n\n        if (rv) {\n            return NGX_ERROR;\n        }\n\n        ctx->redirect_uri.len = 0;\n\n        if (ctx->header_sent) {\n            return NGX_ERROR;\n        }\n\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    if (n != 1) {\n        ngx_log_error(NGX_LOG_ALERT, c->log, 0,\n                      \"call_sv(\\\"%V\\\") returned %d results\", handler, n);\n        status = NGX_OK;\n    }\n\n    if (rv) {\n        return NGX_OK;\n    }\n\n    return (ngx_int_t) status;\n}\n\n\nstatic void\nngx_http_perl_eval_anon_sub(pTHX_ ngx_str_t *handler, SV **sv)\n{\n    u_char  *p;\n\n    for (p = handler->data; *p; p++) {\n        if (*p != ' ' && *p != '\\t' && *p != CR && *p != LF) {\n            break;\n        }\n    }\n\n    if (ngx_strncmp(p, \"sub \", 4) == 0\n        || ngx_strncmp(p, \"sub{\", 4) == 0\n        || ngx_strncmp(p, \"use \", 4) == 0)\n    {\n        *sv = eval_pv((char *) p, FALSE);\n\n        /* eval_pv() does not set ERRSV on failure */\n\n        return;\n    }\n\n    *sv = NULL;\n}\n\n\nstatic void *\nngx_http_perl_create_main_conf(ngx_conf_t *cf)\n{\n    ngx_http_perl_main_conf_t  *pmcf;\n\n    pmcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_perl_main_conf_t));\n    if (pmcf == NULL) {\n        return NULL;\n    }\n\n    pmcf->modules = NGX_CONF_UNSET_PTR;\n    pmcf->requires = NGX_CONF_UNSET_PTR;\n\n    return pmcf;\n}\n\n\nstatic char *\nngx_http_perl_init_main_conf(ngx_conf_t *cf, void *conf)\n{\n    ngx_http_perl_main_conf_t *pmcf = conf;\n\n    if (pmcf->perl == NULL) {\n        if (ngx_http_perl_init_interpreter(cf, pmcf) != NGX_CONF_OK) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    return NGX_CONF_OK;\n}\n\n\n#if (NGX_HAVE_PERL_MULTIPLICITY)\n\nstatic void\nngx_http_perl_cleanup_perl(void *data)\n{\n    PerlInterpreter  *perl = data;\n\n    PERL_SET_CONTEXT(perl);\n    PERL_SET_INTERP(perl);\n\n    (void) perl_destruct(perl);\n\n    perl_free(perl);\n\n    if (ngx_perl_term) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, \"perl term\");\n\n        PERL_SYS_TERM();\n    }\n}\n\n#endif\n\n\nstatic ngx_int_t\nngx_http_perl_preconfiguration(ngx_conf_t *cf)\n{\n#if (NGX_HTTP_SSI)\n    ngx_int_t                  rc;\n    ngx_http_ssi_main_conf_t  *smcf;\n\n    smcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_ssi_filter_module);\n\n    rc = ngx_hash_add_key(&smcf->commands, &ngx_http_perl_ssi_command.name,\n                          &ngx_http_perl_ssi_command, NGX_HASH_READONLY_KEY);\n\n    if (rc != NGX_OK) {\n        if (rc == NGX_BUSY) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"conflicting SSI command \\\"%V\\\"\",\n                               &ngx_http_perl_ssi_command.name);\n        }\n\n        return NGX_ERROR;\n    }\n#endif\n\n    return NGX_OK;\n}\n\n\nstatic void *\nngx_http_perl_create_loc_conf(ngx_conf_t *cf)\n{\n    ngx_http_perl_loc_conf_t *plcf;\n\n    plcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_perl_loc_conf_t));\n    if (plcf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc():\n     *\n     *     plcf->handler = { 0, NULL };\n     */\n\n    return plcf;\n}\n\n\nstatic char *\nngx_http_perl_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_http_perl_loc_conf_t *prev = parent;\n    ngx_http_perl_loc_conf_t *conf = child;\n\n    if (conf->sub == NULL) {\n        conf->sub = prev->sub;\n        conf->handler = prev->handler;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_perl(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_perl_loc_conf_t *plcf = conf;\n\n    ngx_str_t                  *value;\n    ngx_http_core_loc_conf_t   *clcf;\n    ngx_http_perl_main_conf_t  *pmcf;\n\n    value = cf->args->elts;\n\n    if (plcf->handler.data) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"duplicate perl handler \\\"%V\\\"\", &value[1]);\n        return NGX_CONF_ERROR;\n    }\n\n    pmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_perl_module);\n\n    if (pmcf->perl == NULL) {\n        if (ngx_http_perl_init_interpreter(cf, pmcf) != NGX_CONF_OK) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    plcf->handler = value[1];\n\n    {\n\n    dTHXa(pmcf->perl);\n    PERL_SET_CONTEXT(pmcf->perl);\n    PERL_SET_INTERP(pmcf->perl);\n\n    ngx_http_perl_eval_anon_sub(aTHX_ &value[1], &plcf->sub);\n\n    if (plcf->sub == &PL_sv_undef) {\n        ngx_conf_log_error(NGX_LOG_ERR, cf, 0,\n                           \"eval_pv(\\\"%V\\\") failed\", &value[1]);\n        return NGX_CONF_ERROR;\n    }\n\n    if (plcf->sub == NULL) {\n        plcf->sub = newSVpvn((char *) value[1].data, value[1].len);\n    }\n\n    }\n\n    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);\n    clcf->handler = ngx_http_perl_handler;\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_perl_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_int_t                   index;\n    ngx_str_t                  *value;\n    ngx_http_variable_t        *v;\n    ngx_http_perl_variable_t   *pv;\n    ngx_http_perl_main_conf_t  *pmcf;\n\n    value = cf->args->elts;\n\n    if (value[1].data[0] != '$') {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid variable name \\\"%V\\\"\", &value[1]);\n        return NGX_CONF_ERROR;\n    }\n\n    value[1].len--;\n    value[1].data++;\n\n    v = ngx_http_add_variable(cf, &value[1], NGX_HTTP_VAR_CHANGEABLE);\n    if (v == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    pv = ngx_palloc(cf->pool, sizeof(ngx_http_perl_variable_t));\n    if (pv == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    index = ngx_http_get_variable_index(cf, &value[1]);\n    if (index == NGX_ERROR) {\n        return NGX_CONF_ERROR;\n    }\n\n    pmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_perl_module);\n\n    if (pmcf->perl == NULL) {\n        if (ngx_http_perl_init_interpreter(cf, pmcf) != NGX_CONF_OK) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    pv->handler = value[2];\n\n    {\n\n    dTHXa(pmcf->perl);\n    PERL_SET_CONTEXT(pmcf->perl);\n    PERL_SET_INTERP(pmcf->perl);\n\n    ngx_http_perl_eval_anon_sub(aTHX_ &value[2], &pv->sub);\n\n    if (pv->sub == &PL_sv_undef) {\n        ngx_conf_log_error(NGX_LOG_ERR, cf, 0,\n                           \"eval_pv(\\\"%V\\\") failed\", &value[2]);\n        return NGX_CONF_ERROR;\n    }\n\n    if (pv->sub == NULL) {\n        pv->sub = newSVpvn((char *) value[2].data, value[2].len);\n    }\n\n    }\n\n    v->get_handler = ngx_http_perl_variable;\n    v->data = (uintptr_t) pv;\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_perl_init_worker(ngx_cycle_t *cycle)\n{\n    ngx_http_perl_main_conf_t  *pmcf;\n\n    pmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_perl_module);\n\n    if (pmcf) {\n        dTHXa(pmcf->perl);\n        PERL_SET_CONTEXT(pmcf->perl);\n        PERL_SET_INTERP(pmcf->perl);\n\n        /* set worker's $$ */\n\n        sv_setiv(GvSV(gv_fetchpv(\"$\", TRUE, SVt_PV)), (I32) ngx_pid);\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_perl_exit(ngx_cycle_t *cycle)\n{\n#if (NGX_HAVE_PERL_MULTIPLICITY)\n\n    /*\n     * the master exit hook is run before global pool cleanup,\n     * therefore just set flag here\n     */\n\n    ngx_perl_term = 1;\n\n#else\n\n    if (nginx_stash) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, cycle->log, 0, \"perl term\");\n\n        (void) perl_destruct(perl);\n\n        perl_free(perl);\n\n        PERL_SYS_TERM();\n    }\n\n#endif\n}\n"
  },
  {
    "path": "src/http/modules/perl/ngx_http_perl_module.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_HTTP_PERL_MODULE_H_INCLUDED_\n#define _NGX_HTTP_PERL_MODULE_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n#include <nginx.h>\n\n#include <EXTERN.h>\n#include <perl.h>\n\n\ntypedef ngx_http_request_t   *nginx;\n\ntypedef struct {\n    ngx_http_request_t       *request;\n\n    ngx_str_t                 filename;\n    ngx_str_t                 redirect_uri;\n\n    SV                       *next;\n\n    ngx_int_t                 status;\n\n    unsigned                  done:1;\n    unsigned                  error:1;\n    unsigned                  variable:1;\n    unsigned                  header_sent:1;\n\n    ngx_array_t              *variables;  /* array of ngx_http_perl_var_t */\n\n#if (NGX_HTTP_SSI)\n    ngx_http_ssi_ctx_t       *ssi;\n#endif\n} ngx_http_perl_ctx_t;\n\n\ntypedef struct {\n    ngx_uint_t    hash;\n    ngx_str_t     name;\n    ngx_str_t     value;\n} ngx_http_perl_var_t;\n\n\nextern ngx_module_t  ngx_http_perl_module;\n\n\n/*\n * workaround for \"unused variable `Perl___notused'\" warning\n * when building with perl 5.6.1\n */\n#ifndef PERL_IMPLICIT_CONTEXT\n#undef  dTHXa\n#define dTHXa(a)\n#endif\n\n\nextern void boot_DynaLoader(pTHX_ CV* cv);\n\n\nvoid ngx_http_perl_handle_request(ngx_http_request_t *r);\nvoid ngx_http_perl_sleep_handler(ngx_http_request_t *r);\n\n\n#endif /* _NGX_HTTP_PERL_MODULE_H_INCLUDED_ */\n"
  },
  {
    "path": "src/http/modules/perl/typemap",
    "content": "TYPEMAP\n\nnginx\tT_PTROBJ\n"
  },
  {
    "path": "src/http/ngx_http.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\nstatic char *ngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\nstatic ngx_int_t ngx_http_init_phases(ngx_conf_t *cf,\n    ngx_http_core_main_conf_t *cmcf);\nstatic ngx_int_t ngx_http_init_headers_in_hash(ngx_conf_t *cf,\n    ngx_http_core_main_conf_t *cmcf);\nstatic ngx_int_t ngx_http_init_phase_handlers(ngx_conf_t *cf,\n    ngx_http_core_main_conf_t *cmcf);\n\nstatic ngx_int_t ngx_http_add_addresses(ngx_conf_t *cf,\n    ngx_http_core_srv_conf_t *cscf, ngx_http_conf_port_t *port,\n    ngx_http_listen_opt_t *lsopt);\nstatic ngx_int_t ngx_http_add_address(ngx_conf_t *cf,\n    ngx_http_core_srv_conf_t *cscf, ngx_http_conf_port_t *port,\n    ngx_http_listen_opt_t *lsopt);\nstatic ngx_int_t ngx_http_add_server(ngx_conf_t *cf,\n    ngx_http_core_srv_conf_t *cscf, ngx_http_conf_addr_t *addr);\n\nstatic char *ngx_http_merge_servers(ngx_conf_t *cf,\n    ngx_http_core_main_conf_t *cmcf, ngx_http_module_t *module,\n    ngx_uint_t ctx_index);\nstatic char *ngx_http_merge_locations(ngx_conf_t *cf,\n    ngx_queue_t *locations, void **loc_conf, ngx_http_module_t *module,\n    ngx_uint_t ctx_index);\nstatic ngx_int_t ngx_http_init_locations(ngx_conf_t *cf,\n    ngx_http_core_srv_conf_t *cscf, ngx_http_core_loc_conf_t *pclcf);\nstatic ngx_int_t ngx_http_init_static_location_trees(ngx_conf_t *cf,\n    ngx_http_core_loc_conf_t *pclcf);\nstatic ngx_int_t ngx_http_escape_location_name(ngx_conf_t *cf,\n    ngx_http_core_loc_conf_t *clcf);\nstatic ngx_int_t ngx_http_cmp_locations(const ngx_queue_t *one,\n    const ngx_queue_t *two);\nstatic ngx_int_t ngx_http_join_exact_locations(ngx_conf_t *cf,\n    ngx_queue_t *locations);\nstatic void ngx_http_create_locations_list(ngx_queue_t *locations,\n    ngx_queue_t *q);\nstatic ngx_http_location_tree_node_t *\n    ngx_http_create_locations_tree(ngx_conf_t *cf, ngx_queue_t *locations,\n    size_t prefix);\n\nstatic ngx_int_t ngx_http_optimize_servers(ngx_conf_t *cf,\n    ngx_http_core_main_conf_t *cmcf, ngx_array_t *ports);\nstatic ngx_int_t ngx_http_server_names(ngx_conf_t *cf,\n    ngx_http_core_main_conf_t *cmcf, ngx_http_conf_addr_t *addr);\nstatic ngx_int_t ngx_http_cmp_conf_addrs(const void *one, const void *two);\nstatic int ngx_libc_cdecl ngx_http_cmp_dns_wildcards(const void *one,\n    const void *two);\n\nstatic ngx_int_t ngx_http_init_listening(ngx_conf_t *cf,\n    ngx_http_conf_port_t *port);\nstatic ngx_listening_t *ngx_http_add_listening(ngx_conf_t *cf,\n    ngx_http_conf_addr_t *addr);\nstatic ngx_int_t ngx_http_add_addrs(ngx_conf_t *cf, ngx_http_port_t *hport,\n    ngx_http_conf_addr_t *addr);\n#if (NGX_HAVE_INET6)\nstatic ngx_int_t ngx_http_add_addrs6(ngx_conf_t *cf, ngx_http_port_t *hport,\n    ngx_http_conf_addr_t *addr);\n#endif\n\n#if (T_NGX_INPUT_BODY_FILTER)\nstatic ngx_int_t ngx_http_dummy_input_body_filter(ngx_http_request_t *r,\n    ngx_buf_t *buf);\n#endif\n\nngx_uint_t   ngx_http_max_module;\n\n\nngx_http_output_header_filter_pt  ngx_http_top_header_filter;\nngx_http_output_body_filter_pt    ngx_http_top_body_filter;\nngx_http_request_body_filter_pt   ngx_http_top_request_body_filter;\n\n#if (T_NGX_INPUT_BODY_FILTER)\nngx_int_t  (*ngx_http_top_input_body_filter) (ngx_http_request_t *r,\n    ngx_buf_t *buf);\n#endif\n\n\nngx_str_t  ngx_http_html_default_types[] = {\n    ngx_string(\"text/html\"),\n    ngx_null_string\n};\n\n\nstatic ngx_command_t  ngx_http_commands[] = {\n\n    { ngx_string(\"http\"),\n      NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,\n      ngx_http_block,\n      0,\n      0,\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_core_module_t  ngx_http_module_ctx = {\n    ngx_string(\"http\"),\n    NULL,\n    NULL\n};\n\n\nngx_module_t  ngx_http_module = {\n    NGX_MODULE_V1,\n    &ngx_http_module_ctx,                  /* module context */\n    ngx_http_commands,                     /* module directives */\n    NGX_CORE_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 char *\nngx_http_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    char                        *rv;\n    ngx_uint_t                   mi, m, s;\n    ngx_conf_t                   pcf;\n    ngx_http_module_t           *module;\n    ngx_http_conf_ctx_t         *ctx;\n    ngx_http_core_loc_conf_t    *clcf;\n    ngx_http_core_srv_conf_t   **cscfp;\n    ngx_http_core_main_conf_t   *cmcf;\n\n    if (*(ngx_http_conf_ctx_t **) conf) {\n        return \"is duplicate\";\n    }\n\n    /* the main http context */\n\n    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));\n    if (ctx == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    *(ngx_http_conf_ctx_t **) conf = ctx;\n\n\n    /* count the number of the http modules and set up their indices */\n\n    ngx_http_max_module = ngx_count_modules(cf->cycle, NGX_HTTP_MODULE);\n\n\n    /* the http main_conf context, it is the same in the all http contexts */\n\n    ctx->main_conf = ngx_pcalloc(cf->pool,\n                                 sizeof(void *) * ngx_http_max_module);\n    if (ctx->main_conf == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n\n    /*\n     * the http null srv_conf context, it is used to merge\n     * the server{}s' srv_conf's\n     */\n\n    ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);\n    if (ctx->srv_conf == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n\n    /*\n     * the http null loc_conf context, it is used to merge\n     * the server{}s' loc_conf's\n     */\n\n    ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);\n    if (ctx->loc_conf == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n\n    /*\n     * create the main_conf's, the null srv_conf's, and the null loc_conf's\n     * of the all http modules\n     */\n\n    for (m = 0; cf->cycle->modules[m]; m++) {\n        if (cf->cycle->modules[m]->type != NGX_HTTP_MODULE) {\n            continue;\n        }\n\n        module = cf->cycle->modules[m]->ctx;\n        mi = cf->cycle->modules[m]->ctx_index;\n\n        if (module->create_main_conf) {\n            ctx->main_conf[mi] = module->create_main_conf(cf);\n            if (ctx->main_conf[mi] == NULL) {\n                return NGX_CONF_ERROR;\n            }\n        }\n\n        if (module->create_srv_conf) {\n            ctx->srv_conf[mi] = module->create_srv_conf(cf);\n            if (ctx->srv_conf[mi] == NULL) {\n                return NGX_CONF_ERROR;\n            }\n        }\n\n        if (module->create_loc_conf) {\n            ctx->loc_conf[mi] = module->create_loc_conf(cf);\n            if (ctx->loc_conf[mi] == NULL) {\n                return NGX_CONF_ERROR;\n            }\n        }\n    }\n\n    pcf = *cf;\n    cf->ctx = ctx;\n\n#if (T_NGX_INPUT_BODY_FILTER)\n    /* init input body filter pointer */\n\n    ngx_http_top_input_body_filter = ngx_http_dummy_input_body_filter;\n#endif\n\n    for (m = 0; cf->cycle->modules[m]; m++) {\n        if (cf->cycle->modules[m]->type != NGX_HTTP_MODULE) {\n            continue;\n        }\n\n        module = cf->cycle->modules[m]->ctx;\n\n        if (module->preconfiguration) {\n            if (module->preconfiguration(cf) != NGX_OK) {\n                return NGX_CONF_ERROR;\n            }\n        }\n    }\n\n    /* parse inside the http{} block */\n\n    cf->module_type = NGX_HTTP_MODULE;\n    cf->cmd_type = NGX_HTTP_MAIN_CONF;\n    rv = ngx_conf_parse(cf, NULL);\n\n    if (rv != NGX_CONF_OK) {\n        goto failed;\n    }\n\n    /*\n     * init http{} main_conf's, merge the server{}s' srv_conf's\n     * and its location{}s' loc_conf's\n     */\n\n    cmcf = ctx->main_conf[ngx_http_core_module.ctx_index];\n    cscfp = cmcf->servers.elts;\n\n    for (m = 0; cf->cycle->modules[m]; m++) {\n        if (cf->cycle->modules[m]->type != NGX_HTTP_MODULE) {\n            continue;\n        }\n\n        module = cf->cycle->modules[m]->ctx;\n        mi = cf->cycle->modules[m]->ctx_index;\n\n        /* init http{} main_conf's */\n\n        if (module->init_main_conf) {\n            rv = module->init_main_conf(cf, ctx->main_conf[mi]);\n            if (rv != NGX_CONF_OK) {\n                goto failed;\n            }\n        }\n\n        rv = ngx_http_merge_servers(cf, cmcf, module, mi);\n        if (rv != NGX_CONF_OK) {\n            goto failed;\n        }\n    }\n\n\n    /* create location trees */\n\n    for (s = 0; s < cmcf->servers.nelts; s++) {\n\n        clcf = cscfp[s]->ctx->loc_conf[ngx_http_core_module.ctx_index];\n\n        if (ngx_http_init_locations(cf, cscfp[s], clcf) != NGX_OK) {\n            return NGX_CONF_ERROR;\n        }\n\n        if (ngx_http_init_static_location_trees(cf, clcf) != NGX_OK) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n\n    if (ngx_http_init_phases(cf, cmcf) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (ngx_http_init_headers_in_hash(cf, cmcf) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n\n    for (m = 0; cf->cycle->modules[m]; m++) {\n        if (cf->cycle->modules[m]->type != NGX_HTTP_MODULE) {\n            continue;\n        }\n\n        module = cf->cycle->modules[m]->ctx;\n\n        if (module->postconfiguration) {\n            if (module->postconfiguration(cf) != NGX_OK) {\n                return NGX_CONF_ERROR;\n            }\n        }\n    }\n\n    if (ngx_http_variables_init_vars(cf) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    /*\n     * http{}'s cf->ctx was needed while the configuration merging\n     * and in postconfiguration process\n     */\n\n    *cf = pcf;\n\n\n    if (ngx_http_init_phase_handlers(cf, cmcf) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n\n    /* optimize the lists of ports, addresses and server names */\n\n    if (ngx_http_optimize_servers(cf, cmcf, cmcf->ports) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n\nfailed:\n\n    *cf = pcf;\n\n    return rv;\n}\n\n\nstatic ngx_int_t\nngx_http_init_phases(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf)\n{\n    if (ngx_array_init(&cmcf->phases[NGX_HTTP_POST_READ_PHASE].handlers,\n                       cf->pool, 1, sizeof(ngx_http_handler_pt))\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    if (ngx_array_init(&cmcf->phases[NGX_HTTP_SERVER_REWRITE_PHASE].handlers,\n                       cf->pool, 1, sizeof(ngx_http_handler_pt))\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    if (ngx_array_init(&cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers,\n                       cf->pool, 1, sizeof(ngx_http_handler_pt))\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    if (ngx_array_init(&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers,\n                       cf->pool, 1, sizeof(ngx_http_handler_pt))\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    if (ngx_array_init(&cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers,\n                       cf->pool, 2, sizeof(ngx_http_handler_pt))\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    if (ngx_array_init(&cmcf->phases[NGX_HTTP_PRECONTENT_PHASE].handlers,\n                       cf->pool, 2, sizeof(ngx_http_handler_pt))\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    if (ngx_array_init(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers,\n                       cf->pool, 4, sizeof(ngx_http_handler_pt))\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    if (ngx_array_init(&cmcf->phases[NGX_HTTP_LOG_PHASE].handlers,\n                       cf->pool, 1, sizeof(ngx_http_handler_pt))\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_init_headers_in_hash(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf)\n{\n    ngx_array_t         headers_in;\n    ngx_hash_key_t     *hk;\n    ngx_hash_init_t     hash;\n    ngx_http_header_t  *header;\n\n    if (ngx_array_init(&headers_in, cf->temp_pool, 32, sizeof(ngx_hash_key_t))\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    for (header = ngx_http_headers_in; header->name.len; header++) {\n        hk = ngx_array_push(&headers_in);\n        if (hk == NULL) {\n            return NGX_ERROR;\n        }\n\n        hk->key = header->name;\n        hk->key_hash = ngx_hash_key_lc(header->name.data, header->name.len);\n        hk->value = header;\n    }\n\n    hash.hash = &cmcf->headers_in_hash;\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 = \"headers_in_hash\";\n    hash.pool = cf->pool;\n    hash.temp_pool = NULL;\n\n    if (ngx_hash_init(&hash, headers_in.elts, headers_in.nelts) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_init_phase_handlers(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf)\n{\n    ngx_int_t                   j;\n    ngx_uint_t                  i, n;\n    ngx_uint_t                  find_config_index, use_rewrite, use_access;\n    ngx_http_handler_pt        *h;\n    ngx_http_phase_handler_t   *ph;\n    ngx_http_phase_handler_pt   checker;\n\n    cmcf->phase_engine.server_rewrite_index = (ngx_uint_t) -1;\n    cmcf->phase_engine.location_rewrite_index = (ngx_uint_t) -1;\n    find_config_index = 0;\n    use_rewrite = cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers.nelts ? 1 : 0;\n    use_access = cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers.nelts ? 1 : 0;\n\n    n = 1                  /* find config phase */\n        + use_rewrite      /* post rewrite phase */\n        + use_access;      /* post access phase */\n\n    for (i = 0; i < NGX_HTTP_LOG_PHASE; i++) {\n        n += cmcf->phases[i].handlers.nelts;\n    }\n\n    ph = ngx_pcalloc(cf->pool,\n                     n * sizeof(ngx_http_phase_handler_t) + sizeof(void *));\n    if (ph == NULL) {\n        return NGX_ERROR;\n    }\n\n    cmcf->phase_engine.handlers = ph;\n    n = 0;\n\n    for (i = 0; i < NGX_HTTP_LOG_PHASE; i++) {\n        h = cmcf->phases[i].handlers.elts;\n\n        switch (i) {\n\n        case NGX_HTTP_SERVER_REWRITE_PHASE:\n            if (cmcf->phase_engine.server_rewrite_index == (ngx_uint_t) -1) {\n                cmcf->phase_engine.server_rewrite_index = n;\n            }\n            checker = ngx_http_core_rewrite_phase;\n\n            break;\n\n        case NGX_HTTP_FIND_CONFIG_PHASE:\n            find_config_index = n;\n\n            ph->checker = ngx_http_core_find_config_phase;\n            n++;\n            ph++;\n\n            continue;\n\n        case NGX_HTTP_REWRITE_PHASE:\n            if (cmcf->phase_engine.location_rewrite_index == (ngx_uint_t) -1) {\n                cmcf->phase_engine.location_rewrite_index = n;\n            }\n            checker = ngx_http_core_rewrite_phase;\n\n            break;\n\n        case NGX_HTTP_POST_REWRITE_PHASE:\n            if (use_rewrite) {\n                ph->checker = ngx_http_core_post_rewrite_phase;\n                ph->next = find_config_index;\n                n++;\n                ph++;\n            }\n\n            continue;\n\n        case NGX_HTTP_ACCESS_PHASE:\n            checker = ngx_http_core_access_phase;\n            n++;\n            break;\n\n        case NGX_HTTP_POST_ACCESS_PHASE:\n            if (use_access) {\n                ph->checker = ngx_http_core_post_access_phase;\n                ph->next = n;\n                ph++;\n            }\n\n            continue;\n\n        case NGX_HTTP_CONTENT_PHASE:\n            checker = ngx_http_core_content_phase;\n            break;\n\n        default:\n            checker = ngx_http_core_generic_phase;\n        }\n\n        n += cmcf->phases[i].handlers.nelts;\n\n        for (j = cmcf->phases[i].handlers.nelts - 1; j >= 0; j--) {\n            ph->checker = checker;\n            ph->handler = h[j];\n            ph->next = n;\n            ph++;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nstatic char *\nngx_http_merge_servers(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf,\n    ngx_http_module_t *module, ngx_uint_t ctx_index)\n{\n    char                        *rv;\n    ngx_uint_t                   s;\n    ngx_http_conf_ctx_t         *ctx, saved;\n    ngx_http_core_loc_conf_t    *clcf;\n    ngx_http_core_srv_conf_t   **cscfp;\n\n    cscfp = cmcf->servers.elts;\n    ctx = (ngx_http_conf_ctx_t *) cf->ctx;\n    saved = *ctx;\n    rv = NGX_CONF_OK;\n\n    for (s = 0; s < cmcf->servers.nelts; s++) {\n\n        /* merge the server{}s' srv_conf's */\n\n        ctx->srv_conf = cscfp[s]->ctx->srv_conf;\n\n        if (module->merge_srv_conf) {\n            rv = module->merge_srv_conf(cf, saved.srv_conf[ctx_index],\n                                        cscfp[s]->ctx->srv_conf[ctx_index]);\n            if (rv != NGX_CONF_OK) {\n                goto failed;\n            }\n        }\n\n        if (module->merge_loc_conf) {\n\n            /* merge the server{}'s loc_conf */\n\n            ctx->loc_conf = cscfp[s]->ctx->loc_conf;\n\n            rv = module->merge_loc_conf(cf, saved.loc_conf[ctx_index],\n                                        cscfp[s]->ctx->loc_conf[ctx_index]);\n            if (rv != NGX_CONF_OK) {\n                goto failed;\n            }\n\n            /* merge the locations{}' loc_conf's */\n\n            clcf = cscfp[s]->ctx->loc_conf[ngx_http_core_module.ctx_index];\n\n            rv = ngx_http_merge_locations(cf, clcf->locations,\n                                          cscfp[s]->ctx->loc_conf,\n                                          module, ctx_index);\n            if (rv != NGX_CONF_OK) {\n                goto failed;\n            }\n        }\n    }\n\nfailed:\n\n    *ctx = saved;\n\n    return rv;\n}\n\n\nstatic char *\nngx_http_merge_locations(ngx_conf_t *cf, ngx_queue_t *locations,\n    void **loc_conf, ngx_http_module_t *module, ngx_uint_t ctx_index)\n{\n    char                       *rv;\n    ngx_queue_t                *q;\n    ngx_http_conf_ctx_t        *ctx, saved;\n    ngx_http_core_loc_conf_t   *clcf;\n    ngx_http_location_queue_t  *lq;\n\n    if (locations == NULL) {\n        return NGX_CONF_OK;\n    }\n\n    ctx = (ngx_http_conf_ctx_t *) cf->ctx;\n    saved = *ctx;\n\n    for (q = ngx_queue_head(locations);\n         q != ngx_queue_sentinel(locations);\n         q = ngx_queue_next(q))\n    {\n        lq = (ngx_http_location_queue_t *) q;\n\n        clcf = lq->exact ? lq->exact : lq->inclusive;\n        ctx->loc_conf = clcf->loc_conf;\n\n        rv = module->merge_loc_conf(cf, loc_conf[ctx_index],\n                                    clcf->loc_conf[ctx_index]);\n        if (rv != NGX_CONF_OK) {\n            return rv;\n        }\n\n        rv = ngx_http_merge_locations(cf, clcf->locations, clcf->loc_conf,\n                                      module, ctx_index);\n        if (rv != NGX_CONF_OK) {\n            return rv;\n        }\n    }\n\n    *ctx = saved;\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_init_locations(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,\n    ngx_http_core_loc_conf_t *pclcf)\n{\n    ngx_uint_t                   n;\n    ngx_queue_t                 *q, *locations, *named, tail;\n    ngx_http_core_loc_conf_t    *clcf;\n    ngx_http_location_queue_t   *lq;\n    ngx_http_core_loc_conf_t   **clcfp;\n#if (NGX_PCRE)\n    ngx_uint_t                   r;\n    ngx_queue_t                 *regex;\n#endif\n\n    locations = pclcf->locations;\n\n    if (locations == NULL) {\n        return NGX_OK;\n    }\n\n    ngx_queue_sort(locations, ngx_http_cmp_locations);\n\n    named = NULL;\n    n = 0;\n#if (NGX_PCRE)\n    regex = NULL;\n    r = 0;\n#endif\n\n    for (q = ngx_queue_head(locations);\n         q != ngx_queue_sentinel(locations);\n         q = ngx_queue_next(q))\n    {\n        lq = (ngx_http_location_queue_t *) q;\n\n        clcf = lq->exact ? lq->exact : lq->inclusive;\n\n        if (ngx_http_init_locations(cf, NULL, clcf) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n#if (NGX_PCRE)\n\n        if (clcf->regex) {\n            r++;\n\n            if (regex == NULL) {\n                regex = q;\n            }\n\n            continue;\n        }\n\n#endif\n\n        if (clcf->named) {\n            n++;\n\n            if (named == NULL) {\n                named = q;\n            }\n\n            continue;\n        }\n\n        if (clcf->noname) {\n            break;\n        }\n    }\n\n    if (q != ngx_queue_sentinel(locations)) {\n        ngx_queue_split(locations, q, &tail);\n    }\n\n    if (named) {\n        clcfp = ngx_palloc(cf->pool,\n                           (n + 1) * sizeof(ngx_http_core_loc_conf_t *));\n        if (clcfp == NULL) {\n            return NGX_ERROR;\n        }\n\n        cscf->named_locations = clcfp;\n\n        for (q = named;\n             q != ngx_queue_sentinel(locations);\n             q = ngx_queue_next(q))\n        {\n            lq = (ngx_http_location_queue_t *) q;\n\n            *(clcfp++) = lq->exact;\n        }\n\n        *clcfp = NULL;\n\n        ngx_queue_split(locations, named, &tail);\n    }\n\n#if (NGX_PCRE)\n\n    if (regex) {\n\n        clcfp = ngx_palloc(cf->pool,\n                           (r + 1) * sizeof(ngx_http_core_loc_conf_t *));\n        if (clcfp == NULL) {\n            return NGX_ERROR;\n        }\n\n        pclcf->regex_locations = clcfp;\n\n        for (q = regex;\n             q != ngx_queue_sentinel(locations);\n             q = ngx_queue_next(q))\n        {\n            lq = (ngx_http_location_queue_t *) q;\n\n            *(clcfp++) = lq->exact;\n        }\n\n        *clcfp = NULL;\n\n        ngx_queue_split(locations, regex, &tail);\n    }\n\n#endif\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_init_static_location_trees(ngx_conf_t *cf,\n    ngx_http_core_loc_conf_t *pclcf)\n{\n    ngx_queue_t                *q, *locations;\n    ngx_http_core_loc_conf_t   *clcf;\n    ngx_http_location_queue_t  *lq;\n\n    locations = pclcf->locations;\n\n    if (locations == NULL) {\n        return NGX_OK;\n    }\n\n    if (ngx_queue_empty(locations)) {\n        return NGX_OK;\n    }\n\n    for (q = ngx_queue_head(locations);\n         q != ngx_queue_sentinel(locations);\n         q = ngx_queue_next(q))\n    {\n        lq = (ngx_http_location_queue_t *) q;\n\n        clcf = lq->exact ? lq->exact : lq->inclusive;\n\n        if (ngx_http_init_static_location_trees(cf, clcf) != NGX_OK) {\n            return NGX_ERROR;\n        }\n    }\n\n    if (ngx_http_join_exact_locations(cf, locations) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    ngx_http_create_locations_list(locations, ngx_queue_head(locations));\n\n    pclcf->static_locations = ngx_http_create_locations_tree(cf, locations, 0);\n    if (pclcf->static_locations == NULL) {\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_add_location(ngx_conf_t *cf, ngx_queue_t **locations,\n    ngx_http_core_loc_conf_t *clcf)\n{\n    ngx_http_location_queue_t  *lq;\n\n    if (*locations == NULL) {\n        *locations = ngx_palloc(cf->temp_pool,\n                                sizeof(ngx_http_location_queue_t));\n        if (*locations == NULL) {\n            return NGX_ERROR;\n        }\n\n        ngx_queue_init(*locations);\n    }\n\n    lq = ngx_palloc(cf->temp_pool, sizeof(ngx_http_location_queue_t));\n    if (lq == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (clcf->exact_match\n#if (NGX_PCRE)\n        || clcf->regex\n#endif\n        || clcf->named || clcf->noname)\n    {\n        lq->exact = clcf;\n        lq->inclusive = NULL;\n\n    } else {\n        lq->exact = NULL;\n        lq->inclusive = clcf;\n    }\n\n    lq->name = &clcf->name;\n    lq->file_name = cf->conf_file->file.name.data;\n    lq->line = cf->conf_file->line;\n\n    ngx_queue_init(&lq->list);\n\n    ngx_queue_insert_tail(*locations, &lq->queue);\n\n    if (ngx_http_escape_location_name(cf, clcf) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_escape_location_name(ngx_conf_t *cf, ngx_http_core_loc_conf_t *clcf)\n{\n    u_char     *p;\n    size_t      len;\n    uintptr_t   escape;\n\n    escape = 2 * ngx_escape_uri(NULL, clcf->name.data, clcf->name.len,\n                                NGX_ESCAPE_URI);\n\n    if (escape) {\n        len = clcf->name.len + escape;\n\n        p = ngx_pnalloc(cf->pool, len);\n        if (p == NULL) {\n            return NGX_ERROR;\n        }\n\n        clcf->escaped_name.len = len;\n        clcf->escaped_name.data = p;\n\n        ngx_escape_uri(p, clcf->name.data, clcf->name.len, NGX_ESCAPE_URI);\n\n    } else {\n        clcf->escaped_name = clcf->name;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_cmp_locations(const ngx_queue_t *one, const ngx_queue_t *two)\n{\n    ngx_int_t                   rc;\n    ngx_http_core_loc_conf_t   *first, *second;\n    ngx_http_location_queue_t  *lq1, *lq2;\n\n    lq1 = (ngx_http_location_queue_t *) one;\n    lq2 = (ngx_http_location_queue_t *) two;\n\n    first = lq1->exact ? lq1->exact : lq1->inclusive;\n    second = lq2->exact ? lq2->exact : lq2->inclusive;\n\n    if (first->noname && !second->noname) {\n        /* shift no named locations to the end */\n        return 1;\n    }\n\n    if (!first->noname && second->noname) {\n        /* shift no named locations to the end */\n        return -1;\n    }\n\n    if (first->noname || second->noname) {\n        /* do not sort no named locations */\n        return 0;\n    }\n\n    if (first->named && !second->named) {\n        /* shift named locations to the end */\n        return 1;\n    }\n\n    if (!first->named && second->named) {\n        /* shift named locations to the end */\n        return -1;\n    }\n\n    if (first->named && second->named) {\n        return ngx_strcmp(first->name.data, second->name.data);\n    }\n\n#if (NGX_PCRE)\n\n    if (first->regex && !second->regex) {\n        /* shift the regex matches to the end */\n        return 1;\n    }\n\n    if (!first->regex && second->regex) {\n        /* shift the regex matches to the end */\n        return -1;\n    }\n\n    if (first->regex || second->regex) {\n        /* do not sort the regex matches */\n        return 0;\n    }\n\n#endif\n\n    rc = ngx_filename_cmp(first->name.data, second->name.data,\n                          ngx_min(first->name.len, second->name.len) + 1);\n\n    if (rc == 0 && !first->exact_match && second->exact_match) {\n        /* an exact match must be before the same inclusive one */\n        return 1;\n    }\n\n    return rc;\n}\n\n\nstatic ngx_int_t\nngx_http_join_exact_locations(ngx_conf_t *cf, ngx_queue_t *locations)\n{\n    ngx_queue_t                *q, *x;\n    ngx_http_location_queue_t  *lq, *lx;\n\n    q = ngx_queue_head(locations);\n\n    while (q != ngx_queue_last(locations)) {\n\n        x = ngx_queue_next(q);\n\n        lq = (ngx_http_location_queue_t *) q;\n        lx = (ngx_http_location_queue_t *) x;\n\n        if (lq->name->len == lx->name->len\n            && ngx_filename_cmp(lq->name->data, lx->name->data, lx->name->len)\n               == 0)\n        {\n            if ((lq->exact && lx->exact) || (lq->inclusive && lx->inclusive)) {\n                ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                              \"duplicate location \\\"%V\\\" in %s:%ui\",\n                              lx->name, lx->file_name, lx->line);\n\n                return NGX_ERROR;\n            }\n\n            lq->inclusive = lx->inclusive;\n\n            ngx_queue_remove(x);\n\n            continue;\n        }\n\n        q = ngx_queue_next(q);\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_create_locations_list(ngx_queue_t *locations, ngx_queue_t *q)\n{\n    u_char                     *name;\n    size_t                      len;\n    ngx_queue_t                *x, tail;\n    ngx_http_location_queue_t  *lq, *lx;\n\n    if (q == ngx_queue_last(locations)) {\n        return;\n    }\n\n    lq = (ngx_http_location_queue_t *) q;\n\n    if (lq->inclusive == NULL) {\n        ngx_http_create_locations_list(locations, ngx_queue_next(q));\n        return;\n    }\n\n    len = lq->name->len;\n    name = lq->name->data;\n\n    for (x = ngx_queue_next(q);\n         x != ngx_queue_sentinel(locations);\n         x = ngx_queue_next(x))\n    {\n        lx = (ngx_http_location_queue_t *) x;\n\n        if (len > lx->name->len\n            || ngx_filename_cmp(name, lx->name->data, len) != 0)\n        {\n            break;\n        }\n    }\n\n    q = ngx_queue_next(q);\n\n    if (q == x) {\n        ngx_http_create_locations_list(locations, x);\n        return;\n    }\n\n    ngx_queue_split(locations, q, &tail);\n    ngx_queue_add(&lq->list, &tail);\n\n    if (x == ngx_queue_sentinel(locations)) {\n        ngx_http_create_locations_list(&lq->list, ngx_queue_head(&lq->list));\n        return;\n    }\n\n    ngx_queue_split(&lq->list, x, &tail);\n    ngx_queue_add(locations, &tail);\n\n    ngx_http_create_locations_list(&lq->list, ngx_queue_head(&lq->list));\n\n    ngx_http_create_locations_list(locations, x);\n}\n\n\n/*\n * to keep cache locality for left leaf nodes, allocate nodes in following\n * order: node, left subtree, right subtree, inclusive subtree\n */\n\nstatic ngx_http_location_tree_node_t *\nngx_http_create_locations_tree(ngx_conf_t *cf, ngx_queue_t *locations,\n    size_t prefix)\n{\n    size_t                          len;\n    ngx_queue_t                    *q, tail;\n    ngx_http_location_queue_t      *lq;\n    ngx_http_location_tree_node_t  *node;\n\n    q = ngx_queue_middle(locations);\n\n    lq = (ngx_http_location_queue_t *) q;\n    len = lq->name->len - prefix;\n\n    node = ngx_palloc(cf->pool,\n                      offsetof(ngx_http_location_tree_node_t, name) + len);\n    if (node == NULL) {\n        return NULL;\n    }\n\n    node->left = NULL;\n    node->right = NULL;\n    node->tree = NULL;\n    node->exact = lq->exact;\n    node->inclusive = lq->inclusive;\n\n    node->auto_redirect = (u_char) ((lq->exact && lq->exact->auto_redirect)\n                           || (lq->inclusive && lq->inclusive->auto_redirect));\n\n    node->len = (u_short) len;\n    ngx_memcpy(node->name, &lq->name->data[prefix], len);\n\n    ngx_queue_split(locations, q, &tail);\n\n    if (ngx_queue_empty(locations)) {\n        /*\n         * ngx_queue_split() insures that if left part is empty,\n         * then right one is empty too\n         */\n        goto inclusive;\n    }\n\n    node->left = ngx_http_create_locations_tree(cf, locations, prefix);\n    if (node->left == NULL) {\n        return NULL;\n    }\n\n    ngx_queue_remove(q);\n\n    if (ngx_queue_empty(&tail)) {\n        goto inclusive;\n    }\n\n    node->right = ngx_http_create_locations_tree(cf, &tail, prefix);\n    if (node->right == NULL) {\n        return NULL;\n    }\n\ninclusive:\n\n    if (ngx_queue_empty(&lq->list)) {\n        return node;\n    }\n\n    node->tree = ngx_http_create_locations_tree(cf, &lq->list, prefix + len);\n    if (node->tree == NULL) {\n        return NULL;\n    }\n\n    return node;\n}\n\n\nngx_int_t\nngx_http_add_listen(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,\n    ngx_http_listen_opt_t *lsopt)\n{\n    in_port_t                   p;\n    ngx_uint_t                  i;\n    struct sockaddr            *sa;\n    ngx_http_conf_port_t       *port;\n    ngx_http_core_main_conf_t  *cmcf;\n#if (T_NGX_HAVE_XUDP)\n    ngx_http_conf_addr_t       *addr;\n    ngx_uint_t                  idx;\n#endif\n\n    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);\n\n    if (cmcf->ports == NULL) {\n        cmcf->ports = ngx_array_create(cf->temp_pool, 2,\n                                       sizeof(ngx_http_conf_port_t));\n        if (cmcf->ports == NULL) {\n            return NGX_ERROR;\n        }\n    }\n\n    sa = lsopt->sockaddr;\n    p = ngx_inet_get_port(sa);\n\n    port = cmcf->ports->elts;\n    for (i = 0; i < cmcf->ports->nelts; i++) {\n\n        if (p != port[i].port || sa->sa_family != port[i].family) {\n            continue;\n        }\n\n#if (T_NGX_XQUIC)\n        if ((port[i].udp && !lsopt->xquic) || (!port[i].udp && lsopt->xquic)) {\n            continue;\n        }\n#endif\n#if (T_NGX_HAVE_XUDP)\n        /* if wildcard is xudp, all the address will be xudp */\n        if (lsopt->wildcard && lsopt->xudp) {\n\n            u_char      text[NGX_SOCKADDR_STRLEN];\n            ngx_str_t   addr_str;\n\n            addr_str.data   = text;\n\n            if (!port[i].xudp) {\n                port[i].xudp = 1;\n                ngx_memory_barrier();\n                addr = (ngx_http_conf_addr_t*) port[i].addrs.elts;\n                for(idx = 0; idx < port[i].addrs.nelts; idx++) {\n                    /* force xudp */\n                    addr[idx].opt.xudp = 1;\n                    /* log this  */\n                    addr_str.len  = ngx_sock_ntop(addr[idx].opt.sockaddr, addr[idx].opt.socklen, addr_str.data, NGX_SOCKADDR_STRLEN, 1);\n                    ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0,\n                                \"xudp wildcard address force all address with same port[%V] xudp on\", &addr_str);\n                }\n            }\n        }\n#endif\n\n        /* a port is already in the port list */\n\n        return ngx_http_add_addresses(cf, cscf, &port[i], lsopt);\n    }\n\n    /* add a port to the port list */\n\n    port = ngx_array_push(cmcf->ports);\n    if (port == NULL) {\n        return NGX_ERROR;\n    }\n\n    port->family = sa->sa_family;\n    port->port = p;\n    port->addrs.elts = NULL;\n#if (T_NGX_XQUIC)\n    port->udp = lsopt->xquic;\n#endif\n#if (T_NGX_HAVE_XUDP)\n    port->xudp = !!(lsopt->wildcard && lsopt->xudp);\n#endif\n\n    return ngx_http_add_address(cf, cscf, port, lsopt);\n}\n\n\nstatic ngx_int_t\nngx_http_add_addresses(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,\n    ngx_http_conf_port_t *port, ngx_http_listen_opt_t *lsopt)\n{\n    ngx_uint_t             i, default_server, proxy_protocol,\n                           protocols, protocols_prev;\n    ngx_http_conf_addr_t  *addr;\n#if (NGX_HTTP_SSL)\n    ngx_uint_t             ssl;\n#endif\n#if (NGX_HTTP_V2)\n    ngx_uint_t             http2;\n#endif\n#if (T_NGX_XQUIC)\n    ngx_uint_t             xquic;\n#endif\n#if (T_NGX_HAVE_XUDP)\n    ngx_uint_t             xudp;\n#endif\n#if (T_NGX_HTTPS_ALLOW_HTTP)\n    ngx_uint_t             https_allow_http;\n#endif\n\n    /*\n     * we cannot compare whole sockaddr struct's as kernel\n     * may fill some fields in inherited sockaddr struct's\n     */\n\n    addr = port->addrs.elts;\n\n    for (i = 0; i < port->addrs.nelts; i++) {\n\n        if (ngx_cmp_sockaddr(lsopt->sockaddr, lsopt->socklen,\n                             addr[i].opt.sockaddr,\n                             addr[i].opt.socklen, 0)\n            != NGX_OK)\n        {\n            continue;\n        }\n\n        /* the address is already in the address list */\n\n        if (ngx_http_add_server(cf, cscf, &addr[i]) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        /* preserve default_server bit during listen options overwriting */\n        default_server = addr[i].opt.default_server;\n\n        proxy_protocol = lsopt->proxy_protocol || addr[i].opt.proxy_protocol;\n        protocols = lsopt->proxy_protocol;\n        protocols_prev = addr[i].opt.proxy_protocol;\n\n#if (NGX_HTTP_SSL)\n        ssl = lsopt->ssl || addr[i].opt.ssl;\n        protocols |= lsopt->ssl << 1;\n        protocols_prev |= addr[i].opt.ssl << 1;\n#endif\n#if (NGX_HTTP_V2)\n        http2 = lsopt->http2 || addr[i].opt.http2;\n        protocols |= lsopt->http2 << 2;\n        protocols_prev |= addr[i].opt.http2 << 2;\n#endif\n#if (T_NGX_XQUIC)\n        xquic = lsopt->xquic || addr[i].opt.xquic;\n        protocols |= lsopt->xquic << 3;\n        protocols_prev |= addr[i].opt.xquic << 3;\n#endif\n#if (T_NGX_HAVE_XUDP)\n        xudp = lsopt->xudp || addr[i].opt.xudp || port->xudp;\n        protocols |= lsopt->xudp << 4;\n        protocols_prev |= addr[i].opt.xudp << 4;\n#endif\n#if (T_NGX_HTTPS_ALLOW_HTTP)\n        https_allow_http = lsopt->https_allow_http || addr[i].opt.https_allow_http;\n#endif\n\n        if (lsopt->set) {\n\n            if (addr[i].opt.set) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"duplicate listen options for %V\",\n                                   &addr[i].opt.addr_text);\n                return NGX_ERROR;\n            }\n\n            addr[i].opt = *lsopt;\n        }\n\n        /* check the duplicate \"default\" server for this address:port */\n\n        if (lsopt->default_server) {\n\n            if (default_server) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"a duplicate default server for %V\",\n                                   &addr[i].opt.addr_text);\n                return NGX_ERROR;\n            }\n\n            default_server = 1;\n            addr[i].default_server = cscf;\n        }\n\n        /* check for conflicting protocol options */\n\n        if ((protocols | protocols_prev) != protocols_prev) {\n\n            /* options added */\n\n            if ((addr[i].opt.set && !lsopt->set)\n                || addr[i].protocols_changed\n                || (protocols | protocols_prev) != protocols)\n            {\n                ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                                   \"protocol options redefined for %V\",\n                                   &addr[i].opt.addr_text);\n            }\n\n            addr[i].protocols = protocols_prev;\n            addr[i].protocols_set = 1;\n            addr[i].protocols_changed = 1;\n\n        } else if ((protocols_prev | protocols) != protocols) {\n\n            /* options removed */\n\n            if (lsopt->set\n                || (addr[i].protocols_set && protocols != addr[i].protocols))\n            {\n                ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                                   \"protocol options redefined for %V\",\n                                   &addr[i].opt.addr_text);\n            }\n\n            addr[i].protocols = protocols;\n            addr[i].protocols_set = 1;\n            addr[i].protocols_changed = 1;\n\n        } else {\n\n            /* the same options */\n\n            if ((lsopt->set && addr[i].protocols_changed)\n                || (addr[i].protocols_set && protocols != addr[i].protocols))\n            {\n                ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                                   \"protocol options redefined for %V\",\n                                   &addr[i].opt.addr_text);\n            }\n\n            addr[i].protocols = protocols;\n            addr[i].protocols_set = 1;\n        }\n\n        addr[i].opt.default_server = default_server;\n        addr[i].opt.proxy_protocol = proxy_protocol;\n#if (NGX_HTTP_SSL)\n        addr[i].opt.ssl = ssl;\n#endif\n#if (NGX_HTTP_V2)\n        addr[i].opt.http2 = http2;\n#endif\n#if (T_NGX_XQUIC)\n        addr[i].opt.xquic = xquic;\n#endif\n#if (T_NGX_HAVE_XUDP)\n        addr[i].opt.xudp = xudp;\n#endif\n#if (T_NGX_HTTPS_ALLOW_HTTP)\n        addr[i].opt.https_allow_http = https_allow_http;\n#endif\n\n        return NGX_OK;\n    }\n\n    /* add the address to the addresses list that bound to this port */\n\n    return ngx_http_add_address(cf, cscf, port, lsopt);\n}\n\n\n/*\n * add the server address, the server names and the server core module\n * configurations to the port list\n */\n\nstatic ngx_int_t\nngx_http_add_address(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,\n    ngx_http_conf_port_t *port, ngx_http_listen_opt_t *lsopt)\n{\n    ngx_http_conf_addr_t  *addr;\n\n    if (port->addrs.elts == NULL) {\n        if (ngx_array_init(&port->addrs, cf->temp_pool, 4,\n                           sizeof(ngx_http_conf_addr_t))\n            != NGX_OK)\n        {\n            return NGX_ERROR;\n        }\n    }\n\n#if (NGX_HTTP_V2 && NGX_HTTP_SSL                                              \\\n     && !defined TLSEXT_TYPE_application_layer_protocol_negotiation)\n\n    if (lsopt->http2 && lsopt->ssl) {\n        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                           \"nginx was built with OpenSSL that lacks ALPN \"\n                           \"support, HTTP/2 is not enabled for %V\",\n                           &lsopt->addr_text);\n    }\n\n#endif\n\n    addr = ngx_array_push(&port->addrs);\n    if (addr == NULL) {\n        return NGX_ERROR;\n    }\n\n    addr->opt = *lsopt;\n    addr->protocols = 0;\n    addr->protocols_set = 0;\n    addr->protocols_changed = 0;\n    addr->hash.buckets = NULL;\n    addr->hash.size = 0;\n    addr->wc_head = NULL;\n    addr->wc_tail = NULL;\n#if (NGX_PCRE)\n    addr->nregex = 0;\n    addr->regex = NULL;\n#endif\n    addr->default_server = cscf;\n    addr->servers.elts = NULL;\n#if (T_NGX_HAVE_XUDP)\n    addr->opt.xudp = addr->opt.xudp || port->xudp;\n#endif\n\n    return ngx_http_add_server(cf, cscf, addr);\n}\n\n\n/* add the server core module configuration to the address:port */\n\nstatic ngx_int_t\nngx_http_add_server(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,\n    ngx_http_conf_addr_t *addr)\n{\n    ngx_uint_t                  i;\n    ngx_http_core_srv_conf_t  **server;\n\n    if (addr->servers.elts == NULL) {\n        if (ngx_array_init(&addr->servers, cf->temp_pool, 4,\n                           sizeof(ngx_http_core_srv_conf_t *))\n            != NGX_OK)\n        {\n            return NGX_ERROR;\n        }\n\n    } else {\n        server = addr->servers.elts;\n        for (i = 0; i < addr->servers.nelts; i++) {\n            if (server[i] == cscf) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"a duplicate listen %V\",\n                                   &addr->opt.addr_text);\n                return NGX_ERROR;\n            }\n        }\n    }\n\n    server = ngx_array_push(&addr->servers);\n    if (server == NULL) {\n        return NGX_ERROR;\n    }\n\n    *server = cscf;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_optimize_servers(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf,\n    ngx_array_t *ports)\n{\n    ngx_uint_t             p, a;\n    ngx_http_conf_port_t  *port;\n    ngx_http_conf_addr_t  *addr;\n\n    if (ports == NULL) {\n        return NGX_OK;\n    }\n\n    port = ports->elts;\n    for (p = 0; p < ports->nelts; p++) {\n\n        ngx_sort(port[p].addrs.elts, (size_t) port[p].addrs.nelts,\n                 sizeof(ngx_http_conf_addr_t), ngx_http_cmp_conf_addrs);\n\n        /*\n         * check whether all name-based servers have the same\n         * configuration as a default server for given address:port\n         */\n\n        addr = port[p].addrs.elts;\n        for (a = 0; a < port[p].addrs.nelts; a++) {\n\n            if (addr[a].servers.nelts > 1\n#if (NGX_PCRE)\n                || addr[a].default_server->captures\n#endif\n               )\n            {\n                if (ngx_http_server_names(cf, cmcf, &addr[a]) != NGX_OK) {\n                    return NGX_ERROR;\n                }\n            }\n        }\n\n        if (ngx_http_init_listening(cf, &port[p]) != NGX_OK) {\n            return NGX_ERROR;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_server_names(ngx_conf_t *cf, ngx_http_core_main_conf_t *cmcf,\n    ngx_http_conf_addr_t *addr)\n{\n    ngx_int_t                   rc;\n    ngx_uint_t                  n, s;\n    ngx_hash_init_t             hash;\n    ngx_hash_keys_arrays_t      ha;\n    ngx_http_server_name_t     *name;\n    ngx_http_core_srv_conf_t  **cscfp;\n#if (NGX_PCRE)\n    ngx_uint_t                  regex, i;\n\n    regex = 0;\n#endif\n\n    ngx_memzero(&ha, sizeof(ngx_hash_keys_arrays_t));\n\n    ha.temp_pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, cf->log);\n    if (ha.temp_pool == NULL) {\n        return NGX_ERROR;\n    }\n\n    ha.pool = cf->pool;\n\n    if (ngx_hash_keys_array_init(&ha, NGX_HASH_LARGE) != NGX_OK) {\n        goto failed;\n    }\n\n    cscfp = addr->servers.elts;\n\n    for (s = 0; s < addr->servers.nelts; s++) {\n\n        name = cscfp[s]->server_names.elts;\n\n        for (n = 0; n < cscfp[s]->server_names.nelts; n++) {\n\n#if (NGX_PCRE)\n            if (name[n].regex) {\n                regex++;\n                continue;\n            }\n#endif\n\n            rc = ngx_hash_add_key(&ha, &name[n].name, name[n].server,\n                                  NGX_HASH_WILDCARD_KEY);\n\n            if (rc == NGX_ERROR) {\n                goto failed;\n            }\n\n            if (rc == NGX_DECLINED) {\n                ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                              \"invalid server name or wildcard \\\"%V\\\" on %V\",\n                              &name[n].name, &addr->opt.addr_text);\n                goto failed;\n            }\n\n            if (rc == NGX_BUSY) {\n                ngx_log_error(NGX_LOG_WARN, cf->log, 0,\n                              \"conflicting server name \\\"%V\\\" on %V, ignored\",\n                              &name[n].name, &addr->opt.addr_text);\n            }\n        }\n    }\n\n    hash.key = ngx_hash_key_lc;\n    hash.max_size = cmcf->server_names_hash_max_size;\n    hash.bucket_size = cmcf->server_names_hash_bucket_size;\n    hash.name = \"server_names_hash\";\n    hash.pool = cf->pool;\n\n    if (ha.keys.nelts) {\n        hash.hash = &addr->hash;\n        hash.temp_pool = NULL;\n\n        if (ngx_hash_init(&hash, ha.keys.elts, ha.keys.nelts) != NGX_OK) {\n            goto failed;\n        }\n    }\n\n    if (ha.dns_wc_head.nelts) {\n\n        ngx_qsort(ha.dns_wc_head.elts, (size_t) ha.dns_wc_head.nelts,\n                  sizeof(ngx_hash_key_t), ngx_http_cmp_dns_wildcards);\n\n        hash.hash = NULL;\n        hash.temp_pool = ha.temp_pool;\n\n        if (ngx_hash_wildcard_init(&hash, ha.dns_wc_head.elts,\n                                   ha.dns_wc_head.nelts)\n            != NGX_OK)\n        {\n            goto failed;\n        }\n\n        addr->wc_head = (ngx_hash_wildcard_t *) hash.hash;\n    }\n\n    if (ha.dns_wc_tail.nelts) {\n\n        ngx_qsort(ha.dns_wc_tail.elts, (size_t) ha.dns_wc_tail.nelts,\n                  sizeof(ngx_hash_key_t), ngx_http_cmp_dns_wildcards);\n\n        hash.hash = NULL;\n        hash.temp_pool = ha.temp_pool;\n\n        if (ngx_hash_wildcard_init(&hash, ha.dns_wc_tail.elts,\n                                   ha.dns_wc_tail.nelts)\n            != NGX_OK)\n        {\n            goto failed;\n        }\n\n        addr->wc_tail = (ngx_hash_wildcard_t *) hash.hash;\n    }\n\n    ngx_destroy_pool(ha.temp_pool);\n\n#if (NGX_PCRE)\n\n    if (regex == 0) {\n        return NGX_OK;\n    }\n\n    addr->nregex = regex;\n    addr->regex = ngx_palloc(cf->pool, regex * sizeof(ngx_http_server_name_t));\n    if (addr->regex == NULL) {\n        return NGX_ERROR;\n    }\n\n    i = 0;\n\n    for (s = 0; s < addr->servers.nelts; s++) {\n\n        name = cscfp[s]->server_names.elts;\n\n        for (n = 0; n < cscfp[s]->server_names.nelts; n++) {\n            if (name[n].regex) {\n                addr->regex[i++] = name[n];\n            }\n        }\n    }\n\n#endif\n\n    return NGX_OK;\n\nfailed:\n\n    ngx_destroy_pool(ha.temp_pool);\n\n    return NGX_ERROR;\n}\n\n\nstatic ngx_int_t\nngx_http_cmp_conf_addrs(const void *one, const void *two)\n{\n    ngx_http_conf_addr_t  *first, *second;\n\n    first = (ngx_http_conf_addr_t *) one;\n    second = (ngx_http_conf_addr_t *) two;\n\n    if (first->opt.wildcard) {\n        /* a wildcard address must be the last resort, shift it to the end */\n        return 1;\n    }\n\n    if (second->opt.wildcard) {\n        /* a wildcard address must be the last resort, shift it to the end */\n        return -1;\n    }\n\n    if (first->opt.bind && !second->opt.bind) {\n        /* shift explicit bind()ed addresses to the start */\n        return -1;\n    }\n\n    if (!first->opt.bind && second->opt.bind) {\n        /* shift explicit bind()ed addresses to the start */\n        return 1;\n    }\n\n    /* do not sort by default */\n\n    return 0;\n}\n\n\nstatic int ngx_libc_cdecl\nngx_http_cmp_dns_wildcards(const void *one, const void *two)\n{\n    ngx_hash_key_t  *first, *second;\n\n    first = (ngx_hash_key_t *) one;\n    second = (ngx_hash_key_t *) two;\n\n    return ngx_dns_strcmp(first->key.data, second->key.data);\n}\n\n\nstatic ngx_int_t\nngx_http_init_listening(ngx_conf_t *cf, ngx_http_conf_port_t *port)\n{\n    ngx_uint_t                 i, last, bind_wildcard;\n    ngx_listening_t           *ls;\n    ngx_http_port_t           *hport;\n    ngx_http_conf_addr_t      *addr;\n\n    addr = port->addrs.elts;\n    last = port->addrs.nelts;\n\n    /*\n     * If there is a binding to an \"*:port\" then we need to bind() to\n     * the \"*:port\" only and ignore other implicit bindings.  The bindings\n     * have been already sorted: explicit bindings are on the start, then\n     * implicit bindings go, and wildcard binding is in the end.\n     */\n\n    if (addr[last - 1].opt.wildcard) {\n        addr[last - 1].opt.bind = 1;\n        bind_wildcard = 1;\n\n    } else {\n        bind_wildcard = 0;\n    }\n\n    i = 0;\n\n    while (i < last) {\n\n        if (bind_wildcard && !addr[i].opt.bind) {\n            i++;\n            continue;\n        }\n\n        ls = ngx_http_add_listening(cf, &addr[i]);\n        if (ls == NULL) {\n            return NGX_ERROR;\n        }\n\n        hport = ngx_pcalloc(cf->pool, sizeof(ngx_http_port_t));\n        if (hport == NULL) {\n            return NGX_ERROR;\n        }\n\n        ls->servers = hport;\n\n        hport->naddrs = i + 1;\n\n        switch (ls->sockaddr->sa_family) {\n\n#if (NGX_HAVE_INET6)\n        case AF_INET6:\n            if (ngx_http_add_addrs6(cf, hport, addr) != NGX_OK) {\n                return NGX_ERROR;\n            }\n            break;\n#endif\n        default: /* AF_INET */\n            if (ngx_http_add_addrs(cf, hport, addr) != NGX_OK) {\n                return NGX_ERROR;\n            }\n            break;\n        }\n\n        addr++;\n        last--;\n    }\n\n    return NGX_OK;\n}\n\n#if (T_NGX_HAVE_XUDP)\nstatic ngx_int_t\nngx_inet_addr_is_loopback(struct sockaddr *sa)\n{\n    struct sockaddr_in  *v4;\n    struct sockaddr_in6 *v6;\n\n    switch(sa->sa_family) {\n        case AF_INET:\n            v4 = (struct sockaddr_in*) (sa);\n            return v4->sin_addr.s_addr == htonl(INADDR_LOOPBACK);\n        case AF_INET6:\n            v6 = (struct sockaddr_in6 *) (sa);\n            return IN6_IS_ADDR_LOOPBACK(&v6->sin6_addr);\n        default:\n            return 0;\n    }\n}\n#endif\n\nstatic ngx_listening_t *\nngx_http_add_listening(ngx_conf_t *cf, ngx_http_conf_addr_t *addr)\n{\n    ngx_listening_t           *ls;\n    ngx_http_core_loc_conf_t  *clcf;\n    ngx_http_core_srv_conf_t  *cscf;\n\n    ls = ngx_create_listening(cf, addr->opt.sockaddr, addr->opt.socklen);\n    if (ls == NULL) {\n        return NULL;\n    }\n\n    ls->addr_ntop = 1;\n\n    ls->handler = ngx_http_init_connection;\n\n    cscf = addr->default_server;\n    ls->pool_size = cscf->connection_pool_size;\n\n    clcf = cscf->ctx->loc_conf[ngx_http_core_module.ctx_index];\n\n    ls->logp = clcf->error_log;\n    ls->log.data = &ls->addr_text;\n    ls->log.handler = ngx_accept_log_error;\n\n#if (NGX_WIN32)\n    {\n    ngx_iocp_conf_t  *iocpcf = NULL;\n\n    if (ngx_get_conf(cf->cycle->conf_ctx, ngx_events_module)) {\n        iocpcf = ngx_event_get_conf(cf->cycle->conf_ctx, ngx_iocp_module);\n    }\n    if (iocpcf && iocpcf->acceptex_read) {\n        ls->post_accept_buffer_size = cscf->client_header_buffer_size;\n    }\n    }\n#endif\n\n    ls->backlog = addr->opt.backlog;\n    ls->rcvbuf = addr->opt.rcvbuf;\n    ls->sndbuf = addr->opt.sndbuf;\n\n    ls->keepalive = addr->opt.so_keepalive;\n#if (NGX_HAVE_KEEPALIVE_TUNABLE)\n    ls->keepidle = addr->opt.tcp_keepidle;\n    ls->keepintvl = addr->opt.tcp_keepintvl;\n    ls->keepcnt = addr->opt.tcp_keepcnt;\n#endif\n\n#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)\n    ls->accept_filter = addr->opt.accept_filter;\n#endif\n\n#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT)\n    ls->deferred_accept = addr->opt.deferred_accept;\n#endif\n\n#if (NGX_HAVE_INET6)\n    ls->ipv6only = addr->opt.ipv6only;\n#endif\n\n#if (NGX_HAVE_SETFIB)\n    ls->setfib = addr->opt.setfib;\n#endif\n\n#if (NGX_HAVE_TCP_FASTOPEN)\n    ls->fastopen = addr->opt.fastopen;\n#endif\n\n#if (NGX_HAVE_REUSEPORT)\n    ls->reuseport = addr->opt.reuseport;\n#endif\n\n#if (T_NGX_XQUIC)\n    ls->xquic = addr->opt.xquic;\n    if (ls->xquic) {\n        ls->type = SOCK_DGRAM;\n        ls->wildcard = addr->opt.wildcard;\n    }\n#endif\n#if (T_NGX_HAVE_XUDP)\n    if (addr->opt.xudp) {\n        /* udp check */\n        if (ls->type != SOCK_DGRAM) {\n            ngx_log_error(NGX_LOG_ERR, cf->log, 0, \"xudp required udp listening\");\n            return NULL;\n        }\n        /* loopback address check */\n        if (ngx_inet_addr_is_loopback(addr->opt.sockaddr)) {\n            ngx_log_error(NGX_LOG_ERR, cf->log, 0, \"xudp don't support loopback address\");\n            return NULL;\n        }\n        ls->xudp = 1;\n    }\n#endif\n    return ls;\n}\n\n\nstatic ngx_int_t\nngx_http_add_addrs(ngx_conf_t *cf, ngx_http_port_t *hport,\n    ngx_http_conf_addr_t *addr)\n{\n    ngx_uint_t                 i;\n    ngx_http_in_addr_t        *addrs;\n    struct sockaddr_in        *sin;\n    ngx_http_virtual_names_t  *vn;\n\n    hport->addrs = ngx_pcalloc(cf->pool,\n                               hport->naddrs * sizeof(ngx_http_in_addr_t));\n    if (hport->addrs == NULL) {\n        return NGX_ERROR;\n    }\n\n    addrs = hport->addrs;\n\n    for (i = 0; i < hport->naddrs; i++) {\n\n        sin = (struct sockaddr_in *) addr[i].opt.sockaddr;\n        addrs[i].addr = sin->sin_addr.s_addr;\n        addrs[i].conf.default_server = addr[i].default_server;\n#if (NGX_HTTP_SSL)\n        addrs[i].conf.ssl = addr[i].opt.ssl;\n#endif\n#if (NGX_HTTP_V2)\n        addrs[i].conf.http2 = addr[i].opt.http2;\n#endif\n#if (T_NGX_HTTPS_ALLOW_HTTP)\n        addrs[i].conf.https_allow_http = addr[i].opt.https_allow_http;\n#endif\n        addrs[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;\n#if (T_NGX_XQUIC)\n        addrs[i].conf.xquic = addr[i].opt.xquic;\n#endif\n#if (T_NGX_HAVE_XUDP)\n        addrs[i].conf.xudp = addr[i].opt.xudp;\n#endif\n        if (addr[i].hash.buckets == NULL\n            && (addr[i].wc_head == NULL\n                || addr[i].wc_head->hash.buckets == NULL)\n            && (addr[i].wc_tail == NULL\n                || addr[i].wc_tail->hash.buckets == NULL)\n#if (NGX_PCRE)\n            && addr[i].nregex == 0\n#endif\n            )\n        {\n            continue;\n        }\n\n        vn = ngx_palloc(cf->pool, sizeof(ngx_http_virtual_names_t));\n        if (vn == NULL) {\n            return NGX_ERROR;\n        }\n\n        addrs[i].conf.virtual_names = vn;\n\n        vn->names.hash = addr[i].hash;\n        vn->names.wc_head = addr[i].wc_head;\n        vn->names.wc_tail = addr[i].wc_tail;\n#if (NGX_PCRE)\n        vn->nregex = addr[i].nregex;\n        vn->regex = addr[i].regex;\n#endif\n    }\n\n    return NGX_OK;\n}\n\n\n#if (NGX_HAVE_INET6)\n\nstatic ngx_int_t\nngx_http_add_addrs6(ngx_conf_t *cf, ngx_http_port_t *hport,\n    ngx_http_conf_addr_t *addr)\n{\n    ngx_uint_t                 i;\n    ngx_http_in6_addr_t       *addrs6;\n    struct sockaddr_in6       *sin6;\n    ngx_http_virtual_names_t  *vn;\n\n    hport->addrs = ngx_pcalloc(cf->pool,\n                               hport->naddrs * sizeof(ngx_http_in6_addr_t));\n    if (hport->addrs == NULL) {\n        return NGX_ERROR;\n    }\n\n    addrs6 = hport->addrs;\n\n    for (i = 0; i < hport->naddrs; i++) {\n\n        sin6 = (struct sockaddr_in6 *) addr[i].opt.sockaddr;\n        addrs6[i].addr6 = sin6->sin6_addr;\n        addrs6[i].conf.default_server = addr[i].default_server;\n#if (NGX_HTTP_SSL)\n        addrs6[i].conf.ssl = addr[i].opt.ssl;\n#endif\n#if (NGX_HTTP_V2)\n        addrs6[i].conf.http2 = addr[i].opt.http2;\n#endif\n#if (T_NGX_HTTPS_ALLOW_HTTP)\n        addrs6[i].conf.https_allow_http = addr[i].opt.https_allow_http;\n#endif\n        addrs6[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;\n#if (T_NGX_XQUIC)\n        addrs6[i].conf.xquic = addr[i].opt.xquic;\n#endif\n#if (T_NGX_HAVE_XUDP)\n        addrs6[i].conf.xudp = addr[i].opt.xudp;\n#endif\n\n        if (addr[i].hash.buckets == NULL\n            && (addr[i].wc_head == NULL\n                || addr[i].wc_head->hash.buckets == NULL)\n            && (addr[i].wc_tail == NULL\n                || addr[i].wc_tail->hash.buckets == NULL)\n#if (NGX_PCRE)\n            && addr[i].nregex == 0\n#endif\n            )\n        {\n            continue;\n        }\n\n        vn = ngx_palloc(cf->pool, sizeof(ngx_http_virtual_names_t));\n        if (vn == NULL) {\n            return NGX_ERROR;\n        }\n\n        addrs6[i].conf.virtual_names = vn;\n\n        vn->names.hash = addr[i].hash;\n        vn->names.wc_head = addr[i].wc_head;\n        vn->names.wc_tail = addr[i].wc_tail;\n#if (NGX_PCRE)\n        vn->nregex = addr[i].nregex;\n        vn->regex = addr[i].regex;\n#endif\n    }\n\n    return NGX_OK;\n}\n\n#endif\n\n\nchar *\nngx_http_types_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    char  *p = conf;\n\n    ngx_array_t     **types;\n    ngx_str_t        *value, *default_type;\n    ngx_uint_t        i, n, hash;\n    ngx_hash_key_t   *type;\n\n    types = (ngx_array_t **) (p + cmd->offset);\n\n    if (*types == (void *) -1) {\n        return NGX_CONF_OK;\n    }\n\n    default_type = cmd->post;\n\n    if (*types == NULL) {\n        *types = ngx_array_create(cf->temp_pool, 1, sizeof(ngx_hash_key_t));\n        if (*types == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        if (default_type) {\n            type = ngx_array_push(*types);\n            if (type == NULL) {\n                return NGX_CONF_ERROR;\n            }\n\n            type->key = *default_type;\n            type->key_hash = ngx_hash_key(default_type->data,\n                                          default_type->len);\n            type->value = (void *) 4;\n        }\n    }\n\n    value = cf->args->elts;\n\n    for (i = 1; i < cf->args->nelts; i++) {\n\n        if (value[i].len == 1 && value[i].data[0] == '*') {\n            *types = (void *) -1;\n            return NGX_CONF_OK;\n        }\n\n        hash = ngx_hash_strlow(value[i].data, value[i].data, value[i].len);\n        value[i].data[value[i].len] = '\\0';\n\n        type = (*types)->elts;\n        for (n = 0; n < (*types)->nelts; n++) {\n\n            if (ngx_strcmp(value[i].data, type[n].key.data) == 0) {\n                ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                                   \"duplicate MIME type \\\"%V\\\"\", &value[i]);\n                goto next;\n            }\n        }\n\n        type = ngx_array_push(*types);\n        if (type == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        type->key = value[i];\n        type->key_hash = hash;\n        type->value = (void *) 4;\n\n    next:\n\n        continue;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nchar *\nngx_http_merge_types(ngx_conf_t *cf, ngx_array_t **keys, ngx_hash_t *types_hash,\n    ngx_array_t **prev_keys, ngx_hash_t *prev_types_hash,\n    ngx_str_t *default_types)\n{\n    ngx_hash_init_t  hash;\n\n    if (*keys) {\n\n        if (*keys == (void *) -1) {\n            return NGX_CONF_OK;\n        }\n\n        hash.hash = types_hash;\n        hash.key = NULL;\n        hash.max_size = 2048;\n        hash.bucket_size = 64;\n        hash.name = \"test_types_hash\";\n        hash.pool = cf->pool;\n        hash.temp_pool = NULL;\n\n        if (ngx_hash_init(&hash, (*keys)->elts, (*keys)->nelts) != NGX_OK) {\n            return NGX_CONF_ERROR;\n        }\n\n        return NGX_CONF_OK;\n    }\n\n    if (prev_types_hash->buckets == NULL) {\n\n        if (*prev_keys == NULL) {\n\n            if (ngx_http_set_default_types(cf, prev_keys, default_types)\n                != NGX_OK)\n            {\n                return NGX_CONF_ERROR;\n            }\n\n        } else if (*prev_keys == (void *) -1) {\n            *keys = *prev_keys;\n            return NGX_CONF_OK;\n        }\n\n        hash.hash = prev_types_hash;\n        hash.key = NULL;\n        hash.max_size = 2048;\n        hash.bucket_size = 64;\n        hash.name = \"test_types_hash\";\n        hash.pool = cf->pool;\n        hash.temp_pool = NULL;\n\n        if (ngx_hash_init(&hash, (*prev_keys)->elts, (*prev_keys)->nelts)\n            != NGX_OK)\n        {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    *types_hash = *prev_types_hash;\n\n    return NGX_CONF_OK;\n\n}\n\n\nngx_int_t\nngx_http_set_default_types(ngx_conf_t *cf, ngx_array_t **types,\n    ngx_str_t *default_type)\n{\n    ngx_hash_key_t  *type;\n\n    *types = ngx_array_create(cf->temp_pool, 1, sizeof(ngx_hash_key_t));\n    if (*types == NULL) {\n        return NGX_ERROR;\n    }\n\n    while (default_type->len) {\n\n        type = ngx_array_push(*types);\n        if (type == NULL) {\n            return NGX_ERROR;\n        }\n\n        type->key = *default_type;\n        type->key_hash = ngx_hash_key(default_type->data,\n                                      default_type->len);\n        type->value = (void *) 4;\n\n        default_type++;\n    }\n\n    return NGX_OK;\n}\n\n\n#if (T_NGX_INPUT_BODY_FILTER)\nstatic ngx_int_t\nngx_http_dummy_input_body_filter(ngx_http_request_t *r, ngx_buf_t *buf)\n{\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http dummy input body filter\");\n    return NGX_OK;\n}\n#endif\n"
  },
  {
    "path": "src/http/ngx_http.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_HTTP_H_INCLUDED_\n#define _NGX_HTTP_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\ntypedef struct ngx_http_request_s     ngx_http_request_t;\ntypedef struct ngx_http_upstream_s    ngx_http_upstream_t;\ntypedef struct ngx_http_cache_s       ngx_http_cache_t;\ntypedef struct ngx_http_file_cache_s  ngx_http_file_cache_t;\ntypedef struct ngx_http_log_ctx_s     ngx_http_log_ctx_t;\ntypedef struct ngx_http_chunked_s     ngx_http_chunked_t;\ntypedef struct ngx_http_v2_stream_s   ngx_http_v2_stream_t;\n#if (T_NGX_XQUIC)\ntypedef struct ngx_http_v3_stream_s  ngx_http_v3_stream_t;\n#endif\n\ntypedef ngx_int_t (*ngx_http_header_handler_pt)(ngx_http_request_t *r,\n    ngx_table_elt_t *h, ngx_uint_t offset);\ntypedef u_char *(*ngx_http_log_handler_pt)(ngx_http_request_t *r,\n    ngx_http_request_t *sr, u_char *buf, size_t len);\n\n\n#include <ngx_http_variables.h>\n#include <ngx_http_config.h>\n#include <ngx_http_request.h>\n#include <ngx_http_script.h>\n#include <ngx_http_upstream.h>\n#include <ngx_http_upstream_round_robin.h>\n#include <ngx_http_core_module.h>\n\n#if (T_NGX_XQUIC)\n#include <ngx_http_xquic.h>\n#endif\n#if (NGX_HTTP_V2)\n#include <ngx_http_v2.h>\n#endif\n#if (NGX_HTTP_CACHE)\n#include <ngx_http_cache.h>\n#endif\n#if (NGX_HTTP_SSI)\n#include <ngx_http_ssi_filter_module.h>\n#endif\n#if (NGX_HTTP_SSL)\n#include <ngx_http_ssl_module.h>\n#endif\n\n\nstruct ngx_http_log_ctx_s {\n    ngx_connection_t    *connection;\n    ngx_http_request_t  *request;\n    ngx_http_request_t  *current_request;\n};\n\n\nstruct ngx_http_chunked_s {\n    ngx_uint_t           state;\n    off_t                size;\n    off_t                length;\n};\n\n\ntypedef struct {\n    ngx_uint_t           http_version;\n    ngx_uint_t           code;\n    ngx_uint_t           count;\n    u_char              *start;\n    u_char              *end;\n} ngx_http_status_t;\n\n\n#define ngx_http_get_module_ctx(r, module)  (r)->ctx[module.ctx_index]\n#define ngx_http_set_ctx(r, c, module)      r->ctx[module.ctx_index] = c;\n\n\nngx_int_t ngx_http_add_location(ngx_conf_t *cf, ngx_queue_t **locations,\n    ngx_http_core_loc_conf_t *clcf);\nngx_int_t ngx_http_add_listen(ngx_conf_t *cf, ngx_http_core_srv_conf_t *cscf,\n    ngx_http_listen_opt_t *lsopt);\n\n\nvoid ngx_http_init_connection(ngx_connection_t *c);\nvoid ngx_http_close_connection(ngx_connection_t *c);\n\n#if (NGX_HTTP_SSL && defined SSL_CTRL_SET_TLSEXT_HOSTNAME)\nint ngx_http_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg);\n#endif\n#if (NGX_HTTP_SSL && defined SSL_R_CERT_CB_ERROR)\nint ngx_http_ssl_certificate(ngx_ssl_conn_t *ssl_conn, void *arg);\n#endif\n\n#if defined(T_INGRESS_SHARED_MEMORY_PB) && OPENSSL_VERSION_NUMBER >= 0x10101000L\nint ngx_http_ssl_client_hello_callback(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg);\n#endif\n\nngx_int_t ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b);\nngx_int_t ngx_http_parse_uri(ngx_http_request_t *r);\nngx_int_t ngx_http_parse_complex_uri(ngx_http_request_t *r,\n    ngx_uint_t merge_slashes);\nngx_int_t ngx_http_parse_status_line(ngx_http_request_t *r, ngx_buf_t *b,\n    ngx_http_status_t *status);\nngx_int_t ngx_http_parse_unsafe_uri(ngx_http_request_t *r, ngx_str_t *uri,\n    ngx_str_t *args, ngx_uint_t *flags);\nngx_int_t ngx_http_parse_header_line(ngx_http_request_t *r, ngx_buf_t *b,\n    ngx_uint_t allow_underscores);\nngx_table_elt_t *ngx_http_parse_multi_header_lines(ngx_http_request_t *r,\n    ngx_table_elt_t *headers, ngx_str_t *name, ngx_str_t *value);\nngx_table_elt_t *ngx_http_parse_set_cookie_lines(ngx_http_request_t *r,\n    ngx_table_elt_t *headers, ngx_str_t *name, ngx_str_t *value);\nngx_int_t ngx_http_arg(ngx_http_request_t *r, u_char *name, size_t len,\n    ngx_str_t *value);\nvoid ngx_http_split_args(ngx_http_request_t *r, ngx_str_t *uri,\n    ngx_str_t *args);\nngx_int_t ngx_http_parse_chunked(ngx_http_request_t *r, ngx_buf_t *b,\n    ngx_http_chunked_t *ctx);\n\n#if (T_HTTP_HEADER)\nngx_int_t ngx_http_header_in(ngx_http_request_t *r, u_char *name, size_t len,\n    ngx_str_t *value);\nngx_int_t ngx_http_header_out(ngx_http_request_t *r, u_char *name, size_t len,\n    ngx_str_t *value);\n#endif\n\nngx_http_request_t *ngx_http_create_request(ngx_connection_t *c);\nngx_int_t ngx_http_process_request_uri(ngx_http_request_t *r);\nngx_int_t ngx_http_process_request_header(ngx_http_request_t *r);\nvoid ngx_http_process_request(ngx_http_request_t *r);\nvoid ngx_http_update_location_config(ngx_http_request_t *r);\nvoid ngx_http_handler(ngx_http_request_t *r);\nvoid ngx_http_run_posted_requests(ngx_connection_t *c);\nngx_int_t ngx_http_post_request(ngx_http_request_t *r,\n    ngx_http_posted_request_t *pr);\nvoid ngx_http_finalize_request(ngx_http_request_t *r, ngx_int_t rc);\nvoid ngx_http_free_request(ngx_http_request_t *r, ngx_int_t rc);\n\nvoid ngx_http_empty_handler(ngx_event_t *wev);\nvoid ngx_http_request_empty_handler(ngx_http_request_t *r);\n\n\n#define NGX_HTTP_LAST   1\n#define NGX_HTTP_FLUSH  2\n\nngx_int_t ngx_http_send_special(ngx_http_request_t *r, ngx_uint_t flags);\n\n\nngx_int_t ngx_http_read_client_request_body(ngx_http_request_t *r,\n    ngx_http_client_body_handler_pt post_handler);\nngx_int_t ngx_http_read_unbuffered_request_body(ngx_http_request_t *r);\n\nngx_int_t ngx_http_send_header(ngx_http_request_t *r);\nngx_int_t ngx_http_special_response_handler(ngx_http_request_t *r,\n    ngx_int_t error);\nngx_int_t ngx_http_filter_finalize_request(ngx_http_request_t *r,\n    ngx_module_t *m, ngx_int_t error);\nvoid ngx_http_clean_header(ngx_http_request_t *r);\n\n\nngx_int_t ngx_http_discard_request_body(ngx_http_request_t *r);\nvoid ngx_http_discarded_request_body_handler(ngx_http_request_t *r);\nvoid ngx_http_block_reading(ngx_http_request_t *r);\nvoid ngx_http_test_reading(ngx_http_request_t *r);\n\n\nchar *ngx_http_types_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\nchar *ngx_http_merge_types(ngx_conf_t *cf, ngx_array_t **keys,\n    ngx_hash_t *types_hash, ngx_array_t **prev_keys,\n    ngx_hash_t *prev_types_hash, ngx_str_t *default_types);\nngx_int_t ngx_http_set_default_types(ngx_conf_t *cf, ngx_array_t **types,\n    ngx_str_t *default_type);\n\n#if (NGX_HTTP_DEGRADATION)\nngx_uint_t  ngx_http_degraded(ngx_http_request_t *);\n#endif\n\n\n#if (NGX_HTTP_V2)\nngx_int_t ngx_http_huff_decode(u_char *state, u_char *src, size_t len,\n    u_char **dst, ngx_uint_t last, ngx_log_t *log);\nsize_t ngx_http_huff_encode(u_char *src, size_t len, u_char *dst,\n    ngx_uint_t lower);\n#endif\n\n\nextern ngx_module_t  ngx_http_module;\n\nextern ngx_str_t  ngx_http_html_default_types[];\n\n\nextern ngx_http_output_header_filter_pt  ngx_http_top_header_filter;\nextern ngx_http_output_body_filter_pt    ngx_http_top_body_filter;\nextern ngx_http_request_body_filter_pt   ngx_http_top_request_body_filter;\n\n#if (T_NGX_INPUT_BODY_FILTER)\nextern ngx_http_input_body_filter_pt     ngx_http_top_input_body_filter;\n#endif\n\n#endif /* _NGX_HTTP_H_INCLUDED_ */\n"
  },
  {
    "path": "src/http/ngx_http_cache.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_HTTP_CACHE_H_INCLUDED_\n#define _NGX_HTTP_CACHE_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\n#define NGX_HTTP_CACHE_MISS          1\n#define NGX_HTTP_CACHE_BYPASS        2\n#define NGX_HTTP_CACHE_EXPIRED       3\n#define NGX_HTTP_CACHE_STALE         4\n#define NGX_HTTP_CACHE_UPDATING      5\n#define NGX_HTTP_CACHE_REVALIDATED   6\n#define NGX_HTTP_CACHE_HIT           7\n#define NGX_HTTP_CACHE_SCARCE        8\n\n#define NGX_HTTP_CACHE_KEY_LEN       16\n#define NGX_HTTP_CACHE_ETAG_LEN      128\n#define NGX_HTTP_CACHE_VARY_LEN      128\n\n#define NGX_HTTP_CACHE_VERSION       5\n\n\ntypedef struct {\n    ngx_uint_t                       status;\n    time_t                           valid;\n} ngx_http_cache_valid_t;\n\n\ntypedef struct {\n    ngx_rbtree_node_t                node;\n    ngx_queue_t                      queue;\n\n    u_char                           key[NGX_HTTP_CACHE_KEY_LEN\n                                         - sizeof(ngx_rbtree_key_t)];\n\n    unsigned                         count:20;\n    unsigned                         uses:10;\n    unsigned                         valid_msec:10;\n    unsigned                         error:10;\n    unsigned                         exists:1;\n    unsigned                         updating:1;\n    unsigned                         deleting:1;\n    unsigned                         purged:1;\n                                     /* 10 unused bits */\n\n    ngx_file_uniq_t                  uniq;\n    time_t                           expire;\n    time_t                           valid_sec;\n    size_t                           body_start;\n    off_t                            fs_size;\n    ngx_msec_t                       lock_time;\n} ngx_http_file_cache_node_t;\n\n\nstruct ngx_http_cache_s {\n    ngx_file_t                       file;\n    ngx_array_t                      keys;\n    uint32_t                         crc32;\n    u_char                           key[NGX_HTTP_CACHE_KEY_LEN];\n    u_char                           main[NGX_HTTP_CACHE_KEY_LEN];\n\n    ngx_file_uniq_t                  uniq;\n    time_t                           valid_sec;\n    time_t                           updating_sec;\n    time_t                           error_sec;\n    time_t                           last_modified;\n    time_t                           date;\n\n    ngx_str_t                        etag;\n    ngx_str_t                        vary;\n    u_char                           variant[NGX_HTTP_CACHE_KEY_LEN];\n\n    size_t                           buffer_size;\n    size_t                           header_start;\n    size_t                           body_start;\n    off_t                            length;\n    off_t                            fs_size;\n\n    ngx_uint_t                       min_uses;\n    ngx_uint_t                       error;\n    ngx_uint_t                       valid_msec;\n    ngx_uint_t                       vary_tag;\n\n    ngx_buf_t                       *buf;\n\n    ngx_http_file_cache_t           *file_cache;\n    ngx_http_file_cache_node_t      *node;\n\n#if (NGX_THREADS || NGX_COMPAT)\n    ngx_thread_task_t               *thread_task;\n#endif\n\n    ngx_msec_t                       lock_timeout;\n    ngx_msec_t                       lock_age;\n    ngx_msec_t                       lock_time;\n    ngx_msec_t                       wait_time;\n\n    ngx_event_t                      wait_event;\n\n    unsigned                         lock:1;\n    unsigned                         waiting:1;\n\n    unsigned                         updated:1;\n    unsigned                         updating:1;\n    unsigned                         exists:1;\n    unsigned                         temp_file:1;\n    unsigned                         purged:1;\n    unsigned                         reading:1;\n    unsigned                         secondary:1;\n    unsigned                         update_variant:1;\n    unsigned                         background:1;\n\n    unsigned                         stale_updating:1;\n    unsigned                         stale_error:1;\n};\n\n\ntypedef struct {\n    ngx_uint_t                       version;\n    time_t                           valid_sec;\n    time_t                           updating_sec;\n    time_t                           error_sec;\n    time_t                           last_modified;\n    time_t                           date;\n    uint32_t                         crc32;\n    u_short                          valid_msec;\n    u_short                          header_start;\n    u_short                          body_start;\n    u_char                           etag_len;\n    u_char                           etag[NGX_HTTP_CACHE_ETAG_LEN];\n    u_char                           vary_len;\n    u_char                           vary[NGX_HTTP_CACHE_VARY_LEN];\n    u_char                           variant[NGX_HTTP_CACHE_KEY_LEN];\n} ngx_http_file_cache_header_t;\n\n\ntypedef struct {\n    ngx_rbtree_t                     rbtree;\n    ngx_rbtree_node_t                sentinel;\n    ngx_queue_t                      queue;\n    ngx_atomic_t                     cold;\n    ngx_atomic_t                     loading;\n    off_t                            size;\n    ngx_uint_t                       count;\n    ngx_uint_t                       watermark;\n} ngx_http_file_cache_sh_t;\n\n\nstruct ngx_http_file_cache_s {\n    ngx_http_file_cache_sh_t        *sh;\n    ngx_slab_pool_t                 *shpool;\n\n    ngx_path_t                      *path;\n\n    off_t                            min_free;\n    off_t                            max_size;\n    size_t                           bsize;\n\n    time_t                           inactive;\n\n    time_t                           fail_time;\n\n    ngx_uint_t                       files;\n    ngx_uint_t                       loader_files;\n    ngx_msec_t                       last;\n    ngx_msec_t                       loader_sleep;\n    ngx_msec_t                       loader_threshold;\n\n    ngx_uint_t                       manager_files;\n    ngx_msec_t                       manager_sleep;\n    ngx_msec_t                       manager_threshold;\n\n    ngx_shm_zone_t                  *shm_zone;\n\n    ngx_uint_t                       use_temp_path;\n                                     /* unsigned use_temp_path:1 */\n};\n\n\nngx_int_t ngx_http_file_cache_new(ngx_http_request_t *r);\nngx_int_t ngx_http_file_cache_create(ngx_http_request_t *r);\nvoid ngx_http_file_cache_create_key(ngx_http_request_t *r);\nngx_int_t ngx_http_file_cache_open(ngx_http_request_t *r);\nngx_int_t ngx_http_file_cache_set_header(ngx_http_request_t *r, u_char *buf);\nvoid ngx_http_file_cache_update(ngx_http_request_t *r, ngx_temp_file_t *tf);\nvoid ngx_http_file_cache_update_header(ngx_http_request_t *r);\nngx_int_t ngx_http_cache_send(ngx_http_request_t *);\nvoid ngx_http_file_cache_free(ngx_http_cache_t *c, ngx_temp_file_t *tf);\ntime_t ngx_http_file_cache_valid(ngx_array_t *cache_valid, ngx_uint_t status);\n\nchar *ngx_http_file_cache_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nchar *ngx_http_file_cache_valid_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n\n\nextern ngx_str_t  ngx_http_cache_status[];\n\n\n#endif /* _NGX_HTTP_CACHE_H_INCLUDED_ */\n"
  },
  {
    "path": "src/http/ngx_http_config.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_HTTP_CONFIG_H_INCLUDED_\n#define _NGX_HTTP_CONFIG_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\ntypedef struct {\n    void        **main_conf;\n    void        **srv_conf;\n    void        **loc_conf;\n} ngx_http_conf_ctx_t;\n\n\ntypedef struct {\n    ngx_int_t   (*preconfiguration)(ngx_conf_t *cf);\n    ngx_int_t   (*postconfiguration)(ngx_conf_t *cf);\n\n    void       *(*create_main_conf)(ngx_conf_t *cf);\n    char       *(*init_main_conf)(ngx_conf_t *cf, void *conf);\n\n    void       *(*create_srv_conf)(ngx_conf_t *cf);\n    char       *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf);\n\n    void       *(*create_loc_conf)(ngx_conf_t *cf);\n    char       *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf);\n} ngx_http_module_t;\n\n\n#define NGX_HTTP_MODULE           0x50545448   /* \"HTTP\" */\n\n#define NGX_HTTP_MAIN_CONF        0x02000000\n#define NGX_HTTP_SRV_CONF         0x04000000\n#define NGX_HTTP_LOC_CONF         0x08000000\n#define NGX_HTTP_UPS_CONF         0x10000000\n#define NGX_HTTP_SIF_CONF         0x20000000\n#define NGX_HTTP_LIF_CONF         0x40000000\n#define NGX_HTTP_LMT_CONF         0x80000000\n\n\n#define NGX_HTTP_MAIN_CONF_OFFSET  offsetof(ngx_http_conf_ctx_t, main_conf)\n#define NGX_HTTP_SRV_CONF_OFFSET   offsetof(ngx_http_conf_ctx_t, srv_conf)\n#define NGX_HTTP_LOC_CONF_OFFSET   offsetof(ngx_http_conf_ctx_t, loc_conf)\n\n\n#define ngx_http_get_module_main_conf(r, module)                             \\\n    (r)->main_conf[module.ctx_index]\n#define ngx_http_get_module_srv_conf(r, module)  (r)->srv_conf[module.ctx_index]\n#define ngx_http_get_module_loc_conf(r, module)  (r)->loc_conf[module.ctx_index]\n\n\n#define ngx_http_conf_get_module_main_conf(cf, module)                        \\\n    ((ngx_http_conf_ctx_t *) cf->ctx)->main_conf[module.ctx_index]\n#define ngx_http_conf_get_module_srv_conf(cf, module)                         \\\n    ((ngx_http_conf_ctx_t *) cf->ctx)->srv_conf[module.ctx_index]\n#define ngx_http_conf_get_module_loc_conf(cf, module)                         \\\n    ((ngx_http_conf_ctx_t *) cf->ctx)->loc_conf[module.ctx_index]\n\n#define ngx_http_cycle_get_module_main_conf(cycle, module)                    \\\n    (cycle->conf_ctx[ngx_http_module.index] ?                                 \\\n        ((ngx_http_conf_ctx_t *) cycle->conf_ctx[ngx_http_module.index])      \\\n            ->main_conf[module.ctx_index]:                                    \\\n        NULL)\n\n\n#endif /* _NGX_HTTP_CONFIG_H_INCLUDED_ */\n"
  },
  {
    "path": "src/http/ngx_http_copy_filter_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\ntypedef struct {\n    ngx_bufs_t  bufs;\n} ngx_http_copy_filter_conf_t;\n\n\n#if (NGX_HAVE_FILE_AIO)\nstatic void ngx_http_copy_aio_handler(ngx_output_chain_ctx_t *ctx,\n    ngx_file_t *file);\nstatic void ngx_http_copy_aio_event_handler(ngx_event_t *ev);\n#endif\n#if (NGX_THREADS)\nstatic ngx_int_t ngx_http_copy_thread_handler(ngx_thread_task_t *task,\n    ngx_file_t *file);\nstatic void ngx_http_copy_thread_event_handler(ngx_event_t *ev);\n#endif\n\nstatic void *ngx_http_copy_filter_create_conf(ngx_conf_t *cf);\nstatic char *ngx_http_copy_filter_merge_conf(ngx_conf_t *cf,\n    void *parent, void *child);\nstatic ngx_int_t ngx_http_copy_filter_init(ngx_conf_t *cf);\n\n\nstatic ngx_command_t  ngx_http_copy_filter_commands[] = {\n\n    { ngx_string(\"output_buffers\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,\n      ngx_conf_set_bufs_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_copy_filter_conf_t, bufs),\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_copy_filter_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    ngx_http_copy_filter_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    ngx_http_copy_filter_create_conf,      /* create location configuration */\n    ngx_http_copy_filter_merge_conf        /* merge location configuration */\n};\n\n\nngx_module_t  ngx_http_copy_filter_module = {\n    NGX_MODULE_V1,\n    &ngx_http_copy_filter_module_ctx,      /* module context */\n    ngx_http_copy_filter_commands,         /* 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_http_output_body_filter_pt    ngx_http_next_body_filter;\n\n\nstatic ngx_int_t\nngx_http_copy_filter(ngx_http_request_t *r, ngx_chain_t *in)\n{\n    ngx_int_t                     rc;\n    ngx_connection_t             *c;\n    ngx_output_chain_ctx_t       *ctx;\n    ngx_http_core_loc_conf_t     *clcf;\n    ngx_http_copy_filter_conf_t  *conf;\n\n    c = r->connection;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http copy filter: \\\"%V?%V\\\"\", &r->uri, &r->args);\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_copy_filter_module);\n\n    if (ctx == NULL) {\n        ctx = ngx_pcalloc(r->pool, sizeof(ngx_output_chain_ctx_t));\n        if (ctx == NULL) {\n            return NGX_ERROR;\n        }\n\n        ngx_http_set_ctx(r, ctx, ngx_http_copy_filter_module);\n\n        conf = ngx_http_get_module_loc_conf(r, ngx_http_copy_filter_module);\n        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n        ctx->sendfile = c->sendfile;\n        ctx->need_in_memory = r->main_filter_need_in_memory\n                              || r->filter_need_in_memory;\n        ctx->need_in_temp = r->filter_need_temporary;\n\n        ctx->alignment = clcf->directio_alignment;\n\n        ctx->pool = r->pool;\n        ctx->bufs = conf->bufs;\n        ctx->tag = (ngx_buf_tag_t) &ngx_http_copy_filter_module;\n\n        ctx->output_filter = (ngx_output_chain_filter_pt)\n                                  ngx_http_next_body_filter;\n        ctx->filter_ctx = r;\n\n#if (NGX_HAVE_FILE_AIO)\n        if (ngx_file_aio && clcf->aio == NGX_HTTP_AIO_ON) {\n            ctx->aio_handler = ngx_http_copy_aio_handler;\n        }\n#endif\n\n#if (NGX_THREADS)\n        if (clcf->aio == NGX_HTTP_AIO_THREADS) {\n            ctx->thread_handler = ngx_http_copy_thread_handler;\n        }\n#endif\n\n        if (in && in->buf && ngx_buf_size(in->buf)) {\n            r->request_output = 1;\n        }\n    }\n\n#if (NGX_HAVE_FILE_AIO || NGX_THREADS)\n    ctx->aio = r->aio;\n#endif\n\n    rc = ngx_output_chain(ctx, in);\n\n    if (ctx->in == NULL) {\n        r->buffered &= ~NGX_HTTP_COPY_BUFFERED;\n\n    } else {\n        r->buffered |= NGX_HTTP_COPY_BUFFERED;\n    }\n\n    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http copy filter: %i \\\"%V?%V\\\"\", rc, &r->uri, &r->args);\n\n    return rc;\n}\n\n\n#if (NGX_HAVE_FILE_AIO)\n\nstatic void\nngx_http_copy_aio_handler(ngx_output_chain_ctx_t *ctx, ngx_file_t *file)\n{\n    ngx_http_request_t *r;\n\n    r = ctx->filter_ctx;\n\n    file->aio->data = r;\n    file->aio->handler = ngx_http_copy_aio_event_handler;\n\n    r->main->blocked++;\n    r->aio = 1;\n    ctx->aio = 1;\n}\n\n\nstatic void\nngx_http_copy_aio_event_handler(ngx_event_t *ev)\n{\n    ngx_event_aio_t     *aio;\n    ngx_connection_t    *c;\n    ngx_http_request_t  *r;\n\n    aio = ev->data;\n    r = aio->data;\n    c = r->connection;\n\n    ngx_http_set_log_request(c->log, r);\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http aio: \\\"%V?%V\\\"\", &r->uri, &r->args);\n\n    r->main->blocked--;\n    r->aio = 0;\n\n    r->write_event_handler(r);\n\n    ngx_http_run_posted_requests(c);\n}\n\n#endif\n\n\n#if (NGX_THREADS)\n\nstatic ngx_int_t\nngx_http_copy_thread_handler(ngx_thread_task_t *task, ngx_file_t *file)\n{\n    ngx_str_t                  name;\n    ngx_connection_t          *c;\n    ngx_thread_pool_t         *tp;\n    ngx_http_request_t        *r;\n    ngx_output_chain_ctx_t    *ctx;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    r = file->thread_ctx;\n\n    if (r->aio) {\n        /*\n         * tolerate sendfile() calls if another operation is already\n         * running; this can happen due to subrequests, multiple calls\n         * of the next body filter from a filter, or in HTTP/2 due to\n         * a write event on the main connection\n         */\n\n        c = r->connection;\n\n#if (NGX_HTTP_V2)\n        if (r->stream) {\n            c = r->stream->connection->connection;\n        }\n#endif\n\n        if (task == c->sendfile_task) {\n            return NGX_OK;\n        }\n    }\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n    tp = clcf->thread_pool;\n\n    if (tp == NULL) {\n        if (ngx_http_complex_value(r, clcf->thread_pool_value, &name)\n            != NGX_OK)\n        {\n            return NGX_ERROR;\n        }\n\n        tp = ngx_thread_pool_get((ngx_cycle_t *) ngx_cycle, &name);\n\n        if (tp == NULL) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"thread pool \\\"%V\\\" not found\", &name);\n            return NGX_ERROR;\n        }\n    }\n\n    task->event.data = r;\n    task->event.handler = ngx_http_copy_thread_event_handler;\n\n    if (ngx_thread_task_post(tp, task) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    r->main->blocked++;\n    r->aio = 1;\n\n    ctx = ngx_http_get_module_ctx(r, ngx_http_copy_filter_module);\n    ctx->aio = 1;\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_copy_thread_event_handler(ngx_event_t *ev)\n{\n    ngx_connection_t    *c;\n    ngx_http_request_t  *r;\n\n    r = ev->data;\n    c = r->connection;\n\n    ngx_http_set_log_request(c->log, r);\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http thread: \\\"%V?%V\\\"\", &r->uri, &r->args);\n\n    r->main->blocked--;\n    r->aio = 0;\n\n#if (NGX_HTTP_V2)\n\n    if (r->stream) {\n        /*\n         * for HTTP/2, update write event to make sure processing will\n         * reach the main connection to handle sendfile() in threads\n         */\n\n        c->write->ready = 1;\n        c->write->active = 0;\n    }\n\n#endif\n\n    if (r->done) {\n        /*\n         * trigger connection event handler if the subrequest was\n         * already finalized; this can happen if the handler is used\n         * for sendfile() in threads\n         */\n\n        c->write->handler(c->write);\n\n    } else {\n        r->write_event_handler(r);\n        ngx_http_run_posted_requests(c);\n    }\n}\n\n#endif\n\n\nstatic void *\nngx_http_copy_filter_create_conf(ngx_conf_t *cf)\n{\n    ngx_http_copy_filter_conf_t *conf;\n\n    conf = ngx_palloc(cf->pool, sizeof(ngx_http_copy_filter_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    conf->bufs.num = 0;\n\n    return conf;\n}\n\n\nstatic char *\nngx_http_copy_filter_merge_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_http_copy_filter_conf_t *prev = parent;\n    ngx_http_copy_filter_conf_t *conf = child;\n\n    ngx_conf_merge_bufs_value(conf->bufs, prev->bufs, 2, 32768);\n\n    return NULL;\n}\n\n\nstatic ngx_int_t\nngx_http_copy_filter_init(ngx_conf_t *cf)\n{\n    ngx_http_next_body_filter = ngx_http_top_body_filter;\n    ngx_http_top_body_filter = ngx_http_copy_filter;\n\n    return NGX_OK;\n}\n\n"
  },
  {
    "path": "src/http/ngx_http_core_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\ntypedef struct {\n    u_char    *name;\n    uint32_t   method;\n} ngx_http_method_name_t;\n\n\n#define NGX_HTTP_REQUEST_BODY_FILE_OFF    0\n#define NGX_HTTP_REQUEST_BODY_FILE_ON     1\n#define NGX_HTTP_REQUEST_BODY_FILE_CLEAN  2\n\n\nstatic ngx_int_t ngx_http_core_auth_delay(ngx_http_request_t *r);\nstatic void ngx_http_core_auth_delay_handler(ngx_http_request_t *r);\n\nstatic ngx_int_t ngx_http_core_find_location(ngx_http_request_t *r);\n#if (T_NGX_HTTP_IMPROVED_REWRITE)\nstatic ngx_int_t ngx_http_core_find_named_location(ngx_http_request_t *r);\n#endif\nstatic ngx_int_t ngx_http_core_find_static_location(ngx_http_request_t *r,\n    ngx_http_location_tree_node_t *node);\n\nstatic ngx_int_t ngx_http_core_preconfiguration(ngx_conf_t *cf);\nstatic ngx_int_t ngx_http_core_postconfiguration(ngx_conf_t *cf);\nstatic void *ngx_http_core_create_main_conf(ngx_conf_t *cf);\nstatic char *ngx_http_core_init_main_conf(ngx_conf_t *cf, void *conf);\nstatic void *ngx_http_core_create_srv_conf(ngx_conf_t *cf);\nstatic char *ngx_http_core_merge_srv_conf(ngx_conf_t *cf,\n    void *parent, void *child);\nstatic void *ngx_http_core_create_loc_conf(ngx_conf_t *cf);\nstatic char *ngx_http_core_merge_loc_conf(ngx_conf_t *cf,\n    void *parent, void *child);\n\nstatic char *ngx_http_core_server(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *dummy);\nstatic char *ngx_http_core_location(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *dummy);\nstatic ngx_int_t ngx_http_core_regex_location(ngx_conf_t *cf,\n    ngx_http_core_loc_conf_t *clcf, ngx_str_t *regex, ngx_uint_t caseless);\n\nstatic char *ngx_http_core_types(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_http_core_type(ngx_conf_t *cf, ngx_command_t *dummy,\n    void *conf);\n\nstatic char *ngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_http_core_server_name(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_http_core_root(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\nstatic char *ngx_http_core_limit_except(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_http_core_set_aio(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_http_core_directio(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_http_core_error_page(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_http_core_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_http_core_error_log(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_http_core_keepalive(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_http_core_internal(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_http_core_resolver(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n#if (T_NGX_RESOLVER_FILE)\nstatic char *ngx_http_core_resolver_file(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n#endif\n#if (T_NGX_SERVER_INFO)\nstatic char *ngx_http_set_server_tag(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n#endif\n#if (NGX_HTTP_GZIP)\nstatic ngx_int_t ngx_http_gzip_accept_encoding(ngx_str_t *ae);\nstatic ngx_uint_t ngx_http_gzip_quantity(u_char *p, u_char *last);\nstatic char *ngx_http_gzip_disable(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n#endif\nstatic ngx_int_t ngx_http_get_forwarded_addr_internal(ngx_http_request_t *r,\n    ngx_addr_t *addr, u_char *xff, size_t xfflen, ngx_array_t *proxies,\n    int recursive);\n#if (NGX_HAVE_OPENAT)\nstatic char *ngx_http_disable_symlinks(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n#endif\n\nstatic char *ngx_http_core_lowat_check(ngx_conf_t *cf, void *post, void *data);\nstatic char *ngx_http_core_pool_size(ngx_conf_t *cf, void *post, void *data);\n\nstatic ngx_conf_post_t  ngx_http_core_lowat_post =\n    { ngx_http_core_lowat_check };\n\nstatic ngx_conf_post_handler_pt  ngx_http_core_pool_size_p =\n    ngx_http_core_pool_size;\n\n\nstatic ngx_conf_enum_t  ngx_http_core_request_body_in_file[] = {\n    { ngx_string(\"off\"), NGX_HTTP_REQUEST_BODY_FILE_OFF },\n    { ngx_string(\"on\"), NGX_HTTP_REQUEST_BODY_FILE_ON },\n    { ngx_string(\"clean\"), NGX_HTTP_REQUEST_BODY_FILE_CLEAN },\n    { ngx_null_string, 0 }\n};\n\n\nstatic ngx_conf_enum_t  ngx_http_core_satisfy[] = {\n    { ngx_string(\"all\"), NGX_HTTP_SATISFY_ALL },\n    { ngx_string(\"any\"), NGX_HTTP_SATISFY_ANY },\n    { ngx_null_string, 0 }\n};\n\n\nstatic ngx_conf_enum_t  ngx_http_core_lingering_close[] = {\n    { ngx_string(\"off\"), NGX_HTTP_LINGERING_OFF },\n    { ngx_string(\"on\"), NGX_HTTP_LINGERING_ON },\n    { ngx_string(\"always\"), NGX_HTTP_LINGERING_ALWAYS },\n    { ngx_null_string, 0 }\n};\n\n\nstatic ngx_conf_enum_t  ngx_http_core_server_tokens[] = {\n    { ngx_string(\"off\"), NGX_HTTP_SERVER_TOKENS_OFF },\n    { ngx_string(\"on\"), NGX_HTTP_SERVER_TOKENS_ON },\n    { ngx_string(\"build\"), NGX_HTTP_SERVER_TOKENS_BUILD },\n    { ngx_null_string, 0 }\n};\n\n\nstatic ngx_conf_enum_t  ngx_http_core_if_modified_since[] = {\n    { ngx_string(\"off\"), NGX_HTTP_IMS_OFF },\n    { ngx_string(\"exact\"), NGX_HTTP_IMS_EXACT },\n    { ngx_string(\"before\"), NGX_HTTP_IMS_BEFORE },\n    { ngx_null_string, 0 }\n};\n\n\nstatic ngx_conf_bitmask_t  ngx_http_core_keepalive_disable[] = {\n    { ngx_string(\"none\"), NGX_HTTP_KEEPALIVE_DISABLE_NONE },\n    { ngx_string(\"msie6\"), NGX_HTTP_KEEPALIVE_DISABLE_MSIE6 },\n    { ngx_string(\"safari\"), NGX_HTTP_KEEPALIVE_DISABLE_SAFARI },\n    { ngx_null_string, 0 }\n};\n\n\nstatic ngx_path_init_t  ngx_http_client_temp_path = {\n    ngx_string(NGX_HTTP_CLIENT_TEMP_PATH), { 0, 0, 0 }\n};\n\n\n#if (NGX_HTTP_GZIP)\n\nstatic ngx_conf_enum_t  ngx_http_gzip_http_version[] = {\n    { ngx_string(\"1.0\"), NGX_HTTP_VERSION_10 },\n    { ngx_string(\"1.1\"), NGX_HTTP_VERSION_11 },\n    { ngx_null_string, 0 }\n};\n\n\nstatic ngx_conf_bitmask_t  ngx_http_gzip_proxied_mask[] = {\n    { ngx_string(\"off\"), NGX_HTTP_GZIP_PROXIED_OFF },\n    { ngx_string(\"expired\"), NGX_HTTP_GZIP_PROXIED_EXPIRED },\n    { ngx_string(\"no-cache\"), NGX_HTTP_GZIP_PROXIED_NO_CACHE },\n    { ngx_string(\"no-store\"), NGX_HTTP_GZIP_PROXIED_NO_STORE },\n    { ngx_string(\"private\"), NGX_HTTP_GZIP_PROXIED_PRIVATE },\n    { ngx_string(\"no_last_modified\"), NGX_HTTP_GZIP_PROXIED_NO_LM },\n    { ngx_string(\"no_etag\"), NGX_HTTP_GZIP_PROXIED_NO_ETAG },\n    { ngx_string(\"auth\"), NGX_HTTP_GZIP_PROXIED_AUTH },\n    { ngx_string(\"any\"), NGX_HTTP_GZIP_PROXIED_ANY },\n    { ngx_null_string, 0 }\n};\n\n\nstatic ngx_str_t  ngx_http_gzip_no_cache = ngx_string(\"no-cache\");\nstatic ngx_str_t  ngx_http_gzip_no_store = ngx_string(\"no-store\");\nstatic ngx_str_t  ngx_http_gzip_private = ngx_string(\"private\");\n\n#endif\n\n\nstatic ngx_command_t  ngx_http_core_commands[] = {\n\n    { ngx_string(\"variables_hash_max_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_core_main_conf_t, variables_hash_max_size),\n      NULL },\n\n    { ngx_string(\"variables_hash_bucket_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_core_main_conf_t, variables_hash_bucket_size),\n      NULL },\n\n    { ngx_string(\"server_names_hash_max_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_core_main_conf_t, server_names_hash_max_size),\n      NULL },\n\n    { ngx_string(\"server_names_hash_bucket_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_core_main_conf_t, server_names_hash_bucket_size),\n      NULL },\n\n    { ngx_string(\"server\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,\n      ngx_http_core_server,\n      0,\n      0,\n      NULL },\n\n    { ngx_string(\"connection_pool_size\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      offsetof(ngx_http_core_srv_conf_t, connection_pool_size),\n      &ngx_http_core_pool_size_p },\n\n    { ngx_string(\"request_pool_size\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      offsetof(ngx_http_core_srv_conf_t, request_pool_size),\n      &ngx_http_core_pool_size_p },\n\n    { ngx_string(\"client_header_timeout\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      offsetof(ngx_http_core_srv_conf_t, client_header_timeout),\n      NULL },\n\n    { ngx_string(\"client_header_buffer_size\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      offsetof(ngx_http_core_srv_conf_t, client_header_buffer_size),\n      NULL },\n\n    { ngx_string(\"large_client_header_buffers\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE2,\n      ngx_conf_set_bufs_slot,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      offsetof(ngx_http_core_srv_conf_t, large_client_header_buffers),\n      NULL },\n\n    { ngx_string(\"ignore_invalid_headers\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      offsetof(ngx_http_core_srv_conf_t, ignore_invalid_headers),\n      NULL },\n\n    { ngx_string(\"merge_slashes\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      offsetof(ngx_http_core_srv_conf_t, merge_slashes),\n      NULL },\n\n    { ngx_string(\"underscores_in_headers\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      offsetof(ngx_http_core_srv_conf_t, underscores_in_headers),\n      NULL },\n\n    { ngx_string(\"location\"),\n      NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE12,\n      ngx_http_core_location,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"listen\"),\n      NGX_HTTP_SRV_CONF|NGX_CONF_1MORE,\n      ngx_http_core_listen,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"server_name\"),\n      NGX_HTTP_SRV_CONF|NGX_CONF_1MORE,\n      ngx_http_core_server_name,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"types_hash_max_size\"),\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_core_loc_conf_t, types_hash_max_size),\n      NULL },\n\n    { ngx_string(\"types_hash_bucket_size\"),\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_core_loc_conf_t, types_hash_bucket_size),\n      NULL },\n\n    { ngx_string(\"types\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF\n                                          |NGX_CONF_BLOCK|NGX_CONF_NOARGS,\n      ngx_http_core_types,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"default_type\"),\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_core_loc_conf_t, default_type),\n      NULL },\n\n    { ngx_string(\"root\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF\n                        |NGX_CONF_TAKE1,\n      ngx_http_core_root,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"alias\"),\n      NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_http_core_root,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"limit_except\"),\n      NGX_HTTP_LOC_CONF|NGX_CONF_BLOCK|NGX_CONF_1MORE,\n      ngx_http_core_limit_except,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"client_max_body_size\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_off_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_core_loc_conf_t, client_max_body_size),\n      NULL },\n\n    { ngx_string(\"client_body_buffer_size\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_core_loc_conf_t, client_body_buffer_size),\n      NULL },\n\n#if (T_DEPRECATED)\n    { ngx_string(\"client_body_buffers\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,\n      ngx_conf_set_bufs_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_core_loc_conf_t, client_body_buffers),\n      NULL },\n\n    { ngx_string(\"client_body_postpone_size\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_core_loc_conf_t, client_body_postpone_size),\n      NULL },\n#endif\n\n    { ngx_string(\"client_body_timeout\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_core_loc_conf_t, client_body_timeout),\n      NULL },\n\n    { ngx_string(\"client_body_temp_path\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1234,\n      ngx_conf_set_path_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_core_loc_conf_t, client_body_temp_path),\n      NULL },\n\n    { ngx_string(\"client_body_in_file_only\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_enum_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_core_loc_conf_t, client_body_in_file_only),\n      &ngx_http_core_request_body_in_file },\n\n    { ngx_string(\"client_body_in_single_buffer\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_core_loc_conf_t, client_body_in_single_buffer),\n      NULL },\n\n#if (T_NGX_HTTP_UPSTREAM_RETRY_CC)\n    { ngx_string(\"retry_cached_connection\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_core_loc_conf_t, retry_cached_connection),\n      NULL },\n#endif\n\n    { ngx_string(\"sendfile\"),\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_core_loc_conf_t, sendfile),\n      NULL },\n\n    { ngx_string(\"sendfile_max_chunk\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_core_loc_conf_t, sendfile_max_chunk),\n      NULL },\n\n    { ngx_string(\"subrequest_output_buffer_size\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_core_loc_conf_t, subrequest_output_buffer_size),\n      NULL },\n\n    { ngx_string(\"aio\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_http_core_set_aio,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"aio_write\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_core_loc_conf_t, aio_write),\n      NULL },\n\n    { ngx_string(\"read_ahead\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_core_loc_conf_t, read_ahead),\n      NULL },\n\n    { ngx_string(\"directio\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_http_core_directio,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"directio_alignment\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_off_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_core_loc_conf_t, directio_alignment),\n      NULL },\n\n    { ngx_string(\"tcp_nopush\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_core_loc_conf_t, tcp_nopush),\n      NULL },\n\n    { ngx_string(\"tcp_nodelay\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_core_loc_conf_t, tcp_nodelay),\n      NULL },\n\n    { ngx_string(\"send_timeout\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_core_loc_conf_t, send_timeout),\n      NULL },\n\n    { ngx_string(\"send_lowat\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_core_loc_conf_t, send_lowat),\n      &ngx_http_core_lowat_post },\n\n    { ngx_string(\"postpone_output\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_core_loc_conf_t, postpone_output),\n      NULL },\n\n    { ngx_string(\"limit_rate\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF\n                        |NGX_CONF_TAKE1,\n      ngx_http_set_complex_value_size_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_core_loc_conf_t, limit_rate),\n      NULL },\n\n    { ngx_string(\"limit_rate_after\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF\n                        |NGX_CONF_TAKE1,\n      ngx_http_set_complex_value_size_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_core_loc_conf_t, limit_rate_after),\n      NULL },\n\n    { ngx_string(\"keepalive_time\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_core_loc_conf_t, keepalive_time),\n      NULL },\n\n    { ngx_string(\"keepalive_timeout\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,\n      ngx_http_core_keepalive,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"keepalive_requests\"),\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_core_loc_conf_t, keepalive_requests),\n      NULL },\n\n    { ngx_string(\"keepalive_disable\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,\n      ngx_conf_set_bitmask_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_core_loc_conf_t, keepalive_disable),\n      &ngx_http_core_keepalive_disable },\n\n    { ngx_string(\"satisfy\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_enum_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_core_loc_conf_t, satisfy),\n      &ngx_http_core_satisfy },\n\n    { ngx_string(\"auth_delay\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_core_loc_conf_t, auth_delay),\n      NULL },\n\n    { ngx_string(\"internal\"),\n      NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,\n      ngx_http_core_internal,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"lingering_close\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_enum_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_core_loc_conf_t, lingering_close),\n      &ngx_http_core_lingering_close },\n\n    { ngx_string(\"lingering_time\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_core_loc_conf_t, lingering_time),\n      NULL },\n\n    { ngx_string(\"lingering_timeout\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_core_loc_conf_t, lingering_timeout),\n      NULL },\n\n    { ngx_string(\"reset_timedout_connection\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_core_loc_conf_t, reset_timedout_connection),\n      NULL },\n\n    { ngx_string(\"absolute_redirect\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_core_loc_conf_t, absolute_redirect),\n      NULL },\n\n    { ngx_string(\"server_name_in_redirect\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_core_loc_conf_t, server_name_in_redirect),\n      NULL },\n\n    { ngx_string(\"port_in_redirect\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_core_loc_conf_t, port_in_redirect),\n      NULL },\n\n    { ngx_string(\"msie_padding\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_core_loc_conf_t, msie_padding),\n      NULL },\n\n    { ngx_string(\"msie_refresh\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_core_loc_conf_t, msie_refresh),\n      NULL },\n\n    { ngx_string(\"log_not_found\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_core_loc_conf_t, log_not_found),\n      NULL },\n\n    { ngx_string(\"log_subrequest\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_core_loc_conf_t, log_subrequest),\n      NULL },\n\n    { ngx_string(\"recursive_error_pages\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_core_loc_conf_t, recursive_error_pages),\n      NULL },\n\n#if (T_NGX_RET_CACHE)\n    { ngx_string(\"request_time_cache\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_core_loc_conf_t, request_time_cache),\n      NULL },\n#endif\n\n    { ngx_string(\"server_tokens\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_enum_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_core_loc_conf_t, server_tokens),\n      &ngx_http_core_server_tokens },\n\n#if (T_NGX_SERVER_INFO)\n    { ngx_string(\"server_tag\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_http_set_server_tag,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n#endif\n\n    { ngx_string(\"if_modified_since\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_enum_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_core_loc_conf_t, if_modified_since),\n      &ngx_http_core_if_modified_since },\n\n    { ngx_string(\"max_ranges\"),\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_core_loc_conf_t, max_ranges),\n      NULL },\n\n    { ngx_string(\"chunked_transfer_encoding\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_core_loc_conf_t, chunked_transfer_encoding),\n      NULL },\n\n    { ngx_string(\"etag\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_core_loc_conf_t, etag),\n      NULL },\n\n    { ngx_string(\"error_page\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF\n                        |NGX_CONF_2MORE,\n      ngx_http_core_error_page,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"post_action\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF\n                        |NGX_CONF_TAKE1,\n      ngx_conf_set_str_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_core_loc_conf_t, post_action),\n      NULL },\n\n    { ngx_string(\"error_log\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,\n      ngx_http_core_error_log,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"open_file_cache\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,\n      ngx_http_core_open_file_cache,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_core_loc_conf_t, open_file_cache),\n      NULL },\n\n    { ngx_string(\"open_file_cache_valid\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_sec_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_core_loc_conf_t, open_file_cache_valid),\n      NULL },\n\n    { ngx_string(\"open_file_cache_min_uses\"),\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_core_loc_conf_t, open_file_cache_min_uses),\n      NULL },\n\n    { ngx_string(\"open_file_cache_errors\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_core_loc_conf_t, open_file_cache_errors),\n      NULL },\n\n    { ngx_string(\"open_file_cache_events\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_core_loc_conf_t, open_file_cache_events),\n      NULL },\n\n    { ngx_string(\"resolver\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,\n      ngx_http_core_resolver,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n#if (T_NGX_RESOLVER_FILE)\n    { ngx_string(\"resolver_file\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_http_core_resolver_file,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n#endif\n\n    { ngx_string(\"resolver_timeout\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_core_loc_conf_t, resolver_timeout),\n      NULL },\n\n#if (T_NGX_SERVER_INFO)\n    { ngx_string(\"server_admin\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_slot,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      offsetof(ngx_http_core_srv_conf_t, server_admin),\n      NULL },\n\n    { ngx_string(\"server_info\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_core_loc_conf_t, server_info),\n      NULL },\n#endif\n\n#if (NGX_HTTP_GZIP)\n\n    { ngx_string(\"gzip_vary\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_core_loc_conf_t, gzip_vary),\n      NULL },\n\n    { ngx_string(\"gzip_http_version\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_enum_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_core_loc_conf_t, gzip_http_version),\n      &ngx_http_gzip_http_version },\n\n    { ngx_string(\"gzip_proxied\"),\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_core_loc_conf_t, gzip_proxied),\n      &ngx_http_gzip_proxied_mask },\n\n    { ngx_string(\"gzip_disable\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,\n      ngx_http_gzip_disable,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n#endif\n\n#if (NGX_HAVE_OPENAT)\n\n    { ngx_string(\"disable_symlinks\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12,\n      ngx_http_disable_symlinks,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n#endif\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_core_module_ctx = {\n    ngx_http_core_preconfiguration,        /* preconfiguration */\n    ngx_http_core_postconfiguration,       /* postconfiguration */\n\n    ngx_http_core_create_main_conf,        /* create main configuration */\n    ngx_http_core_init_main_conf,          /* init main configuration */\n\n    ngx_http_core_create_srv_conf,         /* create server configuration */\n    ngx_http_core_merge_srv_conf,          /* merge server configuration */\n\n    ngx_http_core_create_loc_conf,         /* create location configuration */\n    ngx_http_core_merge_loc_conf           /* merge location configuration */\n};\n\n\nngx_module_t  ngx_http_core_module = {\n    NGX_MODULE_V1,\n    &ngx_http_core_module_ctx,             /* module context */\n    ngx_http_core_commands,                /* 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\nngx_str_t  ngx_http_core_get_method = { 3, (u_char *) \"GET\" };\n\n\nvoid\nngx_http_handler(ngx_http_request_t *r)\n{\n    ngx_http_core_main_conf_t  *cmcf;\n\n    r->connection->log->action = NULL;\n\n    if (!r->internal) {\n        switch (r->headers_in.connection_type) {\n        case 0:\n            r->keepalive = (r->http_version > NGX_HTTP_VERSION_10);\n            break;\n\n        case NGX_HTTP_CONNECTION_CLOSE:\n            r->keepalive = 0;\n            break;\n\n        case NGX_HTTP_CONNECTION_KEEP_ALIVE:\n            r->keepalive = 1;\n            break;\n        }\n\n        r->lingering_close = (r->headers_in.content_length_n > 0\n                              || r->headers_in.chunked);\n        r->phase_handler = 0;\n\n    } else {\n        cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);\n        r->phase_handler = cmcf->phase_engine.server_rewrite_index;\n    }\n\n    r->valid_location = 1;\n#if (NGX_HTTP_GZIP)\n    r->gzip_tested = 0;\n    r->gzip_ok = 0;\n    r->gzip_vary = 0;\n#endif\n\n    r->write_event_handler = ngx_http_core_run_phases;\n    ngx_http_core_run_phases(r);\n}\n\n\nvoid\nngx_http_core_run_phases(ngx_http_request_t *r)\n{\n    ngx_int_t                   rc;\n    ngx_http_phase_handler_t   *ph;\n    ngx_http_core_main_conf_t  *cmcf;\n\n    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);\n\n    ph = cmcf->phase_engine.handlers;\n\n    while (ph[r->phase_handler].checker) {\n\n        rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]);\n\n        if (rc == NGX_OK) {\n            return;\n        }\n    }\n}\n\n\nngx_int_t\nngx_http_core_generic_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph)\n{\n    ngx_int_t  rc;\n\n    /*\n     * generic phase checker,\n     * used by the post read and pre-access phases\n     */\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"generic phase: %ui\", r->phase_handler);\n\n    rc = ph->handler(r);\n\n    if (rc == NGX_OK) {\n        r->phase_handler = ph->next;\n        return NGX_AGAIN;\n    }\n\n    if (rc == NGX_DECLINED) {\n        r->phase_handler++;\n        return NGX_AGAIN;\n    }\n\n    if (rc == NGX_AGAIN || rc == NGX_DONE) {\n        return NGX_OK;\n    }\n\n    /* rc == NGX_ERROR || rc == NGX_HTTP_...  */\n\n    ngx_http_finalize_request(r, rc);\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_core_rewrite_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph)\n{\n    ngx_int_t  rc;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"rewrite phase: %ui\", r->phase_handler);\n\n    rc = ph->handler(r);\n\n    if (rc == NGX_DECLINED) {\n        r->phase_handler++;\n        return NGX_AGAIN;\n    }\n\n    if (rc == NGX_DONE) {\n        return NGX_OK;\n    }\n\n    /* NGX_OK, NGX_AGAIN, NGX_ERROR, NGX_HTTP_...  */\n\n    ngx_http_finalize_request(r, rc);\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_core_find_config_phase(ngx_http_request_t *r,\n    ngx_http_phase_handler_t *ph)\n{\n    u_char                    *p;\n    size_t                     len;\n    ngx_int_t                  rc;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    r->content_handler = NULL;\n    r->uri_changed = 0;\n\n#if (NGX_HTTP_PROXY_CONNECT)\n    if (r->method == NGX_HTTP_CONNECT) {\n        ngx_http_update_location_config(r);\n        r->phase_handler++;\n        return NGX_AGAIN;\n    }\n#endif\n\n    rc = ngx_http_core_find_location(r);\n\n    if (rc == NGX_ERROR) {\n        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return NGX_OK;\n    }\n\n#if (T_NGX_HTTP_SSL_VCE && NGX_HTTP_SSL)\n    ngx_connection_t  *c;\n    c = r->connection;\n\n    if (r->main == r && r->http_connection->ssl) {\n        long                      rc;\n        X509                     *cert;\n        ngx_http_ssl_srv_conf_t  *sscf;\n        ngx_http_ssl_loc_conf_t  *slcf;\n\n        if (c->ssl == NULL) {\n            ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                          \"client sent plain HTTP request to HTTPS port\");\n            ngx_http_finalize_request(r, NGX_HTTP_TO_HTTPS);\n            return NGX_OK;\n        }\n\n        sscf = ngx_http_get_module_srv_conf(r, ngx_http_ssl_module);\n        slcf = ngx_http_get_module_loc_conf(r, ngx_http_ssl_module);\n\n        if (sscf->verify && slcf->verify_exception < 1) {\n            rc = SSL_get_verify_result(c->ssl->connection);\n\n            if (rc != X509_V_OK\n                && (sscf->verify != 3 || !ngx_ssl_verify_error_optional(rc)))\n            {\n                ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                              \"client SSL certificate verify error: (%l:%s)\",\n                              rc, X509_verify_cert_error_string(rc));\n\n                ngx_ssl_remove_cached_session(c->ssl->session_ctx,\n                                       (SSL_get0_session(c->ssl->connection)));\n\n                ngx_http_finalize_request(r, NGX_HTTPS_CERT_ERROR);\n                return NGX_OK;\n            }\n\n            if (sscf->verify == 1) {\n                cert = SSL_get_peer_certificate(c->ssl->connection);\n\n                if (cert == NULL) {\n                    ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                                  \"client sent no required SSL certificate\");\n\n                    ngx_ssl_remove_cached_session(c->ssl->session_ctx,\n                                       (SSL_get0_session(c->ssl->connection)));\n\n                    ngx_http_finalize_request(r, NGX_HTTPS_NO_CERT);\n                    return NGX_OK;\n                }\n\n                X509_free(cert);\n            }\n        }\n    }\n\n#endif\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    if (!r->internal && clcf->internal) {\n        ngx_http_finalize_request(r, NGX_HTTP_NOT_FOUND);\n        return NGX_OK;\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"using configuration \\\"%s%V\\\"\",\n                   (clcf->noname ? \"*\" : (clcf->exact_match ? \"=\" : \"\")),\n                   &clcf->name);\n\n    ngx_http_update_location_config(r);\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http cl:%O max:%O\",\n                   r->headers_in.content_length_n, clcf->client_max_body_size);\n\n    if (r->headers_in.content_length_n != -1\n        && !r->discard_body\n        && clcf->client_max_body_size\n        && clcf->client_max_body_size < r->headers_in.content_length_n)\n    {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"client intended to send too large body: %O bytes\",\n                      r->headers_in.content_length_n);\n\n        r->expect_tested = 1;\n        (void) ngx_http_discard_request_body(r);\n        ngx_http_finalize_request(r, NGX_HTTP_REQUEST_ENTITY_TOO_LARGE);\n        return NGX_OK;\n    }\n\n    if (rc == NGX_DONE) {\n        ngx_http_clear_location(r);\n\n        r->headers_out.location = ngx_list_push(&r->headers_out.headers);\n        if (r->headers_out.location == NULL) {\n            ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n            return NGX_OK;\n        }\n\n        r->headers_out.location->hash = 1;\n        r->headers_out.location->next = NULL;\n        ngx_str_set(&r->headers_out.location->key, \"Location\");\n\n        if (r->args.len == 0) {\n            r->headers_out.location->value = clcf->escaped_name;\n\n        } else {\n            len = clcf->escaped_name.len + 1 + r->args.len;\n            p = ngx_pnalloc(r->pool, len);\n\n            if (p == NULL) {\n                ngx_http_clear_location(r);\n                ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n                return NGX_OK;\n            }\n\n            r->headers_out.location->value.len = len;\n            r->headers_out.location->value.data = p;\n\n            p = ngx_cpymem(p, clcf->escaped_name.data, clcf->escaped_name.len);\n            *p++ = '?';\n            ngx_memcpy(p, r->args.data, r->args.len);\n        }\n\n        ngx_http_finalize_request(r, NGX_HTTP_MOVED_PERMANENTLY);\n        return NGX_OK;\n    }\n\n    r->phase_handler++;\n    return NGX_AGAIN;\n}\n\n\nngx_int_t\nngx_http_core_post_rewrite_phase(ngx_http_request_t *r,\n    ngx_http_phase_handler_t *ph)\n{\n    ngx_http_core_srv_conf_t  *cscf;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"post rewrite phase: %ui\", r->phase_handler);\n\n    if (!r->uri_changed) {\n        r->phase_handler++;\n        return NGX_AGAIN;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"uri changes: %d\", r->uri_changes);\n\n    /*\n     * gcc before 3.3 compiles the broken code for\n     *     if (r->uri_changes-- == 0)\n     * if the r->uri_changes is defined as\n     *     unsigned  uri_changes:4\n     */\n\n    r->uri_changes--;\n\n    if (r->uri_changes == 0) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"rewrite or internal redirection cycle \"\n                      \"while processing \\\"%V\\\"\", &r->uri);\n\n        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return NGX_OK;\n    }\n\n    r->phase_handler = ph->next;\n\n    cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);\n    r->loc_conf = cscf->ctx->loc_conf;\n\n    return NGX_AGAIN;\n}\n\n\nngx_int_t\nngx_http_core_access_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph)\n{\n    ngx_int_t                  rc;\n    ngx_table_elt_t           *h;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    if (r != r->main) {\n        r->phase_handler = ph->next;\n        return NGX_AGAIN;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"access phase: %ui\", r->phase_handler);\n\n    rc = ph->handler(r);\n\n    if (rc == NGX_DECLINED) {\n        r->phase_handler++;\n        return NGX_AGAIN;\n    }\n\n    if (rc == NGX_AGAIN || rc == NGX_DONE) {\n        return NGX_OK;\n    }\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    if (clcf->satisfy == NGX_HTTP_SATISFY_ALL) {\n\n        if (rc == NGX_OK) {\n            r->phase_handler++;\n            return NGX_AGAIN;\n        }\n\n    } else {\n        if (rc == NGX_OK) {\n            r->access_code = 0;\n\n            for (h = r->headers_out.www_authenticate; h; h = h->next) {\n                h->hash = 0;\n            }\n\n            r->phase_handler = ph->next;\n            return NGX_AGAIN;\n        }\n\n        if (rc == NGX_HTTP_FORBIDDEN || rc == NGX_HTTP_UNAUTHORIZED) {\n            if (r->access_code != NGX_HTTP_UNAUTHORIZED) {\n                r->access_code = rc;\n            }\n\n            r->phase_handler++;\n            return NGX_AGAIN;\n        }\n    }\n\n    /* rc == NGX_ERROR || rc == NGX_HTTP_...  */\n\n    if (rc == NGX_HTTP_UNAUTHORIZED) {\n        return ngx_http_core_auth_delay(r);\n    }\n\n    ngx_http_finalize_request(r, rc);\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_core_post_access_phase(ngx_http_request_t *r,\n    ngx_http_phase_handler_t *ph)\n{\n    ngx_int_t  access_code;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"post access phase: %ui\", r->phase_handler);\n\n    access_code = r->access_code;\n\n    if (access_code) {\n        r->access_code = 0;\n\n        if (access_code == NGX_HTTP_FORBIDDEN) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"access forbidden by rule\");\n        }\n\n        if (access_code == NGX_HTTP_UNAUTHORIZED) {\n            return ngx_http_core_auth_delay(r);\n        }\n\n        ngx_http_finalize_request(r, access_code);\n        return NGX_OK;\n    }\n\n    r->phase_handler++;\n    return NGX_AGAIN;\n}\n\n\nstatic ngx_int_t\nngx_http_core_auth_delay(ngx_http_request_t *r)\n{\n    ngx_http_core_loc_conf_t  *clcf;\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    if (clcf->auth_delay == 0) {\n        ngx_http_finalize_request(r, NGX_HTTP_UNAUTHORIZED);\n        return NGX_OK;\n    }\n\n    ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                  \"delaying unauthorized request\");\n\n    if (r->connection->read->ready) {\n        ngx_post_event(r->connection->read, &ngx_posted_events);\n\n    } else {\n        if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) {\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n    }\n\n    r->read_event_handler = ngx_http_test_reading;\n    r->write_event_handler = ngx_http_core_auth_delay_handler;\n\n    r->connection->write->delayed = 1;\n    ngx_add_timer(r->connection->write, clcf->auth_delay);\n\n    /*\n     * trigger an additional event loop iteration\n     * to ensure constant-time processing\n     */\n\n    ngx_post_event(r->connection->write, &ngx_posted_next_events);\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_core_auth_delay_handler(ngx_http_request_t *r)\n{\n    ngx_event_t  *wev;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"auth delay handler\");\n\n    wev = r->connection->write;\n\n    if (wev->delayed) {\n\n        if (ngx_handle_write_event(wev, 0) != NGX_OK) {\n            ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n        }\n\n        return;\n    }\n\n    ngx_http_finalize_request(r, NGX_HTTP_UNAUTHORIZED);\n}\n\n\nngx_int_t\nngx_http_core_content_phase(ngx_http_request_t *r,\n    ngx_http_phase_handler_t *ph)\n{\n    size_t     root;\n    ngx_int_t  rc;\n    ngx_str_t  path;\n\n    if (r->content_handler) {\n        r->write_event_handler = ngx_http_request_empty_handler;\n        ngx_http_finalize_request(r, r->content_handler(r));\n        return NGX_OK;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"content phase: %ui\", r->phase_handler);\n\n    rc = ph->handler(r);\n\n    if (rc != NGX_DECLINED) {\n        ngx_http_finalize_request(r, rc);\n        return NGX_OK;\n    }\n\n    /* rc == NGX_DECLINED */\n\n    ph++;\n\n    if (ph->checker) {\n        r->phase_handler++;\n        return NGX_AGAIN;\n    }\n\n    /* no content handler was found */\n\n    if (r->uri.data[r->uri.len - 1] == '/') {\n\n        if (ngx_http_map_uri_to_path(r, &path, &root, 0) != NULL) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"directory index of \\\"%s\\\" is forbidden\", path.data);\n        }\n\n        ngx_http_finalize_request(r, NGX_HTTP_FORBIDDEN);\n        return NGX_OK;\n    }\n\n    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, \"no handler found\");\n\n    ngx_http_finalize_request(r, NGX_HTTP_NOT_FOUND);\n    return NGX_OK;\n}\n\n\nvoid\nngx_http_update_location_config(ngx_http_request_t *r)\n{\n    ngx_http_core_loc_conf_t  *clcf;\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    if (r->method & clcf->limit_except) {\n        r->loc_conf = clcf->limit_except_loc_conf;\n        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n    }\n\n    if (r == r->main) {\n        ngx_set_connection_log(r->connection, clcf->error_log);\n    }\n\n    if ((ngx_io.flags & NGX_IO_SENDFILE) && clcf->sendfile) {\n        r->connection->sendfile = 1;\n\n    } else {\n        r->connection->sendfile = 0;\n    }\n\n    if (clcf->client_body_in_file_only) {\n        r->request_body_in_file_only = 1;\n        r->request_body_in_persistent_file = 1;\n        r->request_body_in_clean_file =\n            clcf->client_body_in_file_only == NGX_HTTP_REQUEST_BODY_FILE_CLEAN;\n        r->request_body_file_log_level = NGX_LOG_NOTICE;\n\n    } else {\n        r->request_body_file_log_level = NGX_LOG_WARN;\n    }\n\n    r->request_body_in_single_buf = clcf->client_body_in_single_buffer;\n\n    if (r->keepalive) {\n        if (clcf->keepalive_timeout == 0) {\n            r->keepalive = 0;\n\n        } else if (r->connection->requests >= clcf->keepalive_requests) {\n            r->keepalive = 0;\n\n        } else if (ngx_current_msec - r->connection->start_time\n                   > clcf->keepalive_time)\n        {\n            r->keepalive = 0;\n\n        } else if (r->headers_in.msie6\n                   && r->method == NGX_HTTP_POST\n                   && (clcf->keepalive_disable\n                       & NGX_HTTP_KEEPALIVE_DISABLE_MSIE6))\n        {\n            /*\n             * MSIE may wait for some time if an response for\n             * a POST request was sent over a keepalive connection\n             */\n            r->keepalive = 0;\n\n        } else if (r->headers_in.safari\n                   && (clcf->keepalive_disable\n                       & NGX_HTTP_KEEPALIVE_DISABLE_SAFARI))\n        {\n            /*\n             * Safari may send a POST request to a closed keepalive\n             * connection and may stall for some time, see\n             *     https://bugs.webkit.org/show_bug.cgi?id=5760\n             */\n            r->keepalive = 0;\n        }\n    }\n\n    if (!clcf->tcp_nopush) {\n        /* disable TCP_NOPUSH/TCP_CORK use */\n        r->connection->tcp_nopush = NGX_TCP_NOPUSH_DISABLED;\n    }\n\n    if (clcf->handler) {\n        r->content_handler = clcf->handler;\n    }\n}\n\n\n/*\n * NGX_OK       - exact or regex match\n * NGX_DONE     - auto redirect\n * NGX_AGAIN    - inclusive match\n * NGX_ERROR    - regex error\n * NGX_DECLINED - no match\n */\n\nstatic ngx_int_t\nngx_http_core_find_location(ngx_http_request_t *r)\n{\n    ngx_int_t                  rc;\n    ngx_http_core_loc_conf_t  *pclcf;\n#if (NGX_PCRE)\n    ngx_int_t                  n;\n    ngx_uint_t                 noregex;\n    ngx_http_core_loc_conf_t  *clcf, **clcfp;\n\n    noregex = 0;\n#endif\n\n    pclcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n#if (T_NGX_HTTP_IMPROVED_REWRITE)\n    if (r->uri.len && r->uri.data[0] == '@') {\n        return ngx_http_core_find_named_location(r);\n    }\n#endif\n    rc = ngx_http_core_find_static_location(r, pclcf->static_locations);\n\n    if (rc == NGX_AGAIN) {\n\n#if (NGX_PCRE)\n        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n        noregex = clcf->noregex;\n#endif\n\n        /* look up nested locations */\n\n        rc = ngx_http_core_find_location(r);\n    }\n\n    if (rc == NGX_OK || rc == NGX_DONE) {\n        return rc;\n    }\n\n    /* rc == NGX_DECLINED or rc == NGX_AGAIN in nested location */\n\n#if (NGX_PCRE)\n\n    if (noregex == 0 && pclcf->regex_locations) {\n\n        for (clcfp = pclcf->regex_locations; *clcfp; clcfp++) {\n\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"test location: ~ \\\"%V\\\"\", &(*clcfp)->name);\n\n            n = ngx_http_regex_exec(r, (*clcfp)->regex, &r->uri);\n\n            if (n == NGX_OK) {\n                r->loc_conf = (*clcfp)->loc_conf;\n\n                /* look up nested locations */\n\n                rc = ngx_http_core_find_location(r);\n\n                return (rc == NGX_ERROR) ? rc : NGX_OK;\n            }\n\n            if (n == NGX_DECLINED) {\n                continue;\n            }\n\n            return NGX_ERROR;\n        }\n    }\n#endif\n\n    return rc;\n}\n\n\n/*\n * NGX_OK       - exact match\n * NGX_DONE     - auto redirect\n * NGX_AGAIN    - inclusive match\n * NGX_DECLINED - no match\n */\n\nstatic ngx_int_t\nngx_http_core_find_static_location(ngx_http_request_t *r,\n    ngx_http_location_tree_node_t *node)\n{\n    u_char     *uri;\n    size_t      len, n;\n    ngx_int_t   rc, rv;\n\n    len = r->uri.len;\n    uri = r->uri.data;\n\n    rv = NGX_DECLINED;\n\n    for ( ;; ) {\n\n        if (node == NULL) {\n            return rv;\n        }\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"test location: \\\"%*s\\\"\",\n                       (size_t) node->len, node->name);\n\n        n = (len <= (size_t) node->len) ? len : node->len;\n\n        rc = ngx_filename_cmp(uri, node->name, n);\n\n        if (rc != 0) {\n            node = (rc < 0) ? node->left : node->right;\n\n            continue;\n        }\n\n        if (len > (size_t) node->len) {\n\n            if (node->inclusive) {\n\n                r->loc_conf = node->inclusive->loc_conf;\n                rv = NGX_AGAIN;\n\n                node = node->tree;\n                uri += n;\n                len -= n;\n\n                continue;\n            }\n\n            /* exact only */\n\n            node = node->right;\n\n            continue;\n        }\n\n        if (len == (size_t) node->len) {\n\n            if (node->exact) {\n                r->loc_conf = node->exact->loc_conf;\n                return NGX_OK;\n\n            } else {\n                r->loc_conf = node->inclusive->loc_conf;\n                return NGX_AGAIN;\n            }\n        }\n\n        /* len < node->len */\n\n        if (len + 1 == (size_t) node->len && node->auto_redirect) {\n\n            r->loc_conf = (node->exact) ? node->exact->loc_conf:\n                                          node->inclusive->loc_conf;\n            rv = NGX_DONE;\n        }\n\n        node = node->left;\n    }\n}\n\n\n#if (T_NGX_HTTP_IMPROVED_REWRITE)\nstatic ngx_int_t\nngx_http_core_find_named_location(ngx_http_request_t *r)\n{\n    ngx_http_core_srv_conf_t    *cscf;\n    ngx_http_core_loc_conf_t   **clcfp;\n     cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);\n     if (cscf->named_locations) {\n         for (clcfp = cscf->named_locations; *clcfp; clcfp++) {\n             ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"test location: \\\"%V\\\"\", &(*clcfp)->name);\n             if (r->uri.len != (*clcfp)->name.len\n                || ngx_strncmp(r->uri.data, (*clcfp)->name.data, r->uri.len)\n                != 0)\n            {\n                continue;\n            }\n             ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"using location: %V \\\"%V?%V\\\"\",\n                           &r->uri, &r->uri, &r->args);\n             r->loc_conf = (*clcfp)->loc_conf;\n             return NGX_OK;\n        }\n    }\n     return NGX_DECLINED;\n}\n#endif\n\n\nvoid *\nngx_http_test_content_type(ngx_http_request_t *r, ngx_hash_t *types_hash)\n{\n    u_char      c, *lowcase;\n    size_t      len;\n    ngx_uint_t  i, hash;\n\n    if (types_hash->size == 0) {\n        return (void *) 4;\n    }\n\n    if (r->headers_out.content_type.len == 0) {\n        return NULL;\n    }\n\n    len = r->headers_out.content_type_len;\n\n    if (r->headers_out.content_type_lowcase == NULL) {\n\n        lowcase = ngx_pnalloc(r->pool, len);\n        if (lowcase == NULL) {\n            return NULL;\n        }\n\n        r->headers_out.content_type_lowcase = lowcase;\n\n        hash = 0;\n\n        for (i = 0; i < len; i++) {\n            c = ngx_tolower(r->headers_out.content_type.data[i]);\n            hash = ngx_hash(hash, c);\n            lowcase[i] = c;\n        }\n\n        r->headers_out.content_type_hash = hash;\n    }\n\n    return ngx_hash_find(types_hash, r->headers_out.content_type_hash,\n                         r->headers_out.content_type_lowcase, len);\n}\n\n\nngx_int_t\nngx_http_set_content_type(ngx_http_request_t *r)\n{\n    u_char                     c, *exten;\n    ngx_str_t                 *type;\n    ngx_uint_t                 i, hash;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    if (r->headers_out.content_type.len) {\n        return NGX_OK;\n    }\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    if (r->exten.len) {\n\n        hash = 0;\n\n        for (i = 0; i < r->exten.len; i++) {\n            c = r->exten.data[i];\n\n            if (c >= 'A' && c <= 'Z') {\n\n                exten = ngx_pnalloc(r->pool, r->exten.len);\n                if (exten == NULL) {\n                    return NGX_ERROR;\n                }\n\n                hash = ngx_hash_strlow(exten, r->exten.data, r->exten.len);\n\n                r->exten.data = exten;\n\n                break;\n            }\n\n            hash = ngx_hash(hash, c);\n        }\n\n        type = ngx_hash_find(&clcf->types_hash, hash,\n                             r->exten.data, r->exten.len);\n\n        if (type) {\n            r->headers_out.content_type_len = type->len;\n            r->headers_out.content_type = *type;\n\n            return NGX_OK;\n        }\n    }\n\n    r->headers_out.content_type_len = clcf->default_type.len;\n    r->headers_out.content_type = clcf->default_type;\n\n    return NGX_OK;\n}\n\n\nvoid\nngx_http_set_exten(ngx_http_request_t *r)\n{\n    ngx_int_t  i;\n\n    ngx_str_null(&r->exten);\n\n    for (i = r->uri.len - 1; i > 1; i--) {\n        if (r->uri.data[i] == '.' && r->uri.data[i - 1] != '/') {\n\n            r->exten.len = r->uri.len - i - 1;\n            r->exten.data = &r->uri.data[i + 1];\n\n            return;\n\n        } else if (r->uri.data[i] == '/') {\n            return;\n        }\n    }\n\n    return;\n}\n\n\nngx_int_t\nngx_http_set_etag(ngx_http_request_t *r)\n{\n    ngx_table_elt_t           *etag;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    if (!clcf->etag) {\n        return NGX_OK;\n    }\n\n    etag = ngx_list_push(&r->headers_out.headers);\n    if (etag == NULL) {\n        return NGX_ERROR;\n    }\n\n    etag->hash = 1;\n    etag->next = NULL;\n    ngx_str_set(&etag->key, \"ETag\");\n\n    etag->value.data = ngx_pnalloc(r->pool, NGX_OFF_T_LEN + NGX_TIME_T_LEN + 3);\n    if (etag->value.data == NULL) {\n        etag->hash = 0;\n        return NGX_ERROR;\n    }\n\n    etag->value.len = ngx_sprintf(etag->value.data, \"\\\"%xT-%xO\\\"\",\n                                  r->headers_out.last_modified_time,\n                                  r->headers_out.content_length_n)\n                      - etag->value.data;\n\n    r->headers_out.etag = etag;\n\n    return NGX_OK;\n}\n\n\nvoid\nngx_http_weak_etag(ngx_http_request_t *r)\n{\n    size_t            len;\n    u_char           *p;\n    ngx_table_elt_t  *etag;\n\n    etag = r->headers_out.etag;\n\n    if (etag == NULL) {\n        return;\n    }\n\n    if (etag->value.len > 2\n        && etag->value.data[0] == 'W'\n        && etag->value.data[1] == '/')\n    {\n        return;\n    }\n\n    if (etag->value.len < 1 || etag->value.data[0] != '\"') {\n        r->headers_out.etag->hash = 0;\n        r->headers_out.etag = NULL;\n        return;\n    }\n\n    p = ngx_pnalloc(r->pool, etag->value.len + 2);\n    if (p == NULL) {\n        r->headers_out.etag->hash = 0;\n        r->headers_out.etag = NULL;\n        return;\n    }\n\n    len = ngx_sprintf(p, \"W/%V\", &etag->value) - p;\n\n    etag->value.data = p;\n    etag->value.len = len;\n}\n\n\nngx_int_t\nngx_http_send_response(ngx_http_request_t *r, ngx_uint_t status,\n    ngx_str_t *ct, ngx_http_complex_value_t *cv)\n{\n    ngx_int_t     rc;\n    ngx_str_t     val;\n    ngx_buf_t    *b;\n    ngx_chain_t   out;\n\n    rc = ngx_http_discard_request_body(r);\n\n    if (rc != NGX_OK) {\n        return rc;\n    }\n\n    r->headers_out.status = status;\n\n    if (ngx_http_complex_value(r, cv, &val) != NGX_OK) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    if (status == NGX_HTTP_MOVED_PERMANENTLY\n        || status == NGX_HTTP_MOVED_TEMPORARILY\n        || status == NGX_HTTP_SEE_OTHER\n        || status == NGX_HTTP_TEMPORARY_REDIRECT\n        || status == NGX_HTTP_PERMANENT_REDIRECT)\n    {\n        ngx_http_clear_location(r);\n\n        r->headers_out.location = ngx_list_push(&r->headers_out.headers);\n        if (r->headers_out.location == NULL) {\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n\n        r->headers_out.location->hash = 1;\n        r->headers_out.location->next = NULL;\n        ngx_str_set(&r->headers_out.location->key, \"Location\");\n        r->headers_out.location->value = val;\n\n        return status;\n    }\n\n    r->headers_out.content_length_n = val.len;\n\n    if (ct) {\n        r->headers_out.content_type_len = ct->len;\n        r->headers_out.content_type = *ct;\n\n    } else {\n        if (ngx_http_set_content_type(r) != NGX_OK) {\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n    }\n\n    b = ngx_calloc_buf(r->pool);\n    if (b == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    b->pos = val.data;\n    b->last = val.data + val.len;\n    b->memory = val.len ? 1 : 0;\n    b->last_buf = (r == r->main) ? 1 : 0;\n    b->last_in_chain = 1;\n    b->sync = (b->last_buf || b->memory) ? 0 : 1;\n\n    out.buf = b;\n    out.next = NULL;\n\n    rc = ngx_http_send_header(r);\n\n    if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {\n        return rc;\n    }\n\n    return ngx_http_output_filter(r, &out);\n}\n\n\nngx_int_t\nngx_http_send_header(ngx_http_request_t *r)\n{\n    if (r->post_action) {\n        return NGX_OK;\n    }\n\n    if (r->header_sent) {\n        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,\n                      \"header already sent\");\n        return NGX_ERROR;\n    }\n\n    if (r->err_status) {\n        r->headers_out.status = r->err_status;\n        r->headers_out.status_line.len = 0;\n    }\n\n    return ngx_http_top_header_filter(r);\n}\n\n\nngx_int_t\nngx_http_output_filter(ngx_http_request_t *r, ngx_chain_t *in)\n{\n    ngx_int_t          rc;\n    ngx_connection_t  *c;\n\n    c = r->connection;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http output filter \\\"%V?%V\\\"\", &r->uri, &r->args);\n\n    rc = ngx_http_top_body_filter(r, in);\n\n    if (rc == NGX_ERROR) {\n        /* NGX_ERROR may be returned by any filter */\n        c->error = 1;\n    }\n\n    return rc;\n}\n\n\nu_char *\nngx_http_map_uri_to_path(ngx_http_request_t *r, ngx_str_t *path,\n    size_t *root_length, size_t reserved)\n{\n    u_char                    *last;\n    size_t                     alias;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    alias = clcf->alias;\n\n    if (alias && !r->valid_location) {\n        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,\n                      \"\\\"alias\\\" cannot be used in location \\\"%V\\\" \"\n                      \"where URI was rewritten\", &clcf->name);\n        return NULL;\n    }\n\n    if (clcf->root_lengths == NULL) {\n\n        *root_length = clcf->root.len;\n\n        path->len = clcf->root.len + reserved + r->uri.len - alias + 1;\n\n        path->data = ngx_pnalloc(r->pool, path->len);\n        if (path->data == NULL) {\n            return NULL;\n        }\n\n        last = ngx_copy(path->data, clcf->root.data, clcf->root.len);\n\n    } else {\n\n        if (alias == NGX_MAX_SIZE_T_VALUE) {\n            reserved += r->add_uri_to_alias ? r->uri.len + 1 : 1;\n\n        } else {\n            reserved += r->uri.len - alias + 1;\n        }\n\n        if (ngx_http_script_run(r, path, clcf->root_lengths->elts, reserved,\n                                clcf->root_values->elts)\n            == NULL)\n        {\n            return NULL;\n        }\n\n        if (ngx_get_full_name(r->pool, (ngx_str_t *) &ngx_cycle->prefix, path)\n            != NGX_OK)\n        {\n            return NULL;\n        }\n\n        *root_length = path->len - reserved;\n        last = path->data + *root_length;\n\n        if (alias == NGX_MAX_SIZE_T_VALUE) {\n            if (!r->add_uri_to_alias) {\n                *last = '\\0';\n                return last;\n            }\n\n            alias = 0;\n        }\n    }\n\n    last = ngx_copy(last, r->uri.data + alias, r->uri.len - alias);\n    *last = '\\0';\n\n    return last;\n}\n\n\nngx_int_t\nngx_http_auth_basic_user(ngx_http_request_t *r)\n{\n    ngx_str_t   auth, encoded;\n    ngx_uint_t  len;\n\n    if (r->headers_in.user.len == 0 && r->headers_in.user.data != NULL) {\n        return NGX_DECLINED;\n    }\n\n    if (r->headers_in.authorization == NULL) {\n        r->headers_in.user.data = (u_char *) \"\";\n        return NGX_DECLINED;\n    }\n\n    encoded = r->headers_in.authorization->value;\n\n    if (encoded.len < sizeof(\"Basic \") - 1\n        || ngx_strncasecmp(encoded.data, (u_char *) \"Basic \",\n                           sizeof(\"Basic \") - 1)\n           != 0)\n    {\n        r->headers_in.user.data = (u_char *) \"\";\n        return NGX_DECLINED;\n    }\n\n    encoded.len -= sizeof(\"Basic \") - 1;\n    encoded.data += sizeof(\"Basic \") - 1;\n\n    while (encoded.len && encoded.data[0] == ' ') {\n        encoded.len--;\n        encoded.data++;\n    }\n\n    if (encoded.len == 0) {\n        r->headers_in.user.data = (u_char *) \"\";\n        return NGX_DECLINED;\n    }\n\n    auth.len = ngx_base64_decoded_length(encoded.len);\n    auth.data = ngx_pnalloc(r->pool, auth.len + 1);\n    if (auth.data == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (ngx_decode_base64(&auth, &encoded) != NGX_OK) {\n        r->headers_in.user.data = (u_char *) \"\";\n        return NGX_DECLINED;\n    }\n\n    auth.data[auth.len] = '\\0';\n\n    for (len = 0; len < auth.len; len++) {\n        if (auth.data[len] == ':') {\n            break;\n        }\n    }\n\n    if (len == 0 || len == auth.len) {\n        r->headers_in.user.data = (u_char *) \"\";\n        return NGX_DECLINED;\n    }\n\n    r->headers_in.user.len = len;\n    r->headers_in.user.data = auth.data;\n    r->headers_in.passwd.len = auth.len - len - 1;\n    r->headers_in.passwd.data = &auth.data[len + 1];\n\n    return NGX_OK;\n}\n\n\n#if (NGX_HTTP_GZIP)\n\nngx_int_t\nngx_http_gzip_ok(ngx_http_request_t *r)\n{\n    time_t                     date, expires;\n    ngx_uint_t                 p;\n    ngx_table_elt_t           *e, *d, *ae, *cc;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    r->gzip_tested = 1;\n\n    if (r != r->main) {\n        return NGX_DECLINED;\n    }\n\n    ae = r->headers_in.accept_encoding;\n    if (ae == NULL) {\n        return NGX_DECLINED;\n    }\n\n    if (ae->value.len < sizeof(\"gzip\") - 1) {\n        return NGX_DECLINED;\n    }\n\n    /*\n     * test first for the most common case \"gzip,...\":\n     *   MSIE:    \"gzip, deflate\"\n     *   Firefox: \"gzip,deflate\"\n     *   Chrome:  \"gzip,deflate,sdch\"\n     *   Safari:  \"gzip, deflate\"\n     *   Opera:   \"gzip, deflate\"\n     */\n\n    if (ngx_memcmp(ae->value.data, \"gzip,\", 5) != 0\n        && ngx_http_gzip_accept_encoding(&ae->value) != NGX_OK)\n    {\n        return NGX_DECLINED;\n    }\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    if (r->headers_in.msie6 && clcf->gzip_disable_msie6) {\n        return NGX_DECLINED;\n    }\n\n    if (r->http_version < clcf->gzip_http_version) {\n        return NGX_DECLINED;\n    }\n\n    if (r->headers_in.via == NULL) {\n        goto ok;\n    }\n\n    p = clcf->gzip_proxied;\n\n    if (p & NGX_HTTP_GZIP_PROXIED_OFF) {\n        return NGX_DECLINED;\n    }\n\n    if (p & NGX_HTTP_GZIP_PROXIED_ANY) {\n        goto ok;\n    }\n\n    if (r->headers_in.authorization && (p & NGX_HTTP_GZIP_PROXIED_AUTH)) {\n        goto ok;\n    }\n\n    e = r->headers_out.expires;\n\n    if (e) {\n\n        if (!(p & NGX_HTTP_GZIP_PROXIED_EXPIRED)) {\n            return NGX_DECLINED;\n        }\n\n        expires = ngx_parse_http_time(e->value.data, e->value.len);\n        if (expires == NGX_ERROR) {\n            return NGX_DECLINED;\n        }\n\n        d = r->headers_out.date;\n\n        if (d) {\n            date = ngx_parse_http_time(d->value.data, d->value.len);\n            if (date == NGX_ERROR) {\n                return NGX_DECLINED;\n            }\n\n        } else {\n            date = ngx_time();\n        }\n\n        if (expires < date) {\n            goto ok;\n        }\n\n        return NGX_DECLINED;\n    }\n\n    cc = r->headers_out.cache_control;\n\n    if (cc) {\n\n        if ((p & NGX_HTTP_GZIP_PROXIED_NO_CACHE)\n            && ngx_http_parse_multi_header_lines(r, cc, &ngx_http_gzip_no_cache,\n                                                 NULL)\n               != NULL)\n        {\n            goto ok;\n        }\n\n        if ((p & NGX_HTTP_GZIP_PROXIED_NO_STORE)\n            && ngx_http_parse_multi_header_lines(r, cc, &ngx_http_gzip_no_store,\n                                                 NULL)\n               != NULL)\n        {\n            goto ok;\n        }\n\n        if ((p & NGX_HTTP_GZIP_PROXIED_PRIVATE)\n            && ngx_http_parse_multi_header_lines(r, cc, &ngx_http_gzip_private,\n                                                 NULL)\n               != NULL)\n        {\n            goto ok;\n        }\n\n        return NGX_DECLINED;\n    }\n\n    if ((p & NGX_HTTP_GZIP_PROXIED_NO_LM) && r->headers_out.last_modified) {\n        return NGX_DECLINED;\n    }\n\n    if ((p & NGX_HTTP_GZIP_PROXIED_NO_ETAG) && r->headers_out.etag) {\n        return NGX_DECLINED;\n    }\n\nok:\n\n#if (NGX_PCRE)\n\n    if (clcf->gzip_disable && r->headers_in.user_agent) {\n\n        if (ngx_regex_exec_array(clcf->gzip_disable,\n                                 &r->headers_in.user_agent->value,\n                                 r->connection->log)\n            != NGX_DECLINED)\n        {\n            return NGX_DECLINED;\n        }\n    }\n\n#endif\n\n    r->gzip_ok = 1;\n\n    return NGX_OK;\n}\n\n\n/*\n * gzip is enabled for the following quantities:\n *     \"gzip; q=0.001\" ... \"gzip; q=1.000\"\n * gzip is disabled for the following quantities:\n *     \"gzip; q=0\" ... \"gzip; q=0.000\", and for any invalid cases\n */\n\nstatic ngx_int_t\nngx_http_gzip_accept_encoding(ngx_str_t *ae)\n{\n    u_char  *p, *start, *last;\n\n    start = ae->data;\n    last = start + ae->len;\n\n    for ( ;; ) {\n        p = ngx_strcasestrn(start, \"gzip\", 4 - 1);\n        if (p == NULL) {\n            return NGX_DECLINED;\n        }\n\n        if (p == start || (*(p - 1) == ',' || *(p - 1) == ' ')) {\n            break;\n        }\n\n        start = p + 4;\n    }\n\n    p += 4;\n\n    while (p < last) {\n        switch (*p++) {\n        case ',':\n            return NGX_OK;\n        case ';':\n            goto quantity;\n        case ' ':\n            continue;\n        default:\n            return NGX_DECLINED;\n        }\n    }\n\n    return NGX_OK;\n\nquantity:\n\n    while (p < last) {\n        switch (*p++) {\n        case 'q':\n        case 'Q':\n            goto equal;\n        case ' ':\n            continue;\n        default:\n            return NGX_DECLINED;\n        }\n    }\n\n    return NGX_OK;\n\nequal:\n\n    if (p + 2 > last || *p++ != '=') {\n        return NGX_DECLINED;\n    }\n\n    if (ngx_http_gzip_quantity(p, last) == 0) {\n        return NGX_DECLINED;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_uint_t\nngx_http_gzip_quantity(u_char *p, u_char *last)\n{\n    u_char      c;\n    ngx_uint_t  n, q;\n\n    c = *p++;\n\n    if (c != '0' && c != '1') {\n        return 0;\n    }\n\n    q = (c - '0') * 100;\n\n    if (p == last) {\n        return q;\n    }\n\n    c = *p++;\n\n    if (c == ',' || c == ' ') {\n        return q;\n    }\n\n    if (c != '.') {\n        return 0;\n    }\n\n    n = 0;\n\n    while (p < last) {\n        c = *p++;\n\n        if (c == ',' || c == ' ') {\n            break;\n        }\n\n        if (c >= '0' && c <= '9') {\n            q += c - '0';\n            n++;\n            continue;\n        }\n\n        return 0;\n    }\n\n    if (q > 100 || n > 3) {\n        return 0;\n    }\n\n    return q;\n}\n\n#endif\n\n\nngx_int_t\nngx_http_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    ngx_time_t                    *tp;\n    ngx_connection_t              *c;\n    ngx_http_request_t            *sr;\n    ngx_http_core_srv_conf_t      *cscf;\n#if (T_NGX_RET_CACHE)\n    ngx_http_core_loc_conf_t      *clcf;\n    struct timeval                 tv;\n#endif\n    ngx_http_postponed_request_t  *pr, *p;\n\n    if (r->subrequests == 0) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"subrequests cycle while processing \\\"%V\\\"\", uri);\n        return NGX_ERROR;\n    }\n\n    /*\n     * 1000 is reserved for other purposes.\n     */\n    if (r->main->count >= 65535 - 1000) {\n        ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,\n                      \"request reference counter overflow \"\n                      \"while processing \\\"%V\\\"\", uri);\n        return NGX_ERROR;\n    }\n\n    if (r->subrequest_in_memory) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"nested in-memory subrequest \\\"%V\\\"\", uri);\n        return NGX_ERROR;\n    }\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#if (T_HTTP_UPSTREAM_TIMEOUT_VAR)\n    sr->connect_time = NGX_CONF_UNSET_MSEC;\n    sr->read_time = NGX_CONF_UNSET_MSEC;\n    sr->send_time = NGX_CONF_UNSET_MSEC;\n#endif\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    if (ngx_list_init(&sr->headers_out.trailers, r->pool, 4,\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 = r->headers_in;\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_V2)\n    sr->stream = r->stream;\n#endif\n\n#if (T_NGX_XQUIC)\n    sr->xqstream = r->xqstream;\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                   \"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    sr->background = (flags & NGX_HTTP_SUBREQUEST_BACKGROUND) != 0;\n\n#if (T_NGX_VARS)\n    sr->raw_uri = r->raw_uri;\n#endif\n    sr->unparsed_uri = r->unparsed_uri;\n    sr->method_name = ngx_http_core_get_method;\n    sr->http_protocol = r->http_protocol;\n    sr->schema = r->schema;\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    if (sr->subrequest_in_memory) {\n        sr->filter_need_in_memory = 1;\n    }\n\n    if (!sr->background) {\n        if (c->data == r && r->postponed == NULL) {\n            c->data = sr;\n        }\n\n        pr = ngx_palloc(r->pool, sizeof(ngx_http_postponed_request_t));\n        if (pr == NULL) {\n            return NGX_ERROR;\n        }\n\n        pr->request = sr;\n        pr->out = NULL;\n        pr->next = NULL;\n\n        if (r->postponed) {\n            for (p = r->postponed; p->next; p = p->next) { /* void */ }\n            p->next = pr;\n\n        } else {\n            r->postponed = pr;\n        }\n    }\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    sr->subrequests = r->subrequests - 1;\n\n#if (T_NGX_RET_CACHE)\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    if (clcf->request_time_cache) {\n        tp = ngx_timeofday();\n        sr->start_sec = tp->sec;\n        sr->start_msec = tp->msec;\n#if (T_NGX_VARS)\n        sr->start_usec = tp->usec;\n#endif\n\n    } else {\n        ngx_gettimeofday(&tv);\n        sr->start_sec = tv.tv_sec;\n        sr->start_msec = tv.tv_usec / 1000;\n        sr->start_usec = tv.tv_usec % 1000;\n#if (T_NGX_VARS)\n        sr->start_usec = tv.tv_usec % 1000;\n#endif\n    }\n\n#else\n    tp = ngx_timeofday();\n    sr->start_sec = tp->sec;\n    sr->start_msec = tp->msec;\n#if (T_NGX_VARS)\n    sr->start_usec = tp.usec;\n#endif\n#endif\n\n    r->main->count++;\n\n    *psr = sr;\n\n    if (flags & NGX_HTTP_SUBREQUEST_CLONE) {\n        sr->method = r->method;\n        sr->method_name = r->method_name;\n        sr->loc_conf = r->loc_conf;\n        sr->valid_location = r->valid_location;\n        sr->valid_unparsed_uri = r->valid_unparsed_uri;\n        sr->content_handler = r->content_handler;\n        sr->phase_handler = r->phase_handler;\n        sr->write_event_handler = ngx_http_core_run_phases;\n\n#if (NGX_PCRE)\n        sr->ncaptures = r->ncaptures;\n        sr->captures = r->captures;\n        sr->captures_data = r->captures_data;\n        sr->realloc_captures = 1;\n        r->realloc_captures = 1;\n#endif\n\n        ngx_http_update_location_config(sr);\n    }\n\n    return ngx_http_post_request(sr, NULL);\n}\n\n\nngx_int_t\nngx_http_internal_redirect(ngx_http_request_t *r,\n    ngx_str_t *uri, ngx_str_t *args)\n{\n    ngx_http_core_srv_conf_t  *cscf;\n\n    r->uri_changes--;\n\n    if (r->uri_changes == 0) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"rewrite or internal redirection cycle \"\n                      \"while internally redirecting to \\\"%V\\\"\", uri);\n\n        r->main->count++;\n        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return NGX_DONE;\n    }\n\n    r->uri = *uri;\n\n    if (args) {\n        r->args = *args;\n\n    } else {\n        ngx_str_null(&r->args);\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"internal redirect: \\\"%V?%V\\\"\", uri, &r->args);\n\n    ngx_http_set_exten(r);\n\n    /* clear the modules contexts */\n    ngx_memzero(r->ctx, sizeof(void *) * ngx_http_max_module);\n\n    cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);\n    r->loc_conf = cscf->ctx->loc_conf;\n\n    ngx_http_update_location_config(r);\n\n#if (NGX_HTTP_CACHE)\n    r->cache = NULL;\n#endif\n\n    r->internal = 1;\n    r->valid_unparsed_uri = 0;\n    r->add_uri_to_alias = 0;\n    r->main->count++;\n\n    ngx_http_handler(r);\n\n    return NGX_DONE;\n}\n\n\nngx_int_t\nngx_http_named_location(ngx_http_request_t *r, ngx_str_t *name)\n{\n    ngx_http_core_srv_conf_t    *cscf;\n    ngx_http_core_loc_conf_t   **clcfp;\n    ngx_http_core_main_conf_t   *cmcf;\n\n    r->main->count++;\n    r->uri_changes--;\n\n    if (r->uri_changes == 0) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"rewrite or internal redirection cycle \"\n                      \"while redirect to named location \\\"%V\\\"\", name);\n\n        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return NGX_DONE;\n    }\n\n    if (r->uri.len == 0) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"empty URI in redirect to named location \\\"%V\\\"\", name);\n\n        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return NGX_DONE;\n    }\n\n    cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);\n\n    if (cscf->named_locations) {\n\n        for (clcfp = cscf->named_locations; *clcfp; clcfp++) {\n\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"test location: \\\"%V\\\"\", &(*clcfp)->name);\n\n            if (name->len != (*clcfp)->name.len\n                || ngx_strncmp(name->data, (*clcfp)->name.data, name->len) != 0)\n            {\n                continue;\n            }\n\n            ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"using location: %V \\\"%V?%V\\\"\",\n                           name, &r->uri, &r->args);\n\n            r->internal = 1;\n            r->content_handler = NULL;\n            r->uri_changed = 0;\n            r->loc_conf = (*clcfp)->loc_conf;\n\n            /* clear the modules contexts */\n            ngx_memzero(r->ctx, sizeof(void *) * ngx_http_max_module);\n\n            ngx_http_update_location_config(r);\n\n            cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);\n\n            r->phase_handler = cmcf->phase_engine.location_rewrite_index;\n\n            r->write_event_handler = ngx_http_core_run_phases;\n            ngx_http_core_run_phases(r);\n\n            return NGX_DONE;\n        }\n    }\n\n    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                  \"could not find named location \\\"%V\\\"\", name);\n\n    ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n\n    return NGX_DONE;\n}\n\n\nngx_http_cleanup_t *\nngx_http_cleanup_add(ngx_http_request_t *r, size_t size)\n{\n    ngx_http_cleanup_t  *cln;\n\n    r = r->main;\n\n    cln = ngx_palloc(r->pool, sizeof(ngx_http_cleanup_t));\n    if (cln == NULL) {\n        return NULL;\n    }\n\n    if (size) {\n        cln->data = ngx_palloc(r->pool, size);\n        if (cln->data == NULL) {\n            return NULL;\n        }\n\n    } else {\n        cln->data = NULL;\n    }\n\n    cln->handler = NULL;\n    cln->next = r->cleanup;\n\n    r->cleanup = cln;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http cleanup add: %p\", cln);\n\n    return cln;\n}\n\n\nngx_int_t\nngx_http_set_disable_symlinks(ngx_http_request_t *r,\n    ngx_http_core_loc_conf_t *clcf, ngx_str_t *path, ngx_open_file_info_t *of)\n{\n#if (NGX_HAVE_OPENAT)\n    u_char     *p;\n    ngx_str_t   from;\n\n    of->disable_symlinks = clcf->disable_symlinks;\n\n    if (clcf->disable_symlinks_from == NULL) {\n        return NGX_OK;\n    }\n\n    if (ngx_http_complex_value(r, clcf->disable_symlinks_from, &from)\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    if (from.len == 0\n        || from.len > path->len\n        || ngx_memcmp(path->data, from.data, from.len) != 0)\n    {\n        return NGX_OK;\n    }\n\n    if (from.len == path->len) {\n        of->disable_symlinks = NGX_DISABLE_SYMLINKS_OFF;\n        return NGX_OK;\n    }\n\n    p = path->data + from.len;\n\n    if (*p == '/') {\n        of->disable_symlinks_from = from.len;\n        return NGX_OK;\n    }\n\n    p--;\n\n    if (*p == '/') {\n        of->disable_symlinks_from = from.len - 1;\n    }\n#endif\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_get_forwarded_addr(ngx_http_request_t *r, ngx_addr_t *addr,\n    ngx_table_elt_t *headers, ngx_str_t *value, ngx_array_t *proxies,\n    int recursive)\n{\n    ngx_int_t         rc;\n    ngx_uint_t        found;\n    ngx_table_elt_t  *h, *next;\n\n    if (headers == NULL) {\n        return ngx_http_get_forwarded_addr_internal(r, addr, value->data,\n                                                    value->len, proxies,\n                                                    recursive);\n    }\n\n    /* revert headers order */\n\n    for (h = headers, headers = NULL; h; h = next) {\n        next = h->next;\n        h->next = headers;\n        headers = h;\n    }\n\n    /* iterate over all headers in reverse order */\n\n    rc = NGX_DECLINED;\n\n    found = 0;\n\n    for (h = headers; h; h = h->next) {\n        rc = ngx_http_get_forwarded_addr_internal(r, addr, h->value.data,\n                                                  h->value.len, proxies,\n                                                  recursive);\n\n        if (!recursive) {\n            break;\n        }\n\n        if (rc == NGX_DECLINED && found) {\n            rc = NGX_DONE;\n            break;\n        }\n\n        if (rc != NGX_OK) {\n            break;\n        }\n\n        found = 1;\n    }\n\n    /* restore headers order */\n\n    for (h = headers, headers = NULL; h; h = next) {\n        next = h->next;\n        h->next = headers;\n        headers = h;\n    }\n\n    return rc;\n}\n\n\nstatic ngx_int_t\nngx_http_get_forwarded_addr_internal(ngx_http_request_t *r, ngx_addr_t *addr,\n    u_char *xff, size_t xfflen, ngx_array_t *proxies, int recursive)\n{\n    u_char      *p;\n    ngx_addr_t   paddr;\n    ngx_uint_t   found;\n\n    found = 0;\n\n    do {\n\n        if (ngx_cidr_match(addr->sockaddr, proxies) != NGX_OK) {\n            return found ? NGX_DONE : NGX_DECLINED;\n        }\n\n        for (p = xff + xfflen - 1; p > xff; p--, xfflen--) {\n            if (*p != ' ' && *p != ',') {\n                break;\n            }\n        }\n\n        for ( /* void */ ; p > xff; p--) {\n            if (*p == ' ' || *p == ',') {\n                p++;\n                break;\n            }\n        }\n\n        if (ngx_parse_addr_port(r->pool, &paddr, p, xfflen - (p - xff))\n            != NGX_OK)\n        {\n            return found ? NGX_DONE : NGX_DECLINED;\n        }\n\n        *addr = paddr;\n        found = 1;\n        xfflen = p - 1 - xff;\n\n    } while (recursive && p > xff);\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_link_multi_headers(ngx_http_request_t *r)\n{\n    ngx_uint_t        i, j;\n    ngx_list_part_t  *part, *ppart;\n    ngx_table_elt_t  *header, *pheader, **ph;\n\n    if (r->headers_in.multi_linked) {\n        return NGX_OK;\n    }\n\n    r->headers_in.multi_linked = 1;\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        header[i].next = NULL;\n\n        /*\n         * search for previous headers with the same name;\n         * if there are any, link to them\n         */\n\n        ppart = &r->headers_in.headers.part;\n        pheader = ppart->elts;\n\n        for (j = 0; /* void */; j++) {\n\n            if (j >= ppart->nelts) {\n                if (ppart->next == NULL) {\n                    break;\n                }\n\n                ppart = ppart->next;\n                pheader = ppart->elts;\n                j = 0;\n            }\n\n            if (part == ppart && i == j) {\n                break;\n            }\n\n            if (header[i].key.len == pheader[j].key.len\n                && ngx_strncasecmp(header[i].key.data, pheader[j].key.data,\n                                   header[i].key.len)\n                   == 0)\n            {\n                ph = &pheader[j].next;\n                while (*ph) { ph = &(*ph)->next; }\n                *ph = &header[i];\n\n                r->headers_in.multi = 1;\n\n                break;\n            }\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nstatic char *\nngx_http_core_server(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy)\n{\n    char                        *rv;\n    void                        *mconf;\n    size_t                       len;\n    u_char                      *p;\n    ngx_uint_t                   i;\n    ngx_conf_t                   pcf;\n    ngx_http_module_t           *module;\n    struct sockaddr_in          *sin;\n    ngx_http_conf_ctx_t         *ctx, *http_ctx;\n    ngx_http_listen_opt_t        lsopt;\n    ngx_http_core_srv_conf_t    *cscf, **cscfp;\n    ngx_http_core_main_conf_t   *cmcf;\n\n    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));\n    if (ctx == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    http_ctx = cf->ctx;\n    ctx->main_conf = http_ctx->main_conf;\n\n    /* the server{}'s srv_conf */\n\n    ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);\n    if (ctx->srv_conf == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    /* the server{}'s loc_conf */\n\n    ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);\n    if (ctx->loc_conf == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    for (i = 0; cf->cycle->modules[i]; i++) {\n        if (cf->cycle->modules[i]->type != NGX_HTTP_MODULE) {\n            continue;\n        }\n\n        module = cf->cycle->modules[i]->ctx;\n\n        if (module->create_srv_conf) {\n            mconf = module->create_srv_conf(cf);\n            if (mconf == NULL) {\n                return NGX_CONF_ERROR;\n            }\n\n            ctx->srv_conf[cf->cycle->modules[i]->ctx_index] = mconf;\n        }\n\n        if (module->create_loc_conf) {\n            mconf = module->create_loc_conf(cf);\n            if (mconf == NULL) {\n                return NGX_CONF_ERROR;\n            }\n\n            ctx->loc_conf[cf->cycle->modules[i]->ctx_index] = mconf;\n        }\n    }\n\n\n    /* the server configuration context */\n\n    cscf = ctx->srv_conf[ngx_http_core_module.ctx_index];\n    cscf->ctx = ctx;\n\n\n    cmcf = ctx->main_conf[ngx_http_core_module.ctx_index];\n\n    cscfp = ngx_array_push(&cmcf->servers);\n    if (cscfp == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    *cscfp = cscf;\n\n\n    /* parse inside server{} */\n\n    pcf = *cf;\n    cf->ctx = ctx;\n    cf->cmd_type = NGX_HTTP_SRV_CONF;\n\n    rv = ngx_conf_parse(cf, NULL);\n\n    *cf = pcf;\n\n    if (rv == NGX_CONF_OK && !cscf->listen) {\n        ngx_memzero(&lsopt, sizeof(ngx_http_listen_opt_t));\n\n        p = ngx_pcalloc(cf->pool, sizeof(struct sockaddr_in));\n        if (p == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        lsopt.sockaddr = (struct sockaddr *) p;\n\n        sin = (struct sockaddr_in *) p;\n\n        sin->sin_family = AF_INET;\n#if (NGX_WIN32)\n        sin->sin_port = htons(80);\n#else\n        sin->sin_port = htons((getuid() == 0) ? 80 : 8000);\n#endif\n        sin->sin_addr.s_addr = INADDR_ANY;\n\n        lsopt.socklen = sizeof(struct sockaddr_in);\n\n        lsopt.backlog = NGX_LISTEN_BACKLOG;\n        lsopt.rcvbuf = -1;\n        lsopt.sndbuf = -1;\n#if (NGX_HAVE_SETFIB)\n        lsopt.setfib = -1;\n#endif\n#if (NGX_HAVE_TCP_FASTOPEN)\n        lsopt.fastopen = -1;\n#endif\n        lsopt.wildcard = 1;\n\n        len = NGX_INET_ADDRSTRLEN + sizeof(\":65535\") - 1;\n\n        p = ngx_pnalloc(cf->pool, len);\n        if (p == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        lsopt.addr_text.data = p;\n        lsopt.addr_text.len = ngx_sock_ntop(lsopt.sockaddr, lsopt.socklen, p,\n                                            len, 1);\n\n        if (ngx_http_add_listen(cf, cscf, &lsopt) != NGX_OK) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    return rv;\n}\n\n\nstatic char *\nngx_http_core_location(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy)\n{\n    char                      *rv;\n    u_char                    *mod;\n    size_t                     len;\n    ngx_str_t                 *value, *name;\n    ngx_uint_t                 i;\n    ngx_conf_t                 save;\n    ngx_http_module_t         *module;\n    ngx_http_conf_ctx_t       *ctx, *pctx;\n    ngx_http_core_loc_conf_t  *clcf, *pclcf;\n\n    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));\n    if (ctx == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    pctx = cf->ctx;\n    ctx->main_conf = pctx->main_conf;\n    ctx->srv_conf = pctx->srv_conf;\n\n    ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);\n    if (ctx->loc_conf == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    for (i = 0; cf->cycle->modules[i]; i++) {\n        if (cf->cycle->modules[i]->type != NGX_HTTP_MODULE) {\n            continue;\n        }\n\n        module = cf->cycle->modules[i]->ctx;\n\n        if (module->create_loc_conf) {\n            ctx->loc_conf[cf->cycle->modules[i]->ctx_index] =\n                                                   module->create_loc_conf(cf);\n            if (ctx->loc_conf[cf->cycle->modules[i]->ctx_index] == NULL) {\n                return NGX_CONF_ERROR;\n            }\n        }\n    }\n\n    clcf = ctx->loc_conf[ngx_http_core_module.ctx_index];\n    clcf->loc_conf = ctx->loc_conf;\n\n    value = cf->args->elts;\n\n    if (cf->args->nelts == 3) {\n\n        len = value[1].len;\n        mod = value[1].data;\n        name = &value[2];\n\n        if (len == 1 && mod[0] == '=') {\n\n            clcf->name = *name;\n            clcf->exact_match = 1;\n\n        } else if (len == 2 && mod[0] == '^' && mod[1] == '~') {\n\n            clcf->name = *name;\n            clcf->noregex = 1;\n\n        } else if (len == 1 && mod[0] == '~') {\n\n            if (ngx_http_core_regex_location(cf, clcf, name, 0) != NGX_OK) {\n                return NGX_CONF_ERROR;\n            }\n\n        } else if (len == 2 && mod[0] == '~' && mod[1] == '*') {\n\n            if (ngx_http_core_regex_location(cf, clcf, name, 1) != NGX_OK) {\n                return NGX_CONF_ERROR;\n            }\n\n        } else {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"invalid location modifier \\\"%V\\\"\", &value[1]);\n            return NGX_CONF_ERROR;\n        }\n\n    } else {\n\n        name = &value[1];\n\n        if (name->data[0] == '=') {\n\n            clcf->name.len = name->len - 1;\n            clcf->name.data = name->data + 1;\n            clcf->exact_match = 1;\n\n        } else if (name->data[0] == '^' && name->data[1] == '~') {\n\n            clcf->name.len = name->len - 2;\n            clcf->name.data = name->data + 2;\n            clcf->noregex = 1;\n\n        } else if (name->data[0] == '~') {\n\n            name->len--;\n            name->data++;\n\n            if (name->data[0] == '*') {\n\n                name->len--;\n                name->data++;\n\n                if (ngx_http_core_regex_location(cf, clcf, name, 1) != NGX_OK) {\n                    return NGX_CONF_ERROR;\n                }\n\n            } else {\n                if (ngx_http_core_regex_location(cf, clcf, name, 0) != NGX_OK) {\n                    return NGX_CONF_ERROR;\n                }\n            }\n\n        } else {\n\n            clcf->name = *name;\n\n            if (name->data[0] == '@') {\n                clcf->named = 1;\n            }\n        }\n    }\n\n    pclcf = pctx->loc_conf[ngx_http_core_module.ctx_index];\n\n    if (cf->cmd_type == NGX_HTTP_LOC_CONF) {\n\n        /* nested location */\n\n#if 0\n        clcf->prev_location = pclcf;\n#endif\n\n        if (pclcf->exact_match) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"location \\\"%V\\\" cannot be inside \"\n                               \"the exact location \\\"%V\\\"\",\n                               &clcf->name, &pclcf->name);\n            return NGX_CONF_ERROR;\n        }\n\n        if (pclcf->named) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"location \\\"%V\\\" cannot be inside \"\n                               \"the named location \\\"%V\\\"\",\n                               &clcf->name, &pclcf->name);\n            return NGX_CONF_ERROR;\n        }\n\n        if (clcf->named) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"named location \\\"%V\\\" can be \"\n                               \"on the server level only\",\n                               &clcf->name);\n            return NGX_CONF_ERROR;\n        }\n\n        len = pclcf->name.len;\n\n#if (NGX_PCRE)\n        if (clcf->regex == NULL\n            && ngx_filename_cmp(clcf->name.data, pclcf->name.data, len) != 0)\n#else\n        if (ngx_filename_cmp(clcf->name.data, pclcf->name.data, len) != 0)\n#endif\n        {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"location \\\"%V\\\" is outside location \\\"%V\\\"\",\n                               &clcf->name, &pclcf->name);\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    if (ngx_http_add_location(cf, &pclcf->locations, clcf) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    save = *cf;\n    cf->ctx = ctx;\n    cf->cmd_type = NGX_HTTP_LOC_CONF;\n\n    rv = ngx_conf_parse(cf, NULL);\n\n    *cf = save;\n\n    return rv;\n}\n\n\nstatic ngx_int_t\nngx_http_core_regex_location(ngx_conf_t *cf, ngx_http_core_loc_conf_t *clcf,\n    ngx_str_t *regex, ngx_uint_t caseless)\n{\n#if (NGX_PCRE)\n    ngx_regex_compile_t  rc;\n    u_char               errstr[NGX_MAX_CONF_ERRSTR];\n\n    ngx_memzero(&rc, sizeof(ngx_regex_compile_t));\n\n    rc.pattern = *regex;\n    rc.err.len = NGX_MAX_CONF_ERRSTR;\n    rc.err.data = errstr;\n\n#if (NGX_HAVE_CASELESS_FILESYSTEM)\n    rc.options = NGX_REGEX_CASELESS;\n#else\n    rc.options = caseless ? NGX_REGEX_CASELESS : 0;\n#endif\n\n    clcf->regex = ngx_http_regex_compile(cf, &rc);\n    if (clcf->regex == NULL) {\n        return NGX_ERROR;\n    }\n\n    clcf->name = *regex;\n\n    return NGX_OK;\n\n#else\n\n    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                       \"using regex \\\"%V\\\" requires PCRE library\",\n                       regex);\n    return NGX_ERROR;\n\n#endif\n}\n\n\nstatic char *\nngx_http_core_types(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_core_loc_conf_t *clcf = conf;\n\n    char        *rv;\n    ngx_conf_t   save;\n\n    if (clcf->types == NULL) {\n        clcf->types = ngx_array_create(cf->pool, 64, sizeof(ngx_hash_key_t));\n        if (clcf->types == NULL) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    save = *cf;\n    cf->handler = ngx_http_core_type;\n    cf->handler_conf = conf;\n\n    rv = ngx_conf_parse(cf, NULL);\n\n    *cf = save;\n\n    return rv;\n}\n\n\nstatic char *\nngx_http_core_type(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)\n{\n    ngx_http_core_loc_conf_t *clcf = conf;\n\n    ngx_str_t       *value, *content_type, *old;\n    ngx_uint_t       i, n, hash;\n    ngx_hash_key_t  *type;\n\n    value = cf->args->elts;\n\n    if (ngx_strcmp(value[0].data, \"include\") == 0) {\n        if (cf->args->nelts != 2) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"invalid number of arguments\"\n                               \" in \\\"include\\\" directive\");\n            return NGX_CONF_ERROR;\n        }\n\n        return ngx_conf_include(cf, dummy, conf);\n    }\n\n    content_type = ngx_palloc(cf->pool, sizeof(ngx_str_t));\n    if (content_type == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    *content_type = value[0];\n\n    for (i = 1; i < cf->args->nelts; i++) {\n\n        hash = ngx_hash_strlow(value[i].data, value[i].data, value[i].len);\n\n        type = clcf->types->elts;\n        for (n = 0; n < clcf->types->nelts; n++) {\n            if (ngx_strcmp(value[i].data, type[n].key.data) == 0) {\n                old = type[n].value;\n                type[n].value = content_type;\n\n                ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                                   \"duplicate extension \\\"%V\\\", \"\n                                   \"content type: \\\"%V\\\", \"\n                                   \"previous content type: \\\"%V\\\"\",\n                                   &value[i], content_type, old);\n                goto next;\n            }\n        }\n\n\n        type = ngx_array_push(clcf->types);\n        if (type == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        type->key = value[i];\n        type->key_hash = hash;\n        type->value = content_type;\n\n    next:\n        continue;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_core_preconfiguration(ngx_conf_t *cf)\n{\n    return ngx_http_variables_add_core_vars(cf);\n}\n\n\nstatic ngx_int_t\nngx_http_core_postconfiguration(ngx_conf_t *cf)\n{\n    ngx_http_top_request_body_filter = ngx_http_request_body_save_filter;\n\n    return NGX_OK;\n}\n\n\nstatic void *\nngx_http_core_create_main_conf(ngx_conf_t *cf)\n{\n    ngx_http_core_main_conf_t  *cmcf;\n\n    cmcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_core_main_conf_t));\n    if (cmcf == NULL) {\n        return NULL;\n    }\n\n    if (ngx_array_init(&cmcf->servers, cf->pool, 4,\n                       sizeof(ngx_http_core_srv_conf_t *))\n        != NGX_OK)\n    {\n        return NULL;\n    }\n\n    cmcf->server_names_hash_max_size = NGX_CONF_UNSET_UINT;\n    cmcf->server_names_hash_bucket_size = NGX_CONF_UNSET_UINT;\n\n    cmcf->variables_hash_max_size = NGX_CONF_UNSET_UINT;\n    cmcf->variables_hash_bucket_size = NGX_CONF_UNSET_UINT;\n\n    return cmcf;\n}\n\n\nstatic char *\nngx_http_core_init_main_conf(ngx_conf_t *cf, void *conf)\n{\n    ngx_http_core_main_conf_t *cmcf = conf;\n\n    ngx_conf_init_uint_value(cmcf->server_names_hash_max_size, 512);\n    ngx_conf_init_uint_value(cmcf->server_names_hash_bucket_size,\n                             ngx_cacheline_size);\n\n    cmcf->server_names_hash_bucket_size =\n            ngx_align(cmcf->server_names_hash_bucket_size, ngx_cacheline_size);\n\n\n    ngx_conf_init_uint_value(cmcf->variables_hash_max_size, 1024);\n    ngx_conf_init_uint_value(cmcf->variables_hash_bucket_size, 64);\n\n    cmcf->variables_hash_bucket_size =\n               ngx_align(cmcf->variables_hash_bucket_size, ngx_cacheline_size);\n\n    if (cmcf->ncaptures) {\n        cmcf->ncaptures = (cmcf->ncaptures + 1) * 3;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic void *\nngx_http_core_create_srv_conf(ngx_conf_t *cf)\n{\n    ngx_http_core_srv_conf_t  *cscf;\n\n    cscf = ngx_pcalloc(cf->pool, sizeof(ngx_http_core_srv_conf_t));\n    if (cscf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc():\n     *\n     *     conf->client_large_buffers.num = 0;\n     */\n\n    if (ngx_array_init(&cscf->server_names, cf->temp_pool, 4,\n                       sizeof(ngx_http_server_name_t))\n        != NGX_OK)\n    {\n        return NULL;\n    }\n\n    cscf->connection_pool_size = NGX_CONF_UNSET_SIZE;\n    cscf->request_pool_size = NGX_CONF_UNSET_SIZE;\n    cscf->client_header_timeout = NGX_CONF_UNSET_MSEC;\n    cscf->client_header_buffer_size = NGX_CONF_UNSET_SIZE;\n    cscf->ignore_invalid_headers = NGX_CONF_UNSET;\n    cscf->merge_slashes = NGX_CONF_UNSET;\n    cscf->underscores_in_headers = NGX_CONF_UNSET;\n\n    cscf->file_name = cf->conf_file->file.name.data;\n    cscf->line = cf->conf_file->line;\n\n    return cscf;\n}\n\n\nstatic char *\nngx_http_core_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_http_core_srv_conf_t *prev = parent;\n    ngx_http_core_srv_conf_t *conf = child;\n\n    ngx_str_t                name;\n    ngx_http_server_name_t  *sn;\n\n    /* TODO: it does not merge, it inits only */\n\n    ngx_conf_merge_size_value(conf->connection_pool_size,\n                              prev->connection_pool_size, 64 * sizeof(void *));\n    ngx_conf_merge_size_value(conf->request_pool_size,\n                              prev->request_pool_size, 4096);\n    ngx_conf_merge_msec_value(conf->client_header_timeout,\n                              prev->client_header_timeout, 60000);\n    ngx_conf_merge_size_value(conf->client_header_buffer_size,\n                              prev->client_header_buffer_size, 1024);\n    ngx_conf_merge_bufs_value(conf->large_client_header_buffers,\n                              prev->large_client_header_buffers,\n                              4, 8192);\n#if (T_NGX_SERVER_INFO)\n    ngx_conf_merge_str_value(conf->server_admin, prev->server_admin, \"\");\n#endif\n\n    if (conf->large_client_header_buffers.size < conf->connection_pool_size) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"the \\\"large_client_header_buffers\\\" size must be \"\n                           \"equal to or greater than \\\"connection_pool_size\\\"\");\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_conf_merge_value(conf->ignore_invalid_headers,\n                              prev->ignore_invalid_headers, 1);\n\n    ngx_conf_merge_value(conf->merge_slashes, prev->merge_slashes, 1);\n\n    ngx_conf_merge_value(conf->underscores_in_headers,\n                              prev->underscores_in_headers, 0);\n\n    if (conf->server_names.nelts == 0) {\n        /* the array has 4 empty preallocated elements, so push cannot fail */\n        sn = ngx_array_push(&conf->server_names);\n#if (NGX_PCRE)\n        sn->regex = NULL;\n#endif\n        sn->server = conf;\n        ngx_str_set(&sn->name, \"\");\n    }\n\n    sn = conf->server_names.elts;\n    name = sn[0].name;\n\n#if (NGX_PCRE)\n    if (sn->regex) {\n        name.len++;\n        name.data--;\n    } else\n#endif\n\n    if (name.data[0] == '.') {\n        name.len--;\n        name.data++;\n    }\n\n    conf->server_name.len = name.len;\n    conf->server_name.data = ngx_pstrdup(cf->pool, &name);\n    if (conf->server_name.data == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic void *\nngx_http_core_create_loc_conf(ngx_conf_t *cf)\n{\n    ngx_http_core_loc_conf_t  *clcf;\n\n    clcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_core_loc_conf_t));\n    if (clcf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc():\n     *\n     *     clcf->escaped_name = { 0, NULL };\n     *     clcf->root = { 0, NULL };\n     *     clcf->limit_except = 0;\n     *     clcf->post_action = { 0, NULL };\n     *     clcf->types = NULL;\n     *     clcf->default_type = { 0, NULL };\n     *     clcf->error_log = NULL;\n     *     clcf->error_pages = NULL;\n     *     clcf->client_body_path = NULL;\n     *     clcf->regex = NULL;\n     *     clcf->exact_match = 0;\n     *     clcf->auto_redirect = 0;\n     *     clcf->alias = 0;\n     *     clcf->gzip_proxied = 0;\n     *     clcf->keepalive_disable = 0;\n     */\n\n    clcf->client_max_body_size = NGX_CONF_UNSET;\n    clcf->client_body_buffer_size = NGX_CONF_UNSET_SIZE;\n#if (T_DEPRECATED)\n    clcf->client_body_postpone_size = NGX_CONF_UNSET_SIZE;\n#endif\n    clcf->client_body_timeout = NGX_CONF_UNSET_MSEC;\n    clcf->satisfy = NGX_CONF_UNSET_UINT;\n    clcf->auth_delay = NGX_CONF_UNSET_MSEC;\n    clcf->if_modified_since = NGX_CONF_UNSET_UINT;\n    clcf->max_ranges = NGX_CONF_UNSET_UINT;\n    clcf->client_body_in_file_only = NGX_CONF_UNSET_UINT;\n    clcf->client_body_in_single_buffer = NGX_CONF_UNSET;\n#if (T_NGX_HTTP_UPSTREAM_RETRY_CC)\n    clcf->retry_cached_connection = NGX_CONF_UNSET;\n#endif\n    clcf->internal = NGX_CONF_UNSET;\n    clcf->sendfile = NGX_CONF_UNSET;\n    clcf->sendfile_max_chunk = NGX_CONF_UNSET_SIZE;\n    clcf->subrequest_output_buffer_size = NGX_CONF_UNSET_SIZE;\n    clcf->aio = NGX_CONF_UNSET;\n    clcf->aio_write = NGX_CONF_UNSET;\n#if (NGX_THREADS)\n    clcf->thread_pool = NGX_CONF_UNSET_PTR;\n    clcf->thread_pool_value = NGX_CONF_UNSET_PTR;\n#endif\n    clcf->read_ahead = NGX_CONF_UNSET_SIZE;\n    clcf->directio = NGX_CONF_UNSET;\n    clcf->directio_alignment = NGX_CONF_UNSET;\n    clcf->tcp_nopush = NGX_CONF_UNSET;\n    clcf->tcp_nodelay = NGX_CONF_UNSET;\n    clcf->send_timeout = NGX_CONF_UNSET_MSEC;\n    clcf->send_lowat = NGX_CONF_UNSET_SIZE;\n    clcf->postpone_output = NGX_CONF_UNSET_SIZE;\n    clcf->limit_rate = NGX_CONF_UNSET_PTR;\n    clcf->limit_rate_after = NGX_CONF_UNSET_PTR;\n    clcf->keepalive_time = NGX_CONF_UNSET_MSEC;\n    clcf->keepalive_timeout = NGX_CONF_UNSET_MSEC;\n    clcf->keepalive_header = NGX_CONF_UNSET;\n    clcf->keepalive_requests = NGX_CONF_UNSET_UINT;\n    clcf->lingering_close = NGX_CONF_UNSET_UINT;\n    clcf->lingering_time = NGX_CONF_UNSET_MSEC;\n    clcf->lingering_timeout = NGX_CONF_UNSET_MSEC;\n    clcf->resolver_timeout = NGX_CONF_UNSET_MSEC;\n    clcf->reset_timedout_connection = NGX_CONF_UNSET;\n    clcf->absolute_redirect = NGX_CONF_UNSET;\n    clcf->server_name_in_redirect = NGX_CONF_UNSET;\n    clcf->port_in_redirect = NGX_CONF_UNSET;\n    clcf->msie_padding = NGX_CONF_UNSET;\n    clcf->msie_refresh = NGX_CONF_UNSET;\n    clcf->log_not_found = NGX_CONF_UNSET;\n    clcf->log_subrequest = NGX_CONF_UNSET;\n    clcf->recursive_error_pages = NGX_CONF_UNSET;\n    clcf->chunked_transfer_encoding = NGX_CONF_UNSET;\n#if (T_NGX_SERVER_INFO)\n    clcf->server_info = NGX_CONF_UNSET;\n#endif\n#if (T_NGX_RET_CACHE)\n    clcf->request_time_cache = NGX_CONF_UNSET;\n#endif\n    clcf->etag = NGX_CONF_UNSET;\n    clcf->server_tokens = NGX_CONF_UNSET_UINT;\n    clcf->types_hash_max_size = NGX_CONF_UNSET_UINT;\n    clcf->types_hash_bucket_size = NGX_CONF_UNSET_UINT;\n\n    clcf->open_file_cache = NGX_CONF_UNSET_PTR;\n    clcf->open_file_cache_valid = NGX_CONF_UNSET;\n    clcf->open_file_cache_min_uses = NGX_CONF_UNSET_UINT;\n    clcf->open_file_cache_errors = NGX_CONF_UNSET;\n    clcf->open_file_cache_events = NGX_CONF_UNSET;\n\n#if (T_NGX_SERVER_INFO)\n    clcf->server_tag_type = NGX_CONF_UNSET_UINT;\n#endif\n\n#if (NGX_HTTP_GZIP)\n    clcf->gzip_vary = NGX_CONF_UNSET;\n    clcf->gzip_http_version = NGX_CONF_UNSET_UINT;\n#if (NGX_PCRE)\n    clcf->gzip_disable = NGX_CONF_UNSET_PTR;\n#endif\n    clcf->gzip_disable_msie6 = 3;\n#if (NGX_HTTP_DEGRADATION)\n    clcf->gzip_disable_degradation = 3;\n#endif\n#endif\n\n#if (NGX_HAVE_OPENAT)\n    clcf->disable_symlinks = NGX_CONF_UNSET_UINT;\n    clcf->disable_symlinks_from = NGX_CONF_UNSET_PTR;\n#endif\n\n    return clcf;\n}\n\n\nstatic ngx_str_t  ngx_http_core_text_html_type = ngx_string(\"text/html\");\nstatic ngx_str_t  ngx_http_core_image_gif_type = ngx_string(\"image/gif\");\nstatic ngx_str_t  ngx_http_core_image_jpeg_type = ngx_string(\"image/jpeg\");\n\nstatic ngx_hash_key_t  ngx_http_core_default_types[] = {\n    { ngx_string(\"html\"), 0, &ngx_http_core_text_html_type },\n    { ngx_string(\"gif\"), 0, &ngx_http_core_image_gif_type },\n    { ngx_string(\"jpg\"), 0, &ngx_http_core_image_jpeg_type },\n    { ngx_null_string, 0, NULL }\n};\n\n\nstatic char *\nngx_http_core_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_http_core_loc_conf_t *prev = parent;\n    ngx_http_core_loc_conf_t *conf = child;\n\n    ngx_uint_t        i;\n    ngx_hash_key_t   *type;\n    ngx_hash_init_t   types_hash;\n\n    if (conf->root.data == NULL) {\n\n        conf->alias = prev->alias;\n        conf->root = prev->root;\n        conf->root_lengths = prev->root_lengths;\n        conf->root_values = prev->root_values;\n\n        if (prev->root.data == NULL) {\n            ngx_str_set(&conf->root, \"html\");\n\n            if (ngx_conf_full_name(cf->cycle, &conf->root, 0) != NGX_OK) {\n                return NGX_CONF_ERROR;\n            }\n        }\n    }\n\n    if (conf->post_action.data == NULL) {\n        conf->post_action = prev->post_action;\n    }\n\n    ngx_conf_merge_uint_value(conf->types_hash_max_size,\n                              prev->types_hash_max_size, 1024);\n\n    ngx_conf_merge_uint_value(conf->types_hash_bucket_size,\n                              prev->types_hash_bucket_size, 64);\n\n    conf->types_hash_bucket_size = ngx_align(conf->types_hash_bucket_size,\n                                             ngx_cacheline_size);\n\n    /*\n     * the special handling of the \"types\" directive in the \"http\" section\n     * to inherit the http's conf->types_hash to all servers\n     */\n\n    if (prev->types && prev->types_hash.buckets == NULL) {\n\n        types_hash.hash = &prev->types_hash;\n        types_hash.key = ngx_hash_key_lc;\n        types_hash.max_size = conf->types_hash_max_size;\n        types_hash.bucket_size = conf->types_hash_bucket_size;\n        types_hash.name = \"types_hash\";\n        types_hash.pool = cf->pool;\n        types_hash.temp_pool = NULL;\n\n        if (ngx_hash_init(&types_hash, prev->types->elts, prev->types->nelts)\n            != NGX_OK)\n        {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    if (conf->types == NULL) {\n        conf->types = prev->types;\n        conf->types_hash = prev->types_hash;\n    }\n\n    if (conf->types == NULL) {\n        conf->types = ngx_array_create(cf->pool, 3, sizeof(ngx_hash_key_t));\n        if (conf->types == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        for (i = 0; ngx_http_core_default_types[i].key.len; i++) {\n            type = ngx_array_push(conf->types);\n            if (type == NULL) {\n                return NGX_CONF_ERROR;\n            }\n\n            type->key = ngx_http_core_default_types[i].key;\n            type->key_hash =\n                       ngx_hash_key_lc(ngx_http_core_default_types[i].key.data,\n                                       ngx_http_core_default_types[i].key.len);\n            type->value = ngx_http_core_default_types[i].value;\n        }\n    }\n\n    if (conf->types_hash.buckets == NULL) {\n\n        types_hash.hash = &conf->types_hash;\n        types_hash.key = ngx_hash_key_lc;\n        types_hash.max_size = conf->types_hash_max_size;\n        types_hash.bucket_size = conf->types_hash_bucket_size;\n        types_hash.name = \"types_hash\";\n        types_hash.pool = cf->pool;\n        types_hash.temp_pool = NULL;\n\n        if (ngx_hash_init(&types_hash, conf->types->elts, conf->types->nelts)\n            != NGX_OK)\n        {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    if (conf->error_log == NULL) {\n        if (prev->error_log) {\n            conf->error_log = prev->error_log;\n        } else {\n            conf->error_log = &cf->cycle->new_log;\n        }\n    }\n\n    if (conf->error_pages == NULL && prev->error_pages) {\n        conf->error_pages = prev->error_pages;\n    }\n\n    ngx_conf_merge_str_value(conf->default_type,\n                              prev->default_type, \"text/plain\");\n\n#if (T_NGX_SERVER_INFO)\n    ngx_conf_merge_uint_value(conf->server_tag_type, prev->server_tag_type,\n                              NGX_HTTP_SERVER_TAG_ON);\n    ngx_conf_merge_str_value(conf->server_tag, prev->server_tag, \"\");\n    ngx_conf_merge_str_value(conf->server_tag_header,\n                             prev->server_tag_header, \"\");\n#endif\n\n    ngx_conf_merge_off_value(conf->client_max_body_size,\n                              prev->client_max_body_size, 1 * 1024 * 1024);\n    ngx_conf_merge_size_value(conf->client_body_buffer_size,\n                              prev->client_body_buffer_size,\n                              (size_t) 2 * ngx_pagesize);\n#if (T_DEPRECATED)\n    ngx_conf_merge_bufs_value(conf->client_body_buffers,\n                              prev->client_body_buffers,\n                              16, ngx_pagesize);\n\n    ngx_conf_merge_size_value(conf->client_body_postpone_size,\n                              prev->client_body_postpone_size,\n                              64 * 1024);\n\n    if (conf->client_max_body_size &&\n         (conf->client_max_body_size <\n        (off_t)(conf->client_body_buffers.num *\n                conf->client_body_buffers.size))) {\n\n        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                           \"client_max_body_size %O should be greater than \"\n                           \"total postpone buffer size %O\",\n                           conf->client_max_body_size,\n                           (off_t)(conf->client_body_buffers.num *\n                                   conf->client_body_buffers.size));\n\n        conf->client_body_buffers.num = 1 + (conf->client_max_body_size /\n                                             conf->client_body_buffers.size);\n    }\n\n    if ((off_t)conf->client_body_postpone_size > conf->client_max_body_size\n         && conf->client_max_body_size != 0)\n    {\n        conf->client_body_postpone_size = conf->client_max_body_size;\n    }\n\n    if (conf->client_body_postpone_size >\n        (conf->client_body_buffers.num * conf->client_body_buffers.size)) {\n\n        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                           \"client_body_postpone_size %uz should be less \"\n                           \"than total postpone buffer size %O\",\n                           conf->client_body_postpone_size,\n                           (off_t)(conf->client_body_buffers.num *\n                                   conf->client_body_buffers.size));\n\n        conf->client_body_buffers.num = 1 + (conf->client_body_postpone_size /\n                                             conf->client_body_buffers.size);\n    }\n\n    if (conf->client_body_buffers.num < 2) {\n        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                           \"there must be at least 2 \\\"client_body_buffers\\\"\");\n        conf->client_body_buffers.num = 2;\n    }\n#endif\n\n    ngx_conf_merge_msec_value(conf->client_body_timeout,\n                              prev->client_body_timeout, 60000);\n\n    ngx_conf_merge_bitmask_value(conf->keepalive_disable,\n                              prev->keepalive_disable,\n                              (NGX_CONF_BITMASK_SET\n                               |NGX_HTTP_KEEPALIVE_DISABLE_MSIE6));\n    ngx_conf_merge_uint_value(conf->satisfy, prev->satisfy,\n                              NGX_HTTP_SATISFY_ALL);\n    ngx_conf_merge_msec_value(conf->auth_delay, prev->auth_delay, 0);\n    ngx_conf_merge_uint_value(conf->if_modified_since, prev->if_modified_since,\n                              NGX_HTTP_IMS_EXACT);\n    ngx_conf_merge_uint_value(conf->max_ranges, prev->max_ranges,\n                              NGX_MAX_INT32_VALUE);\n    ngx_conf_merge_uint_value(conf->client_body_in_file_only,\n                              prev->client_body_in_file_only,\n                              NGX_HTTP_REQUEST_BODY_FILE_OFF);\n    ngx_conf_merge_value(conf->client_body_in_single_buffer,\n                              prev->client_body_in_single_buffer, 0);\n#if (T_NGX_HTTP_UPSTREAM_RETRY_CC)\n    ngx_conf_merge_value(conf->retry_cached_connection,\n                         prev->retry_cached_connection, 1);\n#endif\n    ngx_conf_merge_value(conf->internal, prev->internal, 0);\n    ngx_conf_merge_value(conf->sendfile, prev->sendfile, 0);\n    ngx_conf_merge_size_value(conf->sendfile_max_chunk,\n                              prev->sendfile_max_chunk, 2 * 1024 * 1024);\n    ngx_conf_merge_size_value(conf->subrequest_output_buffer_size,\n                              prev->subrequest_output_buffer_size,\n                              (size_t) ngx_pagesize);\n    ngx_conf_merge_value(conf->aio, prev->aio, NGX_HTTP_AIO_OFF);\n    ngx_conf_merge_value(conf->aio_write, prev->aio_write, 0);\n#if (NGX_THREADS)\n    ngx_conf_merge_ptr_value(conf->thread_pool, prev->thread_pool, NULL);\n    ngx_conf_merge_ptr_value(conf->thread_pool_value, prev->thread_pool_value,\n                             NULL);\n#endif\n    ngx_conf_merge_size_value(conf->read_ahead, prev->read_ahead, 0);\n    ngx_conf_merge_off_value(conf->directio, prev->directio,\n                              NGX_OPEN_FILE_DIRECTIO_OFF);\n    ngx_conf_merge_off_value(conf->directio_alignment, prev->directio_alignment,\n                              512);\n    ngx_conf_merge_value(conf->tcp_nopush, prev->tcp_nopush, 0);\n    ngx_conf_merge_value(conf->tcp_nodelay, prev->tcp_nodelay, 1);\n\n    ngx_conf_merge_msec_value(conf->send_timeout, prev->send_timeout, 60000);\n    ngx_conf_merge_size_value(conf->send_lowat, prev->send_lowat, 0);\n    ngx_conf_merge_size_value(conf->postpone_output, prev->postpone_output,\n                              1460);\n\n    ngx_conf_merge_ptr_value(conf->limit_rate, prev->limit_rate, NULL);\n    ngx_conf_merge_ptr_value(conf->limit_rate_after,\n                              prev->limit_rate_after, NULL);\n\n    ngx_conf_merge_msec_value(conf->keepalive_time,\n                              prev->keepalive_time, 3600000);\n    ngx_conf_merge_msec_value(conf->keepalive_timeout,\n                              prev->keepalive_timeout, 75000);\n    ngx_conf_merge_sec_value(conf->keepalive_header,\n                              prev->keepalive_header, 0);\n    ngx_conf_merge_uint_value(conf->keepalive_requests,\n                              prev->keepalive_requests, 1000);\n    ngx_conf_merge_uint_value(conf->lingering_close,\n#if (T_NGX_MODIFY_DEFAULT_VALUE)\n                              prev->lingering_close, NGX_HTTP_LINGERING_OFF);\n#else\n                              prev->lingering_close, NGX_HTTP_LINGERING_ON);\n#endif\n    ngx_conf_merge_msec_value(conf->lingering_time,\n                              prev->lingering_time, 30000);\n    ngx_conf_merge_msec_value(conf->lingering_timeout,\n                              prev->lingering_timeout, 5000);\n    ngx_conf_merge_msec_value(conf->resolver_timeout,\n                              prev->resolver_timeout, 30000);\n\n    if (conf->resolver == NULL) {\n\n        if (prev->resolver == NULL) {\n\n            /*\n             * create dummy resolver in http {} context\n             * to inherit it in all servers\n             */\n\n            prev->resolver = ngx_resolver_create(cf, NULL, 0);\n            if (prev->resolver == NULL) {\n                return NGX_CONF_ERROR;\n            }\n        }\n\n        conf->resolver = prev->resolver;\n    }\n\n    if (ngx_conf_merge_path_value(cf, &conf->client_body_temp_path,\n                              prev->client_body_temp_path,\n                              &ngx_http_client_temp_path)\n        != NGX_OK)\n    {\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_conf_merge_value(conf->reset_timedout_connection,\n                              prev->reset_timedout_connection, 0);\n    ngx_conf_merge_value(conf->absolute_redirect,\n                              prev->absolute_redirect, 1);\n    ngx_conf_merge_value(conf->server_name_in_redirect,\n                              prev->server_name_in_redirect, 0);\n    ngx_conf_merge_value(conf->port_in_redirect, prev->port_in_redirect, 1);\n    ngx_conf_merge_value(conf->msie_padding, prev->msie_padding,\n#if (T_NGX_MODIFY_DEFAULT_VALUE)\n                         0);\n#else \n                         1);\n#endif\n    ngx_conf_merge_value(conf->msie_refresh, prev->msie_refresh, 0);\n    ngx_conf_merge_value(conf->log_not_found, prev->log_not_found, 1);\n    ngx_conf_merge_value(conf->log_subrequest, prev->log_subrequest, 0);\n    ngx_conf_merge_value(conf->recursive_error_pages,\n                              prev->recursive_error_pages, 0);\n#if (T_NGX_SERVER_INFO)\n    ngx_conf_merge_value(conf->server_info, prev->server_info, 1);\n#endif\n    ngx_conf_merge_value(conf->chunked_transfer_encoding,\n                              prev->chunked_transfer_encoding, 1);\n#if (T_NGX_RET_CACHE)\n    ngx_conf_merge_value(conf->request_time_cache,\n                              prev->request_time_cache, 1);\n#endif\n    ngx_conf_merge_value(conf->etag, prev->etag, 1);\n\n    ngx_conf_merge_uint_value(conf->server_tokens, prev->server_tokens,\n                              NGX_HTTP_SERVER_TOKENS_ON);\n\n    ngx_conf_merge_ptr_value(conf->open_file_cache,\n                              prev->open_file_cache, NULL);\n\n    ngx_conf_merge_sec_value(conf->open_file_cache_valid,\n                              prev->open_file_cache_valid, 60);\n\n    ngx_conf_merge_uint_value(conf->open_file_cache_min_uses,\n                              prev->open_file_cache_min_uses, 1);\n\n    ngx_conf_merge_sec_value(conf->open_file_cache_errors,\n                              prev->open_file_cache_errors, 0);\n\n    ngx_conf_merge_sec_value(conf->open_file_cache_events,\n                              prev->open_file_cache_events, 0);\n#if (NGX_HTTP_GZIP)\n\n    ngx_conf_merge_value(conf->gzip_vary, prev->gzip_vary, 0);\n    ngx_conf_merge_uint_value(conf->gzip_http_version, prev->gzip_http_version,\n                              NGX_HTTP_VERSION_11);\n    ngx_conf_merge_bitmask_value(conf->gzip_proxied, prev->gzip_proxied,\n                              (NGX_CONF_BITMASK_SET|NGX_HTTP_GZIP_PROXIED_OFF));\n\n#if (NGX_PCRE)\n    ngx_conf_merge_ptr_value(conf->gzip_disable, prev->gzip_disable, NULL);\n#endif\n\n    if (conf->gzip_disable_msie6 == 3) {\n        conf->gzip_disable_msie6 =\n            (prev->gzip_disable_msie6 == 3) ? 0 : prev->gzip_disable_msie6;\n    }\n\n#if (NGX_HTTP_DEGRADATION)\n\n    if (conf->gzip_disable_degradation == 3) {\n        conf->gzip_disable_degradation =\n            (prev->gzip_disable_degradation == 3) ?\n                 0 : prev->gzip_disable_degradation;\n    }\n\n#endif\n#endif\n\n#if (NGX_HAVE_OPENAT)\n    ngx_conf_merge_uint_value(conf->disable_symlinks, prev->disable_symlinks,\n                              NGX_DISABLE_SYMLINKS_OFF);\n    ngx_conf_merge_ptr_value(conf->disable_symlinks_from,\n                             prev->disable_symlinks_from, NULL);\n#endif\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_core_srv_conf_t *cscf = conf;\n\n    ngx_str_t              *value, size;\n    ngx_url_t               u;\n    ngx_uint_t              n, i;\n    ngx_http_listen_opt_t   lsopt;\n\n    cscf->listen = 1;\n\n    value = cf->args->elts;\n\n    ngx_memzero(&u, sizeof(ngx_url_t));\n\n    u.url = value[1];\n    u.listen = 1;\n    u.default_port = 80;\n\n    if (ngx_parse_url(cf->pool, &u) != NGX_OK) {\n        if (u.err) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"%s in \\\"%V\\\" of the \\\"listen\\\" directive\",\n                               u.err, &u.url);\n        }\n\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_memzero(&lsopt, sizeof(ngx_http_listen_opt_t));\n\n    lsopt.backlog = NGX_LISTEN_BACKLOG;\n    lsopt.rcvbuf = -1;\n    lsopt.sndbuf = -1;\n#if (NGX_HAVE_SETFIB)\n    lsopt.setfib = -1;\n#endif\n#if (NGX_HAVE_TCP_FASTOPEN)\n    lsopt.fastopen = -1;\n#endif\n#if (NGX_HAVE_INET6)\n    lsopt.ipv6only = 1;\n#endif\n\n    for (n = 2; n < cf->args->nelts; n++) {\n\n        if (ngx_strcmp(value[n].data, \"default_server\") == 0\n            || ngx_strcmp(value[n].data, \"default\") == 0)\n        {\n            lsopt.default_server = 1;\n            continue;\n        }\n\n        if (ngx_strcmp(value[n].data, \"bind\") == 0) {\n            lsopt.set = 1;\n            lsopt.bind = 1;\n            continue;\n        }\n\n#if (NGX_HAVE_SETFIB)\n        if (ngx_strncmp(value[n].data, \"setfib=\", 7) == 0) {\n            lsopt.setfib = ngx_atoi(value[n].data + 7, value[n].len - 7);\n            lsopt.set = 1;\n            lsopt.bind = 1;\n\n            if (lsopt.setfib == NGX_ERROR) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"invalid setfib \\\"%V\\\"\", &value[n]);\n                return NGX_CONF_ERROR;\n            }\n\n            continue;\n        }\n#endif\n\n#if (NGX_HAVE_TCP_FASTOPEN)\n        if (ngx_strncmp(value[n].data, \"fastopen=\", 9) == 0) {\n            lsopt.fastopen = ngx_atoi(value[n].data + 9, value[n].len - 9);\n            lsopt.set = 1;\n            lsopt.bind = 1;\n\n            if (lsopt.fastopen == NGX_ERROR) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"invalid fastopen \\\"%V\\\"\", &value[n]);\n                return NGX_CONF_ERROR;\n            }\n\n            continue;\n        }\n#endif\n\n        if (ngx_strncmp(value[n].data, \"backlog=\", 8) == 0) {\n            lsopt.backlog = ngx_atoi(value[n].data + 8, value[n].len - 8);\n            lsopt.set = 1;\n            lsopt.bind = 1;\n\n            if (lsopt.backlog == NGX_ERROR || lsopt.backlog == 0) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"invalid backlog \\\"%V\\\"\", &value[n]);\n                return NGX_CONF_ERROR;\n            }\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[n].data, \"rcvbuf=\", 7) == 0) {\n            size.len = value[n].len - 7;\n            size.data = value[n].data + 7;\n\n            lsopt.rcvbuf = ngx_parse_size(&size);\n            lsopt.set = 1;\n            lsopt.bind = 1;\n\n            if (lsopt.rcvbuf == NGX_ERROR) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"invalid rcvbuf \\\"%V\\\"\", &value[n]);\n                return NGX_CONF_ERROR;\n            }\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[n].data, \"sndbuf=\", 7) == 0) {\n            size.len = value[n].len - 7;\n            size.data = value[n].data + 7;\n\n            lsopt.sndbuf = ngx_parse_size(&size);\n            lsopt.set = 1;\n            lsopt.bind = 1;\n\n            if (lsopt.sndbuf == NGX_ERROR) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"invalid sndbuf \\\"%V\\\"\", &value[n]);\n                return NGX_CONF_ERROR;\n            }\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[n].data, \"accept_filter=\", 14) == 0) {\n#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)\n            lsopt.accept_filter = (char *) &value[n].data[14];\n            lsopt.set = 1;\n            lsopt.bind = 1;\n#else\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"accept filters \\\"%V\\\" are not supported \"\n                               \"on this platform, ignored\",\n                               &value[n]);\n#endif\n            continue;\n        }\n\n        if (ngx_strcmp(value[n].data, \"deferred\") == 0) {\n#if (NGX_HAVE_DEFERRED_ACCEPT && defined TCP_DEFER_ACCEPT)\n            lsopt.deferred_accept = 1;\n            lsopt.set = 1;\n            lsopt.bind = 1;\n#else\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"the deferred accept is not supported \"\n                               \"on this platform, ignored\");\n#endif\n            continue;\n        }\n\n        if (ngx_strncmp(value[n].data, \"ipv6only=o\", 10) == 0) {\n#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)\n            if (ngx_strcmp(&value[n].data[10], \"n\") == 0) {\n                lsopt.ipv6only = 1;\n\n            } else if (ngx_strcmp(&value[n].data[10], \"ff\") == 0) {\n                lsopt.ipv6only = 0;\n\n            } else {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"invalid ipv6only flags \\\"%s\\\"\",\n                                   &value[n].data[9]);\n                return NGX_CONF_ERROR;\n            }\n\n            lsopt.set = 1;\n            lsopt.bind = 1;\n\n            continue;\n#else\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"ipv6only is not supported \"\n                               \"on this platform\");\n            return NGX_CONF_ERROR;\n#endif\n        }\n\n        if (ngx_strcmp(value[n].data, \"reuseport\") == 0) {\n#if (NGX_HAVE_REUSEPORT)\n            lsopt.reuseport = 1;\n            lsopt.set = 1;\n            lsopt.bind = 1;\n#else\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"reuseport is not supported \"\n                               \"on this platform, ignored\");\n#endif\n            continue;\n        }\n\n        if (ngx_strcmp(value[n].data, \"xquic\") == 0) {\n#if (T_NGX_XQUIC)\n            lsopt.xquic = 1;\n            continue;\n#else\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"the \\\"xquic\\\" parameter requires \"\n                               \"ngx_http_xquic_module\");\n            return NGX_CONF_ERROR;\n#endif\n        }\n\n        if (ngx_strcmp(value[n].data, \"xudp\") == 0) {\n#if (T_NGX_HAVE_XUDP)\n            lsopt.xudp = 1;\n            continue;\n#else\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"the \\\"xudp\\\" parameter requires \"\n                               \"mod_xudp\");\n            return NGX_CONF_ERROR;\n#endif\n        }\n\n        if (ngx_strcmp(value[n].data, \"ssl\") == 0) {\n#if (NGX_HTTP_SSL)\n            lsopt.ssl = 1;\n            continue;\n#else\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"the \\\"ssl\\\" parameter requires \"\n                               \"ngx_http_ssl_module\");\n            return NGX_CONF_ERROR;\n#endif\n        }\n\n        if (ngx_strcmp(value[n].data, \"http2\") == 0) {\n#if (NGX_HTTP_V2)\n            lsopt.http2 = 1;\n            continue;\n#else\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"the \\\"http2\\\" parameter requires \"\n                               \"ngx_http_v2_module\");\n            return NGX_CONF_ERROR;\n#endif\n        }\n\n        if (ngx_strncmp(value[n].data, \"so_keepalive=\", 13) == 0) {\n\n            if (ngx_strcmp(&value[n].data[13], \"on\") == 0) {\n                lsopt.so_keepalive = 1;\n\n            } else if (ngx_strcmp(&value[n].data[13], \"off\") == 0) {\n                lsopt.so_keepalive = 2;\n\n            } else {\n\n#if (NGX_HAVE_KEEPALIVE_TUNABLE)\n                u_char     *p, *end;\n                ngx_str_t   s;\n\n                end = value[n].data + value[n].len;\n                s.data = value[n].data + 13;\n\n                p = ngx_strlchr(s.data, end, ':');\n                if (p == NULL) {\n                    p = end;\n                }\n\n                if (p > s.data) {\n                    s.len = p - s.data;\n\n                    lsopt.tcp_keepidle = ngx_parse_time(&s, 1);\n                    if (lsopt.tcp_keepidle == (time_t) NGX_ERROR) {\n                        goto invalid_so_keepalive;\n                    }\n                }\n\n                s.data = (p < end) ? (p + 1) : end;\n\n                p = ngx_strlchr(s.data, end, ':');\n                if (p == NULL) {\n                    p = end;\n                }\n\n                if (p > s.data) {\n                    s.len = p - s.data;\n\n                    lsopt.tcp_keepintvl = ngx_parse_time(&s, 1);\n                    if (lsopt.tcp_keepintvl == (time_t) NGX_ERROR) {\n                        goto invalid_so_keepalive;\n                    }\n                }\n\n                s.data = (p < end) ? (p + 1) : end;\n\n                if (s.data < end) {\n                    s.len = end - s.data;\n\n                    lsopt.tcp_keepcnt = ngx_atoi(s.data, s.len);\n                    if (lsopt.tcp_keepcnt == NGX_ERROR) {\n                        goto invalid_so_keepalive;\n                    }\n                }\n\n                if (lsopt.tcp_keepidle == 0 && lsopt.tcp_keepintvl == 0\n                    && lsopt.tcp_keepcnt == 0)\n                {\n                    goto invalid_so_keepalive;\n                }\n\n                lsopt.so_keepalive = 1;\n\n#else\n\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"the \\\"so_keepalive\\\" parameter accepts \"\n                                   \"only \\\"on\\\" or \\\"off\\\" on this platform\");\n                return NGX_CONF_ERROR;\n\n#endif\n            }\n\n            lsopt.set = 1;\n            lsopt.bind = 1;\n\n            continue;\n\n#if (NGX_HAVE_KEEPALIVE_TUNABLE)\n        invalid_so_keepalive:\n\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"invalid so_keepalive value: \\\"%s\\\"\",\n                               &value[n].data[13]);\n            return NGX_CONF_ERROR;\n#endif\n        }\n\n        if (ngx_strcmp(value[n].data, \"proxy_protocol\") == 0) {\n            lsopt.proxy_protocol = 1;\n            continue;\n        }\n\n#if (T_NGX_HTTPS_ALLOW_HTTP)\n        if (ngx_strcmp(value[n].data, \"https_allow_http\") == 0) {\n#if (NGX_HTTP_SSL)\n            lsopt.https_allow_http = 1;\n            continue;\n#else\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"the \\\"https_allow_http\\\" parameter requires \"\n                               \"ngx_http_ssl_module\");\n            return NGX_CONF_ERROR;\n#endif\n        }\n#endif\n\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid parameter \\\"%V\\\"\", &value[n]);\n        return NGX_CONF_ERROR;\n    }\n\n    for (n = 0; n < u.naddrs; n++) {\n\n        for (i = 0; i < n; i++) {\n            if (ngx_cmp_sockaddr(u.addrs[n].sockaddr, u.addrs[n].socklen,\n                                 u.addrs[i].sockaddr, u.addrs[i].socklen, 1)\n                == NGX_OK)\n            {\n                goto next;\n            }\n        }\n\n        lsopt.sockaddr = u.addrs[n].sockaddr;\n        lsopt.socklen = u.addrs[n].socklen;\n        lsopt.addr_text = u.addrs[n].name;\n        lsopt.wildcard = ngx_inet_wildcard(lsopt.sockaddr);\n\n        if (ngx_http_add_listen(cf, cscf, &lsopt) != NGX_OK) {\n            return NGX_CONF_ERROR;\n        }\n\n    next:\n        continue;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_core_server_name(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_core_srv_conf_t *cscf = conf;\n\n    u_char                   ch;\n    ngx_str_t               *value;\n    ngx_uint_t               i;\n    ngx_http_server_name_t  *sn;\n\n    value = cf->args->elts;\n\n    for (i = 1; i < cf->args->nelts; i++) {\n\n        ch = value[i].data[0];\n\n        if ((ch == '*' && (value[i].len < 3 || value[i].data[1] != '.'))\n            || (ch == '.' && value[i].len < 2))\n        {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"server name \\\"%V\\\" is invalid\", &value[i]);\n            return NGX_CONF_ERROR;\n        }\n\n        if (ngx_strchr(value[i].data, '/')) {\n            ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                               \"server name \\\"%V\\\" has suspicious symbols\",\n                               &value[i]);\n        }\n\n        sn = ngx_array_push(&cscf->server_names);\n        if (sn == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n#if (NGX_PCRE)\n        sn->regex = NULL;\n#endif\n        sn->server = cscf;\n\n        if (ngx_strcasecmp(value[i].data, (u_char *) \"$hostname\") == 0) {\n            sn->name = cf->cycle->hostname;\n\n        } else {\n            sn->name = value[i];\n        }\n\n        if (value[i].data[0] != '~') {\n            ngx_strlow(sn->name.data, sn->name.data, sn->name.len);\n            continue;\n        }\n\n#if (NGX_PCRE)\n        {\n        u_char               *p;\n        ngx_regex_compile_t   rc;\n        u_char                errstr[NGX_MAX_CONF_ERRSTR];\n\n        if (value[i].len == 1) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"empty regex in server name \\\"%V\\\"\", &value[i]);\n            return NGX_CONF_ERROR;\n        }\n\n        value[i].len--;\n        value[i].data++;\n\n        ngx_memzero(&rc, sizeof(ngx_regex_compile_t));\n\n        rc.pattern = value[i];\n        rc.err.len = NGX_MAX_CONF_ERRSTR;\n        rc.err.data = errstr;\n\n        for (p = value[i].data; p < value[i].data + value[i].len; p++) {\n            if (*p >= 'A' && *p <= 'Z') {\n                rc.options = NGX_REGEX_CASELESS;\n                break;\n            }\n        }\n\n        sn->regex = ngx_http_regex_compile(cf, &rc);\n        if (sn->regex == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        sn->name = value[i];\n        cscf->captures = (rc.captures > 0);\n        }\n#else\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"using regex \\\"%V\\\" \"\n                           \"requires PCRE library\", &value[i]);\n\n        return NGX_CONF_ERROR;\n#endif\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_core_root(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_core_loc_conf_t *clcf = conf;\n\n    ngx_str_t                  *value;\n    ngx_int_t                   alias;\n    ngx_uint_t                  n;\n    ngx_http_script_compile_t   sc;\n\n    alias = (cmd->name.len == sizeof(\"alias\") - 1) ? 1 : 0;\n\n    if (clcf->root.data) {\n\n        if ((clcf->alias != 0) == alias) {\n            return \"is duplicate\";\n        }\n\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"\\\"%V\\\" directive is duplicate, \"\n                           \"\\\"%s\\\" directive was specified earlier\",\n                           &cmd->name, clcf->alias ? \"alias\" : \"root\");\n\n        return NGX_CONF_ERROR;\n    }\n\n    if (clcf->named && alias) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"the \\\"alias\\\" directive cannot be used \"\n                           \"inside the named location\");\n\n        return NGX_CONF_ERROR;\n    }\n\n    value = cf->args->elts;\n\n    if (ngx_strstr(value[1].data, \"$document_root\")\n        || ngx_strstr(value[1].data, \"${document_root}\"))\n    {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"the $document_root variable cannot be used \"\n                           \"in the \\\"%V\\\" directive\",\n                           &cmd->name);\n\n        return NGX_CONF_ERROR;\n    }\n\n    if (ngx_strstr(value[1].data, \"$realpath_root\")\n        || ngx_strstr(value[1].data, \"${realpath_root}\"))\n    {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"the $realpath_root variable cannot be used \"\n                           \"in the \\\"%V\\\" directive\",\n                           &cmd->name);\n\n        return NGX_CONF_ERROR;\n    }\n\n    clcf->alias = alias ? clcf->name.len : 0;\n    clcf->root = value[1];\n\n    if (!alias && clcf->root.len > 0\n        && clcf->root.data[clcf->root.len - 1] == '/')\n    {\n        clcf->root.len--;\n    }\n\n    if (clcf->root.data[0] != '$') {\n        if (ngx_conf_full_name(cf->cycle, &clcf->root, 0) != NGX_OK) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    n = ngx_http_script_variables_count(&clcf->root);\n\n    ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));\n    sc.variables = n;\n\n#if (NGX_PCRE)\n    if (alias && clcf->regex) {\n        clcf->alias = NGX_MAX_SIZE_T_VALUE;\n        n = 1;\n    }\n#endif\n\n    if (n) {\n        sc.cf = cf;\n        sc.source = &clcf->root;\n        sc.lengths = &clcf->root_lengths;\n        sc.values = &clcf->root_values;\n        sc.complete_lengths = 1;\n        sc.complete_values = 1;\n\n        if (ngx_http_script_compile(&sc) != NGX_OK) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_http_method_name_t  ngx_methods_names[] = {\n    { (u_char *) \"GET\",       (uint32_t) ~NGX_HTTP_GET },\n    { (u_char *) \"HEAD\",      (uint32_t) ~NGX_HTTP_HEAD },\n    { (u_char *) \"POST\",      (uint32_t) ~NGX_HTTP_POST },\n    { (u_char *) \"PUT\",       (uint32_t) ~NGX_HTTP_PUT },\n    { (u_char *) \"DELETE\",    (uint32_t) ~NGX_HTTP_DELETE },\n    { (u_char *) \"MKCOL\",     (uint32_t) ~NGX_HTTP_MKCOL },\n    { (u_char *) \"COPY\",      (uint32_t) ~NGX_HTTP_COPY },\n    { (u_char *) \"MOVE\",      (uint32_t) ~NGX_HTTP_MOVE },\n    { (u_char *) \"OPTIONS\",   (uint32_t) ~NGX_HTTP_OPTIONS },\n    { (u_char *) \"PROPFIND\",  (uint32_t) ~NGX_HTTP_PROPFIND },\n    { (u_char *) \"PROPPATCH\", (uint32_t) ~NGX_HTTP_PROPPATCH },\n    { (u_char *) \"LOCK\",      (uint32_t) ~NGX_HTTP_LOCK },\n    { (u_char *) \"UNLOCK\",    (uint32_t) ~NGX_HTTP_UNLOCK },\n    { (u_char *) \"PATCH\",     (uint32_t) ~NGX_HTTP_PATCH },\n    { NULL, 0 }\n};\n\n\nstatic char *\nngx_http_core_limit_except(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_core_loc_conf_t *pclcf = conf;\n\n    char                      *rv;\n    void                      *mconf;\n    ngx_str_t                 *value;\n    ngx_uint_t                 i;\n    ngx_conf_t                 save;\n    ngx_http_module_t         *module;\n    ngx_http_conf_ctx_t       *ctx, *pctx;\n    ngx_http_method_name_t    *name;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    if (pclcf->limit_except) {\n        return \"is duplicate\";\n    }\n\n    pclcf->limit_except = 0xffffffff;\n\n    value = cf->args->elts;\n\n    for (i = 1; i < cf->args->nelts; i++) {\n        for (name = ngx_methods_names; name->name; name++) {\n\n            if (ngx_strcasecmp(value[i].data, name->name) == 0) {\n                pclcf->limit_except &= name->method;\n                goto next;\n            }\n        }\n\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid method \\\"%V\\\"\", &value[i]);\n        return NGX_CONF_ERROR;\n\n    next:\n        continue;\n    }\n\n    if (!(pclcf->limit_except & NGX_HTTP_GET)) {\n        pclcf->limit_except &= (uint32_t) ~NGX_HTTP_HEAD;\n    }\n\n    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));\n    if (ctx == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    pctx = cf->ctx;\n    ctx->main_conf = pctx->main_conf;\n    ctx->srv_conf = pctx->srv_conf;\n\n    ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);\n    if (ctx->loc_conf == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    for (i = 0; cf->cycle->modules[i]; i++) {\n        if (cf->cycle->modules[i]->type != NGX_HTTP_MODULE) {\n            continue;\n        }\n\n        module = cf->cycle->modules[i]->ctx;\n\n        if (module->create_loc_conf) {\n\n            mconf = module->create_loc_conf(cf);\n            if (mconf == NULL) {\n                return NGX_CONF_ERROR;\n            }\n\n            ctx->loc_conf[cf->cycle->modules[i]->ctx_index] = mconf;\n        }\n    }\n\n\n    clcf = ctx->loc_conf[ngx_http_core_module.ctx_index];\n    pclcf->limit_except_loc_conf = ctx->loc_conf;\n    clcf->loc_conf = ctx->loc_conf;\n    clcf->name = pclcf->name;\n    clcf->noname = 1;\n    clcf->lmt_excpt = 1;\n\n    if (ngx_http_add_location(cf, &pclcf->locations, clcf) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    save = *cf;\n    cf->ctx = ctx;\n    cf->cmd_type = NGX_HTTP_LMT_CONF;\n\n    rv = ngx_conf_parse(cf, NULL);\n\n    *cf = save;\n\n    return rv;\n}\n\n\nstatic char *\nngx_http_core_set_aio(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_core_loc_conf_t *clcf = conf;\n\n    ngx_str_t  *value;\n\n    if (clcf->aio != NGX_CONF_UNSET) {\n        return \"is duplicate\";\n    }\n\n#if (NGX_THREADS)\n    clcf->thread_pool = NULL;\n    clcf->thread_pool_value = NULL;\n#endif\n\n    value = cf->args->elts;\n\n    if (ngx_strcmp(value[1].data, \"off\") == 0) {\n        clcf->aio = NGX_HTTP_AIO_OFF;\n        return NGX_CONF_OK;\n    }\n\n    if (ngx_strcmp(value[1].data, \"on\") == 0) {\n#if (NGX_HAVE_FILE_AIO)\n        clcf->aio = NGX_HTTP_AIO_ON;\n        return NGX_CONF_OK;\n#else\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"\\\"aio on\\\" \"\n                           \"is unsupported on this platform\");\n        return NGX_CONF_ERROR;\n#endif\n    }\n\n    if (ngx_strncmp(value[1].data, \"threads\", 7) == 0\n        && (value[1].len == 7 || value[1].data[7] == '='))\n    {\n#if (NGX_THREADS)\n        ngx_str_t                          name;\n        ngx_thread_pool_t                 *tp;\n        ngx_http_complex_value_t           cv;\n        ngx_http_compile_complex_value_t   ccv;\n\n        clcf->aio = NGX_HTTP_AIO_THREADS;\n\n        if (value[1].len >= 8) {\n            name.len = value[1].len - 8;\n            name.data = value[1].data + 8;\n\n            ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n\n            ccv.cf = cf;\n            ccv.value = &name;\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                clcf->thread_pool_value = ngx_palloc(cf->pool,\n                                    sizeof(ngx_http_complex_value_t));\n                if (clcf->thread_pool_value == NULL) {\n                    return NGX_CONF_ERROR;\n                }\n\n                *clcf->thread_pool_value = cv;\n\n                return NGX_CONF_OK;\n            }\n\n            tp = ngx_thread_pool_add(cf, &name);\n\n        } else {\n            tp = ngx_thread_pool_add(cf, NULL);\n        }\n\n        if (tp == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        clcf->thread_pool = tp;\n\n        return NGX_CONF_OK;\n#else\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"\\\"aio threads\\\" \"\n                           \"is unsupported on this platform\");\n        return NGX_CONF_ERROR;\n#endif\n    }\n\n    return \"invalid value\";\n}\n\n\nstatic char *\nngx_http_core_directio(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_core_loc_conf_t *clcf = conf;\n\n    ngx_str_t  *value;\n\n    if (clcf->directio != NGX_CONF_UNSET) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    if (ngx_strcmp(value[1].data, \"off\") == 0) {\n        clcf->directio = NGX_OPEN_FILE_DIRECTIO_OFF;\n        return NGX_CONF_OK;\n    }\n\n    clcf->directio = ngx_parse_offset(&value[1]);\n    if (clcf->directio == (off_t) NGX_ERROR) {\n        return \"invalid value\";\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_core_error_page(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_core_loc_conf_t *clcf = conf;\n\n    u_char                            *p;\n    ngx_int_t                          overwrite;\n    ngx_str_t                         *value, uri, args;\n    ngx_uint_t                         i, n;\n    ngx_http_err_page_t               *err;\n    ngx_http_complex_value_t           cv;\n    ngx_http_compile_complex_value_t   ccv;\n\n    if (clcf->error_pages == NULL) {\n        clcf->error_pages = ngx_array_create(cf->pool, 4,\n                                             sizeof(ngx_http_err_page_t));\n        if (clcf->error_pages == NULL) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    value = cf->args->elts;\n\n    i = cf->args->nelts - 2;\n\n    if (value[i].data[0] == '=') {\n        if (i == 1) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"invalid value \\\"%V\\\"\", &value[i]);\n            return NGX_CONF_ERROR;\n        }\n\n        if (value[i].len > 1) {\n            overwrite = ngx_atoi(&value[i].data[1], value[i].len - 1);\n\n            if (overwrite == NGX_ERROR) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"invalid value \\\"%V\\\"\", &value[i]);\n                return NGX_CONF_ERROR;\n            }\n\n        } else {\n            overwrite = 0;\n        }\n\n        n = 2;\n\n    } else {\n        overwrite = -1;\n        n = 1;\n    }\n\n    uri = value[cf->args->nelts - 1];\n\n    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n\n    ccv.cf = cf;\n    ccv.value = &uri;\n    ccv.complex_value = &cv;\n\n    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_str_null(&args);\n\n    if (cv.lengths == NULL && uri.len && uri.data[0] == '/') {\n        p = (u_char *) ngx_strchr(uri.data, '?');\n\n        if (p) {\n            cv.value.len = p - uri.data;\n            cv.value.data = uri.data;\n            p++;\n            args.len = (uri.data + uri.len) - p;\n            args.data = p;\n        }\n    }\n\n    for (i = 1; i < cf->args->nelts - n; i++) {\n        err = ngx_array_push(clcf->error_pages);\n        if (err == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        err->status = ngx_atoi(value[i].data, value[i].len);\n\n        if (err->status == NGX_ERROR || err->status == 499) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"invalid value \\\"%V\\\"\", &value[i]);\n            return NGX_CONF_ERROR;\n        }\n\n        if (err->status < 300 || err->status > 599) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"value \\\"%V\\\" must be between 300 and 599\",\n                               &value[i]);\n            return NGX_CONF_ERROR;\n        }\n\n        err->overwrite = overwrite;\n\n        if (overwrite == -1) {\n            switch (err->status) {\n                case NGX_HTTP_TO_HTTPS:\n                case NGX_HTTPS_CERT_ERROR:\n                case NGX_HTTPS_NO_CERT:\n                case NGX_HTTP_REQUEST_HEADER_TOO_LARGE:\n                    err->overwrite = NGX_HTTP_BAD_REQUEST;\n            }\n        }\n\n        err->value = cv;\n        err->args = args;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_core_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_core_loc_conf_t *clcf = conf;\n\n    time_t       inactive;\n    ngx_str_t   *value, s;\n    ngx_int_t    max;\n    ngx_uint_t   i;\n\n    if (clcf->open_file_cache != NGX_CONF_UNSET_PTR) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    max = 0;\n    inactive = 60;\n\n    for (i = 1; i < cf->args->nelts; i++) {\n\n        if (ngx_strncmp(value[i].data, \"max=\", 4) == 0) {\n\n            max = ngx_atoi(value[i].data + 4, value[i].len - 4);\n            if (max <= 0) {\n                goto failed;\n            }\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"inactive=\", 9) == 0) {\n\n            s.len = value[i].len - 9;\n            s.data = value[i].data + 9;\n\n            inactive = ngx_parse_time(&s, 1);\n            if (inactive == (time_t) NGX_ERROR) {\n                goto failed;\n            }\n\n            continue;\n        }\n\n        if (ngx_strcmp(value[i].data, \"off\") == 0) {\n\n            clcf->open_file_cache = NULL;\n\n            continue;\n        }\n\n    failed:\n\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid \\\"open_file_cache\\\" parameter \\\"%V\\\"\",\n                           &value[i]);\n        return NGX_CONF_ERROR;\n    }\n\n    if (clcf->open_file_cache == NULL) {\n        return NGX_CONF_OK;\n    }\n\n    if (max == 0) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                        \"\\\"open_file_cache\\\" must have the \\\"max\\\" parameter\");\n        return NGX_CONF_ERROR;\n    }\n\n    clcf->open_file_cache = ngx_open_file_cache_init(cf->pool, max, inactive);\n    if (clcf->open_file_cache) {\n        return NGX_CONF_OK;\n    }\n\n    return NGX_CONF_ERROR;\n}\n\n\nstatic char *\nngx_http_core_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_core_loc_conf_t *clcf = conf;\n\n    return ngx_log_set_log(cf, &clcf->error_log);\n}\n\n\nstatic char *\nngx_http_core_keepalive(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_core_loc_conf_t *clcf = conf;\n\n    ngx_str_t  *value;\n\n    if (clcf->keepalive_timeout != NGX_CONF_UNSET_MSEC) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    clcf->keepalive_timeout = ngx_parse_time(&value[1], 0);\n\n    if (clcf->keepalive_timeout == (ngx_msec_t) NGX_ERROR) {\n        return \"invalid value\";\n    }\n\n    if (cf->args->nelts == 2) {\n        return NGX_CONF_OK;\n    }\n\n    clcf->keepalive_header = ngx_parse_time(&value[2], 1);\n\n    if (clcf->keepalive_header == (time_t) NGX_ERROR) {\n        return \"invalid value\";\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_core_internal(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_core_loc_conf_t *clcf = conf;\n\n    if (clcf->internal != NGX_CONF_UNSET) {\n        return \"is duplicate\";\n    }\n\n    clcf->internal = 1;\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_core_resolver(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_core_loc_conf_t  *clcf = conf;\n\n    ngx_str_t  *value;\n\n    if (clcf->resolver) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    clcf->resolver = ngx_resolver_create(cf, &value[1], cf->args->nelts - 1);\n    if (clcf->resolver == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\n#if (T_NGX_RESOLVER_FILE)\nstatic char *\nngx_http_core_resolver_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_core_loc_conf_t  *clcf = conf;\n\n    ngx_str_t  *value, *names;\n    ngx_uint_t  n;\n\n    if (clcf->resolver) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    if (ngx_resolver_read_resolv_file(cf, &value[1], &names, &n) != NGX_OK) {\n        return \"parse failed\";\n    }\n\n    clcf->resolver = ngx_resolver_create(cf, names, n);\n    if (clcf->resolver == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n#endif\n\n\n#if (NGX_HTTP_GZIP)\n\nstatic char *\nngx_http_gzip_disable(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_core_loc_conf_t  *clcf = conf;\n\n#if (NGX_PCRE)\n\n    ngx_str_t            *value;\n    ngx_uint_t            i;\n    ngx_regex_elt_t      *re;\n    ngx_regex_compile_t   rc;\n    u_char                errstr[NGX_MAX_CONF_ERRSTR];\n\n    if (clcf->gzip_disable == NGX_CONF_UNSET_PTR) {\n        clcf->gzip_disable = ngx_array_create(cf->pool, 2,\n                                              sizeof(ngx_regex_elt_t));\n        if (clcf->gzip_disable == NULL) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    value = cf->args->elts;\n\n    ngx_memzero(&rc, sizeof(ngx_regex_compile_t));\n\n    rc.pool = cf->pool;\n    rc.err.len = NGX_MAX_CONF_ERRSTR;\n    rc.err.data = errstr;\n\n    for (i = 1; i < cf->args->nelts; i++) {\n\n        if (ngx_strcmp(value[i].data, \"msie6\") == 0) {\n            clcf->gzip_disable_msie6 = 1;\n            continue;\n        }\n\n#if (NGX_HTTP_DEGRADATION)\n\n        if (ngx_strcmp(value[i].data, \"degradation\") == 0) {\n            clcf->gzip_disable_degradation = 1;\n            continue;\n        }\n\n#endif\n\n        re = ngx_array_push(clcf->gzip_disable);\n        if (re == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        rc.pattern = value[i];\n        rc.options = NGX_REGEX_CASELESS;\n\n        if (ngx_regex_compile(&rc) != NGX_OK) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, \"%V\", &rc.err);\n            return NGX_CONF_ERROR;\n        }\n\n        re->regex = rc.regex;\n        re->name = value[i].data;\n    }\n\n    return NGX_CONF_OK;\n\n#else\n    ngx_str_t   *value;\n    ngx_uint_t   i;\n\n    value = cf->args->elts;\n\n    for (i = 1; i < cf->args->nelts; i++) {\n        if (ngx_strcmp(value[i].data, \"msie6\") == 0) {\n            clcf->gzip_disable_msie6 = 1;\n            continue;\n        }\n\n#if (NGX_HTTP_DEGRADATION)\n\n        if (ngx_strcmp(value[i].data, \"degradation\") == 0) {\n            clcf->gzip_disable_degradation = 1;\n            continue;\n        }\n\n#endif\n\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"without PCRE library \\\"gzip_disable\\\" supports \"\n                           \"builtin \\\"msie6\\\" and \\\"degradation\\\" mask only\");\n\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n\n#endif\n}\n\n#endif\n\n\n#if (NGX_HAVE_OPENAT)\n\nstatic char *\nngx_http_disable_symlinks(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_core_loc_conf_t *clcf = conf;\n\n    ngx_str_t                         *value;\n    ngx_uint_t                         i;\n    ngx_http_compile_complex_value_t   ccv;\n\n    if (clcf->disable_symlinks != NGX_CONF_UNSET_UINT) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    for (i = 1; i < cf->args->nelts; i++) {\n\n        if (ngx_strcmp(value[i].data, \"off\") == 0) {\n            clcf->disable_symlinks = NGX_DISABLE_SYMLINKS_OFF;\n            continue;\n        }\n\n        if (ngx_strcmp(value[i].data, \"if_not_owner\") == 0) {\n            clcf->disable_symlinks = NGX_DISABLE_SYMLINKS_NOTOWNER;\n            continue;\n        }\n\n        if (ngx_strcmp(value[i].data, \"on\") == 0) {\n            clcf->disable_symlinks = NGX_DISABLE_SYMLINKS_ON;\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"from=\", 5) == 0) {\n            value[i].len -= 5;\n            value[i].data += 5;\n\n            ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n\n            ccv.cf = cf;\n            ccv.value = &value[i];\n            ccv.complex_value = ngx_palloc(cf->pool,\n                                           sizeof(ngx_http_complex_value_t));\n            if (ccv.complex_value == NULL) {\n                return NGX_CONF_ERROR;\n            }\n\n            if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {\n                return NGX_CONF_ERROR;\n            }\n\n            clcf->disable_symlinks_from = ccv.complex_value;\n\n            continue;\n        }\n\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid parameter \\\"%V\\\"\", &value[i]);\n        return NGX_CONF_ERROR;\n    }\n\n    if (clcf->disable_symlinks == NGX_CONF_UNSET_UINT) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"\\\"%V\\\" must have \\\"off\\\", \\\"on\\\" \"\n                           \"or \\\"if_not_owner\\\" parameter\",\n                           &cmd->name);\n        return NGX_CONF_ERROR;\n    }\n\n    if (cf->args->nelts == 2) {\n        clcf->disable_symlinks_from = NULL;\n        return NGX_CONF_OK;\n    }\n\n    if (clcf->disable_symlinks_from == NGX_CONF_UNSET_PTR) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"duplicate parameters \\\"%V %V\\\"\",\n                           &value[1], &value[2]);\n        return NGX_CONF_ERROR;\n    }\n\n    if (clcf->disable_symlinks == NGX_DISABLE_SYMLINKS_OFF) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"\\\"from=\\\" cannot be used with \\\"off\\\" parameter\");\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n#endif\n\n\nstatic char *\nngx_http_core_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                           \"\\\"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                       \"\\\"send_lowat\\\" is not supported, ignored\");\n\n    *np = 0;\n\n#endif\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_core_pool_size(ngx_conf_t *cf, void *post, void *data)\n{\n    size_t *sp = data;\n\n    if (*sp < NGX_MIN_POOL_SIZE) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"the pool size must be no less than %uz\",\n                           NGX_MIN_POOL_SIZE);\n        return NGX_CONF_ERROR;\n    }\n\n    if (*sp % NGX_POOL_ALIGNMENT) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"the pool size must be a multiple of %uz\",\n                           NGX_POOL_ALIGNMENT);\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\n#if (T_NGX_SERVER_INFO)\nstatic char *\nngx_http_set_server_tag(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    u_char                     *p;\n    ngx_str_t                  *value;\n    ngx_http_core_loc_conf_t   *ccf;\n\n    ccf = conf;\n    value = cf->args->elts;\n\n    if (ngx_strcasecmp(value[1].data, (u_char *) \"off\") == 0) {\n        ccf->server_tag_type = NGX_HTTP_SERVER_TAG_OFF;\n    } else {\n        ccf->server_tag_type = NGX_HTTP_SERVER_TAG_CUSTOMIZED;\n\n        ccf->server_tag = value[1];\n\n        ccf->server_tag_header.len = value[1].len + sizeof(\"Server: \") - 1\n            + sizeof(CRLF) - 1;\n        ccf->server_tag_header.data = ngx_palloc(cf->pool,\n                                                 ccf->server_tag_header.len);\n        if ((p = ccf->server_tag_header.data) == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        p = ngx_cpymem(p, \"Server: \", sizeof(\"Server: \") - 1);\n        p = ngx_cpymem(p, value[1].data, value[1].len);\n        ngx_memcpy(p, CRLF, sizeof(CRLF) - 1);\n    }\n\n    return NGX_CONF_OK;\n}\n#endif\n"
  },
  {
    "path": "src/http/ngx_http_core_module.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_HTTP_CORE_H_INCLUDED_\n#define _NGX_HTTP_CORE_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n#if (NGX_THREADS)\n#include <ngx_thread_pool.h>\n#elif (NGX_COMPAT)\ntypedef struct ngx_thread_pool_s  ngx_thread_pool_t;\n#endif\n\n\n#define NGX_HTTP_GZIP_PROXIED_OFF       0x0002\n#define NGX_HTTP_GZIP_PROXIED_EXPIRED   0x0004\n#define NGX_HTTP_GZIP_PROXIED_NO_CACHE  0x0008\n#define NGX_HTTP_GZIP_PROXIED_NO_STORE  0x0010\n#define NGX_HTTP_GZIP_PROXIED_PRIVATE   0x0020\n#define NGX_HTTP_GZIP_PROXIED_NO_LM     0x0040\n#define NGX_HTTP_GZIP_PROXIED_NO_ETAG   0x0080\n#define NGX_HTTP_GZIP_PROXIED_AUTH      0x0100\n#define NGX_HTTP_GZIP_PROXIED_ANY       0x0200\n\n\n#define NGX_HTTP_AIO_OFF                0\n#define NGX_HTTP_AIO_ON                 1\n#define NGX_HTTP_AIO_THREADS            2\n\n\n#define NGX_HTTP_SATISFY_ALL            0\n#define NGX_HTTP_SATISFY_ANY            1\n\n\n#define NGX_HTTP_LINGERING_OFF          0\n#define NGX_HTTP_LINGERING_ON           1\n#define NGX_HTTP_LINGERING_ALWAYS       2\n\n\n#define NGX_HTTP_IMS_OFF                0\n#define NGX_HTTP_IMS_EXACT              1\n#define NGX_HTTP_IMS_BEFORE             2\n\n\n#if (T_NGX_SERVER_INFO)\n#define NGX_HTTP_SERVER_TAG_ON          0\n#define NGX_HTTP_SERVER_TAG_OFF         1\n#define NGX_HTTP_SERVER_TAG_CUSTOMIZED  2\n#endif\n\n\n#define NGX_HTTP_KEEPALIVE_DISABLE_NONE    0x0002\n#define NGX_HTTP_KEEPALIVE_DISABLE_MSIE6   0x0004\n#define NGX_HTTP_KEEPALIVE_DISABLE_SAFARI  0x0008\n\n\n#define NGX_HTTP_SERVER_TOKENS_OFF      0\n#define NGX_HTTP_SERVER_TOKENS_ON       1\n#define NGX_HTTP_SERVER_TOKENS_BUILD    2\n\n\ntypedef struct ngx_http_location_tree_node_s  ngx_http_location_tree_node_t;\ntypedef struct ngx_http_core_loc_conf_s  ngx_http_core_loc_conf_t;\n\n\ntypedef struct {\n    struct sockaddr           *sockaddr;\n    socklen_t                  socklen;\n    ngx_str_t                  addr_text;\n\n    unsigned                   set:1;\n    unsigned                   default_server:1;\n    unsigned                   bind:1;\n    unsigned                   wildcard:1;\n    unsigned                   ssl:1;\n    unsigned                   http2:1;\n#if (NGX_HAVE_INET6)\n    unsigned                   ipv6only:1;\n#endif\n    unsigned                   deferred_accept:1;\n    unsigned                   reuseport:1;\n    unsigned                   so_keepalive:2;\n    unsigned                   proxy_protocol:1;\n#if (T_NGX_HTTPS_ALLOW_HTTP)\n    unsigned                   https_allow_http:1;\n#endif\n#if (T_NGX_XQUIC)\n    unsigned                   xquic:1;\n#endif\n#if (T_NGX_HAVE_XUDP)\n    unsigned                   xudp:1;\n#endif\n\n    int                        backlog;\n    int                        rcvbuf;\n    int                        sndbuf;\n#if (NGX_HAVE_SETFIB)\n    int                        setfib;\n#endif\n#if (NGX_HAVE_TCP_FASTOPEN)\n    int                        fastopen;\n#endif\n#if (NGX_HAVE_KEEPALIVE_TUNABLE)\n    int                        tcp_keepidle;\n    int                        tcp_keepintvl;\n    int                        tcp_keepcnt;\n#endif\n\n#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)\n    char                      *accept_filter;\n#endif\n} ngx_http_listen_opt_t;\n\n\ntypedef enum {\n    NGX_HTTP_POST_READ_PHASE = 0,\n\n    NGX_HTTP_SERVER_REWRITE_PHASE,\n\n    NGX_HTTP_FIND_CONFIG_PHASE,\n    NGX_HTTP_REWRITE_PHASE,\n    NGX_HTTP_POST_REWRITE_PHASE,\n\n    NGX_HTTP_PREACCESS_PHASE,\n\n    NGX_HTTP_ACCESS_PHASE,\n    NGX_HTTP_POST_ACCESS_PHASE,\n\n    NGX_HTTP_PRECONTENT_PHASE,\n\n    NGX_HTTP_CONTENT_PHASE,\n\n    NGX_HTTP_LOG_PHASE\n} ngx_http_phases;\n\ntypedef struct ngx_http_phase_handler_s  ngx_http_phase_handler_t;\n\ntypedef ngx_int_t (*ngx_http_phase_handler_pt)(ngx_http_request_t *r,\n    ngx_http_phase_handler_t *ph);\n\nstruct ngx_http_phase_handler_s {\n    ngx_http_phase_handler_pt  checker;\n    ngx_http_handler_pt        handler;\n    ngx_uint_t                 next;\n};\n\n\ntypedef struct {\n    ngx_http_phase_handler_t  *handlers;\n    ngx_uint_t                 server_rewrite_index;\n    ngx_uint_t                 location_rewrite_index;\n} ngx_http_phase_engine_t;\n\n\ntypedef struct {\n    ngx_array_t                handlers;\n} ngx_http_phase_t;\n\n\ntypedef struct {\n    ngx_array_t                servers;         /* ngx_http_core_srv_conf_t */\n\n    ngx_http_phase_engine_t    phase_engine;\n\n    ngx_hash_t                 headers_in_hash;\n\n    ngx_hash_t                 variables_hash;\n\n    ngx_array_t                variables;         /* ngx_http_variable_t */\n    ngx_array_t                prefix_variables;  /* ngx_http_variable_t */\n    ngx_uint_t                 ncaptures;\n\n    ngx_uint_t                 server_names_hash_max_size;\n    ngx_uint_t                 server_names_hash_bucket_size;\n\n    ngx_uint_t                 variables_hash_max_size;\n    ngx_uint_t                 variables_hash_bucket_size;\n\n    ngx_hash_keys_arrays_t    *variables_keys;\n\n    ngx_array_t               *ports;\n\n    ngx_http_phase_t           phases[NGX_HTTP_LOG_PHASE + 1];\n} ngx_http_core_main_conf_t;\n\n\ntypedef struct {\n    /* array of the ngx_http_server_name_t, \"server_name\" directive */\n    ngx_array_t                 server_names;\n\n    /* server ctx */\n    ngx_http_conf_ctx_t        *ctx;\n\n    u_char                     *file_name;\n    ngx_uint_t                  line;\n\n    ngx_str_t                   server_name;\n#if (T_NGX_SERVER_INFO)\n    ngx_str_t                   server_admin;\n#endif\n\n    size_t                      connection_pool_size;\n    size_t                      request_pool_size;\n    size_t                      client_header_buffer_size;\n\n    ngx_bufs_t                  large_client_header_buffers;\n\n    ngx_msec_t                  client_header_timeout;\n\n    ngx_flag_t                  ignore_invalid_headers;\n    ngx_flag_t                  merge_slashes;\n    ngx_flag_t                  underscores_in_headers;\n\n    unsigned                    listen:1;\n#if (NGX_PCRE)\n    unsigned                    captures:1;\n#endif\n\n    ngx_http_core_loc_conf_t  **named_locations;\n} ngx_http_core_srv_conf_t;\n\n\n/* list of structures to find core_srv_conf quickly at run time */\n\n\ntypedef struct {\n#if (NGX_PCRE)\n    ngx_http_regex_t          *regex;\n#endif\n    ngx_http_core_srv_conf_t  *server;   /* virtual name server conf */\n    ngx_str_t                  name;\n} ngx_http_server_name_t;\n\n\ntypedef struct {\n    ngx_hash_combined_t        names;\n\n    ngx_uint_t                 nregex;\n    ngx_http_server_name_t    *regex;\n} ngx_http_virtual_names_t;\n\n\nstruct ngx_http_addr_conf_s {\n    /* the default server configuration for this address:port */\n    ngx_http_core_srv_conf_t  *default_server;\n\n    ngx_http_virtual_names_t  *virtual_names;\n#if (T_NGX_XQUIC)\n    unsigned                   xquic:1;\n#endif\n#if (T_NGX_HAVE_XUDP)\n    unsigned                   xudp:1;\n#endif\n    unsigned                   ssl:1;\n    unsigned                   http2:1;\n    unsigned                   proxy_protocol:1;\n#if (T_NGX_HTTPS_ALLOW_HTTP)\n    unsigned                   https_allow_http:1;\n#endif\n};\n\n\ntypedef struct {\n    in_addr_t                  addr;\n    ngx_http_addr_conf_t       conf;\n} ngx_http_in_addr_t;\n\n\n#if (NGX_HAVE_INET6)\n\ntypedef struct {\n    struct in6_addr            addr6;\n    ngx_http_addr_conf_t       conf;\n} ngx_http_in6_addr_t;\n\n#endif\n\n\ntypedef struct {\n    /* ngx_http_in_addr_t or ngx_http_in6_addr_t */\n    void                      *addrs;\n    ngx_uint_t                 naddrs;\n} ngx_http_port_t;\n\n\ntypedef struct {\n    ngx_int_t                  family;\n    in_port_t                  port;\n    ngx_array_t                addrs;     /* array of ngx_http_conf_addr_t */\n#if (T_NGX_XQUIC)\n    unsigned                   udp:1;\n#endif\n#if (T_NGX_HAVE_XUDP)\n    /**\n     *  the value will be 1 for xudp on\n     *  if xudp on, all the ngx_http_conf_addr_t will be xudp\n     * */\n    unsigned                   xudp:1;\n#endif\n} ngx_http_conf_port_t;\n\n\ntypedef struct {\n    ngx_http_listen_opt_t      opt;\n\n    unsigned                   protocols:3;\n    unsigned                   protocols_set:1;\n    unsigned                   protocols_changed:1;\n\n    ngx_hash_t                 hash;\n    ngx_hash_wildcard_t       *wc_head;\n    ngx_hash_wildcard_t       *wc_tail;\n\n#if (NGX_PCRE)\n    ngx_uint_t                 nregex;\n    ngx_http_server_name_t    *regex;\n#endif\n\n    /* the default server configuration for this address:port */\n    ngx_http_core_srv_conf_t  *default_server;\n    ngx_array_t                servers;  /* array of ngx_http_core_srv_conf_t */\n} ngx_http_conf_addr_t;\n\n\ntypedef struct {\n    ngx_int_t                  status;\n    ngx_int_t                  overwrite;\n    ngx_http_complex_value_t   value;\n    ngx_str_t                  args;\n} ngx_http_err_page_t;\n\n\nstruct ngx_http_core_loc_conf_s {\n    ngx_str_t     name;          /* location name */\n    ngx_str_t     escaped_name;\n\n#if (NGX_PCRE)\n    ngx_http_regex_t  *regex;\n#endif\n\n    unsigned      noname:1;   /* \"if () {}\" block or limit_except */\n    unsigned      lmt_excpt:1;\n    unsigned      named:1;\n\n    unsigned      exact_match:1;\n    unsigned      noregex:1;\n\n    unsigned      auto_redirect:1;\n#if (NGX_HTTP_GZIP)\n    unsigned      gzip_disable_msie6:2;\n    unsigned      gzip_disable_degradation:2;\n#endif\n\n    ngx_http_location_tree_node_t   *static_locations;\n#if (NGX_PCRE)\n    ngx_http_core_loc_conf_t       **regex_locations;\n#endif\n\n    /* pointer to the modules' loc_conf */\n    void        **loc_conf;\n\n    uint32_t      limit_except;\n    void        **limit_except_loc_conf;\n\n    ngx_http_handler_pt  handler;\n\n    /* location name length for inclusive location with inherited alias */\n    size_t        alias;\n    ngx_str_t     root;                    /* root, alias */\n    ngx_str_t     post_action;\n\n    ngx_array_t  *root_lengths;\n    ngx_array_t  *root_values;\n\n    ngx_array_t  *types;\n    ngx_hash_t    types_hash;\n    ngx_str_t     default_type;\n\n    off_t         client_max_body_size;    /* client_max_body_size */\n    off_t         directio;                /* directio */\n    off_t         directio_alignment;      /* directio_alignment */\n\n#if (T_DEPRECATED)\n    ngx_bufs_t    client_body_buffers;\n    size_t        client_body_postpone_size;\n#endif\n    size_t        client_body_buffer_size; /* client_body_buffer_size */\n    size_t        send_lowat;              /* send_lowat */\n    size_t        postpone_output;         /* postpone_output */\n    size_t        sendfile_max_chunk;      /* sendfile_max_chunk */\n    size_t        read_ahead;              /* read_ahead */\n    size_t        subrequest_output_buffer_size;\n                                           /* subrequest_output_buffer_size */\n\n    ngx_http_complex_value_t  *limit_rate; /* limit_rate */\n    ngx_http_complex_value_t  *limit_rate_after; /* limit_rate_after */\n\n    ngx_msec_t    client_body_timeout;     /* client_body_timeout */\n    ngx_msec_t    send_timeout;            /* send_timeout */\n    ngx_msec_t    keepalive_time;          /* keepalive_time */\n    ngx_msec_t    keepalive_timeout;       /* keepalive_timeout */\n    ngx_msec_t    lingering_time;          /* lingering_time */\n    ngx_msec_t    lingering_timeout;       /* lingering_timeout */\n    ngx_msec_t    resolver_timeout;        /* resolver_timeout */\n    ngx_msec_t    auth_delay;              /* auth_delay */\n\n    ngx_resolver_t  *resolver;             /* resolver */\n\n    time_t        keepalive_header;        /* keepalive_timeout */\n\n    ngx_uint_t    keepalive_requests;      /* keepalive_requests */\n    ngx_uint_t    keepalive_disable;       /* keepalive_disable */\n    ngx_uint_t    satisfy;                 /* satisfy */\n    ngx_uint_t    lingering_close;         /* lingering_close */\n    ngx_uint_t    if_modified_since;       /* if_modified_since */\n    ngx_uint_t    max_ranges;              /* max_ranges */\n    ngx_uint_t    client_body_in_file_only; /* client_body_in_file_only */\n\n    ngx_flag_t    client_body_in_single_buffer;\n                                           /* client_body_in_singe_buffer */\n#if (T_NGX_HTTP_UPSTREAM_RETRY_CC)\n    ngx_flag_t    retry_cached_connection;\n#endif\n    ngx_flag_t    internal;                /* internal */\n    ngx_flag_t    sendfile;                /* sendfile */\n    ngx_flag_t    aio;                     /* aio */\n    ngx_flag_t    aio_write;               /* aio_write */\n    ngx_flag_t    tcp_nopush;              /* tcp_nopush */\n    ngx_flag_t    tcp_nodelay;             /* tcp_nodelay */\n    ngx_flag_t    reset_timedout_connection; /* reset_timedout_connection */\n    ngx_flag_t    absolute_redirect;       /* absolute_redirect */\n    ngx_flag_t    server_name_in_redirect; /* server_name_in_redirect */\n    ngx_flag_t    port_in_redirect;        /* port_in_redirect */\n    ngx_flag_t    msie_padding;            /* msie_padding */\n    ngx_flag_t    msie_refresh;            /* msie_refresh */\n    ngx_flag_t    log_not_found;           /* log_not_found */\n    ngx_flag_t    log_subrequest;          /* log_subrequest */\n    ngx_flag_t    recursive_error_pages;   /* recursive_error_pages */\n    ngx_uint_t    server_tokens;           /* server_tokens */\n#if (T_NGX_SERVER_INFO)\n    ngx_flag_t    server_info;             /* server_info */\n#endif\n    ngx_flag_t    chunked_transfer_encoding; /* chunked_transfer_encoding */\n    ngx_flag_t    etag;                    /* etag */\n#if (T_NGX_RET_CACHE)\n    ngx_flag_t    request_time_cache;      /* request_time_cache */\n#endif\n\n#if (T_NGX_SERVER_INFO)\n    ngx_uint_t    server_tag_type;         /* server tag type */\n    ngx_str_t     server_tag;              /* customized server tag */\n    ngx_str_t     server_tag_header;       /* server tag header */\n#endif\n\n#if (NGX_HTTP_GZIP)\n    ngx_flag_t    gzip_vary;               /* gzip_vary */\n\n    ngx_uint_t    gzip_http_version;       /* gzip_http_version */\n    ngx_uint_t    gzip_proxied;            /* gzip_proxied */\n\n#if (NGX_PCRE)\n    ngx_array_t  *gzip_disable;            /* gzip_disable */\n#endif\n#endif\n\n#if (NGX_THREADS || NGX_COMPAT)\n    ngx_thread_pool_t         *thread_pool;\n    ngx_http_complex_value_t  *thread_pool_value;\n#endif\n\n#if (NGX_HAVE_OPENAT)\n    ngx_uint_t    disable_symlinks;        /* disable_symlinks */\n    ngx_http_complex_value_t  *disable_symlinks_from;\n#endif\n\n    ngx_array_t  *error_pages;             /* error_page */\n\n    ngx_path_t   *client_body_temp_path;   /* client_body_temp_path */\n\n    ngx_open_file_cache_t  *open_file_cache;\n    time_t        open_file_cache_valid;\n    ngx_uint_t    open_file_cache_min_uses;\n    ngx_flag_t    open_file_cache_errors;\n    ngx_flag_t    open_file_cache_events;\n\n    ngx_log_t    *error_log;\n\n    ngx_uint_t    types_hash_max_size;\n    ngx_uint_t    types_hash_bucket_size;\n\n    ngx_queue_t  *locations;\n\n#if 0\n    ngx_http_core_loc_conf_t  *prev_location;\n#endif\n};\n\n\ntypedef struct {\n    ngx_queue_t                      queue;\n    ngx_http_core_loc_conf_t        *exact;\n    ngx_http_core_loc_conf_t        *inclusive;\n    ngx_str_t                       *name;\n    u_char                          *file_name;\n    ngx_uint_t                       line;\n    ngx_queue_t                      list;\n} ngx_http_location_queue_t;\n\n\nstruct ngx_http_location_tree_node_s {\n    ngx_http_location_tree_node_t   *left;\n    ngx_http_location_tree_node_t   *right;\n    ngx_http_location_tree_node_t   *tree;\n\n    ngx_http_core_loc_conf_t        *exact;\n    ngx_http_core_loc_conf_t        *inclusive;\n\n    u_short                          len;\n    u_char                           auto_redirect;\n    u_char                           name[1];\n};\n\n\nvoid ngx_http_core_run_phases(ngx_http_request_t *r);\nngx_int_t ngx_http_core_generic_phase(ngx_http_request_t *r,\n    ngx_http_phase_handler_t *ph);\nngx_int_t ngx_http_core_rewrite_phase(ngx_http_request_t *r,\n    ngx_http_phase_handler_t *ph);\nngx_int_t ngx_http_core_find_config_phase(ngx_http_request_t *r,\n    ngx_http_phase_handler_t *ph);\nngx_int_t ngx_http_core_post_rewrite_phase(ngx_http_request_t *r,\n    ngx_http_phase_handler_t *ph);\nngx_int_t ngx_http_core_access_phase(ngx_http_request_t *r,\n    ngx_http_phase_handler_t *ph);\nngx_int_t ngx_http_core_post_access_phase(ngx_http_request_t *r,\n    ngx_http_phase_handler_t *ph);\nngx_int_t ngx_http_core_content_phase(ngx_http_request_t *r,\n    ngx_http_phase_handler_t *ph);\n\n\nvoid *ngx_http_test_content_type(ngx_http_request_t *r, ngx_hash_t *types_hash);\nngx_int_t ngx_http_set_content_type(ngx_http_request_t *r);\nvoid ngx_http_set_exten(ngx_http_request_t *r);\nngx_int_t ngx_http_set_etag(ngx_http_request_t *r);\nvoid ngx_http_weak_etag(ngx_http_request_t *r);\nngx_int_t ngx_http_send_response(ngx_http_request_t *r, ngx_uint_t status,\n    ngx_str_t *ct, ngx_http_complex_value_t *cv);\nu_char *ngx_http_map_uri_to_path(ngx_http_request_t *r, ngx_str_t *name,\n    size_t *root_length, size_t reserved);\nngx_int_t ngx_http_auth_basic_user(ngx_http_request_t *r);\n#if (NGX_HTTP_GZIP)\nngx_int_t ngx_http_gzip_ok(ngx_http_request_t *r);\n#endif\n\n\nngx_int_t ngx_http_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);\nngx_int_t ngx_http_internal_redirect(ngx_http_request_t *r,\n    ngx_str_t *uri, ngx_str_t *args);\nngx_int_t ngx_http_named_location(ngx_http_request_t *r, ngx_str_t *name);\n\n\nngx_http_cleanup_t *ngx_http_cleanup_add(ngx_http_request_t *r, size_t size);\n\n\n#if (T_NGX_INPUT_BODY_FILTER)\ntypedef ngx_int_t (*ngx_http_input_body_filter_pt)\n    (ngx_http_request_t *r, ngx_buf_t *buf);\n#endif\n\ntypedef ngx_int_t (*ngx_http_output_header_filter_pt)(ngx_http_request_t *r);\ntypedef ngx_int_t (*ngx_http_output_body_filter_pt)\n    (ngx_http_request_t *r, ngx_chain_t *chain);\ntypedef ngx_int_t (*ngx_http_request_body_filter_pt)\n    (ngx_http_request_t *r, ngx_chain_t *chain);\n\n\nngx_int_t ngx_http_output_filter(ngx_http_request_t *r, ngx_chain_t *chain);\nngx_int_t ngx_http_write_filter(ngx_http_request_t *r, ngx_chain_t *chain);\nngx_int_t ngx_http_request_body_save_filter(ngx_http_request_t *r,\n    ngx_chain_t *chain);\n\n\nngx_int_t ngx_http_set_disable_symlinks(ngx_http_request_t *r,\n    ngx_http_core_loc_conf_t *clcf, ngx_str_t *path, ngx_open_file_info_t *of);\n\nngx_int_t ngx_http_get_forwarded_addr(ngx_http_request_t *r, ngx_addr_t *addr,\n    ngx_table_elt_t *headers, ngx_str_t *value, ngx_array_t *proxies,\n    int recursive);\n\nngx_int_t ngx_http_link_multi_headers(ngx_http_request_t *r);\n\n\nextern ngx_module_t  ngx_http_core_module;\n\nextern ngx_uint_t ngx_http_max_module;\n\nextern ngx_str_t  ngx_http_core_get_method;\n\n\n#define ngx_http_clear_content_length(r)                                      \\\n                                                                              \\\n    r->headers_out.content_length_n = -1;                                     \\\n    if (r->headers_out.content_length) {                                      \\\n        r->headers_out.content_length->hash = 0;                              \\\n        r->headers_out.content_length = NULL;                                 \\\n    }\n\n#define ngx_http_clear_accept_ranges(r)                                       \\\n                                                                              \\\n    r->allow_ranges = 0;                                                      \\\n    if (r->headers_out.accept_ranges) {                                       \\\n        r->headers_out.accept_ranges->hash = 0;                               \\\n        r->headers_out.accept_ranges = NULL;                                  \\\n    }\n\n#define ngx_http_clear_last_modified(r)                                       \\\n                                                                              \\\n    r->headers_out.last_modified_time = -1;                                   \\\n    if (r->headers_out.last_modified) {                                       \\\n        r->headers_out.last_modified->hash = 0;                               \\\n        r->headers_out.last_modified = NULL;                                  \\\n    }\n\n#define ngx_http_clear_location(r)                                            \\\n                                                                              \\\n    if (r->headers_out.location) {                                            \\\n        r->headers_out.location->hash = 0;                                    \\\n        r->headers_out.location = NULL;                                       \\\n    }\n\n#define ngx_http_clear_etag(r)                                                \\\n                                                                              \\\n    if (r->headers_out.etag) {                                                \\\n        r->headers_out.etag->hash = 0;                                        \\\n        r->headers_out.etag = NULL;                                           \\\n    }\n\n\n#endif /* _NGX_HTTP_CORE_H_INCLUDED_ */\n"
  },
  {
    "path": "src/http/ngx_http_file_cache.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n#include <ngx_md5.h>\n\n\nstatic ngx_int_t ngx_http_file_cache_lock(ngx_http_request_t *r,\n    ngx_http_cache_t *c);\nstatic void ngx_http_file_cache_lock_wait_handler(ngx_event_t *ev);\nstatic void ngx_http_file_cache_lock_wait(ngx_http_request_t *r,\n    ngx_http_cache_t *c);\nstatic ngx_int_t ngx_http_file_cache_read(ngx_http_request_t *r,\n    ngx_http_cache_t *c);\nstatic ssize_t ngx_http_file_cache_aio_read(ngx_http_request_t *r,\n    ngx_http_cache_t *c);\n#if (NGX_HAVE_FILE_AIO)\nstatic void ngx_http_cache_aio_event_handler(ngx_event_t *ev);\n#endif\n#if (NGX_THREADS)\nstatic ngx_int_t ngx_http_cache_thread_handler(ngx_thread_task_t *task,\n    ngx_file_t *file);\nstatic void ngx_http_cache_thread_event_handler(ngx_event_t *ev);\n#endif\nstatic ngx_int_t ngx_http_file_cache_exists(ngx_http_file_cache_t *cache,\n    ngx_http_cache_t *c);\nstatic ngx_int_t ngx_http_file_cache_name(ngx_http_request_t *r,\n    ngx_path_t *path);\nstatic ngx_http_file_cache_node_t *\n    ngx_http_file_cache_lookup(ngx_http_file_cache_t *cache, u_char *key);\nstatic void ngx_http_file_cache_rbtree_insert_value(ngx_rbtree_node_t *temp,\n    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);\nstatic void ngx_http_file_cache_vary(ngx_http_request_t *r, u_char *vary,\n    size_t len, u_char *hash);\nstatic void ngx_http_file_cache_vary_header(ngx_http_request_t *r,\n    ngx_md5_t *md5, ngx_str_t *name);\nstatic ngx_int_t ngx_http_file_cache_reopen(ngx_http_request_t *r,\n    ngx_http_cache_t *c);\nstatic ngx_int_t ngx_http_file_cache_update_variant(ngx_http_request_t *r,\n    ngx_http_cache_t *c);\nstatic void ngx_http_file_cache_cleanup(void *data);\nstatic time_t ngx_http_file_cache_forced_expire(ngx_http_file_cache_t *cache);\nstatic time_t ngx_http_file_cache_expire(ngx_http_file_cache_t *cache);\nstatic void ngx_http_file_cache_delete(ngx_http_file_cache_t *cache,\n    ngx_queue_t *q, u_char *name);\nstatic void ngx_http_file_cache_loader_sleep(ngx_http_file_cache_t *cache);\nstatic ngx_int_t ngx_http_file_cache_noop(ngx_tree_ctx_t *ctx,\n    ngx_str_t *path);\nstatic ngx_int_t ngx_http_file_cache_manage_file(ngx_tree_ctx_t *ctx,\n    ngx_str_t *path);\nstatic ngx_int_t ngx_http_file_cache_manage_directory(ngx_tree_ctx_t *ctx,\n    ngx_str_t *path);\nstatic ngx_int_t ngx_http_file_cache_add_file(ngx_tree_ctx_t *ctx,\n    ngx_str_t *path);\nstatic ngx_int_t ngx_http_file_cache_add(ngx_http_file_cache_t *cache,\n    ngx_http_cache_t *c);\nstatic ngx_int_t ngx_http_file_cache_delete_file(ngx_tree_ctx_t *ctx,\n    ngx_str_t *path);\nstatic void ngx_http_file_cache_set_watermark(ngx_http_file_cache_t *cache);\n\n\nngx_str_t  ngx_http_cache_status[] = {\n    ngx_string(\"MISS\"),\n    ngx_string(\"BYPASS\"),\n    ngx_string(\"EXPIRED\"),\n    ngx_string(\"STALE\"),\n    ngx_string(\"UPDATING\"),\n    ngx_string(\"REVALIDATED\"),\n    ngx_string(\"HIT\")\n};\n\n\nstatic u_char  ngx_http_file_cache_key[] = { LF, 'K', 'E', 'Y', ':', ' ' };\n\n\nstatic ngx_int_t\nngx_http_file_cache_init(ngx_shm_zone_t *shm_zone, void *data)\n{\n    ngx_http_file_cache_t  *ocache = data;\n\n    size_t                  len;\n    ngx_uint_t              n;\n    ngx_http_file_cache_t  *cache;\n\n    cache = shm_zone->data;\n\n    if (ocache) {\n        if (ngx_strcmp(cache->path->name.data, ocache->path->name.data) != 0) {\n            ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0,\n                          \"cache \\\"%V\\\" uses the \\\"%V\\\" cache path \"\n                          \"while previously it used the \\\"%V\\\" cache path\",\n                          &shm_zone->shm.name, &cache->path->name,\n                          &ocache->path->name);\n\n            return NGX_ERROR;\n        }\n\n        for (n = 0; n < NGX_MAX_PATH_LEVEL; n++) {\n            if (cache->path->level[n] != ocache->path->level[n]) {\n                ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0,\n                              \"cache \\\"%V\\\" had previously different levels\",\n                              &shm_zone->shm.name);\n                return NGX_ERROR;\n            }\n        }\n\n        cache->sh = ocache->sh;\n\n        cache->shpool = ocache->shpool;\n        cache->bsize = ocache->bsize;\n\n        cache->max_size /= cache->bsize;\n\n        if (!cache->sh->cold || cache->sh->loading) {\n            cache->path->loader = NULL;\n        }\n\n        return NGX_OK;\n    }\n\n    cache->shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;\n\n    if (shm_zone->shm.exists) {\n        cache->sh = cache->shpool->data;\n        cache->bsize = ngx_fs_bsize(cache->path->name.data);\n        cache->max_size /= cache->bsize;\n\n        return NGX_OK;\n    }\n\n    cache->sh = ngx_slab_alloc(cache->shpool, sizeof(ngx_http_file_cache_sh_t));\n    if (cache->sh == NULL) {\n        return NGX_ERROR;\n    }\n\n    cache->shpool->data = cache->sh;\n\n    ngx_rbtree_init(&cache->sh->rbtree, &cache->sh->sentinel,\n                    ngx_http_file_cache_rbtree_insert_value);\n\n    ngx_queue_init(&cache->sh->queue);\n\n    cache->sh->cold = 1;\n    cache->sh->loading = 0;\n    cache->sh->size = 0;\n    cache->sh->count = 0;\n    cache->sh->watermark = (ngx_uint_t) -1;\n\n    cache->bsize = ngx_fs_bsize(cache->path->name.data);\n\n    cache->max_size /= cache->bsize;\n\n    len = sizeof(\" in cache keys zone \\\"\\\"\") + shm_zone->shm.name.len;\n\n    cache->shpool->log_ctx = ngx_slab_alloc(cache->shpool, len);\n    if (cache->shpool->log_ctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_sprintf(cache->shpool->log_ctx, \" in cache keys zone \\\"%V\\\"%Z\",\n                &shm_zone->shm.name);\n\n    cache->shpool->log_nomem = 0;\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_file_cache_new(ngx_http_request_t *r)\n{\n    ngx_http_cache_t  *c;\n\n    c = ngx_pcalloc(r->pool, sizeof(ngx_http_cache_t));\n    if (c == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (ngx_array_init(&c->keys, r->pool, 4, sizeof(ngx_str_t)) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    r->cache = c;\n    c->file.log = r->connection->log;\n    c->file.fd = NGX_INVALID_FILE;\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_file_cache_create(ngx_http_request_t *r)\n{\n    ngx_http_cache_t       *c;\n    ngx_pool_cleanup_t     *cln;\n    ngx_http_file_cache_t  *cache;\n\n    c = r->cache;\n    cache = c->file_cache;\n\n    cln = ngx_pool_cleanup_add(r->pool, 0);\n    if (cln == NULL) {\n        return NGX_ERROR;\n    }\n\n    cln->handler = ngx_http_file_cache_cleanup;\n    cln->data = c;\n\n    if (ngx_http_file_cache_exists(cache, c) == NGX_ERROR) {\n        return NGX_ERROR;\n    }\n\n    if (ngx_http_file_cache_name(r, cache->path) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nvoid\nngx_http_file_cache_create_key(ngx_http_request_t *r)\n{\n    size_t             len;\n    ngx_str_t         *key;\n    ngx_uint_t         i;\n    ngx_md5_t          md5;\n    ngx_http_cache_t  *c;\n\n    c = r->cache;\n\n    len = 0;\n\n    ngx_crc32_init(c->crc32);\n    ngx_md5_init(&md5);\n\n    key = c->keys.elts;\n    for (i = 0; i < c->keys.nelts; i++) {\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"http cache key: \\\"%V\\\"\", &key[i]);\n\n        len += key[i].len;\n\n        ngx_crc32_update(&c->crc32, key[i].data, key[i].len);\n        ngx_md5_update(&md5, key[i].data, key[i].len);\n    }\n\n    c->header_start = sizeof(ngx_http_file_cache_header_t)\n                      + sizeof(ngx_http_file_cache_key) + len + 1;\n\n    ngx_crc32_final(c->crc32);\n    ngx_md5_final(c->key, &md5);\n\n    ngx_memcpy(c->main, c->key, NGX_HTTP_CACHE_KEY_LEN);\n}\n\n\nngx_int_t\nngx_http_file_cache_open(ngx_http_request_t *r)\n{\n    ngx_int_t                  rc, rv;\n    ngx_uint_t                 test;\n    ngx_http_cache_t          *c;\n    ngx_pool_cleanup_t        *cln;\n    ngx_open_file_info_t       of;\n    ngx_http_file_cache_t     *cache;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    c = r->cache;\n\n    if (c->waiting) {\n        return NGX_AGAIN;\n    }\n\n    if (c->reading) {\n        return ngx_http_file_cache_read(r, c);\n    }\n\n    cache = c->file_cache;\n\n    if (c->node == 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_file_cache_cleanup;\n        cln->data = c;\n    }\n\n    c->buffer_size = c->body_start;\n\n    rc = ngx_http_file_cache_exists(cache, c);\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http file cache exists: %i e:%d\", rc, c->exists);\n\n    if (rc == NGX_ERROR) {\n        return rc;\n    }\n\n    if (rc == NGX_AGAIN) {\n        return NGX_HTTP_CACHE_SCARCE;\n    }\n\n    if (rc == NGX_OK) {\n\n        if (c->error) {\n            return c->error;\n        }\n\n        c->temp_file = 1;\n        test = c->exists ? 1 : 0;\n        rv = NGX_DECLINED;\n\n    } else { /* rc == NGX_DECLINED */\n\n        test = cache->sh->cold ? 1 : 0;\n\n        if (c->min_uses > 1) {\n\n            if (!test) {\n                return NGX_HTTP_CACHE_SCARCE;\n            }\n\n            rv = NGX_HTTP_CACHE_SCARCE;\n\n        } else {\n            c->temp_file = 1;\n            rv = NGX_DECLINED;\n        }\n    }\n\n    if (ngx_http_file_cache_name(r, cache->path) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    if (!test) {\n        goto done;\n    }\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    ngx_memzero(&of, sizeof(ngx_open_file_info_t));\n\n    of.uniq = c->uniq;\n    of.valid = clcf->open_file_cache_valid;\n    of.min_uses = clcf->open_file_cache_min_uses;\n    of.events = clcf->open_file_cache_events;\n    of.directio = NGX_OPEN_FILE_DIRECTIO_OFF;\n    of.read_ahead = clcf->read_ahead;\n\n    if (ngx_open_cached_file(clcf->open_file_cache, &c->file.name, &of, r->pool)\n        != NGX_OK)\n    {\n        switch (of.err) {\n\n        case 0:\n            return NGX_ERROR;\n\n        case NGX_ENOENT:\n        case NGX_ENOTDIR:\n            goto done;\n\n        default:\n            ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err,\n                          ngx_open_file_n \" \\\"%s\\\" failed\", c->file.name.data);\n            return NGX_ERROR;\n        }\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http file cache fd: %d\", of.fd);\n\n    c->file.fd = of.fd;\n    c->file.log = r->connection->log;\n    c->uniq = of.uniq;\n    c->length = of.size;\n    c->fs_size = (of.fs_size + cache->bsize - 1) / cache->bsize;\n\n    c->buf = ngx_create_temp_buf(r->pool, c->body_start);\n    if (c->buf == NULL) {\n        return NGX_ERROR;\n    }\n\n    return ngx_http_file_cache_read(r, c);\n\ndone:\n\n    if (rv == NGX_DECLINED) {\n        return ngx_http_file_cache_lock(r, c);\n    }\n\n    return rv;\n}\n\n\nstatic ngx_int_t\nngx_http_file_cache_lock(ngx_http_request_t *r, ngx_http_cache_t *c)\n{\n    ngx_msec_t                 now, timer;\n    ngx_http_file_cache_t     *cache;\n\n    if (!c->lock) {\n        return NGX_DECLINED;\n    }\n\n    now = ngx_current_msec;\n\n    cache = c->file_cache;\n\n    ngx_shmtx_lock(&cache->shpool->mutex);\n\n    timer = c->node->lock_time - now;\n\n    if (!c->node->updating || (ngx_msec_int_t) timer <= 0) {\n        c->node->updating = 1;\n        c->node->lock_time = now + c->lock_age;\n        c->updating = 1;\n        c->lock_time = c->node->lock_time;\n    }\n\n    ngx_shmtx_unlock(&cache->shpool->mutex);\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http file cache lock u:%d wt:%M\",\n                   c->updating, c->wait_time);\n\n    if (c->updating) {\n        return NGX_DECLINED;\n    }\n\n    if (c->lock_timeout == 0) {\n        return NGX_HTTP_CACHE_SCARCE;\n    }\n\n    c->waiting = 1;\n\n    if (c->wait_time == 0) {\n        c->wait_time = now + c->lock_timeout;\n\n        c->wait_event.handler = ngx_http_file_cache_lock_wait_handler;\n        c->wait_event.data = r;\n        c->wait_event.log = r->connection->log;\n    }\n\n    timer = c->wait_time - now;\n\n    ngx_add_timer(&c->wait_event, (timer > 500) ? 500 : timer);\n\n    r->main->blocked++;\n\n    return NGX_AGAIN;\n}\n\n\nstatic void\nngx_http_file_cache_lock_wait_handler(ngx_event_t *ev)\n{\n    ngx_connection_t    *c;\n    ngx_http_request_t  *r;\n\n    r = ev->data;\n    c = r->connection;\n\n    ngx_http_set_log_request(c->log, r);\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http file cache wait: \\\"%V?%V\\\"\", &r->uri, &r->args);\n\n    ngx_http_file_cache_lock_wait(r, r->cache);\n\n    ngx_http_run_posted_requests(c);\n}\n\n\nstatic void\nngx_http_file_cache_lock_wait(ngx_http_request_t *r, ngx_http_cache_t *c)\n{\n    ngx_uint_t              wait;\n    ngx_msec_t              now, timer;\n    ngx_http_file_cache_t  *cache;\n\n    now = ngx_current_msec;\n\n    timer = c->wait_time - now;\n\n    if ((ngx_msec_int_t) timer <= 0) {\n        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                      \"cache lock timeout\");\n        c->lock_timeout = 0;\n        goto wakeup;\n    }\n\n    cache = c->file_cache;\n    wait = 0;\n\n    ngx_shmtx_lock(&cache->shpool->mutex);\n\n    timer = c->node->lock_time - now;\n\n    if (c->node->updating && (ngx_msec_int_t) timer > 0) {\n        wait = 1;\n    }\n\n    ngx_shmtx_unlock(&cache->shpool->mutex);\n\n    if (wait) {\n        ngx_add_timer(&c->wait_event, (timer > 500) ? 500 : timer);\n        return;\n    }\n\nwakeup:\n\n    c->waiting = 0;\n    r->main->blocked--;\n    r->write_event_handler(r);\n}\n\n\nstatic ngx_int_t\nngx_http_file_cache_read(ngx_http_request_t *r, ngx_http_cache_t *c)\n{\n    u_char                        *p;\n    time_t                         now;\n    ssize_t                        n;\n    ngx_str_t                     *key;\n    ngx_int_t                      rc;\n    ngx_uint_t                     i;\n    ngx_http_file_cache_t         *cache;\n    ngx_http_file_cache_header_t  *h;\n\n    n = ngx_http_file_cache_aio_read(r, c);\n\n    if (n < 0) {\n        return n;\n    }\n\n    if ((size_t) n < c->header_start) {\n        ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,\n                      \"cache file \\\"%s\\\" is too small\", c->file.name.data);\n        return NGX_DECLINED;\n    }\n\n    h = (ngx_http_file_cache_header_t *) c->buf->pos;\n\n    if (h->version != NGX_HTTP_CACHE_VERSION) {\n        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                      \"cache file \\\"%s\\\" version mismatch\", c->file.name.data);\n        return NGX_DECLINED;\n    }\n\n    if (h->crc32 != c->crc32 || (size_t) h->header_start != c->header_start) {\n        ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,\n                      \"cache file \\\"%s\\\" has md5 collision\", c->file.name.data);\n        return NGX_DECLINED;\n    }\n\n    p = c->buf->pos + sizeof(ngx_http_file_cache_header_t)\n        + sizeof(ngx_http_file_cache_key);\n\n    key = c->keys.elts;\n    for (i = 0; i < c->keys.nelts; i++) {\n        if (ngx_memcmp(p, key[i].data, key[i].len) != 0) {\n            ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,\n                          \"cache file \\\"%s\\\" has md5 collision\",\n                          c->file.name.data);\n            return NGX_DECLINED;\n        }\n\n        p += key[i].len;\n    }\n\n    if ((size_t) h->body_start > c->body_start) {\n        ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,\n                      \"cache file \\\"%s\\\" has too long header\",\n                      c->file.name.data);\n        return NGX_DECLINED;\n    }\n\n    if (h->vary_len > NGX_HTTP_CACHE_VARY_LEN) {\n        ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,\n                      \"cache file \\\"%s\\\" has incorrect vary length\",\n                      c->file.name.data);\n        return NGX_DECLINED;\n    }\n\n    if (h->vary_len) {\n        ngx_http_file_cache_vary(r, h->vary, h->vary_len, c->variant);\n\n        if (ngx_memcmp(c->variant, h->variant, NGX_HTTP_CACHE_KEY_LEN) != 0) {\n            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"http file cache vary mismatch\");\n            return ngx_http_file_cache_reopen(r, c);\n        }\n    }\n\n    c->buf->last += n;\n\n    c->valid_sec = h->valid_sec;\n    c->updating_sec = h->updating_sec;\n    c->error_sec = h->error_sec;\n    c->last_modified = h->last_modified;\n    c->date = h->date;\n    c->valid_msec = h->valid_msec;\n    c->body_start = h->body_start;\n    c->etag.len = h->etag_len;\n    c->etag.data = h->etag;\n\n    r->cached = 1;\n\n    cache = c->file_cache;\n\n    if (cache->sh->cold) {\n\n        ngx_shmtx_lock(&cache->shpool->mutex);\n\n        if (!c->node->exists) {\n            c->node->uses = 1;\n            c->node->body_start = c->body_start;\n            c->node->exists = 1;\n            c->node->uniq = c->uniq;\n            c->node->fs_size = c->fs_size;\n\n            cache->sh->size += c->fs_size;\n        }\n\n        ngx_shmtx_unlock(&cache->shpool->mutex);\n    }\n\n    now = ngx_time();\n\n    if (c->valid_sec < now) {\n        c->stale_updating = c->valid_sec + c->updating_sec >= now;\n        c->stale_error = c->valid_sec + c->error_sec >= now;\n\n        ngx_shmtx_lock(&cache->shpool->mutex);\n\n        if (c->node->updating) {\n            rc = NGX_HTTP_CACHE_UPDATING;\n\n        } else {\n            c->node->updating = 1;\n            c->updating = 1;\n            c->lock_time = c->node->lock_time;\n            rc = NGX_HTTP_CACHE_STALE;\n        }\n\n        ngx_shmtx_unlock(&cache->shpool->mutex);\n\n        ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"http file cache expired: %i %T %T\",\n                       rc, c->valid_sec, now);\n\n        return rc;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ssize_t\nngx_http_file_cache_aio_read(ngx_http_request_t *r, ngx_http_cache_t *c)\n{\n#if (NGX_HAVE_FILE_AIO || NGX_THREADS)\n    ssize_t                    n;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n#endif\n\n#if (NGX_HAVE_FILE_AIO)\n\n    if (clcf->aio == NGX_HTTP_AIO_ON && ngx_file_aio) {\n        n = ngx_file_aio_read(&c->file, c->buf->pos, c->body_start, 0, r->pool);\n\n        if (n != NGX_AGAIN) {\n            c->reading = 0;\n            return n;\n        }\n\n        c->reading = 1;\n\n        c->file.aio->data = r;\n        c->file.aio->handler = ngx_http_cache_aio_event_handler;\n\n        r->main->blocked++;\n        r->aio = 1;\n\n        return NGX_AGAIN;\n    }\n\n#endif\n\n#if (NGX_THREADS)\n\n    if (clcf->aio == NGX_HTTP_AIO_THREADS) {\n        c->file.thread_task = c->thread_task;\n        c->file.thread_handler = ngx_http_cache_thread_handler;\n        c->file.thread_ctx = r;\n\n        n = ngx_thread_read(&c->file, c->buf->pos, c->body_start, 0, r->pool);\n\n        c->thread_task = c->file.thread_task;\n        c->reading = (n == NGX_AGAIN);\n\n        return n;\n    }\n\n#endif\n\n    return ngx_read_file(&c->file, c->buf->pos, c->body_start, 0);\n}\n\n\n#if (NGX_HAVE_FILE_AIO)\n\nstatic void\nngx_http_cache_aio_event_handler(ngx_event_t *ev)\n{\n    ngx_event_aio_t     *aio;\n    ngx_connection_t    *c;\n    ngx_http_request_t  *r;\n\n    aio = ev->data;\n    r = aio->data;\n    c = r->connection;\n\n    ngx_http_set_log_request(c->log, r);\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http file cache aio: \\\"%V?%V\\\"\", &r->uri, &r->args);\n\n    r->main->blocked--;\n    r->aio = 0;\n\n    r->write_event_handler(r);\n\n    ngx_http_run_posted_requests(c);\n}\n\n#endif\n\n\n#if (NGX_THREADS)\n\nstatic ngx_int_t\nngx_http_cache_thread_handler(ngx_thread_task_t *task, ngx_file_t *file)\n{\n    ngx_str_t                  name;\n    ngx_thread_pool_t         *tp;\n    ngx_http_request_t        *r;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    r = file->thread_ctx;\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n    tp = clcf->thread_pool;\n\n    if (tp == NULL) {\n        if (ngx_http_complex_value(r, clcf->thread_pool_value, &name)\n            != NGX_OK)\n        {\n            return NGX_ERROR;\n        }\n\n        tp = ngx_thread_pool_get((ngx_cycle_t *) ngx_cycle, &name);\n\n        if (tp == NULL) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"thread pool \\\"%V\\\" not found\", &name);\n            return NGX_ERROR;\n        }\n    }\n\n    task->event.data = r;\n    task->event.handler = ngx_http_cache_thread_event_handler;\n\n    if (ngx_thread_task_post(tp, task) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    r->main->blocked++;\n    r->aio = 1;\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_cache_thread_event_handler(ngx_event_t *ev)\n{\n    ngx_connection_t    *c;\n    ngx_http_request_t  *r;\n\n    r = ev->data;\n    c = r->connection;\n\n    ngx_http_set_log_request(c->log, r);\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http file cache thread: \\\"%V?%V\\\"\", &r->uri, &r->args);\n\n    r->main->blocked--;\n    r->aio = 0;\n\n    r->write_event_handler(r);\n\n    ngx_http_run_posted_requests(c);\n}\n\n#endif\n\n\nstatic ngx_int_t\nngx_http_file_cache_exists(ngx_http_file_cache_t *cache, ngx_http_cache_t *c)\n{\n    ngx_int_t                    rc;\n    ngx_http_file_cache_node_t  *fcn;\n\n    ngx_shmtx_lock(&cache->shpool->mutex);\n\n    fcn = c->node;\n\n    if (fcn == NULL) {\n        fcn = ngx_http_file_cache_lookup(cache, c->key);\n    }\n\n    if (fcn) {\n        ngx_queue_remove(&fcn->queue);\n\n        if (c->node == NULL) {\n            fcn->uses++;\n            fcn->count++;\n        }\n\n        if (fcn->error) {\n\n            if (fcn->valid_sec < ngx_time()) {\n                goto renew;\n            }\n\n            rc = NGX_OK;\n\n            goto done;\n        }\n\n        if (fcn->exists || fcn->uses >= c->min_uses) {\n\n            c->exists = fcn->exists;\n            if (fcn->body_start && !c->update_variant) {\n                c->body_start = fcn->body_start;\n            }\n\n            rc = NGX_OK;\n\n            goto done;\n        }\n\n        rc = NGX_AGAIN;\n\n        goto done;\n    }\n\n    fcn = ngx_slab_calloc_locked(cache->shpool,\n                                 sizeof(ngx_http_file_cache_node_t));\n    if (fcn == NULL) {\n        ngx_http_file_cache_set_watermark(cache);\n\n        ngx_shmtx_unlock(&cache->shpool->mutex);\n\n        (void) ngx_http_file_cache_forced_expire(cache);\n\n        ngx_shmtx_lock(&cache->shpool->mutex);\n\n        fcn = ngx_slab_calloc_locked(cache->shpool,\n                                     sizeof(ngx_http_file_cache_node_t));\n        if (fcn == NULL) {\n            ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,\n                          \"could not allocate node%s\", cache->shpool->log_ctx);\n            rc = NGX_ERROR;\n            goto failed;\n        }\n    }\n\n    cache->sh->count++;\n\n    ngx_memcpy((u_char *) &fcn->node.key, c->key, sizeof(ngx_rbtree_key_t));\n\n    ngx_memcpy(fcn->key, &c->key[sizeof(ngx_rbtree_key_t)],\n               NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t));\n\n    ngx_rbtree_insert(&cache->sh->rbtree, &fcn->node);\n\n    fcn->uses = 1;\n    fcn->count = 1;\n\nrenew:\n\n    rc = NGX_DECLINED;\n\n    fcn->valid_msec = 0;\n    fcn->error = 0;\n    fcn->exists = 0;\n    fcn->valid_sec = 0;\n    fcn->uniq = 0;\n    fcn->body_start = 0;\n    fcn->fs_size = 0;\n\ndone:\n\n    fcn->expire = ngx_time() + cache->inactive;\n\n    ngx_queue_insert_head(&cache->sh->queue, &fcn->queue);\n\n    c->uniq = fcn->uniq;\n    c->error = fcn->error;\n    c->node = fcn;\n\nfailed:\n\n    ngx_shmtx_unlock(&cache->shpool->mutex);\n\n    return rc;\n}\n\n\nstatic ngx_int_t\nngx_http_file_cache_name(ngx_http_request_t *r, ngx_path_t *path)\n{\n    u_char            *p;\n    ngx_http_cache_t  *c;\n\n    c = r->cache;\n\n    if (c->file.name.len) {\n        return NGX_OK;\n    }\n\n    c->file.name.len = path->name.len + 1 + path->len\n                       + 2 * NGX_HTTP_CACHE_KEY_LEN;\n\n    c->file.name.data = ngx_pnalloc(r->pool, c->file.name.len + 1);\n    if (c->file.name.data == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_memcpy(c->file.name.data, path->name.data, path->name.len);\n\n    p = c->file.name.data + path->name.len + 1 + path->len;\n    p = ngx_hex_dump(p, c->key, NGX_HTTP_CACHE_KEY_LEN);\n    *p = '\\0';\n\n    ngx_create_hashed_filename(path, c->file.name.data, c->file.name.len);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"cache file: \\\"%s\\\"\", c->file.name.data);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_http_file_cache_node_t *\nngx_http_file_cache_lookup(ngx_http_file_cache_t *cache, u_char *key)\n{\n    ngx_int_t                    rc;\n    ngx_rbtree_key_t             node_key;\n    ngx_rbtree_node_t           *node, *sentinel;\n    ngx_http_file_cache_node_t  *fcn;\n\n    ngx_memcpy((u_char *) &node_key, key, sizeof(ngx_rbtree_key_t));\n\n    node = cache->sh->rbtree.root;\n    sentinel = cache->sh->rbtree.sentinel;\n\n    while (node != sentinel) {\n\n        if (node_key < node->key) {\n            node = node->left;\n            continue;\n        }\n\n        if (node_key > node->key) {\n            node = node->right;\n            continue;\n        }\n\n        /* node_key == node->key */\n\n        fcn = (ngx_http_file_cache_node_t *) node;\n\n        rc = ngx_memcmp(&key[sizeof(ngx_rbtree_key_t)], fcn->key,\n                        NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t));\n\n        if (rc == 0) {\n            return fcn;\n        }\n\n        node = (rc < 0) ? node->left : node->right;\n    }\n\n    /* not found */\n\n    return NULL;\n}\n\n\nstatic void\nngx_http_file_cache_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_file_cache_node_t   *cn, *cnt;\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            cn = (ngx_http_file_cache_node_t *) node;\n            cnt = (ngx_http_file_cache_node_t *) temp;\n\n            p = (ngx_memcmp(cn->key, cnt->key,\n                            NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t))\n                 < 0)\n                    ? &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 void\nngx_http_file_cache_vary(ngx_http_request_t *r, u_char *vary, size_t len,\n    u_char *hash)\n{\n    u_char     *p, *last;\n    ngx_str_t   name;\n    ngx_md5_t   md5;\n    u_char      buf[NGX_HTTP_CACHE_VARY_LEN];\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http file cache vary: \\\"%*s\\\"\", len, vary);\n\n    ngx_md5_init(&md5);\n    ngx_md5_update(&md5, r->cache->main, NGX_HTTP_CACHE_KEY_LEN);\n\n    ngx_strlow(buf, vary, len);\n\n    p = buf;\n    last = buf + len;\n\n    while (p < last) {\n\n        while (p < last && (*p == ' ' || *p == ',')) { p++; }\n\n        name.data = p;\n\n        while (p < last && *p != ',' && *p != ' ') { p++; }\n\n        name.len = p - name.data;\n\n        if (name.len == 0) {\n            break;\n        }\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"http file cache vary: %V\", &name);\n\n        ngx_md5_update(&md5, name.data, name.len);\n        ngx_md5_update(&md5, (u_char *) \":\", sizeof(\":\") - 1);\n\n        ngx_http_file_cache_vary_header(r, &md5, &name);\n\n        ngx_md5_update(&md5, (u_char *) CRLF, sizeof(CRLF) - 1);\n    }\n\n    ngx_md5_final(hash, &md5);\n}\n\n\nstatic void\nngx_http_file_cache_vary_header(ngx_http_request_t *r, ngx_md5_t *md5,\n    ngx_str_t *name)\n{\n    size_t            len;\n    u_char           *p, *start, *last;\n    ngx_uint_t        i, multiple, normalize;\n    ngx_list_part_t  *part;\n    ngx_table_elt_t  *header;\n\n    multiple = 0;\n    normalize = 0;\n\n    if (name->len == sizeof(\"Accept-Charset\") - 1\n        && ngx_strncasecmp(name->data, (u_char *) \"Accept-Charset\",\n                           sizeof(\"Accept-Charset\") - 1) == 0)\n    {\n        normalize = 1;\n\n    } else if (name->len == sizeof(\"Accept-Encoding\") - 1\n        && ngx_strncasecmp(name->data, (u_char *) \"Accept-Encoding\",\n                           sizeof(\"Accept-Encoding\") - 1) == 0)\n    {\n        normalize = 1;\n\n    } else if (name->len == sizeof(\"Accept-Language\") - 1\n        && ngx_strncasecmp(name->data, (u_char *) \"Accept-Language\",\n                           sizeof(\"Accept-Language\") - 1) == 0)\n    {\n        normalize = 1;\n    }\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 (header[i].hash == 0) {\n            continue;\n        }\n\n        if (header[i].key.len != name->len) {\n            continue;\n        }\n\n        if (ngx_strncasecmp(header[i].key.data, name->data, name->len) != 0) {\n            continue;\n        }\n\n        if (!normalize) {\n\n            if (multiple) {\n                ngx_md5_update(md5, (u_char *) \",\", sizeof(\",\") - 1);\n            }\n\n            ngx_md5_update(md5, header[i].value.data, header[i].value.len);\n\n            multiple = 1;\n\n            continue;\n        }\n\n        /* normalize spaces */\n\n        p = header[i].value.data;\n        last = p + header[i].value.len;\n\n        while (p < last) {\n\n            while (p < last && (*p == ' ' || *p == ',')) { p++; }\n\n            start = p;\n\n            while (p < last && *p != ',' && *p != ' ') { p++; }\n\n            len = p - start;\n\n            if (len == 0) {\n                break;\n            }\n\n            if (multiple) {\n                ngx_md5_update(md5, (u_char *) \",\", sizeof(\",\") - 1);\n            }\n\n            ngx_md5_update(md5, start, len);\n\n            multiple = 1;\n        }\n    }\n}\n\n\nstatic ngx_int_t\nngx_http_file_cache_reopen(ngx_http_request_t *r, ngx_http_cache_t *c)\n{\n    ngx_http_file_cache_t  *cache;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->file.log, 0,\n                   \"http file cache reopen\");\n\n    if (c->secondary) {\n        ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,\n                      \"cache file \\\"%s\\\" has incorrect vary hash\",\n                      c->file.name.data);\n        return NGX_DECLINED;\n    }\n\n    cache = c->file_cache;\n\n    ngx_shmtx_lock(&cache->shpool->mutex);\n\n    c->node->count--;\n    c->node = NULL;\n\n    ngx_shmtx_unlock(&cache->shpool->mutex);\n\n    c->secondary = 1;\n    c->file.name.len = 0;\n    c->body_start = c->buffer_size;\n\n    ngx_memcpy(c->key, c->variant, NGX_HTTP_CACHE_KEY_LEN);\n\n    return ngx_http_file_cache_open(r);\n}\n\n\nngx_int_t\nngx_http_file_cache_set_header(ngx_http_request_t *r, u_char *buf)\n{\n    ngx_http_file_cache_header_t  *h = (ngx_http_file_cache_header_t *) buf;\n\n    u_char            *p;\n    ngx_str_t         *key;\n    ngx_uint_t         i;\n    ngx_http_cache_t  *c;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http file cache set header\");\n\n    c = r->cache;\n\n    ngx_memzero(h, sizeof(ngx_http_file_cache_header_t));\n\n    h->version = NGX_HTTP_CACHE_VERSION;\n    h->valid_sec = c->valid_sec;\n    h->updating_sec = c->updating_sec;\n    h->error_sec = c->error_sec;\n    h->last_modified = c->last_modified;\n    h->date = c->date;\n    h->crc32 = c->crc32;\n    h->valid_msec = (u_short) c->valid_msec;\n    h->header_start = (u_short) c->header_start;\n    h->body_start = (u_short) c->body_start;\n\n    if (c->etag.len <= NGX_HTTP_CACHE_ETAG_LEN) {\n        h->etag_len = (u_char) c->etag.len;\n        ngx_memcpy(h->etag, c->etag.data, c->etag.len);\n    }\n\n    if (c->vary.len) {\n        if (c->vary.len > NGX_HTTP_CACHE_VARY_LEN) {\n            /* should not happen */\n            c->vary.len = NGX_HTTP_CACHE_VARY_LEN;\n        }\n\n        h->vary_len = (u_char) c->vary.len;\n        ngx_memcpy(h->vary, c->vary.data, c->vary.len);\n\n        ngx_http_file_cache_vary(r, c->vary.data, c->vary.len, c->variant);\n        ngx_memcpy(h->variant, c->variant, NGX_HTTP_CACHE_KEY_LEN);\n    }\n\n    if (ngx_http_file_cache_update_variant(r, c) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    p = buf + sizeof(ngx_http_file_cache_header_t);\n\n    p = ngx_cpymem(p, ngx_http_file_cache_key, sizeof(ngx_http_file_cache_key));\n\n    key = c->keys.elts;\n    for (i = 0; i < c->keys.nelts; i++) {\n        p = ngx_copy(p, key[i].data, key[i].len);\n    }\n\n    *p = LF;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_file_cache_update_variant(ngx_http_request_t *r, ngx_http_cache_t *c)\n{\n    ngx_http_file_cache_t  *cache;\n\n    if (!c->secondary) {\n        return NGX_OK;\n    }\n\n    if (c->vary.len\n        && ngx_memcmp(c->variant, c->key, NGX_HTTP_CACHE_KEY_LEN) == 0)\n    {\n        return NGX_OK;\n    }\n\n    /*\n     * if the variant hash doesn't match one we used as a secondary\n     * cache key, switch back to the original key\n     */\n\n    cache = c->file_cache;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http file cache main key\");\n\n    ngx_shmtx_lock(&cache->shpool->mutex);\n\n    c->node->count--;\n    c->node->updating = 0;\n    c->node = NULL;\n\n    ngx_shmtx_unlock(&cache->shpool->mutex);\n\n    c->file.name.len = 0;\n    c->update_variant = 1;\n\n    ngx_memcpy(c->key, c->main, NGX_HTTP_CACHE_KEY_LEN);\n\n    if (ngx_http_file_cache_exists(cache, c) == NGX_ERROR) {\n        return NGX_ERROR;\n    }\n\n    if (ngx_http_file_cache_name(r, cache->path) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nvoid\nngx_http_file_cache_update(ngx_http_request_t *r, ngx_temp_file_t *tf)\n{\n    off_t                   fs_size;\n    ngx_int_t               rc;\n    ngx_file_uniq_t         uniq;\n    ngx_file_info_t         fi;\n    ngx_http_cache_t        *c;\n    ngx_ext_rename_file_t   ext;\n    ngx_http_file_cache_t  *cache;\n\n    c = r->cache;\n\n    if (c->updated) {\n        return;\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http file cache update\");\n\n    cache = c->file_cache;\n\n    c->updated = 1;\n    c->updating = 0;\n\n    uniq = 0;\n    fs_size = 0;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http file cache rename: \\\"%s\\\" to \\\"%s\\\"\",\n                   tf->file.name.data, c->file.name.data);\n\n    ext.access = NGX_FILE_OWNER_ACCESS;\n    ext.path_access = NGX_FILE_OWNER_ACCESS;\n    ext.time = -1;\n    ext.create_path = 1;\n    ext.delete_file = 1;\n    ext.log = r->connection->log;\n\n    rc = ngx_ext_rename_file(&tf->file.name, &c->file.name, &ext);\n\n    if (rc == NGX_OK) {\n\n        if (ngx_fd_info(tf->file.fd, &fi) == NGX_FILE_ERROR) {\n            ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,\n                          ngx_fd_info_n \" \\\"%s\\\" failed\", tf->file.name.data);\n\n            rc = NGX_ERROR;\n\n        } else {\n            uniq = ngx_file_uniq(&fi);\n            fs_size = (ngx_file_fs_size(&fi) + cache->bsize - 1) / cache->bsize;\n        }\n    }\n\n    ngx_shmtx_lock(&cache->shpool->mutex);\n\n    c->node->count--;\n    c->node->error = 0;\n    c->node->uniq = uniq;\n    c->node->body_start = c->body_start;\n\n    cache->sh->size += fs_size - c->node->fs_size;\n    c->node->fs_size = fs_size;\n\n    if (rc == NGX_OK) {\n        c->node->exists = 1;\n    }\n\n    c->node->updating = 0;\n\n    ngx_shmtx_unlock(&cache->shpool->mutex);\n}\n\n\nvoid\nngx_http_file_cache_update_header(ngx_http_request_t *r)\n{\n    ssize_t                        n;\n    ngx_err_t                      err;\n    ngx_file_t                     file;\n    ngx_file_info_t                fi;\n    ngx_http_cache_t              *c;\n    ngx_http_file_cache_header_t   h;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http file cache update header\");\n\n    c = r->cache;\n\n    ngx_memzero(&file, sizeof(ngx_file_t));\n\n    file.name = c->file.name;\n    file.log = r->connection->log;\n    file.fd = ngx_open_file(file.name.data, NGX_FILE_RDWR, NGX_FILE_OPEN, 0);\n\n    if (file.fd == NGX_INVALID_FILE) {\n        err = ngx_errno;\n\n        /* cache file may have been deleted */\n\n        if (err == NGX_ENOENT) {\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"http file cache \\\"%s\\\" not found\",\n                           file.name.data);\n            return;\n        }\n\n        ngx_log_error(NGX_LOG_CRIT, r->connection->log, err,\n                      ngx_open_file_n \" \\\"%s\\\" failed\", file.name.data);\n        return;\n    }\n\n    /*\n     * make sure cache file wasn't replaced;\n     * if it was, do nothing\n     */\n\n    if (ngx_fd_info(file.fd, &fi) == NGX_FILE_ERROR) {\n        ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,\n                      ngx_fd_info_n \" \\\"%s\\\" failed\", file.name.data);\n        goto done;\n    }\n\n    if (c->uniq != ngx_file_uniq(&fi)\n        || c->length != ngx_file_size(&fi))\n    {\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"http file cache \\\"%s\\\" changed\",\n                       file.name.data);\n        goto done;\n    }\n\n    n = ngx_read_file(&file, (u_char *) &h,\n                      sizeof(ngx_http_file_cache_header_t), 0);\n\n    if (n == NGX_ERROR) {\n        goto done;\n    }\n\n    if ((size_t) n != sizeof(ngx_http_file_cache_header_t)) {\n        ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,\n                      ngx_read_file_n \" read only %z of %z from \\\"%s\\\"\",\n                      n, sizeof(ngx_http_file_cache_header_t), file.name.data);\n        goto done;\n    }\n\n    if (h.version != NGX_HTTP_CACHE_VERSION\n        || h.last_modified != c->last_modified\n        || h.crc32 != c->crc32\n        || (size_t) h.header_start != c->header_start\n        || (size_t) h.body_start != c->body_start)\n    {\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"http file cache \\\"%s\\\" content changed\",\n                       file.name.data);\n        goto done;\n    }\n\n    /*\n     * update cache file header with new data,\n     * notably h.valid_sec and h.date\n     */\n\n    ngx_memzero(&h, sizeof(ngx_http_file_cache_header_t));\n\n    h.version = NGX_HTTP_CACHE_VERSION;\n    h.valid_sec = c->valid_sec;\n    h.updating_sec = c->updating_sec;\n    h.error_sec = c->error_sec;\n    h.last_modified = c->last_modified;\n    h.date = c->date;\n    h.crc32 = c->crc32;\n    h.valid_msec = (u_short) c->valid_msec;\n    h.header_start = (u_short) c->header_start;\n    h.body_start = (u_short) c->body_start;\n\n    if (c->etag.len <= NGX_HTTP_CACHE_ETAG_LEN) {\n        h.etag_len = (u_char) c->etag.len;\n        ngx_memcpy(h.etag, c->etag.data, c->etag.len);\n    }\n\n    if (c->vary.len) {\n        if (c->vary.len > NGX_HTTP_CACHE_VARY_LEN) {\n            /* should not happen */\n            c->vary.len = NGX_HTTP_CACHE_VARY_LEN;\n        }\n\n        h.vary_len = (u_char) c->vary.len;\n        ngx_memcpy(h.vary, c->vary.data, c->vary.len);\n\n        ngx_http_file_cache_vary(r, c->vary.data, c->vary.len, c->variant);\n        ngx_memcpy(h.variant, c->variant, NGX_HTTP_CACHE_KEY_LEN);\n    }\n\n    (void) ngx_write_file(&file, (u_char *) &h,\n                          sizeof(ngx_http_file_cache_header_t), 0);\n\ndone:\n\n    if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {\n        ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,\n                      ngx_close_file_n \" \\\"%s\\\" failed\", file.name.data);\n    }\n}\n\n\nngx_int_t\nngx_http_cache_send(ngx_http_request_t *r)\n{\n    ngx_int_t          rc;\n    ngx_buf_t         *b;\n    ngx_chain_t        out;\n    ngx_http_cache_t  *c;\n\n    c = r->cache;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http file cache send: %s\", c->file.name.data);\n\n    /* we need to allocate all before the header would be sent */\n\n    b = ngx_calloc_buf(r->pool);\n    if (b == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t));\n    if (b->file == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    rc = ngx_http_send_header(r);\n\n    if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {\n        return rc;\n    }\n\n    b->file_pos = c->body_start;\n    b->file_last = c->length;\n\n    b->in_file = (c->length - c->body_start) ? 1 : 0;\n    b->last_buf = (r == r->main) ? 1 : 0;\n    b->last_in_chain = 1;\n    b->sync = (b->last_buf || b->in_file) ? 0 : 1;\n\n    b->file->fd = c->file.fd;\n    b->file->name = c->file.name;\n    b->file->log = r->connection->log;\n\n    out.buf = b;\n    out.next = NULL;\n\n    return ngx_http_output_filter(r, &out);\n}\n\n\nvoid\nngx_http_file_cache_free(ngx_http_cache_t *c, ngx_temp_file_t *tf)\n{\n    ngx_http_file_cache_t       *cache;\n    ngx_http_file_cache_node_t  *fcn;\n\n    if (c->updated || c->node == NULL) {\n        return;\n    }\n\n    cache = c->file_cache;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->file.log, 0,\n                   \"http file cache free, fd: %d\", c->file.fd);\n\n    ngx_shmtx_lock(&cache->shpool->mutex);\n\n    fcn = c->node;\n    fcn->count--;\n\n    if (c->updating && fcn->lock_time == c->lock_time) {\n        fcn->updating = 0;\n    }\n\n    if (c->error) {\n        fcn->error = c->error;\n\n        if (c->valid_sec) {\n            fcn->valid_sec = c->valid_sec;\n            fcn->valid_msec = c->valid_msec;\n        }\n\n    } else if (!fcn->exists && fcn->count == 0 && c->min_uses == 1) {\n        ngx_queue_remove(&fcn->queue);\n        ngx_rbtree_delete(&cache->sh->rbtree, &fcn->node);\n        ngx_slab_free_locked(cache->shpool, fcn);\n        cache->sh->count--;\n        c->node = NULL;\n    }\n\n    ngx_shmtx_unlock(&cache->shpool->mutex);\n\n    c->updated = 1;\n    c->updating = 0;\n\n    if (c->temp_file) {\n        if (tf && tf->file.fd != NGX_INVALID_FILE) {\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->file.log, 0,\n                           \"http file cache incomplete: \\\"%s\\\"\",\n                           tf->file.name.data);\n\n            if (ngx_delete_file(tf->file.name.data) == NGX_FILE_ERROR) {\n                ngx_log_error(NGX_LOG_CRIT, c->file.log, ngx_errno,\n                              ngx_delete_file_n \" \\\"%s\\\" failed\",\n                              tf->file.name.data);\n            }\n        }\n    }\n\n    if (c->wait_event.timer_set) {\n        ngx_del_timer(&c->wait_event);\n    }\n}\n\n\nstatic void\nngx_http_file_cache_cleanup(void *data)\n{\n    ngx_http_cache_t  *c = data;\n\n    if (c->updated) {\n        return;\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->file.log, 0,\n                   \"http file cache cleanup\");\n\n    if (c->updating && !c->background) {\n        ngx_log_error(NGX_LOG_ALERT, c->file.log, 0,\n                      \"stalled cache updating, error:%ui\", c->error);\n    }\n\n    ngx_http_file_cache_free(c, NULL);\n}\n\n\nstatic time_t\nngx_http_file_cache_forced_expire(ngx_http_file_cache_t *cache)\n{\n    u_char                      *name, *p;\n    size_t                       len;\n    time_t                       wait;\n    ngx_uint_t                   tries;\n    ngx_path_t                  *path;\n    ngx_queue_t                 *q, *sentinel;\n    ngx_http_file_cache_node_t  *fcn;\n    u_char                       key[2 * NGX_HTTP_CACHE_KEY_LEN];\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                   \"http file cache forced expire\");\n\n    path = cache->path;\n    len = path->name.len + 1 + path->len + 2 * NGX_HTTP_CACHE_KEY_LEN;\n\n    name = ngx_alloc(len + 1, ngx_cycle->log);\n    if (name == NULL) {\n        return 10;\n    }\n\n    ngx_memcpy(name, path->name.data, path->name.len);\n\n    wait = 10;\n    tries = 20;\n    sentinel = NULL;\n\n    ngx_shmtx_lock(&cache->shpool->mutex);\n\n    for ( ;; ) {\n        if (ngx_queue_empty(&cache->sh->queue)) {\n            break;\n        }\n\n        q = ngx_queue_last(&cache->sh->queue);\n\n        if (q == sentinel) {\n            break;\n        }\n\n        fcn = ngx_queue_data(q, ngx_http_file_cache_node_t, queue);\n\n        ngx_log_debug6(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                  \"http file cache forced expire: #%d %d %02xd%02xd%02xd%02xd\",\n                  fcn->count, fcn->exists,\n                  fcn->key[0], fcn->key[1], fcn->key[2], fcn->key[3]);\n\n        if (fcn->count == 0) {\n            ngx_http_file_cache_delete(cache, q, name);\n            wait = 0;\n            break;\n        }\n\n        if (fcn->deleting) {\n            wait = 1;\n            break;\n        }\n\n        p = ngx_hex_dump(key, (u_char *) &fcn->node.key,\n                         sizeof(ngx_rbtree_key_t));\n        len = NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t);\n        (void) ngx_hex_dump(p, fcn->key, len);\n\n        /*\n         * abnormally exited workers may leave locked cache entries,\n         * and although it may be safe to remove them completely,\n         * we prefer to just move them to the top of the inactive queue\n         */\n\n        ngx_queue_remove(q);\n        fcn->expire = ngx_time() + cache->inactive;\n        ngx_queue_insert_head(&cache->sh->queue, &fcn->queue);\n\n        ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,\n                      \"ignore long locked inactive cache entry %*s, count:%d\",\n                      (size_t) 2 * NGX_HTTP_CACHE_KEY_LEN, key, fcn->count);\n\n        if (sentinel == NULL) {\n            sentinel = q;\n        }\n\n        if (--tries) {\n            continue;\n        }\n\n        wait = 1;\n        break;\n    }\n\n    ngx_shmtx_unlock(&cache->shpool->mutex);\n\n    ngx_free(name);\n\n    return wait;\n}\n\n\nstatic time_t\nngx_http_file_cache_expire(ngx_http_file_cache_t *cache)\n{\n    u_char                      *name, *p;\n    size_t                       len;\n    time_t                       now, wait;\n    ngx_path_t                  *path;\n    ngx_msec_t                   elapsed;\n    ngx_queue_t                 *q;\n    ngx_http_file_cache_node_t  *fcn;\n    u_char                       key[2 * NGX_HTTP_CACHE_KEY_LEN];\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                   \"http file cache expire\");\n\n    path = cache->path;\n    len = path->name.len + 1 + path->len + 2 * NGX_HTTP_CACHE_KEY_LEN;\n\n    name = ngx_alloc(len + 1, ngx_cycle->log);\n    if (name == NULL) {\n        return 10;\n    }\n\n    ngx_memcpy(name, path->name.data, path->name.len);\n\n    now = ngx_time();\n\n    ngx_shmtx_lock(&cache->shpool->mutex);\n\n    for ( ;; ) {\n\n        if (ngx_quit || ngx_terminate) {\n            wait = 1;\n            break;\n        }\n\n        if (ngx_queue_empty(&cache->sh->queue)) {\n            wait = 10;\n            break;\n        }\n\n        q = ngx_queue_last(&cache->sh->queue);\n\n        fcn = ngx_queue_data(q, ngx_http_file_cache_node_t, queue);\n\n        wait = fcn->expire - now;\n\n        if (wait > 0) {\n            wait = wait > 10 ? 10 : wait;\n            break;\n        }\n\n        ngx_log_debug6(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                       \"http file cache expire: #%d %d %02xd%02xd%02xd%02xd\",\n                       fcn->count, fcn->exists,\n                       fcn->key[0], fcn->key[1], fcn->key[2], fcn->key[3]);\n\n        if (fcn->count == 0) {\n            ngx_http_file_cache_delete(cache, q, name);\n            goto next;\n        }\n\n        if (fcn->deleting) {\n            wait = 1;\n            break;\n        }\n\n        p = ngx_hex_dump(key, (u_char *) &fcn->node.key,\n                         sizeof(ngx_rbtree_key_t));\n        len = NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t);\n        (void) ngx_hex_dump(p, fcn->key, len);\n\n        /*\n         * abnormally exited workers may leave locked cache entries,\n         * and although it may be safe to remove them completely,\n         * we prefer to just move them to the top of the inactive queue\n         */\n\n        ngx_queue_remove(q);\n        fcn->expire = ngx_time() + cache->inactive;\n        ngx_queue_insert_head(&cache->sh->queue, &fcn->queue);\n\n        ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,\n                      \"ignore long locked inactive cache entry %*s, count:%d\",\n                      (size_t) 2 * NGX_HTTP_CACHE_KEY_LEN, key, fcn->count);\n\nnext:\n\n        if (++cache->files >= cache->manager_files) {\n            wait = 0;\n            break;\n        }\n\n        ngx_time_update();\n\n        elapsed = ngx_abs((ngx_msec_int_t) (ngx_current_msec - cache->last));\n\n        if (elapsed >= cache->manager_threshold) {\n            wait = 0;\n            break;\n        }\n    }\n\n    ngx_shmtx_unlock(&cache->shpool->mutex);\n\n    ngx_free(name);\n\n    return wait;\n}\n\n\nstatic void\nngx_http_file_cache_delete(ngx_http_file_cache_t *cache, ngx_queue_t *q,\n    u_char *name)\n{\n    u_char                      *p;\n    size_t                       len;\n    ngx_path_t                  *path;\n    ngx_http_file_cache_node_t  *fcn;\n\n    fcn = ngx_queue_data(q, ngx_http_file_cache_node_t, queue);\n\n    if (fcn->exists) {\n        cache->sh->size -= fcn->fs_size;\n\n        path = cache->path;\n        p = name + path->name.len + 1 + path->len;\n        p = ngx_hex_dump(p, (u_char *) &fcn->node.key,\n                         sizeof(ngx_rbtree_key_t));\n        len = NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t);\n        p = ngx_hex_dump(p, fcn->key, len);\n        *p = '\\0';\n\n        fcn->count++;\n        fcn->deleting = 1;\n        ngx_shmtx_unlock(&cache->shpool->mutex);\n\n        len = path->name.len + 1 + path->len + 2 * NGX_HTTP_CACHE_KEY_LEN;\n        ngx_create_hashed_filename(path, name, len);\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                       \"http file cache expire: \\\"%s\\\"\", name);\n\n        if (ngx_delete_file(name) == NGX_FILE_ERROR) {\n            ngx_log_error(NGX_LOG_CRIT, ngx_cycle->log, ngx_errno,\n                          ngx_delete_file_n \" \\\"%s\\\" failed\", name);\n        }\n\n        ngx_shmtx_lock(&cache->shpool->mutex);\n        fcn->count--;\n        fcn->deleting = 0;\n    }\n\n    if (fcn->count == 0) {\n        ngx_queue_remove(q);\n        ngx_rbtree_delete(&cache->sh->rbtree, &fcn->node);\n        ngx_slab_free_locked(cache->shpool, fcn);\n        cache->sh->count--;\n    }\n}\n\n\nstatic ngx_msec_t\nngx_http_file_cache_manager(void *data)\n{\n    ngx_http_file_cache_t  *cache = data;\n\n    off_t       size, free;\n    time_t      wait;\n    ngx_msec_t  elapsed, next;\n    ngx_uint_t  count, watermark;\n\n    cache->last = ngx_current_msec;\n    cache->files = 0;\n\n    next = (ngx_msec_t) ngx_http_file_cache_expire(cache) * 1000;\n\n    if (next == 0) {\n        next = cache->manager_sleep;\n        goto done;\n    }\n\n    for ( ;; ) {\n        ngx_shmtx_lock(&cache->shpool->mutex);\n\n        size = cache->sh->size;\n        count = cache->sh->count;\n        watermark = cache->sh->watermark;\n\n        ngx_shmtx_unlock(&cache->shpool->mutex);\n\n        ngx_log_debug3(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                       \"http file cache size: %O c:%ui w:%i\",\n                       size, count, (ngx_int_t) watermark);\n\n        if (size < cache->max_size && count < watermark) {\n\n            if (!cache->min_free) {\n                break;\n            }\n\n            free = ngx_fs_available(cache->path->name.data);\n\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                           \"http file cache free: %O\", free);\n\n            if (free > cache->min_free) {\n                break;\n            }\n        }\n\n        wait = ngx_http_file_cache_forced_expire(cache);\n\n        if (wait > 0) {\n            next = (ngx_msec_t) wait * 1000;\n            break;\n        }\n\n        if (ngx_quit || ngx_terminate) {\n            break;\n        }\n\n        if (++cache->files >= cache->manager_files) {\n            next = cache->manager_sleep;\n            break;\n        }\n\n        ngx_time_update();\n\n        elapsed = ngx_abs((ngx_msec_int_t) (ngx_current_msec - cache->last));\n\n        if (elapsed >= cache->manager_threshold) {\n            next = cache->manager_sleep;\n            break;\n        }\n    }\n\ndone:\n\n    elapsed = ngx_abs((ngx_msec_int_t) (ngx_current_msec - cache->last));\n\n    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                   \"http file cache manager: %ui e:%M n:%M\",\n                   cache->files, elapsed, next);\n\n    return next;\n}\n\n\nstatic void\nngx_http_file_cache_loader(void *data)\n{\n    ngx_http_file_cache_t  *cache = data;\n\n    ngx_tree_ctx_t  tree;\n\n    if (!cache->sh->cold || cache->sh->loading) {\n        return;\n    }\n\n    if (!ngx_atomic_cmp_set(&cache->sh->loading, 0, ngx_pid)) {\n        return;\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                   \"http file cache loader\");\n\n    tree.init_handler = NULL;\n    tree.file_handler = ngx_http_file_cache_manage_file;\n    tree.pre_tree_handler = ngx_http_file_cache_manage_directory;\n    tree.post_tree_handler = ngx_http_file_cache_noop;\n    tree.spec_handler = ngx_http_file_cache_delete_file;\n    tree.data = cache;\n    tree.alloc = 0;\n    tree.log = ngx_cycle->log;\n\n    cache->last = ngx_current_msec;\n    cache->files = 0;\n\n    if (ngx_walk_tree(&tree, &cache->path->name) == NGX_ABORT) {\n        cache->sh->loading = 0;\n        return;\n    }\n\n    cache->sh->cold = 0;\n    cache->sh->loading = 0;\n\n    ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0,\n                  \"http file cache: %V %.3fM, bsize: %uz\",\n                  &cache->path->name,\n                  ((double) cache->sh->size * cache->bsize) / (1024 * 1024),\n                  cache->bsize);\n}\n\n\nstatic ngx_int_t\nngx_http_file_cache_noop(ngx_tree_ctx_t *ctx, ngx_str_t *path)\n{\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_file_cache_manage_file(ngx_tree_ctx_t *ctx, ngx_str_t *path)\n{\n    ngx_msec_t              elapsed;\n    ngx_http_file_cache_t  *cache;\n\n    cache = ctx->data;\n\n    if (ngx_http_file_cache_add_file(ctx, path) != NGX_OK) {\n        (void) ngx_http_file_cache_delete_file(ctx, path);\n    }\n\n    if (++cache->files >= cache->loader_files) {\n        ngx_http_file_cache_loader_sleep(cache);\n\n    } else {\n        ngx_time_update();\n\n        elapsed = ngx_abs((ngx_msec_int_t) (ngx_current_msec - cache->last));\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                       \"http file cache loader time elapsed: %M\", elapsed);\n\n        if (elapsed >= cache->loader_threshold) {\n            ngx_http_file_cache_loader_sleep(cache);\n        }\n    }\n\n    return (ngx_quit || ngx_terminate) ? NGX_ABORT : NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_file_cache_manage_directory(ngx_tree_ctx_t *ctx, ngx_str_t *path)\n{\n    if (path->len >= 5\n        && ngx_strncmp(path->data + path->len - 5, \"/temp\", 5) == 0)\n    {\n        return NGX_DECLINED;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_file_cache_loader_sleep(ngx_http_file_cache_t *cache)\n{\n    ngx_msleep(cache->loader_sleep);\n\n    ngx_time_update();\n\n    cache->last = ngx_current_msec;\n    cache->files = 0;\n}\n\n\nstatic ngx_int_t\nngx_http_file_cache_add_file(ngx_tree_ctx_t *ctx, ngx_str_t *name)\n{\n    u_char                 *p;\n    ngx_int_t               n;\n    ngx_uint_t              i;\n    ngx_http_cache_t        c;\n    ngx_http_file_cache_t  *cache;\n\n    if (name->len < 2 * NGX_HTTP_CACHE_KEY_LEN) {\n        return NGX_ERROR;\n    }\n\n    /*\n     * Temporary files in cache have a suffix consisting of a dot\n     * followed by 10 digits.\n     */\n\n    if (name->len >= 2 * NGX_HTTP_CACHE_KEY_LEN + 1 + 10\n        && name->data[name->len - 10 - 1] == '.')\n    {\n        return NGX_OK;\n    }\n\n    if (ctx->size < (off_t) sizeof(ngx_http_file_cache_header_t)) {\n        ngx_log_error(NGX_LOG_CRIT, ctx->log, 0,\n                      \"cache file \\\"%s\\\" is too small\", name->data);\n        return NGX_ERROR;\n    }\n\n    ngx_memzero(&c, sizeof(ngx_http_cache_t));\n    cache = ctx->data;\n\n    c.length = ctx->size;\n    c.fs_size = (ctx->fs_size + cache->bsize - 1) / cache->bsize;\n\n    p = &name->data[name->len - 2 * NGX_HTTP_CACHE_KEY_LEN];\n\n    for (i = 0; i < NGX_HTTP_CACHE_KEY_LEN; i++) {\n        n = ngx_hextoi(p, 2);\n\n        if (n == NGX_ERROR) {\n            return NGX_ERROR;\n        }\n\n        p += 2;\n\n        c.key[i] = (u_char) n;\n    }\n\n    return ngx_http_file_cache_add(cache, &c);\n}\n\n\nstatic ngx_int_t\nngx_http_file_cache_add(ngx_http_file_cache_t *cache, ngx_http_cache_t *c)\n{\n    ngx_http_file_cache_node_t  *fcn;\n\n    ngx_shmtx_lock(&cache->shpool->mutex);\n\n    fcn = ngx_http_file_cache_lookup(cache, c->key);\n\n    if (fcn == NULL) {\n\n        fcn = ngx_slab_calloc_locked(cache->shpool,\n                                     sizeof(ngx_http_file_cache_node_t));\n        if (fcn == NULL) {\n            ngx_http_file_cache_set_watermark(cache);\n\n            if (cache->fail_time != ngx_time()) {\n                cache->fail_time = ngx_time();\n                ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,\n                           \"could not allocate node%s\", cache->shpool->log_ctx);\n            }\n\n            ngx_shmtx_unlock(&cache->shpool->mutex);\n            return NGX_ERROR;\n        }\n\n        cache->sh->count++;\n\n        ngx_memcpy((u_char *) &fcn->node.key, c->key, sizeof(ngx_rbtree_key_t));\n\n        ngx_memcpy(fcn->key, &c->key[sizeof(ngx_rbtree_key_t)],\n                   NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t));\n\n        ngx_rbtree_insert(&cache->sh->rbtree, &fcn->node);\n\n        fcn->uses = 1;\n        fcn->exists = 1;\n        fcn->fs_size = c->fs_size;\n\n        cache->sh->size += c->fs_size;\n\n    } else {\n        ngx_queue_remove(&fcn->queue);\n    }\n\n    fcn->expire = ngx_time() + cache->inactive;\n\n    ngx_queue_insert_head(&cache->sh->queue, &fcn->queue);\n\n    ngx_shmtx_unlock(&cache->shpool->mutex);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_file_cache_delete_file(ngx_tree_ctx_t *ctx, ngx_str_t *path)\n{\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ctx->log, 0,\n                   \"http file cache delete: \\\"%s\\\"\", path->data);\n\n    if (ngx_delete_file(path->data) == NGX_FILE_ERROR) {\n        ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno,\n                      ngx_delete_file_n \" \\\"%s\\\" failed\", path->data);\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_file_cache_set_watermark(ngx_http_file_cache_t *cache)\n{\n    cache->sh->watermark = cache->sh->count - cache->sh->count / 8;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,\n                   \"http file cache watermark: %ui\", cache->sh->watermark);\n}\n\n\ntime_t\nngx_http_file_cache_valid(ngx_array_t *cache_valid, ngx_uint_t status)\n{\n    ngx_uint_t               i;\n    ngx_http_cache_valid_t  *valid;\n\n    if (cache_valid == NULL) {\n        return 0;\n    }\n\n    valid = cache_valid->elts;\n    for (i = 0; i < cache_valid->nelts; i++) {\n\n        if (valid[i].status == 0) {\n            return valid[i].valid;\n        }\n\n        if (valid[i].status == status) {\n            return valid[i].valid;\n        }\n    }\n\n    return 0;\n}\n\n\nchar *\nngx_http_file_cache_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    char  *confp = conf;\n\n    off_t                   max_size, min_free;\n    u_char                 *last, *p;\n    time_t                  inactive;\n    ssize_t                 size;\n    ngx_str_t               s, name, *value;\n    ngx_int_t               loader_files, manager_files;\n    ngx_msec_t              loader_sleep, manager_sleep, loader_threshold,\n                            manager_threshold;\n    ngx_uint_t              i, n, use_temp_path;\n    ngx_array_t            *caches;\n    ngx_http_file_cache_t  *cache, **ce;\n\n    cache = ngx_pcalloc(cf->pool, sizeof(ngx_http_file_cache_t));\n    if (cache == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    cache->path = ngx_pcalloc(cf->pool, sizeof(ngx_path_t));\n    if (cache->path == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    use_temp_path = 1;\n\n    inactive = 600;\n\n    loader_files = 100;\n    loader_sleep = 50;\n    loader_threshold = 200;\n\n    manager_files = 100;\n    manager_sleep = 50;\n    manager_threshold = 200;\n\n    name.len = 0;\n    size = 0;\n    max_size = NGX_MAX_OFF_T_VALUE;\n    min_free = 0;\n\n    value = cf->args->elts;\n\n    cache->path->name = value[1];\n\n    if (cache->path->name.data[cache->path->name.len - 1] == '/') {\n        cache->path->name.len--;\n    }\n\n    if (ngx_conf_full_name(cf->cycle, &cache->path->name, 0) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    for (i = 2; i < cf->args->nelts; i++) {\n\n        if (ngx_strncmp(value[i].data, \"levels=\", 7) == 0) {\n\n            p = value[i].data + 7;\n            last = value[i].data + value[i].len;\n\n            for (n = 0; n < NGX_MAX_PATH_LEVEL && p < last; n++) {\n\n                if (*p > '0' && *p < '3') {\n\n                    cache->path->level[n] = *p++ - '0';\n                    cache->path->len += cache->path->level[n] + 1;\n\n                    if (p == last) {\n                        break;\n                    }\n\n                    if (*p++ == ':' && n < NGX_MAX_PATH_LEVEL - 1 && p < last) {\n                        continue;\n                    }\n\n                    goto invalid_levels;\n                }\n\n                goto invalid_levels;\n            }\n\n            if (cache->path->len < 10 + NGX_MAX_PATH_LEVEL) {\n                continue;\n            }\n\n        invalid_levels:\n\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"invalid \\\"levels\\\" \\\"%V\\\"\", &value[i]);\n            return NGX_CONF_ERROR;\n        }\n\n        if (ngx_strncmp(value[i].data, \"use_temp_path=\", 14) == 0) {\n\n            if (ngx_strcmp(&value[i].data[14], \"on\") == 0) {\n                use_temp_path = 1;\n\n            } else if (ngx_strcmp(&value[i].data[14], \"off\") == 0) {\n                use_temp_path = 0;\n\n            } else {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"invalid use_temp_path value \\\"%V\\\", \"\n                                   \"it must be \\\"on\\\" or \\\"off\\\"\",\n                                   &value[i]);\n                return NGX_CONF_ERROR;\n            }\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"keys_zone=\", 10) == 0) {\n\n            name.data = value[i].data + 10;\n\n            p = (u_char *) ngx_strchr(name.data, ':');\n\n            if (p == NULL) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"invalid keys zone size \\\"%V\\\"\", &value[i]);\n                return NGX_CONF_ERROR;\n            }\n\n            name.len = p - name.data;\n\n            s.data = p + 1;\n            s.len = value[i].data + value[i].len - s.data;\n\n            size = ngx_parse_size(&s);\n\n            if (size == NGX_ERROR) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"invalid keys zone size \\\"%V\\\"\", &value[i]);\n                return NGX_CONF_ERROR;\n            }\n\n            if (size < (ssize_t) (2 * ngx_pagesize)) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"keys zone \\\"%V\\\" is too small\", &value[i]);\n                return NGX_CONF_ERROR;\n            }\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"inactive=\", 9) == 0) {\n\n            s.len = value[i].len - 9;\n            s.data = value[i].data + 9;\n\n            inactive = ngx_parse_time(&s, 1);\n            if (inactive == (time_t) NGX_ERROR) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"invalid inactive value \\\"%V\\\"\", &value[i]);\n                return NGX_CONF_ERROR;\n            }\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"max_size=\", 9) == 0) {\n\n            s.len = value[i].len - 9;\n            s.data = value[i].data + 9;\n\n            max_size = ngx_parse_offset(&s);\n            if (max_size < 0) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"invalid max_size value \\\"%V\\\"\", &value[i]);\n                return NGX_CONF_ERROR;\n            }\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"min_free=\", 9) == 0) {\n\n#if (NGX_WIN32 || NGX_HAVE_STATFS || NGX_HAVE_STATVFS)\n\n            s.len = value[i].len - 9;\n            s.data = value[i].data + 9;\n\n            min_free = ngx_parse_offset(&s);\n            if (min_free < 0) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"invalid min_free value \\\"%V\\\"\", &value[i]);\n                return NGX_CONF_ERROR;\n            }\n\n#else\n            ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                               \"min_free is not supported \"\n                               \"on this platform, ignored\");\n#endif\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"loader_files=\", 13) == 0) {\n\n            loader_files = ngx_atoi(value[i].data + 13, value[i].len - 13);\n            if (loader_files == NGX_ERROR) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid loader_files value \\\"%V\\\"\", &value[i]);\n                return NGX_CONF_ERROR;\n            }\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"loader_sleep=\", 13) == 0) {\n\n            s.len = value[i].len - 13;\n            s.data = value[i].data + 13;\n\n            loader_sleep = ngx_parse_time(&s, 0);\n            if (loader_sleep == (ngx_msec_t) NGX_ERROR) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid loader_sleep value \\\"%V\\\"\", &value[i]);\n                return NGX_CONF_ERROR;\n            }\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"loader_threshold=\", 17) == 0) {\n\n            s.len = value[i].len - 17;\n            s.data = value[i].data + 17;\n\n            loader_threshold = ngx_parse_time(&s, 0);\n            if (loader_threshold == (ngx_msec_t) NGX_ERROR) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid loader_threshold value \\\"%V\\\"\", &value[i]);\n                return NGX_CONF_ERROR;\n            }\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"manager_files=\", 14) == 0) {\n\n            manager_files = ngx_atoi(value[i].data + 14, value[i].len - 14);\n            if (manager_files == NGX_ERROR) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid manager_files value \\\"%V\\\"\", &value[i]);\n                return NGX_CONF_ERROR;\n            }\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"manager_sleep=\", 14) == 0) {\n\n            s.len = value[i].len - 14;\n            s.data = value[i].data + 14;\n\n            manager_sleep = ngx_parse_time(&s, 0);\n            if (manager_sleep == (ngx_msec_t) NGX_ERROR) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid manager_sleep value \\\"%V\\\"\", &value[i]);\n                return NGX_CONF_ERROR;\n            }\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"manager_threshold=\", 18) == 0) {\n\n            s.len = value[i].len - 18;\n            s.data = value[i].data + 18;\n\n            manager_threshold = ngx_parse_time(&s, 0);\n            if (manager_threshold == (ngx_msec_t) NGX_ERROR) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid manager_threshold value \\\"%V\\\"\", &value[i]);\n                return NGX_CONF_ERROR;\n            }\n\n            continue;\n        }\n\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid parameter \\\"%V\\\"\", &value[i]);\n        return NGX_CONF_ERROR;\n    }\n\n    if (name.len == 0 || size == 0) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"\\\"%V\\\" must have \\\"keys_zone\\\" parameter\",\n                           &cmd->name);\n        return NGX_CONF_ERROR;\n    }\n\n    cache->path->manager = ngx_http_file_cache_manager;\n    cache->path->loader = ngx_http_file_cache_loader;\n    cache->path->data = cache;\n    cache->path->conf_file = cf->conf_file->file.name.data;\n    cache->path->line = cf->conf_file->line;\n    cache->loader_files = loader_files;\n    cache->loader_sleep = loader_sleep;\n    cache->loader_threshold = loader_threshold;\n    cache->manager_files = manager_files;\n    cache->manager_sleep = manager_sleep;\n    cache->manager_threshold = manager_threshold;\n\n    if (ngx_add_path(cf, &cache->path) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    cache->shm_zone = ngx_shared_memory_add(cf, &name, size, cmd->post);\n    if (cache->shm_zone == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (cache->shm_zone->data) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"duplicate zone \\\"%V\\\"\", &name);\n        return NGX_CONF_ERROR;\n    }\n\n\n    cache->shm_zone->init = ngx_http_file_cache_init;\n    cache->shm_zone->data = cache;\n\n    cache->use_temp_path = use_temp_path;\n\n    cache->inactive = inactive;\n    cache->max_size = max_size;\n    cache->min_free = min_free;\n\n    caches = (ngx_array_t *) (confp + cmd->offset);\n\n    ce = ngx_array_push(caches);\n    if (ce == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    *ce = cache;\n\n    return NGX_CONF_OK;\n}\n\n\nchar *\nngx_http_file_cache_valid_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n    char  *p = conf;\n\n    time_t                    valid;\n    ngx_str_t                *value;\n    ngx_int_t                 status;\n    ngx_uint_t                i, n;\n    ngx_array_t             **a;\n    ngx_http_cache_valid_t   *v;\n    static ngx_uint_t         statuses[] = { 200, 301, 302 };\n\n    a = (ngx_array_t **) (p + cmd->offset);\n\n    if (*a == NGX_CONF_UNSET_PTR) {\n        *a = ngx_array_create(cf->pool, 1, sizeof(ngx_http_cache_valid_t));\n        if (*a == NULL) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    value = cf->args->elts;\n    n = cf->args->nelts - 1;\n\n    valid = ngx_parse_time(&value[n], 1);\n    if (valid == (time_t) NGX_ERROR) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid time value \\\"%V\\\"\", &value[n]);\n        return NGX_CONF_ERROR;\n    }\n\n    if (n == 1) {\n\n        for (i = 0; i < 3; i++) {\n            v = ngx_array_push(*a);\n            if (v == NULL) {\n                return NGX_CONF_ERROR;\n            }\n\n            v->status = statuses[i];\n            v->valid = valid;\n        }\n\n        return NGX_CONF_OK;\n    }\n\n    for (i = 1; i < n; i++) {\n\n        if (ngx_strcmp(value[i].data, \"any\") == 0) {\n\n            status = 0;\n\n        } else {\n\n            status = ngx_atoi(value[i].data, value[i].len);\n            if (status < 100 || status > 599) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"invalid status \\\"%V\\\"\", &value[i]);\n                return NGX_CONF_ERROR;\n            }\n        }\n\n        v = ngx_array_push(*a);\n        if (v == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        v->status = status;\n        v->valid = valid;\n    }\n\n    return NGX_CONF_OK;\n}\n"
  },
  {
    "path": "src/http/ngx_http_header_filter_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n#include <nginx.h>\n\n\nstatic ngx_int_t ngx_http_header_filter_init(ngx_conf_t *cf);\nstatic ngx_int_t ngx_http_header_filter(ngx_http_request_t *r);\n\n\nstatic ngx_http_module_t  ngx_http_header_filter_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    ngx_http_header_filter_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\nngx_module_t  ngx_http_header_filter_module = {\n    NGX_MODULE_V1,\n    &ngx_http_header_filter_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#if (T_NGX_SERVER_INFO)\nstatic u_char ngx_http_server_string[] = \"Server: \" TENGINE CRLF;\nstatic u_char ngx_http_server_full_string[] = \"Server: \" TENGINE_VER CRLF;\n#else\nstatic u_char ngx_http_server_string[] = \"Server: nginx\" CRLF;\nstatic u_char ngx_http_server_full_string[] = \"Server: \" NGINX_VER CRLF;\nstatic u_char ngx_http_server_build_string[] = \"Server: \" NGINX_VER_BUILD CRLF;\n#endif\n\n\nstatic ngx_str_t ngx_http_status_lines[] = {\n\n    ngx_string(\"200 OK\"),\n    ngx_string(\"201 Created\"),\n    ngx_string(\"202 Accepted\"),\n    ngx_null_string,  /* \"203 Non-Authoritative Information\" */\n    ngx_string(\"204 No Content\"),\n    ngx_null_string,  /* \"205 Reset Content\" */\n    ngx_string(\"206 Partial Content\"),\n\n    /* ngx_null_string, */  /* \"207 Multi-Status\" */\n\n#define NGX_HTTP_LAST_2XX  207\n#define NGX_HTTP_OFF_3XX   (NGX_HTTP_LAST_2XX - 200)\n\n    /* ngx_null_string, */  /* \"300 Multiple Choices\" */\n\n    ngx_string(\"301 Moved Permanently\"),\n    ngx_string(\"302 Moved Temporarily\"),\n    ngx_string(\"303 See Other\"),\n    ngx_string(\"304 Not Modified\"),\n    ngx_null_string,  /* \"305 Use Proxy\" */\n    ngx_null_string,  /* \"306 unused\" */\n    ngx_string(\"307 Temporary Redirect\"),\n    ngx_string(\"308 Permanent Redirect\"),\n\n#define NGX_HTTP_LAST_3XX  309\n#define NGX_HTTP_OFF_4XX   (NGX_HTTP_LAST_3XX - 301 + NGX_HTTP_OFF_3XX)\n\n    ngx_string(\"400 Bad Request\"),\n    ngx_string(\"401 Unauthorized\"),\n    ngx_string(\"402 Payment Required\"),\n    ngx_string(\"403 Forbidden\"),\n    ngx_string(\"404 Not Found\"),\n    ngx_string(\"405 Not Allowed\"),\n    ngx_string(\"406 Not Acceptable\"),\n    ngx_null_string,  /* \"407 Proxy Authentication Required\" */\n    ngx_string(\"408 Request Time-out\"),\n    ngx_string(\"409 Conflict\"),\n    ngx_string(\"410 Gone\"),\n    ngx_string(\"411 Length Required\"),\n    ngx_string(\"412 Precondition Failed\"),\n    ngx_string(\"413 Request Entity Too Large\"),\n    ngx_string(\"414 Request-URI Too Large\"),\n    ngx_string(\"415 Unsupported Media Type\"),\n    ngx_string(\"416 Requested Range Not Satisfiable\"),\n    ngx_null_string,  /* \"417 Expectation Failed\" */\n    ngx_null_string,  /* \"418 unused\" */\n    ngx_null_string,  /* \"419 unused\" */\n    ngx_null_string,  /* \"420 unused\" */\n    ngx_string(\"421 Misdirected Request\"),\n    ngx_null_string,  /* \"422 Unprocessable Entity\" */\n    ngx_null_string,  /* \"423 Locked\" */\n    ngx_null_string,  /* \"424 Failed Dependency\" */\n    ngx_null_string,  /* \"425 unused\" */\n    ngx_null_string,  /* \"426 Upgrade Required\" */\n    ngx_null_string,  /* \"427 unused\" */\n    ngx_null_string,  /* \"428 Precondition Required\" */\n    ngx_string(\"429 Too Many Requests\"),\n\n#define NGX_HTTP_LAST_4XX  430\n#define NGX_HTTP_OFF_5XX   (NGX_HTTP_LAST_4XX - 400 + NGX_HTTP_OFF_4XX)\n\n    ngx_string(\"500 Internal Server Error\"),\n    ngx_string(\"501 Not Implemented\"),\n    ngx_string(\"502 Bad Gateway\"),\n    ngx_string(\"503 Service Temporarily Unavailable\"),\n    ngx_string(\"504 Gateway Time-out\"),\n    ngx_string(\"505 HTTP Version Not Supported\"),\n    ngx_null_string,        /* \"506 Variant Also Negotiates\" */\n    ngx_string(\"507 Insufficient Storage\"),\n\n    /* ngx_null_string, */  /* \"508 unused\" */\n    /* ngx_null_string, */  /* \"509 unused\" */\n    /* ngx_null_string, */  /* \"510 Not Extended\" */\n\n#define NGX_HTTP_LAST_5XX  508\n\n};\n\n\nngx_http_header_out_t  ngx_http_headers_out[] = {\n    { ngx_string(\"Server\"), offsetof(ngx_http_headers_out_t, server) },\n    { ngx_string(\"Date\"), offsetof(ngx_http_headers_out_t, date) },\n    { ngx_string(\"Content-Length\"),\n                 offsetof(ngx_http_headers_out_t, content_length) },\n    { ngx_string(\"Content-Encoding\"),\n                 offsetof(ngx_http_headers_out_t, content_encoding) },\n    { ngx_string(\"Location\"), offsetof(ngx_http_headers_out_t, location) },\n    { ngx_string(\"Last-Modified\"),\n                 offsetof(ngx_http_headers_out_t, last_modified) },\n    { ngx_string(\"Accept-Ranges\"),\n                 offsetof(ngx_http_headers_out_t, accept_ranges) },\n    { ngx_string(\"Expires\"), offsetof(ngx_http_headers_out_t, expires) },\n    { ngx_string(\"Cache-Control\"),\n                 offsetof(ngx_http_headers_out_t, cache_control) },\n    { ngx_string(\"ETag\"), offsetof(ngx_http_headers_out_t, etag) },\n\n    { ngx_null_string, 0 }\n};\n\n\nstatic ngx_int_t\nngx_http_header_filter(ngx_http_request_t *r)\n{\n    u_char                    *p;\n    size_t                     len;\n    ngx_str_t                  host, *status_line;\n    ngx_buf_t                 *b;\n    ngx_uint_t                 status, i, port;\n    ngx_chain_t                out;\n    ngx_list_part_t           *part;\n    ngx_table_elt_t           *header;\n    ngx_connection_t          *c;\n    ngx_http_core_loc_conf_t  *clcf;\n    ngx_http_core_srv_conf_t  *cscf;\n    u_char                     addr[NGX_SOCKADDR_STRLEN];\n\n    if (r->header_sent) {\n        return NGX_OK;\n    }\n\n    r->header_sent = 1;\n\n    if (r != r->main) {\n        return NGX_OK;\n    }\n\n    if (r->http_version < NGX_HTTP_VERSION_10) {\n        return NGX_OK;\n    }\n\n    if (r->method == NGX_HTTP_HEAD) {\n        r->header_only = 1;\n    }\n\n    if (r->headers_out.last_modified_time != -1) {\n        if (r->headers_out.status != NGX_HTTP_OK\n            && r->headers_out.status != NGX_HTTP_PARTIAL_CONTENT\n            && r->headers_out.status != NGX_HTTP_NOT_MODIFIED)\n        {\n            r->headers_out.last_modified_time = -1;\n            r->headers_out.last_modified = NULL;\n        }\n    }\n\n    if (r->keepalive && (ngx_terminate || ngx_exiting)) {\n        r->keepalive = 0;\n    }\n\n    len = sizeof(\"HTTP/1.x \") - 1 + sizeof(CRLF) - 1\n          /* the end of the header */\n          + sizeof(CRLF) - 1;\n\n    /* status line */\n\n    if (r->headers_out.status_line.len) {\n        len += r->headers_out.status_line.len;\n        status_line = &r->headers_out.status_line;\n#if (NGX_SUPPRESS_WARN)\n        status = 0;\n#endif\n\n    } else {\n\n        status = r->headers_out.status;\n\n        if (status >= NGX_HTTP_OK\n            && status < NGX_HTTP_LAST_2XX)\n        {\n            /* 2XX */\n\n            if (status == NGX_HTTP_NO_CONTENT) {\n                r->header_only = 1;\n                ngx_str_null(&r->headers_out.content_type);\n                r->headers_out.last_modified_time = -1;\n                r->headers_out.last_modified = NULL;\n                r->headers_out.content_length = NULL;\n                r->headers_out.content_length_n = -1;\n            }\n\n            status -= NGX_HTTP_OK;\n            status_line = &ngx_http_status_lines[status];\n            len += ngx_http_status_lines[status].len;\n\n        } else if (status >= NGX_HTTP_MOVED_PERMANENTLY\n                   && status < NGX_HTTP_LAST_3XX)\n        {\n            /* 3XX */\n\n            if (status == NGX_HTTP_NOT_MODIFIED) {\n                r->header_only = 1;\n            }\n\n            status = status - NGX_HTTP_MOVED_PERMANENTLY + NGX_HTTP_OFF_3XX;\n            status_line = &ngx_http_status_lines[status];\n            len += ngx_http_status_lines[status].len;\n\n        } else if (status >= NGX_HTTP_BAD_REQUEST\n                   && status < NGX_HTTP_LAST_4XX)\n        {\n            /* 4XX */\n            status = status - NGX_HTTP_BAD_REQUEST\n                            + NGX_HTTP_OFF_4XX;\n\n            status_line = &ngx_http_status_lines[status];\n            len += ngx_http_status_lines[status].len;\n\n        } else if (status >= NGX_HTTP_INTERNAL_SERVER_ERROR\n                   && status < NGX_HTTP_LAST_5XX)\n        {\n            /* 5XX */\n            status = status - NGX_HTTP_INTERNAL_SERVER_ERROR\n                            + NGX_HTTP_OFF_5XX;\n\n            status_line = &ngx_http_status_lines[status];\n            len += ngx_http_status_lines[status].len;\n\n        } else {\n            len += NGX_INT_T_LEN + 1 /* SP */;\n            status_line = NULL;\n        }\n\n        if (status_line && status_line->len == 0) {\n            status = r->headers_out.status;\n            len += NGX_INT_T_LEN + 1 /* SP */;\n            status_line = NULL;\n        }\n    }\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    if (r->headers_out.server == NULL) {\n#if (T_NGX_SERVER_INFO)\n        if (clcf->server_tag_type == NGX_HTTP_SERVER_TAG_ON) {\n            len += clcf->server_tokens ? sizeof(ngx_http_server_full_string) - 1: \n                                         sizeof(ngx_http_server_string) - 1;\n\n        } else if (clcf->server_tag_type == NGX_HTTP_SERVER_TAG_CUSTOMIZED) {\n            len += clcf->server_tag_header.len;\n        }\n\n#else\n        if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) {\n            len += sizeof(ngx_http_server_full_string) - 1;\n\n        } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) {\n            len += sizeof(ngx_http_server_build_string) - 1;\n\n        } else {\n            len += sizeof(ngx_http_server_string) - 1;\n        }\n#endif\n    }\n\n    if (r->headers_out.date == NULL) {\n        len += sizeof(\"Date: Mon, 28 Sep 1970 06:00:00 GMT\" CRLF) - 1;\n    }\n\n    if (r->headers_out.content_type.len) {\n        len += sizeof(\"Content-Type: \") - 1\n               + r->headers_out.content_type.len + 2;\n\n        if (r->headers_out.content_type_len == r->headers_out.content_type.len\n            && r->headers_out.charset.len)\n        {\n            len += sizeof(\"; charset=\") - 1 + r->headers_out.charset.len;\n        }\n    }\n\n    if (r->headers_out.content_length == NULL\n        && r->headers_out.content_length_n >= 0)\n    {\n        len += sizeof(\"Content-Length: \") - 1 + NGX_OFF_T_LEN + 2;\n    }\n\n    if (r->headers_out.last_modified == NULL\n        && r->headers_out.last_modified_time != -1)\n    {\n        len += sizeof(\"Last-Modified: Mon, 28 Sep 1970 06:00:00 GMT\" CRLF) - 1;\n    }\n\n    c = r->connection;\n\n    if (r->headers_out.location\n        && r->headers_out.location->value.len\n        && r->headers_out.location->value.data[0] == '/'\n        && clcf->absolute_redirect)\n    {\n        r->headers_out.location->hash = 0;\n\n        if (clcf->server_name_in_redirect) {\n            cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);\n            host = cscf->server_name;\n\n        } else if (r->headers_in.server.len) {\n            host = r->headers_in.server;\n\n        } else {\n            host.len = NGX_SOCKADDR_STRLEN;\n            host.data = addr;\n\n            if (ngx_connection_local_sockaddr(c, &host, 0) != NGX_OK) {\n                return NGX_ERROR;\n            }\n        }\n\n        port = ngx_inet_get_port(c->local_sockaddr);\n\n        len += sizeof(\"Location: https://\") - 1\n               + host.len\n               + r->headers_out.location->value.len + 2;\n\n        if (clcf->port_in_redirect) {\n\n#if (NGX_HTTP_SSL)\n            if (c->ssl)\n                port = (port == 443) ? 0 : port;\n            else\n#endif\n                port = (port == 80) ? 0 : port;\n\n        } else {\n            port = 0;\n        }\n\n        if (port) {\n            len += sizeof(\":65535\") - 1;\n        }\n\n    } else {\n        ngx_str_null(&host);\n        port = 0;\n    }\n\n    if (r->chunked) {\n        len += sizeof(\"Transfer-Encoding: chunked\" CRLF) - 1;\n    }\n\n    if (r->headers_out.status == NGX_HTTP_SWITCHING_PROTOCOLS) {\n        len += sizeof(\"Connection: upgrade\" CRLF) - 1;\n\n    } else if (r->keepalive) {\n        len += sizeof(\"Connection: keep-alive\" CRLF) - 1;\n\n        /*\n         * MSIE and Opera ignore the \"Keep-Alive: timeout=<N>\" header.\n         * MSIE keeps the connection alive for about 60-65 seconds.\n         * Opera keeps the connection alive very long.\n         * Mozilla keeps the connection alive for N plus about 1-10 seconds.\n         * Konqueror keeps the connection alive for about N seconds.\n         */\n\n        if (clcf->keepalive_header) {\n            len += sizeof(\"Keep-Alive: timeout=\") - 1 + NGX_TIME_T_LEN + 2;\n        }\n\n    } else {\n        len += sizeof(\"Connection: close\" CRLF) - 1;\n    }\n\n#if (NGX_HTTP_GZIP)\n    if (r->gzip_vary) {\n        if (clcf->gzip_vary) {\n            len += sizeof(\"Vary: Accept-Encoding\" CRLF) - 1;\n\n        } else {\n            r->gzip_vary = 0;\n        }\n    }\n#endif\n\n    part = &r->headers_out.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 (header[i].hash == 0) {\n            continue;\n        }\n\n        len += header[i].key.len + sizeof(\": \") - 1 + header[i].value.len\n               + sizeof(CRLF) - 1;\n    }\n\n    b = ngx_create_temp_buf(r->pool, len);\n    if (b == NULL) {\n        return NGX_ERROR;\n    }\n\n    /* \"HTTP/1.x \" */\n    b->last = ngx_cpymem(b->last, \"HTTP/1.1 \", sizeof(\"HTTP/1.x \") - 1);\n\n    /* status line */\n    if (status_line) {\n        b->last = ngx_copy(b->last, status_line->data, status_line->len);\n\n    } else {\n        b->last = ngx_sprintf(b->last, \"%03ui \", status);\n    }\n    *b->last++ = CR; *b->last++ = LF;\n\n    if (r->headers_out.server == NULL) {\n#if (T_NGX_SERVER_INFO)\n        if (clcf->server_tag_type == NGX_HTTP_SERVER_TAG_ON) {\n            if (clcf->server_tokens) {\n                p = (u_char *) ngx_http_server_full_string;\n                len = sizeof(ngx_http_server_full_string) - 1;\n\n            } else {\n                p = (u_char *) ngx_http_server_string;\n                len = sizeof(ngx_http_server_string) - 1;\n            }\n\n            b->last = ngx_cpymem(b->last, p, len);\n\n        } else if (clcf->server_tag_type == NGX_HTTP_SERVER_TAG_CUSTOMIZED) {\n            p = clcf->server_tag_header.data;\n            len = clcf->server_tag_header.len;\n            b->last = ngx_cpymem(b->last, p, len);\n        }\n\n#else\n        if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) {\n            p = ngx_http_server_full_string;\n            len = sizeof(ngx_http_server_full_string) - 1;\n\n        } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) {\n            p = ngx_http_server_build_string;\n            len = sizeof(ngx_http_server_build_string) - 1;\n\n        } else {\n            p = ngx_http_server_string;\n            len = sizeof(ngx_http_server_string) - 1;\n        }\n\n        b->last = ngx_cpymem(b->last, p, len);\n#endif\n    }\n\n    if (r->headers_out.date == NULL) {\n        b->last = ngx_cpymem(b->last, \"Date: \", sizeof(\"Date: \") - 1);\n        b->last = ngx_cpymem(b->last, ngx_cached_http_time.data,\n                             ngx_cached_http_time.len);\n\n        *b->last++ = CR; *b->last++ = LF;\n    }\n\n    if (r->headers_out.content_type.len) {\n        b->last = ngx_cpymem(b->last, \"Content-Type: \",\n                             sizeof(\"Content-Type: \") - 1);\n        p = b->last;\n        b->last = ngx_copy(b->last, r->headers_out.content_type.data,\n                           r->headers_out.content_type.len);\n\n        if (r->headers_out.content_type_len == r->headers_out.content_type.len\n            && r->headers_out.charset.len)\n        {\n            b->last = ngx_cpymem(b->last, \"; charset=\",\n                                 sizeof(\"; charset=\") - 1);\n            b->last = ngx_copy(b->last, r->headers_out.charset.data,\n                               r->headers_out.charset.len);\n\n            /* update r->headers_out.content_type for possible logging */\n\n            r->headers_out.content_type.len = b->last - p;\n            r->headers_out.content_type.data = p;\n        }\n\n        *b->last++ = CR; *b->last++ = LF;\n    }\n\n    if (r->headers_out.content_length == NULL\n        && r->headers_out.content_length_n >= 0)\n    {\n        b->last = ngx_sprintf(b->last, \"Content-Length: %O\" CRLF,\n                              r->headers_out.content_length_n);\n    }\n\n    if (r->headers_out.last_modified == NULL\n        && r->headers_out.last_modified_time != -1)\n    {\n        b->last = ngx_cpymem(b->last, \"Last-Modified: \",\n                             sizeof(\"Last-Modified: \") - 1);\n        b->last = ngx_http_time(b->last, r->headers_out.last_modified_time);\n\n        *b->last++ = CR; *b->last++ = LF;\n    }\n\n    if (host.data) {\n\n        p = b->last + sizeof(\"Location: \") - 1;\n\n        b->last = ngx_cpymem(b->last, \"Location: http\",\n                             sizeof(\"Location: http\") - 1);\n\n#if (NGX_HTTP_SSL)\n        if (c->ssl) {\n            *b->last++ ='s';\n        }\n#endif\n\n        *b->last++ = ':'; *b->last++ = '/'; *b->last++ = '/';\n        b->last = ngx_copy(b->last, host.data, host.len);\n\n        if (port) {\n            b->last = ngx_sprintf(b->last, \":%ui\", port);\n        }\n\n        b->last = ngx_copy(b->last, r->headers_out.location->value.data,\n                           r->headers_out.location->value.len);\n\n        /* update r->headers_out.location->value for possible logging */\n\n        r->headers_out.location->value.len = b->last - p;\n        r->headers_out.location->value.data = p;\n        ngx_str_set(&r->headers_out.location->key, \"Location\");\n\n        *b->last++ = CR; *b->last++ = LF;\n    }\n\n    if (r->chunked) {\n        b->last = ngx_cpymem(b->last, \"Transfer-Encoding: chunked\" CRLF,\n                             sizeof(\"Transfer-Encoding: chunked\" CRLF) - 1);\n    }\n\n    if (r->headers_out.status == NGX_HTTP_SWITCHING_PROTOCOLS) {\n        b->last = ngx_cpymem(b->last, \"Connection: upgrade\" CRLF,\n                             sizeof(\"Connection: upgrade\" CRLF) - 1);\n\n    } else if (r->keepalive) {\n        b->last = ngx_cpymem(b->last, \"Connection: keep-alive\" CRLF,\n                             sizeof(\"Connection: keep-alive\" CRLF) - 1);\n\n        if (clcf->keepalive_header) {\n            b->last = ngx_sprintf(b->last, \"Keep-Alive: timeout=%T\" CRLF,\n                                  clcf->keepalive_header);\n        }\n\n    } else {\n        b->last = ngx_cpymem(b->last, \"Connection: close\" CRLF,\n                             sizeof(\"Connection: close\" CRLF) - 1);\n    }\n\n#if (NGX_HTTP_GZIP)\n    if (r->gzip_vary) {\n        b->last = ngx_cpymem(b->last, \"Vary: Accept-Encoding\" CRLF,\n                             sizeof(\"Vary: Accept-Encoding\" CRLF) - 1);\n    }\n#endif\n\n    part = &r->headers_out.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 (header[i].hash == 0) {\n            continue;\n        }\n\n        b->last = ngx_copy(b->last, header[i].key.data, header[i].key.len);\n        *b->last++ = ':'; *b->last++ = ' ';\n\n        b->last = ngx_copy(b->last, header[i].value.data, header[i].value.len);\n        *b->last++ = CR; *b->last++ = LF;\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"%*s\", (size_t) (b->last - b->pos), b->pos);\n\n    /* the end of HTTP header */\n    *b->last++ = CR; *b->last++ = LF;\n\n    r->header_size = b->last - b->pos;\n\n    if (r->header_only) {\n        b->last_buf = 1;\n    }\n\n    out.buf = b;\n    out.next = NULL;\n\n    return ngx_http_write_filter(r, &out);\n}\n\n\nstatic ngx_int_t\nngx_http_header_filter_init(ngx_conf_t *cf)\n{\n    ngx_http_top_header_filter = ngx_http_header_filter;\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "src/http/ngx_http_huff_decode.c",
    "content": "\n/*\n * Copyright (C) Nginx, Inc.\n * Copyright (C) Valentin V. Bartenev\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\ntypedef struct {\n    u_char  next;\n    u_char  emit;\n    u_char  sym;\n    u_char  ending;\n} ngx_http_huff_decode_code_t;\n\n\nstatic ngx_inline ngx_int_t ngx_http_huff_decode_bits(u_char *state,\n    u_char *ending, ngx_uint_t bits, u_char **dst);\n\n\nstatic ngx_http_huff_decode_code_t  ngx_http_huff_decode_codes[256][16] =\n{\n    /* 0 */\n    {\n        {0x04, 0x00, 0x00, 0x00}, {0x05, 0x00, 0x00, 0x00},\n        {0x07, 0x00, 0x00, 0x00}, {0x08, 0x00, 0x00, 0x00},\n        {0x0b, 0x00, 0x00, 0x00}, {0x0c, 0x00, 0x00, 0x00},\n        {0x10, 0x00, 0x00, 0x00}, {0x13, 0x00, 0x00, 0x00},\n        {0x19, 0x00, 0x00, 0x00}, {0x1c, 0x00, 0x00, 0x00},\n        {0x20, 0x00, 0x00, 0x00}, {0x23, 0x00, 0x00, 0x00},\n        {0x2a, 0x00, 0x00, 0x00}, {0x31, 0x00, 0x00, 0x00},\n        {0x39, 0x00, 0x00, 0x00}, {0x40, 0x00, 0x00, 0x01}\n    },\n    {\n        {0x00, 0x01, 0x30, 0x01}, {0x00, 0x01, 0x31, 0x01},\n        {0x00, 0x01, 0x32, 0x01}, {0x00, 0x01, 0x61, 0x01},\n        {0x00, 0x01, 0x63, 0x01}, {0x00, 0x01, 0x65, 0x01},\n        {0x00, 0x01, 0x69, 0x01}, {0x00, 0x01, 0x6f, 0x01},\n        {0x00, 0x01, 0x73, 0x01}, {0x00, 0x01, 0x74, 0x01},\n        {0x0d, 0x00, 0x00, 0x00}, {0x0e, 0x00, 0x00, 0x00},\n        {0x11, 0x00, 0x00, 0x00}, {0x12, 0x00, 0x00, 0x00},\n        {0x14, 0x00, 0x00, 0x00}, {0x15, 0x00, 0x00, 0x00}\n    },\n    {\n        {0x01, 0x01, 0x30, 0x00}, {0x16, 0x01, 0x30, 0x01},\n        {0x01, 0x01, 0x31, 0x00}, {0x16, 0x01, 0x31, 0x01},\n        {0x01, 0x01, 0x32, 0x00}, {0x16, 0x01, 0x32, 0x01},\n        {0x01, 0x01, 0x61, 0x00}, {0x16, 0x01, 0x61, 0x01},\n        {0x01, 0x01, 0x63, 0x00}, {0x16, 0x01, 0x63, 0x01},\n        {0x01, 0x01, 0x65, 0x00}, {0x16, 0x01, 0x65, 0x01},\n        {0x01, 0x01, 0x69, 0x00}, {0x16, 0x01, 0x69, 0x01},\n        {0x01, 0x01, 0x6f, 0x00}, {0x16, 0x01, 0x6f, 0x01}\n    },\n    {\n        {0x02, 0x01, 0x30, 0x00}, {0x09, 0x01, 0x30, 0x00},\n        {0x17, 0x01, 0x30, 0x00}, {0x28, 0x01, 0x30, 0x01},\n        {0x02, 0x01, 0x31, 0x00}, {0x09, 0x01, 0x31, 0x00},\n        {0x17, 0x01, 0x31, 0x00}, {0x28, 0x01, 0x31, 0x01},\n        {0x02, 0x01, 0x32, 0x00}, {0x09, 0x01, 0x32, 0x00},\n        {0x17, 0x01, 0x32, 0x00}, {0x28, 0x01, 0x32, 0x01},\n        {0x02, 0x01, 0x61, 0x00}, {0x09, 0x01, 0x61, 0x00},\n        {0x17, 0x01, 0x61, 0x00}, {0x28, 0x01, 0x61, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x30, 0x00}, {0x06, 0x01, 0x30, 0x00},\n        {0x0a, 0x01, 0x30, 0x00}, {0x0f, 0x01, 0x30, 0x00},\n        {0x18, 0x01, 0x30, 0x00}, {0x1f, 0x01, 0x30, 0x00},\n        {0x29, 0x01, 0x30, 0x00}, {0x38, 0x01, 0x30, 0x01},\n        {0x03, 0x01, 0x31, 0x00}, {0x06, 0x01, 0x31, 0x00},\n        {0x0a, 0x01, 0x31, 0x00}, {0x0f, 0x01, 0x31, 0x00},\n        {0x18, 0x01, 0x31, 0x00}, {0x1f, 0x01, 0x31, 0x00},\n        {0x29, 0x01, 0x31, 0x00}, {0x38, 0x01, 0x31, 0x01}\n    },\n    /* 5 */\n    {\n        {0x03, 0x01, 0x32, 0x00}, {0x06, 0x01, 0x32, 0x00},\n        {0x0a, 0x01, 0x32, 0x00}, {0x0f, 0x01, 0x32, 0x00},\n        {0x18, 0x01, 0x32, 0x00}, {0x1f, 0x01, 0x32, 0x00},\n        {0x29, 0x01, 0x32, 0x00}, {0x38, 0x01, 0x32, 0x01},\n        {0x03, 0x01, 0x61, 0x00}, {0x06, 0x01, 0x61, 0x00},\n        {0x0a, 0x01, 0x61, 0x00}, {0x0f, 0x01, 0x61, 0x00},\n        {0x18, 0x01, 0x61, 0x00}, {0x1f, 0x01, 0x61, 0x00},\n        {0x29, 0x01, 0x61, 0x00}, {0x38, 0x01, 0x61, 0x01}\n    },\n    {\n        {0x02, 0x01, 0x63, 0x00}, {0x09, 0x01, 0x63, 0x00},\n        {0x17, 0x01, 0x63, 0x00}, {0x28, 0x01, 0x63, 0x01},\n        {0x02, 0x01, 0x65, 0x00}, {0x09, 0x01, 0x65, 0x00},\n        {0x17, 0x01, 0x65, 0x00}, {0x28, 0x01, 0x65, 0x01},\n        {0x02, 0x01, 0x69, 0x00}, {0x09, 0x01, 0x69, 0x00},\n        {0x17, 0x01, 0x69, 0x00}, {0x28, 0x01, 0x69, 0x01},\n        {0x02, 0x01, 0x6f, 0x00}, {0x09, 0x01, 0x6f, 0x00},\n        {0x17, 0x01, 0x6f, 0x00}, {0x28, 0x01, 0x6f, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x63, 0x00}, {0x06, 0x01, 0x63, 0x00},\n        {0x0a, 0x01, 0x63, 0x00}, {0x0f, 0x01, 0x63, 0x00},\n        {0x18, 0x01, 0x63, 0x00}, {0x1f, 0x01, 0x63, 0x00},\n        {0x29, 0x01, 0x63, 0x00}, {0x38, 0x01, 0x63, 0x01},\n        {0x03, 0x01, 0x65, 0x00}, {0x06, 0x01, 0x65, 0x00},\n        {0x0a, 0x01, 0x65, 0x00}, {0x0f, 0x01, 0x65, 0x00},\n        {0x18, 0x01, 0x65, 0x00}, {0x1f, 0x01, 0x65, 0x00},\n        {0x29, 0x01, 0x65, 0x00}, {0x38, 0x01, 0x65, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x69, 0x00}, {0x06, 0x01, 0x69, 0x00},\n        {0x0a, 0x01, 0x69, 0x00}, {0x0f, 0x01, 0x69, 0x00},\n        {0x18, 0x01, 0x69, 0x00}, {0x1f, 0x01, 0x69, 0x00},\n        {0x29, 0x01, 0x69, 0x00}, {0x38, 0x01, 0x69, 0x01},\n        {0x03, 0x01, 0x6f, 0x00}, {0x06, 0x01, 0x6f, 0x00},\n        {0x0a, 0x01, 0x6f, 0x00}, {0x0f, 0x01, 0x6f, 0x00},\n        {0x18, 0x01, 0x6f, 0x00}, {0x1f, 0x01, 0x6f, 0x00},\n        {0x29, 0x01, 0x6f, 0x00}, {0x38, 0x01, 0x6f, 0x01}\n    },\n    {\n        {0x01, 0x01, 0x73, 0x00}, {0x16, 0x01, 0x73, 0x01},\n        {0x01, 0x01, 0x74, 0x00}, {0x16, 0x01, 0x74, 0x01},\n        {0x00, 0x01, 0x20, 0x01}, {0x00, 0x01, 0x25, 0x01},\n        {0x00, 0x01, 0x2d, 0x01}, {0x00, 0x01, 0x2e, 0x01},\n        {0x00, 0x01, 0x2f, 0x01}, {0x00, 0x01, 0x33, 0x01},\n        {0x00, 0x01, 0x34, 0x01}, {0x00, 0x01, 0x35, 0x01},\n        {0x00, 0x01, 0x36, 0x01}, {0x00, 0x01, 0x37, 0x01},\n        {0x00, 0x01, 0x38, 0x01}, {0x00, 0x01, 0x39, 0x01}\n    },\n    /* 10 */\n    {\n        {0x02, 0x01, 0x73, 0x00}, {0x09, 0x01, 0x73, 0x00},\n        {0x17, 0x01, 0x73, 0x00}, {0x28, 0x01, 0x73, 0x01},\n        {0x02, 0x01, 0x74, 0x00}, {0x09, 0x01, 0x74, 0x00},\n        {0x17, 0x01, 0x74, 0x00}, {0x28, 0x01, 0x74, 0x01},\n        {0x01, 0x01, 0x20, 0x00}, {0x16, 0x01, 0x20, 0x01},\n        {0x01, 0x01, 0x25, 0x00}, {0x16, 0x01, 0x25, 0x01},\n        {0x01, 0x01, 0x2d, 0x00}, {0x16, 0x01, 0x2d, 0x01},\n        {0x01, 0x01, 0x2e, 0x00}, {0x16, 0x01, 0x2e, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x73, 0x00}, {0x06, 0x01, 0x73, 0x00},\n        {0x0a, 0x01, 0x73, 0x00}, {0x0f, 0x01, 0x73, 0x00},\n        {0x18, 0x01, 0x73, 0x00}, {0x1f, 0x01, 0x73, 0x00},\n        {0x29, 0x01, 0x73, 0x00}, {0x38, 0x01, 0x73, 0x01},\n        {0x03, 0x01, 0x74, 0x00}, {0x06, 0x01, 0x74, 0x00},\n        {0x0a, 0x01, 0x74, 0x00}, {0x0f, 0x01, 0x74, 0x00},\n        {0x18, 0x01, 0x74, 0x00}, {0x1f, 0x01, 0x74, 0x00},\n        {0x29, 0x01, 0x74, 0x00}, {0x38, 0x01, 0x74, 0x01}\n    },\n    {\n        {0x02, 0x01, 0x20, 0x00}, {0x09, 0x01, 0x20, 0x00},\n        {0x17, 0x01, 0x20, 0x00}, {0x28, 0x01, 0x20, 0x01},\n        {0x02, 0x01, 0x25, 0x00}, {0x09, 0x01, 0x25, 0x00},\n        {0x17, 0x01, 0x25, 0x00}, {0x28, 0x01, 0x25, 0x01},\n        {0x02, 0x01, 0x2d, 0x00}, {0x09, 0x01, 0x2d, 0x00},\n        {0x17, 0x01, 0x2d, 0x00}, {0x28, 0x01, 0x2d, 0x01},\n        {0x02, 0x01, 0x2e, 0x00}, {0x09, 0x01, 0x2e, 0x00},\n        {0x17, 0x01, 0x2e, 0x00}, {0x28, 0x01, 0x2e, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x20, 0x00}, {0x06, 0x01, 0x20, 0x00},\n        {0x0a, 0x01, 0x20, 0x00}, {0x0f, 0x01, 0x20, 0x00},\n        {0x18, 0x01, 0x20, 0x00}, {0x1f, 0x01, 0x20, 0x00},\n        {0x29, 0x01, 0x20, 0x00}, {0x38, 0x01, 0x20, 0x01},\n        {0x03, 0x01, 0x25, 0x00}, {0x06, 0x01, 0x25, 0x00},\n        {0x0a, 0x01, 0x25, 0x00}, {0x0f, 0x01, 0x25, 0x00},\n        {0x18, 0x01, 0x25, 0x00}, {0x1f, 0x01, 0x25, 0x00},\n        {0x29, 0x01, 0x25, 0x00}, {0x38, 0x01, 0x25, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x2d, 0x00}, {0x06, 0x01, 0x2d, 0x00},\n        {0x0a, 0x01, 0x2d, 0x00}, {0x0f, 0x01, 0x2d, 0x00},\n        {0x18, 0x01, 0x2d, 0x00}, {0x1f, 0x01, 0x2d, 0x00},\n        {0x29, 0x01, 0x2d, 0x00}, {0x38, 0x01, 0x2d, 0x01},\n        {0x03, 0x01, 0x2e, 0x00}, {0x06, 0x01, 0x2e, 0x00},\n        {0x0a, 0x01, 0x2e, 0x00}, {0x0f, 0x01, 0x2e, 0x00},\n        {0x18, 0x01, 0x2e, 0x00}, {0x1f, 0x01, 0x2e, 0x00},\n        {0x29, 0x01, 0x2e, 0x00}, {0x38, 0x01, 0x2e, 0x01}\n    },\n    /* 15 */\n    {\n        {0x01, 0x01, 0x2f, 0x00}, {0x16, 0x01, 0x2f, 0x01},\n        {0x01, 0x01, 0x33, 0x00}, {0x16, 0x01, 0x33, 0x01},\n        {0x01, 0x01, 0x34, 0x00}, {0x16, 0x01, 0x34, 0x01},\n        {0x01, 0x01, 0x35, 0x00}, {0x16, 0x01, 0x35, 0x01},\n        {0x01, 0x01, 0x36, 0x00}, {0x16, 0x01, 0x36, 0x01},\n        {0x01, 0x01, 0x37, 0x00}, {0x16, 0x01, 0x37, 0x01},\n        {0x01, 0x01, 0x38, 0x00}, {0x16, 0x01, 0x38, 0x01},\n        {0x01, 0x01, 0x39, 0x00}, {0x16, 0x01, 0x39, 0x01}\n    },\n    {\n        {0x02, 0x01, 0x2f, 0x00}, {0x09, 0x01, 0x2f, 0x00},\n        {0x17, 0x01, 0x2f, 0x00}, {0x28, 0x01, 0x2f, 0x01},\n        {0x02, 0x01, 0x33, 0x00}, {0x09, 0x01, 0x33, 0x00},\n        {0x17, 0x01, 0x33, 0x00}, {0x28, 0x01, 0x33, 0x01},\n        {0x02, 0x01, 0x34, 0x00}, {0x09, 0x01, 0x34, 0x00},\n        {0x17, 0x01, 0x34, 0x00}, {0x28, 0x01, 0x34, 0x01},\n        {0x02, 0x01, 0x35, 0x00}, {0x09, 0x01, 0x35, 0x00},\n        {0x17, 0x01, 0x35, 0x00}, {0x28, 0x01, 0x35, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x2f, 0x00}, {0x06, 0x01, 0x2f, 0x00},\n        {0x0a, 0x01, 0x2f, 0x00}, {0x0f, 0x01, 0x2f, 0x00},\n        {0x18, 0x01, 0x2f, 0x00}, {0x1f, 0x01, 0x2f, 0x00},\n        {0x29, 0x01, 0x2f, 0x00}, {0x38, 0x01, 0x2f, 0x01},\n        {0x03, 0x01, 0x33, 0x00}, {0x06, 0x01, 0x33, 0x00},\n        {0x0a, 0x01, 0x33, 0x00}, {0x0f, 0x01, 0x33, 0x00},\n        {0x18, 0x01, 0x33, 0x00}, {0x1f, 0x01, 0x33, 0x00},\n        {0x29, 0x01, 0x33, 0x00}, {0x38, 0x01, 0x33, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x34, 0x00}, {0x06, 0x01, 0x34, 0x00},\n        {0x0a, 0x01, 0x34, 0x00}, {0x0f, 0x01, 0x34, 0x00},\n        {0x18, 0x01, 0x34, 0x00}, {0x1f, 0x01, 0x34, 0x00},\n        {0x29, 0x01, 0x34, 0x00}, {0x38, 0x01, 0x34, 0x01},\n        {0x03, 0x01, 0x35, 0x00}, {0x06, 0x01, 0x35, 0x00},\n        {0x0a, 0x01, 0x35, 0x00}, {0x0f, 0x01, 0x35, 0x00},\n        {0x18, 0x01, 0x35, 0x00}, {0x1f, 0x01, 0x35, 0x00},\n        {0x29, 0x01, 0x35, 0x00}, {0x38, 0x01, 0x35, 0x01}\n    },\n    {\n        {0x02, 0x01, 0x36, 0x00}, {0x09, 0x01, 0x36, 0x00},\n        {0x17, 0x01, 0x36, 0x00}, {0x28, 0x01, 0x36, 0x01},\n        {0x02, 0x01, 0x37, 0x00}, {0x09, 0x01, 0x37, 0x00},\n        {0x17, 0x01, 0x37, 0x00}, {0x28, 0x01, 0x37, 0x01},\n        {0x02, 0x01, 0x38, 0x00}, {0x09, 0x01, 0x38, 0x00},\n        {0x17, 0x01, 0x38, 0x00}, {0x28, 0x01, 0x38, 0x01},\n        {0x02, 0x01, 0x39, 0x00}, {0x09, 0x01, 0x39, 0x00},\n        {0x17, 0x01, 0x39, 0x00}, {0x28, 0x01, 0x39, 0x01}\n    },\n    /* 20 */\n    {\n        {0x03, 0x01, 0x36, 0x00}, {0x06, 0x01, 0x36, 0x00},\n        {0x0a, 0x01, 0x36, 0x00}, {0x0f, 0x01, 0x36, 0x00},\n        {0x18, 0x01, 0x36, 0x00}, {0x1f, 0x01, 0x36, 0x00},\n        {0x29, 0x01, 0x36, 0x00}, {0x38, 0x01, 0x36, 0x01},\n        {0x03, 0x01, 0x37, 0x00}, {0x06, 0x01, 0x37, 0x00},\n        {0x0a, 0x01, 0x37, 0x00}, {0x0f, 0x01, 0x37, 0x00},\n        {0x18, 0x01, 0x37, 0x00}, {0x1f, 0x01, 0x37, 0x00},\n        {0x29, 0x01, 0x37, 0x00}, {0x38, 0x01, 0x37, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x38, 0x00}, {0x06, 0x01, 0x38, 0x00},\n        {0x0a, 0x01, 0x38, 0x00}, {0x0f, 0x01, 0x38, 0x00},\n        {0x18, 0x01, 0x38, 0x00}, {0x1f, 0x01, 0x38, 0x00},\n        {0x29, 0x01, 0x38, 0x00}, {0x38, 0x01, 0x38, 0x01},\n        {0x03, 0x01, 0x39, 0x00}, {0x06, 0x01, 0x39, 0x00},\n        {0x0a, 0x01, 0x39, 0x00}, {0x0f, 0x01, 0x39, 0x00},\n        {0x18, 0x01, 0x39, 0x00}, {0x1f, 0x01, 0x39, 0x00},\n        {0x29, 0x01, 0x39, 0x00}, {0x38, 0x01, 0x39, 0x01}\n    },\n    {\n        {0x1a, 0x00, 0x00, 0x00}, {0x1b, 0x00, 0x00, 0x00},\n        {0x1d, 0x00, 0x00, 0x00}, {0x1e, 0x00, 0x00, 0x00},\n        {0x21, 0x00, 0x00, 0x00}, {0x22, 0x00, 0x00, 0x00},\n        {0x24, 0x00, 0x00, 0x00}, {0x25, 0x00, 0x00, 0x00},\n        {0x2b, 0x00, 0x00, 0x00}, {0x2e, 0x00, 0x00, 0x00},\n        {0x32, 0x00, 0x00, 0x00}, {0x35, 0x00, 0x00, 0x00},\n        {0x3a, 0x00, 0x00, 0x00}, {0x3d, 0x00, 0x00, 0x00},\n        {0x41, 0x00, 0x00, 0x00}, {0x44, 0x00, 0x00, 0x01}\n    },\n    {\n        {0x00, 0x01, 0x3d, 0x01}, {0x00, 0x01, 0x41, 0x01},\n        {0x00, 0x01, 0x5f, 0x01}, {0x00, 0x01, 0x62, 0x01},\n        {0x00, 0x01, 0x64, 0x01}, {0x00, 0x01, 0x66, 0x01},\n        {0x00, 0x01, 0x67, 0x01}, {0x00, 0x01, 0x68, 0x01},\n        {0x00, 0x01, 0x6c, 0x01}, {0x00, 0x01, 0x6d, 0x01},\n        {0x00, 0x01, 0x6e, 0x01}, {0x00, 0x01, 0x70, 0x01},\n        {0x00, 0x01, 0x72, 0x01}, {0x00, 0x01, 0x75, 0x01},\n        {0x26, 0x00, 0x00, 0x00}, {0x27, 0x00, 0x00, 0x00}\n    },\n    {\n        {0x01, 0x01, 0x3d, 0x00}, {0x16, 0x01, 0x3d, 0x01},\n        {0x01, 0x01, 0x41, 0x00}, {0x16, 0x01, 0x41, 0x01},\n        {0x01, 0x01, 0x5f, 0x00}, {0x16, 0x01, 0x5f, 0x01},\n        {0x01, 0x01, 0x62, 0x00}, {0x16, 0x01, 0x62, 0x01},\n        {0x01, 0x01, 0x64, 0x00}, {0x16, 0x01, 0x64, 0x01},\n        {0x01, 0x01, 0x66, 0x00}, {0x16, 0x01, 0x66, 0x01},\n        {0x01, 0x01, 0x67, 0x00}, {0x16, 0x01, 0x67, 0x01},\n        {0x01, 0x01, 0x68, 0x00}, {0x16, 0x01, 0x68, 0x01}\n    },\n    /* 25 */\n    {\n        {0x02, 0x01, 0x3d, 0x00}, {0x09, 0x01, 0x3d, 0x00},\n        {0x17, 0x01, 0x3d, 0x00}, {0x28, 0x01, 0x3d, 0x01},\n        {0x02, 0x01, 0x41, 0x00}, {0x09, 0x01, 0x41, 0x00},\n        {0x17, 0x01, 0x41, 0x00}, {0x28, 0x01, 0x41, 0x01},\n        {0x02, 0x01, 0x5f, 0x00}, {0x09, 0x01, 0x5f, 0x00},\n        {0x17, 0x01, 0x5f, 0x00}, {0x28, 0x01, 0x5f, 0x01},\n        {0x02, 0x01, 0x62, 0x00}, {0x09, 0x01, 0x62, 0x00},\n        {0x17, 0x01, 0x62, 0x00}, {0x28, 0x01, 0x62, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x3d, 0x00}, {0x06, 0x01, 0x3d, 0x00},\n        {0x0a, 0x01, 0x3d, 0x00}, {0x0f, 0x01, 0x3d, 0x00},\n        {0x18, 0x01, 0x3d, 0x00}, {0x1f, 0x01, 0x3d, 0x00},\n        {0x29, 0x01, 0x3d, 0x00}, {0x38, 0x01, 0x3d, 0x01},\n        {0x03, 0x01, 0x41, 0x00}, {0x06, 0x01, 0x41, 0x00},\n        {0x0a, 0x01, 0x41, 0x00}, {0x0f, 0x01, 0x41, 0x00},\n        {0x18, 0x01, 0x41, 0x00}, {0x1f, 0x01, 0x41, 0x00},\n        {0x29, 0x01, 0x41, 0x00}, {0x38, 0x01, 0x41, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x5f, 0x00}, {0x06, 0x01, 0x5f, 0x00},\n        {0x0a, 0x01, 0x5f, 0x00}, {0x0f, 0x01, 0x5f, 0x00},\n        {0x18, 0x01, 0x5f, 0x00}, {0x1f, 0x01, 0x5f, 0x00},\n        {0x29, 0x01, 0x5f, 0x00}, {0x38, 0x01, 0x5f, 0x01},\n        {0x03, 0x01, 0x62, 0x00}, {0x06, 0x01, 0x62, 0x00},\n        {0x0a, 0x01, 0x62, 0x00}, {0x0f, 0x01, 0x62, 0x00},\n        {0x18, 0x01, 0x62, 0x00}, {0x1f, 0x01, 0x62, 0x00},\n        {0x29, 0x01, 0x62, 0x00}, {0x38, 0x01, 0x62, 0x01}\n    },\n    {\n        {0x02, 0x01, 0x64, 0x00}, {0x09, 0x01, 0x64, 0x00},\n        {0x17, 0x01, 0x64, 0x00}, {0x28, 0x01, 0x64, 0x01},\n        {0x02, 0x01, 0x66, 0x00}, {0x09, 0x01, 0x66, 0x00},\n        {0x17, 0x01, 0x66, 0x00}, {0x28, 0x01, 0x66, 0x01},\n        {0x02, 0x01, 0x67, 0x00}, {0x09, 0x01, 0x67, 0x00},\n        {0x17, 0x01, 0x67, 0x00}, {0x28, 0x01, 0x67, 0x01},\n        {0x02, 0x01, 0x68, 0x00}, {0x09, 0x01, 0x68, 0x00},\n        {0x17, 0x01, 0x68, 0x00}, {0x28, 0x01, 0x68, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x64, 0x00}, {0x06, 0x01, 0x64, 0x00},\n        {0x0a, 0x01, 0x64, 0x00}, {0x0f, 0x01, 0x64, 0x00},\n        {0x18, 0x01, 0x64, 0x00}, {0x1f, 0x01, 0x64, 0x00},\n        {0x29, 0x01, 0x64, 0x00}, {0x38, 0x01, 0x64, 0x01},\n        {0x03, 0x01, 0x66, 0x00}, {0x06, 0x01, 0x66, 0x00},\n        {0x0a, 0x01, 0x66, 0x00}, {0x0f, 0x01, 0x66, 0x00},\n        {0x18, 0x01, 0x66, 0x00}, {0x1f, 0x01, 0x66, 0x00},\n        {0x29, 0x01, 0x66, 0x00}, {0x38, 0x01, 0x66, 0x01}\n    },\n    /* 30 */\n    {\n        {0x03, 0x01, 0x67, 0x00}, {0x06, 0x01, 0x67, 0x00},\n        {0x0a, 0x01, 0x67, 0x00}, {0x0f, 0x01, 0x67, 0x00},\n        {0x18, 0x01, 0x67, 0x00}, {0x1f, 0x01, 0x67, 0x00},\n        {0x29, 0x01, 0x67, 0x00}, {0x38, 0x01, 0x67, 0x01},\n        {0x03, 0x01, 0x68, 0x00}, {0x06, 0x01, 0x68, 0x00},\n        {0x0a, 0x01, 0x68, 0x00}, {0x0f, 0x01, 0x68, 0x00},\n        {0x18, 0x01, 0x68, 0x00}, {0x1f, 0x01, 0x68, 0x00},\n        {0x29, 0x01, 0x68, 0x00}, {0x38, 0x01, 0x68, 0x01}\n    },\n    {\n        {0x01, 0x01, 0x6c, 0x00}, {0x16, 0x01, 0x6c, 0x01},\n        {0x01, 0x01, 0x6d, 0x00}, {0x16, 0x01, 0x6d, 0x01},\n        {0x01, 0x01, 0x6e, 0x00}, {0x16, 0x01, 0x6e, 0x01},\n        {0x01, 0x01, 0x70, 0x00}, {0x16, 0x01, 0x70, 0x01},\n        {0x01, 0x01, 0x72, 0x00}, {0x16, 0x01, 0x72, 0x01},\n        {0x01, 0x01, 0x75, 0x00}, {0x16, 0x01, 0x75, 0x01},\n        {0x00, 0x01, 0x3a, 0x01}, {0x00, 0x01, 0x42, 0x01},\n        {0x00, 0x01, 0x43, 0x01}, {0x00, 0x01, 0x44, 0x01}\n    },\n    {\n        {0x02, 0x01, 0x6c, 0x00}, {0x09, 0x01, 0x6c, 0x00},\n        {0x17, 0x01, 0x6c, 0x00}, {0x28, 0x01, 0x6c, 0x01},\n        {0x02, 0x01, 0x6d, 0x00}, {0x09, 0x01, 0x6d, 0x00},\n        {0x17, 0x01, 0x6d, 0x00}, {0x28, 0x01, 0x6d, 0x01},\n        {0x02, 0x01, 0x6e, 0x00}, {0x09, 0x01, 0x6e, 0x00},\n        {0x17, 0x01, 0x6e, 0x00}, {0x28, 0x01, 0x6e, 0x01},\n        {0x02, 0x01, 0x70, 0x00}, {0x09, 0x01, 0x70, 0x00},\n        {0x17, 0x01, 0x70, 0x00}, {0x28, 0x01, 0x70, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x6c, 0x00}, {0x06, 0x01, 0x6c, 0x00},\n        {0x0a, 0x01, 0x6c, 0x00}, {0x0f, 0x01, 0x6c, 0x00},\n        {0x18, 0x01, 0x6c, 0x00}, {0x1f, 0x01, 0x6c, 0x00},\n        {0x29, 0x01, 0x6c, 0x00}, {0x38, 0x01, 0x6c, 0x01},\n        {0x03, 0x01, 0x6d, 0x00}, {0x06, 0x01, 0x6d, 0x00},\n        {0x0a, 0x01, 0x6d, 0x00}, {0x0f, 0x01, 0x6d, 0x00},\n        {0x18, 0x01, 0x6d, 0x00}, {0x1f, 0x01, 0x6d, 0x00},\n        {0x29, 0x01, 0x6d, 0x00}, {0x38, 0x01, 0x6d, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x6e, 0x00}, {0x06, 0x01, 0x6e, 0x00},\n        {0x0a, 0x01, 0x6e, 0x00}, {0x0f, 0x01, 0x6e, 0x00},\n        {0x18, 0x01, 0x6e, 0x00}, {0x1f, 0x01, 0x6e, 0x00},\n        {0x29, 0x01, 0x6e, 0x00}, {0x38, 0x01, 0x6e, 0x01},\n        {0x03, 0x01, 0x70, 0x00}, {0x06, 0x01, 0x70, 0x00},\n        {0x0a, 0x01, 0x70, 0x00}, {0x0f, 0x01, 0x70, 0x00},\n        {0x18, 0x01, 0x70, 0x00}, {0x1f, 0x01, 0x70, 0x00},\n        {0x29, 0x01, 0x70, 0x00}, {0x38, 0x01, 0x70, 0x01}\n    },\n    /* 35 */\n    {\n        {0x02, 0x01, 0x72, 0x00}, {0x09, 0x01, 0x72, 0x00},\n        {0x17, 0x01, 0x72, 0x00}, {0x28, 0x01, 0x72, 0x01},\n        {0x02, 0x01, 0x75, 0x00}, {0x09, 0x01, 0x75, 0x00},\n        {0x17, 0x01, 0x75, 0x00}, {0x28, 0x01, 0x75, 0x01},\n        {0x01, 0x01, 0x3a, 0x00}, {0x16, 0x01, 0x3a, 0x01},\n        {0x01, 0x01, 0x42, 0x00}, {0x16, 0x01, 0x42, 0x01},\n        {0x01, 0x01, 0x43, 0x00}, {0x16, 0x01, 0x43, 0x01},\n        {0x01, 0x01, 0x44, 0x00}, {0x16, 0x01, 0x44, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x72, 0x00}, {0x06, 0x01, 0x72, 0x00},\n        {0x0a, 0x01, 0x72, 0x00}, {0x0f, 0x01, 0x72, 0x00},\n        {0x18, 0x01, 0x72, 0x00}, {0x1f, 0x01, 0x72, 0x00},\n        {0x29, 0x01, 0x72, 0x00}, {0x38, 0x01, 0x72, 0x01},\n        {0x03, 0x01, 0x75, 0x00}, {0x06, 0x01, 0x75, 0x00},\n        {0x0a, 0x01, 0x75, 0x00}, {0x0f, 0x01, 0x75, 0x00},\n        {0x18, 0x01, 0x75, 0x00}, {0x1f, 0x01, 0x75, 0x00},\n        {0x29, 0x01, 0x75, 0x00}, {0x38, 0x01, 0x75, 0x01}\n    },\n    {\n        {0x02, 0x01, 0x3a, 0x00}, {0x09, 0x01, 0x3a, 0x00},\n        {0x17, 0x01, 0x3a, 0x00}, {0x28, 0x01, 0x3a, 0x01},\n        {0x02, 0x01, 0x42, 0x00}, {0x09, 0x01, 0x42, 0x00},\n        {0x17, 0x01, 0x42, 0x00}, {0x28, 0x01, 0x42, 0x01},\n        {0x02, 0x01, 0x43, 0x00}, {0x09, 0x01, 0x43, 0x00},\n        {0x17, 0x01, 0x43, 0x00}, {0x28, 0x01, 0x43, 0x01},\n        {0x02, 0x01, 0x44, 0x00}, {0x09, 0x01, 0x44, 0x00},\n        {0x17, 0x01, 0x44, 0x00}, {0x28, 0x01, 0x44, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x3a, 0x00}, {0x06, 0x01, 0x3a, 0x00},\n        {0x0a, 0x01, 0x3a, 0x00}, {0x0f, 0x01, 0x3a, 0x00},\n        {0x18, 0x01, 0x3a, 0x00}, {0x1f, 0x01, 0x3a, 0x00},\n        {0x29, 0x01, 0x3a, 0x00}, {0x38, 0x01, 0x3a, 0x01},\n        {0x03, 0x01, 0x42, 0x00}, {0x06, 0x01, 0x42, 0x00},\n        {0x0a, 0x01, 0x42, 0x00}, {0x0f, 0x01, 0x42, 0x00},\n        {0x18, 0x01, 0x42, 0x00}, {0x1f, 0x01, 0x42, 0x00},\n        {0x29, 0x01, 0x42, 0x00}, {0x38, 0x01, 0x42, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x43, 0x00}, {0x06, 0x01, 0x43, 0x00},\n        {0x0a, 0x01, 0x43, 0x00}, {0x0f, 0x01, 0x43, 0x00},\n        {0x18, 0x01, 0x43, 0x00}, {0x1f, 0x01, 0x43, 0x00},\n        {0x29, 0x01, 0x43, 0x00}, {0x38, 0x01, 0x43, 0x01},\n        {0x03, 0x01, 0x44, 0x00}, {0x06, 0x01, 0x44, 0x00},\n        {0x0a, 0x01, 0x44, 0x00}, {0x0f, 0x01, 0x44, 0x00},\n        {0x18, 0x01, 0x44, 0x00}, {0x1f, 0x01, 0x44, 0x00},\n        {0x29, 0x01, 0x44, 0x00}, {0x38, 0x01, 0x44, 0x01}\n    },\n    /* 40 */\n    {\n        {0x2c, 0x00, 0x00, 0x00}, {0x2d, 0x00, 0x00, 0x00},\n        {0x2f, 0x00, 0x00, 0x00}, {0x30, 0x00, 0x00, 0x00},\n        {0x33, 0x00, 0x00, 0x00}, {0x34, 0x00, 0x00, 0x00},\n        {0x36, 0x00, 0x00, 0x00}, {0x37, 0x00, 0x00, 0x00},\n        {0x3b, 0x00, 0x00, 0x00}, {0x3c, 0x00, 0x00, 0x00},\n        {0x3e, 0x00, 0x00, 0x00}, {0x3f, 0x00, 0x00, 0x00},\n        {0x42, 0x00, 0x00, 0x00}, {0x43, 0x00, 0x00, 0x00},\n        {0x45, 0x00, 0x00, 0x00}, {0x48, 0x00, 0x00, 0x01}\n    },\n    {\n        {0x00, 0x01, 0x45, 0x01}, {0x00, 0x01, 0x46, 0x01},\n        {0x00, 0x01, 0x47, 0x01}, {0x00, 0x01, 0x48, 0x01},\n        {0x00, 0x01, 0x49, 0x01}, {0x00, 0x01, 0x4a, 0x01},\n        {0x00, 0x01, 0x4b, 0x01}, {0x00, 0x01, 0x4c, 0x01},\n        {0x00, 0x01, 0x4d, 0x01}, {0x00, 0x01, 0x4e, 0x01},\n        {0x00, 0x01, 0x4f, 0x01}, {0x00, 0x01, 0x50, 0x01},\n        {0x00, 0x01, 0x51, 0x01}, {0x00, 0x01, 0x52, 0x01},\n        {0x00, 0x01, 0x53, 0x01}, {0x00, 0x01, 0x54, 0x01}\n    },\n    {\n        {0x01, 0x01, 0x45, 0x00}, {0x16, 0x01, 0x45, 0x01},\n        {0x01, 0x01, 0x46, 0x00}, {0x16, 0x01, 0x46, 0x01},\n        {0x01, 0x01, 0x47, 0x00}, {0x16, 0x01, 0x47, 0x01},\n        {0x01, 0x01, 0x48, 0x00}, {0x16, 0x01, 0x48, 0x01},\n        {0x01, 0x01, 0x49, 0x00}, {0x16, 0x01, 0x49, 0x01},\n        {0x01, 0x01, 0x4a, 0x00}, {0x16, 0x01, 0x4a, 0x01},\n        {0x01, 0x01, 0x4b, 0x00}, {0x16, 0x01, 0x4b, 0x01},\n        {0x01, 0x01, 0x4c, 0x00}, {0x16, 0x01, 0x4c, 0x01}\n    },\n    {\n        {0x02, 0x01, 0x45, 0x00}, {0x09, 0x01, 0x45, 0x00},\n        {0x17, 0x01, 0x45, 0x00}, {0x28, 0x01, 0x45, 0x01},\n        {0x02, 0x01, 0x46, 0x00}, {0x09, 0x01, 0x46, 0x00},\n        {0x17, 0x01, 0x46, 0x00}, {0x28, 0x01, 0x46, 0x01},\n        {0x02, 0x01, 0x47, 0x00}, {0x09, 0x01, 0x47, 0x00},\n        {0x17, 0x01, 0x47, 0x00}, {0x28, 0x01, 0x47, 0x01},\n        {0x02, 0x01, 0x48, 0x00}, {0x09, 0x01, 0x48, 0x00},\n        {0x17, 0x01, 0x48, 0x00}, {0x28, 0x01, 0x48, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x45, 0x00}, {0x06, 0x01, 0x45, 0x00},\n        {0x0a, 0x01, 0x45, 0x00}, {0x0f, 0x01, 0x45, 0x00},\n        {0x18, 0x01, 0x45, 0x00}, {0x1f, 0x01, 0x45, 0x00},\n        {0x29, 0x01, 0x45, 0x00}, {0x38, 0x01, 0x45, 0x01},\n        {0x03, 0x01, 0x46, 0x00}, {0x06, 0x01, 0x46, 0x00},\n        {0x0a, 0x01, 0x46, 0x00}, {0x0f, 0x01, 0x46, 0x00},\n        {0x18, 0x01, 0x46, 0x00}, {0x1f, 0x01, 0x46, 0x00},\n        {0x29, 0x01, 0x46, 0x00}, {0x38, 0x01, 0x46, 0x01}\n    },\n    /* 45 */\n    {\n        {0x03, 0x01, 0x47, 0x00}, {0x06, 0x01, 0x47, 0x00},\n        {0x0a, 0x01, 0x47, 0x00}, {0x0f, 0x01, 0x47, 0x00},\n        {0x18, 0x01, 0x47, 0x00}, {0x1f, 0x01, 0x47, 0x00},\n        {0x29, 0x01, 0x47, 0x00}, {0x38, 0x01, 0x47, 0x01},\n        {0x03, 0x01, 0x48, 0x00}, {0x06, 0x01, 0x48, 0x00},\n        {0x0a, 0x01, 0x48, 0x00}, {0x0f, 0x01, 0x48, 0x00},\n        {0x18, 0x01, 0x48, 0x00}, {0x1f, 0x01, 0x48, 0x00},\n        {0x29, 0x01, 0x48, 0x00}, {0x38, 0x01, 0x48, 0x01}\n    },\n    {\n        {0x02, 0x01, 0x49, 0x00}, {0x09, 0x01, 0x49, 0x00},\n        {0x17, 0x01, 0x49, 0x00}, {0x28, 0x01, 0x49, 0x01},\n        {0x02, 0x01, 0x4a, 0x00}, {0x09, 0x01, 0x4a, 0x00},\n        {0x17, 0x01, 0x4a, 0x00}, {0x28, 0x01, 0x4a, 0x01},\n        {0x02, 0x01, 0x4b, 0x00}, {0x09, 0x01, 0x4b, 0x00},\n        {0x17, 0x01, 0x4b, 0x00}, {0x28, 0x01, 0x4b, 0x01},\n        {0x02, 0x01, 0x4c, 0x00}, {0x09, 0x01, 0x4c, 0x00},\n        {0x17, 0x01, 0x4c, 0x00}, {0x28, 0x01, 0x4c, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x49, 0x00}, {0x06, 0x01, 0x49, 0x00},\n        {0x0a, 0x01, 0x49, 0x00}, {0x0f, 0x01, 0x49, 0x00},\n        {0x18, 0x01, 0x49, 0x00}, {0x1f, 0x01, 0x49, 0x00},\n        {0x29, 0x01, 0x49, 0x00}, {0x38, 0x01, 0x49, 0x01},\n        {0x03, 0x01, 0x4a, 0x00}, {0x06, 0x01, 0x4a, 0x00},\n        {0x0a, 0x01, 0x4a, 0x00}, {0x0f, 0x01, 0x4a, 0x00},\n        {0x18, 0x01, 0x4a, 0x00}, {0x1f, 0x01, 0x4a, 0x00},\n        {0x29, 0x01, 0x4a, 0x00}, {0x38, 0x01, 0x4a, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x4b, 0x00}, {0x06, 0x01, 0x4b, 0x00},\n        {0x0a, 0x01, 0x4b, 0x00}, {0x0f, 0x01, 0x4b, 0x00},\n        {0x18, 0x01, 0x4b, 0x00}, {0x1f, 0x01, 0x4b, 0x00},\n        {0x29, 0x01, 0x4b, 0x00}, {0x38, 0x01, 0x4b, 0x01},\n        {0x03, 0x01, 0x4c, 0x00}, {0x06, 0x01, 0x4c, 0x00},\n        {0x0a, 0x01, 0x4c, 0x00}, {0x0f, 0x01, 0x4c, 0x00},\n        {0x18, 0x01, 0x4c, 0x00}, {0x1f, 0x01, 0x4c, 0x00},\n        {0x29, 0x01, 0x4c, 0x00}, {0x38, 0x01, 0x4c, 0x01}\n    },\n    {\n        {0x01, 0x01, 0x4d, 0x00}, {0x16, 0x01, 0x4d, 0x01},\n        {0x01, 0x01, 0x4e, 0x00}, {0x16, 0x01, 0x4e, 0x01},\n        {0x01, 0x01, 0x4f, 0x00}, {0x16, 0x01, 0x4f, 0x01},\n        {0x01, 0x01, 0x50, 0x00}, {0x16, 0x01, 0x50, 0x01},\n        {0x01, 0x01, 0x51, 0x00}, {0x16, 0x01, 0x51, 0x01},\n        {0x01, 0x01, 0x52, 0x00}, {0x16, 0x01, 0x52, 0x01},\n        {0x01, 0x01, 0x53, 0x00}, {0x16, 0x01, 0x53, 0x01},\n        {0x01, 0x01, 0x54, 0x00}, {0x16, 0x01, 0x54, 0x01}\n    },\n    /* 50 */\n    {\n        {0x02, 0x01, 0x4d, 0x00}, {0x09, 0x01, 0x4d, 0x00},\n        {0x17, 0x01, 0x4d, 0x00}, {0x28, 0x01, 0x4d, 0x01},\n        {0x02, 0x01, 0x4e, 0x00}, {0x09, 0x01, 0x4e, 0x00},\n        {0x17, 0x01, 0x4e, 0x00}, {0x28, 0x01, 0x4e, 0x01},\n        {0x02, 0x01, 0x4f, 0x00}, {0x09, 0x01, 0x4f, 0x00},\n        {0x17, 0x01, 0x4f, 0x00}, {0x28, 0x01, 0x4f, 0x01},\n        {0x02, 0x01, 0x50, 0x00}, {0x09, 0x01, 0x50, 0x00},\n        {0x17, 0x01, 0x50, 0x00}, {0x28, 0x01, 0x50, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x4d, 0x00}, {0x06, 0x01, 0x4d, 0x00},\n        {0x0a, 0x01, 0x4d, 0x00}, {0x0f, 0x01, 0x4d, 0x00},\n        {0x18, 0x01, 0x4d, 0x00}, {0x1f, 0x01, 0x4d, 0x00},\n        {0x29, 0x01, 0x4d, 0x00}, {0x38, 0x01, 0x4d, 0x01},\n        {0x03, 0x01, 0x4e, 0x00}, {0x06, 0x01, 0x4e, 0x00},\n        {0x0a, 0x01, 0x4e, 0x00}, {0x0f, 0x01, 0x4e, 0x00},\n        {0x18, 0x01, 0x4e, 0x00}, {0x1f, 0x01, 0x4e, 0x00},\n        {0x29, 0x01, 0x4e, 0x00}, {0x38, 0x01, 0x4e, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x4f, 0x00}, {0x06, 0x01, 0x4f, 0x00},\n        {0x0a, 0x01, 0x4f, 0x00}, {0x0f, 0x01, 0x4f, 0x00},\n        {0x18, 0x01, 0x4f, 0x00}, {0x1f, 0x01, 0x4f, 0x00},\n        {0x29, 0x01, 0x4f, 0x00}, {0x38, 0x01, 0x4f, 0x01},\n        {0x03, 0x01, 0x50, 0x00}, {0x06, 0x01, 0x50, 0x00},\n        {0x0a, 0x01, 0x50, 0x00}, {0x0f, 0x01, 0x50, 0x00},\n        {0x18, 0x01, 0x50, 0x00}, {0x1f, 0x01, 0x50, 0x00},\n        {0x29, 0x01, 0x50, 0x00}, {0x38, 0x01, 0x50, 0x01}\n    },\n    {\n        {0x02, 0x01, 0x51, 0x00}, {0x09, 0x01, 0x51, 0x00},\n        {0x17, 0x01, 0x51, 0x00}, {0x28, 0x01, 0x51, 0x01},\n        {0x02, 0x01, 0x52, 0x00}, {0x09, 0x01, 0x52, 0x00},\n        {0x17, 0x01, 0x52, 0x00}, {0x28, 0x01, 0x52, 0x01},\n        {0x02, 0x01, 0x53, 0x00}, {0x09, 0x01, 0x53, 0x00},\n        {0x17, 0x01, 0x53, 0x00}, {0x28, 0x01, 0x53, 0x01},\n        {0x02, 0x01, 0x54, 0x00}, {0x09, 0x01, 0x54, 0x00},\n        {0x17, 0x01, 0x54, 0x00}, {0x28, 0x01, 0x54, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x51, 0x00}, {0x06, 0x01, 0x51, 0x00},\n        {0x0a, 0x01, 0x51, 0x00}, {0x0f, 0x01, 0x51, 0x00},\n        {0x18, 0x01, 0x51, 0x00}, {0x1f, 0x01, 0x51, 0x00},\n        {0x29, 0x01, 0x51, 0x00}, {0x38, 0x01, 0x51, 0x01},\n        {0x03, 0x01, 0x52, 0x00}, {0x06, 0x01, 0x52, 0x00},\n        {0x0a, 0x01, 0x52, 0x00}, {0x0f, 0x01, 0x52, 0x00},\n        {0x18, 0x01, 0x52, 0x00}, {0x1f, 0x01, 0x52, 0x00},\n        {0x29, 0x01, 0x52, 0x00}, {0x38, 0x01, 0x52, 0x01}\n    },\n    /* 55 */\n    {\n        {0x03, 0x01, 0x53, 0x00}, {0x06, 0x01, 0x53, 0x00},\n        {0x0a, 0x01, 0x53, 0x00}, {0x0f, 0x01, 0x53, 0x00},\n        {0x18, 0x01, 0x53, 0x00}, {0x1f, 0x01, 0x53, 0x00},\n        {0x29, 0x01, 0x53, 0x00}, {0x38, 0x01, 0x53, 0x01},\n        {0x03, 0x01, 0x54, 0x00}, {0x06, 0x01, 0x54, 0x00},\n        {0x0a, 0x01, 0x54, 0x00}, {0x0f, 0x01, 0x54, 0x00},\n        {0x18, 0x01, 0x54, 0x00}, {0x1f, 0x01, 0x54, 0x00},\n        {0x29, 0x01, 0x54, 0x00}, {0x38, 0x01, 0x54, 0x01}\n    },\n    {\n        {0x00, 0x01, 0x55, 0x01}, {0x00, 0x01, 0x56, 0x01},\n        {0x00, 0x01, 0x57, 0x01}, {0x00, 0x01, 0x59, 0x01},\n        {0x00, 0x01, 0x6a, 0x01}, {0x00, 0x01, 0x6b, 0x01},\n        {0x00, 0x01, 0x71, 0x01}, {0x00, 0x01, 0x76, 0x01},\n        {0x00, 0x01, 0x77, 0x01}, {0x00, 0x01, 0x78, 0x01},\n        {0x00, 0x01, 0x79, 0x01}, {0x00, 0x01, 0x7a, 0x01},\n        {0x46, 0x00, 0x00, 0x00}, {0x47, 0x00, 0x00, 0x00},\n        {0x49, 0x00, 0x00, 0x00}, {0x4a, 0x00, 0x00, 0x01}\n    },\n    {\n        {0x01, 0x01, 0x55, 0x00}, {0x16, 0x01, 0x55, 0x01},\n        {0x01, 0x01, 0x56, 0x00}, {0x16, 0x01, 0x56, 0x01},\n        {0x01, 0x01, 0x57, 0x00}, {0x16, 0x01, 0x57, 0x01},\n        {0x01, 0x01, 0x59, 0x00}, {0x16, 0x01, 0x59, 0x01},\n        {0x01, 0x01, 0x6a, 0x00}, {0x16, 0x01, 0x6a, 0x01},\n        {0x01, 0x01, 0x6b, 0x00}, {0x16, 0x01, 0x6b, 0x01},\n        {0x01, 0x01, 0x71, 0x00}, {0x16, 0x01, 0x71, 0x01},\n        {0x01, 0x01, 0x76, 0x00}, {0x16, 0x01, 0x76, 0x01}\n    },\n    {\n        {0x02, 0x01, 0x55, 0x00}, {0x09, 0x01, 0x55, 0x00},\n        {0x17, 0x01, 0x55, 0x00}, {0x28, 0x01, 0x55, 0x01},\n        {0x02, 0x01, 0x56, 0x00}, {0x09, 0x01, 0x56, 0x00},\n        {0x17, 0x01, 0x56, 0x00}, {0x28, 0x01, 0x56, 0x01},\n        {0x02, 0x01, 0x57, 0x00}, {0x09, 0x01, 0x57, 0x00},\n        {0x17, 0x01, 0x57, 0x00}, {0x28, 0x01, 0x57, 0x01},\n        {0x02, 0x01, 0x59, 0x00}, {0x09, 0x01, 0x59, 0x00},\n        {0x17, 0x01, 0x59, 0x00}, {0x28, 0x01, 0x59, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x55, 0x00}, {0x06, 0x01, 0x55, 0x00},\n        {0x0a, 0x01, 0x55, 0x00}, {0x0f, 0x01, 0x55, 0x00},\n        {0x18, 0x01, 0x55, 0x00}, {0x1f, 0x01, 0x55, 0x00},\n        {0x29, 0x01, 0x55, 0x00}, {0x38, 0x01, 0x55, 0x01},\n        {0x03, 0x01, 0x56, 0x00}, {0x06, 0x01, 0x56, 0x00},\n        {0x0a, 0x01, 0x56, 0x00}, {0x0f, 0x01, 0x56, 0x00},\n        {0x18, 0x01, 0x56, 0x00}, {0x1f, 0x01, 0x56, 0x00},\n        {0x29, 0x01, 0x56, 0x00}, {0x38, 0x01, 0x56, 0x01}\n    },\n    /* 60 */\n    {\n        {0x03, 0x01, 0x57, 0x00}, {0x06, 0x01, 0x57, 0x00},\n        {0x0a, 0x01, 0x57, 0x00}, {0x0f, 0x01, 0x57, 0x00},\n        {0x18, 0x01, 0x57, 0x00}, {0x1f, 0x01, 0x57, 0x00},\n        {0x29, 0x01, 0x57, 0x00}, {0x38, 0x01, 0x57, 0x01},\n        {0x03, 0x01, 0x59, 0x00}, {0x06, 0x01, 0x59, 0x00},\n        {0x0a, 0x01, 0x59, 0x00}, {0x0f, 0x01, 0x59, 0x00},\n        {0x18, 0x01, 0x59, 0x00}, {0x1f, 0x01, 0x59, 0x00},\n        {0x29, 0x01, 0x59, 0x00}, {0x38, 0x01, 0x59, 0x01}\n    },\n    {\n        {0x02, 0x01, 0x6a, 0x00}, {0x09, 0x01, 0x6a, 0x00},\n        {0x17, 0x01, 0x6a, 0x00}, {0x28, 0x01, 0x6a, 0x01},\n        {0x02, 0x01, 0x6b, 0x00}, {0x09, 0x01, 0x6b, 0x00},\n        {0x17, 0x01, 0x6b, 0x00}, {0x28, 0x01, 0x6b, 0x01},\n        {0x02, 0x01, 0x71, 0x00}, {0x09, 0x01, 0x71, 0x00},\n        {0x17, 0x01, 0x71, 0x00}, {0x28, 0x01, 0x71, 0x01},\n        {0x02, 0x01, 0x76, 0x00}, {0x09, 0x01, 0x76, 0x00},\n        {0x17, 0x01, 0x76, 0x00}, {0x28, 0x01, 0x76, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x6a, 0x00}, {0x06, 0x01, 0x6a, 0x00},\n        {0x0a, 0x01, 0x6a, 0x00}, {0x0f, 0x01, 0x6a, 0x00},\n        {0x18, 0x01, 0x6a, 0x00}, {0x1f, 0x01, 0x6a, 0x00},\n        {0x29, 0x01, 0x6a, 0x00}, {0x38, 0x01, 0x6a, 0x01},\n        {0x03, 0x01, 0x6b, 0x00}, {0x06, 0x01, 0x6b, 0x00},\n        {0x0a, 0x01, 0x6b, 0x00}, {0x0f, 0x01, 0x6b, 0x00},\n        {0x18, 0x01, 0x6b, 0x00}, {0x1f, 0x01, 0x6b, 0x00},\n        {0x29, 0x01, 0x6b, 0x00}, {0x38, 0x01, 0x6b, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x71, 0x00}, {0x06, 0x01, 0x71, 0x00},\n        {0x0a, 0x01, 0x71, 0x00}, {0x0f, 0x01, 0x71, 0x00},\n        {0x18, 0x01, 0x71, 0x00}, {0x1f, 0x01, 0x71, 0x00},\n        {0x29, 0x01, 0x71, 0x00}, {0x38, 0x01, 0x71, 0x01},\n        {0x03, 0x01, 0x76, 0x00}, {0x06, 0x01, 0x76, 0x00},\n        {0x0a, 0x01, 0x76, 0x00}, {0x0f, 0x01, 0x76, 0x00},\n        {0x18, 0x01, 0x76, 0x00}, {0x1f, 0x01, 0x76, 0x00},\n        {0x29, 0x01, 0x76, 0x00}, {0x38, 0x01, 0x76, 0x01}\n    },\n    {\n        {0x01, 0x01, 0x77, 0x00}, {0x16, 0x01, 0x77, 0x01},\n        {0x01, 0x01, 0x78, 0x00}, {0x16, 0x01, 0x78, 0x01},\n        {0x01, 0x01, 0x79, 0x00}, {0x16, 0x01, 0x79, 0x01},\n        {0x01, 0x01, 0x7a, 0x00}, {0x16, 0x01, 0x7a, 0x01},\n        {0x00, 0x01, 0x26, 0x01}, {0x00, 0x01, 0x2a, 0x01},\n        {0x00, 0x01, 0x2c, 0x01}, {0x00, 0x01, 0x3b, 0x01},\n        {0x00, 0x01, 0x58, 0x01}, {0x00, 0x01, 0x5a, 0x01},\n        {0x4b, 0x00, 0x00, 0x00}, {0x4e, 0x00, 0x00, 0x01}\n    },\n    /* 65 */\n    {\n        {0x02, 0x01, 0x77, 0x00}, {0x09, 0x01, 0x77, 0x00},\n        {0x17, 0x01, 0x77, 0x00}, {0x28, 0x01, 0x77, 0x01},\n        {0x02, 0x01, 0x78, 0x00}, {0x09, 0x01, 0x78, 0x00},\n        {0x17, 0x01, 0x78, 0x00}, {0x28, 0x01, 0x78, 0x01},\n        {0x02, 0x01, 0x79, 0x00}, {0x09, 0x01, 0x79, 0x00},\n        {0x17, 0x01, 0x79, 0x00}, {0x28, 0x01, 0x79, 0x01},\n        {0x02, 0x01, 0x7a, 0x00}, {0x09, 0x01, 0x7a, 0x00},\n        {0x17, 0x01, 0x7a, 0x00}, {0x28, 0x01, 0x7a, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x77, 0x00}, {0x06, 0x01, 0x77, 0x00},\n        {0x0a, 0x01, 0x77, 0x00}, {0x0f, 0x01, 0x77, 0x00},\n        {0x18, 0x01, 0x77, 0x00}, {0x1f, 0x01, 0x77, 0x00},\n        {0x29, 0x01, 0x77, 0x00}, {0x38, 0x01, 0x77, 0x01},\n        {0x03, 0x01, 0x78, 0x00}, {0x06, 0x01, 0x78, 0x00},\n        {0x0a, 0x01, 0x78, 0x00}, {0x0f, 0x01, 0x78, 0x00},\n        {0x18, 0x01, 0x78, 0x00}, {0x1f, 0x01, 0x78, 0x00},\n        {0x29, 0x01, 0x78, 0x00}, {0x38, 0x01, 0x78, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x79, 0x00}, {0x06, 0x01, 0x79, 0x00},\n        {0x0a, 0x01, 0x79, 0x00}, {0x0f, 0x01, 0x79, 0x00},\n        {0x18, 0x01, 0x79, 0x00}, {0x1f, 0x01, 0x79, 0x00},\n        {0x29, 0x01, 0x79, 0x00}, {0x38, 0x01, 0x79, 0x01},\n        {0x03, 0x01, 0x7a, 0x00}, {0x06, 0x01, 0x7a, 0x00},\n        {0x0a, 0x01, 0x7a, 0x00}, {0x0f, 0x01, 0x7a, 0x00},\n        {0x18, 0x01, 0x7a, 0x00}, {0x1f, 0x01, 0x7a, 0x00},\n        {0x29, 0x01, 0x7a, 0x00}, {0x38, 0x01, 0x7a, 0x01}\n    },\n    {\n        {0x01, 0x01, 0x26, 0x00}, {0x16, 0x01, 0x26, 0x01},\n        {0x01, 0x01, 0x2a, 0x00}, {0x16, 0x01, 0x2a, 0x01},\n        {0x01, 0x01, 0x2c, 0x00}, {0x16, 0x01, 0x2c, 0x01},\n        {0x01, 0x01, 0x3b, 0x00}, {0x16, 0x01, 0x3b, 0x01},\n        {0x01, 0x01, 0x58, 0x00}, {0x16, 0x01, 0x58, 0x01},\n        {0x01, 0x01, 0x5a, 0x00}, {0x16, 0x01, 0x5a, 0x01},\n        {0x4c, 0x00, 0x00, 0x00}, {0x4d, 0x00, 0x00, 0x00},\n        {0x4f, 0x00, 0x00, 0x00}, {0x51, 0x00, 0x00, 0x01}\n    },\n    {\n        {0x02, 0x01, 0x26, 0x00}, {0x09, 0x01, 0x26, 0x00},\n        {0x17, 0x01, 0x26, 0x00}, {0x28, 0x01, 0x26, 0x01},\n        {0x02, 0x01, 0x2a, 0x00}, {0x09, 0x01, 0x2a, 0x00},\n        {0x17, 0x01, 0x2a, 0x00}, {0x28, 0x01, 0x2a, 0x01},\n        {0x02, 0x01, 0x2c, 0x00}, {0x09, 0x01, 0x2c, 0x00},\n        {0x17, 0x01, 0x2c, 0x00}, {0x28, 0x01, 0x2c, 0x01},\n        {0x02, 0x01, 0x3b, 0x00}, {0x09, 0x01, 0x3b, 0x00},\n        {0x17, 0x01, 0x3b, 0x00}, {0x28, 0x01, 0x3b, 0x01}\n    },\n    /* 70 */\n    {\n        {0x03, 0x01, 0x26, 0x00}, {0x06, 0x01, 0x26, 0x00},\n        {0x0a, 0x01, 0x26, 0x00}, {0x0f, 0x01, 0x26, 0x00},\n        {0x18, 0x01, 0x26, 0x00}, {0x1f, 0x01, 0x26, 0x00},\n        {0x29, 0x01, 0x26, 0x00}, {0x38, 0x01, 0x26, 0x01},\n        {0x03, 0x01, 0x2a, 0x00}, {0x06, 0x01, 0x2a, 0x00},\n        {0x0a, 0x01, 0x2a, 0x00}, {0x0f, 0x01, 0x2a, 0x00},\n        {0x18, 0x01, 0x2a, 0x00}, {0x1f, 0x01, 0x2a, 0x00},\n        {0x29, 0x01, 0x2a, 0x00}, {0x38, 0x01, 0x2a, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x2c, 0x00}, {0x06, 0x01, 0x2c, 0x00},\n        {0x0a, 0x01, 0x2c, 0x00}, {0x0f, 0x01, 0x2c, 0x00},\n        {0x18, 0x01, 0x2c, 0x00}, {0x1f, 0x01, 0x2c, 0x00},\n        {0x29, 0x01, 0x2c, 0x00}, {0x38, 0x01, 0x2c, 0x01},\n        {0x03, 0x01, 0x3b, 0x00}, {0x06, 0x01, 0x3b, 0x00},\n        {0x0a, 0x01, 0x3b, 0x00}, {0x0f, 0x01, 0x3b, 0x00},\n        {0x18, 0x01, 0x3b, 0x00}, {0x1f, 0x01, 0x3b, 0x00},\n        {0x29, 0x01, 0x3b, 0x00}, {0x38, 0x01, 0x3b, 0x01}\n    },\n    {\n        {0x02, 0x01, 0x58, 0x00}, {0x09, 0x01, 0x58, 0x00},\n        {0x17, 0x01, 0x58, 0x00}, {0x28, 0x01, 0x58, 0x01},\n        {0x02, 0x01, 0x5a, 0x00}, {0x09, 0x01, 0x5a, 0x00},\n        {0x17, 0x01, 0x5a, 0x00}, {0x28, 0x01, 0x5a, 0x01},\n        {0x00, 0x01, 0x21, 0x01}, {0x00, 0x01, 0x22, 0x01},\n        {0x00, 0x01, 0x28, 0x01}, {0x00, 0x01, 0x29, 0x01},\n        {0x00, 0x01, 0x3f, 0x01}, {0x50, 0x00, 0x00, 0x00},\n        {0x52, 0x00, 0x00, 0x00}, {0x54, 0x00, 0x00, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x58, 0x00}, {0x06, 0x01, 0x58, 0x00},\n        {0x0a, 0x01, 0x58, 0x00}, {0x0f, 0x01, 0x58, 0x00},\n        {0x18, 0x01, 0x58, 0x00}, {0x1f, 0x01, 0x58, 0x00},\n        {0x29, 0x01, 0x58, 0x00}, {0x38, 0x01, 0x58, 0x01},\n        {0x03, 0x01, 0x5a, 0x00}, {0x06, 0x01, 0x5a, 0x00},\n        {0x0a, 0x01, 0x5a, 0x00}, {0x0f, 0x01, 0x5a, 0x00},\n        {0x18, 0x01, 0x5a, 0x00}, {0x1f, 0x01, 0x5a, 0x00},\n        {0x29, 0x01, 0x5a, 0x00}, {0x38, 0x01, 0x5a, 0x01}\n    },\n    {\n        {0x01, 0x01, 0x21, 0x00}, {0x16, 0x01, 0x21, 0x01},\n        {0x01, 0x01, 0x22, 0x00}, {0x16, 0x01, 0x22, 0x01},\n        {0x01, 0x01, 0x28, 0x00}, {0x16, 0x01, 0x28, 0x01},\n        {0x01, 0x01, 0x29, 0x00}, {0x16, 0x01, 0x29, 0x01},\n        {0x01, 0x01, 0x3f, 0x00}, {0x16, 0x01, 0x3f, 0x01},\n        {0x00, 0x01, 0x27, 0x01}, {0x00, 0x01, 0x2b, 0x01},\n        {0x00, 0x01, 0x7c, 0x01}, {0x53, 0x00, 0x00, 0x00},\n        {0x55, 0x00, 0x00, 0x00}, {0x58, 0x00, 0x00, 0x01}\n    },\n    /* 75 */\n    {\n        {0x02, 0x01, 0x21, 0x00}, {0x09, 0x01, 0x21, 0x00},\n        {0x17, 0x01, 0x21, 0x00}, {0x28, 0x01, 0x21, 0x01},\n        {0x02, 0x01, 0x22, 0x00}, {0x09, 0x01, 0x22, 0x00},\n        {0x17, 0x01, 0x22, 0x00}, {0x28, 0x01, 0x22, 0x01},\n        {0x02, 0x01, 0x28, 0x00}, {0x09, 0x01, 0x28, 0x00},\n        {0x17, 0x01, 0x28, 0x00}, {0x28, 0x01, 0x28, 0x01},\n        {0x02, 0x01, 0x29, 0x00}, {0x09, 0x01, 0x29, 0x00},\n        {0x17, 0x01, 0x29, 0x00}, {0x28, 0x01, 0x29, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x21, 0x00}, {0x06, 0x01, 0x21, 0x00},\n        {0x0a, 0x01, 0x21, 0x00}, {0x0f, 0x01, 0x21, 0x00},\n        {0x18, 0x01, 0x21, 0x00}, {0x1f, 0x01, 0x21, 0x00},\n        {0x29, 0x01, 0x21, 0x00}, {0x38, 0x01, 0x21, 0x01},\n        {0x03, 0x01, 0x22, 0x00}, {0x06, 0x01, 0x22, 0x00},\n        {0x0a, 0x01, 0x22, 0x00}, {0x0f, 0x01, 0x22, 0x00},\n        {0x18, 0x01, 0x22, 0x00}, {0x1f, 0x01, 0x22, 0x00},\n        {0x29, 0x01, 0x22, 0x00}, {0x38, 0x01, 0x22, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x28, 0x00}, {0x06, 0x01, 0x28, 0x00},\n        {0x0a, 0x01, 0x28, 0x00}, {0x0f, 0x01, 0x28, 0x00},\n        {0x18, 0x01, 0x28, 0x00}, {0x1f, 0x01, 0x28, 0x00},\n        {0x29, 0x01, 0x28, 0x00}, {0x38, 0x01, 0x28, 0x01},\n        {0x03, 0x01, 0x29, 0x00}, {0x06, 0x01, 0x29, 0x00},\n        {0x0a, 0x01, 0x29, 0x00}, {0x0f, 0x01, 0x29, 0x00},\n        {0x18, 0x01, 0x29, 0x00}, {0x1f, 0x01, 0x29, 0x00},\n        {0x29, 0x01, 0x29, 0x00}, {0x38, 0x01, 0x29, 0x01}\n    },\n    {\n        {0x02, 0x01, 0x3f, 0x00}, {0x09, 0x01, 0x3f, 0x00},\n        {0x17, 0x01, 0x3f, 0x00}, {0x28, 0x01, 0x3f, 0x01},\n        {0x01, 0x01, 0x27, 0x00}, {0x16, 0x01, 0x27, 0x01},\n        {0x01, 0x01, 0x2b, 0x00}, {0x16, 0x01, 0x2b, 0x01},\n        {0x01, 0x01, 0x7c, 0x00}, {0x16, 0x01, 0x7c, 0x01},\n        {0x00, 0x01, 0x23, 0x01}, {0x00, 0x01, 0x3e, 0x01},\n        {0x56, 0x00, 0x00, 0x00}, {0x57, 0x00, 0x00, 0x00},\n        {0x59, 0x00, 0x00, 0x00}, {0x5a, 0x00, 0x00, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x3f, 0x00}, {0x06, 0x01, 0x3f, 0x00},\n        {0x0a, 0x01, 0x3f, 0x00}, {0x0f, 0x01, 0x3f, 0x00},\n        {0x18, 0x01, 0x3f, 0x00}, {0x1f, 0x01, 0x3f, 0x00},\n        {0x29, 0x01, 0x3f, 0x00}, {0x38, 0x01, 0x3f, 0x01},\n        {0x02, 0x01, 0x27, 0x00}, {0x09, 0x01, 0x27, 0x00},\n        {0x17, 0x01, 0x27, 0x00}, {0x28, 0x01, 0x27, 0x01},\n        {0x02, 0x01, 0x2b, 0x00}, {0x09, 0x01, 0x2b, 0x00},\n        {0x17, 0x01, 0x2b, 0x00}, {0x28, 0x01, 0x2b, 0x01}\n    },\n    /* 80 */\n    {\n        {0x03, 0x01, 0x27, 0x00}, {0x06, 0x01, 0x27, 0x00},\n        {0x0a, 0x01, 0x27, 0x00}, {0x0f, 0x01, 0x27, 0x00},\n        {0x18, 0x01, 0x27, 0x00}, {0x1f, 0x01, 0x27, 0x00},\n        {0x29, 0x01, 0x27, 0x00}, {0x38, 0x01, 0x27, 0x01},\n        {0x03, 0x01, 0x2b, 0x00}, {0x06, 0x01, 0x2b, 0x00},\n        {0x0a, 0x01, 0x2b, 0x00}, {0x0f, 0x01, 0x2b, 0x00},\n        {0x18, 0x01, 0x2b, 0x00}, {0x1f, 0x01, 0x2b, 0x00},\n        {0x29, 0x01, 0x2b, 0x00}, {0x38, 0x01, 0x2b, 0x01}\n    },\n    {\n        {0x02, 0x01, 0x7c, 0x00}, {0x09, 0x01, 0x7c, 0x00},\n        {0x17, 0x01, 0x7c, 0x00}, {0x28, 0x01, 0x7c, 0x01},\n        {0x01, 0x01, 0x23, 0x00}, {0x16, 0x01, 0x23, 0x01},\n        {0x01, 0x01, 0x3e, 0x00}, {0x16, 0x01, 0x3e, 0x01},\n        {0x00, 0x01, 0x00, 0x01}, {0x00, 0x01, 0x24, 0x01},\n        {0x00, 0x01, 0x40, 0x01}, {0x00, 0x01, 0x5b, 0x01},\n        {0x00, 0x01, 0x5d, 0x01}, {0x00, 0x01, 0x7e, 0x01},\n        {0x5b, 0x00, 0x00, 0x00}, {0x5c, 0x00, 0x00, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x7c, 0x00}, {0x06, 0x01, 0x7c, 0x00},\n        {0x0a, 0x01, 0x7c, 0x00}, {0x0f, 0x01, 0x7c, 0x00},\n        {0x18, 0x01, 0x7c, 0x00}, {0x1f, 0x01, 0x7c, 0x00},\n        {0x29, 0x01, 0x7c, 0x00}, {0x38, 0x01, 0x7c, 0x01},\n        {0x02, 0x01, 0x23, 0x00}, {0x09, 0x01, 0x23, 0x00},\n        {0x17, 0x01, 0x23, 0x00}, {0x28, 0x01, 0x23, 0x01},\n        {0x02, 0x01, 0x3e, 0x00}, {0x09, 0x01, 0x3e, 0x00},\n        {0x17, 0x01, 0x3e, 0x00}, {0x28, 0x01, 0x3e, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x23, 0x00}, {0x06, 0x01, 0x23, 0x00},\n        {0x0a, 0x01, 0x23, 0x00}, {0x0f, 0x01, 0x23, 0x00},\n        {0x18, 0x01, 0x23, 0x00}, {0x1f, 0x01, 0x23, 0x00},\n        {0x29, 0x01, 0x23, 0x00}, {0x38, 0x01, 0x23, 0x01},\n        {0x03, 0x01, 0x3e, 0x00}, {0x06, 0x01, 0x3e, 0x00},\n        {0x0a, 0x01, 0x3e, 0x00}, {0x0f, 0x01, 0x3e, 0x00},\n        {0x18, 0x01, 0x3e, 0x00}, {0x1f, 0x01, 0x3e, 0x00},\n        {0x29, 0x01, 0x3e, 0x00}, {0x38, 0x01, 0x3e, 0x01}\n    },\n    {\n        {0x01, 0x01, 0x00, 0x00}, {0x16, 0x01, 0x00, 0x01},\n        {0x01, 0x01, 0x24, 0x00}, {0x16, 0x01, 0x24, 0x01},\n        {0x01, 0x01, 0x40, 0x00}, {0x16, 0x01, 0x40, 0x01},\n        {0x01, 0x01, 0x5b, 0x00}, {0x16, 0x01, 0x5b, 0x01},\n        {0x01, 0x01, 0x5d, 0x00}, {0x16, 0x01, 0x5d, 0x01},\n        {0x01, 0x01, 0x7e, 0x00}, {0x16, 0x01, 0x7e, 0x01},\n        {0x00, 0x01, 0x5e, 0x01}, {0x00, 0x01, 0x7d, 0x01},\n        {0x5d, 0x00, 0x00, 0x00}, {0x5e, 0x00, 0x00, 0x01}\n    },\n    /* 85 */\n    {\n        {0x02, 0x01, 0x00, 0x00}, {0x09, 0x01, 0x00, 0x00},\n        {0x17, 0x01, 0x00, 0x00}, {0x28, 0x01, 0x00, 0x01},\n        {0x02, 0x01, 0x24, 0x00}, {0x09, 0x01, 0x24, 0x00},\n        {0x17, 0x01, 0x24, 0x00}, {0x28, 0x01, 0x24, 0x01},\n        {0x02, 0x01, 0x40, 0x00}, {0x09, 0x01, 0x40, 0x00},\n        {0x17, 0x01, 0x40, 0x00}, {0x28, 0x01, 0x40, 0x01},\n        {0x02, 0x01, 0x5b, 0x00}, {0x09, 0x01, 0x5b, 0x00},\n        {0x17, 0x01, 0x5b, 0x00}, {0x28, 0x01, 0x5b, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x00, 0x00}, {0x06, 0x01, 0x00, 0x00},\n        {0x0a, 0x01, 0x00, 0x00}, {0x0f, 0x01, 0x00, 0x00},\n        {0x18, 0x01, 0x00, 0x00}, {0x1f, 0x01, 0x00, 0x00},\n        {0x29, 0x01, 0x00, 0x00}, {0x38, 0x01, 0x00, 0x01},\n        {0x03, 0x01, 0x24, 0x00}, {0x06, 0x01, 0x24, 0x00},\n        {0x0a, 0x01, 0x24, 0x00}, {0x0f, 0x01, 0x24, 0x00},\n        {0x18, 0x01, 0x24, 0x00}, {0x1f, 0x01, 0x24, 0x00},\n        {0x29, 0x01, 0x24, 0x00}, {0x38, 0x01, 0x24, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x40, 0x00}, {0x06, 0x01, 0x40, 0x00},\n        {0x0a, 0x01, 0x40, 0x00}, {0x0f, 0x01, 0x40, 0x00},\n        {0x18, 0x01, 0x40, 0x00}, {0x1f, 0x01, 0x40, 0x00},\n        {0x29, 0x01, 0x40, 0x00}, {0x38, 0x01, 0x40, 0x01},\n        {0x03, 0x01, 0x5b, 0x00}, {0x06, 0x01, 0x5b, 0x00},\n        {0x0a, 0x01, 0x5b, 0x00}, {0x0f, 0x01, 0x5b, 0x00},\n        {0x18, 0x01, 0x5b, 0x00}, {0x1f, 0x01, 0x5b, 0x00},\n        {0x29, 0x01, 0x5b, 0x00}, {0x38, 0x01, 0x5b, 0x01}\n    },\n    {\n        {0x02, 0x01, 0x5d, 0x00}, {0x09, 0x01, 0x5d, 0x00},\n        {0x17, 0x01, 0x5d, 0x00}, {0x28, 0x01, 0x5d, 0x01},\n        {0x02, 0x01, 0x7e, 0x00}, {0x09, 0x01, 0x7e, 0x00},\n        {0x17, 0x01, 0x7e, 0x00}, {0x28, 0x01, 0x7e, 0x01},\n        {0x01, 0x01, 0x5e, 0x00}, {0x16, 0x01, 0x5e, 0x01},\n        {0x01, 0x01, 0x7d, 0x00}, {0x16, 0x01, 0x7d, 0x01},\n        {0x00, 0x01, 0x3c, 0x01}, {0x00, 0x01, 0x60, 0x01},\n        {0x00, 0x01, 0x7b, 0x01}, {0x5f, 0x00, 0x00, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x5d, 0x00}, {0x06, 0x01, 0x5d, 0x00},\n        {0x0a, 0x01, 0x5d, 0x00}, {0x0f, 0x01, 0x5d, 0x00},\n        {0x18, 0x01, 0x5d, 0x00}, {0x1f, 0x01, 0x5d, 0x00},\n        {0x29, 0x01, 0x5d, 0x00}, {0x38, 0x01, 0x5d, 0x01},\n        {0x03, 0x01, 0x7e, 0x00}, {0x06, 0x01, 0x7e, 0x00},\n        {0x0a, 0x01, 0x7e, 0x00}, {0x0f, 0x01, 0x7e, 0x00},\n        {0x18, 0x01, 0x7e, 0x00}, {0x1f, 0x01, 0x7e, 0x00},\n        {0x29, 0x01, 0x7e, 0x00}, {0x38, 0x01, 0x7e, 0x01}\n    },\n    /* 90 */\n    {\n        {0x02, 0x01, 0x5e, 0x00}, {0x09, 0x01, 0x5e, 0x00},\n        {0x17, 0x01, 0x5e, 0x00}, {0x28, 0x01, 0x5e, 0x01},\n        {0x02, 0x01, 0x7d, 0x00}, {0x09, 0x01, 0x7d, 0x00},\n        {0x17, 0x01, 0x7d, 0x00}, {0x28, 0x01, 0x7d, 0x01},\n        {0x01, 0x01, 0x3c, 0x00}, {0x16, 0x01, 0x3c, 0x01},\n        {0x01, 0x01, 0x60, 0x00}, {0x16, 0x01, 0x60, 0x01},\n        {0x01, 0x01, 0x7b, 0x00}, {0x16, 0x01, 0x7b, 0x01},\n        {0x60, 0x00, 0x00, 0x00}, {0x6e, 0x00, 0x00, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x5e, 0x00}, {0x06, 0x01, 0x5e, 0x00},\n        {0x0a, 0x01, 0x5e, 0x00}, {0x0f, 0x01, 0x5e, 0x00},\n        {0x18, 0x01, 0x5e, 0x00}, {0x1f, 0x01, 0x5e, 0x00},\n        {0x29, 0x01, 0x5e, 0x00}, {0x38, 0x01, 0x5e, 0x01},\n        {0x03, 0x01, 0x7d, 0x00}, {0x06, 0x01, 0x7d, 0x00},\n        {0x0a, 0x01, 0x7d, 0x00}, {0x0f, 0x01, 0x7d, 0x00},\n        {0x18, 0x01, 0x7d, 0x00}, {0x1f, 0x01, 0x7d, 0x00},\n        {0x29, 0x01, 0x7d, 0x00}, {0x38, 0x01, 0x7d, 0x01}\n    },\n    {\n        {0x02, 0x01, 0x3c, 0x00}, {0x09, 0x01, 0x3c, 0x00},\n        {0x17, 0x01, 0x3c, 0x00}, {0x28, 0x01, 0x3c, 0x01},\n        {0x02, 0x01, 0x60, 0x00}, {0x09, 0x01, 0x60, 0x00},\n        {0x17, 0x01, 0x60, 0x00}, {0x28, 0x01, 0x60, 0x01},\n        {0x02, 0x01, 0x7b, 0x00}, {0x09, 0x01, 0x7b, 0x00},\n        {0x17, 0x01, 0x7b, 0x00}, {0x28, 0x01, 0x7b, 0x01},\n        {0x61, 0x00, 0x00, 0x00}, {0x65, 0x00, 0x00, 0x00},\n        {0x6f, 0x00, 0x00, 0x00}, {0x85, 0x00, 0x00, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x3c, 0x00}, {0x06, 0x01, 0x3c, 0x00},\n        {0x0a, 0x01, 0x3c, 0x00}, {0x0f, 0x01, 0x3c, 0x00},\n        {0x18, 0x01, 0x3c, 0x00}, {0x1f, 0x01, 0x3c, 0x00},\n        {0x29, 0x01, 0x3c, 0x00}, {0x38, 0x01, 0x3c, 0x01},\n        {0x03, 0x01, 0x60, 0x00}, {0x06, 0x01, 0x60, 0x00},\n        {0x0a, 0x01, 0x60, 0x00}, {0x0f, 0x01, 0x60, 0x00},\n        {0x18, 0x01, 0x60, 0x00}, {0x1f, 0x01, 0x60, 0x00},\n        {0x29, 0x01, 0x60, 0x00}, {0x38, 0x01, 0x60, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x7b, 0x00}, {0x06, 0x01, 0x7b, 0x00},\n        {0x0a, 0x01, 0x7b, 0x00}, {0x0f, 0x01, 0x7b, 0x00},\n        {0x18, 0x01, 0x7b, 0x00}, {0x1f, 0x01, 0x7b, 0x00},\n        {0x29, 0x01, 0x7b, 0x00}, {0x38, 0x01, 0x7b, 0x01},\n        {0x62, 0x00, 0x00, 0x00}, {0x63, 0x00, 0x00, 0x00},\n        {0x66, 0x00, 0x00, 0x00}, {0x69, 0x00, 0x00, 0x00},\n        {0x70, 0x00, 0x00, 0x00}, {0x77, 0x00, 0x00, 0x00},\n        {0x86, 0x00, 0x00, 0x00}, {0x99, 0x00, 0x00, 0x01}\n    },\n    /* 95 */\n    {\n        {0x00, 0x01, 0x5c, 0x01}, {0x00, 0x01, 0xc3, 0x01},\n        {0x00, 0x01, 0xd0, 0x01}, {0x64, 0x00, 0x00, 0x00},\n        {0x67, 0x00, 0x00, 0x00}, {0x68, 0x00, 0x00, 0x00},\n        {0x6a, 0x00, 0x00, 0x00}, {0x6b, 0x00, 0x00, 0x00},\n        {0x71, 0x00, 0x00, 0x00}, {0x74, 0x00, 0x00, 0x00},\n        {0x78, 0x00, 0x00, 0x00}, {0x7e, 0x00, 0x00, 0x00},\n        {0x87, 0x00, 0x00, 0x00}, {0x8e, 0x00, 0x00, 0x00},\n        {0x9a, 0x00, 0x00, 0x00}, {0xa9, 0x00, 0x00, 0x01}\n    },\n    {\n        {0x01, 0x01, 0x5c, 0x00}, {0x16, 0x01, 0x5c, 0x01},\n        {0x01, 0x01, 0xc3, 0x00}, {0x16, 0x01, 0xc3, 0x01},\n        {0x01, 0x01, 0xd0, 0x00}, {0x16, 0x01, 0xd0, 0x01},\n        {0x00, 0x01, 0x80, 0x01}, {0x00, 0x01, 0x82, 0x01},\n        {0x00, 0x01, 0x83, 0x01}, {0x00, 0x01, 0xa2, 0x01},\n        {0x00, 0x01, 0xb8, 0x01}, {0x00, 0x01, 0xc2, 0x01},\n        {0x00, 0x01, 0xe0, 0x01}, {0x00, 0x01, 0xe2, 0x01},\n        {0x6c, 0x00, 0x00, 0x00}, {0x6d, 0x00, 0x00, 0x00}\n    },\n    {\n        {0x02, 0x01, 0x5c, 0x00}, {0x09, 0x01, 0x5c, 0x00},\n        {0x17, 0x01, 0x5c, 0x00}, {0x28, 0x01, 0x5c, 0x01},\n        {0x02, 0x01, 0xc3, 0x00}, {0x09, 0x01, 0xc3, 0x00},\n        {0x17, 0x01, 0xc3, 0x00}, {0x28, 0x01, 0xc3, 0x01},\n        {0x02, 0x01, 0xd0, 0x00}, {0x09, 0x01, 0xd0, 0x00},\n        {0x17, 0x01, 0xd0, 0x00}, {0x28, 0x01, 0xd0, 0x01},\n        {0x01, 0x01, 0x80, 0x00}, {0x16, 0x01, 0x80, 0x01},\n        {0x01, 0x01, 0x82, 0x00}, {0x16, 0x01, 0x82, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x5c, 0x00}, {0x06, 0x01, 0x5c, 0x00},\n        {0x0a, 0x01, 0x5c, 0x00}, {0x0f, 0x01, 0x5c, 0x00},\n        {0x18, 0x01, 0x5c, 0x00}, {0x1f, 0x01, 0x5c, 0x00},\n        {0x29, 0x01, 0x5c, 0x00}, {0x38, 0x01, 0x5c, 0x01},\n        {0x03, 0x01, 0xc3, 0x00}, {0x06, 0x01, 0xc3, 0x00},\n        {0x0a, 0x01, 0xc3, 0x00}, {0x0f, 0x01, 0xc3, 0x00},\n        {0x18, 0x01, 0xc3, 0x00}, {0x1f, 0x01, 0xc3, 0x00},\n        {0x29, 0x01, 0xc3, 0x00}, {0x38, 0x01, 0xc3, 0x01}\n    },\n    {\n        {0x03, 0x01, 0xd0, 0x00}, {0x06, 0x01, 0xd0, 0x00},\n        {0x0a, 0x01, 0xd0, 0x00}, {0x0f, 0x01, 0xd0, 0x00},\n        {0x18, 0x01, 0xd0, 0x00}, {0x1f, 0x01, 0xd0, 0x00},\n        {0x29, 0x01, 0xd0, 0x00}, {0x38, 0x01, 0xd0, 0x01},\n        {0x02, 0x01, 0x80, 0x00}, {0x09, 0x01, 0x80, 0x00},\n        {0x17, 0x01, 0x80, 0x00}, {0x28, 0x01, 0x80, 0x01},\n        {0x02, 0x01, 0x82, 0x00}, {0x09, 0x01, 0x82, 0x00},\n        {0x17, 0x01, 0x82, 0x00}, {0x28, 0x01, 0x82, 0x01}\n    },\n    /* 100 */\n    {\n        {0x03, 0x01, 0x80, 0x00}, {0x06, 0x01, 0x80, 0x00},\n        {0x0a, 0x01, 0x80, 0x00}, {0x0f, 0x01, 0x80, 0x00},\n        {0x18, 0x01, 0x80, 0x00}, {0x1f, 0x01, 0x80, 0x00},\n        {0x29, 0x01, 0x80, 0x00}, {0x38, 0x01, 0x80, 0x01},\n        {0x03, 0x01, 0x82, 0x00}, {0x06, 0x01, 0x82, 0x00},\n        {0x0a, 0x01, 0x82, 0x00}, {0x0f, 0x01, 0x82, 0x00},\n        {0x18, 0x01, 0x82, 0x00}, {0x1f, 0x01, 0x82, 0x00},\n        {0x29, 0x01, 0x82, 0x00}, {0x38, 0x01, 0x82, 0x01}\n    },\n    {\n        {0x01, 0x01, 0x83, 0x00}, {0x16, 0x01, 0x83, 0x01},\n        {0x01, 0x01, 0xa2, 0x00}, {0x16, 0x01, 0xa2, 0x01},\n        {0x01, 0x01, 0xb8, 0x00}, {0x16, 0x01, 0xb8, 0x01},\n        {0x01, 0x01, 0xc2, 0x00}, {0x16, 0x01, 0xc2, 0x01},\n        {0x01, 0x01, 0xe0, 0x00}, {0x16, 0x01, 0xe0, 0x01},\n        {0x01, 0x01, 0xe2, 0x00}, {0x16, 0x01, 0xe2, 0x01},\n        {0x00, 0x01, 0x99, 0x01}, {0x00, 0x01, 0xa1, 0x01},\n        {0x00, 0x01, 0xa7, 0x01}, {0x00, 0x01, 0xac, 0x01}\n    },\n    {\n        {0x02, 0x01, 0x83, 0x00}, {0x09, 0x01, 0x83, 0x00},\n        {0x17, 0x01, 0x83, 0x00}, {0x28, 0x01, 0x83, 0x01},\n        {0x02, 0x01, 0xa2, 0x00}, {0x09, 0x01, 0xa2, 0x00},\n        {0x17, 0x01, 0xa2, 0x00}, {0x28, 0x01, 0xa2, 0x01},\n        {0x02, 0x01, 0xb8, 0x00}, {0x09, 0x01, 0xb8, 0x00},\n        {0x17, 0x01, 0xb8, 0x00}, {0x28, 0x01, 0xb8, 0x01},\n        {0x02, 0x01, 0xc2, 0x00}, {0x09, 0x01, 0xc2, 0x00},\n        {0x17, 0x01, 0xc2, 0x00}, {0x28, 0x01, 0xc2, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x83, 0x00}, {0x06, 0x01, 0x83, 0x00},\n        {0x0a, 0x01, 0x83, 0x00}, {0x0f, 0x01, 0x83, 0x00},\n        {0x18, 0x01, 0x83, 0x00}, {0x1f, 0x01, 0x83, 0x00},\n        {0x29, 0x01, 0x83, 0x00}, {0x38, 0x01, 0x83, 0x01},\n        {0x03, 0x01, 0xa2, 0x00}, {0x06, 0x01, 0xa2, 0x00},\n        {0x0a, 0x01, 0xa2, 0x00}, {0x0f, 0x01, 0xa2, 0x00},\n        {0x18, 0x01, 0xa2, 0x00}, {0x1f, 0x01, 0xa2, 0x00},\n        {0x29, 0x01, 0xa2, 0x00}, {0x38, 0x01, 0xa2, 0x01}\n    },\n    {\n        {0x03, 0x01, 0xb8, 0x00}, {0x06, 0x01, 0xb8, 0x00},\n        {0x0a, 0x01, 0xb8, 0x00}, {0x0f, 0x01, 0xb8, 0x00},\n        {0x18, 0x01, 0xb8, 0x00}, {0x1f, 0x01, 0xb8, 0x00},\n        {0x29, 0x01, 0xb8, 0x00}, {0x38, 0x01, 0xb8, 0x01},\n        {0x03, 0x01, 0xc2, 0x00}, {0x06, 0x01, 0xc2, 0x00},\n        {0x0a, 0x01, 0xc2, 0x00}, {0x0f, 0x01, 0xc2, 0x00},\n        {0x18, 0x01, 0xc2, 0x00}, {0x1f, 0x01, 0xc2, 0x00},\n        {0x29, 0x01, 0xc2, 0x00}, {0x38, 0x01, 0xc2, 0x01}\n    },\n    /* 105 */\n    {\n        {0x02, 0x01, 0xe0, 0x00}, {0x09, 0x01, 0xe0, 0x00},\n        {0x17, 0x01, 0xe0, 0x00}, {0x28, 0x01, 0xe0, 0x01},\n        {0x02, 0x01, 0xe2, 0x00}, {0x09, 0x01, 0xe2, 0x00},\n        {0x17, 0x01, 0xe2, 0x00}, {0x28, 0x01, 0xe2, 0x01},\n        {0x01, 0x01, 0x99, 0x00}, {0x16, 0x01, 0x99, 0x01},\n        {0x01, 0x01, 0xa1, 0x00}, {0x16, 0x01, 0xa1, 0x01},\n        {0x01, 0x01, 0xa7, 0x00}, {0x16, 0x01, 0xa7, 0x01},\n        {0x01, 0x01, 0xac, 0x00}, {0x16, 0x01, 0xac, 0x01}\n    },\n    {\n        {0x03, 0x01, 0xe0, 0x00}, {0x06, 0x01, 0xe0, 0x00},\n        {0x0a, 0x01, 0xe0, 0x00}, {0x0f, 0x01, 0xe0, 0x00},\n        {0x18, 0x01, 0xe0, 0x00}, {0x1f, 0x01, 0xe0, 0x00},\n        {0x29, 0x01, 0xe0, 0x00}, {0x38, 0x01, 0xe0, 0x01},\n        {0x03, 0x01, 0xe2, 0x00}, {0x06, 0x01, 0xe2, 0x00},\n        {0x0a, 0x01, 0xe2, 0x00}, {0x0f, 0x01, 0xe2, 0x00},\n        {0x18, 0x01, 0xe2, 0x00}, {0x1f, 0x01, 0xe2, 0x00},\n        {0x29, 0x01, 0xe2, 0x00}, {0x38, 0x01, 0xe2, 0x01}\n    },\n    {\n        {0x02, 0x01, 0x99, 0x00}, {0x09, 0x01, 0x99, 0x00},\n        {0x17, 0x01, 0x99, 0x00}, {0x28, 0x01, 0x99, 0x01},\n        {0x02, 0x01, 0xa1, 0x00}, {0x09, 0x01, 0xa1, 0x00},\n        {0x17, 0x01, 0xa1, 0x00}, {0x28, 0x01, 0xa1, 0x01},\n        {0x02, 0x01, 0xa7, 0x00}, {0x09, 0x01, 0xa7, 0x00},\n        {0x17, 0x01, 0xa7, 0x00}, {0x28, 0x01, 0xa7, 0x01},\n        {0x02, 0x01, 0xac, 0x00}, {0x09, 0x01, 0xac, 0x00},\n        {0x17, 0x01, 0xac, 0x00}, {0x28, 0x01, 0xac, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x99, 0x00}, {0x06, 0x01, 0x99, 0x00},\n        {0x0a, 0x01, 0x99, 0x00}, {0x0f, 0x01, 0x99, 0x00},\n        {0x18, 0x01, 0x99, 0x00}, {0x1f, 0x01, 0x99, 0x00},\n        {0x29, 0x01, 0x99, 0x00}, {0x38, 0x01, 0x99, 0x01},\n        {0x03, 0x01, 0xa1, 0x00}, {0x06, 0x01, 0xa1, 0x00},\n        {0x0a, 0x01, 0xa1, 0x00}, {0x0f, 0x01, 0xa1, 0x00},\n        {0x18, 0x01, 0xa1, 0x00}, {0x1f, 0x01, 0xa1, 0x00},\n        {0x29, 0x01, 0xa1, 0x00}, {0x38, 0x01, 0xa1, 0x01}\n    },\n    {\n        {0x03, 0x01, 0xa7, 0x00}, {0x06, 0x01, 0xa7, 0x00},\n        {0x0a, 0x01, 0xa7, 0x00}, {0x0f, 0x01, 0xa7, 0x00},\n        {0x18, 0x01, 0xa7, 0x00}, {0x1f, 0x01, 0xa7, 0x00},\n        {0x29, 0x01, 0xa7, 0x00}, {0x38, 0x01, 0xa7, 0x01},\n        {0x03, 0x01, 0xac, 0x00}, {0x06, 0x01, 0xac, 0x00},\n        {0x0a, 0x01, 0xac, 0x00}, {0x0f, 0x01, 0xac, 0x00},\n        {0x18, 0x01, 0xac, 0x00}, {0x1f, 0x01, 0xac, 0x00},\n        {0x29, 0x01, 0xac, 0x00}, {0x38, 0x01, 0xac, 0x01}\n    },\n    /* 110 */\n    {\n        {0x72, 0x00, 0x00, 0x00}, {0x73, 0x00, 0x00, 0x00},\n        {0x75, 0x00, 0x00, 0x00}, {0x76, 0x00, 0x00, 0x00},\n        {0x79, 0x00, 0x00, 0x00}, {0x7b, 0x00, 0x00, 0x00},\n        {0x7f, 0x00, 0x00, 0x00}, {0x82, 0x00, 0x00, 0x00},\n        {0x88, 0x00, 0x00, 0x00}, {0x8b, 0x00, 0x00, 0x00},\n        {0x8f, 0x00, 0x00, 0x00}, {0x92, 0x00, 0x00, 0x00},\n        {0x9b, 0x00, 0x00, 0x00}, {0xa2, 0x00, 0x00, 0x00},\n        {0xaa, 0x00, 0x00, 0x00}, {0xb4, 0x00, 0x00, 0x01}\n    },\n    {\n        {0x00, 0x01, 0xb0, 0x01}, {0x00, 0x01, 0xb1, 0x01},\n        {0x00, 0x01, 0xb3, 0x01}, {0x00, 0x01, 0xd1, 0x01},\n        {0x00, 0x01, 0xd8, 0x01}, {0x00, 0x01, 0xd9, 0x01},\n        {0x00, 0x01, 0xe3, 0x01}, {0x00, 0x01, 0xe5, 0x01},\n        {0x00, 0x01, 0xe6, 0x01}, {0x7a, 0x00, 0x00, 0x00},\n        {0x7c, 0x00, 0x00, 0x00}, {0x7d, 0x00, 0x00, 0x00},\n        {0x80, 0x00, 0x00, 0x00}, {0x81, 0x00, 0x00, 0x00},\n        {0x83, 0x00, 0x00, 0x00}, {0x84, 0x00, 0x00, 0x00}\n    },\n    {\n        {0x01, 0x01, 0xb0, 0x00}, {0x16, 0x01, 0xb0, 0x01},\n        {0x01, 0x01, 0xb1, 0x00}, {0x16, 0x01, 0xb1, 0x01},\n        {0x01, 0x01, 0xb3, 0x00}, {0x16, 0x01, 0xb3, 0x01},\n        {0x01, 0x01, 0xd1, 0x00}, {0x16, 0x01, 0xd1, 0x01},\n        {0x01, 0x01, 0xd8, 0x00}, {0x16, 0x01, 0xd8, 0x01},\n        {0x01, 0x01, 0xd9, 0x00}, {0x16, 0x01, 0xd9, 0x01},\n        {0x01, 0x01, 0xe3, 0x00}, {0x16, 0x01, 0xe3, 0x01},\n        {0x01, 0x01, 0xe5, 0x00}, {0x16, 0x01, 0xe5, 0x01}\n    },\n    {\n        {0x02, 0x01, 0xb0, 0x00}, {0x09, 0x01, 0xb0, 0x00},\n        {0x17, 0x01, 0xb0, 0x00}, {0x28, 0x01, 0xb0, 0x01},\n        {0x02, 0x01, 0xb1, 0x00}, {0x09, 0x01, 0xb1, 0x00},\n        {0x17, 0x01, 0xb1, 0x00}, {0x28, 0x01, 0xb1, 0x01},\n        {0x02, 0x01, 0xb3, 0x00}, {0x09, 0x01, 0xb3, 0x00},\n        {0x17, 0x01, 0xb3, 0x00}, {0x28, 0x01, 0xb3, 0x01},\n        {0x02, 0x01, 0xd1, 0x00}, {0x09, 0x01, 0xd1, 0x00},\n        {0x17, 0x01, 0xd1, 0x00}, {0x28, 0x01, 0xd1, 0x01}\n    },\n    {\n        {0x03, 0x01, 0xb0, 0x00}, {0x06, 0x01, 0xb0, 0x00},\n        {0x0a, 0x01, 0xb0, 0x00}, {0x0f, 0x01, 0xb0, 0x00},\n        {0x18, 0x01, 0xb0, 0x00}, {0x1f, 0x01, 0xb0, 0x00},\n        {0x29, 0x01, 0xb0, 0x00}, {0x38, 0x01, 0xb0, 0x01},\n        {0x03, 0x01, 0xb1, 0x00}, {0x06, 0x01, 0xb1, 0x00},\n        {0x0a, 0x01, 0xb1, 0x00}, {0x0f, 0x01, 0xb1, 0x00},\n        {0x18, 0x01, 0xb1, 0x00}, {0x1f, 0x01, 0xb1, 0x00},\n        {0x29, 0x01, 0xb1, 0x00}, {0x38, 0x01, 0xb1, 0x01}\n    },\n    /* 115 */\n    {\n        {0x03, 0x01, 0xb3, 0x00}, {0x06, 0x01, 0xb3, 0x00},\n        {0x0a, 0x01, 0xb3, 0x00}, {0x0f, 0x01, 0xb3, 0x00},\n        {0x18, 0x01, 0xb3, 0x00}, {0x1f, 0x01, 0xb3, 0x00},\n        {0x29, 0x01, 0xb3, 0x00}, {0x38, 0x01, 0xb3, 0x01},\n        {0x03, 0x01, 0xd1, 0x00}, {0x06, 0x01, 0xd1, 0x00},\n        {0x0a, 0x01, 0xd1, 0x00}, {0x0f, 0x01, 0xd1, 0x00},\n        {0x18, 0x01, 0xd1, 0x00}, {0x1f, 0x01, 0xd1, 0x00},\n        {0x29, 0x01, 0xd1, 0x00}, {0x38, 0x01, 0xd1, 0x01}\n    },\n    {\n        {0x02, 0x01, 0xd8, 0x00}, {0x09, 0x01, 0xd8, 0x00},\n        {0x17, 0x01, 0xd8, 0x00}, {0x28, 0x01, 0xd8, 0x01},\n        {0x02, 0x01, 0xd9, 0x00}, {0x09, 0x01, 0xd9, 0x00},\n        {0x17, 0x01, 0xd9, 0x00}, {0x28, 0x01, 0xd9, 0x01},\n        {0x02, 0x01, 0xe3, 0x00}, {0x09, 0x01, 0xe3, 0x00},\n        {0x17, 0x01, 0xe3, 0x00}, {0x28, 0x01, 0xe3, 0x01},\n        {0x02, 0x01, 0xe5, 0x00}, {0x09, 0x01, 0xe5, 0x00},\n        {0x17, 0x01, 0xe5, 0x00}, {0x28, 0x01, 0xe5, 0x01}\n    },\n    {\n        {0x03, 0x01, 0xd8, 0x00}, {0x06, 0x01, 0xd8, 0x00},\n        {0x0a, 0x01, 0xd8, 0x00}, {0x0f, 0x01, 0xd8, 0x00},\n        {0x18, 0x01, 0xd8, 0x00}, {0x1f, 0x01, 0xd8, 0x00},\n        {0x29, 0x01, 0xd8, 0x00}, {0x38, 0x01, 0xd8, 0x01},\n        {0x03, 0x01, 0xd9, 0x00}, {0x06, 0x01, 0xd9, 0x00},\n        {0x0a, 0x01, 0xd9, 0x00}, {0x0f, 0x01, 0xd9, 0x00},\n        {0x18, 0x01, 0xd9, 0x00}, {0x1f, 0x01, 0xd9, 0x00},\n        {0x29, 0x01, 0xd9, 0x00}, {0x38, 0x01, 0xd9, 0x01}\n    },\n    {\n        {0x03, 0x01, 0xe3, 0x00}, {0x06, 0x01, 0xe3, 0x00},\n        {0x0a, 0x01, 0xe3, 0x00}, {0x0f, 0x01, 0xe3, 0x00},\n        {0x18, 0x01, 0xe3, 0x00}, {0x1f, 0x01, 0xe3, 0x00},\n        {0x29, 0x01, 0xe3, 0x00}, {0x38, 0x01, 0xe3, 0x01},\n        {0x03, 0x01, 0xe5, 0x00}, {0x06, 0x01, 0xe5, 0x00},\n        {0x0a, 0x01, 0xe5, 0x00}, {0x0f, 0x01, 0xe5, 0x00},\n        {0x18, 0x01, 0xe5, 0x00}, {0x1f, 0x01, 0xe5, 0x00},\n        {0x29, 0x01, 0xe5, 0x00}, {0x38, 0x01, 0xe5, 0x01}\n    },\n    {\n        {0x01, 0x01, 0xe6, 0x00}, {0x16, 0x01, 0xe6, 0x01},\n        {0x00, 0x01, 0x81, 0x01}, {0x00, 0x01, 0x84, 0x01},\n        {0x00, 0x01, 0x85, 0x01}, {0x00, 0x01, 0x86, 0x01},\n        {0x00, 0x01, 0x88, 0x01}, {0x00, 0x01, 0x92, 0x01},\n        {0x00, 0x01, 0x9a, 0x01}, {0x00, 0x01, 0x9c, 0x01},\n        {0x00, 0x01, 0xa0, 0x01}, {0x00, 0x01, 0xa3, 0x01},\n        {0x00, 0x01, 0xa4, 0x01}, {0x00, 0x01, 0xa9, 0x01},\n        {0x00, 0x01, 0xaa, 0x01}, {0x00, 0x01, 0xad, 0x01}\n    },\n    /* 120 */\n    {\n        {0x02, 0x01, 0xe6, 0x00}, {0x09, 0x01, 0xe6, 0x00},\n        {0x17, 0x01, 0xe6, 0x00}, {0x28, 0x01, 0xe6, 0x01},\n        {0x01, 0x01, 0x81, 0x00}, {0x16, 0x01, 0x81, 0x01},\n        {0x01, 0x01, 0x84, 0x00}, {0x16, 0x01, 0x84, 0x01},\n        {0x01, 0x01, 0x85, 0x00}, {0x16, 0x01, 0x85, 0x01},\n        {0x01, 0x01, 0x86, 0x00}, {0x16, 0x01, 0x86, 0x01},\n        {0x01, 0x01, 0x88, 0x00}, {0x16, 0x01, 0x88, 0x01},\n        {0x01, 0x01, 0x92, 0x00}, {0x16, 0x01, 0x92, 0x01}\n    },\n    {\n        {0x03, 0x01, 0xe6, 0x00}, {0x06, 0x01, 0xe6, 0x00},\n        {0x0a, 0x01, 0xe6, 0x00}, {0x0f, 0x01, 0xe6, 0x00},\n        {0x18, 0x01, 0xe6, 0x00}, {0x1f, 0x01, 0xe6, 0x00},\n        {0x29, 0x01, 0xe6, 0x00}, {0x38, 0x01, 0xe6, 0x01},\n        {0x02, 0x01, 0x81, 0x00}, {0x09, 0x01, 0x81, 0x00},\n        {0x17, 0x01, 0x81, 0x00}, {0x28, 0x01, 0x81, 0x01},\n        {0x02, 0x01, 0x84, 0x00}, {0x09, 0x01, 0x84, 0x00},\n        {0x17, 0x01, 0x84, 0x00}, {0x28, 0x01, 0x84, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x81, 0x00}, {0x06, 0x01, 0x81, 0x00},\n        {0x0a, 0x01, 0x81, 0x00}, {0x0f, 0x01, 0x81, 0x00},\n        {0x18, 0x01, 0x81, 0x00}, {0x1f, 0x01, 0x81, 0x00},\n        {0x29, 0x01, 0x81, 0x00}, {0x38, 0x01, 0x81, 0x01},\n        {0x03, 0x01, 0x84, 0x00}, {0x06, 0x01, 0x84, 0x00},\n        {0x0a, 0x01, 0x84, 0x00}, {0x0f, 0x01, 0x84, 0x00},\n        {0x18, 0x01, 0x84, 0x00}, {0x1f, 0x01, 0x84, 0x00},\n        {0x29, 0x01, 0x84, 0x00}, {0x38, 0x01, 0x84, 0x01}\n    },\n    {\n        {0x02, 0x01, 0x85, 0x00}, {0x09, 0x01, 0x85, 0x00},\n        {0x17, 0x01, 0x85, 0x00}, {0x28, 0x01, 0x85, 0x01},\n        {0x02, 0x01, 0x86, 0x00}, {0x09, 0x01, 0x86, 0x00},\n        {0x17, 0x01, 0x86, 0x00}, {0x28, 0x01, 0x86, 0x01},\n        {0x02, 0x01, 0x88, 0x00}, {0x09, 0x01, 0x88, 0x00},\n        {0x17, 0x01, 0x88, 0x00}, {0x28, 0x01, 0x88, 0x01},\n        {0x02, 0x01, 0x92, 0x00}, {0x09, 0x01, 0x92, 0x00},\n        {0x17, 0x01, 0x92, 0x00}, {0x28, 0x01, 0x92, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x85, 0x00}, {0x06, 0x01, 0x85, 0x00},\n        {0x0a, 0x01, 0x85, 0x00}, {0x0f, 0x01, 0x85, 0x00},\n        {0x18, 0x01, 0x85, 0x00}, {0x1f, 0x01, 0x85, 0x00},\n        {0x29, 0x01, 0x85, 0x00}, {0x38, 0x01, 0x85, 0x01},\n        {0x03, 0x01, 0x86, 0x00}, {0x06, 0x01, 0x86, 0x00},\n        {0x0a, 0x01, 0x86, 0x00}, {0x0f, 0x01, 0x86, 0x00},\n        {0x18, 0x01, 0x86, 0x00}, {0x1f, 0x01, 0x86, 0x00},\n        {0x29, 0x01, 0x86, 0x00}, {0x38, 0x01, 0x86, 0x01}\n    },\n    /* 125 */\n    {\n        {0x03, 0x01, 0x88, 0x00}, {0x06, 0x01, 0x88, 0x00},\n        {0x0a, 0x01, 0x88, 0x00}, {0x0f, 0x01, 0x88, 0x00},\n        {0x18, 0x01, 0x88, 0x00}, {0x1f, 0x01, 0x88, 0x00},\n        {0x29, 0x01, 0x88, 0x00}, {0x38, 0x01, 0x88, 0x01},\n        {0x03, 0x01, 0x92, 0x00}, {0x06, 0x01, 0x92, 0x00},\n        {0x0a, 0x01, 0x92, 0x00}, {0x0f, 0x01, 0x92, 0x00},\n        {0x18, 0x01, 0x92, 0x00}, {0x1f, 0x01, 0x92, 0x00},\n        {0x29, 0x01, 0x92, 0x00}, {0x38, 0x01, 0x92, 0x01}\n    },\n    {\n        {0x01, 0x01, 0x9a, 0x00}, {0x16, 0x01, 0x9a, 0x01},\n        {0x01, 0x01, 0x9c, 0x00}, {0x16, 0x01, 0x9c, 0x01},\n        {0x01, 0x01, 0xa0, 0x00}, {0x16, 0x01, 0xa0, 0x01},\n        {0x01, 0x01, 0xa3, 0x00}, {0x16, 0x01, 0xa3, 0x01},\n        {0x01, 0x01, 0xa4, 0x00}, {0x16, 0x01, 0xa4, 0x01},\n        {0x01, 0x01, 0xa9, 0x00}, {0x16, 0x01, 0xa9, 0x01},\n        {0x01, 0x01, 0xaa, 0x00}, {0x16, 0x01, 0xaa, 0x01},\n        {0x01, 0x01, 0xad, 0x00}, {0x16, 0x01, 0xad, 0x01}\n    },\n    {\n        {0x02, 0x01, 0x9a, 0x00}, {0x09, 0x01, 0x9a, 0x00},\n        {0x17, 0x01, 0x9a, 0x00}, {0x28, 0x01, 0x9a, 0x01},\n        {0x02, 0x01, 0x9c, 0x00}, {0x09, 0x01, 0x9c, 0x00},\n        {0x17, 0x01, 0x9c, 0x00}, {0x28, 0x01, 0x9c, 0x01},\n        {0x02, 0x01, 0xa0, 0x00}, {0x09, 0x01, 0xa0, 0x00},\n        {0x17, 0x01, 0xa0, 0x00}, {0x28, 0x01, 0xa0, 0x01},\n        {0x02, 0x01, 0xa3, 0x00}, {0x09, 0x01, 0xa3, 0x00},\n        {0x17, 0x01, 0xa3, 0x00}, {0x28, 0x01, 0xa3, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x9a, 0x00}, {0x06, 0x01, 0x9a, 0x00},\n        {0x0a, 0x01, 0x9a, 0x00}, {0x0f, 0x01, 0x9a, 0x00},\n        {0x18, 0x01, 0x9a, 0x00}, {0x1f, 0x01, 0x9a, 0x00},\n        {0x29, 0x01, 0x9a, 0x00}, {0x38, 0x01, 0x9a, 0x01},\n        {0x03, 0x01, 0x9c, 0x00}, {0x06, 0x01, 0x9c, 0x00},\n        {0x0a, 0x01, 0x9c, 0x00}, {0x0f, 0x01, 0x9c, 0x00},\n        {0x18, 0x01, 0x9c, 0x00}, {0x1f, 0x01, 0x9c, 0x00},\n        {0x29, 0x01, 0x9c, 0x00}, {0x38, 0x01, 0x9c, 0x01}\n    },\n    {\n        {0x03, 0x01, 0xa0, 0x00}, {0x06, 0x01, 0xa0, 0x00},\n        {0x0a, 0x01, 0xa0, 0x00}, {0x0f, 0x01, 0xa0, 0x00},\n        {0x18, 0x01, 0xa0, 0x00}, {0x1f, 0x01, 0xa0, 0x00},\n        {0x29, 0x01, 0xa0, 0x00}, {0x38, 0x01, 0xa0, 0x01},\n        {0x03, 0x01, 0xa3, 0x00}, {0x06, 0x01, 0xa3, 0x00},\n        {0x0a, 0x01, 0xa3, 0x00}, {0x0f, 0x01, 0xa3, 0x00},\n        {0x18, 0x01, 0xa3, 0x00}, {0x1f, 0x01, 0xa3, 0x00},\n        {0x29, 0x01, 0xa3, 0x00}, {0x38, 0x01, 0xa3, 0x01}\n    },\n    /* 130 */\n    {\n        {0x02, 0x01, 0xa4, 0x00}, {0x09, 0x01, 0xa4, 0x00},\n        {0x17, 0x01, 0xa4, 0x00}, {0x28, 0x01, 0xa4, 0x01},\n        {0x02, 0x01, 0xa9, 0x00}, {0x09, 0x01, 0xa9, 0x00},\n        {0x17, 0x01, 0xa9, 0x00}, {0x28, 0x01, 0xa9, 0x01},\n        {0x02, 0x01, 0xaa, 0x00}, {0x09, 0x01, 0xaa, 0x00},\n        {0x17, 0x01, 0xaa, 0x00}, {0x28, 0x01, 0xaa, 0x01},\n        {0x02, 0x01, 0xad, 0x00}, {0x09, 0x01, 0xad, 0x00},\n        {0x17, 0x01, 0xad, 0x00}, {0x28, 0x01, 0xad, 0x01}\n    },\n    {\n        {0x03, 0x01, 0xa4, 0x00}, {0x06, 0x01, 0xa4, 0x00},\n        {0x0a, 0x01, 0xa4, 0x00}, {0x0f, 0x01, 0xa4, 0x00},\n        {0x18, 0x01, 0xa4, 0x00}, {0x1f, 0x01, 0xa4, 0x00},\n        {0x29, 0x01, 0xa4, 0x00}, {0x38, 0x01, 0xa4, 0x01},\n        {0x03, 0x01, 0xa9, 0x00}, {0x06, 0x01, 0xa9, 0x00},\n        {0x0a, 0x01, 0xa9, 0x00}, {0x0f, 0x01, 0xa9, 0x00},\n        {0x18, 0x01, 0xa9, 0x00}, {0x1f, 0x01, 0xa9, 0x00},\n        {0x29, 0x01, 0xa9, 0x00}, {0x38, 0x01, 0xa9, 0x01}\n    },\n    {\n        {0x03, 0x01, 0xaa, 0x00}, {0x06, 0x01, 0xaa, 0x00},\n        {0x0a, 0x01, 0xaa, 0x00}, {0x0f, 0x01, 0xaa, 0x00},\n        {0x18, 0x01, 0xaa, 0x00}, {0x1f, 0x01, 0xaa, 0x00},\n        {0x29, 0x01, 0xaa, 0x00}, {0x38, 0x01, 0xaa, 0x01},\n        {0x03, 0x01, 0xad, 0x00}, {0x06, 0x01, 0xad, 0x00},\n        {0x0a, 0x01, 0xad, 0x00}, {0x0f, 0x01, 0xad, 0x00},\n        {0x18, 0x01, 0xad, 0x00}, {0x1f, 0x01, 0xad, 0x00},\n        {0x29, 0x01, 0xad, 0x00}, {0x38, 0x01, 0xad, 0x01}\n    },\n    {\n        {0x89, 0x00, 0x00, 0x00}, {0x8a, 0x00, 0x00, 0x00},\n        {0x8c, 0x00, 0x00, 0x00}, {0x8d, 0x00, 0x00, 0x00},\n        {0x90, 0x00, 0x00, 0x00}, {0x91, 0x00, 0x00, 0x00},\n        {0x93, 0x00, 0x00, 0x00}, {0x96, 0x00, 0x00, 0x00},\n        {0x9c, 0x00, 0x00, 0x00}, {0x9f, 0x00, 0x00, 0x00},\n        {0xa3, 0x00, 0x00, 0x00}, {0xa6, 0x00, 0x00, 0x00},\n        {0xab, 0x00, 0x00, 0x00}, {0xae, 0x00, 0x00, 0x00},\n        {0xb5, 0x00, 0x00, 0x00}, {0xbe, 0x00, 0x00, 0x01}\n    },\n    {\n        {0x00, 0x01, 0xb2, 0x01}, {0x00, 0x01, 0xb5, 0x01},\n        {0x00, 0x01, 0xb9, 0x01}, {0x00, 0x01, 0xba, 0x01},\n        {0x00, 0x01, 0xbb, 0x01}, {0x00, 0x01, 0xbd, 0x01},\n        {0x00, 0x01, 0xbe, 0x01}, {0x00, 0x01, 0xc4, 0x01},\n        {0x00, 0x01, 0xc6, 0x01}, {0x00, 0x01, 0xe4, 0x01},\n        {0x00, 0x01, 0xe8, 0x01}, {0x00, 0x01, 0xe9, 0x01},\n        {0x94, 0x00, 0x00, 0x00}, {0x95, 0x00, 0x00, 0x00},\n        {0x97, 0x00, 0x00, 0x00}, {0x98, 0x00, 0x00, 0x00}\n    },\n    /* 135 */\n    {\n        {0x01, 0x01, 0xb2, 0x00}, {0x16, 0x01, 0xb2, 0x01},\n        {0x01, 0x01, 0xb5, 0x00}, {0x16, 0x01, 0xb5, 0x01},\n        {0x01, 0x01, 0xb9, 0x00}, {0x16, 0x01, 0xb9, 0x01},\n        {0x01, 0x01, 0xba, 0x00}, {0x16, 0x01, 0xba, 0x01},\n        {0x01, 0x01, 0xbb, 0x00}, {0x16, 0x01, 0xbb, 0x01},\n        {0x01, 0x01, 0xbd, 0x00}, {0x16, 0x01, 0xbd, 0x01},\n        {0x01, 0x01, 0xbe, 0x00}, {0x16, 0x01, 0xbe, 0x01},\n        {0x01, 0x01, 0xc4, 0x00}, {0x16, 0x01, 0xc4, 0x01}\n    },\n    {\n        {0x02, 0x01, 0xb2, 0x00}, {0x09, 0x01, 0xb2, 0x00},\n        {0x17, 0x01, 0xb2, 0x00}, {0x28, 0x01, 0xb2, 0x01},\n        {0x02, 0x01, 0xb5, 0x00}, {0x09, 0x01, 0xb5, 0x00},\n        {0x17, 0x01, 0xb5, 0x00}, {0x28, 0x01, 0xb5, 0x01},\n        {0x02, 0x01, 0xb9, 0x00}, {0x09, 0x01, 0xb9, 0x00},\n        {0x17, 0x01, 0xb9, 0x00}, {0x28, 0x01, 0xb9, 0x01},\n        {0x02, 0x01, 0xba, 0x00}, {0x09, 0x01, 0xba, 0x00},\n        {0x17, 0x01, 0xba, 0x00}, {0x28, 0x01, 0xba, 0x01}\n    },\n    {\n        {0x03, 0x01, 0xb2, 0x00}, {0x06, 0x01, 0xb2, 0x00},\n        {0x0a, 0x01, 0xb2, 0x00}, {0x0f, 0x01, 0xb2, 0x00},\n        {0x18, 0x01, 0xb2, 0x00}, {0x1f, 0x01, 0xb2, 0x00},\n        {0x29, 0x01, 0xb2, 0x00}, {0x38, 0x01, 0xb2, 0x01},\n        {0x03, 0x01, 0xb5, 0x00}, {0x06, 0x01, 0xb5, 0x00},\n        {0x0a, 0x01, 0xb5, 0x00}, {0x0f, 0x01, 0xb5, 0x00},\n        {0x18, 0x01, 0xb5, 0x00}, {0x1f, 0x01, 0xb5, 0x00},\n        {0x29, 0x01, 0xb5, 0x00}, {0x38, 0x01, 0xb5, 0x01}\n    },\n    {\n        {0x03, 0x01, 0xb9, 0x00}, {0x06, 0x01, 0xb9, 0x00},\n        {0x0a, 0x01, 0xb9, 0x00}, {0x0f, 0x01, 0xb9, 0x00},\n        {0x18, 0x01, 0xb9, 0x00}, {0x1f, 0x01, 0xb9, 0x00},\n        {0x29, 0x01, 0xb9, 0x00}, {0x38, 0x01, 0xb9, 0x01},\n        {0x03, 0x01, 0xba, 0x00}, {0x06, 0x01, 0xba, 0x00},\n        {0x0a, 0x01, 0xba, 0x00}, {0x0f, 0x01, 0xba, 0x00},\n        {0x18, 0x01, 0xba, 0x00}, {0x1f, 0x01, 0xba, 0x00},\n        {0x29, 0x01, 0xba, 0x00}, {0x38, 0x01, 0xba, 0x01}\n    },\n    {\n        {0x02, 0x01, 0xbb, 0x00}, {0x09, 0x01, 0xbb, 0x00},\n        {0x17, 0x01, 0xbb, 0x00}, {0x28, 0x01, 0xbb, 0x01},\n        {0x02, 0x01, 0xbd, 0x00}, {0x09, 0x01, 0xbd, 0x00},\n        {0x17, 0x01, 0xbd, 0x00}, {0x28, 0x01, 0xbd, 0x01},\n        {0x02, 0x01, 0xbe, 0x00}, {0x09, 0x01, 0xbe, 0x00},\n        {0x17, 0x01, 0xbe, 0x00}, {0x28, 0x01, 0xbe, 0x01},\n        {0x02, 0x01, 0xc4, 0x00}, {0x09, 0x01, 0xc4, 0x00},\n        {0x17, 0x01, 0xc4, 0x00}, {0x28, 0x01, 0xc4, 0x01}\n    },\n    /* 140 */\n    {\n        {0x03, 0x01, 0xbb, 0x00}, {0x06, 0x01, 0xbb, 0x00},\n        {0x0a, 0x01, 0xbb, 0x00}, {0x0f, 0x01, 0xbb, 0x00},\n        {0x18, 0x01, 0xbb, 0x00}, {0x1f, 0x01, 0xbb, 0x00},\n        {0x29, 0x01, 0xbb, 0x00}, {0x38, 0x01, 0xbb, 0x01},\n        {0x03, 0x01, 0xbd, 0x00}, {0x06, 0x01, 0xbd, 0x00},\n        {0x0a, 0x01, 0xbd, 0x00}, {0x0f, 0x01, 0xbd, 0x00},\n        {0x18, 0x01, 0xbd, 0x00}, {0x1f, 0x01, 0xbd, 0x00},\n        {0x29, 0x01, 0xbd, 0x00}, {0x38, 0x01, 0xbd, 0x01}\n    },\n    {\n        {0x03, 0x01, 0xbe, 0x00}, {0x06, 0x01, 0xbe, 0x00},\n        {0x0a, 0x01, 0xbe, 0x00}, {0x0f, 0x01, 0xbe, 0x00},\n        {0x18, 0x01, 0xbe, 0x00}, {0x1f, 0x01, 0xbe, 0x00},\n        {0x29, 0x01, 0xbe, 0x00}, {0x38, 0x01, 0xbe, 0x01},\n        {0x03, 0x01, 0xc4, 0x00}, {0x06, 0x01, 0xc4, 0x00},\n        {0x0a, 0x01, 0xc4, 0x00}, {0x0f, 0x01, 0xc4, 0x00},\n        {0x18, 0x01, 0xc4, 0x00}, {0x1f, 0x01, 0xc4, 0x00},\n        {0x29, 0x01, 0xc4, 0x00}, {0x38, 0x01, 0xc4, 0x01}\n    },\n    {\n        {0x01, 0x01, 0xc6, 0x00}, {0x16, 0x01, 0xc6, 0x01},\n        {0x01, 0x01, 0xe4, 0x00}, {0x16, 0x01, 0xe4, 0x01},\n        {0x01, 0x01, 0xe8, 0x00}, {0x16, 0x01, 0xe8, 0x01},\n        {0x01, 0x01, 0xe9, 0x00}, {0x16, 0x01, 0xe9, 0x01},\n        {0x00, 0x01, 0x01, 0x01}, {0x00, 0x01, 0x87, 0x01},\n        {0x00, 0x01, 0x89, 0x01}, {0x00, 0x01, 0x8a, 0x01},\n        {0x00, 0x01, 0x8b, 0x01}, {0x00, 0x01, 0x8c, 0x01},\n        {0x00, 0x01, 0x8d, 0x01}, {0x00, 0x01, 0x8f, 0x01}\n    },\n    {\n        {0x02, 0x01, 0xc6, 0x00}, {0x09, 0x01, 0xc6, 0x00},\n        {0x17, 0x01, 0xc6, 0x00}, {0x28, 0x01, 0xc6, 0x01},\n        {0x02, 0x01, 0xe4, 0x00}, {0x09, 0x01, 0xe4, 0x00},\n        {0x17, 0x01, 0xe4, 0x00}, {0x28, 0x01, 0xe4, 0x01},\n        {0x02, 0x01, 0xe8, 0x00}, {0x09, 0x01, 0xe8, 0x00},\n        {0x17, 0x01, 0xe8, 0x00}, {0x28, 0x01, 0xe8, 0x01},\n        {0x02, 0x01, 0xe9, 0x00}, {0x09, 0x01, 0xe9, 0x00},\n        {0x17, 0x01, 0xe9, 0x00}, {0x28, 0x01, 0xe9, 0x01}\n    },\n    {\n        {0x03, 0x01, 0xc6, 0x00}, {0x06, 0x01, 0xc6, 0x00},\n        {0x0a, 0x01, 0xc6, 0x00}, {0x0f, 0x01, 0xc6, 0x00},\n        {0x18, 0x01, 0xc6, 0x00}, {0x1f, 0x01, 0xc6, 0x00},\n        {0x29, 0x01, 0xc6, 0x00}, {0x38, 0x01, 0xc6, 0x01},\n        {0x03, 0x01, 0xe4, 0x00}, {0x06, 0x01, 0xe4, 0x00},\n        {0x0a, 0x01, 0xe4, 0x00}, {0x0f, 0x01, 0xe4, 0x00},\n        {0x18, 0x01, 0xe4, 0x00}, {0x1f, 0x01, 0xe4, 0x00},\n        {0x29, 0x01, 0xe4, 0x00}, {0x38, 0x01, 0xe4, 0x01}\n    },\n    /* 145 */\n    {\n        {0x03, 0x01, 0xe8, 0x00}, {0x06, 0x01, 0xe8, 0x00},\n        {0x0a, 0x01, 0xe8, 0x00}, {0x0f, 0x01, 0xe8, 0x00},\n        {0x18, 0x01, 0xe8, 0x00}, {0x1f, 0x01, 0xe8, 0x00},\n        {0x29, 0x01, 0xe8, 0x00}, {0x38, 0x01, 0xe8, 0x01},\n        {0x03, 0x01, 0xe9, 0x00}, {0x06, 0x01, 0xe9, 0x00},\n        {0x0a, 0x01, 0xe9, 0x00}, {0x0f, 0x01, 0xe9, 0x00},\n        {0x18, 0x01, 0xe9, 0x00}, {0x1f, 0x01, 0xe9, 0x00},\n        {0x29, 0x01, 0xe9, 0x00}, {0x38, 0x01, 0xe9, 0x01}\n    },\n    {\n        {0x01, 0x01, 0x01, 0x00}, {0x16, 0x01, 0x01, 0x01},\n        {0x01, 0x01, 0x87, 0x00}, {0x16, 0x01, 0x87, 0x01},\n        {0x01, 0x01, 0x89, 0x00}, {0x16, 0x01, 0x89, 0x01},\n        {0x01, 0x01, 0x8a, 0x00}, {0x16, 0x01, 0x8a, 0x01},\n        {0x01, 0x01, 0x8b, 0x00}, {0x16, 0x01, 0x8b, 0x01},\n        {0x01, 0x01, 0x8c, 0x00}, {0x16, 0x01, 0x8c, 0x01},\n        {0x01, 0x01, 0x8d, 0x00}, {0x16, 0x01, 0x8d, 0x01},\n        {0x01, 0x01, 0x8f, 0x00}, {0x16, 0x01, 0x8f, 0x01}\n    },\n    {\n        {0x02, 0x01, 0x01, 0x00}, {0x09, 0x01, 0x01, 0x00},\n        {0x17, 0x01, 0x01, 0x00}, {0x28, 0x01, 0x01, 0x01},\n        {0x02, 0x01, 0x87, 0x00}, {0x09, 0x01, 0x87, 0x00},\n        {0x17, 0x01, 0x87, 0x00}, {0x28, 0x01, 0x87, 0x01},\n        {0x02, 0x01, 0x89, 0x00}, {0x09, 0x01, 0x89, 0x00},\n        {0x17, 0x01, 0x89, 0x00}, {0x28, 0x01, 0x89, 0x01},\n        {0x02, 0x01, 0x8a, 0x00}, {0x09, 0x01, 0x8a, 0x00},\n        {0x17, 0x01, 0x8a, 0x00}, {0x28, 0x01, 0x8a, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x01, 0x00}, {0x06, 0x01, 0x01, 0x00},\n        {0x0a, 0x01, 0x01, 0x00}, {0x0f, 0x01, 0x01, 0x00},\n        {0x18, 0x01, 0x01, 0x00}, {0x1f, 0x01, 0x01, 0x00},\n        {0x29, 0x01, 0x01, 0x00}, {0x38, 0x01, 0x01, 0x01},\n        {0x03, 0x01, 0x87, 0x00}, {0x06, 0x01, 0x87, 0x00},\n        {0x0a, 0x01, 0x87, 0x00}, {0x0f, 0x01, 0x87, 0x00},\n        {0x18, 0x01, 0x87, 0x00}, {0x1f, 0x01, 0x87, 0x00},\n        {0x29, 0x01, 0x87, 0x00}, {0x38, 0x01, 0x87, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x89, 0x00}, {0x06, 0x01, 0x89, 0x00},\n        {0x0a, 0x01, 0x89, 0x00}, {0x0f, 0x01, 0x89, 0x00},\n        {0x18, 0x01, 0x89, 0x00}, {0x1f, 0x01, 0x89, 0x00},\n        {0x29, 0x01, 0x89, 0x00}, {0x38, 0x01, 0x89, 0x01},\n        {0x03, 0x01, 0x8a, 0x00}, {0x06, 0x01, 0x8a, 0x00},\n        {0x0a, 0x01, 0x8a, 0x00}, {0x0f, 0x01, 0x8a, 0x00},\n        {0x18, 0x01, 0x8a, 0x00}, {0x1f, 0x01, 0x8a, 0x00},\n        {0x29, 0x01, 0x8a, 0x00}, {0x38, 0x01, 0x8a, 0x01}\n    },\n    /* 150 */\n    {\n        {0x02, 0x01, 0x8b, 0x00}, {0x09, 0x01, 0x8b, 0x00},\n        {0x17, 0x01, 0x8b, 0x00}, {0x28, 0x01, 0x8b, 0x01},\n        {0x02, 0x01, 0x8c, 0x00}, {0x09, 0x01, 0x8c, 0x00},\n        {0x17, 0x01, 0x8c, 0x00}, {0x28, 0x01, 0x8c, 0x01},\n        {0x02, 0x01, 0x8d, 0x00}, {0x09, 0x01, 0x8d, 0x00},\n        {0x17, 0x01, 0x8d, 0x00}, {0x28, 0x01, 0x8d, 0x01},\n        {0x02, 0x01, 0x8f, 0x00}, {0x09, 0x01, 0x8f, 0x00},\n        {0x17, 0x01, 0x8f, 0x00}, {0x28, 0x01, 0x8f, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x8b, 0x00}, {0x06, 0x01, 0x8b, 0x00},\n        {0x0a, 0x01, 0x8b, 0x00}, {0x0f, 0x01, 0x8b, 0x00},\n        {0x18, 0x01, 0x8b, 0x00}, {0x1f, 0x01, 0x8b, 0x00},\n        {0x29, 0x01, 0x8b, 0x00}, {0x38, 0x01, 0x8b, 0x01},\n        {0x03, 0x01, 0x8c, 0x00}, {0x06, 0x01, 0x8c, 0x00},\n        {0x0a, 0x01, 0x8c, 0x00}, {0x0f, 0x01, 0x8c, 0x00},\n        {0x18, 0x01, 0x8c, 0x00}, {0x1f, 0x01, 0x8c, 0x00},\n        {0x29, 0x01, 0x8c, 0x00}, {0x38, 0x01, 0x8c, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x8d, 0x00}, {0x06, 0x01, 0x8d, 0x00},\n        {0x0a, 0x01, 0x8d, 0x00}, {0x0f, 0x01, 0x8d, 0x00},\n        {0x18, 0x01, 0x8d, 0x00}, {0x1f, 0x01, 0x8d, 0x00},\n        {0x29, 0x01, 0x8d, 0x00}, {0x38, 0x01, 0x8d, 0x01},\n        {0x03, 0x01, 0x8f, 0x00}, {0x06, 0x01, 0x8f, 0x00},\n        {0x0a, 0x01, 0x8f, 0x00}, {0x0f, 0x01, 0x8f, 0x00},\n        {0x18, 0x01, 0x8f, 0x00}, {0x1f, 0x01, 0x8f, 0x00},\n        {0x29, 0x01, 0x8f, 0x00}, {0x38, 0x01, 0x8f, 0x01}\n    },\n    {\n        {0x9d, 0x00, 0x00, 0x00}, {0x9e, 0x00, 0x00, 0x00},\n        {0xa0, 0x00, 0x00, 0x00}, {0xa1, 0x00, 0x00, 0x00},\n        {0xa4, 0x00, 0x00, 0x00}, {0xa5, 0x00, 0x00, 0x00},\n        {0xa7, 0x00, 0x00, 0x00}, {0xa8, 0x00, 0x00, 0x00},\n        {0xac, 0x00, 0x00, 0x00}, {0xad, 0x00, 0x00, 0x00},\n        {0xaf, 0x00, 0x00, 0x00}, {0xb1, 0x00, 0x00, 0x00},\n        {0xb6, 0x00, 0x00, 0x00}, {0xb9, 0x00, 0x00, 0x00},\n        {0xbf, 0x00, 0x00, 0x00}, {0xcf, 0x00, 0x00, 0x01}\n    },\n    {\n        {0x00, 0x01, 0x93, 0x01}, {0x00, 0x01, 0x95, 0x01},\n        {0x00, 0x01, 0x96, 0x01}, {0x00, 0x01, 0x97, 0x01},\n        {0x00, 0x01, 0x98, 0x01}, {0x00, 0x01, 0x9b, 0x01},\n        {0x00, 0x01, 0x9d, 0x01}, {0x00, 0x01, 0x9e, 0x01},\n        {0x00, 0x01, 0xa5, 0x01}, {0x00, 0x01, 0xa6, 0x01},\n        {0x00, 0x01, 0xa8, 0x01}, {0x00, 0x01, 0xae, 0x01},\n        {0x00, 0x01, 0xaf, 0x01}, {0x00, 0x01, 0xb4, 0x01},\n        {0x00, 0x01, 0xb6, 0x01}, {0x00, 0x01, 0xb7, 0x01}\n    },\n    /* 155 */\n    {\n        {0x01, 0x01, 0x93, 0x00}, {0x16, 0x01, 0x93, 0x01},\n        {0x01, 0x01, 0x95, 0x00}, {0x16, 0x01, 0x95, 0x01},\n        {0x01, 0x01, 0x96, 0x00}, {0x16, 0x01, 0x96, 0x01},\n        {0x01, 0x01, 0x97, 0x00}, {0x16, 0x01, 0x97, 0x01},\n        {0x01, 0x01, 0x98, 0x00}, {0x16, 0x01, 0x98, 0x01},\n        {0x01, 0x01, 0x9b, 0x00}, {0x16, 0x01, 0x9b, 0x01},\n        {0x01, 0x01, 0x9d, 0x00}, {0x16, 0x01, 0x9d, 0x01},\n        {0x01, 0x01, 0x9e, 0x00}, {0x16, 0x01, 0x9e, 0x01}\n    },\n    {\n        {0x02, 0x01, 0x93, 0x00}, {0x09, 0x01, 0x93, 0x00},\n        {0x17, 0x01, 0x93, 0x00}, {0x28, 0x01, 0x93, 0x01},\n        {0x02, 0x01, 0x95, 0x00}, {0x09, 0x01, 0x95, 0x00},\n        {0x17, 0x01, 0x95, 0x00}, {0x28, 0x01, 0x95, 0x01},\n        {0x02, 0x01, 0x96, 0x00}, {0x09, 0x01, 0x96, 0x00},\n        {0x17, 0x01, 0x96, 0x00}, {0x28, 0x01, 0x96, 0x01},\n        {0x02, 0x01, 0x97, 0x00}, {0x09, 0x01, 0x97, 0x00},\n        {0x17, 0x01, 0x97, 0x00}, {0x28, 0x01, 0x97, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x93, 0x00}, {0x06, 0x01, 0x93, 0x00},\n        {0x0a, 0x01, 0x93, 0x00}, {0x0f, 0x01, 0x93, 0x00},\n        {0x18, 0x01, 0x93, 0x00}, {0x1f, 0x01, 0x93, 0x00},\n        {0x29, 0x01, 0x93, 0x00}, {0x38, 0x01, 0x93, 0x01},\n        {0x03, 0x01, 0x95, 0x00}, {0x06, 0x01, 0x95, 0x00},\n        {0x0a, 0x01, 0x95, 0x00}, {0x0f, 0x01, 0x95, 0x00},\n        {0x18, 0x01, 0x95, 0x00}, {0x1f, 0x01, 0x95, 0x00},\n        {0x29, 0x01, 0x95, 0x00}, {0x38, 0x01, 0x95, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x96, 0x00}, {0x06, 0x01, 0x96, 0x00},\n        {0x0a, 0x01, 0x96, 0x00}, {0x0f, 0x01, 0x96, 0x00},\n        {0x18, 0x01, 0x96, 0x00}, {0x1f, 0x01, 0x96, 0x00},\n        {0x29, 0x01, 0x96, 0x00}, {0x38, 0x01, 0x96, 0x01},\n        {0x03, 0x01, 0x97, 0x00}, {0x06, 0x01, 0x97, 0x00},\n        {0x0a, 0x01, 0x97, 0x00}, {0x0f, 0x01, 0x97, 0x00},\n        {0x18, 0x01, 0x97, 0x00}, {0x1f, 0x01, 0x97, 0x00},\n        {0x29, 0x01, 0x97, 0x00}, {0x38, 0x01, 0x97, 0x01}\n    },\n    {\n        {0x02, 0x01, 0x98, 0x00}, {0x09, 0x01, 0x98, 0x00},\n        {0x17, 0x01, 0x98, 0x00}, {0x28, 0x01, 0x98, 0x01},\n        {0x02, 0x01, 0x9b, 0x00}, {0x09, 0x01, 0x9b, 0x00},\n        {0x17, 0x01, 0x9b, 0x00}, {0x28, 0x01, 0x9b, 0x01},\n        {0x02, 0x01, 0x9d, 0x00}, {0x09, 0x01, 0x9d, 0x00},\n        {0x17, 0x01, 0x9d, 0x00}, {0x28, 0x01, 0x9d, 0x01},\n        {0x02, 0x01, 0x9e, 0x00}, {0x09, 0x01, 0x9e, 0x00},\n        {0x17, 0x01, 0x9e, 0x00}, {0x28, 0x01, 0x9e, 0x01}\n    },\n    /* 160 */\n    {\n        {0x03, 0x01, 0x98, 0x00}, {0x06, 0x01, 0x98, 0x00},\n        {0x0a, 0x01, 0x98, 0x00}, {0x0f, 0x01, 0x98, 0x00},\n        {0x18, 0x01, 0x98, 0x00}, {0x1f, 0x01, 0x98, 0x00},\n        {0x29, 0x01, 0x98, 0x00}, {0x38, 0x01, 0x98, 0x01},\n        {0x03, 0x01, 0x9b, 0x00}, {0x06, 0x01, 0x9b, 0x00},\n        {0x0a, 0x01, 0x9b, 0x00}, {0x0f, 0x01, 0x9b, 0x00},\n        {0x18, 0x01, 0x9b, 0x00}, {0x1f, 0x01, 0x9b, 0x00},\n        {0x29, 0x01, 0x9b, 0x00}, {0x38, 0x01, 0x9b, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x9d, 0x00}, {0x06, 0x01, 0x9d, 0x00},\n        {0x0a, 0x01, 0x9d, 0x00}, {0x0f, 0x01, 0x9d, 0x00},\n        {0x18, 0x01, 0x9d, 0x00}, {0x1f, 0x01, 0x9d, 0x00},\n        {0x29, 0x01, 0x9d, 0x00}, {0x38, 0x01, 0x9d, 0x01},\n        {0x03, 0x01, 0x9e, 0x00}, {0x06, 0x01, 0x9e, 0x00},\n        {0x0a, 0x01, 0x9e, 0x00}, {0x0f, 0x01, 0x9e, 0x00},\n        {0x18, 0x01, 0x9e, 0x00}, {0x1f, 0x01, 0x9e, 0x00},\n        {0x29, 0x01, 0x9e, 0x00}, {0x38, 0x01, 0x9e, 0x01}\n    },\n    {\n        {0x01, 0x01, 0xa5, 0x00}, {0x16, 0x01, 0xa5, 0x01},\n        {0x01, 0x01, 0xa6, 0x00}, {0x16, 0x01, 0xa6, 0x01},\n        {0x01, 0x01, 0xa8, 0x00}, {0x16, 0x01, 0xa8, 0x01},\n        {0x01, 0x01, 0xae, 0x00}, {0x16, 0x01, 0xae, 0x01},\n        {0x01, 0x01, 0xaf, 0x00}, {0x16, 0x01, 0xaf, 0x01},\n        {0x01, 0x01, 0xb4, 0x00}, {0x16, 0x01, 0xb4, 0x01},\n        {0x01, 0x01, 0xb6, 0x00}, {0x16, 0x01, 0xb6, 0x01},\n        {0x01, 0x01, 0xb7, 0x00}, {0x16, 0x01, 0xb7, 0x01}\n    },\n    {\n        {0x02, 0x01, 0xa5, 0x00}, {0x09, 0x01, 0xa5, 0x00},\n        {0x17, 0x01, 0xa5, 0x00}, {0x28, 0x01, 0xa5, 0x01},\n        {0x02, 0x01, 0xa6, 0x00}, {0x09, 0x01, 0xa6, 0x00},\n        {0x17, 0x01, 0xa6, 0x00}, {0x28, 0x01, 0xa6, 0x01},\n        {0x02, 0x01, 0xa8, 0x00}, {0x09, 0x01, 0xa8, 0x00},\n        {0x17, 0x01, 0xa8, 0x00}, {0x28, 0x01, 0xa8, 0x01},\n        {0x02, 0x01, 0xae, 0x00}, {0x09, 0x01, 0xae, 0x00},\n        {0x17, 0x01, 0xae, 0x00}, {0x28, 0x01, 0xae, 0x01}\n    },\n    {\n        {0x03, 0x01, 0xa5, 0x00}, {0x06, 0x01, 0xa5, 0x00},\n        {0x0a, 0x01, 0xa5, 0x00}, {0x0f, 0x01, 0xa5, 0x00},\n        {0x18, 0x01, 0xa5, 0x00}, {0x1f, 0x01, 0xa5, 0x00},\n        {0x29, 0x01, 0xa5, 0x00}, {0x38, 0x01, 0xa5, 0x01},\n        {0x03, 0x01, 0xa6, 0x00}, {0x06, 0x01, 0xa6, 0x00},\n        {0x0a, 0x01, 0xa6, 0x00}, {0x0f, 0x01, 0xa6, 0x00},\n        {0x18, 0x01, 0xa6, 0x00}, {0x1f, 0x01, 0xa6, 0x00},\n        {0x29, 0x01, 0xa6, 0x00}, {0x38, 0x01, 0xa6, 0x01}\n    },\n    /* 165 */\n    {\n        {0x03, 0x01, 0xa8, 0x00}, {0x06, 0x01, 0xa8, 0x00},\n        {0x0a, 0x01, 0xa8, 0x00}, {0x0f, 0x01, 0xa8, 0x00},\n        {0x18, 0x01, 0xa8, 0x00}, {0x1f, 0x01, 0xa8, 0x00},\n        {0x29, 0x01, 0xa8, 0x00}, {0x38, 0x01, 0xa8, 0x01},\n        {0x03, 0x01, 0xae, 0x00}, {0x06, 0x01, 0xae, 0x00},\n        {0x0a, 0x01, 0xae, 0x00}, {0x0f, 0x01, 0xae, 0x00},\n        {0x18, 0x01, 0xae, 0x00}, {0x1f, 0x01, 0xae, 0x00},\n        {0x29, 0x01, 0xae, 0x00}, {0x38, 0x01, 0xae, 0x01}\n    },\n    {\n        {0x02, 0x01, 0xaf, 0x00}, {0x09, 0x01, 0xaf, 0x00},\n        {0x17, 0x01, 0xaf, 0x00}, {0x28, 0x01, 0xaf, 0x01},\n        {0x02, 0x01, 0xb4, 0x00}, {0x09, 0x01, 0xb4, 0x00},\n        {0x17, 0x01, 0xb4, 0x00}, {0x28, 0x01, 0xb4, 0x01},\n        {0x02, 0x01, 0xb6, 0x00}, {0x09, 0x01, 0xb6, 0x00},\n        {0x17, 0x01, 0xb6, 0x00}, {0x28, 0x01, 0xb6, 0x01},\n        {0x02, 0x01, 0xb7, 0x00}, {0x09, 0x01, 0xb7, 0x00},\n        {0x17, 0x01, 0xb7, 0x00}, {0x28, 0x01, 0xb7, 0x01}\n    },\n    {\n        {0x03, 0x01, 0xaf, 0x00}, {0x06, 0x01, 0xaf, 0x00},\n        {0x0a, 0x01, 0xaf, 0x00}, {0x0f, 0x01, 0xaf, 0x00},\n        {0x18, 0x01, 0xaf, 0x00}, {0x1f, 0x01, 0xaf, 0x00},\n        {0x29, 0x01, 0xaf, 0x00}, {0x38, 0x01, 0xaf, 0x01},\n        {0x03, 0x01, 0xb4, 0x00}, {0x06, 0x01, 0xb4, 0x00},\n        {0x0a, 0x01, 0xb4, 0x00}, {0x0f, 0x01, 0xb4, 0x00},\n        {0x18, 0x01, 0xb4, 0x00}, {0x1f, 0x01, 0xb4, 0x00},\n        {0x29, 0x01, 0xb4, 0x00}, {0x38, 0x01, 0xb4, 0x01}\n    },\n    {\n        {0x03, 0x01, 0xb6, 0x00}, {0x06, 0x01, 0xb6, 0x00},\n        {0x0a, 0x01, 0xb6, 0x00}, {0x0f, 0x01, 0xb6, 0x00},\n        {0x18, 0x01, 0xb6, 0x00}, {0x1f, 0x01, 0xb6, 0x00},\n        {0x29, 0x01, 0xb6, 0x00}, {0x38, 0x01, 0xb6, 0x01},\n        {0x03, 0x01, 0xb7, 0x00}, {0x06, 0x01, 0xb7, 0x00},\n        {0x0a, 0x01, 0xb7, 0x00}, {0x0f, 0x01, 0xb7, 0x00},\n        {0x18, 0x01, 0xb7, 0x00}, {0x1f, 0x01, 0xb7, 0x00},\n        {0x29, 0x01, 0xb7, 0x00}, {0x38, 0x01, 0xb7, 0x01}\n    },\n    {\n        {0x00, 0x01, 0xbc, 0x01}, {0x00, 0x01, 0xbf, 0x01},\n        {0x00, 0x01, 0xc5, 0x01}, {0x00, 0x01, 0xe7, 0x01},\n        {0x00, 0x01, 0xef, 0x01}, {0xb0, 0x00, 0x00, 0x00},\n        {0xb2, 0x00, 0x00, 0x00}, {0xb3, 0x00, 0x00, 0x00},\n        {0xb7, 0x00, 0x00, 0x00}, {0xb8, 0x00, 0x00, 0x00},\n        {0xba, 0x00, 0x00, 0x00}, {0xbb, 0x00, 0x00, 0x00},\n        {0xc0, 0x00, 0x00, 0x00}, {0xc7, 0x00, 0x00, 0x00},\n        {0xd0, 0x00, 0x00, 0x00}, {0xdf, 0x00, 0x00, 0x01}\n    },\n    /* 170 */\n    {\n        {0x01, 0x01, 0xbc, 0x00}, {0x16, 0x01, 0xbc, 0x01},\n        {0x01, 0x01, 0xbf, 0x00}, {0x16, 0x01, 0xbf, 0x01},\n        {0x01, 0x01, 0xc5, 0x00}, {0x16, 0x01, 0xc5, 0x01},\n        {0x01, 0x01, 0xe7, 0x00}, {0x16, 0x01, 0xe7, 0x01},\n        {0x01, 0x01, 0xef, 0x00}, {0x16, 0x01, 0xef, 0x01},\n        {0x00, 0x01, 0x09, 0x01}, {0x00, 0x01, 0x8e, 0x01},\n        {0x00, 0x01, 0x90, 0x01}, {0x00, 0x01, 0x91, 0x01},\n        {0x00, 0x01, 0x94, 0x01}, {0x00, 0x01, 0x9f, 0x01}\n    },\n    {\n        {0x02, 0x01, 0xbc, 0x00}, {0x09, 0x01, 0xbc, 0x00},\n        {0x17, 0x01, 0xbc, 0x00}, {0x28, 0x01, 0xbc, 0x01},\n        {0x02, 0x01, 0xbf, 0x00}, {0x09, 0x01, 0xbf, 0x00},\n        {0x17, 0x01, 0xbf, 0x00}, {0x28, 0x01, 0xbf, 0x01},\n        {0x02, 0x01, 0xc5, 0x00}, {0x09, 0x01, 0xc5, 0x00},\n        {0x17, 0x01, 0xc5, 0x00}, {0x28, 0x01, 0xc5, 0x01},\n        {0x02, 0x01, 0xe7, 0x00}, {0x09, 0x01, 0xe7, 0x00},\n        {0x17, 0x01, 0xe7, 0x00}, {0x28, 0x01, 0xe7, 0x01}\n    },\n    {\n        {0x03, 0x01, 0xbc, 0x00}, {0x06, 0x01, 0xbc, 0x00},\n        {0x0a, 0x01, 0xbc, 0x00}, {0x0f, 0x01, 0xbc, 0x00},\n        {0x18, 0x01, 0xbc, 0x00}, {0x1f, 0x01, 0xbc, 0x00},\n        {0x29, 0x01, 0xbc, 0x00}, {0x38, 0x01, 0xbc, 0x01},\n        {0x03, 0x01, 0xbf, 0x00}, {0x06, 0x01, 0xbf, 0x00},\n        {0x0a, 0x01, 0xbf, 0x00}, {0x0f, 0x01, 0xbf, 0x00},\n        {0x18, 0x01, 0xbf, 0x00}, {0x1f, 0x01, 0xbf, 0x00},\n        {0x29, 0x01, 0xbf, 0x00}, {0x38, 0x01, 0xbf, 0x01}\n    },\n    {\n        {0x03, 0x01, 0xc5, 0x00}, {0x06, 0x01, 0xc5, 0x00},\n        {0x0a, 0x01, 0xc5, 0x00}, {0x0f, 0x01, 0xc5, 0x00},\n        {0x18, 0x01, 0xc5, 0x00}, {0x1f, 0x01, 0xc5, 0x00},\n        {0x29, 0x01, 0xc5, 0x00}, {0x38, 0x01, 0xc5, 0x01},\n        {0x03, 0x01, 0xe7, 0x00}, {0x06, 0x01, 0xe7, 0x00},\n        {0x0a, 0x01, 0xe7, 0x00}, {0x0f, 0x01, 0xe7, 0x00},\n        {0x18, 0x01, 0xe7, 0x00}, {0x1f, 0x01, 0xe7, 0x00},\n        {0x29, 0x01, 0xe7, 0x00}, {0x38, 0x01, 0xe7, 0x01}\n    },\n    {\n        {0x02, 0x01, 0xef, 0x00}, {0x09, 0x01, 0xef, 0x00},\n        {0x17, 0x01, 0xef, 0x00}, {0x28, 0x01, 0xef, 0x01},\n        {0x01, 0x01, 0x09, 0x00}, {0x16, 0x01, 0x09, 0x01},\n        {0x01, 0x01, 0x8e, 0x00}, {0x16, 0x01, 0x8e, 0x01},\n        {0x01, 0x01, 0x90, 0x00}, {0x16, 0x01, 0x90, 0x01},\n        {0x01, 0x01, 0x91, 0x00}, {0x16, 0x01, 0x91, 0x01},\n        {0x01, 0x01, 0x94, 0x00}, {0x16, 0x01, 0x94, 0x01},\n        {0x01, 0x01, 0x9f, 0x00}, {0x16, 0x01, 0x9f, 0x01}\n    },\n    /* 175 */\n    {\n        {0x03, 0x01, 0xef, 0x00}, {0x06, 0x01, 0xef, 0x00},\n        {0x0a, 0x01, 0xef, 0x00}, {0x0f, 0x01, 0xef, 0x00},\n        {0x18, 0x01, 0xef, 0x00}, {0x1f, 0x01, 0xef, 0x00},\n        {0x29, 0x01, 0xef, 0x00}, {0x38, 0x01, 0xef, 0x01},\n        {0x02, 0x01, 0x09, 0x00}, {0x09, 0x01, 0x09, 0x00},\n        {0x17, 0x01, 0x09, 0x00}, {0x28, 0x01, 0x09, 0x01},\n        {0x02, 0x01, 0x8e, 0x00}, {0x09, 0x01, 0x8e, 0x00},\n        {0x17, 0x01, 0x8e, 0x00}, {0x28, 0x01, 0x8e, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x09, 0x00}, {0x06, 0x01, 0x09, 0x00},\n        {0x0a, 0x01, 0x09, 0x00}, {0x0f, 0x01, 0x09, 0x00},\n        {0x18, 0x01, 0x09, 0x00}, {0x1f, 0x01, 0x09, 0x00},\n        {0x29, 0x01, 0x09, 0x00}, {0x38, 0x01, 0x09, 0x01},\n        {0x03, 0x01, 0x8e, 0x00}, {0x06, 0x01, 0x8e, 0x00},\n        {0x0a, 0x01, 0x8e, 0x00}, {0x0f, 0x01, 0x8e, 0x00},\n        {0x18, 0x01, 0x8e, 0x00}, {0x1f, 0x01, 0x8e, 0x00},\n        {0x29, 0x01, 0x8e, 0x00}, {0x38, 0x01, 0x8e, 0x01}\n    },\n    {\n        {0x02, 0x01, 0x90, 0x00}, {0x09, 0x01, 0x90, 0x00},\n        {0x17, 0x01, 0x90, 0x00}, {0x28, 0x01, 0x90, 0x01},\n        {0x02, 0x01, 0x91, 0x00}, {0x09, 0x01, 0x91, 0x00},\n        {0x17, 0x01, 0x91, 0x00}, {0x28, 0x01, 0x91, 0x01},\n        {0x02, 0x01, 0x94, 0x00}, {0x09, 0x01, 0x94, 0x00},\n        {0x17, 0x01, 0x94, 0x00}, {0x28, 0x01, 0x94, 0x01},\n        {0x02, 0x01, 0x9f, 0x00}, {0x09, 0x01, 0x9f, 0x00},\n        {0x17, 0x01, 0x9f, 0x00}, {0x28, 0x01, 0x9f, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x90, 0x00}, {0x06, 0x01, 0x90, 0x00},\n        {0x0a, 0x01, 0x90, 0x00}, {0x0f, 0x01, 0x90, 0x00},\n        {0x18, 0x01, 0x90, 0x00}, {0x1f, 0x01, 0x90, 0x00},\n        {0x29, 0x01, 0x90, 0x00}, {0x38, 0x01, 0x90, 0x01},\n        {0x03, 0x01, 0x91, 0x00}, {0x06, 0x01, 0x91, 0x00},\n        {0x0a, 0x01, 0x91, 0x00}, {0x0f, 0x01, 0x91, 0x00},\n        {0x18, 0x01, 0x91, 0x00}, {0x1f, 0x01, 0x91, 0x00},\n        {0x29, 0x01, 0x91, 0x00}, {0x38, 0x01, 0x91, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x94, 0x00}, {0x06, 0x01, 0x94, 0x00},\n        {0x0a, 0x01, 0x94, 0x00}, {0x0f, 0x01, 0x94, 0x00},\n        {0x18, 0x01, 0x94, 0x00}, {0x1f, 0x01, 0x94, 0x00},\n        {0x29, 0x01, 0x94, 0x00}, {0x38, 0x01, 0x94, 0x01},\n        {0x03, 0x01, 0x9f, 0x00}, {0x06, 0x01, 0x9f, 0x00},\n        {0x0a, 0x01, 0x9f, 0x00}, {0x0f, 0x01, 0x9f, 0x00},\n        {0x18, 0x01, 0x9f, 0x00}, {0x1f, 0x01, 0x9f, 0x00},\n        {0x29, 0x01, 0x9f, 0x00}, {0x38, 0x01, 0x9f, 0x01}\n    },\n    /* 180 */\n    {\n        {0x00, 0x01, 0xab, 0x01}, {0x00, 0x01, 0xce, 0x01},\n        {0x00, 0x01, 0xd7, 0x01}, {0x00, 0x01, 0xe1, 0x01},\n        {0x00, 0x01, 0xec, 0x01}, {0x00, 0x01, 0xed, 0x01},\n        {0xbc, 0x00, 0x00, 0x00}, {0xbd, 0x00, 0x00, 0x00},\n        {0xc1, 0x00, 0x00, 0x00}, {0xc4, 0x00, 0x00, 0x00},\n        {0xc8, 0x00, 0x00, 0x00}, {0xcb, 0x00, 0x00, 0x00},\n        {0xd1, 0x00, 0x00, 0x00}, {0xd8, 0x00, 0x00, 0x00},\n        {0xe0, 0x00, 0x00, 0x00}, {0xee, 0x00, 0x00, 0x01}\n    },\n    {\n        {0x01, 0x01, 0xab, 0x00}, {0x16, 0x01, 0xab, 0x01},\n        {0x01, 0x01, 0xce, 0x00}, {0x16, 0x01, 0xce, 0x01},\n        {0x01, 0x01, 0xd7, 0x00}, {0x16, 0x01, 0xd7, 0x01},\n        {0x01, 0x01, 0xe1, 0x00}, {0x16, 0x01, 0xe1, 0x01},\n        {0x01, 0x01, 0xec, 0x00}, {0x16, 0x01, 0xec, 0x01},\n        {0x01, 0x01, 0xed, 0x00}, {0x16, 0x01, 0xed, 0x01},\n        {0x00, 0x01, 0xc7, 0x01}, {0x00, 0x01, 0xcf, 0x01},\n        {0x00, 0x01, 0xea, 0x01}, {0x00, 0x01, 0xeb, 0x01}\n    },\n    {\n        {0x02, 0x01, 0xab, 0x00}, {0x09, 0x01, 0xab, 0x00},\n        {0x17, 0x01, 0xab, 0x00}, {0x28, 0x01, 0xab, 0x01},\n        {0x02, 0x01, 0xce, 0x00}, {0x09, 0x01, 0xce, 0x00},\n        {0x17, 0x01, 0xce, 0x00}, {0x28, 0x01, 0xce, 0x01},\n        {0x02, 0x01, 0xd7, 0x00}, {0x09, 0x01, 0xd7, 0x00},\n        {0x17, 0x01, 0xd7, 0x00}, {0x28, 0x01, 0xd7, 0x01},\n        {0x02, 0x01, 0xe1, 0x00}, {0x09, 0x01, 0xe1, 0x00},\n        {0x17, 0x01, 0xe1, 0x00}, {0x28, 0x01, 0xe1, 0x01}\n    },\n    {\n        {0x03, 0x01, 0xab, 0x00}, {0x06, 0x01, 0xab, 0x00},\n        {0x0a, 0x01, 0xab, 0x00}, {0x0f, 0x01, 0xab, 0x00},\n        {0x18, 0x01, 0xab, 0x00}, {0x1f, 0x01, 0xab, 0x00},\n        {0x29, 0x01, 0xab, 0x00}, {0x38, 0x01, 0xab, 0x01},\n        {0x03, 0x01, 0xce, 0x00}, {0x06, 0x01, 0xce, 0x00},\n        {0x0a, 0x01, 0xce, 0x00}, {0x0f, 0x01, 0xce, 0x00},\n        {0x18, 0x01, 0xce, 0x00}, {0x1f, 0x01, 0xce, 0x00},\n        {0x29, 0x01, 0xce, 0x00}, {0x38, 0x01, 0xce, 0x01}\n    },\n    {\n        {0x03, 0x01, 0xd7, 0x00}, {0x06, 0x01, 0xd7, 0x00},\n        {0x0a, 0x01, 0xd7, 0x00}, {0x0f, 0x01, 0xd7, 0x00},\n        {0x18, 0x01, 0xd7, 0x00}, {0x1f, 0x01, 0xd7, 0x00},\n        {0x29, 0x01, 0xd7, 0x00}, {0x38, 0x01, 0xd7, 0x01},\n        {0x03, 0x01, 0xe1, 0x00}, {0x06, 0x01, 0xe1, 0x00},\n        {0x0a, 0x01, 0xe1, 0x00}, {0x0f, 0x01, 0xe1, 0x00},\n        {0x18, 0x01, 0xe1, 0x00}, {0x1f, 0x01, 0xe1, 0x00},\n        {0x29, 0x01, 0xe1, 0x00}, {0x38, 0x01, 0xe1, 0x01}\n    },\n    /* 185 */\n    {\n        {0x02, 0x01, 0xec, 0x00}, {0x09, 0x01, 0xec, 0x00},\n        {0x17, 0x01, 0xec, 0x00}, {0x28, 0x01, 0xec, 0x01},\n        {0x02, 0x01, 0xed, 0x00}, {0x09, 0x01, 0xed, 0x00},\n        {0x17, 0x01, 0xed, 0x00}, {0x28, 0x01, 0xed, 0x01},\n        {0x01, 0x01, 0xc7, 0x00}, {0x16, 0x01, 0xc7, 0x01},\n        {0x01, 0x01, 0xcf, 0x00}, {0x16, 0x01, 0xcf, 0x01},\n        {0x01, 0x01, 0xea, 0x00}, {0x16, 0x01, 0xea, 0x01},\n        {0x01, 0x01, 0xeb, 0x00}, {0x16, 0x01, 0xeb, 0x01}\n    },\n    {\n        {0x03, 0x01, 0xec, 0x00}, {0x06, 0x01, 0xec, 0x00},\n        {0x0a, 0x01, 0xec, 0x00}, {0x0f, 0x01, 0xec, 0x00},\n        {0x18, 0x01, 0xec, 0x00}, {0x1f, 0x01, 0xec, 0x00},\n        {0x29, 0x01, 0xec, 0x00}, {0x38, 0x01, 0xec, 0x01},\n        {0x03, 0x01, 0xed, 0x00}, {0x06, 0x01, 0xed, 0x00},\n        {0x0a, 0x01, 0xed, 0x00}, {0x0f, 0x01, 0xed, 0x00},\n        {0x18, 0x01, 0xed, 0x00}, {0x1f, 0x01, 0xed, 0x00},\n        {0x29, 0x01, 0xed, 0x00}, {0x38, 0x01, 0xed, 0x01}\n    },\n    {\n        {0x02, 0x01, 0xc7, 0x00}, {0x09, 0x01, 0xc7, 0x00},\n        {0x17, 0x01, 0xc7, 0x00}, {0x28, 0x01, 0xc7, 0x01},\n        {0x02, 0x01, 0xcf, 0x00}, {0x09, 0x01, 0xcf, 0x00},\n        {0x17, 0x01, 0xcf, 0x00}, {0x28, 0x01, 0xcf, 0x01},\n        {0x02, 0x01, 0xea, 0x00}, {0x09, 0x01, 0xea, 0x00},\n        {0x17, 0x01, 0xea, 0x00}, {0x28, 0x01, 0xea, 0x01},\n        {0x02, 0x01, 0xeb, 0x00}, {0x09, 0x01, 0xeb, 0x00},\n        {0x17, 0x01, 0xeb, 0x00}, {0x28, 0x01, 0xeb, 0x01}\n    },\n    {\n        {0x03, 0x01, 0xc7, 0x00}, {0x06, 0x01, 0xc7, 0x00},\n        {0x0a, 0x01, 0xc7, 0x00}, {0x0f, 0x01, 0xc7, 0x00},\n        {0x18, 0x01, 0xc7, 0x00}, {0x1f, 0x01, 0xc7, 0x00},\n        {0x29, 0x01, 0xc7, 0x00}, {0x38, 0x01, 0xc7, 0x01},\n        {0x03, 0x01, 0xcf, 0x00}, {0x06, 0x01, 0xcf, 0x00},\n        {0x0a, 0x01, 0xcf, 0x00}, {0x0f, 0x01, 0xcf, 0x00},\n        {0x18, 0x01, 0xcf, 0x00}, {0x1f, 0x01, 0xcf, 0x00},\n        {0x29, 0x01, 0xcf, 0x00}, {0x38, 0x01, 0xcf, 0x01}\n    },\n    {\n        {0x03, 0x01, 0xea, 0x00}, {0x06, 0x01, 0xea, 0x00},\n        {0x0a, 0x01, 0xea, 0x00}, {0x0f, 0x01, 0xea, 0x00},\n        {0x18, 0x01, 0xea, 0x00}, {0x1f, 0x01, 0xea, 0x00},\n        {0x29, 0x01, 0xea, 0x00}, {0x38, 0x01, 0xea, 0x01},\n        {0x03, 0x01, 0xeb, 0x00}, {0x06, 0x01, 0xeb, 0x00},\n        {0x0a, 0x01, 0xeb, 0x00}, {0x0f, 0x01, 0xeb, 0x00},\n        {0x18, 0x01, 0xeb, 0x00}, {0x1f, 0x01, 0xeb, 0x00},\n        {0x29, 0x01, 0xeb, 0x00}, {0x38, 0x01, 0xeb, 0x01}\n    },\n    /* 190 */\n    {\n        {0xc2, 0x00, 0x00, 0x00}, {0xc3, 0x00, 0x00, 0x00},\n        {0xc5, 0x00, 0x00, 0x00}, {0xc6, 0x00, 0x00, 0x00},\n        {0xc9, 0x00, 0x00, 0x00}, {0xca, 0x00, 0x00, 0x00},\n        {0xcc, 0x00, 0x00, 0x00}, {0xcd, 0x00, 0x00, 0x00},\n        {0xd2, 0x00, 0x00, 0x00}, {0xd5, 0x00, 0x00, 0x00},\n        {0xd9, 0x00, 0x00, 0x00}, {0xdc, 0x00, 0x00, 0x00},\n        {0xe1, 0x00, 0x00, 0x00}, {0xe7, 0x00, 0x00, 0x00},\n        {0xef, 0x00, 0x00, 0x00}, {0xf6, 0x00, 0x00, 0x01}\n    },\n    {\n        {0x00, 0x01, 0xc0, 0x01}, {0x00, 0x01, 0xc1, 0x01},\n        {0x00, 0x01, 0xc8, 0x01}, {0x00, 0x01, 0xc9, 0x01},\n        {0x00, 0x01, 0xca, 0x01}, {0x00, 0x01, 0xcd, 0x01},\n        {0x00, 0x01, 0xd2, 0x01}, {0x00, 0x01, 0xd5, 0x01},\n        {0x00, 0x01, 0xda, 0x01}, {0x00, 0x01, 0xdb, 0x01},\n        {0x00, 0x01, 0xee, 0x01}, {0x00, 0x01, 0xf0, 0x01},\n        {0x00, 0x01, 0xf2, 0x01}, {0x00, 0x01, 0xf3, 0x01},\n        {0x00, 0x01, 0xff, 0x01}, {0xce, 0x00, 0x00, 0x00}\n    },\n    {\n        {0x01, 0x01, 0xc0, 0x00}, {0x16, 0x01, 0xc0, 0x01},\n        {0x01, 0x01, 0xc1, 0x00}, {0x16, 0x01, 0xc1, 0x01},\n        {0x01, 0x01, 0xc8, 0x00}, {0x16, 0x01, 0xc8, 0x01},\n        {0x01, 0x01, 0xc9, 0x00}, {0x16, 0x01, 0xc9, 0x01},\n        {0x01, 0x01, 0xca, 0x00}, {0x16, 0x01, 0xca, 0x01},\n        {0x01, 0x01, 0xcd, 0x00}, {0x16, 0x01, 0xcd, 0x01},\n        {0x01, 0x01, 0xd2, 0x00}, {0x16, 0x01, 0xd2, 0x01},\n        {0x01, 0x01, 0xd5, 0x00}, {0x16, 0x01, 0xd5, 0x01}\n    },\n    {\n        {0x02, 0x01, 0xc0, 0x00}, {0x09, 0x01, 0xc0, 0x00},\n        {0x17, 0x01, 0xc0, 0x00}, {0x28, 0x01, 0xc0, 0x01},\n        {0x02, 0x01, 0xc1, 0x00}, {0x09, 0x01, 0xc1, 0x00},\n        {0x17, 0x01, 0xc1, 0x00}, {0x28, 0x01, 0xc1, 0x01},\n        {0x02, 0x01, 0xc8, 0x00}, {0x09, 0x01, 0xc8, 0x00},\n        {0x17, 0x01, 0xc8, 0x00}, {0x28, 0x01, 0xc8, 0x01},\n        {0x02, 0x01, 0xc9, 0x00}, {0x09, 0x01, 0xc9, 0x00},\n        {0x17, 0x01, 0xc9, 0x00}, {0x28, 0x01, 0xc9, 0x01}\n    },\n    {\n        {0x03, 0x01, 0xc0, 0x00}, {0x06, 0x01, 0xc0, 0x00},\n        {0x0a, 0x01, 0xc0, 0x00}, {0x0f, 0x01, 0xc0, 0x00},\n        {0x18, 0x01, 0xc0, 0x00}, {0x1f, 0x01, 0xc0, 0x00},\n        {0x29, 0x01, 0xc0, 0x00}, {0x38, 0x01, 0xc0, 0x01},\n        {0x03, 0x01, 0xc1, 0x00}, {0x06, 0x01, 0xc1, 0x00},\n        {0x0a, 0x01, 0xc1, 0x00}, {0x0f, 0x01, 0xc1, 0x00},\n        {0x18, 0x01, 0xc1, 0x00}, {0x1f, 0x01, 0xc1, 0x00},\n        {0x29, 0x01, 0xc1, 0x00}, {0x38, 0x01, 0xc1, 0x01}\n    },\n    /* 195 */\n    {\n        {0x03, 0x01, 0xc8, 0x00}, {0x06, 0x01, 0xc8, 0x00},\n        {0x0a, 0x01, 0xc8, 0x00}, {0x0f, 0x01, 0xc8, 0x00},\n        {0x18, 0x01, 0xc8, 0x00}, {0x1f, 0x01, 0xc8, 0x00},\n        {0x29, 0x01, 0xc8, 0x00}, {0x38, 0x01, 0xc8, 0x01},\n        {0x03, 0x01, 0xc9, 0x00}, {0x06, 0x01, 0xc9, 0x00},\n        {0x0a, 0x01, 0xc9, 0x00}, {0x0f, 0x01, 0xc9, 0x00},\n        {0x18, 0x01, 0xc9, 0x00}, {0x1f, 0x01, 0xc9, 0x00},\n        {0x29, 0x01, 0xc9, 0x00}, {0x38, 0x01, 0xc9, 0x01}\n    },\n    {\n        {0x02, 0x01, 0xca, 0x00}, {0x09, 0x01, 0xca, 0x00},\n        {0x17, 0x01, 0xca, 0x00}, {0x28, 0x01, 0xca, 0x01},\n        {0x02, 0x01, 0xcd, 0x00}, {0x09, 0x01, 0xcd, 0x00},\n        {0x17, 0x01, 0xcd, 0x00}, {0x28, 0x01, 0xcd, 0x01},\n        {0x02, 0x01, 0xd2, 0x00}, {0x09, 0x01, 0xd2, 0x00},\n        {0x17, 0x01, 0xd2, 0x00}, {0x28, 0x01, 0xd2, 0x01},\n        {0x02, 0x01, 0xd5, 0x00}, {0x09, 0x01, 0xd5, 0x00},\n        {0x17, 0x01, 0xd5, 0x00}, {0x28, 0x01, 0xd5, 0x01}\n    },\n    {\n        {0x03, 0x01, 0xca, 0x00}, {0x06, 0x01, 0xca, 0x00},\n        {0x0a, 0x01, 0xca, 0x00}, {0x0f, 0x01, 0xca, 0x00},\n        {0x18, 0x01, 0xca, 0x00}, {0x1f, 0x01, 0xca, 0x00},\n        {0x29, 0x01, 0xca, 0x00}, {0x38, 0x01, 0xca, 0x01},\n        {0x03, 0x01, 0xcd, 0x00}, {0x06, 0x01, 0xcd, 0x00},\n        {0x0a, 0x01, 0xcd, 0x00}, {0x0f, 0x01, 0xcd, 0x00},\n        {0x18, 0x01, 0xcd, 0x00}, {0x1f, 0x01, 0xcd, 0x00},\n        {0x29, 0x01, 0xcd, 0x00}, {0x38, 0x01, 0xcd, 0x01}\n    },\n    {\n        {0x03, 0x01, 0xd2, 0x00}, {0x06, 0x01, 0xd2, 0x00},\n        {0x0a, 0x01, 0xd2, 0x00}, {0x0f, 0x01, 0xd2, 0x00},\n        {0x18, 0x01, 0xd2, 0x00}, {0x1f, 0x01, 0xd2, 0x00},\n        {0x29, 0x01, 0xd2, 0x00}, {0x38, 0x01, 0xd2, 0x01},\n        {0x03, 0x01, 0xd5, 0x00}, {0x06, 0x01, 0xd5, 0x00},\n        {0x0a, 0x01, 0xd5, 0x00}, {0x0f, 0x01, 0xd5, 0x00},\n        {0x18, 0x01, 0xd5, 0x00}, {0x1f, 0x01, 0xd5, 0x00},\n        {0x29, 0x01, 0xd5, 0x00}, {0x38, 0x01, 0xd5, 0x01}\n    },\n    {\n        {0x01, 0x01, 0xda, 0x00}, {0x16, 0x01, 0xda, 0x01},\n        {0x01, 0x01, 0xdb, 0x00}, {0x16, 0x01, 0xdb, 0x01},\n        {0x01, 0x01, 0xee, 0x00}, {0x16, 0x01, 0xee, 0x01},\n        {0x01, 0x01, 0xf0, 0x00}, {0x16, 0x01, 0xf0, 0x01},\n        {0x01, 0x01, 0xf2, 0x00}, {0x16, 0x01, 0xf2, 0x01},\n        {0x01, 0x01, 0xf3, 0x00}, {0x16, 0x01, 0xf3, 0x01},\n        {0x01, 0x01, 0xff, 0x00}, {0x16, 0x01, 0xff, 0x01},\n        {0x00, 0x01, 0xcb, 0x01}, {0x00, 0x01, 0xcc, 0x01}\n    },\n    /* 200 */\n    {\n        {0x02, 0x01, 0xda, 0x00}, {0x09, 0x01, 0xda, 0x00},\n        {0x17, 0x01, 0xda, 0x00}, {0x28, 0x01, 0xda, 0x01},\n        {0x02, 0x01, 0xdb, 0x00}, {0x09, 0x01, 0xdb, 0x00},\n        {0x17, 0x01, 0xdb, 0x00}, {0x28, 0x01, 0xdb, 0x01},\n        {0x02, 0x01, 0xee, 0x00}, {0x09, 0x01, 0xee, 0x00},\n        {0x17, 0x01, 0xee, 0x00}, {0x28, 0x01, 0xee, 0x01},\n        {0x02, 0x01, 0xf0, 0x00}, {0x09, 0x01, 0xf0, 0x00},\n        {0x17, 0x01, 0xf0, 0x00}, {0x28, 0x01, 0xf0, 0x01}\n    },\n    {\n        {0x03, 0x01, 0xda, 0x00}, {0x06, 0x01, 0xda, 0x00},\n        {0x0a, 0x01, 0xda, 0x00}, {0x0f, 0x01, 0xda, 0x00},\n        {0x18, 0x01, 0xda, 0x00}, {0x1f, 0x01, 0xda, 0x00},\n        {0x29, 0x01, 0xda, 0x00}, {0x38, 0x01, 0xda, 0x01},\n        {0x03, 0x01, 0xdb, 0x00}, {0x06, 0x01, 0xdb, 0x00},\n        {0x0a, 0x01, 0xdb, 0x00}, {0x0f, 0x01, 0xdb, 0x00},\n        {0x18, 0x01, 0xdb, 0x00}, {0x1f, 0x01, 0xdb, 0x00},\n        {0x29, 0x01, 0xdb, 0x00}, {0x38, 0x01, 0xdb, 0x01}\n    },\n    {\n        {0x03, 0x01, 0xee, 0x00}, {0x06, 0x01, 0xee, 0x00},\n        {0x0a, 0x01, 0xee, 0x00}, {0x0f, 0x01, 0xee, 0x00},\n        {0x18, 0x01, 0xee, 0x00}, {0x1f, 0x01, 0xee, 0x00},\n        {0x29, 0x01, 0xee, 0x00}, {0x38, 0x01, 0xee, 0x01},\n        {0x03, 0x01, 0xf0, 0x00}, {0x06, 0x01, 0xf0, 0x00},\n        {0x0a, 0x01, 0xf0, 0x00}, {0x0f, 0x01, 0xf0, 0x00},\n        {0x18, 0x01, 0xf0, 0x00}, {0x1f, 0x01, 0xf0, 0x00},\n        {0x29, 0x01, 0xf0, 0x00}, {0x38, 0x01, 0xf0, 0x01}\n    },\n    {\n        {0x02, 0x01, 0xf2, 0x00}, {0x09, 0x01, 0xf2, 0x00},\n        {0x17, 0x01, 0xf2, 0x00}, {0x28, 0x01, 0xf2, 0x01},\n        {0x02, 0x01, 0xf3, 0x00}, {0x09, 0x01, 0xf3, 0x00},\n        {0x17, 0x01, 0xf3, 0x00}, {0x28, 0x01, 0xf3, 0x01},\n        {0x02, 0x01, 0xff, 0x00}, {0x09, 0x01, 0xff, 0x00},\n        {0x17, 0x01, 0xff, 0x00}, {0x28, 0x01, 0xff, 0x01},\n        {0x01, 0x01, 0xcb, 0x00}, {0x16, 0x01, 0xcb, 0x01},\n        {0x01, 0x01, 0xcc, 0x00}, {0x16, 0x01, 0xcc, 0x01}\n    },\n    {\n        {0x03, 0x01, 0xf2, 0x00}, {0x06, 0x01, 0xf2, 0x00},\n        {0x0a, 0x01, 0xf2, 0x00}, {0x0f, 0x01, 0xf2, 0x00},\n        {0x18, 0x01, 0xf2, 0x00}, {0x1f, 0x01, 0xf2, 0x00},\n        {0x29, 0x01, 0xf2, 0x00}, {0x38, 0x01, 0xf2, 0x01},\n        {0x03, 0x01, 0xf3, 0x00}, {0x06, 0x01, 0xf3, 0x00},\n        {0x0a, 0x01, 0xf3, 0x00}, {0x0f, 0x01, 0xf3, 0x00},\n        {0x18, 0x01, 0xf3, 0x00}, {0x1f, 0x01, 0xf3, 0x00},\n        {0x29, 0x01, 0xf3, 0x00}, {0x38, 0x01, 0xf3, 0x01}\n    },\n    /* 205 */\n    {\n        {0x03, 0x01, 0xff, 0x00}, {0x06, 0x01, 0xff, 0x00},\n        {0x0a, 0x01, 0xff, 0x00}, {0x0f, 0x01, 0xff, 0x00},\n        {0x18, 0x01, 0xff, 0x00}, {0x1f, 0x01, 0xff, 0x00},\n        {0x29, 0x01, 0xff, 0x00}, {0x38, 0x01, 0xff, 0x01},\n        {0x02, 0x01, 0xcb, 0x00}, {0x09, 0x01, 0xcb, 0x00},\n        {0x17, 0x01, 0xcb, 0x00}, {0x28, 0x01, 0xcb, 0x01},\n        {0x02, 0x01, 0xcc, 0x00}, {0x09, 0x01, 0xcc, 0x00},\n        {0x17, 0x01, 0xcc, 0x00}, {0x28, 0x01, 0xcc, 0x01}\n    },\n    {\n        {0x03, 0x01, 0xcb, 0x00}, {0x06, 0x01, 0xcb, 0x00},\n        {0x0a, 0x01, 0xcb, 0x00}, {0x0f, 0x01, 0xcb, 0x00},\n        {0x18, 0x01, 0xcb, 0x00}, {0x1f, 0x01, 0xcb, 0x00},\n        {0x29, 0x01, 0xcb, 0x00}, {0x38, 0x01, 0xcb, 0x01},\n        {0x03, 0x01, 0xcc, 0x00}, {0x06, 0x01, 0xcc, 0x00},\n        {0x0a, 0x01, 0xcc, 0x00}, {0x0f, 0x01, 0xcc, 0x00},\n        {0x18, 0x01, 0xcc, 0x00}, {0x1f, 0x01, 0xcc, 0x00},\n        {0x29, 0x01, 0xcc, 0x00}, {0x38, 0x01, 0xcc, 0x01}\n    },\n    {\n        {0xd3, 0x00, 0x00, 0x00}, {0xd4, 0x00, 0x00, 0x00},\n        {0xd6, 0x00, 0x00, 0x00}, {0xd7, 0x00, 0x00, 0x00},\n        {0xda, 0x00, 0x00, 0x00}, {0xdb, 0x00, 0x00, 0x00},\n        {0xdd, 0x00, 0x00, 0x00}, {0xde, 0x00, 0x00, 0x00},\n        {0xe2, 0x00, 0x00, 0x00}, {0xe4, 0x00, 0x00, 0x00},\n        {0xe8, 0x00, 0x00, 0x00}, {0xeb, 0x00, 0x00, 0x00},\n        {0xf0, 0x00, 0x00, 0x00}, {0xf3, 0x00, 0x00, 0x00},\n        {0xf7, 0x00, 0x00, 0x00}, {0xfa, 0x00, 0x00, 0x01}\n    },\n    {\n        {0x00, 0x01, 0xd3, 0x01}, {0x00, 0x01, 0xd4, 0x01},\n        {0x00, 0x01, 0xd6, 0x01}, {0x00, 0x01, 0xdd, 0x01},\n        {0x00, 0x01, 0xde, 0x01}, {0x00, 0x01, 0xdf, 0x01},\n        {0x00, 0x01, 0xf1, 0x01}, {0x00, 0x01, 0xf4, 0x01},\n        {0x00, 0x01, 0xf5, 0x01}, {0x00, 0x01, 0xf6, 0x01},\n        {0x00, 0x01, 0xf7, 0x01}, {0x00, 0x01, 0xf8, 0x01},\n        {0x00, 0x01, 0xfa, 0x01}, {0x00, 0x01, 0xfb, 0x01},\n        {0x00, 0x01, 0xfc, 0x01}, {0x00, 0x01, 0xfd, 0x01}\n    },\n    {\n        {0x01, 0x01, 0xd3, 0x00}, {0x16, 0x01, 0xd3, 0x01},\n        {0x01, 0x01, 0xd4, 0x00}, {0x16, 0x01, 0xd4, 0x01},\n        {0x01, 0x01, 0xd6, 0x00}, {0x16, 0x01, 0xd6, 0x01},\n        {0x01, 0x01, 0xdd, 0x00}, {0x16, 0x01, 0xdd, 0x01},\n        {0x01, 0x01, 0xde, 0x00}, {0x16, 0x01, 0xde, 0x01},\n        {0x01, 0x01, 0xdf, 0x00}, {0x16, 0x01, 0xdf, 0x01},\n        {0x01, 0x01, 0xf1, 0x00}, {0x16, 0x01, 0xf1, 0x01},\n        {0x01, 0x01, 0xf4, 0x00}, {0x16, 0x01, 0xf4, 0x01}\n    },\n    /* 210 */\n    {\n        {0x02, 0x01, 0xd3, 0x00}, {0x09, 0x01, 0xd3, 0x00},\n        {0x17, 0x01, 0xd3, 0x00}, {0x28, 0x01, 0xd3, 0x01},\n        {0x02, 0x01, 0xd4, 0x00}, {0x09, 0x01, 0xd4, 0x00},\n        {0x17, 0x01, 0xd4, 0x00}, {0x28, 0x01, 0xd4, 0x01},\n        {0x02, 0x01, 0xd6, 0x00}, {0x09, 0x01, 0xd6, 0x00},\n        {0x17, 0x01, 0xd6, 0x00}, {0x28, 0x01, 0xd6, 0x01},\n        {0x02, 0x01, 0xdd, 0x00}, {0x09, 0x01, 0xdd, 0x00},\n        {0x17, 0x01, 0xdd, 0x00}, {0x28, 0x01, 0xdd, 0x01}\n    },\n    {\n        {0x03, 0x01, 0xd3, 0x00}, {0x06, 0x01, 0xd3, 0x00},\n        {0x0a, 0x01, 0xd3, 0x00}, {0x0f, 0x01, 0xd3, 0x00},\n        {0x18, 0x01, 0xd3, 0x00}, {0x1f, 0x01, 0xd3, 0x00},\n        {0x29, 0x01, 0xd3, 0x00}, {0x38, 0x01, 0xd3, 0x01},\n        {0x03, 0x01, 0xd4, 0x00}, {0x06, 0x01, 0xd4, 0x00},\n        {0x0a, 0x01, 0xd4, 0x00}, {0x0f, 0x01, 0xd4, 0x00},\n        {0x18, 0x01, 0xd4, 0x00}, {0x1f, 0x01, 0xd4, 0x00},\n        {0x29, 0x01, 0xd4, 0x00}, {0x38, 0x01, 0xd4, 0x01}\n    },\n    {\n        {0x03, 0x01, 0xd6, 0x00}, {0x06, 0x01, 0xd6, 0x00},\n        {0x0a, 0x01, 0xd6, 0x00}, {0x0f, 0x01, 0xd6, 0x00},\n        {0x18, 0x01, 0xd6, 0x00}, {0x1f, 0x01, 0xd6, 0x00},\n        {0x29, 0x01, 0xd6, 0x00}, {0x38, 0x01, 0xd6, 0x01},\n        {0x03, 0x01, 0xdd, 0x00}, {0x06, 0x01, 0xdd, 0x00},\n        {0x0a, 0x01, 0xdd, 0x00}, {0x0f, 0x01, 0xdd, 0x00},\n        {0x18, 0x01, 0xdd, 0x00}, {0x1f, 0x01, 0xdd, 0x00},\n        {0x29, 0x01, 0xdd, 0x00}, {0x38, 0x01, 0xdd, 0x01}\n    },\n    {\n        {0x02, 0x01, 0xde, 0x00}, {0x09, 0x01, 0xde, 0x00},\n        {0x17, 0x01, 0xde, 0x00}, {0x28, 0x01, 0xde, 0x01},\n        {0x02, 0x01, 0xdf, 0x00}, {0x09, 0x01, 0xdf, 0x00},\n        {0x17, 0x01, 0xdf, 0x00}, {0x28, 0x01, 0xdf, 0x01},\n        {0x02, 0x01, 0xf1, 0x00}, {0x09, 0x01, 0xf1, 0x00},\n        {0x17, 0x01, 0xf1, 0x00}, {0x28, 0x01, 0xf1, 0x01},\n        {0x02, 0x01, 0xf4, 0x00}, {0x09, 0x01, 0xf4, 0x00},\n        {0x17, 0x01, 0xf4, 0x00}, {0x28, 0x01, 0xf4, 0x01}\n    },\n    {\n        {0x03, 0x01, 0xde, 0x00}, {0x06, 0x01, 0xde, 0x00},\n        {0x0a, 0x01, 0xde, 0x00}, {0x0f, 0x01, 0xde, 0x00},\n        {0x18, 0x01, 0xde, 0x00}, {0x1f, 0x01, 0xde, 0x00},\n        {0x29, 0x01, 0xde, 0x00}, {0x38, 0x01, 0xde, 0x01},\n        {0x03, 0x01, 0xdf, 0x00}, {0x06, 0x01, 0xdf, 0x00},\n        {0x0a, 0x01, 0xdf, 0x00}, {0x0f, 0x01, 0xdf, 0x00},\n        {0x18, 0x01, 0xdf, 0x00}, {0x1f, 0x01, 0xdf, 0x00},\n        {0x29, 0x01, 0xdf, 0x00}, {0x38, 0x01, 0xdf, 0x01}\n    },\n    /* 215 */\n    {\n        {0x03, 0x01, 0xf1, 0x00}, {0x06, 0x01, 0xf1, 0x00},\n        {0x0a, 0x01, 0xf1, 0x00}, {0x0f, 0x01, 0xf1, 0x00},\n        {0x18, 0x01, 0xf1, 0x00}, {0x1f, 0x01, 0xf1, 0x00},\n        {0x29, 0x01, 0xf1, 0x00}, {0x38, 0x01, 0xf1, 0x01},\n        {0x03, 0x01, 0xf4, 0x00}, {0x06, 0x01, 0xf4, 0x00},\n        {0x0a, 0x01, 0xf4, 0x00}, {0x0f, 0x01, 0xf4, 0x00},\n        {0x18, 0x01, 0xf4, 0x00}, {0x1f, 0x01, 0xf4, 0x00},\n        {0x29, 0x01, 0xf4, 0x00}, {0x38, 0x01, 0xf4, 0x01}\n    },\n    {\n        {0x01, 0x01, 0xf5, 0x00}, {0x16, 0x01, 0xf5, 0x01},\n        {0x01, 0x01, 0xf6, 0x00}, {0x16, 0x01, 0xf6, 0x01},\n        {0x01, 0x01, 0xf7, 0x00}, {0x16, 0x01, 0xf7, 0x01},\n        {0x01, 0x01, 0xf8, 0x00}, {0x16, 0x01, 0xf8, 0x01},\n        {0x01, 0x01, 0xfa, 0x00}, {0x16, 0x01, 0xfa, 0x01},\n        {0x01, 0x01, 0xfb, 0x00}, {0x16, 0x01, 0xfb, 0x01},\n        {0x01, 0x01, 0xfc, 0x00}, {0x16, 0x01, 0xfc, 0x01},\n        {0x01, 0x01, 0xfd, 0x00}, {0x16, 0x01, 0xfd, 0x01}\n    },\n    {\n        {0x02, 0x01, 0xf5, 0x00}, {0x09, 0x01, 0xf5, 0x00},\n        {0x17, 0x01, 0xf5, 0x00}, {0x28, 0x01, 0xf5, 0x01},\n        {0x02, 0x01, 0xf6, 0x00}, {0x09, 0x01, 0xf6, 0x00},\n        {0x17, 0x01, 0xf6, 0x00}, {0x28, 0x01, 0xf6, 0x01},\n        {0x02, 0x01, 0xf7, 0x00}, {0x09, 0x01, 0xf7, 0x00},\n        {0x17, 0x01, 0xf7, 0x00}, {0x28, 0x01, 0xf7, 0x01},\n        {0x02, 0x01, 0xf8, 0x00}, {0x09, 0x01, 0xf8, 0x00},\n        {0x17, 0x01, 0xf8, 0x00}, {0x28, 0x01, 0xf8, 0x01}\n    },\n    {\n        {0x03, 0x01, 0xf5, 0x00}, {0x06, 0x01, 0xf5, 0x00},\n        {0x0a, 0x01, 0xf5, 0x00}, {0x0f, 0x01, 0xf5, 0x00},\n        {0x18, 0x01, 0xf5, 0x00}, {0x1f, 0x01, 0xf5, 0x00},\n        {0x29, 0x01, 0xf5, 0x00}, {0x38, 0x01, 0xf5, 0x01},\n        {0x03, 0x01, 0xf6, 0x00}, {0x06, 0x01, 0xf6, 0x00},\n        {0x0a, 0x01, 0xf6, 0x00}, {0x0f, 0x01, 0xf6, 0x00},\n        {0x18, 0x01, 0xf6, 0x00}, {0x1f, 0x01, 0xf6, 0x00},\n        {0x29, 0x01, 0xf6, 0x00}, {0x38, 0x01, 0xf6, 0x01}\n    },\n    {\n        {0x03, 0x01, 0xf7, 0x00}, {0x06, 0x01, 0xf7, 0x00},\n        {0x0a, 0x01, 0xf7, 0x00}, {0x0f, 0x01, 0xf7, 0x00},\n        {0x18, 0x01, 0xf7, 0x00}, {0x1f, 0x01, 0xf7, 0x00},\n        {0x29, 0x01, 0xf7, 0x00}, {0x38, 0x01, 0xf7, 0x01},\n        {0x03, 0x01, 0xf8, 0x00}, {0x06, 0x01, 0xf8, 0x00},\n        {0x0a, 0x01, 0xf8, 0x00}, {0x0f, 0x01, 0xf8, 0x00},\n        {0x18, 0x01, 0xf8, 0x00}, {0x1f, 0x01, 0xf8, 0x00},\n        {0x29, 0x01, 0xf8, 0x00}, {0x38, 0x01, 0xf8, 0x01}\n    },\n    /* 220 */\n    {\n        {0x02, 0x01, 0xfa, 0x00}, {0x09, 0x01, 0xfa, 0x00},\n        {0x17, 0x01, 0xfa, 0x00}, {0x28, 0x01, 0xfa, 0x01},\n        {0x02, 0x01, 0xfb, 0x00}, {0x09, 0x01, 0xfb, 0x00},\n        {0x17, 0x01, 0xfb, 0x00}, {0x28, 0x01, 0xfb, 0x01},\n        {0x02, 0x01, 0xfc, 0x00}, {0x09, 0x01, 0xfc, 0x00},\n        {0x17, 0x01, 0xfc, 0x00}, {0x28, 0x01, 0xfc, 0x01},\n        {0x02, 0x01, 0xfd, 0x00}, {0x09, 0x01, 0xfd, 0x00},\n        {0x17, 0x01, 0xfd, 0x00}, {0x28, 0x01, 0xfd, 0x01}\n    },\n    {\n        {0x03, 0x01, 0xfa, 0x00}, {0x06, 0x01, 0xfa, 0x00},\n        {0x0a, 0x01, 0xfa, 0x00}, {0x0f, 0x01, 0xfa, 0x00},\n        {0x18, 0x01, 0xfa, 0x00}, {0x1f, 0x01, 0xfa, 0x00},\n        {0x29, 0x01, 0xfa, 0x00}, {0x38, 0x01, 0xfa, 0x01},\n        {0x03, 0x01, 0xfb, 0x00}, {0x06, 0x01, 0xfb, 0x00},\n        {0x0a, 0x01, 0xfb, 0x00}, {0x0f, 0x01, 0xfb, 0x00},\n        {0x18, 0x01, 0xfb, 0x00}, {0x1f, 0x01, 0xfb, 0x00},\n        {0x29, 0x01, 0xfb, 0x00}, {0x38, 0x01, 0xfb, 0x01}\n    },\n    {\n        {0x03, 0x01, 0xfc, 0x00}, {0x06, 0x01, 0xfc, 0x00},\n        {0x0a, 0x01, 0xfc, 0x00}, {0x0f, 0x01, 0xfc, 0x00},\n        {0x18, 0x01, 0xfc, 0x00}, {0x1f, 0x01, 0xfc, 0x00},\n        {0x29, 0x01, 0xfc, 0x00}, {0x38, 0x01, 0xfc, 0x01},\n        {0x03, 0x01, 0xfd, 0x00}, {0x06, 0x01, 0xfd, 0x00},\n        {0x0a, 0x01, 0xfd, 0x00}, {0x0f, 0x01, 0xfd, 0x00},\n        {0x18, 0x01, 0xfd, 0x00}, {0x1f, 0x01, 0xfd, 0x00},\n        {0x29, 0x01, 0xfd, 0x00}, {0x38, 0x01, 0xfd, 0x01}\n    },\n    {\n        {0x00, 0x01, 0xfe, 0x01}, {0xe3, 0x00, 0x00, 0x00},\n        {0xe5, 0x00, 0x00, 0x00}, {0xe6, 0x00, 0x00, 0x00},\n        {0xe9, 0x00, 0x00, 0x00}, {0xea, 0x00, 0x00, 0x00},\n        {0xec, 0x00, 0x00, 0x00}, {0xed, 0x00, 0x00, 0x00},\n        {0xf1, 0x00, 0x00, 0x00}, {0xf2, 0x00, 0x00, 0x00},\n        {0xf4, 0x00, 0x00, 0x00}, {0xf5, 0x00, 0x00, 0x00},\n        {0xf8, 0x00, 0x00, 0x00}, {0xf9, 0x00, 0x00, 0x00},\n        {0xfb, 0x00, 0x00, 0x00}, {0xfc, 0x00, 0x00, 0x01}\n    },\n    {\n        {0x01, 0x01, 0xfe, 0x00}, {0x16, 0x01, 0xfe, 0x01},\n        {0x00, 0x01, 0x02, 0x01}, {0x00, 0x01, 0x03, 0x01},\n        {0x00, 0x01, 0x04, 0x01}, {0x00, 0x01, 0x05, 0x01},\n        {0x00, 0x01, 0x06, 0x01}, {0x00, 0x01, 0x07, 0x01},\n        {0x00, 0x01, 0x08, 0x01}, {0x00, 0x01, 0x0b, 0x01},\n        {0x00, 0x01, 0x0c, 0x01}, {0x00, 0x01, 0x0e, 0x01},\n        {0x00, 0x01, 0x0f, 0x01}, {0x00, 0x01, 0x10, 0x01},\n        {0x00, 0x01, 0x11, 0x01}, {0x00, 0x01, 0x12, 0x01}\n    },\n    /* 225 */\n    {\n        {0x02, 0x01, 0xfe, 0x00}, {0x09, 0x01, 0xfe, 0x00},\n        {0x17, 0x01, 0xfe, 0x00}, {0x28, 0x01, 0xfe, 0x01},\n        {0x01, 0x01, 0x02, 0x00}, {0x16, 0x01, 0x02, 0x01},\n        {0x01, 0x01, 0x03, 0x00}, {0x16, 0x01, 0x03, 0x01},\n        {0x01, 0x01, 0x04, 0x00}, {0x16, 0x01, 0x04, 0x01},\n        {0x01, 0x01, 0x05, 0x00}, {0x16, 0x01, 0x05, 0x01},\n        {0x01, 0x01, 0x06, 0x00}, {0x16, 0x01, 0x06, 0x01},\n        {0x01, 0x01, 0x07, 0x00}, {0x16, 0x01, 0x07, 0x01}\n    },\n    {\n        {0x03, 0x01, 0xfe, 0x00}, {0x06, 0x01, 0xfe, 0x00},\n        {0x0a, 0x01, 0xfe, 0x00}, {0x0f, 0x01, 0xfe, 0x00},\n        {0x18, 0x01, 0xfe, 0x00}, {0x1f, 0x01, 0xfe, 0x00},\n        {0x29, 0x01, 0xfe, 0x00}, {0x38, 0x01, 0xfe, 0x01},\n        {0x02, 0x01, 0x02, 0x00}, {0x09, 0x01, 0x02, 0x00},\n        {0x17, 0x01, 0x02, 0x00}, {0x28, 0x01, 0x02, 0x01},\n        {0x02, 0x01, 0x03, 0x00}, {0x09, 0x01, 0x03, 0x00},\n        {0x17, 0x01, 0x03, 0x00}, {0x28, 0x01, 0x03, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x02, 0x00}, {0x06, 0x01, 0x02, 0x00},\n        {0x0a, 0x01, 0x02, 0x00}, {0x0f, 0x01, 0x02, 0x00},\n        {0x18, 0x01, 0x02, 0x00}, {0x1f, 0x01, 0x02, 0x00},\n        {0x29, 0x01, 0x02, 0x00}, {0x38, 0x01, 0x02, 0x01},\n        {0x03, 0x01, 0x03, 0x00}, {0x06, 0x01, 0x03, 0x00},\n        {0x0a, 0x01, 0x03, 0x00}, {0x0f, 0x01, 0x03, 0x00},\n        {0x18, 0x01, 0x03, 0x00}, {0x1f, 0x01, 0x03, 0x00},\n        {0x29, 0x01, 0x03, 0x00}, {0x38, 0x01, 0x03, 0x01}\n    },\n    {\n        {0x02, 0x01, 0x04, 0x00}, {0x09, 0x01, 0x04, 0x00},\n        {0x17, 0x01, 0x04, 0x00}, {0x28, 0x01, 0x04, 0x01},\n        {0x02, 0x01, 0x05, 0x00}, {0x09, 0x01, 0x05, 0x00},\n        {0x17, 0x01, 0x05, 0x00}, {0x28, 0x01, 0x05, 0x01},\n        {0x02, 0x01, 0x06, 0x00}, {0x09, 0x01, 0x06, 0x00},\n        {0x17, 0x01, 0x06, 0x00}, {0x28, 0x01, 0x06, 0x01},\n        {0x02, 0x01, 0x07, 0x00}, {0x09, 0x01, 0x07, 0x00},\n        {0x17, 0x01, 0x07, 0x00}, {0x28, 0x01, 0x07, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x04, 0x00}, {0x06, 0x01, 0x04, 0x00},\n        {0x0a, 0x01, 0x04, 0x00}, {0x0f, 0x01, 0x04, 0x00},\n        {0x18, 0x01, 0x04, 0x00}, {0x1f, 0x01, 0x04, 0x00},\n        {0x29, 0x01, 0x04, 0x00}, {0x38, 0x01, 0x04, 0x01},\n        {0x03, 0x01, 0x05, 0x00}, {0x06, 0x01, 0x05, 0x00},\n        {0x0a, 0x01, 0x05, 0x00}, {0x0f, 0x01, 0x05, 0x00},\n        {0x18, 0x01, 0x05, 0x00}, {0x1f, 0x01, 0x05, 0x00},\n        {0x29, 0x01, 0x05, 0x00}, {0x38, 0x01, 0x05, 0x01}\n    },\n    /* 230 */\n    {\n        {0x03, 0x01, 0x06, 0x00}, {0x06, 0x01, 0x06, 0x00},\n        {0x0a, 0x01, 0x06, 0x00}, {0x0f, 0x01, 0x06, 0x00},\n        {0x18, 0x01, 0x06, 0x00}, {0x1f, 0x01, 0x06, 0x00},\n        {0x29, 0x01, 0x06, 0x00}, {0x38, 0x01, 0x06, 0x01},\n        {0x03, 0x01, 0x07, 0x00}, {0x06, 0x01, 0x07, 0x00},\n        {0x0a, 0x01, 0x07, 0x00}, {0x0f, 0x01, 0x07, 0x00},\n        {0x18, 0x01, 0x07, 0x00}, {0x1f, 0x01, 0x07, 0x00},\n        {0x29, 0x01, 0x07, 0x00}, {0x38, 0x01, 0x07, 0x01}\n    },\n    {\n        {0x01, 0x01, 0x08, 0x00}, {0x16, 0x01, 0x08, 0x01},\n        {0x01, 0x01, 0x0b, 0x00}, {0x16, 0x01, 0x0b, 0x01},\n        {0x01, 0x01, 0x0c, 0x00}, {0x16, 0x01, 0x0c, 0x01},\n        {0x01, 0x01, 0x0e, 0x00}, {0x16, 0x01, 0x0e, 0x01},\n        {0x01, 0x01, 0x0f, 0x00}, {0x16, 0x01, 0x0f, 0x01},\n        {0x01, 0x01, 0x10, 0x00}, {0x16, 0x01, 0x10, 0x01},\n        {0x01, 0x01, 0x11, 0x00}, {0x16, 0x01, 0x11, 0x01},\n        {0x01, 0x01, 0x12, 0x00}, {0x16, 0x01, 0x12, 0x01}\n    },\n    {\n        {0x02, 0x01, 0x08, 0x00}, {0x09, 0x01, 0x08, 0x00},\n        {0x17, 0x01, 0x08, 0x00}, {0x28, 0x01, 0x08, 0x01},\n        {0x02, 0x01, 0x0b, 0x00}, {0x09, 0x01, 0x0b, 0x00},\n        {0x17, 0x01, 0x0b, 0x00}, {0x28, 0x01, 0x0b, 0x01},\n        {0x02, 0x01, 0x0c, 0x00}, {0x09, 0x01, 0x0c, 0x00},\n        {0x17, 0x01, 0x0c, 0x00}, {0x28, 0x01, 0x0c, 0x01},\n        {0x02, 0x01, 0x0e, 0x00}, {0x09, 0x01, 0x0e, 0x00},\n        {0x17, 0x01, 0x0e, 0x00}, {0x28, 0x01, 0x0e, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x08, 0x00}, {0x06, 0x01, 0x08, 0x00},\n        {0x0a, 0x01, 0x08, 0x00}, {0x0f, 0x01, 0x08, 0x00},\n        {0x18, 0x01, 0x08, 0x00}, {0x1f, 0x01, 0x08, 0x00},\n        {0x29, 0x01, 0x08, 0x00}, {0x38, 0x01, 0x08, 0x01},\n        {0x03, 0x01, 0x0b, 0x00}, {0x06, 0x01, 0x0b, 0x00},\n        {0x0a, 0x01, 0x0b, 0x00}, {0x0f, 0x01, 0x0b, 0x00},\n        {0x18, 0x01, 0x0b, 0x00}, {0x1f, 0x01, 0x0b, 0x00},\n        {0x29, 0x01, 0x0b, 0x00}, {0x38, 0x01, 0x0b, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x0c, 0x00}, {0x06, 0x01, 0x0c, 0x00},\n        {0x0a, 0x01, 0x0c, 0x00}, {0x0f, 0x01, 0x0c, 0x00},\n        {0x18, 0x01, 0x0c, 0x00}, {0x1f, 0x01, 0x0c, 0x00},\n        {0x29, 0x01, 0x0c, 0x00}, {0x38, 0x01, 0x0c, 0x01},\n        {0x03, 0x01, 0x0e, 0x00}, {0x06, 0x01, 0x0e, 0x00},\n        {0x0a, 0x01, 0x0e, 0x00}, {0x0f, 0x01, 0x0e, 0x00},\n        {0x18, 0x01, 0x0e, 0x00}, {0x1f, 0x01, 0x0e, 0x00},\n        {0x29, 0x01, 0x0e, 0x00}, {0x38, 0x01, 0x0e, 0x01}\n    },\n    /* 235 */\n    {\n        {0x02, 0x01, 0x0f, 0x00}, {0x09, 0x01, 0x0f, 0x00},\n        {0x17, 0x01, 0x0f, 0x00}, {0x28, 0x01, 0x0f, 0x01},\n        {0x02, 0x01, 0x10, 0x00}, {0x09, 0x01, 0x10, 0x00},\n        {0x17, 0x01, 0x10, 0x00}, {0x28, 0x01, 0x10, 0x01},\n        {0x02, 0x01, 0x11, 0x00}, {0x09, 0x01, 0x11, 0x00},\n        {0x17, 0x01, 0x11, 0x00}, {0x28, 0x01, 0x11, 0x01},\n        {0x02, 0x01, 0x12, 0x00}, {0x09, 0x01, 0x12, 0x00},\n        {0x17, 0x01, 0x12, 0x00}, {0x28, 0x01, 0x12, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x0f, 0x00}, {0x06, 0x01, 0x0f, 0x00},\n        {0x0a, 0x01, 0x0f, 0x00}, {0x0f, 0x01, 0x0f, 0x00},\n        {0x18, 0x01, 0x0f, 0x00}, {0x1f, 0x01, 0x0f, 0x00},\n        {0x29, 0x01, 0x0f, 0x00}, {0x38, 0x01, 0x0f, 0x01},\n        {0x03, 0x01, 0x10, 0x00}, {0x06, 0x01, 0x10, 0x00},\n        {0x0a, 0x01, 0x10, 0x00}, {0x0f, 0x01, 0x10, 0x00},\n        {0x18, 0x01, 0x10, 0x00}, {0x1f, 0x01, 0x10, 0x00},\n        {0x29, 0x01, 0x10, 0x00}, {0x38, 0x01, 0x10, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x11, 0x00}, {0x06, 0x01, 0x11, 0x00},\n        {0x0a, 0x01, 0x11, 0x00}, {0x0f, 0x01, 0x11, 0x00},\n        {0x18, 0x01, 0x11, 0x00}, {0x1f, 0x01, 0x11, 0x00},\n        {0x29, 0x01, 0x11, 0x00}, {0x38, 0x01, 0x11, 0x01},\n        {0x03, 0x01, 0x12, 0x00}, {0x06, 0x01, 0x12, 0x00},\n        {0x0a, 0x01, 0x12, 0x00}, {0x0f, 0x01, 0x12, 0x00},\n        {0x18, 0x01, 0x12, 0x00}, {0x1f, 0x01, 0x12, 0x00},\n        {0x29, 0x01, 0x12, 0x00}, {0x38, 0x01, 0x12, 0x01}\n    },\n    {\n        {0x00, 0x01, 0x13, 0x01}, {0x00, 0x01, 0x14, 0x01},\n        {0x00, 0x01, 0x15, 0x01}, {0x00, 0x01, 0x17, 0x01},\n        {0x00, 0x01, 0x18, 0x01}, {0x00, 0x01, 0x19, 0x01},\n        {0x00, 0x01, 0x1a, 0x01}, {0x00, 0x01, 0x1b, 0x01},\n        {0x00, 0x01, 0x1c, 0x01}, {0x00, 0x01, 0x1d, 0x01},\n        {0x00, 0x01, 0x1e, 0x01}, {0x00, 0x01, 0x1f, 0x01},\n        {0x00, 0x01, 0x7f, 0x01}, {0x00, 0x01, 0xdc, 0x01},\n        {0x00, 0x01, 0xf9, 0x01}, {0xfd, 0x00, 0x00, 0x01}\n    },\n    {\n        {0x01, 0x01, 0x13, 0x00}, {0x16, 0x01, 0x13, 0x01},\n        {0x01, 0x01, 0x14, 0x00}, {0x16, 0x01, 0x14, 0x01},\n        {0x01, 0x01, 0x15, 0x00}, {0x16, 0x01, 0x15, 0x01},\n        {0x01, 0x01, 0x17, 0x00}, {0x16, 0x01, 0x17, 0x01},\n        {0x01, 0x01, 0x18, 0x00}, {0x16, 0x01, 0x18, 0x01},\n        {0x01, 0x01, 0x19, 0x00}, {0x16, 0x01, 0x19, 0x01},\n        {0x01, 0x01, 0x1a, 0x00}, {0x16, 0x01, 0x1a, 0x01},\n        {0x01, 0x01, 0x1b, 0x00}, {0x16, 0x01, 0x1b, 0x01}\n    },\n    /* 240 */\n    {\n        {0x02, 0x01, 0x13, 0x00}, {0x09, 0x01, 0x13, 0x00},\n        {0x17, 0x01, 0x13, 0x00}, {0x28, 0x01, 0x13, 0x01},\n        {0x02, 0x01, 0x14, 0x00}, {0x09, 0x01, 0x14, 0x00},\n        {0x17, 0x01, 0x14, 0x00}, {0x28, 0x01, 0x14, 0x01},\n        {0x02, 0x01, 0x15, 0x00}, {0x09, 0x01, 0x15, 0x00},\n        {0x17, 0x01, 0x15, 0x00}, {0x28, 0x01, 0x15, 0x01},\n        {0x02, 0x01, 0x17, 0x00}, {0x09, 0x01, 0x17, 0x00},\n        {0x17, 0x01, 0x17, 0x00}, {0x28, 0x01, 0x17, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x13, 0x00}, {0x06, 0x01, 0x13, 0x00},\n        {0x0a, 0x01, 0x13, 0x00}, {0x0f, 0x01, 0x13, 0x00},\n        {0x18, 0x01, 0x13, 0x00}, {0x1f, 0x01, 0x13, 0x00},\n        {0x29, 0x01, 0x13, 0x00}, {0x38, 0x01, 0x13, 0x01},\n        {0x03, 0x01, 0x14, 0x00}, {0x06, 0x01, 0x14, 0x00},\n        {0x0a, 0x01, 0x14, 0x00}, {0x0f, 0x01, 0x14, 0x00},\n        {0x18, 0x01, 0x14, 0x00}, {0x1f, 0x01, 0x14, 0x00},\n        {0x29, 0x01, 0x14, 0x00}, {0x38, 0x01, 0x14, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x15, 0x00}, {0x06, 0x01, 0x15, 0x00},\n        {0x0a, 0x01, 0x15, 0x00}, {0x0f, 0x01, 0x15, 0x00},\n        {0x18, 0x01, 0x15, 0x00}, {0x1f, 0x01, 0x15, 0x00},\n        {0x29, 0x01, 0x15, 0x00}, {0x38, 0x01, 0x15, 0x01},\n        {0x03, 0x01, 0x17, 0x00}, {0x06, 0x01, 0x17, 0x00},\n        {0x0a, 0x01, 0x17, 0x00}, {0x0f, 0x01, 0x17, 0x00},\n        {0x18, 0x01, 0x17, 0x00}, {0x1f, 0x01, 0x17, 0x00},\n        {0x29, 0x01, 0x17, 0x00}, {0x38, 0x01, 0x17, 0x01}\n    },\n    {\n        {0x02, 0x01, 0x18, 0x00}, {0x09, 0x01, 0x18, 0x00},\n        {0x17, 0x01, 0x18, 0x00}, {0x28, 0x01, 0x18, 0x01},\n        {0x02, 0x01, 0x19, 0x00}, {0x09, 0x01, 0x19, 0x00},\n        {0x17, 0x01, 0x19, 0x00}, {0x28, 0x01, 0x19, 0x01},\n        {0x02, 0x01, 0x1a, 0x00}, {0x09, 0x01, 0x1a, 0x00},\n        {0x17, 0x01, 0x1a, 0x00}, {0x28, 0x01, 0x1a, 0x01},\n        {0x02, 0x01, 0x1b, 0x00}, {0x09, 0x01, 0x1b, 0x00},\n        {0x17, 0x01, 0x1b, 0x00}, {0x28, 0x01, 0x1b, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x18, 0x00}, {0x06, 0x01, 0x18, 0x00},\n        {0x0a, 0x01, 0x18, 0x00}, {0x0f, 0x01, 0x18, 0x00},\n        {0x18, 0x01, 0x18, 0x00}, {0x1f, 0x01, 0x18, 0x00},\n        {0x29, 0x01, 0x18, 0x00}, {0x38, 0x01, 0x18, 0x01},\n        {0x03, 0x01, 0x19, 0x00}, {0x06, 0x01, 0x19, 0x00},\n        {0x0a, 0x01, 0x19, 0x00}, {0x0f, 0x01, 0x19, 0x00},\n        {0x18, 0x01, 0x19, 0x00}, {0x1f, 0x01, 0x19, 0x00},\n        {0x29, 0x01, 0x19, 0x00}, {0x38, 0x01, 0x19, 0x01}\n    },\n    /* 245 */\n    {\n        {0x03, 0x01, 0x1a, 0x00}, {0x06, 0x01, 0x1a, 0x00},\n        {0x0a, 0x01, 0x1a, 0x00}, {0x0f, 0x01, 0x1a, 0x00},\n        {0x18, 0x01, 0x1a, 0x00}, {0x1f, 0x01, 0x1a, 0x00},\n        {0x29, 0x01, 0x1a, 0x00}, {0x38, 0x01, 0x1a, 0x01},\n        {0x03, 0x01, 0x1b, 0x00}, {0x06, 0x01, 0x1b, 0x00},\n        {0x0a, 0x01, 0x1b, 0x00}, {0x0f, 0x01, 0x1b, 0x00},\n        {0x18, 0x01, 0x1b, 0x00}, {0x1f, 0x01, 0x1b, 0x00},\n        {0x29, 0x01, 0x1b, 0x00}, {0x38, 0x01, 0x1b, 0x01}\n    },\n    {\n        {0x01, 0x01, 0x1c, 0x00}, {0x16, 0x01, 0x1c, 0x01},\n        {0x01, 0x01, 0x1d, 0x00}, {0x16, 0x01, 0x1d, 0x01},\n        {0x01, 0x01, 0x1e, 0x00}, {0x16, 0x01, 0x1e, 0x01},\n        {0x01, 0x01, 0x1f, 0x00}, {0x16, 0x01, 0x1f, 0x01},\n        {0x01, 0x01, 0x7f, 0x00}, {0x16, 0x01, 0x7f, 0x01},\n        {0x01, 0x01, 0xdc, 0x00}, {0x16, 0x01, 0xdc, 0x01},\n        {0x01, 0x01, 0xf9, 0x00}, {0x16, 0x01, 0xf9, 0x01},\n        {0xfe, 0x00, 0x00, 0x00}, {0xff, 0x00, 0x00, 0x01}\n    },\n    {\n        {0x02, 0x01, 0x1c, 0x00}, {0x09, 0x01, 0x1c, 0x00},\n        {0x17, 0x01, 0x1c, 0x00}, {0x28, 0x01, 0x1c, 0x01},\n        {0x02, 0x01, 0x1d, 0x00}, {0x09, 0x01, 0x1d, 0x00},\n        {0x17, 0x01, 0x1d, 0x00}, {0x28, 0x01, 0x1d, 0x01},\n        {0x02, 0x01, 0x1e, 0x00}, {0x09, 0x01, 0x1e, 0x00},\n        {0x17, 0x01, 0x1e, 0x00}, {0x28, 0x01, 0x1e, 0x01},\n        {0x02, 0x01, 0x1f, 0x00}, {0x09, 0x01, 0x1f, 0x00},\n        {0x17, 0x01, 0x1f, 0x00}, {0x28, 0x01, 0x1f, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x1c, 0x00}, {0x06, 0x01, 0x1c, 0x00},\n        {0x0a, 0x01, 0x1c, 0x00}, {0x0f, 0x01, 0x1c, 0x00},\n        {0x18, 0x01, 0x1c, 0x00}, {0x1f, 0x01, 0x1c, 0x00},\n        {0x29, 0x01, 0x1c, 0x00}, {0x38, 0x01, 0x1c, 0x01},\n        {0x03, 0x01, 0x1d, 0x00}, {0x06, 0x01, 0x1d, 0x00},\n        {0x0a, 0x01, 0x1d, 0x00}, {0x0f, 0x01, 0x1d, 0x00},\n        {0x18, 0x01, 0x1d, 0x00}, {0x1f, 0x01, 0x1d, 0x00},\n        {0x29, 0x01, 0x1d, 0x00}, {0x38, 0x01, 0x1d, 0x01}\n    },\n    {\n        {0x03, 0x01, 0x1e, 0x00}, {0x06, 0x01, 0x1e, 0x00},\n        {0x0a, 0x01, 0x1e, 0x00}, {0x0f, 0x01, 0x1e, 0x00},\n        {0x18, 0x01, 0x1e, 0x00}, {0x1f, 0x01, 0x1e, 0x00},\n        {0x29, 0x01, 0x1e, 0x00}, {0x38, 0x01, 0x1e, 0x01},\n        {0x03, 0x01, 0x1f, 0x00}, {0x06, 0x01, 0x1f, 0x00},\n        {0x0a, 0x01, 0x1f, 0x00}, {0x0f, 0x01, 0x1f, 0x00},\n        {0x18, 0x01, 0x1f, 0x00}, {0x1f, 0x01, 0x1f, 0x00},\n        {0x29, 0x01, 0x1f, 0x00}, {0x38, 0x01, 0x1f, 0x01}\n    },\n    /* 250 */\n    {\n        {0x02, 0x01, 0x7f, 0x00}, {0x09, 0x01, 0x7f, 0x00},\n        {0x17, 0x01, 0x7f, 0x00}, {0x28, 0x01, 0x7f, 0x01},\n        {0x02, 0x01, 0xdc, 0x00}, {0x09, 0x01, 0xdc, 0x00},\n        {0x17, 0x01, 0xdc, 0x00}, {0x28, 0x01, 0xdc, 0x01},\n        {0x02, 0x01, 0xf9, 0x00}, {0x09, 0x01, 0xf9, 0x00},\n        {0x17, 0x01, 0xf9, 0x00}, {0x28, 0x01, 0xf9, 0x01},\n        {0x00, 0x01, 0x0a, 0x01}, {0x00, 0x01, 0x0d, 0x01},\n        {0x00, 0x01, 0x16, 0x01}, {0xfa, 0x00, 0x00, 0x00}\n    },\n    {\n        {0x03, 0x01, 0x7f, 0x00}, {0x06, 0x01, 0x7f, 0x00},\n        {0x0a, 0x01, 0x7f, 0x00}, {0x0f, 0x01, 0x7f, 0x00},\n        {0x18, 0x01, 0x7f, 0x00}, {0x1f, 0x01, 0x7f, 0x00},\n        {0x29, 0x01, 0x7f, 0x00}, {0x38, 0x01, 0x7f, 0x01},\n        {0x03, 0x01, 0xdc, 0x00}, {0x06, 0x01, 0xdc, 0x00},\n        {0x0a, 0x01, 0xdc, 0x00}, {0x0f, 0x01, 0xdc, 0x00},\n        {0x18, 0x01, 0xdc, 0x00}, {0x1f, 0x01, 0xdc, 0x00},\n        {0x29, 0x01, 0xdc, 0x00}, {0x38, 0x01, 0xdc, 0x01}\n    },\n    {\n        {0x03, 0x01, 0xf9, 0x00}, {0x06, 0x01, 0xf9, 0x00},\n        {0x0a, 0x01, 0xf9, 0x00}, {0x0f, 0x01, 0xf9, 0x00},\n        {0x18, 0x01, 0xf9, 0x00}, {0x1f, 0x01, 0xf9, 0x00},\n        {0x29, 0x01, 0xf9, 0x00}, {0x38, 0x01, 0xf9, 0x01},\n        {0x01, 0x01, 0x0a, 0x00}, {0x16, 0x01, 0x0a, 0x01},\n        {0x01, 0x01, 0x0d, 0x00}, {0x16, 0x01, 0x0d, 0x01},\n        {0x01, 0x01, 0x16, 0x00}, {0x16, 0x01, 0x16, 0x01},\n        {0xfc, 0x00, 0x00, 0x00}, {0xfc, 0x00, 0x00, 0x00}\n    },\n    {\n        {0x02, 0x01, 0x0a, 0x00}, {0x09, 0x01, 0x0a, 0x00},\n        {0x17, 0x01, 0x0a, 0x00}, {0x28, 0x01, 0x0a, 0x01},\n        {0x02, 0x01, 0x0d, 0x00}, {0x09, 0x01, 0x0d, 0x00},\n        {0x17, 0x01, 0x0d, 0x00}, {0x28, 0x01, 0x0d, 0x01},\n        {0x02, 0x01, 0x16, 0x00}, {0x09, 0x01, 0x16, 0x00},\n        {0x17, 0x01, 0x16, 0x00}, {0x28, 0x01, 0x16, 0x01},\n        {0xfd, 0x00, 0x00, 0x00}, {0xfd, 0x00, 0x00, 0x00},\n        {0xfd, 0x00, 0x00, 0x00}, {0xfd, 0x00, 0x00, 0x00}\n    },\n    {\n        {0x03, 0x01, 0x0a, 0x00}, {0x06, 0x01, 0x0a, 0x00},\n        {0x0a, 0x01, 0x0a, 0x00}, {0x0f, 0x01, 0x0a, 0x00},\n        {0x18, 0x01, 0x0a, 0x00}, {0x1f, 0x01, 0x0a, 0x00},\n        {0x29, 0x01, 0x0a, 0x00}, {0x38, 0x01, 0x0a, 0x01},\n        {0x03, 0x01, 0x0d, 0x00}, {0x06, 0x01, 0x0d, 0x00},\n        {0x0a, 0x01, 0x0d, 0x00}, {0x0f, 0x01, 0x0d, 0x00},\n        {0x18, 0x01, 0x0d, 0x00}, {0x1f, 0x01, 0x0d, 0x00},\n        {0x29, 0x01, 0x0d, 0x00}, {0x38, 0x01, 0x0d, 0x01}\n    },\n    /* 255 */\n    {\n        {0x03, 0x01, 0x16, 0x00}, {0x06, 0x01, 0x16, 0x00},\n        {0x0a, 0x01, 0x16, 0x00}, {0x0f, 0x01, 0x16, 0x00},\n        {0x18, 0x01, 0x16, 0x00}, {0x1f, 0x01, 0x16, 0x00},\n        {0x29, 0x01, 0x16, 0x00}, {0x38, 0x01, 0x16, 0x01},\n        {0xff, 0x00, 0x00, 0x00}, {0xff, 0x00, 0x00, 0x00},\n        {0xff, 0x00, 0x00, 0x00}, {0xff, 0x00, 0x00, 0x00},\n        {0xff, 0x00, 0x00, 0x00}, {0xff, 0x00, 0x00, 0x00},\n        {0xff, 0x00, 0x00, 0x00}, {0xff, 0x00, 0x00, 0x00}\n    }\n};\n\n\nngx_int_t\nngx_http_huff_decode(u_char *state, u_char *src, size_t len, u_char **dst,\n    ngx_uint_t last, ngx_log_t *log)\n{\n    u_char  *end, ch, ending;\n\n    ch = 0;\n    ending = 1;\n\n    end = src + len;\n\n    while (src != end) {\n        ch = *src++;\n\n        if (ngx_http_huff_decode_bits(state, &ending, ch >> 4, dst)\n            != NGX_OK)\n        {\n            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, log, 0,\n                           \"http2 huffman decoding error at state %d: \"\n                           \"bad code 0x%Xd\", *state, ch >> 4);\n\n            return NGX_ERROR;\n        }\n\n        if (ngx_http_huff_decode_bits(state, &ending, ch & 0xf, dst)\n            != NGX_OK)\n        {\n            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, log, 0,\n                           \"http2 huffman decoding error at state %d: \"\n                           \"bad code 0x%Xd\", *state, ch & 0xf);\n\n            return NGX_ERROR;\n        }\n    }\n\n    if (last) {\n        if (!ending) {\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,\n                           \"http2 huffman decoding error: \"\n                           \"incomplete code 0x%Xd\", ch);\n\n            return NGX_ERROR;\n        }\n\n        *state = 0;\n    }\n\n    return NGX_OK;\n}\n\n\n\nstatic ngx_inline ngx_int_t\nngx_http_huff_decode_bits(u_char *state, u_char *ending, ngx_uint_t bits,\n    u_char **dst)\n{\n    ngx_http_huff_decode_code_t  code;\n\n    code = ngx_http_huff_decode_codes[*state][bits];\n\n    if (code.next == *state) {\n        return NGX_ERROR;\n    }\n\n    if (code.emit) {\n        *(*dst)++ = code.sym;\n    }\n\n    *ending = code.ending;\n    *state = code.next;\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "src/http/ngx_http_huff_encode.c",
    "content": "\n/*\n * Copyright (C) Nginx, Inc.\n * Copyright (C) Valentin V. Bartenev\n * Copyright (C) 2015 Vlad Krasnov\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\ntypedef struct {\n    uint32_t  code;\n    uint32_t  len;\n} ngx_http_huff_encode_code_t;\n\n\nstatic ngx_http_huff_encode_code_t  ngx_http_huff_encode_table[256] =\n{\n    {0x00001ff8, 13}, {0x007fffd8, 23}, {0x0fffffe2, 28}, {0x0fffffe3, 28},\n    {0x0fffffe4, 28}, {0x0fffffe5, 28}, {0x0fffffe6, 28}, {0x0fffffe7, 28},\n    {0x0fffffe8, 28}, {0x00ffffea, 24}, {0x3ffffffc, 30}, {0x0fffffe9, 28},\n    {0x0fffffea, 28}, {0x3ffffffd, 30}, {0x0fffffeb, 28}, {0x0fffffec, 28},\n    {0x0fffffed, 28}, {0x0fffffee, 28}, {0x0fffffef, 28}, {0x0ffffff0, 28},\n    {0x0ffffff1, 28}, {0x0ffffff2, 28}, {0x3ffffffe, 30}, {0x0ffffff3, 28},\n    {0x0ffffff4, 28}, {0x0ffffff5, 28}, {0x0ffffff6, 28}, {0x0ffffff7, 28},\n    {0x0ffffff8, 28}, {0x0ffffff9, 28}, {0x0ffffffa, 28}, {0x0ffffffb, 28},\n    {0x00000014,  6}, {0x000003f8, 10}, {0x000003f9, 10}, {0x00000ffa, 12},\n    {0x00001ff9, 13}, {0x00000015,  6}, {0x000000f8,  8}, {0x000007fa, 11},\n    {0x000003fa, 10}, {0x000003fb, 10}, {0x000000f9,  8}, {0x000007fb, 11},\n    {0x000000fa,  8}, {0x00000016,  6}, {0x00000017,  6}, {0x00000018,  6},\n    {0x00000000,  5}, {0x00000001,  5}, {0x00000002,  5}, {0x00000019,  6},\n    {0x0000001a,  6}, {0x0000001b,  6}, {0x0000001c,  6}, {0x0000001d,  6},\n    {0x0000001e,  6}, {0x0000001f,  6}, {0x0000005c,  7}, {0x000000fb,  8},\n    {0x00007ffc, 15}, {0x00000020,  6}, {0x00000ffb, 12}, {0x000003fc, 10},\n    {0x00001ffa, 13}, {0x00000021,  6}, {0x0000005d,  7}, {0x0000005e,  7},\n    {0x0000005f,  7}, {0x00000060,  7}, {0x00000061,  7}, {0x00000062,  7},\n    {0x00000063,  7}, {0x00000064,  7}, {0x00000065,  7}, {0x00000066,  7},\n    {0x00000067,  7}, {0x00000068,  7}, {0x00000069,  7}, {0x0000006a,  7},\n    {0x0000006b,  7}, {0x0000006c,  7}, {0x0000006d,  7}, {0x0000006e,  7},\n    {0x0000006f,  7}, {0x00000070,  7}, {0x00000071,  7}, {0x00000072,  7},\n    {0x000000fc,  8}, {0x00000073,  7}, {0x000000fd,  8}, {0x00001ffb, 13},\n    {0x0007fff0, 19}, {0x00001ffc, 13}, {0x00003ffc, 14}, {0x00000022,  6},\n    {0x00007ffd, 15}, {0x00000003,  5}, {0x00000023,  6}, {0x00000004,  5},\n    {0x00000024,  6}, {0x00000005,  5}, {0x00000025,  6}, {0x00000026,  6},\n    {0x00000027,  6}, {0x00000006,  5}, {0x00000074,  7}, {0x00000075,  7},\n    {0x00000028,  6}, {0x00000029,  6}, {0x0000002a,  6}, {0x00000007,  5},\n    {0x0000002b,  6}, {0x00000076,  7}, {0x0000002c,  6}, {0x00000008,  5},\n    {0x00000009,  5}, {0x0000002d,  6}, {0x00000077,  7}, {0x00000078,  7},\n    {0x00000079,  7}, {0x0000007a,  7}, {0x0000007b,  7}, {0x00007ffe, 15},\n    {0x000007fc, 11}, {0x00003ffd, 14}, {0x00001ffd, 13}, {0x0ffffffc, 28},\n    {0x000fffe6, 20}, {0x003fffd2, 22}, {0x000fffe7, 20}, {0x000fffe8, 20},\n    {0x003fffd3, 22}, {0x003fffd4, 22}, {0x003fffd5, 22}, {0x007fffd9, 23},\n    {0x003fffd6, 22}, {0x007fffda, 23}, {0x007fffdb, 23}, {0x007fffdc, 23},\n    {0x007fffdd, 23}, {0x007fffde, 23}, {0x00ffffeb, 24}, {0x007fffdf, 23},\n    {0x00ffffec, 24}, {0x00ffffed, 24}, {0x003fffd7, 22}, {0x007fffe0, 23},\n    {0x00ffffee, 24}, {0x007fffe1, 23}, {0x007fffe2, 23}, {0x007fffe3, 23},\n    {0x007fffe4, 23}, {0x001fffdc, 21}, {0x003fffd8, 22}, {0x007fffe5, 23},\n    {0x003fffd9, 22}, {0x007fffe6, 23}, {0x007fffe7, 23}, {0x00ffffef, 24},\n    {0x003fffda, 22}, {0x001fffdd, 21}, {0x000fffe9, 20}, {0x003fffdb, 22},\n    {0x003fffdc, 22}, {0x007fffe8, 23}, {0x007fffe9, 23}, {0x001fffde, 21},\n    {0x007fffea, 23}, {0x003fffdd, 22}, {0x003fffde, 22}, {0x00fffff0, 24},\n    {0x001fffdf, 21}, {0x003fffdf, 22}, {0x007fffeb, 23}, {0x007fffec, 23},\n    {0x001fffe0, 21}, {0x001fffe1, 21}, {0x003fffe0, 22}, {0x001fffe2, 21},\n    {0x007fffed, 23}, {0x003fffe1, 22}, {0x007fffee, 23}, {0x007fffef, 23},\n    {0x000fffea, 20}, {0x003fffe2, 22}, {0x003fffe3, 22}, {0x003fffe4, 22},\n    {0x007ffff0, 23}, {0x003fffe5, 22}, {0x003fffe6, 22}, {0x007ffff1, 23},\n    {0x03ffffe0, 26}, {0x03ffffe1, 26}, {0x000fffeb, 20}, {0x0007fff1, 19},\n    {0x003fffe7, 22}, {0x007ffff2, 23}, {0x003fffe8, 22}, {0x01ffffec, 25},\n    {0x03ffffe2, 26}, {0x03ffffe3, 26}, {0x03ffffe4, 26}, {0x07ffffde, 27},\n    {0x07ffffdf, 27}, {0x03ffffe5, 26}, {0x00fffff1, 24}, {0x01ffffed, 25},\n    {0x0007fff2, 19}, {0x001fffe3, 21}, {0x03ffffe6, 26}, {0x07ffffe0, 27},\n    {0x07ffffe1, 27}, {0x03ffffe7, 26}, {0x07ffffe2, 27}, {0x00fffff2, 24},\n    {0x001fffe4, 21}, {0x001fffe5, 21}, {0x03ffffe8, 26}, {0x03ffffe9, 26},\n    {0x0ffffffd, 28}, {0x07ffffe3, 27}, {0x07ffffe4, 27}, {0x07ffffe5, 27},\n    {0x000fffec, 20}, {0x00fffff3, 24}, {0x000fffed, 20}, {0x001fffe6, 21},\n    {0x003fffe9, 22}, {0x001fffe7, 21}, {0x001fffe8, 21}, {0x007ffff3, 23},\n    {0x003fffea, 22}, {0x003fffeb, 22}, {0x01ffffee, 25}, {0x01ffffef, 25},\n    {0x00fffff4, 24}, {0x00fffff5, 24}, {0x03ffffea, 26}, {0x007ffff4, 23},\n    {0x03ffffeb, 26}, {0x07ffffe6, 27}, {0x03ffffec, 26}, {0x03ffffed, 26},\n    {0x07ffffe7, 27}, {0x07ffffe8, 27}, {0x07ffffe9, 27}, {0x07ffffea, 27},\n    {0x07ffffeb, 27}, {0x0ffffffe, 28}, {0x07ffffec, 27}, {0x07ffffed, 27},\n    {0x07ffffee, 27}, {0x07ffffef, 27}, {0x07fffff0, 27}, {0x03ffffee, 26}\n};\n\n\n/* same as above, but embeds lowercase transformation */\nstatic ngx_http_huff_encode_code_t  ngx_http_huff_encode_table_lc[256] =\n{\n    {0x00001ff8, 13}, {0x007fffd8, 23}, {0x0fffffe2, 28}, {0x0fffffe3, 28},\n    {0x0fffffe4, 28}, {0x0fffffe5, 28}, {0x0fffffe6, 28}, {0x0fffffe7, 28},\n    {0x0fffffe8, 28}, {0x00ffffea, 24}, {0x3ffffffc, 30}, {0x0fffffe9, 28},\n    {0x0fffffea, 28}, {0x3ffffffd, 30}, {0x0fffffeb, 28}, {0x0fffffec, 28},\n    {0x0fffffed, 28}, {0x0fffffee, 28}, {0x0fffffef, 28}, {0x0ffffff0, 28},\n    {0x0ffffff1, 28}, {0x0ffffff2, 28}, {0x3ffffffe, 30}, {0x0ffffff3, 28},\n    {0x0ffffff4, 28}, {0x0ffffff5, 28}, {0x0ffffff6, 28}, {0x0ffffff7, 28},\n    {0x0ffffff8, 28}, {0x0ffffff9, 28}, {0x0ffffffa, 28}, {0x0ffffffb, 28},\n    {0x00000014,  6}, {0x000003f8, 10}, {0x000003f9, 10}, {0x00000ffa, 12},\n    {0x00001ff9, 13}, {0x00000015,  6}, {0x000000f8,  8}, {0x000007fa, 11},\n    {0x000003fa, 10}, {0x000003fb, 10}, {0x000000f9,  8}, {0x000007fb, 11},\n    {0x000000fa,  8}, {0x00000016,  6}, {0x00000017,  6}, {0x00000018,  6},\n    {0x00000000,  5}, {0x00000001,  5}, {0x00000002,  5}, {0x00000019,  6},\n    {0x0000001a,  6}, {0x0000001b,  6}, {0x0000001c,  6}, {0x0000001d,  6},\n    {0x0000001e,  6}, {0x0000001f,  6}, {0x0000005c,  7}, {0x000000fb,  8},\n    {0x00007ffc, 15}, {0x00000020,  6}, {0x00000ffb, 12}, {0x000003fc, 10},\n    {0x00001ffa, 13}, {0x00000003,  5}, {0x00000023,  6}, {0x00000004,  5},\n    {0x00000024,  6}, {0x00000005,  5}, {0x00000025,  6}, {0x00000026,  6},\n    {0x00000027,  6}, {0x00000006,  5}, {0x00000074,  7}, {0x00000075,  7},\n    {0x00000028,  6}, {0x00000029,  6}, {0x0000002a,  6}, {0x00000007,  5},\n    {0x0000002b,  6}, {0x00000076,  7}, {0x0000002c,  6}, {0x00000008,  5},\n    {0x00000009,  5}, {0x0000002d,  6}, {0x00000077,  7}, {0x00000078,  7},\n    {0x00000079,  7}, {0x0000007a,  7}, {0x0000007b,  7}, {0x00001ffb, 13},\n    {0x0007fff0, 19}, {0x00001ffc, 13}, {0x00003ffc, 14}, {0x00000022,  6},\n    {0x00007ffd, 15}, {0x00000003,  5}, {0x00000023,  6}, {0x00000004,  5},\n    {0x00000024,  6}, {0x00000005,  5}, {0x00000025,  6}, {0x00000026,  6},\n    {0x00000027,  6}, {0x00000006,  5}, {0x00000074,  7}, {0x00000075,  7},\n    {0x00000028,  6}, {0x00000029,  6}, {0x0000002a,  6}, {0x00000007,  5},\n    {0x0000002b,  6}, {0x00000076,  7}, {0x0000002c,  6}, {0x00000008,  5},\n    {0x00000009,  5}, {0x0000002d,  6}, {0x00000077,  7}, {0x00000078,  7},\n    {0x00000079,  7}, {0x0000007a,  7}, {0x0000007b,  7}, {0x00007ffe, 15},\n    {0x000007fc, 11}, {0x00003ffd, 14}, {0x00001ffd, 13}, {0x0ffffffc, 28},\n    {0x000fffe6, 20}, {0x003fffd2, 22}, {0x000fffe7, 20}, {0x000fffe8, 20},\n    {0x003fffd3, 22}, {0x003fffd4, 22}, {0x003fffd5, 22}, {0x007fffd9, 23},\n    {0x003fffd6, 22}, {0x007fffda, 23}, {0x007fffdb, 23}, {0x007fffdc, 23},\n    {0x007fffdd, 23}, {0x007fffde, 23}, {0x00ffffeb, 24}, {0x007fffdf, 23},\n    {0x00ffffec, 24}, {0x00ffffed, 24}, {0x003fffd7, 22}, {0x007fffe0, 23},\n    {0x00ffffee, 24}, {0x007fffe1, 23}, {0x007fffe2, 23}, {0x007fffe3, 23},\n    {0x007fffe4, 23}, {0x001fffdc, 21}, {0x003fffd8, 22}, {0x007fffe5, 23},\n    {0x003fffd9, 22}, {0x007fffe6, 23}, {0x007fffe7, 23}, {0x00ffffef, 24},\n    {0x003fffda, 22}, {0x001fffdd, 21}, {0x000fffe9, 20}, {0x003fffdb, 22},\n    {0x003fffdc, 22}, {0x007fffe8, 23}, {0x007fffe9, 23}, {0x001fffde, 21},\n    {0x007fffea, 23}, {0x003fffdd, 22}, {0x003fffde, 22}, {0x00fffff0, 24},\n    {0x001fffdf, 21}, {0x003fffdf, 22}, {0x007fffeb, 23}, {0x007fffec, 23},\n    {0x001fffe0, 21}, {0x001fffe1, 21}, {0x003fffe0, 22}, {0x001fffe2, 21},\n    {0x007fffed, 23}, {0x003fffe1, 22}, {0x007fffee, 23}, {0x007fffef, 23},\n    {0x000fffea, 20}, {0x003fffe2, 22}, {0x003fffe3, 22}, {0x003fffe4, 22},\n    {0x007ffff0, 23}, {0x003fffe5, 22}, {0x003fffe6, 22}, {0x007ffff1, 23},\n    {0x03ffffe0, 26}, {0x03ffffe1, 26}, {0x000fffeb, 20}, {0x0007fff1, 19},\n    {0x003fffe7, 22}, {0x007ffff2, 23}, {0x003fffe8, 22}, {0x01ffffec, 25},\n    {0x03ffffe2, 26}, {0x03ffffe3, 26}, {0x03ffffe4, 26}, {0x07ffffde, 27},\n    {0x07ffffdf, 27}, {0x03ffffe5, 26}, {0x00fffff1, 24}, {0x01ffffed, 25},\n    {0x0007fff2, 19}, {0x001fffe3, 21}, {0x03ffffe6, 26}, {0x07ffffe0, 27},\n    {0x07ffffe1, 27}, {0x03ffffe7, 26}, {0x07ffffe2, 27}, {0x00fffff2, 24},\n    {0x001fffe4, 21}, {0x001fffe5, 21}, {0x03ffffe8, 26}, {0x03ffffe9, 26},\n    {0x0ffffffd, 28}, {0x07ffffe3, 27}, {0x07ffffe4, 27}, {0x07ffffe5, 27},\n    {0x000fffec, 20}, {0x00fffff3, 24}, {0x000fffed, 20}, {0x001fffe6, 21},\n    {0x003fffe9, 22}, {0x001fffe7, 21}, {0x001fffe8, 21}, {0x007ffff3, 23},\n    {0x003fffea, 22}, {0x003fffeb, 22}, {0x01ffffee, 25}, {0x01ffffef, 25},\n    {0x00fffff4, 24}, {0x00fffff5, 24}, {0x03ffffea, 26}, {0x007ffff4, 23},\n    {0x03ffffeb, 26}, {0x07ffffe6, 27}, {0x03ffffec, 26}, {0x03ffffed, 26},\n    {0x07ffffe7, 27}, {0x07ffffe8, 27}, {0x07ffffe9, 27}, {0x07ffffea, 27},\n    {0x07ffffeb, 27}, {0x0ffffffe, 28}, {0x07ffffec, 27}, {0x07ffffed, 27},\n    {0x07ffffee, 27}, {0x07ffffef, 27}, {0x07fffff0, 27}, {0x03ffffee, 26}\n};\n\n\n#if (NGX_PTR_SIZE == 8)\n\n#if (NGX_HAVE_LITTLE_ENDIAN)\n\n#if (NGX_HAVE_GCC_BSWAP64)\n#define ngx_http_huff_encode_buf(dst, buf)                                    \\\n    (*(uint64_t *) (dst) = __builtin_bswap64(buf))\n#else\n#define ngx_http_huff_encode_buf(dst, buf)                                    \\\n    ((dst)[0] = (u_char) ((buf) >> 56),                                       \\\n     (dst)[1] = (u_char) ((buf) >> 48),                                       \\\n     (dst)[2] = (u_char) ((buf) >> 40),                                       \\\n     (dst)[3] = (u_char) ((buf) >> 32),                                       \\\n     (dst)[4] = (u_char) ((buf) >> 24),                                       \\\n     (dst)[5] = (u_char) ((buf) >> 16),                                       \\\n     (dst)[6] = (u_char) ((buf) >> 8),                                        \\\n     (dst)[7] = (u_char)  (buf))\n#endif\n\n#else /* !NGX_HAVE_LITTLE_ENDIAN */\n#define ngx_http_huff_encode_buf(dst, buf)                                    \\\n    (*(uint64_t *) (dst) = (buf))\n#endif\n\n#else /* NGX_PTR_SIZE == 4 */\n\n#define ngx_http_huff_encode_buf(dst, buf)                                    \\\n    (*(uint32_t *) (dst) = htonl(buf))\n\n#endif\n\n\nsize_t\nngx_http_huff_encode(u_char *src, size_t len, u_char *dst, ngx_uint_t lower)\n{\n    u_char                       *end;\n    size_t                        hlen;\n    ngx_uint_t                    buf, pending, code;\n    ngx_http_huff_encode_code_t  *table, *next;\n\n    table = lower ? ngx_http_huff_encode_table_lc\n                  : ngx_http_huff_encode_table;\n    hlen = 0;\n    buf = 0;\n    pending = 0;\n\n    end = src + len;\n\n    while (src != end) {\n        next = &table[*src++];\n\n        code = next->code;\n        pending += next->len;\n\n        /* accumulate bits */\n        if (pending < sizeof(buf) * 8) {\n            buf |= code << (sizeof(buf) * 8 - pending);\n            continue;\n        }\n\n        if (hlen + sizeof(buf) >= len) {\n            return 0;\n        }\n\n        pending -= sizeof(buf) * 8;\n\n        buf |= code >> pending;\n\n        ngx_http_huff_encode_buf(&dst[hlen], buf);\n\n        hlen += sizeof(buf);\n\n        buf = pending ? code << (sizeof(buf) * 8 - pending) : 0;\n    }\n\n    if (pending == 0) {\n        return hlen;\n    }\n\n    buf |= (ngx_uint_t) -1 >> pending;\n\n    pending = ngx_align(pending, 8);\n\n    if (hlen + pending / 8 >= len) {\n        return 0;\n    }\n\n    buf >>= sizeof(buf) * 8 - pending;\n\n    do {\n        pending -= 8;\n        dst[hlen++] = (u_char) (buf >> pending);\n    } while (pending);\n\n    return hlen;\n}\n"
  },
  {
    "path": "src/http/ngx_http_parse.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\nstatic uint32_t  usual[] = {\n    0x00000000, /* 0000 0000 0000 0000  0000 0000 0000 0000 */\n\n                /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #\"!  */\n    0x7fff37d6, /* 0111 1111 1111 1111  0011 0111 1101 0110 */\n\n                /* _^]\\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */\n#if (NGX_WIN32)\n    0xefffffff, /* 1110 1111 1111 1111  1111 1111 1111 1111 */\n#else\n    0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */\n#endif\n\n                /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */\n    0x7fffffff, /* 0111 1111 1111 1111  1111 1111 1111 1111 */\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\n#if (NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED)\n\n#define ngx_str3_cmp(m, c0, c1, c2, c3)                                       \\\n    *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)\n\n#define ngx_str3Ocmp(m, c0, c1, c2, c3)                                       \\\n    *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)\n\n#define ngx_str4cmp(m, c0, c1, c2, c3)                                        \\\n    *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)\n\n#define ngx_str5cmp(m, c0, c1, c2, c3, c4)                                    \\\n    *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)             \\\n        && m[4] == c4\n\n#define ngx_str6cmp(m, c0, c1, c2, c3, c4, c5)                                \\\n    *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)             \\\n        && (((uint32_t *) m)[1] & 0xffff) == ((c5 << 8) | c4)\n\n#define ngx_str7_cmp(m, c0, c1, c2, c3, c4, c5, c6, c7)                       \\\n    *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)             \\\n        && ((uint32_t *) m)[1] == ((c7 << 24) | (c6 << 16) | (c5 << 8) | c4)\n\n#define ngx_str8cmp(m, c0, c1, c2, c3, c4, c5, c6, c7)                        \\\n    *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)             \\\n        && ((uint32_t *) m)[1] == ((c7 << 24) | (c6 << 16) | (c5 << 8) | c4)\n\n#define ngx_str9cmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8)                    \\\n    *(uint32_t *) m == ((c3 << 24) | (c2 << 16) | (c1 << 8) | c0)             \\\n        && ((uint32_t *) m)[1] == ((c7 << 24) | (c6 << 16) | (c5 << 8) | c4)  \\\n        && m[8] == c8\n\n#else /* !(NGX_HAVE_LITTLE_ENDIAN && NGX_HAVE_NONALIGNED) */\n\n#define ngx_str3_cmp(m, c0, c1, c2, c3)                                       \\\n    m[0] == c0 && m[1] == c1 && m[2] == c2\n\n#define ngx_str3Ocmp(m, c0, c1, c2, c3)                                       \\\n    m[0] == c0 && m[2] == c2 && m[3] == c3\n\n#define ngx_str4cmp(m, c0, c1, c2, c3)                                        \\\n    m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3\n\n#define ngx_str5cmp(m, c0, c1, c2, c3, c4)                                    \\\n    m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3 && m[4] == c4\n\n#define ngx_str6cmp(m, c0, c1, c2, c3, c4, c5)                                \\\n    m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3                      \\\n        && m[4] == c4 && m[5] == c5\n\n#define ngx_str7_cmp(m, c0, c1, c2, c3, c4, c5, c6, c7)                       \\\n    m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3                      \\\n        && m[4] == c4 && m[5] == c5 && m[6] == c6\n\n#define ngx_str8cmp(m, c0, c1, c2, c3, c4, c5, c6, c7)                        \\\n    m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3                      \\\n        && m[4] == c4 && m[5] == c5 && m[6] == c6 && m[7] == c7\n\n#define ngx_str9cmp(m, c0, c1, c2, c3, c4, c5, c6, c7, c8)                    \\\n    m[0] == c0 && m[1] == c1 && m[2] == c2 && m[3] == c3                      \\\n        && m[4] == c4 && m[5] == c5 && m[6] == c6 && m[7] == c7 && m[8] == c8\n\n#endif\n\n\n/* gcc, icc, msvc and others compile these switches as an jump table */\n\nngx_int_t\nngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b)\n{\n    u_char  c, ch, *p, *m;\n    enum {\n        sw_start = 0,\n        sw_method,\n#if (NGX_HTTP_PROXY_CONNECT)\n        sw_spaces_before_connect_host,\n        sw_connect_host_start,\n        sw_connect_host,\n        sw_connect_host_end,\n        sw_connect_host_ip_literal,\n        sw_connect_port,\n#endif\n        sw_spaces_before_uri,\n        sw_schema,\n        sw_schema_slash,\n        sw_schema_slash_slash,\n        sw_host_start,\n        sw_host,\n        sw_host_end,\n        sw_host_ip_literal,\n        sw_port,\n        sw_after_slash_in_uri,\n        sw_check_uri,\n        sw_uri,\n        sw_http_09,\n        sw_http_H,\n        sw_http_HT,\n        sw_http_HTT,\n        sw_http_HTTP,\n        sw_first_major_digit,\n        sw_major_digit,\n        sw_first_minor_digit,\n        sw_minor_digit,\n        sw_spaces_after_digit,\n        sw_almost_done\n    } state;\n\n    state = r->state;\n\n    for (p = b->pos; p < b->last; p++) {\n        ch = *p;\n\n        switch (state) {\n\n        /* HTTP methods: GET, HEAD, POST */\n        case sw_start:\n            r->request_start = p;\n\n            if (ch == CR || ch == LF) {\n                break;\n            }\n\n            if ((ch < 'A' || ch > 'Z') && ch != '_' && ch != '-') {\n                return NGX_HTTP_PARSE_INVALID_METHOD;\n            }\n\n            state = sw_method;\n            break;\n\n        case sw_method:\n            if (ch == ' ') {\n                r->method_end = p - 1;\n                m = r->request_start;\n\n                switch (p - m) {\n\n                case 3:\n                    if (ngx_str3_cmp(m, 'G', 'E', 'T', ' ')) {\n                        r->method = NGX_HTTP_GET;\n                        break;\n                    }\n\n                    if (ngx_str3_cmp(m, 'P', 'U', 'T', ' ')) {\n                        r->method = NGX_HTTP_PUT;\n                        break;\n                    }\n\n                    break;\n\n                case 4:\n                    if (m[1] == 'O') {\n\n                        if (ngx_str3Ocmp(m, 'P', 'O', 'S', 'T')) {\n                            r->method = NGX_HTTP_POST;\n                            break;\n                        }\n\n                        if (ngx_str3Ocmp(m, 'C', 'O', 'P', 'Y')) {\n                            r->method = NGX_HTTP_COPY;\n                            break;\n                        }\n\n                        if (ngx_str3Ocmp(m, 'M', 'O', 'V', 'E')) {\n                            r->method = NGX_HTTP_MOVE;\n                            break;\n                        }\n\n                        if (ngx_str3Ocmp(m, 'L', 'O', 'C', 'K')) {\n                            r->method = NGX_HTTP_LOCK;\n                            break;\n                        }\n\n                    } else {\n\n                        if (ngx_str4cmp(m, 'H', 'E', 'A', 'D')) {\n                            r->method = NGX_HTTP_HEAD;\n                            break;\n                        }\n                    }\n\n                    break;\n\n                case 5:\n                    if (ngx_str5cmp(m, 'M', 'K', 'C', 'O', 'L')) {\n                        r->method = NGX_HTTP_MKCOL;\n                        break;\n                    }\n\n                    if (ngx_str5cmp(m, 'P', 'A', 'T', 'C', 'H')) {\n                        r->method = NGX_HTTP_PATCH;\n                        break;\n                    }\n\n                    if (ngx_str5cmp(m, 'T', 'R', 'A', 'C', 'E')) {\n                        r->method = NGX_HTTP_TRACE;\n                        break;\n                    }\n\n                    break;\n\n                case 6:\n                    if (ngx_str6cmp(m, 'D', 'E', 'L', 'E', 'T', 'E')) {\n                        r->method = NGX_HTTP_DELETE;\n                        break;\n                    }\n\n                    if (ngx_str6cmp(m, 'U', 'N', 'L', 'O', 'C', 'K')) {\n                        r->method = NGX_HTTP_UNLOCK;\n                        break;\n                    }\n\n                    break;\n\n                case 7:\n                    if (ngx_str7_cmp(m, 'O', 'P', 'T', 'I', 'O', 'N', 'S', ' '))\n                    {\n                        r->method = NGX_HTTP_OPTIONS;\n                    }\n\n                    if (ngx_str7_cmp(m, 'C', 'O', 'N', 'N', 'E', 'C', 'T', ' '))\n                    {\n                        r->method = NGX_HTTP_CONNECT;\n                    }\n\n                    break;\n\n                case 8:\n                    if (ngx_str8cmp(m, 'P', 'R', 'O', 'P', 'F', 'I', 'N', 'D'))\n                    {\n                        r->method = NGX_HTTP_PROPFIND;\n                    }\n\n                    break;\n\n                case 9:\n                    if (ngx_str9cmp(m,\n                            'P', 'R', 'O', 'P', 'P', 'A', 'T', 'C', 'H'))\n                    {\n                        r->method = NGX_HTTP_PROPPATCH;\n                    }\n\n                    break;\n                }\n\n                state = sw_spaces_before_uri;\n\n#if (NGX_HTTP_PROXY_CONNECT)\n                if (r->method == NGX_HTTP_CONNECT) {\n                    state = sw_spaces_before_connect_host;\n                }\n#endif\n\n                break;\n            }\n\n            if ((ch < 'A' || ch > 'Z') && ch != '_' && ch != '-') {\n                return NGX_HTTP_PARSE_INVALID_METHOD;\n            }\n\n            break;\n\n#if (NGX_HTTP_PROXY_CONNECT)\n        case sw_spaces_before_connect_host:\n\n            if (ch == ' ') {\n                break;\n            }\n\n            /* fall through */\n\n        case sw_connect_host_start:\n\n            r->connect_host_start = p;\n\n            if (ch == '[') {\n                state = sw_connect_host_ip_literal;\n                break;\n            }\n\n            state = sw_connect_host;\n\n            /* fall through */\n\n        case sw_connect_host:\n\n            c = (u_char) (ch | 0x20);\n            if (c >= 'a' && c <= 'z') {\n                break;\n            }\n\n            if ((ch >= '0' && ch <= '9') || ch == '.' || ch == '-') {\n                break;\n            }\n\n            /* fall through */\n\n        case sw_connect_host_end:\n\n            r->connect_host_end = p;\n\n            switch (ch) {\n            case ':':\n                state = sw_connect_port;\n                break;\n            default:\n                return NGX_HTTP_PARSE_INVALID_REQUEST;\n            }\n            break;\n\n        case sw_connect_host_ip_literal:\n\n            if (ch >= '0' && ch <= '9') {\n                break;\n            }\n\n            c = (u_char) (ch | 0x20);\n            if (c >= 'a' && c <= 'z') {\n                break;\n            }\n\n            switch (ch) {\n            case ':':\n                break;\n            case ']':\n                state = sw_connect_host_end;\n                break;\n            case '-':\n            case '.':\n            case '_':\n            case '~':\n                /* unreserved */\n                break;\n            case '!':\n            case '$':\n            case '&':\n            case '\\'':\n            case '(':\n            case ')':\n            case '*':\n            case '+':\n            case ',':\n            case ';':\n            case '=':\n                /* sub-delims */\n                break;\n            default:\n                return NGX_HTTP_PARSE_INVALID_REQUEST;\n            }\n            break;\n\n        case sw_connect_port:\n            if (ch >= '0' && ch <= '9') {\n                break;\n            }\n\n            switch (ch) {\n            case ' ':\n                r->connect_port_end = p;\n                state = sw_http_09;\n                break;\n            default:\n                return NGX_HTTP_PARSE_INVALID_REQUEST;\n            }\n            break;\n#endif\n\n        /* space* before URI */\n        case sw_spaces_before_uri:\n\n            if (ch == '/') {\n                r->uri_start = p;\n                state = sw_after_slash_in_uri;\n                break;\n            }\n\n            c = (u_char) (ch | 0x20);\n            if (c >= 'a' && c <= 'z') {\n                r->schema_start = p;\n                state = sw_schema;\n                break;\n            }\n\n            switch (ch) {\n            case ' ':\n                break;\n            default:\n                return NGX_HTTP_PARSE_INVALID_REQUEST;\n            }\n            break;\n\n        case sw_schema:\n\n            c = (u_char) (ch | 0x20);\n            if (c >= 'a' && c <= 'z') {\n                break;\n            }\n\n            if ((ch >= '0' && ch <= '9') || ch == '+' || ch == '-' || ch == '.')\n            {\n                break;\n            }\n\n            switch (ch) {\n            case ':':\n                r->schema_end = p;\n                state = sw_schema_slash;\n                break;\n            default:\n                return NGX_HTTP_PARSE_INVALID_REQUEST;\n            }\n            break;\n\n        case sw_schema_slash:\n            switch (ch) {\n            case '/':\n                state = sw_schema_slash_slash;\n                break;\n            default:\n                return NGX_HTTP_PARSE_INVALID_REQUEST;\n            }\n            break;\n\n        case sw_schema_slash_slash:\n            switch (ch) {\n            case '/':\n                state = sw_host_start;\n                break;\n            default:\n                return NGX_HTTP_PARSE_INVALID_REQUEST;\n            }\n            break;\n\n        case sw_host_start:\n\n            r->host_start = p;\n\n            if (ch == '[') {\n                state = sw_host_ip_literal;\n                break;\n            }\n\n            state = sw_host;\n\n            /* fall through */\n\n        case sw_host:\n\n            c = (u_char) (ch | 0x20);\n            if (c >= 'a' && c <= 'z') {\n                break;\n            }\n\n            if ((ch >= '0' && ch <= '9') || ch == '.' || ch == '-') {\n                break;\n            }\n\n            /* fall through */\n\n        case sw_host_end:\n\n            r->host_end = p;\n\n            switch (ch) {\n            case ':':\n                state = sw_port;\n                break;\n            case '/':\n                r->uri_start = p;\n                state = sw_after_slash_in_uri;\n                break;\n            case '?':\n                r->uri_start = p;\n                r->args_start = p + 1;\n                r->empty_path_in_uri = 1;\n                state = sw_uri;\n                break;\n            case ' ':\n                /*\n                 * use single \"/\" from request line to preserve pointers,\n                 * if request line will be copied to large client buffer\n                 */\n                r->uri_start = r->schema_end + 1;\n                r->uri_end = r->schema_end + 2;\n                state = sw_http_09;\n                break;\n            default:\n                return NGX_HTTP_PARSE_INVALID_REQUEST;\n            }\n            break;\n\n        case sw_host_ip_literal:\n\n            if (ch >= '0' && ch <= '9') {\n                break;\n            }\n\n            c = (u_char) (ch | 0x20);\n            if (c >= 'a' && c <= 'z') {\n                break;\n            }\n\n            switch (ch) {\n            case ':':\n                break;\n            case ']':\n                state = sw_host_end;\n                break;\n            case '-':\n            case '.':\n            case '_':\n            case '~':\n                /* unreserved */\n                break;\n            case '!':\n            case '$':\n            case '&':\n            case '\\'':\n            case '(':\n            case ')':\n            case '*':\n            case '+':\n            case ',':\n            case ';':\n            case '=':\n                /* sub-delims */\n                break;\n            default:\n                return NGX_HTTP_PARSE_INVALID_REQUEST;\n            }\n            break;\n\n        case sw_port:\n            if (ch >= '0' && ch <= '9') {\n                break;\n            }\n\n            switch (ch) {\n            case '/':\n                r->port_end = p;\n                r->uri_start = p;\n                state = sw_after_slash_in_uri;\n                break;\n            case '?':\n                r->port_end = p;\n                r->uri_start = p;\n                r->args_start = p + 1;\n                r->empty_path_in_uri = 1;\n                state = sw_uri;\n                break;\n            case ' ':\n                r->port_end = p;\n                /*\n                 * use single \"/\" from request line to preserve pointers,\n                 * if request line will be copied to large client buffer\n                 */\n                r->uri_start = r->schema_end + 1;\n                r->uri_end = r->schema_end + 2;\n                state = sw_http_09;\n                break;\n            default:\n                return NGX_HTTP_PARSE_INVALID_REQUEST;\n            }\n            break;\n\n        /* check \"/.\", \"//\", \"%\", and \"\\\" (Win32) in URI */\n        case sw_after_slash_in_uri:\n\n            if (usual[ch >> 5] & (1U << (ch & 0x1f))) {\n                state = sw_check_uri;\n                break;\n            }\n\n            switch (ch) {\n            case ' ':\n                r->uri_end = p;\n                state = sw_http_09;\n                break;\n            case CR:\n                r->uri_end = p;\n                r->http_minor = 9;\n                state = sw_almost_done;\n                break;\n            case LF:\n                r->uri_end = p;\n                r->http_minor = 9;\n                goto done;\n            case '.':\n                r->complex_uri = 1;\n                state = sw_uri;\n                break;\n            case '%':\n                r->quoted_uri = 1;\n                state = sw_uri;\n                break;\n            case '/':\n                r->complex_uri = 1;\n                state = sw_uri;\n                break;\n#if (NGX_WIN32)\n            case '\\\\':\n                r->complex_uri = 1;\n                state = sw_uri;\n                break;\n#endif\n            case '?':\n                r->args_start = p + 1;\n                state = sw_uri;\n                break;\n            case '#':\n                r->complex_uri = 1;\n                state = sw_uri;\n                break;\n            case '+':\n                r->plus_in_uri = 1;\n                break;\n            default:\n                if (ch < 0x20 || ch == 0x7f) {\n                    return NGX_HTTP_PARSE_INVALID_REQUEST;\n                }\n                state = sw_check_uri;\n                break;\n            }\n            break;\n\n        /* check \"/\", \"%\" and \"\\\" (Win32) in URI */\n        case sw_check_uri:\n\n            if (usual[ch >> 5] & (1U << (ch & 0x1f))) {\n                break;\n            }\n\n            switch (ch) {\n            case '/':\n#if (NGX_WIN32)\n                if (r->uri_ext == p) {\n                    r->complex_uri = 1;\n                    state = sw_uri;\n                    break;\n                }\n#endif\n                r->uri_ext = NULL;\n                state = sw_after_slash_in_uri;\n                break;\n            case '.':\n                r->uri_ext = p + 1;\n                break;\n            case ' ':\n                r->uri_end = p;\n                state = sw_http_09;\n                break;\n            case CR:\n                r->uri_end = p;\n                r->http_minor = 9;\n                state = sw_almost_done;\n                break;\n            case LF:\n                r->uri_end = p;\n                r->http_minor = 9;\n                goto done;\n#if (NGX_WIN32)\n            case '\\\\':\n                r->complex_uri = 1;\n                state = sw_after_slash_in_uri;\n                break;\n#endif\n            case '%':\n                r->quoted_uri = 1;\n                state = sw_uri;\n                break;\n            case '?':\n                r->args_start = p + 1;\n                state = sw_uri;\n                break;\n            case '#':\n                r->complex_uri = 1;\n                state = sw_uri;\n                break;\n            case '+':\n                r->plus_in_uri = 1;\n                break;\n            default:\n                if (ch < 0x20 || ch == 0x7f) {\n                    return NGX_HTTP_PARSE_INVALID_REQUEST;\n                }\n                break;\n            }\n            break;\n\n        /* URI */\n        case sw_uri:\n\n            if (usual[ch >> 5] & (1U << (ch & 0x1f))) {\n                break;\n            }\n\n            switch (ch) {\n            case ' ':\n                r->uri_end = p;\n                state = sw_http_09;\n                break;\n            case CR:\n                r->uri_end = p;\n                r->http_minor = 9;\n                state = sw_almost_done;\n                break;\n            case LF:\n                r->uri_end = p;\n                r->http_minor = 9;\n                goto done;\n            case '#':\n                r->complex_uri = 1;\n                break;\n            default:\n                if (ch < 0x20 || ch == 0x7f) {\n                    return NGX_HTTP_PARSE_INVALID_REQUEST;\n                }\n                break;\n            }\n            break;\n\n        /* space+ after URI */\n        case sw_http_09:\n            switch (ch) {\n            case ' ':\n                break;\n            case CR:\n                r->http_minor = 9;\n                state = sw_almost_done;\n                break;\n            case LF:\n                r->http_minor = 9;\n                goto done;\n            case 'H':\n                r->http_protocol.data = p;\n                state = sw_http_H;\n                break;\n            default:\n                return NGX_HTTP_PARSE_INVALID_REQUEST;\n            }\n            break;\n\n        case sw_http_H:\n            switch (ch) {\n            case 'T':\n                state = sw_http_HT;\n                break;\n            default:\n                return NGX_HTTP_PARSE_INVALID_REQUEST;\n            }\n            break;\n\n        case sw_http_HT:\n            switch (ch) {\n            case 'T':\n                state = sw_http_HTT;\n                break;\n            default:\n                return NGX_HTTP_PARSE_INVALID_REQUEST;\n            }\n            break;\n\n        case sw_http_HTT:\n            switch (ch) {\n            case 'P':\n                state = sw_http_HTTP;\n                break;\n            default:\n                return NGX_HTTP_PARSE_INVALID_REQUEST;\n            }\n            break;\n\n        case sw_http_HTTP:\n            switch (ch) {\n            case '/':\n                state = sw_first_major_digit;\n                break;\n            default:\n                return NGX_HTTP_PARSE_INVALID_REQUEST;\n            }\n            break;\n\n        /* first digit of major HTTP version */\n        case sw_first_major_digit:\n            if (ch < '1' || ch > '9') {\n                return NGX_HTTP_PARSE_INVALID_REQUEST;\n            }\n\n            r->http_major = ch - '0';\n\n            if (r->http_major > 1) {\n                return NGX_HTTP_PARSE_INVALID_VERSION;\n            }\n\n            state = sw_major_digit;\n            break;\n\n        /* major HTTP version or dot */\n        case sw_major_digit:\n            if (ch == '.') {\n                state = sw_first_minor_digit;\n                break;\n            }\n\n            if (ch < '0' || ch > '9') {\n                return NGX_HTTP_PARSE_INVALID_REQUEST;\n            }\n\n            r->http_major = r->http_major * 10 + (ch - '0');\n\n            if (r->http_major > 1) {\n                return NGX_HTTP_PARSE_INVALID_VERSION;\n            }\n\n            break;\n\n        /* first digit of minor HTTP version */\n        case sw_first_minor_digit:\n            if (ch < '0' || ch > '9') {\n                return NGX_HTTP_PARSE_INVALID_REQUEST;\n            }\n\n            r->http_minor = ch - '0';\n            state = sw_minor_digit;\n            break;\n\n        /* minor HTTP version or end of request line */\n        case sw_minor_digit:\n            if (ch == CR) {\n                state = sw_almost_done;\n                break;\n            }\n\n            if (ch == LF) {\n                goto done;\n            }\n\n            if (ch == ' ') {\n                state = sw_spaces_after_digit;\n                break;\n            }\n\n            if (ch < '0' || ch > '9') {\n                return NGX_HTTP_PARSE_INVALID_REQUEST;\n            }\n\n            if (r->http_minor > 99) {\n                return NGX_HTTP_PARSE_INVALID_REQUEST;\n            }\n\n            r->http_minor = r->http_minor * 10 + (ch - '0');\n            break;\n\n        case sw_spaces_after_digit:\n            switch (ch) {\n            case ' ':\n                break;\n            case CR:\n                state = sw_almost_done;\n                break;\n            case LF:\n                goto done;\n            default:\n                return NGX_HTTP_PARSE_INVALID_REQUEST;\n            }\n            break;\n\n        /* end of request line */\n        case sw_almost_done:\n            r->request_end = p - 1;\n            switch (ch) {\n            case LF:\n                goto done;\n            default:\n                return NGX_HTTP_PARSE_INVALID_REQUEST;\n            }\n        }\n    }\n\n    b->pos = p;\n    r->state = state;\n\n    return NGX_AGAIN;\n\ndone:\n\n    b->pos = p + 1;\n\n    if (r->request_end == NULL) {\n        r->request_end = p;\n    }\n\n    r->http_version = r->http_major * 1000 + r->http_minor;\n    r->state = sw_start;\n\n    if (r->http_version == 9 && r->method != NGX_HTTP_GET) {\n        return NGX_HTTP_PARSE_INVALID_09_METHOD;\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_parse_header_line(ngx_http_request_t *r, ngx_buf_t *b,\n    ngx_uint_t allow_underscores)\n{\n    u_char      c, ch, *p;\n    ngx_uint_t  hash, i;\n    enum {\n        sw_start = 0,\n        sw_name,\n        sw_space_before_value,\n        sw_value,\n        sw_space_after_value,\n        sw_ignore_line,\n        sw_almost_done,\n        sw_header_almost_done\n    } state;\n\n    /* the last '\\0' is not needed because string is zero terminated */\n\n    static u_char  lowcase[] =\n        \"\\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        \"\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0\\0-\\0\\0\" \"0123456789\\0\\0\\0\\0\\0\\0\"\n        \"\\0abcdefghijklmnopqrstuvwxyz\\0\\0\\0\\0\\0\"\n        \"\\0abcdefghijklmnopqrstuvwxyz\\0\\0\\0\\0\\0\"\n        \"\\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        \"\\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        \"\\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        \"\\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\n    state = r->state;\n    hash = r->header_hash;\n    i = r->lowcase_index;\n\n    for (p = b->pos; p < b->last; p++) {\n        ch = *p;\n\n        switch (state) {\n\n        /* first char */\n        case sw_start:\n            r->header_name_start = p;\n            r->invalid_header = 0;\n\n            switch (ch) {\n            case CR:\n                r->header_end = p;\n                state = sw_header_almost_done;\n                break;\n            case LF:\n                r->header_end = p;\n                goto header_done;\n            default:\n                state = sw_name;\n\n                c = lowcase[ch];\n\n                if (c) {\n                    hash = ngx_hash(0, c);\n                    r->lowcase_header[0] = c;\n                    i = 1;\n                    break;\n                }\n\n                if (ch == '_') {\n                    if (allow_underscores) {\n                        hash = ngx_hash(0, ch);\n                        r->lowcase_header[0] = ch;\n                        i = 1;\n\n                    } else {\n                        hash = 0;\n                        i = 0;\n                        r->invalid_header = 1;\n                    }\n\n                    break;\n                }\n\n                if (ch <= 0x20 || ch == 0x7f || ch == ':') {\n                    r->header_end = p;\n                    return NGX_HTTP_PARSE_INVALID_HEADER;\n                }\n\n                hash = 0;\n                i = 0;\n                r->invalid_header = 1;\n\n                break;\n\n            }\n            break;\n\n        /* header name */\n        case sw_name:\n            c = lowcase[ch];\n\n            if (c) {\n                hash = ngx_hash(hash, c);\n                r->lowcase_header[i++] = c;\n                i &= (NGX_HTTP_LC_HEADER_LEN - 1);\n                break;\n            }\n\n            if (ch == '_') {\n                if (allow_underscores) {\n                    hash = ngx_hash(hash, ch);\n                    r->lowcase_header[i++] = ch;\n                    i &= (NGX_HTTP_LC_HEADER_LEN - 1);\n\n                } else {\n                    r->invalid_header = 1;\n                }\n\n                break;\n            }\n\n            if (ch == ':') {\n                r->header_name_end = p;\n                state = sw_space_before_value;\n                break;\n            }\n\n            if (ch == CR) {\n                r->header_name_end = p;\n                r->header_start = p;\n                r->header_end = p;\n                state = sw_almost_done;\n                break;\n            }\n\n            if (ch == LF) {\n                r->header_name_end = p;\n                r->header_start = p;\n                r->header_end = p;\n                goto done;\n            }\n\n            /* IIS may send the duplicate \"HTTP/1.1 ...\" lines */\n            if (ch == '/'\n                && r->upstream\n                && p - r->header_name_start == 4\n                && ngx_strncmp(r->header_name_start, \"HTTP\", 4) == 0)\n            {\n                state = sw_ignore_line;\n                break;\n            }\n\n            if (ch <= 0x20 || ch == 0x7f) {\n                r->header_end = p;\n                return NGX_HTTP_PARSE_INVALID_HEADER;\n            }\n\n            r->invalid_header = 1;\n\n            break;\n\n        /* space* before header value */\n        case sw_space_before_value:\n            switch (ch) {\n            case ' ':\n                break;\n            case CR:\n                r->header_start = p;\n                r->header_end = p;\n                state = sw_almost_done;\n                break;\n            case LF:\n                r->header_start = p;\n                r->header_end = p;\n                goto done;\n            case '\\0':\n                r->header_end = p;\n                return NGX_HTTP_PARSE_INVALID_HEADER;\n            default:\n                r->header_start = p;\n                state = sw_value;\n                break;\n            }\n            break;\n\n        /* header value */\n        case sw_value:\n            switch (ch) {\n            case ' ':\n                r->header_end = p;\n                state = sw_space_after_value;\n                break;\n            case CR:\n                r->header_end = p;\n                state = sw_almost_done;\n                break;\n            case LF:\n                r->header_end = p;\n                goto done;\n            case '\\0':\n                r->header_end = p;\n                return NGX_HTTP_PARSE_INVALID_HEADER;\n            }\n            break;\n\n        /* space* before end of header line */\n        case sw_space_after_value:\n            switch (ch) {\n            case ' ':\n                break;\n            case CR:\n                state = sw_almost_done;\n                break;\n            case LF:\n                goto done;\n            case '\\0':\n                r->header_end = p;\n                return NGX_HTTP_PARSE_INVALID_HEADER;\n            default:\n                state = sw_value;\n                break;\n            }\n            break;\n\n        /* ignore header line */\n        case sw_ignore_line:\n            switch (ch) {\n            case LF:\n                state = sw_start;\n                break;\n            default:\n                break;\n            }\n            break;\n\n        /* end of header line */\n        case sw_almost_done:\n            switch (ch) {\n            case LF:\n                goto done;\n            case CR:\n                break;\n            default:\n                return NGX_HTTP_PARSE_INVALID_HEADER;\n            }\n            break;\n\n        /* end of header */\n        case sw_header_almost_done:\n            switch (ch) {\n            case LF:\n                goto header_done;\n            default:\n                return NGX_HTTP_PARSE_INVALID_HEADER;\n            }\n        }\n    }\n\n    b->pos = p;\n    r->state = state;\n    r->header_hash = hash;\n    r->lowcase_index = i;\n\n    return NGX_AGAIN;\n\ndone:\n\n    b->pos = p + 1;\n    r->state = sw_start;\n    r->header_hash = hash;\n    r->lowcase_index = i;\n\n    return NGX_OK;\n\nheader_done:\n\n    b->pos = p + 1;\n    r->state = sw_start;\n\n    return NGX_HTTP_PARSE_HEADER_DONE;\n}\n\n\nngx_int_t\nngx_http_parse_uri(ngx_http_request_t *r)\n{\n    u_char  *p, ch;\n    enum {\n        sw_start = 0,\n        sw_after_slash_in_uri,\n        sw_check_uri,\n        sw_uri\n    } state;\n\n    state = sw_start;\n\n    for (p = r->uri_start; p != r->uri_end; p++) {\n\n        ch = *p;\n\n        switch (state) {\n\n        case sw_start:\n\n            if (ch != '/') {\n                return NGX_ERROR;\n            }\n\n            state = sw_after_slash_in_uri;\n            break;\n\n        /* check \"/.\", \"//\", \"%\", and \"\\\" (Win32) in URI */\n        case sw_after_slash_in_uri:\n\n            if (usual[ch >> 5] & (1U << (ch & 0x1f))) {\n                state = sw_check_uri;\n                break;\n            }\n\n            switch (ch) {\n            case '.':\n                r->complex_uri = 1;\n                state = sw_uri;\n                break;\n            case '%':\n                r->quoted_uri = 1;\n                state = sw_uri;\n                break;\n            case '/':\n                r->complex_uri = 1;\n                state = sw_uri;\n                break;\n#if (NGX_WIN32)\n            case '\\\\':\n                r->complex_uri = 1;\n                state = sw_uri;\n                break;\n#endif\n            case '?':\n                r->args_start = p + 1;\n                state = sw_uri;\n                break;\n            case '#':\n                r->complex_uri = 1;\n                state = sw_uri;\n                break;\n            case '+':\n                r->plus_in_uri = 1;\n                break;\n            default:\n                if (ch <= 0x20 || ch == 0x7f) {\n                    return NGX_ERROR;\n                }\n                state = sw_check_uri;\n                break;\n            }\n            break;\n\n        /* check \"/\", \"%\" and \"\\\" (Win32) in URI */\n        case sw_check_uri:\n\n            if (usual[ch >> 5] & (1U << (ch & 0x1f))) {\n                break;\n            }\n\n            switch (ch) {\n            case '/':\n#if (NGX_WIN32)\n                if (r->uri_ext == p) {\n                    r->complex_uri = 1;\n                    state = sw_uri;\n                    break;\n                }\n#endif\n                r->uri_ext = NULL;\n                state = sw_after_slash_in_uri;\n                break;\n            case '.':\n                r->uri_ext = p + 1;\n                break;\n#if (NGX_WIN32)\n            case '\\\\':\n                r->complex_uri = 1;\n                state = sw_after_slash_in_uri;\n                break;\n#endif\n            case '%':\n                r->quoted_uri = 1;\n                state = sw_uri;\n                break;\n            case '?':\n                r->args_start = p + 1;\n                state = sw_uri;\n                break;\n            case '#':\n                r->complex_uri = 1;\n                state = sw_uri;\n                break;\n            case '+':\n                r->plus_in_uri = 1;\n                break;\n            default:\n                if (ch <= 0x20 || ch == 0x7f) {\n                    return NGX_ERROR;\n                }\n                break;\n            }\n            break;\n\n        /* URI */\n        case sw_uri:\n\n            if (usual[ch >> 5] & (1U << (ch & 0x1f))) {\n                break;\n            }\n\n            switch (ch) {\n            case '#':\n                r->complex_uri = 1;\n                break;\n            default:\n                if (ch <= 0x20 || ch == 0x7f) {\n                    return NGX_ERROR;\n                }\n                break;\n            }\n            break;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_parse_complex_uri(ngx_http_request_t *r, ngx_uint_t merge_slashes)\n{\n    u_char  c, ch, decoded, *p, *u;\n    enum {\n        sw_usual = 0,\n        sw_slash,\n        sw_dot,\n        sw_dot_dot,\n        sw_quoted,\n        sw_quoted_second\n    } state, quoted_state;\n\n#if (NGX_SUPPRESS_WARN)\n    decoded = '\\0';\n    quoted_state = sw_usual;\n#endif\n\n    state = sw_usual;\n    p = r->uri_start;\n    u = r->uri.data;\n    r->uri_ext = NULL;\n    r->args_start = NULL;\n\n    if (r->empty_path_in_uri) {\n        *u++ = '/';\n    }\n\n    ch = *p++;\n\n    while (p <= r->uri_end) {\n\n        /*\n         * we use \"ch = *p++\" inside the cycle, but this operation is safe,\n         * because after the URI there is always at least one character:\n         * the line feed\n         */\n\n        ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"s:%d in:'%Xd:%c'\", state, ch, ch);\n\n        switch (state) {\n\n        case sw_usual:\n\n            if (usual[ch >> 5] & (1U << (ch & 0x1f))) {\n                *u++ = ch;\n                ch = *p++;\n                break;\n            }\n\n            switch (ch) {\n#if (NGX_WIN32)\n            case '\\\\':\n                if (u - 2 >= r->uri.data\n                    && *(u - 1) == '.' && *(u - 2) != '.')\n                {\n                    u--;\n                }\n\n                r->uri_ext = NULL;\n\n                if (p == r->uri_start + r->uri.len) {\n\n                    /*\n                     * we omit the last \"\\\" to cause redirect because\n                     * the browsers do not treat \"\\\" as \"/\" in relative URL path\n                     */\n\n                    break;\n                }\n\n                state = sw_slash;\n                *u++ = '/';\n                break;\n#endif\n            case '/':\n#if (NGX_WIN32)\n                if (u - 2 >= r->uri.data\n                    && *(u - 1) == '.' && *(u - 2) != '.')\n                {\n                    u--;\n                }\n#endif\n                r->uri_ext = NULL;\n                state = sw_slash;\n                *u++ = ch;\n                break;\n            case '%':\n                quoted_state = state;\n                state = sw_quoted;\n                break;\n            case '?':\n                r->args_start = p;\n                goto args;\n            case '#':\n                goto done;\n            case '.':\n                r->uri_ext = u + 1;\n                *u++ = ch;\n                break;\n            case '+':\n                r->plus_in_uri = 1;\n                /* fall through */\n            default:\n                *u++ = ch;\n                break;\n            }\n\n            ch = *p++;\n            break;\n\n        case sw_slash:\n\n            if (usual[ch >> 5] & (1U << (ch & 0x1f))) {\n                state = sw_usual;\n                *u++ = ch;\n                ch = *p++;\n                break;\n            }\n\n            switch (ch) {\n#if (NGX_WIN32)\n            case '\\\\':\n                break;\n#endif\n            case '/':\n                if (!merge_slashes) {\n                    *u++ = ch;\n                }\n                break;\n            case '.':\n                state = sw_dot;\n                *u++ = ch;\n                break;\n            case '%':\n                quoted_state = state;\n                state = sw_quoted;\n                break;\n            case '?':\n                r->args_start = p;\n                goto args;\n            case '#':\n                goto done;\n            case '+':\n                r->plus_in_uri = 1;\n                /* fall through */\n            default:\n                state = sw_usual;\n                *u++ = ch;\n                break;\n            }\n\n            ch = *p++;\n            break;\n\n        case sw_dot:\n\n            if (usual[ch >> 5] & (1U << (ch & 0x1f))) {\n                state = sw_usual;\n                *u++ = ch;\n                ch = *p++;\n                break;\n            }\n\n            switch (ch) {\n#if (NGX_WIN32)\n            case '\\\\':\n#endif\n            case '/':\n                state = sw_slash;\n                u--;\n                break;\n            case '.':\n                state = sw_dot_dot;\n                *u++ = ch;\n                break;\n            case '%':\n                quoted_state = state;\n                state = sw_quoted;\n                break;\n            case '?':\n                u--;\n                r->args_start = p;\n                goto args;\n            case '#':\n                u--;\n                goto done;\n            case '+':\n                r->plus_in_uri = 1;\n                /* fall through */\n            default:\n                state = sw_usual;\n                *u++ = ch;\n                break;\n            }\n\n            ch = *p++;\n            break;\n\n        case sw_dot_dot:\n\n            if (usual[ch >> 5] & (1U << (ch & 0x1f))) {\n                state = sw_usual;\n                *u++ = ch;\n                ch = *p++;\n                break;\n            }\n\n            switch (ch) {\n#if (NGX_WIN32)\n            case '\\\\':\n#endif\n            case '/':\n            case '?':\n            case '#':\n                u -= 4;\n                for ( ;; ) {\n                    if (u < r->uri.data) {\n                        return NGX_HTTP_PARSE_INVALID_REQUEST;\n                    }\n                    if (*u == '/') {\n                        u++;\n                        break;\n                    }\n                    u--;\n                }\n                if (ch == '?') {\n                    r->args_start = p;\n                    goto args;\n                }\n                if (ch == '#') {\n                    goto done;\n                }\n                state = sw_slash;\n                break;\n            case '%':\n                quoted_state = state;\n                state = sw_quoted;\n                break;\n            case '+':\n                r->plus_in_uri = 1;\n                /* fall through */\n            default:\n                state = sw_usual;\n                *u++ = ch;\n                break;\n            }\n\n            ch = *p++;\n            break;\n\n        case sw_quoted:\n            r->quoted_uri = 1;\n\n            if (ch >= '0' && ch <= '9') {\n                decoded = (u_char) (ch - '0');\n                state = sw_quoted_second;\n                ch = *p++;\n                break;\n            }\n\n            c = (u_char) (ch | 0x20);\n            if (c >= 'a' && c <= 'f') {\n                decoded = (u_char) (c - 'a' + 10);\n                state = sw_quoted_second;\n                ch = *p++;\n                break;\n            }\n\n            return NGX_HTTP_PARSE_INVALID_REQUEST;\n\n        case sw_quoted_second:\n            if (ch >= '0' && ch <= '9') {\n                ch = (u_char) ((decoded << 4) + (ch - '0'));\n\n                if (ch == '%' || ch == '#') {\n                    state = sw_usual;\n                    *u++ = ch;\n                    ch = *p++;\n                    break;\n\n                } else if (ch == '\\0') {\n                    return NGX_HTTP_PARSE_INVALID_REQUEST;\n                }\n\n                state = quoted_state;\n                break;\n            }\n\n            c = (u_char) (ch | 0x20);\n            if (c >= 'a' && c <= 'f') {\n                ch = (u_char) ((decoded << 4) + (c - 'a') + 10);\n\n                if (ch == '?') {\n                    state = sw_usual;\n                    *u++ = ch;\n                    ch = *p++;\n                    break;\n\n                } else if (ch == '+') {\n                    r->plus_in_uri = 1;\n                }\n\n                state = quoted_state;\n                break;\n            }\n\n            return NGX_HTTP_PARSE_INVALID_REQUEST;\n        }\n    }\n\n    if (state == sw_quoted || state == sw_quoted_second) {\n        return NGX_HTTP_PARSE_INVALID_REQUEST;\n    }\n\n    if (state == sw_dot) {\n        u--;\n\n    } else if (state == sw_dot_dot) {\n        u -= 4;\n\n        for ( ;; ) {\n            if (u < r->uri.data) {\n                return NGX_HTTP_PARSE_INVALID_REQUEST;\n            }\n\n            if (*u == '/') {\n                u++;\n                break;\n            }\n\n            u--;\n        }\n    }\n\ndone:\n\n    r->uri.len = u - r->uri.data;\n\n    if (r->uri_ext) {\n        r->exten.len = u - r->uri_ext;\n        r->exten.data = r->uri_ext;\n    }\n\n    r->uri_ext = NULL;\n\n    return NGX_OK;\n\nargs:\n\n    while (p < r->uri_end) {\n        if (*p++ != '#') {\n            continue;\n        }\n\n        r->args.len = p - 1 - r->args_start;\n        r->args.data = r->args_start;\n        r->args_start = NULL;\n\n        break;\n    }\n\n    r->uri.len = u - r->uri.data;\n\n    if (r->uri_ext) {\n        r->exten.len = u - r->uri_ext;\n        r->exten.data = r->uri_ext;\n    }\n\n    r->uri_ext = NULL;\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_parse_status_line(ngx_http_request_t *r, ngx_buf_t *b,\n    ngx_http_status_t *status)\n{\n    u_char   ch;\n    u_char  *p;\n    enum {\n        sw_start = 0,\n        sw_H,\n        sw_HT,\n        sw_HTT,\n        sw_HTTP,\n        sw_first_major_digit,\n        sw_major_digit,\n        sw_first_minor_digit,\n        sw_minor_digit,\n        sw_status,\n        sw_space_after_status,\n        sw_status_text,\n        sw_almost_done\n    } state;\n\n    state = r->state;\n\n    for (p = b->pos; p < b->last; p++) {\n        ch = *p;\n\n        switch (state) {\n\n        /* \"HTTP/\" */\n        case sw_start:\n            switch (ch) {\n            case 'H':\n                state = sw_H;\n                break;\n            default:\n                return NGX_ERROR;\n            }\n            break;\n\n        case sw_H:\n            switch (ch) {\n            case 'T':\n                state = sw_HT;\n                break;\n            default:\n                return NGX_ERROR;\n            }\n            break;\n\n        case sw_HT:\n            switch (ch) {\n            case 'T':\n                state = sw_HTT;\n                break;\n            default:\n                return NGX_ERROR;\n            }\n            break;\n\n        case sw_HTT:\n            switch (ch) {\n            case 'P':\n                state = sw_HTTP;\n                break;\n            default:\n                return NGX_ERROR;\n            }\n            break;\n\n        case sw_HTTP:\n            switch (ch) {\n            case '/':\n                state = sw_first_major_digit;\n                break;\n            default:\n                return NGX_ERROR;\n            }\n            break;\n\n        /* the first digit of major HTTP version */\n        case sw_first_major_digit:\n            if (ch < '1' || ch > '9') {\n                return NGX_ERROR;\n            }\n\n            r->http_major = ch - '0';\n            state = sw_major_digit;\n            break;\n\n        /* the major HTTP version or dot */\n        case sw_major_digit:\n            if (ch == '.') {\n                state = sw_first_minor_digit;\n                break;\n            }\n\n            if (ch < '0' || ch > '9') {\n                return NGX_ERROR;\n            }\n\n            if (r->http_major > 99) {\n                return NGX_ERROR;\n            }\n\n            r->http_major = r->http_major * 10 + (ch - '0');\n            break;\n\n        /* the first digit of minor HTTP version */\n        case sw_first_minor_digit:\n            if (ch < '0' || ch > '9') {\n                return NGX_ERROR;\n            }\n\n            r->http_minor = ch - '0';\n            state = sw_minor_digit;\n            break;\n\n        /* the minor HTTP version or the end of the request line */\n        case sw_minor_digit:\n            if (ch == ' ') {\n                state = sw_status;\n                break;\n            }\n\n            if (ch < '0' || ch > '9') {\n                return NGX_ERROR;\n            }\n\n            if (r->http_minor > 99) {\n                return NGX_ERROR;\n            }\n\n            r->http_minor = r->http_minor * 10 + (ch - '0');\n            break;\n\n        /* HTTP status code */\n        case sw_status:\n            if (ch == ' ') {\n                break;\n            }\n\n            if (ch < '0' || ch > '9') {\n                return NGX_ERROR;\n            }\n\n            status->code = status->code * 10 + (ch - '0');\n\n            if (++status->count == 3) {\n                state = sw_space_after_status;\n                status->start = p - 2;\n            }\n\n            break;\n\n        /* space or end of line */\n        case sw_space_after_status:\n            switch (ch) {\n            case ' ':\n                state = sw_status_text;\n                break;\n            case '.':                    /* IIS may send 403.1, 403.2, etc */\n                state = sw_status_text;\n                break;\n            case CR:\n                state = sw_almost_done;\n                break;\n            case LF:\n                goto done;\n            default:\n                return NGX_ERROR;\n            }\n            break;\n\n        /* any text until end of line */\n        case sw_status_text:\n            switch (ch) {\n            case CR:\n                state = sw_almost_done;\n\n                break;\n            case LF:\n                goto done;\n            }\n            break;\n\n        /* end of status line */\n        case sw_almost_done:\n            status->end = p - 1;\n            switch (ch) {\n            case LF:\n                goto done;\n            default:\n                return NGX_ERROR;\n            }\n        }\n    }\n\n    b->pos = p;\n    r->state = state;\n\n    return NGX_AGAIN;\n\ndone:\n\n    b->pos = p + 1;\n\n    if (status->end == NULL) {\n        status->end = p;\n    }\n\n    status->http_version = r->http_major * 1000 + r->http_minor;\n    r->state = sw_start;\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_parse_unsafe_uri(ngx_http_request_t *r, ngx_str_t *uri,\n    ngx_str_t *args, ngx_uint_t *flags)\n{\n    u_char      ch, *p, *src, *dst;\n    size_t      len;\n    ngx_uint_t  quoted;\n\n    len = uri->len;\n    p = uri->data;\n    quoted = 0;\n\n    if (len == 0 || p[0] == '?') {\n        goto unsafe;\n    }\n\n    if (p[0] == '.' && len > 1 && p[1] == '.'\n        && (len == 2 || ngx_path_separator(p[2])))\n    {\n        goto unsafe;\n    }\n\n    for ( /* void */ ; len; len--) {\n\n        ch = *p++;\n\n        if (ch == '%') {\n            quoted = 1;\n            continue;\n        }\n\n        if (usual[ch >> 5] & (1U << (ch & 0x1f))) {\n            continue;\n        }\n\n        if (ch == '?') {\n            args->len = len - 1;\n            args->data = p;\n            uri->len -= len;\n\n            break;\n        }\n\n        if (ch == '\\0') {\n            goto unsafe;\n        }\n\n        if (ngx_path_separator(ch) && len > 2) {\n\n            /* detect \"/../\" and \"/..\" */\n\n            if (p[0] == '.' && p[1] == '.'\n                && (len == 3 || ngx_path_separator(p[2])))\n            {\n                goto unsafe;\n            }\n        }\n    }\n\n    if (quoted) {\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"escaped URI: \\\"%V\\\"\", uri);\n\n        src = uri->data;\n\n        dst = ngx_pnalloc(r->pool, uri->len);\n        if (dst == NULL) {\n            return NGX_ERROR;\n        }\n\n        uri->data = dst;\n\n        ngx_unescape_uri(&dst, &src, uri->len, 0);\n\n        uri->len = dst - uri->data;\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"unescaped URI: \\\"%V\\\"\", uri);\n\n        len = uri->len;\n        p = uri->data;\n\n        if (p[0] == '.' && len > 1 && p[1] == '.'\n            && (len == 2 || ngx_path_separator(p[2])))\n        {\n            goto unsafe;\n        }\n\n        for ( /* void */ ; len; len--) {\n\n            ch = *p++;\n\n            if (ch == '\\0') {\n                goto unsafe;\n            }\n\n            if (ngx_path_separator(ch) && len > 2) {\n\n                /* detect \"/../\" and \"/..\" */\n\n                if (p[0] == '.' && p[1] == '.'\n                    && (len == 3 || ngx_path_separator(p[2])))\n                {\n                    goto unsafe;\n                }\n            }\n        }\n    }\n\n    return NGX_OK;\n\nunsafe:\n\n    if (*flags & NGX_HTTP_LOG_UNSAFE) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"unsafe URI \\\"%V\\\" was detected\", uri);\n    }\n\n    return NGX_ERROR;\n}\n\n\nngx_table_elt_t *\nngx_http_parse_multi_header_lines(ngx_http_request_t *r,\n    ngx_table_elt_t *headers, ngx_str_t *name, ngx_str_t *value)\n{\n    u_char           *start, *last, *end, ch;\n    ngx_table_elt_t  *h;\n\n    for (h = headers; h; h = h->next) {\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"parse header: \\\"%V: %V\\\"\", &h->key, &h->value);\n\n        if (name->len > h->value.len) {\n            continue;\n        }\n\n        start = h->value.data;\n        end = h->value.data + h->value.len;\n\n        while (start < end) {\n\n            if (ngx_strncasecmp(start, name->data, name->len) != 0) {\n                goto skip;\n            }\n\n            for (start += name->len; start < end && *start == ' '; start++) {\n                /* void */\n            }\n\n            if (value == NULL) {\n                if (start == end || *start == ',') {\n                    return h;\n                }\n\n                goto skip;\n            }\n\n            if (start == end || *start++ != '=') {\n                /* the invalid header value */\n                goto skip;\n            }\n\n            while (start < end && *start == ' ') { start++; }\n\n            for (last = start; last < end && *last != ';'; last++) {\n                /* void */\n            }\n\n            value->len = last - start;\n            value->data = start;\n\n            return h;\n\n        skip:\n\n            while (start < end) {\n                ch = *start++;\n                if (ch == ';' || ch == ',') {\n                    break;\n                }\n            }\n\n            while (start < end && *start == ' ') { start++; }\n        }\n    }\n\n    return NULL;\n}\n\n\nngx_table_elt_t *\nngx_http_parse_set_cookie_lines(ngx_http_request_t *r,\n    ngx_table_elt_t *headers, ngx_str_t *name, ngx_str_t *value)\n{\n    u_char           *start, *last, *end;\n    ngx_table_elt_t  *h;\n\n    for (h = headers; h; h = h->next) {\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"parse header: \\\"%V: %V\\\"\", &h->key, &h->value);\n\n        if (name->len >= h->value.len) {\n            continue;\n        }\n\n        start = h->value.data;\n        end = h->value.data + h->value.len;\n\n        if (ngx_strncasecmp(start, name->data, name->len) != 0) {\n            continue;\n        }\n\n        for (start += name->len; start < end && *start == ' '; start++) {\n            /* void */\n        }\n\n        if (start == end || *start++ != '=') {\n            /* the invalid header value */\n            continue;\n        }\n\n        while (start < end && *start == ' ') { start++; }\n\n        for (last = start; last < end && *last != ';'; last++) {\n            /* void */\n        }\n\n        value->len = last - start;\n        value->data = start;\n\n        return h;\n    }\n\n    return NULL;\n}\n\n\nngx_int_t\nngx_http_arg(ngx_http_request_t *r, u_char *name, size_t len, ngx_str_t *value)\n{\n    u_char  *p, *last;\n\n    if (r->args.len == 0) {\n        return NGX_DECLINED;\n    }\n\n    p = r->args.data;\n    last = p + r->args.len;\n\n    for ( /* void */ ; p < last; p++) {\n\n        /* we need '=' after name, so drop one char from last */\n\n        p = ngx_strlcasestrn(p, last - 1, name, len - 1);\n\n        if (p == NULL) {\n            return NGX_DECLINED;\n        }\n\n        if ((p == r->args.data || *(p - 1) == '&') && *(p + len) == '=') {\n\n            value->data = p + len + 1;\n\n            p = ngx_strlchr(p, last, '&');\n\n            if (p == NULL) {\n                p = r->args.data + r->args.len;\n            }\n\n            value->len = p - value->data;\n\n            return NGX_OK;\n        }\n    }\n\n    return NGX_DECLINED;\n}\n\n\nvoid\nngx_http_split_args(ngx_http_request_t *r, ngx_str_t *uri, ngx_str_t *args)\n{\n    u_char  *p, *last;\n\n    last = uri->data + uri->len;\n\n    p = ngx_strlchr(uri->data, last, '?');\n\n    if (p) {\n        uri->len = p - uri->data;\n        p++;\n        args->len = last - p;\n        args->data = p;\n\n    } else {\n        args->len = 0;\n    }\n}\n\n\nngx_int_t\nngx_http_parse_chunked(ngx_http_request_t *r, ngx_buf_t *b,\n    ngx_http_chunked_t *ctx)\n{\n    u_char     *pos, ch, c;\n    ngx_int_t   rc;\n    enum {\n        sw_chunk_start = 0,\n        sw_chunk_size,\n        sw_chunk_extension,\n        sw_chunk_extension_almost_done,\n        sw_chunk_data,\n        sw_after_data,\n        sw_after_data_almost_done,\n        sw_last_chunk_extension,\n        sw_last_chunk_extension_almost_done,\n        sw_trailer,\n        sw_trailer_almost_done,\n        sw_trailer_header,\n        sw_trailer_header_almost_done\n    } state;\n\n    state = ctx->state;\n\n    if (state == sw_chunk_data && ctx->size == 0) {\n        state = sw_after_data;\n    }\n\n    rc = NGX_AGAIN;\n\n    for (pos = b->pos; pos < b->last; pos++) {\n\n        ch = *pos;\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"http chunked byte: %02Xd s:%d\", ch, state);\n\n        switch (state) {\n\n        case sw_chunk_start:\n            if (ch >= '0' && ch <= '9') {\n                state = sw_chunk_size;\n                ctx->size = ch - '0';\n                break;\n            }\n\n            c = (u_char) (ch | 0x20);\n\n            if (c >= 'a' && c <= 'f') {\n                state = sw_chunk_size;\n                ctx->size = c - 'a' + 10;\n                break;\n            }\n\n            goto invalid;\n\n        case sw_chunk_size:\n            if (ctx->size > NGX_MAX_OFF_T_VALUE / 16) {\n                goto invalid;\n            }\n\n            if (ch >= '0' && ch <= '9') {\n                ctx->size = ctx->size * 16 + (ch - '0');\n                break;\n            }\n\n            c = (u_char) (ch | 0x20);\n\n            if (c >= 'a' && c <= 'f') {\n                ctx->size = ctx->size * 16 + (c - 'a' + 10);\n                break;\n            }\n\n            if (ctx->size == 0) {\n\n                switch (ch) {\n                case CR:\n                    state = sw_last_chunk_extension_almost_done;\n                    break;\n                case LF:\n                    state = sw_trailer;\n                    break;\n                case ';':\n                case ' ':\n                case '\\t':\n                    state = sw_last_chunk_extension;\n                    break;\n                default:\n                    goto invalid;\n                }\n\n                break;\n            }\n\n            switch (ch) {\n            case CR:\n                state = sw_chunk_extension_almost_done;\n                break;\n            case LF:\n                state = sw_chunk_data;\n                break;\n            case ';':\n            case ' ':\n            case '\\t':\n                state = sw_chunk_extension;\n                break;\n            default:\n                goto invalid;\n            }\n\n            break;\n\n        case sw_chunk_extension:\n            switch (ch) {\n            case CR:\n                state = sw_chunk_extension_almost_done;\n                break;\n            case LF:\n                state = sw_chunk_data;\n            }\n            break;\n\n        case sw_chunk_extension_almost_done:\n            if (ch == LF) {\n                state = sw_chunk_data;\n                break;\n            }\n            goto invalid;\n\n        case sw_chunk_data:\n            rc = NGX_OK;\n            goto data;\n\n        case sw_after_data:\n            switch (ch) {\n            case CR:\n                state = sw_after_data_almost_done;\n                break;\n            case LF:\n                state = sw_chunk_start;\n                break;\n            default:\n                goto invalid;\n            }\n            break;\n\n        case sw_after_data_almost_done:\n            if (ch == LF) {\n                state = sw_chunk_start;\n                break;\n            }\n            goto invalid;\n\n        case sw_last_chunk_extension:\n            switch (ch) {\n            case CR:\n                state = sw_last_chunk_extension_almost_done;\n                break;\n            case LF:\n                state = sw_trailer;\n            }\n            break;\n\n        case sw_last_chunk_extension_almost_done:\n            if (ch == LF) {\n                state = sw_trailer;\n                break;\n            }\n            goto invalid;\n\n        case sw_trailer:\n            switch (ch) {\n            case CR:\n                state = sw_trailer_almost_done;\n                break;\n            case LF:\n                goto done;\n            default:\n                state = sw_trailer_header;\n            }\n            break;\n\n        case sw_trailer_almost_done:\n            if (ch == LF) {\n                goto done;\n            }\n            goto invalid;\n\n        case sw_trailer_header:\n            switch (ch) {\n            case CR:\n                state = sw_trailer_header_almost_done;\n                break;\n            case LF:\n                state = sw_trailer;\n            }\n            break;\n\n        case sw_trailer_header_almost_done:\n            if (ch == LF) {\n                state = sw_trailer;\n                break;\n            }\n            goto invalid;\n\n        }\n    }\n\ndata:\n\n    ctx->state = state;\n    b->pos = pos;\n\n    if (ctx->size > NGX_MAX_OFF_T_VALUE - 5) {\n        goto invalid;\n    }\n\n    switch (state) {\n\n    case sw_chunk_start:\n        ctx->length = 3 /* \"0\" LF LF */;\n        break;\n    case sw_chunk_size:\n        ctx->length = 1 /* LF */\n                      + (ctx->size ? ctx->size + 4 /* LF \"0\" LF LF */\n                                   : 1 /* LF */);\n        break;\n    case sw_chunk_extension:\n    case sw_chunk_extension_almost_done:\n        ctx->length = 1 /* LF */ + ctx->size + 4 /* LF \"0\" LF LF */;\n        break;\n    case sw_chunk_data:\n        ctx->length = ctx->size + 4 /* LF \"0\" LF LF */;\n        break;\n    case sw_after_data:\n    case sw_after_data_almost_done:\n        ctx->length = 4 /* LF \"0\" LF LF */;\n        break;\n    case sw_last_chunk_extension:\n    case sw_last_chunk_extension_almost_done:\n        ctx->length = 2 /* LF LF */;\n        break;\n    case sw_trailer:\n    case sw_trailer_almost_done:\n        ctx->length = 1 /* LF */;\n        break;\n    case sw_trailer_header:\n    case sw_trailer_header_almost_done:\n        ctx->length = 2 /* LF LF */;\n        break;\n\n    }\n\n    return rc;\n\ndone:\n\n    ctx->state = 0;\n    b->pos = pos + 1;\n\n    return NGX_DONE;\n\ninvalid:\n\n    return NGX_ERROR;\n}\n\n#if (T_HTTP_HEADER)\n\nstatic ngx_int_t\nngx_http_header(ngx_list_part_t *part, u_char *name, size_t len,\n    ngx_str_t *value)\n{\n    ngx_uint_t        i;\n    ngx_table_elt_t  *header;\n\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 (header[i].hash == 0) {\n            continue;\n        }\n\n        if (len == header[i].key.len\n            && ngx_strncasecmp(header[i].key.data, name, len) == 0)\n        {\n            *value = header[i].value;\n\n            return NGX_OK;\n        }\n    }\n\n    return NGX_DECLINED;\n\n}\n\nngx_int_t\nngx_http_header_in(ngx_http_request_t *r, u_char *name, size_t len,\n    ngx_str_t *value)\n{\n    return ngx_http_header(&r->headers_in.headers.part, name, len, value);\n}\n\n\nngx_int_t\nngx_http_header_out(ngx_http_request_t *r, u_char *name, size_t len,\n    ngx_str_t *value)\n{\n    return ngx_http_header(&r->headers_out.headers.part, name, len, value);\n}\n#endif\n\n"
  },
  {
    "path": "src/http/ngx_http_postpone_filter_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\nstatic ngx_int_t ngx_http_postpone_filter_add(ngx_http_request_t *r,\n    ngx_chain_t *in);\nstatic ngx_int_t ngx_http_postpone_filter_in_memory(ngx_http_request_t *r,\n    ngx_chain_t *in);\nstatic ngx_int_t ngx_http_postpone_filter_init(ngx_conf_t *cf);\n\n\nstatic ngx_http_module_t  ngx_http_postpone_filter_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    ngx_http_postpone_filter_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\nngx_module_t  ngx_http_postpone_filter_module = {\n    NGX_MODULE_V1,\n    &ngx_http_postpone_filter_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_http_output_body_filter_pt    ngx_http_next_body_filter;\n\n\nstatic ngx_int_t\nngx_http_postpone_filter(ngx_http_request_t *r, ngx_chain_t *in)\n{\n    ngx_connection_t              *c;\n    ngx_http_postponed_request_t  *pr;\n\n    c = r->connection;\n\n    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http postpone filter \\\"%V?%V\\\" %p\", &r->uri, &r->args, in);\n\n    if (r->subrequest_in_memory) {\n        return ngx_http_postpone_filter_in_memory(r, in);\n    }\n\n    if (r != c->data) {\n\n        if (in) {\n            if (ngx_http_postpone_filter_add(r, in) != NGX_OK) {\n                return NGX_ERROR;\n            }\n\n            return NGX_OK;\n        }\n\n#if 0\n        /* TODO: SSI may pass NULL */\n        ngx_log_error(NGX_LOG_ALERT, c->log, 0,\n                      \"http postpone filter NULL inactive request\");\n#endif\n\n        return NGX_OK;\n    }\n\n    if (r->postponed == NULL) {\n\n        if (in || c->buffered) {\n            return ngx_http_next_body_filter(r->main, in);\n        }\n\n        return NGX_OK;\n    }\n\n    if (in) {\n        if (ngx_http_postpone_filter_add(r, in) != NGX_OK) {\n            return NGX_ERROR;\n        }\n    }\n\n    do {\n        pr = r->postponed;\n\n        if (pr->request) {\n\n            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                           \"http postpone filter wake \\\"%V?%V\\\"\",\n                           &pr->request->uri, &pr->request->args);\n\n            r->postponed = pr->next;\n\n            c->data = pr->request;\n\n            return ngx_http_post_request(pr->request, NULL);\n        }\n\n        if (pr->out == NULL) {\n            ngx_log_error(NGX_LOG_ALERT, c->log, 0,\n                          \"http postpone filter NULL output\");\n\n        } else {\n            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                           \"http postpone filter output \\\"%V?%V\\\"\",\n                           &r->uri, &r->args);\n\n            if (ngx_http_next_body_filter(r->main, pr->out) == NGX_ERROR) {\n                return NGX_ERROR;\n            }\n        }\n\n        r->postponed = pr->next;\n\n    } while (r->postponed);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_postpone_filter_add(ngx_http_request_t *r, ngx_chain_t *in)\n{\n    ngx_http_postponed_request_t  *pr, **ppr;\n\n    if (r->postponed) {\n        for (pr = r->postponed; pr->next; pr = pr->next) { /* void */ }\n\n        if (pr->request == NULL) {\n            goto found;\n        }\n\n        ppr = &pr->next;\n\n    } else {\n        ppr = &r->postponed;\n    }\n\n    pr = ngx_palloc(r->pool, sizeof(ngx_http_postponed_request_t));\n    if (pr == NULL) {\n        return NGX_ERROR;\n    }\n\n    *ppr = pr;\n\n    pr->request = NULL;\n    pr->out = NULL;\n    pr->next = NULL;\n\nfound:\n\n    if (ngx_chain_add_copy(r->pool, &pr->out, in) == NGX_OK) {\n        return NGX_OK;\n    }\n\n    return NGX_ERROR;\n}\n\n\nstatic ngx_int_t\nngx_http_postpone_filter_in_memory(ngx_http_request_t *r, ngx_chain_t *in)\n{\n    size_t                     len;\n    ngx_buf_t                 *b;\n    ngx_connection_t          *c;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    c = r->connection;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http postpone filter in memory\");\n\n    if (r->out == NULL) {\n        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n        if (r->headers_out.content_length_n != -1) {\n            len = r->headers_out.content_length_n;\n\n            if (len > clcf->subrequest_output_buffer_size) {\n                ngx_log_error(NGX_LOG_ERR, c->log, 0,\n                              \"too big subrequest response: %uz\", len);\n                return NGX_ERROR;\n            }\n\n        } else {\n            len = clcf->subrequest_output_buffer_size;\n        }\n\n        b = ngx_create_temp_buf(r->pool, len);\n        if (b == NULL) {\n            return NGX_ERROR;\n        }\n\n        b->last_buf = 1;\n\n        r->out = ngx_alloc_chain_link(r->pool);\n        if (r->out == NULL) {\n            return NGX_ERROR;\n        }\n\n        r->out->buf = b;\n        r->out->next = NULL;\n    }\n\n    b = r->out->buf;\n\n    for ( /* void */ ; in; in = in->next) {\n\n        if (ngx_buf_special(in->buf)) {\n            continue;\n        }\n\n        len = in->buf->last - in->buf->pos;\n\n        if (len > (size_t) (b->end - b->last)) {\n            ngx_log_error(NGX_LOG_ERR, c->log, 0,\n                          \"too big subrequest response\");\n            return NGX_ERROR;\n        }\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"http postpone filter in memory %uz bytes\", len);\n\n        b->last = ngx_cpymem(b->last, in->buf->pos, len);\n        in->buf->pos = in->buf->last;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_postpone_filter_init(ngx_conf_t *cf)\n{\n    ngx_http_next_body_filter = ngx_http_top_body_filter;\n    ngx_http_top_body_filter = ngx_http_postpone_filter;\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "src/http/ngx_http_request.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n#if (NGX_HTTP_V2 && T_NGX_HTTP2_SRV_ENABLE)\n#include <ngx_http_v2_module.h>\n#endif\n\n\nstatic void ngx_http_wait_request_handler(ngx_event_t *ev);\nstatic ngx_http_request_t *ngx_http_alloc_request(ngx_connection_t *c);\nstatic void ngx_http_process_request_line(ngx_event_t *rev);\nstatic void ngx_http_process_request_headers(ngx_event_t *rev);\nstatic ssize_t ngx_http_read_request_header(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_alloc_large_header_buffer(ngx_http_request_t *r,\n    ngx_uint_t request_line);\n\nstatic ngx_int_t ngx_http_process_header_line(ngx_http_request_t *r,\n    ngx_table_elt_t *h, ngx_uint_t offset);\nstatic ngx_int_t ngx_http_process_unique_header_line(ngx_http_request_t *r,\n    ngx_table_elt_t *h, ngx_uint_t offset);\nstatic ngx_int_t ngx_http_process_host(ngx_http_request_t *r,\n    ngx_table_elt_t *h, ngx_uint_t offset);\nstatic ngx_int_t ngx_http_process_connection(ngx_http_request_t *r,\n    ngx_table_elt_t *h, ngx_uint_t offset);\nstatic ngx_int_t ngx_http_process_user_agent(ngx_http_request_t *r,\n    ngx_table_elt_t *h, ngx_uint_t offset);\n\nstatic ngx_int_t ngx_http_validate_host(ngx_str_t *host, ngx_pool_t *pool,\n    ngx_uint_t alloc);\nstatic ngx_int_t ngx_http_set_virtual_server(ngx_http_request_t *r,\n    ngx_str_t *host);\nstatic ngx_int_t ngx_http_find_virtual_server(ngx_connection_t *c,\n    ngx_http_virtual_names_t *virtual_names, ngx_str_t *host,\n    ngx_http_request_t *r, ngx_http_core_srv_conf_t **cscfp);\n\nstatic void ngx_http_request_handler(ngx_event_t *ev);\nstatic void ngx_http_terminate_request(ngx_http_request_t *r, ngx_int_t rc);\nstatic void ngx_http_terminate_handler(ngx_http_request_t *r);\nstatic void ngx_http_finalize_connection(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_set_write_handler(ngx_http_request_t *r);\nstatic void ngx_http_writer(ngx_http_request_t *r);\nstatic void ngx_http_request_finalizer(ngx_http_request_t *r);\n\nstatic void ngx_http_set_keepalive(ngx_http_request_t *r);\nstatic void ngx_http_keepalive_handler(ngx_event_t *ev);\nstatic void ngx_http_set_lingering_close(ngx_connection_t *c);\nstatic void ngx_http_lingering_close_handler(ngx_event_t *ev);\nstatic ngx_int_t ngx_http_post_action(ngx_http_request_t *r);\nstatic void ngx_http_close_request(ngx_http_request_t *r, ngx_int_t error);\nstatic void ngx_http_log_request(ngx_http_request_t *r);\n\nstatic u_char *ngx_http_log_error(ngx_log_t *log, u_char *buf, size_t len);\nstatic u_char *ngx_http_log_error_handler(ngx_http_request_t *r,\n    ngx_http_request_t *sr, u_char *buf, size_t len);\n\n#if (NGX_HTTP_SSL)\nstatic void ngx_http_ssl_handshake(ngx_event_t *rev);\nstatic void ngx_http_ssl_handshake_handler(ngx_connection_t *c);\n#endif\n\n\nstatic char *ngx_http_client_errors[] = {\n\n    /* NGX_HTTP_PARSE_INVALID_METHOD */\n    \"client sent invalid method\",\n\n    /* NGX_HTTP_PARSE_INVALID_REQUEST */\n    \"client sent invalid request\",\n\n    /* NGX_HTTP_PARSE_INVALID_VERSION */\n    \"client sent invalid version\",\n\n    /* NGX_HTTP_PARSE_INVALID_09_METHOD */\n    \"client sent invalid method in HTTP/0.9 request\"\n};\n\n\nngx_http_header_t  ngx_http_headers_in[] = {\n    { ngx_string(\"Host\"), offsetof(ngx_http_headers_in_t, host),\n                 ngx_http_process_host },\n\n    { ngx_string(\"Connection\"), offsetof(ngx_http_headers_in_t, connection),\n                 ngx_http_process_connection },\n\n    { ngx_string(\"If-Modified-Since\"),\n                 offsetof(ngx_http_headers_in_t, if_modified_since),\n                 ngx_http_process_unique_header_line },\n\n    { ngx_string(\"If-Unmodified-Since\"),\n                 offsetof(ngx_http_headers_in_t, if_unmodified_since),\n                 ngx_http_process_unique_header_line },\n\n    { ngx_string(\"If-Match\"),\n                 offsetof(ngx_http_headers_in_t, if_match),\n                 ngx_http_process_unique_header_line },\n\n    { ngx_string(\"If-None-Match\"),\n                 offsetof(ngx_http_headers_in_t, if_none_match),\n                 ngx_http_process_unique_header_line },\n\n    { ngx_string(\"User-Agent\"), offsetof(ngx_http_headers_in_t, user_agent),\n                 ngx_http_process_user_agent },\n\n    { ngx_string(\"Referer\"), offsetof(ngx_http_headers_in_t, referer),\n                 ngx_http_process_header_line },\n\n    { ngx_string(\"Content-Length\"),\n                 offsetof(ngx_http_headers_in_t, content_length),\n                 ngx_http_process_unique_header_line },\n\n    { ngx_string(\"Content-Range\"),\n                 offsetof(ngx_http_headers_in_t, content_range),\n                 ngx_http_process_unique_header_line },\n\n    { ngx_string(\"Content-Type\"),\n                 offsetof(ngx_http_headers_in_t, content_type),\n                 ngx_http_process_header_line },\n\n    { ngx_string(\"Range\"), offsetof(ngx_http_headers_in_t, range),\n                 ngx_http_process_header_line },\n\n    { ngx_string(\"If-Range\"),\n                 offsetof(ngx_http_headers_in_t, if_range),\n                 ngx_http_process_unique_header_line },\n\n    { ngx_string(\"Transfer-Encoding\"),\n                 offsetof(ngx_http_headers_in_t, transfer_encoding),\n                 ngx_http_process_unique_header_line },\n\n    { ngx_string(\"TE\"),\n                 offsetof(ngx_http_headers_in_t, te),\n                 ngx_http_process_header_line },\n\n    { ngx_string(\"Expect\"),\n                 offsetof(ngx_http_headers_in_t, expect),\n                 ngx_http_process_unique_header_line },\n\n    { ngx_string(\"Upgrade\"),\n                 offsetof(ngx_http_headers_in_t, upgrade),\n                 ngx_http_process_header_line },\n\n#if (NGX_HTTP_GZIP || NGX_HTTP_HEADERS)\n    { ngx_string(\"Accept-Encoding\"),\n                 offsetof(ngx_http_headers_in_t, accept_encoding),\n                 ngx_http_process_header_line },\n\n    { ngx_string(\"Via\"), offsetof(ngx_http_headers_in_t, via),\n                 ngx_http_process_header_line },\n#endif\n\n    { ngx_string(\"Authorization\"),\n                 offsetof(ngx_http_headers_in_t, authorization),\n                 ngx_http_process_unique_header_line },\n\n    { ngx_string(\"Keep-Alive\"), offsetof(ngx_http_headers_in_t, keep_alive),\n                 ngx_http_process_header_line },\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_process_header_line },\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_process_header_line },\n#endif\n\n#if (NGX_HTTP_HEADERS)\n    { ngx_string(\"Accept\"), offsetof(ngx_http_headers_in_t, accept),\n                 ngx_http_process_header_line },\n\n    { ngx_string(\"Accept-Language\"),\n                 offsetof(ngx_http_headers_in_t, accept_language),\n                 ngx_http_process_header_line },\n#endif\n\n#if (NGX_HTTP_DAV)\n    { ngx_string(\"Depth\"), offsetof(ngx_http_headers_in_t, depth),\n                 ngx_http_process_header_line },\n\n    { ngx_string(\"Destination\"), offsetof(ngx_http_headers_in_t, destination),\n                 ngx_http_process_header_line },\n\n    { ngx_string(\"Overwrite\"), offsetof(ngx_http_headers_in_t, overwrite),\n                 ngx_http_process_header_line },\n\n    { ngx_string(\"Date\"), offsetof(ngx_http_headers_in_t, date),\n                 ngx_http_process_header_line },\n#endif\n\n    { ngx_string(\"Cookie\"), offsetof(ngx_http_headers_in_t, cookie),\n                 ngx_http_process_header_line },\n\n    { ngx_null_string, 0, NULL }\n};\n\n#ifdef T_INGRESS_SHARED_MEMORY_PB\n/* Total number of SSL protocol versions */\n#define NGX_HTTPS_SSL_PROTOCOL_NUM         6\n\nstatic ngx_str_t ngx_ing_ssl_protocols = ngx_string(\"metadata_ssl_protocols\");\n#endif\n\n\nvoid\nngx_http_init_connection(ngx_connection_t *c)\n{\n    ngx_uint_t                 i;\n    ngx_event_t               *rev;\n    struct sockaddr_in        *sin;\n    ngx_http_port_t           *port;\n    ngx_http_in_addr_t        *addr;\n    ngx_http_log_ctx_t        *ctx;\n    ngx_http_connection_t     *hc;\n    ngx_http_core_srv_conf_t  *cscf;\n#if (NGX_HAVE_INET6)\n    struct sockaddr_in6       *sin6;\n    ngx_http_in6_addr_t       *addr6;\n#endif\n#if (NGX_HTTP_V2 && T_NGX_HTTP2_SRV_ENABLE)\n    ngx_http_v2_srv_conf_t *h2scf;\n#endif\n\n\n    hc = ngx_pcalloc(c->pool, sizeof(ngx_http_connection_t));\n    if (hc == NULL) {\n        ngx_http_close_connection(c);\n        return;\n    }\n\n    c->data = hc;\n\n    /* find the server configuration for the address:port */\n\n    port = c->listening->servers;\n\n    if (port->naddrs > 1) {\n\n        /*\n         * there are several addresses on this port and one of them\n         * is an \"*:port\" wildcard so getsockname() in ngx_http_server_addr()\n         * is required to determine a server address\n         */\n\n        if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) {\n            ngx_http_close_connection(c);\n            return;\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\n            addr6 = port->addrs;\n\n            /* the last address is \"*\" */\n\n            for (i = 0; i < port->naddrs - 1; i++) {\n                if (ngx_memcmp(&addr6[i].addr6, &sin6->sin6_addr, 16) == 0) {\n                    break;\n                }\n            }\n\n            hc->addr_conf = &addr6[i].conf;\n\n            break;\n#endif\n\n        default: /* AF_INET */\n            sin = (struct sockaddr_in *) c->local_sockaddr;\n\n            addr = port->addrs;\n\n            /* the last address is \"*\" */\n\n            for (i = 0; i < port->naddrs - 1; i++) {\n                if (addr[i].addr == sin->sin_addr.s_addr) {\n                    break;\n                }\n            }\n\n            hc->addr_conf = &addr[i].conf;\n\n            break;\n        }\n\n    } else {\n\n        switch (c->local_sockaddr->sa_family) {\n\n#if (NGX_HAVE_INET6)\n        case AF_INET6:\n            addr6 = port->addrs;\n            hc->addr_conf = &addr6[0].conf;\n            break;\n#endif\n\n        default: /* AF_INET */\n            addr = port->addrs;\n            hc->addr_conf = &addr[0].conf;\n            break;\n        }\n    }\n\n    /* the default server configuration for the address:port */\n    hc->conf_ctx = hc->addr_conf->default_server->ctx;\n\n    ctx = ngx_palloc(c->pool, sizeof(ngx_http_log_ctx_t));\n    if (ctx == NULL) {\n        ngx_http_close_connection(c);\n        return;\n    }\n\n    ctx->connection = c;\n    ctx->request = NULL;\n    ctx->current_request = NULL;\n\n    c->log->connection = c->number;\n    c->log->handler = ngx_http_log_error;\n    c->log->data = ctx;\n    c->log->action = \"waiting for request\";\n\n    c->log_error = NGX_ERROR_INFO;\n\n    rev = c->read;\n    rev->handler = ngx_http_wait_request_handler;\n    c->write->handler = ngx_http_empty_handler;\n\n#if (NGX_HTTP_V2 && T_NGX_HTTP2_SRV_ENABLE)\n    h2scf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_v2_module);\n#endif\n\n#if (NGX_HTTP_V2)\n    if (\n#if (T_NGX_HTTP2_SRV_ENABLE)\n        (\n#endif\n         hc->addr_conf->http2\n#if (T_NGX_HTTP2_SRV_ENABLE)\n         && h2scf->enable != 0) || h2scf->enable == 1\n#endif\n       )\n    {\n        rev->handler = ngx_http_v2_init;\n    }\n#endif\n\n#if (NGX_HTTP_SSL)\n    {\n    ngx_http_ssl_srv_conf_t  *sscf;\n\n    sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_ssl_module);\n\n    if (sscf->enable || hc->addr_conf->ssl) {\n        hc->ssl = 1;\n        c->log->action = \"SSL handshaking\";\n        rev->handler = ngx_http_ssl_handshake;\n    }\n    }\n#endif\n\n    if (hc->addr_conf->proxy_protocol) {\n        hc->proxy_protocol = 1;\n        c->log->action = \"reading PROXY protocol\";\n    }\n\n    if (rev->ready) {\n        /* the deferred accept(), iocp */\n\n        if (ngx_use_accept_mutex) {\n            ngx_post_event(rev, &ngx_posted_events);\n            return;\n        }\n\n        rev->handler(rev);\n        return;\n    }\n\n    cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module);\n\n    ngx_add_timer(rev, cscf->client_header_timeout);\n    ngx_reusable_connection(c, 1);\n\n    if (ngx_handle_read_event(rev, 0) != NGX_OK) {\n        ngx_http_close_connection(c);\n        return;\n    }\n}\n\n\nstatic void\nngx_http_wait_request_handler(ngx_event_t *rev)\n{\n    u_char                    *p;\n    size_t                     size;\n    ssize_t                    n;\n    ngx_buf_t                 *b;\n    ngx_connection_t          *c;\n    ngx_http_connection_t     *hc;\n    ngx_http_core_srv_conf_t  *cscf;\n\n    c = rev->data;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"http wait request handler\");\n\n    if (rev->timedout) {\n        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, \"client timed out\");\n        ngx_http_close_connection(c);\n        return;\n    }\n\n    if (c->close) {\n        ngx_http_close_connection(c);\n        return;\n    }\n\n    hc = c->data;\n    cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module);\n\n    size = cscf->client_header_buffer_size;\n\n    b = c->buffer;\n\n    if (b == NULL) {\n        b = ngx_create_temp_buf(c->pool, size);\n        if (b == NULL) {\n            ngx_http_close_connection(c);\n            return;\n        }\n\n        c->buffer = b;\n\n    } else if (b->start == NULL) {\n\n        b->start = ngx_palloc(c->pool, size);\n        if (b->start == NULL) {\n            ngx_http_close_connection(c);\n            return;\n        }\n\n        b->pos = b->start;\n        b->last = b->start;\n        b->end = b->last + size;\n    }\n\n    n = c->recv(c, b->last, size);\n\n    if (n == NGX_AGAIN) {\n\n        if (!rev->timer_set) {\n            ngx_add_timer(rev, cscf->client_header_timeout);\n            ngx_reusable_connection(c, 1);\n        }\n\n        if (ngx_handle_read_event(rev, 0) != NGX_OK) {\n            ngx_http_close_connection(c);\n            return;\n        }\n\n        /*\n         * We are trying to not hold c->buffer's memory for an idle connection.\n         */\n\n#if (NGX_HTTP_SSL && NGX_SSL_ASYNC)\n        /* For the Async implementation we need the same buffer to be used\n         * again on any async calls that have not completed.\n         * As such we need to turn off this optimisation if an async request\n         * is still in progress.\n         */\n        if ((c->async_enable && !ngx_ssl_waiting_for_async(c)) || !c->async_enable) {\n#endif\n            if (ngx_pfree(c->pool, b->start) == NGX_OK) {\n                b->start = NULL;\n            }\n#if (NGX_HTTP_SSL && NGX_SSL_ASYNC)\n        }\n#endif\n\n        return;\n    }\n\n    if (n == NGX_ERROR) {\n        ngx_http_close_connection(c);\n        return;\n    }\n\n    if (n == 0) {\n        ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                      \"client closed connection\");\n        ngx_http_close_connection(c);\n        return;\n    }\n\n    b->last += n;\n#if (T_NGX_REQ_STATUS)\n    c->received += n;\n#endif\n\n    if (hc->proxy_protocol) {\n        hc->proxy_protocol = 0;\n\n        p = ngx_proxy_protocol_read(c, b->pos, b->last);\n\n        if (p == NULL) {\n            ngx_http_close_connection(c);\n            return;\n        }\n\n        b->pos = p;\n\n        if (b->pos == b->last) {\n            c->log->action = \"waiting for request\";\n            b->pos = b->start;\n            b->last = b->start;\n            ngx_post_event(rev, &ngx_posted_events);\n            return;\n        }\n    }\n\n    c->log->action = \"reading client request line\";\n\n    ngx_reusable_connection(c, 0);\n\n    c->data = ngx_http_create_request(c);\n    if (c->data == NULL) {\n        ngx_http_close_connection(c);\n        return;\n    }\n\n    rev->handler = ngx_http_process_request_line;\n    ngx_http_process_request_line(rev);\n}\n\n\nngx_http_request_t *\nngx_http_create_request(ngx_connection_t *c)\n{\n    ngx_http_request_t        *r;\n    ngx_http_log_ctx_t        *ctx;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    r = ngx_http_alloc_request(c);\n    if (r == NULL) {\n        return NULL;\n    }\n\n    c->requests++;\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    ngx_set_connection_log(c, clcf->error_log);\n\n    ctx = c->log->data;\n    ctx->request = r;\n    ctx->current_request = r;\n\n#if (NGX_STAT_STUB)\n    (void) ngx_atomic_fetch_add(ngx_stat_reading, 1);\n    r->stat_reading = 1;\n    (void) ngx_atomic_fetch_add(ngx_stat_requests, 1);\n#endif\n\n    return r;\n}\n\n\nstatic ngx_http_request_t *\nngx_http_alloc_request(ngx_connection_t *c)\n{\n    ngx_pool_t                 *pool;\n    ngx_time_t                 *tp;\n    ngx_http_request_t         *r;\n    ngx_http_connection_t      *hc;\n    ngx_http_core_srv_conf_t   *cscf;\n    ngx_http_core_main_conf_t  *cmcf;\n\n    hc = c->data;\n\n    cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module);\n\n    pool = ngx_create_pool(cscf->request_pool_size, c->log);\n    if (pool == NULL) {\n        return NULL;\n    }\n\n    r = ngx_pcalloc(pool, sizeof(ngx_http_request_t));\n    if (r == NULL) {\n        ngx_destroy_pool(pool);\n        return NULL;\n    }\n\n    r->pool = pool;\n\n    r->http_connection = hc;\n    r->signature = NGX_HTTP_MODULE;\n    r->connection = c;\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#if (T_HTTP_UPSTREAM_TIMEOUT_VAR)\n    r->connect_time = NGX_CONF_UNSET_MSEC;\n    r->read_time = NGX_CONF_UNSET_MSEC;\n    r->send_time = NGX_CONF_UNSET_MSEC;\n#endif\n\n    r->read_event_handler = ngx_http_block_reading;\n\n    r->header_in = hc->busy ? hc->busy->buf : c->buffer;\n\n    if (ngx_list_init(&r->headers_out.headers, r->pool, 20,\n                      sizeof(ngx_table_elt_t))\n        != NGX_OK)\n    {\n        ngx_destroy_pool(r->pool);\n        return NULL;\n    }\n\n    if (ngx_list_init(&r->headers_out.trailers, r->pool, 4,\n                      sizeof(ngx_table_elt_t))\n        != NGX_OK)\n    {\n        ngx_destroy_pool(r->pool);\n        return NULL;\n    }\n\n    r->ctx = ngx_pcalloc(r->pool, sizeof(void *) * ngx_http_max_module);\n    if (r->ctx == NULL) {\n        ngx_destroy_pool(r->pool);\n        return NULL;\n    }\n\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        ngx_destroy_pool(r->pool);\n        return NULL;\n    }\n\n#if (NGX_HTTP_SSL)\n    if (c->ssl && !c->ssl->sendfile) {\n        r->main_filter_need_in_memory = 1;\n    }\n#endif\n\n    r->main = r;\n    r->count = 1;\n\n#if (T_NGX_RET_CACHE)\n    {\n    struct timeval              tv;\n    ngx_http_core_loc_conf_t   *clcf;\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    if (clcf->request_time_cache) {\n        tp = ngx_timeofday();\n        r->start_sec = tp->sec;\n        r->start_msec = tp->msec;\n        r->start_usec = tp->usec;\n\n    } else {\n        ngx_gettimeofday(&tv);\n        r->start_sec = tv.tv_sec;\n        r->start_msec = tv.tv_usec / 1000;\n        r->start_usec = tv.tv_usec % 1000;\n    }\n    }\n#else\n    tp = ngx_timeofday();\n    r->start_sec = tp->sec;\n    r->start_msec = tp->msec;\n#endif\n\n    r->method = NGX_HTTP_UNKNOWN;\n    r->http_version = NGX_HTTP_VERSION_10;\n\n    r->headers_in.content_length_n = -1;\n    r->headers_in.keep_alive_n = -1;\n    r->headers_out.content_length_n = -1;\n    r->headers_out.last_modified_time = -1;\n\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_READING_REQUEST_STATE;\n\n    r->log_handler = ngx_http_log_error_handler;\n\n    return r;\n}\n\n\n#if (NGX_HTTP_SSL)\n\nstatic void\nngx_http_ssl_handshake(ngx_event_t *rev)\n{\n    u_char                    *p, buf[NGX_PROXY_PROTOCOL_MAX_HEADER + 1];\n    size_t                     size;\n    ssize_t                    n;\n    ngx_err_t                  err;\n    ngx_int_t                  rc;\n    ngx_connection_t          *c;\n    ngx_http_connection_t     *hc;\n    ngx_http_ssl_srv_conf_t   *sscf;\n    ngx_http_core_loc_conf_t  *clcf;\n    ngx_http_core_srv_conf_t  *cscf;\n\n    c = rev->data;\n    hc = c->data;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0,\n                   \"http check ssl handshake\");\n\n    if (rev->timedout) {\n        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, \"client timed out\");\n        ngx_http_close_connection(c);\n        return;\n    }\n\n    if (c->close) {\n        ngx_http_close_connection(c);\n        return;\n    }\n\n    size = hc->proxy_protocol ? sizeof(buf) : 1;\n\n    n = recv(c->fd, (char *) buf, size, MSG_PEEK);\n\n    err = ngx_socket_errno;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, rev->log, 0, \"http recv(): %z\", n);\n\n    if (n == -1) {\n        if (err == NGX_EAGAIN) {\n            rev->ready = 0;\n\n            if (!rev->timer_set) {\n                cscf = ngx_http_get_module_srv_conf(hc->conf_ctx,\n                                                    ngx_http_core_module);\n                ngx_add_timer(rev, cscf->client_header_timeout);\n                ngx_reusable_connection(c, 1);\n            }\n\n            if (ngx_handle_read_event(rev, 0) != NGX_OK) {\n                ngx_http_close_connection(c);\n            }\n\n            return;\n        }\n\n        ngx_connection_error(c, err, \"recv() failed\");\n        ngx_http_close_connection(c);\n\n        return;\n    }\n\n    if (hc->proxy_protocol) {\n        hc->proxy_protocol = 0;\n\n        p = ngx_proxy_protocol_read(c, buf, buf + n);\n\n        if (p == NULL) {\n            ngx_http_close_connection(c);\n            return;\n        }\n\n        size = p - buf;\n\n        if (c->recv(c, buf, size) != (ssize_t) size) {\n            ngx_http_close_connection(c);\n            return;\n        }\n\n        c->log->action = \"SSL handshaking\";\n\n        if (n == (ssize_t) size) {\n            ngx_post_event(rev, &ngx_posted_events);\n            return;\n        }\n\n        n = 1;\n        buf[0] = *p;\n    }\n\n    if (n == 1) {\n        if (buf[0] & 0x80 /* SSLv2 */ || buf[0] == 0x16 /* SSLv3/TLSv1 */) {\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, rev->log, 0,\n                           \"https ssl handshake: 0x%02Xd\", buf[0]);\n\n            clcf = ngx_http_get_module_loc_conf(hc->conf_ctx,\n                                                ngx_http_core_module);\n\n            if (clcf->tcp_nodelay && ngx_tcp_nodelay(c) != NGX_OK) {\n                ngx_http_close_connection(c);\n                return;\n            }\n\n            sscf = ngx_http_get_module_srv_conf(hc->conf_ctx,\n                                                ngx_http_ssl_module);\n\n            if (ngx_ssl_create_connection(&sscf->ssl, c, NGX_SSL_BUFFER)\n                != NGX_OK)\n            {\n                ngx_http_close_connection(c);\n                return;\n            }\n\n#if (T_NGX_SSL_HANDSHAKE_TIME)\n            {\n            /* ssl handshake start time */\n            ngx_time_t *tp = ngx_timeofday();\n            c->ssl->handshake_start_msec = tp->sec * 1000 + tp->msec;\n            }\n#endif\n#if (T_NGX_SSL_NTLS)\n            if (sscf->enable_ntls) {\n                SSL_enable_ntls(c->ssl->connection);\n            }\n#endif\n\n            ngx_reusable_connection(c, 0);\n\n            rc = ngx_ssl_handshake(c);\n\n            if (rc == NGX_AGAIN) {\n\n                if (!rev->timer_set) {\n                    cscf = ngx_http_get_module_srv_conf(hc->conf_ctx,\n                                                        ngx_http_core_module);\n                    ngx_add_timer(rev, cscf->client_header_timeout);\n                }\n\n                c->ssl->handler = ngx_http_ssl_handshake_handler;\n                return;\n            }\n\n            ngx_http_ssl_handshake_handler(c);\n\n            return;\n        }\n\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, \"plain http\");\n\n        c->log->action = \"waiting for request\";\n#if (T_NGX_HTTPS_ALLOW_HTTP)\n        if (hc->addr_conf->https_allow_http) {\n            hc->ssl = 0;\n        }\n#endif\n\n        rev->handler = ngx_http_wait_request_handler;\n        ngx_http_wait_request_handler(rev);\n\n        return;\n    }\n\n    ngx_log_error(NGX_LOG_INFO, c->log, 0, \"client closed connection\");\n    ngx_http_close_connection(c);\n}\n\n\nstatic void\nngx_http_ssl_handshake_handler(ngx_connection_t *c)\n{\n    if (c->ssl->handshaked) {\n\n        /*\n         * The majority of browsers do not send the \"close notify\" alert.\n         * Among them are MSIE, old Mozilla, Netscape 4, Konqueror,\n         * and Links.  And what is more, MSIE ignores the server's alert.\n         *\n         * Opera and recent Mozilla send the alert.\n         */\n\n        c->ssl->no_wait_shutdown = 1;\n\n#if (NGX_HTTP_V2                                                              \\\n     && defined TLSEXT_TYPE_application_layer_protocol_negotiation)\n        {\n        unsigned int            len;\n        const unsigned char    *data;\n        ngx_http_connection_t  *hc;\n\n#if (T_NGX_HTTP2_SRV_ENABLE)\n        ngx_http_v2_srv_conf_t *h2scf;\n#endif\n        hc = c->data;\n\n#if (T_NGX_HTTP2_SRV_ENABLE)\n        h2scf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_v2_module);\n#endif\n\n        if (\n#if (T_NGX_HTTP2_SRV_ENABLE)\n            (\n#endif\n             hc->addr_conf->http2\n#if (T_NGX_HTTP2_SRV_ENABLE)\n             && h2scf->enable != 0) || h2scf->enable == 1\n#endif\n           )\n        {\n\n#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation\n            SSL_get0_alpn_selected(c->ssl->connection, &data, &len);\n\n#ifdef TLSEXT_TYPE_next_proto_neg\n            if (len == 0) {\n                SSL_get0_next_proto_negotiated(c->ssl->connection, &data, &len);\n            }\n#endif\n\n#else /* TLSEXT_TYPE_next_proto_neg */\n            SSL_get0_next_proto_negotiated(c->ssl->connection, &data, &len);\n#endif\n\n            if (len == 2 && data[0] == 'h' && data[1] == '2') {\n                ngx_http_v2_init(c->read);\n                return;\n            }\n        }\n        }\n#endif\n\n        c->log->action = \"waiting for request\";\n\n        c->read->handler = ngx_http_wait_request_handler;\n        /* STUB: epoll edge */ c->write->handler = ngx_http_empty_handler;\n\n        ngx_reusable_connection(c, 1);\n\n        ngx_http_wait_request_handler(c->read);\n\n        return;\n    }\n\n    if (c->read->timedout) {\n        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, \"client timed out\");\n    }\n\n    ngx_http_close_connection(c);\n}\n\n\n#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME\n\nint\nngx_http_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg)\n{\n#if defined(T_INGRESS_SHARED_MEMORY_PB) && OPENSSL_VERSION_NUMBER >= 0x10101000L\n    return SSL_TLSEXT_ERR_OK;\n#endif\n\n    ngx_int_t                  rc;\n    ngx_str_t                  host;\n    const char                *servername;\n    ngx_connection_t          *c;\n    ngx_http_connection_t     *hc;\n    ngx_http_ssl_srv_conf_t   *sscf;\n    ngx_http_core_loc_conf_t  *clcf;\n    ngx_http_core_srv_conf_t  *cscf;\n\n    c = ngx_ssl_get_connection(ssl_conn);\n\n    if (c->ssl->handshaked) {\n        *ad = SSL_AD_NO_RENEGOTIATION;\n        return SSL_TLSEXT_ERR_ALERT_FATAL;\n    }\n\n    hc = c->data;\n\n    servername = SSL_get_servername(ssl_conn, TLSEXT_NAMETYPE_host_name);\n\n    if (servername == NULL) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"SSL server name: null\");\n        goto done;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"SSL server name: \\\"%s\\\"\", servername);\n\n    host.len = ngx_strlen(servername);\n\n    if (host.len == 0) {\n        goto done;\n    }\n\n    host.data = (u_char *) servername;\n\n    rc = ngx_http_validate_host(&host, c->pool, 1);\n\n    if (rc == NGX_ERROR) {\n        goto error;\n    }\n\n    if (rc == NGX_DECLINED) {\n        goto done;\n    }\n\n    rc = ngx_http_find_virtual_server(c, hc->addr_conf->virtual_names, &host,\n                                      NULL, &cscf);\n\n    if (rc == NGX_ERROR) {\n        goto error;\n    }\n\n    if (rc == NGX_DECLINED) {\n        goto done;\n    }\n\n    hc->ssl_servername = ngx_palloc(c->pool, sizeof(ngx_str_t));\n    if (hc->ssl_servername == NULL) {\n        goto error;\n    }\n\n    *hc->ssl_servername = host;\n\n    hc->conf_ctx = cscf->ctx;\n\n    clcf = ngx_http_get_module_loc_conf(hc->conf_ctx, ngx_http_core_module);\n\n    ngx_set_connection_log(c, clcf->error_log);\n\n    sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_ssl_module);\n\n    c->ssl->buffer_size = sscf->buffer_size;\n\n    if (sscf->ssl.ctx) {\n        if (SSL_set_SSL_CTX(ssl_conn, sscf->ssl.ctx) == NULL) {\n            goto error;\n        }\n\n        /*\n         * SSL_set_SSL_CTX() only changes certs as of 1.0.0d\n         * adjust other things we care about\n         */\n\n        SSL_set_verify(ssl_conn, SSL_CTX_get_verify_mode(sscf->ssl.ctx),\n                       SSL_CTX_get_verify_callback(sscf->ssl.ctx));\n\n        SSL_set_verify_depth(ssl_conn, SSL_CTX_get_verify_depth(sscf->ssl.ctx));\n\n#if OPENSSL_VERSION_NUMBER >= 0x009080dfL\n        /* only in 0.9.8m+ */\n        SSL_clear_options(ssl_conn, SSL_get_options(ssl_conn) &\n                                    ~SSL_CTX_get_options(sscf->ssl.ctx));\n#endif\n\n        SSL_set_options(ssl_conn, SSL_CTX_get_options(sscf->ssl.ctx));\n\n#ifdef SSL_OP_NO_RENEGOTIATION\n        SSL_set_options(ssl_conn, SSL_OP_NO_RENEGOTIATION);\n#endif\n    }\n\ndone:\n\n    sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_ssl_module);\n\n    if (sscf->reject_handshake) {\n        c->ssl->handshake_rejected = 1;\n        *ad = SSL_AD_UNRECOGNIZED_NAME;\n        return SSL_TLSEXT_ERR_ALERT_FATAL;\n    }\n\n    return SSL_TLSEXT_ERR_OK;\n\nerror:\n\n    *ad = SSL_AD_INTERNAL_ERROR;\n    return SSL_TLSEXT_ERR_ALERT_FATAL;\n}\n\n#endif\n\n\n#ifdef T_INGRESS_SHARED_MEMORY_PB\nint\nngx_http_ssl_ctx_reset(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg)\n{\n    ngx_int_t                  rc;\n    ngx_str_t                  host;\n    const char                *servername = NULL;\n    ngx_connection_t          *c;\n    ngx_http_connection_t     *hc;\n    ngx_http_ssl_srv_conf_t   *sscf;\n    ngx_http_core_loc_conf_t  *clcf;\n    ngx_http_core_srv_conf_t  *cscf;\n\n    c = ngx_ssl_get_connection(ssl_conn);\n\n    if (c->ssl->handshaked) {\n        *ad = SSL_AD_NO_RENEGOTIATION;\n        return SSL_TLSEXT_ERR_ALERT_FATAL;\n    }\n\n#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME\n    servername = SSL_get_servername(ssl_conn, TLSEXT_NAMETYPE_host_name);\n#endif\n\n    if (servername == NULL) {\n        return SSL_TLSEXT_ERR_OK;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"SSL server name: \\\"%s\\\"\", servername);\n\n    host.len = ngx_strlen(servername);\n\n    if (host.len == 0) {\n        return SSL_TLSEXT_ERR_OK;\n    }\n\n    host.data = (u_char *) servername;\n\n    rc = ngx_http_validate_host(&host, c->pool, 1);\n\n    if (rc == NGX_ERROR) {\n        *ad = SSL_AD_INTERNAL_ERROR;\n        return SSL_TLSEXT_ERR_ALERT_FATAL;\n    }\n\n    if (rc == NGX_DECLINED) {\n        return SSL_TLSEXT_ERR_OK;\n    }\n\n    hc = c->data;\n\n    rc = ngx_http_find_virtual_server(c, hc->addr_conf->virtual_names, &host,\n                                      NULL, &cscf);\n\n    if (rc == NGX_ERROR) {\n        *ad = SSL_AD_INTERNAL_ERROR;\n        return SSL_TLSEXT_ERR_ALERT_FATAL;\n    }\n\n    if (rc == NGX_DECLINED) {\n        return SSL_TLSEXT_ERR_OK;\n    }\n\n    hc->ssl_servername = ngx_palloc(c->pool, sizeof(ngx_str_t));\n    if (hc->ssl_servername == NULL) {\n        *ad = SSL_AD_INTERNAL_ERROR;\n        return SSL_TLSEXT_ERR_ALERT_FATAL;\n    }\n\n    *hc->ssl_servername = host;\n\n    hc->conf_ctx = cscf->ctx;\n\n    clcf = ngx_http_get_module_loc_conf(hc->conf_ctx, ngx_http_core_module);\n\n    ngx_set_connection_log(c, clcf->error_log);\n\n    sscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_ssl_module);\n\n    c->ssl->buffer_size = sscf->buffer_size;\n\n    if (sscf->ssl.ctx) {\n        SSL_set_SSL_CTX(ssl_conn, sscf->ssl.ctx);\n\n        /*\n         * SSL_set_SSL_CTX() only changes certs as of 1.0.0d\n         * adjust other things we care about\n         */\n\n#ifdef SSL_set_SESSION_CTX\n        /* babassl api */\n        SSL_set_SESSION_CTX(ssl_conn, sscf->ssl.ctx);\n#endif\n\n        SSL_set_verify(ssl_conn, SSL_CTX_get_verify_mode(sscf->ssl.ctx),\n                       SSL_CTX_get_verify_callback(sscf->ssl.ctx));\n\n        SSL_set_verify_depth(ssl_conn, SSL_CTX_get_verify_depth(sscf->ssl.ctx));\n\n#if OPENSSL_VERSION_NUMBER >= 0x009080dfL\n        /* only in 0.9.8m+ */\n        SSL_clear_options(ssl_conn, SSL_get_options(ssl_conn) &\n                                    ~SSL_CTX_get_options(sscf->ssl.ctx));\n#endif\n\n        SSL_set_options(ssl_conn, SSL_CTX_get_options(sscf->ssl.ctx));\n\n#ifdef SSL_OP_NO_RENEGOTIATION\n        SSL_set_options(ssl_conn, SSL_OP_NO_RENEGOTIATION);\n#endif\n    }\n\n    return SSL_TLSEXT_ERR_OK;\n}\n\nstatic ngx_int_t\nngx_http_request_get_variable(ngx_http_request_t *r, ngx_str_t *name, ngx_str_t *value)\n{\n    u_char                      *low;\n    ngx_str_t                    var;\n    ngx_uint_t                   hash;\n    ngx_http_variable_value_t   *vv;\n\n    if (0 >= name->len || NULL == name->data) {\n        return NGX_ERROR;\n    }\n\n    low = ngx_pnalloc(r->pool, name->len);\n    if (low == NULL) {\n        return NGX_ERROR;\n    }\n\n    hash = ngx_hash_strlow(low, name->data, name->len);\n    var.data = low;\n    var.len = name->len;\n\n    vv = ngx_http_get_variable(r, &var, hash);\n\n    if (vv == NULL || vv->not_found || vv->valid == 0) {\n        return NGX_ERROR;\n    }\n\n    value->data = vv->data;\n    value->len = vv->len;\n\n    return NGX_OK;\n}\n\n#endif\n\n\n#if defined(T_INGRESS_SHARED_MEMORY_PB) && OPENSSL_VERSION_NUMBER >= 0x10101000L\n\nint\nngx_http_ssl_client_hello_callback(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg)\n{\n    int                        ret, rv;\n    void                      *cert_cb_arg;\n    size_t                     len, remaining;\n    SSL_CTX                   *ssl_ctx;\n    unsigned char             *servername;\n    unsigned long              options;\n    SSL_cert_cb_fn             cert_cb;\n    ngx_connection_t          *c;\n    const unsigned char       *p;\n    ngx_http_request_t        *r;\n    ngx_uint_t                 i = 0;\n    unsigned int               legacy_version = 0;\n    ngx_str_t                  ssl_protocols = ngx_null_string;\n    char                      *ssl_proto;\n    char                       ssl_protos[4 * NGX_HTTPS_SSL_PROTOCOL_NUM];\n    unsigned int               protos[NGX_HTTPS_SSL_PROTOCOL_NUM];\n    unsigned int               proto_num = 0;\n\n    c = ngx_ssl_get_connection(ssl_conn);\n\n    if (c->ssl && c->ssl->handshaked) {\n        return SSL_CLIENT_HELLO_SUCCESS;\n    }\n\n    if (c->ssl && c->ssl->client_hello_retry) {\n        goto recover;\n    }\n\n    if (SSL_client_hello_get0_ext(ssl_conn, TLSEXT_TYPE_status_request, &p,\n        &remaining) == 1)\n    {\n        if (*p == TLSEXT_STATUSTYPE_ocsp) {\n            SSL_set_tlsext_status_type(ssl_conn, TLSEXT_STATUSTYPE_ocsp);\n        }\n    }\n\n    if (!SSL_client_hello_get0_ext(ssl_conn, TLSEXT_TYPE_server_name, &p,\n        &remaining))\n    {\n        /* servername is NULL */\n        ngx_http_ssl_ctx_reset(ssl_conn, ad, arg);\n\n        goto end;\n    }\n\n    /* Extract the length of the supplied list of names. */\n    len = (*(p++) << 8);\n    len += *(p++);\n    if (len + 2 != remaining) {\n        return SSL_CLIENT_HELLO_ERROR;\n    }\n\n    remaining = len;\n    /*\n     * The list in practice only has a single element, so we only consider\n     * the first one.\n     */\n    if (remaining == 0 || *p++ != TLSEXT_NAMETYPE_host_name) {\n        return SSL_CLIENT_HELLO_ERROR;\n    }\n    remaining--;\n    /* Now we can finally pull out the byte array with the actual hostname. */\n    if (remaining <= 2) {\n        return SSL_CLIENT_HELLO_ERROR;\n    }\n    len = (*(p++) << 8);\n    len += *(p++);\n    if (len + 2 > remaining) {\n        return SSL_CLIENT_HELLO_ERROR;\n    }\n\n    remaining = len;\n\n    servername = ngx_pcalloc(c->pool, len + 1); /* remain 1 byte to '\\0' */\n    if (servername == NULL) {\n        goto end;\n    }\n\n    ngx_memcpy(servername, p, len);\n\n    if (!SSL_set_tlsext_host_name(ssl_conn, servername)) {\n        return SSL_CLIENT_HELLO_ERROR;\n    }\n\n    r = ngx_http_alloc_request(c);\n    if (r == NULL) {\n        return SSL_CLIENT_HELLO_ERROR;\n    }\n\n    r->logged = 1;\n\n    r->headers_in.server.len = len;\n    r->headers_in.server.data = ngx_pnalloc(r->pool, len);\n    if (r->headers_in.server.data == NULL) {\n        goto proto_next;\n    }\n\n    ngx_memcpy(r->headers_in.server.data, servername, len);\n\n    if (NGX_OK != ngx_http_request_get_variable(r, &ngx_ing_ssl_protocols, &ssl_protocols) || \n        0 == ssl_protocols.len) {\n        goto proto_next;\n    }\n\n    if (4 * NGX_HTTPS_SSL_PROTOCOL_NUM <= ssl_protocols.len) {\n        ngx_log_error(NGX_LOG_WARN, c->log, 0, \n                      \"variable %s length %d is invalid\", \n                      ngx_ing_ssl_protocols.data, \n                      ssl_protocols.len);\n        goto proto_next;\n    }\n\n    ngx_memcpy(ssl_protos, ssl_protocols.data, ssl_protocols.len);\n    ssl_protos[ssl_protocols.len] = '\\0';\n    ssl_proto = strtok(ssl_protos, \" \");\n    while(ssl_proto != NULL) {\n        protos[proto_num++] = strtoul(ssl_proto, NULL, 10);\n        ssl_proto = strtok(NULL, \" \");\n    }\n\n    if (!proto_num) {\n        goto proto_next;\n    }\n\n    if (SSL_client_hello_get0_ext(ssl_conn, TLSEXT_TYPE_supported_versions, &p,\n        &remaining))\n    {\n        if (remaining < 1) {\n            goto proto_next;\n        }\n        size_t msglen = p[0];\n        if (remaining != msglen + 1) {\n            goto proto_next;\n        }\n\n        unsigned int val;\n        if (msglen % 2) {\n            goto proto_next;\n        }\n        const unsigned char *msg = p + 1;\n        while (msglen) {\n            val = msg[0];\n            val = (val << 8) | msg[1];\n            if (val <= TLS_MAX_VERSION) {\n                ngx_log_error(NGX_LOG_DEBUG, c->log, 0, \n                              \"SNI: %s with supported versions %d\", \n                              servername, val);\n                for (i = 0; i < proto_num; i++) {\n                    if (val == protos[i]) {\n                        goto proto_next;\n                    }\n                }\n            }\n            msg += 2;\n            msglen -= 2;\n        }\n\n        goto proto_invalid;\n    }\n\n    legacy_version = SSL_client_hello_get0_legacy_version(ssl_conn);\n    for (i = 0; i < proto_num; i++) {\n        if (legacy_version == protos[i]) {\n            goto proto_next;\n        }\n    }\n\nproto_invalid:\n    ngx_log_error(NGX_LOG_ERR, c->log, 0, \n                  \"SNI: %s with ssl_protocols \\\"%V\\\" does not support version %d\", \n                  servername, &ssl_protocols, legacy_version);\n    ngx_http_free_request(r, 0);\n    c->destroyed = 0;\n    *ad = SSL_AD_PROTOCOL_VERSION;\n    return SSL_CLIENT_HELLO_ERROR;\nproto_next:\n    ngx_http_free_request(r, 0);\n    c->destroyed = 0;\n\n    ret = ngx_http_ssl_ctx_reset(ssl_conn, ad, arg);\n\n    if (ret == SSL_TLSEXT_ERR_OK || ret == SSL_TLSEXT_ERR_NOACK) {\nrecover:\n        ssl_ctx = SSL_get_SSL_CTX(ssl_conn);\n        if (c->ssl && c->ssl->client_hello_retry == 0) {\n            options = SSL_get_options(ssl_conn);\n            SSL_clear_options(ssl_conn, options);\n            options = SSL_CTX_get_options(ssl_ctx);\n            SSL_set_options(ssl_conn, options);\n        }\n\n        cert_cb = SSL_CTX_get_cert_cb(ssl_ctx);\n        cert_cb_arg = SSL_CTX_get_cert_cb_arg(ssl_ctx);\n        if (cert_cb != NULL) {\n            rv = cert_cb(ssl_conn, cert_cb_arg);\n            if (rv == 0) {\n                return SSL_CLIENT_HELLO_ERROR;\n            }\n\n            SSL_set_cert_cb(ssl_conn, NULL, NULL);\n\n            if (rv < 0) {\n                c->ssl->client_hello_retry = 1;\n                return SSL_CLIENT_HELLO_RETRY;\n            }\n\n            c->ssl->client_hello_retry = 0;\n        }\n    }\n\nend:\n\n    return SSL_CLIENT_HELLO_SUCCESS;\n}\n\n#endif\n\n\n#ifdef SSL_R_CERT_CB_ERROR\n\nint\nngx_http_ssl_certificate(ngx_ssl_conn_t *ssl_conn, void *arg)\n{\n    ngx_str_t                  cert, key;\n    ngx_uint_t                 i, nelts;\n    ngx_connection_t          *c;\n    ngx_http_request_t        *r;\n    ngx_http_ssl_srv_conf_t   *sscf;\n    ngx_http_complex_value_t  *certs, *keys;\n\n    c = ngx_ssl_get_connection(ssl_conn);\n\n    if (c->ssl->handshaked) {\n        return 0;\n    }\n\n    r = ngx_http_alloc_request(c);\n    if (r == NULL) {\n        return 0;\n    }\n\n    r->logged = 1;\n\n    sscf = arg;\n\n    nelts = sscf->certificate_values->nelts;\n    certs = sscf->certificate_values->elts;\n    keys = sscf->certificate_key_values->elts;\n\n    for (i = 0; i < nelts; i++) {\n\n        if (ngx_http_complex_value(r, &certs[i], &cert) != NGX_OK) {\n            goto failed;\n        }\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"ssl cert: \\\"%s\\\"\", cert.data);\n\n        if (ngx_http_complex_value(r, &keys[i], &key) != NGX_OK) {\n            goto failed;\n        }\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"ssl key: \\\"%s\\\"\", key.data);\n\n        if (ngx_ssl_connection_certificate(c, r->pool, &cert, &key,\n                                           sscf->passwords)\n            != NGX_OK)\n        {\n            goto failed;\n        }\n    }\n\n    ngx_http_free_request(r, 0);\n    c->log->action = \"SSL handshaking\";\n    c->destroyed = 0;\n    return 1;\n\nfailed:\n\n    ngx_http_free_request(r, 0);\n    c->log->action = \"SSL handshaking\";\n    c->destroyed = 0;\n    return 0;\n}\n\n#endif\n\n#endif\n\n\nstatic void\nngx_http_process_request_line(ngx_event_t *rev)\n{\n    ssize_t              n;\n    ngx_int_t            rc, rv;\n    ngx_str_t            host;\n    ngx_connection_t    *c;\n    ngx_http_request_t  *r;\n\n    c = rev->data;\n    r = c->data;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0,\n                   \"http process request line\");\n\n    if (rev->timedout) {\n        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, \"client timed out\");\n        c->timedout = 1;\n        ngx_http_close_request(r, NGX_HTTP_REQUEST_TIME_OUT);\n        return;\n    }\n\n    rc = NGX_AGAIN;\n\n    for ( ;; ) {\n\n        if (rc == NGX_AGAIN) {\n            n = ngx_http_read_request_header(r);\n\n            if (n == NGX_AGAIN || n == NGX_ERROR) {\n                break;\n            }\n        }\n\n        rc = ngx_http_parse_request_line(r, r->header_in);\n\n        if (rc == NGX_OK) {\n\n            /* the request line has been parsed successfully */\n\n            r->request_line.len = r->request_end - r->request_start;\n            r->request_line.data = r->request_start;\n            r->request_length = r->header_in->pos - r->request_start;\n\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                           \"http request line: \\\"%V\\\"\", &r->request_line);\n\n            r->method_name.len = r->method_end - r->request_start + 1;\n            r->method_name.data = r->request_line.data;\n\n            if (r->http_protocol.data) {\n                r->http_protocol.len = r->request_end - r->http_protocol.data;\n            }\n\n#if (NGX_HTTP_PROXY_CONNECT)\n\n            if (r->connect_host_start && r->connect_host_end) {\n\n                host.len = r->connect_host_end - r->connect_host_start;\n                host.data = r->connect_host_start;\n                rc = ngx_http_validate_host(&host, r->pool, 0);\n\n                if (rc == NGX_DECLINED) {\n                    ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                                  \"client sent invalid host in request line\");\n                    ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);\n                    return;\n                }\n\n                if (rc == NGX_ERROR) {\n                    ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n                    return;\n                }\n\n                r->connect_host = host;\n\n                if (!r->connect_port_end) {\n                   ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                                  \"client sent no port in request line\");\n                    ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);\n                    return;\n                }\n\n                r->connect_port.data = r->connect_host_end + 1;\n                r->connect_port.len = r->connect_port_end\n                                      - r->connect_host_end - 1;\n\n                ngx_int_t port;\n\n                port = ngx_atoi(r->connect_port.data, r->connect_port.len);\n                if (port == NGX_ERROR || port < 1 || port > 65535) {\n                    ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                                  \"client sent invalid port in request line\");\n                    ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);\n                    return;\n                }\n\n                r->connect_port_n = port;\n\n                /* skip processing request uri */\n            } else\n#endif\n\n            if (ngx_http_process_request_uri(r) != NGX_OK) {\n                break;\n            }\n\n            if (r->schema_end) {\n                r->schema.len = r->schema_end - r->schema_start;\n                r->schema.data = r->schema_start;\n            }\n\n            if (r->host_end) {\n\n                host.len = r->host_end - r->host_start;\n                host.data = r->host_start;\n\n                rc = ngx_http_validate_host(&host, r->pool, 0);\n\n                if (rc == NGX_DECLINED) {\n                    ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                                  \"client sent invalid host in request line\");\n                    ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);\n                    break;\n                }\n\n                if (rc == NGX_ERROR) {\n                    ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n                    break;\n                }\n\n                if (ngx_http_set_virtual_server(r, &host) == NGX_ERROR) {\n                    break;\n                }\n\n                r->headers_in.server = host;\n            }\n\n            if (r->http_version < NGX_HTTP_VERSION_10) {\n\n                if (r->headers_in.server.len == 0\n                    && ngx_http_set_virtual_server(r, &r->headers_in.server)\n                       == NGX_ERROR)\n                {\n                    break;\n                }\n\n                ngx_http_process_request(r);\n                break;\n            }\n\n\n            if (ngx_list_init(&r->headers_in.headers, r->pool, 20,\n                              sizeof(ngx_table_elt_t))\n                != NGX_OK)\n            {\n                ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n                break;\n            }\n\n            c->log->action = \"reading client request headers\";\n\n            rev->handler = ngx_http_process_request_headers;\n            ngx_http_process_request_headers(rev);\n\n            break;\n        }\n\n        if (rc != NGX_AGAIN) {\n\n            /* there was error while a request line parsing */\n\n            ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                          ngx_http_client_errors[rc - NGX_HTTP_CLIENT_ERROR]);\n\n            if (rc == NGX_HTTP_PARSE_INVALID_VERSION) {\n                ngx_http_finalize_request(r, NGX_HTTP_VERSION_NOT_SUPPORTED);\n\n            } else {\n                ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);\n            }\n\n            break;\n        }\n\n        /* NGX_AGAIN: a request line parsing is still incomplete */\n\n        if (r->header_in->pos == r->header_in->end) {\n\n            rv = ngx_http_alloc_large_header_buffer(r, 1);\n\n            if (rv == NGX_ERROR) {\n                ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n                break;\n            }\n\n            if (rv == NGX_DECLINED) {\n                r->request_line.len = r->header_in->end - r->request_start;\n                r->request_line.data = r->request_start;\n\n                ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                              \"client sent too long URI\");\n                ngx_http_finalize_request(r, NGX_HTTP_REQUEST_URI_TOO_LARGE);\n                break;\n            }\n        }\n    }\n\n    ngx_http_run_posted_requests(c);\n}\n\n\nngx_int_t\nngx_http_process_request_uri(ngx_http_request_t *r)\n{\n    ngx_http_core_srv_conf_t  *cscf;\n\n    if (r->args_start) {\n        r->uri.len = r->args_start - 1 - r->uri_start;\n    } else {\n        r->uri.len = r->uri_end - r->uri_start;\n    }\n\n    if (r->complex_uri || r->quoted_uri || r->empty_path_in_uri) {\n\n        if (r->empty_path_in_uri) {\n            r->uri.len++;\n        }\n\n        r->uri.data = ngx_pnalloc(r->pool, r->uri.len);\n        if (r->uri.data == NULL) {\n            ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n            return NGX_ERROR;\n        }\n\n        cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);\n\n        if (ngx_http_parse_complex_uri(r, cscf->merge_slashes) != NGX_OK) {\n            r->uri.len = 0;\n\n            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                          \"client sent invalid request\");\n            ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);\n            return NGX_ERROR;\n        }\n\n    } else {\n        r->uri.data = r->uri_start;\n    }\n\n#if (T_NGX_VARS)\n    if (r->args_start) {\n        r->raw_uri.len = r->args_start - 1 - r->uri_start;\n\n    } else {\n        r->raw_uri.len = r->uri_end - r->uri_start;\n    }\n    r->raw_uri.data = r->uri_start;\n#endif\n\n    r->unparsed_uri.len = r->uri_end - r->uri_start;\n    r->unparsed_uri.data = r->uri_start;\n\n    r->valid_unparsed_uri = r->empty_path_in_uri ? 0 : 1;\n\n    if (r->uri_ext) {\n        if (r->args_start) {\n            r->exten.len = r->args_start - 1 - r->uri_ext;\n        } else {\n            r->exten.len = r->uri_end - r->uri_ext;\n        }\n\n        r->exten.data = r->uri_ext;\n    }\n\n    if (r->args_start && r->uri_end > r->args_start) {\n        r->args.len = r->uri_end - r->args_start;\n        r->args.data = r->args_start;\n    }\n\n#if (NGX_WIN32)\n    {\n    u_char  *p, *last;\n\n    p = r->uri.data;\n    last = r->uri.data + r->uri.len;\n\n    while (p < last) {\n\n        if (*p++ == ':') {\n\n            /*\n             * this check covers \"::$data\", \"::$index_allocation\" and\n             * \":$i30:$index_allocation\"\n             */\n\n            if (p < last && *p == '$') {\n                ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                              \"client sent unsafe win32 URI\");\n                ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);\n                return NGX_ERROR;\n            }\n        }\n    }\n\n    p = r->uri.data + r->uri.len - 1;\n\n    while (p > r->uri.data) {\n\n        if (*p == ' ') {\n            p--;\n            continue;\n        }\n\n        if (*p == '.') {\n            p--;\n            continue;\n        }\n\n        break;\n    }\n\n    if (p != r->uri.data + r->uri.len - 1) {\n        r->uri.len = p + 1 - r->uri.data;\n        ngx_http_set_exten(r);\n    }\n\n    }\n#endif\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http uri: \\\"%V\\\"\", &r->uri);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http args: \\\"%V\\\"\", &r->args);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http exten: \\\"%V\\\"\", &r->exten);\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_process_request_headers(ngx_event_t *rev)\n{\n    u_char                     *p;\n    size_t                      len;\n    ssize_t                     n;\n    ngx_int_t                   rc, rv;\n    ngx_table_elt_t            *h;\n    ngx_connection_t           *c;\n    ngx_http_header_t          *hh;\n    ngx_http_request_t         *r;\n    ngx_http_core_srv_conf_t   *cscf;\n    ngx_http_core_main_conf_t  *cmcf;\n\n    c = rev->data;\n    r = c->data;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0,\n                   \"http process request header line\");\n\n    if (rev->timedout) {\n        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, \"client timed out\");\n        c->timedout = 1;\n        ngx_http_close_request(r, NGX_HTTP_REQUEST_TIME_OUT);\n        return;\n    }\n\n    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);\n\n    rc = NGX_AGAIN;\n\n    for ( ;; ) {\n\n        if (rc == NGX_AGAIN) {\n\n            if (r->header_in->pos == r->header_in->end) {\n\n                rv = ngx_http_alloc_large_header_buffer(r, 0);\n\n                if (rv == NGX_ERROR) {\n                    ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n                    break;\n                }\n\n                if (rv == NGX_DECLINED) {\n                    p = r->header_name_start;\n\n                    r->lingering_close = 1;\n\n                    if (p == NULL) {\n                        ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                                      \"client sent too large request\");\n                        ngx_http_finalize_request(r,\n                                            NGX_HTTP_REQUEST_HEADER_TOO_LARGE);\n                        break;\n                    }\n\n                    len = r->header_in->end - p;\n\n                    if (len > NGX_MAX_ERROR_STR - 300) {\n                        len = NGX_MAX_ERROR_STR - 300;\n                    }\n\n                    ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                                \"client sent too long header line: \\\"%*s...\\\"\",\n                                len, r->header_name_start);\n\n                    ngx_http_finalize_request(r,\n                                            NGX_HTTP_REQUEST_HEADER_TOO_LARGE);\n                    break;\n                }\n            }\n\n            n = ngx_http_read_request_header(r);\n\n            if (n == NGX_AGAIN || n == NGX_ERROR) {\n                break;\n            }\n        }\n\n        /* the host header could change the server configuration context */\n        cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);\n\n        rc = ngx_http_parse_header_line(r, r->header_in,\n                                        cscf->underscores_in_headers);\n\n        if (rc == NGX_OK) {\n\n            r->request_length += r->header_in->pos - r->header_name_start;\n\n            if (r->invalid_header && cscf->ignore_invalid_headers) {\n\n                /* there was error while a header line parsing */\n\n                ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                              \"client sent invalid header line: \\\"%*s\\\"\",\n                              r->header_end - r->header_name_start,\n                              r->header_name_start);\n                continue;\n            }\n\n            /* a header line has been parsed successfully */\n\n            h = ngx_list_push(&r->headers_in.headers);\n            if (h == NULL) {\n                ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n                break;\n            }\n\n            h->hash = r->header_hash;\n\n            h->key.len = r->header_name_end - r->header_name_start;\n            h->key.data = r->header_name_start;\n            h->key.data[h->key.len] = '\\0';\n\n            h->value.len = r->header_end - r->header_start;\n            h->value.data = r->header_start;\n            h->value.data[h->value.len] = '\\0';\n\n            h->lowcase_key = ngx_pnalloc(r->pool, h->key.len);\n            if (h->lowcase_key == NULL) {\n                ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n                break;\n            }\n\n            if (h->key.len == r->lowcase_index) {\n                ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len);\n\n            } else {\n                ngx_strlow(h->lowcase_key, h->key.data, h->key.len);\n            }\n\n            hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash,\n                               h->lowcase_key, h->key.len);\n\n            if (hh && hh->handler(r, h, hh->offset) != NGX_OK) {\n                break;\n            }\n\n            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"http header: \\\"%V: %V\\\"\",\n                           &h->key, &h->value);\n\n            continue;\n        }\n\n        if (rc == NGX_HTTP_PARSE_HEADER_DONE) {\n\n            /* a whole header has been parsed successfully */\n\n            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"http header done\");\n\n            r->request_length += r->header_in->pos - r->header_name_start;\n\n            r->http_state = NGX_HTTP_PROCESS_REQUEST_STATE;\n\n            rc = ngx_http_process_request_header(r);\n\n            if (rc != NGX_OK) {\n                break;\n            }\n\n            ngx_http_process_request(r);\n\n            break;\n        }\n\n        if (rc == NGX_AGAIN) {\n\n            /* a header line parsing is still not complete */\n\n            continue;\n        }\n\n        /* rc == NGX_HTTP_PARSE_INVALID_HEADER */\n\n        ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                      \"client sent invalid header line: \\\"%*s\\\\x%02xd...\\\"\",\n                      r->header_end - r->header_name_start,\n                      r->header_name_start, *r->header_end);\n\n        ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);\n        break;\n    }\n\n    ngx_http_run_posted_requests(c);\n}\n\n\nstatic ssize_t\nngx_http_read_request_header(ngx_http_request_t *r)\n{\n    ssize_t                    n;\n    ngx_event_t               *rev;\n    ngx_connection_t          *c;\n    ngx_http_core_srv_conf_t  *cscf;\n\n    c = r->connection;\n    rev = c->read;\n\n    n = r->header_in->last - r->header_in->pos;\n\n    if (n > 0) {\n        return n;\n    }\n\n#if (NGX_HTTP_SSL && NGX_SSL_ASYNC)\n    if(c->async_enable)\n        n = c->recv(c, r->header_in->last,\n                    r->header_in->end - r->header_in->last);\n    else {\n#endif\n            if (rev->ready) {\n                n = c->recv(c, r->header_in->last,\n                            r->header_in->end - r->header_in->last);\n            } else {\n                 n = NGX_AGAIN;\n            }\n#if (NGX_HTTP_SSL && NGX_SSL_ASYNC)\n    }\n#endif\n\n    if (n == NGX_AGAIN) {\n        if (!rev->timer_set) {\n            cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);\n            ngx_add_timer(rev, cscf->client_header_timeout);\n        }\n\n        if (ngx_handle_read_event(rev, 0) != NGX_OK) {\n            ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n            return NGX_ERROR;\n        }\n\n        return NGX_AGAIN;\n    }\n\n    if (n == 0) {\n        ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                      \"client prematurely closed connection\");\n    }\n\n    if (n == 0 || n == NGX_ERROR) {\n        c->error = 1;\n        c->log->action = \"reading client request headers\";\n\n        ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);\n        return NGX_ERROR;\n    }\n\n    r->header_in->last += n;\n#if (T_NGX_REQ_STATUS)\n    c->received += n;\n#endif\n\n    return n;\n}\n\n\nstatic ngx_int_t\nngx_http_alloc_large_header_buffer(ngx_http_request_t *r,\n    ngx_uint_t request_line)\n{\n    u_char                    *old, *new;\n    ngx_buf_t                 *b;\n    ngx_chain_t               *cl;\n    ngx_http_connection_t     *hc;\n    ngx_http_core_srv_conf_t  *cscf;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http alloc large header buffer\");\n\n    if (request_line && r->state == 0) {\n\n        /* the client fills up the buffer with \"\\r\\n\" */\n\n        r->header_in->pos = r->header_in->start;\n        r->header_in->last = r->header_in->start;\n\n        return NGX_OK;\n    }\n\n    old = request_line ? r->request_start : r->header_name_start;\n\n    cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);\n\n    if (r->state != 0\n        && (size_t) (r->header_in->pos - old)\n                                     >= cscf->large_client_header_buffers.size)\n    {\n        return NGX_DECLINED;\n    }\n\n    hc = r->http_connection;\n\n    if (hc->free) {\n        cl = hc->free;\n        hc->free = cl->next;\n\n        b = cl->buf;\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"http large header free: %p %uz\",\n                       b->pos, b->end - b->last);\n\n    } else if (hc->nbusy < cscf->large_client_header_buffers.num) {\n\n        b = ngx_create_temp_buf(r->connection->pool,\n                                cscf->large_client_header_buffers.size);\n        if (b == NULL) {\n            return NGX_ERROR;\n        }\n\n        cl = ngx_alloc_chain_link(r->connection->pool);\n        if (cl == NULL) {\n            return NGX_ERROR;\n        }\n\n        cl->buf = b;\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"http large header alloc: %p %uz\",\n                       b->pos, b->end - b->last);\n\n    } else {\n        return NGX_DECLINED;\n    }\n\n    cl->next = hc->busy;\n    hc->busy = cl;\n    hc->nbusy++;\n\n    if (r->state == 0) {\n        /*\n         * r->state == 0 means that a header line was parsed successfully\n         * and we do not need to copy incomplete header line and\n         * to relocate the parser header pointers\n         */\n\n        r->header_in = b;\n\n        return NGX_OK;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http large header copy: %uz\", r->header_in->pos - old);\n\n    if (r->header_in->pos - old > b->end - b->start) {\n        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,\n                      \"too large header to copy\");\n        return NGX_ERROR;\n    }\n\n    new = b->start;\n\n    ngx_memcpy(new, old, r->header_in->pos - old);\n\n    b->pos = new + (r->header_in->pos - old);\n    b->last = new + (r->header_in->pos - old);\n\n    if (request_line) {\n        r->request_start = new;\n\n        if (r->request_end) {\n            r->request_end = new + (r->request_end - old);\n        }\n\n        r->method_end = new + (r->method_end - old);\n\n        r->uri_start = new + (r->uri_start - old);\n        r->uri_end = new + (r->uri_end - old);\n\n        if (r->schema_start) {\n            r->schema_start = new + (r->schema_start - old);\n            r->schema_end = new + (r->schema_end - old);\n        }\n\n#if (NGX_HTTP_PROXY_CONNECT)\n        if (r->connect_host_start) {\n            r->connect_host_start = new + (r->connect_host_start - old);\n            if (r->connect_host_end) {\n                r->connect_host_end = new + (r->connect_host_end - old);\n            }\n\n            if (r->connect_port_end) {\n                r->connect_port_end = new + (r->connect_port_end - old);\n            }\n        }\n#endif\n\n        if (r->host_start) {\n            r->host_start = new + (r->host_start - old);\n            if (r->host_end) {\n                r->host_end = new + (r->host_end - old);\n            }\n        }\n\n        if (r->port_start) {\n            r->port_start = new + (r->port_start - old);\n            r->port_end = new + (r->port_end - old);\n        }\n\n        if (r->uri_ext) {\n            r->uri_ext = new + (r->uri_ext - old);\n        }\n\n        if (r->args_start) {\n            r->args_start = new + (r->args_start - old);\n        }\n\n        if (r->http_protocol.data) {\n            r->http_protocol.data = new + (r->http_protocol.data - old);\n        }\n\n    } else {\n        r->header_name_start = new;\n        r->header_name_end = new + (r->header_name_end - old);\n        r->header_start = new + (r->header_start - old);\n        r->header_end = new + (r->header_end - old);\n    }\n\n    r->header_in = b;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_process_header_line(ngx_http_request_t *r, ngx_table_elt_t *h,\n    ngx_uint_t offset)\n{\n    ngx_table_elt_t  **ph;\n\n    ph = (ngx_table_elt_t **) ((char *) &r->headers_in + offset);\n\n    while (*ph) { ph = &(*ph)->next; }\n\n    *ph = h;\n    h->next = NULL;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_process_unique_header_line(ngx_http_request_t *r, ngx_table_elt_t *h,\n    ngx_uint_t offset)\n{\n    ngx_table_elt_t  **ph;\n\n    ph = (ngx_table_elt_t **) ((char *) &r->headers_in + offset);\n\n    if (*ph == NULL) {\n        *ph = h;\n        h->next = NULL;\n        return NGX_OK;\n    }\n\n    ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                  \"client sent duplicate header line: \\\"%V: %V\\\", \"\n                  \"previous value: \\\"%V: %V\\\"\",\n                  &h->key, &h->value, &(*ph)->key, &(*ph)->value);\n\n    ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);\n\n    return NGX_ERROR;\n}\n\n\nstatic ngx_int_t\nngx_http_process_host(ngx_http_request_t *r, ngx_table_elt_t *h,\n    ngx_uint_t offset)\n{\n    ngx_int_t  rc;\n    ngx_str_t  host;\n\n    if (r->headers_in.host) {\n        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                      \"client sent duplicate host header: \\\"%V: %V\\\", \"\n                      \"previous value: \\\"%V: %V\\\"\",\n                      &h->key, &h->value, &r->headers_in.host->key,\n                      &r->headers_in.host->value);\n        ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);\n        return NGX_ERROR;\n    }\n\n    r->headers_in.host = h;\n    h->next = NULL;\n\n    host = h->value;\n\n    rc = ngx_http_validate_host(&host, r->pool, 0);\n\n    if (rc == NGX_DECLINED) {\n        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                      \"client sent invalid host header\");\n        ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);\n        return NGX_ERROR;\n    }\n\n    if (rc == NGX_ERROR) {\n        ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return NGX_ERROR;\n    }\n\n    if (r->headers_in.server.len) {\n        return NGX_OK;\n    }\n\n    if (ngx_http_set_virtual_server(r, &host) == NGX_ERROR) {\n        return NGX_ERROR;\n    }\n\n    r->headers_in.server = host;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_process_connection(ngx_http_request_t *r, ngx_table_elt_t *h,\n    ngx_uint_t offset)\n{\n    if (ngx_http_process_header_line(r, h, offset) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    if (ngx_strcasestrn(h->value.data, \"close\", 5 - 1)) {\n        r->headers_in.connection_type = NGX_HTTP_CONNECTION_CLOSE;\n\n    } else if (ngx_strcasestrn(h->value.data, \"keep-alive\", 10 - 1)) {\n        r->headers_in.connection_type = NGX_HTTP_CONNECTION_KEEP_ALIVE;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_process_user_agent(ngx_http_request_t *r, ngx_table_elt_t *h,\n    ngx_uint_t offset)\n{\n    u_char  *user_agent, *msie;\n\n    if (ngx_http_process_header_line(r, h, offset) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    /* check some widespread browsers while the header is in CPU cache */\n\n    user_agent = h->value.data;\n\n    msie = ngx_strstrn(user_agent, \"MSIE \", 5 - 1);\n\n    if (msie && msie + 7 < user_agent + h->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#if 0\n        /* MSIE ignores the SSL \"close notify\" alert */\n        if (c->ssl) {\n            c->ssl->no_send_shutdown = 1;\n        }\n#endif\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_OK;\n}\n\n\nngx_int_t\nngx_http_process_request_header(ngx_http_request_t *r)\n{\n    if (r->headers_in.server.len == 0\n        && ngx_http_set_virtual_server(r, &r->headers_in.server)\n           == NGX_ERROR)\n    {\n        return NGX_ERROR;\n    }\n\n    if (r->headers_in.host == NULL && r->http_version > NGX_HTTP_VERSION_10) {\n        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                   \"client sent HTTP/1.1 request without \\\"Host\\\" header\");\n        ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);\n        return NGX_ERROR;\n    }\n\n    if (r->headers_in.content_length) {\n        r->headers_in.content_length_n =\n                            ngx_atoof(r->headers_in.content_length->value.data,\n                                      r->headers_in.content_length->value.len);\n\n        if (r->headers_in.content_length_n == NGX_ERROR) {\n            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                          \"client sent invalid \\\"Content-Length\\\" header\");\n            ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);\n            return NGX_ERROR;\n        }\n    }\n\n    if (r->headers_in.transfer_encoding) {\n        if (r->http_version < NGX_HTTP_VERSION_11) {\n            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                          \"client sent HTTP/1.0 request with \"\n                          \"\\\"Transfer-Encoding\\\" header\");\n            ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);\n            return NGX_ERROR;\n        }\n\n        if (r->headers_in.transfer_encoding->value.len == 7\n            && ngx_strncasecmp(r->headers_in.transfer_encoding->value.data,\n                               (u_char *) \"chunked\", 7) == 0)\n        {\n            if (r->headers_in.content_length) {\n                ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                              \"client sent \\\"Content-Length\\\" and \"\n                              \"\\\"Transfer-Encoding\\\" headers \"\n                              \"at the same time\");\n                ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);\n                return NGX_ERROR;\n            }\n\n            r->headers_in.chunked = 1;\n\n        } else {\n            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                          \"client sent unknown \\\"Transfer-Encoding\\\": \\\"%V\\\"\",\n                          &r->headers_in.transfer_encoding->value);\n            ngx_http_finalize_request(r, NGX_HTTP_NOT_IMPLEMENTED);\n            return NGX_ERROR;\n        }\n    }\n\n    if (r->headers_in.connection_type == NGX_HTTP_CONNECTION_KEEP_ALIVE) {\n        if (r->headers_in.keep_alive) {\n            r->headers_in.keep_alive_n =\n                            ngx_atotm(r->headers_in.keep_alive->value.data,\n                                      r->headers_in.keep_alive->value.len);\n        }\n    }\n\n#if !(NGX_HTTP_PROXY_CONNECT)\n    if (r->method == NGX_HTTP_CONNECT) {\n        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                      \"client sent CONNECT method\");\n        ngx_http_finalize_request(r, NGX_HTTP_NOT_ALLOWED);\n        return NGX_ERROR;\n    }\n#endif\n\n    if (r->method == NGX_HTTP_TRACE) {\n        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                      \"client sent TRACE method\");\n        ngx_http_finalize_request(r, NGX_HTTP_NOT_ALLOWED);\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nvoid\nngx_http_process_request(ngx_http_request_t *r)\n{\n    ngx_connection_t  *c;\n\n    c = r->connection;\n\n#if (NGX_HTTP_SSL && !T_NGX_HTTP_SSL_VCE)\n\n    if (r->http_connection->ssl) {\n        long                      rc;\n        X509                     *cert;\n        const char               *s;\n        ngx_http_ssl_srv_conf_t  *sscf;\n\n        if (c->ssl == NULL) {\n            ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                          \"client sent plain HTTP request to HTTPS port\");\n            ngx_http_finalize_request(r, NGX_HTTP_TO_HTTPS);\n            return;\n        }\n\n        sscf = ngx_http_get_module_srv_conf(r, ngx_http_ssl_module);\n\n        if (sscf->verify) {\n            rc = SSL_get_verify_result(c->ssl->connection);\n\n            if (rc != X509_V_OK\n                && (sscf->verify != 3 || !ngx_ssl_verify_error_optional(rc)))\n            {\n                ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                              \"client SSL certificate verify error: (%l:%s)\",\n                              rc, X509_verify_cert_error_string(rc));\n\n                ngx_ssl_remove_cached_session(c->ssl->session_ctx,\n                                       (SSL_get0_session(c->ssl->connection)));\n\n                ngx_http_finalize_request(r, NGX_HTTPS_CERT_ERROR);\n                return;\n            }\n\n            if (sscf->verify == 1) {\n                cert = SSL_get_peer_certificate(c->ssl->connection);\n\n                if (cert == NULL) {\n                    ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                                  \"client sent no required SSL certificate\");\n\n                    ngx_ssl_remove_cached_session(c->ssl->session_ctx,\n                                       (SSL_get0_session(c->ssl->connection)));\n\n                    ngx_http_finalize_request(r, NGX_HTTPS_NO_CERT);\n                    return;\n                }\n\n                X509_free(cert);\n            }\n\n            if (ngx_ssl_ocsp_get_status(c, &s) != NGX_OK) {\n                ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                              \"client SSL certificate verify error: %s\", s);\n\n                ngx_ssl_remove_cached_session(c->ssl->session_ctx,\n                                       (SSL_get0_session(c->ssl->connection)));\n\n                ngx_http_finalize_request(r, NGX_HTTPS_CERT_ERROR);\n                return;\n            }\n        }\n    }\n\n#endif\n\n    if (c->read->timer_set) {\n        ngx_del_timer(c->read);\n    }\n\n#if (NGX_STAT_STUB)\n    (void) ngx_atomic_fetch_add(ngx_stat_reading, -1);\n    r->stat_reading = 0;\n    (void) ngx_atomic_fetch_add(ngx_stat_writing, 1);\n    r->stat_writing = 1;\n#endif\n\n    c->read->handler = ngx_http_request_handler;\n    c->write->handler = ngx_http_request_handler;\n    r->read_event_handler = ngx_http_block_reading;\n\n    ngx_http_handler(r);\n}\n\n\nstatic ngx_int_t\nngx_http_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            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        default:\n\n            if (ngx_path_separator(ch)) {\n                return NGX_DECLINED;\n            }\n\n            if (ch <= 0x20 || ch == 0x7f) {\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_virtual_server(ngx_http_request_t *r, ngx_str_t *host)\n{\n    ngx_int_t                  rc;\n    ngx_http_connection_t     *hc;\n    ngx_http_core_loc_conf_t  *clcf;\n    ngx_http_core_srv_conf_t  *cscf;\n\n#if (NGX_SUPPRESS_WARN)\n    cscf = NULL;\n#endif\n\n    hc = r->http_connection;\n\n#if (NGX_HTTP_SSL && defined SSL_CTRL_SET_TLSEXT_HOSTNAME)\n\n    if (hc->ssl_servername) {\n        if (hc->ssl_servername->len == host->len\n            && ngx_strncmp(hc->ssl_servername->data,\n                           host->data, host->len) == 0)\n        {\n#if (NGX_PCRE)\n            if (hc->ssl_servername_regex\n                && ngx_http_regex_exec(r, hc->ssl_servername_regex,\n                                          hc->ssl_servername) != NGX_OK)\n            {\n                ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n                return NGX_ERROR;\n            }\n#endif\n            return NGX_OK;\n        }\n    }\n\n#endif\n\n    rc = ngx_http_find_virtual_server(r->connection,\n                                      hc->addr_conf->virtual_names,\n                                      host, r, &cscf);\n\n    if (rc == NGX_ERROR) {\n        ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return NGX_ERROR;\n    }\n\n#if (NGX_HTTP_SSL && defined SSL_CTRL_SET_TLSEXT_HOSTNAME)\n\n    if (hc->ssl_servername) {\n        ngx_http_ssl_srv_conf_t  *sscf;\n\n        if (rc == NGX_DECLINED) {\n            cscf = hc->addr_conf->default_server;\n            rc = NGX_OK;\n        }\n\n        sscf = ngx_http_get_module_srv_conf(cscf->ctx, ngx_http_ssl_module);\n\n        if (sscf->verify) {\n            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                          \"client attempted to request the server name \"\n                          \"different from the one that was negotiated\");\n            ngx_http_finalize_request(r, NGX_HTTP_MISDIRECTED_REQUEST);\n            return NGX_ERROR;\n        }\n    }\n\n#endif\n\n    if (rc == NGX_DECLINED) {\n        return NGX_OK;\n    }\n\n    r->srv_conf = cscf->ctx->srv_conf;\n    r->loc_conf = cscf->ctx->loc_conf;\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    ngx_set_connection_log(r->connection, clcf->error_log);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_find_virtual_server(ngx_connection_t *c,\n    ngx_http_virtual_names_t *virtual_names, ngx_str_t *host,\n    ngx_http_request_t *r, ngx_http_core_srv_conf_t **cscfp)\n{\n    ngx_http_core_srv_conf_t  *cscf;\n\n    if (virtual_names == NULL) {\n        return NGX_DECLINED;\n    }\n\n    cscf = ngx_hash_find_combined(&virtual_names->names,\n                                  ngx_hash_key(host->data, host->len),\n                                  host->data, host->len);\n\n    if (cscf) {\n        *cscfp = cscf;\n        return NGX_OK;\n    }\n\n#if (NGX_PCRE)\n\n    if (host->len && virtual_names->nregex) {\n        ngx_int_t                n;\n        ngx_uint_t               i;\n        ngx_http_server_name_t  *sn;\n\n        sn = virtual_names->regex;\n\n#if (NGX_HTTP_SSL && defined SSL_CTRL_SET_TLSEXT_HOSTNAME)\n\n        if (r == NULL) {\n            ngx_http_connection_t  *hc;\n\n            for (i = 0; i < virtual_names->nregex; i++) {\n\n                n = ngx_regex_exec(sn[i].regex->regex, host, NULL, 0);\n\n                if (n == NGX_REGEX_NO_MATCHED) {\n                    continue;\n                }\n\n                if (n >= 0) {\n                    hc = c->data;\n                    hc->ssl_servername_regex = sn[i].regex;\n\n                    *cscfp = sn[i].server;\n                    return NGX_OK;\n                }\n\n                ngx_log_error(NGX_LOG_ALERT, c->log, 0,\n                              ngx_regex_exec_n \" failed: %i \"\n                              \"on \\\"%V\\\" using \\\"%V\\\"\",\n                              n, host, &sn[i].regex->name);\n\n                return NGX_ERROR;\n            }\n\n            return NGX_DECLINED;\n        }\n\n#endif /* NGX_HTTP_SSL && defined SSL_CTRL_SET_TLSEXT_HOSTNAME */\n\n        for (i = 0; i < virtual_names->nregex; i++) {\n\n            n = ngx_http_regex_exec(r, sn[i].regex, host);\n\n            if (n == NGX_DECLINED) {\n                continue;\n            }\n\n            if (n == NGX_OK) {\n                *cscfp = sn[i].server;\n                return NGX_OK;\n            }\n\n            return NGX_ERROR;\n        }\n    }\n\n#endif /* NGX_PCRE */\n\n    return NGX_DECLINED;\n}\n\n\nstatic void\nngx_http_request_handler(ngx_event_t *ev)\n{\n    ngx_connection_t    *c;\n    ngx_http_request_t  *r;\n\n    c = ev->data;\n    r = c->data;\n\n    ngx_http_set_log_request(c->log, r);\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http run request: \\\"%V?%V\\\"\", &r->uri, &r->args);\n\n    if (c->close) {\n        r->main->count++;\n        ngx_http_terminate_request(r, 0);\n        ngx_http_run_posted_requests(c);\n        return;\n    }\n\n    if (ev->delayed && ev->timedout) {\n        ev->delayed = 0;\n        ev->timedout = 0;\n    }\n\n    if (ev->write) {\n        r->write_event_handler(r);\n\n    } else {\n        r->read_event_handler(r);\n    }\n\n    ngx_http_run_posted_requests(c);\n}\n\n\nvoid\nngx_http_run_posted_requests(ngx_connection_t *c)\n{\n    ngx_http_request_t         *r;\n    ngx_http_posted_request_t  *pr;\n\n    for ( ;; ) {\n\n        if (c->destroyed) {\n            return;\n        }\n\n        r = c->data;\n        pr = r->main->posted_requests;\n\n        if (pr == NULL) {\n            return;\n        }\n\n        r->main->posted_requests = pr->next;\n\n        r = pr->request;\n\n        ngx_http_set_log_request(c->log, r);\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"http posted request: \\\"%V?%V\\\"\", &r->uri, &r->args);\n\n        r->write_event_handler(r);\n    }\n}\n\n\nngx_int_t\nngx_http_post_request(ngx_http_request_t *r, ngx_http_posted_request_t *pr)\n{\n    ngx_http_posted_request_t  **p;\n\n    if (pr == NULL) {\n        pr = ngx_palloc(r->pool, sizeof(ngx_http_posted_request_t));\n        if (pr == NULL) {\n            return NGX_ERROR;\n        }\n    }\n\n    pr->request = r;\n    pr->next = NULL;\n\n    for (p = &r->main->posted_requests; *p; p = &(*p)->next) { /* void */ }\n\n    *p = pr;\n\n    return NGX_OK;\n}\n\n\nvoid\nngx_http_finalize_request(ngx_http_request_t *r, ngx_int_t rc)\n{\n    ngx_connection_t          *c;\n    ngx_http_request_t        *pr;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    c = r->connection;\n\n    ngx_log_debug5(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http finalize request: %i, \\\"%V?%V\\\" a:%d, c:%d\",\n                   rc, &r->uri, &r->args, r == c->data, r->main->count);\n\n    if (rc == NGX_DONE) {\n        ngx_http_finalize_connection(r);\n        return;\n    }\n\n    if (rc == NGX_OK && r->filter_finalize) {\n        c->error = 1;\n    }\n\n    if (rc == NGX_DECLINED) {\n        r->content_handler = NULL;\n        r->write_event_handler = ngx_http_core_run_phases;\n        ngx_http_core_run_phases(r);\n        return;\n    }\n\n    if (r != r->main && r->post_subrequest) {\n        rc = r->post_subrequest->handler(r, r->post_subrequest->data, rc);\n    }\n\n    if (rc == NGX_ERROR\n        || rc == NGX_HTTP_REQUEST_TIME_OUT\n        || rc == NGX_HTTP_CLIENT_CLOSED_REQUEST\n        || c->error)\n    {\n        if (ngx_http_post_action(r) == NGX_OK) {\n            return;\n        }\n\n        ngx_http_terminate_request(r, rc);\n        return;\n    }\n\n    if (rc >= NGX_HTTP_SPECIAL_RESPONSE\n        || rc == NGX_HTTP_CREATED\n        || rc == NGX_HTTP_NO_CONTENT)\n    {\n        if (rc == NGX_HTTP_CLOSE) {\n            c->timedout = 1;\n            ngx_http_terminate_request(r, rc);\n            return;\n        }\n\n        if (r == r->main) {\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\n        c->read->handler = ngx_http_request_handler;\n        c->write->handler = ngx_http_request_handler;\n\n        ngx_http_finalize_request(r, ngx_http_special_response_handler(r, rc));\n        return;\n    }\n\n    if (r != r->main) {\n\n        if (r->buffered || r->postponed) {\n\n            if (ngx_http_set_write_handler(r) != NGX_OK) {\n                ngx_http_terminate_request(r, 0);\n            }\n\n            return;\n        }\n\n        pr = r->parent;\n\n        if (r == c->data || r->background) {\n\n            if (!r->logged) {\n\n                clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n                if (clcf->log_subrequest) {\n                    ngx_http_log_request(r);\n                }\n\n                r->logged = 1;\n\n            } else {\n                ngx_log_error(NGX_LOG_ALERT, c->log, 0,\n                              \"subrequest: \\\"%V?%V\\\" logged again\",\n                              &r->uri, &r->args);\n            }\n\n            r->done = 1;\n\n            if (r->background) {\n                ngx_http_finalize_connection(r);\n                return;\n            }\n\n            r->main->count--;\n\n            if (pr->postponed && pr->postponed->request == r) {\n                pr->postponed = pr->postponed->next;\n            }\n\n            c->data = pr;\n\n        } else {\n\n            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                           \"http finalize non-active request: \\\"%V?%V\\\"\",\n                           &r->uri, &r->args);\n\n            r->write_event_handler = ngx_http_request_finalizer;\n\n            if (r->waited) {\n                r->done = 1;\n            }\n        }\n\n        if (ngx_http_post_request(pr, NULL) != NGX_OK) {\n            r->main->count++;\n            ngx_http_terminate_request(r, 0);\n            return;\n        }\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"http wake parent request: \\\"%V?%V\\\"\",\n                       &pr->uri, &pr->args);\n\n        return;\n    }\n\n    if (r->buffered || c->buffered || r->postponed) {\n\n        if (ngx_http_set_write_handler(r) != NGX_OK) {\n            ngx_http_terminate_request(r, 0);\n        }\n\n        return;\n    }\n\n    if (r != c->data) {\n        ngx_log_error(NGX_LOG_ALERT, c->log, 0,\n                      \"http finalize non-active request: \\\"%V?%V\\\"\",\n                      &r->uri, &r->args);\n        return;\n    }\n\n    r->done = 1;\n\n    r->read_event_handler = ngx_http_block_reading;\n    r->write_event_handler = ngx_http_request_empty_handler;\n\n    if (!r->post_action) {\n        r->request_complete = 1;\n    }\n\n    if (ngx_http_post_action(r) == NGX_OK) {\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_finalize_connection(r);\n}\n\n\nstatic void\nngx_http_terminate_request(ngx_http_request_t *r, ngx_int_t rc)\n{\n    ngx_http_cleanup_t    *cln;\n    ngx_http_request_t    *mr;\n    ngx_http_ephemeral_t  *e;\n\n    mr = r->main;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http terminate request count:%d\", mr->count);\n\n    if (rc > 0 && (mr->headers_out.status == 0 || mr->connection->sent == 0)) {\n        mr->headers_out.status = rc;\n    }\n\n    cln = mr->cleanup;\n    mr->cleanup = NULL;\n\n    while (cln) {\n        if (cln->handler) {\n            cln->handler(cln->data);\n        }\n\n        cln = cln->next;\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http terminate cleanup count:%d blk:%d\",\n                   mr->count, mr->blocked);\n\n    if (mr->write_event_handler) {\n\n        if (mr->blocked) {\n            r->connection->error = 1;\n            r->write_event_handler = ngx_http_request_finalizer;\n            return;\n        }\n\n        e = ngx_http_ephemeral(mr);\n        mr->posted_requests = NULL;\n        mr->write_event_handler = ngx_http_terminate_handler;\n        (void) ngx_http_post_request(mr, &e->terminal_posted_request);\n        return;\n    }\n\n    ngx_http_close_request(mr, rc);\n}\n\n\nstatic void\nngx_http_terminate_handler(ngx_http_request_t *r)\n{\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http terminate handler count:%d\", r->count);\n\n    r->count = 1;\n\n    ngx_http_close_request(r, 0);\n}\n\n\nstatic void\nngx_http_finalize_connection(ngx_http_request_t *r)\n{\n    ngx_http_core_loc_conf_t  *clcf;\n\n#if (NGX_HTTP_V2)\n    if (r->stream) {\n        ngx_http_close_request(r, 0);\n        return;\n    }\n#endif\n\n#if (T_NGX_XQUIC)\n    if (r->xqstream) {\n        ngx_http_close_request(r, 0);\n        return;\n    }\n#endif\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    if (r->main->count != 1) {\n\n        if (r->discard_body) {\n            r->read_event_handler = ngx_http_discarded_request_body_handler;\n            ngx_add_timer(r->connection->read, clcf->lingering_timeout);\n\n            if (r->lingering_time == 0) {\n                r->lingering_time = ngx_time()\n                                      + (time_t) (clcf->lingering_time / 1000);\n            }\n        }\n\n        ngx_http_close_request(r, 0);\n        return;\n    }\n\n    r = r->main;\n\n    if (r->connection->read->eof) {\n        ngx_http_close_request(r, 0);\n        return;\n    }\n\n    if (r->reading_body) {\n        r->keepalive = 0;\n        r->lingering_close = 1;\n    }\n\n    if (!ngx_terminate\n         && !ngx_exiting\n         && r->keepalive\n         && clcf->keepalive_timeout > 0)\n    {\n        ngx_http_set_keepalive(r);\n        return;\n    }\n\n    if (clcf->lingering_close == NGX_HTTP_LINGERING_ALWAYS\n        || (clcf->lingering_close == NGX_HTTP_LINGERING_ON\n            && (r->lingering_close\n                || r->header_in->pos < r->header_in->last\n                || r->connection->read->ready\n                || r->connection->pipeline)))\n    {\n        ngx_http_set_lingering_close(r->connection);\n        return;\n    }\n\n    ngx_http_close_request(r, 0);\n}\n\n\nstatic ngx_int_t\nngx_http_set_write_handler(ngx_http_request_t *r)\n{\n    ngx_event_t               *wev;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    r->http_state = NGX_HTTP_WRITING_REQUEST_STATE;\n\n    r->read_event_handler = r->discard_body ?\n                                ngx_http_discarded_request_body_handler:\n                                ngx_http_test_reading;\n    r->write_event_handler = ngx_http_writer;\n\n    wev = r->connection->write;\n\n    if (wev->ready && wev->delayed) {\n        return NGX_OK;\n    }\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\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        ngx_http_close_request(r, 0);\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_writer(ngx_http_request_t *r)\n{\n    ngx_int_t                  rc;\n    ngx_event_t               *wev;\n    ngx_connection_t          *c;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    c = r->connection;\n    wev = c->write;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, wev->log, 0,\n                   \"http writer handler: \\\"%V?%V\\\"\", &r->uri, &r->args);\n\n    clcf = ngx_http_get_module_loc_conf(r->main, ngx_http_core_module);\n\n    if (wev->timedout) {\n        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,\n                      \"client timed out\");\n        c->timedout = 1;\n\n        ngx_http_finalize_request(r, NGX_HTTP_REQUEST_TIME_OUT);\n        return;\n    }\n\n    if (wev->delayed || r->aio) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, wev->log, 0,\n                       \"http writer delayed\");\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            ngx_http_close_request(r, 0);\n        }\n\n        return;\n    }\n\n    rc = ngx_http_output_filter(r, NULL);\n\n    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http writer output filter: %i, \\\"%V?%V\\\"\",\n                   rc, &r->uri, &r->args);\n\n    if (rc == NGX_ERROR) {\n        ngx_http_finalize_request(r, rc);\n        return;\n    }\n\n    if (r->buffered || r->postponed || (r == r->main && c->buffered)) {\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            ngx_http_close_request(r, 0);\n        }\n\n        return;\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, wev->log, 0,\n                   \"http writer done: \\\"%V?%V\\\"\", &r->uri, &r->args);\n\n    r->write_event_handler = ngx_http_request_empty_handler;\n\n    ngx_http_finalize_request(r, rc);\n}\n\n\nstatic void\nngx_http_request_finalizer(ngx_http_request_t *r)\n{\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http finalizer done: \\\"%V?%V\\\"\", &r->uri, &r->args);\n\n    ngx_http_finalize_request(r, 0);\n}\n\n\nvoid\nngx_http_block_reading(ngx_http_request_t *r)\n{\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http reading blocked\");\n\n    /* aio does not call this handler */\n\n    if ((ngx_event_flags & NGX_USE_LEVEL_EVENT)\n        && r->connection->read->active)\n    {\n        if (ngx_del_event(r->connection->read, NGX_READ_EVENT, 0) != NGX_OK) {\n            ngx_http_close_request(r, 0);\n        }\n    }\n}\n\n\nvoid\nngx_http_test_reading(ngx_http_request_t *r)\n{\n    int                n;\n    char               buf[1];\n    ngx_err_t          err;\n    ngx_event_t       *rev;\n    ngx_connection_t  *c;\n\n    c = r->connection;\n    rev = c->read;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"http test reading\");\n\n#if (NGX_HTTP_V2)\n\n    if (r->stream) {\n        if (c->error) {\n            err = 0;\n            goto closed;\n        }\n\n        return;\n    }\n\n#endif\n\n#if (T_NGX_XQUIC)\n\n    if (r->xqstream) {\n        if (c->error) {\n            err = 0;\n            goto closed;\n        }\n\n        return;\n    }\n\n#endif\n\n#if (NGX_HAVE_KQUEUE)\n\n    if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {\n\n        if (!rev->pending_eof) {\n            return;\n        }\n\n        rev->eof = 1;\n        c->error = 1;\n        err = rev->kq_errno;\n\n        goto closed;\n    }\n\n#endif\n\n#if (NGX_HAVE_EPOLLRDHUP)\n\n    if ((ngx_event_flags & NGX_USE_EPOLL_EVENT) && ngx_use_epoll_rdhup) {\n        socklen_t  len;\n\n        if (!rev->pending_eof) {\n            return;\n        }\n\n        rev->eof = 1;\n        c->error = 1;\n\n        err = 0;\n        len = sizeof(ngx_err_t);\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_socket_errno;\n        }\n\n        goto closed;\n    }\n\n#endif\n\n    n = recv(c->fd, buf, 1, MSG_PEEK);\n\n    if (n == 0) {\n        rev->eof = 1;\n        c->error = 1;\n        err = 0;\n\n        goto closed;\n\n    } else if (n == -1) {\n        err = ngx_socket_errno;\n\n        if (err != NGX_EAGAIN) {\n            rev->eof = 1;\n            c->error = 1;\n\n            goto closed;\n        }\n    }\n\n    /* aio does not call this handler */\n\n    if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && rev->active) {\n\n        if (ngx_del_event(rev, NGX_READ_EVENT, 0) != NGX_OK) {\n            ngx_http_close_request(r, 0);\n        }\n    }\n\n    return;\n\nclosed:\n\n    if (err) {\n        rev->error = 1;\n    }\n\n#if (NGX_HTTP_SSL)\n    if (c->ssl) {\n        c->ssl->no_send_shutdown = 1;\n    }\n#endif\n\n    ngx_log_error(NGX_LOG_INFO, c->log, err,\n                  \"client prematurely closed connection\");\n\n    ngx_http_finalize_request(r, NGX_HTTP_CLIENT_CLOSED_REQUEST);\n}\n\n\nstatic void\nngx_http_set_keepalive(ngx_http_request_t *r)\n{\n    int                        tcp_nodelay;\n    ngx_buf_t                 *b, *f;\n    ngx_chain_t               *cl, *ln;\n    ngx_event_t               *rev, *wev;\n    ngx_connection_t          *c;\n    ngx_http_connection_t     *hc;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    c = r->connection;\n    rev = c->read;\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"set http keepalive handler\");\n\n    c->log->action = \"closing request\";\n\n    hc = r->http_connection;\n    b = r->header_in;\n\n    if (b->pos < b->last) {\n\n        /* the pipelined request */\n\n        if (b != c->buffer) {\n\n            /*\n             * If the large header buffers were allocated while the previous\n             * request processing then we do not use c->buffer for\n             * the pipelined request (see ngx_http_create_request()).\n             *\n             * Now we would move the large header buffers to the free list.\n             */\n\n            for (cl = hc->busy; cl; /* void */) {\n                ln = cl;\n                cl = cl->next;\n\n                if (ln->buf == b) {\n                    ngx_free_chain(c->pool, ln);\n                    continue;\n                }\n\n                f = ln->buf;\n                f->pos = f->start;\n                f->last = f->start;\n\n                ln->next = hc->free;\n                hc->free = ln;\n            }\n\n            cl = ngx_alloc_chain_link(c->pool);\n            if (cl == NULL) {\n                ngx_http_close_request(r, 0);\n                return;\n            }\n\n            cl->buf = b;\n            cl->next = NULL;\n\n            hc->busy = cl;\n            hc->nbusy = 1;\n        }\n    }\n\n    /* guard against recursive call from ngx_http_finalize_connection() */\n    r->keepalive = 0;\n\n    ngx_http_free_request(r, 0);\n\n    c->data = hc;\n\n    if (ngx_handle_read_event(rev, 0) != NGX_OK) {\n        ngx_http_close_connection(c);\n        return;\n    }\n\n    wev = c->write;\n    wev->handler = ngx_http_empty_handler;\n\n    if (b->pos < b->last) {\n\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"pipelined request\");\n\n        c->log->action = \"reading client pipelined request line\";\n\n        r = ngx_http_create_request(c);\n        if (r == NULL) {\n            ngx_http_close_connection(c);\n            return;\n        }\n\n        r->pipeline = 1;\n\n        c->data = r;\n\n        c->sent = 0;\n        c->destroyed = 0;\n        c->pipeline = 1;\n\n#if (T_NGX_REQ_STATUS)\n        /* bytes in the buffer have already been counted */\n        c->received = 0;\n#endif\n\n        if (rev->timer_set) {\n            ngx_del_timer(rev);\n        }\n\n        rev->handler = ngx_http_process_request_line;\n        ngx_post_event(rev, &ngx_posted_events);\n        return;\n    }\n\n    /*\n     * To keep a memory footprint as small as possible for an idle keepalive\n     * connection we try to free c->buffer's memory if it was allocated outside\n     * the c->pool.  The large header buffers are always allocated outside the\n     * c->pool and are freed too.\n     */\n\n#if (NGX_HTTP_SSL && NGX_SSL_ASYNC)\n    /* For the Async implementation we need the same buffer to be used\n     * again on any async calls that have not completed.\n     * As such we need to turn off this optimisation if an async request\n     * is still in progress.\n     */\n    if ((c->async_enable && !ngx_ssl_waiting_for_async(c)) || !c->async_enable)\n    {\n#endif\n    b = c->buffer;\n\n    if (ngx_pfree(c->pool, b->start) == NGX_OK) {\n\n        /*\n         * the special note for ngx_http_keepalive_handler() that\n         * c->buffer's memory was freed\n         */\n\n        b->pos = NULL;\n\n    } else {\n        b->pos = b->start;\n        b->last = b->start;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, \"hc free: %p\",\n                   hc->free);\n\n    if (hc->free) {\n        for (cl = hc->free; cl; /* void */) {\n            ln = cl;\n            cl = cl->next;\n            ngx_pfree(c->pool, ln->buf->start);\n            ngx_free_chain(c->pool, ln);\n        }\n\n        hc->free = NULL;\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, \"hc busy: %p %i\",\n                   hc->busy, hc->nbusy);\n\n    if (hc->busy) {\n        for (cl = hc->busy; cl; /* void */) {\n            ln = cl;\n            cl = cl->next;\n            ngx_pfree(c->pool, ln->buf->start);\n            ngx_free_chain(c->pool, ln);\n        }\n\n        hc->busy = NULL;\n        hc->nbusy = 0;\n    }\n#if (NGX_HTTP_SSL && NGX_SSL_ASYNC)\n    }\n#endif\n\n#if (NGX_HTTP_SSL)\n    if (c->ssl) {\n        ngx_ssl_free_buffer(c);\n    }\n#endif\n\n    rev->handler = ngx_http_keepalive_handler;\n\n    if (wev->active && (ngx_event_flags & NGX_USE_LEVEL_EVENT)) {\n#if (NGX_HTTP_SSL && NGX_SSL_ASYNC)\n        if (c->async_enable && ngx_del_async_conn) {\n            if (c->num_async_fds) {\n                ngx_del_async_conn(c, NGX_DISABLE_EVENT);\n                c->num_async_fds--;\n            }\n        }\n#endif\n        if (ngx_del_event(wev, NGX_WRITE_EVENT, 0) != NGX_OK) {\n            ngx_http_close_connection(c);\n            return;\n        }\n    }\n\n    c->log->action = \"keepalive\";\n\n    if (c->tcp_nopush == NGX_TCP_NOPUSH_SET) {\n        if (ngx_tcp_push(c->fd) == -1) {\n            ngx_connection_error(c, ngx_socket_errno, ngx_tcp_push_n \" failed\");\n            ngx_http_close_connection(c);\n            return;\n        }\n\n        c->tcp_nopush = NGX_TCP_NOPUSH_UNSET;\n        tcp_nodelay = ngx_tcp_nodelay_and_tcp_nopush ? 1 : 0;\n\n    } else {\n        tcp_nodelay = 1;\n    }\n\n    if (tcp_nodelay && clcf->tcp_nodelay && ngx_tcp_nodelay(c) != NGX_OK) {\n        ngx_http_close_connection(c);\n        return;\n    }\n\n#if 0\n    /* if ngx_http_request_t was freed then we need some other place */\n    r->http_state = NGX_HTTP_KEEPALIVE_STATE;\n#endif\n\n    c->idle = 1;\n    ngx_reusable_connection(c, 1);\n\n    ngx_add_timer(rev, clcf->keepalive_timeout);\n\n    if (rev->ready) {\n        ngx_post_event(rev, &ngx_posted_events);\n    }\n}\n\n\nstatic void\nngx_http_keepalive_handler(ngx_event_t *rev)\n{\n    size_t             size;\n    ssize_t            n;\n    ngx_buf_t         *b;\n    ngx_connection_t  *c;\n\n    c = rev->data;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"http keepalive handler\");\n\n    if (rev->timedout || c->close) {\n        ngx_http_close_connection(c);\n        return;\n    }\n\n#if (NGX_HAVE_KQUEUE)\n\n    if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {\n        if (rev->pending_eof) {\n            c->log->handler = NULL;\n            ngx_log_error(NGX_LOG_INFO, c->log, rev->kq_errno,\n                          \"kevent() reported that client %V closed \"\n                          \"keepalive connection\", &c->addr_text);\n#if (NGX_HTTP_SSL)\n            if (c->ssl) {\n                c->ssl->no_send_shutdown = 1;\n            }\n#endif\n            ngx_http_close_connection(c);\n            return;\n        }\n    }\n\n#endif\n\n    b = c->buffer;\n    size = b->end - b->start;\n\n    if (b->pos == NULL) {\n\n        /*\n         * The c->buffer's memory was freed by ngx_http_set_keepalive().\n         * However, the c->buffer->start and c->buffer->end were not changed\n         * to keep the buffer size.\n         */\n\n        b->pos = ngx_palloc(c->pool, size);\n        if (b->pos == NULL) {\n            ngx_http_close_connection(c);\n            return;\n        }\n\n        b->start = b->pos;\n        b->last = b->pos;\n        b->end = b->pos + size;\n    }\n\n    /*\n     * MSIE closes a keepalive connection with RST flag\n     * so we ignore ECONNRESET here.\n     */\n\n    c->log_error = NGX_ERROR_IGNORE_ECONNRESET;\n    ngx_set_socket_errno(0);\n\n    n = c->recv(c, b->last, size);\n    c->log_error = NGX_ERROR_INFO;\n\n    if (n == NGX_AGAIN) {\n        if (ngx_handle_read_event(rev, 0) != NGX_OK) {\n            ngx_http_close_connection(c);\n            return;\n        }\n\n        /*\n         * Like ngx_http_set_keepalive() we are trying to not hold\n         * c->buffer's memory for a keepalive connection.\n         */\n\n#if (NGX_HTTP_SSL && NGX_SSL_ASYNC)\n        /* For the Asynch implementation we need the same buffer to be used\n         * on subsequent read requests. As such we need to turn off this optimisation that\n         * frees the buffer between invocations as may end up with a buffer that is at a\n         * different address */\n        if ((c->async_enable && !ngx_ssl_waiting_for_async(c)) || !c->async_enable)\n        {\n#endif\n            if (ngx_pfree(c->pool, b->start) == NGX_OK) {\n\n                /*\n                 * the special note that c->buffer's memory was freed\n                 */\n\n                b->pos = NULL;\n            }\n#if (NGX_HTTP_SSL && NGX_SSL_ASYNC)\n        }\n#endif\n\n        return;\n    }\n\n    if (n == NGX_ERROR) {\n        ngx_http_close_connection(c);\n        return;\n    }\n\n    c->log->handler = NULL;\n\n    if (n == 0) {\n        ngx_log_error(NGX_LOG_INFO, c->log, ngx_socket_errno,\n                      \"client %V closed keepalive connection\", &c->addr_text);\n        ngx_http_close_connection(c);\n        return;\n    }\n\n    b->last += n;\n\n    c->log->handler = ngx_http_log_error;\n    c->log->action = \"reading client request line\";\n\n    c->idle = 0;\n    ngx_reusable_connection(c, 0);\n\n    c->data = ngx_http_create_request(c);\n    if (c->data == NULL) {\n        ngx_http_close_connection(c);\n        return;\n    }\n\n    c->sent = 0;\n    c->destroyed = 0;\n#if (T_NGX_REQ_STATUS)\n    c->received = n;\n#endif\n\n    ngx_del_timer(rev);\n\n    rev->handler = ngx_http_process_request_line;\n    ngx_http_process_request_line(rev);\n}\n\n\nstatic void\nngx_http_set_lingering_close(ngx_connection_t *c)\n{\n    ngx_event_t               *rev, *wev;\n    ngx_http_request_t        *r;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    r = c->data;\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    if (r->lingering_time == 0) {\n        r->lingering_time = ngx_time() + (time_t) (clcf->lingering_time / 1000);\n    }\n\n#if (NGX_HTTP_SSL)\n    if (c->ssl) {\n        ngx_int_t  rc;\n\n        c->ssl->shutdown_without_free = 1;\n\n        rc = ngx_ssl_shutdown(c);\n\n        if (rc == NGX_ERROR) {\n            ngx_http_close_request(r, 0);\n            return;\n        }\n\n        if (rc == NGX_AGAIN) {\n            c->ssl->handler = ngx_http_set_lingering_close;\n            return;\n        }\n    }\n#endif\n\n    rev = c->read;\n    rev->handler = ngx_http_lingering_close_handler;\n\n    if (ngx_handle_read_event(rev, 0) != NGX_OK) {\n        ngx_http_close_request(r, 0);\n        return;\n    }\n\n    wev = c->write;\n    wev->handler = ngx_http_empty_handler;\n\n    if (wev->active && (ngx_event_flags & NGX_USE_LEVEL_EVENT)) {\n#if (NGX_HTTP_SSL && NGX_SSL_ASYNC)\n        if (c->async_enable && ngx_del_async_conn) {\n            if (c->num_async_fds) {\n                ngx_del_async_conn(c, NGX_DISABLE_EVENT);\n                c->num_async_fds--;\n            }\n        }\n#endif\n        if (ngx_del_event(wev, NGX_WRITE_EVENT, 0) != NGX_OK) {\n            ngx_http_close_request(r, 0);\n            return;\n        }\n    }\n\n    if (ngx_shutdown_socket(c->fd, NGX_WRITE_SHUTDOWN) == -1) {\n        ngx_connection_error(c, ngx_socket_errno,\n                             ngx_shutdown_socket_n \" failed\");\n        ngx_http_close_request(r, 0);\n        return;\n    }\n\n    c->close = 0;\n    ngx_reusable_connection(c, 1);\n\n    ngx_add_timer(rev, clcf->lingering_timeout);\n\n    if (rev->ready) {\n        ngx_http_lingering_close_handler(rev);\n    }\n}\n\n\nstatic void\nngx_http_lingering_close_handler(ngx_event_t *rev)\n{\n    ssize_t                    n;\n    ngx_msec_t                 timer;\n    ngx_connection_t          *c;\n    ngx_http_request_t        *r;\n    ngx_http_core_loc_conf_t  *clcf;\n    u_char                     buffer[NGX_HTTP_LINGERING_BUFFER_SIZE];\n\n    c = rev->data;\n    r = c->data;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http lingering close handler\");\n\n    if (rev->timedout || c->close) {\n        ngx_http_close_request(r, 0);\n        return;\n    }\n\n    timer = (ngx_msec_t) r->lingering_time - (ngx_msec_t) ngx_time();\n    if ((ngx_msec_int_t) timer <= 0) {\n        ngx_http_close_request(r, 0);\n        return;\n    }\n\n    do {\n        n = c->recv(c, buffer, NGX_HTTP_LINGERING_BUFFER_SIZE);\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, \"lingering read: %z\", n);\n\n        if (n == NGX_AGAIN) {\n            break;\n        }\n\n        if (n == NGX_ERROR || n == 0) {\n            ngx_http_close_request(r, 0);\n            return;\n        }\n\n#if (T_NGX_REQ_STATUS)\n        c->received += n;\n#endif\n\n    } while (rev->ready);\n\n    if (ngx_handle_read_event(rev, 0) != NGX_OK) {\n        ngx_http_close_request(r, 0);\n        return;\n    }\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    timer *= 1000;\n\n    if (timer > clcf->lingering_timeout) {\n        timer = clcf->lingering_timeout;\n    }\n\n    ngx_add_timer(rev, timer);\n}\n\n\nvoid\nngx_http_empty_handler(ngx_event_t *wev)\n{\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, wev->log, 0, \"http empty handler\");\n\n    return;\n}\n\n\nvoid\nngx_http_request_empty_handler(ngx_http_request_t *r)\n{\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http request empty handler\");\n\n    return;\n}\n\n\nngx_int_t\nngx_http_send_special(ngx_http_request_t *r, ngx_uint_t flags)\n{\n    ngx_buf_t    *b;\n    ngx_chain_t   out;\n\n    b = ngx_calloc_buf(r->pool);\n    if (b == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (flags & NGX_HTTP_LAST) {\n\n        if (r == r->main && !r->post_action) {\n            b->last_buf = 1;\n\n        } else {\n            b->sync = 1;\n            b->last_in_chain = 1;\n        }\n    }\n\n    if (flags & NGX_HTTP_FLUSH) {\n        b->flush = 1;\n    }\n\n    out.buf = b;\n    out.next = NULL;\n\n    return ngx_http_output_filter(r, &out);\n}\n\n\nstatic ngx_int_t\nngx_http_post_action(ngx_http_request_t *r)\n{\n    ngx_http_core_loc_conf_t  *clcf;\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    if (clcf->post_action.data == NULL) {\n        return NGX_DECLINED;\n    }\n\n    if (r->post_action && r->uri_changes == 0) {\n        return NGX_DECLINED;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"post action: \\\"%V\\\"\", &clcf->post_action);\n\n    r->main->count--;\n\n    r->http_version = NGX_HTTP_VERSION_9;\n    r->header_only = 1;\n    r->post_action = 1;\n\n    r->read_event_handler = ngx_http_block_reading;\n\n    if (clcf->post_action.data[0] == '/') {\n        ngx_http_internal_redirect(r, &clcf->post_action, NULL);\n\n    } else {\n        ngx_http_named_location(r, &clcf->post_action);\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_close_request(ngx_http_request_t *r, ngx_int_t rc)\n{\n    ngx_connection_t  *c;\n\n    r = r->main;\n    c = r->connection;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http request count:%d blk:%d\", r->count, r->blocked);\n\n    if (r->count == 0) {\n        ngx_log_error(NGX_LOG_ALERT, c->log, 0, \"http request count is zero\");\n    }\n\n    r->count--;\n\n    if (r->count || r->blocked) {\n        return;\n    }\n\n#if (NGX_HTTP_V2)\n    if (r->stream) {\n        ngx_http_v2_close_stream(r->stream, rc);\n        return;\n    }\n#endif\n\n#if (T_NGX_XQUIC)\n    if (r->xqstream) {\n        ngx_http_v3_close_stream(r->xqstream, rc);\n        return;\n    }\n#endif\n\n    ngx_http_free_request(r, rc);\n    ngx_http_close_connection(c);\n}\n\n\nvoid\nngx_http_free_request(ngx_http_request_t *r, ngx_int_t rc)\n{\n    ngx_log_t                 *log;\n    ngx_pool_t                *pool;\n    struct linger              linger;\n    ngx_http_cleanup_t        *cln;\n    ngx_http_log_ctx_t        *ctx;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    log = r->connection->log;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, log, 0, \"http close request\");\n\n    if (r->pool == NULL) {\n        ngx_log_error(NGX_LOG_ALERT, log, 0, \"http request 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#if (NGX_STAT_STUB)\n\n    if (r->stat_reading) {\n        (void) ngx_atomic_fetch_add(ngx_stat_reading, -1);\n    }\n\n    if (r->stat_writing) {\n        (void) ngx_atomic_fetch_add(ngx_stat_writing, -1);\n    }\n\n#endif\n\n    if (rc > 0 && (r->headers_out.status == 0 || r->connection->sent == 0)) {\n        r->headers_out.status = rc;\n    }\n\n    if (!r->logged) {\n        log->action = \"logging request\";\n\n        ngx_http_log_request(r);\n    }\n\n    log->action = \"closing request\";\n\n    if (r->connection->timedout) {\n        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n        if (clcf->reset_timedout_connection) {\n            linger.l_onoff = 1;\n            linger.l_linger = 0;\n\n            if (setsockopt(r->connection->fd, SOL_SOCKET, SO_LINGER,\n                           (const void *) &linger, sizeof(struct linger)) == -1)\n            {\n                ngx_log_error(NGX_LOG_ALERT, log, ngx_socket_errno,\n                              \"setsockopt(SO_LINGER) failed\");\n            }\n        }\n    }\n\n    /* the various request strings were allocated from r->pool */\n    ctx = log->data;\n    ctx->request = NULL;\n\n    r->request_line.len = 0;\n\n    r->connection->destroyed = 1;\n\n    /*\n     * Setting r->pool to NULL will increase probability to catch double close\n     * of request since the request object is allocated from its own pool.\n     */\n\n    pool = r->pool;\n    r->pool = NULL;\n\n    ngx_destroy_pool(pool);\n}\n\n\nstatic void\nngx_http_log_request(ngx_http_request_t *r)\n{\n    ngx_uint_t                  i, n;\n    ngx_http_handler_pt        *log_handler;\n    ngx_http_core_main_conf_t  *cmcf;\n\n    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);\n\n    log_handler = cmcf->phases[NGX_HTTP_LOG_PHASE].handlers.elts;\n    n = cmcf->phases[NGX_HTTP_LOG_PHASE].handlers.nelts;\n\n    for (i = 0; i < n; i++) {\n        log_handler[i](r);\n    }\n}\n\n\nvoid\nngx_http_close_connection(ngx_connection_t *c)\n{\n    ngx_pool_t  *pool;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"close http connection: %d\", c->fd);\n\n#if (NGX_HTTP_SSL)\n\n    if (c->ssl) {\n        if (ngx_ssl_shutdown(c) == NGX_AGAIN) {\n            c->ssl->handler = ngx_http_close_connection;\n            return;\n        }\n    }\n\n#endif\n\n#if (NGX_STAT_STUB)\n    (void) ngx_atomic_fetch_add(ngx_stat_active, -1);\n#endif\n\n    c->destroyed = 1;\n\n    pool = c->pool;\n\n    ngx_close_connection(c);\n\n    ngx_destroy_pool(pool);\n}\n\n\nstatic u_char *\nngx_http_log_error(ngx_log_t *log, u_char *buf, size_t len)\n{\n    u_char              *p;\n    ngx_http_request_t  *r;\n    ngx_http_log_ctx_t  *ctx;\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    ctx = log->data;\n\n    p = ngx_snprintf(buf, len, \", client: %V\", &ctx->connection->addr_text);\n    len -= p - buf;\n\n    r = ctx->request;\n\n    if (r) {\n        return r->log_handler(r, ctx->current_request, p, len);\n\n    } else {\n        p = ngx_snprintf(p, len, \", server: %V\",\n                         &ctx->connection->listening->addr_text);\n    }\n\n    return p;\n}\n\n\nstatic u_char *\nngx_http_log_error_handler(ngx_http_request_t *r, ngx_http_request_t *sr,\n    u_char *buf, size_t len)\n{\n    char                      *uri_separator;\n    u_char                    *p;\n    ngx_http_upstream_t       *u;\n    ngx_http_core_srv_conf_t  *cscf;\n\n    cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);\n\n    p = ngx_snprintf(buf, len, \", server: %V\", &cscf->server_name);\n    len -= p - buf;\n    buf = p;\n\n    if (r->request_line.data == NULL && r->request_start) {\n        for (p = r->request_start; p < r->header_in->last; p++) {\n            if (*p == CR || *p == LF) {\n                break;\n            }\n        }\n\n        r->request_line.len = p - r->request_start;\n        r->request_line.data = r->request_start;\n    }\n\n    if (r->request_line.len) {\n        p = ngx_snprintf(buf, len, \", request: \\\"%V\\\"\", &r->request_line);\n        len -= p - buf;\n        buf = p;\n    }\n\n    if (r != sr) {\n        p = ngx_snprintf(buf, len, \", subrequest: \\\"%V\\\"\", &sr->uri);\n        len -= p - buf;\n        buf = p;\n    }\n\n    u = sr->upstream;\n\n    if (u && u->peer.name) {\n\n        uri_separator = \"\";\n\n#if (NGX_HAVE_UNIX_DOMAIN)\n        if (u->peer.sockaddr && u->peer.sockaddr->sa_family == AF_UNIX) {\n            uri_separator = \":\";\n        }\n#endif\n\n        p = ngx_snprintf(buf, len, \", upstream: \\\"%V%V%s%V\\\"\",\n                         &u->schema, u->peer.name,\n                         uri_separator, &u->uri);\n        len -= p - buf;\n        buf = p;\n    }\n\n    if (r->headers_in.host) {\n        p = ngx_snprintf(buf, len, \", host: \\\"%V\\\"\",\n                         &r->headers_in.host->value);\n        len -= p - buf;\n        buf = p;\n    }\n\n    if (r->headers_in.referer) {\n        p = ngx_snprintf(buf, len, \", referrer: \\\"%V\\\"\",\n                         &r->headers_in.referer->value);\n        buf = p;\n    }\n\n    return buf;\n}\n\n#if (T_NGX_XQUIC)\n\nngx_int_t\nngx_http_find_virtual_server_inner(ngx_connection_t *c,\n    ngx_http_virtual_names_t *virtual_names, ngx_str_t *host,\n    ngx_http_request_t *r, ngx_http_core_srv_conf_t **cscfp)\n{\n    return ngx_http_find_virtual_server(c, virtual_names, host, r, cscfp);\n}\n\n#endif\n"
  },
  {
    "path": "src/http/ngx_http_request.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_HTTP_REQUEST_H_INCLUDED_\n#define _NGX_HTTP_REQUEST_H_INCLUDED_\n\n\n#define NGX_HTTP_MAX_URI_CHANGES           10\n#define NGX_HTTP_MAX_SUBREQUESTS           50\n\n/* must be 2^n */\n#define NGX_HTTP_LC_HEADER_LEN             32\n\n\n#define NGX_HTTP_DISCARD_BUFFER_SIZE       4096\n#define NGX_HTTP_LINGERING_BUFFER_SIZE     4096\n\n\n#define NGX_HTTP_VERSION_9                 9\n#define NGX_HTTP_VERSION_10                1000\n#define NGX_HTTP_VERSION_11                1001\n#define NGX_HTTP_VERSION_20                2000\n#if (T_NGX_XQUIC)\n#define NGX_HTTP_VERSION_30                3000\n#endif\n\n#define NGX_HTTP_UNKNOWN                   0x00000001\n#define NGX_HTTP_GET                       0x00000002\n#define NGX_HTTP_HEAD                      0x00000004\n#define NGX_HTTP_POST                      0x00000008\n#define NGX_HTTP_PUT                       0x00000010\n#define NGX_HTTP_DELETE                    0x00000020\n#define NGX_HTTP_MKCOL                     0x00000040\n#define NGX_HTTP_COPY                      0x00000080\n#define NGX_HTTP_MOVE                      0x00000100\n#define NGX_HTTP_OPTIONS                   0x00000200\n#define NGX_HTTP_PROPFIND                  0x00000400\n#define NGX_HTTP_PROPPATCH                 0x00000800\n#define NGX_HTTP_LOCK                      0x00001000\n#define NGX_HTTP_UNLOCK                    0x00002000\n#define NGX_HTTP_PATCH                     0x00004000\n#define NGX_HTTP_TRACE                     0x00008000\n#define NGX_HTTP_CONNECT                   0x00010000\n\n#define NGX_HTTP_CONNECTION_CLOSE          1\n#define NGX_HTTP_CONNECTION_KEEP_ALIVE     2\n\n\n#define NGX_NONE                           1\n\n\n#define NGX_HTTP_PARSE_HEADER_DONE         1\n\n#define NGX_HTTP_CLIENT_ERROR              10\n#define NGX_HTTP_PARSE_INVALID_METHOD      10\n#define NGX_HTTP_PARSE_INVALID_REQUEST     11\n#define NGX_HTTP_PARSE_INVALID_VERSION     12\n#define NGX_HTTP_PARSE_INVALID_09_METHOD   13\n\n#define NGX_HTTP_PARSE_INVALID_HEADER      14\n\n\n/* unused                                  1 */\n#define NGX_HTTP_SUBREQUEST_IN_MEMORY      2\n#define NGX_HTTP_SUBREQUEST_WAITED         4\n#define NGX_HTTP_SUBREQUEST_CLONE          8\n#define NGX_HTTP_SUBREQUEST_BACKGROUND     16\n\n#define NGX_HTTP_LOG_UNSAFE                1\n\n\n#define NGX_HTTP_CONTINUE                  100\n#define NGX_HTTP_SWITCHING_PROTOCOLS       101\n#define NGX_HTTP_PROCESSING                102\n\n#define NGX_HTTP_OK                        200\n#define NGX_HTTP_CREATED                   201\n#define NGX_HTTP_ACCEPTED                  202\n#define NGX_HTTP_NO_CONTENT                204\n#define NGX_HTTP_PARTIAL_CONTENT           206\n\n#define NGX_HTTP_SPECIAL_RESPONSE          300\n#define NGX_HTTP_MOVED_PERMANENTLY         301\n#define NGX_HTTP_MOVED_TEMPORARILY         302\n#define NGX_HTTP_SEE_OTHER                 303\n#define NGX_HTTP_NOT_MODIFIED              304\n#define NGX_HTTP_TEMPORARY_REDIRECT        307\n#define NGX_HTTP_PERMANENT_REDIRECT        308\n\n#define NGX_HTTP_BAD_REQUEST               400\n#define NGX_HTTP_UNAUTHORIZED              401\n#define NGX_HTTP_FORBIDDEN                 403\n#define NGX_HTTP_NOT_FOUND                 404\n#define NGX_HTTP_NOT_ALLOWED               405\n#define NGX_HTTP_REQUEST_TIME_OUT          408\n#define NGX_HTTP_CONFLICT                  409\n#define NGX_HTTP_LENGTH_REQUIRED           411\n#define NGX_HTTP_PRECONDITION_FAILED       412\n#define NGX_HTTP_REQUEST_ENTITY_TOO_LARGE  413\n#define NGX_HTTP_REQUEST_URI_TOO_LARGE     414\n#define NGX_HTTP_UNSUPPORTED_MEDIA_TYPE    415\n#define NGX_HTTP_RANGE_NOT_SATISFIABLE     416\n#define NGX_HTTP_REQUEST_LIMITED           420\n#define NGX_HTTP_MISDIRECTED_REQUEST       421\n#define NGX_HTTP_TOO_MANY_REQUESTS         429\n\n\n/* Our own HTTP codes */\n\n/* The special code to close connection without any response */\n#define NGX_HTTP_CLOSE                     444\n\n#define NGX_HTTP_NGINX_CODES               494\n\n#define NGX_HTTP_REQUEST_HEADER_TOO_LARGE  494\n\n#define NGX_HTTPS_CERT_ERROR               495\n#define NGX_HTTPS_NO_CERT                  496\n\n/*\n * We use the special code for the plain HTTP requests that are sent to\n * HTTPS port to distinguish it from 4XX in an error page redirection\n */\n#define NGX_HTTP_TO_HTTPS                  497\n\n/* 498 is the canceled code for the requests with invalid host name */\n\n/*\n * HTTP does not define the code for the case when a client closed\n * the connection while we are processing its request so we introduce\n * own code to log such situation when a client has closed the connection\n * before we even try to send the HTTP header to it\n */\n#define NGX_HTTP_CLIENT_CLOSED_REQUEST     499\n\n\n#define NGX_HTTP_INTERNAL_SERVER_ERROR     500\n#define NGX_HTTP_NOT_IMPLEMENTED           501\n#define NGX_HTTP_BAD_GATEWAY               502\n#define NGX_HTTP_SERVICE_UNAVAILABLE       503\n#define NGX_HTTP_GATEWAY_TIME_OUT          504\n#define NGX_HTTP_VERSION_NOT_SUPPORTED     505\n#define NGX_HTTP_INSUFFICIENT_STORAGE      507\n\n\n#define NGX_HTTP_LOWLEVEL_BUFFERED         0xf0\n#define NGX_HTTP_WRITE_BUFFERED            0x10\n#define NGX_HTTP_GZIP_BUFFERED             0x20\n#define NGX_HTTP_SSI_BUFFERED              0x01\n#define NGX_HTTP_SUB_BUFFERED              0x02\n#define NGX_HTTP_COPY_BUFFERED             0x04\n\n\ntypedef enum {\n    NGX_HTTP_INITING_REQUEST_STATE = 0,\n    NGX_HTTP_READING_REQUEST_STATE,\n    NGX_HTTP_PROCESS_REQUEST_STATE,\n\n    NGX_HTTP_CONNECT_UPSTREAM_STATE,\n    NGX_HTTP_WRITING_UPSTREAM_STATE,\n    NGX_HTTP_READING_UPSTREAM_STATE,\n\n    NGX_HTTP_WRITING_REQUEST_STATE,\n    NGX_HTTP_LINGERING_CLOSE_STATE,\n    NGX_HTTP_KEEPALIVE_STATE\n} ngx_http_state_e;\n\n\ntypedef struct {\n    ngx_str_t                         name;\n    ngx_uint_t                        offset;\n    ngx_http_header_handler_pt        handler;\n} ngx_http_header_t;\n\n\ntypedef struct {\n    ngx_str_t                         name;\n    ngx_uint_t                        offset;\n} ngx_http_header_out_t;\n\n\ntypedef struct {\n    ngx_list_t                        headers;\n\n    ngx_table_elt_t                  *host;\n    ngx_table_elt_t                  *connection;\n    ngx_table_elt_t                  *if_modified_since;\n    ngx_table_elt_t                  *if_unmodified_since;\n    ngx_table_elt_t                  *if_match;\n    ngx_table_elt_t                  *if_none_match;\n    ngx_table_elt_t                  *user_agent;\n    ngx_table_elt_t                  *referer;\n    ngx_table_elt_t                  *content_length;\n    ngx_table_elt_t                  *content_range;\n    ngx_table_elt_t                  *content_type;\n\n    ngx_table_elt_t                  *range;\n    ngx_table_elt_t                  *if_range;\n\n    ngx_table_elt_t                  *transfer_encoding;\n    ngx_table_elt_t                  *te;\n    ngx_table_elt_t                  *expect;\n    ngx_table_elt_t                  *upgrade;\n\n#if (NGX_HTTP_GZIP || NGX_HTTP_HEADERS)\n    ngx_table_elt_t                  *accept_encoding;\n    ngx_table_elt_t                  *via;\n#endif\n\n    ngx_table_elt_t                  *authorization;\n\n    ngx_table_elt_t                  *keep_alive;\n\n#if (NGX_HTTP_X_FORWARDED_FOR)\n    ngx_table_elt_t                  *x_forwarded_for;\n#endif\n\n#if (NGX_HTTP_REALIP)\n    ngx_table_elt_t                  *x_real_ip;\n#endif\n\n#if (NGX_HTTP_HEADERS)\n    ngx_table_elt_t                  *accept;\n    ngx_table_elt_t                  *accept_language;\n#endif\n\n#if (NGX_HTTP_DAV)\n    ngx_table_elt_t                  *depth;\n    ngx_table_elt_t                  *destination;\n    ngx_table_elt_t                  *overwrite;\n    ngx_table_elt_t                  *date;\n#endif\n\n    ngx_table_elt_t                  *cookie;\n\n    ngx_str_t                         user;\n    ngx_str_t                         passwd;\n\n    ngx_str_t                         server;\n    off_t                             content_length_n;\n    time_t                            keep_alive_n;\n\n    unsigned                          connection_type:2;\n    unsigned                          chunked:1;\n    unsigned                          multi:1;\n    unsigned                          multi_linked:1;\n    unsigned                          msie:1;\n    unsigned                          msie6:1;\n    unsigned                          opera:1;\n    unsigned                          gecko:1;\n    unsigned                          chrome:1;\n    unsigned                          safari:1;\n    unsigned                          konqueror:1;\n} ngx_http_headers_in_t;\n\n\ntypedef struct {\n    ngx_list_t                        headers;\n    ngx_list_t                        trailers;\n\n    ngx_uint_t                        status;\n    ngx_str_t                         status_line;\n\n    ngx_table_elt_t                  *server;\n    ngx_table_elt_t                  *date;\n    ngx_table_elt_t                  *content_length;\n    ngx_table_elt_t                  *content_encoding;\n    ngx_table_elt_t                  *location;\n    ngx_table_elt_t                  *refresh;\n    ngx_table_elt_t                  *last_modified;\n    ngx_table_elt_t                  *content_range;\n    ngx_table_elt_t                  *accept_ranges;\n    ngx_table_elt_t                  *www_authenticate;\n    ngx_table_elt_t                  *expires;\n    ngx_table_elt_t                  *etag;\n\n    ngx_table_elt_t                  *cache_control;\n    ngx_table_elt_t                  *link;\n\n    ngx_str_t                        *override_charset;\n\n    size_t                            content_type_len;\n    ngx_str_t                         content_type;\n    ngx_str_t                         charset;\n    u_char                           *content_type_lowcase;\n    ngx_uint_t                        content_type_hash;\n\n    off_t                             content_length_n;\n    off_t                             content_offset;\n    time_t                            date_time;\n    time_t                            last_modified_time;\n} ngx_http_headers_out_t;\n\n\ntypedef void (*ngx_http_client_body_handler_pt)(ngx_http_request_t *r);\n\ntypedef struct {\n    ngx_temp_file_t                  *temp_file;\n    ngx_chain_t                      *bufs;\n    ngx_buf_t                        *buf;\n    off_t                             rest;\n    off_t                             received;\n    ngx_chain_t                      *free;\n    ngx_chain_t                      *busy;\n    ngx_http_chunked_t               *chunked;\n    ngx_http_client_body_handler_pt   post_handler;\n    unsigned                          filter_need_buffering:1;\n    unsigned                          last_sent:1;\n    unsigned                          last_saved:1;\n} ngx_http_request_body_t;\n\n\ntypedef struct ngx_http_addr_conf_s  ngx_http_addr_conf_t;\n\ntypedef struct {\n    ngx_http_addr_conf_t             *addr_conf;\n    ngx_http_conf_ctx_t              *conf_ctx;\n\n#if (NGX_HTTP_SSL || NGX_COMPAT)\n    ngx_str_t                        *ssl_servername;\n#if (NGX_PCRE)\n    ngx_http_regex_t                 *ssl_servername_regex;\n#endif\n#endif\n\n    ngx_chain_t                      *busy;\n    ngx_int_t                         nbusy;\n\n    ngx_chain_t                      *free;\n\n    unsigned                          ssl:1;\n    unsigned                          proxy_protocol:1;\n} ngx_http_connection_t;\n\n\ntypedef void (*ngx_http_cleanup_pt)(void *data);\n\ntypedef struct ngx_http_cleanup_s  ngx_http_cleanup_t;\n\nstruct ngx_http_cleanup_s {\n    ngx_http_cleanup_pt               handler;\n    void                             *data;\n    ngx_http_cleanup_t               *next;\n};\n\n\ntypedef ngx_int_t (*ngx_http_post_subrequest_pt)(ngx_http_request_t *r,\n    void *data, ngx_int_t rc);\n\ntypedef struct {\n    ngx_http_post_subrequest_pt       handler;\n    void                             *data;\n} ngx_http_post_subrequest_t;\n\n\ntypedef struct ngx_http_postponed_request_s  ngx_http_postponed_request_t;\n\nstruct ngx_http_postponed_request_s {\n    ngx_http_request_t               *request;\n    ngx_chain_t                      *out;\n    ngx_http_postponed_request_t     *next;\n};\n\n\ntypedef struct ngx_http_posted_request_s  ngx_http_posted_request_t;\n\nstruct ngx_http_posted_request_s {\n    ngx_http_request_t               *request;\n    ngx_http_posted_request_t        *next;\n};\n\n\ntypedef ngx_int_t (*ngx_http_handler_pt)(ngx_http_request_t *r);\ntypedef void (*ngx_http_event_handler_pt)(ngx_http_request_t *r);\n\n\nstruct ngx_http_request_s {\n    uint32_t                          signature;         /* \"HTTP\" */\n\n    ngx_connection_t                 *connection;\n\n    void                            **ctx;\n    void                            **main_conf;\n    void                            **srv_conf;\n    void                            **loc_conf;\n\n    ngx_http_event_handler_pt         read_event_handler;\n    ngx_http_event_handler_pt         write_event_handler;\n\n#if (NGX_HTTP_CACHE)\n    ngx_http_cache_t                 *cache;\n#endif\n\n    ngx_http_upstream_t              *upstream;\n    ngx_array_t                      *upstream_states;\n                                         /* of ngx_http_upstream_state_t */\n\n    ngx_pool_t                       *pool;\n    ngx_buf_t                        *header_in;\n\n    ngx_http_headers_in_t             headers_in;\n    ngx_http_headers_out_t            headers_out;\n\n    ngx_http_request_body_t          *request_body;\n\n    time_t                            lingering_time;\n    time_t                            start_sec;\n    ngx_msec_t                        start_msec;\n\n#if (T_HTTP_UPSTREAM_TIMEOUT_VAR)\n    ngx_msec_t                        connect_time;\n    ngx_msec_t                        read_time;\n    ngx_msec_t                        send_time;\n#endif\n\n#if (T_NGX_RET_CACHE)\n    ngx_usec_t                        start_usec;\n#endif\n\n    ngx_uint_t                        method;\n    ngx_uint_t                        http_version;\n\n    ngx_str_t                         request_line;\n#if (T_NGX_VARS)\n    ngx_str_t                         raw_uri;\n#endif\n    ngx_str_t                         uri;\n    ngx_str_t                         args;\n    ngx_str_t                         exten;\n    ngx_str_t                         unparsed_uri;\n\n#if (NGX_HTTP_PROXY_CONNECT)\n    ngx_str_t                         connect_host;\n    ngx_str_t                         connect_port;\n    in_port_t                         connect_port_n;\n    u_char                           *connect_host_start;\n    u_char                           *connect_host_end;\n    u_char                           *connect_port_end;\n#endif\n\n    ngx_str_t                         method_name;\n    ngx_str_t                         http_protocol;\n    ngx_str_t                         schema;\n\n    ngx_chain_t                      *out;\n    ngx_http_request_t               *main;\n    ngx_http_request_t               *parent;\n    ngx_http_postponed_request_t     *postponed;\n    ngx_http_post_subrequest_t       *post_subrequest;\n    ngx_http_posted_request_t        *posted_requests;\n\n    ngx_int_t                         phase_handler;\n    ngx_http_handler_pt               content_handler;\n    ngx_uint_t                        access_code;\n\n    ngx_http_variable_value_t        *variables;\n\n#if (NGX_PCRE)\n    ngx_uint_t                        ncaptures;\n    int                              *captures;\n    u_char                           *captures_data;\n#endif\n\n    size_t                            limit_rate;\n    size_t                            limit_rate_after;\n\n    /* used to learn the Apache compatible response length without a header */\n    size_t                            header_size;\n\n    off_t                             request_length;\n\n    ngx_uint_t                        err_status;\n\n    ngx_http_connection_t            *http_connection;\n    ngx_http_v2_stream_t             *stream;\n#if (T_NGX_XQUIC)\n    ngx_http_v3_stream_t             *xqstream;\n#endif\n\n    ngx_http_log_handler_pt           log_handler;\n\n    ngx_http_cleanup_t               *cleanup;\n\n    unsigned                          count:16;\n    unsigned                          subrequests:8;\n    unsigned                          blocked:8;\n\n    unsigned                          aio:1;\n\n    unsigned                          http_state:4;\n\n    /* URI with \"/.\" and on Win32 with \"//\" */\n    unsigned                          complex_uri:1;\n\n    /* URI with \"%\" */\n    unsigned                          quoted_uri:1;\n\n    /* URI with \"+\" */\n    unsigned                          plus_in_uri:1;\n\n    /* URI with empty path */\n    unsigned                          empty_path_in_uri:1;\n\n    unsigned                          invalid_header:1;\n\n    unsigned                          add_uri_to_alias:1;\n    unsigned                          valid_location:1;\n    unsigned                          valid_unparsed_uri:1;\n    unsigned                          uri_changed:1;\n    unsigned                          uri_changes:4;\n\n    unsigned                          request_body_in_single_buf:1;\n    unsigned                          request_body_in_file_only:1;\n    unsigned                          request_body_in_persistent_file:1;\n    unsigned                          request_body_in_clean_file:1;\n    unsigned                          request_body_file_group_access:1;\n    unsigned                          request_body_file_log_level:3;\n    unsigned                          request_body_no_buffering:1;\n\n    unsigned                          subrequest_in_memory:1;\n    unsigned                          waited:1;\n\n#if (NGX_HTTP_CACHE)\n    unsigned                          cached:1;\n#endif\n\n#if (NGX_HTTP_GZIP)\n    unsigned                          gzip_tested:1;\n    unsigned                          gzip_ok:1;\n    unsigned                          gzip_vary:1;\n#endif\n\n#if (NGX_PCRE)\n    unsigned                          realloc_captures:1;\n#endif\n\n    unsigned                          proxy:1;\n    unsigned                          bypass_cache:1;\n    unsigned                          no_cache:1;\n\n    /*\n     * instead of using the request context data in\n     * ngx_http_limit_conn_module and ngx_http_limit_req_module\n     * we use the bit fields in the request structure\n     */\n    unsigned                          limit_conn_status:2;\n    unsigned                          limit_req_status:3;\n\n    unsigned                          limit_rate_set:1;\n    unsigned                          limit_rate_after_set:1;\n#if (T_NGX_HTTP_SYSGUARD)\n    unsigned                          sysguard_set:1;\n#endif\n\n#if 0\n    unsigned                          cacheable:1;\n#endif\n\n    unsigned                          pipeline:1;\n    unsigned                          chunked:1;\n    unsigned                          header_only:1;\n    unsigned                          expect_trailers:1;\n    unsigned                          keepalive:1;\n    unsigned                          lingering_close:1;\n    unsigned                          discard_body:1;\n    unsigned                          reading_body:1;\n    unsigned                          internal:1;\n    unsigned                          error_page:1;\n    unsigned                          filter_finalize:1;\n    unsigned                          post_action:1;\n    unsigned                          request_complete:1;\n    unsigned                          request_output:1;\n    unsigned                          header_sent:1;\n    unsigned                          expect_tested:1;\n    unsigned                          root_tested:1;\n    unsigned                          done:1;\n    unsigned                          logged:1;\n\n    unsigned                          buffered:4;\n\n    unsigned                          main_filter_need_in_memory:1;\n    unsigned                          filter_need_in_memory:1;\n    unsigned                          filter_need_temporary:1;\n    unsigned                          preserve_body:1;\n    unsigned                          allow_ranges:1;\n    unsigned                          subrequest_ranges:1;\n    unsigned                          single_range:1;\n    unsigned                          disable_not_modified:1;\n    unsigned                          stat_reading:1;\n    unsigned                          stat_writing:1;\n    unsigned                          stat_processing:1;\n\n    unsigned                          background:1;\n    unsigned                          health_check:1;\n\n    /* used to parse HTTP headers */\n\n    ngx_uint_t                        state;\n\n    ngx_uint_t                        header_hash;\n    ngx_uint_t                        lowcase_index;\n    u_char                            lowcase_header[NGX_HTTP_LC_HEADER_LEN];\n\n    u_char                           *header_name_start;\n    u_char                           *header_name_end;\n    u_char                           *header_start;\n    u_char                           *header_end;\n\n    /*\n     * a memory that can be reused after parsing a request line\n     * via ngx_http_ephemeral_t\n     */\n\n    u_char                           *uri_start;\n    u_char                           *uri_end;\n    u_char                           *uri_ext;\n    u_char                           *args_start;\n    u_char                           *request_start;\n    u_char                           *request_end;\n    u_char                           *method_end;\n    u_char                           *schema_start;\n    u_char                           *schema_end;\n    u_char                           *host_start;\n    u_char                           *host_end;\n    u_char                           *port_start;\n    u_char                           *port_end;\n\n    unsigned                          http_minor:16;\n    unsigned                          http_major:16;\n\n#if (T_NGX_MULTI_UPSTREAM)\n    ngx_queue_t                       *multi_item;\n    ngx_queue_t                       *backend_r;\n    ngx_queue_t                        waiting_queue;\n    ngx_flag_t                         waiting;\n#endif\n};\n\n\ntypedef struct {\n    ngx_http_posted_request_t         terminal_posted_request;\n} ngx_http_ephemeral_t;\n\n\n#define ngx_http_ephemeral(r)  (void *) (&r->uri_start)\n\n\nextern ngx_http_header_t       ngx_http_headers_in[];\nextern ngx_http_header_out_t   ngx_http_headers_out[];\n\n\n#define ngx_http_set_log_request(log, r)                                      \\\n    ((ngx_http_log_ctx_t *) log->data)->current_request = r\n\n\n#endif /* _NGX_HTTP_REQUEST_H_INCLUDED_ */\n"
  },
  {
    "path": "src/http/ngx_http_request_body.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\nstatic void ngx_http_read_client_request_body_handler(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_do_read_client_request_body(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_copy_pipelined_header(ngx_http_request_t *r,\n    ngx_buf_t *buf);\nstatic ngx_int_t ngx_http_write_request_body(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_read_discarded_request_body(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_discard_request_body_filter(ngx_http_request_t *r,\n    ngx_buf_t *b);\nstatic ngx_int_t ngx_http_test_expect(ngx_http_request_t *r);\n\nstatic ngx_int_t ngx_http_request_body_filter(ngx_http_request_t *r,\n    ngx_chain_t *in);\nstatic ngx_int_t ngx_http_request_body_length_filter(ngx_http_request_t *r,\n    ngx_chain_t *in);\nstatic ngx_int_t ngx_http_request_body_chunked_filter(ngx_http_request_t *r,\n    ngx_chain_t *in);\n\n\nngx_int_t\nngx_http_read_client_request_body(ngx_http_request_t *r,\n    ngx_http_client_body_handler_pt post_handler)\n{\n    size_t                     preread;\n    ssize_t                    size;\n    ngx_int_t                  rc;\n    ngx_buf_t                 *b;\n    ngx_chain_t                out;\n    ngx_http_request_body_t   *rb;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    r->main->count++;\n\n    if (r != r->main || r->request_body || r->discard_body) {\n        r->request_body_no_buffering = 0;\n        post_handler(r);\n        return NGX_OK;\n    }\n\n    if (ngx_http_test_expect(r) != NGX_OK) {\n        rc = NGX_HTTP_INTERNAL_SERVER_ERROR;\n        goto done;\n    }\n\n    rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t));\n    if (rb == NULL) {\n        rc = NGX_HTTP_INTERNAL_SERVER_ERROR;\n        goto done;\n    }\n\n    /*\n     * set by ngx_pcalloc():\n     *\n     *     rb->temp_file = NULL;\n     *     rb->bufs = NULL;\n     *     rb->buf = NULL;\n     *     rb->free = NULL;\n     *     rb->busy = NULL;\n     *     rb->chunked = NULL;\n     *     rb->received = 0;\n     *     rb->filter_need_buffering = 0;\n     *     rb->last_sent = 0;\n     *     rb->last_saved = 0;\n     */\n\n    rb->rest = -1;\n    rb->post_handler = post_handler;\n\n    r->request_body = rb;\n\n    if (r->headers_in.content_length_n < 0 && !r->headers_in.chunked) {\n        r->request_body_no_buffering = 0;\n        post_handler(r);\n        return NGX_OK;\n    }\n\n#if (NGX_HTTP_V2)\n    if (r->stream) {\n        rc = ngx_http_v2_read_request_body(r);\n        goto done;\n    }\n#endif\n\n#if (T_NGX_XQUIC)\n    if (r->xqstream) {\n        rc = ngx_http_v3_read_request_body(r);\n        goto done;\n    }\n#endif\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 %uz\", preread);\n\n        out.buf = r->header_in;\n        out.next = NULL;\n\n        rc = ngx_http_request_body_filter(r, &out);\n\n        if (rc != NGX_OK) {\n            goto done;\n        }\n\n        r->request_length += preread - (r->header_in->last - r->header_in->pos);\n\n        if (!r->headers_in.chunked\n            && rb->rest > 0\n            && rb->rest <= (off_t) (r->header_in->end - r->header_in->last))\n        {\n            /* the whole request body may be placed in r->header_in */\n\n            b = ngx_calloc_buf(r->pool);\n            if (b == NULL) {\n                rc = NGX_HTTP_INTERNAL_SERVER_ERROR;\n                goto done;\n            }\n\n            b->temporary = 1;\n            b->start = r->header_in->pos;\n            b->pos = r->header_in->pos;\n            b->last = r->header_in->last;\n            b->end = r->header_in->end;\n\n            rb->buf = b;\n\n            r->read_event_handler = ngx_http_read_client_request_body_handler;\n            r->write_event_handler = ngx_http_request_empty_handler;\n\n            rc = ngx_http_do_read_client_request_body(r);\n            goto done;\n        }\n\n    } else {\n        /* set rb->rest */\n\n        rc = ngx_http_request_body_filter(r, NULL);\n\n        if (rc != NGX_OK) {\n            goto done;\n        }\n    }\n\n    if (rb->rest == 0 && rb->last_saved) {\n        /* the whole request body was pre-read */\n        r->request_body_no_buffering = 0;\n        post_handler(r);\n        return NGX_OK;\n    }\n\n    if (rb->rest < 0) {\n        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,\n                      \"negative request body rest\");\n        rc = NGX_HTTP_INTERNAL_SERVER_ERROR;\n        goto done;\n    }\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    size = clcf->client_body_buffer_size;\n    size += size >> 2;\n\n    /* TODO: honor r->request_body_in_single_buf */\n\n    if (!r->headers_in.chunked && rb->rest < size) {\n        size = (ssize_t) rb->rest;\n\n        if (r->request_body_in_single_buf) {\n            size += preread;\n        }\n\n        if (size == 0) {\n            size++;\n        }\n\n    } else {\n        size = clcf->client_body_buffer_size;\n    }\n\n    rb->buf = ngx_create_temp_buf(r->pool, size);\n    if (rb->buf == NULL) {\n        rc = NGX_HTTP_INTERNAL_SERVER_ERROR;\n        goto done;\n    }\n\n    r->read_event_handler = ngx_http_read_client_request_body_handler;\n    r->write_event_handler = ngx_http_request_empty_handler;\n\n    rc = ngx_http_do_read_client_request_body(r);\n\ndone:\n\n    if (r->request_body_no_buffering\n        && (rc == NGX_OK || rc == NGX_AGAIN))\n    {\n        if (rc == NGX_OK) {\n            r->request_body_no_buffering = 0;\n\n        } else {\n            /* rc == NGX_AGAIN */\n            r->reading_body = 1;\n        }\n\n        r->read_event_handler = ngx_http_block_reading;\n        post_handler(r);\n    }\n\n    if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {\n        r->main->count--;\n    }\n\n    return rc;\n}\n\n\nngx_int_t\nngx_http_read_unbuffered_request_body(ngx_http_request_t *r)\n{\n    ngx_int_t  rc;\n\n#if (NGX_HTTP_V2)\n    if (r->stream) {\n        rc = ngx_http_v2_read_unbuffered_request_body(r);\n\n        if (rc == NGX_OK) {\n            r->reading_body = 0;\n        }\n\n        return rc;\n    }\n#endif\n\n#if (T_NGX_XQUIC)\n    if (r->xqstream) {\n        rc = ngx_http_v3_read_unbuffered_request_body(r);\n\n        if (rc == NGX_OK) {\n            r->reading_body = 0;\n        }\n\n        return rc;\n    }\n#endif\n\n    if (r->connection->read->timedout) {\n        r->connection->timedout = 1;\n        return NGX_HTTP_REQUEST_TIME_OUT;\n    }\n\n    rc = ngx_http_do_read_client_request_body(r);\n\n    if (rc == NGX_OK) {\n        r->reading_body = 0;\n    }\n\n    return rc;\n}\n\n\nstatic void\nngx_http_read_client_request_body_handler(ngx_http_request_t *r)\n{\n    ngx_int_t  rc;\n\n    if (r->connection->read->timedout) {\n        r->connection->timedout = 1;\n        ngx_http_finalize_request(r, NGX_HTTP_REQUEST_TIME_OUT);\n        return;\n    }\n\n    rc = ngx_http_do_read_client_request_body(r);\n\n    if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {\n        ngx_http_finalize_request(r, rc);\n    }\n}\n\n\nstatic ngx_int_t\nngx_http_do_read_client_request_body(ngx_http_request_t *r)\n{\n    off_t                      rest;\n    size_t                     size;\n    ssize_t                    n;\n    ngx_int_t                  rc;\n    ngx_uint_t                 flush;\n    ngx_chain_t                out;\n    ngx_connection_t          *c;\n    ngx_http_request_body_t   *rb;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    c = r->connection;\n    rb = r->request_body;\n    flush = 1;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http read client request body\");\n\n    for ( ;; ) {\n        for ( ;; ) {\n            if (rb->rest == 0) {\n                break;\n            }\n\n            if (rb->buf->last == rb->buf->end) {\n\n                /* update chains */\n\n                rc = ngx_http_request_body_filter(r, NULL);\n\n                if (rc != NGX_OK) {\n                    return rc;\n                }\n\n                if (rb->busy != NULL) {\n                    if (r->request_body_no_buffering) {\n                        if (c->read->timer_set) {\n                            ngx_del_timer(c->read);\n                        }\n\n                        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n                            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n                        }\n\n                        return NGX_AGAIN;\n                    }\n\n                    if (rb->filter_need_buffering) {\n                        clcf = ngx_http_get_module_loc_conf(r,\n                                                         ngx_http_core_module);\n                        ngx_add_timer(c->read, clcf->client_body_timeout);\n\n                        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n                            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n                        }\n\n                        return NGX_AGAIN;\n                    }\n\n                    ngx_log_error(NGX_LOG_ALERT, c->log, 0,\n                                  \"busy buffers after request body flush\");\n\n                    return NGX_HTTP_INTERNAL_SERVER_ERROR;\n                }\n\n                flush = 0;\n                rb->buf->pos = rb->buf->start;\n                rb->buf->last = rb->buf->start;\n            }\n\n            size = rb->buf->end - rb->buf->last;\n            rest = rb->rest - (rb->buf->last - rb->buf->pos);\n\n            if ((off_t) size > rest) {\n                size = (size_t) rest;\n            }\n\n            if (size == 0) {\n                break;\n            }\n\n            n = c->recv(c, rb->buf->last, size);\n\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                           \"http client request body recv %z\", n);\n\n            if (n == NGX_AGAIN) {\n                break;\n            }\n\n            if (n == 0) {\n                ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                              \"client prematurely closed connection\");\n            }\n\n            if (n == 0 || n == NGX_ERROR) {\n                c->error = 1;\n                return NGX_HTTP_BAD_REQUEST;\n            }\n\n            rb->buf->last += n;\n            r->request_length += n;\n#if (T_NGX_REQ_STATUS)\n            c->received += n;\n#endif\n\n            /* pass buffer to request body filter chain */\n\n            flush = 0;\n            out.buf = rb->buf;\n            out.next = NULL;\n\n            rc = ngx_http_request_body_filter(r, &out);\n\n            if (rc != NGX_OK) {\n                return rc;\n            }\n\n            if (rb->rest == 0) {\n                break;\n            }\n\n            if (rb->buf->last < rb->buf->end) {\n                break;\n            }\n        }\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"http client request body rest %O\", rb->rest);\n\n        if (flush) {\n            rc = ngx_http_request_body_filter(r, NULL);\n\n            if (rc != NGX_OK) {\n                return rc;\n            }\n        }\n\n        if (rb->rest == 0 && rb->last_saved) {\n            break;\n        }\n\n        if (!c->read->ready || rb->rest == 0) {\n\n            clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n            ngx_add_timer(c->read, clcf->client_body_timeout);\n\n            if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n                return NGX_HTTP_INTERNAL_SERVER_ERROR;\n            }\n\n            return NGX_AGAIN;\n        }\n    }\n\n    if (ngx_http_copy_pipelined_header(r, rb->buf) != NGX_OK) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    if (c->read->timer_set) {\n        ngx_del_timer(c->read);\n    }\n\n    if (!r->request_body_no_buffering) {\n        r->read_event_handler = ngx_http_block_reading;\n        rb->post_handler(r);\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_copy_pipelined_header(ngx_http_request_t *r, ngx_buf_t *buf)\n{\n    size_t                     n;\n    ngx_buf_t                 *b;\n    ngx_chain_t               *cl;\n    ngx_http_connection_t     *hc;\n    ngx_http_core_srv_conf_t  *cscf;\n\n    b = r->header_in;\n    n = buf->last - buf->pos;\n\n    if (buf == b || n == 0) {\n        return NGX_OK;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http body pipelined header: %uz\", n);\n\n    /*\n     * if there is a pipelined request in the client body buffer,\n     * copy it to the r->header_in buffer if there is enough room,\n     * or allocate a large client header buffer\n     */\n\n    if (n > (size_t) (b->end - b->last)) {\n\n        hc = r->http_connection;\n\n        if (hc->free) {\n            cl = hc->free;\n            hc->free = cl->next;\n\n            b = cl->buf;\n\n            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"http large header free: %p %uz\",\n                           b->pos, b->end - b->last);\n\n        } else {\n            cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);\n\n            b = ngx_create_temp_buf(r->connection->pool,\n                                    cscf->large_client_header_buffers.size);\n            if (b == NULL) {\n                return NGX_ERROR;\n            }\n\n            cl = ngx_alloc_chain_link(r->connection->pool);\n            if (cl == NULL) {\n                return NGX_ERROR;\n            }\n\n            cl->buf = b;\n\n            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"http large header alloc: %p %uz\",\n                           b->pos, b->end - b->last);\n        }\n\n        cl->next = hc->busy;\n        hc->busy = cl;\n        hc->nbusy++;\n\n        r->header_in = b;\n\n        if (n > (size_t) (b->end - b->last)) {\n            ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,\n                          \"too large pipelined header after reading body\");\n            return NGX_ERROR;\n        }\n    }\n\n    ngx_memcpy(b->last, buf->pos, n);\n\n    b->last += n;\n    r->request_length -= n;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_write_request_body(ngx_http_request_t *r)\n{\n    ssize_t                    n;\n    ngx_chain_t               *cl, *ln;\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    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http write client request body, bufs %p\", rb->bufs);\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 = r->request_body_in_persistent_file;\n        tf->clean = r->request_body_in_clean_file;\n\n        if (r->request_body_file_group_access) {\n            tf->access = 0660;\n        }\n\n        rb->temp_file = tf;\n\n        if (rb->bufs == 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    if (rb->bufs == NULL) {\n        return NGX_OK;\n    }\n\n    n = ngx_write_chain_to_temp_file(rb->temp_file, rb->bufs);\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    /* mark all buffers as written */\n\n    for (cl = rb->bufs; cl; /* void */) {\n\n        cl->buf->pos = cl->buf->last;\n\n        ln = cl;\n        cl = cl->next;\n        ngx_free_chain(r->pool, ln);\n    }\n\n    rb->bufs = NULL;\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_discard_request_body(ngx_http_request_t *r)\n{\n    ssize_t       size;\n    ngx_int_t     rc;\n    ngx_event_t  *rev;\n\n    if (r != r->main || r->discard_body || r->request_body) {\n        return NGX_OK;\n    }\n\n#if (NGX_HTTP_V2)\n    if (r->stream) {\n        r->stream->skip_data = 1;\n        return NGX_OK;\n    }\n#endif\n\n#if (T_NGX_XQUIC)\n    if (r->xqstream) {\n        r->xqstream->skip_data = 1;\n        return NGX_OK;\n    }\n#endif\n\n    if (ngx_http_test_expect(r) != NGX_OK) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    rev = r->connection->read;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, \"http set discard body\");\n\n    if (rev->timer_set) {\n        ngx_del_timer(rev);\n    }\n\n    if (r->headers_in.content_length_n <= 0 && !r->headers_in.chunked) {\n        return NGX_OK;\n    }\n\n    size = r->header_in->last - r->header_in->pos;\n\n    if (size || r->headers_in.chunked) {\n        rc = ngx_http_discard_request_body_filter(r, r->header_in);\n\n        if (rc != NGX_OK) {\n            return rc;\n        }\n\n        if (r->headers_in.content_length_n == 0) {\n            return NGX_OK;\n        }\n    }\n\n    rc = ngx_http_read_discarded_request_body(r);\n\n    if (rc == NGX_OK) {\n        r->lingering_close = 0;\n        return NGX_OK;\n    }\n\n    if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {\n        return rc;\n    }\n\n    /* rc == NGX_AGAIN */\n\n    r->read_event_handler = ngx_http_discarded_request_body_handler;\n\n    if (ngx_handle_read_event(rev, 0) != NGX_OK) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    r->count++;\n    r->discard_body = 1;\n\n    return NGX_OK;\n}\n\n\nvoid\nngx_http_discarded_request_body_handler(ngx_http_request_t *r)\n{\n    ngx_int_t                  rc;\n    ngx_msec_t                 timer;\n    ngx_event_t               *rev;\n    ngx_connection_t          *c;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    c = r->connection;\n    rev = c->read;\n\n    if (rev->timedout) {\n        c->timedout = 1;\n        c->error = 1;\n        ngx_http_finalize_request(r, NGX_ERROR);\n        return;\n    }\n\n    if (r->lingering_time) {\n        timer = (ngx_msec_t) r->lingering_time - (ngx_msec_t) ngx_time();\n\n        if ((ngx_msec_int_t) timer <= 0) {\n            r->discard_body = 0;\n            r->lingering_close = 0;\n            ngx_http_finalize_request(r, NGX_ERROR);\n            return;\n        }\n\n    } else {\n        timer = 0;\n    }\n\n    rc = ngx_http_read_discarded_request_body(r);\n\n    if (rc == NGX_OK) {\n        r->discard_body = 0;\n        r->lingering_close = 0;\n        r->lingering_time = 0;\n        ngx_http_finalize_request(r, NGX_DONE);\n        return;\n    }\n\n    if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {\n        c->error = 1;\n        ngx_http_finalize_request(r, NGX_ERROR);\n        return;\n    }\n\n    /* rc == NGX_AGAIN */\n\n    if (ngx_handle_read_event(rev, 0) != NGX_OK) {\n        c->error = 1;\n        ngx_http_finalize_request(r, NGX_ERROR);\n        return;\n    }\n\n    if (timer) {\n\n        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n        timer *= 1000;\n\n        if (timer > clcf->lingering_timeout) {\n            timer = clcf->lingering_timeout;\n        }\n\n        ngx_add_timer(rev, timer);\n    }\n}\n\n\nstatic ngx_int_t\nngx_http_read_discarded_request_body(ngx_http_request_t *r)\n{\n    size_t     size;\n    ssize_t    n;\n    ngx_int_t  rc;\n    ngx_buf_t  b;\n    u_char     buffer[NGX_HTTP_DISCARD_BUFFER_SIZE];\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http read discarded body\");\n\n    ngx_memzero(&b, sizeof(ngx_buf_t));\n\n    b.temporary = 1;\n\n    for ( ;; ) {\n        if (r->headers_in.content_length_n == 0) {\n            break;\n        }\n\n        if (!r->connection->read->ready) {\n            return NGX_AGAIN;\n        }\n\n        size = (size_t) ngx_min(r->headers_in.content_length_n,\n                                NGX_HTTP_DISCARD_BUFFER_SIZE);\n\n        n = r->connection->recv(r->connection, buffer, size);\n\n        if (n == NGX_ERROR) {\n            r->connection->error = 1;\n            return NGX_OK;\n        }\n\n        if (n == NGX_AGAIN) {\n            return NGX_AGAIN;\n        }\n\n        if (n == 0) {\n            return NGX_OK;\n        }\n\n        b.pos = buffer;\n        b.last = buffer + n;\n\n        rc = ngx_http_discard_request_body_filter(r, &b);\n\n        if (rc != NGX_OK) {\n            return rc;\n        }\n    }\n\n    if (ngx_http_copy_pipelined_header(r, &b) != NGX_OK) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    r->read_event_handler = ngx_http_block_reading;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_discard_request_body_filter(ngx_http_request_t *r, ngx_buf_t *b)\n{\n    size_t                     size;\n    ngx_int_t                  rc;\n    ngx_http_request_body_t   *rb;\n    ngx_http_core_srv_conf_t  *cscf;\n\n    if (r->headers_in.chunked) {\n\n        rb = r->request_body;\n\n        if (rb == NULL) {\n\n            rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t));\n            if (rb == NULL) {\n                return NGX_HTTP_INTERNAL_SERVER_ERROR;\n            }\n\n            rb->chunked = ngx_pcalloc(r->pool, sizeof(ngx_http_chunked_t));\n            if (rb->chunked == NULL) {\n                return NGX_HTTP_INTERNAL_SERVER_ERROR;\n            }\n\n            r->request_body = rb;\n        }\n\n        for ( ;; ) {\n\n            rc = ngx_http_parse_chunked(r, b, rb->chunked);\n\n            if (rc == NGX_OK) {\n\n                /* a chunk has been parsed successfully */\n\n                size = b->last - b->pos;\n\n                if ((off_t) size > rb->chunked->size) {\n                    b->pos += (size_t) rb->chunked->size;\n                    rb->chunked->size = 0;\n\n                } else {\n                    rb->chunked->size -= size;\n                    b->pos = b->last;\n                }\n\n                continue;\n            }\n\n            if (rc == NGX_DONE) {\n\n                /* a whole response has been parsed successfully */\n\n                r->headers_in.content_length_n = 0;\n                break;\n            }\n\n            if (rc == NGX_AGAIN) {\n\n                /* set amount of data we want to see next time */\n\n                cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);\n\n                r->headers_in.content_length_n = ngx_max(rb->chunked->length,\n                               (off_t) cscf->large_client_header_buffers.size);\n                break;\n            }\n\n            /* invalid */\n\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"client sent invalid chunked body\");\n\n            return NGX_HTTP_BAD_REQUEST;\n        }\n\n    } else {\n        size = b->last - b->pos;\n\n        if ((off_t) size > r->headers_in.content_length_n) {\n            b->pos += (size_t) r->headers_in.content_length_n;\n            r->headers_in.content_length_n = 0;\n\n        } else {\n            b->pos = b->last;\n            r->headers_in.content_length_n -= size;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_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#if (NGX_HTTP_V2)\n        || r->stream != NULL\n#endif\n#if (T_NGX_XQUIC)\n        || r->xqstream != NULL\n#endif\n       )\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    r->connection->error = 1;\n\n    return NGX_ERROR;\n}\n\n\nstatic ngx_int_t\nngx_http_request_body_filter(ngx_http_request_t *r, ngx_chain_t *in)\n{\n    if (r->headers_in.chunked) {\n        return ngx_http_request_body_chunked_filter(r, in);\n\n    } else {\n        return ngx_http_request_body_length_filter(r, in);\n    }\n}\n\n\nstatic ngx_int_t\nngx_http_request_body_length_filter(ngx_http_request_t *r, ngx_chain_t *in)\n{\n    size_t                     size;\n    ngx_int_t                  rc;\n    ngx_buf_t                 *b;\n    ngx_chain_t               *cl, *tl, *out, **ll;\n    ngx_http_request_body_t   *rb;\n\n    rb = r->request_body;\n\n    out = NULL;\n    ll = &out;\n\n    if (rb->rest == -1) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"http request body content length filter\");\n\n        rb->rest = r->headers_in.content_length_n;\n\n        if (rb->rest == 0) {\n\n            tl = ngx_chain_get_free_buf(r->pool, &rb->free);\n            if (tl == NULL) {\n                return NGX_HTTP_INTERNAL_SERVER_ERROR;\n            }\n\n            b = tl->buf;\n\n            ngx_memzero(b, sizeof(ngx_buf_t));\n\n            b->last_buf = 1;\n\n            *ll = tl;\n            ll = &tl->next;\n        }\n    }\n\n    for (cl = in; cl; cl = cl->next) {\n\n        if (rb->rest == 0) {\n            break;\n        }\n\n        tl = ngx_chain_get_free_buf(r->pool, &rb->free);\n        if (tl == NULL) {\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n\n        b = tl->buf;\n\n        ngx_memzero(b, sizeof(ngx_buf_t));\n\n        b->temporary = 1;\n        b->tag = (ngx_buf_tag_t) &ngx_http_read_client_request_body;\n        b->start = cl->buf->pos;\n        b->pos = cl->buf->pos;\n        b->last = cl->buf->last;\n        b->end = cl->buf->end;\n        b->flush = r->request_body_no_buffering;\n\n        size = cl->buf->last - cl->buf->pos;\n\n        if ((off_t) size < rb->rest) {\n            cl->buf->pos = cl->buf->last;\n            rb->rest -= size;\n\n        } else {\n            cl->buf->pos += (size_t) rb->rest;\n            rb->rest = 0;\n            b->last = cl->buf->pos;\n            b->last_buf = 1;\n        }\n\n        *ll = tl;\n        ll = &tl->next;\n    }\n\n    rc = ngx_http_top_request_body_filter(r, out);\n\n    ngx_chain_update_chains(r->pool, &rb->free, &rb->busy, &out,\n                            (ngx_buf_tag_t) &ngx_http_read_client_request_body);\n\n    return rc;\n}\n\n\nstatic ngx_int_t\nngx_http_request_body_chunked_filter(ngx_http_request_t *r, ngx_chain_t *in)\n{\n    size_t                     size;\n    ngx_int_t                  rc;\n    ngx_buf_t                 *b;\n    ngx_chain_t               *cl, *out, *tl, **ll;\n    ngx_http_request_body_t   *rb;\n    ngx_http_core_loc_conf_t  *clcf;\n    ngx_http_core_srv_conf_t  *cscf;\n\n    rb = r->request_body;\n\n    out = NULL;\n    ll = &out;\n\n    if (rb->rest == -1) {\n\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"http request body chunked filter\");\n\n        rb->chunked = ngx_pcalloc(r->pool, sizeof(ngx_http_chunked_t));\n        if (rb->chunked == NULL) {\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n\n        cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);\n\n        r->headers_in.content_length_n = 0;\n        rb->rest = cscf->large_client_header_buffers.size;\n    }\n\n    for (cl = in; cl; cl = cl->next) {\n\n        b = NULL;\n\n        for ( ;; ) {\n\n            ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,\n                           \"http body chunked buf \"\n                           \"t:%d f:%d %p, pos %p, size: %z file: %O, size: %O\",\n                           cl->buf->temporary, cl->buf->in_file,\n                           cl->buf->start, cl->buf->pos,\n                           cl->buf->last - cl->buf->pos,\n                           cl->buf->file_pos,\n                           cl->buf->file_last - cl->buf->file_pos);\n\n            rc = ngx_http_parse_chunked(r, cl->buf, rb->chunked);\n\n            if (rc == NGX_OK) {\n\n                /* a chunk has been parsed successfully */\n\n                clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n                if (clcf->client_max_body_size\n                    && clcf->client_max_body_size\n                       - r->headers_in.content_length_n < rb->chunked->size)\n                {\n                    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                                  \"client intended to send too large chunked \"\n                                  \"body: %O+%O bytes\",\n                                  r->headers_in.content_length_n,\n                                  rb->chunked->size);\n\n                    r->lingering_close = 1;\n\n                    return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE;\n                }\n\n                if (b\n                    && rb->chunked->size <= 128\n                    && cl->buf->last - cl->buf->pos >= rb->chunked->size)\n                {\n                    r->headers_in.content_length_n += rb->chunked->size;\n\n                    if (rb->chunked->size < 8) {\n\n                        while (rb->chunked->size) {\n                            *b->last++ = *cl->buf->pos++;\n                            rb->chunked->size--;\n                        }\n\n                    } else {\n                        ngx_memmove(b->last, cl->buf->pos, rb->chunked->size);\n                        b->last += rb->chunked->size;\n                        cl->buf->pos += rb->chunked->size;\n                        rb->chunked->size = 0;\n                    }\n\n                    continue;\n                }\n\n                tl = ngx_chain_get_free_buf(r->pool, &rb->free);\n                if (tl == NULL) {\n                    return NGX_HTTP_INTERNAL_SERVER_ERROR;\n                }\n\n                b = tl->buf;\n\n                ngx_memzero(b, sizeof(ngx_buf_t));\n\n                b->temporary = 1;\n                b->tag = (ngx_buf_tag_t) &ngx_http_read_client_request_body;\n                b->start = cl->buf->pos;\n                b->pos = cl->buf->pos;\n                b->last = cl->buf->last;\n                b->end = cl->buf->end;\n                b->flush = r->request_body_no_buffering;\n\n                *ll = tl;\n                ll = &tl->next;\n\n                size = cl->buf->last - cl->buf->pos;\n\n                if ((off_t) size > rb->chunked->size) {\n                    cl->buf->pos += (size_t) rb->chunked->size;\n                    r->headers_in.content_length_n += rb->chunked->size;\n                    rb->chunked->size = 0;\n\n                } else {\n                    rb->chunked->size -= size;\n                    r->headers_in.content_length_n += size;\n                    cl->buf->pos = cl->buf->last;\n                }\n\n                b->last = cl->buf->pos;\n\n                continue;\n            }\n\n            if (rc == NGX_DONE) {\n\n                /* a whole response has been parsed successfully */\n\n                rb->rest = 0;\n\n                tl = ngx_chain_get_free_buf(r->pool, &rb->free);\n                if (tl == NULL) {\n                    return NGX_HTTP_INTERNAL_SERVER_ERROR;\n                }\n\n                b = tl->buf;\n\n                ngx_memzero(b, sizeof(ngx_buf_t));\n\n                b->last_buf = 1;\n\n                *ll = tl;\n                ll = &tl->next;\n\n                break;\n            }\n\n            if (rc == NGX_AGAIN) {\n\n                /* set rb->rest, amount of data we want to see next time */\n\n                cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);\n\n                rb->rest = ngx_max(rb->chunked->length,\n                               (off_t) cscf->large_client_header_buffers.size);\n\n                break;\n            }\n\n            /* invalid */\n\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"client sent invalid chunked body\");\n\n            return NGX_HTTP_BAD_REQUEST;\n        }\n    }\n\n    rc = ngx_http_top_request_body_filter(r, out);\n\n    ngx_chain_update_chains(r->pool, &rb->free, &rb->busy, &out,\n                            (ngx_buf_tag_t) &ngx_http_read_client_request_body);\n\n    return rc;\n}\n\n\nngx_int_t\nngx_http_request_body_save_filter(ngx_http_request_t *r, ngx_chain_t *in)\n{\n    ngx_buf_t                 *b;\n    ngx_chain_t               *cl, *tl, **ll;\n    ngx_http_request_body_t   *rb;\n\n    rb = r->request_body;\n\n    ll = &rb->bufs;\n\n    for (cl = rb->bufs; cl; cl = cl->next) {\n\n#if 0\n        ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,\n                       \"http body old buf t:%d f:%d %p, pos %p, size: %z \"\n                       \"file: %O, size: %O\",\n                       cl->buf->temporary, cl->buf->in_file,\n                       cl->buf->start, cl->buf->pos,\n                       cl->buf->last - cl->buf->pos,\n                       cl->buf->file_pos,\n                       cl->buf->file_last - cl->buf->file_pos);\n#endif\n\n        ll = &cl->next;\n    }\n\n    for (cl = in; cl; cl = cl->next) {\n\n        ngx_log_debug7(NGX_LOG_DEBUG_EVENT, r->connection->log, 0,\n                       \"http body new buf t:%d f:%d %p, pos %p, size: %z \"\n                       \"file: %O, size: %O\",\n                       cl->buf->temporary, cl->buf->in_file,\n                       cl->buf->start, cl->buf->pos,\n                       cl->buf->last - cl->buf->pos,\n                       cl->buf->file_pos,\n                       cl->buf->file_last - cl->buf->file_pos);\n\n        if (cl->buf->last_buf) {\n\n            if (rb->last_saved) {\n                ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,\n                              \"duplicate last buf in save filter\");\n                *ll = NULL;\n                return NGX_HTTP_INTERNAL_SERVER_ERROR;\n            }\n\n            rb->last_saved = 1;\n\n#if (T_NGX_INPUT_BODY_FILTER)\n    {\n    ngx_int_t                  rc;\n    ngx_chain_t               *cl;\n\n    for (cl = in; cl; cl = cl->next) {\n        rc = ngx_http_top_input_body_filter(r, cl->buf);\n        if (rc != NGX_OK) {\n            if (rc > NGX_OK && rc < NGX_HTTP_SPECIAL_RESPONSE) {\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                              \"input filter: return code 1xx or 2xx \"\n                              \"will cause trouble and is converted to 500\");\n            }\n\n            /**\n             * NGX_OK: success and continue;\n             * NGX_ERROR: failed and exit;\n             * NGX_AGAIN: not ready and retry later.\n             */\n\n            if (rc < NGX_HTTP_SPECIAL_RESPONSE && rc != NGX_AGAIN) {\n                rc = NGX_HTTP_INTERNAL_SERVER_ERROR;\n            }\n\n            return rc;\n        }\n    }\n    }\n#endif\n\n        }\n\n        tl = ngx_alloc_chain_link(r->pool);\n        if (tl == NULL) {\n            *ll = NULL;\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n\n        tl->buf = cl->buf;\n        *ll = tl;\n        ll = &tl->next;\n    }\n\n    *ll = NULL;\n\n    if (r->request_body_no_buffering) {\n        return NGX_OK;\n    }\n\n    if (rb->rest > 0) {\n\n        if (rb->bufs && rb->buf && rb->buf->last == rb->buf->end\n            && ngx_http_write_request_body(r) != NGX_OK)\n        {\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n\n        return NGX_OK;\n    }\n\n    if (!rb->last_saved) {\n        return NGX_OK;\n    }\n\n    if (rb->temp_file || r->request_body_in_file_only) {\n\n        if (rb->bufs && rb->bufs->buf->in_file) {\n            ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,\n                          \"body already in file\");\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n\n        if (ngx_http_write_request_body(r) != NGX_OK) {\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n\n        if (rb->temp_file->file.offset != 0) {\n\n            cl = ngx_chain_get_free_buf(r->pool, &rb->free);\n            if (cl == NULL) {\n                return NGX_HTTP_INTERNAL_SERVER_ERROR;\n            }\n\n            b = cl->buf;\n\n            ngx_memzero(b, sizeof(ngx_buf_t));\n\n            b->in_file = 1;\n            b->file_last = rb->temp_file->file.offset;\n            b->file = &rb->temp_file->file;\n\n            rb->bufs = cl;\n        }\n    }\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "src/http/ngx_http_script.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\nstatic ngx_int_t ngx_http_script_init_arrays(ngx_http_script_compile_t *sc);\nstatic ngx_int_t ngx_http_script_done(ngx_http_script_compile_t *sc);\nstatic ngx_int_t ngx_http_script_add_copy_code(ngx_http_script_compile_t *sc,\n    ngx_str_t *value, ngx_uint_t last);\nstatic ngx_int_t ngx_http_script_add_var_code(ngx_http_script_compile_t *sc,\n    ngx_str_t *name);\nstatic ngx_int_t ngx_http_script_add_args_code(ngx_http_script_compile_t *sc);\n#if (NGX_PCRE)\nstatic ngx_int_t ngx_http_script_add_capture_code(ngx_http_script_compile_t *sc,\n    ngx_uint_t n);\n#endif\nstatic ngx_int_t\n    ngx_http_script_add_full_name_code(ngx_http_script_compile_t *sc);\nstatic size_t ngx_http_script_full_name_len_code(ngx_http_script_engine_t *e);\nstatic void ngx_http_script_full_name_code(ngx_http_script_engine_t *e);\n\n\n#define ngx_http_script_exit  (u_char *) &ngx_http_script_exit_code\n\nstatic uintptr_t ngx_http_script_exit_code = (uintptr_t) NULL;\n\n\nvoid\nngx_http_script_flush_complex_value(ngx_http_request_t *r,\n    ngx_http_complex_value_t *val)\n{\n    ngx_uint_t *index;\n\n    index = val->flushes;\n\n    if (index) {\n        while (*index != (ngx_uint_t) -1) {\n\n            if (r->variables[*index].no_cacheable) {\n                r->variables[*index].valid = 0;\n                r->variables[*index].not_found = 0;\n            }\n\n            index++;\n        }\n    }\n}\n\n\nngx_int_t\nngx_http_complex_value(ngx_http_request_t *r, ngx_http_complex_value_t *val,\n    ngx_str_t *value)\n{\n    size_t                        len;\n    ngx_http_script_code_pt       code;\n    ngx_http_script_len_code_pt   lcode;\n    ngx_http_script_engine_t      e;\n\n    if (val->lengths == NULL) {\n        *value = val->value;\n        return NGX_OK;\n    }\n\n    ngx_http_script_flush_complex_value(r, val);\n\n    ngx_memzero(&e, sizeof(ngx_http_script_engine_t));\n\n    e.ip = val->lengths;\n    e.request = r;\n    e.flushed = 1;\n\n    len = 0;\n\n    while (*(uintptr_t *) e.ip) {\n        lcode = *(ngx_http_script_len_code_pt *) e.ip;\n        len += lcode(&e);\n    }\n\n    value->len = len;\n    value->data = ngx_pnalloc(r->pool, len);\n    if (value->data == NULL) {\n        return NGX_ERROR;\n    }\n\n    e.ip = val->values;\n    e.pos = value->data;\n    e.buf = *value;\n\n    while (*(uintptr_t *) e.ip) {\n        code = *(ngx_http_script_code_pt *) e.ip;\n        code((ngx_http_script_engine_t *) &e);\n    }\n\n    *value = e.buf;\n\n    return NGX_OK;\n}\n\n\nsize_t\nngx_http_complex_value_size(ngx_http_request_t *r,\n    ngx_http_complex_value_t *val, size_t default_value)\n{\n    size_t     size;\n    ngx_str_t  value;\n\n    if (val == NULL) {\n        return default_value;\n    }\n\n    if (val->lengths == NULL) {\n        return val->u.size;\n    }\n\n    if (ngx_http_complex_value(r, val, &value) != NGX_OK) {\n        return default_value;\n    }\n\n    size = ngx_parse_size(&value);\n\n    if (size == (size_t) NGX_ERROR) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"invalid size \\\"%V\\\"\", &value);\n        return default_value;\n    }\n\n    return size;\n}\n\n\nngx_int_t\nngx_http_compile_complex_value(ngx_http_compile_complex_value_t *ccv)\n{\n    ngx_str_t                  *v;\n    ngx_uint_t                  i, n, nv, nc;\n    ngx_array_t                 flushes, lengths, values, *pf, *pl, *pv;\n    ngx_http_script_compile_t   sc;\n\n    v = ccv->value;\n\n    nv = 0;\n    nc = 0;\n\n    for (i = 0; i < v->len; i++) {\n        if (v->data[i] == '$') {\n            if (v->data[i + 1] >= '1' && v->data[i + 1] <= '9') {\n                nc++;\n\n            } else {\n                nv++;\n            }\n        }\n    }\n\n    if ((v->len == 0 || v->data[0] != '$')\n        && (ccv->conf_prefix || ccv->root_prefix))\n    {\n        if (ngx_conf_full_name(ccv->cf->cycle, v, ccv->conf_prefix) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        ccv->conf_prefix = 0;\n        ccv->root_prefix = 0;\n    }\n\n    ccv->complex_value->value = *v;\n    ccv->complex_value->flushes = NULL;\n    ccv->complex_value->lengths = NULL;\n    ccv->complex_value->values = NULL;\n\n    if (nv == 0 && nc == 0) {\n        return NGX_OK;\n    }\n\n    n = nv + 1;\n\n    if (ngx_array_init(&flushes, ccv->cf->pool, n, sizeof(ngx_uint_t))\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    n = nv * (2 * sizeof(ngx_http_script_copy_code_t)\n                  + sizeof(ngx_http_script_var_code_t))\n        + sizeof(uintptr_t);\n\n    if (ngx_array_init(&lengths, ccv->cf->pool, n, 1) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    n = (nv * (2 * sizeof(ngx_http_script_copy_code_t)\n                   + sizeof(ngx_http_script_var_code_t))\n                + sizeof(uintptr_t)\n                + v->len\n                + sizeof(uintptr_t) - 1)\n            & ~(sizeof(uintptr_t) - 1);\n\n    if (ngx_array_init(&values, ccv->cf->pool, n, 1) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    pf = &flushes;\n    pl = &lengths;\n    pv = &values;\n\n    ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));\n\n    sc.cf = ccv->cf;\n    sc.source = v;\n    sc.flushes = &pf;\n    sc.lengths = &pl;\n    sc.values = &pv;\n    sc.complete_lengths = 1;\n    sc.complete_values = 1;\n    sc.zero = ccv->zero;\n    sc.conf_prefix = ccv->conf_prefix;\n    sc.root_prefix = ccv->root_prefix;\n\n    if (ngx_http_script_compile(&sc) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    if (flushes.nelts) {\n        ccv->complex_value->flushes = flushes.elts;\n        ccv->complex_value->flushes[flushes.nelts] = (ngx_uint_t) -1;\n    }\n\n    ccv->complex_value->lengths = lengths.elts;\n    ccv->complex_value->values = values.elts;\n\n    return NGX_OK;\n}\n\n\nchar *\nngx_http_set_complex_value_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    char  *p = conf;\n\n    ngx_str_t                          *value;\n    ngx_http_complex_value_t          **cv;\n    ngx_http_compile_complex_value_t    ccv;\n\n    cv = (ngx_http_complex_value_t **) (p + cmd->offset);\n\n    if (*cv != NGX_CONF_UNSET_PTR && *cv != NULL) {\n        return \"is duplicate\";\n    }\n\n    *cv = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));\n    if (*cv == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    value = cf->args->elts;\n\n    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n\n    ccv.cf = cf;\n    ccv.value = &value[1];\n    ccv.complex_value = *cv;\n\n    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nchar *\nngx_http_set_complex_value_zero_slot(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n    char  *p = conf;\n\n    ngx_str_t                          *value;\n    ngx_http_complex_value_t          **cv;\n    ngx_http_compile_complex_value_t    ccv;\n\n    cv = (ngx_http_complex_value_t **) (p + cmd->offset);\n\n    if (*cv != NGX_CONF_UNSET_PTR) {\n        return \"is duplicate\";\n    }\n\n    *cv = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));\n    if (*cv == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    value = cf->args->elts;\n\n    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n\n    ccv.cf = cf;\n    ccv.value = &value[1];\n    ccv.complex_value = *cv;\n    ccv.zero = 1;\n\n    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nchar *\nngx_http_set_complex_value_size_slot(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n    char  *p = conf;\n\n    char                      *rv;\n    ngx_http_complex_value_t  *cv;\n\n    rv = ngx_http_set_complex_value_slot(cf, cmd, conf);\n\n    if (rv != NGX_CONF_OK) {\n        return rv;\n    }\n\n    cv = *(ngx_http_complex_value_t **) (p + cmd->offset);\n\n    if (cv->lengths) {\n        return NGX_CONF_OK;\n    }\n\n    cv->u.size = ngx_parse_size(&cv->value);\n    if (cv->u.size == (size_t) NGX_ERROR) {\n        return \"invalid value\";\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nngx_int_t\nngx_http_test_predicates(ngx_http_request_t *r, ngx_array_t *predicates)\n{\n    ngx_str_t                  val;\n    ngx_uint_t                 i;\n    ngx_http_complex_value_t  *cv;\n\n    if (predicates == NULL) {\n        return NGX_OK;\n    }\n\n    cv = predicates->elts;\n\n    for (i = 0; i < predicates->nelts; i++) {\n        if (ngx_http_complex_value(r, &cv[i], &val) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        if (val.len && (val.len != 1 || val.data[0] != '0')) {\n            return NGX_DECLINED;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_test_required_predicates(ngx_http_request_t *r,\n    ngx_array_t *predicates)\n{\n    ngx_str_t                  val;\n    ngx_uint_t                 i;\n    ngx_http_complex_value_t  *cv;\n\n    if (predicates == NULL) {\n        return NGX_OK;\n    }\n\n    cv = predicates->elts;\n\n    for (i = 0; i < predicates->nelts; i++) {\n        if (ngx_http_complex_value(r, &cv[i], &val) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        if (val.len == 0 || (val.len == 1 && val.data[0] == '0')) {\n            return NGX_DECLINED;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nchar *\nngx_http_set_predicate_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    char  *p = conf;\n\n    ngx_str_t                          *value;\n    ngx_uint_t                          i;\n    ngx_array_t                       **a;\n    ngx_http_complex_value_t           *cv;\n    ngx_http_compile_complex_value_t    ccv;\n\n    a = (ngx_array_t **) (p + cmd->offset);\n\n    if (*a == NGX_CONF_UNSET_PTR) {\n        *a = ngx_array_create(cf->pool, 1, sizeof(ngx_http_complex_value_t));\n        if (*a == NULL) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    value = cf->args->elts;\n\n    for (i = 1; i < cf->args->nelts; i++) {\n        cv = ngx_array_push(*a);\n        if (cv == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n\n        ccv.cf = cf;\n        ccv.value = &value[i];\n        ccv.complex_value = cv;\n\n        if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nngx_uint_t\nngx_http_script_variables_count(ngx_str_t *value)\n{\n    ngx_uint_t  i, n;\n\n    for (n = 0, i = 0; i < value->len; i++) {\n        if (value->data[i] == '$') {\n            n++;\n        }\n    }\n\n    return n;\n}\n\n\nngx_int_t\nngx_http_script_compile(ngx_http_script_compile_t *sc)\n{\n    u_char       ch;\n    ngx_str_t    name;\n    ngx_uint_t   i, bracket;\n\n    if (ngx_http_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] >= '1' && sc->source->data[i] <= '9') {\n#if (NGX_PCRE)\n                ngx_uint_t  n;\n\n                n = sc->source->data[i] - '0';\n\n                if (sc->captures_mask & ((ngx_uint_t) 1 << n)) {\n                    sc->dup_capture = 1;\n                }\n\n                sc->captures_mask |= (ngx_uint_t) 1 << n;\n\n                if (ngx_http_script_add_capture_code(sc, n) != NGX_OK) {\n                    return NGX_ERROR;\n                }\n\n                i++;\n\n                continue;\n#else\n                ngx_conf_log_error(NGX_LOG_EMERG, sc->cf, 0,\n                                   \"using variable \\\"$%c\\\" requires \"\n                                   \"PCRE library\", sc->source->data[i]);\n                return NGX_ERROR;\n#endif\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                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 ((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_conf_log_error(NGX_LOG_EMERG, sc->cf, 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            sc->variables++;\n\n            if (ngx_http_script_add_var_code(sc, &name) != NGX_OK) {\n                return NGX_ERROR;\n            }\n\n            continue;\n        }\n\n        if (sc->source->data[i] == '?' && sc->compile_args) {\n            sc->args = 1;\n            sc->compile_args = 0;\n\n            if (ngx_http_script_add_args_code(sc) != NGX_OK) {\n                return NGX_ERROR;\n            }\n\n            i++;\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            if (sc->source->data[i] == '?') {\n\n                sc->args = 1;\n\n                if (sc->compile_args) {\n                    break;\n                }\n            }\n\n            i++;\n            name.len++;\n        }\n\n        sc->size += name.len;\n\n        if (ngx_http_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_script_done(sc);\n\ninvalid_variable:\n\n    ngx_conf_log_error(NGX_LOG_EMERG, sc->cf, 0, \"invalid variable name\");\n\n    return NGX_ERROR;\n}\n\n\nu_char *\nngx_http_script_run(ngx_http_request_t *r, ngx_str_t *value,\n    void *code_lengths, size_t len, void *code_values)\n{\n    ngx_uint_t                    i;\n    ngx_http_script_code_pt       code;\n    ngx_http_script_len_code_pt   lcode;\n    ngx_http_script_engine_t      e;\n    ngx_http_core_main_conf_t    *cmcf;\n\n    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);\n\n    for (i = 0; i < cmcf->variables.nelts; i++) {\n        if (r->variables[i].no_cacheable) {\n            r->variables[i].valid = 0;\n            r->variables[i].not_found = 0;\n        }\n    }\n\n    ngx_memzero(&e, sizeof(ngx_http_script_engine_t));\n\n    e.ip = code_lengths;\n    e.request = r;\n    e.flushed = 1;\n\n    while (*(uintptr_t *) e.ip) {\n        lcode = *(ngx_http_script_len_code_pt *) e.ip;\n        len += lcode(&e);\n    }\n\n\n    value->len = len;\n    value->data = ngx_pnalloc(r->pool, len);\n    if (value->data == NULL) {\n        return NULL;\n    }\n\n    e.ip = code_values;\n    e.pos = value->data;\n\n    while (*(uintptr_t *) e.ip) {\n        code = *(ngx_http_script_code_pt *) e.ip;\n        code((ngx_http_script_engine_t *) &e);\n    }\n\n    return e.pos;\n}\n\n\nvoid\nngx_http_script_flush_no_cacheable_variables(ngx_http_request_t *r,\n    ngx_array_t *indices)\n{\n    ngx_uint_t  n, *index;\n\n    if (indices) {\n        index = indices->elts;\n        for (n = 0; n < indices->nelts; n++) {\n            if (r->variables[index[n]].no_cacheable) {\n                r->variables[index[n]].valid = 0;\n                r->variables[index[n]].not_found = 0;\n            }\n        }\n    }\n}\n\n\nstatic ngx_int_t\nngx_http_script_init_arrays(ngx_http_script_compile_t *sc)\n{\n    ngx_uint_t   n;\n\n    if (sc->flushes && *sc->flushes == NULL) {\n        n = sc->variables ? sc->variables : 1;\n        *sc->flushes = ngx_array_create(sc->cf->pool, n, sizeof(ngx_uint_t));\n        if (*sc->flushes == NULL) {\n            return NGX_ERROR;\n        }\n    }\n\n    if (*sc->lengths == NULL) {\n        n = sc->variables * (2 * sizeof(ngx_http_script_copy_code_t)\n                             + sizeof(ngx_http_script_var_code_t))\n            + sizeof(uintptr_t);\n\n        *sc->lengths = ngx_array_create(sc->cf->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_script_copy_code_t)\n                              + sizeof(ngx_http_script_var_code_t))\n                + sizeof(uintptr_t)\n                + sc->source->len\n                + sizeof(uintptr_t) - 1)\n            & ~(sizeof(uintptr_t) - 1);\n\n        *sc->values = ngx_array_create(sc->cf->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_script_done(ngx_http_script_compile_t *sc)\n{\n    ngx_str_t    zero;\n    uintptr_t   *code;\n\n    if (sc->zero) {\n\n        zero.len = 1;\n        zero.data = (u_char *) \"\\0\";\n\n        if (ngx_http_script_add_copy_code(sc, &zero, 0) != NGX_OK) {\n            return NGX_ERROR;\n        }\n    }\n\n    if (sc->conf_prefix || sc->root_prefix) {\n        if (ngx_http_script_add_full_name_code(sc) != NGX_OK) {\n            return NGX_ERROR;\n        }\n    }\n\n    if (sc->complete_lengths) {\n        code = ngx_http_script_add_code(*sc->lengths, sizeof(uintptr_t), NULL);\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_script_add_code(*sc->values, sizeof(uintptr_t),\n                                        &sc->main);\n        if (code == NULL) {\n            return NGX_ERROR;\n        }\n\n        *code = (uintptr_t) NULL;\n    }\n\n    return NGX_OK;\n}\n\n\nvoid *\nngx_http_script_start_code(ngx_pool_t *pool, ngx_array_t **codes, size_t size)\n{\n    if (*codes == NULL) {\n        *codes = ngx_array_create(pool, 256, 1);\n        if (*codes == NULL) {\n            return NULL;\n        }\n    }\n\n    return ngx_array_push_n(*codes, size);\n}\n\n\nvoid *\nngx_http_script_add_code(ngx_array_t *codes, size_t size, void *code)\n{\n    u_char  *elts, **p;\n    void    *new;\n\n    elts = codes->elts;\n\n    new = ngx_array_push_n(codes, size);\n    if (new == NULL) {\n        return NULL;\n    }\n\n    if (code) {\n        if (elts != codes->elts) {\n            p = code;\n            *p += (u_char *) codes->elts - elts;\n        }\n    }\n\n    return new;\n}\n\n\nstatic ngx_int_t\nngx_http_script_add_copy_code(ngx_http_script_compile_t *sc, ngx_str_t *value,\n    ngx_uint_t last)\n{\n    u_char                       *p;\n    size_t                        size, len, zero;\n    ngx_http_script_copy_code_t  *code;\n\n    zero = (sc->zero && last);\n    len = value->len + zero;\n\n    code = ngx_http_script_add_code(*sc->lengths,\n                                    sizeof(ngx_http_script_copy_code_t), NULL);\n    if (code == NULL) {\n        return NGX_ERROR;\n    }\n\n    code->code = (ngx_http_script_code_pt) (void *)\n                                                 ngx_http_script_copy_len_code;\n    code->len = len;\n\n    size = (sizeof(ngx_http_script_copy_code_t) + len + sizeof(uintptr_t) - 1)\n            & ~(sizeof(uintptr_t) - 1);\n\n    code = ngx_http_script_add_code(*sc->values, size, &sc->main);\n    if (code == NULL) {\n        return NGX_ERROR;\n    }\n\n    code->code = ngx_http_script_copy_code;\n    code->len = len;\n\n    p = ngx_cpymem((u_char *) code + sizeof(ngx_http_script_copy_code_t),\n                   value->data, value->len);\n\n    if (zero) {\n        *p = '\\0';\n        sc->zero = 0;\n    }\n\n    return NGX_OK;\n}\n\n\nsize_t\nngx_http_script_copy_len_code(ngx_http_script_engine_t *e)\n{\n    ngx_http_script_copy_code_t  *code;\n\n    code = (ngx_http_script_copy_code_t *) e->ip;\n\n    e->ip += sizeof(ngx_http_script_copy_code_t);\n\n    return code->len;\n}\n\n\nvoid\nngx_http_script_copy_code(ngx_http_script_engine_t *e)\n{\n    u_char                       *p;\n    ngx_http_script_copy_code_t  *code;\n\n    code = (ngx_http_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_script_copy_code_t),\n                          code->len);\n    }\n\n    e->ip += sizeof(ngx_http_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->request->connection->log, 0,\n                   \"http script copy: \\\"%*s\\\"\", e->pos - p, p);\n}\n\n\nstatic ngx_int_t\nngx_http_script_add_var_code(ngx_http_script_compile_t *sc, ngx_str_t *name)\n{\n    ngx_int_t                    index, *p;\n    ngx_http_script_var_code_t  *code;\n\n    index = ngx_http_get_variable_index(sc->cf, name);\n\n    if (index == NGX_ERROR) {\n        return NGX_ERROR;\n    }\n\n    if (sc->flushes) {\n        p = ngx_array_push(*sc->flushes);\n        if (p == NULL) {\n            return NGX_ERROR;\n        }\n\n        *p = index;\n    }\n\n    code = ngx_http_script_add_code(*sc->lengths,\n                                    sizeof(ngx_http_script_var_code_t), NULL);\n    if (code == NULL) {\n        return NGX_ERROR;\n    }\n\n    code->code = (ngx_http_script_code_pt) (void *)\n                                             ngx_http_script_copy_var_len_code;\n    code->index = (uintptr_t) index;\n\n    code = ngx_http_script_add_code(*sc->values,\n                                    sizeof(ngx_http_script_var_code_t),\n                                    &sc->main);\n    if (code == NULL) {\n        return NGX_ERROR;\n    }\n\n    code->code = ngx_http_script_copy_var_code;\n    code->index = (uintptr_t) index;\n\n    return NGX_OK;\n}\n\n\nsize_t\nngx_http_script_copy_var_len_code(ngx_http_script_engine_t *e)\n{\n    ngx_http_variable_value_t   *value;\n    ngx_http_script_var_code_t  *code;\n\n    code = (ngx_http_script_var_code_t *) e->ip;\n\n    e->ip += sizeof(ngx_http_script_var_code_t);\n\n    if (e->flushed) {\n        value = ngx_http_get_indexed_variable(e->request, code->index);\n\n    } else {\n        value = ngx_http_get_flushed_variable(e->request, code->index);\n    }\n\n    if (value && !value->not_found) {\n        return value->len;\n    }\n\n    return 0;\n}\n\n\nvoid\nngx_http_script_copy_var_code(ngx_http_script_engine_t *e)\n{\n    u_char                      *p;\n    ngx_http_variable_value_t   *value;\n    ngx_http_script_var_code_t  *code;\n\n    code = (ngx_http_script_var_code_t *) e->ip;\n\n    e->ip += sizeof(ngx_http_script_var_code_t);\n\n    if (!e->skip) {\n\n        if (e->flushed) {\n            value = ngx_http_get_indexed_variable(e->request, code->index);\n\n        } else {\n            value = ngx_http_get_flushed_variable(e->request, code->index);\n        }\n\n        if (value && !value->not_found) {\n            p = e->pos;\n            e->pos = ngx_copy(p, value->data, value->len);\n\n            ngx_log_debug2(NGX_LOG_DEBUG_HTTP,\n                           e->request->connection->log, 0,\n                           \"http script var: \\\"%*s\\\"\", e->pos - p, p);\n        }\n    }\n}\n\n\nstatic ngx_int_t\nngx_http_script_add_args_code(ngx_http_script_compile_t *sc)\n{\n    uintptr_t   *code;\n\n    code = ngx_http_script_add_code(*sc->lengths, sizeof(uintptr_t), NULL);\n    if (code == NULL) {\n        return NGX_ERROR;\n    }\n\n    *code = (uintptr_t) ngx_http_script_mark_args_code;\n\n    code = ngx_http_script_add_code(*sc->values, sizeof(uintptr_t), &sc->main);\n    if (code == NULL) {\n        return NGX_ERROR;\n    }\n\n    *code = (uintptr_t) ngx_http_script_start_args_code;\n\n    return NGX_OK;\n}\n\n\nsize_t\nngx_http_script_mark_args_code(ngx_http_script_engine_t *e)\n{\n    e->is_args = 1;\n    e->ip += sizeof(uintptr_t);\n\n    return 1;\n}\n\n\nvoid\nngx_http_script_start_args_code(ngx_http_script_engine_t *e)\n{\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,\n                   \"http script args\");\n\n    e->is_args = 1;\n    e->args = e->pos;\n    e->ip += sizeof(uintptr_t);\n}\n\n\n#if (NGX_PCRE)\n\nvoid\nngx_http_script_regex_start_code(ngx_http_script_engine_t *e)\n{\n    size_t                         len;\n    ngx_int_t                      rc;\n    ngx_uint_t                     n;\n    ngx_http_request_t            *r;\n    ngx_http_script_engine_t       le;\n    ngx_http_script_len_code_pt    lcode;\n    ngx_http_script_regex_code_t  *code;\n\n    code = (ngx_http_script_regex_code_t *) e->ip;\n\n    r = e->request;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http script regex: \\\"%V\\\"\", &code->name);\n\n    if (code->uri) {\n        e->line = r->uri;\n    } else {\n        e->sp--;\n        e->line.len = e->sp->len;\n        e->line.data = e->sp->data;\n    }\n\n    rc = ngx_http_regex_exec(r, code->regex, &e->line);\n\n    if (rc == NGX_DECLINED) {\n        if (e->log || (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP)) {\n            ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0,\n                          \"\\\"%V\\\" does not match \\\"%V\\\"\",\n                          &code->name, &e->line);\n        }\n\n        r->ncaptures = 0;\n\n        if (code->test) {\n            if (code->negative_test) {\n                e->sp->len = 1;\n                e->sp->data = (u_char *) \"1\";\n\n            } else {\n                e->sp->len = 0;\n                e->sp->data = (u_char *) \"\";\n            }\n\n            e->sp++;\n\n            e->ip += sizeof(ngx_http_script_regex_code_t);\n            return;\n        }\n\n        e->ip += code->next;\n        return;\n    }\n\n    if (rc == NGX_ERROR) {\n        e->ip = ngx_http_script_exit;\n        e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;\n        return;\n    }\n\n    if (e->log || (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP)) {\n        ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0,\n                      \"\\\"%V\\\" matches \\\"%V\\\"\", &code->name, &e->line);\n    }\n\n    if (code->test) {\n        if (code->negative_test) {\n            e->sp->len = 0;\n            e->sp->data = (u_char *) \"\";\n\n        } else {\n            e->sp->len = 1;\n            e->sp->data = (u_char *) \"1\";\n        }\n\n        e->sp++;\n\n        e->ip += sizeof(ngx_http_script_regex_code_t);\n        return;\n    }\n\n    if (code->status) {\n        e->status = code->status;\n\n        if (!code->redirect) {\n            e->ip = ngx_http_script_exit;\n            return;\n        }\n    }\n\n    if (code->uri) {\n        r->internal = 1;\n        r->valid_unparsed_uri = 0;\n\n        if (code->break_cycle) {\n            r->valid_location = 0;\n            r->uri_changed = 0;\n\n        } else {\n            r->uri_changed = 1;\n        }\n    }\n\n    if (code->lengths == NULL) {\n        e->buf.len = code->size;\n\n        if (code->uri) {\n            if (r->ncaptures && (r->quoted_uri || r->plus_in_uri)) {\n                e->buf.len += 2 * ngx_escape_uri(NULL, r->uri.data, r->uri.len,\n                                                 NGX_ESCAPE_ARGS);\n            }\n        }\n\n        for (n = 2; n < r->ncaptures; n += 2) {\n            e->buf.len += r->captures[n + 1] - r->captures[n];\n        }\n\n    } else {\n        ngx_memzero(&le, sizeof(ngx_http_script_engine_t));\n\n        le.ip = code->lengths->elts;\n        le.line = e->line;\n        le.request = r;\n        le.quote = code->redirect;\n\n        len = 0;\n\n        while (*(uintptr_t *) le.ip) {\n            lcode = *(ngx_http_script_len_code_pt *) le.ip;\n            len += lcode(&le);\n        }\n\n        e->buf.len = len;\n    }\n\n    if (code->add_args && r->args.len) {\n        e->buf.len += r->args.len + 1;\n    }\n\n    e->buf.data = ngx_pnalloc(r->pool, e->buf.len);\n    if (e->buf.data == NULL) {\n        e->ip = ngx_http_script_exit;\n        e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;\n        return;\n    }\n\n    e->quote = code->redirect;\n\n    e->pos = e->buf.data;\n\n    e->ip += sizeof(ngx_http_script_regex_code_t);\n}\n\n\nvoid\nngx_http_script_regex_end_code(ngx_http_script_engine_t *e)\n{\n    u_char                            *dst, *src;\n    ngx_http_request_t                *r;\n    ngx_http_script_regex_end_code_t  *code;\n\n    code = (ngx_http_script_regex_end_code_t *) e->ip;\n\n    r = e->request;\n\n    e->quote = 0;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http script regex end\");\n\n    if (code->redirect) {\n\n        dst = e->buf.data;\n        src = e->buf.data;\n\n        ngx_unescape_uri(&dst, &src, e->pos - e->buf.data,\n                         NGX_UNESCAPE_REDIRECT);\n\n        if (src < e->pos) {\n            dst = ngx_movemem(dst, src, e->pos - src);\n        }\n\n        e->pos = dst;\n\n        if (code->add_args && r->args.len) {\n            *e->pos++ = (u_char) (code->args ? '&' : '?');\n            e->pos = ngx_copy(e->pos, r->args.data, r->args.len);\n        }\n\n        e->buf.len = e->pos - e->buf.data;\n\n        if (e->log || (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP)) {\n            ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0,\n                          \"rewritten redirect: \\\"%V\\\"\", &e->buf);\n        }\n\n        ngx_http_clear_location(r);\n\n        r->headers_out.location = ngx_list_push(&r->headers_out.headers);\n        if (r->headers_out.location == NULL) {\n            e->ip = ngx_http_script_exit;\n            e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;\n            return;\n        }\n\n        r->headers_out.location->hash = 1;\n        r->headers_out.location->next = NULL;\n        ngx_str_set(&r->headers_out.location->key, \"Location\");\n        r->headers_out.location->value = e->buf;\n\n        e->ip += sizeof(ngx_http_script_regex_end_code_t);\n        return;\n    }\n\n    if (e->args) {\n        e->buf.len = e->args - e->buf.data;\n\n        if (code->add_args && r->args.len) {\n            *e->pos++ = '&';\n            e->pos = ngx_copy(e->pos, r->args.data, r->args.len);\n        }\n\n        r->args.len = e->pos - e->args;\n        r->args.data = e->args;\n\n        e->args = NULL;\n\n    } else {\n        e->buf.len = e->pos - e->buf.data;\n\n        if (!code->add_args) {\n            r->args.len = 0;\n        }\n    }\n\n    if (e->log || (r->connection->log->log_level & NGX_LOG_DEBUG_HTTP)) {\n        ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0,\n                      \"rewritten data: \\\"%V\\\", args: \\\"%V\\\"\",\n                      &e->buf, &r->args);\n    }\n\n    if (code->uri) {\n        r->uri = e->buf;\n\n        if (r->uri.len == 0) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"the rewritten URI has a zero length\");\n            e->ip = ngx_http_script_exit;\n            e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;\n            return;\n        }\n\n        ngx_http_set_exten(r);\n    }\n\n    e->ip += sizeof(ngx_http_script_regex_end_code_t);\n}\n\n\nstatic ngx_int_t\nngx_http_script_add_capture_code(ngx_http_script_compile_t *sc, ngx_uint_t n)\n{\n    ngx_http_script_copy_capture_code_t  *code;\n\n    code = ngx_http_script_add_code(*sc->lengths,\n                                    sizeof(ngx_http_script_copy_capture_code_t),\n                                    NULL);\n    if (code == NULL) {\n        return NGX_ERROR;\n    }\n\n    code->code = (ngx_http_script_code_pt) (void *)\n                                         ngx_http_script_copy_capture_len_code;\n    code->n = 2 * n;\n\n\n    code = ngx_http_script_add_code(*sc->values,\n                                    sizeof(ngx_http_script_copy_capture_code_t),\n                                    &sc->main);\n    if (code == NULL) {\n        return NGX_ERROR;\n    }\n\n    code->code = ngx_http_script_copy_capture_code;\n    code->n = 2 * n;\n\n    if (sc->ncaptures < n) {\n        sc->ncaptures = n;\n    }\n\n    return NGX_OK;\n}\n\n\nsize_t\nngx_http_script_copy_capture_len_code(ngx_http_script_engine_t *e)\n{\n    int                                  *cap;\n    u_char                               *p;\n    ngx_uint_t                            n;\n    ngx_http_request_t                   *r;\n    ngx_http_script_copy_capture_code_t  *code;\n\n    r = e->request;\n\n    code = (ngx_http_script_copy_capture_code_t *) e->ip;\n\n    e->ip += sizeof(ngx_http_script_copy_capture_code_t);\n\n    n = code->n;\n\n    if (n < r->ncaptures) {\n\n        cap = r->captures;\n\n        if ((e->is_args || e->quote)\n            && (e->request->quoted_uri || e->request->plus_in_uri))\n        {\n            p = r->captures_data;\n\n            return cap[n + 1] - cap[n]\n                   + 2 * ngx_escape_uri(NULL, &p[cap[n]], cap[n + 1] - cap[n],\n                                        NGX_ESCAPE_ARGS);\n        } else {\n            return cap[n + 1] - cap[n];\n        }\n    }\n\n    return 0;\n}\n\n\nvoid\nngx_http_script_copy_capture_code(ngx_http_script_engine_t *e)\n{\n    int                                  *cap;\n    u_char                               *p, *pos;\n    ngx_uint_t                            n;\n    ngx_http_request_t                   *r;\n    ngx_http_script_copy_capture_code_t  *code;\n\n    r = e->request;\n\n    code = (ngx_http_script_copy_capture_code_t *) e->ip;\n\n    e->ip += sizeof(ngx_http_script_copy_capture_code_t);\n\n    n = code->n;\n\n    pos = e->pos;\n\n    if (n < r->ncaptures) {\n\n        cap = r->captures;\n        p = r->captures_data;\n\n        if ((e->is_args || e->quote)\n            && (e->request->quoted_uri || e->request->plus_in_uri))\n        {\n            e->pos = (u_char *) ngx_escape_uri(pos, &p[cap[n]],\n                                               cap[n + 1] - cap[n],\n                                               NGX_ESCAPE_ARGS);\n        } else {\n            e->pos = ngx_copy(pos, &p[cap[n]], cap[n + 1] - cap[n]);\n        }\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,\n                   \"http script capture: \\\"%*s\\\"\", e->pos - pos, pos);\n}\n\n#endif\n\n\nstatic ngx_int_t\nngx_http_script_add_full_name_code(ngx_http_script_compile_t *sc)\n{\n    ngx_http_script_full_name_code_t  *code;\n\n    code = ngx_http_script_add_code(*sc->lengths,\n                                    sizeof(ngx_http_script_full_name_code_t),\n                                    NULL);\n    if (code == NULL) {\n        return NGX_ERROR;\n    }\n\n    code->code = (ngx_http_script_code_pt) (void *)\n                                            ngx_http_script_full_name_len_code;\n    code->conf_prefix = sc->conf_prefix;\n\n    code = ngx_http_script_add_code(*sc->values,\n                                    sizeof(ngx_http_script_full_name_code_t),\n                                    &sc->main);\n    if (code == NULL) {\n        return NGX_ERROR;\n    }\n\n    code->code = ngx_http_script_full_name_code;\n    code->conf_prefix = sc->conf_prefix;\n\n    return NGX_OK;\n}\n\n\nstatic size_t\nngx_http_script_full_name_len_code(ngx_http_script_engine_t *e)\n{\n    ngx_http_script_full_name_code_t  *code;\n\n    code = (ngx_http_script_full_name_code_t *) e->ip;\n\n    e->ip += sizeof(ngx_http_script_full_name_code_t);\n\n    return code->conf_prefix ? ngx_cycle->conf_prefix.len:\n                               ngx_cycle->prefix.len;\n}\n\n\nstatic void\nngx_http_script_full_name_code(ngx_http_script_engine_t *e)\n{\n    ngx_http_script_full_name_code_t  *code;\n\n    ngx_str_t  value, *prefix;\n\n    code = (ngx_http_script_full_name_code_t *) e->ip;\n\n    value.data = e->buf.data;\n    value.len = e->pos - e->buf.data;\n\n    prefix = code->conf_prefix ? (ngx_str_t *) &ngx_cycle->conf_prefix:\n                                 (ngx_str_t *) &ngx_cycle->prefix;\n\n    if (ngx_get_full_name(e->request->pool, prefix, &value) != NGX_OK) {\n        e->ip = ngx_http_script_exit;\n        e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;\n        return;\n    }\n\n    e->buf = value;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,\n                   \"http script fullname: \\\"%V\\\"\", &value);\n\n    e->ip += sizeof(ngx_http_script_full_name_code_t);\n}\n\n\nvoid\nngx_http_script_return_code(ngx_http_script_engine_t *e)\n{\n    ngx_http_script_return_code_t  *code;\n\n    code = (ngx_http_script_return_code_t *) e->ip;\n\n    if (code->status < NGX_HTTP_BAD_REQUEST\n        || code->text.value.len\n        || code->text.lengths)\n    {\n        e->status = ngx_http_send_response(e->request, code->status, NULL,\n                                           &code->text);\n    } else {\n        e->status = code->status;\n    }\n\n    e->ip = ngx_http_script_exit;\n}\n\n\nvoid\nngx_http_script_break_code(ngx_http_script_engine_t *e)\n{\n    ngx_http_request_t  *r;\n\n    r = e->request;\n\n    if (r->uri_changed) {\n        r->valid_location = 0;\n        r->uri_changed = 0;\n    }\n\n    e->ip = ngx_http_script_exit;\n}\n\n\nvoid\nngx_http_script_if_code(ngx_http_script_engine_t *e)\n{\n    ngx_http_script_if_code_t  *code;\n\n    code = (ngx_http_script_if_code_t *) e->ip;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,\n                   \"http script if\");\n\n    e->sp--;\n\n    if (e->sp->len && (e->sp->len != 1 || e->sp->data[0] != '0')) {\n        if (code->loc_conf) {\n            e->request->loc_conf = code->loc_conf;\n            ngx_http_update_location_config(e->request);\n        }\n\n        e->ip += sizeof(ngx_http_script_if_code_t);\n        return;\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,\n                   \"http script if: false\");\n\n    e->ip += code->next;\n}\n\n\nvoid\nngx_http_script_equal_code(ngx_http_script_engine_t *e)\n{\n    ngx_http_variable_value_t  *val, *res;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,\n                   \"http script equal\");\n\n    e->sp--;\n    val = e->sp;\n    res = e->sp - 1;\n\n    e->ip += sizeof(uintptr_t);\n\n    if (val->len == res->len\n        && ngx_strncmp(val->data, res->data, res->len) == 0)\n    {\n        *res = ngx_http_variable_true_value;\n        return;\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,\n                   \"http script equal: no\");\n\n    *res = ngx_http_variable_null_value;\n}\n\n\n#if (T_NGX_HTTP_IMPROVED_IF)\nvoid\nngx_http_script_greater_code(ngx_http_script_engine_t *e)\n{\n    int64_t                     val_n, res_n;\n    ngx_http_variable_value_t  *val, *res;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,\n                   \"http script greater\");\n\n    e->sp--;\n    val = e->sp;\n    res = e->sp - 1;\n\n    e->ip += sizeof(uintptr_t);\n\n    val_n = ngx_atoll(val->data, val->len);\n    if (val_n == NGX_ERROR) {\n        *res = ngx_http_variable_null_value;\n        return;\n    }\n\n    res_n = ngx_atoll(res->data, res->len);\n    if (res_n == NGX_ERROR) {\n        *res = ngx_http_variable_null_value;\n        return;\n    }\n\n    if (res_n > val_n) {\n        *res = ngx_http_variable_true_value;\n        return;\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,\n                   \"http script greater: no\");\n\n    *res = ngx_http_variable_null_value;\n}\n\n\nvoid\nngx_http_script_less_code(ngx_http_script_engine_t *e)\n{\n    int64_t                     val_n, res_n;\n    ngx_http_variable_value_t  *val, *res;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,\n                   \"http script less\");\n\n    e->sp--;\n    val = e->sp;\n    res = e->sp - 1;\n\n    e->ip += sizeof(uintptr_t);\n\n    val_n = ngx_atoll(val->data, val->len);\n    if (val_n == NGX_ERROR) {\n        *res = ngx_http_variable_null_value;\n        return;\n    }\n\n    res_n = ngx_atoll(res->data, res->len);\n    if (res_n == NGX_ERROR) {\n        *res = ngx_http_variable_null_value;\n        return;\n    }\n\n    if (res_n < val_n) {\n        *res = ngx_http_variable_true_value;\n        return;\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,\n                   \"http script less: no\");\n\n    *res = ngx_http_variable_null_value;\n}\n\n\nvoid\nngx_http_script_greater_or_equal_code(ngx_http_script_engine_t *e)\n{\n    int64_t                     val_n, res_n;\n    ngx_http_variable_value_t  *val, *res;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,\n                   \"http script greater or equal\");\n\n    e->sp--;\n    val = e->sp;\n    res = e->sp - 1;\n\n    e->ip += sizeof(uintptr_t);\n\n    val_n = ngx_atoll(val->data, val->len);\n    if (val_n == NGX_ERROR) {\n        *res = ngx_http_variable_null_value;\n        return;\n    }\n\n    res_n = ngx_atoll(res->data, res->len);\n    if (res_n == NGX_ERROR) {\n        *res = ngx_http_variable_null_value;\n        return;\n    }\n\n    if (res_n >= val_n) {\n        *res = ngx_http_variable_true_value;\n        return;\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,\n                   \"http script greater or equal: no\");\n\n    *res = ngx_http_variable_null_value;\n}\n\n\nvoid\nngx_http_script_less_or_equal_code(ngx_http_script_engine_t *e)\n{\n    int64_t                     val_n, res_n;\n    ngx_http_variable_value_t  *val, *res;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,\n                   \"http script less or equal\");\n\n    e->sp--;\n    val = e->sp;\n    res = e->sp - 1;\n\n    e->ip += sizeof(uintptr_t);\n\n    val_n = ngx_atoll(val->data, val->len);\n    if (val_n == NGX_ERROR) {\n        *res = ngx_http_variable_null_value;\n        return;\n    }\n\n    res_n = ngx_atoll(res->data, res->len);\n    if (res_n == NGX_ERROR) {\n        *res = ngx_http_variable_null_value;\n        return;\n    }\n\n    if (res_n <= val_n) {\n        *res = ngx_http_variable_true_value;\n        return;\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,\n                   \"http script less or equal: no\");\n\n    *res = ngx_http_variable_null_value;\n}\n#endif\n\n\nvoid\nngx_http_script_not_equal_code(ngx_http_script_engine_t *e)\n{\n    ngx_http_variable_value_t  *val, *res;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,\n                   \"http script not equal\");\n\n    e->sp--;\n    val = e->sp;\n    res = e->sp - 1;\n\n    e->ip += sizeof(uintptr_t);\n\n    if (val->len == res->len\n        && ngx_strncmp(val->data, res->data, res->len) == 0)\n    {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,\n                       \"http script not equal: no\");\n\n        *res = ngx_http_variable_null_value;\n        return;\n    }\n\n    *res = ngx_http_variable_true_value;\n}\n\n\nvoid\nngx_http_script_file_code(ngx_http_script_engine_t *e)\n{\n    ngx_str_t                     path;\n    ngx_http_request_t           *r;\n    ngx_open_file_info_t          of;\n    ngx_http_core_loc_conf_t     *clcf;\n    ngx_http_variable_value_t    *value;\n    ngx_http_script_file_code_t  *code;\n\n    value = e->sp - 1;\n\n    code = (ngx_http_script_file_code_t *) e->ip;\n    e->ip += sizeof(ngx_http_script_file_code_t);\n\n    path.len = value->len - 1;\n    path.data = value->data;\n\n    r = e->request;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http script file op %p \\\"%V\\\"\", (void *) code->op, &path);\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    ngx_memzero(&of, sizeof(ngx_open_file_info_t));\n\n    of.read_ahead = clcf->read_ahead;\n    of.directio = clcf->directio;\n    of.valid = clcf->open_file_cache_valid;\n    of.min_uses = clcf->open_file_cache_min_uses;\n    of.test_only = 1;\n    of.errors = clcf->open_file_cache_errors;\n    of.events = clcf->open_file_cache_events;\n\n    if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) {\n        e->ip = ngx_http_script_exit;\n        e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;\n        return;\n    }\n\n    if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)\n        != NGX_OK)\n    {\n        if (of.err == 0) {\n            e->ip = ngx_http_script_exit;\n            e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;\n            return;\n        }\n\n        if (of.err != NGX_ENOENT\n            && of.err != NGX_ENOTDIR\n            && of.err != NGX_ENAMETOOLONG)\n        {\n            ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err,\n                          \"%s \\\"%s\\\" failed\", of.failed, value->data);\n        }\n\n        switch (code->op) {\n\n        case ngx_http_script_file_plain:\n        case ngx_http_script_file_dir:\n        case ngx_http_script_file_exists:\n        case ngx_http_script_file_exec:\n             goto false_value;\n\n        case ngx_http_script_file_not_plain:\n        case ngx_http_script_file_not_dir:\n        case ngx_http_script_file_not_exists:\n        case ngx_http_script_file_not_exec:\n             goto true_value;\n        }\n\n        goto false_value;\n    }\n\n    switch (code->op) {\n    case ngx_http_script_file_plain:\n        if (of.is_file) {\n             goto true_value;\n        }\n        goto false_value;\n\n    case ngx_http_script_file_not_plain:\n        if (of.is_file) {\n            goto false_value;\n        }\n        goto true_value;\n\n    case ngx_http_script_file_dir:\n        if (of.is_dir) {\n             goto true_value;\n        }\n        goto false_value;\n\n    case ngx_http_script_file_not_dir:\n        if (of.is_dir) {\n            goto false_value;\n        }\n        goto true_value;\n\n    case ngx_http_script_file_exists:\n        if (of.is_file || of.is_dir || of.is_link) {\n             goto true_value;\n        }\n        goto false_value;\n\n    case ngx_http_script_file_not_exists:\n        if (of.is_file || of.is_dir || of.is_link) {\n            goto false_value;\n        }\n        goto true_value;\n\n    case ngx_http_script_file_exec:\n        if (of.is_exec) {\n             goto true_value;\n        }\n        goto false_value;\n\n    case ngx_http_script_file_not_exec:\n        if (of.is_exec) {\n            goto false_value;\n        }\n        goto true_value;\n    }\n\nfalse_value:\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http script file op false\");\n\n    *value = ngx_http_variable_null_value;\n    return;\n\ntrue_value:\n\n    *value = ngx_http_variable_true_value;\n    return;\n}\n\n\nvoid\nngx_http_script_complex_value_code(ngx_http_script_engine_t *e)\n{\n    size_t                                 len;\n    ngx_http_script_engine_t               le;\n    ngx_http_script_len_code_pt            lcode;\n    ngx_http_script_complex_value_code_t  *code;\n\n    code = (ngx_http_script_complex_value_code_t *) e->ip;\n\n    e->ip += sizeof(ngx_http_script_complex_value_code_t);\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,\n                   \"http script complex value\");\n\n    ngx_memzero(&le, sizeof(ngx_http_script_engine_t));\n\n    le.ip = code->lengths->elts;\n    le.line = e->line;\n    le.request = e->request;\n    le.quote = e->quote;\n\n    for (len = 0; *(uintptr_t *) le.ip; len += lcode(&le)) {\n        lcode = *(ngx_http_script_len_code_pt *) le.ip;\n    }\n\n    e->buf.len = len;\n    e->buf.data = ngx_pnalloc(e->request->pool, len);\n    if (e->buf.data == NULL) {\n        e->ip = ngx_http_script_exit;\n        e->status = NGX_HTTP_INTERNAL_SERVER_ERROR;\n        return;\n    }\n\n    e->pos = e->buf.data;\n\n    e->sp->len = e->buf.len;\n    e->sp->data = e->buf.data;\n    e->sp++;\n}\n\n\nvoid\nngx_http_script_value_code(ngx_http_script_engine_t *e)\n{\n    ngx_http_script_value_code_t  *code;\n\n    code = (ngx_http_script_value_code_t *) e->ip;\n\n    e->ip += sizeof(ngx_http_script_value_code_t);\n\n    e->sp->len = code->text_len;\n    e->sp->data = (u_char *) code->text_data;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,\n                   \"http script value: \\\"%v\\\"\", e->sp);\n\n    e->sp++;\n}\n\n\nvoid\nngx_http_script_set_var_code(ngx_http_script_engine_t *e)\n{\n    ngx_http_request_t          *r;\n    ngx_http_script_var_code_t  *code;\n\n    code = (ngx_http_script_var_code_t *) e->ip;\n\n    e->ip += sizeof(ngx_http_script_var_code_t);\n\n    r = e->request;\n\n    e->sp--;\n\n    r->variables[code->index].len = e->sp->len;\n    r->variables[code->index].valid = 1;\n    r->variables[code->index].no_cacheable = 0;\n    r->variables[code->index].not_found = 0;\n    r->variables[code->index].data = e->sp->data;\n\n#if (NGX_DEBUG)\n    {\n    ngx_http_variable_t        *v;\n    ngx_http_core_main_conf_t  *cmcf;\n\n    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);\n\n    v = cmcf->variables.elts;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,\n                   \"http script set $%V\", &v[code->index].name);\n    }\n#endif\n}\n\n\nvoid\nngx_http_script_var_set_handler_code(ngx_http_script_engine_t *e)\n{\n    ngx_http_script_var_handler_code_t  *code;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,\n                   \"http script set var handler\");\n\n    code = (ngx_http_script_var_handler_code_t *) e->ip;\n\n    e->ip += sizeof(ngx_http_script_var_handler_code_t);\n\n    e->sp--;\n\n    code->handler(e->request, e->sp, code->data);\n}\n\n\nvoid\nngx_http_script_var_code(ngx_http_script_engine_t *e)\n{\n    ngx_http_variable_value_t   *value;\n    ngx_http_script_var_code_t  *code;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,\n                   \"http script var\");\n\n    code = (ngx_http_script_var_code_t *) e->ip;\n\n    e->ip += sizeof(ngx_http_script_var_code_t);\n\n    value = ngx_http_get_flushed_variable(e->request, code->index);\n\n    if (value && !value->not_found) {\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, e->request->connection->log, 0,\n                       \"http script var: \\\"%v\\\"\", value);\n\n        *e->sp = *value;\n        e->sp++;\n\n        return;\n    }\n\n    *e->sp = ngx_http_variable_null_value;\n    e->sp++;\n}\n\n\nvoid\nngx_http_script_nop_code(ngx_http_script_engine_t *e)\n{\n    e->ip += sizeof(uintptr_t);\n}\n"
  },
  {
    "path": "src/http/ngx_http_script.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_HTTP_SCRIPT_H_INCLUDED_\n#define _NGX_HTTP_SCRIPT_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\ntypedef struct {\n    u_char                     *ip;\n    u_char                     *pos;\n    ngx_http_variable_value_t  *sp;\n\n    ngx_str_t                   buf;\n    ngx_str_t                   line;\n\n    /* the start of the rewritten arguments */\n    u_char                     *args;\n\n    unsigned                    flushed:1;\n    unsigned                    skip:1;\n    unsigned                    quote:1;\n    unsigned                    is_args:1;\n    unsigned                    log:1;\n\n    ngx_int_t                   status;\n    ngx_http_request_t         *request;\n} ngx_http_script_engine_t;\n\n\ntypedef struct {\n    ngx_conf_t                 *cf;\n    ngx_str_t                  *source;\n\n    ngx_array_t               **flushes;\n    ngx_array_t               **lengths;\n    ngx_array_t               **values;\n\n    ngx_uint_t                  variables;\n    ngx_uint_t                  ncaptures;\n    ngx_uint_t                  captures_mask;\n    ngx_uint_t                  size;\n\n    void                       *main;\n\n    unsigned                    compile_args:1;\n    unsigned                    complete_lengths:1;\n    unsigned                    complete_values:1;\n    unsigned                    zero:1;\n    unsigned                    conf_prefix:1;\n    unsigned                    root_prefix:1;\n\n    unsigned                    dup_capture:1;\n    unsigned                    args:1;\n} ngx_http_script_compile_t;\n\n\ntypedef struct {\n    ngx_str_t                   value;\n    ngx_uint_t                 *flushes;\n    void                       *lengths;\n    void                       *values;\n\n    union {\n        size_t                  size;\n    } u;\n} ngx_http_complex_value_t;\n\n\ntypedef struct {\n    ngx_conf_t                 *cf;\n    ngx_str_t                  *value;\n    ngx_http_complex_value_t   *complex_value;\n\n    unsigned                    zero:1;\n    unsigned                    conf_prefix:1;\n    unsigned                    root_prefix:1;\n} ngx_http_compile_complex_value_t;\n\n\ntypedef void (*ngx_http_script_code_pt) (ngx_http_script_engine_t *e);\ntypedef size_t (*ngx_http_script_len_code_pt) (ngx_http_script_engine_t *e);\n\n\ntypedef struct {\n    ngx_http_script_code_pt     code;\n    uintptr_t                   len;\n} ngx_http_script_copy_code_t;\n\n\ntypedef struct {\n    ngx_http_script_code_pt     code;\n    uintptr_t                   index;\n} ngx_http_script_var_code_t;\n\n\ntypedef struct {\n    ngx_http_script_code_pt     code;\n    ngx_http_set_variable_pt    handler;\n    uintptr_t                   data;\n} ngx_http_script_var_handler_code_t;\n\n\ntypedef struct {\n    ngx_http_script_code_pt     code;\n    uintptr_t                   n;\n} ngx_http_script_copy_capture_code_t;\n\n\n#if (NGX_PCRE)\n\ntypedef struct {\n    ngx_http_script_code_pt     code;\n    ngx_http_regex_t           *regex;\n    ngx_array_t                *lengths;\n    uintptr_t                   size;\n    uintptr_t                   status;\n    uintptr_t                   next;\n\n    unsigned                    test:1;\n    unsigned                    negative_test:1;\n    unsigned                    uri:1;\n    unsigned                    args:1;\n\n    /* add the r->args to the new arguments */\n    unsigned                    add_args:1;\n\n    unsigned                    redirect:1;\n    unsigned                    break_cycle:1;\n\n    ngx_str_t                   name;\n} ngx_http_script_regex_code_t;\n\n\ntypedef struct {\n    ngx_http_script_code_pt     code;\n\n    unsigned                    uri:1;\n    unsigned                    args:1;\n\n    /* add the r->args to the new arguments */\n    unsigned                    add_args:1;\n\n    unsigned                    redirect:1;\n} ngx_http_script_regex_end_code_t;\n\n#endif\n\n\ntypedef struct {\n    ngx_http_script_code_pt     code;\n    uintptr_t                   conf_prefix;\n} ngx_http_script_full_name_code_t;\n\n\ntypedef struct {\n    ngx_http_script_code_pt     code;\n    uintptr_t                   status;\n    ngx_http_complex_value_t    text;\n} ngx_http_script_return_code_t;\n\n\ntypedef enum {\n    ngx_http_script_file_plain = 0,\n    ngx_http_script_file_not_plain,\n    ngx_http_script_file_dir,\n    ngx_http_script_file_not_dir,\n    ngx_http_script_file_exists,\n    ngx_http_script_file_not_exists,\n    ngx_http_script_file_exec,\n    ngx_http_script_file_not_exec\n} ngx_http_script_file_op_e;\n\n\ntypedef struct {\n    ngx_http_script_code_pt     code;\n    uintptr_t                   op;\n} ngx_http_script_file_code_t;\n\n\ntypedef struct {\n    ngx_http_script_code_pt     code;\n    uintptr_t                   next;\n    void                      **loc_conf;\n} ngx_http_script_if_code_t;\n\n\ntypedef struct {\n    ngx_http_script_code_pt     code;\n    ngx_array_t                *lengths;\n} ngx_http_script_complex_value_code_t;\n\n\ntypedef struct {\n    ngx_http_script_code_pt     code;\n    uintptr_t                   value;\n    uintptr_t                   text_len;\n    uintptr_t                   text_data;\n} ngx_http_script_value_code_t;\n\n\nvoid ngx_http_script_flush_complex_value(ngx_http_request_t *r,\n    ngx_http_complex_value_t *val);\nngx_int_t ngx_http_complex_value(ngx_http_request_t *r,\n    ngx_http_complex_value_t *val, ngx_str_t *value);\nsize_t ngx_http_complex_value_size(ngx_http_request_t *r,\n    ngx_http_complex_value_t *val, size_t default_value);\nngx_int_t ngx_http_compile_complex_value(ngx_http_compile_complex_value_t *ccv);\nchar *ngx_http_set_complex_value_slot(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nchar *ngx_http_set_complex_value_zero_slot(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nchar *ngx_http_set_complex_value_size_slot(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n\n\nngx_int_t ngx_http_test_predicates(ngx_http_request_t *r,\n    ngx_array_t *predicates);\nngx_int_t ngx_http_test_required_predicates(ngx_http_request_t *r,\n    ngx_array_t *predicates);\nchar *ngx_http_set_predicate_slot(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n\nngx_uint_t ngx_http_script_variables_count(ngx_str_t *value);\nngx_int_t ngx_http_script_compile(ngx_http_script_compile_t *sc);\nu_char *ngx_http_script_run(ngx_http_request_t *r, ngx_str_t *value,\n    void *code_lengths, size_t reserved, void *code_values);\nvoid ngx_http_script_flush_no_cacheable_variables(ngx_http_request_t *r,\n    ngx_array_t *indices);\n\nvoid *ngx_http_script_start_code(ngx_pool_t *pool, ngx_array_t **codes,\n    size_t size);\nvoid *ngx_http_script_add_code(ngx_array_t *codes, size_t size, void *code);\n\nsize_t ngx_http_script_copy_len_code(ngx_http_script_engine_t *e);\nvoid ngx_http_script_copy_code(ngx_http_script_engine_t *e);\nsize_t ngx_http_script_copy_var_len_code(ngx_http_script_engine_t *e);\nvoid ngx_http_script_copy_var_code(ngx_http_script_engine_t *e);\nsize_t ngx_http_script_copy_capture_len_code(ngx_http_script_engine_t *e);\nvoid ngx_http_script_copy_capture_code(ngx_http_script_engine_t *e);\nsize_t ngx_http_script_mark_args_code(ngx_http_script_engine_t *e);\nvoid ngx_http_script_start_args_code(ngx_http_script_engine_t *e);\n#if (NGX_PCRE)\nvoid ngx_http_script_regex_start_code(ngx_http_script_engine_t *e);\nvoid ngx_http_script_regex_end_code(ngx_http_script_engine_t *e);\n#endif\nvoid ngx_http_script_return_code(ngx_http_script_engine_t *e);\nvoid ngx_http_script_break_code(ngx_http_script_engine_t *e);\nvoid ngx_http_script_if_code(ngx_http_script_engine_t *e);\nvoid ngx_http_script_equal_code(ngx_http_script_engine_t *e);\n#if (T_NGX_HTTP_IMPROVED_IF)\nvoid ngx_http_script_greater_code(ngx_http_script_engine_t *e);\nvoid ngx_http_script_less_code(ngx_http_script_engine_t *e);\nvoid ngx_http_script_greater_or_equal_code(ngx_http_script_engine_t *e);\nvoid ngx_http_script_less_or_equal_code(ngx_http_script_engine_t *e);\n#endif\nvoid ngx_http_script_not_equal_code(ngx_http_script_engine_t *e);\nvoid ngx_http_script_file_code(ngx_http_script_engine_t *e);\nvoid ngx_http_script_complex_value_code(ngx_http_script_engine_t *e);\nvoid ngx_http_script_value_code(ngx_http_script_engine_t *e);\nvoid ngx_http_script_set_var_code(ngx_http_script_engine_t *e);\nvoid ngx_http_script_var_set_handler_code(ngx_http_script_engine_t *e);\nvoid ngx_http_script_var_code(ngx_http_script_engine_t *e);\nvoid ngx_http_script_nop_code(ngx_http_script_engine_t *e);\n\n\n#endif /* _NGX_HTTP_SCRIPT_H_INCLUDED_ */\n"
  },
  {
    "path": "src/http/ngx_http_special_response.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n#include <nginx.h>\n\n\nstatic ngx_int_t ngx_http_send_error_page(ngx_http_request_t *r,\n    ngx_http_err_page_t *err_page);\nstatic ngx_int_t ngx_http_send_special_response(ngx_http_request_t *r,\n    ngx_http_core_loc_conf_t *clcf, ngx_uint_t err);\nstatic ngx_int_t ngx_http_send_refresh(ngx_http_request_t *r);\n#if (T_NGX_SERVER_INFO)\nstatic ngx_buf_t *ngx_http_set_server_info(ngx_http_request_t *r);\n#endif\n\n\n#if (!T_NGX_SERVER_INFO)\nstatic u_char ngx_http_error_full_tail[] =\n\"<hr><center>\" NGINX_VER \"</center>\" CRLF\n\"</body>\" CRLF\n\"</html>\" CRLF\n;\n\n\nstatic u_char ngx_http_error_build_tail[] =\n\"<hr><center>\" NGINX_VER_BUILD \"</center>\" CRLF\n\"</body>\" CRLF\n\"</html>\" CRLF\n;\n#endif\n\n\n#if (T_NGX_SERVER_INFO)\nstatic u_char ngx_http_error_doctype[] =\n\"<!DOCTYPE HTML PUBLIC \\\"-//IETF//DTD HTML 2.0//EN\\\">\" CRLF;\n\n\nstatic u_char ngx_http_server_info_head[] =\n\" Sorry for the inconvenience.<br/>\" CRLF\n\"Please report this message and include the following information to us.<br/>\"\nCRLF\n\"Thank you very much!</p>\" CRLF\n\"<table>\" CRLF\n\"<tr>\" CRLF\n\"<td>URL:</td>\" CRLF\n\"<td>\"\n;\n\n\nstatic u_char ngx_http_server_info_server[] =\n\"</td>\" CRLF\n\"</tr>\" CRLF\n\"<tr>\" CRLF\n\"<td>Server:</td>\" CRLF\n\"<td>\"\n;\n\n\nstatic u_char ngx_http_server_info_admin[] =\n\"</td>\" CRLF\n\"</tr>\" CRLF\n\"<tr>\" CRLF\n\"<td>Admin:</td>\" CRLF\n\"<td>\"\n;\n\n\nstatic u_char ngx_http_server_info_date[] =\n\"</td>\" CRLF\n\"</tr>\" CRLF\n\"<tr>\" CRLF\n\"<td>Date:</td>\" CRLF\n\"<td>\"\n;\n\n\nstatic u_char ngx_http_server_info_tail[] =\n\"</td>\" CRLF\n\"</tr>\" CRLF\n\"</table>\" CRLF\n;\n\n\nstatic u_char ngx_http_error_banner[] =\n\"<hr/>Powered by \" TENGINE;\n\n\nstatic u_char ngx_http_error_full_banner[] =\n\"<hr/>Powered by \" TENGINE_VER_BUILD;\n\n\nstatic u_char ngx_http_error_powered_by[] =\n\"<hr/>Powered by \";\n#endif\n\n\nstatic u_char ngx_http_error_tail[] =\n#if (T_NGX_SERVER_INFO)\n\"<hr><center>tengine</center>\" CRLF\n#else\n\"<hr><center>nginx</center>\" CRLF\n#endif\n\n\"</body>\" CRLF\n\"</html>\" CRLF\n;\n\n\nstatic u_char ngx_http_msie_padding[] =\n\"<!-- a padding to disable MSIE and Chrome friendly error page -->\" CRLF\n\"<!-- a padding to disable MSIE and Chrome friendly error page -->\" CRLF\n\"<!-- a padding to disable MSIE and Chrome friendly error page -->\" CRLF\n\"<!-- a padding to disable MSIE and Chrome friendly error page -->\" CRLF\n\"<!-- a padding to disable MSIE and Chrome friendly error page -->\" CRLF\n\"<!-- a padding to disable MSIE and Chrome friendly error page -->\" CRLF\n;\n\n\nstatic u_char ngx_http_msie_refresh_head[] =\n\"<html><head><meta http-equiv=\\\"Refresh\\\" content=\\\"0; URL=\";\n\n\nstatic u_char ngx_http_msie_refresh_tail[] =\n\"\\\"></head><body></body></html>\" CRLF;\n\n\nstatic char ngx_http_error_301_page[] =\n\"<html>\" CRLF\n\"<head><title>301 Moved Permanently</title></head>\" CRLF\n\"<body>\" CRLF\n\"<center><h1>301 Moved Permanently</h1></center>\" CRLF\n;\n\n\nstatic char ngx_http_error_302_page[] =\n\"<html>\" CRLF\n\"<head><title>302 Found</title></head>\" CRLF\n\"<body>\" CRLF\n\"<center><h1>302 Found</h1></center>\" CRLF\n;\n\n\nstatic char ngx_http_error_303_page[] =\n\"<html>\" CRLF\n\"<head><title>303 See Other</title></head>\" CRLF\n\"<body>\" CRLF\n\"<center><h1>303 See Other</h1></center>\" CRLF\n;\n\n\nstatic char ngx_http_error_307_page[] =\n\"<html>\" CRLF\n\"<head><title>307 Temporary Redirect</title></head>\" CRLF\n\"<body>\" CRLF\n\"<center><h1>307 Temporary Redirect</h1></center>\" CRLF\n;\n\n\nstatic char ngx_http_error_308_page[] =\n\"<html>\" CRLF\n\"<head><title>308 Permanent Redirect</title></head>\" CRLF\n\"<body>\" CRLF\n\"<center><h1>308 Permanent Redirect</h1></center>\" CRLF\n;\n\n\nstatic char ngx_http_error_400_page[] =\n\"<html>\" CRLF\n\"<head><title>400 Bad Request</title></head>\" CRLF\n\"<body>\" CRLF\n\"<center><h1>400 Bad Request</h1></center>\" CRLF\n;\n\n\nstatic char ngx_http_error_401_page[] =\n\"<html>\" CRLF\n\"<head><title>401 Authorization Required</title></head>\" CRLF\n\"<body>\" CRLF\n\"<center><h1>401 Authorization Required</h1></center>\" CRLF\n;\n\n\nstatic char ngx_http_error_402_page[] =\n\"<html>\" CRLF\n\"<head><title>402 Payment Required</title></head>\" CRLF\n\"<body>\" CRLF\n\"<center><h1>402 Payment Required</h1></center>\" CRLF\n;\n\n\nstatic char ngx_http_error_403_page[] =\n\"<html>\" CRLF\n\"<head><title>403 Forbidden</title></head>\" CRLF\n\"<body>\" CRLF\n\"<center><h1>403 Forbidden</h1></center>\" CRLF\n;\n\n\nstatic char ngx_http_error_404_page[] =\n\"<html>\" CRLF\n\"<head><title>404 Not Found</title></head>\" CRLF\n\"<body>\" CRLF\n\"<center><h1>404 Not Found</h1></center>\" CRLF\n;\n\n\nstatic char ngx_http_error_405_page[] =\n\"<html>\" CRLF\n\"<head><title>405 Not Allowed</title></head>\" CRLF\n\"<body>\" CRLF\n\"<center><h1>405 Not Allowed</h1></center>\" CRLF\n;\n\n\nstatic char ngx_http_error_406_page[] =\n\"<html>\" CRLF\n\"<head><title>406 Not Acceptable</title></head>\" CRLF\n\"<body>\" CRLF\n\"<center><h1>406 Not Acceptable</h1></center>\" CRLF\n;\n\n\nstatic char ngx_http_error_408_page[] =\n\"<html>\" CRLF\n\"<head><title>408 Request Time-out</title></head>\" CRLF\n\"<body>\" CRLF\n\"<center><h1>408 Request Time-out</h1></center>\" CRLF\n;\n\n\nstatic char ngx_http_error_409_page[] =\n\"<html>\" CRLF\n\"<head><title>409 Conflict</title></head>\" CRLF\n\"<body>\" CRLF\n\"<center><h1>409 Conflict</h1></center>\" CRLF\n;\n\n\nstatic char ngx_http_error_410_page[] =\n\"<html>\" CRLF\n\"<head><title>410 Gone</title></head>\" CRLF\n\"<body>\" CRLF\n\"<center><h1>410 Gone</h1></center>\" CRLF\n;\n\n\nstatic char ngx_http_error_411_page[] =\n\"<html>\" CRLF\n\"<head><title>411 Length Required</title></head>\" CRLF\n\"<body>\" CRLF\n\"<center><h1>411 Length Required</h1></center>\" CRLF\n;\n\n\nstatic char ngx_http_error_412_page[] =\n\"<html>\" CRLF\n\"<head><title>412 Precondition Failed</title></head>\" CRLF\n\"<body>\" CRLF\n\"<center><h1>412 Precondition Failed</h1></center>\" CRLF\n;\n\n\nstatic char ngx_http_error_413_page[] =\n\"<html>\" CRLF\n\"<head><title>413 Request Entity Too Large</title></head>\" CRLF\n\"<body>\" CRLF\n\"<center><h1>413 Request Entity Too Large</h1></center>\" CRLF\n;\n\n\nstatic char ngx_http_error_414_page[] =\n\"<html>\" CRLF\n\"<head><title>414 Request-URI Too Large</title></head>\" CRLF\n\"<body>\" CRLF\n\"<center><h1>414 Request-URI Too Large</h1></center>\" CRLF\n;\n\n\nstatic char ngx_http_error_415_page[] =\n\"<html>\" CRLF\n\"<head><title>415 Unsupported Media Type</title></head>\" CRLF\n\"<body>\" CRLF\n\"<center><h1>415 Unsupported Media Type</h1></center>\" CRLF\n;\n\n\nstatic char ngx_http_error_416_page[] =\n\"<html>\" CRLF\n\"<head><title>416 Requested Range Not Satisfiable</title></head>\" CRLF\n\"<body>\" CRLF\n\"<center><h1>416 Requested Range Not Satisfiable</h1></center>\" CRLF\n;\n\n\nstatic char ngx_http_error_421_page[] =\n\"<html>\" CRLF\n\"<head><title>421 Misdirected Request</title></head>\" CRLF\n\"<body>\" CRLF\n\"<center><h1>421 Misdirected Request</h1></center>\" CRLF\n;\n\n\nstatic char ngx_http_error_429_page[] =\n\"<html>\" CRLF\n\"<head><title>429 Too Many Requests</title></head>\" CRLF\n\"<body>\" CRLF\n\"<center><h1>429 Too Many Requests</h1></center>\" CRLF\n;\n\n\nstatic char ngx_http_error_494_page[] =\n\"<html>\" CRLF\n\"<head><title>400 Request Header Or Cookie Too Large</title></head>\"\nCRLF\n\"<body>\" CRLF\n\"<center><h1>400 Bad Request</h1></center>\" CRLF\n\"<center>Request Header Or Cookie Too Large</center>\" CRLF\n;\n\n\nstatic char ngx_http_error_495_page[] =\n\"<html>\" CRLF\n\"<head><title>400 The SSL certificate error</title></head>\"\nCRLF\n\"<body>\" CRLF\n\"<center><h1>400 Bad Request</h1></center>\" CRLF\n\"<center>The SSL certificate error</center>\" CRLF\n;\n\n\nstatic char ngx_http_error_496_page[] =\n\"<html>\" CRLF\n\"<head><title>400 No required SSL certificate was sent</title></head>\"\nCRLF\n\"<body>\" CRLF\n\"<center><h1>400 Bad Request</h1></center>\" CRLF\n\"<center>No required SSL certificate was sent</center>\" CRLF\n;\n\n\nstatic char ngx_http_error_497_page[] =\n\"<html>\" CRLF\n\"<head><title>400 The plain HTTP request was sent to HTTPS port</title></head>\"\nCRLF\n\"<body>\" CRLF\n\"<center><h1>400 Bad Request</h1></center>\" CRLF\n\"<center>The plain HTTP request was sent to HTTPS port</center>\" CRLF\n;\n\n\nstatic char ngx_http_error_500_page[] =\n\"<html>\" CRLF\n\"<head><title>500 Internal Server Error</title></head>\" CRLF\n\"<body>\" CRLF\n\"<center><h1>500 Internal Server Error</h1></center>\" CRLF\n;\n\n\nstatic char ngx_http_error_501_page[] =\n\"<html>\" CRLF\n\"<head><title>501 Not Implemented</title></head>\" CRLF\n\"<body>\" CRLF\n\"<center><h1>501 Not Implemented</h1></center>\" CRLF\n;\n\n\nstatic char ngx_http_error_502_page[] =\n\"<html>\" CRLF\n\"<head><title>502 Bad Gateway</title></head>\" CRLF\n\"<body>\" CRLF\n\"<center><h1>502 Bad Gateway</h1></center>\" CRLF\n;\n\n\nstatic char ngx_http_error_503_page[] =\n\"<html>\" CRLF\n\"<head><title>503 Service Temporarily Unavailable</title></head>\" CRLF\n\"<body>\" CRLF\n\"<center><h1>503 Service Temporarily Unavailable</h1></center>\" CRLF\n;\n\n\nstatic char ngx_http_error_504_page[] =\n\"<html>\" CRLF\n\"<head><title>504 Gateway Time-out</title></head>\" CRLF\n\"<body>\" CRLF\n\"<center><h1>504 Gateway Time-out</h1></center>\" CRLF\n;\n\n\nstatic char ngx_http_error_505_page[] =\n\"<html>\" CRLF\n\"<head><title>505 HTTP Version Not Supported</title></head>\" CRLF\n\"<body>\" CRLF\n\"<center><h1>505 HTTP Version Not Supported</h1></center>\" CRLF\n;\n\n\nstatic char ngx_http_error_507_page[] =\n\"<html>\" CRLF\n\"<head><title>507 Insufficient Storage</title></head>\" CRLF\n\"<body>\" CRLF\n\"<center><h1>507 Insufficient Storage</h1></center>\" CRLF\n;\n\n\nstatic ngx_str_t ngx_http_error_pages[] = {\n\n    ngx_null_string,                     /* 201, 204 */\n\n#define NGX_HTTP_LAST_2XX  202\n#define NGX_HTTP_OFF_3XX   (NGX_HTTP_LAST_2XX - 201)\n\n    /* ngx_null_string, */               /* 300 */\n    ngx_string(ngx_http_error_301_page),\n    ngx_string(ngx_http_error_302_page),\n    ngx_string(ngx_http_error_303_page),\n    ngx_null_string,                     /* 304 */\n    ngx_null_string,                     /* 305 */\n    ngx_null_string,                     /* 306 */\n    ngx_string(ngx_http_error_307_page),\n    ngx_string(ngx_http_error_308_page),\n\n#define NGX_HTTP_LAST_3XX  309\n#define NGX_HTTP_OFF_4XX   (NGX_HTTP_LAST_3XX - 301 + NGX_HTTP_OFF_3XX)\n\n    ngx_string(ngx_http_error_400_page),\n    ngx_string(ngx_http_error_401_page),\n    ngx_string(ngx_http_error_402_page),\n    ngx_string(ngx_http_error_403_page),\n    ngx_string(ngx_http_error_404_page),\n    ngx_string(ngx_http_error_405_page),\n    ngx_string(ngx_http_error_406_page),\n    ngx_null_string,                     /* 407 */\n    ngx_string(ngx_http_error_408_page),\n    ngx_string(ngx_http_error_409_page),\n    ngx_string(ngx_http_error_410_page),\n    ngx_string(ngx_http_error_411_page),\n    ngx_string(ngx_http_error_412_page),\n    ngx_string(ngx_http_error_413_page),\n    ngx_string(ngx_http_error_414_page),\n    ngx_string(ngx_http_error_415_page),\n    ngx_string(ngx_http_error_416_page),\n    ngx_null_string,                     /* 417 */\n    ngx_null_string,                     /* 418 */\n    ngx_null_string,                     /* 419 */\n    ngx_null_string,                     /* 420 */\n    ngx_string(ngx_http_error_421_page),\n    ngx_null_string,                     /* 422 */\n    ngx_null_string,                     /* 423 */\n    ngx_null_string,                     /* 424 */\n    ngx_null_string,                     /* 425 */\n    ngx_null_string,                     /* 426 */\n    ngx_null_string,                     /* 427 */\n    ngx_null_string,                     /* 428 */\n    ngx_string(ngx_http_error_429_page),\n\n#define NGX_HTTP_LAST_4XX  430\n#define NGX_HTTP_OFF_5XX   (NGX_HTTP_LAST_4XX - 400 + NGX_HTTP_OFF_4XX)\n\n    ngx_string(ngx_http_error_494_page), /* 494, request header too large */\n    ngx_string(ngx_http_error_495_page), /* 495, https certificate error */\n    ngx_string(ngx_http_error_496_page), /* 496, https no certificate */\n    ngx_string(ngx_http_error_497_page), /* 497, http to https */\n    ngx_string(ngx_http_error_404_page), /* 498, canceled */\n    ngx_null_string,                     /* 499, client has closed connection */\n\n    ngx_string(ngx_http_error_500_page),\n    ngx_string(ngx_http_error_501_page),\n    ngx_string(ngx_http_error_502_page),\n    ngx_string(ngx_http_error_503_page),\n    ngx_string(ngx_http_error_504_page),\n    ngx_string(ngx_http_error_505_page),\n    ngx_null_string,                     /* 506 */\n    ngx_string(ngx_http_error_507_page)\n\n#define NGX_HTTP_LAST_5XX  508\n\n};\n\n\nngx_int_t\nngx_http_special_response_handler(ngx_http_request_t *r, ngx_int_t error)\n{\n    ngx_uint_t                 i, err;\n    ngx_http_err_page_t       *err_page;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http special response: %i, \\\"%V?%V\\\"\",\n                   error, &r->uri, &r->args);\n\n    r->err_status = error;\n\n    if (r->keepalive) {\n        switch (error) {\n            case NGX_HTTP_BAD_REQUEST:\n            case NGX_HTTP_REQUEST_ENTITY_TOO_LARGE:\n            case NGX_HTTP_REQUEST_URI_TOO_LARGE:\n            case NGX_HTTP_TO_HTTPS:\n            case NGX_HTTPS_CERT_ERROR:\n            case NGX_HTTPS_NO_CERT:\n            case NGX_HTTP_INTERNAL_SERVER_ERROR:\n            case NGX_HTTP_NOT_IMPLEMENTED:\n                r->keepalive = 0;\n        }\n    }\n\n    if (r->lingering_close) {\n        switch (error) {\n            case NGX_HTTP_BAD_REQUEST:\n            case NGX_HTTP_TO_HTTPS:\n            case NGX_HTTPS_CERT_ERROR:\n            case NGX_HTTPS_NO_CERT:\n                r->lingering_close = 0;\n        }\n    }\n\n    r->headers_out.content_type.len = 0;\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    if (!r->error_page && clcf->error_pages && r->uri_changes != 0) {\n\n        if (clcf->recursive_error_pages == 0) {\n            r->error_page = 1;\n        }\n\n        err_page = clcf->error_pages->elts;\n\n        for (i = 0; i < clcf->error_pages->nelts; i++) {\n            if (err_page[i].status == error) {\n                return ngx_http_send_error_page(r, &err_page[i]);\n            }\n        }\n    }\n\n    r->expect_tested = 1;\n\n    if (ngx_http_discard_request_body(r) != NGX_OK) {\n        r->keepalive = 0;\n    }\n\n    if (clcf->msie_refresh\n        && r->headers_in.msie\n        && (error == NGX_HTTP_MOVED_PERMANENTLY\n            || error == NGX_HTTP_MOVED_TEMPORARILY))\n    {\n        return ngx_http_send_refresh(r);\n    }\n\n    if (error == NGX_HTTP_CREATED) {\n        /* 201 */\n        err = 0;\n\n    } else if (error == NGX_HTTP_NO_CONTENT) {\n        /* 204 */\n        err = 0;\n\n    } else if (error >= NGX_HTTP_MOVED_PERMANENTLY\n               && error < NGX_HTTP_LAST_3XX)\n    {\n        /* 3XX */\n        err = error - NGX_HTTP_MOVED_PERMANENTLY + NGX_HTTP_OFF_3XX;\n\n    } else if (error >= NGX_HTTP_BAD_REQUEST\n               && error < NGX_HTTP_LAST_4XX)\n    {\n        /* 4XX */\n        err = error - NGX_HTTP_BAD_REQUEST + NGX_HTTP_OFF_4XX;\n\n    } else if (error >= NGX_HTTP_NGINX_CODES\n               && error < NGX_HTTP_LAST_5XX)\n    {\n        /* 49X, 5XX */\n        err = error - NGX_HTTP_NGINX_CODES + NGX_HTTP_OFF_5XX;\n        switch (error) {\n            case NGX_HTTP_TO_HTTPS:\n            case NGX_HTTPS_CERT_ERROR:\n            case NGX_HTTPS_NO_CERT:\n            case NGX_HTTP_REQUEST_HEADER_TOO_LARGE:\n                r->err_status = NGX_HTTP_BAD_REQUEST;\n        }\n\n    } else {\n        /* unknown code, zero body */\n        err = 0;\n    }\n\n    return ngx_http_send_special_response(r, clcf, err);\n}\n\n\nngx_int_t\nngx_http_filter_finalize_request(ngx_http_request_t *r, ngx_module_t *m,\n    ngx_int_t error)\n{\n    void       *ctx;\n    ngx_int_t   rc;\n\n    ngx_http_clean_header(r);\n\n    ctx = NULL;\n\n    if (m) {\n        ctx = r->ctx[m->ctx_index];\n    }\n\n    /* clear the modules contexts */\n    ngx_memzero(r->ctx, sizeof(void *) * ngx_http_max_module);\n\n    if (m) {\n        r->ctx[m->ctx_index] = ctx;\n    }\n\n    r->filter_finalize = 1;\n\n    rc = ngx_http_special_response_handler(r, error);\n\n    /* NGX_ERROR resets any pending data */\n\n    switch (rc) {\n\n    case NGX_OK:\n    case NGX_DONE:\n        return NGX_ERROR;\n\n    default:\n        return rc;\n    }\n}\n\n\nvoid\nngx_http_clean_header(ngx_http_request_t *r)\n{\n    ngx_memzero(&r->headers_out.status,\n                sizeof(ngx_http_headers_out_t)\n                    - offsetof(ngx_http_headers_out_t, status));\n\n    r->headers_out.headers.part.nelts = 0;\n    r->headers_out.headers.part.next = NULL;\n    r->headers_out.headers.last = &r->headers_out.headers.part;\n\n    r->headers_out.trailers.part.nelts = 0;\n    r->headers_out.trailers.part.next = NULL;\n    r->headers_out.trailers.last = &r->headers_out.trailers.part;\n\n    r->headers_out.content_length_n = -1;\n    r->headers_out.last_modified_time = -1;\n}\n\n\nstatic ngx_int_t\nngx_http_send_error_page(ngx_http_request_t *r, ngx_http_err_page_t *err_page)\n{\n    ngx_int_t                  overwrite;\n    ngx_str_t                  uri, args;\n    ngx_table_elt_t           *location;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    overwrite = err_page->overwrite;\n\n    if (overwrite && overwrite != NGX_HTTP_OK) {\n        r->expect_tested = 1;\n    }\n\n    if (overwrite >= 0) {\n        r->err_status = overwrite;\n    }\n\n    if (ngx_http_complex_value(r, &err_page->value, &uri) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    if (uri.len && uri.data[0] == '/') {\n\n        if (err_page->value.lengths) {\n            ngx_http_split_args(r, &uri, &args);\n\n        } else {\n            args = err_page->args;\n        }\n\n        if (r->method != NGX_HTTP_HEAD) {\n            r->method = NGX_HTTP_GET;\n            r->method_name = ngx_http_core_get_method;\n        }\n\n        return ngx_http_internal_redirect(r, &uri, &args);\n    }\n\n    if (uri.len && uri.data[0] == '@') {\n        return ngx_http_named_location(r, &uri);\n    }\n\n    r->expect_tested = 1;\n\n    if (ngx_http_discard_request_body(r) != NGX_OK) {\n        r->keepalive = 0;\n    }\n\n    location = ngx_list_push(&r->headers_out.headers);\n\n    if (location == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (overwrite != NGX_HTTP_MOVED_PERMANENTLY\n        && overwrite != NGX_HTTP_MOVED_TEMPORARILY\n        && overwrite != NGX_HTTP_SEE_OTHER\n        && overwrite != NGX_HTTP_TEMPORARY_REDIRECT\n        && overwrite != NGX_HTTP_PERMANENT_REDIRECT)\n    {\n        r->err_status = NGX_HTTP_MOVED_TEMPORARILY;\n    }\n\n    location->hash = 1;\n    location->next = NULL;\n    ngx_str_set(&location->key, \"Location\");\n    location->value = uri;\n\n    ngx_http_clear_location(r);\n\n    r->headers_out.location = location;\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    if (clcf->msie_refresh && r->headers_in.msie) {\n        return ngx_http_send_refresh(r);\n    }\n\n    return ngx_http_send_special_response(r, clcf, r->err_status\n                                                   - NGX_HTTP_MOVED_PERMANENTLY\n                                                   + NGX_HTTP_OFF_3XX);\n}\n\n\nstatic ngx_int_t\nngx_http_send_special_response(ngx_http_request_t *r,\n    ngx_http_core_loc_conf_t *clcf, ngx_uint_t err)\n{\n#if (!T_NGX_SERVER_INFO)\n    u_char       *tail;\n    size_t        len;\n#endif    \n    ngx_int_t     rc;\n    ngx_buf_t    *b;\n    ngx_uint_t    msie_padding;\n#if (T_NGX_SERVER_INFO)\n    ngx_chain_t   out[7];\n    ngx_buf_t    *ib;\n    ngx_uint_t    i;\n\n#else \n    ngx_chain_t   out[3];\n#endif\n\n#if (T_NGX_SERVER_INFO)\n    if (clcf->server_info && err >= NGX_HTTP_OFF_4XX) {\n        ib = ngx_http_set_server_info(r);\n        if (ib == NULL) {\n            return NGX_ERROR;\n        }\n\n    } else {\n        ib = NULL;\n    }\n\n#else \n    if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) {\n        len = sizeof(ngx_http_error_full_tail) - 1;\n        tail = ngx_http_error_full_tail;\n\n    } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) {\n        len = sizeof(ngx_http_error_build_tail) - 1;\n        tail = ngx_http_error_build_tail;\n\n    } else {\n        len = sizeof(ngx_http_error_tail) - 1;\n        tail = ngx_http_error_tail;\n    }\n#endif\n\n    msie_padding = 0;\n\n    if (ngx_http_error_pages[err].len) {\n#if (T_NGX_SERVER_INFO)\n        r->headers_out.content_length_n = sizeof(ngx_http_error_doctype) - 1\n                                          + ngx_http_error_pages[err].len\n                                          + (ib ? (ib->last - ib->pos) : 0)\n                                          + sizeof(ngx_http_error_tail) - 1;\n\n        if (clcf->server_tag_type == NGX_HTTP_SERVER_TAG_ON) {\n            r->headers_out.content_length_n += clcf->server_tokens\n                ? sizeof(ngx_http_error_full_banner) - 1\n                : sizeof(ngx_http_error_banner) - 1;\n\n        } else if (clcf->server_tag_type == NGX_HTTP_SERVER_TAG_CUSTOMIZED) {\n            r->headers_out.content_length_n += sizeof(ngx_http_error_powered_by)\n                                               - 1;\n            r->headers_out.content_length_n += clcf->server_tag.len;\n        }\n#else\n        r->headers_out.content_length_n = ngx_http_error_pages[err].len + len;\n#endif\n\n        if (clcf->msie_padding\n            && (r->headers_in.msie || r->headers_in.chrome)\n            && r->http_version >= NGX_HTTP_VERSION_10\n            && err >= NGX_HTTP_OFF_4XX)\n        {\n            r->headers_out.content_length_n +=\n                                         sizeof(ngx_http_msie_padding) - 1;\n            msie_padding = 1;\n        }\n\n        r->headers_out.content_type_len = sizeof(\"text/html\") - 1;\n        ngx_str_set(&r->headers_out.content_type, \"text/html\");\n        r->headers_out.content_type_lowcase = NULL;\n\n    } else {\n        r->headers_out.content_length_n = 0;\n    }\n\n    if (r->headers_out.content_length) {\n        r->headers_out.content_length->hash = 0;\n        r->headers_out.content_length = NULL;\n    }\n\n    ngx_http_clear_accept_ranges(r);\n    ngx_http_clear_last_modified(r);\n    ngx_http_clear_etag(r);\n\n    rc = ngx_http_send_header(r);\n\n    if (rc == NGX_ERROR || r->header_only) {\n        return rc;\n    }\n\n    if (ngx_http_error_pages[err].len == 0) {\n        return ngx_http_send_special(r, NGX_HTTP_LAST);\n    }\n\n#if (T_NGX_SERVER_INFO)\n    i = 0;\n\n    b = ngx_calloc_buf(r->pool);\n    if (b == NULL) {\n        return NGX_ERROR;\n    }\n\n    b->memory = 1;\n    b->pos = ngx_http_error_doctype;\n    b->last = ngx_http_error_doctype + sizeof(ngx_http_error_doctype) - 1;\n\n    out[i].buf = b;\n    out[i].next = &out[i + 1];\n    i++;\n#endif\n\n    b = ngx_calloc_buf(r->pool);\n    if (b == NULL) {\n        return NGX_ERROR;\n    }\n\n    b->memory = 1;\n    b->pos = ngx_http_error_pages[err].data;\n    b->last = ngx_http_error_pages[err].data + ngx_http_error_pages[err].len;\n\n#if (T_NGX_SERVER_INFO)\n    out[i].buf = b;\n    out[i].next = &out[i + 1];\n    i++;\n\n    if (ib) {\n        out[i].buf = ib;\n        out[i].next = &out[i + 1];\n        i++;\n    }\n\n    if (clcf->server_tag_type == NGX_HTTP_SERVER_TAG_ON) {\n        b = ngx_calloc_buf(r->pool);\n        if (b == NULL) {\n            return NGX_ERROR;\n        }\n\n        b->memory = 1;\n\n        if (clcf->server_tokens) {\n            b->pos = ngx_http_error_full_banner;\n            b->last = ngx_http_error_full_banner\n                      + sizeof(ngx_http_error_full_banner) - 1;\n\n        } else {\n            b->pos = ngx_http_error_banner;\n            b->last = ngx_http_error_banner + sizeof(ngx_http_error_banner) - 1;\n        }\n\n        out[i].buf = b;\n        out[i].next = &out[i + 1];\n        i++;\n\n    } else if (clcf->server_tag_type == NGX_HTTP_SERVER_TAG_CUSTOMIZED) {\n        b = ngx_calloc_buf(r->pool);\n        if (b == NULL) {\n            return NGX_ERROR;\n        }\n\n        b->memory = 1;\n        b->pos = ngx_http_error_powered_by;\n        b->last = ngx_http_error_powered_by\n                  + sizeof(ngx_http_error_powered_by) - 1;\n\n        out[i].buf = b;\n        out[i].next = &out[i + 1];\n        i++;\n\n        b = ngx_calloc_buf(r->pool);\n        if (b == NULL) {\n            return NGX_ERROR;\n        }\n\n        b->memory = 1;\n        b->pos = clcf->server_tag.data;\n        b->last = clcf->server_tag.data + clcf->server_tag.len;\n\n        out[i].buf = b;\n        out[i].next = &out[i + 1];\n        i++;\n    }\n\n#else\n    out[0].buf = b;\n    out[0].next = &out[1];\n#endif\n\n    b = ngx_calloc_buf(r->pool);\n    if (b == NULL) {\n        return NGX_ERROR;\n    }\n\n    b->memory = 1;\n\n#if (T_NGX_SERVER_INFO)\n    b->pos = ngx_http_error_tail;\n    b->last = ngx_http_error_tail + sizeof(ngx_http_error_tail) - 1;\n\n    out[i].buf = b;\n    out[i].next = NULL;\n#else\n    b->pos = tail;\n    b->last = tail + len;\n\n    out[1].buf = b;\n    out[1].next = NULL;\n#endif\n\n    if (msie_padding) {\n        b = ngx_calloc_buf(r->pool);\n        if (b == NULL) {\n            return NGX_ERROR;\n        }\n\n        b->memory = 1;\n        b->pos = ngx_http_msie_padding;\n        b->last = ngx_http_msie_padding + sizeof(ngx_http_msie_padding) - 1;\n\n#if (T_NGX_SERVER_INFO)\n        out[i].next = &out[i + 1];\n        i++;\n        out[i].buf = b;\n        out[i].next = NULL;\n#else\n        out[1].next = &out[2];\n        out[2].buf = b;\n        out[2].next = NULL;\n#endif\n    }\n\n    if (r == r->main) {\n        b->last_buf = 1;\n    }\n\n    b->last_in_chain = 1;\n\n    return ngx_http_output_filter(r, &out[0]);\n}\n\n\nstatic ngx_int_t\nngx_http_send_refresh(ngx_http_request_t *r)\n{\n    u_char       *p, *location;\n    size_t        len, size;\n    uintptr_t     escape;\n    ngx_int_t     rc;\n    ngx_buf_t    *b;\n    ngx_chain_t   out;\n\n    len = r->headers_out.location->value.len;\n    location = r->headers_out.location->value.data;\n\n    escape = 2 * ngx_escape_uri(NULL, location, len, NGX_ESCAPE_REFRESH);\n\n    size = sizeof(ngx_http_msie_refresh_head) - 1\n           + escape + len\n           + sizeof(ngx_http_msie_refresh_tail) - 1;\n\n    r->err_status = NGX_HTTP_OK;\n\n    r->headers_out.content_type_len = sizeof(\"text/html\") - 1;\n    ngx_str_set(&r->headers_out.content_type, \"text/html\");\n    r->headers_out.content_type_lowcase = NULL;\n\n    r->headers_out.location->hash = 0;\n    r->headers_out.location = NULL;\n\n    r->headers_out.content_length_n = size;\n\n    if (r->headers_out.content_length) {\n        r->headers_out.content_length->hash = 0;\n        r->headers_out.content_length = NULL;\n    }\n\n    ngx_http_clear_accept_ranges(r);\n    ngx_http_clear_last_modified(r);\n    ngx_http_clear_etag(r);\n\n    rc = ngx_http_send_header(r);\n\n    if (rc == NGX_ERROR || r->header_only) {\n        return rc;\n    }\n\n    b = ngx_create_temp_buf(r->pool, size);\n    if (b == NULL) {\n        return NGX_ERROR;\n    }\n\n    p = ngx_cpymem(b->pos, ngx_http_msie_refresh_head,\n                   sizeof(ngx_http_msie_refresh_head) - 1);\n\n    if (escape == 0) {\n        p = ngx_cpymem(p, location, len);\n\n    } else {\n        p = (u_char *) ngx_escape_uri(p, location, len, NGX_ESCAPE_REFRESH);\n    }\n\n    b->last = ngx_cpymem(p, ngx_http_msie_refresh_tail,\n                         sizeof(ngx_http_msie_refresh_tail) - 1);\n\n    b->last_buf = (r == r->main) ? 1 : 0;\n    b->last_in_chain = 1;\n\n    out.buf = b;\n    out.next = NULL;\n\n    return ngx_http_output_filter(r, &out);\n}\n\n\n#if (T_NGX_SERVER_INFO)\nstatic ngx_buf_t *\nngx_http_set_server_info(ngx_http_request_t *r)\n{\n    size_t                     size;\n    ngx_buf_t                 *b;\n    uintptr_t                  euri, ehost;\n    ngx_str_t                 *host, scheme, port;\n    ngx_uint_t                 p;\n    struct sockaddr_in        *sin;\n#if (NGX_HAVE_INET6)\n    struct sockaddr_in6       *sin6;\n#endif\n    ngx_http_core_srv_conf_t  *cscf;\n\n    cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);\n\n    ngx_str_null(&scheme);\n\n#if (NGX_HTTP_SSL)\n\n    if (r->connection->ssl) {\n        ngx_str_set(&scheme, \"https://\");\n    }\n\n#endif\n\n    if (scheme.len == 0) {\n        ngx_str_set(&scheme, \"http://\");\n    }\n\n    if (r->headers_in.server.len) {\n        host = &r->headers_in.server;\n\n    } else {\n        host = &cscf->server_name;\n    }\n\n    if (ngx_connection_local_sockaddr(r->connection, NULL, 0) != NGX_OK) {\n        return NULL;\n    }\n\n    ngx_str_null(&port);\n\n    switch (r->connection->local_sockaddr->sa_family) {\n\n#if (NGX_HAVE_INET6)\n    case AF_INET6:\n        sin6 = (struct sockaddr_in6 *) r->connection->local_sockaddr;\n        p = ntohs(sin6->sin6_port);\n        break;\n#endif\n\n    default:\n        sin = (struct sockaddr_in *) r->connection->local_sockaddr;\n        p = ntohs(sin->sin_port);\n        break;\n    }\n\n    if (p > 0 && p < 65536 && p != 80 && p != 443) {\n        port.data = ngx_pnalloc(r->pool, sizeof(\":65535\") - 1);\n        if (port.data == NULL) {\n            return NULL;\n        }\n\n        port.len = ngx_sprintf(port.data, \":%ui\", p) - port.data;\n    }\n\n    ehost = ngx_escape_html(NULL, host->data, host->len);\n    euri = ngx_escape_html(NULL, r->unparsed_uri.data, r->unparsed_uri.len);\n\n    size = sizeof(ngx_http_server_info_head) - 1\n           + scheme.len + host->len + ehost\n           + port.len + r->unparsed_uri.len + euri\n           + sizeof(ngx_http_server_info_server) - 1\n           + ngx_cycle->hostname.len\n           + sizeof(ngx_http_server_info_date) - 1\n           + ngx_cached_err_log_time.len\n           + sizeof(ngx_http_server_info_tail) - 1;\n\n    if (cscf->server_admin.len) {\n        size += sizeof(ngx_http_server_info_admin) - 1 + cscf->server_admin.len;\n    }\n\n    b = ngx_create_temp_buf(r->pool, size);\n    if (b == NULL) {\n        return NULL;\n    }\n\n    b->last = ngx_cpymem(b->last, ngx_http_server_info_head,\n                         sizeof(ngx_http_server_info_head) - 1);\n    b->last = ngx_cpymem(b->last, scheme.data, scheme.len);\n\n    if (ehost == 0) {\n        b->last = ngx_cpymem(b->last, host->data, host->len);\n\n    } else {\n        b->last = (u_char *) ngx_escape_html(b->last, host->data, host->len);\n    }\n\n    if (port.len) {\n        b->last = ngx_cpymem(b->last, port.data, port.len);\n    }\n\n    if (euri == 0) {\n        b->last = ngx_cpymem(b->last, r->unparsed_uri.data,\n                             r->unparsed_uri.len);\n\n    } else {\n        b->last = (u_char *) ngx_escape_html(b->last, r->unparsed_uri.data,\n                                             r->unparsed_uri.len);\n    }\n\n    b->last = ngx_cpymem(b->last, ngx_http_server_info_server,\n                         sizeof(ngx_http_server_info_server) - 1);\n    b->last = ngx_cpymem(b->last, ngx_cycle->hostname.data,\n                         ngx_cycle->hostname.len);\n\n    if (cscf->server_admin.len) {\n        b->last = ngx_cpymem(b->last, ngx_http_server_info_admin,\n                             sizeof(ngx_http_server_info_admin) - 1);\n        b->last = ngx_cpymem(b->last, cscf->server_admin.data,\n                             cscf->server_admin.len);\n    }\n\n    b->last = ngx_cpymem(b->last, ngx_http_server_info_date,\n                         sizeof(ngx_http_server_info_date) - 1);\n    b->last = ngx_cpymem(b->last, ngx_cached_err_log_time.data,\n                         ngx_cached_err_log_time.len);\n    b->last = ngx_cpymem(b->last, ngx_http_server_info_tail,\n                         sizeof(ngx_http_server_info_tail) - 1);\n\n    return b;\n}\n#endif\n"
  },
  {
    "path": "src/http/ngx_http_upstream.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n#if (T_NGX_HTTP_UPSTREAM_RANDOM)\n#include <ngx_md5.h>\n#endif\n\n#if (T_NGX_MULTI_UPSTREAM)\n#include <ngx_http_multi_upstream_module.h>\n#endif\n\n\n#if (NGX_HTTP_CACHE)\nstatic ngx_int_t ngx_http_upstream_cache(ngx_http_request_t *r,\n    ngx_http_upstream_t *u);\nstatic ngx_int_t ngx_http_upstream_cache_get(ngx_http_request_t *r,\n    ngx_http_upstream_t *u, ngx_http_file_cache_t **cache);\nstatic ngx_int_t ngx_http_upstream_cache_send(ngx_http_request_t *r,\n    ngx_http_upstream_t *u);\nstatic ngx_int_t ngx_http_upstream_cache_background_update(\n    ngx_http_request_t *r, ngx_http_upstream_t *u);\nstatic ngx_int_t ngx_http_upstream_cache_check_range(ngx_http_request_t *r,\n    ngx_http_upstream_t *u);\nstatic ngx_int_t ngx_http_upstream_cache_status(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_upstream_cache_last_modified(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_upstream_cache_etag(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\n#endif\n\nstatic void ngx_http_upstream_init_request(ngx_http_request_t *r);\nstatic void ngx_http_upstream_resolve_handler(ngx_resolver_ctx_t *ctx);\nstatic void ngx_http_upstream_rd_check_broken_connection(ngx_http_request_t *r);\nstatic void ngx_http_upstream_wr_check_broken_connection(ngx_http_request_t *r);\nstatic void ngx_http_upstream_check_broken_connection(ngx_http_request_t *r,\n    ngx_event_t *ev);\n#if (!T_NGX_HTTP_DYNAMIC_RESOLVE)\nstatic\n#endif\nvoid ngx_http_upstream_connect(ngx_http_request_t *r,\n    ngx_http_upstream_t *u);\nstatic ngx_int_t ngx_http_upstream_reinit(ngx_http_request_t *r,\n    ngx_http_upstream_t *u);\nstatic void ngx_http_upstream_send_request(ngx_http_request_t *r,\n    ngx_http_upstream_t *u, ngx_uint_t do_write);\nstatic ngx_int_t ngx_http_upstream_send_request_body(ngx_http_request_t *r,\n    ngx_http_upstream_t *u, ngx_uint_t do_write);\nstatic void ngx_http_upstream_send_request_handler(ngx_http_request_t *r,\n    ngx_http_upstream_t *u);\nstatic void ngx_http_upstream_read_request_handler(ngx_http_request_t *r);\nstatic void ngx_http_upstream_process_header(ngx_http_request_t *r,\n    ngx_http_upstream_t *u);\nstatic ngx_int_t ngx_http_upstream_test_next(ngx_http_request_t *r,\n    ngx_http_upstream_t *u);\nstatic ngx_int_t ngx_http_upstream_intercept_errors(ngx_http_request_t *r,\n    ngx_http_upstream_t *u);\nstatic ngx_int_t ngx_http_upstream_test_connect(ngx_connection_t *c);\nstatic ngx_int_t ngx_http_upstream_process_headers(ngx_http_request_t *r,\n    ngx_http_upstream_t *u);\nstatic ngx_int_t ngx_http_upstream_process_trailers(ngx_http_request_t *r,\n    ngx_http_upstream_t *u);\nstatic void ngx_http_upstream_send_response(ngx_http_request_t *r,\n    ngx_http_upstream_t *u);\nstatic void ngx_http_upstream_upgrade(ngx_http_request_t *r,\n    ngx_http_upstream_t *u);\nstatic void ngx_http_upstream_upgraded_read_downstream(ngx_http_request_t *r);\nstatic void ngx_http_upstream_upgraded_write_downstream(ngx_http_request_t *r);\nstatic void ngx_http_upstream_upgraded_read_upstream(ngx_http_request_t *r,\n    ngx_http_upstream_t *u);\nstatic void ngx_http_upstream_upgraded_write_upstream(ngx_http_request_t *r,\n    ngx_http_upstream_t *u);\nstatic void ngx_http_upstream_process_upgraded(ngx_http_request_t *r,\n    ngx_uint_t from_upstream, ngx_uint_t do_write);\nstatic void\n    ngx_http_upstream_process_non_buffered_downstream(ngx_http_request_t *r);\nstatic void\n    ngx_http_upstream_process_non_buffered_upstream(ngx_http_request_t *r,\n    ngx_http_upstream_t *u);\nstatic void\n    ngx_http_upstream_process_non_buffered_request(ngx_http_request_t *r,\n    ngx_uint_t do_write);\n#if (NGX_THREADS)\nstatic ngx_int_t ngx_http_upstream_thread_handler(ngx_thread_task_t *task,\n    ngx_file_t *file);\nstatic void ngx_http_upstream_thread_event_handler(ngx_event_t *ev);\n#endif\nstatic ngx_int_t ngx_http_upstream_output_filter(void *data,\n    ngx_chain_t *chain);\nstatic void ngx_http_upstream_process_downstream(ngx_http_request_t *r);\nstatic void ngx_http_upstream_process_upstream(ngx_http_request_t *r,\n    ngx_http_upstream_t *u);\nstatic void ngx_http_upstream_process_request(ngx_http_request_t *r,\n    ngx_http_upstream_t *u);\nstatic void ngx_http_upstream_store(ngx_http_request_t *r,\n    ngx_http_upstream_t *u);\nstatic void ngx_http_upstream_dummy_handler(ngx_http_request_t *r,\n    ngx_http_upstream_t *u);\nstatic void ngx_http_upstream_next(ngx_http_request_t *r,\n    ngx_http_upstream_t *u, ngx_uint_t ft_type);\nstatic void ngx_http_upstream_cleanup(void *data);\n#if (!T_NGX_HTTP_DYNAMIC_RESOLVE)\nstatic\n#endif\nvoid ngx_http_upstream_finalize_request(ngx_http_request_t *r,\n    ngx_http_upstream_t *u, ngx_int_t rc);\n\nstatic ngx_int_t ngx_http_upstream_process_header_line(ngx_http_request_t *r,\n    ngx_table_elt_t *h, ngx_uint_t offset);\nstatic ngx_int_t\n    ngx_http_upstream_process_multi_header_lines(ngx_http_request_t *r,\n    ngx_table_elt_t *h, ngx_uint_t offset);\nstatic ngx_int_t ngx_http_upstream_process_content_length(ngx_http_request_t *r,\n    ngx_table_elt_t *h, ngx_uint_t offset);\nstatic ngx_int_t ngx_http_upstream_process_last_modified(ngx_http_request_t *r,\n    ngx_table_elt_t *h, ngx_uint_t offset);\nstatic ngx_int_t ngx_http_upstream_process_set_cookie(ngx_http_request_t *r,\n    ngx_table_elt_t *h, ngx_uint_t offset);\nstatic ngx_int_t\n    ngx_http_upstream_process_cache_control(ngx_http_request_t *r,\n    ngx_table_elt_t *h, ngx_uint_t offset);\nstatic ngx_int_t ngx_http_upstream_ignore_header_line(ngx_http_request_t *r,\n    ngx_table_elt_t *h, ngx_uint_t offset);\nstatic ngx_int_t ngx_http_upstream_process_expires(ngx_http_request_t *r,\n    ngx_table_elt_t *h, ngx_uint_t offset);\nstatic ngx_int_t ngx_http_upstream_process_accel_expires(ngx_http_request_t *r,\n    ngx_table_elt_t *h, ngx_uint_t offset);\nstatic ngx_int_t ngx_http_upstream_process_limit_rate(ngx_http_request_t *r,\n    ngx_table_elt_t *h, ngx_uint_t offset);\nstatic ngx_int_t ngx_http_upstream_process_buffering(ngx_http_request_t *r,\n    ngx_table_elt_t *h, ngx_uint_t offset);\nstatic ngx_int_t ngx_http_upstream_process_charset(ngx_http_request_t *r,\n    ngx_table_elt_t *h, ngx_uint_t offset);\nstatic ngx_int_t ngx_http_upstream_process_connection(ngx_http_request_t *r,\n    ngx_table_elt_t *h, ngx_uint_t offset);\nstatic ngx_int_t\n    ngx_http_upstream_process_transfer_encoding(ngx_http_request_t *r,\n    ngx_table_elt_t *h, ngx_uint_t offset);\nstatic ngx_int_t ngx_http_upstream_process_vary(ngx_http_request_t *r,\n    ngx_table_elt_t *h, ngx_uint_t offset);\nstatic ngx_int_t ngx_http_upstream_copy_header_line(ngx_http_request_t *r,\n    ngx_table_elt_t *h, ngx_uint_t offset);\nstatic ngx_int_t\n    ngx_http_upstream_copy_multi_header_lines(ngx_http_request_t *r,\n    ngx_table_elt_t *h, ngx_uint_t offset);\nstatic ngx_int_t ngx_http_upstream_copy_content_type(ngx_http_request_t *r,\n    ngx_table_elt_t *h, ngx_uint_t offset);\nstatic ngx_int_t ngx_http_upstream_copy_last_modified(ngx_http_request_t *r,\n    ngx_table_elt_t *h, ngx_uint_t offset);\nstatic ngx_int_t ngx_http_upstream_rewrite_location(ngx_http_request_t *r,\n    ngx_table_elt_t *h, ngx_uint_t offset);\nstatic ngx_int_t ngx_http_upstream_rewrite_refresh(ngx_http_request_t *r,\n    ngx_table_elt_t *h, ngx_uint_t offset);\nstatic ngx_int_t ngx_http_upstream_rewrite_set_cookie(ngx_http_request_t *r,\n    ngx_table_elt_t *h, ngx_uint_t offset);\nstatic ngx_int_t ngx_http_upstream_copy_allow_ranges(ngx_http_request_t *r,\n    ngx_table_elt_t *h, ngx_uint_t offset);\n\nstatic ngx_int_t ngx_http_upstream_add_variables(ngx_conf_t *cf);\nstatic ngx_int_t ngx_http_upstream_addr_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_upstream_status_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_upstream_response_time_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_upstream_response_length_variable(\n    ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_upstream_header_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_upstream_trailer_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_upstream_cookie_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\n\nstatic char *ngx_http_upstream(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy);\nstatic char *ngx_http_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n\nstatic ngx_int_t ngx_http_upstream_set_local(ngx_http_request_t *r,\n  ngx_http_upstream_t *u, ngx_http_upstream_local_t *local);\n\nstatic void *ngx_http_upstream_create_main_conf(ngx_conf_t *cf);\nstatic char *ngx_http_upstream_init_main_conf(ngx_conf_t *cf, void *conf);\n\n#if (T_NGX_HTTP_UPSTREAM_RANDOM)\nstatic ngx_int_t ngx_http_upstream_init_process(ngx_cycle_t *cycle);\n#endif\n\n#if (NGX_HTTP_SSL)\nstatic void ngx_http_upstream_ssl_init_connection(ngx_http_request_t *,\n    ngx_http_upstream_t *u, ngx_connection_t *c);\nstatic void ngx_http_upstream_ssl_handshake_handler(ngx_connection_t *c);\nstatic void ngx_http_upstream_ssl_handshake(ngx_http_request_t *,\n    ngx_http_upstream_t *u, ngx_connection_t *c);\nstatic void ngx_http_upstream_ssl_save_session(ngx_connection_t *c);\nstatic ngx_int_t ngx_http_upstream_ssl_name(ngx_http_request_t *r,\n    ngx_http_upstream_t *u, ngx_connection_t *c);\nstatic ngx_int_t ngx_http_upstream_ssl_certificate(ngx_http_request_t *r,\n    ngx_http_upstream_t *u, ngx_connection_t *c);\n#endif\n\n#if (NGX_HTTP_UPSTREAM_RBTREE)\nstatic void ngx_http_upstream_rbtree_insert_value(ngx_rbtree_node_t *temp,\n    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);\n#endif\n\nstatic ngx_http_upstream_header_t  ngx_http_upstream_headers_in[] = {\n\n    { ngx_string(\"Status\"),\n                 ngx_http_upstream_process_header_line,\n                 offsetof(ngx_http_upstream_headers_in_t, status),\n                 ngx_http_upstream_copy_header_line, 0, 0 },\n\n    { ngx_string(\"Content-Type\"),\n                 ngx_http_upstream_process_header_line,\n                 offsetof(ngx_http_upstream_headers_in_t, content_type),\n                 ngx_http_upstream_copy_content_type, 0, 1 },\n\n    { ngx_string(\"Content-Length\"),\n                 ngx_http_upstream_process_content_length, 0,\n                 ngx_http_upstream_ignore_header_line, 0, 0 },\n\n    { ngx_string(\"Date\"),\n                 ngx_http_upstream_process_header_line,\n                 offsetof(ngx_http_upstream_headers_in_t, date),\n                 ngx_http_upstream_copy_header_line,\n                 offsetof(ngx_http_headers_out_t, date), 0 },\n\n    { ngx_string(\"Last-Modified\"),\n                 ngx_http_upstream_process_last_modified, 0,\n                 ngx_http_upstream_copy_last_modified, 0, 0 },\n\n    { ngx_string(\"ETag\"),\n                 ngx_http_upstream_process_header_line,\n                 offsetof(ngx_http_upstream_headers_in_t, etag),\n                 ngx_http_upstream_copy_header_line,\n                 offsetof(ngx_http_headers_out_t, etag), 0 },\n\n    { ngx_string(\"Server\"),\n                 ngx_http_upstream_process_header_line,\n                 offsetof(ngx_http_upstream_headers_in_t, server),\n                 ngx_http_upstream_copy_header_line,\n                 offsetof(ngx_http_headers_out_t, server), 0 },\n\n    { ngx_string(\"WWW-Authenticate\"),\n                 ngx_http_upstream_process_multi_header_lines,\n                 offsetof(ngx_http_upstream_headers_in_t, www_authenticate),\n                 ngx_http_upstream_copy_header_line, 0, 0 },\n\n    { ngx_string(\"Location\"),\n                 ngx_http_upstream_process_header_line,\n                 offsetof(ngx_http_upstream_headers_in_t, location),\n                 ngx_http_upstream_rewrite_location, 0, 0 },\n\n    { ngx_string(\"Refresh\"),\n                 ngx_http_upstream_process_header_line,\n                 offsetof(ngx_http_upstream_headers_in_t, refresh),\n                 ngx_http_upstream_rewrite_refresh, 0, 0 },\n\n    { ngx_string(\"Set-Cookie\"),\n                 ngx_http_upstream_process_set_cookie,\n                 offsetof(ngx_http_upstream_headers_in_t, set_cookie),\n                 ngx_http_upstream_rewrite_set_cookie, 0, 1 },\n\n    { ngx_string(\"Content-Disposition\"),\n                 ngx_http_upstream_ignore_header_line, 0,\n                 ngx_http_upstream_copy_header_line, 0, 1 },\n\n    { ngx_string(\"Cache-Control\"),\n                 ngx_http_upstream_process_cache_control, 0,\n                 ngx_http_upstream_copy_multi_header_lines,\n                 offsetof(ngx_http_headers_out_t, cache_control), 1 },\n\n    { ngx_string(\"Expires\"),\n                 ngx_http_upstream_process_expires, 0,\n                 ngx_http_upstream_copy_header_line,\n                 offsetof(ngx_http_headers_out_t, expires), 1 },\n\n    { ngx_string(\"Accept-Ranges\"),\n                 ngx_http_upstream_ignore_header_line, 0,\n                 ngx_http_upstream_copy_allow_ranges,\n                 offsetof(ngx_http_headers_out_t, accept_ranges), 1 },\n\n    { ngx_string(\"Content-Range\"),\n                 ngx_http_upstream_ignore_header_line, 0,\n                 ngx_http_upstream_copy_header_line,\n                 offsetof(ngx_http_headers_out_t, content_range), 0 },\n\n    { ngx_string(\"Connection\"),\n                 ngx_http_upstream_process_connection, 0,\n                 ngx_http_upstream_ignore_header_line, 0, 0 },\n\n    { ngx_string(\"Keep-Alive\"),\n                 ngx_http_upstream_ignore_header_line, 0,\n                 ngx_http_upstream_ignore_header_line, 0, 0 },\n\n    { ngx_string(\"Vary\"),\n                 ngx_http_upstream_process_vary, 0,\n                 ngx_http_upstream_copy_header_line, 0, 0 },\n\n    { ngx_string(\"Link\"),\n                 ngx_http_upstream_ignore_header_line, 0,\n                 ngx_http_upstream_copy_multi_header_lines,\n                 offsetof(ngx_http_headers_out_t, link), 0 },\n\n    { ngx_string(\"X-Accel-Expires\"),\n                 ngx_http_upstream_process_accel_expires, 0,\n                 ngx_http_upstream_copy_header_line, 0, 0 },\n\n    { ngx_string(\"X-Accel-Redirect\"),\n                 ngx_http_upstream_process_header_line,\n                 offsetof(ngx_http_upstream_headers_in_t, x_accel_redirect),\n                 ngx_http_upstream_copy_header_line, 0, 0 },\n\n    { ngx_string(\"X-Accel-Limit-Rate\"),\n                 ngx_http_upstream_process_limit_rate, 0,\n                 ngx_http_upstream_copy_header_line, 0, 0 },\n\n    { ngx_string(\"X-Accel-Buffering\"),\n                 ngx_http_upstream_process_buffering, 0,\n                 ngx_http_upstream_copy_header_line, 0, 0 },\n\n    { ngx_string(\"X-Accel-Charset\"),\n                 ngx_http_upstream_process_charset, 0,\n                 ngx_http_upstream_copy_header_line, 0, 0 },\n\n    { ngx_string(\"Transfer-Encoding\"),\n                 ngx_http_upstream_process_transfer_encoding, 0,\n                 ngx_http_upstream_ignore_header_line, 0, 0 },\n\n    { ngx_string(\"Content-Encoding\"),\n                 ngx_http_upstream_ignore_header_line, 0,\n                 ngx_http_upstream_copy_header_line,\n                 offsetof(ngx_http_headers_out_t, content_encoding), 0 },\n\n    { ngx_null_string, NULL, 0, NULL, 0, 0 }\n};\n\n\nstatic ngx_command_t  ngx_http_upstream_commands[] = {\n\n    { ngx_string(\"upstream\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE1,\n      ngx_http_upstream,\n      0,\n      0,\n      NULL },\n\n    { ngx_string(\"server\"),\n      NGX_HTTP_UPS_CONF|NGX_CONF_1MORE,\n      ngx_http_upstream_server,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      0,\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_upstream_module_ctx = {\n    ngx_http_upstream_add_variables,       /* preconfiguration */\n    NULL,                                  /* postconfiguration */\n\n    ngx_http_upstream_create_main_conf,    /* create main configuration */\n    ngx_http_upstream_init_main_conf,      /* 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_upstream_module = {\n    NGX_MODULE_V1,\n    &ngx_http_upstream_module_ctx,         /* module context */\n    ngx_http_upstream_commands,            /* module directives */\n    NGX_HTTP_MODULE,                       /* module type */\n    NULL,                                  /* init master */\n    NULL,                                  /* init module */\n#if (T_NGX_HTTP_UPSTREAM_RANDOM)\n    ngx_http_upstream_init_process,        /* init process */\n#else\n    NULL,                                  /* init process */\n#endif\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_http_variable_t  ngx_http_upstream_vars[] = {\n\n    { ngx_string(\"upstream_addr\"), NULL,\n      ngx_http_upstream_addr_variable, 0,\n      NGX_HTTP_VAR_NOCACHEABLE, 0 },\n\n    { ngx_string(\"upstream_status\"), NULL,\n      ngx_http_upstream_status_variable, 0,\n      NGX_HTTP_VAR_NOCACHEABLE, 0 },\n\n    { ngx_string(\"upstream_connect_time\"), NULL,\n      ngx_http_upstream_response_time_variable, 2,\n      NGX_HTTP_VAR_NOCACHEABLE, 0 },\n\n    { ngx_string(\"upstream_header_time\"), NULL,\n      ngx_http_upstream_response_time_variable, 1,\n      NGX_HTTP_VAR_NOCACHEABLE, 0 },\n\n    { ngx_string(\"upstream_response_time\"), NULL,\n      ngx_http_upstream_response_time_variable, 0,\n      NGX_HTTP_VAR_NOCACHEABLE, 0 },\n\n    { ngx_string(\"upstream_response_length\"), NULL,\n      ngx_http_upstream_response_length_variable, 0,\n      NGX_HTTP_VAR_NOCACHEABLE, 0 },\n\n    { ngx_string(\"upstream_bytes_received\"), NULL,\n      ngx_http_upstream_response_length_variable, 1,\n      NGX_HTTP_VAR_NOCACHEABLE, 0 },\n\n    { ngx_string(\"upstream_bytes_sent\"), NULL,\n      ngx_http_upstream_response_length_variable, 2,\n      NGX_HTTP_VAR_NOCACHEABLE, 0 },\n\n#if (NGX_HTTP_CACHE)\n\n    { ngx_string(\"upstream_cache_status\"), NULL,\n      ngx_http_upstream_cache_status, 0,\n      NGX_HTTP_VAR_NOCACHEABLE, 0 },\n\n    { ngx_string(\"upstream_cache_last_modified\"), NULL,\n      ngx_http_upstream_cache_last_modified, 0,\n      NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },\n\n    { ngx_string(\"upstream_cache_etag\"), NULL,\n      ngx_http_upstream_cache_etag, 0,\n      NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },\n\n#endif\n\n    { ngx_string(\"upstream_http_\"), NULL, ngx_http_upstream_header_variable,\n      0, NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_PREFIX, 0 },\n\n    { ngx_string(\"upstream_trailer_\"), NULL, ngx_http_upstream_trailer_variable,\n      0, NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_PREFIX, 0 },\n\n    { ngx_string(\"upstream_cookie_\"), NULL, ngx_http_upstream_cookie_variable,\n      0, NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_PREFIX, 0 },\n\n      ngx_http_null_variable\n};\n\n\nstatic ngx_http_upstream_next_t  ngx_http_upstream_next_errors[] = {\n    { 500, NGX_HTTP_UPSTREAM_FT_HTTP_500 },\n    { 502, NGX_HTTP_UPSTREAM_FT_HTTP_502 },\n    { 503, NGX_HTTP_UPSTREAM_FT_HTTP_503 },\n    { 504, NGX_HTTP_UPSTREAM_FT_HTTP_504 },\n    { 403, NGX_HTTP_UPSTREAM_FT_HTTP_403 },\n    { 404, NGX_HTTP_UPSTREAM_FT_HTTP_404 },\n    { 429, NGX_HTTP_UPSTREAM_FT_HTTP_429 },\n    { 0, 0 }\n};\n\n\nngx_conf_bitmask_t  ngx_http_upstream_cache_method_mask[] = {\n    { ngx_string(\"GET\"), NGX_HTTP_GET },\n    { ngx_string(\"HEAD\"), NGX_HTTP_HEAD },\n    { ngx_string(\"POST\"), NGX_HTTP_POST },\n    { ngx_null_string, 0 }\n};\n\n\nngx_conf_bitmask_t  ngx_http_upstream_ignore_headers_masks[] = {\n    { ngx_string(\"X-Accel-Redirect\"), NGX_HTTP_UPSTREAM_IGN_XA_REDIRECT },\n    { ngx_string(\"X-Accel-Expires\"), NGX_HTTP_UPSTREAM_IGN_XA_EXPIRES },\n    { ngx_string(\"X-Accel-Limit-Rate\"), NGX_HTTP_UPSTREAM_IGN_XA_LIMIT_RATE },\n    { ngx_string(\"X-Accel-Buffering\"), NGX_HTTP_UPSTREAM_IGN_XA_BUFFERING },\n    { ngx_string(\"X-Accel-Charset\"), NGX_HTTP_UPSTREAM_IGN_XA_CHARSET },\n    { ngx_string(\"Expires\"), NGX_HTTP_UPSTREAM_IGN_EXPIRES },\n    { ngx_string(\"Cache-Control\"), NGX_HTTP_UPSTREAM_IGN_CACHE_CONTROL },\n    { ngx_string(\"Set-Cookie\"), NGX_HTTP_UPSTREAM_IGN_SET_COOKIE },\n    { ngx_string(\"Vary\"), NGX_HTTP_UPSTREAM_IGN_VARY },\n    { ngx_null_string, 0 }\n};\n\n\nngx_int_t\nngx_http_upstream_create(ngx_http_request_t *r)\n{\n    ngx_http_upstream_t  *u;\n\n    u = r->upstream;\n\n    if (u && u->cleanup) {\n        r->main->count++;\n        ngx_http_upstream_cleanup(r);\n    }\n\n    u = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_t));\n    if (u == NULL) {\n        return NGX_ERROR;\n    }\n\n    r->upstream = u;\n\n    u->peer.log = r->connection->log;\n    u->peer.log_error = NGX_ERROR_ERR;\n\n#if (NGX_HTTP_CACHE)\n    r->cache = NULL;\n#endif\n\n    u->headers_in.content_length_n = -1;\n    u->headers_in.last_modified_time = -1;\n\n    return NGX_OK;\n}\n\n\nvoid\nngx_http_upstream_init(ngx_http_request_t *r)\n{\n    ngx_connection_t     *c;\n\n    c = r->connection;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http init upstream, client timer: %d\", c->read->timer_set);\n\n#if (NGX_HTTP_V2)\n    if (r->stream) {\n        ngx_http_upstream_init_request(r);\n        return;\n    }\n#endif\n\n#if (T_NGX_XQUIC)\n    if (r->xqstream) {\n        ngx_http_upstream_init_request(r);\n        return;\n    }\n#endif\n\n    if (c->read->timer_set) {\n        ngx_del_timer(c->read);\n    }\n\n    if (ngx_event_flags & NGX_USE_CLEAR_EVENT) {\n\n        if (!c->write->active) {\n            if (ngx_add_event(c->write, NGX_WRITE_EVENT, NGX_CLEAR_EVENT)\n                == NGX_ERROR)\n            {\n                ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n                return;\n            }\n        }\n    }\n\n    ngx_http_upstream_init_request(r);\n}\n\n\nstatic void\nngx_http_upstream_init_request(ngx_http_request_t *r)\n{\n    ngx_str_t                      *host;\n    ngx_uint_t                      i;\n\n#if (NGX_HTTP_UPSTREAM_RBTREE)\n\n    ngx_list_part_t                *part;\n\n#endif\n\n    ngx_resolver_ctx_t             *ctx, temp;\n    ngx_http_cleanup_t             *cln;\n    ngx_http_upstream_t            *u;\n    ngx_http_core_loc_conf_t       *clcf;\n    ngx_http_upstream_srv_conf_t   *uscf, **uscfp;\n    ngx_http_upstream_main_conf_t  *umcf;\n\n    if (r->aio) {\n        return;\n    }\n\n    u = r->upstream;\n\n#if (NGX_HTTP_CACHE)\n\n    if (u->conf->cache) {\n        ngx_int_t  rc;\n\n        rc = ngx_http_upstream_cache(r, u);\n\n        if (rc == NGX_BUSY) {\n            r->write_event_handler = ngx_http_upstream_init_request;\n            return;\n        }\n\n        r->write_event_handler = ngx_http_request_empty_handler;\n\n        if (rc == NGX_ERROR) {\n            ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n            return;\n        }\n\n        if (rc == NGX_OK) {\n            rc = ngx_http_upstream_cache_send(r, u);\n\n            if (rc == NGX_DONE) {\n                return;\n            }\n\n            if (rc == NGX_HTTP_UPSTREAM_INVALID_HEADER) {\n                rc = NGX_DECLINED;\n                r->cached = 0;\n                u->buffer.start = NULL;\n                u->cache_status = NGX_HTTP_CACHE_MISS;\n                u->request_sent = 1;\n            }\n        }\n\n        if (rc != NGX_DECLINED) {\n            ngx_http_finalize_request(r, rc);\n            return;\n        }\n    }\n\n#endif\n\n    u->store = u->conf->store;\n\n    if (!u->store && !r->post_action && !u->conf->ignore_client_abort) {\n\n        if (r->connection->read->ready) {\n            ngx_post_event(r->connection->read, &ngx_posted_events);\n\n        } else {\n            if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) {\n                ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n                return;\n            }\n        }\n\n        r->read_event_handler = ngx_http_upstream_rd_check_broken_connection;\n        r->write_event_handler = ngx_http_upstream_wr_check_broken_connection;\n    }\n\n    if (r->request_body) {\n        u->request_bufs = r->request_body->bufs;\n    }\n\n    if (u->create_request(r) != NGX_OK) {\n        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return;\n    }\n\n    if (ngx_http_upstream_set_local(r, u, u->conf->local) != NGX_OK) {\n        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return;\n    }\n\n    if (u->conf->socket_keepalive) {\n        u->peer.so_keepalive = 1;\n    }\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    u->output.alignment = clcf->directio_alignment;\n    u->output.pool = r->pool;\n    u->output.bufs.num = 1;\n    u->output.bufs.size = clcf->client_body_buffer_size;\n\n    if (u->output.output_filter == NULL) {\n        u->output.output_filter = ngx_chain_writer;\n        u->output.filter_ctx = &u->writer;\n    }\n\n    u->writer.pool = r->pool;\n\n    if (r->upstream_states == NULL) {\n\n        r->upstream_states = ngx_array_create(r->pool, 1,\n                                            sizeof(ngx_http_upstream_state_t));\n        if (r->upstream_states == NULL) {\n            ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n            return;\n        }\n\n    } else {\n\n        u->state = ngx_array_push(r->upstream_states);\n        if (u->state == NULL) {\n            ngx_http_upstream_finalize_request(r, u,\n                                               NGX_HTTP_INTERNAL_SERVER_ERROR);\n            return;\n        }\n\n        ngx_memzero(u->state, sizeof(ngx_http_upstream_state_t));\n    }\n\n    cln = ngx_http_cleanup_add(r, 0);\n    if (cln == NULL) {\n        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return;\n    }\n\n    cln->handler = ngx_http_upstream_cleanup;\n    cln->data = r;\n    u->cleanup = &cln->handler;\n\n    if (u->resolved == NULL) {\n\n        uscf = u->conf->upstream;\n\n    } else {\n\n#if (NGX_HTTP_SSL)\n        u->ssl_name = u->resolved->host;\n#endif\n\n        host = &u->resolved->host;\n\n        umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);\n\n#if (NGX_HTTP_UPSTREAM_RBTREE)\n\n        uscf = ngx_http_upstream_rbtree_lookup(umcf, host);\n\n        if (uscf != NULL && ((uscf->port == 0 && u->resolved->no_port)\n            || uscf->port == u->resolved->port))\n        {\n            goto found;\n        }\n\n        part = &umcf->implicit_upstreams.part;\n        uscfp = 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                uscfp = part->elts;\n                i = 0;\n            }\n\n#else\n\n        uscfp = umcf->upstreams.elts;\n\n        for (i = 0; i < umcf->upstreams.nelts; i++) {\n\n#endif\n\n            uscf = uscfp[i];\n\n            if (uscf->host.len == host->len\n                && ((uscf->port == 0 && u->resolved->no_port)\n                     || uscf->port == u->resolved->port)\n                && ngx_strncasecmp(uscf->host.data, host->data, host->len) == 0)\n            {\n                goto found;\n            }\n        }\n\n        if (u->resolved->sockaddr) {\n\n            if (u->resolved->port == 0\n                && u->resolved->sockaddr->sa_family != AF_UNIX)\n            {\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                              \"no port in upstream \\\"%V\\\"\", host);\n                ngx_http_upstream_finalize_request(r, u,\n                                               NGX_HTTP_INTERNAL_SERVER_ERROR);\n                return;\n            }\n\n            if (ngx_http_upstream_create_round_robin_peer(r, u->resolved)\n                != NGX_OK)\n            {\n                ngx_http_upstream_finalize_request(r, u,\n                                               NGX_HTTP_INTERNAL_SERVER_ERROR);\n                return;\n            }\n\n            ngx_http_upstream_connect(r, u);\n\n            return;\n        }\n\n        if (u->resolved->port == 0) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"no port in upstream \\\"%V\\\"\", host);\n            ngx_http_upstream_finalize_request(r, u,\n                                               NGX_HTTP_INTERNAL_SERVER_ERROR);\n            return;\n        }\n\n        temp.name = *host;\n\n        ctx = ngx_resolve_start(clcf->resolver, &temp);\n        if (ctx == NULL) {\n            ngx_http_upstream_finalize_request(r, u,\n                                               NGX_HTTP_INTERNAL_SERVER_ERROR);\n            return;\n        }\n\n        if (ctx == NGX_NO_RESOLVER) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"no resolver defined to resolve %V\", host);\n\n            ngx_http_upstream_finalize_request(r, u, NGX_HTTP_BAD_GATEWAY);\n            return;\n        }\n\n        ctx->name = *host;\n        ctx->handler = ngx_http_upstream_resolve_handler;\n        ctx->data = r;\n        ctx->timeout = clcf->resolver_timeout;\n\n        u->resolved->ctx = ctx;\n\n        if (ngx_resolve_name(ctx) != NGX_OK) {\n            u->resolved->ctx = NULL;\n            ngx_http_upstream_finalize_request(r, u,\n                                               NGX_HTTP_INTERNAL_SERVER_ERROR);\n            return;\n        }\n\n        return;\n    }\n\nfound:\n\n    if (uscf == NULL) {\n        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,\n                      \"no upstream configuration\");\n        ngx_http_upstream_finalize_request(r, u,\n                                           NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return;\n    }\n\n    u->upstream = uscf;\n\n#if (NGX_HTTP_SSL)\n    u->ssl_name = uscf->host;\n#endif\n\n    if (uscf->peer.init(r, uscf) != NGX_OK) {\n        ngx_http_upstream_finalize_request(r, u,\n                                           NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return;\n    }\n\n    u->peer.start_time = ngx_current_msec;\n\n    if (u->conf->next_upstream_tries\n        && u->peer.tries > u->conf->next_upstream_tries)\n    {\n        u->peer.tries = u->conf->next_upstream_tries;\n    }\n\n    ngx_http_upstream_connect(r, u);\n}\n\n\n#if (NGX_HTTP_CACHE)\n\nstatic ngx_int_t\nngx_http_upstream_cache(ngx_http_request_t *r, ngx_http_upstream_t *u)\n{\n    ngx_int_t               rc;\n    ngx_http_cache_t       *c;\n    ngx_http_file_cache_t  *cache;\n\n    c = r->cache;\n\n    if (c == NULL) {\n\n        if (!(r->method & u->conf->cache_methods)) {\n            return NGX_DECLINED;\n        }\n\n        rc = ngx_http_upstream_cache_get(r, u, &cache);\n\n        if (rc != NGX_OK) {\n            return rc;\n        }\n\n        if (r->method == NGX_HTTP_HEAD && u->conf->cache_convert_head) {\n            u->method = ngx_http_core_get_method;\n        }\n\n        if (ngx_http_file_cache_new(r) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        if (u->create_key(r) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        /* TODO: add keys */\n\n        ngx_http_file_cache_create_key(r);\n\n        if (r->cache->header_start + 256 > u->conf->buffer_size) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"%V_buffer_size %uz is not enough for cache key, \"\n                          \"it should be increased to at least %uz\",\n                          &u->conf->module, u->conf->buffer_size,\n                          ngx_align(r->cache->header_start + 256, 1024));\n\n            r->cache = NULL;\n            return NGX_DECLINED;\n        }\n\n        u->cacheable = 1;\n\n        c = r->cache;\n\n        c->body_start = u->conf->buffer_size;\n        c->min_uses = u->conf->cache_min_uses;\n        c->file_cache = cache;\n\n        switch (ngx_http_test_predicates(r, u->conf->cache_bypass)) {\n\n        case NGX_ERROR:\n            return NGX_ERROR;\n\n        case NGX_DECLINED:\n            u->cache_status = NGX_HTTP_CACHE_BYPASS;\n            return NGX_DECLINED;\n\n        default: /* NGX_OK */\n            break;\n        }\n\n        c->lock = u->conf->cache_lock;\n        c->lock_timeout = u->conf->cache_lock_timeout;\n        c->lock_age = u->conf->cache_lock_age;\n\n        u->cache_status = NGX_HTTP_CACHE_MISS;\n    }\n\n    rc = ngx_http_file_cache_open(r);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http upstream cache: %i\", rc);\n\n    switch (rc) {\n\n    case NGX_HTTP_CACHE_STALE:\n\n        if (((u->conf->cache_use_stale & NGX_HTTP_UPSTREAM_FT_UPDATING)\n             || c->stale_updating) && !r->background\n            && u->conf->cache_background_update)\n        {\n            if (ngx_http_upstream_cache_background_update(r, u) == NGX_OK) {\n                r->cache->background = 1;\n                u->cache_status = rc;\n                rc = NGX_OK;\n\n            } else {\n                rc = NGX_ERROR;\n            }\n        }\n\n        break;\n\n    case NGX_HTTP_CACHE_UPDATING:\n\n        if (((u->conf->cache_use_stale & NGX_HTTP_UPSTREAM_FT_UPDATING)\n             || c->stale_updating) && !r->background)\n        {\n            u->cache_status = rc;\n            rc = NGX_OK;\n\n        } else {\n            rc = NGX_HTTP_CACHE_STALE;\n        }\n\n        break;\n\n    case NGX_OK:\n        u->cache_status = NGX_HTTP_CACHE_HIT;\n    }\n\n    switch (rc) {\n\n    case NGX_OK:\n\n        return NGX_OK;\n\n    case NGX_HTTP_CACHE_STALE:\n\n        c->valid_sec = 0;\n        c->updating_sec = 0;\n        c->error_sec = 0;\n\n        u->buffer.start = NULL;\n        u->cache_status = NGX_HTTP_CACHE_EXPIRED;\n\n        break;\n\n    case NGX_DECLINED:\n\n        if ((size_t) (u->buffer.end - u->buffer.start) < u->conf->buffer_size) {\n            u->buffer.start = NULL;\n\n        } else {\n            u->buffer.pos = u->buffer.start + c->header_start;\n            u->buffer.last = u->buffer.pos;\n        }\n\n        break;\n\n    case NGX_HTTP_CACHE_SCARCE:\n\n        u->cacheable = 0;\n\n        break;\n\n    case NGX_AGAIN:\n\n        return NGX_BUSY;\n\n    case NGX_ERROR:\n\n        return NGX_ERROR;\n\n    default:\n\n        /* cached NGX_HTTP_BAD_GATEWAY, NGX_HTTP_GATEWAY_TIME_OUT, etc. */\n\n        u->cache_status = NGX_HTTP_CACHE_HIT;\n\n        return rc;\n    }\n\n    if (ngx_http_upstream_cache_check_range(r, u) == NGX_DECLINED) {\n        u->cacheable = 0;\n    }\n\n    r->cached = 0;\n\n    return NGX_DECLINED;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_cache_get(ngx_http_request_t *r, ngx_http_upstream_t *u,\n    ngx_http_file_cache_t **cache)\n{\n    ngx_str_t               *name, val;\n    ngx_uint_t               i;\n    ngx_http_file_cache_t  **caches;\n\n    if (u->conf->cache_zone) {\n        *cache = u->conf->cache_zone->data;\n        return NGX_OK;\n    }\n\n    if (ngx_http_complex_value(r, u->conf->cache_value, &val) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    if (val.len == 0\n        || (val.len == 3 && ngx_strncmp(val.data, \"off\", 3) == 0))\n    {\n        return NGX_DECLINED;\n    }\n\n    caches = u->caches->elts;\n\n    for (i = 0; i < u->caches->nelts; i++) {\n        name = &caches[i]->shm_zone->shm.name;\n\n        if (name->len == val.len\n            && ngx_strncmp(name->data, val.data, val.len) == 0)\n        {\n            *cache = caches[i];\n            return NGX_OK;\n        }\n    }\n\n    ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                  \"cache \\\"%V\\\" not found\", &val);\n\n    return NGX_ERROR;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_cache_send(ngx_http_request_t *r, ngx_http_upstream_t *u)\n{\n    ngx_int_t          rc;\n    ngx_http_cache_t  *c;\n\n    r->cached = 1;\n    c = r->cache;\n\n    if (c->header_start == c->body_start) {\n        r->http_version = NGX_HTTP_VERSION_9;\n        return ngx_http_cache_send(r);\n    }\n\n    /* TODO: cache stack */\n\n    u->buffer = *c->buf;\n    u->buffer.pos += c->header_start;\n\n    ngx_memzero(&u->headers_in, sizeof(ngx_http_upstream_headers_in_t));\n    u->headers_in.content_length_n = -1;\n    u->headers_in.last_modified_time = -1;\n\n    if (ngx_list_init(&u->headers_in.headers, r->pool, 8,\n                      sizeof(ngx_table_elt_t))\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    if (ngx_list_init(&u->headers_in.trailers, r->pool, 2,\n                      sizeof(ngx_table_elt_t))\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    rc = u->process_header(r);\n\n    if (rc == NGX_OK) {\n\n        if (ngx_http_upstream_process_headers(r, u) != NGX_OK) {\n            return NGX_DONE;\n        }\n\n        return ngx_http_cache_send(r);\n    }\n\n    if (rc == NGX_ERROR) {\n        return NGX_ERROR;\n    }\n\n    if (rc == NGX_AGAIN) {\n        rc = NGX_HTTP_UPSTREAM_INVALID_HEADER;\n    }\n\n    /* rc == NGX_HTTP_UPSTREAM_INVALID_HEADER */\n\n    ngx_log_error(NGX_LOG_CRIT, r->connection->log, 0,\n                  \"cache file \\\"%s\\\" contains invalid header\",\n                  c->file.name.data);\n\n    /* TODO: delete file */\n\n    return rc;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_cache_background_update(ngx_http_request_t *r,\n    ngx_http_upstream_t *u)\n{\n    ngx_http_request_t  *sr;\n\n    if (r == r->main) {\n        r->preserve_body = 1;\n    }\n\n    if (ngx_http_subrequest(r, &r->uri, &r->args, &sr, NULL,\n                            NGX_HTTP_SUBREQUEST_CLONE\n                            |NGX_HTTP_SUBREQUEST_BACKGROUND)\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    sr->header_only = 1;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_cache_check_range(ngx_http_request_t *r,\n    ngx_http_upstream_t *u)\n{\n    off_t             offset;\n    u_char           *p, *start;\n    ngx_table_elt_t  *h;\n\n    h = r->headers_in.range;\n\n    if (h == NULL\n        || !u->cacheable\n        || u->conf->cache_max_range_offset == NGX_MAX_OFF_T_VALUE)\n    {\n        return NGX_OK;\n    }\n\n    if (u->conf->cache_max_range_offset == 0) {\n        return NGX_DECLINED;\n    }\n\n    if (h->value.len < 7\n        || ngx_strncasecmp(h->value.data, (u_char *) \"bytes=\", 6) != 0)\n    {\n        return NGX_OK;\n    }\n\n    p = h->value.data + 6;\n\n    while (*p == ' ') { p++; }\n\n    if (*p == '-') {\n        return NGX_DECLINED;\n    }\n\n    start = p;\n\n    while (*p >= '0' && *p <= '9') { p++; }\n\n    offset = ngx_atoof(start, p - start);\n\n    if (offset >= u->conf->cache_max_range_offset) {\n        return NGX_DECLINED;\n    }\n\n    return NGX_OK;\n}\n\n#endif\n\n\nstatic void\nngx_http_upstream_resolve_handler(ngx_resolver_ctx_t *ctx)\n{\n    ngx_uint_t                     run_posted;\n    ngx_connection_t              *c;\n    ngx_http_request_t            *r;\n    ngx_http_upstream_t           *u;\n    ngx_http_upstream_resolved_t  *ur;\n\n    run_posted = ctx->async;\n\n    r = ctx->data;\n    c = r->connection;\n\n    u = r->upstream;\n    ur = u->resolved;\n\n    ngx_http_set_log_request(c->log, r);\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http upstream resolve: \\\"%V?%V\\\"\", &r->uri, &r->args);\n\n    if (ctx->state) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"%V could not be resolved (%i: %s)\",\n                      &ctx->name, ctx->state,\n                      ngx_resolver_strerror(ctx->state));\n\n        ngx_http_upstream_finalize_request(r, u, NGX_HTTP_BAD_GATEWAY);\n        goto failed;\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, ur->addrs[i].socklen,\n                                 text, 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    if (ngx_http_upstream_create_round_robin_peer(r, ur) != NGX_OK) {\n        ngx_http_upstream_finalize_request(r, u,\n                                           NGX_HTTP_INTERNAL_SERVER_ERROR);\n        goto failed;\n    }\n\n    ngx_resolve_name_done(ctx);\n    ur->ctx = NULL;\n\n    u->peer.start_time = ngx_current_msec;\n\n    if (u->conf->next_upstream_tries\n        && u->peer.tries > u->conf->next_upstream_tries)\n    {\n        u->peer.tries = u->conf->next_upstream_tries;\n    }\n\n    ngx_http_upstream_connect(r, u);\n\nfailed:\n\n    if (run_posted) {\n        ngx_http_run_posted_requests(c);\n    }\n}\n\n\nstatic void\nngx_http_upstream_handler(ngx_event_t *ev)\n{\n    ngx_connection_t     *c;\n    ngx_http_request_t   *r;\n    ngx_http_upstream_t  *u;\n\n    c = ev->data;\n    r = c->data;\n\n    u = r->upstream;\n    c = r->connection;\n\n    ngx_http_set_log_request(c->log, r);\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http upstream request: \\\"%V?%V\\\"\", &r->uri, &r->args);\n\n    if (ev->delayed && ev->timedout) {\n        ev->delayed = 0;\n        ev->timedout = 0;\n    }\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 void\nngx_http_upstream_rd_check_broken_connection(ngx_http_request_t *r)\n{\n    ngx_http_upstream_check_broken_connection(r, r->connection->read);\n}\n\n\nstatic void\nngx_http_upstream_wr_check_broken_connection(ngx_http_request_t *r)\n{\n    ngx_http_upstream_check_broken_connection(r, r->connection->write);\n}\n\n\nstatic void\nngx_http_upstream_check_broken_connection(ngx_http_request_t *r,\n    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    ngx_http_upstream_t  *u;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ev->log, 0,\n                   \"http upstream check client, write event:%d, \\\"%V\\\"\",\n                   ev->write, &r->uri);\n\n    c = r->connection;\n    u = r->upstream;\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#if (NGX_HTTP_SSL && NGX_SSL_ASYNC)\n            if (c->async_enable && ngx_del_async_conn) {\n                if (c->num_async_fds) {\n                    ngx_del_async_conn(c, NGX_DISABLE_EVENT);\n                    c->num_async_fds--;\n                }\n            }\n#endif\n\n            if (ngx_del_event(ev, event, 0) != NGX_OK) {\n                ngx_http_upstream_finalize_request(r, u,\n                                               NGX_HTTP_INTERNAL_SERVER_ERROR);\n                return;\n            }\n        }\n\n        if (!u->cacheable) {\n            ngx_http_upstream_finalize_request(r, u,\n                                               NGX_HTTP_CLIENT_CLOSED_REQUEST);\n        }\n\n        return;\n    }\n\n#if (NGX_HTTP_V2)\n    if (r->stream) {\n        return;\n    }\n#endif\n\n#if (T_NGX_XQUIC)\n    if (r->xqstream) {\n        return;\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;\n        }\n\n        ev->eof = 1;\n        c->error = 1;\n\n        if (ev->kq_errno) {\n            ev->error = 1;\n        }\n\n        if (!u->cacheable && u->peer.connection) {\n            ngx_log_error(NGX_LOG_INFO, ev->log, ev->kq_errno,\n                          \"kevent() reported that client prematurely closed \"\n                          \"connection, so upstream connection is closed too\");\n            ngx_http_upstream_finalize_request(r, u,\n                                               NGX_HTTP_CLIENT_CLOSED_REQUEST);\n            return;\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        if (u->peer.connection == NULL) {\n            ngx_http_upstream_finalize_request(r, u,\n                                               NGX_HTTP_CLIENT_CLOSED_REQUEST);\n        }\n\n        return;\n    }\n\n#endif\n\n#if (NGX_HAVE_EPOLLRDHUP)\n\n    if ((ngx_event_flags & NGX_USE_EPOLL_EVENT) && ngx_use_epoll_rdhup) {\n        socklen_t  len;\n\n        if (!ev->pending_eof) {\n            return;\n        }\n\n        ev->eof = 1;\n        c->error = 1;\n\n        err = 0;\n        len = sizeof(ngx_err_t);\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_socket_errno;\n        }\n\n        if (err) {\n            ev->error = 1;\n        }\n\n        if (!u->cacheable && u->peer.connection) {\n            ngx_log_error(NGX_LOG_INFO, ev->log, err,\n                        \"epoll_wait() reported that client prematurely closed \"\n                        \"connection, so upstream connection is closed too\");\n            ngx_http_upstream_finalize_request(r, u,\n                                               NGX_HTTP_CLIENT_CLOSED_REQUEST);\n            return;\n        }\n\n        ngx_log_error(NGX_LOG_INFO, ev->log, err,\n                      \"epoll_wait() reported that client prematurely closed \"\n                      \"connection\");\n\n        if (u->peer.connection == NULL) {\n            ngx_http_upstream_finalize_request(r, u,\n                                               NGX_HTTP_CLIENT_CLOSED_REQUEST);\n        }\n\n        return;\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 upstream recv(): %d\", n);\n\n    if (ev->write && (n >= 0 || err == NGX_EAGAIN)) {\n        return;\n    }\n\n    if ((ngx_event_flags & NGX_USE_LEVEL_EVENT) && ev->active) {\n\n        event = ev->write ? NGX_WRITE_EVENT : NGX_READ_EVENT;\n#if (NGX_HTTP_SSL && NGX_SSL_ASYNC)\n        if (c->async_enable && ngx_del_async_conn) {\n            if (c->num_async_fds) {\n                ngx_del_async_conn(c, NGX_DISABLE_EVENT);\n                c->num_async_fds--;\n            }\n        }\n#endif\n\n        if (ngx_del_event(ev, event, 0) != NGX_OK) {\n            ngx_http_upstream_finalize_request(r, u,\n                                               NGX_HTTP_INTERNAL_SERVER_ERROR);\n            return;\n        }\n    }\n\n    if (n > 0) {\n        return;\n    }\n\n    if (n == -1) {\n        if (err == NGX_EAGAIN) {\n            return;\n        }\n\n        ev->error = 1;\n\n    } else { /* n == 0 */\n        err = 0;\n    }\n\n    ev->eof = 1;\n    c->error = 1;\n\n    if (!u->cacheable && u->peer.connection) {\n        ngx_log_error(NGX_LOG_INFO, ev->log, err,\n                      \"client prematurely closed connection, \"\n                      \"so upstream connection is closed too\");\n        ngx_http_upstream_finalize_request(r, u,\n                                           NGX_HTTP_CLIENT_CLOSED_REQUEST);\n        return;\n    }\n\n    ngx_log_error(NGX_LOG_INFO, ev->log, err,\n                  \"client prematurely closed connection\");\n\n    if (u->peer.connection == NULL) {\n        ngx_http_upstream_finalize_request(r, u,\n                                           NGX_HTTP_CLIENT_CLOSED_REQUEST);\n    }\n}\n\n#if (T_NGX_MULTI_UPSTREAM)\n\n#include \"ngx_http_multi_upstream.c\"\n\n#endif /* T_NGX_MULTI_UPSTREAM */\n\n#if (!T_NGX_HTTP_DYNAMIC_RESOLVE)\nstatic\n#endif\nvoid\nngx_http_upstream_connect(ngx_http_request_t *r, ngx_http_upstream_t *u)\n{\n    ngx_int_t                  rc;\n    ngx_connection_t          *c;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    r->connection->log->action = \"connecting to upstream\";\n\n    if (u->state && u->state->response_time == (ngx_msec_t) -1) {\n        u->state->response_time = ngx_current_msec - u->start_time;\n    }\n\n    u->state = ngx_array_push(r->upstream_states);\n    if (u->state == NULL) {\n        ngx_http_upstream_finalize_request(r, u,\n                                           NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return;\n    }\n\n    ngx_memzero(u->state, sizeof(ngx_http_upstream_state_t));\n\n    u->start_time = ngx_current_msec;\n\n    u->state->response_time = (ngx_msec_t) -1;\n    u->state->connect_time = (ngx_msec_t) -1;\n    u->state->header_time = (ngx_msec_t) -1;\n\n    rc = ngx_event_connect_peer(&u->peer);\n#if (T_NGX_HTTP_DYNAMIC_RESOLVE)\n    if (rc == NGX_YIELD) {\n        return;\n    }\n#endif\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http upstream connect: %i\", rc);\n\n    if (rc == NGX_ERROR) {\n        ngx_http_upstream_finalize_request(r, u,\n                                           NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return;\n    }\n\n    u->state->peer = u->peer.name;\n\n    if (rc == NGX_BUSY) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, \"no live upstreams\");\n        ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_NOLIVE);\n        return;\n    }\n\n    if (rc == NGX_DECLINED) {\n        ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);\n        return;\n    }\n\n    /* rc == NGX_OK || rc == NGX_AGAIN || rc == NGX_DONE */\n\n    c = u->peer.connection;\n\n#if (T_NGX_MULTI_UPSTREAM)\n    if (u->multi) {\n        if (!(u->multi_mode & NGX_MULTI_UPS_SUPPORT_MULTI)) {\n            ngx_http_multi_upstream_finalize_request(c,\n                                                     NGX_HTTP_INTERNAL_SERVER_ERROR);\n            ngx_log_error(NGX_LOG_ERR, c->log, 0,\n                          \"multi: upstream configured multi, but handler no support\");\n            return;\n        }\n\n        if (rc == NGX_AGAIN) { //first real connect\n            c->read->handler = ngx_http_multi_upstream_connect_handler;\n            c->write->handler = ngx_http_multi_upstream_connect_handler;\n#if (T_HTTP_UPSTREAM_TIMEOUT_VAR)\n            ngx_add_timer(c->write, (r->connect_time == NGX_CONF_UNSET_MSEC)\n                          ? u->conf->connect_timeout : r->connect_time);\n#else\n            ngx_add_timer(c->write, u->conf->connect_timeout);\n#endif\n\n            ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                          \"multi: connect new to backend %p\", c);\n        } else if (rc == NGX_DONE) { //use exist connection\n            if (ngx_multi_connected(c)) {\n                ngx_log_error(NGX_LOG_INFO, c->log, 0, \"multi: connect reuse %p\", c);\n\n                ngx_http_multi_upstream_init_request(c, r);\n                ngx_http_multi_upstream_process(c, 1);\n            } else {\n                ngx_log_error(NGX_LOG_ERR, c->log, 0, \"multi: connect reuse unfinished %p\", c);\n            }\n        } else {\n            ngx_log_error(NGX_LOG_ERR, c->log, 0,\n                          \"multi: connect return %i error\", rc);\n        }\n\n        return;\n    } else if ((u->multi_mode & NGX_MULTI_UPS_NEED_MULTI) == NGX_MULTI_UPS_NEED_MULTI) {\n        ngx_http_upstream_finalize_request(r, u,\n                                           NGX_HTTP_INTERNAL_SERVER_ERROR);\n        ngx_log_error(NGX_LOG_ERR, c->log, 0,\n                      \"multi: need multi, but upstream not support, \"\n                      \"maybee need configuration 'multi' in upstream\");\n        return;\n    }\n#endif\n\n    c->requests++;\n\n    c->data = r;\n\n    c->write->handler = ngx_http_upstream_handler;\n    c->read->handler = ngx_http_upstream_handler;\n\n    u->write_event_handler = ngx_http_upstream_send_request_handler;\n    u->read_event_handler = ngx_http_upstream_process_header;\n\n    c->sendfile &= r->connection->sendfile;\n    u->output.sendfile = c->sendfile;\n\n    if (r->connection->tcp_nopush == NGX_TCP_NOPUSH_DISABLED) {\n        c->tcp_nopush = NGX_TCP_NOPUSH_DISABLED;\n    }\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            ngx_http_upstream_finalize_request(r, u,\n                                               NGX_HTTP_INTERNAL_SERVER_ERROR);\n            return;\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    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    u->writer.out = NULL;\n    u->writer.last = &u->writer.out;\n    u->writer.connection = c;\n    u->writer.limit = clcf->sendfile_max_chunk;\n\n    if (u->request_sent) {\n        if (ngx_http_upstream_reinit(r, u) != NGX_OK) {\n            ngx_http_upstream_finalize_request(r, u,\n                                               NGX_HTTP_INTERNAL_SERVER_ERROR);\n            return;\n        }\n    }\n\n    if (r->request_body\n        && r->request_body->buf\n        && r->request_body->temp_file\n        && r == r->main)\n    {\n        /*\n         * the r->request_body->buf can be reused for one request only,\n         * the subrequests should allocate their own temporary bufs\n         */\n\n        u->output.free = ngx_alloc_chain_link(r->pool);\n        if (u->output.free == NULL) {\n            ngx_http_upstream_finalize_request(r, u,\n                                               NGX_HTTP_INTERNAL_SERVER_ERROR);\n            return;\n        }\n\n        u->output.free->buf = r->request_body->buf;\n        u->output.free->next = NULL;\n        u->output.allocated = 1;\n\n        r->request_body->buf->pos = r->request_body->buf->start;\n        r->request_body->buf->last = r->request_body->buf->start;\n        r->request_body->buf->tag = u->output.tag;\n    }\n\n    u->request_sent = 0;\n    u->request_body_sent = 0;\n    u->request_body_blocked = 0;\n\n    if (rc == NGX_AGAIN) {\n#if (T_HTTP_UPSTREAM_TIMEOUT_VAR)\n        ngx_add_timer(c->write, (r->connect_time == NGX_CONF_UNSET_MSEC)\n                      ? u->conf->connect_timeout : r->connect_time);\n#else\n        ngx_add_timer(c->write, u->conf->connect_timeout);\n#endif\n        return;\n    }\n\n#if (NGX_HTTP_SSL)\n\n    if (u->ssl && c->ssl == NULL) {\n        ngx_http_upstream_ssl_init_connection(r, u, c);\n        return;\n    }\n\n#endif\n\n    ngx_http_upstream_send_request(r, u, 1);\n}\n\n\n#if (NGX_HTTP_SSL)\n\nstatic void\nngx_http_upstream_ssl_init_connection(ngx_http_request_t *r,\n    ngx_http_upstream_t *u, ngx_connection_t *c)\n{\n    ngx_int_t                  rc;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    if (ngx_http_upstream_test_connect(c) != NGX_OK) {\n        ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);\n        return;\n    }\n\n#if (T_NGX_SSL_NTLS)\n    if (u->conf->enable_ntls) {\n        ngx_str_t                    enable_ntls;\n\n        if (ngx_http_complex_value(r, u->conf->enable_ntls,\n                                   &enable_ntls) == NGX_OK &&\n            enable_ntls.len == 2 &&\n            ngx_strncmp(enable_ntls.data, \"on\", 2) == 0)\n        {\n            if (u->conf->tls_method != NTLS_method()) {\n                SSL_CTX_set_ssl_version(u->conf->ssl->ctx, NTLS_method());\n                SSL_CTX_set_cipher_list(u->conf->ssl->ctx,\n                                        (char *)u->conf->ssl_ciphers.data);\n                SSL_CTX_enable_ntls(u->conf->ssl->ctx);\n            }\n        } else {\n            if (SSL_CTX_get_ssl_method(u->conf->ssl->ctx) == NTLS_method()) {\n                SSL_CTX_set_ssl_version(u->conf->ssl->ctx, u->conf->tls_method);\n                SSL_CTX_set_cipher_list(u->conf->ssl->ctx,\n                                        (char *)u->conf->ssl_ciphers.data);\n                SSL_CTX_disable_ntls(u->conf->ssl->ctx);\n            }\n        }\n    }\n#endif\n\n    if (ngx_ssl_create_connection(u->conf->ssl, c,\n                                  NGX_SSL_BUFFER|NGX_SSL_CLIENT)\n        != NGX_OK)\n    {\n        ngx_http_upstream_finalize_request(r, u,\n                                           NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return;\n    }\n\n    if (u->conf->ssl_server_name || u->conf->ssl_verify) {\n        if (ngx_http_upstream_ssl_name(r, u, c) != NGX_OK) {\n            ngx_http_upstream_finalize_request(r, u,\n                                               NGX_HTTP_INTERNAL_SERVER_ERROR);\n            return;\n        }\n    }\n\n    if (u->conf->ssl_certificate\n        && u->conf->ssl_certificate->value.len\n        && (u->conf->ssl_certificate->lengths\n            || u->conf->ssl_certificate_key->lengths))\n    {\n        if (ngx_http_upstream_ssl_certificate(r, u, c) != NGX_OK) {\n            ngx_http_upstream_finalize_request(r, u,\n                                               NGX_HTTP_INTERNAL_SERVER_ERROR);\n            return;\n        }\n    }\n\n    if (u->conf->ssl_session_reuse) {\n        c->ssl->save_session = ngx_http_upstream_ssl_save_session;\n\n        if (u->peer.set_session(&u->peer, u->peer.data) != NGX_OK) {\n            ngx_http_upstream_finalize_request(r, u,\n                                               NGX_HTTP_INTERNAL_SERVER_ERROR);\n            return;\n        }\n\n        /* abbreviated SSL handshake may interact badly with Nagle */\n\n        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n        if (clcf->tcp_nodelay && ngx_tcp_nodelay(c) != NGX_OK) {\n            ngx_http_upstream_finalize_request(r, u,\n                                               NGX_HTTP_INTERNAL_SERVER_ERROR);\n            return;\n        }\n    }\n\n    r->connection->log->action = \"SSL handshaking to upstream\";\n\n    rc = ngx_ssl_handshake(c);\n\n    if (rc == NGX_AGAIN) {\n\n        if (!c->write->timer_set) {\n#if (T_HTTP_UPSTREAM_TIMEOUT_VAR)\n            ngx_add_timer(c->write, (r->connect_time == NGX_CONF_UNSET_MSEC)\n                          ? u->conf->connect_timeout : r->connect_time);\n#else\n            ngx_add_timer(c->write, u->conf->connect_timeout);\n#endif\n        }\n\n        c->ssl->handler = ngx_http_upstream_ssl_handshake_handler;\n        return;\n    }\n\n    ngx_http_upstream_ssl_handshake(r, u, c);\n}\n\n\nstatic void\nngx_http_upstream_ssl_handshake_handler(ngx_connection_t *c)\n{\n    ngx_http_request_t   *r;\n    ngx_http_upstream_t  *u;\n\n    r = c->data;\n\n    u = r->upstream;\n    c = r->connection;\n\n    ngx_http_set_log_request(c->log, r);\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http upstream ssl handshake: \\\"%V?%V\\\"\",\n                   &r->uri, &r->args);\n\n    ngx_http_upstream_ssl_handshake(r, u, u->peer.connection);\n\n    ngx_http_run_posted_requests(c);\n}\n\n\nstatic void\nngx_http_upstream_ssl_handshake(ngx_http_request_t *r, ngx_http_upstream_t *u,\n    ngx_connection_t *c)\n{\n    long  rc;\n\n    if (c->ssl->handshaked) {\n\n        if (u->conf->ssl_verify) {\n            rc = SSL_get_verify_result(c->ssl->connection);\n\n            if (rc != X509_V_OK) {\n                ngx_log_error(NGX_LOG_ERR, c->log, 0,\n                              \"upstream SSL certificate verify error: (%l:%s)\",\n                              rc, X509_verify_cert_error_string(rc));\n                goto failed;\n            }\n\n            if (ngx_ssl_check_host(c, &u->ssl_name) != NGX_OK) {\n                ngx_log_error(NGX_LOG_ERR, c->log, 0,\n                              \"upstream SSL certificate does not match \\\"%V\\\"\",\n                              &u->ssl_name);\n                goto failed;\n            }\n        }\n\n        if (!c->ssl->sendfile) {\n            c->sendfile = 0;\n            u->output.sendfile = 0;\n        }\n\n#if (T_NGX_MULTI_UPSTREAM)\n        if (u->multi) {\n            ngx_http_multi_upstream_connect_init(c);\n            return;\n        }\n#endif\n\n        c->write->handler = ngx_http_upstream_handler;\n        c->read->handler = ngx_http_upstream_handler;\n\n        ngx_http_upstream_send_request(r, u, 1);\n\n        return;\n    }\n\n    if (c->write->timedout) {\n        ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_TIMEOUT);\n        return;\n    }\n\nfailed:\n\n    ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);\n}\n\n\nstatic void\nngx_http_upstream_ssl_save_session(ngx_connection_t *c)\n{\n    ngx_http_request_t   *r;\n    ngx_http_upstream_t  *u;\n\n    if (c->idle) {\n        return;\n    }\n\n    r = c->data;\n\n    u = r->upstream;\n    c = r->connection;\n\n    ngx_http_set_log_request(c->log, r);\n\n    u->peer.save_session(&u->peer, u->peer.data);\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_ssl_name(ngx_http_request_t *r, ngx_http_upstream_t *u,\n    ngx_connection_t *c)\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\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\n    if (!u->conf->ssl_server_name) {\n        goto done;\n    }\n\n#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME\n\n    /* as per RFC 6066, literal IPv4 and IPv6 addresses are not permitted */\n\n    if (name.len == 0 || *name.data == '[') {\n        goto done;\n    }\n\n    if (ngx_inet_addr(name.data, name.len) != INADDR_NONE) {\n        goto done;\n    }\n\n    /*\n     * SSL_set_tlsext_host_name() needs a null-terminated string,\n     * hence we explicitly null-terminate name here\n     */\n\n    p = ngx_pnalloc(r->pool, name.len + 1);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    (void) ngx_cpystrn(p, name.data, name.len + 1);\n\n    name.data = p;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"upstream SSL server name: \\\"%s\\\"\", name.data);\n\n    if (SSL_set_tlsext_host_name(c->ssl->connection,\n                                 (char *) name.data)\n        == 0)\n    {\n        ngx_ssl_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"SSL_set_tlsext_host_name(\\\"%s\\\") failed\", name.data);\n        return NGX_ERROR;\n    }\n\n#endif\n\ndone:\n\n    u->ssl_name = name;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_ssl_certificate(ngx_http_request_t *r,\n    ngx_http_upstream_t *u, ngx_connection_t *c)\n{\n    ngx_str_t  cert, key;\n\n    if (ngx_http_complex_value(r, u->conf->ssl_certificate, &cert)\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http upstream ssl cert: \\\"%s\\\"\", cert.data);\n\n    if (*cert.data == '\\0') {\n        return NGX_OK;\n    }\n\n    if (ngx_http_complex_value(r, u->conf->ssl_certificate_key, &key)\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http upstream ssl key: \\\"%s\\\"\", key.data);\n\n    if (ngx_ssl_connection_certificate(c, r->pool, &cert, &key,\n                                       u->conf->ssl_passwords)\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n#endif\n\n\nstatic ngx_int_t\nngx_http_upstream_reinit(ngx_http_request_t *r, ngx_http_upstream_t *u)\n{\n    off_t         file_pos;\n    ngx_chain_t  *cl;\n\n    if (u->reinit_request(r) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    u->keepalive = 0;\n    u->upgrade = 0;\n    u->error = 0;\n\n    ngx_memzero(&u->headers_in, sizeof(ngx_http_upstream_headers_in_t));\n    u->headers_in.content_length_n = -1;\n    u->headers_in.last_modified_time = -1;\n\n    if (ngx_list_init(&u->headers_in.headers, r->pool, 8,\n                      sizeof(ngx_table_elt_t))\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    if (ngx_list_init(&u->headers_in.trailers, r->pool, 2,\n                      sizeof(ngx_table_elt_t))\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    /* reinit the request chain */\n\n    file_pos = 0;\n\n    for (cl = u->request_bufs; cl; cl = cl->next) {\n        cl->buf->pos = cl->buf->start;\n\n        /* there is at most one file */\n\n        if (cl->buf->in_file) {\n            cl->buf->file_pos = file_pos;\n            file_pos = cl->buf->file_last;\n        }\n    }\n\n    /* reinit the subrequest's ngx_output_chain() context */\n\n    if (r->request_body && r->request_body->temp_file\n        && r != r->main && u->output.buf)\n    {\n        u->output.free = ngx_alloc_chain_link(r->pool);\n        if (u->output.free == NULL) {\n            return NGX_ERROR;\n        }\n\n        u->output.free->buf = u->output.buf;\n        u->output.free->next = NULL;\n\n        u->output.buf->pos = u->output.buf->start;\n        u->output.buf->last = u->output.buf->start;\n    }\n\n    u->output.buf = NULL;\n    u->output.in = NULL;\n    u->output.busy = NULL;\n\n    /* reinit u->buffer */\n\n    u->buffer.pos = u->buffer.start;\n\n#if (NGX_HTTP_CACHE)\n\n    if (r->cache) {\n        u->buffer.pos += r->cache->header_start;\n    }\n\n#endif\n\n    u->buffer.last = u->buffer.pos;\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_upstream_send_request(ngx_http_request_t *r, ngx_http_upstream_t *u,\n    ngx_uint_t do_write)\n{\n    ngx_int_t          rc;\n    ngx_connection_t  *c;\n\n    c = u->peer.connection;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http upstream send request\");\n\n    if (u->state->connect_time == (ngx_msec_t) -1) {\n        u->state->connect_time = ngx_current_msec - u->start_time;\n    }\n\n    if (!u->request_sent && ngx_http_upstream_test_connect(c) != NGX_OK) {\n        ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);\n        return;\n    }\n\n    c->log->action = \"sending request to upstream\";\n\n    rc = ngx_http_upstream_send_request_body(r, u, do_write);\n\n    if (rc == NGX_ERROR) {\n        ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);\n        return;\n    }\n\n    if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {\n        ngx_http_upstream_finalize_request(r, u, rc);\n        return;\n    }\n\n    if (rc == NGX_AGAIN) {\n        if (!c->write->ready || u->request_body_blocked) {\n#if (T_HTTP_UPSTREAM_TIMEOUT_VAR)\n            ngx_add_timer(c->write, (r->send_time == NGX_CONF_UNSET_MSEC)\n                          ? u->conf->send_timeout : r->send_time);\n#else\n            ngx_add_timer(c->write, u->conf->send_timeout);\n#endif\n\n        } else if (c->write->timer_set) {\n            ngx_del_timer(c->write);\n        }\n\n        if (ngx_handle_write_event(c->write, u->conf->send_lowat) != NGX_OK) {\n            ngx_http_upstream_finalize_request(r, u,\n                                               NGX_HTTP_INTERNAL_SERVER_ERROR);\n            return;\n        }\n\n        if (c->write->ready && c->tcp_nopush == NGX_TCP_NOPUSH_SET) {\n            if (ngx_tcp_push(c->fd) == -1) {\n                ngx_log_error(NGX_LOG_CRIT, c->log, ngx_socket_errno,\n                              ngx_tcp_push_n \" failed\");\n                ngx_http_upstream_finalize_request(r, u,\n                                               NGX_HTTP_INTERNAL_SERVER_ERROR);\n                return;\n            }\n\n            c->tcp_nopush = NGX_TCP_NOPUSH_UNSET;\n        }\n\n        if (c->read->ready) {\n            ngx_post_event(c->read, &ngx_posted_events);\n        }\n\n#if (T_NGX_MULTI_UPSTREAM)\n        if (u->multi && r->connection != u->peer.connection && !r->waiting) {\n            ngx_multi_connection_t *multi_c = ngx_get_multi_connection(c);\n            ngx_queue_insert_tail(&multi_c->waiting_list, &r->waiting_queue);\n            r->waiting = 1;\n        }\n#endif\n\n        return;\n    }\n\n    /* rc == NGX_OK */\n\n    if (c->write->timer_set) {\n        ngx_del_timer(c->write);\n    }\n\n    if (c->tcp_nopush == NGX_TCP_NOPUSH_SET) {\n        if (ngx_tcp_push(c->fd) == -1) {\n            ngx_log_error(NGX_LOG_CRIT, c->log, ngx_socket_errno,\n                          ngx_tcp_push_n \" failed\");\n            ngx_http_upstream_finalize_request(r, u,\n                                               NGX_HTTP_INTERNAL_SERVER_ERROR);\n            return;\n        }\n\n        c->tcp_nopush = NGX_TCP_NOPUSH_UNSET;\n    }\n\n    if (!u->conf->preserve_output) {\n        u->write_event_handler = ngx_http_upstream_dummy_handler;\n    }\n\n    if (ngx_handle_write_event(c->write, 0) != NGX_OK) {\n        ngx_http_upstream_finalize_request(r, u,\n                                           NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return;\n    }\n\n    if (!u->request_body_sent) {\n        u->request_body_sent = 1;\n\n        if (u->header_sent) {\n            return;\n        }\n\n#if (T_HTTP_UPSTREAM_TIMEOUT_VAR)\n        ngx_add_timer(c->read, (r->read_time == NGX_CONF_UNSET_MSEC)\n                      ? u->conf->read_timeout : r->read_time);\n#else\n        ngx_add_timer(c->read, u->conf->read_timeout);\n#endif\n\n        if (c->read->ready) {\n#if (T_NGX_MULTI_UPSTREAM)\n            if (u->multi) {\n                ngx_http_multi_upstream_read_handler(c);\n                return;\n            }\n#endif\n            ngx_http_upstream_process_header(r, u);\n            return;\n        }\n    }\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_send_request_body(ngx_http_request_t *r,\n    ngx_http_upstream_t *u, ngx_uint_t do_write)\n{\n    ngx_int_t                  rc;\n    ngx_chain_t               *out, *cl, *ln;\n    ngx_connection_t          *c;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http upstream send request body\");\n\n    if (!r->request_body_no_buffering) {\n\n        /* buffered request body */\n\n        if (!u->request_sent) {\n            u->request_sent = 1;\n            out = u->request_bufs;\n\n        } else {\n            out = NULL;\n        }\n\n        rc = ngx_output_chain(&u->output, out);\n\n        if (rc == NGX_AGAIN) {\n            u->request_body_blocked = 1;\n\n        } else {\n#if T_NGX_MULTI_UPSTREAM\n            if (u->multi && rc == NGX_OK) {\n                ngx_multi_clean_leak(u->peer.connection);\n            }\n#endif\n            u->request_body_blocked = 0;\n        }\n\n        return rc;\n    }\n\n    if (!u->request_sent) {\n        u->request_sent = 1;\n        out = u->request_bufs;\n\n        if (r->request_body->bufs) {\n            for (cl = out; cl->next; cl = cl->next) { /* void */ }\n            cl->next = r->request_body->bufs;\n            r->request_body->bufs = NULL;\n        }\n\n        c = u->peer.connection;\n        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n        if (clcf->tcp_nodelay && ngx_tcp_nodelay(c) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        r->read_event_handler = ngx_http_upstream_read_request_handler;\n\n    } else {\n        out = NULL;\n    }\n\n    for ( ;; ) {\n\n        if (do_write) {\n            rc = ngx_output_chain(&u->output, out);\n\n            if (rc == NGX_ERROR) {\n                return NGX_ERROR;\n            }\n\n            while (out) {\n                ln = out;\n                out = out->next;\n                ngx_free_chain(r->pool, ln);\n            }\n\n            if (rc == NGX_AGAIN) {\n                u->request_body_blocked = 1;\n\n            } else {\n                u->request_body_blocked = 0;\n            }\n\n            if (rc == NGX_OK && !r->reading_body) {\n                break;\n            }\n        }\n\n        if (r->reading_body) {\n            /* read client request body */\n\n            rc = ngx_http_read_unbuffered_request_body(r);\n\n            if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {\n                return rc;\n            }\n\n            out = r->request_body->bufs;\n            r->request_body->bufs = NULL;\n        }\n\n        /* stop if there is nothing to send */\n\n        if (out == NULL) {\n            rc = NGX_AGAIN;\n            break;\n        }\n\n        do_write = 1;\n    }\n\n    if (!r->reading_body) {\n        if (!u->store && !r->post_action && !u->conf->ignore_client_abort) {\n            r->read_event_handler =\n                                  ngx_http_upstream_rd_check_broken_connection;\n        }\n    }\n\n    return rc;\n}\n\n\nstatic void\nngx_http_upstream_send_request_handler(ngx_http_request_t *r,\n    ngx_http_upstream_t *u)\n{\n    ngx_connection_t  *c;\n\n    c = u->peer.connection;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http upstream send request handler\");\n\n    if (c->write->timedout) {\n        ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_TIMEOUT);\n        return;\n    }\n\n#if (NGX_HTTP_SSL)\n\n    if (u->ssl && c->ssl == NULL) {\n        ngx_http_upstream_ssl_init_connection(r, u, c);\n        return;\n    }\n\n#endif\n\n    if (u->header_sent && !u->conf->preserve_output) {\n        u->write_event_handler = ngx_http_upstream_dummy_handler;\n\n        (void) ngx_handle_write_event(c->write, 0);\n\n        return;\n    }\n\n    ngx_http_upstream_send_request(r, u, 1);\n}\n\n\nstatic void\nngx_http_upstream_read_request_handler(ngx_http_request_t *r)\n{\n    ngx_connection_t     *c;\n    ngx_http_upstream_t  *u;\n\n    c = r->connection;\n    u = r->upstream;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http upstream read request handler\");\n\n    if (c->read->timedout) {\n        c->timedout = 1;\n        ngx_http_upstream_finalize_request(r, u, NGX_HTTP_REQUEST_TIME_OUT);\n        return;\n    }\n\n    ngx_http_upstream_send_request(r, u, 0);\n}\n\n\nstatic void\nngx_http_upstream_process_header(ngx_http_request_t *r, ngx_http_upstream_t *u)\n{\n    ssize_t            n;\n    ngx_int_t          rc;\n    ngx_connection_t  *c;\n\n    c = u->peer.connection;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http upstream process header\");\n\n    c->log->action = \"reading response header from upstream\";\n\n    if (c->read->timedout) {\n        ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_TIMEOUT);\n        return;\n    }\n\n    if (!u->request_sent && ngx_http_upstream_test_connect(c) != NGX_OK) {\n        ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);\n        return;\n    }\n\n    if (u->buffer.start == NULL) {\n        u->buffer.start = ngx_palloc(r->pool, u->conf->buffer_size);\n        if (u->buffer.start == NULL) {\n            ngx_http_upstream_finalize_request(r, u,\n                                               NGX_HTTP_INTERNAL_SERVER_ERROR);\n            return;\n        }\n\n        u->buffer.pos = u->buffer.start;\n        u->buffer.last = u->buffer.start;\n        u->buffer.end = u->buffer.start + u->conf->buffer_size;\n        u->buffer.temporary = 1;\n\n        u->buffer.tag = u->output.tag;\n\n        if (ngx_list_init(&u->headers_in.headers, r->pool, 8,\n                          sizeof(ngx_table_elt_t))\n            != NGX_OK)\n        {\n            ngx_http_upstream_finalize_request(r, u,\n                                               NGX_HTTP_INTERNAL_SERVER_ERROR);\n            return;\n        }\n\n        if (ngx_list_init(&u->headers_in.trailers, r->pool, 2,\n                          sizeof(ngx_table_elt_t))\n            != NGX_OK)\n        {\n            ngx_http_upstream_finalize_request(r, u,\n                                               NGX_HTTP_INTERNAL_SERVER_ERROR);\n            return;\n        }\n\n#if (NGX_HTTP_CACHE)\n\n        if (r->cache) {\n            u->buffer.pos += r->cache->header_start;\n            u->buffer.last = u->buffer.pos;\n        }\n#endif\n    }\n\n    for ( ;; ) {\n\n        n = c->recv(c, u->buffer.last, u->buffer.end - u->buffer.last);\n\n        if (n == NGX_AGAIN) {\n#if 0\n            ngx_add_timer(rev, u->read_timeout);\n#endif\n\n            if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n                ngx_http_upstream_finalize_request(r, u,\n                                               NGX_HTTP_INTERNAL_SERVER_ERROR);\n                return;\n            }\n\n            return;\n        }\n\n        if (n == 0) {\n            ngx_log_error(NGX_LOG_ERR, c->log, 0,\n                          \"upstream prematurely closed connection\");\n        }\n\n        if (n == NGX_ERROR || n == 0) {\n            ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR);\n            return;\n        }\n\n        u->state->bytes_received += n;\n\n        u->buffer.last += n;\n\n#if 0\n        u->valid_header_in = 0;\n\n        u->peer.cached = 0;\n#endif\n\n        rc = u->process_header(r);\n\n        if (rc == NGX_AGAIN) {\n\n            if (u->buffer.last == u->buffer.end) {\n                ngx_log_error(NGX_LOG_ERR, c->log, 0,\n                              \"upstream sent too big header\");\n\n                ngx_http_upstream_next(r, u,\n                                       NGX_HTTP_UPSTREAM_FT_INVALID_HEADER);\n                return;\n            }\n\n            continue;\n        }\n\n        break;\n    }\n\n    if (rc == NGX_HTTP_UPSTREAM_INVALID_HEADER) {\n        ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_INVALID_HEADER);\n        return;\n    }\n\n    if (rc == NGX_ERROR) {\n        ngx_http_upstream_finalize_request(r, u,\n                                           NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return;\n    }\n\n    /* rc == NGX_OK */\n\n    u->state->header_time = ngx_current_msec - u->start_time;\n\n    if (u->headers_in.status_n >= NGX_HTTP_SPECIAL_RESPONSE) {\n\n        if (ngx_http_upstream_test_next(r, u) == NGX_OK) {\n            return;\n        }\n\n        if (ngx_http_upstream_intercept_errors(r, u) == NGX_OK) {\n            return;\n        }\n    }\n\n    if (ngx_http_upstream_process_headers(r, u) != NGX_OK) {\n        return;\n    }\n\n    ngx_http_upstream_send_response(r, u);\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_test_next(ngx_http_request_t *r, ngx_http_upstream_t *u)\n{\n    ngx_msec_t                 timeout;\n    ngx_uint_t                 status, mask;\n    ngx_http_upstream_next_t  *un;\n\n    status = u->headers_in.status_n;\n\n    for (un = ngx_http_upstream_next_errors; un->status; un++) {\n\n        if (status != un->status) {\n            continue;\n        }\n\n        timeout = u->conf->next_upstream_timeout;\n\n        if (u->request_sent\n            && (r->method & (NGX_HTTP_POST|NGX_HTTP_LOCK|NGX_HTTP_PATCH)))\n        {\n            mask = un->mask | NGX_HTTP_UPSTREAM_FT_NON_IDEMPOTENT;\n\n        } else {\n            mask = un->mask;\n        }\n\n        if (u->peer.tries > 1\n            && ((u->conf->next_upstream & mask) == mask)\n            && !(u->request_sent && r->request_body_no_buffering)\n            && !(timeout && ngx_current_msec - u->peer.start_time >= timeout))\n        {\n            ngx_http_upstream_next(r, u, un->mask);\n            return NGX_OK;\n        }\n\n#if (NGX_HTTP_CACHE)\n\n        if (u->cache_status == NGX_HTTP_CACHE_EXPIRED\n            && (u->conf->cache_use_stale & un->mask))\n        {\n            ngx_int_t  rc;\n\n            rc = u->reinit_request(r);\n\n            if (rc != NGX_OK) {\n                ngx_http_upstream_finalize_request(r, u, rc);\n                return NGX_OK;\n            }\n\n            u->cache_status = NGX_HTTP_CACHE_STALE;\n            rc = ngx_http_upstream_cache_send(r, u);\n\n            if (rc == NGX_DONE) {\n                return NGX_OK;\n            }\n\n            if (rc == NGX_HTTP_UPSTREAM_INVALID_HEADER) {\n                rc = NGX_HTTP_INTERNAL_SERVER_ERROR;\n            }\n\n            ngx_http_upstream_finalize_request(r, u, rc);\n            return NGX_OK;\n        }\n\n#endif\n\n        break;\n    }\n\n#if (NGX_HTTP_CACHE)\n\n    if (status == NGX_HTTP_NOT_MODIFIED\n        && u->cache_status == NGX_HTTP_CACHE_EXPIRED\n        && u->conf->cache_revalidate)\n    {\n        time_t     now, valid, updating, error;\n        ngx_int_t  rc;\n\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"http upstream not modified\");\n\n        now = ngx_time();\n\n        valid = r->cache->valid_sec;\n        updating = r->cache->updating_sec;\n        error = r->cache->error_sec;\n\n        rc = u->reinit_request(r);\n\n        if (rc != NGX_OK) {\n            ngx_http_upstream_finalize_request(r, u, rc);\n            return NGX_OK;\n        }\n\n        u->cache_status = NGX_HTTP_CACHE_REVALIDATED;\n        rc = ngx_http_upstream_cache_send(r, u);\n\n        if (rc == NGX_DONE) {\n            return NGX_OK;\n        }\n\n        if (rc == NGX_HTTP_UPSTREAM_INVALID_HEADER) {\n            rc = NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n\n        if (valid == 0) {\n            valid = r->cache->valid_sec;\n            updating = r->cache->updating_sec;\n            error = r->cache->error_sec;\n        }\n\n        if (valid == 0) {\n            valid = ngx_http_file_cache_valid(u->conf->cache_valid,\n                                              u->headers_in.status_n);\n            if (valid) {\n                valid = now + valid;\n            }\n        }\n\n        if (valid) {\n            r->cache->valid_sec = valid;\n            r->cache->updating_sec = updating;\n            r->cache->error_sec = error;\n\n            r->cache->date = now;\n\n            ngx_http_file_cache_update_header(r);\n        }\n\n        ngx_http_upstream_finalize_request(r, u, rc);\n        return NGX_OK;\n    }\n\n#endif\n\n    return NGX_DECLINED;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_intercept_errors(ngx_http_request_t *r,\n    ngx_http_upstream_t *u)\n{\n    ngx_int_t                  status;\n    ngx_uint_t                 i;\n    ngx_table_elt_t           *h, *ho, **ph;\n    ngx_http_err_page_t       *err_page;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    status = u->headers_in.status_n;\n\n    if (status == NGX_HTTP_NOT_FOUND && u->conf->intercept_404) {\n        ngx_http_upstream_finalize_request(r, u, NGX_HTTP_NOT_FOUND);\n        return NGX_OK;\n    }\n\n    if (!u->conf->intercept_errors) {\n        return NGX_DECLINED;\n    }\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    if (clcf->error_pages == NULL) {\n        return NGX_DECLINED;\n    }\n\n    err_page = clcf->error_pages->elts;\n    for (i = 0; i < clcf->error_pages->nelts; i++) {\n\n        if (err_page[i].status == status) {\n\n            if (status == NGX_HTTP_UNAUTHORIZED\n                && u->headers_in.www_authenticate)\n            {\n                h = u->headers_in.www_authenticate;\n                ph = &r->headers_out.www_authenticate;\n\n                while (h) {\n                    ho = ngx_list_push(&r->headers_out.headers);\n\n                    if (ho == NULL) {\n                        ngx_http_upstream_finalize_request(r, u,\n                                               NGX_HTTP_INTERNAL_SERVER_ERROR);\n                        return NGX_OK;\n                    }\n\n                    *ho = *h;\n                    ho->next = NULL;\n\n                    *ph = ho;\n                    ph = &ho->next;\n\n                    h = h->next;\n                }\n            }\n\n#if (NGX_HTTP_CACHE)\n\n            if (r->cache) {\n\n                if (u->headers_in.no_cache || u->headers_in.expired) {\n                    u->cacheable = 0;\n                }\n\n                if (u->cacheable) {\n                    time_t  valid;\n\n                    valid = r->cache->valid_sec;\n\n                    if (valid == 0) {\n                        valid = ngx_http_file_cache_valid(u->conf->cache_valid,\n                                                          status);\n                        if (valid) {\n                            r->cache->valid_sec = ngx_time() + valid;\n                        }\n                    }\n\n                    if (valid) {\n                        r->cache->error = status;\n                    }\n                }\n\n                ngx_http_file_cache_free(r->cache, u->pipe->temp_file);\n            }\n#endif\n            ngx_http_upstream_finalize_request(r, u, status);\n\n            return NGX_OK;\n        }\n    }\n\n    return NGX_DECLINED;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_test_connect(ngx_connection_t *c)\n{\n    int        err;\n    socklen_t  len;\n\n#if (NGX_HAVE_KQUEUE)\n\n    if (ngx_event_flags & NGX_USE_KQUEUE_EVENT)  {\n        if (c->write->pending_eof || c->read->pending_eof) {\n            if (c->write->pending_eof) {\n                err = c->write->kq_errno;\n\n            } else {\n                err = c->read->kq_errno;\n            }\n\n            c->log->action = \"connecting to upstream\";\n            (void) ngx_connection_error(c, err,\n                                    \"kevent() reported that connect() failed\");\n            return NGX_ERROR;\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_socket_errno;\n        }\n\n        if (err) {\n            c->log->action = \"connecting to upstream\";\n            (void) ngx_connection_error(c, err, \"connect() failed\");\n            return NGX_ERROR;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_process_headers(ngx_http_request_t *r, ngx_http_upstream_t *u)\n{\n    ngx_str_t                       uri, args;\n    ngx_uint_t                      i, flags;\n    ngx_list_part_t                *part;\n    ngx_table_elt_t                *h;\n    ngx_http_upstream_header_t     *hh;\n    ngx_http_upstream_main_conf_t  *umcf;\n\n    umcf = ngx_http_get_module_main_conf(r, ngx_http_upstream_module);\n\n    if (u->headers_in.no_cache || u->headers_in.expired) {\n        u->cacheable = 0;\n    }\n\n    if (u->headers_in.x_accel_redirect\n        && !(u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_XA_REDIRECT))\n    {\n        ngx_http_upstream_finalize_request(r, u, NGX_DECLINED);\n\n        part = &u->headers_in.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            hh = ngx_hash_find(&umcf->headers_in_hash, h[i].hash,\n                               h[i].lowcase_key, h[i].key.len);\n\n            if (hh && hh->redirect) {\n                if (hh->copy_handler(r, &h[i], hh->conf) != NGX_OK) {\n                    ngx_http_finalize_request(r,\n                                              NGX_HTTP_INTERNAL_SERVER_ERROR);\n                    return NGX_DONE;\n                }\n            }\n        }\n\n        uri = u->headers_in.x_accel_redirect->value;\n\n        if (uri.data[0] == '@') {\n            ngx_http_named_location(r, &uri);\n\n        } else {\n            ngx_str_null(&args);\n            flags = NGX_HTTP_LOG_UNSAFE;\n\n            if (ngx_http_parse_unsafe_uri(r, &uri, &args, &flags) != NGX_OK) {\n                ngx_http_finalize_request(r, NGX_HTTP_NOT_FOUND);\n                return NGX_DONE;\n            }\n\n            if (r->method != NGX_HTTP_HEAD) {\n                r->method = NGX_HTTP_GET;\n                r->method_name = ngx_http_core_get_method;\n            }\n\n            ngx_http_internal_redirect(r, &uri, &args);\n        }\n\n        ngx_http_finalize_request(r, NGX_DONE);\n        return NGX_DONE;\n    }\n\n    part = &u->headers_in.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 (ngx_hash_find(&u->conf->hide_headers_hash, h[i].hash,\n                          h[i].lowcase_key, h[i].key.len))\n        {\n            continue;\n        }\n\n        hh = ngx_hash_find(&umcf->headers_in_hash, h[i].hash,\n                           h[i].lowcase_key, h[i].key.len);\n\n        if (hh) {\n            if (hh->copy_handler(r, &h[i], hh->conf) != NGX_OK) {\n                ngx_http_upstream_finalize_request(r, u,\n                                               NGX_HTTP_INTERNAL_SERVER_ERROR);\n                return NGX_DONE;\n            }\n\n            continue;\n        }\n\n        if (ngx_http_upstream_copy_header_line(r, &h[i], 0) != NGX_OK) {\n            ngx_http_upstream_finalize_request(r, u,\n                                               NGX_HTTP_INTERNAL_SERVER_ERROR);\n            return NGX_DONE;\n        }\n    }\n\n    if (r->headers_out.server && r->headers_out.server->value.data == NULL) {\n        r->headers_out.server->hash = 0;\n    }\n\n    if (r->headers_out.date && r->headers_out.date->value.data == NULL) {\n        r->headers_out.date->hash = 0;\n    }\n\n    r->headers_out.status = u->headers_in.status_n;\n    r->headers_out.status_line = u->headers_in.status_line;\n\n    r->headers_out.content_length_n = u->headers_in.content_length_n;\n\n    r->disable_not_modified = !u->cacheable;\n\n    if (u->conf->force_ranges) {\n        r->allow_ranges = 1;\n        r->single_range = 1;\n\n#if (NGX_HTTP_CACHE)\n        if (r->cached) {\n            r->single_range = 0;\n        }\n#endif\n    }\n\n    u->length = -1;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_process_trailers(ngx_http_request_t *r,\n    ngx_http_upstream_t *u)\n{\n    ngx_uint_t        i;\n    ngx_list_part_t  *part;\n    ngx_table_elt_t  *h, *ho;\n\n    if (!u->conf->pass_trailers) {\n        return NGX_OK;\n    }\n\n    part = &u->headers_in.trailers.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 (ngx_hash_find(&u->conf->hide_headers_hash, h[i].hash,\n                          h[i].lowcase_key, h[i].key.len))\n        {\n            continue;\n        }\n\n        ho = ngx_list_push(&r->headers_out.trailers);\n        if (ho == NULL) {\n            return NGX_ERROR;\n        }\n\n        *ho = h[i];\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_upstream_send_response(ngx_http_request_t *r, ngx_http_upstream_t *u)\n{\n    ssize_t                    n;\n    ngx_int_t                  rc;\n    ngx_event_pipe_t          *p;\n    ngx_connection_t          *c;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    rc = ngx_http_send_header(r);\n\n    if (rc == NGX_ERROR || rc > NGX_OK || r->post_action) {\n        ngx_http_upstream_finalize_request(r, u, rc);\n        return;\n    }\n\n    u->header_sent = 1;\n\n    if (u->upgrade) {\n\n#if (NGX_HTTP_CACHE)\n\n        if (r->cache) {\n            ngx_http_file_cache_free(r->cache, u->pipe->temp_file);\n        }\n\n#endif\n\n        ngx_http_upstream_upgrade(r, u);\n        return;\n    }\n\n    c = r->connection;\n\n    if (r->header_only) {\n\n        if (!u->buffering) {\n            ngx_http_upstream_finalize_request(r, u, rc);\n            return;\n        }\n\n        if (!u->cacheable && !u->store) {\n            ngx_http_upstream_finalize_request(r, u, rc);\n            return;\n        }\n\n        u->pipe->downstream_error = 1;\n    }\n\n    if (r->request_body && r->request_body->temp_file\n        && r == r->main && !r->preserve_body\n        && !u->conf->preserve_output)\n    {\n        ngx_pool_run_cleanup_file(r->pool, r->request_body->temp_file->file.fd);\n        r->request_body->temp_file->file.fd = NGX_INVALID_FILE;\n    }\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    if (!u->buffering) {\n\n#if (NGX_HTTP_CACHE)\n\n        if (r->cache) {\n            ngx_http_file_cache_free(r->cache, u->pipe->temp_file);\n        }\n\n#endif\n\n        if (u->input_filter == NULL) {\n            u->input_filter_init = ngx_http_upstream_non_buffered_filter_init;\n            u->input_filter = ngx_http_upstream_non_buffered_filter;\n            u->input_filter_ctx = r;\n        }\n\n        u->read_event_handler = ngx_http_upstream_process_non_buffered_upstream;\n        r->write_event_handler =\n                             ngx_http_upstream_process_non_buffered_downstream;\n\n        r->limit_rate = 0;\n        r->limit_rate_set = 1;\n\n        if (u->input_filter_init(u->input_filter_ctx) == NGX_ERROR) {\n            ngx_http_upstream_finalize_request(r, u, NGX_ERROR);\n            return;\n        }\n\n        if (clcf->tcp_nodelay && ngx_tcp_nodelay(c) != NGX_OK) {\n            ngx_http_upstream_finalize_request(r, u, NGX_ERROR);\n            return;\n        }\n\n        n = u->buffer.last - u->buffer.pos;\n\n        if (n) {\n            u->buffer.last = u->buffer.pos;\n\n            u->state->response_length += n;\n\n            if (u->input_filter(u->input_filter_ctx, n) == NGX_ERROR) {\n                ngx_http_upstream_finalize_request(r, u, NGX_ERROR);\n                return;\n            }\n\n            ngx_http_upstream_process_non_buffered_downstream(r);\n\n        } else {\n            u->buffer.pos = u->buffer.start;\n            u->buffer.last = u->buffer.start;\n\n            if (ngx_http_send_special(r, NGX_HTTP_FLUSH) == NGX_ERROR) {\n                ngx_http_upstream_finalize_request(r, u, NGX_ERROR);\n                return;\n            }\n\n            ngx_http_upstream_process_non_buffered_upstream(r, u);\n        }\n\n        return;\n    }\n\n    /* TODO: preallocate event_pipe bufs, look \"Content-Length\" */\n\n#if (NGX_HTTP_CACHE)\n\n    if (r->cache && r->cache->file.fd != NGX_INVALID_FILE) {\n        ngx_pool_run_cleanup_file(r->pool, r->cache->file.fd);\n        r->cache->file.fd = NGX_INVALID_FILE;\n    }\n\n    switch (ngx_http_test_predicates(r, u->conf->no_cache)) {\n\n    case NGX_ERROR:\n        ngx_http_upstream_finalize_request(r, u, NGX_ERROR);\n        return;\n\n    case NGX_DECLINED:\n        u->cacheable = 0;\n        break;\n\n    default: /* NGX_OK */\n\n        if (u->cache_status == NGX_HTTP_CACHE_BYPASS) {\n\n            /* create cache if previously bypassed */\n\n            if (ngx_http_file_cache_create(r) != NGX_OK) {\n                ngx_http_upstream_finalize_request(r, u, NGX_ERROR);\n                return;\n            }\n        }\n\n        break;\n    }\n\n    if (u->cacheable) {\n        time_t  now, valid;\n\n        now = ngx_time();\n\n        valid = r->cache->valid_sec;\n\n        if (valid == 0) {\n            valid = ngx_http_file_cache_valid(u->conf->cache_valid,\n                                              u->headers_in.status_n);\n            if (valid) {\n                r->cache->valid_sec = now + valid;\n            }\n        }\n\n        if (valid) {\n            r->cache->date = now;\n            r->cache->body_start = (u_short) (u->buffer.pos - u->buffer.start);\n\n            if (u->headers_in.status_n == NGX_HTTP_OK\n                || u->headers_in.status_n == NGX_HTTP_PARTIAL_CONTENT)\n            {\n                r->cache->last_modified = u->headers_in.last_modified_time;\n\n                if (u->headers_in.etag) {\n                    r->cache->etag = u->headers_in.etag->value;\n\n                } else {\n                    ngx_str_null(&r->cache->etag);\n                }\n\n            } else {\n                r->cache->last_modified = -1;\n                ngx_str_null(&r->cache->etag);\n            }\n\n            if (ngx_http_file_cache_set_header(r, u->buffer.start) != NGX_OK) {\n                ngx_http_upstream_finalize_request(r, u, NGX_ERROR);\n                return;\n            }\n\n        } else {\n            u->cacheable = 0;\n        }\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http cacheable: %d\", u->cacheable);\n\n    if (u->cacheable == 0 && r->cache) {\n        ngx_http_file_cache_free(r->cache, u->pipe->temp_file);\n    }\n\n    if (r->header_only && !u->cacheable && !u->store) {\n        ngx_http_upstream_finalize_request(r, u, 0);\n        return;\n    }\n\n#endif\n\n    p = u->pipe;\n\n    p->output_filter = ngx_http_upstream_output_filter;\n    p->output_ctx = r;\n    p->tag = u->output.tag;\n    p->bufs = u->conf->bufs;\n    p->busy_size = u->conf->busy_buffers_size;\n    p->upstream = u->peer.connection;\n    p->downstream = c;\n    p->pool = r->pool;\n    p->log = c->log;\n    p->limit_rate = u->conf->limit_rate;\n    p->start_sec = ngx_time();\n\n    p->cacheable = u->cacheable || u->store;\n\n    p->temp_file = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t));\n    if (p->temp_file == NULL) {\n        ngx_http_upstream_finalize_request(r, u, NGX_ERROR);\n        return;\n    }\n\n    p->temp_file->file.fd = NGX_INVALID_FILE;\n    p->temp_file->file.log = c->log;\n    p->temp_file->path = u->conf->temp_path;\n    p->temp_file->pool = r->pool;\n\n    if (p->cacheable) {\n        p->temp_file->persistent = 1;\n\n#if (NGX_HTTP_CACHE)\n        if (r->cache && !r->cache->file_cache->use_temp_path) {\n            p->temp_file->path = r->cache->file_cache->path;\n            p->temp_file->file.name = r->cache->file.name;\n        }\n#endif\n\n    } else {\n        p->temp_file->log_level = NGX_LOG_WARN;\n        p->temp_file->warn = \"an upstream response is buffered \"\n                             \"to a temporary file\";\n    }\n\n    p->max_temp_file_size = u->conf->max_temp_file_size;\n    p->temp_file_write_size = u->conf->temp_file_write_size;\n\n#if (NGX_THREADS)\n    if (clcf->aio == NGX_HTTP_AIO_THREADS && clcf->aio_write) {\n        p->thread_handler = ngx_http_upstream_thread_handler;\n        p->thread_ctx = r;\n    }\n#endif\n\n    p->preread_bufs = ngx_alloc_chain_link(r->pool);\n    if (p->preread_bufs == NULL) {\n        ngx_http_upstream_finalize_request(r, u, NGX_ERROR);\n        return;\n    }\n\n    p->preread_bufs->buf = &u->buffer;\n    p->preread_bufs->next = NULL;\n    u->buffer.recycled = 1;\n\n    p->preread_size = u->buffer.last - u->buffer.pos;\n\n    if (u->cacheable) {\n\n        p->buf_to_file = ngx_calloc_buf(r->pool);\n        if (p->buf_to_file == NULL) {\n            ngx_http_upstream_finalize_request(r, u, NGX_ERROR);\n            return;\n        }\n\n        p->buf_to_file->start = u->buffer.start;\n        p->buf_to_file->pos = u->buffer.start;\n        p->buf_to_file->last = u->buffer.pos;\n        p->buf_to_file->temporary = 1;\n    }\n\n    if (ngx_event_flags & NGX_USE_IOCP_EVENT) {\n        /* the posted aio operation may corrupt a shadow buffer */\n        p->single_buf = 1;\n    }\n\n    /* TODO: p->free_bufs = 0 if use ngx_create_chain_of_bufs() */\n    p->free_bufs = 1;\n\n    /*\n     * event_pipe would do u->buffer.last += p->preread_size\n     * as though these bytes were read\n     */\n    u->buffer.last = u->buffer.pos;\n\n    if (u->conf->cyclic_temp_file) {\n\n        /*\n         * we need to disable the use of sendfile() if we use cyclic temp file\n         * because the writing a new data may interfere with sendfile()\n         * that uses the same kernel file pages (at least on FreeBSD)\n         */\n\n        p->cyclic_temp_file = 1;\n        c->sendfile = 0;\n\n    } else {\n        p->cyclic_temp_file = 0;\n    }\n\n#if (T_HTTP_UPSTREAM_TIMEOUT_VAR)\n    p->read_timeout = (r->read_time == NGX_CONF_UNSET_MSEC)\n                       ? u->conf->read_timeout : r->read_time;\n#else\n    p->read_timeout = u->conf->read_timeout;\n#endif\n\n    p->send_timeout = clcf->send_timeout;\n    p->send_lowat = clcf->send_lowat;\n\n    p->length = -1;\n\n    if (u->input_filter_init\n        && u->input_filter_init(p->input_ctx) != NGX_OK)\n    {\n        ngx_http_upstream_finalize_request(r, u, NGX_ERROR);\n        return;\n    }\n\n    u->read_event_handler = ngx_http_upstream_process_upstream;\n    r->write_event_handler = ngx_http_upstream_process_downstream;\n\n    ngx_http_upstream_process_upstream(r, u);\n}\n\n\nstatic void\nngx_http_upstream_upgrade(ngx_http_request_t *r, ngx_http_upstream_t *u)\n{\n    ngx_connection_t          *c;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    c = r->connection;\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    /* TODO: prevent upgrade if not requested or not possible */\n\n    if (r != r->main) {\n        ngx_log_error(NGX_LOG_ERR, c->log, 0,\n                      \"connection upgrade in subrequest\");\n        ngx_http_upstream_finalize_request(r, u, NGX_ERROR);\n        return;\n    }\n\n    r->keepalive = 0;\n    c->log->action = \"proxying upgraded connection\";\n\n    u->read_event_handler = ngx_http_upstream_upgraded_read_upstream;\n    u->write_event_handler = ngx_http_upstream_upgraded_write_upstream;\n    r->read_event_handler = ngx_http_upstream_upgraded_read_downstream;\n    r->write_event_handler = ngx_http_upstream_upgraded_write_downstream;\n\n    if (clcf->tcp_nodelay) {\n\n        if (ngx_tcp_nodelay(c) != NGX_OK) {\n            ngx_http_upstream_finalize_request(r, u, NGX_ERROR);\n            return;\n        }\n\n        if (ngx_tcp_nodelay(u->peer.connection) != NGX_OK) {\n            ngx_http_upstream_finalize_request(r, u, NGX_ERROR);\n            return;\n        }\n    }\n\n    if (ngx_http_send_special(r, NGX_HTTP_FLUSH) == NGX_ERROR) {\n        ngx_http_upstream_finalize_request(r, u, NGX_ERROR);\n        return;\n    }\n\n    if (u->peer.connection->read->ready\n        || u->buffer.pos != u->buffer.last)\n    {\n        ngx_post_event(c->read, &ngx_posted_events);\n        ngx_http_upstream_process_upgraded(r, 1, 1);\n        return;\n    }\n\n    ngx_http_upstream_process_upgraded(r, 0, 1);\n}\n\n\nstatic void\nngx_http_upstream_upgraded_read_downstream(ngx_http_request_t *r)\n{\n    ngx_http_upstream_process_upgraded(r, 0, 0);\n}\n\n\nstatic void\nngx_http_upstream_upgraded_write_downstream(ngx_http_request_t *r)\n{\n    ngx_http_upstream_process_upgraded(r, 1, 1);\n}\n\n\nstatic void\nngx_http_upstream_upgraded_read_upstream(ngx_http_request_t *r,\n    ngx_http_upstream_t *u)\n{\n    ngx_http_upstream_process_upgraded(r, 1, 0);\n}\n\n\nstatic void\nngx_http_upstream_upgraded_write_upstream(ngx_http_request_t *r,\n    ngx_http_upstream_t *u)\n{\n    ngx_http_upstream_process_upgraded(r, 0, 1);\n}\n\n\nstatic void\nngx_http_upstream_process_upgraded(ngx_http_request_t *r,\n    ngx_uint_t from_upstream, ngx_uint_t do_write)\n{\n    size_t                     size;\n    ssize_t                    n;\n    ngx_buf_t                 *b;\n    ngx_uint_t                 flags;\n    ngx_connection_t          *c, *downstream, *upstream, *dst, *src;\n    ngx_http_upstream_t       *u;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    c = r->connection;\n    u = r->upstream;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http upstream process upgraded, fu:%ui\", from_upstream);\n\n    downstream = c;\n    upstream = u->peer.connection;\n\n    if (downstream->write->timedout) {\n        c->timedout = 1;\n        ngx_connection_error(c, NGX_ETIMEDOUT, \"client timed out\");\n        ngx_http_upstream_finalize_request(r, u, NGX_HTTP_REQUEST_TIME_OUT);\n        return;\n    }\n\n    if (upstream->read->timedout || upstream->write->timedout) {\n        ngx_connection_error(c, NGX_ETIMEDOUT, \"upstream timed out\");\n        ngx_http_upstream_finalize_request(r, u, NGX_HTTP_GATEWAY_TIME_OUT);\n        return;\n    }\n\n    if (from_upstream) {\n        src = upstream;\n        dst = downstream;\n        b = &u->buffer;\n\n    } else {\n        src = downstream;\n        dst = upstream;\n        b = &u->from_client;\n\n        if (r->header_in->last > r->header_in->pos) {\n            b = r->header_in;\n            b->end = b->last;\n            do_write = 1;\n        }\n\n        if (b->start == NULL) {\n            b->start = ngx_palloc(r->pool, u->conf->buffer_size);\n            if (b->start == NULL) {\n                ngx_http_upstream_finalize_request(r, u, NGX_ERROR);\n                return;\n            }\n\n            b->pos = b->start;\n            b->last = b->start;\n            b->end = b->start + u->conf->buffer_size;\n            b->temporary = 1;\n            b->tag = u->output.tag;\n        }\n    }\n\n    for ( ;; ) {\n\n        if (do_write) {\n\n            size = b->last - b->pos;\n\n            if (size && dst->write->ready) {\n\n                n = dst->send(dst, b->pos, size);\n\n                if (n == NGX_ERROR) {\n                    ngx_http_upstream_finalize_request(r, u, NGX_ERROR);\n                    return;\n                }\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                }\n            }\n        }\n\n        size = b->end - b->last;\n\n        if (size && src->read->ready) {\n\n            n = src->recv(src, b->last, size);\n\n            if (n == NGX_AGAIN || n == 0) {\n                break;\n            }\n\n            if (n > 0) {\n                do_write = 1;\n                b->last += n;\n\n                if (from_upstream) {\n                    u->state->bytes_received += n;\n                }\n\n                continue;\n            }\n\n            if (n == NGX_ERROR) {\n                src->read->eof = 1;\n            }\n        }\n\n        break;\n    }\n\n    if ((upstream->read->eof && u->buffer.pos == u->buffer.last)\n        || (downstream->read->eof && u->from_client.pos == u->from_client.last)\n        || (downstream->read->eof && upstream->read->eof))\n    {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"http upstream upgraded done\");\n        ngx_http_upstream_finalize_request(r, u, 0);\n        return;\n    }\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    if (ngx_handle_write_event(upstream->write, u->conf->send_lowat)\n        != NGX_OK)\n    {\n        ngx_http_upstream_finalize_request(r, u, NGX_ERROR);\n        return;\n    }\n\n    if (upstream->write->active && !upstream->write->ready) {\n#if (T_HTTP_UPSTREAM_TIMEOUT_VAR)\n        ngx_add_timer(upstream->write, (r->send_time == NGX_CONF_UNSET_MSEC)\n                      ? u->conf->send_timeout : r->send_time);\n#else\n        ngx_add_timer(upstream->write, u->conf->send_timeout);\n#endif\n\n    } else if (upstream->write->timer_set) {\n        ngx_del_timer(upstream->write);\n    }\n\n    if (upstream->read->eof || upstream->read->error) {\n        flags = NGX_CLOSE_EVENT;\n\n    } else {\n        flags = 0;\n    }\n\n    if (ngx_handle_read_event(upstream->read, flags) != NGX_OK) {\n        ngx_http_upstream_finalize_request(r, u, NGX_ERROR);\n        return;\n    }\n\n    if (upstream->read->active && !upstream->read->ready) {\n#if (T_HTTP_UPSTREAM_TIMEOUT_VAR)\n        ngx_add_timer(upstream->read, (r->read_time == NGX_CONF_UNSET_MSEC)\n                      ? u->conf->read_timeout : r->read_time);\n#else\n        ngx_add_timer(upstream->read, u->conf->read_timeout);\n#endif\n\n    } else if (upstream->read->timer_set) {\n        ngx_del_timer(upstream->read);\n    }\n\n    if (ngx_handle_write_event(downstream->write, clcf->send_lowat)\n        != NGX_OK)\n    {\n        ngx_http_upstream_finalize_request(r, u, NGX_ERROR);\n        return;\n    }\n\n    if (downstream->read->eof || downstream->read->error) {\n        flags = NGX_CLOSE_EVENT;\n\n    } else {\n        flags = 0;\n    }\n\n    if (ngx_handle_read_event(downstream->read, flags) != NGX_OK) {\n        ngx_http_upstream_finalize_request(r, u, NGX_ERROR);\n        return;\n    }\n\n    if (downstream->write->active && !downstream->write->ready) {\n        ngx_add_timer(downstream->write, clcf->send_timeout);\n\n    } else if (downstream->write->timer_set) {\n        ngx_del_timer(downstream->write);\n    }\n}\n\n\nstatic void\nngx_http_upstream_process_non_buffered_downstream(ngx_http_request_t *r)\n{\n    ngx_event_t          *wev;\n    ngx_connection_t     *c;\n    ngx_http_upstream_t  *u;\n\n    c = r->connection;\n    u = r->upstream;\n    wev = c->write;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http upstream process non buffered downstream\");\n\n    c->log->action = \"sending to client\";\n\n    if (wev->timedout) {\n        c->timedout = 1;\n        ngx_connection_error(c, NGX_ETIMEDOUT, \"client timed out\");\n        ngx_http_upstream_finalize_request(r, u, NGX_HTTP_REQUEST_TIME_OUT);\n        return;\n    }\n\n#if (T_NGX_MULTI_UPSTREAM)\n    if (u->multi) {\n        ngx_http_multi_upstream_process_non_buffered_request(r);\n        return;\n    }\n#endif\n\n    ngx_http_upstream_process_non_buffered_request(r, 1);\n}\n\n\nstatic void\nngx_http_upstream_process_non_buffered_upstream(ngx_http_request_t *r,\n    ngx_http_upstream_t *u)\n{\n    ngx_connection_t  *c;\n\n    c = u->peer.connection;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http upstream process non buffered upstream\");\n\n    c->log->action = \"reading upstream\";\n\n    if (c->read->timedout) {\n        ngx_connection_error(c, NGX_ETIMEDOUT, \"upstream timed out\");\n        ngx_http_upstream_finalize_request(r, u, NGX_HTTP_GATEWAY_TIME_OUT);\n        return;\n    }\n\n    ngx_http_upstream_process_non_buffered_request(r, 0);\n}\n\n\nstatic void\nngx_http_upstream_process_non_buffered_request(ngx_http_request_t *r,\n    ngx_uint_t do_write)\n{\n    size_t                     size;\n    ssize_t                    n;\n    ngx_buf_t                 *b;\n    ngx_int_t                  rc;\n    ngx_uint_t                 flags;\n    ngx_connection_t          *downstream, *upstream;\n    ngx_http_upstream_t       *u;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    u = r->upstream;\n    downstream = r->connection;\n    upstream = u->peer.connection;\n\n    b = &u->buffer;\n\n    do_write = do_write || u->length == 0;\n\n    for ( ;; ) {\n\n        if (do_write) {\n\n            if (u->out_bufs || u->busy_bufs || downstream->buffered) {\n                rc = ngx_http_output_filter(r, u->out_bufs);\n\n                if (rc == NGX_ERROR) {\n                    ngx_http_upstream_finalize_request(r, u, NGX_ERROR);\n                    return;\n                }\n\n                ngx_chain_update_chains(r->pool, &u->free_bufs, &u->busy_bufs,\n                                        &u->out_bufs, u->output.tag);\n            }\n\n            if (u->busy_bufs == NULL) {\n\n                if (u->length == 0\n                    || (upstream->read->eof && u->length == -1))\n                {\n                    ngx_http_upstream_finalize_request(r, u, 0);\n                    return;\n                }\n\n                if (upstream->read->eof) {\n                    ngx_log_error(NGX_LOG_ERR, upstream->log, 0,\n                                  \"upstream prematurely closed connection\");\n\n                    ngx_http_upstream_finalize_request(r, u,\n                                                       NGX_HTTP_BAD_GATEWAY);\n                    return;\n                }\n\n                if (upstream->read->error || u->error) {\n                    ngx_http_upstream_finalize_request(r, u,\n                                                       NGX_HTTP_BAD_GATEWAY);\n                    return;\n                }\n\n                b->pos = b->start;\n                b->last = b->start;\n            }\n        }\n\n        size = b->end - b->last;\n\n        if (size && upstream->read->ready) {\n\n            n = upstream->recv(upstream, b->last, size);\n\n            if (n == NGX_AGAIN) {\n                break;\n            }\n\n            if (n > 0) {\n                u->state->bytes_received += n;\n                u->state->response_length += n;\n\n                if (u->input_filter(u->input_filter_ctx, n) == NGX_ERROR) {\n                    ngx_http_upstream_finalize_request(r, u, NGX_ERROR);\n                    return;\n                }\n            }\n\n            do_write = 1;\n\n            continue;\n        }\n\n        break;\n    }\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    if (downstream->data == r) {\n        if (ngx_handle_write_event(downstream->write, clcf->send_lowat)\n            != NGX_OK)\n        {\n            ngx_http_upstream_finalize_request(r, u, NGX_ERROR);\n            return;\n        }\n    }\n\n    if (downstream->write->active && !downstream->write->ready) {\n        ngx_add_timer(downstream->write, clcf->send_timeout);\n\n    } else if (downstream->write->timer_set) {\n        ngx_del_timer(downstream->write);\n    }\n\n    if (upstream->read->eof || upstream->read->error) {\n        flags = NGX_CLOSE_EVENT;\n\n    } else {\n        flags = 0;\n    }\n\n    if (ngx_handle_read_event(upstream->read, flags) != NGX_OK) {\n        ngx_http_upstream_finalize_request(r, u, NGX_ERROR);\n        return;\n    }\n\n    if (upstream->read->active && !upstream->read->ready) {\n#if (T_HTTP_UPSTREAM_TIMEOUT_VAR)\n        ngx_add_timer(upstream->read, (r->read_time == NGX_CONF_UNSET_MSEC)\n                      ? u->conf->read_timeout : r->read_time);\n#else\n        ngx_add_timer(upstream->read, u->conf->read_timeout);\n#endif\n\n    } else if (upstream->read->timer_set) {\n        ngx_del_timer(upstream->read);\n    }\n}\n\n\nngx_int_t\nngx_http_upstream_non_buffered_filter_init(void *data)\n{\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_upstream_non_buffered_filter(void *data, ssize_t bytes)\n{\n    ngx_http_request_t  *r = data;\n\n    ngx_buf_t            *b;\n    ngx_chain_t          *cl, **ll;\n    ngx_http_upstream_t  *u;\n\n    u = r->upstream;\n\n    if (u->length == 0) {\n        ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,\n                      \"upstream sent more data than specified in \"\n                      \"\\\"Content-Length\\\" header\");\n        return NGX_OK;\n    }\n\n    for (cl = u->out_bufs, ll = &u->out_bufs; cl; cl = cl->next) {\n        ll = &cl->next;\n    }\n\n    cl = ngx_chain_get_free_buf(r->pool, &u->free_bufs);\n    if (cl == NULL) {\n        return NGX_ERROR;\n    }\n\n    *ll = cl;\n\n    cl->buf->flush = 1;\n    cl->buf->memory = 1;\n\n    b = &u->buffer;\n\n    cl->buf->pos = b->last;\n    b->last += bytes;\n    cl->buf->last = b->last;\n    cl->buf->tag = u->output.tag;\n\n    if (u->length == -1) {\n        return NGX_OK;\n    }\n\n    if (bytes > u->length) {\n\n        ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,\n                      \"upstream sent more data than specified in \"\n                      \"\\\"Content-Length\\\" header\");\n\n        cl->buf->last = cl->buf->pos + u->length;\n        u->length = 0;\n\n        return NGX_OK;\n    }\n\n    u->length -= bytes;\n\n    return NGX_OK;\n}\n\n\n#if (NGX_THREADS)\n\nstatic ngx_int_t\nngx_http_upstream_thread_handler(ngx_thread_task_t *task, ngx_file_t *file)\n{\n    ngx_str_t                  name;\n    ngx_event_pipe_t          *p;\n    ngx_connection_t          *c;\n    ngx_thread_pool_t         *tp;\n    ngx_http_request_t        *r;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    r = file->thread_ctx;\n    p = r->upstream->pipe;\n\n    if (r->aio) {\n        /*\n         * tolerate sendfile() calls if another operation is already\n         * running; this can happen due to subrequests, multiple calls\n         * of the next body filter from a filter, or in HTTP/2 due to\n         * a write event on the main connection\n         */\n\n        c = r->connection;\n\n#if (NGX_HTTP_V2)\n        if (r->stream) {\n            c = r->stream->connection->connection;\n        }\n#endif\n\n        if (task == c->sendfile_task) {\n            return NGX_OK;\n        }\n    }\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n    tp = clcf->thread_pool;\n\n    if (tp == NULL) {\n        if (ngx_http_complex_value(r, clcf->thread_pool_value, &name)\n            != NGX_OK)\n        {\n            return NGX_ERROR;\n        }\n\n        tp = ngx_thread_pool_get((ngx_cycle_t *) ngx_cycle, &name);\n\n        if (tp == NULL) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"thread pool \\\"%V\\\" not found\", &name);\n            return NGX_ERROR;\n        }\n    }\n\n    task->event.data = r;\n    task->event.handler = ngx_http_upstream_thread_event_handler;\n\n    if (ngx_thread_task_post(tp, task) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    r->main->blocked++;\n    r->aio = 1;\n    p->aio = 1;\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_upstream_thread_event_handler(ngx_event_t *ev)\n{\n    ngx_connection_t    *c;\n    ngx_http_request_t  *r;\n\n    r = ev->data;\n    c = r->connection;\n\n    ngx_http_set_log_request(c->log, r);\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http upstream thread: \\\"%V?%V\\\"\", &r->uri, &r->args);\n\n    r->main->blocked--;\n    r->aio = 0;\n\n#if (NGX_HTTP_V2)\n\n    if (r->stream) {\n        /*\n         * for HTTP/2, update write event to make sure processing will\n         * reach the main connection to handle sendfile() in threads\n         */\n\n        c->write->ready = 1;\n        c->write->active = 0;\n    }\n\n#endif\n\n    if (r->done) {\n        /*\n         * trigger connection event handler if the subrequest was\n         * already finalized; this can happen if the handler is used\n         * for sendfile() in threads\n         */\n\n        c->write->handler(c->write);\n\n    } else {\n        r->write_event_handler(r);\n        ngx_http_run_posted_requests(c);\n    }\n}\n\n#endif\n\n\nstatic ngx_int_t\nngx_http_upstream_output_filter(void *data, ngx_chain_t *chain)\n{\n    ngx_int_t            rc;\n    ngx_event_pipe_t    *p;\n    ngx_http_request_t  *r;\n\n    r = data;\n    p = r->upstream->pipe;\n\n    rc = ngx_http_output_filter(r, chain);\n\n    p->aio = r->aio;\n\n    return rc;\n}\n\n\nstatic void\nngx_http_upstream_process_downstream(ngx_http_request_t *r)\n{\n    ngx_event_t          *wev;\n    ngx_connection_t     *c;\n    ngx_event_pipe_t     *p;\n    ngx_http_upstream_t  *u;\n\n    c = r->connection;\n    u = r->upstream;\n    p = u->pipe;\n    wev = c->write;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http upstream process downstream\");\n\n    c->log->action = \"sending to client\";\n\n#if (NGX_THREADS)\n    p->aio = r->aio;\n#endif\n\n    if (wev->timedout) {\n\n        p->downstream_error = 1;\n        c->timedout = 1;\n        ngx_connection_error(c, NGX_ETIMEDOUT, \"client timed out\");\n\n    } else {\n\n        if (wev->delayed) {\n\n            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                           \"http downstream delayed\");\n\n            if (ngx_handle_write_event(wev, p->send_lowat) != NGX_OK) {\n                ngx_http_upstream_finalize_request(r, u, NGX_ERROR);\n            }\n\n            return;\n        }\n\n        if (ngx_event_pipe(p, 1) == NGX_ABORT) {\n            ngx_http_upstream_finalize_request(r, u, NGX_ERROR);\n            return;\n        }\n    }\n\n    ngx_http_upstream_process_request(r, u);\n}\n\n\nstatic void\nngx_http_upstream_process_upstream(ngx_http_request_t *r,\n    ngx_http_upstream_t *u)\n{\n    ngx_event_t       *rev;\n    ngx_event_pipe_t  *p;\n    ngx_connection_t  *c;\n\n    c = u->peer.connection;\n    p = u->pipe;\n    rev = c->read;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http upstream process upstream\");\n\n    c->log->action = \"reading upstream\";\n\n    if (rev->timedout) {\n\n        p->upstream_error = 1;\n        ngx_connection_error(c, NGX_ETIMEDOUT, \"upstream timed out\");\n\n    } else {\n\n        if (rev->delayed) {\n\n            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                           \"http upstream delayed\");\n\n            if (ngx_handle_read_event(rev, 0) != NGX_OK) {\n                ngx_http_upstream_finalize_request(r, u, NGX_ERROR);\n            }\n\n            return;\n        }\n\n        if (ngx_event_pipe(p, 0) == NGX_ABORT) {\n            ngx_http_upstream_finalize_request(r, u, NGX_ERROR);\n            return;\n        }\n    }\n\n    ngx_http_upstream_process_request(r, u);\n}\n\n\nstatic void\nngx_http_upstream_process_request(ngx_http_request_t *r,\n    ngx_http_upstream_t *u)\n{\n    ngx_temp_file_t   *tf;\n    ngx_event_pipe_t  *p;\n\n    p = u->pipe;\n\n#if (NGX_THREADS)\n\n    if (p->writing && !p->aio) {\n\n        /*\n         * make sure to call ngx_event_pipe()\n         * if there is an incomplete aio write\n         */\n\n        if (ngx_event_pipe(p, 1) == NGX_ABORT) {\n            ngx_http_upstream_finalize_request(r, u, NGX_ERROR);\n            return;\n        }\n    }\n\n    if (p->writing) {\n        return;\n    }\n\n#endif\n\n    if (u->peer.connection) {\n\n        if (u->store) {\n\n            if (p->upstream_eof || p->upstream_done) {\n\n                tf = p->temp_file;\n\n                if (u->headers_in.status_n == NGX_HTTP_OK\n                    && (p->upstream_done || p->length == -1)\n                    && (u->headers_in.content_length_n == -1\n                        || u->headers_in.content_length_n == tf->offset))\n                {\n                    ngx_http_upstream_store(r, u);\n                }\n            }\n        }\n\n#if (NGX_HTTP_CACHE)\n\n        if (u->cacheable) {\n\n            if (p->upstream_done) {\n                ngx_http_file_cache_update(r, p->temp_file);\n\n            } else if (p->upstream_eof) {\n\n                tf = p->temp_file;\n\n                if (p->length == -1\n                    && (u->headers_in.content_length_n == -1\n                        || u->headers_in.content_length_n\n                           == tf->offset - (off_t) r->cache->body_start))\n                {\n                    ngx_http_file_cache_update(r, tf);\n\n                } else {\n                    ngx_http_file_cache_free(r->cache, tf);\n                }\n\n            } else if (p->upstream_error) {\n                ngx_http_file_cache_free(r->cache, p->temp_file);\n            }\n        }\n\n#endif\n\n        if (p->upstream_done || p->upstream_eof || p->upstream_error) {\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"http upstream exit: %p\", p->out);\n\n            if (p->upstream_done\n                || (p->upstream_eof && p->length == -1))\n            {\n                ngx_http_upstream_finalize_request(r, u, 0);\n                return;\n            }\n\n            if (p->upstream_eof) {\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                              \"upstream prematurely closed connection\");\n            }\n\n            ngx_http_upstream_finalize_request(r, u, NGX_HTTP_BAD_GATEWAY);\n            return;\n        }\n    }\n\n    if (p->downstream_error) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"http upstream downstream error\");\n\n        if (!u->cacheable && !u->store && u->peer.connection) {\n            ngx_http_upstream_finalize_request(r, u, NGX_ERROR);\n        }\n    }\n}\n\n\nstatic void\nngx_http_upstream_store(ngx_http_request_t *r, ngx_http_upstream_t *u)\n{\n    size_t                  root;\n    time_t                  lm;\n    ngx_str_t               path;\n    ngx_temp_file_t        *tf;\n    ngx_ext_rename_file_t   ext;\n\n    tf = u->pipe->temp_file;\n\n    if (tf->file.fd == NGX_INVALID_FILE) {\n\n        /* create file for empty 200 response */\n\n        tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t));\n        if (tf == NULL) {\n            return;\n        }\n\n        tf->file.fd = NGX_INVALID_FILE;\n        tf->file.log = r->connection->log;\n        tf->path = u->conf->temp_path;\n        tf->pool = r->pool;\n        tf->persistent = 1;\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;\n        }\n\n        u->pipe->temp_file = tf;\n    }\n\n    ext.access = u->conf->store_access;\n    ext.path_access = u->conf->store_access;\n    ext.time = -1;\n    ext.create_path = 1;\n    ext.delete_file = 1;\n    ext.log = r->connection->log;\n\n    if (u->headers_in.last_modified) {\n\n        lm = ngx_parse_http_time(u->headers_in.last_modified->value.data,\n                                 u->headers_in.last_modified->value.len);\n\n        if (lm != NGX_ERROR) {\n            ext.time = lm;\n            ext.fd = tf->file.fd;\n        }\n    }\n\n    if (u->conf->store_lengths == NULL) {\n\n        if (ngx_http_map_uri_to_path(r, &path, &root, 0) == NULL) {\n            return;\n        }\n\n    } else {\n        if (ngx_http_script_run(r, &path, u->conf->store_lengths->elts, 0,\n                                u->conf->store_values->elts)\n            == NULL)\n        {\n            return;\n        }\n    }\n\n    path.len--;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"upstream stores \\\"%s\\\" to \\\"%s\\\"\",\n                   tf->file.name.data, path.data);\n\n    (void) ngx_ext_rename_file(&tf->file.name, &path, &ext);\n\n    u->store = 0;\n}\n\n\nstatic void\nngx_http_upstream_dummy_handler(ngx_http_request_t *r, ngx_http_upstream_t *u)\n{\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http upstream dummy handler\");\n}\n\n\nstatic void\nngx_http_upstream_next(ngx_http_request_t *r, ngx_http_upstream_t *u,\n    ngx_uint_t ft_type)\n{\n    ngx_msec_t  timeout;\n    ngx_uint_t  status, state;\n\n#if (T_NGX_HTTP_UPSTREAM_RETRY_CC)\n    ngx_http_core_loc_conf_t  *clcf;\n#endif\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http next upstream, %xi\", ft_type);\n\n#if (T_NGX_MULTI_UPSTREAM)\n    if (u->multi && ngx_http_multi_connection_fake(r)) {\n        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                \"multi: http next upstream fake_r %p\", r);\n\n        ngx_http_multi_upstream_next(r->connection, ft_type);\n        return;\n    }\n#endif\n\n    if (u->peer.sockaddr) {\n\n        if (u->peer.connection) {\n            u->state->bytes_sent = u->peer.connection->sent;\n        }\n\n        if (ft_type == NGX_HTTP_UPSTREAM_FT_HTTP_403\n            || ft_type == NGX_HTTP_UPSTREAM_FT_HTTP_404)\n        {\n            state = NGX_PEER_NEXT;\n\n        } else {\n            state = NGX_PEER_FAILED;\n        }\n\n        u->peer.free(&u->peer, u->peer.data, state);\n        u->peer.sockaddr = NULL;\n    }\n\n    if (ft_type == NGX_HTTP_UPSTREAM_FT_TIMEOUT) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, NGX_ETIMEDOUT,\n                      \"upstream timed out\");\n    }\n\n#if (T_NGX_HTTP_UPSTREAM_RETRY_CC)\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n#endif\n\n    if (u->peer.cached && ft_type == NGX_HTTP_UPSTREAM_FT_ERROR\n#if (T_NGX_HTTP_UPSTREAM_RETRY_CC)\n        && clcf->retry_cached_connection\n#endif\n       )\n    {\n        /* TODO: inform balancer instead */\n        u->peer.tries++;\n    }\n\n    switch (ft_type) {\n\n    case NGX_HTTP_UPSTREAM_FT_TIMEOUT:\n    case NGX_HTTP_UPSTREAM_FT_HTTP_504:\n        status = NGX_HTTP_GATEWAY_TIME_OUT;\n        break;\n\n    case NGX_HTTP_UPSTREAM_FT_HTTP_500:\n        status = NGX_HTTP_INTERNAL_SERVER_ERROR;\n        break;\n\n    case NGX_HTTP_UPSTREAM_FT_HTTP_503:\n        status = NGX_HTTP_SERVICE_UNAVAILABLE;\n        break;\n\n    case NGX_HTTP_UPSTREAM_FT_HTTP_403:\n        status = NGX_HTTP_FORBIDDEN;\n        break;\n\n    case NGX_HTTP_UPSTREAM_FT_HTTP_404:\n        status = NGX_HTTP_NOT_FOUND;\n        break;\n\n    case NGX_HTTP_UPSTREAM_FT_HTTP_429:\n        status = NGX_HTTP_TOO_MANY_REQUESTS;\n        break;\n\n    /*\n     * NGX_HTTP_UPSTREAM_FT_BUSY_LOCK and NGX_HTTP_UPSTREAM_FT_MAX_WAITING\n     * never reach here\n     */\n\n    default:\n        status = NGX_HTTP_BAD_GATEWAY;\n    }\n\n    if (r->connection->error) {\n        ngx_http_upstream_finalize_request(r, u,\n                                           NGX_HTTP_CLIENT_CLOSED_REQUEST);\n        return;\n    }\n\n    u->state->status = status;\n\n    timeout = u->conf->next_upstream_timeout;\n\n    if (u->request_sent\n        && (r->method & (NGX_HTTP_POST|NGX_HTTP_LOCK|NGX_HTTP_PATCH)))\n    {\n        ft_type |= NGX_HTTP_UPSTREAM_FT_NON_IDEMPOTENT;\n    }\n\n    if (u->peer.tries == 0\n        || ((u->conf->next_upstream & ft_type) != ft_type)\n        || (u->request_sent && r->request_body_no_buffering)\n        || (timeout && ngx_current_msec - u->peer.start_time >= timeout))\n    {\n#if (NGX_HTTP_CACHE)\n\n        if (u->cache_status == NGX_HTTP_CACHE_EXPIRED\n            && ((u->conf->cache_use_stale & ft_type) || r->cache->stale_error))\n        {\n            ngx_int_t  rc;\n\n            rc = u->reinit_request(r);\n\n            if (rc != NGX_OK) {\n                ngx_http_upstream_finalize_request(r, u, rc);\n                return;\n            }\n\n            u->cache_status = NGX_HTTP_CACHE_STALE;\n            rc = ngx_http_upstream_cache_send(r, u);\n\n            if (rc == NGX_DONE) {\n                return;\n            }\n\n            if (rc == NGX_HTTP_UPSTREAM_INVALID_HEADER) {\n                rc = NGX_HTTP_INTERNAL_SERVER_ERROR;\n            }\n\n            ngx_http_upstream_finalize_request(r, u, rc);\n            return;\n        }\n#endif\n\n        ngx_http_upstream_finalize_request(r, u, status);\n        return;\n    }\n\n    if (u->peer.connection) {\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"close http upstream connection: %d\",\n                       u->peer.connection->fd);\n#if (NGX_HTTP_SSL)\n\n        if (u->peer.connection->ssl) {\n            u->peer.connection->ssl->no_wait_shutdown = 1;\n            u->peer.connection->ssl->no_send_shutdown = 1;\n\n            (void) ngx_ssl_shutdown(u->peer.connection);\n        }\n#endif\n\n        if (u->peer.connection->pool) {\n            ngx_destroy_pool(u->peer.connection->pool);\n        }\n\n        ngx_close_connection(u->peer.connection);\n        u->peer.connection = NULL;\n    }\n\n#if (T_NGX_HTTP_DYNAMIC_RESOLVE)\n    u->peer.resolved = 0;\n#endif\n    ngx_http_upstream_connect(r, u);\n}\n\n\nstatic void\nngx_http_upstream_cleanup(void *data)\n{\n    ngx_http_request_t *r = data;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"cleanup http upstream request: \\\"%V\\\"\", &r->uri);\n\n    ngx_http_upstream_finalize_request(r, r->upstream, NGX_DONE);\n}\n\n\n#if (!T_NGX_HTTP_DYNAMIC_RESOLVE)\nstatic\n#endif\nvoid\nngx_http_upstream_finalize_request(ngx_http_request_t *r,\n    ngx_http_upstream_t *u, ngx_int_t rc)\n{\n    ngx_uint_t  flush;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"finalize http upstream request: %i\", rc);\n\n#if (T_NGX_MULTI_UPSTREAM)\n    if (u->multi && ngx_http_multi_connection_fake(r)) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                \"http finalize upstream fake_r %p\", r);\n\n        ngx_http_multi_upstream_finalize_request(r->connection, rc);\n        return;\n    }\n#endif\n\n    if (u->cleanup == NULL) {\n        /* the request was already finalized */\n        ngx_http_finalize_request(r, NGX_DONE);\n        return;\n    }\n\n    *u->cleanup = NULL;\n    u->cleanup = NULL;\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 (T_NGX_HTTP_DYNAMIC_RESOLVE)\n    if (u->dyn_resolve_ctx) {\n        ngx_resolve_name_done(u->dyn_resolve_ctx);\n        u->dyn_resolve_ctx = NULL;\n    }\n#endif\n\n    if (u->state && u->state->response_time == (ngx_msec_t) -1) {\n        u->state->response_time = ngx_current_msec - u->start_time;\n\n        if (u->pipe && u->pipe->read_length) {\n            u->state->bytes_received += u->pipe->read_length\n                                        - u->pipe->preread_size;\n            u->state->response_length = u->pipe->read_length;\n        }\n\n        if (u->peer.connection) {\n            u->state->bytes_sent = u->peer.connection->sent;\n        }\n    }\n\n    u->finalize_request(r, rc);\n\n    if (u->peer.free && u->peer.sockaddr) {\n        u->peer.free(&u->peer, u->peer.data, 0);\n        u->peer.sockaddr = NULL;\n    }\n\n    if (u->peer.connection) {\n\n#if (NGX_HTTP_SSL)\n\n        /* TODO: do not shutdown persistent connection */\n\n        if (u->peer.connection->ssl) {\n\n            /*\n             * We send the \"close notify\" shutdown alert to the upstream only\n             * and do not wait its \"close notify\" shutdown alert.\n             * It is acceptable according to the TLS standard.\n             */\n\n            u->peer.connection->ssl->no_wait_shutdown = 1;\n\n            (void) ngx_ssl_shutdown(u->peer.connection);\n        }\n#endif\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"close http upstream connection: %d\",\n                       u->peer.connection->fd);\n\n        if (u->peer.connection->pool) {\n            ngx_destroy_pool(u->peer.connection->pool);\n        }\n\n        ngx_close_connection(u->peer.connection);\n    }\n\n    u->peer.connection = NULL;\n\n    if (u->pipe && u->pipe->temp_file) {\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"http upstream temp fd: %d\",\n                       u->pipe->temp_file->file.fd);\n    }\n\n    if (u->store && u->pipe && u->pipe->temp_file\n        && u->pipe->temp_file->file.fd != NGX_INVALID_FILE)\n    {\n        if (ngx_delete_file(u->pipe->temp_file->file.name.data)\n            == NGX_FILE_ERROR)\n        {\n            ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,\n                          ngx_delete_file_n \" \\\"%s\\\" failed\",\n                          u->pipe->temp_file->file.name.data);\n        }\n    }\n\n#if (NGX_HTTP_CACHE)\n\n    if (r->cache) {\n\n        if (u->cacheable) {\n\n            if (rc == NGX_HTTP_BAD_GATEWAY || rc == NGX_HTTP_GATEWAY_TIME_OUT) {\n                time_t  valid;\n\n                valid = ngx_http_file_cache_valid(u->conf->cache_valid, rc);\n\n                if (valid) {\n                    r->cache->valid_sec = ngx_time() + valid;\n                    r->cache->error = rc;\n                }\n            }\n        }\n\n        ngx_http_file_cache_free(r->cache, u->pipe->temp_file);\n    }\n\n#endif\n\n    r->read_event_handler = ngx_http_block_reading;\n\n    if (rc == NGX_DECLINED) {\n        return;\n    }\n\n    r->connection->log->action = \"sending to client\";\n\n    if (!u->header_sent\n        || rc == NGX_HTTP_REQUEST_TIME_OUT\n        || rc == NGX_HTTP_CLIENT_CLOSED_REQUEST)\n    {\n        ngx_http_finalize_request(r, rc);\n        return;\n    }\n\n    flush = 0;\n\n    if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {\n        rc = NGX_ERROR;\n        flush = 1;\n    }\n\n    if (r->header_only\n        || (u->pipe && u->pipe->downstream_error))\n    {\n        ngx_http_finalize_request(r, rc);\n        return;\n    }\n\n    if (rc == 0) {\n\n        if (ngx_http_upstream_process_trailers(r, u) != NGX_OK) {\n            ngx_http_finalize_request(r, NGX_ERROR);\n            return;\n        }\n\n        rc = ngx_http_send_special(r, NGX_HTTP_LAST);\n\n    } else if (flush) {\n        r->keepalive = 0;\n        rc = ngx_http_send_special(r, NGX_HTTP_FLUSH);\n    }\n\n    ngx_http_finalize_request(r, rc);\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_process_header_line(ngx_http_request_t *r, ngx_table_elt_t *h,\n    ngx_uint_t offset)\n{\n    ngx_table_elt_t  **ph;\n\n    ph = (ngx_table_elt_t **) ((char *) &r->upstream->headers_in + offset);\n\n    if (*ph) {\n        ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,\n                      \"upstream sent duplicate header line: \\\"%V: %V\\\", \"\n                      \"previous value: \\\"%V: %V\\\", ignored\",\n                      &h->key, &h->value,\n                      &(*ph)->key, &(*ph)->value);\n        h->hash = 0;\n        return NGX_OK;\n    }\n\n    *ph = h;\n    h->next = NULL;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_process_multi_header_lines(ngx_http_request_t *r,\n    ngx_table_elt_t *h, ngx_uint_t offset)\n{\n    ngx_table_elt_t  **ph;\n\n    ph = (ngx_table_elt_t **) ((char *) &r->upstream->headers_in + offset);\n\n    while (*ph) { ph = &(*ph)->next; }\n\n    *ph = h;\n    h->next = NULL;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_ignore_header_line(ngx_http_request_t *r, ngx_table_elt_t *h,\n    ngx_uint_t offset)\n{\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_process_content_length(ngx_http_request_t *r,\n    ngx_table_elt_t *h, ngx_uint_t offset)\n{\n    ngx_http_upstream_t  *u;\n\n    u = r->upstream;\n\n    if (u->headers_in.content_length) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"upstream sent duplicate header line: \\\"%V: %V\\\", \"\n                      \"previous value: \\\"%V: %V\\\"\",\n                      &h->key, &h->value,\n                      &u->headers_in.content_length->key,\n                      &u->headers_in.content_length->value);\n        return NGX_HTTP_UPSTREAM_INVALID_HEADER;\n    }\n\n    if (u->headers_in.transfer_encoding) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"upstream sent \\\"Content-Length\\\" and \"\n                      \"\\\"Transfer-Encoding\\\" headers at the same time\");\n        return NGX_HTTP_UPSTREAM_INVALID_HEADER;\n    }\n\n    h->next = NULL;\n    u->headers_in.content_length = h;\n    u->headers_in.content_length_n = ngx_atoof(h->value.data, h->value.len);\n\n    if (u->headers_in.content_length_n == NGX_ERROR) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"upstream sent invalid \\\"Content-Length\\\" header: \"\n                      \"\\\"%V: %V\\\"\", &h->key, &h->value);\n        return NGX_HTTP_UPSTREAM_INVALID_HEADER;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_process_last_modified(ngx_http_request_t *r,\n    ngx_table_elt_t *h, ngx_uint_t offset)\n{\n    ngx_http_upstream_t  *u;\n\n    u = r->upstream;\n\n    if (u->headers_in.last_modified) {\n        ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,\n                      \"upstream sent duplicate header line: \\\"%V: %V\\\", \"\n                      \"previous value: \\\"%V: %V\\\", ignored\",\n                      &h->key, &h->value,\n                      &u->headers_in.last_modified->key,\n                      &u->headers_in.last_modified->value);\n        h->hash = 0;\n        return NGX_OK;\n    }\n\n    h->next = NULL;\n    u->headers_in.last_modified = h;\n    u->headers_in.last_modified_time = ngx_parse_http_time(h->value.data,\n                                                           h->value.len);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_process_set_cookie(ngx_http_request_t *r, ngx_table_elt_t *h,\n    ngx_uint_t offset)\n{\n    ngx_table_elt_t      **ph;\n    ngx_http_upstream_t   *u;\n\n    u = r->upstream;\n    ph = &u->headers_in.set_cookie;\n\n    while (*ph) { ph = &(*ph)->next; }\n\n    *ph = h;\n    h->next = NULL;\n\n#if (NGX_HTTP_CACHE)\n    if (!(u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_SET_COOKIE)) {\n        u->cacheable = 0;\n    }\n#endif\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_process_cache_control(ngx_http_request_t *r,\n    ngx_table_elt_t *h, ngx_uint_t offset)\n{\n    ngx_table_elt_t      **ph;\n    ngx_http_upstream_t   *u;\n\n    u = r->upstream;\n    ph = &u->headers_in.cache_control;\n\n    while (*ph) { ph = &(*ph)->next; }\n\n    *ph = h;\n    h->next = NULL;\n\n#if (NGX_HTTP_CACHE)\n    {\n    u_char     *p, *start, *last;\n    ngx_int_t   n;\n\n    if (u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_CACHE_CONTROL) {\n        return NGX_OK;\n    }\n\n    if (r->cache == NULL) {\n        return NGX_OK;\n    }\n\n    start = h->value.data;\n    last = start + h->value.len;\n\n    if (r->cache->valid_sec != 0 && u->headers_in.x_accel_expires != NULL) {\n        goto extensions;\n    }\n\n    if (ngx_strlcasestrn(start, last, (u_char *) \"no-cache\", 8 - 1) != NULL\n        || ngx_strlcasestrn(start, last, (u_char *) \"no-store\", 8 - 1) != NULL\n        || ngx_strlcasestrn(start, last, (u_char *) \"private\", 7 - 1) != NULL)\n    {\n        u->headers_in.no_cache = 1;\n        return NGX_OK;\n    }\n\n    p = ngx_strlcasestrn(start, last, (u_char *) \"s-maxage=\", 9 - 1);\n    offset = 9;\n\n    if (p == NULL) {\n        p = ngx_strlcasestrn(start, last, (u_char *) \"max-age=\", 8 - 1);\n        offset = 8;\n    }\n\n    if (p) {\n        n = 0;\n\n        for (p += offset; p < last; p++) {\n            if (*p == ',' || *p == ';' || *p == ' ') {\n                break;\n            }\n\n            if (*p >= '0' && *p <= '9') {\n                n = n * 10 + (*p - '0');\n                continue;\n            }\n\n            u->cacheable = 0;\n            return NGX_OK;\n        }\n\n        if (n == 0) {\n            u->headers_in.no_cache = 1;\n            return NGX_OK;\n        }\n\n        r->cache->valid_sec = ngx_time() + n;\n        u->headers_in.expired = 0;\n    }\n\nextensions:\n\n    p = ngx_strlcasestrn(start, last, (u_char *) \"stale-while-revalidate=\",\n                         23 - 1);\n\n    if (p) {\n        n = 0;\n\n        for (p += 23; p < last; p++) {\n            if (*p == ',' || *p == ';' || *p == ' ') {\n                break;\n            }\n\n            if (*p >= '0' && *p <= '9') {\n                n = n * 10 + (*p - '0');\n                continue;\n            }\n\n            u->cacheable = 0;\n            return NGX_OK;\n        }\n\n        r->cache->updating_sec = n;\n        r->cache->error_sec = n;\n    }\n\n    p = ngx_strlcasestrn(start, last, (u_char *) \"stale-if-error=\", 15 - 1);\n\n    if (p) {\n        n = 0;\n\n        for (p += 15; p < last; p++) {\n            if (*p == ',' || *p == ';' || *p == ' ') {\n                break;\n            }\n\n            if (*p >= '0' && *p <= '9') {\n                n = n * 10 + (*p - '0');\n                continue;\n            }\n\n            u->cacheable = 0;\n            return NGX_OK;\n        }\n\n        r->cache->error_sec = n;\n    }\n    }\n#endif\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_process_expires(ngx_http_request_t *r, ngx_table_elt_t *h,\n    ngx_uint_t offset)\n{\n    ngx_http_upstream_t  *u;\n\n    u = r->upstream;\n\n    if (u->headers_in.expires) {\n        ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,\n                      \"upstream sent duplicate header line: \\\"%V: %V\\\", \"\n                      \"previous value: \\\"%V: %V\\\", ignored\",\n                      &h->key, &h->value,\n                      &u->headers_in.expires->key,\n                      &u->headers_in.expires->value);\n        h->hash = 0;\n        return NGX_OK;\n    }\n\n    u->headers_in.expires = h;\n    h->next = NULL;\n\n#if (NGX_HTTP_CACHE)\n    {\n    time_t  expires;\n\n    if (u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_EXPIRES) {\n        return NGX_OK;\n    }\n\n    if (r->cache == NULL) {\n        return NGX_OK;\n    }\n\n    if (r->cache->valid_sec != 0) {\n        return NGX_OK;\n    }\n\n    expires = ngx_parse_http_time(h->value.data, h->value.len);\n\n    if (expires == NGX_ERROR || expires < ngx_time()) {\n        u->headers_in.expired = 1;\n        return NGX_OK;\n    }\n\n    r->cache->valid_sec = expires;\n    }\n#endif\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_process_accel_expires(ngx_http_request_t *r,\n    ngx_table_elt_t *h, ngx_uint_t offset)\n{\n    ngx_http_upstream_t  *u;\n\n    u = r->upstream;\n\n    if (u->headers_in.x_accel_expires) {\n        ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,\n                      \"upstream sent duplicate header line: \\\"%V: %V\\\", \"\n                      \"previous value: \\\"%V: %V\\\", ignored\",\n                      &h->key, &h->value,\n                      &u->headers_in.x_accel_expires->key,\n                      &u->headers_in.x_accel_expires->value);\n        h->hash = 0;\n        return NGX_OK;\n    }\n\n    u->headers_in.x_accel_expires = h;\n    h->next = NULL;\n\n#if (NGX_HTTP_CACHE)\n    {\n    u_char     *p;\n    size_t      len;\n    ngx_int_t   n;\n\n    if (u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_XA_EXPIRES) {\n        return NGX_OK;\n    }\n\n    if (r->cache == NULL) {\n        return NGX_OK;\n    }\n\n    len = h->value.len;\n    p = h->value.data;\n\n    if (p[0] != '@') {\n        n = ngx_atoi(p, len);\n\n        switch (n) {\n        case 0:\n            u->cacheable = 0;\n            /* fall through */\n\n        case NGX_ERROR:\n            return NGX_OK;\n\n        default:\n            r->cache->valid_sec = ngx_time() + n;\n            u->headers_in.no_cache = 0;\n            u->headers_in.expired = 0;\n            return NGX_OK;\n        }\n    }\n\n    p++;\n    len--;\n\n    n = ngx_atoi(p, len);\n\n    if (n != NGX_ERROR) {\n        r->cache->valid_sec = n;\n        u->headers_in.no_cache = 0;\n        u->headers_in.expired = 0;\n    }\n    }\n#endif\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_process_limit_rate(ngx_http_request_t *r, ngx_table_elt_t *h,\n    ngx_uint_t offset)\n{\n    ngx_int_t             n;\n    ngx_http_upstream_t  *u;\n\n    u = r->upstream;\n\n    if (u->headers_in.x_accel_limit_rate) {\n        ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,\n                      \"upstream sent duplicate header line: \\\"%V: %V\\\", \"\n                      \"previous value: \\\"%V: %V\\\", ignored\",\n                      &h->key, &h->value,\n                      &u->headers_in.x_accel_limit_rate->key,\n                      &u->headers_in.x_accel_limit_rate->value);\n        h->hash = 0;\n        return NGX_OK;\n    }\n\n    u->headers_in.x_accel_limit_rate = h;\n    h->next = NULL;\n\n    if (u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_XA_LIMIT_RATE) {\n        return NGX_OK;\n    }\n\n    n = ngx_atoi(h->value.data, h->value.len);\n\n    if (n != NGX_ERROR) {\n        r->limit_rate = (size_t) n;\n        r->limit_rate_set = 1;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_process_buffering(ngx_http_request_t *r, ngx_table_elt_t *h,\n    ngx_uint_t offset)\n{\n    u_char                c0, c1, c2;\n    ngx_http_upstream_t  *u;\n\n    u = r->upstream;\n\n    if (u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_XA_BUFFERING) {\n        return NGX_OK;\n    }\n\n    if (u->conf->change_buffering) {\n\n        if (h->value.len == 2) {\n            c0 = ngx_tolower(h->value.data[0]);\n            c1 = ngx_tolower(h->value.data[1]);\n\n            if (c0 == 'n' && c1 == 'o') {\n                u->buffering = 0;\n            }\n\n        } else if (h->value.len == 3) {\n            c0 = ngx_tolower(h->value.data[0]);\n            c1 = ngx_tolower(h->value.data[1]);\n            c2 = ngx_tolower(h->value.data[2]);\n\n            if (c0 == 'y' && c1 == 'e' && c2 == 's') {\n                u->buffering = 1;\n            }\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_process_charset(ngx_http_request_t *r, ngx_table_elt_t *h,\n    ngx_uint_t offset)\n{\n    ngx_http_upstream_t  *u;\n\n    u = r->upstream;\n\n    if (u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_XA_CHARSET) {\n        return NGX_OK;\n    }\n\n    r->headers_out.override_charset = &h->value;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_process_connection(ngx_http_request_t *r, ngx_table_elt_t *h,\n    ngx_uint_t offset)\n{\n    ngx_table_elt_t      **ph;\n    ngx_http_upstream_t   *u;\n\n    u = r->upstream;\n    ph = &u->headers_in.connection;\n\n    while (*ph) { ph = &(*ph)->next; }\n\n    *ph = h;\n    h->next = NULL;\n\n    if (ngx_strlcasestrn(h->value.data, h->value.data + h->value.len,\n                         (u_char *) \"close\", 5 - 1)\n        != NULL)\n    {\n        u->headers_in.connection_close = 1;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_process_transfer_encoding(ngx_http_request_t *r,\n    ngx_table_elt_t *h, ngx_uint_t offset)\n{\n    ngx_http_upstream_t  *u;\n\n    u = r->upstream;\n\n    if (u->headers_in.transfer_encoding) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"upstream sent duplicate header line: \\\"%V: %V\\\", \"\n                      \"previous value: \\\"%V: %V\\\"\",\n                      &h->key, &h->value,\n                      &u->headers_in.transfer_encoding->key,\n                      &u->headers_in.transfer_encoding->value);\n        return NGX_HTTP_UPSTREAM_INVALID_HEADER;\n    }\n\n    if (u->headers_in.content_length) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"upstream sent \\\"Content-Length\\\" and \"\n                      \"\\\"Transfer-Encoding\\\" headers at the same time\");\n        return NGX_HTTP_UPSTREAM_INVALID_HEADER;\n    }\n\n    u->headers_in.transfer_encoding = h;\n    h->next = NULL;\n\n    if (h->value.len == 7\n        && ngx_strncasecmp(h->value.data, (u_char *) \"chunked\", 7) == 0)\n    {\n        u->headers_in.chunked = 1;\n\n    } else {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"upstream sent unknown \\\"Transfer-Encoding\\\": \\\"%V\\\"\",\n                      &h->value);\n        return NGX_HTTP_UPSTREAM_INVALID_HEADER;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_process_vary(ngx_http_request_t *r,\n    ngx_table_elt_t *h, ngx_uint_t offset)\n{\n    ngx_table_elt_t      **ph;\n    ngx_http_upstream_t   *u;\n\n    u = r->upstream;\n    ph = &u->headers_in.vary;\n\n    while (*ph) { ph = &(*ph)->next; }\n\n    *ph = h;\n    h->next = NULL;\n\n#if (NGX_HTTP_CACHE)\n    {\n    u_char     *p;\n    size_t      len;\n    ngx_str_t   vary;\n\n    if (u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_VARY) {\n        return NGX_OK;\n    }\n\n    if (r->cache == NULL || !u->cacheable) {\n        return NGX_OK;\n    }\n\n    if (h->value.len == 1 && h->value.data[0] == '*') {\n        u->cacheable = 0;\n        return NGX_OK;\n    }\n\n    if (u->headers_in.vary->next) {\n\n        len = 0;\n\n        for (h = u->headers_in.vary; h; h = h->next) {\n            len += h->value.len + 2;\n        }\n\n        len -= 2;\n\n        p = ngx_pnalloc(r->pool, len);\n        if (p == NULL) {\n            return NGX_ERROR;\n        }\n\n        vary.len = len;\n        vary.data = p;\n\n        for (h = u->headers_in.vary; h; h = h->next) {\n            p = ngx_copy(p, h->value.data, h->value.len);\n\n            if (h->next == NULL) {\n                break;\n            }\n\n            *p++ = ','; *p++ = ' ';\n        }\n\n    } else {\n        vary = h->value;\n    }\n\n    if (vary.len > NGX_HTTP_CACHE_VARY_LEN) {\n        u->cacheable = 0;\n    }\n\n    r->cache->vary = vary;\n    }\n#endif\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_copy_header_line(ngx_http_request_t *r, ngx_table_elt_t *h,\n    ngx_uint_t offset)\n{\n    ngx_table_elt_t  *ho, **ph;\n\n    ho = ngx_list_push(&r->headers_out.headers);\n    if (ho == NULL) {\n        return NGX_ERROR;\n    }\n\n    *ho = *h;\n\n    if (offset) {\n        ph = (ngx_table_elt_t **) ((char *) &r->headers_out + offset);\n        *ph = ho;\n        ho->next = NULL;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_copy_multi_header_lines(ngx_http_request_t *r,\n    ngx_table_elt_t *h, ngx_uint_t offset)\n{\n    ngx_table_elt_t  *ho, **ph;\n\n    ho = ngx_list_push(&r->headers_out.headers);\n    if (ho == NULL) {\n        return NGX_ERROR;\n    }\n\n    *ho = *h;\n\n    ph = (ngx_table_elt_t **) ((char *) &r->headers_out + offset);\n\n    while (*ph) { ph = &(*ph)->next; }\n\n    *ph = ho;\n    ho->next = NULL;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_copy_content_type(ngx_http_request_t *r, ngx_table_elt_t *h,\n    ngx_uint_t offset)\n{\n    u_char  *p, *last;\n\n    r->headers_out.content_type_len = h->value.len;\n    r->headers_out.content_type = h->value;\n    r->headers_out.content_type_lowcase = NULL;\n\n    for (p = h->value.data; *p; p++) {\n\n        if (*p != ';') {\n            continue;\n        }\n\n        last = p;\n\n        while (*++p == ' ') { /* void */ }\n\n        if (*p == '\\0') {\n            return NGX_OK;\n        }\n\n        if (ngx_strncasecmp(p, (u_char *) \"charset=\", 8) != 0) {\n            continue;\n        }\n\n        p += 8;\n\n        r->headers_out.content_type_len = last - h->value.data;\n\n        if (*p == '\"') {\n            p++;\n        }\n\n        last = h->value.data + h->value.len;\n\n        if (*(last - 1) == '\"') {\n            last--;\n        }\n\n        r->headers_out.charset.len = last - p;\n        r->headers_out.charset.data = p;\n\n        return NGX_OK;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_copy_last_modified(ngx_http_request_t *r, ngx_table_elt_t *h,\n    ngx_uint_t offset)\n{\n    ngx_table_elt_t  *ho;\n\n    ho = ngx_list_push(&r->headers_out.headers);\n    if (ho == NULL) {\n        return NGX_ERROR;\n    }\n\n    *ho = *h;\n    ho->next = NULL;\n\n    r->headers_out.last_modified = ho;\n    r->headers_out.last_modified_time =\n                                    r->upstream->headers_in.last_modified_time;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_rewrite_location(ngx_http_request_t *r, ngx_table_elt_t *h,\n    ngx_uint_t offset)\n{\n    ngx_int_t         rc;\n    ngx_table_elt_t  *ho;\n\n    ho = ngx_list_push(&r->headers_out.headers);\n    if (ho == NULL) {\n        return NGX_ERROR;\n    }\n\n    *ho = *h;\n    ho->next = NULL;\n\n    if (r->upstream->rewrite_redirect) {\n        rc = r->upstream->rewrite_redirect(r, ho, 0);\n\n        if (rc == NGX_DECLINED) {\n            return NGX_OK;\n        }\n\n        if (rc == NGX_OK) {\n            r->headers_out.location = ho;\n\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"rewritten location: \\\"%V\\\"\", &ho->value);\n        }\n\n        return rc;\n    }\n\n    if (ho->value.data[0] != '/') {\n        r->headers_out.location = ho;\n    }\n\n    /*\n     * we do not set r->headers_out.location here to avoid handling\n     * relative redirects in ngx_http_header_filter()\n     */\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_rewrite_refresh(ngx_http_request_t *r, ngx_table_elt_t *h,\n    ngx_uint_t offset)\n{\n    u_char           *p;\n    ngx_int_t         rc;\n    ngx_table_elt_t  *ho;\n\n    ho = ngx_list_push(&r->headers_out.headers);\n    if (ho == NULL) {\n        return NGX_ERROR;\n    }\n\n    *ho = *h;\n    ho->next = NULL;\n\n    if (r->upstream->rewrite_redirect) {\n\n        p = ngx_strcasestrn(ho->value.data, \"url=\", 4 - 1);\n\n        if (p) {\n            rc = r->upstream->rewrite_redirect(r, ho, p + 4 - ho->value.data);\n\n        } else {\n            return NGX_OK;\n        }\n\n        if (rc == NGX_DECLINED) {\n            return NGX_OK;\n        }\n\n        if (rc == NGX_OK) {\n            r->headers_out.refresh = ho;\n\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"rewritten refresh: \\\"%V\\\"\", &ho->value);\n        }\n\n        return rc;\n    }\n\n    r->headers_out.refresh = ho;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_rewrite_set_cookie(ngx_http_request_t *r, ngx_table_elt_t *h,\n    ngx_uint_t offset)\n{\n    ngx_int_t         rc;\n    ngx_table_elt_t  *ho;\n\n    ho = ngx_list_push(&r->headers_out.headers);\n    if (ho == NULL) {\n        return NGX_ERROR;\n    }\n\n    *ho = *h;\n    ho->next = NULL;\n\n    if (r->upstream->rewrite_cookie) {\n        rc = r->upstream->rewrite_cookie(r, ho);\n\n        if (rc == NGX_DECLINED) {\n            return NGX_OK;\n        }\n\n#if (NGX_DEBUG)\n        if (rc == NGX_OK) {\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"rewritten cookie: \\\"%V\\\"\", &ho->value);\n        }\n#endif\n\n        return rc;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_copy_allow_ranges(ngx_http_request_t *r,\n    ngx_table_elt_t *h, ngx_uint_t offset)\n{\n    ngx_table_elt_t  *ho;\n\n    if (r->upstream->conf->force_ranges) {\n        return NGX_OK;\n    }\n\n#if (NGX_HTTP_CACHE)\n\n    if (r->cached) {\n        r->allow_ranges = 1;\n        return NGX_OK;\n    }\n\n    if (r->upstream->cacheable) {\n        r->allow_ranges = 1;\n        r->single_range = 1;\n        return NGX_OK;\n    }\n\n#endif\n\n    ho = ngx_list_push(&r->headers_out.headers);\n    if (ho == NULL) {\n        return NGX_ERROR;\n    }\n\n    *ho = *h;\n    ho->next = NULL;\n\n    r->headers_out.accept_ranges = ho;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_add_variables(ngx_conf_t *cf)\n{\n    ngx_http_variable_t  *var, *v;\n\n    for (v = ngx_http_upstream_vars; v->name.len; v++) {\n        var = ngx_http_add_variable(cf, &v->name, v->flags);\n        if (var == NULL) {\n            return NGX_ERROR;\n        }\n\n        var->get_handler = v->get_handler;\n        var->data = v->data;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_addr_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    u_char                     *p;\n    size_t                      len;\n    ngx_uint_t                  i;\n    ngx_http_upstream_state_t  *state;\n\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    if (r->upstream_states == NULL || r->upstream_states->nelts == 0) {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    len = 0;\n    state = r->upstream_states->elts;\n\n    for (i = 0; i < r->upstream_states->nelts; i++) {\n        if (state[i].peer) {\n            len += state[i].peer->len + 2;\n\n        } else {\n            len += 3;\n        }\n    }\n\n    p = ngx_pnalloc(r->pool, len);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    v->data = p;\n\n    i = 0;\n\n    for ( ;; ) {\n        if (state[i].peer) {\n            p = ngx_cpymem(p, state[i].peer->data, state[i].peer->len);\n        }\n\n        if (++i == r->upstream_states->nelts) {\n            break;\n        }\n\n        if (state[i].peer) {\n            *p++ = ',';\n            *p++ = ' ';\n\n        } else {\n            *p++ = ' ';\n            *p++ = ':';\n            *p++ = ' ';\n\n            if (++i == r->upstream_states->nelts) {\n                break;\n            }\n\n            continue;\n        }\n    }\n\n    v->len = p - v->data;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_status_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    u_char                     *p;\n    size_t                      len;\n    ngx_uint_t                  i;\n    ngx_http_upstream_state_t  *state;\n\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    if (r->upstream_states == NULL || r->upstream_states->nelts == 0) {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    len = r->upstream_states->nelts * (3 + 2);\n\n    p = ngx_pnalloc(r->pool, len);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    v->data = p;\n\n    i = 0;\n    state = r->upstream_states->elts;\n\n    for ( ;; ) {\n        if (state[i].status) {\n            p = ngx_sprintf(p, \"%ui\", state[i].status);\n\n        } else {\n            *p++ = '-';\n        }\n\n        if (++i == r->upstream_states->nelts) {\n            break;\n        }\n\n        if (state[i].peer) {\n            *p++ = ',';\n            *p++ = ' ';\n\n        } else {\n            *p++ = ' ';\n            *p++ = ':';\n            *p++ = ' ';\n\n            if (++i == r->upstream_states->nelts) {\n                break;\n            }\n\n            continue;\n        }\n    }\n\n    v->len = p - v->data;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_response_time_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    u_char                     *p;\n    size_t                      len;\n    ngx_uint_t                  i;\n    ngx_msec_int_t              ms;\n    ngx_http_upstream_state_t  *state;\n\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    if (r->upstream_states == NULL || r->upstream_states->nelts == 0) {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    len = r->upstream_states->nelts * (NGX_TIME_T_LEN + 4 + 2);\n\n    p = ngx_pnalloc(r->pool, len);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    v->data = p;\n\n    i = 0;\n    state = r->upstream_states->elts;\n\n    for ( ;; ) {\n\n        if (data == 1) {\n            ms = state[i].header_time;\n\n        } else if (data == 2) {\n            ms = state[i].connect_time;\n\n        } else {\n            ms = state[i].response_time;\n        }\n\n        if (ms != -1) {\n            ms = ngx_max(ms, 0);\n            p = ngx_sprintf(p, \"%T.%03M\", (time_t) ms / 1000, ms % 1000);\n\n        } else {\n            *p++ = '-';\n        }\n\n        if (++i == r->upstream_states->nelts) {\n            break;\n        }\n\n        if (state[i].peer) {\n            *p++ = ',';\n            *p++ = ' ';\n\n        } else {\n            *p++ = ' ';\n            *p++ = ':';\n            *p++ = ' ';\n\n            if (++i == r->upstream_states->nelts) {\n                break;\n            }\n\n            continue;\n        }\n    }\n\n    v->len = p - v->data;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_response_length_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    u_char                     *p;\n    size_t                      len;\n    ngx_uint_t                  i;\n    ngx_http_upstream_state_t  *state;\n\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    if (r->upstream_states == NULL || r->upstream_states->nelts == 0) {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    len = r->upstream_states->nelts * (NGX_OFF_T_LEN + 2);\n\n    p = ngx_pnalloc(r->pool, len);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    v->data = p;\n\n    i = 0;\n    state = r->upstream_states->elts;\n\n    for ( ;; ) {\n\n        if (data == 1) {\n            p = ngx_sprintf(p, \"%O\", state[i].bytes_received);\n\n        } else if (data == 2) {\n            p = ngx_sprintf(p, \"%O\", state[i].bytes_sent);\n\n        } else {\n            p = ngx_sprintf(p, \"%O\", state[i].response_length);\n        }\n\n        if (++i == r->upstream_states->nelts) {\n            break;\n        }\n\n        if (state[i].peer) {\n            *p++ = ',';\n            *p++ = ' ';\n\n        } else {\n            *p++ = ' ';\n            *p++ = ':';\n            *p++ = ' ';\n\n            if (++i == r->upstream_states->nelts) {\n                break;\n            }\n\n            continue;\n        }\n    }\n\n    v->len = p - v->data;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_header_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    if (r->upstream == NULL) {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    return ngx_http_variable_unknown_header(r, v, (ngx_str_t *) data,\n                                         &r->upstream->headers_in.headers.part,\n                                         sizeof(\"upstream_http_\") - 1);\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_trailer_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    if (r->upstream == NULL) {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    return ngx_http_variable_unknown_header(r, v, (ngx_str_t *) data,\n                                        &r->upstream->headers_in.trailers.part,\n                                        sizeof(\"upstream_trailer_\") - 1);\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_cookie_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    ngx_str_t  *name = (ngx_str_t *) data;\n\n    ngx_str_t   cookie, s;\n\n    if (r->upstream == NULL) {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    s.len = name->len - (sizeof(\"upstream_cookie_\") - 1);\n    s.data = name->data + sizeof(\"upstream_cookie_\") - 1;\n\n    if (ngx_http_parse_set_cookie_lines(r, r->upstream->headers_in.set_cookie,\n                                        &s, &cookie)\n        == NULL)\n    {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    v->len = cookie.len;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = cookie.data;\n\n    return NGX_OK;\n}\n\n\n#if (NGX_HTTP_CACHE)\n\nstatic ngx_int_t\nngx_http_upstream_cache_status(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    ngx_uint_t  n;\n\n    if (r->upstream == NULL || r->upstream->cache_status == 0) {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    n = r->upstream->cache_status - 1;\n\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->len = ngx_http_cache_status[n].len;\n    v->data = ngx_http_cache_status[n].data;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_cache_last_modified(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    u_char  *p;\n\n    if (r->upstream == NULL\n        || !r->upstream->conf->cache_revalidate\n        || r->upstream->cache_status != NGX_HTTP_CACHE_EXPIRED\n        || r->cache->last_modified == -1)\n    {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    p = ngx_pnalloc(r->pool, sizeof(\"Mon, 28 Sep 1970 06:00:00 GMT\") - 1);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    v->len = ngx_http_time(p, r->cache->last_modified) - p;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = p;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_cache_etag(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    if (r->upstream == NULL\n        || !r->upstream->conf->cache_revalidate\n        || r->upstream->cache_status != NGX_HTTP_CACHE_EXPIRED\n        || r->cache->etag.len == 0)\n    {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->len = r->cache->etag.len;\n    v->data = r->cache->etag.data;\n\n    return NGX_OK;\n}\n\n#endif\n\n\nstatic char *\nngx_http_upstream(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy)\n{\n    char                          *rv;\n    void                          *mconf;\n    ngx_str_t                     *value;\n    ngx_url_t                      u;\n    ngx_uint_t                     m;\n    ngx_conf_t                     pcf;\n    ngx_http_module_t             *module;\n    ngx_http_conf_ctx_t           *ctx, *http_ctx;\n    ngx_http_upstream_srv_conf_t  *uscf;\n\n    ngx_memzero(&u, sizeof(ngx_url_t));\n\n    value = cf->args->elts;\n    u.host = value[1];\n    u.no_resolve = 1;\n    u.no_port = 1;\n\n    uscf = ngx_http_upstream_add(cf, &u, NGX_HTTP_UPSTREAM_CREATE\n                                         |NGX_HTTP_UPSTREAM_WEIGHT\n                                         |NGX_HTTP_UPSTREAM_MAX_CONNS\n                                         |NGX_HTTP_UPSTREAM_MAX_FAILS\n                                         |NGX_HTTP_UPSTREAM_FAIL_TIMEOUT\n                                         |NGX_HTTP_UPSTREAM_DOWN\n                                         |NGX_HTTP_UPSTREAM_BACKUP);\n    if (uscf == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n\n    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_http_conf_ctx_t));\n    if (ctx == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    http_ctx = cf->ctx;\n    ctx->main_conf = http_ctx->main_conf;\n\n    /* the upstream{}'s srv_conf */\n\n    ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);\n    if (ctx->srv_conf == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    ctx->srv_conf[ngx_http_upstream_module.ctx_index] = uscf;\n\n    uscf->srv_conf = ctx->srv_conf;\n\n\n    /* the upstream{}'s loc_conf */\n\n    ctx->loc_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_http_max_module);\n    if (ctx->loc_conf == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    for (m = 0; cf->cycle->modules[m]; m++) {\n        if (cf->cycle->modules[m]->type != NGX_HTTP_MODULE) {\n            continue;\n        }\n\n        module = cf->cycle->modules[m]->ctx;\n\n        if (module->create_srv_conf) {\n            mconf = module->create_srv_conf(cf);\n            if (mconf == NULL) {\n                return NGX_CONF_ERROR;\n            }\n\n            ctx->srv_conf[cf->cycle->modules[m]->ctx_index] = mconf;\n        }\n\n        if (module->create_loc_conf) {\n            mconf = module->create_loc_conf(cf);\n            if (mconf == NULL) {\n                return NGX_CONF_ERROR;\n            }\n\n            ctx->loc_conf[cf->cycle->modules[m]->ctx_index] = mconf;\n        }\n    }\n\n    uscf->servers = ngx_array_create(cf->pool, 4,\n                                     sizeof(ngx_http_upstream_server_t));\n    if (uscf->servers == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n\n    /* parse inside upstream{} */\n\n    pcf = *cf;\n    cf->ctx = ctx;\n    cf->cmd_type = NGX_HTTP_UPS_CONF;\n\n    rv = ngx_conf_parse(cf, NULL);\n\n    *cf = pcf;\n\n    if (rv != NGX_CONF_OK) {\n        return rv;\n    }\n\n    if (uscf->servers->nelts == 0) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"no servers are inside upstream\");\n        return NGX_CONF_ERROR;\n    }\n\n    return rv;\n}\n\n\nstatic char *\nngx_http_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_upstream_srv_conf_t  *uscf = conf;\n\n    time_t                       fail_timeout;\n    ngx_str_t                   *value, s;\n#if (T_NGX_HTTP_UPSTREAM_ID)\n    ngx_str_t                    id;\n#endif\n    ngx_url_t                    u;\n    ngx_int_t                    weight, max_conns, max_fails;\n    ngx_uint_t                   i;\n    ngx_http_upstream_server_t  *us;\n\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\n    value = cf->args->elts;\n\n    weight = 1;\n    max_conns = 0;\n    max_fails = 1;\n    fail_timeout = 10;\n#if (T_NGX_HTTP_UPSTREAM_ID)\n    ngx_str_null(&id);\n#endif\n\n    for (i = 2; i < cf->args->nelts; i++) {\n\n        if (ngx_strncmp(value[i].data, \"weight=\", 7) == 0) {\n\n            if (!(uscf->flags & NGX_HTTP_UPSTREAM_WEIGHT)) {\n                goto not_supported;\n            }\n\n            weight = ngx_atoi(&value[i].data[7], value[i].len - 7);\n\n            if (weight == NGX_ERROR || weight == 0) {\n                goto invalid;\n            }\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"max_conns=\", 10) == 0) {\n\n            if (!(uscf->flags & NGX_HTTP_UPSTREAM_MAX_CONNS)) {\n                goto not_supported;\n            }\n\n            max_conns = ngx_atoi(&value[i].data[10], value[i].len - 10);\n\n            if (max_conns == NGX_ERROR) {\n                goto invalid;\n            }\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"max_fails=\", 10) == 0) {\n\n            if (!(uscf->flags & NGX_HTTP_UPSTREAM_MAX_FAILS)) {\n                goto not_supported;\n            }\n\n            max_fails = ngx_atoi(&value[i].data[10], value[i].len - 10);\n\n            if (max_fails == NGX_ERROR) {\n                goto invalid;\n            }\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"fail_timeout=\", 13) == 0) {\n\n            if (!(uscf->flags & NGX_HTTP_UPSTREAM_FAIL_TIMEOUT)) {\n                goto not_supported;\n            }\n\n            s.len = value[i].len - 13;\n            s.data = &value[i].data[13];\n\n            fail_timeout = ngx_parse_time(&s, 1);\n\n            if (fail_timeout == (time_t) NGX_ERROR) {\n                goto invalid;\n            }\n\n            continue;\n        }\n\n        if (ngx_strcmp(value[i].data, \"backup\") == 0) {\n\n            if (!(uscf->flags & NGX_HTTP_UPSTREAM_BACKUP)) {\n                goto not_supported;\n            }\n\n            us->backup = 1;\n\n            continue;\n        }\n\n        if (ngx_strcmp(value[i].data, \"down\") == 0) {\n\n            if (!(uscf->flags & NGX_HTTP_UPSTREAM_DOWN)) {\n                goto not_supported;\n            }\n\n            us->down = 1;\n\n            continue;\n        }\n\n#if (T_NGX_HTTP_UPSTREAM_ID)\n        if (ngx_strncmp(value[i].data, \"id=\", 3) == 0) {\n\n            id.len = value[i].len - 3;\n            id.data = &value[i].data[3];\n\n            continue;\n        }\n#endif\n\n        goto invalid;\n    }\n\n    ngx_memzero(&u, sizeof(ngx_url_t));\n\n    u.url = value[1];\n    u.default_port = 80;\n\n    if (ngx_parse_url(cf->pool, &u) != NGX_OK) {\n        if (u.err) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"%s in upstream \\\"%V\\\"\", u.err, &u.url);\n        }\n\n        return NGX_CONF_ERROR;\n    }\n\n    us->name = u.url;\n    us->addrs = u.addrs;\n    us->naddrs = u.naddrs;\n    us->weight = weight;\n    us->max_conns = max_conns;\n    us->max_fails = max_fails;\n    us->fail_timeout = fail_timeout;\n#if (T_NGX_HTTP_UPSTREAM_ID)\n    us->id = id;\n#endif\n\n#if (T_NGX_HTTP_DYNAMIC_RESOLVE)\n    us->host = u.host;\n#endif\n\n    return NGX_CONF_OK;\n\ninvalid:\n\n    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                       \"invalid parameter \\\"%V\\\"\", &value[i]);\n\n    return NGX_CONF_ERROR;\n\nnot_supported:\n\n    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                       \"balancing method does not support parameter \\\"%V\\\"\",\n                       &value[i]);\n\n    return NGX_CONF_ERROR;\n}\n\n\nngx_http_upstream_srv_conf_t *\nngx_http_upstream_add(ngx_conf_t *cf, ngx_url_t *u, ngx_uint_t flags)\n{\n    ngx_uint_t                      i;\n\n#if (NGX_HTTP_UPSTREAM_RBTREE)\n\n    ngx_list_part_t                *part;\n\n#endif\n\n    ngx_http_upstream_server_t     *us;\n    ngx_http_upstream_srv_conf_t   *uscf, **uscfp;\n    ngx_http_upstream_main_conf_t  *umcf;\n\n    if (!(flags & NGX_HTTP_UPSTREAM_CREATE)) {\n\n        if (ngx_parse_url(cf->pool, u) != NGX_OK) {\n            if (u->err) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"%s in upstream \\\"%V\\\"\", u->err, &u->url);\n            }\n\n            return NULL;\n        }\n    }\n\n    umcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_upstream_module);\n\n    uscfp = umcf->upstreams.elts;\n\n#if (NGX_HTTP_UPSTREAM_RBTREE)\n\n    uscf = ngx_http_upstream_rbtree_lookup(umcf, &u->host);\n\n    if (uscf != NULL) {\n\n        if (flags & NGX_HTTP_UPSTREAM_CREATE) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"duplicate upstream \\\"%V\\\"\", &u->host);\n            return NULL;\n        }\n\n        if (!u->no_port) {\n            ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                               \"upstream \\\"%V\\\" may not have port %d\",\n                               &u->host, u->port);\n            return NULL;\n        }\n\n        if (uscf->port && u->port && uscf->port != u->port) {\n            goto not_found;\n        }\n\n        return uscf;\n    }\n\nnot_found:\n\n    part = &umcf->implicit_upstreams.part;\n    uscfp = 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            uscfp = part->elts;\n            i = 0;\n        }\n\n#else\n\n    for (i = 0; i < umcf->upstreams.nelts; i++) {\n\n#endif\n\n        if (uscfp[i]->host.len != u->host.len\n            || ngx_strncasecmp(uscfp[i]->host.data, u->host.data, u->host.len)\n               != 0)\n        {\n            continue;\n        }\n\n        if ((flags & NGX_HTTP_UPSTREAM_CREATE)\n             && (uscfp[i]->flags & NGX_HTTP_UPSTREAM_CREATE))\n        {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"duplicate upstream \\\"%V\\\"\", &u->host);\n            return NULL;\n        }\n\n        if ((uscfp[i]->flags & NGX_HTTP_UPSTREAM_CREATE) && !u->no_port) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"upstream \\\"%V\\\" may not have port %d\",\n                               &u->host, u->port);\n            return NULL;\n        }\n\n        if ((flags & NGX_HTTP_UPSTREAM_CREATE) && !uscfp[i]->no_port) {\n            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                          \"upstream \\\"%V\\\" may not have port %d in %s:%ui\",\n                          &u->host, uscfp[i]->port,\n                          uscfp[i]->file_name, uscfp[i]->line);\n            return NULL;\n        }\n\n        if (uscfp[i]->port && u->port\n            && uscfp[i]->port != u->port)\n        {\n            continue;\n        }\n\n        if (flags & NGX_HTTP_UPSTREAM_CREATE) {\n            uscfp[i]->flags = flags;\n            uscfp[i]->port = 0;\n\n#if (NGX_HTTP_UPSTREAM_RBTREE)\n\n            uscf = uscfp[i];\n\n            ngx_rbtree_insert(&umcf->rbtree, &uscfp[i]->node);\n#if (T_NGX_IMPROVED_LIST)\n            ngx_list_delete(&umcf->implicit_upstreams, &uscfp[i]);\n#endif\n\n            return uscf;\n\n#endif\n        }\n\n        return uscfp[i];\n    }\n\n    uscf = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_srv_conf_t));\n    if (uscf == NULL) {\n        return NULL;\n    }\n\n    uscf->flags = flags;\n    uscf->host = u->host;\n    uscf->file_name = cf->conf_file->file.name.data;\n    uscf->line = cf->conf_file->line;\n    uscf->port = u->port;\n    uscf->no_port = u->no_port;\n\n    if (u->naddrs == 1 && (u->port || u->family == AF_UNIX)) {\n        uscf->servers = ngx_array_create(cf->pool, 1,\n                                         sizeof(ngx_http_upstream_server_t));\n        if (uscf->servers == NULL) {\n            return NULL;\n        }\n\n        us = ngx_array_push(uscf->servers);\n        if (us == NULL) {\n            return NULL;\n        }\n\n        ngx_memzero(us, sizeof(ngx_http_upstream_server_t));\n\n        us->addrs = u->addrs;\n        us->naddrs = 1;\n    }\n\n    uscfp = ngx_array_push(&umcf->upstreams);\n    if (uscfp == NULL) {\n        return NULL;\n    }\n\n    *uscfp = uscf;\n\n#if (NGX_HTTP_UPSTREAM_RBTREE)\n\n    uscf->node.key = ngx_crc32_short(uscf->host.data, uscf->host.len);\n\n    if (flags & NGX_HTTP_UPSTREAM_CREATE) {\n        ngx_rbtree_insert(&umcf->rbtree, &uscf->node);\n    }\n    else {\n       uscfp = ngx_list_push(&umcf->implicit_upstreams);\n       if (uscfp == NULL) {\n           return NULL;\n       }\n\n       *uscfp = uscf;\n    }\n\n#endif\n\n    return uscf;\n}\n\n\n#if (NGX_HTTP_UPSTREAM_RBTREE)\n\nstatic void\nngx_http_upstream_rbtree_insert_value(ngx_rbtree_node_t *temp,\n    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)\n{\n    ngx_int_t                      n;\n    ngx_rbtree_node_t            **p;\n    ngx_http_upstream_srv_conf_t  *urn, *urnt;\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            urn = (ngx_http_upstream_srv_conf_t *) node;\n            urnt = (ngx_http_upstream_srv_conf_t *) temp;\n\n            n = ngx_memn2cmp(urn->host.data, urnt->host.data, urn->host.len,\n                             urnt->host.len);\n\n            p = n < 0 ? &temp->left : &temp->right;\n\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\nngx_http_upstream_srv_conf_t *\nngx_http_upstream_rbtree_lookup(ngx_http_upstream_main_conf_t *umcf,\n    ngx_str_t *host)\n{\n    uint32_t                        hash;\n    ngx_int_t                       rc;\n    ngx_rbtree_node_t              *node, *sentinel;\n    ngx_http_upstream_srv_conf_t   *uscf;\n\n    node = umcf->rbtree.root;\n    sentinel = umcf->rbtree.sentinel;\n\n    hash = ngx_crc32_short(host->data, host->len);\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        /* node_key == node->key */\n\n        uscf = (ngx_http_upstream_srv_conf_t *) node;\n\n        rc = ngx_memn2cmp(host->data, uscf->host.data,\n                          host->len, uscf->host.len);\n\n        if (rc == 0) {\n            return uscf;\n        }\n\n        node = (rc < 0) ? node->left : node->right;\n    }\n\n    /* not found */\n\n    return NULL;\n}\n\n#endif\n\nchar *\nngx_http_upstream_bind_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n    char  *p = conf;\n\n    ngx_int_t                           rc;\n    ngx_str_t                          *value;\n    ngx_http_complex_value_t            cv;\n    ngx_http_upstream_local_t         **plocal, *local;\n    ngx_http_compile_complex_value_t    ccv;\n\n    plocal = (ngx_http_upstream_local_t **) (p + cmd->offset);\n\n    if (*plocal != NGX_CONF_UNSET_PTR) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    if (cf->args->nelts == 2 && ngx_strcmp(value[1].data, \"off\") == 0) {\n        *plocal = NULL;\n        return NGX_CONF_OK;\n    }\n\n    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n\n    ccv.cf = cf;\n    ccv.value = &value[1];\n    ccv.complex_value = &cv;\n\n    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    local = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_local_t));\n    if (local == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    *plocal = local;\n\n    if (cv.lengths) {\n        local->value = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));\n        if (local->value == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        *local->value = cv;\n\n    } else {\n        local->addr = ngx_palloc(cf->pool, sizeof(ngx_addr_t));\n        if (local->addr == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        rc = ngx_parse_addr_port(cf->pool, local->addr, value[1].data,\n                                 value[1].len);\n\n        switch (rc) {\n        case NGX_OK:\n            local->addr->name = value[1];\n            break;\n\n        case NGX_DECLINED:\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"invalid address \\\"%V\\\"\", &value[1]);\n            /* fall through */\n\n        default:\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    if (cf->args->nelts > 2) {\n        if (ngx_strcmp(value[2].data, \"transparent\") == 0) {\n#if (NGX_HAVE_TRANSPARENT_PROXY)\n            ngx_core_conf_t  *ccf;\n\n            ccf = (ngx_core_conf_t *) ngx_get_conf(cf->cycle->conf_ctx,\n                                                   ngx_core_module);\n\n            ccf->transparent = 1;\n            local->transparent = 1;\n#else\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"transparent proxying is not supported \"\n                               \"on this platform, ignored\");\n#endif\n        } else {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"invalid parameter \\\"%V\\\"\", &value[2]);\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_set_local(ngx_http_request_t *r, ngx_http_upstream_t *u,\n    ngx_http_upstream_local_t *local)\n{\n    ngx_int_t    rc;\n    ngx_str_t    val;\n    ngx_addr_t  *addr;\n\n    if (local == NULL) {\n        u->peer.local = NULL;\n        return NGX_OK;\n    }\n\n#if (NGX_HAVE_TRANSPARENT_PROXY)\n    u->peer.transparent = local->transparent;\n#endif\n\n    if (local->value == NULL) {\n        u->peer.local = local->addr;\n        return NGX_OK;\n    }\n\n    if (ngx_http_complex_value(r, local->value, &val) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    if (val.len == 0) {\n        return NGX_OK;\n    }\n\n    addr = ngx_palloc(r->pool, sizeof(ngx_addr_t));\n    if (addr == NULL) {\n        return NGX_ERROR;\n    }\n\n    rc = ngx_parse_addr_port(r->pool, addr, val.data, val.len);\n    if (rc == NGX_ERROR) {\n        return NGX_ERROR;\n    }\n\n    if (rc != NGX_OK) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"invalid local address \\\"%V\\\"\", &val);\n        return NGX_OK;\n    }\n\n    addr->name = val;\n    u->peer.local = addr;\n\n    return NGX_OK;\n}\n\n\nchar *\nngx_http_upstream_param_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n    char  *p = conf;\n\n    ngx_str_t                   *value;\n    ngx_array_t                **a;\n    ngx_http_upstream_param_t   *param;\n\n    a = (ngx_array_t **) (p + cmd->offset);\n\n    if (*a == NULL) {\n        *a = ngx_array_create(cf->pool, 4, sizeof(ngx_http_upstream_param_t));\n        if (*a == NULL) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    param = ngx_array_push(*a);\n    if (param == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    value = cf->args->elts;\n\n    param->key = value[1];\n    param->value = value[2];\n    param->skip_empty = 0;\n\n    if (cf->args->nelts == 4) {\n        if (ngx_strcmp(value[3].data, \"if_not_empty\") != 0) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"invalid parameter \\\"%V\\\"\", &value[3]);\n            return NGX_CONF_ERROR;\n        }\n\n        param->skip_empty = 1;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nngx_int_t\nngx_http_upstream_hide_headers_hash(ngx_conf_t *cf,\n    ngx_http_upstream_conf_t *conf, ngx_http_upstream_conf_t *prev,\n    ngx_str_t *default_hide_headers, ngx_hash_init_t *hash)\n{\n    ngx_str_t       *h;\n    ngx_uint_t       i, j;\n    ngx_array_t      hide_headers;\n    ngx_hash_key_t  *hk;\n\n    if (conf->hide_headers == NGX_CONF_UNSET_PTR\n        && conf->pass_headers == NGX_CONF_UNSET_PTR)\n    {\n        conf->hide_headers = prev->hide_headers;\n        conf->pass_headers = prev->pass_headers;\n\n        conf->hide_headers_hash = prev->hide_headers_hash;\n\n        if (conf->hide_headers_hash.buckets) {\n            return NGX_OK;\n        }\n\n    } else {\n        if (conf->hide_headers == NGX_CONF_UNSET_PTR) {\n            conf->hide_headers = prev->hide_headers;\n        }\n\n        if (conf->pass_headers == NGX_CONF_UNSET_PTR) {\n            conf->pass_headers = prev->pass_headers;\n        }\n    }\n\n    if (ngx_array_init(&hide_headers, cf->temp_pool, 4, sizeof(ngx_hash_key_t))\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    for (h = default_hide_headers; h->len; h++) {\n        hk = ngx_array_push(&hide_headers);\n        if (hk == NULL) {\n            return NGX_ERROR;\n        }\n\n        hk->key = *h;\n        hk->key_hash = ngx_hash_key_lc(h->data, h->len);\n        hk->value = (void *) 1;\n    }\n\n    if (conf->hide_headers != NGX_CONF_UNSET_PTR) {\n\n        h = conf->hide_headers->elts;\n\n        for (i = 0; i < conf->hide_headers->nelts; i++) {\n\n            hk = hide_headers.elts;\n\n            for (j = 0; j < hide_headers.nelts; j++) {\n                if (ngx_strcasecmp(h[i].data, hk[j].key.data) == 0) {\n                    goto exist;\n                }\n            }\n\n            hk = ngx_array_push(&hide_headers);\n            if (hk == NULL) {\n                return NGX_ERROR;\n            }\n\n            hk->key = h[i];\n            hk->key_hash = ngx_hash_key_lc(h[i].data, h[i].len);\n            hk->value = (void *) 1;\n\n        exist:\n\n            continue;\n        }\n    }\n\n    if (conf->pass_headers != NGX_CONF_UNSET_PTR) {\n\n        h = conf->pass_headers->elts;\n        hk = hide_headers.elts;\n\n        for (i = 0; i < conf->pass_headers->nelts; i++) {\n            for (j = 0; j < hide_headers.nelts; j++) {\n\n                if (hk[j].key.data == NULL) {\n                    continue;\n                }\n\n                if (ngx_strcasecmp(h[i].data, hk[j].key.data) == 0) {\n                    hk[j].key.data = NULL;\n                    break;\n                }\n            }\n        }\n    }\n\n    hash->hash = &conf->hide_headers_hash;\n    hash->key = ngx_hash_key_lc;\n    hash->pool = cf->pool;\n    hash->temp_pool = NULL;\n\n    if (ngx_hash_init(hash, hide_headers.elts, hide_headers.nelts) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    /*\n     * special handling to preserve conf->hide_headers_hash\n     * in the \"http\" section to inherit it to all servers\n     */\n\n    if (prev->hide_headers_hash.buckets == NULL\n        && conf->hide_headers == prev->hide_headers\n        && conf->pass_headers == prev->pass_headers)\n    {\n        prev->hide_headers_hash = conf->hide_headers_hash;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void *\nngx_http_upstream_create_main_conf(ngx_conf_t *cf)\n{\n    ngx_http_upstream_main_conf_t  *umcf;\n\n    umcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_main_conf_t));\n    if (umcf == NULL) {\n        return NULL;\n    }\n\n    if (ngx_array_init(&umcf->upstreams, cf->pool, 4,\n                       sizeof(ngx_http_upstream_srv_conf_t *))\n        != NGX_OK)\n    {\n        return NULL;\n    }\n\n#if (NGX_HTTP_UPSTREAM_RBTREE)\n\n    if (ngx_list_init(&umcf->implicit_upstreams, cf->pool, 4,\n                       sizeof(ngx_http_upstream_srv_conf_t *))\n        != NGX_OK)\n    {\n        return NULL;\n    }\n\n    ngx_rbtree_init(&umcf->rbtree, &umcf->sentinel,\n                    ngx_http_upstream_rbtree_insert_value);\n\n#endif\n\n    return umcf;\n}\n\n\nstatic char *\nngx_http_upstream_init_main_conf(ngx_conf_t *cf, void *conf)\n{\n    ngx_http_upstream_main_conf_t  *umcf = conf;\n\n    ngx_uint_t                      i;\n    ngx_array_t                     headers_in;\n    ngx_hash_key_t                 *hk;\n    ngx_hash_init_t                 hash;\n    ngx_http_upstream_init_pt       init;\n    ngx_http_upstream_header_t     *header;\n    ngx_http_upstream_srv_conf_t  **uscfp;\n\n    uscfp = umcf->upstreams.elts;\n\n    for (i = 0; i < umcf->upstreams.nelts; i++) {\n\n        init = uscfp[i]->peer.init_upstream ? uscfp[i]->peer.init_upstream:\n                                            ngx_http_upstream_init_round_robin;\n\n        if (init(cf, uscfp[i]) != NGX_OK) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n\n    /* upstream_headers_in_hash */\n\n    if (ngx_array_init(&headers_in, cf->temp_pool, 32, sizeof(ngx_hash_key_t))\n        != NGX_OK)\n    {\n        return NGX_CONF_ERROR;\n    }\n\n    for (header = ngx_http_upstream_headers_in; header->name.len; header++) {\n        hk = ngx_array_push(&headers_in);\n        if (hk == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        hk->key = header->name;\n        hk->key_hash = ngx_hash_key_lc(header->name.data, header->name.len);\n        hk->value = header;\n    }\n\n    hash.hash = &umcf->headers_in_hash;\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 = \"upstream_headers_in_hash\";\n    hash.pool = cf->pool;\n    hash.temp_pool = NULL;\n\n    if (ngx_hash_init(&hash, headers_in.elts, headers_in.nelts) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\n#if (T_NGX_HTTP_UPSTREAM_RANDOM)\nstatic ngx_int_t\nngx_http_upstream_init_process(ngx_cycle_t *cycle)\n{\n    u_char                            buf[16];\n    ngx_md5_t                         md5;\n    ngx_uint_t                        i, seed;\n    ngx_http_upstream_rr_peers_t     *peers, *backup;\n    ngx_http_upstream_srv_conf_t    **uscfp;\n    ngx_http_upstream_main_conf_t    *umcf;\n\n    umcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_upstream_module);\n    if (umcf == NULL) {\n        return NGX_OK;\n    }\n\n    ngx_md5_init(&md5);\n    ngx_md5_update(&md5, cycle->hostname.data, cycle->hostname.len);\n    ngx_md5_final(buf, &md5);\n\n    seed = (ngx_uint_t) buf[0] + ((ngx_uint_t) buf[1] << 8)\n           + ((ngx_uint_t) ngx_pid << 16);\n\n    srandom(seed);\n\n    uscfp = umcf->upstreams.elts;\n\n    for (i = 0; i < umcf->upstreams.nelts; i++) {\n\tif (!(uscfp[i]->flags & T_NGX_HTTP_UPSTREAM_RANDOM_FLAG)) {\n\t    continue;\n\t}\n\n        peers = uscfp[i]->peer.data;\n\n        if (peers == NULL) {\n            continue;\n        }\n\n        peers->init_number = ngx_random() % peers->number;\n\n        backup = peers->next;\n        if (backup) {\n            backup->init_number = ngx_random() % backup->number;\n        }\n    }\n\n    return NGX_OK;\n}\n#endif\n"
  },
  {
    "path": "src/http/ngx_http_upstream.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_HTTP_UPSTREAM_H_INCLUDED_\n#define _NGX_HTTP_UPSTREAM_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n#include <ngx_event_connect.h>\n#include <ngx_event_pipe.h>\n#include <ngx_http.h>\n\n\n#define NGX_HTTP_UPSTREAM_FT_ERROR           0x00000002\n#define NGX_HTTP_UPSTREAM_FT_TIMEOUT         0x00000004\n#define NGX_HTTP_UPSTREAM_FT_INVALID_HEADER  0x00000008\n#define NGX_HTTP_UPSTREAM_FT_HTTP_500        0x00000010\n#define NGX_HTTP_UPSTREAM_FT_HTTP_502        0x00000020\n#define NGX_HTTP_UPSTREAM_FT_HTTP_503        0x00000040\n#define NGX_HTTP_UPSTREAM_FT_HTTP_504        0x00000080\n#define NGX_HTTP_UPSTREAM_FT_HTTP_403        0x00000100\n#define NGX_HTTP_UPSTREAM_FT_HTTP_404        0x00000200\n#define NGX_HTTP_UPSTREAM_FT_HTTP_429        0x00000400\n#define NGX_HTTP_UPSTREAM_FT_UPDATING        0x00000800\n#define NGX_HTTP_UPSTREAM_FT_BUSY_LOCK       0x00001000\n#define NGX_HTTP_UPSTREAM_FT_MAX_WAITING     0x00002000\n#define NGX_HTTP_UPSTREAM_FT_NON_IDEMPOTENT  0x00004000\n#define NGX_HTTP_UPSTREAM_FT_NOLIVE          0x40000000\n#define NGX_HTTP_UPSTREAM_FT_OFF             0x80000000\n\n#define NGX_HTTP_UPSTREAM_FT_STATUS          (NGX_HTTP_UPSTREAM_FT_HTTP_500  \\\n                                             |NGX_HTTP_UPSTREAM_FT_HTTP_502  \\\n                                             |NGX_HTTP_UPSTREAM_FT_HTTP_503  \\\n                                             |NGX_HTTP_UPSTREAM_FT_HTTP_504  \\\n                                             |NGX_HTTP_UPSTREAM_FT_HTTP_403  \\\n                                             |NGX_HTTP_UPSTREAM_FT_HTTP_404  \\\n                                             |NGX_HTTP_UPSTREAM_FT_HTTP_429)\n\n#define NGX_HTTP_UPSTREAM_INVALID_HEADER     40\n\n\n#define NGX_HTTP_UPSTREAM_IGN_XA_REDIRECT    0x00000002\n#define NGX_HTTP_UPSTREAM_IGN_XA_EXPIRES     0x00000004\n#define NGX_HTTP_UPSTREAM_IGN_EXPIRES        0x00000008\n#define NGX_HTTP_UPSTREAM_IGN_CACHE_CONTROL  0x00000010\n#define NGX_HTTP_UPSTREAM_IGN_SET_COOKIE     0x00000020\n#define NGX_HTTP_UPSTREAM_IGN_XA_LIMIT_RATE  0x00000040\n#define NGX_HTTP_UPSTREAM_IGN_XA_BUFFERING   0x00000080\n#define NGX_HTTP_UPSTREAM_IGN_XA_CHARSET     0x00000100\n#define NGX_HTTP_UPSTREAM_IGN_VARY           0x00000200\n\n\ntypedef struct {\n    ngx_uint_t                       status;\n    ngx_msec_t                       response_time;\n    ngx_msec_t                       connect_time;\n    ngx_msec_t                       header_time;\n    ngx_msec_t                       queue_time;\n    off_t                            response_length;\n    off_t                            bytes_received;\n    off_t                            bytes_sent;\n\n    ngx_str_t                       *peer;\n} ngx_http_upstream_state_t;\n\n\ntypedef struct {\n    ngx_hash_t                       headers_in_hash;\n    ngx_array_t                      upstreams;\n                                             /* ngx_http_upstream_srv_conf_t */\n#if (NGX_HTTP_UPSTREAM_RBTREE)\n\n    ngx_list_t                       implicit_upstreams;\n                                            /* ngx_http_upstream_srv_conf_t */\n\n    ngx_rbtree_t                     rbtree;\n    ngx_rbtree_node_t                sentinel;\n\n#endif\n} ngx_http_upstream_main_conf_t;\n\ntypedef struct ngx_http_upstream_srv_conf_s  ngx_http_upstream_srv_conf_t;\n\ntypedef ngx_int_t (*ngx_http_upstream_init_pt)(ngx_conf_t *cf,\n    ngx_http_upstream_srv_conf_t *us);\ntypedef ngx_int_t (*ngx_http_upstream_init_peer_pt)(ngx_http_request_t *r,\n    ngx_http_upstream_srv_conf_t *us);\n\n\ntypedef struct {\n    ngx_http_upstream_init_pt        init_upstream;\n    ngx_http_upstream_init_peer_pt   init;\n    void                            *data;\n} ngx_http_upstream_peer_t;\n\n\ntypedef struct {\n    ngx_str_t                        name;\n    ngx_addr_t                      *addrs;\n    ngx_uint_t                       naddrs;\n    ngx_uint_t                       weight;\n    ngx_uint_t                       max_conns;\n    ngx_uint_t                       max_fails;\n    time_t                           fail_timeout;\n    ngx_msec_t                       slow_start;\n    ngx_uint_t                       down;\n#if (T_NGX_HTTP_UPSTREAM_ID)\n    ngx_str_t                        id;\n#endif\n\n#if (T_NGX_HTTP_DYNAMIC_RESOLVE)\n    ngx_str_t                        host;\n#endif\n\n    unsigned                         backup:1;\n\n    NGX_COMPAT_BEGIN(6)\n    NGX_COMPAT_END\n} ngx_http_upstream_server_t;\n\n\n#define NGX_HTTP_UPSTREAM_CREATE        0x0001\n#define NGX_HTTP_UPSTREAM_WEIGHT        0x0002\n#define NGX_HTTP_UPSTREAM_MAX_FAILS     0x0004\n#define NGX_HTTP_UPSTREAM_FAIL_TIMEOUT  0x0008\n#define NGX_HTTP_UPSTREAM_DOWN          0x0010\n#define NGX_HTTP_UPSTREAM_BACKUP        0x0020\n#define NGX_HTTP_UPSTREAM_MAX_CONNS     0x0100\n#define NGX_HTTP_UPSTREAM_ID            0x0040\n#if (T_NGX_HTTP_UPSTREAM_RANDOM)\n#define T_NGX_HTTP_UPSTREAM_RANDOM_FLAG 0x0200\n#endif\n\n\nstruct ngx_http_upstream_srv_conf_s {\n#if (NGX_HTTP_UPSTREAM_RBTREE)\n    ngx_rbtree_node_t                node;\n#endif\n    ngx_http_upstream_peer_t         peer;\n    void                           **srv_conf;\n\n    ngx_array_t                     *servers;  /* ngx_http_upstream_server_t */\n\n#if (NGX_HTTP_UPSTREAM_HASH)\n    ngx_array_t                     *values;\n    ngx_array_t                     *lengths;\n    ngx_uint_t                       retries;\n#endif\n\n    ngx_uint_t                       flags;\n    ngx_str_t                        host;\n    u_char                          *file_name;\n    ngx_uint_t                       line;\n    in_port_t                        port;\n    ngx_uint_t                       no_port;  /* unsigned no_port:1 */\n\n#if (NGX_HTTP_UPSTREAM_ZONE)\n    ngx_shm_zone_t                  *shm_zone;\n#endif\n};\n\n\ntypedef struct {\n    ngx_addr_t                      *addr;\n    ngx_http_complex_value_t        *value;\n#if (NGX_HAVE_TRANSPARENT_PROXY)\n    ngx_uint_t                       transparent; /* unsigned  transparent:1; */\n#endif\n} ngx_http_upstream_local_t;\n\n\ntypedef struct {\n    ngx_http_upstream_srv_conf_t    *upstream;\n\n    ngx_msec_t                       connect_timeout;\n    ngx_msec_t                       send_timeout;\n    ngx_msec_t                       read_timeout;\n    ngx_msec_t                       next_upstream_timeout;\n\n    size_t                           send_lowat;\n    size_t                           buffer_size;\n    size_t                           limit_rate;\n\n    size_t                           busy_buffers_size;\n    size_t                           max_temp_file_size;\n    size_t                           temp_file_write_size;\n\n    size_t                           busy_buffers_size_conf;\n    size_t                           max_temp_file_size_conf;\n    size_t                           temp_file_write_size_conf;\n\n    ngx_bufs_t                       bufs;\n\n    ngx_uint_t                       ignore_headers;\n    ngx_uint_t                       next_upstream;\n    ngx_uint_t                       store_access;\n    ngx_uint_t                       next_upstream_tries;\n    ngx_flag_t                       buffering;\n    ngx_flag_t                       request_buffering;\n    ngx_flag_t                       pass_request_headers;\n    ngx_flag_t                       pass_request_body;\n\n    ngx_flag_t                       ignore_client_abort;\n    ngx_flag_t                       intercept_errors;\n    ngx_flag_t                       cyclic_temp_file;\n    ngx_flag_t                       force_ranges;\n\n    ngx_path_t                      *temp_path;\n\n    ngx_hash_t                       hide_headers_hash;\n    ngx_array_t                     *hide_headers;\n    ngx_array_t                     *pass_headers;\n\n    ngx_http_upstream_local_t       *local;\n    ngx_flag_t                       socket_keepalive;\n\n#if (NGX_HTTP_CACHE)\n    ngx_shm_zone_t                  *cache_zone;\n    ngx_http_complex_value_t        *cache_value;\n\n    ngx_uint_t                       cache_min_uses;\n    ngx_uint_t                       cache_use_stale;\n    ngx_uint_t                       cache_methods;\n\n    off_t                            cache_max_range_offset;\n\n    ngx_flag_t                       cache_lock;\n    ngx_msec_t                       cache_lock_timeout;\n    ngx_msec_t                       cache_lock_age;\n\n    ngx_flag_t                       cache_revalidate;\n    ngx_flag_t                       cache_convert_head;\n    ngx_flag_t                       cache_background_update;\n\n    ngx_array_t                     *cache_valid;\n    ngx_array_t                     *cache_bypass;\n    ngx_array_t                     *cache_purge;\n    ngx_array_t                     *no_cache;\n#endif\n\n    ngx_array_t                     *store_lengths;\n    ngx_array_t                     *store_values;\n\n#if (NGX_HTTP_CACHE)\n    signed                           cache:2;\n#endif\n    signed                           store:2;\n    unsigned                         intercept_404:1;\n    unsigned                         change_buffering:1;\n    unsigned                         pass_trailers:1;\n    unsigned                         preserve_output:1;\n\n#if (NGX_HTTP_SSL || NGX_COMPAT)\n    ngx_ssl_t                       *ssl;\n    ngx_flag_t                       ssl_session_reuse;\n\n    ngx_http_complex_value_t        *ssl_name;\n    ngx_flag_t                       ssl_server_name;\n    ngx_flag_t                       ssl_verify;\n\n    ngx_http_complex_value_t        *ssl_certificate;\n    ngx_http_complex_value_t        *ssl_certificate_key;\n    ngx_array_t                     *ssl_passwords;\n\n#if (T_NGX_SSL_NTLS)\n    ngx_str_t                        enc_certificate;\n    ngx_str_t                        enc_certificate_key;\n    ngx_str_t                        sign_certificate;\n    ngx_str_t                        sign_certificate_key;\n#endif\n#endif\n\n#if (T_NGX_SSL_NTLS)\n    ngx_str_t                        ssl_ciphers;\n    const SSL_METHOD                *tls_method;\n    ngx_http_complex_value_t        *enable_ntls;\n#endif\n\n    ngx_str_t                        module;\n\n    NGX_COMPAT_BEGIN(2)\n    NGX_COMPAT_END\n} ngx_http_upstream_conf_t;\n\n\ntypedef struct {\n    ngx_str_t                        name;\n    ngx_http_header_handler_pt       handler;\n    ngx_uint_t                       offset;\n    ngx_http_header_handler_pt       copy_handler;\n    ngx_uint_t                       conf;\n    ngx_uint_t                       redirect;  /* unsigned   redirect:1; */\n} ngx_http_upstream_header_t;\n\n\ntypedef struct {\n    ngx_list_t                       headers;\n    ngx_list_t                       trailers;\n\n    ngx_uint_t                       status_n;\n    ngx_str_t                        status_line;\n\n    ngx_table_elt_t                 *status;\n    ngx_table_elt_t                 *date;\n    ngx_table_elt_t                 *server;\n    ngx_table_elt_t                 *connection;\n\n    ngx_table_elt_t                 *expires;\n    ngx_table_elt_t                 *etag;\n    ngx_table_elt_t                 *x_accel_expires;\n    ngx_table_elt_t                 *x_accel_redirect;\n    ngx_table_elt_t                 *x_accel_limit_rate;\n\n    ngx_table_elt_t                 *content_type;\n    ngx_table_elt_t                 *content_length;\n\n    ngx_table_elt_t                 *last_modified;\n    ngx_table_elt_t                 *location;\n    ngx_table_elt_t                 *refresh;\n    ngx_table_elt_t                 *www_authenticate;\n    ngx_table_elt_t                 *transfer_encoding;\n    ngx_table_elt_t                 *vary;\n\n    ngx_table_elt_t                 *cache_control;\n    ngx_table_elt_t                 *set_cookie;\n\n    off_t                            content_length_n;\n    time_t                           last_modified_time;\n\n    unsigned                         connection_close:1;\n    unsigned                         chunked:1;\n    unsigned                         no_cache:1;\n    unsigned                         expired:1;\n} ngx_http_upstream_headers_in_t;\n\n\ntypedef struct {\n    ngx_str_t                        host;\n    in_port_t                        port;\n    ngx_uint_t                       no_port; /* unsigned no_port:1 */\n\n    ngx_uint_t                       naddrs;\n    ngx_resolver_addr_t             *addrs;\n\n    struct sockaddr                 *sockaddr;\n    socklen_t                        socklen;\n    ngx_str_t                        name;\n\n    ngx_resolver_ctx_t              *ctx;\n} ngx_http_upstream_resolved_t;\n\n\ntypedef void (*ngx_http_upstream_handler_pt)(ngx_http_request_t *r,\n    ngx_http_upstream_t *u);\n\n\nstruct ngx_http_upstream_s {\n    ngx_http_upstream_handler_pt     read_event_handler;\n    ngx_http_upstream_handler_pt     write_event_handler;\n\n    ngx_peer_connection_t            peer;\n\n    ngx_event_pipe_t                *pipe;\n\n    ngx_chain_t                     *request_bufs;\n\n    ngx_output_chain_ctx_t           output;\n    ngx_chain_writer_ctx_t           writer;\n\n    ngx_http_upstream_conf_t        *conf;\n    ngx_http_upstream_srv_conf_t    *upstream;\n#if (NGX_HTTP_CACHE)\n    ngx_array_t                     *caches;\n#endif\n\n    ngx_http_upstream_headers_in_t   headers_in;\n\n    ngx_http_upstream_resolved_t    *resolved;\n#if (T_NGX_HTTP_DYNAMIC_RESOLVE)\n    ngx_resolver_ctx_t              *dyn_resolve_ctx;\n#endif\n\n    ngx_buf_t                        from_client;\n\n    ngx_buf_t                        buffer;\n    off_t                            length;\n\n    ngx_chain_t                     *out_bufs;\n    ngx_chain_t                     *busy_bufs;\n    ngx_chain_t                     *free_bufs;\n\n    ngx_int_t                      (*input_filter_init)(void *data);\n    ngx_int_t                      (*input_filter)(void *data, ssize_t bytes);\n    void                            *input_filter_ctx;\n\n#if (NGX_HTTP_CACHE)\n    ngx_int_t                      (*create_key)(ngx_http_request_t *r);\n#endif\n    ngx_int_t                      (*create_request)(ngx_http_request_t *r);\n    ngx_int_t                      (*reinit_request)(ngx_http_request_t *r);\n    ngx_int_t                      (*process_header)(ngx_http_request_t *r);\n    void                           (*abort_request)(ngx_http_request_t *r);\n    void                           (*finalize_request)(ngx_http_request_t *r,\n                                         ngx_int_t rc);\n    ngx_int_t                      (*rewrite_redirect)(ngx_http_request_t *r,\n                                         ngx_table_elt_t *h, size_t prefix);\n    ngx_int_t                      (*rewrite_cookie)(ngx_http_request_t *r,\n                                         ngx_table_elt_t *h);\n\n    ngx_msec_t                       start_time;\n\n    ngx_http_upstream_state_t       *state;\n\n    ngx_str_t                        method;\n    ngx_str_t                        schema;\n    ngx_str_t                        uri;\n\n#if (NGX_HTTP_SSL || NGX_COMPAT)\n    ngx_str_t                        ssl_name;\n#endif\n\n    ngx_http_cleanup_pt             *cleanup;\n\n    unsigned                         store:1;\n    unsigned                         cacheable:1;\n    unsigned                         accel:1;\n    unsigned                         ssl:1;\n#if (NGX_HTTP_CACHE)\n    unsigned                         cache_status:3;\n#endif\n\n    unsigned                         buffering:1;\n    unsigned                         keepalive:1;\n    unsigned                         upgrade:1;\n    unsigned                         error:1;\n\n    unsigned                         request_sent:1;\n    unsigned                         request_body_sent:1;\n    unsigned                         request_body_blocked:1;\n    unsigned                         header_sent:1;\n\n#if (T_NGX_MULTI_UPSTREAM)\n    unsigned                         multi:1;\n    void                            *multi_init;\n    ngx_pool_t                      *send_pool;\n    ngx_flag_t                       multi_mode;\n#endif\n\n};\n\n\ntypedef struct {\n    ngx_uint_t                      status;\n    ngx_uint_t                      mask;\n} ngx_http_upstream_next_t;\n\n\ntypedef struct {\n    ngx_str_t   key;\n    ngx_str_t   value;\n    ngx_uint_t  skip_empty;\n} ngx_http_upstream_param_t;\n\n\nngx_int_t ngx_http_upstream_create(ngx_http_request_t *r);\nvoid ngx_http_upstream_init(ngx_http_request_t *r);\nngx_int_t ngx_http_upstream_non_buffered_filter_init(void *data);\nngx_int_t ngx_http_upstream_non_buffered_filter(void *data, ssize_t bytes);\nngx_http_upstream_srv_conf_t *ngx_http_upstream_add(ngx_conf_t *cf,\n    ngx_url_t *u, ngx_uint_t flags);\nchar *ngx_http_upstream_bind_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nchar *ngx_http_upstream_param_set_slot(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nngx_int_t ngx_http_upstream_hide_headers_hash(ngx_conf_t *cf,\n    ngx_http_upstream_conf_t *conf, ngx_http_upstream_conf_t *prev,\n    ngx_str_t *default_hide_headers, ngx_hash_init_t *hash);\n\n\n#define ngx_http_conf_upstream_srv_conf(uscf, module)                         \\\n    uscf->srv_conf[module.ctx_index]\n\n#if (NGX_HTTP_UPSTREAM_CHECK)\n\nngx_uint_t ngx_http_upstream_check_add_peer(ngx_conf_t *cf,\n    ngx_http_upstream_srv_conf_t *us, ngx_addr_t *peer);\n\nngx_uint_t ngx_http_upstream_check_peer_down(ngx_uint_t index);\nngx_uint_t ngx_http_upstream_check_upstream_down(ngx_str_t *upstream);\n\nvoid ngx_http_upstream_check_get_peer(ngx_uint_t index);\nvoid ngx_http_upstream_check_free_peer(ngx_uint_t index);\n\nngx_uint_t ngx_http_upstream_check_add_dynamic_peer(ngx_pool_t *pool,\n    ngx_http_upstream_srv_conf_t *us, ngx_addr_t *peer);\nvoid ngx_http_upstream_check_delete_dynamic_peer(ngx_str_t *name,\n     ngx_addr_t *peer_addr);\n\n#endif\n\n#if (NGX_HTTP_UPSTREAM_RBTREE)\nngx_http_upstream_srv_conf_t *\nngx_http_upstream_rbtree_lookup(ngx_http_upstream_main_conf_t *umcf,\n    ngx_str_t *host);\n#endif\n\nextern ngx_module_t        ngx_http_upstream_module;\nextern ngx_conf_bitmask_t  ngx_http_upstream_cache_method_mask[];\nextern ngx_conf_bitmask_t  ngx_http_upstream_ignore_headers_masks[];\n\n\n#endif /* _NGX_HTTP_UPSTREAM_H_INCLUDED_ */\n"
  },
  {
    "path": "src/http/ngx_http_upstream_round_robin.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n#if (NGX_HTTP_UPSTREAM_CHECK)\n#include \"ngx_http_upstream_check_module.h\"\n#endif\n\n#define ngx_http_upstream_tries(p) ((p)->tries                                \\\n                                    + ((p)->next ? (p)->next->tries : 0))\n\n\nstatic ngx_http_upstream_rr_peer_t *ngx_http_upstream_get_peer(\n    ngx_http_upstream_rr_peer_data_t *rrp);\n\n#if (NGX_HTTP_SSL)\n\nstatic ngx_int_t ngx_http_upstream_empty_set_session(ngx_peer_connection_t *pc,\n    void *data);\nstatic void ngx_http_upstream_empty_save_session(ngx_peer_connection_t *pc,\n    void *data);\n\n#endif\n\n\nngx_int_t\nngx_http_upstream_init_round_robin(ngx_conf_t *cf,\n    ngx_http_upstream_srv_conf_t *us)\n{\n    ngx_url_t                      u;\n    ngx_uint_t                     i, j, n, w, t;\n    ngx_http_upstream_server_t    *server;\n    ngx_http_upstream_rr_peer_t   *peer, **peerp;\n    ngx_http_upstream_rr_peers_t  *peers, *backup;\n\n    us->peer.init = ngx_http_upstream_init_round_robin_peer;\n#if (T_NGX_HTTP_UPSTREAM_RANDOM)\n    us->flags |= T_NGX_HTTP_UPSTREAM_RANDOM_FLAG;\n#endif\n\n    if (us->servers) {\n        server = us->servers->elts;\n\n        n = 0;\n        w = 0;\n        t = 0;\n\n        for (i = 0; i < us->servers->nelts; i++) {\n            if (server[i].backup) {\n                continue;\n            }\n\n            n += server[i].naddrs;\n            w += server[i].naddrs * server[i].weight;\n\n            if (!server[i].down) {\n                t += server[i].naddrs;\n            }\n        }\n\n        if (n == 0) {\n            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                          \"no servers in upstream \\\"%V\\\" in %s:%ui\",\n                          &us->host, us->file_name, us->line);\n            return NGX_ERROR;\n        }\n\n        peers = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_rr_peers_t));\n        if (peers == NULL) {\n            return NGX_ERROR;\n        }\n\n        peer = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_rr_peer_t) * n);\n        if (peer == NULL) {\n            return NGX_ERROR;\n        }\n\n        peers->single = (n == 1);\n        peers->number = n;\n        peers->weighted = (w != n);\n        peers->total_weight = w;\n        peers->tries = t;\n        peers->name = &us->host;\n#if (T_NGX_HTTP_UPSTREAM_RANDOM)\n        peers->init_number = NGX_CONF_UNSET_UINT;\n#endif\n\n        n = 0;\n        peerp = &peers->peer;\n\n        for (i = 0; i < us->servers->nelts; i++) {\n            if (server[i].backup) {\n                continue;\n            }\n\n            for (j = 0; j < server[i].naddrs; j++) {\n                peer[n].sockaddr = server[i].addrs[j].sockaddr;\n                peer[n].socklen = server[i].addrs[j].socklen;\n                peer[n].name = server[i].addrs[j].name;\n#if (T_NGX_HTTP_UPSTREAM_ID)\n                peer[n].id = server[i].id;\n#endif                \n                peer[n].weight = server[i].weight;\n                peer[n].effective_weight = server[i].weight;\n                peer[n].current_weight = 0;\n                peer[n].max_conns = server[i].max_conns;\n                peer[n].max_fails = server[i].max_fails;\n                peer[n].fail_timeout = server[i].fail_timeout;\n                peer[n].down = server[i].down;\n                peer[n].server = server[i].name;\n#if (T_NGX_HTTP_DYNAMIC_RESOLVE)\n                peer[n].host = server[i].host;\n#endif\n         \n#if (NGX_HTTP_UPSTREAM_CHECK)\n                if (!server[i].down) {\n                    peer[n].check_index =\n                        ngx_http_upstream_check_add_peer(cf, us,\n                                                         &server[i].addrs[j]);\n                } else {\n                    peer[n].check_index = (ngx_uint_t) NGX_ERROR;\n                }\n#endif\n\n                *peerp = &peer[n];\n                peerp = &peer[n].next;\n                n++;\n            }\n        }\n\n        us->peer.data = peers;\n\n        /* backup servers */\n\n        n = 0;\n        w = 0;\n        t = 0;\n\n        for (i = 0; i < us->servers->nelts; i++) {\n            if (!server[i].backup) {\n                continue;\n            }\n\n            n += server[i].naddrs;\n            w += server[i].naddrs * server[i].weight;\n\n            if (!server[i].down) {\n                t += server[i].naddrs;\n            }\n        }\n\n        if (n == 0) {\n            return NGX_OK;\n        }\n\n        backup = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_rr_peers_t));\n        if (backup == NULL) {\n            return NGX_ERROR;\n        }\n\n        peer = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_rr_peer_t) * n);\n        if (peer == NULL) {\n            return NGX_ERROR;\n        }\n\n        peers->single = 0;\n        backup->single = 0;\n        backup->number = n;\n        backup->weighted = (w != n);\n        backup->total_weight = w;\n        backup->tries = t;\n        backup->name = &us->host;\n#if (T_NGX_HTTP_UPSTREAM_RANDOM)\n        backup->init_number = NGX_CONF_UNSET_UINT;\n#endif\n\n        n = 0;\n        peerp = &backup->peer;\n\n        for (i = 0; i < us->servers->nelts; i++) {\n            if (!server[i].backup) {\n                continue;\n            }\n\n            for (j = 0; j < server[i].naddrs; j++) {\n                peer[n].sockaddr = server[i].addrs[j].sockaddr;\n                peer[n].socklen = server[i].addrs[j].socklen;\n                peer[n].name = server[i].addrs[j].name;\n#if (T_NGX_HTTP_UPSTREAM_ID)\n                peer[n].id = server[i].id;\n#endif\n                peer[n].weight = server[i].weight;\n                peer[n].effective_weight = server[i].weight;\n                peer[n].current_weight = 0;\n                peer[n].max_conns = server[i].max_conns;\n                peer[n].max_fails = server[i].max_fails;\n                peer[n].fail_timeout = server[i].fail_timeout;\n                peer[n].down = server[i].down;\n#if (T_NGX_HTTP_DYNAMIC_RESOLVE)\n                peer[n].host = server[i].host;\n#endif\n                peer[n].server = server[i].name;\n\n                *peerp = &peer[n];\n                peerp = &peer[n].next;\n#if (NGX_HTTP_UPSTREAM_CHECK)\n                if (!server[i].down) {\n                    peer[n].check_index =\n                        ngx_http_upstream_check_add_peer(cf, us,\n                                                         &server[i].addrs[j]);\n                } else {\n                    peer[n].check_index = (ngx_uint_t) NGX_ERROR;\n                }\n#endif\n\n                n++;\n            }\n        }\n\n        peers->next = backup;\n\n        return NGX_OK;\n    }\n\n\n    /* an upstream implicitly defined by proxy_pass, etc. */\n\n    if (us->port == 0) {\n        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                      \"no port in upstream \\\"%V\\\" in %s:%ui\",\n                      &us->host, us->file_name, us->line);\n        return NGX_ERROR;\n    }\n\n    ngx_memzero(&u, sizeof(ngx_url_t));\n\n    u.host = us->host;\n    u.port = us->port;\n\n    if (ngx_inet_resolve_host(cf->pool, &u) != NGX_OK) {\n        if (u.err) {\n            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                          \"%s in upstream \\\"%V\\\" in %s:%ui\",\n                          u.err, &us->host, us->file_name, us->line);\n        }\n\n        return NGX_ERROR;\n    }\n\n    n = u.naddrs;\n\n    peers = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_rr_peers_t));\n    if (peers == NULL) {\n        return NGX_ERROR;\n    }\n\n    peer = ngx_pcalloc(cf->pool, sizeof(ngx_http_upstream_rr_peer_t) * n);\n    if (peer == NULL) {\n        return NGX_ERROR;\n    }\n\n    peers->single = (n == 1);\n    peers->number = n;\n    peers->weighted = 0;\n    peers->total_weight = n;\n    peers->tries = n;\n    peers->name = &us->host;\n#if (T_NGX_HTTP_UPSTREAM_RANDOM)\n    peers->init_number = NGX_CONF_UNSET_UINT;\n#endif\n\n    peerp = &peers->peer;\n\n    for (i = 0; i < u.naddrs; i++) {\n        peer[i].sockaddr = u.addrs[i].sockaddr;\n        peer[i].socklen = u.addrs[i].socklen;\n        peer[i].name = u.addrs[i].name;\n#if (T_NGX_HTTP_UPSTREAM_ID)\n        peer[i].id.len = 0;\n        peer[i].id.data = NULL;\n#endif\n        peer[i].weight = 1;\n        peer[i].effective_weight = 1;\n        peer[i].current_weight = 0;\n        peer[i].max_conns = 0;\n        peer[i].max_fails = 1;\n        peer[i].fail_timeout = 10;\n        *peerp = &peer[i];\n        peerp = &peer[i].next;\n#if (T_NGX_HTTP_DYNAMIC_RESOLVE)\n        peer[i].host = u.host;\n#endif\n\n#if (NGX_HTTP_UPSTREAM_CHECK)\n        peer[i].check_index = (ngx_uint_t) NGX_ERROR;\n#endif\n    }\n\n    us->peer.data = peers;\n\n    /* implicitly defined upstream has no backup servers */\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_upstream_init_round_robin_peer(ngx_http_request_t *r,\n    ngx_http_upstream_srv_conf_t *us)\n{\n    ngx_uint_t                         n;\n    ngx_http_upstream_rr_peer_data_t  *rrp;\n\n    rrp = r->upstream->peer.data;\n\n    if (rrp == NULL) {\n        rrp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_rr_peer_data_t));\n        if (rrp == NULL) {\n            return NGX_ERROR;\n        }\n\n        r->upstream->peer.data = rrp;\n    }\n\n    rrp->peers = us->peer.data;\n    rrp->current = NULL;\n    rrp->config = 0;\n\n    n = rrp->peers->number;\n\n    if (rrp->peers->next && rrp->peers->next->number > n) {\n        n = rrp->peers->next->number;\n    }\n\n    if (n <= 8 * sizeof(uintptr_t)) {\n        rrp->tried = &rrp->data;\n        rrp->data = 0;\n\n    } else {\n        n = (n + (8 * sizeof(uintptr_t) - 1)) / (8 * sizeof(uintptr_t));\n\n        rrp->tried = ngx_pcalloc(r->pool, n * sizeof(uintptr_t));\n        if (rrp->tried == NULL) {\n            return NGX_ERROR;\n        }\n    }\n\n    r->upstream->peer.get = ngx_http_upstream_get_round_robin_peer;\n    r->upstream->peer.free = ngx_http_upstream_free_round_robin_peer;\n    r->upstream->peer.tries = ngx_http_upstream_tries(rrp->peers);\n#if (NGX_HTTP_SSL)\n    r->upstream->peer.set_session =\n                               ngx_http_upstream_set_round_robin_peer_session;\n    r->upstream->peer.save_session =\n                               ngx_http_upstream_save_round_robin_peer_session;\n#endif\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_upstream_create_round_robin_peer(ngx_http_request_t *r,\n    ngx_http_upstream_resolved_t *ur)\n{\n    u_char                            *p;\n    size_t                             len;\n    socklen_t                          socklen;\n    ngx_uint_t                         i, n;\n    struct sockaddr                   *sockaddr;\n    ngx_http_upstream_rr_peer_t       *peer, **peerp;\n    ngx_http_upstream_rr_peers_t      *peers;\n    ngx_http_upstream_rr_peer_data_t  *rrp;\n\n    rrp = r->upstream->peer.data;\n\n    if (rrp == NULL) {\n        rrp = ngx_palloc(r->pool, sizeof(ngx_http_upstream_rr_peer_data_t));\n        if (rrp == NULL) {\n            return NGX_ERROR;\n        }\n\n        r->upstream->peer.data = rrp;\n    }\n\n    peers = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_rr_peers_t));\n    if (peers == NULL) {\n        return NGX_ERROR;\n    }\n\n    peer = ngx_pcalloc(r->pool, sizeof(ngx_http_upstream_rr_peer_t)\n                                * ur->naddrs);\n    if (peer == NULL) {\n        return NGX_ERROR;\n    }\n\n    peers->single = (ur->naddrs == 1);\n    peers->number = ur->naddrs;\n    peers->tries = ur->naddrs;\n#if (T_NGX_HTTP_UPSTREAM_RANDOM)\n    peers->init_number = NGX_CONF_UNSET_UINT;\n#endif\n    peers->name = &ur->host;\n\n    if (ur->sockaddr) {\n        peer[0].sockaddr = ur->sockaddr;\n        peer[0].socklen = ur->socklen;\n        peer[0].name = ur->name.data ? ur->name : ur->host;\n#if (T_NGX_HTTP_UPSTREAM_ID)\n        peer[0].id.len = 0;\n        peer[0].id.data = NULL;\n#endif\n        peer[0].weight = 1;\n        peer[0].effective_weight = 1;\n        peer[0].current_weight = 0;\n        peer[0].max_conns = 0;\n        peer[0].max_fails = 1;\n        peer[0].fail_timeout = 10;\n        peers->peer = peer;\n#if (NGX_HTTP_UPSTREAM_CHECK)\n        peer[0].check_index = (ngx_uint_t) NGX_ERROR;\n#endif\n\n    } else {\n        peerp = &peers->peer;\n\n        for (i = 0; i < ur->naddrs; i++) {\n\n            socklen = ur->addrs[i].socklen;\n\n            sockaddr = ngx_palloc(r->pool, socklen);\n            if (sockaddr == NULL) {\n                return NGX_ERROR;\n            }\n\n            ngx_memcpy(sockaddr, ur->addrs[i].sockaddr, socklen);\n            ngx_inet_set_port(sockaddr, ur->port);\n\n            p = ngx_pnalloc(r->pool, NGX_SOCKADDR_STRLEN);\n            if (p == NULL) {\n                return NGX_ERROR;\n            }\n\n            len = ngx_sock_ntop(sockaddr, socklen, p, NGX_SOCKADDR_STRLEN, 1);\n\n            peer[i].sockaddr = sockaddr;\n            peer[i].socklen = socklen;\n            peer[i].name.len = len;\n            peer[i].name.data = p;\n#if (T_NGX_HTTP_UPSTREAM_ID)\n            peer[i].id.len = 0;\n            peer[i].id.data = NULL;\n#endif\n            peer[i].weight = 1;\n            peer[i].effective_weight = 1;\n            peer[i].current_weight = 0;\n            peer[i].max_conns = 0;\n            peer[i].max_fails = 1;\n            peer[i].fail_timeout = 10;\n            *peerp = &peer[i];\n            peerp = &peer[i].next;\n#if (NGX_HTTP_UPSTREAM_CHECK)\n            peer[i].check_index = (ngx_uint_t) NGX_ERROR;\n#endif\n        }\n    }\n\n    rrp->peers = peers;\n    rrp->current = NULL;\n    rrp->config = 0;\n\n    if (rrp->peers->number <= 8 * sizeof(uintptr_t)) {\n        rrp->tried = &rrp->data;\n        rrp->data = 0;\n\n    } else {\n        n = (rrp->peers->number + (8 * sizeof(uintptr_t) - 1))\n                / (8 * sizeof(uintptr_t));\n\n        rrp->tried = ngx_pcalloc(r->pool, n * sizeof(uintptr_t));\n        if (rrp->tried == NULL) {\n            return NGX_ERROR;\n        }\n    }\n\n    r->upstream->peer.get = ngx_http_upstream_get_round_robin_peer;\n    r->upstream->peer.free = ngx_http_upstream_free_round_robin_peer;\n    r->upstream->peer.tries = ngx_http_upstream_tries(rrp->peers);\n#if (NGX_HTTP_SSL)\n    r->upstream->peer.set_session = ngx_http_upstream_empty_set_session;\n    r->upstream->peer.save_session = ngx_http_upstream_empty_save_session;\n#endif\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_upstream_get_round_robin_peer(ngx_peer_connection_t *pc, void *data)\n{\n    ngx_http_upstream_rr_peer_data_t  *rrp = data;\n\n    ngx_int_t                      rc;\n    ngx_uint_t                     i, n;\n    ngx_http_upstream_rr_peer_t   *peer;\n    ngx_http_upstream_rr_peers_t  *peers;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                   \"get rr peer, try: %ui\", pc->tries);\n\n    pc->cached = 0;\n    pc->connection = NULL;\n\n    peers = rrp->peers;\n    ngx_http_upstream_rr_peers_wlock(peers);\n\n    if (peers->single) {\n        peer = peers->peer;\n\n        if (peer->down) {\n            goto failed;\n        }\n\n        if (peer->max_conns && peer->conns >= peer->max_conns) {\n            goto failed;\n        }\n\n#if (NGX_HTTP_UPSTREAM_CHECK)\n        if (ngx_http_upstream_check_peer_down(peer->check_index)) {\n            goto failed;\n        }\n#endif\n        rrp->current = peer;\n\n    } else {\n\n        /* there are several peers */\n\n        peer = ngx_http_upstream_get_peer(rrp);\n\n        if (peer == NULL) {\n            goto failed;\n        }\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                       \"get rr peer, current: %p %i\",\n                       peer, peer->current_weight);\n    }\n\n    pc->sockaddr = peer->sockaddr;\n    pc->socklen = peer->socklen;\n    pc->name = &peer->name;\n#if (T_NGX_HTTP_DYNAMIC_RESOLVE)\n    pc->host = &peer->host;\n#endif\n\n    peer->conns++;\n\n    ngx_http_upstream_rr_peers_unlock(peers);\n\n    return NGX_OK;\n\nfailed:\n\n    if (peers->next) {\n\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, pc->log, 0, \"backup servers\");\n\n        rrp->peers = peers->next;\n\n        n = (rrp->peers->number + (8 * sizeof(uintptr_t) - 1))\n                / (8 * sizeof(uintptr_t));\n\n        for (i = 0; i < n; i++) {\n            rrp->tried[i] = 0;\n        }\n\n        ngx_http_upstream_rr_peers_unlock(peers);\n\n        rc = ngx_http_upstream_get_round_robin_peer(pc, rrp);\n\n        if (rc != NGX_BUSY) {\n            return rc;\n        }\n\n        ngx_http_upstream_rr_peers_wlock(peers);\n    }\n\n    ngx_http_upstream_rr_peers_unlock(peers);\n\n    pc->name = peers->name;\n\n    return NGX_BUSY;\n}\n\n\nstatic ngx_http_upstream_rr_peer_t *\nngx_http_upstream_get_peer(ngx_http_upstream_rr_peer_data_t *rrp)\n{\n    time_t                        now;\n    uintptr_t                     m;\n#if (T_NGX_HTTP_UPSTREAM_RANDOM)\n    ngx_int_t                     total, flag;\n#else\n    ngx_int_t                     total;\n#endif\n    ngx_uint_t                    i, n, p;\n    ngx_http_upstream_rr_peer_t  *peer, *best;\n\n    now = ngx_time();\n\n    best = NULL;\n    total = 0;\n\n#if (NGX_SUPPRESS_WARN)\n    p = 0;\n#endif\n\n#if (T_NGX_HTTP_UPSTREAM_RANDOM)\n    if (rrp->peers->init_number == NGX_CONF_UNSET_UINT) {\n         rrp->peers->init_number = ngx_random() % rrp->peers->number;\n    }\n\n    for (peer = rrp->peers->peer, i = 0; i < rrp->peers->init_number; i++) {\n        peer = peer->next;\n    }\n\n    flag = 1;\n    for (i = rrp->peers->init_number;\n         i != rrp->peers->init_number || flag;\n         i = (i + 1) % rrp->peers->number,\n         peer = peer->next ? peer->next : rrp->peers->peer)\n    {\n        flag = 0;\n\n#else\n    for (peer = rrp->peers->peer, i = 0;\n         peer;\n         peer = peer->next, i++)\n    {\n#endif\n        n = i / (8 * sizeof(uintptr_t));\n        m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));\n\n        if (rrp->tried[n] & m) {\n            continue;\n        }\n\n        if (peer->down) {\n            continue;\n        }\n\n#if (NGX_HTTP_UPSTREAM_CHECK)\n        if (ngx_http_upstream_check_peer_down(peer->check_index)) {\n            continue;\n        }\n#endif\n\n        if (peer->max_fails\n            && peer->fails >= peer->max_fails\n            && now - peer->checked <= peer->fail_timeout)\n        {\n            continue;\n        }\n\n        if (peer->max_conns && peer->conns >= peer->max_conns) {\n            continue;\n        }\n\n        peer->current_weight += peer->effective_weight;\n        total += peer->effective_weight;\n\n        if (peer->effective_weight < peer->weight) {\n            peer->effective_weight++;\n        }\n\n        if (best == NULL || peer->current_weight > best->current_weight) {\n            best = peer;\n            p = i;\n        }\n    }\n\n    if (best == NULL) {\n        return NULL;\n    }\n\n    rrp->current = best;\n\n    n = p / (8 * sizeof(uintptr_t));\n    m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t));\n\n    rrp->tried[n] |= m;\n\n    best->current_weight -= total;\n\n    if (now - best->checked > best->fail_timeout) {\n        best->checked = now;\n    }\n\n    return best;\n}\n\n\nvoid\nngx_http_upstream_free_round_robin_peer(ngx_peer_connection_t *pc, void *data,\n    ngx_uint_t state)\n{\n    ngx_http_upstream_rr_peer_data_t  *rrp = data;\n\n    time_t                       now;\n    ngx_http_upstream_rr_peer_t  *peer;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                   \"free rr peer %ui %ui\", pc->tries, state);\n\n    /* TODO: NGX_PEER_KEEPALIVE */\n\n    peer = rrp->current;\n\n    ngx_http_upstream_rr_peers_rlock(rrp->peers);\n    ngx_http_upstream_rr_peer_lock(rrp->peers, peer);\n\n    if (rrp->peers->single) {\n\n        peer->conns--;\n\n        ngx_http_upstream_rr_peer_unlock(rrp->peers, peer);\n        ngx_http_upstream_rr_peers_unlock(rrp->peers);\n\n        pc->tries = 0;\n        return;\n    }\n\n    if (state & NGX_PEER_FAILED) {\n        now = ngx_time();\n\n        peer->fails++;\n        peer->accessed = now;\n        peer->checked = now;\n\n        if (peer->max_fails) {\n            peer->effective_weight -= peer->weight / peer->max_fails;\n\n            if (peer->fails >= peer->max_fails) {\n                ngx_log_error(NGX_LOG_WARN, pc->log, 0,\n                              \"upstream server temporarily disabled\");\n            }\n        }\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                       \"free rr peer failed: %p %i\",\n                       peer, peer->effective_weight);\n\n        if (peer->effective_weight < 0) {\n            peer->effective_weight = 0;\n        }\n\n    } else {\n\n        /* mark peer live if check passed */\n\n        if (peer->accessed < peer->checked) {\n            peer->fails = 0;\n        }\n    }\n\n    peer->conns--;\n\n    ngx_http_upstream_rr_peer_unlock(rrp->peers, peer);\n    ngx_http_upstream_rr_peers_unlock(rrp->peers);\n\n    if (pc->tries) {\n        pc->tries--;\n    }\n}\n\n\n#if (NGX_HTTP_SSL)\n\nngx_int_t\nngx_http_upstream_set_round_robin_peer_session(ngx_peer_connection_t *pc,\n    void *data)\n{\n    ngx_http_upstream_rr_peer_data_t  *rrp = data;\n\n    ngx_int_t                      rc;\n    ngx_ssl_session_t             *ssl_session;\n    ngx_http_upstream_rr_peer_t   *peer;\n#if (NGX_HTTP_UPSTREAM_ZONE)\n    int                            len;\n    const u_char                  *p;\n    ngx_http_upstream_rr_peers_t  *peers;\n    u_char                         buf[NGX_SSL_MAX_SESSION_SIZE];\n#endif\n\n    peer = rrp->current;\n\n#if (NGX_HTTP_UPSTREAM_ZONE)\n    peers = rrp->peers;\n\n    if (peers->shpool) {\n        ngx_http_upstream_rr_peers_rlock(peers);\n        ngx_http_upstream_rr_peer_lock(peers, peer);\n\n        if (peer->ssl_session == NULL) {\n            ngx_http_upstream_rr_peer_unlock(peers, peer);\n            ngx_http_upstream_rr_peers_unlock(peers);\n            return NGX_OK;\n        }\n\n        len = peer->ssl_session_len;\n\n        ngx_memcpy(buf, peer->ssl_session, len);\n\n        ngx_http_upstream_rr_peer_unlock(peers, peer);\n        ngx_http_upstream_rr_peers_unlock(peers);\n\n        p = buf;\n        ssl_session = d2i_SSL_SESSION(NULL, &p, len);\n\n        rc = ngx_ssl_set_session(pc->connection, ssl_session);\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                       \"set session: %p\", ssl_session);\n\n        ngx_ssl_free_session(ssl_session);\n\n        return rc;\n    }\n#endif\n\n    ssl_session = peer->ssl_session;\n\n    rc = ngx_ssl_set_session(pc->connection, ssl_session);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                   \"set session: %p\", ssl_session);\n\n    return rc;\n}\n\n\nvoid\nngx_http_upstream_save_round_robin_peer_session(ngx_peer_connection_t *pc,\n    void *data)\n{\n    ngx_http_upstream_rr_peer_data_t  *rrp = data;\n\n    ngx_ssl_session_t             *old_ssl_session, *ssl_session;\n    ngx_http_upstream_rr_peer_t   *peer;\n#if (NGX_HTTP_UPSTREAM_ZONE)\n    int                            len;\n    u_char                        *p;\n    ngx_http_upstream_rr_peers_t  *peers;\n    u_char                         buf[NGX_SSL_MAX_SESSION_SIZE];\n#endif\n\n#if (NGX_HTTP_UPSTREAM_ZONE)\n    peers = rrp->peers;\n\n    if (peers->shpool) {\n\n        ssl_session = ngx_ssl_get0_session(pc->connection);\n\n        if (ssl_session == NULL) {\n            return;\n        }\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                       \"save session: %p\", ssl_session);\n\n        len = i2d_SSL_SESSION(ssl_session, NULL);\n\n        /* do not cache too big session */\n\n        if (len > NGX_SSL_MAX_SESSION_SIZE) {\n            return;\n        }\n\n        p = buf;\n        (void) i2d_SSL_SESSION(ssl_session, &p);\n\n        peer = rrp->current;\n\n        ngx_http_upstream_rr_peers_rlock(peers);\n        ngx_http_upstream_rr_peer_lock(peers, peer);\n\n        if (len > peer->ssl_session_len) {\n            ngx_shmtx_lock(&peers->shpool->mutex);\n\n            if (peer->ssl_session) {\n                ngx_slab_free_locked(peers->shpool, peer->ssl_session);\n            }\n\n            peer->ssl_session = ngx_slab_alloc_locked(peers->shpool, len);\n\n            ngx_shmtx_unlock(&peers->shpool->mutex);\n\n            if (peer->ssl_session == NULL) {\n                peer->ssl_session_len = 0;\n\n                ngx_http_upstream_rr_peer_unlock(peers, peer);\n                ngx_http_upstream_rr_peers_unlock(peers);\n                return;\n            }\n\n            peer->ssl_session_len = len;\n        }\n\n        ngx_memcpy(peer->ssl_session, buf, len);\n\n        ngx_http_upstream_rr_peer_unlock(peers, peer);\n        ngx_http_upstream_rr_peers_unlock(peers);\n\n        return;\n    }\n#endif\n\n    ssl_session = ngx_ssl_get_session(pc->connection);\n\n    if (ssl_session == NULL) {\n        return;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                   \"save session: %p\", ssl_session);\n\n    peer = rrp->current;\n\n    old_ssl_session = peer->ssl_session;\n    peer->ssl_session = ssl_session;\n\n    if (old_ssl_session) {\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pc->log, 0,\n                       \"old session: %p\", old_ssl_session);\n\n        /* TODO: may block */\n\n        ngx_ssl_free_session(old_ssl_session);\n    }\n}\n\n\nstatic ngx_int_t\nngx_http_upstream_empty_set_session(ngx_peer_connection_t *pc, void *data)\n{\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_upstream_empty_save_session(ngx_peer_connection_t *pc, void *data)\n{\n    return;\n}\n\n#endif\n"
  },
  {
    "path": "src/http/ngx_http_upstream_round_robin.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_HTTP_UPSTREAM_ROUND_ROBIN_H_INCLUDED_\n#define _NGX_HTTP_UPSTREAM_ROUND_ROBIN_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\ntypedef struct ngx_http_upstream_rr_peer_s   ngx_http_upstream_rr_peer_t;\n\nstruct ngx_http_upstream_rr_peer_s {\n    struct sockaddr                *sockaddr;\n    socklen_t                       socklen;\n    ngx_str_t                       name;\n    ngx_str_t                       server;\n#if (T_NGX_HTTP_UPSTREAM_ID)\n    ngx_str_t                       id;\n#endif\n\n#if (T_NGX_HTTP_DYNAMIC_RESOLVE)\n    ngx_str_t                       host;\n#endif\n\n    ngx_int_t                       current_weight;\n    ngx_int_t                       effective_weight;\n    ngx_int_t                       weight;\n\n    ngx_uint_t                      conns;\n    ngx_uint_t                      max_conns;\n\n    ngx_uint_t                      fails;\n    time_t                          accessed;\n    time_t                          checked;\n\n    ngx_uint_t                      max_fails;\n    time_t                          fail_timeout;\n    ngx_msec_t                      slow_start;\n    ngx_msec_t                      start_time;\n\n    ngx_uint_t                      down;\n\n#if (NGX_HTTP_SSL || NGX_COMPAT)\n    void                           *ssl_session;\n    int                             ssl_session_len;\n#endif\n\n#if (NGX_HTTP_UPSTREAM_ZONE)\n    ngx_atomic_t                    lock;\n#endif\n#if (NGX_HTTP_UPSTREAM_CHECK)\n    ngx_uint_t                      check_index;\n#endif\n\n    ngx_http_upstream_rr_peer_t    *next;\n\n    NGX_COMPAT_BEGIN(32)\n    NGX_COMPAT_END\n};\n\n\ntypedef struct ngx_http_upstream_rr_peers_s  ngx_http_upstream_rr_peers_t;\n\nstruct ngx_http_upstream_rr_peers_s {\n    ngx_uint_t                      number;\n\n#if (NGX_HTTP_UPSTREAM_ZONE)\n    ngx_slab_pool_t                *shpool;\n    ngx_atomic_t                    rwlock;\n    ngx_http_upstream_rr_peers_t   *zone_next;\n#endif\n\n#if (T_NGX_HTTP_UPSTREAM_RANDOM)\n    ngx_uint_t                      init_number;\n#endif\n\n    ngx_uint_t                      total_weight;\n    ngx_uint_t                      tries;\n\n    unsigned                        single:1;\n    unsigned                        weighted:1;\n\n    ngx_str_t                      *name;\n\n    ngx_http_upstream_rr_peers_t   *next;\n\n    ngx_http_upstream_rr_peer_t    *peer;\n};\n\n\n#if (NGX_HTTP_UPSTREAM_ZONE)\n\n#define ngx_http_upstream_rr_peers_rlock(peers)                               \\\n                                                                              \\\n    if (peers->shpool) {                                                      \\\n        ngx_rwlock_rlock(&peers->rwlock);                                     \\\n    }\n\n#define ngx_http_upstream_rr_peers_wlock(peers)                               \\\n                                                                              \\\n    if (peers->shpool) {                                                      \\\n        ngx_rwlock_wlock(&peers->rwlock);                                     \\\n    }\n\n#define ngx_http_upstream_rr_peers_unlock(peers)                              \\\n                                                                              \\\n    if (peers->shpool) {                                                      \\\n        ngx_rwlock_unlock(&peers->rwlock);                                    \\\n    }\n\n\n#define ngx_http_upstream_rr_peer_lock(peers, peer)                           \\\n                                                                              \\\n    if (peers->shpool) {                                                      \\\n        ngx_rwlock_wlock(&peer->lock);                                        \\\n    }\n\n#define ngx_http_upstream_rr_peer_unlock(peers, peer)                         \\\n                                                                              \\\n    if (peers->shpool) {                                                      \\\n        ngx_rwlock_unlock(&peer->lock);                                       \\\n    }\n\n#else\n\n#define ngx_http_upstream_rr_peers_rlock(peers)\n#define ngx_http_upstream_rr_peers_wlock(peers)\n#define ngx_http_upstream_rr_peers_unlock(peers)\n#define ngx_http_upstream_rr_peer_lock(peers, peer)\n#define ngx_http_upstream_rr_peer_unlock(peers, peer)\n\n#endif\n\n\ntypedef struct {\n    ngx_uint_t                      config;\n    ngx_http_upstream_rr_peers_t   *peers;\n    ngx_http_upstream_rr_peer_t    *current;\n    uintptr_t                      *tried;\n    uintptr_t                       data;\n} ngx_http_upstream_rr_peer_data_t;\n\n\nngx_int_t ngx_http_upstream_init_round_robin(ngx_conf_t *cf,\n    ngx_http_upstream_srv_conf_t *us);\nngx_int_t ngx_http_upstream_init_round_robin_peer(ngx_http_request_t *r,\n    ngx_http_upstream_srv_conf_t *us);\nngx_int_t ngx_http_upstream_create_round_robin_peer(ngx_http_request_t *r,\n    ngx_http_upstream_resolved_t *ur);\nngx_int_t ngx_http_upstream_get_round_robin_peer(ngx_peer_connection_t *pc,\n    void *data);\nvoid ngx_http_upstream_free_round_robin_peer(ngx_peer_connection_t *pc,\n    void *data, ngx_uint_t state);\n\n#if (NGX_HTTP_SSL)\nngx_int_t\n    ngx_http_upstream_set_round_robin_peer_session(ngx_peer_connection_t *pc,\n    void *data);\nvoid ngx_http_upstream_save_round_robin_peer_session(ngx_peer_connection_t *pc,\n    void *data);\n#endif\n\n\n#endif /* _NGX_HTTP_UPSTREAM_ROUND_ROBIN_H_INCLUDED_ */\n"
  },
  {
    "path": "src/http/ngx_http_variables.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n#include <nginx.h>\n#if (T_NGX_VARS)\n#include <ngx_md5.h>\n#endif\n\n\nstatic ngx_http_variable_t *ngx_http_add_prefix_variable(ngx_conf_t *cf,\n    ngx_str_t *name, ngx_uint_t flags);\n\nstatic ngx_int_t ngx_http_variable_request(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\n#if 0\nstatic void ngx_http_variable_request_set(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\n#endif\nstatic ngx_int_t ngx_http_variable_request_get_size(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_header(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\n\n#if (T_HTTP_UPSTREAM_TIMEOUT_VAR)\nstatic ngx_int_t ngx_http_variable_request_get_time(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic void ngx_http_variable_request_set_time(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\n#endif\n\nstatic ngx_int_t ngx_http_variable_cookies(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_headers_internal(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data, u_char sep);\n\nstatic ngx_int_t ngx_http_variable_unknown_header_in(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_unknown_header_out(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_unknown_trailer_out(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_request_line(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_cookie(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\n#if (T_NGX_VARS)\nstatic ngx_int_t ngx_http_variable_sent_cookie(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\n#endif\nstatic ngx_int_t ngx_http_variable_argument(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\n#if (T_NGX_VARS)\nstatic ngx_int_t ngx_http_variable_decode_base64(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_md5(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_escape_uri(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_ascii(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_full_request(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_normalized_request(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\n#endif\n\n\n#if (NGX_HAVE_TCP_INFO)\nstatic ngx_int_t ngx_http_variable_tcpinfo(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\n#endif\n\n#if (T_NGX_REQUEST_START_TIME)\nstatic ngx_int_t\nngx_http_variable_request_start_time(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\n#endif\n\nstatic ngx_int_t ngx_http_variable_content_length(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_host(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_binary_remote_addr(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_remote_addr(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_remote_port(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_proxy_protocol_addr(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_proxy_protocol_port(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_proxy_protocol_tlv(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_server_addr(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_server_port(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_scheme(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_https(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic void ngx_http_variable_set_args(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_is_args(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_document_root(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_realpath_root(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_request_filename(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_server_name(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_request_method(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_remote_user(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_bytes_sent(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_body_bytes_sent(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_pipe(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_request_completion(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_request_body(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_request_body_file(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_request_length(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_request_time(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_request_id(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_status(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\n\nstatic ngx_int_t ngx_http_variable_sent_content_type(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_sent_content_length(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_sent_location(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_sent_last_modified(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_sent_connection(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_sent_keep_alive(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_sent_transfer_encoding(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic void ngx_http_variable_set_limit_rate(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\n\nstatic ngx_int_t ngx_http_variable_connection(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_connection_requests(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_connection_time(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\n\nstatic ngx_int_t ngx_http_variable_nginx_version(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_hostname(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_pid(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_msec(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_time_iso8601(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_time_local(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\n\n#if (T_NGX_VARS)\nstatic ngx_int_t ngx_http_variable_dollar(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_host_comment(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\n\nstatic ngx_int_t ngx_http_variable_unix_time(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_year(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_year2(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_month(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_day(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_hour(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_hour12(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_minute(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_second(ngx_http_request_t *r,\n        ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variable_time_http(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_http_variables_time_fmt(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, ngx_int_t t, ngx_int_t len);\n#endif\n\n/*\n * TODO:\n *     Apache CGI: AUTH_TYPE, PATH_INFO (null), PATH_TRANSLATED\n *                 REMOTE_HOST (null), REMOTE_IDENT (null),\n *                 SERVER_SOFTWARE\n *\n *     Apache SSI: DOCUMENT_NAME, LAST_MODIFIED, USER_NAME (file owner)\n */\n\n/*\n * the $http_host, $http_user_agent, $http_referer, and $http_via\n * variables may be handled by generic\n * ngx_http_variable_unknown_header_in(), but for performance reasons\n * they are handled using dedicated entries\n */\n\nstatic ngx_http_variable_t  ngx_http_core_variables[] = {\n\n#if (NGX_HTTP_PROXY_CONNECT)\n    { ngx_string(\"connect_host\"), NULL, ngx_http_variable_request,\n      offsetof(ngx_http_request_t, connect_host), 0, 0 },\n\n    { ngx_string(\"connect_port\"), NULL, ngx_http_variable_request,\n      offsetof(ngx_http_request_t, connect_port), 0, 0 },\n#endif\n\n    { ngx_string(\"http_host\"), NULL, ngx_http_variable_header,\n      offsetof(ngx_http_request_t, headers_in.host), 0, 0 },\n\n    { ngx_string(\"http_user_agent\"), NULL, ngx_http_variable_header,\n      offsetof(ngx_http_request_t, headers_in.user_agent), 0, 0 },\n\n    { ngx_string(\"http_referer\"), NULL, ngx_http_variable_header,\n      offsetof(ngx_http_request_t, headers_in.referer), 0, 0 },\n\n#if (NGX_HTTP_GZIP)\n    { ngx_string(\"http_via\"), NULL, ngx_http_variable_header,\n      offsetof(ngx_http_request_t, headers_in.via), 0, 0 },\n#endif\n\n#if (NGX_HTTP_X_FORWARDED_FOR)\n    { ngx_string(\"http_x_forwarded_for\"), NULL, ngx_http_variable_header,\n      offsetof(ngx_http_request_t, headers_in.x_forwarded_for), 0, 0 },\n#endif\n\n    { ngx_string(\"http_cookie\"), NULL, ngx_http_variable_cookies,\n      offsetof(ngx_http_request_t, headers_in.cookie), 0, 0 },\n\n    { ngx_string(\"content_length\"), NULL, ngx_http_variable_content_length,\n      0, 0, 0 },\n\n    { ngx_string(\"content_type\"), NULL, ngx_http_variable_header,\n      offsetof(ngx_http_request_t, headers_in.content_type), 0, 0 },\n\n    { ngx_string(\"host\"), NULL, ngx_http_variable_host, 0, 0, 0 },\n\n    { ngx_string(\"binary_remote_addr\"), NULL,\n      ngx_http_variable_binary_remote_addr, 0, 0, 0 },\n\n    { ngx_string(\"remote_addr\"), NULL, ngx_http_variable_remote_addr, 0, 0, 0 },\n\n    { ngx_string(\"remote_port\"), NULL, ngx_http_variable_remote_port, 0, 0, 0 },\n\n    { ngx_string(\"proxy_protocol_addr\"), NULL,\n      ngx_http_variable_proxy_protocol_addr,\n      offsetof(ngx_proxy_protocol_t, src_addr), 0, 0 },\n\n    { ngx_string(\"proxy_protocol_port\"), NULL,\n      ngx_http_variable_proxy_protocol_port,\n      offsetof(ngx_proxy_protocol_t, src_port), 0, 0 },\n\n    { ngx_string(\"proxy_protocol_server_addr\"), NULL,\n      ngx_http_variable_proxy_protocol_addr,\n      offsetof(ngx_proxy_protocol_t, dst_addr), 0, 0 },\n\n    { ngx_string(\"proxy_protocol_server_port\"), NULL,\n      ngx_http_variable_proxy_protocol_port,\n      offsetof(ngx_proxy_protocol_t, dst_port), 0, 0 },\n\n    { ngx_string(\"proxy_protocol_tlv_\"), NULL,\n      ngx_http_variable_proxy_protocol_tlv,\n      0, NGX_HTTP_VAR_PREFIX, 0 },\n\n    { ngx_string(\"server_addr\"), NULL, ngx_http_variable_server_addr, 0, 0, 0 },\n\n    { ngx_string(\"server_port\"), NULL, ngx_http_variable_server_port, 0, 0, 0 },\n\n    { ngx_string(\"server_protocol\"), NULL, ngx_http_variable_request,\n      offsetof(ngx_http_request_t, http_protocol), 0, 0 },\n\n    { ngx_string(\"scheme\"), NULL, ngx_http_variable_scheme, 0, 0, 0 },\n\n    { ngx_string(\"https\"), NULL, ngx_http_variable_https, 0, 0, 0 },\n\n#if (T_NGX_VARS)\n    { ngx_string(\"full_request\"), NULL, ngx_http_variable_full_request,\n      0, 0, 0 },\n\n    { ngx_string(\"normalized_request\"), NULL, ngx_http_variable_normalized_request,\n      0, 0, 0 },\n#endif\n\n    { ngx_string(\"request_uri\"), NULL, ngx_http_variable_request,\n      offsetof(ngx_http_request_t, unparsed_uri), 0, 0 },\n\n#if (T_NGX_VARS)\n    { ngx_string(\"raw_uri\"), NULL, ngx_http_variable_request,\n      offsetof(ngx_http_request_t, raw_uri), 0, 0 },\n#endif\n\n    { ngx_string(\"uri\"), NULL, ngx_http_variable_request,\n      offsetof(ngx_http_request_t, uri),\n      NGX_HTTP_VAR_NOCACHEABLE, 0 },\n\n    { ngx_string(\"document_uri\"), NULL, ngx_http_variable_request,\n      offsetof(ngx_http_request_t, uri),\n      NGX_HTTP_VAR_NOCACHEABLE, 0 },\n\n    { ngx_string(\"request\"), NULL, ngx_http_variable_request_line, 0, 0, 0 },\n\n    { ngx_string(\"document_root\"), NULL,\n      ngx_http_variable_document_root, 0, NGX_HTTP_VAR_NOCACHEABLE, 0 },\n\n    { ngx_string(\"realpath_root\"), NULL,\n      ngx_http_variable_realpath_root, 0, NGX_HTTP_VAR_NOCACHEABLE, 0 },\n\n    { ngx_string(\"query_string\"), NULL, ngx_http_variable_request,\n      offsetof(ngx_http_request_t, args),\n      NGX_HTTP_VAR_NOCACHEABLE, 0 },\n\n    { ngx_string(\"args\"),\n      ngx_http_variable_set_args,\n      ngx_http_variable_request,\n      offsetof(ngx_http_request_t, args),\n      NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE, 0 },\n\n    { ngx_string(\"is_args\"), NULL, ngx_http_variable_is_args,\n      0, NGX_HTTP_VAR_NOCACHEABLE, 0 },\n\n    { ngx_string(\"request_filename\"), NULL,\n      ngx_http_variable_request_filename, 0,\n      NGX_HTTP_VAR_NOCACHEABLE, 0 },\n\n    { ngx_string(\"server_name\"), NULL, ngx_http_variable_server_name, 0, 0, 0 },\n\n    { ngx_string(\"request_method\"), NULL,\n      ngx_http_variable_request_method, 0,\n      NGX_HTTP_VAR_NOCACHEABLE, 0 },\n\n    { ngx_string(\"remote_user\"), NULL, ngx_http_variable_remote_user, 0, 0, 0 },\n\n    { ngx_string(\"bytes_sent\"), NULL, ngx_http_variable_bytes_sent,\n      0, 0, 0 },\n\n    { ngx_string(\"body_bytes_sent\"), NULL, ngx_http_variable_body_bytes_sent,\n      0, 0, 0 },\n\n    { ngx_string(\"pipe\"), NULL, ngx_http_variable_pipe,\n      0, 0, 0 },\n\n    { ngx_string(\"request_completion\"), NULL,\n      ngx_http_variable_request_completion,\n      0, 0, 0 },\n\n    { ngx_string(\"request_body\"), NULL,\n      ngx_http_variable_request_body,\n      0, 0, 0 },\n\n    { ngx_string(\"request_body_file\"), NULL,\n      ngx_http_variable_request_body_file,\n      0, 0, 0 },\n\n    { ngx_string(\"request_length\"), NULL, ngx_http_variable_request_length,\n      0, NGX_HTTP_VAR_NOCACHEABLE, 0 },\n\n    { ngx_string(\"request_time\"), NULL, ngx_http_variable_request_time,\n      0, NGX_HTTP_VAR_NOCACHEABLE, 0 },\n\n    { ngx_string(\"request_id\"), NULL,\n      ngx_http_variable_request_id,\n      0, 0, 0 },\n\n    { ngx_string(\"status\"), NULL,\n      ngx_http_variable_status, 0,\n      NGX_HTTP_VAR_NOCACHEABLE, 0 },\n\n    { ngx_string(\"sent_http_content_type\"), NULL,\n      ngx_http_variable_sent_content_type, 0, 0, 0 },\n\n    { ngx_string(\"sent_http_content_length\"), NULL,\n      ngx_http_variable_sent_content_length, 0, 0, 0 },\n\n    { ngx_string(\"sent_http_location\"), NULL,\n      ngx_http_variable_sent_location, 0, 0, 0 },\n\n    { ngx_string(\"sent_http_last_modified\"), NULL,\n      ngx_http_variable_sent_last_modified, 0, 0, 0 },\n\n    { ngx_string(\"sent_http_connection\"), NULL,\n      ngx_http_variable_sent_connection, 0, 0, 0 },\n\n    { ngx_string(\"sent_http_keep_alive\"), NULL,\n      ngx_http_variable_sent_keep_alive, 0, 0, 0 },\n\n    { ngx_string(\"sent_http_transfer_encoding\"), NULL,\n      ngx_http_variable_sent_transfer_encoding, 0, 0, 0 },\n\n    { ngx_string(\"sent_http_cache_control\"), NULL, ngx_http_variable_header,\n      offsetof(ngx_http_request_t, headers_out.cache_control), 0, 0 },\n\n    { ngx_string(\"sent_http_link\"), NULL, ngx_http_variable_header,\n      offsetof(ngx_http_request_t, headers_out.link), 0, 0 },\n\n    { ngx_string(\"limit_rate\"), ngx_http_variable_set_limit_rate,\n      ngx_http_variable_request_get_size,\n      offsetof(ngx_http_request_t, limit_rate),\n      NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE, 0 },\n\n#if (T_HTTP_UPSTREAM_TIMEOUT_VAR)\n    { ngx_string(\"upstream_connect_time\"), ngx_http_variable_request_set_time,\n      ngx_http_variable_request_get_time,\n      offsetof(ngx_http_request_t, connect_time),\n      NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE, 0 },\n\n    { ngx_string(\"upstream_read_time\"), ngx_http_variable_request_set_time,\n      ngx_http_variable_request_get_time,\n      offsetof(ngx_http_request_t, read_time),\n      NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE, 0 },\n\n    { ngx_string(\"upstream_send_time\"), ngx_http_variable_request_set_time,\n      ngx_http_variable_request_get_time,\n      offsetof(ngx_http_request_t, send_time),\n      NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE, 0 },\n#endif\n\n    { ngx_string(\"connection\"), NULL,\n      ngx_http_variable_connection, 0, 0, 0 },\n\n    { ngx_string(\"connection_requests\"), NULL,\n      ngx_http_variable_connection_requests, 0, 0, 0 },\n\n    { ngx_string(\"connection_time\"), NULL, ngx_http_variable_connection_time,\n      0, NGX_HTTP_VAR_NOCACHEABLE, 0 },\n\n    { ngx_string(\"nginx_version\"), NULL, ngx_http_variable_nginx_version,\n      0, 0, 0 },\n\n    { ngx_string(\"hostname\"), NULL, ngx_http_variable_hostname,\n      0, 0, 0 },\n\n    { ngx_string(\"pid\"), NULL, ngx_http_variable_pid,\n      0, 0, 0 },\n\n    { ngx_string(\"msec\"), NULL, ngx_http_variable_msec,\n      0, NGX_HTTP_VAR_NOCACHEABLE, 0 },\n\n#if (T_NGX_VARS)\n    { ngx_string(\"dollar\"), NULL, ngx_http_variable_dollar,\n      0, 0, 0 },\n\n    { ngx_string(\"host_comment\"), NULL, ngx_http_variable_host_comment,\n      0, 0, 0 },\n\n    { ngx_string(\"unix_time\"), NULL, ngx_http_variable_unix_time,\n      0, NGX_HTTP_VAR_NOCACHEABLE, 0 },\n\n    { ngx_string(\"year\"), NULL, ngx_http_variable_year,\n      0, NGX_HTTP_VAR_NOCACHEABLE, 0 },\n\n    { ngx_string(\"year2\"), NULL, ngx_http_variable_year2,\n      0, NGX_HTTP_VAR_NOCACHEABLE, 0 },\n\n    { ngx_string(\"month\"), NULL, ngx_http_variable_month,\n      0, NGX_HTTP_VAR_NOCACHEABLE, 0 },\n\n    { ngx_string(\"day\"), NULL, ngx_http_variable_day,\n      0, NGX_HTTP_VAR_NOCACHEABLE, 0 },\n\n    { ngx_string(\"hour\"), NULL, ngx_http_variable_hour,\n      0, NGX_HTTP_VAR_NOCACHEABLE, 0 },\n\n    { ngx_string(\"hour12\"), NULL, ngx_http_variable_hour12,\n      0, NGX_HTTP_VAR_NOCACHEABLE, 0 },\n\n    { ngx_string(\"minute\"), NULL, ngx_http_variable_minute,\n      0, NGX_HTTP_VAR_NOCACHEABLE, 0 },\n\n    { ngx_string(\"second\"), NULL, ngx_http_variable_second,\n      0, NGX_HTTP_VAR_NOCACHEABLE, 0 },\n\n    { ngx_string(\"time_http\"), NULL, ngx_http_variable_time_http,\n      0, NGX_HTTP_VAR_NOCACHEABLE, 0 },\n#endif\n\n    { ngx_string(\"time_iso8601\"), NULL, ngx_http_variable_time_iso8601,\n      0, NGX_HTTP_VAR_NOCACHEABLE, 0 },\n\n    { ngx_string(\"time_local\"), NULL, ngx_http_variable_time_local,\n      0, NGX_HTTP_VAR_NOCACHEABLE, 0 },\n\n#if (NGX_HAVE_TCP_INFO)\n    { ngx_string(\"tcpinfo_rtt\"), NULL, ngx_http_variable_tcpinfo,\n      0, NGX_HTTP_VAR_NOCACHEABLE, 0 },\n\n    { ngx_string(\"tcpinfo_rttvar\"), NULL, ngx_http_variable_tcpinfo,\n      1, NGX_HTTP_VAR_NOCACHEABLE, 0 },\n\n    { ngx_string(\"tcpinfo_snd_cwnd\"), NULL, ngx_http_variable_tcpinfo,\n      2, NGX_HTTP_VAR_NOCACHEABLE, 0 },\n\n    { ngx_string(\"tcpinfo_rcv_space\"), NULL, ngx_http_variable_tcpinfo,\n      3, NGX_HTTP_VAR_NOCACHEABLE, 0 },\n#endif\n\n    { ngx_string(\"http_\"), NULL, ngx_http_variable_unknown_header_in,\n      0, NGX_HTTP_VAR_PREFIX, 0 },\n\n    { ngx_string(\"sent_http_\"), NULL, ngx_http_variable_unknown_header_out,\n      0, NGX_HTTP_VAR_PREFIX, 0 },\n\n    { ngx_string(\"sent_trailer_\"), NULL, ngx_http_variable_unknown_trailer_out,\n      0, NGX_HTTP_VAR_PREFIX, 0 },\n\n    { ngx_string(\"cookie_\"), NULL, ngx_http_variable_cookie,\n      0, NGX_HTTP_VAR_PREFIX, 0 },\n\n    { ngx_string(\"arg_\"), NULL, ngx_http_variable_argument,\n      0, NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_PREFIX, 0 },\n#if (T_NGX_VARS)\n\n    { ngx_string(\"base64_decode_\"), NULL, ngx_http_variable_decode_base64,\n      0, NGX_HTTP_VAR_PREFIX, 0 },\n\n    { ngx_string(\"md5_encode_\"), NULL, ngx_http_variable_md5,\n      0, NGX_HTTP_VAR_PREFIX, 0 },\n\n    { ngx_string(\"escape_uri_\"), NULL, ngx_http_variable_escape_uri,\n      0, NGX_HTTP_VAR_PREFIX, 0 },\n\n    { ngx_string(\"ascii_\"), NULL, ngx_http_variable_ascii,\n      0, NGX_HTTP_VAR_PREFIX, 0 },\n\n    { ngx_string(\"sent_cookie_\"), NULL, ngx_http_variable_sent_cookie,\n      0, NGX_HTTP_VAR_PREFIX, 0 },\n\n#endif\n\n#if (T_NGX_REQUEST_START_TIME)\n    { ngx_string(\"request_start_time\"), NULL, ngx_http_variable_request_start_time,\n      0, NGX_HTTP_VAR_NOCACHEABLE, 0 },\n#endif\n\n      ngx_http_null_variable\n};\n\n\nngx_http_variable_value_t  ngx_http_variable_null_value =\n    ngx_http_variable(\"\");\nngx_http_variable_value_t  ngx_http_variable_true_value =\n    ngx_http_variable(\"1\");\n\n\nstatic ngx_uint_t  ngx_http_variable_depth = 100;\n\n\nngx_http_variable_t *\nngx_http_add_variable(ngx_conf_t *cf, ngx_str_t *name, ngx_uint_t flags)\n{\n    ngx_int_t                   rc;\n    ngx_uint_t                  i;\n    ngx_hash_key_t             *key;\n    ngx_http_variable_t        *v;\n    ngx_http_core_main_conf_t  *cmcf;\n\n    if (name->len == 0) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid variable name \\\"$\\\"\");\n        return NULL;\n    }\n\n    if (flags & NGX_HTTP_VAR_PREFIX) {\n        return ngx_http_add_prefix_variable(cf, name, flags);\n    }\n\n    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);\n\n    key = cmcf->variables_keys->keys.elts;\n    for (i = 0; i < cmcf->variables_keys->keys.nelts; i++) {\n        if (name->len != key[i].key.len\n            || ngx_strncasecmp(name->data, key[i].key.data, name->len) != 0)\n        {\n            continue;\n        }\n\n        v = key[i].value;\n\n        if (!(v->flags & NGX_HTTP_VAR_CHANGEABLE)) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"the duplicate \\\"%V\\\" variable\", name);\n            return NULL;\n        }\n\n        if (!(flags & NGX_HTTP_VAR_WEAK)) {\n            v->flags &= ~NGX_HTTP_VAR_WEAK;\n        }\n\n        return v;\n    }\n\n    v = ngx_palloc(cf->pool, sizeof(ngx_http_variable_t));\n    if (v == NULL) {\n        return NULL;\n    }\n\n    v->name.len = name->len;\n    v->name.data = ngx_pnalloc(cf->pool, name->len);\n    if (v->name.data == NULL) {\n        return NULL;\n    }\n\n    ngx_strlow(v->name.data, name->data, name->len);\n\n    v->set_handler = NULL;\n    v->get_handler = NULL;\n    v->data = 0;\n    v->flags = flags;\n    v->index = 0;\n\n    rc = ngx_hash_add_key(cmcf->variables_keys, &v->name, v, 0);\n\n    if (rc == NGX_ERROR) {\n        return NULL;\n    }\n\n    if (rc == NGX_BUSY) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"conflicting variable name \\\"%V\\\"\", name);\n        return NULL;\n    }\n\n    return v;\n}\n\n\nstatic ngx_http_variable_t *\nngx_http_add_prefix_variable(ngx_conf_t *cf, ngx_str_t *name, ngx_uint_t flags)\n{\n    ngx_uint_t                  i;\n    ngx_http_variable_t        *v;\n    ngx_http_core_main_conf_t  *cmcf;\n\n    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);\n\n    v = cmcf->prefix_variables.elts;\n    for (i = 0; i < cmcf->prefix_variables.nelts; i++) {\n        if (name->len != v[i].name.len\n            || ngx_strncasecmp(name->data, v[i].name.data, name->len) != 0)\n        {\n            continue;\n        }\n\n        v = &v[i];\n\n        if (!(v->flags & NGX_HTTP_VAR_CHANGEABLE)) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"the duplicate \\\"%V\\\" variable\", name);\n            return NULL;\n        }\n\n        if (!(flags & NGX_HTTP_VAR_WEAK)) {\n            v->flags &= ~NGX_HTTP_VAR_WEAK;\n        }\n\n        return v;\n    }\n\n    v = ngx_array_push(&cmcf->prefix_variables);\n    if (v == NULL) {\n        return NULL;\n    }\n\n    v->name.len = name->len;\n    v->name.data = ngx_pnalloc(cf->pool, name->len);\n    if (v->name.data == NULL) {\n        return NULL;\n    }\n\n    ngx_strlow(v->name.data, name->data, name->len);\n\n    v->set_handler = NULL;\n    v->get_handler = NULL;\n    v->data = 0;\n    v->flags = flags;\n    v->index = 0;\n\n    return v;\n}\n\n\nngx_int_t\nngx_http_get_variable_index(ngx_conf_t *cf, ngx_str_t *name)\n{\n    ngx_uint_t                  i;\n    ngx_http_variable_t        *v;\n    ngx_http_core_main_conf_t  *cmcf;\n\n    if (name->len == 0) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid variable name \\\"$\\\"\");\n        return NGX_ERROR;\n    }\n\n    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);\n\n    v = cmcf->variables.elts;\n\n    if (v == NULL) {\n        if (ngx_array_init(&cmcf->variables, cf->pool, 4,\n                           sizeof(ngx_http_variable_t))\n            != NGX_OK)\n        {\n            return NGX_ERROR;\n        }\n\n    } else {\n        for (i = 0; i < cmcf->variables.nelts; i++) {\n            if (name->len != v[i].name.len\n                || ngx_strncasecmp(name->data, v[i].name.data, name->len) != 0)\n            {\n                continue;\n            }\n\n            return i;\n        }\n    }\n\n    v = ngx_array_push(&cmcf->variables);\n    if (v == NULL) {\n        return NGX_ERROR;\n    }\n\n    v->name.len = name->len;\n    v->name.data = ngx_pnalloc(cf->pool, name->len);\n    if (v->name.data == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_strlow(v->name.data, name->data, name->len);\n\n    v->set_handler = NULL;\n    v->get_handler = NULL;\n    v->data = 0;\n    v->flags = 0;\n    v->index = cmcf->variables.nelts - 1;\n\n    return v->index;\n}\n\n\nngx_http_variable_value_t *\nngx_http_get_indexed_variable(ngx_http_request_t *r, ngx_uint_t index)\n{\n    ngx_http_variable_t        *v;\n    ngx_http_core_main_conf_t  *cmcf;\n\n    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);\n\n    if (cmcf->variables.nelts <= index) {\n        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,\n                      \"unknown variable index: %ui\", index);\n        return NULL;\n    }\n\n    if (r->variables[index].not_found || r->variables[index].valid) {\n        return &r->variables[index];\n    }\n\n    v = cmcf->variables.elts;\n\n    if (ngx_http_variable_depth == 0) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"cycle while evaluating variable \\\"%V\\\"\",\n                      &v[index].name);\n        return NULL;\n    }\n\n    ngx_http_variable_depth--;\n\n    if (v[index].get_handler(r, &r->variables[index], v[index].data)\n        == NGX_OK)\n    {\n        ngx_http_variable_depth++;\n\n        if (v[index].flags & NGX_HTTP_VAR_NOCACHEABLE) {\n            r->variables[index].no_cacheable = 1;\n        }\n\n        return &r->variables[index];\n    }\n\n    ngx_http_variable_depth++;\n\n    r->variables[index].valid = 0;\n    r->variables[index].not_found = 1;\n\n    return NULL;\n}\n\n\nngx_http_variable_value_t *\nngx_http_get_flushed_variable(ngx_http_request_t *r, ngx_uint_t index)\n{\n    ngx_http_variable_value_t  *v;\n\n    v = &r->variables[index];\n\n    if (v->valid || v->not_found) {\n        if (!v->no_cacheable) {\n            return v;\n        }\n\n        v->valid = 0;\n        v->not_found = 0;\n    }\n\n    return ngx_http_get_indexed_variable(r, index);\n}\n\n\nngx_http_variable_value_t *\nngx_http_get_variable(ngx_http_request_t *r, ngx_str_t *name, ngx_uint_t key)\n{\n    size_t                      len;\n    ngx_uint_t                  i, n;\n    ngx_http_variable_t        *v;\n    ngx_http_variable_value_t  *vv;\n    ngx_http_core_main_conf_t  *cmcf;\n\n    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);\n\n    v = ngx_hash_find(&cmcf->variables_hash, key, name->data, name->len);\n\n    if (v) {\n        if (v->flags & NGX_HTTP_VAR_INDEXED) {\n            return ngx_http_get_flushed_variable(r, v->index);\n        }\n\n        if (ngx_http_variable_depth == 0) {\n            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                          \"cycle while evaluating variable \\\"%V\\\"\", name);\n            return NULL;\n        }\n\n        ngx_http_variable_depth--;\n\n        vv = ngx_palloc(r->pool, sizeof(ngx_http_variable_value_t));\n\n        if (vv && v->get_handler(r, vv, v->data) == NGX_OK) {\n            ngx_http_variable_depth++;\n            return vv;\n        }\n\n        ngx_http_variable_depth++;\n        return NULL;\n    }\n\n    vv = ngx_palloc(r->pool, sizeof(ngx_http_variable_value_t));\n    if (vv == NULL) {\n        return NULL;\n    }\n\n    len = 0;\n\n    v = cmcf->prefix_variables.elts;\n    n = cmcf->prefix_variables.nelts;\n\n    for (i = 0; i < cmcf->prefix_variables.nelts; i++) {\n        if (name->len >= v[i].name.len && name->len > len\n            && ngx_strncmp(name->data, v[i].name.data, v[i].name.len) == 0)\n        {\n            len = v[i].name.len;\n            n = i;\n        }\n    }\n\n    if (n != cmcf->prefix_variables.nelts) {\n        if (v[n].get_handler(r, vv, (uintptr_t) name) == NGX_OK) {\n            return vv;\n        }\n\n        return NULL;\n    }\n\n    vv->not_found = 1;\n\n    return vv;\n}\n\n\nstatic ngx_int_t\nngx_http_variable_request(ngx_http_request_t *r, ngx_http_variable_value_t *v,\n    uintptr_t data)\n{\n    ngx_str_t  *s;\n\n    s = (ngx_str_t *) ((char *) r + data);\n\n    if (s->data) {\n        v->len = s->len;\n        v->valid = 1;\n        v->no_cacheable = 0;\n        v->not_found = 0;\n        v->data = s->data;\n\n    } else {\n        v->not_found = 1;\n    }\n\n    return NGX_OK;\n}\n\n\n#if 0\n\nstatic void\nngx_http_variable_request_set(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    ngx_str_t  *s;\n\n    s = (ngx_str_t *) ((char *) r + data);\n\n    s->len = v->len;\n    s->data = v->data;\n}\n\n#endif\n\n\nstatic ngx_int_t\nngx_http_variable_request_get_size(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    size_t  *sp;\n\n    sp = (size_t *) ((char *) r + data);\n\n    v->data = ngx_pnalloc(r->pool, NGX_SIZE_T_LEN);\n    if (v->data == NULL) {\n        return NGX_ERROR;\n    }\n\n    v->len = ngx_sprintf(v->data, \"%uz\", *sp) - v->data;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    return NGX_OK;\n}\n\n#if (T_HTTP_UPSTREAM_TIMEOUT_VAR)\nstatic ngx_int_t\nngx_http_variable_request_get_time(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    ngx_msec_t  *tp;\n\n    tp = (ngx_msec_t *) ((char *) r + data);\n\n    v->data = ngx_pnalloc(r->pool, NGX_INT_T_LEN);\n    if (v->data == NULL) {\n        return NGX_ERROR;\n    }\n\n    v->len = ngx_sprintf(v->data, \"%M\", *tp) - v->data;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    return NGX_OK;\n}\n\nstatic void\nngx_http_variable_request_set_time(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    ngx_msec_t    t, *tp;\n    ngx_str_t     val;\n\n    if (v->not_found || v->len == 0) {\n        return;\n    }\n\n    val.len = v->len;\n    val.data = v->data;\n\n    t = ngx_parse_time(&val, 0);\n\n    if (t == (ngx_msec_t) NGX_ERROR) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"invalid time \\\"%V\\\"\", &val);\n        return;\n    }\n\n    tp = (ngx_msec_t *) ((char *) r + data);\n\n    *tp = t;\n\n    return;\n}\n#endif\n\n\nstatic ngx_int_t\nngx_http_variable_header(ngx_http_request_t *r, ngx_http_variable_value_t *v,\n    uintptr_t data)\n{\n    return ngx_http_variable_headers_internal(r, v, data, ',');\n}\n\n\nstatic ngx_int_t\nngx_http_variable_cookies(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    return ngx_http_variable_headers_internal(r, v, data, ';');\n}\n\n\nstatic ngx_int_t\nngx_http_variable_headers_internal(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data, u_char sep)\n{\n    size_t            len;\n    u_char           *p;\n    ngx_table_elt_t  *h, *th;\n\n    h = *(ngx_table_elt_t **) ((char *) r + data);\n\n    len = 0;\n\n    for (th = h; th; th = th->next) {\n\n        if (th->hash == 0) {\n            continue;\n        }\n\n        len += th->value.len + 2;\n    }\n\n    if (len == 0) {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    len -= 2;\n\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    if (h->next == NULL) {\n        v->len = h->value.len;\n        v->data = h->value.data;\n\n        return NGX_OK;\n    }\n\n    p = ngx_pnalloc(r->pool, len);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    v->len = len;\n    v->data = p;\n\n    for (th = h; th; th = th->next) {\n\n        if (th->hash == 0) {\n            continue;\n        }\n\n        p = ngx_copy(p, th->value.data, th->value.len);\n\n        if (th->next == NULL) {\n            break;\n        }\n\n        *p++ = sep; *p++ = ' ';\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_variable_unknown_header_in(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    return ngx_http_variable_unknown_header(r, v, (ngx_str_t *) data,\n                                            &r->headers_in.headers.part,\n                                            sizeof(\"http_\") - 1);\n}\n\n\nstatic ngx_int_t\nngx_http_variable_unknown_header_out(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    return ngx_http_variable_unknown_header(r, v, (ngx_str_t *) data,\n                                            &r->headers_out.headers.part,\n                                            sizeof(\"sent_http_\") - 1);\n}\n\n\nstatic ngx_int_t\nngx_http_variable_unknown_trailer_out(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    return ngx_http_variable_unknown_header(r, v, (ngx_str_t *) data,\n                                            &r->headers_out.trailers.part,\n                                            sizeof(\"sent_trailer_\") - 1);\n}\n\n\nngx_int_t\nngx_http_variable_unknown_header(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, ngx_str_t *var,\n    ngx_list_part_t *part, size_t prefix)\n{\n    u_char           *p, ch;\n    size_t            len;\n    ngx_uint_t        i, n;\n    ngx_table_elt_t  *header, *h, **ph;\n\n    ph = &h;\n#if (NGX_SUPPRESS_WARN)\n    len = 0;\n#endif\n\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 (header[i].hash == 0) {\n            continue;\n        }\n\n        if (header[i].key.len != var->len - prefix) {\n            continue;\n        }\n\n        for (n = 0; n < var->len - prefix; n++) {\n            ch = header[i].key.data[n];\n\n            if (ch >= 'A' && ch <= 'Z') {\n                ch |= 0x20;\n\n            } else if (ch == '-') {\n                ch = '_';\n            }\n\n            if (var->data[n + prefix] != ch) {\n                break;\n            }\n        }\n\n        if (n != var->len - prefix) {\n            continue;\n        }\n\n        len += header[i].value.len + 2;\n\n        *ph = &header[i];\n        ph = &header[i].next;\n    }\n\n    *ph = NULL;\n\n    if (h == NULL) {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    len -= 2;\n\n    if (h->next == NULL) {\n\n        v->len = h->value.len;\n        v->valid = 1;\n        v->no_cacheable = 0;\n        v->not_found = 0;\n        v->data = h->value.data;\n\n        return NGX_OK;\n    }\n\n    p = ngx_pnalloc(r->pool, len);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    v->len = len;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = p;\n\n    for ( ;; ) {\n\n        p = ngx_copy(p, h->value.data, h->value.len);\n\n        if (h->next == NULL) {\n            break;\n        }\n\n        *p++ = ','; *p++ = ' ';\n\n        h = h->next;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_variable_request_line(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    u_char  *p, *s;\n\n    s = r->request_line.data;\n\n    if (s == NULL) {\n        s = r->request_start;\n\n        if (s == NULL) {\n            v->not_found = 1;\n            return NGX_OK;\n        }\n\n        for (p = s; p < r->header_in->last; p++) {\n            if (*p == CR || *p == LF) {\n                break;\n            }\n        }\n\n        r->request_line.len = p - s;\n        r->request_line.data = s;\n    }\n\n    v->len = r->request_line.len;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = s;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_variable_cookie(ngx_http_request_t *r, ngx_http_variable_value_t *v,\n    uintptr_t data)\n{\n    ngx_str_t *name = (ngx_str_t *) data;\n\n    ngx_str_t  cookie, s;\n\n    s.len = name->len - (sizeof(\"cookie_\") - 1);\n    s.data = name->data + sizeof(\"cookie_\") - 1;\n\n    if (ngx_http_parse_multi_header_lines(r, r->headers_in.cookie, &s, &cookie)\n        == NULL)\n    {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    v->len = cookie.len;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = cookie.data;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_variable_argument(ngx_http_request_t *r, ngx_http_variable_value_t *v,\n    uintptr_t data)\n{\n    ngx_str_t *name = (ngx_str_t *) data;\n\n    u_char     *arg;\n    size_t      len;\n    ngx_str_t   value;\n\n    len = name->len - (sizeof(\"arg_\") - 1);\n    arg = name->data + sizeof(\"arg_\") - 1;\n\n    if (len == 0 || ngx_http_arg(r, arg, len, &value) != NGX_OK) {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    v->data = value.data;\n    v->len = value.len;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    return NGX_OK;\n}\n\n\n#if (NGX_HAVE_TCP_INFO)\n\nstatic ngx_int_t\nngx_http_variable_tcpinfo(ngx_http_request_t *r, ngx_http_variable_value_t *v,\n    uintptr_t data)\n{\n    struct tcp_info  ti;\n    socklen_t        len;\n    uint32_t         value;\n\n    len = sizeof(struct tcp_info);\n    if (getsockopt(r->connection->fd, IPPROTO_TCP, TCP_INFO, &ti, &len) == -1) {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    v->data = ngx_pnalloc(r->pool, NGX_INT32_LEN);\n    if (v->data == NULL) {\n        return NGX_ERROR;\n    }\n\n    switch (data) {\n    case 0:\n        value = ti.tcpi_rtt;\n        break;\n\n    case 1:\n        value = ti.tcpi_rttvar;\n        break;\n\n    case 2:\n        value = ti.tcpi_snd_cwnd;\n        break;\n\n    case 3:\n        value = ti.tcpi_rcv_space;\n        break;\n\n    /* suppress warning */\n    default:\n        value = 0;\n        break;\n    }\n\n    v->len = ngx_sprintf(v->data, \"%uD\", value) - v->data;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    return NGX_OK;\n}\n\n#endif\n\n#if (T_NGX_REQUEST_START_TIME)\nstatic char  *months[] = { \"Jan\", \"Feb\", \"Mar\", \"Apr\", \"May\", \"Jun\",\n                           \"Jul\", \"Aug\", \"Sep\", \"Oct\", \"Nov\", \"Dec\" };\n\nstatic ngx_int_t\nngx_http_variable_request_start_time(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    u_char          *p;\n    ngx_time_t      *tp;\n    ngx_tm_t         tm;\n\n    p = ngx_pnalloc(r->pool, sizeof(\"28/Sep/1970:12:00:00 +0600\") - 1);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    tp = ngx_timeofday();\n    ngx_gmtime(r->start_sec + tp->gmtoff * 60, &tm);\n\n    /*The time that the first request pass in*/\n    (void) ngx_sprintf(p, \"%02d/%s/%d:%02d:%02d:%02d %c%02i%02i\",\n                       tm.ngx_tm_mday, months[tm.ngx_tm_mon - 1],\n                       tm.ngx_tm_year, tm.ngx_tm_hour,\n                       tm.ngx_tm_min, tm.ngx_tm_sec,\n                       tp->gmtoff < 0 ? '-' : '+',\n                       ngx_abs(tp->gmtoff / 60), ngx_abs(tp->gmtoff % 60));\n\n    v->len = sizeof(\"28/Sep/1970:12:00:00 +0600\") - 1;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = p;\n\n    return NGX_OK;\n}\n#endif\n\nstatic ngx_int_t\nngx_http_variable_content_length(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    u_char  *p;\n\n    if (r->headers_in.content_length) {\n        v->len = r->headers_in.content_length->value.len;\n        v->data = r->headers_in.content_length->value.data;\n        v->valid = 1;\n        v->no_cacheable = 0;\n        v->not_found = 0;\n\n    } else if (r->reading_body) {\n        v->not_found = 1;\n        v->no_cacheable = 1;\n\n    } else if (r->headers_in.content_length_n >= 0) {\n        p = ngx_pnalloc(r->pool, NGX_OFF_T_LEN);\n        if (p == NULL) {\n            return NGX_ERROR;\n        }\n\n        v->len = ngx_sprintf(p, \"%O\", r->headers_in.content_length_n) - p;\n        v->data = p;\n        v->valid = 1;\n        v->no_cacheable = 0;\n        v->not_found = 0;\n\n    } else if (r->headers_in.chunked) {\n        v->not_found = 1;\n        v->no_cacheable = 1;\n\n    } else {\n        v->not_found = 1;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_variable_host(ngx_http_request_t *r, ngx_http_variable_value_t *v,\n    uintptr_t data)\n{\n    ngx_http_core_srv_conf_t  *cscf;\n\n    if (r->headers_in.server.len) {\n        v->len = r->headers_in.server.len;\n        v->data = r->headers_in.server.data;\n\n    } else {\n        cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);\n\n        v->len = cscf->server_name.len;\n        v->data = cscf->server_name.data;\n    }\n\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_variable_binary_remote_addr(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    struct sockaddr_in   *sin;\n#if (NGX_HAVE_INET6)\n    struct sockaddr_in6  *sin6;\n#endif\n\n    switch (r->connection->sockaddr->sa_family) {\n\n#if (NGX_HAVE_INET6)\n    case AF_INET6:\n        sin6 = (struct sockaddr_in6 *) r->connection->sockaddr;\n\n        v->len = sizeof(struct in6_addr);\n        v->valid = 1;\n        v->no_cacheable = 0;\n        v->not_found = 0;\n        v->data = sin6->sin6_addr.s6_addr;\n\n        break;\n#endif\n\n#if (NGX_HAVE_UNIX_DOMAIN)\n    case AF_UNIX:\n\n        v->len = r->connection->addr_text.len;\n        v->valid = 1;\n        v->no_cacheable = 0;\n        v->not_found = 0;\n        v->data = r->connection->addr_text.data;\n\n        break;\n#endif\n\n    default: /* AF_INET */\n        sin = (struct sockaddr_in *) r->connection->sockaddr;\n\n        v->len = sizeof(in_addr_t);\n        v->valid = 1;\n        v->no_cacheable = 0;\n        v->not_found = 0;\n        v->data = (u_char *) &sin->sin_addr;\n\n        break;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_variable_remote_addr(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    v->len = r->connection->addr_text.len;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = r->connection->addr_text.data;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_variable_remote_port(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    ngx_uint_t  port;\n\n    v->len = 0;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    v->data = ngx_pnalloc(r->pool, sizeof(\"65535\") - 1);\n    if (v->data == NULL) {\n        return NGX_ERROR;\n    }\n\n    port = ngx_inet_get_port(r->connection->sockaddr);\n\n    if (port > 0 && port < 65536) {\n        v->len = ngx_sprintf(v->data, \"%ui\", port) - v->data;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_variable_proxy_protocol_addr(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    ngx_str_t             *addr;\n    ngx_proxy_protocol_t  *pp;\n\n    pp = r->connection->proxy_protocol;\n    if (pp == NULL) {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    addr = (ngx_str_t *) ((char *) pp + data);\n\n    v->len = addr->len;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = addr->data;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_variable_proxy_protocol_port(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    ngx_uint_t             port;\n    ngx_proxy_protocol_t  *pp;\n\n    pp = r->connection->proxy_protocol;\n    if (pp == NULL) {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    v->len = 0;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    v->data = ngx_pnalloc(r->pool, sizeof(\"65535\") - 1);\n    if (v->data == NULL) {\n        return NGX_ERROR;\n    }\n\n    port = *(in_port_t *) ((char *) pp + data);\n\n    if (port > 0 && port < 65536) {\n        v->len = ngx_sprintf(v->data, \"%ui\", port) - v->data;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_variable_proxy_protocol_tlv(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    ngx_str_t *name = (ngx_str_t *) data;\n\n    ngx_int_t  rc;\n    ngx_str_t  tlv, value;\n\n    tlv.len = name->len - (sizeof(\"proxy_protocol_tlv_\") - 1);\n    tlv.data = name->data + sizeof(\"proxy_protocol_tlv_\") - 1;\n\n    rc = ngx_proxy_protocol_get_tlv(r->connection, &tlv, &value);\n\n    if (rc == NGX_ERROR) {\n        return NGX_ERROR;\n    }\n\n    if (rc == NGX_DECLINED) {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    v->len = value.len;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = value.data;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_variable_server_addr(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    ngx_str_t  s;\n    u_char     addr[NGX_SOCKADDR_STRLEN];\n\n    s.len = NGX_SOCKADDR_STRLEN;\n    s.data = addr;\n\n    if (ngx_connection_local_sockaddr(r->connection, &s, 0) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    s.data = ngx_pnalloc(r->pool, s.len);\n    if (s.data == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_memcpy(s.data, addr, s.len);\n\n    v->len = s.len;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = s.data;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_variable_server_port(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    ngx_uint_t  port;\n\n    v->len = 0;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    if (ngx_connection_local_sockaddr(r->connection, NULL, 0) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    v->data = ngx_pnalloc(r->pool, sizeof(\"65535\") - 1);\n    if (v->data == NULL) {\n        return NGX_ERROR;\n    }\n\n    port = ngx_inet_get_port(r->connection->local_sockaddr);\n\n    if (port > 0 && port < 65536) {\n        v->len = ngx_sprintf(v->data, \"%ui\", port) - v->data;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_variable_scheme(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n#if (NGX_HTTP_SSL)\n\n    if (r->connection->ssl) {\n        v->len = sizeof(\"https\") - 1;\n        v->valid = 1;\n        v->no_cacheable = 0;\n        v->not_found = 0;\n        v->data = (u_char *) \"https\";\n\n        return NGX_OK;\n    }\n\n#endif\n\n#if (T_NGX_XQUIC)\n\n    if (r->xqstream) {\n        if (r->schema_end - r->schema_start == 5\n            && ngx_strncmp(r->schema_start, \"https\", 5) == 0) {\n            v->len = sizeof(\"https\") - 1;\n            v->valid = 1;\n            v->no_cacheable = 0;\n            v->not_found = 0;\n            v->data = (u_char *) \"https\";\n\n            return NGX_OK;\n        }\n    }\n\n#endif\n\n    v->len = sizeof(\"http\") - 1;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = (u_char *) \"http\";\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_variable_https(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n#if (NGX_HTTP_SSL)\n\n    if (r->connection->ssl) {\n        v->len = sizeof(\"on\") - 1;\n        v->valid = 1;\n        v->no_cacheable = 0;\n        v->not_found = 0;\n        v->data = (u_char *) \"on\";\n\n        return NGX_OK;\n    }\n\n#endif\n\n    *v = ngx_http_variable_null_value;\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_variable_set_args(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    r->args.len = v->len;\n    r->args.data = v->data;\n    r->valid_unparsed_uri = 0;\n}\n\n\nstatic ngx_int_t\nngx_http_variable_is_args(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    if (r->args.len == 0) {\n        *v = ngx_http_variable_null_value;\n        return NGX_OK;\n    }\n\n    v->len = 1;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = (u_char *) \"?\";\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_variable_document_root(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    ngx_str_t                  path;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    if (clcf->root_lengths == NULL) {\n        v->len = clcf->root.len;\n        v->valid = 1;\n        v->no_cacheable = 0;\n        v->not_found = 0;\n        v->data = clcf->root.data;\n\n    } else {\n        if (ngx_http_script_run(r, &path, clcf->root_lengths->elts, 0,\n                                clcf->root_values->elts)\n            == NULL)\n        {\n            return NGX_ERROR;\n        }\n\n        if (ngx_get_full_name(r->pool, (ngx_str_t *) &ngx_cycle->prefix, &path)\n            != NGX_OK)\n        {\n            return NGX_ERROR;\n        }\n\n        v->len = path.len;\n        v->valid = 1;\n        v->no_cacheable = 0;\n        v->not_found = 0;\n        v->data = path.data;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_variable_realpath_root(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    u_char                    *real;\n    size_t                     len;\n    ngx_str_t                  path;\n    ngx_http_core_loc_conf_t  *clcf;\n#if (NGX_HAVE_MAX_PATH)\n    u_char                     buffer[NGX_MAX_PATH];\n#endif\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    if (clcf->root_lengths == NULL) {\n        path = clcf->root;\n\n    } else {\n        if (ngx_http_script_run(r, &path, clcf->root_lengths->elts, 1,\n                                clcf->root_values->elts)\n            == NULL)\n        {\n            return NGX_ERROR;\n        }\n\n        path.data[path.len - 1] = '\\0';\n\n        if (ngx_get_full_name(r->pool, (ngx_str_t *) &ngx_cycle->prefix, &path)\n            != NGX_OK)\n        {\n            return NGX_ERROR;\n        }\n    }\n\n#if (NGX_HAVE_MAX_PATH)\n    real = buffer;\n#else\n    real = NULL;\n#endif\n\n    real = ngx_realpath(path.data, real);\n\n    if (real == NULL) {\n        ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,\n                      ngx_realpath_n \" \\\"%s\\\" failed\", path.data);\n        return NGX_ERROR;\n    }\n\n    len = ngx_strlen(real);\n\n    v->data = ngx_pnalloc(r->pool, len);\n    if (v->data == NULL) {\n#if !(NGX_HAVE_MAX_PATH)\n        ngx_free(real);\n#endif\n        return NGX_ERROR;\n    }\n\n    v->len = len;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    ngx_memcpy(v->data, real, len);\n\n#if !(NGX_HAVE_MAX_PATH)\n    ngx_free(real);\n#endif\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_variable_request_filename(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    size_t     root;\n    ngx_str_t  path;\n\n    if (ngx_http_map_uri_to_path(r, &path, &root, 0) == NULL) {\n        return NGX_ERROR;\n    }\n\n    /* ngx_http_map_uri_to_path() allocates memory for terminating '\\0' */\n\n    v->len = path.len - 1;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = path.data;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_variable_server_name(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    ngx_http_core_srv_conf_t  *cscf;\n\n    cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);\n\n    v->len = cscf->server_name.len;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = cscf->server_name.data;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_variable_request_method(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    if (r->main->method_name.data) {\n        v->len = r->main->method_name.len;\n        v->valid = 1;\n        v->no_cacheable = 0;\n        v->not_found = 0;\n        v->data = r->main->method_name.data;\n\n    } else {\n        v->not_found = 1;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_variable_remote_user(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    ngx_int_t  rc;\n\n    rc = ngx_http_auth_basic_user(r);\n\n    if (rc == NGX_DECLINED) {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    if (rc == NGX_ERROR) {\n        return NGX_ERROR;\n    }\n\n    v->len = r->headers_in.user.len;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = r->headers_in.user.data;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_variable_bytes_sent(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    u_char  *p;\n\n    p = ngx_pnalloc(r->pool, NGX_OFF_T_LEN);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    v->len = ngx_sprintf(p, \"%O\", r->connection->sent) - p;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = p;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_variable_body_bytes_sent(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    off_t    sent;\n    u_char  *p;\n\n    sent = r->connection->sent - r->header_size;\n\n    if (sent < 0) {\n        sent = 0;\n    }\n\n    p = ngx_pnalloc(r->pool, NGX_OFF_T_LEN);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    v->len = ngx_sprintf(p, \"%O\", sent) - p;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = p;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_variable_pipe(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    v->data = (u_char *) (r->pipeline ? \"p\" : \".\");\n    v->len = 1;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_variable_status(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    ngx_uint_t  status;\n\n    v->data = ngx_pnalloc(r->pool, NGX_INT_T_LEN);\n    if (v->data == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (r->err_status) {\n        status = r->err_status;\n\n    } else if (r->headers_out.status) {\n        status = r->headers_out.status;\n\n    } else if (r->http_version == NGX_HTTP_VERSION_9) {\n        status = 9;\n\n    } else {\n        status = 0;\n    }\n\n    v->len = ngx_sprintf(v->data, \"%03ui\", status) - v->data;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_variable_sent_content_type(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    if (r->headers_out.content_type.len) {\n        v->len = r->headers_out.content_type.len;\n        v->valid = 1;\n        v->no_cacheable = 0;\n        v->not_found = 0;\n        v->data = r->headers_out.content_type.data;\n\n    } else {\n        v->not_found = 1;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_variable_sent_content_length(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    u_char  *p;\n\n    if (r->headers_out.content_length) {\n        v->len = r->headers_out.content_length->value.len;\n        v->valid = 1;\n        v->no_cacheable = 0;\n        v->not_found = 0;\n        v->data = r->headers_out.content_length->value.data;\n\n        return NGX_OK;\n    }\n\n    if (r->headers_out.content_length_n >= 0) {\n        p = ngx_pnalloc(r->pool, NGX_OFF_T_LEN);\n        if (p == NULL) {\n            return NGX_ERROR;\n        }\n\n        v->len = ngx_sprintf(p, \"%O\", r->headers_out.content_length_n) - p;\n        v->valid = 1;\n        v->no_cacheable = 0;\n        v->not_found = 0;\n        v->data = p;\n\n        return NGX_OK;\n    }\n\n    v->not_found = 1;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_variable_sent_location(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    ngx_str_t  name;\n\n    if (r->headers_out.location) {\n        v->len = r->headers_out.location->value.len;\n        v->valid = 1;\n        v->no_cacheable = 0;\n        v->not_found = 0;\n        v->data = r->headers_out.location->value.data;\n\n        return NGX_OK;\n    }\n\n    ngx_str_set(&name, \"sent_http_location\");\n\n    return ngx_http_variable_unknown_header(r, v, &name,\n                                            &r->headers_out.headers.part,\n                                            sizeof(\"sent_http_\") - 1);\n}\n\n\nstatic ngx_int_t\nngx_http_variable_sent_last_modified(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    u_char  *p;\n\n    if (r->headers_out.last_modified) {\n        v->len = r->headers_out.last_modified->value.len;\n        v->valid = 1;\n        v->no_cacheable = 0;\n        v->not_found = 0;\n        v->data = r->headers_out.last_modified->value.data;\n\n        return NGX_OK;\n    }\n\n    if (r->headers_out.last_modified_time >= 0) {\n        p = ngx_pnalloc(r->pool, sizeof(\"Mon, 28 Sep 1970 06:00:00 GMT\") - 1);\n        if (p == NULL) {\n            return NGX_ERROR;\n        }\n\n        v->len = ngx_http_time(p, r->headers_out.last_modified_time) - p;\n        v->valid = 1;\n        v->no_cacheable = 0;\n        v->not_found = 0;\n        v->data = p;\n\n        return NGX_OK;\n    }\n\n    v->not_found = 1;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_variable_sent_connection(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    size_t   len;\n    char    *p;\n\n    if (r->headers_out.status == NGX_HTTP_SWITCHING_PROTOCOLS) {\n        len = sizeof(\"upgrade\") - 1;\n        p = \"upgrade\";\n\n    } else if (r->keepalive) {\n        len = sizeof(\"keep-alive\") - 1;\n        p = \"keep-alive\";\n\n    } else {\n        len = sizeof(\"close\") - 1;\n        p = \"close\";\n    }\n\n    v->len = len;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = (u_char *) p;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_variable_sent_keep_alive(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    u_char                    *p;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    if (r->keepalive) {\n        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n        if (clcf->keepalive_header) {\n\n            p = ngx_pnalloc(r->pool, sizeof(\"timeout=\") - 1 + NGX_TIME_T_LEN);\n            if (p == NULL) {\n                return NGX_ERROR;\n            }\n\n            v->len = ngx_sprintf(p, \"timeout=%T\", clcf->keepalive_header) - p;\n            v->valid = 1;\n            v->no_cacheable = 0;\n            v->not_found = 0;\n            v->data = p;\n\n            return NGX_OK;\n        }\n    }\n\n    v->not_found = 1;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_variable_sent_transfer_encoding(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    if (r->chunked) {\n        v->len = sizeof(\"chunked\") - 1;\n        v->valid = 1;\n        v->no_cacheable = 0;\n        v->not_found = 0;\n        v->data = (u_char *) \"chunked\";\n\n    } else {\n        v->not_found = 1;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_variable_set_limit_rate(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    ssize_t    s;\n    ngx_str_t  val;\n\n    val.len = v->len;\n    val.data = v->data;\n\n    s = ngx_parse_size(&val);\n\n    if (s == NGX_ERROR) {\n        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                      \"invalid $limit_rate \\\"%V\\\"\", &val);\n        return;\n    }\n\n    r->limit_rate = s;\n    r->limit_rate_set = 1;\n}\n\n\nstatic ngx_int_t\nngx_http_variable_request_completion(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    if (r->request_complete) {\n        v->len = 2;\n        v->valid = 1;\n        v->no_cacheable = 0;\n        v->not_found = 0;\n        v->data = (u_char *) \"OK\";\n\n        return NGX_OK;\n    }\n\n    *v = ngx_http_variable_null_value;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_variable_request_body(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    u_char       *p;\n    size_t        len;\n    ngx_buf_t    *buf;\n    ngx_chain_t  *cl;\n\n    if (r->request_body == NULL\n        || r->request_body->bufs == NULL\n        || r->request_body->temp_file)\n    {\n        v->not_found = 1;\n\n        return NGX_OK;\n    }\n\n    cl = r->request_body->bufs;\n    buf = cl->buf;\n\n    if (cl->next == NULL) {\n        v->len = buf->last - buf->pos;\n        v->valid = 1;\n        v->no_cacheable = 0;\n        v->not_found = 0;\n        v->data = buf->pos;\n\n        return NGX_OK;\n    }\n\n    len = buf->last - buf->pos;\n    cl = cl->next;\n\n    for ( /* void */ ; cl; cl = cl->next) {\n        buf = cl->buf;\n        len += buf->last - buf->pos;\n    }\n\n    p = ngx_pnalloc(r->pool, len);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    v->data = p;\n    cl = r->request_body->bufs;\n\n    for ( /* void */ ; cl; cl = cl->next) {\n        buf = cl->buf;\n        p = ngx_cpymem(p, buf->pos, buf->last - buf->pos);\n    }\n\n    v->len = len;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_variable_request_body_file(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    if (r->request_body == NULL || r->request_body->temp_file == NULL) {\n        v->not_found = 1;\n\n        return NGX_OK;\n    }\n\n    v->len = r->request_body->temp_file->file.name.len;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = r->request_body->temp_file->file.name.data;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_variable_request_length(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    u_char  *p;\n\n    p = ngx_pnalloc(r->pool, NGX_OFF_T_LEN);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    v->len = ngx_sprintf(p, \"%O\", r->request_length) - p;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = p;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_variable_request_time(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    u_char          *p;\n    ngx_time_t      *tp;\n    ngx_msec_int_t   ms;\n\n    p = ngx_pnalloc(r->pool, NGX_TIME_T_LEN + 4);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    tp = ngx_timeofday();\n\n    ms = (ngx_msec_int_t)\n             ((tp->sec - r->start_sec) * 1000 + (tp->msec - r->start_msec));\n    ms = ngx_max(ms, 0);\n\n    v->len = ngx_sprintf(p, \"%T.%03M\", (time_t) ms / 1000, ms % 1000) - p;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = p;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_variable_request_id(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    u_char  *id;\n\n#if (NGX_OPENSSL)\n    u_char   random_bytes[16];\n#endif\n\n    id = ngx_pnalloc(r->pool, 32);\n    if (id == NULL) {\n        return NGX_ERROR;\n    }\n\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    v->len = 32;\n    v->data = id;\n\n#if (NGX_OPENSSL)\n\n    if (RAND_bytes(random_bytes, 16) == 1) {\n        ngx_hex_dump(id, random_bytes, 16);\n        return NGX_OK;\n    }\n\n    ngx_ssl_error(NGX_LOG_ERR, r->connection->log, 0, \"RAND_bytes() failed\");\n\n#endif\n\n    ngx_sprintf(id, \"%08xD%08xD%08xD%08xD\",\n                (uint32_t) ngx_random(), (uint32_t) ngx_random(),\n                (uint32_t) ngx_random(), (uint32_t) ngx_random());\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_variable_connection(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    u_char  *p;\n\n    p = ngx_pnalloc(r->pool, NGX_ATOMIC_T_LEN);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    v->len = ngx_sprintf(p, \"%uA\", r->connection->number) - p;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = p;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_variable_connection_requests(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    u_char  *p;\n\n    p = ngx_pnalloc(r->pool, NGX_INT_T_LEN);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    v->len = ngx_sprintf(p, \"%ui\", r->connection->requests) - p;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = p;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_variable_connection_time(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    u_char          *p;\n    ngx_msec_int_t   ms;\n\n    p = ngx_pnalloc(r->pool, NGX_TIME_T_LEN + 4);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    ms = ngx_current_msec - r->connection->start_time;\n    ms = ngx_max(ms, 0);\n\n    v->len = ngx_sprintf(p, \"%T.%03M\", (time_t) ms / 1000, ms % 1000) - p;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = p;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_variable_nginx_version(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    v->len = sizeof(NGINX_VERSION) - 1;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = (u_char *) NGINX_VERSION;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_variable_hostname(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    v->len = ngx_cycle->hostname.len;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = ngx_cycle->hostname.data;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_variable_pid(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    u_char  *p;\n\n    p = ngx_pnalloc(r->pool, NGX_INT64_LEN);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    v->len = ngx_sprintf(p, \"%P\", ngx_pid) - p;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = p;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_variable_msec(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    u_char      *p;\n    ngx_time_t  *tp;\n\n    p = ngx_pnalloc(r->pool, NGX_TIME_T_LEN + 4);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    tp = ngx_timeofday();\n\n    v->len = ngx_sprintf(p, \"%T.%03M\", tp->sec, tp->msec) - p;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = p;\n\n    return NGX_OK;\n}\n\n\n#if (T_NGX_VARS)\nstatic ngx_int_t\nngx_http_variable_sent_cookie(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    u_char           *p, *last;\n    ngx_str_t         name;\n    ngx_uint_t        i;\n    ngx_table_elt_t  *header;\n    ngx_list_part_t  *part;\n\n    part = &r->headers_out.headers.part;\n\n    name.len = ((ngx_str_t *) data)->len - (sizeof(\"sent_cookie_\") - 1);\n    name.data = ((ngx_str_t *) data)->data + sizeof(\"sent_cookie_\") - 1;\n\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 (header[i].hash == 0) {\n            continue;\n        }\n\n        if (header[i].key.len == (sizeof(\"Set-Cookie\") - 1)\n            && ngx_strncasecmp((u_char *) \"Set-Cookie\", header[i].key.data,\n                               header[i].key.len) == 0)\n        {\n            if (name.len > header[i].value.len) {\n                continue;\n            }\n\n            if (ngx_strncasecmp(header[i].value.data, name.data, name.len)) {\n                continue;\n            }\n\n            p = header[i].value.data + name.len;\n            last = header[i].value.data + header[i].value.len;\n\n            if (*p != '=') {\n                continue;\n            }\n\n            for (v->data = ++p; p < last; p++) {\n                if (*p == ';') {\n                    break;\n                }\n            }\n\n            v->len = p - v->data;\n            v->valid = 1;\n            v->no_cacheable = 0;\n            v->not_found = 0;\n\n            return NGX_OK;\n        }\n    }\n\n    v->not_found = 1;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_variable_decode_base64(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    ngx_str_t *name = (ngx_str_t *) data;\n\n    size_t                      len;\n    u_char                     *low, *p;\n    ngx_str_t                   var, src;\n    ngx_uint_t                  hash;\n    ngx_http_variable_value_t  *vv;\n\n    len = name->len - (sizeof(\"base64_decode_\") - 1);\n\n    low = ngx_pnalloc(r->pool, len);\n    if (low == NULL) {\n        return NGX_ERROR;\n    }\n\n    p = name->data + sizeof(\"base64_decode_\") - 1;\n\n    hash = ngx_hash_strlow(low, p, len);\n\n    var.len = len;\n    var.data = low;\n\n    vv = ngx_http_get_variable(r, &var, hash);\n\n    if (vv == NULL || vv->not_found) {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    src.data = vv->data;\n    src.len = vv->len;\n\n    var.len = ngx_base64_decoded_length(src.len);\n    var.data = ngx_palloc(r->pool, var.len);\n    if (var.data == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (ngx_decode_base64(&var, &src) != NGX_OK) {\n\n        v->data = src.data;\n        v->len = src.len;\n        v->valid = 1;\n        v->no_cacheable = 0;\n        v->not_found = 0;\n\n        return NGX_OK;\n    }\n\n    v->data = var.data;\n    v->len = var.len;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_variable_md5(ngx_http_request_t *r, ngx_http_variable_value_t *v,\n    uintptr_t data)\n{\n    ngx_str_t *name = (ngx_str_t *) data;\n\n    size_t                      len;\n    u_char                     *low, *p, md5_final[16];\n    ngx_md5_t                   md5;\n    ngx_str_t                   var, src;\n    ngx_uint_t                  hash;\n    ngx_http_variable_value_t  *vv;\n\n    len = name->len - (sizeof(\"md5_encode_\") - 1);\n\n    low = ngx_pnalloc(r->pool, len);\n    if (low == NULL) {\n        return NGX_ERROR;\n    }\n\n    p = name->data + sizeof(\"md5_encode_\") - 1;\n\n    hash = ngx_hash_strlow(low, p, len);\n\n    var.len = len;\n    var.data = low;\n\n    vv = ngx_http_get_variable(r, &var, hash);\n\n    if (vv == NULL || vv->not_found) {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    src.data = vv->data;\n    src.len = vv->len;\n\n    ngx_md5_init(&md5);\n    ngx_md5_update(&md5, src.data, src.len);\n    ngx_md5_final(md5_final, &md5);\n\n    var.len = 32;\n    var.data = ngx_palloc(r->pool, var.len);\n    if (var.data == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_hex_dump(var.data, md5_final, sizeof(md5_final));\n\n    v->data = var.data;\n    v->len = var.len;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_variable_escape_uri(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    ngx_str_t *name = (ngx_str_t *) data;\n\n    size_t                      len, size;\n    u_char                     *low, *p;\n    ngx_str_t                   var, src;\n    uintptr_t                   escape;\n    ngx_uint_t                  hash;\n    ngx_http_variable_value_t  *vv;\n\n    len = name->len - (sizeof(\"escape_uri_\") - 1);\n\n    low = ngx_pnalloc(r->pool, len);\n    if (low == NULL) {\n        return NGX_ERROR;\n    }\n\n    p = name->data + sizeof(\"escape_uri_\") - 1;\n\n    hash = ngx_hash_strlow(low, p, len);\n\n    var.len = len;\n    var.data = low;\n\n    vv = ngx_http_get_variable(r, &var, hash);\n\n    if (vv == NULL || vv->not_found) {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    src.data = vv->data;\n    src.len = vv->len;\n\n    escape = 2 * ngx_escape_uri(NULL, src.data, src.len, NGX_ESCAPE_URI);\n\n    size = escape + src.len;\n\n    v->data = ngx_pnalloc(r->pool, size);\n    if (v->data == NULL) {\n        return NGX_ERROR;\n    }\n\n    p = (u_char *) ngx_escape_uri(v->data, src.data, src.len, NGX_ESCAPE_URI);\n    v->len = p - v->data;\n\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_variable_ascii(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    ngx_str_t *name = (ngx_str_t *) data;\n\n    size_t     len;\n    u_char    *p;\n    ngx_int_t  c;\n\n    len = name->len - (sizeof(\"ascii_\") - 1);\n    p = name->data + sizeof(\"ascii_\") - 1;\n\n    if (ngx_strncasecmp(p, (u_char *) \"0x\", 2) == 0) {\n\n        p = p + 2;\n        len = len - 2;\n        c = ngx_hextoi(p, len);\n\n    } else {\n\n        c = ngx_atoi(p, len);\n    }\n\n    if (c == NGX_ERROR || c > 255) {\n        return NGX_ERROR;\n    }\n\n    v->data = ngx_pnalloc(r->pool, 1);\n    if (v->data == NULL) {\n        return NGX_ERROR;\n    }\n\n    (*v->data) = (u_char) c;\n\n    v->len = 1;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_variable_full_request(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    size_t                     size;\n    u_char                    *last;\n    ngx_str_t                 *host, scheme, port;\n    ngx_uint_t                 p;\n    struct sockaddr_in        *sin;\n#if (NGX_HAVE_INET6)\n    struct sockaddr_in6       *sin6;\n#endif\n    ngx_http_core_srv_conf_t  *cscf;\n\n    cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);\n\n    ngx_str_null(&scheme);\n\n#if (NGX_HTTP_SSL)\n\n    if (r->connection->ssl) {\n        ngx_str_set(&scheme, \"https://\");\n    }\n\n#endif\n\n    if (scheme.len == 0) {\n        ngx_str_set(&scheme, \"http://\");\n    }\n\n    if (r->headers_in.server.len) {\n        host = &r->headers_in.server;\n\n    } else {\n        host = &cscf->server_name;\n    }\n\n    if (ngx_connection_local_sockaddr(r->connection, NULL, 0) != NGX_OK) {\n        goto failed;\n    }\n\n    ngx_str_null(&port);\n\n    switch (r->connection->local_sockaddr->sa_family) {\n\n#if (NGX_HAVE_INET6)\n    case AF_INET6:\n        sin6 = (struct sockaddr_in6 *) r->connection->local_sockaddr;\n        p = ntohs(sin6->sin6_port);\n        break;\n#endif\n\n    default:\n        sin = (struct sockaddr_in *) r->connection->local_sockaddr;\n        p = ntohs(sin->sin_port);\n        break;\n    }\n    if (p > 0 && p < 65536 && ((p != 80 && scheme.len == 7)\n                               || (p != 443 && scheme.len == 8)))\n    {\n        port.data = ngx_pnalloc(r->pool, sizeof(\":65535\") - 1);\n        if (port.data == NULL) {\n            goto failed;\n        }\n\n        port.len = ngx_sprintf(port.data, \":%ui\", p) - port.data;\n    }\n\n    size = scheme.len + host->len + port.len + r->unparsed_uri.len;\n\n    v->data = ngx_pnalloc(r->pool, size);\n    if (v->data == NULL) {\n        goto failed;\n    }\n\n    last = v->data;\n\n    last = ngx_cpymem(last, scheme.data, scheme.len);\n\n    last = ngx_cpymem(last, host->data, host->len);\n\n    if (port.len) {\n        last = ngx_cpymem(last, port.data, port.len);\n    }\n\n    last = ngx_cpymem(last, r->unparsed_uri.data, r->unparsed_uri.len);\n\n    v->len = size;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    return NGX_OK;\n\nfailed:\n\n    v->len = 0;\n    v->data = NULL;\n    v->valid = 0;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_variable_normalized_request(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    size_t                     size;\n    u_char                    *s, *last;\n    ngx_str_t                 *host, scheme, port;\n    ngx_uint_t                 p, len;\n    struct sockaddr_in        *sin;\n#if (NGX_HAVE_INET6)\n    struct sockaddr_in6       *sin6;\n#endif\n    ngx_http_core_srv_conf_t  *cscf;\n    enum {\n        s_usual = 0,\n        s_first,\n        s_second\n    } state;\n\n    cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);\n\n    ngx_str_null(&scheme);\n\n#if (NGX_HTTP_SSL)\n\n    if (r->connection->ssl) {\n        ngx_str_set(&scheme, \"https://\");\n    }\n\n#endif\n\n    if (scheme.len == 0) {\n        ngx_str_set(&scheme, \"http://\");\n    }\n\n    if (r->headers_in.server.len) {\n        host = &r->headers_in.server;\n\n    } else {\n        host = &cscf->server_name;\n    }\n\n    if (ngx_connection_local_sockaddr(r->connection, NULL, 0) != NGX_OK) {\n        goto failed;\n    }\n\n    ngx_str_null(&port);\n\n    switch (r->connection->local_sockaddr->sa_family) {\n\n#if (NGX_HAVE_INET6)\n    case AF_INET6:\n        sin6 = (struct sockaddr_in6 *) r->connection->local_sockaddr;\n        p = ntohs(sin6->sin6_port);\n        break;\n#endif\n\n    default:\n        sin = (struct sockaddr_in *) r->connection->local_sockaddr;\n        p = ntohs(sin->sin_port);\n        break;\n    }\n\n    if (p > 0 && p < 65536 && ((p != 80 && scheme.len == 7)\n                               || (p != 443 && scheme.len == 8)))\n    {\n        port.data = ngx_pnalloc(r->pool, sizeof(\":65535\") - 1);\n        if (port.data == NULL) {\n            goto failed;\n        }\n\n        port.len = ngx_sprintf(port.data, \":%ui\", p) - port.data;\n    }\n\n    len = r->unparsed_uri.len;\n\n    /* exclude #<fragment> part from URL */\n    s = ngx_strlchr(r->unparsed_uri.data,\n                    r->unparsed_uri.data + len, '#');\n    if (s != NULL) {\n        len = s - r->unparsed_uri.data;\n    }\n\n    size = scheme.len + host->len + port.len + len;\n\n    v->data = ngx_pnalloc(r->pool, size);\n    if (v->data == NULL) {\n        goto failed;\n    }\n\n    last = v->data;\n\n    last = ngx_cpymem(last, scheme.data, scheme.len);\n\n    last = ngx_cpymem(last, host->data, host->len);\n\n    if (port.len) {\n        last = ngx_cpymem(last, port.data, port.len);\n    }\n\n    last = ngx_cpymem(last, r->unparsed_uri.data, len);\n\n    /* convert all escape characters in unparsed_uri to upper-case */\n    state = s_usual;\n    s = last - len;\n    while (s < last) {\n        switch (state) {\n        case s_usual:\n            if (s[0] == '%') {\n                state = s_first;\n            }\n            s++;\n            break;\n        case s_first:\n            if (isxdigit(s[0])) {\n                s++;\n                state = s_second;\n\n            } else {\n                state = s_usual;\n            }\n            break;\n        case s_second:\n            if (isxdigit(s[0])) {\n                s[-1] = ngx_toupper(s[-1]);\n                s[0] = ngx_toupper(s[0]);\n                s++;\n            }\n            state = s_usual;\n            break;\n        }\n    }\n\n    v->len = size;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    return NGX_OK;\n\nfailed:\n\n    v->len = 0;\n    v->data = NULL;\n    v->valid = 0;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_variable_dollar(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    v->len = 1;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = (u_char *) \"$\";\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_variable_host_comment(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    size_t   len;\n    u_char  *p;\n\n    len = sizeof(\"<!-- \") - 1\n          + ngx_cached_http_time.len\n          + 1\n          + ngx_cycle->hostname.len\n          + sizeof(\" -->\" CRLF) - 1;\n\n    p = ngx_pnalloc(r->pool, len);\n    if (p == NULL){\n        return NGX_ERROR;\n    }\n\n    v->len = len;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = p;\n\n    ngx_memcpy(p, \"<!-- \", sizeof(\"<!-- \") - 1);\n    p += sizeof(\"<!-- \") - 1;\n\n    ngx_memcpy(p, ngx_cycle->hostname.data, ngx_cycle->hostname.len);\n    p += ngx_cycle->hostname.len;\n\n    *p++ = ' ';\n\n    ngx_memcpy(p, ngx_cached_http_time.data, ngx_cached_http_time.len);\n    p += ngx_cached_http_time.len;\n\n    ngx_memcpy(p, \" -->\" CRLF, sizeof(\" -->\" CRLF) - 1);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_variable_unix_time(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    u_char *p;\n\n    p = ngx_pnalloc(r->pool, NGX_TIME_T_LEN);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    v->len = ngx_sprintf(p, \"%T\", ngx_time()) - p;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = p;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_variable_year(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    return ngx_http_variables_time_fmt(r, v,\n                                       ngx_cached_tm->ngx_tm_year,\n                                       NGX_INT32_LEN);\n}\n\n\nstatic ngx_int_t\nngx_http_variable_year2(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    return ngx_http_variables_time_fmt(r, v,\n                                       ngx_cached_tm->ngx_tm_year % 100, 2);\n}\n\n\nstatic ngx_int_t\nngx_http_variable_month(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    return ngx_http_variables_time_fmt(r, v, ngx_cached_tm->ngx_tm_mon, 2);\n}\n\n\nstatic ngx_int_t\nngx_http_variable_day(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    return ngx_http_variables_time_fmt(r, v, ngx_cached_tm->ngx_tm_mday, 2);\n}\n\n\nstatic ngx_int_t\nngx_http_variable_hour(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    return ngx_http_variables_time_fmt(r, v, ngx_cached_tm->ngx_tm_hour, 2);\n}\n\n\nstatic ngx_int_t\nngx_http_variable_hour12(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    ngx_int_t hour;\n\n    hour = ngx_cached_tm->ngx_tm_hour;\n\n    if (hour == 0 || hour == 12) {\n        return ngx_http_variables_time_fmt(r, v, 12, 2);\n    } else {\n        return ngx_http_variables_time_fmt(r, v, hour % 12, 2);\n    }\n}\n\n\nstatic ngx_int_t\nngx_http_variable_minute(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    return ngx_http_variables_time_fmt(r, v, ngx_cached_tm->ngx_tm_min, 2);\n}\n\n\nstatic ngx_int_t\nngx_http_variable_second(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    return ngx_http_variables_time_fmt(r, v, ngx_cached_tm->ngx_tm_sec, 2);\n}\n\n\nstatic ngx_int_t\nngx_http_variable_time_http(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    u_char  *p;\n\n    p = ngx_pnalloc(r->pool, ngx_cached_http_time.len);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_memcpy(p, ngx_cached_http_time.data, ngx_cached_http_time.len);\n\n    v->len = ngx_cached_http_time.len;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = p;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_variables_time_fmt(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, ngx_int_t t, ngx_int_t len)\n{\n    u_char *p;\n\n    p = ngx_pnalloc(r->pool, len);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    v->len = ngx_sprintf(p, \"%02i\", t) - p;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = p;\n\n    return NGX_OK;\n}\n#endif\n\n\nstatic ngx_int_t\nngx_http_variable_time_iso8601(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    u_char  *p;\n\n    p = ngx_pnalloc(r->pool, ngx_cached_http_log_iso8601.len);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_memcpy(p, ngx_cached_http_log_iso8601.data,\n               ngx_cached_http_log_iso8601.len);\n\n    v->len = ngx_cached_http_log_iso8601.len;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = p;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_variable_time_local(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n    u_char  *p;\n\n    p = ngx_pnalloc(r->pool, ngx_cached_http_log_time.len);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_memcpy(p, ngx_cached_http_log_time.data, ngx_cached_http_log_time.len);\n\n    v->len = ngx_cached_http_log_time.len;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = p;\n\n    return NGX_OK;\n}\n\n\nvoid *\nngx_http_map_find(ngx_http_request_t *r, ngx_http_map_t *map, ngx_str_t *match)\n{\n    void        *value;\n    u_char      *low;\n    size_t       len;\n    ngx_uint_t   key;\n\n    len = match->len;\n\n    if (len) {\n        low = ngx_pnalloc(r->pool, len);\n        if (low == NULL) {\n            return NULL;\n        }\n\n    } else {\n        low = NULL;\n    }\n\n    key = ngx_hash_strlow(low, match->data, len);\n\n    value = ngx_hash_find_combined(&map->hash, key, low, len);\n    if (value) {\n        return value;\n    }\n\n#if (NGX_PCRE)\n\n    if (len && map->nregex) {\n        ngx_int_t              n;\n        ngx_uint_t             i;\n        ngx_http_map_regex_t  *reg;\n\n        reg = map->regex;\n\n        for (i = 0; i < map->nregex; i++) {\n\n            n = ngx_http_regex_exec(r, reg[i].regex, match);\n\n            if (n == NGX_OK) {\n                return reg[i].value;\n            }\n\n            if (n == NGX_DECLINED) {\n                continue;\n            }\n\n            /* NGX_ERROR */\n\n            return NULL;\n        }\n    }\n\n#endif\n\n    return NULL;\n}\n\n\n#if (NGX_PCRE)\n\nstatic ngx_int_t\nngx_http_variable_not_found(ngx_http_request_t *r, ngx_http_variable_value_t *v,\n    uintptr_t data)\n{\n    v->not_found = 1;\n    return NGX_OK;\n}\n\n\nngx_http_regex_t *\nngx_http_regex_compile(ngx_conf_t *cf, ngx_regex_compile_t *rc)\n{\n    u_char                     *p;\n    size_t                      size;\n    ngx_str_t                   name;\n    ngx_uint_t                  i, n;\n    ngx_http_variable_t        *v;\n    ngx_http_regex_t           *re;\n    ngx_http_regex_variable_t  *rv;\n    ngx_http_core_main_conf_t  *cmcf;\n\n    rc->pool = cf->pool;\n\n    if (ngx_regex_compile(rc) != NGX_OK) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, \"%V\", &rc->err);\n        return NULL;\n    }\n\n    re = ngx_pcalloc(cf->pool, sizeof(ngx_http_regex_t));\n    if (re == NULL) {\n        return NULL;\n    }\n\n    re->regex = rc->regex;\n    re->ncaptures = rc->captures;\n    re->name = rc->pattern;\n\n    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);\n    cmcf->ncaptures = ngx_max(cmcf->ncaptures, re->ncaptures);\n\n    n = (ngx_uint_t) rc->named_captures;\n\n    if (n == 0) {\n        return re;\n    }\n\n    rv = ngx_palloc(rc->pool, n * sizeof(ngx_http_regex_variable_t));\n    if (rv == NULL) {\n        return NULL;\n    }\n\n    re->variables = rv;\n    re->nvariables = n;\n\n    size = rc->name_size;\n    p = rc->names;\n\n    for (i = 0; i < n; i++) {\n        rv[i].capture = 2 * ((p[0] << 8) + p[1]);\n\n        name.data = &p[2];\n        name.len = ngx_strlen(name.data);\n\n        v = ngx_http_add_variable(cf, &name, NGX_HTTP_VAR_CHANGEABLE);\n        if (v == NULL) {\n            return NULL;\n        }\n\n        rv[i].index = ngx_http_get_variable_index(cf, &name);\n        if (rv[i].index == NGX_ERROR) {\n            return NULL;\n        }\n\n        v->get_handler = ngx_http_variable_not_found;\n\n        p += size;\n    }\n\n    return re;\n}\n\n\nngx_int_t\nngx_http_regex_exec(ngx_http_request_t *r, ngx_http_regex_t *re, ngx_str_t *s)\n{\n    ngx_int_t                   rc, index;\n    ngx_uint_t                  i, n, len;\n    ngx_http_variable_value_t  *vv;\n    ngx_http_core_main_conf_t  *cmcf;\n\n    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);\n\n    if (re->ncaptures) {\n        len = cmcf->ncaptures;\n\n        if (r->captures == NULL || r->realloc_captures) {\n            r->realloc_captures = 0;\n\n            r->captures = ngx_palloc(r->pool, len * sizeof(int));\n            if (r->captures == NULL) {\n                return NGX_ERROR;\n            }\n        }\n\n    } else {\n        len = 0;\n    }\n\n    rc = ngx_regex_exec(re->regex, s, r->captures, len);\n\n    if (rc == NGX_REGEX_NO_MATCHED) {\n        return NGX_DECLINED;\n    }\n\n    if (rc < 0) {\n        ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,\n                      ngx_regex_exec_n \" failed: %i on \\\"%V\\\" using \\\"%V\\\"\",\n                      rc, s, &re->name);\n        return NGX_ERROR;\n    }\n\n    for (i = 0; i < re->nvariables; i++) {\n\n        n = re->variables[i].capture;\n        index = re->variables[i].index;\n        vv = &r->variables[index];\n\n        vv->len = r->captures[n + 1] - r->captures[n];\n        vv->valid = 1;\n        vv->no_cacheable = 0;\n        vv->not_found = 0;\n        vv->data = &s->data[r->captures[n]];\n\n#if (NGX_DEBUG)\n        {\n        ngx_http_variable_t  *v;\n\n        v = cmcf->variables.elts;\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"http regex set $%V to \\\"%v\\\"\", &v[index].name, vv);\n        }\n#endif\n    }\n\n    r->ncaptures = rc * 2;\n    r->captures_data = s->data;\n\n    return NGX_OK;\n}\n\n#endif\n\n\nngx_int_t\nngx_http_variables_add_core_vars(ngx_conf_t *cf)\n{\n    ngx_http_variable_t        *cv, *v;\n    ngx_http_core_main_conf_t  *cmcf;\n\n    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);\n\n    cmcf->variables_keys = ngx_pcalloc(cf->temp_pool,\n                                       sizeof(ngx_hash_keys_arrays_t));\n    if (cmcf->variables_keys == NULL) {\n        return NGX_ERROR;\n    }\n\n    cmcf->variables_keys->pool = cf->pool;\n    cmcf->variables_keys->temp_pool = cf->pool;\n\n    if (ngx_hash_keys_array_init(cmcf->variables_keys, NGX_HASH_SMALL)\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    if (ngx_array_init(&cmcf->prefix_variables, cf->pool, 8,\n                       sizeof(ngx_http_variable_t))\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    for (cv = ngx_http_core_variables; cv->name.len; cv++) {\n        v = ngx_http_add_variable(cf, &cv->name, cv->flags);\n        if (v == NULL) {\n            return NGX_ERROR;\n        }\n\n        *v = *cv;\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_variables_init_vars(ngx_conf_t *cf)\n{\n    size_t                      len;\n    ngx_uint_t                  i, n;\n    ngx_hash_key_t             *key;\n    ngx_hash_init_t             hash;\n    ngx_http_variable_t        *v, *av, *pv;\n    ngx_http_core_main_conf_t  *cmcf;\n\n    /* set the handlers for the indexed http variables */\n\n    cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);\n\n    v = cmcf->variables.elts;\n    pv = cmcf->prefix_variables.elts;\n    key = cmcf->variables_keys->keys.elts;\n\n    for (i = 0; i < cmcf->variables.nelts; i++) {\n\n        for (n = 0; n < cmcf->variables_keys->keys.nelts; n++) {\n\n            av = key[n].value;\n\n            if (v[i].name.len == key[n].key.len\n                && ngx_strncmp(v[i].name.data, key[n].key.data, v[i].name.len)\n                   == 0)\n            {\n                v[i].get_handler = av->get_handler;\n                v[i].data = av->data;\n\n                av->flags |= NGX_HTTP_VAR_INDEXED;\n                v[i].flags = av->flags;\n\n                av->index = i;\n\n                if (av->get_handler == NULL\n                    || (av->flags & NGX_HTTP_VAR_WEAK))\n                {\n                    break;\n                }\n\n                goto next;\n            }\n        }\n\n        len = 0;\n        av = NULL;\n\n        for (n = 0; n < cmcf->prefix_variables.nelts; n++) {\n            if (v[i].name.len >= pv[n].name.len && v[i].name.len > len\n                && ngx_strncmp(v[i].name.data, pv[n].name.data, pv[n].name.len)\n                   == 0)\n            {\n                av = &pv[n];\n                len = pv[n].name.len;\n            }\n        }\n\n        if (av) {\n            v[i].get_handler = av->get_handler;\n            v[i].data = (uintptr_t) &v[i].name;\n            v[i].flags = av->flags;\n\n            goto next;\n        }\n\n        if (v[i].get_handler == NULL) {\n            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                          \"unknown \\\"%V\\\" variable\", &v[i].name);\n\n            return NGX_ERROR;\n        }\n\n    next:\n        continue;\n    }\n\n\n    for (n = 0; n < cmcf->variables_keys->keys.nelts; n++) {\n        av = key[n].value;\n\n        if (av->flags & NGX_HTTP_VAR_NOHASH) {\n            key[n].key.data = NULL;\n        }\n    }\n\n\n    hash.hash = &cmcf->variables_hash;\n    hash.key = ngx_hash_key;\n    hash.max_size = cmcf->variables_hash_max_size;\n    hash.bucket_size = cmcf->variables_hash_bucket_size;\n    hash.name = \"variables_hash\";\n    hash.pool = cf->pool;\n    hash.temp_pool = NULL;\n\n    if (ngx_hash_init(&hash, cmcf->variables_keys->keys.elts,\n                      cmcf->variables_keys->keys.nelts)\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    cmcf->variables_keys = NULL;\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "src/http/ngx_http_variables.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_HTTP_VARIABLES_H_INCLUDED_\n#define _NGX_HTTP_VARIABLES_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\ntypedef ngx_variable_value_t  ngx_http_variable_value_t;\n\n#define ngx_http_variable(v)     { sizeof(v) - 1, 1, 0, 0, 0, (u_char *) v }\n\ntypedef struct ngx_http_variable_s  ngx_http_variable_t;\n\ntypedef void (*ngx_http_set_variable_pt) (ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\ntypedef ngx_int_t (*ngx_http_get_variable_pt) (ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\n\n\n#define NGX_HTTP_VAR_CHANGEABLE   1\n#define NGX_HTTP_VAR_NOCACHEABLE  2\n#define NGX_HTTP_VAR_INDEXED      4\n#define NGX_HTTP_VAR_NOHASH       8\n#define NGX_HTTP_VAR_WEAK         16\n#define NGX_HTTP_VAR_PREFIX       32\n\n\nstruct ngx_http_variable_s {\n    ngx_str_t                     name;   /* must be first to build the hash */\n    ngx_http_set_variable_pt      set_handler;\n    ngx_http_get_variable_pt      get_handler;\n    uintptr_t                     data;\n    ngx_uint_t                    flags;\n    ngx_uint_t                    index;\n};\n\n#define ngx_http_null_variable  { ngx_null_string, NULL, NULL, 0, 0, 0 }\n\n\nngx_http_variable_t *ngx_http_add_variable(ngx_conf_t *cf, ngx_str_t *name,\n    ngx_uint_t flags);\nngx_int_t ngx_http_get_variable_index(ngx_conf_t *cf, ngx_str_t *name);\nngx_http_variable_value_t *ngx_http_get_indexed_variable(ngx_http_request_t *r,\n    ngx_uint_t index);\nngx_http_variable_value_t *ngx_http_get_flushed_variable(ngx_http_request_t *r,\n    ngx_uint_t index);\n\nngx_http_variable_value_t *ngx_http_get_variable(ngx_http_request_t *r,\n    ngx_str_t *name, ngx_uint_t key);\n\nngx_int_t ngx_http_variable_unknown_header(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, ngx_str_t *var, ngx_list_part_t *part,\n    size_t prefix);\n\n\n#if (NGX_PCRE)\n\ntypedef struct {\n    ngx_uint_t                    capture;\n    ngx_int_t                     index;\n} ngx_http_regex_variable_t;\n\n\ntypedef struct {\n    ngx_regex_t                  *regex;\n    ngx_uint_t                    ncaptures;\n    ngx_http_regex_variable_t    *variables;\n    ngx_uint_t                    nvariables;\n    ngx_str_t                     name;\n} ngx_http_regex_t;\n\n\ntypedef struct {\n    ngx_http_regex_t             *regex;\n    void                         *value;\n} ngx_http_map_regex_t;\n\n\nngx_http_regex_t *ngx_http_regex_compile(ngx_conf_t *cf,\n    ngx_regex_compile_t *rc);\nngx_int_t ngx_http_regex_exec(ngx_http_request_t *r, ngx_http_regex_t *re,\n    ngx_str_t *s);\n\n#endif\n\n\ntypedef struct {\n    ngx_hash_combined_t           hash;\n#if (NGX_PCRE)\n    ngx_http_map_regex_t         *regex;\n    ngx_uint_t                    nregex;\n#endif\n} ngx_http_map_t;\n\n\nvoid *ngx_http_map_find(ngx_http_request_t *r, ngx_http_map_t *map,\n    ngx_str_t *match);\n\n\nngx_int_t ngx_http_variables_add_core_vars(ngx_conf_t *cf);\nngx_int_t ngx_http_variables_init_vars(ngx_conf_t *cf);\n\n\nextern ngx_http_variable_value_t  ngx_http_variable_null_value;\nextern ngx_http_variable_value_t  ngx_http_variable_true_value;\n\n\n#endif /* _NGX_HTTP_VARIABLES_H_INCLUDED_ */\n"
  },
  {
    "path": "src/http/ngx_http_write_filter_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\nstatic ngx_int_t ngx_http_write_filter_init(ngx_conf_t *cf);\n\n#if (T_NGX_REQ_STATUS )\nngx_int_t (*ngx_http_write_filter_stat)(ngx_http_request_t *r) = NULL;\n#endif\n\n\nstatic ngx_http_module_t  ngx_http_write_filter_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    ngx_http_write_filter_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\nngx_module_t  ngx_http_write_filter_module = {\n    NGX_MODULE_V1,\n    &ngx_http_write_filter_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\nngx_int_t\nngx_http_write_filter(ngx_http_request_t *r, ngx_chain_t *in)\n{\n    off_t                      size, sent, nsent, limit;\n    ngx_uint_t                 last, flush, sync;\n    ngx_msec_t                 delay;\n    ngx_chain_t               *cl, *ln, **ll, *chain;\n    ngx_connection_t          *c;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    c = r->connection;\n\n    if (c->error) {\n        return NGX_ERROR;\n    }\n\n    size = 0;\n    flush = 0;\n    sync = 0;\n    last = 0;\n    ll = &r->out;\n\n    /* find the size, the flush point and the last link of the saved chain */\n\n    for (cl = r->out; cl; cl = cl->next) {\n        ll = &cl->next;\n\n        ngx_log_debug7(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"write old buf t:%d f:%d %p, pos %p, size: %z \"\n                       \"file: %O, size: %O\",\n                       cl->buf->temporary, cl->buf->in_file,\n                       cl->buf->start, cl->buf->pos,\n                       cl->buf->last - cl->buf->pos,\n                       cl->buf->file_pos,\n                       cl->buf->file_last - cl->buf->file_pos);\n\n        if (ngx_buf_size(cl->buf) == 0 && !ngx_buf_special(cl->buf)) {\n            ngx_log_error(NGX_LOG_ALERT, c->log, 0,\n                          \"zero size buf in writer \"\n                          \"t:%d r:%d f:%d %p %p-%p %p %O-%O\",\n                          cl->buf->temporary,\n                          cl->buf->recycled,\n                          cl->buf->in_file,\n                          cl->buf->start,\n                          cl->buf->pos,\n                          cl->buf->last,\n                          cl->buf->file,\n                          cl->buf->file_pos,\n                          cl->buf->file_last);\n\n            ngx_debug_point();\n            return NGX_ERROR;\n        }\n\n        if (ngx_buf_size(cl->buf) < 0) {\n            ngx_log_error(NGX_LOG_ALERT, c->log, 0,\n                          \"negative size buf in writer \"\n                          \"t:%d r:%d f:%d %p %p-%p %p %O-%O\",\n                          cl->buf->temporary,\n                          cl->buf->recycled,\n                          cl->buf->in_file,\n                          cl->buf->start,\n                          cl->buf->pos,\n                          cl->buf->last,\n                          cl->buf->file,\n                          cl->buf->file_pos,\n                          cl->buf->file_last);\n\n            ngx_debug_point();\n            return NGX_ERROR;\n        }\n\n        size += ngx_buf_size(cl->buf);\n\n        if (cl->buf->flush || cl->buf->recycled) {\n            flush = 1;\n        }\n\n        if (cl->buf->sync) {\n            sync = 1;\n        }\n\n        if (cl->buf->last_buf) {\n            last = 1;\n        }\n    }\n\n    /* add the new chain to the existent one */\n\n    for (ln = in; ln; ln = ln->next) {\n        cl = ngx_alloc_chain_link(r->pool);\n        if (cl == NULL) {\n            return NGX_ERROR;\n        }\n\n        cl->buf = ln->buf;\n        *ll = cl;\n        ll = &cl->next;\n\n        ngx_log_debug7(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"write new buf t:%d f:%d %p, pos %p, size: %z \"\n                       \"file: %O, size: %O\",\n                       cl->buf->temporary, cl->buf->in_file,\n                       cl->buf->start, cl->buf->pos,\n                       cl->buf->last - cl->buf->pos,\n                       cl->buf->file_pos,\n                       cl->buf->file_last - cl->buf->file_pos);\n\n        if (ngx_buf_size(cl->buf) == 0 && !ngx_buf_special(cl->buf)) {\n            ngx_log_error(NGX_LOG_ALERT, c->log, 0,\n                          \"zero size buf in writer \"\n                          \"t:%d r:%d f:%d %p %p-%p %p %O-%O\",\n                          cl->buf->temporary,\n                          cl->buf->recycled,\n                          cl->buf->in_file,\n                          cl->buf->start,\n                          cl->buf->pos,\n                          cl->buf->last,\n                          cl->buf->file,\n                          cl->buf->file_pos,\n                          cl->buf->file_last);\n\n            ngx_debug_point();\n            return NGX_ERROR;\n        }\n\n        if (ngx_buf_size(cl->buf) < 0) {\n            ngx_log_error(NGX_LOG_ALERT, c->log, 0,\n                          \"negative size buf in writer \"\n                          \"t:%d r:%d f:%d %p %p-%p %p %O-%O\",\n                          cl->buf->temporary,\n                          cl->buf->recycled,\n                          cl->buf->in_file,\n                          cl->buf->start,\n                          cl->buf->pos,\n                          cl->buf->last,\n                          cl->buf->file,\n                          cl->buf->file_pos,\n                          cl->buf->file_last);\n\n            ngx_debug_point();\n            return NGX_ERROR;\n        }\n\n        size += ngx_buf_size(cl->buf);\n\n        if (cl->buf->flush || cl->buf->recycled) {\n            flush = 1;\n        }\n\n        if (cl->buf->sync) {\n            sync = 1;\n        }\n\n        if (cl->buf->last_buf) {\n            last = 1;\n        }\n    }\n\n    *ll = NULL;\n\n    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http write filter: l:%ui f:%ui s:%O\", last, flush, size);\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    /*\n     * avoid the output if there are no last buf, no flush point,\n     * there are the incoming bufs and the size of all bufs\n     * is smaller than \"postpone_output\" directive\n     */\n\n    if (!last && !flush && in && size < (off_t) clcf->postpone_output) {\n        return NGX_OK;\n    }\n\n    if (c->write->delayed) {\n        c->buffered |= NGX_HTTP_WRITE_BUFFERED;\n        return NGX_AGAIN;\n    }\n\n    if (size == 0\n        && !(c->buffered & NGX_LOWLEVEL_BUFFERED)\n        && !(last && c->need_last_buf)\n        && !(flush && c->need_flush_buf))\n    {\n        if (last || flush || sync) {\n            for (cl = r->out; cl; /* void */) {\n                ln = cl;\n                cl = cl->next;\n                ngx_free_chain(r->pool, ln);\n            }\n\n            r->out = NULL;\n            c->buffered &= ~NGX_HTTP_WRITE_BUFFERED;\n\n            return NGX_OK;\n        }\n\n        ngx_log_error(NGX_LOG_ALERT, c->log, 0,\n                      \"the http output chain is empty\");\n\n        ngx_debug_point();\n\n        return NGX_ERROR;\n    }\n\n    if (!r->limit_rate_set) {\n        r->limit_rate = ngx_http_complex_value_size(r, clcf->limit_rate, 0);\n        r->limit_rate_set = 1;\n    }\n\n    if (r->limit_rate) {\n\n        if (!r->limit_rate_after_set) {\n            r->limit_rate_after = ngx_http_complex_value_size(r,\n                                                    clcf->limit_rate_after, 0);\n            r->limit_rate_after_set = 1;\n        }\n\n        limit = (off_t) r->limit_rate * (ngx_time() - r->start_sec + 1)\n                - (c->sent - r->limit_rate_after);\n\n        if (limit <= 0) {\n            c->write->delayed = 1;\n            delay = (ngx_msec_t) (- limit * 1000 / r->limit_rate + 1);\n            ngx_add_timer(c->write, delay);\n\n            c->buffered |= NGX_HTTP_WRITE_BUFFERED;\n\n            return NGX_AGAIN;\n        }\n\n        if (clcf->sendfile_max_chunk\n            && (off_t) clcf->sendfile_max_chunk < limit)\n        {\n            limit = clcf->sendfile_max_chunk;\n        }\n\n    } else {\n        limit = clcf->sendfile_max_chunk;\n    }\n\n    sent = c->sent;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http write filter limit %O\", limit);\n\n    chain = c->send_chain(c, r->out, limit);\n\n#if (T_NGX_REQ_STATUS )\n    if (ngx_http_write_filter_stat != NULL) {\n        if (ngx_http_write_filter_stat(r) == NGX_ERROR) {\n            return NGX_ERROR;\n        }\n    }\n#endif\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http write filter %p\", chain);\n\n    if (chain == NGX_CHAIN_ERROR) {\n        c->error = 1;\n        return NGX_ERROR;\n    }\n\n    if (r->limit_rate) {\n\n        nsent = c->sent;\n\n        if (r->limit_rate_after) {\n\n            sent -= r->limit_rate_after;\n            if (sent < 0) {\n                sent = 0;\n            }\n\n            nsent -= r->limit_rate_after;\n            if (nsent < 0) {\n                nsent = 0;\n            }\n        }\n\n        delay = (ngx_msec_t) ((nsent - sent) * 1000 / r->limit_rate);\n\n        if (delay > 0) {\n            c->write->delayed = 1;\n            ngx_add_timer(c->write, delay);\n        }\n    }\n\n    if (chain && c->write->ready && !c->write->delayed) {\n        ngx_post_event(c->write, &ngx_posted_next_events);\n    }\n\n    for (cl = r->out; cl && cl != chain; /* void */) {\n        ln = cl;\n        cl = cl->next;\n        ngx_free_chain(r->pool, ln);\n    }\n\n    r->out = chain;\n\n    if (chain) {\n        c->buffered |= NGX_HTTP_WRITE_BUFFERED;\n        return NGX_AGAIN;\n    }\n\n    c->buffered &= ~NGX_HTTP_WRITE_BUFFERED;\n\n    if ((c->buffered & NGX_LOWLEVEL_BUFFERED) && r->postponed == NULL) {\n        return NGX_AGAIN;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_write_filter_init(ngx_conf_t *cf)\n{\n    ngx_http_top_body_filter = ngx_http_write_filter;\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "src/http/v2/ngx_http_v2.c",
    "content": "\n/*\n * Copyright (C) Nginx, Inc.\n * Copyright (C) Valentin V. Bartenev\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n#include <ngx_http_v2_module.h>\n\n\ntypedef struct {\n    ngx_str_t           name;\n    ngx_uint_t          offset;\n    ngx_uint_t          hash;\n    ngx_http_header_t  *hh;\n} ngx_http_v2_parse_header_t;\n\n\n/* errors */\n#define NGX_HTTP_V2_NO_ERROR                     0x0\n#define NGX_HTTP_V2_PROTOCOL_ERROR               0x1\n#define NGX_HTTP_V2_INTERNAL_ERROR               0x2\n#define NGX_HTTP_V2_FLOW_CTRL_ERROR              0x3\n#define NGX_HTTP_V2_SETTINGS_TIMEOUT             0x4\n#define NGX_HTTP_V2_STREAM_CLOSED                0x5\n#define NGX_HTTP_V2_SIZE_ERROR                   0x6\n#define NGX_HTTP_V2_REFUSED_STREAM               0x7\n#define NGX_HTTP_V2_CANCEL                       0x8\n#define NGX_HTTP_V2_COMP_ERROR                   0x9\n#define NGX_HTTP_V2_CONNECT_ERROR                0xa\n#define NGX_HTTP_V2_ENHANCE_YOUR_CALM            0xb\n#define NGX_HTTP_V2_INADEQUATE_SECURITY          0xc\n#define NGX_HTTP_V2_HTTP_1_1_REQUIRED            0xd\n\n/* frame sizes */\n#define NGX_HTTP_V2_SETTINGS_ACK_SIZE            0\n#define NGX_HTTP_V2_RST_STREAM_SIZE              4\n#define NGX_HTTP_V2_PRIORITY_SIZE                5\n#define NGX_HTTP_V2_PING_SIZE                    8\n#define NGX_HTTP_V2_GOAWAY_SIZE                  8\n#define NGX_HTTP_V2_WINDOW_UPDATE_SIZE           4\n\n#define NGX_HTTP_V2_SETTINGS_PARAM_SIZE          6\n\n/* settings fields */\n#define NGX_HTTP_V2_HEADER_TABLE_SIZE_SETTING    0x1\n#define NGX_HTTP_V2_ENABLE_PUSH_SETTING          0x2\n#define NGX_HTTP_V2_MAX_STREAMS_SETTING          0x3\n#define NGX_HTTP_V2_INIT_WINDOW_SIZE_SETTING     0x4\n#define NGX_HTTP_V2_MAX_FRAME_SIZE_SETTING       0x5\n\n#define NGX_HTTP_V2_FRAME_BUFFER_SIZE            24\n\n#define NGX_HTTP_V2_ROOT                         (void *) -1\n\n\nstatic void ngx_http_v2_read_handler(ngx_event_t *rev);\nstatic void ngx_http_v2_write_handler(ngx_event_t *wev);\nstatic void ngx_http_v2_handle_connection(ngx_http_v2_connection_t *h2c);\nstatic void ngx_http_v2_lingering_close(ngx_connection_t *c);\nstatic void ngx_http_v2_lingering_close_handler(ngx_event_t *rev);\n\nstatic u_char *ngx_http_v2_state_proxy_protocol(ngx_http_v2_connection_t *h2c,\n    u_char *pos, u_char *end);\nstatic u_char *ngx_http_v2_state_preface(ngx_http_v2_connection_t *h2c,\n    u_char *pos, u_char *end);\nstatic u_char *ngx_http_v2_state_preface_end(ngx_http_v2_connection_t *h2c,\n    u_char *pos, u_char *end);\nstatic u_char *ngx_http_v2_state_head(ngx_http_v2_connection_t *h2c,\n    u_char *pos, u_char *end);\nstatic u_char *ngx_http_v2_state_data(ngx_http_v2_connection_t *h2c,\n    u_char *pos, u_char *end);\nstatic u_char *ngx_http_v2_state_read_data(ngx_http_v2_connection_t *h2c,\n    u_char *pos, u_char *end);\nstatic u_char *ngx_http_v2_state_headers(ngx_http_v2_connection_t *h2c,\n    u_char *pos, u_char *end);\nstatic u_char *ngx_http_v2_state_header_block(ngx_http_v2_connection_t *h2c,\n    u_char *pos, u_char *end);\nstatic u_char *ngx_http_v2_state_field_len(ngx_http_v2_connection_t *h2c,\n    u_char *pos, u_char *end);\nstatic u_char *ngx_http_v2_state_field_huff(ngx_http_v2_connection_t *h2c,\n    u_char *pos, u_char *end);\nstatic u_char *ngx_http_v2_state_field_raw(ngx_http_v2_connection_t *h2c,\n    u_char *pos, u_char *end);\nstatic u_char *ngx_http_v2_state_field_skip(ngx_http_v2_connection_t *h2c,\n    u_char *pos, u_char *end);\nstatic u_char *ngx_http_v2_state_process_header(ngx_http_v2_connection_t *h2c,\n    u_char *pos, u_char *end);\nstatic u_char *ngx_http_v2_state_header_complete(ngx_http_v2_connection_t *h2c,\n    u_char *pos, u_char *end);\nstatic u_char *ngx_http_v2_handle_continuation(ngx_http_v2_connection_t *h2c,\n    u_char *pos, u_char *end, ngx_http_v2_handler_pt handler);\nstatic u_char *ngx_http_v2_state_priority(ngx_http_v2_connection_t *h2c,\n    u_char *pos, u_char *end);\nstatic u_char *ngx_http_v2_state_rst_stream(ngx_http_v2_connection_t *h2c,\n    u_char *pos, u_char *end);\nstatic u_char *ngx_http_v2_state_settings(ngx_http_v2_connection_t *h2c,\n    u_char *pos, u_char *end);\nstatic u_char *ngx_http_v2_state_settings_params(ngx_http_v2_connection_t *h2c,\n    u_char *pos, u_char *end);\nstatic u_char *ngx_http_v2_state_push_promise(ngx_http_v2_connection_t *h2c,\n    u_char *pos, u_char *end);\nstatic u_char *ngx_http_v2_state_ping(ngx_http_v2_connection_t *h2c,\n    u_char *pos, u_char *end);\nstatic u_char *ngx_http_v2_state_goaway(ngx_http_v2_connection_t *h2c,\n    u_char *pos, u_char *end);\nstatic u_char *ngx_http_v2_state_window_update(ngx_http_v2_connection_t *h2c,\n    u_char *pos, u_char *end);\nstatic u_char *ngx_http_v2_state_continuation(ngx_http_v2_connection_t *h2c,\n    u_char *pos, u_char *end);\nstatic u_char *ngx_http_v2_state_complete(ngx_http_v2_connection_t *h2c,\n    u_char *pos, u_char *end);\nstatic u_char *ngx_http_v2_state_skip_padded(ngx_http_v2_connection_t *h2c,\n    u_char *pos, u_char *end);\nstatic u_char *ngx_http_v2_state_skip(ngx_http_v2_connection_t *h2c,\n    u_char *pos, u_char *end);\nstatic u_char *ngx_http_v2_state_save(ngx_http_v2_connection_t *h2c,\n    u_char *pos, u_char *end, ngx_http_v2_handler_pt handler);\nstatic u_char *ngx_http_v2_state_headers_save(ngx_http_v2_connection_t *h2c,\n    u_char *pos, u_char *end, ngx_http_v2_handler_pt handler);\nstatic u_char *ngx_http_v2_connection_error(ngx_http_v2_connection_t *h2c,\n    ngx_uint_t err);\n\nstatic ngx_int_t ngx_http_v2_parse_int(ngx_http_v2_connection_t *h2c,\n    u_char **pos, u_char *end, ngx_uint_t prefix);\n\nstatic ngx_http_v2_stream_t *ngx_http_v2_create_stream(\n    ngx_http_v2_connection_t *h2c, ngx_uint_t push);\nstatic ngx_http_v2_node_t *ngx_http_v2_get_node_by_id(\n    ngx_http_v2_connection_t *h2c, ngx_uint_t sid, ngx_uint_t alloc);\nstatic ngx_http_v2_node_t *ngx_http_v2_get_closed_node(\n    ngx_http_v2_connection_t *h2c);\n#define ngx_http_v2_index_size(h2scf)  (h2scf->streams_index_mask + 1)\n#define ngx_http_v2_index(h2scf, sid)  ((sid >> 1) & h2scf->streams_index_mask)\n\nstatic ngx_int_t ngx_http_v2_send_settings(ngx_http_v2_connection_t *h2c);\nstatic ngx_int_t ngx_http_v2_settings_frame_handler(\n    ngx_http_v2_connection_t *h2c, ngx_http_v2_out_frame_t *frame);\nstatic ngx_int_t ngx_http_v2_send_window_update(ngx_http_v2_connection_t *h2c,\n    ngx_uint_t sid, size_t window);\nstatic ngx_int_t ngx_http_v2_send_rst_stream(ngx_http_v2_connection_t *h2c,\n    ngx_uint_t sid, ngx_uint_t status);\nstatic ngx_int_t ngx_http_v2_send_goaway(ngx_http_v2_connection_t *h2c,\n    ngx_uint_t status);\n\nstatic ngx_http_v2_out_frame_t *ngx_http_v2_get_frame(\n    ngx_http_v2_connection_t *h2c, size_t length, ngx_uint_t type,\n    u_char flags, ngx_uint_t sid);\nstatic ngx_int_t ngx_http_v2_frame_handler(ngx_http_v2_connection_t *h2c,\n    ngx_http_v2_out_frame_t *frame);\n\nstatic ngx_int_t ngx_http_v2_validate_header(ngx_http_request_t *r,\n    ngx_http_v2_header_t *header);\nstatic ngx_int_t ngx_http_v2_pseudo_header(ngx_http_request_t *r,\n    ngx_http_v2_header_t *header);\nstatic ngx_int_t ngx_http_v2_parse_path(ngx_http_request_t *r,\n    ngx_str_t *value);\nstatic ngx_int_t ngx_http_v2_parse_method(ngx_http_request_t *r,\n    ngx_str_t *value);\nstatic ngx_int_t ngx_http_v2_parse_scheme(ngx_http_request_t *r,\n    ngx_str_t *value);\nstatic ngx_int_t ngx_http_v2_parse_authority(ngx_http_request_t *r,\n    ngx_str_t *value);\nstatic ngx_int_t ngx_http_v2_parse_header(ngx_http_request_t *r,\n    ngx_http_v2_parse_header_t *header, ngx_str_t *value);\nstatic ngx_int_t ngx_http_v2_construct_request_line(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_v2_cookie(ngx_http_request_t *r,\n    ngx_http_v2_header_t *header);\nstatic ngx_int_t ngx_http_v2_construct_cookie_header(ngx_http_request_t *r);\nstatic void ngx_http_v2_run_request(ngx_http_request_t *r);\nstatic void ngx_http_v2_run_request_handler(ngx_event_t *ev);\nstatic ngx_int_t ngx_http_v2_process_request_body(ngx_http_request_t *r,\n    u_char *pos, size_t size, ngx_uint_t last, ngx_uint_t flush);\nstatic ngx_int_t ngx_http_v2_filter_request_body(ngx_http_request_t *r);\nstatic void ngx_http_v2_read_client_request_body_handler(ngx_http_request_t *r);\n\nstatic ngx_int_t ngx_http_v2_terminate_stream(ngx_http_v2_connection_t *h2c,\n    ngx_http_v2_stream_t *stream, ngx_uint_t status);\nstatic void ngx_http_v2_close_stream_handler(ngx_event_t *ev);\nstatic void ngx_http_v2_retry_close_stream_handler(ngx_event_t *ev);\nstatic void ngx_http_v2_handle_connection_handler(ngx_event_t *rev);\nstatic void ngx_http_v2_idle_handler(ngx_event_t *rev);\nstatic void ngx_http_v2_finalize_connection(ngx_http_v2_connection_t *h2c,\n    ngx_uint_t status);\n\nstatic ngx_int_t ngx_http_v2_adjust_windows(ngx_http_v2_connection_t *h2c,\n    ssize_t delta);\nstatic void ngx_http_v2_set_dependency(ngx_http_v2_connection_t *h2c,\n    ngx_http_v2_node_t *node, ngx_uint_t depend, ngx_uint_t exclusive);\nstatic void ngx_http_v2_node_children_update(ngx_http_v2_node_t *node);\n\nstatic void ngx_http_v2_pool_cleanup(void *data);\n\n\nstatic ngx_http_v2_handler_pt ngx_http_v2_frame_states[] = {\n    ngx_http_v2_state_data,               /* NGX_HTTP_V2_DATA_FRAME */\n    ngx_http_v2_state_headers,            /* NGX_HTTP_V2_HEADERS_FRAME */\n    ngx_http_v2_state_priority,           /* NGX_HTTP_V2_PRIORITY_FRAME */\n    ngx_http_v2_state_rst_stream,         /* NGX_HTTP_V2_RST_STREAM_FRAME */\n    ngx_http_v2_state_settings,           /* NGX_HTTP_V2_SETTINGS_FRAME */\n    ngx_http_v2_state_push_promise,       /* NGX_HTTP_V2_PUSH_PROMISE_FRAME */\n    ngx_http_v2_state_ping,               /* NGX_HTTP_V2_PING_FRAME */\n    ngx_http_v2_state_goaway,             /* NGX_HTTP_V2_GOAWAY_FRAME */\n    ngx_http_v2_state_window_update,      /* NGX_HTTP_V2_WINDOW_UPDATE_FRAME */\n    ngx_http_v2_state_continuation        /* NGX_HTTP_V2_CONTINUATION_FRAME */\n};\n\n#define NGX_HTTP_V2_FRAME_STATES                                              \\\n    (sizeof(ngx_http_v2_frame_states) / sizeof(ngx_http_v2_handler_pt))\n\n\nstatic ngx_http_v2_parse_header_t  ngx_http_v2_parse_headers[] = {\n    { ngx_string(\"host\"),\n      offsetof(ngx_http_headers_in_t, host), 0, NULL },\n\n    { ngx_string(\"accept-encoding\"),\n      offsetof(ngx_http_headers_in_t, accept_encoding), 0, NULL },\n\n    { ngx_string(\"accept-language\"),\n      offsetof(ngx_http_headers_in_t, accept_language), 0, NULL },\n\n    { ngx_string(\"user-agent\"),\n      offsetof(ngx_http_headers_in_t, user_agent), 0, NULL },\n\n    { ngx_null_string, 0, 0, NULL }\n};\n\n\nvoid\nngx_http_v2_init(ngx_event_t *rev)\n{\n    ngx_connection_t          *c;\n    ngx_pool_cleanup_t        *cln;\n    ngx_http_connection_t     *hc;\n    ngx_http_v2_srv_conf_t    *h2scf;\n    ngx_http_v2_main_conf_t   *h2mcf;\n    ngx_http_v2_connection_t  *h2c;\n    ngx_http_core_srv_conf_t  *cscf;\n\n    c = rev->data;\n    hc = c->data;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"init http2 connection\");\n\n    c->log->action = \"processing HTTP/2 connection\";\n\n    h2mcf = ngx_http_get_module_main_conf(hc->conf_ctx, ngx_http_v2_module);\n\n    if (h2mcf->recv_buffer == NULL) {\n        h2mcf->recv_buffer = ngx_palloc(ngx_cycle->pool,\n                                        h2mcf->recv_buffer_size);\n        if (h2mcf->recv_buffer == NULL) {\n            ngx_http_close_connection(c);\n            return;\n        }\n    }\n\n    h2c = ngx_pcalloc(c->pool, sizeof(ngx_http_v2_connection_t));\n    if (h2c == NULL) {\n        ngx_http_close_connection(c);\n        return;\n    }\n\n    h2c->connection = c;\n    h2c->http_connection = hc;\n\n    h2c->send_window = NGX_HTTP_V2_DEFAULT_WINDOW;\n    h2c->recv_window = NGX_HTTP_V2_MAX_WINDOW;\n\n    h2c->init_window = NGX_HTTP_V2_DEFAULT_WINDOW;\n\n    h2c->frame_size = NGX_HTTP_V2_DEFAULT_FRAME_SIZE;\n\n    h2scf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_v2_module);\n\n    h2c->concurrent_pushes = h2scf->concurrent_pushes;\n    h2c->priority_limit = ngx_max(h2scf->concurrent_streams, 100);\n\n    h2c->pool = ngx_create_pool(h2scf->pool_size, h2c->connection->log);\n    if (h2c->pool == NULL) {\n        ngx_http_close_connection(c);\n        return;\n    }\n\n    cln = ngx_pool_cleanup_add(c->pool, 0);\n    if (cln == NULL) {\n        ngx_http_close_connection(c);\n        return;\n    }\n\n    cln->handler = ngx_http_v2_pool_cleanup;\n    cln->data = h2c;\n\n    h2c->streams_index = ngx_pcalloc(c->pool, ngx_http_v2_index_size(h2scf)\n                                              * sizeof(ngx_http_v2_node_t *));\n    if (h2c->streams_index == NULL) {\n        ngx_http_close_connection(c);\n        return;\n    }\n\n    if (ngx_http_v2_send_settings(h2c) == NGX_ERROR) {\n        ngx_http_close_connection(c);\n        return;\n    }\n\n    if (ngx_http_v2_send_window_update(h2c, 0, NGX_HTTP_V2_MAX_WINDOW\n                                               - NGX_HTTP_V2_DEFAULT_WINDOW)\n        == NGX_ERROR)\n    {\n        ngx_http_close_connection(c);\n        return;\n    }\n\n    h2c->state.handler = hc->proxy_protocol ? ngx_http_v2_state_proxy_protocol\n                                            : ngx_http_v2_state_preface;\n\n    ngx_queue_init(&h2c->waiting);\n    ngx_queue_init(&h2c->dependencies);\n    ngx_queue_init(&h2c->closed);\n\n    c->data = h2c;\n\n    rev->handler = ngx_http_v2_read_handler;\n    c->write->handler = ngx_http_v2_write_handler;\n\n    if (!rev->timer_set) {\n        cscf = ngx_http_get_module_srv_conf(hc->conf_ctx,\n                                            ngx_http_core_module);\n        ngx_add_timer(rev, cscf->client_header_timeout);\n    }\n\n    c->idle = 1;\n    ngx_reusable_connection(c, 0);\n\n    ngx_http_v2_read_handler(rev);\n}\n\n\nstatic void\nngx_http_v2_read_handler(ngx_event_t *rev)\n{\n    u_char                    *p, *end;\n    size_t                     available;\n    ssize_t                    n;\n    ngx_connection_t          *c;\n    ngx_http_v2_main_conf_t   *h2mcf;\n    ngx_http_v2_connection_t  *h2c;\n\n    c = rev->data;\n    h2c = c->data;\n\n    if (rev->timedout) {\n        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, \"client timed out\");\n        ngx_http_v2_finalize_connection(h2c, NGX_HTTP_V2_PROTOCOL_ERROR);\n        return;\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"http2 read handler\");\n\n    h2c->blocked = 1;\n    h2c->new_streams = 0;\n\n    if (c->close) {\n        c->close = 0;\n\n        if (c->error) {\n            ngx_http_v2_finalize_connection(h2c, 0);\n            return;\n        }\n\n        if (!h2c->processing && !h2c->pushing) {\n            ngx_http_v2_finalize_connection(h2c, NGX_HTTP_V2_NO_ERROR);\n            return;\n        }\n\n        if (!h2c->goaway) {\n            h2c->goaway = 1;\n\n            if (ngx_http_v2_send_goaway(h2c, NGX_HTTP_V2_NO_ERROR)\n                == NGX_ERROR)\n            {\n                ngx_http_v2_finalize_connection(h2c, 0);\n                return;\n            }\n\n            if (ngx_http_v2_send_output_queue(h2c) == NGX_ERROR) {\n                ngx_http_v2_finalize_connection(h2c, 0);\n                return;\n            }\n        }\n\n        h2c->blocked = 0;\n\n        return;\n    }\n\n    h2mcf = ngx_http_get_module_main_conf(h2c->http_connection->conf_ctx,\n                                          ngx_http_v2_module);\n\n    available = h2mcf->recv_buffer_size - 2 * NGX_HTTP_V2_STATE_BUFFER_SIZE;\n\n    do {\n        p = h2mcf->recv_buffer;\n\n        ngx_memcpy(p, h2c->state.buffer, NGX_HTTP_V2_STATE_BUFFER_SIZE);\n        end = p + h2c->state.buffer_used;\n\n        n = c->recv(c, end, available);\n\n        if (n == NGX_AGAIN) {\n            break;\n        }\n\n        if (n == 0\n            && (h2c->state.incomplete || h2c->processing || h2c->pushing))\n        {\n            ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                          \"client prematurely closed connection\");\n        }\n\n        if (n == 0 || n == NGX_ERROR) {\n            c->error = 1;\n            ngx_http_v2_finalize_connection(h2c, 0);\n            return;\n        }\n\n        end += n;\n\n        h2c->state.buffer_used = 0;\n        h2c->state.incomplete = 0;\n\n        do {\n            p = h2c->state.handler(h2c, p, end);\n\n            if (p == NULL) {\n                return;\n            }\n\n        } while (p != end);\n\n        h2c->total_bytes += n;\n\n        if (h2c->total_bytes / 8 > h2c->payload_bytes + 1048576) {\n            ngx_log_error(NGX_LOG_INFO, c->log, 0, \"http2 flood detected\");\n            ngx_http_v2_finalize_connection(h2c, NGX_HTTP_V2_NO_ERROR);\n            return;\n        }\n\n    } while (rev->ready);\n\n    if (ngx_handle_read_event(rev, 0) != NGX_OK) {\n        ngx_http_v2_finalize_connection(h2c, NGX_HTTP_V2_INTERNAL_ERROR);\n        return;\n    }\n\n    if (h2c->last_out && ngx_http_v2_send_output_queue(h2c) == NGX_ERROR) {\n        ngx_http_v2_finalize_connection(h2c, 0);\n        return;\n    }\n\n    h2c->blocked = 0;\n\n    ngx_http_v2_handle_connection(h2c);\n}\n\n\nstatic void\nngx_http_v2_write_handler(ngx_event_t *wev)\n{\n    ngx_int_t                  rc;\n    ngx_connection_t          *c;\n    ngx_http_v2_connection_t  *h2c;\n\n    c = wev->data;\n    h2c = c->data;\n\n    if (wev->timedout) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"http2 write event timed out\");\n        c->error = 1;\n        c->timedout = 1;\n        ngx_http_v2_finalize_connection(h2c, 0);\n        return;\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"http2 write handler\");\n\n    if (h2c->last_out == NULL && !c->buffered) {\n\n        if (wev->timer_set) {\n            ngx_del_timer(wev);\n        }\n\n        ngx_http_v2_handle_connection(h2c);\n        return;\n    }\n\n    h2c->blocked = 1;\n\n    rc = ngx_http_v2_send_output_queue(h2c);\n\n    if (rc == NGX_ERROR) {\n        ngx_http_v2_finalize_connection(h2c, 0);\n        return;\n    }\n\n    h2c->blocked = 0;\n\n    if (rc == NGX_AGAIN) {\n        return;\n    }\n\n    ngx_http_v2_handle_connection(h2c);\n}\n\n\nngx_int_t\nngx_http_v2_send_output_queue(ngx_http_v2_connection_t *h2c)\n{\n    int                        tcp_nodelay;\n    ngx_chain_t               *cl;\n    ngx_event_t               *wev;\n    ngx_connection_t          *c;\n    ngx_http_v2_out_frame_t   *out, *frame, *fn;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    c = h2c->connection;\n    wev = c->write;\n\n    if (c->error) {\n        goto error;\n    }\n\n    if (!wev->ready) {\n        return NGX_AGAIN;\n    }\n\n    cl = NULL;\n    out = NULL;\n\n    for (frame = h2c->last_out; frame; frame = fn) {\n        frame->last->next = cl;\n        cl = frame->first;\n\n        fn = frame->next;\n        frame->next = out;\n        out = frame;\n\n        ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"http2 frame out: %p sid:%ui bl:%d len:%uz\",\n                       out, out->stream ? out->stream->node->id : 0,\n                       out->blocked, out->length);\n    }\n\n    cl = c->send_chain(c, cl, 0);\n\n    if (cl == NGX_CHAIN_ERROR) {\n        goto error;\n    }\n\n    clcf = ngx_http_get_module_loc_conf(h2c->http_connection->conf_ctx,\n                                        ngx_http_core_module);\n\n    if (ngx_handle_write_event(wev, clcf->send_lowat) != NGX_OK) {\n        goto error;\n    }\n\n    if (c->tcp_nopush == NGX_TCP_NOPUSH_SET) {\n        if (ngx_tcp_push(c->fd) == -1) {\n            ngx_connection_error(c, ngx_socket_errno, ngx_tcp_push_n \" failed\");\n            goto error;\n        }\n\n        c->tcp_nopush = NGX_TCP_NOPUSH_UNSET;\n        tcp_nodelay = ngx_tcp_nodelay_and_tcp_nopush ? 1 : 0;\n\n    } else {\n        tcp_nodelay = 1;\n    }\n\n    if (tcp_nodelay && clcf->tcp_nodelay && ngx_tcp_nodelay(c) != NGX_OK) {\n        goto error;\n    }\n\n    for ( /* void */ ; out; out = fn) {\n        fn = out->next;\n\n        if (out->handler(h2c, out) != NGX_OK) {\n            out->blocked = 1;\n            break;\n        }\n\n        ngx_log_debug4(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                       \"http2 frame sent: %p sid:%ui bl:%d len:%uz\",\n                       out, out->stream ? out->stream->node->id : 0,\n                       out->blocked, out->length);\n    }\n\n    frame = NULL;\n\n    for ( /* void */ ; out; out = fn) {\n        fn = out->next;\n        out->next = frame;\n        frame = out;\n    }\n\n    h2c->last_out = frame;\n\n    if (!wev->ready) {\n        ngx_add_timer(wev, clcf->send_timeout);\n        return NGX_AGAIN;\n    }\n\n    if (wev->timer_set) {\n        ngx_del_timer(wev);\n    }\n\n    return NGX_OK;\n\nerror:\n\n    c->error = 1;\n\n    if (!h2c->blocked) {\n        ngx_post_event(wev, &ngx_posted_events);\n    }\n\n    return NGX_ERROR;\n}\n\n\nstatic void\nngx_http_v2_handle_connection(ngx_http_v2_connection_t *h2c)\n{\n    ngx_int_t                  rc;\n    ngx_connection_t          *c;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    if (h2c->last_out || h2c->processing || h2c->pushing) {\n        return;\n    }\n\n    c = h2c->connection;\n\n    if (c->error) {\n        ngx_http_close_connection(c);\n        return;\n    }\n\n    if (c->buffered) {\n        h2c->blocked = 1;\n\n        rc = ngx_http_v2_send_output_queue(h2c);\n\n        h2c->blocked = 0;\n\n        if (rc == NGX_ERROR) {\n            ngx_http_close_connection(c);\n            return;\n        }\n\n        if (rc == NGX_AGAIN) {\n            return;\n        }\n\n        /* rc == NGX_OK */\n    }\n\n    if (h2c->goaway) {\n        ngx_http_v2_lingering_close(c);\n        return;\n    }\n\n    clcf = ngx_http_get_module_loc_conf(h2c->http_connection->conf_ctx,\n                                        ngx_http_core_module);\n\n    if (!c->read->timer_set) {\n        ngx_add_timer(c->read, clcf->keepalive_timeout);\n    }\n\n    ngx_reusable_connection(c, 1);\n\n    if (h2c->state.incomplete) {\n        return;\n    }\n\n    ngx_destroy_pool(h2c->pool);\n\n    h2c->pool = NULL;\n    h2c->free_frames = NULL;\n    h2c->frames = 0;\n    h2c->free_fake_connections = NULL;\n\n#if (NGX_HTTP_SSL)\n    if (c->ssl) {\n        ngx_ssl_free_buffer(c);\n    }\n#endif\n\n    c->destroyed = 1;\n\n    c->write->handler = ngx_http_empty_handler;\n    c->read->handler = ngx_http_v2_idle_handler;\n\n    if (c->write->timer_set) {\n        ngx_del_timer(c->write);\n    }\n}\n\n\nstatic void\nngx_http_v2_lingering_close(ngx_connection_t *c)\n{\n    ngx_event_t               *rev, *wev;\n    ngx_http_v2_connection_t  *h2c;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    h2c = c->data;\n\n    clcf = ngx_http_get_module_loc_conf(h2c->http_connection->conf_ctx,\n                                        ngx_http_core_module);\n\n    if (clcf->lingering_close == NGX_HTTP_LINGERING_OFF) {\n        ngx_http_close_connection(c);\n        return;\n    }\n\n    if (h2c->lingering_time == 0) {\n        h2c->lingering_time = ngx_time()\n                              + (time_t) (clcf->lingering_time / 1000);\n    }\n\n#if (NGX_HTTP_SSL)\n    if (c->ssl) {\n        ngx_int_t  rc;\n\n        rc = ngx_ssl_shutdown(c);\n\n        if (rc == NGX_ERROR) {\n            ngx_http_close_connection(c);\n            return;\n        }\n\n        if (rc == NGX_AGAIN) {\n            c->ssl->handler = ngx_http_v2_lingering_close;\n            return;\n        }\n    }\n#endif\n\n    rev = c->read;\n    rev->handler = ngx_http_v2_lingering_close_handler;\n\n    if (ngx_handle_read_event(rev, 0) != NGX_OK) {\n        ngx_http_close_connection(c);\n        return;\n    }\n\n    wev = c->write;\n    wev->handler = ngx_http_empty_handler;\n\n    if (wev->active && (ngx_event_flags & NGX_USE_LEVEL_EVENT)) {\n        if (ngx_del_event(wev, NGX_WRITE_EVENT, 0) != NGX_OK) {\n            ngx_http_close_connection(c);\n            return;\n        }\n    }\n\n    if (ngx_shutdown_socket(c->fd, NGX_WRITE_SHUTDOWN) == -1) {\n        ngx_connection_error(c, ngx_socket_errno,\n                             ngx_shutdown_socket_n \" failed\");\n        ngx_http_close_connection(c);\n        return;\n    }\n\n    c->close = 0;\n    ngx_reusable_connection(c, 1);\n\n    ngx_add_timer(rev, clcf->lingering_timeout);\n\n    if (rev->ready) {\n        ngx_http_v2_lingering_close_handler(rev);\n    }\n}\n\n\nstatic void\nngx_http_v2_lingering_close_handler(ngx_event_t *rev)\n{\n    ssize_t                    n;\n    ngx_msec_t                 timer;\n    ngx_connection_t          *c;\n    ngx_http_core_loc_conf_t  *clcf;\n    ngx_http_v2_connection_t  *h2c;\n    u_char                     buffer[NGX_HTTP_LINGERING_BUFFER_SIZE];\n\n    c = rev->data;\n    h2c = c->data;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,\n                   \"http2 lingering close handler\");\n\n    if (rev->timedout || c->close) {\n        ngx_http_close_connection(c);\n        return;\n    }\n\n    timer = (ngx_msec_t) h2c->lingering_time - (ngx_msec_t) ngx_time();\n    if ((ngx_msec_int_t) timer <= 0) {\n        ngx_http_close_connection(c);\n        return;\n    }\n\n    do {\n        n = c->recv(c, buffer, NGX_HTTP_LINGERING_BUFFER_SIZE);\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, \"lingering read: %z\", n);\n\n        if (n == NGX_AGAIN) {\n            break;\n        }\n\n        if (n == NGX_ERROR || n == 0) {\n            ngx_http_close_connection(c);\n            return;\n        }\n\n    } while (rev->ready);\n\n    if (ngx_handle_read_event(rev, 0) != NGX_OK) {\n        ngx_http_close_connection(c);\n        return;\n    }\n\n    clcf = ngx_http_get_module_loc_conf(h2c->http_connection->conf_ctx,\n                                        ngx_http_core_module);\n    timer *= 1000;\n\n    if (timer > clcf->lingering_timeout) {\n        timer = clcf->lingering_timeout;\n    }\n\n    ngx_add_timer(rev, timer);\n}\n\n\nstatic u_char *\nngx_http_v2_state_proxy_protocol(ngx_http_v2_connection_t *h2c, u_char *pos,\n    u_char *end)\n{\n    ngx_log_t  *log;\n\n    log = h2c->connection->log;\n    log->action = \"reading PROXY protocol\";\n\n    pos = ngx_proxy_protocol_read(h2c->connection, pos, end);\n\n    log->action = \"processing HTTP/2 connection\";\n\n    if (pos == NULL) {\n        return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR);\n    }\n\n    return ngx_http_v2_state_preface(h2c, pos, end);\n}\n\n\nstatic u_char *\nngx_http_v2_state_preface(ngx_http_v2_connection_t *h2c, u_char *pos,\n    u_char *end)\n{\n    static const u_char preface[] = \"PRI * HTTP/2.0\\r\\n\";\n\n    if ((size_t) (end - pos) < sizeof(preface) - 1) {\n        return ngx_http_v2_state_save(h2c, pos, end, ngx_http_v2_state_preface);\n    }\n\n    if (ngx_memcmp(pos, preface, sizeof(preface) - 1) != 0) {\n        ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                      \"invalid connection preface\");\n\n        return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR);\n    }\n\n    return ngx_http_v2_state_preface_end(h2c, pos + sizeof(preface) - 1, end);\n}\n\n\nstatic u_char *\nngx_http_v2_state_preface_end(ngx_http_v2_connection_t *h2c, u_char *pos,\n    u_char *end)\n{\n    static const u_char preface[] = \"\\r\\nSM\\r\\n\\r\\n\";\n\n    if ((size_t) (end - pos) < sizeof(preface) - 1) {\n        return ngx_http_v2_state_save(h2c, pos, end,\n                                      ngx_http_v2_state_preface_end);\n    }\n\n    if (ngx_memcmp(pos, preface, sizeof(preface) - 1) != 0) {\n        ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                      \"invalid connection preface\");\n\n        return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR);\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n                   \"http2 preface verified\");\n\n    return ngx_http_v2_state_head(h2c, pos + sizeof(preface) - 1, end);\n}\n\n\nstatic u_char *\nngx_http_v2_state_head(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end)\n{\n    uint32_t    head;\n    ngx_uint_t  type;\n\n    if (end - pos < NGX_HTTP_V2_FRAME_HEADER_SIZE) {\n        return ngx_http_v2_state_save(h2c, pos, end, ngx_http_v2_state_head);\n    }\n\n    head = ngx_http_v2_parse_uint32(pos);\n\n    h2c->state.length = ngx_http_v2_parse_length(head);\n    h2c->state.flags = pos[4];\n\n    h2c->state.sid = ngx_http_v2_parse_sid(&pos[5]);\n\n    pos += NGX_HTTP_V2_FRAME_HEADER_SIZE;\n\n    type = ngx_http_v2_parse_type(head);\n\n    ngx_log_debug4(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n                   \"http2 frame type:%ui f:%Xd l:%uz sid:%ui\",\n                   type, h2c->state.flags, h2c->state.length, h2c->state.sid);\n\n    if (type >= NGX_HTTP_V2_FRAME_STATES) {\n        ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                      \"client sent frame with unknown type %ui\", type);\n        return ngx_http_v2_state_skip(h2c, pos, end);\n    }\n\n    return ngx_http_v2_frame_states[type](h2c, pos, end);\n}\n\n\nstatic u_char *\nngx_http_v2_state_data(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end)\n{\n    size_t                 size;\n    ngx_http_v2_node_t    *node;\n    ngx_http_v2_stream_t  *stream;\n\n    size = h2c->state.length;\n\n    if (h2c->state.flags & NGX_HTTP_V2_PADDED_FLAG) {\n\n        if (h2c->state.length == 0) {\n            ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                          \"client sent padded DATA frame \"\n                          \"with incorrect length: 0\");\n\n            return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR);\n        }\n\n        if (end - pos == 0) {\n            return ngx_http_v2_state_save(h2c, pos, end,\n                                          ngx_http_v2_state_data);\n        }\n\n        h2c->state.padding = *pos++;\n\n        if (h2c->state.padding >= size) {\n            ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                          \"client sent padded DATA frame \"\n                          \"with incorrect length: %uz, padding: %uz\",\n                          size, h2c->state.padding);\n\n            return ngx_http_v2_connection_error(h2c,\n                                                NGX_HTTP_V2_PROTOCOL_ERROR);\n        }\n\n        h2c->state.length -= 1 + h2c->state.padding;\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n                   \"http2 DATA frame\");\n\n    if (h2c->state.sid == 0) {\n        ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                      \"client sent DATA frame with incorrect identifier\");\n\n        return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR);\n    }\n\n    if (size > h2c->recv_window) {\n        ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                      \"client violated connection flow control: \"\n                      \"received DATA frame length %uz, available window %uz\",\n                      size, h2c->recv_window);\n\n        return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_FLOW_CTRL_ERROR);\n    }\n\n    h2c->recv_window -= size;\n\n    if (h2c->recv_window < NGX_HTTP_V2_MAX_WINDOW / 4) {\n\n        if (ngx_http_v2_send_window_update(h2c, 0, NGX_HTTP_V2_MAX_WINDOW\n                                                   - h2c->recv_window)\n            == NGX_ERROR)\n        {\n            return ngx_http_v2_connection_error(h2c,\n                                                NGX_HTTP_V2_INTERNAL_ERROR);\n        }\n\n        h2c->recv_window = NGX_HTTP_V2_MAX_WINDOW;\n    }\n\n    node = ngx_http_v2_get_node_by_id(h2c, h2c->state.sid, 0);\n\n    if (node == NULL || node->stream == NULL) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n                       \"unknown http2 stream\");\n\n        return ngx_http_v2_state_skip_padded(h2c, pos, end);\n    }\n\n    stream = node->stream;\n\n    if (size > stream->recv_window) {\n        ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                      \"client violated flow control for stream %ui: \"\n                      \"received DATA frame length %uz, available window %uz\",\n                      node->id, size, stream->recv_window);\n\n        if (ngx_http_v2_terminate_stream(h2c, stream,\n                                         NGX_HTTP_V2_FLOW_CTRL_ERROR)\n            == NGX_ERROR)\n        {\n            return ngx_http_v2_connection_error(h2c,\n                                                NGX_HTTP_V2_INTERNAL_ERROR);\n        }\n\n        return ngx_http_v2_state_skip_padded(h2c, pos, end);\n    }\n\n    stream->recv_window -= size;\n\n    if (stream->no_flow_control\n        && stream->recv_window < NGX_HTTP_V2_MAX_WINDOW / 4)\n    {\n        if (ngx_http_v2_send_window_update(h2c, node->id,\n                                           NGX_HTTP_V2_MAX_WINDOW\n                                           - stream->recv_window)\n            == NGX_ERROR)\n        {\n            return ngx_http_v2_connection_error(h2c,\n                                                NGX_HTTP_V2_INTERNAL_ERROR);\n        }\n\n        stream->recv_window = NGX_HTTP_V2_MAX_WINDOW;\n    }\n\n    if (stream->in_closed) {\n        ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                      \"client sent DATA frame for half-closed stream %ui\",\n                      node->id);\n\n        if (ngx_http_v2_terminate_stream(h2c, stream,\n                                         NGX_HTTP_V2_STREAM_CLOSED)\n            == NGX_ERROR)\n        {\n            return ngx_http_v2_connection_error(h2c,\n                                                NGX_HTTP_V2_INTERNAL_ERROR);\n        }\n\n        return ngx_http_v2_state_skip_padded(h2c, pos, end);\n    }\n\n    h2c->state.stream = stream;\n\n    return ngx_http_v2_state_read_data(h2c, pos, end);\n}\n\n\nstatic u_char *\nngx_http_v2_state_read_data(ngx_http_v2_connection_t *h2c, u_char *pos,\n    u_char *end)\n{\n    size_t                   size;\n    ngx_buf_t               *buf;\n    ngx_int_t                rc;\n    ngx_connection_t        *fc;\n    ngx_http_request_t      *r;\n    ngx_http_v2_stream_t    *stream;\n    ngx_http_v2_srv_conf_t  *h2scf;\n\n    stream = h2c->state.stream;\n\n    if (stream == NULL) {\n        return ngx_http_v2_state_skip_padded(h2c, pos, end);\n    }\n\n    if (stream->skip_data) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n                       \"skipping http2 DATA frame\");\n\n        return ngx_http_v2_state_skip_padded(h2c, pos, end);\n    }\n\n    r = stream->request;\n    fc = r->connection;\n\n    if (r->reading_body && !r->request_body_no_buffering) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n                       \"skipping http2 DATA frame\");\n\n        return ngx_http_v2_state_skip_padded(h2c, pos, end);\n    }\n\n    if (r->headers_in.content_length_n < 0 && !r->headers_in.chunked) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n                       \"skipping http2 DATA frame\");\n\n        return ngx_http_v2_state_skip_padded(h2c, pos, end);\n    }\n\n    size = end - pos;\n\n    if (size >= h2c->state.length) {\n        size = h2c->state.length;\n        stream->in_closed = h2c->state.flags & NGX_HTTP_V2_END_STREAM_FLAG;\n    }\n\n    h2c->payload_bytes += size;\n\n    if (r->request_body) {\n        rc = ngx_http_v2_process_request_body(r, pos, size,\n                                              stream->in_closed, 0);\n\n        if (rc != NGX_OK && rc != NGX_AGAIN) {\n            stream->skip_data = 1;\n            ngx_http_finalize_request(r, rc);\n        }\n\n        ngx_http_run_posted_requests(fc);\n\n    } else if (size) {\n        buf = stream->preread;\n\n        if (buf == NULL) {\n            h2scf = ngx_http_get_module_srv_conf(r, ngx_http_v2_module);\n\n            buf = ngx_create_temp_buf(r->pool, h2scf->preread_size);\n            if (buf == NULL) {\n                return ngx_http_v2_connection_error(h2c,\n                                                    NGX_HTTP_V2_INTERNAL_ERROR);\n            }\n\n            stream->preread = buf;\n        }\n\n        if (size > (size_t) (buf->end - buf->last)) {\n            ngx_log_error(NGX_LOG_ALERT, h2c->connection->log, 0,\n                          \"http2 preread buffer overflow\");\n            return ngx_http_v2_connection_error(h2c,\n                                                NGX_HTTP_V2_INTERNAL_ERROR);\n        }\n\n        buf->last = ngx_cpymem(buf->last, pos, size);\n    }\n\n    pos += size;\n    h2c->state.length -= size;\n\n    if (h2c->state.length) {\n        return ngx_http_v2_state_save(h2c, pos, end,\n                                      ngx_http_v2_state_read_data);\n    }\n\n    if (h2c->state.padding) {\n        return ngx_http_v2_state_skip_padded(h2c, pos, end);\n    }\n\n    return ngx_http_v2_state_complete(h2c, pos, end);\n}\n\n\nstatic u_char *\nngx_http_v2_state_headers(ngx_http_v2_connection_t *h2c, u_char *pos,\n    u_char *end)\n{\n    size_t                     size;\n    ngx_uint_t                 padded, priority, depend, dependency, excl,\n                               weight;\n    ngx_uint_t                 status;\n    ngx_http_v2_node_t        *node;\n    ngx_http_v2_stream_t      *stream;\n    ngx_http_v2_srv_conf_t    *h2scf;\n    ngx_http_core_srv_conf_t  *cscf;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    padded = h2c->state.flags & NGX_HTTP_V2_PADDED_FLAG;\n    priority = h2c->state.flags & NGX_HTTP_V2_PRIORITY_FLAG;\n\n    size = 0;\n\n    if (padded) {\n        size++;\n    }\n\n    if (priority) {\n        size += sizeof(uint32_t) + 1;\n    }\n\n    if (h2c->state.length < size) {\n        ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                      \"client sent HEADERS frame with incorrect length %uz\",\n                      h2c->state.length);\n\n        return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR);\n    }\n\n    if (h2c->state.length == size) {\n        ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                      \"client sent HEADERS frame with empty header block\");\n\n        return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR);\n    }\n\n    if (h2c->goaway) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n                       \"skipping http2 HEADERS frame\");\n        return ngx_http_v2_state_skip(h2c, pos, end);\n    }\n\n    if ((size_t) (end - pos) < size) {\n        return ngx_http_v2_state_save(h2c, pos, end,\n                                      ngx_http_v2_state_headers);\n    }\n\n    h2c->state.length -= size;\n\n    if (padded) {\n        h2c->state.padding = *pos++;\n\n        if (h2c->state.padding > h2c->state.length) {\n            ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                          \"client sent padded HEADERS frame \"\n                          \"with incorrect length: %uz, padding: %uz\",\n                          h2c->state.length, h2c->state.padding);\n\n            return ngx_http_v2_connection_error(h2c,\n                                                NGX_HTTP_V2_PROTOCOL_ERROR);\n        }\n\n        h2c->state.length -= h2c->state.padding;\n    }\n\n    depend = 0;\n    excl = 0;\n    weight = NGX_HTTP_V2_DEFAULT_WEIGHT;\n\n    if (priority) {\n        dependency = ngx_http_v2_parse_uint32(pos);\n\n        depend = dependency & 0x7fffffff;\n        excl = dependency >> 31;\n        weight = pos[4] + 1;\n\n        pos += sizeof(uint32_t) + 1;\n    }\n\n    ngx_log_debug4(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n                   \"http2 HEADERS frame sid:%ui \"\n                   \"depends on %ui excl:%ui weight:%ui\",\n                   h2c->state.sid, depend, excl, weight);\n\n    if (h2c->state.sid % 2 == 0 || h2c->state.sid <= h2c->last_sid) {\n        ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                      \"client sent HEADERS frame with incorrect identifier \"\n                      \"%ui, the last was %ui\", h2c->state.sid, h2c->last_sid);\n\n        return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR);\n    }\n\n    if (depend == h2c->state.sid) {\n        ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                      \"client sent HEADERS frame for stream %ui \"\n                      \"with incorrect dependency\", h2c->state.sid);\n\n        return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR);\n    }\n\n    h2c->last_sid = h2c->state.sid;\n\n    h2c->state.pool = ngx_create_pool(1024, h2c->connection->log);\n    if (h2c->state.pool == NULL) {\n        return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR);\n    }\n\n    cscf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx,\n                                        ngx_http_core_module);\n\n    h2c->state.header_limit = cscf->large_client_header_buffers.size\n                              * cscf->large_client_header_buffers.num;\n\n    h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx,\n                                         ngx_http_v2_module);\n\n    if (h2c->processing >= h2scf->concurrent_streams) {\n        ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                      \"concurrent streams exceeded %ui\", h2c->processing);\n\n        status = NGX_HTTP_V2_REFUSED_STREAM;\n        goto rst_stream;\n    }\n\n    if (h2c->new_streams++ >= 2 * h2scf->concurrent_streams) {\n        ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                      \"client sent too many streams at once\");\n\n        status = NGX_HTTP_V2_REFUSED_STREAM;\n        goto rst_stream;\n    }\n\n    if (!h2c->settings_ack\n        && !(h2c->state.flags & NGX_HTTP_V2_END_STREAM_FLAG)\n        && h2scf->preread_size < NGX_HTTP_V2_DEFAULT_WINDOW)\n    {\n        ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                      \"client sent stream with data \"\n                      \"before settings were acknowledged\");\n\n        status = NGX_HTTP_V2_REFUSED_STREAM;\n        goto rst_stream;\n    }\n\n    node = ngx_http_v2_get_node_by_id(h2c, h2c->state.sid, 1);\n\n    if (node == NULL) {\n        return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR);\n    }\n\n    if (node->parent) {\n        ngx_queue_remove(&node->reuse);\n        h2c->closed_nodes--;\n    }\n\n    stream = ngx_http_v2_create_stream(h2c, 0);\n    if (stream == NULL) {\n        return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR);\n    }\n\n    h2c->state.stream = stream;\n\n    stream->pool = h2c->state.pool;\n    h2c->state.keep_pool = 1;\n\n    stream->request->request_length = h2c->state.length;\n\n    stream->in_closed = h2c->state.flags & NGX_HTTP_V2_END_STREAM_FLAG;\n    stream->node = node;\n\n    node->stream = stream;\n\n    if (priority || node->parent == NULL) {\n        node->weight = weight;\n        ngx_http_v2_set_dependency(h2c, node, depend, excl);\n    }\n\n    clcf = ngx_http_get_module_loc_conf(h2c->http_connection->conf_ctx,\n                                        ngx_http_core_module);\n\n    if (clcf->keepalive_timeout == 0\n        || h2c->connection->requests >= clcf->keepalive_requests\n        || ngx_current_msec - h2c->connection->start_time\n           > clcf->keepalive_time)\n    {\n        h2c->goaway = 1;\n\n        if (ngx_http_v2_send_goaway(h2c, NGX_HTTP_V2_NO_ERROR) == NGX_ERROR) {\n            return ngx_http_v2_connection_error(h2c,\n                                                NGX_HTTP_V2_INTERNAL_ERROR);\n        }\n    }\n\n    return ngx_http_v2_state_header_block(h2c, pos, end);\n\nrst_stream:\n\n    if (h2c->refused_streams++ > ngx_max(h2scf->concurrent_streams, 100)) {\n        ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                      \"client sent too many refused streams\");\n        return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_NO_ERROR);\n    }\n\n    if (ngx_http_v2_send_rst_stream(h2c, h2c->state.sid, status) != NGX_OK) {\n        return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR);\n    }\n\n    return ngx_http_v2_state_header_block(h2c, pos, end);\n}\n\n\nstatic u_char *\nngx_http_v2_state_header_block(ngx_http_v2_connection_t *h2c, u_char *pos,\n    u_char *end)\n{\n    u_char      ch;\n    ngx_int_t   value;\n    ngx_uint_t  indexed, size_update, prefix;\n\n    if (end - pos < 1) {\n        return ngx_http_v2_state_headers_save(h2c, pos, end,\n                                              ngx_http_v2_state_header_block);\n    }\n\n    if (!(h2c->state.flags & NGX_HTTP_V2_END_HEADERS_FLAG)\n        && h2c->state.length < NGX_HTTP_V2_INT_OCTETS)\n    {\n        return ngx_http_v2_handle_continuation(h2c, pos, end,\n                                               ngx_http_v2_state_header_block);\n    }\n\n    size_update = 0;\n    indexed = 0;\n\n    ch = *pos;\n\n    if (ch >= (1 << 7)) {\n        /* indexed header field */\n        indexed = 1;\n        prefix = ngx_http_v2_prefix(7);\n\n    } else if (ch >= (1 << 6)) {\n        /* literal header field with incremental indexing */\n        h2c->state.index = 1;\n        prefix = ngx_http_v2_prefix(6);\n\n    } else if (ch >= (1 << 5)) {\n        /* dynamic table size update */\n        size_update = 1;\n        prefix = ngx_http_v2_prefix(5);\n\n    } else if (ch >= (1 << 4)) {\n        /* literal header field never indexed */\n        prefix = ngx_http_v2_prefix(4);\n\n    } else {\n        /* literal header field without indexing */\n        prefix = ngx_http_v2_prefix(4);\n    }\n\n    value = ngx_http_v2_parse_int(h2c, &pos, end, prefix);\n\n    if (value < 0) {\n        if (value == NGX_AGAIN) {\n            return ngx_http_v2_state_headers_save(h2c, pos, end,\n                                               ngx_http_v2_state_header_block);\n        }\n\n        if (value == NGX_DECLINED) {\n            ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                          \"client sent header block with too long %s value\",\n                          size_update ? \"size update\" : \"header index\");\n\n            return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_COMP_ERROR);\n        }\n\n        ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                      \"client sent header block with incorrect length\");\n\n        return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR);\n    }\n\n    if (indexed) {\n        if (ngx_http_v2_get_indexed_header(h2c, value, 0) != NGX_OK) {\n            return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_COMP_ERROR);\n        }\n\n        return ngx_http_v2_state_process_header(h2c, pos, end);\n    }\n\n    if (size_update) {\n        if (ngx_http_v2_table_size(h2c, value) != NGX_OK) {\n            return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_COMP_ERROR);\n        }\n\n        return ngx_http_v2_state_header_complete(h2c, pos, end);\n    }\n\n    if (value == 0) {\n        h2c->state.parse_name = 1;\n\n    } else if (ngx_http_v2_get_indexed_header(h2c, value, 1) != NGX_OK) {\n        return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_COMP_ERROR);\n    }\n\n    h2c->state.parse_value = 1;\n\n    return ngx_http_v2_state_field_len(h2c, pos, end);\n}\n\n\nstatic u_char *\nngx_http_v2_state_field_len(ngx_http_v2_connection_t *h2c, u_char *pos,\n    u_char *end)\n{\n    size_t                     alloc;\n    ngx_int_t                  len;\n    ngx_uint_t                 huff;\n    ngx_http_core_srv_conf_t  *cscf;\n\n    if (!(h2c->state.flags & NGX_HTTP_V2_END_HEADERS_FLAG)\n        && h2c->state.length < NGX_HTTP_V2_INT_OCTETS)\n    {\n        return ngx_http_v2_handle_continuation(h2c, pos, end,\n                                               ngx_http_v2_state_field_len);\n    }\n\n    if (h2c->state.length < 1) {\n        ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                      \"client sent header block with incorrect length\");\n\n        return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR);\n    }\n\n    if (end - pos < 1) {\n        return ngx_http_v2_state_headers_save(h2c, pos, end,\n                                              ngx_http_v2_state_field_len);\n    }\n\n    huff = *pos >> 7;\n    len = ngx_http_v2_parse_int(h2c, &pos, end, ngx_http_v2_prefix(7));\n\n    if (len < 0) {\n        if (len == NGX_AGAIN) {\n            return ngx_http_v2_state_headers_save(h2c, pos, end,\n                                                  ngx_http_v2_state_field_len);\n        }\n\n        if (len == NGX_DECLINED) {\n            ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                        \"client sent header field with too long length value\");\n\n            return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_COMP_ERROR);\n        }\n\n        ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                      \"client sent header block with incorrect length\");\n\n        return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR);\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n                   \"http2 %s string, len:%i\",\n                   huff ? \"encoded\" : \"raw\", len);\n\n    cscf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx,\n                                        ngx_http_core_module);\n\n    if ((size_t) len > cscf->large_client_header_buffers.size) {\n        ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                      \"client sent too large header field\");\n\n        return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_ENHANCE_YOUR_CALM);\n    }\n\n    h2c->state.field_rest = len;\n\n    if (h2c->state.stream == NULL && !h2c->state.index) {\n        return ngx_http_v2_state_field_skip(h2c, pos, end);\n    }\n\n    alloc = (huff ? len * 8 / 5 : len) + 1;\n\n    h2c->state.field_start = ngx_pnalloc(h2c->state.pool, alloc);\n    if (h2c->state.field_start == NULL) {\n        return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR);\n    }\n\n    h2c->state.field_end = h2c->state.field_start;\n\n    if (huff) {\n        return ngx_http_v2_state_field_huff(h2c, pos, end);\n    }\n\n    return ngx_http_v2_state_field_raw(h2c, pos, end);\n}\n\n\nstatic u_char *\nngx_http_v2_state_field_huff(ngx_http_v2_connection_t *h2c, u_char *pos,\n    u_char *end)\n{\n    size_t  size;\n\n    size = end - pos;\n\n    if (size > h2c->state.field_rest) {\n        size = h2c->state.field_rest;\n    }\n\n    if (size > h2c->state.length) {\n        size = h2c->state.length;\n    }\n\n    h2c->state.length -= size;\n    h2c->state.field_rest -= size;\n\n    if (ngx_http_huff_decode(&h2c->state.field_state, pos, size,\n                             &h2c->state.field_end,\n                             h2c->state.field_rest == 0,\n                             h2c->connection->log)\n        != NGX_OK)\n    {\n        ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                      \"client sent invalid encoded header field\");\n\n        return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_COMP_ERROR);\n    }\n\n    pos += size;\n\n    if (h2c->state.field_rest == 0) {\n        *h2c->state.field_end = '\\0';\n        return ngx_http_v2_state_process_header(h2c, pos, end);\n    }\n\n    if (h2c->state.length) {\n        return ngx_http_v2_state_headers_save(h2c, pos, end,\n                                              ngx_http_v2_state_field_huff);\n    }\n\n    if (h2c->state.flags & NGX_HTTP_V2_END_HEADERS_FLAG) {\n        ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                      \"client sent header field with incorrect length\");\n\n        return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR);\n    }\n\n    return ngx_http_v2_handle_continuation(h2c, pos, end,\n                                           ngx_http_v2_state_field_huff);\n}\n\n\nstatic u_char *\nngx_http_v2_state_field_raw(ngx_http_v2_connection_t *h2c, u_char *pos,\n    u_char *end)\n{\n    size_t  size;\n\n    size = end - pos;\n\n    if (size > h2c->state.field_rest) {\n        size = h2c->state.field_rest;\n    }\n\n    if (size > h2c->state.length) {\n        size = h2c->state.length;\n    }\n\n    h2c->state.length -= size;\n    h2c->state.field_rest -= size;\n\n    h2c->state.field_end = ngx_cpymem(h2c->state.field_end, pos, size);\n\n    pos += size;\n\n    if (h2c->state.field_rest == 0) {\n        *h2c->state.field_end = '\\0';\n        return ngx_http_v2_state_process_header(h2c, pos, end);\n    }\n\n    if (h2c->state.length) {\n        return ngx_http_v2_state_headers_save(h2c, pos, end,\n                                              ngx_http_v2_state_field_raw);\n    }\n\n    if (h2c->state.flags & NGX_HTTP_V2_END_HEADERS_FLAG) {\n        ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                      \"client sent header field with incorrect length\");\n\n        return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR);\n    }\n\n    return ngx_http_v2_handle_continuation(h2c, pos, end,\n                                           ngx_http_v2_state_field_raw);\n}\n\n\nstatic u_char *\nngx_http_v2_state_field_skip(ngx_http_v2_connection_t *h2c, u_char *pos,\n    u_char *end)\n{\n    size_t  size;\n\n    size = end - pos;\n\n    if (size > h2c->state.field_rest) {\n        size = h2c->state.field_rest;\n    }\n\n    if (size > h2c->state.length) {\n        size = h2c->state.length;\n    }\n\n    h2c->state.length -= size;\n    h2c->state.field_rest -= size;\n\n    pos += size;\n\n    if (h2c->state.field_rest == 0) {\n        return ngx_http_v2_state_process_header(h2c, pos, end);\n    }\n\n    if (h2c->state.length) {\n        return ngx_http_v2_state_save(h2c, pos, end,\n                                      ngx_http_v2_state_field_skip);\n    }\n\n    if (h2c->state.flags & NGX_HTTP_V2_END_HEADERS_FLAG) {\n        ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                      \"client sent header field with incorrect length\");\n\n        return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR);\n    }\n\n    return ngx_http_v2_handle_continuation(h2c, pos, end,\n                                           ngx_http_v2_state_field_skip);\n}\n\n\nstatic u_char *\nngx_http_v2_state_process_header(ngx_http_v2_connection_t *h2c, u_char *pos,\n    u_char *end)\n{\n    size_t                      len;\n    ngx_int_t                   rc;\n    ngx_table_elt_t            *h;\n    ngx_connection_t           *fc;\n    ngx_http_header_t          *hh;\n    ngx_http_request_t         *r;\n    ngx_http_v2_header_t       *header;\n    ngx_http_core_srv_conf_t   *cscf;\n    ngx_http_core_main_conf_t  *cmcf;\n\n    static ngx_str_t cookie = ngx_string(\"cookie\");\n\n    header = &h2c->state.header;\n\n    if (h2c->state.parse_name) {\n        h2c->state.parse_name = 0;\n\n        header->name.len = h2c->state.field_end - h2c->state.field_start;\n        header->name.data = h2c->state.field_start;\n\n        if (header->name.len == 0) {\n            ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                          \"client sent zero header name length\");\n\n            return ngx_http_v2_connection_error(h2c,\n                                                NGX_HTTP_V2_PROTOCOL_ERROR);\n        }\n\n        return ngx_http_v2_state_field_len(h2c, pos, end);\n    }\n\n    if (h2c->state.parse_value) {\n        h2c->state.parse_value = 0;\n\n        header->value.len = h2c->state.field_end - h2c->state.field_start;\n        header->value.data = h2c->state.field_start;\n    }\n\n    len = header->name.len + header->value.len;\n\n    if (len > h2c->state.header_limit) {\n        ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                      \"client sent too large header\");\n\n        return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_ENHANCE_YOUR_CALM);\n    }\n\n    h2c->state.header_limit -= len;\n\n    if (h2c->state.index) {\n        if (ngx_http_v2_add_header(h2c, header) != NGX_OK) {\n            return ngx_http_v2_connection_error(h2c,\n                                                NGX_HTTP_V2_INTERNAL_ERROR);\n        }\n\n        h2c->state.index = 0;\n    }\n\n    if (h2c->state.stream == NULL) {\n        return ngx_http_v2_state_header_complete(h2c, pos, end);\n    }\n\n    r = h2c->state.stream->request;\n    fc = r->connection;\n\n    /* TODO Optimization: validate headers while parsing. */\n    if (ngx_http_v2_validate_header(r, header) != NGX_OK) {\n        ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);\n        goto error;\n    }\n\n    if (header->name.data[0] == ':') {\n        rc = ngx_http_v2_pseudo_header(r, header);\n\n        if (rc == NGX_OK) {\n            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                           \"http2 header: \\\":%V: %V\\\"\",\n                           &header->name, &header->value);\n\n            return ngx_http_v2_state_header_complete(h2c, pos, end);\n        }\n\n        if (rc == NGX_ABORT) {\n            goto error;\n        }\n\n        if (rc == NGX_DECLINED) {\n            ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);\n            goto error;\n        }\n\n        return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR);\n    }\n\n    if (r->invalid_header) {\n        cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);\n\n        if (cscf->ignore_invalid_headers) {\n            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                          \"client sent invalid header: \\\"%V\\\"\", &header->name);\n\n            return ngx_http_v2_state_header_complete(h2c, pos, end);\n        }\n    }\n\n    if (header->name.len == cookie.len\n        && ngx_memcmp(header->name.data, cookie.data, cookie.len) == 0)\n    {\n        if (ngx_http_v2_cookie(r, header) != NGX_OK) {\n            return ngx_http_v2_connection_error(h2c,\n                                                NGX_HTTP_V2_INTERNAL_ERROR);\n        }\n\n    } else {\n        h = ngx_list_push(&r->headers_in.headers);\n        if (h == NULL) {\n            return ngx_http_v2_connection_error(h2c,\n                                                NGX_HTTP_V2_INTERNAL_ERROR);\n        }\n\n        h->key.len = header->name.len;\n        h->key.data = header->name.data;\n\n        /*\n         * TODO Optimization: precalculate hash\n         * and handler for indexed headers.\n         */\n        h->hash = ngx_hash_key(h->key.data, h->key.len);\n\n        h->value.len = header->value.len;\n        h->value.data = header->value.data;\n\n        h->lowcase_key = h->key.data;\n\n        cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);\n\n        hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash,\n                           h->lowcase_key, h->key.len);\n\n        if (hh && hh->handler(r, h, hh->offset) != NGX_OK) {\n            goto error;\n        }\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http2 header: \\\"%V: %V\\\"\",\n                   &header->name, &header->value);\n\n    return ngx_http_v2_state_header_complete(h2c, pos, end);\n\nerror:\n\n    h2c->state.stream = NULL;\n\n    ngx_http_run_posted_requests(fc);\n\n    return ngx_http_v2_state_header_complete(h2c, pos, end);\n}\n\n\nstatic u_char *\nngx_http_v2_state_header_complete(ngx_http_v2_connection_t *h2c, u_char *pos,\n    u_char *end)\n{\n    ngx_http_v2_stream_t  *stream;\n\n    if (h2c->state.length) {\n        if (end - pos > 0) {\n            h2c->state.handler = ngx_http_v2_state_header_block;\n            return pos;\n        }\n\n        return ngx_http_v2_state_headers_save(h2c, pos, end,\n                                              ngx_http_v2_state_header_block);\n    }\n\n    if (!(h2c->state.flags & NGX_HTTP_V2_END_HEADERS_FLAG)) {\n        return ngx_http_v2_handle_continuation(h2c, pos, end,\n                                             ngx_http_v2_state_header_complete);\n    }\n\n    stream = h2c->state.stream;\n\n    if (stream) {\n        ngx_http_v2_run_request(stream->request);\n    }\n\n    if (!h2c->state.keep_pool) {\n        ngx_destroy_pool(h2c->state.pool);\n    }\n\n    h2c->state.pool = NULL;\n    h2c->state.keep_pool = 0;\n\n    if (h2c->state.padding) {\n        return ngx_http_v2_state_skip_padded(h2c, pos, end);\n    }\n\n    return ngx_http_v2_state_complete(h2c, pos, end);\n}\n\n\nstatic u_char *\nngx_http_v2_handle_continuation(ngx_http_v2_connection_t *h2c, u_char *pos,\n    u_char *end, ngx_http_v2_handler_pt handler)\n{\n    u_char    *p;\n    size_t     len, skip;\n    uint32_t   head;\n\n    len = h2c->state.length;\n\n    if (h2c->state.padding && (size_t) (end - pos) > len) {\n        skip = ngx_min(h2c->state.padding, (end - pos) - len);\n\n        h2c->state.padding -= skip;\n\n        p = pos;\n        pos += skip;\n        ngx_memmove(pos, p, len);\n    }\n\n    if ((size_t) (end - pos) < len + NGX_HTTP_V2_FRAME_HEADER_SIZE) {\n        return ngx_http_v2_state_headers_save(h2c, pos, end, handler);\n    }\n\n    p = pos + len;\n\n    head = ngx_http_v2_parse_uint32(p);\n\n    if (ngx_http_v2_parse_type(head) != NGX_HTTP_V2_CONTINUATION_FRAME) {\n        ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n             \"client sent inappropriate frame while CONTINUATION was expected\");\n\n        return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR);\n    }\n\n    h2c->state.flags |= p[4];\n\n    if (h2c->state.sid != ngx_http_v2_parse_sid(&p[5])) {\n        ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                    \"client sent CONTINUATION frame with incorrect identifier\");\n\n        return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR);\n    }\n\n    p = pos;\n    pos += NGX_HTTP_V2_FRAME_HEADER_SIZE;\n\n    ngx_memcpy(pos, p, len);\n\n    len = ngx_http_v2_parse_length(head);\n\n    h2c->state.length += len;\n\n    if (h2c->state.stream) {\n        h2c->state.stream->request->request_length += len;\n    }\n\n    h2c->state.handler = handler;\n    return pos;\n}\n\n\nstatic u_char *\nngx_http_v2_state_priority(ngx_http_v2_connection_t *h2c, u_char *pos,\n    u_char *end)\n{\n    ngx_uint_t           depend, dependency, excl, weight;\n    ngx_http_v2_node_t  *node;\n\n    if (h2c->state.length != NGX_HTTP_V2_PRIORITY_SIZE) {\n        ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                      \"client sent PRIORITY frame with incorrect length %uz\",\n                      h2c->state.length);\n\n        return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR);\n    }\n\n    if (--h2c->priority_limit == 0) {\n        ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                      \"client sent too many PRIORITY frames\");\n\n        return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_ENHANCE_YOUR_CALM);\n    }\n\n    if (end - pos < NGX_HTTP_V2_PRIORITY_SIZE) {\n        return ngx_http_v2_state_save(h2c, pos, end,\n                                      ngx_http_v2_state_priority);\n    }\n\n    dependency = ngx_http_v2_parse_uint32(pos);\n\n    depend = dependency & 0x7fffffff;\n    excl = dependency >> 31;\n    weight = pos[4] + 1;\n\n    pos += NGX_HTTP_V2_PRIORITY_SIZE;\n\n    ngx_log_debug4(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n                   \"http2 PRIORITY frame sid:%ui \"\n                   \"depends on %ui excl:%ui weight:%ui\",\n                   h2c->state.sid, depend, excl, weight);\n\n    if (h2c->state.sid == 0) {\n        ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                      \"client sent PRIORITY frame with incorrect identifier\");\n\n        return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR);\n    }\n\n    if (depend == h2c->state.sid) {\n        ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                      \"client sent PRIORITY frame for stream %ui \"\n                      \"with incorrect dependency\", h2c->state.sid);\n\n        return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR);\n    }\n\n    node = ngx_http_v2_get_node_by_id(h2c, h2c->state.sid, 1);\n\n    if (node == NULL) {\n        return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR);\n    }\n\n    node->weight = weight;\n\n    if (node->stream == NULL) {\n        if (node->parent == NULL) {\n            h2c->closed_nodes++;\n\n        } else {\n            ngx_queue_remove(&node->reuse);\n        }\n\n        ngx_queue_insert_tail(&h2c->closed, &node->reuse);\n    }\n\n    ngx_http_v2_set_dependency(h2c, node, depend, excl);\n\n    return ngx_http_v2_state_complete(h2c, pos, end);\n}\n\n\nstatic u_char *\nngx_http_v2_state_rst_stream(ngx_http_v2_connection_t *h2c, u_char *pos,\n    u_char *end)\n{\n    ngx_uint_t             status;\n    ngx_event_t           *ev;\n    ngx_connection_t      *fc;\n    ngx_http_v2_node_t    *node;\n    ngx_http_v2_stream_t  *stream;\n\n    if (h2c->state.length != NGX_HTTP_V2_RST_STREAM_SIZE) {\n        ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                      \"client sent RST_STREAM frame with incorrect length %uz\",\n                      h2c->state.length);\n\n        return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR);\n    }\n\n    if (end - pos < NGX_HTTP_V2_RST_STREAM_SIZE) {\n        return ngx_http_v2_state_save(h2c, pos, end,\n                                      ngx_http_v2_state_rst_stream);\n    }\n\n    status = ngx_http_v2_parse_uint32(pos);\n\n    pos += NGX_HTTP_V2_RST_STREAM_SIZE;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n                   \"http2 RST_STREAM frame, sid:%ui status:%ui\",\n                   h2c->state.sid, status);\n\n    if (h2c->state.sid == 0) {\n        ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                      \"client sent RST_STREAM frame with incorrect identifier\");\n\n        return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR);\n    }\n\n    node = ngx_http_v2_get_node_by_id(h2c, h2c->state.sid, 0);\n\n    if (node == NULL || node->stream == NULL) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n                       \"unknown http2 stream\");\n\n        return ngx_http_v2_state_complete(h2c, pos, end);\n    }\n\n    stream = node->stream;\n\n    stream->in_closed = 1;\n    stream->out_closed = 1;\n\n    fc = stream->request->connection;\n    fc->error = 1;\n\n    switch (status) {\n\n    case NGX_HTTP_V2_CANCEL:\n        ngx_log_error(NGX_LOG_INFO, fc->log, 0,\n                      \"client canceled stream %ui\", h2c->state.sid);\n        break;\n\n    case NGX_HTTP_V2_REFUSED_STREAM:\n        ngx_log_error(NGX_LOG_INFO, fc->log, 0,\n                      \"client refused stream %ui\", h2c->state.sid);\n        break;\n\n    case NGX_HTTP_V2_INTERNAL_ERROR:\n        ngx_log_error(NGX_LOG_INFO, fc->log, 0,\n                      \"client terminated stream %ui due to internal error\",\n                      h2c->state.sid);\n        break;\n\n    default:\n        ngx_log_error(NGX_LOG_INFO, fc->log, 0,\n                      \"client terminated stream %ui with status %ui\",\n                      h2c->state.sid, status);\n        break;\n    }\n\n    ev = fc->read;\n    ev->handler(ev);\n\n    return ngx_http_v2_state_complete(h2c, pos, end);\n}\n\n\nstatic u_char *\nngx_http_v2_state_settings(ngx_http_v2_connection_t *h2c, u_char *pos,\n    u_char *end)\n{\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n                   \"http2 SETTINGS frame\");\n\n    if (h2c->state.sid) {\n        ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                      \"client sent SETTINGS frame with incorrect identifier\");\n\n        return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR);\n    }\n\n    if (h2c->state.flags == NGX_HTTP_V2_ACK_FLAG) {\n\n        if (h2c->state.length != 0) {\n            ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                          \"client sent SETTINGS frame with the ACK flag \"\n                          \"and nonzero length\");\n\n            return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR);\n        }\n\n        h2c->settings_ack = 1;\n\n        return ngx_http_v2_state_complete(h2c, pos, end);\n    }\n\n    if (h2c->state.length % NGX_HTTP_V2_SETTINGS_PARAM_SIZE) {\n        ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                      \"client sent SETTINGS frame with incorrect length %uz\",\n                      h2c->state.length);\n\n        return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR);\n    }\n\n    return ngx_http_v2_state_settings_params(h2c, pos, end);\n}\n\n\nstatic u_char *\nngx_http_v2_state_settings_params(ngx_http_v2_connection_t *h2c, u_char *pos,\n    u_char *end)\n{\n    ssize_t                   window_delta;\n    ngx_uint_t                id, value;\n    ngx_http_v2_srv_conf_t   *h2scf;\n    ngx_http_v2_out_frame_t  *frame;\n\n    window_delta = 0;\n\n    while (h2c->state.length) {\n        if (end - pos < NGX_HTTP_V2_SETTINGS_PARAM_SIZE) {\n            return ngx_http_v2_state_save(h2c, pos, end,\n                                          ngx_http_v2_state_settings_params);\n        }\n\n        h2c->state.length -= NGX_HTTP_V2_SETTINGS_PARAM_SIZE;\n\n        id = ngx_http_v2_parse_uint16(pos);\n        value = ngx_http_v2_parse_uint32(&pos[2]);\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n                       \"http2 setting %ui:%ui\", id, value);\n\n        switch (id) {\n\n        case NGX_HTTP_V2_INIT_WINDOW_SIZE_SETTING:\n\n            if (value > NGX_HTTP_V2_MAX_WINDOW) {\n                ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                              \"client sent SETTINGS frame with incorrect \"\n                              \"INITIAL_WINDOW_SIZE value %ui\", value);\n\n                return ngx_http_v2_connection_error(h2c,\n                                                  NGX_HTTP_V2_FLOW_CTRL_ERROR);\n            }\n\n            window_delta = value - h2c->init_window;\n            break;\n\n        case NGX_HTTP_V2_MAX_FRAME_SIZE_SETTING:\n\n            if (value > NGX_HTTP_V2_MAX_FRAME_SIZE\n                || value < NGX_HTTP_V2_DEFAULT_FRAME_SIZE)\n            {\n                ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                              \"client sent SETTINGS frame with incorrect \"\n                              \"MAX_FRAME_SIZE value %ui\", value);\n\n                return ngx_http_v2_connection_error(h2c,\n                                                    NGX_HTTP_V2_PROTOCOL_ERROR);\n            }\n\n            h2c->frame_size = value;\n            break;\n\n        case NGX_HTTP_V2_ENABLE_PUSH_SETTING:\n\n            if (value > 1) {\n                ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                              \"client sent SETTINGS frame with incorrect \"\n                              \"ENABLE_PUSH value %ui\", value);\n\n                return ngx_http_v2_connection_error(h2c,\n                                                    NGX_HTTP_V2_PROTOCOL_ERROR);\n            }\n\n            h2c->push_disabled = !value;\n            break;\n\n        case NGX_HTTP_V2_MAX_STREAMS_SETTING:\n            h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx,\n                                                 ngx_http_v2_module);\n\n            h2c->concurrent_pushes = ngx_min(value, h2scf->concurrent_pushes);\n            break;\n\n        case NGX_HTTP_V2_HEADER_TABLE_SIZE_SETTING:\n\n            h2c->table_update = 1;\n            break;\n\n        default:\n            break;\n        }\n\n        pos += NGX_HTTP_V2_SETTINGS_PARAM_SIZE;\n    }\n\n    frame = ngx_http_v2_get_frame(h2c, NGX_HTTP_V2_SETTINGS_ACK_SIZE,\n                                  NGX_HTTP_V2_SETTINGS_FRAME,\n                                  NGX_HTTP_V2_ACK_FLAG, 0);\n    if (frame == NULL) {\n        return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR);\n    }\n\n    ngx_http_v2_queue_ordered_frame(h2c, frame);\n\n    if (window_delta) {\n        h2c->init_window += window_delta;\n\n        if (ngx_http_v2_adjust_windows(h2c, window_delta) != NGX_OK) {\n            return ngx_http_v2_connection_error(h2c,\n                                                NGX_HTTP_V2_INTERNAL_ERROR);\n        }\n    }\n\n    return ngx_http_v2_state_complete(h2c, pos, end);\n}\n\n\nstatic u_char *\nngx_http_v2_state_push_promise(ngx_http_v2_connection_t *h2c, u_char *pos,\n    u_char *end)\n{\n    ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                  \"client sent PUSH_PROMISE frame\");\n\n    return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR);\n}\n\n\nstatic u_char *\nngx_http_v2_state_ping(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end)\n{\n    ngx_buf_t                *buf;\n    ngx_http_v2_out_frame_t  *frame;\n\n    if (h2c->state.length != NGX_HTTP_V2_PING_SIZE) {\n        ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                      \"client sent PING frame with incorrect length %uz\",\n                      h2c->state.length);\n\n        return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR);\n    }\n\n    if (end - pos < NGX_HTTP_V2_PING_SIZE) {\n        return ngx_http_v2_state_save(h2c, pos, end, ngx_http_v2_state_ping);\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n                   \"http2 PING frame\");\n\n    if (h2c->state.sid) {\n        ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                      \"client sent PING frame with incorrect identifier\");\n\n        return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR);\n    }\n\n    if (h2c->state.flags & NGX_HTTP_V2_ACK_FLAG) {\n        return ngx_http_v2_state_skip(h2c, pos, end);\n    }\n\n    frame = ngx_http_v2_get_frame(h2c, NGX_HTTP_V2_PING_SIZE,\n                                  NGX_HTTP_V2_PING_FRAME,\n                                  NGX_HTTP_V2_ACK_FLAG, 0);\n    if (frame == NULL) {\n        return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR);\n    }\n\n    buf = frame->first->buf;\n\n    buf->last = ngx_cpymem(buf->last, pos, NGX_HTTP_V2_PING_SIZE);\n\n    ngx_http_v2_queue_blocked_frame(h2c, frame);\n\n    return ngx_http_v2_state_complete(h2c, pos + NGX_HTTP_V2_PING_SIZE, end);\n}\n\n\nstatic u_char *\nngx_http_v2_state_goaway(ngx_http_v2_connection_t *h2c, u_char *pos,\n    u_char *end)\n{\n#if (NGX_DEBUG)\n    ngx_uint_t  last_sid, error;\n#endif\n\n    if (h2c->state.length < NGX_HTTP_V2_GOAWAY_SIZE) {\n        ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                      \"client sent GOAWAY frame \"\n                      \"with incorrect length %uz\", h2c->state.length);\n\n        return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR);\n    }\n\n    if (end - pos < NGX_HTTP_V2_GOAWAY_SIZE) {\n        return ngx_http_v2_state_save(h2c, pos, end, ngx_http_v2_state_goaway);\n    }\n\n    if (h2c->state.sid) {\n        ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                      \"client sent GOAWAY frame with incorrect identifier\");\n\n        return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR);\n    }\n\n#if (NGX_DEBUG)\n    h2c->state.length -= NGX_HTTP_V2_GOAWAY_SIZE;\n\n    last_sid = ngx_http_v2_parse_sid(pos);\n    error = ngx_http_v2_parse_uint32(&pos[4]);\n\n    pos += NGX_HTTP_V2_GOAWAY_SIZE;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n                   \"http2 GOAWAY frame: last sid %ui, error %ui\",\n                   last_sid, error);\n#endif\n\n    return ngx_http_v2_state_skip(h2c, pos, end);\n}\n\n\nstatic u_char *\nngx_http_v2_state_window_update(ngx_http_v2_connection_t *h2c, u_char *pos,\n    u_char *end)\n{\n    size_t                 window;\n    ngx_event_t           *wev;\n    ngx_queue_t           *q;\n    ngx_http_v2_node_t    *node;\n    ngx_http_v2_stream_t  *stream;\n\n    if (h2c->state.length != NGX_HTTP_V2_WINDOW_UPDATE_SIZE) {\n        ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                      \"client sent WINDOW_UPDATE frame \"\n                      \"with incorrect length %uz\", h2c->state.length);\n\n        return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_SIZE_ERROR);\n    }\n\n    if (end - pos < NGX_HTTP_V2_WINDOW_UPDATE_SIZE) {\n        return ngx_http_v2_state_save(h2c, pos, end,\n                                      ngx_http_v2_state_window_update);\n    }\n\n    window = ngx_http_v2_parse_window(pos);\n\n    pos += NGX_HTTP_V2_WINDOW_UPDATE_SIZE;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n                   \"http2 WINDOW_UPDATE frame sid:%ui window:%uz\",\n                   h2c->state.sid, window);\n\n    if (window == 0) {\n        ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                      \"client sent WINDOW_UPDATE frame \"\n                      \"with incorrect window increment 0\");\n\n        return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR);\n    }\n\n    if (h2c->state.sid) {\n        node = ngx_http_v2_get_node_by_id(h2c, h2c->state.sid, 0);\n\n        if (node == NULL || node->stream == NULL) {\n            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n                           \"unknown http2 stream\");\n\n            return ngx_http_v2_state_complete(h2c, pos, end);\n        }\n\n        stream = node->stream;\n\n        if (window > (size_t) (NGX_HTTP_V2_MAX_WINDOW - stream->send_window)) {\n\n            ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                          \"client violated flow control for stream %ui: \"\n                          \"received WINDOW_UPDATE frame \"\n                          \"with window increment %uz \"\n                          \"not allowed for window %z\",\n                          h2c->state.sid, window, stream->send_window);\n\n            if (ngx_http_v2_terminate_stream(h2c, stream,\n                                             NGX_HTTP_V2_FLOW_CTRL_ERROR)\n                == NGX_ERROR)\n            {\n                return ngx_http_v2_connection_error(h2c,\n                                                    NGX_HTTP_V2_INTERNAL_ERROR);\n            }\n\n            return ngx_http_v2_state_complete(h2c, pos, end);\n        }\n\n        stream->send_window += window;\n\n        if (stream->exhausted) {\n            stream->exhausted = 0;\n\n            wev = stream->request->connection->write;\n\n            wev->active = 0;\n            wev->ready = 1;\n\n            if (!wev->delayed) {\n                wev->handler(wev);\n            }\n        }\n\n        return ngx_http_v2_state_complete(h2c, pos, end);\n    }\n\n    if (window > NGX_HTTP_V2_MAX_WINDOW - h2c->send_window) {\n        ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                      \"client violated connection flow control: \"\n                      \"received WINDOW_UPDATE frame \"\n                      \"with window increment %uz \"\n                      \"not allowed for window %uz\",\n                      window, h2c->send_window);\n\n        return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_FLOW_CTRL_ERROR);\n    }\n\n    h2c->send_window += window;\n\n    while (!ngx_queue_empty(&h2c->waiting)) {\n        q = ngx_queue_head(&h2c->waiting);\n\n        ngx_queue_remove(q);\n\n        stream = ngx_queue_data(q, ngx_http_v2_stream_t, queue);\n\n        stream->waiting = 0;\n\n        wev = stream->request->connection->write;\n\n        wev->active = 0;\n        wev->ready = 1;\n\n        if (!wev->delayed) {\n            wev->handler(wev);\n\n            if (h2c->send_window == 0) {\n                break;\n            }\n        }\n    }\n\n    return ngx_http_v2_state_complete(h2c, pos, end);\n}\n\n\nstatic u_char *\nngx_http_v2_state_continuation(ngx_http_v2_connection_t *h2c, u_char *pos,\n    u_char *end)\n{\n    ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                  \"client sent unexpected CONTINUATION frame\");\n\n    return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_PROTOCOL_ERROR);\n}\n\n\nstatic u_char *\nngx_http_v2_state_complete(ngx_http_v2_connection_t *h2c, u_char *pos,\n    u_char *end)\n{\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n                   \"http2 frame complete pos:%p end:%p\", pos, end);\n\n    if (pos > end) {\n        ngx_log_error(NGX_LOG_ALERT, h2c->connection->log, 0,\n                      \"receive buffer overrun\");\n\n        return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR);\n    }\n\n    h2c->state.stream = NULL;\n    h2c->state.handler = ngx_http_v2_state_head;\n\n    return pos;\n}\n\n\nstatic u_char *\nngx_http_v2_state_skip_padded(ngx_http_v2_connection_t *h2c, u_char *pos,\n    u_char *end)\n{\n    h2c->state.length += h2c->state.padding;\n    h2c->state.padding = 0;\n\n    return ngx_http_v2_state_skip(h2c, pos, end);\n}\n\n\nstatic u_char *\nngx_http_v2_state_skip(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end)\n{\n    size_t  size;\n\n    size = end - pos;\n\n    if (size < h2c->state.length) {\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n                       \"http2 frame skip %uz of %uz\", size, h2c->state.length);\n\n        h2c->state.length -= size;\n        return ngx_http_v2_state_save(h2c, end, end, ngx_http_v2_state_skip);\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n                   \"http2 frame skip %uz\", h2c->state.length);\n\n    return ngx_http_v2_state_complete(h2c, pos + h2c->state.length, end);\n}\n\n\nstatic u_char *\nngx_http_v2_state_save(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end,\n    ngx_http_v2_handler_pt handler)\n{\n    size_t  size;\n\n    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n                   \"http2 frame state save pos:%p end:%p handler:%p\",\n                   pos, end, handler);\n\n    size = end - pos;\n\n    if (size > NGX_HTTP_V2_STATE_BUFFER_SIZE) {\n        ngx_log_error(NGX_LOG_ALERT, h2c->connection->log, 0,\n                      \"state buffer overflow: %uz bytes required\", size);\n\n        return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR);\n    }\n\n    ngx_memcpy(h2c->state.buffer, pos, NGX_HTTP_V2_STATE_BUFFER_SIZE);\n\n    h2c->state.buffer_used = size;\n    h2c->state.handler = handler;\n    h2c->state.incomplete = 1;\n\n    return end;\n}\n\n\nstatic u_char *\nngx_http_v2_state_headers_save(ngx_http_v2_connection_t *h2c, u_char *pos,\n    u_char *end, ngx_http_v2_handler_pt handler)\n{\n    ngx_event_t               *rev;\n    ngx_http_request_t        *r;\n    ngx_http_core_srv_conf_t  *cscf;\n\n    if (h2c->state.stream) {\n        r = h2c->state.stream->request;\n        rev = r->connection->read;\n\n        if (!rev->timer_set) {\n            cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);\n            ngx_add_timer(rev, cscf->client_header_timeout);\n        }\n    }\n\n    return ngx_http_v2_state_save(h2c, pos, end, handler);\n}\n\n\nstatic u_char *\nngx_http_v2_connection_error(ngx_http_v2_connection_t *h2c,\n    ngx_uint_t err)\n{\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n                   \"http2 state connection error\");\n\n    ngx_http_v2_finalize_connection(h2c, err);\n\n    return NULL;\n}\n\n\nstatic ngx_int_t\nngx_http_v2_parse_int(ngx_http_v2_connection_t *h2c, u_char **pos, u_char *end,\n    ngx_uint_t prefix)\n{\n    u_char      *start, *p;\n    ngx_uint_t   value, octet, shift;\n\n    start = *pos;\n    p = start;\n\n    value = *p++ & prefix;\n\n    if (value != prefix) {\n        if (h2c->state.length == 0) {\n            return NGX_ERROR;\n        }\n\n        h2c->state.length--;\n\n        *pos = p;\n        return value;\n    }\n\n    if (end - start > NGX_HTTP_V2_INT_OCTETS) {\n        end = start + NGX_HTTP_V2_INT_OCTETS;\n    }\n\n    for (shift = 0; p != end; shift += 7) {\n        octet = *p++;\n\n        value += (octet & 0x7f) << shift;\n\n        if (octet < 128) {\n            if ((size_t) (p - start) > h2c->state.length) {\n                return NGX_ERROR;\n            }\n\n            h2c->state.length -= p - start;\n\n            *pos = p;\n            return value;\n        }\n    }\n\n    if ((size_t) (end - start) >= h2c->state.length) {\n        return NGX_ERROR;\n    }\n\n    if (end == start + NGX_HTTP_V2_INT_OCTETS) {\n        return NGX_DECLINED;\n    }\n\n    return NGX_AGAIN;\n}\n\n\nngx_http_v2_stream_t *\nngx_http_v2_push_stream(ngx_http_v2_stream_t *parent, ngx_str_t *path)\n{\n    ngx_int_t                     rc;\n    ngx_str_t                     value;\n    ngx_pool_t                   *pool;\n    ngx_uint_t                    index;\n    ngx_table_elt_t             **h;\n    ngx_connection_t             *fc;\n    ngx_http_request_t           *r;\n    ngx_http_v2_node_t           *node;\n    ngx_http_v2_stream_t         *stream;\n    ngx_http_v2_srv_conf_t       *h2scf;\n    ngx_http_v2_connection_t     *h2c;\n    ngx_http_v2_parse_header_t   *header;\n\n    h2c = parent->connection;\n\n    pool = ngx_create_pool(1024, h2c->connection->log);\n    if (pool == NULL) {\n        goto rst_stream;\n    }\n\n    node = ngx_http_v2_get_node_by_id(h2c, h2c->last_push, 1);\n\n    if (node == NULL) {\n        ngx_destroy_pool(pool);\n        goto rst_stream;\n    }\n\n    stream = ngx_http_v2_create_stream(h2c, 1);\n    if (stream == NULL) {\n\n        if (node->parent == NULL) {\n            h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx,\n                                                 ngx_http_v2_module);\n\n            index = ngx_http_v2_index(h2scf, h2c->last_push);\n            h2c->streams_index[index] = node->index;\n\n            ngx_queue_insert_tail(&h2c->closed, &node->reuse);\n            h2c->closed_nodes++;\n        }\n\n        ngx_destroy_pool(pool);\n        goto rst_stream;\n    }\n\n    if (node->parent) {\n        ngx_queue_remove(&node->reuse);\n        h2c->closed_nodes--;\n    }\n\n    stream->pool = pool;\n\n    r = stream->request;\n    fc = r->connection;\n\n    stream->in_closed = 1;\n    stream->node = node;\n\n    node->stream = stream;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n                   \"http2 push stream sid:%ui \"\n                   \"depends on %ui excl:0 weight:16\",\n                   h2c->last_push, parent->node->id);\n\n    node->weight = NGX_HTTP_V2_DEFAULT_WEIGHT;\n    ngx_http_v2_set_dependency(h2c, node, parent->node->id, 0);\n\n    r->method_name = ngx_http_core_get_method;\n    r->method = NGX_HTTP_GET;\n\n    r->schema.data = ngx_pstrdup(pool, &parent->request->schema);\n    if (r->schema.data == NULL) {\n        goto close;\n    }\n\n    r->schema.len = parent->request->schema.len;\n\n    value.data = ngx_pstrdup(pool, path);\n    if (value.data == NULL) {\n        goto close;\n    }\n\n    value.len = path->len;\n\n    rc = ngx_http_v2_parse_path(r, &value);\n\n    if (rc != NGX_OK) {\n        goto error;\n    }\n\n    for (header = ngx_http_v2_parse_headers; header->name.len; header++) {\n        h = (ngx_table_elt_t **)\n                ((char *) &parent->request->headers_in + header->offset);\n\n        if (*h == NULL) {\n            continue;\n        }\n\n        value.len = (*h)->value.len;\n\n        value.data = ngx_pnalloc(pool, value.len + 1);\n        if (value.data == NULL) {\n            goto close;\n        }\n\n        ngx_memcpy(value.data, (*h)->value.data, value.len);\n        value.data[value.len] = '\\0';\n\n        rc = ngx_http_v2_parse_header(r, header, &value);\n\n        if (rc != NGX_OK) {\n            goto error;\n        }\n    }\n\n    fc->write->handler = ngx_http_v2_run_request_handler;\n    ngx_post_event(fc->write, &ngx_posted_events);\n\n    return stream;\n\nerror:\n\n    if (rc == NGX_ABORT) {\n        /* header handler has already finalized request */\n        ngx_http_run_posted_requests(fc);\n        return NULL;\n    }\n\n    if (rc == NGX_DECLINED) {\n        ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);\n        ngx_http_run_posted_requests(fc);\n        return NULL;\n    }\n\nclose:\n\n    ngx_http_v2_close_stream(stream, NGX_HTTP_INTERNAL_SERVER_ERROR);\n\n    return NULL;\n\nrst_stream:\n\n    if (ngx_http_v2_send_rst_stream(h2c, h2c->last_push,\n                                    NGX_HTTP_INTERNAL_SERVER_ERROR)\n        != NGX_OK)\n    {\n        h2c->connection->error = 1;\n    }\n\n    return NULL;\n}\n\n\nstatic ngx_int_t\nngx_http_v2_send_settings(ngx_http_v2_connection_t *h2c)\n{\n    size_t                    len;\n    ngx_buf_t                *buf;\n    ngx_chain_t              *cl;\n    ngx_http_v2_srv_conf_t   *h2scf;\n    ngx_http_v2_out_frame_t  *frame;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n                   \"http2 send SETTINGS frame\");\n\n    frame = ngx_palloc(h2c->pool, sizeof(ngx_http_v2_out_frame_t));\n    if (frame == NULL) {\n        return NGX_ERROR;\n    }\n\n    cl = ngx_alloc_chain_link(h2c->pool);\n    if (cl == NULL) {\n        return NGX_ERROR;\n    }\n\n    len = NGX_HTTP_V2_SETTINGS_PARAM_SIZE * 3;\n\n    buf = ngx_create_temp_buf(h2c->pool, NGX_HTTP_V2_FRAME_HEADER_SIZE + len);\n    if (buf == NULL) {\n        return NGX_ERROR;\n    }\n\n    buf->last_buf = 1;\n\n    cl->buf = buf;\n    cl->next = NULL;\n\n    frame->first = cl;\n    frame->last = cl;\n    frame->handler = ngx_http_v2_settings_frame_handler;\n    frame->stream = NULL;\n#if (NGX_DEBUG)\n    frame->length = len;\n#endif\n    frame->blocked = 0;\n\n    buf->last = ngx_http_v2_write_len_and_type(buf->last, len,\n                                               NGX_HTTP_V2_SETTINGS_FRAME);\n\n    *buf->last++ = NGX_HTTP_V2_NO_FLAG;\n\n    buf->last = ngx_http_v2_write_sid(buf->last, 0);\n\n    h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx,\n                                         ngx_http_v2_module);\n\n    buf->last = ngx_http_v2_write_uint16(buf->last,\n                                         NGX_HTTP_V2_MAX_STREAMS_SETTING);\n    buf->last = ngx_http_v2_write_uint32(buf->last,\n                                         h2scf->concurrent_streams);\n\n    buf->last = ngx_http_v2_write_uint16(buf->last,\n                                         NGX_HTTP_V2_INIT_WINDOW_SIZE_SETTING);\n    buf->last = ngx_http_v2_write_uint32(buf->last, h2scf->preread_size);\n\n    buf->last = ngx_http_v2_write_uint16(buf->last,\n                                         NGX_HTTP_V2_MAX_FRAME_SIZE_SETTING);\n    buf->last = ngx_http_v2_write_uint32(buf->last,\n                                         NGX_HTTP_V2_MAX_FRAME_SIZE);\n\n    ngx_http_v2_queue_blocked_frame(h2c, frame);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_v2_settings_frame_handler(ngx_http_v2_connection_t *h2c,\n    ngx_http_v2_out_frame_t *frame)\n{\n    ngx_buf_t  *buf;\n\n    buf = frame->first->buf;\n\n    if (buf->pos != buf->last) {\n        return NGX_AGAIN;\n    }\n\n    ngx_free_chain(h2c->pool, frame->first);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_v2_send_window_update(ngx_http_v2_connection_t *h2c, ngx_uint_t sid,\n    size_t window)\n{\n    ngx_buf_t                *buf;\n    ngx_http_v2_out_frame_t  *frame;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n                   \"http2 send WINDOW_UPDATE frame sid:%ui, window:%uz\",\n                   sid, window);\n\n    frame = ngx_http_v2_get_frame(h2c, NGX_HTTP_V2_WINDOW_UPDATE_SIZE,\n                                  NGX_HTTP_V2_WINDOW_UPDATE_FRAME,\n                                  NGX_HTTP_V2_NO_FLAG, sid);\n    if (frame == NULL) {\n        return NGX_ERROR;\n    }\n\n    buf = frame->first->buf;\n\n    buf->last = ngx_http_v2_write_uint32(buf->last, window);\n\n    ngx_http_v2_queue_blocked_frame(h2c, frame);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_v2_send_rst_stream(ngx_http_v2_connection_t *h2c, ngx_uint_t sid,\n    ngx_uint_t status)\n{\n    ngx_buf_t                *buf;\n    ngx_http_v2_out_frame_t  *frame;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n                   \"http2 send RST_STREAM frame sid:%ui, status:%ui\",\n                   sid, status);\n\n    frame = ngx_http_v2_get_frame(h2c, NGX_HTTP_V2_RST_STREAM_SIZE,\n                                  NGX_HTTP_V2_RST_STREAM_FRAME,\n                                  NGX_HTTP_V2_NO_FLAG, sid);\n    if (frame == NULL) {\n        return NGX_ERROR;\n    }\n\n    buf = frame->first->buf;\n\n    buf->last = ngx_http_v2_write_uint32(buf->last, status);\n\n    ngx_http_v2_queue_blocked_frame(h2c, frame);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_v2_send_goaway(ngx_http_v2_connection_t *h2c, ngx_uint_t status)\n{\n    ngx_buf_t                *buf;\n    ngx_http_v2_out_frame_t  *frame;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n                   \"http2 send GOAWAY frame: last sid %ui, error %ui\",\n                   h2c->last_sid, status);\n\n    frame = ngx_http_v2_get_frame(h2c, NGX_HTTP_V2_GOAWAY_SIZE,\n                                  NGX_HTTP_V2_GOAWAY_FRAME,\n                                  NGX_HTTP_V2_NO_FLAG, 0);\n    if (frame == NULL) {\n        return NGX_ERROR;\n    }\n\n    buf = frame->first->buf;\n\n    buf->last = ngx_http_v2_write_sid(buf->last, h2c->last_sid);\n    buf->last = ngx_http_v2_write_uint32(buf->last, status);\n\n    ngx_http_v2_queue_blocked_frame(h2c, frame);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_http_v2_out_frame_t *\nngx_http_v2_get_frame(ngx_http_v2_connection_t *h2c, size_t length,\n    ngx_uint_t type, u_char flags, ngx_uint_t sid)\n{\n    ngx_buf_t                *buf;\n    ngx_pool_t               *pool;\n    ngx_http_v2_out_frame_t  *frame;\n\n    frame = h2c->free_frames;\n\n    if (frame) {\n        h2c->free_frames = frame->next;\n\n        buf = frame->first->buf;\n        buf->pos = buf->start;\n\n        frame->blocked = 0;\n\n    } else if (h2c->frames < 10000) {\n        pool = h2c->pool ? h2c->pool : h2c->connection->pool;\n\n        frame = ngx_pcalloc(pool, sizeof(ngx_http_v2_out_frame_t));\n        if (frame == NULL) {\n            return NULL;\n        }\n\n        frame->first = ngx_alloc_chain_link(pool);\n        if (frame->first == NULL) {\n            return NULL;\n        }\n\n        buf = ngx_create_temp_buf(pool, NGX_HTTP_V2_FRAME_BUFFER_SIZE);\n        if (buf == NULL) {\n            return NULL;\n        }\n\n        buf->last_buf = 1;\n\n        frame->first->buf = buf;\n        frame->last = frame->first;\n\n        frame->handler = ngx_http_v2_frame_handler;\n\n        h2c->frames++;\n\n    } else {\n        ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                      \"http2 flood detected\");\n\n        h2c->connection->error = 1;\n        return NULL;\n    }\n\n#if (NGX_DEBUG)\n    if (length > NGX_HTTP_V2_FRAME_BUFFER_SIZE - NGX_HTTP_V2_FRAME_HEADER_SIZE)\n    {\n        ngx_log_error(NGX_LOG_ALERT, h2c->connection->log, 0,\n                      \"requested control frame is too large: %uz\", length);\n        return NULL;\n    }\n#endif\n\n    frame->length = length;\n\n    buf->last = ngx_http_v2_write_len_and_type(buf->pos, length, type);\n\n    *buf->last++ = flags;\n\n    buf->last = ngx_http_v2_write_sid(buf->last, sid);\n\n    return frame;\n}\n\n\nstatic ngx_int_t\nngx_http_v2_frame_handler(ngx_http_v2_connection_t *h2c,\n    ngx_http_v2_out_frame_t *frame)\n{\n    ngx_buf_t  *buf;\n\n    buf = frame->first->buf;\n\n    if (buf->pos != buf->last) {\n        return NGX_AGAIN;\n    }\n\n    frame->next = h2c->free_frames;\n    h2c->free_frames = frame;\n\n    h2c->total_bytes += NGX_HTTP_V2_FRAME_HEADER_SIZE + frame->length;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_http_v2_stream_t *\nngx_http_v2_create_stream(ngx_http_v2_connection_t *h2c, ngx_uint_t push)\n{\n    ngx_log_t                 *log;\n    ngx_event_t               *rev, *wev;\n    ngx_connection_t          *fc;\n    ngx_http_log_ctx_t        *ctx;\n    ngx_http_request_t        *r;\n    ngx_http_v2_stream_t      *stream;\n    ngx_http_v2_srv_conf_t    *h2scf;\n    ngx_http_core_srv_conf_t  *cscf;\n\n    fc = h2c->free_fake_connections;\n\n    if (fc) {\n        h2c->free_fake_connections = fc->data;\n\n        rev = fc->read;\n        wev = fc->write;\n        log = fc->log;\n        ctx = log->data;\n\n    } else {\n        fc = ngx_palloc(h2c->pool, sizeof(ngx_connection_t));\n        if (fc == NULL) {\n            return NULL;\n        }\n\n        rev = ngx_palloc(h2c->pool, sizeof(ngx_event_t));\n        if (rev == NULL) {\n            return NULL;\n        }\n\n        wev = ngx_palloc(h2c->pool, sizeof(ngx_event_t));\n        if (wev == NULL) {\n            return NULL;\n        }\n\n        log = ngx_palloc(h2c->pool, sizeof(ngx_log_t));\n        if (log == NULL) {\n            return NULL;\n        }\n\n        ctx = ngx_palloc(h2c->pool, sizeof(ngx_http_log_ctx_t));\n        if (ctx == NULL) {\n            return NULL;\n        }\n\n        ctx->connection = fc;\n        ctx->request = NULL;\n        ctx->current_request = NULL;\n    }\n\n    ngx_memcpy(log, h2c->connection->log, sizeof(ngx_log_t));\n\n    log->data = ctx;\n\n    if (push) {\n        log->action = \"processing pushed request headers\";\n\n    } else {\n        log->action = \"reading client request headers\";\n    }\n\n    ngx_memzero(rev, sizeof(ngx_event_t));\n\n    rev->data = fc;\n    rev->ready = 1;\n    rev->handler = ngx_http_v2_close_stream_handler;\n    rev->log = log;\n\n    ngx_memcpy(wev, rev, sizeof(ngx_event_t));\n\n    wev->write = 1;\n\n    ngx_memcpy(fc, h2c->connection, sizeof(ngx_connection_t));\n\n    fc->data = h2c->http_connection;\n    fc->read = rev;\n    fc->write = wev;\n    fc->sent = 0;\n    fc->log = log;\n    fc->buffered = 0;\n    fc->sndlowat = 1;\n    fc->tcp_nodelay = NGX_TCP_NODELAY_DISABLED;\n\n    r = ngx_http_create_request(fc);\n    if (r == NULL) {\n        return NULL;\n    }\n\n    ngx_str_set(&r->http_protocol, \"HTTP/2.0\");\n\n    r->http_version = NGX_HTTP_VERSION_20;\n    r->valid_location = 1;\n\n    fc->data = r;\n    h2c->connection->requests++;\n\n    cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);\n\n    r->header_in = ngx_create_temp_buf(r->pool,\n                                       cscf->client_header_buffer_size);\n    if (r->header_in == NULL) {\n        ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return NULL;\n    }\n\n    if (ngx_list_init(&r->headers_in.headers, r->pool, 20,\n                      sizeof(ngx_table_elt_t))\n        != NGX_OK)\n    {\n        ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return NULL;\n    }\n\n    r->headers_in.connection_type = NGX_HTTP_CONNECTION_CLOSE;\n\n    stream = ngx_pcalloc(r->pool, sizeof(ngx_http_v2_stream_t));\n    if (stream == NULL) {\n        ngx_http_free_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return NULL;\n    }\n\n    r->stream = stream;\n\n    stream->request = r;\n    stream->connection = h2c;\n\n    h2scf = ngx_http_get_module_srv_conf(r, ngx_http_v2_module);\n\n    stream->send_window = h2c->init_window;\n    stream->recv_window = h2scf->preread_size;\n\n    if (push) {\n        h2c->pushing++;\n\n    } else {\n        h2c->processing++;\n    }\n\n    h2c->priority_limit += h2scf->concurrent_streams;\n\n    if (h2c->connection->read->timer_set) {\n        ngx_del_timer(h2c->connection->read);\n    }\n\n    return stream;\n}\n\n\nstatic ngx_http_v2_node_t *\nngx_http_v2_get_node_by_id(ngx_http_v2_connection_t *h2c, ngx_uint_t sid,\n    ngx_uint_t alloc)\n{\n    ngx_uint_t               index;\n    ngx_http_v2_node_t      *node;\n    ngx_http_v2_srv_conf_t  *h2scf;\n\n    h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx,\n                                         ngx_http_v2_module);\n\n    index = ngx_http_v2_index(h2scf, sid);\n\n    for (node = h2c->streams_index[index]; node; node = node->index) {\n\n        if (node->id == sid) {\n            return node;\n        }\n    }\n\n    if (!alloc) {\n        return NULL;\n    }\n\n    if (h2c->closed_nodes < 32) {\n        node = ngx_pcalloc(h2c->connection->pool, sizeof(ngx_http_v2_node_t));\n        if (node == NULL) {\n            return NULL;\n        }\n\n    } else {\n        node = ngx_http_v2_get_closed_node(h2c);\n    }\n\n    node->id = sid;\n\n    ngx_queue_init(&node->children);\n\n    node->index = h2c->streams_index[index];\n    h2c->streams_index[index] = node;\n\n    return node;\n}\n\n\nstatic ngx_http_v2_node_t *\nngx_http_v2_get_closed_node(ngx_http_v2_connection_t *h2c)\n{\n    ngx_uint_t               weight;\n    ngx_queue_t             *q, *children;\n    ngx_http_v2_node_t      *node, **next, *n, *parent, *child;\n    ngx_http_v2_srv_conf_t  *h2scf;\n\n    h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx,\n                                         ngx_http_v2_module);\n\n    h2c->closed_nodes--;\n\n    q = ngx_queue_head(&h2c->closed);\n\n    ngx_queue_remove(q);\n\n    node = ngx_queue_data(q, ngx_http_v2_node_t, reuse);\n\n    next = &h2c->streams_index[ngx_http_v2_index(h2scf, node->id)];\n\n    for ( ;; ) {\n        n = *next;\n\n        if (n == node) {\n            *next = n->index;\n            break;\n        }\n\n        next = &n->index;\n    }\n\n    ngx_queue_remove(&node->queue);\n\n    weight = 0;\n\n    for (q = ngx_queue_head(&node->children);\n         q != ngx_queue_sentinel(&node->children);\n         q = ngx_queue_next(q))\n    {\n        child = ngx_queue_data(q, ngx_http_v2_node_t, queue);\n        weight += child->weight;\n    }\n\n    parent = node->parent;\n\n    for (q = ngx_queue_head(&node->children);\n         q != ngx_queue_sentinel(&node->children);\n         q = ngx_queue_next(q))\n    {\n        child = ngx_queue_data(q, ngx_http_v2_node_t, queue);\n        child->parent = parent;\n        child->weight = node->weight * child->weight / weight;\n\n        if (child->weight == 0) {\n            child->weight = 1;\n        }\n    }\n\n    if (parent == NGX_HTTP_V2_ROOT) {\n        node->rank = 0;\n        node->rel_weight = 1.0;\n\n        children = &h2c->dependencies;\n\n    } else {\n        node->rank = parent->rank;\n        node->rel_weight = parent->rel_weight;\n\n        children = &parent->children;\n    }\n\n    ngx_http_v2_node_children_update(node);\n    ngx_queue_add(children, &node->children);\n\n    ngx_memzero(node, sizeof(ngx_http_v2_node_t));\n\n    return node;\n}\n\n\nstatic ngx_int_t\nngx_http_v2_validate_header(ngx_http_request_t *r, ngx_http_v2_header_t *header)\n{\n    u_char                     ch;\n    ngx_uint_t                 i;\n    ngx_http_core_srv_conf_t  *cscf;\n\n    r->invalid_header = 0;\n\n    cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);\n\n    for (i = (header->name.data[0] == ':'); i != header->name.len; i++) {\n        ch = header->name.data[i];\n\n        if ((ch >= 'a' && ch <= 'z')\n            || (ch == '-')\n            || (ch >= '0' && ch <= '9')\n            || (ch == '_' && cscf->underscores_in_headers))\n        {\n            continue;\n        }\n\n        if (ch <= 0x20 || ch == 0x7f || ch == ':'\n            || (ch >= 'A' && ch <= 'Z'))\n        {\n            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                          \"client sent invalid header name: \\\"%V\\\"\",\n                          &header->name);\n\n            return NGX_ERROR;\n        }\n\n        r->invalid_header = 1;\n    }\n\n    for (i = 0; i != header->value.len; i++) {\n        ch = header->value.data[i];\n\n        if (ch == '\\0' || ch == LF || ch == CR) {\n            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                          \"client sent header \\\"%V\\\" with \"\n                          \"invalid value: \\\"%V\\\"\",\n                          &header->name, &header->value);\n\n            return NGX_ERROR;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_v2_pseudo_header(ngx_http_request_t *r, ngx_http_v2_header_t *header)\n{\n    header->name.len--;\n    header->name.data++;\n\n    switch (header->name.len) {\n    case 4:\n        if (ngx_memcmp(header->name.data, \"path\", sizeof(\"path\") - 1)\n            == 0)\n        {\n            return ngx_http_v2_parse_path(r, &header->value);\n        }\n\n        break;\n\n    case 6:\n        if (ngx_memcmp(header->name.data, \"method\", sizeof(\"method\") - 1)\n            == 0)\n        {\n            return ngx_http_v2_parse_method(r, &header->value);\n        }\n\n        if (ngx_memcmp(header->name.data, \"scheme\", sizeof(\"scheme\") - 1)\n            == 0)\n        {\n            return ngx_http_v2_parse_scheme(r, &header->value);\n        }\n\n        break;\n\n    case 9:\n        if (ngx_memcmp(header->name.data, \"authority\", sizeof(\"authority\") - 1)\n            == 0)\n        {\n            return ngx_http_v2_parse_authority(r, &header->value);\n        }\n\n        break;\n    }\n\n    ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                  \"client sent unknown pseudo-header \\\":%V\\\"\",\n                  &header->name);\n\n    return NGX_DECLINED;\n}\n\n\nstatic ngx_int_t\nngx_http_v2_parse_path(ngx_http_request_t *r, ngx_str_t *value)\n{\n    if (r->unparsed_uri.len) {\n        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                      \"client sent duplicate :path header\");\n\n        return NGX_DECLINED;\n    }\n\n    if (value->len == 0) {\n        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                      \"client sent empty :path header\");\n\n        return NGX_DECLINED;\n    }\n\n    r->uri_start = value->data;\n    r->uri_end = value->data + value->len;\n\n    if (ngx_http_parse_uri(r) != NGX_OK) {\n        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                      \"client sent invalid :path header: \\\"%V\\\"\", value);\n\n        return NGX_DECLINED;\n    }\n\n    if (ngx_http_process_request_uri(r) != NGX_OK) {\n        /*\n         * request has been finalized already\n         * in ngx_http_process_request_uri()\n         */\n        return NGX_ABORT;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_v2_parse_method(ngx_http_request_t *r, ngx_str_t *value)\n{\n    size_t         k, len;\n    ngx_uint_t     n;\n    const u_char  *p, *m;\n\n    /*\n     * This array takes less than 256 sequential bytes,\n     * and if typical CPU cache line size is 64 bytes,\n     * it is prefetched for 4 load operations.\n     */\n    static const struct {\n        u_char            len;\n        const u_char      method[11];\n        uint32_t          value;\n    } tests[] = {\n        { 3, \"GET\",       NGX_HTTP_GET },\n        { 4, \"POST\",      NGX_HTTP_POST },\n        { 4, \"HEAD\",      NGX_HTTP_HEAD },\n        { 7, \"OPTIONS\",   NGX_HTTP_OPTIONS },\n        { 8, \"PROPFIND\",  NGX_HTTP_PROPFIND },\n        { 3, \"PUT\",       NGX_HTTP_PUT },\n        { 5, \"MKCOL\",     NGX_HTTP_MKCOL },\n        { 6, \"DELETE\",    NGX_HTTP_DELETE },\n        { 4, \"COPY\",      NGX_HTTP_COPY },\n        { 4, \"MOVE\",      NGX_HTTP_MOVE },\n        { 9, \"PROPPATCH\", NGX_HTTP_PROPPATCH },\n        { 4, \"LOCK\",      NGX_HTTP_LOCK },\n        { 6, \"UNLOCK\",    NGX_HTTP_UNLOCK },\n        { 5, \"PATCH\",     NGX_HTTP_PATCH },\n        { 5, \"TRACE\",     NGX_HTTP_TRACE },\n        { 7, \"CONNECT\",   NGX_HTTP_CONNECT }\n    }, *test;\n\n    if (r->method_name.len) {\n        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                      \"client sent duplicate :method header\");\n\n        return NGX_DECLINED;\n    }\n\n    if (value->len == 0) {\n        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                      \"client sent empty :method header\");\n\n        return NGX_DECLINED;\n    }\n\n    r->method_name.len = value->len;\n    r->method_name.data = value->data;\n\n    len = r->method_name.len;\n    n = sizeof(tests) / sizeof(tests[0]);\n    test = tests;\n\n    do {\n        if (len == test->len) {\n            p = r->method_name.data;\n            m = test->method;\n            k = len;\n\n            do {\n                if (*p++ != *m++) {\n                    goto next;\n                }\n            } while (--k);\n\n            r->method = test->value;\n            return NGX_OK;\n        }\n\n    next:\n        test++;\n\n    } while (--n);\n\n    p = r->method_name.data;\n\n    do {\n        if ((*p < 'A' || *p > 'Z') && *p != '_' && *p != '-') {\n            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                          \"client sent invalid method: \\\"%V\\\"\",\n                          &r->method_name);\n\n            return NGX_DECLINED;\n        }\n\n        p++;\n\n    } while (--len);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_v2_parse_scheme(ngx_http_request_t *r, ngx_str_t *value)\n{\n    u_char      c, ch;\n    ngx_uint_t  i;\n\n    if (r->schema.len) {\n        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                      \"client sent duplicate :scheme header\");\n\n        return NGX_DECLINED;\n    }\n\n    if (value->len == 0) {\n        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                      \"client sent empty :scheme header\");\n\n        return NGX_DECLINED;\n    }\n\n    for (i = 0; i < value->len; i++) {\n        ch = value->data[i];\n\n        c = (u_char) (ch | 0x20);\n        if (c >= 'a' && c <= 'z') {\n            continue;\n        }\n\n        if (((ch >= '0' && ch <= '9') || ch == '+' || ch == '-' || ch == '.')\n            && i > 0)\n        {\n            continue;\n        }\n\n        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                      \"client sent invalid :scheme header: \\\"%V\\\"\", value);\n\n        return NGX_DECLINED;\n    }\n\n    r->schema = *value;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_v2_parse_authority(ngx_http_request_t *r, ngx_str_t *value)\n{\n    return ngx_http_v2_parse_header(r, &ngx_http_v2_parse_headers[0], value);\n}\n\n\nstatic ngx_int_t\nngx_http_v2_parse_header(ngx_http_request_t *r,\n    ngx_http_v2_parse_header_t *header, ngx_str_t *value)\n{\n    ngx_table_elt_t            *h;\n    ngx_http_core_main_conf_t  *cmcf;\n\n    h = ngx_list_push(&r->headers_in.headers);\n    if (h == NULL) {\n        return NGX_ERROR;\n    }\n\n    h->key.len = header->name.len;\n    h->key.data = header->name.data;\n    h->lowcase_key = header->name.data;\n\n    if (header->hh == NULL) {\n        header->hash = ngx_hash_key(header->name.data, header->name.len);\n\n        cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);\n\n        header->hh = ngx_hash_find(&cmcf->headers_in_hash, header->hash,\n                                   h->lowcase_key, h->key.len);\n        if (header->hh == NULL) {\n            return NGX_ERROR;\n        }\n    }\n\n    h->hash = header->hash;\n\n    h->value.len = value->len;\n    h->value.data = value->data;\n\n    if (header->hh->handler(r, h, header->hh->offset) != NGX_OK) {\n        /* header handler has already finalized request */\n        return NGX_ABORT;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_v2_construct_request_line(ngx_http_request_t *r)\n{\n    u_char  *p;\n\n    static const u_char ending[] = \" HTTP/2.0\";\n\n    if (r->method_name.len == 0\n        || r->schema.len == 0\n        || r->unparsed_uri.len == 0)\n    {\n        if (r->method_name.len == 0) {\n            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                          \"client sent no :method header\");\n\n        } else if (r->schema.len == 0) {\n            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                          \"client sent no :scheme header\");\n\n        } else {\n            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                          \"client sent no :path header\");\n        }\n\n        ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);\n        return NGX_ERROR;\n    }\n\n    r->request_line.len = r->method_name.len + 1\n                          + r->unparsed_uri.len\n                          + sizeof(ending) - 1;\n\n    p = ngx_pnalloc(r->pool, r->request_line.len + 1);\n    if (p == NULL) {\n        ngx_http_v2_close_stream(r->stream, NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return NGX_ERROR;\n    }\n\n    r->request_line.data = p;\n\n    p = ngx_cpymem(p, r->method_name.data, r->method_name.len);\n\n    *p++ = ' ';\n\n    p = ngx_cpymem(p, r->unparsed_uri.data, r->unparsed_uri.len);\n\n    ngx_memcpy(p, ending, sizeof(ending));\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http2 request line: \\\"%V\\\"\", &r->request_line);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_v2_cookie(ngx_http_request_t *r, ngx_http_v2_header_t *header)\n{\n    ngx_str_t    *val;\n    ngx_array_t  *cookies;\n\n    cookies = r->stream->cookies;\n\n    if (cookies == NULL) {\n        cookies = ngx_array_create(r->pool, 2, sizeof(ngx_str_t));\n        if (cookies == NULL) {\n            return NGX_ERROR;\n        }\n\n        r->stream->cookies = cookies;\n    }\n\n    val = ngx_array_push(cookies);\n    if (val == NULL) {\n        return NGX_ERROR;\n    }\n\n    val->len = header->value.len;\n    val->data = header->value.data;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_v2_construct_cookie_header(ngx_http_request_t *r)\n{\n    u_char                     *buf, *p, *end;\n    size_t                      len;\n    ngx_str_t                  *vals;\n    ngx_uint_t                  i;\n    ngx_array_t                *cookies;\n    ngx_table_elt_t            *h;\n    ngx_http_header_t          *hh;\n    ngx_http_core_main_conf_t  *cmcf;\n\n    static ngx_str_t cookie = ngx_string(\"cookie\");\n\n    cookies = r->stream->cookies;\n\n    if (cookies == NULL) {\n        return NGX_OK;\n    }\n\n    vals = cookies->elts;\n\n    i = 0;\n    len = 0;\n\n    do {\n        len += vals[i].len + 2;\n    } while (++i != cookies->nelts);\n\n    len -= 2;\n\n    buf = ngx_pnalloc(r->pool, len + 1);\n    if (buf == NULL) {\n        ngx_http_v2_close_stream(r->stream, NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return NGX_ERROR;\n    }\n\n    p = buf;\n    end = buf + len;\n\n    for (i = 0; /* void */ ; i++) {\n\n        p = ngx_cpymem(p, vals[i].data, vals[i].len);\n\n        if (p == end) {\n            *p = '\\0';\n            break;\n        }\n\n        *p++ = ';'; *p++ = ' ';\n    }\n\n    h = ngx_list_push(&r->headers_in.headers);\n    if (h == NULL) {\n        ngx_http_v2_close_stream(r->stream, NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return NGX_ERROR;\n    }\n\n    h->hash = ngx_hash(ngx_hash(ngx_hash(ngx_hash(\n                                    ngx_hash('c', 'o'), 'o'), 'k'), 'i'), 'e');\n\n    h->key.len = cookie.len;\n    h->key.data = cookie.data;\n\n    h->value.len = len;\n    h->value.data = buf;\n\n    h->lowcase_key = cookie.data;\n\n    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);\n\n    hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash,\n                       h->lowcase_key, h->key.len);\n\n    if (hh == NULL) {\n        ngx_http_v2_close_stream(r->stream, NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return NGX_ERROR;\n    }\n\n    if (hh->handler(r, h, hh->offset) != NGX_OK) {\n        /*\n         * request has been finalized already\n         * in ngx_http_process_multi_header_lines()\n         */\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_v2_run_request(ngx_http_request_t *r)\n{\n    ngx_connection_t          *fc;\n    ngx_http_v2_connection_t  *h2c;\n\n    fc = r->connection;\n\n    if (ngx_http_v2_construct_request_line(r) != NGX_OK) {\n        goto failed;\n    }\n\n    if (ngx_http_v2_construct_cookie_header(r) != NGX_OK) {\n        goto failed;\n    }\n\n    r->http_state = NGX_HTTP_PROCESS_REQUEST_STATE;\n\n    if (ngx_http_process_request_header(r) != NGX_OK) {\n        goto failed;\n    }\n\n    if (r->headers_in.content_length_n > 0 && r->stream->in_closed) {\n        ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                      \"client prematurely closed stream\");\n\n        r->stream->skip_data = 1;\n\n        ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST);\n        goto failed;\n    }\n\n    if (r->headers_in.content_length_n == -1 && !r->stream->in_closed) {\n        r->headers_in.chunked = 1;\n    }\n\n    h2c = r->stream->connection;\n\n    h2c->payload_bytes += r->request_length;\n\n    ngx_http_process_request(r);\n\nfailed:\n\n    ngx_http_run_posted_requests(fc);\n}\n\n\nstatic void\nngx_http_v2_run_request_handler(ngx_event_t *ev)\n{\n    ngx_connection_t    *fc;\n    ngx_http_request_t  *r;\n\n    fc = ev->data;\n    r = fc->data;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n                   \"http2 run request handler\");\n\n    ngx_http_v2_run_request(r);\n}\n\n\nngx_int_t\nngx_http_v2_read_request_body(ngx_http_request_t *r)\n{\n    off_t                      len;\n    size_t                     size;\n    ngx_buf_t                 *buf;\n    ngx_int_t                  rc;\n    ngx_http_v2_stream_t      *stream;\n    ngx_http_v2_srv_conf_t    *h2scf;\n    ngx_http_request_body_t   *rb;\n    ngx_http_core_loc_conf_t  *clcf;\n    ngx_http_v2_connection_t  *h2c;\n\n    stream = r->stream;\n    rb = r->request_body;\n\n    if (stream->skip_data) {\n        r->request_body_no_buffering = 0;\n        rb->post_handler(r);\n        return NGX_OK;\n    }\n\n    rb->rest = 1;\n\n    /* set rb->filter_need_buffering */\n\n    rc = ngx_http_top_request_body_filter(r, NULL);\n\n    if (rc != NGX_OK) {\n        stream->skip_data = 1;\n        return rc;\n    }\n\n    h2scf = ngx_http_get_module_srv_conf(r, ngx_http_v2_module);\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    len = r->headers_in.content_length_n;\n\n    if (len < 0 || len > (off_t) clcf->client_body_buffer_size) {\n        len = clcf->client_body_buffer_size;\n\n    } else {\n        len++;\n    }\n\n    if (r->request_body_no_buffering || rb->filter_need_buffering) {\n\n        /*\n         * We need a room to store data up to the stream's initial window size,\n         * at least until this window will be exhausted.\n         */\n\n        if (len < (off_t) h2scf->preread_size) {\n            len = h2scf->preread_size;\n        }\n\n        if (len > NGX_HTTP_V2_MAX_WINDOW) {\n            len = NGX_HTTP_V2_MAX_WINDOW;\n        }\n    }\n\n    rb->buf = ngx_create_temp_buf(r->pool, (size_t) len);\n\n    if (rb->buf == NULL) {\n        stream->skip_data = 1;\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    buf = stream->preread;\n\n    if (stream->in_closed) {\n        if (!rb->filter_need_buffering) {\n            r->request_body_no_buffering = 0;\n        }\n\n        if (buf) {\n            rc = ngx_http_v2_process_request_body(r, buf->pos,\n                                                  buf->last - buf->pos, 1, 0);\n            ngx_pfree(r->pool, buf->start);\n\n        } else {\n            rc = ngx_http_v2_process_request_body(r, NULL, 0, 1, 0);\n        }\n\n        if (rc != NGX_AGAIN) {\n            return rc;\n        }\n\n        r->read_event_handler = ngx_http_v2_read_client_request_body_handler;\n        r->write_event_handler = ngx_http_request_empty_handler;\n\n        return NGX_AGAIN;\n    }\n\n    if (buf) {\n        rc = ngx_http_v2_process_request_body(r, buf->pos,\n                                              buf->last - buf->pos, 0, 0);\n\n        ngx_pfree(r->pool, buf->start);\n\n        if (rc != NGX_OK && rc != NGX_AGAIN) {\n            stream->skip_data = 1;\n            return rc;\n        }\n    }\n\n    if (r->request_body_no_buffering || rb->filter_need_buffering) {\n        size = (size_t) len - h2scf->preread_size;\n\n    } else {\n        stream->no_flow_control = 1;\n        size = NGX_HTTP_V2_MAX_WINDOW - stream->recv_window;\n    }\n\n    if (size) {\n        if (ngx_http_v2_send_window_update(stream->connection,\n                                           stream->node->id, size)\n            == NGX_ERROR)\n        {\n            stream->skip_data = 1;\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n\n        h2c = stream->connection;\n\n        if (!h2c->blocked) {\n            if (ngx_http_v2_send_output_queue(h2c) == NGX_ERROR) {\n                stream->skip_data = 1;\n                return NGX_HTTP_INTERNAL_SERVER_ERROR;\n            }\n        }\n\n        stream->recv_window += size;\n    }\n\n    if (!buf) {\n        ngx_add_timer(r->connection->read, clcf->client_body_timeout);\n    }\n\n    r->read_event_handler = ngx_http_v2_read_client_request_body_handler;\n    r->write_event_handler = ngx_http_request_empty_handler;\n\n    return NGX_AGAIN;\n}\n\n\nstatic ngx_int_t\nngx_http_v2_process_request_body(ngx_http_request_t *r, u_char *pos,\n    size_t size, ngx_uint_t last, ngx_uint_t flush)\n{\n    size_t                     n;\n    ngx_int_t                  rc;\n    ngx_connection_t          *fc;\n    ngx_http_request_body_t   *rb;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    fc = r->connection;\n    rb = r->request_body;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n                   \"http2 process request body\");\n\n    if (size == 0 && !last && !flush) {\n        return NGX_AGAIN;\n    }\n\n    for ( ;; ) {\n        for ( ;; ) {\n            if (rb->buf->last == rb->buf->end && size) {\n\n                if (r->request_body_no_buffering) {\n\n                    /* should never happen due to flow control */\n\n                    ngx_log_error(NGX_LOG_ALERT, fc->log, 0,\n                                  \"no space in http2 body buffer\");\n\n                    return NGX_HTTP_INTERNAL_SERVER_ERROR;\n                }\n\n                /* update chains */\n\n                ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n                               \"http2 body update chains\");\n\n                rc = ngx_http_v2_filter_request_body(r);\n\n                if (rc != NGX_OK) {\n                    return rc;\n                }\n\n                if (rb->busy != NULL) {\n                    ngx_log_error(NGX_LOG_ALERT, fc->log, 0,\n                                  \"busy buffers after request body flush\");\n                    return NGX_HTTP_INTERNAL_SERVER_ERROR;\n                }\n\n                rb->buf->pos = rb->buf->start;\n                rb->buf->last = rb->buf->start;\n            }\n\n            /* copy body data to the buffer */\n\n            n = rb->buf->end - rb->buf->last;\n\n            if (n > size) {\n                n = size;\n            }\n\n            if (n > 0) {\n                rb->buf->last = ngx_cpymem(rb->buf->last, pos, n);\n            }\n\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n                           \"http2 request body recv %uz\", n);\n\n            pos += n;\n            size -= n;\n\n            if (size == 0 && last) {\n                rb->rest = 0;\n            }\n\n            if (size == 0) {\n                break;\n            }\n        }\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n                       \"http2 request body rest %O\", rb->rest);\n\n        if (flush) {\n            rc = ngx_http_v2_filter_request_body(r);\n\n            if (rc != NGX_OK) {\n                return rc;\n            }\n        }\n\n        if (rb->rest == 0 && rb->last_saved) {\n            break;\n        }\n\n        if (size == 0) {\n            clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n            ngx_add_timer(fc->read, clcf->client_body_timeout);\n\n            if (!flush) {\n                ngx_post_event(fc->read, &ngx_posted_events);\n            }\n\n            return NGX_AGAIN;\n        }\n    }\n\n    if (fc->read->timer_set) {\n        ngx_del_timer(fc->read);\n    }\n\n    if (r->request_body_no_buffering) {\n        if (!flush) {\n            ngx_post_event(fc->read, &ngx_posted_events);\n        }\n\n        return NGX_OK;\n    }\n\n    if (r->headers_in.chunked) {\n        r->headers_in.content_length_n = rb->received;\n    }\n\n    r->read_event_handler = ngx_http_block_reading;\n    rb->post_handler(r);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_v2_filter_request_body(ngx_http_request_t *r)\n{\n    ngx_buf_t                 *b, *buf;\n    ngx_int_t                  rc;\n    ngx_chain_t               *cl;\n    ngx_http_request_body_t   *rb;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    rb = r->request_body;\n    buf = rb->buf;\n\n    if (buf->pos == buf->last && (rb->rest || rb->last_sent)) {\n        cl = NULL;\n        goto update;\n    }\n\n    cl = ngx_chain_get_free_buf(r->pool, &rb->free);\n    if (cl == NULL) {\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    b = cl->buf;\n\n    ngx_memzero(b, sizeof(ngx_buf_t));\n\n    if (buf->pos != buf->last) {\n        r->request_length += buf->last - buf->pos;\n        rb->received += buf->last - buf->pos;\n\n        if (r->headers_in.content_length_n != -1) {\n            if (rb->received > r->headers_in.content_length_n) {\n                ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                              \"client intended to send body data \"\n                              \"larger than declared\");\n\n                return NGX_HTTP_BAD_REQUEST;\n            }\n\n        } else {\n            clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n            if (clcf->client_max_body_size\n                && rb->received > clcf->client_max_body_size)\n            {\n                ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,\n                              \"client intended to send too large chunked body: \"\n                              \"%O bytes\", rb->received);\n\n                return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE;\n            }\n        }\n\n        b->temporary = 1;\n        b->pos = buf->pos;\n        b->last = buf->last;\n        b->start = b->pos;\n        b->end = b->last;\n\n        buf->pos = buf->last;\n    }\n\n    if (!rb->rest) {\n        if (r->headers_in.content_length_n != -1\n            && r->headers_in.content_length_n != rb->received)\n        {\n            ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,\n                          \"client prematurely closed stream: \"\n                          \"only %O out of %O bytes of request body received\",\n                          rb->received, r->headers_in.content_length_n);\n\n            return NGX_HTTP_BAD_REQUEST;\n        }\n\n        b->last_buf = 1;\n        rb->last_sent = 1;\n    }\n\n    b->tag = (ngx_buf_tag_t) &ngx_http_v2_filter_request_body;\n    b->flush = r->request_body_no_buffering;\n\nupdate:\n\n    rc = ngx_http_top_request_body_filter(r, cl);\n\n    ngx_chain_update_chains(r->pool, &rb->free, &rb->busy, &cl,\n                            (ngx_buf_tag_t) &ngx_http_v2_filter_request_body);\n\n    return rc;\n}\n\n\nstatic void\nngx_http_v2_read_client_request_body_handler(ngx_http_request_t *r)\n{\n    size_t                     window;\n    ngx_buf_t                 *buf;\n    ngx_int_t                  rc;\n    ngx_connection_t          *fc;\n    ngx_http_v2_stream_t      *stream;\n    ngx_http_v2_connection_t  *h2c;\n\n    fc = r->connection;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n                   \"http2 read client request body handler\");\n\n    if (fc->read->timedout) {\n        ngx_log_error(NGX_LOG_INFO, fc->log, NGX_ETIMEDOUT, \"client timed out\");\n\n        fc->timedout = 1;\n        r->stream->skip_data = 1;\n\n        ngx_http_finalize_request(r, NGX_HTTP_REQUEST_TIME_OUT);\n        return;\n    }\n\n    if (fc->error) {\n        ngx_log_error(NGX_LOG_INFO, fc->log, 0,\n                      \"client prematurely closed stream\");\n\n        r->stream->skip_data = 1;\n\n        ngx_http_finalize_request(r, NGX_HTTP_CLIENT_CLOSED_REQUEST);\n        return;\n    }\n\n    rc = ngx_http_v2_process_request_body(r, NULL, 0, r->stream->in_closed, 1);\n\n    if (rc != NGX_OK && rc != NGX_AGAIN) {\n        r->stream->skip_data = 1;\n        ngx_http_finalize_request(r, rc);\n        return;\n    }\n\n    if (rc == NGX_OK) {\n        return;\n    }\n\n    if (r->stream->no_flow_control) {\n        return;\n    }\n\n    if (r->request_body->rest == 0) {\n        return;\n    }\n\n    if (r->request_body->busy != NULL) {\n        return;\n    }\n\n    stream = r->stream;\n    h2c = stream->connection;\n\n    buf = r->request_body->buf;\n\n    buf->pos = buf->start;\n    buf->last = buf->start;\n\n    window = buf->end - buf->start;\n\n    if (h2c->state.stream == stream) {\n        window -= h2c->state.length;\n    }\n\n    if (window <= stream->recv_window) {\n        if (window < stream->recv_window) {\n            ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,\n                          \"http2 negative window update\");\n\n            stream->skip_data = 1;\n\n            ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n            return;\n        }\n\n        return;\n    }\n\n    if (ngx_http_v2_send_window_update(h2c, stream->node->id,\n                                       window - stream->recv_window)\n        == NGX_ERROR)\n    {\n        stream->skip_data = 1;\n        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return;\n    }\n\n    stream->recv_window = window;\n\n    if (ngx_http_v2_send_output_queue(h2c) == NGX_ERROR) {\n        stream->skip_data = 1;\n        ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);\n        return;\n    }\n}\n\n\nngx_int_t\nngx_http_v2_read_unbuffered_request_body(ngx_http_request_t *r)\n{\n    size_t                     window;\n    ngx_buf_t                 *buf;\n    ngx_int_t                  rc;\n    ngx_connection_t          *fc;\n    ngx_http_v2_stream_t      *stream;\n    ngx_http_v2_connection_t  *h2c;\n\n    stream = r->stream;\n    fc = r->connection;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n                   \"http2 read unbuffered request body\");\n\n    if (fc->read->timedout) {\n        if (stream->recv_window) {\n            stream->skip_data = 1;\n            fc->timedout = 1;\n\n            return NGX_HTTP_REQUEST_TIME_OUT;\n        }\n\n        fc->read->timedout = 0;\n    }\n\n    if (fc->error) {\n        stream->skip_data = 1;\n        return NGX_HTTP_BAD_REQUEST;\n    }\n\n    rc = ngx_http_v2_process_request_body(r, NULL, 0, r->stream->in_closed, 1);\n\n    if (rc != NGX_OK && rc != NGX_AGAIN) {\n        stream->skip_data = 1;\n        return rc;\n    }\n\n    if (rc == NGX_OK) {\n        return NGX_OK;\n    }\n\n    if (r->request_body->rest == 0) {\n        return NGX_AGAIN;\n    }\n\n    if (r->request_body->busy != NULL) {\n        return NGX_AGAIN;\n    }\n\n    buf = r->request_body->buf;\n\n    buf->pos = buf->start;\n    buf->last = buf->start;\n\n    window = buf->end - buf->start;\n    h2c = stream->connection;\n\n    if (h2c->state.stream == stream) {\n        window -= h2c->state.length;\n    }\n\n    if (window <= stream->recv_window) {\n        if (window < stream->recv_window) {\n            ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0,\n                          \"http2 negative window update\");\n            stream->skip_data = 1;\n            return NGX_HTTP_INTERNAL_SERVER_ERROR;\n        }\n\n        return NGX_AGAIN;\n    }\n\n    if (ngx_http_v2_send_window_update(h2c, stream->node->id,\n                                       window - stream->recv_window)\n        == NGX_ERROR)\n    {\n        stream->skip_data = 1;\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    if (ngx_http_v2_send_output_queue(h2c) == NGX_ERROR) {\n        stream->skip_data = 1;\n        return NGX_HTTP_INTERNAL_SERVER_ERROR;\n    }\n\n    stream->recv_window = window;\n\n    return NGX_AGAIN;\n}\n\n\nstatic ngx_int_t\nngx_http_v2_terminate_stream(ngx_http_v2_connection_t *h2c,\n    ngx_http_v2_stream_t *stream, ngx_uint_t status)\n{\n    ngx_event_t       *rev;\n    ngx_connection_t  *fc;\n\n    if (stream->rst_sent) {\n        return NGX_OK;\n    }\n\n    if (ngx_http_v2_send_rst_stream(h2c, stream->node->id, status)\n        == NGX_ERROR)\n    {\n        return NGX_ERROR;\n    }\n\n    stream->rst_sent = 1;\n    stream->skip_data = 1;\n\n    fc = stream->request->connection;\n    fc->error = 1;\n\n    rev = fc->read;\n    rev->handler(rev);\n\n    return NGX_OK;\n}\n\n\nvoid\nngx_http_v2_close_stream(ngx_http_v2_stream_t *stream, ngx_int_t rc)\n{\n    ngx_pool_t                *pool;\n    ngx_uint_t                 push;\n    ngx_event_t               *ev;\n    ngx_connection_t          *fc;\n    ngx_http_v2_node_t        *node;\n    ngx_http_v2_connection_t  *h2c;\n\n    h2c = stream->connection;\n    node = stream->node;\n\n    ngx_log_debug4(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n                   \"http2 close stream %ui, queued %ui, \"\n                   \"processing %ui, pushing %ui\",\n                   node->id, stream->queued, h2c->processing, h2c->pushing);\n\n    fc = stream->request->connection;\n\n    if (stream->queued) {\n        fc->error = 1;\n        fc->write->handler = ngx_http_v2_retry_close_stream_handler;\n        fc->read->handler = ngx_http_v2_retry_close_stream_handler;\n        return;\n    }\n\n    if (!stream->rst_sent && !h2c->connection->error) {\n\n        if (!stream->out_closed) {\n            if (ngx_http_v2_send_rst_stream(h2c, node->id,\n                                      fc->timedout ? NGX_HTTP_V2_PROTOCOL_ERROR\n                                                   : NGX_HTTP_V2_INTERNAL_ERROR)\n                != NGX_OK)\n            {\n                h2c->connection->error = 1;\n            }\n\n        } else if (!stream->in_closed) {\n            if (ngx_http_v2_send_rst_stream(h2c, node->id, NGX_HTTP_V2_NO_ERROR)\n                != NGX_OK)\n            {\n                h2c->connection->error = 1;\n            }\n        }\n    }\n\n    if (h2c->state.stream == stream) {\n        h2c->state.stream = NULL;\n    }\n\n    push = stream->node->id % 2 == 0;\n\n    node->stream = NULL;\n\n    ngx_queue_insert_tail(&h2c->closed, &node->reuse);\n    h2c->closed_nodes++;\n\n    /*\n     * This pool keeps decoded request headers which can be used by log phase\n     * handlers in ngx_http_free_request().\n     *\n     * The pointer is stored into local variable because the stream object\n     * will be destroyed after a call to ngx_http_free_request().\n     */\n    pool = stream->pool;\n\n    h2c->frames -= stream->frames;\n\n    ngx_http_free_request(stream->request, rc);\n\n    if (pool != h2c->state.pool) {\n        ngx_destroy_pool(pool);\n\n    } else {\n        /* pool will be destroyed when the complete header is parsed */\n        h2c->state.keep_pool = 0;\n    }\n\n    ev = fc->read;\n\n    if (ev->timer_set) {\n        ngx_del_timer(ev);\n    }\n\n    if (ev->posted) {\n        ngx_delete_posted_event(ev);\n    }\n\n    ev = fc->write;\n\n    if (ev->timer_set) {\n        ngx_del_timer(ev);\n    }\n\n    if (ev->posted) {\n        ngx_delete_posted_event(ev);\n    }\n\n    fc->data = h2c->free_fake_connections;\n    h2c->free_fake_connections = fc;\n\n    if (push) {\n        h2c->pushing--;\n\n    } else {\n        h2c->processing--;\n    }\n\n    if (h2c->processing || h2c->pushing || h2c->blocked) {\n        return;\n    }\n\n    ev = h2c->connection->read;\n\n    ev->handler = ngx_http_v2_handle_connection_handler;\n    ngx_post_event(ev, &ngx_posted_events);\n}\n\n\nstatic void\nngx_http_v2_close_stream_handler(ngx_event_t *ev)\n{\n    ngx_connection_t    *fc;\n    ngx_http_request_t  *r;\n\n    fc = ev->data;\n    r = fc->data;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n                   \"http2 close stream handler\");\n\n    if (ev->timedout) {\n        ngx_log_error(NGX_LOG_INFO, fc->log, NGX_ETIMEDOUT, \"client timed out\");\n\n        fc->timedout = 1;\n\n        ngx_http_v2_close_stream(r->stream, NGX_HTTP_REQUEST_TIME_OUT);\n        return;\n    }\n\n    ngx_http_v2_close_stream(r->stream, 0);\n}\n\n\nstatic void\nngx_http_v2_retry_close_stream_handler(ngx_event_t *ev)\n{\n    ngx_connection_t    *fc;\n    ngx_http_request_t  *r;\n\n    fc = ev->data;\n    r = fc->data;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n                   \"http2 retry close stream handler\");\n\n    ngx_http_v2_close_stream(r->stream, 0);\n}\n\n\nstatic void\nngx_http_v2_handle_connection_handler(ngx_event_t *rev)\n{\n    ngx_connection_t          *c;\n    ngx_http_v2_connection_t  *h2c;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0,\n                   \"http2 handle connection handler\");\n\n    c = rev->data;\n    h2c = c->data;\n\n    if (c->error) {\n        ngx_http_v2_finalize_connection(h2c, 0);\n        return;\n    }\n\n    rev->handler = ngx_http_v2_read_handler;\n\n    if (rev->ready) {\n        ngx_http_v2_read_handler(rev);\n        return;\n    }\n\n    if (h2c->last_out && ngx_http_v2_send_output_queue(h2c) == NGX_ERROR) {\n        ngx_http_v2_finalize_connection(h2c, 0);\n        return;\n    }\n\n    ngx_http_v2_handle_connection(c->data);\n}\n\n\nstatic void\nngx_http_v2_idle_handler(ngx_event_t *rev)\n{\n    ngx_connection_t          *c;\n    ngx_http_v2_srv_conf_t    *h2scf;\n    ngx_http_v2_connection_t  *h2c;\n    ngx_http_core_loc_conf_t  *clcf;\n\n    c = rev->data;\n    h2c = c->data;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, \"http2 idle handler\");\n\n    if (rev->timedout || c->close) {\n        ngx_http_v2_finalize_connection(h2c, NGX_HTTP_V2_NO_ERROR);\n        return;\n    }\n\n#if (NGX_HAVE_KQUEUE)\n\n    if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {\n        if (rev->pending_eof) {\n            c->log->handler = NULL;\n            ngx_log_error(NGX_LOG_INFO, c->log, rev->kq_errno,\n                          \"kevent() reported that client %V closed \"\n                          \"idle connection\", &c->addr_text);\n#if (NGX_HTTP_SSL)\n            if (c->ssl) {\n                c->ssl->no_send_shutdown = 1;\n            }\n#endif\n            ngx_http_close_connection(c);\n            return;\n        }\n    }\n\n#endif\n\n    clcf = ngx_http_get_module_loc_conf(h2c->http_connection->conf_ctx,\n                                        ngx_http_core_module);\n\n    if (h2c->idle++ > 10 * clcf->keepalive_requests) {\n        ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                      \"http2 flood detected\");\n        ngx_http_v2_finalize_connection(h2c, NGX_HTTP_V2_NO_ERROR);\n        return;\n    }\n\n    c->destroyed = 0;\n    ngx_reusable_connection(c, 0);\n\n    h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx,\n                                         ngx_http_v2_module);\n\n    h2c->pool = ngx_create_pool(h2scf->pool_size, h2c->connection->log);\n    if (h2c->pool == NULL) {\n        ngx_http_v2_finalize_connection(h2c, NGX_HTTP_V2_INTERNAL_ERROR);\n        return;\n    }\n\n    c->write->handler = ngx_http_v2_write_handler;\n\n    rev->handler = ngx_http_v2_read_handler;\n    ngx_http_v2_read_handler(rev);\n}\n\n\nstatic void\nngx_http_v2_finalize_connection(ngx_http_v2_connection_t *h2c,\n    ngx_uint_t status)\n{\n    ngx_uint_t               i, size;\n    ngx_event_t             *ev;\n    ngx_connection_t        *c, *fc;\n    ngx_http_request_t      *r;\n    ngx_http_v2_node_t      *node;\n    ngx_http_v2_stream_t    *stream;\n    ngx_http_v2_srv_conf_t  *h2scf;\n\n    c = h2c->connection;\n\n    h2c->blocked = 1;\n\n    if (!c->error && !h2c->goaway) {\n        h2c->goaway = 1;\n\n        if (ngx_http_v2_send_goaway(h2c, status) != NGX_ERROR) {\n            (void) ngx_http_v2_send_output_queue(h2c);\n        }\n    }\n\n    if (!h2c->processing && !h2c->pushing) {\n        goto done;\n    }\n\n    c->read->handler = ngx_http_empty_handler;\n    c->write->handler = ngx_http_empty_handler;\n\n    h2c->last_out = NULL;\n\n    h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx,\n                                         ngx_http_v2_module);\n\n    size = ngx_http_v2_index_size(h2scf);\n\n    for (i = 0; i < size; i++) {\n\n        for (node = h2c->streams_index[i]; node; node = node->index) {\n            stream = node->stream;\n\n            if (stream == NULL) {\n                continue;\n            }\n\n            stream->waiting = 0;\n\n            r = stream->request;\n            fc = r->connection;\n\n            fc->error = 1;\n\n            if (stream->queued) {\n                stream->queued = 0;\n\n                ev = fc->write;\n                ev->active = 0;\n                ev->ready = 1;\n\n            } else {\n                ev = fc->read;\n            }\n\n            ev->eof = 1;\n            ev->handler(ev);\n        }\n    }\n\n    h2c->blocked = 0;\n\n    if (h2c->processing || h2c->pushing) {\n        c->error = 1;\n        return;\n    }\n\ndone:\n\n    if (c->error) {\n        ngx_http_close_connection(c);\n        return;\n    }\n\n    ngx_http_v2_lingering_close(c);\n}\n\n\nstatic ngx_int_t\nngx_http_v2_adjust_windows(ngx_http_v2_connection_t *h2c, ssize_t delta)\n{\n    ngx_uint_t               i, size;\n    ngx_event_t             *wev;\n    ngx_http_v2_node_t      *node;\n    ngx_http_v2_stream_t    *stream;\n    ngx_http_v2_srv_conf_t  *h2scf;\n\n    h2scf = ngx_http_get_module_srv_conf(h2c->http_connection->conf_ctx,\n                                         ngx_http_v2_module);\n\n    size = ngx_http_v2_index_size(h2scf);\n\n    for (i = 0; i < size; i++) {\n\n        for (node = h2c->streams_index[i]; node; node = node->index) {\n            stream = node->stream;\n\n            if (stream == NULL) {\n                continue;\n            }\n\n            if (delta > 0\n                && stream->send_window\n                      > (ssize_t) (NGX_HTTP_V2_MAX_WINDOW - delta))\n            {\n                if (ngx_http_v2_terminate_stream(h2c, stream,\n                                                 NGX_HTTP_V2_FLOW_CTRL_ERROR)\n                    == NGX_ERROR)\n                {\n                    return NGX_ERROR;\n                }\n\n                continue;\n            }\n\n            stream->send_window += delta;\n\n            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n                           \"http2:%ui adjusted window: %z\",\n                           node->id, stream->send_window);\n\n            if (stream->send_window > 0 && stream->exhausted) {\n                stream->exhausted = 0;\n\n                wev = stream->request->connection->write;\n\n                wev->active = 0;\n                wev->ready = 1;\n\n                if (!wev->delayed) {\n                    wev->handler(wev);\n                }\n            }\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_v2_set_dependency(ngx_http_v2_connection_t *h2c,\n    ngx_http_v2_node_t *node, ngx_uint_t depend, ngx_uint_t exclusive)\n{\n    ngx_queue_t         *children, *q;\n    ngx_http_v2_node_t  *parent, *child, *next;\n\n    parent = depend ? ngx_http_v2_get_node_by_id(h2c, depend, 0) : NULL;\n\n    if (parent == NULL) {\n        parent = NGX_HTTP_V2_ROOT;\n\n        if (depend != 0) {\n            exclusive = 0;\n        }\n\n        node->rank = 1;\n        node->rel_weight = (1.0 / 256) * node->weight;\n\n        children = &h2c->dependencies;\n\n    } else {\n        if (node->parent != NULL) {\n\n            for (next = parent->parent;\n                 next != NGX_HTTP_V2_ROOT && next->rank >= node->rank;\n                 next = next->parent)\n            {\n                if (next != node) {\n                    continue;\n                }\n\n                ngx_queue_remove(&parent->queue);\n                ngx_queue_insert_after(&node->queue, &parent->queue);\n\n                parent->parent = node->parent;\n\n                if (node->parent == NGX_HTTP_V2_ROOT) {\n                    parent->rank = 1;\n                    parent->rel_weight = (1.0 / 256) * parent->weight;\n\n                } else {\n                    parent->rank = node->parent->rank + 1;\n                    parent->rel_weight = (node->parent->rel_weight / 256)\n                                         * parent->weight;\n                }\n\n                if (!exclusive) {\n                    ngx_http_v2_node_children_update(parent);\n                }\n\n                break;\n            }\n        }\n\n        node->rank = parent->rank + 1;\n        node->rel_weight = (parent->rel_weight / 256) * node->weight;\n\n        if (parent->stream == NULL) {\n            ngx_queue_remove(&parent->reuse);\n            ngx_queue_insert_tail(&h2c->closed, &parent->reuse);\n        }\n\n        children = &parent->children;\n    }\n\n    if (exclusive) {\n        for (q = ngx_queue_head(children);\n             q != ngx_queue_sentinel(children);\n             q = ngx_queue_next(q))\n        {\n            child = ngx_queue_data(q, ngx_http_v2_node_t, queue);\n            child->parent = node;\n        }\n\n        ngx_queue_add(&node->children, children);\n        ngx_queue_init(children);\n    }\n\n    if (node->parent != NULL) {\n        ngx_queue_remove(&node->queue);\n    }\n\n    ngx_queue_insert_tail(children, &node->queue);\n\n    node->parent = parent;\n\n    ngx_http_v2_node_children_update(node);\n}\n\n\nstatic void\nngx_http_v2_node_children_update(ngx_http_v2_node_t *node)\n{\n    ngx_queue_t         *q;\n    ngx_http_v2_node_t  *child;\n\n    for (q = ngx_queue_head(&node->children);\n         q != ngx_queue_sentinel(&node->children);\n         q = ngx_queue_next(q))\n    {\n        child = ngx_queue_data(q, ngx_http_v2_node_t, queue);\n\n        child->rank = node->rank + 1;\n        child->rel_weight = (node->rel_weight / 256) * child->weight;\n\n        ngx_http_v2_node_children_update(child);\n    }\n}\n\n\nstatic void\nngx_http_v2_pool_cleanup(void *data)\n{\n    ngx_http_v2_connection_t  *h2c = data;\n\n    if (h2c->state.pool) {\n        ngx_destroy_pool(h2c->state.pool);\n    }\n\n    if (h2c->pool) {\n        ngx_destroy_pool(h2c->pool);\n    }\n}\n"
  },
  {
    "path": "src/http/v2/ngx_http_v2.h",
    "content": "/*\n * Copyright (C) Nginx, Inc.\n * Copyright (C) Valentin V. Bartenev\n */\n\n\n#ifndef _NGX_HTTP_V2_H_INCLUDED_\n#define _NGX_HTTP_V2_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\n#define NGX_HTTP_V2_ALPN_PROTO           \"\\x02h2\"\n\n#define NGX_HTTP_V2_STATE_BUFFER_SIZE    16\n\n#define NGX_HTTP_V2_DEFAULT_FRAME_SIZE   (1 << 14)\n#define NGX_HTTP_V2_MAX_FRAME_SIZE       ((1 << 24) - 1)\n\n#define NGX_HTTP_V2_INT_OCTETS           4\n#define NGX_HTTP_V2_MAX_FIELD                                                 \\\n    (127 + (1 << (NGX_HTTP_V2_INT_OCTETS - 1) * 7) - 1)\n\n#define NGX_HTTP_V2_STREAM_ID_SIZE       4\n\n#define NGX_HTTP_V2_FRAME_HEADER_SIZE    9\n\n/* frame types */\n#define NGX_HTTP_V2_DATA_FRAME           0x0\n#define NGX_HTTP_V2_HEADERS_FRAME        0x1\n#define NGX_HTTP_V2_PRIORITY_FRAME       0x2\n#define NGX_HTTP_V2_RST_STREAM_FRAME     0x3\n#define NGX_HTTP_V2_SETTINGS_FRAME       0x4\n#define NGX_HTTP_V2_PUSH_PROMISE_FRAME   0x5\n#define NGX_HTTP_V2_PING_FRAME           0x6\n#define NGX_HTTP_V2_GOAWAY_FRAME         0x7\n#define NGX_HTTP_V2_WINDOW_UPDATE_FRAME  0x8\n#define NGX_HTTP_V2_CONTINUATION_FRAME   0x9\n\n/* frame flags */\n#define NGX_HTTP_V2_NO_FLAG              0x00\n#define NGX_HTTP_V2_ACK_FLAG             0x01\n#define NGX_HTTP_V2_END_STREAM_FLAG      0x01\n#define NGX_HTTP_V2_END_HEADERS_FLAG     0x04\n#define NGX_HTTP_V2_PADDED_FLAG          0x08\n#define NGX_HTTP_V2_PRIORITY_FLAG        0x20\n\n#define NGX_HTTP_V2_MAX_WINDOW           ((1U << 31) - 1)\n#define NGX_HTTP_V2_DEFAULT_WINDOW       65535\n\n#define NGX_HTTP_V2_DEFAULT_WEIGHT       16\n\n\ntypedef struct ngx_http_v2_connection_s   ngx_http_v2_connection_t;\ntypedef struct ngx_http_v2_node_s         ngx_http_v2_node_t;\ntypedef struct ngx_http_v2_out_frame_s    ngx_http_v2_out_frame_t;\n\n\ntypedef u_char *(*ngx_http_v2_handler_pt) (ngx_http_v2_connection_t *h2c,\n    u_char *pos, u_char *end);\n\n\ntypedef struct {\n    ngx_str_t                        name;\n    ngx_str_t                        value;\n} ngx_http_v2_header_t;\n\n\ntypedef struct {\n    ngx_uint_t                       sid;\n    size_t                           length;\n    size_t                           padding;\n    unsigned                         flags:8;\n\n    unsigned                         incomplete:1;\n    unsigned                         keep_pool:1;\n\n    /* HPACK */\n    unsigned                         parse_name:1;\n    unsigned                         parse_value:1;\n    unsigned                         index:1;\n    ngx_http_v2_header_t             header;\n    size_t                           header_limit;\n    u_char                           field_state;\n    u_char                          *field_start;\n    u_char                          *field_end;\n    size_t                           field_rest;\n    ngx_pool_t                      *pool;\n\n    ngx_http_v2_stream_t            *stream;\n\n    u_char                           buffer[NGX_HTTP_V2_STATE_BUFFER_SIZE];\n    size_t                           buffer_used;\n    ngx_http_v2_handler_pt           handler;\n} ngx_http_v2_state_t;\n\n\n\ntypedef struct {\n    ngx_http_v2_header_t           **entries;\n\n    ngx_uint_t                       added;\n    ngx_uint_t                       deleted;\n    ngx_uint_t                       reused;\n    ngx_uint_t                       allocated;\n\n    size_t                           size;\n    size_t                           free;\n    u_char                          *storage;\n    u_char                          *pos;\n} ngx_http_v2_hpack_t;\n\n\nstruct ngx_http_v2_connection_s {\n    ngx_connection_t                *connection;\n    ngx_http_connection_t           *http_connection;\n\n    off_t                            total_bytes;\n    off_t                            payload_bytes;\n\n    ngx_uint_t                       processing;\n    ngx_uint_t                       frames;\n    ngx_uint_t                       idle;\n    ngx_uint_t                       new_streams;\n    ngx_uint_t                       refused_streams;\n    ngx_uint_t                       priority_limit;\n\n    ngx_uint_t                       pushing;\n    ngx_uint_t                       concurrent_pushes;\n\n    size_t                           send_window;\n    size_t                           recv_window;\n    size_t                           init_window;\n\n    size_t                           frame_size;\n\n    ngx_queue_t                      waiting;\n\n    ngx_http_v2_state_t              state;\n\n    ngx_http_v2_hpack_t              hpack;\n\n    ngx_pool_t                      *pool;\n\n    ngx_http_v2_out_frame_t         *free_frames;\n    ngx_connection_t                *free_fake_connections;\n\n    ngx_http_v2_node_t             **streams_index;\n\n    ngx_http_v2_out_frame_t         *last_out;\n\n    ngx_queue_t                      dependencies;\n    ngx_queue_t                      closed;\n\n    ngx_uint_t                       closed_nodes;\n    ngx_uint_t                       last_sid;\n    ngx_uint_t                       last_push;\n\n    time_t                           lingering_time;\n\n    unsigned                         settings_ack:1;\n    unsigned                         table_update:1;\n    unsigned                         blocked:1;\n    unsigned                         goaway:1;\n    unsigned                         push_disabled:1;\n};\n\n\nstruct ngx_http_v2_node_s {\n    ngx_uint_t                       id;\n    ngx_http_v2_node_t              *index;\n    ngx_http_v2_node_t              *parent;\n    ngx_queue_t                      queue;\n    ngx_queue_t                      children;\n    ngx_queue_t                      reuse;\n    ngx_uint_t                       rank;\n    ngx_uint_t                       weight;\n    double                           rel_weight;\n    ngx_http_v2_stream_t            *stream;\n};\n\n\nstruct ngx_http_v2_stream_s {\n    ngx_http_request_t              *request;\n    ngx_http_v2_connection_t        *connection;\n    ngx_http_v2_node_t              *node;\n\n    ngx_uint_t                       queued;\n\n    /*\n     * A change to SETTINGS_INITIAL_WINDOW_SIZE could cause the\n     * send_window to become negative, hence it's signed.\n     */\n    ssize_t                          send_window;\n    size_t                           recv_window;\n\n    ngx_buf_t                       *preread;\n\n    ngx_uint_t                       frames;\n\n    ngx_http_v2_out_frame_t         *free_frames;\n    ngx_chain_t                     *free_frame_headers;\n    ngx_chain_t                     *free_bufs;\n\n    ngx_queue_t                      queue;\n\n    ngx_array_t                     *cookies;\n\n    ngx_pool_t                      *pool;\n\n    unsigned                         waiting:1;\n    unsigned                         blocked:1;\n    unsigned                         exhausted:1;\n    unsigned                         in_closed:1;\n    unsigned                         out_closed:1;\n    unsigned                         rst_sent:1;\n    unsigned                         no_flow_control:1;\n    unsigned                         skip_data:1;\n};\n\n\nstruct ngx_http_v2_out_frame_s {\n    ngx_http_v2_out_frame_t         *next;\n    ngx_chain_t                     *first;\n    ngx_chain_t                     *last;\n    ngx_int_t                      (*handler)(ngx_http_v2_connection_t *h2c,\n                                        ngx_http_v2_out_frame_t *frame);\n\n    ngx_http_v2_stream_t            *stream;\n    size_t                           length;\n\n    unsigned                         blocked:1;\n    unsigned                         fin:1;\n};\n\n\nstatic ngx_inline void\nngx_http_v2_queue_frame(ngx_http_v2_connection_t *h2c,\n    ngx_http_v2_out_frame_t *frame)\n{\n    ngx_http_v2_out_frame_t  **out;\n\n    for (out = &h2c->last_out; *out; out = &(*out)->next) {\n\n        if ((*out)->blocked || (*out)->stream == NULL) {\n            break;\n        }\n\n        if ((*out)->stream->node->rank < frame->stream->node->rank\n            || ((*out)->stream->node->rank == frame->stream->node->rank\n                && (*out)->stream->node->rel_weight\n                   >= frame->stream->node->rel_weight))\n        {\n            break;\n        }\n    }\n\n    frame->next = *out;\n    *out = frame;\n}\n\n\nstatic ngx_inline void\nngx_http_v2_queue_blocked_frame(ngx_http_v2_connection_t *h2c,\n    ngx_http_v2_out_frame_t *frame)\n{\n    ngx_http_v2_out_frame_t  **out;\n\n    for (out = &h2c->last_out; *out; out = &(*out)->next) {\n\n        if ((*out)->blocked || (*out)->stream == NULL) {\n            break;\n        }\n    }\n\n    frame->next = *out;\n    *out = frame;\n}\n\n\nstatic ngx_inline void\nngx_http_v2_queue_ordered_frame(ngx_http_v2_connection_t *h2c,\n    ngx_http_v2_out_frame_t *frame)\n{\n    frame->next = h2c->last_out;\n    h2c->last_out = frame;\n}\n\n\nvoid ngx_http_v2_init(ngx_event_t *rev);\n\nngx_int_t ngx_http_v2_read_request_body(ngx_http_request_t *r);\nngx_int_t ngx_http_v2_read_unbuffered_request_body(ngx_http_request_t *r);\n\nngx_http_v2_stream_t *ngx_http_v2_push_stream(ngx_http_v2_stream_t *parent,\n    ngx_str_t *path);\n\nvoid ngx_http_v2_close_stream(ngx_http_v2_stream_t *stream, ngx_int_t rc);\n\nngx_int_t ngx_http_v2_send_output_queue(ngx_http_v2_connection_t *h2c);\n\n\nngx_str_t *ngx_http_v2_get_static_name(ngx_uint_t index);\nngx_str_t *ngx_http_v2_get_static_value(ngx_uint_t index);\n\nngx_int_t ngx_http_v2_get_indexed_header(ngx_http_v2_connection_t *h2c,\n    ngx_uint_t index, ngx_uint_t name_only);\nngx_int_t ngx_http_v2_add_header(ngx_http_v2_connection_t *h2c,\n    ngx_http_v2_header_t *header);\nngx_int_t ngx_http_v2_table_size(ngx_http_v2_connection_t *h2c, size_t size);\n\n\n#define ngx_http_v2_prefix(bits)  ((1 << (bits)) - 1)\n\n\n#if (NGX_HAVE_NONALIGNED)\n\n#define ngx_http_v2_parse_uint16(p)  ntohs(*(uint16_t *) (p))\n#define ngx_http_v2_parse_uint32(p)  ntohl(*(uint32_t *) (p))\n\n#else\n\n#define ngx_http_v2_parse_uint16(p)  ((p)[0] << 8 | (p)[1])\n#define ngx_http_v2_parse_uint32(p)                                           \\\n    ((uint32_t) (p)[0] << 24 | (p)[1] << 16 | (p)[2] << 8 | (p)[3])\n\n#endif\n\n#define ngx_http_v2_parse_length(p)  ((p) >> 8)\n#define ngx_http_v2_parse_type(p)    ((p) & 0xff)\n#define ngx_http_v2_parse_sid(p)     (ngx_http_v2_parse_uint32(p) & 0x7fffffff)\n#define ngx_http_v2_parse_window(p)  (ngx_http_v2_parse_uint32(p) & 0x7fffffff)\n\n\n#define ngx_http_v2_write_uint16_aligned(p, s)                                \\\n    (*(uint16_t *) (p) = htons((uint16_t) (s)), (p) + sizeof(uint16_t))\n#define ngx_http_v2_write_uint32_aligned(p, s)                                \\\n    (*(uint32_t *) (p) = htonl((uint32_t) (s)), (p) + sizeof(uint32_t))\n\n#if (NGX_HAVE_NONALIGNED)\n\n#define ngx_http_v2_write_uint16  ngx_http_v2_write_uint16_aligned\n#define ngx_http_v2_write_uint32  ngx_http_v2_write_uint32_aligned\n\n#else\n\n#define ngx_http_v2_write_uint16(p, s)                                        \\\n    ((p)[0] = (u_char) ((s) >> 8),                                            \\\n     (p)[1] = (u_char)  (s),                                                  \\\n     (p) + sizeof(uint16_t))\n\n#define ngx_http_v2_write_uint32(p, s)                                        \\\n    ((p)[0] = (u_char) ((s) >> 24),                                           \\\n     (p)[1] = (u_char) ((s) >> 16),                                           \\\n     (p)[2] = (u_char) ((s) >> 8),                                            \\\n     (p)[3] = (u_char)  (s),                                                  \\\n     (p) + sizeof(uint32_t))\n\n#endif\n\n#define ngx_http_v2_write_len_and_type(p, l, t)                               \\\n    ngx_http_v2_write_uint32_aligned(p, (l) << 8 | (t))\n\n#define ngx_http_v2_write_sid  ngx_http_v2_write_uint32\n\n\n#define ngx_http_v2_indexed(i)      (128 + (i))\n#define ngx_http_v2_inc_indexed(i)  (64 + (i))\n\n#define ngx_http_v2_write_name(dst, src, len, tmp)                            \\\n    ngx_http_v2_string_encode(dst, src, len, tmp, 1)\n#define ngx_http_v2_write_value(dst, src, len, tmp)                           \\\n    ngx_http_v2_string_encode(dst, src, len, tmp, 0)\n\n#define NGX_HTTP_V2_ENCODE_RAW            0\n#define NGX_HTTP_V2_ENCODE_HUFF           0x80\n\n#define NGX_HTTP_V2_AUTHORITY_INDEX       1\n\n#define NGX_HTTP_V2_METHOD_INDEX          2\n#define NGX_HTTP_V2_METHOD_GET_INDEX      2\n#define NGX_HTTP_V2_METHOD_POST_INDEX     3\n\n#define NGX_HTTP_V2_PATH_INDEX            4\n#define NGX_HTTP_V2_PATH_ROOT_INDEX       4\n\n#define NGX_HTTP_V2_SCHEME_HTTP_INDEX     6\n#define NGX_HTTP_V2_SCHEME_HTTPS_INDEX    7\n\n#define NGX_HTTP_V2_STATUS_INDEX          8\n#define NGX_HTTP_V2_STATUS_200_INDEX      8\n#define NGX_HTTP_V2_STATUS_204_INDEX      9\n#define NGX_HTTP_V2_STATUS_206_INDEX      10\n#define NGX_HTTP_V2_STATUS_304_INDEX      11\n#define NGX_HTTP_V2_STATUS_400_INDEX      12\n#define NGX_HTTP_V2_STATUS_404_INDEX      13\n#define NGX_HTTP_V2_STATUS_500_INDEX      14\n\n#define NGX_HTTP_V2_ACCEPT_ENCODING_INDEX 16\n#define NGX_HTTP_V2_ACCEPT_LANGUAGE_INDEX 17\n#define NGX_HTTP_V2_CONTENT_LENGTH_INDEX  28\n#define NGX_HTTP_V2_CONTENT_TYPE_INDEX    31\n#define NGX_HTTP_V2_DATE_INDEX            33\n#define NGX_HTTP_V2_LAST_MODIFIED_INDEX   44\n#define NGX_HTTP_V2_LOCATION_INDEX        46\n#define NGX_HTTP_V2_SERVER_INDEX          54\n#define NGX_HTTP_V2_USER_AGENT_INDEX      58\n#define NGX_HTTP_V2_VARY_INDEX            59\n\n\nu_char *ngx_http_v2_string_encode(u_char *dst, u_char *src, size_t len,\n    u_char *tmp, ngx_uint_t lower);\n\n\n#endif /* _NGX_HTTP_V2_H_INCLUDED_ */\n"
  },
  {
    "path": "src/http/v2/ngx_http_v2_encode.c",
    "content": "\n/*\n * Copyright (C) Nginx, Inc.\n * Copyright (C) Valentin V. Bartenev\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\nstatic u_char *ngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix,\n    ngx_uint_t value);\n\n\nu_char *\nngx_http_v2_string_encode(u_char *dst, u_char *src, size_t len, u_char *tmp,\n    ngx_uint_t lower)\n{\n    size_t  hlen;\n\n    hlen = ngx_http_huff_encode(src, len, tmp, lower);\n\n    if (hlen > 0) {\n        *dst = NGX_HTTP_V2_ENCODE_HUFF;\n        dst = ngx_http_v2_write_int(dst, ngx_http_v2_prefix(7), hlen);\n        return ngx_cpymem(dst, tmp, hlen);\n    }\n\n    *dst = NGX_HTTP_V2_ENCODE_RAW;\n    dst = ngx_http_v2_write_int(dst, ngx_http_v2_prefix(7), len);\n\n    if (lower) {\n        ngx_strlow(dst, src, len);\n        return dst + len;\n    }\n\n    return ngx_cpymem(dst, src, len);\n}\n\n\nstatic u_char *\nngx_http_v2_write_int(u_char *pos, ngx_uint_t prefix, ngx_uint_t value)\n{\n    if (value < prefix) {\n        *pos++ |= value;\n        return pos;\n    }\n\n    *pos++ |= prefix;\n    value -= prefix;\n\n    while (value >= 128) {\n        *pos++ = value % 128 + 128;\n        value /= 128;\n    }\n\n    *pos++ = (u_char) value;\n\n    return pos;\n}\n"
  },
  {
    "path": "src/http/v2/ngx_http_v2_filter_module.c",
    "content": "\n/*\n * Copyright (C) Nginx, Inc.\n * Copyright (C) Valentin V. Bartenev\n * Copyright (C) Ruslan Ermilov\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n#include <nginx.h>\n#include <ngx_http_v2_module.h>\n\n\n/*\n * This returns precise number of octets for values in range 0..253\n * and estimate number for the rest, but not smaller than required.\n */\n\n#define ngx_http_v2_integer_octets(v)  (1 + (v) / 127)\n\n#define ngx_http_v2_literal_size(h)                                           \\\n    (ngx_http_v2_integer_octets(sizeof(h) - 1) + sizeof(h) - 1)\n\n\n#define NGX_HTTP_V2_NO_TRAILERS           (ngx_http_v2_out_frame_t *) -1\n\n\ntypedef struct {\n    ngx_str_t      name;\n    u_char         index;\n    ngx_uint_t     offset;\n} ngx_http_v2_push_header_t;\n\n\nstatic ngx_http_v2_push_header_t  ngx_http_v2_push_headers[] = {\n    { ngx_string(\":authority\"), NGX_HTTP_V2_AUTHORITY_INDEX,\n      offsetof(ngx_http_headers_in_t, host) },\n\n    { ngx_string(\"accept-encoding\"), NGX_HTTP_V2_ACCEPT_ENCODING_INDEX,\n      offsetof(ngx_http_headers_in_t, accept_encoding) },\n\n    { ngx_string(\"accept-language\"), NGX_HTTP_V2_ACCEPT_LANGUAGE_INDEX,\n      offsetof(ngx_http_headers_in_t, accept_language) },\n\n    { ngx_string(\"user-agent\"), NGX_HTTP_V2_USER_AGENT_INDEX,\n      offsetof(ngx_http_headers_in_t, user_agent) },\n};\n\n#define NGX_HTTP_V2_PUSH_HEADERS                                              \\\n    (sizeof(ngx_http_v2_push_headers) / sizeof(ngx_http_v2_push_header_t))\n\n\nstatic ngx_int_t ngx_http_v2_push_resources(ngx_http_request_t *r);\nstatic ngx_int_t ngx_http_v2_push_resource(ngx_http_request_t *r,\n    ngx_str_t *path, ngx_str_t *binary);\n\nstatic ngx_http_v2_out_frame_t *ngx_http_v2_create_headers_frame(\n    ngx_http_request_t *r, u_char *pos, u_char *end, ngx_uint_t fin);\nstatic ngx_http_v2_out_frame_t *ngx_http_v2_create_push_frame(\n    ngx_http_request_t *r, u_char *pos, u_char *end);\nstatic ngx_http_v2_out_frame_t *ngx_http_v2_create_trailers_frame(\n    ngx_http_request_t *r);\n\nstatic ngx_chain_t *ngx_http_v2_send_chain(ngx_connection_t *fc,\n    ngx_chain_t *in, off_t limit);\n\nstatic ngx_chain_t *ngx_http_v2_filter_get_shadow(\n    ngx_http_v2_stream_t *stream, ngx_buf_t *buf, off_t offset, off_t size);\nstatic ngx_http_v2_out_frame_t *ngx_http_v2_filter_get_data_frame(\n    ngx_http_v2_stream_t *stream, size_t len, ngx_chain_t *first,\n    ngx_chain_t *last);\n\nstatic ngx_inline ngx_int_t ngx_http_v2_flow_control(\n    ngx_http_v2_connection_t *h2c, ngx_http_v2_stream_t *stream);\nstatic void ngx_http_v2_waiting_queue(ngx_http_v2_connection_t *h2c,\n    ngx_http_v2_stream_t *stream);\n\nstatic ngx_inline ngx_int_t ngx_http_v2_filter_send(\n    ngx_connection_t *fc, ngx_http_v2_stream_t *stream);\n\nstatic ngx_int_t ngx_http_v2_headers_frame_handler(\n    ngx_http_v2_connection_t *h2c, ngx_http_v2_out_frame_t *frame);\nstatic ngx_int_t ngx_http_v2_push_frame_handler(\n    ngx_http_v2_connection_t *h2c, ngx_http_v2_out_frame_t *frame);\nstatic ngx_int_t ngx_http_v2_data_frame_handler(\n    ngx_http_v2_connection_t *h2c, ngx_http_v2_out_frame_t *frame);\nstatic ngx_inline void ngx_http_v2_handle_frame(\n    ngx_http_v2_stream_t *stream, ngx_http_v2_out_frame_t *frame);\nstatic ngx_inline void ngx_http_v2_handle_stream(\n    ngx_http_v2_connection_t *h2c, ngx_http_v2_stream_t *stream);\n\nstatic void ngx_http_v2_filter_cleanup(void *data);\n\nstatic ngx_int_t ngx_http_v2_filter_init(ngx_conf_t *cf);\n\n\nstatic ngx_http_module_t  ngx_http_v2_filter_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    ngx_http_v2_filter_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\nngx_module_t  ngx_http_v2_filter_module = {\n    NGX_MODULE_V1,\n    &ngx_http_v2_filter_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_http_output_header_filter_pt  ngx_http_next_header_filter;\n\n\nstatic ngx_int_t\nngx_http_v2_header_filter(ngx_http_request_t *r)\n{\n    u_char                     status, *pos, *start, *p, *tmp;\n    size_t                     len, tmp_len;\n    ngx_str_t                  host, location;\n    ngx_uint_t                 i, port, fin;\n    ngx_list_part_t           *part;\n    ngx_table_elt_t           *header;\n    ngx_connection_t          *fc;\n    ngx_http_cleanup_t        *cln;\n    ngx_http_v2_stream_t      *stream;\n    ngx_http_v2_out_frame_t   *frame;\n    ngx_http_v2_connection_t  *h2c;\n    ngx_http_core_loc_conf_t  *clcf;\n    ngx_http_core_srv_conf_t  *cscf;\n    u_char                     addr[NGX_SOCKADDR_STRLEN];\n\n#if (T_NGX_SERVER_INFO)\n    static const u_char nginx[6] = \"\\x85\\xde\\x5a\\xa6\\x35\\x45\";\n#else\n    static const u_char nginx[5] = \"\\x84\\xaa\\x63\\x55\\xe7\";\n#endif\n#if (NGX_HTTP_GZIP)\n    static const u_char accept_encoding[12] =\n        \"\\x8b\\x84\\x84\\x2d\\x69\\x5b\\x05\\x44\\x3c\\x86\\xaa\\x6f\";\n#endif\n\n#if (T_NGX_SERVER_INFO)\n    static size_t nginx_ver_len = ngx_http_v2_literal_size(TENGINE_VER);\n    static u_char nginx_ver[ngx_http_v2_literal_size(TENGINE_VER)];\n\n    static size_t nginx_ver_build_len =\n                                  ngx_http_v2_literal_size(TENGINE_VER_BUILD);\n    static u_char nginx_ver_build[ngx_http_v2_literal_size(TENGINE_VER_BUILD)];\n#else\n    static size_t nginx_ver_len = ngx_http_v2_literal_size(NGINX_VER);\n    static u_char nginx_ver[ngx_http_v2_literal_size(NGINX_VER)];\n\n    static size_t nginx_ver_build_len =\n                                  ngx_http_v2_literal_size(NGINX_VER_BUILD);\n    static u_char nginx_ver_build[ngx_http_v2_literal_size(NGINX_VER_BUILD)];\n#endif\n\n    stream = r->stream;\n\n    if (!stream) {\n        return ngx_http_next_header_filter(r);\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http2 header filter\");\n\n    if (r->header_sent) {\n        return NGX_OK;\n    }\n\n    r->header_sent = 1;\n\n    if (r != r->main) {\n        return NGX_OK;\n    }\n\n    fc = r->connection;\n\n    if (fc->error) {\n        return NGX_ERROR;\n    }\n\n    if (r->method == NGX_HTTP_HEAD) {\n        r->header_only = 1;\n    }\n\n    switch (r->headers_out.status) {\n\n    case NGX_HTTP_OK:\n        status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_200_INDEX);\n        break;\n\n    case NGX_HTTP_NO_CONTENT:\n        r->header_only = 1;\n\n        ngx_str_null(&r->headers_out.content_type);\n\n        r->headers_out.content_length = NULL;\n        r->headers_out.content_length_n = -1;\n\n        r->headers_out.last_modified_time = -1;\n        r->headers_out.last_modified = NULL;\n\n        status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_204_INDEX);\n        break;\n\n    case NGX_HTTP_PARTIAL_CONTENT:\n        status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_206_INDEX);\n        break;\n\n    case NGX_HTTP_NOT_MODIFIED:\n        r->header_only = 1;\n        status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_304_INDEX);\n        break;\n\n    default:\n        r->headers_out.last_modified_time = -1;\n        r->headers_out.last_modified = NULL;\n\n        switch (r->headers_out.status) {\n\n        case NGX_HTTP_BAD_REQUEST:\n            status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_400_INDEX);\n            break;\n\n        case NGX_HTTP_NOT_FOUND:\n            status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_404_INDEX);\n            break;\n\n        case NGX_HTTP_INTERNAL_SERVER_ERROR:\n            status = ngx_http_v2_indexed(NGX_HTTP_V2_STATUS_500_INDEX);\n            break;\n\n        default:\n            status = 0;\n        }\n    }\n\n    h2c = stream->connection;\n\n    if (!h2c->push_disabled && !h2c->goaway\n        && stream->node->id % 2 == 1\n        && r->method != NGX_HTTP_HEAD)\n    {\n        if (ngx_http_v2_push_resources(r) != NGX_OK) {\n            return NGX_ERROR;\n        }\n    }\n\n    len = h2c->table_update ? 1 : 0;\n\n    len += status ? 1 : 1 + ngx_http_v2_literal_size(\"418\");\n\n    clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);\n\n    if (r->headers_out.server == NULL) {\n\n        if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) {\n            len += 1 + nginx_ver_len;\n\n        } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) {\n            len += 1 + nginx_ver_build_len;\n\n        } else {\n            len += 1 + sizeof(nginx);\n        }\n    }\n\n    if (r->headers_out.date == NULL) {\n        len += 1 + ngx_http_v2_literal_size(\"Wed, 31 Dec 1986 18:00:00 GMT\");\n    }\n\n    if (r->headers_out.content_type.len) {\n        len += 1 + NGX_HTTP_V2_INT_OCTETS + r->headers_out.content_type.len;\n\n        if (r->headers_out.content_type_len == r->headers_out.content_type.len\n            && r->headers_out.charset.len)\n        {\n            len += sizeof(\"; charset=\") - 1 + r->headers_out.charset.len;\n        }\n    }\n\n    if (r->headers_out.content_length == NULL\n        && r->headers_out.content_length_n >= 0)\n    {\n        len += 1 + ngx_http_v2_integer_octets(NGX_OFF_T_LEN) + NGX_OFF_T_LEN;\n    }\n\n    if (r->headers_out.last_modified == NULL\n        && r->headers_out.last_modified_time != -1)\n    {\n        len += 1 + ngx_http_v2_literal_size(\"Wed, 31 Dec 1986 18:00:00 GMT\");\n    }\n\n    if (r->headers_out.location && r->headers_out.location->value.len) {\n\n        if (r->headers_out.location->value.data[0] == '/'\n            && clcf->absolute_redirect)\n        {\n            if (clcf->server_name_in_redirect) {\n                cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);\n                host = cscf->server_name;\n\n            } else if (r->headers_in.server.len) {\n                host = r->headers_in.server;\n\n            } else {\n                host.len = NGX_SOCKADDR_STRLEN;\n                host.data = addr;\n\n                if (ngx_connection_local_sockaddr(fc, &host, 0) != NGX_OK) {\n                    return NGX_ERROR;\n                }\n            }\n\n            port = ngx_inet_get_port(fc->local_sockaddr);\n\n            location.len = sizeof(\"https://\") - 1 + host.len\n                           + r->headers_out.location->value.len;\n\n            if (clcf->port_in_redirect) {\n\n#if (NGX_HTTP_SSL)\n                if (fc->ssl)\n                    port = (port == 443) ? 0 : port;\n                else\n#endif\n                    port = (port == 80) ? 0 : port;\n\n            } else {\n                port = 0;\n            }\n\n            if (port) {\n                location.len += sizeof(\":65535\") - 1;\n            }\n\n            location.data = ngx_pnalloc(r->pool, location.len);\n            if (location.data == NULL) {\n                return NGX_ERROR;\n            }\n\n            p = ngx_cpymem(location.data, \"http\", sizeof(\"http\") - 1);\n\n#if (NGX_HTTP_SSL)\n            if (fc->ssl) {\n                *p++ = 's';\n            }\n#endif\n\n            *p++ = ':'; *p++ = '/'; *p++ = '/';\n            p = ngx_cpymem(p, host.data, host.len);\n\n            if (port) {\n                p = ngx_sprintf(p, \":%ui\", port);\n            }\n\n            p = ngx_cpymem(p, r->headers_out.location->value.data,\n                              r->headers_out.location->value.len);\n\n            /* update r->headers_out.location->value for possible logging */\n\n            r->headers_out.location->value.len = p - location.data;\n            r->headers_out.location->value.data = location.data;\n            ngx_str_set(&r->headers_out.location->key, \"Location\");\n        }\n\n        r->headers_out.location->hash = 0;\n\n        len += 1 + NGX_HTTP_V2_INT_OCTETS + r->headers_out.location->value.len;\n    }\n\n    tmp_len = len;\n\n#if (NGX_HTTP_GZIP)\n    if (r->gzip_vary) {\n        if (clcf->gzip_vary) {\n            len += 1 + sizeof(accept_encoding);\n\n        } else {\n            r->gzip_vary = 0;\n        }\n    }\n#endif\n\n    part = &r->headers_out.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 (header[i].hash == 0) {\n            continue;\n        }\n\n        if (header[i].key.len > NGX_HTTP_V2_MAX_FIELD) {\n            ngx_log_error(NGX_LOG_CRIT, fc->log, 0,\n                          \"too long response header name: \\\"%V\\\"\",\n                          &header[i].key);\n            return NGX_ERROR;\n        }\n\n        if (header[i].value.len > NGX_HTTP_V2_MAX_FIELD) {\n            ngx_log_error(NGX_LOG_CRIT, fc->log, 0,\n                          \"too long response header value: \\\"%V: %V\\\"\",\n                          &header[i].key, &header[i].value);\n            return NGX_ERROR;\n        }\n\n        len += 1 + NGX_HTTP_V2_INT_OCTETS + header[i].key.len\n                 + NGX_HTTP_V2_INT_OCTETS + header[i].value.len;\n\n        if (header[i].key.len > tmp_len) {\n            tmp_len = header[i].key.len;\n        }\n\n        if (header[i].value.len > tmp_len) {\n            tmp_len = header[i].value.len;\n        }\n    }\n\n    tmp = ngx_palloc(r->pool, tmp_len);\n    pos = ngx_pnalloc(r->pool, len);\n\n    if (pos == NULL || tmp == NULL) {\n        return NGX_ERROR;\n    }\n\n    start = pos;\n\n    if (h2c->table_update) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n                       \"http2 table size update: 0\");\n        *pos++ = (1 << 5) | 0;\n        h2c->table_update = 0;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n                   \"http2 output header: \\\":status: %03ui\\\"\",\n                   r->headers_out.status);\n\n    if (status) {\n        *pos++ = status;\n\n    } else {\n        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_STATUS_INDEX);\n        *pos++ = NGX_HTTP_V2_ENCODE_RAW | 3;\n        pos = ngx_sprintf(pos, \"%03ui\", r->headers_out.status);\n    }\n\n    if (r->headers_out.server == NULL) {\n\n        if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) {\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n                           \"http2 output header: \\\"server: %s\\\"\",\n                           NGINX_VER);\n\n        } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) {\n            ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n                           \"http2 output header: \\\"server: %s\\\"\",\n                           NGINX_VER_BUILD);\n\n        } else {\n            ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n                           \"http2 output header: \\\"server: nginx\\\"\");\n        }\n\n        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_SERVER_INDEX);\n\n        if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) {\n            if (nginx_ver[0] == '\\0') {\n#if (T_NGX_SERVER_INFO)\n                p = ngx_http_v2_write_value(nginx_ver, (u_char *) TENGINE_VER,\n                                            sizeof(TENGINE_VER) - 1, tmp);\n#else\n                p = ngx_http_v2_write_value(nginx_ver, (u_char *) NGINX_VER,\n                                            sizeof(NGINX_VER) - 1, tmp);\n#endif\n                nginx_ver_len = p - nginx_ver;\n            }\n\n            pos = ngx_cpymem(pos, nginx_ver, nginx_ver_len);\n\n        } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) {\n            if (nginx_ver_build[0] == '\\0') {\n#if (T_NGX_SERVER_INFO)\n                p = ngx_http_v2_write_value(nginx_ver_build,\n                                            (u_char *) TENGINE_VER_BUILD,\n                                            sizeof(TENGINE_VER_BUILD) - 1, tmp);\n#else\n                p = ngx_http_v2_write_value(nginx_ver_build,\n                                            (u_char *) NGINX_VER_BUILD,\n                                            sizeof(NGINX_VER_BUILD) - 1, tmp);\n#endif\n                nginx_ver_build_len = p - nginx_ver_build;\n            }\n\n            pos = ngx_cpymem(pos, nginx_ver_build, nginx_ver_build_len);\n\n        } else {\n            pos = ngx_cpymem(pos, nginx, sizeof(nginx));\n        }\n    }\n\n    if (r->headers_out.date == NULL) {\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n                       \"http2 output header: \\\"date: %V\\\"\",\n                       &ngx_cached_http_time);\n\n        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_DATE_INDEX);\n        pos = ngx_http_v2_write_value(pos, ngx_cached_http_time.data,\n                                      ngx_cached_http_time.len, tmp);\n    }\n\n    if (r->headers_out.content_type.len) {\n        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_CONTENT_TYPE_INDEX);\n\n        if (r->headers_out.content_type_len == r->headers_out.content_type.len\n            && r->headers_out.charset.len)\n        {\n            len = r->headers_out.content_type.len + sizeof(\"; charset=\") - 1\n                  + r->headers_out.charset.len;\n\n            p = ngx_pnalloc(r->pool, len);\n            if (p == NULL) {\n                return NGX_ERROR;\n            }\n\n            p = ngx_cpymem(p, r->headers_out.content_type.data,\n                           r->headers_out.content_type.len);\n\n            p = ngx_cpymem(p, \"; charset=\", sizeof(\"; charset=\") - 1);\n\n            p = ngx_cpymem(p, r->headers_out.charset.data,\n                           r->headers_out.charset.len);\n\n            /* updated r->headers_out.content_type is also needed for logging */\n\n            r->headers_out.content_type.len = len;\n            r->headers_out.content_type.data = p - len;\n        }\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n                       \"http2 output header: \\\"content-type: %V\\\"\",\n                       &r->headers_out.content_type);\n\n        pos = ngx_http_v2_write_value(pos, r->headers_out.content_type.data,\n                                      r->headers_out.content_type.len, tmp);\n    }\n\n    if (r->headers_out.content_length == NULL\n        && r->headers_out.content_length_n >= 0)\n    {\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n                       \"http2 output header: \\\"content-length: %O\\\"\",\n                       r->headers_out.content_length_n);\n\n        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_CONTENT_LENGTH_INDEX);\n\n        p = pos;\n        pos = ngx_sprintf(pos + 1, \"%O\", r->headers_out.content_length_n);\n        *p = NGX_HTTP_V2_ENCODE_RAW | (u_char) (pos - p - 1);\n    }\n\n    if (r->headers_out.last_modified == NULL\n        && r->headers_out.last_modified_time != -1)\n    {\n        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_LAST_MODIFIED_INDEX);\n\n        ngx_http_time(pos, r->headers_out.last_modified_time);\n        len = sizeof(\"Wed, 31 Dec 1986 18:00:00 GMT\") - 1;\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n                       \"http2 output header: \\\"last-modified: %*s\\\"\",\n                       len, pos);\n\n        /*\n         * Date will always be encoded using huffman in the temporary buffer,\n         * so it's safe here to use src and dst pointing to the same address.\n         */\n        pos = ngx_http_v2_write_value(pos, pos, len, tmp);\n    }\n\n    if (r->headers_out.location && r->headers_out.location->value.len) {\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n                       \"http2 output header: \\\"location: %V\\\"\",\n                       &r->headers_out.location->value);\n\n        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_LOCATION_INDEX);\n        pos = ngx_http_v2_write_value(pos, r->headers_out.location->value.data,\n                                      r->headers_out.location->value.len, tmp);\n    }\n\n#if (NGX_HTTP_GZIP)\n    if (r->gzip_vary) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n                       \"http2 output header: \\\"vary: Accept-Encoding\\\"\");\n\n        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_VARY_INDEX);\n        pos = ngx_cpymem(pos, accept_encoding, sizeof(accept_encoding));\n    }\n#endif\n\n    part = &r->headers_out.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 (header[i].hash == 0) {\n            continue;\n        }\n\n#if (NGX_DEBUG)\n        if (fc->log->log_level & NGX_LOG_DEBUG_HTTP) {\n            ngx_strlow(tmp, header[i].key.data, header[i].key.len);\n\n            ngx_log_debug3(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n                           \"http2 output header: \\\"%*s: %V\\\"\",\n                           header[i].key.len, tmp, &header[i].value);\n        }\n#endif\n\n        *pos++ = 0;\n\n        pos = ngx_http_v2_write_name(pos, header[i].key.data,\n                                     header[i].key.len, tmp);\n\n        pos = ngx_http_v2_write_value(pos, header[i].value.data,\n                                      header[i].value.len, tmp);\n    }\n\n    fin = r->header_only\n          || (r->headers_out.content_length_n == 0 && !r->expect_trailers);\n\n    frame = ngx_http_v2_create_headers_frame(r, start, pos, fin);\n    if (frame == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_http_v2_queue_blocked_frame(h2c, frame);\n\n    stream->queued++;\n\n    cln = ngx_http_cleanup_add(r, 0);\n    if (cln == NULL) {\n        return NGX_ERROR;\n    }\n\n    cln->handler = ngx_http_v2_filter_cleanup;\n    cln->data = stream;\n\n    fc->send_chain = ngx_http_v2_send_chain;\n    fc->need_last_buf = 1;\n    fc->need_flush_buf = 1;\n\n    return ngx_http_v2_filter_send(fc, stream);\n}\n\n\nstatic ngx_int_t\nngx_http_v2_push_resources(ngx_http_request_t *r)\n{\n    u_char                    *start, *end, *last;\n    ngx_int_t                  rc;\n    ngx_str_t                  path;\n    ngx_uint_t                 i, push;\n    ngx_table_elt_t           *h;\n    ngx_http_v2_loc_conf_t    *h2lcf;\n    ngx_http_complex_value_t  *pushes;\n    ngx_str_t                  binary[NGX_HTTP_V2_PUSH_HEADERS];\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                   \"http2 push resources\");\n\n    ngx_memzero(binary, NGX_HTTP_V2_PUSH_HEADERS * sizeof(ngx_str_t));\n\n    h2lcf = ngx_http_get_module_loc_conf(r, ngx_http_v2_module);\n\n    if (h2lcf->pushes) {\n        pushes = h2lcf->pushes->elts;\n\n        for (i = 0; i < h2lcf->pushes->nelts; i++) {\n\n            if (ngx_http_complex_value(r, &pushes[i], &path) != NGX_OK) {\n                return NGX_ERROR;\n            }\n\n            if (path.len == 0) {\n                continue;\n            }\n\n            if (path.len == 3 && ngx_strncmp(path.data, \"off\", 3) == 0) {\n                continue;\n            }\n\n            rc = ngx_http_v2_push_resource(r, &path, binary);\n\n            if (rc == NGX_ERROR) {\n                return NGX_ERROR;\n            }\n\n            if (rc == NGX_ABORT) {\n                return NGX_OK;\n            }\n\n            /* NGX_OK, NGX_DECLINED */\n        }\n    }\n\n    if (!h2lcf->push_preload) {\n        return NGX_OK;\n    }\n\n    for (h = r->headers_out.link; h; h = h->next) {\n\n        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"http2 parse link: \\\"%V\\\"\", &h->value);\n\n        start = h->value.data;\n        end = h->value.data + h->value.len;\n\n    next_link:\n\n        while (start < end && *start == ' ') { start++; }\n\n        if (start == end || *start++ != '<') {\n            continue;\n        }\n\n        while (start < end && *start == ' ') { start++; }\n\n        for (last = start; last < end && *last != '>'; last++) {\n            /* void */\n        }\n\n        if (last == start || last == end) {\n            continue;\n        }\n\n        path.len = last - start;\n        path.data = start;\n\n        start = last + 1;\n\n        while (start < end && *start == ' ') { start++; }\n\n        if (start == end) {\n            continue;\n        }\n\n        if (*start == ',') {\n            start++;\n            goto next_link;\n        }\n\n        if (*start++ != ';') {\n            continue;\n        }\n\n        last = ngx_strlchr(start, end, ',');\n\n        if (last == NULL) {\n            last = end;\n        }\n\n        push = 0;\n\n        for ( ;; ) {\n\n            while (start < last && *start == ' ') { start++; }\n\n            if (last - start >= 6\n                && ngx_strncasecmp(start, (u_char *) \"nopush\", 6) == 0)\n            {\n                start += 6;\n\n                if (start == last || *start == ' ' || *start == ';') {\n                    push = 0;\n                    break;\n                }\n\n                goto next_param;\n            }\n\n            if (last - start >= 11\n                && ngx_strncasecmp(start, (u_char *) \"rel=preload\", 11) == 0)\n            {\n                start += 11;\n\n                if (start == last || *start == ' ' || *start == ';') {\n                    push = 1;\n                }\n\n                goto next_param;\n            }\n\n            if (last - start >= 4\n                && ngx_strncasecmp(start, (u_char *) \"rel=\", 4) == 0)\n            {\n                start += 4;\n\n                while (start < last && *start == ' ') { start++; }\n\n                if (start == last || *start++ != '\"') {\n                    goto next_param;\n                }\n\n                for ( ;; ) {\n\n                    while (start < last && *start == ' ') { start++; }\n\n                    if (last - start >= 7\n                        && ngx_strncasecmp(start, (u_char *) \"preload\", 7) == 0)\n                    {\n                        start += 7;\n\n                        if (start < last && (*start == ' ' || *start == '\"')) {\n                            push = 1;\n                            break;\n                        }\n                    }\n\n                    while (start < last && *start != ' ' && *start != '\"') {\n                        start++;\n                    }\n\n                    if (start == last) {\n                        break;\n                    }\n\n                    if (*start == '\"') {\n                        break;\n                    }\n\n                    start++;\n                }\n            }\n\n        next_param:\n\n            start = ngx_strlchr(start, last, ';');\n\n            if (start == NULL) {\n                break;\n            }\n\n            start++;\n        }\n\n        if (push) {\n            while (path.len && path.data[path.len - 1] == ' ') {\n                path.len--;\n            }\n        }\n\n        if (push && path.len\n            && !(path.len > 1 && path.data[0] == '/' && path.data[1] == '/'))\n        {\n            rc = ngx_http_v2_push_resource(r, &path, binary);\n\n            if (rc == NGX_ERROR) {\n                return NGX_ERROR;\n            }\n\n            if (rc == NGX_ABORT) {\n                return NGX_OK;\n            }\n\n            /* NGX_OK, NGX_DECLINED */\n        }\n\n        if (last < end) {\n            start = last + 1;\n            goto next_link;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_v2_push_resource(ngx_http_request_t *r, ngx_str_t *path,\n    ngx_str_t *binary)\n{\n    u_char                      *start, *pos, *tmp;\n    size_t                       len;\n    ngx_str_t                   *value;\n    ngx_uint_t                   i;\n    ngx_table_elt_t            **h;\n    ngx_connection_t            *fc;\n    ngx_http_v2_stream_t        *stream;\n    ngx_http_v2_out_frame_t     *frame;\n    ngx_http_v2_connection_t    *h2c;\n    ngx_http_v2_push_header_t   *ph;\n\n    fc = r->connection;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0, \"http2 push resource\");\n\n    stream = r->stream;\n    h2c = stream->connection;\n\n    if (!ngx_path_separator(path->data[0])) {\n        ngx_log_error(NGX_LOG_WARN, fc->log, 0,\n                      \"non-absolute path \\\"%V\\\" not pushed\", path);\n        return NGX_DECLINED;\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n                   \"http2 pushing:%ui limit:%ui\",\n                   h2c->pushing, h2c->concurrent_pushes);\n\n    if (h2c->pushing >= h2c->concurrent_pushes) {\n        return NGX_ABORT;\n    }\n\n    if (h2c->last_push == 0x7ffffffe) {\n        return NGX_ABORT;\n    }\n\n    if (path->len > NGX_HTTP_V2_MAX_FIELD) {\n        return NGX_DECLINED;\n    }\n\n    if (r->headers_in.host == NULL) {\n        return NGX_ABORT;\n    }\n\n    ph = ngx_http_v2_push_headers;\n\n    len = ngx_max(r->schema.len, path->len);\n\n    if (binary[0].len) {\n        tmp = ngx_palloc(r->pool, len);\n        if (tmp == NULL) {\n            return NGX_ERROR;\n        }\n\n    } else {\n        for (i = 0; i < NGX_HTTP_V2_PUSH_HEADERS; i++) {\n            h = (ngx_table_elt_t **) ((char *) &r->headers_in + ph[i].offset);\n\n            if (*h) {\n                len = ngx_max(len, (*h)->value.len);\n            }\n        }\n\n        tmp = ngx_palloc(r->pool, len);\n        if (tmp == NULL) {\n            return NGX_ERROR;\n        }\n\n        for (i = 0; i < NGX_HTTP_V2_PUSH_HEADERS; i++) {\n            h = (ngx_table_elt_t **) ((char *) &r->headers_in + ph[i].offset);\n\n            if (*h == NULL) {\n                continue;\n            }\n\n            value = &(*h)->value;\n\n            len = 1 + NGX_HTTP_V2_INT_OCTETS + value->len;\n\n            pos = ngx_pnalloc(r->pool, len);\n            if (pos == NULL) {\n                return NGX_ERROR;\n            }\n\n            binary[i].data = pos;\n\n            *pos++ = ngx_http_v2_inc_indexed(ph[i].index);\n            pos = ngx_http_v2_write_value(pos, value->data, value->len, tmp);\n\n            binary[i].len = pos - binary[i].data;\n        }\n    }\n\n    len = (h2c->table_update ? 1 : 0)\n          + 1\n          + 1 + NGX_HTTP_V2_INT_OCTETS + path->len\n          + 1 + NGX_HTTP_V2_INT_OCTETS + r->schema.len;\n\n    for (i = 0; i < NGX_HTTP_V2_PUSH_HEADERS; i++) {\n        len += binary[i].len;\n    }\n\n    pos = ngx_pnalloc(r->pool, len);\n    if (pos == NULL) {\n        return NGX_ERROR;\n    }\n\n    start = pos;\n\n    if (h2c->table_update) {\n        ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n                       \"http2 table size update: 0\");\n        *pos++ = (1 << 5) | 0;\n        h2c->table_update = 0;\n    }\n\n    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n                   \"http2 push header: \\\":method: GET\\\"\");\n\n    *pos++ = ngx_http_v2_indexed(NGX_HTTP_V2_METHOD_GET_INDEX);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n                   \"http2 push header: \\\":path: %V\\\"\", path);\n\n    *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_PATH_INDEX);\n    pos = ngx_http_v2_write_value(pos, path->data, path->len, tmp);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n                   \"http2 push header: \\\":scheme: %V\\\"\", &r->schema);\n\n    if (r->schema.len == 5 && ngx_strncmp(r->schema.data, \"https\", 5) == 0) {\n        *pos++ = ngx_http_v2_indexed(NGX_HTTP_V2_SCHEME_HTTPS_INDEX);\n\n    } else if (r->schema.len == 4\n               && ngx_strncmp(r->schema.data, \"http\", 4) == 0)\n    {\n        *pos++ = ngx_http_v2_indexed(NGX_HTTP_V2_SCHEME_HTTP_INDEX);\n\n    } else {\n        *pos++ = ngx_http_v2_inc_indexed(NGX_HTTP_V2_SCHEME_HTTP_INDEX);\n        pos = ngx_http_v2_write_value(pos, r->schema.data, r->schema.len, tmp);\n    }\n\n    for (i = 0; i < NGX_HTTP_V2_PUSH_HEADERS; i++) {\n        h = (ngx_table_elt_t **) ((char *) &r->headers_in + ph[i].offset);\n\n        if (*h == NULL) {\n            continue;\n        }\n\n        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n                       \"http2 push header: \\\"%V: %V\\\"\",\n                       &ph[i].name, &(*h)->value);\n\n        pos = ngx_cpymem(pos, binary[i].data, binary[i].len);\n    }\n\n    frame = ngx_http_v2_create_push_frame(r, start, pos);\n    if (frame == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_http_v2_queue_blocked_frame(h2c, frame);\n\n    stream->queued++;\n\n    stream = ngx_http_v2_push_stream(stream, path);\n\n    if (stream) {\n        stream->request->request_length = pos - start;\n        return NGX_OK;\n    }\n\n    return NGX_ERROR;\n}\n\n\nstatic ngx_http_v2_out_frame_t *\nngx_http_v2_create_headers_frame(ngx_http_request_t *r, u_char *pos,\n    u_char *end, ngx_uint_t fin)\n{\n    u_char                    type, flags;\n    size_t                    rest, frame_size;\n    ngx_buf_t                *b;\n    ngx_chain_t              *cl, **ll;\n    ngx_http_v2_stream_t     *stream;\n    ngx_http_v2_out_frame_t  *frame;\n\n    stream = r->stream;\n    rest = end - pos;\n\n    frame = ngx_palloc(r->pool, sizeof(ngx_http_v2_out_frame_t));\n    if (frame == NULL) {\n        return NULL;\n    }\n\n    frame->handler = ngx_http_v2_headers_frame_handler;\n    frame->stream = stream;\n    frame->length = rest;\n    frame->blocked = 1;\n    frame->fin = fin;\n\n    ll = &frame->first;\n\n    type = NGX_HTTP_V2_HEADERS_FRAME;\n    flags = fin ? NGX_HTTP_V2_END_STREAM_FLAG : NGX_HTTP_V2_NO_FLAG;\n    frame_size = stream->connection->frame_size;\n\n    for ( ;; ) {\n        if (rest <= frame_size) {\n            frame_size = rest;\n            flags |= NGX_HTTP_V2_END_HEADERS_FLAG;\n        }\n\n        b = ngx_create_temp_buf(r->pool, NGX_HTTP_V2_FRAME_HEADER_SIZE);\n        if (b == NULL) {\n            return NULL;\n        }\n\n        b->last = ngx_http_v2_write_len_and_type(b->last, frame_size, type);\n        *b->last++ = flags;\n        b->last = ngx_http_v2_write_sid(b->last, stream->node->id);\n\n        b->tag = (ngx_buf_tag_t) &ngx_http_v2_module;\n\n        cl = ngx_alloc_chain_link(r->pool);\n        if (cl == NULL) {\n            return NULL;\n        }\n\n        cl->buf = b;\n\n        *ll = cl;\n        ll = &cl->next;\n\n        b = ngx_calloc_buf(r->pool);\n        if (b == NULL) {\n            return NULL;\n        }\n\n        b->pos = pos;\n\n        pos += frame_size;\n\n        b->last = pos;\n        b->start = b->pos;\n        b->end = b->last;\n        b->temporary = 1;\n\n        cl = ngx_alloc_chain_link(r->pool);\n        if (cl == NULL) {\n            return NULL;\n        }\n\n        cl->buf = b;\n\n        *ll = cl;\n        ll = &cl->next;\n\n        rest -= frame_size;\n\n        if (rest) {\n            frame->length += NGX_HTTP_V2_FRAME_HEADER_SIZE;\n\n            type = NGX_HTTP_V2_CONTINUATION_FRAME;\n            flags = NGX_HTTP_V2_NO_FLAG;\n            continue;\n        }\n\n        b->last_buf = fin;\n        cl->next = NULL;\n        frame->last = cl;\n\n        ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"http2:%ui create HEADERS frame %p: len:%uz fin:%ui\",\n                       stream->node->id, frame, frame->length, fin);\n\n        return frame;\n    }\n}\n\n\nstatic ngx_http_v2_out_frame_t *\nngx_http_v2_create_push_frame(ngx_http_request_t *r, u_char *pos, u_char *end)\n{\n    u_char                     type, flags;\n    size_t                     rest, frame_size, len;\n    ngx_buf_t                 *b;\n    ngx_chain_t               *cl, **ll;\n    ngx_http_v2_stream_t      *stream;\n    ngx_http_v2_out_frame_t   *frame;\n    ngx_http_v2_connection_t  *h2c;\n\n    stream = r->stream;\n    h2c = stream->connection;\n    rest = NGX_HTTP_V2_STREAM_ID_SIZE + (end - pos);\n\n    frame = ngx_palloc(r->pool, sizeof(ngx_http_v2_out_frame_t));\n    if (frame == NULL) {\n        return NULL;\n    }\n\n    frame->handler = ngx_http_v2_push_frame_handler;\n    frame->stream = stream;\n    frame->length = rest;\n    frame->blocked = 1;\n    frame->fin = 0;\n\n    ll = &frame->first;\n\n    type = NGX_HTTP_V2_PUSH_PROMISE_FRAME;\n    flags = NGX_HTTP_V2_NO_FLAG;\n    frame_size = h2c->frame_size;\n\n    for ( ;; ) {\n        if (rest <= frame_size) {\n            frame_size = rest;\n            flags |= NGX_HTTP_V2_END_HEADERS_FLAG;\n        }\n\n        b = ngx_create_temp_buf(r->pool,\n                                NGX_HTTP_V2_FRAME_HEADER_SIZE\n                                + ((type == NGX_HTTP_V2_PUSH_PROMISE_FRAME)\n                                   ? NGX_HTTP_V2_STREAM_ID_SIZE : 0));\n        if (b == NULL) {\n            return NULL;\n        }\n\n        b->last = ngx_http_v2_write_len_and_type(b->last, frame_size, type);\n        *b->last++ = flags;\n        b->last = ngx_http_v2_write_sid(b->last, stream->node->id);\n\n        b->tag = (ngx_buf_tag_t) &ngx_http_v2_module;\n\n        if (type == NGX_HTTP_V2_PUSH_PROMISE_FRAME) {\n            h2c->last_push += 2;\n\n            b->last = ngx_http_v2_write_sid(b->last, h2c->last_push);\n            len = frame_size - NGX_HTTP_V2_STREAM_ID_SIZE;\n\n        } else {\n            len = frame_size;\n        }\n\n        cl = ngx_alloc_chain_link(r->pool);\n        if (cl == NULL) {\n            return NULL;\n        }\n\n        cl->buf = b;\n\n        *ll = cl;\n        ll = &cl->next;\n\n        b = ngx_calloc_buf(r->pool);\n        if (b == NULL) {\n            return NULL;\n        }\n\n        b->pos = pos;\n\n        pos += len;\n\n        b->last = pos;\n        b->start = b->pos;\n        b->end = b->last;\n        b->temporary = 1;\n\n        cl = ngx_alloc_chain_link(r->pool);\n        if (cl == NULL) {\n            return NULL;\n        }\n\n        cl->buf = b;\n\n        *ll = cl;\n        ll = &cl->next;\n\n        rest -= frame_size;\n\n        if (rest) {\n            frame->length += NGX_HTTP_V2_FRAME_HEADER_SIZE;\n\n            type = NGX_HTTP_V2_CONTINUATION_FRAME;\n            continue;\n        }\n\n        cl->next = NULL;\n        frame->last = cl;\n\n        ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,\n                       \"http2:%ui create PUSH_PROMISE frame %p: \"\n                       \"sid:%ui len:%uz\",\n                       stream->node->id, frame, h2c->last_push,\n                       frame->length);\n\n        return frame;\n    }\n}\n\n\nstatic ngx_http_v2_out_frame_t *\nngx_http_v2_create_trailers_frame(ngx_http_request_t *r)\n{\n    u_char            *pos, *start, *tmp;\n    size_t             len, tmp_len;\n    ngx_uint_t         i;\n    ngx_list_part_t   *part;\n    ngx_table_elt_t   *header;\n    ngx_connection_t  *fc;\n\n    fc = r->connection;\n    len = 0;\n    tmp_len = 0;\n\n    part = &r->headers_out.trailers.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 (header[i].hash == 0) {\n            continue;\n        }\n\n        if (header[i].key.len > NGX_HTTP_V2_MAX_FIELD) {\n            ngx_log_error(NGX_LOG_CRIT, fc->log, 0,\n                          \"too long response trailer name: \\\"%V\\\"\",\n                          &header[i].key);\n            return NULL;\n        }\n\n        if (header[i].value.len > NGX_HTTP_V2_MAX_FIELD) {\n            ngx_log_error(NGX_LOG_CRIT, fc->log, 0,\n                          \"too long response trailer value: \\\"%V: %V\\\"\",\n                          &header[i].key, &header[i].value);\n            return NULL;\n        }\n\n        len += 1 + NGX_HTTP_V2_INT_OCTETS + header[i].key.len\n                 + NGX_HTTP_V2_INT_OCTETS + header[i].value.len;\n\n        if (header[i].key.len > tmp_len) {\n            tmp_len = header[i].key.len;\n        }\n\n        if (header[i].value.len > tmp_len) {\n            tmp_len = header[i].value.len;\n        }\n    }\n\n    if (len == 0) {\n        return NGX_HTTP_V2_NO_TRAILERS;\n    }\n\n    tmp = ngx_palloc(r->pool, tmp_len);\n    pos = ngx_pnalloc(r->pool, len);\n\n    if (pos == NULL || tmp == NULL) {\n        return NULL;\n    }\n\n    start = pos;\n\n    part = &r->headers_out.trailers.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 (header[i].hash == 0) {\n            continue;\n        }\n\n#if (NGX_DEBUG)\n        if (fc->log->log_level & NGX_LOG_DEBUG_HTTP) {\n            ngx_strlow(tmp, header[i].key.data, header[i].key.len);\n\n            ngx_log_debug3(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n                           \"http2 output trailer: \\\"%*s: %V\\\"\",\n                           header[i].key.len, tmp, &header[i].value);\n        }\n#endif\n\n        *pos++ = 0;\n\n        pos = ngx_http_v2_write_name(pos, header[i].key.data,\n                                     header[i].key.len, tmp);\n\n        pos = ngx_http_v2_write_value(pos, header[i].value.data,\n                                      header[i].value.len, tmp);\n    }\n\n    return ngx_http_v2_create_headers_frame(r, start, pos, 1);\n}\n\n\nstatic ngx_chain_t *\nngx_http_v2_send_chain(ngx_connection_t *fc, ngx_chain_t *in, off_t limit)\n{\n    off_t                      size, offset;\n    size_t                     rest, frame_size;\n    ngx_chain_t               *cl, *out, **ln;\n    ngx_http_request_t        *r;\n    ngx_http_v2_stream_t      *stream;\n    ngx_http_v2_loc_conf_t    *h2lcf;\n    ngx_http_v2_out_frame_t   *frame, *trailers;\n    ngx_http_v2_connection_t  *h2c;\n\n    r = fc->data;\n    stream = r->stream;\n\n#if (NGX_SUPPRESS_WARN)\n    size = 0;\n#endif\n\n    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, fc->log, 0,\n                   \"http2 send chain: %p\", in);\n\n    while (in) {\n        size = ngx_buf_size(in->buf);\n\n        if (size || in->buf->last_buf) {\n            break;\n        }\n\n        in = in->next;\n    }\n\n    if (in == NULL || stream->out_closed) {\n\n        if (size) {\n            ngx_log_error(NGX_LOG_ERR, fc->log, 0,\n                          \"output on closed stream\");\n            return NGX_CHAIN_ERROR;\n        }\n\n        if (ngx_http_v2_filter_send(fc, stream) == NGX_ERROR) {\n            return NGX_CHAIN_ERROR;\n        }\n\n        return NULL;\n    }\n\n    h2c = stream->connection;\n\n    if (size && ngx_http_v2_flow_control(h2c, stream) == NGX_DECLINED) {\n\n        if (ngx_http_v2_filter_send(fc, stream) == NGX_ERROR) {\n            return NGX_CHAIN_ERROR;\n        }\n\n        if (ngx_http_v2_flow_control(h2c, stream) == NGX_DECLINED) {\n            fc->write->active = 1;\n            fc->write->ready = 0;\n            return in;\n        }\n    }\n\n    if (in->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_filter_get_shadow) {\n        cl = ngx_alloc_chain_link(r->pool);\n        if (cl == NULL) {\n            return NGX_CHAIN_ERROR;\n        }\n\n        cl->buf = in->buf;\n        in->buf = cl->buf->shadow;\n\n        offset = ngx_buf_in_memory(in->buf)\n                 ? (cl->buf->pos - in->buf->pos)\n                 : (cl->buf->file_pos - in->buf->file_pos);\n\n        cl->next = stream->free_bufs;\n        stream->free_bufs = cl;\n\n    } else {\n        offset = 0;\n    }\n\n    if (limit == 0 || limit > (off_t) h2c->send_window) {\n        limit = h2c->send_window;\n    }\n\n    if (limit > stream->send_window) {\n        limit = (stream->send_window > 0) ? stream->send_window : 0;\n    }\n\n    h2lcf = ngx_http_get_module_loc_conf(r, ngx_http_v2_module);\n\n    frame_size = (h2lcf->chunk_size < h2c->frame_size)\n                 ? h2lcf->chunk_size : h2c->frame_size;\n\n    trailers = NGX_HTTP_V2_NO_TRAILERS;\n\n#if (NGX_SUPPRESS_WARN)\n    cl = NULL;\n#endif\n\n    for ( ;; ) {\n        if ((off_t) frame_size > limit) {\n            frame_size = (size_t) limit;\n        }\n\n        ln = &out;\n        rest = frame_size;\n\n        while ((off_t) rest >= size) {\n\n            if (offset) {\n                cl = ngx_http_v2_filter_get_shadow(stream, in->buf,\n                                                   offset, size);\n                if (cl == NULL) {\n                    return NGX_CHAIN_ERROR;\n                }\n\n                offset = 0;\n\n            } else {\n                cl = ngx_alloc_chain_link(r->pool);\n                if (cl == NULL) {\n                    return NGX_CHAIN_ERROR;\n                }\n\n                cl->buf = in->buf;\n            }\n\n            *ln = cl;\n            ln = &cl->next;\n\n            rest -= (size_t) size;\n            in = in->next;\n\n            if (in == NULL) {\n                frame_size -= rest;\n                rest = 0;\n                break;\n            }\n\n            size = ngx_buf_size(in->buf);\n        }\n\n        if (rest) {\n            cl = ngx_http_v2_filter_get_shadow(stream, in->buf, offset, rest);\n            if (cl == NULL) {\n                return NGX_CHAIN_ERROR;\n            }\n\n            cl->buf->flush = 0;\n            cl->buf->last_buf = 0;\n\n            *ln = cl;\n\n            offset += rest;\n            size -= rest;\n        }\n\n        if (cl->buf->last_buf) {\n            trailers = ngx_http_v2_create_trailers_frame(r);\n            if (trailers == NULL) {\n                return NGX_CHAIN_ERROR;\n            }\n\n            if (trailers != NGX_HTTP_V2_NO_TRAILERS) {\n                cl->buf->last_buf = 0;\n            }\n        }\n\n        if (frame_size || cl->buf->last_buf) {\n            frame = ngx_http_v2_filter_get_data_frame(stream, frame_size,\n                                                      out, cl);\n            if (frame == NULL) {\n                return NGX_CHAIN_ERROR;\n            }\n\n            ngx_http_v2_queue_frame(h2c, frame);\n\n            h2c->send_window -= frame_size;\n\n            stream->send_window -= frame_size;\n            stream->queued++;\n        }\n\n        if (in == NULL) {\n\n            if (trailers != NGX_HTTP_V2_NO_TRAILERS) {\n                ngx_http_v2_queue_frame(h2c, trailers);\n                stream->queued++;\n            }\n\n            break;\n        }\n\n        limit -= frame_size;\n\n        if (limit == 0) {\n            break;\n        }\n    }\n\n    if (offset) {\n        cl = ngx_http_v2_filter_get_shadow(stream, in->buf, offset, size);\n        if (cl == NULL) {\n            return NGX_CHAIN_ERROR;\n        }\n\n        in->buf = cl->buf;\n        ngx_free_chain(r->pool, cl);\n    }\n\n    if (ngx_http_v2_filter_send(fc, stream) == NGX_ERROR) {\n        return NGX_CHAIN_ERROR;\n    }\n\n    if (in && ngx_http_v2_flow_control(h2c, stream) == NGX_DECLINED) {\n        fc->write->active = 1;\n        fc->write->ready = 0;\n    }\n\n    return in;\n}\n\n\nstatic ngx_chain_t *\nngx_http_v2_filter_get_shadow(ngx_http_v2_stream_t *stream, ngx_buf_t *buf,\n    off_t offset, off_t size)\n{\n    ngx_buf_t    *chunk;\n    ngx_chain_t  *cl;\n\n    cl = ngx_chain_get_free_buf(stream->request->pool, &stream->free_bufs);\n    if (cl == NULL) {\n        return NULL;\n    }\n\n    chunk = cl->buf;\n\n    ngx_memcpy(chunk, buf, sizeof(ngx_buf_t));\n\n    chunk->tag = (ngx_buf_tag_t) &ngx_http_v2_filter_get_shadow;\n    chunk->shadow = buf;\n\n    if (ngx_buf_in_memory(chunk)) {\n        chunk->pos += offset;\n        chunk->last = chunk->pos + size;\n    }\n\n    if (chunk->in_file) {\n        chunk->file_pos += offset;\n        chunk->file_last = chunk->file_pos + size;\n    }\n\n    return cl;\n}\n\n\nstatic ngx_http_v2_out_frame_t *\nngx_http_v2_filter_get_data_frame(ngx_http_v2_stream_t *stream,\n    size_t len, ngx_chain_t *first, ngx_chain_t *last)\n{\n    u_char                     flags;\n    ngx_buf_t                 *buf;\n    ngx_chain_t               *cl;\n    ngx_http_v2_out_frame_t   *frame;\n    ngx_http_v2_connection_t  *h2c;\n\n    frame = stream->free_frames;\n    h2c = stream->connection;\n\n    if (frame) {\n        stream->free_frames = frame->next;\n\n    } else if (h2c->frames < 10000) {\n        frame = ngx_palloc(stream->request->pool,\n                           sizeof(ngx_http_v2_out_frame_t));\n        if (frame == NULL) {\n            return NULL;\n        }\n\n        stream->frames++;\n        h2c->frames++;\n\n    } else {\n        ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                      \"http2 flood detected\");\n\n        h2c->connection->error = 1;\n        return NULL;\n    }\n\n    flags = last->buf->last_buf ? NGX_HTTP_V2_END_STREAM_FLAG : 0;\n\n    ngx_log_debug4(NGX_LOG_DEBUG_HTTP, stream->request->connection->log, 0,\n                   \"http2:%ui create DATA frame %p: len:%uz flags:%ui\",\n                   stream->node->id, frame, len, (ngx_uint_t) flags);\n\n    cl = ngx_chain_get_free_buf(stream->request->pool,\n                                &stream->free_frame_headers);\n    if (cl == NULL) {\n        return NULL;\n    }\n\n    buf = cl->buf;\n\n    if (buf->start == NULL) {\n        buf->start = ngx_palloc(stream->request->pool,\n                                NGX_HTTP_V2_FRAME_HEADER_SIZE);\n        if (buf->start == NULL) {\n            return NULL;\n        }\n\n        buf->end = buf->start + NGX_HTTP_V2_FRAME_HEADER_SIZE;\n        buf->last = buf->end;\n\n        buf->tag = (ngx_buf_tag_t) &ngx_http_v2_module;\n        buf->memory = 1;\n    }\n\n    buf->pos = buf->start;\n    buf->last = buf->pos;\n\n    buf->last = ngx_http_v2_write_len_and_type(buf->last, len,\n                                               NGX_HTTP_V2_DATA_FRAME);\n    *buf->last++ = flags;\n\n    buf->last = ngx_http_v2_write_sid(buf->last, stream->node->id);\n\n    cl->next = first;\n    first = cl;\n\n    last->buf->flush = 1;\n\n    frame->first = first;\n    frame->last = last;\n    frame->handler = ngx_http_v2_data_frame_handler;\n    frame->stream = stream;\n    frame->length = len;\n    frame->blocked = 0;\n    frame->fin = last->buf->last_buf;\n\n    return frame;\n}\n\n\nstatic ngx_inline ngx_int_t\nngx_http_v2_flow_control(ngx_http_v2_connection_t *h2c,\n    ngx_http_v2_stream_t *stream)\n{\n    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n                   \"http2:%ui windows: conn:%uz stream:%z\",\n                   stream->node->id, h2c->send_window, stream->send_window);\n\n    if (stream->send_window <= 0) {\n        stream->exhausted = 1;\n        return NGX_DECLINED;\n    }\n\n    if (h2c->send_window == 0) {\n        ngx_http_v2_waiting_queue(h2c, stream);\n        return NGX_DECLINED;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_http_v2_waiting_queue(ngx_http_v2_connection_t *h2c,\n    ngx_http_v2_stream_t *stream)\n{\n    ngx_queue_t           *q;\n    ngx_http_v2_stream_t  *s;\n\n    if (stream->waiting) {\n        return;\n    }\n\n    stream->waiting = 1;\n\n    for (q = ngx_queue_last(&h2c->waiting);\n         q != ngx_queue_sentinel(&h2c->waiting);\n         q = ngx_queue_prev(q))\n    {\n        s = ngx_queue_data(q, ngx_http_v2_stream_t, queue);\n\n        if (s->node->rank < stream->node->rank\n            || (s->node->rank == stream->node->rank\n                && s->node->rel_weight >= stream->node->rel_weight))\n        {\n            break;\n        }\n    }\n\n    ngx_queue_insert_after(q, &stream->queue);\n}\n\n\nstatic ngx_inline ngx_int_t\nngx_http_v2_filter_send(ngx_connection_t *fc, ngx_http_v2_stream_t *stream)\n{\n    ngx_connection_t  *c;\n\n    c = stream->connection->connection;\n\n    if (stream->queued == 0 && !c->buffered) {\n        fc->buffered &= ~NGX_HTTP_V2_BUFFERED;\n        return NGX_OK;\n    }\n\n    stream->blocked = 1;\n\n    if (ngx_http_v2_send_output_queue(stream->connection) == NGX_ERROR) {\n        fc->error = 1;\n        return NGX_ERROR;\n    }\n\n    stream->blocked = 0;\n\n    if (stream->queued) {\n        fc->buffered |= NGX_HTTP_V2_BUFFERED;\n        fc->write->active = 1;\n        fc->write->ready = 0;\n        return NGX_AGAIN;\n    }\n\n    fc->buffered &= ~NGX_HTTP_V2_BUFFERED;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_v2_headers_frame_handler(ngx_http_v2_connection_t *h2c,\n    ngx_http_v2_out_frame_t *frame)\n{\n    ngx_chain_t           *cl, *ln;\n    ngx_http_v2_stream_t  *stream;\n\n    stream = frame->stream;\n    cl = frame->first;\n\n    for ( ;; ) {\n        if (cl->buf->pos != cl->buf->last) {\n            frame->first = cl;\n\n            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n                           \"http2:%ui HEADERS frame %p was sent partially\",\n                           stream->node->id, frame);\n\n            return NGX_AGAIN;\n        }\n\n        ln = cl->next;\n\n        if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_module) {\n            cl->next = stream->free_frame_headers;\n            stream->free_frame_headers = cl;\n\n        } else {\n            cl->next = stream->free_bufs;\n            stream->free_bufs = cl;\n        }\n\n        if (cl == frame->last) {\n            break;\n        }\n\n        cl = ln;\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n                   \"http2:%ui HEADERS frame %p was sent\",\n                   stream->node->id, frame);\n\n    stream->request->header_size += NGX_HTTP_V2_FRAME_HEADER_SIZE\n                                    + frame->length;\n\n    h2c->payload_bytes += frame->length;\n\n    ngx_http_v2_handle_frame(stream, frame);\n\n    ngx_http_v2_handle_stream(h2c, stream);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_v2_push_frame_handler(ngx_http_v2_connection_t *h2c,\n    ngx_http_v2_out_frame_t *frame)\n{\n    ngx_chain_t           *cl, *ln;\n    ngx_http_v2_stream_t  *stream;\n\n    stream = frame->stream;\n    cl = frame->first;\n\n    for ( ;; ) {\n        if (cl->buf->pos != cl->buf->last) {\n            frame->first = cl;\n\n            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n                           \"http2:%ui PUSH_PROMISE frame %p was sent partially\",\n                           stream->node->id, frame);\n\n            return NGX_AGAIN;\n        }\n\n        ln = cl->next;\n\n        if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_module) {\n            cl->next = stream->free_frame_headers;\n            stream->free_frame_headers = cl;\n\n        } else {\n            cl->next = stream->free_bufs;\n            stream->free_bufs = cl;\n        }\n\n        if (cl == frame->last) {\n            break;\n        }\n\n        cl = ln;\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n                   \"http2:%ui PUSH_PROMISE frame %p was sent\",\n                   stream->node->id, frame);\n\n    stream->request->header_size += NGX_HTTP_V2_FRAME_HEADER_SIZE\n                                    + frame->length;\n\n    h2c->payload_bytes += frame->length;\n\n    ngx_http_v2_handle_frame(stream, frame);\n\n    ngx_http_v2_handle_stream(h2c, stream);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_v2_data_frame_handler(ngx_http_v2_connection_t *h2c,\n    ngx_http_v2_out_frame_t *frame)\n{\n    ngx_buf_t             *buf;\n    ngx_chain_t           *cl, *ln;\n    ngx_http_v2_stream_t  *stream;\n\n    stream = frame->stream;\n    cl = frame->first;\n\n    if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_module) {\n\n        if (cl->buf->pos != cl->buf->last) {\n            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n                           \"http2:%ui DATA frame %p was sent partially\",\n                           stream->node->id, frame);\n\n            return NGX_AGAIN;\n        }\n\n        ln = cl->next;\n\n        cl->next = stream->free_frame_headers;\n        stream->free_frame_headers = cl;\n\n        if (cl == frame->last) {\n            goto done;\n        }\n\n        cl = ln;\n    }\n\n    for ( ;; ) {\n        if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_filter_get_shadow) {\n            buf = cl->buf->shadow;\n\n            if (ngx_buf_in_memory(buf)) {\n                buf->pos = cl->buf->pos;\n            }\n\n            if (buf->in_file) {\n                buf->file_pos = cl->buf->file_pos;\n            }\n        }\n\n        if (ngx_buf_size(cl->buf) != 0) {\n\n            if (cl != frame->first) {\n                frame->first = cl;\n                ngx_http_v2_handle_stream(h2c, stream);\n            }\n\n            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n                           \"http2:%ui DATA frame %p was sent partially\",\n                           stream->node->id, frame);\n\n            return NGX_AGAIN;\n        }\n\n        ln = cl->next;\n\n        if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_filter_get_shadow) {\n            cl->next = stream->free_bufs;\n            stream->free_bufs = cl;\n\n        } else {\n            ngx_free_chain(stream->request->pool, cl);\n        }\n\n        if (cl == frame->last) {\n            goto done;\n        }\n\n        cl = ln;\n    }\n\ndone:\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n                   \"http2:%ui DATA frame %p was sent\",\n                   stream->node->id, frame);\n\n    stream->request->header_size += NGX_HTTP_V2_FRAME_HEADER_SIZE;\n\n    h2c->payload_bytes += frame->length;\n\n    ngx_http_v2_handle_frame(stream, frame);\n\n    ngx_http_v2_handle_stream(h2c, stream);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_inline void\nngx_http_v2_handle_frame(ngx_http_v2_stream_t *stream,\n    ngx_http_v2_out_frame_t *frame)\n{\n    ngx_http_request_t        *r;\n    ngx_http_v2_connection_t  *h2c;\n\n    r = stream->request;\n\n    r->connection->sent += NGX_HTTP_V2_FRAME_HEADER_SIZE + frame->length;\n\n    h2c = stream->connection;\n\n    h2c->total_bytes += NGX_HTTP_V2_FRAME_HEADER_SIZE + frame->length;\n\n    if (frame->fin) {\n        stream->out_closed = 1;\n    }\n\n    frame->next = stream->free_frames;\n    stream->free_frames = frame;\n\n    stream->queued--;\n}\n\n\nstatic ngx_inline void\nngx_http_v2_handle_stream(ngx_http_v2_connection_t *h2c,\n    ngx_http_v2_stream_t *stream)\n{\n    ngx_event_t       *wev;\n    ngx_connection_t  *fc;\n\n    if (stream->waiting || stream->blocked) {\n        return;\n    }\n\n    fc = stream->request->connection;\n\n    if (!fc->error && stream->exhausted) {\n        return;\n    }\n\n    wev = fc->write;\n\n    wev->active = 0;\n    wev->ready = 1;\n\n    if (!fc->error && wev->delayed) {\n        return;\n    }\n\n    ngx_post_event(wev, &ngx_posted_events);\n}\n\n\nstatic void\nngx_http_v2_filter_cleanup(void *data)\n{\n    ngx_http_v2_stream_t *stream = data;\n\n    size_t                     window;\n    ngx_event_t               *wev;\n    ngx_queue_t               *q;\n    ngx_http_v2_out_frame_t   *frame, **fn;\n    ngx_http_v2_connection_t  *h2c;\n\n    if (stream->waiting) {\n        stream->waiting = 0;\n        ngx_queue_remove(&stream->queue);\n    }\n\n    if (stream->queued == 0) {\n        return;\n    }\n\n    window = 0;\n    h2c = stream->connection;\n    fn = &h2c->last_out;\n\n    for ( ;; ) {\n        frame = *fn;\n\n        if (frame == NULL) {\n            break;\n        }\n\n        if (frame->stream == stream && !frame->blocked) {\n            *fn = frame->next;\n\n            window += frame->length;\n\n            if (--stream->queued == 0) {\n                break;\n            }\n\n            continue;\n        }\n\n        fn = &frame->next;\n    }\n\n    if (h2c->send_window == 0 && window) {\n\n        while (!ngx_queue_empty(&h2c->waiting)) {\n            q = ngx_queue_head(&h2c->waiting);\n\n            ngx_queue_remove(q);\n\n            stream = ngx_queue_data(q, ngx_http_v2_stream_t, queue);\n\n            stream->waiting = 0;\n\n            wev = stream->request->connection->write;\n\n            wev->active = 0;\n            wev->ready = 1;\n\n            if (!wev->delayed) {\n                ngx_post_event(wev, &ngx_posted_events);\n            }\n        }\n    }\n\n    h2c->send_window += window;\n}\n\n\nstatic ngx_int_t\nngx_http_v2_filter_init(ngx_conf_t *cf)\n{\n    ngx_http_next_header_filter = ngx_http_top_header_filter;\n    ngx_http_top_header_filter = ngx_http_v2_header_filter;\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "src/http/v2/ngx_http_v2_module.c",
    "content": "\n/*\n * Copyright (C) Nginx, Inc.\n * Copyright (C) Valentin V. Bartenev\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n#include <ngx_http_v2_module.h>\n\n\nstatic ngx_int_t ngx_http_v2_add_variables(ngx_conf_t *cf);\n\nstatic ngx_int_t ngx_http_v2_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data);\n\nstatic ngx_int_t ngx_http_v2_module_init(ngx_cycle_t *cycle);\n\nstatic void *ngx_http_v2_create_main_conf(ngx_conf_t *cf);\nstatic char *ngx_http_v2_init_main_conf(ngx_conf_t *cf, void *conf);\nstatic void *ngx_http_v2_create_srv_conf(ngx_conf_t *cf);\nstatic char *ngx_http_v2_merge_srv_conf(ngx_conf_t *cf, void *parent,\n    void *child);\nstatic void *ngx_http_v2_create_loc_conf(ngx_conf_t *cf);\nstatic char *ngx_http_v2_merge_loc_conf(ngx_conf_t *cf, void *parent,\n    void *child);\n\nstatic char *ngx_http_v2_push(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\n\nstatic char *ngx_http_v2_recv_buffer_size(ngx_conf_t *cf, void *post,\n    void *data);\nstatic char *ngx_http_v2_pool_size(ngx_conf_t *cf, void *post, void *data);\nstatic char *ngx_http_v2_preread_size(ngx_conf_t *cf, void *post, void *data);\nstatic char *ngx_http_v2_streams_index_mask(ngx_conf_t *cf, void *post,\n    void *data);\nstatic char *ngx_http_v2_chunk_size(ngx_conf_t *cf, void *post, void *data);\nstatic char *ngx_http_v2_obsolete(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n\n\nstatic ngx_conf_deprecated_t  ngx_http_v2_recv_timeout_deprecated = {\n    ngx_conf_deprecated, \"http2_recv_timeout\", \"client_header_timeout\"\n};\n\nstatic ngx_conf_deprecated_t  ngx_http_v2_idle_timeout_deprecated = {\n    ngx_conf_deprecated, \"http2_idle_timeout\", \"keepalive_timeout\"\n};\n\nstatic ngx_conf_deprecated_t  ngx_http_v2_max_requests_deprecated = {\n    ngx_conf_deprecated, \"http2_max_requests\", \"keepalive_requests\"\n};\n\nstatic ngx_conf_deprecated_t  ngx_http_v2_max_field_size_deprecated = {\n    ngx_conf_deprecated, \"http2_max_field_size\", \"large_client_header_buffers\"\n};\n\nstatic ngx_conf_deprecated_t  ngx_http_v2_max_header_size_deprecated = {\n    ngx_conf_deprecated, \"http2_max_header_size\", \"large_client_header_buffers\"\n};\n\n\nstatic ngx_conf_post_t  ngx_http_v2_recv_buffer_size_post =\n    { ngx_http_v2_recv_buffer_size };\nstatic ngx_conf_post_t  ngx_http_v2_pool_size_post =\n    { ngx_http_v2_pool_size };\nstatic ngx_conf_post_t  ngx_http_v2_preread_size_post =\n    { ngx_http_v2_preread_size };\nstatic ngx_conf_post_t  ngx_http_v2_streams_index_mask_post =\n    { ngx_http_v2_streams_index_mask };\nstatic ngx_conf_post_t  ngx_http_v2_chunk_size_post =\n    { ngx_http_v2_chunk_size };\n\n\nstatic ngx_command_t  ngx_http_v2_commands[] = {\n\n    { ngx_string(\"http2_recv_buffer_size\"),\n      NGX_HTTP_MAIN_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_MAIN_CONF_OFFSET,\n      offsetof(ngx_http_v2_main_conf_t, recv_buffer_size),\n      &ngx_http_v2_recv_buffer_size_post },\n\n    { ngx_string(\"http2_pool_size\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      offsetof(ngx_http_v2_srv_conf_t, pool_size),\n      &ngx_http_v2_pool_size_post },\n\n    { ngx_string(\"http2_max_concurrent_streams\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_num_slot,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      offsetof(ngx_http_v2_srv_conf_t, concurrent_streams),\n      NULL },\n\n    { ngx_string(\"http2_max_concurrent_pushes\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_num_slot,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      offsetof(ngx_http_v2_srv_conf_t, concurrent_pushes),\n      NULL },\n\n    { ngx_string(\"http2_max_requests\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_http_v2_obsolete,\n      0,\n      0,\n      &ngx_http_v2_max_requests_deprecated },\n\n    { ngx_string(\"http2_max_field_size\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_http_v2_obsolete,\n      0,\n      0,\n      &ngx_http_v2_max_field_size_deprecated },\n\n    { ngx_string(\"http2_max_header_size\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_http_v2_obsolete,\n      0,\n      0,\n      &ngx_http_v2_max_header_size_deprecated },\n\n    { ngx_string(\"http2_body_preread_size\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      offsetof(ngx_http_v2_srv_conf_t, preread_size),\n      &ngx_http_v2_preread_size_post },\n\n    { ngx_string(\"http2_streams_index_size\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_num_slot,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      offsetof(ngx_http_v2_srv_conf_t, streams_index_mask),\n      &ngx_http_v2_streams_index_mask_post },\n\n    { ngx_string(\"http2_recv_timeout\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_http_v2_obsolete,\n      0,\n      0,\n      &ngx_http_v2_recv_timeout_deprecated },\n\n    { ngx_string(\"http2_idle_timeout\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_http_v2_obsolete,\n      0,\n      0,\n      &ngx_http_v2_idle_timeout_deprecated },\n\n    { ngx_string(\"http2_chunk_size\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_v2_loc_conf_t, chunk_size),\n      &ngx_http_v2_chunk_size_post },\n\n    { ngx_string(\"http2_push_preload\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      offsetof(ngx_http_v2_loc_conf_t, push_preload),\n      NULL },\n\n    { ngx_string(\"http2_push\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,\n      ngx_http_v2_push,\n      NGX_HTTP_LOC_CONF_OFFSET,\n      0,\n      NULL },\n\n#if (T_NGX_HTTP2_SRV_ENABLE)\n    { ngx_string(\"http2\"),\n      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_flag_slot,\n      NGX_HTTP_SRV_CONF_OFFSET,\n      offsetof(ngx_http_v2_srv_conf_t, enable),\n      NULL },\n#endif\n\n      ngx_null_command\n};\n\n\nstatic ngx_http_module_t  ngx_http_v2_module_ctx = {\n    ngx_http_v2_add_variables,             /* preconfiguration */\n    NULL,                                  /* postconfiguration */\n\n    ngx_http_v2_create_main_conf,          /* create main configuration */\n    ngx_http_v2_init_main_conf,            /* init main configuration */\n\n    ngx_http_v2_create_srv_conf,           /* create server configuration */\n    ngx_http_v2_merge_srv_conf,            /* merge server configuration */\n\n    ngx_http_v2_create_loc_conf,           /* create location configuration */\n    ngx_http_v2_merge_loc_conf             /* merge location configuration */\n};\n\n\nngx_module_t  ngx_http_v2_module = {\n    NGX_MODULE_V1,\n    &ngx_http_v2_module_ctx,               /* module context */\n    ngx_http_v2_commands,                  /* module directives */\n    NGX_HTTP_MODULE,                       /* module type */\n    NULL,                                  /* init master */\n    ngx_http_v2_module_init,               /* 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_http_variable_t  ngx_http_v2_vars[] = {\n\n    { ngx_string(\"http2\"), NULL,\n      ngx_http_v2_variable, 0, 0, 0 },\n\n      ngx_http_null_variable\n};\n\n\nstatic ngx_int_t\nngx_http_v2_add_variables(ngx_conf_t *cf)\n{\n    ngx_http_variable_t  *var, *v;\n\n    for (v = ngx_http_v2_vars; v->name.len; v++) {\n        var = ngx_http_add_variable(cf, &v->name, v->flags);\n        if (var == NULL) {\n            return NGX_ERROR;\n        }\n\n        var->get_handler = v->get_handler;\n        var->data = v->data;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_v2_variable(ngx_http_request_t *r,\n    ngx_http_variable_value_t *v, uintptr_t data)\n{\n\n    if (r->stream) {\n#if (NGX_HTTP_SSL)\n\n        if (r->connection->ssl) {\n            v->len = sizeof(\"h2\") - 1;\n            v->valid = 1;\n            v->no_cacheable = 0;\n            v->not_found = 0;\n            v->data = (u_char *) \"h2\";\n\n            return NGX_OK;\n        }\n\n#endif\n        v->len = sizeof(\"h2c\") - 1;\n        v->valid = 1;\n        v->no_cacheable = 0;\n        v->not_found = 0;\n        v->data = (u_char *) \"h2c\";\n\n        return NGX_OK;\n    }\n\n    *v = ngx_http_variable_null_value;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_v2_module_init(ngx_cycle_t *cycle)\n{\n    return NGX_OK;\n}\n\n\nstatic void *\nngx_http_v2_create_main_conf(ngx_conf_t *cf)\n{\n    ngx_http_v2_main_conf_t  *h2mcf;\n\n    h2mcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_v2_main_conf_t));\n    if (h2mcf == NULL) {\n        return NULL;\n    }\n\n    h2mcf->recv_buffer_size = NGX_CONF_UNSET_SIZE;\n\n    return h2mcf;\n}\n\n\nstatic char *\nngx_http_v2_init_main_conf(ngx_conf_t *cf, void *conf)\n{\n    ngx_http_v2_main_conf_t *h2mcf = conf;\n\n    ngx_conf_init_size_value(h2mcf->recv_buffer_size, 256 * 1024);\n\n    return NGX_CONF_OK;\n}\n\n\nstatic void *\nngx_http_v2_create_srv_conf(ngx_conf_t *cf)\n{\n    ngx_http_v2_srv_conf_t  *h2scf;\n\n    h2scf = ngx_pcalloc(cf->pool, sizeof(ngx_http_v2_srv_conf_t));\n    if (h2scf == NULL) {\n        return NULL;\n    }\n\n    h2scf->pool_size = NGX_CONF_UNSET_SIZE;\n\n    h2scf->concurrent_streams = NGX_CONF_UNSET_UINT;\n    h2scf->concurrent_pushes = NGX_CONF_UNSET_UINT;\n\n    h2scf->preread_size = NGX_CONF_UNSET_SIZE;\n\n    h2scf->streams_index_mask = NGX_CONF_UNSET_UINT;\n\n#if (T_NGX_HTTP2_SRV_ENABLE)\n    h2scf->enable = NGX_CONF_UNSET;\n#endif\n\n    return h2scf;\n}\n\n\nstatic char *\nngx_http_v2_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_http_v2_srv_conf_t *prev = parent;\n    ngx_http_v2_srv_conf_t *conf = child;\n\n    ngx_conf_merge_size_value(conf->pool_size, prev->pool_size, 4096);\n\n    ngx_conf_merge_uint_value(conf->concurrent_streams,\n                              prev->concurrent_streams, 128);\n    ngx_conf_merge_uint_value(conf->concurrent_pushes,\n                              prev->concurrent_pushes, 10);\n\n    ngx_conf_merge_size_value(conf->preread_size, prev->preread_size, 65536);\n\n    ngx_conf_merge_uint_value(conf->streams_index_mask,\n                              prev->streams_index_mask, 32 - 1);\n\n#if (T_NGX_HTTP2_SRV_ENABLE)\n    if (conf->enable == NGX_CONF_UNSET) {\n        conf->enable = prev->enable;\n    }\n#endif\n\n    return NGX_CONF_OK;\n}\n\n\nstatic void *\nngx_http_v2_create_loc_conf(ngx_conf_t *cf)\n{\n    ngx_http_v2_loc_conf_t  *h2lcf;\n\n    h2lcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_v2_loc_conf_t));\n    if (h2lcf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc():\n     *\n     *     h2lcf->pushes = NULL;\n     */\n\n    h2lcf->chunk_size = NGX_CONF_UNSET_SIZE;\n\n    h2lcf->push_preload = NGX_CONF_UNSET;\n    h2lcf->push = NGX_CONF_UNSET;\n\n    return h2lcf;\n}\n\n\nstatic char *\nngx_http_v2_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_http_v2_loc_conf_t *prev = parent;\n    ngx_http_v2_loc_conf_t *conf = child;\n\n    ngx_conf_merge_size_value(conf->chunk_size, prev->chunk_size, 8 * 1024);\n\n    ngx_conf_merge_value(conf->push, prev->push, 1);\n\n    if (conf->push && conf->pushes == NULL) {\n        conf->pushes = prev->pushes;\n    }\n\n    ngx_conf_merge_value(conf->push_preload, prev->push_preload, 0);\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_v2_push(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_http_v2_loc_conf_t *h2lcf = conf;\n\n    ngx_str_t                         *value;\n    ngx_http_complex_value_t          *cv;\n    ngx_http_compile_complex_value_t   ccv;\n\n    value = cf->args->elts;\n\n    if (ngx_strcmp(value[1].data, \"off\") == 0) {\n\n        if (h2lcf->pushes) {\n            return \"\\\"off\\\" parameter cannot be used with URI\";\n        }\n\n        if (h2lcf->push == 0) {\n            return \"is duplicate\";\n        }\n\n        h2lcf->push = 0;\n        return NGX_CONF_OK;\n    }\n\n    if (h2lcf->push == 0) {\n        return \"URI cannot be used with \\\"off\\\" parameter\";\n    }\n\n    h2lcf->push = 1;\n\n    if (h2lcf->pushes == NULL) {\n        h2lcf->pushes = ngx_array_create(cf->pool, 1,\n                                         sizeof(ngx_http_complex_value_t));\n        if (h2lcf->pushes == NULL) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    cv = ngx_array_push(h2lcf->pushes);\n    if (cv == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));\n\n    ccv.cf = cf;\n    ccv.value = &value[1];\n    ccv.complex_value = cv;\n\n    if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_v2_recv_buffer_size(ngx_conf_t *cf, void *post, void *data)\n{\n    size_t *sp = data;\n\n    if (*sp <= 2 * NGX_HTTP_V2_STATE_BUFFER_SIZE) {\n        return \"value is too small\";\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_v2_pool_size(ngx_conf_t *cf, void *post, void *data)\n{\n    size_t *sp = data;\n\n    if (*sp < NGX_MIN_POOL_SIZE) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"the pool size must be no less than %uz\",\n                           NGX_MIN_POOL_SIZE);\n\n        return NGX_CONF_ERROR;\n    }\n\n    if (*sp % NGX_POOL_ALIGNMENT) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"the pool size must be a multiple of %uz\",\n                           NGX_POOL_ALIGNMENT);\n\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_v2_preread_size(ngx_conf_t *cf, void *post, void *data)\n{\n    size_t *sp = data;\n\n    if (*sp > NGX_HTTP_V2_MAX_WINDOW) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"the maximum body preread buffer size is %uz\",\n                           NGX_HTTP_V2_MAX_WINDOW);\n\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_v2_streams_index_mask(ngx_conf_t *cf, void *post, void *data)\n{\n    ngx_uint_t *np = data;\n\n    ngx_uint_t  mask;\n\n    mask = *np - 1;\n\n    if (*np == 0 || (*np & mask)) {\n        return \"must be a power of two\";\n    }\n\n    *np = mask;\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_v2_chunk_size(ngx_conf_t *cf, void *post, void *data)\n{\n    size_t *sp = data;\n\n    if (*sp == 0) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"the http2 chunk size cannot be zero\");\n\n        return NGX_CONF_ERROR;\n    }\n\n    if (*sp > NGX_HTTP_V2_MAX_FRAME_SIZE) {\n        *sp = NGX_HTTP_V2_MAX_FRAME_SIZE;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_http_v2_obsolete(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_conf_deprecated_t  *d = cmd->post;\n\n    ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                       \"the \\\"%s\\\" directive is obsolete, \"\n                       \"use the \\\"%s\\\" directive instead\",\n                       d->old_name, d->new_name);\n\n    return NGX_CONF_OK;\n}\n"
  },
  {
    "path": "src/http/v2/ngx_http_v2_module.h",
    "content": "\n/*\n * Copyright (C) Nginx, Inc.\n * Copyright (C) Valentin V. Bartenev\n */\n\n\n#ifndef _NGX_HTTP_V2_MODULE_H_INCLUDED_\n#define _NGX_HTTP_V2_MODULE_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\ntypedef struct {\n    size_t                          recv_buffer_size;\n    u_char                         *recv_buffer;\n} ngx_http_v2_main_conf_t;\n\n\ntypedef struct {\n    size_t                          pool_size;\n    ngx_uint_t                      concurrent_streams;\n    ngx_uint_t                      concurrent_pushes;\n    size_t                          preread_size;\n    ngx_uint_t                      streams_index_mask;\n#if (T_NGX_HTTP2_SRV_ENABLE)\n    ngx_flag_t                      enable;\n#endif\n} ngx_http_v2_srv_conf_t;\n\n\ntypedef struct {\n    size_t                          chunk_size;\n\n    ngx_flag_t                      push_preload;\n\n    ngx_flag_t                      push;\n    ngx_array_t                    *pushes;\n} ngx_http_v2_loc_conf_t;\n\n\nextern ngx_module_t  ngx_http_v2_module;\n\n\n#endif /* _NGX_HTTP_V2_MODULE_H_INCLUDED_ */\n"
  },
  {
    "path": "src/http/v2/ngx_http_v2_table.c",
    "content": "\n/*\n * Copyright (C) Nginx, Inc.\n * Copyright (C) Valentin V. Bartenev\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_http.h>\n\n\n#define NGX_HTTP_V2_TABLE_SIZE  4096\n\n\nstatic ngx_int_t ngx_http_v2_table_account(ngx_http_v2_connection_t *h2c,\n    size_t size);\n\n\nstatic ngx_http_v2_header_t  ngx_http_v2_static_table[] = {\n    { ngx_string(\":authority\"), ngx_string(\"\") },\n    { ngx_string(\":method\"), ngx_string(\"GET\") },\n    { ngx_string(\":method\"), ngx_string(\"POST\") },\n    { ngx_string(\":path\"), ngx_string(\"/\") },\n    { ngx_string(\":path\"), ngx_string(\"/index.html\") },\n    { ngx_string(\":scheme\"), ngx_string(\"http\") },\n    { ngx_string(\":scheme\"), ngx_string(\"https\") },\n    { ngx_string(\":status\"), ngx_string(\"200\") },\n    { ngx_string(\":status\"), ngx_string(\"204\") },\n    { ngx_string(\":status\"), ngx_string(\"206\") },\n    { ngx_string(\":status\"), ngx_string(\"304\") },\n    { ngx_string(\":status\"), ngx_string(\"400\") },\n    { ngx_string(\":status\"), ngx_string(\"404\") },\n    { ngx_string(\":status\"), ngx_string(\"500\") },\n    { ngx_string(\"accept-charset\"), ngx_string(\"\") },\n    { ngx_string(\"accept-encoding\"), ngx_string(\"gzip, deflate\") },\n    { ngx_string(\"accept-language\"), ngx_string(\"\") },\n    { ngx_string(\"accept-ranges\"), ngx_string(\"\") },\n    { ngx_string(\"accept\"), ngx_string(\"\") },\n    { ngx_string(\"access-control-allow-origin\"), ngx_string(\"\") },\n    { ngx_string(\"age\"), ngx_string(\"\") },\n    { ngx_string(\"allow\"), ngx_string(\"\") },\n    { ngx_string(\"authorization\"), ngx_string(\"\") },\n    { ngx_string(\"cache-control\"), ngx_string(\"\") },\n    { ngx_string(\"content-disposition\"), ngx_string(\"\") },\n    { ngx_string(\"content-encoding\"), ngx_string(\"\") },\n    { ngx_string(\"content-language\"), ngx_string(\"\") },\n    { ngx_string(\"content-length\"), ngx_string(\"\") },\n    { ngx_string(\"content-location\"), ngx_string(\"\") },\n    { ngx_string(\"content-range\"), ngx_string(\"\") },\n    { ngx_string(\"content-type\"), ngx_string(\"\") },\n    { ngx_string(\"cookie\"), ngx_string(\"\") },\n    { ngx_string(\"date\"), ngx_string(\"\") },\n    { ngx_string(\"etag\"), ngx_string(\"\") },\n    { ngx_string(\"expect\"), ngx_string(\"\") },\n    { ngx_string(\"expires\"), ngx_string(\"\") },\n    { ngx_string(\"from\"), ngx_string(\"\") },\n    { ngx_string(\"host\"), ngx_string(\"\") },\n    { ngx_string(\"if-match\"), ngx_string(\"\") },\n    { ngx_string(\"if-modified-since\"), ngx_string(\"\") },\n    { ngx_string(\"if-none-match\"), ngx_string(\"\") },\n    { ngx_string(\"if-range\"), ngx_string(\"\") },\n    { ngx_string(\"if-unmodified-since\"), ngx_string(\"\") },\n    { ngx_string(\"last-modified\"), ngx_string(\"\") },\n    { ngx_string(\"link\"), ngx_string(\"\") },\n    { ngx_string(\"location\"), ngx_string(\"\") },\n    { ngx_string(\"max-forwards\"), ngx_string(\"\") },\n    { ngx_string(\"proxy-authenticate\"), ngx_string(\"\") },\n    { ngx_string(\"proxy-authorization\"), ngx_string(\"\") },\n    { ngx_string(\"range\"), ngx_string(\"\") },\n    { ngx_string(\"referer\"), ngx_string(\"\") },\n    { ngx_string(\"refresh\"), ngx_string(\"\") },\n    { ngx_string(\"retry-after\"), ngx_string(\"\") },\n    { ngx_string(\"server\"), ngx_string(\"\") },\n    { ngx_string(\"set-cookie\"), ngx_string(\"\") },\n    { ngx_string(\"strict-transport-security\"), ngx_string(\"\") },\n    { ngx_string(\"transfer-encoding\"), ngx_string(\"\") },\n    { ngx_string(\"user-agent\"), ngx_string(\"\") },\n    { ngx_string(\"vary\"), ngx_string(\"\") },\n    { ngx_string(\"via\"), ngx_string(\"\") },\n    { ngx_string(\"www-authenticate\"), ngx_string(\"\") },\n};\n\n#define NGX_HTTP_V2_STATIC_TABLE_ENTRIES                                      \\\n    (sizeof(ngx_http_v2_static_table)                                         \\\n     / sizeof(ngx_http_v2_header_t))\n\n\nngx_str_t *\nngx_http_v2_get_static_name(ngx_uint_t index)\n{\n    return &ngx_http_v2_static_table[index - 1].name;\n}\n\n\nngx_str_t *\nngx_http_v2_get_static_value(ngx_uint_t index)\n{\n    return &ngx_http_v2_static_table[index - 1].value;\n}\n\n\nngx_int_t\nngx_http_v2_get_indexed_header(ngx_http_v2_connection_t *h2c, ngx_uint_t index,\n    ngx_uint_t name_only)\n{\n    u_char                *p;\n    size_t                 rest;\n    ngx_http_v2_header_t  *entry;\n\n    if (index == 0) {\n        ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                      \"client sent invalid hpack table index 0\");\n        return NGX_ERROR;\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n                   \"http2 get indexed %s: %ui\",\n                   name_only ? \"name\" : \"header\", index);\n\n    index--;\n\n    if (index < NGX_HTTP_V2_STATIC_TABLE_ENTRIES) {\n        h2c->state.header = ngx_http_v2_static_table[index];\n        return NGX_OK;\n    }\n\n    index -= NGX_HTTP_V2_STATIC_TABLE_ENTRIES;\n\n    if (index < h2c->hpack.added - h2c->hpack.deleted) {\n        index = (h2c->hpack.added - index - 1) % h2c->hpack.allocated;\n        entry = h2c->hpack.entries[index];\n\n        p = ngx_pnalloc(h2c->state.pool, entry->name.len + 1);\n        if (p == NULL) {\n            return NGX_ERROR;\n        }\n\n        h2c->state.header.name.len = entry->name.len;\n        h2c->state.header.name.data = p;\n\n        rest = h2c->hpack.storage + NGX_HTTP_V2_TABLE_SIZE - entry->name.data;\n\n        if (entry->name.len > rest) {\n            p = ngx_cpymem(p, entry->name.data, rest);\n            p = ngx_cpymem(p, h2c->hpack.storage, entry->name.len - rest);\n\n        } else {\n            p = ngx_cpymem(p, entry->name.data, entry->name.len);\n        }\n\n        *p = '\\0';\n\n        if (name_only) {\n            return NGX_OK;\n        }\n\n        p = ngx_pnalloc(h2c->state.pool, entry->value.len + 1);\n        if (p == NULL) {\n            return NGX_ERROR;\n        }\n\n        h2c->state.header.value.len = entry->value.len;\n        h2c->state.header.value.data = p;\n\n        rest = h2c->hpack.storage + NGX_HTTP_V2_TABLE_SIZE - entry->value.data;\n\n        if (entry->value.len > rest) {\n            p = ngx_cpymem(p, entry->value.data, rest);\n            p = ngx_cpymem(p, h2c->hpack.storage, entry->value.len - rest);\n\n        } else {\n            p = ngx_cpymem(p, entry->value.data, entry->value.len);\n        }\n\n        *p = '\\0';\n\n        return NGX_OK;\n    }\n\n    ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                  \"client sent out of bound hpack table index: %ui\", index);\n\n    return NGX_ERROR;\n}\n\n\nngx_int_t\nngx_http_v2_add_header(ngx_http_v2_connection_t *h2c,\n    ngx_http_v2_header_t *header)\n{\n    size_t                 avail;\n    ngx_uint_t             index;\n    ngx_http_v2_header_t  *entry, **entries;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n                   \"http2 table add: \\\"%V: %V\\\"\",\n                   &header->name, &header->value);\n\n    if (h2c->hpack.entries == NULL) {\n        h2c->hpack.allocated = 64;\n        h2c->hpack.size = NGX_HTTP_V2_TABLE_SIZE;\n        h2c->hpack.free = NGX_HTTP_V2_TABLE_SIZE;\n\n        h2c->hpack.entries = ngx_palloc(h2c->connection->pool,\n                                        sizeof(ngx_http_v2_header_t *)\n                                        * h2c->hpack.allocated);\n        if (h2c->hpack.entries == NULL) {\n            return NGX_ERROR;\n        }\n\n        h2c->hpack.storage = ngx_palloc(h2c->connection->pool,\n                                        h2c->hpack.free);\n        if (h2c->hpack.storage == NULL) {\n            return NGX_ERROR;\n        }\n\n        h2c->hpack.pos = h2c->hpack.storage;\n    }\n\n    if (ngx_http_v2_table_account(h2c, header->name.len + header->value.len)\n        != NGX_OK)\n    {\n        return NGX_OK;\n    }\n\n    if (h2c->hpack.reused == h2c->hpack.deleted) {\n        entry = ngx_palloc(h2c->connection->pool, sizeof(ngx_http_v2_header_t));\n        if (entry == NULL) {\n            return NGX_ERROR;\n        }\n\n    } else {\n        entry = h2c->hpack.entries[h2c->hpack.reused++ % h2c->hpack.allocated];\n    }\n\n    avail = h2c->hpack.storage + NGX_HTTP_V2_TABLE_SIZE - h2c->hpack.pos;\n\n    entry->name.len = header->name.len;\n    entry->name.data = h2c->hpack.pos;\n\n    if (avail >= header->name.len) {\n        h2c->hpack.pos = ngx_cpymem(h2c->hpack.pos, header->name.data,\n                                    header->name.len);\n    } else {\n        ngx_memcpy(h2c->hpack.pos, header->name.data, avail);\n        h2c->hpack.pos = ngx_cpymem(h2c->hpack.storage,\n                                    header->name.data + avail,\n                                    header->name.len - avail);\n        avail = NGX_HTTP_V2_TABLE_SIZE;\n    }\n\n    avail -= header->name.len;\n\n    entry->value.len = header->value.len;\n    entry->value.data = h2c->hpack.pos;\n\n    if (avail >= header->value.len) {\n        h2c->hpack.pos = ngx_cpymem(h2c->hpack.pos, header->value.data,\n                                    header->value.len);\n    } else {\n        ngx_memcpy(h2c->hpack.pos, header->value.data, avail);\n        h2c->hpack.pos = ngx_cpymem(h2c->hpack.storage,\n                                    header->value.data + avail,\n                                    header->value.len - avail);\n    }\n\n    if (h2c->hpack.allocated == h2c->hpack.added - h2c->hpack.deleted) {\n\n        entries = ngx_palloc(h2c->connection->pool,\n                             sizeof(ngx_http_v2_header_t *)\n                             * (h2c->hpack.allocated + 64));\n        if (entries == NULL) {\n            return NGX_ERROR;\n        }\n\n        index = h2c->hpack.deleted % h2c->hpack.allocated;\n\n        ngx_memcpy(entries, &h2c->hpack.entries[index],\n                   (h2c->hpack.allocated - index)\n                   * sizeof(ngx_http_v2_header_t *));\n\n        ngx_memcpy(&entries[h2c->hpack.allocated - index], h2c->hpack.entries,\n                   index * sizeof(ngx_http_v2_header_t *));\n\n        (void) ngx_pfree(h2c->connection->pool, h2c->hpack.entries);\n\n        h2c->hpack.entries = entries;\n\n        h2c->hpack.added = h2c->hpack.allocated;\n        h2c->hpack.deleted = 0;\n        h2c->hpack.reused = 0;\n        h2c->hpack.allocated += 64;\n    }\n\n    h2c->hpack.entries[h2c->hpack.added++ % h2c->hpack.allocated] = entry;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_http_v2_table_account(ngx_http_v2_connection_t *h2c, size_t size)\n{\n    ngx_http_v2_header_t  *entry;\n\n    size += 32;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n                   \"http2 table account: %uz free:%uz\",\n                   size, h2c->hpack.free);\n\n    if (size <= h2c->hpack.free) {\n        h2c->hpack.free -= size;\n        return NGX_OK;\n    }\n\n    if (size > h2c->hpack.size) {\n        h2c->hpack.deleted = h2c->hpack.added;\n        h2c->hpack.free = h2c->hpack.size;\n        return NGX_DECLINED;\n    }\n\n    do {\n        entry = h2c->hpack.entries[h2c->hpack.deleted++ % h2c->hpack.allocated];\n        h2c->hpack.free += 32 + entry->name.len + entry->value.len;\n    } while (size > h2c->hpack.free);\n\n    h2c->hpack.free -= size;\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_http_v2_table_size(ngx_http_v2_connection_t *h2c, size_t size)\n{\n    ssize_t                needed;\n    ngx_http_v2_header_t  *entry;\n\n    if (size > NGX_HTTP_V2_TABLE_SIZE) {\n        ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0,\n                      \"client sent invalid table size update: %uz\", size);\n\n        return NGX_ERROR;\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0,\n                   \"http2 new hpack table size: %uz was:%uz\",\n                   size, h2c->hpack.size);\n\n    needed = h2c->hpack.size - size;\n\n    while (needed > (ssize_t) h2c->hpack.free) {\n        entry = h2c->hpack.entries[h2c->hpack.deleted++ % h2c->hpack.allocated];\n        h2c->hpack.free += 32 + entry->name.len + entry->value.len;\n    }\n\n    h2c->hpack.size = size;\n    h2c->hpack.free -= needed;\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "src/mail/ngx_mail.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n#include <ngx_mail.h>\n\n\nstatic char *ngx_mail_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\nstatic ngx_int_t ngx_mail_add_ports(ngx_conf_t *cf, ngx_array_t *ports,\n    ngx_mail_listen_t *listen);\nstatic char *ngx_mail_optimize_servers(ngx_conf_t *cf, ngx_array_t *ports);\nstatic ngx_int_t ngx_mail_add_addrs(ngx_conf_t *cf, ngx_mail_port_t *mport,\n    ngx_mail_conf_addr_t *addr);\n#if (NGX_HAVE_INET6)\nstatic ngx_int_t ngx_mail_add_addrs6(ngx_conf_t *cf, ngx_mail_port_t *mport,\n    ngx_mail_conf_addr_t *addr);\n#endif\nstatic ngx_int_t ngx_mail_cmp_conf_addrs(const void *one, const void *two);\n\n\nngx_uint_t  ngx_mail_max_module;\n\n\nstatic ngx_command_t  ngx_mail_commands[] = {\n\n    { ngx_string(\"mail\"),\n      NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,\n      ngx_mail_block,\n      0,\n      0,\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_core_module_t  ngx_mail_module_ctx = {\n    ngx_string(\"mail\"),\n    NULL,\n    NULL\n};\n\n\nngx_module_t  ngx_mail_module = {\n    NGX_MODULE_V1,\n    &ngx_mail_module_ctx,                  /* module context */\n    ngx_mail_commands,                     /* module directives */\n    NGX_CORE_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 char *\nngx_mail_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    char                        *rv;\n    ngx_uint_t                   i, m, mi, s;\n    ngx_conf_t                   pcf;\n    ngx_array_t                  ports;\n    ngx_mail_listen_t           *listen;\n    ngx_mail_module_t           *module;\n    ngx_mail_conf_ctx_t         *ctx;\n    ngx_mail_core_srv_conf_t   **cscfp;\n    ngx_mail_core_main_conf_t   *cmcf;\n\n    if (*(ngx_mail_conf_ctx_t **) conf) {\n        return \"is duplicate\";\n    }\n\n    /* the main mail context */\n\n    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_mail_conf_ctx_t));\n    if (ctx == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    *(ngx_mail_conf_ctx_t **) conf = ctx;\n\n    /* count the number of the mail modules and set up their indices */\n\n    ngx_mail_max_module = ngx_count_modules(cf->cycle, NGX_MAIL_MODULE);\n\n\n    /* the mail main_conf context, it is the same in the all mail contexts */\n\n    ctx->main_conf = ngx_pcalloc(cf->pool,\n                                 sizeof(void *) * ngx_mail_max_module);\n    if (ctx->main_conf == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n\n    /*\n     * the mail null srv_conf context, it is used to merge\n     * the server{}s' srv_conf's\n     */\n\n    ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_mail_max_module);\n    if (ctx->srv_conf == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n\n    /*\n     * create the main_conf's and the null srv_conf's of the all mail modules\n     */\n\n    for (m = 0; cf->cycle->modules[m]; m++) {\n        if (cf->cycle->modules[m]->type != NGX_MAIL_MODULE) {\n            continue;\n        }\n\n        module = cf->cycle->modules[m]->ctx;\n        mi = cf->cycle->modules[m]->ctx_index;\n\n        if (module->create_main_conf) {\n            ctx->main_conf[mi] = module->create_main_conf(cf);\n            if (ctx->main_conf[mi] == NULL) {\n                return NGX_CONF_ERROR;\n            }\n        }\n\n        if (module->create_srv_conf) {\n            ctx->srv_conf[mi] = module->create_srv_conf(cf);\n            if (ctx->srv_conf[mi] == NULL) {\n                return NGX_CONF_ERROR;\n            }\n        }\n    }\n\n\n    /* parse inside the mail{} block */\n\n    pcf = *cf;\n    cf->ctx = ctx;\n\n    cf->module_type = NGX_MAIL_MODULE;\n    cf->cmd_type = NGX_MAIL_MAIN_CONF;\n    rv = ngx_conf_parse(cf, NULL);\n\n    if (rv != NGX_CONF_OK) {\n        *cf = pcf;\n        return rv;\n    }\n\n\n    /* init mail{} main_conf's, merge the server{}s' srv_conf's */\n\n    cmcf = ctx->main_conf[ngx_mail_core_module.ctx_index];\n    cscfp = cmcf->servers.elts;\n\n    for (m = 0; cf->cycle->modules[m]; m++) {\n        if (cf->cycle->modules[m]->type != NGX_MAIL_MODULE) {\n            continue;\n        }\n\n        module = cf->cycle->modules[m]->ctx;\n        mi = cf->cycle->modules[m]->ctx_index;\n\n        /* init mail{} main_conf's */\n\n        cf->ctx = ctx;\n\n        if (module->init_main_conf) {\n            rv = module->init_main_conf(cf, ctx->main_conf[mi]);\n            if (rv != NGX_CONF_OK) {\n                *cf = pcf;\n                return rv;\n            }\n        }\n\n        for (s = 0; s < cmcf->servers.nelts; s++) {\n\n            /* merge the server{}s' srv_conf's */\n\n            cf->ctx = cscfp[s]->ctx;\n\n            if (module->merge_srv_conf) {\n                rv = module->merge_srv_conf(cf,\n                                            ctx->srv_conf[mi],\n                                            cscfp[s]->ctx->srv_conf[mi]);\n                if (rv != NGX_CONF_OK) {\n                    *cf = pcf;\n                    return rv;\n                }\n            }\n        }\n    }\n\n    *cf = pcf;\n\n\n    if (ngx_array_init(&ports, cf->temp_pool, 4, sizeof(ngx_mail_conf_port_t))\n        != NGX_OK)\n    {\n        return NGX_CONF_ERROR;\n    }\n\n    listen = cmcf->listen.elts;\n\n    for (i = 0; i < cmcf->listen.nelts; i++) {\n        if (ngx_mail_add_ports(cf, &ports, &listen[i]) != NGX_OK) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    return ngx_mail_optimize_servers(cf, &ports);\n}\n\n\nstatic ngx_int_t\nngx_mail_add_ports(ngx_conf_t *cf, ngx_array_t *ports,\n    ngx_mail_listen_t *listen)\n{\n    in_port_t              p;\n    ngx_uint_t             i;\n    struct sockaddr       *sa;\n    ngx_mail_conf_port_t  *port;\n    ngx_mail_conf_addr_t  *addr;\n\n    sa = listen->sockaddr;\n    p = ngx_inet_get_port(sa);\n\n    port = ports->elts;\n    for (i = 0; i < ports->nelts; i++) {\n        if (p == port[i].port && sa->sa_family == port[i].family) {\n\n            /* a port is already in the port list */\n\n            port = &port[i];\n            goto found;\n        }\n    }\n\n    /* add a port to the port list */\n\n    port = ngx_array_push(ports);\n    if (port == NULL) {\n        return NGX_ERROR;\n    }\n\n    port->family = sa->sa_family;\n    port->port = p;\n\n    if (ngx_array_init(&port->addrs, cf->temp_pool, 2,\n                       sizeof(ngx_mail_conf_addr_t))\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\nfound:\n\n    addr = ngx_array_push(&port->addrs);\n    if (addr == NULL) {\n        return NGX_ERROR;\n    }\n\n    addr->opt = *listen;\n\n    return NGX_OK;\n}\n\n\nstatic char *\nngx_mail_optimize_servers(ngx_conf_t *cf, ngx_array_t *ports)\n{\n    ngx_uint_t                 i, p, last, bind_wildcard;\n    ngx_listening_t           *ls;\n    ngx_mail_port_t           *mport;\n    ngx_mail_conf_port_t      *port;\n    ngx_mail_conf_addr_t      *addr;\n    ngx_mail_core_srv_conf_t  *cscf;\n\n    port = ports->elts;\n    for (p = 0; p < ports->nelts; p++) {\n\n        ngx_sort(port[p].addrs.elts, (size_t) port[p].addrs.nelts,\n                 sizeof(ngx_mail_conf_addr_t), ngx_mail_cmp_conf_addrs);\n\n        addr = port[p].addrs.elts;\n        last = port[p].addrs.nelts;\n\n        /*\n         * if there is the binding to the \"*:port\" then we need to bind()\n         * to the \"*:port\" only and ignore the other bindings\n         */\n\n        if (addr[last - 1].opt.wildcard) {\n            addr[last - 1].opt.bind = 1;\n            bind_wildcard = 1;\n\n        } else {\n            bind_wildcard = 0;\n        }\n\n        i = 0;\n\n        while (i < last) {\n\n            if (bind_wildcard && !addr[i].opt.bind) {\n                i++;\n                continue;\n            }\n\n            ls = ngx_create_listening(cf, addr[i].opt.sockaddr,\n                                      addr[i].opt.socklen);\n            if (ls == NULL) {\n                return NGX_CONF_ERROR;\n            }\n\n            ls->addr_ntop = 1;\n            ls->handler = ngx_mail_init_connection;\n            ls->pool_size = 256;\n\n            cscf = addr->opt.ctx->srv_conf[ngx_mail_core_module.ctx_index];\n\n            ls->logp = cscf->error_log;\n            ls->log.data = &ls->addr_text;\n            ls->log.handler = ngx_accept_log_error;\n\n            ls->backlog = addr[i].opt.backlog;\n            ls->rcvbuf = addr[i].opt.rcvbuf;\n            ls->sndbuf = addr[i].opt.sndbuf;\n\n            ls->keepalive = addr[i].opt.so_keepalive;\n#if (NGX_HAVE_KEEPALIVE_TUNABLE)\n            ls->keepidle = addr[i].opt.tcp_keepidle;\n            ls->keepintvl = addr[i].opt.tcp_keepintvl;\n            ls->keepcnt = addr[i].opt.tcp_keepcnt;\n#endif\n\n#if (NGX_HAVE_INET6)\n            ls->ipv6only = addr[i].opt.ipv6only;\n#endif\n\n            mport = ngx_palloc(cf->pool, sizeof(ngx_mail_port_t));\n            if (mport == NULL) {\n                return NGX_CONF_ERROR;\n            }\n\n            ls->servers = mport;\n\n            mport->naddrs = i + 1;\n\n            switch (ls->sockaddr->sa_family) {\n#if (NGX_HAVE_INET6)\n            case AF_INET6:\n                if (ngx_mail_add_addrs6(cf, mport, addr) != NGX_OK) {\n                    return NGX_CONF_ERROR;\n                }\n                break;\n#endif\n            default: /* AF_INET */\n                if (ngx_mail_add_addrs(cf, mport, addr) != NGX_OK) {\n                    return NGX_CONF_ERROR;\n                }\n                break;\n            }\n\n            addr++;\n            last--;\n        }\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_mail_add_addrs(ngx_conf_t *cf, ngx_mail_port_t *mport,\n    ngx_mail_conf_addr_t *addr)\n{\n    ngx_uint_t           i;\n    ngx_mail_in_addr_t  *addrs;\n    struct sockaddr_in  *sin;\n\n    mport->addrs = ngx_pcalloc(cf->pool,\n                               mport->naddrs * sizeof(ngx_mail_in_addr_t));\n    if (mport->addrs == NULL) {\n        return NGX_ERROR;\n    }\n\n    addrs = mport->addrs;\n\n    for (i = 0; i < mport->naddrs; i++) {\n\n        sin = (struct sockaddr_in *) addr[i].opt.sockaddr;\n        addrs[i].addr = sin->sin_addr.s_addr;\n\n        addrs[i].conf.ctx = addr[i].opt.ctx;\n#if (NGX_MAIL_SSL)\n        addrs[i].conf.ssl = addr[i].opt.ssl;\n#endif\n        addrs[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;\n        addrs[i].conf.addr_text = addr[i].opt.addr_text;\n    }\n\n    return NGX_OK;\n}\n\n\n#if (NGX_HAVE_INET6)\n\nstatic ngx_int_t\nngx_mail_add_addrs6(ngx_conf_t *cf, ngx_mail_port_t *mport,\n    ngx_mail_conf_addr_t *addr)\n{\n    ngx_uint_t            i;\n    ngx_mail_in6_addr_t  *addrs6;\n    struct sockaddr_in6  *sin6;\n\n    mport->addrs = ngx_pcalloc(cf->pool,\n                               mport->naddrs * sizeof(ngx_mail_in6_addr_t));\n    if (mport->addrs == NULL) {\n        return NGX_ERROR;\n    }\n\n    addrs6 = mport->addrs;\n\n    for (i = 0; i < mport->naddrs; i++) {\n\n        sin6 = (struct sockaddr_in6 *) addr[i].opt.sockaddr;\n        addrs6[i].addr6 = sin6->sin6_addr;\n\n        addrs6[i].conf.ctx = addr[i].opt.ctx;\n#if (NGX_MAIL_SSL)\n        addrs6[i].conf.ssl = addr[i].opt.ssl;\n#endif\n        addrs6[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;\n        addrs6[i].conf.addr_text = addr[i].opt.addr_text;\n    }\n\n    return NGX_OK;\n}\n\n#endif\n\n\nstatic ngx_int_t\nngx_mail_cmp_conf_addrs(const void *one, const void *two)\n{\n    ngx_mail_conf_addr_t  *first, *second;\n\n    first = (ngx_mail_conf_addr_t *) one;\n    second = (ngx_mail_conf_addr_t *) two;\n\n    if (first->opt.wildcard) {\n        /* a wildcard must be the last resort, shift it to the end */\n        return 1;\n    }\n\n    if (second->opt.wildcard) {\n        /* a wildcard must be the last resort, shift it to the end */\n        return -1;\n    }\n\n    if (first->opt.bind && !second->opt.bind) {\n        /* shift explicit bind()ed addresses to the start */\n        return -1;\n    }\n\n    if (!first->opt.bind && second->opt.bind) {\n        /* shift explicit bind()ed addresses to the start */\n        return 1;\n    }\n\n    /* do not sort by default */\n\n    return 0;\n}\n"
  },
  {
    "path": "src/mail/ngx_mail.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_MAIL_H_INCLUDED_\n#define _NGX_MAIL_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n#include <ngx_event_connect.h>\n\n#if (NGX_MAIL_SSL)\n#include <ngx_mail_ssl_module.h>\n#endif\n\n\n\ntypedef struct {\n    void                  **main_conf;\n    void                  **srv_conf;\n} ngx_mail_conf_ctx_t;\n\n\ntypedef struct {\n    struct sockaddr        *sockaddr;\n    socklen_t               socklen;\n    ngx_str_t               addr_text;\n\n    /* server ctx */\n    ngx_mail_conf_ctx_t    *ctx;\n\n    unsigned                bind:1;\n    unsigned                wildcard:1;\n    unsigned                ssl:1;\n#if (NGX_HAVE_INET6)\n    unsigned                ipv6only:1;\n#endif\n    unsigned                so_keepalive:2;\n    unsigned                proxy_protocol:1;\n#if (NGX_HAVE_KEEPALIVE_TUNABLE)\n    int                     tcp_keepidle;\n    int                     tcp_keepintvl;\n    int                     tcp_keepcnt;\n#endif\n    int                     backlog;\n    int                     rcvbuf;\n    int                     sndbuf;\n} ngx_mail_listen_t;\n\n\ntypedef struct {\n    ngx_mail_conf_ctx_t    *ctx;\n    ngx_str_t               addr_text;\n    unsigned                ssl:1;\n    unsigned                proxy_protocol:1;\n} ngx_mail_addr_conf_t;\n\ntypedef struct {\n    in_addr_t               addr;\n    ngx_mail_addr_conf_t    conf;\n} ngx_mail_in_addr_t;\n\n\n#if (NGX_HAVE_INET6)\n\ntypedef struct {\n    struct in6_addr         addr6;\n    ngx_mail_addr_conf_t    conf;\n} ngx_mail_in6_addr_t;\n\n#endif\n\n\ntypedef struct {\n    /* ngx_mail_in_addr_t or ngx_mail_in6_addr_t */\n    void                   *addrs;\n    ngx_uint_t              naddrs;\n} ngx_mail_port_t;\n\n\ntypedef struct {\n    int                     family;\n    in_port_t               port;\n    ngx_array_t             addrs;       /* array of ngx_mail_conf_addr_t */\n} ngx_mail_conf_port_t;\n\n\ntypedef struct {\n    ngx_mail_listen_t       opt;\n} ngx_mail_conf_addr_t;\n\n\ntypedef struct {\n    ngx_array_t             servers;     /* ngx_mail_core_srv_conf_t */\n    ngx_array_t             listen;      /* ngx_mail_listen_t */\n} ngx_mail_core_main_conf_t;\n\n\n#define NGX_MAIL_POP3_PROTOCOL  0\n#define NGX_MAIL_IMAP_PROTOCOL  1\n#define NGX_MAIL_SMTP_PROTOCOL  2\n\n\ntypedef struct ngx_mail_protocol_s  ngx_mail_protocol_t;\n\n\ntypedef struct {\n    ngx_mail_protocol_t    *protocol;\n\n    ngx_msec_t              timeout;\n    ngx_msec_t              resolver_timeout;\n\n    ngx_uint_t              max_errors;\n\n    ngx_str_t               server_name;\n\n    u_char                 *file_name;\n    ngx_uint_t              line;\n\n    ngx_resolver_t         *resolver;\n    ngx_log_t              *error_log;\n\n    /* server ctx */\n    ngx_mail_conf_ctx_t    *ctx;\n\n    ngx_uint_t              listen;  /* unsigned  listen:1; */\n} ngx_mail_core_srv_conf_t;\n\n\ntypedef enum {\n    ngx_pop3_start = 0,\n    ngx_pop3_user,\n    ngx_pop3_passwd,\n    ngx_pop3_auth_login_username,\n    ngx_pop3_auth_login_password,\n    ngx_pop3_auth_plain,\n    ngx_pop3_auth_cram_md5,\n    ngx_pop3_auth_external\n} ngx_pop3_state_e;\n\n\ntypedef enum {\n    ngx_imap_start = 0,\n    ngx_imap_auth_login_username,\n    ngx_imap_auth_login_password,\n    ngx_imap_auth_plain,\n    ngx_imap_auth_cram_md5,\n    ngx_imap_auth_external,\n    ngx_imap_login,\n    ngx_imap_user,\n    ngx_imap_passwd\n} ngx_imap_state_e;\n\n\ntypedef enum {\n    ngx_smtp_start = 0,\n    ngx_smtp_auth_login_username,\n    ngx_smtp_auth_login_password,\n    ngx_smtp_auth_plain,\n    ngx_smtp_auth_cram_md5,\n    ngx_smtp_auth_external,\n    ngx_smtp_helo,\n    ngx_smtp_helo_xclient,\n    ngx_smtp_helo_auth,\n    ngx_smtp_helo_from,\n    ngx_smtp_xclient,\n    ngx_smtp_xclient_from,\n    ngx_smtp_xclient_helo,\n    ngx_smtp_xclient_auth,\n    ngx_smtp_from,\n    ngx_smtp_to\n} ngx_smtp_state_e;\n\n\ntypedef struct {\n    ngx_peer_connection_t   upstream;\n    ngx_buf_t              *buffer;\n    ngx_uint_t              proxy_protocol;  /* unsigned  proxy_protocol:1; */\n} ngx_mail_proxy_ctx_t;\n\n\ntypedef struct {\n    uint32_t                signature;         /* \"MAIL\" */\n\n    ngx_connection_t       *connection;\n\n    ngx_str_t               out;\n    ngx_buf_t              *buffer;\n\n    void                  **ctx;\n    void                  **main_conf;\n    void                  **srv_conf;\n\n    ngx_resolver_ctx_t     *resolver_ctx;\n\n    ngx_mail_proxy_ctx_t   *proxy;\n\n    ngx_uint_t              mail_state;\n\n    unsigned                ssl:1;\n    unsigned                protocol:3;\n    unsigned                blocked:1;\n    unsigned                quit:1;\n    unsigned                quoted:1;\n    unsigned                backslash:1;\n    unsigned                no_sync_literal:1;\n    unsigned                starttls:1;\n    unsigned                esmtp:1;\n    unsigned                auth_method:3;\n    unsigned                auth_wait:1;\n\n    ngx_str_t               login;\n    ngx_str_t               passwd;\n\n    ngx_str_t               salt;\n    ngx_str_t               tag;\n    ngx_str_t               tagged_line;\n    ngx_str_t               text;\n\n    ngx_str_t              *addr_text;\n    ngx_str_t               host;\n    ngx_str_t               smtp_helo;\n    ngx_str_t               smtp_from;\n    ngx_str_t               smtp_to;\n\n    ngx_str_t               cmd;\n\n    ngx_uint_t              command;\n    ngx_array_t             args;\n\n    ngx_uint_t              errors;\n    ngx_uint_t              login_attempt;\n\n    /* used to parse POP3/IMAP/SMTP command */\n\n    ngx_uint_t              state;\n    u_char                 *tag_start;\n    u_char                 *cmd_start;\n    u_char                 *arg_start;\n    ngx_uint_t              literal_len;\n} ngx_mail_session_t;\n\n\ntypedef struct {\n    ngx_str_t              *client;\n    ngx_mail_session_t     *session;\n} ngx_mail_log_ctx_t;\n\n\n#define NGX_POP3_USER          1\n#define NGX_POP3_PASS          2\n#define NGX_POP3_CAPA          3\n#define NGX_POP3_QUIT          4\n#define NGX_POP3_NOOP          5\n#define NGX_POP3_STLS          6\n#define NGX_POP3_APOP          7\n#define NGX_POP3_AUTH          8\n#define NGX_POP3_STAT          9\n#define NGX_POP3_LIST          10\n#define NGX_POP3_RETR          11\n#define NGX_POP3_DELE          12\n#define NGX_POP3_RSET          13\n#define NGX_POP3_TOP           14\n#define NGX_POP3_UIDL          15\n\n\n#define NGX_IMAP_LOGIN         1\n#define NGX_IMAP_LOGOUT        2\n#define NGX_IMAP_CAPABILITY    3\n#define NGX_IMAP_NOOP          4\n#define NGX_IMAP_STARTTLS      5\n\n#define NGX_IMAP_NEXT          6\n\n#define NGX_IMAP_AUTHENTICATE  7\n\n\n#define NGX_SMTP_HELO          1\n#define NGX_SMTP_EHLO          2\n#define NGX_SMTP_AUTH          3\n#define NGX_SMTP_QUIT          4\n#define NGX_SMTP_NOOP          5\n#define NGX_SMTP_MAIL          6\n#define NGX_SMTP_RSET          7\n#define NGX_SMTP_RCPT          8\n#define NGX_SMTP_DATA          9\n#define NGX_SMTP_VRFY          10\n#define NGX_SMTP_EXPN          11\n#define NGX_SMTP_HELP          12\n#define NGX_SMTP_STARTTLS      13\n\n\n#define NGX_MAIL_AUTH_PLAIN             0\n#define NGX_MAIL_AUTH_LOGIN             1\n#define NGX_MAIL_AUTH_LOGIN_USERNAME    2\n#define NGX_MAIL_AUTH_APOP              3\n#define NGX_MAIL_AUTH_CRAM_MD5          4\n#define NGX_MAIL_AUTH_EXTERNAL          5\n#define NGX_MAIL_AUTH_NONE              6\n\n\n#define NGX_MAIL_AUTH_PLAIN_ENABLED     0x0002\n#define NGX_MAIL_AUTH_LOGIN_ENABLED     0x0004\n#define NGX_MAIL_AUTH_APOP_ENABLED      0x0008\n#define NGX_MAIL_AUTH_CRAM_MD5_ENABLED  0x0010\n#define NGX_MAIL_AUTH_EXTERNAL_ENABLED  0x0020\n#define NGX_MAIL_AUTH_NONE_ENABLED      0x0040\n\n\n#define NGX_MAIL_PARSE_INVALID_COMMAND  20\n\n\ntypedef void (*ngx_mail_init_session_pt)(ngx_mail_session_t *s,\n    ngx_connection_t *c);\ntypedef void (*ngx_mail_init_protocol_pt)(ngx_event_t *rev);\ntypedef void (*ngx_mail_auth_state_pt)(ngx_event_t *rev);\ntypedef ngx_int_t (*ngx_mail_parse_command_pt)(ngx_mail_session_t *s);\n\n\nstruct ngx_mail_protocol_s {\n    ngx_str_t                   name;\n    ngx_str_t                   alpn;\n    in_port_t                   port[4];\n    ngx_uint_t                  type;\n\n    ngx_mail_init_session_pt    init_session;\n    ngx_mail_init_protocol_pt   init_protocol;\n    ngx_mail_parse_command_pt   parse_command;\n    ngx_mail_auth_state_pt      auth_state;\n\n    ngx_str_t                   internal_server_error;\n    ngx_str_t                   cert_error;\n    ngx_str_t                   no_cert;\n};\n\n\ntypedef struct {\n    ngx_mail_protocol_t        *protocol;\n\n    void                       *(*create_main_conf)(ngx_conf_t *cf);\n    char                       *(*init_main_conf)(ngx_conf_t *cf, void *conf);\n\n    void                       *(*create_srv_conf)(ngx_conf_t *cf);\n    char                       *(*merge_srv_conf)(ngx_conf_t *cf, void *prev,\n                                                  void *conf);\n} ngx_mail_module_t;\n\n\n#define NGX_MAIL_MODULE         0x4C49414D     /* \"MAIL\" */\n\n#define NGX_MAIL_MAIN_CONF      0x02000000\n#define NGX_MAIL_SRV_CONF       0x04000000\n\n\n#define NGX_MAIL_MAIN_CONF_OFFSET  offsetof(ngx_mail_conf_ctx_t, main_conf)\n#define NGX_MAIL_SRV_CONF_OFFSET   offsetof(ngx_mail_conf_ctx_t, srv_conf)\n\n\n#define ngx_mail_get_module_ctx(s, module)     (s)->ctx[module.ctx_index]\n#define ngx_mail_set_ctx(s, c, module)         s->ctx[module.ctx_index] = c;\n#define ngx_mail_delete_ctx(s, module)         s->ctx[module.ctx_index] = NULL;\n\n\n#define ngx_mail_get_module_main_conf(s, module)                             \\\n    (s)->main_conf[module.ctx_index]\n#define ngx_mail_get_module_srv_conf(s, module)  (s)->srv_conf[module.ctx_index]\n\n#define ngx_mail_conf_get_module_main_conf(cf, module)                       \\\n    ((ngx_mail_conf_ctx_t *) cf->ctx)->main_conf[module.ctx_index]\n#define ngx_mail_conf_get_module_srv_conf(cf, module)                        \\\n    ((ngx_mail_conf_ctx_t *) cf->ctx)->srv_conf[module.ctx_index]\n\n\n#if (NGX_MAIL_SSL)\nvoid ngx_mail_starttls_handler(ngx_event_t *rev);\nngx_int_t ngx_mail_starttls_only(ngx_mail_session_t *s, ngx_connection_t *c);\n#endif\n\n\nvoid ngx_mail_init_connection(ngx_connection_t *c);\n\nngx_int_t ngx_mail_salt(ngx_mail_session_t *s, ngx_connection_t *c,\n    ngx_mail_core_srv_conf_t *cscf);\nngx_int_t ngx_mail_auth_plain(ngx_mail_session_t *s, ngx_connection_t *c,\n    ngx_uint_t n);\nngx_int_t ngx_mail_auth_login_username(ngx_mail_session_t *s,\n    ngx_connection_t *c, ngx_uint_t n);\nngx_int_t ngx_mail_auth_login_password(ngx_mail_session_t *s,\n    ngx_connection_t *c);\nngx_int_t ngx_mail_auth_cram_md5_salt(ngx_mail_session_t *s,\n    ngx_connection_t *c, char *prefix, size_t len);\nngx_int_t ngx_mail_auth_cram_md5(ngx_mail_session_t *s, ngx_connection_t *c);\nngx_int_t ngx_mail_auth_external(ngx_mail_session_t *s, ngx_connection_t *c,\n    ngx_uint_t n);\nngx_int_t ngx_mail_auth_parse(ngx_mail_session_t *s, ngx_connection_t *c);\n\nvoid ngx_mail_send(ngx_event_t *wev);\nngx_int_t ngx_mail_read_command(ngx_mail_session_t *s, ngx_connection_t *c);\nvoid ngx_mail_auth(ngx_mail_session_t *s, ngx_connection_t *c);\nvoid ngx_mail_close_connection(ngx_connection_t *c);\nvoid ngx_mail_session_internal_server_error(ngx_mail_session_t *s);\nu_char *ngx_mail_log_error(ngx_log_t *log, u_char *buf, size_t len);\n\n\nchar *ngx_mail_capabilities(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\n\n\n/* STUB */\nvoid ngx_mail_proxy_init(ngx_mail_session_t *s, ngx_addr_t *peer);\nvoid ngx_mail_auth_http_init(ngx_mail_session_t *s);\nngx_int_t ngx_mail_realip_handler(ngx_mail_session_t *s);\n/**/\n\n\nextern ngx_uint_t    ngx_mail_max_module;\nextern ngx_module_t  ngx_mail_core_module;\n\n\n#endif /* _NGX_MAIL_H_INCLUDED_ */\n"
  },
  {
    "path": "src/mail/ngx_mail_auth_http_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n#include <ngx_event_connect.h>\n#include <ngx_mail.h>\n\n\ntypedef struct {\n    ngx_addr_t                     *peer;\n\n    ngx_msec_t                      timeout;\n    ngx_flag_t                      pass_client_cert;\n\n    ngx_str_t                       host_header;\n    ngx_str_t                       uri;\n    ngx_str_t                       header;\n\n    ngx_array_t                    *headers;\n\n    u_char                         *file;\n    ngx_uint_t                      line;\n} ngx_mail_auth_http_conf_t;\n\n\ntypedef struct ngx_mail_auth_http_ctx_s  ngx_mail_auth_http_ctx_t;\n\ntypedef void (*ngx_mail_auth_http_handler_pt)(ngx_mail_session_t *s,\n    ngx_mail_auth_http_ctx_t *ctx);\n\nstruct ngx_mail_auth_http_ctx_s {\n    ngx_buf_t                      *request;\n    ngx_buf_t                      *response;\n    ngx_peer_connection_t           peer;\n\n    ngx_mail_auth_http_handler_pt   handler;\n\n    ngx_uint_t                      state;\n\n    u_char                         *header_name_start;\n    u_char                         *header_name_end;\n    u_char                         *header_start;\n    u_char                         *header_end;\n\n    ngx_str_t                       addr;\n    ngx_str_t                       port;\n    ngx_str_t                       err;\n    ngx_str_t                       errmsg;\n    ngx_str_t                       errcode;\n\n    time_t                          sleep;\n\n    ngx_pool_t                     *pool;\n};\n\n\nstatic void ngx_mail_auth_http_write_handler(ngx_event_t *wev);\nstatic void ngx_mail_auth_http_read_handler(ngx_event_t *rev);\nstatic void ngx_mail_auth_http_ignore_status_line(ngx_mail_session_t *s,\n    ngx_mail_auth_http_ctx_t *ctx);\nstatic void ngx_mail_auth_http_process_headers(ngx_mail_session_t *s,\n    ngx_mail_auth_http_ctx_t *ctx);\nstatic void ngx_mail_auth_sleep_handler(ngx_event_t *rev);\nstatic ngx_int_t ngx_mail_auth_http_parse_header_line(ngx_mail_session_t *s,\n    ngx_mail_auth_http_ctx_t *ctx);\nstatic void ngx_mail_auth_http_block_read(ngx_event_t *rev);\nstatic void ngx_mail_auth_http_dummy_handler(ngx_event_t *ev);\nstatic ngx_buf_t *ngx_mail_auth_http_create_request(ngx_mail_session_t *s,\n    ngx_pool_t *pool, ngx_mail_auth_http_conf_t *ahcf);\nstatic ngx_int_t ngx_mail_auth_http_escape(ngx_pool_t *pool, ngx_str_t *text,\n    ngx_str_t *escaped);\n\nstatic void *ngx_mail_auth_http_create_conf(ngx_conf_t *cf);\nstatic char *ngx_mail_auth_http_merge_conf(ngx_conf_t *cf, void *parent,\n    void *child);\nstatic char *ngx_mail_auth_http(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\nstatic char *ngx_mail_auth_http_header(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n\n\nstatic ngx_command_t  ngx_mail_auth_http_commands[] = {\n\n    { ngx_string(\"auth_http\"),\n      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_mail_auth_http,\n      NGX_MAIL_SRV_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"auth_http_timeout\"),\n      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_MAIL_SRV_CONF_OFFSET,\n      offsetof(ngx_mail_auth_http_conf_t, timeout),\n      NULL },\n\n    { ngx_string(\"auth_http_header\"),\n      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE2,\n      ngx_mail_auth_http_header,\n      NGX_MAIL_SRV_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"auth_http_pass_client_cert\"),\n      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_MAIL_SRV_CONF_OFFSET,\n      offsetof(ngx_mail_auth_http_conf_t, pass_client_cert),\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_mail_module_t  ngx_mail_auth_http_module_ctx = {\n    NULL,                                  /* protocol */\n\n    NULL,                                  /* create main configuration */\n    NULL,                                  /* init main configuration */\n\n    ngx_mail_auth_http_create_conf,        /* create server configuration */\n    ngx_mail_auth_http_merge_conf          /* merge server configuration */\n};\n\n\nngx_module_t  ngx_mail_auth_http_module = {\n    NGX_MODULE_V1,\n    &ngx_mail_auth_http_module_ctx,        /* module context */\n    ngx_mail_auth_http_commands,           /* module directives */\n    NGX_MAIL_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_str_t   ngx_mail_auth_http_method[] = {\n    ngx_string(\"plain\"),\n    ngx_string(\"plain\"),\n    ngx_string(\"plain\"),\n    ngx_string(\"apop\"),\n    ngx_string(\"cram-md5\"),\n    ngx_string(\"external\"),\n    ngx_string(\"none\")\n};\n\nstatic ngx_str_t   ngx_mail_smtp_errcode = ngx_string(\"535 5.7.0\");\n\n\nvoid\nngx_mail_auth_http_init(ngx_mail_session_t *s)\n{\n    ngx_int_t                   rc;\n    ngx_pool_t                 *pool;\n    ngx_mail_auth_http_ctx_t   *ctx;\n    ngx_mail_auth_http_conf_t  *ahcf;\n\n    s->connection->log->action = \"in http auth state\";\n\n    pool = ngx_create_pool(2048, s->connection->log);\n    if (pool == NULL) {\n        ngx_mail_session_internal_server_error(s);\n        return;\n    }\n\n    ctx = ngx_pcalloc(pool, sizeof(ngx_mail_auth_http_ctx_t));\n    if (ctx == NULL) {\n        ngx_destroy_pool(pool);\n        ngx_mail_session_internal_server_error(s);\n        return;\n    }\n\n    ctx->pool = pool;\n\n    ahcf = ngx_mail_get_module_srv_conf(s, ngx_mail_auth_http_module);\n\n    ctx->request = ngx_mail_auth_http_create_request(s, pool, ahcf);\n    if (ctx->request == NULL) {\n        ngx_destroy_pool(ctx->pool);\n        ngx_mail_session_internal_server_error(s);\n        return;\n    }\n\n    ngx_mail_set_ctx(s, ctx, ngx_mail_auth_http_module);\n\n    ctx->peer.sockaddr = ahcf->peer->sockaddr;\n    ctx->peer.socklen = ahcf->peer->socklen;\n    ctx->peer.name = &ahcf->peer->name;\n    ctx->peer.get = ngx_event_get_peer;\n    ctx->peer.log = s->connection->log;\n    ctx->peer.log_error = NGX_ERROR_ERR;\n\n    rc = ngx_event_connect_peer(&ctx->peer);\n\n    if (rc == NGX_ERROR || rc == NGX_BUSY || rc == NGX_DECLINED) {\n        if (ctx->peer.connection) {\n            ngx_close_connection(ctx->peer.connection);\n        }\n\n        ngx_destroy_pool(ctx->pool);\n        ngx_mail_session_internal_server_error(s);\n        return;\n    }\n\n    ctx->peer.connection->data = s;\n    ctx->peer.connection->pool = s->connection->pool;\n\n    s->connection->read->handler = ngx_mail_auth_http_block_read;\n    ctx->peer.connection->read->handler = ngx_mail_auth_http_read_handler;\n    ctx->peer.connection->write->handler = ngx_mail_auth_http_write_handler;\n\n    ctx->handler = ngx_mail_auth_http_ignore_status_line;\n\n    ngx_add_timer(ctx->peer.connection->read, ahcf->timeout);\n    ngx_add_timer(ctx->peer.connection->write, ahcf->timeout);\n\n    if (rc == NGX_OK) {\n        ngx_mail_auth_http_write_handler(ctx->peer.connection->write);\n        return;\n    }\n}\n\n\nstatic void\nngx_mail_auth_http_write_handler(ngx_event_t *wev)\n{\n    ssize_t                     n, size;\n    ngx_connection_t           *c;\n    ngx_mail_session_t         *s;\n    ngx_mail_auth_http_ctx_t   *ctx;\n    ngx_mail_auth_http_conf_t  *ahcf;\n\n    c = wev->data;\n    s = c->data;\n\n    ctx = ngx_mail_get_module_ctx(s, ngx_mail_auth_http_module);\n\n    ngx_log_debug0(NGX_LOG_DEBUG_MAIL, wev->log, 0,\n                   \"mail auth http write handler\");\n\n    if (wev->timedout) {\n        ngx_log_error(NGX_LOG_ERR, wev->log, NGX_ETIMEDOUT,\n                      \"auth http server %V timed out\", ctx->peer.name);\n        ngx_close_connection(c);\n        ngx_destroy_pool(ctx->pool);\n        ngx_mail_session_internal_server_error(s);\n        return;\n    }\n\n    size = ctx->request->last - ctx->request->pos;\n\n    n = ngx_send(c, ctx->request->pos, size);\n\n    if (n == NGX_ERROR) {\n        ngx_close_connection(c);\n        ngx_destroy_pool(ctx->pool);\n        ngx_mail_session_internal_server_error(s);\n        return;\n    }\n\n    if (n > 0) {\n        ctx->request->pos += n;\n\n        if (n == size) {\n            wev->handler = ngx_mail_auth_http_dummy_handler;\n\n            if (wev->timer_set) {\n                ngx_del_timer(wev);\n            }\n\n            if (ngx_handle_write_event(wev, 0) != NGX_OK) {\n                ngx_close_connection(c);\n                ngx_destroy_pool(ctx->pool);\n                ngx_mail_session_internal_server_error(s);\n            }\n\n            return;\n        }\n    }\n\n    if (!wev->timer_set) {\n        ahcf = ngx_mail_get_module_srv_conf(s, ngx_mail_auth_http_module);\n        ngx_add_timer(wev, ahcf->timeout);\n    }\n}\n\n\nstatic void\nngx_mail_auth_http_read_handler(ngx_event_t *rev)\n{\n    ssize_t                     n, size;\n    ngx_connection_t          *c;\n    ngx_mail_session_t        *s;\n    ngx_mail_auth_http_ctx_t  *ctx;\n\n    c = rev->data;\n    s = c->data;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,\n                   \"mail auth http read handler\");\n\n    ctx = ngx_mail_get_module_ctx(s, ngx_mail_auth_http_module);\n\n    if (rev->timedout) {\n        ngx_log_error(NGX_LOG_ERR, rev->log, NGX_ETIMEDOUT,\n                      \"auth http server %V timed out\", ctx->peer.name);\n        ngx_close_connection(c);\n        ngx_destroy_pool(ctx->pool);\n        ngx_mail_session_internal_server_error(s);\n        return;\n    }\n\n    if (ctx->response == NULL) {\n        ctx->response = ngx_create_temp_buf(ctx->pool, 1024);\n        if (ctx->response == NULL) {\n            ngx_close_connection(c);\n            ngx_destroy_pool(ctx->pool);\n            ngx_mail_session_internal_server_error(s);\n            return;\n        }\n    }\n\n    size = ctx->response->end - ctx->response->last;\n\n    n = ngx_recv(c, ctx->response->pos, size);\n\n    if (n > 0) {\n        ctx->response->last += n;\n\n        ctx->handler(s, ctx);\n        return;\n    }\n\n    if (n == NGX_AGAIN) {\n        return;\n    }\n\n    ngx_close_connection(c);\n    ngx_destroy_pool(ctx->pool);\n    ngx_mail_session_internal_server_error(s);\n}\n\n\nstatic void\nngx_mail_auth_http_ignore_status_line(ngx_mail_session_t *s,\n    ngx_mail_auth_http_ctx_t *ctx)\n{\n    u_char  *p, ch;\n    enum  {\n        sw_start = 0,\n        sw_H,\n        sw_HT,\n        sw_HTT,\n        sw_HTTP,\n        sw_skip,\n        sw_almost_done\n    } state;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,\n                   \"mail auth http process status line\");\n\n    state = ctx->state;\n\n    for (p = ctx->response->pos; p < ctx->response->last; p++) {\n        ch = *p;\n\n        switch (state) {\n\n        /* \"HTTP/\" */\n        case sw_start:\n            if (ch == 'H') {\n                state = sw_H;\n                break;\n            }\n            goto next;\n\n        case sw_H:\n            if (ch == 'T') {\n                state = sw_HT;\n                break;\n            }\n            goto next;\n\n        case sw_HT:\n            if (ch == 'T') {\n                state = sw_HTT;\n                break;\n            }\n            goto next;\n\n        case sw_HTT:\n            if (ch == 'P') {\n                state = sw_HTTP;\n                break;\n            }\n            goto next;\n\n        case sw_HTTP:\n            if (ch == '/') {\n                state = sw_skip;\n                break;\n            }\n            goto next;\n\n        /* any text until end of line */\n        case sw_skip:\n            switch (ch) {\n            case CR:\n                state = sw_almost_done;\n\n                break;\n            case LF:\n                goto done;\n            }\n            break;\n\n        /* end of status line */\n        case sw_almost_done:\n            if (ch == LF) {\n                goto done;\n            }\n\n            ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,\n                          \"auth http server %V sent invalid response\",\n                          ctx->peer.name);\n            ngx_close_connection(ctx->peer.connection);\n            ngx_destroy_pool(ctx->pool);\n            ngx_mail_session_internal_server_error(s);\n            return;\n        }\n    }\n\n    ctx->response->pos = p;\n    ctx->state = state;\n\n    return;\n\nnext:\n\n    p = ctx->response->start - 1;\n\ndone:\n\n    ctx->response->pos = p + 1;\n    ctx->state = 0;\n    ctx->handler = ngx_mail_auth_http_process_headers;\n    ctx->handler(s, ctx);\n}\n\n\nstatic void\nngx_mail_auth_http_process_headers(ngx_mail_session_t *s,\n    ngx_mail_auth_http_ctx_t *ctx)\n{\n    u_char      *p;\n    time_t       timer;\n    size_t       len, size;\n    ngx_int_t    rc, port, n;\n    ngx_addr_t  *peer;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,\n                   \"mail auth http process headers\");\n\n    for ( ;; ) {\n        rc = ngx_mail_auth_http_parse_header_line(s, ctx);\n\n        if (rc == NGX_OK) {\n\n#if (NGX_DEBUG)\n            {\n            ngx_str_t  key, value;\n\n            key.len = ctx->header_name_end - ctx->header_name_start;\n            key.data = ctx->header_name_start;\n            value.len = ctx->header_end - ctx->header_start;\n            value.data = ctx->header_start;\n\n            ngx_log_debug2(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,\n                           \"mail auth http header: \\\"%V: %V\\\"\",\n                           &key, &value);\n            }\n#endif\n\n            len = ctx->header_name_end - ctx->header_name_start;\n\n            if (len == sizeof(\"Auth-Status\") - 1\n                && ngx_strncasecmp(ctx->header_name_start,\n                                   (u_char *) \"Auth-Status\",\n                                   sizeof(\"Auth-Status\") - 1)\n                   == 0)\n            {\n                len = ctx->header_end - ctx->header_start;\n\n                if (len == 2\n                    && ctx->header_start[0] == 'O'\n                    && ctx->header_start[1] == 'K')\n                {\n                    continue;\n                }\n\n                if (len == 4\n                    && ctx->header_start[0] == 'W'\n                    && ctx->header_start[1] == 'A'\n                    && ctx->header_start[2] == 'I'\n                    && ctx->header_start[3] == 'T')\n                {\n                    s->auth_wait = 1;\n                    continue;\n                }\n\n                ctx->errmsg.len = len;\n                ctx->errmsg.data = ctx->header_start;\n\n                switch (s->protocol) {\n\n                case NGX_MAIL_POP3_PROTOCOL:\n                    size = sizeof(\"-ERR \") - 1 + len + sizeof(CRLF) - 1;\n                    break;\n\n                case NGX_MAIL_IMAP_PROTOCOL:\n                    size = s->tag.len + sizeof(\"NO \") - 1 + len\n                           + sizeof(CRLF) - 1;\n                    break;\n\n                default: /* NGX_MAIL_SMTP_PROTOCOL */\n                    ctx->err = ctx->errmsg;\n                    continue;\n                }\n\n                p = ngx_pnalloc(s->connection->pool, size);\n                if (p == NULL) {\n                    ngx_close_connection(ctx->peer.connection);\n                    ngx_destroy_pool(ctx->pool);\n                    ngx_mail_session_internal_server_error(s);\n                    return;\n                }\n\n                ctx->err.data = p;\n\n                switch (s->protocol) {\n\n                case NGX_MAIL_POP3_PROTOCOL:\n                    *p++ = '-'; *p++ = 'E'; *p++ = 'R'; *p++ = 'R'; *p++ = ' ';\n                    break;\n\n                case NGX_MAIL_IMAP_PROTOCOL:\n                    p = ngx_cpymem(p, s->tag.data, s->tag.len);\n                    *p++ = 'N'; *p++ = 'O'; *p++ = ' ';\n                    break;\n\n                default: /* NGX_MAIL_SMTP_PROTOCOL */\n                    break;\n                }\n\n                p = ngx_cpymem(p, ctx->header_start, len);\n                *p++ = CR; *p++ = LF;\n\n                ctx->err.len = p - ctx->err.data;\n\n                continue;\n            }\n\n            if (len == sizeof(\"Auth-Server\") - 1\n                && ngx_strncasecmp(ctx->header_name_start,\n                                   (u_char *) \"Auth-Server\",\n                                   sizeof(\"Auth-Server\") - 1)\n                    == 0)\n            {\n                ctx->addr.len = ctx->header_end - ctx->header_start;\n                ctx->addr.data = ctx->header_start;\n\n                continue;\n            }\n\n            if (len == sizeof(\"Auth-Port\") - 1\n                && ngx_strncasecmp(ctx->header_name_start,\n                                   (u_char *) \"Auth-Port\",\n                                   sizeof(\"Auth-Port\") - 1)\n                   == 0)\n            {\n                ctx->port.len = ctx->header_end - ctx->header_start;\n                ctx->port.data = ctx->header_start;\n\n                continue;\n            }\n\n            if (len == sizeof(\"Auth-User\") - 1\n                && ngx_strncasecmp(ctx->header_name_start,\n                                   (u_char *) \"Auth-User\",\n                                   sizeof(\"Auth-User\") - 1)\n                   == 0)\n            {\n                s->login.len = ctx->header_end - ctx->header_start;\n\n                s->login.data = ngx_pnalloc(s->connection->pool, s->login.len);\n                if (s->login.data == NULL) {\n                    ngx_close_connection(ctx->peer.connection);\n                    ngx_destroy_pool(ctx->pool);\n                    ngx_mail_session_internal_server_error(s);\n                    return;\n                }\n\n                ngx_memcpy(s->login.data, ctx->header_start, s->login.len);\n\n                continue;\n            }\n\n            if (len == sizeof(\"Auth-Pass\") - 1\n                && ngx_strncasecmp(ctx->header_name_start,\n                                   (u_char *) \"Auth-Pass\",\n                                   sizeof(\"Auth-Pass\") - 1)\n                   == 0)\n            {\n                s->passwd.len = ctx->header_end - ctx->header_start;\n\n                s->passwd.data = ngx_pnalloc(s->connection->pool,\n                                             s->passwd.len);\n                if (s->passwd.data == NULL) {\n                    ngx_close_connection(ctx->peer.connection);\n                    ngx_destroy_pool(ctx->pool);\n                    ngx_mail_session_internal_server_error(s);\n                    return;\n                }\n\n                ngx_memcpy(s->passwd.data, ctx->header_start, s->passwd.len);\n\n                continue;\n            }\n\n            if (len == sizeof(\"Auth-Wait\") - 1\n                && ngx_strncasecmp(ctx->header_name_start,\n                                   (u_char *) \"Auth-Wait\",\n                                   sizeof(\"Auth-Wait\") - 1)\n                   == 0)\n            {\n                n = ngx_atoi(ctx->header_start,\n                             ctx->header_end - ctx->header_start);\n\n                if (n != NGX_ERROR) {\n                    ctx->sleep = n;\n                }\n\n                continue;\n            }\n\n            if (len == sizeof(\"Auth-Error-Code\") - 1\n                && ngx_strncasecmp(ctx->header_name_start,\n                                   (u_char *) \"Auth-Error-Code\",\n                                   sizeof(\"Auth-Error-Code\") - 1)\n                   == 0)\n            {\n                ctx->errcode.len = ctx->header_end - ctx->header_start;\n\n                ctx->errcode.data = ngx_pnalloc(s->connection->pool,\n                                                ctx->errcode.len);\n                if (ctx->errcode.data == NULL) {\n                    ngx_close_connection(ctx->peer.connection);\n                    ngx_destroy_pool(ctx->pool);\n                    ngx_mail_session_internal_server_error(s);\n                    return;\n                }\n\n                ngx_memcpy(ctx->errcode.data, ctx->header_start,\n                           ctx->errcode.len);\n\n                continue;\n            }\n\n            /* ignore other headers */\n\n            continue;\n        }\n\n        if (rc == NGX_DONE) {\n            ngx_log_debug0(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,\n                           \"mail auth http header done\");\n\n            ngx_close_connection(ctx->peer.connection);\n\n            if (ctx->err.len) {\n\n                ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,\n                              \"client login failed: \\\"%V\\\"\", &ctx->errmsg);\n\n                if (s->protocol == NGX_MAIL_SMTP_PROTOCOL) {\n\n                    if (ctx->errcode.len == 0) {\n                        ctx->errcode = ngx_mail_smtp_errcode;\n                    }\n\n                    ctx->err.len = ctx->errcode.len + ctx->errmsg.len\n                                   + sizeof(\" \" CRLF) - 1;\n\n                    p = ngx_pnalloc(s->connection->pool, ctx->err.len);\n                    if (p == NULL) {\n                        ngx_destroy_pool(ctx->pool);\n                        ngx_mail_session_internal_server_error(s);\n                        return;\n                    }\n\n                    ctx->err.data = p;\n\n                    p = ngx_cpymem(p, ctx->errcode.data, ctx->errcode.len);\n                    *p++ = ' ';\n                    p = ngx_cpymem(p, ctx->errmsg.data, ctx->errmsg.len);\n                    *p++ = CR; *p = LF;\n                }\n\n                s->out = ctx->err;\n                timer = ctx->sleep;\n\n                ngx_destroy_pool(ctx->pool);\n\n                if (timer == 0) {\n                    s->quit = 1;\n                    ngx_mail_send(s->connection->write);\n                    return;\n                }\n\n                ngx_add_timer(s->connection->read, (ngx_msec_t) (timer * 1000));\n\n                s->connection->read->handler = ngx_mail_auth_sleep_handler;\n\n                return;\n            }\n\n            if (s->auth_wait) {\n                timer = ctx->sleep;\n\n                ngx_destroy_pool(ctx->pool);\n\n                if (timer == 0) {\n                    ngx_mail_auth_http_init(s);\n                    return;\n                }\n\n                ngx_add_timer(s->connection->read, (ngx_msec_t) (timer * 1000));\n\n                s->connection->read->handler = ngx_mail_auth_sleep_handler;\n\n                return;\n            }\n\n            if (ctx->addr.len == 0 || ctx->port.len == 0) {\n                ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,\n                              \"auth http server %V did not send server or port\",\n                              ctx->peer.name);\n                ngx_destroy_pool(ctx->pool);\n                ngx_mail_session_internal_server_error(s);\n                return;\n            }\n\n            if (s->passwd.data == NULL\n                && s->protocol != NGX_MAIL_SMTP_PROTOCOL)\n            {\n                ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,\n                              \"auth http server %V did not send password\",\n                              ctx->peer.name);\n                ngx_destroy_pool(ctx->pool);\n                ngx_mail_session_internal_server_error(s);\n                return;\n            }\n\n            peer = ngx_pcalloc(s->connection->pool, sizeof(ngx_addr_t));\n            if (peer == NULL) {\n                ngx_destroy_pool(ctx->pool);\n                ngx_mail_session_internal_server_error(s);\n                return;\n            }\n\n            rc = ngx_parse_addr(s->connection->pool, peer,\n                                ctx->addr.data, ctx->addr.len);\n\n            switch (rc) {\n            case NGX_OK:\n                break;\n\n            case NGX_DECLINED:\n                ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,\n                              \"auth http server %V sent invalid server \"\n                              \"address:\\\"%V\\\"\",\n                              ctx->peer.name, &ctx->addr);\n                /* fall through */\n\n            default:\n                ngx_destroy_pool(ctx->pool);\n                ngx_mail_session_internal_server_error(s);\n                return;\n            }\n\n            port = ngx_atoi(ctx->port.data, ctx->port.len);\n            if (port == NGX_ERROR || port < 1 || port > 65535) {\n                ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,\n                              \"auth http server %V sent invalid server \"\n                              \"port:\\\"%V\\\"\",\n                              ctx->peer.name, &ctx->port);\n                ngx_destroy_pool(ctx->pool);\n                ngx_mail_session_internal_server_error(s);\n                return;\n            }\n\n            ngx_inet_set_port(peer->sockaddr, (in_port_t) port);\n\n            len = ctx->addr.len + 1 + ctx->port.len;\n\n            peer->name.len = len;\n\n            peer->name.data = ngx_pnalloc(s->connection->pool, len);\n            if (peer->name.data == NULL) {\n                ngx_destroy_pool(ctx->pool);\n                ngx_mail_session_internal_server_error(s);\n                return;\n            }\n\n            len = ctx->addr.len;\n\n            ngx_memcpy(peer->name.data, ctx->addr.data, len);\n\n            peer->name.data[len++] = ':';\n\n            ngx_memcpy(peer->name.data + len, ctx->port.data, ctx->port.len);\n\n            ngx_destroy_pool(ctx->pool);\n            ngx_mail_proxy_init(s, peer);\n\n            return;\n        }\n\n        if (rc == NGX_AGAIN ) {\n            return;\n        }\n\n        /* rc == NGX_ERROR */\n\n        ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,\n                      \"auth http server %V sent invalid header in response\",\n                      ctx->peer.name);\n        ngx_close_connection(ctx->peer.connection);\n        ngx_destroy_pool(ctx->pool);\n        ngx_mail_session_internal_server_error(s);\n\n        return;\n    }\n}\n\n\nstatic void\nngx_mail_auth_sleep_handler(ngx_event_t *rev)\n{\n    ngx_connection_t          *c;\n    ngx_mail_session_t        *s;\n    ngx_mail_core_srv_conf_t  *cscf;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, \"mail auth sleep handler\");\n\n    c = rev->data;\n    s = c->data;\n\n    if (rev->timedout) {\n\n        rev->timedout = 0;\n\n        if (s->auth_wait) {\n            s->auth_wait = 0;\n            ngx_mail_auth_http_init(s);\n            return;\n        }\n\n        cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);\n\n        rev->handler = cscf->protocol->auth_state;\n\n        s->mail_state = 0;\n        s->auth_method = NGX_MAIL_AUTH_PLAIN;\n\n        c->log->action = \"in auth state\";\n\n        ngx_mail_send(c->write);\n\n        if (c->destroyed) {\n            return;\n        }\n\n        ngx_add_timer(rev, cscf->timeout);\n\n        if (rev->ready) {\n            rev->handler(rev);\n            return;\n        }\n\n        if (ngx_handle_read_event(rev, 0) != NGX_OK) {\n            ngx_mail_close_connection(c);\n        }\n\n        return;\n    }\n\n    if (rev->active) {\n        if (ngx_handle_read_event(rev, 0) != NGX_OK) {\n            ngx_mail_close_connection(c);\n        }\n    }\n}\n\n\nstatic ngx_int_t\nngx_mail_auth_http_parse_header_line(ngx_mail_session_t *s,\n    ngx_mail_auth_http_ctx_t *ctx)\n{\n    u_char      c, ch, *p;\n    enum {\n        sw_start = 0,\n        sw_name,\n        sw_space_before_value,\n        sw_value,\n        sw_space_after_value,\n        sw_almost_done,\n        sw_header_almost_done\n    } state;\n\n    state = ctx->state;\n\n    for (p = ctx->response->pos; p < ctx->response->last; p++) {\n        ch = *p;\n\n        switch (state) {\n\n        /* first char */\n        case sw_start:\n\n            switch (ch) {\n            case CR:\n                ctx->header_end = p;\n                state = sw_header_almost_done;\n                break;\n            case LF:\n                ctx->header_end = p;\n                goto header_done;\n            default:\n                state = sw_name;\n                ctx->header_name_start = p;\n\n                c = (u_char) (ch | 0x20);\n                if (c >= 'a' && c <= 'z') {\n                    break;\n                }\n\n                if (ch >= '0' && ch <= '9') {\n                    break;\n                }\n\n                return NGX_ERROR;\n            }\n            break;\n\n        /* header name */\n        case sw_name:\n            c = (u_char) (ch | 0x20);\n            if (c >= 'a' && c <= 'z') {\n                break;\n            }\n\n            if (ch == ':') {\n                ctx->header_name_end = p;\n                state = sw_space_before_value;\n                break;\n            }\n\n            if (ch == '-') {\n                break;\n            }\n\n            if (ch >= '0' && ch <= '9') {\n                break;\n            }\n\n            if (ch == CR) {\n                ctx->header_name_end = p;\n                ctx->header_start = p;\n                ctx->header_end = p;\n                state = sw_almost_done;\n                break;\n            }\n\n            if (ch == LF) {\n                ctx->header_name_end = p;\n                ctx->header_start = p;\n                ctx->header_end = p;\n                goto done;\n            }\n\n            return NGX_ERROR;\n\n        /* space* before header value */\n        case sw_space_before_value:\n            switch (ch) {\n            case ' ':\n                break;\n            case CR:\n                ctx->header_start = p;\n                ctx->header_end = p;\n                state = sw_almost_done;\n                break;\n            case LF:\n                ctx->header_start = p;\n                ctx->header_end = p;\n                goto done;\n            default:\n                ctx->header_start = p;\n                state = sw_value;\n                break;\n            }\n            break;\n\n        /* header value */\n        case sw_value:\n            switch (ch) {\n            case ' ':\n                ctx->header_end = p;\n                state = sw_space_after_value;\n                break;\n            case CR:\n                ctx->header_end = p;\n                state = sw_almost_done;\n                break;\n            case LF:\n                ctx->header_end = p;\n                goto done;\n            }\n            break;\n\n        /* space* before end of header line */\n        case sw_space_after_value:\n            switch (ch) {\n            case ' ':\n                break;\n            case CR:\n                state = sw_almost_done;\n                break;\n            case LF:\n                goto done;\n            default:\n                state = sw_value;\n                break;\n            }\n            break;\n\n        /* end of header line */\n        case sw_almost_done:\n            switch (ch) {\n            case LF:\n                goto done;\n            default:\n                return NGX_ERROR;\n            }\n\n        /* end of header */\n        case sw_header_almost_done:\n            switch (ch) {\n            case LF:\n                goto header_done;\n            default:\n                return NGX_ERROR;\n            }\n        }\n    }\n\n    ctx->response->pos = p;\n    ctx->state = state;\n\n    return NGX_AGAIN;\n\ndone:\n\n    ctx->response->pos = p + 1;\n    ctx->state = sw_start;\n\n    return NGX_OK;\n\nheader_done:\n\n    ctx->response->pos = p + 1;\n    ctx->state = sw_start;\n\n    return NGX_DONE;\n}\n\n\nstatic void\nngx_mail_auth_http_block_read(ngx_event_t *rev)\n{\n    ngx_connection_t          *c;\n    ngx_mail_session_t        *s;\n    ngx_mail_auth_http_ctx_t  *ctx;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,\n                   \"mail auth http block read\");\n\n    if (ngx_handle_read_event(rev, 0) != NGX_OK) {\n        c = rev->data;\n        s = c->data;\n\n        ctx = ngx_mail_get_module_ctx(s, ngx_mail_auth_http_module);\n\n        ngx_close_connection(ctx->peer.connection);\n        ngx_destroy_pool(ctx->pool);\n        ngx_mail_session_internal_server_error(s);\n    }\n}\n\n\nstatic void\nngx_mail_auth_http_dummy_handler(ngx_event_t *ev)\n{\n    ngx_log_debug0(NGX_LOG_DEBUG_MAIL, ev->log, 0,\n                   \"mail auth http dummy handler\");\n}\n\n\nstatic ngx_buf_t *\nngx_mail_auth_http_create_request(ngx_mail_session_t *s, ngx_pool_t *pool,\n    ngx_mail_auth_http_conf_t *ahcf)\n{\n    size_t                     len;\n    ngx_buf_t                 *b;\n    ngx_str_t                  login, passwd;\n    ngx_connection_t          *c;\n#if (NGX_MAIL_SSL)\n    ngx_str_t                  protocol, cipher, verify, subject, issuer,\n                               serial, fingerprint, raw_cert, cert;\n    ngx_mail_ssl_conf_t       *sslcf;\n#endif\n    ngx_mail_core_srv_conf_t  *cscf;\n\n    if (ngx_mail_auth_http_escape(pool, &s->login, &login) != NGX_OK) {\n        return NULL;\n    }\n\n    if (ngx_mail_auth_http_escape(pool, &s->passwd, &passwd) != NGX_OK) {\n        return NULL;\n    }\n\n    c = s->connection;\n\n#if (NGX_MAIL_SSL)\n\n    if (c->ssl) {\n\n        if (ngx_ssl_get_protocol(c, pool, &protocol) != NGX_OK) {\n            return NULL;\n        }\n\n        protocol.len = ngx_strlen(protocol.data);\n\n        if (ngx_ssl_get_cipher_name(c, pool, &cipher) != NGX_OK) {\n            return NULL;\n        }\n\n        cipher.len = ngx_strlen(cipher.data);\n\n    } else {\n        ngx_str_null(&protocol);\n        ngx_str_null(&cipher);\n    }\n\n    sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);\n\n    if (c->ssl && sslcf->verify) {\n\n        /* certificate details */\n\n        if (ngx_ssl_get_client_verify(c, pool, &verify) != NGX_OK) {\n            return NULL;\n        }\n\n        if (ngx_ssl_get_subject_dn(c, pool, &subject) != NGX_OK) {\n            return NULL;\n        }\n\n        if (ngx_ssl_get_issuer_dn(c, pool, &issuer) != NGX_OK) {\n            return NULL;\n        }\n\n        if (ngx_ssl_get_serial_number(c, pool, &serial) != NGX_OK) {\n            return NULL;\n        }\n\n        if (ngx_ssl_get_fingerprint(c, pool, &fingerprint) != NGX_OK) {\n            return NULL;\n        }\n\n        if (ahcf->pass_client_cert) {\n\n            /* certificate itself, if configured */\n\n            if (ngx_ssl_get_raw_certificate(c, pool, &raw_cert) != NGX_OK) {\n                return NULL;\n            }\n\n            if (ngx_mail_auth_http_escape(pool, &raw_cert, &cert) != NGX_OK) {\n                return NULL;\n            }\n\n        } else {\n            ngx_str_null(&cert);\n        }\n\n    } else {\n        ngx_str_null(&verify);\n        ngx_str_null(&subject);\n        ngx_str_null(&issuer);\n        ngx_str_null(&serial);\n        ngx_str_null(&fingerprint);\n        ngx_str_null(&cert);\n    }\n\n#endif\n\n    cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);\n\n    len = sizeof(\"GET \") - 1 + ahcf->uri.len + sizeof(\" HTTP/1.0\" CRLF) - 1\n          + sizeof(\"Host: \") - 1 + ahcf->host_header.len + sizeof(CRLF) - 1\n          + sizeof(\"Auth-Method: \") - 1\n                + ngx_mail_auth_http_method[s->auth_method].len\n                + sizeof(CRLF) - 1\n          + sizeof(\"Auth-User: \") - 1 + login.len + sizeof(CRLF) - 1\n          + sizeof(\"Auth-Pass: \") - 1 + passwd.len + sizeof(CRLF) - 1\n          + sizeof(\"Auth-Salt: \") - 1 + s->salt.len\n          + sizeof(\"Auth-Protocol: \") - 1 + cscf->protocol->name.len\n                + sizeof(CRLF) - 1\n          + sizeof(\"Auth-Login-Attempt: \") - 1 + NGX_INT_T_LEN\n                + sizeof(CRLF) - 1\n          + sizeof(\"Client-IP: \") - 1 + s->connection->addr_text.len\n                + sizeof(CRLF) - 1\n          + sizeof(\"Client-Host: \") - 1 + s->host.len + sizeof(CRLF) - 1\n          + ahcf->header.len\n          + sizeof(CRLF) - 1;\n\n    if (c->proxy_protocol) {\n        len += sizeof(\"Proxy-Protocol-Addr: \") - 1\n                     + c->proxy_protocol->src_addr.len + sizeof(CRLF) - 1\n               + sizeof(\"Proxy-Protocol-Port: \") - 1\n                     + sizeof(\"65535\") - 1 + sizeof(CRLF) - 1\n               + sizeof(\"Proxy-Protocol-Server-Addr: \") - 1\n                     + c->proxy_protocol->dst_addr.len + sizeof(CRLF) - 1\n               + sizeof(\"Proxy-Protocol-Server-Port: \") - 1\n                     + sizeof(\"65535\") - 1 + sizeof(CRLF) - 1;\n    }\n\n    if (s->auth_method == NGX_MAIL_AUTH_NONE) {\n        len += sizeof(\"Auth-SMTP-Helo: \") - 1 + s->smtp_helo.len\n                     + sizeof(CRLF) - 1\n               + sizeof(\"Auth-SMTP-From: \") - 1 + s->smtp_from.len\n                     + sizeof(CRLF) - 1\n               + sizeof(\"Auth-SMTP-To: \") - 1 + s->smtp_to.len\n                     + sizeof(CRLF) - 1;\n    }\n\n#if (NGX_MAIL_SSL)\n\n    if (c->ssl) {\n        len += sizeof(\"Auth-SSL: on\" CRLF) - 1\n               + sizeof(\"Auth-SSL-Protocol: \") - 1 + protocol.len\n                     + sizeof(CRLF) - 1\n               + sizeof(\"Auth-SSL-Cipher: \") - 1 + cipher.len\n                     + sizeof(CRLF) - 1\n               + sizeof(\"Auth-SSL-Verify: \") - 1 + verify.len\n                     + sizeof(CRLF) - 1\n               + sizeof(\"Auth-SSL-Subject: \") - 1 + subject.len\n                     + sizeof(CRLF) - 1\n               + sizeof(\"Auth-SSL-Issuer: \") - 1 + issuer.len\n                     + sizeof(CRLF) - 1\n               + sizeof(\"Auth-SSL-Serial: \") - 1 + serial.len\n                     + sizeof(CRLF) - 1\n               + sizeof(\"Auth-SSL-Fingerprint: \") - 1 + fingerprint.len\n                     + sizeof(CRLF) - 1\n               + sizeof(\"Auth-SSL-Cert: \") - 1 + cert.len\n                     + sizeof(CRLF) - 1;\n    }\n\n#endif\n\n    b = ngx_create_temp_buf(pool, len);\n    if (b == NULL) {\n        return NULL;\n    }\n\n    b->last = ngx_cpymem(b->last, \"GET \", sizeof(\"GET \") - 1);\n    b->last = ngx_copy(b->last, ahcf->uri.data, ahcf->uri.len);\n    b->last = ngx_cpymem(b->last, \" HTTP/1.0\" CRLF,\n                         sizeof(\" HTTP/1.0\" CRLF) - 1);\n\n    b->last = ngx_cpymem(b->last, \"Host: \", sizeof(\"Host: \") - 1);\n    b->last = ngx_copy(b->last, ahcf->host_header.data,\n                         ahcf->host_header.len);\n    *b->last++ = CR; *b->last++ = LF;\n\n    b->last = ngx_cpymem(b->last, \"Auth-Method: \",\n                         sizeof(\"Auth-Method: \") - 1);\n    b->last = ngx_cpymem(b->last,\n                         ngx_mail_auth_http_method[s->auth_method].data,\n                         ngx_mail_auth_http_method[s->auth_method].len);\n    *b->last++ = CR; *b->last++ = LF;\n\n    b->last = ngx_cpymem(b->last, \"Auth-User: \", sizeof(\"Auth-User: \") - 1);\n    b->last = ngx_copy(b->last, login.data, login.len);\n    *b->last++ = CR; *b->last++ = LF;\n\n    b->last = ngx_cpymem(b->last, \"Auth-Pass: \", sizeof(\"Auth-Pass: \") - 1);\n    b->last = ngx_copy(b->last, passwd.data, passwd.len);\n    *b->last++ = CR; *b->last++ = LF;\n\n    if (s->auth_method != NGX_MAIL_AUTH_PLAIN && s->salt.len) {\n        b->last = ngx_cpymem(b->last, \"Auth-Salt: \", sizeof(\"Auth-Salt: \") - 1);\n        b->last = ngx_copy(b->last, s->salt.data, s->salt.len);\n\n        s->passwd.data = NULL;\n    }\n\n    b->last = ngx_cpymem(b->last, \"Auth-Protocol: \",\n                         sizeof(\"Auth-Protocol: \") - 1);\n    b->last = ngx_cpymem(b->last, cscf->protocol->name.data,\n                         cscf->protocol->name.len);\n    *b->last++ = CR; *b->last++ = LF;\n\n    b->last = ngx_sprintf(b->last, \"Auth-Login-Attempt: %ui\" CRLF,\n                          s->login_attempt);\n\n    b->last = ngx_cpymem(b->last, \"Client-IP: \", sizeof(\"Client-IP: \") - 1);\n    b->last = ngx_copy(b->last, s->connection->addr_text.data,\n                       s->connection->addr_text.len);\n    *b->last++ = CR; *b->last++ = LF;\n\n    if (s->host.len) {\n        b->last = ngx_cpymem(b->last, \"Client-Host: \",\n                             sizeof(\"Client-Host: \") - 1);\n        b->last = ngx_copy(b->last, s->host.data, s->host.len);\n        *b->last++ = CR; *b->last++ = LF;\n    }\n\n    if (c->proxy_protocol) {\n        b->last = ngx_cpymem(b->last, \"Proxy-Protocol-Addr: \",\n                             sizeof(\"Proxy-Protocol-Addr: \") - 1);\n        b->last = ngx_copy(b->last, c->proxy_protocol->src_addr.data,\n                           c->proxy_protocol->src_addr.len);\n        *b->last++ = CR; *b->last++ = LF;\n\n        b->last = ngx_sprintf(b->last, \"Proxy-Protocol-Port: %d\" CRLF,\n                              c->proxy_protocol->src_port);\n\n        b->last = ngx_cpymem(b->last, \"Proxy-Protocol-Server-Addr: \",\n                             sizeof(\"Proxy-Protocol-Server-Addr: \") - 1);\n        b->last = ngx_copy(b->last, c->proxy_protocol->dst_addr.data,\n                           c->proxy_protocol->dst_addr.len);\n        *b->last++ = CR; *b->last++ = LF;\n\n        b->last = ngx_sprintf(b->last, \"Proxy-Protocol-Server-Port: %d\" CRLF,\n                              c->proxy_protocol->dst_port);\n    }\n\n    if (s->auth_method == NGX_MAIL_AUTH_NONE) {\n\n        /* HELO, MAIL FROM, and RCPT TO can't contain CRLF, no need to escape */\n\n        b->last = ngx_cpymem(b->last, \"Auth-SMTP-Helo: \",\n                             sizeof(\"Auth-SMTP-Helo: \") - 1);\n        b->last = ngx_copy(b->last, s->smtp_helo.data, s->smtp_helo.len);\n        *b->last++ = CR; *b->last++ = LF;\n\n        b->last = ngx_cpymem(b->last, \"Auth-SMTP-From: \",\n                             sizeof(\"Auth-SMTP-From: \") - 1);\n        b->last = ngx_copy(b->last, s->smtp_from.data, s->smtp_from.len);\n        *b->last++ = CR; *b->last++ = LF;\n\n        b->last = ngx_cpymem(b->last, \"Auth-SMTP-To: \",\n                             sizeof(\"Auth-SMTP-To: \") - 1);\n        b->last = ngx_copy(b->last, s->smtp_to.data, s->smtp_to.len);\n        *b->last++ = CR; *b->last++ = LF;\n\n    }\n\n#if (NGX_MAIL_SSL)\n\n    if (c->ssl) {\n        b->last = ngx_cpymem(b->last, \"Auth-SSL: on\" CRLF,\n                             sizeof(\"Auth-SSL: on\" CRLF) - 1);\n\n        if (protocol.len) {\n            b->last = ngx_cpymem(b->last, \"Auth-SSL-Protocol: \",\n                                 sizeof(\"Auth-SSL-Protocol: \") - 1);\n            b->last = ngx_copy(b->last, protocol.data, protocol.len);\n            *b->last++ = CR; *b->last++ = LF;\n        }\n\n        if (cipher.len) {\n            b->last = ngx_cpymem(b->last, \"Auth-SSL-Cipher: \",\n                                 sizeof(\"Auth-SSL-Cipher: \") - 1);\n            b->last = ngx_copy(b->last, cipher.data, cipher.len);\n            *b->last++ = CR; *b->last++ = LF;\n        }\n\n        if (verify.len) {\n            b->last = ngx_cpymem(b->last, \"Auth-SSL-Verify: \",\n                                 sizeof(\"Auth-SSL-Verify: \") - 1);\n            b->last = ngx_copy(b->last, verify.data, verify.len);\n            *b->last++ = CR; *b->last++ = LF;\n        }\n\n        if (subject.len) {\n            b->last = ngx_cpymem(b->last, \"Auth-SSL-Subject: \",\n                                 sizeof(\"Auth-SSL-Subject: \") - 1);\n            b->last = ngx_copy(b->last, subject.data, subject.len);\n            *b->last++ = CR; *b->last++ = LF;\n        }\n\n        if (issuer.len) {\n            b->last = ngx_cpymem(b->last, \"Auth-SSL-Issuer: \",\n                                 sizeof(\"Auth-SSL-Issuer: \") - 1);\n            b->last = ngx_copy(b->last, issuer.data, issuer.len);\n            *b->last++ = CR; *b->last++ = LF;\n        }\n\n        if (serial.len) {\n            b->last = ngx_cpymem(b->last, \"Auth-SSL-Serial: \",\n                                 sizeof(\"Auth-SSL-Serial: \") - 1);\n            b->last = ngx_copy(b->last, serial.data, serial.len);\n            *b->last++ = CR; *b->last++ = LF;\n        }\n\n        if (fingerprint.len) {\n            b->last = ngx_cpymem(b->last, \"Auth-SSL-Fingerprint: \",\n                                 sizeof(\"Auth-SSL-Fingerprint: \") - 1);\n            b->last = ngx_copy(b->last, fingerprint.data, fingerprint.len);\n            *b->last++ = CR; *b->last++ = LF;\n        }\n\n        if (cert.len) {\n            b->last = ngx_cpymem(b->last, \"Auth-SSL-Cert: \",\n                                 sizeof(\"Auth-SSL-Cert: \") - 1);\n            b->last = ngx_copy(b->last, cert.data, cert.len);\n            *b->last++ = CR; *b->last++ = LF;\n        }\n    }\n\n#endif\n\n    if (ahcf->header.len) {\n        b->last = ngx_copy(b->last, ahcf->header.data, ahcf->header.len);\n    }\n\n    /* add \"\\r\\n\" at the header end */\n    *b->last++ = CR; *b->last++ = LF;\n\n#if (NGX_DEBUG_MAIL_PASSWD)\n    ngx_log_debug2(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,\n                   \"mail auth http header:%N\\\"%*s\\\"\",\n                   (size_t) (b->last - b->pos), b->pos);\n#endif\n\n    return b;\n}\n\n\nstatic ngx_int_t\nngx_mail_auth_http_escape(ngx_pool_t *pool, ngx_str_t *text, ngx_str_t *escaped)\n{\n    u_char     *p;\n    uintptr_t   n;\n\n    n = ngx_escape_uri(NULL, text->data, text->len, NGX_ESCAPE_MAIL_AUTH);\n\n    if (n == 0) {\n        *escaped = *text;\n        return NGX_OK;\n    }\n\n    escaped->len = text->len + n * 2;\n\n    p = ngx_pnalloc(pool, escaped->len);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    (void) ngx_escape_uri(p, text->data, text->len, NGX_ESCAPE_MAIL_AUTH);\n\n    escaped->data = p;\n\n    return NGX_OK;\n}\n\n\nstatic void *\nngx_mail_auth_http_create_conf(ngx_conf_t *cf)\n{\n    ngx_mail_auth_http_conf_t  *ahcf;\n\n    ahcf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_auth_http_conf_t));\n    if (ahcf == NULL) {\n        return NULL;\n    }\n\n    ahcf->timeout = NGX_CONF_UNSET_MSEC;\n    ahcf->pass_client_cert = NGX_CONF_UNSET;\n\n    ahcf->file = cf->conf_file->file.name.data;\n    ahcf->line = cf->conf_file->line;\n\n    return ahcf;\n}\n\n\nstatic char *\nngx_mail_auth_http_merge_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_mail_auth_http_conf_t *prev = parent;\n    ngx_mail_auth_http_conf_t *conf = child;\n\n    u_char           *p;\n    size_t            len;\n    ngx_uint_t        i;\n    ngx_table_elt_t  *header;\n\n    if (conf->peer == NULL) {\n        conf->peer = prev->peer;\n        conf->host_header = prev->host_header;\n        conf->uri = prev->uri;\n\n        if (conf->peer == NULL) {\n            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                          \"no \\\"auth_http\\\" is defined for server in %s:%ui\",\n                          conf->file, conf->line);\n\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 60000);\n\n    ngx_conf_merge_value(conf->pass_client_cert, prev->pass_client_cert, 0);\n\n    if (conf->headers == NULL) {\n        conf->headers = prev->headers;\n        conf->header = prev->header;\n    }\n\n    if (conf->headers && conf->header.len == 0) {\n        len = 0;\n        header = conf->headers->elts;\n        for (i = 0; i < conf->headers->nelts; i++) {\n            len += header[i].key.len + 2 + header[i].value.len + 2;\n        }\n\n        p = ngx_pnalloc(cf->pool, len);\n        if (p == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        conf->header.len = len;\n        conf->header.data = p;\n\n        for (i = 0; i < conf->headers->nelts; i++) {\n            p = ngx_cpymem(p, header[i].key.data, header[i].key.len);\n            *p++ = ':'; *p++ = ' ';\n            p = ngx_cpymem(p, header[i].value.data, header[i].value.len);\n            *p++ = CR; *p++ = LF;\n        }\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_mail_auth_http(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_mail_auth_http_conf_t *ahcf = conf;\n\n    ngx_str_t  *value;\n    ngx_url_t   u;\n\n    value = cf->args->elts;\n\n    ngx_memzero(&u, sizeof(ngx_url_t));\n\n    u.url = value[1];\n    u.default_port = 80;\n    u.uri_part = 1;\n\n    if (ngx_strncmp(u.url.data, \"http://\", 7) == 0) {\n        u.url.len -= 7;\n        u.url.data += 7;\n    }\n\n    if (ngx_parse_url(cf->pool, &u) != NGX_OK) {\n        if (u.err) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"%s in auth_http \\\"%V\\\"\", u.err, &u.url);\n        }\n\n        return NGX_CONF_ERROR;\n    }\n\n    ahcf->peer = u.addrs;\n\n    if (u.family != AF_UNIX) {\n        ahcf->host_header = u.host;\n\n    } else {\n        ngx_str_set(&ahcf->host_header, \"localhost\");\n    }\n\n    ahcf->uri = u.uri;\n\n    if (ahcf->uri.len == 0) {\n        ngx_str_set(&ahcf->uri, \"/\");\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_mail_auth_http_header(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_mail_auth_http_conf_t *ahcf = conf;\n\n    ngx_str_t        *value;\n    ngx_table_elt_t  *header;\n\n    if (ahcf->headers == NULL) {\n        ahcf->headers = ngx_array_create(cf->pool, 1, sizeof(ngx_table_elt_t));\n        if (ahcf->headers == NULL) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    header = ngx_array_push(ahcf->headers);\n    if (header == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    value = cf->args->elts;\n\n    header->key = value[1];\n    header->value = value[2];\n\n    return NGX_CONF_OK;\n}\n"
  },
  {
    "path": "src/mail/ngx_mail_core_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n#include <ngx_mail.h>\n\n\nstatic void *ngx_mail_core_create_main_conf(ngx_conf_t *cf);\nstatic void *ngx_mail_core_create_srv_conf(ngx_conf_t *cf);\nstatic char *ngx_mail_core_merge_srv_conf(ngx_conf_t *cf, void *parent,\n    void *child);\nstatic char *ngx_mail_core_server(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_mail_core_listen(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_mail_core_protocol(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_mail_core_error_log(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_mail_core_resolver(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n\n\nstatic ngx_command_t  ngx_mail_core_commands[] = {\n\n    { ngx_string(\"server\"),\n      NGX_MAIL_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,\n      ngx_mail_core_server,\n      0,\n      0,\n      NULL },\n\n    { ngx_string(\"listen\"),\n      NGX_MAIL_SRV_CONF|NGX_CONF_1MORE,\n      ngx_mail_core_listen,\n      NGX_MAIL_SRV_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"protocol\"),\n      NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_mail_core_protocol,\n      NGX_MAIL_SRV_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"timeout\"),\n      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_MAIL_SRV_CONF_OFFSET,\n      offsetof(ngx_mail_core_srv_conf_t, timeout),\n      NULL },\n\n    { ngx_string(\"server_name\"),\n      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_slot,\n      NGX_MAIL_SRV_CONF_OFFSET,\n      offsetof(ngx_mail_core_srv_conf_t, server_name),\n      NULL },\n\n    { ngx_string(\"error_log\"),\n      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE,\n      ngx_mail_core_error_log,\n      NGX_MAIL_SRV_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"resolver\"),\n      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE,\n      ngx_mail_core_resolver,\n      NGX_MAIL_SRV_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"resolver_timeout\"),\n      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_MAIL_SRV_CONF_OFFSET,\n      offsetof(ngx_mail_core_srv_conf_t, resolver_timeout),\n      NULL },\n\n    { ngx_string(\"max_errors\"),\n      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_num_slot,\n      NGX_MAIL_SRV_CONF_OFFSET,\n      offsetof(ngx_mail_core_srv_conf_t, max_errors),\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_mail_module_t  ngx_mail_core_module_ctx = {\n    NULL,                                  /* protocol */\n\n    ngx_mail_core_create_main_conf,        /* create main configuration */\n    NULL,                                  /* init main configuration */\n\n    ngx_mail_core_create_srv_conf,         /* create server configuration */\n    ngx_mail_core_merge_srv_conf           /* merge server configuration */\n};\n\n\nngx_module_t  ngx_mail_core_module = {\n    NGX_MODULE_V1,\n    &ngx_mail_core_module_ctx,             /* module context */\n    ngx_mail_core_commands,                /* module directives */\n    NGX_MAIL_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 void *\nngx_mail_core_create_main_conf(ngx_conf_t *cf)\n{\n    ngx_mail_core_main_conf_t  *cmcf;\n\n    cmcf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_core_main_conf_t));\n    if (cmcf == NULL) {\n        return NULL;\n    }\n\n    if (ngx_array_init(&cmcf->servers, cf->pool, 4,\n                       sizeof(ngx_mail_core_srv_conf_t *))\n        != NGX_OK)\n    {\n        return NULL;\n    }\n\n    if (ngx_array_init(&cmcf->listen, cf->pool, 4, sizeof(ngx_mail_listen_t))\n        != NGX_OK)\n    {\n        return NULL;\n    }\n\n    return cmcf;\n}\n\n\nstatic void *\nngx_mail_core_create_srv_conf(ngx_conf_t *cf)\n{\n    ngx_mail_core_srv_conf_t  *cscf;\n\n    cscf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_core_srv_conf_t));\n    if (cscf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc():\n     *\n     *     cscf->protocol = NULL;\n     *     cscf->error_log = NULL;\n     */\n\n    cscf->timeout = NGX_CONF_UNSET_MSEC;\n    cscf->resolver_timeout = NGX_CONF_UNSET_MSEC;\n\n    cscf->max_errors = NGX_CONF_UNSET_UINT;\n\n    cscf->resolver = NGX_CONF_UNSET_PTR;\n\n    cscf->file_name = cf->conf_file->file.name.data;\n    cscf->line = cf->conf_file->line;\n\n    return cscf;\n}\n\n\nstatic char *\nngx_mail_core_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_mail_core_srv_conf_t *prev = parent;\n    ngx_mail_core_srv_conf_t *conf = child;\n\n    ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 60000);\n    ngx_conf_merge_msec_value(conf->resolver_timeout, prev->resolver_timeout,\n                              30000);\n\n    ngx_conf_merge_uint_value(conf->max_errors, prev->max_errors, 5);\n\n    ngx_conf_merge_str_value(conf->server_name, prev->server_name, \"\");\n\n    if (conf->server_name.len == 0) {\n        conf->server_name = cf->cycle->hostname;\n    }\n\n    if (conf->protocol == NULL) {\n        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                      \"unknown mail protocol for server in %s:%ui\",\n                      conf->file_name, conf->line);\n        return NGX_CONF_ERROR;\n    }\n\n    if (conf->error_log == NULL) {\n        if (prev->error_log) {\n            conf->error_log = prev->error_log;\n        } else {\n            conf->error_log = &cf->cycle->new_log;\n        }\n    }\n\n    ngx_conf_merge_ptr_value(conf->resolver, prev->resolver, NULL);\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_mail_core_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    char                       *rv;\n    void                       *mconf;\n    ngx_uint_t                  m;\n    ngx_conf_t                  pcf;\n    ngx_mail_module_t          *module;\n    ngx_mail_conf_ctx_t        *ctx, *mail_ctx;\n    ngx_mail_core_srv_conf_t   *cscf, **cscfp;\n    ngx_mail_core_main_conf_t  *cmcf;\n\n    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_mail_conf_ctx_t));\n    if (ctx == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    mail_ctx = cf->ctx;\n    ctx->main_conf = mail_ctx->main_conf;\n\n    /* the server{}'s srv_conf */\n\n    ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_mail_max_module);\n    if (ctx->srv_conf == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    for (m = 0; cf->cycle->modules[m]; m++) {\n        if (cf->cycle->modules[m]->type != NGX_MAIL_MODULE) {\n            continue;\n        }\n\n        module = cf->cycle->modules[m]->ctx;\n\n        if (module->create_srv_conf) {\n            mconf = module->create_srv_conf(cf);\n            if (mconf == NULL) {\n                return NGX_CONF_ERROR;\n            }\n\n            ctx->srv_conf[cf->cycle->modules[m]->ctx_index] = mconf;\n        }\n    }\n\n    /* the server configuration context */\n\n    cscf = ctx->srv_conf[ngx_mail_core_module.ctx_index];\n    cscf->ctx = ctx;\n\n    cmcf = ctx->main_conf[ngx_mail_core_module.ctx_index];\n\n    cscfp = ngx_array_push(&cmcf->servers);\n    if (cscfp == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    *cscfp = cscf;\n\n\n    /* parse inside server{} */\n\n    pcf = *cf;\n    cf->ctx = ctx;\n    cf->cmd_type = NGX_MAIL_SRV_CONF;\n\n    rv = ngx_conf_parse(cf, NULL);\n\n    *cf = pcf;\n\n    if (rv == NGX_CONF_OK && !cscf->listen) {\n        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                      \"no \\\"listen\\\" is defined for server in %s:%ui\",\n                      cscf->file_name, cscf->line);\n        return NGX_CONF_ERROR;\n    }\n\n    return rv;\n}\n\n\nstatic char *\nngx_mail_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_mail_core_srv_conf_t  *cscf = conf;\n\n    ngx_str_t                  *value, size;\n    ngx_url_t                   u;\n    ngx_uint_t                  i, n, m;\n    ngx_mail_listen_t          *ls, *als, *nls;\n    ngx_mail_module_t          *module;\n    ngx_mail_core_main_conf_t  *cmcf;\n\n    cscf->listen = 1;\n\n    value = cf->args->elts;\n\n    ngx_memzero(&u, sizeof(ngx_url_t));\n\n    u.url = value[1];\n    u.listen = 1;\n\n    if (ngx_parse_url(cf->pool, &u) != NGX_OK) {\n        if (u.err) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"%s in \\\"%V\\\" of the \\\"listen\\\" directive\",\n                               u.err, &u.url);\n        }\n\n        return NGX_CONF_ERROR;\n    }\n\n    cmcf = ngx_mail_conf_get_module_main_conf(cf, ngx_mail_core_module);\n\n    ls = ngx_array_push(&cmcf->listen);\n    if (ls == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_memzero(ls, sizeof(ngx_mail_listen_t));\n\n    ls->backlog = NGX_LISTEN_BACKLOG;\n    ls->rcvbuf = -1;\n    ls->sndbuf = -1;\n    ls->ctx = cf->ctx;\n\n#if (NGX_HAVE_INET6)\n    ls->ipv6only = 1;\n#endif\n\n    if (cscf->protocol == NULL) {\n        for (m = 0; cf->cycle->modules[m]; m++) {\n            if (cf->cycle->modules[m]->type != NGX_MAIL_MODULE) {\n                continue;\n            }\n\n            module = cf->cycle->modules[m]->ctx;\n\n            if (module->protocol == NULL) {\n                continue;\n            }\n\n            for (i = 0; module->protocol->port[i]; i++) {\n                if (module->protocol->port[i] == u.port) {\n                    cscf->protocol = module->protocol;\n                    break;\n                }\n            }\n        }\n    }\n\n    for (i = 2; i < cf->args->nelts; i++) {\n\n        if (ngx_strcmp(value[i].data, \"bind\") == 0) {\n            ls->bind = 1;\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"backlog=\", 8) == 0) {\n            ls->backlog = ngx_atoi(value[i].data + 8, value[i].len - 8);\n            ls->bind = 1;\n\n            if (ls->backlog == NGX_ERROR || ls->backlog == 0) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"invalid backlog \\\"%V\\\"\", &value[i]);\n                return NGX_CONF_ERROR;\n            }\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"rcvbuf=\", 7) == 0) {\n            size.len = value[i].len - 7;\n            size.data = value[i].data + 7;\n\n            ls->rcvbuf = ngx_parse_size(&size);\n            ls->bind = 1;\n\n            if (ls->rcvbuf == NGX_ERROR) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"invalid rcvbuf \\\"%V\\\"\", &value[i]);\n                return NGX_CONF_ERROR;\n            }\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"sndbuf=\", 7) == 0) {\n            size.len = value[i].len - 7;\n            size.data = value[i].data + 7;\n\n            ls->sndbuf = ngx_parse_size(&size);\n            ls->bind = 1;\n\n            if (ls->sndbuf == NGX_ERROR) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"invalid sndbuf \\\"%V\\\"\", &value[i]);\n                return NGX_CONF_ERROR;\n            }\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"ipv6only=o\", 10) == 0) {\n#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)\n            if (ngx_strcmp(&value[i].data[10], \"n\") == 0) {\n                ls->ipv6only = 1;\n\n            } else if (ngx_strcmp(&value[i].data[10], \"ff\") == 0) {\n                ls->ipv6only = 0;\n\n            } else {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"invalid ipv6only flags \\\"%s\\\"\",\n                                   &value[i].data[9]);\n                return NGX_CONF_ERROR;\n            }\n\n            ls->bind = 1;\n            continue;\n#else\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"bind ipv6only is not supported \"\n                               \"on this platform\");\n            return NGX_CONF_ERROR;\n#endif\n        }\n\n        if (ngx_strcmp(value[i].data, \"ssl\") == 0) {\n#if (NGX_MAIL_SSL)\n            ngx_mail_ssl_conf_t  *sslcf;\n\n            sslcf = ngx_mail_conf_get_module_srv_conf(cf, ngx_mail_ssl_module);\n\n            sslcf->listen = 1;\n            sslcf->file = cf->conf_file->file.name.data;\n            sslcf->line = cf->conf_file->line;\n\n            ls->ssl = 1;\n\n            continue;\n#else\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"the \\\"ssl\\\" parameter requires \"\n                               \"ngx_mail_ssl_module\");\n            return NGX_CONF_ERROR;\n#endif\n        }\n\n        if (ngx_strncmp(value[i].data, \"so_keepalive=\", 13) == 0) {\n\n            if (ngx_strcmp(&value[i].data[13], \"on\") == 0) {\n                ls->so_keepalive = 1;\n\n            } else if (ngx_strcmp(&value[i].data[13], \"off\") == 0) {\n                ls->so_keepalive = 2;\n\n            } else {\n\n#if (NGX_HAVE_KEEPALIVE_TUNABLE)\n                u_char     *p, *end;\n                ngx_str_t   s;\n\n                end = value[i].data + value[i].len;\n                s.data = value[i].data + 13;\n\n                p = ngx_strlchr(s.data, end, ':');\n                if (p == NULL) {\n                    p = end;\n                }\n\n                if (p > s.data) {\n                    s.len = p - s.data;\n\n                    ls->tcp_keepidle = ngx_parse_time(&s, 1);\n                    if (ls->tcp_keepidle == (time_t) NGX_ERROR) {\n                        goto invalid_so_keepalive;\n                    }\n                }\n\n                s.data = (p < end) ? (p + 1) : end;\n\n                p = ngx_strlchr(s.data, end, ':');\n                if (p == NULL) {\n                    p = end;\n                }\n\n                if (p > s.data) {\n                    s.len = p - s.data;\n\n                    ls->tcp_keepintvl = ngx_parse_time(&s, 1);\n                    if (ls->tcp_keepintvl == (time_t) NGX_ERROR) {\n                        goto invalid_so_keepalive;\n                    }\n                }\n\n                s.data = (p < end) ? (p + 1) : end;\n\n                if (s.data < end) {\n                    s.len = end - s.data;\n\n                    ls->tcp_keepcnt = ngx_atoi(s.data, s.len);\n                    if (ls->tcp_keepcnt == NGX_ERROR) {\n                        goto invalid_so_keepalive;\n                    }\n                }\n\n                if (ls->tcp_keepidle == 0 && ls->tcp_keepintvl == 0\n                    && ls->tcp_keepcnt == 0)\n                {\n                    goto invalid_so_keepalive;\n                }\n\n                ls->so_keepalive = 1;\n\n#else\n\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"the \\\"so_keepalive\\\" parameter accepts \"\n                                   \"only \\\"on\\\" or \\\"off\\\" on this platform\");\n                return NGX_CONF_ERROR;\n\n#endif\n            }\n\n            ls->bind = 1;\n\n            continue;\n\n#if (NGX_HAVE_KEEPALIVE_TUNABLE)\n        invalid_so_keepalive:\n\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"invalid so_keepalive value: \\\"%s\\\"\",\n                               &value[i].data[13]);\n            return NGX_CONF_ERROR;\n#endif\n        }\n\n        if (ngx_strcmp(value[i].data, \"proxy_protocol\") == 0) {\n            ls->proxy_protocol = 1;\n            continue;\n        }\n\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"the invalid \\\"%V\\\" parameter\", &value[i]);\n        return NGX_CONF_ERROR;\n    }\n\n    for (n = 0; n < u.naddrs; n++) {\n\n        for (i = 0; i < n; i++) {\n            if (ngx_cmp_sockaddr(u.addrs[n].sockaddr, u.addrs[n].socklen,\n                                 u.addrs[i].sockaddr, u.addrs[i].socklen, 1)\n                == NGX_OK)\n            {\n                goto next;\n            }\n        }\n\n        if (n != 0) {\n            nls = ngx_array_push(&cmcf->listen);\n            if (nls == NULL) {\n                return NGX_CONF_ERROR;\n            }\n\n            *nls = *ls;\n\n        } else {\n            nls = ls;\n        }\n\n        nls->sockaddr = u.addrs[n].sockaddr;\n        nls->socklen = u.addrs[n].socklen;\n        nls->addr_text = u.addrs[n].name;\n        nls->wildcard = ngx_inet_wildcard(nls->sockaddr);\n\n        als = cmcf->listen.elts;\n\n        for (i = 0; i < cmcf->listen.nelts - 1; i++) {\n\n            if (ngx_cmp_sockaddr(als[i].sockaddr, als[i].socklen,\n                                 nls->sockaddr, nls->socklen, 1)\n                != NGX_OK)\n            {\n                continue;\n            }\n\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"duplicate \\\"%V\\\" address and port pair\",\n                               &nls->addr_text);\n            return NGX_CONF_ERROR;\n        }\n\n    next:\n        continue;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_mail_core_protocol(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_mail_core_srv_conf_t  *cscf = conf;\n\n    ngx_str_t          *value;\n    ngx_uint_t          m;\n    ngx_mail_module_t  *module;\n\n    value = cf->args->elts;\n\n    for (m = 0; cf->cycle->modules[m]; m++) {\n        if (cf->cycle->modules[m]->type != NGX_MAIL_MODULE) {\n            continue;\n        }\n\n        module = cf->cycle->modules[m]->ctx;\n\n        if (module->protocol\n            && ngx_strcmp(module->protocol->name.data, value[1].data) == 0)\n        {\n            cscf->protocol = module->protocol;\n\n            return NGX_CONF_OK;\n        }\n    }\n\n    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                       \"unknown protocol \\\"%V\\\"\", &value[1]);\n    return NGX_CONF_ERROR;\n}\n\n\nstatic char *\nngx_mail_core_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_mail_core_srv_conf_t  *cscf = conf;\n\n    return ngx_log_set_log(cf, &cscf->error_log);\n}\n\n\nstatic char *\nngx_mail_core_resolver(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_mail_core_srv_conf_t  *cscf = conf;\n\n    ngx_str_t  *value;\n\n    value = cf->args->elts;\n\n    if (cscf->resolver != NGX_CONF_UNSET_PTR) {\n        return \"is duplicate\";\n    }\n\n    if (ngx_strcmp(value[1].data, \"off\") == 0) {\n        cscf->resolver = NULL;\n        return NGX_CONF_OK;\n    }\n\n    cscf->resolver = ngx_resolver_create(cf, &value[1], cf->args->nelts - 1);\n    if (cscf->resolver == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nchar *\nngx_mail_capabilities(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    char  *p = conf;\n\n    ngx_str_t    *c, *value;\n    ngx_uint_t    i;\n    ngx_array_t  *a;\n\n    a = (ngx_array_t *) (p + cmd->offset);\n\n    value = cf->args->elts;\n\n    for (i = 1; i < cf->args->nelts; i++) {\n        c = ngx_array_push(a);\n        if (c == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        *c = value[i];\n    }\n\n    return NGX_CONF_OK;\n}\n"
  },
  {
    "path": "src/mail/ngx_mail_handler.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n#include <ngx_mail.h>\n\n\nstatic void ngx_mail_proxy_protocol_handler(ngx_event_t *rev);\nstatic void ngx_mail_init_session_handler(ngx_event_t *rev);\nstatic void ngx_mail_init_session(ngx_connection_t *c);\n\n#if (NGX_MAIL_SSL)\nstatic void ngx_mail_ssl_init_connection(ngx_ssl_t *ssl, ngx_connection_t *c);\nstatic void ngx_mail_ssl_handshake_handler(ngx_connection_t *c);\nstatic ngx_int_t ngx_mail_verify_cert(ngx_mail_session_t *s,\n    ngx_connection_t *c);\n#endif\n\n\nvoid\nngx_mail_init_connection(ngx_connection_t *c)\n{\n    size_t                     len;\n    ngx_uint_t                 i;\n    ngx_event_t               *rev;\n    ngx_mail_port_t           *port;\n    struct sockaddr           *sa;\n    struct sockaddr_in        *sin;\n    ngx_mail_log_ctx_t        *ctx;\n    ngx_mail_in_addr_t        *addr;\n    ngx_mail_session_t        *s;\n    ngx_mail_addr_conf_t      *addr_conf;\n    ngx_mail_core_srv_conf_t  *cscf;\n    u_char                     text[NGX_SOCKADDR_STRLEN];\n#if (NGX_HAVE_INET6)\n    struct sockaddr_in6       *sin6;\n    ngx_mail_in6_addr_t       *addr6;\n#endif\n\n\n    /* find the server configuration for the address:port */\n\n    port = c->listening->servers;\n\n    if (port->naddrs > 1) {\n\n        /*\n         * There are several addresses on this port and one of them\n         * is the \"*:port\" wildcard so getsockname() is needed to determine\n         * the server address.\n         *\n         * AcceptEx() already gave this address.\n         */\n\n        if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) {\n            ngx_mail_close_connection(c);\n            return;\n        }\n\n        sa = c->local_sockaddr;\n\n        switch (sa->sa_family) {\n\n#if (NGX_HAVE_INET6)\n        case AF_INET6:\n            sin6 = (struct sockaddr_in6 *) sa;\n\n            addr6 = port->addrs;\n\n            /* the last address is \"*\" */\n\n            for (i = 0; i < port->naddrs - 1; i++) {\n                if (ngx_memcmp(&addr6[i].addr6, &sin6->sin6_addr, 16) == 0) {\n                    break;\n                }\n            }\n\n            addr_conf = &addr6[i].conf;\n\n            break;\n#endif\n\n        default: /* AF_INET */\n            sin = (struct sockaddr_in *) sa;\n\n            addr = port->addrs;\n\n            /* the last address is \"*\" */\n\n            for (i = 0; i < port->naddrs - 1; i++) {\n                if (addr[i].addr == sin->sin_addr.s_addr) {\n                    break;\n                }\n            }\n\n            addr_conf = &addr[i].conf;\n\n            break;\n        }\n\n    } else {\n        switch (c->local_sockaddr->sa_family) {\n\n#if (NGX_HAVE_INET6)\n        case AF_INET6:\n            addr6 = port->addrs;\n            addr_conf = &addr6[0].conf;\n            break;\n#endif\n\n        default: /* AF_INET */\n            addr = port->addrs;\n            addr_conf = &addr[0].conf;\n            break;\n        }\n    }\n\n    s = ngx_pcalloc(c->pool, sizeof(ngx_mail_session_t));\n    if (s == NULL) {\n        ngx_mail_close_connection(c);\n        return;\n    }\n\n    s->signature = NGX_MAIL_MODULE;\n\n    s->main_conf = addr_conf->ctx->main_conf;\n    s->srv_conf = addr_conf->ctx->srv_conf;\n\n#if (NGX_MAIL_SSL)\n    s->ssl = addr_conf->ssl;\n#endif\n\n    s->addr_text = &addr_conf->addr_text;\n\n    c->data = s;\n    s->connection = c;\n\n    cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);\n\n    ngx_set_connection_log(c, cscf->error_log);\n\n    len = ngx_sock_ntop(c->sockaddr, c->socklen, text, NGX_SOCKADDR_STRLEN, 1);\n\n    ngx_log_error(NGX_LOG_INFO, c->log, 0, \"*%uA client %*s connected to %V\",\n                  c->number, len, text, s->addr_text);\n\n    ctx = ngx_palloc(c->pool, sizeof(ngx_mail_log_ctx_t));\n    if (ctx == NULL) {\n        ngx_mail_close_connection(c);\n        return;\n    }\n\n    ctx->client = &c->addr_text;\n    ctx->session = s;\n\n    c->log->connection = c->number;\n    c->log->handler = ngx_mail_log_error;\n    c->log->data = ctx;\n    c->log->action = \"sending client greeting line\";\n\n    c->log_error = NGX_ERROR_INFO;\n\n    rev = c->read;\n    rev->handler = ngx_mail_init_session_handler;\n\n    if (addr_conf->proxy_protocol) {\n        c->log->action = \"reading PROXY protocol\";\n\n        rev->handler = ngx_mail_proxy_protocol_handler;\n\n        if (!rev->ready) {\n            ngx_add_timer(rev, cscf->timeout);\n\n            if (ngx_handle_read_event(rev, 0) != NGX_OK) {\n                ngx_mail_close_connection(c);\n            }\n\n            return;\n        }\n    }\n\n    if (ngx_use_accept_mutex) {\n        ngx_post_event(rev, &ngx_posted_events);\n        return;\n    }\n\n    rev->handler(rev);\n}\n\n\nstatic void\nngx_mail_proxy_protocol_handler(ngx_event_t *rev)\n{\n    u_char                    *p, buf[NGX_PROXY_PROTOCOL_MAX_HEADER];\n    size_t                     size;\n    ssize_t                    n;\n    ngx_err_t                  err;\n    ngx_connection_t          *c;\n    ngx_mail_session_t        *s;\n    ngx_mail_core_srv_conf_t  *cscf;\n\n    c = rev->data;\n    s = c->data;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0,\n                   \"mail PROXY protocol handler\");\n\n    if (rev->timedout) {\n        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, \"client timed out\");\n        c->timedout = 1;\n        ngx_mail_close_connection(c);\n        return;\n    }\n\n    n = recv(c->fd, (char *) buf, sizeof(buf), MSG_PEEK);\n\n    err = ngx_socket_errno;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, \"recv(): %z\", n);\n\n    if (n == -1) {\n        if (err == NGX_EAGAIN) {\n            rev->ready = 0;\n\n            if (!rev->timer_set) {\n                cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);\n                ngx_add_timer(rev, cscf->timeout);\n            }\n\n            if (ngx_handle_read_event(rev, 0) != NGX_OK) {\n                ngx_mail_close_connection(c);\n            }\n\n            return;\n        }\n\n        ngx_connection_error(c, err, \"recv() failed\");\n\n        ngx_mail_close_connection(c);\n        return;\n    }\n\n    p = ngx_proxy_protocol_read(c, buf, buf + n);\n\n    if (p == NULL) {\n        ngx_mail_close_connection(c);\n        return;\n    }\n\n    size = p - buf;\n\n    if (c->recv(c, buf, size) != (ssize_t) size) {\n        ngx_mail_close_connection(c);\n        return;\n    }\n\n    if (ngx_mail_realip_handler(s) != NGX_OK) {\n        ngx_mail_close_connection(c);\n        return;\n    }\n\n    ngx_mail_init_session_handler(rev);\n}\n\n\nstatic void\nngx_mail_init_session_handler(ngx_event_t *rev)\n{\n    ngx_connection_t  *c;\n\n    c = rev->data;\n\n#if (NGX_MAIL_SSL)\n    {\n    ngx_mail_session_t   *s;\n    ngx_mail_ssl_conf_t  *sslcf;\n\n    s = c->data;\n\n    sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);\n\n    if (sslcf->enable || s->ssl) {\n        c->log->action = \"SSL handshaking\";\n\n        ngx_mail_ssl_init_connection(&sslcf->ssl, c);\n        return;\n    }\n\n    }\n#endif\n\n    ngx_mail_init_session(c);\n}\n\n\n#if (NGX_MAIL_SSL)\n\nvoid\nngx_mail_starttls_handler(ngx_event_t *rev)\n{\n    ngx_connection_t     *c;\n    ngx_mail_session_t   *s;\n    ngx_mail_ssl_conf_t  *sslcf;\n\n    c = rev->data;\n    s = c->data;\n    s->starttls = 1;\n\n    c->log->action = \"in starttls state\";\n\n    sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);\n\n    ngx_mail_ssl_init_connection(&sslcf->ssl, c);\n}\n\n\nstatic void\nngx_mail_ssl_init_connection(ngx_ssl_t *ssl, ngx_connection_t *c)\n{\n    ngx_mail_session_t        *s;\n    ngx_mail_core_srv_conf_t  *cscf;\n\n    if (ngx_ssl_create_connection(ssl, c, 0) != NGX_OK) {\n        ngx_mail_close_connection(c);\n        return;\n    }\n\n    if (ngx_ssl_handshake(c) == NGX_AGAIN) {\n\n        s = c->data;\n\n        if (!c->read->timer_set) {\n            cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);\n            ngx_add_timer(c->read, cscf->timeout);\n        }\n\n        c->ssl->handler = ngx_mail_ssl_handshake_handler;\n\n        return;\n    }\n\n    ngx_mail_ssl_handshake_handler(c);\n}\n\n\nstatic void\nngx_mail_ssl_handshake_handler(ngx_connection_t *c)\n{\n    ngx_mail_session_t        *s;\n    ngx_mail_core_srv_conf_t  *cscf;\n\n    if (c->ssl->handshaked) {\n\n        s = c->data;\n\n        if (ngx_mail_verify_cert(s, c) != NGX_OK) {\n            return;\n        }\n\n        if (s->starttls) {\n            cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);\n\n            c->read->handler = cscf->protocol->init_protocol;\n            c->write->handler = ngx_mail_send;\n\n            cscf->protocol->init_protocol(c->read);\n\n            return;\n        }\n\n        c->read->ready = 0;\n\n        ngx_mail_init_session(c);\n        return;\n    }\n\n    ngx_mail_close_connection(c);\n}\n\n\nstatic ngx_int_t\nngx_mail_verify_cert(ngx_mail_session_t *s, ngx_connection_t *c)\n{\n    long                       rc;\n    X509                      *cert;\n    ngx_mail_ssl_conf_t       *sslcf;\n    ngx_mail_core_srv_conf_t  *cscf;\n\n    sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);\n\n    if (!sslcf->verify) {\n        return NGX_OK;\n    }\n\n    rc = SSL_get_verify_result(c->ssl->connection);\n\n    if (rc != X509_V_OK\n        && (sslcf->verify != 3 || !ngx_ssl_verify_error_optional(rc)))\n    {\n        ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                      \"client SSL certificate verify error: (%l:%s)\",\n                      rc, X509_verify_cert_error_string(rc));\n\n        ngx_ssl_remove_cached_session(c->ssl->session_ctx,\n                                      (SSL_get0_session(c->ssl->connection)));\n\n        cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);\n\n        s->out = cscf->protocol->cert_error;\n        s->quit = 1;\n\n        c->write->handler = ngx_mail_send;\n\n        ngx_mail_send(s->connection->write);\n        return NGX_ERROR;\n    }\n\n    if (sslcf->verify == 1) {\n        cert = SSL_get_peer_certificate(c->ssl->connection);\n\n        if (cert == NULL) {\n            ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                          \"client sent no required SSL certificate\");\n\n            ngx_ssl_remove_cached_session(c->ssl->session_ctx,\n                                       (SSL_get0_session(c->ssl->connection)));\n\n            cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);\n\n            s->out = cscf->protocol->no_cert;\n            s->quit = 1;\n\n            c->write->handler = ngx_mail_send;\n\n            ngx_mail_send(s->connection->write);\n            return NGX_ERROR;\n        }\n\n        X509_free(cert);\n    }\n\n    return NGX_OK;\n}\n\n#endif\n\n\nstatic void\nngx_mail_init_session(ngx_connection_t *c)\n{\n    ngx_mail_session_t        *s;\n    ngx_mail_core_srv_conf_t  *cscf;\n\n    s = c->data;\n\n    c->log->action = \"sending client greeting line\";\n\n    cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);\n\n    s->protocol = cscf->protocol->type;\n\n    s->ctx = ngx_pcalloc(c->pool, sizeof(void *) * ngx_mail_max_module);\n    if (s->ctx == NULL) {\n        ngx_mail_session_internal_server_error(s);\n        return;\n    }\n\n    c->write->handler = ngx_mail_send;\n\n    cscf->protocol->init_session(s, c);\n}\n\n\nngx_int_t\nngx_mail_salt(ngx_mail_session_t *s, ngx_connection_t *c,\n    ngx_mail_core_srv_conf_t *cscf)\n{\n    s->salt.data = ngx_pnalloc(c->pool,\n                               sizeof(\" <18446744073709551616.@>\" CRLF) - 1\n                               + NGX_TIME_T_LEN\n                               + cscf->server_name.len);\n    if (s->salt.data == NULL) {\n        return NGX_ERROR;\n    }\n\n    s->salt.len = ngx_sprintf(s->salt.data, \"<%ul.%T@%V>\" CRLF,\n                              ngx_random(), ngx_time(), &cscf->server_name)\n                  - s->salt.data;\n\n    return NGX_OK;\n}\n\n\n#if (NGX_MAIL_SSL)\n\nngx_int_t\nngx_mail_starttls_only(ngx_mail_session_t *s, ngx_connection_t *c)\n{\n    ngx_mail_ssl_conf_t  *sslcf;\n\n    if (c->ssl) {\n        return 0;\n    }\n\n    sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);\n\n    if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) {\n        return 1;\n    }\n\n    return 0;\n}\n\n#endif\n\n\nngx_int_t\nngx_mail_auth_plain(ngx_mail_session_t *s, ngx_connection_t *c, ngx_uint_t n)\n{\n    u_char     *p, *last;\n    ngx_str_t  *arg, plain;\n\n    arg = s->args.elts;\n\n#if (NGX_DEBUG_MAIL_PASSWD)\n    ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,\n                   \"mail auth plain: \\\"%V\\\"\", &arg[n]);\n#endif\n\n    plain.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[n].len));\n    if (plain.data == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (ngx_decode_base64(&plain, &arg[n]) != NGX_OK) {\n        ngx_log_error(NGX_LOG_INFO, c->log, 0,\n            \"client sent invalid base64 encoding in AUTH PLAIN command\");\n        return NGX_MAIL_PARSE_INVALID_COMMAND;\n    }\n\n    p = plain.data;\n    last = p + plain.len;\n\n    while (p < last && *p++) { /* void */ }\n\n    if (p == last) {\n        ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                      \"client sent invalid login in AUTH PLAIN command\");\n        return NGX_MAIL_PARSE_INVALID_COMMAND;\n    }\n\n    s->login.data = p;\n\n    while (p < last && *p) { p++; }\n\n    if (p == last) {\n        ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                      \"client sent invalid password in AUTH PLAIN command\");\n        return NGX_MAIL_PARSE_INVALID_COMMAND;\n    }\n\n    s->login.len = p++ - s->login.data;\n\n    s->passwd.len = last - p;\n    s->passwd.data = p;\n\n#if (NGX_DEBUG_MAIL_PASSWD)\n    ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0,\n                   \"mail auth plain: \\\"%V\\\" \\\"%V\\\"\", &s->login, &s->passwd);\n#endif\n\n    return NGX_DONE;\n}\n\n\nngx_int_t\nngx_mail_auth_login_username(ngx_mail_session_t *s, ngx_connection_t *c,\n    ngx_uint_t n)\n{\n    ngx_str_t  *arg;\n\n    arg = s->args.elts;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,\n                   \"mail auth login username: \\\"%V\\\"\", &arg[n]);\n\n    s->login.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[n].len));\n    if (s->login.data == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (ngx_decode_base64(&s->login, &arg[n]) != NGX_OK) {\n        ngx_log_error(NGX_LOG_INFO, c->log, 0,\n            \"client sent invalid base64 encoding in AUTH LOGIN command\");\n        return NGX_MAIL_PARSE_INVALID_COMMAND;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,\n                   \"mail auth login username: \\\"%V\\\"\", &s->login);\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_mail_auth_login_password(ngx_mail_session_t *s, ngx_connection_t *c)\n{\n    ngx_str_t  *arg;\n\n    arg = s->args.elts;\n\n#if (NGX_DEBUG_MAIL_PASSWD)\n    ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,\n                   \"mail auth login password: \\\"%V\\\"\", &arg[0]);\n#endif\n\n    s->passwd.data = ngx_pnalloc(c->pool,\n                                 ngx_base64_decoded_length(arg[0].len));\n    if (s->passwd.data == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (ngx_decode_base64(&s->passwd, &arg[0]) != NGX_OK) {\n        ngx_log_error(NGX_LOG_INFO, c->log, 0,\n            \"client sent invalid base64 encoding in AUTH LOGIN command\");\n        return NGX_MAIL_PARSE_INVALID_COMMAND;\n    }\n\n#if (NGX_DEBUG_MAIL_PASSWD)\n    ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,\n                   \"mail auth login password: \\\"%V\\\"\", &s->passwd);\n#endif\n\n    return NGX_DONE;\n}\n\n\nngx_int_t\nngx_mail_auth_cram_md5_salt(ngx_mail_session_t *s, ngx_connection_t *c,\n    char *prefix, size_t len)\n{\n    u_char      *p;\n    ngx_str_t    salt;\n    ngx_uint_t   n;\n\n    p = ngx_pnalloc(c->pool, len + ngx_base64_encoded_length(s->salt.len) + 2);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    salt.data = ngx_cpymem(p, prefix, len);\n    s->salt.len -= 2;\n\n    ngx_encode_base64(&salt, &s->salt);\n\n    s->salt.len += 2;\n    n = len + salt.len;\n    p[n++] = CR; p[n++] = LF;\n\n    s->out.len = n;\n    s->out.data = p;\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_mail_auth_cram_md5(ngx_mail_session_t *s, ngx_connection_t *c)\n{\n    u_char     *p, *last;\n    ngx_str_t  *arg;\n\n    arg = s->args.elts;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,\n                   \"mail auth cram-md5: \\\"%V\\\"\", &arg[0]);\n\n    s->login.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[0].len));\n    if (s->login.data == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (ngx_decode_base64(&s->login, &arg[0]) != NGX_OK) {\n        ngx_log_error(NGX_LOG_INFO, c->log, 0,\n            \"client sent invalid base64 encoding in AUTH CRAM-MD5 command\");\n        return NGX_MAIL_PARSE_INVALID_COMMAND;\n    }\n\n    p = s->login.data;\n    last = p + s->login.len;\n\n    while (p < last) {\n        if (*p++ == ' ') {\n            s->login.len = p - s->login.data - 1;\n            s->passwd.len = last - p;\n            s->passwd.data = p;\n            break;\n        }\n    }\n\n    if (s->passwd.len != 32) {\n        ngx_log_error(NGX_LOG_INFO, c->log, 0,\n            \"client sent invalid CRAM-MD5 hash in AUTH CRAM-MD5 command\");\n        return NGX_MAIL_PARSE_INVALID_COMMAND;\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0,\n                   \"mail auth cram-md5: \\\"%V\\\" \\\"%V\\\"\", &s->login, &s->passwd);\n\n    s->auth_method = NGX_MAIL_AUTH_CRAM_MD5;\n\n    return NGX_DONE;\n}\n\n\nngx_int_t\nngx_mail_auth_external(ngx_mail_session_t *s, ngx_connection_t *c,\n    ngx_uint_t n)\n{\n    ngx_str_t  *arg, external;\n\n    arg = s->args.elts;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,\n                   \"mail auth external: \\\"%V\\\"\", &arg[n]);\n\n    external.data = ngx_pnalloc(c->pool, ngx_base64_decoded_length(arg[n].len));\n    if (external.data == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (ngx_decode_base64(&external, &arg[n]) != NGX_OK) {\n        ngx_log_error(NGX_LOG_INFO, c->log, 0,\n            \"client sent invalid base64 encoding in AUTH EXTERNAL command\");\n        return NGX_MAIL_PARSE_INVALID_COMMAND;\n    }\n\n    s->login.len = external.len;\n    s->login.data = external.data;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,\n                   \"mail auth external: \\\"%V\\\"\", &s->login);\n\n    s->auth_method = NGX_MAIL_AUTH_EXTERNAL;\n\n    return NGX_DONE;\n}\n\n\nvoid\nngx_mail_send(ngx_event_t *wev)\n{\n    ngx_int_t                  n;\n    ngx_connection_t          *c;\n    ngx_mail_session_t        *s;\n    ngx_mail_core_srv_conf_t  *cscf;\n\n    c = wev->data;\n    s = c->data;\n\n    if (wev->timedout) {\n        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, \"client timed out\");\n        c->timedout = 1;\n        ngx_mail_close_connection(c);\n        return;\n    }\n\n    if (s->out.len == 0) {\n        if (ngx_handle_write_event(c->write, 0) != NGX_OK) {\n            ngx_mail_close_connection(c);\n        }\n\n        return;\n    }\n\n    n = c->send(c, s->out.data, s->out.len);\n\n    if (n > 0) {\n        s->out.data += n;\n        s->out.len -= n;\n\n        if (s->out.len != 0) {\n            goto again;\n        }\n\n        if (wev->timer_set) {\n            ngx_del_timer(wev);\n        }\n\n        if (s->quit) {\n            ngx_mail_close_connection(c);\n            return;\n        }\n\n        if (s->blocked) {\n            c->read->handler(c->read);\n        }\n\n        return;\n    }\n\n    if (n == NGX_ERROR) {\n        ngx_mail_close_connection(c);\n        return;\n    }\n\n    /* n == NGX_AGAIN */\n\nagain:\n\n    cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);\n\n    ngx_add_timer(c->write, cscf->timeout);\n\n    if (ngx_handle_write_event(c->write, 0) != NGX_OK) {\n        ngx_mail_close_connection(c);\n        return;\n    }\n}\n\n\nngx_int_t\nngx_mail_read_command(ngx_mail_session_t *s, ngx_connection_t *c)\n{\n    ssize_t                    n;\n    ngx_int_t                  rc;\n    ngx_str_t                  l;\n    ngx_mail_core_srv_conf_t  *cscf;\n\n    if (s->buffer->last < s->buffer->end) {\n\n        n = c->recv(c, s->buffer->last, s->buffer->end - s->buffer->last);\n\n        if (n == NGX_ERROR || n == 0) {\n            ngx_mail_close_connection(c);\n            return NGX_ERROR;\n        }\n\n        if (n > 0) {\n            s->buffer->last += n;\n        }\n\n        if (n == NGX_AGAIN) {\n            if (s->buffer->pos == s->buffer->last) {\n                return NGX_AGAIN;\n            }\n        }\n    }\n\n    cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);\n\n    rc = cscf->protocol->parse_command(s);\n\n    if (rc == NGX_AGAIN) {\n\n        if (s->buffer->last < s->buffer->end) {\n            return rc;\n        }\n\n        l.len = s->buffer->last - s->buffer->start;\n        l.data = s->buffer->start;\n\n        ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                      \"client sent too long command \\\"%V\\\"\", &l);\n\n        s->quit = 1;\n\n        return NGX_MAIL_PARSE_INVALID_COMMAND;\n    }\n\n    if (rc == NGX_MAIL_PARSE_INVALID_COMMAND) {\n\n        s->errors++;\n\n        if (s->errors >= cscf->max_errors) {\n            ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                          \"client sent too many invalid commands\");\n            s->quit = 1;\n        }\n\n        return rc;\n    }\n\n    if (rc == NGX_IMAP_NEXT) {\n        return rc;\n    }\n\n    if (rc == NGX_ERROR) {\n        ngx_mail_close_connection(c);\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nvoid\nngx_mail_auth(ngx_mail_session_t *s, ngx_connection_t *c)\n{\n    s->args.nelts = 0;\n\n    if (s->buffer->pos == s->buffer->last) {\n        s->buffer->pos = s->buffer->start;\n        s->buffer->last = s->buffer->start;\n    }\n\n    s->state = 0;\n\n    if (c->read->timer_set) {\n        ngx_del_timer(c->read);\n    }\n\n    s->login_attempt++;\n\n    ngx_mail_auth_http_init(s);\n}\n\n\nvoid\nngx_mail_session_internal_server_error(ngx_mail_session_t *s)\n{\n    ngx_mail_core_srv_conf_t  *cscf;\n\n    cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);\n\n    s->out = cscf->protocol->internal_server_error;\n    s->quit = 1;\n\n    ngx_mail_send(s->connection->write);\n}\n\n\nvoid\nngx_mail_close_connection(ngx_connection_t *c)\n{\n    ngx_pool_t  *pool;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,\n                   \"close mail connection: %d\", c->fd);\n\n#if (NGX_MAIL_SSL)\n\n    if (c->ssl) {\n        if (ngx_ssl_shutdown(c) == NGX_AGAIN) {\n            c->ssl->handler = ngx_mail_close_connection;\n            return;\n        }\n    }\n\n#endif\n\n#if (NGX_STAT_STUB)\n    (void) ngx_atomic_fetch_add(ngx_stat_active, -1);\n#endif\n\n    c->destroyed = 1;\n\n    pool = c->pool;\n\n    ngx_close_connection(c);\n\n    ngx_destroy_pool(pool);\n}\n\n\nu_char *\nngx_mail_log_error(ngx_log_t *log, u_char *buf, size_t len)\n{\n    u_char              *p;\n    ngx_mail_session_t  *s;\n    ngx_mail_log_ctx_t  *ctx;\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    ctx = log->data;\n\n    p = ngx_snprintf(buf, len, \", client: %V\", ctx->client);\n    len -= p - buf;\n    buf = p;\n\n    s = ctx->session;\n\n    if (s == NULL) {\n        return p;\n    }\n\n    p = ngx_snprintf(buf, len, \"%s, server: %V\",\n                     s->starttls ? \" using starttls\" : \"\",\n                     s->addr_text);\n    len -= p - buf;\n    buf = p;\n\n    if (s->login.len == 0) {\n        return p;\n    }\n\n    p = ngx_snprintf(buf, len, \", login: \\\"%V\\\"\", &s->login);\n    len -= p - buf;\n    buf = p;\n\n    if (s->proxy == NULL) {\n        return p;\n    }\n\n    p = ngx_snprintf(buf, len, \", upstream: %V\", s->proxy->upstream.name);\n\n    return p;\n}\n"
  },
  {
    "path": "src/mail/ngx_mail_imap_handler.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n#include <ngx_mail.h>\n#include <ngx_mail_imap_module.h>\n\n\nstatic ngx_int_t ngx_mail_imap_login(ngx_mail_session_t *s,\n    ngx_connection_t *c);\nstatic ngx_int_t ngx_mail_imap_authenticate(ngx_mail_session_t *s,\n    ngx_connection_t *c);\nstatic ngx_int_t ngx_mail_imap_capability(ngx_mail_session_t *s,\n    ngx_connection_t *c);\nstatic ngx_int_t ngx_mail_imap_starttls(ngx_mail_session_t *s,\n    ngx_connection_t *c);\n\n\nstatic u_char  imap_greeting[] = \"* OK IMAP4 ready\" CRLF;\nstatic u_char  imap_star[] = \"* \";\nstatic u_char  imap_ok[] = \"OK completed\" CRLF;\nstatic u_char  imap_next[] = \"+ OK\" CRLF;\nstatic u_char  imap_plain_next[] = \"+ \" CRLF;\nstatic u_char  imap_username[] = \"+ VXNlcm5hbWU6\" CRLF;\nstatic u_char  imap_password[] = \"+ UGFzc3dvcmQ6\" CRLF;\nstatic u_char  imap_bye[] = \"* BYE\" CRLF;\nstatic u_char  imap_invalid_command[] = \"BAD invalid command\" CRLF;\n\n\nvoid\nngx_mail_imap_init_session(ngx_mail_session_t *s, ngx_connection_t *c)\n{\n    ngx_mail_core_srv_conf_t  *cscf;\n\n    cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);\n\n    ngx_str_set(&s->out, imap_greeting);\n\n    c->read->handler = ngx_mail_imap_init_protocol;\n\n    ngx_add_timer(c->read, cscf->timeout);\n\n    if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n        ngx_mail_close_connection(c);\n    }\n\n    ngx_mail_send(c->write);\n}\n\n\nvoid\nngx_mail_imap_init_protocol(ngx_event_t *rev)\n{\n    ngx_connection_t          *c;\n    ngx_mail_session_t        *s;\n    ngx_mail_imap_srv_conf_t  *iscf;\n\n    c = rev->data;\n\n    c->log->action = \"in auth state\";\n\n    if (rev->timedout) {\n        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, \"client timed out\");\n        c->timedout = 1;\n        ngx_mail_close_connection(c);\n        return;\n    }\n\n    s = c->data;\n\n    if (s->buffer == NULL) {\n        if (ngx_array_init(&s->args, c->pool, 2, sizeof(ngx_str_t))\n            == NGX_ERROR)\n        {\n            ngx_mail_session_internal_server_error(s);\n            return;\n        }\n\n        iscf = ngx_mail_get_module_srv_conf(s, ngx_mail_imap_module);\n\n        s->buffer = ngx_create_temp_buf(c->pool, iscf->client_buffer_size);\n        if (s->buffer == NULL) {\n            ngx_mail_session_internal_server_error(s);\n            return;\n        }\n    }\n\n    s->mail_state = ngx_imap_start;\n    c->read->handler = ngx_mail_imap_auth_state;\n\n    ngx_mail_imap_auth_state(rev);\n}\n\n\nvoid\nngx_mail_imap_auth_state(ngx_event_t *rev)\n{\n    u_char              *p;\n    ngx_int_t            rc;\n    ngx_uint_t           tag;\n    ngx_connection_t    *c;\n    ngx_mail_session_t  *s;\n\n    c = rev->data;\n    s = c->data;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, \"imap auth state\");\n\n    if (rev->timedout) {\n        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, \"client timed out\");\n        c->timedout = 1;\n        ngx_mail_close_connection(c);\n        return;\n    }\n\n    if (s->out.len) {\n        ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, \"imap send handler busy\");\n        s->blocked = 1;\n\n        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n            ngx_mail_close_connection(c);\n            return;\n        }\n\n        return;\n    }\n\n    s->blocked = 0;\n\n    rc = ngx_mail_read_command(s, c);\n\n    if (rc == NGX_AGAIN) {\n        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n            ngx_mail_session_internal_server_error(s);\n            return;\n        }\n\n        return;\n    }\n\n    if (rc == NGX_ERROR) {\n        return;\n    }\n\n    tag = 1;\n    s->text.len = 0;\n    ngx_str_set(&s->out, imap_ok);\n\n    if (rc == NGX_OK) {\n\n        ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, \"imap auth command: %i\",\n                       s->command);\n\n        switch (s->mail_state) {\n\n        case ngx_imap_start:\n\n            switch (s->command) {\n\n            case NGX_IMAP_LOGIN:\n                rc = ngx_mail_imap_login(s, c);\n                break;\n\n            case NGX_IMAP_AUTHENTICATE:\n                rc = ngx_mail_imap_authenticate(s, c);\n                tag = (rc != NGX_OK);\n                break;\n\n            case NGX_IMAP_CAPABILITY:\n                rc = ngx_mail_imap_capability(s, c);\n                break;\n\n            case NGX_IMAP_LOGOUT:\n                s->quit = 1;\n                ngx_str_set(&s->text, imap_bye);\n                break;\n\n            case NGX_IMAP_NOOP:\n                break;\n\n            case NGX_IMAP_STARTTLS:\n                rc = ngx_mail_imap_starttls(s, c);\n                break;\n\n            default:\n                rc = NGX_MAIL_PARSE_INVALID_COMMAND;\n                break;\n            }\n\n            break;\n\n        case ngx_imap_auth_login_username:\n            rc = ngx_mail_auth_login_username(s, c, 0);\n\n            tag = 0;\n            ngx_str_set(&s->out, imap_password);\n            s->mail_state = ngx_imap_auth_login_password;\n\n            break;\n\n        case ngx_imap_auth_login_password:\n            rc = ngx_mail_auth_login_password(s, c);\n            break;\n\n        case ngx_imap_auth_plain:\n            rc = ngx_mail_auth_plain(s, c, 0);\n            break;\n\n        case ngx_imap_auth_cram_md5:\n            rc = ngx_mail_auth_cram_md5(s, c);\n            break;\n\n        case ngx_imap_auth_external:\n            rc = ngx_mail_auth_external(s, c, 0);\n            break;\n        }\n\n    } else if (rc == NGX_IMAP_NEXT) {\n        tag = 0;\n        ngx_str_set(&s->out, imap_next);\n    }\n\n    if (s->buffer->pos < s->buffer->last) {\n        s->blocked = 1;\n    }\n\n    switch (rc) {\n\n    case NGX_DONE:\n        ngx_mail_auth(s, c);\n        return;\n\n    case NGX_ERROR:\n        ngx_mail_session_internal_server_error(s);\n        return;\n\n    case NGX_MAIL_PARSE_INVALID_COMMAND:\n        s->state = 0;\n        ngx_str_set(&s->out, imap_invalid_command);\n        s->mail_state = ngx_imap_start;\n        break;\n    }\n\n    if (tag) {\n        if (s->tag.len == 0) {\n            ngx_str_set(&s->tag, imap_star);\n        }\n\n        if (s->tagged_line.len < s->tag.len + s->text.len + s->out.len) {\n            s->tagged_line.len = s->tag.len + s->text.len + s->out.len;\n            s->tagged_line.data = ngx_pnalloc(c->pool, s->tagged_line.len);\n            if (s->tagged_line.data == NULL) {\n                ngx_mail_close_connection(c);\n                return;\n            }\n        }\n\n        p = s->tagged_line.data;\n\n        if (s->text.len) {\n            p = ngx_cpymem(p, s->text.data, s->text.len);\n        }\n\n        p = ngx_cpymem(p, s->tag.data, s->tag.len);\n        ngx_memcpy(p, s->out.data, s->out.len);\n\n        s->out.len = s->text.len + s->tag.len + s->out.len;\n        s->out.data = s->tagged_line.data;\n    }\n\n    if (rc != NGX_IMAP_NEXT) {\n        s->args.nelts = 0;\n\n        if (s->state) {\n            /* preserve tag */\n            s->arg_start = s->buffer->pos;\n\n        } else {\n            if (s->buffer->pos == s->buffer->last) {\n                s->buffer->pos = s->buffer->start;\n                s->buffer->last = s->buffer->start;\n            }\n\n            s->tag.len = 0;\n        }\n    }\n\n    if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n        ngx_mail_session_internal_server_error(s);\n        return;\n    }\n\n    ngx_mail_send(c->write);\n}\n\n\nstatic ngx_int_t\nngx_mail_imap_login(ngx_mail_session_t *s, ngx_connection_t *c)\n{\n    ngx_str_t  *arg;\n\n#if (NGX_MAIL_SSL)\n    if (ngx_mail_starttls_only(s, c)) {\n        return NGX_MAIL_PARSE_INVALID_COMMAND;\n    }\n#endif\n\n    arg = s->args.elts;\n\n    if (s->args.nelts != 2 || arg[0].len == 0) {\n        return NGX_MAIL_PARSE_INVALID_COMMAND;\n    }\n\n    s->login.len = arg[0].len;\n    s->login.data = ngx_pnalloc(c->pool, s->login.len);\n    if (s->login.data == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_memcpy(s->login.data, arg[0].data, s->login.len);\n\n    s->passwd.len = arg[1].len;\n    s->passwd.data = ngx_pnalloc(c->pool, s->passwd.len);\n    if (s->passwd.data == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_memcpy(s->passwd.data, arg[1].data, s->passwd.len);\n\n#if (NGX_DEBUG_MAIL_PASSWD)\n    ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0,\n                   \"imap login:\\\"%V\\\" passwd:\\\"%V\\\"\",\n                   &s->login, &s->passwd);\n#else\n    ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,\n                   \"imap login:\\\"%V\\\"\", &s->login);\n#endif\n\n    return NGX_DONE;\n}\n\n\nstatic ngx_int_t\nngx_mail_imap_authenticate(ngx_mail_session_t *s, ngx_connection_t *c)\n{\n    ngx_int_t                  rc;\n    ngx_mail_core_srv_conf_t  *cscf;\n    ngx_mail_imap_srv_conf_t  *iscf;\n\n#if (NGX_MAIL_SSL)\n    if (ngx_mail_starttls_only(s, c)) {\n        return NGX_MAIL_PARSE_INVALID_COMMAND;\n    }\n#endif\n\n    iscf = ngx_mail_get_module_srv_conf(s, ngx_mail_imap_module);\n\n    rc = ngx_mail_auth_parse(s, c);\n\n    switch (rc) {\n\n    case NGX_MAIL_AUTH_LOGIN:\n\n        ngx_str_set(&s->out, imap_username);\n        s->mail_state = ngx_imap_auth_login_username;\n\n        return NGX_OK;\n\n    case NGX_MAIL_AUTH_LOGIN_USERNAME:\n\n        ngx_str_set(&s->out, imap_password);\n        s->mail_state = ngx_imap_auth_login_password;\n\n        return ngx_mail_auth_login_username(s, c, 1);\n\n    case NGX_MAIL_AUTH_PLAIN:\n\n        ngx_str_set(&s->out, imap_plain_next);\n        s->mail_state = ngx_imap_auth_plain;\n\n        return NGX_OK;\n\n    case NGX_MAIL_AUTH_CRAM_MD5:\n\n        if (!(iscf->auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED)) {\n            return NGX_MAIL_PARSE_INVALID_COMMAND;\n        }\n\n        if (s->salt.data == NULL) {\n            cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);\n\n            if (ngx_mail_salt(s, c, cscf) != NGX_OK) {\n                return NGX_ERROR;\n            }\n        }\n\n        if (ngx_mail_auth_cram_md5_salt(s, c, \"+ \", 2) == NGX_OK) {\n            s->mail_state = ngx_imap_auth_cram_md5;\n            return NGX_OK;\n        }\n\n        return NGX_ERROR;\n\n    case NGX_MAIL_AUTH_EXTERNAL:\n\n        if (!(iscf->auth_methods & NGX_MAIL_AUTH_EXTERNAL_ENABLED)) {\n            return NGX_MAIL_PARSE_INVALID_COMMAND;\n        }\n\n        ngx_str_set(&s->out, imap_username);\n        s->mail_state = ngx_imap_auth_external;\n\n        return NGX_OK;\n    }\n\n    return rc;\n}\n\n\nstatic ngx_int_t\nngx_mail_imap_capability(ngx_mail_session_t *s, ngx_connection_t *c)\n{\n    ngx_mail_imap_srv_conf_t  *iscf;\n\n    iscf = ngx_mail_get_module_srv_conf(s, ngx_mail_imap_module);\n\n#if (NGX_MAIL_SSL)\n\n    if (c->ssl == NULL) {\n        ngx_mail_ssl_conf_t  *sslcf;\n\n        sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);\n\n        if (sslcf->starttls == NGX_MAIL_STARTTLS_ON) {\n            s->text = iscf->starttls_capability;\n            return NGX_OK;\n        }\n\n        if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) {\n            s->text = iscf->starttls_only_capability;\n            return NGX_OK;\n        }\n    }\n#endif\n\n    s->text = iscf->capability;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_mail_imap_starttls(ngx_mail_session_t *s, ngx_connection_t *c)\n{\n#if (NGX_MAIL_SSL)\n    ngx_mail_ssl_conf_t  *sslcf;\n\n    if (c->ssl == NULL) {\n        sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);\n        if (sslcf->starttls) {\n            s->buffer->pos = s->buffer->start;\n            s->buffer->last = s->buffer->start;\n            c->read->handler = ngx_mail_starttls_handler;\n            return NGX_OK;\n        }\n    }\n\n#endif\n\n    return NGX_MAIL_PARSE_INVALID_COMMAND;\n}\n"
  },
  {
    "path": "src/mail/ngx_mail_imap_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n#include <ngx_mail.h>\n#include <ngx_mail_imap_module.h>\n\n\nstatic void *ngx_mail_imap_create_srv_conf(ngx_conf_t *cf);\nstatic char *ngx_mail_imap_merge_srv_conf(ngx_conf_t *cf, void *parent,\n    void *child);\n\n\nstatic ngx_str_t  ngx_mail_imap_default_capabilities[] = {\n    ngx_string(\"IMAP4\"),\n    ngx_string(\"IMAP4rev1\"),\n    ngx_string(\"UIDPLUS\"),\n    ngx_null_string\n};\n\n\nstatic ngx_conf_bitmask_t  ngx_mail_imap_auth_methods[] = {\n    { ngx_string(\"plain\"), NGX_MAIL_AUTH_PLAIN_ENABLED },\n    { ngx_string(\"login\"), NGX_MAIL_AUTH_LOGIN_ENABLED },\n    { ngx_string(\"cram-md5\"), NGX_MAIL_AUTH_CRAM_MD5_ENABLED },\n    { ngx_string(\"external\"), NGX_MAIL_AUTH_EXTERNAL_ENABLED },\n    { ngx_null_string, 0 }\n};\n\n\nstatic ngx_str_t  ngx_mail_imap_auth_methods_names[] = {\n    ngx_string(\"AUTH=PLAIN\"),\n    ngx_string(\"AUTH=LOGIN\"),\n    ngx_null_string,  /* APOP */\n    ngx_string(\"AUTH=CRAM-MD5\"),\n    ngx_string(\"AUTH=EXTERNAL\"),\n    ngx_null_string   /* NONE */\n};\n\n\nstatic ngx_mail_protocol_t  ngx_mail_imap_protocol = {\n    ngx_string(\"imap\"),\n    ngx_string(\"\\x04imap\"),\n    { 143, 993, 0, 0 },\n    NGX_MAIL_IMAP_PROTOCOL,\n\n    ngx_mail_imap_init_session,\n    ngx_mail_imap_init_protocol,\n    ngx_mail_imap_parse_command,\n    ngx_mail_imap_auth_state,\n\n    ngx_string(\"* BAD internal server error\" CRLF),\n    ngx_string(\"* BYE SSL certificate error\" CRLF),\n    ngx_string(\"* BYE No required SSL certificate\" CRLF)\n};\n\n\nstatic ngx_command_t  ngx_mail_imap_commands[] = {\n\n    { ngx_string(\"imap_client_buffer\"),\n      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_MAIL_SRV_CONF_OFFSET,\n      offsetof(ngx_mail_imap_srv_conf_t, client_buffer_size),\n      NULL },\n\n    { ngx_string(\"imap_capabilities\"),\n      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE,\n      ngx_mail_capabilities,\n      NGX_MAIL_SRV_CONF_OFFSET,\n      offsetof(ngx_mail_imap_srv_conf_t, capabilities),\n      NULL },\n\n    { ngx_string(\"imap_auth\"),\n      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE,\n      ngx_conf_set_bitmask_slot,\n      NGX_MAIL_SRV_CONF_OFFSET,\n      offsetof(ngx_mail_imap_srv_conf_t, auth_methods),\n      &ngx_mail_imap_auth_methods },\n\n      ngx_null_command\n};\n\n\nstatic ngx_mail_module_t  ngx_mail_imap_module_ctx = {\n    &ngx_mail_imap_protocol,               /* protocol */\n\n    NULL,                                  /* create main configuration */\n    NULL,                                  /* init main configuration */\n\n    ngx_mail_imap_create_srv_conf,         /* create server configuration */\n    ngx_mail_imap_merge_srv_conf           /* merge server configuration */\n};\n\n\nngx_module_t  ngx_mail_imap_module = {\n    NGX_MODULE_V1,\n    &ngx_mail_imap_module_ctx,             /* module context */\n    ngx_mail_imap_commands,                /* module directives */\n    NGX_MAIL_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 void *\nngx_mail_imap_create_srv_conf(ngx_conf_t *cf)\n{\n    ngx_mail_imap_srv_conf_t  *iscf;\n\n    iscf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_imap_srv_conf_t));\n    if (iscf == NULL) {\n        return NULL;\n    }\n\n    iscf->client_buffer_size = NGX_CONF_UNSET_SIZE;\n\n    if (ngx_array_init(&iscf->capabilities, cf->pool, 4, sizeof(ngx_str_t))\n        != NGX_OK)\n    {\n        return NULL;\n    }\n\n    return iscf;\n}\n\n\nstatic char *\nngx_mail_imap_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_mail_imap_srv_conf_t *prev = parent;\n    ngx_mail_imap_srv_conf_t *conf = child;\n\n    u_char      *p, *auth;\n    size_t       size;\n    ngx_str_t   *c, *d;\n    ngx_uint_t   i, m;\n\n    ngx_conf_merge_size_value(conf->client_buffer_size,\n                              prev->client_buffer_size,\n                              (size_t) ngx_pagesize);\n\n    ngx_conf_merge_bitmask_value(conf->auth_methods,\n                              prev->auth_methods,\n                              (NGX_CONF_BITMASK_SET\n                               |NGX_MAIL_AUTH_PLAIN_ENABLED));\n\n\n    if (conf->capabilities.nelts == 0) {\n        conf->capabilities = prev->capabilities;\n    }\n\n    if (conf->capabilities.nelts == 0) {\n\n        for (d = ngx_mail_imap_default_capabilities; d->len; d++) {\n            c = ngx_array_push(&conf->capabilities);\n            if (c == NULL) {\n                return NGX_CONF_ERROR;\n            }\n\n            *c = *d;\n        }\n    }\n\n    size = sizeof(\"* CAPABILITY\" CRLF) - 1;\n\n    c = conf->capabilities.elts;\n    for (i = 0; i < conf->capabilities.nelts; i++) {\n        size += 1 + c[i].len;\n    }\n\n    for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0;\n         m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED;\n         m <<= 1, i++)\n    {\n        if (m & conf->auth_methods) {\n            size += 1 + ngx_mail_imap_auth_methods_names[i].len;\n        }\n    }\n\n    p = ngx_pnalloc(cf->pool, size);\n    if (p == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    conf->capability.len = size;\n    conf->capability.data = p;\n\n    p = ngx_cpymem(p, \"* CAPABILITY\", sizeof(\"* CAPABILITY\") - 1);\n\n    for (i = 0; i < conf->capabilities.nelts; i++) {\n        *p++ = ' ';\n        p = ngx_cpymem(p, c[i].data, c[i].len);\n    }\n\n    auth = p;\n\n    for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0;\n         m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED;\n         m <<= 1, i++)\n    {\n        if (m & conf->auth_methods) {\n            *p++ = ' ';\n            p = ngx_cpymem(p, ngx_mail_imap_auth_methods_names[i].data,\n                           ngx_mail_imap_auth_methods_names[i].len);\n        }\n    }\n\n    *p++ = CR; *p = LF;\n\n\n    size += sizeof(\" STARTTLS\") - 1;\n\n    p = ngx_pnalloc(cf->pool, size);\n    if (p == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    conf->starttls_capability.len = size;\n    conf->starttls_capability.data = p;\n\n    p = ngx_cpymem(p, conf->capability.data,\n                   conf->capability.len - (sizeof(CRLF) - 1));\n    p = ngx_cpymem(p, \" STARTTLS\", sizeof(\" STARTTLS\") - 1);\n    *p++ = CR; *p = LF;\n\n\n    size = (auth - conf->capability.data) + sizeof(CRLF) - 1\n            + sizeof(\" STARTTLS LOGINDISABLED\") - 1;\n\n    p = ngx_pnalloc(cf->pool, size);\n    if (p == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    conf->starttls_only_capability.len = size;\n    conf->starttls_only_capability.data = p;\n\n    p = ngx_cpymem(p, conf->capability.data,\n                   auth - conf->capability.data);\n    p = ngx_cpymem(p, \" STARTTLS LOGINDISABLED\",\n                   sizeof(\" STARTTLS LOGINDISABLED\") - 1);\n    *p++ = CR; *p = LF;\n\n    return NGX_CONF_OK;\n}\n"
  },
  {
    "path": "src/mail/ngx_mail_imap_module.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_MAIL_IMAP_MODULE_H_INCLUDED_\n#define _NGX_MAIL_IMAP_MODULE_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_mail.h>\n\n\ntypedef struct {\n    size_t       client_buffer_size;\n\n    ngx_str_t    capability;\n    ngx_str_t    starttls_capability;\n    ngx_str_t    starttls_only_capability;\n\n    ngx_uint_t   auth_methods;\n\n    ngx_array_t  capabilities;\n} ngx_mail_imap_srv_conf_t;\n\n\nvoid ngx_mail_imap_init_session(ngx_mail_session_t *s, ngx_connection_t *c);\nvoid ngx_mail_imap_init_protocol(ngx_event_t *rev);\nvoid ngx_mail_imap_auth_state(ngx_event_t *rev);\nngx_int_t ngx_mail_imap_parse_command(ngx_mail_session_t *s);\n\n\nextern ngx_module_t  ngx_mail_imap_module;\n\n\n#endif /* _NGX_MAIL_IMAP_MODULE_H_INCLUDED_ */\n"
  },
  {
    "path": "src/mail/ngx_mail_parse.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n#include <ngx_mail.h>\n#include <ngx_mail_pop3_module.h>\n#include <ngx_mail_imap_module.h>\n#include <ngx_mail_smtp_module.h>\n\n\nngx_int_t\nngx_mail_pop3_parse_command(ngx_mail_session_t *s)\n{\n    u_char      ch, *p, *c, c0, c1, c2, c3;\n    ngx_str_t  *arg;\n    enum {\n        sw_start = 0,\n        sw_command,\n        sw_invalid,\n        sw_spaces_before_argument,\n        sw_argument,\n        sw_almost_done\n    } state;\n\n    state = s->state;\n\n    for (p = s->buffer->pos; p < s->buffer->last; p++) {\n        ch = *p;\n\n        switch (state) {\n\n        /* POP3 command */\n        case sw_start:\n            s->cmd_start = p;\n            state = sw_command;\n\n            /* fall through */\n\n        case sw_command:\n            if (ch == ' ' || ch == CR || ch == LF) {\n                c = s->cmd_start;\n\n                if (p - c == 4) {\n\n                    c0 = ngx_toupper(c[0]);\n                    c1 = ngx_toupper(c[1]);\n                    c2 = ngx_toupper(c[2]);\n                    c3 = ngx_toupper(c[3]);\n\n                    if (c0 == 'U' && c1 == 'S' && c2 == 'E' && c3 == 'R')\n                    {\n                        s->command = NGX_POP3_USER;\n\n                    } else if (c0 == 'P' && c1 == 'A' && c2 == 'S' && c3 == 'S')\n                    {\n                        s->command = NGX_POP3_PASS;\n\n                    } else if (c0 == 'A' && c1 == 'P' && c2 == 'O' && c3 == 'P')\n                    {\n                        s->command = NGX_POP3_APOP;\n\n                    } else if (c0 == 'Q' && c1 == 'U' && c2 == 'I' && c3 == 'T')\n                    {\n                        s->command = NGX_POP3_QUIT;\n\n                    } else if (c0 == 'C' && c1 == 'A' && c2 == 'P' && c3 == 'A')\n                    {\n                        s->command = NGX_POP3_CAPA;\n\n                    } else if (c0 == 'A' && c1 == 'U' && c2 == 'T' && c3 == 'H')\n                    {\n                        s->command = NGX_POP3_AUTH;\n\n                    } else if (c0 == 'N' && c1 == 'O' && c2 == 'O' && c3 == 'P')\n                    {\n                        s->command = NGX_POP3_NOOP;\n#if (NGX_MAIL_SSL)\n                    } else if (c0 == 'S' && c1 == 'T' && c2 == 'L' && c3 == 'S')\n                    {\n                        s->command = NGX_POP3_STLS;\n#endif\n                    } else {\n                        goto invalid;\n                    }\n\n                } else {\n                    goto invalid;\n                }\n\n                s->cmd.data = s->cmd_start;\n                s->cmd.len = p - s->cmd_start;\n\n                switch (ch) {\n                case ' ':\n                    state = sw_spaces_before_argument;\n                    break;\n                case CR:\n                    state = sw_almost_done;\n                    break;\n                case LF:\n                    goto done;\n                }\n                break;\n            }\n\n            if ((ch < 'A' || ch > 'Z') && (ch < 'a' || ch > 'z')) {\n                goto invalid;\n            }\n\n            break;\n\n        case sw_invalid:\n            goto invalid;\n\n        case sw_spaces_before_argument:\n            switch (ch) {\n            case ' ':\n                break;\n            case CR:\n                state = sw_almost_done;\n                break;\n            case LF:\n                goto done;\n            default:\n                if (s->args.nelts <= 2) {\n                    state = sw_argument;\n                    s->arg_start = p;\n                    break;\n                }\n                goto invalid;\n            }\n            break;\n\n        case sw_argument:\n            switch (ch) {\n\n            case ' ':\n\n                /*\n                 * the space should be considered as part of the at username\n                 * or password, but not of argument in other commands\n                 */\n\n                if (s->command == NGX_POP3_USER\n                    || s->command == NGX_POP3_PASS)\n                {\n                    break;\n                }\n\n                /* fall through */\n\n            case CR:\n            case LF:\n                arg = ngx_array_push(&s->args);\n                if (arg == NULL) {\n                    return NGX_ERROR;\n                }\n                arg->len = p - s->arg_start;\n                arg->data = s->arg_start;\n                s->arg_start = NULL;\n\n                switch (ch) {\n                case ' ':\n                    state = sw_spaces_before_argument;\n                    break;\n                case CR:\n                    state = sw_almost_done;\n                    break;\n                case LF:\n                    goto done;\n                }\n                break;\n\n            default:\n                break;\n            }\n            break;\n\n        case sw_almost_done:\n            switch (ch) {\n            case LF:\n                goto done;\n            default:\n                goto invalid;\n            }\n        }\n    }\n\n    s->buffer->pos = p;\n    s->state = state;\n\n    return NGX_AGAIN;\n\ndone:\n\n    s->buffer->pos = p + 1;\n    s->state = (s->command != NGX_POP3_AUTH) ? sw_start : sw_argument;\n\n    return NGX_OK;\n\ninvalid:\n\n    s->state = sw_invalid;\n\n    /* skip invalid command till LF */\n\n    for ( /* void */ ; p < s->buffer->last; p++) {\n        if (*p == LF) {\n            s->state = sw_start;\n            s->buffer->pos = p + 1;\n            return NGX_MAIL_PARSE_INVALID_COMMAND;\n        }\n    }\n\n    s->buffer->pos = p;\n\n    return NGX_AGAIN;\n}\n\n\nngx_int_t\nngx_mail_imap_parse_command(ngx_mail_session_t *s)\n{\n    u_char      ch, *p, *c, *dst, *src, *end;\n    ngx_str_t  *arg;\n    enum {\n        sw_start = 0,\n        sw_tag,\n        sw_invalid,\n        sw_spaces_before_command,\n        sw_command,\n        sw_spaces_before_argument,\n        sw_argument,\n        sw_backslash,\n        sw_literal,\n        sw_no_sync_literal_argument,\n        sw_start_literal_argument,\n        sw_literal_argument,\n        sw_end_literal_argument,\n        sw_almost_done\n    } state;\n\n    state = s->state;\n\n    for (p = s->buffer->pos; p < s->buffer->last; p++) {\n        ch = *p;\n\n        switch (state) {\n\n        /* IMAP tag */\n        case sw_start:\n            s->tag_start = p;\n            state = sw_tag;\n\n            /* fall through */\n\n        case sw_tag:\n            switch (ch) {\n            case ' ':\n                s->tag.len = p - s->tag_start + 1;\n                s->tag.data = s->tag_start;\n                state = sw_spaces_before_command;\n                break;\n            case CR:\n            case LF:\n                goto invalid;\n            default:\n                if ((ch < 'A' || ch > 'Z') && (ch < 'a' || ch > 'z')\n                    && (ch < '0' || ch > '9') && ch != '-' && ch != '.'\n                    && ch != '_')\n                {\n                    goto invalid;\n                }\n                if (p - s->tag_start > 31) {\n                    goto invalid;\n                }\n                break;\n            }\n            break;\n\n        case sw_invalid:\n            goto invalid;\n\n        case sw_spaces_before_command:\n            switch (ch) {\n            case ' ':\n                break;\n            case CR:\n            case LF:\n                goto invalid;\n            default:\n                s->cmd_start = p;\n                state = sw_command;\n                break;\n            }\n            break;\n\n        case sw_command:\n            if (ch == ' ' || ch == CR || ch == LF) {\n\n                c = s->cmd_start;\n\n                switch (p - c) {\n\n                case 4:\n                    if ((c[0] == 'N' || c[0] == 'n')\n                        && (c[1] == 'O'|| c[1] == 'o')\n                        && (c[2] == 'O'|| c[2] == 'o')\n                        && (c[3] == 'P'|| c[3] == 'p'))\n                    {\n                        s->command = NGX_IMAP_NOOP;\n\n                    } else {\n                        goto invalid;\n                    }\n                    break;\n\n                case 5:\n                    if ((c[0] == 'L'|| c[0] == 'l')\n                        && (c[1] == 'O'|| c[1] == 'o')\n                        && (c[2] == 'G'|| c[2] == 'g')\n                        && (c[3] == 'I'|| c[3] == 'i')\n                        && (c[4] == 'N'|| c[4] == 'n'))\n                    {\n                        s->command = NGX_IMAP_LOGIN;\n\n                    } else {\n                        goto invalid;\n                    }\n                    break;\n\n                case 6:\n                    if ((c[0] == 'L'|| c[0] == 'l')\n                        && (c[1] == 'O'|| c[1] == 'o')\n                        && (c[2] == 'G'|| c[2] == 'g')\n                        && (c[3] == 'O'|| c[3] == 'o')\n                        && (c[4] == 'U'|| c[4] == 'u')\n                        && (c[5] == 'T'|| c[5] == 't'))\n                    {\n                        s->command = NGX_IMAP_LOGOUT;\n\n                    } else {\n                        goto invalid;\n                    }\n                    break;\n\n#if (NGX_MAIL_SSL)\n                case 8:\n                    if ((c[0] == 'S'|| c[0] == 's')\n                        && (c[1] == 'T'|| c[1] == 't')\n                        && (c[2] == 'A'|| c[2] == 'a')\n                        && (c[3] == 'R'|| c[3] == 'r')\n                        && (c[4] == 'T'|| c[4] == 't')\n                        && (c[5] == 'T'|| c[5] == 't')\n                        && (c[6] == 'L'|| c[6] == 'l')\n                        && (c[7] == 'S'|| c[7] == 's'))\n                    {\n                        s->command = NGX_IMAP_STARTTLS;\n\n                    } else {\n                        goto invalid;\n                    }\n                    break;\n#endif\n\n                case 10:\n                    if ((c[0] == 'C'|| c[0] == 'c')\n                        && (c[1] == 'A'|| c[1] == 'a')\n                        && (c[2] == 'P'|| c[2] == 'p')\n                        && (c[3] == 'A'|| c[3] == 'a')\n                        && (c[4] == 'B'|| c[4] == 'b')\n                        && (c[5] == 'I'|| c[5] == 'i')\n                        && (c[6] == 'L'|| c[6] == 'l')\n                        && (c[7] == 'I'|| c[7] == 'i')\n                        && (c[8] == 'T'|| c[8] == 't')\n                        && (c[9] == 'Y'|| c[9] == 'y'))\n                    {\n                        s->command = NGX_IMAP_CAPABILITY;\n\n                    } else {\n                        goto invalid;\n                    }\n                    break;\n\n                case 12:\n                    if ((c[0] == 'A'|| c[0] == 'a')\n                        && (c[1] == 'U'|| c[1] == 'u')\n                        && (c[2] == 'T'|| c[2] == 't')\n                        && (c[3] == 'H'|| c[3] == 'h')\n                        && (c[4] == 'E'|| c[4] == 'e')\n                        && (c[5] == 'N'|| c[5] == 'n')\n                        && (c[6] == 'T'|| c[6] == 't')\n                        && (c[7] == 'I'|| c[7] == 'i')\n                        && (c[8] == 'C'|| c[8] == 'c')\n                        && (c[9] == 'A'|| c[9] == 'a')\n                        && (c[10] == 'T'|| c[10] == 't')\n                        && (c[11] == 'E'|| c[11] == 'e'))\n                    {\n                        s->command = NGX_IMAP_AUTHENTICATE;\n\n                    } else {\n                        goto invalid;\n                    }\n                    break;\n\n                default:\n                    goto invalid;\n                }\n\n                s->cmd.data = s->cmd_start;\n                s->cmd.len = p - s->cmd_start;\n\n                switch (ch) {\n                case ' ':\n                    state = sw_spaces_before_argument;\n                    break;\n                case CR:\n                    state = sw_almost_done;\n                    break;\n                case LF:\n                    goto done;\n                }\n                break;\n            }\n\n            if ((ch < 'A' || ch > 'Z') && (ch < 'a' || ch > 'z')) {\n                goto invalid;\n            }\n\n            break;\n\n        case sw_spaces_before_argument:\n            switch (ch) {\n            case ' ':\n                break;\n            case CR:\n                state = sw_almost_done;\n                break;\n            case LF:\n                goto done;\n            case '\"':\n                if (s->args.nelts <= 2) {\n                    s->quoted = 1;\n                    s->arg_start = p + 1;\n                    state = sw_argument;\n                    break;\n                }\n                goto invalid;\n            case '{':\n                if (s->args.nelts <= 2) {\n                    state = sw_literal;\n                    break;\n                }\n                goto invalid;\n            default:\n                if (s->args.nelts <= 2) {\n                    s->arg_start = p;\n                    state = sw_argument;\n                    break;\n                }\n                goto invalid;\n            }\n            break;\n\n        case sw_argument:\n            if (ch == ' ' && s->quoted) {\n                break;\n            }\n\n            switch (ch) {\n            case '\"':\n                if (!s->quoted) {\n                    break;\n                }\n                s->quoted = 0;\n                /* fall through */\n            case ' ':\n            case CR:\n            case LF:\n                arg = ngx_array_push(&s->args);\n                if (arg == NULL) {\n                    return NGX_ERROR;\n                }\n                arg->len = p - s->arg_start;\n                arg->data = s->arg_start;\n\n                if (s->backslash) {\n                    dst = s->arg_start;\n                    end = p;\n\n                    for (src = dst; src < end; dst++) {\n                        *dst = *src;\n                        if (*src++ == '\\\\') {\n                            *dst = *src++;\n                        }\n                    }\n\n                    arg->len = dst - s->arg_start;\n                    s->backslash = 0;\n                }\n\n                s->arg_start = NULL;\n\n                switch (ch) {\n                case '\"':\n                case ' ':\n                    state = sw_spaces_before_argument;\n                    break;\n                case CR:\n                    state = sw_almost_done;\n                    break;\n                case LF:\n                    goto done;\n                }\n                break;\n            case '\\\\':\n                if (s->quoted) {\n                    s->backslash = 1;\n                    state = sw_backslash;\n                }\n                break;\n            }\n            break;\n\n        case sw_backslash:\n            switch (ch) {\n            case CR:\n            case LF:\n                goto invalid;\n            default:\n                state = sw_argument;\n            }\n            break;\n\n        case sw_literal:\n            if (ch >= '0' && ch <= '9') {\n                s->literal_len = s->literal_len * 10 + (ch - '0');\n                break;\n            }\n            if (ch == '}') {\n                state = sw_start_literal_argument;\n                break;\n            }\n            if (ch == '+') {\n                state = sw_no_sync_literal_argument;\n                break;\n            }\n            goto invalid;\n\n        case sw_no_sync_literal_argument:\n            if (ch == '}') {\n                s->no_sync_literal = 1;\n                state = sw_start_literal_argument;\n                break;\n            }\n            goto invalid;\n\n        case sw_start_literal_argument:\n            switch (ch) {\n            case CR:\n                break;\n            case LF:\n                s->buffer->pos = p + 1;\n                s->arg_start = p + 1;\n                if (s->no_sync_literal == 0) {\n                    s->state = sw_literal_argument;\n                    return NGX_IMAP_NEXT;\n                }\n                state = sw_literal_argument;\n                s->no_sync_literal = 0;\n                break;\n            default:\n                goto invalid;\n            }\n            break;\n\n        case sw_literal_argument:\n            if (s->literal_len && --s->literal_len) {\n                break;\n            }\n\n            arg = ngx_array_push(&s->args);\n            if (arg == NULL) {\n                return NGX_ERROR;\n            }\n            arg->len = p + 1 - s->arg_start;\n            arg->data = s->arg_start;\n            s->arg_start = NULL;\n            state = sw_end_literal_argument;\n\n            break;\n\n        case sw_end_literal_argument:\n            switch (ch) {\n            case '{':\n                if (s->args.nelts <= 2) {\n                    state = sw_literal;\n                    break;\n                }\n                goto invalid;\n            case CR:\n                state = sw_almost_done;\n                break;\n            case LF:\n                goto done;\n            default:\n                state = sw_spaces_before_argument;\n                break;\n            }\n            break;\n\n        case sw_almost_done:\n            switch (ch) {\n            case LF:\n                goto done;\n            default:\n                goto invalid;\n            }\n        }\n    }\n\n    s->buffer->pos = p;\n    s->state = state;\n\n    return NGX_AGAIN;\n\ndone:\n\n    s->buffer->pos = p + 1;\n    s->state = (s->command != NGX_IMAP_AUTHENTICATE) ? sw_start : sw_argument;\n\n    return NGX_OK;\n\ninvalid:\n\n    s->state = sw_invalid;\n    s->quoted = 0;\n    s->backslash = 0;\n    s->no_sync_literal = 0;\n    s->literal_len = 0;\n\n    /* skip invalid command till LF */\n\n    for ( /* void */ ; p < s->buffer->last; p++) {\n        if (*p == LF) {\n            s->state = sw_start;\n            s->buffer->pos = p + 1;\n\n            /* detect non-synchronizing literals */\n\n            if ((size_t) (p - s->buffer->start) > sizeof(\"{1+}\") - 1) {\n                p--;\n\n                if (*p == CR) {\n                    p--;\n                }\n\n                if (*p == '}' && *(p - 1) == '+') {\n                    s->quit = 1;\n                }\n            }\n\n            return NGX_MAIL_PARSE_INVALID_COMMAND;\n        }\n    }\n\n    s->buffer->pos = p;\n\n    return NGX_AGAIN;\n}\n\n\nngx_int_t\nngx_mail_smtp_parse_command(ngx_mail_session_t *s)\n{\n    u_char      ch, *p, *c, c0, c1, c2, c3;\n    ngx_str_t  *arg;\n    enum {\n        sw_start = 0,\n        sw_command,\n        sw_invalid,\n        sw_spaces_before_argument,\n        sw_argument,\n        sw_almost_done\n    } state;\n\n    state = s->state;\n\n    for (p = s->buffer->pos; p < s->buffer->last; p++) {\n        ch = *p;\n\n        switch (state) {\n\n        /* SMTP command */\n        case sw_start:\n            s->cmd_start = p;\n            state = sw_command;\n\n            /* fall through */\n\n        case sw_command:\n            if (ch == ' ' || ch == CR || ch == LF) {\n                c = s->cmd_start;\n\n                if (p - c == 4) {\n\n                    c0 = ngx_toupper(c[0]);\n                    c1 = ngx_toupper(c[1]);\n                    c2 = ngx_toupper(c[2]);\n                    c3 = ngx_toupper(c[3]);\n\n                    if (c0 == 'H' && c1 == 'E' && c2 == 'L' && c3 == 'O')\n                    {\n                        s->command = NGX_SMTP_HELO;\n\n                    } else if (c0 == 'E' && c1 == 'H' && c2 == 'L' && c3 == 'O')\n                    {\n                        s->command = NGX_SMTP_EHLO;\n\n                    } else if (c0 == 'Q' && c1 == 'U' && c2 == 'I' && c3 == 'T')\n                    {\n                        s->command = NGX_SMTP_QUIT;\n\n                    } else if (c0 == 'A' && c1 == 'U' && c2 == 'T' && c3 == 'H')\n                    {\n                        s->command = NGX_SMTP_AUTH;\n\n                    } else if (c0 == 'N' && c1 == 'O' && c2 == 'O' && c3 == 'P')\n                    {\n                        s->command = NGX_SMTP_NOOP;\n\n                    } else if (c0 == 'M' && c1 == 'A' && c2 == 'I' && c3 == 'L')\n                    {\n                        s->command = NGX_SMTP_MAIL;\n\n                    } else if (c0 == 'R' && c1 == 'S' && c2 == 'E' && c3 == 'T')\n                    {\n                        s->command = NGX_SMTP_RSET;\n\n                    } else if (c0 == 'R' && c1 == 'C' && c2 == 'P' && c3 == 'T')\n                    {\n                        s->command = NGX_SMTP_RCPT;\n\n                    } else if (c0 == 'V' && c1 == 'R' && c2 == 'F' && c3 == 'Y')\n                    {\n                        s->command = NGX_SMTP_VRFY;\n\n                    } else if (c0 == 'E' && c1 == 'X' && c2 == 'P' && c3 == 'N')\n                    {\n                        s->command = NGX_SMTP_EXPN;\n\n                    } else if (c0 == 'H' && c1 == 'E' && c2 == 'L' && c3 == 'P')\n                    {\n                        s->command = NGX_SMTP_HELP;\n\n                    } else {\n                        goto invalid;\n                    }\n#if (NGX_MAIL_SSL)\n                } else if (p - c == 8) {\n\n                    if ((c[0] == 'S'|| c[0] == 's')\n                        && (c[1] == 'T'|| c[1] == 't')\n                        && (c[2] == 'A'|| c[2] == 'a')\n                        && (c[3] == 'R'|| c[3] == 'r')\n                        && (c[4] == 'T'|| c[4] == 't')\n                        && (c[5] == 'T'|| c[5] == 't')\n                        && (c[6] == 'L'|| c[6] == 'l')\n                        && (c[7] == 'S'|| c[7] == 's'))\n                    {\n                        s->command = NGX_SMTP_STARTTLS;\n\n                    } else {\n                        goto invalid;\n                    }\n#endif\n                } else {\n                    goto invalid;\n                }\n\n                s->cmd.data = s->cmd_start;\n                s->cmd.len = p - s->cmd_start;\n\n                switch (ch) {\n                case ' ':\n                    state = sw_spaces_before_argument;\n                    break;\n                case CR:\n                    state = sw_almost_done;\n                    break;\n                case LF:\n                    goto done;\n                }\n                break;\n            }\n\n            if ((ch < 'A' || ch > 'Z') && (ch < 'a' || ch > 'z')) {\n                goto invalid;\n            }\n\n            break;\n\n        case sw_invalid:\n            goto invalid;\n\n        case sw_spaces_before_argument:\n            switch (ch) {\n            case ' ':\n                break;\n            case CR:\n                state = sw_almost_done;\n                break;\n            case LF:\n                goto done;\n            default:\n                if (s->args.nelts <= 10) {\n                    state = sw_argument;\n                    s->arg_start = p;\n                    break;\n                }\n                goto invalid;\n            }\n            break;\n\n        case sw_argument:\n            switch (ch) {\n            case ' ':\n            case CR:\n            case LF:\n                arg = ngx_array_push(&s->args);\n                if (arg == NULL) {\n                    return NGX_ERROR;\n                }\n                arg->len = p - s->arg_start;\n                arg->data = s->arg_start;\n                s->arg_start = NULL;\n\n                switch (ch) {\n                case ' ':\n                    state = sw_spaces_before_argument;\n                    break;\n                case CR:\n                    state = sw_almost_done;\n                    break;\n                case LF:\n                    goto done;\n                }\n                break;\n\n            default:\n                break;\n            }\n            break;\n\n        case sw_almost_done:\n            switch (ch) {\n            case LF:\n                goto done;\n            default:\n                goto invalid;\n            }\n        }\n    }\n\n    s->buffer->pos = p;\n    s->state = state;\n\n    return NGX_AGAIN;\n\ndone:\n\n    s->buffer->pos = p + 1;\n    s->state = (s->command != NGX_SMTP_AUTH) ? sw_start : sw_argument;\n\n    return NGX_OK;\n\ninvalid:\n\n    s->state = sw_invalid;\n\n    /* skip invalid command till LF */\n\n    for ( /* void */ ; p < s->buffer->last; p++) {\n        if (*p == LF) {\n            s->state = sw_start;\n            s->buffer->pos = p + 1;\n            return NGX_MAIL_PARSE_INVALID_COMMAND;\n        }\n    }\n\n    s->buffer->pos = p;\n\n    return NGX_AGAIN;\n}\n\n\nngx_int_t\nngx_mail_auth_parse(ngx_mail_session_t *s, ngx_connection_t *c)\n{\n    ngx_str_t                 *arg;\n\n#if (NGX_MAIL_SSL)\n    if (ngx_mail_starttls_only(s, c)) {\n        return NGX_MAIL_PARSE_INVALID_COMMAND;\n    }\n#endif\n\n    if (s->args.nelts == 0) {\n        return NGX_MAIL_PARSE_INVALID_COMMAND;\n    }\n\n    arg = s->args.elts;\n\n    if (arg[0].len == 5) {\n\n        if (ngx_strncasecmp(arg[0].data, (u_char *) \"LOGIN\", 5) == 0) {\n\n            if (s->args.nelts == 1) {\n                return NGX_MAIL_AUTH_LOGIN;\n            }\n\n            if (s->args.nelts == 2) {\n                return NGX_MAIL_AUTH_LOGIN_USERNAME;\n            }\n\n            return NGX_MAIL_PARSE_INVALID_COMMAND;\n        }\n\n        if (ngx_strncasecmp(arg[0].data, (u_char *) \"PLAIN\", 5) == 0) {\n\n            if (s->args.nelts == 1) {\n                return NGX_MAIL_AUTH_PLAIN;\n            }\n\n            if (s->args.nelts == 2) {\n                return ngx_mail_auth_plain(s, c, 1);\n            }\n        }\n\n        return NGX_MAIL_PARSE_INVALID_COMMAND;\n    }\n\n    if (arg[0].len == 8) {\n\n        if (ngx_strncasecmp(arg[0].data, (u_char *) \"CRAM-MD5\", 8) == 0) {\n\n            if (s->args.nelts != 1) {\n                return NGX_MAIL_PARSE_INVALID_COMMAND;\n            }\n\n            return NGX_MAIL_AUTH_CRAM_MD5;\n        }\n\n        if (ngx_strncasecmp(arg[0].data, (u_char *) \"EXTERNAL\", 8) == 0) {\n\n            if (s->args.nelts == 1) {\n                return NGX_MAIL_AUTH_EXTERNAL;\n            }\n\n            if (s->args.nelts == 2) {\n                return ngx_mail_auth_external(s, c, 1);\n            }\n        }\n\n        return NGX_MAIL_PARSE_INVALID_COMMAND;\n    }\n\n    return NGX_MAIL_PARSE_INVALID_COMMAND;\n}\n"
  },
  {
    "path": "src/mail/ngx_mail_pop3_handler.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n#include <ngx_mail.h>\n#include <ngx_mail_pop3_module.h>\n\n\nstatic ngx_int_t ngx_mail_pop3_user(ngx_mail_session_t *s, ngx_connection_t *c);\nstatic ngx_int_t ngx_mail_pop3_pass(ngx_mail_session_t *s, ngx_connection_t *c);\nstatic ngx_int_t ngx_mail_pop3_capa(ngx_mail_session_t *s, ngx_connection_t *c,\n    ngx_int_t stls);\nstatic ngx_int_t ngx_mail_pop3_stls(ngx_mail_session_t *s, ngx_connection_t *c);\nstatic ngx_int_t ngx_mail_pop3_apop(ngx_mail_session_t *s, ngx_connection_t *c);\nstatic ngx_int_t ngx_mail_pop3_auth(ngx_mail_session_t *s, ngx_connection_t *c);\n\n\nstatic u_char  pop3_greeting[] = \"+OK POP3 ready\" CRLF;\nstatic u_char  pop3_ok[] = \"+OK\" CRLF;\nstatic u_char  pop3_next[] = \"+ \" CRLF;\nstatic u_char  pop3_username[] = \"+ VXNlcm5hbWU6\" CRLF;\nstatic u_char  pop3_password[] = \"+ UGFzc3dvcmQ6\" CRLF;\nstatic u_char  pop3_invalid_command[] = \"-ERR invalid command\" CRLF;\n\n\nvoid\nngx_mail_pop3_init_session(ngx_mail_session_t *s, ngx_connection_t *c)\n{\n    u_char                    *p;\n    ngx_mail_core_srv_conf_t  *cscf;\n    ngx_mail_pop3_srv_conf_t  *pscf;\n\n    pscf = ngx_mail_get_module_srv_conf(s, ngx_mail_pop3_module);\n    cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);\n\n    if (pscf->auth_methods\n        & (NGX_MAIL_AUTH_APOP_ENABLED|NGX_MAIL_AUTH_CRAM_MD5_ENABLED))\n    {\n        if (ngx_mail_salt(s, c, cscf) != NGX_OK) {\n            ngx_mail_session_internal_server_error(s);\n            return;\n        }\n\n        s->out.data = ngx_pnalloc(c->pool, sizeof(pop3_greeting) + s->salt.len);\n        if (s->out.data == NULL) {\n            ngx_mail_session_internal_server_error(s);\n            return;\n        }\n\n        p = ngx_cpymem(s->out.data, pop3_greeting, sizeof(pop3_greeting) - 3);\n        *p++ = ' ';\n        p = ngx_cpymem(p, s->salt.data, s->salt.len);\n\n        s->out.len = p - s->out.data;\n\n    } else {\n        ngx_str_set(&s->out, pop3_greeting);\n    }\n\n    c->read->handler = ngx_mail_pop3_init_protocol;\n\n    ngx_add_timer(c->read, cscf->timeout);\n\n    if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n        ngx_mail_close_connection(c);\n    }\n\n    ngx_mail_send(c->write);\n}\n\n\nvoid\nngx_mail_pop3_init_protocol(ngx_event_t *rev)\n{\n    ngx_connection_t    *c;\n    ngx_mail_session_t  *s;\n\n    c = rev->data;\n\n    c->log->action = \"in auth state\";\n\n    if (rev->timedout) {\n        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, \"client timed out\");\n        c->timedout = 1;\n        ngx_mail_close_connection(c);\n        return;\n    }\n\n    s = c->data;\n\n    if (s->buffer == NULL) {\n        if (ngx_array_init(&s->args, c->pool, 2, sizeof(ngx_str_t))\n            == NGX_ERROR)\n        {\n            ngx_mail_session_internal_server_error(s);\n            return;\n        }\n\n        s->buffer = ngx_create_temp_buf(c->pool, 128);\n        if (s->buffer == NULL) {\n            ngx_mail_session_internal_server_error(s);\n            return;\n        }\n    }\n\n    s->mail_state = ngx_pop3_start;\n    c->read->handler = ngx_mail_pop3_auth_state;\n\n    ngx_mail_pop3_auth_state(rev);\n}\n\n\nvoid\nngx_mail_pop3_auth_state(ngx_event_t *rev)\n{\n    ngx_int_t            rc;\n    ngx_connection_t    *c;\n    ngx_mail_session_t  *s;\n\n    c = rev->data;\n    s = c->data;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, \"pop3 auth state\");\n\n    if (rev->timedout) {\n        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, \"client timed out\");\n        c->timedout = 1;\n        ngx_mail_close_connection(c);\n        return;\n    }\n\n    if (s->out.len) {\n        ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, \"pop3 send handler busy\");\n        s->blocked = 1;\n\n        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n            ngx_mail_close_connection(c);\n            return;\n        }\n\n        return;\n    }\n\n    s->blocked = 0;\n\n    rc = ngx_mail_read_command(s, c);\n\n    if (rc == NGX_AGAIN) {\n        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n            ngx_mail_session_internal_server_error(s);\n            return;\n        }\n\n        return;\n    }\n\n    if (rc == NGX_ERROR) {\n        return;\n    }\n\n    ngx_str_set(&s->out, pop3_ok);\n\n    if (rc == NGX_OK) {\n        switch (s->mail_state) {\n\n        case ngx_pop3_start:\n\n            switch (s->command) {\n\n            case NGX_POP3_USER:\n                rc = ngx_mail_pop3_user(s, c);\n                break;\n\n            case NGX_POP3_CAPA:\n                rc = ngx_mail_pop3_capa(s, c, 1);\n                break;\n\n            case NGX_POP3_APOP:\n                rc = ngx_mail_pop3_apop(s, c);\n                break;\n\n            case NGX_POP3_AUTH:\n                rc = ngx_mail_pop3_auth(s, c);\n                break;\n\n            case NGX_POP3_QUIT:\n                s->quit = 1;\n                break;\n\n            case NGX_POP3_NOOP:\n                break;\n\n            case NGX_POP3_STLS:\n                rc = ngx_mail_pop3_stls(s, c);\n                break;\n\n            default:\n                rc = NGX_MAIL_PARSE_INVALID_COMMAND;\n                break;\n            }\n\n            break;\n\n        case ngx_pop3_user:\n\n            switch (s->command) {\n\n            case NGX_POP3_PASS:\n                rc = ngx_mail_pop3_pass(s, c);\n                break;\n\n            case NGX_POP3_CAPA:\n                rc = ngx_mail_pop3_capa(s, c, 0);\n                break;\n\n            case NGX_POP3_QUIT:\n                s->quit = 1;\n                break;\n\n            case NGX_POP3_NOOP:\n                break;\n\n            default:\n                rc = NGX_MAIL_PARSE_INVALID_COMMAND;\n                break;\n            }\n\n            break;\n\n        /* suppress warnings */\n        case ngx_pop3_passwd:\n            break;\n\n        case ngx_pop3_auth_login_username:\n            rc = ngx_mail_auth_login_username(s, c, 0);\n\n            ngx_str_set(&s->out, pop3_password);\n            s->mail_state = ngx_pop3_auth_login_password;\n            break;\n\n        case ngx_pop3_auth_login_password:\n            rc = ngx_mail_auth_login_password(s, c);\n            break;\n\n        case ngx_pop3_auth_plain:\n            rc = ngx_mail_auth_plain(s, c, 0);\n            break;\n\n        case ngx_pop3_auth_cram_md5:\n            rc = ngx_mail_auth_cram_md5(s, c);\n            break;\n\n        case ngx_pop3_auth_external:\n            rc = ngx_mail_auth_external(s, c, 0);\n            break;\n        }\n    }\n\n    if (s->buffer->pos < s->buffer->last) {\n        s->blocked = 1;\n    }\n\n    switch (rc) {\n\n    case NGX_DONE:\n        ngx_mail_auth(s, c);\n        return;\n\n    case NGX_ERROR:\n        ngx_mail_session_internal_server_error(s);\n        return;\n\n    case NGX_MAIL_PARSE_INVALID_COMMAND:\n        s->mail_state = ngx_pop3_start;\n        s->state = 0;\n\n        ngx_str_set(&s->out, pop3_invalid_command);\n\n        /* fall through */\n\n    case NGX_OK:\n\n        s->args.nelts = 0;\n\n        if (s->buffer->pos == s->buffer->last) {\n            s->buffer->pos = s->buffer->start;\n            s->buffer->last = s->buffer->start;\n        }\n\n        if (s->state) {\n            s->arg_start = s->buffer->pos;\n        }\n\n        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n            ngx_mail_session_internal_server_error(s);\n            return;\n        }\n\n        ngx_mail_send(c->write);\n    }\n}\n\nstatic ngx_int_t\nngx_mail_pop3_user(ngx_mail_session_t *s, ngx_connection_t *c)\n{\n    ngx_str_t  *arg;\n\n#if (NGX_MAIL_SSL)\n    if (ngx_mail_starttls_only(s, c)) {\n        return NGX_MAIL_PARSE_INVALID_COMMAND;\n    }\n#endif\n\n    if (s->args.nelts != 1) {\n        return NGX_MAIL_PARSE_INVALID_COMMAND;\n    }\n\n    arg = s->args.elts;\n    s->login.len = arg[0].len;\n    s->login.data = ngx_pnalloc(c->pool, s->login.len);\n    if (s->login.data == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_memcpy(s->login.data, arg[0].data, s->login.len);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,\n                   \"pop3 login: \\\"%V\\\"\", &s->login);\n\n    s->mail_state = ngx_pop3_user;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_mail_pop3_pass(ngx_mail_session_t *s, ngx_connection_t *c)\n{\n    ngx_str_t  *arg;\n\n    if (s->args.nelts != 1) {\n        return NGX_MAIL_PARSE_INVALID_COMMAND;\n    }\n\n    arg = s->args.elts;\n    s->passwd.len = arg[0].len;\n    s->passwd.data = ngx_pnalloc(c->pool, s->passwd.len);\n    if (s->passwd.data == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_memcpy(s->passwd.data, arg[0].data, s->passwd.len);\n\n#if (NGX_DEBUG_MAIL_PASSWD)\n    ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,\n                   \"pop3 passwd: \\\"%V\\\"\", &s->passwd);\n#endif\n\n    return NGX_DONE;\n}\n\n\nstatic ngx_int_t\nngx_mail_pop3_capa(ngx_mail_session_t *s, ngx_connection_t *c, ngx_int_t stls)\n{\n    ngx_mail_pop3_srv_conf_t  *pscf;\n\n    pscf = ngx_mail_get_module_srv_conf(s, ngx_mail_pop3_module);\n\n#if (NGX_MAIL_SSL)\n\n    if (stls && c->ssl == NULL) {\n        ngx_mail_ssl_conf_t  *sslcf;\n\n        sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);\n\n        if (sslcf->starttls == NGX_MAIL_STARTTLS_ON) {\n            s->out = pscf->starttls_capability;\n            return NGX_OK;\n        }\n\n        if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) {\n            s->out = pscf->starttls_only_capability;\n            return NGX_OK;\n        }\n    }\n\n#endif\n\n    s->out = pscf->capability;\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_mail_pop3_stls(ngx_mail_session_t *s, ngx_connection_t *c)\n{\n#if (NGX_MAIL_SSL)\n    ngx_mail_ssl_conf_t  *sslcf;\n\n    if (c->ssl == NULL) {\n        sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);\n        if (sslcf->starttls) {\n            s->buffer->pos = s->buffer->start;\n            s->buffer->last = s->buffer->start;\n            c->read->handler = ngx_mail_starttls_handler;\n            return NGX_OK;\n        }\n    }\n\n#endif\n\n    return NGX_MAIL_PARSE_INVALID_COMMAND;\n}\n\n\nstatic ngx_int_t\nngx_mail_pop3_apop(ngx_mail_session_t *s, ngx_connection_t *c)\n{\n    ngx_str_t                 *arg;\n    ngx_mail_pop3_srv_conf_t  *pscf;\n\n#if (NGX_MAIL_SSL)\n    if (ngx_mail_starttls_only(s, c)) {\n        return NGX_MAIL_PARSE_INVALID_COMMAND;\n    }\n#endif\n\n    if (s->args.nelts != 2) {\n        return NGX_MAIL_PARSE_INVALID_COMMAND;\n    }\n\n    pscf = ngx_mail_get_module_srv_conf(s, ngx_mail_pop3_module);\n\n    if (!(pscf->auth_methods & NGX_MAIL_AUTH_APOP_ENABLED)) {\n        return NGX_MAIL_PARSE_INVALID_COMMAND;\n    }\n\n    arg = s->args.elts;\n\n    s->login.len = arg[0].len;\n    s->login.data = ngx_pnalloc(c->pool, s->login.len);\n    if (s->login.data == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_memcpy(s->login.data, arg[0].data, s->login.len);\n\n    s->passwd.len = arg[1].len;\n    s->passwd.data = ngx_pnalloc(c->pool, s->passwd.len);\n    if (s->passwd.data == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_memcpy(s->passwd.data, arg[1].data, s->passwd.len);\n\n    ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0,\n                   \"pop3 apop: \\\"%V\\\" \\\"%V\\\"\", &s->login, &s->passwd);\n\n    s->auth_method = NGX_MAIL_AUTH_APOP;\n\n    return NGX_DONE;\n}\n\n\nstatic ngx_int_t\nngx_mail_pop3_auth(ngx_mail_session_t *s, ngx_connection_t *c)\n{\n    ngx_int_t                  rc;\n    ngx_mail_pop3_srv_conf_t  *pscf;\n\n#if (NGX_MAIL_SSL)\n    if (ngx_mail_starttls_only(s, c)) {\n        return NGX_MAIL_PARSE_INVALID_COMMAND;\n    }\n#endif\n\n    pscf = ngx_mail_get_module_srv_conf(s, ngx_mail_pop3_module);\n\n    if (s->args.nelts == 0) {\n        s->out = pscf->auth_capability;\n        s->state = 0;\n\n        return NGX_OK;\n    }\n\n    rc = ngx_mail_auth_parse(s, c);\n\n    switch (rc) {\n\n    case NGX_MAIL_AUTH_LOGIN:\n\n        ngx_str_set(&s->out, pop3_username);\n        s->mail_state = ngx_pop3_auth_login_username;\n\n        return NGX_OK;\n\n    case NGX_MAIL_AUTH_LOGIN_USERNAME:\n\n        ngx_str_set(&s->out, pop3_password);\n        s->mail_state = ngx_pop3_auth_login_password;\n\n        return ngx_mail_auth_login_username(s, c, 1);\n\n    case NGX_MAIL_AUTH_PLAIN:\n\n        ngx_str_set(&s->out, pop3_next);\n        s->mail_state = ngx_pop3_auth_plain;\n\n        return NGX_OK;\n\n    case NGX_MAIL_AUTH_CRAM_MD5:\n\n        if (!(pscf->auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED)) {\n            return NGX_MAIL_PARSE_INVALID_COMMAND;\n        }\n\n        if (ngx_mail_auth_cram_md5_salt(s, c, \"+ \", 2) == NGX_OK) {\n            s->mail_state = ngx_pop3_auth_cram_md5;\n            return NGX_OK;\n        }\n\n        return NGX_ERROR;\n\n    case NGX_MAIL_AUTH_EXTERNAL:\n\n        if (!(pscf->auth_methods & NGX_MAIL_AUTH_EXTERNAL_ENABLED)) {\n            return NGX_MAIL_PARSE_INVALID_COMMAND;\n        }\n\n        ngx_str_set(&s->out, pop3_username);\n        s->mail_state = ngx_pop3_auth_external;\n\n        return NGX_OK;\n    }\n\n    return rc;\n}\n"
  },
  {
    "path": "src/mail/ngx_mail_pop3_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n#include <ngx_mail.h>\n#include <ngx_mail_pop3_module.h>\n\n\nstatic void *ngx_mail_pop3_create_srv_conf(ngx_conf_t *cf);\nstatic char *ngx_mail_pop3_merge_srv_conf(ngx_conf_t *cf, void *parent,\n    void *child);\n\n\nstatic ngx_str_t  ngx_mail_pop3_default_capabilities[] = {\n    ngx_string(\"TOP\"),\n    ngx_string(\"USER\"),\n    ngx_string(\"UIDL\"),\n    ngx_null_string\n};\n\n\nstatic ngx_conf_bitmask_t  ngx_mail_pop3_auth_methods[] = {\n    { ngx_string(\"plain\"), NGX_MAIL_AUTH_PLAIN_ENABLED },\n    { ngx_string(\"apop\"), NGX_MAIL_AUTH_APOP_ENABLED },\n    { ngx_string(\"cram-md5\"), NGX_MAIL_AUTH_CRAM_MD5_ENABLED },\n    { ngx_string(\"external\"), NGX_MAIL_AUTH_EXTERNAL_ENABLED },\n    { ngx_null_string, 0 }\n};\n\n\nstatic ngx_str_t  ngx_mail_pop3_auth_methods_names[] = {\n    ngx_string(\"PLAIN\"),\n    ngx_string(\"LOGIN\"),\n    ngx_null_string,  /* APOP */\n    ngx_string(\"CRAM-MD5\"),\n    ngx_string(\"EXTERNAL\"),\n    ngx_null_string   /* NONE */\n};\n\n\nstatic ngx_mail_protocol_t  ngx_mail_pop3_protocol = {\n    ngx_string(\"pop3\"),\n    ngx_string(\"\\x04pop3\"),\n    { 110, 995, 0, 0 },\n    NGX_MAIL_POP3_PROTOCOL,\n\n    ngx_mail_pop3_init_session,\n    ngx_mail_pop3_init_protocol,\n    ngx_mail_pop3_parse_command,\n    ngx_mail_pop3_auth_state,\n\n    ngx_string(\"-ERR internal server error\" CRLF),\n    ngx_string(\"-ERR SSL certificate error\" CRLF),\n    ngx_string(\"-ERR No required SSL certificate\" CRLF)\n};\n\n\nstatic ngx_command_t  ngx_mail_pop3_commands[] = {\n\n    { ngx_string(\"pop3_capabilities\"),\n      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE,\n      ngx_mail_capabilities,\n      NGX_MAIL_SRV_CONF_OFFSET,\n      offsetof(ngx_mail_pop3_srv_conf_t, capabilities),\n      NULL },\n\n    { ngx_string(\"pop3_auth\"),\n      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE,\n      ngx_conf_set_bitmask_slot,\n      NGX_MAIL_SRV_CONF_OFFSET,\n      offsetof(ngx_mail_pop3_srv_conf_t, auth_methods),\n      &ngx_mail_pop3_auth_methods },\n\n      ngx_null_command\n};\n\n\nstatic ngx_mail_module_t  ngx_mail_pop3_module_ctx = {\n    &ngx_mail_pop3_protocol,               /* protocol */\n\n    NULL,                                  /* create main configuration */\n    NULL,                                  /* init main configuration */\n\n    ngx_mail_pop3_create_srv_conf,         /* create server configuration */\n    ngx_mail_pop3_merge_srv_conf           /* merge server configuration */\n};\n\n\nngx_module_t  ngx_mail_pop3_module = {\n    NGX_MODULE_V1,\n    &ngx_mail_pop3_module_ctx,             /* module context */\n    ngx_mail_pop3_commands,                /* module directives */\n    NGX_MAIL_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 void *\nngx_mail_pop3_create_srv_conf(ngx_conf_t *cf)\n{\n    ngx_mail_pop3_srv_conf_t  *pscf;\n\n    pscf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_pop3_srv_conf_t));\n    if (pscf == NULL) {\n        return NULL;\n    }\n\n    if (ngx_array_init(&pscf->capabilities, cf->pool, 4, sizeof(ngx_str_t))\n        != NGX_OK)\n    {\n        return NULL;\n    }\n\n    return pscf;\n}\n\n\nstatic char *\nngx_mail_pop3_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_mail_pop3_srv_conf_t *prev = parent;\n    ngx_mail_pop3_srv_conf_t *conf = child;\n\n    u_char      *p;\n    size_t       size, stls_only_size;\n    ngx_str_t   *c, *d;\n    ngx_uint_t   i, m;\n\n    ngx_conf_merge_bitmask_value(conf->auth_methods,\n                                 prev->auth_methods,\n                                 (NGX_CONF_BITMASK_SET\n                                  |NGX_MAIL_AUTH_PLAIN_ENABLED));\n\n    if (conf->auth_methods & NGX_MAIL_AUTH_PLAIN_ENABLED) {\n        conf->auth_methods |= NGX_MAIL_AUTH_LOGIN_ENABLED;\n    }\n\n    if (conf->capabilities.nelts == 0) {\n        conf->capabilities = prev->capabilities;\n    }\n\n    if (conf->capabilities.nelts == 0) {\n\n        for (d = ngx_mail_pop3_default_capabilities; d->len; d++) {\n            c = ngx_array_push(&conf->capabilities);\n            if (c == NULL) {\n                return NGX_CONF_ERROR;\n            }\n\n            *c = *d;\n        }\n    }\n\n    size = sizeof(\"+OK Capability list follows\" CRLF) - 1\n           + sizeof(\".\" CRLF) - 1;\n\n    stls_only_size = size + sizeof(\"STLS\" CRLF) - 1;\n\n    c = conf->capabilities.elts;\n    for (i = 0; i < conf->capabilities.nelts; i++) {\n        size += c[i].len + sizeof(CRLF) - 1;\n\n        if (ngx_strcasecmp(c[i].data, (u_char *) \"USER\") == 0) {\n            continue;\n        }\n\n        stls_only_size += c[i].len + sizeof(CRLF) - 1;\n    }\n\n    size += sizeof(\"SASL\") - 1 + sizeof(CRLF) - 1;\n\n    for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0;\n         m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED;\n         m <<= 1, i++)\n    {\n        if (ngx_mail_pop3_auth_methods_names[i].len == 0) {\n            continue;\n        }\n\n        if (m & conf->auth_methods) {\n            size += 1 + ngx_mail_pop3_auth_methods_names[i].len;\n        }\n    }\n\n    p = ngx_pnalloc(cf->pool, size);\n    if (p == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    conf->capability.len = size;\n    conf->capability.data = p;\n\n    p = ngx_cpymem(p, \"+OK Capability list follows\" CRLF,\n                   sizeof(\"+OK Capability list follows\" CRLF) - 1);\n\n    for (i = 0; i < conf->capabilities.nelts; i++) {\n        p = ngx_cpymem(p, c[i].data, c[i].len);\n        *p++ = CR; *p++ = LF;\n    }\n\n    p = ngx_cpymem(p, \"SASL\", sizeof(\"SASL\") - 1);\n\n    for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0;\n         m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED;\n         m <<= 1, i++)\n    {\n        if (ngx_mail_pop3_auth_methods_names[i].len == 0) {\n            continue;\n        }\n\n        if (m & conf->auth_methods) {\n            *p++ = ' ';\n            p = ngx_cpymem(p, ngx_mail_pop3_auth_methods_names[i].data,\n                           ngx_mail_pop3_auth_methods_names[i].len);\n        }\n    }\n\n    *p++ = CR; *p++ = LF;\n\n    *p++ = '.'; *p++ = CR; *p = LF;\n\n\n    size += sizeof(\"STLS\" CRLF) - 1;\n\n    p = ngx_pnalloc(cf->pool, size);\n    if (p == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    conf->starttls_capability.len = size;\n    conf->starttls_capability.data = p;\n\n    p = ngx_cpymem(p, conf->capability.data,\n                   conf->capability.len - (sizeof(\".\" CRLF) - 1));\n\n    p = ngx_cpymem(p, \"STLS\" CRLF, sizeof(\"STLS\" CRLF) - 1);\n    *p++ = '.'; *p++ = CR; *p = LF;\n\n\n    size = sizeof(\"+OK methods supported:\" CRLF) - 1\n           + sizeof(\".\" CRLF) - 1;\n\n    for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0;\n         m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED;\n         m <<= 1, i++)\n    {\n        if (ngx_mail_pop3_auth_methods_names[i].len == 0) {\n            continue;\n        }\n\n        if (m & conf->auth_methods) {\n            size += ngx_mail_pop3_auth_methods_names[i].len\n                    + sizeof(CRLF) - 1;\n        }\n    }\n\n    p = ngx_pnalloc(cf->pool, size);\n    if (p == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    conf->auth_capability.data = p;\n    conf->auth_capability.len = size;\n\n    p = ngx_cpymem(p, \"+OK methods supported:\" CRLF,\n                   sizeof(\"+OK methods supported:\" CRLF) - 1);\n\n    for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0;\n         m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED;\n         m <<= 1, i++)\n    {\n        if (ngx_mail_pop3_auth_methods_names[i].len == 0) {\n            continue;\n        }\n\n        if (m & conf->auth_methods) {\n            p = ngx_cpymem(p, ngx_mail_pop3_auth_methods_names[i].data,\n                           ngx_mail_pop3_auth_methods_names[i].len);\n            *p++ = CR; *p++ = LF;\n        }\n    }\n\n    *p++ = '.'; *p++ = CR; *p = LF;\n\n\n    p = ngx_pnalloc(cf->pool, stls_only_size);\n    if (p == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    conf->starttls_only_capability.len = stls_only_size;\n    conf->starttls_only_capability.data = p;\n\n    p = ngx_cpymem(p, \"+OK Capability list follows\" CRLF,\n                   sizeof(\"+OK Capability list follows\" CRLF) - 1);\n\n    for (i = 0; i < conf->capabilities.nelts; i++) {\n        if (ngx_strcasecmp(c[i].data, (u_char *) \"USER\") == 0) {\n            continue;\n        }\n\n        p = ngx_cpymem(p, c[i].data, c[i].len);\n        *p++ = CR; *p++ = LF;\n    }\n\n    p = ngx_cpymem(p, \"STLS\" CRLF, sizeof(\"STLS\" CRLF) - 1);\n    *p++ = '.'; *p++ = CR; *p = LF;\n\n    return NGX_CONF_OK;\n}\n"
  },
  {
    "path": "src/mail/ngx_mail_pop3_module.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_MAIL_POP3_MODULE_H_INCLUDED_\n#define _NGX_MAIL_POP3_MODULE_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_mail.h>\n\n\ntypedef struct {\n    ngx_str_t    capability;\n    ngx_str_t    starttls_capability;\n    ngx_str_t    starttls_only_capability;\n    ngx_str_t    auth_capability;\n\n    ngx_uint_t   auth_methods;\n\n    ngx_array_t  capabilities;\n} ngx_mail_pop3_srv_conf_t;\n\n\nvoid ngx_mail_pop3_init_session(ngx_mail_session_t *s, ngx_connection_t *c);\nvoid ngx_mail_pop3_init_protocol(ngx_event_t *rev);\nvoid ngx_mail_pop3_auth_state(ngx_event_t *rev);\nngx_int_t ngx_mail_pop3_parse_command(ngx_mail_session_t *s);\n\n\nextern ngx_module_t  ngx_mail_pop3_module;\n\n\n#endif /* _NGX_MAIL_POP3_MODULE_H_INCLUDED_ */\n"
  },
  {
    "path": "src/mail/ngx_mail_proxy_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n#include <ngx_event_connect.h>\n#include <ngx_mail.h>\n\n\ntypedef struct {\n    ngx_flag_t  enable;\n    ngx_flag_t  pass_error_message;\n    ngx_flag_t  xclient;\n    ngx_flag_t  smtp_auth;\n    ngx_flag_t  proxy_protocol;\n    size_t      buffer_size;\n    ngx_msec_t  timeout;\n} ngx_mail_proxy_conf_t;\n\n\nstatic void ngx_mail_proxy_block_read(ngx_event_t *rev);\nstatic void ngx_mail_proxy_pop3_handler(ngx_event_t *rev);\nstatic void ngx_mail_proxy_imap_handler(ngx_event_t *rev);\nstatic void ngx_mail_proxy_smtp_handler(ngx_event_t *rev);\nstatic void ngx_mail_proxy_write_handler(ngx_event_t *wev);\nstatic ngx_int_t ngx_mail_proxy_send_proxy_protocol(ngx_mail_session_t *s);\nstatic ngx_int_t ngx_mail_proxy_read_response(ngx_mail_session_t *s,\n    ngx_uint_t state);\nstatic void ngx_mail_proxy_handler(ngx_event_t *ev);\nstatic void ngx_mail_proxy_upstream_error(ngx_mail_session_t *s);\nstatic void ngx_mail_proxy_internal_server_error(ngx_mail_session_t *s);\nstatic void ngx_mail_proxy_close_session(ngx_mail_session_t *s);\nstatic void *ngx_mail_proxy_create_conf(ngx_conf_t *cf);\nstatic char *ngx_mail_proxy_merge_conf(ngx_conf_t *cf, void *parent,\n    void *child);\n\n\nstatic ngx_command_t  ngx_mail_proxy_commands[] = {\n\n    { ngx_string(\"proxy\"),\n      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_MAIL_SRV_CONF_OFFSET,\n      offsetof(ngx_mail_proxy_conf_t, enable),\n      NULL },\n\n    { ngx_string(\"proxy_buffer\"),\n      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_MAIL_SRV_CONF_OFFSET,\n      offsetof(ngx_mail_proxy_conf_t, buffer_size),\n      NULL },\n\n    { ngx_string(\"proxy_timeout\"),\n      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_MAIL_SRV_CONF_OFFSET,\n      offsetof(ngx_mail_proxy_conf_t, timeout),\n      NULL },\n\n    { ngx_string(\"proxy_pass_error_message\"),\n      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_MAIL_SRV_CONF_OFFSET,\n      offsetof(ngx_mail_proxy_conf_t, pass_error_message),\n      NULL },\n\n    { ngx_string(\"xclient\"),\n      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_MAIL_SRV_CONF_OFFSET,\n      offsetof(ngx_mail_proxy_conf_t, xclient),\n      NULL },\n\n    { ngx_string(\"proxy_smtp_auth\"),\n      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_MAIL_SRV_CONF_OFFSET,\n      offsetof(ngx_mail_proxy_conf_t, smtp_auth),\n      NULL },\n\n    { ngx_string(\"proxy_protocol\"),\n      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_MAIL_SRV_CONF_OFFSET,\n      offsetof(ngx_mail_proxy_conf_t, proxy_protocol),\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_mail_module_t  ngx_mail_proxy_module_ctx = {\n    NULL,                                  /* protocol */\n\n    NULL,                                  /* create main configuration */\n    NULL,                                  /* init main configuration */\n\n    ngx_mail_proxy_create_conf,            /* create server configuration */\n    ngx_mail_proxy_merge_conf              /* merge server configuration */\n};\n\n\nngx_module_t  ngx_mail_proxy_module = {\n    NGX_MODULE_V1,\n    &ngx_mail_proxy_module_ctx,            /* module context */\n    ngx_mail_proxy_commands,               /* module directives */\n    NGX_MAIL_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 u_char  smtp_auth_ok[] = \"235 2.0.0 OK\" CRLF;\n\n\nvoid\nngx_mail_proxy_init(ngx_mail_session_t *s, ngx_addr_t *peer)\n{\n    ngx_int_t                  rc;\n    ngx_mail_proxy_ctx_t      *p;\n    ngx_mail_proxy_conf_t     *pcf;\n    ngx_mail_core_srv_conf_t  *cscf;\n\n    s->connection->log->action = \"connecting to upstream\";\n\n    cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);\n\n    p = ngx_pcalloc(s->connection->pool, sizeof(ngx_mail_proxy_ctx_t));\n    if (p == NULL) {\n        ngx_mail_session_internal_server_error(s);\n        return;\n    }\n\n    s->proxy = p;\n\n    p->upstream.sockaddr = peer->sockaddr;\n    p->upstream.socklen = peer->socklen;\n    p->upstream.name = &peer->name;\n    p->upstream.get = ngx_event_get_peer;\n    p->upstream.log = s->connection->log;\n    p->upstream.log_error = NGX_ERROR_ERR;\n\n    rc = ngx_event_connect_peer(&p->upstream);\n\n    if (rc == NGX_ERROR || rc == NGX_BUSY || rc == NGX_DECLINED) {\n        ngx_mail_proxy_internal_server_error(s);\n        return;\n    }\n\n    ngx_add_timer(p->upstream.connection->read, cscf->timeout);\n\n    p->upstream.connection->data = s;\n    p->upstream.connection->pool = s->connection->pool;\n\n    s->connection->read->handler = ngx_mail_proxy_block_read;\n    p->upstream.connection->write->handler = ngx_mail_proxy_write_handler;\n\n    pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);\n\n    s->proxy->buffer = ngx_create_temp_buf(s->connection->pool,\n                                           pcf->buffer_size);\n    if (s->proxy->buffer == NULL) {\n        ngx_mail_proxy_internal_server_error(s);\n        return;\n    }\n\n    s->proxy->proxy_protocol = pcf->proxy_protocol;\n\n    s->out.len = 0;\n\n    switch (s->protocol) {\n\n    case NGX_MAIL_POP3_PROTOCOL:\n        p->upstream.connection->read->handler = ngx_mail_proxy_pop3_handler;\n        s->mail_state = ngx_pop3_start;\n        break;\n\n    case NGX_MAIL_IMAP_PROTOCOL:\n        p->upstream.connection->read->handler = ngx_mail_proxy_imap_handler;\n        s->mail_state = ngx_imap_start;\n        break;\n\n    default: /* NGX_MAIL_SMTP_PROTOCOL */\n        p->upstream.connection->read->handler = ngx_mail_proxy_smtp_handler;\n        s->mail_state = ngx_smtp_start;\n        break;\n    }\n\n    if (rc == NGX_AGAIN) {\n        return;\n    }\n\n    ngx_mail_proxy_write_handler(p->upstream.connection->write);\n}\n\n\nstatic void\nngx_mail_proxy_block_read(ngx_event_t *rev)\n{\n    ngx_connection_t    *c;\n    ngx_mail_session_t  *s;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, \"mail proxy block read\");\n\n    if (ngx_handle_read_event(rev, 0) != NGX_OK) {\n        c = rev->data;\n        s = c->data;\n\n        ngx_mail_proxy_close_session(s);\n    }\n}\n\n\nstatic void\nngx_mail_proxy_pop3_handler(ngx_event_t *rev)\n{\n    u_char                 *p;\n    ngx_int_t               rc;\n    ngx_str_t               line;\n    ngx_connection_t       *c;\n    ngx_mail_session_t     *s;\n    ngx_mail_proxy_conf_t  *pcf;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,\n                   \"mail proxy pop3 auth handler\");\n\n    c = rev->data;\n    s = c->data;\n\n    if (rev->timedout) {\n        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,\n                      \"upstream timed out\");\n        c->timedout = 1;\n        ngx_mail_proxy_internal_server_error(s);\n        return;\n    }\n\n    if (s->proxy->proxy_protocol) {\n        ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, \"mail proxy pop3 busy\");\n\n        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n            ngx_mail_proxy_internal_server_error(s);\n            return;\n        }\n\n        return;\n    }\n\n    rc = ngx_mail_proxy_read_response(s, 0);\n\n    if (rc == NGX_AGAIN) {\n        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n            ngx_mail_proxy_internal_server_error(s);\n            return;\n        }\n\n        return;\n    }\n\n    if (rc == NGX_ERROR) {\n        ngx_mail_proxy_upstream_error(s);\n        return;\n    }\n\n    switch (s->mail_state) {\n\n    case ngx_pop3_start:\n        ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, \"mail proxy send user\");\n\n        s->connection->log->action = \"sending user name to upstream\";\n\n        line.len = sizeof(\"USER \")  - 1 + s->login.len + 2;\n        line.data = ngx_pnalloc(c->pool, line.len);\n        if (line.data == NULL) {\n            ngx_mail_proxy_internal_server_error(s);\n            return;\n        }\n\n        p = ngx_cpymem(line.data, \"USER \", sizeof(\"USER \") - 1);\n        p = ngx_cpymem(p, s->login.data, s->login.len);\n        *p++ = CR; *p = LF;\n\n        s->mail_state = ngx_pop3_user;\n        break;\n\n    case ngx_pop3_user:\n        ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, \"mail proxy send pass\");\n\n        s->connection->log->action = \"sending password to upstream\";\n\n        line.len = sizeof(\"PASS \")  - 1 + s->passwd.len + 2;\n        line.data = ngx_pnalloc(c->pool, line.len);\n        if (line.data == NULL) {\n            ngx_mail_proxy_internal_server_error(s);\n            return;\n        }\n\n        p = ngx_cpymem(line.data, \"PASS \", sizeof(\"PASS \") - 1);\n        p = ngx_cpymem(p, s->passwd.data, s->passwd.len);\n        *p++ = CR; *p = LF;\n\n        s->mail_state = ngx_pop3_passwd;\n        break;\n\n    case ngx_pop3_passwd:\n        s->connection->read->handler = ngx_mail_proxy_handler;\n        s->connection->write->handler = ngx_mail_proxy_handler;\n        rev->handler = ngx_mail_proxy_handler;\n        c->write->handler = ngx_mail_proxy_handler;\n\n        pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);\n        ngx_add_timer(s->connection->read, pcf->timeout);\n        ngx_del_timer(c->read);\n\n        c->log->action = NULL;\n        ngx_log_error(NGX_LOG_INFO, c->log, 0, \"client logged in\");\n\n        if (s->buffer->pos < s->buffer->last\n            || s->connection->read->ready)\n        {\n            ngx_post_event(c->write, &ngx_posted_events);\n        }\n\n        ngx_mail_proxy_handler(s->connection->write);\n\n        return;\n\n    default:\n#if (NGX_SUPPRESS_WARN)\n        ngx_str_null(&line);\n#endif\n        break;\n    }\n\n    if (c->send(c, line.data, line.len) < (ssize_t) line.len) {\n        /*\n         * we treat the incomplete sending as NGX_ERROR\n         * because it is very strange here\n         */\n        ngx_mail_proxy_internal_server_error(s);\n        return;\n    }\n\n    if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n        ngx_mail_proxy_internal_server_error(s);\n        return;\n    }\n\n    s->proxy->buffer->pos = s->proxy->buffer->start;\n    s->proxy->buffer->last = s->proxy->buffer->start;\n}\n\n\nstatic void\nngx_mail_proxy_imap_handler(ngx_event_t *rev)\n{\n    u_char                 *p;\n    ngx_int_t               rc;\n    ngx_str_t               line;\n    ngx_connection_t       *c;\n    ngx_mail_session_t     *s;\n    ngx_mail_proxy_conf_t  *pcf;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,\n                   \"mail proxy imap auth handler\");\n\n    c = rev->data;\n    s = c->data;\n\n    if (rev->timedout) {\n        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,\n                      \"upstream timed out\");\n        c->timedout = 1;\n        ngx_mail_proxy_internal_server_error(s);\n        return;\n    }\n\n    if (s->proxy->proxy_protocol) {\n        ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, \"mail proxy imap busy\");\n\n        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n            ngx_mail_proxy_internal_server_error(s);\n            return;\n        }\n\n        return;\n    }\n\n    rc = ngx_mail_proxy_read_response(s, s->mail_state);\n\n    if (rc == NGX_AGAIN) {\n        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n            ngx_mail_proxy_internal_server_error(s);\n            return;\n        }\n\n        return;\n    }\n\n    if (rc == NGX_ERROR) {\n        ngx_mail_proxy_upstream_error(s);\n        return;\n    }\n\n    switch (s->mail_state) {\n\n    case ngx_imap_start:\n        ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,\n                       \"mail proxy send login\");\n\n        s->connection->log->action = \"sending LOGIN command to upstream\";\n\n        line.len = s->tag.len + sizeof(\"LOGIN \") - 1\n                   + 1 + NGX_SIZE_T_LEN + 1 + 2;\n        line.data = ngx_pnalloc(c->pool, line.len);\n        if (line.data == NULL) {\n            ngx_mail_proxy_internal_server_error(s);\n            return;\n        }\n\n        line.len = ngx_sprintf(line.data, \"%VLOGIN {%uz}\" CRLF,\n                               &s->tag, s->login.len)\n                   - line.data;\n\n        s->mail_state = ngx_imap_login;\n        break;\n\n    case ngx_imap_login:\n        ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, \"mail proxy send user\");\n\n        s->connection->log->action = \"sending user name to upstream\";\n\n        line.len = s->login.len + 1 + 1 + NGX_SIZE_T_LEN + 1 + 2;\n        line.data = ngx_pnalloc(c->pool, line.len);\n        if (line.data == NULL) {\n            ngx_mail_proxy_internal_server_error(s);\n            return;\n        }\n\n        line.len = ngx_sprintf(line.data, \"%V {%uz}\" CRLF,\n                               &s->login, s->passwd.len)\n                   - line.data;\n\n        s->mail_state = ngx_imap_user;\n        break;\n\n    case ngx_imap_user:\n        ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,\n                       \"mail proxy send passwd\");\n\n        s->connection->log->action = \"sending password to upstream\";\n\n        line.len = s->passwd.len + 2;\n        line.data = ngx_pnalloc(c->pool, line.len);\n        if (line.data == NULL) {\n            ngx_mail_proxy_internal_server_error(s);\n            return;\n        }\n\n        p = ngx_cpymem(line.data, s->passwd.data, s->passwd.len);\n        *p++ = CR; *p = LF;\n\n        s->mail_state = ngx_imap_passwd;\n        break;\n\n    case ngx_imap_passwd:\n        s->connection->read->handler = ngx_mail_proxy_handler;\n        s->connection->write->handler = ngx_mail_proxy_handler;\n        rev->handler = ngx_mail_proxy_handler;\n        c->write->handler = ngx_mail_proxy_handler;\n\n        pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);\n        ngx_add_timer(s->connection->read, pcf->timeout);\n        ngx_del_timer(c->read);\n\n        c->log->action = NULL;\n        ngx_log_error(NGX_LOG_INFO, c->log, 0, \"client logged in\");\n\n        if (s->buffer->pos < s->buffer->last\n            || s->connection->read->ready)\n        {\n            ngx_post_event(c->write, &ngx_posted_events);\n        }\n\n        ngx_mail_proxy_handler(s->connection->write);\n\n        return;\n\n    default:\n#if (NGX_SUPPRESS_WARN)\n        ngx_str_null(&line);\n#endif\n        break;\n    }\n\n    if (c->send(c, line.data, line.len) < (ssize_t) line.len) {\n        /*\n         * we treat the incomplete sending as NGX_ERROR\n         * because it is very strange here\n         */\n        ngx_mail_proxy_internal_server_error(s);\n        return;\n    }\n\n    if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n        ngx_mail_proxy_internal_server_error(s);\n        return;\n    }\n\n    s->proxy->buffer->pos = s->proxy->buffer->start;\n    s->proxy->buffer->last = s->proxy->buffer->start;\n}\n\n\nstatic void\nngx_mail_proxy_smtp_handler(ngx_event_t *rev)\n{\n    u_char                    *p;\n    ngx_int_t                  rc;\n    ngx_str_t                  line, auth, encoded;\n    ngx_buf_t                 *b;\n    ngx_connection_t          *c;\n    ngx_mail_session_t        *s;\n    ngx_mail_proxy_conf_t     *pcf;\n    ngx_mail_core_srv_conf_t  *cscf;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,\n                   \"mail proxy smtp auth handler\");\n\n    c = rev->data;\n    s = c->data;\n\n    if (rev->timedout) {\n        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,\n                      \"upstream timed out\");\n        c->timedout = 1;\n        ngx_mail_proxy_internal_server_error(s);\n        return;\n    }\n\n    if (s->proxy->proxy_protocol) {\n        ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, \"mail proxy smtp busy\");\n\n        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n            ngx_mail_proxy_internal_server_error(s);\n            return;\n        }\n\n        return;\n    }\n\n    rc = ngx_mail_proxy_read_response(s, s->mail_state);\n\n    if (rc == NGX_AGAIN) {\n        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n            ngx_mail_proxy_internal_server_error(s);\n            return;\n        }\n\n        return;\n    }\n\n    if (rc == NGX_ERROR) {\n        ngx_mail_proxy_upstream_error(s);\n        return;\n    }\n\n    switch (s->mail_state) {\n\n    case ngx_smtp_start:\n        ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0, \"mail proxy send ehlo\");\n\n        s->connection->log->action = \"sending HELO/EHLO to upstream\";\n\n        cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);\n\n        line.len = sizeof(\"HELO \")  - 1 + cscf->server_name.len + 2;\n        line.data = ngx_pnalloc(c->pool, line.len);\n        if (line.data == NULL) {\n            ngx_mail_proxy_internal_server_error(s);\n            return;\n        }\n\n        pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);\n\n        p = ngx_cpymem(line.data,\n                       ((s->esmtp || pcf->xclient) ? \"EHLO \" : \"HELO \"),\n                       sizeof(\"HELO \") - 1);\n\n        p = ngx_cpymem(p, cscf->server_name.data, cscf->server_name.len);\n        *p++ = CR; *p = LF;\n\n        if (pcf->xclient) {\n            s->mail_state = ngx_smtp_helo_xclient;\n\n        } else if (s->auth_method == NGX_MAIL_AUTH_NONE) {\n            s->mail_state = ngx_smtp_helo_from;\n\n        } else if (pcf->smtp_auth) {\n            s->mail_state = ngx_smtp_helo_auth;\n\n        } else {\n            s->mail_state = ngx_smtp_helo;\n        }\n\n        break;\n\n    case ngx_smtp_helo_xclient:\n        ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,\n                       \"mail proxy send xclient\");\n\n        s->connection->log->action = \"sending XCLIENT to upstream\";\n\n        line.len = sizeof(\"XCLIENT ADDR= LOGIN= NAME=\"\n                          CRLF) - 1\n                   + s->connection->addr_text.len + s->login.len + s->host.len;\n\n#if (NGX_HAVE_INET6)\n        if (s->connection->sockaddr->sa_family == AF_INET6) {\n            line.len += sizeof(\"IPV6:\") - 1;\n        }\n#endif\n\n        line.data = ngx_pnalloc(c->pool, line.len);\n        if (line.data == NULL) {\n            ngx_mail_proxy_internal_server_error(s);\n            return;\n        }\n\n        p = ngx_cpymem(line.data, \"XCLIENT ADDR=\", sizeof(\"XCLIENT ADDR=\") - 1);\n\n#if (NGX_HAVE_INET6)\n        if (s->connection->sockaddr->sa_family == AF_INET6) {\n            p = ngx_cpymem(p, \"IPV6:\", sizeof(\"IPV6:\") - 1);\n        }\n#endif\n\n        p = ngx_copy(p, s->connection->addr_text.data,\n                     s->connection->addr_text.len);\n\n        pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);\n\n        if (s->login.len && !pcf->smtp_auth) {\n            p = ngx_cpymem(p, \" LOGIN=\", sizeof(\" LOGIN=\") - 1);\n            p = ngx_copy(p, s->login.data, s->login.len);\n        }\n\n        p = ngx_cpymem(p, \" NAME=\", sizeof(\" NAME=\") - 1);\n        p = ngx_copy(p, s->host.data, s->host.len);\n\n        *p++ = CR; *p++ = LF;\n\n        line.len = p - line.data;\n\n        if (s->smtp_helo.len) {\n            s->mail_state = ngx_smtp_xclient_helo;\n\n        } else if (s->auth_method == NGX_MAIL_AUTH_NONE) {\n            s->mail_state = ngx_smtp_xclient_from;\n\n        } else if (pcf->smtp_auth) {\n            s->mail_state = ngx_smtp_xclient_auth;\n\n        } else {\n            s->mail_state = ngx_smtp_xclient;\n        }\n\n        break;\n\n    case ngx_smtp_xclient_helo:\n        ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,\n                       \"mail proxy send client ehlo\");\n\n        s->connection->log->action = \"sending client HELO/EHLO to upstream\";\n\n        line.len = sizeof(\"HELO \" CRLF) - 1 + s->smtp_helo.len;\n\n        line.data = ngx_pnalloc(c->pool, line.len);\n        if (line.data == NULL) {\n            ngx_mail_proxy_internal_server_error(s);\n            return;\n        }\n\n        line.len = ngx_sprintf(line.data,\n                       ((s->esmtp) ? \"EHLO %V\" CRLF : \"HELO %V\" CRLF),\n                       &s->smtp_helo)\n                   - line.data;\n\n        pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);\n\n        if (s->auth_method == NGX_MAIL_AUTH_NONE) {\n            s->mail_state = ngx_smtp_helo_from;\n\n        } else if (pcf->smtp_auth) {\n            s->mail_state = ngx_smtp_helo_auth;\n\n        } else {\n            s->mail_state = ngx_smtp_helo;\n        }\n\n        break;\n\n    case ngx_smtp_helo_auth:\n    case ngx_smtp_xclient_auth:\n        ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,\n                       \"mail proxy send auth\");\n\n        s->connection->log->action = \"sending AUTH to upstream\";\n\n        if (s->passwd.data == NULL) {\n            ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,\n                          \"no password available\");\n            ngx_mail_proxy_internal_server_error(s);\n            return;\n        }\n\n        auth.len = 1 + s->login.len + 1 + s->passwd.len;\n        auth.data = ngx_pnalloc(c->pool, auth.len);\n        if (auth.data == NULL) {\n            ngx_mail_proxy_internal_server_error(s);\n            return;\n        }\n\n        auth.len = ngx_sprintf(auth.data, \"%Z%V%Z%V\", &s->login, &s->passwd)\n                   - auth.data;\n\n        line.len = sizeof(\"AUTH PLAIN \" CRLF) - 1\n                   + ngx_base64_encoded_length(auth.len);\n\n        line.data = ngx_pnalloc(c->pool, line.len);\n        if (line.data == NULL) {\n            ngx_mail_proxy_internal_server_error(s);\n            return;\n        }\n\n        encoded.data = ngx_cpymem(line.data, \"AUTH PLAIN \",\n                                  sizeof(\"AUTH PLAIN \") - 1);\n\n        ngx_encode_base64(&encoded, &auth);\n\n        p = encoded.data + encoded.len;\n        *p++ = CR; *p = LF;\n\n        s->mail_state = ngx_smtp_auth_plain;\n\n        break;\n\n    case ngx_smtp_helo_from:\n    case ngx_smtp_xclient_from:\n        ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,\n                       \"mail proxy send mail from\");\n\n        s->connection->log->action = \"sending MAIL FROM to upstream\";\n\n        line.len = s->smtp_from.len + sizeof(CRLF) - 1;\n        line.data = ngx_pnalloc(c->pool, line.len);\n        if (line.data == NULL) {\n            ngx_mail_proxy_internal_server_error(s);\n            return;\n        }\n\n        p = ngx_cpymem(line.data, s->smtp_from.data, s->smtp_from.len);\n        *p++ = CR; *p = LF;\n\n        s->mail_state = ngx_smtp_from;\n\n        break;\n\n    case ngx_smtp_from:\n        ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,\n                       \"mail proxy send rcpt to\");\n\n        s->connection->log->action = \"sending RCPT TO to upstream\";\n\n        line.len = s->smtp_to.len + sizeof(CRLF) - 1;\n        line.data = ngx_pnalloc(c->pool, line.len);\n        if (line.data == NULL) {\n            ngx_mail_proxy_internal_server_error(s);\n            return;\n        }\n\n        p = ngx_cpymem(line.data, s->smtp_to.data, s->smtp_to.len);\n        *p++ = CR; *p = LF;\n\n        s->mail_state = ngx_smtp_to;\n\n        break;\n\n    case ngx_smtp_helo:\n    case ngx_smtp_xclient:\n    case ngx_smtp_auth_plain:\n    case ngx_smtp_to:\n\n        b = s->proxy->buffer;\n\n        if (s->auth_method == NGX_MAIL_AUTH_NONE) {\n            b->pos = b->start;\n\n        } else {\n            ngx_memcpy(b->start, smtp_auth_ok, sizeof(smtp_auth_ok) - 1);\n            b->last = b->start + sizeof(smtp_auth_ok) - 1;\n        }\n\n        s->connection->read->handler = ngx_mail_proxy_handler;\n        s->connection->write->handler = ngx_mail_proxy_handler;\n        rev->handler = ngx_mail_proxy_handler;\n        c->write->handler = ngx_mail_proxy_handler;\n\n        pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);\n        ngx_add_timer(s->connection->read, pcf->timeout);\n        ngx_del_timer(c->read);\n\n        c->log->action = NULL;\n        ngx_log_error(NGX_LOG_INFO, c->log, 0, \"client logged in\");\n\n        if (s->buffer->pos < s->buffer->last\n            || s->connection->read->ready)\n        {\n            ngx_post_event(c->write, &ngx_posted_events);\n        }\n\n        ngx_mail_proxy_handler(s->connection->write);\n\n        return;\n\n    default:\n#if (NGX_SUPPRESS_WARN)\n        ngx_str_null(&line);\n#endif\n        break;\n    }\n\n    if (c->send(c, line.data, line.len) < (ssize_t) line.len) {\n        /*\n         * we treat the incomplete sending as NGX_ERROR\n         * because it is very strange here\n         */\n        ngx_mail_proxy_internal_server_error(s);\n        return;\n    }\n\n    if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n        ngx_mail_proxy_internal_server_error(s);\n        return;\n    }\n\n    s->proxy->buffer->pos = s->proxy->buffer->start;\n    s->proxy->buffer->last = s->proxy->buffer->start;\n}\n\n\nstatic void\nngx_mail_proxy_write_handler(ngx_event_t *wev)\n{\n    ngx_connection_t    *c;\n    ngx_mail_session_t  *s;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_MAIL, wev->log, 0, \"mail proxy write handler\");\n\n    c = wev->data;\n    s = c->data;\n\n    if (s->proxy->proxy_protocol) {\n        if (ngx_mail_proxy_send_proxy_protocol(s) != NGX_OK) {\n            return;\n        }\n\n        s->proxy->proxy_protocol = 0;\n    }\n\n    if (ngx_handle_write_event(wev, 0) != NGX_OK) {\n        ngx_mail_proxy_internal_server_error(s);\n    }\n\n    if (c->read->ready) {\n        ngx_post_event(c->read, &ngx_posted_events);\n    }\n}\n\n\nstatic ngx_int_t\nngx_mail_proxy_send_proxy_protocol(ngx_mail_session_t *s)\n{\n    u_char            *p;\n    ssize_t            n, size;\n    ngx_connection_t  *c;\n    u_char             buf[NGX_PROXY_PROTOCOL_V1_MAX_HEADER];\n\n    s->connection->log->action = \"sending PROXY protocol header to upstream\";\n\n    ngx_log_debug0(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,\n                   \"mail proxy send PROXY protocol header\");\n\n    p = ngx_proxy_protocol_write(s->connection, buf,\n                                 buf + NGX_PROXY_PROTOCOL_V1_MAX_HEADER);\n    if (p == NULL) {\n        ngx_mail_proxy_internal_server_error(s);\n        return NGX_ERROR;\n    }\n\n    c = s->proxy->upstream.connection;\n\n    size = p - buf;\n\n    n = c->send(c, buf, size);\n\n    if (n == NGX_AGAIN) {\n        if (ngx_handle_write_event(c->write, 0) != NGX_OK) {\n            ngx_mail_proxy_internal_server_error(s);\n            return NGX_ERROR;\n        }\n\n        return NGX_AGAIN;\n    }\n\n    if (n == NGX_ERROR) {\n        ngx_mail_proxy_internal_server_error(s);\n        return NGX_ERROR;\n    }\n\n    if (n != size) {\n\n        /*\n         * PROXY protocol specification:\n         * The sender must always ensure that the header\n         * is sent at once, so that the transport layer\n         * maintains atomicity along the path to the receiver.\n         */\n\n        ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,\n                      \"could not send PROXY protocol header at once\");\n\n        ngx_mail_proxy_internal_server_error(s);\n\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_mail_proxy_read_response(ngx_mail_session_t *s, ngx_uint_t state)\n{\n    u_char                 *p, *m;\n    ssize_t                 n;\n    ngx_buf_t              *b;\n    ngx_mail_proxy_conf_t  *pcf;\n\n    s->connection->log->action = \"reading response from upstream\";\n\n    b = s->proxy->buffer;\n\n    n = s->proxy->upstream.connection->recv(s->proxy->upstream.connection,\n                                            b->last, b->end - b->last);\n\n    if (n == NGX_ERROR || n == 0) {\n        return NGX_ERROR;\n    }\n\n    if (n == NGX_AGAIN) {\n        return NGX_AGAIN;\n    }\n\n    b->last += n;\n\n    if (b->last - b->pos < 4) {\n        return NGX_AGAIN;\n    }\n\n    if (*(b->last - 2) != CR || *(b->last - 1) != LF) {\n        if (b->last == b->end) {\n            *(b->last - 1) = '\\0';\n            ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,\n                          \"upstream sent too long response line: \\\"%s\\\"\",\n                          b->pos);\n            return NGX_ERROR;\n        }\n\n        return NGX_AGAIN;\n    }\n\n    p = b->pos;\n\n    switch (s->protocol) {\n\n    case NGX_MAIL_POP3_PROTOCOL:\n        if (p[0] == '+' && p[1] == 'O' && p[2] == 'K') {\n            return NGX_OK;\n        }\n        break;\n\n    case NGX_MAIL_IMAP_PROTOCOL:\n        switch (state) {\n\n        case ngx_imap_start:\n            if (p[0] == '*' && p[1] == ' ' && p[2] == 'O' && p[3] == 'K') {\n                return NGX_OK;\n            }\n            break;\n\n        case ngx_imap_login:\n        case ngx_imap_user:\n            if (p[0] == '+') {\n                return NGX_OK;\n            }\n            break;\n\n        case ngx_imap_passwd:\n            if (ngx_strncmp(p, s->tag.data, s->tag.len) == 0) {\n                p += s->tag.len;\n                if (p[0] == 'O' && p[1] == 'K') {\n                    return NGX_OK;\n                }\n            }\n            break;\n        }\n\n        break;\n\n    default: /* NGX_MAIL_SMTP_PROTOCOL */\n\n        if (p[3] == '-') {\n            /* multiline reply, check if we got last line */\n\n            m = b->last - (sizeof(CRLF \"200\" CRLF) - 1);\n\n            while (m > p) {\n                if (m[0] == CR && m[1] == LF) {\n                    break;\n                }\n\n                m--;\n            }\n\n            if (m <= p || m[5] == '-') {\n                return NGX_AGAIN;\n            }\n        }\n\n        switch (state) {\n\n        case ngx_smtp_start:\n            if (p[0] == '2' && p[1] == '2' && p[2] == '0') {\n                return NGX_OK;\n            }\n            break;\n\n        case ngx_smtp_helo:\n        case ngx_smtp_helo_xclient:\n        case ngx_smtp_helo_from:\n        case ngx_smtp_helo_auth:\n        case ngx_smtp_from:\n            if (p[0] == '2' && p[1] == '5' && p[2] == '0') {\n                return NGX_OK;\n            }\n            break;\n\n        case ngx_smtp_xclient:\n        case ngx_smtp_xclient_from:\n        case ngx_smtp_xclient_helo:\n        case ngx_smtp_xclient_auth:\n            if (p[0] == '2' && (p[1] == '2' || p[1] == '5') && p[2] == '0') {\n                return NGX_OK;\n            }\n            break;\n\n        case ngx_smtp_auth_plain:\n            if (p[0] == '2' && p[1] == '3' && p[2] == '5') {\n                return NGX_OK;\n            }\n            break;\n\n        case ngx_smtp_to:\n            return NGX_OK;\n        }\n\n        break;\n    }\n\n    pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);\n\n    if (pcf->pass_error_message == 0) {\n        *(b->last - 2) = '\\0';\n        ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,\n                      \"upstream sent invalid response: \\\"%s\\\"\", p);\n        return NGX_ERROR;\n    }\n\n    s->out.len = b->last - p - 2;\n    s->out.data = p;\n\n    ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,\n                  \"upstream sent invalid response: \\\"%V\\\"\", &s->out);\n\n    s->out.len = b->last - b->pos;\n    s->out.data = b->pos;\n\n    return NGX_ERROR;\n}\n\n\nstatic void\nngx_mail_proxy_handler(ngx_event_t *ev)\n{\n    char                   *action, *recv_action, *send_action;\n    size_t                  size;\n    ssize_t                 n;\n    ngx_buf_t              *b;\n    ngx_uint_t              do_write;\n    ngx_connection_t       *c, *src, *dst;\n    ngx_mail_session_t     *s;\n    ngx_mail_proxy_conf_t  *pcf;\n\n    c = ev->data;\n    s = c->data;\n\n    if (ev->timedout || c->close) {\n        c->log->action = \"proxying\";\n\n        if (c->close) {\n            ngx_log_error(NGX_LOG_INFO, c->log, 0, \"shutdown timeout\");\n\n        } else if (c == s->connection) {\n            ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,\n                          \"client timed out\");\n            c->timedout = 1;\n\n        } else {\n            ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,\n                          \"upstream timed out\");\n        }\n\n        ngx_mail_proxy_close_session(s);\n        return;\n    }\n\n    if (c == s->connection) {\n        if (ev->write) {\n            recv_action = \"proxying and reading from upstream\";\n            send_action = \"proxying and sending to client\";\n            src = s->proxy->upstream.connection;\n            dst = c;\n            b = s->proxy->buffer;\n\n        } else {\n            recv_action = \"proxying and reading from client\";\n            send_action = \"proxying and sending to upstream\";\n            src = c;\n            dst = s->proxy->upstream.connection;\n            b = s->buffer;\n        }\n\n    } else {\n        if (ev->write) {\n            recv_action = \"proxying and reading from client\";\n            send_action = \"proxying and sending to upstream\";\n            src = s->connection;\n            dst = c;\n            b = s->buffer;\n\n        } else {\n            recv_action = \"proxying and reading from upstream\";\n            send_action = \"proxying and sending to client\";\n            src = c;\n            dst = s->connection;\n            b = s->proxy->buffer;\n        }\n    }\n\n    do_write = ev->write ? 1 : 0;\n\n    ngx_log_debug3(NGX_LOG_DEBUG_MAIL, ev->log, 0,\n                   \"mail proxy handler: %ui, #%d > #%d\",\n                   do_write, src->fd, dst->fd);\n\n    for ( ;; ) {\n\n        if (do_write) {\n\n            size = b->last - b->pos;\n\n            if (size && dst->write->ready) {\n                c->log->action = send_action;\n\n                n = dst->send(dst, b->pos, size);\n\n                if (n == NGX_ERROR) {\n                    ngx_mail_proxy_close_session(s);\n                    return;\n                }\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                }\n            }\n        }\n\n        size = b->end - b->last;\n\n        if (size && src->read->ready) {\n            c->log->action = recv_action;\n\n            n = src->recv(src, b->last, size);\n\n            if (n == NGX_AGAIN || n == 0) {\n                break;\n            }\n\n            if (n > 0) {\n                do_write = 1;\n                b->last += n;\n\n                continue;\n            }\n\n            if (n == NGX_ERROR) {\n                src->read->eof = 1;\n            }\n        }\n\n        break;\n    }\n\n    c->log->action = \"proxying\";\n\n    if ((s->connection->read->eof && s->buffer->pos == s->buffer->last)\n        || (s->proxy->upstream.connection->read->eof\n            && s->proxy->buffer->pos == s->proxy->buffer->last)\n        || (s->connection->read->eof\n            && s->proxy->upstream.connection->read->eof))\n    {\n        action = c->log->action;\n        c->log->action = NULL;\n        ngx_log_error(NGX_LOG_INFO, c->log, 0, \"proxied session done\");\n        c->log->action = action;\n\n        ngx_mail_proxy_close_session(s);\n        return;\n    }\n\n    if (ngx_handle_write_event(dst->write, 0) != NGX_OK) {\n        ngx_mail_proxy_close_session(s);\n        return;\n    }\n\n    if (ngx_handle_read_event(dst->read, 0) != NGX_OK) {\n        ngx_mail_proxy_close_session(s);\n        return;\n    }\n\n    if (ngx_handle_write_event(src->write, 0) != NGX_OK) {\n        ngx_mail_proxy_close_session(s);\n        return;\n    }\n\n    if (ngx_handle_read_event(src->read, 0) != NGX_OK) {\n        ngx_mail_proxy_close_session(s);\n        return;\n    }\n\n    if (c == s->connection) {\n        pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);\n        ngx_add_timer(c->read, pcf->timeout);\n    }\n}\n\n\nstatic void\nngx_mail_proxy_upstream_error(ngx_mail_session_t *s)\n{\n    if (s->proxy->upstream.connection) {\n        ngx_log_debug1(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,\n                       \"close mail proxy connection: %d\",\n                       s->proxy->upstream.connection->fd);\n\n        ngx_close_connection(s->proxy->upstream.connection);\n    }\n\n    if (s->out.len == 0) {\n        ngx_mail_session_internal_server_error(s);\n        return;\n    }\n\n    s->quit = 1;\n    ngx_mail_send(s->connection->write);\n}\n\n\nstatic void\nngx_mail_proxy_internal_server_error(ngx_mail_session_t *s)\n{\n    if (s->proxy->upstream.connection) {\n        ngx_log_debug1(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,\n                       \"close mail proxy connection: %d\",\n                       s->proxy->upstream.connection->fd);\n\n        ngx_close_connection(s->proxy->upstream.connection);\n    }\n\n    ngx_mail_session_internal_server_error(s);\n}\n\n\nstatic void\nngx_mail_proxy_close_session(ngx_mail_session_t *s)\n{\n    if (s->proxy->upstream.connection) {\n        ngx_log_debug1(NGX_LOG_DEBUG_MAIL, s->connection->log, 0,\n                       \"close mail proxy connection: %d\",\n                       s->proxy->upstream.connection->fd);\n\n        ngx_close_connection(s->proxy->upstream.connection);\n    }\n\n    ngx_mail_close_connection(s->connection);\n}\n\n\nstatic void *\nngx_mail_proxy_create_conf(ngx_conf_t *cf)\n{\n    ngx_mail_proxy_conf_t  *pcf;\n\n    pcf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_proxy_conf_t));\n    if (pcf == NULL) {\n        return NULL;\n    }\n\n    pcf->enable = NGX_CONF_UNSET;\n    pcf->pass_error_message = NGX_CONF_UNSET;\n    pcf->xclient = NGX_CONF_UNSET;\n    pcf->smtp_auth = NGX_CONF_UNSET;\n    pcf->proxy_protocol = NGX_CONF_UNSET;\n    pcf->buffer_size = NGX_CONF_UNSET_SIZE;\n    pcf->timeout = NGX_CONF_UNSET_MSEC;\n\n    return pcf;\n}\n\n\nstatic char *\nngx_mail_proxy_merge_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_mail_proxy_conf_t *prev = parent;\n    ngx_mail_proxy_conf_t *conf = child;\n\n    ngx_conf_merge_value(conf->enable, prev->enable, 0);\n    ngx_conf_merge_value(conf->pass_error_message, prev->pass_error_message, 0);\n    ngx_conf_merge_value(conf->xclient, prev->xclient, 1);\n    ngx_conf_merge_value(conf->smtp_auth, prev->smtp_auth, 0);\n    ngx_conf_merge_value(conf->proxy_protocol, prev->proxy_protocol, 0);\n    ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size,\n                              (size_t) ngx_pagesize);\n    ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 24 * 60 * 60000);\n\n    return NGX_CONF_OK;\n}\n"
  },
  {
    "path": "src/mail/ngx_mail_realip_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_mail.h>\n\n\ntypedef struct {\n    ngx_array_t       *from;     /* array of ngx_cidr_t */\n} ngx_mail_realip_srv_conf_t;\n\n\nstatic ngx_int_t ngx_mail_realip_set_addr(ngx_mail_session_t *s,\n    ngx_addr_t *addr);\nstatic char *ngx_mail_realip_from(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic void *ngx_mail_realip_create_srv_conf(ngx_conf_t *cf);\nstatic char *ngx_mail_realip_merge_srv_conf(ngx_conf_t *cf, void *parent,\n    void *child);\n\n\nstatic ngx_command_t  ngx_mail_realip_commands[] = {\n\n    { ngx_string(\"set_real_ip_from\"),\n      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_mail_realip_from,\n      NGX_MAIL_SRV_CONF_OFFSET,\n      0,\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_mail_module_t  ngx_mail_realip_module_ctx = {\n    NULL,                                  /* protocol */\n\n    NULL,                                  /* create main configuration */\n    NULL,                                  /* init main configuration */\n\n    ngx_mail_realip_create_srv_conf,       /* create server configuration */\n    ngx_mail_realip_merge_srv_conf         /* merge server configuration */\n};\n\n\nngx_module_t  ngx_mail_realip_module = {\n    NGX_MODULE_V1,\n    &ngx_mail_realip_module_ctx,           /* module context */\n    ngx_mail_realip_commands,              /* module directives */\n    NGX_MAIL_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\nngx_int_t\nngx_mail_realip_handler(ngx_mail_session_t *s)\n{\n    ngx_addr_t                   addr;\n    ngx_connection_t            *c;\n    ngx_mail_realip_srv_conf_t  *rscf;\n\n    rscf = ngx_mail_get_module_srv_conf(s, ngx_mail_realip_module);\n\n    if (rscf->from == NULL) {\n        return NGX_OK;\n    }\n\n    c = s->connection;\n\n    if (c->proxy_protocol == NULL) {\n        return NGX_OK;\n    }\n\n    if (ngx_cidr_match(c->sockaddr, rscf->from) != NGX_OK) {\n        return NGX_OK;\n    }\n\n    if (ngx_parse_addr(c->pool, &addr, c->proxy_protocol->src_addr.data,\n                       c->proxy_protocol->src_addr.len)\n        != NGX_OK)\n    {\n        return NGX_OK;\n    }\n\n    ngx_inet_set_port(addr.sockaddr, c->proxy_protocol->src_port);\n\n    return ngx_mail_realip_set_addr(s, &addr);\n}\n\n\nstatic ngx_int_t\nngx_mail_realip_set_addr(ngx_mail_session_t *s, ngx_addr_t *addr)\n{\n    size_t             len;\n    u_char            *p;\n    u_char             text[NGX_SOCKADDR_STRLEN];\n    ngx_connection_t  *c;\n\n    c = s->connection;\n\n    len = ngx_sock_ntop(addr->sockaddr, addr->socklen, text,\n                        NGX_SOCKADDR_STRLEN, 0);\n    if (len == 0) {\n        return NGX_ERROR;\n    }\n\n    p = ngx_pnalloc(c->pool, len);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_memcpy(p, text, len);\n\n    c->sockaddr = addr->sockaddr;\n    c->socklen = addr->socklen;\n    c->addr_text.len = len;\n    c->addr_text.data = p;\n\n    return NGX_OK;\n}\n\n\nstatic char *\nngx_mail_realip_from(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_mail_realip_srv_conf_t *rscf = conf;\n\n    ngx_int_t             rc;\n    ngx_str_t            *value;\n    ngx_url_t             u;\n    ngx_cidr_t            c, *cidr;\n    ngx_uint_t            i;\n    struct sockaddr_in   *sin;\n#if (NGX_HAVE_INET6)\n    struct sockaddr_in6  *sin6;\n#endif\n\n    value = cf->args->elts;\n\n    if (rscf->from == NULL) {\n        rscf->from = ngx_array_create(cf->pool, 2,\n                                      sizeof(ngx_cidr_t));\n        if (rscf->from == NULL) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n#if (NGX_HAVE_UNIX_DOMAIN)\n\n    if (ngx_strcmp(value[1].data, \"unix:\") == 0) {\n        cidr = ngx_array_push(rscf->from);\n        if (cidr == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        cidr->family = AF_UNIX;\n        return NGX_CONF_OK;\n    }\n\n#endif\n\n    rc = ngx_ptocidr(&value[1], &c);\n\n    if (rc != NGX_ERROR) {\n        if (rc == NGX_DONE) {\n            ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                               \"low address bits of %V are meaningless\",\n                               &value[1]);\n        }\n\n        cidr = ngx_array_push(rscf->from);\n        if (cidr == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        *cidr = c;\n\n        return NGX_CONF_OK;\n    }\n\n    ngx_memzero(&u, sizeof(ngx_url_t));\n    u.host = value[1];\n\n    if (ngx_inet_resolve_host(cf->pool, &u) != NGX_OK) {\n        if (u.err) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"%s in set_real_ip_from \\\"%V\\\"\",\n                               u.err, &u.host);\n        }\n\n        return NGX_CONF_ERROR;\n    }\n\n    cidr = ngx_array_push_n(rscf->from, u.naddrs);\n    if (cidr == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_memzero(cidr, u.naddrs * sizeof(ngx_cidr_t));\n\n    for (i = 0; i < u.naddrs; i++) {\n        cidr[i].family = u.addrs[i].sockaddr->sa_family;\n\n        switch (cidr[i].family) {\n\n#if (NGX_HAVE_INET6)\n        case AF_INET6:\n            sin6 = (struct sockaddr_in6 *) u.addrs[i].sockaddr;\n            cidr[i].u.in6.addr = sin6->sin6_addr;\n            ngx_memset(cidr[i].u.in6.mask.s6_addr, 0xff, 16);\n            break;\n#endif\n\n        default: /* AF_INET */\n            sin = (struct sockaddr_in *) u.addrs[i].sockaddr;\n            cidr[i].u.in.addr = sin->sin_addr.s_addr;\n            cidr[i].u.in.mask = 0xffffffff;\n            break;\n        }\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic void *\nngx_mail_realip_create_srv_conf(ngx_conf_t *cf)\n{\n    ngx_mail_realip_srv_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_realip_srv_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc():\n     *\n     *     conf->from = NULL;\n     */\n\n    return conf;\n}\n\n\nstatic char *\nngx_mail_realip_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_mail_realip_srv_conf_t *prev = parent;\n    ngx_mail_realip_srv_conf_t *conf = child;\n\n    if (conf->from == NULL) {\n        conf->from = prev->from;\n    }\n\n    return NGX_CONF_OK;\n}\n"
  },
  {
    "path": "src/mail/ngx_mail_smtp_handler.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n#include <ngx_mail.h>\n#include <ngx_mail_smtp_module.h>\n\n\nstatic void ngx_mail_smtp_resolve_addr_handler(ngx_resolver_ctx_t *ctx);\nstatic void ngx_mail_smtp_resolve_name(ngx_event_t *rev);\nstatic void ngx_mail_smtp_resolve_name_handler(ngx_resolver_ctx_t *ctx);\nstatic void ngx_mail_smtp_block_reading(ngx_event_t *rev);\nstatic void ngx_mail_smtp_greeting(ngx_mail_session_t *s, ngx_connection_t *c);\nstatic void ngx_mail_smtp_invalid_pipelining(ngx_event_t *rev);\nstatic ngx_int_t ngx_mail_smtp_create_buffer(ngx_mail_session_t *s,\n    ngx_connection_t *c);\n\nstatic ngx_int_t ngx_mail_smtp_helo(ngx_mail_session_t *s, ngx_connection_t *c);\nstatic ngx_int_t ngx_mail_smtp_auth(ngx_mail_session_t *s, ngx_connection_t *c);\nstatic ngx_int_t ngx_mail_smtp_mail(ngx_mail_session_t *s, ngx_connection_t *c);\nstatic ngx_int_t ngx_mail_smtp_starttls(ngx_mail_session_t *s,\n    ngx_connection_t *c);\nstatic ngx_int_t ngx_mail_smtp_rset(ngx_mail_session_t *s, ngx_connection_t *c);\nstatic ngx_int_t ngx_mail_smtp_rcpt(ngx_mail_session_t *s, ngx_connection_t *c);\n\nstatic ngx_int_t ngx_mail_smtp_discard_command(ngx_mail_session_t *s,\n    ngx_connection_t *c, char *err);\nstatic void ngx_mail_smtp_log_rejected_command(ngx_mail_session_t *s,\n    ngx_connection_t *c, char *err);\n\n\nstatic u_char  smtp_ok[] = \"250 2.0.0 OK\" CRLF;\nstatic u_char  smtp_bye[] = \"221 2.0.0 Bye\" CRLF;\nstatic u_char  smtp_starttls[] = \"220 2.0.0 Start TLS\" CRLF;\nstatic u_char  smtp_next[] = \"334 \" CRLF;\nstatic u_char  smtp_username[] = \"334 VXNlcm5hbWU6\" CRLF;\nstatic u_char  smtp_password[] = \"334 UGFzc3dvcmQ6\" CRLF;\nstatic u_char  smtp_invalid_command[] = \"500 5.5.1 Invalid command\" CRLF;\nstatic u_char  smtp_invalid_pipelining[] =\n    \"503 5.5.0 Improper use of SMTP command pipelining\" CRLF;\nstatic u_char  smtp_invalid_argument[] = \"501 5.5.4 Invalid argument\" CRLF;\nstatic u_char  smtp_auth_required[] = \"530 5.7.1 Authentication required\" CRLF;\nstatic u_char  smtp_bad_sequence[] = \"503 5.5.1 Bad sequence of commands\" CRLF;\n\n\nstatic ngx_str_t  smtp_unavailable = ngx_string(\"[UNAVAILABLE]\");\nstatic ngx_str_t  smtp_tempunavail = ngx_string(\"[TEMPUNAVAIL]\");\n\n\nvoid\nngx_mail_smtp_init_session(ngx_mail_session_t *s, ngx_connection_t *c)\n{\n    ngx_resolver_ctx_t        *ctx;\n    ngx_mail_core_srv_conf_t  *cscf;\n\n    cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);\n\n    if (cscf->resolver == NULL) {\n        s->host = smtp_unavailable;\n        ngx_mail_smtp_greeting(s, c);\n        return;\n    }\n\n#if (NGX_HAVE_UNIX_DOMAIN)\n    if (c->sockaddr->sa_family == AF_UNIX) {\n        s->host = smtp_tempunavail;\n        ngx_mail_smtp_greeting(s, c);\n        return;\n    }\n#endif\n\n    c->log->action = \"in resolving client address\";\n\n    ctx = ngx_resolve_start(cscf->resolver, NULL);\n    if (ctx == NULL) {\n        ngx_mail_close_connection(c);\n        return;\n    }\n\n    ctx->addr.sockaddr = c->sockaddr;\n    ctx->addr.socklen = c->socklen;\n    ctx->handler = ngx_mail_smtp_resolve_addr_handler;\n    ctx->data = s;\n    ctx->timeout = cscf->resolver_timeout;\n\n    s->resolver_ctx = ctx;\n    c->read->handler = ngx_mail_smtp_block_reading;\n\n    if (ngx_resolve_addr(ctx) != NGX_OK) {\n        ngx_mail_close_connection(c);\n    }\n}\n\n\nstatic void\nngx_mail_smtp_resolve_addr_handler(ngx_resolver_ctx_t *ctx)\n{\n    ngx_connection_t    *c;\n    ngx_mail_session_t  *s;\n\n    s = ctx->data;\n    c = s->connection;\n\n    if (ctx->state) {\n        ngx_log_error(NGX_LOG_ERR, c->log, 0,\n                      \"%V could not be resolved (%i: %s)\",\n                      &c->addr_text, ctx->state,\n                      ngx_resolver_strerror(ctx->state));\n\n        if (ctx->state == NGX_RESOLVE_NXDOMAIN) {\n            s->host = smtp_unavailable;\n\n        } else {\n            s->host = smtp_tempunavail;\n        }\n\n        ngx_resolve_addr_done(ctx);\n\n        ngx_mail_smtp_greeting(s, s->connection);\n\n        return;\n    }\n\n    c->log->action = \"in resolving client hostname\";\n\n    s->host.data = ngx_pstrdup(c->pool, &ctx->name);\n    if (s->host.data == NULL) {\n        ngx_resolve_addr_done(ctx);\n        ngx_mail_close_connection(c);\n        return;\n    }\n\n    s->host.len = ctx->name.len;\n\n    ngx_resolve_addr_done(ctx);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,\n                   \"address resolved: %V\", &s->host);\n\n    c->read->handler = ngx_mail_smtp_resolve_name;\n\n    ngx_post_event(c->read, &ngx_posted_events);\n}\n\n\nstatic void\nngx_mail_smtp_resolve_name(ngx_event_t *rev)\n{\n    ngx_connection_t          *c;\n    ngx_mail_session_t        *s;\n    ngx_resolver_ctx_t        *ctx;\n    ngx_mail_core_srv_conf_t  *cscf;\n\n    c = rev->data;\n    s = c->data;\n\n    cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);\n\n    ctx = ngx_resolve_start(cscf->resolver, NULL);\n    if (ctx == NULL) {\n        ngx_mail_close_connection(c);\n        return;\n    }\n\n    ctx->name = s->host;\n    ctx->handler = ngx_mail_smtp_resolve_name_handler;\n    ctx->data = s;\n    ctx->timeout = cscf->resolver_timeout;\n\n    s->resolver_ctx = ctx;\n    c->read->handler = ngx_mail_smtp_block_reading;\n\n    if (ngx_resolve_name(ctx) != NGX_OK) {\n        ngx_mail_close_connection(c);\n    }\n}\n\n\nstatic void\nngx_mail_smtp_resolve_name_handler(ngx_resolver_ctx_t *ctx)\n{\n    ngx_uint_t           i;\n    ngx_connection_t    *c;\n    ngx_mail_session_t  *s;\n\n    s = ctx->data;\n    c = s->connection;\n\n    if (ctx->state) {\n        ngx_log_error(NGX_LOG_ERR, c->log, 0,\n                      \"\\\"%V\\\" could not be resolved (%i: %s)\",\n                      &ctx->name, ctx->state,\n                      ngx_resolver_strerror(ctx->state));\n\n        if (ctx->state == NGX_RESOLVE_NXDOMAIN) {\n            s->host = smtp_unavailable;\n\n        } else {\n            s->host = smtp_tempunavail;\n        }\n\n    } else {\n\n#if (NGX_DEBUG)\n        {\n        u_char     text[NGX_SOCKADDR_STRLEN];\n        ngx_str_t  addr;\n\n        addr.data = text;\n\n        for (i = 0; i < ctx->naddrs; i++) {\n            addr.len = ngx_sock_ntop(ctx->addrs[i].sockaddr,\n                                     ctx->addrs[i].socklen,\n                                     text, NGX_SOCKADDR_STRLEN, 0);\n\n            ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,\n                           \"name was resolved to %V\", &addr);\n        }\n        }\n#endif\n\n        for (i = 0; i < ctx->naddrs; i++) {\n            if (ngx_cmp_sockaddr(ctx->addrs[i].sockaddr, ctx->addrs[i].socklen,\n                                 c->sockaddr, c->socklen, 0)\n                == NGX_OK)\n            {\n                goto found;\n            }\n        }\n\n        s->host = smtp_unavailable;\n    }\n\nfound:\n\n    ngx_resolve_name_done(ctx);\n\n    ngx_mail_smtp_greeting(s, c);\n}\n\n\nstatic void\nngx_mail_smtp_block_reading(ngx_event_t *rev)\n{\n    ngx_connection_t    *c;\n    ngx_mail_session_t  *s;\n    ngx_resolver_ctx_t  *ctx;\n\n    c = rev->data;\n    s = c->data;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, \"smtp reading blocked\");\n\n    if (ngx_handle_read_event(rev, 0) != NGX_OK) {\n\n        if (s->resolver_ctx) {\n            ctx = s->resolver_ctx;\n\n            if (ctx->handler == ngx_mail_smtp_resolve_addr_handler) {\n                ngx_resolve_addr_done(ctx);\n\n            } else if (ctx->handler == ngx_mail_smtp_resolve_name_handler) {\n                ngx_resolve_name_done(ctx);\n            }\n\n            s->resolver_ctx = NULL;\n        }\n\n        ngx_mail_close_connection(c);\n    }\n}\n\n\nstatic void\nngx_mail_smtp_greeting(ngx_mail_session_t *s, ngx_connection_t *c)\n{\n    ngx_msec_t                 timeout;\n    ngx_mail_core_srv_conf_t  *cscf;\n    ngx_mail_smtp_srv_conf_t  *sscf;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,\n                   \"smtp greeting for \\\"%V\\\"\", &s->host);\n\n    cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);\n    sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module);\n\n    timeout = sscf->greeting_delay ? sscf->greeting_delay : cscf->timeout;\n    ngx_add_timer(c->read, timeout);\n\n    if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n        ngx_mail_close_connection(c);\n    }\n\n    if (c->read->ready) {\n        ngx_post_event(c->read, &ngx_posted_events);\n    }\n\n    if (sscf->greeting_delay) {\n         c->read->handler = ngx_mail_smtp_invalid_pipelining;\n         return;\n    }\n\n    c->read->handler = ngx_mail_smtp_init_protocol;\n\n    s->out = sscf->greeting;\n\n    ngx_mail_send(c->write);\n}\n\n\nstatic void\nngx_mail_smtp_invalid_pipelining(ngx_event_t *rev)\n{\n    ngx_connection_t          *c;\n    ngx_mail_session_t        *s;\n    ngx_mail_core_srv_conf_t  *cscf;\n    ngx_mail_smtp_srv_conf_t  *sscf;\n\n    c = rev->data;\n    s = c->data;\n\n    c->log->action = \"in delay pipelining state\";\n\n    if (rev->timedout) {\n\n        ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, \"delay greeting\");\n\n        rev->timedout = 0;\n\n        cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);\n\n        c->read->handler = ngx_mail_smtp_init_protocol;\n\n        ngx_add_timer(c->read, cscf->timeout);\n\n        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n            ngx_mail_close_connection(c);\n            return;\n        }\n\n        sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module);\n\n        s->out = sscf->greeting;\n\n    } else {\n\n        ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, \"invalid pipelining\");\n\n        if (s->buffer == NULL) {\n            if (ngx_mail_smtp_create_buffer(s, c) != NGX_OK) {\n                return;\n            }\n        }\n\n        if (ngx_mail_smtp_discard_command(s, c,\n                                \"client was rejected before greeting: \\\"%V\\\"\")\n            != NGX_OK)\n        {\n            return;\n        }\n\n        ngx_str_set(&s->out, smtp_invalid_pipelining);\n        s->quit = 1;\n    }\n\n    ngx_mail_send(c->write);\n}\n\n\nvoid\nngx_mail_smtp_init_protocol(ngx_event_t *rev)\n{\n    ngx_connection_t    *c;\n    ngx_mail_session_t  *s;\n\n    c = rev->data;\n\n    c->log->action = \"in auth state\";\n\n    if (rev->timedout) {\n        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, \"client timed out\");\n        c->timedout = 1;\n        ngx_mail_close_connection(c);\n        return;\n    }\n\n    s = c->data;\n\n    if (s->buffer == NULL) {\n        if (ngx_mail_smtp_create_buffer(s, c) != NGX_OK) {\n            return;\n        }\n    }\n\n    s->mail_state = ngx_smtp_start;\n    c->read->handler = ngx_mail_smtp_auth_state;\n\n    ngx_mail_smtp_auth_state(rev);\n}\n\n\nstatic ngx_int_t\nngx_mail_smtp_create_buffer(ngx_mail_session_t *s, ngx_connection_t *c)\n{\n    ngx_mail_smtp_srv_conf_t  *sscf;\n\n    if (ngx_array_init(&s->args, c->pool, 2, sizeof(ngx_str_t)) == NGX_ERROR) {\n        ngx_mail_session_internal_server_error(s);\n        return NGX_ERROR;\n    }\n\n    sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module);\n\n    s->buffer = ngx_create_temp_buf(c->pool, sscf->client_buffer_size);\n    if (s->buffer == NULL) {\n        ngx_mail_session_internal_server_error(s);\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nvoid\nngx_mail_smtp_auth_state(ngx_event_t *rev)\n{\n    ngx_int_t            rc;\n    ngx_connection_t    *c;\n    ngx_mail_session_t  *s;\n\n    c = rev->data;\n    s = c->data;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, \"smtp auth state\");\n\n    if (rev->timedout) {\n        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, \"client timed out\");\n        c->timedout = 1;\n        ngx_mail_close_connection(c);\n        return;\n    }\n\n    if (s->out.len) {\n        ngx_log_debug0(NGX_LOG_DEBUG_MAIL, c->log, 0, \"smtp send handler busy\");\n        s->blocked = 1;\n\n        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n            ngx_mail_close_connection(c);\n            return;\n        }\n\n        return;\n    }\n\n    s->blocked = 0;\n\n    rc = ngx_mail_read_command(s, c);\n\n    if (rc == NGX_AGAIN) {\n        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n            ngx_mail_session_internal_server_error(s);\n            return;\n        }\n\n        return;\n    }\n\n    if (rc == NGX_ERROR) {\n        return;\n    }\n\n    ngx_str_set(&s->out, smtp_ok);\n\n    if (rc == NGX_OK) {\n        switch (s->mail_state) {\n\n        case ngx_smtp_start:\n\n            switch (s->command) {\n\n            case NGX_SMTP_HELO:\n            case NGX_SMTP_EHLO:\n                rc = ngx_mail_smtp_helo(s, c);\n                break;\n\n            case NGX_SMTP_AUTH:\n                rc = ngx_mail_smtp_auth(s, c);\n                break;\n\n            case NGX_SMTP_QUIT:\n                s->quit = 1;\n                ngx_str_set(&s->out, smtp_bye);\n                break;\n\n            case NGX_SMTP_MAIL:\n                rc = ngx_mail_smtp_mail(s, c);\n                break;\n\n            case NGX_SMTP_RCPT:\n                rc = ngx_mail_smtp_rcpt(s, c);\n                break;\n\n            case NGX_SMTP_RSET:\n                rc = ngx_mail_smtp_rset(s, c);\n                break;\n\n            case NGX_SMTP_NOOP:\n                break;\n\n            case NGX_SMTP_STARTTLS:\n                rc = ngx_mail_smtp_starttls(s, c);\n                ngx_str_set(&s->out, smtp_starttls);\n                break;\n\n            default:\n                rc = NGX_MAIL_PARSE_INVALID_COMMAND;\n                break;\n            }\n\n            break;\n\n        case ngx_smtp_auth_login_username:\n            rc = ngx_mail_auth_login_username(s, c, 0);\n\n            ngx_str_set(&s->out, smtp_password);\n            s->mail_state = ngx_smtp_auth_login_password;\n            break;\n\n        case ngx_smtp_auth_login_password:\n            rc = ngx_mail_auth_login_password(s, c);\n            break;\n\n        case ngx_smtp_auth_plain:\n            rc = ngx_mail_auth_plain(s, c, 0);\n            break;\n\n        case ngx_smtp_auth_cram_md5:\n            rc = ngx_mail_auth_cram_md5(s, c);\n            break;\n\n        case ngx_smtp_auth_external:\n            rc = ngx_mail_auth_external(s, c, 0);\n            break;\n        }\n    }\n\n    if (s->buffer->pos < s->buffer->last) {\n        s->blocked = 1;\n    }\n\n    switch (rc) {\n\n    case NGX_DONE:\n        ngx_mail_auth(s, c);\n        return;\n\n    case NGX_ERROR:\n        ngx_mail_session_internal_server_error(s);\n        return;\n\n    case NGX_MAIL_PARSE_INVALID_COMMAND:\n        s->mail_state = ngx_smtp_start;\n        s->state = 0;\n        ngx_str_set(&s->out, smtp_invalid_command);\n\n        /* fall through */\n\n    case NGX_OK:\n        s->args.nelts = 0;\n\n        if (s->buffer->pos == s->buffer->last) {\n            s->buffer->pos = s->buffer->start;\n            s->buffer->last = s->buffer->start;\n        }\n\n        if (s->state) {\n            s->arg_start = s->buffer->pos;\n        }\n\n        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n            ngx_mail_session_internal_server_error(s);\n            return;\n        }\n\n        ngx_mail_send(c->write);\n    }\n}\n\n\nstatic ngx_int_t\nngx_mail_smtp_helo(ngx_mail_session_t *s, ngx_connection_t *c)\n{\n    ngx_str_t                 *arg;\n    ngx_mail_smtp_srv_conf_t  *sscf;\n\n    if (s->args.nelts != 1) {\n        ngx_str_set(&s->out, smtp_invalid_argument);\n        s->state = 0;\n        return NGX_OK;\n    }\n\n    arg = s->args.elts;\n\n    s->smtp_helo.len = arg[0].len;\n\n    s->smtp_helo.data = ngx_pnalloc(c->pool, arg[0].len);\n    if (s->smtp_helo.data == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_memcpy(s->smtp_helo.data, arg[0].data, arg[0].len);\n\n    ngx_str_null(&s->smtp_from);\n    ngx_str_null(&s->smtp_to);\n\n    sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module);\n\n    if (s->command == NGX_SMTP_HELO) {\n        s->out = sscf->server_name;\n\n    } else {\n        s->esmtp = 1;\n\n#if (NGX_MAIL_SSL)\n\n        if (c->ssl == NULL) {\n            ngx_mail_ssl_conf_t  *sslcf;\n\n            sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);\n\n            if (sslcf->starttls == NGX_MAIL_STARTTLS_ON) {\n                s->out = sscf->starttls_capability;\n                return NGX_OK;\n            }\n\n            if (sslcf->starttls == NGX_MAIL_STARTTLS_ONLY) {\n                s->out = sscf->starttls_only_capability;\n                return NGX_OK;\n            }\n        }\n#endif\n\n        s->out = sscf->capability;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_mail_smtp_auth(ngx_mail_session_t *s, ngx_connection_t *c)\n{\n    ngx_int_t                  rc;\n    ngx_mail_core_srv_conf_t  *cscf;\n    ngx_mail_smtp_srv_conf_t  *sscf;\n\n#if (NGX_MAIL_SSL)\n    if (ngx_mail_starttls_only(s, c)) {\n        return NGX_MAIL_PARSE_INVALID_COMMAND;\n    }\n#endif\n\n    if (s->args.nelts == 0) {\n        ngx_str_set(&s->out, smtp_invalid_argument);\n        s->state = 0;\n        return NGX_OK;\n    }\n\n    sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module);\n\n    rc = ngx_mail_auth_parse(s, c);\n\n    switch (rc) {\n\n    case NGX_MAIL_AUTH_LOGIN:\n\n        ngx_str_set(&s->out, smtp_username);\n        s->mail_state = ngx_smtp_auth_login_username;\n\n        return NGX_OK;\n\n    case NGX_MAIL_AUTH_LOGIN_USERNAME:\n\n        ngx_str_set(&s->out, smtp_password);\n        s->mail_state = ngx_smtp_auth_login_password;\n\n        return ngx_mail_auth_login_username(s, c, 1);\n\n    case NGX_MAIL_AUTH_PLAIN:\n\n        ngx_str_set(&s->out, smtp_next);\n        s->mail_state = ngx_smtp_auth_plain;\n\n        return NGX_OK;\n\n    case NGX_MAIL_AUTH_CRAM_MD5:\n\n        if (!(sscf->auth_methods & NGX_MAIL_AUTH_CRAM_MD5_ENABLED)) {\n            return NGX_MAIL_PARSE_INVALID_COMMAND;\n        }\n\n        if (s->salt.data == NULL) {\n            cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);\n\n            if (ngx_mail_salt(s, c, cscf) != NGX_OK) {\n                return NGX_ERROR;\n            }\n        }\n\n        if (ngx_mail_auth_cram_md5_salt(s, c, \"334 \", 4) == NGX_OK) {\n            s->mail_state = ngx_smtp_auth_cram_md5;\n            return NGX_OK;\n        }\n\n        return NGX_ERROR;\n\n    case NGX_MAIL_AUTH_EXTERNAL:\n\n        if (!(sscf->auth_methods & NGX_MAIL_AUTH_EXTERNAL_ENABLED)) {\n            return NGX_MAIL_PARSE_INVALID_COMMAND;\n        }\n\n        ngx_str_set(&s->out, smtp_username);\n        s->mail_state = ngx_smtp_auth_external;\n\n        return NGX_OK;\n    }\n\n    return rc;\n}\n\n\nstatic ngx_int_t\nngx_mail_smtp_mail(ngx_mail_session_t *s, ngx_connection_t *c)\n{\n    ngx_str_t                 *arg, cmd;\n    ngx_mail_smtp_srv_conf_t  *sscf;\n\n    sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module);\n\n    if (!(sscf->auth_methods & NGX_MAIL_AUTH_NONE_ENABLED)) {\n        ngx_mail_smtp_log_rejected_command(s, c, \"client was rejected: \\\"%V\\\"\");\n        ngx_str_set(&s->out, smtp_auth_required);\n        return NGX_OK;\n    }\n\n    /* auth none */\n\n    if (s->smtp_from.len) {\n        ngx_str_set(&s->out, smtp_bad_sequence);\n        return NGX_OK;\n    }\n\n    if (s->args.nelts == 0) {\n        ngx_str_set(&s->out, smtp_invalid_argument);\n        return NGX_OK;\n    }\n\n    arg = s->args.elts;\n    arg += s->args.nelts - 1;\n\n    cmd.len = arg->data + arg->len - s->cmd.data;\n    cmd.data = s->cmd.data;\n\n    s->smtp_from.len = cmd.len;\n\n    s->smtp_from.data = ngx_pnalloc(c->pool, cmd.len);\n    if (s->smtp_from.data == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_memcpy(s->smtp_from.data, cmd.data, cmd.len);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,\n                   \"smtp mail from:\\\"%V\\\"\", &s->smtp_from);\n\n    ngx_str_set(&s->out, smtp_ok);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_mail_smtp_rcpt(ngx_mail_session_t *s, ngx_connection_t *c)\n{\n    ngx_str_t  *arg, cmd;\n\n    if (s->smtp_from.len == 0) {\n        ngx_str_set(&s->out, smtp_bad_sequence);\n        return NGX_OK;\n    }\n\n    if (s->args.nelts == 0) {\n        ngx_str_set(&s->out, smtp_invalid_argument);\n        return NGX_OK;\n    }\n\n    arg = s->args.elts;\n    arg += s->args.nelts - 1;\n\n    cmd.len = arg->data + arg->len - s->cmd.data;\n    cmd.data = s->cmd.data;\n\n    s->smtp_to.len = cmd.len;\n\n    s->smtp_to.data = ngx_pnalloc(c->pool, cmd.len);\n    if (s->smtp_to.data == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_memcpy(s->smtp_to.data, cmd.data, cmd.len);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,\n                   \"smtp rcpt to:\\\"%V\\\"\", &s->smtp_to);\n\n    s->auth_method = NGX_MAIL_AUTH_NONE;\n\n    return NGX_DONE;\n}\n\n\nstatic ngx_int_t\nngx_mail_smtp_rset(ngx_mail_session_t *s, ngx_connection_t *c)\n{\n    ngx_str_null(&s->smtp_from);\n    ngx_str_null(&s->smtp_to);\n    ngx_str_set(&s->out, smtp_ok);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_mail_smtp_starttls(ngx_mail_session_t *s, ngx_connection_t *c)\n{\n#if (NGX_MAIL_SSL)\n    ngx_mail_ssl_conf_t  *sslcf;\n\n    if (c->ssl == NULL) {\n        sslcf = ngx_mail_get_module_srv_conf(s, ngx_mail_ssl_module);\n        if (sslcf->starttls) {\n\n            /*\n             * RFC3207 requires us to discard any knowledge\n             * obtained from client before STARTTLS.\n             */\n\n            ngx_str_null(&s->smtp_helo);\n            ngx_str_null(&s->smtp_from);\n            ngx_str_null(&s->smtp_to);\n\n            s->buffer->pos = s->buffer->start;\n            s->buffer->last = s->buffer->start;\n\n            c->read->handler = ngx_mail_starttls_handler;\n            return NGX_OK;\n        }\n    }\n\n#endif\n\n    return NGX_MAIL_PARSE_INVALID_COMMAND;\n}\n\n\nstatic ngx_int_t\nngx_mail_smtp_discard_command(ngx_mail_session_t *s, ngx_connection_t *c,\n    char *err)\n{\n    ssize_t    n;\n\n    n = c->recv(c, s->buffer->last, s->buffer->end - s->buffer->last);\n\n    if (n == NGX_ERROR || n == 0) {\n        ngx_mail_close_connection(c);\n        return NGX_ERROR;\n    }\n\n    if (n > 0) {\n        s->buffer->last += n;\n    }\n\n    if (n == NGX_AGAIN) {\n        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n            ngx_mail_session_internal_server_error(s);\n            return NGX_ERROR;\n        }\n\n        return NGX_AGAIN;\n    }\n\n    ngx_mail_smtp_log_rejected_command(s, c, err);\n\n    s->buffer->pos = s->buffer->start;\n    s->buffer->last = s->buffer->start;\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_mail_smtp_log_rejected_command(ngx_mail_session_t *s, ngx_connection_t *c,\n    char *err)\n{\n    u_char      ch;\n    ngx_str_t   cmd;\n    ngx_uint_t  i;\n\n    if (c->log->log_level < NGX_LOG_INFO) {\n        return;\n    }\n\n    cmd.len = s->buffer->last - s->buffer->start;\n    cmd.data = s->buffer->start;\n\n    for (i = 0; i < cmd.len; i++) {\n        ch = cmd.data[i];\n\n        if (ch != CR && ch != LF) {\n            continue;\n        }\n\n        cmd.data[i] = '_';\n    }\n\n    cmd.len = i;\n\n    ngx_log_error(NGX_LOG_INFO, c->log, 0, err, &cmd);\n}\n"
  },
  {
    "path": "src/mail/ngx_mail_smtp_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n#include <ngx_mail.h>\n#include <ngx_mail_smtp_module.h>\n\n\nstatic void *ngx_mail_smtp_create_srv_conf(ngx_conf_t *cf);\nstatic char *ngx_mail_smtp_merge_srv_conf(ngx_conf_t *cf, void *parent,\n    void *child);\n\n\nstatic ngx_conf_bitmask_t  ngx_mail_smtp_auth_methods[] = {\n    { ngx_string(\"plain\"), NGX_MAIL_AUTH_PLAIN_ENABLED },\n    { ngx_string(\"login\"), NGX_MAIL_AUTH_LOGIN_ENABLED },\n    { ngx_string(\"cram-md5\"), NGX_MAIL_AUTH_CRAM_MD5_ENABLED },\n    { ngx_string(\"external\"), NGX_MAIL_AUTH_EXTERNAL_ENABLED },\n    { ngx_string(\"none\"), NGX_MAIL_AUTH_NONE_ENABLED },\n    { ngx_null_string, 0 }\n};\n\n\nstatic ngx_str_t  ngx_mail_smtp_auth_methods_names[] = {\n    ngx_string(\"PLAIN\"),\n    ngx_string(\"LOGIN\"),\n    ngx_null_string,  /* APOP */\n    ngx_string(\"CRAM-MD5\"),\n    ngx_string(\"EXTERNAL\"),\n    ngx_null_string   /* NONE */\n};\n\n\nstatic ngx_mail_protocol_t  ngx_mail_smtp_protocol = {\n    ngx_string(\"smtp\"),\n    ngx_string(\"\\x04smtp\"),\n    { 25, 465, 587, 0 },\n    NGX_MAIL_SMTP_PROTOCOL,\n\n    ngx_mail_smtp_init_session,\n    ngx_mail_smtp_init_protocol,\n    ngx_mail_smtp_parse_command,\n    ngx_mail_smtp_auth_state,\n\n    ngx_string(\"451 4.3.2 Internal server error\" CRLF),\n    ngx_string(\"421 4.7.1 SSL certificate error\" CRLF),\n    ngx_string(\"421 4.7.1 No required SSL certificate\" CRLF)\n};\n\n\nstatic ngx_command_t  ngx_mail_smtp_commands[] = {\n\n    { ngx_string(\"smtp_client_buffer\"),\n      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_MAIL_SRV_CONF_OFFSET,\n      offsetof(ngx_mail_smtp_srv_conf_t, client_buffer_size),\n      NULL },\n\n    { ngx_string(\"smtp_greeting_delay\"),\n      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_MAIL_SRV_CONF_OFFSET,\n      offsetof(ngx_mail_smtp_srv_conf_t, greeting_delay),\n      NULL },\n\n    { ngx_string(\"smtp_capabilities\"),\n      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE,\n      ngx_mail_capabilities,\n      NGX_MAIL_SRV_CONF_OFFSET,\n      offsetof(ngx_mail_smtp_srv_conf_t, capabilities),\n      NULL },\n\n    { ngx_string(\"smtp_auth\"),\n      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE,\n      ngx_conf_set_bitmask_slot,\n      NGX_MAIL_SRV_CONF_OFFSET,\n      offsetof(ngx_mail_smtp_srv_conf_t, auth_methods),\n      &ngx_mail_smtp_auth_methods },\n\n      ngx_null_command\n};\n\n\nstatic ngx_mail_module_t  ngx_mail_smtp_module_ctx = {\n    &ngx_mail_smtp_protocol,               /* protocol */\n\n    NULL,                                  /* create main configuration */\n    NULL,                                  /* init main configuration */\n\n    ngx_mail_smtp_create_srv_conf,         /* create server configuration */\n    ngx_mail_smtp_merge_srv_conf           /* merge server configuration */\n};\n\n\nngx_module_t  ngx_mail_smtp_module = {\n    NGX_MODULE_V1,\n    &ngx_mail_smtp_module_ctx,             /* module context */\n    ngx_mail_smtp_commands,                /* module directives */\n    NGX_MAIL_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 void *\nngx_mail_smtp_create_srv_conf(ngx_conf_t *cf)\n{\n    ngx_mail_smtp_srv_conf_t  *sscf;\n\n    sscf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_smtp_srv_conf_t));\n    if (sscf == NULL) {\n        return NULL;\n    }\n\n    sscf->client_buffer_size = NGX_CONF_UNSET_SIZE;\n    sscf->greeting_delay = NGX_CONF_UNSET_MSEC;\n\n    if (ngx_array_init(&sscf->capabilities, cf->pool, 4, sizeof(ngx_str_t))\n        != NGX_OK)\n    {\n        return NULL;\n    }\n\n    return sscf;\n}\n\n\nstatic char *\nngx_mail_smtp_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_mail_smtp_srv_conf_t *prev = parent;\n    ngx_mail_smtp_srv_conf_t *conf = child;\n\n    u_char                    *p, *auth, *last;\n    size_t                     size;\n    ngx_str_t                 *c;\n    ngx_uint_t                 i, m, auth_enabled;\n    ngx_mail_core_srv_conf_t  *cscf;\n\n    ngx_conf_merge_size_value(conf->client_buffer_size,\n                              prev->client_buffer_size,\n                              (size_t) ngx_pagesize);\n\n    ngx_conf_merge_msec_value(conf->greeting_delay,\n                              prev->greeting_delay, 0);\n\n    ngx_conf_merge_bitmask_value(conf->auth_methods,\n                              prev->auth_methods,\n                              (NGX_CONF_BITMASK_SET\n                               |NGX_MAIL_AUTH_PLAIN_ENABLED\n                               |NGX_MAIL_AUTH_LOGIN_ENABLED));\n\n\n    cscf = ngx_mail_conf_get_module_srv_conf(cf, ngx_mail_core_module);\n\n    size = sizeof(\"220  ESMTP ready\" CRLF) - 1 + cscf->server_name.len;\n\n    p = ngx_pnalloc(cf->pool, size);\n    if (p == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    conf->greeting.len = size;\n    conf->greeting.data = p;\n\n    *p++ = '2'; *p++ = '2'; *p++ = '0'; *p++ = ' ';\n    p = ngx_cpymem(p, cscf->server_name.data, cscf->server_name.len);\n    ngx_memcpy(p, \" ESMTP ready\" CRLF, sizeof(\" ESMTP ready\" CRLF) - 1);\n\n\n    size = sizeof(\"250 \" CRLF) - 1 + cscf->server_name.len;\n\n    p = ngx_pnalloc(cf->pool, size);\n    if (p == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    conf->server_name.len = size;\n    conf->server_name.data = p;\n\n    *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = ' ';\n    p = ngx_cpymem(p, cscf->server_name.data, cscf->server_name.len);\n    *p++ = CR; *p = LF;\n\n\n    if (conf->capabilities.nelts == 0) {\n        conf->capabilities = prev->capabilities;\n    }\n\n    size = sizeof(\"250-\") - 1 + cscf->server_name.len + sizeof(CRLF) - 1;\n\n    c = conf->capabilities.elts;\n    for (i = 0; i < conf->capabilities.nelts; i++) {\n        size += sizeof(\"250 \") - 1 + c[i].len + sizeof(CRLF) - 1;\n    }\n\n    auth_enabled = 0;\n\n    for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0;\n         m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED;\n         m <<= 1, i++)\n    {\n        if (m & conf->auth_methods) {\n            size += 1 + ngx_mail_smtp_auth_methods_names[i].len;\n            auth_enabled = 1;\n        }\n    }\n\n    if (auth_enabled) {\n        size += sizeof(\"250 AUTH\") - 1 + sizeof(CRLF) - 1;\n    }\n\n    p = ngx_pnalloc(cf->pool, size);\n    if (p == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    conf->capability.len = size;\n    conf->capability.data = p;\n\n    last = p;\n\n    *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = '-';\n    p = ngx_cpymem(p, cscf->server_name.data, cscf->server_name.len);\n    *p++ = CR; *p++ = LF;\n\n    for (i = 0; i < conf->capabilities.nelts; i++) {\n        last = p;\n        *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = '-';\n        p = ngx_cpymem(p, c[i].data, c[i].len);\n        *p++ = CR; *p++ = LF;\n    }\n\n    auth = p;\n\n    if (auth_enabled) {\n        last = p;\n\n        *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = ' ';\n        *p++ = 'A'; *p++ = 'U'; *p++ = 'T'; *p++ = 'H';\n\n        for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0;\n             m <= NGX_MAIL_AUTH_EXTERNAL_ENABLED;\n             m <<= 1, i++)\n        {\n            if (m & conf->auth_methods) {\n                *p++ = ' ';\n                p = ngx_cpymem(p, ngx_mail_smtp_auth_methods_names[i].data,\n                               ngx_mail_smtp_auth_methods_names[i].len);\n            }\n        }\n\n        *p++ = CR; *p = LF;\n\n    } else {\n        last[3] = ' ';\n    }\n\n    size += sizeof(\"250 STARTTLS\" CRLF) - 1;\n\n    p = ngx_pnalloc(cf->pool, size);\n    if (p == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    conf->starttls_capability.len = size;\n    conf->starttls_capability.data = p;\n\n    p = ngx_cpymem(p, conf->capability.data, conf->capability.len);\n\n    ngx_memcpy(p, \"250 STARTTLS\" CRLF, sizeof(\"250 STARTTLS\" CRLF) - 1);\n\n    p = conf->starttls_capability.data\n        + (last - conf->capability.data) + 3;\n    *p = '-';\n\n    size = (auth - conf->capability.data)\n            + sizeof(\"250 STARTTLS\" CRLF) - 1;\n\n    p = ngx_pnalloc(cf->pool, size);\n    if (p == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    conf->starttls_only_capability.len = size;\n    conf->starttls_only_capability.data = p;\n\n    p = ngx_cpymem(p, conf->capability.data, auth - conf->capability.data);\n\n    ngx_memcpy(p, \"250 STARTTLS\" CRLF, sizeof(\"250 STARTTLS\" CRLF) - 1);\n\n    if (last < auth) {\n        p = conf->starttls_only_capability.data\n            + (last - conf->capability.data) + 3;\n        *p = '-';\n    }\n\n    return NGX_CONF_OK;\n}\n"
  },
  {
    "path": "src/mail/ngx_mail_smtp_module.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_MAIL_SMTP_MODULE_H_INCLUDED_\n#define _NGX_MAIL_SMTP_MODULE_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_mail.h>\n#include <ngx_mail_smtp_module.h>\n\n\ntypedef struct {\n    ngx_msec_t   greeting_delay;\n\n    size_t       client_buffer_size;\n\n    ngx_str_t    capability;\n    ngx_str_t    starttls_capability;\n    ngx_str_t    starttls_only_capability;\n\n    ngx_str_t    server_name;\n    ngx_str_t    greeting;\n\n    ngx_uint_t   auth_methods;\n\n    ngx_array_t  capabilities;\n} ngx_mail_smtp_srv_conf_t;\n\n\nvoid ngx_mail_smtp_init_session(ngx_mail_session_t *s, ngx_connection_t *c);\nvoid ngx_mail_smtp_init_protocol(ngx_event_t *rev);\nvoid ngx_mail_smtp_auth_state(ngx_event_t *rev);\nngx_int_t ngx_mail_smtp_parse_command(ngx_mail_session_t *s);\n\n\nextern ngx_module_t  ngx_mail_smtp_module;\n\n\n#endif /* _NGX_MAIL_SMTP_MODULE_H_INCLUDED_ */\n"
  },
  {
    "path": "src/mail/ngx_mail_ssl_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_mail.h>\n\n\n#define NGX_DEFAULT_CIPHERS     \"HIGH:!aNULL:!MD5\"\n#define NGX_DEFAULT_ECDH_CURVE  \"auto\"\n\n\n#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation\nstatic int ngx_mail_ssl_alpn_select(ngx_ssl_conn_t *ssl_conn,\n    const unsigned char **out, unsigned char *outlen,\n    const unsigned char *in, unsigned int inlen, void *arg);\n#endif\n\nstatic void *ngx_mail_ssl_create_conf(ngx_conf_t *cf);\nstatic char *ngx_mail_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child);\n\nstatic char *ngx_mail_ssl_enable(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_mail_ssl_starttls(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_mail_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_mail_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n\nstatic char *ngx_mail_ssl_conf_command_check(ngx_conf_t *cf, void *post,\n    void *data);\n\n\nstatic ngx_conf_enum_t  ngx_mail_starttls_state[] = {\n    { ngx_string(\"off\"), NGX_MAIL_STARTTLS_OFF },\n    { ngx_string(\"on\"), NGX_MAIL_STARTTLS_ON },\n    { ngx_string(\"only\"), NGX_MAIL_STARTTLS_ONLY },\n    { ngx_null_string, 0 }\n};\n\n\n\nstatic ngx_conf_bitmask_t  ngx_mail_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    { ngx_string(\"TLSv1.3\"), NGX_SSL_TLSv1_3 },\n    { ngx_null_string, 0 }\n};\n\n\nstatic ngx_conf_enum_t  ngx_mail_ssl_verify[] = {\n    { ngx_string(\"off\"), 0 },\n    { ngx_string(\"on\"), 1 },\n    { ngx_string(\"optional\"), 2 },\n    { ngx_string(\"optional_no_ca\"), 3 },\n    { ngx_null_string, 0 }\n};\n\n\nstatic ngx_conf_deprecated_t  ngx_mail_ssl_deprecated = {\n    ngx_conf_deprecated, \"ssl\", \"listen ... ssl\"\n};\n\n\nstatic ngx_conf_post_t  ngx_mail_ssl_conf_command_post =\n    { ngx_mail_ssl_conf_command_check };\n\n\nstatic ngx_command_t  ngx_mail_ssl_commands[] = {\n\n    { ngx_string(\"ssl\"),\n      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG,\n      ngx_mail_ssl_enable,\n      NGX_MAIL_SRV_CONF_OFFSET,\n      offsetof(ngx_mail_ssl_conf_t, enable),\n      &ngx_mail_ssl_deprecated },\n\n    { ngx_string(\"starttls\"),\n      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_mail_ssl_starttls,\n      NGX_MAIL_SRV_CONF_OFFSET,\n      offsetof(ngx_mail_ssl_conf_t, starttls),\n      ngx_mail_starttls_state },\n\n    { ngx_string(\"ssl_certificate\"),\n      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_array_slot,\n      NGX_MAIL_SRV_CONF_OFFSET,\n      offsetof(ngx_mail_ssl_conf_t, certificates),\n      NULL },\n\n    { ngx_string(\"ssl_certificate_key\"),\n      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_array_slot,\n      NGX_MAIL_SRV_CONF_OFFSET,\n      offsetof(ngx_mail_ssl_conf_t, certificate_keys),\n      NULL },\n\n    { ngx_string(\"ssl_password_file\"),\n      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_mail_ssl_password_file,\n      NGX_MAIL_SRV_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"ssl_dhparam\"),\n      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_slot,\n      NGX_MAIL_SRV_CONF_OFFSET,\n      offsetof(ngx_mail_ssl_conf_t, dhparam),\n      NULL },\n\n    { ngx_string(\"ssl_ecdh_curve\"),\n      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_slot,\n      NGX_MAIL_SRV_CONF_OFFSET,\n      offsetof(ngx_mail_ssl_conf_t, ecdh_curve),\n      NULL },\n\n    { ngx_string(\"ssl_protocols\"),\n      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_1MORE,\n      ngx_conf_set_bitmask_slot,\n      NGX_MAIL_SRV_CONF_OFFSET,\n      offsetof(ngx_mail_ssl_conf_t, protocols),\n      &ngx_mail_ssl_protocols },\n\n    { ngx_string(\"ssl_ciphers\"),\n      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_slot,\n      NGX_MAIL_SRV_CONF_OFFSET,\n      offsetof(ngx_mail_ssl_conf_t, ciphers),\n      NULL },\n\n    { ngx_string(\"ssl_prefer_server_ciphers\"),\n      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_MAIL_SRV_CONF_OFFSET,\n      offsetof(ngx_mail_ssl_conf_t, prefer_server_ciphers),\n      NULL },\n\n    { ngx_string(\"ssl_session_cache\"),\n      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE12,\n      ngx_mail_ssl_session_cache,\n      NGX_MAIL_SRV_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"ssl_session_tickets\"),\n      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_MAIL_SRV_CONF_OFFSET,\n      offsetof(ngx_mail_ssl_conf_t, session_tickets),\n      NULL },\n\n    { ngx_string(\"ssl_session_ticket_key\"),\n      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_array_slot,\n      NGX_MAIL_SRV_CONF_OFFSET,\n      offsetof(ngx_mail_ssl_conf_t, session_ticket_keys),\n      NULL },\n\n    { ngx_string(\"ssl_session_timeout\"),\n      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_sec_slot,\n      NGX_MAIL_SRV_CONF_OFFSET,\n      offsetof(ngx_mail_ssl_conf_t, session_timeout),\n      NULL },\n\n    { ngx_string(\"ssl_verify_client\"),\n      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_enum_slot,\n      NGX_MAIL_SRV_CONF_OFFSET,\n      offsetof(ngx_mail_ssl_conf_t, verify),\n      &ngx_mail_ssl_verify },\n\n    { ngx_string(\"ssl_verify_depth\"),\n      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_num_slot,\n      NGX_MAIL_SRV_CONF_OFFSET,\n      offsetof(ngx_mail_ssl_conf_t, verify_depth),\n      NULL },\n\n    { ngx_string(\"ssl_client_certificate\"),\n      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_slot,\n      NGX_MAIL_SRV_CONF_OFFSET,\n      offsetof(ngx_mail_ssl_conf_t, client_certificate),\n      NULL },\n\n    { ngx_string(\"ssl_trusted_certificate\"),\n      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_slot,\n      NGX_MAIL_SRV_CONF_OFFSET,\n      offsetof(ngx_mail_ssl_conf_t, trusted_certificate),\n      NULL },\n\n    { ngx_string(\"ssl_crl\"),\n      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_slot,\n      NGX_MAIL_SRV_CONF_OFFSET,\n      offsetof(ngx_mail_ssl_conf_t, crl),\n      NULL },\n\n    { ngx_string(\"ssl_conf_command\"),\n      NGX_MAIL_MAIN_CONF|NGX_MAIL_SRV_CONF|NGX_CONF_TAKE2,\n      ngx_conf_set_keyval_slot,\n      NGX_MAIL_SRV_CONF_OFFSET,\n      offsetof(ngx_mail_ssl_conf_t, conf_commands),\n      &ngx_mail_ssl_conf_command_post },\n\n      ngx_null_command\n};\n\n\nstatic ngx_mail_module_t  ngx_mail_ssl_module_ctx = {\n    NULL,                                  /* protocol */\n\n    NULL,                                  /* create main configuration */\n    NULL,                                  /* init main configuration */\n\n    ngx_mail_ssl_create_conf,              /* create server configuration */\n    ngx_mail_ssl_merge_conf                /* merge server configuration */\n};\n\n\nngx_module_t  ngx_mail_ssl_module = {\n    NGX_MODULE_V1,\n    &ngx_mail_ssl_module_ctx,              /* module context */\n    ngx_mail_ssl_commands,                 /* module directives */\n    NGX_MAIL_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_str_t ngx_mail_ssl_sess_id_ctx = ngx_string(\"MAIL\");\n\n\n#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation\n\nstatic int\nngx_mail_ssl_alpn_select(ngx_ssl_conn_t *ssl_conn, const unsigned char **out,\n    unsigned char *outlen, const unsigned char *in, unsigned int inlen,\n    void *arg)\n{\n    unsigned int               srvlen;\n    unsigned char             *srv;\n    ngx_connection_t          *c;\n    ngx_mail_session_t        *s;\n    ngx_mail_core_srv_conf_t  *cscf;\n#if (NGX_DEBUG)\n    unsigned int               i;\n#endif\n\n    c = ngx_ssl_get_connection(ssl_conn);\n    s = c->data;\n\n#if (NGX_DEBUG)\n    for (i = 0; i < inlen; i += in[i] + 1) {\n        ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0,\n                       \"SSL ALPN supported by client: %*s\",\n                       (size_t) in[i], &in[i + 1]);\n    }\n#endif\n\n    cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module);\n\n    srv = cscf->protocol->alpn.data;\n    srvlen = cscf->protocol->alpn.len;\n\n    if (SSL_select_next_proto((unsigned char **) out, outlen, srv, srvlen,\n                              in, inlen)\n        != OPENSSL_NPN_NEGOTIATED)\n    {\n        return SSL_TLSEXT_ERR_ALERT_FATAL;\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0,\n                   \"SSL ALPN selected: %*s\", (size_t) *outlen, *out);\n\n    return SSL_TLSEXT_ERR_OK;\n}\n\n#endif\n\n\nstatic void *\nngx_mail_ssl_create_conf(ngx_conf_t *cf)\n{\n    ngx_mail_ssl_conf_t  *scf;\n\n    scf = ngx_pcalloc(cf->pool, sizeof(ngx_mail_ssl_conf_t));\n    if (scf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc():\n     *\n     *     scf->listen = 0;\n     *     scf->protocols = 0;\n     *     scf->dhparam = { 0, NULL };\n     *     scf->ecdh_curve = { 0, NULL };\n     *     scf->client_certificate = { 0, NULL };\n     *     scf->trusted_certificate = { 0, NULL };\n     *     scf->crl = { 0, NULL };\n     *     scf->ciphers = { 0, NULL };\n     *     scf->shm_zone = NULL;\n     */\n\n    scf->enable = NGX_CONF_UNSET;\n    scf->starttls = NGX_CONF_UNSET_UINT;\n    scf->certificates = NGX_CONF_UNSET_PTR;\n    scf->certificate_keys = NGX_CONF_UNSET_PTR;\n    scf->passwords = NGX_CONF_UNSET_PTR;\n    scf->conf_commands = NGX_CONF_UNSET_PTR;\n    scf->prefer_server_ciphers = NGX_CONF_UNSET;\n    scf->verify = NGX_CONF_UNSET_UINT;\n    scf->verify_depth = NGX_CONF_UNSET_UINT;\n    scf->builtin_session_cache = NGX_CONF_UNSET;\n    scf->session_timeout = NGX_CONF_UNSET;\n    scf->session_tickets = NGX_CONF_UNSET;\n    scf->session_ticket_keys = NGX_CONF_UNSET_PTR;\n\n    return scf;\n}\n\n\nstatic char *\nngx_mail_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_mail_ssl_conf_t *prev = parent;\n    ngx_mail_ssl_conf_t *conf = child;\n\n    char                *mode;\n    ngx_pool_cleanup_t  *cln;\n\n    ngx_conf_merge_value(conf->enable, prev->enable, 0);\n    ngx_conf_merge_uint_value(conf->starttls, prev->starttls,\n                         NGX_MAIL_STARTTLS_OFF);\n\n    ngx_conf_merge_value(conf->session_timeout,\n                         prev->session_timeout, 300);\n\n    ngx_conf_merge_value(conf->prefer_server_ciphers,\n                         prev->prefer_server_ciphers, 0);\n\n    ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols,\n                         (NGX_CONF_BITMASK_SET\n                          |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1\n                          |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3));\n\n    ngx_conf_merge_uint_value(conf->verify, prev->verify, 0);\n    ngx_conf_merge_uint_value(conf->verify_depth, prev->verify_depth, 1);\n\n    ngx_conf_merge_ptr_value(conf->certificates, prev->certificates, NULL);\n    ngx_conf_merge_ptr_value(conf->certificate_keys, prev->certificate_keys,\n                         NULL);\n\n    ngx_conf_merge_ptr_value(conf->passwords, prev->passwords, NULL);\n\n    ngx_conf_merge_str_value(conf->dhparam, prev->dhparam, \"\");\n\n    ngx_conf_merge_str_value(conf->ecdh_curve, prev->ecdh_curve,\n                         NGX_DEFAULT_ECDH_CURVE);\n\n    ngx_conf_merge_str_value(conf->client_certificate,\n                         prev->client_certificate, \"\");\n    ngx_conf_merge_str_value(conf->trusted_certificate,\n                         prev->trusted_certificate, \"\");\n    ngx_conf_merge_str_value(conf->crl, prev->crl, \"\");\n\n    ngx_conf_merge_str_value(conf->ciphers, prev->ciphers, NGX_DEFAULT_CIPHERS);\n\n    ngx_conf_merge_ptr_value(conf->conf_commands, prev->conf_commands, NULL);\n\n\n    conf->ssl.log = cf->log;\n\n    if (conf->listen) {\n        mode = \"listen ... ssl\";\n\n    } else if (conf->enable) {\n        mode = \"ssl\";\n\n    } else if (conf->starttls != NGX_MAIL_STARTTLS_OFF) {\n        mode = \"starttls\";\n\n    } else {\n        return NGX_CONF_OK;\n    }\n\n    if (conf->file == NULL) {\n        conf->file = prev->file;\n        conf->line = prev->line;\n    }\n\n    if (conf->certificates == NULL) {\n        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                      \"no \\\"ssl_certificate\\\" is defined for \"\n                      \"the \\\"%s\\\" directive in %s:%ui\",\n                      mode, conf->file, conf->line);\n        return NGX_CONF_ERROR;\n    }\n\n    if (conf->certificate_keys == NULL) {\n        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                      \"no \\\"ssl_certificate_key\\\" is defined for \"\n                      \"the \\\"%s\\\" directive in %s:%ui\",\n                      mode, conf->file, conf->line);\n        return NGX_CONF_ERROR;\n    }\n\n    if (conf->certificate_keys->nelts < conf->certificates->nelts) {\n        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                      \"no \\\"ssl_certificate_key\\\" is defined \"\n                      \"for certificate \\\"%V\\\" and \"\n                      \"the \\\"%s\\\" directive in %s:%ui\",\n                      ((ngx_str_t *) conf->certificates->elts)\n                      + conf->certificates->nelts - 1,\n                      mode, conf->file, conf->line);\n        return NGX_CONF_ERROR;\n    }\n\n    if (ngx_ssl_create(&conf->ssl, conf->protocols, NULL) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    cln = ngx_pool_cleanup_add(cf->pool, 0);\n    if (cln == NULL) {\n        ngx_ssl_cleanup_ctx(&conf->ssl);\n        return NGX_CONF_ERROR;\n    }\n\n    cln->handler = ngx_ssl_cleanup_ctx;\n    cln->data = &conf->ssl;\n\n#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation\n    SSL_CTX_set_alpn_select_cb(conf->ssl.ctx, ngx_mail_ssl_alpn_select, NULL);\n#endif\n\n    if (ngx_ssl_ciphers(cf, &conf->ssl, &conf->ciphers,\n                        conf->prefer_server_ciphers)\n        != NGX_OK)\n    {\n        return NGX_CONF_ERROR;\n    }\n\n    if (ngx_ssl_certificates(cf, &conf->ssl, conf->certificates,\n                             conf->certificate_keys, conf->passwords)\n        != NGX_OK)\n    {\n        return NGX_CONF_ERROR;\n    }\n\n    if (conf->verify) {\n\n        if (conf->client_certificate.len == 0 && conf->verify != 3) {\n            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                          \"no ssl_client_certificate for ssl_verify_client\");\n            return NGX_CONF_ERROR;\n        }\n\n        if (ngx_ssl_client_certificate(cf, &conf->ssl,\n                                       &conf->client_certificate,\n                                       conf->verify_depth)\n            != NGX_OK)\n        {\n            return NGX_CONF_ERROR;\n        }\n\n        if (ngx_ssl_trusted_certificate(cf, &conf->ssl,\n                                        &conf->trusted_certificate,\n                                        conf->verify_depth)\n            != NGX_OK)\n        {\n            return NGX_CONF_ERROR;\n        }\n\n        if (ngx_ssl_crl(cf, &conf->ssl, &conf->crl) != NGX_OK) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    if (ngx_ssl_dhparam(cf, &conf->ssl, &conf->dhparam) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (ngx_ssl_ecdh_curve(cf, &conf->ssl, &conf->ecdh_curve) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_conf_merge_value(conf->builtin_session_cache,\n                         prev->builtin_session_cache, NGX_SSL_NONE_SCACHE);\n\n    if (conf->shm_zone == NULL) {\n        conf->shm_zone = prev->shm_zone;\n    }\n\n    if (ngx_ssl_session_cache(&conf->ssl, &ngx_mail_ssl_sess_id_ctx,\n                              conf->certificates, conf->builtin_session_cache,\n                              conf->shm_zone, conf->session_timeout)\n        != NGX_OK)\n    {\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_conf_merge_value(conf->session_tickets,\n                         prev->session_tickets, 1);\n\n#ifdef SSL_OP_NO_TICKET\n    if (!conf->session_tickets) {\n        SSL_CTX_set_options(conf->ssl.ctx, SSL_OP_NO_TICKET);\n    }\n#endif\n\n    ngx_conf_merge_ptr_value(conf->session_ticket_keys,\n                         prev->session_ticket_keys, NULL);\n\n    if (ngx_ssl_session_ticket_keys(cf, &conf->ssl, conf->session_ticket_keys)\n        != NGX_OK)\n    {\n        return NGX_CONF_ERROR;\n    }\n\n    if (ngx_ssl_conf_commands(cf, &conf->ssl, conf->conf_commands) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_mail_ssl_enable(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_mail_ssl_conf_t  *scf = conf;\n\n    char  *rv;\n\n    rv = ngx_conf_set_flag_slot(cf, cmd, conf);\n\n    if (rv != NGX_CONF_OK) {\n        return rv;\n    }\n\n    if (scf->enable && (ngx_int_t) scf->starttls > NGX_MAIL_STARTTLS_OFF) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"\\\"starttls\\\" directive conflicts with \\\"ssl on\\\"\");\n        return NGX_CONF_ERROR;\n    }\n\n    if (!scf->listen) {\n        scf->file = cf->conf_file->file.name.data;\n        scf->line = cf->conf_file->line;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_mail_ssl_starttls(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_mail_ssl_conf_t  *scf = conf;\n\n    char  *rv;\n\n    rv = ngx_conf_set_enum_slot(cf, cmd, conf);\n\n    if (rv != NGX_CONF_OK) {\n        return rv;\n    }\n\n    if (scf->enable == 1 && (ngx_int_t) scf->starttls > NGX_MAIL_STARTTLS_OFF) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"\\\"ssl\\\" directive conflicts with \\\"starttls\\\"\");\n        return NGX_CONF_ERROR;\n    }\n\n    if (!scf->listen) {\n        scf->file = cf->conf_file->file.name.data;\n        scf->line = cf->conf_file->line;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_mail_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_mail_ssl_conf_t  *scf = conf;\n\n    ngx_str_t  *value;\n\n    if (scf->passwords != NGX_CONF_UNSET_PTR) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    scf->passwords = ngx_ssl_read_password_file(cf, &value[1]);\n\n    if (scf->passwords == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_mail_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_mail_ssl_conf_t  *scf = conf;\n\n    size_t       len;\n    ngx_str_t   *value, name, size;\n    ngx_int_t    n;\n    ngx_uint_t   i, j;\n\n    value = cf->args->elts;\n\n    for (i = 1; i < cf->args->nelts; i++) {\n\n        if (ngx_strcmp(value[i].data, \"off\") == 0) {\n            scf->builtin_session_cache = NGX_SSL_NO_SCACHE;\n            continue;\n        }\n\n        if (ngx_strcmp(value[i].data, \"none\") == 0) {\n            scf->builtin_session_cache = NGX_SSL_NONE_SCACHE;\n            continue;\n        }\n\n        if (ngx_strcmp(value[i].data, \"builtin\") == 0) {\n            scf->builtin_session_cache = NGX_SSL_DFLT_BUILTIN_SCACHE;\n            continue;\n        }\n\n        if (value[i].len > sizeof(\"builtin:\") - 1\n            && ngx_strncmp(value[i].data, \"builtin:\", sizeof(\"builtin:\") - 1)\n               == 0)\n        {\n            n = ngx_atoi(value[i].data + sizeof(\"builtin:\") - 1,\n                         value[i].len - (sizeof(\"builtin:\") - 1));\n\n            if (n == NGX_ERROR) {\n                goto invalid;\n            }\n\n            scf->builtin_session_cache = n;\n\n            continue;\n        }\n\n        if (value[i].len > sizeof(\"shared:\") - 1\n            && ngx_strncmp(value[i].data, \"shared:\", sizeof(\"shared:\") - 1)\n               == 0)\n        {\n            len = 0;\n\n            for (j = sizeof(\"shared:\") - 1; j < value[i].len; j++) {\n                if (value[i].data[j] == ':') {\n                    break;\n                }\n\n                len++;\n            }\n\n            if (len == 0 || j == value[i].len) {\n                goto invalid;\n            }\n\n            name.len = len;\n            name.data = value[i].data + sizeof(\"shared:\") - 1;\n\n            size.len = value[i].len - j - 1;\n            size.data = name.data + len + 1;\n\n            n = ngx_parse_size(&size);\n\n            if (n == NGX_ERROR) {\n                goto invalid;\n            }\n\n            if (n < (ngx_int_t) (8 * ngx_pagesize)) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"session cache \\\"%V\\\" is too small\",\n                                   &value[i]);\n\n                return NGX_CONF_ERROR;\n            }\n\n            scf->shm_zone = ngx_shared_memory_add(cf, &name, n,\n                                                   &ngx_mail_ssl_module);\n            if (scf->shm_zone == NULL) {\n                return NGX_CONF_ERROR;\n            }\n\n            scf->shm_zone->init = ngx_ssl_session_cache_init;\n\n            continue;\n        }\n\n        goto invalid;\n    }\n\n    if (scf->shm_zone && scf->builtin_session_cache == NGX_CONF_UNSET) {\n        scf->builtin_session_cache = NGX_SSL_NO_BUILTIN_SCACHE;\n    }\n\n    return NGX_CONF_OK;\n\ninvalid:\n\n    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                       \"invalid session cache \\\"%V\\\"\", &value[i]);\n\n    return NGX_CONF_ERROR;\n}\n\n\nstatic char *\nngx_mail_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#else\n    return NGX_CONF_OK;\n#endif\n}\n"
  },
  {
    "path": "src/mail/ngx_mail_ssl_module.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_MAIL_SSL_H_INCLUDED_\n#define _NGX_MAIL_SSL_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_mail.h>\n\n\n#define NGX_MAIL_STARTTLS_OFF   0\n#define NGX_MAIL_STARTTLS_ON    1\n#define NGX_MAIL_STARTTLS_ONLY  2\n\n\ntypedef struct {\n    ngx_flag_t       enable;\n    ngx_flag_t       prefer_server_ciphers;\n\n    ngx_ssl_t        ssl;\n\n    ngx_uint_t       starttls;\n    ngx_uint_t       listen;\n    ngx_uint_t       protocols;\n\n    ngx_uint_t       verify;\n    ngx_uint_t       verify_depth;\n\n    ssize_t          builtin_session_cache;\n\n    time_t           session_timeout;\n\n    ngx_array_t     *certificates;\n    ngx_array_t     *certificate_keys;\n\n    ngx_str_t        dhparam;\n    ngx_str_t        ecdh_curve;\n    ngx_str_t        client_certificate;\n    ngx_str_t        trusted_certificate;\n    ngx_str_t        crl;\n\n    ngx_str_t        ciphers;\n\n    ngx_array_t     *passwords;\n    ngx_array_t     *conf_commands;\n\n    ngx_shm_zone_t  *shm_zone;\n\n    ngx_flag_t       session_tickets;\n    ngx_array_t     *session_ticket_keys;\n\n    u_char          *file;\n    ngx_uint_t       line;\n} ngx_mail_ssl_conf_t;\n\n\nextern ngx_module_t  ngx_mail_ssl_module;\n\n\n#endif /* _NGX_MAIL_SSL_H_INCLUDED_ */\n"
  },
  {
    "path": "src/misc/ngx_cpp_test_module.cpp",
    "content": "\n// stub module to test header files' C++ compatibility\n\nextern \"C\" {\n  #include <ngx_config.h>\n  #include <ngx_core.h>\n  #include <ngx_event.h>\n  #include <ngx_event_connect.h>\n  #include <ngx_event_pipe.h>\n\n  #include <ngx_http.h>\n\n  #include <ngx_mail.h>\n  #include <ngx_mail_pop3_module.h>\n  #include <ngx_mail_imap_module.h>\n  #include <ngx_mail_smtp_module.h>\n\n  #include <ngx_stream.h>\n}\n\n// nginx header files should go before other, because they define 64-bit off_t\n// #include <string>\n\n\nvoid ngx_cpp_test_handler(void *data);\n\nvoid\nngx_cpp_test_handler(void *data)\n{\n    return;\n}\n"
  },
  {
    "path": "src/misc/ngx_google_perftools_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n/*\n * declare Profiler interface here because\n * <google/profiler.h> is C++ header file\n */\n\nint ProfilerStart(u_char* fname);\nvoid ProfilerStop(void);\nvoid ProfilerRegisterThread(void);\n\n\nstatic void *ngx_google_perftools_create_conf(ngx_cycle_t *cycle);\nstatic ngx_int_t ngx_google_perftools_worker(ngx_cycle_t *cycle);\n\n\ntypedef struct {\n    ngx_str_t  profiles;\n} ngx_google_perftools_conf_t;\n\n\nstatic ngx_command_t  ngx_google_perftools_commands[] = {\n\n    { ngx_string(\"google_perftools_profiles\"),\n      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_slot,\n      0,\n      offsetof(ngx_google_perftools_conf_t, profiles),\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_core_module_t  ngx_google_perftools_module_ctx = {\n    ngx_string(\"google_perftools\"),\n    ngx_google_perftools_create_conf,\n    NULL\n};\n\n\nngx_module_t  ngx_google_perftools_module = {\n    NGX_MODULE_V1,\n    &ngx_google_perftools_module_ctx,      /* module context */\n    ngx_google_perftools_commands,         /* module directives */\n    NGX_CORE_MODULE,                       /* module type */\n    NULL,                                  /* init master */\n    NULL,                                  /* init module */\n    ngx_google_perftools_worker,           /* 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 void *\nngx_google_perftools_create_conf(ngx_cycle_t *cycle)\n{\n    ngx_google_perftools_conf_t  *gptcf;\n\n    gptcf = ngx_pcalloc(cycle->pool, sizeof(ngx_google_perftools_conf_t));\n    if (gptcf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc()\n     *\n     *     gptcf->profiles = { 0, NULL };\n     */\n\n    return gptcf;\n}\n\n\nstatic ngx_int_t\nngx_google_perftools_worker(ngx_cycle_t *cycle)\n{\n    u_char                       *profile;\n    ngx_google_perftools_conf_t  *gptcf;\n\n    gptcf = (ngx_google_perftools_conf_t *)\n                ngx_get_conf(cycle->conf_ctx, ngx_google_perftools_module);\n\n    if (gptcf->profiles.len == 0) {\n        return NGX_OK;\n    }\n\n    profile = ngx_alloc(gptcf->profiles.len + NGX_INT_T_LEN + 2, cycle->log);\n    if (profile == NULL) {\n        return NGX_OK;\n    }\n\n    if (getenv(\"CPUPROFILE\")) {\n        /* disable inherited Profiler enabled in master process */\n        ProfilerStop();\n    }\n\n    ngx_sprintf(profile, \"%V.%d%Z\", &gptcf->profiles, ngx_pid);\n\n    if (ProfilerStart(profile)) {\n        /* start ITIMER_PROF timer */\n        ProfilerRegisterThread();\n\n    } else {\n        ngx_log_error(NGX_LOG_CRIT, cycle->log, ngx_errno,\n                      \"ProfilerStart(%s) failed\", profile);\n    }\n\n    ngx_free(profile);\n\n    return NGX_OK;\n}\n\n\n/* ProfilerStop() is called on Profiler destruction */\n"
  },
  {
    "path": "src/os/unix/ngx_aio_read.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n\n\nextern int  ngx_kqueue;\n\n\nssize_t\nngx_aio_read(ngx_connection_t *c, u_char *buf, size_t size)\n{\n    int           n;\n    ngx_event_t  *rev;\n\n    rev = c->read;\n\n    if (!rev->ready) {\n        ngx_log_error(NGX_LOG_ALERT, c->log, 0, \"second aio post\");\n        return NGX_AGAIN;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"rev->complete: %d\", rev->complete);\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"aio size: %d\", size);\n\n    if (!rev->complete) {\n        ngx_memzero(&rev->aiocb, sizeof(struct aiocb));\n\n        rev->aiocb.aio_fildes = c->fd;\n        rev->aiocb.aio_buf = buf;\n        rev->aiocb.aio_nbytes = size;\n\n#if (NGX_HAVE_KQUEUE)\n        rev->aiocb.aio_sigevent.sigev_notify_kqueue = ngx_kqueue;\n        rev->aiocb.aio_sigevent.sigev_notify = SIGEV_KEVENT;\n        rev->aiocb.aio_sigevent.sigev_value.sigval_ptr = rev;\n#endif\n\n        if (aio_read(&rev->aiocb) == -1) {\n            ngx_log_error(NGX_LOG_CRIT, rev->log, ngx_errno,\n                          \"aio_read() failed\");\n            rev->error = 1;\n            return NGX_ERROR;\n        }\n\n        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"aio_read: #%d OK\", c->fd);\n\n        rev->active = 1;\n        rev->ready = 0;\n    }\n\n    rev->complete = 0;\n\n    n = aio_error(&rev->aiocb);\n    if (n == -1) {\n        ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno, \"aio_error() failed\");\n        rev->error = 1;\n        return NGX_ERROR;\n    }\n\n    if (n != 0) {\n        if (n == NGX_EINPROGRESS) {\n            if (rev->ready) {\n                ngx_log_error(NGX_LOG_ALERT, c->log, n,\n                              \"aio_read() still in progress\");\n                rev->ready = 0;\n            }\n            return NGX_AGAIN;\n        }\n\n        ngx_log_error(NGX_LOG_CRIT, c->log, n, \"aio_read() failed\");\n        rev->error = 1;\n        rev->ready = 0;\n        return NGX_ERROR;\n    }\n\n    n = aio_return(&rev->aiocb);\n    if (n == -1) {\n        ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,\n                      \"aio_return() failed\");\n\n        rev->error = 1;\n        rev->ready = 0;\n        return NGX_ERROR;\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, rev->log, 0,\n                   \"aio_read: #%d %d\", c->fd, n);\n\n    if (n == 0) {\n        rev->eof = 1;\n        rev->ready = 0;\n    } else {\n        rev->ready = 1;\n    }\n\n    rev->active = 0;\n\n    return n;\n}\n"
  },
  {
    "path": "src/os/unix/ngx_aio_read_chain.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n\n\nssize_t\nngx_aio_read_chain(ngx_connection_t *c, ngx_chain_t *cl, off_t limit)\n{\n    int           n;\n    u_char       *buf, *prev;\n    size_t        size;\n    ssize_t       total;\n\n    if (c->read->pending_eof) {\n        c->read->ready = 0;\n        return 0;\n    }\n\n    total = 0;\n\n    while (cl) {\n\n        /* we can post the single aio operation only */\n\n        if (!c->read->ready) {\n            return total ? total : NGX_AGAIN;\n        }\n\n        buf = cl->buf->last;\n        prev = cl->buf->last;\n        size = 0;\n\n        /* coalesce the neighbouring bufs */\n\n        while (cl && prev == cl->buf->last) {\n            size += cl->buf->end - cl->buf->last;\n            prev = cl->buf->end;\n            cl = cl->next;\n        }\n\n        n = ngx_aio_read(c, buf, size);\n\n        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, \"aio_read: %d\", n);\n\n        if (n == NGX_AGAIN) {\n            return total ? total : NGX_AGAIN;\n        }\n\n        if (n == NGX_ERROR) {\n            return NGX_ERROR;\n        }\n\n        if (n == 0) {\n            c->read->pending_eof = 1;\n            if (total) {\n                c->read->eof = 0;\n                c->read->ready = 1;\n            }\n            return total;\n        }\n\n        if (n > 0) {\n            total += n;\n        }\n\n        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"aio_read total: %d\", total);\n    }\n\n    return total ? total : NGX_AGAIN;\n}\n"
  },
  {
    "path": "src/os/unix/ngx_aio_write.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n\n\nextern int  ngx_kqueue;\n\n\nssize_t\nngx_aio_write(ngx_connection_t *c, u_char *buf, size_t size)\n{\n    int           n;\n    ngx_event_t  *wev;\n\n    wev = c->write;\n\n    if (!wev->ready) {\n        return NGX_AGAIN;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, wev->log, 0,\n                   \"aio: wev->complete: %d\", wev->complete);\n\n    if (!wev->complete) {\n        ngx_memzero(&wev->aiocb, sizeof(struct aiocb));\n\n        wev->aiocb.aio_fildes = c->fd;\n        wev->aiocb.aio_buf = buf;\n        wev->aiocb.aio_nbytes = size;\n\n#if (NGX_HAVE_KQUEUE)\n        wev->aiocb.aio_sigevent.sigev_notify_kqueue = ngx_kqueue;\n        wev->aiocb.aio_sigevent.sigev_notify = SIGEV_KEVENT;\n        wev->aiocb.aio_sigevent.sigev_value.sigval_ptr = wev;\n#endif\n\n        if (aio_write(&wev->aiocb) == -1) {\n            ngx_log_error(NGX_LOG_CRIT, wev->log, ngx_errno,\n                          \"aio_write() failed\");\n            return NGX_ERROR;\n        }\n\n        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, wev->log, 0, \"aio_write: OK\");\n\n        wev->active = 1;\n        wev->ready = 0;\n    }\n\n    wev->complete = 0;\n\n    n = aio_error(&wev->aiocb);\n    if (n == -1) {\n        ngx_log_error(NGX_LOG_CRIT, wev->log, ngx_errno, \"aio_error() failed\");\n        wev->error = 1;\n        return NGX_ERROR;\n    }\n\n    if (n != 0) {\n        if (n == NGX_EINPROGRESS) {\n            if (wev->ready) {\n                ngx_log_error(NGX_LOG_ALERT, wev->log, n,\n                              \"aio_write() still in progress\");\n                wev->ready = 0;\n            }\n            return NGX_AGAIN;\n        }\n\n        ngx_log_error(NGX_LOG_CRIT, wev->log, n, \"aio_write() failed\");\n        wev->error = 1;\n        wev->ready = 0;\n\n#if 1\n        n = aio_return(&wev->aiocb);\n        if (n == -1) {\n            ngx_log_error(NGX_LOG_ALERT, wev->log, ngx_errno,\n                          \"aio_return() failed\");\n        }\n\n        ngx_log_error(NGX_LOG_CRIT, wev->log, n, \"aio_return() %d\", n);\n#endif\n\n        return NGX_ERROR;\n    }\n\n    n = aio_return(&wev->aiocb);\n    if (n == -1) {\n        ngx_log_error(NGX_LOG_ALERT, wev->log, ngx_errno,\n                      \"aio_return() failed\");\n\n        wev->error = 1;\n        wev->ready = 0;\n        return NGX_ERROR;\n    }\n\n\n    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, wev->log, 0, \"aio_write: %d\", n);\n\n    wev->active = 0;\n    wev->ready = 1;\n\n    return n;\n}\n"
  },
  {
    "path": "src/os/unix/ngx_aio_write_chain.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n\n\nngx_chain_t *\nngx_aio_write_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)\n{\n    u_char       *buf, *prev;\n    off_t         send, sent;\n    size_t        len;\n    ssize_t       n, size;\n    ngx_chain_t  *cl;\n\n    /* the maximum limit size is the maximum size_t value - the page size */\n\n    if (limit == 0 || limit > (off_t) (NGX_MAX_SIZE_T_VALUE - ngx_pagesize)) {\n        limit = NGX_MAX_SIZE_T_VALUE - ngx_pagesize;\n    }\n\n    send = 0;\n    sent = 0;\n    cl = in;\n\n    while (cl) {\n\n        if (cl->buf->pos == cl->buf->last) {\n            cl = cl->next;\n            continue;\n        }\n\n        /* we can post the single aio operation only */\n\n        if (!c->write->ready) {\n            return cl;\n        }\n\n        buf = cl->buf->pos;\n        prev = buf;\n        len = 0;\n\n        /* coalesce the neighbouring bufs */\n\n        while (cl && prev == cl->buf->pos && send < limit) {\n            if (ngx_buf_special(cl->buf)) {\n                continue;\n            }\n\n            size = cl->buf->last - cl->buf->pos;\n\n            if (send + size > limit) {\n                size = limit - send;\n            }\n\n            len += size;\n            prev = cl->buf->pos + size;\n            send += size;\n            cl = cl->next;\n        }\n\n        n = ngx_aio_write(c, buf, len);\n\n        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, \"aio_write: %z\", n);\n\n        if (n == NGX_ERROR) {\n            return NGX_CHAIN_ERROR;\n        }\n\n        if (n > 0) {\n            sent += n;\n            c->sent += n;\n        }\n\n        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"aio_write sent: %O\", c->sent);\n\n        for (cl = in; cl; cl = cl->next) {\n\n            if (sent >= cl->buf->last - cl->buf->pos) {\n                sent -= cl->buf->last - cl->buf->pos;\n                cl->buf->pos = cl->buf->last;\n\n                continue;\n            }\n\n            cl->buf->pos += sent;\n\n            break;\n        }\n    }\n\n    return cl;\n}\n"
  },
  {
    "path": "src/os/unix/ngx_alloc.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\nngx_uint_t  ngx_pagesize;\nngx_uint_t  ngx_pagesize_shift;\nngx_uint_t  ngx_cacheline_size;\n\n\nvoid *\nngx_alloc(size_t size, ngx_log_t *log)\n{\n    void  *p;\n\n    p = malloc(size);\n    if (p == NULL) {\n        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,\n                      \"malloc(%uz) failed\", size);\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, log, 0, \"malloc: %p:%uz\", p, size);\n\n    return p;\n}\n\n\nvoid *\nngx_calloc(size_t size, ngx_log_t *log)\n{\n    void  *p;\n\n    p = ngx_alloc(size, log);\n\n    if (p) {\n        ngx_memzero(p, size);\n    }\n\n    return p;\n}\n\n\n#if (T_DEPRECATED)\nvoid *\nngx_realloc(void *p, size_t size, ngx_log_t *log)\n{\n    void *new;\n\n    new = realloc(p, size);\n    if (new == NULL) {\n        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,\n                      \"realloc(%p:%uz) failed\", p, size);\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_ALLOC, log, 0, \"realloc: %p:%uz\", new, size);\n\n    return new;\n}\n#endif\n\n\n#if (NGX_HAVE_POSIX_MEMALIGN)\n\nvoid *\nngx_memalign(size_t alignment, size_t size, ngx_log_t *log)\n{\n    void  *p;\n    int    err;\n\n    err = posix_memalign(&p, alignment, size);\n\n    if (err) {\n        ngx_log_error(NGX_LOG_EMERG, log, err,\n                      \"posix_memalign(%uz, %uz) failed\", alignment, size);\n        p = NULL;\n    }\n\n    ngx_log_debug3(NGX_LOG_DEBUG_ALLOC, log, 0,\n                   \"posix_memalign: %p:%uz @%uz\", p, size, alignment);\n\n    return p;\n}\n\n#elif (NGX_HAVE_MEMALIGN)\n\nvoid *\nngx_memalign(size_t alignment, size_t size, ngx_log_t *log)\n{\n    void  *p;\n\n    p = memalign(alignment, size);\n    if (p == NULL) {\n        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,\n                      \"memalign(%uz, %uz) failed\", alignment, size);\n    }\n\n    ngx_log_debug3(NGX_LOG_DEBUG_ALLOC, log, 0,\n                   \"memalign: %p:%uz @%uz\", p, size, alignment);\n\n    return p;\n}\n\n#endif\n"
  },
  {
    "path": "src/os/unix/ngx_alloc.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_ALLOC_H_INCLUDED_\n#define _NGX_ALLOC_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\nvoid *ngx_alloc(size_t size, ngx_log_t *log);\nvoid *ngx_calloc(size_t size, ngx_log_t *log);\n#if (T_DEPRECATED)\nvoid *ngx_realloc(void *p, size_t size, ngx_log_t *log);\n#endif\n\n#define ngx_free          free\n\n#if (NGX_JEMALLOC)\n\n#define NGX_HAVE_POSIX_MEMALIGN 1\n#define NGX_HAVE_MEMALIGN 1\n\n#endif /* (NGX_JEMALLOC) */\n\n/*\n * Linux has memalign() or posix_memalign()\n * Solaris has memalign()\n * FreeBSD 7.0 has posix_memalign(), besides, early version's malloc()\n * aligns allocations bigger than page size at the page boundary\n */\n\n#if (NGX_HAVE_POSIX_MEMALIGN || NGX_HAVE_MEMALIGN)\n\nvoid *ngx_memalign(size_t alignment, size_t size, ngx_log_t *log);\n\n#else\n\n#define ngx_memalign(alignment, size, log)  ngx_alloc(size, log)\n\n#endif\n\n\nextern ngx_uint_t  ngx_pagesize;\nextern ngx_uint_t  ngx_pagesize_shift;\nextern ngx_uint_t  ngx_cacheline_size;\n\n\n#endif /* _NGX_ALLOC_H_INCLUDED_ */\n"
  },
  {
    "path": "src/os/unix/ngx_atomic.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_ATOMIC_H_INCLUDED_\n#define _NGX_ATOMIC_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\n#if (NGX_HAVE_LIBATOMIC)\n\n#define AO_REQUIRE_CAS\n#include <atomic_ops.h>\n\n#define NGX_HAVE_ATOMIC_OPS  1\n\ntypedef long                        ngx_atomic_int_t;\ntypedef AO_t                        ngx_atomic_uint_t;\ntypedef volatile ngx_atomic_uint_t  ngx_atomic_t;\n\n#if (NGX_PTR_SIZE == 8)\n#define NGX_ATOMIC_T_LEN            (sizeof(\"-9223372036854775808\") - 1)\n#else\n#define NGX_ATOMIC_T_LEN            (sizeof(\"-2147483648\") - 1)\n#endif\n\n#define ngx_atomic_cmp_set(lock, old, new)                                    \\\n    AO_compare_and_swap(lock, old, new)\n#define ngx_atomic_fetch_add(value, add)                                      \\\n    AO_fetch_and_add(value, add)\n#define ngx_memory_barrier()        AO_nop()\n#define ngx_cpu_pause()\n\n\n#elif (NGX_HAVE_GCC_ATOMIC)\n\n/* GCC 4.1 builtin atomic operations */\n\n#define NGX_HAVE_ATOMIC_OPS  1\n\ntypedef long                        ngx_atomic_int_t;\ntypedef unsigned long               ngx_atomic_uint_t;\n\n#if (NGX_PTR_SIZE == 8)\n#define NGX_ATOMIC_T_LEN            (sizeof(\"-9223372036854775808\") - 1)\n#else\n#define NGX_ATOMIC_T_LEN            (sizeof(\"-2147483648\") - 1)\n#endif\n\ntypedef volatile ngx_atomic_uint_t  ngx_atomic_t;\n\n\n#define ngx_atomic_cmp_set(lock, old, set)                                    \\\n    __sync_bool_compare_and_swap(lock, old, set)\n\n#define ngx_atomic_fetch_add(value, add)                                      \\\n    __sync_fetch_and_add(value, add)\n\n#define ngx_memory_barrier()        __sync_synchronize()\n\n#if ( __i386__ || __i386 || __amd64__ || __amd64 )\n#define ngx_cpu_pause()             __asm__ (\"pause\")\n#else\n#define ngx_cpu_pause()\n#endif\n\n\n#elif (NGX_DARWIN_ATOMIC)\n\n/*\n * use Darwin 8 atomic(3) and barrier(3) operations\n * optimized at run-time for UP and SMP\n */\n\n#include <libkern/OSAtomic.h>\n\n/* \"bool\" conflicts with perl's CORE/handy.h */\n#if 0\n#undef bool\n#endif\n\n\n#define NGX_HAVE_ATOMIC_OPS  1\n\n#if (NGX_PTR_SIZE == 8)\n\ntypedef int64_t                     ngx_atomic_int_t;\ntypedef uint64_t                    ngx_atomic_uint_t;\n#define NGX_ATOMIC_T_LEN            (sizeof(\"-9223372036854775808\") - 1)\n\n#define ngx_atomic_cmp_set(lock, old, new)                                    \\\n    OSAtomicCompareAndSwap64Barrier(old, new, (int64_t *) lock)\n\n#define ngx_atomic_fetch_add(value, add)                                      \\\n    (OSAtomicAdd64(add, (int64_t *) value) - add)\n\n#else\n\ntypedef int32_t                     ngx_atomic_int_t;\ntypedef uint32_t                    ngx_atomic_uint_t;\n#define NGX_ATOMIC_T_LEN            (sizeof(\"-2147483648\") - 1)\n\n#define ngx_atomic_cmp_set(lock, old, new)                                    \\\n    OSAtomicCompareAndSwap32Barrier(old, new, (int32_t *) lock)\n\n#define ngx_atomic_fetch_add(value, add)                                      \\\n    (OSAtomicAdd32(add, (int32_t *) value) - add)\n\n#endif\n\n#define ngx_memory_barrier()        OSMemoryBarrier()\n\n#define ngx_cpu_pause()\n\ntypedef volatile ngx_atomic_uint_t  ngx_atomic_t;\n\n\n#elif ( __i386__ || __i386 )\n\ntypedef int32_t                     ngx_atomic_int_t;\ntypedef uint32_t                    ngx_atomic_uint_t;\ntypedef volatile ngx_atomic_uint_t  ngx_atomic_t;\n#define NGX_ATOMIC_T_LEN            (sizeof(\"-2147483648\") - 1)\n\n\n#if ( __SUNPRO_C )\n\n#define NGX_HAVE_ATOMIC_OPS  1\n\nngx_atomic_uint_t\nngx_atomic_cmp_set(ngx_atomic_t *lock, ngx_atomic_uint_t old,\n    ngx_atomic_uint_t set);\n\nngx_atomic_int_t\nngx_atomic_fetch_add(ngx_atomic_t *value, ngx_atomic_int_t add);\n\n/*\n * Sun Studio 12 exits with segmentation fault on '__asm (\"pause\")',\n * so ngx_cpu_pause is declared in src/os/unix/ngx_sunpro_x86.il\n */\n\nvoid\nngx_cpu_pause(void);\n\n/* the code in src/os/unix/ngx_sunpro_x86.il */\n\n#define ngx_memory_barrier()        __asm (\".volatile\"); __asm (\".nonvolatile\")\n\n\n#else /* ( __GNUC__ || __INTEL_COMPILER ) */\n\n#define NGX_HAVE_ATOMIC_OPS  1\n\n#include \"ngx_gcc_atomic_x86.h\"\n\n#endif\n\n\n#elif ( __amd64__ || __amd64 )\n\ntypedef int64_t                     ngx_atomic_int_t;\ntypedef uint64_t                    ngx_atomic_uint_t;\ntypedef volatile ngx_atomic_uint_t  ngx_atomic_t;\n#define NGX_ATOMIC_T_LEN            (sizeof(\"-9223372036854775808\") - 1)\n\n\n#if ( __SUNPRO_C )\n\n#define NGX_HAVE_ATOMIC_OPS  1\n\nngx_atomic_uint_t\nngx_atomic_cmp_set(ngx_atomic_t *lock, ngx_atomic_uint_t old,\n    ngx_atomic_uint_t set);\n\nngx_atomic_int_t\nngx_atomic_fetch_add(ngx_atomic_t *value, ngx_atomic_int_t add);\n\n/*\n * Sun Studio 12 exits with segmentation fault on '__asm (\"pause\")',\n * so ngx_cpu_pause is declared in src/os/unix/ngx_sunpro_amd64.il\n */\n\nvoid\nngx_cpu_pause(void);\n\n/* the code in src/os/unix/ngx_sunpro_amd64.il */\n\n#define ngx_memory_barrier()        __asm (\".volatile\"); __asm (\".nonvolatile\")\n\n\n#else /* ( __GNUC__ || __INTEL_COMPILER ) */\n\n#define NGX_HAVE_ATOMIC_OPS  1\n\n#include \"ngx_gcc_atomic_amd64.h\"\n\n#endif\n\n\n#elif ( __sparc__ || __sparc || __sparcv9 )\n\n#if (NGX_PTR_SIZE == 8)\n\ntypedef int64_t                     ngx_atomic_int_t;\ntypedef uint64_t                    ngx_atomic_uint_t;\n#define NGX_ATOMIC_T_LEN            (sizeof(\"-9223372036854775808\") - 1)\n\n#else\n\ntypedef int32_t                     ngx_atomic_int_t;\ntypedef uint32_t                    ngx_atomic_uint_t;\n#define NGX_ATOMIC_T_LEN            (sizeof(\"-2147483648\") - 1)\n\n#endif\n\ntypedef volatile ngx_atomic_uint_t  ngx_atomic_t;\n\n\n#if ( __SUNPRO_C )\n\n#define NGX_HAVE_ATOMIC_OPS  1\n\n#include \"ngx_sunpro_atomic_sparc64.h\"\n\n\n#else /* ( __GNUC__ || __INTEL_COMPILER ) */\n\n#define NGX_HAVE_ATOMIC_OPS  1\n\n#include \"ngx_gcc_atomic_sparc64.h\"\n\n#endif\n\n\n#elif ( __powerpc__ || __POWERPC__ )\n\n#define NGX_HAVE_ATOMIC_OPS  1\n\n#if (NGX_PTR_SIZE == 8)\n\ntypedef int64_t                     ngx_atomic_int_t;\ntypedef uint64_t                    ngx_atomic_uint_t;\n#define NGX_ATOMIC_T_LEN            (sizeof(\"-9223372036854775808\") - 1)\n\n#else\n\ntypedef int32_t                     ngx_atomic_int_t;\ntypedef uint32_t                    ngx_atomic_uint_t;\n#define NGX_ATOMIC_T_LEN            (sizeof(\"-2147483648\") - 1)\n\n#endif\n\ntypedef volatile ngx_atomic_uint_t  ngx_atomic_t;\n\n\n#include \"ngx_gcc_atomic_ppc.h\"\n\n#endif\n\n\n#if !(NGX_HAVE_ATOMIC_OPS)\n\n#define NGX_HAVE_ATOMIC_OPS  0\n\ntypedef int32_t                     ngx_atomic_int_t;\ntypedef uint32_t                    ngx_atomic_uint_t;\ntypedef volatile ngx_atomic_uint_t  ngx_atomic_t;\n#define NGX_ATOMIC_T_LEN            (sizeof(\"-2147483648\") - 1)\n\n\nstatic ngx_inline ngx_atomic_uint_t\nngx_atomic_cmp_set(ngx_atomic_t *lock, ngx_atomic_uint_t old,\n    ngx_atomic_uint_t set)\n{\n    if (*lock == old) {\n        *lock = set;\n        return 1;\n    }\n\n    return 0;\n}\n\n\nstatic ngx_inline ngx_atomic_int_t\nngx_atomic_fetch_add(ngx_atomic_t *value, ngx_atomic_int_t add)\n{\n    ngx_atomic_int_t  old;\n\n    old = *value;\n    *value += add;\n\n    return old;\n}\n\n#define ngx_memory_barrier()\n#define ngx_cpu_pause()\n\n#endif\n\n\nvoid ngx_spinlock(ngx_atomic_t *lock, ngx_atomic_int_t value, ngx_uint_t spin);\n\n#define ngx_trylock(lock)  (*(lock) == 0 && ngx_atomic_cmp_set(lock, 0, 1))\n#define ngx_unlock(lock)    *(lock) = 0\n\n\n#endif /* _NGX_ATOMIC_H_INCLUDED_ */\n"
  },
  {
    "path": "src/os/unix/ngx_channel.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_channel.h>\n\n\nngx_int_t\nngx_write_channel(ngx_socket_t s, ngx_channel_t *ch, size_t size,\n    ngx_log_t *log)\n{\n    ssize_t             n;\n    ngx_err_t           err;\n    struct iovec        iov[1];\n    struct msghdr       msg;\n\n#if (NGX_HAVE_MSGHDR_MSG_CONTROL)\n\n    union {\n        struct cmsghdr  cm;\n        char            space[CMSG_SPACE(sizeof(int))];\n    } cmsg;\n\n    if (ch->fd == -1) {\n        msg.msg_control = NULL;\n        msg.msg_controllen = 0;\n\n    } else {\n        msg.msg_control = (caddr_t) &cmsg;\n        msg.msg_controllen = sizeof(cmsg);\n\n        ngx_memzero(&cmsg, sizeof(cmsg));\n\n        cmsg.cm.cmsg_len = CMSG_LEN(sizeof(int));\n        cmsg.cm.cmsg_level = SOL_SOCKET;\n        cmsg.cm.cmsg_type = SCM_RIGHTS;\n\n        /*\n         * We have to use ngx_memcpy() instead of simple\n         *   *(int *) CMSG_DATA(&cmsg.cm) = ch->fd;\n         * because some gcc 4.4 with -O2/3/s optimization issues the warning:\n         *   dereferencing type-punned pointer will break strict-aliasing rules\n         *\n         * Fortunately, gcc with -O1 compiles this ngx_memcpy()\n         * in the same simple assignment as in the code above\n         */\n\n        ngx_memcpy(CMSG_DATA(&cmsg.cm), &ch->fd, sizeof(int));\n    }\n\n    msg.msg_flags = 0;\n\n#else\n\n    if (ch->fd == -1) {\n        msg.msg_accrights = NULL;\n        msg.msg_accrightslen = 0;\n\n    } else {\n        msg.msg_accrights = (caddr_t) &ch->fd;\n        msg.msg_accrightslen = sizeof(int);\n    }\n\n#endif\n\n    iov[0].iov_base = (char *) ch;\n    iov[0].iov_len = size;\n\n    msg.msg_name = NULL;\n    msg.msg_namelen = 0;\n    msg.msg_iov = iov;\n    msg.msg_iovlen = 1;\n\n    n = sendmsg(s, &msg, 0);\n\n    if (n == -1) {\n        err = ngx_errno;\n        if (err == NGX_EAGAIN) {\n            return NGX_AGAIN;\n        }\n\n        ngx_log_error(NGX_LOG_ALERT, log, err, \"sendmsg() failed\");\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_read_channel(ngx_socket_t s, ngx_channel_t *ch, size_t size, ngx_log_t *log)\n{\n    ssize_t             n;\n    ngx_err_t           err;\n    struct iovec        iov[1];\n    struct msghdr       msg;\n\n#if (NGX_HAVE_MSGHDR_MSG_CONTROL)\n    union {\n        struct cmsghdr  cm;\n        char            space[CMSG_SPACE(sizeof(int))];\n    } cmsg;\n#else\n    int                 fd;\n#endif\n\n    iov[0].iov_base = (char *) ch;\n    iov[0].iov_len = size;\n\n    msg.msg_name = NULL;\n    msg.msg_namelen = 0;\n    msg.msg_iov = iov;\n    msg.msg_iovlen = 1;\n\n#if (NGX_HAVE_MSGHDR_MSG_CONTROL)\n    msg.msg_control = (caddr_t) &cmsg;\n    msg.msg_controllen = sizeof(cmsg);\n#else\n    msg.msg_accrights = (caddr_t) &fd;\n    msg.msg_accrightslen = sizeof(int);\n#endif\n\n    n = recvmsg(s, &msg, 0);\n\n    if (n == -1) {\n        err = ngx_errno;\n        if (err == NGX_EAGAIN) {\n            return NGX_AGAIN;\n        }\n\n        ngx_log_error(NGX_LOG_ALERT, log, err, \"recvmsg() failed\");\n        return NGX_ERROR;\n    }\n\n    if (n == 0) {\n        ngx_log_debug0(NGX_LOG_DEBUG_CORE, log, 0, \"recvmsg() returned zero\");\n        return NGX_ERROR;\n    }\n\n    if ((size_t) n < sizeof(ngx_channel_t)) {\n        ngx_log_error(NGX_LOG_ALERT, log, 0,\n                      \"recvmsg() returned not enough data: %z\", n);\n        return NGX_ERROR;\n    }\n\n#if (NGX_HAVE_MSGHDR_MSG_CONTROL)\n\n    if (ch->command == NGX_CMD_OPEN_CHANNEL) {\n\n        if (cmsg.cm.cmsg_len < (socklen_t) CMSG_LEN(sizeof(int))) {\n            ngx_log_error(NGX_LOG_ALERT, log, 0,\n                          \"recvmsg() returned too small ancillary data\");\n            return NGX_ERROR;\n        }\n\n        if (cmsg.cm.cmsg_level != SOL_SOCKET || cmsg.cm.cmsg_type != SCM_RIGHTS)\n        {\n            ngx_log_error(NGX_LOG_ALERT, log, 0,\n                          \"recvmsg() returned invalid ancillary data \"\n                          \"level %d or type %d\",\n                          cmsg.cm.cmsg_level, cmsg.cm.cmsg_type);\n            return NGX_ERROR;\n        }\n\n        /* ch->fd = *(int *) CMSG_DATA(&cmsg.cm); */\n\n        ngx_memcpy(&ch->fd, CMSG_DATA(&cmsg.cm), sizeof(int));\n    }\n\n    if (msg.msg_flags & (MSG_TRUNC|MSG_CTRUNC)) {\n        ngx_log_error(NGX_LOG_ALERT, log, 0,\n                      \"recvmsg() truncated data\");\n    }\n\n#else\n\n    if (ch->command == NGX_CMD_OPEN_CHANNEL) {\n        if (msg.msg_accrightslen != sizeof(int)) {\n            ngx_log_error(NGX_LOG_ALERT, log, 0,\n                          \"recvmsg() returned no ancillary data\");\n            return NGX_ERROR;\n        }\n\n        ch->fd = fd;\n    }\n\n#endif\n\n    return n;\n}\n\n\nngx_int_t\nngx_add_channel_event(ngx_cycle_t *cycle, ngx_fd_t fd, ngx_int_t event,\n    ngx_event_handler_pt handler)\n{\n    ngx_event_t       *ev, *rev, *wev;\n    ngx_connection_t  *c;\n\n    c = ngx_get_connection(fd, cycle->log);\n\n    if (c == NULL) {\n        return NGX_ERROR;\n    }\n\n    c->pool = cycle->pool;\n\n    rev = c->read;\n    wev = c->write;\n\n    rev->log = cycle->log;\n    wev->log = cycle->log;\n\n    rev->channel = 1;\n    wev->channel = 1;\n\n    ev = (event == NGX_READ_EVENT) ? rev : wev;\n\n    ev->handler = handler;\n\n    if (ngx_add_conn && (ngx_event_flags & NGX_USE_EPOLL_EVENT) == 0) {\n        if (ngx_add_conn(c) == NGX_ERROR) {\n            ngx_free_connection(c);\n            return NGX_ERROR;\n        }\n\n    } else {\n        if (ngx_add_event(ev, event, 0) == NGX_ERROR) {\n            ngx_free_connection(c);\n            return NGX_ERROR;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nvoid\nngx_close_channel(ngx_fd_t *fd, ngx_log_t *log)\n{\n    if (close(fd[0]) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, \"close() channel failed\");\n    }\n\n    if (close(fd[1]) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, \"close() channel failed\");\n    }\n}\n"
  },
  {
    "path": "src/os/unix/ngx_channel.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_CHANNEL_H_INCLUDED_\n#define _NGX_CHANNEL_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n\n\ntypedef struct {\n    ngx_uint_t  command;\n    ngx_pid_t   pid;\n    ngx_int_t   slot;\n    ngx_fd_t    fd;\n} ngx_channel_t;\n\n\nngx_int_t ngx_write_channel(ngx_socket_t s, ngx_channel_t *ch, size_t size,\n    ngx_log_t *log);\nngx_int_t ngx_read_channel(ngx_socket_t s, ngx_channel_t *ch, size_t size,\n    ngx_log_t *log);\nngx_int_t ngx_add_channel_event(ngx_cycle_t *cycle, ngx_fd_t fd,\n    ngx_int_t event, ngx_event_handler_pt handler);\nvoid ngx_close_channel(ngx_fd_t *fd, ngx_log_t *log);\n\n\n#endif /* _NGX_CHANNEL_H_INCLUDED_ */\n"
  },
  {
    "path": "src/os/unix/ngx_daemon.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\nngx_int_t\nngx_daemon(ngx_log_t *log)\n{\n    int  fd;\n\n    switch (fork()) {\n    case -1:\n        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, \"fork() failed\");\n        return NGX_ERROR;\n\n    case 0:\n        break;\n\n    default:\n        exit(0);\n    }\n\n    ngx_parent = ngx_pid;\n    ngx_pid = ngx_getpid();\n\n    if (setsid() == -1) {\n        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, \"setsid() failed\");\n        return NGX_ERROR;\n    }\n\n    umask(0);\n\n    fd = open(\"/dev/null\", O_RDWR);\n    if (fd == -1) {\n        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,\n                      \"open(\\\"/dev/null\\\") failed\");\n        return NGX_ERROR;\n    }\n\n    if (dup2(fd, STDIN_FILENO) == -1) {\n        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, \"dup2(STDIN) failed\");\n        return NGX_ERROR;\n    }\n\n    if (dup2(fd, STDOUT_FILENO) == -1) {\n        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, \"dup2(STDOUT) failed\");\n        return NGX_ERROR;\n    }\n\n#if 0\n    if (dup2(fd, STDERR_FILENO) == -1) {\n        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, \"dup2(STDERR) failed\");\n        return NGX_ERROR;\n    }\n#endif\n\n    if (fd > STDERR_FILENO) {\n        if (close(fd) == -1) {\n            ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, \"close() failed\");\n            return NGX_ERROR;\n        }\n    }\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "src/os/unix/ngx_darwin.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_DARWIN_H_INCLUDED_\n#define _NGX_DARWIN_H_INCLUDED_\n\n\nvoid ngx_debug_init(void);\nngx_chain_t *ngx_darwin_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in,\n    off_t limit);\n\nextern int       ngx_darwin_kern_osreldate;\nextern int       ngx_darwin_hw_ncpu;\nextern u_long    ngx_darwin_net_inet_tcp_sendspace;\n\nextern ngx_uint_t  ngx_debug_malloc;\n\n\n#endif /* _NGX_DARWIN_H_INCLUDED_ */\n"
  },
  {
    "path": "src/os/unix/ngx_darwin_config.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_DARWIN_CONFIG_H_INCLUDED_\n#define _NGX_DARWIN_CONFIG_H_INCLUDED_\n\n\n#define __APPLE_USE_RFC_3542    /* IPV6_PKTINFO */\n\n\n#include <sys/types.h>\n#include <sys/time.h>\n#include <unistd.h>\n#include <inttypes.h>\n#include <stdarg.h>\n#include <stddef.h>             /* offsetof() */\n#include <stdio.h>\n#include <stdlib.h>\n#include <ctype.h>\n#include <errno.h>\n#include <string.h>\n#include <signal.h>\n#include <pwd.h>\n#include <grp.h>\n#include <dirent.h>\n#include <glob.h>\n#include <sys/mount.h>          /* statfs() */\n\n#include <sys/filio.h>          /* FIONBIO */\n#include <sys/ioctl.h>\n#include <sys/uio.h>\n#include <sys/stat.h>\n#include <fcntl.h>\n\n#include <sys/wait.h>\n#include <sys/mman.h>\n#include <sys/resource.h>\n#include <sched.h>\n\n#include <sys/socket.h>\n#include <netinet/in.h>\n#include <netinet/tcp.h>        /* TCP_NODELAY */\n#include <arpa/inet.h>\n#include <netdb.h>\n#include <sys/un.h>\n\n#include <sys/sysctl.h>\n#include <xlocale.h>\n\n#include <dlfcn.h>\n\n\n#ifndef IOV_MAX\n#define IOV_MAX   64\n#endif\n\n\n#include <ngx_auto_config.h>\n\n\n#if (NGX_HAVE_POSIX_SEM)\n#include <semaphore.h>\n#endif\n\n\n#if (NGX_HAVE_POLL)\n#include <poll.h>\n#endif\n\n\n#if (NGX_HAVE_KQUEUE)\n#include <sys/event.h>\n#endif\n\n\n#define NGX_LISTEN_BACKLOG  -1\n\n\n#ifndef NGX_HAVE_INHERITED_NONBLOCK\n#define NGX_HAVE_INHERITED_NONBLOCK  1\n#endif\n\n\n#ifndef NGX_HAVE_CASELESS_FILESYSTEM\n#define NGX_HAVE_CASELESS_FILESYSTEM  1\n#endif\n\n\n#define NGX_HAVE_OS_SPECIFIC_INIT    1\n#define NGX_HAVE_DEBUG_MALLOC        1\n\n\nextern char **environ;\n\n\n#endif /* _NGX_DARWIN_CONFIG_H_INCLUDED_ */\n"
  },
  {
    "path": "src/os/unix/ngx_darwin_init.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\nchar    ngx_darwin_kern_ostype[16];\nchar    ngx_darwin_kern_osrelease[128];\nint     ngx_darwin_hw_ncpu;\nint     ngx_darwin_kern_ipc_somaxconn;\nu_long  ngx_darwin_net_inet_tcp_sendspace;\n\nngx_uint_t  ngx_debug_malloc;\n\n\nstatic ngx_os_io_t ngx_darwin_io = {\n    ngx_unix_recv,\n    ngx_readv_chain,\n    ngx_udp_unix_recv,\n    ngx_unix_send,\n    ngx_udp_unix_send,\n    ngx_udp_unix_sendmsg_chain,\n#if (NGX_HAVE_SENDFILE)\n    ngx_darwin_sendfile_chain,\n    NGX_IO_SENDFILE\n#else\n    ngx_writev_chain,\n    0\n#endif\n};\n\n\ntypedef struct {\n    char        *name;\n    void        *value;\n    size_t       size;\n    ngx_uint_t   exists;\n} sysctl_t;\n\n\nsysctl_t sysctls[] = {\n    { \"hw.ncpu\",\n      &ngx_darwin_hw_ncpu,\n      sizeof(ngx_darwin_hw_ncpu), 0 },\n\n    { \"net.inet.tcp.sendspace\",\n      &ngx_darwin_net_inet_tcp_sendspace,\n      sizeof(ngx_darwin_net_inet_tcp_sendspace), 0 },\n\n    { \"kern.ipc.somaxconn\",\n      &ngx_darwin_kern_ipc_somaxconn,\n      sizeof(ngx_darwin_kern_ipc_somaxconn), 0 },\n\n    { NULL, NULL, 0, 0 }\n};\n\n\nvoid\nngx_debug_init(void)\n{\n#if (NGX_DEBUG_MALLOC)\n\n    /*\n     * MacOSX 10.6, 10.7:  MallocScribble fills freed memory with 0x55\n     *                     and fills allocated memory with 0xAA.\n     * MacOSX 10.4, 10.5:  MallocScribble fills freed memory with 0x55,\n     *                     MallocPreScribble fills allocated memory with 0xAA.\n     * MacOSX 10.3:        MallocScribble fills freed memory with 0x55,\n     *                     and no way to fill allocated memory.\n     */\n\n    setenv(\"MallocScribble\", \"1\", 0);\n\n    ngx_debug_malloc = 1;\n\n#else\n\n    if (getenv(\"MallocScribble\")) {\n        ngx_debug_malloc = 1;\n    }\n\n#endif\n}\n\n\nngx_int_t\nngx_os_specific_init(ngx_log_t *log)\n{\n    size_t      size;\n    ngx_err_t   err;\n    ngx_uint_t  i;\n\n    size = sizeof(ngx_darwin_kern_ostype);\n    if (sysctlbyname(\"kern.ostype\", ngx_darwin_kern_ostype, &size, NULL, 0)\n        == -1)\n    {\n        err = ngx_errno;\n\n        if (err != NGX_ENOENT) {\n\n            ngx_log_error(NGX_LOG_ALERT, log, err,\n                          \"sysctlbyname(kern.ostype) failed\");\n\n            if (err != NGX_ENOMEM) {\n                return NGX_ERROR;\n            }\n\n            ngx_darwin_kern_ostype[size - 1] = '\\0';\n        }\n    }\n\n    size = sizeof(ngx_darwin_kern_osrelease);\n    if (sysctlbyname(\"kern.osrelease\", ngx_darwin_kern_osrelease, &size,\n                     NULL, 0)\n        == -1)\n    {\n        err = ngx_errno;\n\n        if (err != NGX_ENOENT) {\n\n            ngx_log_error(NGX_LOG_ALERT, log, err,\n                          \"sysctlbyname(kern.osrelease) failed\");\n\n            if (err != NGX_ENOMEM) {\n                return NGX_ERROR;\n            }\n\n            ngx_darwin_kern_osrelease[size - 1] = '\\0';\n        }\n    }\n\n    for (i = 0; sysctls[i].name; i++) {\n        size = sysctls[i].size;\n\n        if (sysctlbyname(sysctls[i].name, sysctls[i].value, &size, NULL, 0)\n            == 0)\n        {\n            sysctls[i].exists = 1;\n            continue;\n        }\n\n        err = ngx_errno;\n\n        if (err == NGX_ENOENT) {\n            continue;\n        }\n\n        ngx_log_error(NGX_LOG_ALERT, log, err,\n                      \"sysctlbyname(%s) failed\", sysctls[i].name);\n        return NGX_ERROR;\n    }\n\n    ngx_ncpu = ngx_darwin_hw_ncpu;\n\n    if (ngx_darwin_kern_ipc_somaxconn > 32767) {\n        ngx_log_error(NGX_LOG_ALERT, log, 0,\n                      \"sysctl kern.ipc.somaxconn must be less than 32768\");\n        return NGX_ERROR;\n    }\n\n    ngx_tcp_nodelay_and_tcp_nopush = 1;\n\n    ngx_os_io = ngx_darwin_io;\n\n    return NGX_OK;\n}\n\n\nvoid\nngx_os_specific_status(ngx_log_t *log)\n{\n    u_long      value;\n    ngx_uint_t  i;\n\n    if (ngx_darwin_kern_ostype[0]) {\n        ngx_log_error(NGX_LOG_NOTICE, log, 0, \"OS: %s %s\",\n                      ngx_darwin_kern_ostype, ngx_darwin_kern_osrelease);\n    }\n\n    for (i = 0; sysctls[i].name; i++) {\n        if (sysctls[i].exists) {\n            if (sysctls[i].size == sizeof(long)) {\n                value = *(long *) sysctls[i].value;\n\n            } else {\n                value = *(int *) sysctls[i].value;\n            }\n\n            ngx_log_error(NGX_LOG_NOTICE, log, 0, \"%s: %l\",\n                          sysctls[i].name, value);\n        }\n    }\n}\n"
  },
  {
    "path": "src/os/unix/ngx_darwin_sendfile_chain.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n\n\n/*\n * It seems that Darwin 9.4 (Mac OS X 1.5) sendfile() has the same\n * old bug as early FreeBSD sendfile() syscall:\n * http://bugs.freebsd.org/33771\n *\n * Besides sendfile() has another bug: if one calls sendfile()\n * with both a header and a trailer, then sendfile() ignores a file part\n * at all and sends only the header and the trailer together.\n * For this reason we send a trailer only if there is no a header.\n *\n * Although sendfile() allows to pass a header or a trailer,\n * it may send the header or the trailer and a part of the file\n * in different packets.  And FreeBSD workaround (TCP_NOPUSH option)\n * does not help.\n */\n\n\nngx_chain_t *\nngx_darwin_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)\n{\n    int              rc;\n    off_t            send, prev_send, sent;\n    off_t            file_size;\n    ssize_t          n;\n    ngx_uint_t       eintr;\n    ngx_err_t        err;\n    ngx_buf_t       *file;\n    ngx_event_t     *wev;\n    ngx_chain_t     *cl;\n    ngx_iovec_t      header, trailer;\n    struct sf_hdtr   hdtr;\n    struct iovec     headers[NGX_IOVS_PREALLOCATE];\n    struct iovec     trailers[NGX_IOVS_PREALLOCATE];\n\n    wev = c->write;\n\n    if (!wev->ready) {\n        return in;\n    }\n\n#if (NGX_HAVE_KQUEUE)\n\n    if ((ngx_event_flags & NGX_USE_KQUEUE_EVENT) && wev->pending_eof) {\n        (void) ngx_connection_error(c, wev->kq_errno,\n                               \"kevent() reported about an closed connection\");\n        wev->error = 1;\n        return NGX_CHAIN_ERROR;\n    }\n\n#endif\n\n    /* the maximum limit size is the maximum size_t value - the page size */\n\n    if (limit == 0 || limit > (off_t) (NGX_MAX_SIZE_T_VALUE - ngx_pagesize)) {\n        limit = NGX_MAX_SIZE_T_VALUE - ngx_pagesize;\n    }\n\n    send = 0;\n\n    header.iovs = headers;\n    header.nalloc = NGX_IOVS_PREALLOCATE;\n\n    trailer.iovs = trailers;\n    trailer.nalloc = NGX_IOVS_PREALLOCATE;\n\n    for ( ;; ) {\n        eintr = 0;\n        prev_send = send;\n\n        /* create the header iovec and coalesce the neighbouring bufs */\n\n        cl = ngx_output_chain_to_iovec(&header, in, limit - send, c->log);\n\n        if (cl == NGX_CHAIN_ERROR) {\n            return NGX_CHAIN_ERROR;\n        }\n\n        send += header.size;\n\n        if (cl && cl->buf->in_file && send < limit) {\n            file = cl->buf;\n\n            /* coalesce the neighbouring file bufs */\n\n            file_size = ngx_chain_coalesce_file(&cl, limit - send);\n\n            send += file_size;\n\n            if (header.count == 0 && send < limit) {\n\n                /*\n                 * create the trailer iovec and coalesce the neighbouring bufs\n                 */\n\n                cl = ngx_output_chain_to_iovec(&trailer, cl, limit - send,\n                                               c->log);\n                if (cl == NGX_CHAIN_ERROR) {\n                    return NGX_CHAIN_ERROR;\n                }\n\n                send += trailer.size;\n\n            } else {\n                trailer.count = 0;\n            }\n\n            /*\n             * sendfile() returns EINVAL if sf_hdtr's count is 0,\n             * but corresponding pointer is not NULL\n             */\n\n            hdtr.headers = header.count ? header.iovs : NULL;\n            hdtr.hdr_cnt = header.count;\n            hdtr.trailers = trailer.count ? trailer.iovs : NULL;\n            hdtr.trl_cnt = trailer.count;\n\n            sent = header.size + file_size;\n\n            ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                           \"sendfile: @%O %O h:%uz\",\n                           file->file_pos, sent, header.size);\n\n            rc = sendfile(file->file->fd, c->fd, file->file_pos,\n                          &sent, &hdtr, 0);\n\n            if (rc == -1) {\n                err = ngx_errno;\n\n                switch (err) {\n                case NGX_EAGAIN:\n                    break;\n\n                case NGX_EINTR:\n                    eintr = 1;\n                    break;\n\n                default:\n                    wev->error = 1;\n                    (void) ngx_connection_error(c, err, \"sendfile() failed\");\n                    return NGX_CHAIN_ERROR;\n                }\n\n                ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, err,\n                               \"sendfile() sent only %O bytes\", sent);\n            }\n\n            if (rc == 0 && sent == 0) {\n\n                /*\n                 * if rc and sent equal to zero, then someone\n                 * has truncated the file, so the offset became beyond\n                 * the end of the file\n                 */\n\n                ngx_log_error(NGX_LOG_ALERT, c->log, 0,\n                              \"sendfile() reported that \\\"%s\\\" was truncated\",\n                              file->file->name.data);\n\n                return NGX_CHAIN_ERROR;\n            }\n\n            ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                           \"sendfile: %d, @%O %O:%O\",\n                           rc, file->file_pos, sent, file_size + header.size);\n\n        } else {\n            n = ngx_writev(c, &header);\n\n            if (n == NGX_ERROR) {\n                return NGX_CHAIN_ERROR;\n            }\n\n            sent = (n == NGX_AGAIN) ? 0 : n;\n        }\n\n        c->sent += sent;\n\n        in = ngx_chain_update_sent(in, sent);\n\n        if (eintr) {\n            send = prev_send + sent;\n            continue;\n        }\n\n        if (send - prev_send != sent) {\n            wev->ready = 0;\n            return in;\n        }\n\n        if (send >= limit || in == NULL) {\n            return in;\n        }\n    }\n}\n"
  },
  {
    "path": "src/os/unix/ngx_dlopen.c",
    "content": "\n/*\n * Copyright (C) Maxim Dounin\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\n#if (NGX_HAVE_DLOPEN)\n\nchar *\nngx_dlerror(void)\n{\n    char  *err;\n\n    err = (char *) dlerror();\n\n    if (err == NULL) {\n        return \"\";\n    }\n\n    return err;\n}\n\n#endif\n"
  },
  {
    "path": "src/os/unix/ngx_dlopen.h",
    "content": "\n/*\n * Copyright (C) Maxim Dounin\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_DLOPEN_H_INCLUDED_\n#define _NGX_DLOPEN_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\n#define ngx_dlopen(path)           dlopen((char *) path, RTLD_NOW | RTLD_GLOBAL)\n#define ngx_dlopen_n               \"dlopen()\"\n\n#define ngx_dlsym(handle, symbol)  dlsym(handle, symbol)\n#define ngx_dlsym_n                \"dlsym()\"\n\n#define ngx_dlclose(handle)        dlclose(handle)\n#define ngx_dlclose_n              \"dlclose()\"\n\n\n#if (NGX_HAVE_DLOPEN)\nchar *ngx_dlerror(void);\n#endif\n\n\n#endif /* _NGX_DLOPEN_H_INCLUDED_ */\n"
  },
  {
    "path": "src/os/unix/ngx_errno.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\nstatic ngx_str_t   ngx_unknown_error = ngx_string(\"Unknown error\");\n\n\n#if (NGX_HAVE_STRERRORDESC_NP)\n\n/*\n * The strerrordesc_np() function, introduced in glibc 2.32, is\n * async-signal-safe.  This makes it possible to use it directly,\n * without copying error messages.\n */\n\n\nu_char *\nngx_strerror(ngx_err_t err, u_char *errstr, size_t size)\n{\n    size_t       len;\n    const char  *msg;\n\n    msg = strerrordesc_np(err);\n\n    if (msg == NULL) {\n        msg = (char *) ngx_unknown_error.data;\n        len = ngx_unknown_error.len;\n\n    } else {\n        len = ngx_strlen(msg);\n    }\n\n    size = ngx_min(size, len);\n\n    return ngx_cpymem(errstr, msg, size);\n}\n\n\nngx_int_t\nngx_strerror_init(void)\n{\n    return NGX_OK;\n}\n\n\n#else\n\n/*\n * The strerror() messages are copied because:\n *\n * 1) strerror() and strerror_r() functions are not Async-Signal-Safe,\n *    therefore, they cannot be used in signal handlers;\n *\n * 2) a direct sys_errlist[] array may be used instead of these functions,\n *    but Linux linker warns about its usage:\n *\n * warning: `sys_errlist' is deprecated; use `strerror' or `strerror_r' instead\n * warning: `sys_nerr' is deprecated; use `strerror' or `strerror_r' instead\n *\n *    causing false bug reports.\n */\n\n\nstatic ngx_str_t  *ngx_sys_errlist;\nstatic ngx_err_t   ngx_first_error;\nstatic ngx_err_t   ngx_last_error;\n\n\nu_char *\nngx_strerror(ngx_err_t err, u_char *errstr, size_t size)\n{\n    ngx_str_t  *msg;\n\n    if (err >= ngx_first_error && err < ngx_last_error) {\n        msg = &ngx_sys_errlist[err - ngx_first_error];\n\n    } else {\n        msg = &ngx_unknown_error;\n    }\n\n    size = ngx_min(size, msg->len);\n\n    return ngx_cpymem(errstr, msg->data, size);\n}\n\n\nngx_int_t\nngx_strerror_init(void)\n{\n    char       *msg;\n    u_char     *p;\n    size_t      len;\n    ngx_err_t   err;\n\n#if (NGX_SYS_NERR)\n    ngx_first_error = 0;\n    ngx_last_error = NGX_SYS_NERR;\n\n#elif (EPERM > 1000 && EPERM < 0x7fffffff - 1000)\n\n    /*\n     * If number of errors is not known, and EPERM error code has large\n     * but reasonable value, guess possible error codes based on the error\n     * messages returned by strerror(), starting from EPERM.  Notably,\n     * this covers GNU/Hurd, where errors start at 0x40000001.\n     */\n\n    for (err = EPERM; err > EPERM - 1000; err--) {\n        ngx_set_errno(0);\n        msg = strerror(err);\n\n        if (errno == EINVAL\n            || msg == NULL\n            || strncmp(msg, \"Unknown error\", 13) == 0)\n        {\n            continue;\n        }\n\n        ngx_first_error = err;\n    }\n\n    for (err = EPERM; err < EPERM + 1000; err++) {\n        ngx_set_errno(0);\n        msg = strerror(err);\n\n        if (errno == EINVAL\n            || msg == NULL\n            || strncmp(msg, \"Unknown error\", 13) == 0)\n        {\n            continue;\n        }\n\n        ngx_last_error = err + 1;\n    }\n\n#else\n\n    /*\n     * If number of errors is not known, guess it based on the error\n     * messages returned by strerror().\n     */\n\n    ngx_first_error = 0;\n\n    for (err = 0; err < 1000; err++) {\n        ngx_set_errno(0);\n        msg = strerror(err);\n\n        if (errno == EINVAL\n            || msg == NULL\n            || strncmp(msg, \"Unknown error\", 13) == 0)\n        {\n            continue;\n        }\n\n        ngx_last_error = err + 1;\n    }\n\n#endif\n\n    /*\n     * ngx_strerror() is not ready to work at this stage, therefore,\n     * malloc() is used and possible errors are logged using strerror().\n     */\n\n    len = (ngx_last_error - ngx_first_error) * sizeof(ngx_str_t);\n\n    ngx_sys_errlist = malloc(len);\n    if (ngx_sys_errlist == NULL) {\n        goto failed;\n    }\n\n    for (err = ngx_first_error; err < ngx_last_error; err++) {\n        msg = strerror(err);\n\n        if (msg == NULL) {\n            ngx_sys_errlist[err - ngx_first_error] = ngx_unknown_error;\n            continue;\n        }\n\n        len = ngx_strlen(msg);\n\n        p = malloc(len);\n        if (p == NULL) {\n            goto failed;\n        }\n\n        ngx_memcpy(p, msg, len);\n        ngx_sys_errlist[err - ngx_first_error].len = len;\n        ngx_sys_errlist[err - ngx_first_error].data = p;\n    }\n\n    return NGX_OK;\n\nfailed:\n\n    err = errno;\n    ngx_log_stderr(0, \"malloc(%uz) failed (%d: %s)\", len, err, strerror(err));\n\n    return NGX_ERROR;\n}\n\n#endif\n"
  },
  {
    "path": "src/os/unix/ngx_errno.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_ERRNO_H_INCLUDED_\n#define _NGX_ERRNO_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\ntypedef int               ngx_err_t;\n\n#define NGX_EPERM         EPERM\n#define NGX_ENOENT        ENOENT\n#define NGX_ENOPATH       ENOENT\n#define NGX_ESRCH         ESRCH\n#define NGX_EINTR         EINTR\n#define NGX_ECHILD        ECHILD\n#define NGX_ENOMEM        ENOMEM\n#define NGX_EACCES        EACCES\n#define NGX_EBUSY         EBUSY\n#define NGX_EEXIST        EEXIST\n#define NGX_EEXIST_FILE   EEXIST\n#define NGX_EXDEV         EXDEV\n#define NGX_ENOTDIR       ENOTDIR\n#define NGX_EISDIR        EISDIR\n#define NGX_EINVAL        EINVAL\n#define NGX_ENFILE        ENFILE\n#define NGX_EMFILE        EMFILE\n#define NGX_ENOSPC        ENOSPC\n#define NGX_EPIPE         EPIPE\n#define NGX_EINPROGRESS   EINPROGRESS\n#define NGX_ENOPROTOOPT   ENOPROTOOPT\n#define NGX_EOPNOTSUPP    EOPNOTSUPP\n#define NGX_EADDRINUSE    EADDRINUSE\n#define NGX_ECONNABORTED  ECONNABORTED\n#define NGX_ECONNRESET    ECONNRESET\n#define NGX_ENOTCONN      ENOTCONN\n#define NGX_ETIMEDOUT     ETIMEDOUT\n#define NGX_ECONNREFUSED  ECONNREFUSED\n#define NGX_ENAMETOOLONG  ENAMETOOLONG\n#define NGX_ENETDOWN      ENETDOWN\n#define NGX_ENETUNREACH   ENETUNREACH\n#define NGX_EHOSTDOWN     EHOSTDOWN\n#define NGX_EHOSTUNREACH  EHOSTUNREACH\n#define NGX_ENOSYS        ENOSYS\n#define NGX_ECANCELED     ECANCELED\n#define NGX_EILSEQ        EILSEQ\n#define NGX_ENOMOREFILES  0\n#define NGX_ELOOP         ELOOP\n#define NGX_EBADF         EBADF\n\n#if (NGX_HAVE_OPENAT)\n#define NGX_EMLINK        EMLINK\n#endif\n\n#if (__hpux__)\n#define NGX_EAGAIN        EWOULDBLOCK\n#else\n#define NGX_EAGAIN        EAGAIN\n#endif\n\n\n#define ngx_errno                  errno\n#define ngx_socket_errno           errno\n#define ngx_set_errno(err)         errno = err\n#define ngx_set_socket_errno(err)  errno = err\n\n\nu_char *ngx_strerror(ngx_err_t err, u_char *errstr, size_t size);\nngx_int_t ngx_strerror_init(void);\n\n\n#endif /* _NGX_ERRNO_H_INCLUDED_ */\n"
  },
  {
    "path": "src/os/unix/ngx_file_aio_read.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n\n\n/*\n * FreeBSD file AIO features and quirks:\n *\n *    if an asked data are already in VM cache, then aio_error() returns 0,\n *    and the data are already copied in buffer;\n *\n *    aio_read() preread in VM cache as minimum 16K (probably BKVASIZE);\n *    the first AIO preload may be up to 128K;\n *\n *    aio_read/aio_error() may return EINPROGRESS for just written data;\n *\n *    kqueue EVFILT_AIO filter is level triggered only: an event repeats\n *    until aio_return() will be called;\n *\n *    aio_cancel() cannot cancel file AIO: it returns AIO_NOTCANCELED always.\n */\n\n\nextern int  ngx_kqueue;\n\n\nstatic ssize_t ngx_file_aio_result(ngx_file_t *file, ngx_event_aio_t *aio,\n    ngx_event_t *ev);\nstatic void ngx_file_aio_event_handler(ngx_event_t *ev);\n\n\nngx_int_t\nngx_file_aio_init(ngx_file_t *file, ngx_pool_t *pool)\n{\n    ngx_event_aio_t  *aio;\n\n    aio = ngx_pcalloc(pool, sizeof(ngx_event_aio_t));\n    if (aio == NULL) {\n        return NGX_ERROR;\n    }\n\n    aio->file = file;\n    aio->fd = file->fd;\n    aio->event.data = aio;\n    aio->event.ready = 1;\n    aio->event.log = file->log;\n\n    file->aio = aio;\n\n    return NGX_OK;\n}\n\n\nssize_t\nngx_file_aio_read(ngx_file_t *file, u_char *buf, size_t size, off_t offset,\n    ngx_pool_t *pool)\n{\n    int               n;\n    ngx_event_t      *ev;\n    ngx_event_aio_t  *aio;\n\n    if (!ngx_file_aio) {\n        return ngx_read_file(file, buf, size, offset);\n    }\n\n    if (file->aio == NULL && ngx_file_aio_init(file, pool) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    aio = file->aio;\n    ev = &aio->event;\n\n    if (!ev->ready) {\n        ngx_log_error(NGX_LOG_ALERT, file->log, 0,\n                      \"second aio post for \\\"%V\\\"\", &file->name);\n        return NGX_AGAIN;\n    }\n\n    ngx_log_debug4(NGX_LOG_DEBUG_CORE, file->log, 0,\n                   \"aio complete:%d @%O:%uz %V\",\n                   ev->complete, offset, size, &file->name);\n\n    if (ev->complete) {\n        ev->complete = 0;\n        ngx_set_errno(aio->err);\n\n        if (aio->err == 0) {\n            return aio->nbytes;\n        }\n\n        ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno,\n                      \"aio read \\\"%s\\\" failed\", file->name.data);\n\n        return NGX_ERROR;\n    }\n\n    ngx_memzero(&aio->aiocb, sizeof(struct aiocb));\n\n    aio->aiocb.aio_fildes = file->fd;\n    aio->aiocb.aio_offset = offset;\n    aio->aiocb.aio_buf = buf;\n    aio->aiocb.aio_nbytes = size;\n#if (NGX_HAVE_KQUEUE)\n    aio->aiocb.aio_sigevent.sigev_notify_kqueue = ngx_kqueue;\n    aio->aiocb.aio_sigevent.sigev_notify = SIGEV_KEVENT;\n    aio->aiocb.aio_sigevent.sigev_value.sival_ptr = ev;\n#endif\n    ev->handler = ngx_file_aio_event_handler;\n\n    n = aio_read(&aio->aiocb);\n\n    if (n == -1) {\n        n = ngx_errno;\n\n        if (n == NGX_EAGAIN) {\n            return ngx_read_file(file, buf, size, offset);\n        }\n\n        ngx_log_error(NGX_LOG_CRIT, file->log, n,\n                      \"aio_read(\\\"%V\\\") failed\", &file->name);\n\n        if (n == NGX_ENOSYS) {\n            ngx_file_aio = 0;\n            return ngx_read_file(file, buf, size, offset);\n        }\n\n        return NGX_ERROR;\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_CORE, file->log, 0,\n                   \"aio_read: fd:%d %d\", file->fd, n);\n\n    ev->active = 1;\n    ev->ready = 0;\n    ev->complete = 0;\n\n    return ngx_file_aio_result(aio->file, aio, ev);\n}\n\n\nstatic ssize_t\nngx_file_aio_result(ngx_file_t *file, ngx_event_aio_t *aio, ngx_event_t *ev)\n{\n    int        n;\n    ngx_err_t  err;\n\n    n = aio_error(&aio->aiocb);\n\n    ngx_log_debug2(NGX_LOG_DEBUG_CORE, file->log, 0,\n                   \"aio_error: fd:%d %d\", file->fd, n);\n\n    if (n == -1) {\n        err = ngx_errno;\n        aio->err = err;\n\n        ngx_log_error(NGX_LOG_ALERT, file->log, err,\n                      \"aio_error(\\\"%V\\\") failed\", &file->name);\n        return NGX_ERROR;\n    }\n\n    if (n == NGX_EINPROGRESS) {\n        if (ev->ready) {\n            ev->ready = 0;\n            ngx_log_error(NGX_LOG_ALERT, file->log, n,\n                          \"aio_read(\\\"%V\\\") still in progress\",\n                          &file->name);\n        }\n\n        return NGX_AGAIN;\n    }\n\n    n = aio_return(&aio->aiocb);\n\n    if (n == -1) {\n        err = ngx_errno;\n        aio->err = err;\n        ev->ready = 1;\n\n        ngx_log_error(NGX_LOG_CRIT, file->log, err,\n                      \"aio_return(\\\"%V\\\") failed\", &file->name);\n        return NGX_ERROR;\n    }\n\n    aio->err = 0;\n    aio->nbytes = n;\n    ev->ready = 1;\n    ev->active = 0;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_CORE, file->log, 0,\n                   \"aio_return: fd:%d %d\", file->fd, n);\n\n    return n;\n}\n\n\nstatic void\nngx_file_aio_event_handler(ngx_event_t *ev)\n{\n    ngx_event_aio_t  *aio;\n\n    aio = ev->data;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_CORE, ev->log, 0,\n                   \"aio event handler fd:%d %V\", aio->fd, &aio->file->name);\n\n    if (ngx_file_aio_result(aio->file, aio, ev) != NGX_AGAIN) {\n        aio->handler(ev);\n    }\n}\n"
  },
  {
    "path": "src/os/unix/ngx_files.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\n#if (NGX_THREADS)\n#include <ngx_thread_pool.h>\nstatic void ngx_thread_read_handler(void *data, ngx_log_t *log);\nstatic void ngx_thread_write_chain_to_file_handler(void *data, ngx_log_t *log);\n#endif\n\nstatic ngx_chain_t *ngx_chain_to_iovec(ngx_iovec_t *vec, ngx_chain_t *cl);\nstatic ssize_t ngx_writev_file(ngx_file_t *file, ngx_iovec_t *vec,\n    off_t offset);\n\n\n#if (NGX_HAVE_FILE_AIO)\n\nngx_uint_t  ngx_file_aio = 1;\n\n#endif\n\n\nssize_t\nngx_read_file(ngx_file_t *file, u_char *buf, size_t size, off_t offset)\n{\n    ssize_t  n;\n\n    ngx_log_debug4(NGX_LOG_DEBUG_CORE, file->log, 0,\n                   \"read: %d, %p, %uz, %O\", file->fd, buf, size, offset);\n\n#if (NGX_HAVE_PREAD)\n\n    n = pread(file->fd, buf, size, offset);\n\n    if (n == -1) {\n        ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno,\n                      \"pread() \\\"%s\\\" failed\", file->name.data);\n        return NGX_ERROR;\n    }\n\n#else\n\n    if (file->sys_offset != offset) {\n        if (lseek(file->fd, offset, SEEK_SET) == -1) {\n            ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno,\n                          \"lseek() \\\"%s\\\" failed\", file->name.data);\n            return NGX_ERROR;\n        }\n\n        file->sys_offset = offset;\n    }\n\n    n = read(file->fd, buf, size);\n\n    if (n == -1) {\n        ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno,\n                      \"read() \\\"%s\\\" failed\", file->name.data);\n        return NGX_ERROR;\n    }\n\n    file->sys_offset += n;\n\n#endif\n\n    file->offset += n;\n\n    return n;\n}\n\n\n#if (NGX_THREADS)\n\ntypedef struct {\n    ngx_fd_t       fd;\n    ngx_uint_t     write;   /* unsigned  write:1; */\n\n    u_char        *buf;\n    size_t         size;\n    ngx_chain_t   *chain;\n    off_t          offset;\n\n    size_t         nbytes;\n    ngx_err_t      err;\n} ngx_thread_file_ctx_t;\n\n\nssize_t\nngx_thread_read(ngx_file_t *file, u_char *buf, size_t size, off_t offset,\n    ngx_pool_t *pool)\n{\n    ngx_thread_task_t      *task;\n    ngx_thread_file_ctx_t  *ctx;\n\n    ngx_log_debug4(NGX_LOG_DEBUG_CORE, file->log, 0,\n                   \"thread read: %d, %p, %uz, %O\",\n                   file->fd, buf, size, offset);\n\n    task = file->thread_task;\n\n    if (task == NULL) {\n        task = ngx_thread_task_alloc(pool, sizeof(ngx_thread_file_ctx_t));\n        if (task == NULL) {\n            return NGX_ERROR;\n        }\n\n        file->thread_task = task;\n    }\n\n    ctx = task->ctx;\n\n    if (task->event.complete) {\n        task->event.complete = 0;\n\n        if (ctx->write) {\n            ngx_log_error(NGX_LOG_ALERT, file->log, 0,\n                          \"invalid thread call, read instead of write\");\n            return NGX_ERROR;\n        }\n\n        if (ctx->err) {\n            ngx_log_error(NGX_LOG_CRIT, file->log, ctx->err,\n                          \"pread() \\\"%s\\\" failed\", file->name.data);\n            return NGX_ERROR;\n        }\n\n        return ctx->nbytes;\n    }\n\n    task->handler = ngx_thread_read_handler;\n\n    ctx->write = 0;\n\n    ctx->fd = file->fd;\n    ctx->buf = buf;\n    ctx->size = size;\n    ctx->offset = offset;\n\n    if (file->thread_handler(task, file) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    return NGX_AGAIN;\n}\n\n\n#if (NGX_HAVE_PREAD)\n\nstatic void\nngx_thread_read_handler(void *data, ngx_log_t *log)\n{\n    ngx_thread_file_ctx_t *ctx = data;\n\n    ssize_t  n;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_CORE, log, 0, \"thread read handler\");\n\n    n = pread(ctx->fd, ctx->buf, ctx->size, ctx->offset);\n\n    if (n == -1) {\n        ctx->err = ngx_errno;\n\n    } else {\n        ctx->nbytes = n;\n        ctx->err = 0;\n    }\n\n#if 0\n    ngx_time_update();\n#endif\n\n    ngx_log_debug4(NGX_LOG_DEBUG_CORE, log, 0,\n                   \"pread: %z (err: %d) of %uz @%O\",\n                   n, ctx->err, ctx->size, ctx->offset);\n}\n\n#else\n\n#error pread() is required!\n\n#endif\n\n#endif /* NGX_THREADS */\n\n\nssize_t\nngx_write_file(ngx_file_t *file, u_char *buf, size_t size, off_t offset)\n{\n    ssize_t    n, written;\n    ngx_err_t  err;\n\n    ngx_log_debug4(NGX_LOG_DEBUG_CORE, file->log, 0,\n                   \"write: %d, %p, %uz, %O\", file->fd, buf, size, offset);\n\n    written = 0;\n\n#if (NGX_HAVE_PWRITE)\n\n    for ( ;; ) {\n        n = pwrite(file->fd, buf + written, size, offset);\n\n        if (n == -1) {\n            err = ngx_errno;\n\n            if (err == NGX_EINTR) {\n                ngx_log_debug0(NGX_LOG_DEBUG_CORE, file->log, err,\n                               \"pwrite() was interrupted\");\n                continue;\n            }\n\n            ngx_log_error(NGX_LOG_CRIT, file->log, err,\n                          \"pwrite() \\\"%s\\\" failed\", file->name.data);\n            return NGX_ERROR;\n        }\n\n        file->offset += n;\n        written += n;\n\n        if ((size_t) n == size) {\n            return written;\n        }\n\n        offset += n;\n        size -= n;\n    }\n\n#else\n\n    if (file->sys_offset != offset) {\n        if (lseek(file->fd, offset, SEEK_SET) == -1) {\n            ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno,\n                          \"lseek() \\\"%s\\\" failed\", file->name.data);\n            return NGX_ERROR;\n        }\n\n        file->sys_offset = offset;\n    }\n\n    for ( ;; ) {\n        n = write(file->fd, buf + written, size);\n\n        if (n == -1) {\n            err = ngx_errno;\n\n            if (err == NGX_EINTR) {\n                ngx_log_debug0(NGX_LOG_DEBUG_CORE, file->log, err,\n                               \"write() was interrupted\");\n                continue;\n            }\n\n            ngx_log_error(NGX_LOG_CRIT, file->log, err,\n                          \"write() \\\"%s\\\" failed\", file->name.data);\n            return NGX_ERROR;\n        }\n\n        file->sys_offset += n;\n        file->offset += n;\n        written += n;\n\n        if ((size_t) n == size) {\n            return written;\n        }\n\n        size -= n;\n    }\n#endif\n}\n\n\nngx_fd_t\nngx_open_tempfile(u_char *name, ngx_uint_t persistent, ngx_uint_t access)\n{\n    ngx_fd_t  fd;\n\n    fd = open((const char *) name, O_CREAT|O_EXCL|O_RDWR,\n              access ? access : 0600);\n\n    if (fd != -1 && !persistent) {\n        (void) unlink((const char *) name);\n    }\n\n    return fd;\n}\n\n\nssize_t\nngx_write_chain_to_file(ngx_file_t *file, ngx_chain_t *cl, off_t offset,\n    ngx_pool_t *pool)\n{\n    ssize_t        total, n;\n    ngx_iovec_t    vec;\n    struct iovec   iovs[NGX_IOVS_PREALLOCATE];\n\n    /* use pwrite() if there is the only buf in a chain */\n\n    if (cl->next == NULL) {\n        return ngx_write_file(file, cl->buf->pos,\n                              (size_t) (cl->buf->last - cl->buf->pos),\n                              offset);\n    }\n\n    total = 0;\n\n    vec.iovs = iovs;\n    vec.nalloc = NGX_IOVS_PREALLOCATE;\n\n    do {\n        /* create the iovec and coalesce the neighbouring bufs */\n        cl = ngx_chain_to_iovec(&vec, cl);\n\n        /* use pwrite() if there is the only iovec buffer */\n\n        if (vec.count == 1) {\n            n = ngx_write_file(file, (u_char *) iovs[0].iov_base,\n                               iovs[0].iov_len, offset);\n\n            if (n == NGX_ERROR) {\n                return n;\n            }\n\n            return total + n;\n        }\n\n        n = ngx_writev_file(file, &vec, offset);\n\n        if (n == NGX_ERROR) {\n            return n;\n        }\n\n        offset += n;\n        total += n;\n\n    } while (cl);\n\n    return total;\n}\n\n\nstatic ngx_chain_t *\nngx_chain_to_iovec(ngx_iovec_t *vec, ngx_chain_t *cl)\n{\n    size_t         total, size;\n    u_char        *prev;\n    ngx_uint_t     n;\n    struct iovec  *iov;\n\n    iov = NULL;\n    prev = NULL;\n    total = 0;\n    n = 0;\n\n    for ( /* void */ ; cl; cl = cl->next) {\n\n        if (ngx_buf_special(cl->buf)) {\n            continue;\n        }\n\n        size = cl->buf->last - cl->buf->pos;\n\n        if (prev == cl->buf->pos) {\n            iov->iov_len += size;\n\n        } else {\n            if (n == vec->nalloc) {\n                break;\n            }\n\n            iov = &vec->iovs[n++];\n\n            iov->iov_base = (void *) cl->buf->pos;\n            iov->iov_len = size;\n        }\n\n        prev = cl->buf->pos + size;\n        total += size;\n    }\n\n    vec->count = n;\n    vec->size = total;\n\n    return cl;\n}\n\n\nstatic ssize_t\nngx_writev_file(ngx_file_t *file, ngx_iovec_t *vec, off_t offset)\n{\n    ssize_t    n;\n    ngx_err_t  err;\n\n    ngx_log_debug3(NGX_LOG_DEBUG_CORE, file->log, 0,\n                   \"writev: %d, %uz, %O\", file->fd, vec->size, offset);\n\n#if (NGX_HAVE_PWRITEV)\n\neintr:\n\n    n = pwritev(file->fd, vec->iovs, vec->count, offset);\n\n    if (n == -1) {\n        err = ngx_errno;\n\n        if (err == NGX_EINTR) {\n            ngx_log_debug0(NGX_LOG_DEBUG_CORE, file->log, err,\n                           \"pwritev() was interrupted\");\n            goto eintr;\n        }\n\n        ngx_log_error(NGX_LOG_CRIT, file->log, err,\n                      \"pwritev() \\\"%s\\\" failed\", file->name.data);\n        return NGX_ERROR;\n    }\n\n    if ((size_t) n != vec->size) {\n        ngx_log_error(NGX_LOG_CRIT, file->log, 0,\n                      \"pwritev() \\\"%s\\\" has written only %z of %uz\",\n                      file->name.data, n, vec->size);\n        return NGX_ERROR;\n    }\n\n#else\n\n    if (file->sys_offset != offset) {\n        if (lseek(file->fd, offset, SEEK_SET) == -1) {\n            ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno,\n                          \"lseek() \\\"%s\\\" failed\", file->name.data);\n            return NGX_ERROR;\n        }\n\n        file->sys_offset = offset;\n    }\n\neintr:\n\n    n = writev(file->fd, vec->iovs, vec->count);\n\n    if (n == -1) {\n        err = ngx_errno;\n\n        if (err == NGX_EINTR) {\n            ngx_log_debug0(NGX_LOG_DEBUG_CORE, file->log, err,\n                           \"writev() was interrupted\");\n            goto eintr;\n        }\n\n        ngx_log_error(NGX_LOG_CRIT, file->log, err,\n                      \"writev() \\\"%s\\\" failed\", file->name.data);\n        return NGX_ERROR;\n    }\n\n    if ((size_t) n != vec->size) {\n        ngx_log_error(NGX_LOG_CRIT, file->log, 0,\n                      \"writev() \\\"%s\\\" has written only %z of %uz\",\n                      file->name.data, n, vec->size);\n        return NGX_ERROR;\n    }\n\n    file->sys_offset += n;\n\n#endif\n\n    file->offset += n;\n\n    return n;\n}\n\n\n#if (NGX_THREADS)\n\nssize_t\nngx_thread_write_chain_to_file(ngx_file_t *file, ngx_chain_t *cl, off_t offset,\n    ngx_pool_t *pool)\n{\n    ngx_thread_task_t      *task;\n    ngx_thread_file_ctx_t  *ctx;\n\n    ngx_log_debug3(NGX_LOG_DEBUG_CORE, file->log, 0,\n                   \"thread write chain: %d, %p, %O\",\n                   file->fd, cl, offset);\n\n    task = file->thread_task;\n\n    if (task == NULL) {\n        task = ngx_thread_task_alloc(pool,\n                                     sizeof(ngx_thread_file_ctx_t));\n        if (task == NULL) {\n            return NGX_ERROR;\n        }\n\n        file->thread_task = task;\n    }\n\n    ctx = task->ctx;\n\n    if (task->event.complete) {\n        task->event.complete = 0;\n\n        if (!ctx->write) {\n            ngx_log_error(NGX_LOG_ALERT, file->log, 0,\n                          \"invalid thread call, write instead of read\");\n            return NGX_ERROR;\n        }\n\n        if (ctx->err || ctx->nbytes == 0) {\n            ngx_log_error(NGX_LOG_CRIT, file->log, ctx->err,\n                          \"pwritev() \\\"%s\\\" failed\", file->name.data);\n            return NGX_ERROR;\n        }\n\n        file->offset += ctx->nbytes;\n        return ctx->nbytes;\n    }\n\n    task->handler = ngx_thread_write_chain_to_file_handler;\n\n    ctx->write = 1;\n\n    ctx->fd = file->fd;\n    ctx->chain = cl;\n    ctx->offset = offset;\n\n    if (file->thread_handler(task, file) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    return NGX_AGAIN;\n}\n\n\nstatic void\nngx_thread_write_chain_to_file_handler(void *data, ngx_log_t *log)\n{\n    ngx_thread_file_ctx_t *ctx = data;\n\n#if (NGX_HAVE_PWRITEV)\n\n    off_t          offset;\n    ssize_t        n;\n    ngx_err_t      err;\n    ngx_chain_t   *cl;\n    ngx_iovec_t    vec;\n    struct iovec   iovs[NGX_IOVS_PREALLOCATE];\n\n    vec.iovs = iovs;\n    vec.nalloc = NGX_IOVS_PREALLOCATE;\n\n    cl = ctx->chain;\n    offset = ctx->offset;\n\n    ctx->nbytes = 0;\n    ctx->err = 0;\n\n    do {\n        /* create the iovec and coalesce the neighbouring bufs */\n        cl = ngx_chain_to_iovec(&vec, cl);\n\neintr:\n\n        n = pwritev(ctx->fd, iovs, vec.count, offset);\n\n        if (n == -1) {\n            err = ngx_errno;\n\n            if (err == NGX_EINTR) {\n                ngx_log_debug0(NGX_LOG_DEBUG_CORE, log, err,\n                               \"pwritev() was interrupted\");\n                goto eintr;\n            }\n\n            ctx->err = err;\n            return;\n        }\n\n        if ((size_t) n != vec.size) {\n            ctx->nbytes = 0;\n            return;\n        }\n\n        ctx->nbytes += n;\n        offset += n;\n    } while (cl);\n\n#else\n\n    ctx->err = NGX_ENOSYS;\n    return;\n\n#endif\n}\n\n#endif /* NGX_THREADS */\n\n\nngx_int_t\nngx_set_file_time(u_char *name, ngx_fd_t fd, time_t s)\n{\n    struct timeval  tv[2];\n\n    tv[0].tv_sec = ngx_time();\n    tv[0].tv_usec = 0;\n    tv[1].tv_sec = s;\n    tv[1].tv_usec = 0;\n\n    if (utimes((char *) name, tv) != -1) {\n        return NGX_OK;\n    }\n\n    return NGX_ERROR;\n}\n\n\nngx_int_t\nngx_create_file_mapping(ngx_file_mapping_t *fm)\n{\n    fm->fd = ngx_open_file(fm->name, NGX_FILE_RDWR, NGX_FILE_TRUNCATE,\n                           NGX_FILE_DEFAULT_ACCESS);\n\n    if (fm->fd == NGX_INVALID_FILE) {\n        ngx_log_error(NGX_LOG_CRIT, fm->log, ngx_errno,\n                      ngx_open_file_n \" \\\"%s\\\" failed\", fm->name);\n        return NGX_ERROR;\n    }\n\n    if (ftruncate(fm->fd, fm->size) == -1) {\n        ngx_log_error(NGX_LOG_CRIT, fm->log, ngx_errno,\n                      \"ftruncate() \\\"%s\\\" failed\", fm->name);\n        goto failed;\n    }\n\n    fm->addr = mmap(NULL, fm->size, PROT_READ|PROT_WRITE, MAP_SHARED,\n                    fm->fd, 0);\n    if (fm->addr != MAP_FAILED) {\n        return NGX_OK;\n    }\n\n    ngx_log_error(NGX_LOG_CRIT, fm->log, ngx_errno,\n                  \"mmap(%uz) \\\"%s\\\" failed\", fm->size, fm->name);\n\nfailed:\n\n    if (ngx_close_file(fm->fd) == NGX_FILE_ERROR) {\n        ngx_log_error(NGX_LOG_ALERT, fm->log, ngx_errno,\n                      ngx_close_file_n \" \\\"%s\\\" failed\", fm->name);\n    }\n\n    return NGX_ERROR;\n}\n\n\nvoid\nngx_close_file_mapping(ngx_file_mapping_t *fm)\n{\n    if (munmap(fm->addr, fm->size) == -1) {\n        ngx_log_error(NGX_LOG_CRIT, fm->log, ngx_errno,\n                      \"munmap(%uz) \\\"%s\\\" failed\", fm->size, fm->name);\n    }\n\n    if (ngx_close_file(fm->fd) == NGX_FILE_ERROR) {\n        ngx_log_error(NGX_LOG_ALERT, fm->log, ngx_errno,\n                      ngx_close_file_n \" \\\"%s\\\" failed\", fm->name);\n    }\n}\n\n\nngx_int_t\nngx_open_dir(ngx_str_t *name, ngx_dir_t *dir)\n{\n    dir->dir = opendir((const char *) name->data);\n\n    if (dir->dir == NULL) {\n        return NGX_ERROR;\n    }\n\n    dir->valid_info = 0;\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_read_dir(ngx_dir_t *dir)\n{\n    dir->de = readdir(dir->dir);\n\n    if (dir->de) {\n#if (NGX_HAVE_D_TYPE)\n        dir->type = dir->de->d_type;\n#else\n        dir->type = 0;\n#endif\n        return NGX_OK;\n    }\n\n    return NGX_ERROR;\n}\n\n\nngx_int_t\nngx_open_glob(ngx_glob_t *gl)\n{\n    int  n;\n\n    n = glob((char *) gl->pattern, 0, NULL, &gl->pglob);\n\n    if (n == 0) {\n        return NGX_OK;\n    }\n\n#ifdef GLOB_NOMATCH\n\n    if (n == GLOB_NOMATCH && gl->test) {\n        return NGX_OK;\n    }\n\n#endif\n\n    return NGX_ERROR;\n}\n\n\nngx_int_t\nngx_read_glob(ngx_glob_t *gl, ngx_str_t *name)\n{\n    size_t  count;\n\n#ifdef GLOB_NOMATCH\n    count = (size_t) gl->pglob.gl_pathc;\n#else\n    count = (size_t) gl->pglob.gl_matchc;\n#endif\n\n    if (gl->n < count) {\n\n        name->len = (size_t) ngx_strlen(gl->pglob.gl_pathv[gl->n]);\n        name->data = (u_char *) gl->pglob.gl_pathv[gl->n];\n        gl->n++;\n\n        return NGX_OK;\n    }\n\n    return NGX_DONE;\n}\n\n\nvoid\nngx_close_glob(ngx_glob_t *gl)\n{\n    globfree(&gl->pglob);\n}\n\n\nngx_err_t\nngx_trylock_fd(ngx_fd_t fd)\n{\n    struct flock  fl;\n\n    ngx_memzero(&fl, sizeof(struct flock));\n    fl.l_type = F_WRLCK;\n    fl.l_whence = SEEK_SET;\n\n    if (fcntl(fd, F_SETLK, &fl) == -1) {\n        return ngx_errno;\n    }\n\n    return 0;\n}\n\n\nngx_err_t\nngx_lock_fd(ngx_fd_t fd)\n{\n    struct flock  fl;\n\n    ngx_memzero(&fl, sizeof(struct flock));\n    fl.l_type = F_WRLCK;\n    fl.l_whence = SEEK_SET;\n\n    if (fcntl(fd, F_SETLKW, &fl) == -1) {\n        return ngx_errno;\n    }\n\n    return 0;\n}\n\n\nngx_err_t\nngx_unlock_fd(ngx_fd_t fd)\n{\n    struct flock  fl;\n\n    ngx_memzero(&fl, sizeof(struct flock));\n    fl.l_type = F_UNLCK;\n    fl.l_whence = SEEK_SET;\n\n    if (fcntl(fd, F_SETLK, &fl) == -1) {\n        return  ngx_errno;\n    }\n\n    return 0;\n}\n\n\n#if (NGX_HAVE_POSIX_FADVISE) && !(NGX_HAVE_F_READAHEAD)\n\nngx_int_t\nngx_read_ahead(ngx_fd_t fd, size_t n)\n{\n    int  err;\n\n    err = posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL);\n\n    if (err == 0) {\n        return 0;\n    }\n\n    ngx_set_errno(err);\n    return NGX_FILE_ERROR;\n}\n\n#endif\n\n\n#if (NGX_HAVE_O_DIRECT)\n\nngx_int_t\nngx_directio_on(ngx_fd_t fd)\n{\n    int  flags;\n\n    flags = fcntl(fd, F_GETFL);\n\n    if (flags == -1) {\n        return NGX_FILE_ERROR;\n    }\n\n    return fcntl(fd, F_SETFL, flags | O_DIRECT);\n}\n\n\nngx_int_t\nngx_directio_off(ngx_fd_t fd)\n{\n    int  flags;\n\n    flags = fcntl(fd, F_GETFL);\n\n    if (flags == -1) {\n        return NGX_FILE_ERROR;\n    }\n\n    return fcntl(fd, F_SETFL, flags & ~O_DIRECT);\n}\n\n#endif\n\n\n#if (NGX_HAVE_STATFS)\n\nsize_t\nngx_fs_bsize(u_char *name)\n{\n    struct statfs  fs;\n\n    if (statfs((char *) name, &fs) == -1) {\n        return 512;\n    }\n\n    if ((fs.f_bsize % 512) != 0) {\n        return 512;\n    }\n\n#if (NGX_LINUX)\n    if ((size_t) fs.f_bsize > ngx_pagesize) {\n        return 512;\n    }\n#endif\n\n    return (size_t) fs.f_bsize;\n}\n\n\noff_t\nngx_fs_available(u_char *name)\n{\n    struct statfs  fs;\n\n    if (statfs((char *) name, &fs) == -1) {\n        return NGX_MAX_OFF_T_VALUE;\n    }\n\n    return (off_t) fs.f_bavail * fs.f_bsize;\n}\n\n#elif (NGX_HAVE_STATVFS)\n\nsize_t\nngx_fs_bsize(u_char *name)\n{\n    struct statvfs  fs;\n\n    if (statvfs((char *) name, &fs) == -1) {\n        return 512;\n    }\n\n    if ((fs.f_frsize % 512) != 0) {\n        return 512;\n    }\n\n#if (NGX_LINUX)\n    if ((size_t) fs.f_frsize > ngx_pagesize) {\n        return 512;\n    }\n#endif\n\n    return (size_t) fs.f_frsize;\n}\n\n\noff_t\nngx_fs_available(u_char *name)\n{\n    struct statvfs  fs;\n\n    if (statvfs((char *) name, &fs) == -1) {\n        return NGX_MAX_OFF_T_VALUE;\n    }\n\n    return (off_t) fs.f_bavail * fs.f_frsize;\n}\n\n#else\n\nsize_t\nngx_fs_bsize(u_char *name)\n{\n    return 512;\n}\n\n\noff_t\nngx_fs_available(u_char *name)\n{\n    return NGX_MAX_OFF_T_VALUE;\n}\n\n#endif\n"
  },
  {
    "path": "src/os/unix/ngx_files.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_FILES_H_INCLUDED_\n#define _NGX_FILES_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\ntypedef int                      ngx_fd_t;\ntypedef struct stat              ngx_file_info_t;\ntypedef ino_t                    ngx_file_uniq_t;\n\n\ntypedef struct {\n    u_char                      *name;\n    size_t                       size;\n    void                        *addr;\n    ngx_fd_t                     fd;\n    ngx_log_t                   *log;\n} ngx_file_mapping_t;\n\n\ntypedef struct {\n    DIR                         *dir;\n    struct dirent               *de;\n    struct stat                  info;\n\n    unsigned                     type:8;\n    unsigned                     valid_info:1;\n} ngx_dir_t;\n\n\ntypedef struct {\n    size_t                       n;\n    glob_t                       pglob;\n    u_char                      *pattern;\n    ngx_log_t                   *log;\n    ngx_uint_t                   test;\n} ngx_glob_t;\n\n\n#define NGX_INVALID_FILE         -1\n#define NGX_FILE_ERROR           -1\n\n\n\n#ifdef __CYGWIN__\n\n#ifndef NGX_HAVE_CASELESS_FILESYSTEM\n#define NGX_HAVE_CASELESS_FILESYSTEM  1\n#endif\n\n#define ngx_open_file(name, mode, create, access)                            \\\n    open((const char *) name, mode|create|O_BINARY, access)\n\n#else\n\n#define ngx_open_file(name, mode, create, access)                            \\\n    open((const char *) name, mode|create, access)\n\n#endif\n\n#define ngx_open_file_n          \"open()\"\n\n#define NGX_FILE_RDONLY          O_RDONLY\n#define NGX_FILE_WRONLY          O_WRONLY\n#define NGX_FILE_RDWR            O_RDWR\n#define NGX_FILE_CREATE_OR_OPEN  O_CREAT\n#define NGX_FILE_OPEN            0\n#define NGX_FILE_TRUNCATE        (O_CREAT|O_TRUNC)\n#define NGX_FILE_APPEND          (O_WRONLY|O_APPEND)\n#define NGX_FILE_NONBLOCK        O_NONBLOCK\n\n#if (NGX_HAVE_OPENAT)\n#define NGX_FILE_NOFOLLOW        O_NOFOLLOW\n\n#if defined(O_DIRECTORY)\n#define NGX_FILE_DIRECTORY       O_DIRECTORY\n#else\n#define NGX_FILE_DIRECTORY       0\n#endif\n\n#if defined(O_SEARCH)\n#define NGX_FILE_SEARCH          (O_SEARCH|NGX_FILE_DIRECTORY)\n\n#elif defined(O_EXEC)\n#define NGX_FILE_SEARCH          (O_EXEC|NGX_FILE_DIRECTORY)\n\n#elif (NGX_HAVE_O_PATH)\n#define NGX_FILE_SEARCH          (O_PATH|O_RDONLY|NGX_FILE_DIRECTORY)\n\n#else\n#define NGX_FILE_SEARCH          (O_RDONLY|NGX_FILE_DIRECTORY)\n#endif\n\n#endif /* NGX_HAVE_OPENAT */\n\n#define NGX_FILE_DEFAULT_ACCESS  0644\n#define NGX_FILE_OWNER_ACCESS    0600\n\n\n#define ngx_close_file           close\n#define ngx_close_file_n         \"close()\"\n\n\n#define ngx_delete_file(name)    unlink((const char *) name)\n#define ngx_delete_file_n        \"unlink()\"\n\n\nngx_fd_t ngx_open_tempfile(u_char *name, ngx_uint_t persistent,\n    ngx_uint_t access);\n#define ngx_open_tempfile_n      \"open()\"\n\n\nssize_t ngx_read_file(ngx_file_t *file, u_char *buf, size_t size, off_t offset);\n#if (NGX_HAVE_PREAD)\n#define ngx_read_file_n          \"pread()\"\n#else\n#define ngx_read_file_n          \"read()\"\n#endif\n\nssize_t ngx_write_file(ngx_file_t *file, u_char *buf, size_t size,\n    off_t offset);\n\nssize_t ngx_write_chain_to_file(ngx_file_t *file, ngx_chain_t *ce,\n    off_t offset, ngx_pool_t *pool);\n\n\n#define ngx_read_fd              read\n#define ngx_read_fd_n            \"read()\"\n\n/*\n * we use inlined function instead of simple #define\n * because glibc 2.3 sets warn_unused_result attribute for write()\n * and in this case gcc 4.3 ignores (void) cast\n */\nstatic ngx_inline ssize_t\nngx_write_fd(ngx_fd_t fd, void *buf, size_t n)\n{\n    return write(fd, buf, n);\n}\n\n#define ngx_write_fd_n           \"write()\"\n\n\n#define ngx_write_console        ngx_write_fd\n\n\n#define ngx_linefeed(p)          *p++ = LF;\n#define NGX_LINEFEED_SIZE        1\n#define NGX_LINEFEED             \"\\x0a\"\n\n\n#define ngx_rename_file(o, n)    rename((const char *) o, (const char *) n)\n#define ngx_rename_file_n        \"rename()\"\n\n\n#define ngx_change_file_access(n, a) chmod((const char *) n, a)\n#define ngx_change_file_access_n \"chmod()\"\n\n\nngx_int_t ngx_set_file_time(u_char *name, ngx_fd_t fd, time_t s);\n#define ngx_set_file_time_n      \"utimes()\"\n\n\n#define ngx_file_info(file, sb)  stat((const char *) file, sb)\n#define ngx_file_info_n          \"stat()\"\n\n#define ngx_fd_info(fd, sb)      fstat(fd, sb)\n#define ngx_fd_info_n            \"fstat()\"\n\n#define ngx_link_info(file, sb)  lstat((const char *) file, sb)\n#define ngx_link_info_n          \"lstat()\"\n\n#define ngx_is_dir(sb)           (S_ISDIR((sb)->st_mode))\n#define ngx_is_file(sb)          (S_ISREG((sb)->st_mode))\n#define ngx_is_link(sb)          (S_ISLNK((sb)->st_mode))\n#define ngx_is_exec(sb)          (((sb)->st_mode & S_IXUSR) == S_IXUSR)\n#define ngx_file_access(sb)      ((sb)->st_mode & 0777)\n#define ngx_file_size(sb)        (sb)->st_size\n#define ngx_file_fs_size(sb)                                                 \\\n    (((sb)->st_blocks * 512 > (sb)->st_size                                  \\\n     && (sb)->st_blocks * 512 < (sb)->st_size + 8 * (sb)->st_blksize)        \\\n     ? (sb)->st_blocks * 512 : (sb)->st_size)\n#define ngx_file_mtime(sb)       (sb)->st_mtime\n#define ngx_file_uniq(sb)        (sb)->st_ino\n\n\nngx_int_t ngx_create_file_mapping(ngx_file_mapping_t *fm);\nvoid ngx_close_file_mapping(ngx_file_mapping_t *fm);\n\n\n#define ngx_realpath(p, r)       (u_char *) realpath((char *) p, (char *) r)\n#define ngx_realpath_n           \"realpath()\"\n#define ngx_getcwd(buf, size)    (getcwd((char *) buf, size) != NULL)\n#define ngx_getcwd_n             \"getcwd()\"\n#define ngx_path_separator(c)    ((c) == '/')\n\n\n#if defined(PATH_MAX)\n\n#define NGX_HAVE_MAX_PATH        1\n#define NGX_MAX_PATH             PATH_MAX\n\n#else\n\n#define NGX_MAX_PATH             4096\n\n#endif\n\n\nngx_int_t ngx_open_dir(ngx_str_t *name, ngx_dir_t *dir);\n#define ngx_open_dir_n           \"opendir()\"\n\n\n#define ngx_close_dir(d)         closedir((d)->dir)\n#define ngx_close_dir_n          \"closedir()\"\n\n\nngx_int_t ngx_read_dir(ngx_dir_t *dir);\n#define ngx_read_dir_n           \"readdir()\"\n\n\n#define ngx_create_dir(name, access) mkdir((const char *) name, access)\n#define ngx_create_dir_n         \"mkdir()\"\n\n\n#define ngx_delete_dir(name)     rmdir((const char *) name)\n#define ngx_delete_dir_n         \"rmdir()\"\n\n\n#define ngx_dir_access(a)        (a | (a & 0444) >> 2)\n\n\n#define ngx_de_name(dir)         ((u_char *) (dir)->de->d_name)\n#if (NGX_HAVE_D_NAMLEN)\n#define ngx_de_namelen(dir)      (dir)->de->d_namlen\n#else\n#define ngx_de_namelen(dir)      ngx_strlen((dir)->de->d_name)\n#endif\n\nstatic ngx_inline ngx_int_t\nngx_de_info(u_char *name, ngx_dir_t *dir)\n{\n    dir->type = 0;\n    return stat((const char *) name, &dir->info);\n}\n\n#define ngx_de_info_n            \"stat()\"\n#define ngx_de_link_info(name, dir)  lstat((const char *) name, &(dir)->info)\n#define ngx_de_link_info_n       \"lstat()\"\n\n#if (NGX_HAVE_D_TYPE)\n\n/*\n * some file systems (e.g. XFS on Linux and CD9660 on FreeBSD)\n * do not set dirent.d_type\n */\n\n#define ngx_de_is_dir(dir)                                                   \\\n    (((dir)->type) ? ((dir)->type == DT_DIR) : (S_ISDIR((dir)->info.st_mode)))\n#define ngx_de_is_file(dir)                                                  \\\n    (((dir)->type) ? ((dir)->type == DT_REG) : (S_ISREG((dir)->info.st_mode)))\n#define ngx_de_is_link(dir)                                                  \\\n    (((dir)->type) ? ((dir)->type == DT_LNK) : (S_ISLNK((dir)->info.st_mode)))\n\n#else\n\n#define ngx_de_is_dir(dir)       (S_ISDIR((dir)->info.st_mode))\n#define ngx_de_is_file(dir)      (S_ISREG((dir)->info.st_mode))\n#define ngx_de_is_link(dir)      (S_ISLNK((dir)->info.st_mode))\n\n#endif\n\n#define ngx_de_access(dir)       (((dir)->info.st_mode) & 0777)\n#define ngx_de_size(dir)         (dir)->info.st_size\n#define ngx_de_fs_size(dir)                                                  \\\n    ngx_max((dir)->info.st_size, (dir)->info.st_blocks * 512)\n#define ngx_de_mtime(dir)        (dir)->info.st_mtime\n\n\nngx_int_t ngx_open_glob(ngx_glob_t *gl);\n#define ngx_open_glob_n          \"glob()\"\nngx_int_t ngx_read_glob(ngx_glob_t *gl, ngx_str_t *name);\nvoid ngx_close_glob(ngx_glob_t *gl);\n\n\nngx_err_t ngx_trylock_fd(ngx_fd_t fd);\nngx_err_t ngx_lock_fd(ngx_fd_t fd);\nngx_err_t ngx_unlock_fd(ngx_fd_t fd);\n\n#define ngx_trylock_fd_n         \"fcntl(F_SETLK, F_WRLCK)\"\n#define ngx_lock_fd_n            \"fcntl(F_SETLKW, F_WRLCK)\"\n#define ngx_unlock_fd_n          \"fcntl(F_SETLK, F_UNLCK)\"\n\n\n#if (NGX_HAVE_F_READAHEAD)\n\n#define NGX_HAVE_READ_AHEAD      1\n\n#define ngx_read_ahead(fd, n)    fcntl(fd, F_READAHEAD, (int) n)\n#define ngx_read_ahead_n         \"fcntl(fd, F_READAHEAD)\"\n\n#elif (NGX_HAVE_POSIX_FADVISE)\n\n#define NGX_HAVE_READ_AHEAD      1\n\nngx_int_t ngx_read_ahead(ngx_fd_t fd, size_t n);\n#define ngx_read_ahead_n         \"posix_fadvise(POSIX_FADV_SEQUENTIAL)\"\n\n#else\n\n#define ngx_read_ahead(fd, n)    0\n#define ngx_read_ahead_n         \"ngx_read_ahead_n\"\n\n#endif\n\n\n#if (NGX_HAVE_O_DIRECT)\n\nngx_int_t ngx_directio_on(ngx_fd_t fd);\n#define ngx_directio_on_n        \"fcntl(O_DIRECT)\"\n\nngx_int_t ngx_directio_off(ngx_fd_t fd);\n#define ngx_directio_off_n       \"fcntl(!O_DIRECT)\"\n\n#elif (NGX_HAVE_F_NOCACHE)\n\n#define ngx_directio_on(fd)      fcntl(fd, F_NOCACHE, 1)\n#define ngx_directio_on_n        \"fcntl(F_NOCACHE, 1)\"\n\n#elif (NGX_HAVE_DIRECTIO)\n\n#define ngx_directio_on(fd)      directio(fd, DIRECTIO_ON)\n#define ngx_directio_on_n        \"directio(DIRECTIO_ON)\"\n\n#else\n\n#define ngx_directio_on(fd)      0\n#define ngx_directio_on_n        \"ngx_directio_on_n\"\n\n#endif\n\nsize_t ngx_fs_bsize(u_char *name);\noff_t ngx_fs_available(u_char *name);\n\n\n#if (NGX_HAVE_OPENAT)\n\n#define ngx_openat_file(fd, name, mode, create, access)                      \\\n    openat(fd, (const char *) name, mode|create, access)\n\n#define ngx_openat_file_n        \"openat()\"\n\n#define ngx_file_at_info(fd, name, sb, flag)                                 \\\n    fstatat(fd, (const char *) name, sb, flag)\n\n#define ngx_file_at_info_n       \"fstatat()\"\n\n#define NGX_AT_FDCWD             (ngx_fd_t) AT_FDCWD\n\n#endif\n\n\n#define ngx_stdout               STDOUT_FILENO\n#define ngx_stderr               STDERR_FILENO\n#define ngx_set_stderr(fd)       dup2(fd, STDERR_FILENO)\n#define ngx_set_stderr_n         \"dup2(STDERR_FILENO)\"\n\n\n#if (NGX_HAVE_FILE_AIO)\n\nngx_int_t ngx_file_aio_init(ngx_file_t *file, ngx_pool_t *pool);\nssize_t ngx_file_aio_read(ngx_file_t *file, u_char *buf, size_t size,\n    off_t offset, ngx_pool_t *pool);\n\nextern ngx_uint_t  ngx_file_aio;\n\n#endif\n\n#if (NGX_THREADS)\nssize_t ngx_thread_read(ngx_file_t *file, u_char *buf, size_t size,\n    off_t offset, ngx_pool_t *pool);\nssize_t ngx_thread_write_chain_to_file(ngx_file_t *file, ngx_chain_t *cl,\n    off_t offset, ngx_pool_t *pool);\n#endif\n\n\n#endif /* _NGX_FILES_H_INCLUDED_ */\n"
  },
  {
    "path": "src/os/unix/ngx_freebsd.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_FREEBSD_H_INCLUDED_\n#define _NGX_FREEBSD_H_INCLUDED_\n\n\nvoid ngx_debug_init(void);\nngx_chain_t *ngx_freebsd_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in,\n    off_t limit);\n\nextern int         ngx_freebsd_kern_osreldate;\nextern int         ngx_freebsd_hw_ncpu;\nextern u_long      ngx_freebsd_net_inet_tcp_sendspace;\n\nextern ngx_uint_t  ngx_freebsd_sendfile_nbytes_bug;\nextern ngx_uint_t  ngx_freebsd_use_tcp_nopush;\nextern ngx_uint_t  ngx_debug_malloc;\n\n\n#endif /* _NGX_FREEBSD_H_INCLUDED_ */\n"
  },
  {
    "path": "src/os/unix/ngx_freebsd_config.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_FREEBSD_CONFIG_H_INCLUDED_\n#define _NGX_FREEBSD_CONFIG_H_INCLUDED_\n\n\n#include <sys/types.h>\n#include <sys/time.h>\n#include <unistd.h>\n#include <stdarg.h>\n#include <stddef.h>             /* offsetof() */\n#include <stdio.h>\n#include <stdlib.h>\n#include <ctype.h>\n#include <errno.h>\n#include <string.h>\n#include <signal.h>\n#include <pwd.h>\n#include <grp.h>\n#include <dirent.h>\n#include <glob.h>\n#include <time.h>\n#include <sys/param.h>          /* ALIGN() */\n#include <sys/mount.h>          /* statfs() */\n\n#include <sys/filio.h>          /* FIONBIO */\n#include <sys/uio.h>\n#include <sys/stat.h>\n#include <fcntl.h>\n\n#include <sys/wait.h>\n#include <sys/mman.h>\n#include <sys/resource.h>\n#include <sched.h>\n\n#include <sys/socket.h>\n#include <netinet/in.h>\n#include <netinet/tcp.h>        /* TCP_NODELAY, TCP_NOPUSH */\n#include <arpa/inet.h>\n#include <netdb.h>\n#include <sys/un.h>\n\n#include <libutil.h>            /* setproctitle() before 4.1 */\n#include <osreldate.h>\n#include <sys/sysctl.h>\n\n#include <dlfcn.h>\n\n\n#if __FreeBSD_version < 400017\n\n/*\n * FreeBSD 3.x has no CMSG_SPACE() and CMSG_LEN() and has the broken CMSG_DATA()\n */\n\n#undef  CMSG_SPACE\n#define CMSG_SPACE(l)       (ALIGN(sizeof(struct cmsghdr)) + ALIGN(l))\n\n#undef  CMSG_LEN\n#define CMSG_LEN(l)         (ALIGN(sizeof(struct cmsghdr)) + (l))\n\n#undef  CMSG_DATA\n#define CMSG_DATA(cmsg)     ((u_char *)(cmsg) + ALIGN(sizeof(struct cmsghdr)))\n\n#endif\n\n\n#include <ngx_auto_config.h>\n\n\n#if (NGX_HAVE_POSIX_SEM)\n#include <semaphore.h>\n#endif\n\n\n#if (NGX_HAVE_POLL)\n#include <poll.h>\n#endif\n\n\n#if (NGX_HAVE_KQUEUE)\n#include <sys/event.h>\n#endif\n\n\n#if (NGX_HAVE_FILE_AIO)\n\n#include <aio.h>\ntypedef struct aiocb  ngx_aiocb_t;\n\n#if (__FreeBSD_version < 700005 && !defined __DragonFly__)\n#define sival_ptr     sigval_ptr\n#endif\n\n#endif\n\n\n#define NGX_LISTEN_BACKLOG        -1\n\n\n#ifdef __DragonFly__\n#define NGX_KEEPALIVE_FACTOR      1000\n#endif\n\n\n#ifndef IOV_MAX\n#define IOV_MAX   1024\n#endif\n\n\n#ifndef NGX_HAVE_INHERITED_NONBLOCK\n#define NGX_HAVE_INHERITED_NONBLOCK  1\n#endif\n\n\n#define NGX_HAVE_OS_SPECIFIC_INIT    1\n#define NGX_HAVE_DEBUG_MALLOC        1\n\n\nextern char **environ;\nextern char  *malloc_options;\n\n\n#endif /* _NGX_FREEBSD_CONFIG_H_INCLUDED_ */\n"
  },
  {
    "path": "src/os/unix/ngx_freebsd_init.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\n/* FreeBSD 3.0 at least */\nchar    ngx_freebsd_kern_ostype[16];\nchar    ngx_freebsd_kern_osrelease[128];\nint     ngx_freebsd_kern_osreldate;\nint     ngx_freebsd_hw_ncpu;\nint     ngx_freebsd_kern_ipc_somaxconn;\nu_long  ngx_freebsd_net_inet_tcp_sendspace;\n\n/* FreeBSD 4.9 */\nint     ngx_freebsd_machdep_hlt_logical_cpus;\n\n\nngx_uint_t  ngx_freebsd_sendfile_nbytes_bug;\nngx_uint_t  ngx_freebsd_use_tcp_nopush;\n\nngx_uint_t  ngx_debug_malloc;\n\n\nstatic ngx_os_io_t ngx_freebsd_io = {\n    ngx_unix_recv,\n    ngx_readv_chain,\n    ngx_udp_unix_recv,\n    ngx_unix_send,\n    ngx_udp_unix_send,\n    ngx_udp_unix_sendmsg_chain,\n#if (NGX_HAVE_SENDFILE)\n    ngx_freebsd_sendfile_chain,\n    NGX_IO_SENDFILE\n#else\n    ngx_writev_chain,\n    0\n#endif\n};\n\n\ntypedef struct {\n    char        *name;\n    void        *value;\n    size_t       size;\n    ngx_uint_t   exists;\n} sysctl_t;\n\n\nsysctl_t sysctls[] = {\n    { \"hw.ncpu\",\n      &ngx_freebsd_hw_ncpu,\n      sizeof(ngx_freebsd_hw_ncpu), 0 },\n\n    { \"machdep.hlt_logical_cpus\",\n      &ngx_freebsd_machdep_hlt_logical_cpus,\n      sizeof(ngx_freebsd_machdep_hlt_logical_cpus), 0 },\n\n    { \"net.inet.tcp.sendspace\",\n      &ngx_freebsd_net_inet_tcp_sendspace,\n      sizeof(ngx_freebsd_net_inet_tcp_sendspace), 0 },\n\n    { \"kern.ipc.somaxconn\",\n      &ngx_freebsd_kern_ipc_somaxconn,\n      sizeof(ngx_freebsd_kern_ipc_somaxconn), 0 },\n\n    { NULL, NULL, 0, 0 }\n};\n\n\nvoid\nngx_debug_init(void)\n{\n#if (NGX_DEBUG_MALLOC)\n\n#if __FreeBSD_version >= 500014 && __FreeBSD_version < 1000011\n    _malloc_options = \"J\";\n#elif __FreeBSD_version < 500014\n    malloc_options = \"J\";\n#endif\n\n    ngx_debug_malloc = 1;\n\n#else\n    char  *mo;\n\n    mo = getenv(\"MALLOC_OPTIONS\");\n\n    if (mo && ngx_strchr(mo, 'J')) {\n        ngx_debug_malloc = 1;\n    }\n#endif\n}\n\n\nngx_int_t\nngx_os_specific_init(ngx_log_t *log)\n{\n    int         version;\n    size_t      size;\n    ngx_err_t   err;\n    ngx_uint_t  i;\n\n    size = sizeof(ngx_freebsd_kern_ostype);\n    if (sysctlbyname(\"kern.ostype\",\n                     ngx_freebsd_kern_ostype, &size, NULL, 0) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,\n                      \"sysctlbyname(kern.ostype) failed\");\n\n        if (ngx_errno != NGX_ENOMEM) {\n            return NGX_ERROR;\n        }\n\n        ngx_freebsd_kern_ostype[size - 1] = '\\0';\n    }\n\n    size = sizeof(ngx_freebsd_kern_osrelease);\n    if (sysctlbyname(\"kern.osrelease\",\n                     ngx_freebsd_kern_osrelease, &size, NULL, 0) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,\n                      \"sysctlbyname(kern.osrelease) failed\");\n\n        if (ngx_errno != NGX_ENOMEM) {\n            return NGX_ERROR;\n        }\n\n        ngx_freebsd_kern_osrelease[size - 1] = '\\0';\n    }\n\n\n    size = sizeof(int);\n    if (sysctlbyname(\"kern.osreldate\",\n                     &ngx_freebsd_kern_osreldate, &size, NULL, 0) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,\n                      \"sysctlbyname(kern.osreldate) failed\");\n        return NGX_ERROR;\n    }\n\n    version = ngx_freebsd_kern_osreldate;\n\n\n#if (NGX_HAVE_SENDFILE)\n\n    /*\n     * The determination of the sendfile() \"nbytes bug\" is complex enough.\n     * There are two sendfile() syscalls: a new #393 has no bug while\n     * an old #336 has the bug in some versions and has not in others.\n     * Besides libc_r wrapper also emulates the bug in some versions.\n     * There is no way to say exactly if syscall #336 in FreeBSD circa 4.6\n     * has the bug.  We use the algorithm that is correct at least for\n     * RELEASEs and for syscalls only (not libc_r wrapper).\n     *\n     * 4.6.1-RELEASE and below have the bug\n     * 4.6.2-RELEASE and above have the new syscall\n     *\n     * We detect the new sendfile() syscall available at the compile time\n     * to allow an old binary to run correctly on an updated FreeBSD system.\n     */\n\n#if (__FreeBSD__ == 4 && __FreeBSD_version >= 460102) \\\n    || __FreeBSD_version == 460002 || __FreeBSD_version >= 500039\n\n    /* a new syscall without the bug */\n\n    ngx_freebsd_sendfile_nbytes_bug = 0;\n\n#else\n\n    /* an old syscall that may have the bug */\n\n    ngx_freebsd_sendfile_nbytes_bug = 1;\n\n#endif\n\n#endif /* NGX_HAVE_SENDFILE */\n\n\n    if ((version < 500000 && version >= 440003) || version >= 500017) {\n        ngx_freebsd_use_tcp_nopush = 1;\n    }\n\n\n    for (i = 0; sysctls[i].name; i++) {\n        size = sysctls[i].size;\n\n        if (sysctlbyname(sysctls[i].name, sysctls[i].value, &size, NULL, 0)\n            == 0)\n        {\n            sysctls[i].exists = 1;\n            continue;\n        }\n\n        err = ngx_errno;\n\n        if (err == NGX_ENOENT) {\n            continue;\n        }\n\n        ngx_log_error(NGX_LOG_ALERT, log, err,\n                      \"sysctlbyname(%s) failed\", sysctls[i].name);\n        return NGX_ERROR;\n    }\n\n    if (ngx_freebsd_machdep_hlt_logical_cpus) {\n        ngx_ncpu = ngx_freebsd_hw_ncpu / 2;\n\n    } else {\n        ngx_ncpu = ngx_freebsd_hw_ncpu;\n    }\n\n    if (version < 600008 && ngx_freebsd_kern_ipc_somaxconn > 32767) {\n        ngx_log_error(NGX_LOG_ALERT, log, 0,\n                      \"sysctl kern.ipc.somaxconn must be less than 32768\");\n        return NGX_ERROR;\n    }\n\n    ngx_tcp_nodelay_and_tcp_nopush = 1;\n\n    ngx_os_io = ngx_freebsd_io;\n\n    return NGX_OK;\n}\n\n\nvoid\nngx_os_specific_status(ngx_log_t *log)\n{\n    u_long      value;\n    ngx_uint_t  i;\n\n    ngx_log_error(NGX_LOG_NOTICE, log, 0, \"OS: %s %s\",\n                  ngx_freebsd_kern_ostype, ngx_freebsd_kern_osrelease);\n\n#ifdef __DragonFly_version\n    ngx_log_error(NGX_LOG_NOTICE, log, 0,\n                  \"kern.osreldate: %d, built on %d\",\n                  ngx_freebsd_kern_osreldate, __DragonFly_version);\n#else\n    ngx_log_error(NGX_LOG_NOTICE, log, 0,\n                  \"kern.osreldate: %d, built on %d\",\n                  ngx_freebsd_kern_osreldate, __FreeBSD_version);\n#endif\n\n    for (i = 0; sysctls[i].name; i++) {\n        if (sysctls[i].exists) {\n            if (sysctls[i].size == sizeof(long)) {\n                value = *(long *) sysctls[i].value;\n\n            } else {\n                value = *(int *) sysctls[i].value;\n            }\n\n            ngx_log_error(NGX_LOG_NOTICE, log, 0, \"%s: %l\",\n                          sysctls[i].name, value);\n        }\n    }\n}\n"
  },
  {
    "path": "src/os/unix/ngx_freebsd_sendfile_chain.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n\n\n/*\n * Although FreeBSD sendfile() allows to pass a header and a trailer,\n * it cannot send a header with a part of the file in one packet until\n * FreeBSD 5.3.  Besides, over the fast ethernet connection sendfile()\n * may send the partially filled packets, i.e. the 8 file pages may be sent\n * as the 11 full 1460-bytes packets, then one incomplete 324-bytes packet,\n * and then again the 11 full 1460-bytes packets.\n *\n * Therefore we use the TCP_NOPUSH option (similar to Linux's TCP_CORK)\n * to postpone the sending - it not only sends a header and the first part of\n * the file in one packet, but also sends the file pages in the full packets.\n *\n * But until FreeBSD 4.5 turning TCP_NOPUSH off does not flush a pending\n * data that less than MSS, so that data may be sent with 5 second delay.\n * So we do not use TCP_NOPUSH on FreeBSD prior to 4.5, although it can be used\n * for non-keepalive HTTP connections.\n */\n\n\nngx_chain_t *\nngx_freebsd_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)\n{\n    int              rc, flags;\n    off_t            send, prev_send, sent;\n    size_t           file_size;\n    ssize_t          n;\n    ngx_err_t        err;\n    ngx_buf_t       *file;\n    ngx_uint_t       eintr, eagain;\n#if (NGX_HAVE_SENDFILE_NODISKIO)\n    ngx_uint_t       ebusy;\n#endif\n    ngx_event_t     *wev;\n    ngx_chain_t     *cl;\n    ngx_iovec_t      header, trailer;\n    struct sf_hdtr   hdtr;\n    struct iovec     headers[NGX_IOVS_PREALLOCATE];\n    struct iovec     trailers[NGX_IOVS_PREALLOCATE];\n\n    wev = c->write;\n\n    if (!wev->ready) {\n        return in;\n    }\n\n#if (NGX_HAVE_KQUEUE)\n\n    if ((ngx_event_flags & NGX_USE_KQUEUE_EVENT) && wev->pending_eof) {\n        (void) ngx_connection_error(c, wev->kq_errno,\n                               \"kevent() reported about an closed connection\");\n        wev->error = 1;\n        return NGX_CHAIN_ERROR;\n    }\n\n#endif\n\n    /* the maximum limit size is the maximum size_t value - the page size */\n\n    if (limit == 0 || limit > (off_t) (NGX_MAX_SIZE_T_VALUE - ngx_pagesize)) {\n        limit = NGX_MAX_SIZE_T_VALUE - ngx_pagesize;\n    }\n\n    send = 0;\n    eagain = 0;\n    flags = 0;\n\n    header.iovs = headers;\n    header.nalloc = NGX_IOVS_PREALLOCATE;\n\n    trailer.iovs = trailers;\n    trailer.nalloc = NGX_IOVS_PREALLOCATE;\n\n    for ( ;; ) {\n        eintr = 0;\n#if (NGX_HAVE_SENDFILE_NODISKIO)\n        ebusy = 0;\n#endif\n        prev_send = send;\n\n        /* create the header iovec and coalesce the neighbouring bufs */\n\n        cl = ngx_output_chain_to_iovec(&header, in, limit - send, c->log);\n\n        if (cl == NGX_CHAIN_ERROR) {\n            return NGX_CHAIN_ERROR;\n        }\n\n        send += header.size;\n\n        if (cl && cl->buf->in_file && send < limit) {\n            file = cl->buf;\n\n            /* coalesce the neighbouring file bufs */\n\n            file_size = (size_t) ngx_chain_coalesce_file(&cl, limit - send);\n\n            send += file_size;\n\n            if (send < limit) {\n\n                /*\n                 * create the trailer iovec and coalesce the neighbouring bufs\n                 */\n\n                cl = ngx_output_chain_to_iovec(&trailer, cl, limit - send,\n                                               c->log);\n                if (cl == NGX_CHAIN_ERROR) {\n                    return NGX_CHAIN_ERROR;\n                }\n\n                send += trailer.size;\n\n            } else {\n                trailer.count = 0;\n            }\n\n            if (ngx_freebsd_use_tcp_nopush\n                && c->tcp_nopush == NGX_TCP_NOPUSH_UNSET)\n            {\n                if (ngx_tcp_nopush(c->fd) == -1) {\n                    err = ngx_socket_errno;\n\n                    /*\n                     * there is a tiny chance to be interrupted, however,\n                     * we continue a processing without the TCP_NOPUSH\n                     */\n\n                    if (err != NGX_EINTR) {\n                        wev->error = 1;\n                        (void) ngx_connection_error(c, err,\n                                                    ngx_tcp_nopush_n \" failed\");\n                        return NGX_CHAIN_ERROR;\n                    }\n\n                } else {\n                    c->tcp_nopush = NGX_TCP_NOPUSH_SET;\n\n                    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                                   \"tcp_nopush\");\n                }\n            }\n\n            /*\n             * sendfile() does unneeded work if sf_hdtr's count is 0,\n             * but corresponding pointer is not NULL\n             */\n\n            hdtr.headers = header.count ? header.iovs : NULL;\n            hdtr.hdr_cnt = header.count;\n            hdtr.trailers = trailer.count ? trailer.iovs : NULL;\n            hdtr.trl_cnt = trailer.count;\n\n            /*\n             * the \"nbytes bug\" of the old sendfile() syscall:\n             * http://bugs.freebsd.org/33771\n             */\n\n            if (!ngx_freebsd_sendfile_nbytes_bug) {\n                header.size = 0;\n            }\n\n            sent = 0;\n\n#if (NGX_HAVE_SENDFILE_NODISKIO)\n\n            flags = (c->busy_count <= 2) ? SF_NODISKIO : 0;\n\n            if (file->file->directio) {\n                flags |= SF_NOCACHE;\n            }\n\n#endif\n\n            rc = sendfile(file->file->fd, c->fd, file->file_pos,\n                          file_size + header.size, &hdtr, &sent, flags);\n\n            if (rc == -1) {\n                err = ngx_errno;\n\n                switch (err) {\n                case NGX_EAGAIN:\n                    eagain = 1;\n                    break;\n\n                case NGX_EINTR:\n                    eintr = 1;\n                    break;\n\n#if (NGX_HAVE_SENDFILE_NODISKIO)\n                case NGX_EBUSY:\n                    ebusy = 1;\n                    break;\n#endif\n\n                default:\n                    wev->error = 1;\n                    (void) ngx_connection_error(c, err, \"sendfile() failed\");\n                    return NGX_CHAIN_ERROR;\n                }\n\n                ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, err,\n                               \"sendfile() sent only %O bytes\", sent);\n\n            /*\n             * sendfile() in FreeBSD 3.x-4.x may return value >= 0\n             * on success, although only 0 is documented\n             */\n\n            } else if (rc >= 0 && sent == 0) {\n\n                /*\n                 * if rc is OK and sent equal to zero, then someone\n                 * has truncated the file, so the offset became beyond\n                 * the end of the file\n                 */\n\n                ngx_log_error(NGX_LOG_ALERT, c->log, 0,\n                         \"sendfile() reported that \\\"%s\\\" was truncated at %O\",\n                         file->file->name.data, file->file_pos);\n\n                return NGX_CHAIN_ERROR;\n            }\n\n            ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                           \"sendfile: %d, @%O %O:%uz\",\n                           rc, file->file_pos, sent, file_size + header.size);\n\n        } else {\n            n = ngx_writev(c, &header);\n\n            if (n == NGX_ERROR) {\n                return NGX_CHAIN_ERROR;\n            }\n\n            sent = (n == NGX_AGAIN) ? 0 : n;\n        }\n\n        c->sent += sent;\n\n        in = ngx_chain_update_sent(in, sent);\n\n#if (NGX_HAVE_SENDFILE_NODISKIO)\n\n        if (ebusy) {\n            if (sent == 0) {\n                c->busy_count++;\n\n                ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                               \"sendfile() busy, count:%d\", c->busy_count);\n\n            } else {\n                c->busy_count = 0;\n            }\n\n            if (wev->posted) {\n                ngx_delete_posted_event(wev);\n            }\n\n            ngx_post_event(wev, &ngx_posted_next_events);\n\n            wev->ready = 0;\n            return in;\n        }\n\n        c->busy_count = 0;\n\n#endif\n\n        if (eagain) {\n\n            /*\n             * sendfile() may return EAGAIN, even if it has sent a whole file\n             * part, it indicates that the successive sendfile() call would\n             * return EAGAIN right away and would not send anything.\n             * We use it as a hint.\n             */\n\n            wev->ready = 0;\n            return in;\n        }\n\n        if (eintr) {\n            send = prev_send + sent;\n            continue;\n        }\n\n        if (send - prev_send != sent) {\n            wev->ready = 0;\n            return in;\n        }\n\n        if (send >= limit || in == NULL) {\n            return in;\n        }\n    }\n}\n"
  },
  {
    "path": "src/os/unix/ngx_gcc_atomic_amd64.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#if (NGX_SMP)\n#define NGX_SMP_LOCK  \"lock;\"\n#else\n#define NGX_SMP_LOCK\n#endif\n\n\n/*\n * \"cmpxchgq  r, [m]\":\n *\n *     if (rax == [m]) {\n *         zf = 1;\n *         [m] = r;\n *     } else {\n *         zf = 0;\n *         rax = [m];\n *     }\n *\n *\n * The \"r\" is any register, %rax (%r0) - %r16.\n * The \"=a\" and \"a\" are the %rax register.\n * Although we can return result in any register, we use \"a\" because it is\n * used in cmpxchgq anyway.  The result is actually in %al but not in $rax,\n * however as the code is inlined gcc can test %al as well as %rax.\n *\n * The \"cc\" means that flags were changed.\n */\n\nstatic ngx_inline ngx_atomic_uint_t\nngx_atomic_cmp_set(ngx_atomic_t *lock, ngx_atomic_uint_t old,\n    ngx_atomic_uint_t set)\n{\n    u_char  res;\n\n    __asm__ volatile (\n\n         NGX_SMP_LOCK\n    \"    cmpxchgq  %3, %1;   \"\n    \"    sete      %0;       \"\n\n    : \"=a\" (res) : \"m\" (*lock), \"a\" (old), \"r\" (set) : \"cc\", \"memory\");\n\n    return res;\n}\n\n\n/*\n * \"xaddq  r, [m]\":\n *\n *     temp = [m];\n *     [m] += r;\n *     r = temp;\n *\n *\n * The \"+r\" is any register, %rax (%r0) - %r16.\n * The \"cc\" means that flags were changed.\n */\n\nstatic ngx_inline ngx_atomic_int_t\nngx_atomic_fetch_add(ngx_atomic_t *value, ngx_atomic_int_t add)\n{\n    __asm__ volatile (\n\n         NGX_SMP_LOCK\n    \"    xaddq  %0, %1;   \"\n\n    : \"+r\" (add) : \"m\" (*value) : \"cc\", \"memory\");\n\n    return add;\n}\n\n\n#define ngx_memory_barrier()    __asm__ volatile (\"\" ::: \"memory\")\n\n#define ngx_cpu_pause()         __asm__ (\"pause\")\n"
  },
  {
    "path": "src/os/unix/ngx_gcc_atomic_ppc.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n/*\n * The ppc assembler treats \";\" as comment, so we have to use \"\\n\".\n * The minus in \"bne-\" is a hint for the branch prediction unit that\n * this branch is unlikely to be taken.\n * The \"1b\" means the nearest backward label \"1\" and the \"1f\" means\n * the nearest forward label \"1\".\n *\n * The \"b\" means that the base registers can be used only, i.e.\n * any register except r0.  The r0 register always has a zero value and\n * could not be used in \"addi  r0, r0, 1\".\n * The \"=&b\" means that no input registers can be used.\n *\n * \"sync\"    read and write barriers\n * \"isync\"   read barrier, is faster than \"sync\"\n * \"eieio\"   write barrier, is faster than \"sync\"\n * \"lwsync\"  write barrier, is faster than \"eieio\" on ppc64\n */\n\n#if (NGX_PTR_SIZE == 8)\n\nstatic ngx_inline ngx_atomic_uint_t\nngx_atomic_cmp_set(ngx_atomic_t *lock, ngx_atomic_uint_t old,\n    ngx_atomic_uint_t set)\n{\n    ngx_atomic_uint_t  res, temp;\n\n    __asm__ volatile (\n\n    \"    li      %0, 0       \\n\" /* preset \"0\" to \"res\"                      */\n    \"    lwsync              \\n\" /* write barrier                            */\n    \"1:                      \\n\"\n    \"    ldarx   %1, 0, %2   \\n\" /* load from [lock] into \"temp\"             */\n                                 /*   and store reservation                  */\n    \"    cmpd    %1, %3      \\n\" /* compare \"temp\" and \"old\"                 */\n    \"    bne-    2f          \\n\" /* not equal                                */\n    \"    stdcx.  %4, 0, %2   \\n\" /* store \"set\" into [lock] if reservation   */\n                                 /*   is not cleared                         */\n    \"    bne-    1b          \\n\" /* the reservation was cleared              */\n    \"    isync               \\n\" /* read barrier                             */\n    \"    li      %0, 1       \\n\" /* set \"1\" to \"res\"                         */\n    \"2:                      \\n\"\n\n    : \"=&b\" (res), \"=&b\" (temp)\n    : \"b\" (lock), \"b\" (old), \"b\" (set)\n    : \"cc\", \"memory\");\n\n    return res;\n}\n\n\nstatic ngx_inline ngx_atomic_int_t\nngx_atomic_fetch_add(ngx_atomic_t *value, ngx_atomic_int_t add)\n{\n    ngx_atomic_uint_t  res, temp;\n\n    __asm__ volatile (\n\n    \"    lwsync              \\n\" /* write barrier                            */\n    \"1:  ldarx   %0, 0, %2   \\n\" /* load from [value] into \"res\"             */\n                                 /*   and store reservation                  */\n    \"    add     %1, %0, %3  \\n\" /* \"res\" + \"add\" store in \"temp\"            */\n    \"    stdcx.  %1, 0, %2   \\n\" /* store \"temp\" into [value] if reservation */\n                                 /*   is not cleared                         */\n    \"    bne-    1b          \\n\" /* try again if reservation was cleared     */\n    \"    isync               \\n\" /* read barrier                             */\n\n    : \"=&b\" (res), \"=&b\" (temp)\n    : \"b\" (value), \"b\" (add)\n    : \"cc\", \"memory\");\n\n    return res;\n}\n\n\n#if (NGX_SMP)\n#define ngx_memory_barrier()                                                  \\\n    __asm__ volatile (\"isync  \\n  lwsync  \\n\" ::: \"memory\")\n#else\n#define ngx_memory_barrier()   __asm__ volatile (\"\" ::: \"memory\")\n#endif\n\n#else\n\nstatic ngx_inline ngx_atomic_uint_t\nngx_atomic_cmp_set(ngx_atomic_t *lock, ngx_atomic_uint_t old,\n    ngx_atomic_uint_t set)\n{\n    ngx_atomic_uint_t  res, temp;\n\n    __asm__ volatile (\n\n    \"    li      %0, 0       \\n\" /* preset \"0\" to \"res\"                      */\n    \"    eieio               \\n\" /* write barrier                            */\n    \"1:                      \\n\"\n    \"    lwarx   %1, 0, %2   \\n\" /* load from [lock] into \"temp\"             */\n                                 /*   and store reservation                  */\n    \"    cmpw    %1, %3      \\n\" /* compare \"temp\" and \"old\"                 */\n    \"    bne-    2f          \\n\" /* not equal                                */\n    \"    stwcx.  %4, 0, %2   \\n\" /* store \"set\" into [lock] if reservation   */\n                                 /*   is not cleared                         */\n    \"    bne-    1b          \\n\" /* the reservation was cleared              */\n    \"    isync               \\n\" /* read barrier                             */\n    \"    li      %0, 1       \\n\" /* set \"1\" to \"res\"                         */\n    \"2:                      \\n\"\n\n    : \"=&b\" (res), \"=&b\" (temp)\n    : \"b\" (lock), \"b\" (old), \"b\" (set)\n    : \"cc\", \"memory\");\n\n    return res;\n}\n\n\nstatic ngx_inline ngx_atomic_int_t\nngx_atomic_fetch_add(ngx_atomic_t *value, ngx_atomic_int_t add)\n{\n    ngx_atomic_uint_t  res, temp;\n\n    __asm__ volatile (\n\n    \"    eieio               \\n\" /* write barrier                            */\n    \"1:  lwarx   %0, 0, %2   \\n\" /* load from [value] into \"res\"             */\n                                 /*   and store reservation                  */\n    \"    add     %1, %0, %3  \\n\" /* \"res\" + \"add\" store in \"temp\"            */\n    \"    stwcx.  %1, 0, %2   \\n\" /* store \"temp\" into [value] if reservation */\n                                 /*   is not cleared                         */\n    \"    bne-    1b          \\n\" /* try again if reservation was cleared     */\n    \"    isync               \\n\" /* read barrier                             */\n\n    : \"=&b\" (res), \"=&b\" (temp)\n    : \"b\" (value), \"b\" (add)\n    : \"cc\", \"memory\");\n\n    return res;\n}\n\n\n#if (NGX_SMP)\n#define ngx_memory_barrier()                                                  \\\n    __asm__ volatile (\"isync  \\n  eieio  \\n\" ::: \"memory\")\n#else\n#define ngx_memory_barrier()   __asm__ volatile (\"\" ::: \"memory\")\n#endif\n\n#endif\n\n\n#define ngx_cpu_pause()\n"
  },
  {
    "path": "src/os/unix/ngx_gcc_atomic_sparc64.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n/*\n * \"casa   [r1] 0x80, r2, r0\"  and\n * \"casxa  [r1] 0x80, r2, r0\"  do the following:\n *\n *     if ([r1] == r2) {\n *         swap(r0, [r1]);\n *     } else {\n *         r0 = [r1];\n *     }\n *\n * so \"r0 == r2\" means that the operation was successful.\n *\n *\n * The \"r\" means the general register.\n * The \"+r\" means the general register used for both input and output.\n */\n\n\n#if (NGX_PTR_SIZE == 4)\n#define NGX_CASA  \"casa\"\n#else\n#define NGX_CASA  \"casxa\"\n#endif\n\n\nstatic ngx_inline ngx_atomic_uint_t\nngx_atomic_cmp_set(ngx_atomic_t *lock, ngx_atomic_uint_t old,\n    ngx_atomic_uint_t set)\n{\n    __asm__ volatile (\n\n    NGX_CASA \" [%1] 0x80, %2, %0\"\n\n    : \"+r\" (set) : \"r\" (lock), \"r\" (old) : \"memory\");\n\n    return (set == old);\n}\n\n\nstatic ngx_inline ngx_atomic_int_t\nngx_atomic_fetch_add(ngx_atomic_t *value, ngx_atomic_int_t add)\n{\n    ngx_atomic_uint_t  old, res;\n\n    old = *value;\n\n    for ( ;; ) {\n\n        res = old + add;\n\n        __asm__ volatile (\n\n        NGX_CASA \" [%1] 0x80, %2, %0\"\n\n        : \"+r\" (res) : \"r\" (value), \"r\" (old) : \"memory\");\n\n        if (res == old) {\n            return res;\n        }\n\n        old = res;\n    }\n}\n\n\n#if (NGX_SMP)\n#define ngx_memory_barrier()                                                  \\\n            __asm__ volatile (                                                \\\n            \"membar #LoadLoad | #LoadStore | #StoreStore | #StoreLoad\"        \\\n            ::: \"memory\")\n#else\n#define ngx_memory_barrier()   __asm__ volatile (\"\" ::: \"memory\")\n#endif\n\n#define ngx_cpu_pause()\n"
  },
  {
    "path": "src/os/unix/ngx_gcc_atomic_x86.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#if (NGX_SMP)\n#define NGX_SMP_LOCK  \"lock;\"\n#else\n#define NGX_SMP_LOCK\n#endif\n\n\n/*\n * \"cmpxchgl  r, [m]\":\n *\n *     if (eax == [m]) {\n *         zf = 1;\n *         [m] = r;\n *     } else {\n *         zf = 0;\n *         eax = [m];\n *     }\n *\n *\n * The \"r\" means the general register.\n * The \"=a\" and \"a\" are the %eax register.\n * Although we can return result in any register, we use \"a\" because it is\n * used in cmpxchgl anyway.  The result is actually in %al but not in %eax,\n * however, as the code is inlined gcc can test %al as well as %eax,\n * and icc adds \"movzbl %al, %eax\" by itself.\n *\n * The \"cc\" means that flags were changed.\n */\n\nstatic ngx_inline ngx_atomic_uint_t\nngx_atomic_cmp_set(ngx_atomic_t *lock, ngx_atomic_uint_t old,\n    ngx_atomic_uint_t set)\n{\n    u_char  res;\n\n    __asm__ volatile (\n\n         NGX_SMP_LOCK\n    \"    cmpxchgl  %3, %1;   \"\n    \"    sete      %0;       \"\n\n    : \"=a\" (res) : \"m\" (*lock), \"a\" (old), \"r\" (set) : \"cc\", \"memory\");\n\n    return res;\n}\n\n\n/*\n * \"xaddl  r, [m]\":\n *\n *     temp = [m];\n *     [m] += r;\n *     r = temp;\n *\n *\n * The \"+r\" means the general register.\n * The \"cc\" means that flags were changed.\n */\n\n\n#if !(( __GNUC__ == 2 && __GNUC_MINOR__ <= 7 ) || ( __INTEL_COMPILER >= 800 ))\n\n/*\n * icc 8.1 and 9.0 compile broken code with -march=pentium4 option:\n * ngx_atomic_fetch_add() always return the input \"add\" value,\n * so we use the gcc 2.7 version.\n *\n * icc 8.1 and 9.0 with -march=pentiumpro option or icc 7.1 compile\n * correct code.\n */\n\nstatic ngx_inline ngx_atomic_int_t\nngx_atomic_fetch_add(ngx_atomic_t *value, ngx_atomic_int_t add)\n{\n    __asm__ volatile (\n\n         NGX_SMP_LOCK\n    \"    xaddl  %0, %1;   \"\n\n    : \"+r\" (add) : \"m\" (*value) : \"cc\", \"memory\");\n\n    return add;\n}\n\n\n#else\n\n/*\n * gcc 2.7 does not support \"+r\", so we have to use the fixed\n * %eax (\"=a\" and \"a\") and this adds two superfluous instructions in the end\n * of code, something like this: \"mov %eax, %edx / mov %edx, %eax\".\n */\n\nstatic ngx_inline ngx_atomic_int_t\nngx_atomic_fetch_add(ngx_atomic_t *value, ngx_atomic_int_t add)\n{\n    ngx_atomic_uint_t  old;\n\n    __asm__ volatile (\n\n         NGX_SMP_LOCK\n    \"    xaddl  %2, %1;   \"\n\n    : \"=a\" (old) : \"m\" (*value), \"a\" (add) : \"cc\", \"memory\");\n\n    return old;\n}\n\n#endif\n\n\n/*\n * on x86 the write operations go in a program order, so we need only\n * to disable the gcc reorder optimizations\n */\n\n#define ngx_memory_barrier()    __asm__ volatile (\"\" ::: \"memory\")\n\n/* old \"as\" does not support \"pause\" opcode */\n#define ngx_cpu_pause()         __asm__ (\".byte 0xf3, 0x90\")\n"
  },
  {
    "path": "src/os/unix/ngx_linux.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_LINUX_H_INCLUDED_\n#define _NGX_LINUX_H_INCLUDED_\n\n\nngx_chain_t *ngx_linux_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in,\n    off_t limit);\n\n\n#endif /* _NGX_LINUX_H_INCLUDED_ */\n"
  },
  {
    "path": "src/os/unix/ngx_linux_aio_read.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n\n\nextern int            ngx_eventfd;\nextern aio_context_t  ngx_aio_ctx;\n\n\nstatic void ngx_file_aio_event_handler(ngx_event_t *ev);\n\n\nstatic int\nio_submit(aio_context_t ctx, long n, struct iocb **paiocb)\n{\n    return syscall(SYS_io_submit, ctx, n, paiocb);\n}\n\n\nngx_int_t\nngx_file_aio_init(ngx_file_t *file, ngx_pool_t *pool)\n{\n    ngx_event_aio_t  *aio;\n\n    aio = ngx_pcalloc(pool, sizeof(ngx_event_aio_t));\n    if (aio == NULL) {\n        return NGX_ERROR;\n    }\n\n    aio->file = file;\n    aio->fd = file->fd;\n    aio->event.data = aio;\n    aio->event.ready = 1;\n    aio->event.log = file->log;\n\n    file->aio = aio;\n\n    return NGX_OK;\n}\n\n\nssize_t\nngx_file_aio_read(ngx_file_t *file, u_char *buf, size_t size, off_t offset,\n    ngx_pool_t *pool)\n{\n    ngx_err_t         err;\n    struct iocb      *piocb[1];\n    ngx_event_t      *ev;\n    ngx_event_aio_t  *aio;\n\n    if (!ngx_file_aio) {\n        return ngx_read_file(file, buf, size, offset);\n    }\n\n    if (file->aio == NULL && ngx_file_aio_init(file, pool) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    aio = file->aio;\n    ev = &aio->event;\n\n    if (!ev->ready) {\n        ngx_log_error(NGX_LOG_ALERT, file->log, 0,\n                      \"second aio post for \\\"%V\\\"\", &file->name);\n        return NGX_AGAIN;\n    }\n\n    ngx_log_debug4(NGX_LOG_DEBUG_CORE, file->log, 0,\n                   \"aio complete:%d @%O:%uz %V\",\n                   ev->complete, offset, size, &file->name);\n\n    if (ev->complete) {\n        ev->active = 0;\n        ev->complete = 0;\n\n        if (aio->res >= 0) {\n            ngx_set_errno(0);\n            return aio->res;\n        }\n\n        ngx_set_errno(-aio->res);\n\n        ngx_log_error(NGX_LOG_CRIT, file->log, ngx_errno,\n                      \"aio read \\\"%s\\\" failed\", file->name.data);\n\n        return NGX_ERROR;\n    }\n\n    ngx_memzero(&aio->aiocb, sizeof(struct iocb));\n\n    aio->aiocb.aio_data = (uint64_t) (uintptr_t) ev;\n    aio->aiocb.aio_lio_opcode = IOCB_CMD_PREAD;\n    aio->aiocb.aio_fildes = file->fd;\n    aio->aiocb.aio_buf = (uint64_t) (uintptr_t) buf;\n    aio->aiocb.aio_nbytes = size;\n    aio->aiocb.aio_offset = offset;\n    aio->aiocb.aio_flags = IOCB_FLAG_RESFD;\n    aio->aiocb.aio_resfd = ngx_eventfd;\n\n    ev->handler = ngx_file_aio_event_handler;\n\n    piocb[0] = &aio->aiocb;\n\n    if (io_submit(ngx_aio_ctx, 1, piocb) == 1) {\n        ev->active = 1;\n        ev->ready = 0;\n        ev->complete = 0;\n\n        return NGX_AGAIN;\n    }\n\n    err = ngx_errno;\n\n    if (err == NGX_EAGAIN) {\n        return ngx_read_file(file, buf, size, offset);\n    }\n\n    ngx_log_error(NGX_LOG_CRIT, file->log, err,\n                  \"io_submit(\\\"%V\\\") failed\", &file->name);\n\n    if (err == NGX_ENOSYS) {\n        ngx_file_aio = 0;\n        return ngx_read_file(file, buf, size, offset);\n    }\n\n    return NGX_ERROR;\n}\n\n\nstatic void\nngx_file_aio_event_handler(ngx_event_t *ev)\n{\n    ngx_event_aio_t  *aio;\n\n    aio = ev->data;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_CORE, ev->log, 0,\n                   \"aio event handler fd:%d %V\", aio->fd, &aio->file->name);\n\n    aio->handler(ev);\n}\n"
  },
  {
    "path": "src/os/unix/ngx_linux_config.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_LINUX_CONFIG_H_INCLUDED_\n#define _NGX_LINUX_CONFIG_H_INCLUDED_\n\n\n#ifndef _GNU_SOURCE\n#define _GNU_SOURCE             /* pread(), pwrite(), gethostname() */\n#endif\n\n#define _FILE_OFFSET_BITS  64\n\n#include <sys/types.h>\n#include <sys/time.h>\n#include <unistd.h>\n#include <stdarg.h>\n#include <stddef.h>             /* offsetof() */\n#include <stdio.h>\n#include <stdlib.h>\n#include <ctype.h>\n#include <errno.h>\n#include <string.h>\n#include <signal.h>\n#include <pwd.h>\n#include <grp.h>\n#include <dirent.h>\n#include <glob.h>\n#include <sys/vfs.h>            /* statfs() */\n\n#include <sys/uio.h>\n#include <sys/stat.h>\n#include <fcntl.h>\n\n#include <sys/wait.h>\n#include <sys/mman.h>\n#include <sys/resource.h>\n#include <sched.h>\n\n#include <sys/socket.h>\n#include <netinet/in.h>\n#include <netinet/tcp.h>        /* TCP_NODELAY, TCP_CORK */\n#include <arpa/inet.h>\n#include <netdb.h>\n#include <sys/un.h>\n\n#include <time.h>               /* tzset() */\n#include <malloc.h>             /* memalign() */\n#include <limits.h>             /* IOV_MAX */\n#include <sys/ioctl.h>\n#include <crypt.h>\n#include <sys/utsname.h>        /* uname() */\n\n#include <dlfcn.h>\n\n\n#include <ngx_auto_config.h>\n\n\n#if (NGX_HAVE_POSIX_SEM)\n#include <semaphore.h>\n#endif\n\n\n#if (NGX_HAVE_SYS_PRCTL_H)\n#include <sys/prctl.h>\n#endif\n\n\n#if (NGX_HAVE_SENDFILE64)\n#include <sys/sendfile.h>\n#else\nextern ssize_t sendfile(int s, int fd, int32_t *offset, size_t size);\n#define NGX_SENDFILE_LIMIT  0x80000000\n#endif\n\n\n#if (NGX_HAVE_POLL)\n#include <poll.h>\n#endif\n\n\n#if (NGX_HAVE_EPOLL)\n#include <sys/epoll.h>\n#endif\n\n\n#if (NGX_HAVE_SYS_EVENTFD_H)\n#include <sys/eventfd.h>\n#endif\n#include <sys/syscall.h>\n#if (NGX_HAVE_FILE_AIO)\n#include <linux/aio_abi.h>\ntypedef struct iocb  ngx_aiocb_t;\n#endif\n\n\n#if (NGX_HAVE_CAPABILITIES)\n#include <linux/capability.h>\n#endif\n\n#if (NGX_HAVE_UDP_SEGMENT)\n#include <netinet/udp.h>\n#endif\n\n\n#define NGX_LISTEN_BACKLOG        511\n\n\n#ifndef NGX_HAVE_SO_SNDLOWAT\n/* setsockopt(SO_SNDLOWAT) returns ENOPROTOOPT */\n#define NGX_HAVE_SO_SNDLOWAT         0\n#endif\n\n\n#ifndef NGX_HAVE_INHERITED_NONBLOCK\n#define NGX_HAVE_INHERITED_NONBLOCK  0\n#endif\n\n\n#define NGX_HAVE_OS_SPECIFIC_INIT    1\n#define ngx_debug_init()\n\n\nextern char **environ;\n\n\n#endif /* _NGX_LINUX_CONFIG_H_INCLUDED_ */\n"
  },
  {
    "path": "src/os/unix/ngx_linux_init.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\nu_char  ngx_linux_kern_ostype[50];\nu_char  ngx_linux_kern_osrelease[50];\n\n\nstatic ngx_os_io_t ngx_linux_io = {\n    ngx_unix_recv,\n    ngx_readv_chain,\n    ngx_udp_unix_recv,\n    ngx_unix_send,\n    ngx_udp_unix_send,\n    ngx_udp_unix_sendmsg_chain,\n#if (NGX_HAVE_SENDFILE)\n    ngx_linux_sendfile_chain,\n    NGX_IO_SENDFILE\n#else\n    ngx_writev_chain,\n    0\n#endif\n};\n\n\nngx_int_t\nngx_os_specific_init(ngx_log_t *log)\n{\n    struct utsname  u;\n\n    if (uname(&u) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, \"uname() failed\");\n        return NGX_ERROR;\n    }\n\n    (void) ngx_cpystrn(ngx_linux_kern_ostype, (u_char *) u.sysname,\n                       sizeof(ngx_linux_kern_ostype));\n\n    (void) ngx_cpystrn(ngx_linux_kern_osrelease, (u_char *) u.release,\n                       sizeof(ngx_linux_kern_osrelease));\n\n    ngx_os_io = ngx_linux_io;\n\n    return NGX_OK;\n}\n\n\nvoid\nngx_os_specific_status(ngx_log_t *log)\n{\n    ngx_log_error(NGX_LOG_NOTICE, log, 0, \"OS: %s %s\",\n                  ngx_linux_kern_ostype, ngx_linux_kern_osrelease);\n}\n"
  },
  {
    "path": "src/os/unix/ngx_linux_sendfile_chain.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n\n\nstatic ssize_t ngx_linux_sendfile(ngx_connection_t *c, ngx_buf_t *file,\n    size_t size);\n\n#if (NGX_THREADS)\n#include <ngx_thread_pool.h>\n\n#if !(NGX_HAVE_SENDFILE64)\n#error sendfile64() is required!\n#endif\n\nstatic ssize_t ngx_linux_sendfile_thread(ngx_connection_t *c, ngx_buf_t *file,\n    size_t size);\nstatic void ngx_linux_sendfile_thread_handler(void *data, ngx_log_t *log);\n#endif\n\n\n/*\n * On Linux up to 2.4.21 sendfile() (syscall #187) works with 32-bit\n * offsets only, and the including <sys/sendfile.h> breaks the compiling,\n * if off_t is 64 bit wide.  So we use own sendfile() definition, where offset\n * parameter is int32_t, and use sendfile() for the file parts below 2G only,\n * see src/os/unix/ngx_linux_config.h\n *\n * Linux 2.4.21 has the new sendfile64() syscall #239.\n *\n * On Linux up to 2.6.16 sendfile() does not allow to pass the count parameter\n * more than 2G-1 bytes even on 64-bit platforms: it returns EINVAL,\n * so we limit it to 2G-1 bytes.\n *\n * On Linux 2.6.16 and later, sendfile() silently limits the count parameter\n * to 2G minus the page size, even on 64-bit platforms.\n */\n\n#define NGX_SENDFILE_MAXSIZE  2147483647L\n\n\nngx_chain_t *\nngx_linux_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)\n{\n    int            tcp_nodelay;\n    off_t          send, prev_send;\n    size_t         file_size, sent;\n    ssize_t        n;\n    ngx_err_t      err;\n    ngx_buf_t     *file;\n    ngx_event_t   *wev;\n    ngx_chain_t   *cl;\n    ngx_iovec_t    header;\n    struct iovec   headers[NGX_IOVS_PREALLOCATE];\n\n    wev = c->write;\n\n    if (!wev->ready) {\n        return in;\n    }\n\n\n    /* the maximum limit size is 2G-1 - the page size */\n\n    if (limit == 0 || limit > (off_t) (NGX_SENDFILE_MAXSIZE - ngx_pagesize)) {\n        limit = NGX_SENDFILE_MAXSIZE - ngx_pagesize;\n    }\n\n\n    send = 0;\n\n    header.iovs = headers;\n    header.nalloc = NGX_IOVS_PREALLOCATE;\n\n    for ( ;; ) {\n        prev_send = send;\n\n        /* create the iovec and coalesce the neighbouring bufs */\n\n        cl = ngx_output_chain_to_iovec(&header, in, limit - send, c->log);\n\n        if (cl == NGX_CHAIN_ERROR) {\n            return NGX_CHAIN_ERROR;\n        }\n\n        send += header.size;\n\n        /* set TCP_CORK if there is a header before a file */\n\n        if (c->tcp_nopush == NGX_TCP_NOPUSH_UNSET\n            && header.count != 0\n            && cl\n            && cl->buf->in_file)\n        {\n            /* the TCP_CORK and TCP_NODELAY are mutually exclusive */\n\n            if (c->tcp_nodelay == NGX_TCP_NODELAY_SET) {\n\n                tcp_nodelay = 0;\n\n                if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY,\n                               (const void *) &tcp_nodelay, sizeof(int)) == -1)\n                {\n                    err = ngx_socket_errno;\n\n                    /*\n                     * there is a tiny chance to be interrupted, however,\n                     * we continue a processing with the TCP_NODELAY\n                     * and without the TCP_CORK\n                     */\n\n                    if (err != NGX_EINTR) {\n                        wev->error = 1;\n                        ngx_connection_error(c, err,\n                                             \"setsockopt(TCP_NODELAY) failed\");\n                        return NGX_CHAIN_ERROR;\n                    }\n\n                } else {\n                    c->tcp_nodelay = NGX_TCP_NODELAY_UNSET;\n\n                    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                                   \"no tcp_nodelay\");\n                }\n            }\n\n            if (c->tcp_nodelay == NGX_TCP_NODELAY_UNSET) {\n\n                if (ngx_tcp_nopush(c->fd) == -1) {\n                    err = ngx_socket_errno;\n\n                    /*\n                     * there is a tiny chance to be interrupted, however,\n                     * we continue a processing without the TCP_CORK\n                     */\n\n                    if (err != NGX_EINTR) {\n                        wev->error = 1;\n                        ngx_connection_error(c, err,\n                                             ngx_tcp_nopush_n \" failed\");\n                        return NGX_CHAIN_ERROR;\n                    }\n\n                } else {\n                    c->tcp_nopush = NGX_TCP_NOPUSH_SET;\n\n                    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                                   \"tcp_nopush\");\n                }\n            }\n        }\n\n        /* get the file buf */\n\n        if (header.count == 0 && cl && cl->buf->in_file && send < limit) {\n            file = cl->buf;\n\n            /* coalesce the neighbouring file bufs */\n\n            file_size = (size_t) ngx_chain_coalesce_file(&cl, limit - send);\n\n            send += file_size;\n#if 1\n            if (file_size == 0) {\n                ngx_debug_point();\n                return NGX_CHAIN_ERROR;\n            }\n#endif\n\n            n = ngx_linux_sendfile(c, file, file_size);\n\n            if (n == NGX_ERROR) {\n                return NGX_CHAIN_ERROR;\n            }\n\n            if (n == NGX_DONE) {\n                /* thread task posted */\n                return in;\n            }\n\n            sent = (n == NGX_AGAIN) ? 0 : n;\n\n        } else {\n            n = ngx_writev(c, &header);\n\n            if (n == NGX_ERROR) {\n                return NGX_CHAIN_ERROR;\n            }\n\n            sent = (n == NGX_AGAIN) ? 0 : n;\n        }\n\n        c->sent += sent;\n\n        in = ngx_chain_update_sent(in, sent);\n\n        if (n == NGX_AGAIN) {\n            wev->ready = 0;\n            return in;\n        }\n\n        if ((size_t) (send - prev_send) != sent) {\n\n            /*\n             * sendfile() on Linux 4.3+ might be interrupted at any time,\n             * and provides no indication if it was interrupted or not,\n             * so we have to retry till an explicit EAGAIN\n             *\n             * sendfile() in threads can also report less bytes written\n             * than we are prepared to send now, since it was started in\n             * some point in the past, so we again have to retry\n             */\n\n            send = prev_send + sent;\n        }\n\n        if (send >= limit || in == NULL) {\n            return in;\n        }\n    }\n}\n\n\nstatic ssize_t\nngx_linux_sendfile(ngx_connection_t *c, ngx_buf_t *file, size_t size)\n{\n#if (NGX_HAVE_SENDFILE64)\n    off_t      offset;\n#else\n    int32_t    offset;\n#endif\n    ssize_t    n;\n    ngx_err_t  err;\n\n#if (NGX_THREADS)\n\n    if (file->file->thread_handler) {\n        return ngx_linux_sendfile_thread(c, file, size);\n    }\n\n#endif\n\n#if (NGX_HAVE_SENDFILE64)\n    offset = file->file_pos;\n#else\n    offset = (int32_t) file->file_pos;\n#endif\n\neintr:\n\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"sendfile: @%O %uz\", file->file_pos, size);\n\n    n = sendfile(c->fd, file->file->fd, &offset, size);\n\n    if (n == -1) {\n        err = ngx_errno;\n\n        switch (err) {\n        case NGX_EAGAIN:\n            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,\n                           \"sendfile() is not ready\");\n            return NGX_AGAIN;\n\n        case NGX_EINTR:\n            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,\n                           \"sendfile() was interrupted\");\n            goto eintr;\n\n        default:\n            c->write->error = 1;\n            ngx_connection_error(c, err, \"sendfile() failed\");\n            return NGX_ERROR;\n        }\n    }\n\n    if (n == 0) {\n        /*\n         * if sendfile returns zero, then someone has truncated the file,\n         * so the offset became beyond the end of the file\n         */\n\n        ngx_log_error(NGX_LOG_ALERT, c->log, 0,\n                      \"sendfile() reported that \\\"%s\\\" was truncated at %O\",\n                      file->file->name.data, file->file_pos);\n\n        return NGX_ERROR;\n    }\n\n    ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, \"sendfile: %z of %uz @%O\",\n                   n, size, file->file_pos);\n\n    return n;\n}\n\n\n#if (NGX_THREADS)\n\ntypedef struct {\n    ngx_buf_t     *file;\n    ngx_socket_t   socket;\n    size_t         size;\n\n    size_t         sent;\n    ngx_err_t      err;\n} ngx_linux_sendfile_ctx_t;\n\n\nstatic ssize_t\nngx_linux_sendfile_thread(ngx_connection_t *c, ngx_buf_t *file, size_t size)\n{\n    ngx_event_t               *wev;\n    ngx_thread_task_t         *task;\n    ngx_linux_sendfile_ctx_t  *ctx;\n\n    ngx_log_debug3(NGX_LOG_DEBUG_CORE, c->log, 0,\n                   \"linux sendfile thread: %d, %uz, %O\",\n                   file->file->fd, size, file->file_pos);\n\n    task = c->sendfile_task;\n\n    if (task == NULL) {\n        task = ngx_thread_task_alloc(c->pool, sizeof(ngx_linux_sendfile_ctx_t));\n        if (task == NULL) {\n            return NGX_ERROR;\n        }\n\n        task->handler = ngx_linux_sendfile_thread_handler;\n\n        c->sendfile_task = task;\n    }\n\n    ctx = task->ctx;\n    wev = c->write;\n\n    if (task->event.complete) {\n        task->event.complete = 0;\n\n        if (ctx->err == NGX_EAGAIN) {\n            /*\n             * if wev->complete is set, this means that a write event\n             * happened while we were waiting for the thread task, so\n             * we have to retry sending even on EAGAIN\n             */\n\n            if (wev->complete) {\n                return 0;\n            }\n\n            return NGX_AGAIN;\n        }\n\n        if (ctx->err) {\n            wev->error = 1;\n            ngx_connection_error(c, ctx->err, \"sendfile() failed\");\n            return NGX_ERROR;\n        }\n\n        if (ctx->sent == 0) {\n            /*\n             * if sendfile returns zero, then someone has truncated the file,\n             * so the offset became beyond the end of the file\n             */\n\n            ngx_log_error(NGX_LOG_ALERT, c->log, 0,\n                          \"sendfile() reported that \\\"%s\\\" was truncated at %O\",\n                          file->file->name.data, file->file_pos);\n\n            return NGX_ERROR;\n        }\n\n        return ctx->sent;\n    }\n\n    ctx->file = file;\n    ctx->socket = c->fd;\n    ctx->size = size;\n\n    wev->complete = 0;\n\n    if (file->file->thread_handler(task, file->file) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    return NGX_DONE;\n}\n\n\nstatic void\nngx_linux_sendfile_thread_handler(void *data, ngx_log_t *log)\n{\n    ngx_linux_sendfile_ctx_t *ctx = data;\n\n    off_t       offset;\n    ssize_t     n;\n    ngx_buf_t  *file;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_CORE, log, 0, \"linux sendfile thread handler\");\n\n    file = ctx->file;\n    offset = file->file_pos;\n\nagain:\n\n    n = sendfile(ctx->socket, file->file->fd, &offset, ctx->size);\n\n    if (n == -1) {\n        ctx->err = ngx_errno;\n\n    } else {\n        ctx->sent = n;\n        ctx->err = 0;\n    }\n\n#if 0\n    ngx_time_update();\n#endif\n\n    ngx_log_debug4(NGX_LOG_DEBUG_EVENT, log, 0,\n                   \"sendfile: %z (err: %d) of %uz @%O\",\n                   n, ctx->err, ctx->size, file->file_pos);\n\n    if (ctx->err == NGX_EINTR) {\n        goto again;\n    }\n}\n\n#endif /* NGX_THREADS */\n"
  },
  {
    "path": "src/os/unix/ngx_os.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_OS_H_INCLUDED_\n#define _NGX_OS_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\n#define NGX_IO_SENDFILE    1\n\n\ntypedef ssize_t (*ngx_recv_pt)(ngx_connection_t *c, u_char *buf, size_t size);\ntypedef ssize_t (*ngx_recv_chain_pt)(ngx_connection_t *c, ngx_chain_t *in,\n    off_t limit);\ntypedef ssize_t (*ngx_send_pt)(ngx_connection_t *c, u_char *buf, size_t size);\ntypedef ngx_chain_t *(*ngx_send_chain_pt)(ngx_connection_t *c, ngx_chain_t *in,\n    off_t limit);\n\ntypedef struct {\n    ngx_recv_pt        recv;\n    ngx_recv_chain_pt  recv_chain;\n    ngx_recv_pt        udp_recv;\n    ngx_send_pt        send;\n    ngx_send_pt        udp_send;\n    ngx_send_chain_pt  udp_send_chain;\n    ngx_send_chain_pt  send_chain;\n    ngx_uint_t         flags;\n} ngx_os_io_t;\n\n\nngx_int_t ngx_os_init(ngx_log_t *log);\nvoid ngx_os_status(ngx_log_t *log);\nngx_int_t ngx_os_specific_init(ngx_log_t *log);\nvoid ngx_os_specific_status(ngx_log_t *log);\nngx_int_t ngx_daemon(ngx_log_t *log);\nngx_int_t ngx_os_signal_process(ngx_cycle_t *cycle, char *sig, ngx_pid_t pid);\n\n\nssize_t ngx_unix_recv(ngx_connection_t *c, u_char *buf, size_t size);\nssize_t ngx_readv_chain(ngx_connection_t *c, ngx_chain_t *entry, off_t limit);\nssize_t ngx_udp_unix_recv(ngx_connection_t *c, u_char *buf, size_t size);\nssize_t ngx_unix_send(ngx_connection_t *c, u_char *buf, size_t size);\nngx_chain_t *ngx_writev_chain(ngx_connection_t *c, ngx_chain_t *in,\n    off_t limit);\nssize_t ngx_udp_unix_send(ngx_connection_t *c, u_char *buf, size_t size);\nngx_chain_t *ngx_udp_unix_sendmsg_chain(ngx_connection_t *c, ngx_chain_t *in,\n    off_t limit);\n\n\n#if (IOV_MAX > 64)\n#define NGX_IOVS_PREALLOCATE  64\n#else\n#define NGX_IOVS_PREALLOCATE  IOV_MAX\n#endif\n\n\ntypedef struct {\n    struct iovec  *iovs;\n    ngx_uint_t     count;\n    size_t         size;\n    ngx_uint_t     nalloc;\n} ngx_iovec_t;\n\nngx_chain_t *ngx_output_chain_to_iovec(ngx_iovec_t *vec, ngx_chain_t *in,\n    size_t limit, ngx_log_t *log);\n\n\nssize_t ngx_writev(ngx_connection_t *c, ngx_iovec_t *vec);\n\n\nextern ngx_os_io_t  ngx_os_io;\nextern ngx_int_t    ngx_ncpu;\nextern ngx_int_t    ngx_max_sockets;\nextern ngx_uint_t   ngx_inherited_nonblocking;\nextern ngx_uint_t   ngx_tcp_nodelay_and_tcp_nopush;\n\n\n#if (NGX_FREEBSD)\n#include <ngx_freebsd.h>\n\n\n#elif (NGX_LINUX)\n#include <ngx_linux.h>\n\n\n#elif (NGX_SOLARIS)\n#include <ngx_solaris.h>\n\n\n#elif (NGX_DARWIN)\n#include <ngx_darwin.h>\n#endif\n\n\n#endif /* _NGX_OS_H_INCLUDED_ */\n"
  },
  {
    "path": "src/os/unix/ngx_pipe.c",
    "content": "\n/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_channel.h>\n\n\n#if !(NGX_WIN32)\n\nstatic ngx_uint_t       ngx_pipe_generation;\nstatic ngx_uint_t       ngx_last_pipe;\nstatic ngx_open_pipe_t  ngx_pipes[NGX_MAX_PROCESSES];\n\n#define MAX_BACKUP_NUM          128\n#define NGX_PIPE_DIR_ACCESS     S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH\n#define NGX_PIPE_FILE_ACCESS    S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH\n\ntypedef struct {\n    ngx_int_t       time_now;\n    ngx_int_t       last_open_time;\n    ngx_int_t       log_size;\n    ngx_int_t       last_suit_time;\n\n    char           *logname;\n    char           *backup[MAX_BACKUP_NUM];\n\n    ngx_int_t       backup_num;\n    ngx_int_t       log_max_size;\n    ngx_int_t       interval;\n    char           *suitpath;\n    ngx_int_t       adjust_time;\n    ngx_int_t       adjust_time_raw;\n} ngx_pipe_rollback_conf_t;\n\nstatic void ngx_signal_pipe_broken(ngx_log_t *log, ngx_pid_t pid);\nstatic ngx_int_t ngx_open_pipe(ngx_cycle_t *cycle, ngx_open_pipe_t *op);\nstatic void ngx_close_pipe(ngx_open_pipe_t *pipe);\n\nstatic void ngx_pipe_log(ngx_cycle_t *cycle, ngx_open_pipe_t *op);\nvoid ngx_pipe_get_last_rollback_time(ngx_pipe_rollback_conf_t *rbcf);\nstatic void ngx_pipe_do_rollback(ngx_cycle_t *cycle, ngx_pipe_rollback_conf_t *rbcf);\nstatic ngx_int_t ngx_pipe_rollback_parse_args(ngx_cycle_t *cycle,\n    ngx_open_pipe_t *op, ngx_pipe_rollback_conf_t *rbcf);\n\nngx_str_t ngx_log_error_backup = ngx_string(NGX_ERROR_LOG_PATH);\nngx_str_t ngx_log_access_backup = ngx_string(NGX_HTTP_LOG_PATH);\n\nngx_str_t ngx_pipe_dev_null_file = ngx_string(\"/dev/null\");\n\nngx_open_pipe_t *\nngx_conf_open_pipe(ngx_cycle_t *cycle, ngx_str_t *cmd, const char *type)\n{\n    u_char           *cp, *ct, *dup, **argi, **c1, **c2;\n    ngx_int_t         same, ti, use;\n    ngx_uint_t        i, j, numargs = 0;\n    ngx_array_t      *argv_out;\n\n    dup = ngx_pnalloc(cycle->pool, cmd->len + 1);\n    if (dup == NULL) {\n        return NULL;\n    }\n\n    (void) ngx_cpystrn(dup, cmd->data, cmd->len + 1);\n\n    for (cp = cmd->data; *cp == ' ' || *cp == '\\t'; cp++);\n    ct = cp;\n\n    if (ngx_strcmp(type, \"r\") == 0) {\n        ti = NGX_PIPE_READ;\n    } else if (ngx_strcmp(type, \"w\") == 0) {\n        ti = NGX_PIPE_WRITE;\n    } else {\n        return NULL;\n    }\n\n    numargs = 1;\n    while (*ct != '\\0') {\n        for ( /* void */ ; *ct != '\\0'; ct++) {\n            if (*ct == ' ' || *ct == '\\t') {\n                break;\n            }\n        }\n\n        if (*ct != '\\0') {\n            ct++;\n        }\n\n        numargs++;\n\n        for ( /* void */ ; *ct == ' ' || *ct == '\\t'; ct++);\n    }\n\n    argv_out = ngx_array_create(cycle->pool, numargs, sizeof(u_char *));\n    if (argv_out == NULL) {\n        return NULL;\n    }\n\n    for (i = 0; i < (numargs - 1); i++) {\n        for ( /* void */ ; *cp == ' ' || *cp == '\\t'; cp++);\n\n        for (ct = cp; *cp != '\\0'; cp++) {\n            if (*cp == ' ' || *cp == '\\t') {\n                break;\n            }\n        }\n\n        *cp = '\\0';\n        argi = (u_char **) ngx_array_push(argv_out);\n        *argi = ct;\n        cp++;\n    }\n\n    argi = (u_char **) ngx_array_push(argv_out);\n    if (argi == NULL) {\n        return NULL;\n    }\n\n    *argi = NULL;\n\n    for (i = 0, use = -1; i < ngx_last_pipe; i++) {\n\n        if (!ngx_pipes[i].configured) {\n            if (use == -1) {\n                use = i;\n            }\n            continue;\n        }\n\n        if (ngx_pipes[i].generation != ngx_pipe_generation) {\n            continue;\n        }\n\n        if (argv_out->nelts != ngx_pipes[i].argv->nelts) {\n            continue;\n        }\n\n        if (ti != ngx_pipes[i].type) {\n            continue;\n        }\n\n        same = 1;\n        c1 = argv_out->elts;\n        c2 = ngx_pipes[i].argv->elts;\n        for (j = 0; j < argv_out->nelts - 1; j++) {\n            if (ngx_strcmp(c1[j], c2[j]) != 0) {\n                same = 0;\n                break;\n            }\n        }\n        if (same) {\n            return &ngx_pipes[i];\n        }\n    }\n\n    if (use == -1) {\n        if (ngx_last_pipe < NGX_MAX_PROCESSES) {\n            use = ngx_last_pipe++;\n        } else {\n            return NULL;\n        }\n    }\n\n    ngx_memzero(&ngx_pipes[use], sizeof(ngx_open_pipe_t));\n\n    ngx_pipes[use].open_fd = ngx_list_push(&cycle->open_files);\n    if (ngx_pipes[use].open_fd == NULL) {\n        return NULL;\n    }\n\n    ngx_memzero(ngx_pipes[use].open_fd, sizeof(ngx_open_file_t));\n    ngx_pipes[use].open_fd->fd = NGX_INVALID_FILE;\n\n    ngx_pipes[use].pid = -1;\n    ngx_pipes[use].cmd = dup;\n    ngx_pipes[use].argv = argv_out;\n    ngx_pipes[use].type = ti;\n    ngx_pipes[use].generation = ngx_pipe_generation;\n    ngx_pipes[use].configured = 1;\n\n    return &ngx_pipes[use];\n}\n\n\nstatic void\nngx_close_pipe(ngx_open_pipe_t *pipe)\n{\n    /*\n     * No waitpid at this place, because it is called at\n     * ngx_process_get_status first.\n     */\n\n    if (pipe->pid != -1) {\n        kill(pipe->pid, SIGTERM);\n    }\n\n    pipe->configured = 0;\n}\n\n\nvoid\nngx_increase_pipe_generation(void)\n{\n    ngx_pipe_generation++;\n}\n\n\nngx_int_t\nngx_open_pipes(ngx_cycle_t *cycle)\n{\n    ngx_int_t          stat;\n    ngx_uint_t         i;\n    ngx_core_conf_t   *ccf;\n\n    ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);\n\n    for (i = 0; i < ngx_last_pipe; i++) {\n\n        if (!ngx_pipes[i].configured) {\n            continue;\n        }\n\n        if (ngx_pipes[i].generation != ngx_pipe_generation) {\n            continue;\n        }\n\n        ngx_pipes[i].backup = ngx_pipes[i].open_fd->name;\n        ngx_pipes[i].user = ccf->user;\n\n        stat = ngx_open_pipe(cycle, &ngx_pipes[i]);\n\n        ngx_log_debug4(NGX_LOG_DEBUG_CORE, cycle->log, 0,\n                       \"pipe: %ui(%d, %d) \\\"%s\\\"\",\n                       i, ngx_pipes[i].pfd[0],\n                       ngx_pipes[i].pfd[1], ngx_pipes[i].cmd);\n\n        if (stat == NGX_ERROR) {\n            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                          \"open pipe \\\"%s\\\" failed\",\n                          ngx_pipes[i].cmd);\n            return NGX_ERROR;\n        }\n\n        if (fcntl(ngx_pipes[i].open_fd->fd, F_SETFD, FD_CLOEXEC) == -1) {\n            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                          \"fcntl(FD_CLOEXEC) \\\"%s\\\" failed\",\n                          ngx_pipes[i].cmd);\n            return NGX_ERROR;\n        }\n\n        if (ngx_nonblocking(ngx_pipes[i].open_fd->fd) == -1) {\n            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                          \"nonblock \\\"%s\\\" failed\",\n                          ngx_pipes[i].cmd);\n            return NGX_ERROR;\n        }\n\n        ngx_pipes[i].open_fd->name.len = 0;\n        ngx_pipes[i].open_fd->name.data = NULL;\n    }\n\n    return NGX_OK;\n}\n\n\nvoid\nngx_close_old_pipes(void)\n{\n    ngx_uint_t i, last;\n\n    for (i = 0, last = -1; i < ngx_last_pipe; i++) {\n\n        if (!ngx_pipes[i].configured) {\n            continue;\n        }\n\n        if (ngx_pipes[i].generation < ngx_pipe_generation) {\n            ngx_close_pipe(&ngx_pipes[i]);\n        } else {\n            last = i;\n        }\n    }\n\n    ngx_last_pipe = last + 1;\n}\n\n\nvoid\nngx_close_pipes(void)\n{\n    ngx_uint_t i, last;\n\n    for (i = 0, last = -1; i < ngx_last_pipe; i++) {\n\n        if (!ngx_pipes[i].configured) {\n            continue;\n        }\n\n        if (ngx_pipes[i].generation == ngx_pipe_generation) {\n            ngx_close_pipe(&ngx_pipes[i]);\n        } else {\n            last = i;\n        }\n    }\n\n    ngx_last_pipe = last + 1;\n}\n\n\nvoid\nngx_pipe_broken_action(ngx_log_t *log, ngx_pid_t pid, ngx_int_t master)\n{\n    ngx_uint_t i;\n#ifdef T_PIPES_OUTPUT_ON_BROKEN\n    ngx_uint_t is_stderr = 0;\n#endif\n\n    for (i = 0; i < ngx_last_pipe; i++) {\n\n        if (!ngx_pipes[i].configured) {\n            continue;\n        }\n\n        if (ngx_pipes[i].generation != ngx_pipe_generation) {\n            continue;\n        }\n\n        if (ngx_pipes[i].pid == pid) {\n\n            if (close(ngx_pipes[i].open_fd->fd) == NGX_FILE_ERROR) {\n                ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,\n                              \"close \\\"%s\\\" failed\",\n                              ngx_pipes[i].cmd);\n            }\n\n#ifdef T_PIPES_OUTPUT_ON_BROKEN\n            if (ngx_pipes[i].open_fd == ngx_cycle->log->file) {\n                is_stderr = 1;\n            }\n#endif\n\n            ngx_pipes[i].open_fd->fd = NGX_INVALID_FILE;\n\n#ifdef T_PIPES_OUTPUT_ON_BROKEN\n            if (ngx_pipes[i].backup.len > 0 && ngx_pipes[i].backup.data != NULL) {\n#else\n                //just write to /dev/null\n                ngx_pipes[i].backup.data = ngx_pipe_dev_null_file.data;\n                ngx_pipes[i].backup.len = ngx_pipe_dev_null_file.len;\n#endif\n                ngx_pipes[i].open_fd->name.len = ngx_pipes[i].backup.len;\n                ngx_pipes[i].open_fd->name.data = ngx_pipes[i].backup.data;\n\n                ngx_pipes[i].open_fd->fd = ngx_open_file(ngx_pipes[i].backup.data,\n                                                         NGX_FILE_APPEND,\n                                                         NGX_FILE_CREATE_OR_OPEN,\n                                                         NGX_FILE_DEFAULT_ACCESS);\n\n                if (ngx_pipes[i].open_fd->fd == NGX_INVALID_FILE) {\n                    ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,\n                                  ngx_open_file_n \" \\\"%s\\\" failed\",\n                                  ngx_pipes[i].backup.data);\n                }\n\n                if (fcntl(ngx_pipes[i].open_fd->fd, F_SETFD, FD_CLOEXEC) == -1) {\n                    ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,\n                                  \"fcntl(FD_CLOEXEC) \\\"%s\\\" failed\",\n                                  ngx_pipes[i].backup.data);\n                }\n#ifdef T_PIPES_OUTPUT_ON_BROKEN\n            }\n\n            if (is_stderr) {\n                ngx_set_stderr(ngx_cycle->log->file->fd);\n            }\n#endif\n\n            if (master) {\n#ifdef T_PIPES_OUTPUT_ON_BROKEN\n                if (ngx_pipes[i].backup.len > 0 && ngx_pipes[i].backup.data != NULL) {\n                    if (chown((const char *) ngx_pipes[i].backup.data,\n                              ngx_pipes[i].user, -1)\n                        == -1)\n                    {\n                        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,\n                                      \"chown() \\\"%s\\\" failed\",\n                                      ngx_pipes[i].backup.data);\n                    }\n                }\n#endif\n\n                ngx_signal_pipe_broken(log, pid);\n            }\n        }\n    }\n}\n\n\nstatic void\nngx_signal_pipe_broken(ngx_log_t *log, ngx_pid_t pid)\n{\n    ngx_int_t      i;\n    ngx_channel_t  ch;\n\n    ch.fd = -1;\n    ch.pid = pid;\n    ch.command = NGX_CMD_PIPE_BROKEN;\n\n    for (i = 0; i < ngx_last_process; i++) {\n\n        if (ngx_processes[i].detached || ngx_processes[i].pid == -1) {\n            continue;\n        }\n\n        ngx_write_channel(ngx_processes[i].channel[0],\n                          &ch, sizeof(ngx_channel_t), log);\n    }\n}\n\n\nstatic ngx_int_t\nngx_open_pipe(ngx_cycle_t *cycle, ngx_open_pipe_t *op)\n{\n    int               fd;\n    u_char          **argv;\n    ngx_pid_t         pid;\n    sigset_t          set;\n#if defined(T_PIPE_USE_USER) || defined(T_PIPE_SET_SIZE)\n    ngx_core_conf_t  *ccf;\n\n    ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);\n#endif\n\n    if (pipe(op->pfd) < 0) {\n        return NGX_ERROR;\n    }\n\n#ifdef T_PIPE_SET_SIZE\n    if (ccf->pipe_size != NGX_CONF_UNSET_SIZE && ccf->pipe_size != 0) {\n        if (fcntl(op->pfd[1], F_SETPIPE_SZ, ccf->pipe_size) == -1) {\n            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                          \"set pipe size (%d) failed\", ccf->pipe_size);\n            goto err;\n        }\n    }\n#endif\n\n    argv = op->argv->elts;\n\n    if ((pid = fork()) < 0) {\n        goto err;\n    } else if (pid > 0) {\n        op->pid = pid;\n\n        if (op->open_fd->fd != NGX_INVALID_FILE) {\n            if (close(op->open_fd->fd) == NGX_FILE_ERROR) {\n                ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                              \"close \\\"%s\\\" failed\",\n                              op->open_fd->name.data);\n            }\n        }\n\n        if (op->type == NGX_PIPE_WRITE) {\n            op->open_fd->fd = op->pfd[1];\n            close(op->pfd[0]);\n        } else {\n            op->open_fd->fd = op->pfd[0];\n            close(op->pfd[1]);\n        }\n    } else {\n\n       /*\n        * Set correct process type since closing listening Unix domain socket\n        * in a master process also removes the Unix domain socket file.\n        */\n        ngx_process = NGX_PROCESS_PIPE;\n        ngx_close_listening_sockets(cycle);\n\n        if (op->type == 1) {\n            close(op->pfd[1]);\n            if (op->pfd[0] != STDIN_FILENO) {\n                dup2(op->pfd[0], STDIN_FILENO);\n                close(op->pfd[0]);\n            }\n        } else {\n            close(op->pfd[0]);\n            if (op->pfd[1] != STDOUT_FILENO) {\n                dup2(op->pfd[1], STDOUT_FILENO);\n                close(op->pfd[1]);\n            }\n        }\n#ifdef T_PIPE_USE_USER\n        if (geteuid() == 0) {\n            if (setgid(ccf->group) == -1) {\n                ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                              \"setgid(%d) failed\", ccf->group);\n                exit(2);\n            }\n\n            if (initgroups(ccf->username, ccf->group) == -1) {\n                ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                              \"initgroups(%s, %d) failed\",\n                              ccf->username, ccf->group);\n            }\n\n            if (setuid(ccf->user) == -1) {\n                ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                              \"setuid(%d) failed\", ccf->user);\n                exit(2);\n            }\n        }\n#endif\n\n        /*\n         * redirect stderr to /dev/null, because stderr will be connected with\n         * fd used by the last pipe when error log is configured using pipe,\n         * that will cause it no close\n         */\n\n        fd = ngx_open_file(\"/dev/null\", NGX_FILE_WRONLY, NGX_FILE_OPEN, 0);\n        if (fd == -1) {\n            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                          \"open(\\\"/dev/null\\\") failed\");\n            exit(2);\n        }\n\n        if (dup2(fd, STDERR_FILENO) == -1) {\n            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                          \"dup2(STDERR) failed\");\n            exit(2);\n        }\n\n        if (fd > STDERR_FILENO && close(fd) == -1) {\n            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                          \"close() failed\");\n            exit(2);\n        }\n\n        sigemptyset(&set);\n\n        if (sigprocmask(SIG_SETMASK, &set, NULL) == -1) {\n            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                          \"sigprocmask() failed\");\n            exit(2);\n        }\n\n        if (ngx_strncmp(argv[0], \"rollback\", sizeof(\"rollback\") - 1) == 0) {\n            ngx_pipe_log(cycle, op);\n            exit(0);\n\n        } else {\n            execv((const char *) argv[0], (char *const *) op->argv->elts);\n            exit(0);\n        }\n    }\n\n    return NGX_OK;\n\nerr:\n\n    close(op->pfd[0]);\n    close(op->pfd[1]);\n\n    return NGX_ERROR;\n}\n\n\nstatic void\nngx_pipe_create_subdirs(char *filename, ngx_cycle_t *cycle)\n{\n    ngx_file_info_t stat_buf;\n    char            dirname[1024];\n    char           *p;\n\n    for (p = filename; (p = strchr(p, '/')); p++)\n    {\n        if (p == filename) {\n            continue;       // Don't bother with the root directory\n        }\n\n        ngx_memcpy(dirname, filename, p - filename);\n        dirname[p-filename] = '\\0';\n\n        if (ngx_file_info(dirname, &stat_buf) < 0) {\n            if (errno != ENOENT) {\n                ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                              \"stat [%s] failed\", dirname);\n                exit(2);\n\n            } else {\n                if ((ngx_create_dir(dirname, NGX_PIPE_DIR_ACCESS) < 0) && (errno != EEXIST)) {\n                    ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                                  \"mkdir [%s] failed\", dirname);\n                    exit(2);\n                }\n            }\n        }\n    }\n}\n\nstatic void\nngx_pipe_create_suitpath(ngx_cycle_t *cycle, ngx_pipe_rollback_conf_t *rbcf)\n{\n    char        realpath[256];\n    struct      tm *tm_now;\n    int         result;\n    time_t      t = ngx_time();\n\n    tm_now = localtime(&t);\n    if (tm_now == NULL) {\n        ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,\n                      \"get now time failed\");\n        return;\n    }\n    if (0 >= strftime(realpath, sizeof(realpath), rbcf->suitpath, tm_now)) {\n        ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,\n                      \"parse suitpath with time now failed\");\n        return;\n    }\n\n    ngx_pipe_create_subdirs(realpath, cycle);\n\n    result = unlink(realpath);\n    if (0 == result) {\n        ngx_log_error(NGX_LOG_INFO, cycle->log, 0,\n                      \"unlink [%s] success\", realpath);\n    } else {\n        ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                      \"unlink [%s] failed\", realpath);\n    }\n\n    result = symlink(rbcf->logname, realpath);\n    if (0 == result) {\n        ngx_log_error(NGX_LOG_INFO, cycle->log, 0,\n                      \"symlink [%s] success\", realpath);\n    } else {\n        ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                      \"symlink [%s] failed\", realpath);\n    }\n\n    rbcf->last_suit_time = rbcf->time_now;\n}\n\nstatic time_t\nngx_pipe_get_now_sec()\n{\n    ngx_time_update();\n\n    return ngx_time() + ngx_cached_time->gmtoff * 60;\n}\n\nstatic void\nngx_pipe_log(ngx_cycle_t *cycle, ngx_open_pipe_t *op)\n{\n    ngx_int_t                   n_bytes_read;\n    u_char                     *read_buf;\n    size_t                      read_buf_len = 65536;\n    ngx_fd_t                    log_fd = NGX_INVALID_FILE;\n#ifdef T_PIPE_NO_DISPLAY\n    size_t                      title_len;\n#endif\n    ngx_pipe_rollback_conf_t    rbcf;\n    ngx_file_info_t             sb;\n    size_t                      one_day = 24 * 60 * 60;\n\n    ngx_pid = ngx_getpid();\n\n    rbcf.last_open_time = 0;\n    rbcf.last_suit_time = 0;\n    rbcf.log_size = 0;\n\n    if (ngx_pipe_rollback_parse_args(cycle, op, &rbcf) != NGX_OK) {\n        ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, \"log rollback parse error\");\n        return;\n    }\n\n    read_buf = ngx_pcalloc(cycle->pool, read_buf_len);\n    if (read_buf == NULL) {\n        return;\n    }\n\n    //set title\n    ngx_setproctitle((char *) op->cmd);\n#ifdef T_PIPE_NO_DISPLAY\n    title_len = ngx_strlen(ngx_os_argv[0]);\n#if (NGX_SOLARIS)\n#else\n    ngx_memset((u_char *) ngx_os_argv[0], NGX_SETPROCTITLE_PAD, title_len);\n    ngx_cpystrn((u_char *) ngx_os_argv[0], op->cmd, title_len);\n#endif\n#endif\n\n    for (;;)\n    {\n        if (ngx_terminate == 1) {\n            return;\n        }\n\n        n_bytes_read = ngx_read_fd(0, read_buf, read_buf_len);\n        if (n_bytes_read == 0) {\n            return;\n        }\n        if (errno == EINTR) {\n            continue;\n\n        } else if (n_bytes_read < 0) {\n            return;\n        }\n\n        rbcf.time_now = ngx_pipe_get_now_sec();\n\n        if (NULL != rbcf.suitpath) {\n            if (rbcf.time_now / one_day >\n                    rbcf.last_suit_time / one_day) {\n                ngx_pipe_create_suitpath(cycle, &rbcf);\n            }\n        }\n\n        if (log_fd >= 0) {\n            if (rbcf.interval > 0) {\n                if (((rbcf.time_now - rbcf.adjust_time) / rbcf.interval) >\n                        (rbcf.last_open_time / rbcf.interval)) {\n                    //need check rollback\n                    ngx_close_file(log_fd);\n                    log_fd = NGX_INVALID_FILE;\n                    ngx_log_error(NGX_LOG_INFO, cycle->log, 0,\n                                  \"need check rollback time [%s]\", rbcf.logname);\n                    ngx_pipe_do_rollback(cycle, &rbcf);\n                }\n            }\n        }\n\n        if (log_fd >= 0 && rbcf.log_max_size > 0 &&\n                           rbcf.log_size >= rbcf.log_max_size) {\n            ngx_close_file(log_fd);\n            log_fd = NGX_INVALID_FILE;\n            ngx_log_error(NGX_LOG_INFO, cycle->log, 0,\n                          \"need check rollback size [%s] [%d]\",\n                          rbcf.logname, rbcf.log_size);\n            ngx_pipe_do_rollback(cycle, &rbcf);\n        }\n\n        /* If there is no log file open then open a new one.\n         *   */\n        if (log_fd < 0) {\n            ngx_pipe_create_subdirs(rbcf.logname, cycle);\n            if (rbcf.interval > 0 && rbcf.last_open_time == 0 \n                    && ((rbcf.time_now - rbcf.adjust_time_raw) / rbcf.interval < rbcf.time_now / rbcf.interval)) { /*just check\n                when time is after interval and befor adjust_time_raw, fix when no backup file, do rollback\n                */\n                //need check last rollback time, because other process may do rollback after when it adjust time more than this process\n                ngx_pipe_get_last_rollback_time(&rbcf);\n            } else {\n                rbcf.last_open_time = ngx_pipe_get_now_sec();\n            }\n\n            log_fd = ngx_open_file(rbcf.logname, NGX_FILE_APPEND, NGX_FILE_CREATE_OR_OPEN,\n                          NGX_PIPE_FILE_ACCESS);\n            if (log_fd < 0) {\n                ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                              \"open [%s] failed\", rbcf.logname);\n                return;\n            }\n\n            if (0 == ngx_fd_info(log_fd, &sb)) {\n                rbcf.log_size = sb.st_size;\n            }\n        }\n\n        if (ngx_write_fd(log_fd, read_buf, n_bytes_read) != n_bytes_read) {\n            if (errno != EINTR) {\n                ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                              \"write to [%s] failed\", rbcf.logname);\n                return;\n            }\n        }\n        rbcf.log_size += n_bytes_read;\n    }\n\n}\n\nngx_int_t\nngx_pipe_rollback_parse_args(ngx_cycle_t *cycle, ngx_open_pipe_t *op,\n    ngx_pipe_rollback_conf_t *rbcf)\n{\n    u_char         **argv;\n    ngx_uint_t       i;\n    ngx_int_t        j;\n    size_t           len;\n    ngx_str_t        filename;\n    ngx_str_t        value;\n    uint32_t         hash;\n\n    if (op->argv->nelts < 3) {\n        //no logname\n        return NGX_ERROR;\n    }\n\n    //parse args\n    argv = op->argv->elts;\n\n    //set default param\n    filename.data = (u_char *) argv[1];\n    filename.len = ngx_strlen(filename.data);\n    if (ngx_conf_full_name(cycle, &filename, 0) != NGX_OK) {\n        ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                      \"get fullname failed\");\n        return NGX_ERROR;\n    }\n    rbcf->logname = (char *) filename.data;\n    rbcf->backup_num = 1;\n    rbcf->log_max_size = -1;\n    rbcf->interval = -1;\n    rbcf->suitpath = NULL;\n    rbcf->adjust_time = 60;\n    rbcf->adjust_time_raw = 60;\n    memset(rbcf->backup, 0, sizeof(rbcf->backup));\n\n    for (i = 2; i < op->argv->nelts; i++) {\n        if (argv[i] == NULL) {\n            break;\n        }\n        if (ngx_strncmp((u_char *) \"interval=\", argv[i], 9) == 0) {\n            value.data = argv[i] + 9;\n            value.len = ngx_strlen((char *) argv[i]) - 9;\n#ifdef T_PIPE_OLD_CONF\n            //just compatibility\n            rbcf->interval = ngx_atoi(value.data, value.len);\n            if (rbcf->interval > 0) {\n                rbcf->interval = rbcf->interval * 60;   //convert minute to second\n                continue;\n            }\n#endif\n            rbcf->interval = ngx_parse_time(&value, 1);\n            if (rbcf->interval <= 0) {\n                rbcf->interval = -1;\n            }\n\n        } else if (ngx_strncmp((u_char *) \"baknum=\", argv[i], 7) == 0) {\n            rbcf->backup_num = ngx_atoi(argv[i] + 7,\n                                        ngx_strlen((char *) argv[i]) - 7);\n            if (rbcf->backup_num <= 0) {\n                rbcf->backup_num = 1;\n\n            } else if (MAX_BACKUP_NUM < (size_t)rbcf->backup_num) {\n                rbcf->backup_num = MAX_BACKUP_NUM;\n            }\n\n        } else if (ngx_strncmp((u_char *) \"maxsize=\", argv[i], 8) == 0) {\n            value.data = argv[i] + 8;\n            value.len = ngx_strlen((char *) argv[i]) - 8;\n#ifdef T_PIPE_OLD_CONF\n            //just compatibility\n            rbcf->log_max_size = ngx_atoi(value.data, value.len);\n            if (rbcf->log_max_size > 0) {\n                rbcf->log_max_size = rbcf->log_max_size * 1024 * 1024; //M\n                continue;\n            }\n#endif\n            rbcf->log_max_size = ngx_parse_offset(&value);\n            if (rbcf->log_max_size <= 0) {\n                rbcf->log_max_size = -1;\n            } \n        } else if (ngx_strncmp((u_char *) \"suitpath=\", argv[i], 9) == 0) {\n            filename.data = (u_char*)(argv[i] + 9);\n            filename.len = ngx_strlen(filename.data);\n            if (ngx_conf_full_name(cycle, &filename, 0) != NGX_OK) {\n                ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                              \"get fullname failed\");\n                return NGX_ERROR;\n            }\n            rbcf->suitpath = (char*)filename.data;\n\n            rbcf->time_now = ngx_pipe_get_now_sec();\n            ngx_pipe_create_suitpath(cycle, rbcf);\n        } else if (ngx_strncmp((u_char *) \"adjust=\", argv[i], 7) == 0) {\n            value.data =argv[i] + 7;\n            value.len = ngx_strlen((char *) argv[i]) - 7;\n            rbcf->adjust_time_raw = ngx_parse_time(&value, 1);\n            if (rbcf->adjust_time_raw < 1) {\n                rbcf->adjust_time_raw = 1;\n            }\n        }\n    } \n\n    len = ngx_strlen(rbcf->logname) + 5; //max is \".128\"\n    for (j = 0; j < rbcf->backup_num; j++) {\n        rbcf->backup[j] = ngx_pcalloc(cycle->pool, len);\n        if (rbcf->backup[j] == NULL) {\n            return NGX_ERROR;\n        }\n        ngx_snprintf((u_char *) rbcf->backup[j], len, \"%s.%i%Z\", rbcf->logname, j + 1);\n    }\n\n    //use same seed for same log file, when reload may have same adjust time\n    hash = ngx_crc32_short((u_char*)rbcf->logname, ngx_strlen(rbcf->logname));\n    srand(hash);\n    rbcf->adjust_time = rand() % rbcf->adjust_time_raw;\n\n    ngx_log_error(NGX_LOG_INFO, cycle->log, 0,\n                  \"log rollback param: num [%i], interval %i(S), size %i(B), adjust %i/%i(S)\", \n                  rbcf->backup_num, rbcf->interval, rbcf->log_max_size, rbcf->adjust_time, rbcf->adjust_time_raw);\n\n    return NGX_OK;\n}\n\nvoid ngx_pipe_get_last_rollback_time(ngx_pipe_rollback_conf_t *rbcf)\n{\n    int             fd;\n    struct flock    lock;\n    int             ret;\n\n    struct stat     sb;\n\n    fd = ngx_open_file(rbcf->logname, NGX_FILE_RDWR, NGX_FILE_OPEN, 0);\n    if (fd < 0) {\n        //open lock file failed just use now\n        rbcf->last_open_time = rbcf->time_now;\n        return;\n    }\n\n    lock.l_type     = F_WRLCK;\n    lock.l_whence   = SEEK_SET;\n    lock.l_start    = 0;\n    lock.l_len      = 0;\n\n    ret = fcntl(fd, F_SETLKW, &lock);\n    if (ret < 0) {\n        close(fd);\n        //lock failed just use now\n        rbcf->last_open_time = rbcf->time_now;\n        return;\n    }\n\n    //check time\n    if (rbcf->interval > 0) {\n        if (ngx_file_info(rbcf->backup[0], &sb) == -1) {\n            //no backup file ,so need rollback just set 1\n            rbcf->last_open_time = 1;\n        } else if (sb.st_ctime / rbcf->interval < ngx_time() / rbcf->interval) {\n            //need rollback just set 1\n            rbcf->last_open_time = 1;\n        } else {\n            //just no need rollback\n            rbcf->last_open_time = rbcf->time_now;\n        }\n    } else {\n        rbcf->last_open_time = rbcf->time_now;\n    }\n\n    close(fd);\n}\n\nvoid\nngx_pipe_do_rollback(ngx_cycle_t *cycle, ngx_pipe_rollback_conf_t *rbcf)\n{\n    int             fd;\n    struct flock    lock;\n    int             ret;\n    ngx_int_t       i;\n    ngx_file_info_t sb;\n    ngx_int_t       need_do = 0;\n\n    fd = ngx_open_file(rbcf->logname, NGX_FILE_RDWR, NGX_FILE_OPEN, 0);\n    if (fd < 0) {\n        //open lock file failed just no need rollback\n        return;\n    }\n\n    lock.l_type     = F_WRLCK;\n    lock.l_whence   = SEEK_SET;\n    lock.l_start    = 0;\n    lock.l_len      = 0;\n\n    ret = fcntl(fd, F_SETLKW, &lock);\n    if (ret < 0) {\n        ngx_close_file(fd);\n        //lock failed just no need rollback\n        return;\n    }\n\n    //check time\n    if (rbcf->interval >= 0) {\n        if (ngx_file_info(rbcf->backup[0], &sb) == -1) {\n            need_do = 1;\n            ngx_log_error(NGX_LOG_INFO, cycle->log, 0,\n                          \"need rollback [%s]: cannot open backup\", rbcf->logname);\n\n        } else if (sb.st_ctime / rbcf->interval < ngx_time() / rbcf->interval) {\n            need_do = 1;\n            ngx_log_error(NGX_LOG_INFO, cycle->log, 0,\n                          \"need rollback [%s]: time on [%d] [%d]\",\n                          rbcf->logname, sb.st_ctime, rbcf->time_now);\n\n        } else {\n            ngx_log_error(NGX_LOG_INFO, cycle->log, 0,\n                          \"no need rollback [%s]: time not on [%d] [%d]\",\n                          rbcf->logname, sb.st_ctime, rbcf->time_now);\n        }\n\n    } else {\n        ngx_log_error(NGX_LOG_INFO, cycle->log, 0,\n                      \"no need check rollback [%s] time: no interval\", rbcf->logname);\n    }\n\n    //check size\n    if (rbcf->log_max_size > 0) {\n        if (ngx_file_info(rbcf->logname, &sb) == 0 && (sb.st_size >= rbcf->log_max_size)) {\n            need_do = 1;\n            ngx_log_error(NGX_LOG_INFO, cycle->log, 0,\n                          \"need rollback [%s]: size on [%d]\", rbcf->logname, sb.st_size);\n\n        } else {\n            ngx_log_error(NGX_LOG_INFO, cycle->log, 0,\n                          \"no need rollback [%s]: size not on\", rbcf->logname);\n        }\n\n    } else {\n        ngx_log_error(NGX_LOG_INFO, cycle->log, 0,\n                      \"no need check rollback [%s] size: no max size\", rbcf->logname);\n    }\n\n    if (need_do) {\n        for (i = 1; i < rbcf->backup_num; i++) {\n            ngx_rename_file(rbcf->backup[rbcf->backup_num - i - 1],\n                   rbcf->backup[rbcf->backup_num - i]);\n        }\n        if (ngx_rename_file(rbcf->logname, rbcf->backup[0]) < 0) {\n            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                          \"rname %s to %s failed\", rbcf->logname, rbcf->backup[0]);\n        } else {\n            ngx_log_error(NGX_LOG_WARN, cycle->log, 0,\n                          \"rollback [%s] success\", rbcf->logname);\n        }\n    }\n    ngx_close_file(fd);\n}\n\n#endif\n\n"
  },
  {
    "path": "src/os/unix/ngx_pipe.h",
    "content": "\n/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#ifndef _NGX_PIPE_H_INCLUDED_\n#define _NGX_PIPE_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\n#if !(NGX_WIN32)\n\ntypedef struct {\n    u_char           *cmd;\n    ngx_fd_t          pfd[2];\n    ngx_pid_t         pid;\n    ngx_str_t         backup;         /* when pipe is broken, log into it */\n    ngx_uid_t         user;\n    ngx_uint_t        generation;\n    ngx_array_t      *argv;\n    ngx_open_file_t  *open_fd;        /* the fd of pipe left open in master */\n\n    unsigned          type:1;         /* 1: write, 0: read */\n    unsigned          configured:1;\n} ngx_open_pipe_t;\n\n\n#define NGX_PIPE_WRITE    1\n#define NGX_PIPE_READ     0\n\n\nngx_open_pipe_t *ngx_conf_open_pipe(ngx_cycle_t *cycle, ngx_str_t *cmd,\n    const char *type);\nvoid ngx_increase_pipe_generation(void);\nvoid ngx_close_old_pipes(void);\nngx_int_t ngx_open_pipes(ngx_cycle_t *cycle);\nvoid ngx_close_pipes(void);\nvoid ngx_pipe_broken_action(ngx_log_t *log, ngx_pid_t pid, ngx_int_t master);\n\n\nextern ngx_str_t ngx_log_error_backup;\nextern ngx_str_t ngx_log_access_backup;\n\n#else\n\n#define ngx_increase_pipe_generation\n#define ngx_close_old_pipes\n#define ngx_open_pipes(cycle)\n#define ngx_close_pipes\n#define ngx_pipe_broken_action(log, pid, master)\n\n#endif\n\n\n#endif /* _NGX_PIPE_H_INCLUDED_ */\n"
  },
  {
    "path": "src/os/unix/ngx_posix_config.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_POSIX_CONFIG_H_INCLUDED_\n#define _NGX_POSIX_CONFIG_H_INCLUDED_\n\n\n#if (NGX_HPUX)\n#define _XOPEN_SOURCE\n#define _XOPEN_SOURCE_EXTENDED  1\n#define _HPUX_ALT_XOPEN_SOCKET_API\n#endif\n\n\n#if (NGX_TRU64)\n#define _REENTRANT\n#endif\n\n\n#if (NGX_GNU_HURD)\n#ifndef _GNU_SOURCE\n#define _GNU_SOURCE             /* accept4() */\n#endif\n#define _FILE_OFFSET_BITS       64\n#endif\n\n\n#ifdef __CYGWIN__\n#define timezonevar             /* timezone is variable */\n#define NGX_BROKEN_SCM_RIGHTS   1\n#endif\n\n\n#include <sys/types.h>\n#include <sys/time.h>\n#if (NGX_HAVE_UNISTD_H)\n#include <unistd.h>\n#endif\n#if (NGX_HAVE_INTTYPES_H)\n#include <inttypes.h>\n#endif\n#include <stdarg.h>\n#include <stddef.h>             /* offsetof() */\n#include <stdio.h>\n#include <stdlib.h>\n#include <ctype.h>\n#include <errno.h>\n#include <string.h>\n#include <signal.h>\n#include <pwd.h>\n#include <grp.h>\n#include <dirent.h>\n#include <glob.h>\n#include <time.h>\n#if (NGX_HAVE_SYS_PARAM_H)\n#include <sys/param.h>          /* statfs() */\n#endif\n#if (NGX_HAVE_SYS_MOUNT_H)\n#include <sys/mount.h>          /* statfs() */\n#endif\n#if (NGX_HAVE_SYS_STATVFS_H)\n#include <sys/statvfs.h>        /* statvfs() */\n#endif\n\n#if (NGX_HAVE_SYS_FILIO_H)\n#include <sys/filio.h>          /* FIONBIO */\n#endif\n#include <sys/ioctl.h>          /* FIONBIO */\n\n#include <sys/uio.h>\n#include <sys/stat.h>\n#include <fcntl.h>\n\n#include <sys/wait.h>\n#include <sys/mman.h>\n#include <sys/resource.h>\n#include <sched.h>\n\n#include <sys/socket.h>\n#include <netinet/in.h>\n#include <netinet/tcp.h>        /* TCP_NODELAY */\n#include <arpa/inet.h>\n#include <netdb.h>\n#include <sys/un.h>\n\n#if (NGX_HAVE_LIMITS_H)\n#include <limits.h>             /* IOV_MAX */\n#endif\n\n#ifdef __CYGWIN__\n#include <malloc.h>             /* memalign() */\n#endif\n\n#if (NGX_HAVE_CRYPT_H)\n#include <crypt.h>\n#endif\n\n\n#ifndef IOV_MAX\n#define IOV_MAX   16\n#endif\n\n\n#include <ngx_auto_config.h>\n\n\n#if (NGX_HAVE_DLOPEN)\n#include <dlfcn.h>\n#endif\n\n\n#if (NGX_HAVE_POSIX_SEM)\n#include <semaphore.h>\n#endif\n\n\n#if (NGX_HAVE_POLL)\n#include <poll.h>\n#endif\n\n\n#if (NGX_HAVE_KQUEUE)\n#include <sys/event.h>\n#endif\n\n\n#if (NGX_HAVE_DEVPOLL) && !(NGX_TEST_BUILD_DEVPOLL)\n#include <sys/ioctl.h>\n#include <sys/devpoll.h>\n#endif\n\n\n#if (NGX_HAVE_FILE_AIO)\n#include <aio.h>\ntypedef struct aiocb  ngx_aiocb_t;\n#endif\n\n\n#define NGX_LISTEN_BACKLOG  511\n\n#define ngx_debug_init()\n\n\nextern char **environ;\n\n\n#endif /* _NGX_POSIX_CONFIG_H_INCLUDED_ */\n"
  },
  {
    "path": "src/os/unix/ngx_posix_init.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <nginx.h>\n\n\nngx_int_t   ngx_ncpu;\nngx_int_t   ngx_max_sockets;\nngx_uint_t  ngx_inherited_nonblocking;\nngx_uint_t  ngx_tcp_nodelay_and_tcp_nopush;\n\n\nstruct rlimit  rlmt;\n\n\nngx_os_io_t ngx_os_io = {\n    ngx_unix_recv,\n    ngx_readv_chain,\n    ngx_udp_unix_recv,\n    ngx_unix_send,\n    ngx_udp_unix_send,\n    ngx_udp_unix_sendmsg_chain,\n    ngx_writev_chain,\n    0\n};\n\n\nngx_int_t\nngx_os_init(ngx_log_t *log)\n{\n    ngx_time_t  *tp;\n    ngx_uint_t   n;\n#if (NGX_HAVE_LEVEL1_DCACHE_LINESIZE)\n    long         size;\n#endif\n\n#if (NGX_HAVE_OS_SPECIFIC_INIT)\n    if (ngx_os_specific_init(log) != NGX_OK) {\n        return NGX_ERROR;\n    }\n#endif\n\n    if (ngx_init_setproctitle(log) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    ngx_pagesize = getpagesize();\n    ngx_cacheline_size = NGX_CPU_CACHE_LINE;\n\n    for (n = ngx_pagesize; n >>= 1; ngx_pagesize_shift++) { /* void */ }\n\n#if (NGX_HAVE_SC_NPROCESSORS_ONLN)\n    if (ngx_ncpu == 0) {\n        ngx_ncpu = sysconf(_SC_NPROCESSORS_ONLN);\n    }\n#endif\n\n    if (ngx_ncpu < 1) {\n        ngx_ncpu = 1;\n    }\n\n#if (NGX_HAVE_LEVEL1_DCACHE_LINESIZE)\n    size = sysconf(_SC_LEVEL1_DCACHE_LINESIZE);\n    if (size > 0) {\n        ngx_cacheline_size = size;\n    }\n#endif\n\n    ngx_cpuinfo();\n\n    if (getrlimit(RLIMIT_NOFILE, &rlmt) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, log, errno,\n                      \"getrlimit(RLIMIT_NOFILE) failed\");\n        return NGX_ERROR;\n    }\n\n    ngx_max_sockets = (ngx_int_t) rlmt.rlim_cur;\n\n#if (NGX_HAVE_INHERITED_NONBLOCK || NGX_HAVE_ACCEPT4)\n    ngx_inherited_nonblocking = 1;\n#else\n    ngx_inherited_nonblocking = 0;\n#endif\n\n    tp = ngx_timeofday();\n    srandom(((unsigned) ngx_pid << 16) ^ tp->sec ^ tp->msec);\n\n    return NGX_OK;\n}\n\n\nvoid\nngx_os_status(ngx_log_t *log)\n{\n    ngx_log_error(NGX_LOG_NOTICE, log, 0, NGINX_VER_BUILD);\n#if (T_NGX_SERVER_INFO)\n    ngx_log_error(NGX_LOG_NOTICE, log, 0, TENGINE_VER_BUILD);\n#endif\n\n#ifdef NGX_COMPILER\n    ngx_log_error(NGX_LOG_NOTICE, log, 0, \"built by \" NGX_COMPILER);\n#endif\n\n#if (NGX_HAVE_OS_SPECIFIC_INIT)\n    ngx_os_specific_status(log);\n#endif\n\n    ngx_log_error(NGX_LOG_NOTICE, log, 0,\n                  \"getrlimit(RLIMIT_NOFILE): %r:%r\",\n                  rlmt.rlim_cur, rlmt.rlim_max);\n}\n\n\n#if 0\n\nngx_int_t\nngx_posix_post_conf_init(ngx_log_t *log)\n{\n    ngx_fd_t  pp[2];\n\n    if (pipe(pp) == -1) {\n        ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, \"pipe() failed\");\n        return NGX_ERROR;\n    }\n\n    if (dup2(pp[1], STDERR_FILENO) == -1) {\n        ngx_log_error(NGX_LOG_EMERG, log, errno, \"dup2(STDERR) failed\");\n        return NGX_ERROR;\n    }\n\n    if (pp[1] > STDERR_FILENO) {\n        if (close(pp[1]) == -1) {\n            ngx_log_error(NGX_LOG_EMERG, log, errno, \"close() failed\");\n            return NGX_ERROR;\n        }\n    }\n\n    return NGX_OK;\n}\n\n#endif\n"
  },
  {
    "path": "src/os/unix/ngx_process.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n#include <ngx_channel.h>\n\n\ntypedef struct {\n    int     signo;\n    char   *signame;\n    char   *name;\n    void  (*handler)(int signo, siginfo_t *siginfo, void *ucontext);\n} ngx_signal_t;\n\n\n\nstatic void ngx_execute_proc(ngx_cycle_t *cycle, void *data);\nstatic void ngx_signal_handler(int signo, siginfo_t *siginfo, void *ucontext);\nstatic void ngx_process_get_status(void);\nstatic void ngx_unlock_mutexes(ngx_pid_t pid);\n\n\nint              ngx_argc;\nchar           **ngx_argv;\nchar           **ngx_os_argv;\n\nngx_int_t        ngx_process_slot;\nngx_socket_t     ngx_channel;\nngx_int_t        ngx_last_process;\nngx_process_t    ngx_processes[NGX_MAX_PROCESSES];\n\n\nngx_signal_t  signals[] = {\n    { ngx_signal_value(NGX_RECONFIGURE_SIGNAL),\n      \"SIG\" ngx_value(NGX_RECONFIGURE_SIGNAL),\n      \"reload\",\n      ngx_signal_handler },\n\n    { ngx_signal_value(NGX_REOPEN_SIGNAL),\n      \"SIG\" ngx_value(NGX_REOPEN_SIGNAL),\n      \"reopen\",\n      ngx_signal_handler },\n\n    { ngx_signal_value(NGX_NOACCEPT_SIGNAL),\n      \"SIG\" ngx_value(NGX_NOACCEPT_SIGNAL),\n      \"\",\n      ngx_signal_handler },\n\n    { ngx_signal_value(NGX_TERMINATE_SIGNAL),\n      \"SIG\" ngx_value(NGX_TERMINATE_SIGNAL),\n      \"stop\",\n      ngx_signal_handler },\n\n    { ngx_signal_value(NGX_SHUTDOWN_SIGNAL),\n      \"SIG\" ngx_value(NGX_SHUTDOWN_SIGNAL),\n      \"quit\",\n      ngx_signal_handler },\n\n    { ngx_signal_value(NGX_CHANGEBIN_SIGNAL),\n      \"SIG\" ngx_value(NGX_CHANGEBIN_SIGNAL),\n      \"\",\n      ngx_signal_handler },\n\n#if (T_NGX_HAVE_XUDP)\n    { ngx_signal_value(NGX_XUDP_TERMINATE_SIGNAL),\n      \"SIG\" ngx_value(NGX_XUDP_TERMINATE_SIGNAL),\n      \"\",\n      ngx_signal_handler },\n#endif\n\n    { SIGALRM, \"SIGALRM\", \"\", ngx_signal_handler },\n\n    { SIGINT, \"SIGINT\", \"\", ngx_signal_handler },\n\n    { SIGIO, \"SIGIO\", \"\", ngx_signal_handler },\n\n    { SIGCHLD, \"SIGCHLD\", \"\", ngx_signal_handler },\n\n    { SIGSYS, \"SIGSYS, SIG_IGN\", \"\", NULL },\n\n    { SIGPIPE, \"SIGPIPE, SIG_IGN\", \"\", NULL },\n\n    { 0, NULL, \"\", NULL }\n};\n\n\nngx_pid_t\nngx_spawn_process(ngx_cycle_t *cycle, ngx_spawn_proc_pt proc, void *data,\n    char *name, ngx_int_t respawn)\n{\n    u_long     on;\n    ngx_pid_t  pid;\n    ngx_int_t  s;\n\n    if (respawn >= 0) {\n        s = respawn;\n\n    } else {\n        for (s = 0; s < ngx_last_process; s++) {\n            if (ngx_processes[s].pid == -1) {\n                break;\n            }\n        }\n\n        if (s == NGX_MAX_PROCESSES) {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,\n                          \"no more than %d processes can be spawned\",\n                          NGX_MAX_PROCESSES);\n            return NGX_INVALID_PID;\n        }\n    }\n\n\n    if (respawn != NGX_PROCESS_DETACHED) {\n\n        /* Solaris 9 still has no AF_LOCAL */\n\n        if (socketpair(AF_UNIX, SOCK_STREAM, 0, ngx_processes[s].channel) == -1)\n        {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                          \"socketpair() failed while spawning \\\"%s\\\"\", name);\n            return NGX_INVALID_PID;\n        }\n\n        ngx_log_debug2(NGX_LOG_DEBUG_CORE, cycle->log, 0,\n                       \"channel %d:%d\",\n                       ngx_processes[s].channel[0],\n                       ngx_processes[s].channel[1]);\n\n        if (ngx_nonblocking(ngx_processes[s].channel[0]) == -1) {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                          ngx_nonblocking_n \" failed while spawning \\\"%s\\\"\",\n                          name);\n            ngx_close_channel(ngx_processes[s].channel, cycle->log);\n            return NGX_INVALID_PID;\n        }\n\n        if (ngx_nonblocking(ngx_processes[s].channel[1]) == -1) {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                          ngx_nonblocking_n \" failed while spawning \\\"%s\\\"\",\n                          name);\n            ngx_close_channel(ngx_processes[s].channel, cycle->log);\n            return NGX_INVALID_PID;\n        }\n\n        on = 1;\n        if (ioctl(ngx_processes[s].channel[0], FIOASYNC, &on) == -1) {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                          \"ioctl(FIOASYNC) failed while spawning \\\"%s\\\"\", name);\n            ngx_close_channel(ngx_processes[s].channel, cycle->log);\n            return NGX_INVALID_PID;\n        }\n\n        if (fcntl(ngx_processes[s].channel[0], F_SETOWN, ngx_pid) == -1) {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                          \"fcntl(F_SETOWN) failed while spawning \\\"%s\\\"\", name);\n            ngx_close_channel(ngx_processes[s].channel, cycle->log);\n            return NGX_INVALID_PID;\n        }\n\n        if (fcntl(ngx_processes[s].channel[0], F_SETFD, FD_CLOEXEC) == -1) {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                          \"fcntl(FD_CLOEXEC) failed while spawning \\\"%s\\\"\",\n                           name);\n            ngx_close_channel(ngx_processes[s].channel, cycle->log);\n            return NGX_INVALID_PID;\n        }\n\n        if (fcntl(ngx_processes[s].channel[1], F_SETFD, FD_CLOEXEC) == -1) {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                          \"fcntl(FD_CLOEXEC) failed while spawning \\\"%s\\\"\",\n                           name);\n            ngx_close_channel(ngx_processes[s].channel, cycle->log);\n            return NGX_INVALID_PID;\n        }\n\n        ngx_channel = ngx_processes[s].channel[1];\n\n    } else {\n        ngx_processes[s].channel[0] = -1;\n        ngx_processes[s].channel[1] = -1;\n    }\n\n    ngx_process_slot = s;\n\n\n    pid = fork();\n\n    switch (pid) {\n\n    case -1:\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                      \"fork() failed while spawning \\\"%s\\\"\", name);\n        ngx_close_channel(ngx_processes[s].channel, cycle->log);\n        return NGX_INVALID_PID;\n\n    case 0:\n        ngx_parent = ngx_pid;\n        ngx_pid = ngx_getpid();\n        proc(cycle, data);\n        break;\n\n    default:\n        break;\n    }\n\n    ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, \"start %s %P\", name, pid);\n\n    ngx_processes[s].pid = pid;\n    ngx_processes[s].exited = 0;\n\n    if (respawn >= 0) {\n        return pid;\n    }\n\n    ngx_processes[s].proc = proc;\n    ngx_processes[s].data = data;\n    ngx_processes[s].name = name;\n    ngx_processes[s].exiting = 0;\n\n    switch (respawn) {\n\n    case NGX_PROCESS_NORESPAWN:\n        ngx_processes[s].respawn = 0;\n        ngx_processes[s].just_spawn = 0;\n        ngx_processes[s].detached = 0;\n        break;\n\n    case NGX_PROCESS_JUST_SPAWN:\n        ngx_processes[s].respawn = 0;\n        ngx_processes[s].just_spawn = 1;\n        ngx_processes[s].detached = 0;\n        break;\n\n    case NGX_PROCESS_RESPAWN:\n        ngx_processes[s].respawn = 1;\n        ngx_processes[s].just_spawn = 0;\n        ngx_processes[s].detached = 0;\n        break;\n\n    case NGX_PROCESS_JUST_RESPAWN:\n        ngx_processes[s].respawn = 1;\n        ngx_processes[s].just_spawn = 1;\n        ngx_processes[s].detached = 0;\n        break;\n\n    case NGX_PROCESS_DETACHED:\n        ngx_processes[s].respawn = 0;\n        ngx_processes[s].just_spawn = 0;\n        ngx_processes[s].detached = 1;\n        break;\n    }\n\n    if (s == ngx_last_process) {\n        ngx_last_process++;\n    }\n\n    return pid;\n}\n\n\nngx_pid_t\nngx_execute(ngx_cycle_t *cycle, ngx_exec_ctx_t *ctx)\n{\n    return ngx_spawn_process(cycle, ngx_execute_proc, ctx, ctx->name,\n                             NGX_PROCESS_DETACHED);\n}\n\n\nstatic void\nngx_execute_proc(ngx_cycle_t *cycle, void *data)\n{\n    ngx_exec_ctx_t  *ctx = data;\n\n    if (execve(ctx->path, ctx->argv, ctx->envp) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                      \"execve() failed while executing %s \\\"%s\\\"\",\n                      ctx->name, ctx->path);\n    }\n\n    exit(1);\n}\n\n\nngx_int_t\nngx_init_signals(ngx_log_t *log)\n{\n    ngx_signal_t      *sig;\n    struct sigaction   sa;\n\n    for (sig = signals; sig->signo != 0; sig++) {\n        ngx_memzero(&sa, sizeof(struct sigaction));\n\n        if (sig->handler) {\n            sa.sa_sigaction = sig->handler;\n            sa.sa_flags = SA_SIGINFO;\n\n        } else {\n            sa.sa_handler = SIG_IGN;\n        }\n\n        sigemptyset(&sa.sa_mask);\n        if (sigaction(sig->signo, &sa, NULL) == -1) {\n#if (NGX_VALGRIND)\n            ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,\n                          \"sigaction(%s) failed, ignored\", sig->signame);\n#else\n            ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,\n                          \"sigaction(%s) failed\", sig->signame);\n            return NGX_ERROR;\n#endif\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_signal_handler(int signo, siginfo_t *siginfo, void *ucontext)\n{\n    char            *action;\n    ngx_int_t        ignore;\n    ngx_err_t        err;\n    ngx_signal_t    *sig;\n\n    ignore = 0;\n\n    err = ngx_errno;\n\n    for (sig = signals; sig->signo != 0; sig++) {\n        if (sig->signo == signo) {\n            break;\n        }\n    }\n\n    ngx_time_sigsafe_update();\n\n    action = \"\";\n\n    switch (ngx_process) {\n\n    case NGX_PROCESS_MASTER:\n    case NGX_PROCESS_SINGLE:\n#if (T_PIPES)\n    case NGX_PROCESS_PIPE:\n#endif\n        switch (signo) {\n\n        case ngx_signal_value(NGX_SHUTDOWN_SIGNAL):\n            ngx_quit = 1;\n            action = \", shutting down\";\n            break;\n\n        case ngx_signal_value(NGX_TERMINATE_SIGNAL):\n        case SIGINT:\n            ngx_terminate = 1;\n            action = \", exiting\";\n            break;\n\n        case ngx_signal_value(NGX_NOACCEPT_SIGNAL):\n            if (ngx_daemonized) {\n                ngx_noaccept = 1;\n                action = \", stop accepting connections\";\n            }\n            break;\n\n        case ngx_signal_value(NGX_RECONFIGURE_SIGNAL):\n            ngx_reconfigure = 1;\n            action = \", reconfiguring\";\n            break;\n\n        case ngx_signal_value(NGX_REOPEN_SIGNAL):\n            ngx_reopen = 1;\n            action = \", reopening logs\";\n            break;\n\n        case ngx_signal_value(NGX_CHANGEBIN_SIGNAL):\n            if (ngx_getppid() == ngx_parent || ngx_new_binary > 0) {\n\n                /*\n                 * Ignore the signal in the new binary if its parent is\n                 * not changed, i.e. the old binary's process is still\n                 * running.  Or ignore the signal in the old binary's\n                 * process if the new binary's process is already running.\n                 */\n\n                action = \", ignoring\";\n                ignore = 1;\n                break;\n            }\n\n            ngx_change_binary = 1;\n            action = \", changing binary\";\n            break;\n\n        case SIGALRM:\n            ngx_sigalrm = 1;\n            break;\n\n        case SIGIO:\n            ngx_sigio = 1;\n            break;\n\n        case SIGCHLD:\n            ngx_reap = 1;\n            break;\n        }\n\n        break;\n\n    case NGX_PROCESS_WORKER:\n    case NGX_PROCESS_HELPER:\n#if (NGX_PROCS)\n    case NGX_PROCESS_PROC:\n#endif\n        switch (signo) {\n\n        case ngx_signal_value(NGX_NOACCEPT_SIGNAL):\n            if (!ngx_daemonized) {\n                break;\n            }\n            ngx_debug_quit = 1;\n            /* fall through */\n        case ngx_signal_value(NGX_SHUTDOWN_SIGNAL):\n            ngx_quit = 1;\n            action = \", shutting down\";\n            break;\n\n        case ngx_signal_value(NGX_TERMINATE_SIGNAL):\n        case SIGINT:\n            ngx_terminate = 1;\n            action = \", exiting\";\n            break;\n\n        case ngx_signal_value(NGX_REOPEN_SIGNAL):\n            ngx_reopen = 1;\n            action = \", reopening logs\";\n            break;\n\n        case ngx_signal_value(NGX_RECONFIGURE_SIGNAL):\n        case ngx_signal_value(NGX_CHANGEBIN_SIGNAL):\n        case SIGIO:\n            action = \", ignoring\";\n            break;\n        }\n\n        break;\n    }\n\n    if (siginfo && siginfo->si_pid) {\n        ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0,\n                      \"signal %d (%s) received from %P%s\",\n                      signo, sig->signame, siginfo->si_pid, action);\n\n    } else {\n        ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0,\n                      \"signal %d (%s) received%s\",\n                      signo, sig->signame, action);\n    }\n\n    if (ignore) {\n        ngx_log_error(NGX_LOG_CRIT, ngx_cycle->log, 0,\n                      \"the changing binary signal is ignored: \"\n                      \"you should shutdown or terminate \"\n                      \"before either old or new binary's process\");\n    }\n\n    if (signo == SIGCHLD) {\n        ngx_process_get_status();\n    }\n\n    ngx_set_errno(err);\n}\n\n\nstatic void\nngx_process_get_status(void)\n{\n    int              status;\n    char            *process;\n    ngx_pid_t        pid;\n    ngx_err_t        err;\n    ngx_int_t        i;\n    ngx_uint_t       one;\n\n    one = 0;\n\n    for ( ;; ) {\n        pid = waitpid(-1, &status, WNOHANG);\n\n        if (pid == 0) {\n            return;\n        }\n\n        if (pid == -1) {\n            err = ngx_errno;\n\n            if (err == NGX_EINTR) {\n                continue;\n            }\n\n            if (err == NGX_ECHILD && one) {\n                return;\n            }\n\n            /*\n             * Solaris always calls the signal handler for each exited process\n             * despite waitpid() may be already called for this process.\n             *\n             * When several processes exit at the same time FreeBSD may\n             * erroneously call the signal handler for exited process\n             * despite waitpid() may be already called for this process.\n             */\n\n            if (err == NGX_ECHILD) {\n                ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, err,\n                              \"waitpid() failed\");\n                return;\n            }\n\n            ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, err,\n                          \"waitpid() failed\");\n            return;\n        }\n\n\n        one = 1;\n        process = \"unknown process\";\n\n        for (i = 0; i < ngx_last_process; i++) {\n            if (ngx_processes[i].pid == pid) {\n                ngx_processes[i].status = status;\n                ngx_processes[i].exited = 1;\n                process = ngx_processes[i].name;\n                break;\n            }\n        }\n\n#if (T_PIPES)\n        if (i == ngx_last_process) {\n            process = \"pipe process\";\n            ngx_pipe_broken_action(ngx_cycle->log, pid, 1);\n        }\n#endif\n\n        if (WTERMSIG(status)) {\n#ifdef WCOREDUMP\n            ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,\n                          \"%s %P exited on signal %d%s\",\n                          process, pid, WTERMSIG(status),\n                          WCOREDUMP(status) ? \" (core dumped)\" : \"\");\n#else\n            ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,\n                          \"%s %P exited on signal %d\",\n                          process, pid, WTERMSIG(status));\n#endif\n\n        } else {\n            ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0,\n                          \"%s %P exited with code %d\",\n                          process, pid, WEXITSTATUS(status));\n        }\n\n        if (WEXITSTATUS(status) == 2 && ngx_processes[i].respawn) {\n            ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,\n                          \"%s %P exited with fatal code %d \"\n                          \"and cannot be respawned\",\n                          process, pid, WEXITSTATUS(status));\n            ngx_processes[i].respawn = 0;\n        }\n\n        ngx_unlock_mutexes(pid);\n    }\n}\n\n\nstatic void\nngx_unlock_mutexes(ngx_pid_t pid)\n{\n    ngx_uint_t        i;\n    ngx_shm_zone_t   *shm_zone;\n    ngx_list_part_t  *part;\n    ngx_slab_pool_t  *sp;\n\n    /*\n     * unlock the accept mutex if the abnormally exited process\n     * held it\n     */\n\n    if (ngx_accept_mutex_ptr) {\n        (void) ngx_shmtx_force_unlock(&ngx_accept_mutex, pid);\n    }\n\n    /*\n     * unlock shared memory mutexes if held by the abnormally exited\n     * process\n     */\n\n    part = (ngx_list_part_t *) &ngx_cycle->shared_memory.part;\n    shm_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            part = part->next;\n            shm_zone = part->elts;\n            i = 0;\n        }\n\n        sp = (ngx_slab_pool_t *) shm_zone[i].shm.addr;\n\n        if (ngx_shmtx_force_unlock(&sp->mutex, pid)) {\n            ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,\n                          \"shared memory zone \\\"%V\\\" was locked by %P\",\n                          &shm_zone[i].shm.name, pid);\n        }\n    }\n}\n\n\nvoid\nngx_debug_point(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    switch (ccf->debug_points) {\n\n    case NGX_DEBUG_POINTS_STOP:\n        raise(SIGSTOP);\n        break;\n\n    case NGX_DEBUG_POINTS_ABORT:\n        ngx_abort();\n    }\n}\n\n\nngx_int_t\nngx_os_signal_process(ngx_cycle_t *cycle, char *name, ngx_pid_t pid)\n{\n    ngx_signal_t  *sig;\n\n    for (sig = signals; sig->signo != 0; sig++) {\n        if (ngx_strcmp(name, sig->name) == 0) {\n            if (kill(pid, sig->signo) != -1) {\n                return 0;\n            }\n\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                          \"kill(%P, %d) failed\", pid, sig->signo);\n        }\n    }\n\n    return 1;\n}\n"
  },
  {
    "path": "src/os/unix/ngx_process.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_PROCESS_H_INCLUDED_\n#define _NGX_PROCESS_H_INCLUDED_\n\n\n#include <ngx_setaffinity.h>\n#include <ngx_setproctitle.h>\n\n\ntypedef pid_t       ngx_pid_t;\n\n#define NGX_INVALID_PID  -1\n\ntypedef void (*ngx_spawn_proc_pt) (ngx_cycle_t *cycle, void *data);\n\ntypedef struct {\n    ngx_pid_t           pid;\n    int                 status;\n    ngx_socket_t        channel[2];\n\n    ngx_spawn_proc_pt   proc;\n    void               *data;\n    char               *name;\n\n    unsigned            respawn:1;\n    unsigned            just_spawn:1;\n    unsigned            detached:1;\n    unsigned            exiting:1;\n    unsigned            exited:1;\n} ngx_process_t;\n\n\ntypedef struct {\n    char         *path;\n    char         *name;\n    char *const  *argv;\n    char *const  *envp;\n} ngx_exec_ctx_t;\n\n\n#define NGX_MAX_PROCESSES         1024\n\n#define NGX_PROCESS_NORESPAWN     -1\n#define NGX_PROCESS_JUST_SPAWN    -2\n#define NGX_PROCESS_RESPAWN       -3\n#define NGX_PROCESS_JUST_RESPAWN  -4\n#define NGX_PROCESS_DETACHED      -5\n\n\n#define ngx_getpid   getpid\n#define ngx_getppid  getppid\n\n#ifndef ngx_log_pid\n#define ngx_log_pid  ngx_pid\n#endif\n\n\nngx_pid_t ngx_spawn_process(ngx_cycle_t *cycle,\n    ngx_spawn_proc_pt proc, void *data, char *name, ngx_int_t respawn);\nngx_pid_t ngx_execute(ngx_cycle_t *cycle, ngx_exec_ctx_t *ctx);\nngx_int_t ngx_init_signals(ngx_log_t *log);\nvoid ngx_debug_point(void);\n\n\n#if (NGX_HAVE_SCHED_YIELD)\n#define ngx_sched_yield()  sched_yield()\n#else\n#define ngx_sched_yield()  usleep(1)\n#endif\n\n\nextern int            ngx_argc;\nextern char         **ngx_argv;\nextern char         **ngx_os_argv;\n\nextern ngx_pid_t      ngx_pid;\nextern ngx_pid_t      ngx_parent;\nextern ngx_socket_t   ngx_channel;\nextern ngx_int_t      ngx_process_slot;\nextern ngx_int_t      ngx_last_process;\nextern ngx_process_t  ngx_processes[NGX_MAX_PROCESSES];\n\n\n#endif /* _NGX_PROCESS_H_INCLUDED_ */\n"
  },
  {
    "path": "src/os/unix/ngx_process_cycle.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n#include <ngx_channel.h>\n\n\n#if (T_PIPES)\n#define NGX_PIPE_STILL_NEEDED     0x02\n#endif\n\nstatic void ngx_start_worker_processes(ngx_cycle_t *cycle, ngx_int_t n,\n    ngx_int_t type);\nstatic void ngx_start_cache_manager_processes(ngx_cycle_t *cycle,\n    ngx_uint_t respawn);\nstatic void ngx_pass_open_channel(ngx_cycle_t *cycle);\nstatic void ngx_signal_worker_processes(ngx_cycle_t *cycle, int signo);\nstatic ngx_uint_t ngx_reap_children(ngx_cycle_t *cycle);\nstatic void ngx_master_process_exit(ngx_cycle_t *cycle);\nstatic void ngx_worker_process_cycle(ngx_cycle_t *cycle, void *data);\nstatic void ngx_worker_process_init(ngx_cycle_t *cycle, ngx_int_t worker);\nstatic void ngx_worker_process_exit(ngx_cycle_t *cycle);\nstatic void ngx_channel_handler(ngx_event_t *ev);\nstatic void ngx_cache_manager_process_cycle(ngx_cycle_t *cycle, void *data);\nstatic void ngx_cache_manager_process_handler(ngx_event_t *ev);\nstatic void ngx_cache_loader_process_handler(ngx_event_t *ev);\n\n\nngx_uint_t    ngx_process;\nngx_uint_t    ngx_worker;\nngx_pid_t     ngx_pid;\nngx_pid_t     ngx_parent;\n\nsig_atomic_t  ngx_reap;\nsig_atomic_t  ngx_sigio;\nsig_atomic_t  ngx_sigalrm;\nsig_atomic_t  ngx_terminate;\nsig_atomic_t  ngx_quit;\nsig_atomic_t  ngx_debug_quit;\nngx_uint_t    ngx_exiting;\nsig_atomic_t  ngx_reconfigure;\nsig_atomic_t  ngx_reopen;\n\nsig_atomic_t  ngx_change_binary;\nngx_pid_t     ngx_new_binary;\nngx_uint_t    ngx_inherited;\nngx_uint_t    ngx_daemonized;\n\nsig_atomic_t  ngx_noaccept;\nngx_uint_t    ngx_noaccepting;\nngx_uint_t    ngx_restart;\n\n\nstatic u_char  master_process[] = \"master process\";\n\n\nstatic ngx_cache_manager_ctx_t  ngx_cache_manager_ctx = {\n    ngx_cache_manager_process_handler, \"cache manager process\", 0\n};\n\nstatic ngx_cache_manager_ctx_t  ngx_cache_loader_ctx = {\n    ngx_cache_loader_process_handler, \"cache loader process\", 60000\n};\n\n\nstatic ngx_cycle_t      ngx_exit_cycle;\nstatic ngx_log_t        ngx_exit_log;\nstatic ngx_open_file_t  ngx_exit_log_file;\n\n\nvoid\nngx_master_process_cycle(ngx_cycle_t *cycle)\n{\n    char              *title;\n    u_char            *p;\n    size_t             size;\n    ngx_int_t          i;\n    ngx_uint_t         sigio;\n#if (T_PIPES)\n    ngx_uint_t         close_old_pipe;\n#endif\n    sigset_t           set;\n    struct itimerval   itv;\n    ngx_uint_t         live;\n    ngx_msec_t         delay;\n    ngx_core_conf_t   *ccf;\n\n    sigemptyset(&set);\n    sigaddset(&set, SIGCHLD);\n    sigaddset(&set, SIGALRM);\n    sigaddset(&set, SIGIO);\n    sigaddset(&set, SIGINT);\n    sigaddset(&set, ngx_signal_value(NGX_RECONFIGURE_SIGNAL));\n    sigaddset(&set, ngx_signal_value(NGX_REOPEN_SIGNAL));\n    sigaddset(&set, ngx_signal_value(NGX_NOACCEPT_SIGNAL));\n    sigaddset(&set, ngx_signal_value(NGX_TERMINATE_SIGNAL));\n    sigaddset(&set, ngx_signal_value(NGX_SHUTDOWN_SIGNAL));\n    sigaddset(&set, ngx_signal_value(NGX_CHANGEBIN_SIGNAL));\n\n    if (sigprocmask(SIG_BLOCK, &set, NULL) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                      \"sigprocmask() failed\");\n    }\n\n    sigemptyset(&set);\n\n\n    size = sizeof(master_process);\n\n    for (i = 0; i < ngx_argc; i++) {\n        size += ngx_strlen(ngx_argv[i]) + 1;\n    }\n\n    title = ngx_pnalloc(cycle->pool, size);\n    if (title == NULL) {\n        /* fatal */\n        exit(2);\n    }\n\n    p = ngx_cpymem(title, master_process, sizeof(master_process) - 1);\n    for (i = 0; i < ngx_argc; i++) {\n        *p++ = ' ';\n        p = ngx_cpystrn(p, (u_char *) ngx_argv[i], size);\n    }\n\n    ngx_setproctitle(title);\n\n\n    ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);\n\n    ngx_start_worker_processes(cycle, ccf->worker_processes,\n                               NGX_PROCESS_RESPAWN);\n    ngx_start_cache_manager_processes(cycle, 0);\n\n#if (NGX_PROCS)\n    ngx_procs_start(cycle, 0);\n#endif\n\n    ngx_new_binary = 0;\n#if (T_PIPES)\n    close_old_pipe = 0;\n#endif\n    delay = 0;\n    sigio = 0;\n    live = 1;\n\n    for ( ;; ) {\n        if (delay) {\n            if (ngx_sigalrm) {\n                sigio = 0;\n                delay *= 2;\n                ngx_sigalrm = 0;\n            }\n\n            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                           \"termination cycle: %M\", delay);\n\n            itv.it_interval.tv_sec = 0;\n            itv.it_interval.tv_usec = 0;\n            itv.it_value.tv_sec = delay / 1000;\n            itv.it_value.tv_usec = (delay % 1000 ) * 1000;\n\n            if (setitimer(ITIMER_REAL, &itv, NULL) == -1) {\n                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                              \"setitimer() failed\");\n            }\n        }\n\n        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, \"sigsuspend\");\n\n        sigsuspend(&set);\n\n        ngx_time_update();\n\n        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                       \"wake up, sigio %i\", sigio);\n\n        if (ngx_reap) {\n            ngx_reap = 0;\n            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, \"reap children\");\n\n            live = ngx_reap_children(cycle);\n#if (T_PIPES)\n            if (!(live & NGX_PIPE_STILL_NEEDED) && close_old_pipe) {\n                ngx_close_old_pipes();\n                close_old_pipe = 0;\n            }\n#endif\n        }\n\n        if (!live && (ngx_terminate || ngx_quit)) {\n            ngx_master_process_exit(cycle);\n        }\n\n        if (ngx_terminate) {\n            if (delay == 0) {\n                delay = 50;\n            }\n\n            if (sigio) {\n                sigio--;\n                continue;\n            }\n\n            sigio = ccf->worker_processes + 2 /* cache processes */;\n\n            if (delay > 1000) {\n                ngx_signal_worker_processes(cycle, SIGKILL);\n            } else {\n                ngx_signal_worker_processes(cycle,\n                                       ngx_signal_value(NGX_TERMINATE_SIGNAL));\n            }\n\n            continue;\n        }\n\n        if (ngx_quit) {\n            ngx_signal_worker_processes(cycle,\n                                        ngx_signal_value(NGX_SHUTDOWN_SIGNAL));\n            ngx_close_listening_sockets(cycle);\n\n            continue;\n        }\n\n        if (ngx_reconfigure) {\n            ngx_reconfigure = 0;\n\n            if (ngx_new_binary) {\n                ngx_start_worker_processes(cycle, ccf->worker_processes,\n                                           NGX_PROCESS_RESPAWN);\n                ngx_start_cache_manager_processes(cycle, 0);\n\n#if (NGX_PROCS)\n                ngx_procs_start(cycle, 0);\n#endif\n                ngx_noaccepting = 0;\n\n                continue;\n            }\n\n            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, \"reconfiguring\");\n\n            cycle = ngx_init_cycle(cycle);\n            if (cycle == NULL) {\n                cycle = (ngx_cycle_t *) ngx_cycle;\n                continue;\n            }\n\n            ngx_cycle = cycle;\n            ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx,\n                                                   ngx_core_module);\n            ngx_start_worker_processes(cycle, ccf->worker_processes,\n                                       NGX_PROCESS_JUST_RESPAWN);\n            ngx_start_cache_manager_processes(cycle, 1);\n\n#if (NGX_PROCS)\n            ngx_procs_start(cycle, 1);\n#endif\n\n            /* allow new processes to start */\n            ngx_msleep(100);\n\n            live = 1;\n#if (T_PIPES)\n            close_old_pipe = 1;\n#endif\n            ngx_signal_worker_processes(cycle,\n                                        ngx_signal_value(NGX_SHUTDOWN_SIGNAL));\n        }\n\n        if (ngx_restart) {\n            ngx_restart = 0;\n            ngx_start_worker_processes(cycle, ccf->worker_processes,\n                                       NGX_PROCESS_RESPAWN);\n            ngx_start_cache_manager_processes(cycle, 0);\n\n#if (NGX_PROCS)\n            ngx_procs_start(cycle, 0);\n#endif\n\n            live = 1;\n        }\n\n        if (ngx_reopen) {\n            ngx_reopen = 0;\n            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, \"reopening logs\");\n            ngx_reopen_files(cycle, ccf->user);\n            ngx_signal_worker_processes(cycle,\n                                        ngx_signal_value(NGX_REOPEN_SIGNAL));\n        }\n\n        if (ngx_change_binary) {\n            ngx_change_binary = 0;\n            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, \"changing binary\");\n            ngx_new_binary = ngx_exec_new_binary(cycle, ngx_argv);\n        }\n\n        if (ngx_noaccept) {\n            ngx_noaccept = 0;\n            ngx_noaccepting = 1;\n            ngx_signal_worker_processes(cycle,\n                                        ngx_signal_value(NGX_SHUTDOWN_SIGNAL));\n        }\n    }\n}\n\n\nvoid\nngx_single_process_cycle(ngx_cycle_t *cycle)\n{\n    ngx_uint_t  i;\n\n    if (ngx_set_environment(cycle, NULL) == NULL) {\n        /* fatal */\n        exit(2);\n    }\n\n    for (i = 0; cycle->modules[i]; i++) {\n        if (cycle->modules[i]->init_process) {\n            if (cycle->modules[i]->init_process(cycle) == NGX_ERROR) {\n                /* fatal */\n                exit(2);\n            }\n        }\n    }\n\n    for ( ;; ) {\n        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, \"worker cycle\");\n\n        ngx_process_events_and_timers(cycle);\n\n        if (ngx_terminate || ngx_quit) {\n\n            for (i = 0; cycle->modules[i]; i++) {\n                if (cycle->modules[i]->exit_process) {\n                    cycle->modules[i]->exit_process(cycle);\n                }\n            }\n\n            ngx_master_process_exit(cycle);\n        }\n\n        if (ngx_reconfigure) {\n            ngx_reconfigure = 0;\n            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, \"reconfiguring\");\n\n            cycle = ngx_init_cycle(cycle);\n            if (cycle == NULL) {\n                cycle = (ngx_cycle_t *) ngx_cycle;\n                continue;\n            }\n\n            ngx_cycle = cycle;\n        }\n\n        if (ngx_reopen) {\n            ngx_reopen = 0;\n            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, \"reopening logs\");\n            ngx_reopen_files(cycle, (ngx_uid_t) -1);\n        }\n    }\n}\n\n\nstatic void\nngx_start_worker_processes(ngx_cycle_t *cycle, ngx_int_t n, ngx_int_t type)\n{\n    ngx_int_t  i;\n\n    ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, \"start worker processes\");\n\n    for (i = 0; i < n; i++) {\n\n        ngx_spawn_process(cycle, ngx_worker_process_cycle,\n                          (void *) (intptr_t) i, \"worker process\", type);\n\n        ngx_pass_open_channel(cycle);\n    }\n}\n\n\nstatic void\nngx_start_cache_manager_processes(ngx_cycle_t *cycle, ngx_uint_t respawn)\n{\n    ngx_uint_t    i, manager, loader;\n    ngx_path_t  **path;\n\n    manager = 0;\n    loader = 0;\n\n    path = ngx_cycle->paths.elts;\n    for (i = 0; i < ngx_cycle->paths.nelts; i++) {\n\n        if (path[i]->manager) {\n            manager = 1;\n        }\n\n        if (path[i]->loader) {\n            loader = 1;\n        }\n    }\n\n    if (manager == 0) {\n        return;\n    }\n\n    ngx_spawn_process(cycle, ngx_cache_manager_process_cycle,\n                      &ngx_cache_manager_ctx, \"cache manager process\",\n                      respawn ? NGX_PROCESS_JUST_RESPAWN : NGX_PROCESS_RESPAWN);\n\n    ngx_pass_open_channel(cycle);\n\n    if (loader == 0) {\n        return;\n    }\n\n    ngx_spawn_process(cycle, ngx_cache_manager_process_cycle,\n                      &ngx_cache_loader_ctx, \"cache loader process\",\n                      respawn ? NGX_PROCESS_JUST_SPAWN : NGX_PROCESS_NORESPAWN);\n\n    ngx_pass_open_channel(cycle);\n}\n\n\nstatic void\nngx_pass_open_channel(ngx_cycle_t *cycle)\n{\n    ngx_int_t      i;\n    ngx_channel_t  ch;\n\n    ngx_memzero(&ch, sizeof(ngx_channel_t));\n\n    ch.command = NGX_CMD_OPEN_CHANNEL;\n    ch.pid = ngx_processes[ngx_process_slot].pid;\n    ch.slot = ngx_process_slot;\n    ch.fd = ngx_processes[ngx_process_slot].channel[0];\n\n    for (i = 0; i < ngx_last_process; i++) {\n\n        if (i == ngx_process_slot\n            || ngx_processes[i].pid == -1\n            || ngx_processes[i].channel[0] == -1)\n        {\n            continue;\n        }\n\n        ngx_log_debug6(NGX_LOG_DEBUG_CORE, cycle->log, 0,\n                      \"pass channel s:%i pid:%P fd:%d to s:%i pid:%P fd:%d\",\n                      ch.slot, ch.pid, ch.fd,\n                      i, ngx_processes[i].pid,\n                      ngx_processes[i].channel[0]);\n\n        /* TODO: NGX_AGAIN */\n\n        ngx_write_channel(ngx_processes[i].channel[0],\n                          &ch, sizeof(ngx_channel_t), cycle->log);\n    }\n}\n\n\nstatic void\nngx_signal_worker_processes(ngx_cycle_t *cycle, int signo)\n{\n    ngx_int_t      i;\n    ngx_err_t      err;\n    ngx_channel_t  ch;\n\n    ngx_memzero(&ch, sizeof(ngx_channel_t));\n\n#if (NGX_BROKEN_SCM_RIGHTS)\n\n    ch.command = 0;\n\n#else\n\n    switch (signo) {\n\n    case ngx_signal_value(NGX_SHUTDOWN_SIGNAL):\n        ch.command = NGX_CMD_QUIT;\n        break;\n\n    case ngx_signal_value(NGX_TERMINATE_SIGNAL):\n        ch.command = NGX_CMD_TERMINATE;\n        break;\n\n    case ngx_signal_value(NGX_REOPEN_SIGNAL):\n        ch.command = NGX_CMD_REOPEN;\n        break;\n\n#if (T_NGX_HAVE_XUDP)\n    case ngx_signal_value(NGX_XUDP_TERMINATE_SIGNAL):\n        ch.command = NGX_CMD_UNBIND_XDP;\n        break;\n#endif\n\n    default:\n        ch.command = 0;\n    }\n\n#endif\n\n    ch.fd = -1;\n\n\n    for (i = 0; i < ngx_last_process; i++) {\n\n        ngx_log_debug7(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                       \"child: %i %P e:%d t:%d d:%d r:%d j:%d\",\n                       i,\n                       ngx_processes[i].pid,\n                       ngx_processes[i].exiting,\n                       ngx_processes[i].exited,\n                       ngx_processes[i].detached,\n                       ngx_processes[i].respawn,\n                       ngx_processes[i].just_spawn);\n\n        if (ngx_processes[i].detached || ngx_processes[i].pid == -1) {\n            continue;\n        }\n\n        if (ngx_processes[i].just_spawn) {\n            ngx_processes[i].just_spawn = 0;\n            continue;\n        }\n\n        if (ngx_processes[i].exiting\n            && signo == ngx_signal_value(NGX_SHUTDOWN_SIGNAL))\n        {\n            continue;\n        }\n\n        if (ch.command) {\n            if (ngx_write_channel(ngx_processes[i].channel[0],\n                                  &ch, sizeof(ngx_channel_t), cycle->log)\n                == NGX_OK)\n            {\n                if (signo != ngx_signal_value(NGX_REOPEN_SIGNAL)\n#if (T_NGX_HAVE_XUDP)\n                    && (signo != ngx_signal_value(NGX_XUDP_TERMINATE_SIGNAL))\n#endif\n                    ) {\n                    ngx_processes[i].exiting = 1;\n                }\n\n                continue;\n            }\n        }\n\n        ngx_log_debug2(NGX_LOG_DEBUG_CORE, cycle->log, 0,\n                       \"kill (%P, %d)\", ngx_processes[i].pid, signo);\n\n        if (kill(ngx_processes[i].pid, signo) == -1) {\n            err = ngx_errno;\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, err,\n                          \"kill(%P, %d) failed\", ngx_processes[i].pid, signo);\n\n            if (err == NGX_ESRCH) {\n                ngx_processes[i].exited = 1;\n                ngx_processes[i].exiting = 0;\n                ngx_reap = 1;\n            }\n\n            continue;\n        }\n\n        if (signo != ngx_signal_value(NGX_REOPEN_SIGNAL)) {\n            ngx_processes[i].exiting = 1;\n        }\n    }\n}\n\n\nstatic ngx_uint_t\nngx_reap_children(ngx_cycle_t *cycle)\n{\n    ngx_int_t         i, n;\n    ngx_uint_t        live;\n    ngx_channel_t     ch;\n    ngx_core_conf_t  *ccf;\n\n    ngx_memzero(&ch, sizeof(ngx_channel_t));\n\n    ch.command = NGX_CMD_CLOSE_CHANNEL;\n    ch.fd = -1;\n\n    live = 0;\n    for (i = 0; i < ngx_last_process; i++) {\n\n        ngx_log_debug7(NGX_LOG_DEBUG_EVENT, cycle->log, 0,\n                       \"child: %i %P e:%d t:%d d:%d r:%d j:%d\",\n                       i,\n                       ngx_processes[i].pid,\n                       ngx_processes[i].exiting,\n                       ngx_processes[i].exited,\n                       ngx_processes[i].detached,\n                       ngx_processes[i].respawn,\n                       ngx_processes[i].just_spawn);\n\n        if (ngx_processes[i].pid == -1) {\n            continue;\n        }\n\n        if (ngx_processes[i].exited) {\n\n            if (!ngx_processes[i].detached) {\n                ngx_close_channel(ngx_processes[i].channel, cycle->log);\n\n                ngx_processes[i].channel[0] = -1;\n                ngx_processes[i].channel[1] = -1;\n\n                ch.pid = ngx_processes[i].pid;\n                ch.slot = i;\n\n                for (n = 0; n < ngx_last_process; n++) {\n                    if (ngx_processes[n].exited\n                        || ngx_processes[n].pid == -1\n                        || ngx_processes[n].channel[0] == -1)\n                    {\n                        continue;\n                    }\n\n                    ngx_log_debug3(NGX_LOG_DEBUG_CORE, cycle->log, 0,\n                                   \"pass close channel s:%i pid:%P to:%P\",\n                                   ch.slot, ch.pid, ngx_processes[n].pid);\n\n                    /* TODO: NGX_AGAIN */\n\n                    ngx_write_channel(ngx_processes[n].channel[0],\n                                      &ch, sizeof(ngx_channel_t), cycle->log);\n                }\n            }\n\n            if (ngx_processes[i].respawn\n                && !ngx_processes[i].exiting\n                && !ngx_terminate\n                && !ngx_quit)\n            {\n\n#if (NGX_SSL && NGX_SSL_ASYNC)\n                /* Delay added to give Quickassist Driver time to cleanup\n                * if worker exit with non-zero code. */\n                if(ngx_processes[i].status != 0) {\n                    usleep(2000000);\n                }\n#endif\n                if (ngx_spawn_process(cycle, ngx_processes[i].proc,\n                                      ngx_processes[i].data,\n                                      ngx_processes[i].name, i)\n                    == NGX_INVALID_PID)\n                {\n                    ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,\n                                  \"could not respawn %s\",\n                                  ngx_processes[i].name);\n                    continue;\n                }\n\n\n                ngx_pass_open_channel(cycle);\n#if (T_PIPES)\n                live |= 1;\n#else\n                live = 1;\n#endif\n\n                continue;\n            }\n\n            if (ngx_processes[i].pid == ngx_new_binary) {\n\n                ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx,\n                                                       ngx_core_module);\n\n                if (ngx_rename_file((char *) ccf->oldpid.data,\n                                    (char *) ccf->pid.data)\n                    == NGX_FILE_ERROR)\n                {\n                    ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                                  ngx_rename_file_n \" %s back to %s failed \"\n                                  \"after the new binary process \\\"%s\\\" exited\",\n                                  ccf->oldpid.data, ccf->pid.data, ngx_argv[0]);\n                }\n\n                ngx_new_binary = 0;\n                if (ngx_noaccepting) {\n                    ngx_restart = 1;\n                    ngx_noaccepting = 0;\n                }\n            }\n\n            if (i == ngx_last_process - 1) {\n                ngx_last_process--;\n\n            } else {\n                ngx_processes[i].pid = -1;\n            }\n\n        } else if (ngx_processes[i].exiting || !ngx_processes[i].detached) {\n#if (T_PIPES)\n            live |= 1;\n\n            if (ngx_processes[i].exiting) {\n                live |= NGX_PIPE_STILL_NEEDED;\n            }\n#else\n            live = 1;\n#endif\n        }\n    }\n\n    return live;\n}\n\n\nstatic void\nngx_master_process_exit(ngx_cycle_t *cycle)\n{\n    ngx_uint_t  i;\n\n    ngx_delete_pidfile(cycle);\n\n    ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, \"exit\");\n\n    for (i = 0; cycle->modules[i]; i++) {\n        if (cycle->modules[i]->exit_master) {\n            cycle->modules[i]->exit_master(cycle);\n        }\n    }\n\n    ngx_close_listening_sockets(cycle);\n\n    /*\n     * Copy ngx_cycle->log related data to the special static exit cycle,\n     * log, and log file structures enough to allow a signal handler to log.\n     * The handler may be called when standard ngx_cycle->log allocated from\n     * ngx_cycle->pool is already destroyed.\n     */\n\n\n    ngx_exit_log = *ngx_log_get_file_log(ngx_cycle->log);\n\n    ngx_exit_log_file.fd = ngx_exit_log.file->fd;\n    ngx_exit_log.file = &ngx_exit_log_file;\n    ngx_exit_log.next = NULL;\n    ngx_exit_log.writer = NULL;\n\n    ngx_exit_cycle.log = &ngx_exit_log;\n    ngx_exit_cycle.files = ngx_cycle->files;\n    ngx_exit_cycle.files_n = ngx_cycle->files_n;\n    ngx_cycle = &ngx_exit_cycle;\n\n    ngx_destroy_pool(cycle->pool);\n#if (T_PIPES)\n    /* Terminate all pipe processes */\n\n    ngx_increase_pipe_generation();\n    ngx_close_old_pipes();\n#endif\n\n    exit(0);\n}\n\n\nstatic void\nngx_worker_process_cycle(ngx_cycle_t *cycle, void *data)\n{\n    ngx_int_t worker = (intptr_t) data;\n\n    ngx_process = NGX_PROCESS_WORKER;\n    ngx_worker = worker;\n\n    ngx_worker_process_init(cycle, worker);\n\n    ngx_setproctitle(\"worker process\");\n\n    for ( ;; ) {\n\n        if (ngx_exiting) {\n            if (ngx_event_no_timers_left() == NGX_OK) {\n                ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, \"exiting\");\n                ngx_worker_process_exit(cycle);\n            }\n        }\n\n        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, \"worker cycle\");\n\n        ngx_process_events_and_timers(cycle);\n\n        if (ngx_terminate) {\n            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, \"exiting\");\n            ngx_worker_process_exit(cycle);\n        }\n\n        if (ngx_quit) {\n            ngx_quit = 0;\n            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0,\n                          \"gracefully shutting down\");\n            ngx_setproctitle(\"worker process is shutting down\");\n\n            if (!ngx_exiting) {\n                ngx_exiting = 1;\n                ngx_set_shutdown_timer(cycle);\n                ngx_close_listening_sockets(cycle);\n                ngx_close_idle_connections(cycle);\n                ngx_event_process_posted(cycle, &ngx_posted_events);\n            }\n        }\n\n        if (ngx_reopen) {\n            ngx_reopen = 0;\n            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, \"reopening logs\");\n            ngx_reopen_files(cycle, -1);\n        }\n    }\n}\n\n\nstatic void\nngx_worker_process_init(ngx_cycle_t *cycle, ngx_int_t worker)\n{\n    sigset_t          set;\n    ngx_int_t         n;\n    ngx_time_t       *tp;\n    ngx_uint_t        i;\n    ngx_cpuset_t     *cpu_affinity;\n    struct rlimit     rlmt;\n    ngx_core_conf_t  *ccf;\n\n    if (ngx_set_environment(cycle, NULL) == NULL) {\n        /* fatal */\n        exit(2);\n    }\n\n    ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);\n\n    if (worker >= 0 && ccf->priority != 0) {\n        if (setpriority(PRIO_PROCESS, 0, ccf->priority) == -1) {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                          \"setpriority(%d) failed\", ccf->priority);\n        }\n    }\n\n    if (ccf->rlimit_nofile != NGX_CONF_UNSET) {\n        rlmt.rlim_cur = (rlim_t) ccf->rlimit_nofile;\n        rlmt.rlim_max = (rlim_t) ccf->rlimit_nofile;\n\n        if (setrlimit(RLIMIT_NOFILE, &rlmt) == -1) {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                          \"setrlimit(RLIMIT_NOFILE, %i) failed\",\n                          ccf->rlimit_nofile);\n        }\n    }\n\n    if (ccf->rlimit_core != NGX_CONF_UNSET) {\n        rlmt.rlim_cur = (rlim_t) ccf->rlimit_core;\n        rlmt.rlim_max = (rlim_t) ccf->rlimit_core;\n\n        if (setrlimit(RLIMIT_CORE, &rlmt) == -1) {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                          \"setrlimit(RLIMIT_CORE, %O) failed\",\n                          ccf->rlimit_core);\n        }\n    }\n\n#if (T_NGX_HAVE_XUDP)\n    if (ngx_xudp_open_listening_sockets(cycle) != NGX_OK) {\n        /* fatal */\n        exit(2);\n    }\n#endif\n\n    if (geteuid() == 0) {\n        if (setgid(ccf->group) == -1) {\n            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                          \"setgid(%d) failed\", ccf->group);\n            /* fatal */\n            exit(2);\n        }\n\n        if (initgroups(ccf->username, ccf->group) == -1) {\n            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                          \"initgroups(%s, %d) failed\",\n                          ccf->username, ccf->group);\n        }\n\n#if (NGX_HAVE_PR_SET_KEEPCAPS && NGX_HAVE_CAPABILITIES)\n        if (ccf->transparent && ccf->user) {\n            if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1) {\n                ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                              \"prctl(PR_SET_KEEPCAPS, 1) failed\");\n                /* fatal */\n                exit(2);\n            }\n        }\n#endif\n\n        if (setuid(ccf->user) == -1) {\n            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                          \"setuid(%d) failed\", ccf->user);\n            /* fatal */\n            exit(2);\n        }\n\n#if (NGX_HAVE_CAPABILITIES)\n        if (ccf->transparent && ccf->user) {\n            struct __user_cap_data_struct    data;\n            struct __user_cap_header_struct  header;\n\n            ngx_memzero(&header, sizeof(struct __user_cap_header_struct));\n            ngx_memzero(&data, sizeof(struct __user_cap_data_struct));\n\n            header.version = _LINUX_CAPABILITY_VERSION_1;\n            data.effective = CAP_TO_MASK(CAP_NET_RAW);\n            data.permitted = data.effective;\n\n            if (syscall(SYS_capset, &header, &data) == -1) {\n                ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                              \"capset() failed\");\n                /* fatal */\n                exit(2);\n            }\n        }\n#endif\n    }\n\n    if (worker >= 0) {\n        cpu_affinity = ngx_get_cpu_affinity(worker);\n\n        if (cpu_affinity) {\n            ngx_setaffinity(cpu_affinity, cycle->log);\n        }\n    }\n\n#if (NGX_HAVE_PR_SET_DUMPABLE)\n\n    /* allow coredump after setuid() in Linux 2.4.x */\n\n    if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                      \"prctl(PR_SET_DUMPABLE) failed\");\n    }\n\n#endif\n\n    if (ccf->working_directory.len) {\n        if (chdir((char *) ccf->working_directory.data) == -1) {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                          \"chdir(\\\"%s\\\") failed\", ccf->working_directory.data);\n            /* fatal */\n            exit(2);\n        }\n    }\n\n    sigemptyset(&set);\n\n    if (sigprocmask(SIG_SETMASK, &set, NULL) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                      \"sigprocmask() failed\");\n    }\n\n    tp = ngx_timeofday();\n    srandom(((unsigned) ngx_pid << 16) ^ tp->sec ^ tp->msec);\n\n    for (i = 0; cycle->modules[i]; i++) {\n        if (cycle->modules[i]->init_process) {\n            if (cycle->modules[i]->init_process(cycle) == NGX_ERROR) {\n                /* fatal */\n                exit(2);\n            }\n        }\n    }\n\n    for (n = 0; n < ngx_last_process; n++) {\n\n        if (ngx_processes[n].pid == -1) {\n            continue;\n        }\n\n        if (n == ngx_process_slot) {\n            continue;\n        }\n\n        if (ngx_processes[n].channel[1] == -1) {\n            continue;\n        }\n\n        if (close(ngx_processes[n].channel[1]) == -1) {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                          \"close() channel failed\");\n        }\n    }\n\n    if (close(ngx_processes[ngx_process_slot].channel[0]) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                      \"close() channel failed\");\n    }\n\n#if 0\n    ngx_last_process = 0;\n#endif\n\n    if (ngx_add_channel_event(cycle, ngx_channel, NGX_READ_EVENT,\n                              ngx_channel_handler)\n        == NGX_ERROR)\n    {\n        /* fatal */\n        exit(2);\n    }\n}\n\n\nstatic void\nngx_worker_process_exit(ngx_cycle_t *cycle)\n{\n    ngx_uint_t         i;\n    ngx_connection_t  *c;\n\n    for (i = 0; cycle->modules[i]; i++) {\n        if (cycle->modules[i]->exit_process) {\n            cycle->modules[i]->exit_process(cycle);\n        }\n    }\n\n    if (ngx_exiting) {\n        c = cycle->connections;\n        for (i = 0; i < cycle->connection_n; i++) {\n            if (c[i].fd != -1\n                && c[i].read\n                && !c[i].read->accept\n                && !c[i].read->channel\n                && !c[i].read->resolver)\n            {\n                ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,\n                              \"*%uA open socket #%d left in connection %ui\",\n                              c[i].number, c[i].fd, i);\n                ngx_debug_quit = 1;\n            }\n        }\n\n        if (ngx_debug_quit) {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, \"aborting\");\n            ngx_debug_point();\n        }\n    }\n\n    /*\n     * Copy ngx_cycle->log related data to the special static exit cycle,\n     * log, and log file structures enough to allow a signal handler to log.\n     * The handler may be called when standard ngx_cycle->log allocated from\n     * ngx_cycle->pool is already destroyed.\n     */\n\n    ngx_exit_log = *ngx_log_get_file_log(ngx_cycle->log);\n\n    ngx_exit_log_file.fd = ngx_exit_log.file->fd;\n    ngx_exit_log.file = &ngx_exit_log_file;\n    ngx_exit_log.next = NULL;\n    ngx_exit_log.writer = NULL;\n\n    ngx_exit_cycle.log = &ngx_exit_log;\n    ngx_exit_cycle.files = ngx_cycle->files;\n    ngx_exit_cycle.files_n = ngx_cycle->files_n;\n    ngx_cycle = &ngx_exit_cycle;\n\n    ngx_destroy_pool(cycle->pool);\n\n    ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0, \"exit\");\n\n    exit(0);\n}\n\n\nstatic void\nngx_channel_handler(ngx_event_t *ev)\n{\n    ngx_int_t          n;\n    ngx_channel_t      ch;\n    ngx_connection_t  *c;\n\n    if (ev->timedout) {\n        ev->timedout = 0;\n        return;\n    }\n\n    c = ev->data;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_CORE, ev->log, 0, \"channel handler\");\n\n    for ( ;; ) {\n\n        n = ngx_read_channel(c->fd, &ch, sizeof(ngx_channel_t), ev->log);\n\n        ngx_log_debug1(NGX_LOG_DEBUG_CORE, ev->log, 0, \"channel: %i\", n);\n\n        if (n == NGX_ERROR) {\n\n            if (ngx_event_flags & NGX_USE_EPOLL_EVENT) {\n#if (NGX_HTTP_SSL && NGX_SSL_ASYNC)\n            if (c->async_enable && ngx_del_async_conn) {\n                if (c->num_async_fds) {\n                    ngx_del_async_conn(c, NGX_DISABLE_EVENT);\n                    c->num_async_fds--;\n                }\n            }\n#endif\n                ngx_del_conn(c, 0);\n            }\n\n            ngx_close_connection(c);\n            return;\n        }\n\n        if (ngx_event_flags & NGX_USE_EVENTPORT_EVENT) {\n            if (ngx_add_event(ev, NGX_READ_EVENT, 0) == NGX_ERROR) {\n                return;\n            }\n        }\n\n        if (n == NGX_AGAIN) {\n            return;\n        }\n\n        ngx_log_debug1(NGX_LOG_DEBUG_CORE, ev->log, 0,\n                       \"channel command: %ui\", ch.command);\n\n        switch (ch.command) {\n\n        case NGX_CMD_QUIT:\n            ngx_quit = 1;\n            break;\n\n        case NGX_CMD_TERMINATE:\n            ngx_terminate = 1;\n            break;\n\n        case NGX_CMD_REOPEN:\n            ngx_reopen = 1;\n            break;\n\n        case NGX_CMD_OPEN_CHANNEL:\n\n            ngx_log_debug3(NGX_LOG_DEBUG_CORE, ev->log, 0,\n                           \"get channel s:%i pid:%P fd:%d\",\n                           ch.slot, ch.pid, ch.fd);\n\n            ngx_processes[ch.slot].pid = ch.pid;\n            ngx_processes[ch.slot].channel[0] = ch.fd;\n            break;\n\n        case NGX_CMD_CLOSE_CHANNEL:\n\n            ngx_log_debug4(NGX_LOG_DEBUG_CORE, ev->log, 0,\n                           \"close channel s:%i pid:%P our:%P fd:%d\",\n                           ch.slot, ch.pid, ngx_processes[ch.slot].pid,\n                           ngx_processes[ch.slot].channel[0]);\n\n            if (close(ngx_processes[ch.slot].channel[0]) == -1) {\n                ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,\n                              \"close() channel failed\");\n            }\n\n            ngx_processes[ch.slot].channel[0] = -1;\n            break;\n\n#if (T_PIPES)\n        case NGX_CMD_PIPE_BROKEN:\n            ngx_pipe_broken_action(ev->log, ch.pid, 0);\n            break;\n#endif\n#if (T_NGX_HAVE_XUDP)\n        case NGX_CMD_UNBIND_XDP:\n            ngx_xudp_terminate_xudp_binding((ngx_cycle_t *) ngx_cycle);\n            break;\n#endif\n        }\n    }\n}\n\n\nstatic void\nngx_cache_manager_process_cycle(ngx_cycle_t *cycle, void *data)\n{\n    ngx_cache_manager_ctx_t *ctx = data;\n\n    void         *ident[4];\n    ngx_event_t   ev;\n\n    /*\n     * Set correct process type since closing listening Unix domain socket\n     * in a master process also removes the Unix domain socket file.\n     */\n    ngx_process = NGX_PROCESS_HELPER;\n\n    ngx_close_listening_sockets(cycle);\n\n    /* Set a moderate number of connections for a helper process. */\n    cycle->connection_n = 512;\n\n    ngx_worker_process_init(cycle, -1);\n\n    ngx_memzero(&ev, sizeof(ngx_event_t));\n    ev.handler = ctx->handler;\n    ev.data = ident;\n    ev.log = cycle->log;\n    ident[3] = (void *) -1;\n\n    ngx_use_accept_mutex = 0;\n\n    ngx_setproctitle(ctx->name);\n\n    ngx_add_timer(&ev, ctx->delay);\n\n    for ( ;; ) {\n\n        if (ngx_terminate || ngx_quit) {\n            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, \"exiting\");\n            exit(0);\n        }\n\n        if (ngx_reopen) {\n            ngx_reopen = 0;\n            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, \"reopening logs\");\n            ngx_reopen_files(cycle, -1);\n        }\n\n        ngx_process_events_and_timers(cycle);\n    }\n}\n\n\nstatic void\nngx_cache_manager_process_handler(ngx_event_t *ev)\n{\n    ngx_uint_t    i;\n    ngx_msec_t    next, n;\n    ngx_path_t  **path;\n\n    next = 60 * 60 * 1000;\n\n    path = ngx_cycle->paths.elts;\n    for (i = 0; i < ngx_cycle->paths.nelts; i++) {\n\n        if (path[i]->manager) {\n            n = path[i]->manager(path[i]->data);\n\n            next = (n <= next) ? n : next;\n\n            ngx_time_update();\n        }\n    }\n\n    if (next == 0) {\n        next = 1;\n    }\n\n    ngx_add_timer(ev, next);\n}\n\n\nstatic void\nngx_cache_loader_process_handler(ngx_event_t *ev)\n{\n    ngx_uint_t     i;\n    ngx_path_t   **path;\n    ngx_cycle_t   *cycle;\n\n    cycle = (ngx_cycle_t *) ngx_cycle;\n\n    path = cycle->paths.elts;\n    for (i = 0; i < cycle->paths.nelts; i++) {\n\n        if (ngx_terminate || ngx_quit) {\n            break;\n        }\n\n        if (path[i]->loader) {\n            path[i]->loader(path[i]->data);\n            ngx_time_update();\n        }\n    }\n\n    exit(0);\n}\n\n#if (T_NGX_HAVE_XUDP)\nvoid ngx_xudp_signal_worker_process(ngx_cycle_t *cycle) {\n    ngx_signal_worker_processes(cycle, ngx_signal_value(NGX_XUDP_TERMINATE_SIGNAL));\n}\n#endif"
  },
  {
    "path": "src/os/unix/ngx_process_cycle.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_PROCESS_CYCLE_H_INCLUDED_\n#define _NGX_PROCESS_CYCLE_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\n#define NGX_CMD_OPEN_CHANNEL   1\n#define NGX_CMD_CLOSE_CHANNEL  2\n#define NGX_CMD_QUIT           3\n#define NGX_CMD_TERMINATE      4\n#define NGX_CMD_REOPEN         5\n#if (T_PIPES)\n#define NGX_CMD_PIPE_BROKEN    6\n#endif\n#if (T_NGX_HAVE_XUDP)\n#define NGX_CMD_UNBIND_XDP     7\n#endif\n\n\n#define NGX_PROCESS_SINGLE     0\n#define NGX_PROCESS_MASTER     1\n#define NGX_PROCESS_SIGNALLER  2\n#define NGX_PROCESS_WORKER     3\n#define NGX_PROCESS_HELPER     4\n\n#if (NGX_PROCS)\n#define NGX_PROCESS_PROC       5\n#endif\n\n#if (T_PIPES)\n#define NGX_PROCESS_PIPE       6\n#endif\n\n\ntypedef struct {\n    ngx_event_handler_pt       handler;\n    char                      *name;\n    ngx_msec_t                 delay;\n} ngx_cache_manager_ctx_t;\n\n\nvoid ngx_master_process_cycle(ngx_cycle_t *cycle);\nvoid ngx_single_process_cycle(ngx_cycle_t *cycle);\n\n#if (T_NGX_HAVE_XUDP)\nvoid ngx_xudp_signal_worker_process(ngx_cycle_t *cycle);\n#endif\n\nextern ngx_uint_t      ngx_process;\nextern ngx_uint_t      ngx_worker;\nextern ngx_pid_t       ngx_pid;\nextern ngx_pid_t       ngx_new_binary;\nextern ngx_uint_t      ngx_inherited;\nextern ngx_uint_t      ngx_daemonized;\nextern ngx_uint_t      ngx_exiting;\n\nextern sig_atomic_t    ngx_reap;\nextern sig_atomic_t    ngx_sigio;\nextern sig_atomic_t    ngx_sigalrm;\nextern sig_atomic_t    ngx_quit;\nextern sig_atomic_t    ngx_debug_quit;\nextern sig_atomic_t    ngx_terminate;\nextern sig_atomic_t    ngx_noaccept;\nextern sig_atomic_t    ngx_reconfigure;\nextern sig_atomic_t    ngx_reopen;\nextern sig_atomic_t    ngx_change_binary;\n\n\n#endif /* _NGX_PROCESS_CYCLE_H_INCLUDED_ */\n"
  },
  {
    "path": "src/os/unix/ngx_readv_chain.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n\n\nssize_t\nngx_readv_chain(ngx_connection_t *c, ngx_chain_t *chain, off_t limit)\n{\n    u_char        *prev;\n    ssize_t        n, size;\n    ngx_err_t      err;\n    ngx_array_t    vec;\n    ngx_event_t   *rev;\n    struct iovec  *iov, iovs[NGX_IOVS_PREALLOCATE];\n\n    rev = c->read;\n\n#if (NGX_HAVE_KQUEUE)\n\n    if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {\n        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"readv: eof:%d, avail:%d, err:%d\",\n                       rev->pending_eof, rev->available, rev->kq_errno);\n\n        if (rev->available == 0) {\n            if (rev->pending_eof) {\n                rev->ready = 0;\n                rev->eof = 1;\n\n                ngx_log_error(NGX_LOG_INFO, c->log, rev->kq_errno,\n                              \"kevent() reported about an closed connection\");\n\n                if (rev->kq_errno) {\n                    rev->error = 1;\n                    ngx_set_socket_errno(rev->kq_errno);\n                    return NGX_ERROR;\n                }\n\n                return 0;\n\n            } else {\n                rev->ready = 0;\n                return NGX_AGAIN;\n            }\n        }\n    }\n\n#endif\n\n#if (NGX_HAVE_EPOLLRDHUP)\n\n    if ((ngx_event_flags & NGX_USE_EPOLL_EVENT)\n        && ngx_use_epoll_rdhup)\n    {\n        ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"readv: eof:%d, avail:%d\",\n                       rev->pending_eof, rev->available);\n\n        if (rev->available == 0 && !rev->pending_eof) {\n            rev->ready = 0;\n            return NGX_AGAIN;\n        }\n    }\n\n#endif\n\n    prev = NULL;\n    iov = NULL;\n    size = 0;\n\n    vec.elts = iovs;\n    vec.nelts = 0;\n    vec.size = sizeof(struct iovec);\n    vec.nalloc = NGX_IOVS_PREALLOCATE;\n    vec.pool = c->pool;\n\n    /* coalesce the neighbouring bufs */\n\n    while (chain) {\n        n = chain->buf->end - chain->buf->last;\n\n        if (limit) {\n            if (size >= limit) {\n                break;\n            }\n\n            if (size + n > limit) {\n                n = (ssize_t) (limit - size);\n            }\n        }\n\n        if (prev == chain->buf->last) {\n            iov->iov_len += n;\n\n        } else {\n            if (vec.nelts == vec.nalloc) {\n                break;\n            }\n\n            iov = ngx_array_push(&vec);\n            if (iov == NULL) {\n                return NGX_ERROR;\n            }\n\n            iov->iov_base = (void *) chain->buf->last;\n            iov->iov_len = n;\n        }\n\n        size += n;\n        prev = chain->buf->end;\n        chain = chain->next;\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"readv: %ui, last:%uz\", vec.nelts, iov->iov_len);\n\n    do {\n        n = readv(c->fd, (struct iovec *) vec.elts, vec.nelts);\n\n        if (n == 0) {\n            rev->ready = 0;\n            rev->eof = 1;\n\n#if (NGX_HAVE_KQUEUE)\n\n            /*\n             * on FreeBSD readv() may return 0 on closed socket\n             * even if kqueue reported about available data\n             */\n\n            if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {\n                rev->available = 0;\n            }\n\n#endif\n\n            return 0;\n        }\n\n        if (n > 0) {\n\n#if (NGX_HAVE_KQUEUE)\n\n            if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {\n                rev->available -= n;\n\n                /*\n                 * rev->available may be negative here because some additional\n                 * bytes may be received between kevent() and readv()\n                 */\n\n                if (rev->available <= 0) {\n                    if (!rev->pending_eof) {\n                        rev->ready = 0;\n                    }\n\n                    rev->available = 0;\n                }\n\n                return n;\n            }\n\n#endif\n\n#if (NGX_HAVE_FIONREAD)\n\n            if (rev->available >= 0) {\n                rev->available -= n;\n\n                /*\n                 * negative rev->available means some additional bytes\n                 * were received between kernel notification and readv(),\n                 * and therefore ev->ready can be safely reset even for\n                 * edge-triggered event methods\n                 */\n\n                if (rev->available < 0) {\n                    rev->available = 0;\n                    rev->ready = 0;\n                }\n\n                ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                               \"readv: avail:%d\", rev->available);\n\n            } else if (n == size) {\n\n                if (ngx_socket_nread(c->fd, &rev->available) == -1) {\n                    n = ngx_connection_error(c, ngx_socket_errno,\n                                             ngx_socket_nread_n \" failed\");\n                    break;\n                }\n\n                ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                               \"readv: avail:%d\", rev->available);\n            }\n\n#endif\n\n#if (NGX_HAVE_EPOLLRDHUP)\n\n            if ((ngx_event_flags & NGX_USE_EPOLL_EVENT)\n                && ngx_use_epoll_rdhup)\n            {\n                if (n < size) {\n                    if (!rev->pending_eof) {\n                        rev->ready = 0;\n                    }\n\n                    rev->available = 0;\n                }\n\n                return n;\n            }\n\n#endif\n\n            if (n < size && !(ngx_event_flags & NGX_USE_GREEDY_EVENT)) {\n                rev->ready = 0;\n            }\n\n            return n;\n        }\n\n        err = ngx_socket_errno;\n\n        if (err == NGX_EAGAIN || err == NGX_EINTR) {\n            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,\n                           \"readv() not ready\");\n            n = NGX_AGAIN;\n\n        } else {\n            n = ngx_connection_error(c, err, \"readv() failed\");\n            break;\n        }\n\n    } while (err == NGX_EINTR);\n\n    rev->ready = 0;\n\n    if (n == NGX_ERROR) {\n        c->read->error = 1;\n    }\n\n    return n;\n}\n"
  },
  {
    "path": "src/os/unix/ngx_recv.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n\n\nssize_t\nngx_unix_recv(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#if (NGX_HAVE_KQUEUE)\n\n    if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {\n        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"recv: eof:%d, avail:%d, err:%d\",\n                       rev->pending_eof, rev->available, rev->kq_errno);\n\n        if (rev->available == 0) {\n            if (rev->pending_eof) {\n                rev->ready = 0;\n                rev->eof = 1;\n\n                if (rev->kq_errno) {\n                    rev->error = 1;\n                    ngx_set_socket_errno(rev->kq_errno);\n\n                    return ngx_connection_error(c, rev->kq_errno,\n                               \"kevent() reported about an closed connection\");\n                }\n\n                return 0;\n\n            } else {\n                rev->ready = 0;\n                return NGX_AGAIN;\n            }\n        }\n    }\n\n#endif\n\n#if (NGX_HAVE_EPOLLRDHUP)\n\n    if ((ngx_event_flags & NGX_USE_EPOLL_EVENT)\n        && ngx_use_epoll_rdhup)\n    {\n        ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"recv: eof:%d, avail:%d\",\n                       rev->pending_eof, rev->available);\n\n        if (rev->available == 0 && !rev->pending_eof) {\n            rev->ready = 0;\n            return NGX_AGAIN;\n        }\n    }\n\n#endif\n\n    do {\n        n = recv(c->fd, buf, size, 0);\n\n        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"recv: fd:%d %z of %uz\", c->fd, n, size);\n\n        if (n == 0) {\n            rev->ready = 0;\n            rev->eof = 1;\n\n#if (NGX_HAVE_KQUEUE)\n\n            /*\n             * on FreeBSD recv() may return 0 on closed socket\n             * even if kqueue reported about available data\n             */\n\n            if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {\n                rev->available = 0;\n            }\n\n#endif\n\n            return 0;\n        }\n\n        if (n > 0) {\n\n#if (NGX_HAVE_KQUEUE)\n\n            if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {\n                rev->available -= n;\n\n                /*\n                 * rev->available may be negative here because some additional\n                 * bytes may be received between kevent() and recv()\n                 */\n\n                if (rev->available <= 0) {\n                    if (!rev->pending_eof) {\n                        rev->ready = 0;\n                    }\n\n                    rev->available = 0;\n                }\n\n                return n;\n            }\n\n#endif\n\n#if (NGX_HAVE_FIONREAD)\n\n            if (rev->available >= 0) {\n                rev->available -= n;\n\n                /*\n                 * negative rev->available means some additional bytes\n                 * were received between kernel notification and recv(),\n                 * and therefore ev->ready can be safely reset even for\n                 * edge-triggered event methods\n                 */\n\n                if (rev->available < 0) {\n                    rev->available = 0;\n                    rev->ready = 0;\n                }\n\n                ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                               \"recv: avail:%d\", rev->available);\n\n            } else if ((size_t) n == size) {\n\n                if (ngx_socket_nread(c->fd, &rev->available) == -1) {\n                    n = ngx_connection_error(c, ngx_socket_errno,\n                                             ngx_socket_nread_n \" failed\");\n                    break;\n                }\n\n                ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                               \"recv: avail:%d\", rev->available);\n            }\n\n#endif\n\n#if (NGX_HAVE_EPOLLRDHUP)\n\n            if ((ngx_event_flags & NGX_USE_EPOLL_EVENT)\n                && ngx_use_epoll_rdhup)\n            {\n                if ((size_t) n < size) {\n                    if (!rev->pending_eof) {\n                        rev->ready = 0;\n                    }\n\n                    rev->available = 0;\n                }\n\n                return n;\n            }\n\n#endif\n\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        err = ngx_socket_errno;\n\n        if (err == NGX_EAGAIN || err == NGX_EINTR) {\n            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,\n                           \"recv() not ready\");\n            n = NGX_AGAIN;\n\n        } else {\n            n = ngx_connection_error(c, err, \"recv() 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"
  },
  {
    "path": "src/os/unix/ngx_send.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n\n\nssize_t\nngx_unix_send(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#if (NGX_HAVE_KQUEUE)\n\n    if ((ngx_event_flags & NGX_USE_KQUEUE_EVENT) && wev->pending_eof) {\n        (void) ngx_connection_error(c, wev->kq_errno,\n                               \"kevent() reported about an closed connection\");\n        wev->error = 1;\n        return NGX_ERROR;\n    }\n\n#endif\n\n    for ( ;; ) {\n        n = send(c->fd, buf, size, 0);\n\n        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"send: fd:%d %z of %uz\", c->fd, n, size);\n\n        if (n > 0) {\n            if (n < (ssize_t) size) {\n                wev->ready = 0;\n            }\n\n            c->sent += n;\n\n            return n;\n        }\n\n        err = ngx_socket_errno;\n\n        if (n == 0) {\n            ngx_log_error(NGX_LOG_ALERT, c->log, err, \"send() returned zero\");\n            wev->ready = 0;\n            return n;\n        }\n\n        if (err == NGX_EAGAIN || err == NGX_EINTR) {\n            wev->ready = 0;\n\n            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,\n                           \"send() not ready\");\n\n            if (err == NGX_EAGAIN) {\n                return NGX_AGAIN;\n            }\n\n        } else {\n            wev->error = 1;\n            (void) ngx_connection_error(c, err, \"send() failed\");\n            return NGX_ERROR;\n        }\n    }\n}\n"
  },
  {
    "path": "src/os/unix/ngx_setaffinity.c",
    "content": "\n/*\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\n#if (NGX_HAVE_CPUSET_SETAFFINITY)\n\nvoid\nngx_setaffinity(ngx_cpuset_t *cpu_affinity, ngx_log_t *log)\n{\n    ngx_uint_t  i;\n\n    for (i = 0; i < CPU_SETSIZE; i++) {\n        if (CPU_ISSET(i, cpu_affinity)) {\n            ngx_log_error(NGX_LOG_NOTICE, log, 0,\n                          \"cpuset_setaffinity(): using cpu #%ui\", i);\n        }\n    }\n\n    if (cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_PID, -1,\n                           sizeof(cpuset_t), cpu_affinity) == -1)\n    {\n        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,\n                      \"cpuset_setaffinity() failed\");\n    }\n}\n\n#elif (NGX_HAVE_SCHED_SETAFFINITY)\n\nvoid\nngx_setaffinity(ngx_cpuset_t *cpu_affinity, ngx_log_t *log)\n{\n    ngx_uint_t  i;\n\n    for (i = 0; i < CPU_SETSIZE; i++) {\n        if (CPU_ISSET(i, cpu_affinity)) {\n            ngx_log_error(NGX_LOG_NOTICE, log, 0,\n                          \"sched_setaffinity(): using cpu #%ui\", i);\n        }\n    }\n\n    if (sched_setaffinity(0, sizeof(cpu_set_t), cpu_affinity) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,\n                      \"sched_setaffinity() failed\");\n    }\n}\n\n#endif\n"
  },
  {
    "path": "src/os/unix/ngx_setaffinity.h",
    "content": "\n/*\n * Copyright (C) Nginx, Inc.\n */\n\n#ifndef _NGX_SETAFFINITY_H_INCLUDED_\n#define _NGX_SETAFFINITY_H_INCLUDED_\n\n\n#if (NGX_HAVE_SCHED_SETAFFINITY || NGX_HAVE_CPUSET_SETAFFINITY)\n\n#define NGX_HAVE_CPU_AFFINITY 1\n\n#if (NGX_HAVE_SCHED_SETAFFINITY)\n\ntypedef cpu_set_t  ngx_cpuset_t;\n\n#elif (NGX_HAVE_CPUSET_SETAFFINITY)\n\n#include <sys/cpuset.h>\n\ntypedef cpuset_t  ngx_cpuset_t;\n\n#endif\n\nvoid ngx_setaffinity(ngx_cpuset_t *cpu_affinity, ngx_log_t *log);\n\n#else\n\n#define ngx_setaffinity(cpu_affinity, log)\n\ntypedef uint64_t  ngx_cpuset_t;\n\n#endif\n\n\n#endif /* _NGX_SETAFFINITY_H_INCLUDED_ */\n"
  },
  {
    "path": "src/os/unix/ngx_setproctitle.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\n#if (NGX_SETPROCTITLE_USES_ENV)\n\n/*\n * To change the process title in Linux and Solaris we have to set argv[1]\n * to NULL and to copy the title to the same place where the argv[0] points to.\n * However, argv[0] may be too small to hold a new title.  Fortunately, Linux\n * and Solaris store argv[] and environ[] one after another.  So we should\n * ensure that is the continuous memory and then we allocate the new memory\n * for environ[] and copy it.  After this we could use the memory starting\n * from argv[0] for our process title.\n *\n * The Solaris's standard /bin/ps does not show the changed process title.\n * You have to use \"/usr/ucb/ps -w\" instead.  Besides, the UCB ps does not\n * show a new title if its length less than the origin command line length.\n * To avoid it we append to a new title the origin command line in the\n * parenthesis.\n */\n\nextern char **environ;\n\nstatic char *ngx_os_argv_last;\n\nngx_int_t\nngx_init_setproctitle(ngx_log_t *log)\n{\n    u_char      *p;\n    size_t       size;\n    ngx_uint_t   i;\n\n    size = 0;\n\n    for (i = 0; environ[i]; i++) {\n        size += ngx_strlen(environ[i]) + 1;\n    }\n\n    p = ngx_alloc(size, log);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_os_argv_last = ngx_os_argv[0];\n\n    for (i = 0; ngx_os_argv[i]; i++) {\n        if (ngx_os_argv_last == ngx_os_argv[i]) {\n            ngx_os_argv_last = ngx_os_argv[i] + ngx_strlen(ngx_os_argv[i]) + 1;\n        }\n    }\n\n    for (i = 0; environ[i]; i++) {\n        if (ngx_os_argv_last == environ[i]) {\n\n            size = ngx_strlen(environ[i]) + 1;\n            ngx_os_argv_last = environ[i] + size;\n\n            ngx_cpystrn(p, (u_char *) environ[i], size);\n            environ[i] = (char *) p;\n            p += size;\n        }\n    }\n\n    ngx_os_argv_last--;\n\n    return NGX_OK;\n}\n\n\nvoid\nngx_setproctitle(char *title)\n{\n    u_char     *p;\n\n#if (NGX_SOLARIS)\n\n    ngx_int_t   i;\n    size_t      size;\n\n#endif\n\n    ngx_os_argv[1] = NULL;\n\n    p = ngx_cpystrn((u_char *) ngx_os_argv[0], (u_char *) \"nginx: \",\n                    ngx_os_argv_last - ngx_os_argv[0]);\n\n    p = ngx_cpystrn(p, (u_char *) title, ngx_os_argv_last - (char *) p);\n\n#if (NGX_SOLARIS)\n\n    size = 0;\n\n    for (i = 0; i < ngx_argc; i++) {\n        size += ngx_strlen(ngx_argv[i]) + 1;\n    }\n\n    if (size > (size_t) ((char *) p - ngx_os_argv[0])) {\n\n        /*\n         * ngx_setproctitle() is too rare operation so we use\n         * the non-optimized copies\n         */\n\n        p = ngx_cpystrn(p, (u_char *) \" (\", ngx_os_argv_last - (char *) p);\n\n        for (i = 0; i < ngx_argc; i++) {\n            p = ngx_cpystrn(p, (u_char *) ngx_argv[i],\n                            ngx_os_argv_last - (char *) p);\n            p = ngx_cpystrn(p, (u_char *) \" \", ngx_os_argv_last - (char *) p);\n        }\n\n        if (*(p - 1) == ' ') {\n            *(p - 1) = ')';\n        }\n    }\n\n#endif\n\n    if (ngx_os_argv_last - (char *) p) {\n        ngx_memset(p, NGX_SETPROCTITLE_PAD, ngx_os_argv_last - (char *) p);\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_CORE, ngx_cycle->log, 0,\n                   \"setproctitle: \\\"%s\\\"\", ngx_os_argv[0]);\n}\n\n#endif /* NGX_SETPROCTITLE_USES_ENV */\n"
  },
  {
    "path": "src/os/unix/ngx_setproctitle.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_SETPROCTITLE_H_INCLUDED_\n#define _NGX_SETPROCTITLE_H_INCLUDED_\n\n\n#if (NGX_HAVE_SETPROCTITLE)\n\n/* FreeBSD, NetBSD, OpenBSD */\n\n#define ngx_init_setproctitle(log) NGX_OK\n#define ngx_setproctitle(title)    setproctitle(\"%s\", title)\n\n\n#else /* !NGX_HAVE_SETPROCTITLE */\n\n#if !defined NGX_SETPROCTITLE_USES_ENV\n\n#if (NGX_SOLARIS)\n\n#define NGX_SETPROCTITLE_USES_ENV  1\n#define NGX_SETPROCTITLE_PAD       ' '\n\nngx_int_t ngx_init_setproctitle(ngx_log_t *log);\nvoid ngx_setproctitle(char *title);\n\n#elif (NGX_LINUX) || (NGX_DARWIN)\n\n#define NGX_SETPROCTITLE_USES_ENV  1\n#define NGX_SETPROCTITLE_PAD       '\\0'\n\nngx_int_t ngx_init_setproctitle(ngx_log_t *log);\nvoid ngx_setproctitle(char *title);\n\n#else\n\n#define ngx_init_setproctitle(log) NGX_OK\n#define ngx_setproctitle(title)\n\n#endif /* OSes */\n\n#endif /* NGX_SETPROCTITLE_USES_ENV */\n\n#endif /* NGX_HAVE_SETPROCTITLE */\n\n\n#endif /* _NGX_SETPROCTITLE_H_INCLUDED_ */\n"
  },
  {
    "path": "src/os/unix/ngx_shmem.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\n#if (NGX_HAVE_MAP_ANON)\n\nngx_int_t\nngx_shm_alloc(ngx_shm_t *shm)\n{\n    shm->addr = (u_char *) mmap(NULL, shm->size,\n                                PROT_READ|PROT_WRITE,\n                                MAP_ANON|MAP_SHARED, -1, 0);\n\n    if (shm->addr == MAP_FAILED) {\n        ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,\n                      \"mmap(MAP_ANON|MAP_SHARED, %uz) failed\", shm->size);\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nvoid\nngx_shm_free(ngx_shm_t *shm)\n{\n    if (munmap((void *) shm->addr, shm->size) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,\n                      \"munmap(%p, %uz) failed\", shm->addr, shm->size);\n    }\n}\n\n#elif (NGX_HAVE_MAP_DEVZERO)\n\nngx_int_t\nngx_shm_alloc(ngx_shm_t *shm)\n{\n    ngx_fd_t  fd;\n\n    fd = open(\"/dev/zero\", O_RDWR);\n\n    if (fd == -1) {\n        ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,\n                      \"open(\\\"/dev/zero\\\") failed\");\n        return NGX_ERROR;\n    }\n\n    shm->addr = (u_char *) mmap(NULL, shm->size, PROT_READ|PROT_WRITE,\n                                MAP_SHARED, fd, 0);\n\n    if (shm->addr == MAP_FAILED) {\n        ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,\n                      \"mmap(/dev/zero, MAP_SHARED, %uz) failed\", shm->size);\n    }\n\n    if (close(fd) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,\n                      \"close(\\\"/dev/zero\\\") failed\");\n    }\n\n    return (shm->addr == MAP_FAILED) ? NGX_ERROR : NGX_OK;\n}\n\n\nvoid\nngx_shm_free(ngx_shm_t *shm)\n{\n    if (munmap((void *) shm->addr, shm->size) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,\n                      \"munmap(%p, %uz) failed\", shm->addr, shm->size);\n    }\n}\n\n#elif (NGX_HAVE_SYSVSHM)\n\n#include <sys/ipc.h>\n#include <sys/shm.h>\n\n\nngx_int_t\nngx_shm_alloc(ngx_shm_t *shm)\n{\n    int  id;\n\n    id = shmget(IPC_PRIVATE, shm->size, (SHM_R|SHM_W|IPC_CREAT));\n\n    if (id == -1) {\n        ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,\n                      \"shmget(%uz) failed\", shm->size);\n        return NGX_ERROR;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_CORE, shm->log, 0, \"shmget id: %d\", id);\n\n    shm->addr = shmat(id, NULL, 0);\n\n    if (shm->addr == (void *) -1) {\n        ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno, \"shmat() failed\");\n    }\n\n    if (shmctl(id, IPC_RMID, NULL) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,\n                      \"shmctl(IPC_RMID) failed\");\n    }\n\n    return (shm->addr == (void *) -1) ? NGX_ERROR : NGX_OK;\n}\n\n\nvoid\nngx_shm_free(ngx_shm_t *shm)\n{\n    if (shmdt(shm->addr) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,\n                      \"shmdt(%p) failed\", shm->addr);\n    }\n}\n\n#endif\n"
  },
  {
    "path": "src/os/unix/ngx_shmem.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_SHMEM_H_INCLUDED_\n#define _NGX_SHMEM_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\ntypedef struct {\n    u_char      *addr;\n    size_t       size;\n    ngx_str_t    name;\n    ngx_log_t   *log;\n    ngx_uint_t   exists;   /* unsigned  exists:1;  */\n} ngx_shm_t;\n\n\nngx_int_t ngx_shm_alloc(ngx_shm_t *shm);\nvoid ngx_shm_free(ngx_shm_t *shm);\n\n\n#endif /* _NGX_SHMEM_H_INCLUDED_ */\n"
  },
  {
    "path": "src/os/unix/ngx_socket.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\n/*\n * ioctl(FIONBIO) sets a non-blocking mode with the single syscall\n * while fcntl(F_SETFL, O_NONBLOCK) needs to learn the current state\n * using fcntl(F_GETFL).\n *\n * ioctl() and fcntl() are syscalls at least in FreeBSD 2.x, Linux 2.2\n * and Solaris 7.\n *\n * ioctl() in Linux 2.4 and 2.6 uses BKL, however, fcntl(F_SETFL) uses it too.\n */\n\n\n#if (NGX_HAVE_FIONBIO)\n\nint\nngx_nonblocking(ngx_socket_t s)\n{\n    int  nb;\n\n    nb = 1;\n\n    return ioctl(s, FIONBIO, &nb);\n}\n\n\nint\nngx_blocking(ngx_socket_t s)\n{\n    int  nb;\n\n    nb = 0;\n\n    return ioctl(s, FIONBIO, &nb);\n}\n\n#endif\n\n\n#if (NGX_FREEBSD)\n\nint\nngx_tcp_nopush(ngx_socket_t s)\n{\n    int  tcp_nopush;\n\n    tcp_nopush = 1;\n\n    return setsockopt(s, IPPROTO_TCP, TCP_NOPUSH,\n                      (const void *) &tcp_nopush, sizeof(int));\n}\n\n\nint\nngx_tcp_push(ngx_socket_t s)\n{\n    int  tcp_nopush;\n\n    tcp_nopush = 0;\n\n    return setsockopt(s, IPPROTO_TCP, TCP_NOPUSH,\n                      (const void *) &tcp_nopush, sizeof(int));\n}\n\n#elif (NGX_LINUX)\n\n\nint\nngx_tcp_nopush(ngx_socket_t s)\n{\n    int  cork;\n\n    cork = 1;\n\n    return setsockopt(s, IPPROTO_TCP, TCP_CORK,\n                      (const void *) &cork, sizeof(int));\n}\n\n\nint\nngx_tcp_push(ngx_socket_t s)\n{\n    int  cork;\n\n    cork = 0;\n\n    return setsockopt(s, IPPROTO_TCP, TCP_CORK,\n                      (const void *) &cork, sizeof(int));\n}\n\n#else\n\nint\nngx_tcp_nopush(ngx_socket_t s)\n{\n    return 0;\n}\n\n\nint\nngx_tcp_push(ngx_socket_t s)\n{\n    return 0;\n}\n\n#endif\n"
  },
  {
    "path": "src/os/unix/ngx_socket.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_SOCKET_H_INCLUDED_\n#define _NGX_SOCKET_H_INCLUDED_\n\n\n#include <ngx_config.h>\n\n\n#define NGX_WRITE_SHUTDOWN SHUT_WR\n\ntypedef int  ngx_socket_t;\n\n#define ngx_socket          socket\n#define ngx_socket_n        \"socket()\"\n\n\n#if (NGX_HAVE_FIONBIO)\n\nint ngx_nonblocking(ngx_socket_t s);\nint ngx_blocking(ngx_socket_t s);\n\n#define ngx_nonblocking_n   \"ioctl(FIONBIO)\"\n#define ngx_blocking_n      \"ioctl(!FIONBIO)\"\n\n#else\n\n#define ngx_nonblocking(s)  fcntl(s, F_SETFL, fcntl(s, F_GETFL) | O_NONBLOCK)\n#define ngx_nonblocking_n   \"fcntl(O_NONBLOCK)\"\n\n#define ngx_blocking(s)     fcntl(s, F_SETFL, fcntl(s, F_GETFL) & ~O_NONBLOCK)\n#define ngx_blocking_n      \"fcntl(!O_NONBLOCK)\"\n\n#endif\n\n#if (NGX_HAVE_FIONREAD)\n\n#define ngx_socket_nread(s, n)  ioctl(s, FIONREAD, n)\n#define ngx_socket_nread_n      \"ioctl(FIONREAD)\"\n\n#endif\n\nint ngx_tcp_nopush(ngx_socket_t s);\nint ngx_tcp_push(ngx_socket_t s);\n\n#if (NGX_LINUX)\n\n#define ngx_tcp_nopush_n   \"setsockopt(TCP_CORK)\"\n#define ngx_tcp_push_n     \"setsockopt(!TCP_CORK)\"\n\n#else\n\n#define ngx_tcp_nopush_n   \"setsockopt(TCP_NOPUSH)\"\n#define ngx_tcp_push_n     \"setsockopt(!TCP_NOPUSH)\"\n\n#endif\n\n\n#define ngx_shutdown_socket    shutdown\n#define ngx_shutdown_socket_n  \"shutdown()\"\n\n#define ngx_close_socket    close\n#define ngx_close_socket_n  \"close() socket\"\n\n\n#endif /* _NGX_SOCKET_H_INCLUDED_ */\n"
  },
  {
    "path": "src/os/unix/ngx_solaris.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_SOLARIS_H_INCLUDED_\n#define _NGX_SOLARIS_H_INCLUDED_\n\n\n#if (NGX_HAVE_GETLOADAVG)\n#include <sys/loadavg.h>\n#endif\n\nngx_chain_t *ngx_solaris_sendfilev_chain(ngx_connection_t *c, ngx_chain_t *in,\n    off_t limit);\n\n\n#endif /* _NGX_SOLARIS_H_INCLUDED_ */\n"
  },
  {
    "path": "src/os/unix/ngx_solaris_config.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_SOLARIS_CONFIG_H_INCLUDED_\n#define _NGX_SOLARIS_CONFIG_H_INCLUDED_\n\n\n#ifndef _REENTRANT\n#define _REENTRANT\n#endif\n\n#define _FILE_OFFSET_BITS  64   /* must be before <sys/types.h> */\n\n#include <sys/types.h>\n#include <sys/time.h>\n#include <unistd.h>\n#include <stdarg.h>\n#include <stddef.h>             /* offsetof() */\n#include <stdio.h>\n#include <stdlib.h>\n#include <ctype.h>\n#include <errno.h>\n#include <string.h>\n#include <signal.h>\n#include <pwd.h>\n#include <grp.h>\n#include <dirent.h>\n#include <glob.h>\n#include <time.h>\n#include <sys/statvfs.h>        /* statvfs() */\n\n#include <sys/filio.h>          /* FIONBIO */\n#include <sys/uio.h>\n#include <sys/stat.h>\n#include <fcntl.h>\n\n#include <sys/wait.h>\n#include <sys/mman.h>\n#include <sys/resource.h>\n#include <sched.h>\n\n#include <sys/socket.h>\n#include <netinet/in.h>\n#include <netinet/tcp.h>        /* TCP_NODELAY */\n#include <arpa/inet.h>\n#include <netdb.h>\n#include <sys/un.h>\n\n#include <sys/systeminfo.h>\n#include <limits.h>             /* IOV_MAX */\n#include <inttypes.h>\n#include <crypt.h>\n\n#include <dlfcn.h>\n\n#define NGX_ALIGNMENT  _MAX_ALIGNMENT\n\n#include <ngx_auto_config.h>\n\n\n#if (NGX_HAVE_POSIX_SEM)\n#include <semaphore.h>\n#endif\n\n\n#if (NGX_HAVE_POLL)\n#include <poll.h>\n#endif\n\n\n#if (NGX_HAVE_DEVPOLL)\n#include <sys/ioctl.h>\n#include <sys/devpoll.h>\n#endif\n\n\n#if (NGX_HAVE_EVENTPORT)\n#include <port.h>\n#endif\n\n\n#if (NGX_HAVE_SENDFILE)\n#include <sys/sendfile.h>\n#endif\n\n\n#define NGX_LISTEN_BACKLOG           511\n\n\n#ifndef NGX_HAVE_INHERITED_NONBLOCK\n#define NGX_HAVE_INHERITED_NONBLOCK  1\n#endif\n\n\n#ifndef NGX_HAVE_SO_SNDLOWAT\n/* setsockopt(SO_SNDLOWAT) returns ENOPROTOOPT */\n#define NGX_HAVE_SO_SNDLOWAT         0\n#endif\n\n\n#define NGX_HAVE_OS_SPECIFIC_INIT    1\n#define ngx_debug_init()\n\n\nextern char **environ;\n\n\n#endif /* _NGX_SOLARIS_CONFIG_H_INCLUDED_ */\n"
  },
  {
    "path": "src/os/unix/ngx_solaris_init.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\nchar ngx_solaris_sysname[20];\nchar ngx_solaris_release[10];\nchar ngx_solaris_version[50];\n\n\nstatic ngx_os_io_t ngx_solaris_io = {\n    ngx_unix_recv,\n    ngx_readv_chain,\n    ngx_udp_unix_recv,\n    ngx_unix_send,\n    ngx_udp_unix_send,\n    ngx_udp_unix_sendmsg_chain,\n#if (NGX_HAVE_SENDFILE)\n    ngx_solaris_sendfilev_chain,\n    NGX_IO_SENDFILE\n#else\n    ngx_writev_chain,\n    0\n#endif\n};\n\n\nngx_int_t\nngx_os_specific_init(ngx_log_t *log)\n{\n    if (sysinfo(SI_SYSNAME, ngx_solaris_sysname, sizeof(ngx_solaris_sysname))\n        == -1)\n    {\n        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,\n                      \"sysinfo(SI_SYSNAME) failed\");\n        return NGX_ERROR;\n    }\n\n    if (sysinfo(SI_RELEASE, ngx_solaris_release, sizeof(ngx_solaris_release))\n        == -1)\n    {\n        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,\n                      \"sysinfo(SI_RELEASE) failed\");\n        return NGX_ERROR;\n    }\n\n    if (sysinfo(SI_VERSION, ngx_solaris_version, sizeof(ngx_solaris_version))\n        == -1)\n    {\n        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,\n                      \"sysinfo(SI_SYSNAME) failed\");\n        return NGX_ERROR;\n    }\n\n\n    ngx_os_io = ngx_solaris_io;\n\n    return NGX_OK;\n}\n\n\nvoid\nngx_os_specific_status(ngx_log_t *log)\n{\n\n    ngx_log_error(NGX_LOG_NOTICE, log, 0, \"OS: %s %s\",\n                  ngx_solaris_sysname, ngx_solaris_release);\n\n    ngx_log_error(NGX_LOG_NOTICE, log, 0, \"version: %s\",\n                  ngx_solaris_version);\n}\n"
  },
  {
    "path": "src/os/unix/ngx_solaris_sendfilev_chain.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n\n\n#if (NGX_TEST_BUILD_SOLARIS_SENDFILEV)\n\n/* Solaris declarations */\n\ntypedef struct sendfilevec {\n    int     sfv_fd;\n    u_int   sfv_flag;\n    off_t   sfv_off;\n    size_t  sfv_len;\n} sendfilevec_t;\n\n#define SFV_FD_SELF  -2\n\nstatic ssize_t sendfilev(int fd, const struct sendfilevec *vec,\n    int sfvcnt, size_t *xferred)\n{\n    return -1;\n}\n\nngx_chain_t *ngx_solaris_sendfilev_chain(ngx_connection_t *c, ngx_chain_t *in,\n    off_t limit);\n\n#endif\n\n\n#define NGX_SENDFILEVECS  NGX_IOVS_PREALLOCATE\n\n\nngx_chain_t *\nngx_solaris_sendfilev_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)\n{\n    int             fd;\n    u_char         *prev;\n    off_t           size, send, prev_send, aligned, fprev;\n    size_t          sent;\n    ssize_t         n;\n    ngx_int_t       eintr;\n    ngx_err_t       err;\n    ngx_buf_t      *file;\n    ngx_uint_t      nsfv;\n    sendfilevec_t  *sfv, sfvs[NGX_SENDFILEVECS];\n    ngx_event_t    *wev;\n    ngx_chain_t    *cl;\n\n    wev = c->write;\n\n    if (!wev->ready) {\n        return in;\n    }\n\n    if (!c->sendfile) {\n        return ngx_writev_chain(c, in, limit);\n    }\n\n\n    /* the maximum limit size is the maximum size_t value - the page size */\n\n    if (limit == 0 || limit > (off_t) (NGX_MAX_SIZE_T_VALUE - ngx_pagesize)) {\n        limit = NGX_MAX_SIZE_T_VALUE - ngx_pagesize;\n    }\n\n\n    send = 0;\n\n    for ( ;; ) {\n        fd = SFV_FD_SELF;\n        prev = NULL;\n        fprev = 0;\n        file = NULL;\n        sfv = NULL;\n        eintr = 0;\n        sent = 0;\n        prev_send = send;\n\n        nsfv = 0;\n\n        /* create the sendfilevec and coalesce the neighbouring bufs */\n\n        for (cl = in; cl && send < limit; cl = cl->next) {\n\n            if (ngx_buf_special(cl->buf)) {\n                continue;\n            }\n\n            if (ngx_buf_in_memory_only(cl->buf)) {\n                fd = SFV_FD_SELF;\n\n                size = cl->buf->last - cl->buf->pos;\n\n                if (send + size > limit) {\n                    size = limit - send;\n                }\n\n                if (prev == cl->buf->pos) {\n                    sfv->sfv_len += (size_t) size;\n\n                } else {\n                    if (nsfv == NGX_SENDFILEVECS) {\n                        break;\n                    }\n\n                    sfv = &sfvs[nsfv++];\n\n                    sfv->sfv_fd = SFV_FD_SELF;\n                    sfv->sfv_flag = 0;\n                    sfv->sfv_off = (off_t) (uintptr_t) cl->buf->pos;\n                    sfv->sfv_len = (size_t) size;\n                }\n\n                prev = cl->buf->pos + (size_t) size;\n                send += size;\n\n            } else {\n                prev = NULL;\n\n                size = cl->buf->file_last - cl->buf->file_pos;\n\n                if (send + size > limit) {\n                    size = limit - send;\n\n                    aligned = (cl->buf->file_pos + size + ngx_pagesize - 1)\n                               & ~((off_t) ngx_pagesize - 1);\n\n                    if (aligned <= cl->buf->file_last) {\n                        size = aligned - cl->buf->file_pos;\n                    }\n                }\n\n                if (fd == cl->buf->file->fd && fprev == cl->buf->file_pos) {\n                    sfv->sfv_len += (size_t) size;\n\n                } else {\n                    if (nsfv == NGX_SENDFILEVECS) {\n                        break;\n                    }\n\n                    sfv = &sfvs[nsfv++];\n\n                    fd = cl->buf->file->fd;\n                    sfv->sfv_fd = fd;\n                    sfv->sfv_flag = 0;\n                    sfv->sfv_off = cl->buf->file_pos;\n                    sfv->sfv_len = (size_t) size;\n                }\n\n                file = cl->buf;\n                fprev = cl->buf->file_pos + size;\n                send += size;\n            }\n        }\n\n        n = sendfilev(c->fd, sfvs, nsfv, &sent);\n\n        if (n == -1) {\n            err = ngx_errno;\n\n            switch (err) {\n            case NGX_EAGAIN:\n                break;\n\n            case NGX_EINTR:\n                eintr = 1;\n                break;\n\n            default:\n                wev->error = 1;\n                ngx_connection_error(c, err, \"sendfilev() failed\");\n                return NGX_CHAIN_ERROR;\n            }\n\n            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, err,\n                          \"sendfilev() sent only %uz bytes\", sent);\n\n        } else if (n == 0 && sent == 0) {\n\n            /*\n             * sendfilev() is documented to return -1 with errno\n             * set to EINVAL if svf_len is greater than the file size,\n             * but at least Solaris 11 returns 0 instead\n             */\n\n            if (file) {\n                ngx_log_error(NGX_LOG_ALERT, c->log, 0,\n                        \"sendfilev() reported that \\\"%s\\\" was truncated at %O\",\n                        file->file->name.data, file->file_pos);\n\n            } else {\n                ngx_log_error(NGX_LOG_ALERT, c->log, 0,\n                              \"sendfilev() returned 0 with memory buffers\");\n            }\n\n            return NGX_CHAIN_ERROR;\n        }\n\n        ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"sendfilev: %z %z\", n, sent);\n\n        c->sent += sent;\n\n        in = ngx_chain_update_sent(in, sent);\n\n        if (eintr) {\n            send = prev_send + sent;\n            continue;\n        }\n\n        if (send - prev_send != (off_t) sent) {\n            wev->ready = 0;\n            return in;\n        }\n\n        if (send >= limit || in == NULL) {\n            return in;\n        }\n    }\n}\n"
  },
  {
    "path": "src/os/unix/ngx_sunpro_amd64.il",
    "content": "/\n/ Copyright (C) Igor Sysoev\n/ Copyright (C) Nginx, Inc.\n/\n\n/ ngx_atomic_uint_t ngx_atomic_cmp_set(ngx_atomic_t *lock,\n/     ngx_atomic_uint_t old, ngx_atomic_uint_t set);\n/\n/ the arguments are passed in %rdi, %rsi, %rdx\n/ the result is returned in the %rax\n\n        .inline ngx_atomic_cmp_set,0\n        movq      %rsi, %rax\n        lock\n        cmpxchgq  %rdx, (%rdi)\n        setz      %al\n        movzbq    %al, %rax\n        .end\n\n\n/ ngx_atomic_int_t ngx_atomic_fetch_add(ngx_atomic_t *value,\n/     ngx_atomic_int_t add);\n/\n/ the arguments are passed in %rdi, %rsi\n/ the result is returned in the %rax\n\n        .inline ngx_atomic_fetch_add,0\n        movq      %rsi, %rax\n        lock\n        xaddq     %rax, (%rdi)\n        .end\n\n\n/ ngx_cpu_pause()\n/\n/ the \"rep; nop\" is used instead of \"pause\" to avoid the \"[ PAUSE ]\" hardware\n/ capability added by linker because Solaris/amd64 does not know about it:\n/\n/ ld.so.1: nginx: fatal: hardware capability unsupported: 0x2000 [ PAUSE ]\n\n        .inline ngx_cpu_pause,0\n        rep; nop\n        .end\n"
  },
  {
    "path": "src/os/unix/ngx_sunpro_atomic_sparc64.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#if (NGX_PTR_SIZE == 4)\n#define NGX_CASA  ngx_casa\n#else\n#define NGX_CASA  ngx_casxa\n#endif\n\n\nngx_atomic_uint_t\nngx_casa(ngx_atomic_uint_t set, ngx_atomic_uint_t old, ngx_atomic_t *lock);\n\nngx_atomic_uint_t\nngx_casxa(ngx_atomic_uint_t set, ngx_atomic_uint_t old, ngx_atomic_t *lock);\n\n/* the code in src/os/unix/ngx_sunpro_sparc64.il */\n\n\nstatic ngx_inline ngx_atomic_uint_t\nngx_atomic_cmp_set(ngx_atomic_t *lock, ngx_atomic_uint_t old,\n    ngx_atomic_uint_t set)\n{\n    set = NGX_CASA(set, old, lock);\n\n    return (set == old);\n}\n\n\nstatic ngx_inline ngx_atomic_int_t\nngx_atomic_fetch_add(ngx_atomic_t *value, ngx_atomic_int_t add)\n{\n    ngx_atomic_uint_t  old, res;\n\n    old = *value;\n\n    for ( ;; ) {\n\n        res = old + add;\n\n        res = NGX_CASA(res, old, value);\n\n        if (res == old) {\n            return res;\n        }\n\n        old = res;\n    }\n}\n\n\n#define ngx_memory_barrier()                                                  \\\n        __asm (\".volatile\");                                                  \\\n        __asm (\"membar #LoadLoad | #LoadStore | #StoreStore | #StoreLoad\");   \\\n        __asm (\".nonvolatile\")\n\n#define ngx_cpu_pause()\n"
  },
  {
    "path": "src/os/unix/ngx_sunpro_sparc64.il",
    "content": "/\n/ Copyright (C) Igor Sysoev\n/ Copyright (C) Nginx, Inc.\n/\n\n\n/  \"casa   [%o2] 0x80, %o1, %o0\"  and\n/  \"casxa  [%o2] 0x80, %o1, %o0\"  do the following:\n/\n/       if ([%o2] == %o1) {\n/           swap(%o0, [%o2]);\n/       } else {\n/           %o0 = [%o2];\n/       }\n\n\n/ ngx_atomic_uint_t ngx_casa(ngx_atomic_uint_t set, ngx_atomic_uint_t old,\n/      ngx_atomic_t *lock);\n/\n/ the arguments are passed in the %o0, %o1, %o2\n/ the result is returned in the %o0\n\n        .inline ngx_casa,0\n        casa    [%o2] 0x80, %o1, %o0\n        .end\n\n\n/ ngx_atomic_uint_t ngx_casxa(ngx_atomic_uint_t set, ngx_atomic_uint_t old,\n/      ngx_atomic_t *lock);\n/\n/ the arguments are passed in the %o0, %o1, %o2\n/ the result is returned in the %o0\n\n        .inline ngx_casxa,0\n        casxa   [%o2] 0x80, %o1, %o0\n        .end\n"
  },
  {
    "path": "src/os/unix/ngx_sunpro_x86.il",
    "content": "/\n/ Copyright (C) Igor Sysoev\n/ Copyright (C) Nginx, Inc.\n/\n\n/ ngx_atomic_uint_t ngx_atomic_cmp_set(ngx_atomic_t *lock,\n/     ngx_atomic_uint_t old, ngx_atomic_uint_t set);\n/\n/ the arguments are passed on stack (%esp), 4(%esp), 8(%esp)\n\n        .inline ngx_atomic_cmp_set,0\n        movl      (%esp), %ecx\n        movl      4(%esp), %eax\n        movl      8(%esp), %edx\n        lock\n        cmpxchgl  %edx, (%ecx)\n        setz      %al\n        movzbl    %al, %eax\n        .end\n\n\n/ ngx_atomic_int_t ngx_atomic_fetch_add(ngx_atomic_t *value,\n/     ngx_atomic_int_t add);\n/\n/ the arguments are passed on stack (%esp), 4(%esp)\n\n        .inline ngx_atomic_fetch_add,0\n        movl      (%esp), %ecx\n        movl      4(%esp), %eax\n        lock\n        xaddl     %eax, (%ecx)\n        .end\n\n\n/ ngx_cpu_pause()\n/\n/ the \"rep; nop\" is used instead of \"pause\" to avoid the \"[ PAUSE ]\" hardware\n/ capability added by linker because Solaris/i386 does not know about it:\n/\n/ ld.so.1: nginx: fatal: hardware capability unsupported: 0x2000  [ PAUSE ]\n\n        .inline ngx_cpu_pause,0\n        rep; nop\n        .end\n"
  },
  {
    "path": "src/os/unix/ngx_sysinfo.c",
    "content": "\n/*\n * Copyright (C) 2010-2017 Alibaba Group Holding Limited\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n#if (NGX_HAVE_SYSINFO)\n#include <sys/sysinfo.h>\n#endif\n\n\nngx_int_t\nngx_getloadavg(ngx_int_t avg[], ngx_int_t nelem, ngx_log_t *log)\n{\n#if (NGX_HAVE_GETLOADAVG)\n    double      loadavg[3];\n    ngx_int_t   i;\n\n    if (getloadavg(loadavg, nelem) == -1) {\n        return NGX_ERROR;\n    }\n\n    for (i = 0; i < nelem; i ++) {\n        avg[i] = loadavg[i] * 1000;\n    }\n\n    return NGX_OK;\n\n#elif (NGX_HAVE_SYSINFO)\n\n    struct sysinfo s;\n    ngx_int_t   i;\n\n    if (sysinfo(&s)) {\n        return NGX_ERROR;\n    }\n\n    for (i = 0; i < nelem; i ++) {\n        avg[i] = s.loads[i] * 1000 / 65536;\n    }\n\n    return NGX_OK;\n\n#else\n\n    ngx_log_error(NGX_LOG_EMERG, log, 0,\n                  \"getloadavg is unsupported under current os\");\n\n    return NGX_ERROR;\n#endif\n}\n\n#if (NGX_HAVE_PROC_MEMINFO)\n\nstatic ngx_file_t                   ngx_meminfo_file;\n\n#define NGX_MEMINFO_FILE            \"/proc/meminfo\"\n#define NGX_MEMINFO_MAX_NAME_LEN    16\n\n\nngx_int_t\nngx_getmeminfo(ngx_meminfo_t *meminfo, ngx_log_t *log)\n{\n    u_char              buf[2048];\n    u_char             *p, *start, *last;\n    size_t             *sz = NULL;\n    ssize_t             n, len;\n    ngx_fd_t            fd;\n    enum {\n        sw_name = 0,\n        sw_value_start,\n        sw_value,\n        sw_skipline,\n        sw_newline,\n    } state;\n\n    ngx_memzero(meminfo, sizeof(ngx_meminfo_t));\n\n    if (ngx_meminfo_file.fd == 0) {\n\n        fd = ngx_open_file(NGX_MEMINFO_FILE, NGX_FILE_RDONLY,\n                           NGX_FILE_OPEN,\n                           NGX_FILE_DEFAULT_ACCESS);\n\n        if (fd == NGX_INVALID_FILE) {\n            ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,\n                          ngx_open_file_n \" \\\"%s\\\" failed\",\n                          NGX_MEMINFO_FILE);\n\n            return NGX_ERROR;\n        }\n\n        ngx_meminfo_file.name.data = (u_char *) NGX_MEMINFO_FILE;\n        ngx_meminfo_file.name.len = ngx_strlen(NGX_MEMINFO_FILE);\n\n        ngx_meminfo_file.fd = fd;\n    }\n\n    ngx_meminfo_file.log = log;\n    n = ngx_read_file(&ngx_meminfo_file, buf, sizeof(buf) - 1, 0);\n    if (n == NGX_ERROR) {\n        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,\n                      ngx_read_file_n \" \\\"%s\\\" failed\",\n                      NGX_MEMINFO_FILE);\n\n        return NGX_ERROR;\n    }\n\n    p = buf;\n    start = buf;\n    last = buf + n;\n    state = sw_name;\n\n    for (; p < last; p++) {\n\n        if (*p == '\\n') {\n            state = sw_newline;\n        }\n\n        switch (state) {\n\n        case sw_name:\n            if (*p != ':') {\n                continue;\n            }\n\n            len = p - start;\n            sz = NULL;\n\n            switch (len) {\n            case 6:\n                /* Cached */\n                if (meminfo->cachedram == 0 &&\n                    ngx_strncmp(start, \"Cached\", len) == 0)\n                {\n                    sz = &meminfo->cachedram;\n                }\n                break;\n            case 7:\n                /* Buffers MemFree */\n                if (meminfo->bufferram == 0 &&\n                    ngx_strncmp(start, \"Buffers\", len) == 0)\n                {\n                    sz = &meminfo->bufferram;\n                } else if (meminfo->freeram == 0 &&\n                           ngx_strncmp(start, \"MemFree\", len) == 0)\n                {\n                    sz = &meminfo->freeram;\n                }\n                break;\n            case 8:\n                /* MemTotal SwapFree */\n                if (meminfo->totalram == 0 &&\n                    ngx_strncmp(start, \"MemTotal\", len) == 0)\n                {\n                    sz = &meminfo->totalram;\n                } else if (meminfo->freeswap == 0 &&\n                           ngx_strncmp(start, \"SwapFree\", len) == 0)\n                {\n                    sz = &meminfo->freeswap;\n                }\n                break;\n            case 9:\n                /* SwapTotal */\n                if (meminfo->totalswap == 0 &&\n                    ngx_strncmp(start, \"SwapTotal\", len) == 0)\n                {\n                    sz = &meminfo->totalswap;\n                }\n                break;\n            }\n\n            if (sz == NULL) {\n                state = sw_skipline;\n                continue;\n            }\n\n            state = sw_value_start;\n\n            continue;\n\n        case sw_value_start:\n\n            if (*p == ' ') {\n                continue;\n            }\n\n            start = p;\n            state = sw_value;\n\n            continue;\n\n        case sw_value:\n\n            if (*p >= '0' && *p <= '9') {\n                continue;\n            }\n\n            *(sz) =  ngx_atosz(start, p - start) * 1024;\n\n            state = sw_skipline;\n\n            continue;\n\n        case sw_skipline:\n\n            continue;\n\n        case sw_newline:\n\n            state = sw_name;\n            start = p + 1;\n\n            continue;\n        }\n    }\n\n    return NGX_OK;\n}\n\n#else\n\nngx_int_t\nngx_getmeminfo(ngx_meminfo_t *meminfo, ngx_log_t *log)\n{\n    ngx_log_error(NGX_LOG_EMERG, log, 0,\n                  \"getmeminfo is unsupported under current os\");\n\n    return NGX_ERROR;\n}\n\n#endif\n\n#if (NGX_HAVE_PROC_STAT)\n\nstatic ngx_file_t                   ngx_cpuinfo_file;\n\n#define NGX_CPUINFO_FILE            \"/proc/stat\"\n\n\nngx_int_t\nngx_getcpuinfo(ngx_str_t *cpunumber, ngx_cpuinfo_t *cpuinfo, ngx_log_t *log)\n{\n    u_char              buf[1024 * 1024];\n    u_char             *p, *q, *last;\n    ssize_t             n;\n    ngx_fd_t            fd;\n    time_t              cputime;\n    enum {\n        sw_user = 0,\n        sw_nice,\n        sw_sys,\n        sw_idle,\n        sw_iowait,\n        sw_irq,\n        sw_softirq ,        \n    } state;\n\n    ngx_memzero(cpuinfo, sizeof(ngx_cpuinfo_t));\n\n    if (ngx_cpuinfo_file.fd == 0) {\n\n        fd = ngx_open_file(NGX_CPUINFO_FILE, NGX_FILE_RDONLY,\n                           NGX_FILE_OPEN,\n                           NGX_FILE_DEFAULT_ACCESS);\n\n        if (fd == NGX_INVALID_FILE) {\n            ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,\n                          ngx_open_file_n \" \\\"%s\\\" failed\",\n                          NGX_CPUINFO_FILE);\n\n            return NGX_ERROR;\n        }\n\n        ngx_cpuinfo_file.name.data = (u_char *) NGX_CPUINFO_FILE;\n        ngx_cpuinfo_file.name.len = ngx_strlen(NGX_CPUINFO_FILE);\n\n        ngx_cpuinfo_file.fd = fd;\n    }\n\n    ngx_cpuinfo_file.log = log;\n    n = ngx_read_file(&ngx_cpuinfo_file, buf, sizeof(buf) - 1, 0);\n    if (n == NGX_ERROR) {\n        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,\n                      ngx_read_file_n \" \\\"%s\\\" failed\",\n                      NGX_CPUINFO_FILE);\n\n        return NGX_ERROR;\n    }\n\n    p = buf;\n    last = buf + n;\n    \n    for (; p < last; p++) {\n        while(*p == ' ' || *p == '\\n') {\n            p++;\n        }\n        \n        if (ngx_strncasecmp((u_char *) cpunumber->data,\n                            (u_char *) p, cpunumber->len) == 0) \n        {\n            \n            for (state = 0, p += cpunumber->len, \n                 q = (u_char *) strtok((char *) p, \" \"); q; state++) \n            {\n                cputime = ngx_atotm(q, strlen((char *) q));\n                        \n                switch (state) {\n                case sw_user:\n                    cpuinfo->usr = cputime;\n                    break;\n                case sw_nice:\n                    cpuinfo->nice = cputime;\n                    break;\n                case sw_sys:\n                    cpuinfo->sys = cputime;\n                    break;\n                case sw_idle:\n                    cpuinfo->idle = cputime;\n                    break;\n                case sw_iowait:\n                    cpuinfo->iowait = cputime;\n                    break;\n                case sw_irq:\n                    cpuinfo->irq = cputime;\n                    break;  \n                case sw_softirq:\n                    cpuinfo->softirq = cputime;\n                    break;                    \n                }    \n                \n                q = (u_char *) strtok(NULL, \" \");\n            }\n        }\n        \n        break;\n        \n    }\n\n    return NGX_OK;\n}\n\n#else\n\nngx_int_t\nngx_getcpuinfo(ngx_str_t *cpunumber, ngx_cpuinfo_t *cpuinfo, ngx_log_t *log)\n{\n    ngx_log_error(NGX_LOG_EMERG, log, 0,\n                  \"ngx_getcpuinfo is unsupported under current os\");\n\n    return NGX_ERROR;\n}\n\n#endif\n"
  },
  {
    "path": "src/os/unix/ngx_sysinfo.h",
    "content": "/*\n * Copyright (C) 2010-2017 Alibaba Group Holding Limited\n */\n\n\n#ifndef _NGX_SYSINFO_H_INCLUDED_\n#define _NGX_SYSINFO_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\n/* in bytes */\ntypedef struct {\n    size_t totalram;\n    size_t freeram;\n    size_t bufferram;\n    size_t cachedram;\n    size_t totalswap;\n    size_t freeswap;\n} ngx_meminfo_t;\n\n\ntypedef struct {\n    time_t usr;\n    time_t nice;\n    time_t sys;\n    time_t idle;\n    time_t iowait;\n    time_t irq;\n    time_t softirq;    \n}ngx_cpuinfo_t;\n\n\nngx_int_t ngx_getloadavg(ngx_int_t avg[], ngx_int_t nelem, ngx_log_t *log);\nngx_int_t ngx_getmeminfo(ngx_meminfo_t *meminfo, ngx_log_t *log);\nngx_int_t ngx_getcpuinfo(ngx_str_t *cpunumber, ngx_cpuinfo_t *cpuinfo,\n    ngx_log_t *log);\n\n#endif /* _NGX_SYSINFO_H_INCLUDED_ */\n\n"
  },
  {
    "path": "src/os/unix/ngx_thread.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_THREAD_H_INCLUDED_\n#define _NGX_THREAD_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n#if (NGX_THREADS)\n\n#include <pthread.h>\n\n\ntypedef pthread_mutex_t  ngx_thread_mutex_t;\n\nngx_int_t ngx_thread_mutex_create(ngx_thread_mutex_t *mtx, ngx_log_t *log);\nngx_int_t ngx_thread_mutex_destroy(ngx_thread_mutex_t *mtx, ngx_log_t *log);\nngx_int_t ngx_thread_mutex_lock(ngx_thread_mutex_t *mtx, ngx_log_t *log);\nngx_int_t ngx_thread_mutex_unlock(ngx_thread_mutex_t *mtx, ngx_log_t *log);\n\n\ntypedef pthread_cond_t  ngx_thread_cond_t;\n\nngx_int_t ngx_thread_cond_create(ngx_thread_cond_t *cond, ngx_log_t *log);\nngx_int_t ngx_thread_cond_destroy(ngx_thread_cond_t *cond, ngx_log_t *log);\nngx_int_t ngx_thread_cond_signal(ngx_thread_cond_t *cond, ngx_log_t *log);\nngx_int_t ngx_thread_cond_wait(ngx_thread_cond_t *cond, ngx_thread_mutex_t *mtx,\n    ngx_log_t *log);\n\n\n#if (NGX_LINUX)\n\ntypedef pid_t      ngx_tid_t;\n#define NGX_TID_T_FMT         \"%P\"\n\n#elif (NGX_FREEBSD)\n\ntypedef uint32_t   ngx_tid_t;\n#define NGX_TID_T_FMT         \"%uD\"\n\n#elif (NGX_DARWIN)\n\ntypedef uint64_t   ngx_tid_t;\n#define NGX_TID_T_FMT         \"%uL\"\n\n#else\n\ntypedef uint64_t   ngx_tid_t;\n#define NGX_TID_T_FMT         \"%uL\"\n\n#endif\n\nngx_tid_t ngx_thread_tid(void);\n\n#define ngx_log_tid           ngx_thread_tid()\n\n#else\n\n#define ngx_log_tid           0\n#define NGX_TID_T_FMT         \"%d\"\n\n#endif\n\n\n#endif /* _NGX_THREAD_H_INCLUDED_ */\n"
  },
  {
    "path": "src/os/unix/ngx_thread_cond.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\nngx_int_t\nngx_thread_cond_create(ngx_thread_cond_t *cond, ngx_log_t *log)\n{\n    ngx_err_t  err;\n\n    err = pthread_cond_init(cond, NULL);\n    if (err == 0) {\n        return NGX_OK;\n    }\n\n    ngx_log_error(NGX_LOG_EMERG, log, err, \"pthread_cond_init() failed\");\n    return NGX_ERROR;\n}\n\n\nngx_int_t\nngx_thread_cond_destroy(ngx_thread_cond_t *cond, ngx_log_t *log)\n{\n    ngx_err_t  err;\n\n    err = pthread_cond_destroy(cond);\n    if (err == 0) {\n        return NGX_OK;\n    }\n\n    ngx_log_error(NGX_LOG_EMERG, log, err, \"pthread_cond_destroy() failed\");\n    return NGX_ERROR;\n}\n\n\nngx_int_t\nngx_thread_cond_signal(ngx_thread_cond_t *cond, ngx_log_t *log)\n{\n    ngx_err_t  err;\n\n    err = pthread_cond_signal(cond);\n    if (err == 0) {\n        return NGX_OK;\n    }\n\n    ngx_log_error(NGX_LOG_EMERG, log, err, \"pthread_cond_signal() failed\");\n    return NGX_ERROR;\n}\n\n\nngx_int_t\nngx_thread_cond_wait(ngx_thread_cond_t *cond, ngx_thread_mutex_t *mtx,\n    ngx_log_t *log)\n{\n    ngx_err_t  err;\n\n    err = pthread_cond_wait(cond, mtx);\n\n#if 0\n    ngx_time_update();\n#endif\n\n    if (err == 0) {\n        return NGX_OK;\n    }\n\n    ngx_log_error(NGX_LOG_ALERT, log, err, \"pthread_cond_wait() failed\");\n\n    return NGX_ERROR;\n}\n"
  },
  {
    "path": "src/os/unix/ngx_thread_id.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_thread_pool.h>\n\n\n#if (NGX_LINUX)\n\n/*\n * Linux thread id is a pid of thread created by clone(2),\n * glibc does not provide a wrapper for gettid().\n */\n\nngx_tid_t\nngx_thread_tid(void)\n{\n    return syscall(SYS_gettid);\n}\n\n#elif (NGX_FREEBSD) && (__FreeBSD_version >= 900031)\n\n#include <pthread_np.h>\n\nngx_tid_t\nngx_thread_tid(void)\n{\n    return pthread_getthreadid_np();\n}\n\n#elif (NGX_DARWIN)\n\n/*\n * MacOSX thread has two thread ids:\n *\n * 1) MacOSX 10.6 (Snow Leoprad) has pthread_threadid_np() returning\n *    an uint64_t value, which is obtained using the __thread_selfid()\n *    syscall.  It is a number above 300,000.\n */\n\nngx_tid_t\nngx_thread_tid(void)\n{\n    uint64_t  tid;\n\n    (void) pthread_threadid_np(NULL, &tid);\n    return tid;\n}\n\n/*\n * 2) Kernel thread mach_port_t returned by pthread_mach_thread_np().\n *    It is a number in range 100-100,000.\n *\n * return pthread_mach_thread_np(pthread_self());\n */\n\n#else\n\nngx_tid_t\nngx_thread_tid(void)\n{\n    return (uint64_t) (uintptr_t) pthread_self();\n}\n\n#endif\n"
  },
  {
    "path": "src/os/unix/ngx_thread_mutex.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\n/*\n * All modern pthread mutex implementations try to acquire a lock\n * atomically in userland before going to sleep in kernel.  Some\n * spins before the sleeping.\n *\n * In Solaris since version 8 all mutex types spin before sleeping.\n * The default spin count is 1000.  It can be overridden using\n * _THREAD_ADAPTIVE_SPIN=100 environment variable.\n *\n * In MacOSX all mutex types spin to acquire a lock protecting a mutex's\n * internals.  If the mutex is busy, thread calls Mach semaphore_wait().\n *\n *\n * PTHREAD_MUTEX_NORMAL lacks deadlock detection and is the fastest\n * mutex type.\n *\n *   Linux:    No spinning.  The internal name PTHREAD_MUTEX_TIMED_NP\n *             remains from the times when pthread_mutex_timedlock() was\n *             non-standard extension.  Alias name: PTHREAD_MUTEX_FAST_NP.\n *   FreeBSD:  No spinning.\n *\n *\n * PTHREAD_MUTEX_ERRORCHECK is usually as fast as PTHREAD_MUTEX_NORMAL\n * yet has lightweight deadlock detection.\n *\n *   Linux:    No spinning.  The internal name: PTHREAD_MUTEX_ERRORCHECK_NP.\n *   FreeBSD:  No spinning.\n *\n *\n * PTHREAD_MUTEX_RECURSIVE allows recursive locking.\n *\n *   Linux:    No spinning.  The internal name: PTHREAD_MUTEX_RECURSIVE_NP.\n *   FreeBSD:  No spinning.\n *\n *\n * PTHREAD_MUTEX_ADAPTIVE_NP spins on SMP systems before sleeping.\n *\n *   Linux:    No deadlock detection.  Dynamically changes a spin count\n *             for each mutex from 10 to 100 based on spin count taken\n *             previously.\n *   FreeBSD:  Deadlock detection.  The default spin count is 2000.\n *             It can be overridden using LIBPTHREAD_SPINLOOPS environment\n *             variable or by pthread_mutex_setspinloops_np().  If a lock\n *             is still busy, sched_yield() can be called on both UP and\n *             SMP systems.  The default yield loop count is zero, but\n *             it can be set by LIBPTHREAD_YIELDLOOPS environment\n *             variable or by pthread_mutex_setyieldloops_np().\n *   Solaris:  No PTHREAD_MUTEX_ADAPTIVE_NP.\n *   MacOSX:   No PTHREAD_MUTEX_ADAPTIVE_NP.\n *\n *\n * PTHREAD_MUTEX_ELISION_NP is a Linux extension to elide locks using\n * Intel Restricted Transactional Memory.  It is the most suitable for\n * rwlock pattern access because it allows simultaneous reads without lock.\n * Supported since glibc 2.18.\n *\n *\n * PTHREAD_MUTEX_DEFAULT is default mutex type.\n *\n *   Linux:    PTHREAD_MUTEX_NORMAL.\n *   FreeBSD:  PTHREAD_MUTEX_ERRORCHECK.\n *   Solaris:  PTHREAD_MUTEX_NORMAL.\n *   MacOSX:   PTHREAD_MUTEX_NORMAL.\n */\n\n\nngx_int_t\nngx_thread_mutex_create(ngx_thread_mutex_t *mtx, ngx_log_t *log)\n{\n    ngx_err_t            err;\n    pthread_mutexattr_t  attr;\n\n    err = pthread_mutexattr_init(&attr);\n    if (err != 0) {\n        ngx_log_error(NGX_LOG_EMERG, log, err,\n                      \"pthread_mutexattr_init() failed\");\n        return NGX_ERROR;\n    }\n\n    err = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);\n    if (err != 0) {\n        ngx_log_error(NGX_LOG_EMERG, log, err,\n                      \"pthread_mutexattr_settype\"\n                      \"(PTHREAD_MUTEX_ERRORCHECK) failed\");\n        return NGX_ERROR;\n    }\n\n    err = pthread_mutex_init(mtx, &attr);\n    if (err != 0) {\n        ngx_log_error(NGX_LOG_EMERG, log, err,\n                      \"pthread_mutex_init() failed\");\n        return NGX_ERROR;\n    }\n\n    err = pthread_mutexattr_destroy(&attr);\n    if (err != 0) {\n        ngx_log_error(NGX_LOG_ALERT, log, err,\n                      \"pthread_mutexattr_destroy() failed\");\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_thread_mutex_destroy(ngx_thread_mutex_t *mtx, ngx_log_t *log)\n{\n    ngx_err_t  err;\n\n    err = pthread_mutex_destroy(mtx);\n    if (err != 0) {\n        ngx_log_error(NGX_LOG_ALERT, log, err,\n                      \"pthread_mutex_destroy() failed\");\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_thread_mutex_lock(ngx_thread_mutex_t *mtx, ngx_log_t *log)\n{\n    ngx_err_t  err;\n\n    err = pthread_mutex_lock(mtx);\n    if (err == 0) {\n        return NGX_OK;\n    }\n\n    ngx_log_error(NGX_LOG_ALERT, log, err, \"pthread_mutex_lock() failed\");\n\n    return NGX_ERROR;\n}\n\n\nngx_int_t\nngx_thread_mutex_unlock(ngx_thread_mutex_t *mtx, ngx_log_t *log)\n{\n    ngx_err_t  err;\n\n    err = pthread_mutex_unlock(mtx);\n\n#if 0\n    ngx_time_update();\n#endif\n\n    if (err == 0) {\n        return NGX_OK;\n    }\n\n    ngx_log_error(NGX_LOG_ALERT, log, err, \"pthread_mutex_unlock() failed\");\n\n    return NGX_ERROR;\n}\n"
  },
  {
    "path": "src/os/unix/ngx_time.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\n/*\n * FreeBSD does not test /etc/localtime change, however, we can workaround it\n * by calling tzset() with TZ and then without TZ to update timezone.\n * The trick should work since FreeBSD 2.1.0.\n *\n * Linux does not test /etc/localtime change in localtime(),\n * but may stat(\"/etc/localtime\") several times in every strftime(),\n * therefore we use it to update timezone.\n *\n * Solaris does not test /etc/TIMEZONE change too and no workaround available.\n */\n\nvoid\nngx_timezone_update(void)\n{\n#if (NGX_FREEBSD)\n\n    if (getenv(\"TZ\")) {\n        return;\n    }\n\n    putenv(\"TZ=UTC\");\n\n    tzset();\n\n    unsetenv(\"TZ\");\n\n    tzset();\n\n#elif (NGX_LINUX)\n    time_t      s;\n    struct tm  *t;\n    char        buf[4];\n\n    s = time(0);\n\n    t = localtime(&s);\n\n    strftime(buf, 4, \"%H\", t);\n\n#endif\n}\n\n\nvoid\nngx_localtime(time_t s, ngx_tm_t *tm)\n{\n#if (NGX_HAVE_LOCALTIME_R)\n    (void) localtime_r(&s, tm);\n\n#else\n    ngx_tm_t  *t;\n\n    t = localtime(&s);\n    *tm = *t;\n\n#endif\n\n    tm->ngx_tm_mon++;\n    tm->ngx_tm_year += 1900;\n}\n\n\nvoid\nngx_libc_localtime(time_t s, struct tm *tm)\n{\n#if (NGX_HAVE_LOCALTIME_R)\n    (void) localtime_r(&s, tm);\n\n#else\n    struct tm  *t;\n\n    t = localtime(&s);\n    *tm = *t;\n\n#endif\n}\n\n\nvoid\nngx_libc_gmtime(time_t s, struct tm *tm)\n{\n#if (NGX_HAVE_LOCALTIME_R)\n    (void) gmtime_r(&s, tm);\n\n#else\n    struct tm  *t;\n\n    t = gmtime(&s);\n    *tm = *t;\n\n#endif\n}\n"
  },
  {
    "path": "src/os/unix/ngx_time.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_TIME_H_INCLUDED_\n#define _NGX_TIME_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\ntypedef ngx_rbtree_key_t      ngx_msec_t;\n#if (T_NGX_RET_CACHE)\ntypedef ngx_rbtree_key_t      ngx_usec_t;\n#endif\ntypedef ngx_rbtree_key_int_t  ngx_msec_int_t;\n#if (T_NGX_VARS)\ntypedef ngx_rbtree_key_int_t  ngx_usec_int_t;\n#endif\n\ntypedef struct tm             ngx_tm_t;\n\n#define ngx_tm_sec            tm_sec\n#define ngx_tm_min            tm_min\n#define ngx_tm_hour           tm_hour\n#define ngx_tm_mday           tm_mday\n#define ngx_tm_mon            tm_mon\n#define ngx_tm_year           tm_year\n#define ngx_tm_wday           tm_wday\n#define ngx_tm_isdst          tm_isdst\n\n#define ngx_tm_sec_t          int\n#define ngx_tm_min_t          int\n#define ngx_tm_hour_t         int\n#define ngx_tm_mday_t         int\n#define ngx_tm_mon_t          int\n#define ngx_tm_year_t         int\n#define ngx_tm_wday_t         int\n\n\n#if (NGX_HAVE_GMTOFF)\n#define ngx_tm_gmtoff         tm_gmtoff\n#define ngx_tm_zone           tm_zone\n#endif\n\n\n#if (NGX_SOLARIS)\n\n#define ngx_timezone(isdst) (- (isdst ? altzone : timezone) / 60)\n\n#else\n\n#define ngx_timezone(isdst) (- (isdst ? timezone + 3600 : timezone) / 60)\n\n#endif\n\n\nvoid ngx_timezone_update(void);\nvoid ngx_localtime(time_t s, ngx_tm_t *tm);\nvoid ngx_libc_localtime(time_t s, struct tm *tm);\nvoid ngx_libc_gmtime(time_t s, struct tm *tm);\n\n#define ngx_gettimeofday(tp)  (void) gettimeofday(tp, NULL);\n#define ngx_msleep(ms)        (void) usleep(ms * 1000)\n#define ngx_sleep(s)          (void) sleep(s)\n\n\n#endif /* _NGX_TIME_H_INCLUDED_ */\n"
  },
  {
    "path": "src/os/unix/ngx_udp_recv.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n\n\nssize_t\nngx_udp_unix_recv(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 = recv(c->fd, buf, size, 0);\n\n        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"recv: fd:%d %z of %uz\", c->fd, n, size);\n\n        if (n >= 0) {\n\n#if (NGX_HAVE_KQUEUE)\n\n            if (ngx_event_flags & NGX_USE_KQUEUE_EVENT) {\n                rev->available -= n;\n\n                /*\n                 * rev->available may be negative here because some additional\n                 * bytes may be received between kevent() and recv()\n                 */\n\n                if (rev->available <= 0) {\n                    rev->ready = 0;\n                    rev->available = 0;\n                }\n            }\n\n#endif\n\n            return n;\n        }\n\n        err = ngx_socket_errno;\n\n        if (err == NGX_EAGAIN || err == NGX_EINTR) {\n            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,\n                           \"recv() not ready\");\n            n = NGX_AGAIN;\n\n        } else {\n            n = ngx_connection_error(c, err, \"recv() 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"
  },
  {
    "path": "src/os/unix/ngx_udp_send.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n\n\nssize_t\nngx_udp_unix_send(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    for ( ;; ) {\n        n = sendto(c->fd, buf, size, 0, c->sockaddr, c->socklen);\n\n        ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"sendto: fd:%d %z of %uz to \\\"%V\\\"\",\n                       c->fd, n, size, &c->addr_text);\n\n        if (n >= 0) {\n            if ((size_t) n != size) {\n                wev->error = 1;\n                (void) ngx_connection_error(c, 0, \"sendto() incomplete\");\n                return NGX_ERROR;\n            }\n\n            c->sent += n;\n\n            return n;\n        }\n\n        err = ngx_socket_errno;\n\n        if (err == NGX_EAGAIN) {\n            wev->ready = 0;\n            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, NGX_EAGAIN,\n                           \"sendto() not ready\");\n            return NGX_AGAIN;\n        }\n\n        if (err != NGX_EINTR) {\n            wev->error = 1;\n            (void) ngx_connection_error(c, err, \"sendto() failed\");\n            return NGX_ERROR;\n        }\n    }\n}\n"
  },
  {
    "path": "src/os/unix/ngx_udp_sendmsg_chain.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n\n\nstatic ngx_chain_t *ngx_udp_output_chain_to_iovec(ngx_iovec_t *vec,\n    ngx_chain_t *in, ngx_log_t *log);\nstatic ssize_t ngx_sendmsg_vec(ngx_connection_t *c, ngx_iovec_t *vec);\n\n\nngx_chain_t *\nngx_udp_unix_sendmsg_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)\n{\n    ssize_t        n;\n    off_t          send;\n    ngx_chain_t   *cl;\n    ngx_event_t   *wev;\n    ngx_iovec_t    vec;\n    struct iovec   iovs[NGX_IOVS_PREALLOCATE];\n\n    wev = c->write;\n\n    if (!wev->ready) {\n        return in;\n    }\n\n#if (NGX_HAVE_KQUEUE)\n\n    if ((ngx_event_flags & NGX_USE_KQUEUE_EVENT) && wev->pending_eof) {\n        (void) ngx_connection_error(c, wev->kq_errno,\n                               \"kevent() reported about an closed connection\");\n        wev->error = 1;\n        return NGX_CHAIN_ERROR;\n    }\n\n#endif\n\n    /* the maximum limit size is the maximum size_t value - the page size */\n\n    if (limit == 0 || limit > (off_t) (NGX_MAX_SIZE_T_VALUE - ngx_pagesize)) {\n        limit = NGX_MAX_SIZE_T_VALUE - ngx_pagesize;\n    }\n\n    send = 0;\n\n    vec.iovs = iovs;\n    vec.nalloc = NGX_IOVS_PREALLOCATE;\n\n    for ( ;; ) {\n\n        /* create the iovec and coalesce the neighbouring bufs */\n\n        cl = ngx_udp_output_chain_to_iovec(&vec, in, c->log);\n\n        if (cl == NGX_CHAIN_ERROR) {\n            return NGX_CHAIN_ERROR;\n        }\n\n        if (cl && cl->buf->in_file) {\n            ngx_log_error(NGX_LOG_ALERT, c->log, 0,\n                          \"file buf in sendmsg \"\n                          \"t:%d r:%d f:%d %p %p-%p %p %O-%O\",\n                          cl->buf->temporary,\n                          cl->buf->recycled,\n                          cl->buf->in_file,\n                          cl->buf->start,\n                          cl->buf->pos,\n                          cl->buf->last,\n                          cl->buf->file,\n                          cl->buf->file_pos,\n                          cl->buf->file_last);\n\n            ngx_debug_point();\n\n            return NGX_CHAIN_ERROR;\n        }\n\n        if (cl == in) {\n            return in;\n        }\n\n        send += vec.size;\n\n        n = ngx_sendmsg_vec(c, &vec);\n\n        if (n == NGX_ERROR) {\n            return NGX_CHAIN_ERROR;\n        }\n\n        if (n == NGX_AGAIN) {\n            wev->ready = 0;\n            return in;\n        }\n\n        c->sent += n;\n\n        in = ngx_chain_update_sent(in, n);\n\n        if (send >= limit || in == NULL) {\n            return in;\n        }\n    }\n}\n\n\nstatic ngx_chain_t *\nngx_udp_output_chain_to_iovec(ngx_iovec_t *vec, ngx_chain_t *in, ngx_log_t *log)\n{\n    size_t         total, size;\n    u_char        *prev;\n    ngx_uint_t     n, flush;\n    ngx_chain_t   *cl;\n    struct iovec  *iov;\n\n    cl = in;\n    iov = NULL;\n    prev = NULL;\n    total = 0;\n    n = 0;\n    flush = 0;\n\n    for ( /* void */ ; in && !flush; in = in->next) {\n\n        if (in->buf->flush || in->buf->last_buf) {\n            flush = 1;\n        }\n\n        if (ngx_buf_special(in->buf)) {\n            continue;\n        }\n\n        if (in->buf->in_file) {\n            break;\n        }\n\n        if (!ngx_buf_in_memory(in->buf)) {\n            ngx_log_error(NGX_LOG_ALERT, log, 0,\n                          \"bad buf in output chain \"\n                          \"t:%d r:%d f:%d %p %p-%p %p %O-%O\",\n                          in->buf->temporary,\n                          in->buf->recycled,\n                          in->buf->in_file,\n                          in->buf->start,\n                          in->buf->pos,\n                          in->buf->last,\n                          in->buf->file,\n                          in->buf->file_pos,\n                          in->buf->file_last);\n\n            ngx_debug_point();\n\n            return NGX_CHAIN_ERROR;\n        }\n\n        size = in->buf->last - in->buf->pos;\n\n        if (prev == in->buf->pos) {\n            iov->iov_len += size;\n\n        } else {\n            if (n == vec->nalloc) {\n                ngx_log_error(NGX_LOG_ALERT, log, 0,\n                              \"too many parts in a datagram\");\n                return NGX_CHAIN_ERROR;\n            }\n\n            iov = &vec->iovs[n++];\n\n            iov->iov_base = (void *) in->buf->pos;\n            iov->iov_len = size;\n        }\n\n        prev = in->buf->pos + size;\n        total += size;\n    }\n\n    if (!flush) {\n#if (NGX_SUPPRESS_WARN)\n        vec->size = 0;\n        vec->count = 0;\n#endif\n        return cl;\n    }\n\n    /* zero-sized datagram; pretend to have at least 1 iov */\n    if (n == 0) {\n        iov = &vec->iovs[n++];\n        iov->iov_base = NULL;\n        iov->iov_len = 0;\n    }\n\n    vec->count = n;\n    vec->size = total;\n\n    return in;\n}\n\n\nstatic ssize_t\nngx_sendmsg_vec(ngx_connection_t *c, ngx_iovec_t *vec)\n{\n    struct msghdr    msg;\n\n#if (NGX_HAVE_ADDRINFO_CMSG)\n    struct cmsghdr  *cmsg;\n    u_char           msg_control[CMSG_SPACE(sizeof(ngx_addrinfo_t))];\n#endif\n\n    ngx_memzero(&msg, sizeof(struct msghdr));\n\n    if (c->socklen) {\n        msg.msg_name = c->sockaddr;\n        msg.msg_namelen = c->socklen;\n    }\n\n    msg.msg_iov = vec->iovs;\n    msg.msg_iovlen = vec->count;\n\n#if (NGX_HAVE_ADDRINFO_CMSG)\n    if (c->listening && c->listening->wildcard && c->local_sockaddr) {\n\n        msg.msg_control = msg_control;\n        msg.msg_controllen = sizeof(msg_control);\n        ngx_memzero(msg_control, sizeof(msg_control));\n\n        cmsg = CMSG_FIRSTHDR(&msg);\n\n        msg.msg_controllen = ngx_set_srcaddr_cmsg(cmsg, c->local_sockaddr);\n    }\n#endif\n\n    return ngx_sendmsg(c, &msg, 0);\n}\n\n\n#if (NGX_HAVE_ADDRINFO_CMSG)\n\nsize_t\nngx_set_srcaddr_cmsg(struct cmsghdr *cmsg, struct sockaddr *local_sockaddr)\n{\n    size_t                len;\n#if (NGX_HAVE_IP_SENDSRCADDR)\n    struct in_addr       *addr;\n    struct sockaddr_in   *sin;\n#elif (NGX_HAVE_IP_PKTINFO)\n    struct in_pktinfo    *pkt;\n    struct sockaddr_in   *sin;\n#endif\n\n#if (NGX_HAVE_INET6 && NGX_HAVE_IPV6_RECVPKTINFO)\n    struct in6_pktinfo   *pkt6;\n    struct sockaddr_in6  *sin6;\n#endif\n\n\n#if (NGX_HAVE_IP_SENDSRCADDR) || (NGX_HAVE_IP_PKTINFO)\n\n    if (local_sockaddr->sa_family == AF_INET) {\n\n        cmsg->cmsg_level = IPPROTO_IP;\n\n#if (NGX_HAVE_IP_SENDSRCADDR)\n\n        cmsg->cmsg_type = IP_SENDSRCADDR;\n        cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_addr));\n        len = CMSG_SPACE(sizeof(struct in_addr));\n\n        sin = (struct sockaddr_in *) local_sockaddr;\n\n        addr = (struct in_addr *) CMSG_DATA(cmsg);\n        *addr = sin->sin_addr;\n\n#elif (NGX_HAVE_IP_PKTINFO)\n\n        cmsg->cmsg_type = IP_PKTINFO;\n        cmsg->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));\n        len = CMSG_SPACE(sizeof(struct in_pktinfo));\n\n        sin = (struct sockaddr_in *) local_sockaddr;\n\n        pkt = (struct in_pktinfo *) CMSG_DATA(cmsg);\n        ngx_memzero(pkt, sizeof(struct in_pktinfo));\n        pkt->ipi_spec_dst = sin->sin_addr;\n\n#endif\n        return len;\n    }\n\n#endif\n\n#if (NGX_HAVE_INET6 && NGX_HAVE_IPV6_RECVPKTINFO)\n    if (local_sockaddr->sa_family == AF_INET6) {\n\n        cmsg->cmsg_level = IPPROTO_IPV6;\n        cmsg->cmsg_type = IPV6_PKTINFO;\n        cmsg->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));\n        len = CMSG_SPACE(sizeof(struct in6_pktinfo));\n\n        sin6 = (struct sockaddr_in6 *) local_sockaddr;\n\n        pkt6 = (struct in6_pktinfo *) CMSG_DATA(cmsg);\n        ngx_memzero(pkt6, sizeof(struct in6_pktinfo));\n        pkt6->ipi6_addr = sin6->sin6_addr;\n\n        return len;\n    }\n#endif\n\n    return 0;\n}\n\n\nngx_int_t\nngx_get_srcaddr_cmsg(struct cmsghdr *cmsg, struct sockaddr *local_sockaddr)\n{\n\n#if (NGX_HAVE_IP_RECVDSTADDR)\n    struct in_addr       *addr;\n    struct sockaddr_in   *sin;\n#elif (NGX_HAVE_IP_PKTINFO)\n    struct in_pktinfo    *pkt;\n    struct sockaddr_in   *sin;\n#endif\n\n#if (NGX_HAVE_INET6 && NGX_HAVE_IPV6_RECVPKTINFO)\n    struct in6_pktinfo   *pkt6;\n    struct sockaddr_in6  *sin6;\n#endif\n\n\n#if (NGX_HAVE_IP_RECVDSTADDR)\n\n    if (cmsg->cmsg_level == IPPROTO_IP\n        && cmsg->cmsg_type == IP_RECVDSTADDR\n        && local_sockaddr->sa_family == AF_INET)\n    {\n        addr = (struct in_addr *) CMSG_DATA(cmsg);\n        sin = (struct sockaddr_in *) local_sockaddr;\n        sin->sin_addr = *addr;\n\n        return NGX_OK;\n    }\n\n#elif (NGX_HAVE_IP_PKTINFO)\n\n    if (cmsg->cmsg_level == IPPROTO_IP\n        && cmsg->cmsg_type == IP_PKTINFO\n        && local_sockaddr->sa_family == AF_INET)\n    {\n        pkt = (struct in_pktinfo *) CMSG_DATA(cmsg);\n        sin = (struct sockaddr_in *) local_sockaddr;\n        sin->sin_addr = pkt->ipi_addr;\n\n        return NGX_OK;\n    }\n\n#endif\n\n#if (NGX_HAVE_INET6 && NGX_HAVE_IPV6_RECVPKTINFO)\n\n    if (cmsg->cmsg_level == IPPROTO_IPV6\n        && cmsg->cmsg_type == IPV6_PKTINFO\n        && local_sockaddr->sa_family == AF_INET6)\n    {\n        pkt6 = (struct in6_pktinfo *) CMSG_DATA(cmsg);\n        sin6 = (struct sockaddr_in6 *) local_sockaddr;\n        sin6->sin6_addr = pkt6->ipi6_addr;\n\n        return NGX_OK;\n    }\n\n#endif\n\n    return NGX_DECLINED;\n}\n\n#endif\n\n\nssize_t\nngx_sendmsg(ngx_connection_t *c, struct msghdr *msg, int flags)\n{\n    ssize_t    n;\n    ngx_err_t  err;\n#if (NGX_DEBUG)\n    size_t      size;\n    ngx_uint_t  i;\n#endif\n\neintr:\n\n    n = sendmsg(c->fd, msg, flags);\n\n    if (n == -1) {\n        err = ngx_errno;\n\n        switch (err) {\n        case NGX_EAGAIN:\n            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,\n                           \"sendmsg() not ready\");\n            return NGX_AGAIN;\n\n        case NGX_EINTR:\n            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,\n                           \"sendmsg() was interrupted\");\n            goto eintr;\n\n        default:\n            c->write->error = 1;\n            ngx_connection_error(c, err, \"sendmsg() failed\");\n            return NGX_ERROR;\n        }\n    }\n\n#if (NGX_DEBUG)\n    for (i = 0, size = 0; i < (size_t) msg->msg_iovlen; i++) {\n        size += msg->msg_iov[i].iov_len;\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"sendmsg: %z of %uz\", n, size);\n#endif\n\n    return n;\n}\n"
  },
  {
    "path": "src/os/unix/ngx_user.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\n#if (NGX_CRYPT)\n\n#if (NGX_HAVE_GNU_CRYPT_R)\n\nngx_int_t\nngx_libc_crypt(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted)\n{\n    char               *value;\n    size_t              len;\n    struct crypt_data   cd;\n\n    cd.initialized = 0;\n\n    value = crypt_r((char *) key, (char *) salt, &cd);\n\n    if (value) {\n        len = ngx_strlen(value) + 1;\n\n        *encrypted = ngx_pnalloc(pool, len);\n        if (*encrypted == NULL) {\n            return NGX_ERROR;\n        }\n\n        ngx_memcpy(*encrypted, value, len);\n        return NGX_OK;\n    }\n\n    ngx_log_error(NGX_LOG_CRIT, pool->log, ngx_errno, \"crypt_r() failed\");\n\n    return NGX_ERROR;\n}\n\n#else\n\nngx_int_t\nngx_libc_crypt(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted)\n{\n    char       *value;\n    size_t      len;\n    ngx_err_t   err;\n\n    value = crypt((char *) key, (char *) salt);\n\n    if (value) {\n        len = ngx_strlen(value) + 1;\n\n        *encrypted = ngx_pnalloc(pool, len);\n        if (*encrypted == NULL) {\n            return NGX_ERROR;\n        }\n\n        ngx_memcpy(*encrypted, value, len);\n        return NGX_OK;\n    }\n\n    err = ngx_errno;\n\n    ngx_log_error(NGX_LOG_CRIT, pool->log, err, \"crypt() failed\");\n\n    return NGX_ERROR;\n}\n\n#endif\n\n#endif /* NGX_CRYPT */\n"
  },
  {
    "path": "src/os/unix/ngx_user.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_USER_H_INCLUDED_\n#define _NGX_USER_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\ntypedef uid_t  ngx_uid_t;\ntypedef gid_t  ngx_gid_t;\n\n\nngx_int_t ngx_libc_crypt(ngx_pool_t *pool, u_char *key, u_char *salt,\n    u_char **encrypted);\n\n\n#endif /* _NGX_USER_H_INCLUDED_ */\n"
  },
  {
    "path": "src/os/unix/ngx_writev_chain.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n\n\nngx_chain_t *\nngx_writev_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)\n{\n    ssize_t        n, sent;\n    off_t          send, prev_send;\n    ngx_chain_t   *cl;\n    ngx_event_t   *wev;\n    ngx_iovec_t    vec;\n    struct iovec   iovs[NGX_IOVS_PREALLOCATE];\n\n    wev = c->write;\n\n    if (!wev->ready) {\n        return in;\n    }\n\n#if (NGX_HAVE_KQUEUE)\n\n    if ((ngx_event_flags & NGX_USE_KQUEUE_EVENT) && wev->pending_eof) {\n        (void) ngx_connection_error(c, wev->kq_errno,\n                               \"kevent() reported about an closed connection\");\n        wev->error = 1;\n        return NGX_CHAIN_ERROR;\n    }\n\n#endif\n\n    /* the maximum limit size is the maximum size_t value - the page size */\n\n    if (limit == 0 || limit > (off_t) (NGX_MAX_SIZE_T_VALUE - ngx_pagesize)) {\n        limit = NGX_MAX_SIZE_T_VALUE - ngx_pagesize;\n    }\n\n    send = 0;\n\n    vec.iovs = iovs;\n    vec.nalloc = NGX_IOVS_PREALLOCATE;\n\n    for ( ;; ) {\n        prev_send = send;\n\n        /* create the iovec and coalesce the neighbouring bufs */\n\n        cl = ngx_output_chain_to_iovec(&vec, in, limit - send, c->log);\n\n        if (cl == NGX_CHAIN_ERROR) {\n            return NGX_CHAIN_ERROR;\n        }\n\n        if (cl && cl->buf->in_file) {\n            ngx_log_error(NGX_LOG_ALERT, c->log, 0,\n                          \"file buf in writev \"\n                          \"t:%d r:%d f:%d %p %p-%p %p %O-%O\",\n                          cl->buf->temporary,\n                          cl->buf->recycled,\n                          cl->buf->in_file,\n                          cl->buf->start,\n                          cl->buf->pos,\n                          cl->buf->last,\n                          cl->buf->file,\n                          cl->buf->file_pos,\n                          cl->buf->file_last);\n\n            ngx_debug_point();\n\n            return NGX_CHAIN_ERROR;\n        }\n\n        send += vec.size;\n\n        n = ngx_writev(c, &vec);\n\n        if (n == NGX_ERROR) {\n            return NGX_CHAIN_ERROR;\n        }\n\n        sent = (n == NGX_AGAIN) ? 0 : n;\n\n        c->sent += sent;\n\n        in = ngx_chain_update_sent(in, sent);\n\n        if (send - prev_send != sent) {\n            wev->ready = 0;\n            return in;\n        }\n\n        if (send >= limit || in == NULL) {\n            return in;\n        }\n    }\n}\n\n\nngx_chain_t *\nngx_output_chain_to_iovec(ngx_iovec_t *vec, ngx_chain_t *in, size_t limit,\n    ngx_log_t *log)\n{\n    size_t         total, size;\n    u_char        *prev;\n    ngx_uint_t     n;\n    struct iovec  *iov;\n\n    iov = NULL;\n    prev = NULL;\n    total = 0;\n    n = 0;\n\n    for ( /* void */ ; in && total < limit; in = in->next) {\n\n        if (ngx_buf_special(in->buf)) {\n            continue;\n        }\n\n        if (in->buf->in_file) {\n            break;\n        }\n\n        if (!ngx_buf_in_memory(in->buf)) {\n            ngx_log_error(NGX_LOG_ALERT, log, 0,\n                          \"bad buf in output chain \"\n                          \"t:%d r:%d f:%d %p %p-%p %p %O-%O\",\n                          in->buf->temporary,\n                          in->buf->recycled,\n                          in->buf->in_file,\n                          in->buf->start,\n                          in->buf->pos,\n                          in->buf->last,\n                          in->buf->file,\n                          in->buf->file_pos,\n                          in->buf->file_last);\n\n            ngx_debug_point();\n\n            return NGX_CHAIN_ERROR;\n        }\n\n        size = in->buf->last - in->buf->pos;\n\n        if (size > limit - total) {\n            size = limit - total;\n        }\n\n        if (prev == in->buf->pos) {\n            iov->iov_len += size;\n\n        } else {\n            if (n == vec->nalloc) {\n                break;\n            }\n\n            iov = &vec->iovs[n++];\n\n            iov->iov_base = (void *) in->buf->pos;\n            iov->iov_len = size;\n        }\n\n        prev = in->buf->pos + size;\n        total += size;\n    }\n\n    vec->count = n;\n    vec->size = total;\n\n    return in;\n}\n\n\nssize_t\nngx_writev(ngx_connection_t *c, ngx_iovec_t *vec)\n{\n    ssize_t    n;\n    ngx_err_t  err;\n\neintr:\n\n    n = writev(c->fd, vec->iovs, vec->count);\n\n    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                   \"writev: %z of %uz\", n, vec->size);\n\n    if (n == -1) {\n        err = ngx_errno;\n\n        switch (err) {\n        case NGX_EAGAIN:\n            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,\n                           \"writev() not ready\");\n            return NGX_AGAIN;\n\n        case NGX_EINTR:\n            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,\n                           \"writev() was interrupted\");\n            goto eintr;\n\n        default:\n            c->write->error = 1;\n            ngx_connection_error(c, err, \"writev() failed\");\n            return NGX_ERROR;\n        }\n    }\n\n    return n;\n}\n"
  },
  {
    "path": "src/proc/ngx_proc.c",
    "content": "/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_channel.h>\n#include <ngx_proc.h>\n\n\nstatic char *ngx_procs_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\nstatic void ngx_procs_cycle(ngx_cycle_t *cycle, void *data);\nstatic void ngx_procs_process_init(ngx_cycle_t *cycle,\n    ngx_proc_module_t *module, ngx_int_t priority);\nstatic void ngx_procs_channel_handler(ngx_event_t *ev);\nstatic void ngx_procs_process_exit(ngx_cycle_t *cycle,\n    ngx_proc_module_t *module);\nstatic void ngx_procs_pass_open_channel(ngx_cycle_t *cycle, ngx_channel_t *ch);\n\nstatic char *ngx_proc_process(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\nstatic void *ngx_proc_create_main_conf(ngx_conf_t *cf);\nstatic void *ngx_proc_create_conf(ngx_conf_t *cf);\nstatic char *ngx_proc_merge_conf(ngx_conf_t *cf, void *parent, void *child);\nstatic char *ngx_procs_set_priority(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n\n\nstatic ngx_command_t ngx_procs_commands[] = {\n\n    { ngx_string(\"processes\"),\n      NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,\n      ngx_procs_block,\n      0,\n      0,\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_core_module_t  ngx_procs_module_ctx = {\n    ngx_string(\"procs\"),\n    NULL,\n    NULL\n};\n\n\nngx_module_t  ngx_procs_module = {\n    NGX_MODULE_V1,\n    &ngx_procs_module_ctx,                 /* module context */\n    ngx_procs_commands,                    /* module directives */\n    NGX_CORE_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_command_t ngx_proc_core_commands[] = {\n\n    { ngx_string(\"process\"),\n      NGX_PROC_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE1,\n      ngx_proc_process,\n      0,\n      0,\n      NULL },\n\n    { ngx_string(\"count\"),\n      NGX_PROC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_num_slot,\n      NGX_PROC_CONF_OFFSET,\n      offsetof(ngx_proc_conf_t, count),\n      NULL },\n\n    { ngx_string(\"priority\"),\n      NGX_PROC_CONF|NGX_CONF_TAKE1,\n      ngx_procs_set_priority,\n      NGX_PROC_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"delay_start\"),\n      NGX_PROC_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_PROC_CONF_OFFSET,\n      offsetof(ngx_proc_conf_t, delay_start),\n      NULL },\n\n    { ngx_string(\"respawn\"),\n      NGX_PROC_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_PROC_CONF_OFFSET,\n      offsetof(ngx_proc_conf_t, respawn),\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_proc_module_t ngx_proc_core_module_ctx = {\n    ngx_string(\"proc_core\"),\n    ngx_proc_create_main_conf,\n    NULL,\n    ngx_proc_create_conf,\n    ngx_proc_merge_conf,\n    NULL,\n    NULL,\n    NULL,\n    NULL\n};\n\n\nngx_module_t  ngx_proc_core_module = {\n    NGX_MODULE_V1,\n    &ngx_proc_core_module_ctx,             /* module context */\n    ngx_proc_core_commands,                /* module directives */\n    NGX_PROC_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_uint_t       ngx_procs_max_module;\nstatic ngx_cycle_t      ngx_procs_exit_cycle;\nstatic ngx_log_t        ngx_procs_exit_log;\nstatic ngx_open_file_t  ngx_procs_exit_log_file;\n\n\nstatic char *\nngx_procs_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    char                 *rv;\n    ngx_uint_t            i, mi, p;\n    ngx_conf_t            pcf;\n    ngx_proc_conf_t     **cpcfp;\n    ngx_proc_module_t     *module;\n    ngx_proc_conf_ctx_t   *ctx;\n    ngx_proc_main_conf_t  *cmcf;\n\n    /* the procs context */\n    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_proc_conf_ctx_t));\n    if (ctx == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    *(ngx_proc_conf_ctx_t **) conf = ctx;\n\n    ngx_procs_max_module = 0;\n\n    for (i = 0; ngx_modules[i]; i++) {\n        if (ngx_modules[i]->type != NGX_PROC_MODULE) {\n            continue;\n        }\n\n        ngx_modules[i]->ctx_index = ngx_procs_max_module++;\n    }\n\n    ctx->main_conf = ngx_pcalloc(cf->pool,\n                                 ngx_procs_max_module * sizeof(void *));\n    if (ctx->main_conf == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    ctx->proc_conf = ngx_pcalloc(cf->pool,\n                                 sizeof(void *) * ngx_procs_max_module);\n    if (ctx->proc_conf == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    /* create the main_confs for all proc modules */\n\n    for (i = 0; ngx_modules[i]; i++) {\n        if (ngx_modules[i]->type != NGX_PROC_MODULE) {\n            continue;\n        }\n\n        module = ngx_modules[i]->ctx;\n        mi = ngx_modules[i]->ctx_index;\n\n        if (module->create_main_conf) {\n\n            ctx->main_conf[mi] = module->create_main_conf(cf);\n\n            if (ctx->main_conf[mi] == NULL) {\n                return NGX_CONF_ERROR;\n            }\n        }\n\n        if (module->create_proc_conf) {\n            ctx->proc_conf[mi] = module->create_proc_conf(cf);\n\n            if (ctx->proc_conf[mi] == NULL) {\n                return NGX_CONF_ERROR;\n            }\n        }\n    }\n\n\n    pcf = *cf;\n    cf->ctx = ctx;\n\n    /* parse inside the procs block */\n    cf->module_type = NGX_PROC_MODULE;\n    cf->cmd_type = NGX_PROC_MAIN_CONF;\n\n    rv = ngx_conf_parse(cf, NULL);\n\n    if (rv != NGX_CONF_OK) {\n        *cf = pcf;\n        return rv;\n    }\n\n    cmcf = ctx->main_conf[ngx_proc_core_module.ctx_index];\n    cpcfp = cmcf->processes.elts;\n\n    for (i = 0; ngx_modules[i]; i++) {\n\n        if (ngx_modules[i]->type != NGX_PROC_MODULE) {\n            continue;\n        }\n\n        module = ngx_modules[i]->ctx;\n        mi = ngx_modules[i]->ctx_index;\n\n        cf->ctx = ctx;\n\n        if (module->init_main_conf) {\n            rv = module->init_main_conf(cf,ctx->main_conf[mi]);\n            if (rv != NGX_CONF_OK) {\n                *cf = pcf;\n                return rv;\n            }\n        }\n\n        for (p = 0; p < cmcf->processes.nelts; p++) {\n\n            cf->ctx = cpcfp[p]->ctx;\n\n            if (ngx_strcmp(module->name.data, cpcfp[p]->name.data) == 0\n                || ngx_strcmp(module->name.data, \"proc_core\") == 0)\n            {\n                if (module->merge_proc_conf) {\n                    rv = module->merge_proc_conf(cf, ctx->proc_conf[mi],\n                                                 cpcfp[p]->ctx->proc_conf[mi]);\n\n                    if (rv != NGX_CONF_OK) {\n                        *cf = pcf;\n                        return rv;\n                    }\n\n                    /* copy child to parent, tricky */\n                    ctx->proc_conf[mi] = cpcfp[p]->ctx->proc_conf[mi];\n                }\n            }\n        }\n    }\n\n    *cf = pcf;\n\n    return NGX_CONF_OK;\n}\n\n\nngx_int_t\nngx_procs_start(ngx_cycle_t *cycle, ngx_int_t type)\n{\n    ngx_int_t              rc, respawn;\n    ngx_uint_t             i, p, n;\n    ngx_channel_t          ch;\n    ngx_proc_args_t      **args;\n    ngx_proc_conf_t      **cpcfp;\n    ngx_proc_module_t     *module;\n    ngx_proc_main_conf_t  *cmcf;\n\n    ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, \"start procs processes\");\n\n    if (ngx_get_conf(cycle->conf_ctx, ngx_procs_module) == NULL) {\n        return NGX_OK;\n    }\n\n    ch.command = NGX_CMD_OPEN_CHANNEL;\n    cmcf = ngx_proc_get_main_conf(cycle->conf_ctx, ngx_proc_core_module);\n\n    cpcfp = cmcf->processes.elts;\n    args = ngx_pcalloc(cycle->pool,\n                       sizeof(ngx_proc_args_t *) * cmcf->processes.nelts);\n    if (args == NULL) {\n        return NGX_ERROR;\n    }\n\n    for (p = 0; p< cmcf->processes.nelts; p++) {\n        args[p] = ngx_pcalloc(cycle->pool, sizeof(ngx_proc_args_t));\n        if (args[p] == NULL) {\n            return NGX_ERROR;\n        }\n    }\n\n    respawn = type ? NGX_PROCESS_JUST_RESPAWN : NGX_PROCESS_RESPAWN;\n\n    for (i = 0; ngx_modules[i]; i++) {\n\n        if (ngx_modules[i]->type != NGX_PROC_MODULE) {\n            continue;\n        }\n\n        module = ngx_modules[i]->ctx;\n\n        for (p = 0; p < cmcf->processes.nelts; p++) {\n            if (ngx_strcmp(cpcfp[p]->name.data, module->name.data) == 0) {\n\n                if (module->prepare) {\n                    rc = module->prepare(cycle);\n                    if (rc != NGX_OK) {\n                        break;\n                    }\n                }\n\n                if (type == 1) {\n                    if (cpcfp[p]->respawn) {\n                        respawn = NGX_PROCESS_JUST_RESPAWN;\n                    }\n                } else {\n                    if (cpcfp[p]->respawn) {\n                        respawn = NGX_PROCESS_RESPAWN;\n                    } else {\n                        respawn = NGX_PROCESS_NORESPAWN;\n                    }\n                }\n\n                /* processes count */\n                for (n = 0; n < cpcfp[p]->count; n++) {\n                    args[p]->module = ngx_modules[i];\n                    args[p]->proc_conf = cpcfp[p];\n\n                    ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0,\n                                  \"start process %V\", &cpcfp[p]->name);\n\n                    ngx_spawn_process(cycle, ngx_procs_cycle, args[p],\n                                      (char *) cpcfp[p]->name.data, respawn);\n\n                    ch.pid = ngx_processes[ngx_process_slot].pid;\n                    ch.slot = ngx_process_slot;\n                    ch.fd = ngx_processes[ngx_process_slot].channel[0];\n\n                    ngx_procs_pass_open_channel(cycle, &ch);\n                }\n            }\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_procs_cycle(ngx_cycle_t *cycle, void *data)\n{\n    ngx_int_t           rc;\n    ngx_uint_t          i;\n    ngx_module_t       *module;\n    ngx_proc_args_t    *args;\n    ngx_proc_conf_t    *cpcf;\n    ngx_connection_t   *c;\n    ngx_proc_module_t  *ctx;\n\n    args = data;\n    module = args->module;\n    cpcf = args->proc_conf;\n    ctx = module->ctx;\n    ngx_process = NGX_PROCESS_PROC;\n\n    ngx_setproctitle((char *) ctx->name.data);\n    ngx_msleep(cpcf->delay_start);\n\n    ngx_procs_process_init(cycle, ctx, cpcf->priority);\n    ngx_close_listening_sockets(cycle);\n    ngx_use_accept_mutex = 0;\n\n    for ( ;; ) {\n        if (ngx_exiting || ngx_quit) {\n            ngx_exiting = 1;\n            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0,\n                          \"process %V gracefully shutting down\", &ctx->name);\n            ngx_setproctitle(\"processes are shutting down\");\n\n            c = cycle->connections;\n\n            for (i = 0; i < cycle->connection_n; i++) {\n                if (c[i].fd != -1 && c[i].idle) {\n                    c[i].close = 1;\n                    c[i].read->handler(c[i].read);\n                }\n            }\n\n            ngx_procs_process_exit(cycle, ctx);\n        }\n\n        if (ngx_terminate) {\n            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, \"process %V exiting\",\n                          &ctx->name);\n\n            ngx_procs_process_exit(cycle, ctx);\n        }\n\n        if (ngx_reopen) {\n            ngx_reopen = 0;\n            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, \"reopening logs\");\n            ngx_reopen_files(cycle, -1);\n        }\n\n        if (ctx->loop) {\n            rc = ctx->loop(cycle);\n            if (rc != NGX_OK) {\n                break;\n            }\n        }\n\n        ngx_time_update();\n\n        ngx_process_events_and_timers(cycle);\n    }\n\n    ngx_procs_process_exit(cycle, ctx);\n}\n\n\nstatic void\nngx_procs_process_init(ngx_cycle_t *cycle, ngx_proc_module_t *module,\n    ngx_int_t priority)\n{\n    sigset_t          set;\n    ngx_int_t         n;\n    ngx_uint_t        i;\n    struct rlimit     rlmt;\n    ngx_core_conf_t  *ccf;\n    ngx_listening_t  *ls;\n\n    if (ngx_set_environment(cycle, NULL) == NULL) {\n        /* fatal */\n        exit(2);\n    }\n\n    ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);\n\n    if (priority != 0) {\n        if (setpriority(PRIO_PROCESS, 0, (int) priority) == -1) {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                          \"process %V setpriority(%i) failed\", &module->name,\n                          priority);\n        }\n    }\n\n    if (ccf->rlimit_nofile != NGX_CONF_UNSET) {\n        rlmt.rlim_cur = (rlim_t) ccf->rlimit_nofile;\n        rlmt.rlim_max = (rlim_t) ccf->rlimit_nofile;\n\n        if (setrlimit(RLIMIT_NOFILE, &rlmt) == -1) {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                          \"process %V setrlimit(RLIMIT_NOFILE, %i) failed\",\n                          &module->name, ccf->rlimit_nofile);\n        }\n    }\n\n    if (ccf->rlimit_core != NGX_CONF_UNSET) {\n        rlmt.rlim_cur = (rlim_t) ccf->rlimit_core;\n        rlmt.rlim_max = (rlim_t) ccf->rlimit_core;\n\n        if (setrlimit(RLIMIT_CORE, &rlmt) == -1) {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                          \"process %V setrlimit(RLIMIT_CORE, %O) failed\",\n                          &module->name, ccf->rlimit_core);\n        }\n    }\n\n    if (geteuid() == 0) {\n        if (setgid(ccf->group) == -1) {\n            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                          \"process %V setgid(%d) failed\", &module->name,\n                          ccf->group);\n            /* fatal */\n            exit(2);\n        }\n\n        if (initgroups(ccf->username, ccf->group) == -1) {\n            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                          \"process %V initgroups(%s, %d) failed\", &module->name,\n                          ccf->username, ccf->group);\n        }\n\n        if (setuid(ccf->user) == -1) {\n            ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,\n                          \"process %V setuid(%d) failed\", &module->name,\n                          ccf->user);\n            /* fatal */\n            exit(2);\n        }\n    }\n\n#if (NGX_HAVE_PR_SET_DUMPABLE)\n\n    /* allow coredump after setuid() in Linux 2.4.x */\n\n    if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                      \"process %V prctl(PR_SET_DUMPABLE) failed\",\n                      &module->name);\n    }\n\n#endif\n\n    if (ccf->working_directory.len) {\n        if (chdir((char *) ccf->working_directory.data) == -1) {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                          \"process %V chdir(\\\"%s\\\") failed\", &module->name,\n                          ccf->working_directory.data);\n            /* fatal */\n            exit(2);\n        }\n    }\n\n    sigemptyset(&set);\n\n    if (sigprocmask(SIG_SETMASK, &set, NULL) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                      \"process %V sigprocmask() failed\", &module->name);\n    }\n\n    /*\n     * disable deleting previous events for the listening sockets because\n     * in the worker processes there are no events at all at this point\n     */\n    ls = cycle->listening.elts;\n\n    for (i = 0; i < cycle->listening.nelts; i++) {\n        ls[i].previous = NULL;\n    }\n\n    if (ngx_event_core_module.init_process(cycle) != NGX_OK) {\n        ngx_log_error(NGX_LOG_ERR, cycle->log, 0,\n                      \"process %V init event error\", &module->name);\n        exit(2);\n    }\n\n    if (module->init) {\n        if (module->init(cycle) != NGX_OK) {\n            ngx_log_error(NGX_LOG_ERR, cycle->log, 0,\n                          \"process %V process init error\", &module->name);\n            exit(2);\n        }\n    }\n\n\n    for (n = 0; n < ngx_last_process; n++) {\n\n        if (ngx_processes[n].pid == -1) {\n            continue;\n        }\n\n        if (n == ngx_process_slot) {\n            continue;\n        }\n\n        if (ngx_processes[n].channel[1] == -1) {\n            continue;\n        }\n\n        if (close(ngx_processes[n].channel[1]) == -1) {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                          \"process %V close() channel failed\", &module->name);\n        }\n    }\n\n    if (close(ngx_processes[ngx_process_slot].channel[0]) == -1) {\n        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,\n                      \"process %V close() channel failed\", &module->name);\n    }\n\n#if 0\n    ngx_last_process = 0;\n#endif\n\n    if (ngx_add_channel_event(cycle, ngx_channel, NGX_READ_EVENT,\n                              ngx_procs_channel_handler)\n        == NGX_ERROR)\n    {\n        /* fatal */\n        exit(2);\n    }\n}\n\n\nstatic void\nngx_procs_channel_handler(ngx_event_t *ev)\n{\n    ngx_int_t          n;\n    ngx_channel_t      ch;\n    ngx_connection_t  *c;\n\n    if (ev->timedout) {\n        ev->timedout = 0;\n        return;\n    }\n\n    c = ev->data;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_CORE, ev->log, 0, \"process channel handler\");\n\n    for ( ;; ) {\n\n        n = ngx_read_channel(c->fd, &ch, sizeof(ngx_channel_t), ev->log);\n\n        ngx_log_debug1(NGX_LOG_DEBUG_CORE, ev->log, 0,\n                       \"process channel: %i\", n);\n\n        if (n == NGX_ERROR) {\n\n            if (ngx_event_flags & NGX_USE_EPOLL_EVENT) {\n                ngx_del_conn(c, 0);\n            }\n\n            ngx_close_connection(c);\n            return;\n        }\n\n        if (ngx_event_flags & NGX_USE_EVENTPORT_EVENT) {\n            if (ngx_add_event(ev, NGX_READ_EVENT, 0) == NGX_ERROR) {\n                return;\n            }\n        }\n\n        if (n == NGX_AGAIN) {\n            return;\n        }\n\n        ngx_log_debug1(NGX_LOG_DEBUG_CORE, ev->log, 0,\n                       \"process channel command: %ui\", ch.command);\n\n        switch (ch.command) {\n\n        case NGX_CMD_QUIT:\n            ngx_quit = 1;\n            break;\n\n        case NGX_CMD_TERMINATE:\n            ngx_terminate = 1;\n            break;\n\n        case NGX_CMD_REOPEN:\n            ngx_reopen = 1;\n            break;\n\n        case NGX_CMD_OPEN_CHANNEL:\n\n            ngx_log_debug3(NGX_LOG_DEBUG_CORE, ev->log, 0,\n                           \"process got channel s:%i pid:%P fd:%d\",\n                           ch.slot, ch.pid, ch.fd);\n\n            ngx_processes[ch.slot].pid = ch.pid;\n            ngx_processes[ch.slot].channel[0] = ch.fd;\n            break;\n\n        case NGX_CMD_CLOSE_CHANNEL:\n\n            ngx_log_debug4(NGX_LOG_DEBUG_CORE, ev->log, 0,\n                           \"process closed channel s:%i pid:%P our:%P fd:%d\",\n                           ch.slot, ch.pid, ngx_processes[ch.slot].pid,\n                           ngx_processes[ch.slot].channel[0]);\n\n            if (close(ngx_processes[ch.slot].channel[0]) == -1) {\n                ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,\n                              \"process close() channel failed\");\n            }\n\n            ngx_processes[ch.slot].channel[0] = -1;\n            break;\n#if (T_PIPES)\n        case NGX_CMD_PIPE_BROKEN:\n            ngx_pipe_broken_action(ev->log, ch.pid, 0);\n            break;\n#endif\n        }\n    }\n}\n\n\nstatic void\nngx_procs_process_exit(ngx_cycle_t *cycle, ngx_proc_module_t *module)\n{\n    ngx_uint_t         i;\n    ngx_connection_t  *c;\n\n    if (module->exit) {\n        module->exit(cycle);\n    }\n\n    if (ngx_exiting) {\n        c = cycle->connections;\n        for (i = 0; i < cycle->connection_n; i++) {\n            if (c[i].fd != -1\n                && c[i].read\n                && !c[i].read->accept\n                && !c[i].read->channel\n                && !c[i].read->resolver)\n            {\n                ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,\n                              \"open socket #%d left in connection %ui\",\n                              c[i].fd, i);\n                ngx_debug_quit = 1;\n            }\n        }\n\n        if (ngx_debug_quit) {\n            ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, \"aborting\");\n            ngx_debug_point();\n        }\n    }\n\n    /*\n     * Copy ngx_cycle->log related data to the special static exit cycle,\n     * log, and log file structures enough to allow a signal handler to log.\n     * The handler may be called when standard ngx_cycle->log allocated from\n     * ngx_cycle->pool is already destroyed.\n     */\n\n    ngx_procs_exit_log_file.fd = ngx_cycle->log->file->fd;\n\n    ngx_procs_exit_log = *ngx_cycle->log;\n    ngx_procs_exit_log.file = &ngx_procs_exit_log_file;\n\n    ngx_procs_exit_cycle.log = &ngx_procs_exit_log;\n    ngx_cycle = &ngx_procs_exit_cycle;\n\n    ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0, \"process %V exit\",\n                  &module->name);\n\n    ngx_destroy_pool(cycle->pool);\n\n    exit(0);\n}\n\n\nstatic void\nngx_procs_pass_open_channel(ngx_cycle_t *cycle, ngx_channel_t *ch)\n{\n    ngx_int_t  i;\n\n    for (i = 0; i < ngx_last_process; i++) {\n\n        if (i == ngx_process_slot\n            || ngx_processes[i].pid == -1\n            || ngx_processes[i].channel[0] == -1)\n        {\n            continue;\n        }\n\n        ngx_log_debug6(NGX_LOG_DEBUG_CORE, cycle->log, 0,\n            \"process passed channel s:%d pid:%P fd:%d to s:%i pid:%P fd:%d\",\n             ch->slot, ch->pid, ch->fd, i, ngx_processes[i].pid,\n             ngx_processes[i].channel[0]);\n\n        /* TODO: NGX_AGAIN */\n\n        ngx_write_channel(ngx_processes[i].channel[0],\n                          ch, sizeof(ngx_channel_t), cycle->log);\n    }\n}\n\n\nstatic char *\nngx_proc_process(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    char                  *rv;\n    void                  *mconf;\n    ngx_int_t              i;\n    ngx_str_t             *value;\n    ngx_flag_t             flag;\n    ngx_conf_t             pcf;\n    ngx_uint_t             m;\n    ngx_proc_conf_t       *cpcf, **cpcfp;\n    ngx_proc_module_t     *module;\n    ngx_proc_conf_ctx_t   *ctx, *procs_ctx;\n    ngx_proc_main_conf_t  *cmcf;\n\n    value = cf->args->elts;\n    flag = 0;\n\n    for (m = 0; ngx_modules[m]; m++) {\n        if (ngx_modules[m]->type != NGX_PROC_MODULE) {\n            continue;\n        }\n        module = ngx_modules[m]->ctx;\n\n        if (ngx_strcmp(module->name.data, value[1].data) == 0) {\n            flag = 1;\n            break;\n        }\n    }\n\n    if (flag == 0) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"no %V process module\", &value[1]);\n        return NGX_CONF_ERROR;\n    }\n\n    /* new conf ctx */\n    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_proc_conf_ctx_t));\n    if (ctx == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    procs_ctx = cf->ctx;\n    ctx->main_conf = procs_ctx->main_conf; /* old main conf */\n\n    /* the processes{}'s proc_conf */\n\n    ctx->proc_conf = ngx_pcalloc(cf->pool,\n                                 sizeof(void *) * ngx_procs_max_module);\n    if (ctx->proc_conf == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    for (m = 0; ngx_modules[m]; m++) {\n        if (ngx_modules[m]->type != NGX_PROC_MODULE) {\n            continue;\n        }\n\n        module = ngx_modules[m]->ctx;\n\n        if (module->create_proc_conf) {\n            mconf = module->create_proc_conf(cf);\n\n            if (mconf == NULL) {\n                return NGX_CONF_ERROR;\n            }\n\n            /* new proc conf */\n            ctx->proc_conf[ngx_modules[m]->ctx_index] = mconf;\n        }\n    }\n\n    /* the proc configuration context */\n\n    cpcf = ctx->proc_conf[ngx_proc_core_module.ctx_index];\n    cpcf->ctx = ctx;\n    cpcf->name = value[1];\n\n    cmcf = ctx->main_conf[ngx_proc_core_module.ctx_index];\n\n    cpcfp = ngx_array_push(&cmcf->processes);\n    if (cpcfp == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    *cpcfp = cpcf;\n\n    /* check process conf repeat */\n    cpcfp = cmcf->processes.elts;\n    for (i = cmcf->processes.nelts - 2; i >= 0 ; i--) {\n        if (ngx_strcmp(cpcfp[i]->name.data, cpcf->name.data) == 0) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, \"process repeat\");\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    /* parse inside process{} */\n\n    pcf = *cf;\n    cf->ctx = ctx;\n    cf->cmd_type = NGX_PROC_CONF;\n\n    rv = ngx_conf_parse(cf, NULL);\n\n    *cf = pcf;\n\n    return rv;\n}\n\n\nstatic void *\nngx_proc_create_main_conf(ngx_conf_t *cf)\n{\n    ngx_proc_main_conf_t  *cmcf;\n\n    cmcf = ngx_pcalloc(cf->pool, sizeof(ngx_proc_main_conf_t));\n    if (cmcf == NULL) {\n        return NULL;\n    }\n\n    if (ngx_array_init(&cmcf->processes, cf->pool, 4, sizeof(ngx_proc_conf_t *))\n        != NGX_OK)\n    {\n        return NULL;\n    }\n\n    return cmcf;\n}\n\n\nstatic void *\nngx_proc_create_conf(ngx_conf_t *cf)\n{\n    ngx_proc_conf_t  *cpcf;\n\n    cpcf = ngx_pcalloc(cf->pool, sizeof(ngx_proc_conf_t));\n    if (cpcf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc()\n     *\n     *     cpcf->delay_start = 0;\n     *     cpcf->priority = 0;\n     *     cpcf->count = 0;\n     *     cpcf->respawn = 0;\n     */\n\n    cpcf->delay_start = NGX_CONF_UNSET_MSEC;\n    cpcf->count = NGX_CONF_UNSET_UINT;\n    cpcf->respawn = NGX_CONF_UNSET;\n\n    return cpcf;\n}\n\n\nstatic char *\nngx_proc_merge_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_proc_conf_t  *prev = parent;\n    ngx_proc_conf_t  *conf = child;\n\n    ngx_conf_merge_msec_value(conf->delay_start, prev->delay_start, 300);\n    ngx_conf_merge_uint_value(conf->count, prev->count, 1);\n    ngx_conf_merge_value(conf->respawn, prev->respawn, 1);\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_procs_set_priority(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_proc_conf_t  *pcf = conf;\n\n    ngx_str_t        *value;\n    ngx_uint_t        n, minus;\n\n    if (pcf->priority != 0) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    if (value[1].data[0] == '-') {\n        n = 1;\n        minus = 1;\n\n    } else if (value[1].data[0] == '+') {\n        n = 1;\n        minus = 0;\n\n    } else {\n        n = 0;\n        minus = 0;\n    }\n\n    pcf->priority = ngx_atoi(&value[1].data[n], value[1].len - n);\n    if (pcf->priority == NGX_ERROR) {\n        return \"invalid number\";\n    }\n\n    if (minus) {\n        pcf->priority = -pcf->priority;\n    }\n\n    return NGX_CONF_OK;\n}\n"
  },
  {
    "path": "src/proc/ngx_proc.h",
    "content": "\n/*\n * Copyright (C) 2010-2015 Alibaba Group Holding Limited\n */\n\n#ifndef _NGX_PROC_H_INCLUDED_\n#define _NGX_PROC_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n\n#define NGX_PROC_MODULE            0x434f5250  /* \"PROC\" */\n#define NGX_PROC_MAIN_CONF         0x02000000\n#define NGX_PROC_CONF              0x04000000\n\n\n#define NGX_PROC_MAIN_CONF_OFFSET  offsetof(ngx_proc_conf_ctx_t, main_conf)\n#define NGX_PROC_CONF_OFFSET       offsetof(ngx_proc_conf_ctx_t, proc_conf)\n\n\ntypedef struct {\n    void                         **main_conf;\n    void                         **proc_conf;\n} ngx_proc_conf_ctx_t;\n\n\ntypedef struct {\n    ngx_str_t                      name;\n\n    ngx_int_t                      priority;\n    ngx_msec_t                     delay_start;\n    ngx_uint_t                     count;\n    ngx_flag_t                     respawn;\n\n    ngx_proc_conf_ctx_t           *ctx;\n} ngx_proc_conf_t;\n\n\ntypedef struct {\n    ngx_array_t                    processes; /* ngx_proc_conf_t */\n} ngx_proc_main_conf_t;\n\n\ntypedef struct ngx_proc_args_s {\n    ngx_module_t                  *module;\n    ngx_proc_conf_t               *proc_conf;\n} ngx_proc_args_t;\n\n\ntypedef struct {\n    ngx_str_t                      name;\n    void                        *(*create_main_conf)(ngx_conf_t *cf);\n    char                        *(*init_main_conf)(ngx_conf_t *cf, void *conf);\n    void                        *(*create_proc_conf)(ngx_conf_t *cf);\n    char                        *(*merge_proc_conf)(ngx_conf_t *cf,\n                                                    void *parent, void *child);\n\n    ngx_int_t                    (*prepare)(ngx_cycle_t *cycle);\n    ngx_int_t                    (*init)(ngx_cycle_t *cycle);\n    ngx_int_t                    (*loop)(ngx_cycle_t *cycle);\n    void                         (*exit)(ngx_cycle_t *cycle);\n} ngx_proc_module_t;\n\n\n#define ngx_proc_get_main_conf(conf_ctx, module)           \\\n    ((ngx_get_conf(conf_ctx, ngx_procs_module)) ?          \\\n        ((ngx_proc_conf_ctx_t *) (ngx_get_conf(conf_ctx,   \\\n              ngx_procs_module)))->main_conf[module.ctx_index] : NULL)\n\n\n#define ngx_proc_get_conf(conf_ctx, module)                \\\n    ((ngx_get_conf(conf_ctx, ngx_procs_module)) ?          \\\n        ((ngx_proc_conf_ctx_t *) (ngx_get_conf(conf_ctx,   \\\n              ngx_procs_module)))->proc_conf[module.ctx_index] : NULL)\n\n\nngx_int_t ngx_procs_start(ngx_cycle_t *cycle, ngx_int_t type);\n\n\nextern ngx_module_t  ngx_procs_module;\nextern ngx_module_t  ngx_proc_core_module;\n\n\n#endif /* _NGX_PROC_H_INCLUDED_ */\n"
  },
  {
    "path": "src/stream/ngx_stream.c",
    "content": "\n/*\n * Copyright (C) Roman Arutyunyan\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n#include <ngx_stream.h>\n\n\nstatic char *ngx_stream_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\nstatic ngx_int_t ngx_stream_init_phases(ngx_conf_t *cf,\n    ngx_stream_core_main_conf_t *cmcf);\nstatic ngx_int_t ngx_stream_init_phase_handlers(ngx_conf_t *cf,\n    ngx_stream_core_main_conf_t *cmcf);\nstatic ngx_int_t ngx_stream_add_ports(ngx_conf_t *cf, ngx_array_t *ports,\n    ngx_stream_listen_t *listen);\nstatic char *ngx_stream_optimize_servers(ngx_conf_t *cf, ngx_array_t *ports);\nstatic ngx_int_t ngx_stream_add_addrs(ngx_conf_t *cf, ngx_stream_port_t *stport,\n    ngx_stream_conf_addr_t *addr);\n#if (NGX_HAVE_INET6)\nstatic ngx_int_t ngx_stream_add_addrs6(ngx_conf_t *cf,\n    ngx_stream_port_t *stport, ngx_stream_conf_addr_t *addr);\n#endif\nstatic ngx_int_t ngx_stream_cmp_conf_addrs(const void *one, const void *two);\n\n\nngx_uint_t  ngx_stream_max_module;\n\n\nngx_stream_filter_pt  ngx_stream_top_filter;\n\n\nstatic ngx_command_t  ngx_stream_commands[] = {\n\n    { ngx_string(\"stream\"),\n      NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,\n      ngx_stream_block,\n      0,\n      0,\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_core_module_t  ngx_stream_module_ctx = {\n    ngx_string(\"stream\"),\n    NULL,\n    NULL\n};\n\n\nngx_module_t  ngx_stream_module = {\n    NGX_MODULE_V1,\n    &ngx_stream_module_ctx,                /* module context */\n    ngx_stream_commands,                   /* module directives */\n    NGX_CORE_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 char *\nngx_stream_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    char                          *rv;\n    ngx_uint_t                     i, m, mi, s;\n    ngx_conf_t                     pcf;\n    ngx_array_t                    ports;\n    ngx_stream_listen_t           *listen;\n    ngx_stream_module_t           *module;\n    ngx_stream_conf_ctx_t         *ctx;\n    ngx_stream_core_srv_conf_t   **cscfp;\n    ngx_stream_core_main_conf_t   *cmcf;\n\n    if (*(ngx_stream_conf_ctx_t **) conf) {\n        return \"is duplicate\";\n    }\n\n    /* the main stream context */\n\n    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_stream_conf_ctx_t));\n    if (ctx == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    *(ngx_stream_conf_ctx_t **) conf = ctx;\n\n    /* count the number of the stream modules and set up their indices */\n\n    ngx_stream_max_module = ngx_count_modules(cf->cycle, NGX_STREAM_MODULE);\n\n\n    /* the stream main_conf context, it's the same in the all stream contexts */\n\n    ctx->main_conf = ngx_pcalloc(cf->pool,\n                                 sizeof(void *) * ngx_stream_max_module);\n    if (ctx->main_conf == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n\n    /*\n     * the stream null srv_conf context, it is used to merge\n     * the server{}s' srv_conf's\n     */\n\n    ctx->srv_conf = ngx_pcalloc(cf->pool,\n                                sizeof(void *) * ngx_stream_max_module);\n    if (ctx->srv_conf == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n\n    /*\n     * create the main_conf's and the null srv_conf's of the all stream modules\n     */\n\n    for (m = 0; cf->cycle->modules[m]; m++) {\n        if (cf->cycle->modules[m]->type != NGX_STREAM_MODULE) {\n            continue;\n        }\n\n        module = cf->cycle->modules[m]->ctx;\n        mi = cf->cycle->modules[m]->ctx_index;\n\n        if (module->create_main_conf) {\n            ctx->main_conf[mi] = module->create_main_conf(cf);\n            if (ctx->main_conf[mi] == NULL) {\n                return NGX_CONF_ERROR;\n            }\n        }\n\n        if (module->create_srv_conf) {\n            ctx->srv_conf[mi] = module->create_srv_conf(cf);\n            if (ctx->srv_conf[mi] == NULL) {\n                return NGX_CONF_ERROR;\n            }\n        }\n    }\n\n\n    pcf = *cf;\n    cf->ctx = ctx;\n\n    for (m = 0; cf->cycle->modules[m]; m++) {\n        if (cf->cycle->modules[m]->type != NGX_STREAM_MODULE) {\n            continue;\n        }\n\n        module = cf->cycle->modules[m]->ctx;\n\n        if (module->preconfiguration) {\n            if (module->preconfiguration(cf) != NGX_OK) {\n                return NGX_CONF_ERROR;\n            }\n        }\n    }\n\n\n    /* parse inside the stream{} block */\n\n    cf->module_type = NGX_STREAM_MODULE;\n    cf->cmd_type = NGX_STREAM_MAIN_CONF;\n    rv = ngx_conf_parse(cf, NULL);\n\n    if (rv != NGX_CONF_OK) {\n        *cf = pcf;\n        return rv;\n    }\n\n\n    /* init stream{} main_conf's, merge the server{}s' srv_conf's */\n\n    cmcf = ctx->main_conf[ngx_stream_core_module.ctx_index];\n    cscfp = cmcf->servers.elts;\n\n    for (m = 0; cf->cycle->modules[m]; m++) {\n        if (cf->cycle->modules[m]->type != NGX_STREAM_MODULE) {\n            continue;\n        }\n\n        module = cf->cycle->modules[m]->ctx;\n        mi = cf->cycle->modules[m]->ctx_index;\n\n        /* init stream{} main_conf's */\n\n        cf->ctx = ctx;\n\n        if (module->init_main_conf) {\n            rv = module->init_main_conf(cf, ctx->main_conf[mi]);\n            if (rv != NGX_CONF_OK) {\n                *cf = pcf;\n                return rv;\n            }\n        }\n\n        for (s = 0; s < cmcf->servers.nelts; s++) {\n\n            /* merge the server{}s' srv_conf's */\n\n            cf->ctx = cscfp[s]->ctx;\n\n            if (module->merge_srv_conf) {\n                rv = module->merge_srv_conf(cf,\n                                            ctx->srv_conf[mi],\n                                            cscfp[s]->ctx->srv_conf[mi]);\n                if (rv != NGX_CONF_OK) {\n                    *cf = pcf;\n                    return rv;\n                }\n            }\n        }\n    }\n\n    if (ngx_stream_init_phases(cf, cmcf) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    for (m = 0; cf->cycle->modules[m]; m++) {\n        if (cf->cycle->modules[m]->type != NGX_STREAM_MODULE) {\n            continue;\n        }\n\n        module = cf->cycle->modules[m]->ctx;\n\n        if (module->postconfiguration) {\n            if (module->postconfiguration(cf) != NGX_OK) {\n                return NGX_CONF_ERROR;\n            }\n        }\n    }\n\n    if (ngx_stream_variables_init_vars(cf) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    *cf = pcf;\n\n    if (ngx_stream_init_phase_handlers(cf, cmcf) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (ngx_array_init(&ports, cf->temp_pool, 4, sizeof(ngx_stream_conf_port_t))\n        != NGX_OK)\n    {\n        return NGX_CONF_ERROR;\n    }\n\n    listen = cmcf->listen.elts;\n\n    for (i = 0; i < cmcf->listen.nelts; i++) {\n        if (ngx_stream_add_ports(cf, &ports, &listen[i]) != NGX_OK) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    return ngx_stream_optimize_servers(cf, &ports);\n}\n\n\nstatic ngx_int_t\nngx_stream_init_phases(ngx_conf_t *cf, ngx_stream_core_main_conf_t *cmcf)\n{\n    if (ngx_array_init(&cmcf->phases[NGX_STREAM_POST_ACCEPT_PHASE].handlers,\n                       cf->pool, 1, sizeof(ngx_stream_handler_pt))\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    if (ngx_array_init(&cmcf->phases[NGX_STREAM_PREACCESS_PHASE].handlers,\n                       cf->pool, 1, sizeof(ngx_stream_handler_pt))\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    if (ngx_array_init(&cmcf->phases[NGX_STREAM_ACCESS_PHASE].handlers,\n                       cf->pool, 1, sizeof(ngx_stream_handler_pt))\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    if (ngx_array_init(&cmcf->phases[NGX_STREAM_SSL_PHASE].handlers,\n                       cf->pool, 1, sizeof(ngx_stream_handler_pt))\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    if (ngx_array_init(&cmcf->phases[NGX_STREAM_PREREAD_PHASE].handlers,\n                       cf->pool, 1, sizeof(ngx_stream_handler_pt))\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    if (ngx_array_init(&cmcf->phases[NGX_STREAM_LOG_PHASE].handlers,\n                       cf->pool, 1, sizeof(ngx_stream_handler_pt))\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n#if (T_NGX_STREAM_SNI)\n\nstatic int ngx_libc_cdecl\nngx_stream_cmp_dns_wildcards(const void *one, const void *two)\n{\n    ngx_hash_key_t  *first, *second;\n\n    first = (ngx_hash_key_t *) one;\n    second = (ngx_hash_key_t *) two;\n\n    return ngx_dns_strcmp(first->key.data, second->key.data);\n}\n\n\nstatic ngx_int_t\nngx_stream_server_names(ngx_conf_t *cf, ngx_stream_core_main_conf_t *cmcf,\n    ngx_stream_conf_addr_t *addr)\n{\n    ngx_int_t                   rc;\n    ngx_uint_t                  n, s;\n    ngx_hash_init_t             hash;\n    ngx_hash_keys_arrays_t      ha;\n    ngx_stream_server_name_t   *name;\n    ngx_stream_core_srv_conf_t **cscfp;\n\n    ngx_memzero(&ha, sizeof(ngx_hash_keys_arrays_t));\n\n    ha.temp_pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, cf->log);\n    if (ha.temp_pool == NULL) {\n        return NGX_ERROR;\n    }\n\n    ha.pool = cf->pool;\n\n    if (ngx_hash_keys_array_init(&ha, NGX_HASH_LARGE) != NGX_OK) {\n        goto failed;\n    }\n\n    cscfp = addr->servers.elts;\n\n    for (s = 0; s < addr->servers.nelts; s++) {\n\n        name = cscfp[s]->server_names.elts;\n\n        for (n = 0; n < cscfp[s]->server_names.nelts; n++) {\n\n            rc = ngx_hash_add_key(&ha, &name[n].name, name[n].server,\n                                  NGX_HASH_WILDCARD_KEY);\n\n            if (rc == NGX_ERROR) {\n                return NGX_ERROR;\n            }\n\n            if (rc == NGX_DECLINED) {\n                ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                              \"invalid server name or wildcard \\\"%V\\\"\",\n                              &name[n].name);\n                return NGX_ERROR;\n            }\n\n            if (rc == NGX_BUSY) {\n                ngx_log_error(NGX_LOG_WARN, cf->log, 0,\n                              \"conflicting server name \\\"%V\\\" ignored\",\n                              &name[n].name);\n            }\n        }\n    }\n\n    hash.key = ngx_hash_key_lc;\n    hash.max_size = cmcf->server_names_hash_max_size;\n    hash.bucket_size = cmcf->server_names_hash_bucket_size;\n    hash.name = \"server_names_hash\";\n    hash.pool = cf->pool;\n\n    if (ha.keys.nelts) {\n        hash.hash = &addr->hash;\n        hash.temp_pool = NULL;\n\n        if (ngx_hash_init(&hash, ha.keys.elts, ha.keys.nelts) != NGX_OK) {\n            goto failed;\n        }\n    }\n\n    if (ha.dns_wc_head.nelts) {\n\n        ngx_qsort(ha.dns_wc_head.elts, (size_t) ha.dns_wc_head.nelts,\n                  sizeof(ngx_hash_key_t), ngx_stream_cmp_dns_wildcards);\n\n        hash.hash = NULL;\n        hash.temp_pool = ha.temp_pool;\n\n        if (ngx_hash_wildcard_init(&hash, ha.dns_wc_head.elts,\n                                   ha.dns_wc_head.nelts)\n            != NGX_OK)\n        {\n            goto failed;\n        }\n\n        addr->wc_head = (ngx_hash_wildcard_t *) hash.hash;\n    }\n\n    if (ha.dns_wc_tail.nelts) {\n\n        ngx_qsort(ha.dns_wc_tail.elts, (size_t) ha.dns_wc_tail.nelts,\n                  sizeof(ngx_hash_key_t), ngx_stream_cmp_dns_wildcards);\n\n        hash.hash = NULL;\n        hash.temp_pool = ha.temp_pool;\n\n        if (ngx_hash_wildcard_init(&hash, ha.dns_wc_tail.elts,\n                                   ha.dns_wc_tail.nelts)\n            != NGX_OK)\n        {\n            goto failed;\n        }\n\n        addr->wc_tail = (ngx_hash_wildcard_t *) hash.hash;\n    }\n\n    ngx_destroy_pool(ha.temp_pool);\n\n    return NGX_OK;\n\nfailed:\n\n    ngx_destroy_pool(ha.temp_pool);\n\n    return NGX_ERROR;\n}\n\n/* add the server core module configuration to the address:port */\n\nstatic ngx_int_t\nngx_stream_add_server(ngx_conf_t *cf, ngx_stream_core_srv_conf_t *cscf,\n    ngx_stream_conf_addr_t *addr)\n{\n    ngx_uint_t                  i;\n    ngx_stream_core_srv_conf_t  **server;\n\n    if (addr->servers.elts == NULL) {\n        if (ngx_array_init(&addr->servers, cf->temp_pool, 4,\n                           sizeof(ngx_stream_core_srv_conf_t *))\n            != NGX_OK)\n        {\n            return NGX_ERROR;\n        }\n\n    } else {\n        server = addr->servers.elts;\n        for (i = 0; i < addr->servers.nelts; i++) {\n            if (server[i] == cscf) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"a duplicate listen\");\n                return NGX_ERROR;\n            }\n        }\n    }\n\n    server = ngx_array_push(&addr->servers);\n    if (server == NULL) {\n        return NGX_ERROR;\n    }\n\n    *server = cscf;\n\n    return NGX_OK;\n}\n#endif\n\n\nstatic ngx_int_t\nngx_stream_init_phase_handlers(ngx_conf_t *cf,\n    ngx_stream_core_main_conf_t *cmcf)\n{\n    ngx_int_t                     j;\n    ngx_uint_t                    i, n;\n    ngx_stream_handler_pt        *h;\n    ngx_stream_phase_handler_t   *ph;\n    ngx_stream_phase_handler_pt   checker;\n\n    n = 1 /* content phase */;\n\n    for (i = 0; i < NGX_STREAM_LOG_PHASE; i++) {\n        n += cmcf->phases[i].handlers.nelts;\n    }\n\n    ph = ngx_pcalloc(cf->pool,\n                     n * sizeof(ngx_stream_phase_handler_t) + sizeof(void *));\n    if (ph == NULL) {\n        return NGX_ERROR;\n    }\n\n    cmcf->phase_engine.handlers = ph;\n    n = 0;\n\n    for (i = 0; i < NGX_STREAM_LOG_PHASE; i++) {\n        h = cmcf->phases[i].handlers.elts;\n\n        switch (i) {\n\n        case NGX_STREAM_PREREAD_PHASE:\n            checker = ngx_stream_core_preread_phase;\n            break;\n\n        case NGX_STREAM_CONTENT_PHASE:\n            ph->checker = ngx_stream_core_content_phase;\n            n++;\n            ph++;\n\n            continue;\n\n        default:\n            checker = ngx_stream_core_generic_phase;\n        }\n\n        n += cmcf->phases[i].handlers.nelts;\n\n        for (j = cmcf->phases[i].handlers.nelts - 1; j >= 0; j--) {\n            ph->checker = checker;\n            ph->handler = h[j];\n            ph->next = n;\n            ph++;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_add_ports(ngx_conf_t *cf, ngx_array_t *ports,\n    ngx_stream_listen_t *listen)\n{\n    in_port_t                p;\n    ngx_uint_t               i;\n    struct sockaddr         *sa;\n    ngx_stream_conf_port_t  *port;\n    ngx_stream_conf_addr_t  *addr;\n#if (T_NGX_STREAM_SNI)\n    ngx_stream_core_srv_conf_t *cscf;\n#endif\n\n    sa = listen->sockaddr;\n    p = ngx_inet_get_port(sa);\n\n    port = ports->elts;\n    for (i = 0; i < ports->nelts; i++) {\n\n        if (p == port[i].port\n            && listen->type == port[i].type\n            && sa->sa_family == port[i].family)\n        {\n            /* a port is already in the port list */\n\n            port = &port[i];\n            goto found;\n        }\n    }\n\n    /* add a port to the port list */\n\n    port = ngx_array_push(ports);\n    if (port == NULL) {\n        return NGX_ERROR;\n    }\n\n    port->family = sa->sa_family;\n    port->type = listen->type;\n    port->port = p;\n\n    if (ngx_array_init(&port->addrs, cf->temp_pool, 2,\n                       sizeof(ngx_stream_conf_addr_t))\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\nfound:\n#if (T_NGX_STREAM_SNI)\n\n    cscf = listen->ctx->srv_conf[ngx_stream_core_module.ctx_index];\n    addr = port->addrs.elts;\n\n    for (i = 0; i < port->addrs.nelts; i++) {\n        if (ngx_cmp_sockaddr(listen->sockaddr, listen->socklen,\n            addr[i].opt.sockaddr,\n            addr[i].opt.socklen, 0)\n            != NGX_OK)\n        {\n            continue;\n        }\n\n        /* the address is already in the address list */\n\n        if (ngx_stream_add_server(cf, cscf, &addr[i]) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        if (listen->default_server) {\n\n            if (addr[i].opt.default_server) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                        \"a duplicate default server for\");\n                return NGX_ERROR;\n            }\n            addr[i].default_server = cscf;\n            addr[i].opt.default_server = 1;\n        }\n        return NGX_OK;\n    }\n\n    addr = ngx_array_push(&port->addrs);\n    if (addr == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_memset(addr, 0, sizeof(ngx_stream_conf_addr_t));\n    addr->opt = *listen;\n    addr->default_server = cscf;\n\n    return ngx_stream_add_server(cf, cscf, addr);\n\n#else\n\n    addr = ngx_array_push(&port->addrs);\n    if (addr == NULL) {\n        return NGX_ERROR;\n    }\n\n    addr->opt = *listen;\n\n    return NGX_OK;\n#endif\n}\n\n\nstatic char *\nngx_stream_optimize_servers(ngx_conf_t *cf, ngx_array_t *ports)\n{\n    ngx_uint_t                   i, p, last, bind_wildcard;\n    ngx_listening_t             *ls;\n    ngx_stream_port_t           *stport;\n    ngx_stream_conf_port_t      *port;\n    ngx_stream_conf_addr_t      *addr;\n    ngx_stream_core_srv_conf_t  *cscf;\n\n#if (T_NGX_STREAM_SNI)\n    ngx_stream_core_main_conf_t *cmcf;\n#endif\n    port = ports->elts;\n    for (p = 0; p < ports->nelts; p++) {\n\n        ngx_sort(port[p].addrs.elts, (size_t) port[p].addrs.nelts,\n                 sizeof(ngx_stream_conf_addr_t), ngx_stream_cmp_conf_addrs);\n\n        addr = port[p].addrs.elts;\n        last = port[p].addrs.nelts;\n\n        /*\n         * if there is the binding to the \"*:port\" then we need to bind()\n         * to the \"*:port\" only and ignore the other bindings\n         */\n\n        if (addr[last - 1].opt.wildcard) {\n            addr[last - 1].opt.bind = 1;\n            bind_wildcard = 1;\n\n        } else {\n            bind_wildcard = 0;\n        }\n\n        i = 0;\n\n        while (i < last) {\n\n            if (bind_wildcard && !addr[i].opt.bind) {\n                i++;\n                continue;\n            }\n\n            ls = ngx_create_listening(cf, addr[i].opt.sockaddr,\n                                      addr[i].opt.socklen);\n            if (ls == NULL) {\n                return NGX_CONF_ERROR;\n            }\n\n            ls->addr_ntop = 1;\n            ls->handler = ngx_stream_init_connection;\n            ls->pool_size = 256;\n            ls->type = addr[i].opt.type;\n\n            cscf = addr->opt.ctx->srv_conf[ngx_stream_core_module.ctx_index];\n\n            ls->logp = cscf->error_log;\n            ls->log.data = &ls->addr_text;\n            ls->log.handler = ngx_accept_log_error;\n\n            ls->backlog = addr[i].opt.backlog;\n            ls->rcvbuf = addr[i].opt.rcvbuf;\n            ls->sndbuf = addr[i].opt.sndbuf;\n\n            ls->wildcard = addr[i].opt.wildcard;\n\n            ls->keepalive = addr[i].opt.so_keepalive;\n#if (NGX_HAVE_KEEPALIVE_TUNABLE)\n            ls->keepidle = addr[i].opt.tcp_keepidle;\n            ls->keepintvl = addr[i].opt.tcp_keepintvl;\n            ls->keepcnt = addr[i].opt.tcp_keepcnt;\n#endif\n\n#if (NGX_HAVE_INET6)\n            ls->ipv6only = addr[i].opt.ipv6only;\n#endif\n\n#if (NGX_HAVE_TCP_FASTOPEN)\n            ls->fastopen = addr[i].opt.fastopen;\n#endif\n\n#if (NGX_HAVE_REUSEPORT)\n            ls->reuseport = addr[i].opt.reuseport;\n#endif\n\n            stport = ngx_palloc(cf->pool, sizeof(ngx_stream_port_t));\n            if (stport == NULL) {\n                return NGX_CONF_ERROR;\n            }\n\n            ls->servers = stport;\n\n            stport->naddrs = i + 1;\n\n#if (T_NGX_STREAM_SNI)\n            cmcf = addr->opt.ctx->main_conf[ngx_stream_core_module.ctx_index];\n            /*Because of ssl_sni_force we have to do this even one server*/\n            if (addr[i].servers.nelts >= 1) {\n                if (ngx_stream_server_names(cf, cmcf, &addr[i]) != NGX_OK) {\n                    return NGX_CONF_ERROR;\n                }\n            }\n#endif\n\n            switch (ls->sockaddr->sa_family) {\n#if (NGX_HAVE_INET6)\n            case AF_INET6:\n                if (ngx_stream_add_addrs6(cf, stport, addr) != NGX_OK) {\n                    return NGX_CONF_ERROR;\n                }\n                break;\n#endif\n            default: /* AF_INET */\n                if (ngx_stream_add_addrs(cf, stport, addr) != NGX_OK) {\n                    return NGX_CONF_ERROR;\n                }\n                break;\n            }\n\n            addr++;\n            last--;\n        }\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_add_addrs(ngx_conf_t *cf, ngx_stream_port_t *stport,\n    ngx_stream_conf_addr_t *addr)\n{\n    ngx_uint_t             i;\n    struct sockaddr_in    *sin;\n    ngx_stream_in_addr_t  *addrs;\n#if (T_NGX_STREAM_SNI)\n    ngx_stream_virtual_names_t  *vn;\n#endif\n\n    stport->addrs = ngx_pcalloc(cf->pool,\n                                stport->naddrs * sizeof(ngx_stream_in_addr_t));\n    if (stport->addrs == NULL) {\n        return NGX_ERROR;\n    }\n\n    addrs = stport->addrs;\n\n    for (i = 0; i < stport->naddrs; i++) {\n\n        sin = (struct sockaddr_in *) addr[i].opt.sockaddr;\n        addrs[i].addr = sin->sin_addr.s_addr;\n\n        addrs[i].conf.ctx = addr[i].opt.ctx;\n#if (NGX_STREAM_SSL)\n        addrs[i].conf.ssl = addr[i].opt.ssl;\n#endif\n        addrs[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;\n        addrs[i].conf.addr_text = addr[i].opt.addr_text;\n\n#if (T_NGX_STREAM_SNI)\n        addrs[i].conf.default_server = addr[i].default_server;\n\n        if (addr[i].hash.buckets == NULL\n            && (addr[i].wc_head == NULL\n                || addr[i].wc_head->hash.buckets == NULL)\n            && (addr[i].wc_tail == NULL\n                || addr[i].wc_tail->hash.buckets == NULL)\n            )\n        {\n            continue;\n        }\n\n        vn = ngx_palloc(cf->pool, sizeof(ngx_stream_virtual_names_t));\n        if (vn == NULL) {\n            return NGX_ERROR;\n        }\n\n        addrs[i].conf.virtual_names = vn;\n\n        vn->names.hash = addr[i].hash;\n        vn->names.wc_head = addr[i].wc_head;\n        vn->names.wc_tail = addr[i].wc_tail;\n#endif\n    }\n\n    return NGX_OK;\n}\n\n\n#if (NGX_HAVE_INET6)\n\nstatic ngx_int_t\nngx_stream_add_addrs6(ngx_conf_t *cf, ngx_stream_port_t *stport,\n    ngx_stream_conf_addr_t *addr)\n{\n    ngx_uint_t              i;\n    struct sockaddr_in6    *sin6;\n    ngx_stream_in6_addr_t  *addrs6;\n#if (T_NGX_STREAM_SNI)\n    ngx_stream_virtual_names_t  *vn;\n#endif\n\n    stport->addrs = ngx_pcalloc(cf->pool,\n                                stport->naddrs * sizeof(ngx_stream_in6_addr_t));\n    if (stport->addrs == NULL) {\n        return NGX_ERROR;\n    }\n\n    addrs6 = stport->addrs;\n\n    for (i = 0; i < stport->naddrs; i++) {\n\n        sin6 = (struct sockaddr_in6 *) addr[i].opt.sockaddr;\n        addrs6[i].addr6 = sin6->sin6_addr;\n\n        addrs6[i].conf.ctx = addr[i].opt.ctx;\n#if (NGX_STREAM_SSL)\n        addrs6[i].conf.ssl = addr[i].opt.ssl;\n#endif\n        addrs6[i].conf.proxy_protocol = addr[i].opt.proxy_protocol;\n        addrs6[i].conf.addr_text = addr[i].opt.addr_text;\n\n#if (T_NGX_STREAM_SNI)\n        addrs6[i].conf.default_server = addr[i].default_server;\n\n        if (addr[i].hash.buckets == NULL\n            && (addr[i].wc_head == NULL\n                || addr[i].wc_head->hash.buckets == NULL)\n            && (addr[i].wc_tail == NULL\n                || addr[i].wc_tail->hash.buckets == NULL)\n            )\n        {\n            continue;\n        }\n\n        vn = ngx_palloc(cf->pool, sizeof(ngx_stream_virtual_names_t));\n        if (vn == NULL) {\n            return NGX_ERROR;\n        }\n\n        addrs6[i].conf.virtual_names = vn;\n\n        vn->names.hash = addr[i].hash;\n        vn->names.wc_head = addr[i].wc_head;\n        vn->names.wc_tail = addr[i].wc_tail;\n#endif\n    }\n\n    return NGX_OK;\n}\n\n#endif\n\n\nstatic ngx_int_t\nngx_stream_cmp_conf_addrs(const void *one, const void *two)\n{\n    ngx_stream_conf_addr_t  *first, *second;\n\n    first = (ngx_stream_conf_addr_t *) one;\n    second = (ngx_stream_conf_addr_t *) two;\n\n    if (first->opt.wildcard) {\n        /* a wildcard must be the last resort, shift it to the end */\n        return 1;\n    }\n\n    if (second->opt.wildcard) {\n        /* a wildcard must be the last resort, shift it to the end */\n        return -1;\n    }\n\n    if (first->opt.bind && !second->opt.bind) {\n        /* shift explicit bind()ed addresses to the start */\n        return -1;\n    }\n\n    if (!first->opt.bind && second->opt.bind) {\n        /* shift explicit bind()ed addresses to the start */\n        return 1;\n    }\n\n    /* do not sort by default */\n\n    return 0;\n}\n"
  },
  {
    "path": "src/stream/ngx_stream.h",
    "content": "\n/*\n * Copyright (C) Roman Arutyunyan\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_STREAM_H_INCLUDED_\n#define _NGX_STREAM_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n\n#if (NGX_STREAM_SSL)\n#include <ngx_stream_ssl_module.h>\n#endif\n\n\ntypedef struct ngx_stream_session_s  ngx_stream_session_t;\n\n\n#include <ngx_stream_variables.h>\n#include <ngx_stream_script.h>\n#include <ngx_stream_upstream.h>\n#include <ngx_stream_upstream_round_robin.h>\n\n\n#define NGX_STREAM_OK                        200\n#define NGX_STREAM_BAD_REQUEST               400\n#define NGX_STREAM_FORBIDDEN                 403\n#define NGX_STREAM_INTERNAL_SERVER_ERROR     500\n#define NGX_STREAM_BAD_GATEWAY               502\n#define NGX_STREAM_SERVICE_UNAVAILABLE       503\n\n\ntypedef struct {\n    void                         **main_conf;\n    void                         **srv_conf;\n} ngx_stream_conf_ctx_t;\n\n\ntypedef struct {\n    struct sockaddr               *sockaddr;\n    socklen_t                      socklen;\n    ngx_str_t                      addr_text;\n\n    /* server ctx */\n    ngx_stream_conf_ctx_t         *ctx;\n\n    unsigned                       bind:1;\n    unsigned                       wildcard:1;\n    unsigned                       ssl:1;\n#if (NGX_HAVE_INET6)\n    unsigned                       ipv6only:1;\n#endif\n    unsigned                       reuseport:1;\n    unsigned                       so_keepalive:2;\n    unsigned                       proxy_protocol:1;\n#if (NGX_HAVE_KEEPALIVE_TUNABLE)\n    int                            tcp_keepidle;\n    int                            tcp_keepintvl;\n    int                            tcp_keepcnt;\n#endif\n    int                            backlog;\n    int                            rcvbuf;\n    int                            sndbuf;\n#if (NGX_HAVE_TCP_FASTOPEN)\n    int                            fastopen;\n#endif\n    int                            type;\n#if (T_NGX_STREAM_SNI)\n    unsigned                       default_server;\n#endif\n} ngx_stream_listen_t;\n\n\ntypedef struct {\n    ngx_stream_conf_ctx_t         *ctx;\n    ngx_str_t                      addr_text;\n    unsigned                       ssl:1;\n    unsigned                       proxy_protocol:1;\n#if (T_NGX_STREAM_SNI)\n    void                          *default_server;\n    void                          *virtual_names;\n#endif\n} ngx_stream_addr_conf_t;\n\ntypedef struct {\n    in_addr_t                      addr;\n    ngx_stream_addr_conf_t         conf;\n} ngx_stream_in_addr_t;\n\n\n#if (NGX_HAVE_INET6)\n\ntypedef struct {\n    struct in6_addr                addr6;\n    ngx_stream_addr_conf_t         conf;\n} ngx_stream_in6_addr_t;\n\n#endif\n\n\ntypedef struct {\n    /* ngx_stream_in_addr_t or ngx_stream_in6_addr_t */\n    void                          *addrs;\n    ngx_uint_t                     naddrs;\n} ngx_stream_port_t;\n\n\ntypedef struct {\n    int                            family;\n    int                            type;\n    in_port_t                      port;\n    ngx_array_t                    addrs; /* array of ngx_stream_conf_addr_t */\n} ngx_stream_conf_port_t;\n\n\ntypedef struct {\n    ngx_stream_listen_t            opt;\n#if (T_NGX_STREAM_SNI)\n    void                          *default_server;\n    ngx_array_t                    servers;  /* array of ngx_stream_core_srv_conf_t */\n    ngx_hash_t                     hash;\n    ngx_hash_wildcard_t           *wc_head;\n    ngx_hash_wildcard_t           *wc_tail;\n#endif\n} ngx_stream_conf_addr_t;\n\n\ntypedef enum {\n    NGX_STREAM_POST_ACCEPT_PHASE = 0,\n    NGX_STREAM_PREACCESS_PHASE,\n    NGX_STREAM_ACCESS_PHASE,\n    NGX_STREAM_SSL_PHASE,\n    NGX_STREAM_PREREAD_PHASE,\n    NGX_STREAM_CONTENT_PHASE,\n    NGX_STREAM_LOG_PHASE\n} ngx_stream_phases;\n\n\ntypedef struct ngx_stream_phase_handler_s  ngx_stream_phase_handler_t;\n\ntypedef ngx_int_t (*ngx_stream_phase_handler_pt)(ngx_stream_session_t *s,\n    ngx_stream_phase_handler_t *ph);\ntypedef ngx_int_t (*ngx_stream_handler_pt)(ngx_stream_session_t *s);\ntypedef void (*ngx_stream_content_handler_pt)(ngx_stream_session_t *s);\n\n\nstruct ngx_stream_phase_handler_s {\n    ngx_stream_phase_handler_pt    checker;\n    ngx_stream_handler_pt          handler;\n    ngx_uint_t                     next;\n};\n\n\ntypedef struct {\n    ngx_stream_phase_handler_t    *handlers;\n} ngx_stream_phase_engine_t;\n\n\ntypedef struct {\n    ngx_array_t                    handlers;\n} ngx_stream_phase_t;\n\n\ntypedef struct {\n    ngx_array_t                    servers;     /* ngx_stream_core_srv_conf_t */\n    ngx_array_t                    listen;      /* ngx_stream_listen_t */\n\n    ngx_stream_phase_engine_t      phase_engine;\n\n    ngx_hash_t                     variables_hash;\n\n    ngx_array_t                    variables;        /* ngx_stream_variable_t */\n    ngx_array_t                    prefix_variables; /* ngx_stream_variable_t */\n    ngx_uint_t                     ncaptures;\n\n    ngx_uint_t                     variables_hash_max_size;\n    ngx_uint_t                     variables_hash_bucket_size;\n\n    ngx_hash_keys_arrays_t        *variables_keys;\n\n    ngx_stream_phase_t             phases[NGX_STREAM_LOG_PHASE + 1];\n\n#if (T_NGX_STREAM_SNI)\n    ngx_uint_t                     server_names_hash_max_size;\n    ngx_uint_t                     server_names_hash_bucket_size;\n#endif\n} ngx_stream_core_main_conf_t;\n\n\ntypedef struct {\n    ngx_stream_content_handler_pt  handler;\n\n    ngx_stream_conf_ctx_t         *ctx;\n\n    u_char                        *file_name;\n    ngx_uint_t                     line;\n\n    ngx_flag_t                     tcp_nodelay;\n    size_t                         preread_buffer_size;\n    ngx_msec_t                     preread_timeout;\n\n    ngx_log_t                     *error_log;\n\n    ngx_msec_t                     resolver_timeout;\n    ngx_resolver_t                *resolver;\n\n    ngx_msec_t                     proxy_protocol_timeout;\n\n    ngx_uint_t                     listen;  /* unsigned  listen:1; */\n\n#if (T_NGX_STREAM_SNI)\n    /* array of the ngx_stream_server_name_t, \"server_name\" directive */\n    ngx_array_t                    server_names;\n    ngx_str_t                      server_name;\n#endif\n} ngx_stream_core_srv_conf_t;\n\n#if (T_NGX_STREAM_SNI)\n/* list of structures to find core_srv_conf quickly at run time */\ntypedef struct {\n    ngx_stream_core_srv_conf_t  *server;   /* virtual name server conf */\n    ngx_str_t                    name;\n} ngx_stream_server_name_t;\n\ntypedef struct {\n    ngx_hash_combined_t        names;\n} ngx_stream_virtual_names_t;\n#endif\n\nstruct ngx_stream_session_s {\n    uint32_t                       signature;         /* \"STRM\" */\n\n    ngx_connection_t              *connection;\n\n    off_t                          received;\n    time_t                         start_sec;\n    ngx_msec_t                     start_msec;\n\n    ngx_log_handler_pt             log_handler;\n\n    void                         **ctx;\n    void                         **main_conf;\n    void                         **srv_conf;\n\n    ngx_stream_upstream_t         *upstream;\n    ngx_array_t                   *upstream_states;\n                                           /* of ngx_stream_upstream_state_t */\n    ngx_stream_variable_value_t   *variables;\n\n#if (NGX_PCRE)\n    ngx_uint_t                     ncaptures;\n    int                           *captures;\n    u_char                        *captures_data;\n#endif\n\n    ngx_int_t                      phase_handler;\n    ngx_uint_t                     status;\n\n    unsigned                       ssl:1;\n\n    unsigned                       stat_processing:1;\n\n    unsigned                       health_check:1;\n#if (T_NGX_STREAM_SNI)\n    ngx_stream_addr_conf_t        *addr_conf;\n#endif\n\n    unsigned                       limit_conn_status:2;\n\n#if (T_NGX_MULTI_UPSTREAM)\n    ngx_queue_t                   *multi_item;\n    ngx_queue_t                   *backend_r;\n    ngx_queue_t                    waiting_queue;\n    ngx_flag_t                     waiting;\n#endif\n};\n\n\ntypedef struct {\n    ngx_int_t                    (*preconfiguration)(ngx_conf_t *cf);\n    ngx_int_t                    (*postconfiguration)(ngx_conf_t *cf);\n\n    void                        *(*create_main_conf)(ngx_conf_t *cf);\n    char                        *(*init_main_conf)(ngx_conf_t *cf, void *conf);\n\n    void                        *(*create_srv_conf)(ngx_conf_t *cf);\n    char                        *(*merge_srv_conf)(ngx_conf_t *cf, void *prev,\n                                                   void *conf);\n} ngx_stream_module_t;\n\n\n#define NGX_STREAM_MODULE       0x4d525453     /* \"STRM\" */\n\n#define NGX_STREAM_MAIN_CONF    0x02000000\n#define NGX_STREAM_SRV_CONF     0x04000000\n#define NGX_STREAM_UPS_CONF     0x08000000\n\n\n#define NGX_STREAM_MAIN_CONF_OFFSET  offsetof(ngx_stream_conf_ctx_t, main_conf)\n#define NGX_STREAM_SRV_CONF_OFFSET   offsetof(ngx_stream_conf_ctx_t, srv_conf)\n\n\n#define ngx_stream_get_module_ctx(s, module)   (s)->ctx[module.ctx_index]\n#define ngx_stream_set_ctx(s, c, module)       s->ctx[module.ctx_index] = c;\n#define ngx_stream_delete_ctx(s, module)       s->ctx[module.ctx_index] = NULL;\n\n\n#define ngx_stream_get_module_main_conf(s, module)                             \\\n    (s)->main_conf[module.ctx_index]\n#define ngx_stream_get_module_srv_conf(s, module)                              \\\n    (s)->srv_conf[module.ctx_index]\n\n#define ngx_stream_conf_get_module_main_conf(cf, module)                       \\\n    ((ngx_stream_conf_ctx_t *) cf->ctx)->main_conf[module.ctx_index]\n#define ngx_stream_conf_get_module_srv_conf(cf, module)                        \\\n    ((ngx_stream_conf_ctx_t *) cf->ctx)->srv_conf[module.ctx_index]\n\n#define ngx_stream_cycle_get_module_main_conf(cycle, module)                   \\\n    (cycle->conf_ctx[ngx_stream_module.index] ?                                \\\n        ((ngx_stream_conf_ctx_t *) cycle->conf_ctx[ngx_stream_module.index])   \\\n            ->main_conf[module.ctx_index]:                                     \\\n        NULL)\n\n\n#define NGX_STREAM_WRITE_BUFFERED  0x10\n\n\nvoid ngx_stream_core_run_phases(ngx_stream_session_t *s);\nngx_int_t ngx_stream_core_generic_phase(ngx_stream_session_t *s,\n    ngx_stream_phase_handler_t *ph);\nngx_int_t ngx_stream_core_preread_phase(ngx_stream_session_t *s,\n    ngx_stream_phase_handler_t *ph);\nngx_int_t ngx_stream_core_content_phase(ngx_stream_session_t *s,\n    ngx_stream_phase_handler_t *ph);\n\n\nvoid ngx_stream_init_connection(ngx_connection_t *c);\nvoid ngx_stream_session_handler(ngx_event_t *rev);\nvoid ngx_stream_finalize_session(ngx_stream_session_t *s, ngx_uint_t rc);\n\n\nextern ngx_module_t  ngx_stream_module;\nextern ngx_uint_t    ngx_stream_max_module;\nextern ngx_module_t  ngx_stream_core_module;\n\n\ntypedef ngx_int_t (*ngx_stream_filter_pt)(ngx_stream_session_t *s,\n    ngx_chain_t *chain, ngx_uint_t from_upstream);\n\n\nextern ngx_stream_filter_pt  ngx_stream_top_filter;\n\n\n#endif /* _NGX_STREAM_H_INCLUDED_ */\n"
  },
  {
    "path": "src/stream/ngx_stream_access_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_stream.h>\n\n\ntypedef struct {\n    in_addr_t         mask;\n    in_addr_t         addr;\n    ngx_uint_t        deny;      /* unsigned  deny:1; */\n} ngx_stream_access_rule_t;\n\n#if (NGX_HAVE_INET6)\n\ntypedef struct {\n    struct in6_addr   addr;\n    struct in6_addr   mask;\n    ngx_uint_t        deny;      /* unsigned  deny:1; */\n} ngx_stream_access_rule6_t;\n\n#endif\n\n#if (NGX_HAVE_UNIX_DOMAIN)\n\ntypedef struct {\n    ngx_uint_t        deny;      /* unsigned  deny:1; */\n} ngx_stream_access_rule_un_t;\n\n#endif\n\ntypedef struct {\n    ngx_array_t      *rules;     /* array of ngx_stream_access_rule_t */\n#if (NGX_HAVE_INET6)\n    ngx_array_t      *rules6;    /* array of ngx_stream_access_rule6_t */\n#endif\n#if (NGX_HAVE_UNIX_DOMAIN)\n    ngx_array_t      *rules_un;  /* array of ngx_stream_access_rule_un_t */\n#endif\n} ngx_stream_access_srv_conf_t;\n\n\nstatic ngx_int_t ngx_stream_access_handler(ngx_stream_session_t *s);\nstatic ngx_int_t ngx_stream_access_inet(ngx_stream_session_t *s,\n    ngx_stream_access_srv_conf_t *ascf, in_addr_t addr);\n#if (NGX_HAVE_INET6)\nstatic ngx_int_t ngx_stream_access_inet6(ngx_stream_session_t *s,\n    ngx_stream_access_srv_conf_t *ascf, u_char *p);\n#endif\n#if (NGX_HAVE_UNIX_DOMAIN)\nstatic ngx_int_t ngx_stream_access_unix(ngx_stream_session_t *s,\n    ngx_stream_access_srv_conf_t *ascf);\n#endif\nstatic ngx_int_t ngx_stream_access_found(ngx_stream_session_t *s,\n    ngx_uint_t deny);\nstatic char *ngx_stream_access_rule(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic void *ngx_stream_access_create_srv_conf(ngx_conf_t *cf);\nstatic char *ngx_stream_access_merge_srv_conf(ngx_conf_t *cf,\n    void *parent, void *child);\nstatic ngx_int_t ngx_stream_access_init(ngx_conf_t *cf);\n\n\nstatic ngx_command_t  ngx_stream_access_commands[] = {\n\n    { ngx_string(\"allow\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_stream_access_rule,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"deny\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_stream_access_rule,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      0,\n      NULL },\n\n      ngx_null_command\n};\n\n\n\nstatic ngx_stream_module_t  ngx_stream_access_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    ngx_stream_access_init,                /* postconfiguration */\n\n    NULL,                                  /* create main configuration */\n    NULL,                                  /* init main configuration */\n\n    ngx_stream_access_create_srv_conf,     /* create server configuration */\n    ngx_stream_access_merge_srv_conf       /* merge server configuration */\n};\n\n\nngx_module_t  ngx_stream_access_module = {\n    NGX_MODULE_V1,\n    &ngx_stream_access_module_ctx,         /* module context */\n    ngx_stream_access_commands,            /* module directives */\n    NGX_STREAM_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_stream_access_handler(ngx_stream_session_t *s)\n{\n    struct sockaddr_in            *sin;\n    ngx_stream_access_srv_conf_t  *ascf;\n#if (NGX_HAVE_INET6)\n    u_char                        *p;\n    in_addr_t                      addr;\n    struct sockaddr_in6           *sin6;\n#endif\n\n    ascf = ngx_stream_get_module_srv_conf(s, ngx_stream_access_module);\n\n    switch (s->connection->sockaddr->sa_family) {\n\n    case AF_INET:\n        if (ascf->rules) {\n            sin = (struct sockaddr_in *) s->connection->sockaddr;\n            return ngx_stream_access_inet(s, ascf, sin->sin_addr.s_addr);\n        }\n        break;\n\n#if (NGX_HAVE_INET6)\n\n    case AF_INET6:\n        sin6 = (struct sockaddr_in6 *) s->connection->sockaddr;\n        p = sin6->sin6_addr.s6_addr;\n\n        if (ascf->rules && IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) {\n            addr = p[12] << 24;\n            addr += p[13] << 16;\n            addr += p[14] << 8;\n            addr += p[15];\n            return ngx_stream_access_inet(s, ascf, htonl(addr));\n        }\n\n        if (ascf->rules6) {\n            return ngx_stream_access_inet6(s, ascf, p);\n        }\n\n        break;\n\n#endif\n\n#if (NGX_HAVE_UNIX_DOMAIN)\n\n    case AF_UNIX:\n        if (ascf->rules_un) {\n            return ngx_stream_access_unix(s, ascf);\n        }\n\n        break;\n\n#endif\n    }\n\n    return NGX_DECLINED;\n}\n\n\nstatic ngx_int_t\nngx_stream_access_inet(ngx_stream_session_t *s,\n    ngx_stream_access_srv_conf_t *ascf, in_addr_t addr)\n{\n    ngx_uint_t                 i;\n    ngx_stream_access_rule_t  *rule;\n\n    rule = ascf->rules->elts;\n    for (i = 0; i < ascf->rules->nelts; i++) {\n\n        ngx_log_debug3(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,\n                       \"access: %08XD %08XD %08XD\",\n                       addr, rule[i].mask, rule[i].addr);\n\n        if ((addr & rule[i].mask) == rule[i].addr) {\n            return ngx_stream_access_found(s, rule[i].deny);\n        }\n    }\n\n    return NGX_DECLINED;\n}\n\n\n#if (NGX_HAVE_INET6)\n\nstatic ngx_int_t\nngx_stream_access_inet6(ngx_stream_session_t *s,\n    ngx_stream_access_srv_conf_t *ascf, u_char *p)\n{\n    ngx_uint_t                  n;\n    ngx_uint_t                  i;\n    ngx_stream_access_rule6_t  *rule6;\n\n    rule6 = ascf->rules6->elts;\n    for (i = 0; i < ascf->rules6->nelts; i++) {\n\n#if (NGX_DEBUG)\n        {\n        size_t  cl, ml, al;\n        u_char  ct[NGX_INET6_ADDRSTRLEN];\n        u_char  mt[NGX_INET6_ADDRSTRLEN];\n        u_char  at[NGX_INET6_ADDRSTRLEN];\n\n        cl = ngx_inet6_ntop(p, ct, NGX_INET6_ADDRSTRLEN);\n        ml = ngx_inet6_ntop(rule6[i].mask.s6_addr, mt, NGX_INET6_ADDRSTRLEN);\n        al = ngx_inet6_ntop(rule6[i].addr.s6_addr, at, NGX_INET6_ADDRSTRLEN);\n\n        ngx_log_debug6(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,\n                       \"access: %*s %*s %*s\", cl, ct, ml, mt, al, at);\n        }\n#endif\n\n        for (n = 0; n < 16; n++) {\n            if ((p[n] & rule6[i].mask.s6_addr[n]) != rule6[i].addr.s6_addr[n]) {\n                goto next;\n            }\n        }\n\n        return ngx_stream_access_found(s, rule6[i].deny);\n\n    next:\n        continue;\n    }\n\n    return NGX_DECLINED;\n}\n\n#endif\n\n\n#if (NGX_HAVE_UNIX_DOMAIN)\n\nstatic ngx_int_t\nngx_stream_access_unix(ngx_stream_session_t *s,\n    ngx_stream_access_srv_conf_t *ascf)\n{\n    ngx_uint_t                    i;\n    ngx_stream_access_rule_un_t  *rule_un;\n\n    rule_un = ascf->rules_un->elts;\n    for (i = 0; i < ascf->rules_un->nelts; i++) {\n\n        /* TODO: check path */\n        if (1) {\n            return ngx_stream_access_found(s, rule_un[i].deny);\n        }\n    }\n\n    return NGX_DECLINED;\n}\n\n#endif\n\n\nstatic ngx_int_t\nngx_stream_access_found(ngx_stream_session_t *s, ngx_uint_t deny)\n{\n    if (deny) {\n        ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,\n                      \"access forbidden by rule\");\n        return NGX_STREAM_FORBIDDEN;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic char *\nngx_stream_access_rule(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_stream_access_srv_conf_t *ascf = conf;\n\n    ngx_int_t                     rc;\n    ngx_uint_t                    all;\n    ngx_str_t                    *value;\n    ngx_cidr_t                    cidr;\n    ngx_stream_access_rule_t     *rule;\n#if (NGX_HAVE_INET6)\n    ngx_stream_access_rule6_t    *rule6;\n#endif\n#if (NGX_HAVE_UNIX_DOMAIN)\n    ngx_stream_access_rule_un_t  *rule_un;\n#endif\n\n    all = 0;\n    ngx_memzero(&cidr, sizeof(ngx_cidr_t));\n\n    value = cf->args->elts;\n\n    if (value[1].len == 3 && ngx_strcmp(value[1].data, \"all\") == 0) {\n        all = 1;\n\n#if (NGX_HAVE_UNIX_DOMAIN)\n    } else if (value[1].len == 5 && ngx_strcmp(value[1].data, \"unix:\") == 0) {\n        cidr.family = AF_UNIX;\n#endif\n\n    } else {\n        rc = ngx_ptocidr(&value[1], &cidr);\n\n        if (rc == NGX_ERROR) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                         \"invalid parameter \\\"%V\\\"\", &value[1]);\n            return NGX_CONF_ERROR;\n        }\n\n        if (rc == NGX_DONE) {\n            ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                         \"low address bits of %V are meaningless\", &value[1]);\n        }\n    }\n\n    if (cidr.family == AF_INET || all) {\n\n        if (ascf->rules == NULL) {\n            ascf->rules = ngx_array_create(cf->pool, 4,\n                                           sizeof(ngx_stream_access_rule_t));\n            if (ascf->rules == NULL) {\n                return NGX_CONF_ERROR;\n            }\n        }\n\n        rule = ngx_array_push(ascf->rules);\n        if (rule == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        rule->mask = cidr.u.in.mask;\n        rule->addr = cidr.u.in.addr;\n        rule->deny = (value[0].data[0] == 'd') ? 1 : 0;\n    }\n\n#if (NGX_HAVE_INET6)\n    if (cidr.family == AF_INET6 || all) {\n\n        if (ascf->rules6 == NULL) {\n            ascf->rules6 = ngx_array_create(cf->pool, 4,\n                                            sizeof(ngx_stream_access_rule6_t));\n            if (ascf->rules6 == NULL) {\n                return NGX_CONF_ERROR;\n            }\n        }\n\n        rule6 = ngx_array_push(ascf->rules6);\n        if (rule6 == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        rule6->mask = cidr.u.in6.mask;\n        rule6->addr = cidr.u.in6.addr;\n        rule6->deny = (value[0].data[0] == 'd') ? 1 : 0;\n    }\n#endif\n\n#if (NGX_HAVE_UNIX_DOMAIN)\n    if (cidr.family == AF_UNIX || all) {\n\n        if (ascf->rules_un == NULL) {\n            ascf->rules_un = ngx_array_create(cf->pool, 1,\n                                          sizeof(ngx_stream_access_rule_un_t));\n            if (ascf->rules_un == NULL) {\n                return NGX_CONF_ERROR;\n            }\n        }\n\n        rule_un = ngx_array_push(ascf->rules_un);\n        if (rule_un == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        rule_un->deny = (value[0].data[0] == 'd') ? 1 : 0;\n    }\n#endif\n\n    return NGX_CONF_OK;\n}\n\n\nstatic void *\nngx_stream_access_create_srv_conf(ngx_conf_t *cf)\n{\n    ngx_stream_access_srv_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_access_srv_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    return conf;\n}\n\n\nstatic char *\nngx_stream_access_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_stream_access_srv_conf_t  *prev = parent;\n    ngx_stream_access_srv_conf_t  *conf = child;\n\n    if (conf->rules == NULL\n#if (NGX_HAVE_INET6)\n        && conf->rules6 == NULL\n#endif\n#if (NGX_HAVE_UNIX_DOMAIN)\n        && conf->rules_un == NULL\n#endif\n    ) {\n        conf->rules = prev->rules;\n#if (NGX_HAVE_INET6)\n        conf->rules6 = prev->rules6;\n#endif\n#if (NGX_HAVE_UNIX_DOMAIN)\n        conf->rules_un = prev->rules_un;\n#endif\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_access_init(ngx_conf_t *cf)\n{\n    ngx_stream_handler_pt        *h;\n    ngx_stream_core_main_conf_t  *cmcf;\n\n    cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);\n\n    h = ngx_array_push(&cmcf->phases[NGX_STREAM_ACCESS_PHASE].handlers);\n    if (h == NULL) {\n        return NGX_ERROR;\n    }\n\n    *h = ngx_stream_access_handler;\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "src/stream/ngx_stream_core_module.c",
    "content": "\n/*\n * Copyright (C) Roman Arutyunyan\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_stream.h>\n\n\nstatic ngx_int_t ngx_stream_core_preconfiguration(ngx_conf_t *cf);\nstatic void *ngx_stream_core_create_main_conf(ngx_conf_t *cf);\nstatic char *ngx_stream_core_init_main_conf(ngx_conf_t *cf, void *conf);\nstatic void *ngx_stream_core_create_srv_conf(ngx_conf_t *cf);\nstatic char *ngx_stream_core_merge_srv_conf(ngx_conf_t *cf, void *parent,\n    void *child);\nstatic char *ngx_stream_core_error_log(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_stream_core_server(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_stream_core_listen(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_stream_core_resolver(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n\n#if (T_NGX_STREAM_SNI)\nstatic char *ngx_stream_core_server_name(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n#endif\n\nstatic ngx_command_t  ngx_stream_core_commands[] = {\n\n    { ngx_string(\"variables_hash_max_size\"),\n      NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_num_slot,\n      NGX_STREAM_MAIN_CONF_OFFSET,\n      offsetof(ngx_stream_core_main_conf_t, variables_hash_max_size),\n      NULL },\n\n    { ngx_string(\"variables_hash_bucket_size\"),\n      NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_num_slot,\n      NGX_STREAM_MAIN_CONF_OFFSET,\n      offsetof(ngx_stream_core_main_conf_t, variables_hash_bucket_size),\n      NULL },\n\n    { ngx_string(\"server\"),\n      NGX_STREAM_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,\n      ngx_stream_core_server,\n      0,\n      0,\n      NULL },\n\n    { ngx_string(\"listen\"),\n      NGX_STREAM_SRV_CONF|NGX_CONF_1MORE,\n      ngx_stream_core_listen,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"error_log\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_1MORE,\n      ngx_stream_core_error_log,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"resolver\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_1MORE,\n      ngx_stream_core_resolver,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"resolver_timeout\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_core_srv_conf_t, resolver_timeout),\n      NULL },\n\n    { ngx_string(\"proxy_protocol_timeout\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_core_srv_conf_t, proxy_protocol_timeout),\n      NULL },\n\n    { ngx_string(\"tcp_nodelay\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_core_srv_conf_t, tcp_nodelay),\n      NULL },\n\n    { ngx_string(\"preread_buffer_size\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_core_srv_conf_t, preread_buffer_size),\n      NULL },\n\n    { ngx_string(\"preread_timeout\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_core_srv_conf_t, preread_timeout),\n      NULL },\n\n#if (T_NGX_STREAM_SNI)\n    { ngx_string(\"server_name\"),\n      NGX_STREAM_SRV_CONF|NGX_CONF_1MORE,\n      ngx_stream_core_server_name,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"server_names_hash_max_size\"),\n      NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_num_slot,\n      NGX_STREAM_MAIN_CONF_OFFSET,\n      offsetof(ngx_stream_core_main_conf_t, server_names_hash_max_size),\n      NULL },\n\n    { ngx_string(\"server_names_hash_bucket_size\"),\n      NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_num_slot,\n      NGX_STREAM_MAIN_CONF_OFFSET,\n      offsetof(ngx_stream_core_main_conf_t, server_names_hash_bucket_size),\n      NULL },\n\n#endif\n      ngx_null_command\n};\n\n\nstatic ngx_stream_module_t  ngx_stream_core_module_ctx = {\n    ngx_stream_core_preconfiguration,      /* preconfiguration */\n    NULL,                                  /* postconfiguration */\n\n    ngx_stream_core_create_main_conf,      /* create main configuration */\n    ngx_stream_core_init_main_conf,        /* init main configuration */\n\n    ngx_stream_core_create_srv_conf,       /* create server configuration */\n    ngx_stream_core_merge_srv_conf         /* merge server configuration */\n};\n\n\nngx_module_t  ngx_stream_core_module = {\n    NGX_MODULE_V1,\n    &ngx_stream_core_module_ctx,           /* module context */\n    ngx_stream_core_commands,              /* module directives */\n    NGX_STREAM_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\nvoid\nngx_stream_core_run_phases(ngx_stream_session_t *s)\n{\n    ngx_int_t                     rc;\n    ngx_stream_phase_handler_t   *ph;\n    ngx_stream_core_main_conf_t  *cmcf;\n\n    cmcf = ngx_stream_get_module_main_conf(s, ngx_stream_core_module);\n\n    ph = cmcf->phase_engine.handlers;\n\n    while (ph[s->phase_handler].checker) {\n\n        rc = ph[s->phase_handler].checker(s, &ph[s->phase_handler]);\n\n        if (rc == NGX_OK) {\n            return;\n        }\n    }\n}\n\n\nngx_int_t\nngx_stream_core_generic_phase(ngx_stream_session_t *s,\n    ngx_stream_phase_handler_t *ph)\n{\n    ngx_int_t  rc;\n\n    /*\n     * generic phase checker,\n     * used by all phases, except for preread and content\n     */\n\n    ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,\n                   \"generic phase: %ui\", s->phase_handler);\n\n    rc = ph->handler(s);\n\n    if (rc == NGX_OK) {\n        s->phase_handler = ph->next;\n        return NGX_AGAIN;\n    }\n\n    if (rc == NGX_DECLINED) {\n        s->phase_handler++;\n        return NGX_AGAIN;\n    }\n\n    if (rc == NGX_AGAIN || rc == NGX_DONE) {\n        return NGX_OK;\n    }\n\n    if (rc == NGX_ERROR) {\n        rc = NGX_STREAM_INTERNAL_SERVER_ERROR;\n    }\n\n    ngx_stream_finalize_session(s, rc);\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_stream_core_preread_phase(ngx_stream_session_t *s,\n    ngx_stream_phase_handler_t *ph)\n{\n    size_t                       size;\n    ssize_t                      n;\n    ngx_int_t                    rc;\n    ngx_connection_t            *c;\n    ngx_stream_core_srv_conf_t  *cscf;\n\n    c = s->connection;\n\n    c->log->action = \"prereading client data\";\n\n    cscf = ngx_stream_get_module_srv_conf(s, ngx_stream_core_module);\n\n    if (c->read->timedout) {\n        rc = NGX_STREAM_OK;\n\n    } else if (c->read->timer_set) {\n        rc = NGX_AGAIN;\n\n    } else {\n        rc = ph->handler(s);\n    }\n\n    while (rc == NGX_AGAIN) {\n\n        if (c->buffer == NULL) {\n            c->buffer = ngx_create_temp_buf(c->pool, cscf->preread_buffer_size);\n            if (c->buffer == NULL) {\n                rc = NGX_ERROR;\n                break;\n            }\n        }\n\n        size = c->buffer->end - c->buffer->last;\n\n        if (size == 0) {\n            ngx_log_error(NGX_LOG_ERR, c->log, 0, \"preread buffer full\");\n            rc = NGX_STREAM_BAD_REQUEST;\n            break;\n        }\n\n        if (c->read->eof) {\n            rc = NGX_STREAM_OK;\n            break;\n        }\n\n        if (!c->read->ready) {\n            break;\n        }\n\n        n = c->recv(c, c->buffer->last, size);\n\n        if (n == NGX_ERROR || n == 0) {\n            rc = NGX_STREAM_OK;\n            break;\n        }\n\n        if (n == NGX_AGAIN) {\n            break;\n        }\n\n        c->buffer->last += n;\n\n        rc = ph->handler(s);\n    }\n\n    if (rc == NGX_AGAIN) {\n        if (ngx_handle_read_event(c->read, 0) != NGX_OK) {\n            ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR);\n            return NGX_OK;\n        }\n\n        if (!c->read->timer_set) {\n            ngx_add_timer(c->read, cscf->preread_timeout);\n        }\n\n        c->read->handler = ngx_stream_session_handler;\n\n        return NGX_OK;\n    }\n\n    if (c->read->timer_set) {\n        ngx_del_timer(c->read);\n    }\n\n    if (rc == NGX_OK) {\n        s->phase_handler = ph->next;\n        return NGX_AGAIN;\n    }\n\n    if (rc == NGX_DECLINED) {\n        s->phase_handler++;\n        return NGX_AGAIN;\n    }\n\n    if (rc == NGX_DONE) {\n        return NGX_OK;\n    }\n\n    if (rc == NGX_ERROR) {\n        rc = NGX_STREAM_INTERNAL_SERVER_ERROR;\n    }\n\n    ngx_stream_finalize_session(s, rc);\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_stream_core_content_phase(ngx_stream_session_t *s,\n    ngx_stream_phase_handler_t *ph)\n{\n    ngx_connection_t            *c;\n    ngx_stream_core_srv_conf_t  *cscf;\n\n    c = s->connection;\n\n    c->log->action = NULL;\n\n    cscf = ngx_stream_get_module_srv_conf(s, ngx_stream_core_module);\n\n    if (c->type == SOCK_STREAM\n        && cscf->tcp_nodelay\n        && ngx_tcp_nodelay(c) != NGX_OK)\n    {\n        ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR);\n        return NGX_OK;\n    }\n\n    cscf->handler(s);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_core_preconfiguration(ngx_conf_t *cf)\n{\n    return ngx_stream_variables_add_core_vars(cf);\n}\n\n\nstatic void *\nngx_stream_core_create_main_conf(ngx_conf_t *cf)\n{\n    ngx_stream_core_main_conf_t  *cmcf;\n\n    cmcf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_core_main_conf_t));\n    if (cmcf == NULL) {\n        return NULL;\n    }\n\n    if (ngx_array_init(&cmcf->servers, cf->pool, 4,\n                       sizeof(ngx_stream_core_srv_conf_t *))\n        != NGX_OK)\n    {\n        return NULL;\n    }\n\n    if (ngx_array_init(&cmcf->listen, cf->pool, 4, sizeof(ngx_stream_listen_t))\n        != NGX_OK)\n    {\n        return NULL;\n    }\n\n    cmcf->variables_hash_max_size = NGX_CONF_UNSET_UINT;\n    cmcf->variables_hash_bucket_size = NGX_CONF_UNSET_UINT;\n\n#if (T_NGX_STREAM_SNI)\n    cmcf->server_names_hash_max_size = NGX_CONF_UNSET_UINT;\n    cmcf->server_names_hash_bucket_size = NGX_CONF_UNSET_UINT;\n#endif\n\n    return cmcf;\n}\n\n\nstatic char *\nngx_stream_core_init_main_conf(ngx_conf_t *cf, void *conf)\n{\n    ngx_stream_core_main_conf_t *cmcf = conf;\n\n    ngx_conf_init_uint_value(cmcf->variables_hash_max_size, 1024);\n    ngx_conf_init_uint_value(cmcf->variables_hash_bucket_size, 64);\n\n#if (T_NGX_STREAM_SNI)\n    ngx_conf_init_uint_value(cmcf->server_names_hash_max_size, 512);\n    ngx_conf_init_uint_value(cmcf->server_names_hash_bucket_size,\n                             ngx_cacheline_size);\n#endif\n\n    cmcf->variables_hash_bucket_size =\n               ngx_align(cmcf->variables_hash_bucket_size, ngx_cacheline_size);\n\n    if (cmcf->ncaptures) {\n        cmcf->ncaptures = (cmcf->ncaptures + 1) * 3;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic void *\nngx_stream_core_create_srv_conf(ngx_conf_t *cf)\n{\n    ngx_stream_core_srv_conf_t  *cscf;\n\n    cscf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_core_srv_conf_t));\n    if (cscf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc():\n     *\n     *     cscf->handler = NULL;\n     *     cscf->error_log = NULL;\n     */\n\n    cscf->file_name = cf->conf_file->file.name.data;\n    cscf->line = cf->conf_file->line;\n    cscf->resolver_timeout = NGX_CONF_UNSET_MSEC;\n    cscf->proxy_protocol_timeout = NGX_CONF_UNSET_MSEC;\n    cscf->tcp_nodelay = NGX_CONF_UNSET;\n    cscf->preread_buffer_size = NGX_CONF_UNSET_SIZE;\n    cscf->preread_timeout = NGX_CONF_UNSET_MSEC;\n\n#if (T_NGX_STREAM_SNI)\n    if (ngx_array_init(&cscf->server_names, cf->temp_pool, 4,\n                       sizeof(ngx_stream_server_name_t))\n        != NGX_OK)\n    {\n        return NULL;\n    }\n#endif\n\n    return cscf;\n}\n\n\nstatic char *\nngx_stream_core_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_stream_core_srv_conf_t *prev = parent;\n    ngx_stream_core_srv_conf_t *conf = child;\n#if (T_NGX_STREAM_SNI)\n    ngx_str_t                   name;\n    ngx_stream_server_name_t   *sn;\n#endif\n\n\n    ngx_conf_merge_msec_value(conf->resolver_timeout,\n                              prev->resolver_timeout, 30000);\n\n    if (conf->resolver == NULL) {\n\n        if (prev->resolver == NULL) {\n\n            /*\n             * create dummy resolver in stream {} context\n             * to inherit it in all servers\n             */\n\n            prev->resolver = ngx_resolver_create(cf, NULL, 0);\n            if (prev->resolver == NULL) {\n                return NGX_CONF_ERROR;\n            }\n        }\n\n        conf->resolver = prev->resolver;\n    }\n\n    if (conf->handler == NULL) {\n        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                      \"no handler for server in %s:%ui\",\n                      conf->file_name, conf->line);\n        return NGX_CONF_ERROR;\n    }\n\n    if (conf->error_log == NULL) {\n        if (prev->error_log) {\n            conf->error_log = prev->error_log;\n        } else {\n            conf->error_log = &cf->cycle->new_log;\n        }\n    }\n\n    ngx_conf_merge_msec_value(conf->proxy_protocol_timeout,\n                              prev->proxy_protocol_timeout, 30000);\n\n    ngx_conf_merge_value(conf->tcp_nodelay, prev->tcp_nodelay, 1);\n\n    ngx_conf_merge_size_value(conf->preread_buffer_size,\n                              prev->preread_buffer_size, 16384);\n\n    ngx_conf_merge_msec_value(conf->preread_timeout,\n                              prev->preread_timeout, 30000);\n\n#if (T_NGX_STREAM_SNI)\n    if (conf->server_names.nelts == 0) {\n        /* the array has 4 empty preallocated elements, so push cannot fail */\n        sn = ngx_array_push(&conf->server_names);\n        sn->server = conf;\n        ngx_str_set(&sn->name, \"\");\n    }\n\n    sn = conf->server_names.elts;\n    name = sn[0].name;\n\n    if (name.data[0] == '.') {\n        name.len--;\n        name.data++;\n    }\n\n    conf->server_name.len = name.len;\n    conf->server_name.data = ngx_pstrdup(cf->pool, &name);\n    if (conf->server_name.data == NULL) {\n        return NGX_CONF_ERROR;\n    }\n#endif\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_stream_core_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_stream_core_srv_conf_t  *cscf = conf;\n\n    return ngx_log_set_log(cf, &cscf->error_log);\n}\n\n\nstatic char *\nngx_stream_core_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    char                         *rv;\n    void                         *mconf;\n    ngx_uint_t                    m;\n    ngx_conf_t                    pcf;\n    ngx_stream_module_t          *module;\n    ngx_stream_conf_ctx_t        *ctx, *stream_ctx;\n    ngx_stream_core_srv_conf_t   *cscf, **cscfp;\n    ngx_stream_core_main_conf_t  *cmcf;\n\n    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_stream_conf_ctx_t));\n    if (ctx == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    stream_ctx = cf->ctx;\n    ctx->main_conf = stream_ctx->main_conf;\n\n    /* the server{}'s srv_conf */\n\n    ctx->srv_conf = ngx_pcalloc(cf->pool,\n                                sizeof(void *) * ngx_stream_max_module);\n    if (ctx->srv_conf == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    for (m = 0; cf->cycle->modules[m]; m++) {\n        if (cf->cycle->modules[m]->type != NGX_STREAM_MODULE) {\n            continue;\n        }\n\n        module = cf->cycle->modules[m]->ctx;\n\n        if (module->create_srv_conf) {\n            mconf = module->create_srv_conf(cf);\n            if (mconf == NULL) {\n                return NGX_CONF_ERROR;\n            }\n\n            ctx->srv_conf[cf->cycle->modules[m]->ctx_index] = mconf;\n        }\n    }\n\n    /* the server configuration context */\n\n    cscf = ctx->srv_conf[ngx_stream_core_module.ctx_index];\n    cscf->ctx = ctx;\n\n    cmcf = ctx->main_conf[ngx_stream_core_module.ctx_index];\n\n    cscfp = ngx_array_push(&cmcf->servers);\n    if (cscfp == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    *cscfp = cscf;\n\n\n    /* parse inside server{} */\n\n    pcf = *cf;\n    cf->ctx = ctx;\n    cf->cmd_type = NGX_STREAM_SRV_CONF;\n\n    rv = ngx_conf_parse(cf, NULL);\n\n    *cf = pcf;\n\n    if (rv == NGX_CONF_OK && !cscf->listen) {\n        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                      \"no \\\"listen\\\" is defined for server in %s:%ui\",\n                      cscf->file_name, cscf->line);\n        return NGX_CONF_ERROR;\n    }\n\n    return rv;\n}\n\n\nstatic char *\nngx_stream_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_stream_core_srv_conf_t  *cscf = conf;\n\n    ngx_str_t                    *value, size;\n    ngx_url_t                     u;\n    ngx_uint_t                    i, n, backlog;\n    ngx_stream_listen_t          *ls, *als, *nls;\n    ngx_stream_core_main_conf_t  *cmcf;\n\n    cscf->listen = 1;\n\n    value = cf->args->elts;\n\n    ngx_memzero(&u, sizeof(ngx_url_t));\n\n    u.url = value[1];\n    u.listen = 1;\n\n    if (ngx_parse_url(cf->pool, &u) != NGX_OK) {\n        if (u.err) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"%s in \\\"%V\\\" of the \\\"listen\\\" directive\",\n                               u.err, &u.url);\n        }\n\n        return NGX_CONF_ERROR;\n    }\n\n    cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);\n\n    ls = ngx_array_push(&cmcf->listen);\n    if (ls == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_memzero(ls, sizeof(ngx_stream_listen_t));\n\n    ls->backlog = NGX_LISTEN_BACKLOG;\n    ls->rcvbuf = -1;\n    ls->sndbuf = -1;\n    ls->type = SOCK_STREAM;\n    ls->ctx = cf->ctx;\n\n#if (NGX_HAVE_TCP_FASTOPEN)\n    ls->fastopen = -1;\n#endif\n\n#if (NGX_HAVE_INET6)\n    ls->ipv6only = 1;\n#endif\n\n    backlog = 0;\n\n    for (i = 2; i < cf->args->nelts; i++) {\n\n#if !(NGX_WIN32)\n        if (ngx_strcmp(value[i].data, \"udp\") == 0) {\n            ls->type = SOCK_DGRAM;\n            continue;\n        }\n#endif\n\n#if (T_NGX_STREAM_SNI)\n        if (ngx_strcmp(value[i].data, \"default_server\") == 0\n            || ngx_strcmp(value[i].data, \"default\") == 0) {\n\n            ls->default_server = 1;\n            continue;\n        }\n#endif\n\n        if (ngx_strcmp(value[i].data, \"bind\") == 0) {\n            ls->bind = 1;\n            continue;\n        }\n\n#if (NGX_HAVE_TCP_FASTOPEN)\n        if (ngx_strncmp(value[i].data, \"fastopen=\", 9) == 0) {\n            ls->fastopen = ngx_atoi(value[i].data + 9, value[i].len - 9);\n            ls->bind = 1;\n\n            if (ls->fastopen == NGX_ERROR) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"invalid fastopen \\\"%V\\\"\", &value[i]);\n                return NGX_CONF_ERROR;\n            }\n\n            continue;\n        }\n#endif\n\n        if (ngx_strncmp(value[i].data, \"backlog=\", 8) == 0) {\n            ls->backlog = ngx_atoi(value[i].data + 8, value[i].len - 8);\n            ls->bind = 1;\n\n            if (ls->backlog == NGX_ERROR || ls->backlog == 0) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"invalid backlog \\\"%V\\\"\", &value[i]);\n                return NGX_CONF_ERROR;\n            }\n\n            backlog = 1;\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"rcvbuf=\", 7) == 0) {\n            size.len = value[i].len - 7;\n            size.data = value[i].data + 7;\n\n            ls->rcvbuf = ngx_parse_size(&size);\n            ls->bind = 1;\n\n            if (ls->rcvbuf == NGX_ERROR) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"invalid rcvbuf \\\"%V\\\"\", &value[i]);\n                return NGX_CONF_ERROR;\n            }\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"sndbuf=\", 7) == 0) {\n            size.len = value[i].len - 7;\n            size.data = value[i].data + 7;\n\n            ls->sndbuf = ngx_parse_size(&size);\n            ls->bind = 1;\n\n            if (ls->sndbuf == NGX_ERROR) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"invalid sndbuf \\\"%V\\\"\", &value[i]);\n                return NGX_CONF_ERROR;\n            }\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"ipv6only=o\", 10) == 0) {\n#if (NGX_HAVE_INET6 && defined IPV6_V6ONLY)\n            if (ngx_strcmp(&value[i].data[10], \"n\") == 0) {\n                ls->ipv6only = 1;\n\n            } else if (ngx_strcmp(&value[i].data[10], \"ff\") == 0) {\n                ls->ipv6only = 0;\n\n            } else {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"invalid ipv6only flags \\\"%s\\\"\",\n                                   &value[i].data[9]);\n                return NGX_CONF_ERROR;\n            }\n\n            ls->bind = 1;\n            continue;\n#else\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"bind ipv6only is not supported \"\n                               \"on this platform\");\n            return NGX_CONF_ERROR;\n#endif\n        }\n\n        if (ngx_strcmp(value[i].data, \"reuseport\") == 0) {\n#if (NGX_HAVE_REUSEPORT)\n            ls->reuseport = 1;\n            ls->bind = 1;\n#else\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"reuseport is not supported \"\n                               \"on this platform, ignored\");\n#endif\n            continue;\n        }\n\n        if (ngx_strcmp(value[i].data, \"ssl\") == 0) {\n#if (NGX_STREAM_SSL)\n            ngx_stream_ssl_conf_t  *sslcf;\n\n            sslcf = ngx_stream_conf_get_module_srv_conf(cf,\n                                                        ngx_stream_ssl_module);\n\n            sslcf->listen = 1;\n            sslcf->file = cf->conf_file->file.name.data;\n            sslcf->line = cf->conf_file->line;\n\n            ls->ssl = 1;\n\n            continue;\n#else\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"the \\\"ssl\\\" parameter requires \"\n                               \"ngx_stream_ssl_module\");\n            return NGX_CONF_ERROR;\n#endif\n        }\n\n        if (ngx_strncmp(value[i].data, \"so_keepalive=\", 13) == 0) {\n\n            if (ngx_strcmp(&value[i].data[13], \"on\") == 0) {\n                ls->so_keepalive = 1;\n\n            } else if (ngx_strcmp(&value[i].data[13], \"off\") == 0) {\n                ls->so_keepalive = 2;\n\n            } else {\n\n#if (NGX_HAVE_KEEPALIVE_TUNABLE)\n                u_char     *p, *end;\n                ngx_str_t   s;\n\n                end = value[i].data + value[i].len;\n                s.data = value[i].data + 13;\n\n                p = ngx_strlchr(s.data, end, ':');\n                if (p == NULL) {\n                    p = end;\n                }\n\n                if (p > s.data) {\n                    s.len = p - s.data;\n\n                    ls->tcp_keepidle = ngx_parse_time(&s, 1);\n                    if (ls->tcp_keepidle == (time_t) NGX_ERROR) {\n                        goto invalid_so_keepalive;\n                    }\n                }\n\n                s.data = (p < end) ? (p + 1) : end;\n\n                p = ngx_strlchr(s.data, end, ':');\n                if (p == NULL) {\n                    p = end;\n                }\n\n                if (p > s.data) {\n                    s.len = p - s.data;\n\n                    ls->tcp_keepintvl = ngx_parse_time(&s, 1);\n                    if (ls->tcp_keepintvl == (time_t) NGX_ERROR) {\n                        goto invalid_so_keepalive;\n                    }\n                }\n\n                s.data = (p < end) ? (p + 1) : end;\n\n                if (s.data < end) {\n                    s.len = end - s.data;\n\n                    ls->tcp_keepcnt = ngx_atoi(s.data, s.len);\n                    if (ls->tcp_keepcnt == NGX_ERROR) {\n                        goto invalid_so_keepalive;\n                    }\n                }\n\n                if (ls->tcp_keepidle == 0 && ls->tcp_keepintvl == 0\n                    && ls->tcp_keepcnt == 0)\n                {\n                    goto invalid_so_keepalive;\n                }\n\n                ls->so_keepalive = 1;\n\n#else\n\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"the \\\"so_keepalive\\\" parameter accepts \"\n                                   \"only \\\"on\\\" or \\\"off\\\" on this platform\");\n                return NGX_CONF_ERROR;\n\n#endif\n            }\n\n            ls->bind = 1;\n\n            continue;\n\n#if (NGX_HAVE_KEEPALIVE_TUNABLE)\n        invalid_so_keepalive:\n\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"invalid so_keepalive value: \\\"%s\\\"\",\n                               &value[i].data[13]);\n            return NGX_CONF_ERROR;\n#endif\n        }\n\n        if (ngx_strcmp(value[i].data, \"proxy_protocol\") == 0) {\n            ls->proxy_protocol = 1;\n            continue;\n        }\n\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"the invalid \\\"%V\\\" parameter\", &value[i]);\n        return NGX_CONF_ERROR;\n    }\n\n    if (ls->type == SOCK_DGRAM) {\n        if (backlog) {\n            return \"\\\"backlog\\\" parameter is incompatible with \\\"udp\\\"\";\n        }\n\n#if (NGX_STREAM_SSL)\n#if !(T_NGX_HAVE_DTLS)\n        if (ls->ssl) {\n            return \"\\\"ssl\\\" parameter is incompatible with \\\"udp\\\"\";\n        }\n#endif\n#endif\n\n        if (ls->so_keepalive) {\n            return \"\\\"so_keepalive\\\" parameter is incompatible with \\\"udp\\\"\";\n        }\n\n        if (ls->proxy_protocol) {\n            return \"\\\"proxy_protocol\\\" parameter is incompatible with \\\"udp\\\"\";\n        }\n\n#if (NGX_HAVE_TCP_FASTOPEN)\n        if (ls->fastopen != -1) {\n            return \"\\\"fastopen\\\" parameter is incompatible with \\\"udp\\\"\";\n        }\n#endif\n    }\n\n    for (n = 0; n < u.naddrs; n++) {\n\n        for (i = 0; i < n; i++) {\n            if (ngx_cmp_sockaddr(u.addrs[n].sockaddr, u.addrs[n].socklen,\n                                 u.addrs[i].sockaddr, u.addrs[i].socklen, 1)\n                == NGX_OK)\n            {\n                goto next;\n            }\n        }\n\n        if (n != 0) {\n            nls = ngx_array_push(&cmcf->listen);\n            if (nls == NULL) {\n                return NGX_CONF_ERROR;\n            }\n\n            *nls = *ls;\n\n        } else {\n            nls = ls;\n        }\n\n        nls->sockaddr = u.addrs[n].sockaddr;\n        nls->socklen = u.addrs[n].socklen;\n        nls->addr_text = u.addrs[n].name;\n        nls->wildcard = ngx_inet_wildcard(nls->sockaddr);\n\n        als = cmcf->listen.elts;\n\n#if (T_NGX_STREAM_SNI)\n        continue;\n#endif\n\n        for (i = 0; i < cmcf->listen.nelts - 1; i++) {\n            if (nls->type != als[i].type) {\n                continue;\n            }\n\n            if (ngx_cmp_sockaddr(als[i].sockaddr, als[i].socklen,\n                                 nls->sockaddr, nls->socklen, 1)\n                != NGX_OK)\n            {\n                continue;\n            }\n\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"duplicate \\\"%V\\\" address and port pair\",\n                               &nls->addr_text);\n            return NGX_CONF_ERROR;\n        }\n\n    next:\n        continue;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_stream_core_resolver(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_stream_core_srv_conf_t  *cscf = conf;\n\n    ngx_str_t  *value;\n\n    if (cscf->resolver) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    cscf->resolver = ngx_resolver_create(cf, &value[1], cf->args->nelts - 1);\n    if (cscf->resolver == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n#if (T_NGX_STREAM_SNI)\nstatic char *\nngx_stream_core_server_name(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_stream_core_srv_conf_t *cscf = conf;\n\n    u_char                   ch;\n    ngx_str_t               *value;\n    ngx_uint_t               i;\n    ngx_stream_server_name_t  *sn;\n\n    value = cf->args->elts;\n\n    for (i = 1; i < cf->args->nelts; i++) {\n\n        ch = value[i].data[0];\n\n        if ((ch == '*' && (value[i].len < 3 || value[i].data[1] != '.'))\n            || (ch == '.' && value[i].len < 2))\n        {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"server name \\\"%V\\\" is invalid\", &value[i]);\n            return NGX_CONF_ERROR;\n        }\n\n        if (ngx_strchr(value[i].data, '/')) {\n            ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                               \"server name \\\"%V\\\" has suspicious symbols\",\n                               &value[i]);\n        }\n\n        sn = ngx_array_push(&cscf->server_names);\n        if (sn == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        sn->server = cscf;\n\n        if (ngx_strcasecmp(value[i].data, (u_char *) \"$hostname\") == 0) {\n            sn->name = cf->cycle->hostname;\n\n        } else {\n            sn->name = value[i];\n        }\n\n        if (value[i].data[0] != '~') {\n            ngx_strlow(sn->name.data, sn->name.data, sn->name.len);\n            continue;\n        }\n\n    }\n\n    return NGX_CONF_OK;\n}\n#endif\n"
  },
  {
    "path": "src/stream/ngx_stream_geo_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_stream.h>\n\n\ntypedef struct {\n    ngx_stream_variable_value_t       *value;\n    u_short                            start;\n    u_short                            end;\n} ngx_stream_geo_range_t;\n\n\ntypedef struct {\n    ngx_radix_tree_t                  *tree;\n#if (NGX_HAVE_INET6)\n    ngx_radix_tree_t                  *tree6;\n#endif\n} ngx_stream_geo_trees_t;\n\n\ntypedef struct {\n    ngx_stream_geo_range_t           **low;\n    ngx_stream_variable_value_t       *default_value;\n} ngx_stream_geo_high_ranges_t;\n\n\ntypedef struct {\n    ngx_str_node_t                     sn;\n    ngx_stream_variable_value_t       *value;\n    size_t                             offset;\n} ngx_stream_geo_variable_value_node_t;\n\n\ntypedef struct {\n    ngx_stream_variable_value_t       *value;\n    ngx_str_t                         *net;\n    ngx_stream_geo_high_ranges_t       high;\n    ngx_radix_tree_t                  *tree;\n#if (NGX_HAVE_INET6)\n    ngx_radix_tree_t                  *tree6;\n#endif\n    ngx_rbtree_t                       rbtree;\n    ngx_rbtree_node_t                  sentinel;\n    ngx_pool_t                        *pool;\n    ngx_pool_t                        *temp_pool;\n\n    size_t                             data_size;\n\n    ngx_str_t                          include_name;\n    ngx_uint_t                         includes;\n    ngx_uint_t                         entries;\n\n    unsigned                           ranges:1;\n    unsigned                           outside_entries:1;\n    unsigned                           allow_binary_include:1;\n    unsigned                           binary_include:1;\n} ngx_stream_geo_conf_ctx_t;\n\n\ntypedef struct {\n    union {\n        ngx_stream_geo_trees_t         trees;\n        ngx_stream_geo_high_ranges_t   high;\n    } u;\n\n    ngx_int_t                          index;\n} ngx_stream_geo_ctx_t;\n\n\nstatic ngx_int_t ngx_stream_geo_addr(ngx_stream_session_t *s,\n    ngx_stream_geo_ctx_t *ctx, ngx_addr_t *addr);\n\nstatic char *ngx_stream_geo_block(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_stream_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf);\nstatic char *ngx_stream_geo_range(ngx_conf_t *cf,\n    ngx_stream_geo_conf_ctx_t *ctx, ngx_str_t *value);\nstatic char *ngx_stream_geo_add_range(ngx_conf_t *cf,\n    ngx_stream_geo_conf_ctx_t *ctx, in_addr_t start, in_addr_t end);\nstatic ngx_uint_t ngx_stream_geo_delete_range(ngx_conf_t *cf,\n    ngx_stream_geo_conf_ctx_t *ctx, in_addr_t start, in_addr_t end);\nstatic char *ngx_stream_geo_cidr(ngx_conf_t *cf,\n    ngx_stream_geo_conf_ctx_t *ctx, ngx_str_t *value);\nstatic char *ngx_stream_geo_cidr_add(ngx_conf_t *cf,\n    ngx_stream_geo_conf_ctx_t *ctx, ngx_cidr_t *cidr, ngx_str_t *value,\n    ngx_str_t *net);\nstatic ngx_stream_variable_value_t *ngx_stream_geo_value(ngx_conf_t *cf,\n    ngx_stream_geo_conf_ctx_t *ctx, ngx_str_t *value);\nstatic ngx_int_t ngx_stream_geo_cidr_value(ngx_conf_t *cf, ngx_str_t *net,\n    ngx_cidr_t *cidr);\nstatic char *ngx_stream_geo_include(ngx_conf_t *cf,\n    ngx_stream_geo_conf_ctx_t *ctx, ngx_str_t *name);\nstatic ngx_int_t ngx_stream_geo_include_binary_base(ngx_conf_t *cf,\n    ngx_stream_geo_conf_ctx_t *ctx, ngx_str_t *name);\nstatic void ngx_stream_geo_create_binary_base(ngx_stream_geo_conf_ctx_t *ctx);\nstatic u_char *ngx_stream_geo_copy_values(u_char *base, u_char *p,\n    ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);\n\n\nstatic ngx_command_t  ngx_stream_geo_commands[] = {\n\n    { ngx_string(\"geo\"),\n      NGX_STREAM_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE12,\n      ngx_stream_geo_block,\n      0,\n      0,\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_stream_module_t  ngx_stream_geo_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    NULL,                                  /* 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\n\nngx_module_t  ngx_stream_geo_module = {\n    NGX_MODULE_V1,\n    &ngx_stream_geo_module_ctx,            /* module context */\n    ngx_stream_geo_commands,               /* module directives */\n    NGX_STREAM_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    u_char    GEORNG[6];\n    u_char    version;\n    u_char    ptr_size;\n    uint32_t  endianness;\n    uint32_t  crc32;\n} ngx_stream_geo_header_t;\n\n\nstatic ngx_stream_geo_header_t  ngx_stream_geo_header = {\n    { 'G', 'E', 'O', 'R', 'N', 'G' }, 0, sizeof(void *), 0x12345678, 0\n};\n\n\n/* geo range is AF_INET only */\n\nstatic ngx_int_t\nngx_stream_geo_cidr_variable(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data)\n{\n    ngx_stream_geo_ctx_t *ctx = (ngx_stream_geo_ctx_t *) data;\n\n    in_addr_t                     inaddr;\n    ngx_addr_t                    addr;\n    struct sockaddr_in           *sin;\n    ngx_stream_variable_value_t  *vv;\n#if (NGX_HAVE_INET6)\n    u_char                       *p;\n    struct in6_addr              *inaddr6;\n#endif\n\n    if (ngx_stream_geo_addr(s, ctx, &addr) != NGX_OK) {\n        vv = (ngx_stream_variable_value_t *)\n                  ngx_radix32tree_find(ctx->u.trees.tree, INADDR_NONE);\n        goto done;\n    }\n\n    switch (addr.sockaddr->sa_family) {\n\n#if (NGX_HAVE_INET6)\n    case AF_INET6:\n        inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr;\n        p = inaddr6->s6_addr;\n\n        if (IN6_IS_ADDR_V4MAPPED(inaddr6)) {\n            inaddr = p[12] << 24;\n            inaddr += p[13] << 16;\n            inaddr += p[14] << 8;\n            inaddr += p[15];\n\n            vv = (ngx_stream_variable_value_t *)\n                      ngx_radix32tree_find(ctx->u.trees.tree, inaddr);\n\n        } else {\n            vv = (ngx_stream_variable_value_t *)\n                      ngx_radix128tree_find(ctx->u.trees.tree6, p);\n        }\n\n        break;\n#endif\n\n#if (NGX_HAVE_UNIX_DOMAIN)\n    case AF_UNIX:\n        vv = (ngx_stream_variable_value_t *)\n                  ngx_radix32tree_find(ctx->u.trees.tree, INADDR_NONE);\n        break;\n#endif\n\n    default: /* AF_INET */\n        sin = (struct sockaddr_in *) addr.sockaddr;\n        inaddr = ntohl(sin->sin_addr.s_addr);\n\n        vv = (ngx_stream_variable_value_t *)\n                  ngx_radix32tree_find(ctx->u.trees.tree, inaddr);\n\n        break;\n    }\n\ndone:\n\n    *v = *vv;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,\n                   \"stream geo: %v\", v);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_geo_range_variable(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data)\n{\n    ngx_stream_geo_ctx_t *ctx = (ngx_stream_geo_ctx_t *) data;\n\n    in_addr_t                inaddr;\n    ngx_addr_t               addr;\n    ngx_uint_t               n;\n    struct sockaddr_in      *sin;\n    ngx_stream_geo_range_t  *range;\n#if (NGX_HAVE_INET6)\n    u_char                  *p;\n    struct in6_addr         *inaddr6;\n#endif\n\n    *v = *ctx->u.high.default_value;\n\n    if (ngx_stream_geo_addr(s, ctx, &addr) == NGX_OK) {\n\n        switch (addr.sockaddr->sa_family) {\n\n#if (NGX_HAVE_INET6)\n        case AF_INET6:\n            inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr;\n\n            if (IN6_IS_ADDR_V4MAPPED(inaddr6)) {\n                p = inaddr6->s6_addr;\n\n                inaddr = p[12] << 24;\n                inaddr += p[13] << 16;\n                inaddr += p[14] << 8;\n                inaddr += p[15];\n\n            } else {\n                inaddr = INADDR_NONE;\n            }\n\n            break;\n#endif\n\n#if (NGX_HAVE_UNIX_DOMAIN)\n        case AF_UNIX:\n            inaddr = INADDR_NONE;\n            break;\n#endif\n\n        default: /* AF_INET */\n            sin = (struct sockaddr_in *) addr.sockaddr;\n            inaddr = ntohl(sin->sin_addr.s_addr);\n            break;\n        }\n\n    } else {\n        inaddr = INADDR_NONE;\n    }\n\n    if (ctx->u.high.low) {\n        range = ctx->u.high.low[inaddr >> 16];\n\n        if (range) {\n            n = inaddr & 0xffff;\n            do {\n                if (n >= (ngx_uint_t) range->start\n                    && n <= (ngx_uint_t) range->end)\n                {\n                    *v = *range->value;\n                    break;\n                }\n            } while ((++range)->value);\n        }\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,\n                   \"stream geo: %v\", v);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_geo_addr(ngx_stream_session_t *s, ngx_stream_geo_ctx_t *ctx,\n    ngx_addr_t *addr)\n{\n    ngx_stream_variable_value_t  *v;\n\n    if (ctx->index == -1) {\n        ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,\n                       \"stream geo started: %V\", &s->connection->addr_text);\n\n        addr->sockaddr = s->connection->sockaddr;\n        addr->socklen = s->connection->socklen;\n        /* addr->name = s->connection->addr_text; */\n\n        return NGX_OK;\n    }\n\n    v = ngx_stream_get_flushed_variable(s, ctx->index);\n\n    if (v == NULL || v->not_found) {\n        ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,\n                       \"stream geo not found\");\n\n        return NGX_ERROR;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,\n                   \"stream geo started: %v\", v);\n\n    if (ngx_parse_addr(s->connection->pool, addr, v->data, v->len) == NGX_OK) {\n        return NGX_OK;\n    }\n\n    return NGX_ERROR;\n}\n\n\nstatic char *\nngx_stream_geo_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    char                       *rv;\n    size_t                      len;\n    ngx_str_t                  *value, name;\n    ngx_uint_t                  i;\n    ngx_conf_t                  save;\n    ngx_pool_t                 *pool;\n    ngx_array_t                *a;\n    ngx_stream_variable_t      *var;\n    ngx_stream_geo_ctx_t       *geo;\n    ngx_stream_geo_conf_ctx_t   ctx;\n#if (NGX_HAVE_INET6)\n    static struct in6_addr      zero;\n#endif\n\n    value = cf->args->elts;\n\n    geo = ngx_palloc(cf->pool, sizeof(ngx_stream_geo_ctx_t));\n    if (geo == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    name = value[1];\n\n    if (name.data[0] != '$') {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid variable name \\\"%V\\\"\", &name);\n        return NGX_CONF_ERROR;\n    }\n\n    name.len--;\n    name.data++;\n\n    if (cf->args->nelts == 3) {\n\n        geo->index = ngx_stream_get_variable_index(cf, &name);\n        if (geo->index == NGX_ERROR) {\n            return NGX_CONF_ERROR;\n        }\n\n        name = value[2];\n\n        if (name.data[0] != '$') {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"invalid variable name \\\"%V\\\"\", &name);\n            return NGX_CONF_ERROR;\n        }\n\n        name.len--;\n        name.data++;\n\n    } else {\n        geo->index = -1;\n    }\n\n    var = ngx_stream_add_variable(cf, &name, NGX_STREAM_VAR_CHANGEABLE);\n    if (var == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, cf->log);\n    if (pool == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_memzero(&ctx, sizeof(ngx_stream_geo_conf_ctx_t));\n\n    ctx.temp_pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, cf->log);\n    if (ctx.temp_pool == NULL) {\n        ngx_destroy_pool(pool);\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_rbtree_init(&ctx.rbtree, &ctx.sentinel, ngx_str_rbtree_insert_value);\n\n    ctx.pool = cf->pool;\n    ctx.data_size = sizeof(ngx_stream_geo_header_t)\n                  + sizeof(ngx_stream_variable_value_t)\n                  + 0x10000 * sizeof(ngx_stream_geo_range_t *);\n    ctx.allow_binary_include = 1;\n\n    save = *cf;\n    cf->pool = pool;\n    cf->ctx = &ctx;\n    cf->handler = ngx_stream_geo;\n    cf->handler_conf = conf;\n\n    rv = ngx_conf_parse(cf, NULL);\n\n    *cf = save;\n\n    if (rv != NGX_CONF_OK) {\n        goto failed;\n    }\n\n    if (ctx.ranges) {\n\n        if (ctx.high.low && !ctx.binary_include) {\n            for (i = 0; i < 0x10000; i++) {\n                a = (ngx_array_t *) ctx.high.low[i];\n\n                if (a == NULL) {\n                    continue;\n                }\n\n                if (a->nelts == 0) {\n                    ctx.high.low[i] = NULL;\n                    continue;\n                }\n\n                len = a->nelts * sizeof(ngx_stream_geo_range_t);\n\n                ctx.high.low[i] = ngx_palloc(cf->pool, len + sizeof(void *));\n                if (ctx.high.low[i] == NULL) {\n                    goto failed;\n                }\n\n                ngx_memcpy(ctx.high.low[i], a->elts, len);\n                ctx.high.low[i][a->nelts].value = NULL;\n                ctx.data_size += len + sizeof(void *);\n            }\n\n            if (ctx.allow_binary_include\n                && !ctx.outside_entries\n                && ctx.entries > 100000\n                && ctx.includes == 1)\n            {\n                ngx_stream_geo_create_binary_base(&ctx);\n            }\n        }\n\n        if (ctx.high.default_value == NULL) {\n            ctx.high.default_value = &ngx_stream_variable_null_value;\n        }\n\n        geo->u.high = ctx.high;\n\n        var->get_handler = ngx_stream_geo_range_variable;\n        var->data = (uintptr_t) geo;\n\n    } else {\n        if (ctx.tree == NULL) {\n            ctx.tree = ngx_radix_tree_create(cf->pool, -1);\n            if (ctx.tree == NULL) {\n                goto failed;\n            }\n        }\n\n        geo->u.trees.tree = ctx.tree;\n\n#if (NGX_HAVE_INET6)\n        if (ctx.tree6 == NULL) {\n            ctx.tree6 = ngx_radix_tree_create(cf->pool, -1);\n            if (ctx.tree6 == NULL) {\n                goto failed;\n            }\n        }\n\n        geo->u.trees.tree6 = ctx.tree6;\n#endif\n\n        var->get_handler = ngx_stream_geo_cidr_variable;\n        var->data = (uintptr_t) geo;\n\n        if (ngx_radix32tree_insert(ctx.tree, 0, 0,\n                                   (uintptr_t) &ngx_stream_variable_null_value)\n            == NGX_ERROR)\n        {\n            goto failed;\n        }\n\n        /* NGX_BUSY is okay (default was set explicitly) */\n\n#if (NGX_HAVE_INET6)\n        if (ngx_radix128tree_insert(ctx.tree6, zero.s6_addr, zero.s6_addr,\n                                    (uintptr_t) &ngx_stream_variable_null_value)\n            == NGX_ERROR)\n        {\n            goto failed;\n        }\n#endif\n    }\n\n    ngx_destroy_pool(ctx.temp_pool);\n    ngx_destroy_pool(pool);\n\n    return NGX_CONF_OK;\n\nfailed:\n\n    ngx_destroy_pool(ctx.temp_pool);\n    ngx_destroy_pool(pool);\n\n    return NGX_CONF_ERROR;\n}\n\n\nstatic char *\nngx_stream_geo(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)\n{\n    char                       *rv;\n    ngx_str_t                  *value;\n    ngx_stream_geo_conf_ctx_t  *ctx;\n\n    ctx = cf->ctx;\n\n    value = cf->args->elts;\n\n    if (cf->args->nelts == 1) {\n\n        if (ngx_strcmp(value[0].data, \"ranges\") == 0) {\n\n            if (ctx->tree\n#if (NGX_HAVE_INET6)\n                || ctx->tree6\n#endif\n               )\n            {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"the \\\"ranges\\\" directive must be \"\n                                   \"the first directive inside \\\"geo\\\" block\");\n                goto failed;\n            }\n\n            ctx->ranges = 1;\n\n            rv = NGX_CONF_OK;\n\n            goto done;\n        }\n    }\n\n    if (cf->args->nelts != 2) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid number of the geo parameters\");\n        goto failed;\n    }\n\n    if (ngx_strcmp(value[0].data, \"include\") == 0) {\n\n        rv = ngx_stream_geo_include(cf, ctx, &value[1]);\n\n        goto done;\n    }\n\n    if (ctx->ranges) {\n        rv = ngx_stream_geo_range(cf, ctx, value);\n\n    } else {\n        rv = ngx_stream_geo_cidr(cf, ctx, value);\n    }\n\ndone:\n\n    ngx_reset_pool(cf->pool);\n\n    return rv;\n\nfailed:\n\n    ngx_reset_pool(cf->pool);\n\n    return NGX_CONF_ERROR;\n}\n\n\nstatic char *\nngx_stream_geo_range(ngx_conf_t *cf, ngx_stream_geo_conf_ctx_t *ctx,\n    ngx_str_t *value)\n{\n    u_char      *p, *last;\n    in_addr_t    start, end;\n    ngx_str_t   *net;\n    ngx_uint_t   del;\n\n    if (ngx_strcmp(value[0].data, \"default\") == 0) {\n\n        if (ctx->high.default_value) {\n            ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                \"duplicate default geo range value: \\\"%V\\\", old value: \\\"%v\\\"\",\n                &value[1], ctx->high.default_value);\n        }\n\n        ctx->high.default_value = ngx_stream_geo_value(cf, ctx, &value[1]);\n        if (ctx->high.default_value == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        return NGX_CONF_OK;\n    }\n\n    if (ctx->binary_include) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n            \"binary geo range base \\\"%s\\\" cannot be mixed with usual entries\",\n            ctx->include_name.data);\n        return NGX_CONF_ERROR;\n    }\n\n    if (ctx->high.low == NULL) {\n        ctx->high.low = ngx_pcalloc(ctx->pool,\n                                    0x10000 * sizeof(ngx_stream_geo_range_t *));\n        if (ctx->high.low == NULL) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    ctx->entries++;\n    ctx->outside_entries = 1;\n\n    if (ngx_strcmp(value[0].data, \"delete\") == 0) {\n        net = &value[1];\n        del = 1;\n\n    } else {\n        net = &value[0];\n        del = 0;\n    }\n\n    last = net->data + net->len;\n\n    p = ngx_strlchr(net->data, last, '-');\n\n    if (p == NULL) {\n        goto invalid;\n    }\n\n    start = ngx_inet_addr(net->data, p - net->data);\n\n    if (start == INADDR_NONE) {\n        goto invalid;\n    }\n\n    start = ntohl(start);\n\n    p++;\n\n    end = ngx_inet_addr(p, last - p);\n\n    if (end == INADDR_NONE) {\n        goto invalid;\n    }\n\n    end = ntohl(end);\n\n    if (start > end) {\n        goto invalid;\n    }\n\n    if (del) {\n        if (ngx_stream_geo_delete_range(cf, ctx, start, end)) {\n            ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                               \"no address range \\\"%V\\\" to delete\", net);\n        }\n\n        return NGX_CONF_OK;\n    }\n\n    ctx->value = ngx_stream_geo_value(cf, ctx, &value[1]);\n\n    if (ctx->value == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    ctx->net = net;\n\n    return ngx_stream_geo_add_range(cf, ctx, start, end);\n\ninvalid:\n\n    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, \"invalid range \\\"%V\\\"\", net);\n\n    return NGX_CONF_ERROR;\n}\n\n\n/* the add procedure is optimized to add a growing up sequence */\n\nstatic char *\nngx_stream_geo_add_range(ngx_conf_t *cf, ngx_stream_geo_conf_ctx_t *ctx,\n    in_addr_t start, in_addr_t end)\n{\n    in_addr_t                n;\n    ngx_uint_t               h, i, s, e;\n    ngx_array_t             *a;\n    ngx_stream_geo_range_t  *range;\n\n    for (n = start; n <= end; n = (n + 0x10000) & 0xffff0000) {\n\n        h = n >> 16;\n\n        if (n == start) {\n            s = n & 0xffff;\n        } else {\n            s = 0;\n        }\n\n        if ((n | 0xffff) > end) {\n            e = end & 0xffff;\n\n        } else {\n            e = 0xffff;\n        }\n\n        a = (ngx_array_t *) ctx->high.low[h];\n\n        if (a == NULL) {\n            a = ngx_array_create(ctx->temp_pool, 64,\n                                 sizeof(ngx_stream_geo_range_t));\n            if (a == NULL) {\n                return NGX_CONF_ERROR;\n            }\n\n            ctx->high.low[h] = (ngx_stream_geo_range_t *) a;\n        }\n\n        i = a->nelts;\n        range = a->elts;\n\n        while (i) {\n\n            i--;\n\n            if (e < (ngx_uint_t) range[i].start) {\n                continue;\n            }\n\n            if (s > (ngx_uint_t) range[i].end) {\n\n                /* add after the range */\n\n                range = ngx_array_push(a);\n                if (range == NULL) {\n                    return NGX_CONF_ERROR;\n                }\n\n                range = a->elts;\n\n                ngx_memmove(&range[i + 2], &range[i + 1],\n                           (a->nelts - 2 - i) * sizeof(ngx_stream_geo_range_t));\n\n                range[i + 1].start = (u_short) s;\n                range[i + 1].end = (u_short) e;\n                range[i + 1].value = ctx->value;\n\n                goto next;\n            }\n\n            if (s == (ngx_uint_t) range[i].start\n                && e == (ngx_uint_t) range[i].end)\n            {\n                ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                    \"duplicate range \\\"%V\\\", value: \\\"%v\\\", old value: \\\"%v\\\"\",\n                    ctx->net, ctx->value, range[i].value);\n\n                range[i].value = ctx->value;\n\n                goto next;\n            }\n\n            if (s > (ngx_uint_t) range[i].start\n                && e < (ngx_uint_t) range[i].end)\n            {\n                /* split the range and insert the new one */\n\n                range = ngx_array_push(a);\n                if (range == NULL) {\n                    return NGX_CONF_ERROR;\n                }\n\n                range = ngx_array_push(a);\n                if (range == NULL) {\n                    return NGX_CONF_ERROR;\n                }\n\n                range = a->elts;\n\n                ngx_memmove(&range[i + 3], &range[i + 1],\n                           (a->nelts - 3 - i) * sizeof(ngx_stream_geo_range_t));\n\n                range[i + 2].start = (u_short) (e + 1);\n                range[i + 2].end = range[i].end;\n                range[i + 2].value = range[i].value;\n\n                range[i + 1].start = (u_short) s;\n                range[i + 1].end = (u_short) e;\n                range[i + 1].value = ctx->value;\n\n                range[i].end = (u_short) (s - 1);\n\n                goto next;\n            }\n\n            if (s == (ngx_uint_t) range[i].start\n                && e < (ngx_uint_t) range[i].end)\n            {\n                /* shift the range start and insert the new range */\n\n                range = ngx_array_push(a);\n                if (range == NULL) {\n                    return NGX_CONF_ERROR;\n                }\n\n                range = a->elts;\n\n                ngx_memmove(&range[i + 1], &range[i],\n                           (a->nelts - 1 - i) * sizeof(ngx_stream_geo_range_t));\n\n                range[i + 1].start = (u_short) (e + 1);\n\n                range[i].start = (u_short) s;\n                range[i].end = (u_short) e;\n                range[i].value = ctx->value;\n\n                goto next;\n            }\n\n            if (s > (ngx_uint_t) range[i].start\n                && e == (ngx_uint_t) range[i].end)\n            {\n                /* shift the range end and insert the new range */\n\n                range = ngx_array_push(a);\n                if (range == NULL) {\n                    return NGX_CONF_ERROR;\n                }\n\n                range = a->elts;\n\n                ngx_memmove(&range[i + 2], &range[i + 1],\n                           (a->nelts - 2 - i) * sizeof(ngx_stream_geo_range_t));\n\n                range[i + 1].start = (u_short) s;\n                range[i + 1].end = (u_short) e;\n                range[i + 1].value = ctx->value;\n\n                range[i].end = (u_short) (s - 1);\n\n                goto next;\n            }\n\n            s = (ngx_uint_t) range[i].start;\n            e = (ngx_uint_t) range[i].end;\n\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                         \"range \\\"%V\\\" overlaps \\\"%d.%d.%d.%d-%d.%d.%d.%d\\\"\",\n                         ctx->net,\n                         h >> 8, h & 0xff, s >> 8, s & 0xff,\n                         h >> 8, h & 0xff, e >> 8, e & 0xff);\n\n            return NGX_CONF_ERROR;\n        }\n\n        /* add the first range */\n\n        range = ngx_array_push(a);\n        if (range == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        range = a->elts;\n\n        ngx_memmove(&range[1], &range[0],\n                    (a->nelts - 1) * sizeof(ngx_stream_geo_range_t));\n\n        range[0].start = (u_short) s;\n        range[0].end = (u_short) e;\n        range[0].value = ctx->value;\n\n    next:\n\n        if (h == 0xffff) {\n            break;\n        }\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_uint_t\nngx_stream_geo_delete_range(ngx_conf_t *cf, ngx_stream_geo_conf_ctx_t *ctx,\n    in_addr_t start, in_addr_t end)\n{\n    in_addr_t                n;\n    ngx_uint_t               h, i, s, e, warn;\n    ngx_array_t             *a;\n    ngx_stream_geo_range_t  *range;\n\n    warn = 0;\n\n    for (n = start; n <= end; n = (n + 0x10000) & 0xffff0000) {\n\n        h = n >> 16;\n\n        if (n == start) {\n            s = n & 0xffff;\n        } else {\n            s = 0;\n        }\n\n        if ((n | 0xffff) > end) {\n            e = end & 0xffff;\n\n        } else {\n            e = 0xffff;\n        }\n\n        a = (ngx_array_t *) ctx->high.low[h];\n\n        if (a == NULL || a->nelts == 0) {\n            warn = 1;\n            goto next;\n        }\n\n        range = a->elts;\n        for (i = 0; i < a->nelts; i++) {\n\n            if (s == (ngx_uint_t) range[i].start\n                && e == (ngx_uint_t) range[i].end)\n            {\n                ngx_memmove(&range[i], &range[i + 1],\n                           (a->nelts - 1 - i) * sizeof(ngx_stream_geo_range_t));\n\n                a->nelts--;\n\n                break;\n            }\n\n            if (i == a->nelts - 1) {\n                warn = 1;\n            }\n        }\n\n    next:\n\n        if (h == 0xffff) {\n            break;\n        }\n    }\n\n    return warn;\n}\n\n\nstatic char *\nngx_stream_geo_cidr(ngx_conf_t *cf, ngx_stream_geo_conf_ctx_t *ctx,\n    ngx_str_t *value)\n{\n    char        *rv;\n    ngx_int_t    rc, del;\n    ngx_str_t   *net;\n    ngx_cidr_t   cidr;\n\n    if (ctx->tree == NULL) {\n        ctx->tree = ngx_radix_tree_create(ctx->pool, -1);\n        if (ctx->tree == NULL) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n#if (NGX_HAVE_INET6)\n    if (ctx->tree6 == NULL) {\n        ctx->tree6 = ngx_radix_tree_create(ctx->pool, -1);\n        if (ctx->tree6 == NULL) {\n            return NGX_CONF_ERROR;\n        }\n    }\n#endif\n\n    if (ngx_strcmp(value[0].data, \"default\") == 0) {\n        cidr.family = AF_INET;\n        cidr.u.in.addr = 0;\n        cidr.u.in.mask = 0;\n\n        rv = ngx_stream_geo_cidr_add(cf, ctx, &cidr, &value[1], &value[0]);\n\n        if (rv != NGX_CONF_OK) {\n            return rv;\n        }\n\n#if (NGX_HAVE_INET6)\n        cidr.family = AF_INET6;\n        ngx_memzero(&cidr.u.in6, sizeof(ngx_in6_cidr_t));\n\n        rv = ngx_stream_geo_cidr_add(cf, ctx, &cidr, &value[1], &value[0]);\n\n        if (rv != NGX_CONF_OK) {\n            return rv;\n        }\n#endif\n\n        return NGX_CONF_OK;\n    }\n\n    if (ngx_strcmp(value[0].data, \"delete\") == 0) {\n        net = &value[1];\n        del = 1;\n\n    } else {\n        net = &value[0];\n        del = 0;\n    }\n\n    if (ngx_stream_geo_cidr_value(cf, net, &cidr) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (cidr.family == AF_INET) {\n        cidr.u.in.addr = ntohl(cidr.u.in.addr);\n        cidr.u.in.mask = ntohl(cidr.u.in.mask);\n    }\n\n    if (del) {\n        switch (cidr.family) {\n\n#if (NGX_HAVE_INET6)\n        case AF_INET6:\n            rc = ngx_radix128tree_delete(ctx->tree6,\n                                         cidr.u.in6.addr.s6_addr,\n                                         cidr.u.in6.mask.s6_addr);\n            break;\n#endif\n\n        default: /* AF_INET */\n            rc = ngx_radix32tree_delete(ctx->tree, cidr.u.in.addr,\n                                        cidr.u.in.mask);\n            break;\n        }\n\n        if (rc != NGX_OK) {\n            ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                               \"no network \\\"%V\\\" to delete\", net);\n        }\n\n        return NGX_CONF_OK;\n    }\n\n    return ngx_stream_geo_cidr_add(cf, ctx, &cidr, &value[1], net);\n}\n\n\nstatic char *\nngx_stream_geo_cidr_add(ngx_conf_t *cf, ngx_stream_geo_conf_ctx_t *ctx,\n    ngx_cidr_t *cidr, ngx_str_t *value, ngx_str_t *net)\n{\n    ngx_int_t                     rc;\n    ngx_stream_variable_value_t  *val, *old;\n\n    val = ngx_stream_geo_value(cf, ctx, value);\n\n    if (val == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    switch (cidr->family) {\n\n#if (NGX_HAVE_INET6)\n    case AF_INET6:\n        rc = ngx_radix128tree_insert(ctx->tree6, cidr->u.in6.addr.s6_addr,\n                                     cidr->u.in6.mask.s6_addr,\n                                     (uintptr_t) val);\n\n        if (rc == NGX_OK) {\n            return NGX_CONF_OK;\n        }\n\n        if (rc == NGX_ERROR) {\n            return NGX_CONF_ERROR;\n        }\n\n        /* rc == NGX_BUSY */\n\n        old = (ngx_stream_variable_value_t *)\n                   ngx_radix128tree_find(ctx->tree6,\n                                         cidr->u.in6.addr.s6_addr);\n\n        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n              \"duplicate network \\\"%V\\\", value: \\\"%v\\\", old value: \\\"%v\\\"\",\n              net, val, old);\n\n        rc = ngx_radix128tree_delete(ctx->tree6,\n                                     cidr->u.in6.addr.s6_addr,\n                                     cidr->u.in6.mask.s6_addr);\n\n        if (rc == NGX_ERROR) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, \"invalid radix tree\");\n            return NGX_CONF_ERROR;\n        }\n\n        rc = ngx_radix128tree_insert(ctx->tree6, cidr->u.in6.addr.s6_addr,\n                                     cidr->u.in6.mask.s6_addr,\n                                     (uintptr_t) val);\n\n        break;\n#endif\n\n    default: /* AF_INET */\n        rc = ngx_radix32tree_insert(ctx->tree, cidr->u.in.addr,\n                                    cidr->u.in.mask, (uintptr_t) val);\n\n        if (rc == NGX_OK) {\n            return NGX_CONF_OK;\n        }\n\n        if (rc == NGX_ERROR) {\n            return NGX_CONF_ERROR;\n        }\n\n        /* rc == NGX_BUSY */\n\n        old = (ngx_stream_variable_value_t *)\n                   ngx_radix32tree_find(ctx->tree, cidr->u.in.addr);\n\n        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n              \"duplicate network \\\"%V\\\", value: \\\"%v\\\", old value: \\\"%v\\\"\",\n              net, val, old);\n\n        rc = ngx_radix32tree_delete(ctx->tree,\n                                    cidr->u.in.addr, cidr->u.in.mask);\n\n        if (rc == NGX_ERROR) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, \"invalid radix tree\");\n            return NGX_CONF_ERROR;\n        }\n\n        rc = ngx_radix32tree_insert(ctx->tree, cidr->u.in.addr,\n                                    cidr->u.in.mask, (uintptr_t) val);\n\n        break;\n    }\n\n    if (rc == NGX_OK) {\n        return NGX_CONF_OK;\n    }\n\n    return NGX_CONF_ERROR;\n}\n\n\nstatic ngx_stream_variable_value_t *\nngx_stream_geo_value(ngx_conf_t *cf, ngx_stream_geo_conf_ctx_t *ctx,\n    ngx_str_t *value)\n{\n    uint32_t                               hash;\n    ngx_stream_variable_value_t           *val;\n    ngx_stream_geo_variable_value_node_t  *gvvn;\n\n    hash = ngx_crc32_long(value->data, value->len);\n\n    gvvn = (ngx_stream_geo_variable_value_node_t *)\n               ngx_str_rbtree_lookup(&ctx->rbtree, value, hash);\n\n    if (gvvn) {\n        return gvvn->value;\n    }\n\n    val = ngx_palloc(ctx->pool, sizeof(ngx_stream_variable_value_t));\n    if (val == NULL) {\n        return NULL;\n    }\n\n    val->len = value->len;\n    val->data = ngx_pstrdup(ctx->pool, value);\n    if (val->data == NULL) {\n        return NULL;\n    }\n\n    val->valid = 1;\n    val->no_cacheable = 0;\n    val->not_found = 0;\n\n    gvvn = ngx_palloc(ctx->temp_pool,\n                      sizeof(ngx_stream_geo_variable_value_node_t));\n    if (gvvn == NULL) {\n        return NULL;\n    }\n\n    gvvn->sn.node.key = hash;\n    gvvn->sn.str.len = val->len;\n    gvvn->sn.str.data = val->data;\n    gvvn->value = val;\n    gvvn->offset = 0;\n\n    ngx_rbtree_insert(&ctx->rbtree, &gvvn->sn.node);\n\n    ctx->data_size += ngx_align(sizeof(ngx_stream_variable_value_t)\n                                + value->len, sizeof(void *));\n\n    return val;\n}\n\n\nstatic ngx_int_t\nngx_stream_geo_cidr_value(ngx_conf_t *cf, ngx_str_t *net, ngx_cidr_t *cidr)\n{\n    ngx_int_t  rc;\n\n    if (ngx_strcmp(net->data, \"255.255.255.255\") == 0) {\n        cidr->family = AF_INET;\n        cidr->u.in.addr = 0xffffffff;\n        cidr->u.in.mask = 0xffffffff;\n\n        return NGX_OK;\n    }\n\n    rc = ngx_ptocidr(net, cidr);\n\n    if (rc == NGX_ERROR) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, \"invalid network \\\"%V\\\"\", net);\n        return NGX_ERROR;\n    }\n\n    if (rc == NGX_DONE) {\n        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                           \"low address bits of %V are meaningless\", net);\n    }\n\n    return NGX_OK;\n}\n\n\nstatic char *\nngx_stream_geo_include(ngx_conf_t *cf, ngx_stream_geo_conf_ctx_t *ctx,\n    ngx_str_t *name)\n{\n    char       *rv;\n    ngx_str_t   file;\n\n    file.len = name->len + 4;\n    file.data = ngx_pnalloc(ctx->temp_pool, name->len + 5);\n    if (file.data == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_sprintf(file.data, \"%V.bin%Z\", name);\n\n    if (ngx_conf_full_name(cf->cycle, &file, 1) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (ctx->ranges) {\n        ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, \"include %s\", file.data);\n\n        switch (ngx_stream_geo_include_binary_base(cf, ctx, &file)) {\n        case NGX_OK:\n            return NGX_CONF_OK;\n        case NGX_ERROR:\n            return NGX_CONF_ERROR;\n        default:\n            break;\n        }\n    }\n\n    file.len -= 4;\n    file.data[file.len] = '\\0';\n\n    ctx->include_name = file;\n\n    if (ctx->outside_entries) {\n        ctx->allow_binary_include = 0;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_CORE, cf->log, 0, \"include %s\", file.data);\n\n    rv = ngx_conf_parse(cf, &file);\n\n    ctx->includes++;\n    ctx->outside_entries = 0;\n\n    return rv;\n}\n\n\nstatic ngx_int_t\nngx_stream_geo_include_binary_base(ngx_conf_t *cf,\n    ngx_stream_geo_conf_ctx_t *ctx, ngx_str_t *name)\n{\n    u_char                       *base, ch;\n    time_t                        mtime;\n    size_t                        size, len;\n    ssize_t                       n;\n    uint32_t                      crc32;\n    ngx_err_t                     err;\n    ngx_int_t                     rc;\n    ngx_uint_t                    i;\n    ngx_file_t                    file;\n    ngx_file_info_t               fi;\n    ngx_stream_geo_range_t       *range, **ranges;\n    ngx_stream_geo_header_t      *header;\n    ngx_stream_variable_value_t  *vv;\n\n    ngx_memzero(&file, sizeof(ngx_file_t));\n    file.name = *name;\n    file.log = cf->log;\n\n    file.fd = ngx_open_file(name->data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0);\n\n    if (file.fd == NGX_INVALID_FILE) {\n        err = ngx_errno;\n        if (err != NGX_ENOENT) {\n            ngx_conf_log_error(NGX_LOG_CRIT, cf, err,\n                               ngx_open_file_n \" \\\"%s\\\" failed\", name->data);\n        }\n        return NGX_DECLINED;\n    }\n\n    if (ctx->outside_entries) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n            \"binary geo range base \\\"%s\\\" cannot be mixed with usual entries\",\n            name->data);\n        rc = NGX_ERROR;\n        goto done;\n    }\n\n    if (ctx->binary_include) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n            \"second binary geo range base \\\"%s\\\" cannot be mixed with \\\"%s\\\"\",\n            name->data, ctx->include_name.data);\n        rc = NGX_ERROR;\n        goto done;\n    }\n\n    if (ngx_fd_info(file.fd, &fi) == NGX_FILE_ERROR) {\n        ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,\n                           ngx_fd_info_n \" \\\"%s\\\" failed\", name->data);\n        goto failed;\n    }\n\n    size = (size_t) ngx_file_size(&fi);\n    mtime = ngx_file_mtime(&fi);\n\n    ch = name->data[name->len - 4];\n    name->data[name->len - 4] = '\\0';\n\n    if (ngx_file_info(name->data, &fi) == NGX_FILE_ERROR) {\n        ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,\n                           ngx_file_info_n \" \\\"%s\\\" failed\", name->data);\n        goto failed;\n    }\n\n    name->data[name->len - 4] = ch;\n\n    if (mtime < ngx_file_mtime(&fi)) {\n        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                           \"stale binary geo range base \\\"%s\\\"\", name->data);\n        goto failed;\n    }\n\n    base = ngx_palloc(ctx->pool, size);\n    if (base == NULL) {\n        goto failed;\n    }\n\n    n = ngx_read_file(&file, base, size, 0);\n\n    if (n == NGX_ERROR) {\n        ngx_conf_log_error(NGX_LOG_CRIT, cf, ngx_errno,\n                           ngx_read_file_n \" \\\"%s\\\" failed\", name->data);\n        goto failed;\n    }\n\n    if ((size_t) n != size) {\n        ngx_conf_log_error(NGX_LOG_CRIT, cf, 0,\n            ngx_read_file_n \" \\\"%s\\\" returned only %z bytes instead of %z\",\n            name->data, n, size);\n        goto failed;\n    }\n\n    header = (ngx_stream_geo_header_t *) base;\n\n    if (size < 16 || ngx_memcmp(&ngx_stream_geo_header, header, 12) != 0) {\n        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n             \"incompatible binary geo range base \\\"%s\\\"\", name->data);\n        goto failed;\n    }\n\n    ngx_crc32_init(crc32);\n\n    vv = (ngx_stream_variable_value_t *)\n            (base + sizeof(ngx_stream_geo_header_t));\n\n    while (vv->data) {\n        len = ngx_align(sizeof(ngx_stream_variable_value_t) + vv->len,\n                        sizeof(void *));\n        ngx_crc32_update(&crc32, (u_char *) vv, len);\n        vv->data += (size_t) base;\n        vv = (ngx_stream_variable_value_t *) ((u_char *) vv + len);\n    }\n    ngx_crc32_update(&crc32, (u_char *) vv,\n                     sizeof(ngx_stream_variable_value_t));\n    vv++;\n\n    ranges = (ngx_stream_geo_range_t **) vv;\n\n    for (i = 0; i < 0x10000; i++) {\n        ngx_crc32_update(&crc32, (u_char *) &ranges[i], sizeof(void *));\n        if (ranges[i]) {\n            ranges[i] = (ngx_stream_geo_range_t *)\n                            ((u_char *) ranges[i] + (size_t) base);\n        }\n    }\n\n    range = (ngx_stream_geo_range_t *) &ranges[0x10000];\n\n    while ((u_char *) range < base + size) {\n        while (range->value) {\n            ngx_crc32_update(&crc32, (u_char *) range,\n                             sizeof(ngx_stream_geo_range_t));\n            range->value = (ngx_stream_variable_value_t *)\n                               ((u_char *) range->value + (size_t) base);\n            range++;\n        }\n        ngx_crc32_update(&crc32, (u_char *) range, sizeof(void *));\n        range = (ngx_stream_geo_range_t *) ((u_char *) range + sizeof(void *));\n    }\n\n    ngx_crc32_final(crc32);\n\n    if (crc32 != header->crc32) {\n        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                  \"CRC32 mismatch in binary geo range base \\\"%s\\\"\", name->data);\n        goto failed;\n    }\n\n    ngx_conf_log_error(NGX_LOG_NOTICE, cf, 0,\n                       \"using binary geo range base \\\"%s\\\"\", name->data);\n\n    ctx->include_name = *name;\n    ctx->binary_include = 1;\n    ctx->high.low = ranges;\n    rc = NGX_OK;\n\n    goto done;\n\nfailed:\n\n    rc = NGX_DECLINED;\n\ndone:\n\n    if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {\n        ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno,\n                      ngx_close_file_n \" \\\"%s\\\" failed\", name->data);\n    }\n\n    return rc;\n}\n\n\nstatic void\nngx_stream_geo_create_binary_base(ngx_stream_geo_conf_ctx_t *ctx)\n{\n    u_char                                *p;\n    uint32_t                               hash;\n    ngx_str_t                              s;\n    ngx_uint_t                             i;\n    ngx_file_mapping_t                     fm;\n    ngx_stream_geo_range_t                *r, *range, **ranges;\n    ngx_stream_geo_header_t               *header;\n    ngx_stream_geo_variable_value_node_t  *gvvn;\n\n    fm.name = ngx_pnalloc(ctx->temp_pool, ctx->include_name.len + 5);\n    if (fm.name == NULL) {\n        return;\n    }\n\n    ngx_sprintf(fm.name, \"%V.bin%Z\", &ctx->include_name);\n\n    fm.size = ctx->data_size;\n    fm.log = ctx->pool->log;\n\n    ngx_log_error(NGX_LOG_NOTICE, fm.log, 0,\n                  \"creating binary geo range base \\\"%s\\\"\", fm.name);\n\n    if (ngx_create_file_mapping(&fm) != NGX_OK) {\n        return;\n    }\n\n    p = ngx_cpymem(fm.addr, &ngx_stream_geo_header,\n                   sizeof(ngx_stream_geo_header_t));\n\n    p = ngx_stream_geo_copy_values(fm.addr, p, ctx->rbtree.root,\n                                   ctx->rbtree.sentinel);\n\n    p += sizeof(ngx_stream_variable_value_t);\n\n    ranges = (ngx_stream_geo_range_t **) p;\n\n    p += 0x10000 * sizeof(ngx_stream_geo_range_t *);\n\n    for (i = 0; i < 0x10000; i++) {\n        r = ctx->high.low[i];\n        if (r == NULL) {\n            continue;\n        }\n\n        range = (ngx_stream_geo_range_t *) p;\n        ranges[i] = (ngx_stream_geo_range_t *) (p - (u_char *) fm.addr);\n\n        do {\n            s.len = r->value->len;\n            s.data = r->value->data;\n            hash = ngx_crc32_long(s.data, s.len);\n            gvvn = (ngx_stream_geo_variable_value_node_t *)\n                        ngx_str_rbtree_lookup(&ctx->rbtree, &s, hash);\n\n            range->value = (ngx_stream_variable_value_t *) gvvn->offset;\n            range->start = r->start;\n            range->end = r->end;\n            range++;\n\n        } while ((++r)->value);\n\n        range->value = NULL;\n\n        p = (u_char *) range + sizeof(void *);\n    }\n\n    header = fm.addr;\n    header->crc32 = ngx_crc32_long((u_char *) fm.addr\n                                       + sizeof(ngx_stream_geo_header_t),\n                                   fm.size - sizeof(ngx_stream_geo_header_t));\n\n    ngx_close_file_mapping(&fm);\n}\n\n\nstatic u_char *\nngx_stream_geo_copy_values(u_char *base, u_char *p, ngx_rbtree_node_t *node,\n    ngx_rbtree_node_t *sentinel)\n{\n    ngx_stream_variable_value_t           *vv;\n    ngx_stream_geo_variable_value_node_t  *gvvn;\n\n    if (node == sentinel) {\n        return p;\n    }\n\n    gvvn = (ngx_stream_geo_variable_value_node_t *) node;\n    gvvn->offset = p - base;\n\n    vv = (ngx_stream_variable_value_t *) p;\n    *vv = *gvvn->value;\n    p += sizeof(ngx_stream_variable_value_t);\n    vv->data = (u_char *) (p - base);\n\n    p = ngx_cpymem(p, gvvn->sn.str.data, gvvn->sn.str.len);\n\n    p = ngx_align_ptr(p, sizeof(void *));\n\n    p = ngx_stream_geo_copy_values(base, p, node->left, sentinel);\n\n    return ngx_stream_geo_copy_values(base, p, node->right, sentinel);\n}\n"
  },
  {
    "path": "src/stream/ngx_stream_geoip_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_stream.h>\n\n#include <GeoIP.h>\n#include <GeoIPCity.h>\n\n\n#define NGX_GEOIP_COUNTRY_CODE   0\n#define NGX_GEOIP_COUNTRY_CODE3  1\n#define NGX_GEOIP_COUNTRY_NAME   2\n\n\ntypedef struct {\n    GeoIP        *country;\n    GeoIP        *org;\n    GeoIP        *city;\n#if (NGX_HAVE_GEOIP_V6)\n    unsigned      country_v6:1;\n    unsigned      org_v6:1;\n    unsigned      city_v6:1;\n#endif\n} ngx_stream_geoip_conf_t;\n\n\ntypedef struct {\n    ngx_str_t    *name;\n    uintptr_t     data;\n} ngx_stream_geoip_var_t;\n\n\ntypedef const char *(*ngx_stream_geoip_variable_handler_pt)(GeoIP *,\n    u_long addr);\n\n\nngx_stream_geoip_variable_handler_pt ngx_stream_geoip_country_functions[] = {\n    GeoIP_country_code_by_ipnum,\n    GeoIP_country_code3_by_ipnum,\n    GeoIP_country_name_by_ipnum,\n};\n\n\n#if (NGX_HAVE_GEOIP_V6)\n\ntypedef const char *(*ngx_stream_geoip_variable_handler_v6_pt)(GeoIP *,\n    geoipv6_t addr);\n\n\nngx_stream_geoip_variable_handler_v6_pt\n    ngx_stream_geoip_country_v6_functions[] =\n{\n    GeoIP_country_code_by_ipnum_v6,\n    GeoIP_country_code3_by_ipnum_v6,\n    GeoIP_country_name_by_ipnum_v6,\n};\n\n#endif\n\n\nstatic ngx_int_t ngx_stream_geoip_country_variable(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_stream_geoip_org_variable(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_stream_geoip_city_variable(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_stream_geoip_region_name_variable(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_stream_geoip_city_float_variable(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_stream_geoip_city_int_variable(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data);\nstatic GeoIPRecord *ngx_stream_geoip_get_city_record(ngx_stream_session_t *s);\n\nstatic ngx_int_t ngx_stream_geoip_add_variables(ngx_conf_t *cf);\nstatic void *ngx_stream_geoip_create_conf(ngx_conf_t *cf);\nstatic char *ngx_stream_geoip_country(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_stream_geoip_org(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_stream_geoip_city(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic void ngx_stream_geoip_cleanup(void *data);\n\n\nstatic ngx_command_t  ngx_stream_geoip_commands[] = {\n\n    { ngx_string(\"geoip_country\"),\n      NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE12,\n      ngx_stream_geoip_country,\n      NGX_STREAM_MAIN_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"geoip_org\"),\n      NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE12,\n      ngx_stream_geoip_org,\n      NGX_STREAM_MAIN_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"geoip_city\"),\n      NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE12,\n      ngx_stream_geoip_city,\n      NGX_STREAM_MAIN_CONF_OFFSET,\n      0,\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_stream_module_t  ngx_stream_geoip_module_ctx = {\n    ngx_stream_geoip_add_variables,        /* preconfiguration */\n    NULL,                                  /* postconfiguration */\n\n    ngx_stream_geoip_create_conf,          /* create main configuration */\n    NULL,                                  /* init main configuration */\n\n    NULL,                                  /* create server configuration */\n    NULL                                   /* merge server configuration */\n};\n\n\nngx_module_t  ngx_stream_geoip_module = {\n    NGX_MODULE_V1,\n    &ngx_stream_geoip_module_ctx,          /* module context */\n    ngx_stream_geoip_commands,             /* module directives */\n    NGX_STREAM_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_stream_variable_t  ngx_stream_geoip_vars[] = {\n\n    { ngx_string(\"geoip_country_code\"), NULL,\n      ngx_stream_geoip_country_variable,\n      NGX_GEOIP_COUNTRY_CODE, 0, 0 },\n\n    { ngx_string(\"geoip_country_code3\"), NULL,\n      ngx_stream_geoip_country_variable,\n      NGX_GEOIP_COUNTRY_CODE3, 0, 0 },\n\n    { ngx_string(\"geoip_country_name\"), NULL,\n      ngx_stream_geoip_country_variable,\n      NGX_GEOIP_COUNTRY_NAME, 0, 0 },\n\n    { ngx_string(\"geoip_org\"), NULL,\n      ngx_stream_geoip_org_variable,\n      0, 0, 0 },\n\n    { ngx_string(\"geoip_city_continent_code\"), NULL,\n      ngx_stream_geoip_city_variable,\n      offsetof(GeoIPRecord, continent_code), 0, 0 },\n\n    { ngx_string(\"geoip_city_country_code\"), NULL,\n      ngx_stream_geoip_city_variable,\n      offsetof(GeoIPRecord, country_code), 0, 0 },\n\n    { ngx_string(\"geoip_city_country_code3\"), NULL,\n      ngx_stream_geoip_city_variable,\n      offsetof(GeoIPRecord, country_code3), 0, 0 },\n\n    { ngx_string(\"geoip_city_country_name\"), NULL,\n      ngx_stream_geoip_city_variable,\n      offsetof(GeoIPRecord, country_name), 0, 0 },\n\n    { ngx_string(\"geoip_region\"), NULL,\n      ngx_stream_geoip_city_variable,\n      offsetof(GeoIPRecord, region), 0, 0 },\n\n    { ngx_string(\"geoip_region_name\"), NULL,\n      ngx_stream_geoip_region_name_variable,\n      0, 0, 0 },\n\n    { ngx_string(\"geoip_city\"), NULL,\n      ngx_stream_geoip_city_variable,\n      offsetof(GeoIPRecord, city), 0, 0 },\n\n    { ngx_string(\"geoip_postal_code\"), NULL,\n      ngx_stream_geoip_city_variable,\n      offsetof(GeoIPRecord, postal_code), 0, 0 },\n\n    { ngx_string(\"geoip_latitude\"), NULL,\n      ngx_stream_geoip_city_float_variable,\n      offsetof(GeoIPRecord, latitude), 0, 0 },\n\n    { ngx_string(\"geoip_longitude\"), NULL,\n      ngx_stream_geoip_city_float_variable,\n      offsetof(GeoIPRecord, longitude), 0, 0 },\n\n    { ngx_string(\"geoip_dma_code\"), NULL,\n      ngx_stream_geoip_city_int_variable,\n      offsetof(GeoIPRecord, dma_code), 0, 0 },\n\n    { ngx_string(\"geoip_area_code\"), NULL,\n      ngx_stream_geoip_city_int_variable,\n      offsetof(GeoIPRecord, area_code), 0, 0 },\n\n      ngx_stream_null_variable\n};\n\n\nstatic u_long\nngx_stream_geoip_addr(ngx_stream_session_t *s, ngx_stream_geoip_conf_t *gcf)\n{\n    ngx_addr_t           addr;\n    struct sockaddr_in  *sin;\n\n    addr.sockaddr = s->connection->sockaddr;\n    addr.socklen = s->connection->socklen;\n    /* addr.name = s->connection->addr_text; */\n\n#if (NGX_HAVE_INET6)\n\n    if (addr.sockaddr->sa_family == AF_INET6) {\n        u_char           *p;\n        in_addr_t         inaddr;\n        struct in6_addr  *inaddr6;\n\n        inaddr6 = &((struct sockaddr_in6 *) addr.sockaddr)->sin6_addr;\n\n        if (IN6_IS_ADDR_V4MAPPED(inaddr6)) {\n            p = inaddr6->s6_addr;\n\n            inaddr = p[12] << 24;\n            inaddr += p[13] << 16;\n            inaddr += p[14] << 8;\n            inaddr += p[15];\n\n            return inaddr;\n        }\n    }\n\n#endif\n\n    if (addr.sockaddr->sa_family != AF_INET) {\n        return INADDR_NONE;\n    }\n\n    sin = (struct sockaddr_in *) addr.sockaddr;\n    return ntohl(sin->sin_addr.s_addr);\n}\n\n\n#if (NGX_HAVE_GEOIP_V6)\n\nstatic geoipv6_t\nngx_stream_geoip_addr_v6(ngx_stream_session_t *s, ngx_stream_geoip_conf_t *gcf)\n{\n    ngx_addr_t            addr;\n    in_addr_t             addr4;\n    struct in6_addr       addr6;\n    struct sockaddr_in   *sin;\n    struct sockaddr_in6  *sin6;\n\n    addr.sockaddr = s->connection->sockaddr;\n    addr.socklen = s->connection->socklen;\n    /* addr.name = s->connection->addr_text; */\n\n    switch (addr.sockaddr->sa_family) {\n\n    case AF_INET:\n        /* Produce IPv4-mapped IPv6 address. */\n        sin = (struct sockaddr_in *) addr.sockaddr;\n        addr4 = ntohl(sin->sin_addr.s_addr);\n\n        ngx_memzero(&addr6, sizeof(struct in6_addr));\n        addr6.s6_addr[10] = 0xff;\n        addr6.s6_addr[11] = 0xff;\n        addr6.s6_addr[12] = addr4 >> 24;\n        addr6.s6_addr[13] = addr4 >> 16;\n        addr6.s6_addr[14] = addr4 >> 8;\n        addr6.s6_addr[15] = addr4;\n        return addr6;\n\n    case AF_INET6:\n        sin6 = (struct sockaddr_in6 *) addr.sockaddr;\n        return sin6->sin6_addr;\n\n    default:\n        return in6addr_any;\n    }\n}\n\n#endif\n\n\nstatic ngx_int_t\nngx_stream_geoip_country_variable(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data)\n{\n    ngx_stream_geoip_variable_handler_pt     handler =\n        ngx_stream_geoip_country_functions[data];\n#if (NGX_HAVE_GEOIP_V6)\n    ngx_stream_geoip_variable_handler_v6_pt  handler_v6 =\n        ngx_stream_geoip_country_v6_functions[data];\n#endif\n\n    const char               *val;\n    ngx_stream_geoip_conf_t  *gcf;\n\n    gcf = ngx_stream_get_module_main_conf(s, ngx_stream_geoip_module);\n\n    if (gcf->country == NULL) {\n        goto not_found;\n    }\n\n#if (NGX_HAVE_GEOIP_V6)\n    val = gcf->country_v6\n              ? handler_v6(gcf->country, ngx_stream_geoip_addr_v6(s, gcf))\n              : handler(gcf->country, ngx_stream_geoip_addr(s, gcf));\n#else\n    val = handler(gcf->country, ngx_stream_geoip_addr(s, gcf));\n#endif\n\n    if (val == NULL) {\n        goto not_found;\n    }\n\n    v->len = ngx_strlen(val);\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = (u_char *) val;\n\n    return NGX_OK;\n\nnot_found:\n\n    v->not_found = 1;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_geoip_org_variable(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data)\n{\n    size_t                    len;\n    char                     *val;\n    ngx_stream_geoip_conf_t  *gcf;\n\n    gcf = ngx_stream_get_module_main_conf(s, ngx_stream_geoip_module);\n\n    if (gcf->org == NULL) {\n        goto not_found;\n    }\n\n#if (NGX_HAVE_GEOIP_V6)\n    val = gcf->org_v6\n              ? GeoIP_name_by_ipnum_v6(gcf->org,\n                                       ngx_stream_geoip_addr_v6(s, gcf))\n              : GeoIP_name_by_ipnum(gcf->org,\n                                    ngx_stream_geoip_addr(s, gcf));\n#else\n    val = GeoIP_name_by_ipnum(gcf->org, ngx_stream_geoip_addr(s, gcf));\n#endif\n\n    if (val == NULL) {\n        goto not_found;\n    }\n\n    len = ngx_strlen(val);\n    v->data = ngx_pnalloc(s->connection->pool, len);\n    if (v->data == NULL) {\n        ngx_free(val);\n        return NGX_ERROR;\n    }\n\n    ngx_memcpy(v->data, val, len);\n\n    v->len = len;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    ngx_free(val);\n\n    return NGX_OK;\n\nnot_found:\n\n    v->not_found = 1;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_geoip_city_variable(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data)\n{\n    char         *val;\n    size_t        len;\n    GeoIPRecord  *gr;\n\n    gr = ngx_stream_geoip_get_city_record(s);\n    if (gr == NULL) {\n        goto not_found;\n    }\n\n    val = *(char **) ((char *) gr + data);\n    if (val == NULL) {\n        goto no_value;\n    }\n\n    len = ngx_strlen(val);\n    v->data = ngx_pnalloc(s->connection->pool, len);\n    if (v->data == NULL) {\n        GeoIPRecord_delete(gr);\n        return NGX_ERROR;\n    }\n\n    ngx_memcpy(v->data, val, len);\n\n    v->len = len;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    GeoIPRecord_delete(gr);\n\n    return NGX_OK;\n\nno_value:\n\n    GeoIPRecord_delete(gr);\n\nnot_found:\n\n    v->not_found = 1;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_geoip_region_name_variable(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data)\n{\n    size_t        len;\n    const char   *val;\n    GeoIPRecord  *gr;\n\n    gr = ngx_stream_geoip_get_city_record(s);\n    if (gr == NULL) {\n        goto not_found;\n    }\n\n    val = GeoIP_region_name_by_code(gr->country_code, gr->region);\n\n    GeoIPRecord_delete(gr);\n\n    if (val == NULL) {\n        goto not_found;\n    }\n\n    len = ngx_strlen(val);\n    v->data = ngx_pnalloc(s->connection->pool, len);\n    if (v->data == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_memcpy(v->data, val, len);\n\n    v->len = len;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    return NGX_OK;\n\nnot_found:\n\n    v->not_found = 1;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_geoip_city_float_variable(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data)\n{\n    float         val;\n    GeoIPRecord  *gr;\n\n    gr = ngx_stream_geoip_get_city_record(s);\n    if (gr == NULL) {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    v->data = ngx_pnalloc(s->connection->pool, NGX_INT64_LEN + 5);\n    if (v->data == NULL) {\n        GeoIPRecord_delete(gr);\n        return NGX_ERROR;\n    }\n\n    val = *(float *) ((char *) gr + data);\n\n    v->len = ngx_sprintf(v->data, \"%.4f\", val) - v->data;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    GeoIPRecord_delete(gr);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_geoip_city_int_variable(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data)\n{\n    int           val;\n    GeoIPRecord  *gr;\n\n    gr = ngx_stream_geoip_get_city_record(s);\n    if (gr == NULL) {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    v->data = ngx_pnalloc(s->connection->pool, NGX_INT64_LEN);\n    if (v->data == NULL) {\n        GeoIPRecord_delete(gr);\n        return NGX_ERROR;\n    }\n\n    val = *(int *) ((char *) gr + data);\n\n    v->len = ngx_sprintf(v->data, \"%d\", val) - v->data;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    GeoIPRecord_delete(gr);\n\n    return NGX_OK;\n}\n\n\nstatic GeoIPRecord *\nngx_stream_geoip_get_city_record(ngx_stream_session_t *s)\n{\n    ngx_stream_geoip_conf_t  *gcf;\n\n    gcf = ngx_stream_get_module_main_conf(s, ngx_stream_geoip_module);\n\n    if (gcf->city) {\n#if (NGX_HAVE_GEOIP_V6)\n        return gcf->city_v6\n                   ? GeoIP_record_by_ipnum_v6(gcf->city,\n                                              ngx_stream_geoip_addr_v6(s, gcf))\n                   : GeoIP_record_by_ipnum(gcf->city,\n                                           ngx_stream_geoip_addr(s, gcf));\n#else\n        return GeoIP_record_by_ipnum(gcf->city, ngx_stream_geoip_addr(s, gcf));\n#endif\n    }\n\n    return NULL;\n}\n\n\nstatic ngx_int_t\nngx_stream_geoip_add_variables(ngx_conf_t *cf)\n{\n    ngx_stream_variable_t  *var, *v;\n\n    for (v = ngx_stream_geoip_vars; v->name.len; v++) {\n        var = ngx_stream_add_variable(cf, &v->name, v->flags);\n        if (var == NULL) {\n            return NGX_ERROR;\n        }\n\n        var->get_handler = v->get_handler;\n        var->data = v->data;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void *\nngx_stream_geoip_create_conf(ngx_conf_t *cf)\n{\n    ngx_pool_cleanup_t       *cln;\n    ngx_stream_geoip_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_geoip_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    cln = ngx_pool_cleanup_add(cf->pool, 0);\n    if (cln == NULL) {\n        return NULL;\n    }\n\n    cln->handler = ngx_stream_geoip_cleanup;\n    cln->data = conf;\n\n    return conf;\n}\n\n\nstatic char *\nngx_stream_geoip_country(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_stream_geoip_conf_t  *gcf = conf;\n\n    ngx_str_t  *value;\n\n    if (gcf->country) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    gcf->country = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE);\n\n    if (gcf->country == NULL) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"GeoIP_open(\\\"%V\\\") failed\", &value[1]);\n\n        return NGX_CONF_ERROR;\n    }\n\n    if (cf->args->nelts == 3) {\n        if (ngx_strcmp(value[2].data, \"utf8\") == 0) {\n            GeoIP_set_charset(gcf->country, GEOIP_CHARSET_UTF8);\n\n        } else {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"invalid parameter \\\"%V\\\"\", &value[2]);\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    switch (gcf->country->databaseType) {\n\n    case GEOIP_COUNTRY_EDITION:\n\n        return NGX_CONF_OK;\n\n#if (NGX_HAVE_GEOIP_V6)\n    case GEOIP_COUNTRY_EDITION_V6:\n\n        gcf->country_v6 = 1;\n        return NGX_CONF_OK;\n#endif\n\n    default:\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid GeoIP database \\\"%V\\\" type:%d\",\n                           &value[1], gcf->country->databaseType);\n        return NGX_CONF_ERROR;\n    }\n}\n\n\nstatic char *\nngx_stream_geoip_org(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_stream_geoip_conf_t  *gcf = conf;\n\n    ngx_str_t  *value;\n\n    if (gcf->org) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    gcf->org = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE);\n\n    if (gcf->org == NULL) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"GeoIP_open(\\\"%V\\\") failed\", &value[1]);\n\n        return NGX_CONF_ERROR;\n    }\n\n    if (cf->args->nelts == 3) {\n        if (ngx_strcmp(value[2].data, \"utf8\") == 0) {\n            GeoIP_set_charset(gcf->org, GEOIP_CHARSET_UTF8);\n\n        } else {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"invalid parameter \\\"%V\\\"\", &value[2]);\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    switch (gcf->org->databaseType) {\n\n    case GEOIP_ISP_EDITION:\n    case GEOIP_ORG_EDITION:\n    case GEOIP_DOMAIN_EDITION:\n    case GEOIP_ASNUM_EDITION:\n\n        return NGX_CONF_OK;\n\n#if (NGX_HAVE_GEOIP_V6)\n    case GEOIP_ISP_EDITION_V6:\n    case GEOIP_ORG_EDITION_V6:\n    case GEOIP_DOMAIN_EDITION_V6:\n    case GEOIP_ASNUM_EDITION_V6:\n\n        gcf->org_v6 = 1;\n        return NGX_CONF_OK;\n#endif\n\n    default:\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid GeoIP database \\\"%V\\\" type:%d\",\n                           &value[1], gcf->org->databaseType);\n        return NGX_CONF_ERROR;\n    }\n}\n\n\nstatic char *\nngx_stream_geoip_city(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_stream_geoip_conf_t  *gcf = conf;\n\n    ngx_str_t  *value;\n\n    if (gcf->city) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    gcf->city = GeoIP_open((char *) value[1].data, GEOIP_MEMORY_CACHE);\n\n    if (gcf->city == NULL) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"GeoIP_open(\\\"%V\\\") failed\", &value[1]);\n\n        return NGX_CONF_ERROR;\n    }\n\n    if (cf->args->nelts == 3) {\n        if (ngx_strcmp(value[2].data, \"utf8\") == 0) {\n            GeoIP_set_charset(gcf->city, GEOIP_CHARSET_UTF8);\n\n        } else {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"invalid parameter \\\"%V\\\"\", &value[2]);\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    switch (gcf->city->databaseType) {\n\n    case GEOIP_CITY_EDITION_REV0:\n    case GEOIP_CITY_EDITION_REV1:\n\n        return NGX_CONF_OK;\n\n#if (NGX_HAVE_GEOIP_V6)\n    case GEOIP_CITY_EDITION_REV0_V6:\n    case GEOIP_CITY_EDITION_REV1_V6:\n\n        gcf->city_v6 = 1;\n        return NGX_CONF_OK;\n#endif\n\n    default:\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid GeoIP City database \\\"%V\\\" type:%d\",\n                           &value[1], gcf->city->databaseType);\n        return NGX_CONF_ERROR;\n    }\n}\n\n\nstatic void\nngx_stream_geoip_cleanup(void *data)\n{\n    ngx_stream_geoip_conf_t  *gcf = data;\n\n    if (gcf->country) {\n        GeoIP_delete(gcf->country);\n    }\n\n    if (gcf->org) {\n        GeoIP_delete(gcf->org);\n    }\n\n    if (gcf->city) {\n        GeoIP_delete(gcf->city);\n    }\n}\n"
  },
  {
    "path": "src/stream/ngx_stream_handler.c",
    "content": "\n/*\n * Copyright (C) Roman Arutyunyan\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_event.h>\n#include <ngx_stream.h>\n\n\nstatic void ngx_stream_log_session(ngx_stream_session_t *s);\nstatic void ngx_stream_close_connection(ngx_connection_t *c);\nstatic u_char *ngx_stream_log_error(ngx_log_t *log, u_char *buf, size_t len);\nstatic void ngx_stream_proxy_protocol_handler(ngx_event_t *rev);\n\n\nvoid\nngx_stream_init_connection(ngx_connection_t *c)\n{\n    u_char                        text[NGX_SOCKADDR_STRLEN];\n    size_t                        len;\n    ngx_uint_t                    i;\n    ngx_time_t                   *tp;\n    ngx_event_t                  *rev;\n    struct sockaddr              *sa;\n    ngx_stream_port_t            *port;\n    struct sockaddr_in           *sin;\n    ngx_stream_in_addr_t         *addr;\n    ngx_stream_session_t         *s;\n    ngx_stream_addr_conf_t       *addr_conf;\n#if (NGX_HAVE_INET6)\n    struct sockaddr_in6          *sin6;\n    ngx_stream_in6_addr_t        *addr6;\n#endif\n    ngx_stream_core_srv_conf_t   *cscf;\n    ngx_stream_core_main_conf_t  *cmcf;\n\n    /* find the server configuration for the address:port */\n\n    port = c->listening->servers;\n\n    if (port->naddrs > 1) {\n\n        /*\n         * There are several addresses on this port and one of them\n         * is the \"*:port\" wildcard so getsockname() is needed to determine\n         * the server address.\n         *\n         * AcceptEx() and recvmsg() already gave this address.\n         */\n\n        if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK) {\n            ngx_stream_close_connection(c);\n            return;\n        }\n\n        sa = c->local_sockaddr;\n\n        switch (sa->sa_family) {\n\n#if (NGX_HAVE_INET6)\n        case AF_INET6:\n            sin6 = (struct sockaddr_in6 *) sa;\n\n            addr6 = port->addrs;\n\n            /* the last address is \"*\" */\n\n            for (i = 0; i < port->naddrs - 1; i++) {\n                if (ngx_memcmp(&addr6[i].addr6, &sin6->sin6_addr, 16) == 0) {\n                    break;\n                }\n            }\n\n            addr_conf = &addr6[i].conf;\n\n            break;\n#endif\n\n        default: /* AF_INET */\n            sin = (struct sockaddr_in *) sa;\n\n            addr = port->addrs;\n\n            /* the last address is \"*\" */\n\n            for (i = 0; i < port->naddrs - 1; i++) {\n                if (addr[i].addr == sin->sin_addr.s_addr) {\n                    break;\n                }\n            }\n\n            addr_conf = &addr[i].conf;\n\n            break;\n        }\n\n    } else {\n        switch (c->local_sockaddr->sa_family) {\n\n#if (NGX_HAVE_INET6)\n        case AF_INET6:\n            addr6 = port->addrs;\n            addr_conf = &addr6[0].conf;\n            break;\n#endif\n\n        default: /* AF_INET */\n            addr = port->addrs;\n            addr_conf = &addr[0].conf;\n            break;\n        }\n    }\n\n    s = ngx_pcalloc(c->pool, sizeof(ngx_stream_session_t));\n    if (s == NULL) {\n        ngx_stream_close_connection(c);\n        return;\n    }\n\n    s->signature = NGX_STREAM_MODULE;\n    s->main_conf = addr_conf->ctx->main_conf;\n    s->srv_conf = addr_conf->ctx->srv_conf;\n\n#if (T_NGX_STREAM_SNI)\n    s->addr_conf = addr_conf;\n    s->main_conf = ((ngx_stream_core_srv_conf_t*)addr_conf->default_server)->ctx->main_conf;\n    s->srv_conf = ((ngx_stream_core_srv_conf_t*)addr_conf->default_server)->ctx->srv_conf;\n#endif\n\n#if (NGX_STREAM_SSL)\n    s->ssl = addr_conf->ssl;\n#endif\n\n    if (c->buffer) {\n        s->received += c->buffer->last - c->buffer->pos;\n    }\n\n    s->connection = c;\n    c->data = s;\n\n    cscf = ngx_stream_get_module_srv_conf(s, ngx_stream_core_module);\n\n    ngx_set_connection_log(c, cscf->error_log);\n\n    len = ngx_sock_ntop(c->sockaddr, c->socklen, text, NGX_SOCKADDR_STRLEN, 1);\n\n    ngx_log_error(NGX_LOG_INFO, c->log, 0, \"*%uA %sclient %*s connected to %V\",\n                  c->number, c->type == SOCK_DGRAM ? \"udp \" : \"\",\n                  len, text, &addr_conf->addr_text);\n\n    c->log->connection = c->number;\n    c->log->handler = ngx_stream_log_error;\n    c->log->data = s;\n    c->log->action = \"initializing session\";\n    c->log_error = NGX_ERROR_INFO;\n\n    s->ctx = ngx_pcalloc(c->pool, sizeof(void *) * ngx_stream_max_module);\n    if (s->ctx == NULL) {\n        ngx_stream_close_connection(c);\n        return;\n    }\n\n    cmcf = ngx_stream_get_module_main_conf(s, ngx_stream_core_module);\n\n    s->variables = ngx_pcalloc(s->connection->pool,\n                               cmcf->variables.nelts\n                               * sizeof(ngx_stream_variable_value_t));\n\n    if (s->variables == NULL) {\n        ngx_stream_close_connection(c);\n        return;\n    }\n\n    tp = ngx_timeofday();\n    s->start_sec = tp->sec;\n    s->start_msec = tp->msec;\n\n    rev = c->read;\n    rev->handler = ngx_stream_session_handler;\n\n    if (addr_conf->proxy_protocol) {\n        c->log->action = \"reading PROXY protocol\";\n\n        rev->handler = ngx_stream_proxy_protocol_handler;\n\n        if (!rev->ready) {\n            ngx_add_timer(rev, cscf->proxy_protocol_timeout);\n\n            if (ngx_handle_read_event(rev, 0) != NGX_OK) {\n                ngx_stream_finalize_session(s,\n                                            NGX_STREAM_INTERNAL_SERVER_ERROR);\n            }\n\n            return;\n        }\n    }\n\n    if (ngx_use_accept_mutex) {\n        ngx_post_event(rev, &ngx_posted_events);\n        return;\n    }\n\n    rev->handler(rev);\n}\n\n\nstatic void\nngx_stream_proxy_protocol_handler(ngx_event_t *rev)\n{\n    u_char                      *p, buf[NGX_PROXY_PROTOCOL_MAX_HEADER];\n    size_t                       size;\n    ssize_t                      n;\n    ngx_err_t                    err;\n    ngx_connection_t            *c;\n    ngx_stream_session_t        *s;\n    ngx_stream_core_srv_conf_t  *cscf;\n\n    c = rev->data;\n    s = c->data;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0,\n                   \"stream PROXY protocol handler\");\n\n    if (rev->timedout) {\n        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, \"client timed out\");\n        ngx_stream_finalize_session(s, NGX_STREAM_OK);\n        return;\n    }\n\n    n = recv(c->fd, (char *) buf, sizeof(buf), MSG_PEEK);\n\n    err = ngx_socket_errno;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, \"recv(): %z\", n);\n\n    if (n == -1) {\n        if (err == NGX_EAGAIN) {\n            rev->ready = 0;\n\n            if (!rev->timer_set) {\n                cscf = ngx_stream_get_module_srv_conf(s,\n                                                      ngx_stream_core_module);\n\n                ngx_add_timer(rev, cscf->proxy_protocol_timeout);\n            }\n\n            if (ngx_handle_read_event(rev, 0) != NGX_OK) {\n                ngx_stream_finalize_session(s,\n                                            NGX_STREAM_INTERNAL_SERVER_ERROR);\n            }\n\n            return;\n        }\n\n        ngx_connection_error(c, err, \"recv() failed\");\n\n        ngx_stream_finalize_session(s, NGX_STREAM_OK);\n        return;\n    }\n\n    if (rev->timer_set) {\n        ngx_del_timer(rev);\n    }\n\n    p = ngx_proxy_protocol_read(c, buf, buf + n);\n\n    if (p == NULL) {\n        ngx_stream_finalize_session(s, NGX_STREAM_BAD_REQUEST);\n        return;\n    }\n\n    size = p - buf;\n\n    if (c->recv(c, buf, size) != (ssize_t) size) {\n        ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR);\n        return;\n    }\n\n    c->log->action = \"initializing session\";\n\n    ngx_stream_session_handler(rev);\n}\n\n\nvoid\nngx_stream_session_handler(ngx_event_t *rev)\n{\n    ngx_connection_t      *c;\n    ngx_stream_session_t  *s;\n\n    c = rev->data;\n    s = c->data;\n\n    ngx_stream_core_run_phases(s);\n}\n\n\nvoid\nngx_stream_finalize_session(ngx_stream_session_t *s, ngx_uint_t rc)\n{\n    ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,\n                   \"finalize stream session: %i\", rc);\n\n    s->status = rc;\n\n    ngx_stream_log_session(s);\n\n    ngx_stream_close_connection(s->connection);\n}\n\n\nstatic void\nngx_stream_log_session(ngx_stream_session_t *s)\n{\n    ngx_uint_t                    i, n;\n    ngx_stream_handler_pt        *log_handler;\n    ngx_stream_core_main_conf_t  *cmcf;\n\n    cmcf = ngx_stream_get_module_main_conf(s, ngx_stream_core_module);\n\n    log_handler = cmcf->phases[NGX_STREAM_LOG_PHASE].handlers.elts;\n    n = cmcf->phases[NGX_STREAM_LOG_PHASE].handlers.nelts;\n\n    for (i = 0; i < n; i++) {\n        log_handler[i](s);\n    }\n}\n\n\nstatic void\nngx_stream_close_connection(ngx_connection_t *c)\n{\n    ngx_pool_t  *pool;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0,\n                   \"close stream connection: %d\", c->fd);\n\n#if (NGX_STREAM_SSL)\n\n    if (c->ssl) {\n        if (ngx_ssl_shutdown(c) == NGX_AGAIN) {\n            c->ssl->handler = ngx_stream_close_connection;\n            return;\n        }\n    }\n\n#endif\n\n#if (NGX_STAT_STUB)\n    (void) ngx_atomic_fetch_add(ngx_stat_active, -1);\n#endif\n\n    pool = c->pool;\n\n    ngx_close_connection(c);\n\n    ngx_destroy_pool(pool);\n}\n\n\nstatic u_char *\nngx_stream_log_error(ngx_log_t *log, u_char *buf, size_t len)\n{\n    u_char                *p;\n    ngx_stream_session_t  *s;\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    s = log->data;\n\n    p = ngx_snprintf(buf, len, \", %sclient: %V, server: %V\",\n                     s->connection->type == SOCK_DGRAM ? \"udp \" : \"\",\n                     &s->connection->addr_text,\n                     &s->connection->listening->addr_text);\n    len -= p - buf;\n    buf = p;\n\n    if (s->log_handler) {\n        p = s->log_handler(log, buf, len);\n    }\n\n    return p;\n}\n"
  },
  {
    "path": "src/stream/ngx_stream_limit_conn_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_stream.h>\n\n\n#define NGX_STREAM_LIMIT_CONN_PASSED            1\n#define NGX_STREAM_LIMIT_CONN_REJECTED          2\n#define NGX_STREAM_LIMIT_CONN_REJECTED_DRY_RUN  3\n\n\ntypedef struct {\n    u_char                          color;\n    u_char                          len;\n    u_short                         conn;\n    u_char                          data[1];\n} ngx_stream_limit_conn_node_t;\n\n\ntypedef struct {\n    ngx_shm_zone_t                 *shm_zone;\n    ngx_rbtree_node_t              *node;\n} ngx_stream_limit_conn_cleanup_t;\n\n\ntypedef struct {\n    ngx_rbtree_t                    rbtree;\n    ngx_rbtree_node_t               sentinel;\n} ngx_stream_limit_conn_shctx_t;\n\n\ntypedef struct {\n    ngx_stream_limit_conn_shctx_t  *sh;\n    ngx_slab_pool_t                *shpool;\n    ngx_stream_complex_value_t      key;\n} ngx_stream_limit_conn_ctx_t;\n\n\ntypedef struct {\n    ngx_shm_zone_t                 *shm_zone;\n    ngx_uint_t                      conn;\n} ngx_stream_limit_conn_limit_t;\n\n\ntypedef struct {\n    ngx_array_t                     limits;\n    ngx_uint_t                      log_level;\n    ngx_flag_t                      dry_run;\n} ngx_stream_limit_conn_conf_t;\n\n\nstatic ngx_rbtree_node_t *ngx_stream_limit_conn_lookup(ngx_rbtree_t *rbtree,\n    ngx_str_t *key, uint32_t hash);\nstatic void ngx_stream_limit_conn_cleanup(void *data);\nstatic ngx_inline void ngx_stream_limit_conn_cleanup_all(ngx_pool_t *pool);\n\nstatic ngx_int_t ngx_stream_limit_conn_status_variable(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data);\nstatic void *ngx_stream_limit_conn_create_conf(ngx_conf_t *cf);\nstatic char *ngx_stream_limit_conn_merge_conf(ngx_conf_t *cf, void *parent,\n    void *child);\nstatic char *ngx_stream_limit_conn_zone(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_stream_limit_conn(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic ngx_int_t ngx_stream_limit_conn_add_variables(ngx_conf_t *cf);\nstatic ngx_int_t ngx_stream_limit_conn_init(ngx_conf_t *cf);\n\n\nstatic ngx_conf_enum_t  ngx_stream_limit_conn_log_levels[] = {\n    { ngx_string(\"info\"), NGX_LOG_INFO },\n    { ngx_string(\"notice\"), NGX_LOG_NOTICE },\n    { ngx_string(\"warn\"), NGX_LOG_WARN },\n    { ngx_string(\"error\"), NGX_LOG_ERR },\n    { ngx_null_string, 0 }\n};\n\n\nstatic ngx_command_t  ngx_stream_limit_conn_commands[] = {\n\n    { ngx_string(\"limit_conn_zone\"),\n      NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE2,\n      ngx_stream_limit_conn_zone,\n      0,\n      0,\n      NULL },\n\n    { ngx_string(\"limit_conn\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE2,\n      ngx_stream_limit_conn,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"limit_conn_log_level\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_enum_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_limit_conn_conf_t, log_level),\n      &ngx_stream_limit_conn_log_levels },\n\n    { ngx_string(\"limit_conn_dry_run\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_limit_conn_conf_t, dry_run),\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_stream_module_t  ngx_stream_limit_conn_module_ctx = {\n    ngx_stream_limit_conn_add_variables,   /* preconfiguration */\n    ngx_stream_limit_conn_init,            /* postconfiguration */\n\n    NULL,                                  /* create main configuration */\n    NULL,                                  /* init main configuration */\n\n    ngx_stream_limit_conn_create_conf,     /* create server configuration */\n    ngx_stream_limit_conn_merge_conf       /* merge server configuration */\n};\n\n\nngx_module_t  ngx_stream_limit_conn_module = {\n    NGX_MODULE_V1,\n    &ngx_stream_limit_conn_module_ctx,     /* module context */\n    ngx_stream_limit_conn_commands,        /* module directives */\n    NGX_STREAM_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_stream_variable_t  ngx_stream_limit_conn_vars[] = {\n\n    { ngx_string(\"limit_conn_status\"), NULL,\n      ngx_stream_limit_conn_status_variable, 0, NGX_STREAM_VAR_NOCACHEABLE, 0 },\n\n      ngx_stream_null_variable\n};\n\n\nstatic ngx_str_t  ngx_stream_limit_conn_status[] = {\n    ngx_string(\"PASSED\"),\n    ngx_string(\"REJECTED\"),\n    ngx_string(\"REJECTED_DRY_RUN\")\n};\n\n\nstatic ngx_int_t\nngx_stream_limit_conn_handler(ngx_stream_session_t *s)\n{\n    size_t                            n;\n    uint32_t                          hash;\n    ngx_str_t                         key;\n    ngx_uint_t                        i;\n    ngx_rbtree_node_t                *node;\n    ngx_pool_cleanup_t               *cln;\n    ngx_stream_limit_conn_ctx_t      *ctx;\n    ngx_stream_limit_conn_node_t     *lc;\n    ngx_stream_limit_conn_conf_t     *lccf;\n    ngx_stream_limit_conn_limit_t    *limits;\n    ngx_stream_limit_conn_cleanup_t  *lccln;\n\n    lccf = ngx_stream_get_module_srv_conf(s, ngx_stream_limit_conn_module);\n    limits = lccf->limits.elts;\n\n    for (i = 0; i < lccf->limits.nelts; i++) {\n        ctx = limits[i].shm_zone->data;\n\n        if (ngx_stream_complex_value(s, &ctx->key, &key) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        if (key.len == 0) {\n            continue;\n        }\n\n        if (key.len > 255) {\n            ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,\n                          \"the value of the \\\"%V\\\" key \"\n                          \"is more than 255 bytes: \\\"%V\\\"\",\n                          &ctx->key.value, &key);\n            continue;\n        }\n\n        s->limit_conn_status = NGX_STREAM_LIMIT_CONN_PASSED;\n\n        hash = ngx_crc32_short(key.data, key.len);\n\n        ngx_shmtx_lock(&ctx->shpool->mutex);\n\n        node = ngx_stream_limit_conn_lookup(&ctx->sh->rbtree, &key, hash);\n\n        if (node == NULL) {\n\n            n = offsetof(ngx_rbtree_node_t, color)\n                + offsetof(ngx_stream_limit_conn_node_t, data)\n                + key.len;\n\n            node = ngx_slab_alloc_locked(ctx->shpool, n);\n\n            if (node == NULL) {\n                ngx_shmtx_unlock(&ctx->shpool->mutex);\n                ngx_stream_limit_conn_cleanup_all(s->connection->pool);\n\n                if (lccf->dry_run) {\n                    s->limit_conn_status =\n                                        NGX_STREAM_LIMIT_CONN_REJECTED_DRY_RUN;\n                    return NGX_DECLINED;\n                }\n\n                s->limit_conn_status = NGX_STREAM_LIMIT_CONN_REJECTED;\n\n                return NGX_STREAM_SERVICE_UNAVAILABLE;\n            }\n\n            lc = (ngx_stream_limit_conn_node_t *) &node->color;\n\n            node->key = hash;\n            lc->len = (u_char) key.len;\n            lc->conn = 1;\n            ngx_memcpy(lc->data, key.data, key.len);\n\n            ngx_rbtree_insert(&ctx->sh->rbtree, node);\n\n        } else {\n\n            lc = (ngx_stream_limit_conn_node_t *) &node->color;\n\n            if ((ngx_uint_t) lc->conn >= limits[i].conn) {\n\n                ngx_shmtx_unlock(&ctx->shpool->mutex);\n\n                ngx_log_error(lccf->log_level, s->connection->log, 0,\n                              \"limiting connections%s by zone \\\"%V\\\"\",\n                              lccf->dry_run ? \", dry run,\" : \"\",\n                              &limits[i].shm_zone->shm.name);\n\n                ngx_stream_limit_conn_cleanup_all(s->connection->pool);\n\n                if (lccf->dry_run) {\n                    s->limit_conn_status =\n                                        NGX_STREAM_LIMIT_CONN_REJECTED_DRY_RUN;\n                    return NGX_DECLINED;\n                }\n\n                s->limit_conn_status = NGX_STREAM_LIMIT_CONN_REJECTED;\n\n                return NGX_STREAM_SERVICE_UNAVAILABLE;\n            }\n\n            lc->conn++;\n        }\n\n        ngx_log_debug2(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,\n                       \"limit conn: %08Xi %d\", node->key, lc->conn);\n\n        ngx_shmtx_unlock(&ctx->shpool->mutex);\n\n        cln = ngx_pool_cleanup_add(s->connection->pool,\n                                   sizeof(ngx_stream_limit_conn_cleanup_t));\n        if (cln == NULL) {\n            return NGX_ERROR;\n        }\n\n        cln->handler = ngx_stream_limit_conn_cleanup;\n        lccln = cln->data;\n\n        lccln->shm_zone = limits[i].shm_zone;\n        lccln->node = node;\n    }\n\n    return NGX_DECLINED;\n}\n\n\nstatic void\nngx_stream_limit_conn_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_stream_limit_conn_node_t   *lcn, *lcnt;\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            lcn = (ngx_stream_limit_conn_node_t *) &node->color;\n            lcnt = (ngx_stream_limit_conn_node_t *) &temp->color;\n\n            p = (ngx_memn2cmp(lcn->data, lcnt->data, lcn->len, lcnt->len) < 0)\n                ? &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_rbtree_node_t *\nngx_stream_limit_conn_lookup(ngx_rbtree_t *rbtree, ngx_str_t *key,\n    uint32_t hash)\n{\n    ngx_int_t                      rc;\n    ngx_rbtree_node_t             *node, *sentinel;\n    ngx_stream_limit_conn_node_t  *lcn;\n\n    node = rbtree->root;\n    sentinel = 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        lcn = (ngx_stream_limit_conn_node_t *) &node->color;\n\n        rc = ngx_memn2cmp(key->data, lcn->data, key->len, (size_t) lcn->len);\n\n        if (rc == 0) {\n            return node;\n        }\n\n        node = (rc < 0) ? node->left : node->right;\n    }\n\n    return NULL;\n}\n\n\nstatic void\nngx_stream_limit_conn_cleanup(void *data)\n{\n    ngx_stream_limit_conn_cleanup_t  *lccln = data;\n\n    ngx_rbtree_node_t             *node;\n    ngx_stream_limit_conn_ctx_t   *ctx;\n    ngx_stream_limit_conn_node_t  *lc;\n\n    ctx = lccln->shm_zone->data;\n    node = lccln->node;\n    lc = (ngx_stream_limit_conn_node_t *) &node->color;\n\n    ngx_shmtx_lock(&ctx->shpool->mutex);\n\n    ngx_log_debug2(NGX_LOG_DEBUG_STREAM, lccln->shm_zone->shm.log, 0,\n                   \"limit conn cleanup: %08Xi %d\", node->key, lc->conn);\n\n    lc->conn--;\n\n    if (lc->conn == 0) {\n        ngx_rbtree_delete(&ctx->sh->rbtree, node);\n        ngx_slab_free_locked(ctx->shpool, node);\n    }\n\n    ngx_shmtx_unlock(&ctx->shpool->mutex);\n}\n\n\nstatic ngx_inline void\nngx_stream_limit_conn_cleanup_all(ngx_pool_t *pool)\n{\n    ngx_pool_cleanup_t  *cln;\n\n    cln = pool->cleanup;\n\n    while (cln && cln->handler == ngx_stream_limit_conn_cleanup) {\n        ngx_stream_limit_conn_cleanup(cln->data);\n        cln = cln->next;\n    }\n\n    pool->cleanup = cln;\n}\n\n\nstatic ngx_int_t\nngx_stream_limit_conn_init_zone(ngx_shm_zone_t *shm_zone, void *data)\n{\n    ngx_stream_limit_conn_ctx_t  *octx = data;\n\n    size_t                        len;\n    ngx_stream_limit_conn_ctx_t  *ctx;\n\n    ctx = shm_zone->data;\n\n    if (octx) {\n        if (ctx->key.value.len != octx->key.value.len\n            || ngx_strncmp(ctx->key.value.data, octx->key.value.data,\n                           ctx->key.value.len)\n               != 0)\n        {\n            ngx_log_error(NGX_LOG_EMERG, shm_zone->shm.log, 0,\n                          \"limit_conn_zone \\\"%V\\\" uses the \\\"%V\\\" key \"\n                          \"while previously it used the \\\"%V\\\" key\",\n                          &shm_zone->shm.name, &ctx->key.value,\n                          &octx->key.value);\n            return NGX_ERROR;\n        }\n\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,\n                             sizeof(ngx_stream_limit_conn_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_stream_limit_conn_rbtree_insert_value);\n\n    len = sizeof(\" in limit_conn_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 limit_conn_zone \\\"%V\\\"%Z\",\n                &shm_zone->shm.name);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_limit_conn_status_variable(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data)\n{\n    if (s->limit_conn_status == 0) {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->len = ngx_stream_limit_conn_status[s->limit_conn_status - 1].len;\n    v->data = ngx_stream_limit_conn_status[s->limit_conn_status - 1].data;\n\n    return NGX_OK;\n}\n\n\nstatic void *\nngx_stream_limit_conn_create_conf(ngx_conf_t *cf)\n{\n    ngx_stream_limit_conn_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_limit_conn_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc():\n     *\n     *     conf->limits.elts = NULL;\n     */\n\n    conf->log_level = NGX_CONF_UNSET_UINT;\n    conf->dry_run = NGX_CONF_UNSET;\n\n    return conf;\n}\n\n\nstatic char *\nngx_stream_limit_conn_merge_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_stream_limit_conn_conf_t *prev = parent;\n    ngx_stream_limit_conn_conf_t *conf = child;\n\n    if (conf->limits.elts == NULL) {\n        conf->limits = prev->limits;\n    }\n\n    ngx_conf_merge_uint_value(conf->log_level, prev->log_level, NGX_LOG_ERR);\n\n    ngx_conf_merge_value(conf->dry_run, prev->dry_run, 0);\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_stream_limit_conn_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    u_char                              *p;\n    ssize_t                              size;\n    ngx_str_t                           *value, name, s;\n    ngx_uint_t                           i;\n    ngx_shm_zone_t                      *shm_zone;\n    ngx_stream_limit_conn_ctx_t         *ctx;\n    ngx_stream_compile_complex_value_t   ccv;\n\n    value = cf->args->elts;\n\n    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_stream_limit_conn_ctx_t));\n    if (ctx == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t));\n\n    ccv.cf = cf;\n    ccv.value = &value[1];\n    ccv.complex_value = &ctx->key;\n\n    if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    size = 0;\n    name.len = 0;\n\n    for (i = 2; i < cf->args->nelts; i++) {\n\n        if (ngx_strncmp(value[i].data, \"zone=\", 5) == 0) {\n\n            name.data = value[i].data + 5;\n\n            p = (u_char *) ngx_strchr(name.data, ':');\n\n            if (p == NULL) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"invalid zone size \\\"%V\\\"\", &value[i]);\n                return NGX_CONF_ERROR;\n            }\n\n            name.len = p - name.data;\n\n            s.data = p + 1;\n            s.len = value[i].data + value[i].len - s.data;\n\n            size = ngx_parse_size(&s);\n\n            if (size == NGX_ERROR) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"invalid zone size \\\"%V\\\"\", &value[i]);\n                return NGX_CONF_ERROR;\n            }\n\n            if (size < (ssize_t) (8 * ngx_pagesize)) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"zone \\\"%V\\\" is too small\", &value[i]);\n                return NGX_CONF_ERROR;\n            }\n\n            continue;\n        }\n\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid parameter \\\"%V\\\"\", &value[i]);\n        return NGX_CONF_ERROR;\n    }\n\n    if (name.len == 0) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"\\\"%V\\\" must have \\\"zone\\\" parameter\",\n                           &cmd->name);\n        return NGX_CONF_ERROR;\n    }\n\n    shm_zone = ngx_shared_memory_add(cf, &name, size,\n                                     &ngx_stream_limit_conn_module);\n    if (shm_zone == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (shm_zone->data) {\n        ctx = shm_zone->data;\n\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"%V \\\"%V\\\" is already bound to key \\\"%V\\\"\",\n                           &cmd->name, &name, &ctx->key.value);\n        return NGX_CONF_ERROR;\n    }\n\n    shm_zone->init = ngx_stream_limit_conn_init_zone;\n    shm_zone->data = ctx;\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_stream_limit_conn(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_shm_zone_t                 *shm_zone;\n    ngx_stream_limit_conn_conf_t   *lccf = conf;\n    ngx_stream_limit_conn_limit_t  *limit, *limits;\n\n    ngx_str_t   *value;\n    ngx_int_t    n;\n    ngx_uint_t   i;\n\n    value = cf->args->elts;\n\n    shm_zone = ngx_shared_memory_add(cf, &value[1], 0,\n                                     &ngx_stream_limit_conn_module);\n    if (shm_zone == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    limits = lccf->limits.elts;\n\n    if (limits == NULL) {\n        if (ngx_array_init(&lccf->limits, cf->pool, 1,\n                           sizeof(ngx_stream_limit_conn_limit_t))\n            != NGX_OK)\n        {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    for (i = 0; i < lccf->limits.nelts; i++) {\n        if (shm_zone == limits[i].shm_zone) {\n            return \"is duplicate\";\n        }\n    }\n\n    n = ngx_atoi(value[2].data, value[2].len);\n    if (n <= 0) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid number of connections \\\"%V\\\"\", &value[2]);\n        return NGX_CONF_ERROR;\n    }\n\n    if (n > 65535) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"connection limit must be less 65536\");\n        return NGX_CONF_ERROR;\n    }\n\n    limit = ngx_array_push(&lccf->limits);\n    if (limit == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    limit->conn = n;\n    limit->shm_zone = shm_zone;\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_limit_conn_add_variables(ngx_conf_t *cf)\n{\n    ngx_stream_variable_t  *var, *v;\n\n    for (v = ngx_stream_limit_conn_vars; v->name.len; v++) {\n        var = ngx_stream_add_variable(cf, &v->name, v->flags);\n        if (var == NULL) {\n            return NGX_ERROR;\n        }\n\n        var->get_handler = v->get_handler;\n        var->data = v->data;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_limit_conn_init(ngx_conf_t *cf)\n{\n    ngx_stream_handler_pt        *h;\n    ngx_stream_core_main_conf_t  *cmcf;\n\n    cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);\n\n    h = ngx_array_push(&cmcf->phases[NGX_STREAM_PREACCESS_PHASE].handlers);\n    if (h == NULL) {\n        return NGX_ERROR;\n    }\n\n    *h = ngx_stream_limit_conn_handler;\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "src/stream/ngx_stream_log_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_stream.h>\n\n#if (NGX_ZLIB)\n#include <zlib.h>\n#endif\n\n\ntypedef struct ngx_stream_log_op_s  ngx_stream_log_op_t;\n\ntypedef u_char *(*ngx_stream_log_op_run_pt) (ngx_stream_session_t *s,\n    u_char *buf, ngx_stream_log_op_t *op);\n\ntypedef size_t (*ngx_stream_log_op_getlen_pt) (ngx_stream_session_t *s,\n    uintptr_t data);\n\n\nstruct ngx_stream_log_op_s {\n    size_t                       len;\n    ngx_stream_log_op_getlen_pt  getlen;\n    ngx_stream_log_op_run_pt     run;\n    uintptr_t                    data;\n};\n\n\ntypedef struct {\n    ngx_str_t                    name;\n    ngx_array_t                 *flushes;\n    ngx_array_t                 *ops;        /* array of ngx_stream_log_op_t */\n} ngx_stream_log_fmt_t;\n\n\ntypedef struct {\n    ngx_array_t                  formats;    /* array of ngx_stream_log_fmt_t */\n} ngx_stream_log_main_conf_t;\n\n\ntypedef struct {\n    u_char                      *start;\n    u_char                      *pos;\n    u_char                      *last;\n\n    ngx_event_t                 *event;\n    ngx_msec_t                   flush;\n    ngx_int_t                    gzip;\n} ngx_stream_log_buf_t;\n\n\ntypedef struct {\n    ngx_array_t                 *lengths;\n    ngx_array_t                 *values;\n} ngx_stream_log_script_t;\n\n\ntypedef struct {\n    ngx_open_file_t             *file;\n    ngx_stream_log_script_t     *script;\n    time_t                       disk_full_time;\n    time_t                       error_log_time;\n    ngx_syslog_peer_t           *syslog_peer;\n    ngx_stream_log_fmt_t        *format;\n    ngx_stream_complex_value_t  *filter;\n} ngx_stream_log_t;\n\n\ntypedef struct {\n    ngx_array_t                 *logs;       /* array of ngx_stream_log_t */\n\n    ngx_open_file_cache_t       *open_file_cache;\n    time_t                       open_file_cache_valid;\n    ngx_uint_t                   open_file_cache_min_uses;\n\n    ngx_uint_t                   off;        /* unsigned  off:1 */\n} ngx_stream_log_srv_conf_t;\n\n\ntypedef struct {\n    ngx_str_t                    name;\n    size_t                       len;\n    ngx_stream_log_op_run_pt     run;\n} ngx_stream_log_var_t;\n\n\n#define NGX_STREAM_LOG_ESCAPE_DEFAULT  0\n#define NGX_STREAM_LOG_ESCAPE_JSON     1\n#define NGX_STREAM_LOG_ESCAPE_NONE     2\n\n\nstatic void ngx_stream_log_write(ngx_stream_session_t *s, ngx_stream_log_t *log,\n    u_char *buf, size_t len);\nstatic ssize_t ngx_stream_log_script_write(ngx_stream_session_t *s,\n    ngx_stream_log_script_t *script, u_char **name, u_char *buf, size_t len);\n\n#if (NGX_ZLIB)\nstatic ssize_t ngx_stream_log_gzip(ngx_fd_t fd, u_char *buf, size_t len,\n    ngx_int_t level, ngx_log_t *log);\n\nstatic void *ngx_stream_log_gzip_alloc(void *opaque, u_int items, u_int size);\nstatic void ngx_stream_log_gzip_free(void *opaque, void *address);\n#endif\n\nstatic void ngx_stream_log_flush(ngx_open_file_t *file, ngx_log_t *log);\nstatic void ngx_stream_log_flush_handler(ngx_event_t *ev);\n\nstatic ngx_int_t ngx_stream_log_variable_compile(ngx_conf_t *cf,\n    ngx_stream_log_op_t *op, ngx_str_t *value, ngx_uint_t escape);\nstatic size_t ngx_stream_log_variable_getlen(ngx_stream_session_t *s,\n    uintptr_t data);\nstatic u_char *ngx_stream_log_variable(ngx_stream_session_t *s, u_char *buf,\n    ngx_stream_log_op_t *op);\nstatic uintptr_t ngx_stream_log_escape(u_char *dst, u_char *src, size_t size);\nstatic size_t ngx_stream_log_json_variable_getlen(ngx_stream_session_t *s,\n    uintptr_t data);\nstatic u_char *ngx_stream_log_json_variable(ngx_stream_session_t *s,\n    u_char *buf, ngx_stream_log_op_t *op);\nstatic size_t ngx_stream_log_unescaped_variable_getlen(ngx_stream_session_t *s,\n    uintptr_t data);\nstatic u_char *ngx_stream_log_unescaped_variable(ngx_stream_session_t *s,\n    u_char *buf, ngx_stream_log_op_t *op);\n\n\nstatic void *ngx_stream_log_create_main_conf(ngx_conf_t *cf);\nstatic void *ngx_stream_log_create_srv_conf(ngx_conf_t *cf);\nstatic char *ngx_stream_log_merge_srv_conf(ngx_conf_t *cf, void *parent,\n    void *child);\nstatic char *ngx_stream_log_set_log(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_stream_log_set_format(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_stream_log_compile_format(ngx_conf_t *cf,\n    ngx_array_t *flushes, ngx_array_t *ops, ngx_array_t *args, ngx_uint_t s);\nstatic char *ngx_stream_log_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic ngx_int_t ngx_stream_log_init(ngx_conf_t *cf);\n\n\nstatic ngx_command_t  ngx_stream_log_commands[] = {\n\n    { ngx_string(\"log_format\"),\n      NGX_STREAM_MAIN_CONF|NGX_CONF_2MORE,\n      ngx_stream_log_set_format,\n      NGX_STREAM_MAIN_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"access_log\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_1MORE,\n      ngx_stream_log_set_log,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"open_log_file_cache\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1234,\n      ngx_stream_log_open_file_cache,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      0,\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_stream_module_t  ngx_stream_log_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    ngx_stream_log_init,                   /* postconfiguration */\n\n    ngx_stream_log_create_main_conf,       /* create main configuration */\n    NULL,                                  /* init main configuration */\n\n    ngx_stream_log_create_srv_conf,        /* create server configuration */\n    ngx_stream_log_merge_srv_conf          /* merge server configuration */\n};\n\n\nngx_module_t  ngx_stream_log_module = {\n    NGX_MODULE_V1,\n    &ngx_stream_log_module_ctx,            /* module context */\n    ngx_stream_log_commands,               /* module directives */\n    NGX_STREAM_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_stream_log_handler(ngx_stream_session_t *s)\n{\n    u_char                     *line, *p;\n    size_t                      len, size;\n    ssize_t                     n;\n    ngx_str_t                   val;\n    ngx_uint_t                  i, l;\n    ngx_stream_log_t           *log;\n    ngx_stream_log_op_t        *op;\n    ngx_stream_log_buf_t       *buffer;\n    ngx_stream_log_srv_conf_t  *lscf;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,\n                   \"stream log handler\");\n\n    lscf = ngx_stream_get_module_srv_conf(s, ngx_stream_log_module);\n\n    if (lscf->off || lscf->logs == NULL) {\n        return NGX_OK;\n    }\n\n    log = lscf->logs->elts;\n    for (l = 0; l < lscf->logs->nelts; l++) {\n\n        if (log[l].filter) {\n            if (ngx_stream_complex_value(s, log[l].filter, &val) != NGX_OK) {\n                return NGX_ERROR;\n            }\n\n            if (val.len == 0 || (val.len == 1 && val.data[0] == '0')) {\n                continue;\n            }\n        }\n\n        if (ngx_time() == log[l].disk_full_time) {\n\n            /*\n             * on FreeBSD writing to a full filesystem with enabled softupdates\n             * may block process for much longer time than writing to non-full\n             * filesystem, so we skip writing to a log for one second\n             */\n\n            continue;\n        }\n\n        ngx_stream_script_flush_no_cacheable_variables(s,\n                                                       log[l].format->flushes);\n\n        len = 0;\n        op = log[l].format->ops->elts;\n        for (i = 0; i < log[l].format->ops->nelts; i++) {\n            if (op[i].len == 0) {\n                len += op[i].getlen(s, op[i].data);\n\n            } else {\n                len += op[i].len;\n            }\n        }\n\n        if (log[l].syslog_peer) {\n\n            /* length of syslog's PRI and HEADER message parts */\n            len += sizeof(\"<255>Jan 01 00:00:00 \") - 1\n                   + ngx_cycle->hostname.len + 1\n                   + log[l].syslog_peer->tag.len + 2;\n\n            goto alloc_line;\n        }\n\n        len += NGX_LINEFEED_SIZE;\n\n        buffer = log[l].file ? log[l].file->data : NULL;\n\n        if (buffer) {\n\n            if (len > (size_t) (buffer->last - buffer->pos)) {\n\n                ngx_stream_log_write(s, &log[l], buffer->start,\n                                     buffer->pos - buffer->start);\n\n                buffer->pos = buffer->start;\n            }\n\n            if (len <= (size_t) (buffer->last - buffer->pos)) {\n\n                p = buffer->pos;\n\n                if (buffer->event && p == buffer->start) {\n                    ngx_add_timer(buffer->event, buffer->flush);\n                }\n\n                for (i = 0; i < log[l].format->ops->nelts; i++) {\n                    p = op[i].run(s, p, &op[i]);\n                }\n\n                ngx_linefeed(p);\n\n                buffer->pos = p;\n\n                continue;\n            }\n\n            if (buffer->event && buffer->event->timer_set) {\n                ngx_del_timer(buffer->event);\n            }\n        }\n\n    alloc_line:\n\n        line = ngx_pnalloc(s->connection->pool, len);\n        if (line == NULL) {\n            return NGX_ERROR;\n        }\n\n        p = line;\n\n        if (log[l].syslog_peer) {\n            p = ngx_syslog_add_header(log[l].syslog_peer, line);\n        }\n\n        for (i = 0; i < log[l].format->ops->nelts; i++) {\n            p = op[i].run(s, p, &op[i]);\n        }\n\n        if (log[l].syslog_peer) {\n\n            size = p - line;\n\n            n = ngx_syslog_send(log[l].syslog_peer, line, size);\n\n            if (n < 0) {\n                ngx_log_error(NGX_LOG_WARN, s->connection->log, 0,\n                              \"send() to syslog failed\");\n\n            } else if ((size_t) n != size) {\n                ngx_log_error(NGX_LOG_WARN, s->connection->log, 0,\n                              \"send() to syslog has written only %z of %uz\",\n                              n, size);\n            }\n\n            continue;\n        }\n\n        ngx_linefeed(p);\n\n        ngx_stream_log_write(s, &log[l], line, p - line);\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_stream_log_write(ngx_stream_session_t *s, ngx_stream_log_t *log,\n    u_char *buf, size_t len)\n{\n    u_char                *name;\n    time_t                 now;\n    ssize_t                n;\n    ngx_err_t              err;\n#if (NGX_ZLIB)\n    ngx_stream_log_buf_t  *buffer;\n#endif\n\n    if (log->script == NULL) {\n        name = log->file->name.data;\n\n#if (NGX_ZLIB)\n        buffer = log->file->data;\n\n        if (buffer && buffer->gzip) {\n            n = ngx_stream_log_gzip(log->file->fd, buf, len, buffer->gzip,\n                                    s->connection->log);\n        } else {\n            n = ngx_write_fd(log->file->fd, buf, len);\n        }\n#else\n        n = ngx_write_fd(log->file->fd, buf, len);\n#endif\n\n    } else {\n        name = NULL;\n        n = ngx_stream_log_script_write(s, log->script, &name, buf, len);\n    }\n\n    if (n == (ssize_t) len) {\n        return;\n    }\n\n    now = ngx_time();\n\n#if (T_PIPES)\n    if (name == NULL) {\n        name = (u_char *) \"log file\";\n    }\n#endif\n\n    if (n == -1) {\n        err = ngx_errno;\n\n        if (err == NGX_ENOSPC) {\n            log->disk_full_time = now;\n        }\n\n        if (now - log->error_log_time > 59) {\n            ngx_log_error(NGX_LOG_ALERT, s->connection->log, err,\n                          ngx_write_fd_n \" to \\\"%s\\\" failed\", name);\n\n            log->error_log_time = now;\n        }\n\n        return;\n    }\n\n    if (now - log->error_log_time > 59) {\n        ngx_log_error(NGX_LOG_ALERT, s->connection->log, 0,\n                      ngx_write_fd_n \" to \\\"%s\\\" was incomplete: %z of %uz\",\n                      name, n, len);\n\n        log->error_log_time = now;\n    }\n}\n\n\nstatic ssize_t\nngx_stream_log_script_write(ngx_stream_session_t *s,\n    ngx_stream_log_script_t *script, u_char **name, u_char *buf, size_t len)\n{\n    ssize_t                     n;\n    ngx_str_t                   log;\n    ngx_open_file_info_t        of;\n    ngx_stream_log_srv_conf_t  *lscf;\n\n    if (ngx_stream_script_run(s, &log, script->lengths->elts, 1,\n                              script->values->elts)\n        == NULL)\n    {\n        /* simulate successful logging */\n        return len;\n    }\n\n    log.data[log.len - 1] = '\\0';\n    *name = log.data;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,\n                   \"stream log \\\"%s\\\"\", log.data);\n\n    lscf = ngx_stream_get_module_srv_conf(s, ngx_stream_log_module);\n\n    ngx_memzero(&of, sizeof(ngx_open_file_info_t));\n\n    of.log = 1;\n    of.valid = lscf->open_file_cache_valid;\n    of.min_uses = lscf->open_file_cache_min_uses;\n    of.directio = NGX_OPEN_FILE_DIRECTIO_OFF;\n\n    if (ngx_open_cached_file(lscf->open_file_cache, &log, &of,\n                             s->connection->pool)\n        != NGX_OK)\n    {\n        if (of.err == 0) {\n            /* simulate successful logging */\n            return len;\n        }\n\n        ngx_log_error(NGX_LOG_CRIT, s->connection->log, ngx_errno,\n                      \"%s \\\"%s\\\" failed\", of.failed, log.data);\n        /* simulate successful logging */\n        return len;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,\n                   \"stream log #%d\", of.fd);\n\n    n = ngx_write_fd(of.fd, buf, len);\n\n    return n;\n}\n\n\n#if (NGX_ZLIB)\n\nstatic ssize_t\nngx_stream_log_gzip(ngx_fd_t fd, u_char *buf, size_t len, ngx_int_t level,\n    ngx_log_t *log)\n{\n    int          rc, wbits, memlevel;\n    u_char      *out;\n    size_t       size;\n    ssize_t      n;\n    z_stream     zstream;\n    ngx_err_t    err;\n    ngx_pool_t  *pool;\n\n    wbits = MAX_WBITS;\n    memlevel = MAX_MEM_LEVEL - 1;\n\n    while ((ssize_t) len < ((1 << (wbits - 1)) - 262)) {\n        wbits--;\n        memlevel--;\n    }\n\n    /*\n     * This is a formula from deflateBound() for conservative upper bound of\n     * compressed data plus 18 bytes of gzip wrapper.\n     */\n\n    size = len + ((len + 7) >> 3) + ((len + 63) >> 6) + 5 + 18;\n\n    ngx_memzero(&zstream, sizeof(z_stream));\n\n    pool = ngx_create_pool(256, log);\n    if (pool == NULL) {\n        /* simulate successful logging */\n        return len;\n    }\n\n    pool->log = log;\n\n    zstream.zalloc = ngx_stream_log_gzip_alloc;\n    zstream.zfree = ngx_stream_log_gzip_free;\n    zstream.opaque = pool;\n\n    out = ngx_pnalloc(pool, size);\n    if (out == NULL) {\n        goto done;\n    }\n\n    zstream.next_in = buf;\n    zstream.avail_in = len;\n    zstream.next_out = out;\n    zstream.avail_out = size;\n\n    rc = deflateInit2(&zstream, (int) level, Z_DEFLATED, wbits + 16, memlevel,\n                      Z_DEFAULT_STRATEGY);\n\n    if (rc != Z_OK) {\n        ngx_log_error(NGX_LOG_ALERT, log, 0, \"deflateInit2() failed: %d\", rc);\n        goto done;\n    }\n\n    ngx_log_debug4(NGX_LOG_DEBUG_STREAM, log, 0,\n                   \"deflate in: ni:%p no:%p ai:%ud ao:%ud\",\n                   zstream.next_in, zstream.next_out,\n                   zstream.avail_in, zstream.avail_out);\n\n    rc = deflate(&zstream, Z_FINISH);\n\n    if (rc != Z_STREAM_END) {\n        ngx_log_error(NGX_LOG_ALERT, log, 0,\n                      \"deflate(Z_FINISH) failed: %d\", rc);\n        goto done;\n    }\n\n    ngx_log_debug5(NGX_LOG_DEBUG_STREAM, log, 0,\n                   \"deflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d\",\n                   zstream.next_in, zstream.next_out,\n                   zstream.avail_in, zstream.avail_out,\n                   rc);\n\n    size -= zstream.avail_out;\n\n    rc = deflateEnd(&zstream);\n\n    if (rc != Z_OK) {\n        ngx_log_error(NGX_LOG_ALERT, log, 0, \"deflateEnd() failed: %d\", rc);\n        goto done;\n    }\n\n    n = ngx_write_fd(fd, out, size);\n\n    if (n != (ssize_t) size) {\n        err = (n == -1) ? ngx_errno : 0;\n\n        ngx_destroy_pool(pool);\n\n        ngx_set_errno(err);\n        return -1;\n    }\n\ndone:\n\n    ngx_destroy_pool(pool);\n\n    /* simulate successful logging */\n    return len;\n}\n\n\nstatic void *\nngx_stream_log_gzip_alloc(void *opaque, u_int items, u_int size)\n{\n    ngx_pool_t *pool = opaque;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_STREAM, pool->log, 0,\n                   \"gzip alloc: n:%ud s:%ud\", items, size);\n\n    return ngx_palloc(pool, items * size);\n}\n\n\nstatic void\nngx_stream_log_gzip_free(void *opaque, void *address)\n{\n#if 0\n    ngx_pool_t *pool = opaque;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pool->log, 0,\n                   \"gzip free: %p\", address);\n#endif\n}\n\n#endif\n\n\nstatic void\nngx_stream_log_flush(ngx_open_file_t *file, ngx_log_t *log)\n{\n    size_t                 len;\n    ssize_t                n;\n    ngx_stream_log_buf_t  *buffer;\n\n    buffer = file->data;\n\n    len = buffer->pos - buffer->start;\n\n    if (len == 0) {\n        return;\n    }\n\n#if (NGX_ZLIB)\n    if (buffer->gzip) {\n        n = ngx_stream_log_gzip(file->fd, buffer->start, len, buffer->gzip,\n                                log);\n    } else {\n        n = ngx_write_fd(file->fd, buffer->start, len);\n    }\n#else\n    n = ngx_write_fd(file->fd, buffer->start, len);\n#endif\n\n    if (n == -1) {\n        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,\n                      ngx_write_fd_n \" to \\\"%s\\\" failed\",\n                      file->name.data);\n\n    } else if ((size_t) n != len) {\n        ngx_log_error(NGX_LOG_ALERT, log, 0,\n                      ngx_write_fd_n \" to \\\"%s\\\" was incomplete: %z of %uz\",\n                      file->name.data, n, len);\n    }\n\n    buffer->pos = buffer->start;\n\n    if (buffer->event && buffer->event->timer_set) {\n        ngx_del_timer(buffer->event);\n    }\n}\n\n\nstatic void\nngx_stream_log_flush_handler(ngx_event_t *ev)\n{\n    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0,\n                   \"stream log buffer flush handler\");\n\n    ngx_stream_log_flush(ev->data, ev->log);\n}\n\n\nstatic u_char *\nngx_stream_log_copy_short(ngx_stream_session_t *s, u_char *buf,\n    ngx_stream_log_op_t *op)\n{\n    size_t     len;\n    uintptr_t  data;\n\n    len = op->len;\n    data = op->data;\n\n    while (len--) {\n        *buf++ = (u_char) (data & 0xff);\n        data >>= 8;\n    }\n\n    return buf;\n}\n\n\nstatic u_char *\nngx_stream_log_copy_long(ngx_stream_session_t *s, u_char *buf,\n    ngx_stream_log_op_t *op)\n{\n    return ngx_cpymem(buf, (u_char *) op->data, op->len);\n}\n\n\nstatic ngx_int_t\nngx_stream_log_variable_compile(ngx_conf_t *cf, ngx_stream_log_op_t *op,\n    ngx_str_t *value, ngx_uint_t escape)\n{\n    ngx_int_t  index;\n\n    index = ngx_stream_get_variable_index(cf, value);\n    if (index == NGX_ERROR) {\n        return NGX_ERROR;\n    }\n\n    op->len = 0;\n\n    switch (escape) {\n    case NGX_STREAM_LOG_ESCAPE_JSON:\n        op->getlen = ngx_stream_log_json_variable_getlen;\n        op->run = ngx_stream_log_json_variable;\n        break;\n\n    case NGX_STREAM_LOG_ESCAPE_NONE:\n        op->getlen = ngx_stream_log_unescaped_variable_getlen;\n        op->run = ngx_stream_log_unescaped_variable;\n        break;\n\n    default: /* NGX_STREAM_LOG_ESCAPE_DEFAULT */\n        op->getlen = ngx_stream_log_variable_getlen;\n        op->run = ngx_stream_log_variable;\n    }\n\n    op->data = index;\n\n    return NGX_OK;\n}\n\n\nstatic size_t\nngx_stream_log_variable_getlen(ngx_stream_session_t *s, uintptr_t data)\n{\n    uintptr_t                     len;\n    ngx_stream_variable_value_t  *value;\n\n    value = ngx_stream_get_indexed_variable(s, data);\n\n    if (value == NULL || value->not_found) {\n        return 1;\n    }\n\n    len = ngx_stream_log_escape(NULL, value->data, value->len);\n\n    value->escape = len ? 1 : 0;\n\n    return value->len + len * 3;\n}\n\n\nstatic u_char *\nngx_stream_log_variable(ngx_stream_session_t *s, u_char *buf,\n    ngx_stream_log_op_t *op)\n{\n    ngx_stream_variable_value_t  *value;\n\n    value = ngx_stream_get_indexed_variable(s, op->data);\n\n    if (value == NULL || value->not_found) {\n        *buf = '-';\n        return buf + 1;\n    }\n\n    if (value->escape == 0) {\n        return ngx_cpymem(buf, value->data, value->len);\n\n    } else {\n        return (u_char *) ngx_stream_log_escape(buf, value->data, value->len);\n    }\n}\n\n\nstatic uintptr_t\nngx_stream_log_escape(u_char *dst, u_char *src, size_t size)\n{\n    ngx_uint_t      n;\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\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] & (1U << (*src & 0x1f))) {\n                n++;\n            }\n            src++;\n            size--;\n        }\n\n        return (uintptr_t) n;\n    }\n\n    while (size) {\n        if (escape[*src >> 5] & (1U << (*src & 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        size--;\n    }\n\n    return (uintptr_t) dst;\n}\n\n\nstatic size_t\nngx_stream_log_json_variable_getlen(ngx_stream_session_t *s, uintptr_t data)\n{\n    uintptr_t                     len;\n    ngx_stream_variable_value_t  *value;\n\n    value = ngx_stream_get_indexed_variable(s, data);\n\n    if (value == NULL || value->not_found) {\n        return 0;\n    }\n\n    len = ngx_escape_json(NULL, value->data, value->len);\n\n    value->escape = len ? 1 : 0;\n\n    return value->len + len;\n}\n\n\nstatic u_char *\nngx_stream_log_json_variable(ngx_stream_session_t *s, u_char *buf,\n    ngx_stream_log_op_t *op)\n{\n    ngx_stream_variable_value_t  *value;\n\n    value = ngx_stream_get_indexed_variable(s, op->data);\n\n    if (value == NULL || value->not_found) {\n        return buf;\n    }\n\n    if (value->escape == 0) {\n        return ngx_cpymem(buf, value->data, value->len);\n\n    } else {\n        return (u_char *) ngx_escape_json(buf, value->data, value->len);\n    }\n}\n\n\nstatic size_t\nngx_stream_log_unescaped_variable_getlen(ngx_stream_session_t *s,\n    uintptr_t data)\n{\n    ngx_stream_variable_value_t  *value;\n\n    value = ngx_stream_get_indexed_variable(s, data);\n\n    if (value == NULL || value->not_found) {\n        return 0;\n    }\n\n    value->escape = 0;\n\n    return value->len;\n}\n\n\nstatic u_char *\nngx_stream_log_unescaped_variable(ngx_stream_session_t *s, u_char *buf,\n                                  ngx_stream_log_op_t *op)\n{\n    ngx_stream_variable_value_t  *value;\n\n    value = ngx_stream_get_indexed_variable(s, op->data);\n\n    if (value == NULL || value->not_found) {\n        return buf;\n    }\n\n    return ngx_cpymem(buf, value->data, value->len);\n}\n\n\nstatic void *\nngx_stream_log_create_main_conf(ngx_conf_t *cf)\n{\n    ngx_stream_log_main_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_log_main_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    if (ngx_array_init(&conf->formats, cf->pool, 4,\n                       sizeof(ngx_stream_log_fmt_t))\n        != NGX_OK)\n    {\n        return NULL;\n    }\n\n    return conf;\n}\n\n\nstatic void *\nngx_stream_log_create_srv_conf(ngx_conf_t *cf)\n{\n    ngx_stream_log_srv_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_log_srv_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    conf->open_file_cache = NGX_CONF_UNSET_PTR;\n\n    return conf;\n}\n\n\nstatic char *\nngx_stream_log_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_stream_log_srv_conf_t *prev = parent;\n    ngx_stream_log_srv_conf_t *conf = child;\n\n    if (conf->open_file_cache == NGX_CONF_UNSET_PTR) {\n\n        conf->open_file_cache = prev->open_file_cache;\n        conf->open_file_cache_valid = prev->open_file_cache_valid;\n        conf->open_file_cache_min_uses = prev->open_file_cache_min_uses;\n\n        if (conf->open_file_cache == NGX_CONF_UNSET_PTR) {\n            conf->open_file_cache = NULL;\n        }\n    }\n\n    if (conf->logs || conf->off) {\n        return NGX_CONF_OK;\n    }\n\n    conf->logs = prev->logs;\n    conf->off = prev->off;\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_stream_log_set_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_stream_log_srv_conf_t *lscf = conf;\n\n    ssize_t                              size;\n    ngx_int_t                            gzip;\n    ngx_uint_t                           i, n;\n    ngx_msec_t                           flush;\n    ngx_str_t                           *value, name, s;\n    ngx_stream_log_t                    *log;\n    ngx_syslog_peer_t                   *peer;\n    ngx_stream_log_buf_t                *buffer;\n    ngx_stream_log_fmt_t                *fmt;\n    ngx_stream_script_compile_t          sc;\n    ngx_stream_log_main_conf_t          *lmcf;\n    ngx_stream_compile_complex_value_t   ccv;\n\n    value = cf->args->elts;\n\n    if (ngx_strcmp(value[1].data, \"off\") == 0) {\n        lscf->off = 1;\n        if (cf->args->nelts == 2) {\n            return NGX_CONF_OK;\n        }\n\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid parameter \\\"%V\\\"\", &value[2]);\n        return NGX_CONF_ERROR;\n    }\n\n    if (lscf->logs == NULL) {\n        lscf->logs = ngx_array_create(cf->pool, 2, sizeof(ngx_stream_log_t));\n        if (lscf->logs == NULL) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    lmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_log_module);\n\n    log = ngx_array_push(lscf->logs);\n    if (log == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_memzero(log, sizeof(ngx_stream_log_t));\n\n\n    if (ngx_strncmp(value[1].data, \"syslog:\", 7) == 0) {\n\n        peer = ngx_pcalloc(cf->pool, sizeof(ngx_syslog_peer_t));\n        if (peer == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        if (ngx_syslog_process_conf(cf, peer) != NGX_CONF_OK) {\n            return NGX_CONF_ERROR;\n        }\n\n        log->syslog_peer = peer;\n\n        goto process_formats;\n#if (T_PIPES) && !(NGX_WIN32)\n    } else if (ngx_strncmp(value[1].data, \"pipe:\", 5) == 0) {\n        if (value[1].len == 5) {\n            return NGX_CONF_ERROR;\n        }\n\n        value[1].len -= 5;\n        value[1].data += 5;\n\n        ngx_open_pipe_t *pipe_conf = ngx_conf_open_pipe(cf->cycle, &value[1], \"w\");\n        if (pipe_conf == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        log->file = pipe_conf->open_fd;\n#ifdef LOG_PIPE_NEED_BACKUP\n        if (log->file != NULL) {\n            name = ngx_log_access_backup;\n            if (ngx_conf_full_name(cf->cycle, &name, 0) != NGX_OK) {\n                return \"fail to set bakup\";\n            }\n\n            log->file->name = name;\n        }\n#endif\n\n        goto process_formats;\n#endif\n    }\n\n    n = ngx_stream_script_variables_count(&value[1]);\n\n    if (n == 0) {\n        log->file = ngx_conf_open_file(cf->cycle, &value[1]);\n        if (log->file == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n    } else {\n        if (ngx_conf_full_name(cf->cycle, &value[1], 0) != NGX_OK) {\n            return NGX_CONF_ERROR;\n        }\n\n        log->script = ngx_pcalloc(cf->pool, sizeof(ngx_stream_log_script_t));\n        if (log->script == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        ngx_memzero(&sc, sizeof(ngx_stream_script_compile_t));\n\n        sc.cf = cf;\n        sc.source = &value[1];\n        sc.lengths = &log->script->lengths;\n        sc.values = &log->script->values;\n        sc.variables = n;\n        sc.complete_lengths = 1;\n        sc.complete_values = 1;\n\n        if (ngx_stream_script_compile(&sc) != NGX_OK) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\nprocess_formats:\n\n    if (cf->args->nelts >= 3) {\n        name = value[2];\n\n    } else {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"log format is not specified\");\n        return NGX_CONF_ERROR;\n    }\n\n    fmt = lmcf->formats.elts;\n    for (i = 0; i < lmcf->formats.nelts; i++) {\n        if (fmt[i].name.len == name.len\n            && ngx_strcasecmp(fmt[i].name.data, name.data) == 0)\n        {\n            log->format = &fmt[i];\n            break;\n        }\n    }\n\n    if (log->format == NULL) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"unknown log format \\\"%V\\\"\", &name);\n        return NGX_CONF_ERROR;\n    }\n\n    size = 0;\n    flush = 0;\n    gzip = 0;\n\n    for (i = 3; i < cf->args->nelts; i++) {\n\n        if (ngx_strncmp(value[i].data, \"buffer=\", 7) == 0) {\n            s.len = value[i].len - 7;\n            s.data = value[i].data + 7;\n\n            size = ngx_parse_size(&s);\n\n            if (size == NGX_ERROR || size == 0) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"invalid buffer size \\\"%V\\\"\", &s);\n                return NGX_CONF_ERROR;\n            }\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"flush=\", 6) == 0) {\n            s.len = value[i].len - 6;\n            s.data = value[i].data + 6;\n\n            flush = ngx_parse_time(&s, 0);\n\n            if (flush == (ngx_msec_t) NGX_ERROR || flush == 0) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"invalid flush time \\\"%V\\\"\", &s);\n                return NGX_CONF_ERROR;\n            }\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"gzip\", 4) == 0\n            && (value[i].len == 4 || value[i].data[4] == '='))\n        {\n#if (NGX_ZLIB)\n            if (size == 0) {\n                size = 64 * 1024;\n            }\n\n            if (value[i].len == 4) {\n                gzip = Z_BEST_SPEED;\n                continue;\n            }\n\n            s.len = value[i].len - 5;\n            s.data = value[i].data + 5;\n\n            gzip = ngx_atoi(s.data, s.len);\n\n            if (gzip < 1 || gzip > 9) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"invalid compression level \\\"%V\\\"\", &s);\n                return NGX_CONF_ERROR;\n            }\n\n            continue;\n\n#else\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"nginx was built without zlib support\");\n            return NGX_CONF_ERROR;\n#endif\n        }\n\n        if (ngx_strncmp(value[i].data, \"if=\", 3) == 0) {\n            s.len = value[i].len - 3;\n            s.data = value[i].data + 3;\n\n            ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t));\n\n            ccv.cf = cf;\n            ccv.value = &s;\n            ccv.complex_value = ngx_palloc(cf->pool,\n                                           sizeof(ngx_stream_complex_value_t));\n            if (ccv.complex_value == NULL) {\n                return NGX_CONF_ERROR;\n            }\n\n            if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) {\n                return NGX_CONF_ERROR;\n            }\n\n            log->filter = ccv.complex_value;\n\n            continue;\n        }\n\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid parameter \\\"%V\\\"\", &value[i]);\n        return NGX_CONF_ERROR;\n    }\n\n    if (flush && size == 0) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"no buffer is defined for access_log \\\"%V\\\"\",\n                           &value[1]);\n        return NGX_CONF_ERROR;\n    }\n\n    if (size) {\n\n        if (log->script) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"buffered logs cannot have variables in name\");\n            return NGX_CONF_ERROR;\n        }\n\n        if (log->syslog_peer) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"logs to syslog cannot be buffered\");\n            return NGX_CONF_ERROR;\n        }\n\n        if (log->file->data) {\n            buffer = log->file->data;\n\n            if (buffer->last - buffer->start != size\n                || buffer->flush != flush\n                || buffer->gzip != gzip)\n            {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"access_log \\\"%V\\\" already defined \"\n                                   \"with conflicting parameters\",\n                                   &value[1]);\n                return NGX_CONF_ERROR;\n            }\n\n            return NGX_CONF_OK;\n        }\n\n        buffer = ngx_pcalloc(cf->pool, sizeof(ngx_stream_log_buf_t));\n        if (buffer == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        buffer->start = ngx_pnalloc(cf->pool, size);\n        if (buffer->start == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        buffer->pos = buffer->start;\n        buffer->last = buffer->start + size;\n\n        if (flush) {\n            buffer->event = ngx_pcalloc(cf->pool, sizeof(ngx_event_t));\n            if (buffer->event == NULL) {\n                return NGX_CONF_ERROR;\n            }\n\n            buffer->event->data = log->file;\n            buffer->event->handler = ngx_stream_log_flush_handler;\n            buffer->event->log = &cf->cycle->new_log;\n            buffer->event->cancelable = 1;\n\n            buffer->flush = flush;\n        }\n\n        buffer->gzip = gzip;\n\n        log->file->flush = ngx_stream_log_flush;\n        log->file->data = buffer;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_stream_log_set_format(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_stream_log_main_conf_t *lmcf = conf;\n\n    ngx_str_t             *value;\n    ngx_uint_t             i;\n    ngx_stream_log_fmt_t  *fmt;\n\n    value = cf->args->elts;\n\n    fmt = lmcf->formats.elts;\n    for (i = 0; i < lmcf->formats.nelts; i++) {\n        if (fmt[i].name.len == value[1].len\n            && ngx_strcmp(fmt[i].name.data, value[1].data) == 0)\n        {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"duplicate \\\"log_format\\\" name \\\"%V\\\"\",\n                               &value[1]);\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    fmt = ngx_array_push(&lmcf->formats);\n    if (fmt == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    fmt->name = value[1];\n\n    fmt->flushes = ngx_array_create(cf->pool, 4, sizeof(ngx_int_t));\n    if (fmt->flushes == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    fmt->ops = ngx_array_create(cf->pool, 16, sizeof(ngx_stream_log_op_t));\n    if (fmt->ops == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    return ngx_stream_log_compile_format(cf, fmt->flushes, fmt->ops,\n                                         cf->args, 2);\n}\n\n\nstatic char *\nngx_stream_log_compile_format(ngx_conf_t *cf, ngx_array_t *flushes,\n    ngx_array_t *ops, ngx_array_t *args, ngx_uint_t s)\n{\n    u_char                *data, *p, ch;\n    size_t                 i, len;\n    ngx_str_t             *value, var;\n    ngx_int_t             *flush;\n    ngx_uint_t             bracket, escape;\n    ngx_stream_log_op_t   *op;\n\n    escape = NGX_STREAM_LOG_ESCAPE_DEFAULT;\n    value = args->elts;\n\n    if (s < args->nelts && ngx_strncmp(value[s].data, \"escape=\", 7) == 0) {\n        data = value[s].data + 7;\n\n        if (ngx_strcmp(data, \"json\") == 0) {\n            escape = NGX_STREAM_LOG_ESCAPE_JSON;\n\n        } else if (ngx_strcmp(data, \"none\") == 0) {\n            escape = NGX_STREAM_LOG_ESCAPE_NONE;\n\n        } else if (ngx_strcmp(data, \"default\") != 0) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"unknown log format escaping \\\"%s\\\"\", data);\n            return NGX_CONF_ERROR;\n        }\n\n        s++;\n    }\n\n    for ( /* void */ ; s < args->nelts; s++) {\n\n        i = 0;\n\n        while (i < value[s].len) {\n\n            op = ngx_array_push(ops);\n            if (op == NULL) {\n                return NGX_CONF_ERROR;\n            }\n\n            data = &value[s].data[i];\n\n            if (value[s].data[i] == '$') {\n\n                if (++i == value[s].len) {\n                    goto invalid;\n                }\n\n                if (value[s].data[i] == '{') {\n                    bracket = 1;\n\n                    if (++i == value[s].len) {\n                        goto invalid;\n                    }\n\n                    var.data = &value[s].data[i];\n\n                } else {\n                    bracket = 0;\n                    var.data = &value[s].data[i];\n                }\n\n                for (var.len = 0; i < value[s].len; i++, var.len++) {\n                    ch = value[s].data[i];\n\n                    if (ch == '}' && bracket) {\n                        i++;\n                        bracket = 0;\n                        break;\n                    }\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_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                       \"the closing bracket in \\\"%V\\\" \"\n                                       \"variable is missing\", &var);\n                    return NGX_CONF_ERROR;\n                }\n\n                if (var.len == 0) {\n                    goto invalid;\n                }\n\n                if (ngx_stream_log_variable_compile(cf, op, &var, escape)\n                    != NGX_OK)\n                {\n                    return NGX_CONF_ERROR;\n                }\n\n                if (flushes) {\n\n                    flush = ngx_array_push(flushes);\n                    if (flush == NULL) {\n                        return NGX_CONF_ERROR;\n                    }\n\n                    *flush = op->data; /* variable index */\n                }\n\n                continue;\n            }\n\n            i++;\n\n            while (i < value[s].len && value[s].data[i] != '$') {\n                i++;\n            }\n\n            len = &value[s].data[i] - data;\n\n            if (len) {\n\n                op->len = len;\n                op->getlen = NULL;\n\n                if (len <= sizeof(uintptr_t)) {\n                    op->run = ngx_stream_log_copy_short;\n                    op->data = 0;\n\n                    while (len--) {\n                        op->data <<= 8;\n                        op->data |= data[len];\n                    }\n\n                } else {\n                    op->run = ngx_stream_log_copy_long;\n\n                    p = ngx_pnalloc(cf->pool, len);\n                    if (p == NULL) {\n                        return NGX_CONF_ERROR;\n                    }\n\n                    ngx_memcpy(p, data, len);\n                    op->data = (uintptr_t) p;\n                }\n            }\n        }\n    }\n\n    return NGX_CONF_OK;\n\ninvalid:\n\n    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, \"invalid parameter \\\"%s\\\"\", data);\n\n    return NGX_CONF_ERROR;\n}\n\n\nstatic char *\nngx_stream_log_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_stream_log_srv_conf_t *lscf = conf;\n\n    time_t       inactive, valid;\n    ngx_str_t   *value, s;\n    ngx_int_t    max, min_uses;\n    ngx_uint_t   i;\n\n    if (lscf->open_file_cache != NGX_CONF_UNSET_PTR) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    max = 0;\n    inactive = 10;\n    valid = 60;\n    min_uses = 1;\n\n    for (i = 1; i < cf->args->nelts; i++) {\n\n        if (ngx_strncmp(value[i].data, \"max=\", 4) == 0) {\n\n            max = ngx_atoi(value[i].data + 4, value[i].len - 4);\n            if (max == NGX_ERROR) {\n                goto failed;\n            }\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"inactive=\", 9) == 0) {\n\n            s.len = value[i].len - 9;\n            s.data = value[i].data + 9;\n\n            inactive = ngx_parse_time(&s, 1);\n            if (inactive == (time_t) NGX_ERROR) {\n                goto failed;\n            }\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"min_uses=\", 9) == 0) {\n\n            min_uses = ngx_atoi(value[i].data + 9, value[i].len - 9);\n            if (min_uses == NGX_ERROR) {\n                goto failed;\n            }\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"valid=\", 6) == 0) {\n\n            s.len = value[i].len - 6;\n            s.data = value[i].data + 6;\n\n            valid = ngx_parse_time(&s, 1);\n            if (valid == (time_t) NGX_ERROR) {\n                goto failed;\n            }\n\n            continue;\n        }\n\n        if (ngx_strcmp(value[i].data, \"off\") == 0) {\n\n            lscf->open_file_cache = NULL;\n\n            continue;\n        }\n\n    failed:\n\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid \\\"open_log_file_cache\\\" parameter \\\"%V\\\"\",\n                           &value[i]);\n        return NGX_CONF_ERROR;\n    }\n\n    if (lscf->open_file_cache == NULL) {\n        return NGX_CONF_OK;\n    }\n\n    if (max == 0) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                        \"\\\"open_log_file_cache\\\" must have \\\"max\\\" parameter\");\n        return NGX_CONF_ERROR;\n    }\n\n    lscf->open_file_cache = ngx_open_file_cache_init(cf->pool, max, inactive);\n\n    if (lscf->open_file_cache) {\n\n        lscf->open_file_cache_valid = valid;\n        lscf->open_file_cache_min_uses = min_uses;\n\n        return NGX_CONF_OK;\n    }\n\n    return NGX_CONF_ERROR;\n}\n\n\nstatic ngx_int_t\nngx_stream_log_init(ngx_conf_t *cf)\n{\n    ngx_stream_handler_pt        *h;\n    ngx_stream_core_main_conf_t  *cmcf;\n\n    cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);\n\n    h = ngx_array_push(&cmcf->phases[NGX_STREAM_LOG_PHASE].handlers);\n    if (h == NULL) {\n        return NGX_ERROR;\n    }\n\n    *h = ngx_stream_log_handler;\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "src/stream/ngx_stream_map_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_stream.h>\n\n\ntypedef struct {\n    ngx_uint_t                    hash_max_size;\n    ngx_uint_t                    hash_bucket_size;\n} ngx_stream_map_conf_t;\n\n\ntypedef struct {\n    ngx_hash_keys_arrays_t        keys;\n\n    ngx_array_t                  *values_hash;\n#if (NGX_PCRE)\n    ngx_array_t                   regexes;\n#endif\n\n    ngx_stream_variable_value_t  *default_value;\n    ngx_conf_t                   *cf;\n    unsigned                      hostnames:1;\n    unsigned                      no_cacheable:1;\n} ngx_stream_map_conf_ctx_t;\n\n\ntypedef struct {\n    ngx_stream_map_t              map;\n    ngx_stream_complex_value_t    value;\n    ngx_stream_variable_value_t  *default_value;\n    ngx_uint_t                    hostnames;      /* unsigned  hostnames:1 */\n} ngx_stream_map_ctx_t;\n\n\nstatic int ngx_libc_cdecl ngx_stream_map_cmp_dns_wildcards(const void *one,\n    const void *two);\nstatic void *ngx_stream_map_create_conf(ngx_conf_t *cf);\nstatic char *ngx_stream_map_block(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_stream_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf);\n\n\nstatic ngx_command_t  ngx_stream_map_commands[] = {\n\n    { ngx_string(\"map\"),\n      NGX_STREAM_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE2,\n      ngx_stream_map_block,\n      NGX_STREAM_MAIN_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"map_hash_max_size\"),\n      NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_num_slot,\n      NGX_STREAM_MAIN_CONF_OFFSET,\n      offsetof(ngx_stream_map_conf_t, hash_max_size),\n      NULL },\n\n    { ngx_string(\"map_hash_bucket_size\"),\n      NGX_STREAM_MAIN_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_num_slot,\n      NGX_STREAM_MAIN_CONF_OFFSET,\n      offsetof(ngx_stream_map_conf_t, hash_bucket_size),\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_stream_module_t  ngx_stream_map_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    NULL,                                  /* postconfiguration */\n\n    ngx_stream_map_create_conf,            /* create main configuration */\n    NULL,                                  /* init main configuration */\n\n    NULL,                                  /* create server configuration */\n    NULL                                   /* merge server configuration */\n};\n\n\nngx_module_t  ngx_stream_map_module = {\n    NGX_MODULE_V1,\n    &ngx_stream_map_module_ctx,            /* module context */\n    ngx_stream_map_commands,               /* module directives */\n    NGX_STREAM_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_stream_map_variable(ngx_stream_session_t *s, ngx_stream_variable_value_t *v,\n    uintptr_t data)\n{\n    ngx_stream_map_ctx_t  *map = (ngx_stream_map_ctx_t *) data;\n\n    ngx_str_t                     val, str;\n    ngx_stream_complex_value_t   *cv;\n    ngx_stream_variable_value_t  *value;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,\n                   \"stream map started\");\n\n    if (ngx_stream_complex_value(s, &map->value, &val) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    if (map->hostnames && val.len > 0 && val.data[val.len - 1] == '.') {\n        val.len--;\n    }\n\n    value = ngx_stream_map_find(s, &map->map, &val);\n\n    if (value == NULL) {\n        value = map->default_value;\n    }\n\n    if (!value->valid) {\n        cv = (ngx_stream_complex_value_t *) value->data;\n\n        if (ngx_stream_complex_value(s, cv, &str) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        v->valid = 1;\n        v->no_cacheable = 0;\n        v->not_found = 0;\n        v->len = str.len;\n        v->data = str.data;\n\n    } else {\n        *v = *value;\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,\n                   \"stream map: \\\"%V\\\" \\\"%v\\\"\", &val, v);\n\n    return NGX_OK;\n}\n\n\nstatic void *\nngx_stream_map_create_conf(ngx_conf_t *cf)\n{\n    ngx_stream_map_conf_t  *mcf;\n\n    mcf = ngx_palloc(cf->pool, sizeof(ngx_stream_map_conf_t));\n    if (mcf == NULL) {\n        return NULL;\n    }\n\n    mcf->hash_max_size = NGX_CONF_UNSET_UINT;\n    mcf->hash_bucket_size = NGX_CONF_UNSET_UINT;\n\n    return mcf;\n}\n\n\nstatic char *\nngx_stream_map_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_stream_map_conf_t  *mcf = conf;\n\n    char                                *rv;\n    ngx_str_t                           *value, name;\n    ngx_conf_t                           save;\n    ngx_pool_t                          *pool;\n    ngx_hash_init_t                      hash;\n    ngx_stream_map_ctx_t                *map;\n    ngx_stream_variable_t               *var;\n    ngx_stream_map_conf_ctx_t            ctx;\n    ngx_stream_compile_complex_value_t   ccv;\n\n    if (mcf->hash_max_size == NGX_CONF_UNSET_UINT) {\n        mcf->hash_max_size = 2048;\n    }\n\n    if (mcf->hash_bucket_size == NGX_CONF_UNSET_UINT) {\n        mcf->hash_bucket_size = ngx_cacheline_size;\n\n    } else {\n        mcf->hash_bucket_size = ngx_align(mcf->hash_bucket_size,\n                                          ngx_cacheline_size);\n    }\n\n    map = ngx_pcalloc(cf->pool, sizeof(ngx_stream_map_ctx_t));\n    if (map == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    value = cf->args->elts;\n\n    ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t));\n\n    ccv.cf = cf;\n    ccv.value = &value[1];\n    ccv.complex_value = &map->value;\n\n    if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    name = value[2];\n\n    if (name.data[0] != '$') {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid variable name \\\"%V\\\"\", &name);\n        return NGX_CONF_ERROR;\n    }\n\n    name.len--;\n    name.data++;\n\n    var = ngx_stream_add_variable(cf, &name, NGX_STREAM_VAR_CHANGEABLE);\n    if (var == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    var->get_handler = ngx_stream_map_variable;\n    var->data = (uintptr_t) map;\n\n    pool = ngx_create_pool(NGX_DEFAULT_POOL_SIZE, cf->log);\n    if (pool == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    ctx.keys.pool = cf->pool;\n    ctx.keys.temp_pool = pool;\n\n    if (ngx_hash_keys_array_init(&ctx.keys, NGX_HASH_LARGE) != NGX_OK) {\n        ngx_destroy_pool(pool);\n        return NGX_CONF_ERROR;\n    }\n\n    ctx.values_hash = ngx_pcalloc(pool, sizeof(ngx_array_t) * ctx.keys.hsize);\n    if (ctx.values_hash == NULL) {\n        ngx_destroy_pool(pool);\n        return NGX_CONF_ERROR;\n    }\n\n#if (NGX_PCRE)\n    if (ngx_array_init(&ctx.regexes, cf->pool, 2,\n                       sizeof(ngx_stream_map_regex_t))\n        != NGX_OK)\n    {\n        ngx_destroy_pool(pool);\n        return NGX_CONF_ERROR;\n    }\n#endif\n\n    ctx.default_value = NULL;\n    ctx.cf = &save;\n    ctx.hostnames = 0;\n    ctx.no_cacheable = 0;\n\n    save = *cf;\n    cf->pool = pool;\n    cf->ctx = &ctx;\n    cf->handler = ngx_stream_map;\n    cf->handler_conf = conf;\n\n    rv = ngx_conf_parse(cf, NULL);\n\n    *cf = save;\n\n    if (rv != NGX_CONF_OK) {\n        ngx_destroy_pool(pool);\n        return rv;\n    }\n\n    if (ctx.no_cacheable) {\n        var->flags |= NGX_STREAM_VAR_NOCACHEABLE;\n    }\n\n    map->default_value = ctx.default_value ? ctx.default_value:\n                                             &ngx_stream_variable_null_value;\n\n    map->hostnames = ctx.hostnames;\n\n    hash.key = ngx_hash_key_lc;\n    hash.max_size = mcf->hash_max_size;\n    hash.bucket_size = mcf->hash_bucket_size;\n    hash.name = \"map_hash\";\n    hash.pool = cf->pool;\n\n    if (ctx.keys.keys.nelts) {\n        hash.hash = &map->map.hash.hash;\n        hash.temp_pool = NULL;\n\n        if (ngx_hash_init(&hash, ctx.keys.keys.elts, ctx.keys.keys.nelts)\n            != NGX_OK)\n        {\n            ngx_destroy_pool(pool);\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    if (ctx.keys.dns_wc_head.nelts) {\n\n        ngx_qsort(ctx.keys.dns_wc_head.elts,\n                  (size_t) ctx.keys.dns_wc_head.nelts,\n                  sizeof(ngx_hash_key_t), ngx_stream_map_cmp_dns_wildcards);\n\n        hash.hash = NULL;\n        hash.temp_pool = pool;\n\n        if (ngx_hash_wildcard_init(&hash, ctx.keys.dns_wc_head.elts,\n                                   ctx.keys.dns_wc_head.nelts)\n            != NGX_OK)\n        {\n            ngx_destroy_pool(pool);\n            return NGX_CONF_ERROR;\n        }\n\n        map->map.hash.wc_head = (ngx_hash_wildcard_t *) hash.hash;\n    }\n\n    if (ctx.keys.dns_wc_tail.nelts) {\n\n        ngx_qsort(ctx.keys.dns_wc_tail.elts,\n                  (size_t) ctx.keys.dns_wc_tail.nelts,\n                  sizeof(ngx_hash_key_t), ngx_stream_map_cmp_dns_wildcards);\n\n        hash.hash = NULL;\n        hash.temp_pool = pool;\n\n        if (ngx_hash_wildcard_init(&hash, ctx.keys.dns_wc_tail.elts,\n                                   ctx.keys.dns_wc_tail.nelts)\n            != NGX_OK)\n        {\n            ngx_destroy_pool(pool);\n            return NGX_CONF_ERROR;\n        }\n\n        map->map.hash.wc_tail = (ngx_hash_wildcard_t *) hash.hash;\n    }\n\n#if (NGX_PCRE)\n\n    if (ctx.regexes.nelts) {\n        map->map.regex = ctx.regexes.elts;\n        map->map.nregex = ctx.regexes.nelts;\n    }\n\n#endif\n\n    ngx_destroy_pool(pool);\n\n    return rv;\n}\n\n\nstatic int ngx_libc_cdecl\nngx_stream_map_cmp_dns_wildcards(const void *one, const void *two)\n{\n    ngx_hash_key_t  *first, *second;\n\n    first = (ngx_hash_key_t *) one;\n    second = (ngx_hash_key_t *) two;\n\n    return ngx_dns_strcmp(first->key.data, second->key.data);\n}\n\n\nstatic char *\nngx_stream_map(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)\n{\n    u_char                              *data;\n    size_t                               len;\n    ngx_int_t                            rv;\n    ngx_str_t                           *value, v;\n    ngx_uint_t                           i, key;\n    ngx_stream_map_conf_ctx_t           *ctx;\n    ngx_stream_complex_value_t           cv, *cvp;\n    ngx_stream_variable_value_t         *var, **vp;\n    ngx_stream_compile_complex_value_t   ccv;\n\n    ctx = cf->ctx;\n\n    value = cf->args->elts;\n\n    if (cf->args->nelts == 1\n        && ngx_strcmp(value[0].data, \"hostnames\") == 0)\n    {\n        ctx->hostnames = 1;\n        return NGX_CONF_OK;\n    }\n\n    if (cf->args->nelts == 1\n        && ngx_strcmp(value[0].data, \"volatile\") == 0)\n    {\n        ctx->no_cacheable = 1;\n        return NGX_CONF_OK;\n    }\n\n    if (cf->args->nelts != 2) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid number of the map parameters\");\n        return NGX_CONF_ERROR;\n    }\n\n    if (ngx_strcmp(value[0].data, \"include\") == 0) {\n        return ngx_conf_include(cf, dummy, conf);\n    }\n\n    key = 0;\n\n    for (i = 0; i < value[1].len; i++) {\n        key = ngx_hash(key, value[1].data[i]);\n    }\n\n    key %= ctx->keys.hsize;\n\n    vp = ctx->values_hash[key].elts;\n\n    if (vp) {\n        for (i = 0; i < ctx->values_hash[key].nelts; i++) {\n\n            if (vp[i]->valid) {\n                data = vp[i]->data;\n                len = vp[i]->len;\n\n            } else {\n                cvp = (ngx_stream_complex_value_t *) vp[i]->data;\n                data = cvp->value.data;\n                len = cvp->value.len;\n            }\n\n            if (value[1].len != len) {\n                continue;\n            }\n\n            if (ngx_strncmp(value[1].data, data, len) == 0) {\n                var = vp[i];\n                goto found;\n            }\n        }\n\n    } else {\n        if (ngx_array_init(&ctx->values_hash[key], cf->pool, 4,\n                           sizeof(ngx_stream_variable_value_t *))\n            != NGX_OK)\n        {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    var = ngx_palloc(ctx->keys.pool, sizeof(ngx_stream_variable_value_t));\n    if (var == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    v.len = value[1].len;\n    v.data = ngx_pstrdup(ctx->keys.pool, &value[1]);\n    if (v.data == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t));\n\n    ccv.cf = ctx->cf;\n    ccv.value = &v;\n    ccv.complex_value = &cv;\n\n    if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (cv.lengths != NULL) {\n        cvp = ngx_palloc(ctx->keys.pool, sizeof(ngx_stream_complex_value_t));\n        if (cvp == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        *cvp = cv;\n\n        var->len = 0;\n        var->data = (u_char *) cvp;\n        var->valid = 0;\n\n    } else {\n        var->len = v.len;\n        var->data = v.data;\n        var->valid = 1;\n    }\n\n    var->no_cacheable = 0;\n    var->not_found = 0;\n\n    vp = ngx_array_push(&ctx->values_hash[key]);\n    if (vp == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    *vp = var;\n\nfound:\n\n    if (ngx_strcmp(value[0].data, \"default\") == 0) {\n\n        if (ctx->default_value) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"duplicate default map parameter\");\n            return NGX_CONF_ERROR;\n        }\n\n        ctx->default_value = var;\n\n        return NGX_CONF_OK;\n    }\n\n#if (NGX_PCRE)\n\n    if (value[0].len && value[0].data[0] == '~') {\n        ngx_regex_compile_t      rc;\n        ngx_stream_map_regex_t  *regex;\n        u_char                   errstr[NGX_MAX_CONF_ERRSTR];\n\n        regex = ngx_array_push(&ctx->regexes);\n        if (regex == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        value[0].len--;\n        value[0].data++;\n\n        ngx_memzero(&rc, sizeof(ngx_regex_compile_t));\n\n        if (value[0].data[0] == '*') {\n            value[0].len--;\n            value[0].data++;\n            rc.options = NGX_REGEX_CASELESS;\n        }\n\n        rc.pattern = value[0];\n        rc.err.len = NGX_MAX_CONF_ERRSTR;\n        rc.err.data = errstr;\n\n        regex->regex = ngx_stream_regex_compile(ctx->cf, &rc);\n        if (regex->regex == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        regex->value = var;\n\n        return NGX_CONF_OK;\n    }\n\n#endif\n\n    if (value[0].len && value[0].data[0] == '\\\\') {\n        value[0].len--;\n        value[0].data++;\n    }\n\n    rv = ngx_hash_add_key(&ctx->keys, &value[0], var,\n                          (ctx->hostnames) ? NGX_HASH_WILDCARD_KEY : 0);\n\n    if (rv == NGX_OK) {\n        return NGX_CONF_OK;\n    }\n\n    if (rv == NGX_DECLINED) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid hostname or wildcard \\\"%V\\\"\", &value[0]);\n    }\n\n    if (rv == NGX_BUSY) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"conflicting parameter \\\"%V\\\"\", &value[0]);\n    }\n\n    return NGX_CONF_ERROR;\n}\n"
  },
  {
    "path": "src/stream/ngx_stream_proxy_module.c",
    "content": "\n/*\n * Copyright (C) Roman Arutyunyan\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_stream.h>\n\n\ntypedef struct {\n    ngx_addr_t                      *addr;\n    ngx_stream_complex_value_t      *value;\n#if (NGX_HAVE_TRANSPARENT_PROXY)\n    ngx_uint_t                       transparent; /* unsigned  transparent:1; */\n#endif\n} ngx_stream_upstream_local_t;\n\n\ntypedef struct {\n    ngx_msec_t                       connect_timeout;\n    ngx_msec_t                       timeout;\n    ngx_msec_t                       next_upstream_timeout;\n    size_t                           buffer_size;\n    ngx_stream_complex_value_t      *upload_rate;\n    ngx_stream_complex_value_t      *download_rate;\n    ngx_uint_t                       requests;\n    ngx_uint_t                       responses;\n    ngx_uint_t                       next_upstream_tries;\n    ngx_flag_t                       next_upstream;\n    ngx_flag_t                       proxy_protocol;\n    ngx_flag_t                       half_close;\n    ngx_stream_upstream_local_t     *local;\n    ngx_flag_t                       socket_keepalive;\n\n#if (NGX_STREAM_SSL)\n    ngx_flag_t                       ssl_enable;\n    ngx_flag_t                       ssl_session_reuse;\n    ngx_uint_t                       ssl_protocols;\n    ngx_str_t                        ssl_ciphers;\n    ngx_stream_complex_value_t      *ssl_name;\n    ngx_flag_t                       ssl_server_name;\n\n    ngx_flag_t                       ssl_verify;\n    ngx_uint_t                       ssl_verify_depth;\n    ngx_str_t                        ssl_trusted_certificate;\n    ngx_str_t                        ssl_crl;\n    ngx_stream_complex_value_t      *ssl_certificate;\n    ngx_stream_complex_value_t      *ssl_certificate_key;\n    ngx_array_t                     *ssl_passwords;\n    ngx_array_t                     *ssl_conf_commands;\n\n    ngx_ssl_t                       *ssl;\n\n#if (T_NGX_SSL_NTLS)\n    const SSL_METHOD                *tls_method;\n    ngx_flag_t                       enable_ntls;\n    ngx_str_t                        enc_certificate;\n    ngx_str_t                        enc_certificate_key;\n    ngx_str_t                        sign_certificate;\n    ngx_str_t                        sign_certificate_key;\n#endif\n#endif\n\n    ngx_stream_upstream_srv_conf_t  *upstream;\n    ngx_stream_complex_value_t      *upstream_value;\n} ngx_stream_proxy_srv_conf_t;\n\n\nstatic void ngx_stream_proxy_handler(ngx_stream_session_t *s);\nstatic ngx_int_t ngx_stream_proxy_eval(ngx_stream_session_t *s,\n    ngx_stream_proxy_srv_conf_t *pscf);\nstatic ngx_int_t ngx_stream_proxy_set_local(ngx_stream_session_t *s,\n    ngx_stream_upstream_t *u, ngx_stream_upstream_local_t *local);\nstatic void ngx_stream_proxy_connect(ngx_stream_session_t *s);\nstatic void ngx_stream_proxy_init_upstream(ngx_stream_session_t *s);\nstatic void ngx_stream_proxy_resolve_handler(ngx_resolver_ctx_t *ctx);\nstatic void ngx_stream_proxy_upstream_handler(ngx_event_t *ev);\nstatic void ngx_stream_proxy_downstream_handler(ngx_event_t *ev);\nstatic void ngx_stream_proxy_process_connection(ngx_event_t *ev,\n    ngx_uint_t from_upstream);\nstatic void ngx_stream_proxy_connect_handler(ngx_event_t *ev);\nstatic ngx_int_t ngx_stream_proxy_test_connect(ngx_connection_t *c);\nstatic void ngx_stream_proxy_process(ngx_stream_session_t *s,\n    ngx_uint_t from_upstream, ngx_uint_t do_write);\nstatic ngx_int_t ngx_stream_proxy_test_finalize(ngx_stream_session_t *s,\n    ngx_uint_t from_upstream);\nstatic void ngx_stream_proxy_next_upstream(ngx_stream_session_t *s);\nstatic void ngx_stream_proxy_finalize(ngx_stream_session_t *s, ngx_uint_t rc);\nstatic u_char *ngx_stream_proxy_log_error(ngx_log_t *log, u_char *buf,\n    size_t len);\n\nstatic void *ngx_stream_proxy_create_srv_conf(ngx_conf_t *cf);\nstatic char *ngx_stream_proxy_merge_srv_conf(ngx_conf_t *cf, void *parent,\n    void *child);\nstatic char *ngx_stream_proxy_pass(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_stream_proxy_bind(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n\n#if (NGX_STREAM_SSL)\n\nstatic ngx_int_t ngx_stream_proxy_send_proxy_protocol(ngx_stream_session_t *s);\nstatic char *ngx_stream_proxy_ssl_password_file(ngx_conf_t *cf,\n    ngx_command_t *cmd, void *conf);\nstatic char *ngx_stream_proxy_ssl_conf_command_check(ngx_conf_t *cf, void *post,\n    void *data);\nstatic void ngx_stream_proxy_ssl_init_connection(ngx_stream_session_t *s);\nstatic void ngx_stream_proxy_ssl_handshake(ngx_connection_t *pc);\nstatic void ngx_stream_proxy_ssl_save_session(ngx_connection_t *c);\nstatic ngx_int_t ngx_stream_proxy_ssl_name(ngx_stream_session_t *s);\nstatic ngx_int_t ngx_stream_proxy_ssl_certificate(ngx_stream_session_t *s);\nstatic ngx_int_t ngx_stream_proxy_merge_ssl(ngx_conf_t *cf,\n    ngx_stream_proxy_srv_conf_t *conf, ngx_stream_proxy_srv_conf_t *prev);\nstatic ngx_int_t ngx_stream_proxy_set_ssl(ngx_conf_t *cf,\n    ngx_stream_proxy_srv_conf_t *pscf);\n\n\nstatic ngx_conf_bitmask_t  ngx_stream_proxy_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    { ngx_string(\"TLSv1.3\"), NGX_SSL_TLSv1_3 },\n\n#if (T_NGX_HAVE_DTLS)\n    { ngx_string(\"DTLSv1\"), NGX_SSL_DTLSv1 },\n    { ngx_string(\"DTLSv1.2\"), NGX_SSL_DTLSv1_2 },\n#endif\n    { ngx_null_string, 0 }\n};\n\nstatic ngx_conf_post_t  ngx_stream_proxy_ssl_conf_command_post =\n    { ngx_stream_proxy_ssl_conf_command_check };\n\n#endif\n\n\nstatic ngx_conf_deprecated_t  ngx_conf_deprecated_proxy_downstream_buffer = {\n    ngx_conf_deprecated, \"proxy_downstream_buffer\", \"proxy_buffer_size\"\n};\n\nstatic ngx_conf_deprecated_t  ngx_conf_deprecated_proxy_upstream_buffer = {\n    ngx_conf_deprecated, \"proxy_upstream_buffer\", \"proxy_buffer_size\"\n};\n\n\nstatic ngx_command_t  ngx_stream_proxy_commands[] = {\n\n    { ngx_string(\"proxy_pass\"),\n      NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_stream_proxy_pass,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"proxy_bind\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE12,\n      ngx_stream_proxy_bind,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"proxy_socket_keepalive\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_proxy_srv_conf_t, socket_keepalive),\n      NULL },\n\n    { ngx_string(\"proxy_connect_timeout\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_proxy_srv_conf_t, connect_timeout),\n      NULL },\n\n    { ngx_string(\"proxy_timeout\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_proxy_srv_conf_t, timeout),\n      NULL },\n\n    { ngx_string(\"proxy_buffer_size\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_proxy_srv_conf_t, buffer_size),\n      NULL },\n\n    { ngx_string(\"proxy_downstream_buffer\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_proxy_srv_conf_t, buffer_size),\n      &ngx_conf_deprecated_proxy_downstream_buffer },\n\n    { ngx_string(\"proxy_upstream_buffer\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_size_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_proxy_srv_conf_t, buffer_size),\n      &ngx_conf_deprecated_proxy_upstream_buffer },\n\n    { ngx_string(\"proxy_upload_rate\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_stream_set_complex_value_size_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_proxy_srv_conf_t, upload_rate),\n      NULL },\n\n    { ngx_string(\"proxy_download_rate\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_stream_set_complex_value_size_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_proxy_srv_conf_t, download_rate),\n      NULL },\n\n    { ngx_string(\"proxy_requests\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_num_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_proxy_srv_conf_t, requests),\n      NULL },\n\n    { ngx_string(\"proxy_responses\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_num_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_proxy_srv_conf_t, responses),\n      NULL },\n\n    { ngx_string(\"proxy_next_upstream\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_proxy_srv_conf_t, next_upstream),\n      NULL },\n\n    { ngx_string(\"proxy_next_upstream_tries\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_num_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_proxy_srv_conf_t, next_upstream_tries),\n      NULL },\n\n    { ngx_string(\"proxy_next_upstream_timeout\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_proxy_srv_conf_t, next_upstream_timeout),\n      NULL },\n\n    { ngx_string(\"proxy_protocol\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_proxy_srv_conf_t, proxy_protocol),\n      NULL },\n\n    { ngx_string(\"proxy_half_close\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_proxy_srv_conf_t, half_close),\n      NULL },\n\n#if (NGX_STREAM_SSL)\n\n    { ngx_string(\"proxy_ssl\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_proxy_srv_conf_t, ssl_enable),\n      NULL },\n\n    { ngx_string(\"proxy_ssl_session_reuse\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_proxy_srv_conf_t, ssl_session_reuse),\n      NULL },\n\n    { ngx_string(\"proxy_ssl_protocols\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_1MORE,\n      ngx_conf_set_bitmask_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_proxy_srv_conf_t, ssl_protocols),\n      &ngx_stream_proxy_ssl_protocols },\n\n    { ngx_string(\"proxy_ssl_ciphers\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_proxy_srv_conf_t, ssl_ciphers),\n      NULL },\n\n    { ngx_string(\"proxy_ssl_name\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_stream_set_complex_value_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_proxy_srv_conf_t, ssl_name),\n      NULL },\n\n    { ngx_string(\"proxy_ssl_server_name\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_proxy_srv_conf_t, ssl_server_name),\n      NULL },\n\n    { ngx_string(\"proxy_ssl_verify\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_proxy_srv_conf_t, ssl_verify),\n      NULL },\n\n    { ngx_string(\"proxy_ssl_verify_depth\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_num_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_proxy_srv_conf_t, ssl_verify_depth),\n      NULL },\n\n    { ngx_string(\"proxy_ssl_trusted_certificate\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_proxy_srv_conf_t, ssl_trusted_certificate),\n      NULL },\n\n    { ngx_string(\"proxy_ssl_crl\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_proxy_srv_conf_t, ssl_crl),\n      NULL },\n\n    { ngx_string(\"proxy_ssl_certificate\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_stream_set_complex_value_zero_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_proxy_srv_conf_t, ssl_certificate),\n      NULL },\n\n    { ngx_string(\"proxy_ssl_certificate_key\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_stream_set_complex_value_zero_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_proxy_srv_conf_t, ssl_certificate_key),\n      NULL },\n\n    { ngx_string(\"proxy_ssl_password_file\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_stream_proxy_ssl_password_file,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"proxy_ssl_conf_command\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE2,\n      ngx_conf_set_keyval_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_proxy_srv_conf_t, ssl_conf_commands),\n      &ngx_stream_proxy_ssl_conf_command_post },\n\n#if (T_NGX_SSL_NTLS)\n    { ngx_string(\"proxy_enable_ntls\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_flag_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_proxy_srv_conf_t, enable_ntls),\n      NULL },\n\n    { ngx_string(\"proxy_ssl_enc_certificate\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_proxy_srv_conf_t, enc_certificate),\n      NULL },\n\n    { ngx_string(\"proxy_ssl_enc_certificate_key\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_proxy_srv_conf_t, enc_certificate_key),\n      NULL },\n\n    { ngx_string(\"proxy_ssl_sign_certificate\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_proxy_srv_conf_t, sign_certificate),\n      NULL },\n\n    { ngx_string(\"proxy_ssl_sign_certificate_key\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_proxy_srv_conf_t, sign_certificate_key),\n      NULL },\n#endif\n#endif\n\n      ngx_null_command\n};\n\n\nstatic ngx_stream_module_t  ngx_stream_proxy_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    NULL,                                  /* postconfiguration */\n\n    NULL,                                  /* create main configuration */\n    NULL,                                  /* init main configuration */\n\n    ngx_stream_proxy_create_srv_conf,      /* create server configuration */\n    ngx_stream_proxy_merge_srv_conf        /* merge server configuration */\n};\n\n\nngx_module_t  ngx_stream_proxy_module = {\n    NGX_MODULE_V1,\n    &ngx_stream_proxy_module_ctx,          /* module context */\n    ngx_stream_proxy_commands,             /* module directives */\n    NGX_STREAM_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 void\nngx_stream_proxy_handler(ngx_stream_session_t *s)\n{\n    u_char                           *p;\n    ngx_str_t                        *host;\n    ngx_uint_t                        i;\n    ngx_connection_t                 *c;\n    ngx_resolver_ctx_t               *ctx, temp;\n    ngx_stream_upstream_t            *u;\n    ngx_stream_core_srv_conf_t       *cscf;\n    ngx_stream_proxy_srv_conf_t      *pscf;\n    ngx_stream_upstream_srv_conf_t   *uscf, **uscfp;\n    ngx_stream_upstream_main_conf_t  *umcf;\n\n    c = s->connection;\n\n    pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_proxy_module);\n\n    ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0,\n                   \"proxy connection handler\");\n\n    u = ngx_pcalloc(c->pool, sizeof(ngx_stream_upstream_t));\n    if (u == NULL) {\n        ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR);\n        return;\n    }\n\n    s->upstream = u;\n\n    s->log_handler = ngx_stream_proxy_log_error;\n\n    u->requests = 1;\n\n    u->peer.log = c->log;\n    u->peer.log_error = NGX_ERROR_ERR;\n\n    if (ngx_stream_proxy_set_local(s, u, pscf->local) != NGX_OK) {\n        ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR);\n        return;\n    }\n\n    if (pscf->socket_keepalive) {\n        u->peer.so_keepalive = 1;\n    }\n\n    u->peer.type = c->type;\n    u->start_sec = ngx_time();\n\n    c->write->handler = ngx_stream_proxy_downstream_handler;\n    c->read->handler = ngx_stream_proxy_downstream_handler;\n\n    s->upstream_states = ngx_array_create(c->pool, 1,\n                                          sizeof(ngx_stream_upstream_state_t));\n    if (s->upstream_states == NULL) {\n        ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR);\n        return;\n    }\n\n    p = ngx_pnalloc(c->pool, pscf->buffer_size);\n    if (p == NULL) {\n        ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR);\n        return;\n    }\n\n    u->downstream_buf.start = p;\n    u->downstream_buf.end = p + pscf->buffer_size;\n    u->downstream_buf.pos = p;\n    u->downstream_buf.last = p;\n\n    if (c->read->ready) {\n        ngx_post_event(c->read, &ngx_posted_events);\n    }\n\n    if (pscf->upstream_value) {\n        if (ngx_stream_proxy_eval(s, pscf) != NGX_OK) {\n            ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR);\n            return;\n        }\n    }\n\n    if (u->resolved == NULL) {\n\n        uscf = pscf->upstream;\n\n    } else {\n\n#if (NGX_STREAM_SSL)\n        u->ssl_name = u->resolved->host;\n#endif\n\n        host = &u->resolved->host;\n\n        umcf = ngx_stream_get_module_main_conf(s, ngx_stream_upstream_module);\n\n        uscfp = umcf->upstreams.elts;\n\n        for (i = 0; i < umcf->upstreams.nelts; i++) {\n\n            uscf = uscfp[i];\n\n            if (uscf->host.len == host->len\n                && ((uscf->port == 0 && u->resolved->no_port)\n                     || uscf->port == u->resolved->port)\n                && ngx_strncasecmp(uscf->host.data, host->data, host->len) == 0)\n            {\n                goto found;\n            }\n        }\n\n        if (u->resolved->sockaddr) {\n\n            if (u->resolved->port == 0\n                && u->resolved->sockaddr->sa_family != AF_UNIX)\n            {\n                ngx_log_error(NGX_LOG_ERR, c->log, 0,\n                              \"no port in upstream \\\"%V\\\"\", host);\n                ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR);\n                return;\n            }\n\n            if (ngx_stream_upstream_create_round_robin_peer(s, u->resolved)\n                != NGX_OK)\n            {\n                ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR);\n                return;\n            }\n\n            ngx_stream_proxy_connect(s);\n\n            return;\n        }\n\n        if (u->resolved->port == 0) {\n            ngx_log_error(NGX_LOG_ERR, c->log, 0,\n                          \"no port in upstream \\\"%V\\\"\", host);\n            ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR);\n            return;\n        }\n\n        temp.name = *host;\n\n        cscf = ngx_stream_get_module_srv_conf(s, ngx_stream_core_module);\n\n        ctx = ngx_resolve_start(cscf->resolver, &temp);\n        if (ctx == NULL) {\n            ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR);\n            return;\n        }\n\n        if (ctx == NGX_NO_RESOLVER) {\n            ngx_log_error(NGX_LOG_ERR, c->log, 0,\n                          \"no resolver defined to resolve %V\", host);\n            ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR);\n            return;\n        }\n\n        ctx->name = *host;\n        ctx->handler = ngx_stream_proxy_resolve_handler;\n        ctx->data = s;\n        ctx->timeout = cscf->resolver_timeout;\n\n        u->resolved->ctx = ctx;\n\n        if (ngx_resolve_name(ctx) != NGX_OK) {\n            u->resolved->ctx = NULL;\n            ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR);\n            return;\n        }\n\n        return;\n    }\n\nfound:\n\n    if (uscf == NULL) {\n        ngx_log_error(NGX_LOG_ALERT, c->log, 0, \"no upstream configuration\");\n        ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR);\n        return;\n    }\n\n    u->upstream = uscf;\n\n#if (NGX_STREAM_SSL)\n    u->ssl_name = uscf->host;\n#endif\n\n    if (uscf->peer.init(s, uscf) != NGX_OK) {\n        ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR);\n        return;\n    }\n\n    u->peer.start_time = ngx_current_msec;\n\n    if (pscf->next_upstream_tries\n        && u->peer.tries > pscf->next_upstream_tries)\n    {\n        u->peer.tries = pscf->next_upstream_tries;\n    }\n\n    ngx_stream_proxy_connect(s);\n}\n\n\nstatic ngx_int_t\nngx_stream_proxy_eval(ngx_stream_session_t *s,\n    ngx_stream_proxy_srv_conf_t *pscf)\n{\n    ngx_str_t               host;\n    ngx_url_t               url;\n    ngx_stream_upstream_t  *u;\n\n    if (ngx_stream_complex_value(s, pscf->upstream_value, &host) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    ngx_memzero(&url, sizeof(ngx_url_t));\n\n    url.url = host;\n    url.no_resolve = 1;\n\n    if (ngx_parse_url(s->connection->pool, &url) != NGX_OK) {\n        if (url.err) {\n            ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,\n                          \"%s in upstream \\\"%V\\\"\", url.err, &url.url);\n        }\n\n        return NGX_ERROR;\n    }\n\n    u = s->upstream;\n\n    u->resolved = ngx_pcalloc(s->connection->pool,\n                              sizeof(ngx_stream_upstream_resolved_t));\n    if (u->resolved == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (url.addrs) {\n        u->resolved->sockaddr = url.addrs[0].sockaddr;\n        u->resolved->socklen = url.addrs[0].socklen;\n        u->resolved->name = url.addrs[0].name;\n        u->resolved->naddrs = 1;\n    }\n\n    u->resolved->host = url.host;\n    u->resolved->port = url.port;\n    u->resolved->no_port = url.no_port;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_proxy_set_local(ngx_stream_session_t *s, ngx_stream_upstream_t *u,\n    ngx_stream_upstream_local_t *local)\n{\n    ngx_int_t    rc;\n    ngx_str_t    val;\n    ngx_addr_t  *addr;\n\n    if (local == NULL) {\n        u->peer.local = NULL;\n        return NGX_OK;\n    }\n\n#if (NGX_HAVE_TRANSPARENT_PROXY)\n    u->peer.transparent = local->transparent;\n#endif\n\n    if (local->value == NULL) {\n        u->peer.local = local->addr;\n        return NGX_OK;\n    }\n\n    if (ngx_stream_complex_value(s, local->value, &val) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    if (val.len == 0) {\n        return NGX_OK;\n    }\n\n    addr = ngx_palloc(s->connection->pool, sizeof(ngx_addr_t));\n    if (addr == NULL) {\n        return NGX_ERROR;\n    }\n\n    rc = ngx_parse_addr_port(s->connection->pool, addr, val.data, val.len);\n    if (rc == NGX_ERROR) {\n        return NGX_ERROR;\n    }\n\n    if (rc != NGX_OK) {\n        ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,\n                      \"invalid local address \\\"%V\\\"\", &val);\n        return NGX_OK;\n    }\n\n    addr->name = val;\n    u->peer.local = addr;\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_stream_proxy_connect(ngx_stream_session_t *s)\n{\n    ngx_int_t                     rc;\n    ngx_connection_t             *c, *pc;\n    ngx_stream_upstream_t        *u;\n    ngx_stream_proxy_srv_conf_t  *pscf;\n\n    c = s->connection;\n\n    c->log->action = \"connecting to upstream\";\n\n    pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_proxy_module);\n\n    u = s->upstream;\n\n    u->connected = 0;\n    u->proxy_protocol = pscf->proxy_protocol;\n\n    if (u->state) {\n        u->state->response_time = ngx_current_msec - u->start_time;\n    }\n\n    u->state = ngx_array_push(s->upstream_states);\n    if (u->state == NULL) {\n        ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR);\n        return;\n    }\n\n    ngx_memzero(u->state, sizeof(ngx_stream_upstream_state_t));\n\n    u->start_time = ngx_current_msec;\n\n    u->state->connect_time = (ngx_msec_t) -1;\n    u->state->first_byte_time = (ngx_msec_t) -1;\n    u->state->response_time = (ngx_msec_t) -1;\n\n    rc = ngx_event_connect_peer(&u->peer);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0, \"proxy connect: %i\", rc);\n\n    if (rc == NGX_ERROR) {\n        ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR);\n        return;\n    }\n\n    u->state->peer = u->peer.name;\n\n    if (rc == NGX_BUSY) {\n        ngx_log_error(NGX_LOG_ERR, c->log, 0, \"no live upstreams\");\n        ngx_stream_proxy_finalize(s, NGX_STREAM_BAD_GATEWAY);\n        return;\n    }\n\n    if (rc == NGX_DECLINED) {\n        ngx_stream_proxy_next_upstream(s);\n        return;\n    }\n\n    /* rc == NGX_OK || rc == NGX_AGAIN || rc == NGX_DONE */\n\n    pc = u->peer.connection;\n\n    pc->data = s;\n    pc->log = c->log;\n    pc->pool = c->pool;\n    pc->read->log = c->log;\n    pc->write->log = c->log;\n\n    if (rc != NGX_AGAIN) {\n        ngx_stream_proxy_init_upstream(s);\n        return;\n    }\n\n    pc->read->handler = ngx_stream_proxy_connect_handler;\n    pc->write->handler = ngx_stream_proxy_connect_handler;\n\n    ngx_add_timer(pc->write, pscf->connect_timeout);\n}\n\n\nstatic void\nngx_stream_proxy_init_upstream(ngx_stream_session_t *s)\n{\n    u_char                       *p;\n    ngx_chain_t                  *cl;\n    ngx_connection_t             *c, *pc;\n    ngx_log_handler_pt            handler;\n    ngx_stream_upstream_t        *u;\n    ngx_stream_core_srv_conf_t   *cscf;\n    ngx_stream_proxy_srv_conf_t  *pscf;\n\n    u = s->upstream;\n    pc = u->peer.connection;\n\n    cscf = ngx_stream_get_module_srv_conf(s, ngx_stream_core_module);\n\n    if (pc->type == SOCK_STREAM\n        && cscf->tcp_nodelay\n        && ngx_tcp_nodelay(pc) != NGX_OK)\n    {\n        ngx_stream_proxy_next_upstream(s);\n        return;\n    }\n\n    pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_proxy_module);\n\n#if (NGX_STREAM_SSL)\n\n    if (pc->type == SOCK_STREAM && pscf->ssl_enable) {\n\n        if (u->proxy_protocol) {\n            if (ngx_stream_proxy_send_proxy_protocol(s) != NGX_OK) {\n                return;\n            }\n\n            u->proxy_protocol = 0;\n        }\n\n        if (pc->ssl == NULL) {\n            ngx_stream_proxy_ssl_init_connection(s);\n            return;\n        }\n    }\n\n#endif\n\n    c = s->connection;\n\n    if (c->log->log_level >= NGX_LOG_INFO) {\n        ngx_str_t  str;\n        u_char     addr[NGX_SOCKADDR_STRLEN];\n\n        str.len = NGX_SOCKADDR_STRLEN;\n        str.data = addr;\n\n        if (ngx_connection_local_sockaddr(pc, &str, 1) == NGX_OK) {\n            handler = c->log->handler;\n            c->log->handler = NULL;\n\n            ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                          \"%sproxy %V connected to %V\",\n                          pc->type == SOCK_DGRAM ? \"udp \" : \"\",\n                          &str, u->peer.name);\n\n            c->log->handler = handler;\n        }\n    }\n\n    u->state->connect_time = ngx_current_msec - u->start_time;\n\n    if (u->peer.notify) {\n        u->peer.notify(&u->peer, u->peer.data,\n                       NGX_STREAM_UPSTREAM_NOTIFY_CONNECT);\n    }\n\n    if (u->upstream_buf.start == NULL) {\n        p = ngx_pnalloc(c->pool, pscf->buffer_size);\n        if (p == NULL) {\n            ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR);\n            return;\n        }\n\n        u->upstream_buf.start = p;\n        u->upstream_buf.end = p + pscf->buffer_size;\n        u->upstream_buf.pos = p;\n        u->upstream_buf.last = p;\n    }\n\n    if (c->buffer && c->buffer->pos <= c->buffer->last) {\n        ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0,\n                       \"stream proxy add preread buffer: %uz\",\n                       c->buffer->last - c->buffer->pos);\n\n        cl = ngx_chain_get_free_buf(c->pool, &u->free);\n        if (cl == NULL) {\n            ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR);\n            return;\n        }\n\n        *cl->buf = *c->buffer;\n\n        cl->buf->tag = (ngx_buf_tag_t) &ngx_stream_proxy_module;\n        cl->buf->temporary = (cl->buf->pos == cl->buf->last) ? 0 : 1;\n        cl->buf->flush = 1;\n\n        cl->next = u->upstream_out;\n        u->upstream_out = cl;\n    }\n\n    if (u->proxy_protocol) {\n        ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0,\n                       \"stream proxy add PROXY protocol header\");\n\n        cl = ngx_chain_get_free_buf(c->pool, &u->free);\n        if (cl == NULL) {\n            ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR);\n            return;\n        }\n\n        p = ngx_pnalloc(c->pool, NGX_PROXY_PROTOCOL_V1_MAX_HEADER);\n        if (p == NULL) {\n            ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR);\n            return;\n        }\n\n        cl->buf->pos = p;\n\n        p = ngx_proxy_protocol_write(c, p,\n                                     p + NGX_PROXY_PROTOCOL_V1_MAX_HEADER);\n        if (p == NULL) {\n            ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR);\n            return;\n        }\n\n        cl->buf->last = p;\n        cl->buf->temporary = 1;\n        cl->buf->flush = 0;\n        cl->buf->last_buf = 0;\n        cl->buf->tag = (ngx_buf_tag_t) &ngx_stream_proxy_module;\n\n        cl->next = u->upstream_out;\n        u->upstream_out = cl;\n\n        u->proxy_protocol = 0;\n    }\n\n    u->upload_rate = ngx_stream_complex_value_size(s, pscf->upload_rate, 0);\n    u->download_rate = ngx_stream_complex_value_size(s, pscf->download_rate, 0);\n\n    u->connected = 1;\n\n    pc->read->handler = ngx_stream_proxy_upstream_handler;\n    pc->write->handler = ngx_stream_proxy_upstream_handler;\n\n    if (pc->read->ready) {\n        ngx_post_event(pc->read, &ngx_posted_events);\n    }\n\n    ngx_stream_proxy_process(s, 0, 1);\n}\n\n\n#if (NGX_STREAM_SSL)\n\nstatic ngx_int_t\nngx_stream_proxy_send_proxy_protocol(ngx_stream_session_t *s)\n{\n    u_char                       *p;\n    ssize_t                       n, size;\n    ngx_connection_t             *c, *pc;\n    ngx_stream_upstream_t        *u;\n    ngx_stream_proxy_srv_conf_t  *pscf;\n    u_char                        buf[NGX_PROXY_PROTOCOL_V1_MAX_HEADER];\n\n    c = s->connection;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0,\n                   \"stream proxy send PROXY protocol header\");\n\n    p = ngx_proxy_protocol_write(c, buf,\n                                 buf + NGX_PROXY_PROTOCOL_V1_MAX_HEADER);\n    if (p == NULL) {\n        ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR);\n        return NGX_ERROR;\n    }\n\n    u = s->upstream;\n\n    pc = u->peer.connection;\n\n    size = p - buf;\n\n    n = pc->send(pc, buf, size);\n\n    if (n == NGX_AGAIN) {\n        if (ngx_handle_write_event(pc->write, 0) != NGX_OK) {\n            ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR);\n            return NGX_ERROR;\n        }\n\n        pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_proxy_module);\n\n        ngx_add_timer(pc->write, pscf->timeout);\n\n        pc->write->handler = ngx_stream_proxy_connect_handler;\n\n        return NGX_AGAIN;\n    }\n\n    if (n == NGX_ERROR) {\n        ngx_stream_proxy_finalize(s, NGX_STREAM_OK);\n        return NGX_ERROR;\n    }\n\n    if (n != size) {\n\n        /*\n         * PROXY protocol specification:\n         * The sender must always ensure that the header\n         * is sent at once, so that the transport layer\n         * maintains atomicity along the path to the receiver.\n         */\n\n        ngx_log_error(NGX_LOG_ERR, c->log, 0,\n                      \"could not send PROXY protocol header at once\");\n\n        ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR);\n\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic char *\nngx_stream_proxy_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n    ngx_stream_proxy_srv_conf_t *pscf = conf;\n\n    ngx_str_t  *value;\n\n    if (pscf->ssl_passwords != NGX_CONF_UNSET_PTR) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    pscf->ssl_passwords = ngx_ssl_read_password_file(cf, &value[1]);\n\n    if (pscf->ssl_passwords == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_stream_proxy_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#else\n    return NGX_CONF_OK;\n#endif\n}\n\n\nstatic void\nngx_stream_proxy_ssl_init_connection(ngx_stream_session_t *s)\n{\n    ngx_int_t                     rc;\n    ngx_connection_t             *pc;\n    ngx_stream_upstream_t        *u;\n    ngx_stream_proxy_srv_conf_t  *pscf;\n\n    u = s->upstream;\n\n    pc = u->peer.connection;\n\n    pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_proxy_module);\n\n#if (T_NGX_SSL_NTLS)\n    if (pscf->enable_ntls) {\n        if (pscf->tls_method != NTLS_method()) {\n            SSL_CTX_set_ssl_version(pscf->ssl->ctx, NTLS_method());\n            SSL_CTX_set_cipher_list(pscf->ssl->ctx,\n                                    (char *)pscf->ssl_ciphers.data);\n            SSL_CTX_enable_ntls(pscf->ssl->ctx);\n        }\n    } else {\n        if (SSL_CTX_get_ssl_method(pscf->ssl->ctx) == NTLS_method()) {\n            SSL_CTX_set_ssl_version(pscf->ssl->ctx, pscf->tls_method);\n            SSL_CTX_set_cipher_list(pscf->ssl->ctx,\n                                    (char *)pscf->ssl_ciphers.data);\n            SSL_CTX_disable_ntls(pscf->ssl->ctx);\n        }\n    }\n#endif\n\n    if (ngx_ssl_create_connection(pscf->ssl, pc, NGX_SSL_BUFFER|NGX_SSL_CLIENT)\n        != NGX_OK)\n    {\n        ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR);\n        return;\n    }\n\n    if (pscf->ssl_server_name || pscf->ssl_verify) {\n        if (ngx_stream_proxy_ssl_name(s) != NGX_OK) {\n            ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR);\n            return;\n        }\n    }\n\n    if (pscf->ssl_certificate\n        && pscf->ssl_certificate->value.len\n        && (pscf->ssl_certificate->lengths\n            || pscf->ssl_certificate_key->lengths))\n    {\n        if (ngx_stream_proxy_ssl_certificate(s) != NGX_OK) {\n            ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR);\n            return;\n        }\n    }\n\n    if (pscf->ssl_session_reuse) {\n        pc->ssl->save_session = ngx_stream_proxy_ssl_save_session;\n\n        if (u->peer.set_session(&u->peer, u->peer.data) != NGX_OK) {\n            ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR);\n            return;\n        }\n    }\n\n    s->connection->log->action = \"SSL handshaking to upstream\";\n\n    rc = ngx_ssl_handshake(pc);\n\n    if (rc == NGX_AGAIN) {\n\n        if (!pc->write->timer_set) {\n            ngx_add_timer(pc->write, pscf->connect_timeout);\n        }\n\n        pc->ssl->handler = ngx_stream_proxy_ssl_handshake;\n        return;\n    }\n\n    ngx_stream_proxy_ssl_handshake(pc);\n}\n\n\nstatic void\nngx_stream_proxy_ssl_handshake(ngx_connection_t *pc)\n{\n    long                          rc;\n    ngx_stream_session_t         *s;\n    ngx_stream_upstream_t        *u;\n    ngx_stream_proxy_srv_conf_t  *pscf;\n\n    s = pc->data;\n\n    pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_proxy_module);\n\n    if (pc->ssl->handshaked) {\n\n        if (pscf->ssl_verify) {\n            rc = SSL_get_verify_result(pc->ssl->connection);\n\n            if (rc != X509_V_OK) {\n                ngx_log_error(NGX_LOG_ERR, pc->log, 0,\n                              \"upstream SSL certificate verify error: (%l:%s)\",\n                              rc, X509_verify_cert_error_string(rc));\n                goto failed;\n            }\n\n            u = s->upstream;\n\n            if (ngx_ssl_check_host(pc, &u->ssl_name) != NGX_OK) {\n                ngx_log_error(NGX_LOG_ERR, pc->log, 0,\n                              \"upstream SSL certificate does not match \\\"%V\\\"\",\n                              &u->ssl_name);\n                goto failed;\n            }\n        }\n\n        if (pc->write->timer_set) {\n            ngx_del_timer(pc->write);\n        }\n\n        ngx_stream_proxy_init_upstream(s);\n\n        return;\n    }\n\nfailed:\n\n    ngx_stream_proxy_next_upstream(s);\n}\n\n\nstatic void\nngx_stream_proxy_ssl_save_session(ngx_connection_t *c)\n{\n    ngx_stream_session_t   *s;\n    ngx_stream_upstream_t  *u;\n\n    s = c->data;\n    u = s->upstream;\n\n    u->peer.save_session(&u->peer, u->peer.data);\n}\n\n\nstatic ngx_int_t\nngx_stream_proxy_ssl_name(ngx_stream_session_t *s)\n{\n    u_char                       *p, *last;\n    ngx_str_t                     name;\n    ngx_stream_upstream_t        *u;\n    ngx_stream_proxy_srv_conf_t  *pscf;\n\n    pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_proxy_module);\n\n    u = s->upstream;\n\n    if (pscf->ssl_name) {\n        if (ngx_stream_complex_value(s, pscf->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, strip it for compatibility\n     * with the http module\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\n    if (!pscf->ssl_server_name) {\n        goto done;\n    }\n\n#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME\n\n    /* as per RFC 6066, literal IPv4 and IPv6 addresses are not permitted */\n\n    if (name.len == 0 || *name.data == '[') {\n        goto done;\n    }\n\n    if (ngx_inet_addr(name.data, name.len) != INADDR_NONE) {\n        goto done;\n    }\n\n    /*\n     * SSL_set_tlsext_host_name() needs a null-terminated string,\n     * hence we explicitly null-terminate name here\n     */\n\n    p = ngx_pnalloc(s->connection->pool, name.len + 1);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    (void) ngx_cpystrn(p, name.data, name.len + 1);\n\n    name.data = p;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,\n                   \"upstream SSL server name: \\\"%s\\\"\", name.data);\n\n    if (SSL_set_tlsext_host_name(u->peer.connection->ssl->connection,\n                                 (char *) name.data)\n        == 0)\n    {\n        ngx_ssl_error(NGX_LOG_ERR, s->connection->log, 0,\n                      \"SSL_set_tlsext_host_name(\\\"%s\\\") failed\", name.data);\n        return NGX_ERROR;\n    }\n\n#endif\n\ndone:\n\n    u->ssl_name = name;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_proxy_ssl_certificate(ngx_stream_session_t *s)\n{\n    ngx_str_t                     cert, key;\n    ngx_connection_t             *c;\n    ngx_stream_proxy_srv_conf_t  *pscf;\n\n    c = s->upstream->peer.connection;\n\n    pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_proxy_module);\n\n    if (ngx_stream_complex_value(s, pscf->ssl_certificate, &cert)\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0,\n                   \"stream upstream ssl cert: \\\"%s\\\"\", cert.data);\n\n    if (*cert.data == '\\0') {\n        return NGX_OK;\n    }\n\n    if (ngx_stream_complex_value(s, pscf->ssl_certificate_key, &key)\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0,\n                   \"stream upstream ssl key: \\\"%s\\\"\", key.data);\n\n    if (ngx_ssl_connection_certificate(c, c->pool, &cert, &key,\n                                       pscf->ssl_passwords)\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n#endif\n\n\nstatic void\nngx_stream_proxy_downstream_handler(ngx_event_t *ev)\n{\n    ngx_stream_proxy_process_connection(ev, ev->write);\n}\n\n\nstatic void\nngx_stream_proxy_resolve_handler(ngx_resolver_ctx_t *ctx)\n{\n    ngx_stream_session_t            *s;\n    ngx_stream_upstream_t           *u;\n    ngx_stream_proxy_srv_conf_t     *pscf;\n    ngx_stream_upstream_resolved_t  *ur;\n\n    s = ctx->data;\n\n    u = s->upstream;\n    ur = u->resolved;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,\n                   \"stream upstream resolve\");\n\n    if (ctx->state) {\n        ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,\n                      \"%V could not be resolved (%i: %s)\",\n                      &ctx->name, ctx->state,\n                      ngx_resolver_strerror(ctx->state));\n\n        ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR);\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, ur->addrs[i].socklen,\n                                 text, NGX_SOCKADDR_STRLEN, 0);\n\n        ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,\n                       \"name was resolved to %V\", &addr);\n    }\n    }\n#endif\n\n    if (ngx_stream_upstream_create_round_robin_peer(s, ur) != NGX_OK) {\n        ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR);\n        return;\n    }\n\n    ngx_resolve_name_done(ctx);\n    ur->ctx = NULL;\n\n    u->peer.start_time = ngx_current_msec;\n\n    pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_proxy_module);\n\n    if (pscf->next_upstream_tries\n        && u->peer.tries > pscf->next_upstream_tries)\n    {\n        u->peer.tries = pscf->next_upstream_tries;\n    }\n\n    ngx_stream_proxy_connect(s);\n}\n\n\nstatic void\nngx_stream_proxy_upstream_handler(ngx_event_t *ev)\n{\n    ngx_stream_proxy_process_connection(ev, !ev->write);\n}\n\n\nstatic void\nngx_stream_proxy_process_connection(ngx_event_t *ev, ngx_uint_t from_upstream)\n{\n    ngx_connection_t             *c, *pc;\n    ngx_log_handler_pt            handler;\n    ngx_stream_session_t         *s;\n    ngx_stream_upstream_t        *u;\n    ngx_stream_proxy_srv_conf_t  *pscf;\n\n    c = ev->data;\n    s = c->data;\n    u = s->upstream;\n\n    if (c->close) {\n        ngx_log_error(NGX_LOG_INFO, c->log, 0, \"shutdown timeout\");\n        ngx_stream_proxy_finalize(s, NGX_STREAM_OK);\n        return;\n    }\n\n    c = s->connection;\n    pc = u->peer.connection;\n\n    pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_proxy_module);\n\n    if (ev->timedout) {\n        ev->timedout = 0;\n\n        if (ev->delayed) {\n            ev->delayed = 0;\n\n            if (!ev->ready) {\n                if (ngx_handle_read_event(ev, 0) != NGX_OK) {\n                    ngx_stream_proxy_finalize(s,\n                                              NGX_STREAM_INTERNAL_SERVER_ERROR);\n                    return;\n                }\n\n                if (u->connected && !c->read->delayed && !pc->read->delayed) {\n                    ngx_add_timer(c->write, pscf->timeout);\n                }\n\n                return;\n            }\n\n        } else {\n            if (s->connection->type == SOCK_DGRAM) {\n\n                if (pscf->responses == NGX_MAX_INT32_VALUE\n                    || (u->responses >= pscf->responses * u->requests))\n                {\n\n                    /*\n                     * successfully terminate timed out UDP session\n                     * if expected number of responses was received\n                     */\n\n                    handler = c->log->handler;\n                    c->log->handler = NULL;\n\n                    ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                                  \"udp timed out\"\n                                  \", packets from/to client:%ui/%ui\"\n                                  \", bytes from/to client:%O/%O\"\n                                  \", bytes from/to upstream:%O/%O\",\n                                  u->requests, u->responses,\n                                  s->received, c->sent, u->received,\n                                  pc ? pc->sent : 0);\n\n                    c->log->handler = handler;\n\n                    ngx_stream_proxy_finalize(s, NGX_STREAM_OK);\n                    return;\n                }\n\n                ngx_connection_error(pc, NGX_ETIMEDOUT, \"upstream timed out\");\n\n                pc->read->error = 1;\n\n                ngx_stream_proxy_finalize(s, NGX_STREAM_BAD_GATEWAY);\n\n                return;\n            }\n\n            ngx_connection_error(c, NGX_ETIMEDOUT, \"connection timed out\");\n\n            ngx_stream_proxy_finalize(s, NGX_STREAM_OK);\n\n            return;\n        }\n\n    } else if (ev->delayed) {\n\n        ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0,\n                       \"stream connection delayed\");\n\n        if (ngx_handle_read_event(ev, 0) != NGX_OK) {\n            ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR);\n        }\n\n        return;\n    }\n\n    if (from_upstream && !u->connected) {\n        return;\n    }\n\n    ngx_stream_proxy_process(s, from_upstream, ev->write);\n}\n\n\nstatic void\nngx_stream_proxy_connect_handler(ngx_event_t *ev)\n{\n    ngx_connection_t      *c;\n    ngx_stream_session_t  *s;\n\n    c = ev->data;\n    s = c->data;\n\n    if (ev->timedout) {\n        ngx_log_error(NGX_LOG_ERR, c->log, NGX_ETIMEDOUT, \"upstream timed out\");\n        ngx_stream_proxy_next_upstream(s);\n        return;\n    }\n\n    ngx_del_timer(c->write);\n\n    ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0,\n                   \"stream proxy connect upstream\");\n\n    if (ngx_stream_proxy_test_connect(c) != NGX_OK) {\n        ngx_stream_proxy_next_upstream(s);\n        return;\n    }\n\n    ngx_stream_proxy_init_upstream(s);\n}\n\n\nstatic ngx_int_t\nngx_stream_proxy_test_connect(ngx_connection_t *c)\n{\n    int        err;\n    socklen_t  len;\n\n#if (NGX_HAVE_KQUEUE)\n\n    if (ngx_event_flags & NGX_USE_KQUEUE_EVENT)  {\n        err = c->write->kq_errno ? c->write->kq_errno : c->read->kq_errno;\n\n        if (err) {\n            (void) ngx_connection_error(c, err,\n                                    \"kevent() reported that connect() failed\");\n            return NGX_ERROR;\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_socket_errno;\n        }\n\n        if (err) {\n            (void) ngx_connection_error(c, err, \"connect() failed\");\n            return NGX_ERROR;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_stream_proxy_process(ngx_stream_session_t *s, ngx_uint_t from_upstream,\n    ngx_uint_t do_write)\n{\n    char                         *recv_action, *send_action;\n    off_t                        *received, limit;\n    size_t                        size, limit_rate;\n    ssize_t                       n;\n    ngx_buf_t                    *b;\n    ngx_int_t                     rc;\n    ngx_uint_t                    flags, *packets;\n    ngx_msec_t                    delay;\n    ngx_chain_t                  *cl, **ll, **out, **busy;\n    ngx_connection_t             *c, *pc, *src, *dst;\n    ngx_log_handler_pt            handler;\n    ngx_stream_upstream_t        *u;\n    ngx_stream_proxy_srv_conf_t  *pscf;\n\n    u = s->upstream;\n\n    c = s->connection;\n    pc = u->connected ? u->peer.connection : NULL;\n\n    if (c->type == SOCK_DGRAM && (ngx_terminate || ngx_exiting)) {\n\n        /* socket is already closed on worker shutdown */\n\n        handler = c->log->handler;\n        c->log->handler = NULL;\n\n        ngx_log_error(NGX_LOG_INFO, c->log, 0, \"disconnected on shutdown\");\n\n        c->log->handler = handler;\n\n        ngx_stream_proxy_finalize(s, NGX_STREAM_OK);\n        return;\n    }\n\n    pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_proxy_module);\n\n    if (from_upstream) {\n        src = pc;\n        dst = c;\n        b = &u->upstream_buf;\n        limit_rate = u->download_rate;\n        received = &u->received;\n        packets = &u->responses;\n        out = &u->downstream_out;\n        busy = &u->downstream_busy;\n        recv_action = \"proxying and reading from upstream\";\n        send_action = \"proxying and sending to client\";\n\n    } else {\n        src = c;\n        dst = pc;\n        b = &u->downstream_buf;\n        limit_rate = u->upload_rate;\n        received = &s->received;\n        packets = &u->requests;\n        out = &u->upstream_out;\n        busy = &u->upstream_busy;\n        recv_action = \"proxying and reading from client\";\n        send_action = \"proxying and sending to upstream\";\n    }\n\n    for ( ;; ) {\n\n        if (do_write && dst) {\n\n            if (*out || *busy || dst->buffered) {\n                c->log->action = send_action;\n\n                rc = ngx_stream_top_filter(s, *out, from_upstream);\n\n                if (rc == NGX_ERROR) {\n                    ngx_stream_proxy_finalize(s, NGX_STREAM_OK);\n                    return;\n                }\n\n                ngx_chain_update_chains(c->pool, &u->free, busy, out,\n                                      (ngx_buf_tag_t) &ngx_stream_proxy_module);\n\n                if (*busy == NULL) {\n                    b->pos = b->start;\n                    b->last = b->start;\n                }\n            }\n        }\n\n        size = b->end - b->last;\n\n        if (size && src->read->ready && !src->read->delayed) {\n\n            if (limit_rate) {\n                limit = (off_t) limit_rate * (ngx_time() - u->start_sec + 1)\n                        - *received;\n\n                if (limit <= 0) {\n                    src->read->delayed = 1;\n                    delay = (ngx_msec_t) (- limit * 1000 / limit_rate + 1);\n                    ngx_add_timer(src->read, delay);\n                    break;\n                }\n\n                if (c->type == SOCK_STREAM && (off_t) size > limit) {\n                    size = (size_t) limit;\n                }\n            }\n\n            c->log->action = recv_action;\n\n            n = src->recv(src, b->last, size);\n\n            if (n == NGX_AGAIN) {\n                break;\n            }\n\n            if (n == NGX_ERROR) {\n                src->read->eof = 1;\n                n = 0;\n            }\n\n            if (n >= 0) {\n                if (limit_rate) {\n                    delay = (ngx_msec_t) (n * 1000 / limit_rate);\n\n                    if (delay > 0) {\n                        src->read->delayed = 1;\n                        ngx_add_timer(src->read, delay);\n                    }\n                }\n\n                if (from_upstream) {\n                    if (u->state->first_byte_time == (ngx_msec_t) -1) {\n                        u->state->first_byte_time = ngx_current_msec\n                                                    - u->start_time;\n                    }\n                }\n\n                for (ll = out; *ll; ll = &(*ll)->next) { /* void */ }\n\n                cl = ngx_chain_get_free_buf(c->pool, &u->free);\n                if (cl == NULL) {\n                    ngx_stream_proxy_finalize(s,\n                                              NGX_STREAM_INTERNAL_SERVER_ERROR);\n                    return;\n                }\n\n                *ll = cl;\n\n                cl->buf->pos = b->last;\n                cl->buf->last = b->last + n;\n                cl->buf->tag = (ngx_buf_tag_t) &ngx_stream_proxy_module;\n\n                cl->buf->temporary = (n ? 1 : 0);\n                cl->buf->last_buf = src->read->eof;\n                cl->buf->flush = !src->read->eof;\n\n                (*packets)++;\n                *received += n;\n                b->last += n;\n                do_write = 1;\n\n                continue;\n            }\n        }\n\n        break;\n    }\n\n    c->log->action = \"proxying connection\";\n\n    if (ngx_stream_proxy_test_finalize(s, from_upstream) == NGX_OK) {\n        return;\n    }\n\n    flags = src->read->eof ? NGX_CLOSE_EVENT : 0;\n\n    if (ngx_handle_read_event(src->read, flags) != NGX_OK) {\n        ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR);\n        return;\n    }\n\n    if (dst) {\n\n        if (dst->type == SOCK_STREAM && pscf->half_close\n            && src->read->eof && !u->half_closed && !dst->buffered)\n        {\n            if (ngx_shutdown_socket(dst->fd, NGX_WRITE_SHUTDOWN) == -1) {\n                ngx_connection_error(c, ngx_socket_errno,\n                                     ngx_shutdown_socket_n \" failed\");\n\n                ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR);\n                return;\n            }\n\n            u->half_closed = 1;\n            ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,\n                           \"stream proxy %s socket shutdown\",\n                           from_upstream ? \"client\" : \"upstream\");\n        }\n\n        if (ngx_handle_write_event(dst->write, 0) != NGX_OK) {\n            ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR);\n            return;\n        }\n\n        if (!c->read->delayed && !pc->read->delayed) {\n            ngx_add_timer(c->write, pscf->timeout);\n\n        } else if (c->write->timer_set) {\n            ngx_del_timer(c->write);\n        }\n    }\n}\n\n\nstatic ngx_int_t\nngx_stream_proxy_test_finalize(ngx_stream_session_t *s,\n    ngx_uint_t from_upstream)\n{\n    ngx_connection_t             *c, *pc;\n    ngx_log_handler_pt            handler;\n    ngx_stream_upstream_t        *u;\n    ngx_stream_proxy_srv_conf_t  *pscf;\n\n    pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_proxy_module);\n\n    c = s->connection;\n    u = s->upstream;\n    pc = u->connected ? u->peer.connection : NULL;\n\n    if (c->type == SOCK_DGRAM) {\n\n        if (pscf->requests && u->requests < pscf->requests) {\n            return NGX_DECLINED;\n        }\n\n        if (pscf->requests) {\n            ngx_delete_udp_connection(c);\n        }\n\n        if (pscf->responses == NGX_MAX_INT32_VALUE\n            || u->responses < pscf->responses * u->requests)\n        {\n            return NGX_DECLINED;\n        }\n\n        if (pc == NULL || c->buffered || pc->buffered) {\n            return NGX_DECLINED;\n        }\n\n        handler = c->log->handler;\n        c->log->handler = NULL;\n\n        ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                      \"udp done\"\n                      \", packets from/to client:%ui/%ui\"\n                      \", bytes from/to client:%O/%O\"\n                      \", bytes from/to upstream:%O/%O\",\n                      u->requests, u->responses,\n                      s->received, c->sent, u->received, pc ? pc->sent : 0);\n\n        c->log->handler = handler;\n\n        ngx_stream_proxy_finalize(s, NGX_STREAM_OK);\n\n        return NGX_OK;\n    }\n\n    /* c->type == SOCK_STREAM */\n\n    if (pc == NULL\n        || (!c->read->eof && !pc->read->eof)\n        || (!c->read->eof && c->buffered)\n        || (!pc->read->eof && pc->buffered))\n    {\n        return NGX_DECLINED;\n    }\n\n    if (pscf->half_close) {\n        /* avoid closing live connections until both read ends get EOF */\n        if (!(c->read->eof && pc->read->eof && !c->buffered && !pc->buffered)) {\n             return NGX_DECLINED;\n        }\n    }\n\n    handler = c->log->handler;\n    c->log->handler = NULL;\n\n    ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                  \"%s disconnected\"\n                  \", bytes from/to client:%O/%O\"\n                  \", bytes from/to upstream:%O/%O\",\n                  from_upstream ? \"upstream\" : \"client\",\n                  s->received, c->sent, u->received, pc ? pc->sent : 0);\n\n    c->log->handler = handler;\n\n    ngx_stream_proxy_finalize(s, NGX_STREAM_OK);\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_stream_proxy_next_upstream(ngx_stream_session_t *s)\n{\n    ngx_msec_t                    timeout;\n    ngx_connection_t             *pc;\n    ngx_stream_upstream_t        *u;\n    ngx_stream_proxy_srv_conf_t  *pscf;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,\n                   \"stream proxy next upstream\");\n\n    u = s->upstream;\n    pc = u->peer.connection;\n\n    if (pc && pc->buffered) {\n        ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,\n                      \"buffered data on next upstream\");\n        ngx_stream_proxy_finalize(s, NGX_STREAM_INTERNAL_SERVER_ERROR);\n        return;\n    }\n\n    if (s->connection->type == SOCK_DGRAM) {\n        u->upstream_out = NULL;\n    }\n\n    if (u->peer.sockaddr) {\n        u->peer.free(&u->peer, u->peer.data, NGX_PEER_FAILED);\n        u->peer.sockaddr = NULL;\n    }\n\n    pscf = ngx_stream_get_module_srv_conf(s, ngx_stream_proxy_module);\n\n    timeout = pscf->next_upstream_timeout;\n\n    if (u->peer.tries == 0\n        || !pscf->next_upstream\n        || (timeout && ngx_current_msec - u->peer.start_time >= timeout))\n    {\n        ngx_stream_proxy_finalize(s, NGX_STREAM_BAD_GATEWAY);\n        return;\n    }\n\n    if (pc) {\n        ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,\n                       \"close proxy upstream connection: %d\", pc->fd);\n\n#if (NGX_STREAM_SSL)\n        if (pc->ssl) {\n            pc->ssl->no_wait_shutdown = 1;\n            pc->ssl->no_send_shutdown = 1;\n\n            (void) ngx_ssl_shutdown(pc);\n        }\n#endif\n\n        u->state->bytes_received = u->received;\n        u->state->bytes_sent = pc->sent;\n\n        ngx_close_connection(pc);\n        u->peer.connection = NULL;\n    }\n\n    ngx_stream_proxy_connect(s);\n}\n\n\nstatic void\nngx_stream_proxy_finalize(ngx_stream_session_t *s, ngx_uint_t rc)\n{\n    ngx_uint_t              state;\n    ngx_connection_t       *pc;\n    ngx_stream_upstream_t  *u;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,\n                   \"finalize stream proxy: %i\", rc);\n\n    u = s->upstream;\n\n    if (u == NULL) {\n        goto noupstream;\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    pc = u->peer.connection;\n\n    if (u->state) {\n        if (u->state->response_time == (ngx_msec_t) -1) {\n            u->state->response_time = ngx_current_msec - u->start_time;\n        }\n\n        if (pc) {\n            u->state->bytes_received = u->received;\n            u->state->bytes_sent = pc->sent;\n        }\n    }\n\n    if (u->peer.free && u->peer.sockaddr) {\n        state = 0;\n\n        if (pc && pc->type == SOCK_DGRAM\n            && (pc->read->error || pc->write->error))\n        {\n            state = NGX_PEER_FAILED;\n        }\n\n        u->peer.free(&u->peer, u->peer.data, state);\n        u->peer.sockaddr = NULL;\n    }\n\n    if (pc) {\n        ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,\n                       \"close stream proxy upstream connection: %d\", pc->fd);\n\n#if (NGX_STREAM_SSL)\n        if (pc->ssl) {\n            pc->ssl->no_wait_shutdown = 1;\n            (void) ngx_ssl_shutdown(pc);\n        }\n#endif\n\n        ngx_close_connection(pc);\n        u->peer.connection = NULL;\n    }\n\nnoupstream:\n\n    ngx_stream_finalize_session(s, rc);\n}\n\n\nstatic u_char *\nngx_stream_proxy_log_error(ngx_log_t *log, u_char *buf, size_t len)\n{\n    u_char                 *p;\n    ngx_connection_t       *pc;\n    ngx_stream_session_t   *s;\n    ngx_stream_upstream_t  *u;\n\n    s = log->data;\n\n    u = s->upstream;\n\n    p = buf;\n\n    if (u->peer.name) {\n        p = ngx_snprintf(p, len, \", upstream: \\\"%V\\\"\", u->peer.name);\n        len -= p - buf;\n    }\n\n    pc = u->peer.connection;\n\n    p = ngx_snprintf(p, len,\n                     \", bytes from/to client:%O/%O\"\n                     \", bytes from/to upstream:%O/%O\",\n                     s->received, s->connection->sent,\n                     u->received, pc ? pc->sent : 0);\n\n    return p;\n}\n\n\nstatic void *\nngx_stream_proxy_create_srv_conf(ngx_conf_t *cf)\n{\n    ngx_stream_proxy_srv_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_proxy_srv_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc():\n     *\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     *\n     *     conf->ssl = NULL;\n     *     conf->upstream = NULL;\n     *     conf->upstream_value = NULL;\n     */\n\n    conf->connect_timeout = NGX_CONF_UNSET_MSEC;\n    conf->timeout = NGX_CONF_UNSET_MSEC;\n    conf->next_upstream_timeout = NGX_CONF_UNSET_MSEC;\n    conf->buffer_size = NGX_CONF_UNSET_SIZE;\n    conf->upload_rate = NGX_CONF_UNSET_PTR;\n    conf->download_rate = NGX_CONF_UNSET_PTR;\n    conf->requests = NGX_CONF_UNSET_UINT;\n    conf->responses = NGX_CONF_UNSET_UINT;\n    conf->next_upstream_tries = NGX_CONF_UNSET_UINT;\n    conf->next_upstream = NGX_CONF_UNSET;\n    conf->proxy_protocol = NGX_CONF_UNSET;\n    conf->local = NGX_CONF_UNSET_PTR;\n    conf->socket_keepalive = NGX_CONF_UNSET;\n    conf->half_close = NGX_CONF_UNSET;\n\n#if (NGX_STREAM_SSL)\n    conf->ssl_enable = NGX_CONF_UNSET;\n    conf->ssl_session_reuse = NGX_CONF_UNSET;\n    conf->ssl_name = NGX_CONF_UNSET_PTR;\n    conf->ssl_server_name = NGX_CONF_UNSET;\n    conf->ssl_verify = NGX_CONF_UNSET;\n    conf->ssl_verify_depth = NGX_CONF_UNSET_UINT;\n    conf->ssl_certificate = NGX_CONF_UNSET_PTR;\n    conf->ssl_certificate_key = NGX_CONF_UNSET_PTR;\n    conf->ssl_passwords = NGX_CONF_UNSET_PTR;\n    conf->ssl_conf_commands = NGX_CONF_UNSET_PTR;\n\n#if (T_NGX_SSL_NTLS)\n    conf->tls_method = NULL;\n    conf->enable_ntls = NGX_CONF_UNSET;\n#endif\n#endif\n\n    return conf;\n}\n\n\nstatic char *\nngx_stream_proxy_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_stream_proxy_srv_conf_t *prev = parent;\n    ngx_stream_proxy_srv_conf_t *conf = child;\n\n    ngx_conf_merge_msec_value(conf->connect_timeout,\n                              prev->connect_timeout, 60000);\n\n    ngx_conf_merge_msec_value(conf->timeout,\n                              prev->timeout, 10 * 60000);\n\n    ngx_conf_merge_msec_value(conf->next_upstream_timeout,\n                              prev->next_upstream_timeout, 0);\n\n    ngx_conf_merge_size_value(conf->buffer_size,\n                              prev->buffer_size, 16384);\n\n    ngx_conf_merge_ptr_value(conf->upload_rate, prev->upload_rate, NULL);\n\n    ngx_conf_merge_ptr_value(conf->download_rate, prev->download_rate, NULL);\n\n    ngx_conf_merge_uint_value(conf->requests,\n                              prev->requests, 0);\n\n    ngx_conf_merge_uint_value(conf->responses,\n                              prev->responses, NGX_MAX_INT32_VALUE);\n\n    ngx_conf_merge_uint_value(conf->next_upstream_tries,\n                              prev->next_upstream_tries, 0);\n\n    ngx_conf_merge_value(conf->next_upstream, prev->next_upstream, 1);\n\n    ngx_conf_merge_value(conf->proxy_protocol, prev->proxy_protocol, 0);\n\n    ngx_conf_merge_ptr_value(conf->local, prev->local, NULL);\n\n    ngx_conf_merge_value(conf->socket_keepalive,\n                              prev->socket_keepalive, 0);\n\n    ngx_conf_merge_value(conf->half_close, prev->half_close, 0);\n\n#if (NGX_STREAM_SSL)\n\n    if (ngx_stream_proxy_merge_ssl(cf, conf, prev) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_conf_merge_value(conf->ssl_enable, prev->ssl_enable, 0);\n\n    ngx_conf_merge_value(conf->ssl_session_reuse,\n                              prev->ssl_session_reuse, 1);\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|NGX_SSL_TLSv1_3));\n\n    ngx_conf_merge_str_value(conf->ssl_ciphers, prev->ssl_ciphers, \"DEFAULT\");\n\n    ngx_conf_merge_ptr_value(conf->ssl_name, prev->ssl_name, NULL);\n\n    ngx_conf_merge_value(conf->ssl_server_name, prev->ssl_server_name, 0);\n\n    ngx_conf_merge_value(conf->ssl_verify, prev->ssl_verify, 0);\n\n    ngx_conf_merge_uint_value(conf->ssl_verify_depth,\n                              prev->ssl_verify_depth, 1);\n\n    ngx_conf_merge_str_value(conf->ssl_trusted_certificate,\n                              prev->ssl_trusted_certificate, \"\");\n\n    ngx_conf_merge_str_value(conf->ssl_crl, prev->ssl_crl, \"\");\n\n    ngx_conf_merge_ptr_value(conf->ssl_certificate,\n                              prev->ssl_certificate, NULL);\n\n    ngx_conf_merge_ptr_value(conf->ssl_certificate_key,\n                              prev->ssl_certificate_key, NULL);\n\n    ngx_conf_merge_ptr_value(conf->ssl_passwords, prev->ssl_passwords, NULL);\n\n    ngx_conf_merge_ptr_value(conf->ssl_conf_commands,\n                              prev->ssl_conf_commands, NULL);\n\n    if (conf->ssl_enable && ngx_stream_proxy_set_ssl(cf, conf) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n#if (T_NGX_SSL_NTLS)\n    ngx_conf_merge_value(conf->enable_ntls, prev->enable_ntls, 0);\n    ngx_conf_merge_str_value(conf->enc_certificate,\n                             prev->enc_certificate, \"\");\n    ngx_conf_merge_str_value(conf->enc_certificate_key,\n                             prev->enc_certificate_key, \"\");\n    ngx_conf_merge_str_value(conf->sign_certificate,\n                             prev->sign_certificate, \"\");\n    ngx_conf_merge_str_value(conf->sign_certificate_key,\n                             prev->sign_certificate_key, \"\");\n#endif\n#endif\n\n    return NGX_CONF_OK;\n}\n\n\n#if (NGX_STREAM_SSL)\n\nstatic ngx_int_t\nngx_stream_proxy_merge_ssl(ngx_conf_t *cf, ngx_stream_proxy_srv_conf_t *conf,\n    ngx_stream_proxy_srv_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_certificate == NGX_CONF_UNSET_PTR\n        && conf->ssl_certificate_key == NGX_CONF_UNSET_PTR\n        && conf->ssl_passwords == NGX_CONF_UNSET_PTR\n        && conf->ssl_verify == NGX_CONF_UNSET\n        && conf->ssl_verify_depth == NGX_CONF_UNSET_UINT\n        && conf->ssl_trusted_certificate.data == NULL\n        && conf->ssl_crl.data == NULL\n        && conf->ssl_session_reuse == NGX_CONF_UNSET\n        && conf->ssl_conf_commands == NGX_CONF_UNSET_PTR)\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\n     * in the \"stream\" section 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_stream_proxy_set_ssl(ngx_conf_t *cf, ngx_stream_proxy_srv_conf_t *pscf)\n{\n    ngx_pool_cleanup_t  *cln;\n\n    if (pscf->ssl->ctx) {\n        return NGX_OK;\n    }\n\n    if (ngx_ssl_create(pscf->ssl, pscf->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(pscf->ssl);\n        return NGX_ERROR;\n    }\n\n    cln->handler = ngx_ssl_cleanup_ctx;\n    cln->data = pscf->ssl;\n\n    if (ngx_ssl_ciphers(cf, pscf->ssl, &pscf->ssl_ciphers, 0) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    if (pscf->ssl_certificate\n        && pscf->ssl_certificate->value.len)\n    {\n        if (pscf->ssl_certificate_key == NULL) {\n            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                          \"no \\\"proxy_ssl_certificate_key\\\" is defined \"\n                          \"for certificate \\\"%V\\\"\",\n                          &pscf->ssl_certificate->value);\n            return NGX_ERROR;\n        }\n\n        if (pscf->ssl_certificate->lengths\n            || pscf->ssl_certificate_key->lengths)\n        {\n            pscf->ssl_passwords =\n                           ngx_ssl_preserve_passwords(cf, pscf->ssl_passwords);\n            if (pscf->ssl_passwords == NULL) {\n                return NGX_ERROR;\n            }\n\n        } else {\n\n#if (T_NGX_SSL_NTLS)\n            if (ngx_ssl_certificate(cf, pscf->ssl,\n                                    &pscf->ssl_certificate->value,\n                                    &pscf->ssl_certificate_key->value,\n                                    pscf->ssl_passwords,\n                                    SSL_NORMAL_CERT)\n#else\n            if (ngx_ssl_certificate(cf, pscf->ssl,\n                                    &pscf->ssl_certificate->value,\n                                    &pscf->ssl_certificate_key->value,\n                                    pscf->ssl_passwords)\n#endif\n                != NGX_OK)\n            {\n                return NGX_ERROR;\n            }\n        }\n    }\n\n#if (T_NGX_SSL_NTLS)\n    pscf->tls_method = SSL_CTX_get_ssl_method(pscf->ssl->ctx);\n    if (pscf->enc_certificate.len) {\n\n        if (pscf->enc_certificate_key.len == 0) {\n            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                          \"no \\\"proxy_ssl_enc_certificate_key\\\" is defined \"\n                          \"for certificate \\\"%V\\\"\", &pscf->enc_certificate);\n            return NGX_ERROR;\n        }\n\n        if (ngx_ssl_certificate(cf, pscf->ssl,\n                                &pscf->enc_certificate,\n                                &pscf->enc_certificate_key,\n                                pscf->ssl_passwords,\n                                SSL_ENC_CERT)\n            != NGX_OK)\n        {\n            return NGX_ERROR;\n        }\n    }\n\n    if (pscf->sign_certificate.len) {\n\n        if (pscf->sign_certificate_key.len == 0) {\n            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                          \"no \\\"proxy_ssl_sign_certificate_key\\\" is defined \"\n                          \"for certificate \\\"%V\\\"\", &pscf->sign_certificate);\n            return NGX_ERROR;\n        }\n\n        if (ngx_ssl_certificate(cf, pscf->ssl,\n                                &pscf->sign_certificate,\n                                &pscf->sign_certificate_key,\n                                pscf->ssl_passwords,\n                                SSL_SIGN_CERT)\n            != NGX_OK)\n        {\n            return NGX_ERROR;\n        }\n    }\n#endif\n\n    if (pscf->ssl_verify) {\n        if (pscf->ssl_trusted_certificate.len == 0) {\n            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                      \"no proxy_ssl_trusted_certificate for proxy_ssl_verify\");\n            return NGX_ERROR;\n        }\n\n        if (ngx_ssl_trusted_certificate(cf, pscf->ssl,\n                                        &pscf->ssl_trusted_certificate,\n                                        pscf->ssl_verify_depth)\n            != NGX_OK)\n        {\n            return NGX_ERROR;\n        }\n\n        if (ngx_ssl_crl(cf, pscf->ssl, &pscf->ssl_crl) != NGX_OK) {\n            return NGX_ERROR;\n        }\n    }\n\n    if (ngx_ssl_client_session_cache(cf, pscf->ssl, pscf->ssl_session_reuse)\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    if (ngx_ssl_conf_commands(cf, pscf->ssl, pscf->ssl_conf_commands)\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n#endif\n\n\nstatic char *\nngx_stream_proxy_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_stream_proxy_srv_conf_t *pscf = conf;\n\n    ngx_url_t                            u;\n    ngx_str_t                           *value, *url;\n    ngx_stream_complex_value_t           cv;\n    ngx_stream_core_srv_conf_t          *cscf;\n    ngx_stream_compile_complex_value_t   ccv;\n\n    if (pscf->upstream || pscf->upstream_value) {\n        return \"is duplicate\";\n    }\n\n    cscf = ngx_stream_conf_get_module_srv_conf(cf, ngx_stream_core_module);\n\n    cscf->handler = ngx_stream_proxy_handler;\n\n    value = cf->args->elts;\n\n    url = &value[1];\n\n    ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t));\n\n    ccv.cf = cf;\n    ccv.value = url;\n    ccv.complex_value = &cv;\n\n    if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (cv.lengths) {\n        pscf->upstream_value = ngx_palloc(cf->pool,\n                                          sizeof(ngx_stream_complex_value_t));\n        if (pscf->upstream_value == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        *pscf->upstream_value = cv;\n\n        return NGX_CONF_OK;\n    }\n\n    ngx_memzero(&u, sizeof(ngx_url_t));\n\n    u.url = *url;\n    u.no_resolve = 1;\n\n    pscf->upstream = ngx_stream_upstream_add(cf, &u, 0);\n    if (pscf->upstream == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic char *\nngx_stream_proxy_bind(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_stream_proxy_srv_conf_t *pscf = conf;\n\n    ngx_int_t                            rc;\n    ngx_str_t                           *value;\n    ngx_stream_complex_value_t           cv;\n    ngx_stream_upstream_local_t         *local;\n    ngx_stream_compile_complex_value_t   ccv;\n\n    if (pscf->local != NGX_CONF_UNSET_PTR) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    if (cf->args->nelts == 2 && ngx_strcmp(value[1].data, \"off\") == 0) {\n        pscf->local = NULL;\n        return NGX_CONF_OK;\n    }\n\n    ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t));\n\n    ccv.cf = cf;\n    ccv.value = &value[1];\n    ccv.complex_value = &cv;\n\n    if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    local = ngx_pcalloc(cf->pool, sizeof(ngx_stream_upstream_local_t));\n    if (local == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    pscf->local = local;\n\n    if (cv.lengths) {\n        local->value = ngx_palloc(cf->pool, sizeof(ngx_stream_complex_value_t));\n        if (local->value == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        *local->value = cv;\n\n    } else {\n        local->addr = ngx_palloc(cf->pool, sizeof(ngx_addr_t));\n        if (local->addr == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        rc = ngx_parse_addr_port(cf->pool, local->addr, value[1].data,\n                                 value[1].len);\n\n        switch (rc) {\n        case NGX_OK:\n            local->addr->name = value[1];\n            break;\n\n        case NGX_DECLINED:\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"invalid address \\\"%V\\\"\", &value[1]);\n            /* fall through */\n\n        default:\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    if (cf->args->nelts > 2) {\n        if (ngx_strcmp(value[2].data, \"transparent\") == 0) {\n#if (NGX_HAVE_TRANSPARENT_PROXY)\n            ngx_core_conf_t  *ccf;\n\n            ccf = (ngx_core_conf_t *) ngx_get_conf(cf->cycle->conf_ctx,\n                                                   ngx_core_module);\n\n            ccf->transparent = 1;\n            local->transparent = 1;\n#else\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"transparent proxying is not supported \"\n                               \"on this platform, ignored\");\n#endif\n        } else {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"invalid parameter \\\"%V\\\"\", &value[2]);\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    return NGX_CONF_OK;\n}\n"
  },
  {
    "path": "src/stream/ngx_stream_realip_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_stream.h>\n\n\ntypedef struct {\n    ngx_array_t       *from;     /* array of ngx_cidr_t */\n} ngx_stream_realip_srv_conf_t;\n\n\ntypedef struct {\n    struct sockaddr   *sockaddr;\n    socklen_t          socklen;\n    ngx_str_t          addr_text;\n} ngx_stream_realip_ctx_t;\n\n\nstatic ngx_int_t ngx_stream_realip_handler(ngx_stream_session_t *s);\nstatic ngx_int_t ngx_stream_realip_set_addr(ngx_stream_session_t *s,\n    ngx_addr_t *addr);\nstatic char *ngx_stream_realip_from(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic void *ngx_stream_realip_create_srv_conf(ngx_conf_t *cf);\nstatic char *ngx_stream_realip_merge_srv_conf(ngx_conf_t *cf, void *parent,\n    void *child);\nstatic ngx_int_t ngx_stream_realip_add_variables(ngx_conf_t *cf);\nstatic ngx_int_t ngx_stream_realip_init(ngx_conf_t *cf);\n\n\nstatic ngx_int_t ngx_stream_realip_remote_addr_variable(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_stream_realip_remote_port_variable(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data);\n\n\nstatic ngx_command_t  ngx_stream_realip_commands[] = {\n\n    { ngx_string(\"set_real_ip_from\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_stream_realip_from,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      0,\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_stream_module_t  ngx_stream_realip_module_ctx = {\n    ngx_stream_realip_add_variables,       /* preconfiguration */\n    ngx_stream_realip_init,                /* postconfiguration */\n\n    NULL,                                  /* create main configuration */\n    NULL,                                  /* init main configuration */\n\n    ngx_stream_realip_create_srv_conf,     /* create server configuration */\n    ngx_stream_realip_merge_srv_conf       /* merge server configuration */\n};\n\n\nngx_module_t  ngx_stream_realip_module = {\n    NGX_MODULE_V1,\n    &ngx_stream_realip_module_ctx,         /* module context */\n    ngx_stream_realip_commands,            /* module directives */\n    NGX_STREAM_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_stream_variable_t  ngx_stream_realip_vars[] = {\n\n    { ngx_string(\"realip_remote_addr\"), NULL,\n      ngx_stream_realip_remote_addr_variable, 0, 0, 0 },\n\n    { ngx_string(\"realip_remote_port\"), NULL,\n      ngx_stream_realip_remote_port_variable, 0, 0, 0 },\n\n      ngx_stream_null_variable\n};\n\n\nstatic ngx_int_t\nngx_stream_realip_handler(ngx_stream_session_t *s)\n{\n    ngx_addr_t                     addr;\n    ngx_connection_t              *c;\n    ngx_stream_realip_srv_conf_t  *rscf;\n\n    rscf = ngx_stream_get_module_srv_conf(s, ngx_stream_realip_module);\n\n    if (rscf->from == NULL) {\n        return NGX_DECLINED;\n    }\n\n    c = s->connection;\n\n    if (c->proxy_protocol == NULL) {\n        return NGX_DECLINED;\n    }\n\n    if (ngx_cidr_match(c->sockaddr, rscf->from) != NGX_OK) {\n        return NGX_DECLINED;\n    }\n\n    if (ngx_parse_addr(c->pool, &addr, c->proxy_protocol->src_addr.data,\n                       c->proxy_protocol->src_addr.len)\n        != NGX_OK)\n    {\n        return NGX_DECLINED;\n    }\n\n    ngx_inet_set_port(addr.sockaddr, c->proxy_protocol->src_port);\n\n    return ngx_stream_realip_set_addr(s, &addr);\n}\n\n\nstatic ngx_int_t\nngx_stream_realip_set_addr(ngx_stream_session_t *s, ngx_addr_t *addr)\n{\n    size_t                    len;\n    u_char                   *p;\n    u_char                    text[NGX_SOCKADDR_STRLEN];\n    ngx_connection_t         *c;\n    ngx_stream_realip_ctx_t  *ctx;\n\n    c = s->connection;\n\n    ctx = ngx_palloc(c->pool, sizeof(ngx_stream_realip_ctx_t));\n    if (ctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    len = ngx_sock_ntop(addr->sockaddr, addr->socklen, text,\n                        NGX_SOCKADDR_STRLEN, 0);\n    if (len == 0) {\n        return NGX_ERROR;\n    }\n\n    p = ngx_pnalloc(c->pool, len);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_memcpy(p, text, len);\n\n    ngx_stream_set_ctx(s, ctx, ngx_stream_realip_module);\n\n    ctx->sockaddr = c->sockaddr;\n    ctx->socklen = c->socklen;\n    ctx->addr_text = c->addr_text;\n\n    c->sockaddr = addr->sockaddr;\n    c->socklen = addr->socklen;\n    c->addr_text.len = len;\n    c->addr_text.data = p;\n\n    return NGX_DECLINED;\n}\n\n\nstatic char *\nngx_stream_realip_from(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_stream_realip_srv_conf_t *rscf = conf;\n\n    ngx_int_t             rc;\n    ngx_str_t            *value;\n    ngx_url_t             u;\n    ngx_cidr_t            c, *cidr;\n    ngx_uint_t            i;\n    struct sockaddr_in   *sin;\n#if (NGX_HAVE_INET6)\n    struct sockaddr_in6  *sin6;\n#endif\n\n    value = cf->args->elts;\n\n    if (rscf->from == NULL) {\n        rscf->from = ngx_array_create(cf->pool, 2,\n                                      sizeof(ngx_cidr_t));\n        if (rscf->from == NULL) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n#if (NGX_HAVE_UNIX_DOMAIN)\n\n    if (ngx_strcmp(value[1].data, \"unix:\") == 0) {\n        cidr = ngx_array_push(rscf->from);\n        if (cidr == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        cidr->family = AF_UNIX;\n        return NGX_CONF_OK;\n    }\n\n#endif\n\n    rc = ngx_ptocidr(&value[1], &c);\n\n    if (rc != NGX_ERROR) {\n        if (rc == NGX_DONE) {\n            ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                               \"low address bits of %V are meaningless\",\n                               &value[1]);\n        }\n\n        cidr = ngx_array_push(rscf->from);\n        if (cidr == NULL) {\n            return NGX_CONF_ERROR;\n        }\n\n        *cidr = c;\n\n        return NGX_CONF_OK;\n    }\n\n    ngx_memzero(&u, sizeof(ngx_url_t));\n    u.host = value[1];\n\n    if (ngx_inet_resolve_host(cf->pool, &u) != NGX_OK) {\n        if (u.err) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"%s in set_real_ip_from \\\"%V\\\"\",\n                               u.err, &u.host);\n        }\n\n        return NGX_CONF_ERROR;\n    }\n\n    cidr = ngx_array_push_n(rscf->from, u.naddrs);\n    if (cidr == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_memzero(cidr, u.naddrs * sizeof(ngx_cidr_t));\n\n    for (i = 0; i < u.naddrs; i++) {\n        cidr[i].family = u.addrs[i].sockaddr->sa_family;\n\n        switch (cidr[i].family) {\n\n#if (NGX_HAVE_INET6)\n        case AF_INET6:\n            sin6 = (struct sockaddr_in6 *) u.addrs[i].sockaddr;\n            cidr[i].u.in6.addr = sin6->sin6_addr;\n            ngx_memset(cidr[i].u.in6.mask.s6_addr, 0xff, 16);\n            break;\n#endif\n\n        default: /* AF_INET */\n            sin = (struct sockaddr_in *) u.addrs[i].sockaddr;\n            cidr[i].u.in.addr = sin->sin_addr.s_addr;\n            cidr[i].u.in.mask = 0xffffffff;\n            break;\n        }\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic void *\nngx_stream_realip_create_srv_conf(ngx_conf_t *cf)\n{\n    ngx_stream_realip_srv_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_realip_srv_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc():\n     *\n     *     conf->from = NULL;\n     */\n\n    return conf;\n}\n\n\nstatic char *\nngx_stream_realip_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_stream_realip_srv_conf_t *prev = parent;\n    ngx_stream_realip_srv_conf_t *conf = child;\n\n    if (conf->from == NULL) {\n        conf->from = prev->from;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_realip_add_variables(ngx_conf_t *cf)\n{\n    ngx_stream_variable_t  *var, *v;\n\n    for (v = ngx_stream_realip_vars; v->name.len; v++) {\n        var = ngx_stream_add_variable(cf, &v->name, v->flags);\n        if (var == NULL) {\n            return NGX_ERROR;\n        }\n\n        var->get_handler = v->get_handler;\n        var->data = v->data;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_realip_init(ngx_conf_t *cf)\n{\n    ngx_stream_handler_pt        *h;\n    ngx_stream_core_main_conf_t  *cmcf;\n\n    cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);\n\n    h = ngx_array_push(&cmcf->phases[NGX_STREAM_POST_ACCEPT_PHASE].handlers);\n    if (h == NULL) {\n        return NGX_ERROR;\n    }\n\n    *h = ngx_stream_realip_handler;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_realip_remote_addr_variable(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data)\n{\n    ngx_str_t                *addr_text;\n    ngx_stream_realip_ctx_t  *ctx;\n\n    ctx = ngx_stream_get_module_ctx(s, ngx_stream_realip_module);\n\n    addr_text = ctx ? &ctx->addr_text : &s->connection->addr_text;\n\n    v->len = addr_text->len;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = addr_text->data;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_realip_remote_port_variable(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data)\n{\n    ngx_uint_t                port;\n    struct sockaddr          *sa;\n    ngx_stream_realip_ctx_t  *ctx;\n\n    ctx = ngx_stream_get_module_ctx(s, ngx_stream_realip_module);\n\n    sa = ctx ? ctx->sockaddr : s->connection->sockaddr;\n\n    v->len = 0;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    v->data = ngx_pnalloc(s->connection->pool, sizeof(\"65535\") - 1);\n    if (v->data == NULL) {\n        return NGX_ERROR;\n    }\n\n    port = ngx_inet_get_port(sa);\n\n    if (port > 0 && port < 65536) {\n        v->len = ngx_sprintf(v->data, \"%ui\", port) - v->data;\n    }\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "src/stream/ngx_stream_return_module.c",
    "content": "\n/*\n * Copyright (C) Roman Arutyunyan\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_stream.h>\n\n\ntypedef struct {\n    ngx_stream_complex_value_t   text;\n} ngx_stream_return_srv_conf_t;\n\n\ntypedef struct {\n    ngx_chain_t                 *out;\n} ngx_stream_return_ctx_t;\n\n\nstatic void ngx_stream_return_handler(ngx_stream_session_t *s);\nstatic void ngx_stream_return_write_handler(ngx_event_t *ev);\n\nstatic void *ngx_stream_return_create_srv_conf(ngx_conf_t *cf);\nstatic char *ngx_stream_return(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\n\n\nstatic ngx_command_t  ngx_stream_return_commands[] = {\n\n    { ngx_string(\"return\"),\n      NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_stream_return,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      0,\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_stream_module_t  ngx_stream_return_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    NULL,                                  /* postconfiguration */\n\n    NULL,                                  /* create main configuration */\n    NULL,                                  /* init main configuration */\n\n    ngx_stream_return_create_srv_conf,     /* create server configuration */\n    NULL                                   /* merge server configuration */\n};\n\n\nngx_module_t  ngx_stream_return_module = {\n    NGX_MODULE_V1,\n    &ngx_stream_return_module_ctx,         /* module context */\n    ngx_stream_return_commands,            /* module directives */\n    NGX_STREAM_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 void\nngx_stream_return_handler(ngx_stream_session_t *s)\n{\n    ngx_str_t                      text;\n    ngx_buf_t                     *b;\n    ngx_connection_t              *c;\n    ngx_stream_return_ctx_t       *ctx;\n    ngx_stream_return_srv_conf_t  *rscf;\n\n    c = s->connection;\n\n    c->log->action = \"returning text\";\n\n    rscf = ngx_stream_get_module_srv_conf(s, ngx_stream_return_module);\n\n    if (ngx_stream_complex_value(s, &rscf->text, &text) != NGX_OK) {\n        ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR);\n        return;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0,\n                   \"stream return text: \\\"%V\\\"\", &text);\n\n    if (text.len == 0) {\n        ngx_stream_finalize_session(s, NGX_STREAM_OK);\n        return;\n    }\n\n    ctx = ngx_pcalloc(c->pool, sizeof(ngx_stream_return_ctx_t));\n    if (ctx == NULL) {\n        ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR);\n        return;\n    }\n\n    ngx_stream_set_ctx(s, ctx, ngx_stream_return_module);\n\n    b = ngx_calloc_buf(c->pool);\n    if (b == NULL) {\n        ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR);\n        return;\n    }\n\n    b->memory = 1;\n    b->pos = text.data;\n    b->last = text.data + text.len;\n    b->last_buf = 1;\n\n    ctx->out = ngx_alloc_chain_link(c->pool);\n    if (ctx->out == NULL) {\n        ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR);\n        return;\n    }\n\n    ctx->out->buf = b;\n    ctx->out->next = NULL;\n\n    c->write->handler = ngx_stream_return_write_handler;\n\n    ngx_stream_return_write_handler(c->write);\n}\n\n\nstatic void\nngx_stream_return_write_handler(ngx_event_t *ev)\n{\n    ngx_connection_t         *c;\n    ngx_stream_session_t     *s;\n    ngx_stream_return_ctx_t  *ctx;\n\n    c = ev->data;\n    s = c->data;\n\n    if (ev->timedout) {\n        ngx_connection_error(c, NGX_ETIMEDOUT, \"connection timed out\");\n        ngx_stream_finalize_session(s, NGX_STREAM_OK);\n        return;\n    }\n\n    ctx = ngx_stream_get_module_ctx(s, ngx_stream_return_module);\n\n    if (ngx_stream_top_filter(s, ctx->out, 1) == NGX_ERROR) {\n        ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR);\n        return;\n    }\n\n    ctx->out = NULL;\n\n    if (!c->buffered) {\n        ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0,\n                       \"stream return done sending\");\n        ngx_stream_finalize_session(s, NGX_STREAM_OK);\n        return;\n    }\n\n    if (ngx_handle_write_event(ev, 0) != NGX_OK) {\n        ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR);\n        return;\n    }\n\n    ngx_add_timer(ev, 5000);\n}\n\n\nstatic void *\nngx_stream_return_create_srv_conf(ngx_conf_t *cf)\n{\n    ngx_stream_return_srv_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_return_srv_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    return conf;\n}\n\n\nstatic char *\nngx_stream_return(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_stream_return_srv_conf_t *rscf = conf;\n\n    ngx_str_t                           *value;\n    ngx_stream_core_srv_conf_t          *cscf;\n    ngx_stream_compile_complex_value_t   ccv;\n\n    if (rscf->text.value.data) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t));\n\n    ccv.cf = cf;\n    ccv.value = &value[1];\n    ccv.complex_value = &rscf->text;\n\n    if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    cscf = ngx_stream_conf_get_module_srv_conf(cf, ngx_stream_core_module);\n\n    cscf->handler = ngx_stream_return_handler;\n\n    return NGX_CONF_OK;\n}\n"
  },
  {
    "path": "src/stream/ngx_stream_script.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_stream.h>\n\n\nstatic ngx_int_t ngx_stream_script_init_arrays(\n    ngx_stream_script_compile_t *sc);\nstatic ngx_int_t ngx_stream_script_done(ngx_stream_script_compile_t *sc);\nstatic ngx_int_t ngx_stream_script_add_copy_code(\n    ngx_stream_script_compile_t *sc, ngx_str_t *value, ngx_uint_t last);\nstatic ngx_int_t ngx_stream_script_add_var_code(\n    ngx_stream_script_compile_t *sc, ngx_str_t *name);\n#if (NGX_PCRE)\nstatic ngx_int_t ngx_stream_script_add_capture_code(\n    ngx_stream_script_compile_t *sc, ngx_uint_t n);\n#endif\nstatic ngx_int_t ngx_stream_script_add_full_name_code(\n    ngx_stream_script_compile_t *sc);\nstatic size_t ngx_stream_script_full_name_len_code(\n    ngx_stream_script_engine_t *e);\nstatic void ngx_stream_script_full_name_code(ngx_stream_script_engine_t *e);\n\n\n#define ngx_stream_script_exit  (u_char *) &ngx_stream_script_exit_code\n\nstatic uintptr_t ngx_stream_script_exit_code = (uintptr_t) NULL;\n\n\nvoid\nngx_stream_script_flush_complex_value(ngx_stream_session_t *s,\n    ngx_stream_complex_value_t *val)\n{\n    ngx_uint_t *index;\n\n    index = val->flushes;\n\n    if (index) {\n        while (*index != (ngx_uint_t) -1) {\n\n            if (s->variables[*index].no_cacheable) {\n                s->variables[*index].valid = 0;\n                s->variables[*index].not_found = 0;\n            }\n\n            index++;\n        }\n    }\n}\n\n\nngx_int_t\nngx_stream_complex_value(ngx_stream_session_t *s,\n    ngx_stream_complex_value_t *val, ngx_str_t *value)\n{\n    size_t                         len;\n    ngx_stream_script_code_pt      code;\n    ngx_stream_script_engine_t     e;\n    ngx_stream_script_len_code_pt  lcode;\n\n    if (val->lengths == NULL) {\n        *value = val->value;\n        return NGX_OK;\n    }\n\n    ngx_stream_script_flush_complex_value(s, val);\n\n    ngx_memzero(&e, sizeof(ngx_stream_script_engine_t));\n\n    e.ip = val->lengths;\n    e.session = s;\n    e.flushed = 1;\n\n    len = 0;\n\n    while (*(uintptr_t *) e.ip) {\n        lcode = *(ngx_stream_script_len_code_pt *) e.ip;\n        len += lcode(&e);\n    }\n\n    value->len = len;\n    value->data = ngx_pnalloc(s->connection->pool, len);\n    if (value->data == NULL) {\n        return NGX_ERROR;\n    }\n\n    e.ip = val->values;\n    e.pos = value->data;\n    e.buf = *value;\n\n    while (*(uintptr_t *) e.ip) {\n        code = *(ngx_stream_script_code_pt *) e.ip;\n        code((ngx_stream_script_engine_t *) &e);\n    }\n\n    *value = e.buf;\n\n    return NGX_OK;\n}\n\n\nsize_t\nngx_stream_complex_value_size(ngx_stream_session_t *s,\n    ngx_stream_complex_value_t *val, size_t default_value)\n{\n    size_t     size;\n    ngx_str_t  value;\n\n    if (val == NULL) {\n        return default_value;\n    }\n\n    if (val->lengths == NULL) {\n        return val->u.size;\n    }\n\n    if (ngx_stream_complex_value(s, val, &value) != NGX_OK) {\n        return default_value;\n    }\n\n    size = ngx_parse_size(&value);\n\n    if (size == (size_t) NGX_ERROR) {\n        ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,\n                      \"invalid size \\\"%V\\\"\", &value);\n        return default_value;\n    }\n\n    return size;\n}\n\n\nngx_int_t\nngx_stream_compile_complex_value(ngx_stream_compile_complex_value_t *ccv)\n{\n    ngx_str_t                    *v;\n    ngx_uint_t                    i, n, nv, nc;\n    ngx_array_t                   flushes, lengths, values, *pf, *pl, *pv;\n    ngx_stream_script_compile_t   sc;\n\n    v = ccv->value;\n\n    nv = 0;\n    nc = 0;\n\n    for (i = 0; i < v->len; i++) {\n        if (v->data[i] == '$') {\n            if (v->data[i + 1] >= '1' && v->data[i + 1] <= '9') {\n                nc++;\n\n            } else {\n                nv++;\n            }\n        }\n    }\n\n    if ((v->len == 0 || v->data[0] != '$')\n        && (ccv->conf_prefix || ccv->root_prefix))\n    {\n        if (ngx_conf_full_name(ccv->cf->cycle, v, ccv->conf_prefix) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        ccv->conf_prefix = 0;\n        ccv->root_prefix = 0;\n    }\n\n    ccv->complex_value->value = *v;\n    ccv->complex_value->flushes = NULL;\n    ccv->complex_value->lengths = NULL;\n    ccv->complex_value->values = NULL;\n\n    if (nv == 0 && nc == 0) {\n        return NGX_OK;\n    }\n\n    n = nv + 1;\n\n    if (ngx_array_init(&flushes, ccv->cf->pool, n, sizeof(ngx_uint_t))\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    n = nv * (2 * sizeof(ngx_stream_script_copy_code_t)\n                  + sizeof(ngx_stream_script_var_code_t))\n        + sizeof(uintptr_t);\n\n    if (ngx_array_init(&lengths, ccv->cf->pool, n, 1) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    n = (nv * (2 * sizeof(ngx_stream_script_copy_code_t)\n                   + sizeof(ngx_stream_script_var_code_t))\n                + sizeof(uintptr_t)\n                + v->len\n                + sizeof(uintptr_t) - 1)\n            & ~(sizeof(uintptr_t) - 1);\n\n    if (ngx_array_init(&values, ccv->cf->pool, n, 1) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    pf = &flushes;\n    pl = &lengths;\n    pv = &values;\n\n    ngx_memzero(&sc, sizeof(ngx_stream_script_compile_t));\n\n    sc.cf = ccv->cf;\n    sc.source = v;\n    sc.flushes = &pf;\n    sc.lengths = &pl;\n    sc.values = &pv;\n    sc.complete_lengths = 1;\n    sc.complete_values = 1;\n    sc.zero = ccv->zero;\n    sc.conf_prefix = ccv->conf_prefix;\n    sc.root_prefix = ccv->root_prefix;\n\n    if (ngx_stream_script_compile(&sc) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    if (flushes.nelts) {\n        ccv->complex_value->flushes = flushes.elts;\n        ccv->complex_value->flushes[flushes.nelts] = (ngx_uint_t) -1;\n    }\n\n    ccv->complex_value->lengths = lengths.elts;\n    ccv->complex_value->values = values.elts;\n\n    return NGX_OK;\n}\n\n\nchar *\nngx_stream_set_complex_value_slot(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n    char  *p = conf;\n\n    ngx_str_t                            *value;\n    ngx_stream_complex_value_t          **cv;\n    ngx_stream_compile_complex_value_t    ccv;\n\n    cv = (ngx_stream_complex_value_t **) (p + cmd->offset);\n\n    if (*cv != NGX_CONF_UNSET_PTR && *cv != NULL) {\n        return \"is duplicate\";\n    }\n\n    *cv = ngx_palloc(cf->pool, sizeof(ngx_stream_complex_value_t));\n    if (*cv == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    value = cf->args->elts;\n\n    ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t));\n\n    ccv.cf = cf;\n    ccv.value = &value[1];\n    ccv.complex_value = *cv;\n\n    if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nchar *\nngx_stream_set_complex_value_zero_slot(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n    char  *p = conf;\n\n    ngx_str_t                            *value;\n    ngx_stream_complex_value_t          **cv;\n    ngx_stream_compile_complex_value_t    ccv;\n\n    cv = (ngx_stream_complex_value_t **) (p + cmd->offset);\n\n    if (*cv != NGX_CONF_UNSET_PTR) {\n        return \"is duplicate\";\n    }\n\n    *cv = ngx_palloc(cf->pool, sizeof(ngx_stream_complex_value_t));\n    if (*cv == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    value = cf->args->elts;\n\n    ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t));\n\n    ccv.cf = cf;\n    ccv.value = &value[1];\n    ccv.complex_value = *cv;\n    ccv.zero = 1;\n\n    if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nchar *\nngx_stream_set_complex_value_size_slot(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf)\n{\n    char  *p = conf;\n\n    char                        *rv;\n    ngx_stream_complex_value_t  *cv;\n\n    rv = ngx_stream_set_complex_value_slot(cf, cmd, conf);\n\n    if (rv != NGX_CONF_OK) {\n        return rv;\n    }\n\n    cv = *(ngx_stream_complex_value_t **) (p + cmd->offset);\n\n    if (cv->lengths) {\n        return NGX_CONF_OK;\n    }\n\n    cv->u.size = ngx_parse_size(&cv->value);\n    if (cv->u.size == (size_t) NGX_ERROR) {\n        return \"invalid value\";\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nngx_uint_t\nngx_stream_script_variables_count(ngx_str_t *value)\n{\n    ngx_uint_t  i, n;\n\n    for (n = 0, i = 0; i < value->len; i++) {\n        if (value->data[i] == '$') {\n            n++;\n        }\n    }\n\n    return n;\n}\n\n\nngx_int_t\nngx_stream_script_compile(ngx_stream_script_compile_t *sc)\n{\n    u_char       ch;\n    ngx_str_t    name;\n    ngx_uint_t   i, bracket;\n\n    if (ngx_stream_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] >= '1' && sc->source->data[i] <= '9') {\n#if (NGX_PCRE)\n                ngx_uint_t  n;\n\n                n = sc->source->data[i] - '0';\n\n                if (ngx_stream_script_add_capture_code(sc, n) != NGX_OK) {\n                    return NGX_ERROR;\n                }\n\n                i++;\n\n                continue;\n#else\n                ngx_conf_log_error(NGX_LOG_EMERG, sc->cf, 0,\n                                   \"using variable \\\"$%c\\\" requires \"\n                                   \"PCRE library\", sc->source->data[i]);\n                return NGX_ERROR;\n#endif\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                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 ((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_conf_log_error(NGX_LOG_EMERG, sc->cf, 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            sc->variables++;\n\n            if (ngx_stream_script_add_var_code(sc, &name) != 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        sc->size += name.len;\n\n        if (ngx_stream_script_add_copy_code(sc, &name, (i == sc->source->len))\n            != NGX_OK)\n        {\n            return NGX_ERROR;\n        }\n    }\n\n    return ngx_stream_script_done(sc);\n\ninvalid_variable:\n\n    ngx_conf_log_error(NGX_LOG_EMERG, sc->cf, 0, \"invalid variable name\");\n\n    return NGX_ERROR;\n}\n\n\nu_char *\nngx_stream_script_run(ngx_stream_session_t *s, ngx_str_t *value,\n    void *code_lengths, size_t len, void *code_values)\n{\n    ngx_uint_t                      i;\n    ngx_stream_script_code_pt       code;\n    ngx_stream_script_engine_t      e;\n    ngx_stream_core_main_conf_t    *cmcf;\n    ngx_stream_script_len_code_pt   lcode;\n\n    cmcf = ngx_stream_get_module_main_conf(s, ngx_stream_core_module);\n\n    for (i = 0; i < cmcf->variables.nelts; i++) {\n        if (s->variables[i].no_cacheable) {\n            s->variables[i].valid = 0;\n            s->variables[i].not_found = 0;\n        }\n    }\n\n    ngx_memzero(&e, sizeof(ngx_stream_script_engine_t));\n\n    e.ip = code_lengths;\n    e.session = s;\n    e.flushed = 1;\n\n    while (*(uintptr_t *) e.ip) {\n        lcode = *(ngx_stream_script_len_code_pt *) e.ip;\n        len += lcode(&e);\n    }\n\n\n    value->len = len;\n    value->data = ngx_pnalloc(s->connection->pool, len);\n    if (value->data == NULL) {\n        return NULL;\n    }\n\n    e.ip = code_values;\n    e.pos = value->data;\n\n    while (*(uintptr_t *) e.ip) {\n        code = *(ngx_stream_script_code_pt *) e.ip;\n        code((ngx_stream_script_engine_t *) &e);\n    }\n\n    return e.pos;\n}\n\n\nvoid\nngx_stream_script_flush_no_cacheable_variables(ngx_stream_session_t *s,\n    ngx_array_t *indices)\n{\n    ngx_uint_t  n, *index;\n\n    if (indices) {\n        index = indices->elts;\n        for (n = 0; n < indices->nelts; n++) {\n            if (s->variables[index[n]].no_cacheable) {\n                s->variables[index[n]].valid = 0;\n                s->variables[index[n]].not_found = 0;\n            }\n        }\n    }\n}\n\n\nstatic ngx_int_t\nngx_stream_script_init_arrays(ngx_stream_script_compile_t *sc)\n{\n    ngx_uint_t   n;\n\n    if (sc->flushes && *sc->flushes == NULL) {\n        n = sc->variables ? sc->variables : 1;\n        *sc->flushes = ngx_array_create(sc->cf->pool, n, sizeof(ngx_uint_t));\n        if (*sc->flushes == NULL) {\n            return NGX_ERROR;\n        }\n    }\n\n    if (*sc->lengths == NULL) {\n        n = sc->variables * (2 * sizeof(ngx_stream_script_copy_code_t)\n                             + sizeof(ngx_stream_script_var_code_t))\n            + sizeof(uintptr_t);\n\n        *sc->lengths = ngx_array_create(sc->cf->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_stream_script_copy_code_t)\n                              + sizeof(ngx_stream_script_var_code_t))\n                + sizeof(uintptr_t)\n                + sc->source->len\n                + sizeof(uintptr_t) - 1)\n            & ~(sizeof(uintptr_t) - 1);\n\n        *sc->values = ngx_array_create(sc->cf->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_stream_script_done(ngx_stream_script_compile_t *sc)\n{\n    ngx_str_t    zero;\n    uintptr_t   *code;\n\n    if (sc->zero) {\n\n        zero.len = 1;\n        zero.data = (u_char *) \"\\0\";\n\n        if (ngx_stream_script_add_copy_code(sc, &zero, 0) != NGX_OK) {\n            return NGX_ERROR;\n        }\n    }\n\n    if (sc->conf_prefix || sc->root_prefix) {\n        if (ngx_stream_script_add_full_name_code(sc) != NGX_OK) {\n            return NGX_ERROR;\n        }\n    }\n\n    if (sc->complete_lengths) {\n        code = ngx_stream_script_add_code(*sc->lengths, sizeof(uintptr_t),\n                                          NULL);\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_stream_script_add_code(*sc->values, sizeof(uintptr_t),\n                                          &sc->main);\n        if (code == NULL) {\n            return NGX_ERROR;\n        }\n\n        *code = (uintptr_t) NULL;\n    }\n\n    return NGX_OK;\n}\n\n\nvoid *\nngx_stream_script_add_code(ngx_array_t *codes, size_t size, void *code)\n{\n    u_char  *elts, **p;\n    void    *new;\n\n    elts = codes->elts;\n\n    new = ngx_array_push_n(codes, size);\n    if (new == NULL) {\n        return NULL;\n    }\n\n    if (code) {\n        if (elts != codes->elts) {\n            p = code;\n            *p += (u_char *) codes->elts - elts;\n        }\n    }\n\n    return new;\n}\n\n\nstatic ngx_int_t\nngx_stream_script_add_copy_code(ngx_stream_script_compile_t *sc,\n    ngx_str_t *value, ngx_uint_t last)\n{\n    u_char                         *p;\n    size_t                          size, len, zero;\n    ngx_stream_script_copy_code_t  *code;\n\n    zero = (sc->zero && last);\n    len = value->len + zero;\n\n    code = ngx_stream_script_add_code(*sc->lengths,\n                                      sizeof(ngx_stream_script_copy_code_t),\n                                      NULL);\n    if (code == NULL) {\n        return NGX_ERROR;\n    }\n\n    code->code = (ngx_stream_script_code_pt) (void *)\n                                               ngx_stream_script_copy_len_code;\n    code->len = len;\n\n    size = (sizeof(ngx_stream_script_copy_code_t) + len + sizeof(uintptr_t) - 1)\n            & ~(sizeof(uintptr_t) - 1);\n\n    code = ngx_stream_script_add_code(*sc->values, size, &sc->main);\n    if (code == NULL) {\n        return NGX_ERROR;\n    }\n\n    code->code = ngx_stream_script_copy_code;\n    code->len = len;\n\n    p = ngx_cpymem((u_char *) code + sizeof(ngx_stream_script_copy_code_t),\n                   value->data, value->len);\n\n    if (zero) {\n        *p = '\\0';\n        sc->zero = 0;\n    }\n\n    return NGX_OK;\n}\n\n\nsize_t\nngx_stream_script_copy_len_code(ngx_stream_script_engine_t *e)\n{\n    ngx_stream_script_copy_code_t  *code;\n\n    code = (ngx_stream_script_copy_code_t *) e->ip;\n\n    e->ip += sizeof(ngx_stream_script_copy_code_t);\n\n    return code->len;\n}\n\n\nvoid\nngx_stream_script_copy_code(ngx_stream_script_engine_t *e)\n{\n    u_char                         *p;\n    ngx_stream_script_copy_code_t  *code;\n\n    code = (ngx_stream_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_stream_script_copy_code_t),\n                          code->len);\n    }\n\n    e->ip += sizeof(ngx_stream_script_copy_code_t)\n          + ((code->len + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1));\n\n    ngx_log_debug2(NGX_LOG_DEBUG_STREAM, e->session->connection->log, 0,\n                   \"stream script copy: \\\"%*s\\\"\", e->pos - p, p);\n}\n\n\nstatic ngx_int_t\nngx_stream_script_add_var_code(ngx_stream_script_compile_t *sc, ngx_str_t *name)\n{\n    ngx_int_t                      index, *p;\n    ngx_stream_script_var_code_t  *code;\n\n    index = ngx_stream_get_variable_index(sc->cf, name);\n\n    if (index == NGX_ERROR) {\n        return NGX_ERROR;\n    }\n\n    if (sc->flushes) {\n        p = ngx_array_push(*sc->flushes);\n        if (p == NULL) {\n            return NGX_ERROR;\n        }\n\n        *p = index;\n    }\n\n    code = ngx_stream_script_add_code(*sc->lengths,\n                                      sizeof(ngx_stream_script_var_code_t),\n                                      NULL);\n    if (code == NULL) {\n        return NGX_ERROR;\n    }\n\n    code->code = (ngx_stream_script_code_pt) (void *)\n                                           ngx_stream_script_copy_var_len_code;\n    code->index = (uintptr_t) index;\n\n    code = ngx_stream_script_add_code(*sc->values,\n                                      sizeof(ngx_stream_script_var_code_t),\n                                      &sc->main);\n    if (code == NULL) {\n        return NGX_ERROR;\n    }\n\n    code->code = ngx_stream_script_copy_var_code;\n    code->index = (uintptr_t) index;\n\n    return NGX_OK;\n}\n\n\nsize_t\nngx_stream_script_copy_var_len_code(ngx_stream_script_engine_t *e)\n{\n    ngx_stream_variable_value_t   *value;\n    ngx_stream_script_var_code_t  *code;\n\n    code = (ngx_stream_script_var_code_t *) e->ip;\n\n    e->ip += sizeof(ngx_stream_script_var_code_t);\n\n    if (e->flushed) {\n        value = ngx_stream_get_indexed_variable(e->session, code->index);\n\n    } else {\n        value = ngx_stream_get_flushed_variable(e->session, code->index);\n    }\n\n    if (value && !value->not_found) {\n        return value->len;\n    }\n\n    return 0;\n}\n\n\nvoid\nngx_stream_script_copy_var_code(ngx_stream_script_engine_t *e)\n{\n    u_char                        *p;\n    ngx_stream_variable_value_t   *value;\n    ngx_stream_script_var_code_t  *code;\n\n    code = (ngx_stream_script_var_code_t *) e->ip;\n\n    e->ip += sizeof(ngx_stream_script_var_code_t);\n\n    if (!e->skip) {\n\n        if (e->flushed) {\n            value = ngx_stream_get_indexed_variable(e->session, code->index);\n\n        } else {\n            value = ngx_stream_get_flushed_variable(e->session, code->index);\n        }\n\n        if (value && !value->not_found) {\n            p = e->pos;\n            e->pos = ngx_copy(p, value->data, value->len);\n\n            ngx_log_debug2(NGX_LOG_DEBUG_STREAM,\n                           e->session->connection->log, 0,\n                           \"stream script var: \\\"%*s\\\"\", e->pos - p, p);\n        }\n    }\n}\n\n\n#if (NGX_PCRE)\n\nstatic ngx_int_t\nngx_stream_script_add_capture_code(ngx_stream_script_compile_t *sc,\n    ngx_uint_t n)\n{\n    ngx_stream_script_copy_capture_code_t  *code;\n\n    code = ngx_stream_script_add_code(*sc->lengths,\n                                  sizeof(ngx_stream_script_copy_capture_code_t),\n                                  NULL);\n    if (code == NULL) {\n        return NGX_ERROR;\n    }\n\n    code->code = (ngx_stream_script_code_pt) (void *)\n                                       ngx_stream_script_copy_capture_len_code;\n    code->n = 2 * n;\n\n\n    code = ngx_stream_script_add_code(*sc->values,\n                                  sizeof(ngx_stream_script_copy_capture_code_t),\n                                  &sc->main);\n    if (code == NULL) {\n        return NGX_ERROR;\n    }\n\n    code->code = ngx_stream_script_copy_capture_code;\n    code->n = 2 * n;\n\n    if (sc->ncaptures < n) {\n        sc->ncaptures = n;\n    }\n\n    return NGX_OK;\n}\n\n\nsize_t\nngx_stream_script_copy_capture_len_code(ngx_stream_script_engine_t *e)\n{\n    int                                    *cap;\n    ngx_uint_t                              n;\n    ngx_stream_session_t                   *s;\n    ngx_stream_script_copy_capture_code_t  *code;\n\n    s = e->session;\n\n    code = (ngx_stream_script_copy_capture_code_t *) e->ip;\n\n    e->ip += sizeof(ngx_stream_script_copy_capture_code_t);\n\n    n = code->n;\n\n    if (n < s->ncaptures) {\n        cap = s->captures;\n        return cap[n + 1] - cap[n];\n    }\n\n    return 0;\n}\n\n\nvoid\nngx_stream_script_copy_capture_code(ngx_stream_script_engine_t *e)\n{\n    int                                    *cap;\n    u_char                                 *p, *pos;\n    ngx_uint_t                              n;\n    ngx_stream_session_t                   *s;\n    ngx_stream_script_copy_capture_code_t  *code;\n\n    s = e->session;\n\n    code = (ngx_stream_script_copy_capture_code_t *) e->ip;\n\n    e->ip += sizeof(ngx_stream_script_copy_capture_code_t);\n\n    n = code->n;\n\n    pos = e->pos;\n\n    if (n < s->ncaptures) {\n        cap = s->captures;\n        p = s->captures_data;\n        e->pos = ngx_copy(pos, &p[cap[n]], cap[n + 1] - cap[n]);\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_STREAM, e->session->connection->log, 0,\n                   \"stream script capture: \\\"%*s\\\"\", e->pos - pos, pos);\n}\n\n#endif\n\n\nstatic ngx_int_t\nngx_stream_script_add_full_name_code(ngx_stream_script_compile_t *sc)\n{\n    ngx_stream_script_full_name_code_t  *code;\n\n    code = ngx_stream_script_add_code(*sc->lengths,\n                                    sizeof(ngx_stream_script_full_name_code_t),\n                                    NULL);\n    if (code == NULL) {\n        return NGX_ERROR;\n    }\n\n    code->code = (ngx_stream_script_code_pt) (void *)\n                                          ngx_stream_script_full_name_len_code;\n    code->conf_prefix = sc->conf_prefix;\n\n    code = ngx_stream_script_add_code(*sc->values,\n                        sizeof(ngx_stream_script_full_name_code_t), &sc->main);\n    if (code == NULL) {\n        return NGX_ERROR;\n    }\n\n    code->code = ngx_stream_script_full_name_code;\n    code->conf_prefix = sc->conf_prefix;\n\n    return NGX_OK;\n}\n\n\nstatic size_t\nngx_stream_script_full_name_len_code(ngx_stream_script_engine_t *e)\n{\n    ngx_stream_script_full_name_code_t  *code;\n\n    code = (ngx_stream_script_full_name_code_t *) e->ip;\n\n    e->ip += sizeof(ngx_stream_script_full_name_code_t);\n\n    return code->conf_prefix ? ngx_cycle->conf_prefix.len:\n                               ngx_cycle->prefix.len;\n}\n\n\nstatic void\nngx_stream_script_full_name_code(ngx_stream_script_engine_t *e)\n{\n    ngx_stream_script_full_name_code_t  *code;\n\n    ngx_str_t  value, *prefix;\n\n    code = (ngx_stream_script_full_name_code_t *) e->ip;\n\n    value.data = e->buf.data;\n    value.len = e->pos - e->buf.data;\n\n    prefix = code->conf_prefix ? (ngx_str_t *) &ngx_cycle->conf_prefix:\n                                 (ngx_str_t *) &ngx_cycle->prefix;\n\n    if (ngx_get_full_name(e->session->connection->pool, prefix, &value)\n        != NGX_OK)\n    {\n        e->ip = ngx_stream_script_exit;\n        return;\n    }\n\n    e->buf = value;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_STREAM, e->session->connection->log, 0,\n                   \"stream script fullname: \\\"%V\\\"\", &value);\n\n    e->ip += sizeof(ngx_stream_script_full_name_code_t);\n}\n"
  },
  {
    "path": "src/stream/ngx_stream_script.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_STREAM_SCRIPT_H_INCLUDED_\n#define _NGX_STREAM_SCRIPT_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_stream.h>\n\n\ntypedef struct {\n    u_char                       *ip;\n    u_char                       *pos;\n    ngx_stream_variable_value_t  *sp;\n\n    ngx_str_t                     buf;\n    ngx_str_t                     line;\n\n    unsigned                      flushed:1;\n    unsigned                      skip:1;\n\n    ngx_stream_session_t         *session;\n} ngx_stream_script_engine_t;\n\n\ntypedef struct {\n    ngx_conf_t                   *cf;\n    ngx_str_t                    *source;\n\n    ngx_array_t                 **flushes;\n    ngx_array_t                 **lengths;\n    ngx_array_t                 **values;\n\n    ngx_uint_t                    variables;\n    ngx_uint_t                    ncaptures;\n    ngx_uint_t                    size;\n\n    void                         *main;\n\n    unsigned                      complete_lengths:1;\n    unsigned                      complete_values:1;\n    unsigned                      zero:1;\n    unsigned                      conf_prefix:1;\n    unsigned                      root_prefix:1;\n} ngx_stream_script_compile_t;\n\n\ntypedef struct {\n    ngx_str_t                     value;\n    ngx_uint_t                   *flushes;\n    void                         *lengths;\n    void                         *values;\n\n    union {\n        size_t                    size;\n    } u;\n} ngx_stream_complex_value_t;\n\n\ntypedef struct {\n    ngx_conf_t                   *cf;\n    ngx_str_t                    *value;\n    ngx_stream_complex_value_t   *complex_value;\n\n    unsigned                      zero:1;\n    unsigned                      conf_prefix:1;\n    unsigned                      root_prefix:1;\n} ngx_stream_compile_complex_value_t;\n\n\ntypedef void (*ngx_stream_script_code_pt) (ngx_stream_script_engine_t *e);\ntypedef size_t (*ngx_stream_script_len_code_pt) (ngx_stream_script_engine_t *e);\n\n\ntypedef struct {\n    ngx_stream_script_code_pt     code;\n    uintptr_t                     len;\n} ngx_stream_script_copy_code_t;\n\n\ntypedef struct {\n    ngx_stream_script_code_pt     code;\n    uintptr_t                     index;\n} ngx_stream_script_var_code_t;\n\n\ntypedef struct {\n    ngx_stream_script_code_pt     code;\n    uintptr_t                     n;\n} ngx_stream_script_copy_capture_code_t;\n\n\ntypedef struct {\n    ngx_stream_script_code_pt     code;\n    uintptr_t                     conf_prefix;\n} ngx_stream_script_full_name_code_t;\n\n\nvoid ngx_stream_script_flush_complex_value(ngx_stream_session_t *s,\n    ngx_stream_complex_value_t *val);\nngx_int_t ngx_stream_complex_value(ngx_stream_session_t *s,\n    ngx_stream_complex_value_t *val, ngx_str_t *value);\nsize_t ngx_stream_complex_value_size(ngx_stream_session_t *s,\n    ngx_stream_complex_value_t *val, size_t default_value);\nngx_int_t ngx_stream_compile_complex_value(\n    ngx_stream_compile_complex_value_t *ccv);\nchar *ngx_stream_set_complex_value_slot(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nchar *ngx_stream_set_complex_value_zero_slot(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nchar *ngx_stream_set_complex_value_size_slot(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n\n\nngx_uint_t ngx_stream_script_variables_count(ngx_str_t *value);\nngx_int_t ngx_stream_script_compile(ngx_stream_script_compile_t *sc);\nu_char *ngx_stream_script_run(ngx_stream_session_t *s, ngx_str_t *value,\n    void *code_lengths, size_t reserved, void *code_values);\nvoid ngx_stream_script_flush_no_cacheable_variables(ngx_stream_session_t *s,\n    ngx_array_t *indices);\n\nvoid *ngx_stream_script_add_code(ngx_array_t *codes, size_t size, void *code);\n\nsize_t ngx_stream_script_copy_len_code(ngx_stream_script_engine_t *e);\nvoid ngx_stream_script_copy_code(ngx_stream_script_engine_t *e);\nsize_t ngx_stream_script_copy_var_len_code(ngx_stream_script_engine_t *e);\nvoid ngx_stream_script_copy_var_code(ngx_stream_script_engine_t *e);\nsize_t ngx_stream_script_copy_capture_len_code(ngx_stream_script_engine_t *e);\nvoid ngx_stream_script_copy_capture_code(ngx_stream_script_engine_t *e);\n\n#endif /* _NGX_STREAM_SCRIPT_H_INCLUDED_ */\n"
  },
  {
    "path": "src/stream/ngx_stream_set_module.c",
    "content": "\n/*\n * Copyright (C) Pavel Pautov\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_stream.h>\n\n\ntypedef struct {\n    ngx_int_t                   index;\n    ngx_stream_set_variable_pt  set_handler;\n    uintptr_t                   data;\n    ngx_stream_complex_value_t  value;\n} ngx_stream_set_cmd_t;\n\n\ntypedef struct {\n    ngx_array_t                 commands;\n} ngx_stream_set_srv_conf_t;\n\n\nstatic ngx_int_t ngx_stream_set_handler(ngx_stream_session_t *s);\nstatic ngx_int_t ngx_stream_set_var(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_stream_set_init(ngx_conf_t *cf);\nstatic void *ngx_stream_set_create_srv_conf(ngx_conf_t *cf);\nstatic char *ngx_stream_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);\n\n\nstatic ngx_command_t  ngx_stream_set_commands[] = {\n\n    { ngx_string(\"set\"),\n      NGX_STREAM_SRV_CONF|NGX_CONF_TAKE2,\n      ngx_stream_set,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      0,\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_stream_module_t  ngx_stream_set_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    ngx_stream_set_init,                   /* postconfiguration */\n\n    NULL,                                  /* create main configuration */\n    NULL,                                  /* init main configuration */\n\n    ngx_stream_set_create_srv_conf,        /* create server configuration */\n    NULL                                   /* merge server configuration */\n};\n\n\nngx_module_t  ngx_stream_set_module = {\n    NGX_MODULE_V1,\n    &ngx_stream_set_module_ctx,            /* module context */\n    ngx_stream_set_commands,               /* module directives */\n    NGX_STREAM_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_stream_set_handler(ngx_stream_session_t *s)\n{\n    ngx_str_t                     str;\n    ngx_uint_t                    i;\n    ngx_stream_set_cmd_t         *cmds;\n    ngx_stream_set_srv_conf_t    *scf;\n    ngx_stream_variable_value_t   vv;\n\n    scf = ngx_stream_get_module_srv_conf(s, ngx_stream_set_module);\n    cmds = scf->commands.elts;\n    vv = ngx_stream_variable_null_value;\n\n    for (i = 0; i < scf->commands.nelts; i++) {\n        if (ngx_stream_complex_value(s, &cmds[i].value, &str) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        if (cmds[i].set_handler != NULL) {\n            vv.len = str.len;\n            vv.data = str.data;\n            cmds[i].set_handler(s, &vv, cmds[i].data);\n\n        } else {\n            s->variables[cmds[i].index].len = str.len;\n            s->variables[cmds[i].index].valid = 1;\n            s->variables[cmds[i].index].no_cacheable = 0;\n            s->variables[cmds[i].index].not_found = 0;\n            s->variables[cmds[i].index].data = str.data;\n        }\n    }\n\n    return NGX_DECLINED;\n}\n\n\nstatic ngx_int_t\nngx_stream_set_var(ngx_stream_session_t *s, ngx_stream_variable_value_t *v,\n    uintptr_t data)\n{\n    *v = ngx_stream_variable_null_value;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_set_init(ngx_conf_t *cf)\n{\n    ngx_stream_handler_pt        *h;\n    ngx_stream_core_main_conf_t  *cmcf;\n\n    cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);\n\n    h = ngx_array_push(&cmcf->phases[NGX_STREAM_PREACCESS_PHASE].handlers);\n    if (h == NULL) {\n        return NGX_ERROR;\n    }\n\n    *h = ngx_stream_set_handler;\n\n    return NGX_OK;\n}\n\n\nstatic void *\nngx_stream_set_create_srv_conf(ngx_conf_t *cf)\n{\n    ngx_stream_set_srv_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_set_srv_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc():\n     *\n     *     conf->commands = { NULL };\n     */\n\n    return conf;\n}\n\n\nstatic char *\nngx_stream_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_stream_set_srv_conf_t  *scf = conf;\n\n    ngx_str_t                           *args;\n    ngx_int_t                            index;\n    ngx_stream_set_cmd_t                *set_cmd;\n    ngx_stream_variable_t               *v;\n    ngx_stream_compile_complex_value_t   ccv;\n\n    args = cf->args->elts;\n\n    if (args[1].data[0] != '$') {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid variable name \\\"%V\\\"\", &args[1]);\n        return NGX_CONF_ERROR;\n    }\n\n    args[1].len--;\n    args[1].data++;\n\n    v = ngx_stream_add_variable(cf, &args[1],\n                                NGX_STREAM_VAR_CHANGEABLE|NGX_STREAM_VAR_WEAK);\n    if (v == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    index = ngx_stream_get_variable_index(cf, &args[1]);\n    if (index == NGX_ERROR) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (v->get_handler == NULL) {\n        v->get_handler = ngx_stream_set_var;\n    }\n\n    if (scf->commands.elts == NULL) {\n        if (ngx_array_init(&scf->commands, cf->pool, 1,\n                           sizeof(ngx_stream_set_cmd_t))\n            != NGX_OK)\n        {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    set_cmd = ngx_array_push(&scf->commands);\n    if (set_cmd == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    set_cmd->index = index;\n    set_cmd->set_handler = v->set_handler;\n    set_cmd->data = v->data;\n\n    ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t));\n\n    ccv.cf = cf;\n    ccv.value = &args[2];\n    ccv.complex_value = &set_cmd->value;\n\n    if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n"
  },
  {
    "path": "src/stream/ngx_stream_split_clients_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_stream.h>\n\n\ntypedef struct {\n    uint32_t                      percent;\n    ngx_stream_variable_value_t   value;\n} ngx_stream_split_clients_part_t;\n\n\ntypedef struct {\n    ngx_stream_complex_value_t    value;\n    ngx_array_t                   parts;\n} ngx_stream_split_clients_ctx_t;\n\n\nstatic char *ngx_conf_split_clients_block(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_stream_split_clients(ngx_conf_t *cf, ngx_command_t *dummy,\n    void *conf);\n\nstatic ngx_command_t  ngx_stream_split_clients_commands[] = {\n\n    { ngx_string(\"split_clients\"),\n      NGX_STREAM_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE2,\n      ngx_conf_split_clients_block,\n      NGX_STREAM_MAIN_CONF_OFFSET,\n      0,\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_stream_module_t  ngx_stream_split_clients_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    NULL,                                  /* 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\n\nngx_module_t  ngx_stream_split_clients_module = {\n    NGX_MODULE_V1,\n    &ngx_stream_split_clients_module_ctx,  /* module context */\n    ngx_stream_split_clients_commands,     /* module directives */\n    NGX_STREAM_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_stream_split_clients_variable(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data)\n{\n    ngx_stream_split_clients_ctx_t *ctx =\n                                       (ngx_stream_split_clients_ctx_t *) data;\n\n    uint32_t                          hash;\n    ngx_str_t                         val;\n    ngx_uint_t                        i;\n    ngx_stream_split_clients_part_t  *part;\n\n    *v = ngx_stream_variable_null_value;\n\n    if (ngx_stream_complex_value(s, &ctx->value, &val) != NGX_OK) {\n        return NGX_OK;\n    }\n\n    hash = ngx_murmur_hash2(val.data, val.len);\n\n    part = ctx->parts.elts;\n\n    for (i = 0; i < ctx->parts.nelts; i++) {\n\n        ngx_log_debug2(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,\n                       \"stream split: %uD %uD\", hash, part[i].percent);\n\n        if (hash < part[i].percent || part[i].percent == 0) {\n            *v = part[i].value;\n            return NGX_OK;\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nstatic char *\nngx_conf_split_clients_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    char                                *rv;\n    uint32_t                             sum, last;\n    ngx_str_t                           *value, name;\n    ngx_uint_t                           i;\n    ngx_conf_t                           save;\n    ngx_stream_variable_t               *var;\n    ngx_stream_split_clients_ctx_t      *ctx;\n    ngx_stream_split_clients_part_t     *part;\n    ngx_stream_compile_complex_value_t   ccv;\n\n    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_stream_split_clients_ctx_t));\n    if (ctx == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    value = cf->args->elts;\n\n    ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t));\n\n    ccv.cf = cf;\n    ccv.value = &value[1];\n    ccv.complex_value = &ctx->value;\n\n    if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    name = value[2];\n\n    if (name.data[0] != '$') {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid variable name \\\"%V\\\"\", &name);\n        return NGX_CONF_ERROR;\n    }\n\n    name.len--;\n    name.data++;\n\n    var = ngx_stream_add_variable(cf, &name, NGX_STREAM_VAR_CHANGEABLE);\n    if (var == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    var->get_handler = ngx_stream_split_clients_variable;\n    var->data = (uintptr_t) ctx;\n\n    if (ngx_array_init(&ctx->parts, cf->pool, 2,\n                       sizeof(ngx_stream_split_clients_part_t))\n        != NGX_OK)\n    {\n        return NGX_CONF_ERROR;\n    }\n\n    save = *cf;\n    cf->ctx = ctx;\n    cf->handler = ngx_stream_split_clients;\n    cf->handler_conf = conf;\n\n    rv = ngx_conf_parse(cf, NULL);\n\n    *cf = save;\n\n    if (rv != NGX_CONF_OK) {\n        return rv;\n    }\n\n    sum = 0;\n    last = 0;\n    part = ctx->parts.elts;\n\n    for (i = 0; i < ctx->parts.nelts; i++) {\n        sum = part[i].percent ? sum + part[i].percent : 10000;\n        if (sum > 10000) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"percent total is greater than 100%%\");\n            return NGX_CONF_ERROR;\n        }\n\n        if (part[i].percent) {\n            last += part[i].percent * (uint64_t) 0xffffffff / 10000;\n            part[i].percent = last;\n        }\n    }\n\n    return rv;\n}\n\n\nstatic char *\nngx_stream_split_clients(ngx_conf_t *cf, ngx_command_t *dummy, void *conf)\n{\n    ngx_int_t                         n;\n    ngx_str_t                        *value;\n    ngx_stream_split_clients_ctx_t   *ctx;\n    ngx_stream_split_clients_part_t  *part;\n\n    ctx = cf->ctx;\n    value = cf->args->elts;\n\n    part = ngx_array_push(&ctx->parts);\n    if (part == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (value[0].len == 1 && value[0].data[0] == '*') {\n        part->percent = 0;\n\n    } else {\n        if (value[0].len == 0 || value[0].data[value[0].len - 1] != '%') {\n            goto invalid;\n        }\n\n        n = ngx_atofp(value[0].data, value[0].len - 1, 2);\n        if (n == NGX_ERROR || n == 0) {\n            goto invalid;\n        }\n\n        part->percent = (uint32_t) n;\n    }\n\n    part->value.len = value[1].len;\n    part->value.valid = 1;\n    part->value.no_cacheable = 0;\n    part->value.not_found = 0;\n    part->value.data = value[1].data;\n\n    return NGX_CONF_OK;\n\ninvalid:\n\n    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                       \"invalid percent value \\\"%V\\\"\", &value[0]);\n    return NGX_CONF_ERROR;\n}\n"
  },
  {
    "path": "src/stream/ngx_stream_ssl_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_stream.h>\n\n\ntypedef ngx_int_t (*ngx_ssl_variable_handler_pt)(ngx_connection_t *c,\n    ngx_pool_t *pool, ngx_str_t *s);\n\n\n#define NGX_DEFAULT_CIPHERS     \"HIGH:!aNULL:!MD5\"\n#define NGX_DEFAULT_ECDH_CURVE  \"auto\"\n\n\nstatic ngx_int_t ngx_stream_ssl_handler(ngx_stream_session_t *s);\nstatic ngx_int_t ngx_stream_ssl_init_connection(ngx_ssl_t *ssl,\n    ngx_connection_t *c);\nstatic void ngx_stream_ssl_handshake_handler(ngx_connection_t *c);\n#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME\nstatic int ngx_stream_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad,\n    void *arg);\n#endif\n#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation\nstatic int ngx_stream_ssl_alpn_select(ngx_ssl_conn_t *ssl_conn,\n    const unsigned char **out, unsigned char *outlen,\n    const unsigned char *in, unsigned int inlen, void *arg);\n#endif\n#ifdef SSL_R_CERT_CB_ERROR\nstatic int ngx_stream_ssl_certificate(ngx_ssl_conn_t *ssl_conn, void *arg);\n#endif\nstatic ngx_int_t ngx_stream_ssl_static_variable(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_stream_ssl_variable(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data);\n\nstatic ngx_int_t ngx_stream_ssl_add_variables(ngx_conf_t *cf);\nstatic void *ngx_stream_ssl_create_conf(ngx_conf_t *cf);\nstatic char *ngx_stream_ssl_merge_conf(ngx_conf_t *cf, void *parent,\n    void *child);\n\nstatic ngx_int_t ngx_stream_ssl_compile_certificates(ngx_conf_t *cf,\n    ngx_stream_ssl_conf_t *conf);\n\nstatic char *ngx_stream_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_stream_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic char *ngx_stream_ssl_alpn(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n\nstatic char *ngx_stream_ssl_conf_command_check(ngx_conf_t *cf, void *post,\n    void *data);\n\nstatic ngx_int_t ngx_stream_ssl_init(ngx_conf_t *cf);\n\n#if (T_NGX_STREAM_SNI)\nint ngx_stream_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad,\n    void *arg);\n#endif\n\n#if (T_NGX_HAVE_DTLS)\nstatic char *ngx_stream_set_ssl_protocols(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n#endif\n\nstatic ngx_conf_bitmask_t  ngx_stream_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    { ngx_string(\"TLSv1.3\"), NGX_SSL_TLSv1_3 },\n\n#if (T_NGX_HAVE_DTLS)\n    { ngx_string(\"DTLSv1\"), NGX_SSL_DTLSv1 },\n    { ngx_string(\"DTLSv1.2\"), NGX_SSL_DTLSv1_2 },\n#endif\n    { ngx_null_string, 0 }\n};\n\n\nstatic ngx_conf_enum_t  ngx_stream_ssl_verify[] = {\n    { ngx_string(\"off\"), 0 },\n    { ngx_string(\"on\"), 1 },\n    { ngx_string(\"optional\"), 2 },\n    { ngx_string(\"optional_no_ca\"), 3 },\n    { ngx_null_string, 0 }\n};\n\n\nstatic ngx_conf_post_t  ngx_stream_ssl_conf_command_post =\n    { ngx_stream_ssl_conf_command_check };\n\n\nstatic ngx_command_t  ngx_stream_ssl_commands[] = {\n\n    { ngx_string(\"ssl_handshake_timeout\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_msec_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_ssl_conf_t, handshake_timeout),\n      NULL },\n\n    { ngx_string(\"ssl_certificate\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_array_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_ssl_conf_t, certificates),\n      NULL },\n\n    { ngx_string(\"ssl_certificate_key\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_array_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_ssl_conf_t, certificate_keys),\n      NULL },\n\n    { ngx_string(\"ssl_password_file\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_stream_ssl_password_file,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      0,\n      NULL },\n\n#if (T_NGX_SSL_NTLS)\n    { ngx_string(\"enable_ntls\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_ssl_conf_t, enable_ntls),\n      NULL },\n\n    { ngx_string(\"ssl_enc_certificate\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_ssl_conf_t, enc_certificate),\n      NULL },\n\n    { ngx_string(\"ssl_enc_certificate_key\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_ssl_conf_t, enc_certificate_key),\n      NULL },\n\n    { ngx_string(\"ssl_sign_certificate\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_ssl_conf_t, sign_certificate),\n      NULL },\n\n    { ngx_string(\"ssl_sign_certificate_key\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_ssl_conf_t, sign_certificate_key),\n      NULL },\n#endif\n\n    { ngx_string(\"ssl_dhparam\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_ssl_conf_t, dhparam),\n      NULL },\n\n    { ngx_string(\"ssl_ecdh_curve\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_ssl_conf_t, ecdh_curve),\n      NULL },\n\n    { ngx_string(\"ssl_protocols\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_1MORE,\n#if (T_NGX_HAVE_DTLS)\n      ngx_stream_set_ssl_protocols,\n#else\n      ngx_conf_set_bitmask_slot,\n#endif\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_ssl_conf_t, protocols),\n      &ngx_stream_ssl_protocols },\n\n    { ngx_string(\"ssl_ciphers\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_ssl_conf_t, ciphers),\n      NULL },\n\n    { ngx_string(\"ssl_verify_client\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_enum_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_ssl_conf_t, verify),\n      &ngx_stream_ssl_verify },\n\n    { ngx_string(\"ssl_verify_depth\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_num_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_ssl_conf_t, verify_depth),\n      NULL },\n\n    { ngx_string(\"ssl_client_certificate\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_ssl_conf_t, client_certificate),\n      NULL },\n\n    { ngx_string(\"ssl_trusted_certificate\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_ssl_conf_t, trusted_certificate),\n      NULL },\n\n    { ngx_string(\"ssl_prefer_server_ciphers\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_ssl_conf_t, prefer_server_ciphers),\n      NULL },\n\n    { ngx_string(\"ssl_session_cache\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE12,\n      ngx_stream_ssl_session_cache,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      0,\n      NULL },\n\n    { ngx_string(\"ssl_session_tickets\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_ssl_conf_t, session_tickets),\n      NULL },\n\n    { ngx_string(\"ssl_session_ticket_key\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_array_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_ssl_conf_t, session_ticket_keys),\n      NULL },\n\n    { ngx_string(\"ssl_session_timeout\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_sec_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_ssl_conf_t, session_timeout),\n      NULL },\n\n    { ngx_string(\"ssl_crl\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1,\n      ngx_conf_set_str_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_ssl_conf_t, crl),\n      NULL },\n\n    { ngx_string(\"ssl_conf_command\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE2,\n      ngx_conf_set_keyval_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_ssl_conf_t, conf_commands),\n      &ngx_stream_ssl_conf_command_post },\n\n    { ngx_string(\"ssl_alpn\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_1MORE,\n      ngx_stream_ssl_alpn,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      0,\n      NULL },\n\n#if (T_NGX_STREAM_SNI)\n    { ngx_string(\"ssl_sni_force\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_ssl_conf_t, sni_force),\n      NULL },\n#endif\n\n      ngx_null_command\n};\n\n\nstatic ngx_stream_module_t  ngx_stream_ssl_module_ctx = {\n    ngx_stream_ssl_add_variables,          /* preconfiguration */\n    ngx_stream_ssl_init,                   /* postconfiguration */\n\n    NULL,                                  /* create main configuration */\n    NULL,                                  /* init main configuration */\n\n    ngx_stream_ssl_create_conf,            /* create server configuration */\n    ngx_stream_ssl_merge_conf              /* merge server configuration */\n};\n\n\nngx_module_t  ngx_stream_ssl_module = {\n    NGX_MODULE_V1,\n    &ngx_stream_ssl_module_ctx,            /* module context */\n    ngx_stream_ssl_commands,               /* module directives */\n    NGX_STREAM_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_stream_variable_t  ngx_stream_ssl_vars[] = {\n\n    { ngx_string(\"ssl_protocol\"), NULL, ngx_stream_ssl_static_variable,\n      (uintptr_t) ngx_ssl_get_protocol, NGX_STREAM_VAR_CHANGEABLE, 0 },\n\n    { ngx_string(\"ssl_cipher\"), NULL, ngx_stream_ssl_static_variable,\n      (uintptr_t) ngx_ssl_get_cipher_name, NGX_STREAM_VAR_CHANGEABLE, 0 },\n\n    { ngx_string(\"ssl_ciphers\"), NULL, ngx_stream_ssl_variable,\n      (uintptr_t) ngx_ssl_get_ciphers, NGX_STREAM_VAR_CHANGEABLE, 0 },\n\n    { ngx_string(\"ssl_curve\"), NULL, ngx_stream_ssl_variable,\n      (uintptr_t) ngx_ssl_get_curve, NGX_STREAM_VAR_CHANGEABLE, 0 },\n\n    { ngx_string(\"ssl_curves\"), NULL, ngx_stream_ssl_variable,\n      (uintptr_t) ngx_ssl_get_curves, NGX_STREAM_VAR_CHANGEABLE, 0 },\n\n    { ngx_string(\"ssl_session_id\"), NULL, ngx_stream_ssl_variable,\n      (uintptr_t) ngx_ssl_get_session_id, NGX_STREAM_VAR_CHANGEABLE, 0 },\n\n    { ngx_string(\"ssl_session_reused\"), NULL, ngx_stream_ssl_variable,\n      (uintptr_t) ngx_ssl_get_session_reused, NGX_STREAM_VAR_CHANGEABLE, 0 },\n\n    { ngx_string(\"ssl_server_name\"), NULL, ngx_stream_ssl_variable,\n      (uintptr_t) ngx_ssl_get_server_name, NGX_STREAM_VAR_CHANGEABLE, 0 },\n\n    { ngx_string(\"ssl_alpn_protocol\"), NULL, ngx_stream_ssl_variable,\n      (uintptr_t) ngx_ssl_get_alpn_protocol, NGX_STREAM_VAR_CHANGEABLE, 0 },\n\n    { ngx_string(\"ssl_client_cert\"), NULL, ngx_stream_ssl_variable,\n      (uintptr_t) ngx_ssl_get_certificate, NGX_STREAM_VAR_CHANGEABLE, 0 },\n\n    { ngx_string(\"ssl_client_raw_cert\"), NULL, ngx_stream_ssl_variable,\n      (uintptr_t) ngx_ssl_get_raw_certificate,\n      NGX_STREAM_VAR_CHANGEABLE, 0 },\n\n    { ngx_string(\"ssl_client_escaped_cert\"), NULL, ngx_stream_ssl_variable,\n      (uintptr_t) ngx_ssl_get_escaped_certificate,\n      NGX_STREAM_VAR_CHANGEABLE, 0 },\n\n    { ngx_string(\"ssl_client_s_dn\"), NULL, ngx_stream_ssl_variable,\n      (uintptr_t) ngx_ssl_get_subject_dn, NGX_STREAM_VAR_CHANGEABLE, 0 },\n\n    { ngx_string(\"ssl_client_i_dn\"), NULL, ngx_stream_ssl_variable,\n      (uintptr_t) ngx_ssl_get_issuer_dn, NGX_STREAM_VAR_CHANGEABLE, 0 },\n\n    { ngx_string(\"ssl_client_serial\"), NULL, ngx_stream_ssl_variable,\n      (uintptr_t) ngx_ssl_get_serial_number, NGX_STREAM_VAR_CHANGEABLE, 0 },\n\n    { ngx_string(\"ssl_client_fingerprint\"), NULL, ngx_stream_ssl_variable,\n      (uintptr_t) ngx_ssl_get_fingerprint, NGX_STREAM_VAR_CHANGEABLE, 0 },\n\n    { ngx_string(\"ssl_client_verify\"), NULL, ngx_stream_ssl_variable,\n      (uintptr_t) ngx_ssl_get_client_verify, NGX_STREAM_VAR_CHANGEABLE, 0 },\n\n    { ngx_string(\"ssl_client_v_start\"), NULL, ngx_stream_ssl_variable,\n      (uintptr_t) ngx_ssl_get_client_v_start, NGX_STREAM_VAR_CHANGEABLE, 0 },\n\n    { ngx_string(\"ssl_client_v_end\"), NULL, ngx_stream_ssl_variable,\n      (uintptr_t) ngx_ssl_get_client_v_end, NGX_STREAM_VAR_CHANGEABLE, 0 },\n\n    { ngx_string(\"ssl_client_v_remain\"), NULL, ngx_stream_ssl_variable,\n      (uintptr_t) ngx_ssl_get_client_v_remain, NGX_STREAM_VAR_CHANGEABLE, 0 },\n\n#if (T_NGX_SSL_HANDSHAKE_TIME)\n    /* $ssl_shandshakd_time deprecated and will be removed in the next release */\n    { ngx_string(\"ssl_handshakd_time\"), NULL, ngx_stream_ssl_variable,\n      (uintptr_t) ngx_ssl_get_handshake_time, NGX_STREAM_VAR_CHANGEABLE, 0 },\n\n    { ngx_string(\"ssl_handshake_time\"), NULL, ngx_stream_ssl_variable,\n      (uintptr_t) ngx_ssl_get_handshake_time, NGX_STREAM_VAR_CHANGEABLE, 0 },\n\n    { ngx_string(\"ssl_handshake_time_msec\"), NULL, ngx_stream_ssl_variable,\n      (uintptr_t) ngx_ssl_get_handshake_time_msec, NGX_STREAM_VAR_CHANGEABLE, 0 },\n#endif\n\n      ngx_stream_null_variable\n};\n\n\nstatic ngx_str_t ngx_stream_ssl_sess_id_ctx = ngx_string(\"STREAM\");\n\n\nstatic ngx_int_t\nngx_stream_ssl_handler(ngx_stream_session_t *s)\n{\n    long                    rc;\n    X509                   *cert;\n    ngx_int_t               rv;\n    ngx_connection_t       *c;\n    ngx_stream_ssl_conf_t  *sslcf;\n\n    if (!s->ssl) {\n        return NGX_OK;\n    }\n\n    c = s->connection;\n\n    sslcf = ngx_stream_get_module_srv_conf(s, ngx_stream_ssl_module);\n\n    if (c->ssl == NULL) {\n        c->log->action = \"SSL handshaking\";\n\n        rv = ngx_stream_ssl_init_connection(&sslcf->ssl, c);\n\n        if (rv != NGX_OK) {\n            return rv;\n        }\n    }\n\n    if (sslcf->verify) {\n        rc = SSL_get_verify_result(c->ssl->connection);\n\n        if (rc != X509_V_OK\n            && (sslcf->verify != 3 || !ngx_ssl_verify_error_optional(rc)))\n        {\n            ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                          \"client SSL certificate verify error: (%l:%s)\",\n                          rc, X509_verify_cert_error_string(rc));\n\n            ngx_ssl_remove_cached_session(c->ssl->session_ctx,\n                                       (SSL_get0_session(c->ssl->connection)));\n            return NGX_ERROR;\n        }\n\n        if (sslcf->verify == 1) {\n            cert = SSL_get_peer_certificate(c->ssl->connection);\n\n            if (cert == NULL) {\n                ngx_log_error(NGX_LOG_INFO, c->log, 0,\n                              \"client sent no required SSL certificate\");\n\n                ngx_ssl_remove_cached_session(c->ssl->session_ctx,\n                                       (SSL_get0_session(c->ssl->connection)));\n                return NGX_ERROR;\n            }\n\n            X509_free(cert);\n        }\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_ssl_init_connection(ngx_ssl_t *ssl, ngx_connection_t *c)\n{\n    ngx_int_t                    rc;\n    ngx_stream_session_t        *s;\n    ngx_stream_ssl_conf_t       *sslcf;\n    ngx_stream_core_srv_conf_t  *cscf;\n\n    s = c->data;\n\n    cscf = ngx_stream_get_module_srv_conf(s, ngx_stream_core_module);\n\n    if (\n#if (T_NGX_HAVE_DTLS)\n        c->type == SOCK_STREAM &&\n#endif\n        cscf->tcp_nodelay && ngx_tcp_nodelay(c) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    if (ngx_ssl_create_connection(ssl, c, 0) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n#if (T_NGX_SSL_HANDSHAKE_TIME)\n    {\n    /* ssl handshake start time */\n    ngx_time_t *tp = ngx_timeofday();\n    c->ssl->handshake_start_msec = tp->sec * 1000 + tp->msec;\n    }\n#endif\n#if (T_NGX_SSL_NTLS)\n    sslcf = ngx_stream_get_module_srv_conf(s, ngx_stream_ssl_module);\n\n    if (sslcf->enable_ntls) {\n        SSL_enable_ntls(c->ssl->connection);\n    }\n#endif\n\n    rc = ngx_ssl_handshake(c);\n\n    if (rc == NGX_ERROR) {\n        return NGX_ERROR;\n    }\n\n    if (rc == NGX_AGAIN) {\n#if (T_NGX_SSL_NTLS)\n        ;\n#else\n        sslcf = ngx_stream_get_module_srv_conf(s, ngx_stream_ssl_module);\n#endif\n        ngx_add_timer(c->read, sslcf->handshake_timeout);\n\n        c->ssl->handler = ngx_stream_ssl_handshake_handler;\n\n        return NGX_AGAIN;\n    }\n\n    /* rc == NGX_OK */\n\n    return NGX_OK;\n}\n\n\nstatic void\nngx_stream_ssl_handshake_handler(ngx_connection_t *c)\n{\n    ngx_stream_session_t  *s;\n\n    s = c->data;\n\n    if (!c->ssl->handshaked) {\n        ngx_stream_finalize_session(s, NGX_STREAM_INTERNAL_SERVER_ERROR);\n        return;\n    }\n\n    if (c->read->timer_set) {\n        ngx_del_timer(c->read);\n    }\n\n    ngx_stream_core_run_phases(s);\n}\n\n#ifdef T_NGX_STREAM_SNI\nstatic ngx_int_t\nngx_stream_find_virtual_server(ngx_connection_t *c,\n    ngx_stream_virtual_names_t *virtual_names, ngx_str_t *host,\n    ngx_stream_core_srv_conf_t **cscfp)\n{\n    ngx_stream_core_srv_conf_t  *cscf;\n\n    if (virtual_names == NULL) {\n        return NGX_DECLINED;\n    }\n\n    cscf = ngx_hash_find_combined(&virtual_names->names,\n                                  ngx_hash_key(host->data, host->len),\n                                  host->data, host->len);\n\n    if (cscf) {\n        *cscfp = cscf;\n        return NGX_OK;\n    }\n\n    return NGX_DECLINED;\n}\n\nint\nngx_stream_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg)\n{\n    ngx_str_t                   host;\n    const char                 *servername;\n    ngx_connection_t           *c;\n    ngx_stream_session_t       *s;\n    ngx_stream_ssl_conf_t      *sscf;\n    ngx_stream_core_srv_conf_t *cscf;\n\n    c = ngx_ssl_get_connection(ssl_conn);\n    s = c->data;\n\n    servername = SSL_get_servername(ssl_conn, TLSEXT_NAMETYPE_host_name);\n\n    if (servername == NULL) {\n        goto not_match;\n    }\n\n    if (c->ssl->renegotiation) {\n        return SSL_TLSEXT_ERR_NOACK;\n    }\n\n    host.len = ngx_strlen(servername);\n    if (host.len == 0) {\n        goto not_match;\n    }\n\n    host.data = (u_char *) servername;\n\n\n    if (ngx_stream_find_virtual_server(c, s->addr_conf->virtual_names, &host,\n                                       &cscf)\n        != NGX_OK)\n    {\n        goto not_match;\n    }\n\n    ngx_set_connection_log(c, cscf->error_log);\n\n    s->main_conf = cscf->ctx->main_conf;\n    s->srv_conf  = cscf->ctx->srv_conf;\n\n    sscf = ngx_stream_get_module_srv_conf(cscf->ctx, ngx_stream_ssl_module);\n\n    if (sscf->ssl.ctx) {\n        SSL_set_SSL_CTX(ssl_conn, sscf->ssl.ctx);\n\n        /*\n         * SSL_set_SSL_CTX() only changes certs as of 1.0.0d\n         * adjust other things we care about\n         */\n\n        SSL_set_verify(ssl_conn, SSL_CTX_get_verify_mode(sscf->ssl.ctx),\n                       SSL_CTX_get_verify_callback(sscf->ssl.ctx));\n\n        SSL_set_verify_depth(ssl_conn, SSL_CTX_get_verify_depth(sscf->ssl.ctx));\n\n#ifdef SSL_CTRL_CLEAR_OPTIONS\n        /* only in 0.9.8m+ */\n        SSL_clear_options(ssl_conn, SSL_get_options(ssl_conn) &\n                                    ~SSL_CTX_get_options(sscf->ssl.ctx));\n#endif\n\n        SSL_set_options(ssl_conn, SSL_CTX_get_options(sscf->ssl.ctx));\n    }\n\n    return SSL_TLSEXT_ERR_OK;\n\nnot_match:\n    sscf = ngx_stream_get_module_srv_conf(s, ngx_stream_ssl_module);\n\n    if (sscf->sni_force) {\n        ngx_log_error(NGX_LOG_ERR, c->log, 0,\n                      \"SSL sni not match, sni:%s, reject\", servername?servername:\"NULL\");\n        return SSL_TLSEXT_ERR_ALERT_FATAL;\n\n    } else {\n        return SSL_TLSEXT_ERR_NOACK;\n    }\n}\n\n#else\n#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME\n\nstatic int\nngx_stream_ssl_servername(ngx_ssl_conn_t *ssl_conn, int *ad, void *arg)\n{\n    return SSL_TLSEXT_ERR_OK;\n}\n\n#endif\n#endif\n\n\n#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation\n\nstatic int\nngx_stream_ssl_alpn_select(ngx_ssl_conn_t *ssl_conn, const unsigned char **out,\n    unsigned char *outlen, const unsigned char *in, unsigned int inlen,\n    void *arg)\n{\n    ngx_str_t         *alpn;\n#if (NGX_DEBUG)\n    unsigned int       i;\n    ngx_connection_t  *c;\n\n    c = ngx_ssl_get_connection(ssl_conn);\n\n    for (i = 0; i < inlen; i += in[i] + 1) {\n        ngx_log_debug2(NGX_LOG_DEBUG_STREAM, c->log, 0,\n                       \"SSL ALPN supported by client: %*s\",\n                       (size_t) in[i], &in[i + 1]);\n    }\n\n#endif\n\n    alpn = arg;\n\n    if (SSL_select_next_proto((unsigned char **) out, outlen, alpn->data,\n                              alpn->len, in, inlen)\n        != OPENSSL_NPN_NEGOTIATED)\n    {\n        return SSL_TLSEXT_ERR_ALERT_FATAL;\n    }\n\n    ngx_log_debug2(NGX_LOG_DEBUG_STREAM, c->log, 0,\n                   \"SSL ALPN selected: %*s\", (size_t) *outlen, *out);\n\n    return SSL_TLSEXT_ERR_OK;\n}\n\n#endif\n\n\n#ifdef SSL_R_CERT_CB_ERROR\n\nstatic int\nngx_stream_ssl_certificate(ngx_ssl_conn_t *ssl_conn, void *arg)\n{\n    ngx_str_t                    cert, key;\n    ngx_uint_t                   i, nelts;\n    ngx_connection_t            *c;\n    ngx_stream_session_t        *s;\n    ngx_stream_ssl_conf_t       *sslcf;\n    ngx_stream_complex_value_t  *certs, *keys;\n\n    c = ngx_ssl_get_connection(ssl_conn);\n\n    if (c->ssl->handshaked) {\n        return 0;\n    }\n\n    s = c->data;\n\n    sslcf = arg;\n\n    nelts = sslcf->certificate_values->nelts;\n    certs = sslcf->certificate_values->elts;\n    keys = sslcf->certificate_key_values->elts;\n\n    for (i = 0; i < nelts; i++) {\n\n        if (ngx_stream_complex_value(s, &certs[i], &cert) != NGX_OK) {\n            return 0;\n        }\n\n        ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0,\n                       \"ssl cert: \\\"%s\\\"\", cert.data);\n\n        if (ngx_stream_complex_value(s, &keys[i], &key) != NGX_OK) {\n            return 0;\n        }\n\n        ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0,\n                       \"ssl key: \\\"%s\\\"\", key.data);\n\n        if (ngx_ssl_connection_certificate(c, c->pool, &cert, &key,\n                                           sslcf->passwords)\n            != NGX_OK)\n        {\n            return 0;\n        }\n    }\n\n    return 1;\n}\n\n#endif\n\n\nstatic ngx_int_t\nngx_stream_ssl_static_variable(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data)\n{\n    ngx_ssl_variable_handler_pt  handler = (ngx_ssl_variable_handler_pt) data;\n\n    size_t     len;\n    ngx_str_t  str;\n\n    if (s->connection->ssl) {\n\n        (void) handler(s->connection, NULL, &str);\n\n        v->data = str.data;\n\n        for (len = 0; v->data[len]; len++) { /* void */ }\n\n        v->len = len;\n        v->valid = 1;\n        v->no_cacheable = 0;\n        v->not_found = 0;\n\n        return NGX_OK;\n    }\n\n    v->not_found = 1;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_ssl_variable(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data)\n{\n    ngx_ssl_variable_handler_pt  handler = (ngx_ssl_variable_handler_pt) data;\n\n    ngx_str_t  str;\n\n    if (s->connection->ssl) {\n\n        if (handler(s->connection, s->connection->pool, &str) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        v->len = str.len;\n        v->data = str.data;\n\n        if (v->len) {\n            v->valid = 1;\n            v->no_cacheable = 0;\n            v->not_found = 0;\n\n            return NGX_OK;\n        }\n    }\n\n    v->not_found = 1;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_ssl_add_variables(ngx_conf_t *cf)\n{\n    ngx_stream_variable_t  *var, *v;\n\n    for (v = ngx_stream_ssl_vars; v->name.len; v++) {\n        var = ngx_stream_add_variable(cf, &v->name, v->flags);\n        if (var == NULL) {\n            return NGX_ERROR;\n        }\n\n        var->get_handler = v->get_handler;\n        var->data = v->data;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void *\nngx_stream_ssl_create_conf(ngx_conf_t *cf)\n{\n    ngx_stream_ssl_conf_t  *scf;\n\n    scf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_ssl_conf_t));\n    if (scf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc():\n     *\n     *     scf->listen = 0;\n     *     scf->protocols = 0;\n     *     scf->certificate_values = NULL;\n     *     scf->dhparam = { 0, NULL };\n     *     scf->ecdh_curve = { 0, NULL };\n     *     scf->client_certificate = { 0, NULL };\n     *     scf->trusted_certificate = { 0, NULL };\n     *     scf->crl = { 0, NULL };\n     *     scf->alpn = { 0, NULL };\n     *     scf->ciphers = { 0, NULL };\n     *     scf->shm_zone = NULL;\n     */\n\n    scf->handshake_timeout = NGX_CONF_UNSET_MSEC;\n    scf->certificates = NGX_CONF_UNSET_PTR;\n    scf->certificate_keys = NGX_CONF_UNSET_PTR;\n    scf->passwords = NGX_CONF_UNSET_PTR;\n    scf->conf_commands = NGX_CONF_UNSET_PTR;\n    scf->prefer_server_ciphers = NGX_CONF_UNSET;\n    scf->verify = NGX_CONF_UNSET_UINT;\n    scf->verify_depth = NGX_CONF_UNSET_UINT;\n    scf->builtin_session_cache = NGX_CONF_UNSET;\n    scf->session_timeout = NGX_CONF_UNSET;\n    scf->session_tickets = NGX_CONF_UNSET;\n    scf->session_ticket_keys = NGX_CONF_UNSET_PTR;\n\n#if (T_NGX_STREAM_SNI)\n    scf->sni_force = NGX_CONF_UNSET;\n#endif\n\n#if (T_NGX_SSL_NTLS)\n    scf->enable_ntls = NGX_CONF_UNSET;\n#endif\n    return scf;\n}\n\n\nstatic char *\nngx_stream_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_stream_ssl_conf_t *prev = parent;\n    ngx_stream_ssl_conf_t *conf = child;\n\n    ngx_pool_cleanup_t  *cln;\n\n    ngx_conf_merge_msec_value(conf->handshake_timeout,\n                         prev->handshake_timeout, 60000);\n\n    ngx_conf_merge_value(conf->session_timeout,\n                         prev->session_timeout, 300);\n\n    ngx_conf_merge_value(conf->prefer_server_ciphers,\n                         prev->prefer_server_ciphers, 0);\n\n    ngx_conf_merge_bitmask_value(conf->protocols, prev->protocols,\n                         (NGX_CONF_BITMASK_SET\n                          |NGX_SSL_TLSv1|NGX_SSL_TLSv1_1\n                          |NGX_SSL_TLSv1_2|NGX_SSL_TLSv1_3));\n\n    ngx_conf_merge_uint_value(conf->verify, prev->verify, 0);\n    ngx_conf_merge_uint_value(conf->verify_depth, prev->verify_depth, 1);\n\n    ngx_conf_merge_ptr_value(conf->certificates, prev->certificates, NULL);\n    ngx_conf_merge_ptr_value(conf->certificate_keys, prev->certificate_keys,\n                         NULL);\n\n    ngx_conf_merge_ptr_value(conf->passwords, prev->passwords, NULL);\n\n#if (T_NGX_SSL_NTLS)\n    ngx_conf_merge_value(conf->enable_ntls, prev->enable_ntls, 0);\n    ngx_conf_merge_str_value(conf->enc_certificate,\n                             prev->enc_certificate, \"\");\n    ngx_conf_merge_str_value(conf->enc_certificate_key,\n                             prev->enc_certificate_key, \"\");\n    ngx_conf_merge_str_value(conf->sign_certificate,\n                             prev->sign_certificate, \"\");\n    ngx_conf_merge_str_value(conf->sign_certificate_key,\n                             prev->sign_certificate_key, \"\");\n#endif\n\n    ngx_conf_merge_str_value(conf->dhparam, prev->dhparam, \"\");\n\n    ngx_conf_merge_str_value(conf->client_certificate, prev->client_certificate,\n                         \"\");\n    ngx_conf_merge_str_value(conf->trusted_certificate,\n                         prev->trusted_certificate, \"\");\n    ngx_conf_merge_str_value(conf->crl, prev->crl, \"\");\n    ngx_conf_merge_str_value(conf->alpn, prev->alpn, \"\");\n\n    ngx_conf_merge_str_value(conf->ecdh_curve, prev->ecdh_curve,\n                         NGX_DEFAULT_ECDH_CURVE);\n\n    ngx_conf_merge_str_value(conf->ciphers, prev->ciphers, NGX_DEFAULT_CIPHERS);\n\n    ngx_conf_merge_ptr_value(conf->conf_commands, prev->conf_commands, NULL);\n\n#if (T_NGX_STREAM_SNI)\n    ngx_conf_merge_value(conf->sni_force, prev->sni_force, 0);\n#endif\n\n    conf->ssl.log = cf->log;\n\n    if (!conf->listen) {\n        return NGX_CONF_OK;\n    }\n\n    if (conf->certificates == NULL\n#if (T_NGX_SSL_NTLS)\n        && conf->enc_certificate.len == 0\n        && conf->sign_certificate.len == 0\n#endif\n    ) {\n        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n#if (T_NGX_SSL_NTLS)\n                      \"no \\\"ssl_certificate\\\", \\\"ssl_enc_certificate\\\" or \"\n                      \"\\\"ssl_sign_certificate\\\" is defined for \"\n#else\n                      \"no \\\"ssl_certificate\\\" is defined for \"\n#endif\n                      \"the \\\"listen ... ssl\\\" directive in %s:%ui\",\n                      conf->file, conf->line);\n        return NGX_CONF_ERROR;\n    }\n#if (T_NGX_SSL_NTLS)\n    if (conf->certificates != NULL) {\n#endif\n    if (conf->certificate_keys == NULL) {\n        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                      \"no \\\"ssl_certificate_key\\\" is defined for \"\n                      \"the \\\"listen ... ssl\\\" directive in %s:%ui\",\n                      conf->file, conf->line);\n        return NGX_CONF_ERROR;\n    }\n\n    if (conf->certificate_keys->nelts < conf->certificates->nelts) {\n        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                      \"no \\\"ssl_certificate_key\\\" is defined \"\n                      \"for certificate \\\"%V\\\" and \"\n                      \"the \\\"listen ... ssl\\\" directive in %s:%ui\",\n                      ((ngx_str_t *) conf->certificates->elts)\n                      + conf->certificates->nelts - 1,\n                      conf->file, conf->line);\n        return NGX_CONF_ERROR;\n    }\n#if (T_NGX_SSL_NTLS)\n    }\n\n    if (conf->enc_certificate.len != 0 || conf->sign_certificate.len != 0) {\n        if (conf->enc_certificate.len == 0) {\n            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                          \"no \\\"ssl_enc_certificate\\\" is defined for \"\n                          \"the \\\"ssl\\\" directive in %s:%ui\",\n                          conf->file, conf->line);\n            return NGX_CONF_ERROR;\n        }\n\n        if (conf->sign_certificate.len == 0) {\n            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                          \"no \\\"ssl_sign_certificate\\\" is defined for \"\n                          \"the \\\"ssl\\\" directive in %s:%ui\",\n                          conf->file, conf->line);\n            return NGX_CONF_ERROR;\n        }\n\n        if (conf->enc_certificate_key.len == 0) {\n            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                          \"no \\\"ssl_enc_certificate_key\\\" is defined for \"\n                          \"the \\\"ssl\\\" directive in %s:%ui\",\n                          conf->file, conf->line);\n            return NGX_CONF_ERROR;\n        }\n\n        if (conf->sign_certificate_key.len == 0) {\n            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                          \"no \\\"ssl_sign_certificate_key\\\" is defined for \"\n                          \"the \\\"ssl\\\" directive in %s:%ui\",\n                          conf->file, conf->line);\n            return NGX_CONF_ERROR;\n        }\n    }\n#endif\n\n    if (ngx_ssl_create(&conf->ssl, conf->protocols, NULL) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    cln = ngx_pool_cleanup_add(cf->pool, 0);\n    if (cln == NULL) {\n        ngx_ssl_cleanup_ctx(&conf->ssl);\n        return NGX_CONF_ERROR;\n    }\n\n    cln->handler = ngx_ssl_cleanup_ctx;\n    cln->data = &conf->ssl;\n\n#if (T_NGX_STREAM_SNI)\n#if (SSL_CTRL_SET_TLSEXT_HOSTNAME)\n    if (SSL_CTX_set_tlsext_servername_callback(conf->ssl.ctx,\n                                               ngx_stream_ssl_servername)\n        == 0)\n    {\n#endif\n        ngx_log_error(NGX_LOG_WARN, cf->log, 0,\n            \"nginx was built with SNI support, however, now it is linked \"\n            \"dynamically to an OpenSSL library which has no tlsext support, \"\n            \"therefore SNI is not available\");\n#if (SSL_CTRL_SET_TLSEXT_HOSTNAME)\n    }\n#endif\n#else\n#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME\n    SSL_CTX_set_tlsext_servername_callback(conf->ssl.ctx,\n                                           ngx_stream_ssl_servername);\n#endif\n#endif\n\n#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation\n    if (conf->alpn.len) {\n        SSL_CTX_set_alpn_select_cb(conf->ssl.ctx, ngx_stream_ssl_alpn_select,\n                                   &conf->alpn);\n    }\n#endif\n\n    if (ngx_ssl_ciphers(cf, &conf->ssl, &conf->ciphers,\n                        conf->prefer_server_ciphers)\n        != NGX_OK)\n    {\n        return NGX_CONF_ERROR;\n    }\n\n    if (ngx_stream_ssl_compile_certificates(cf, conf) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (conf->certificate_values) {\n\n#ifdef SSL_R_CERT_CB_ERROR\n\n        /* install callback to lookup certificates */\n\n        SSL_CTX_set_cert_cb(conf->ssl.ctx, ngx_stream_ssl_certificate, conf);\n\n#else\n        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                      \"variables in \"\n                      \"\\\"ssl_certificate\\\" and \\\"ssl_certificate_key\\\" \"\n                      \"directives are not supported on this platform\");\n        return NGX_CONF_ERROR;\n#endif\n\n    } else {\n\n        /* configure certificates */\n\n        if (ngx_ssl_certificates(cf, &conf->ssl, conf->certificates,\n                                 conf->certificate_keys, conf->passwords)\n            != NGX_OK)\n        {\n            return NGX_CONF_ERROR;\n        }\n    }\n#if (T_NGX_SSL_NTLS)\n    if (conf->enc_certificate.len != 0) {\n        if (ngx_ssl_certificate(cf, &conf->ssl, &conf->enc_certificate,\n                                &conf->enc_certificate_key, conf->passwords,\n                                SSL_ENC_CERT)\n            != NGX_OK)\n        {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    if (conf->sign_certificate.len != 0) {\n        if (ngx_ssl_certificate(cf, &conf->ssl, &conf->sign_certificate,\n                                &conf->sign_certificate_key, conf->passwords,\n                                SSL_SIGN_CERT)\n            != NGX_OK)\n        {\n            return NGX_CONF_ERROR;\n        }\n    }\n#endif\n\n    if (conf->verify) {\n\n        if (conf->client_certificate.len == 0 && conf->verify != 3) {\n            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                          \"no ssl_client_certificate for ssl_verify_client\");\n            return NGX_CONF_ERROR;\n        }\n\n        if (ngx_ssl_client_certificate(cf, &conf->ssl,\n                                       &conf->client_certificate,\n                                       conf->verify_depth)\n            != NGX_OK)\n        {\n            return NGX_CONF_ERROR;\n        }\n\n        if (ngx_ssl_trusted_certificate(cf, &conf->ssl,\n                                        &conf->trusted_certificate,\n                                        conf->verify_depth)\n            != NGX_OK)\n        {\n            return NGX_CONF_ERROR;\n        }\n\n        if (ngx_ssl_crl(cf, &conf->ssl, &conf->crl) != NGX_OK) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    if (ngx_ssl_dhparam(cf, &conf->ssl, &conf->dhparam) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    if (ngx_ssl_ecdh_curve(cf, &conf->ssl, &conf->ecdh_curve) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_conf_merge_value(conf->builtin_session_cache,\n                         prev->builtin_session_cache, NGX_SSL_NONE_SCACHE);\n\n    if (conf->shm_zone == NULL) {\n        conf->shm_zone = prev->shm_zone;\n    }\n\n    if (ngx_ssl_session_cache(&conf->ssl, &ngx_stream_ssl_sess_id_ctx,\n                              conf->certificates, conf->builtin_session_cache,\n                              conf->shm_zone, conf->session_timeout)\n        != NGX_OK)\n    {\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_conf_merge_value(conf->session_tickets,\n                         prev->session_tickets, 1);\n\n#ifdef SSL_OP_NO_TICKET\n    if (!conf->session_tickets) {\n        SSL_CTX_set_options(conf->ssl.ctx, SSL_OP_NO_TICKET);\n    }\n#endif\n\n    ngx_conf_merge_ptr_value(conf->session_ticket_keys,\n                         prev->session_ticket_keys, NULL);\n\n    if (ngx_ssl_session_ticket_keys(cf, &conf->ssl, conf->session_ticket_keys)\n        != NGX_OK)\n    {\n        return NGX_CONF_ERROR;\n    }\n\n    if (ngx_ssl_conf_commands(cf, &conf->ssl, conf->conf_commands) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_ssl_compile_certificates(ngx_conf_t *cf,\n    ngx_stream_ssl_conf_t *conf)\n{\n    ngx_str_t                           *cert, *key;\n    ngx_uint_t                           i, nelts;\n    ngx_stream_complex_value_t          *cv;\n    ngx_stream_compile_complex_value_t   ccv;\n\n#if (T_NGX_SSL_NTLS)\n    if (conf->certificates == NULL)\n        return NGX_OK;\n#endif\n    cert = conf->certificates->elts;\n    key = conf->certificate_keys->elts;\n    nelts = conf->certificates->nelts;\n\n    for (i = 0; i < nelts; i++) {\n\n        if (ngx_stream_script_variables_count(&cert[i])) {\n            goto found;\n        }\n\n        if (ngx_stream_script_variables_count(&key[i])) {\n            goto found;\n        }\n    }\n\n    return NGX_OK;\n\nfound:\n\n    conf->certificate_values = ngx_array_create(cf->pool, nelts,\n                                           sizeof(ngx_stream_complex_value_t));\n    if (conf->certificate_values == NULL) {\n        return NGX_ERROR;\n    }\n\n    conf->certificate_key_values = ngx_array_create(cf->pool, nelts,\n                                           sizeof(ngx_stream_complex_value_t));\n    if (conf->certificate_key_values == NULL) {\n        return NGX_ERROR;\n    }\n\n    for (i = 0; i < nelts; i++) {\n\n        cv = ngx_array_push(conf->certificate_values);\n        if (cv == NULL) {\n            return NGX_ERROR;\n        }\n\n        ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t));\n\n        ccv.cf = cf;\n        ccv.value = &cert[i];\n        ccv.complex_value = cv;\n        ccv.zero = 1;\n\n        if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) {\n            return NGX_ERROR;\n        }\n\n        cv = ngx_array_push(conf->certificate_key_values);\n        if (cv == NULL) {\n            return NGX_ERROR;\n        }\n\n        ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t));\n\n        ccv.cf = cf;\n        ccv.value = &key[i];\n        ccv.complex_value = cv;\n        ccv.zero = 1;\n\n        if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) {\n            return NGX_ERROR;\n        }\n    }\n\n    conf->passwords = ngx_ssl_preserve_passwords(cf, conf->passwords);\n    if (conf->passwords == NULL) {\n        return NGX_ERROR;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic char *\nngx_stream_ssl_password_file(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_stream_ssl_conf_t  *scf = conf;\n\n    ngx_str_t  *value;\n\n    if (scf->passwords != NGX_CONF_UNSET_PTR) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    scf->passwords = ngx_ssl_read_password_file(cf, &value[1]);\n\n    if (scf->passwords == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n\n#if (T_NGX_HAVE_DTLS)\nstatic char *\nngx_stream_set_ssl_protocols(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_stream_ssl_conf_t  *scf = conf;\n\n    char  *rv;\n\n    rv = ngx_conf_set_bitmask_slot(cf, cmd, conf);\n\n    if (rv != NGX_CONF_OK) {\n        return rv;\n    }\n\n    /* DTLS protocol requires corresponding TLS version to be set */\n\n    if (scf->protocols & NGX_SSL_DTLSv1) {\n        scf->protocols |= NGX_SSL_TLSv1;\n    }\n\n    if (scf->protocols & NGX_SSL_DTLSv1_2) {\n        scf->protocols |= NGX_SSL_TLSv1_2;\n    }\n\n    return NGX_CONF_OK;\n}\n#endif\n\nstatic char *\nngx_stream_ssl_session_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_stream_ssl_conf_t  *scf = conf;\n\n    size_t       len;\n    ngx_str_t   *value, name, size;\n    ngx_int_t    n;\n    ngx_uint_t   i, j;\n\n    value = cf->args->elts;\n\n    for (i = 1; i < cf->args->nelts; i++) {\n\n        if (ngx_strcmp(value[i].data, \"off\") == 0) {\n            scf->builtin_session_cache = NGX_SSL_NO_SCACHE;\n            continue;\n        }\n\n        if (ngx_strcmp(value[i].data, \"none\") == 0) {\n            scf->builtin_session_cache = NGX_SSL_NONE_SCACHE;\n            continue;\n        }\n\n        if (ngx_strcmp(value[i].data, \"builtin\") == 0) {\n            scf->builtin_session_cache = NGX_SSL_DFLT_BUILTIN_SCACHE;\n            continue;\n        }\n\n        if (value[i].len > sizeof(\"builtin:\") - 1\n            && ngx_strncmp(value[i].data, \"builtin:\", sizeof(\"builtin:\") - 1)\n               == 0)\n        {\n            n = ngx_atoi(value[i].data + sizeof(\"builtin:\") - 1,\n                         value[i].len - (sizeof(\"builtin:\") - 1));\n\n            if (n == NGX_ERROR) {\n                goto invalid;\n            }\n\n            scf->builtin_session_cache = n;\n\n            continue;\n        }\n\n        if (value[i].len > sizeof(\"shared:\") - 1\n            && ngx_strncmp(value[i].data, \"shared:\", sizeof(\"shared:\") - 1)\n               == 0)\n        {\n            len = 0;\n\n            for (j = sizeof(\"shared:\") - 1; j < value[i].len; j++) {\n                if (value[i].data[j] == ':') {\n                    break;\n                }\n\n                len++;\n            }\n\n            if (len == 0 || j == value[i].len) {\n                goto invalid;\n            }\n\n            name.len = len;\n            name.data = value[i].data + sizeof(\"shared:\") - 1;\n\n            size.len = value[i].len - j - 1;\n            size.data = name.data + len + 1;\n\n            n = ngx_parse_size(&size);\n\n            if (n == NGX_ERROR) {\n                goto invalid;\n            }\n\n            if (n < (ngx_int_t) (8 * ngx_pagesize)) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"session cache \\\"%V\\\" is too small\",\n                                   &value[i]);\n\n                return NGX_CONF_ERROR;\n            }\n\n            scf->shm_zone = ngx_shared_memory_add(cf, &name, n,\n                                                   &ngx_stream_ssl_module);\n            if (scf->shm_zone == NULL) {\n                return NGX_CONF_ERROR;\n            }\n\n            scf->shm_zone->init = ngx_ssl_session_cache_init;\n\n            continue;\n        }\n\n        goto invalid;\n    }\n\n    if (scf->shm_zone && scf->builtin_session_cache == NGX_CONF_UNSET) {\n        scf->builtin_session_cache = NGX_SSL_NO_BUILTIN_SCACHE;\n    }\n\n    return NGX_CONF_OK;\n\ninvalid:\n\n    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                       \"invalid session cache \\\"%V\\\"\", &value[i]);\n\n    return NGX_CONF_ERROR;\n}\n\n\nstatic char *\nngx_stream_ssl_alpn(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n#ifdef TLSEXT_TYPE_application_layer_protocol_negotiation\n\n    ngx_stream_ssl_conf_t  *scf = conf;\n\n    u_char      *p;\n    size_t       len;\n    ngx_str_t   *value;\n    ngx_uint_t   i;\n\n    if (scf->alpn.len) {\n        return \"is duplicate\";\n    }\n\n    value = cf->args->elts;\n\n    len = 0;\n\n    for (i = 1; i < cf->args->nelts; i++) {\n\n        if (value[i].len > 255) {\n            return \"protocol too long\";\n        }\n\n        len += value[i].len + 1;\n    }\n\n    scf->alpn.data = ngx_pnalloc(cf->pool, len);\n    if (scf->alpn.data == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    p = scf->alpn.data;\n\n    for (i = 1; i < cf->args->nelts; i++) {\n        *p++ = value[i].len;\n        p = ngx_cpymem(p, value[i].data, value[i].len);\n    }\n\n    scf->alpn.len = len;\n\n    return NGX_CONF_OK;\n\n#else\n    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                       \"the \\\"ssl_alpn\\\" directive requires OpenSSL \"\n                       \"with ALPN support\");\n    return NGX_CONF_ERROR;\n#endif\n}\n\n\nstatic char *\nngx_stream_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#else\n    return NGX_CONF_OK;\n#endif\n}\n\n\nstatic ngx_int_t\nngx_stream_ssl_init(ngx_conf_t *cf)\n{\n    ngx_stream_handler_pt        *h;\n    ngx_stream_core_main_conf_t  *cmcf;\n#if (T_NGX_HAVE_DTLS)\n    ngx_uint_t                    i;\n    ngx_stream_listen_t          *ls;\n    ngx_stream_conf_ctx_t       *sctx;\n    ngx_stream_ssl_conf_t       **sscfp, *sscf;\n    ngx_stream_core_srv_conf_t  **cscfp, *cscf;\n#endif\n\n    cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);\n\n    h = ngx_array_push(&cmcf->phases[NGX_STREAM_SSL_PHASE].handlers);\n    if (h == NULL) {\n        return NGX_ERROR;\n    }\n\n    *h = ngx_stream_ssl_handler;\n\n#if (T_NGX_HAVE_DTLS)\n    cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);\n\n    ls = cmcf->listen.elts;\n\n    for (i = 0; i < cmcf->listen.nelts; i++) {\n        if (ls[i].ssl) {\n            sctx = ls[i].ctx;\n\n            sscfp = (ngx_stream_ssl_conf_t **)sctx->srv_conf;\n            cscfp = (ngx_stream_core_srv_conf_t **)sctx->srv_conf;\n\n            sscf = sscfp[ngx_stream_ssl_module.ctx_index];\n            cscf = cscfp[ngx_stream_core_module.ctx_index];\n\n            if (sscf->certificates == NULL\n#if (T_NGX_SSL_NTLS)\n                && sscf->sign_certificate.len == 0\n                && sscf->enc_certificate.len == 0\n#endif\n            ) {\n                ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n#if (T_NGX_SSL_NTLS)\n                              \"no \\\"ssl_certificate\\\", \\\"ssl_enc_certificate\\\" \"\n                              \"or \\\"ssl_sign_certificate\\\" is defined \"\n#else\n                              \"no \\\"ssl_certificate\\\" is defined \"\n#endif\n                              \"in server listening on SSL port at %s:%ui\",\n                              cscf->file_name, cscf->line);\n                return NGX_ERROR;\n            }\n\n            if (ls[i].type == SOCK_DGRAM) {\n                if (!(sscf->protocols & NGX_SSL_DTLSv1\n                      || sscf->protocols & NGX_SSL_DTLSv1_2))\n                {\n                    ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                                  \"\\\"ssl_protocols\\\" does not enable DTLS in a \"\n                                  \"server listening on UDP SSL port at %s:%ui\",\n                                   cscf->file_name, cscf->line);\n                    return NGX_ERROR;\n                }\n\n            } else {\n                if (sscf->protocols & NGX_SSL_DTLSv1\n                    || sscf->protocols & NGX_SSL_DTLSv1_2 )\n                {\n                    ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                                  \"\\\"ssl_protocols\\\" includes DTLS in a server \"\n                                  \"listening on SSL port at %s:%ui\",\n                                  cscf->file_name, cscf->line);\n                    return NGX_ERROR;\n                }\n            }\n        }\n    }\n#endif\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "src/stream/ngx_stream_ssl_module.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_STREAM_SSL_H_INCLUDED_\n#define _NGX_STREAM_SSL_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_stream.h>\n\n\ntypedef struct {\n    ngx_msec_t       handshake_timeout;\n\n    ngx_flag_t       prefer_server_ciphers;\n\n    ngx_ssl_t        ssl;\n\n    ngx_uint_t       listen;\n    ngx_uint_t       protocols;\n\n    ngx_uint_t       verify;\n    ngx_uint_t       verify_depth;\n\n    ssize_t          builtin_session_cache;\n\n    time_t           session_timeout;\n\n    ngx_array_t     *certificates;\n    ngx_array_t     *certificate_keys;\n\n    ngx_array_t     *certificate_values;\n    ngx_array_t     *certificate_key_values;\n\n    ngx_str_t        dhparam;\n    ngx_str_t        ecdh_curve;\n    ngx_str_t        client_certificate;\n    ngx_str_t        trusted_certificate;\n    ngx_str_t        crl;\n    ngx_str_t        alpn;\n\n    ngx_str_t        ciphers;\n\n    ngx_array_t     *passwords;\n    ngx_array_t     *conf_commands;\n\n    ngx_shm_zone_t  *shm_zone;\n\n    ngx_flag_t       session_tickets;\n    ngx_array_t     *session_ticket_keys;\n\n    u_char          *file;\n    ngx_uint_t       line;\n\n#if (T_NGX_SSL_NTLS)\n    ngx_flag_t       enable_ntls;\n    ngx_str_t        enc_certificate;\n    ngx_str_t        enc_certificate_key;\n    ngx_str_t        sign_certificate;\n    ngx_str_t        sign_certificate_key;\n#endif\n#if (T_NGX_STREAM_SNI)\n    ngx_flag_t       sni_force;\n#endif\n} ngx_stream_ssl_conf_t;\n\n\nextern ngx_module_t  ngx_stream_ssl_module;\n\n\n#endif /* _NGX_STREAM_SSL_H_INCLUDED_ */\n"
  },
  {
    "path": "src/stream/ngx_stream_ssl_preread_module.c",
    "content": "\n/*\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_stream.h>\n\n\ntypedef struct {\n    ngx_flag_t      enabled;\n} ngx_stream_ssl_preread_srv_conf_t;\n\n\ntypedef struct {\n    size_t          left;\n    size_t          size;\n    size_t          ext;\n    u_char         *pos;\n    u_char         *dst;\n    u_char          buf[4];\n    u_char          version[2];\n    ngx_str_t       host;\n    ngx_str_t       alpn;\n    ngx_log_t      *log;\n    ngx_pool_t     *pool;\n    ngx_uint_t      state;\n} ngx_stream_ssl_preread_ctx_t;\n\n\nstatic ngx_int_t ngx_stream_ssl_preread_handler(ngx_stream_session_t *s);\nstatic ngx_int_t ngx_stream_ssl_preread_parse_record(\n    ngx_stream_ssl_preread_ctx_t *ctx, u_char *pos, u_char *last);\nstatic ngx_int_t ngx_stream_ssl_preread_protocol_variable(\n    ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_stream_ssl_preread_server_name_variable(\n    ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_stream_ssl_preread_alpn_protocols_variable(\n    ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_stream_ssl_preread_add_variables(ngx_conf_t *cf);\nstatic void *ngx_stream_ssl_preread_create_srv_conf(ngx_conf_t *cf);\nstatic char *ngx_stream_ssl_preread_merge_srv_conf(ngx_conf_t *cf, void *parent,\n    void *child);\nstatic ngx_int_t ngx_stream_ssl_preread_init(ngx_conf_t *cf);\n\n\nstatic ngx_command_t  ngx_stream_ssl_preread_commands[] = {\n\n    { ngx_string(\"ssl_preread\"),\n      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_FLAG,\n      ngx_conf_set_flag_slot,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      offsetof(ngx_stream_ssl_preread_srv_conf_t, enabled),\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_stream_module_t  ngx_stream_ssl_preread_module_ctx = {\n    ngx_stream_ssl_preread_add_variables,   /* preconfiguration */\n    ngx_stream_ssl_preread_init,            /* postconfiguration */\n\n    NULL,                                   /* create main configuration */\n    NULL,                                   /* init main configuration */\n\n    ngx_stream_ssl_preread_create_srv_conf, /* create server configuration */\n    ngx_stream_ssl_preread_merge_srv_conf   /* merge server configuration */\n};\n\n\nngx_module_t  ngx_stream_ssl_preread_module = {\n    NGX_MODULE_V1,\n    &ngx_stream_ssl_preread_module_ctx,     /* module context */\n    ngx_stream_ssl_preread_commands,        /* module directives */\n    NGX_STREAM_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_stream_variable_t  ngx_stream_ssl_preread_vars[] = {\n\n    { ngx_string(\"ssl_preread_protocol\"), NULL,\n      ngx_stream_ssl_preread_protocol_variable, 0, 0, 0 },\n\n    { ngx_string(\"ssl_preread_server_name\"), NULL,\n      ngx_stream_ssl_preread_server_name_variable, 0, 0, 0 },\n\n    { ngx_string(\"ssl_preread_alpn_protocols\"), NULL,\n      ngx_stream_ssl_preread_alpn_protocols_variable, 0, 0, 0 },\n\n      ngx_stream_null_variable\n};\n\n\nstatic ngx_int_t\nngx_stream_ssl_preread_handler(ngx_stream_session_t *s)\n{\n    u_char                             *last, *p;\n    size_t                              len;\n    ngx_int_t                           rc;\n    ngx_connection_t                   *c;\n    ngx_stream_ssl_preread_ctx_t       *ctx;\n    ngx_stream_ssl_preread_srv_conf_t  *sscf;\n\n    c = s->connection;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_STREAM, c->log, 0, \"ssl preread handler\");\n\n    sscf = ngx_stream_get_module_srv_conf(s, ngx_stream_ssl_preread_module);\n\n    if (!sscf->enabled) {\n        return NGX_DECLINED;\n    }\n\n    if (c->type != SOCK_STREAM) {\n        return NGX_DECLINED;\n    }\n\n    if (c->buffer == NULL) {\n        return NGX_AGAIN;\n    }\n\n    ctx = ngx_stream_get_module_ctx(s, ngx_stream_ssl_preread_module);\n    if (ctx == NULL) {\n        ctx = ngx_pcalloc(c->pool, sizeof(ngx_stream_ssl_preread_ctx_t));\n        if (ctx == NULL) {\n            return NGX_ERROR;\n        }\n\n        ngx_stream_set_ctx(s, ctx, ngx_stream_ssl_preread_module);\n\n        ctx->pool = c->pool;\n        ctx->log = c->log;\n        ctx->pos = c->buffer->pos;\n    }\n\n    p = ctx->pos;\n    last = c->buffer->last;\n\n    while (last - p >= 5) {\n\n        if ((p[0] & 0x80) && p[2] == 1 && (p[3] == 0 || p[3] == 3)) {\n            ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0,\n                           \"ssl preread: version 2 ClientHello\");\n            ctx->version[0] = p[3];\n            ctx->version[1] = p[4];\n            return NGX_OK;\n        }\n\n        if (p[0] != 0x16) {\n            ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0,\n                           \"ssl preread: not a handshake\");\n            ngx_stream_set_ctx(s, NULL, ngx_stream_ssl_preread_module);\n            return NGX_DECLINED;\n        }\n\n        if (p[1] != 3) {\n            ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0,\n                           \"ssl preread: unsupported SSL version\");\n            ngx_stream_set_ctx(s, NULL, ngx_stream_ssl_preread_module);\n            return NGX_DECLINED;\n        }\n\n        len = (p[3] << 8) + p[4];\n\n        /* read the whole record before parsing */\n        if ((size_t) (last - p) < len + 5) {\n            break;\n        }\n\n        p += 5;\n\n        rc = ngx_stream_ssl_preread_parse_record(ctx, p, p + len);\n\n        if (rc == NGX_DECLINED) {\n            ngx_stream_set_ctx(s, NULL, ngx_stream_ssl_preread_module);\n            return NGX_DECLINED;\n        }\n\n        if (rc != NGX_AGAIN) {\n            return rc;\n        }\n\n        p += len;\n    }\n\n    ctx->pos = p;\n\n    return NGX_AGAIN;\n}\n\n\nstatic ngx_int_t\nngx_stream_ssl_preread_parse_record(ngx_stream_ssl_preread_ctx_t *ctx,\n    u_char *pos, u_char *last)\n{\n    size_t   left, n, size, ext;\n    u_char  *dst, *p;\n\n    enum {\n        sw_start = 0,\n        sw_header,          /* handshake msg_type, length */\n        sw_version,         /* client_version */\n        sw_random,          /* random */\n        sw_sid_len,         /* session_id length */\n        sw_sid,             /* session_id */\n        sw_cs_len,          /* cipher_suites length */\n        sw_cs,              /* cipher_suites */\n        sw_cm_len,          /* compression_methods length */\n        sw_cm,              /* compression_methods */\n        sw_ext,             /* extension */\n        sw_ext_header,      /* extension_type, extension_data length */\n        sw_sni_len,         /* SNI length */\n        sw_sni_host_head,   /* SNI name_type, host_name length */\n        sw_sni_host,        /* SNI host_name */\n        sw_alpn_len,        /* ALPN length */\n        sw_alpn_proto_len,  /* ALPN protocol_name length */\n        sw_alpn_proto_data, /* ALPN protocol_name */\n        sw_supver_len       /* supported_versions length */\n    } state;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_STREAM, ctx->log, 0,\n                   \"ssl preread: state %ui left %z\", ctx->state, ctx->left);\n\n    state = ctx->state;\n    size = ctx->size;\n    left = ctx->left;\n    ext = ctx->ext;\n    dst = ctx->dst;\n    p = ctx->buf;\n\n    for ( ;; ) {\n        n = ngx_min((size_t) (last - pos), size);\n\n        if (dst) {\n            dst = ngx_cpymem(dst, pos, n);\n        }\n\n        pos += n;\n        size -= n;\n        left -= n;\n\n        if (size != 0) {\n            break;\n        }\n\n        switch (state) {\n\n        case sw_start:\n            state = sw_header;\n            dst = p;\n            size = 4;\n            left = size;\n            break;\n\n        case sw_header:\n            if (p[0] != 1) {\n                ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0,\n                               \"ssl preread: not a client hello\");\n                return NGX_DECLINED;\n            }\n\n            state = sw_version;\n            dst = ctx->version;\n            size = 2;\n            left = (p[1] << 16) + (p[2] << 8) + p[3];\n            break;\n\n        case sw_version:\n            state = sw_random;\n            dst = NULL;\n            size = 32;\n            break;\n\n        case sw_random:\n            state = sw_sid_len;\n            dst = p;\n            size = 1;\n            break;\n\n        case sw_sid_len:\n            state = sw_sid;\n            dst = NULL;\n            size = p[0];\n            break;\n\n        case sw_sid:\n            state = sw_cs_len;\n            dst = p;\n            size = 2;\n            break;\n\n        case sw_cs_len:\n            state = sw_cs;\n            dst = NULL;\n            size = (p[0] << 8) + p[1];\n            break;\n\n        case sw_cs:\n            state = sw_cm_len;\n            dst = p;\n            size = 1;\n            break;\n\n        case sw_cm_len:\n            state = sw_cm;\n            dst = NULL;\n            size = p[0];\n            break;\n\n        case sw_cm:\n            if (left == 0) {\n                /* no extensions */\n                return NGX_OK;\n            }\n\n            state = sw_ext;\n            dst = p;\n            size = 2;\n            break;\n\n        case sw_ext:\n            if (left == 0) {\n                return NGX_OK;\n            }\n\n            state = sw_ext_header;\n            dst = p;\n            size = 4;\n            break;\n\n        case sw_ext_header:\n            if (p[0] == 0 && p[1] == 0 && ctx->host.data == NULL) {\n                /* SNI extension */\n                state = sw_sni_len;\n                dst = p;\n                size = 2;\n                break;\n            }\n\n            if (p[0] == 0 && p[1] == 16 && ctx->alpn.data == NULL) {\n                /* ALPN extension */\n                state = sw_alpn_len;\n                dst = p;\n                size = 2;\n                break;\n            }\n\n            if (p[0] == 0 && p[1] == 43) {\n                /* supported_versions extension */\n                state = sw_supver_len;\n                dst = p;\n                size = 1;\n                break;\n            }\n\n            state = sw_ext;\n            dst = NULL;\n            size = (p[2] << 8) + p[3];\n            break;\n\n        case sw_sni_len:\n            ext = (p[0] << 8) + p[1];\n            state = sw_sni_host_head;\n            dst = p;\n            size = 3;\n            break;\n\n        case sw_sni_host_head:\n            if (p[0] != 0) {\n                ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0,\n                               \"ssl preread: SNI hostname type is not DNS\");\n                return NGX_DECLINED;\n            }\n\n            size = (p[1] << 8) + p[2];\n\n            if (ext < 3 + size) {\n                ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0,\n                               \"ssl preread: SNI format error\");\n                return NGX_DECLINED;\n            }\n            ext -= 3 + size;\n\n            ctx->host.data = ngx_pnalloc(ctx->pool, size);\n            if (ctx->host.data == NULL) {\n                return NGX_ERROR;\n            }\n\n            state = sw_sni_host;\n            dst = ctx->host.data;\n            break;\n\n        case sw_sni_host:\n            ctx->host.len = (p[1] << 8) + p[2];\n\n            ngx_log_debug1(NGX_LOG_DEBUG_STREAM, ctx->log, 0,\n                           \"ssl preread: SNI hostname \\\"%V\\\"\", &ctx->host);\n\n            state = sw_ext;\n            dst = NULL;\n            size = ext;\n            break;\n\n        case sw_alpn_len:\n            ext = (p[0] << 8) + p[1];\n\n            ctx->alpn.data = ngx_pnalloc(ctx->pool, ext);\n            if (ctx->alpn.data == NULL) {\n                return NGX_ERROR;\n            }\n\n            state = sw_alpn_proto_len;\n            dst = p;\n            size = 1;\n            break;\n\n        case sw_alpn_proto_len:\n            size = p[0];\n\n            if (size == 0) {\n                ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0,\n                               \"ssl preread: ALPN empty protocol\");\n                return NGX_DECLINED;\n            }\n\n            if (ext < 1 + size) {\n                ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0,\n                               \"ssl preread: ALPN format error\");\n                return NGX_DECLINED;\n            }\n            ext -= 1 + size;\n\n            state = sw_alpn_proto_data;\n            dst = ctx->alpn.data + ctx->alpn.len;\n            break;\n\n        case sw_alpn_proto_data:\n            ctx->alpn.len += p[0];\n\n            ngx_log_debug1(NGX_LOG_DEBUG_STREAM, ctx->log, 0,\n                           \"ssl preread: ALPN protocols \\\"%V\\\"\", &ctx->alpn);\n\n            if (ext) {\n                ctx->alpn.data[ctx->alpn.len++] = ',';\n\n                state = sw_alpn_proto_len;\n                dst = p;\n                size = 1;\n                break;\n            }\n\n            state = sw_ext;\n            dst = NULL;\n            size = 0;\n            break;\n\n        case sw_supver_len:\n            ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0,\n                           \"ssl preread: supported_versions\");\n\n            /* set TLSv1.3 */\n            ctx->version[0] = 3;\n            ctx->version[1] = 4;\n\n            state = sw_ext;\n            dst = NULL;\n            size = p[0];\n            break;\n        }\n\n        if (left < size) {\n            ngx_log_debug0(NGX_LOG_DEBUG_STREAM, ctx->log, 0,\n                           \"ssl preread: failed to parse handshake\");\n            return NGX_DECLINED;\n        }\n    }\n\n    ctx->state = state;\n    ctx->size = size;\n    ctx->left = left;\n    ctx->ext = ext;\n    ctx->dst = dst;\n\n    return NGX_AGAIN;\n}\n\n\nstatic ngx_int_t\nngx_stream_ssl_preread_protocol_variable(ngx_stream_session_t *s,\n    ngx_variable_value_t *v, uintptr_t data)\n{\n    ngx_str_t                      version;\n    ngx_stream_ssl_preread_ctx_t  *ctx;\n\n    ctx = ngx_stream_get_module_ctx(s, ngx_stream_ssl_preread_module);\n\n    if (ctx == NULL) {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    /* SSL_get_version() format */\n\n    ngx_str_null(&version);\n\n    switch (ctx->version[0]) {\n    case 0:\n        switch (ctx->version[1]) {\n        case 2:\n            ngx_str_set(&version, \"SSLv2\");\n            break;\n        }\n        break;\n    case 3:\n        switch (ctx->version[1]) {\n        case 0:\n            ngx_str_set(&version, \"SSLv3\");\n            break;\n        case 1:\n            ngx_str_set(&version, \"TLSv1\");\n            break;\n        case 2:\n            ngx_str_set(&version, \"TLSv1.1\");\n            break;\n        case 3:\n            ngx_str_set(&version, \"TLSv1.2\");\n            break;\n        case 4:\n            ngx_str_set(&version, \"TLSv1.3\");\n            break;\n        }\n    }\n\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->len = version.len;\n    v->data = version.data;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_ssl_preread_server_name_variable(ngx_stream_session_t *s,\n    ngx_variable_value_t *v, uintptr_t data)\n{\n    ngx_stream_ssl_preread_ctx_t  *ctx;\n\n    ctx = ngx_stream_get_module_ctx(s, ngx_stream_ssl_preread_module);\n\n    if (ctx == NULL) {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->len = ctx->host.len;\n    v->data = ctx->host.data;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_ssl_preread_alpn_protocols_variable(ngx_stream_session_t *s,\n    ngx_variable_value_t *v, uintptr_t data)\n{\n    ngx_stream_ssl_preread_ctx_t  *ctx;\n\n    ctx = ngx_stream_get_module_ctx(s, ngx_stream_ssl_preread_module);\n\n    if (ctx == NULL) {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->len = ctx->alpn.len;\n    v->data = ctx->alpn.data;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_ssl_preread_add_variables(ngx_conf_t *cf)\n{\n    ngx_stream_variable_t  *var, *v;\n\n    for (v = ngx_stream_ssl_preread_vars; v->name.len; v++) {\n        var = ngx_stream_add_variable(cf, &v->name, v->flags);\n        if (var == NULL) {\n            return NGX_ERROR;\n        }\n\n        var->get_handler = v->get_handler;\n        var->data = v->data;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic void *\nngx_stream_ssl_preread_create_srv_conf(ngx_conf_t *cf)\n{\n    ngx_stream_ssl_preread_srv_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_ssl_preread_srv_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    conf->enabled = NGX_CONF_UNSET;\n\n    return conf;\n}\n\n\nstatic char *\nngx_stream_ssl_preread_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)\n{\n    ngx_stream_ssl_preread_srv_conf_t *prev = parent;\n    ngx_stream_ssl_preread_srv_conf_t *conf = child;\n\n    ngx_conf_merge_value(conf->enabled, prev->enabled, 0);\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_ssl_preread_init(ngx_conf_t *cf)\n{\n    ngx_stream_handler_pt        *h;\n    ngx_stream_core_main_conf_t  *cmcf;\n\n    cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);\n\n    h = ngx_array_push(&cmcf->phases[NGX_STREAM_PREREAD_PHASE].handlers);\n    if (h == NULL) {\n        return NGX_ERROR;\n    }\n\n    *h = ngx_stream_ssl_preread_handler;\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "src/stream/ngx_stream_upstream.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_stream.h>\n\n\nstatic ngx_int_t ngx_stream_upstream_add_variables(ngx_conf_t *cf);\nstatic ngx_int_t ngx_stream_upstream_addr_variable(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_stream_upstream_response_time_variable(\n    ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_stream_upstream_bytes_variable(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data);\n\nstatic char *ngx_stream_upstream(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *dummy);\nstatic char *ngx_stream_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic void *ngx_stream_upstream_create_main_conf(ngx_conf_t *cf);\nstatic char *ngx_stream_upstream_init_main_conf(ngx_conf_t *cf, void *conf);\n\n\nstatic ngx_command_t  ngx_stream_upstream_commands[] = {\n\n    { ngx_string(\"upstream\"),\n      NGX_STREAM_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE1,\n      ngx_stream_upstream,\n      0,\n      0,\n      NULL },\n\n    { ngx_string(\"server\"),\n      NGX_STREAM_UPS_CONF|NGX_CONF_1MORE,\n      ngx_stream_upstream_server,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      0,\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_stream_module_t  ngx_stream_upstream_module_ctx = {\n    ngx_stream_upstream_add_variables,     /* preconfiguration */\n    NULL,                                  /* postconfiguration */\n\n    ngx_stream_upstream_create_main_conf,  /* create main configuration */\n    ngx_stream_upstream_init_main_conf,    /* init main configuration */\n\n    NULL,                                  /* create server configuration */\n    NULL                                   /* merge server configuration */\n};\n\n\nngx_module_t  ngx_stream_upstream_module = {\n    NGX_MODULE_V1,\n    &ngx_stream_upstream_module_ctx,       /* module context */\n    ngx_stream_upstream_commands,          /* module directives */\n    NGX_STREAM_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_stream_variable_t  ngx_stream_upstream_vars[] = {\n\n    { ngx_string(\"upstream_addr\"), NULL,\n      ngx_stream_upstream_addr_variable, 0,\n      NGX_STREAM_VAR_NOCACHEABLE, 0 },\n\n    { ngx_string(\"upstream_bytes_sent\"), NULL,\n      ngx_stream_upstream_bytes_variable, 0,\n      NGX_STREAM_VAR_NOCACHEABLE, 0 },\n\n    { ngx_string(\"upstream_connect_time\"), NULL,\n      ngx_stream_upstream_response_time_variable, 2,\n      NGX_STREAM_VAR_NOCACHEABLE, 0 },\n\n    { ngx_string(\"upstream_first_byte_time\"), NULL,\n      ngx_stream_upstream_response_time_variable, 1,\n      NGX_STREAM_VAR_NOCACHEABLE, 0 },\n\n    { ngx_string(\"upstream_session_time\"), NULL,\n      ngx_stream_upstream_response_time_variable, 0,\n      NGX_STREAM_VAR_NOCACHEABLE, 0 },\n\n    { ngx_string(\"upstream_bytes_received\"), NULL,\n      ngx_stream_upstream_bytes_variable, 1,\n      NGX_STREAM_VAR_NOCACHEABLE, 0 },\n\n      ngx_stream_null_variable\n};\n\n\nstatic ngx_int_t\nngx_stream_upstream_add_variables(ngx_conf_t *cf)\n{\n    ngx_stream_variable_t  *var, *v;\n\n    for (v = ngx_stream_upstream_vars; v->name.len; v++) {\n        var = ngx_stream_add_variable(cf, &v->name, v->flags);\n        if (var == NULL) {\n            return NGX_ERROR;\n        }\n\n        var->get_handler = v->get_handler;\n        var->data = v->data;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_upstream_addr_variable(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data)\n{\n    u_char                       *p;\n    size_t                        len;\n    ngx_uint_t                    i;\n    ngx_stream_upstream_state_t  *state;\n\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    if (s->upstream_states == NULL || s->upstream_states->nelts == 0) {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    len = 0;\n    state = s->upstream_states->elts;\n\n    for (i = 0; i < s->upstream_states->nelts; i++) {\n        if (state[i].peer) {\n            len += state[i].peer->len;\n        }\n\n        len += 2;\n    }\n\n    p = ngx_pnalloc(s->connection->pool, len);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    v->data = p;\n\n    i = 0;\n\n    for ( ;; ) {\n        if (state[i].peer) {\n            p = ngx_cpymem(p, state[i].peer->data, state[i].peer->len);\n        }\n\n        if (++i == s->upstream_states->nelts) {\n            break;\n        }\n\n        *p++ = ',';\n        *p++ = ' ';\n    }\n\n    v->len = p - v->data;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_upstream_bytes_variable(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data)\n{\n    u_char                       *p;\n    size_t                        len;\n    ngx_uint_t                    i;\n    ngx_stream_upstream_state_t  *state;\n\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    if (s->upstream_states == NULL || s->upstream_states->nelts == 0) {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    len = s->upstream_states->nelts * (NGX_OFF_T_LEN + 2);\n\n    p = ngx_pnalloc(s->connection->pool, len);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    v->data = p;\n\n    i = 0;\n    state = s->upstream_states->elts;\n\n    for ( ;; ) {\n\n        if (data == 1) {\n            p = ngx_sprintf(p, \"%O\", state[i].bytes_received);\n\n        } else {\n            p = ngx_sprintf(p, \"%O\", state[i].bytes_sent);\n        }\n\n        if (++i == s->upstream_states->nelts) {\n            break;\n        }\n\n        *p++ = ',';\n        *p++ = ' ';\n    }\n\n    v->len = p - v->data;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_upstream_response_time_variable(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data)\n{\n    u_char                       *p;\n    size_t                        len;\n    ngx_uint_t                    i;\n    ngx_msec_int_t                ms;\n    ngx_stream_upstream_state_t  *state;\n\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    if (s->upstream_states == NULL || s->upstream_states->nelts == 0) {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    len = s->upstream_states->nelts * (NGX_TIME_T_LEN + 4 + 2);\n\n    p = ngx_pnalloc(s->connection->pool, len);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    v->data = p;\n\n    i = 0;\n    state = s->upstream_states->elts;\n\n    for ( ;; ) {\n\n        if (data == 1) {\n            ms = state[i].first_byte_time;\n\n        } else if (data == 2) {\n            ms = state[i].connect_time;\n\n        } else {\n            ms = state[i].response_time;\n        }\n\n        if (ms != -1) {\n            ms = ngx_max(ms, 0);\n            p = ngx_sprintf(p, \"%T.%03M\", (time_t) ms / 1000, ms % 1000);\n\n        } else {\n            *p++ = '-';\n        }\n\n        if (++i == s->upstream_states->nelts) {\n            break;\n        }\n\n        *p++ = ',';\n        *p++ = ' ';\n    }\n\n    v->len = p - v->data;\n\n    return NGX_OK;\n}\n\n\nstatic char *\nngx_stream_upstream(ngx_conf_t *cf, ngx_command_t *cmd, void *dummy)\n{\n    char                            *rv;\n    void                            *mconf;\n    ngx_str_t                       *value;\n    ngx_url_t                        u;\n    ngx_uint_t                       m;\n    ngx_conf_t                       pcf;\n    ngx_stream_module_t             *module;\n    ngx_stream_conf_ctx_t           *ctx, *stream_ctx;\n    ngx_stream_upstream_srv_conf_t  *uscf;\n\n    ngx_memzero(&u, sizeof(ngx_url_t));\n\n    value = cf->args->elts;\n    u.host = value[1];\n    u.no_resolve = 1;\n    u.no_port = 1;\n\n    uscf = ngx_stream_upstream_add(cf, &u, NGX_STREAM_UPSTREAM_CREATE\n                                           |NGX_STREAM_UPSTREAM_WEIGHT\n                                           |NGX_STREAM_UPSTREAM_MAX_CONNS\n                                           |NGX_STREAM_UPSTREAM_MAX_FAILS\n                                           |NGX_STREAM_UPSTREAM_FAIL_TIMEOUT\n                                           |NGX_STREAM_UPSTREAM_DOWN\n                                           |NGX_STREAM_UPSTREAM_BACKUP);\n    if (uscf == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n\n    ctx = ngx_pcalloc(cf->pool, sizeof(ngx_stream_conf_ctx_t));\n    if (ctx == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    stream_ctx = cf->ctx;\n    ctx->main_conf = stream_ctx->main_conf;\n\n    /* the upstream{}'s srv_conf */\n\n    ctx->srv_conf = ngx_pcalloc(cf->pool,\n                                sizeof(void *) * ngx_stream_max_module);\n    if (ctx->srv_conf == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    ctx->srv_conf[ngx_stream_upstream_module.ctx_index] = uscf;\n\n    uscf->srv_conf = ctx->srv_conf;\n\n    for (m = 0; cf->cycle->modules[m]; m++) {\n        if (cf->cycle->modules[m]->type != NGX_STREAM_MODULE) {\n            continue;\n        }\n\n        module = cf->cycle->modules[m]->ctx;\n\n        if (module->create_srv_conf) {\n            mconf = module->create_srv_conf(cf);\n            if (mconf == NULL) {\n                return NGX_CONF_ERROR;\n            }\n\n            ctx->srv_conf[cf->cycle->modules[m]->ctx_index] = mconf;\n        }\n    }\n\n    uscf->servers = ngx_array_create(cf->pool, 4,\n                                     sizeof(ngx_stream_upstream_server_t));\n    if (uscf->servers == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n\n    /* parse inside upstream{} */\n\n    pcf = *cf;\n    cf->ctx = ctx;\n    cf->cmd_type = NGX_STREAM_UPS_CONF;\n\n    rv = ngx_conf_parse(cf, NULL);\n\n    *cf = pcf;\n\n    if (rv != NGX_CONF_OK) {\n        return rv;\n    }\n\n    if (uscf->servers->nelts == 0) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"no servers are inside upstream\");\n        return NGX_CONF_ERROR;\n    }\n\n    return rv;\n}\n\n\nstatic char *\nngx_stream_upstream_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_stream_upstream_srv_conf_t  *uscf = conf;\n\n    time_t                         fail_timeout;\n    ngx_str_t                     *value, s;\n    ngx_url_t                      u;\n    ngx_int_t                      weight, max_conns, max_fails;\n    ngx_uint_t                     i;\n    ngx_stream_upstream_server_t  *us;\n\n    us = ngx_array_push(uscf->servers);\n    if (us == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    ngx_memzero(us, sizeof(ngx_stream_upstream_server_t));\n\n    value = cf->args->elts;\n\n    weight = 1;\n    max_conns = 0;\n    max_fails = 1;\n    fail_timeout = 10;\n\n    for (i = 2; i < cf->args->nelts; i++) {\n\n        if (ngx_strncmp(value[i].data, \"weight=\", 7) == 0) {\n\n            if (!(uscf->flags & NGX_STREAM_UPSTREAM_WEIGHT)) {\n                goto not_supported;\n            }\n\n            weight = ngx_atoi(&value[i].data[7], value[i].len - 7);\n\n            if (weight == NGX_ERROR || weight == 0) {\n                goto invalid;\n            }\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"max_conns=\", 10) == 0) {\n\n            if (!(uscf->flags & NGX_STREAM_UPSTREAM_MAX_CONNS)) {\n                goto not_supported;\n            }\n\n            max_conns = ngx_atoi(&value[i].data[10], value[i].len - 10);\n\n            if (max_conns == NGX_ERROR) {\n                goto invalid;\n            }\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"max_fails=\", 10) == 0) {\n\n            if (!(uscf->flags & NGX_STREAM_UPSTREAM_MAX_FAILS)) {\n                goto not_supported;\n            }\n\n            max_fails = ngx_atoi(&value[i].data[10], value[i].len - 10);\n\n            if (max_fails == NGX_ERROR) {\n                goto invalid;\n            }\n\n            continue;\n        }\n\n        if (ngx_strncmp(value[i].data, \"fail_timeout=\", 13) == 0) {\n\n            if (!(uscf->flags & NGX_STREAM_UPSTREAM_FAIL_TIMEOUT)) {\n                goto not_supported;\n            }\n\n            s.len = value[i].len - 13;\n            s.data = &value[i].data[13];\n\n            fail_timeout = ngx_parse_time(&s, 1);\n\n            if (fail_timeout == (time_t) NGX_ERROR) {\n                goto invalid;\n            }\n\n            continue;\n        }\n\n        if (ngx_strcmp(value[i].data, \"backup\") == 0) {\n\n            if (!(uscf->flags & NGX_STREAM_UPSTREAM_BACKUP)) {\n                goto not_supported;\n            }\n\n            us->backup = 1;\n\n            continue;\n        }\n\n        if (ngx_strcmp(value[i].data, \"down\") == 0) {\n\n            if (!(uscf->flags & NGX_STREAM_UPSTREAM_DOWN)) {\n                goto not_supported;\n            }\n\n            us->down = 1;\n\n            continue;\n        }\n\n        goto invalid;\n    }\n\n    ngx_memzero(&u, sizeof(ngx_url_t));\n\n    u.url = value[1];\n\n    if (ngx_parse_url(cf->pool, &u) != NGX_OK) {\n        if (u.err) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"%s in upstream \\\"%V\\\"\", u.err, &u.url);\n        }\n\n        return NGX_CONF_ERROR;\n    }\n\n    if (u.no_port) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"no port in upstream \\\"%V\\\"\", &u.url);\n        return NGX_CONF_ERROR;\n    }\n\n    us->name = u.url;\n    us->addrs = u.addrs;\n    us->naddrs = u.naddrs;\n    us->weight = weight;\n    us->max_conns = max_conns;\n    us->max_fails = max_fails;\n    us->fail_timeout = fail_timeout;\n\n    return NGX_CONF_OK;\n\ninvalid:\n\n    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                       \"invalid parameter \\\"%V\\\"\", &value[i]);\n\n    return NGX_CONF_ERROR;\n\nnot_supported:\n\n    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                       \"balancing method does not support parameter \\\"%V\\\"\",\n                       &value[i]);\n\n    return NGX_CONF_ERROR;\n}\n\n\nngx_stream_upstream_srv_conf_t *\nngx_stream_upstream_add(ngx_conf_t *cf, ngx_url_t *u, ngx_uint_t flags)\n{\n    ngx_uint_t                        i;\n    ngx_stream_upstream_server_t     *us;\n    ngx_stream_upstream_srv_conf_t   *uscf, **uscfp;\n    ngx_stream_upstream_main_conf_t  *umcf;\n\n    if (!(flags & NGX_STREAM_UPSTREAM_CREATE)) {\n\n        if (ngx_parse_url(cf->pool, u) != NGX_OK) {\n            if (u->err) {\n                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                                   \"%s in upstream \\\"%V\\\"\", u->err, &u->url);\n            }\n\n            return NULL;\n        }\n    }\n\n    umcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_upstream_module);\n\n    uscfp = umcf->upstreams.elts;\n\n    for (i = 0; i < umcf->upstreams.nelts; i++) {\n\n        if (uscfp[i]->host.len != u->host.len\n            || ngx_strncasecmp(uscfp[i]->host.data, u->host.data, u->host.len)\n               != 0)\n        {\n            continue;\n        }\n\n        if ((flags & NGX_STREAM_UPSTREAM_CREATE)\n             && (uscfp[i]->flags & NGX_STREAM_UPSTREAM_CREATE))\n        {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"duplicate upstream \\\"%V\\\"\", &u->host);\n            return NULL;\n        }\n\n        if ((uscfp[i]->flags & NGX_STREAM_UPSTREAM_CREATE) && !u->no_port) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"upstream \\\"%V\\\" may not have port %d\",\n                               &u->host, u->port);\n            return NULL;\n        }\n\n        if ((flags & NGX_STREAM_UPSTREAM_CREATE) && !uscfp[i]->no_port) {\n            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                          \"upstream \\\"%V\\\" may not have port %d in %s:%ui\",\n                          &u->host, uscfp[i]->port,\n                          uscfp[i]->file_name, uscfp[i]->line);\n            return NULL;\n        }\n\n        if (uscfp[i]->port != u->port) {\n            continue;\n        }\n\n        if (flags & NGX_STREAM_UPSTREAM_CREATE) {\n            uscfp[i]->flags = flags;\n        }\n\n        return uscfp[i];\n    }\n\n    uscf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_upstream_srv_conf_t));\n    if (uscf == NULL) {\n        return NULL;\n    }\n\n    uscf->flags = flags;\n    uscf->host = u->host;\n    uscf->file_name = cf->conf_file->file.name.data;\n    uscf->line = cf->conf_file->line;\n    uscf->port = u->port;\n    uscf->no_port = u->no_port;\n\n    if (u->naddrs == 1 && (u->port || u->family == AF_UNIX)) {\n        uscf->servers = ngx_array_create(cf->pool, 1,\n                                         sizeof(ngx_stream_upstream_server_t));\n        if (uscf->servers == NULL) {\n            return NULL;\n        }\n\n        us = ngx_array_push(uscf->servers);\n        if (us == NULL) {\n            return NULL;\n        }\n\n        ngx_memzero(us, sizeof(ngx_stream_upstream_server_t));\n\n        us->addrs = u->addrs;\n        us->naddrs = 1;\n    }\n\n    uscfp = ngx_array_push(&umcf->upstreams);\n    if (uscfp == NULL) {\n        return NULL;\n    }\n\n    *uscfp = uscf;\n\n    return uscf;\n}\n\n\nstatic void *\nngx_stream_upstream_create_main_conf(ngx_conf_t *cf)\n{\n    ngx_stream_upstream_main_conf_t  *umcf;\n\n    umcf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_upstream_main_conf_t));\n    if (umcf == NULL) {\n        return NULL;\n    }\n\n    if (ngx_array_init(&umcf->upstreams, cf->pool, 4,\n                       sizeof(ngx_stream_upstream_srv_conf_t *))\n        != NGX_OK)\n    {\n        return NULL;\n    }\n\n    return umcf;\n}\n\n\nstatic char *\nngx_stream_upstream_init_main_conf(ngx_conf_t *cf, void *conf)\n{\n    ngx_stream_upstream_main_conf_t *umcf = conf;\n\n    ngx_uint_t                        i;\n    ngx_stream_upstream_init_pt       init;\n    ngx_stream_upstream_srv_conf_t  **uscfp;\n\n    uscfp = umcf->upstreams.elts;\n\n    for (i = 0; i < umcf->upstreams.nelts; i++) {\n\n        init = uscfp[i]->peer.init_upstream\n                                         ? uscfp[i]->peer.init_upstream\n                                         : ngx_stream_upstream_init_round_robin;\n\n        if (init(cf, uscfp[i]) != NGX_OK) {\n            return NGX_CONF_ERROR;\n        }\n    }\n\n    return NGX_CONF_OK;\n}\n"
  },
  {
    "path": "src/stream/ngx_stream_upstream.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_STREAM_UPSTREAM_H_INCLUDED_\n#define _NGX_STREAM_UPSTREAM_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_stream.h>\n#include <ngx_event_connect.h>\n\n\n#define NGX_STREAM_UPSTREAM_CREATE        0x0001\n#define NGX_STREAM_UPSTREAM_WEIGHT        0x0002\n#define NGX_STREAM_UPSTREAM_MAX_FAILS     0x0004\n#define NGX_STREAM_UPSTREAM_FAIL_TIMEOUT  0x0008\n#define NGX_STREAM_UPSTREAM_DOWN          0x0010\n#define NGX_STREAM_UPSTREAM_BACKUP        0x0020\n#define NGX_STREAM_UPSTREAM_MAX_CONNS     0x0100\n\n\n#define NGX_STREAM_UPSTREAM_NOTIFY_CONNECT     0x1\n\n\ntypedef struct {\n    ngx_array_t                        upstreams;\n                                           /* ngx_stream_upstream_srv_conf_t */\n} ngx_stream_upstream_main_conf_t;\n\n\ntypedef struct ngx_stream_upstream_srv_conf_s  ngx_stream_upstream_srv_conf_t;\n\n\ntypedef ngx_int_t (*ngx_stream_upstream_init_pt)(ngx_conf_t *cf,\n    ngx_stream_upstream_srv_conf_t *us);\ntypedef ngx_int_t (*ngx_stream_upstream_init_peer_pt)(ngx_stream_session_t *s,\n    ngx_stream_upstream_srv_conf_t *us);\n\n\ntypedef struct {\n    ngx_stream_upstream_init_pt        init_upstream;\n    ngx_stream_upstream_init_peer_pt   init;\n    void                              *data;\n} ngx_stream_upstream_peer_t;\n\n\ntypedef struct {\n    ngx_str_t                          name;\n    ngx_addr_t                        *addrs;\n    ngx_uint_t                         naddrs;\n    ngx_uint_t                         weight;\n    ngx_uint_t                         max_conns;\n    ngx_uint_t                         max_fails;\n    time_t                             fail_timeout;\n    ngx_msec_t                         slow_start;\n    ngx_uint_t                         down;\n\n    unsigned                           backup:1;\n\n    NGX_COMPAT_BEGIN(4)\n    NGX_COMPAT_END\n} ngx_stream_upstream_server_t;\n\n\nstruct ngx_stream_upstream_srv_conf_s {\n    ngx_stream_upstream_peer_t         peer;\n    void                             **srv_conf;\n\n    ngx_array_t                       *servers;\n                                              /* ngx_stream_upstream_server_t */\n\n    ngx_uint_t                         flags;\n    ngx_str_t                          host;\n    u_char                            *file_name;\n    ngx_uint_t                         line;\n    in_port_t                          port;\n    ngx_uint_t                         no_port;  /* unsigned no_port:1 */\n\n#if (NGX_STREAM_UPSTREAM_ZONE)\n    ngx_shm_zone_t                    *shm_zone;\n#endif\n};\n\n\ntypedef struct {\n    ngx_msec_t                         response_time;\n    ngx_msec_t                         connect_time;\n    ngx_msec_t                         first_byte_time;\n    off_t                              bytes_sent;\n    off_t                              bytes_received;\n\n    ngx_str_t                         *peer;\n} ngx_stream_upstream_state_t;\n\n\ntypedef struct {\n    ngx_str_t                          host;\n    in_port_t                          port;\n    ngx_uint_t                         no_port; /* unsigned no_port:1 */\n\n    ngx_uint_t                         naddrs;\n    ngx_resolver_addr_t               *addrs;\n\n    struct sockaddr                   *sockaddr;\n    socklen_t                          socklen;\n    ngx_str_t                          name;\n\n    ngx_resolver_ctx_t                *ctx;\n} ngx_stream_upstream_resolved_t;\n\n\ntypedef struct {\n    ngx_peer_connection_t              peer;\n\n    ngx_buf_t                          downstream_buf;\n    ngx_buf_t                          upstream_buf;\n\n    ngx_chain_t                       *free;\n    ngx_chain_t                       *upstream_out;\n    ngx_chain_t                       *upstream_busy;\n    ngx_chain_t                       *downstream_out;\n    ngx_chain_t                       *downstream_busy;\n\n    off_t                              received;\n    time_t                             start_sec;\n    ngx_uint_t                         requests;\n    ngx_uint_t                         responses;\n    ngx_msec_t                         start_time;\n\n    size_t                             upload_rate;\n    size_t                             download_rate;\n\n    ngx_str_t                          ssl_name;\n\n    ngx_stream_upstream_srv_conf_t    *upstream;\n    ngx_stream_upstream_resolved_t    *resolved;\n    ngx_stream_upstream_state_t       *state;\n    unsigned                           connected:1;\n    unsigned                           proxy_protocol:1;\n    unsigned                           half_closed:1;\n\n#if (T_NGX_MULTI_UPSTREAM)\n    unsigned                           multi:1;\n#endif\n} ngx_stream_upstream_t;\n\n\nngx_stream_upstream_srv_conf_t *ngx_stream_upstream_add(ngx_conf_t *cf,\n    ngx_url_t *u, ngx_uint_t flags);\n\n\n#define ngx_stream_conf_upstream_srv_conf(uscf, module)                       \\\n    uscf->srv_conf[module.ctx_index]\n\n\nextern ngx_module_t  ngx_stream_upstream_module;\n\n\n#endif /* _NGX_STREAM_UPSTREAM_H_INCLUDED_ */\n"
  },
  {
    "path": "src/stream/ngx_stream_upstream_hash_module.c",
    "content": "\n/*\n * Copyright (C) Roman Arutyunyan\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_stream.h>\n\n\ntypedef struct {\n    uint32_t                              hash;\n    ngx_str_t                            *server;\n} ngx_stream_upstream_chash_point_t;\n\n\ntypedef struct {\n    ngx_uint_t                            number;\n    ngx_stream_upstream_chash_point_t     point[1];\n} ngx_stream_upstream_chash_points_t;\n\n\ntypedef struct {\n    ngx_stream_complex_value_t            key;\n    ngx_stream_upstream_chash_points_t   *points;\n} ngx_stream_upstream_hash_srv_conf_t;\n\n\ntypedef struct {\n    /* the round robin data must be first */\n    ngx_stream_upstream_rr_peer_data_t    rrp;\n    ngx_stream_upstream_hash_srv_conf_t  *conf;\n    ngx_str_t                             key;\n    ngx_uint_t                            tries;\n    ngx_uint_t                            rehash;\n    uint32_t                              hash;\n    ngx_event_get_peer_pt                 get_rr_peer;\n} ngx_stream_upstream_hash_peer_data_t;\n\n\nstatic ngx_int_t ngx_stream_upstream_init_hash(ngx_conf_t *cf,\n    ngx_stream_upstream_srv_conf_t *us);\nstatic ngx_int_t ngx_stream_upstream_init_hash_peer(ngx_stream_session_t *s,\n    ngx_stream_upstream_srv_conf_t *us);\nstatic ngx_int_t ngx_stream_upstream_get_hash_peer(ngx_peer_connection_t *pc,\n    void *data);\n\nstatic ngx_int_t ngx_stream_upstream_init_chash(ngx_conf_t *cf,\n    ngx_stream_upstream_srv_conf_t *us);\nstatic int ngx_libc_cdecl\n    ngx_stream_upstream_chash_cmp_points(const void *one, const void *two);\nstatic ngx_uint_t ngx_stream_upstream_find_chash_point(\n    ngx_stream_upstream_chash_points_t *points, uint32_t hash);\nstatic ngx_int_t ngx_stream_upstream_init_chash_peer(ngx_stream_session_t *s,\n    ngx_stream_upstream_srv_conf_t *us);\nstatic ngx_int_t ngx_stream_upstream_get_chash_peer(ngx_peer_connection_t *pc,\n    void *data);\n\nstatic void *ngx_stream_upstream_hash_create_conf(ngx_conf_t *cf);\nstatic char *ngx_stream_upstream_hash(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n\n\nstatic ngx_command_t  ngx_stream_upstream_hash_commands[] = {\n\n    { ngx_string(\"hash\"),\n      NGX_STREAM_UPS_CONF|NGX_CONF_TAKE12,\n      ngx_stream_upstream_hash,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      0,\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_stream_module_t  ngx_stream_upstream_hash_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    NULL,                                  /* postconfiguration */\n\n    NULL,                                  /* create main configuration */\n    NULL,                                  /* init main configuration */\n\n    ngx_stream_upstream_hash_create_conf,  /* create server configuration */\n    NULL                                   /* merge server configuration */\n};\n\n\nngx_module_t  ngx_stream_upstream_hash_module = {\n    NGX_MODULE_V1,\n    &ngx_stream_upstream_hash_module_ctx,  /* module context */\n    ngx_stream_upstream_hash_commands,     /* module directives */\n    NGX_STREAM_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_stream_upstream_init_hash(ngx_conf_t *cf,\n    ngx_stream_upstream_srv_conf_t *us)\n{\n    if (ngx_stream_upstream_init_round_robin(cf, us) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    us->peer.init = ngx_stream_upstream_init_hash_peer;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_upstream_init_hash_peer(ngx_stream_session_t *s,\n    ngx_stream_upstream_srv_conf_t *us)\n{\n    ngx_stream_upstream_hash_srv_conf_t   *hcf;\n    ngx_stream_upstream_hash_peer_data_t  *hp;\n\n    hp = ngx_palloc(s->connection->pool,\n                    sizeof(ngx_stream_upstream_hash_peer_data_t));\n    if (hp == NULL) {\n        return NGX_ERROR;\n    }\n\n    s->upstream->peer.data = &hp->rrp;\n\n    if (ngx_stream_upstream_init_round_robin_peer(s, us) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    s->upstream->peer.get = ngx_stream_upstream_get_hash_peer;\n\n    hcf = ngx_stream_conf_upstream_srv_conf(us,\n                                            ngx_stream_upstream_hash_module);\n\n    if (ngx_stream_complex_value(s, &hcf->key, &hp->key) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,\n                   \"upstream hash key:\\\"%V\\\"\", &hp->key);\n\n    hp->conf = hcf;\n    hp->tries = 0;\n    hp->rehash = 0;\n    hp->hash = 0;\n    hp->get_rr_peer = ngx_stream_upstream_get_round_robin_peer;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_upstream_get_hash_peer(ngx_peer_connection_t *pc, void *data)\n{\n    ngx_stream_upstream_hash_peer_data_t *hp = data;\n\n    time_t                          now;\n    u_char                          buf[NGX_INT_T_LEN];\n    size_t                          size;\n    uint32_t                        hash;\n    ngx_int_t                       w;\n    uintptr_t                       m;\n    ngx_uint_t                      n, p;\n    ngx_stream_upstream_rr_peer_t  *peer;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0,\n                   \"get hash peer, try: %ui\", pc->tries);\n\n    ngx_stream_upstream_rr_peers_rlock(hp->rrp.peers);\n\n    if (hp->tries > 20 || hp->rrp.peers->single || hp->key.len == 0) {\n        ngx_stream_upstream_rr_peers_unlock(hp->rrp.peers);\n        return hp->get_rr_peer(pc, &hp->rrp);\n    }\n\n    now = ngx_time();\n\n    pc->connection = NULL;\n\n    for ( ;; ) {\n\n        /*\n         * Hash expression is compatible with Cache::Memcached:\n         * ((crc32([REHASH] KEY) >> 16) & 0x7fff) + PREV_HASH\n         * with REHASH omitted at the first iteration.\n         */\n\n        ngx_crc32_init(hash);\n\n        if (hp->rehash > 0) {\n            size = ngx_sprintf(buf, \"%ui\", hp->rehash) - buf;\n            ngx_crc32_update(&hash, buf, size);\n        }\n\n        ngx_crc32_update(&hash, hp->key.data, hp->key.len);\n        ngx_crc32_final(hash);\n\n        hash = (hash >> 16) & 0x7fff;\n\n        hp->hash += hash;\n        hp->rehash++;\n\n        w = hp->hash % hp->rrp.peers->total_weight;\n        peer = hp->rrp.peers->peer;\n        p = 0;\n\n        while (w >= peer->weight) {\n            w -= peer->weight;\n            peer = peer->next;\n            p++;\n        }\n\n        n = p / (8 * sizeof(uintptr_t));\n        m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t));\n\n        if (hp->rrp.tried[n] & m) {\n            goto next;\n        }\n\n        ngx_stream_upstream_rr_peer_lock(hp->rrp.peers, peer);\n\n        ngx_log_debug2(NGX_LOG_DEBUG_STREAM, pc->log, 0,\n                       \"get hash peer, value:%uD, peer:%ui\", hp->hash, p);\n\n        if (peer->down) {\n            ngx_stream_upstream_rr_peer_unlock(hp->rrp.peers, peer);\n            goto next;\n        }\n\n        if (peer->max_fails\n            && peer->fails >= peer->max_fails\n            && now - peer->checked <= peer->fail_timeout)\n        {\n            ngx_stream_upstream_rr_peer_unlock(hp->rrp.peers, peer);\n            goto next;\n        }\n\n        if (peer->max_conns && peer->conns >= peer->max_conns) {\n            ngx_stream_upstream_rr_peer_unlock(hp->rrp.peers, peer);\n            goto next;\n        }\n\n        break;\n\n    next:\n\n        if (++hp->tries > 20) {\n            ngx_stream_upstream_rr_peers_unlock(hp->rrp.peers);\n            return hp->get_rr_peer(pc, &hp->rrp);\n        }\n    }\n\n    hp->rrp.current = peer;\n\n    pc->sockaddr = peer->sockaddr;\n    pc->socklen = peer->socklen;\n    pc->name = &peer->name;\n\n    peer->conns++;\n\n    if (now - peer->checked > peer->fail_timeout) {\n        peer->checked = now;\n    }\n\n    ngx_stream_upstream_rr_peer_unlock(hp->rrp.peers, peer);\n    ngx_stream_upstream_rr_peers_unlock(hp->rrp.peers);\n\n    hp->rrp.tried[n] |= m;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_upstream_init_chash(ngx_conf_t *cf,\n    ngx_stream_upstream_srv_conf_t *us)\n{\n    u_char                               *host, *port, c;\n    size_t                                host_len, port_len, size;\n    uint32_t                              hash, base_hash;\n    ngx_str_t                            *server;\n    ngx_uint_t                            npoints, i, j;\n    ngx_stream_upstream_rr_peer_t        *peer;\n    ngx_stream_upstream_rr_peers_t       *peers;\n    ngx_stream_upstream_chash_points_t   *points;\n    ngx_stream_upstream_hash_srv_conf_t  *hcf;\n    union {\n        uint32_t                          value;\n        u_char                            byte[4];\n    } prev_hash;\n\n    if (ngx_stream_upstream_init_round_robin(cf, us) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    us->peer.init = ngx_stream_upstream_init_chash_peer;\n\n    peers = us->peer.data;\n    npoints = peers->total_weight * 160;\n\n    size = sizeof(ngx_stream_upstream_chash_points_t)\n           + sizeof(ngx_stream_upstream_chash_point_t) * (npoints - 1);\n\n    points = ngx_palloc(cf->pool, size);\n    if (points == NULL) {\n        return NGX_ERROR;\n    }\n\n    points->number = 0;\n\n    for (peer = peers->peer; peer; peer = peer->next) {\n        server = &peer->server;\n\n        /*\n         * Hash expression is compatible with Cache::Memcached::Fast:\n         * crc32(HOST \\0 PORT PREV_HASH).\n         */\n\n        if (server->len >= 5\n            && ngx_strncasecmp(server->data, (u_char *) \"unix:\", 5) == 0)\n        {\n            host = server->data + 5;\n            host_len = server->len - 5;\n            port = NULL;\n            port_len = 0;\n            goto done;\n        }\n\n        for (j = 0; j < server->len; j++) {\n            c = server->data[server->len - j - 1];\n\n            if (c == ':') {\n                host = server->data;\n                host_len = server->len - j - 1;\n                port = server->data + server->len - j;\n                port_len = j;\n                goto done;\n            }\n\n            if (c < '0' || c > '9') {\n                break;\n            }\n        }\n\n        host = server->data;\n        host_len = server->len;\n        port = NULL;\n        port_len = 0;\n\n    done:\n\n        ngx_crc32_init(base_hash);\n        ngx_crc32_update(&base_hash, host, host_len);\n        ngx_crc32_update(&base_hash, (u_char *) \"\", 1);\n        ngx_crc32_update(&base_hash, port, port_len);\n\n        prev_hash.value = 0;\n        npoints = peer->weight * 160;\n\n        for (j = 0; j < npoints; j++) {\n            hash = base_hash;\n\n            ngx_crc32_update(&hash, prev_hash.byte, 4);\n            ngx_crc32_final(hash);\n\n            points->point[points->number].hash = hash;\n            points->point[points->number].server = server;\n            points->number++;\n\n#if (NGX_HAVE_LITTLE_ENDIAN)\n            prev_hash.value = hash;\n#else\n            prev_hash.byte[0] = (u_char) (hash & 0xff);\n            prev_hash.byte[1] = (u_char) ((hash >> 8) & 0xff);\n            prev_hash.byte[2] = (u_char) ((hash >> 16) & 0xff);\n            prev_hash.byte[3] = (u_char) ((hash >> 24) & 0xff);\n#endif\n        }\n    }\n\n    ngx_qsort(points->point,\n              points->number,\n              sizeof(ngx_stream_upstream_chash_point_t),\n              ngx_stream_upstream_chash_cmp_points);\n\n    for (i = 0, j = 1; j < points->number; j++) {\n        if (points->point[i].hash != points->point[j].hash) {\n            points->point[++i] = points->point[j];\n        }\n    }\n\n    points->number = i + 1;\n\n    hcf = ngx_stream_conf_upstream_srv_conf(us,\n                                            ngx_stream_upstream_hash_module);\n    hcf->points = points;\n\n    return NGX_OK;\n}\n\n\nstatic int ngx_libc_cdecl\nngx_stream_upstream_chash_cmp_points(const void *one, const void *two)\n{\n    ngx_stream_upstream_chash_point_t *first =\n                                     (ngx_stream_upstream_chash_point_t *) one;\n    ngx_stream_upstream_chash_point_t *second =\n                                     (ngx_stream_upstream_chash_point_t *) two;\n\n    if (first->hash < second->hash) {\n        return -1;\n\n    } else if (first->hash > second->hash) {\n        return 1;\n\n    } else {\n        return 0;\n    }\n}\n\n\nstatic ngx_uint_t\nngx_stream_upstream_find_chash_point(ngx_stream_upstream_chash_points_t *points,\n    uint32_t hash)\n{\n    ngx_uint_t                          i, j, k;\n    ngx_stream_upstream_chash_point_t  *point;\n\n    /* find first point >= hash */\n\n    point = &points->point[0];\n\n    i = 0;\n    j = points->number;\n\n    while (i < j) {\n        k = (i + j) / 2;\n\n        if (hash > point[k].hash) {\n            i = k + 1;\n\n        } else if (hash < point[k].hash) {\n            j = k;\n\n        } else {\n            return k;\n        }\n    }\n\n    return i;\n}\n\n\nstatic ngx_int_t\nngx_stream_upstream_init_chash_peer(ngx_stream_session_t *s,\n    ngx_stream_upstream_srv_conf_t *us)\n{\n    uint32_t                               hash;\n    ngx_stream_upstream_hash_srv_conf_t   *hcf;\n    ngx_stream_upstream_hash_peer_data_t  *hp;\n\n    if (ngx_stream_upstream_init_hash_peer(s, us) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    s->upstream->peer.get = ngx_stream_upstream_get_chash_peer;\n\n    hp = s->upstream->peer.data;\n    hcf = ngx_stream_conf_upstream_srv_conf(us,\n                                            ngx_stream_upstream_hash_module);\n\n    hash = ngx_crc32_long(hp->key.data, hp->key.len);\n\n    ngx_stream_upstream_rr_peers_rlock(hp->rrp.peers);\n\n    hp->hash = ngx_stream_upstream_find_chash_point(hcf->points, hash);\n\n    ngx_stream_upstream_rr_peers_unlock(hp->rrp.peers);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_upstream_get_chash_peer(ngx_peer_connection_t *pc, void *data)\n{\n    ngx_stream_upstream_hash_peer_data_t *hp = data;\n\n    time_t                                now;\n    intptr_t                              m;\n    ngx_str_t                            *server;\n    ngx_int_t                             total;\n    ngx_uint_t                            i, n, best_i;\n    ngx_stream_upstream_rr_peer_t        *peer, *best;\n    ngx_stream_upstream_chash_point_t    *point;\n    ngx_stream_upstream_chash_points_t   *points;\n    ngx_stream_upstream_hash_srv_conf_t  *hcf;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0,\n                   \"get consistent hash peer, try: %ui\", pc->tries);\n\n    ngx_stream_upstream_rr_peers_wlock(hp->rrp.peers);\n\n    if (hp->tries > 20 || hp->rrp.peers->single || hp->key.len == 0) {\n        ngx_stream_upstream_rr_peers_unlock(hp->rrp.peers);\n        return hp->get_rr_peer(pc, &hp->rrp);\n    }\n\n    pc->connection = NULL;\n\n    now = ngx_time();\n    hcf = hp->conf;\n\n    points = hcf->points;\n    point = &points->point[0];\n\n    for ( ;; ) {\n        server = point[hp->hash % points->number].server;\n\n        ngx_log_debug2(NGX_LOG_DEBUG_STREAM, pc->log, 0,\n                       \"consistent hash peer:%uD, server:\\\"%V\\\"\",\n                       hp->hash, server);\n\n        best = NULL;\n        best_i = 0;\n        total = 0;\n\n        for (peer = hp->rrp.peers->peer, i = 0;\n             peer;\n             peer = peer->next, i++)\n        {\n            n = i / (8 * sizeof(uintptr_t));\n            m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));\n\n            if (hp->rrp.tried[n] & m) {\n                continue;\n            }\n\n            if (peer->down) {\n                continue;\n            }\n\n            if (peer->max_fails\n                && peer->fails >= peer->max_fails\n                && now - peer->checked <= peer->fail_timeout)\n            {\n                continue;\n            }\n\n            if (peer->max_conns && peer->conns >= peer->max_conns) {\n                continue;\n            }\n\n            if (peer->server.len != server->len\n                || ngx_strncmp(peer->server.data, server->data, server->len)\n                   != 0)\n            {\n                continue;\n            }\n\n            peer->current_weight += peer->effective_weight;\n            total += peer->effective_weight;\n\n            if (peer->effective_weight < peer->weight) {\n                peer->effective_weight++;\n            }\n\n            if (best == NULL || peer->current_weight > best->current_weight) {\n                best = peer;\n                best_i = i;\n            }\n        }\n\n        if (best) {\n            best->current_weight -= total;\n            break;\n        }\n\n        hp->hash++;\n        hp->tries++;\n\n        if (hp->tries > 20) {\n            ngx_stream_upstream_rr_peers_unlock(hp->rrp.peers);\n            return hp->get_rr_peer(pc, &hp->rrp);\n        }\n    }\n\n    hp->rrp.current = best;\n\n    pc->sockaddr = best->sockaddr;\n    pc->socklen = best->socklen;\n    pc->name = &best->name;\n\n    best->conns++;\n\n    if (now - best->checked > best->fail_timeout) {\n        best->checked = now;\n    }\n\n    ngx_stream_upstream_rr_peers_unlock(hp->rrp.peers);\n\n    n = best_i / (8 * sizeof(uintptr_t));\n    m = (uintptr_t) 1 << best_i % (8 * sizeof(uintptr_t));\n\n    hp->rrp.tried[n] |= m;\n\n    return NGX_OK;\n}\n\n\nstatic void *\nngx_stream_upstream_hash_create_conf(ngx_conf_t *cf)\n{\n    ngx_stream_upstream_hash_srv_conf_t  *conf;\n\n    conf = ngx_palloc(cf->pool, sizeof(ngx_stream_upstream_hash_srv_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    conf->points = NULL;\n\n    return conf;\n}\n\n\nstatic char *\nngx_stream_upstream_hash(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_stream_upstream_hash_srv_conf_t  *hcf = conf;\n\n    ngx_str_t                           *value;\n    ngx_stream_upstream_srv_conf_t      *uscf;\n    ngx_stream_compile_complex_value_t   ccv;\n\n    value = cf->args->elts;\n\n    ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t));\n\n    ccv.cf = cf;\n    ccv.value = &value[1];\n    ccv.complex_value = &hcf->key;\n\n    if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) {\n        return NGX_CONF_ERROR;\n    }\n\n    uscf = ngx_stream_conf_get_module_srv_conf(cf, ngx_stream_upstream_module);\n\n    if (uscf->peer.init_upstream) {\n        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                           \"load balancing method redefined\");\n    }\n\n    uscf->flags = NGX_STREAM_UPSTREAM_CREATE\n                  |NGX_STREAM_UPSTREAM_WEIGHT\n                  |NGX_STREAM_UPSTREAM_MAX_CONNS\n                  |NGX_STREAM_UPSTREAM_MAX_FAILS\n                  |NGX_STREAM_UPSTREAM_FAIL_TIMEOUT\n                  |NGX_STREAM_UPSTREAM_DOWN;\n\n    if (cf->args->nelts == 2) {\n        uscf->peer.init_upstream = ngx_stream_upstream_init_hash;\n\n    } else if (ngx_strcmp(value[2].data, \"consistent\") == 0) {\n        uscf->peer.init_upstream = ngx_stream_upstream_init_chash;\n\n    } else {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid parameter \\\"%V\\\"\", &value[2]);\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n"
  },
  {
    "path": "src/stream/ngx_stream_upstream_least_conn_module.c",
    "content": "\n/*\n * Copyright (C) Maxim Dounin\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_stream.h>\n\n\nstatic ngx_int_t ngx_stream_upstream_init_least_conn_peer(\n    ngx_stream_session_t *s, ngx_stream_upstream_srv_conf_t *us);\nstatic ngx_int_t ngx_stream_upstream_get_least_conn_peer(\n    ngx_peer_connection_t *pc, void *data);\nstatic char *ngx_stream_upstream_least_conn(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n\n\nstatic ngx_command_t  ngx_stream_upstream_least_conn_commands[] = {\n\n    { ngx_string(\"least_conn\"),\n      NGX_STREAM_UPS_CONF|NGX_CONF_NOARGS,\n      ngx_stream_upstream_least_conn,\n      0,\n      0,\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_stream_module_t  ngx_stream_upstream_least_conn_module_ctx = {\n    NULL,                                    /* preconfiguration */\n    NULL,                                    /* 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\n\nngx_module_t  ngx_stream_upstream_least_conn_module = {\n    NGX_MODULE_V1,\n    &ngx_stream_upstream_least_conn_module_ctx, /* module context */\n    ngx_stream_upstream_least_conn_commands, /* module directives */\n    NGX_STREAM_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_stream_upstream_init_least_conn(ngx_conf_t *cf,\n    ngx_stream_upstream_srv_conf_t *us)\n{\n    ngx_log_debug0(NGX_LOG_DEBUG_STREAM, cf->log, 0,\n                   \"init least conn\");\n\n    if (ngx_stream_upstream_init_round_robin(cf, us) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    us->peer.init = ngx_stream_upstream_init_least_conn_peer;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_upstream_init_least_conn_peer(ngx_stream_session_t *s,\n    ngx_stream_upstream_srv_conf_t *us)\n{\n    ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,\n                   \"init least conn peer\");\n\n    if (ngx_stream_upstream_init_round_robin_peer(s, us) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    s->upstream->peer.get = ngx_stream_upstream_get_least_conn_peer;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_upstream_get_least_conn_peer(ngx_peer_connection_t *pc, void *data)\n{\n    ngx_stream_upstream_rr_peer_data_t *rrp = data;\n\n    time_t                           now;\n    uintptr_t                        m;\n    ngx_int_t                        rc, total;\n    ngx_uint_t                       i, n, p, many;\n    ngx_stream_upstream_rr_peer_t   *peer, *best;\n    ngx_stream_upstream_rr_peers_t  *peers;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0,\n                   \"get least conn peer, try: %ui\", pc->tries);\n\n    if (rrp->peers->single) {\n        return ngx_stream_upstream_get_round_robin_peer(pc, rrp);\n    }\n\n    pc->connection = NULL;\n\n    now = ngx_time();\n\n    peers = rrp->peers;\n\n    ngx_stream_upstream_rr_peers_wlock(peers);\n\n    best = NULL;\n    total = 0;\n\n#if (NGX_SUPPRESS_WARN)\n    many = 0;\n    p = 0;\n#endif\n\n    for (peer = peers->peer, i = 0;\n         peer;\n         peer = peer->next, i++)\n    {\n        n = i / (8 * sizeof(uintptr_t));\n        m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));\n\n        if (rrp->tried[n] & m) {\n            continue;\n        }\n\n        if (peer->down) {\n            continue;\n        }\n\n        if (peer->max_fails\n            && peer->fails >= peer->max_fails\n            && now - peer->checked <= peer->fail_timeout)\n        {\n            continue;\n        }\n\n        if (peer->max_conns && peer->conns >= peer->max_conns) {\n            continue;\n        }\n\n        /*\n         * select peer with least number of connections; if there are\n         * multiple peers with the same number of connections, select\n         * based on round-robin\n         */\n\n        if (best == NULL\n            || peer->conns * best->weight < best->conns * peer->weight)\n        {\n            best = peer;\n            many = 0;\n            p = i;\n\n        } else if (peer->conns * best->weight == best->conns * peer->weight) {\n            many = 1;\n        }\n    }\n\n    if (best == NULL) {\n        ngx_log_debug0(NGX_LOG_DEBUG_STREAM, pc->log, 0,\n                       \"get least conn peer, no peer found\");\n\n        goto failed;\n    }\n\n    if (many) {\n        ngx_log_debug0(NGX_LOG_DEBUG_STREAM, pc->log, 0,\n                       \"get least conn peer, many\");\n\n        for (peer = best, i = p;\n             peer;\n             peer = peer->next, i++)\n        {\n            n = i / (8 * sizeof(uintptr_t));\n            m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));\n\n            if (rrp->tried[n] & m) {\n                continue;\n            }\n\n            if (peer->down) {\n                continue;\n            }\n\n            if (peer->conns * best->weight != best->conns * peer->weight) {\n                continue;\n            }\n\n            if (peer->max_fails\n                && peer->fails >= peer->max_fails\n                && now - peer->checked <= peer->fail_timeout)\n            {\n                continue;\n            }\n\n            if (peer->max_conns && peer->conns >= peer->max_conns) {\n                continue;\n            }\n\n            peer->current_weight += peer->effective_weight;\n            total += peer->effective_weight;\n\n            if (peer->effective_weight < peer->weight) {\n                peer->effective_weight++;\n            }\n\n            if (peer->current_weight > best->current_weight) {\n                best = peer;\n                p = i;\n            }\n        }\n    }\n\n    best->current_weight -= total;\n\n    if (now - best->checked > best->fail_timeout) {\n        best->checked = now;\n    }\n\n    pc->sockaddr = best->sockaddr;\n    pc->socklen = best->socklen;\n    pc->name = &best->name;\n\n    best->conns++;\n\n    rrp->current = best;\n\n    n = p / (8 * sizeof(uintptr_t));\n    m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t));\n\n    rrp->tried[n] |= m;\n\n    ngx_stream_upstream_rr_peers_unlock(peers);\n\n    return NGX_OK;\n\nfailed:\n\n    if (peers->next) {\n        ngx_log_debug0(NGX_LOG_DEBUG_STREAM, pc->log, 0,\n                       \"get least conn peer, backup servers\");\n\n        rrp->peers = peers->next;\n\n        n = (rrp->peers->number + (8 * sizeof(uintptr_t) - 1))\n                / (8 * sizeof(uintptr_t));\n\n        for (i = 0; i < n; i++) {\n            rrp->tried[i] = 0;\n        }\n\n        ngx_stream_upstream_rr_peers_unlock(peers);\n\n        rc = ngx_stream_upstream_get_least_conn_peer(pc, rrp);\n\n        if (rc != NGX_BUSY) {\n            return rc;\n        }\n\n        ngx_stream_upstream_rr_peers_wlock(peers);\n    }\n\n    ngx_stream_upstream_rr_peers_unlock(peers);\n\n    pc->name = peers->name;\n\n    return NGX_BUSY;\n}\n\n\nstatic char *\nngx_stream_upstream_least_conn(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_stream_upstream_srv_conf_t  *uscf;\n\n    uscf = ngx_stream_conf_get_module_srv_conf(cf, ngx_stream_upstream_module);\n\n    if (uscf->peer.init_upstream) {\n        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                           \"load balancing method redefined\");\n    }\n\n    uscf->peer.init_upstream = ngx_stream_upstream_init_least_conn;\n\n    uscf->flags = NGX_STREAM_UPSTREAM_CREATE\n                  |NGX_STREAM_UPSTREAM_WEIGHT\n                  |NGX_STREAM_UPSTREAM_MAX_CONNS\n                  |NGX_STREAM_UPSTREAM_MAX_FAILS\n                  |NGX_STREAM_UPSTREAM_FAIL_TIMEOUT\n                  |NGX_STREAM_UPSTREAM_DOWN\n                  |NGX_STREAM_UPSTREAM_BACKUP;\n\n    return NGX_CONF_OK;\n}\n"
  },
  {
    "path": "src/stream/ngx_stream_upstream_random_module.c",
    "content": "\n/*\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_stream.h>\n\n\ntypedef struct {\n    ngx_stream_upstream_rr_peer_t          *peer;\n    ngx_uint_t                              range;\n} ngx_stream_upstream_random_range_t;\n\n\ntypedef struct {\n    ngx_uint_t                              two;\n    ngx_stream_upstream_random_range_t     *ranges;\n} ngx_stream_upstream_random_srv_conf_t;\n\n\ntypedef struct {\n    /* the round robin data must be first */\n    ngx_stream_upstream_rr_peer_data_t      rrp;\n\n    ngx_stream_upstream_random_srv_conf_t  *conf;\n    u_char                                  tries;\n} ngx_stream_upstream_random_peer_data_t;\n\n\nstatic ngx_int_t ngx_stream_upstream_init_random(ngx_conf_t *cf,\n    ngx_stream_upstream_srv_conf_t *us);\nstatic ngx_int_t ngx_stream_upstream_update_random(ngx_pool_t *pool,\n    ngx_stream_upstream_srv_conf_t *us);\n\nstatic ngx_int_t ngx_stream_upstream_init_random_peer(ngx_stream_session_t *s,\n    ngx_stream_upstream_srv_conf_t *us);\nstatic ngx_int_t ngx_stream_upstream_get_random_peer(ngx_peer_connection_t *pc,\n    void *data);\nstatic ngx_int_t ngx_stream_upstream_get_random2_peer(ngx_peer_connection_t *pc,\n    void *data);\nstatic ngx_uint_t ngx_stream_upstream_peek_random_peer(\n    ngx_stream_upstream_rr_peers_t *peers,\n    ngx_stream_upstream_random_peer_data_t *rp);\nstatic void *ngx_stream_upstream_random_create_conf(ngx_conf_t *cf);\nstatic char *ngx_stream_upstream_random(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\n\n\nstatic ngx_command_t  ngx_stream_upstream_random_commands[] = {\n\n    { ngx_string(\"random\"),\n      NGX_STREAM_UPS_CONF|NGX_CONF_NOARGS|NGX_CONF_TAKE12,\n      ngx_stream_upstream_random,\n      NGX_STREAM_SRV_CONF_OFFSET,\n      0,\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_stream_module_t  ngx_stream_upstream_random_module_ctx = {\n    NULL,                                    /* preconfiguration */\n    NULL,                                    /* postconfiguration */\n\n    NULL,                                    /* create main configuration */\n    NULL,                                    /* init main configuration */\n\n    ngx_stream_upstream_random_create_conf,  /* create server configuration */\n    NULL                                     /* merge server configuration */\n};\n\n\nngx_module_t  ngx_stream_upstream_random_module = {\n    NGX_MODULE_V1,\n    &ngx_stream_upstream_random_module_ctx,  /* module context */\n    ngx_stream_upstream_random_commands,     /* module directives */\n    NGX_STREAM_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_stream_upstream_init_random(ngx_conf_t *cf,\n    ngx_stream_upstream_srv_conf_t *us)\n{\n    ngx_log_debug0(NGX_LOG_DEBUG_STREAM, cf->log, 0, \"init random\");\n\n    if (ngx_stream_upstream_init_round_robin(cf, us) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    us->peer.init = ngx_stream_upstream_init_random_peer;\n\n#if (NGX_STREAM_UPSTREAM_ZONE)\n    if (us->shm_zone) {\n        return NGX_OK;\n    }\n#endif\n\n    return ngx_stream_upstream_update_random(cf->pool, us);\n}\n\n\nstatic ngx_int_t\nngx_stream_upstream_update_random(ngx_pool_t *pool,\n    ngx_stream_upstream_srv_conf_t *us)\n{\n    size_t                                  size;\n    ngx_uint_t                              i, total_weight;\n    ngx_stream_upstream_rr_peer_t          *peer;\n    ngx_stream_upstream_rr_peers_t         *peers;\n    ngx_stream_upstream_random_range_t     *ranges;\n    ngx_stream_upstream_random_srv_conf_t  *rcf;\n\n    rcf = ngx_stream_conf_upstream_srv_conf(us,\n                                            ngx_stream_upstream_random_module);\n    peers = us->peer.data;\n\n    size = peers->number * sizeof(ngx_stream_upstream_random_range_t);\n\n    ranges = pool ? ngx_palloc(pool, size) : ngx_alloc(size, ngx_cycle->log);\n    if (ranges == NULL) {\n        return NGX_ERROR;\n    }\n\n    total_weight = 0;\n\n    for (peer = peers->peer, i = 0; peer; peer = peer->next, i++) {\n        ranges[i].peer = peer;\n        ranges[i].range = total_weight;\n        total_weight += peer->weight;\n    }\n\n    rcf->ranges = ranges;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_upstream_init_random_peer(ngx_stream_session_t *s,\n    ngx_stream_upstream_srv_conf_t *us)\n{\n    ngx_stream_upstream_random_srv_conf_t   *rcf;\n    ngx_stream_upstream_random_peer_data_t  *rp;\n\n    ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,\n                   \"init random peer\");\n\n    rcf = ngx_stream_conf_upstream_srv_conf(us,\n                                            ngx_stream_upstream_random_module);\n\n    rp = ngx_palloc(s->connection->pool,\n                    sizeof(ngx_stream_upstream_random_peer_data_t));\n    if (rp == NULL) {\n        return NGX_ERROR;\n    }\n\n    s->upstream->peer.data = &rp->rrp;\n\n    if (ngx_stream_upstream_init_round_robin_peer(s, us) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    if (rcf->two) {\n        s->upstream->peer.get = ngx_stream_upstream_get_random2_peer;\n\n    } else {\n        s->upstream->peer.get = ngx_stream_upstream_get_random_peer;\n    }\n\n    rp->conf = rcf;\n    rp->tries = 0;\n\n    ngx_stream_upstream_rr_peers_rlock(rp->rrp.peers);\n\n#if (NGX_STREAM_UPSTREAM_ZONE)\n    if (rp->rrp.peers->shpool && rcf->ranges == NULL) {\n        if (ngx_stream_upstream_update_random(NULL, us) != NGX_OK) {\n            ngx_stream_upstream_rr_peers_unlock(rp->rrp.peers);\n            return NGX_ERROR;\n        }\n    }\n#endif\n\n    ngx_stream_upstream_rr_peers_unlock(rp->rrp.peers);\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_upstream_get_random_peer(ngx_peer_connection_t *pc, void *data)\n{\n    ngx_stream_upstream_random_peer_data_t  *rp = data;\n\n    time_t                               now;\n    uintptr_t                            m;\n    ngx_uint_t                           i, n;\n    ngx_stream_upstream_rr_peer_t       *peer;\n    ngx_stream_upstream_rr_peers_t      *peers;\n    ngx_stream_upstream_rr_peer_data_t  *rrp;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0,\n                   \"get random peer, try: %ui\", pc->tries);\n\n    rrp = &rp->rrp;\n    peers = rrp->peers;\n\n    ngx_stream_upstream_rr_peers_rlock(peers);\n\n    if (rp->tries > 20 || peers->single) {\n        ngx_stream_upstream_rr_peers_unlock(peers);\n        return ngx_stream_upstream_get_round_robin_peer(pc, rrp);\n    }\n\n    pc->cached = 0;\n    pc->connection = NULL;\n\n    now = ngx_time();\n\n    for ( ;; ) {\n\n        i = ngx_stream_upstream_peek_random_peer(peers, rp);\n\n        peer = rp->conf->ranges[i].peer;\n\n        n = i / (8 * sizeof(uintptr_t));\n        m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));\n\n        if (rrp->tried[n] & m) {\n            goto next;\n        }\n\n        ngx_stream_upstream_rr_peer_lock(peers, peer);\n\n        if (peer->down) {\n            ngx_stream_upstream_rr_peer_unlock(peers, peer);\n            goto next;\n        }\n\n        if (peer->max_fails\n            && peer->fails >= peer->max_fails\n            && now - peer->checked <= peer->fail_timeout)\n        {\n            ngx_stream_upstream_rr_peer_unlock(peers, peer);\n            goto next;\n        }\n\n        if (peer->max_conns && peer->conns >= peer->max_conns) {\n            ngx_stream_upstream_rr_peer_unlock(peers, peer);\n            goto next;\n        }\n\n        break;\n\n    next:\n\n        if (++rp->tries > 20) {\n            ngx_stream_upstream_rr_peers_unlock(peers);\n            return ngx_stream_upstream_get_round_robin_peer(pc, rrp);\n        }\n    }\n\n    rrp->current = peer;\n\n    if (now - peer->checked > peer->fail_timeout) {\n        peer->checked = now;\n    }\n\n    pc->sockaddr = peer->sockaddr;\n    pc->socklen = peer->socklen;\n    pc->name = &peer->name;\n\n    peer->conns++;\n\n    ngx_stream_upstream_rr_peer_unlock(peers, peer);\n    ngx_stream_upstream_rr_peers_unlock(peers);\n\n    rrp->tried[n] |= m;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_upstream_get_random2_peer(ngx_peer_connection_t *pc, void *data)\n{\n    ngx_stream_upstream_random_peer_data_t  *rp = data;\n\n    time_t                               now;\n    uintptr_t                            m;\n    ngx_uint_t                           i, n, p;\n    ngx_stream_upstream_rr_peer_t       *peer, *prev;\n    ngx_stream_upstream_rr_peers_t      *peers;\n    ngx_stream_upstream_rr_peer_data_t  *rrp;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0,\n                   \"get random2 peer, try: %ui\", pc->tries);\n\n    rrp = &rp->rrp;\n    peers = rrp->peers;\n\n    ngx_stream_upstream_rr_peers_wlock(peers);\n\n    if (rp->tries > 20 || peers->single) {\n        ngx_stream_upstream_rr_peers_unlock(peers);\n        return ngx_stream_upstream_get_round_robin_peer(pc, rrp);\n    }\n\n    pc->cached = 0;\n    pc->connection = NULL;\n\n    now = ngx_time();\n\n    prev = NULL;\n\n#if (NGX_SUPPRESS_WARN)\n    p = 0;\n#endif\n\n    for ( ;; ) {\n\n        i = ngx_stream_upstream_peek_random_peer(peers, rp);\n\n        peer = rp->conf->ranges[i].peer;\n\n        if (peer == prev) {\n            goto next;\n        }\n\n        n = i / (8 * sizeof(uintptr_t));\n        m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));\n\n        if (rrp->tried[n] & m) {\n            goto next;\n        }\n\n        if (peer->down) {\n            goto next;\n        }\n\n        if (peer->max_fails\n            && peer->fails >= peer->max_fails\n            && now - peer->checked <= peer->fail_timeout)\n        {\n            goto next;\n        }\n\n        if (peer->max_conns && peer->conns >= peer->max_conns) {\n            goto next;\n        }\n\n        if (prev) {\n            if (peer->conns * prev->weight > prev->conns * peer->weight) {\n                peer = prev;\n                n = p / (8 * sizeof(uintptr_t));\n                m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t));\n            }\n\n            break;\n        }\n\n        prev = peer;\n        p = i;\n\n    next:\n\n        if (++rp->tries > 20) {\n            ngx_stream_upstream_rr_peers_unlock(peers);\n            return ngx_stream_upstream_get_round_robin_peer(pc, rrp);\n        }\n    }\n\n    rrp->current = peer;\n\n    if (now - peer->checked > peer->fail_timeout) {\n        peer->checked = now;\n    }\n\n    pc->sockaddr = peer->sockaddr;\n    pc->socklen = peer->socklen;\n    pc->name = &peer->name;\n\n    peer->conns++;\n\n    ngx_stream_upstream_rr_peers_unlock(peers);\n\n    rrp->tried[n] |= m;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_uint_t\nngx_stream_upstream_peek_random_peer(ngx_stream_upstream_rr_peers_t *peers,\n    ngx_stream_upstream_random_peer_data_t *rp)\n{\n    ngx_uint_t  i, j, k, x;\n\n    x = ngx_random() % peers->total_weight;\n\n    i = 0;\n    j = peers->number;\n\n    while (j - i > 1) {\n        k = (i + j) / 2;\n\n        if (x < rp->conf->ranges[k].range) {\n            j = k;\n\n        } else {\n            i = k;\n        }\n    }\n\n    return i;\n}\n\n\nstatic void *\nngx_stream_upstream_random_create_conf(ngx_conf_t *cf)\n{\n    ngx_stream_upstream_random_srv_conf_t  *conf;\n\n    conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_upstream_random_srv_conf_t));\n    if (conf == NULL) {\n        return NULL;\n    }\n\n    /*\n     * set by ngx_pcalloc():\n     *\n     *     conf->two = 0;\n     */\n\n    return conf;\n}\n\n\nstatic char *\nngx_stream_upstream_random(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ngx_stream_upstream_random_srv_conf_t  *rcf = conf;\n\n    ngx_str_t                       *value;\n    ngx_stream_upstream_srv_conf_t  *uscf;\n\n    uscf = ngx_stream_conf_get_module_srv_conf(cf, ngx_stream_upstream_module);\n\n    if (uscf->peer.init_upstream) {\n        ngx_conf_log_error(NGX_LOG_WARN, cf, 0,\n                           \"load balancing method redefined\");\n    }\n\n    uscf->peer.init_upstream = ngx_stream_upstream_init_random;\n\n    uscf->flags = NGX_STREAM_UPSTREAM_CREATE\n                  |NGX_STREAM_UPSTREAM_WEIGHT\n                  |NGX_STREAM_UPSTREAM_MAX_CONNS\n                  |NGX_STREAM_UPSTREAM_MAX_FAILS\n                  |NGX_STREAM_UPSTREAM_FAIL_TIMEOUT\n                  |NGX_STREAM_UPSTREAM_DOWN;\n\n    if (cf->args->nelts == 1) {\n        return NGX_CONF_OK;\n    }\n\n    value = cf->args->elts;\n\n    if (ngx_strcmp(value[1].data, \"two\") == 0) {\n        rcf->two = 1;\n\n    } else {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid parameter \\\"%V\\\"\", &value[1]);\n        return NGX_CONF_ERROR;\n    }\n\n    if (cf->args->nelts == 2) {\n        return NGX_CONF_OK;\n    }\n\n    if (ngx_strcmp(value[2].data, \"least_conn\") != 0) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid parameter \\\"%V\\\"\", &value[2]);\n        return NGX_CONF_ERROR;\n    }\n\n    return NGX_CONF_OK;\n}\n"
  },
  {
    "path": "src/stream/ngx_stream_upstream_round_robin.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_stream.h>\n\n\n#define ngx_stream_upstream_tries(p) ((p)->tries                              \\\n                                      + ((p)->next ? (p)->next->tries : 0))\n\n\nstatic ngx_stream_upstream_rr_peer_t *ngx_stream_upstream_get_peer(\n    ngx_stream_upstream_rr_peer_data_t *rrp);\nstatic void ngx_stream_upstream_notify_round_robin_peer(\n    ngx_peer_connection_t *pc, void *data, ngx_uint_t state);\n\n#if (NGX_STREAM_SSL)\n\nstatic ngx_int_t ngx_stream_upstream_set_round_robin_peer_session(\n    ngx_peer_connection_t *pc, void *data);\nstatic void ngx_stream_upstream_save_round_robin_peer_session(\n    ngx_peer_connection_t *pc, void *data);\nstatic ngx_int_t ngx_stream_upstream_empty_set_session(\n    ngx_peer_connection_t *pc, void *data);\nstatic void ngx_stream_upstream_empty_save_session(ngx_peer_connection_t *pc,\n    void *data);\n\n#endif\n\n\nngx_int_t\nngx_stream_upstream_init_round_robin(ngx_conf_t *cf,\n    ngx_stream_upstream_srv_conf_t *us)\n{\n    ngx_url_t                        u;\n    ngx_uint_t                       i, j, n, w, t;\n    ngx_stream_upstream_server_t    *server;\n    ngx_stream_upstream_rr_peer_t   *peer, **peerp;\n    ngx_stream_upstream_rr_peers_t  *peers, *backup;\n\n    us->peer.init = ngx_stream_upstream_init_round_robin_peer;\n\n    if (us->servers) {\n        server = us->servers->elts;\n\n        n = 0;\n        w = 0;\n        t = 0;\n\n        for (i = 0; i < us->servers->nelts; i++) {\n            if (server[i].backup) {\n                continue;\n            }\n\n            n += server[i].naddrs;\n            w += server[i].naddrs * server[i].weight;\n\n            if (!server[i].down) {\n                t += server[i].naddrs;\n            }\n        }\n\n        if (n == 0) {\n            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                          \"no servers in upstream \\\"%V\\\" in %s:%ui\",\n                          &us->host, us->file_name, us->line);\n            return NGX_ERROR;\n        }\n\n        peers = ngx_pcalloc(cf->pool, sizeof(ngx_stream_upstream_rr_peers_t));\n        if (peers == NULL) {\n            return NGX_ERROR;\n        }\n\n        peer = ngx_pcalloc(cf->pool, sizeof(ngx_stream_upstream_rr_peer_t) * n);\n        if (peer == NULL) {\n            return NGX_ERROR;\n        }\n\n        peers->single = (n == 1);\n        peers->number = n;\n        peers->weighted = (w != n);\n        peers->total_weight = w;\n        peers->tries = t;\n        peers->name = &us->host;\n\n        n = 0;\n        peerp = &peers->peer;\n\n        for (i = 0; i < us->servers->nelts; i++) {\n            if (server[i].backup) {\n                continue;\n            }\n\n            for (j = 0; j < server[i].naddrs; j++) {\n                peer[n].sockaddr = server[i].addrs[j].sockaddr;\n                peer[n].socklen = server[i].addrs[j].socklen;\n                peer[n].name = server[i].addrs[j].name;\n                peer[n].weight = server[i].weight;\n                peer[n].effective_weight = server[i].weight;\n                peer[n].current_weight = 0;\n                peer[n].max_conns = server[i].max_conns;\n                peer[n].max_fails = server[i].max_fails;\n                peer[n].fail_timeout = server[i].fail_timeout;\n                peer[n].down = server[i].down;\n                peer[n].server = server[i].name;\n\n                *peerp = &peer[n];\n                peerp = &peer[n].next;\n                n++;\n            }\n        }\n\n        us->peer.data = peers;\n\n        /* backup servers */\n\n        n = 0;\n        w = 0;\n        t = 0;\n\n        for (i = 0; i < us->servers->nelts; i++) {\n            if (!server[i].backup) {\n                continue;\n            }\n\n            n += server[i].naddrs;\n            w += server[i].naddrs * server[i].weight;\n\n            if (!server[i].down) {\n                t += server[i].naddrs;\n            }\n        }\n\n        if (n == 0) {\n            return NGX_OK;\n        }\n\n        backup = ngx_pcalloc(cf->pool, sizeof(ngx_stream_upstream_rr_peers_t));\n        if (backup == NULL) {\n            return NGX_ERROR;\n        }\n\n        peer = ngx_pcalloc(cf->pool, sizeof(ngx_stream_upstream_rr_peer_t) * n);\n        if (peer == NULL) {\n            return NGX_ERROR;\n        }\n\n        peers->single = 0;\n        backup->single = 0;\n        backup->number = n;\n        backup->weighted = (w != n);\n        backup->total_weight = w;\n        backup->tries = t;\n        backup->name = &us->host;\n\n        n = 0;\n        peerp = &backup->peer;\n\n        for (i = 0; i < us->servers->nelts; i++) {\n            if (!server[i].backup) {\n                continue;\n            }\n\n            for (j = 0; j < server[i].naddrs; j++) {\n                peer[n].sockaddr = server[i].addrs[j].sockaddr;\n                peer[n].socklen = server[i].addrs[j].socklen;\n                peer[n].name = server[i].addrs[j].name;\n                peer[n].weight = server[i].weight;\n                peer[n].effective_weight = server[i].weight;\n                peer[n].current_weight = 0;\n                peer[n].max_conns = server[i].max_conns;\n                peer[n].max_fails = server[i].max_fails;\n                peer[n].fail_timeout = server[i].fail_timeout;\n                peer[n].down = server[i].down;\n                peer[n].server = server[i].name;\n\n                *peerp = &peer[n];\n                peerp = &peer[n].next;\n                n++;\n            }\n        }\n\n        peers->next = backup;\n\n        return NGX_OK;\n    }\n\n\n    /* an upstream implicitly defined by proxy_pass, etc. */\n\n    if (us->port == 0) {\n        ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                      \"no port in upstream \\\"%V\\\" in %s:%ui\",\n                      &us->host, us->file_name, us->line);\n        return NGX_ERROR;\n    }\n\n    ngx_memzero(&u, sizeof(ngx_url_t));\n\n    u.host = us->host;\n    u.port = us->port;\n\n    if (ngx_inet_resolve_host(cf->pool, &u) != NGX_OK) {\n        if (u.err) {\n            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                          \"%s in upstream \\\"%V\\\" in %s:%ui\",\n                          u.err, &us->host, us->file_name, us->line);\n        }\n\n        return NGX_ERROR;\n    }\n\n    n = u.naddrs;\n\n    peers = ngx_pcalloc(cf->pool, sizeof(ngx_stream_upstream_rr_peers_t));\n    if (peers == NULL) {\n        return NGX_ERROR;\n    }\n\n    peer = ngx_pcalloc(cf->pool, sizeof(ngx_stream_upstream_rr_peer_t) * n);\n    if (peer == NULL) {\n        return NGX_ERROR;\n    }\n\n    peers->single = (n == 1);\n    peers->number = n;\n    peers->weighted = 0;\n    peers->total_weight = n;\n    peers->tries = n;\n    peers->name = &us->host;\n\n    peerp = &peers->peer;\n\n    for (i = 0; i < u.naddrs; i++) {\n        peer[i].sockaddr = u.addrs[i].sockaddr;\n        peer[i].socklen = u.addrs[i].socklen;\n        peer[i].name = u.addrs[i].name;\n        peer[i].weight = 1;\n        peer[i].effective_weight = 1;\n        peer[i].current_weight = 0;\n        peer[i].max_conns = 0;\n        peer[i].max_fails = 1;\n        peer[i].fail_timeout = 10;\n        *peerp = &peer[i];\n        peerp = &peer[i].next;\n    }\n\n    us->peer.data = peers;\n\n    /* implicitly defined upstream has no backup servers */\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_stream_upstream_init_round_robin_peer(ngx_stream_session_t *s,\n    ngx_stream_upstream_srv_conf_t *us)\n{\n    ngx_uint_t                           n;\n    ngx_stream_upstream_rr_peer_data_t  *rrp;\n\n    rrp = s->upstream->peer.data;\n\n    if (rrp == NULL) {\n        rrp = ngx_palloc(s->connection->pool,\n                         sizeof(ngx_stream_upstream_rr_peer_data_t));\n        if (rrp == NULL) {\n            return NGX_ERROR;\n        }\n\n        s->upstream->peer.data = rrp;\n    }\n\n    rrp->peers = us->peer.data;\n    rrp->current = NULL;\n    rrp->config = 0;\n\n    n = rrp->peers->number;\n\n    if (rrp->peers->next && rrp->peers->next->number > n) {\n        n = rrp->peers->next->number;\n    }\n\n    if (n <= 8 * sizeof(uintptr_t)) {\n        rrp->tried = &rrp->data;\n        rrp->data = 0;\n\n    } else {\n        n = (n + (8 * sizeof(uintptr_t) - 1)) / (8 * sizeof(uintptr_t));\n\n        rrp->tried = ngx_pcalloc(s->connection->pool, n * sizeof(uintptr_t));\n        if (rrp->tried == NULL) {\n            return NGX_ERROR;\n        }\n    }\n\n    s->upstream->peer.get = ngx_stream_upstream_get_round_robin_peer;\n    s->upstream->peer.free = ngx_stream_upstream_free_round_robin_peer;\n    s->upstream->peer.notify = ngx_stream_upstream_notify_round_robin_peer;\n    s->upstream->peer.tries = ngx_stream_upstream_tries(rrp->peers);\n#if (NGX_STREAM_SSL)\n    s->upstream->peer.set_session =\n                             ngx_stream_upstream_set_round_robin_peer_session;\n    s->upstream->peer.save_session =\n                             ngx_stream_upstream_save_round_robin_peer_session;\n#endif\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_stream_upstream_create_round_robin_peer(ngx_stream_session_t *s,\n    ngx_stream_upstream_resolved_t *ur)\n{\n    u_char                              *p;\n    size_t                               len;\n    socklen_t                            socklen;\n    ngx_uint_t                           i, n;\n    struct sockaddr                     *sockaddr;\n    ngx_stream_upstream_rr_peer_t       *peer, **peerp;\n    ngx_stream_upstream_rr_peers_t      *peers;\n    ngx_stream_upstream_rr_peer_data_t  *rrp;\n\n    rrp = s->upstream->peer.data;\n\n    if (rrp == NULL) {\n        rrp = ngx_palloc(s->connection->pool,\n                         sizeof(ngx_stream_upstream_rr_peer_data_t));\n        if (rrp == NULL) {\n            return NGX_ERROR;\n        }\n\n        s->upstream->peer.data = rrp;\n    }\n\n    peers = ngx_pcalloc(s->connection->pool,\n                        sizeof(ngx_stream_upstream_rr_peers_t));\n    if (peers == NULL) {\n        return NGX_ERROR;\n    }\n\n    peer = ngx_pcalloc(s->connection->pool,\n                       sizeof(ngx_stream_upstream_rr_peer_t) * ur->naddrs);\n    if (peer == NULL) {\n        return NGX_ERROR;\n    }\n\n    peers->single = (ur->naddrs == 1);\n    peers->number = ur->naddrs;\n    peers->tries = ur->naddrs;\n    peers->name = &ur->host;\n\n    if (ur->sockaddr) {\n        peer[0].sockaddr = ur->sockaddr;\n        peer[0].socklen = ur->socklen;\n        peer[0].name = ur->name;\n        peer[0].weight = 1;\n        peer[0].effective_weight = 1;\n        peer[0].current_weight = 0;\n        peer[0].max_conns = 0;\n        peer[0].max_fails = 1;\n        peer[0].fail_timeout = 10;\n        peers->peer = peer;\n\n    } else {\n        peerp = &peers->peer;\n\n        for (i = 0; i < ur->naddrs; i++) {\n\n            socklen = ur->addrs[i].socklen;\n\n            sockaddr = ngx_palloc(s->connection->pool, socklen);\n            if (sockaddr == NULL) {\n                return NGX_ERROR;\n            }\n\n            ngx_memcpy(sockaddr, ur->addrs[i].sockaddr, socklen);\n            ngx_inet_set_port(sockaddr, ur->port);\n\n            p = ngx_pnalloc(s->connection->pool, NGX_SOCKADDR_STRLEN);\n            if (p == NULL) {\n                return NGX_ERROR;\n            }\n\n            len = ngx_sock_ntop(sockaddr, socklen, p, NGX_SOCKADDR_STRLEN, 1);\n\n            peer[i].sockaddr = sockaddr;\n            peer[i].socklen = socklen;\n            peer[i].name.len = len;\n            peer[i].name.data = p;\n            peer[i].weight = 1;\n            peer[i].effective_weight = 1;\n            peer[i].current_weight = 0;\n            peer[i].max_conns = 0;\n            peer[i].max_fails = 1;\n            peer[i].fail_timeout = 10;\n            *peerp = &peer[i];\n            peerp = &peer[i].next;\n        }\n    }\n\n    rrp->peers = peers;\n    rrp->current = NULL;\n    rrp->config = 0;\n\n    if (rrp->peers->number <= 8 * sizeof(uintptr_t)) {\n        rrp->tried = &rrp->data;\n        rrp->data = 0;\n\n    } else {\n        n = (rrp->peers->number + (8 * sizeof(uintptr_t) - 1))\n                / (8 * sizeof(uintptr_t));\n\n        rrp->tried = ngx_pcalloc(s->connection->pool, n * sizeof(uintptr_t));\n        if (rrp->tried == NULL) {\n            return NGX_ERROR;\n        }\n    }\n\n    s->upstream->peer.get = ngx_stream_upstream_get_round_robin_peer;\n    s->upstream->peer.free = ngx_stream_upstream_free_round_robin_peer;\n    s->upstream->peer.tries = ngx_stream_upstream_tries(rrp->peers);\n#if (NGX_STREAM_SSL)\n    s->upstream->peer.set_session = ngx_stream_upstream_empty_set_session;\n    s->upstream->peer.save_session = ngx_stream_upstream_empty_save_session;\n#endif\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_stream_upstream_get_round_robin_peer(ngx_peer_connection_t *pc, void *data)\n{\n    ngx_stream_upstream_rr_peer_data_t *rrp = data;\n\n    ngx_int_t                        rc;\n    ngx_uint_t                       i, n;\n    ngx_stream_upstream_rr_peer_t   *peer;\n    ngx_stream_upstream_rr_peers_t  *peers;\n\n    ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0,\n                   \"get rr peer, try: %ui\", pc->tries);\n\n    pc->connection = NULL;\n\n    peers = rrp->peers;\n    ngx_stream_upstream_rr_peers_wlock(peers);\n\n    if (peers->single) {\n        peer = peers->peer;\n\n        if (peer->down) {\n            goto failed;\n        }\n\n        if (peer->max_conns && peer->conns >= peer->max_conns) {\n            goto failed;\n        }\n\n        rrp->current = peer;\n\n    } else {\n\n        /* there are several peers */\n\n        peer = ngx_stream_upstream_get_peer(rrp);\n\n        if (peer == NULL) {\n            goto failed;\n        }\n\n        ngx_log_debug2(NGX_LOG_DEBUG_STREAM, pc->log, 0,\n                       \"get rr peer, current: %p %i\",\n                       peer, peer->current_weight);\n    }\n\n    pc->sockaddr = peer->sockaddr;\n    pc->socklen = peer->socklen;\n    pc->name = &peer->name;\n\n    peer->conns++;\n\n    ngx_stream_upstream_rr_peers_unlock(peers);\n\n    return NGX_OK;\n\nfailed:\n\n    if (peers->next) {\n\n        ngx_log_debug0(NGX_LOG_DEBUG_STREAM, pc->log, 0, \"backup servers\");\n\n        rrp->peers = peers->next;\n\n        n = (rrp->peers->number + (8 * sizeof(uintptr_t) - 1))\n                / (8 * sizeof(uintptr_t));\n\n        for (i = 0; i < n; i++) {\n            rrp->tried[i] = 0;\n        }\n\n        ngx_stream_upstream_rr_peers_unlock(peers);\n\n        rc = ngx_stream_upstream_get_round_robin_peer(pc, rrp);\n\n        if (rc != NGX_BUSY) {\n            return rc;\n        }\n\n        ngx_stream_upstream_rr_peers_wlock(peers);\n    }\n\n    ngx_stream_upstream_rr_peers_unlock(peers);\n\n    pc->name = peers->name;\n\n    return NGX_BUSY;\n}\n\n\nstatic ngx_stream_upstream_rr_peer_t *\nngx_stream_upstream_get_peer(ngx_stream_upstream_rr_peer_data_t *rrp)\n{\n    time_t                          now;\n    uintptr_t                       m;\n    ngx_int_t                       total;\n    ngx_uint_t                      i, n, p;\n    ngx_stream_upstream_rr_peer_t  *peer, *best;\n\n    now = ngx_time();\n\n    best = NULL;\n    total = 0;\n\n#if (NGX_SUPPRESS_WARN)\n    p = 0;\n#endif\n\n    for (peer = rrp->peers->peer, i = 0;\n         peer;\n         peer = peer->next, i++)\n    {\n        n = i / (8 * sizeof(uintptr_t));\n        m = (uintptr_t) 1 << i % (8 * sizeof(uintptr_t));\n\n        if (rrp->tried[n] & m) {\n            continue;\n        }\n\n        if (peer->down) {\n            continue;\n        }\n\n        if (peer->max_fails\n            && peer->fails >= peer->max_fails\n            && now - peer->checked <= peer->fail_timeout)\n        {\n            continue;\n        }\n\n        if (peer->max_conns && peer->conns >= peer->max_conns) {\n            continue;\n        }\n\n        peer->current_weight += peer->effective_weight;\n        total += peer->effective_weight;\n\n        if (peer->effective_weight < peer->weight) {\n            peer->effective_weight++;\n        }\n\n        if (best == NULL || peer->current_weight > best->current_weight) {\n            best = peer;\n            p = i;\n        }\n    }\n\n    if (best == NULL) {\n        return NULL;\n    }\n\n    rrp->current = best;\n\n    n = p / (8 * sizeof(uintptr_t));\n    m = (uintptr_t) 1 << p % (8 * sizeof(uintptr_t));\n\n    rrp->tried[n] |= m;\n\n    best->current_weight -= total;\n\n    if (now - best->checked > best->fail_timeout) {\n        best->checked = now;\n    }\n\n    return best;\n}\n\n\nvoid\nngx_stream_upstream_free_round_robin_peer(ngx_peer_connection_t *pc, void *data,\n    ngx_uint_t state)\n{\n    ngx_stream_upstream_rr_peer_data_t  *rrp = data;\n\n    time_t                          now;\n    ngx_stream_upstream_rr_peer_t  *peer;\n\n    ngx_log_debug2(NGX_LOG_DEBUG_STREAM, pc->log, 0,\n                   \"free rr peer %ui %ui\", pc->tries, state);\n\n    peer = rrp->current;\n\n    ngx_stream_upstream_rr_peers_rlock(rrp->peers);\n    ngx_stream_upstream_rr_peer_lock(rrp->peers, peer);\n\n    if (rrp->peers->single) {\n        peer->conns--;\n\n        ngx_stream_upstream_rr_peer_unlock(rrp->peers, peer);\n        ngx_stream_upstream_rr_peers_unlock(rrp->peers);\n\n        pc->tries = 0;\n        return;\n    }\n\n    if (state & NGX_PEER_FAILED) {\n        now = ngx_time();\n\n        peer->fails++;\n        peer->accessed = now;\n        peer->checked = now;\n\n        if (peer->max_fails) {\n            peer->effective_weight -= peer->weight / peer->max_fails;\n\n            if (peer->fails >= peer->max_fails) {\n                ngx_log_error(NGX_LOG_WARN, pc->log, 0,\n                              \"upstream server temporarily disabled\");\n            }\n        }\n\n        ngx_log_debug2(NGX_LOG_DEBUG_STREAM, pc->log, 0,\n                       \"free rr peer failed: %p %i\",\n                       peer, peer->effective_weight);\n\n        if (peer->effective_weight < 0) {\n            peer->effective_weight = 0;\n        }\n\n    } else {\n\n        /* mark peer live if check passed */\n\n        if (peer->accessed < peer->checked) {\n            peer->fails = 0;\n        }\n    }\n\n    peer->conns--;\n\n    ngx_stream_upstream_rr_peer_unlock(rrp->peers, peer);\n    ngx_stream_upstream_rr_peers_unlock(rrp->peers);\n\n    if (pc->tries) {\n        pc->tries--;\n    }\n}\n\n\nstatic void\nngx_stream_upstream_notify_round_robin_peer(ngx_peer_connection_t *pc,\n    void *data, ngx_uint_t type)\n{\n    ngx_stream_upstream_rr_peer_data_t  *rrp = data;\n\n    ngx_stream_upstream_rr_peer_t  *peer;\n\n    peer = rrp->current;\n\n    if (type == NGX_STREAM_UPSTREAM_NOTIFY_CONNECT\n        && pc->connection->type == SOCK_STREAM)\n    {\n        ngx_stream_upstream_rr_peers_rlock(rrp->peers);\n        ngx_stream_upstream_rr_peer_lock(rrp->peers, peer);\n\n        if (peer->accessed < peer->checked) {\n            peer->fails = 0;\n        }\n\n        ngx_stream_upstream_rr_peer_unlock(rrp->peers, peer);\n        ngx_stream_upstream_rr_peers_unlock(rrp->peers);\n    }\n}\n\n\n#if (NGX_STREAM_SSL)\n\nstatic ngx_int_t\nngx_stream_upstream_set_round_robin_peer_session(ngx_peer_connection_t *pc,\n    void *data)\n{\n    ngx_stream_upstream_rr_peer_data_t  *rrp = data;\n\n    ngx_int_t                        rc;\n    ngx_ssl_session_t               *ssl_session;\n    ngx_stream_upstream_rr_peer_t   *peer;\n#if (NGX_STREAM_UPSTREAM_ZONE)\n    int                              len;\n    const u_char                    *p;\n    ngx_stream_upstream_rr_peers_t  *peers;\n    u_char                           buf[NGX_SSL_MAX_SESSION_SIZE];\n#endif\n\n    peer = rrp->current;\n\n#if (NGX_STREAM_UPSTREAM_ZONE)\n    peers = rrp->peers;\n\n    if (peers->shpool) {\n        ngx_stream_upstream_rr_peers_rlock(peers);\n        ngx_stream_upstream_rr_peer_lock(peers, peer);\n\n        if (peer->ssl_session == NULL) {\n            ngx_stream_upstream_rr_peer_unlock(peers, peer);\n            ngx_stream_upstream_rr_peers_unlock(peers);\n            return NGX_OK;\n        }\n\n        len = peer->ssl_session_len;\n\n        ngx_memcpy(buf, peer->ssl_session, len);\n\n        ngx_stream_upstream_rr_peer_unlock(peers, peer);\n        ngx_stream_upstream_rr_peers_unlock(peers);\n\n        p = buf;\n        ssl_session = d2i_SSL_SESSION(NULL, &p, len);\n\n        rc = ngx_ssl_set_session(pc->connection, ssl_session);\n\n        ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0,\n                       \"set session: %p\", ssl_session);\n\n        ngx_ssl_free_session(ssl_session);\n\n        return rc;\n    }\n#endif\n\n    ssl_session = peer->ssl_session;\n\n    rc = ngx_ssl_set_session(pc->connection, ssl_session);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0,\n                   \"set session: %p\", ssl_session);\n\n    return rc;\n}\n\n\nstatic void\nngx_stream_upstream_save_round_robin_peer_session(ngx_peer_connection_t *pc,\n    void *data)\n{\n    ngx_stream_upstream_rr_peer_data_t  *rrp = data;\n\n    ngx_ssl_session_t               *old_ssl_session, *ssl_session;\n    ngx_stream_upstream_rr_peer_t   *peer;\n#if (NGX_STREAM_UPSTREAM_ZONE)\n    int                              len;\n    u_char                          *p;\n    ngx_stream_upstream_rr_peers_t  *peers;\n    u_char                           buf[NGX_SSL_MAX_SESSION_SIZE];\n#endif\n\n#if (NGX_STREAM_UPSTREAM_ZONE)\n    peers = rrp->peers;\n\n    if (peers->shpool) {\n\n        ssl_session = ngx_ssl_get0_session(pc->connection);\n\n        if (ssl_session == NULL) {\n            return;\n        }\n\n        ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0,\n                       \"save session: %p\", ssl_session);\n\n        len = i2d_SSL_SESSION(ssl_session, NULL);\n\n        /* do not cache too big session */\n\n        if (len > NGX_SSL_MAX_SESSION_SIZE) {\n            return;\n        }\n\n        p = buf;\n        (void) i2d_SSL_SESSION(ssl_session, &p);\n\n        peer = rrp->current;\n\n        ngx_stream_upstream_rr_peers_rlock(peers);\n        ngx_stream_upstream_rr_peer_lock(peers, peer);\n\n        if (len > peer->ssl_session_len) {\n            ngx_shmtx_lock(&peers->shpool->mutex);\n\n            if (peer->ssl_session) {\n                ngx_slab_free_locked(peers->shpool, peer->ssl_session);\n            }\n\n            peer->ssl_session = ngx_slab_alloc_locked(peers->shpool, len);\n\n            ngx_shmtx_unlock(&peers->shpool->mutex);\n\n            if (peer->ssl_session == NULL) {\n                peer->ssl_session_len = 0;\n\n                ngx_stream_upstream_rr_peer_unlock(peers, peer);\n                ngx_stream_upstream_rr_peers_unlock(peers);\n                return;\n            }\n\n            peer->ssl_session_len = len;\n        }\n\n        ngx_memcpy(peer->ssl_session, buf, len);\n\n        ngx_stream_upstream_rr_peer_unlock(peers, peer);\n        ngx_stream_upstream_rr_peers_unlock(peers);\n\n        return;\n    }\n#endif\n\n    ssl_session = ngx_ssl_get_session(pc->connection);\n\n    if (ssl_session == NULL) {\n        return;\n    }\n\n    ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0,\n                   \"save session: %p\", ssl_session);\n\n    peer = rrp->current;\n\n    old_ssl_session = peer->ssl_session;\n    peer->ssl_session = ssl_session;\n\n    if (old_ssl_session) {\n\n        ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pc->log, 0,\n                       \"old session: %p\", old_ssl_session);\n\n        /* TODO: may block */\n\n        ngx_ssl_free_session(old_ssl_session);\n    }\n}\n\n\nstatic ngx_int_t\nngx_stream_upstream_empty_set_session(ngx_peer_connection_t *pc, void *data)\n{\n    return NGX_OK;\n}\n\n\nstatic void\nngx_stream_upstream_empty_save_session(ngx_peer_connection_t *pc, void *data)\n{\n    return;\n}\n\n#endif\n"
  },
  {
    "path": "src/stream/ngx_stream_upstream_round_robin.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_STREAM_UPSTREAM_ROUND_ROBIN_H_INCLUDED_\n#define _NGX_STREAM_UPSTREAM_ROUND_ROBIN_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_stream.h>\n\n\ntypedef struct ngx_stream_upstream_rr_peer_s   ngx_stream_upstream_rr_peer_t;\n\nstruct ngx_stream_upstream_rr_peer_s {\n    struct sockaddr                 *sockaddr;\n    socklen_t                        socklen;\n    ngx_str_t                        name;\n    ngx_str_t                        server;\n\n    ngx_int_t                        current_weight;\n    ngx_int_t                        effective_weight;\n    ngx_int_t                        weight;\n\n    ngx_uint_t                       conns;\n    ngx_uint_t                       max_conns;\n\n    ngx_uint_t                       fails;\n    time_t                           accessed;\n    time_t                           checked;\n\n    ngx_uint_t                       max_fails;\n    time_t                           fail_timeout;\n    ngx_msec_t                       slow_start;\n    ngx_msec_t                       start_time;\n\n    ngx_uint_t                       down;\n\n    void                            *ssl_session;\n    int                              ssl_session_len;\n\n#if (NGX_STREAM_UPSTREAM_ZONE)\n    ngx_atomic_t                     lock;\n#endif\n\n    ngx_stream_upstream_rr_peer_t   *next;\n\n    NGX_COMPAT_BEGIN(25)\n    NGX_COMPAT_END\n};\n\n\ntypedef struct ngx_stream_upstream_rr_peers_s  ngx_stream_upstream_rr_peers_t;\n\nstruct ngx_stream_upstream_rr_peers_s {\n    ngx_uint_t                       number;\n\n#if (NGX_STREAM_UPSTREAM_ZONE)\n    ngx_slab_pool_t                 *shpool;\n    ngx_atomic_t                     rwlock;\n    ngx_stream_upstream_rr_peers_t  *zone_next;\n#endif\n\n    ngx_uint_t                       total_weight;\n    ngx_uint_t                       tries;\n\n    unsigned                         single:1;\n    unsigned                         weighted:1;\n\n    ngx_str_t                       *name;\n\n    ngx_stream_upstream_rr_peers_t  *next;\n\n    ngx_stream_upstream_rr_peer_t   *peer;\n};\n\n\n#if (NGX_STREAM_UPSTREAM_ZONE)\n\n#define ngx_stream_upstream_rr_peers_rlock(peers)                             \\\n                                                                              \\\n    if (peers->shpool) {                                                      \\\n        ngx_rwlock_rlock(&peers->rwlock);                                     \\\n    }\n\n#define ngx_stream_upstream_rr_peers_wlock(peers)                             \\\n                                                                              \\\n    if (peers->shpool) {                                                      \\\n        ngx_rwlock_wlock(&peers->rwlock);                                     \\\n    }\n\n#define ngx_stream_upstream_rr_peers_unlock(peers)                            \\\n                                                                              \\\n    if (peers->shpool) {                                                      \\\n        ngx_rwlock_unlock(&peers->rwlock);                                    \\\n    }\n\n\n#define ngx_stream_upstream_rr_peer_lock(peers, peer)                         \\\n                                                                              \\\n    if (peers->shpool) {                                                      \\\n        ngx_rwlock_wlock(&peer->lock);                                        \\\n    }\n\n#define ngx_stream_upstream_rr_peer_unlock(peers, peer)                       \\\n                                                                              \\\n    if (peers->shpool) {                                                      \\\n        ngx_rwlock_unlock(&peer->lock);                                       \\\n    }\n\n#else\n\n#define ngx_stream_upstream_rr_peers_rlock(peers)\n#define ngx_stream_upstream_rr_peers_wlock(peers)\n#define ngx_stream_upstream_rr_peers_unlock(peers)\n#define ngx_stream_upstream_rr_peer_lock(peers, peer)\n#define ngx_stream_upstream_rr_peer_unlock(peers, peer)\n\n#endif\n\n\ntypedef struct {\n    ngx_uint_t                       config;\n    ngx_stream_upstream_rr_peers_t  *peers;\n    ngx_stream_upstream_rr_peer_t   *current;\n    uintptr_t                       *tried;\n    uintptr_t                        data;\n} ngx_stream_upstream_rr_peer_data_t;\n\n\nngx_int_t ngx_stream_upstream_init_round_robin(ngx_conf_t *cf,\n    ngx_stream_upstream_srv_conf_t *us);\nngx_int_t ngx_stream_upstream_init_round_robin_peer(ngx_stream_session_t *s,\n    ngx_stream_upstream_srv_conf_t *us);\nngx_int_t ngx_stream_upstream_create_round_robin_peer(ngx_stream_session_t *s,\n    ngx_stream_upstream_resolved_t *ur);\nngx_int_t ngx_stream_upstream_get_round_robin_peer(ngx_peer_connection_t *pc,\n    void *data);\nvoid ngx_stream_upstream_free_round_robin_peer(ngx_peer_connection_t *pc,\n    void *data, ngx_uint_t state);\n\n\n#endif /* _NGX_STREAM_UPSTREAM_ROUND_ROBIN_H_INCLUDED_ */\n"
  },
  {
    "path": "src/stream/ngx_stream_upstream_zone_module.c",
    "content": "\n/*\n * Copyright (C) Ruslan Ermilov\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_stream.h>\n\n\nstatic char *ngx_stream_upstream_zone(ngx_conf_t *cf, ngx_command_t *cmd,\n    void *conf);\nstatic ngx_int_t ngx_stream_upstream_init_zone(ngx_shm_zone_t *shm_zone,\n    void *data);\nstatic ngx_stream_upstream_rr_peers_t *ngx_stream_upstream_zone_copy_peers(\n    ngx_slab_pool_t *shpool, ngx_stream_upstream_srv_conf_t *uscf);\nstatic ngx_stream_upstream_rr_peer_t *ngx_stream_upstream_zone_copy_peer(\n    ngx_stream_upstream_rr_peers_t *peers, ngx_stream_upstream_rr_peer_t *src);\n\n\nstatic ngx_command_t  ngx_stream_upstream_zone_commands[] = {\n\n    { ngx_string(\"zone\"),\n      NGX_STREAM_UPS_CONF|NGX_CONF_TAKE12,\n      ngx_stream_upstream_zone,\n      0,\n      0,\n      NULL },\n\n      ngx_null_command\n};\n\n\nstatic ngx_stream_module_t  ngx_stream_upstream_zone_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    NULL,                                  /* 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\n\nngx_module_t  ngx_stream_upstream_zone_module = {\n    NGX_MODULE_V1,\n    &ngx_stream_upstream_zone_module_ctx,  /* module context */\n    ngx_stream_upstream_zone_commands,     /* module directives */\n    NGX_STREAM_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 char *\nngx_stream_upstream_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)\n{\n    ssize_t                           size;\n    ngx_str_t                        *value;\n    ngx_stream_upstream_srv_conf_t   *uscf;\n    ngx_stream_upstream_main_conf_t  *umcf;\n\n    uscf = ngx_stream_conf_get_module_srv_conf(cf, ngx_stream_upstream_module);\n    umcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_upstream_module);\n\n    value = cf->args->elts;\n\n    if (!value[1].len) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid zone name \\\"%V\\\"\", &value[1]);\n        return NGX_CONF_ERROR;\n    }\n\n    if (cf->args->nelts == 3) {\n        size = ngx_parse_size(&value[2]);\n\n        if (size == NGX_ERROR) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"invalid zone size \\\"%V\\\"\", &value[2]);\n            return NGX_CONF_ERROR;\n        }\n\n        if (size < (ssize_t) (8 * ngx_pagesize)) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"zone \\\"%V\\\" is too small\", &value[1]);\n            return NGX_CONF_ERROR;\n        }\n\n    } else {\n        size = 0;\n    }\n\n    uscf->shm_zone = ngx_shared_memory_add(cf, &value[1], size,\n                                           &ngx_stream_upstream_module);\n    if (uscf->shm_zone == NULL) {\n        return NGX_CONF_ERROR;\n    }\n\n    uscf->shm_zone->init = ngx_stream_upstream_init_zone;\n    uscf->shm_zone->data = umcf;\n\n    uscf->shm_zone->noreuse = 1;\n\n    return NGX_CONF_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_upstream_init_zone(ngx_shm_zone_t *shm_zone, void *data)\n{\n    size_t                            len;\n    ngx_uint_t                        i;\n    ngx_slab_pool_t                  *shpool;\n    ngx_stream_upstream_rr_peers_t   *peers, **peersp;\n    ngx_stream_upstream_srv_conf_t   *uscf, **uscfp;\n    ngx_stream_upstream_main_conf_t  *umcf;\n\n    shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;\n    umcf = shm_zone->data;\n    uscfp = umcf->upstreams.elts;\n\n    if (shm_zone->shm.exists) {\n        peers = shpool->data;\n\n        for (i = 0; i < umcf->upstreams.nelts; i++) {\n            uscf = uscfp[i];\n\n            if (uscf->shm_zone != shm_zone) {\n                continue;\n            }\n\n            uscf->peer.data = peers;\n            peers = peers->zone_next;\n        }\n\n        return NGX_OK;\n    }\n\n    len = sizeof(\" in upstream zone \\\"\\\"\") + shm_zone->shm.name.len;\n\n    shpool->log_ctx = ngx_slab_alloc(shpool, len);\n    if (shpool->log_ctx == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_sprintf(shpool->log_ctx, \" in upstream zone \\\"%V\\\"%Z\",\n                &shm_zone->shm.name);\n\n\n    /* copy peers to shared memory */\n\n    peersp = (ngx_stream_upstream_rr_peers_t **) (void *) &shpool->data;\n\n    for (i = 0; i < umcf->upstreams.nelts; i++) {\n        uscf = uscfp[i];\n\n        if (uscf->shm_zone != shm_zone) {\n            continue;\n        }\n\n        peers = ngx_stream_upstream_zone_copy_peers(shpool, uscf);\n        if (peers == NULL) {\n            return NGX_ERROR;\n        }\n\n        *peersp = peers;\n        peersp = &peers->zone_next;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_stream_upstream_rr_peers_t *\nngx_stream_upstream_zone_copy_peers(ngx_slab_pool_t *shpool,\n    ngx_stream_upstream_srv_conf_t *uscf)\n{\n    ngx_str_t                       *name;\n    ngx_stream_upstream_rr_peer_t   *peer, **peerp;\n    ngx_stream_upstream_rr_peers_t  *peers, *backup;\n\n    peers = ngx_slab_alloc(shpool, sizeof(ngx_stream_upstream_rr_peers_t));\n    if (peers == NULL) {\n        return NULL;\n    }\n\n    ngx_memcpy(peers, uscf->peer.data, sizeof(ngx_stream_upstream_rr_peers_t));\n\n    name = ngx_slab_alloc(shpool, sizeof(ngx_str_t));\n    if (name == NULL) {\n        return NULL;\n    }\n\n    name->data = ngx_slab_alloc(shpool, peers->name->len);\n    if (name->data == NULL) {\n        return NULL;\n    }\n\n    ngx_memcpy(name->data, peers->name->data, peers->name->len);\n    name->len = peers->name->len;\n\n    peers->name = name;\n\n    peers->shpool = shpool;\n\n    for (peerp = &peers->peer; *peerp; peerp = &peer->next) {\n        /* pool is unlocked */\n        peer = ngx_stream_upstream_zone_copy_peer(peers, *peerp);\n        if (peer == NULL) {\n            return NULL;\n        }\n\n        *peerp = peer;\n    }\n\n    if (peers->next == NULL) {\n        goto done;\n    }\n\n    backup = ngx_slab_alloc(shpool, sizeof(ngx_stream_upstream_rr_peers_t));\n    if (backup == NULL) {\n        return NULL;\n    }\n\n    ngx_memcpy(backup, peers->next, sizeof(ngx_stream_upstream_rr_peers_t));\n\n    backup->name = name;\n\n    backup->shpool = shpool;\n\n    for (peerp = &backup->peer; *peerp; peerp = &peer->next) {\n        /* pool is unlocked */\n        peer = ngx_stream_upstream_zone_copy_peer(backup, *peerp);\n        if (peer == NULL) {\n            return NULL;\n        }\n\n        *peerp = peer;\n    }\n\n    peers->next = backup;\n\ndone:\n\n    uscf->peer.data = peers;\n\n    return peers;\n}\n\n\nstatic ngx_stream_upstream_rr_peer_t *\nngx_stream_upstream_zone_copy_peer(ngx_stream_upstream_rr_peers_t *peers,\n    ngx_stream_upstream_rr_peer_t *src)\n{\n    ngx_slab_pool_t                *pool;\n    ngx_stream_upstream_rr_peer_t  *dst;\n\n    pool = peers->shpool;\n\n    dst = ngx_slab_calloc_locked(pool, sizeof(ngx_stream_upstream_rr_peer_t));\n    if (dst == NULL) {\n        return NULL;\n    }\n\n    if (src) {\n        ngx_memcpy(dst, src, sizeof(ngx_stream_upstream_rr_peer_t));\n        dst->sockaddr = NULL;\n        dst->name.data = NULL;\n        dst->server.data = NULL;\n    }\n\n    dst->sockaddr = ngx_slab_calloc_locked(pool, sizeof(ngx_sockaddr_t));\n    if (dst->sockaddr == NULL) {\n        goto failed;\n    }\n\n    dst->name.data = ngx_slab_calloc_locked(pool, NGX_SOCKADDR_STRLEN);\n    if (dst->name.data == NULL) {\n        goto failed;\n    }\n\n    if (src) {\n        ngx_memcpy(dst->sockaddr, src->sockaddr, src->socklen);\n        ngx_memcpy(dst->name.data, src->name.data, src->name.len);\n\n        dst->server.data = ngx_slab_alloc_locked(pool, src->server.len);\n        if (dst->server.data == NULL) {\n            goto failed;\n        }\n\n        ngx_memcpy(dst->server.data, src->server.data, src->server.len);\n    }\n\n    return dst;\n\nfailed:\n\n    if (dst->server.data) {\n        ngx_slab_free_locked(pool, dst->server.data);\n    }\n\n    if (dst->name.data) {\n        ngx_slab_free_locked(pool, dst->name.data);\n    }\n\n    if (dst->sockaddr) {\n        ngx_slab_free_locked(pool, dst->sockaddr);\n    }\n\n    ngx_slab_free_locked(pool, dst);\n\n    return NULL;\n}\n"
  },
  {
    "path": "src/stream/ngx_stream_variables.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_stream.h>\n#include <nginx.h>\n\nstatic ngx_stream_variable_t *ngx_stream_add_prefix_variable(ngx_conf_t *cf,\n    ngx_str_t *name, ngx_uint_t flags);\n\nstatic ngx_int_t ngx_stream_variable_binary_remote_addr(\n    ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_stream_variable_remote_addr(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_stream_variable_remote_port(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_stream_variable_proxy_protocol_addr(\n    ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_stream_variable_proxy_protocol_port(\n    ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_stream_variable_proxy_protocol_tlv(\n    ngx_stream_session_t *s, ngx_stream_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_stream_variable_server_addr(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_stream_variable_server_port(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_stream_variable_bytes(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_stream_variable_session_time(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_stream_variable_status(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_stream_variable_connection(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data);\n\nstatic ngx_int_t ngx_stream_variable_nginx_version(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_stream_variable_hostname(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_stream_variable_pid(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_stream_variable_msec(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_stream_variable_time_iso8601(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_stream_variable_time_local(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data);\nstatic ngx_int_t ngx_stream_variable_protocol(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data);\n\n\nstatic ngx_stream_variable_t  ngx_stream_core_variables[] = {\n\n    { ngx_string(\"binary_remote_addr\"), NULL,\n      ngx_stream_variable_binary_remote_addr, 0, 0, 0 },\n\n    { ngx_string(\"remote_addr\"), NULL,\n      ngx_stream_variable_remote_addr, 0, 0, 0 },\n\n    { ngx_string(\"remote_port\"), NULL,\n      ngx_stream_variable_remote_port, 0, 0, 0 },\n\n    { ngx_string(\"proxy_protocol_addr\"), NULL,\n      ngx_stream_variable_proxy_protocol_addr,\n      offsetof(ngx_proxy_protocol_t, src_addr), 0, 0 },\n\n    { ngx_string(\"proxy_protocol_port\"), NULL,\n      ngx_stream_variable_proxy_protocol_port,\n      offsetof(ngx_proxy_protocol_t, src_port), 0, 0 },\n\n    { ngx_string(\"proxy_protocol_server_addr\"), NULL,\n      ngx_stream_variable_proxy_protocol_addr,\n      offsetof(ngx_proxy_protocol_t, dst_addr), 0, 0 },\n\n    { ngx_string(\"proxy_protocol_server_port\"), NULL,\n      ngx_stream_variable_proxy_protocol_port,\n      offsetof(ngx_proxy_protocol_t, dst_port), 0, 0 },\n\n    { ngx_string(\"proxy_protocol_tlv_\"), NULL,\n      ngx_stream_variable_proxy_protocol_tlv,\n      0, NGX_STREAM_VAR_PREFIX, 0 },\n\n    { ngx_string(\"server_addr\"), NULL,\n      ngx_stream_variable_server_addr, 0, 0, 0 },\n\n    { ngx_string(\"server_port\"), NULL,\n      ngx_stream_variable_server_port, 0, 0, 0 },\n\n    { ngx_string(\"bytes_sent\"), NULL, ngx_stream_variable_bytes,\n      0, 0, 0 },\n\n    { ngx_string(\"bytes_received\"), NULL, ngx_stream_variable_bytes,\n      1, 0, 0 },\n\n    { ngx_string(\"session_time\"), NULL, ngx_stream_variable_session_time,\n      0, NGX_STREAM_VAR_NOCACHEABLE, 0 },\n\n    { ngx_string(\"status\"), NULL, ngx_stream_variable_status,\n      0, NGX_STREAM_VAR_NOCACHEABLE, 0 },\n\n    { ngx_string(\"connection\"), NULL,\n      ngx_stream_variable_connection, 0, 0, 0 },\n\n    { ngx_string(\"nginx_version\"), NULL, ngx_stream_variable_nginx_version,\n      0, 0, 0 },\n\n    { ngx_string(\"hostname\"), NULL, ngx_stream_variable_hostname,\n      0, 0, 0 },\n\n    { ngx_string(\"pid\"), NULL, ngx_stream_variable_pid,\n      0, 0, 0 },\n\n    { ngx_string(\"msec\"), NULL, ngx_stream_variable_msec,\n      0, NGX_STREAM_VAR_NOCACHEABLE, 0 },\n\n    { ngx_string(\"time_iso8601\"), NULL, ngx_stream_variable_time_iso8601,\n      0, NGX_STREAM_VAR_NOCACHEABLE, 0 },\n\n    { ngx_string(\"time_local\"), NULL, ngx_stream_variable_time_local,\n      0, NGX_STREAM_VAR_NOCACHEABLE, 0 },\n\n    { ngx_string(\"protocol\"), NULL,\n      ngx_stream_variable_protocol, 0, 0, 0 },\n\n      ngx_stream_null_variable\n};\n\n\nngx_stream_variable_value_t  ngx_stream_variable_null_value =\n    ngx_stream_variable(\"\");\nngx_stream_variable_value_t  ngx_stream_variable_true_value =\n    ngx_stream_variable(\"1\");\n\n\nstatic ngx_uint_t  ngx_stream_variable_depth = 100;\n\n\nngx_stream_variable_t *\nngx_stream_add_variable(ngx_conf_t *cf, ngx_str_t *name, ngx_uint_t flags)\n{\n    ngx_int_t                     rc;\n    ngx_uint_t                    i;\n    ngx_hash_key_t               *key;\n    ngx_stream_variable_t        *v;\n    ngx_stream_core_main_conf_t  *cmcf;\n\n    if (name->len == 0) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid variable name \\\"$\\\"\");\n        return NULL;\n    }\n\n    if (flags & NGX_STREAM_VAR_PREFIX) {\n        return ngx_stream_add_prefix_variable(cf, name, flags);\n    }\n\n    cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);\n\n    key = cmcf->variables_keys->keys.elts;\n    for (i = 0; i < cmcf->variables_keys->keys.nelts; i++) {\n        if (name->len != key[i].key.len\n            || ngx_strncasecmp(name->data, key[i].key.data, name->len) != 0)\n        {\n            continue;\n        }\n\n        v = key[i].value;\n\n        if (!(v->flags & NGX_STREAM_VAR_CHANGEABLE)) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"the duplicate \\\"%V\\\" variable\", name);\n            return NULL;\n        }\n\n        if (!(flags & NGX_STREAM_VAR_WEAK)) {\n            v->flags &= ~NGX_STREAM_VAR_WEAK;\n        }\n\n        return v;\n    }\n\n    v = ngx_palloc(cf->pool, sizeof(ngx_stream_variable_t));\n    if (v == NULL) {\n        return NULL;\n    }\n\n    v->name.len = name->len;\n    v->name.data = ngx_pnalloc(cf->pool, name->len);\n    if (v->name.data == NULL) {\n        return NULL;\n    }\n\n    ngx_strlow(v->name.data, name->data, name->len);\n\n    v->set_handler = NULL;\n    v->get_handler = NULL;\n    v->data = 0;\n    v->flags = flags;\n    v->index = 0;\n\n    rc = ngx_hash_add_key(cmcf->variables_keys, &v->name, v, 0);\n\n    if (rc == NGX_ERROR) {\n        return NULL;\n    }\n\n    if (rc == NGX_BUSY) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"conflicting variable name \\\"%V\\\"\", name);\n        return NULL;\n    }\n\n    return v;\n}\n\n\nstatic ngx_stream_variable_t *\nngx_stream_add_prefix_variable(ngx_conf_t *cf, ngx_str_t *name,\n    ngx_uint_t flags)\n{\n    ngx_uint_t                    i;\n    ngx_stream_variable_t        *v;\n    ngx_stream_core_main_conf_t  *cmcf;\n\n    cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);\n\n    v = cmcf->prefix_variables.elts;\n    for (i = 0; i < cmcf->prefix_variables.nelts; i++) {\n        if (name->len != v[i].name.len\n            || ngx_strncasecmp(name->data, v[i].name.data, name->len) != 0)\n        {\n            continue;\n        }\n\n        v = &v[i];\n\n        if (!(v->flags & NGX_STREAM_VAR_CHANGEABLE)) {\n            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                               \"the duplicate \\\"%V\\\" variable\", name);\n            return NULL;\n        }\n\n        if (!(flags & NGX_STREAM_VAR_WEAK)) {\n            v->flags &= ~NGX_STREAM_VAR_WEAK;\n        }\n\n        return v;\n    }\n\n    v = ngx_array_push(&cmcf->prefix_variables);\n    if (v == NULL) {\n        return NULL;\n    }\n\n    v->name.len = name->len;\n    v->name.data = ngx_pnalloc(cf->pool, name->len);\n    if (v->name.data == NULL) {\n        return NULL;\n    }\n\n    ngx_strlow(v->name.data, name->data, name->len);\n\n    v->set_handler = NULL;\n    v->get_handler = NULL;\n    v->data = 0;\n    v->flags = flags;\n    v->index = 0;\n\n    return v;\n}\n\n\nngx_int_t\nngx_stream_get_variable_index(ngx_conf_t *cf, ngx_str_t *name)\n{\n    ngx_uint_t                    i;\n    ngx_stream_variable_t        *v;\n    ngx_stream_core_main_conf_t  *cmcf;\n\n    if (name->len == 0) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,\n                           \"invalid variable name \\\"$\\\"\");\n        return NGX_ERROR;\n    }\n\n    cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);\n\n    v = cmcf->variables.elts;\n\n    if (v == NULL) {\n        if (ngx_array_init(&cmcf->variables, cf->pool, 4,\n                           sizeof(ngx_stream_variable_t))\n            != NGX_OK)\n        {\n            return NGX_ERROR;\n        }\n\n    } else {\n        for (i = 0; i < cmcf->variables.nelts; i++) {\n            if (name->len != v[i].name.len\n                || ngx_strncasecmp(name->data, v[i].name.data, name->len) != 0)\n            {\n                continue;\n            }\n\n            return i;\n        }\n    }\n\n    v = ngx_array_push(&cmcf->variables);\n    if (v == NULL) {\n        return NGX_ERROR;\n    }\n\n    v->name.len = name->len;\n    v->name.data = ngx_pnalloc(cf->pool, name->len);\n    if (v->name.data == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_strlow(v->name.data, name->data, name->len);\n\n    v->set_handler = NULL;\n    v->get_handler = NULL;\n    v->data = 0;\n    v->flags = 0;\n    v->index = cmcf->variables.nelts - 1;\n\n    return v->index;\n}\n\n\nngx_stream_variable_value_t *\nngx_stream_get_indexed_variable(ngx_stream_session_t *s, ngx_uint_t index)\n{\n    ngx_stream_variable_t        *v;\n    ngx_stream_core_main_conf_t  *cmcf;\n\n    cmcf = ngx_stream_get_module_main_conf(s, ngx_stream_core_module);\n\n    if (cmcf->variables.nelts <= index) {\n        ngx_log_error(NGX_LOG_ALERT, s->connection->log, 0,\n                      \"unknown variable index: %ui\", index);\n        return NULL;\n    }\n\n    if (s->variables[index].not_found || s->variables[index].valid) {\n        return &s->variables[index];\n    }\n\n    v = cmcf->variables.elts;\n\n    if (ngx_stream_variable_depth == 0) {\n        ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,\n                      \"cycle while evaluating variable \\\"%V\\\"\",\n                      &v[index].name);\n        return NULL;\n    }\n\n    ngx_stream_variable_depth--;\n\n    if (v[index].get_handler(s, &s->variables[index], v[index].data)\n        == NGX_OK)\n    {\n        ngx_stream_variable_depth++;\n\n        if (v[index].flags & NGX_STREAM_VAR_NOCACHEABLE) {\n            s->variables[index].no_cacheable = 1;\n        }\n\n        return &s->variables[index];\n    }\n\n    ngx_stream_variable_depth++;\n\n    s->variables[index].valid = 0;\n    s->variables[index].not_found = 1;\n\n    return NULL;\n}\n\n\nngx_stream_variable_value_t *\nngx_stream_get_flushed_variable(ngx_stream_session_t *s, ngx_uint_t index)\n{\n    ngx_stream_variable_value_t  *v;\n\n    v = &s->variables[index];\n\n    if (v->valid || v->not_found) {\n        if (!v->no_cacheable) {\n            return v;\n        }\n\n        v->valid = 0;\n        v->not_found = 0;\n    }\n\n    return ngx_stream_get_indexed_variable(s, index);\n}\n\n\nngx_stream_variable_value_t *\nngx_stream_get_variable(ngx_stream_session_t *s, ngx_str_t *name,\n    ngx_uint_t key)\n{\n    size_t                        len;\n    ngx_uint_t                    i, n;\n    ngx_stream_variable_t        *v;\n    ngx_stream_variable_value_t  *vv;\n    ngx_stream_core_main_conf_t  *cmcf;\n\n    cmcf = ngx_stream_get_module_main_conf(s, ngx_stream_core_module);\n\n    v = ngx_hash_find(&cmcf->variables_hash, key, name->data, name->len);\n\n    if (v) {\n        if (v->flags & NGX_STREAM_VAR_INDEXED) {\n            return ngx_stream_get_flushed_variable(s, v->index);\n        }\n\n        if (ngx_stream_variable_depth == 0) {\n            ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,\n                          \"cycle while evaluating variable \\\"%V\\\"\", name);\n            return NULL;\n        }\n\n        ngx_stream_variable_depth--;\n\n        vv = ngx_palloc(s->connection->pool,\n                        sizeof(ngx_stream_variable_value_t));\n\n        if (vv && v->get_handler(s, vv, v->data) == NGX_OK) {\n            ngx_stream_variable_depth++;\n            return vv;\n        }\n\n        ngx_stream_variable_depth++;\n        return NULL;\n    }\n\n    vv = ngx_palloc(s->connection->pool, sizeof(ngx_stream_variable_value_t));\n    if (vv == NULL) {\n        return NULL;\n    }\n\n    len = 0;\n\n    v = cmcf->prefix_variables.elts;\n    n = cmcf->prefix_variables.nelts;\n\n    for (i = 0; i < cmcf->prefix_variables.nelts; i++) {\n        if (name->len >= v[i].name.len && name->len > len\n            && ngx_strncmp(name->data, v[i].name.data, v[i].name.len) == 0)\n        {\n            len = v[i].name.len;\n            n = i;\n        }\n    }\n\n    if (n != cmcf->prefix_variables.nelts) {\n        if (v[n].get_handler(s, vv, (uintptr_t) name) == NGX_OK) {\n            return vv;\n        }\n\n        return NULL;\n    }\n\n    vv->not_found = 1;\n\n    return vv;\n}\n\n\nstatic ngx_int_t\nngx_stream_variable_binary_remote_addr(ngx_stream_session_t *s,\n     ngx_stream_variable_value_t *v, uintptr_t data)\n{\n    struct sockaddr_in   *sin;\n#if (NGX_HAVE_INET6)\n    struct sockaddr_in6  *sin6;\n#endif\n\n    switch (s->connection->sockaddr->sa_family) {\n\n#if (NGX_HAVE_INET6)\n    case AF_INET6:\n        sin6 = (struct sockaddr_in6 *) s->connection->sockaddr;\n\n        v->len = sizeof(struct in6_addr);\n        v->valid = 1;\n        v->no_cacheable = 0;\n        v->not_found = 0;\n        v->data = sin6->sin6_addr.s6_addr;\n\n        break;\n#endif\n\n#if (NGX_HAVE_UNIX_DOMAIN)\n    case AF_UNIX:\n\n        v->len = s->connection->addr_text.len;\n        v->valid = 1;\n        v->no_cacheable = 0;\n        v->not_found = 0;\n        v->data = s->connection->addr_text.data;\n\n        break;\n#endif\n\n    default: /* AF_INET */\n        sin = (struct sockaddr_in *) s->connection->sockaddr;\n\n        v->len = sizeof(in_addr_t);\n        v->valid = 1;\n        v->no_cacheable = 0;\n        v->not_found = 0;\n        v->data = (u_char *) &sin->sin_addr;\n\n        break;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_variable_remote_addr(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data)\n{\n    v->len = s->connection->addr_text.len;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = s->connection->addr_text.data;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_variable_remote_port(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data)\n{\n    ngx_uint_t  port;\n\n    v->len = 0;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    v->data = ngx_pnalloc(s->connection->pool, sizeof(\"65535\") - 1);\n    if (v->data == NULL) {\n        return NGX_ERROR;\n    }\n\n    port = ngx_inet_get_port(s->connection->sockaddr);\n\n    if (port > 0 && port < 65536) {\n        v->len = ngx_sprintf(v->data, \"%ui\", port) - v->data;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_variable_proxy_protocol_addr(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data)\n{\n    ngx_str_t             *addr;\n    ngx_proxy_protocol_t  *pp;\n\n    pp = s->connection->proxy_protocol;\n    if (pp == NULL) {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    addr = (ngx_str_t *) ((char *) pp + data);\n\n    v->len = addr->len;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = addr->data;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_variable_proxy_protocol_port(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data)\n{\n    ngx_uint_t             port;\n    ngx_proxy_protocol_t  *pp;\n\n    pp = s->connection->proxy_protocol;\n    if (pp == NULL) {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    v->len = 0;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    v->data = ngx_pnalloc(s->connection->pool, sizeof(\"65535\") - 1);\n    if (v->data == NULL) {\n        return NGX_ERROR;\n    }\n\n    port = *(in_port_t *) ((char *) pp + data);\n\n    if (port > 0 && port < 65536) {\n        v->len = ngx_sprintf(v->data, \"%ui\", port) - v->data;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_variable_proxy_protocol_tlv(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data)\n{\n    ngx_str_t *name = (ngx_str_t *) data;\n\n    ngx_int_t  rc;\n    ngx_str_t  tlv, value;\n\n    tlv.len = name->len - (sizeof(\"proxy_protocol_tlv_\") - 1);\n    tlv.data = name->data + sizeof(\"proxy_protocol_tlv_\") - 1;\n\n    rc = ngx_proxy_protocol_get_tlv(s->connection, &tlv, &value);\n\n    if (rc == NGX_ERROR) {\n        return NGX_ERROR;\n    }\n\n    if (rc == NGX_DECLINED) {\n        v->not_found = 1;\n        return NGX_OK;\n    }\n\n    v->len = value.len;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = value.data;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_variable_server_addr(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data)\n{\n    ngx_str_t  str;\n    u_char     addr[NGX_SOCKADDR_STRLEN];\n\n    str.len = NGX_SOCKADDR_STRLEN;\n    str.data = addr;\n\n    if (ngx_connection_local_sockaddr(s->connection, &str, 0) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    str.data = ngx_pnalloc(s->connection->pool, str.len);\n    if (str.data == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_memcpy(str.data, addr, str.len);\n\n    v->len = str.len;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = str.data;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_variable_server_port(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data)\n{\n    ngx_uint_t  port;\n\n    v->len = 0;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    if (ngx_connection_local_sockaddr(s->connection, NULL, 0) != NGX_OK) {\n        return NGX_ERROR;\n    }\n\n    v->data = ngx_pnalloc(s->connection->pool, sizeof(\"65535\") - 1);\n    if (v->data == NULL) {\n        return NGX_ERROR;\n    }\n\n    port = ngx_inet_get_port(s->connection->local_sockaddr);\n\n    if (port > 0 && port < 65536) {\n        v->len = ngx_sprintf(v->data, \"%ui\", port) - v->data;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_variable_bytes(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data)\n{\n    u_char  *p;\n\n    p = ngx_pnalloc(s->connection->pool, NGX_OFF_T_LEN);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    if (data == 1) {\n        v->len = ngx_sprintf(p, \"%O\", s->received) - p;\n\n    } else {\n        v->len = ngx_sprintf(p, \"%O\", s->connection->sent) - p;\n    }\n\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = p;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_variable_session_time(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data)\n{\n    u_char          *p;\n    ngx_time_t      *tp;\n    ngx_msec_int_t   ms;\n\n    p = ngx_pnalloc(s->connection->pool, NGX_TIME_T_LEN + 4);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    tp = ngx_timeofday();\n\n    ms = (ngx_msec_int_t)\n             ((tp->sec - s->start_sec) * 1000 + (tp->msec - s->start_msec));\n    ms = ngx_max(ms, 0);\n\n    v->len = ngx_sprintf(p, \"%T.%03M\", (time_t) ms / 1000, ms % 1000) - p;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = p;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_variable_status(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data)\n{\n    v->data = ngx_pnalloc(s->connection->pool, NGX_INT_T_LEN);\n    if (v->data == NULL) {\n        return NGX_ERROR;\n    }\n\n    v->len = ngx_sprintf(v->data, \"%03ui\", s->status) - v->data;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_variable_connection(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data)\n{\n    u_char  *p;\n\n    p = ngx_pnalloc(s->connection->pool, NGX_ATOMIC_T_LEN);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    v->len = ngx_sprintf(p, \"%uA\", s->connection->number) - p;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = p;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_variable_nginx_version(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data)\n{\n    v->len = sizeof(NGINX_VERSION) - 1;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = (u_char *) NGINX_VERSION;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_variable_hostname(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data)\n{\n    v->len = ngx_cycle->hostname.len;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = ngx_cycle->hostname.data;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_variable_pid(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data)\n{\n    u_char  *p;\n\n    p = ngx_pnalloc(s->connection->pool, NGX_INT64_LEN);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    v->len = ngx_sprintf(p, \"%P\", ngx_pid) - p;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = p;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_variable_msec(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data)\n{\n    u_char      *p;\n    ngx_time_t  *tp;\n\n    p = ngx_pnalloc(s->connection->pool, NGX_TIME_T_LEN + 4);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    tp = ngx_timeofday();\n\n    v->len = ngx_sprintf(p, \"%T.%03M\", tp->sec, tp->msec) - p;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = p;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_variable_time_iso8601(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data)\n{\n    u_char  *p;\n\n    p = ngx_pnalloc(s->connection->pool, ngx_cached_http_log_iso8601.len);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_memcpy(p, ngx_cached_http_log_iso8601.data,\n               ngx_cached_http_log_iso8601.len);\n\n    v->len = ngx_cached_http_log_iso8601.len;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = p;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_variable_time_local(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data)\n{\n    u_char  *p;\n\n    p = ngx_pnalloc(s->connection->pool, ngx_cached_http_log_time.len);\n    if (p == NULL) {\n        return NGX_ERROR;\n    }\n\n    ngx_memcpy(p, ngx_cached_http_log_time.data, ngx_cached_http_log_time.len);\n\n    v->len = ngx_cached_http_log_time.len;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = p;\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_variable_protocol(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data)\n{\n    v->len = 3;\n    v->valid = 1;\n    v->no_cacheable = 0;\n    v->not_found = 0;\n    v->data = (u_char *) (s->connection->type == SOCK_DGRAM ? \"UDP\" : \"TCP\");\n\n    return NGX_OK;\n}\n\n\nvoid *\nngx_stream_map_find(ngx_stream_session_t *s, ngx_stream_map_t *map,\n    ngx_str_t *match)\n{\n    void        *value;\n    u_char      *low;\n    size_t       len;\n    ngx_uint_t   key;\n\n    len = match->len;\n\n    if (len) {\n        low = ngx_pnalloc(s->connection->pool, len);\n        if (low == NULL) {\n            return NULL;\n        }\n\n    } else {\n        low = NULL;\n    }\n\n    key = ngx_hash_strlow(low, match->data, len);\n\n    value = ngx_hash_find_combined(&map->hash, key, low, len);\n    if (value) {\n        return value;\n    }\n\n#if (NGX_PCRE)\n\n    if (len && map->nregex) {\n        ngx_int_t                n;\n        ngx_uint_t               i;\n        ngx_stream_map_regex_t  *reg;\n\n        reg = map->regex;\n\n        for (i = 0; i < map->nregex; i++) {\n\n            n = ngx_stream_regex_exec(s, reg[i].regex, match);\n\n            if (n == NGX_OK) {\n                return reg[i].value;\n            }\n\n            if (n == NGX_DECLINED) {\n                continue;\n            }\n\n            /* NGX_ERROR */\n\n            return NULL;\n        }\n    }\n\n#endif\n\n    return NULL;\n}\n\n\n#if (NGX_PCRE)\n\nstatic ngx_int_t\nngx_stream_variable_not_found(ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data)\n{\n    v->not_found = 1;\n    return NGX_OK;\n}\n\n\nngx_stream_regex_t *\nngx_stream_regex_compile(ngx_conf_t *cf, ngx_regex_compile_t *rc)\n{\n    u_char                       *p;\n    size_t                        size;\n    ngx_str_t                     name;\n    ngx_uint_t                    i, n;\n    ngx_stream_variable_t        *v;\n    ngx_stream_regex_t           *re;\n    ngx_stream_regex_variable_t  *rv;\n    ngx_stream_core_main_conf_t  *cmcf;\n\n    rc->pool = cf->pool;\n\n    if (ngx_regex_compile(rc) != NGX_OK) {\n        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, \"%V\", &rc->err);\n        return NULL;\n    }\n\n    re = ngx_pcalloc(cf->pool, sizeof(ngx_stream_regex_t));\n    if (re == NULL) {\n        return NULL;\n    }\n\n    re->regex = rc->regex;\n    re->ncaptures = rc->captures;\n    re->name = rc->pattern;\n\n    cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);\n    cmcf->ncaptures = ngx_max(cmcf->ncaptures, re->ncaptures);\n\n    n = (ngx_uint_t) rc->named_captures;\n\n    if (n == 0) {\n        return re;\n    }\n\n    rv = ngx_palloc(rc->pool, n * sizeof(ngx_stream_regex_variable_t));\n    if (rv == NULL) {\n        return NULL;\n    }\n\n    re->variables = rv;\n    re->nvariables = n;\n\n    size = rc->name_size;\n    p = rc->names;\n\n    for (i = 0; i < n; i++) {\n        rv[i].capture = 2 * ((p[0] << 8) + p[1]);\n\n        name.data = &p[2];\n        name.len = ngx_strlen(name.data);\n\n        v = ngx_stream_add_variable(cf, &name, NGX_STREAM_VAR_CHANGEABLE);\n        if (v == NULL) {\n            return NULL;\n        }\n\n        rv[i].index = ngx_stream_get_variable_index(cf, &name);\n        if (rv[i].index == NGX_ERROR) {\n            return NULL;\n        }\n\n        v->get_handler = ngx_stream_variable_not_found;\n\n        p += size;\n    }\n\n    return re;\n}\n\n\nngx_int_t\nngx_stream_regex_exec(ngx_stream_session_t *s, ngx_stream_regex_t *re,\n    ngx_str_t *str)\n{\n    ngx_int_t                     rc, index;\n    ngx_uint_t                    i, n, len;\n    ngx_stream_variable_value_t  *vv;\n    ngx_stream_core_main_conf_t  *cmcf;\n\n    cmcf = ngx_stream_get_module_main_conf(s, ngx_stream_core_module);\n\n    if (re->ncaptures) {\n        len = cmcf->ncaptures;\n\n        if (s->captures == NULL) {\n            s->captures = ngx_palloc(s->connection->pool, len * sizeof(int));\n            if (s->captures == NULL) {\n                return NGX_ERROR;\n            }\n        }\n\n    } else {\n        len = 0;\n    }\n\n    rc = ngx_regex_exec(re->regex, str, s->captures, len);\n\n    if (rc == NGX_REGEX_NO_MATCHED) {\n        return NGX_DECLINED;\n    }\n\n    if (rc < 0) {\n        ngx_log_error(NGX_LOG_ALERT, s->connection->log, 0,\n                      ngx_regex_exec_n \" failed: %i on \\\"%V\\\" using \\\"%V\\\"\",\n                      rc, str, &re->name);\n        return NGX_ERROR;\n    }\n\n    for (i = 0; i < re->nvariables; i++) {\n\n        n = re->variables[i].capture;\n        index = re->variables[i].index;\n        vv = &s->variables[index];\n\n        vv->len = s->captures[n + 1] - s->captures[n];\n        vv->valid = 1;\n        vv->no_cacheable = 0;\n        vv->not_found = 0;\n        vv->data = &str->data[s->captures[n]];\n\n#if (NGX_DEBUG)\n        {\n        ngx_stream_variable_t  *v;\n\n        v = cmcf->variables.elts;\n\n        ngx_log_debug2(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,\n                       \"stream regex set $%V to \\\"%v\\\"\", &v[index].name, vv);\n        }\n#endif\n    }\n\n    s->ncaptures = rc * 2;\n    s->captures_data = str->data;\n\n    return NGX_OK;\n}\n\n#endif\n\n\nngx_int_t\nngx_stream_variables_add_core_vars(ngx_conf_t *cf)\n{\n    ngx_stream_variable_t        *cv, *v;\n    ngx_stream_core_main_conf_t  *cmcf;\n\n    cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);\n\n    cmcf->variables_keys = ngx_pcalloc(cf->temp_pool,\n                                       sizeof(ngx_hash_keys_arrays_t));\n    if (cmcf->variables_keys == NULL) {\n        return NGX_ERROR;\n    }\n\n    cmcf->variables_keys->pool = cf->pool;\n    cmcf->variables_keys->temp_pool = cf->pool;\n\n    if (ngx_hash_keys_array_init(cmcf->variables_keys, NGX_HASH_SMALL)\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    if (ngx_array_init(&cmcf->prefix_variables, cf->pool, 8,\n                       sizeof(ngx_stream_variable_t))\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    for (cv = ngx_stream_core_variables; cv->name.len; cv++) {\n        v = ngx_stream_add_variable(cf, &cv->name, cv->flags);\n        if (v == NULL) {\n            return NGX_ERROR;\n        }\n\n        *v = *cv;\n    }\n\n    return NGX_OK;\n}\n\n\nngx_int_t\nngx_stream_variables_init_vars(ngx_conf_t *cf)\n{\n    size_t                        len;\n    ngx_uint_t                    i, n;\n    ngx_hash_key_t               *key;\n    ngx_hash_init_t               hash;\n    ngx_stream_variable_t        *v, *av, *pv;\n    ngx_stream_core_main_conf_t  *cmcf;\n\n    /* set the handlers for the indexed stream variables */\n\n    cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);\n\n    v = cmcf->variables.elts;\n    pv = cmcf->prefix_variables.elts;\n    key = cmcf->variables_keys->keys.elts;\n\n    for (i = 0; i < cmcf->variables.nelts; i++) {\n\n        for (n = 0; n < cmcf->variables_keys->keys.nelts; n++) {\n\n            av = key[n].value;\n\n            if (v[i].name.len == key[n].key.len\n                && ngx_strncmp(v[i].name.data, key[n].key.data, v[i].name.len)\n                   == 0)\n            {\n                v[i].get_handler = av->get_handler;\n                v[i].data = av->data;\n\n                av->flags |= NGX_STREAM_VAR_INDEXED;\n                v[i].flags = av->flags;\n\n                av->index = i;\n\n                if (av->get_handler == NULL\n                    || (av->flags & NGX_STREAM_VAR_WEAK))\n                {\n                    break;\n                }\n\n                goto next;\n            }\n        }\n\n        len = 0;\n        av = NULL;\n\n        for (n = 0; n < cmcf->prefix_variables.nelts; n++) {\n            if (v[i].name.len >= pv[n].name.len && v[i].name.len > len\n                && ngx_strncmp(v[i].name.data, pv[n].name.data, pv[n].name.len)\n                   == 0)\n            {\n                av = &pv[n];\n                len = pv[n].name.len;\n            }\n        }\n\n        if (av) {\n            v[i].get_handler = av->get_handler;\n            v[i].data = (uintptr_t) &v[i].name;\n            v[i].flags = av->flags;\n\n            goto next;\n         }\n\n        if (v[i].get_handler == NULL) {\n            ngx_log_error(NGX_LOG_EMERG, cf->log, 0,\n                          \"unknown \\\"%V\\\" variable\", &v[i].name);\n            return NGX_ERROR;\n        }\n\n    next:\n        continue;\n    }\n\n\n    for (n = 0; n < cmcf->variables_keys->keys.nelts; n++) {\n        av = key[n].value;\n\n        if (av->flags & NGX_STREAM_VAR_NOHASH) {\n            key[n].key.data = NULL;\n        }\n    }\n\n\n    hash.hash = &cmcf->variables_hash;\n    hash.key = ngx_hash_key;\n    hash.max_size = cmcf->variables_hash_max_size;\n    hash.bucket_size = cmcf->variables_hash_bucket_size;\n    hash.name = \"variables_hash\";\n    hash.pool = cf->pool;\n    hash.temp_pool = NULL;\n\n    if (ngx_hash_init(&hash, cmcf->variables_keys->keys.elts,\n                      cmcf->variables_keys->keys.nelts)\n        != NGX_OK)\n    {\n        return NGX_ERROR;\n    }\n\n    cmcf->variables_keys = NULL;\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "src/stream/ngx_stream_variables.h",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#ifndef _NGX_STREAM_VARIABLES_H_INCLUDED_\n#define _NGX_STREAM_VARIABLES_H_INCLUDED_\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_stream.h>\n\n\ntypedef ngx_variable_value_t  ngx_stream_variable_value_t;\n\n#define ngx_stream_variable(v)     { sizeof(v) - 1, 1, 0, 0, 0, (u_char *) v }\n\ntypedef struct ngx_stream_variable_s  ngx_stream_variable_t;\n\ntypedef void (*ngx_stream_set_variable_pt) (ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data);\ntypedef ngx_int_t (*ngx_stream_get_variable_pt) (ngx_stream_session_t *s,\n    ngx_stream_variable_value_t *v, uintptr_t data);\n\n\n#define NGX_STREAM_VAR_CHANGEABLE   1\n#define NGX_STREAM_VAR_NOCACHEABLE  2\n#define NGX_STREAM_VAR_INDEXED      4\n#define NGX_STREAM_VAR_NOHASH       8\n#define NGX_STREAM_VAR_WEAK         16\n#define NGX_STREAM_VAR_PREFIX       32\n\n\nstruct ngx_stream_variable_s {\n    ngx_str_t                     name;   /* must be first to build the hash */\n    ngx_stream_set_variable_pt    set_handler;\n    ngx_stream_get_variable_pt    get_handler;\n    uintptr_t                     data;\n    ngx_uint_t                    flags;\n    ngx_uint_t                    index;\n};\n\n#define ngx_stream_null_variable  { ngx_null_string, NULL, NULL, 0, 0, 0 }\n\n\nngx_stream_variable_t *ngx_stream_add_variable(ngx_conf_t *cf, ngx_str_t *name,\n    ngx_uint_t flags);\nngx_int_t ngx_stream_get_variable_index(ngx_conf_t *cf, ngx_str_t *name);\nngx_stream_variable_value_t *ngx_stream_get_indexed_variable(\n    ngx_stream_session_t *s, ngx_uint_t index);\nngx_stream_variable_value_t *ngx_stream_get_flushed_variable(\n    ngx_stream_session_t *s, ngx_uint_t index);\n\nngx_stream_variable_value_t *ngx_stream_get_variable(ngx_stream_session_t *s,\n    ngx_str_t *name, ngx_uint_t key);\n\n\n#if (NGX_PCRE)\n\ntypedef struct {\n    ngx_uint_t                    capture;\n    ngx_int_t                     index;\n} ngx_stream_regex_variable_t;\n\n\ntypedef struct {\n    ngx_regex_t                  *regex;\n    ngx_uint_t                    ncaptures;\n    ngx_stream_regex_variable_t  *variables;\n    ngx_uint_t                    nvariables;\n    ngx_str_t                     name;\n} ngx_stream_regex_t;\n\n\ntypedef struct {\n    ngx_stream_regex_t           *regex;\n    void                         *value;\n} ngx_stream_map_regex_t;\n\n\nngx_stream_regex_t *ngx_stream_regex_compile(ngx_conf_t *cf,\n    ngx_regex_compile_t *rc);\nngx_int_t ngx_stream_regex_exec(ngx_stream_session_t *s, ngx_stream_regex_t *re,\n    ngx_str_t *str);\n\n#endif\n\n\ntypedef struct {\n    ngx_hash_combined_t           hash;\n#if (NGX_PCRE)\n    ngx_stream_map_regex_t       *regex;\n    ngx_uint_t                    nregex;\n#endif\n} ngx_stream_map_t;\n\n\nvoid *ngx_stream_map_find(ngx_stream_session_t *s, ngx_stream_map_t *map,\n    ngx_str_t *match);\n\n\nngx_int_t ngx_stream_variables_add_core_vars(ngx_conf_t *cf);\nngx_int_t ngx_stream_variables_init_vars(ngx_conf_t *cf);\n\n\nextern ngx_stream_variable_value_t  ngx_stream_variable_null_value;\nextern ngx_stream_variable_value_t  ngx_stream_variable_true_value;\n\n\n#endif /* _NGX_STREAM_VARIABLES_H_INCLUDED_ */\n"
  },
  {
    "path": "src/stream/ngx_stream_write_filter_module.c",
    "content": "\n/*\n * Copyright (C) Igor Sysoev\n * Copyright (C) Nginx, Inc.\n */\n\n\n#include <ngx_config.h>\n#include <ngx_core.h>\n#include <ngx_stream.h>\n\n\ntypedef struct {\n    ngx_chain_t  *from_upstream;\n    ngx_chain_t  *from_downstream;\n} ngx_stream_write_filter_ctx_t;\n\n\nstatic ngx_int_t ngx_stream_write_filter(ngx_stream_session_t *s,\n    ngx_chain_t *in, ngx_uint_t from_upstream);\nstatic ngx_int_t ngx_stream_write_filter_init(ngx_conf_t *cf);\n\n\nstatic ngx_stream_module_t  ngx_stream_write_filter_module_ctx = {\n    NULL,                                  /* preconfiguration */\n    ngx_stream_write_filter_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\n\nngx_module_t  ngx_stream_write_filter_module = {\n    NGX_MODULE_V1,\n    &ngx_stream_write_filter_module_ctx,   /* module context */\n    NULL,                                  /* module directives */\n    NGX_STREAM_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_stream_write_filter(ngx_stream_session_t *s, ngx_chain_t *in,\n    ngx_uint_t from_upstream)\n{\n    off_t                           size;\n    ngx_uint_t                      last, flush, sync;\n    ngx_chain_t                    *cl, *ln, **ll, **out, *chain;\n    ngx_connection_t               *c;\n    ngx_stream_write_filter_ctx_t  *ctx;\n\n    ctx = ngx_stream_get_module_ctx(s, ngx_stream_write_filter_module);\n\n    if (ctx == NULL) {\n        ctx = ngx_pcalloc(s->connection->pool,\n                          sizeof(ngx_stream_write_filter_ctx_t));\n        if (ctx == NULL) {\n            return NGX_ERROR;\n        }\n\n        ngx_stream_set_ctx(s, ctx, ngx_stream_write_filter_module);\n    }\n\n    if (from_upstream) {\n        c = s->connection;\n        out = &ctx->from_upstream;\n\n    } else {\n        c = s->upstream->peer.connection;\n        out = &ctx->from_downstream;\n    }\n\n    if (c->error) {\n        return NGX_ERROR;\n    }\n\n    size = 0;\n    flush = 0;\n    sync = 0;\n    last = 0;\n    ll = out;\n\n    /* find the size, the flush point and the last link of the saved chain */\n\n    for (cl = *out; cl; cl = cl->next) {\n        ll = &cl->next;\n\n        ngx_log_debug7(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"write old buf t:%d f:%d %p, pos %p, size: %z \"\n                       \"file: %O, size: %O\",\n                       cl->buf->temporary, cl->buf->in_file,\n                       cl->buf->start, cl->buf->pos,\n                       cl->buf->last - cl->buf->pos,\n                       cl->buf->file_pos,\n                       cl->buf->file_last - cl->buf->file_pos);\n\n        if (ngx_buf_size(cl->buf) == 0 && !ngx_buf_special(cl->buf)) {\n            ngx_log_error(NGX_LOG_ALERT, c->log, 0,\n                          \"zero size buf in writer \"\n                          \"t:%d r:%d f:%d %p %p-%p %p %O-%O\",\n                          cl->buf->temporary,\n                          cl->buf->recycled,\n                          cl->buf->in_file,\n                          cl->buf->start,\n                          cl->buf->pos,\n                          cl->buf->last,\n                          cl->buf->file,\n                          cl->buf->file_pos,\n                          cl->buf->file_last);\n\n            ngx_debug_point();\n            return NGX_ERROR;\n        }\n\n        if (ngx_buf_size(cl->buf) < 0) {\n            ngx_log_error(NGX_LOG_ALERT, c->log, 0,\n                          \"negative size buf in writer \"\n                          \"t:%d r:%d f:%d %p %p-%p %p %O-%O\",\n                          cl->buf->temporary,\n                          cl->buf->recycled,\n                          cl->buf->in_file,\n                          cl->buf->start,\n                          cl->buf->pos,\n                          cl->buf->last,\n                          cl->buf->file,\n                          cl->buf->file_pos,\n                          cl->buf->file_last);\n\n            ngx_debug_point();\n            return NGX_ERROR;\n        }\n\n        size += ngx_buf_size(cl->buf);\n\n        if (cl->buf->flush || cl->buf->recycled) {\n            flush = 1;\n        }\n\n        if (cl->buf->sync) {\n            sync = 1;\n        }\n\n        if (cl->buf->last_buf) {\n            last = 1;\n        }\n    }\n\n    /* add the new chain to the existent one */\n\n    for (ln = in; ln; ln = ln->next) {\n        cl = ngx_alloc_chain_link(c->pool);\n        if (cl == NULL) {\n            return NGX_ERROR;\n        }\n\n        cl->buf = ln->buf;\n        *ll = cl;\n        ll = &cl->next;\n\n        ngx_log_debug7(NGX_LOG_DEBUG_EVENT, c->log, 0,\n                       \"write new buf t:%d f:%d %p, pos %p, size: %z \"\n                       \"file: %O, size: %O\",\n                       cl->buf->temporary, cl->buf->in_file,\n                       cl->buf->start, cl->buf->pos,\n                       cl->buf->last - cl->buf->pos,\n                       cl->buf->file_pos,\n                       cl->buf->file_last - cl->buf->file_pos);\n\n        if (ngx_buf_size(cl->buf) == 0 && !ngx_buf_special(cl->buf)) {\n            ngx_log_error(NGX_LOG_ALERT, c->log, 0,\n                          \"zero size buf in writer \"\n                          \"t:%d r:%d f:%d %p %p-%p %p %O-%O\",\n                          cl->buf->temporary,\n                          cl->buf->recycled,\n                          cl->buf->in_file,\n                          cl->buf->start,\n                          cl->buf->pos,\n                          cl->buf->last,\n                          cl->buf->file,\n                          cl->buf->file_pos,\n                          cl->buf->file_last);\n\n            ngx_debug_point();\n            return NGX_ERROR;\n        }\n\n        if (ngx_buf_size(cl->buf) < 0) {\n            ngx_log_error(NGX_LOG_ALERT, c->log, 0,\n                          \"negative size buf in writer \"\n                          \"t:%d r:%d f:%d %p %p-%p %p %O-%O\",\n                          cl->buf->temporary,\n                          cl->buf->recycled,\n                          cl->buf->in_file,\n                          cl->buf->start,\n                          cl->buf->pos,\n                          cl->buf->last,\n                          cl->buf->file,\n                          cl->buf->file_pos,\n                          cl->buf->file_last);\n\n            ngx_debug_point();\n            return NGX_ERROR;\n        }\n\n        size += ngx_buf_size(cl->buf);\n\n        if (cl->buf->flush || cl->buf->recycled) {\n            flush = 1;\n        }\n\n        if (cl->buf->sync) {\n            sync = 1;\n        }\n\n        if (cl->buf->last_buf) {\n            last = 1;\n        }\n    }\n\n    *ll = NULL;\n\n    ngx_log_debug3(NGX_LOG_DEBUG_STREAM, c->log, 0,\n                   \"stream write filter: l:%ui f:%ui s:%O\", last, flush, size);\n\n    if (size == 0\n        && !(c->buffered & NGX_LOWLEVEL_BUFFERED)\n        && !(last && c->need_last_buf)\n        && !(flush && c->need_flush_buf))\n    {\n        if (last || flush || sync) {\n            for (cl = *out; cl; /* void */) {\n                ln = cl;\n                cl = cl->next;\n                ngx_free_chain(c->pool, ln);\n            }\n\n            *out = NULL;\n            c->buffered &= ~NGX_STREAM_WRITE_BUFFERED;\n\n            return NGX_OK;\n        }\n\n        ngx_log_error(NGX_LOG_ALERT, c->log, 0,\n                      \"the stream output chain is empty\");\n\n        ngx_debug_point();\n\n        return NGX_ERROR;\n    }\n\n    chain = c->send_chain(c, *out, 0);\n\n    ngx_log_debug1(NGX_LOG_DEBUG_STREAM, c->log, 0,\n                   \"stream write filter %p\", chain);\n\n    if (chain == NGX_CHAIN_ERROR) {\n        c->error = 1;\n        return NGX_ERROR;\n    }\n\n    for (cl = *out; cl && cl != chain; /* void */) {\n        ln = cl;\n        cl = cl->next;\n        ngx_free_chain(c->pool, ln);\n    }\n\n    *out = chain;\n\n    if (chain) {\n        if (c->shared) {\n            ngx_log_error(NGX_LOG_ALERT, c->log, 0,\n                          \"shared connection is busy\");\n            return NGX_ERROR;\n        }\n\n        c->buffered |= NGX_STREAM_WRITE_BUFFERED;\n        return NGX_AGAIN;\n    }\n\n    c->buffered &= ~NGX_STREAM_WRITE_BUFFERED;\n\n    if (c->buffered & NGX_LOWLEVEL_BUFFERED) {\n        return NGX_AGAIN;\n    }\n\n    return NGX_OK;\n}\n\n\nstatic ngx_int_t\nngx_stream_write_filter_init(ngx_conf_t *cf)\n{\n    ngx_stream_top_filter = ngx_stream_write_filter;\n\n    return NGX_OK;\n}\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/LICENSE",
    "content": "/* \n * Copyright (C) 2008-2011 Maxim Dounin\n *\n * Redistribution and use in source and binary forms, with or without\n * modification, are permitted provided that the following conditions\n * are met:\n * 1. Redistributions of source code must retain the above copyright\n *    notice, this list of conditions and the following disclaimer.\n * 2. Redistributions in binary form must reproduce the above copyright\n *    notice, this list of conditions and the following disclaimer in the\n *    documentation and/or other materials provided with the distribution.\n *\n * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND\n * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\n * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE\n * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\n * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\n * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\n * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT\n * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY\n * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF\n * SUCH DAMAGE.\n */\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/README",
    "content": "Test suite for nginx.\n\nUse prove to run tests as one usually do for perl tests.  Individual tests\nmay be run as well.\n\nNote: tests run nginx (and backend daemons if needed) listening on localhost\nand may use various ports in 8000 .. 8999 range.\n\nUsage:\n\n    $ TEST_NGINX_BINARY=/path/to/nginx prove .\n\nBy default tests expect nginx binary to be at ../nginx/objs/nginx.\n\nEnvironment variables:\n\nTEST_NGINX_BINARY\n\n    Sets path to nginx binary to be tested, defaults to \"../nginx/objs/nginx\".\n\nTEST_NGINX_MODULES\n\n    Sets path to modules directory, defaults to dirname of TEST_NGINX_BINARY.\n\nTEST_NGINX_VERBOSE\n\n    Be a bit more verbose (in particular, print requests sent and responses\n    got from nginx).  Note that this requires prove -v (or HARNESS_VERBOSE).\n\nTEST_NGINX_LEAVE\n\n    If set, temporary directory with configs and logs won't be deleted on test\n    completion.  Useful for debugging.\n\nTEST_NGINX_CATLOG\n\n    Cat error log to stdout after test completion.  Useful for debugging.\n\nTEST_NGINX_UNSAFE\n\n    Run unsafe tests.\n\nTEST_NGINX_GLOBALS\n\n    Sets additional directives in main context.\n\nTEST_NGINX_GLOBALS_HTTP\n\n    Sets additional directives in http context.\n\nTEST_NGINX_GLOBALS_STREAM\n\n    Sets additional directives in stream context.\n\nHappy testing!\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/access.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n\n# Tests for nginx access module.\n\n# At the moment only the new \"unix:\" syntax is tested (cf \"all\").\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy access unix/);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location /inet/ {\n            proxy_pass http://127.0.0.1:8081/;\n        }\n\n        location /inet6/ {\n            proxy_pass http://[::1]:%%PORT_8081%%/;\n        }\n\n        location /unix/ {\n            proxy_pass http://unix:%%TESTDIR%%/unix.sock:/;\n        }\n\n    }\n\n    server {\n        listen       127.0.0.1:8081;\n        listen       [::1]:%%PORT_8081%%;\n        listen       unix:%%TESTDIR%%/unix.sock;\n\n        location /allow_all {\n            allow all;\n        }\n\n        location /allow_unix {\n            allow unix:;\n        }\n\n        location /deny_all {\n            deny all;\n        }\n\n        location /deny_unix {\n            deny unix:;\n        }\n    }\n}\n\nEOF\n\n$t->try_run('no inet6 support')->plan(12);\n\n###############################################################################\n\n# tests with inet socket\n\nlike(http_get('/inet/allow_all'), qr/404 Not Found/, 'inet allow all');\nlike(http_get('/inet/allow_unix'), qr/404 Not Found/, 'inet allow unix');\nlike(http_get('/inet/deny_all'), qr/403 Forbidden/, 'inet deny all');\nlike(http_get('/inet/deny_unix'), qr/404 Not Found/, 'inet deny unix');\n\n# tests with inet6 socket\n\nlike(http_get('/inet6/allow_all'), qr/404 Not Found/, 'inet6 allow all');\nlike(http_get('/inet6/allow_unix'), qr/404 Not Found/, 'inet6 allow unix');\nlike(http_get('/inet6/deny_all'), qr/403 Forbidden/, 'inet6 deny all');\nlike(http_get('/inet6/deny_unix'), qr/404 Not Found/, 'inet6 deny unix');\n\n# tests with unix socket\n\nlike(http_get('/unix/allow_all'), qr/404 Not Found/, 'unix allow all');\nlike(http_get('/unix/allow_unix'), qr/404 Not Found/, 'unix allow unix');\nlike(http_get('/unix/deny_all'), qr/403 Forbidden/, 'unix deny all');\nlike(http_get('/unix/deny_unix'), qr/403 Forbidden/, 'unix deny unix');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/access_log.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for access_log.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http rewrite gzip/)->plan(19)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nworker_processes 1;  # NOTE: The default value of Tengine worker_processes directive is `worker_processes auto;`.\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    log_format test \"$uri:$status\";\n    log_format long \"long line $uri:$status\";\n    log_format addr \"$remote_addr:$remote_port:$server_addr:$server_port\";\n    log_format binary $binary_remote_addr;\n\n    log_format default  escape=default  $uri$arg_b$arg_c;\n    log_format none     escape=none     $uri$arg_b$arg_c;\n    log_format json     escape=json     $uri$arg_b$arg_c;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location /combined {\n            access_log %%TESTDIR%%/combined.log;\n            return 200 OK;\n\n            location /combined/off {\n                access_log off;\n                return 200 OK;\n            }\n        }\n\n        location /filtered {\n            access_log %%TESTDIR%%/filtered.log test\n                       if=$arg_logme;\n            return 200 OK;\n        }\n\n        location /filtered/complex {\n            access_log %%TESTDIR%%/complex.log test\n                       if=$arg_logme$arg_logmetoo;\n            return 200 OK;\n        }\n\n        location /filtered/noreuse {\n            access_log %%TESTDIR%%/noreuse.log test buffer=16k\n                       if=$arg_a;\n            access_log %%TESTDIR%%/noreuse.log test buffer=16k\n                       if=$arg_b;\n            return 200 OK;\n        }\n\n        location /compressed {\n            access_log %%TESTDIR%%/compressed.log test\n                       gzip buffer=1m flush=100ms;\n            return 200 OK;\n        }\n\n        location /multi {\n            access_log %%TESTDIR%%/multi1.log test;\n            access_log %%TESTDIR%%/multi2.log test;\n            access_log %%TESTDIR%%/long.log long;\n            return 200 OK;\n        }\n\n        location /varlog {\n            access_log %%TESTDIR%%/varlog_${arg_logname} test;\n            return 200 OK;\n        }\n\n        location /cache {\n            open_log_file_cache max=3 inactive=20s valid=1m min_uses=2;\n            access_log %%TESTDIR%%/dir/cache_${arg_logname} test;\n            return 200 OK;\n        }\n\n        location /addr {\n            access_log %%TESTDIR%%/addr.log addr;\n        }\n\n        location /binary {\n            access_log %%TESTDIR%%/binary.log binary;\n        }\n\n        location /escape {\n            access_log %%TESTDIR%%/test.log default;\n            access_log %%TESTDIR%%/none.log none;\n            access_log %%TESTDIR%%/json.log json;\n        }\n    }\n}\n\nEOF\n\nmy $d = $t->testdir();\n\nmkdir \"$d/dir\";\n\n$t->run();\n\n###############################################################################\n\nhttp_get('/combined');\nhttp_get('/combined/off');\n\nhttp_get('/filtered');\nhttp_get('/filtered/empty?logme=');\nhttp_get('/filtered/zero?logme=0');\nhttp_get('/filtered/good?logme=1');\nhttp_get('/filtered/work?logme=yes');\n\nhttp_get('/filtered/complex');\nhttp_get('/filtered/complex/one?logme=1');\nhttp_get('/filtered/complex/two?logmetoo=1');\nhttp_get('/filtered/complex/either1?logme=A&logmetoo=B');\nhttp_get('/filtered/complex/either2?logme=A');\nhttp_get('/filtered/complex/either3?logmetoo=B');\nhttp_get('/filtered/complex/either4?logme=0&logmetoo=0');\nhttp_get('/filtered/complex/neither?logme=&logmetoo=');\n\nhttp_get('/filtered/noreuse1/zero?a=0');\nhttp_get('/filtered/noreuse1/good?a=1');\nhttp_get('/filtered/noreuse2/zero?b=0');\nhttp_get('/filtered/noreuse2/good?b=1');\n\nhttp_get('/compressed');\n\nhttp_get('/multi');\n\nhttp_get('/varlog');\nhttp_get('/varlog?logname=');\nhttp_get('/varlog?logname=0');\nhttp_get('/varlog?logname=filename');\n\nmy $s = http('', start => 1);\nmy $addr = $s->sockhost();\nmy $port = $s->sockport();\nmy $saddr = $s->peerhost();\nmy $sport = $s->peerport();\nhttp_get('/addr', socket => $s);\n\nhttp_get('/binary');\n\n# /escape/\"1 %1B%1C \"?c=2\nhttp_get('/escape/%221%20%1B%1C%20%22?c=2');\n\nhttp_get('/cache?logname=lru');\nhttp_get('/cache?logname=lru');\nhttp_get('/cache?logname=once');\nhttp_get('/cache?logname=first');\nhttp_get('/cache?logname=first');\nhttp_get('/cache?logname=second');\nhttp_get('/cache?logname=second');\n\nrename \"$d/dir\", \"$d/dir_moved\";\n\nhttp_get('/cache?logname=lru');\nhttp_get('/cache?logname=once');\nhttp_get('/cache?logname=first');\nhttp_get('/cache?logname=second');\n\nrename \"$d/dir_moved\",  \"$d/dir\";\n\n# wait for file to appear with nonzero size thanks to the flush parameter\n\nfor (1 .. 10) {\n\tlast if -s \"$d/compressed.log\";\n\tselect undef, undef, undef, 0.1;\n}\n\n# verify that \"gzip\" parameter turns on compression\n\nmy $log;\n\nSKIP: {\n\teval { require IO::Uncompress::Gunzip; };\n\tskip(\"IO::Uncompress::Gunzip not installed\", 1) if $@;\n\n\tmy $gzipped = $t->read_file('compressed.log');\n\tIO::Uncompress::Gunzip::gunzip(\\$gzipped => \\$log);\n\tlike($log, qr!^/compressed:200!s, 'compressed log - flush time');\n}\n\n# now verify all other logs\n\n$t->stop();\n\n\n# verify that by default, 'combined' format is used, 'off' disables logging\n\nlike($t->read_file('combined.log'),\n\tqr!^\\Q$addr - - [\\E .*\n\t\t\\Q] \"GET /combined HTTP/1.0\" 200 2 \"-\" \"-\"\\E$!x,\n\t'default log format');\n\n# verify that log filtering works\n\n$log = $t->read_file('filtered.log');\nis($log, \"/filtered/good:200\\n/filtered/work:200\\n\", 'log filtering');\n\n# verify \"if=\" argument works with complex value\n\nmy $exp_complex = <<'EOF';\n/filtered/complex/one:200\n/filtered/complex/two:200\n/filtered/complex/either1:200\n/filtered/complex/either2:200\n/filtered/complex/either3:200\n/filtered/complex/either4:200\nEOF\n\nis($t->read_file('complex.log'), $exp_complex, 'if with complex value');\n\n# buffer created with false \"if\" is not reused among multiple access_log\n\n$log = $t->read_file('noreuse.log');\nis($log, \"/filtered/noreuse1/good:200\\n/filtered/noreuse2/good:200\\n\",\n\t'log filtering with buffering');\n\n# multiple logs in a same location\n\nis($t->read_file('multi1.log'), \"/multi:200\\n\", 'multiple logs 1');\n\n# same content in the second log\n\nis($t->read_file('multi2.log'), \"/multi:200\\n\", 'multiple logs 2');\n\nis($t->read_file('long.log'), \"long line /multi:200\\n\", 'long line format');\n\n# test log destinations with variables\n\nis($t->read_file('varlog_0'), \"/varlog:200\\n\", 'varlog literal zero name');\nis($t->read_file('varlog_filename'), \"/varlog:200\\n\", 'varlog good name');\n\nis($t->read_file('addr.log'), \"$addr:$port:$saddr:$sport\\n\", 'addr');\n\n# binary data is escaped\n# that's \"\\\\x7F\\\\x00\\\\x00\\\\x01\\n\" in $binary_remote_addr for \"127.0.0.1\"\n\nmy $expected = join '', map { sprintf \"\\\\x%02X\", $_ } split /\\./, $addr;\n\nis($t->read_file('binary.log'), \"$expected\\n\", 'binary');\n\n# characters escaping\n\nis($t->read_file('test.log'),\n\t'/escape/\\x221 \\x1B\\x1C \\x22-2' . \"\\n\", 'escape - default');\nis($t->read_file('none.log'),\n\t\"/escape/\\\"1 \\x1B\\x1C \\\"2\\n\", 'escape - none');\nis($t->read_file('json.log'),\n\t'/escape/\\\"1 \\u001B\\u001C \\\"2' . \"\\n\", 'escape - json');\n\nSKIP: {\nskip 'win32', 4 if $^O eq 'MSWin32';\n\nis(@{[$t->read_file('/dir/cache_lru') =~ /\\//g]}, 2, 'cache - closed lru');\nis(@{[$t->read_file('/dir/cache_once') =~ /\\//g]}, 1, 'cache - min_uses');\nis(@{[$t->read_file('/dir/cache_first') =~ /\\//g]}, 3, 'cache - cached 1');\nis(@{[$t->read_file('/dir/cache_second') =~ /\\//g]}, 3, 'cache - cached 2');\n\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/access_log_variables.t",
    "content": "#!/usr/bin/perl\n\n# (C) Andrey Zelenkov\n# (C) Nginx, Inc.\n\n# Tests for log module variables.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http rewrite/)->plan(6)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    log_format time_iso8601  '$uri $time_iso8601';\n    log_format time_local    '$uri $time_local';\n    log_format msec          '$uri $msec';\n    log_format request       '$uri $status $request_length $request_time';\n    log_format bytes         '$uri $bytes_sent $body_bytes_sent';\n    log_format pipe          '$uri $pipe';\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location /iso8601 {\n            access_log %%TESTDIR%%/iso8601.log time_iso8601;\n            return 200;\n        }\n\n        location /local {\n            access_log %%TESTDIR%%/local.log time_local;\n            return 200;\n        }\n\n        location /msec {\n            access_log %%TESTDIR%%/msec.log msec;\n            return 200;\n        }\n\n        location /request {\n            access_log %%TESTDIR%%/request.log request;\n            return 200;\n        }\n\n        location /bytes {\n           access_log %%TESTDIR%%/bytes.log bytes;\n           return 200 OK;\n        }\n\n        location /pipe {\n            access_log %%TESTDIR%%/pipe.log pipe;\n            return 200;\n        }\n    }\n}\n\nEOF\n\n$t->run();\n\n###############################################################################\n\nhttp_get('/iso8601');\nhttp_get('/local');\nhttp_get('/msec');\nhttp_get('/request');\nmy $bytes_sent = length http_get('/bytes');\n\n# pipelined requests\n\nhttp(<<EOF);\nGET /pipe HTTP/1.1\nHost: localhost\n\nGET /pipe HTTP/1.1\nHost: localhost\nConnection: close\n\nEOF\n\n$t->stop();\n\nmy $log = $t->read_file('iso8601.log');\nlike($log, qr!/iso8601 \\d{4}-\\d\\d-\\d\\dT\\d\\d:\\d\\d:\\d\\d[+-]\\d\\d:\\d\\d!,\n\t'time_iso8601');\n\n$log = $t->read_file('local.log');\nlike($log, qr!/local \\d\\d/[A-Z][a-z]{2}/\\d{4}:\\d\\d:\\d\\d:\\d\\d [+-]\\d{4}!,\n\t'time_local');\n\n$log = $t->read_file('msec.log');\nlike($log, qr!/msec [\\d\\.]+!, 'msec');\n\n$log = $t->read_file('request.log');\nlike($log, qr!/request 200 39 [\\d\\.]+!, 'request');\n\n$log = $t->read_file('bytes.log');\nis($log, \"/bytes $bytes_sent 2\\n\", 'bytes sent');\n\n$log = $t->read_file('pipe.log');\nis($log, \"/pipe .\\n/pipe p\\n\", 'pipe');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/addition.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for addition module.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http rewrite addition/)->plan(9);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location /regular {\n            return 200 \"body\";\n        }\n\n        location /b.html {\n            add_before_body /add_before;\n            return 200 \"body\";\n        }\n\n        location /a.html {\n            add_after_body /add_after;\n            return 200 \"body\";\n        }\n\n        location /ba.html {\n            add_before_body /add_before;\n            add_after_body /add_after;\n            return 200 \"body\";\n        }\n\n        location /notype {\n            add_before_body /add_before;\n            add_after_body /add_after;\n            return 200 \"body\";\n        }\n\n        location /notype2 {\n            addition_types text/plain;\n            add_after_body /add_after;\n            return 200 \"body\";\n        }\n\n        location /notype.html {\n            types {}\n            add_before_body /add_before;\n            return 200 \"body\";\n        }\n\n        location /add_before {\n            return 200 \"before\";\n        }\n\n        location /add_after {\n            return 200 \"after\";\n        }\n\n        location /self.html {\n            add_after_body /self.html;\n            return 200 \"self\";\n        }\n\n        location /return202.html {\n            add_after_body /add_after;\n            return 202 \"body\";\n        }\n    }\n}\n\nEOF\n\n$t->run();\n\n###############################################################################\n\nlike(http_get('/regular'), qr/^body$/ms, 'no addition');\nlike(http_get('/b.html'), qr/^beforebody$/ms, 'add_before');\nlike(http_get('/a.html'), qr/^bodyafter$/ms, 'add_after');\nlike(http_get('/ba.html'), qr/^beforebodyafter$/ms, 'both');\nlike(http_get('/notype'), qr/^body$/ms, 'no content type');\nlike(http_get('/notype2'), qr/^bodyafter$/ms, 'addition_types');\nlike(http_get('/notype.html'), qr/^body$/ms, 'empty content type');\nlike(http_get('/self.html'), qr/^selfself$/ms, 'self');\nlike(http_get('/return202.html'), qr/HTTP\\/1.. 202.*^body$/ms, 'not 200');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/addition_buffered.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for addition module with buffered data from other filters.\n\n# In particular, sub filter may have a partial match buffered.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy sub addition/)->plan(1);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / { }\n        location /proxy/ {\n            sub_filter foo bar;\n            add_after_body /after.html;\n            proxy_pass http://127.0.0.1:8080/;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('after.html', 'after');\n$t->write_file('body.html', 'XXXXX');\n\n$t->run();\n\n###############################################################################\n\n# if data is buffered, there should be no interleaved data in output\n\nlike(http_get('/proxy/body.html'), qr/^XXXXXafter$/m, 'request');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/auth_basic.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for auth basic module.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse MIME::Base64;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http auth_basic/)->plan(24)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            auth_basic           \"closed site\";\n            auth_basic_user_file %%TESTDIR%%/htpasswd;\n\n            location /inner {\n                auth_basic off;\n                alias %%TESTDIR%%/;\n            }\n\n            location /var {\n                # prepended with conf_prefix\n                auth_basic_user_file $arg_f;\n                alias %%TESTDIR%%/;\n\t    }\n        }\n    }\n}\n\nEOF\n\n$t->write_file('index.html', 'SEETHIS');\n\n$t->write_file(\n\t'htpasswd',\n\t'crypt:' . (crypt('password', 'salt') || '') . \"\\n\" .\n\t'crypt1:' . (crypt('password', '$1$salt$') || '') . \"\\n\" .\n\t'crypt2:' . '$1$' . \"\\n\" .\n\t'apr1:' . '$apr1$salt$Xxd1irWT9ycqoYxGFn4cb.' . \"\\n\" .\n\t'apr12:' . '$apr1$' . \"\\n\" .\n\t'plain:' . '{PLAIN}password' . \"\\n\" .\n\t'ssha:' . '{SSHA}yI6cZwQadOA1e+/f+T+H3eCQQhRzYWx0' . \"\\n\" .\n\t'ssha2:' . '{SSHA}_____wQadOA1e+/f+T+H3eCQQhRzYWx0' . \"\\n\" .\n\t'ssha3:' . '{SSHA}Zm9vCg==' . \"\\n\" .\n\t'sha:' . '{SHA}W6ph5Mm5Pz8GgiULbPgzG37mj9g=' . \"\\n\" .\n\t'sha2:' . '{SHA}_____Mm5Pz8GgiULbPgzG37mj9g=' . \"\\n\" .\n\t'sha3:' . '{SHA}Zm9vCg==' . \"\\n\"\n);\n\n$t->run();\n\n###############################################################################\n\nlike(http_get('/'), qr!401 Unauthorized!ms, 'rejects unathorized');\n\nSKIP: {\n\nskip 'no crypt on win32', 5 if $^O eq 'MSWin32';\n\nlike(http_get_auth('/', 'crypt', 'password'), qr!SEETHIS!, 'normal crypt');\nunlike(http_get_auth('/', 'crypt', '123'), qr!SEETHIS!, 'normal wrong');\n\nlike(http_get_auth('/', 'crypt1', 'password'), qr!SEETHIS!, 'crypt $1$ (md5)');\nunlike(http_get_auth('/', 'crypt1', '123'), qr!SEETHIS!, 'crypt $1$ wrong');\n\nlike(http_get_auth('/', 'crypt2', '1'), qr!401 Unauthorized!,\n\t'crypt $1$ broken');\n\n}\n\nlike(http_get_auth('/', 'apr1', 'password'), qr!SEETHIS!, 'apr1 md5');\nlike(http_get_auth('/', 'plain', 'password'), qr!SEETHIS!, 'plain password');\nlike(http_get_auth('/', 'ssha', 'password'), qr!SEETHIS!, 'ssha');\nlike(http_get_auth('/', 'sha', 'password'), qr!SEETHIS!, 'sha');\n\nunlike(http_get_auth('/', 'apr1', '123'), qr!SEETHIS!, 'apr1 md5 wrong');\nunlike(http_get_auth('/', 'plain', '123'), qr!SEETHIS!, 'plain wrong');\nunlike(http_get_auth('/', 'ssha', '123'), qr!SEETHIS!, 'ssha wrong');\nunlike(http_get_auth('/', 'sha', '123'), qr!SEETHIS!, 'sha wrong');\n\nlike(http_get_auth('/', 'apr12', '1'), qr!401 Unauthorized!, 'apr1 md5 broken');\nlike(http_get_auth('/', 'ssha2', '1'), qr!401 Unauthorized!, 'ssha broken 1');\nlike(http_get_auth('/', 'ssha3', '1'), qr!401 Unauthorized!, 'ssha broken 2');\nlike(http_get_auth('/', 'sha2', '1'), qr!401 Unauthorized!, 'sha broken 1');\nlike(http_get_auth('/', 'sha3', '1'), qr!401 Unauthorized!, 'sha broken 2');\n\nlike(http_get_auth('/', 'notfound', '1'), qr!401 Unauthorized!, 'not found');\nlike(http_get('/inner/'), qr!SEETHIS!, 'inner off');\n\nlike(http_get_auth('/var/?f=htpasswd', 'apr1', 'password'), qr!SEETHIS!,\n\t'user file variable');\nunlike(http_get_auth('/var/?f=nx', 'apr1', 'password'), qr!SEETHIS!,\n\t'user file variable not found');\nunlike(http_get_auth('/var/', 'apr1', 'password'), qr!SEETHIS!,\n\t'user file variable bad value');\n\n###############################################################################\n\nsub http_get_auth {\n\tmy ($url, $user, $password) = @_;\n\n\tmy $auth = encode_base64($user . ':' . $password, '');\n\n\treturn http(<<EOF);\nGET $url HTTP/1.0\nHost: localhost\nAuthorization: Basic $auth\n\nEOF\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/auth_delay.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for auth_delay directive using auth basic module.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse MIME::Base64;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http auth_basic/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            auth_delay           2s;\n\n            auth_basic           \"closed site\";\n            auth_basic_user_file %%TESTDIR%%/htpasswd;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('index.html', '');\n$t->write_file('htpasswd', 'user:' . '{PLAIN}good' . \"\\n\");\n\n$t->run()->plan(4);\n\n###############################################################################\n\nmy $t1 = time();\nlike(http_get_auth('/', 'user', 'bad'), qr/401 Unauthorize/, 'not authorized');\ncmp_ok(time() - $t1, '>=', 2, 'auth delay');\n\n$t1 = time();\nlike(http_get_auth('/', 'user', 'good'), qr/200 OK/, 'authorized');\ncmp_ok(time() - $t1, '<', 2, 'no delay');\n\n###############################################################################\n\nsub http_get_auth {\n\tmy ($url, $user, $password) = @_;\n\n\tmy $auth = encode_base64($user . ':' . $password, '');\n\n\treturn http(<<EOF);\nGET $url HTTP/1.0\nHost: localhost\nAuthorization: Basic $auth\n\nEOF\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/auth_request.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for auth request module.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse Socket qw/ CRLF /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()\n\t->has(qw/http rewrite proxy cache fastcgi auth_basic auth_request/)\n\t->plan(20);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    proxy_cache_path   %%TESTDIR%%/cache  levels=1:2\n                       keys_zone=NAME:1m;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            return 444;\n        }\n\n        location /open {\n            auth_request /auth-open;\n        }\n        location = /auth-open {\n            return 204;\n        }\n\n        location /open-static {\n            auth_request /auth-open-static;\n        }\n        location = /auth-open-static {\n            # nothing, use static file\n        }\n\n        location /unauthorized {\n            auth_request /auth-unauthorized;\n        }\n        location = /auth-unauthorized {\n            return 401;\n        }\n\n        location /forbidden {\n            auth_request /auth-forbidden;\n        }\n        location = /auth-forbidden {\n            return 403;\n        }\n\n        location /error {\n            auth_request /auth-error;\n        }\n        location = /auth-error {\n            return 404;\n        }\n\n        location /off {\n            auth_request off;\n        }\n\n        location /proxy {\n            auth_request /auth-proxy;\n        }\n        location = /auth-proxy {\n            proxy_pass http://127.0.0.1:8080/auth-basic;\n            proxy_pass_request_body off;\n            proxy_set_header Content-Length \"\";\n        }\n        location = /auth-basic {\n            auth_basic \"restricted\";\n            auth_basic_user_file %%TESTDIR%%/htpasswd;\n        }\n\n        location = /proxy-double {\n            proxy_pass http://127.0.0.1:8080/auth-error;\n            proxy_intercept_errors on;\n            error_page 404 = /proxy-double-fallback;\n            client_body_buffer_size 4k;\n        }\n        location = /proxy-double-fallback {\n            auth_request /auth-proxy-double;\n            proxy_pass http://127.0.0.1:8080/auth-open;\n        }\n        location = /auth-proxy-double {\n            proxy_pass http://127.0.0.1:8080/auth-open;\n            proxy_pass_request_body off;\n            proxy_set_header Content-Length \"\";\n        }\n\n        location /proxy-cache {\n            auth_request /auth-proxy-cache;\n        }\n        location = /auth-proxy-cache {\n            proxy_pass http://127.0.0.1:8080/auth-basic;\n            proxy_pass_request_body off;\n            proxy_set_header Content-Length \"\";\n            proxy_cache NAME;\n            proxy_cache_valid 1m;\n        }\n\n        location /proxy-multi {\n            auth_request /auth-proxy-multi;\n        }\n        location = /auth-proxy-multi {\n            proxy_pass http://127.0.0.1:8080/auth-multi;\n            proxy_pass_request_body off;\n            proxy_set_header Content-Length \"\";\n        }\n        location = /auth-multi {\n            add_header WWW-Authenticate foo always;\n            add_header WWW-Authenticate bar always;\n            return 401;\n        }\n\n        location /fastcgi {\n            auth_request /auth-fastcgi;\n        }\n        location = /auth-fastcgi {\n            fastcgi_pass 127.0.0.1:8081;\n            fastcgi_pass_request_body off;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('htpasswd', 'user:{PLAIN}secret' . \"\\n\");\n$t->write_file('auth-basic', 'INVISIBLE');\n$t->write_file('auth-open-static', 'INVISIBLE');\n$t->run();\n\n###############################################################################\n\nlike(http_get('/open'), qr/ 404 /, 'auth open');\nlike(http_get('/unauthorized'), qr/ 401 /, 'auth unauthorized');\nlike(http_get('/forbidden'), qr/ 403 /, 'auth forbidden');\nlike(http_get('/error'), qr/ 500 /, 'auth error');\nlike(http_get('/off'), qr/ 404 /, 'auth off');\n\nlike(http_post('/open'), qr/ 404 /, 'auth post open');\nlike(http_post('/unauthorized'), qr/ 401 /, 'auth post unauthorized');\n\nlike(http_get('/open-static'), qr/ 404 /, 'auth open static');\nunlike(http_get('/open-static'), qr/INVISIBLE/, 'auth static no content');\n\nlike(http_get('/proxy'), qr/ 401 /, 'proxy auth unauthorized');\nlike(http_get('/proxy'), qr/WWW-Authenticate: Basic realm=\"restricted\"/,\n\t'proxy auth has www-authenticate');\nlike(http_get_auth('/proxy'), qr/ 404 /, 'proxy auth pass');\nunlike(http_get_auth('/proxy'), qr/INVISIBLE/, 'proxy auth no content');\n\nlike(http_post('/proxy'), qr/ 401 /, 'proxy auth post');\n\nlike(http_get_auth('/proxy-cache'), qr/ 404 /, 'proxy auth with cache');\nlike(http_get('/proxy-cache'), qr/ 404 /, 'proxy auth cached');\n\n# Consider the following scenario:\n#\n# 1. proxy_pass reads request body, then goes to fallback via error_page\n# 2. auth request uses proxy_pass, and upstream module closes request body file\n#    in ngx_http_upstream_send_response()\n# 3. oops: fallback has no body\n#\n# To prevent this we always allocate fake request body for auth request.\n#\n# Note that this doesn't happen when using header_only as relevant code\n# in ngx_http_upstream_send_response() isn't reached.  It may be reached\n# with proxy_cache or proxy_store, but they will shutdown client connection\n# in case of header_only and hence do not work for us at all.\n\nlike(http_post_big('/proxy-double'), qr/ 204 /, 'proxy auth with body read');\n\n# Multiple WWW-Authenticate headers (ticket #485).\n\nTODO: {\nlocal $TODO = 'not yet' unless $t->has_version('1.23.0');\n\nlike(http_get('/proxy-multi-auth'), qr/WWW-Authenticate: foo.*bar/s,\n\t'multiple www-authenticate headers');\n\n}\n\nSKIP: {\n\teval { require FCGI; };\n\tskip 'FCGI not installed', 2 if $@;\n\tskip 'win32', 2 if $^O eq 'MSWin32';\n\n\t$t->run_daemon(\\&fastcgi_daemon);\n\t$t->waitforsocket('127.0.0.1:' . port(8081));\n\n\tlike(http_get('/fastcgi'), qr/ 404 /, 'fastcgi auth open');\n\tunlike(http_get('/fastcgi'), qr/INVISIBLE/, 'fastcgi auth no content');\n}\n\n###############################################################################\n\nsub http_get_auth {\n\tmy ($url, %extra) = @_;\n\treturn http(<<EOF, %extra);\nGET $url HTTP/1.0\nHost: localhost\nAuthorization: Basic dXNlcjpzZWNyZXQ=\n\nEOF\n}\n\nsub http_post {\n\tmy ($url, %extra) = @_;\n\n\tmy $p = \"POST $url HTTP/1.0\" . CRLF .\n\t\t\"Host: localhost\" . CRLF .\n\t\t\"Content-Length: 10\" . CRLF .\n\t\tCRLF .\n\t\t\"1234567890\";\n\n\treturn http($p, %extra);\n}\n\nsub http_post_big {\n\tmy ($url, %extra) = @_;\n\n\tmy $p = \"POST $url HTTP/1.0\" . CRLF .\n\t\t\"Host: localhost\" . CRLF .\n\t\t\"Content-Length: 10240\" . CRLF .\n\t\tCRLF .\n\t\t(\"1234567890\" x 1024);\n\n\treturn http($p, %extra);\n}\n\n###############################################################################\n\nsub fastcgi_daemon {\n\tmy $socket = FCGI::OpenSocket('127.0.0.1:' . port(8081), 5);\n\tmy $request = FCGI::Request(\\*STDIN, \\*STDOUT, \\*STDERR, \\%ENV,\n\t\t$socket);\n\n\twhile ($request->Accept() >= 0) {\n\t\tprint <<EOF;\nContent-Type: text/html\n\nINVISIBLE\nEOF\n\t}\n\n\tFCGI::CloseSocket($socket);\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/auth_request_satisfy.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for auth request module with satisfy directive.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()\n\t->has(qw/http rewrite access auth_basic auth_request/)\n\t->plan(18);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            return 444;\n        }\n\n        location /all/allow {\n            satisfy all;\n            allow all;\n            auth_request /auth;\n        }\n\n        location /all/deny {\n            satisfy all;\n            deny all;\n            auth_request /auth;\n        }\n\n        location /all/basic {\n            satisfy all;\n            auth_basic \"restricted\";\n            auth_basic_user_file %%TESTDIR%%/htpasswd;\n            auth_request /auth;\n        }\n\n        location /any/allow {\n            satisfy any;\n            allow all;\n            auth_request /auth;\n        }\n\n        location /any/deny {\n            satisfy any;\n            deny all;\n            auth_request /auth;\n        }\n\n        location /any/basic {\n            satisfy any;\n            auth_basic \"restricted\";\n            auth_basic_user_file %%TESTDIR%%/htpasswd;\n            auth_request /auth;\n        }\n\n        location = /auth {\n            if ($request_uri ~ \"open$\") {\n                return 204;\n            }\n            if ($request_uri ~ \"unauthorized$\") {\n                return 401;\n            }\n            if ($request_uri ~ \"forbidden$\") {\n                return 403;\n            }\n        }\n    }\n}\n\nEOF\n\n$t->write_file('htpasswd', 'user:{PLAIN}secret' . \"\\n\");\n$t->run();\n\n###############################################################################\n\n# satisfy all - first 401/403 wins\n\nlike(http_get('/all/allow+open'), qr/ 404 /, 'all allow+open');\nlike(http_get('/all/allow+unauthorized'), qr/ 401 /, 'all allow+unauthorized');\nlike(http_get('/all/allow+forbidden'), qr/ 403 /, 'all allow+forbidden');\n\nlike(http_get('/all/deny+open'), qr/ 403 /, 'all deny+open');\nlike(http_get('/all/deny+unauthorized'), qr/ 403 /, 'all deny+unauthorized');\nlike(http_get('/all/deny+forbidden'), qr/ 403 /, 'all deny+forbidden');\n\nlike(http_get('/all/basic+open'), qr/ 401 /, 'all basic+open');\nlike(http_get('/all/basic+unauthorized'), qr/ 401 /, 'all basic+unauthorized');\nlike(http_get('/all/basic+forbidden'), qr/ 401 /, 'all basic+forbidden');\n\n# satisfy any - first ok wins\n# additionally, 403 shouldn't override 401 status\n\nlike(http_get('/any/allow+open'), qr/ 404 /, 'any allow+open');\nlike(http_get('/any/allow+unauthorized'), qr/ 404 /, 'any allow+unauthorized');\nlike(http_get('/any/allow+forbidden'), qr/ 404 /, 'any allow+forbidden');\n\nlike(http_get('/any/deny+open'), qr/ 404 /, 'any deny+open');\nlike(http_get('/any/deny+unauthorized'), qr/ 401 /, 'any deny+unauthorized');\nlike(http_get('/any/deny+forbidden'), qr/ 403 /, 'any deny+forbidden');\n\nlike(http_get('/any/basic+open'), qr/ 404 /, 'any basic+open');\nlike(http_get('/any/basic+unauthorized'), qr/ 401 /, 'any basic+unauthorized');\nlike(http_get('/any/basic+forbidden'), qr/ 401 /, 'any basic+forbidden');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/auth_request_set.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for auth request module, auth_request_set.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http rewrite proxy auth_request/)\n\t->plan(6);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location = /t1.html {\n            auth_request /auth;\n            auth_request_set $username $upstream_http_x_username;\n            add_header X-Set-Username $username;\n        }\n\n        location = /t2.html {\n            auth_request /auth;\n            auth_request_set $username $upstream_http_x_username;\n            error_page 404 = /fallback;\n        }\n        location = /fallback {\n            add_header X-Set-Username $username;\n            return 204;\n        }\n\n        location = /t3.html {\n            auth_request /auth;\n            auth_request_set $username $upstream_http_x_username;\n            error_page 404 = @fallback;\n        }\n        location @fallback {\n            add_header X-Set-Username $username;\n            return 204;\n        }\n\n        location = /t4.html {\n            auth_request /auth;\n            auth_request_set $username $upstream_http_x_username;\n            error_page 404 = /t4-fallback.html;\n        }\n        location = /t4-fallback.html {\n            auth_request /auth2;\n            auth_request_set $username $upstream_http_x_username;\n            add_header X-Set-Username $username;\n        }\n\n        location = /t5.html {\n            auth_request /auth;\n            auth_request_set $args \"setargs\";\n            proxy_pass http://127.0.0.1:8081/t5.html;\n        }\n\n        location = /t6.html {\n            add_header X-Unset-Username \"x${username}x\";\n            return 204;\n        }\n\n        location = /auth {\n            proxy_pass http://127.0.0.1:8081;\n        }\n        location = /auth2 {\n            proxy_pass http://127.0.0.1:8081;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        location = /auth {\n            add_header X-Username \"username\";\n            return 204;\n        }\n\n        location = /auth2 {\n            add_header X-Username \"username2\";\n            return 204;\n        }\n\n        location = /t5.html {\n            add_header X-Args $args;\n            return 204;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('t1.html', '');\n$t->write_file('t4-fallback.html', '');\n$t->run();\n\n###############################################################################\n\nlike(http_get('/t1.html'), qr/X-Set-Username: username/, 'set normal');\nlike(http_get('/t2.html'), qr/X-Set-Username: username/, 'set after redirect');\nlike(http_get('/t3.html'), qr/X-Set-Username: username/,\n\t'set after named location');\nlike(http_get('/t4.html'), qr/X-Set-Username: username2/,\n\t'set on second auth');\n\n# there are two variables with set_handler: $args and $limit_rate\n# we do test $args as it's a bit more simple thing to do\n\nlike(http_get('/t5.html'), qr/X-Args: setargs/, 'variable with set_handler');\n\n# check that using variable without setting it returns empty content\n\nlike(http_get('/t6.html'), qr/X-Unset-Username: xx/, 'unset variable');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/autoindex.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for autoindex module.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http autoindex charset symlink/)->plan(16)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            autoindex on;\n        }\n        location /utf8/ {\n            autoindex on;\n            charset utf-8;\n        }\n    }\n}\n\nEOF\n\nmy $d = $t->testdir();\n\nmkdir(\"$d/test-dir\");\nsymlink(\"$d/test-dir\", \"$d/test-dir-link\");\n\n$t->write_file('test-file', '');\nsymlink(\"$d/test-file\", \"$d/test-file-link\");\n\n$t->write_file('test-colon:blah', '');\n$t->write_file('test-long-' . ('0' x 50), '');\n$t->write_file('test-long-' . ('>' x 50), '');\n$t->write_file('test-escape-url-%', '');\n$t->write_file('test-escape-url2-?', '');\n$t->write_file('test-escape-html-<>&', '');\n\nmkdir($d . '/utf8');\n$t->write_file('utf8/test-utf8-' . (\"\\xd1\\x84\" x 3), '');\n$t->write_file('utf8/test-utf8-' . (\"\\xd1\\x84\" x 45), '');\n$t->write_file('utf8/test-utf8-<>&-' . \"\\xd1\\x84\", '');\n$t->write_file('utf8/test-utf8-<>&-' . (\"\\xd1\\x84\" x 45), '');\n$t->write_file('utf8/test-utf8-' . (\"\\xd1\\x84\" x 3) . '-' . ('>' x 45), '');\n\nmkdir($d . '/test-dir-escape-<>&');\n\n$t->run();\n\n###############################################################################\n\nmy $r = http_get('/');\n\nlike($r, qr!href=\"test-file\"!ms, 'file');\nlike($r, qr!href=\"test-file-link\"!ms, 'symlink to file');\nlike($r, qr!href=\"test-dir/\"!ms, 'directory');\nlike($r, qr!href=\"test-dir-link/\"!ms, 'symlink to directory');\n\nunlike($r, qr!href=\"test-colon:blah\"!ms, 'colon not scheme');\nlike($r, qr!test-long-0{37}\\.\\.&gt;!ms, 'long name');\n\nlike($r, qr!href=\"test-escape-url-%25\"!ms, 'escaped url');\nlike($r, qr!href=\"test-escape-url2-%3f\"!msi, 'escaped ? in url');\nlike($r, qr!test-escape-html-&lt;&gt;&amp;!ms, 'escaped html');\nlike($r, qr!test-long-(&gt;){37}\\.\\.&gt;!ms, 'long escaped html');\n\n$r = http_get('/utf8/');\n\nlike($r, qr!test-utf8-(\\xd1\\x84){3}</a>!ms, 'utf8');\nlike($r, qr!test-utf8-(\\xd1\\x84){37}\\.\\.!ms, 'utf8 long');\n\nlike($r, qr!test-utf8-&lt;&gt;&amp;-\\xd1\\x84</a>!ms, 'utf8 escaped');\nlike($r, qr!test-utf8-&lt;&gt;&amp;-(\\xd1\\x84){33}\\.\\.!ms,\n\t'utf8 escaped long');\nlike($r, qr!test-utf8-(\\xd1\\x84){3}-(&gt;){33}\\.\\.!ms, 'utf8 long escaped');\n\nlike(http_get('/test-dir-escape-<>&/'), qr!test-dir-escape-&lt;&gt;&amp;!ms,\n\t'escaped title');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/autoindex_format.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for autoindex module with autoindex_format directive.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http autoindex symlink/)->plan(37)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        autoindex on;\n\n        location /xml/ {\n            autoindex_format xml;\n            alias %%TESTDIR%%/;\n        }\n        location /json/ {\n            autoindex_format json;\n            alias %%TESTDIR%%/;\n        }\n        location /jsonp/ {\n            autoindex_format jsonp;\n            alias %%TESTDIR%%/;\n        }\n    }\n}\n\nEOF\n\nmy $d = $t->testdir();\n\nmkdir(\"$d/test-dir\");\nsymlink(\"$d/test-dir\", \"$d/test-dir-link\");\n\n$t->write_file('test-file', 'x' x 42);\nsymlink(\"$d/test-file\", \"$d/test-file-link\");\n\n$t->write_file('test-\\'-quote', '');\n$t->write_file('test-\"-double', '');\n$t->write_file('test-<>-angle', '');\n\nmkdir($d . '/utf8');\n$t->write_file('utf8/test-utf8-' . (\"\\xd1\\x84\" x 3), '');\n$t->write_file('utf8/test-utf8-' . (\"\\xd1\\x84\" x 45), '');\n\n$t->run();\n\n###############################################################################\n\nmy ($r, $mtime, $data);\n\n$r = http_get('/xml/');\n$mtime = qr/mtime=\"\\d{4}-\\d\\d-\\d\\dT\\d\\d:\\d\\d:\\d\\dZ\"/;\n\nlike($r, qr!Content-Type: text/xml; charset=utf-8!, 'xml content type');\nlike($r, qr!<file(\\s+\\w+=\"[^=]*?\")+\\s*>test-file</file>!,\n\t'xml file format');\nlike($r, qr!<directory(\\s+\\w+=\"[^=]*?\")+\\s*>test-dir</directory>!,\n\t'xml dir format');\n\n($data) = $r =~ qr!<file\\s+(.*?)>test-file</file>!;\nlike($data, $mtime, 'xml file mtime');\nlike($data, qr!size=\"42\"!, 'xml file size');\n\n($data) = $r =~ qr!<file\\s+(.*?)>test-file-link</file>!;\nlike($data, $mtime, 'xml file link mtime');\nlike($data, qr!size=\"42\"!, 'xml file link size');\n\n($data) = $r =~ qr!<directory\\s+(.*?)>test-dir</directory>!;\nlike($data, $mtime, 'xml dir mtime');\nunlike($data, qr!size=\"\\d+\"!, 'xml dir size');\n\n($data) = $r =~ qr!<directory\\s+(.*?)>test-dir-link</directory>!;\nlike($data, $mtime, 'xml dir link mtime');\nunlike($data, qr!size=\"\\d+\"!, 'xml dir link size');\n\nlike($r, qr!<file.*?>test-\\'-quote</file>!, 'xml quote');\nlike($r, qr!<file.*?>test-\\&quot;-double</file>!, 'xml double');\nlike($r, qr!<file.*?>test-&lt;&gt;-angle</file>!, 'xml angle');\n\n\n$r = http_get('/json/');\n$mtime = qr/\"mtime\"\\s*:\\s*\"\\w{3}, \\d\\d \\w{3} \\d{4} \\d\\d:\\d\\d:\\d\\d \\w{3}\"/;\n\nmy $string = qr!\"(?:[^\\\\\"]+|\\\\[\"\\\\/bfnrt])*\"!;\nmy $number = qr!-?(?:0|[1-9]\\d*)(?:\\.\\d+)?(?:[eE][-+]?\\d+)?!;\nmy $kv = qr!\\s*$string\\s*:\\s*($string|$number)\\s*!;\n\nlike($r, qr!Content-Type: application/json!, 'json content type');\nlike($r, qr!{$kv(,$kv)*}!, 'json format');\n\n($data) = $r =~ qr!(\\{[^}]*?\"name\"\\s*:\\s*\"test-file\".*?})!;\nlike($data, qr!\"type\"\\s*:\\s*\"file\"!, 'json file');\nlike($data, $mtime, 'json file mtime');\nlike($data, qr!\"size\"\\s*:\\s*42!, 'json file size');\n\n($data) = $r =~ qr!(\\{[^}]*?\"name\"\\s*:\\s*\"test-file-link\".*?})!;\nlike($data, qr!\"type\"\\s*:\\s*\"file\"!, 'json file link');\nlike($data, $mtime, 'json file link mtime');\nlike($data, qr!\"size\"\\s*:\\s*42!, 'json file link size');\n\n($data) = $r =~ qr!(\\{[^}]*?\"name\"\\s*:\\s*\"test-dir\".*?})!;\nlike($data, qr!\"type\"\\s*:\\s*\"directory\"!, 'json dir');\nlike($data, $mtime, 'json dir mtime');\nunlike($data, qr!\"size\"\\s*:\\s*$number!, 'json dir size');\n\n($data) = $r =~ qr!(\\{[^}]*?\"name\"\\s*:\\s*\"test-dir-link\".*?})!;\nlike($data, qr!\"type\"\\s*:\\s*\"directory\"!, 'json dir link');\nlike($data, $mtime, 'json dir link mtime');\nunlike($data, qr!\"size\"\\s*:\\s*$number!, 'json dir link size');\n\nlike($r, qr!\"name\"\\s*:\\s*\"test-'-quote\"!, 'json quote');\nlike($r, qr!\"name\"\\s*:\\s*\"test-\\\\\\\"-double\"!, 'json double');\nlike($r, qr!\"name\"\\s*:\\s*\"test-<>-angle\"!, 'json angle');\n\nlike(http_get_body('/jsonp/test-dir/?callback=foo'),\n\tqr/^\\s*foo\\s*\\(\\s*\\[\\s*\\]\\s*\\)\\s*;\\s*$/ms, 'jsonp callback');\nlike(http_get_body('/jsonp/test-dir/?callback='),\n\tqr/^\\s*\\[\\s*\\s*\\]\\s*$/ms, 'jsonp callback empty');\n\n# utf8 tests\n\n$r = http_get('/xml/utf8/');\nlike($r, qr!test-utf8-(\\xd1\\x84){3}</file>!ms, 'xml utf8');\nlike($r, qr!test-utf8-(\\xd1\\x84){45}</file>!ms, 'xml utf8 long');\n\n$r = http_get('/json/utf8/');\nlike($r, qr!test-utf8-(\\xd1\\x84){3}\"!ms, 'json utf8');\nlike($r, qr!test-utf8-(\\xd1\\x84){45}\"!ms, 'json utf8 long');\n\n###############################################################################\n\nsub http_get_body {\n\tmy ($uri) = @_;\n\n\treturn undef if !defined $uri;\n\n\thttp_get($uri) =~ /(.*?)\\x0d\\x0a?\\x0d\\x0a?(.*)/ms;\n\n\treturn $2;\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/binary_upgrade.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for binary upgrade.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nplan(skip_all => 'can leave orphaned process group')\n\tunless $ENV{TEST_NGINX_UNSAFE};\n\nmy $t = Test::Nginx->new(qr/http unix/)->plan(4)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       unix:%%TESTDIR%%/unix.sock;\n        server_name  localhost;\n    }\n}\n\nEOF\n\nmy $d = $t->testdir();\n\n$t->run();\n\n###############################################################################\n\nmy $pid = $t->read_file('nginx.pid');\nok($pid, 'master pid');\n\nkill 'USR2', $pid;\n\nfor (1 .. 30) {\n\tlast if -e \"$d/nginx.pid\" && -e \"$d/nginx.pid.oldbin\";\n\tselect undef, undef, undef, 0.2\n}\n\nisnt($t->read_file('nginx.pid'), $pid, 'master pid changed');\n\nkill 'QUIT', $pid;\n\nfor (1 .. 30) {\n\tlast if ! -e \"$d/nginx.pid.oldbin\";\n\tselect undef, undef, undef, 0.2\n}\n\nok(-e \"$d/unix.sock\", 'unix socket exists on old master shutdown');\n\n# unix socket on new master termination\n\n$pid = $t->read_file('nginx.pid');\n\nkill 'USR2', $pid;\n\nfor (1 .. 30) {\n\tlast if -e \"$d/nginx.pid\" && -e \"$d/nginx.pid.oldbin\";\n\tselect undef, undef, undef, 0.2\n}\n\nkill 'TERM', $t->read_file('nginx.pid');\n\nfor (1 .. 30) {\n\tlast if ! -e \"$d/nginx.pid.oldbin\";\n\tselect undef, undef, undef, 0.2\n}\n\nok(-e \"$d/unix.sock\", 'unix socket exists on new master termination');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/body.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for nginx request body reading.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\nuse Socket qw/ CRLF /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy rewrite/)->plan(15);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    upstream u {\n        server 127.0.0.1:8082;\n        server 127.0.0.1:8080 backup;\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        client_header_buffer_size 1k;\n\n        location / {\n            client_body_buffer_size 2k;\n            add_header X-Body \"$request_body\";\n            add_header X-Body-File \"$request_body_file\";\n            proxy_pass http://127.0.0.1:8081;\n        }\n        location /b {\n            client_body_buffer_size 2k;\n            client_body_in_file_only on;\n            add_header X-Body \"$request_body\";\n            add_header X-Body-File \"$request_body_file\";\n            proxy_pass http://127.0.0.1:8081;\n        }\n        location /small {\n            client_body_in_file_only on;\n            add_header X-Original-Uri \"$request_uri\";\n            proxy_pass http://127.0.0.1:8080/;\n        }\n        location /single {\n            client_body_in_single_buffer on;\n            add_header X-Body \"$request_body\";\n            add_header X-Body-File \"$request_body_file\";\n            proxy_pass http://127.0.0.1:8081;\n        }\n        location /large {\n            client_max_body_size 1k;\n            proxy_pass http://127.0.0.1:8081;\n        }\n        location /discard {\n            return 200 \"TEST\\n\";\n        }\n        location /next {\n            proxy_pass http://u/;\n        }\n        location /redirect {\n            error_page 404 http://example.com/;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        location / {\n            return 200 \"TEST\\n\";\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8082;\n        server_name  localhost;\n\n        location / {\n            return 444;\n        }\n    }\n}\n\nEOF\n\n$t->run();\n\n###############################################################################\n\nunlike(http_get('/'), qr/X-Body:/ms, 'no body');\n\nlike(http_get_body('/', '0123456789'),\n\tqr/X-Body: 0123456789\\x0d?$/ms, 'body');\n\nlike(http_get_body('/', '0123456789' x 128),\n\tqr/X-Body: (0123456789){128}\\x0d?$/ms, 'body in two buffers');\n\nlike(http_get_body('/', '0123456789' x 512),\n\tqr/X-Body-File/ms, 'body in file');\n\nlike(read_body_file(http_get_body('/b', '0123456789' x 512)),\n\tqr/^(0123456789){512}$/s, 'body in file only');\n\nlike(http_get_body('/single', '0123456789' x 128),\n\tqr/X-Body: (0123456789){128}\\x0d?$/ms, 'body in single buffer');\n\nlike(http_get_body('/large', '0123456789' x 128), qr/ 413 /, 'body too large');\n\n# pipelined requests\n\nlike(http_get_body('/', '0123456789', '0123456789' x 128, '0123456789' x 512,\n\t'foobar'), qr/X-Body: foobar\\x0d?$/ms, 'body pipelined');\nlike(http_get_body('/', '0123456789' x 128, '0123456789' x 512, '0123456789',\n\t'foobar'), qr/X-Body: foobar\\x0d?$/ms, 'body pipelined 2');\n\nlike(http_get_body('/discard', '0123456789', '0123456789' x 128,\n\t'0123456789' x 512, 'foobar'), qr/(TEST.*){4}/ms,\n\t'body discard');\nlike(http_get_body('/discard', '0123456789' x 128, '0123456789' x 512,\n\t'0123456789', 'foobar'), qr/(TEST.*){4}/ms,\n\t'body discard 2');\n\n# proxy with file only\n\nlike(http_get_body('/small', '0123456789'),\n\tqr/X-Body: 0123456789\\x0d?$/ms, 'small body in file only');\n\n# proxy with file only - reuse of r->header_in\n\nlike(\n\thttp(\n\t\t'GET /small HTTP/1.0' . CRLF\n\t\t. 'Content-Length: 10' . CRLF . CRLF\n\t\t. '01234',\n\t\tsleep => 0.1,\n\t\tbody => '56789'\n\t),\n\tqr!X-Body: 0123456789\\x0d?\\x0a.*X-Original-Uri: /small!ms,\n\t'small body in file only, not preread'\n);\n\n# proxy_next_upstream\n\nlike(http_get_body('/next', '0123456789'),\n\tqr/X-Body: 0123456789\\x0d?$/ms, 'body next upstream');\n\n# discarded request body in redirect via error_page\n\nunlike(\n\thttp(\n\t\t'POST /redirect HTTP/1.1' . CRLF\n\t\t. 'Host: localhost' . CRLF\n\t\t. 'Content-Length: 10' . CRLF . CRLF\n\t\t. '0123456789' .\n\t\t'GET /next HTTP/1.0' . CRLF . CRLF\n\t),\n\tqr/400 Bad Request/ms, 'redirect - discard request body'\n);\n\n###############################################################################\n\nsub read_body_file {\n\tmy ($r) = @_;\n\treturn '' unless $r =~ m/X-Body-File: (.*)/;\n\topen FILE, $1\n\t\tor return \"$!\";\n\tlocal $/;\n\tmy $content = <FILE>;\n\tclose FILE;\n\treturn $content;\n}\n\nsub http_get_body {\n\tmy $uri = shift;\n\tmy $last = pop;\n\treturn http( join '', (map {\n\t\tmy $body = $_;\n\t\t\"GET $uri HTTP/1.1\" . CRLF\n\t\t. \"Host: localhost\" . CRLF\n\t\t. \"Content-Length: \" . (length $body) . CRLF . CRLF\n\t\t. $body\n\t} @_),\n\t\t\"GET $uri HTTP/1.1\" . CRLF\n\t\t. \"Host: localhost\" . CRLF\n\t\t. \"Connection: close\" . CRLF\n\t\t. \"Content-Length: \" . (length $last) . CRLF . CRLF\n\t\t. $last\n\t);\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/body_chunked.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for nginx request body reading, with chunked transfer-coding.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\nuse Socket qw/ CRLF /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy rewrite/)->plan(18);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    upstream u {\n        server 127.0.0.1:8082;\n        server 127.0.0.1:8080 backup;\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        client_header_buffer_size 1k;\n\n        location / {\n            client_body_buffer_size 2k;\n            add_header X-Body \"$request_body\";\n            add_header X-Body-File \"$request_body_file\";\n            proxy_pass http://127.0.0.1:8081;\n        }\n        location /b {\n            client_body_buffer_size 2k;\n            client_body_in_file_only on;\n            add_header X-Body \"$request_body\";\n            add_header X-Body-File \"$request_body_file\";\n            proxy_pass http://127.0.0.1:8081;\n        }\n        location /single {\n            client_body_in_single_buffer on;\n            add_header X-Body \"$request_body\";\n            add_header X-Body-File \"$request_body_file\";\n            proxy_pass http://127.0.0.1:8081;\n        }\n        location /large {\n            client_max_body_size 1k;\n            proxy_pass http://127.0.0.1:8081;\n        }\n        location /discard {\n            return 200 \"TEST\\n\";\n        }\n        location /next {\n            proxy_pass http://u/;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        location / {\n            return 200 \"TEST\\n\";\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8082;\n        server_name  localhost;\n\n        location / {\n            return 444;\n        }\n    }\n}\n\nEOF\n\n$t->run();\n\n###############################################################################\n\nlike(http_get_body('/', '0123456789'),\n\tqr/X-Body: 0123456789\\x0d?$/ms, 'body');\n\nlike(http_get_body('/', '0123456789' x 128),\n\tqr/X-Body: (0123456789){128}\\x0d?$/ms, 'body in two buffers');\n\nlike(http_get_body('/', '0123456789' x 512),\n\tqr/X-Body-File/ms, 'body in file');\n\nlike(read_body_file(http_get_body('/b', '0123456789' x 512)),\n\tqr/^(0123456789){512}$/s, 'body in file only');\n\nlike(http_get_body('/single', '0123456789' x 128),\n\tqr/X-Body: (0123456789){128}\\x0d?$/ms, 'body in single buffer');\n\nlike(http_get_body('/large', '0123456789' x 128), qr/ 413 /, 'body too large');\n\n# pipelined requests\n\nlike(http_get_body('/', '0123456789', '0123456789' x 128, '0123456789' x 512,\n\t'foobar'), qr/X-Body: foobar\\x0d?$/ms, 'chunked body pipelined');\nlike(http_get_body('/', '0123456789' x 128, '0123456789' x 512, '0123456789',\n\t'foobar'), qr/X-Body: foobar\\x0d?$/ms, 'chunked body pipelined 2');\n\nlike(http_get_body('/discard', '0123456789', '0123456789' x 128,\n\t'0123456789' x 512, 'foobar'), qr/(TEST.*){4}/ms,\n\t'chunked body discard');\nlike(http_get_body('/discard', '0123456789' x 128, '0123456789' x 512,\n\t'0123456789', 'foobar'), qr/(TEST.*){4}/ms,\n\t'chunked body discard 2');\n\n# invalid chunks\n\nlike(\n\thttp(\n\t\t'GET / HTTP/1.1' . CRLF\n\t\t. 'Host: localhost' . CRLF\n\t\t. 'Connection: close' . CRLF\n\t\t. 'Transfer-Encoding: chunked' . CRLF . CRLF\n\t\t. '4' . CRLF\n\t\t. 'SEE-THIS' . CRLF\n\t\t. '0' . CRLF . CRLF\n\t),\n\tqr/400 Bad/, 'runaway chunk'\n);\n\nlike(\n\thttp(\n\t\t'GET /discard HTTP/1.1' . CRLF\n\t\t. 'Host: localhost' . CRLF\n\t\t. 'Connection: close' . CRLF\n\t\t. 'Transfer-Encoding: chunked' . CRLF . CRLF\n\t\t. '4' . CRLF\n\t\t. 'SEE-THIS' . CRLF\n\t\t. '0' . CRLF . CRLF\n\t),\n\tqr/400 Bad/, 'runaway chunk discard'\n);\n\n# proxy_next_upstream\n\nlike(http_get_body('/next', '0123456789'),\n\tqr/X-Body: 0123456789\\x0d?$/ms, 'body chunked next upstream');\n\n# invalid Transfer-Encoding\n\nlike(http_transfer_encoding('identity'), qr/501 Not Implemented/,\n\t'transfer encoding identity');\n\nlike(http_transfer_encoding(\"chunked\\nTransfer-Encoding: chunked\"),\n\tqr/400 Bad/, 'transfer encoding repeat');\n\nlike(http_transfer_encoding('chunked, identity'), qr/501 Not Implemented/,\n\t'transfer encoding list');\n\nTODO: {\nlocal $TODO = 'not yet' unless $t->has_version('1.21.1');\n\nlike(http_transfer_encoding(\"chunked\\nContent-Length: 5\"), qr/400 Bad/,\n\t'transfer encoding with content-length');\n\n}\n\nTODO: {\nlocal $TODO = 'not yet' unless $t->has_version('1.21.2');\n\nlike(http_transfer_encoding(\"chunked\", \"1.0\"), qr/400 Bad/,\n\t'transfer encoding in HTTP/1.0 requests');\n\n}\n\n###############################################################################\n\nsub read_body_file {\n\tmy ($r) = @_;\n\treturn '' unless $r =~ m/X-Body-File: (.*)/;\n\topen FILE, $1\n\t\tor return \"$!\";\n\tlocal $/;\n\tmy $content = <FILE>;\n\tclose FILE;\n\treturn $content;\n}\n\nsub http_get_body {\n\tmy $uri = shift;\n\tmy $last = pop;\n\treturn http( join '', (map {\n\t\tmy $body = $_;\n\t\t\"GET $uri HTTP/1.1\" . CRLF\n\t\t. \"Host: localhost\" . CRLF\n\t\t. \"Transfer-Encoding: chunked\" . CRLF . CRLF\n\t\t. sprintf(\"%x\", length $body) . CRLF\n\t\t. $body . CRLF\n\t\t. \"0\" . CRLF . CRLF\n\t} @_),\n\t\t\"GET $uri HTTP/1.1\" . CRLF\n\t\t. \"Host: localhost\" . CRLF\n\t\t. \"Connection: close\" . CRLF\n\t\t. \"Transfer-Encoding: chunked\" . CRLF . CRLF\n\t\t. sprintf(\"%x\", length $last) . CRLF\n\t\t. $last . CRLF\n\t\t. \"0\" . CRLF . CRLF\n\t);\n}\n\nsub http_transfer_encoding {\n\tmy ($encoding, $version) = @_;\n\t$version ||= \"1.1\";\n\n\thttp(\"GET / HTTP/$version\" . CRLF\n\t\t. \"Host: localhost\" . CRLF\n\t\t. \"Connection: close\" . CRLF\n\t\t. \"Transfer-Encoding: $encoding\" . CRLF . CRLF\n\t\t. \"0\" . CRLF . CRLF);\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/charset.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n# (C) Nginx, Inc.\n\n# Tests for charset filter.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http charset proxy/)->plan(7)\n\t->write_file_expand('nginx.conf', <<'EOF')->run();\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    types {\n        text/html html;\n        text/foo  foo;\n    }\n\n    charset_map B A {\n        58 59; # X -> Y\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            charset utf-8;\n        }\n\n        location /t3.foo {\n            charset utf-8;\n            charset_types text/foo;\n        }\n\n        location /t4.any {\n            charset utf-8;\n            charset_types *;\n        }\n\n        location /t5.html {\n            charset $arg_c;\n        }\n\n        location /t.html {\n            charset A;\n            source_charset B;\n        }\n\n        location /proxy/ {\n            charset B;\n            override_charset on;\n            proxy_pass http://127.0.0.1:8080/;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('t1.html', '');\n$t->write_file('t2.foo', '');\n$t->write_file('t3.foo', '');\n$t->write_file('t4.any', '');\n$t->write_file('t5.html', '');\n$t->write_file('t.html', 'X' x 99);\n\n###############################################################################\n\nlike(http_get('/t1.html'), qr!text/html; charset=utf-8!, 'charset indicated');\nlike(http_get('/t2.foo'), qr!text/foo\\x0d!, 'wrong type');\nlike(http_get('/t3.foo'), qr!text/foo; charset=utf-8!, 'charset_types');\nlike(http_get('/t4.any'), qr!text/plain; charset=utf-8!, 'charset_types any');\nlike(http_get('/t5.html?c=utf-8'), qr!text/html; charset=utf-8!, 'variables');\n\nlike(http_get('/t.html'), qr!Y{99}!, 'recode');\nlike(http_get('/proxy/t.html'), qr!X{99}!, 'override charset');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/charset_gzip_static.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n# (C) Nginx, Inc.\n\n# Tests for charset filter.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx qw/ :DEFAULT :gzip /;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy charset gzip_static/)->plan(13)\n\t->write_file_expand('nginx.conf', <<'EOF')->run();\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    types {\n        text/html html;\n    }\n\n    charset_map B A {\n        58 59; # X -> Y\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location /t1 {\n            charset utf-8;\n            gzip_static on;\n        }\n\n        location /t2 {\n            gzip_static on;\n            charset A;\n            source_charset B;\n        }\n\n        location /t {\n            gzip_static on;\n        }\n\n        location /p/ {\n            charset utf-8;\n            proxy_pass http://127.0.0.1:8080/;\n            proxy_http_version 1.1;\n        }\n\n        location /p.ab/ {\n            charset A;\n            source_charset B;\n            proxy_pass http://127.0.0.1:8080/;\n            proxy_http_version 1.1;\n        }\n\n        location /p.aa/ {\n            charset A;\n            source_charset A;\n            proxy_pass http://127.0.0.1:8080/;\n            proxy_http_version 1.1;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('t1.html', '');\n$t->write_file('t1.html.gz', '');\n\nmy $in = 'X' x 99;\nmy $out = '';\n\neval {\n\trequire IO::Compress::Gzip;\n\tIO::Compress::Gzip::gzip(\\$in => \\$out);\n};\n\n$t->write_file('t2.html', $in);\n$t->write_file('t2.html.gz', $out);\n\n$t->write_file('t.html', '');\n$t->write_file('t.html.gz', '');\n\n###############################################################################\n\n# charset filter currently ignores responses with Content-Encoding set\n# (except ones with r->ignore_content_encoding used by gzip_static)\n# as it can't convert such content; there are two problems though:\n#\n# - it make sense to indicate charset\n#   if conversion isn't needed\n#\n# - gzip_static may need conversion, too\n#\n# proper solution seems to be to always allow charset indication, but\n# don't try to do anything if recoding is needed\n\nlike(http_get('/t1.html'), qr!text/html; charset=!, 'plain');\nlike(http_gzip_request('/t1.html'), qr!text/html; charset=.*gzip!ms, 'gzip');\n\nlike(http_get('/t2.html'), qr!text/html; charset=A.*Y{99}!ms, 'recode plain');\nlike(http_gzip_request('/t2.html'), qr!text/html\\x0d.*gzip!ms, 'recode gzip');\nhttp_gzip_like(http_gzip_request('/t2.html'), qr!X{99}!, 'recode content');\n\nlike(http_get('/t.html'), qr!text/html\\x0d!, 'nocharset plain');\nlike(http_gzip_request('/t.html'), qr!text/html\\x0d.*gzip!ms, 'nocharset gzip');\n\nlike(http_get('/p/t.html'), qr!text/html; charset=!, 'proxy plain');\nlike(http_gzip_request('/p/t.html'), qr!text/html; charset=.*gzip!ms,\n\t'proxy gzip');\n\nlike(http_get('/p.ab/t.html'), qr!text/html; charset=A!ms,\n\t'proxy recode plain');\nlike(http_gzip_request('/p.ab/t.html'), qr!text/html\\x0d.*gzip!ms,\n\t'proxy recode gzip');\n\nlike(http_get('/p.aa/t.html'), qr!text/html; charset=A!ms,\n\t'proxy nullrecode plain');\nlike(http_gzip_request('/p.aa/t.html'), qr!text/html; charset=A.*gzip!ms,\n\t'proxy nullrecode gzip');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/config_dump.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for dumped nginx configuration (nginx -T).\n# Among other things, test that configuration blocks are properly processed.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http map/);\n\n$t->plan(13)->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\ninclude %%TESTDIR%%/inc.conf;\ninclude %%TESTDIR%%/inc.conf;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    map $args $x {\n        default  0;\n        foo      bar;\n        include  map.conf;\n        include  map.conf;\n    }\n\n    upstream u {\n        server 127.0.0.1:8081;\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / { }\n    }\n}\n\nEOF\n\n$t->write_file('inc.conf', 'include inc2.conf;');\n$t->write_file('inc2.conf', '#inc2.conf');\n$t->write_file('map.conf', '#map.conf;');\n\n$t->run();\n\n###############################################################################\n\nmy $d = $t->testdir;\n\nmy $dump = $t->dump_config();\nlike($dump, qr!^# configuration file $d/nginx.conf:$!m, 'nginx.conf found');\nlike($dump, qr!^# configuration file $d/inc.conf:$!m, 'inc.conf found');\nlike($dump, qr!^# configuration file $d/inc2.conf:$!m, 'inc2.conf found');\nlike($dump, qr!^# configuration file $d/map.conf:$!m, 'map.conf found');\n\nunlike($dump, qr!(# configuration file $d/inc.conf:).*\\1!s, 'inc.conf uniq');\nunlike($dump, qr!(# configuration file $d/inc2.conf:).*\\1!s, 'inc2.conf uniq');\nunlike($dump, qr!(# configuration file $d/map.conf:).*\\1!s, 'map.conf uniq');\n\nis(getconf($t, $dump, 'nginx.conf'), $t->read_file('nginx.conf'), 'content');\nis(getconf($t, $dump, 'inc.conf'), $t->read_file('inc.conf'), 'content inc');\nis(getconf($t, $dump, 'map.conf'), $t->read_file('map.conf'), 'content inc 2');\n\nunlink($t->testdir . \"/inc.conf\");\nunlink($t->testdir . \"/map.conf\");\n\n$dump = $t->dump_config();\nunlike($dump, qr!file $d/inc.conf!, 'missing inc.conf');\nunlike($dump, qr!file $d/map.conf!, 'missing map.conf');\nlike($dump, qr!file $d/nginx.conf test failed!, 'test failed');\n\n$t->write_file('inc.conf', 'include inc2.conf;');\n$t->write_file('inc2.conf', '#inc2.conf');\n$t->write_file('map.conf', '#map.conf;');\n\n###############################################################################\n\nsub getconf {\n\tmy ($t, $string, $conf) = @_;\n\tmy $prefix = \"# configuration file $d/$conf:\\n\";\n\tmy $offset = index($string, $prefix) + length($prefix);\n\tmy $len = length($t->read_file($conf));\n\tmy $s = substr($string, $offset, $len);\n\t$s =~ tr/\\r//d;\n\treturn $s;\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/dav.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for nginx dav module.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http dav/)->plan(28);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        absolute_redirect off;\n\n        location / {\n            dav_methods PUT DELETE MKCOL COPY MOVE;\n        }\n\n        location /i/ {\n            alias %%TESTDIR%%/;\n            dav_methods PUT DELETE MKCOL COPY MOVE;\n        }\n    }\n}\n\nEOF\n\n$t->run();\n\n###############################################################################\n\nmy $r;\n\n$r = http(<<EOF . '0123456789');\nPUT /file HTTP/1.1\nHost: localhost\nConnection: close\nContent-Length: 10\n\nEOF\n\nlike($r, qr/201 Created.*(Content-Length|\\x0d\\0a0\\x0d\\x0a)/ms, 'put file');\nis(-s $t->testdir() . '/file', 10, 'put file size');\n\n$r = http(<<EOF);\nPUT /file HTTP/1.1\nHost: localhost\nConnection: close\nContent-Length: 0\n\nEOF\n\nlike($r, qr/204 No Content/, 'put file again');\nunlike($r, qr/Content-Length|Transfer-Encoding/, 'no length in 204');\nis(-s $t->testdir() . '/file', 0, 'put file again size');\n\n$r = http(<<EOF);\nDELETE /file HTTP/1.1\nHost: localhost\nConnection: close\nContent-Length: 0\n\nEOF\n\nlike($r, qr/204 No Content/, 'delete file');\nunlike($r, qr/Content-Length|Transfer-Encoding/, 'no length in 204');\nok(!-f $t->testdir() . '/file', 'file deleted');\n\n$r = http(<<EOF . '0123456789' . 'extra');\nPUT /file HTTP/1.1\nHost: localhost\nConnection: close\nContent-Length: 10\n\nEOF\n\nlike($r, qr/201 Created.*(Content-Length|\\x0d\\0a0\\x0d\\x0a)/ms,\n\t'put file extra data');\nis(-s $t->testdir() . '/file', 10,\n\t'put file extra data size');\n\nTODO: {\nlocal $TODO = 'not yet' unless $t->has_version('1.21.0');\n\n$r = http(<<EOF . '0123456789');\nPUT /file%20sp HTTP/1.1\nHost: localhost\nConnection: close\nContent-Length: 10\n\nEOF\n\nlike($r, qr!Location: /file%20sp\\x0d?$!ms, 'put file escaped');\n\n}\n\n# 201 replies contain body, response should indicate it's empty\n\n$r = http(<<EOF);\nMKCOL /test/ HTTP/1.1\nHost: localhost\nConnection: close\n\nEOF\n\nlike($r, qr/201 Created.*(Content-Length|\\x0d\\0a0\\x0d\\x0a)/ms, 'mkcol');\n\nSKIP: {\nskip 'perl too old', 1 if !$^V or $^V lt v5.12.0;\n\nlike($r, qr!(?(?{ $r =~ /Location/ })Location: /test/)!, 'mkcol location');\n\n}\n\n$r = http(<<EOF);\nCOPY /test/ HTTP/1.1\nHost: localhost\nDestination: /test-moved/\nConnection: close\n\nEOF\n\nlike($r, qr/201 Created.*(Content-Length|\\x0d\\0a0\\x0d\\x0a)/ms, 'copy dir');\n\n$r = http(<<EOF);\nMOVE /test/ HTTP/1.1\nHost: localhost\nDestination: /test-moved/\nConnection: close\n\nEOF\n\nlike($r, qr/201 Created.*(Content-Length|\\x0d\\0a0\\x0d\\x0a)/ms, 'move dir');\n\n$r = http(<<EOF);\nCOPY /file HTTP/1.1\nHost: localhost\nDestination: /file-moved%20escape\nConnection: close\n\nEOF\n\nlike($r, qr/204 No Content/, 'copy file escaped');\nis(-s $t->testdir() . '/file-moved escape', 10, 'file copied unescaped');\n\n$t->write_file('file.exist', join '', (1 .. 42));\n\n$r = http(<<EOF);\nCOPY /file HTTP/1.1\nHost: localhost\nDestination: /file.exist\nConnection: close\n\nEOF\n\nlike($r, qr/204 No Content/, 'copy file overwrite');\nis(-s $t->testdir() . '/file.exist', 10, 'target file truncated');\n\n$r = http(<<EOF . '0123456789');\nPUT /i/alias HTTP/1.1\nHost: localhost\nConnection: close\nContent-Length: 10\n\nEOF\n\nlike($r, qr/201 Created.*(Content-Length|\\x0d\\0a0\\x0d\\x0a)/ms, 'put alias');\nlike($r, qr!Location: /i/alias\\x0d?$!ms, 'location alias');\nis(-s $t->testdir() . '/alias', 10, 'put alias size');\n\n# request methods with unsupported request body\n\n$r = http(<<EOF . '0123456789');\nMKCOL /test/ HTTP/1.1\nHost: localhost\nConnection: close\nContent-Length: 10\n\nEOF\n\nlike($r, qr/415 Unsupported/, 'mkcol body');\n\n$r = http(<<EOF . '0123456789');\nCOPY /file HTTP/1.1\nHost: localhost\nDestination: /file.exist\nConnection: close\nContent-Length: 10\n\nEOF\n\nlike($r, qr/415 Unsupported/, 'copy body');\n\n$r = http(<<EOF . '0123456789');\nDELETE /file HTTP/1.1\nHost: localhost\nConnection: close\nContent-Length: 10\n\nEOF\n\nlike($r, qr/415 Unsupported/, 'delete body');\n\n$r = http(<<EOF);\nMKCOL /test/ HTTP/1.1\nHost: localhost\nConnection: close\nTransfer-Encoding: chunked\n\na\n0123456789\n0\n\nEOF\n\nlike($r, qr/415 Unsupported/, 'mkcol body chunked');\n\n$r = http(<<EOF);\nCOPY /file HTTP/1.1\nHost: localhost\nDestination: /file.exist\nConnection: close\nTransfer-Encoding: chunked\n\na\n0123456789\n0\n\nEOF\n\nlike($r, qr/415 Unsupported/, 'copy body chunked');\n\n$r = http(<<EOF);\nDELETE /file HTTP/1.1\nHost: localhost\nConnection: close\nTransfer-Encoding: chunked\n\na\n0123456789\n0\n\nEOF\n\nlike($r, qr/415 Unsupported/, 'delete body chunked');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/dav_chunked.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for nginx dav module with chunked request body.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\nuse Socket qw/ CRLF /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http dav/)->plan(6);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        client_header_buffer_size 1k;\n        client_body_buffer_size 2k;\n\n        location / {\n            dav_methods PUT;\n        }\n    }\n}\n\nEOF\n\n$t->run();\n\n###############################################################################\n\nmy $r;\n\n$r = http(<<EOF);\nPUT /file HTTP/1.1\nHost: localhost\nConnection: close\nTransfer-Encoding: chunked\n\na\n1234567890\n0\n\nEOF\n\nlike($r, qr/201 Created.*(Content-Length|\\x0d\\0a0\\x0d\\x0a)/ms, 'put chunked');\nis($t->read_file('file'), '1234567890', 'put content');\n\n$r = http(<<EOF);\nPUT /file HTTP/1.1\nHost: localhost\nConnection: close\nTransfer-Encoding: chunked\n\n0\n\nEOF\n\nlike($r, qr/204 No Content/, 'put chunked empty');\nis($t->read_file('file'), '', 'put empty content');\n\nmy $body = ('a' . CRLF . '1234567890' . CRLF) x 1024 . '0' . CRLF . CRLF;\n\n$r = http(<<EOF);\nPUT /file HTTP/1.1\nHost: localhost\nConnection: close\nTransfer-Encoding: chunked\n\n$body\nEOF\n\nlike($r, qr/204 No Content/, 'put chunked big');\nis($t->read_file('file'), '1234567890' x 1024, 'put big content');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/dav_utf8.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for nginx dav module with utf8 encoded names.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse Encode qw/ encode /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\neval { require Win32API::File if $^O eq 'MSWin32'; };\nplan(skip_all => 'Win32API::File not installed') if $@;\n\nmy $t = Test::Nginx->new()->has(qw/http dav/)->plan(16);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            dav_methods PUT DELETE MKCOL COPY MOVE;\n        }\n    }\n}\n\nEOF\n\n$t->run();\n\n###############################################################################\n\nlocal $TODO = 'not yet' if $^O eq 'MSWin32' and !$t->has_version('1.23.4');\n\nmy $d = $t->testdir();\nmy $r;\n\nmy $file = \"file-%D0%BC%D0%B8\";\nmy $file_path = \"file-\\x{043c}\\x{0438}\";\n\n$r = http(<<EOF . '0123456789');\nPUT /$file HTTP/1.1\nHost: localhost\nConnection: close\nContent-Length: 10\n\nEOF\n\nlike($r, qr/201 Created.*(Content-Length|\\x0d\\0a0\\x0d\\x0a)/ms, 'put file');\nok(fileexists(\"$d/$file_path\"), 'put file exist');\n\n$r = http(<<EOF);\nCOPY /$file HTTP/1.1\nHost: localhost\nDestination: /$file-moved\nConnection: close\n\nEOF\n\nlike($r, qr/204 No Content/, 'copy file');\nok(fileexists(\"$d/$file_path-moved\"), 'copy file exist');\n\n$r = http(<<EOF);\nMOVE /$file HTTP/1.1\nHost: localhost\nDestination: /$file-moved\nConnection: close\n\nEOF\n\nlike($r, qr/204 No Content/, 'move file');\nok(!fileexists(\"$d/$file_path\"), 'file moved');\n\n$r = http(<<EOF);\nDELETE /$file-moved HTTP/1.1\nHost: localhost\nConnection: close\nContent-Length: 0\n\nEOF\n\nlike($r, qr/204 No Content/, 'delete file');\nok(!fileexists(\"$d/$file_path-moved\"), 'file deleted');\n\nmy $dir = \"dir-%D0%BC%D0%B8\";\nmy $dir_path = \"dir-\\x{043c}\\x{0438}\";\n\n$r = http(<<EOF);\nMKCOL /$dir/ HTTP/1.1\nHost: localhost\nConnection: close\n\nEOF\n\nlike($r, qr/201 Created.*(Content-Length|\\x0d\\0a0\\x0d\\x0a)/ms, 'mkcol');\nok(fileexists(\"$d/$dir_path\"), 'mkcol exist');\n\n$r = http(<<EOF);\nCOPY /$dir/ HTTP/1.1\nHost: localhost\nDestination: /$dir-moved/\nConnection: close\n\nEOF\n\nlike($r, qr/201 Created.*(Content-Length|\\x0d\\0a0\\x0d\\x0a)/ms, 'copy dir');\nok(fileexists(\"$d/$dir_path-moved\"), 'copy dir exist');\n\n$r = http(<<EOF);\nMOVE /$dir/ HTTP/1.1\nHost: localhost\nDestination: /$dir-moved/\nConnection: close\n\nEOF\n\nlike($r, qr/201 Created.*(Content-Length|\\x0d\\0a0\\x0d\\x0a)/ms, 'move dir');\nok(!fileexists(\"$d/$dir_path\"), 'dir moved');\n\n$r = http(<<EOF);\nDELETE /$dir-moved/ HTTP/1.1\nHost: localhost\nConnection: close\n\nEOF\n\nunlike($r, qr/200 OK.*Content-Length|Transfer-Encoding/ms, 'delete dir');\nok(!fileexists(\"$d/$dir_path-moved\"), 'dir deleted');\n\n###############################################################################\n\nsub fileexists {\n\tmy ($path) = @_;\n\n\treturn -e $path if $^O ne 'MSWin32';\n\n\t$path = encode(\"UTF-16LE\", $path . \"\\0\");\n\tmy $attr = Win32API::File::GetFileAttributesW($path);\n\treturn 0 if $attr == Win32API::File::INVALID_HANDLE_VALUE();\n\treturn $attr;\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/debug_connection.t",
    "content": "#!/usr/bin/perl\n\n# (C) Nginx, Inc.\n\n# Tests for debug_connection.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http --with-debug proxy/);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n    debug_connection ::1;\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    error_log %%TESTDIR%%/debug1.log alert;\n    error_log %%TESTDIR%%/debug2.log alert;\n\n    server {\n        listen       127.0.0.1:8080;\n        listen       [::1]:%%PORT_8080%%;\n        server_name  localhost;\n\n        location /debug {\n            proxy_pass http://[::1]:%%PORT_8080%%/;\n        }\n    }\n}\n\nEOF\n\n$t->try_run('no inet6 support')->plan(5);\n\n###############################################################################\n\nhttp_get('/');\n\nselect undef, undef, undef, 0.1;\nis($t->read_file('debug1.log'), '', 'no debug_connection file 1');\nis($t->read_file('debug2.log'), '', 'no debug_connection file 2');\n\nhttp_get('/debug');\n\nselect undef, undef, undef, 0.1;\nlike($t->read_file('debug1.log'), qr/\\[debug\\]/, 'debug_connection file 1');\nlike($t->read_file('debug2.log'), qr/\\[debug\\]/, 'debug_connection file 2');\nis($t->read_file('debug1.log'), $t->read_file('debug2.log'),\n\t'debug_connection file1 file2 match');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/debug_connection_syslog.t",
    "content": "#!/usr/bin/perl\n\n# (C) Nginx, Inc.\n\n# Tests for debug_connection with syslog.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse IO::Select;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http --with-debug proxy/);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n    debug_connection ::1;\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    error_log syslog:server=127.0.0.1:%%PORT_8981_UDP%% alert;\n    error_log syslog:server=127.0.0.1:%%PORT_8982_UDP%% alert;\n\n    server {\n        listen       127.0.0.1:8080;\n        listen       [::1]:%%PORT_8080%%;\n        server_name  localhost;\n\n        location /debug {\n            proxy_pass http://[::1]:%%PORT_8080%%/;\n        }\n    }\n}\n\nEOF\n\n$t->try_run('no inet6 support')->plan(5);\n\n###############################################################################\n\nmy ($s1, $s2) = map {\n\tIO::Socket::INET->new(\n\t\tProto => 'udp',\n\t\tLocalAddr => \"127.0.0.1:$_\"\n\t)\n\t\tor die \"Can't open syslog socket $_: $!\";\n} port(8981), port(8982);\n\nis(get_syslog('/', $s1), '', 'no debug_connection syslog 1');\nis(get_syslog('/', $s2), '', 'no debug_connection syslog 2');\n\nmy @msgs = get_syslog('/debug', $s1, $s2);\nlike($msgs[0], qr/\\[debug\\]/, 'debug_connection syslog 1');\nlike($msgs[1], qr/\\[debug\\]/, 'debug_connection syslog 2');\nis($msgs[0], $msgs[1], 'debug_connection syslog1 syslog2 match');\n\n###############################################################################\n\nsub get_syslog {\n\tmy ($uri, @s) = @_;\n\tmy @data;\n\n\thttp_get($uri);\n\n\tmap {\n\t\tmy $data = '';\n\t\tIO::Select->new($_)->can_read(1);\n\t\twhile (IO::Select->new($_)->can_read(0.1)) {\n\t\t\tmy ($buffer);\n\t\t\tsysread($_, $buffer, 4096);\n\t\t\t$data .= $buffer;\n\t\t}\n\t\tpush @data, $data;\n\t} (@s);\n\n\treturn $data[0] if scalar @data == 1;\n\treturn @data;\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/debug_connection_unix.t",
    "content": "#!/usr/bin/perl\n\n# (C) Nginx, Inc.\n\n# Tests for debug_connection with unix socket.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http --with-debug unix proxy/);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n    debug_connection unix:;\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    error_log %%TESTDIR%%/debug1.log alert;\n    error_log %%TESTDIR%%/debug2.log alert;\n\n    server {\n        listen       127.0.0.1:8080;\n        listen       unix:%%TESTDIR%%/unix.sock;\n        server_name  localhost;\n\n        location /debug {\n            proxy_pass http://unix:%%TESTDIR%%/unix.sock:/;\n        }\n    }\n}\n\nEOF\n\n$t->run()->plan(5);\n\n###############################################################################\n\nhttp_get('/');\n\nselect undef, undef, undef, 0.1;\nis($t->read_file('debug1.log'), '', 'no debug_connection file 1');\nis($t->read_file('debug2.log'), '', 'no debug_connection file 2');\n\nhttp_get('/debug');\n\nselect undef, undef, undef, 0.1;\nlike($t->read_file('debug1.log'), qr/\\[debug\\]/, 'debug_connection file 1');\nlike($t->read_file('debug2.log'), qr/\\[debug\\]/, 'debug_connection file 2');\nis($t->read_file('debug1.log'), $t->read_file('debug2.log'),\n\t'debug_connection file1 file2 match');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/empty_gif.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n\n# Tests for empty gif module.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse Socket qw/ CRLF /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http empty_gif/)->plan(4);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            empty_gif;\n        }\n    }\n}\n\nEOF\n\n$t->run();\n\nmy $gif = unhex(<<'EOF');\n0x0000:  47 49 46 38 39 61 01 00  01 00 80 01 00 00 00 00  |GIF89a.. ........|\n0x0010:  ff ff ff 21 f9 04 01 00  00 01 00 2c 00 00 00 00  |...!.... ...,....|\n0x0020:  01 00 01 00 00 02 02 4c  01 00 3b                 |.......L ..;|\nEOF\n\n###############################################################################\n\nis(http_get_body('/'), $gif, 'empty gif');\nlike(http_get('/'), qr!Content-Type: image/gif!i, 'get content type');\nlike(http_head('/'), qr!Content-Type: image/gif!i, 'head content type');\nlike(http('PUT / HTTP/1.0' . CRLF . CRLF), qr!405 Not Allowed!i, 'put');\n\n###############################################################################\n\nsub unhex {\n\tmy ($input) = @_;\n\tmy $buffer = '';\n\n\tfor my $l ($input =~ m/:  +((?:[0-9a-f]{2,4} +)+) /gms) {\n\t\tfor my $v ($l =~ m/[0-9a-f]{2}/g) {\n\t\t\t$buffer .= chr(hex($v));\n\t\t}\n\t}\n\n\treturn $buffer;\n}\n\nsub http_get_body {\n\tmy ($uri) = @_;\n\n\treturn undef if !defined $uri;\n\n\tmy $text = http_get($uri);\n\n\tif ($text !~ /(.*?)\\x0d\\x0a?\\x0d\\x0a?(.*)/ms) {\n\t\treturn undef;\n\t}\n\n\treturn $2;\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/error_log.t",
    "content": "#!/usr/bin/perl\n\n# (C) Nginx, Inc.\n\n# Tests for error_log.\n# Various log levels emitted with limit_req_log_level.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nplan(skip_all => 'win32') if $^O eq 'MSWin32';\n\nmy $t = Test::Nginx->new()->has(qw/http limit_req/)\n\t->plan(25)->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    limit_req_zone $binary_remote_addr zone=one:1m rate=1r/m;\n    limit_req zone=one;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location /debug {\n            error_log %%TESTDIR%%/e_debug_debug.log debug;\n            error_log %%TESTDIR%%/e_debug_info.log info;\n            error_log stderr debug;\n        }\n        location /info {\n            limit_req_log_level info;\n            error_log %%TESTDIR%%/e_info_debug.log debug;\n            error_log %%TESTDIR%%/e_info_info.log info;\n            error_log %%TESTDIR%%/e_info_notice.log notice;\n            error_log stderr info;\n        }\n        location /notice {\n            limit_req_log_level notice;\n            error_log %%TESTDIR%%/e_notice_info.log info;\n            error_log %%TESTDIR%%/e_notice_notice.log notice;\n            error_log %%TESTDIR%%/e_notice_warn.log warn;\n            error_log stderr notice;\n        }\n        location /warn {\n            limit_req_log_level warn;\n            error_log %%TESTDIR%%/e_warn_notice.log notice;\n            error_log %%TESTDIR%%/e_warn_warn.log warn;\n            error_log %%TESTDIR%%/e_warn_error.log error;\n            error_log stderr warn;\n        }\n        location /error {\n            error_log %%TESTDIR%%/e_error_warn.log warn;\n            error_log %%TESTDIR%%/e_error_error.log;\n            error_log %%TESTDIR%%/e_error_alert.log alert;\n            error_log stderr;\n        }\n\n        location /file_low {\n            error_log %%TESTDIR%%/e_multi_low.log warn;\n            error_log %%TESTDIR%%/e_multi_low.log;\n        }\n        location /file_dup {\n            error_log %%TESTDIR%%/e_multi.log;\n            error_log %%TESTDIR%%/e_multi.log;\n        }\n        location /file_high {\n            error_log %%TESTDIR%%/e_multi_high.log emerg;\n            error_log %%TESTDIR%%/e_multi_high.log;\n        }\n\n        location /stderr_low {\n            error_log stderr warn;\n            error_log stderr;\n        }\n        location /stderr_dup {\n            error_log stderr;\n            error_log stderr;\n        }\n        location /stderr_high {\n            error_log stderr emerg;\n            error_log stderr;\n        }\n    }\n}\n\nEOF\n\nopen OLDERR, \">&\", \\*STDERR;\nopen STDERR, '>', $t->testdir() . '/stderr' or die \"Can't reopen STDERR: $!\";\nopen my $stderr, '<', $t->testdir() . '/stderr'\n\tor die \"Can't open stderr file: $!\";\n\n$t->run();\n\nopen STDERR, \">&\", \\*OLDERR;\n\n###############################################################################\n\n# charge limit_req\n\nhttp_get('/');\n\nSKIP: {\n\nskip \"no --with-debug\", 3 unless $t->has_module('--with-debug');\n\nhttp_get('/debug');\nisnt(lines($t, 'e_debug_debug.log', '[debug]'), 0, 'file debug debug');\nis(lines($t, 'e_debug_info.log', '[debug]'), 0, 'file debug info');\nisnt(lines($t, 'stderr', '[debug]'), 0, 'stderr debug');\n\n}\n\nhttp_get('/info');\nis(lines($t, 'e_info_debug.log', '[info]'), 1, 'file info debug');\nis(lines($t, 'e_info_info.log', '[info]'), 1, 'file info info');\nis(lines($t, 'e_info_notice.log', '[info]'), 0, 'file info notice');\nis(lines($t, 'stderr', '[info]'), 1, 'stderr info');\n\nhttp_get('/notice');\nis(lines($t, 'e_notice_info.log', '[notice]'), 1, 'file notice info');\nis(lines($t, 'e_notice_notice.log', '[notice]'), 1, 'file notice notice');\nis(lines($t, 'e_notice_warn.log', '[notice]'), 0, 'file notice warn');\nis(lines($t, 'stderr', '[notice]'), 1, 'stderr notice');\n\nhttp_get('/warn');\nis(lines($t, 'e_warn_notice.log', '[warn]'), 1, 'file warn notice');\nis(lines($t, 'e_warn_warn.log', '[warn]'), 1, 'file warn warn');\nis(lines($t, 'e_warn_error.log', '[warn]'), 0, 'file warn error');\nis(lines($t, 'stderr', '[warn]'), 1, 'stderr warn');\n\nhttp_get('/error');\nis(lines($t, 'e_error_warn.log', '[error]'), 1, 'file error warn');\nis(lines($t, 'e_error_error.log', '[error]'), 1, 'file error error');\nis(lines($t, 'e_error_alert.log', '[error]'), 0, 'file error alert');\nis(lines($t, 'stderr', '[error]'), 1, 'stderr error');\n\n# count log messages emitted with various error_log levels\n\nhttp_get('/file_low');\nis(lines($t, 'e_multi_low.log', '[error]'), 2, 'file low');\n\nhttp_get('/file_dup');\nis(lines($t, 'e_multi.log', '[error]'), 2, 'file dup');\n\nhttp_get('/file_high');\nis(lines($t, 'e_multi_high.log', '[error]'), 1, 'file high');\n\nhttp_get('/stderr_low');\nis(lines($t, 'stderr', '[error]'), 2, 'stderr low');\n\nhttp_get('/stderr_dup');\nis(lines($t, 'stderr', '[error]'), 2, 'stderr dup');\n\nhttp_get('/stderr_high');\nis(lines($t, 'stderr', '[error]'), 1, 'stderr high');\n\n###############################################################################\n\nsub lines {\n\tmy ($t, $file, $pattern) = @_;\n\n\tif ($file eq 'stderr') {\n\t\tmy $value = map { $_ =~ /\\Q$pattern\\E/ } (<$stderr>);\n\t\t$stderr->clearerr();\n\t\treturn $value;\n\t}\n\n\tmy $path = $t->testdir() . '/' . $file;\n\topen my $fh, '<', $path or return \"$!\";\n\tmy $value = map { $_ =~ /\\Q$pattern\\E/ } (<$fh>);\n\tclose $fh;\n\treturn $value;\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/fastcgi.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Test for fastcgi backend.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\neval { require FCGI; };\nplan(skip_all => 'FCGI not installed') if $@;\nplan(skip_all => 'win32') if $^O eq 'MSWin32';\n\nmy $t = Test::Nginx->new()->has(qw/http fastcgi/)->plan(8)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    upstream u {\n        server 127.0.0.1:8081;\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            fastcgi_pass 127.0.0.1:8081;\n            fastcgi_param REQUEST_URI $request_uri;\n        }\n\n        location /catch {\n            fastcgi_pass 127.0.0.1:8081;\n            fastcgi_param REQUEST_URI \"/stderr\";\n            fastcgi_catch_stderr sample;\n        }\n\n        location /var {\n            fastcgi_pass $arg_b;\n            fastcgi_param REQUEST_URI $request_uri;\n        }\n    }\n}\n\nEOF\n\n$t->run_daemon(\\&fastcgi_daemon);\n$t->run()->waitforsocket('127.0.0.1:' . port(8081));\n\n###############################################################################\n\nlike(http_get('/'), qr/SEE-THIS/, 'fastcgi request');\nlike(http_get('/redir'), qr/ 302 /, 'fastcgi redirect');\nlike(http_get('/'), qr/^3$/m, 'fastcgi third request');\n\nunlike(http_head('/'), qr/SEE-THIS/, 'no data in HEAD');\n\nlike(http_get('/stderr'), qr/SEE-THIS/, 'large stderr handled');\nlike(http_get('/catch'), qr/502 Bad/, 'catch stderr');\n\nlike(http_get('/var?b=127.0.0.1:' . port(8081)), qr/SEE-THIS/,\n\t'fastcgi with variables');\nlike(http_get('/var?b=u'), qr/SEE-THIS/, 'fastcgi with variables to upstream');\n\n###############################################################################\n\nsub fastcgi_daemon {\n\tmy $socket = FCGI::OpenSocket('127.0.0.1:' . port(8081), 5);\n\tmy $request = FCGI::Request(\\*STDIN, \\*STDOUT, \\*STDERR, \\%ENV,\n\t\t$socket);\n\n\tmy $count;\n\twhile( $request->Accept() >= 0 ) {\n\t\t$count++;\n\n\t\tif ($ENV{REQUEST_URI} eq '/stderr') {\n\t\t\twarn \"sample stderr text\" x 512;\n\t\t}\n\n\t\tprint <<EOF;\nLocation: http://localhost/redirect\nContent-Type: text/html\n\nSEE-THIS\n$count\nEOF\n\t}\n\n\tFCGI::CloseSocket($socket);\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/fastcgi_body.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Test for fastcgi backend with chunked request body.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http fastcgi/)->plan(5)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            fastcgi_pass 127.0.0.1:8081;\n            fastcgi_param REQUEST_URI $request_uri;\n            fastcgi_param CONTENT_LENGTH $content_length;\n        }\n    }\n}\n\nEOF\n\n$t->run_daemon(\\&fastcgi_daemon);\n$t->run()->waitforsocket('127.0.0.1:' . port(8081));\n\n###############################################################################\n\nlike(http_get('/'), qr/X-Body: _eos\\x0d?$/ms, 'fastcgi no body');\n\nlike(http_get_length('/', ''), qr/X-Body: _eos\\x0d?$/ms, 'fastcgi empty body');\nlike(http_get_length('/', 'foobar'), qr/X-Body: foobar_eos\\x0d?$/ms,\n\t'fastcgi body');\n\nlike(http(<<EOF), qr/X-Body: foobar_eos\\x0d?$/ms, 'fastcgi chunked');\nGET / HTTP/1.1\nHost: localhost\nConnection: close\nTransfer-Encoding: chunked\n\n6\nfoobar\n0\n\nEOF\n\nlike(http(<<EOF), qr/X-Body: _eos\\x0d?$/ms, 'fastcgi empty chunked');\nGET / HTTP/1.1\nHost: localhost\nConnection: close\nTransfer-Encoding: chunked\n\n0\n\nEOF\n\n###############################################################################\n\nsub http_get_length {\n\tmy ($url, $body) = @_;\n\tmy $length = length $body;\n\treturn http(<<EOF);\nGET $url HTTP/1.1\nHost: localhost\nConnection: close\nContent-Length: $length\n\n$body\nEOF\n}\n\n###############################################################################\n\n# Simple FastCGI responder implementation.\n\n# http://www.fastcgi.com/devkit/doc/fcgi-spec.html\n\nsub fastcgi_read_record($) {\n\tmy ($buf) = @_;\n\tmy $h;\n\n\treturn undef unless length $$buf;\n\n\t@{$h}{qw/ version type id clen plen /} = unpack(\"CCnnC\", $$buf);\n\n\t$h->{content} = substr $$buf, 8, $h->{clen};\n\t$h->{padding} = substr $$buf, 8 + $h->{clen}, $h->{plen};\n\n\t$$buf = substr $$buf, 8 + $h->{clen} + $h->{plen};\n\n\treturn $h;\n}\n\nsub fastcgi_respond($$$$) {\n\tmy ($socket, $version, $id, $body) = @_;\n\n\t# stdout\n\t$socket->write(pack(\"CCnnCx\", $version, 6, $id, length($body), 0));\n\t$socket->write($body);\n\n\t# close stdout\n\t$socket->write(pack(\"CCnnCx\", $version, 6, $id, 0, 0));\n\n\t# end request\n\t$socket->write(pack(\"CCnnCx\", $version, 3, $id, 8, 0));\n\t$socket->write(pack(\"NCxxx\", 0, 0));\n}\n\nsub fastcgi_daemon {\n\tmy $server = IO::Socket::INET->new(\n\t\tProto => 'tcp',\n\t\tLocalAddr => '127.0.0.1:' . port(8081),\n\t\tListen => 5,\n\t\tReuse => 1\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\tlocal $SIG{PIPE} = 'IGNORE';\n\n\twhile (my $client = $server->accept()) {\n\t\t$client->autoflush(1);\n\t\tTest::Nginx::log_core('||', \"fastcgi connection\");\n\n\t\t$client->sysread(my $buf, 1024) or next;\n\n\t\tmy ($version, $id);\n\t\tmy $body = '';\n\n\t\twhile (my $h = fastcgi_read_record(\\$buf)) {\n\t\t\t$version = $h->{version};\n\t\t\t$id = $h->{id};\n\n\t\t\tTest::Nginx::log_core('||', \"fastcgi record: \"\n\t\t\t\t. \" $h->{version}, $h->{type}, $h->{id}, \"\n\t\t\t\t. \"'$h->{content}'\");\n\n\t\t\tif ($h->{type} == 5) {\n\t\t\t\t$body .= $h->{content} if $h->{clen} > 0;\n\n\t\t\t\t# count stdin end-of-stream\n\t\t\t\t$body .= '_eos' if $h->{clen} == 0;\n\t\t\t}\n\t\t}\n\n\t\t# respond\n\t\tfastcgi_respond($client, $version, $id, <<EOF);\nLocation: http://localhost/redirect\nContent-Type: text/html\nX-Body: $body\n\nSEE-THIS\nEOF\n\t}\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/fastcgi_body2.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Test for fastcgi backend with large request body,\n# with fastcgi_next_upstream directive.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\neval { require FCGI; };\nplan(skip_all => 'FCGI not installed') if $@;\nplan(skip_all => 'win32') if $^O eq 'MSWin32';\n\nmy $t = Test::Nginx->new()->has(qw/http fastcgi/)->plan(2)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    upstream u {\n        server 127.0.0.1:8081;\n        server 127.0.0.1:8082 backup;\n    }\n\n    upstream u2 {\n        server 127.0.0.1:8081;\n        server 127.0.0.1:8082 backup;\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            fastcgi_pass u;\n            fastcgi_param REQUEST_URI $request_uri;\n            fastcgi_param CONTENT_LENGTH $content_length;\n            # fastcgi_next_upstream error timeout;\n            fastcgi_read_timeout 1s;\n        }\n\n        location /in_memory {\n            fastcgi_pass u2;\n            fastcgi_param REQUEST_URI $request_uri;\n            fastcgi_param CONTENT_LENGTH $content_length;\n            # fastcgi_next_upstream error timeout;\n            fastcgi_read_timeout 1s;\n            client_body_buffer_size 128k;\n        }\n    }\n}\n\nEOF\n\n$t->run_daemon(\\&fastcgi_daemon, port(8081));\n$t->run_daemon(\\&fastcgi_daemon, port(8082));\n$t->run();\n\n$t->waitforsocket('127.0.0.1:' . port(8081));\n$t->waitforsocket('127.0.0.1:' . port(8082));\n\n###############################################################################\n\nlike(http_get_length('/', 'x' x 102400), qr/X-Length: 102400/,\n\t'body length - in file');\n\nlike(http_get_length('/in_memory', 'x' x 102400), qr/X-Length: 102400/,\n\t'body length - in memory');\n\n###############################################################################\n\nsub http_get_length {\n\tmy ($url, $body) = @_;\n\tmy $length = length $body;\n\treturn http(<<EOF);\nGET $url HTTP/1.0\nHost: localhost\nContent-Length: $length\n\n$body\nEOF\n}\n\n###############################################################################\n\nsub fastcgi_daemon {\n\tmy ($port) = @_;\n\tmy $socket = FCGI::OpenSocket(\"127.0.0.1:$port\", 5);\n\tmy $request = FCGI::Request(\\*STDIN, \\*STDOUT, \\*STDERR, \\%ENV,\n\t\t$socket);\n\n\twhile( $request->Accept() >= 0 ) {\n\t\tread(STDIN, my $body, $ENV{'CONTENT_LENGTH'} || 0);\n\t\tmy $len = length $body;\n\n\t\tsleep 3 if $port == port(8081);\n\n\t\tprint <<EOF;\nLocation: http://localhost/redirect\nContent-Type: text/html\nX-Length: $len\n\nEOF\n\t}\n\n\tFCGI::CloseSocket($socket);\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/fastcgi_buffering.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Test for fastcgi backend with fastcgi_buffering off.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse Socket qw/ CRLF /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\neval { require FCGI; };\nplan(skip_all => 'FCGI not installed') if $@;\nplan(skip_all => 'win32') if $^O eq 'MSWin32';\n\nmy $t = Test::Nginx->new()->has(qw/http fastcgi ssi/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            fastcgi_pass 127.0.0.1:8081;\n            fastcgi_param REQUEST_URI $request_uri;\n            fastcgi_buffering off;\n        }\n\n        location /inmemory.html {\n            ssi on;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('inmemory.html',\n\t'<!--#include virtual=\"/include$request_uri\" set=\"x\" -->' .\n\t'set: <!--#echo var=\"x\" -->');\n\n$t->run()->plan(2);\n\n$t->run_daemon(\\&fastcgi_daemon)->waitforsocket('127.0.0.1:' . port(8081));\n\n###############################################################################\n\nlike(http_get('/'), qr/SEE-THIS/, 'fastcgi unbuffered');\nlike(http_get('/inmemory.html'), qr/set: SEE-THIS/, 'fastcgi inmemory');\n\n###############################################################################\n\nsub fastcgi_daemon {\n\tmy $socket = FCGI::OpenSocket('127.0.0.1:' . port(8081), 5);\n\tmy $request = FCGI::Request(\\*STDIN, \\*STDOUT, \\*STDERR, \\%ENV,\n\t\t$socket);\n\n\tmy $count;\n\twhile( $request->Accept() >= 0 ) {\n\t\t$count++;\n\n\t\t# this intentionally uses multiple print()'s to test\n\t\t# parsing of multiple records\n\n\t\tprint(\n\t\t\t\"Status: 200 OK\" . CRLF .\n\t\t\t\"Content-Type: text/plain\" . CRLF . CRLF\n\t\t);\n\n\t\tprint \"SEE\";\n\t\tprint \"-THIS\" . CRLF;\n\t\tprint \"$count\" . CRLF;\n\t}\n\n\tFCGI::CloseSocket($socket);\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/fastcgi_cache.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Test for fastcgi backend with cache.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\neval { require FCGI; };\nplan(skip_all => 'FCGI not installed') if $@;\nplan(skip_all => 'win32') if $^O eq 'MSWin32';\n\nmy $t = Test::Nginx->new()->has(qw/http fastcgi cache/)->plan(5)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    fastcgi_cache_path   %%TESTDIR%%/cache  levels=1:2\n                         keys_zone=NAME:1m;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            fastcgi_pass 127.0.0.1:8081;\n            fastcgi_param REQUEST_URI $request_uri;\n            fastcgi_cache NAME;\n            fastcgi_cache_key $request_uri;\n            fastcgi_cache_valid 302 1m;\n        }\n    }\n}\n\nEOF\n\n$t->run_daemon(\\&fastcgi_daemon);\n$t->run()->waitforsocket('127.0.0.1:' . port(8081));\n\n###############################################################################\n\nlike(http_get('/'), qr/SEE-THIS.*^1$/ms, 'fastcgi request');\nlike(http_get('/'), qr/SEE-THIS.*^1$/ms, 'fastcgi request cached');\n\nunlike(http_head('/'), qr/SEE-THIS/, 'no data in cached HEAD');\n\nSKIP: {\nskip 'broken with header crossing buffer boundary', 2\n\tunless $ENV{TEST_NGINX_UNSAFE};\n\nlike(http_get('/stderr'), qr/SEE-THIS.*^2$/ms, 'large stderr handled');\nlike(http_get('/stderr'), qr/SEE-THIS.*^2$/ms, 'large stderr cached');\n\n}\n\n###############################################################################\n\nsub fastcgi_daemon {\n\tmy $socket = FCGI::OpenSocket('127.0.0.1:' . port(8081), 5);\n\tmy $request = FCGI::Request(\\*STDIN, \\*STDOUT, \\*STDERR, \\%ENV,\n\t\t$socket);\n\n\tmy $count;\n\twhile( $request->Accept() >= 0 ) {\n\t\t$count++;\n\n\t\tif ($ENV{REQUEST_URI} eq '/stderr') {\n\t\t\twarn \"sample stderr text\" x 512;\n\t\t}\n\n\t\tprint <<EOF;\nLocation: http://localhost/redirect\nContent-Type: text/html\n\nSEE-THIS\n$count\nEOF\n\t}\n\n\tFCGI::CloseSocket($socket);\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/fastcgi_extra_data.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n# (C) Nginx, Inc.\n\n# Test for fastcgi backend, responses with extra data or premature close.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\neval { require FCGI; };\nplan(skip_all => 'FCGI not installed') if $@;\nplan(skip_all => 'win32') if $^O eq 'MSWin32';\n\nmy $t = Test::Nginx->new()\n\t->has(qw/http fastcgi cache rewrite addition/)->plan(22)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    fastcgi_param REQUEST_URI $request_uri;\n    fastcgi_param REQUEST_METHOD $request_method;\n\n    fastcgi_cache_path cache keys_zone=one:1m;\n    fastcgi_cache_key $request_uri;\n    fastcgi_cache_valid any 1m;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            fastcgi_pass 127.0.0.1:8081;\n            add_after_body /after;\n        }\n\n        location /unbuf/ {\n            fastcgi_pass 127.0.0.1:8081;\n            fastcgi_buffering off;\n            add_after_body /after;\n        }\n\n        location /head/ {\n            fastcgi_pass 127.0.0.1:8081;\n            fastcgi_cache one;\n            add_after_body /after;\n        }\n\n        location /after {\n            return 200 \":after\\n\";\n        }\n    }\n}\n\nEOF\n\n$t->run_daemon(\\&fastcgi_daemon);\n$t->run()->waitforsocket('127.0.0.1:' . port(8081));\n\n###############################################################################\n\nlike(http_get('/'), qr/SEE-THIS(?!-BUT-NOT-THIS)/, 'response with extra data');\nlike(http_get('/short'), qr/SEE-THIS(?!.*:after)/s, 'too short response');\nlike(http_get('/empty'), qr/200 OK(?!.*:after)/s, 'empty too short response');\n\nlike(http_head('/'), qr/200 OK(?!.*SEE-THIS)/s, 'no data in HEAD');\nlike(http_head('/short'), qr/200 OK(?!.*SEE-THIS)/s, 'too short to HEAD');\nlike(http_head('/empty'), qr/200 OK/, 'empty response to HEAD');\n\n# unbuffered responses\n\nlike(http_get('/unbuf/'), qr/SEE-THIS(?!-BUT-NOT-THIS)/,\n\t'unbuffered with extra data');\nlike(http_get('/unbuf/short'), qr/SEE-THIS(?!.*:after)/s,\n\t'unbuffered too short response');\nlike(http_get('/unbuf/empty'), qr/200 OK(?!.*:after)/s,\n\t'unbuffered empty too short response');\n\nlike(http_head('/unbuf/'), qr/200 OK(?!.*SEE-THIS)/s,\n\t'unbuffered no data in HEAD');\nlike(http_head('/unbuf/short'), qr/200 OK(?!.*SEE-THIS)/s,\n\t'unbuffered too short response to HEAD');\nlike(http_head('/unbuf/empty'), qr/200 OK/,\n\t'unbuffered empty response to HEAD');\n\n# caching of responsses to HEAD requests\n\nlike(http_head('/head/empty'), qr/200 OK(?!.*SEE-THIS)/s, 'head no body');\nlike(http_head('/head/matching'), qr/200 OK(?!.*SEE-THIS)/s, 'head matching');\nlike(http_head('/head/extra'), qr/200 OK(?!.*SEE-THIS)/s, 'head extra');\nlike(http_head('/head/short'), qr/200 OK(?!.*SEE-THIS)/s, 'head too short');\n\nlike(http_get('/head/empty'), qr/200 OK/, 'head no body cached');\nlike(http_get('/head/matching'), qr/SEE-THIS/, 'head matching cached');\nlike(http_get('/head/extra'), qr/SEE-THIS(?!-BUT-NOT-THIS)/s,\n\t'head extra cached');\nlike(http_get('/head/short'), qr/SEE-THIS(?!.*:after)/s,\n\t'head too short cached');\n\n# \"zero size buf\" alerts (ticket #2018)\n\nlike(http_get('/zero'), qr/200 OK(?!.*NOT-THIS)/s, 'zero size');\nlike(http_get('/unbuf/zero'), qr/200 OK(?!.*NOT-THIS)/s,\n\t'unbuffered zero size');\n\n###############################################################################\n\nsub fastcgi_daemon {\n\tmy $socket = FCGI::OpenSocket('127.0.0.1:' . port(8081), 5);\n\tmy $request = FCGI::Request(\\*STDIN, \\*STDOUT, \\*STDERR, \\%ENV,\n\t\t$socket);\n\n\tmy ($uri, $head);\n\n\twhile( $request->Accept() >= 0 ) {\n\t\t$uri = $ENV{REQUEST_URI};\n\t\t$uri =~ s!^/unbuf!!;\n\n\t\t$head = $ENV{REQUEST_METHOD} eq 'HEAD';\n\n\t\tif ($uri eq '/') {\n\t\t\tprint \"Content-Type: text/html\\n\";\n\t\t\tprint \"Content-Length: 8\\n\\n\";\n\t\t\tprint \"SEE-THIS-BUT-NOT-THIS\\n\";\n\n\t\t} elsif ($uri eq '/zero') {\n\t\t\tprint \"Content-Type: text/html\\n\";\n\t\t\tprint \"Content-Length: 0\\n\\n\";\n\t\t\tprint \"NOT-THIS\\n\";\n\n\t\t} elsif ($uri eq '/short') {\n\t\t\tprint \"Content-Type: text/html\\n\";\n\t\t\tprint \"Content-Length: 100\\n\\n\";\n\t\t\tprint \"SEE-THIS-TOO-SHORT-RESPONSE\\n\";\n\n\t\t} elsif ($uri eq '/empty') {\n\t\t\tprint \"Content-Type: text/html\\n\";\n\t\t\tprint \"Content-Length: 100\\n\\n\";\n\n\t\t} elsif ($uri eq '/head/empty') {\n\t\t\tprint \"Content-Type: text/html\\n\";\n\t\t\tprint \"Content-Length: 8\\n\\n\";\n\t\t\tprint \"SEE-THIS\" unless $head;\n\n\t\t} elsif ($uri eq '/head/matching') {\n\t\t\tprint \"Content-Type: text/html\\n\";\n\t\t\tprint \"Content-Length: 8\\n\\n\";\n\t\t\tprint \"SEE-THIS\";\n\n\t\t} elsif ($uri eq '/head/extra') {\n\t\t\tprint \"Content-Type: text/html\\n\";\n\t\t\tprint \"Content-Length: 8\\n\\n\";\n\t\t\tprint \"SEE-THIS-BUT-NOT-THIS\\n\";\n\n\t\t} elsif ($uri eq '/head/short') {\n\t\t\tprint \"Content-Type: text/html\\n\";\n\t\t\tprint \"Content-Length: 100\\n\\n\";\n\t\t\tprint \"SEE-THIS\\n\";\n\t\t}\n\t}\n\n\tFCGI::CloseSocket($socket);\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/fastcgi_header_params.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Test for fastcgi header params.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\neval { require FCGI; };\nplan(skip_all => 'FCGI not installed') if $@;\nplan(skip_all => 'win32') if $^O eq 'MSWin32';\n\nmy $t = Test::Nginx->new()->has(qw/http fastcgi/)->plan(4)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            fastcgi_pass 127.0.0.1:8081;\n            fastcgi_param HTTP_X_BLAH \"blah\";\n        }\n    }\n}\n\nEOF\n\n$t->run_daemon(\\&fastcgi_daemon);\n$t->run()->waitforsocket('127.0.0.1:' . port(8081));\n\n###############################################################################\n\nlike(http_get_headers('/'), qr/SEE-THIS/,\n\t'fastcgi request with many ignored headers');\n\nTODO: {\nlocal $TODO = 'not yet' unless $t->has_version('1.23.0');\n\nmy $r;\n\n$r = http(<<EOF);\nGET / HTTP/1.0\nHost: localhost\nX-Forwarded-For: foo\nX-Forwarded-For: bar\nX-Forwarded-For: bazz\nCookie: foo\nCookie: bar\nCookie: bazz\nFoo: foo\nFoo: bar\nFoo: bazz\n\nEOF\n\nlike($r, qr/X-Forwarded-For: foo, bar, bazz/,\n\t'fastcgi with multiple X-Forwarded-For headers');\n\nlike($r, qr/X-Cookie: foo; bar; bazz/,\n\t'fastcgi with multiple Cookie headers');\n\nlike($r, qr/X-Foo: foo, bar, bazz/,\n\t'fastcgi with multiple unknown headers');\n\n}\n\n###############################################################################\n\nsub http_get_headers {\n\tmy ($url, %extra) = @_;\n\treturn http(<<EOF, %extra);\nGET $url HTTP/1.0\nHost: localhost\nX-Blah: ignored header\nX-Blah: ignored header\nX-Blah: ignored header\nX-Blah: ignored header\nX-Blah: ignored header\nX-Blah: ignored header\nX-Blah: ignored header\nX-Blah: ignored header\nX-Blah: ignored header\nX-Blah: ignored header\nX-Blah: ignored header\nX-Blah: ignored header\nX-Blah: ignored header\nX-Blah: ignored header\nX-Blah: ignored header\nX-Blah: ignored header\nX-Blah: ignored header\nX-Blah: ignored header\nX-Blah: ignored header\n\nEOF\n}\n\n###############################################################################\n\nsub fastcgi_daemon {\n\tmy $socket = FCGI::OpenSocket('127.0.0.1:' . port(8081), 5);\n\tmy $request = FCGI::Request(\\*STDIN, \\*STDOUT, \\*STDERR, \\%ENV,\n\t\t$socket);\n\n\tmy $count;\n\twhile( $request->Accept() >= 0 ) {\n\t\t$count++;\n\n\t\tmy $xfwd = $ENV{HTTP_X_FORWARDED_FOR} || '';\n\t\tmy $cookie = $ENV{HTTP_COOKIE} || '';\n\t\tmy $foo = $ENV{HTTP_FOO} || '';\n\n\t\tprint <<EOF;\nLocation: http://localhost/redirect\nContent-Type: text/html\nX-Forwarded-For: $xfwd\nX-Cookie: $cookie\nX-Foo: $foo\n\nSEE-THIS\n$count\nEOF\n\t}\n\n\tFCGI::CloseSocket($socket);\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/fastcgi_keepalive.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Test for fastcgi backend with keepalive.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http fastcgi upstream_keepalive/)->plan(6)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\nworker_processes 1; # fixed for tengine\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    upstream backend {\n        server 127.0.0.1:8081;\n        keepalive 1;\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            fastcgi_pass backend;\n            fastcgi_keep_conn on;\n        }\n    }\n}\n\nEOF\n\n$t->run_daemon(\\&fastcgi_test_daemon);\n$t->run()->waitforsocket('127.0.0.1:' . port(8081));\n\n###############################################################################\n\nlike(http_get('/'), qr/SEE-THIS/, 'fastcgi request');\nlike(http_get('/redir'), qr/ 302 /, 'fastcgi redirect');\nlike(http_get('/'), qr/^request: 3$/m, 'fastcgi third request');\n\nlike(http_get('/single'), qr/^connection: 1$/m, 'single connection used');\n\n# New connection to fastcgi application should be established after HEAD\n# requests since nginx doesn't read whole response (as it doesn't need\n# body).\n\nunlike(http_head('/head'), qr/SEE-THIS/, 'no data in HEAD');\n\nlike(http_get('/after'), qr/^connection: 2$/m, 'new connection after HEAD');\n\n###############################################################################\n\n# Simple FastCGI responder implementation.  Unlike FCGI and FCGI::Async it's\n# able to count connections.\n\n# http://www.fastcgi.com/devkit/doc/fcgi-spec.html\n\nsub fastcgi_read_record($) {\n\tmy ($socket) = @_;\n\n\tmy ($n, $h, $header);\n\n\t$n = $socket->read($header, 8);\n\treturn undef if !defined $n or $n != 8;\n\n\t@{$h}{qw/ version type id clen plen /} = unpack(\"CCnnC\", $header);\n\n\t$n = $socket->read($h->{content}, $h->{clen});\n\treturn undef if $n != $h->{clen};\n\n\t$n = $socket->read($h->{padding}, $h->{plen});\n\treturn undef if $n != $h->{plen};\n\n\t$h->{socket} = $socket;\n\treturn $h;\n}\n\nsub fastcgi_respond($$) {\n\tmy ($h, $body) = @_;\n\n\t# stdout\n\t$h->{socket}->write(pack(\"CCnnCx\", $h->{version}, 6, $h->{id},\n\t\tlength($body), 8));\n\t$h->{socket}->write($body);\n\tselect(undef, undef, undef, 0.1);\n\t$h->{socket}->write(pack(\"xxxxxxxx\"));\n\tselect(undef, undef, undef, 0.1);\n\n\t# write some text to stdout and stderr split over multiple network\n\t# packets to test if we correctly set pipe length in various places\n\n\tmy $tt = \"test text, just for test\";\n\n\t$h->{socket}->write(pack(\"CCnnCx\", $h->{version}, 6, $h->{id},\n\t\tlength($tt . $tt), 0) . $tt);\n\tselect(undef, undef, undef, 0.1);\n\t$h->{socket}->write($tt . pack(\"CC\", $h->{version}, 7));\n\tselect(undef, undef, undef, 0.1);\n\t$h->{socket}->write(pack(\"nnCx\", $h->{id}, length($tt), 0));\n\tselect(undef, undef, undef, 0.1);\n\t$h->{socket}->write($tt);\n\tselect(undef, undef, undef, 0.1);\n\n\t# close stdout\n\t$h->{socket}->write(pack(\"CCnnCx\", $h->{version}, 6, $h->{id}, 0, 0));\n\n\tselect(undef, undef, undef, 0.1);\n\n\t# end request\n\t$h->{socket}->write(pack(\"CCnnCx\", $h->{version}, 3, $h->{id}, 8, 0));\n\tselect(undef, undef, undef, 0.1);\n\t$h->{socket}->write(pack(\"NCxxx\", 0, 0));\n}\n\nsub fastcgi_test_daemon {\n\tmy $server = IO::Socket::INET->new(\n\t\tProto => 'tcp',\n\t\tLocalAddr => '127.0.0.1:' . port(8081),\n\t\tListen => 5,\n\t\tReuse => 1\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\tlocal $SIG{PIPE} = 'IGNORE';\n\n\tmy $ccount = 0;\n\tmy $rcount = 0;\n\n\twhile (my $client = $server->accept()) {\n\t\t$client->autoflush(1);\n\t\tTest::Nginx::log_core('||', \"fastcgi connection\");\n\n\t\t$ccount++;\n\n\t\twhile (my $h = fastcgi_read_record($client)) {\n\t\t\tTest::Nginx::log_core('||', \"fastcgi record: \"\n\t\t\t\t. \" $h->{version}, $h->{type}, $h->{id}, \"\n\t\t\t\t. \"'$h->{content}'\");\n\n\t\t\t# skip everything unless stdin, then respond\n\t\t\tnext if $h->{type} != 5;\n\n\t\t\t$rcount++;\n\n\t\t\t# respond\n\t\t\tfastcgi_respond($h, <<EOF);\nLocation: http://localhost/redirect\nContent-Type: text/html\n\nSEE-THIS\nrequest: $rcount\nconnection: $ccount\nEOF\n\t\t}\n\n\t\t$ccount-- unless $rcount;\n\n\t\tclose $client;\n\t}\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/fastcgi_merge_params.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for fastcgi_param inheritance.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\neval { require FCGI; };\nplan(skip_all => 'FCGI not installed') if $@;\nplan(skip_all => 'win32') if $^O eq 'MSWin32';\n\nmy $t = Test::Nginx->new()->has(qw/http fastcgi cache/)->plan(9)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    fastcgi_cache_path  %%TESTDIR%%/cache  levels=1:2\n                        keys_zone=NAME:1m;\n\n    fastcgi_cache_key   stub;\n\n    fastcgi_param       HTTP_X_BLAH  \"blah\";\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        fastcgi_cache  NAME;\n\n        location / {\n            fastcgi_pass    127.0.0.1:8081;\n        }\n\n        location /no/ {\n            fastcgi_pass    127.0.0.1:8081;\n            fastcgi_cache   off;\n        }\n\n        location /custom/ {\n            fastcgi_pass    127.0.0.1:8081;\n            fastcgi_param   HTTP_X_BLAH  \"custom\";\n        }\n    }\n}\n\nEOF\n\n$t->run_daemon(\\&fastcgi_daemon);\n$t->run()->waitforsocket('127.0.0.1:' . port(8081));\n\n###############################################################################\n\nlike(http_get_ims('/'), qr/ims=;/,\n\t'if-modified-since cleared with cache');\nlike(http_get_ims('/'), qr/iums=;/,\n\t'if-unmodified-since cleared with cache');\nlike(http_get_ims('/'), qr/blah=blah;/,\n\t'custom params with cache');\n\nlike(http_get_ims('/no/'), qr/ims=blah;/,\n\t'if-modified-since preserved without cache');\nlike(http_get_ims('/no/'), qr/iums=blah;/,\n\t'if-unmodified-since preserved without cache');\nlike(http_get_ims('/'), qr/blah=blah;/,\n\t'custom params without cache');\n\nlike(http_get_ims('/custom/'), qr/ims=;/,\n\t'if-modified-since cleared with cache custom');\nlike(http_get_ims('/custom/'), qr/iums=;/,\n\t'if-unmodified-since cleared with cache custom');\nlike(http_get_ims('/custom/'), qr/blah=custom;/,\n\t'custom params with cache custom');\n\n###############################################################################\n\nsub http_get_ims {\n\tmy ($url) = @_;\n\treturn http(<<EOF);\nGET $url HTTP/1.0\nHost: localhost\nConnection: close\nIf-Modified-Since: blah\nIf-Unmodified-Since: blah\n\nEOF\n}\n\n###############################################################################\n\nsub fastcgi_daemon {\n\tmy $socket = FCGI::OpenSocket('127.0.0.1:' . port(8081), 5);\n\tmy $request = FCGI::Request(\\*STDIN, \\*STDOUT, \\*STDERR, \\%ENV,\n\t\t$socket);\n\n\tmy $count;\n\twhile( $request->Accept() >= 0 ) {\n\t\t$count++;\n\n\t\tmy $ims = $ENV{HTTP_IF_MODIFIED_SINCE} || '';\n\t\tmy $iums = $ENV{HTTP_IF_UNMODIFIED_SINCE} || '';\n\t\tmy $blah = $ENV{HTTP_X_BLAH} || '';\n\n\t\tprint <<EOF;\nLocation: http://localhost/redirect\nContent-Type: text/html\n\nims=$ims;iums=$iums;blah=$blah;\nEOF\n\t}\n\n\tFCGI::CloseSocket($socket);\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/fastcgi_merge_params2.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for fastcgi_param inheritance.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\neval { require FCGI; };\nplan(skip_all => 'FCGI not installed') if $@;\nplan(skip_all => 'win32') if $^O eq 'MSWin32';\n\nmy $t = Test::Nginx->new()->has(qw/http fastcgi cache/)->plan(4)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    fastcgi_cache_path  %%TESTDIR%%/cache  levels=1:2\n                        keys_zone=NAME:1m;\n\n    fastcgi_cache_key   stub;\n\n    # no fastcgi_param at all, cache switched on at server level\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        fastcgi_cache  NAME;\n\n        location / {\n            fastcgi_pass    127.0.0.1:8081;\n        }\n\n        location /no/ {\n            fastcgi_pass    127.0.0.1:8081;\n            fastcgi_cache   off;\n        }\n    }\n}\n\nEOF\n\n$t->run_daemon(\\&fastcgi_daemon);\n$t->run()->waitforsocket('127.0.0.1:' . port(8081));\n\n###############################################################################\n\nlike(http_get_ims('/'), qr/ims=;/,\n\t'if-modified-since cleared with cache');\nlike(http_get_ims('/'), qr/iums=;/,\n\t'if-unmodified-since cleared with cache');\n\nlike(http_get_ims('/no/'), qr/ims=blah;/,\n\t'if-modified-since preserved without cache');\nlike(http_get_ims('/no/'), qr/iums=blah;/,\n\t'if-unmodified-since preserved without cache');\n\n###############################################################################\n\nsub http_get_ims {\n\tmy ($url) = @_;\n\treturn http(<<EOF);\nGET $url HTTP/1.0\nHost: localhost\nConnection: close\nIf-Modified-Since: blah\nIf-Unmodified-Since: blah\n\nEOF\n}\n\n###############################################################################\n\nsub fastcgi_daemon {\n\tmy $socket = FCGI::OpenSocket('127.0.0.1:' . port(8081), 5);\n\tmy $request = FCGI::Request(\\*STDIN, \\*STDOUT, \\*STDERR, \\%ENV,\n\t\t$socket);\n\n\tmy $count;\n\twhile( $request->Accept() >= 0 ) {\n\t\t$count++;\n\n\t\tmy $ims = $ENV{HTTP_IF_MODIFIED_SINCE} || '';\n\t\tmy $iums = $ENV{HTTP_IF_UNMODIFIED_SINCE} || '';\n\t\tmy $blah = $ENV{HTTP_X_BLAH} || '';\n\n\t\tprint <<EOF;\nLocation: http://localhost/redirect\nContent-Type: text/html\n\nims=$ims;iums=$iums;blah=$blah;\nEOF\n\t}\n\n\tFCGI::CloseSocket($socket);\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/fastcgi_request_buffering.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for unbuffered request body with fastcgi backend.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\nuse Socket qw/ CRLF /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\neval { require FCGI; };\nplan(skip_all => 'FCGI not installed') if $@;\nplan(skip_all => 'win32') if $^O eq 'MSWin32';\n\nmy $t = Test::Nginx->new()->has(qw/http fastcgi rewrite/)->plan(15);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        client_header_buffer_size 1k;\n        fastcgi_request_buffering off;\n        fastcgi_param REQUEST_URI $request_uri;\n        fastcgi_param CONTENT_LENGTH $content_length;\n\n        location / {\n            client_body_buffer_size 2k;\n            fastcgi_pass 127.0.0.1:8081;\n        }\n        location /single {\n            client_body_in_single_buffer on;\n            fastcgi_pass 127.0.0.1:8081;\n        }\n        location /preread {\n            fastcgi_pass 127.0.0.1:8082;\n        }\n        location /error_page {\n            fastcgi_pass 127.0.0.1:8081;\n            error_page 404 /404;\n            fastcgi_intercept_errors on;\n        }\n        location /404 {\n            return 200 \"$request_body\\n\";\n        }\n    }\n}\n\nEOF\n\n$t->run_daemon(\\&fastcgi_daemon);\n$t->run()->waitforsocket('127.0.0.1:' . port(8081));\n\n###############################################################################\n\nlike(http_get('/'), qr/X-Body: \\x0d\\x0a?/ms, 'no body');\n\nlike(http_get_body('/', '0123456789'),\n\tqr/X-Body: 0123456789\\x0d?$/ms, 'body');\n\nlike(http_get_body('/', '0123456789' x 128),\n\tqr/X-Body: (0123456789){128}\\x0d?$/ms, 'body in two buffers');\n\nlike(http_get_body('/single', '0123456789' x 128),\n\tqr/X-Body: (0123456789){128}\\x0d?$/ms, 'body in single buffer');\n\nlike(http_get_body('/error_page', '0123456789'),\n\tqr/^0123456789$/m, 'body in error page');\n\n# pipelined requests\n\nlike(http_get_body('/', '0123456789', '0123456789' x 128, '0123456789' x 512,\n\t'foobar'), qr/X-Body: foobar\\x0d?$/ms, 'body pipelined');\nlike(http_get_body('/', '0123456789' x 128, '0123456789' x 512, '0123456789',\n\t'foobar'), qr/X-Body: foobar\\x0d?$/ms, 'body pipelined 2');\n\n# interactive tests\n\nmy $s = get_body('/preread', port(8082), 10);\nok($s, 'no preread');\n\nSKIP: {\nskip 'no preread failed', 3 unless $s;\n\nis($s->{upload}('01234'), '01234', 'no preread - body part');\nis($s->{upload}('56789'), '56789', 'no preread - body part 2');\n\nlike($s->{http_end}(), qr/200 OK/, 'no preread - response');\n\n}\n\n$s = get_body('/preread', port(8082), 10, '01234');\nok($s, 'preread');\n\nSKIP: {\nskip 'preread failed', 3 unless $s;\n\nis($s->{preread}, '01234', 'preread - preread');\nis($s->{upload}('56789'), '56789', 'preread - body');\n\nlike($s->{http_end}(), qr/200 OK/, 'preread - response');\n\n}\n\n###############################################################################\n\nsub http_get_body {\n\tmy $uri = shift;\n\tmy $last = pop;\n\treturn http( join '', (map {\n\t\tmy $body = $_;\n\t\t\"GET $uri HTTP/1.1\" . CRLF\n\t\t. \"Host: localhost\" . CRLF\n\t\t. \"Content-Length: \" . (length $body) . CRLF . CRLF\n\t\t. $body\n\t} @_),\n\t\t\"GET $uri HTTP/1.1\" . CRLF\n\t\t. \"Host: localhost\" . CRLF\n\t\t. \"Connection: close\" . CRLF\n\t\t. \"Content-Length: \" . (length $last) . CRLF . CRLF\n\t\t. $last\n\t);\n}\n\n# Simple FastCGI responder implementation.\n\n# http://www.fastcgi.com/devkit/doc/fcgi-spec.html\n\nsub fastcgi_read_record($) {\n\tmy ($buf) = @_;\n\tmy $h;\n\n\treturn undef unless length $$buf;\n\n\t@{$h}{qw/ version type id clen plen /} = unpack(\"CCnnC\", $$buf);\n\n\t$h->{content} = substr $$buf, 8, $h->{clen};\n\t$h->{padding} = substr $$buf, 8 + $h->{clen}, $h->{plen};\n\n\t$$buf = substr $$buf, 8 + $h->{clen} + $h->{plen};\n\n\treturn $h;\n}\n\nsub fastcgi_respond($$$$) {\n\tmy ($socket, $version, $id, $body) = @_;\n\n\t# stdout\n\t$socket->write(pack(\"CCnnCx\", $version, 6, $id, length($body), 8));\n\t$socket->write($body);\n\tselect(undef, undef, undef, 0.1);\n\t$socket->write(pack(\"xxxxxxxx\"));\n\tselect(undef, undef, undef, 0.1);\n\n\t# write some text to stdout and stderr split over multiple network\n\t# packets to test if we correctly set pipe length in various places\n\n\tmy $tt = \"test text, just for test\";\n\n\t$socket->write(pack(\"CCnnCx\", $version, 6, $id,\n\t\tlength($tt . $tt), 0) . $tt);\n\tselect(undef, undef, undef, 0.1);\n\t$socket->write($tt . pack(\"CC\", $version, 7));\n\tselect(undef, undef, undef, 0.1);\n\t$socket->write(pack(\"nnCx\", $id, length($tt), 0));\n\tselect(undef, undef, undef, 0.1);\n\t$socket->write($tt);\n\tselect(undef, undef, undef, 0.1);\n\n\t# close stdout\n\t$socket->write(pack(\"CCnnCx\", $version, 6, $id, 0, 0));\n\n\tselect(undef, undef, undef, 0.1);\n\n\t# end request\n\t$socket->write(pack(\"CCnnCx\", $version, 3, $id, 8, 0));\n\tselect(undef, undef, undef, 0.1);\n\t$socket->write(pack(\"NCxxx\", 0, 0));\n}\n\nsub get_body {\n\tmy ($url, $port, $length, $body) = @_;\n\tmy ($server, $client, $s);\n\tmy ($version, $id);\n\n\t$server = IO::Socket::INET->new(\n\t\tProto => 'tcp',\n\t\tLocalHost => '127.0.0.1',\n\t\tLocalPort => $port,\n\t\tListen => 5,\n\t\tReuse => 1\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\tmy $r = <<EOF;\nGET $url HTTP/1.1\nHost: localhost\nConnection: close\nContent-Length: $length\n\nEOF\n\n\tif (defined $body) {\n\t\t$r .= $body;\n\t}\n\n\t$s = http($r, start => 1);\n\n\teval {\n\t\tlocal $SIG{ALRM} = sub { die \"timeout\\n\" };\n\t\tlocal $SIG{PIPE} = sub { die \"sigpipe\\n\" };\n\t\talarm(5);\n\n\t\t$client = $server->accept();\n\n\t\tlog2c(\"(new connection $client)\");\n\n\t\talarm(0);\n\t};\n\talarm(0);\n\tif ($@) {\n\t\tlog_in(\"died: $@\");\n\t\treturn undef;\n\t}\n\n\t$client->sysread(my $buf, 1024);\n\tlog2i($buf);\n\n\t$body = '';\n\n\twhile (my $h = fastcgi_read_record(\\$buf)) {\n\t\t$version = $h->{version};\n\t\t$id = $h->{id};\n\n\t\t# skip everything unless stdin\n\t\tnext if $h->{type} != 5;\n\n\t\t$body .= $h->{content};\n\t}\n\n\tmy $f = { preread => $body };\n\t$f->{upload} = sub {\n\t\tmy $buf = shift;\n\n\t\teval {\n\t\t\tlocal $SIG{ALRM} = sub { die \"timeout\\n\" };\n\t\t\tlocal $SIG{PIPE} = sub { die \"sigpipe\\n\" };\n\t\t\talarm(5);\n\n\t\t\tlog_out($buf);\n\t\t\t$s->write($buf);\n\n\t\t\t$client->sysread($buf, 1024);\n\t\t\tlog2i($buf);\n\n\t\t\t$body = '';\n\n\t\t\twhile (my $h = fastcgi_read_record(\\$buf)) {\n\n\t\t\t\t# skip everything unless stdin\n\t\t\t\tnext if $h->{type} != 5;\n\n\t\t\t\t$body .= $h->{content};\n\t\t\t}\n\n\t\t\talarm(0);\n\t\t};\n\t\talarm(0);\n\t\tif ($@) {\n\t\t\tlog_in(\"died: $@\");\n\t\t\treturn undef;\n\t\t}\n\n\t\treturn $body;\n\t};\n\t$f->{http_end} = sub {\n\t\tmy $buf = '';\n\n\t\teval {\n\t\t\tlocal $SIG{ALRM} = sub { die \"timeout\\n\" };\n\t\t\tlocal $SIG{PIPE} = sub { die \"sigpipe\\n\" };\n\t\t\talarm(5);\n\n\t\t\tfastcgi_respond($client, $version, $id, <<EOF);\nStatus: 200 OK\nConnection: close\nX-Port: $port\n\nOK\nEOF\n\n\t\t\t$client->close;\n\n\t\t\t$s->sysread($buf, 1024);\n\t\t\tlog_in($buf);\n\n\t\t\talarm(0);\n\t\t};\n\t\talarm(0);\n\t\tif ($@) {\n\t\t\tlog_in(\"died: $@\");\n\t\t\treturn undef;\n\t\t}\n\n\t\treturn $buf;\n\t};\n\treturn $f;\n}\n\nsub log2i { Test::Nginx::log_core('|| <<', @_); }\nsub log2o { Test::Nginx::log_core('|| >>', @_); }\nsub log2c { Test::Nginx::log_core('||', @_); }\n\n###############################################################################\n\nsub fastcgi_daemon {\n\tmy $socket = FCGI::OpenSocket('127.0.0.1:' . port(8081), 5);\n\tmy $request = FCGI::Request(\\*STDIN, \\*STDOUT, \\*STDERR, \\%ENV,\n\t\t$socket);\n\n\tmy $count;\n\tmy $body;\n\n\twhile( $request->Accept() >= 0 ) {\n\t\t$count++;\n\t\tread(STDIN, $body, $ENV{'CONTENT_LENGTH'} || 0);\n\n\t\tif ($ENV{REQUEST_URI} eq '/error_page') {\n\t\t\tprint \"Status: 404 Not Found\" . CRLF . CRLF;\n\t\t\tnext;\n\t\t}\n\n\t\tprint <<EOF;\nLocation: http://localhost/redirect\nContent-Type: text/html\nX-Body: $body\n\nSEE-THIS\n$count\nEOF\n\t}\n\n\tFCGI::CloseSocket($socket);\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/fastcgi_request_buffering_chunked.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for unbuffered request body with fastcgi backend,\n# chunked transfer-encoding.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\nuse Socket qw/ CRLF /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\neval { require FCGI; };\nplan(skip_all => 'FCGI not installed') if $@;\nplan(skip_all => 'win32') if $^O eq 'MSWin32';\n\nmy $t = Test::Nginx->new()->has(qw/http fastcgi rewrite/)->plan(19);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        client_header_buffer_size 1k;\n        fastcgi_request_buffering off;\n        fastcgi_param REQUEST_URI $request_uri;\n\n        location / {\n            client_body_buffer_size 2k;\n            fastcgi_pass 127.0.0.1:8081;\n        }\n        location /single {\n            client_body_in_single_buffer on;\n            fastcgi_pass 127.0.0.1:8081;\n        }\n        location /preread {\n            fastcgi_pass 127.0.0.1:8082;\n        }\n        location /error_page {\n            fastcgi_pass 127.0.0.1:8081;\n            error_page 404 /404;\n            fastcgi_intercept_errors on;\n        }\n        location /404 {\n            return 200 \"$request_body\\n\";\n        }\n    }\n}\n\nEOF\n\n$t->run_daemon(\\&fastcgi_daemon);\n$t->run()->waitforsocket('127.0.0.1:' . port(8081));\n\n###############################################################################\n\nlike(http_get('/'), qr/X-Body: \\x0d\\x0a?/ms, 'no body');\n\nlike(http_get_body('/', '0123456789'),\n\tqr/X-Body: 0123456789\\x0d?$/ms, 'body');\n\nlike(http_get_body('/', '0123456789' x 128),\n\tqr/X-Body: (0123456789){128}\\x0d?$/ms, 'body in two buffers');\n\nlike(http_get_body('/single', '0123456789' x 128),\n\tqr/X-Body: (0123456789){128}\\x0d?$/ms, 'body in single buffer');\n\nlike(http_get_body('/error_page', '0123456789'),\n\tqr/^0123456789$/m, 'body in error page');\n\n# pipelined requests\n\nlike(http_get_body('/', '0123456789', '0123456789' x 128, '0123456789' x 512,\n\t'foobar'), qr/X-Body: foobar\\x0d?$/ms, 'body pipelined');\nlike(http_get_body('/', '0123456789' x 128, '0123456789' x 512, '0123456789',\n\t'foobar'), qr/X-Body: foobar\\x0d?$/ms, 'body pipelined 2');\n\n# interactive tests\n\nmy $s = get_body('/preread', port(8082));\nok($s, 'no preread');\n\nSKIP: {\nskip 'no preread failed', 3 unless $s;\n\nis($s->{upload}('01234'), '01234', 'no preread - body part');\nis($s->{upload}('56789', last => 1), '56789', 'no preread - body part 2');\n\nlike($s->{http_end}(), qr/200 OK/, 'no preread - response');\n\n}\n\n$s = get_body('/preread', port(8082), '01234');\nok($s, 'preread');\n\nSKIP: {\nskip 'preread failed', 3 unless $s;\n\nis($s->{preread}, '01234', 'preread - preread');\nis($s->{upload}('56789', last => 1), '56789', 'preread - body');\n\nlike($s->{http_end}(), qr/200 OK/, 'preread - response');\n\n}\n\n$s = get_body('/preread', port(8082), '01234', many => 1);\nok($s, 'chunks');\n\nSKIP: {\nskip 'chunks failed', 3 unless $s;\n\nis($s->{preread}, '01234many', 'chunks - preread');\nis($s->{upload}('56789', many => 1, last => 1), '56789many', 'chunks - body');\n\nlike($s->{http_end}(), qr/200 OK/, 'chunks - response');\n\n}\n\n###############################################################################\n\nsub http_get_body {\n\tmy $uri = shift;\n\tmy $last = pop;\n\treturn http( join '', (map {\n\t\tmy $body = $_;\n\t\t\"GET $uri HTTP/1.1\" . CRLF\n\t\t. \"Host: localhost\" . CRLF\n\t\t. \"Transfer-Encoding: chunked\" . CRLF . CRLF\n\t\t. sprintf(\"%x\", length $body) . CRLF\n\t\t. $body . CRLF\n\t\t. \"0\" . CRLF . CRLF\n\t} @_),\n\t\t\"GET $uri HTTP/1.1\" . CRLF\n\t\t. \"Host: localhost\" . CRLF\n\t\t. \"Connection: close\" . CRLF\n\t\t. \"Transfer-Encoding: chunked\" . CRLF . CRLF\n\t\t. sprintf(\"%x\", length $last) . CRLF\n\t\t. $last . CRLF\n\t\t. \"0\" . CRLF . CRLF\n\t);\n}\n\n# Simple FastCGI responder implementation.\n\n# http://www.fastcgi.com/devkit/doc/fcgi-spec.html\n\nsub fastcgi_read_record($) {\n\tmy ($buf) = @_;\n\tmy $h;\n\n\treturn undef unless length $$buf;\n\n\t@{$h}{qw/ version type id clen plen /} = unpack(\"CCnnC\", $$buf);\n\n\t$h->{content} = substr $$buf, 8, $h->{clen};\n\t$h->{padding} = substr $$buf, 8 + $h->{clen}, $h->{plen};\n\n\t$$buf = substr $$buf, 8 + $h->{clen} + $h->{plen};\n\n\treturn $h;\n}\n\nsub fastcgi_respond($$$$) {\n\tmy ($socket, $version, $id, $body) = @_;\n\n\t# stdout\n\t$socket->write(pack(\"CCnnCx\", $version, 6, $id, length($body), 8));\n\t$socket->write($body);\n\tselect(undef, undef, undef, 0.1);\n\t$socket->write(pack(\"xxxxxxxx\"));\n\tselect(undef, undef, undef, 0.1);\n\n\t# write some text to stdout and stderr split over multiple network\n\t# packets to test if we correctly set pipe length in various places\n\n\tmy $tt = \"test text, just for test\";\n\n\t$socket->write(pack(\"CCnnCx\", $version, 6, $id,\n\t\tlength($tt . $tt), 0) . $tt);\n\tselect(undef, undef, undef, 0.1);\n\t$socket->write($tt . pack(\"CC\", $version, 7));\n\tselect(undef, undef, undef, 0.1);\n\t$socket->write(pack(\"nnCx\", $id, length($tt), 0));\n\tselect(undef, undef, undef, 0.1);\n\t$socket->write($tt);\n\tselect(undef, undef, undef, 0.1);\n\n\t# close stdout\n\t$socket->write(pack(\"CCnnCx\", $version, 6, $id, 0, 0));\n\n\tselect(undef, undef, undef, 0.1);\n\n\t# end request\n\t$socket->write(pack(\"CCnnCx\", $version, 3, $id, 8, 0));\n\tselect(undef, undef, undef, 0.1);\n\t$socket->write(pack(\"NCxxx\", 0, 0));\n}\n\nsub get_body {\n\tmy ($url, $port, $body, %extra) = @_;\n\tmy ($server, $client, $s);\n\tmy ($last, $many) = (0, 0);\n\tmy ($version, $id);\n\n\t$last = $extra{last} if defined $extra{last};\n\t$many = $extra{many} if defined $extra{many};\n\n\t$server = IO::Socket::INET->new(\n\t\tProto => 'tcp',\n\t\tLocalHost => '127.0.0.1',\n\t\tLocalPort => $port,\n\t\tListen => 5,\n\t\tReuse => 1\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\tmy $r = <<EOF;\nGET $url HTTP/1.1\nHost: localhost\nConnection: close\nTransfer-Encoding: chunked\n\nEOF\n\n\tif (defined $body) {\n\t\t$r .= sprintf(\"%x\", length $body) . CRLF;\n\t\t$r .= $body . CRLF;\n\t}\n\tif (defined $body && $many) {\n\t\t$r .= sprintf(\"%x\", length 'many') . CRLF;\n\t\t$r .= 'many' . CRLF;\n\t}\n\tif ($last) {\n\t\t$r .= \"0\" . CRLF . CRLF;\n\t}\n\n\t$s = http($r, start => 1);\n\n\teval {\n\t\tlocal $SIG{ALRM} = sub { die \"timeout\\n\" };\n\t\tlocal $SIG{PIPE} = sub { die \"sigpipe\\n\" };\n\t\talarm(5);\n\n\t\t$client = $server->accept();\n\n\t\tlog2c(\"(new connection $client)\");\n\n\t\talarm(0);\n\t};\n\talarm(0);\n\tif ($@) {\n\t\tlog_in(\"died: $@\");\n\t\treturn undef;\n\t}\n\n\t$client->sysread(my $buf, 1024);\n\tlog2i($buf);\n\n\t$body = '';\n\n\twhile (my $h = fastcgi_read_record(\\$buf)) {\n\t\t$version = $h->{version};\n\t\t$id = $h->{id};\n\n\t\t# skip everything unless stdin\n\t\tnext if $h->{type} != 5;\n\n\t\t$body .= $h->{content};\n\t}\n\n\tmy $f = { preread => $body };\n\t$f->{upload} = sub {\n\t\tmy ($body, %extra) = @_;\n\t\tmy ($last, $many) = (0, 0);\n\n\t\t$last = $extra{last} if defined $extra{last};\n\t\t$many = $extra{many} if defined $extra{many};\n\n\t\tmy $buf = sprintf(\"%x\", length $body) . CRLF;\n\t\t$buf .= $body . CRLF;\n\t\tif ($many) {\n\t\t\t$buf .= sprintf(\"%x\", length 'many') . CRLF;\n\t\t\t$buf .= 'many' . CRLF;\n\t\t}\n\t\tif ($last) {\n\t\t\t$buf .= \"0\" . CRLF . CRLF;\n\t\t}\n\n\t\teval {\n\t\t\tlocal $SIG{ALRM} = sub { die \"timeout\\n\" };\n\t\t\tlocal $SIG{PIPE} = sub { die \"sigpipe\\n\" };\n\t\t\talarm(5);\n\n\t\t\tlog_out($buf);\n\t\t\t$s->write($buf);\n\n\t\t\t$client->sysread($buf, 1024);\n\t\t\tlog2i($buf);\n\n\t\t\t$body = '';\n\n\t\t\twhile (my $h = fastcgi_read_record(\\$buf)) {\n\n\t\t\t\t# skip everything unless stdin\n\t\t\t\tnext if $h->{type} != 5;\n\n\t\t\t\t$body .= $h->{content};\n\t\t\t}\n\n\t\t\talarm(0);\n\t\t};\n\t\talarm(0);\n\t\tif ($@) {\n\t\t\tlog_in(\"died: $@\");\n\t\t\treturn undef;\n\t\t}\n\n\t\treturn $body;\n\t};\n\t$f->{http_end} = sub {\n\t\tmy $buf = '';\n\n\t\teval {\n\t\t\tlocal $SIG{ALRM} = sub { die \"timeout\\n\" };\n\t\t\tlocal $SIG{PIPE} = sub { die \"sigpipe\\n\" };\n\t\t\talarm(5);\n\n\t\t\tfastcgi_respond($client, $version, $id, <<EOF);\nStatus: 200 OK\nConnection: close\nX-Port: $port\n\nOK\nEOF\n\n\t\t\t$client->close;\n\n\t\t\t$s->sysread($buf, 1024);\n\t\t\tlog_in($buf);\n\n\t\t\t$s->close();\n\n\t\t\talarm(0);\n\t\t};\n\t\talarm(0);\n\t\tif ($@) {\n\t\t\tlog_in(\"died: $@\");\n\t\t\treturn undef;\n\t\t}\n\n\t\treturn $buf;\n\t};\n\treturn $f;\n}\n\nsub log2i { Test::Nginx::log_core('|| <<', @_); }\nsub log2o { Test::Nginx::log_core('|| >>', @_); }\nsub log2c { Test::Nginx::log_core('||', @_); }\n\n###############################################################################\n\nsub fastcgi_daemon {\n\tmy $socket = FCGI::OpenSocket('127.0.0.1:' . port(8081), 5);\n\tmy $request = FCGI::Request(\\*STDIN, \\*STDOUT, \\*STDERR, \\%ENV,\n\t\t$socket);\n\n\tmy $count;\n\tmy ($body, $buf);\n\n\twhile( $request->Accept() >= 0 ) {\n\t\t$count++;\n\t\t$body = '';\n\n\t\tdo {\n\t\t\tread(STDIN, $buf, 1024);\n\t\t\t$body .= $buf;\n\t\t} while (length $buf);\n\n\t\tif ($ENV{REQUEST_URI} eq '/error_page') {\n\t\t\tprint \"Status: 404 Not Found\" . CRLF . CRLF;\n\t\t\tnext;\n\t\t}\n\n\t\tprint <<EOF;\nLocation: http://localhost/redirect\nContent-Type: text/html\nX-Body: $body\n\nSEE-THIS\n$count\nEOF\n\t}\n\n\tFCGI::CloseSocket($socket);\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/fastcgi_split.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Test for fastcgi backend.\n# Incorrect split headers handling after switching to next server,\n# as reported by Lucas Molas.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\nuse Socket qw/ CR LF CRLF /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\neval { require FCGI; };\nplan(skip_all => 'FCGI not installed') if $@;\nplan(skip_all => 'win32') if $^O eq 'MSWin32';\n\nmy $t = Test::Nginx->new()->has(qw/http fastcgi/)->plan(1)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    upstream u {\n        server 127.0.0.1:8081;\n        server 127.0.0.1:8082;\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            fastcgi_pass u;\n            fastcgi_param REQUEST_URI $request_uri;\n            fastcgi_next_upstream invalid_header;\n        }\n    }\n}\n\nEOF\n\n$t->run_daemon(\\&fastcgi_daemon, port(8081));\n$t->run_daemon(\\&fastcgi_daemon, port(8082));\n\n$t->run();\n\n$t->waitforsocket('127.0.0.1:' . port(8081));\n$t->waitforsocket('127.0.0.1:' . port(8082));\n\n###############################################################################\n\nlike(http_get('/'), qr/^Good: header/ms, 'fastcgi next upstream');\n\n###############################################################################\n\nsub fastcgi_daemon {\n\tmy ($port) = @_;\n\tmy $socket = FCGI::OpenSocket(\"127.0.0.1:$port\", 5);\n\tmy $request = FCGI::Request(\\*STDIN, \\*STDOUT, \\*STDERR, \\%ENV,\n\t\t$socket);\n\n\tmy $count;\n\twhile( $request->Accept() >= 0 ) {\n\t\t$count++;\n\n\t\tif ($port == port(8081)) {\n\t\t\tprint 'BAD';\n\t\t}\n\t\tif ($port == port(8082)) {\n\t\t\tprint 'Good: header' . CRLF . CRLF;\n\t\t}\n\t}\n\n\tFCGI::CloseSocket($socket);\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/fastcgi_unix.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Test for fastcgi backend with unix socket.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\neval { require FCGI; };\nplan(skip_all => 'FCGI not installed') if $@;\nplan(skip_all => 'win32') if $^O eq 'MSWin32';\n\neval { require IO::Socket::UNIX; };\nplan(skip_all => 'IO::Socket::UNIX not installed') if $@;\n\nmy $t = Test::Nginx->new()->has(qw/http fastcgi unix/)->plan(6)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    upstream u {\n        server 127.0.0.1:8081;\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            fastcgi_pass unix:%%TESTDIR%%/unix.sock;\n            fastcgi_param REQUEST_URI $request_uri;\n        }\n\n        location /var {\n            fastcgi_pass $arg_b;\n            fastcgi_param REQUEST_URI $request_uri;\n        }\n    }\n}\n\nEOF\n\nmy $path = $t->testdir() . '/unix.sock';\n\n$t->run_daemon(\\&fastcgi_daemon, $path);\n$t->run();\n\n# wait for unix socket to appear\n\nfor (1 .. 50) {\n\tlast if -S $path;\n\tselect undef, undef, undef, 0.1;\n}\n\n###############################################################################\n\nlike(http_get('/'), qr/SEE-THIS/, 'fastcgi request');\nlike(http_get('/redir'), qr/ 302 /, 'fastcgi redirect');\nlike(http_get('/'), qr/^3$/m, 'fastcgi third request');\n\nunlike(http_head('/'), qr/SEE-THIS/, 'no data in HEAD');\n\nlike(http_get('/stderr'), qr/SEE-THIS/, 'large stderr handled');\n\nlike(http_get(\"/var?b=unix:$path\"), qr/SEE-THIS/, 'fastcgi with variables');\n\n###############################################################################\n\nsub fastcgi_daemon {\n\tmy $socket = FCGI::OpenSocket(shift, 5);\n\tmy $request = FCGI::Request(\\*STDIN, \\*STDOUT, \\*STDERR, \\%ENV,\n\t\t$socket);\n\n\tmy $count;\n\twhile( $request->Accept() >= 0 ) {\n\t\t$count++;\n\n\t\tif ($ENV{REQUEST_URI} eq '/stderr') {\n\t\t\twarn \"sample stderr text\" x 512;\n\t\t}\n\n\t\tprint <<EOF;\nLocation: http://localhost/redirect\nContent-Type: text/html\n\nSEE-THIS\n$count\nEOF\n\t}\n\n\tFCGI::CloseSocket($socket);\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/fastcgi_variables.t",
    "content": "#!/usr/bin/perl\n\n# (C) Andrey Zelenkov\n# (C) Nginx, Inc.\n\n# Test for fastcgi variables.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse Socket qw/ CRLF /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\neval { require FCGI; };\nplan(skip_all => 'FCGI not installed') if $@;\nplan(skip_all => 'win32') if $^O eq 'MSWin32';\n\nmy $t = Test::Nginx->new()->has(qw/http fastcgi rewrite/)->plan(3)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        add_header X-Script-Name $fastcgi_script_name;\n        add_header X-Path-Info $fastcgi_path_info;\n\n        location / {\n            fastcgi_pass 127.0.0.1:8081;\n            fastcgi_index index.php;\n        }\n\n        location /info {\n            fastcgi_pass 127.0.0.1:8081;\n            fastcgi_split_path_info ^(.+\\.php)(.*)$;\n        }\n    }\n}\n\nEOF\n\n$t->run_daemon(\\&fastcgi_daemon);\n$t->run()->waitforsocket('127.0.0.1:' . port(8081));\n\n###############################################################################\n\nlike(http_get('/'), qr/X-Script-Name: \\/index\\.php/ms, 'script name');\nlike(http_get('/info.php/path/info'), qr/X-Script-Name: \\/info\\.php/ms,\n\t'info script name');\nlike(http_get('/info.php/path/info'), qr/X-Path-Info: \\/path\\/info/ms,\n\t'info path');\n\n###############################################################################\n\nsub fastcgi_daemon {\n\tmy $socket = FCGI::OpenSocket('127.0.0.1:' . port(8081), 5);\n\tmy $request = FCGI::Request(\\*STDIN, \\*STDOUT, \\*STDERR, \\%ENV,\n\t\t$socket);\n\n\twhile( $request->Accept() >= 0 ) {\n\t\tprint CRLF . CRLF;\n\t}\n\n\tFCGI::CloseSocket($socket);\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/geo.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n# (C) Andrey Zelenkov\n# (C) Nginx, Inc.\n\n# Tests for nginx geo module.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http geo/);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    geo $geo {\n        127.0.0.0/8   loopback;\n        192.0.2.0/24  test;\n        0.0.0.0/0     world;\n    }\n\n    geo $geo_include {\n        include       geo.conf;\n        192.0.2.0/24  test;\n        0.0.0.0/0     world;\n    }\n\n    geo $geo_delete {\n        127.0.0.0/8   loopback;\n        192.0.2.0/24  test;\n        0.0.0.0/0     world;\n        delete        127.0.0.0/8;\n    }\n\n    geo $arg_ip $geo_from_arg {\n        default       default;\n        127.0.0.0/8   loopback;\n        192.0.2.0/24  test;\n    }\n\n    geo $arg_ip $geo_arg_ranges {\n        ranges;\n        default                default;\n        127.0.0.0-127.0.0.1    loopback;\n\n        # ranges with two /16 networks\n        # the latter network has greater two least octets\n        # (see 1301a58b5dac for details)\n        10.10.3.0-10.11.2.255  foo;\n        10.12.3.0-10.13.2.255  foo2;\n        delete                 10.10.3.0-10.11.2.255;\n    }\n\n    geo $geo_proxy {\n        default       default;\n        proxy         127.0.0.1;\n        127.0.0.0/8   loopback;\n        192.0.2.0/24  test;\n    }\n\n    geo $geo_proxy_recursive {\n        proxy_recursive;\n        default       default;\n        proxy         127.0.0.1;\n        127.0.0.0/8   loopback;\n        192.0.2.0/24  test;\n    }\n\n    geo $geo_ranges {\n        ranges;\n        default                    default;\n        127.0.0.0-127.255.255.255  loopback;\n        192.0.2.0-192.0.2.255      test;\n    }\n\n    geo $geo_ranges_include {\n        ranges;\n        default                default;\n        include                geo-ranges.conf;\n        192.0.2.0-192.0.2.255  test;\n    }\n\n    geo $geo_ranges_delete {\n        ranges;\n        default                default;\n        127.0.0.0-127.0.0.255  test;\n        127.0.0.1-127.0.0.1    loopback;\n        delete                 127.0.0.0-127.0.0.0;\n        delete                 127.0.0.2-127.0.0.255;\n        delete                 127.0.0.1-127.0.0.1;\n    }\n\n    # delete range with two /16\n    geo $geo_ranges_delete_2 {\n        ranges;\n        default              default;\n        127.0.0.0-127.1.0.0  loopback;\n        delete               127.0.0.0-127.1.0.0;\n    }\n\n    geo $geo_before {\n        ranges;\n        default                default;\n        127.0.0.1-127.0.0.255  loopback;\n        127.0.0.0-127.0.0.0    test;\n    }\n\n    geo $geo_after {\n        ranges;\n        default                default;\n        127.0.0.0-127.0.0.1    loopback;\n        127.0.0.2-127.0.0.255  test;\n    }\n\n    geo $geo_insert {\n        ranges;\n        default                default;\n        127.0.0.0-127.0.0.255  test;\n        127.0.0.1-127.0.0.2    test2;\n        127.0.0.1-127.0.0.1    loopback;\n    }\n\n    geo $geo_insert_before {\n        ranges;\n        default                default;\n        127.0.0.0-127.0.0.255  test;\n        127.0.0.0-127.0.0.1    loopback;\n    }\n\n    geo $geo_insert_after {\n        ranges;\n        default                default;\n        127.0.0.0-127.0.0.255  test;\n        127.0.0.1-127.0.0.255  loopback;\n     }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            add_header X-IP   $remote_addr;\n            add_header X-Geo  $geo;\n            add_header X-Inc  $geo_include;\n            add_header X-Del  $geo_delete;\n            add_header X-Ran  $geo_ranges;\n            add_header X-RIn  $geo_ranges_include;\n            add_header X-ABe  $geo_before;\n            add_header X-AAf  $geo_after;\n            add_header X-Ins  $geo_insert;\n            add_header X-IBe  $geo_insert_before;\n            add_header X-IAf  $geo_insert_after;\n            add_header X-Arg  $geo_from_arg;\n            add_header X-ARa  $geo_arg_ranges;\n            add_header X-XFF  $geo_proxy;\n            add_header X-XFR  $geo_proxy_recursive;\n        }\n\n        location /2 {\n            add_header X-RDe  $geo_ranges_delete;\n            add_header X-RD2  $geo_ranges_delete_2;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('1', '');\n$t->write_file('2', '');\n$t->write_file('geo.conf', '127.0.0.0/8  loopback;');\n$t->write_file('geo-ranges.conf', '127.0.0.0-127.255.255.255  loopback;');\n\n$t->run();\n\nplan(skip_all => 'no 127.0.0.1 on host')\n\tif http_get('/1') !~ /X-IP: 127.0.0.1/m;\n\n$t->plan(22);\n\n###############################################################################\n\nmy $r = http_get('/1');\nlike($r, qr/^X-Geo: loopback/m, 'geo');\nlike($r, qr/^X-Inc: loopback/m, 'geo include');\nlike($r, qr/^X-Del: world/m, 'geo delete');\nlike($r, qr/^X-Ran: loopback/m, 'geo ranges');\nlike($r, qr/^X-RIn: loopback/m, 'geo ranges include');\n\nlike(http_get('/2'), qr/^X-RDe: default/m, 'geo ranges delete');\nlike(http_get('/2'), qr/^X-RD2: default/m, 'geo ranges delete 2');\n\nlike($r, qr/^X-ABe: loopback/m, 'geo ranges add before');\nlike($r, qr/^X-AAf: loopback/m, 'geo ranges add after');\nlike($r, qr/^X-Ins: loopback/m, 'geo ranges insert');\nlike($r, qr/^X-IBe: loopback/m, 'geo ranges insert before');\nlike($r, qr/^X-IAf: loopback/m, 'geo ranges insert after');\n\nlike(http_get('/1?ip=192.0.2.1'), qr/^X-Arg: test/m, 'geo from variable');\nlike(http_get('/1?ip=10.0.0.1'), qr/^X-Arg: default/m, 'geo default');\nlike(http_get('/1?ip=10.0.0.1'), qr/^X-ARa: default/m, 'geo ranges default');\nlike(http_get('/1?ip=10.13.2.1'), qr/^X-ARa: foo2/m, 'geo ranges add');\nlike(http_get('/1?ip=10.11.2.1'), qr/^X-ARa: default/m,\n\t'geo delete range from variable');\n\nlike(http_xff('192.0.2.1'), qr/^X-XFF: test/m, 'geo proxy');\nlike(http_xff('10.0.0.1'), qr/^X-XFF: default/m, 'geo proxy default');\nlike(http_xff('10.0.0.1, 192.0.2.1'), qr/^X-XFF: test/m, 'geo proxy long');\n\nlike(http_xff('192.0.2.1, 127.0.0.1'), qr/^X-XFF: loopback/m,\n\t'geo proxy_recursive off');\nlike(http_xff('192.0.2.1, 127.0.0.1'), qr/^X-XFR: test/m,\n\t'geo proxy_recursive on');\n\n###############################################################################\n\nsub http_xff {\n\tmy ($xff) = @_;\n\treturn http(<<EOF);\nGET /1 HTTP/1.0\nHost: localhost\nX-Forwarded-For: $xff\n\nEOF\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/geo_binary.t",
    "content": "#!/usr/bin/perl\n\n# (C) Andrey Zelenkov\n# (C) Nginx, Inc.\n\n# Tests for nginx geo module with binary base.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nplan(skip_all => 'long configuration parsing') unless $ENV{TEST_NGINX_UNSAFE};\n\nmy $t = Test::Nginx->new()->has(qw/http geo/);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    geo $geo_base_create {\n        ranges;\n        include  base.conf;\n    }\n\n    geo $geo_base_include {\n        ranges;\n        include  base.conf;\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            add_header X-IP   $remote_addr;\n            add_header X-GBc  $geo_base_create;\n            add_header X-GBi  $geo_base_include;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('1', '');\n$t->write_file('base.conf', join('', map {\n\t\"127.\" . $_/256/256 % 256 . \".\" . $_/256 % 256 . \".\" . $_ % 256 .\n\t\"-127.\" . $_/256/256 % 256 . \".\" . $_/256 % 256 . \".\" .$_ % 256 . \" \" .\n\t($_ == 1 ? \"loopback\" : \"range$_\") . \";\" } (0 .. 100000)));\n\n$t->run();\n\nplan(skip_all => 'no 127.0.0.1 on host')\n\tif http_get('/1') !~ /X-IP: 127.0.0.1/m;\n\n$t->plan(2);\n\n###############################################################################\n\nmy $r = http_get('/1');\nlike($r, qr/^X-GBc: loopback/m, 'geo binary base create');\nlike($r, qr/^X-GBi: loopback/m, 'geo binary base include');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/geo_ipv6.t",
    "content": "#!/usr/bin/perl\n\n# (C) Andrey Zelenkov\n# (C) Nginx, Inc.\n\n# Tests for geo module with IPv6.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http geo proxy/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    geo $geo {\n        ::1/128         loopback;\n        2001:0db8::/32  test;\n        ::/0            world;\n    }\n\n    geo $geo_delete {\n        ::1/128         loopback;\n        2001:0db8::/32  test;\n        ::/0            world;\n        delete          ::1/128;\n    }\n\n    geo $geo_proxy {\n        ranges;\n        proxy                ::1;\n        default              default;\n        192.0.2.1-192.0.2.1  test;\n    }\n\n    geo $arg_ip $geo_arg {\n        default       default;\n        ::1/128       loopback;\n        192.0.2.0/24  test;\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            proxy_pass http://[::1]:%%PORT_8080%%/;\n        }\n    }\n\n    server {\n        listen       [::1]:%%PORT_8080%%;\n        server_name  localhost;\n\n        location / {\n            add_header X-Geo  $geo;\n            add_header X-Del  $geo_delete;\n            add_header X-XFF  $geo_proxy;\n            add_header X-Arg  $geo_arg;\n        }\n\n        location /addr {\n            add_header X-IP   $remote_addr;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('index.html', '');\n$t->write_file('addr', '');\n$t->try_run('no inet6 support');\n\nplan(skip_all => 'no ::1 on host')\n\tif http_get('/addr') !~ /X-IP: ::1/m;\n\n$t->plan(4);\n\n###############################################################################\n\nlike(http_get('/'), qr/^X-Geo: loopback/m, 'geo ipv6');\nlike(http_get('/'), qr/^X-Del: world/m, 'geo ipv6 delete');\n\nlike(http_xff('::ffff:192.0.2.1'), qr/^X-XFF: test/m, 'geo ipv6 ipv4-mapped');\nlike(http_get('/?ip=::ffff:192.0.2.1'), qr/^X-Arg: test/m,\n\t'geo ipv6 ipv4-mapped from variable');\n\n###############################################################################\n\nsub http_xff {\n\tmy ($xff) = @_;\n\treturn http(<<EOF);\nGET / HTTP/1.0\nHost: localhost\nX-Forwarded-For: $xff\n\nEOF\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/geo_unix.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for http geo module with unix socket.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http geo proxy unix/)->plan(6);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    geo $geo {\n        default                  default;\n        255.255.255.255          none;\n    }\n\n    geo $remote_addr $geora {\n        default                  default;\n        255.255.255.255          none;\n    }\n\n    geo $geor {\n        ranges;\n        0.0.0.0-255.255.255.254  test;\n        default                  none;\n    }\n\n    geo $remote_addr $georra {\n        ranges;\n        0.0.0.0-255.255.255.254  test;\n        default                  none;\n    }\n\n    geo $arg_ip $geo_arg {\n        default                  default;\n        192.0.2.0/24             test;\n    }\n\n    server {\n        listen       unix:%%TESTDIR%%/unix.sock;\n        server_name  localhost;\n\n        location / {\n            add_header X-Geo          $geo;\n            add_header X-Addr         $geora;\n            add_header X-Ranges       $geor;\n            add_header X-Ranges-Addr  $georra;\n            add_header X-Arg          $geo_arg;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n\n        location / {\n            proxy_pass http://unix:%%TESTDIR%%/unix.sock;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('index.html', '');\n$t->run();\n\n###############################################################################\n\nmy $r = http_get('/');\nlike($r, qr/^X-Geo: none/m, 'geo unix');\nlike($r, qr/^X-Ranges: none/m, 'geo unix ranges');\nlike($r, qr/^X-Addr: none/m, 'geo unix remote addr');\nlike($r, qr/^X-Ranges-Addr: none/m, 'geo unix ranges remote addr');\n\nlike(http_get('/?ip=192.0.2.1'), qr/^X-Arg: test/m, 'geo unix variable');\n\n$t->stop();\n\nis(-e $t->testdir() . '/unix.sock', undef, 'unix socket removed');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/geoip.t",
    "content": "#!/usr/bin/perl\n\n# (C) Andrey Zelenkov\n# (C) Nginx, Inc.\n\n# Tests for geoip module.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http http_geoip/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    geoip_proxy    127.0.0.1/32;\n\n    geoip_country  %%TESTDIR%%/country.dat;\n    geoip_city     %%TESTDIR%%/city.dat;\n    geoip_org      %%TESTDIR%%/org.dat;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            add_header X-Country-Code      $geoip_country_code;\n            add_header X-Country-Code3     $geoip_country_code3;\n            add_header X-Country-Name      $geoip_country_name;\n\n            add_header X-Area-Code         $geoip_area_code;\n            add_header X-C-Continent-Code  $geoip_city_continent_code;\n            add_header X-C-Country-Code    $geoip_city_country_code;\n            add_header X-C-Country-Code3   $geoip_city_country_code3;\n            add_header X-C-Country-Name    $geoip_city_country_name;\n            add_header X-Dma-Code          $geoip_dma_code;\n            add_header X-Latitude          $geoip_latitude;\n            add_header X-Longitude         $geoip_longitude;\n            add_header X-Region            $geoip_region;\n            add_header X-Region-Name       $geoip_region_name;\n            add_header X-City              $geoip_city;\n            add_header X-Postal-Code       $geoip_postal_code;\n\n            add_header X-Org               $geoip_org;\n        }\n    }\n}\n\nEOF\n\nmy $d = $t->testdir();\n\n# country database:\n#\n# \"10.0.0.1\",\"10.0.0.1\",\"RU\",\"Russian Federation\"\n# \"2001:db8::\",\"2001:db8::\",\"US\",\"United States\"\n\nmy $data = '';\n\nfor my $i (0 .. 156) {\n\t# skip to offset 32 if 1st bit set in ipv6 address wins\n\t$data .= pack_node($i + 1) . pack_node(32), next if $i == 2;\n\t# otherwise default to RU\n\t$data .= pack_node(0xffffb9) . pack_node(0xffff00), next if $i == 31;\n\t# continue checking bits set in ipv6 address\n\t$data .= pack_node(0xffff00) . pack_node($i + 1), next\n\t\tif grep $_ == $i, (44, 49, 50, 52, 53, 55, 56, 57);\n\t# last bit set in ipv6 address\n\t$data .= pack_node(0xffffe1) . pack_node(0xffff00), next if $i == 156;\n\t$data .= pack_node($i + 1) . pack_node(0xffff00);\n}\n\n$data .= chr(0x00) x 3;\n$data .= chr(0xFF) x 3;\n$data .= chr(12);\n\n$t->write_file('country.dat', $data);\n\n# city database:\n#\n# \"167772161\",\"167772161\",\"RU\",\"48\",\"Moscow\",\"119034\",\"55.7543\",37.6202\",,\n\n$data = '';\n\nfor my $i (0 .. 31) {\n\t$data .= pack_node(32) . pack_node($i + 1), next if $i == 4 or $i == 6;\n\t$data .= pack_node(32) . pack_node($i + 2), next if $i == 31;\n\t$data .= pack_node($i + 1) . pack_node(32);\n}\n\n$data .= chr(42);\n$data .= chr(185);\n$data .= pack('Z*', 48);\n$data .= pack('Z*', 'Moscow');\n$data .= pack('Z*', 119034);\n$data .= pack_node(int((55.7543 + 180) * 10000));\n$data .= pack_node(int((37.6202 + 180) * 10000));\n$data .= chr(0) x 3;\n$data .= chr(0xFF) x 3;\n$data .= chr(2);\n$data .= pack_node(32);\n\n$t->write_file('city.dat', $data);\n\n# organization database:\n#\n# \"167772161\",\"167772161\",\"Nginx\"\n\n$data = '';\n\nfor my $i (0 .. 31) {\n\t$data .= pack_org(32) . pack_org($i + 1), next if $i == 4 or $i == 6;\n\t$data .= pack_org(32) . pack_org($i + 2), next if $i == 31;\n\t$data .= pack_org($i + 1) . pack_org(32);\n}\n\n$data .= chr(42);\n$data .= pack('Z*', 'Nginx');\n$data .= chr(0xFF) x 3;\n$data .= chr(5);\n$data .= pack_node(32);\n\n$t->write_file('org.dat', $data);\n$t->write_file('index.html', '');\n$t->try_run('no inet6 support')->plan(20);\n\n###############################################################################\n\nmy $r = http_xff('10.0.0.1');\nlike($r, qr/X-Country-Code: RU/, 'geoip country code');\nlike($r, qr/X-Country-Code3: RUS/, 'geoip country code 3');\nlike($r, qr/X-Country-Name: Russian Federation/, 'geoip country name');\n\nlike($r, qr/X-Area-Code: 0/, 'geoip area code');\nlike($r, qr/X-C-Continent-Code: EU/, 'geoip city continent code');\nlike($r, qr/X-C-Country-Code: RU/, 'geoip city country code');\nlike($r, qr/X-C-Country-Code3: RUS/, 'geoip city country code 3');\nlike($r, qr/X-C-Country-Name: Russian Federation/, 'geoip city country name');\nlike($r, qr/X-Dma-Code: 0/, 'geoip dma code');\nlike($r, qr/X-Latitude: 55.7543/, 'geoip latitude');\nlike($r, qr/X-Longitude: 37.6202/, 'geoip longitude');\nlike($r, qr/X-Region: 48/, 'geoip region');\nlike($r, qr/X-Region-Name: Moscow City/, 'geoip region name');\nlike($r, qr/X-City: Moscow/, 'geoip city');\nlike($r, qr/X-Postal-Code: 119034/, 'geoip postal code');\n\nlike($r, qr/X-Org: Nginx/, 'geoip org');\n\nlike(http_xff('::ffff:10.0.0.1'), qr/X-Org: Nginx/, 'geoip ipv6 ipv4-mapped');\n\n$r = http_xff('2001:db8::');\nlike($r, qr/X-Country-Code: US/, 'geoip ipv6 country code');\nlike($r, qr/X-Country-Code3: USA/, 'geoip ipv6 country code 3');\nlike($r, qr/X-Country-Name: United States/, 'geoip ipv6 country name');\n\n###############################################################################\n\nsub http_xff {\n\tmy ($xff) = @_;\n\treturn http(<<EOF);\nGET / HTTP/1.0\nHost: localhost\nX-Forwarded-For: $xff\n\nEOF\n}\n\nsub pack_node {\n\tsubstr pack('V', shift), 0, 3;\n}\n\nsub pack_org {\n\tpack('V', shift);\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/grpc.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for grpc backend.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse Test::Nginx::HTTP2;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http rewrite http_v2 grpc/)\n\t->has(qw/upstream_keepalive/)->plan(146);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    upstream u {\n        server 127.0.0.1:8081;\n        keepalive 1;\n    }\n\n    server {\n        listen       127.0.0.1:8080 http2;\n        server_name  localhost;\n\n        http2_body_preread_size 128k;\n        large_client_header_buffers 4 32k;\n\n        location / {\n            grpc_pass grpc://127.0.0.1:8081;\n\n            if ($arg_if) {\n                # nothing\n            }\n\n            limit_except GET {\n                # nothing\n            }\n        }\n\n        location /KeepAlive {\n            grpc_pass u;\n        }\n\n        location /LongHeader {\n            grpc_pass 127.0.0.1:8081;\n            grpc_set_header X-LongHeader $arg_h;\n        }\n\n        location /LongField {\n            grpc_pass 127.0.0.1:8081;\n            grpc_buffer_size 65k;\n        }\n\n        location /SetHost {\n            grpc_pass 127.0.0.1:8081;\n            grpc_set_header Host custom;\n        }\n\n        location /SetArgs {\n            grpc_pass 127.0.0.1:8081;\n            set $args $arg_c;\n        }\n    }\n}\n\nEOF\n\n# suppress deprecation warning\nopen OLDERR, \">&\", \\*STDERR; close STDERR;\n$t->run();\nopen STDERR, \">&\", \\*OLDERR;\n\n###############################################################################\n\nmy $p = port(8081);\nmy $f = grpc();\n\nmy $frames = $f->{http_start}('/SayHello');\nmy ($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{flags}, 4, 'request - HEADERS flags');\nok((my $sid = $frame->{sid}) % 2, 'request - HEADERS sid odd');\nis($frame->{headers}{':method'}, 'POST', 'request - method');\nis($frame->{headers}{':scheme'}, 'http', 'request - scheme');\nis($frame->{headers}{':path'}, '/SayHello', 'request - path');\nis($frame->{headers}{':authority'}, \"127.0.0.1:$p\", 'request - authority');\nis($frame->{headers}{'content-type'}, 'application/grpc',\n\t'request - content type');\nis($frame->{headers}{te}, 'trailers', 'request - te');\n\n$frames = $f->{data}('Hello');\n($frame) = grep { $_->{type} eq \"SETTINGS\" } @$frames;\nis($frame->{flags}, 1, 'request - SETTINGS ack');\nis($frame->{sid}, 0, 'request - SETTINGS sid');\nis($frame->{length}, 0, 'request - SETTINGS length');\n\n($frame) = grep { $_->{type} eq \"DATA\" } @$frames;\nis($frame->{data}, 'Hello', 'request - DATA');\nis($frame->{length}, 5, 'request - DATA length');\nis($frame->{flags}, 1, 'request - DATA flags');\nis($frame->{sid}, $sid, 'request - DATA sid match');\n\n$frames = $f->{http_end}();\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{flags}, 4, 'response - HEADERS flags');\nis($frame->{sid}, 1, 'response - HEADERS sid');\nis($frame->{headers}{':status'}, '200', 'response - status');\nis($frame->{headers}{'content-type'}, 'application/grpc',\n\t'response - content type');\nok($frame->{headers}{server}, 'response - server');\nok($frame->{headers}{date}, 'response - date');\nok(my $c = $frame->{headers}{'x-connection'}, 'response - connection');\n\n($frame) = grep { $_->{type} eq \"DATA\" } @$frames;\nis($frame->{data}, 'Hello world', 'response - DATA');\nis($frame->{length}, 11, 'response - DATA length');\nis($frame->{flags}, 0, 'response - DATA flags');\nis($frame->{sid}, 1, 'response - DATA sid');\n\n(undef, $frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{flags}, 5, 'response - trailers flags');\nis($frame->{sid}, 1, 'response - trailers sid');\nis($frame->{headers}{'grpc-message'}, '', 'response - trailers message');\nis($frame->{headers}{'grpc-status'}, '0', 'response - trailers status');\n\n# next request is on a new backend connection, no sid incremented\n\n$frames = $f->{http_start}('/SayHello');\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{sid}, $sid, 'request 2 - HEADERS sid again');\n$f->{data}('Hello');\n$frames = $f->{http_end}();\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\ncmp_ok($frame->{headers}{'x-connection'}, '>', $c, 'response 2 - connection');\n\n# request body - special last buffer\n\n$f->{http_start}('/SayHello');\n$frames = $f->{data}('Hello', body_more => 1);\n($frame) = grep { $_->{type} eq \"DATA\" } @$frames;\nis($frame->{data}, 'Hello', 'request body first - DATA');\nis($frame->{length}, 5, 'request body first - DATA length');\nis($frame->{flags}, 0, 'request body first - DATA flags');\n$frames = $f->{data}('');\n($frame) = grep { $_->{type} eq \"DATA\" } @$frames;\nis($frame->{data}, '', 'special buffer last - DATA');\nis($frame->{length}, 0, 'special buffer last - DATA length');\nis($frame->{flags}, 1, 'special buffer last - DATA flags');\n$frames = $f->{http_end}();\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}{':status'}, '200', 'special buffer last - response');\n\n# upstream keepalive\n\n$frames = $f->{http_start}('/KeepAlive');\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{sid}, $sid, 'keepalive - HEADERS sid');\n$f->{data}('Hello');\n$frames = $f->{http_end}();\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nok($c = $frame->{headers}{'x-connection'}, 'keepalive - connection');\n\n$frames = $f->{http_start}('/KeepAlive', reuse => 1);\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\ncmp_ok($frame->{sid}, '>', $sid, 'keepalive - HEADERS sid next');\n$f->{data}('Hello');\n$frames = $f->{http_end}();\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}{'x-connection'}, $c, 'keepalive - connection reuse');\n\n# upstream keepalive\n# pending control frame ack after the response\n\nundef $f;\n$f = grpc();\n\n$frames = $f->{http_start}('/KeepAlive');\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{sid}, $sid, 'keepalive 2 - HEADERS sid');\n$f->{data}('Hello');\n$f->{settings}(0, 1 => 4096);\n$frames = $f->{http_end}();\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nok($c = $frame->{headers}{'x-connection'}, 'keepalive 2 - connection');\n\n$frames = $f->{http_start}('/KeepAlive', reuse => 1);\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nok($frame, 'upstream keepalive reused');\n\ncmp_ok($frame->{sid}, '>', $sid, 'keepalive 2 - HEADERS sid next');\n$f->{data}('Hello');\n$frames = $f->{http_end}();\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}{'x-connection'}, $c, 'keepalive 2 - connection reuse');\n\nundef $f;\n$f = grpc();\n\n# upstream keepalive\n# grpc filter setting INITIAL_WINDOW_SIZE is inherited in the next stream\n\n$f->{http_start}('/KeepAlive');\n$f->{data}('Hello');\n$f->{settings}(0, 1 => 4096);\n$frames = $f->{http_end}(grpc_filter_settings => { 0x4 => 2 });\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nok($c = $frame->{headers}{'x-connection'}, 'keepalive 3 - connection');\n\n$f->{http_start}('/KeepAlive', reuse => 1);\n$frames = $f->{data_len}('Hello', 2);\n($frame) = grep { $_->{type} eq \"DATA\" } @$frames;\nis($frame->{data}, 'He', 'grpc filter setting - DATA');\nis($frame->{length}, 2, 'grpc filter setting - DATA length');\nis($frame->{flags}, 0, 'grpc filter setting - DATA flags');\n$f->{settings}(0, 0x4 => 5);\n$frames = $f->{data_len}(undef, 3);\n($frame) = grep { $_->{type} eq \"DATA\" } @$frames;\nis($frame->{data}, 'llo', 'setting updated - DATA');\nis($frame->{length}, 3, 'setting updated - DATA length');\nis($frame->{flags}, 1, 'setting updated - DATA flags');\n$frames = $f->{http_end}();\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}{'x-connection'}, $c, 'keepalive 3 - connection reuse');\n\nundef $f;\n$f = grpc();\n\n# upstream keepalive - GOAWAY, current request aborted\n\n$f->{http_start}('/KeepAlive');\n$f->{data}('Hello');\n$frames = $f->{http_end}();\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nok($c = $frame->{headers}{'x-connection'}, 'keepalive 4 - connection');\n\n$f->{http_start}('/KeepAlive', reuse => 1);\n$f->{goaway}(0, 0, 5);\n$f->{data}('Hello');\n$frames = $f->{http_end}();\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}{':status'}, 502, 'keepalive 4 - GOAWAY aborted request');\n\n$f->{http_start}('/KeepAlive');\n$f->{data}('Hello');\n$frames = $f->{http_end}();\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\ncmp_ok($frame->{headers}{'x-connection'}, '>', $c, 'keepalive 4 - closed');\n\nundef $f;\n$f = grpc();\n\n# upstream keepalive - disabled with a higher GOAWAY Last-Stream-ID\n\n$f->{http_start}('/KeepAlive');\n$f->{goaway}(0, 3, 5);\n$f->{data}('Hello');\n$frames = $f->{http_end}();\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nok($c = $frame->{headers}{'x-connection'}, 'keepalive 5 - GOAWAY next stream');\n\n$f->{http_start}('/KeepAlive');\n$f->{data}('Hello');\n$frames = $f->{http_end}();\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\ncmp_ok($frame->{headers}{'x-connection'}, '>', $c, 'keepalive 5 - closed');\n\nundef $f;\n$f = grpc();\n\n# upstream keepalive - GOAWAY in grpc filter, current stream aborted\n\n$f->{http_start}('/KeepAlive');\n$f->{data}('Hello');\n$frames = $f->{http_end}(grpc_filter_goaway => [0, 0, 5]);\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nok($c = $frame->{headers}{'x-connection'}, 'keepalive 6 - connection');\n($frame) = grep { $_->{type} eq \"RST_STREAM\" } @$frames;\nok($frame, 'keepalive 6 - grpc filter GOAWAY aborted stream');\n\n$f->{http_start}('/KeepAlive');\n$f->{data}('Hello');\n$frames = $f->{http_end}();\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\ncmp_ok($frame->{headers}{'x-connection'}, '>', $c, 'keepalive 6 - closed');\n\nundef $f;\n$f = grpc();\n\n# various header compression formats\n\n$f->{http_start}('/SayHello');\n$f->{data}('Hello');\n$frames = $f->{http_end}(mode => 3);\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}{':status'}, '200', 'without indexing');\nis($frame->{headers}{'content-type'}, 'application/grpc',\n\t'without indexing 2');\n\n$f->{http_start}('/SayHello');\n$f->{data}('Hello');\n$frames = $f->{http_end}(mode => 4);\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}{':status'}, '200', 'without indexing new');\nis($frame->{headers}{'content-type'}, 'application/grpc',\n\t'without indexing new 2');\n\n$f->{http_start}('/SayHello');\n$f->{data}('Hello');\n$frames = $f->{http_end}(mode => 5);\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}{':status'}, '200', 'never indexed');\nis($frame->{headers}{'content-type'}, 'application/grpc',\n\t'never indexed 2');\n\n$f->{http_start}('/SayHello');\n$f->{data}('Hello');\n$frames = $f->{http_end}(mode => 6);\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}{':status'}, '200', 'never indexed new');\nis($frame->{headers}{'content-type'}, 'application/grpc',\n\t'never indexed new 2');\n\n# padding & priority\n\n$f->{http_start}('/SayHello');\n$f->{data}('Hello');\n$frames = $f->{http_end}(padding => 7);\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}{':status'}, '200', 'padding');\n\n$f->{http_start}('/SayHello');\n$f->{data}('Hello');\n$frames = $f->{http_end}(prio => 137, dep => 0x01020304);\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}{':status'}, '200', 'priority');\n\n$f->{http_start}('/SayHello');\n$f->{data}('Hello');\n$frames = $f->{http_end}(padding => 7, prio => 137, dep => 0x01020304);\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}{':status'}, '200', 'padding priority');\n\nSKIP: {\nskip 'long test', 1 unless $ENV{TEST_NGINX_UNSAFE};\n\n$f->{http_start}('/SaySplit');\n$f->{data}('Hello');\n$frames = $f->{http_end}(padding => 7, prio => 137, dep => 0x01020304,\n\tsplit => [(map{1}(1..20)), 30], split_delay => 0.1);\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}{':status'}, '200', 'padding priority split');\n\n}\n\n# grpc error, no empty data frame expected\n\n$f->{http_start}('/SayHello');\n$f->{data}('Hello');\n$frames = $f->{http_err}();\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{flags}, 5, 'grpc error - HEADERS flags');\n($frame) = grep { $_->{type} eq \"DATA\" } @$frames;\nok(!$frame, 'grpc error - no DATA frame');\n\n# malformed response body length not equal to content-length\n\n$f->{http_start}('/SayHello');\n$f->{data}('Hello');\n$frames = $f->{http_err2}(cl => 42);\n($frame) = grep { $_->{type} eq \"RST_STREAM\" } @$frames;\nok($frame, 'response body less than content-length');\n\n$f->{http_start}('/SayHello');\n$f->{data}('Hello');\n$frames = $f->{http_err2}(cl => 8);\n($frame) = grep { $_->{type} eq \"RST_STREAM\" } @$frames;\nok($frame, 'response body more than content-length');\n\n# continuation from backend, expect parts assembled\n\n$f->{http_start}('/SayHello');\n$f->{data}('Hello');\n$frames = $f->{continuation}();\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{flags}, 4, 'continuation - HEADERS flags');\nis($frame->{headers}{':status'}, '200', 'continuation - status');\nis($frame->{headers}{'content-type'}, 'application/grpc',\n\t'continuation - content type');\n\n($frame) = grep { $_->{type} eq \"DATA\" } @$frames;\nis($frame->{data}, 'Hello world', 'continuation - DATA');\nis($frame->{length}, 11, 'continuation - DATA length');\nis($frame->{flags}, 0, 'continuation - DATA flags');\n\n(undef, $frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{flags}, 5, 'continuation - trailers flags');\nis($frame->{headers}{'grpc-message'}, '', 'continuation - trailers message');\nis($frame->{headers}{'grpc-status'}, '0', 'continuation - trailers status');\n\n# continuation from backend, header split\n\n$f->{http_start}('/SayHello');\n$f->{data}('Hello');\n$frames = $f->{http_end}(mode => 6, continuation => [map { 1 } (1 .. 42)]);\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}{':status'}, '200', 'continuation - header split');\n\n# continuation to backend\n\n$frames = $f->{http_start}('/LongHeader?h=' . ('Z' x 31337));\n@$frames = grep { $_->{type} =~ \"HEADERS|CONTINUATION\" } @$frames;\nis(@$frames, 4, 'continuation - frames');\n\n$frame = shift @$frames;\nis($frame->{type}, 'HEADERS', 'continuation - HEADERS');\nis($frame->{length}, 16384, 'continuation - HEADERS length');\nis($frame->{flags}, 1, 'continuation - HEADERS flags');\nok($frame->{sid}, 'continuation - HEADERS sid');\n\n$frame = shift @$frames;\nis($frame->{type}, 'CONTINUATION', 'continuation - CONTINUATION');\nis($frame->{length}, 16384, 'continuation - CONTINUATION length');\nis($frame->{flags}, 0, 'continuation - CONTINUATION flags');\nok($frame->{sid}, 'continuation - CONTINUATION sid');\n\n$frame = shift @$frames;\nis($frame->{type}, 'CONTINUATION', 'continuation - CONTINUATION 2');\nis($frame->{length}, 16384, 'continuation - CONTINUATION 2 length');\nis($frame->{flags}, 0, 'continuation - CONTINUATION 2 flags');\n\n$frame = shift @$frames;\nis($frame->{type}, 'CONTINUATION', 'continuation - CONTINUATION n');\ncmp_ok($frame->{length}, '<', 16384, 'continuation - CONTINUATION n length');\nis($frame->{flags}, 4, 'continuation - CONTINUATION n flags');\nis($frame->{headers}{':path'}, '/LongHeader?h=' . 'Z' x 31337,\n\t'continuation - path');\nis($frame->{headers}{'x-longheader'}, 'Z' x 31337, 'continuation - header');\n\n$f->{http_end}();\n\n# long header field\n\n$f->{http_start}('/LongField');\n$f->{data}('Hello');\n$frames = $f->{field_len}(2**7);\n($frame) = grep { $_->{flags} & 0x4 } @$frames;\nis($frame->{headers}{'x' x 2**7}, 'y' x 2**7, 'long header field 1');\n\n$f->{http_start}('/LongField');\n$f->{data}('Hello');\n$frames = $f->{field_len}(2**8);\n($frame) = grep { $_->{flags} & 0x4 } @$frames;\nis($frame->{headers}{'x' x 2**8}, 'y' x 2**8, 'long header field 2');\n\n$f->{http_start}('/LongField');\n$f->{data}('Hello');\n$frames = $f->{field_len}(2**15);\n($frame) = grep { $_->{flags} & 0x4 } @$frames;\nis($frame->{headers}{'x' x 2**15}, 'y' x 2**15, 'long header field 3');\n\n# Intermediary Encapsulation Attacks, malformed header fields\n\n$f->{http_start}('/');\n$f->{data}('Hello');\n$frames = $f->{field_bad}(n => 'n:n');\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}{':status'}, 502, 'invalid header name colon');\n\n$f->{http_start}('/');\n$f->{data}('Hello');\n$frames = $f->{field_bad}(n => 'NN');\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}{':status'}, 502, 'invalid header name uppercase');\n\n$f->{http_start}('/');\n$f->{data}('Hello');\n$frames = $f->{field_bad}(n => \"n\\nn\");\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}{':status'}, 502, 'invalid header name ctl');\n\nTODO: {\nlocal $TODO = 'not yet' unless $t->has_version('1.21.1');\n\n$f->{http_start}('/');\n$f->{data}('Hello');\n$frames = $f->{field_bad}(n => \"n n\");\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}{':status'}, 502, 'invalid header name space');\n\n}\n\n$f->{http_start}('/');\n$f->{data}('Hello');\n$frames = $f->{field_bad}(v => \"v\\nv\");\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}{':status'}, 502, 'invalid header value ctl');\n\n# invalid HPACK index\n\n$f->{http_start}('/');\n$f->{data}('Hello');\n$frames = $f->{field_bad}('m' => 0);\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}{':status'}, 502, 'invalid index - indexed header');\n\n$f->{http_start}('/');\n$f->{data}('Hello');\n$frames = $f->{field_bad}('m' => 1);\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}{':status'}, 502, 'invalid index - with indexing');\n\n$f->{http_start}('/');\n$f->{data}('Hello');\n$frames = $f->{field_bad}('m' => 3);\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}{':status'}, 502, 'invalid index - without indexing');\n\n# flow control\n\n$f->{http_start}('/FlowControl');\n$frames = $f->{data_len}(('Hello' x 13000) . ('x' x 550), 65535);\nmy $sum = eval join '+', map { $_->{type} eq \"DATA\" && $_->{length} } @$frames;\nis($sum, 65535, 'flow control - iws length');\n\n$f->{update}(10);\n$f->{update_sid}(10);\n\n$frames = $f->{data_len}(undef, 10);\n($frame) = grep { $_->{type} eq \"DATA\" } @$frames;\nis($frame->{length}, 10, 'flow control - update length');\nis($frame->{flags}, 0, 'flow control - update flags');\n\n$f->{update_sid}(10);\n$f->{update}(10);\n\n$frames = $f->{data_len}(undef, 5);\n($frame) = grep { $_->{type} eq \"DATA\" } @$frames;\nis($frame->{length}, 5, 'flow control - rest length');\nis($frame->{flags}, 1, 'flow control - rest flags');\n\n$f->{http_end}();\n\n# preserve output\n\n$f->{http_start}('/Preserve');\n$f->{data}('Hello');\n$frames = $f->{http_pres}();\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{flags}, 4, 'preserve - HEADERS');\n\nmy @data = grep { $_->{type} eq \"DATA\" } @$frames;\n$sum = eval join '+', map { $_->{length} } @data;\nis($sum, 20480, 'preserve - DATA');\n\n(undef, $frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{flags}, 5, 'preserve - trailers');\n\n# DATA padding\n\n$f->{http_start}('/SayPadding');\n$f->{data}('Hello');\n$frames = $f->{http_end}(body_padding => 42);\n($frame) = grep { $_->{type} eq \"DATA\" } @$frames;\nis($frame->{data}, 'Hello world', 'DATA padding');\nis($frame->{length}, 11, 'DATA padding - length');\nis($frame->{flags}, 0, 'DATA padding - flags');\n\n# DATA padding with Content-Length\n\n$f->{http_start}('/SayPadding');\n$f->{data}('Hello');\n$frames = $f->{http_end}(body_padding => 42, cl => length('Hello world'));\n($frame) = grep { $_->{type} eq \"DATA\" } @$frames;\nis($frame->{data}, 'Hello world', 'DATA padding cl');\nis($frame->{length}, 11, 'DATA padding cl - length');\nis($frame->{flags}, 0, 'DATA padding cl - flags');\n\n# :authority inheritance\n\n$frames = $f->{http_start}('/SayHello?if=1');\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}{':authority'}, \"127.0.0.1:$p\", 'authority in if');\n$f->{data}('Hello');\n$f->{http_end}();\n\n# misc tests\n\n$frames = $f->{http_start}('/SetHost');\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nok(!$frame->{headers}{':authority'}, 'set host - authority');\nis($frame->{headers}{'host'}, 'custom', 'set host - host');\n$f->{data}('Hello');\n$f->{http_end}();\n\n$frames = $f->{http_start}('/SetArgs?f');\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}{':path'}, '/SetArgs', 'set args');\n$f->{data}('Hello');\n$f->{http_end}();\n\n$frames = $f->{http_start}('/SetArgs?c=1');\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}{':path'}, '/SetArgs?1', 'set args len');\n$f->{data}('Hello');\n$f->{http_end}();\n\n$frames = $f->{http_start}('/');\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}{':path'}, '/', 'root index');\n$f->{data}('Hello');\n$f->{http_end}();\n\n$frames = $f->{http_start}('/', method => 'GET');\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}{':method'}, 'GET', 'method get');\n$f->{data}('Hello');\n$f->{http_end}();\n\n$frames = $f->{http_start}('/', method => 'HEAD');\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}{':method'}, 'HEAD', 'method head');\n$f->{data}('Hello');\n$f->{http_end}();\n\n# receiving END_STREAM followed by WINDOW_UPDATE on incomplete request body\n\n$f->{http_start}('/Discard_WU');\n$frames = $f->{discard}();\n(undef, $frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{flags}, 5, 'discard WINDOW_UPDATE - trailers');\n\n# receiving END_STREAM followed by RST_STREAM NO_ERROR\n\n$f->{http_start}('/Discard_NE');\n$frames = $f->{discard}();\n(undef, $frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{flags}, 5, 'discard NO_ERROR - trailers');\n\n# receiving END_STREAM followed by several RST_STREAM NO_ERROR\n\n$f->{http_start}('/Discard_NE3');\n$frames = $f->{discard}();\n(undef, $frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{flags}, undef, 'discard NO_ERROR many - no trailers');\n\n# receiving END_STREAM followed by RST_STREAM CANCEL\n\n$f->{http_start}('/Discard_CNL');\n$frames = $f->{discard}();\n(undef, $frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{flags}, undef, 'discard CANCEL - no trailers');\n\nundef $f;\n$f = grpc();\n\n# upstream keepalive, grpc error\n# receiving END_STREAM followed by RST_STREAM NO_ERROR\n\n$f->{http_start}('/KeepAlive');\n$f->{data}('Hello');\n$frames = $f->{http_err_rst}();\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nok($frame->{headers}{'grpc-status'}, 'keepalive 3 - grpc error, rst');\n\n$frames = $f->{http_start}('/KeepAlive', reuse => 1);\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nok($frame, 'keepalive 3 - connection reused');\n\nundef $f;\n$f = grpc();\n\n###############################################################################\n\nsub grpc {\n\tmy ($server, $client, $f, $s, $c, $sid, $csid, $uri);\n\tmy $n = 0;\n\n\t$server = IO::Socket::INET->new(\n\t\tProto => 'tcp',\n\t\tLocalHost => '127.0.0.1',\n\t\tLocalPort => $p,\n\t\tListen => 5,\n\t\tReuse => 1\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\t$f->{http_start} = sub {\n\t\t($uri, my %extra) = @_;\n\t\tmy $body_more = 1 if $uri !~ /LongHeader/;\n\t\tmy $meth = $extra{method} || 'POST';\n\t\t$s = Test::Nginx::HTTP2->new() if !defined $s;\n\t\t$csid = $s->new_stream({ body_more => $body_more, headers => [\n\t\t\t{ name => ':method', value => $meth, mode => !!$meth },\n\t\t\t{ name => ':scheme', value => 'http', mode => 0 },\n\t\t\t{ name => ':path', value => $uri, },\n\t\t\t{ name => ':authority', value => 'localhost' },\n\t\t\t{ name => 'content-type', value => 'application/grpc' },\n\t\t\t{ name => 'te', value => 'trailers', mode => 2 }]});\n\n\t\tif (!$extra{reuse}) {\n\t\t\tif (IO::Select->new($server)->can_read(5)) {\n\t\t\t\t$client = $server->accept();\n\n\t\t\t} else {\n\t\t\t\tlog_in(\"timeout\");\n\t\t\t\t# connection could be unexpectedly reused\n\t\t\t\tgoto reused if $client;\n\t\t\t\treturn undef;\n\t\t\t}\n\n\t\t\tlog2c(\"(new connection $client)\");\n\t\t\t$n++;\n\n\t\t\t$client->sysread(my $buf, 24) == 24 or return; # preface\n\n\t\t\t$c = Test::Nginx::HTTP2->new(1, socket => $client,\n\t\t\t\tpure => 1, preface => \"\") or return;\n\t\t}\n\nreused:\n\t\tmy $frames = $c->read(all => [{ fin => 4 }]);\n\n\t\tif (!$extra{reuse}) {\n\t\t\t$c->h2_settings(0);\n\t\t\t$c->h2_settings(1);\n\t\t}\n\n\t\tmy ($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\n\t\t$sid = $frame->{sid};\n\t\treturn $frames;\n\t};\n\t$f->{data} = sub {\n\t\tmy ($body, %extra) = @_;\n\t\t$s->h2_body($body, { %extra });\n\t\treturn $c->read(all => [{ sid => $sid,\n\t\t\tlength => length($body) }]);\n\t};\n\t$f->{data_len} = sub {\n\t\tmy ($body, $len) = @_;\n\t\t$s->h2_body($body) if defined $body;\n\t\treturn $c->read(all => [{ sid => $sid, length => $len }]);\n\t};\n\t$f->{update} = sub {\n\t\t$c->h2_window(shift);\n\t};\n\t$f->{update_sid} = sub {\n\t\t$c->h2_window(shift, $sid);\n\t};\n\t$f->{settings} = sub {\n\t\t$c->h2_settings(@_);\n\t};\n\t$f->{goaway} = sub {\n\t\t$c->h2_goaway(@_);\n\t};\n\t$f->{http_end} = sub {\n\t\tmy (%extra) = @_;\n\t\tmy $h = [\n\t\t\t{ name => ':status', value => '200',\n\t\t\t\tmode => $extra{mode} || 0 },\n\t\t\t{ name => 'content-type', value => 'application/grpc',\n\t\t\t\tmode => $extra{mode} || 1, huff => 1 },\n\t\t\t{ name => 'x-connection', value => $n,\n\t\t\t\tmode => 2, huff => 1 }];\n\t\tpush @$h, { name => 'content-length', value => $extra{cl} }\n\t\t\tif $extra{cl};\n\t\t$c->new_stream({ body_more => 1, headers => $h, %extra }, $sid);\n\t\t$c->h2_body('Hello world', { body_more => 1,\n\t\t\tbody_padding => $extra{body_padding} });\n\t\t$c->h2_settings(0, %{$extra{grpc_filter_settings}})\n\t\t\tif $extra{grpc_filter_settings};\n\t\t$c->h2_goaway(@{$extra{grpc_filter_goaway}})\n\t\t\tif $extra{grpc_filter_goaway};\n\t\t$c->new_stream({ headers => [\n\t\t\t{ name => 'grpc-status', value => '0',\n\t\t\t\tmode => 2, huff => 1 },\n\t\t\t{ name => 'grpc-message', value => '',\n\t\t\t\tmode => 2, huff => 1 },\n\t\t]}, $sid);\n\n\t\treturn $s->read(all => [{ type => 'RST_STREAM' }])\n\t\t\tif $extra{grpc_filter_goaway};\n\t\treturn $s->read(all => [{ fin => 1 }]);\n\t};\n\t$f->{http_pres} = sub {\n\t\tmy (%extra) = @_;\n\t\t$s->h2_settings(0, 0x4 => 8192);\n\t\t$c->new_stream({ body_more => 1, %extra, headers => [\n\t\t\t{ name => ':status', value => '200',\n\t\t\t\tmode => $extra{mode} || 0 },\n\t\t\t{ name => 'content-type', value => 'application/grpc',\n\t\t\t\tmode => $extra{mode} || 1, huff => 1 },\n\t\t\t{ name => 'x-connection', value => $n,\n\t\t\t\tmode => 2, huff => 1 },\n\t\t]}, $sid);\n\t\tfor (1 .. 20) {\n\t\t\t$c->h2_body(sprintf('Hello %02d', $_) x 128, {\n\t\t\t\tbody_more => 1,\n\t\t\t\tbody_padding => $extra{body_padding} });\n\t\t\t$c->h2_ping(\"PING\");\n\t\t}\n\t\t# reopen window\n\t\t$s->h2_window(2**24);\n\t\t$s->h2_window(2**24, $csid);\n\t\t$c->new_stream({ headers => [\n\t\t\t{ name => 'grpc-status', value => '0',\n\t\t\t\tmode => 2, huff => 1 },\n\t\t\t{ name => 'grpc-message', value => '',\n\t\t\t\tmode => 2, huff => 1 },\n\t\t]}, $sid);\n\n\t\treturn $s->read(all => [{ sid => $csid, fin => 1 }]);\n\t};\n\t$f->{http_err} = sub {\n\t\t$c->new_stream({ headers => [\n\t\t\t{ name => ':status', value => '200', mode => 0 },\n\t\t\t{ name => 'content-type', value => 'application/grpc',\n\t\t\t\tmode => 1, huff => 1 },\n\t\t\t{ name => 'grpc-status', value => '12',\n\t\t\t\tmode => 2, huff => 1 },\n\t\t\t{ name => 'grpc-message', value => 'unknown service',\n\t\t\t\tmode => 2, huff => 1 },\n\t\t]}, $sid);\n\n\t\treturn $s->read(all => [{ fin => 1 }]);\n\t};\n\t$f->{http_err_rst} = sub {\n\t\t$c->start_chain();\n\t\t$c->new_stream({ headers => [\n\t\t\t{ name => ':status', value => '200', mode => 0 },\n\t\t\t{ name => 'content-type', value => 'application/grpc' },\n\t\t\t{ name => 'grpc-status', value => '12', mode => 2 },\n\t\t\t{ name => 'grpc-message', value => 'unknown service',\n\t\t\t\tmode => 2 },\n\t\t]}, $sid);\n\t\t$c->h2_rst($sid, 0);\n\t\t$c->send_chain();\n\n\t\treturn $s->read(all => [{ fin => 1 }]);\n\t};\n\t$f->{http_err2} = sub {\n\t\tmy %extra = @_;\n\t\t$c->new_stream({ body_more => 1, headers => [\n\t\t\t{ name => ':status', value => '200', mode => 0 },\n\t\t\t{ name => 'content-type', value => 'application/grpc',\n\t\t\t\tmode => 1, huff => 1 },\n\t\t\t{ name => 'content-length', value => $extra{cl},\n\t\t\t\tmode => 1, huff => 1 },\n\t\t]}, $sid);\n\t\t$c->h2_body('Hello world',\n\t\t\t{ body_more => 1, body_split => [5] });\n\t\t$c->new_stream({ headers => [\n\t\t\t{ name => 'grpc-status', value => '0',\n\t\t\t\tmode => 2, huff => 1 },\n\t\t\t{ name => 'grpc-message', value => '',\n\t\t\t\tmode => 2, huff => 1 },\n\t\t]}, $sid);\n\n\t\treturn $s->read(all => [{ type => 'RST_STREAM' }]);\n\t};\n\t$f->{continuation} = sub {\n\t\t$c->new_stream({ continuation => 1, body_more => 1, headers => [\n\t\t\t{ name => ':status', value => '200', mode => 0 },\n\t\t]}, $sid);\n\t\t$c->h2_continue($sid, { continuation => 1, headers => [\n\t\t\t{ name => 'content-type', value => 'application/grpc',\n\t\t\t\tmode => 1, huff => 1 },\n\t\t]});\n\t\t$c->h2_continue($sid, { headers => [\n\t\t\t# an empty CONTINUATION frame is legitimate\n\t\t]});\n\t\t$c->h2_body('Hello world', { body_more => 1 });\n\t\t$c->new_stream({ continuation => 1, headers => [\n\t\t\t{ name => 'grpc-status', value => '0',\n\t\t\t\tmode => 2, huff => 1 },\n\t\t]}, $sid);\n\t\t$c->h2_continue($sid, { headers => [\n\t\t\t{ name => 'grpc-message', value => '',\n\t\t\t\tmode => 2, huff => 1 },\n\t\t]});\n\n\t\treturn $s->read(all => [{ fin => 1 }]);\n\t};\n\t$f->{field_len} = sub {\n\t\tmy ($len) = @_;\n\t\t$c->new_stream({ continuation => [map {2**14} (0..$len/2**13)],\n\t\t\tbody_more => 1, headers => [\n\t\t\t{ name => ':status', value => '200', mode => 0 },\n\t\t\t{ name => 'content-type', value => 'application/grpc',\n\t\t\t\tmode => 1, huff => 1 },\n\t\t\t{ name => 'x' x $len, value => 'y' x $len, mode => 6 },\n\t\t]}, $sid);\n\t\t$c->h2_body('Hello world', { body_more => 1 });\n\t\t$c->new_stream({ headers => [\n\t\t\t{ name => 'grpc-status', value => '0',\n\t\t\t\tmode => 2, huff => 1 },\n\t\t\t{ name => 'grpc-message', value => '',\n\t\t\t\tmode => 2, huff => 1 },\n\t\t]}, $sid);\n\n\t\treturn $s->read(all => [{ fin => 1 }]);\n\t};\n\t$f->{field_bad} = sub {\n\t\tmy (%extra) = @_;\n\t\tmy $n = defined $extra{'n'} ? $extra{'n'} : 'n';\n\t\tmy $v = defined $extra{'v'} ? $extra{'v'} : 'v';\n\t\tmy $m = defined $extra{'m'} ? $extra{'m'} : 2;\n\t\t$c->new_stream({ headers => [\n\t\t\t{ name => ':status', value => '200' },\n\t\t\t{ name => $n, value => $v, mode => $m },\n\t\t]}, $sid);\n\n\t\treturn $s->read(all => [{ fin => 1 }]);\n\t};\n\t$f->{discard} = sub {\n\t\tmy (%extra) = @_;\n\t\t$c->new_stream({ body_more => 1, %extra, headers => [\n\t\t\t{ name => ':status', value => '200',\n\t\t\t\tmode => $extra{mode} || 0 },\n\t\t\t{ name => 'content-type', value => 'application/grpc',\n\t\t\t\tmode => $extra{mode} || 1, huff => 1 },\n\t\t\t{ name => 'x-connection', value => $n,\n\t\t\t\tmode => 2, huff => 1 },\n\t\t]}, $sid);\n\t\t$c->h2_body('Hello world', { body_more => 1,\n\t\t\tbody_padding => $extra{body_padding} });\n\n\t\t# stick trailers and subsequent frames for reproducibility\n\n\t\t$c->start_chain();\n\t\t$c->new_stream({ headers => [\n\t\t\t{ name => 'grpc-status', value => '0', mode => 2 }\n\t\t]}, $sid);\n\t\t$c->h2_window(42, $sid) if $uri eq '/Discard_WU';\n\t\t$c->h2_rst($sid, 0) if $uri eq '/Discard_NE';\n\t\t$c->h2_rst($sid, 0), $c->h2_rst($sid, 0), $c->h2_rst($sid, 0)\n\t\t\tif $uri eq '/Discard_NE3';\n\t\t$c->h2_rst($sid, 8) if $uri eq '/Discard_CNL';\n\t\t$c->send_chain();\n\n\t\treturn $s->read(all => [{ fin => 1 }], wait => 2)\n\t\t\tif $uri eq '/Discard_WU' || $uri eq '/Discard_NE';\n\t\treturn $s->read(all => [{ type => 'RST_STREAM' }]);\n\t};\n\treturn $f;\n}\n\nsub log2i { Test::Nginx::log_core('|| <<', @_); }\nsub log2o { Test::Nginx::log_core('|| >>', @_); }\nsub log2c { Test::Nginx::log_core('||', @_); }\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/grpc_next_upstream.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for grpc module, grpc_next_upstream directive.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http http_v2 grpc rewrite/)->plan(9);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\nworker_processes 1;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    upstream u {\n        server 127.0.0.1:8081 max_fails=2;\n        server 127.0.0.1:8082;\n    }\n\n    upstream u2 {\n        server 127.0.0.1:8081;\n        server 127.0.0.1:8082;\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            grpc_pass u;\n            grpc_next_upstream http_500 http_404 invalid_header;\n        }\n\n        location /all/ {\n            grpc_pass u2;\n            grpc_next_upstream http_500 http_404;\n            error_page 404 /all/404;\n            grpc_intercept_errors on;\n        }\n\n        location /all/404 {\n            return 200 \"$upstream_addr\\n\";\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081 http2;\n        server_name  localhost;\n\n        location / {\n            return 404;\n        }\n        location /ok {\n            return 200 \"AND-THIS\\n\";\n        }\n        location /500 {\n            return 500;\n        }\n        location /444 {\n            return 444;\n        }\n\n        location /all/ {\n            return 404;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8082 http2;\n        server_name  localhost;\n\n        location / {\n            return 200 \"TEST-OK-IF-YOU-SEE-THIS\\n\";\n        }\n\n        location /all/ {\n            return 404;\n        }\n    }\n}\n\nEOF\n\n# suppress deprecation warning\nopen OLDERR, \">&\", \\*STDERR; close STDERR;\n$t->run();\nopen STDERR, \">&\", \\*OLDERR;\n\n###############################################################################\n\nmy ($p1, $p2) = (port(8081), port(8082));\n\n# check if both request fallback to a backend\n# which returns valid response\n\nlike(http_get('/'), qr/SEE-THIS/, 'grpc request');\nlike(http_get('/'), qr/SEE-THIS/, 'second request');\n\n# make sure backend isn't switched off after\n# grpc_next_upstream http_404\n\nlike(http_get('/ok') . http_get('/ok'), qr/AND-THIS/, 'not down');\n\n# next upstream on invalid_header\n\nlike(http_get('/444'), qr/SEE-THIS/, 'request 444');\nlike(http_get('/444'), qr/SEE-THIS/, 'request 444 second');\n\n# next upstream on http_500\n\nlike(http_get('/500'), qr/SEE-THIS/, 'request 500');\nlike(http_get('/500'), qr/SEE-THIS/, 'request 500 second');\n\n# make sure backend switched off with http_500\n\nunlike(http_get('/ok') . http_get('/ok'), qr/AND-THIS/, 'down after 500');\n\n# make sure all backends are tried once\n\nlike(http_get('/all/rr'),\n\tqr/^127.0.0.1:($p1, 127.0.0.1:$p2|$p2, 127.0.0.1:$p1)$/mi,\n\t'all tried once');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/grpc_pass.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for the grpc_pass directive with variables.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http http_ssl http_v2 grpc rewrite/)\n\t->has_daemon('openssl')->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    upstream u {\n        server 127.0.0.1:8081;\n    }\n\n    resolver 127.0.0.1:%%PORT_8982_UDP%%;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            grpc_pass $host:%%PORT_8081%%;\n        }\n\n        location /grpc {\n            grpc_pass grpc://$host:%%PORT_8081%%;\n        }\n\n        location /grpcs {\n            grpc_pass grpcs://$host:%%PORT_8082%%;\n        }\n\n        location /arg {\n            grpc_pass $arg_b;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081 http2;\n        listen       127.0.0.1:8082 http2 ssl;\n        server_name  localhost;\n\n        ssl_certificate_key localhost.key;\n        ssl_certificate localhost.crt;\n\n        location / {\n            return 200 $http_host;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('openssl.conf', <<EOF);\n[ req ]\ndefault_bits = 2048\nencrypt_key = no\ndistinguished_name = req_distinguished_name\n[ req_distinguished_name ]\nEOF\n\nmy $d = $t->testdir();\n\nforeach my $name ('localhost') {\n\tsystem('openssl req -x509 -new '\n\t\t. \"-config $d/openssl.conf -subj /CN=$name/ \"\n\t\t. \"-out $d/$name.crt -keyout $d/$name.key \"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create certificate for $name: $!\\n\";\n}\n\n$t->run_daemon(\\&dns_daemon, port(8982), $t);\n# suppress deprecation warning\nopen OLDERR, \">&\", \\*STDERR; close STDERR;\n$t->run()->plan(5);\nopen STDERR, \">&\", \\*OLDERR;\n\n$t->waitforfile($t->testdir . '/' . port(8982));\n\n###############################################################################\n\nlike(http_get('/basic'), qr/200 OK/, 'no scheme');\nlike(http_get('/grpc'), qr/200 OK/, 'grpc scheme');\n\nSKIP: {\nskip 'OpenSSL too old', 1 unless $t->has_feature('openssl:1.0.2');\n\nlike(http_get('/grpcs'), qr/200 OK/, 'grpcs scheme');\n\n}\n\nlike(http_get('/arg?b=grpc://127.0.0.1:' . port(8081)), qr/200 OK/, 'addrs');\nlike(http_get('/arg?b=grpc://u'), qr/200 OK/, 'no_port');\n\n###############################################################################\n\nsub reply_handler {\n\tmy ($recv_data) = @_;\n\n\tmy (@name, @rdata);\n\n\tuse constant NOERROR\t=> 0;\n\tuse constant A\t\t=> 1;\n\tuse constant IN\t\t=> 1;\n\n\t# default values\n\n\tmy ($hdr, $rcode, $ttl) = (0x8180, NOERROR, 3600);\n\n\t# decode name\n\n\tmy ($len, $offset) = (undef, 12);\n\twhile (1) {\n\t\t$len = unpack(\"\\@$offset C\", $recv_data);\n\t\tlast if $len == 0;\n\t\t$offset++;\n\t\tpush @name, unpack(\"\\@$offset A$len\", $recv_data);\n\t\t$offset += $len;\n\t}\n\n\t$offset -= 1;\n\tmy ($id, $type, $class) = unpack(\"n x$offset n2\", $recv_data);\n\n\tmy $name = join('.', @name);\n\tif ($name eq 'localhost' && $type == A) {\n\t\tpush @rdata, rd_addr($ttl, '127.0.0.1');\n\t}\n\n\t$len = @name;\n\tpack(\"n6 (C/a*)$len x n2\", $id, $hdr | $rcode, 1, scalar @rdata,\n\t\t0, 0, @name, $type, $class) . join('', @rdata);\n}\n\nsub rd_addr {\n\tmy ($ttl, $addr) = @_;\n\n\tmy $code = 'split(/\\./, $addr)';\n\n\treturn pack 'n3N', 0xc00c, A, IN, $ttl if $addr eq '';\n\n\tpack 'n3N nC4', 0xc00c, A, IN, $ttl, eval \"scalar $code\", eval($code);\n}\n\nsub dns_daemon {\n\tmy ($port, $t) = @_;\n\n\tmy ($data, $recv_data);\n\tmy $socket = IO::Socket::INET->new(\n\t\tLocalAddr => '127.0.0.1',\n\t\tLocalPort => $port,\n\t\tProto => 'udp',\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\t# signal we are ready\n\n\topen my $fh, '>', $t->testdir() . '/' . $port;\n\tclose $fh;\n\n\twhile (1) {\n\t\t$socket->recv($recv_data, 65536);\n\t\t$data = reply_handler($recv_data);\n\t\t$socket->send($data);\n\t}\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/grpc_request_buffering.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for grpc module, request body buffered.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse Test::Nginx::HTTP2;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http http_v2 grpc mirror proxy/)->plan(12);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080 http2;\n        listen       127.0.0.1:8082;\n        server_name  localhost;\n\n        location /mirror { }\n\n        location / {\n            grpc_pass 127.0.0.1:8081;\n            add_header X-Body $request_body;\n            mirror /mirror;\n        }\n\n        location /proxy {\n            proxy_pass http://127.0.0.1:8082/mirror;\n            proxy_intercept_errors on;\n            error_page 404 = @fallback;\n        }\n\n        location @fallback {\n            grpc_pass 127.0.0.1:8081;\n        }\n    }\n}\n\nEOF\n\n# suppress deprecation warning\nopen OLDERR, \">&\", \\*STDERR; close STDERR;\n$t->run();\nopen STDERR, \">&\", \\*OLDERR;\n\n###############################################################################\n\nmy $p = port(8081);\nmy $f = grpc();\n\nmy $frames = $f->{http_start}('/SayHello');\nmy ($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{flags}, 4, 'request - HEADERS flags');\nis($frame->{headers}{':method'}, 'POST', 'request - method');\nis($frame->{headers}{':scheme'}, 'http', 'request - scheme');\nis($frame->{headers}{':path'}, '/SayHello', 'request - path');\nis($frame->{headers}{':authority'}, \"127.0.0.1:$p\", 'request - authority');\n\n($frame) = grep { $_->{type} eq \"DATA\" } @$frames;\nis($frame->{data}, 'Hello', 'request - DATA');\nis($frame->{length}, 5, 'request - DATA length');\nis($frame->{flags}, 1, 'request - DATA flags');\n\n$frames = $f->{http_end}();\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}{'x-body'}, 'Hello', 'request body in memory');\n\n# tcp_nopush usage on peer connections\n# reopen window for request body after initial window was exhausted\n\n$frames = $f->{http_start}('/proxy');\nis(eval(join '+', map { $_->{length} } grep { $_->{type} eq \"DATA\" } @$frames),\n\t65535, 'preserve_output - first body bytes');\n\n# expect body cleanup is disabled with preserve_output (ticket #1565).\n# after request body first bytes were proxied on behalf of initial window size,\n# send response header from upstream, this leads to body cleanup code path\n\n$frames = $f->{http_end}();\nis(eval(join '+', map { $_->{length} } grep { $_->{type} eq \"DATA\" } @$frames),\n\t465, 'preserve_output - last body bytes');\n\nlike(`grep -F '[crit]' ${\\($t->testdir())}/error.log`, qr/^$/s, 'no crits');\n\n###############################################################################\n\nsub grpc {\n\tmy ($server, $client, $f, $s, $c, $sid, $uri);\n\n\t$server = IO::Socket::INET->new(\n\t\tProto => 'tcp',\n\t\tLocalHost => '127.0.0.1',\n\t\tLocalPort => $p,\n\t\tListen => 5,\n\t\tReuse => 1\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\t$f->{http_start} = sub {\n\t\t($uri, my %extra) = @_;\n\t\t$s = Test::Nginx::HTTP2->new() if !defined $s;\n\t\tmy ($body) = $uri eq '/proxy' ? 'Hello' x 13200 : 'Hello';\n\t\t$s->new_stream({ body => $body, headers => [\n\t\t\t{ name => ':method', value => 'POST', mode => 0 },\n\t\t\t{ name => ':scheme', value => 'http', mode => 0 },\n\t\t\t{ name => ':path', value => $uri },\n\t\t\t{ name => ':authority', value => 'localhost' },\n\t\t\t{ name => 'content-length', value => length($body) }]});\n\n\t\tif (!$extra{reuse}) {\n\t\t\teval {\n\t\t\t\tlocal $SIG{ALRM} = sub { die \"timeout\\n\" };\n\t\t\t\talarm(5);\n\n\t\t\t\t$client = $server->accept() or return;\n\n\t\t\t\talarm(0);\n\t\t\t};\n\t\t\talarm(0);\n\t\t\tif ($@) {\n\t\t\t\tlog_in(\"died: $@\");\n\t\t\t\treturn undef;\n\t\t\t}\n\n\t\t\tlog2c(\"(new connection $client)\");\n\n\t\t\t$client->sysread(my $buf, 24) == 24 or return; # preface\n\n\t\t\t$c = Test::Nginx::HTTP2->new(1, socket => $client,\n\t\t\t\tpure => 1, preface => \"\") or return;\n\t\t}\n\n\t\tmy $frames = $uri eq '/proxy'\n\t\t\t? $c->read(all => [{ length => 65535 }])\n\t\t\t: $c->read(all => [{ fin => 1 }]);\n\n\t\tif (!$extra{reuse}) {\n\t\t\t$c->h2_settings(0);\n\t\t\t$c->h2_settings(1);\n\t\t}\n\n\t\tmy ($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\n\t\t$sid = $frame->{sid};\n\t\treturn $frames;\n\t};\n\t$f->{http_end} = sub {\n\t\t$c->new_stream({ body_more => 1, headers => [\n\t\t\t{ name => ':status', value => '200', mode => 0 },\n\t\t\t{ name => 'content-type', value => 'application/grpc' },\n\t\t]}, $sid);\n\n\t\t# reopen window for request body after response HEADERS is sent\n\n\t\tif ($uri eq '/proxy') {\n\t\t\t$c->h2_window(2**16, $sid);\n\t\t\t$c->h2_window(2**16);\n\t\t\treturn $c->read(all => [{ sid => $sid, fin => 1 }]);\n\t\t}\n\n\t\t$c->h2_body('Hello world', { body_more => 1 });\n\t\t$c->new_stream({ headers => [\n\t\t\t{ name => 'grpc-status', value => '0', mode => 2 },\n\t\t\t{ name => 'grpc-message', value => '', mode => 2 },\n\t\t]}, $sid);\n\n\t\treturn $s->read(all => [{ fin => 1 }]);\n\t};\n\treturn $f;\n}\n\nsub log2c { Test::Nginx::log_core('||', @_); }\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/grpc_ssl.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for grpc backend with ssl.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse Test::Nginx::HTTP2;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http rewrite http_v2 grpc/)\n\t->has(qw/upstream_keepalive http_ssl openssl:1.0.2/)\n\t->has_daemon('openssl')\n\n\t->write_file_expand('nginx.conf', <<'EOF')->plan(38);\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    upstream u {\n        server 127.0.0.1:8081;\n        keepalive 1;\n    }\n\n    server {\n        listen       127.0.0.1:8081 http2 ssl;\n        server_name  localhost;\n\n        ssl_certificate_key localhost.key;\n        ssl_certificate localhost.crt;\n\n        ssl_verify_client optional;\n        ssl_client_certificate client.crt;\n\n        http2_body_preread_size 128k;\n\n        location / {\n            grpc_pass 127.0.0.1:8082;\n            add_header X-Connection $connection;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8080 http2;\n        server_name  localhost;\n\n        http2_body_preread_size 128k;\n\n        location / {\n            grpc_pass grpcs://127.0.0.1:8081;\n            grpc_ssl_name localhost;\n            grpc_ssl_verify on;\n            grpc_ssl_trusted_certificate localhost.crt;\n\n            grpc_ssl_certificate client.crt;\n            grpc_ssl_certificate_key client.key;\n            grpc_ssl_password_file password;\n\n            if ($arg_if) {\n                # nothing\n            }\n\n            limit_except GET {\n                # nothing\n            }\n        }\n\n        location /KeepAlive {\n            grpc_pass grpcs://u;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('openssl.conf', <<EOF);\n[ req ]\ndefault_bits = 2048\nencrypt_key = no\ndistinguished_name = req_distinguished_name\n[ req_distinguished_name ]\nEOF\n\nmy $d = $t->testdir();\n\nforeach my $name ('localhost') {\n\tsystem('openssl req -x509 -new '\n\t\t. \"-config $d/openssl.conf -subj /CN=$name/ \"\n\t\t. \"-out $d/$name.crt -keyout $d/$name.key \"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create certificate for $name: $!\\n\";\n}\n\nforeach my $name ('client') {\n\tsystem(\"openssl genrsa -out $d/$name.key -passout pass:$name \"\n\t\t. \"-aes128 2048 >>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create private key: $!\\n\";\n\tsystem('openssl req -x509 -new '\n\t\t. \"-config $d/openssl.conf -subj /CN=$name/ \"\n\t\t. \"-out $d/$name.crt \"\n\t\t. \"-key $d/$name.key -passin pass:$name\"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create certificate for $name: $!\\n\";\n}\n\nsleep 1 if $^O eq 'MSWin32';\n\n$t->write_file('password', 'client');\n\n# suppress deprecation warning\nopen OLDERR, \">&\", \\*STDERR; close STDERR;\n$t->run();\nopen STDERR, \">&\", \\*OLDERR;\n\n###############################################################################\n\nmy $p = port(8082);\nmy $f = grpc();\n\nmy $frames = $f->{http_start}('/SayHello');\nmy ($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{flags}, 4, 'request - HEADERS flags');\nok((my $sid = $frame->{sid}) % 2, 'request - HEADERS sid odd');\nis($frame->{headers}{':method'}, 'POST', 'request - method');\nis($frame->{headers}{':scheme'}, 'http', 'request - scheme');\nis($frame->{headers}{':path'}, '/SayHello', 'request - path');\nis($frame->{headers}{':authority'}, \"127.0.0.1:$p\", 'request - authority');\nis($frame->{headers}{'content-type'}, 'application/grpc',\n\t'request - content type');\nis($frame->{headers}{te}, 'trailers', 'request - te');\n\n$frames = $f->{data}('Hello');\n($frame) = grep { $_->{type} eq \"SETTINGS\" } @$frames;\nis($frame->{flags}, 1, 'request - SETTINGS ack');\nis($frame->{sid}, 0, 'request - SETTINGS sid');\nis($frame->{length}, 0, 'request - SETTINGS length');\n\n($frame) = grep { $_->{type} eq \"DATA\" } @$frames;\nis($frame->{data}, 'Hello', 'request - DATA');\nis($frame->{length}, 5, 'request - DATA length');\nis($frame->{flags}, 1, 'request - DATA flags');\nis($frame->{sid}, $sid, 'request - DATA sid match');\n\n$frames = $f->{http_end}();\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{flags}, 4, 'response - HEADERS flags');\nis($frame->{sid}, 1, 'response - HEADERS sid');\nis($frame->{headers}{':status'}, '200', 'response - status');\nis($frame->{headers}{'content-type'}, 'application/grpc',\n\t'response - content type');\nok($frame->{headers}{server}, 'response - server');\nok($frame->{headers}{date}, 'response - date');\nok(my $c = $frame->{headers}{'x-connection'}, 'response - connection');\n\n($frame) = grep { $_->{type} eq \"DATA\" } @$frames;\nis($frame->{data}, 'Hello world', 'response - DATA');\nis($frame->{length}, 11, 'response - DATA length');\nis($frame->{flags}, 0, 'response - DATA flags');\nis($frame->{sid}, 1, 'response - DATA sid');\n\n(undef, $frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{flags}, 5, 'response - trailers flags');\nis($frame->{sid}, 1, 'response - trailers sid');\nis($frame->{headers}{'grpc-message'}, '', 'response - trailers message');\nis($frame->{headers}{'grpc-status'}, '0', 'response - trailers status');\n\n# next request is on a new backend connection, no sid incremented\n\n$f->{http_start}('/SayHello');\n$f->{data}('Hello');\n$frames = $f->{http_end}();\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\ncmp_ok($frame->{headers}{'x-connection'}, '>', $c, 'response 2 - connection');\n\n# flow control\n\n$f->{http_start}('/FlowControl');\n$frames = $f->{data_len}(('Hello' x 13000) . ('x' x 550), 65535);\nmy $sum = eval join '+', map { $_->{type} eq \"DATA\" && $_->{length} } @$frames;\nis($sum, 65535, 'flow control - iws length');\n\n$f->{update}(10);\n$f->{update_sid}(10);\n\n$frames = $f->{data_len}(undef, 10);\n($frame) = grep { $_->{type} eq \"DATA\" } @$frames;\nis($frame->{length}, 10, 'flow control - update length');\nis($frame->{flags}, 0, 'flow control - update flags');\n\n$f->{update_sid}(10);\n$f->{update}(10);\n\n$frames = $f->{data_len}(undef, 5);\n($frame) = grep { $_->{type} eq \"DATA\" } @$frames;\nis($frame->{length}, 5, 'flow control - rest length');\nis($frame->{flags}, 1, 'flow control - rest flags');\n\n$f->{http_end}();\n\n# upstream keepalive\n\n$f->{http_start}('/KeepAlive');\n$f->{data}('Hello');\n$frames = $f->{http_end}();\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nok($c = $frame->{headers}{'x-connection'}, 'keepalive - connection');\n\n$f->{http_start}('/KeepAlive');\n$f->{data}('Hello');\n$frames = $f->{http_end}();\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}{'x-connection'}, $c, 'keepalive - connection reuse');\n\n###############################################################################\n\nsub grpc {\n\tmy ($server, $client, $f, $s, $c, $sid, $uri);\n\n\t$server = IO::Socket::INET->new(\n\t\tProto => 'tcp',\n\t\tLocalHost => '127.0.0.1',\n\t\tLocalPort => $p,\n\t\tListen => 5,\n\t\tReuse => 1\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\t$f->{http_start} = sub {\n\t\t($uri, my %extra) = @_;\n\t\tmy $body_more = 1 if $uri !~ /LongHeader/;\n\t\t$s = Test::Nginx::HTTP2->new() if !defined $s;\n\t\t$s->new_stream({ body_more => $body_more, headers => [\n\t\t\t{ name => ':method', value => 'POST', mode => 0 },\n\t\t\t{ name => ':scheme', value => 'http', mode => 0 },\n\t\t\t{ name => ':path', value => $uri, },\n\t\t\t{ name => ':authority', value => 'localhost' },\n\t\t\t{ name => 'content-type', value => 'application/grpc' },\n\t\t\t{ name => 'te', value => 'trailers', mode => 2 }]});\n\n\t\tif (!$extra{reuse}) {\n\t\t\teval {\n\t\t\t\tlocal $SIG{ALRM} = sub { die \"timeout\\n\" };\n\t\t\t\talarm(5);\n\n\t\t\t\t$client = $server->accept() or return;\n\n\t\t\t\talarm(0);\n\t\t\t};\n\t\t\talarm(0);\n\t\t\tif ($@) {\n\t\t\t\tlog_in(\"died: $@\");\n\t\t\t\treturn undef;\n\t\t\t}\n\n\t\t\tlog2c(\"(new connection $client)\");\n\n\t\t\t$client->sysread(my $buf, 24) == 24 or return; # preface\n\n\t\t\t$c = Test::Nginx::HTTP2->new(1, socket => $client,\n\t\t\t\tpure => 1, preface => \"\") or return;\n\t\t}\n\n\t\tmy $frames = $c->read(all => [{ fin => 4 }]);\n\n\t\tif (!$extra{reuse}) {\n\t\t\t$c->h2_settings(0);\n\t\t\t$c->h2_settings(1);\n\t\t}\n\n\t\tmy ($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\n\t\t$sid = $frame->{sid};\n\t\treturn $frames;\n\t};\n\t$f->{data_len} = sub {\n\t\tmy ($body, $len) = @_;\n\t\t$s->h2_body($body) if defined $body;\n\t\treturn $c->read(all => [{ sid => $sid, length => $len }]);\n\t};\n\t$f->{update} = sub {\n\t\t$c->h2_window(shift);\n\t};\n\t$f->{update_sid} = sub {\n\t\t$c->h2_window(shift, $sid);\n\t};\n\t$f->{data} = sub {\n\t\tmy ($body, %extra) = @_;\n\t\t$s->h2_body($body, { %extra });\n\t\treturn $c->read(all => [{ sid => $sid,\n\t\t\tlength => length($body) }]);\n\t};\n\t$f->{http_end} = sub {\n\t\t$c->new_stream({ body_more => 1, headers => [\n\t\t\t{ name => ':status', value => '200', mode => 0 },\n\t\t\t{ name => 'content-type', value => 'application/grpc',\n\t\t\t\tmode => 1, huff => 1 },\n\t\t]}, $sid);\n\t\t$c->h2_body('Hello world', { body_more => 1 });\n\t\t$c->new_stream({ headers => [\n\t\t\t{ name => 'grpc-status', value => '0',\n\t\t\t\tmode => 2, huff => 1 },\n\t\t\t{ name => 'grpc-message', value => '',\n\t\t\t\tmode => 2, huff => 1 },\n\t\t]}, $sid);\n\n\t\treturn $s->read(all => [{ fin => 1 }]);\n\t};\n\treturn $f;\n}\n\nsub log2i { Test::Nginx::log_core('|| <<', @_); }\nsub log2o { Test::Nginx::log_core('|| >>', @_); }\nsub log2c { Test::Nginx::log_core('||', @_); }\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/gunzip.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for gunzip filter module.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx qw/ :DEFAULT :gzip /;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\neval { require IO::Compress::Gzip; };\nplan(skip_all => \"IO::Compress::Gzip not found\") if $@;\n\nmy $t = Test::Nginx->new()->has(qw/http gunzip proxy gzip_static rewrite/)\n\t->plan(13);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n        location / {\n            gunzip on;\n            gzip_vary on;\n            proxy_pass http://127.0.0.1:8081/;\n            proxy_set_header Accept-Encoding gzip;\n        }\n        location /error {\n            error_page 500 /t1;\n            return 500;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        location / {\n            default_type text/plain;\n            gzip_static on;\n            gzip_http_version 1.0;\n            gzip_types text/plain;\n        }\n    }\n}\n\nEOF\n\nmy $in = join('', map { sprintf \"X%03dXXXXXX\", $_ } (0 .. 99));\nmy $out;\n\nIO::Compress::Gzip::gzip(\\$in => \\$out);\n\n$t->write_file('t1.gz', $out);\n$t->write_file('t2.gz', $out . $out);\n$t->write_file('t3', 'not compressed');\n\nmy $emptyin = '';\nmy $emptyout;\nIO::Compress::Gzip::gzip(\\$emptyin => \\$emptyout);\n\n$t->write_file('empty.gz', $emptyout);\n\n$t->run();\n\n###############################################################################\n\npass('runs');\n\nmy $r = http_get('/t1');\nunlike($r, qr/Content-Encoding/, 'no content encoding');\nlike($r, qr/^(X\\d\\d\\dXXXXXX){100}$/m, 'correct gunzipped response');\n\n$r = http_gzip_request('/t1');\nlike($r, qr/Content-Encoding: gzip/, 'gzip still works - encoding');\nlike($r, qr/\\Q$out\\E/, 'gzip still works - content');\n\nlike(http_get('/t2'), qr/^(X\\d\\d\\dXXXXXX){200}$/m, 'multiple gzip members');\n\nlike(http_get('/error'), qr/^(X\\d\\d\\dXXXXXX){100}$/m, 'errors gunzipped');\n\nunlike(http_head('/t1'), qr/Content-Encoding/, 'head - no content encoding');\n\nlike(http_get('/t1'), qr/Vary/, 'get vary');\nlike(http_head('/t1'), qr/Vary/, 'head vary');\nunlike(http_get('/t3'), qr/Vary/, 'no vary on non-gzipped get');\nunlike(http_head('/t3'), qr/Vary/, 'no vary on non-gzipped head');\n\nlike(http_get('/empty'), qr/ 200 /, 'gunzip empty');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/gunzip_memcached.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for gunzip filter module with memcached.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx qw/ :DEFAULT :gzip /;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\neval { require Cache::Memcached; };\nplan(skip_all => 'Cache::Memcached not installed') if $@;\n\neval { require IO::Compress::Gzip; };\nplan(skip_all => \"IO::Compress::Gzip not found\") if $@;\n\nmy $t = Test::Nginx->new()->has(qw/http gunzip memcached rewrite/)\n\t->has_daemon('memcached')\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        gunzip on;\n\n        location / {\n            set $memcached_key $uri;\n            memcached_pass 127.0.0.1:8081;\n            memcached_gzip_flag 2;\n        }\n    }\n}\n\nEOF\n\nmy $memhelp = `memcached -h`;\nmy @memopts = ();\n\nif ($memhelp =~ /repcached/) {\n\t# repcached patch adds additional listen socket\n\tpush @memopts, '-X', port(8082);\n}\nif ($memhelp =~ /-U/) {\n\t# UDP port is on by default in memcached 1.2.7+\n\tpush @memopts, '-U', '0';\n}\n\n$t->run_daemon('memcached', '-l', '127.0.0.1', '-p', port(8081), @memopts);\n\n$t->run()->plan(2);\n\n$t->waitforsocket('127.0.0.1:' . port(8081))\n\tor die \"Can't start memcached\";\n\n# Put compressed value into memcached.  This requires compress_threshold to be\n# set and compressed value to be at least 20% less than original one.\n\nmy $memd = Cache::Memcached->new(servers => [ '127.0.0.1:' . port(8081) ],\n\tcompress_threshold => 1, connect_timeout => 1.0);\n$memd->set('/', 'TEST' x 10)\n\tor die \"can't put value into memcached: $!\";\n\n###############################################################################\n\nhttp_gzip_like(http_gzip_request('/'), qr/TEST/, 'memcached response gzipped');\nlike(http_get('/'), qr/TEST/, 'memcached response gunzipped');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/gunzip_perl.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for gunzip filter module with perl module.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx qw/ :DEFAULT :gzip /;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\neval { require IO::Compress::Gzip; };\nplan(skip_all => \"IO::Compress::Gzip not found\") if $@;\n\nmy $t = Test::Nginx->new()->has(qw/http gunzip perl/)->plan(2)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        gunzip on;\n\n        location / {\n            perl 'sub {\n                my $r = shift;\n                $r->header_out(\"Content-Encoding\", \"gzip\");\n                $r->send_http_header(\"text/plain\");\n                return OK if $r->header_only;\n                use IO::Compress::Gzip;\n                my $in = \"TEST\";\n                my $out;\n                IO::Compress::Gzip::gzip(\\\\$in => \\\\$out);\n                $r->print($out);\n                return OK;\n            }';\n        }\n    }\n}\n\nEOF\n\n$t->run();\n\n###############################################################################\n\nhttp_gzip_like(http_gzip_request('/'), qr/TEST/, 'perl response gzipped');\nlike(http_get('/'), qr/TEST/, 'perl response gunzipped');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/gunzip_ssi.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for gunzip filter module with subrequests.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx qw/ :DEFAULT :gzip /;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\neval { require IO::Compress::Gzip; };\nplan(skip_all => \"IO::Compress::Gzip not found\") if $@;\n\nmy $t = Test::Nginx->new()->has(qw/http gunzip ssi proxy gzip_static/)\n\t->plan(4);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            gunzip on;\n            gzip_vary on;\n            proxy_pass http://127.0.0.1:8081/;\n            proxy_set_header Accept-Encoding gzip;\n        }\n\n        location /t.html {\n            ssi on;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        location / {\n            default_type text/plain;\n            gzip_static on;\n            gzip_http_version 1.0;\n            gzip_types text/plain;\n        }\n    }\n}\n\nEOF\n\nmy $in = join('', map { sprintf \"X%03dXXXXXX\", $_ } (0 .. 99));\nmy $out;\n\nIO::Compress::Gzip::gzip(\\$in => \\$out);\n\n$t->write_file('t1.gz', $out);\n$t->write_file('t.html', 'xxx <!--#include virtual=\"/t1\" --> xxx');\n\n$t->run();\n\n###############################################################################\n\nmy $r = http_get('/t.html');\nunlike($r, qr/Content-Encoding/, 'no content encoding');\nlike($r, qr/^xxx (X\\d\\d\\dXXXXXX){100} xxx$/m, 'correct gunzipped response');\n\n$r = http_gzip_request('/t.html');\nunlike($r, qr/Content-Encoding/, 'gzip - no content encoding');\nlike($r, qr/(X\\d\\d\\dXXXXXX){100}/m, 'gzip - correct gunzipped response');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/gunzip_static.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for gunzip filter module with gzip_static always.  It is basically\n# the copy of gunzip.t with minor modifications.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx qw/ :DEFAULT :gzip /;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\neval { require IO::Compress::Gzip; };\nplan(skip_all => \"IO::Compress::Gzip not found\") if $@;\n\nmy $t = Test::Nginx->new()->has(qw/http gunzip gzip_static rewrite/);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n        location / {\n            gunzip on;\n            gzip_vary on;\n            gzip_static always;\n        }\n        location = /double {\n            error_page 404 @double;\n            gzip_static on;\n        }\n        location @double {\n            rewrite ^ /t1 break;\n            gunzip on;\n            gzip_static always;\n        }\n        location /error {\n            error_page 500 /t1;\n            return 500;\n        }\n    }\n}\n\nEOF\n\nmy $in = join('', map { sprintf \"X%03dXXXXXX\", $_ } (0 .. 99));\nmy $out;\n\nIO::Compress::Gzip::gzip(\\$in => \\$out);\n\n$t->write_file('t1.gz', $out);\n$t->write_file('t2.gz', $out . $out);\n$t->write_file('t3', 'not compressed');\n\n$t->run()->plan(12);\n\n###############################################################################\n\npass('runs');\n\nlike(http_get('/t1'), qr/(?!Content-Encoding).*^(X\\d\\d\\dXXXXXX){100}$/m,\n\t'correct gunzipped response');\nlike(http_gzip_request('/t1'), qr/Content-Encoding: gzip.*\\Q$out\\E/ms,\n\t'gzip still works');\n\nlike(http_get('/double'), qr/(?!Content-Encoding).^(X\\d\\d\\dXXXXXX){100}$/ms,\n\t'gunzip with gzip_tested');\nlike(http_gzip_request('/double'), qr/Content-Encoding: gzip.*\\Q$out\\E/ms,\n\t'gzip still works with gzip_tested');\n\nlike(http_get('/t2'), qr/^(X\\d\\d\\dXXXXXX){200}$/m, 'multiple gzip members');\n\nlike(http_get('/error'), qr/^(X\\d\\d\\dXXXXXX){100}$/m, 'errors gunzipped');\n\nunlike(http_head('/t1'), qr/Content-Encoding/, 'head - no content encoding');\n\nlike(http_get('/t1'), qr/Vary/, 'get vary');\nlike(http_head('/t1'), qr/Vary/, 'head vary');\nunlike(http_get('/t3'), qr/Vary/, 'no vary on non-gzipped get');\nunlike(http_head('/t3'), qr/Vary/, 'no vary on non-gzipped head');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/gzip.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for nginx gzip filter module.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx qw/ :DEFAULT :gzip /;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy gzip/)->plan(8);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n        location / {\n            gzip on;\n        }\n        location /proxy/ {\n            gzip on;\n            proxy_pass http://127.0.0.1:8080/local/;\n        }\n        location /local/ {\n            gzip off;\n            alias %%TESTDIR%%/;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('index.html', 'X' x 64);\n\n$t->run();\n\n###############################################################################\n\nmy $r;\n\n$r = http_gzip_request('/');\nlike($r, qr/^Content-Encoding: gzip/m, 'gzip');\nhttp_gzip_like($r, qr/^X{64}\\Z/, 'gzip content correct');\n\n$r = http_gzip_request('/proxy/');\nlike($r, qr/^Content-Encoding: gzip/m, 'gzip proxied');\nhttp_gzip_like($r, qr/^X{64}\\Z/, 'gzip proxied content');\n\n# Accept-Ranges headers should be cleared\n\nunlike(http_gzip_request('/'), qr/Accept-Ranges/im, 'cleared accept-ranges');\nunlike(http_gzip_request('/proxy/'), qr/Accept-Ranges/im,\n\t'cleared headers from proxy');\n\n# HEAD requests should return correct headers\n\nlike(http_gzip_head('/'), qr/Content-Encoding: gzip/, 'gzip head');\nunlike(http_head('/'), qr/Content-Encoding: gzip/, 'no gzip head');\n\n###############################################################################\n\nsub http_gzip_head {\n\tmy ($uri) = @_;\n\treturn http(<<EOF);\nHEAD $uri HTTP/1.1\nHost: localhost\nConnection: close\nAccept-Encoding: gzip\n\nEOF\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/gzip_flush.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for gzip filter module.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx qw/ :DEFAULT :gzip /;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http gzip perl/)->plan(2)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        gzip on;\n        gzip_min_length 0;\n\n        location / {\n            perl 'sub {\n                my $r = shift;\n                $r->send_http_header(\"text/html\");\n                return OK if $r->header_only;\n                $r->print(\"DA\");\n                $r->flush();\n                $r->flush();\n                $r->print(\"TA\");\n                return OK;\n            }';\n        }\n    }\n}\n\nEOF\n\n$t->run();\n\n###############################################################################\n\nlike(http_get('/'), qr/DATA/, 'request with flush');\n\n# gzip filter wasn't able to handle empty flush buffers, see\n# http://nginx.org/pipermail/nginx/2010-November/023693.html\n\nhttp_gzip_like(http_gzip_request('/'), qr/DATA/, 'gzip request with flush');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/h2.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for HTTP/2 protocol [RFC7540].\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse Socket qw/ CRLF /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse Test::Nginx::HTTP2;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http http_v2 proxy rewrite charset gzip/)\n\t->plan(142);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080 http2;\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        location / {\n            add_header X-Header X-Foo;\n            add_header X-Sent-Foo $http_x_foo;\n            add_header X-Referer $http_referer;\n            return 200 'body';\n        }\n        location /t {\n        }\n        location /gzip.html {\n            gzip on;\n            gzip_min_length 0;\n            gzip_vary on;\n            alias %%TESTDIR%%/t2.html;\n        }\n        location /frame_size {\n            http2_chunk_size 64k;\n            alias %%TESTDIR%%;\n            output_buffers 2 1m;\n        }\n        location /chunk_size {\n            http2_chunk_size 1;\n            return 200 'body';\n        }\n        location /redirect {\n            error_page 405 /;\n            return 405;\n        }\n        location /return301 {\n            return 301;\n        }\n        location /return301_absolute {\n            return 301 text;\n        }\n        location /return301_relative {\n            return 301 /;\n        }\n        location /charset {\n            charset utf-8;\n            return 200;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8082 http2;\n        server_name  localhost;\n        return 200   first;\n    }\n\n    server {\n        listen       127.0.0.1:8082 http2;\n        server_name  localhost2;\n        return 200   second;\n    }\n\n    server {\n        listen       127.0.0.1:8083 http2;\n        server_name  localhost;\n\n        http2_max_concurrent_streams 1;\n    }\n\n    server {\n        listen       127.0.0.1:8086 http2;\n        server_name  localhost;\n\n        send_timeout 1s;\n        lingering_close off;\n    }\n\n    server {\n        listen       127.0.0.1:8087 http2;\n        server_name  localhost;\n\n        client_header_timeout 1s;\n        client_body_timeout 1s;\n        lingering_close off;\n\n        location / { }\n\n        location /proxy/ {\n            proxy_pass http://127.0.0.1:8081/;\n        }\n    }\n}\n\nEOF\n\n# suppress deprecation warning\nopen OLDERR, \">&\", \\*STDERR; close STDERR;\n$t->run();\nopen STDERR, \">&\", \\*OLDERR;\n\n# file size is slightly beyond initial window size: 2**16 + 80 bytes\n\n$t->write_file('t1.html',\n\tjoin('', map { sprintf \"X%04dXXX\", $_ } (1 .. 8202)));\n$t->write_file('tbig.html',\n\tjoin('', map { sprintf \"XX%06dXX\", $_ } (1 .. 500000)));\n\n$t->write_file('t2.html', 'SEE-THIS');\n\n###############################################################################\n\n# SETTINGS\n\nmy $s = Test::Nginx::HTTP2->new(port(8080), pure => 1);\nmy $frames = $s->read(all => [\n\t{ type => 'WINDOW_UPDATE' },\n\t{ type => 'SETTINGS'}\n]);\n\nmy ($frame) = grep { $_->{type} eq 'WINDOW_UPDATE' } @$frames;\nok($frame, 'WINDOW_UPDATE frame');\nis($frame->{flags}, 0, 'WINDOW_UPDATE zero flags');\nis($frame->{sid}, 0, 'WINDOW_UPDATE zero sid');\nis($frame->{length}, 4, 'WINDOW_UPDATE fixed length');\n\n($frame) = grep { $_->{type} eq 'SETTINGS' } @$frames;\nok($frame, 'SETTINGS frame');\nis($frame->{flags}, 0, 'SETTINGS flags');\nis($frame->{sid}, 0, 'SETTINGS stream');\n\n$s->h2_settings(1);\n$s->h2_settings(0);\n\n$frames = $s->read(all => [{ type => 'SETTINGS' }]);\n\n($frame) = grep { $_->{type} eq 'SETTINGS' } @$frames;\nok($frame, 'SETTINGS frame ack');\nis($frame->{flags}, 1, 'SETTINGS flags ack');\n\n# SETTINGS - no ack on PROTOCOL_ERROR\n\n$s = Test::Nginx::HTTP2->new(port(8080), pure => 1);\n$frames = $s->read(all => [\n\t{ type => 'WINDOW_UPDATE' },\n\t{ type => 'SETTINGS'}\n]);\n\n$s->h2_settings(1);\n$s->h2_settings(0, 0x5 => 42);\n\n$frames = $s->read(all => [\n\t{ type => 'SETTINGS'},\n\t{ type => 'GOAWAY' }\n]);\n\n($frame) = grep { $_->{type} eq 'SETTINGS' } @$frames;\nis($frame, undef, 'SETTINGS PROTOCOL_ERROR - no ack');\n\n($frame) = grep { $_->{type} eq 'GOAWAY' } @$frames;\nok($frame, 'SETTINGS PROTOCOL_ERROR - GOAWAY');\n\n# PING\n\n$s = Test::Nginx::HTTP2->new();\n$s->h2_ping('SEE-THIS');\n$frames = $s->read(all => [{ type => 'PING' }]);\n\n($frame) = grep { $_->{type} eq \"PING\" } @$frames;\nok($frame, 'PING frame');\nis($frame->{value}, 'SEE-THIS', 'PING payload');\nis($frame->{flags}, 1, 'PING flags ack');\nis($frame->{sid}, 0, 'PING stream');\n\n# GOAWAY\n\nTest::Nginx::HTTP2->new()->h2_goaway(0, 0, 5);\nTest::Nginx::HTTP2->new()->h2_goaway(0, 0, 5, 'foobar');\nTest::Nginx::HTTP2->new()->h2_goaway(0, 0, 5, 'foobar', split => [ 8, 8, 4 ]);\n\n$s = Test::Nginx::HTTP2->new();\n$s->h2_goaway(0, 0, 5);\n$s->h2_goaway(0, 0, 5);\n\n$s = Test::Nginx::HTTP2->new();\n$s->h2_goaway(0, 0, 5, 'foobar', len => 0);\n$frames = $s->read(all => [{ type => \"GOAWAY\" }]);\n\n($frame) = grep { $_->{type} eq \"GOAWAY\" } @$frames;\nok($frame, 'GOAWAY invalid length - GOAWAY frame');\nis($frame->{code}, 6, 'GOAWAY invalid length - GOAWAY FRAME_SIZE_ERROR');\n\n# 6.8.  GOAWAY\n#   An endpoint MUST treat a GOAWAY frame with a stream identifier other\n#   than 0x0 as a connection error (Section 5.4.1) of type PROTOCOL_ERROR.\n\n$s = Test::Nginx::HTTP2->new();\n$s->h2_goaway(1, 0, 5, 'foobar');\n$frames = $s->read(all => [{ type => \"GOAWAY\" }], wait => 0.5);\n\n($frame) = grep { $_->{type} eq \"GOAWAY\" } @$frames;\nok($frame, 'GOAWAY invalid stream - GOAWAY frame');\nis($frame->{code}, 1, 'GOAWAY invalid stream - GOAWAY PROTOCOL_ERROR');\n\n# client-initiated PUSH_PROMISE, just to ensure nothing went wrong\n# N.B. other implementation returns zero code, which is not anyhow regulated\n\n$s = Test::Nginx::HTTP2->new();\n{\n\tlocal $SIG{PIPE} = 'IGNORE';\n\tsyswrite($s->{socket}, pack(\"x2C2xN\", 4, 0x5, 1));\n}\n$frames = $s->read(all => [{ type => \"GOAWAY\" }]);\n\n($frame) = grep { $_->{type} eq \"GOAWAY\" } @$frames;\nok($frame, 'client-initiated PUSH_PROMISE - GOAWAY frame');\nis($frame->{code}, 1, 'client-initiated PUSH_PROMISE - GOAWAY PROTOCOL_ERROR');\n\n# GET\n\n$s = Test::Nginx::HTTP2->new();\nmy $sid = $s->new_stream();\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nok($frame, 'HEADERS frame');\nis($frame->{sid}, $sid, 'HEADERS stream');\nis($frame->{headers}->{':status'}, 200, 'HEADERS status');\nis($frame->{headers}->{'x-header'}, 'X-Foo', 'HEADERS header');\n\n($frame) = grep { $_->{type} eq \"DATA\" } @$frames;\nok($frame, 'DATA frame');\nis($frame->{length}, length 'body', 'DATA length');\nis($frame->{data}, 'body', 'DATA payload');\n\n# GET in the new stream on same connection\n\n$sid = $s->new_stream();\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{sid}, $sid, 'HEADERS stream 2');\nis($frame->{headers}->{':status'}, 200, 'HEADERS status 2');\nis($frame->{headers}->{'x-header'}, 'X-Foo', 'HEADERS header 2');\n\n($frame) = grep { $_->{type} eq \"DATA\" } @$frames;\nok($frame, 'DATA frame 2');\nis($frame->{sid}, $sid, 'HEADERS stream 2');\nis($frame->{length}, length 'body', 'DATA length 2');\nis($frame->{data}, 'body', 'DATA payload 2');\n\n# HEAD\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ method => 'HEAD' });\n$frames = $s->read(all => [{ sid => $sid, fin => 0x4 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{sid}, $sid, 'HEAD - HEADERS');\nis($frame->{headers}->{':status'}, 200, 'HEAD - HEADERS status');\nis($frame->{headers}->{'x-header'}, 'X-Foo', 'HEAD - HEADERS header');\n\n($frame) = grep { $_->{type} eq \"DATA\" } @$frames;\nis($frame, undef, 'HEAD - no body');\n\n# CONNECT\n\nTODO: {\nlocal $TODO = 'not yet' unless $t->has_version('1.21.1');\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ method => 'CONNECT' });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 405, 'CONNECT - not allowed');\n\n}\n\n# TRACE\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ method => 'TRACE' });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 405, 'TRACE - not allowed');\n\n# range filter\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/t1.html', mode => 1 },\n\t{ name => ':authority', value => 'localhost', mode => 1 },\n\t{ name => 'range', value => 'bytes=10-19', mode => 1 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 206, 'range - HEADERS status');\n\n($frame) = grep { $_->{type} eq \"DATA\" } @$frames;\nis($frame->{length}, 10, 'range - DATA length');\nis($frame->{data}, '002XXXX000', 'range - DATA payload');\n\n# http2_chunk_size=1\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/chunk_size' });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\nmy @data = grep { $_->{type} eq \"DATA\" } @$frames;\nis(@data, 4, 'chunk_size frames');\nis(join(' ', map { $_->{data} } @data), 'b o d y', 'chunk_size data');\nis(join(' ', map { $_->{flags} } @data), '0 0 0 1', 'chunk_size flags');\n\n# CONTINUATION\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ continuation => 1, headers => [\n\t{ name => ':method', value => 'HEAD', mode => 1 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/', mode => 0 },\n\t{ name => ':authority', value => 'localhost', mode => 1 }]});\n$s->h2_continue($sid, { continuation => 1, headers => [\n\t{ name => 'x-foo', value => 'X-Bar', mode => 2 }]});\n$s->h2_continue($sid, { headers => [\n\t{ name => 'referer', value => 'foo', mode => 2 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"DATA\" } @$frames;\nis($frame, undef, 'CONTINUATION - fragment 1');\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{'x-sent-foo'}, 'X-Bar', 'CONTINUATION - fragment 2');\nis($frame->{headers}->{'x-referer'}, 'foo', 'CONTINUATION - fragment 3');\n\n# CONTINUATION - in the middle of request header field\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ continuation => [ 2, 4, 1, 5 ], headers => [\n\t{ name => ':method', value => 'HEAD', mode => 1 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/', mode => 0 },\n\t{ name => ':authority', value => 'localhost', mode => 1 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 200, 'CONTINUATION - in header field');\n\n# CONTINUATION on a closed stream\n\n$s->h2_continue(1, { headers => [\n\t{ name => 'x-foo', value => 'X-Bar', mode => 2 }]});\n$frames = $s->read(all => [{ sid => 1, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"GOAWAY\" } @$frames;\nis($frame->{type}, 'GOAWAY', 'GOAWAY - CONTINUATION closed stream');\nis($frame->{code}, 1, 'GOAWAY - CONTINUATION closed stream - PROTOCOL_ERROR');\n\n# frame padding\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ padding => 42, headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/', mode => 0 },\n\t{ name => ':authority', value => 'localhost', mode => 1 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 200, 'padding - HEADERS status');\n\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/', mode => 0 },\n\t{ name => ':authority', value => 'localhost', mode => 1 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 200, 'padding - next stream');\n\n# padding followed by CONTINUATION\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ padding => 42, continuation => [ 2, 4, 1, 5 ],\n\theaders => [\n\t{ name => ':method', value => 'GET', mode => 1 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/', mode => 0 },\n\t{ name => ':authority', value => 'localhost', mode => 1 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 200, 'padding - CONTINUATION');\n\n# internal redirect\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/redirect' });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 405, 'redirect - HEADERS');\n\n($frame) = grep { $_->{type} eq \"DATA\" } @$frames;\nok($frame, 'redirect - DATA');\nis($frame->{data}, 'body', 'redirect - DATA payload');\n\n# return 301 with absolute URI\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/return301_absolute' });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 301, 'return 301 absolute - status');\nis($frame->{headers}->{'location'}, 'text', 'return 301 absolute - location');\n\n# return 301 with relative URI\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/return301_relative' });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 301, 'return 301 relative - status');\nis($frame->{headers}->{'location'}, 'http://localhost:' . port(8080) . '/',\n\t'return 301 relative - location');\n\n# return 301 with relative URI and ':authority' request header field\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/return301_relative', mode => 2 },\n\t{ name => ':authority', value => 'localhost', mode => 2 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 301,\n\t'return 301 relative - authority - status');\nis($frame->{headers}->{'location'}, 'http://localhost:' . port(8080) . '/',\n\t'return 301 relative - authority - location');\n\n# return 301 with relative URI and 'host' request header field\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/return301_relative', mode => 2 },\n\t{ name => 'host', value => 'localhost', mode => 2 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 301,\n\t'return 301 relative - host - status');\nis($frame->{headers}->{'location'}, 'http://localhost:' . port(8080) . '/',\n\t'return 301 relative - host - location');\n\n# virtual host\n\n$s = Test::Nginx::HTTP2->new(port(8082));\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/', mode => 0 },\n\t{ name => 'host', value => 'localhost', mode => 2 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 200,\n\t'virtual host - host - status');\n\n($frame) = grep { $_->{type} eq \"DATA\" } @$frames;\nis($frame->{data}, 'first', 'virtual host - host - DATA');\n\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/', mode => 0 },\n\t{ name => ':authority', value => 'localhost', mode => 2 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 200,\n\t'virtual host - authority - status');\n\n($frame) = grep { $_->{type} eq \"DATA\" } @$frames;\nis($frame->{data}, 'first', 'virtual host - authority - DATA');\n\n# virtual host - second\n\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/', mode => 0 },\n\t{ name => 'host', value => 'localhost2', mode => 2 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 200,\n\t'virtual host 2 - host - status');\n\n($frame) = grep { $_->{type} eq \"DATA\" } @$frames;\nis($frame->{data}, 'second', 'virtual host 2 - host - DATA');\n\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/', mode => 0 },\n\t{ name => ':authority', value => 'localhost2', mode => 2 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 200,\n\t'virtual host 2 - authority - status');\n\n($frame) = grep { $_->{type} eq \"DATA\" } @$frames;\nis($frame->{data}, 'second', 'virtual host 2 - authority - DATA');\n\n# gzip tests for internal nginx version\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/gzip.html' },\n\t{ name => ':authority', value => 'localhost', mode => 1 },\n\t{ name => 'accept-encoding', value => 'gzip' }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{'content-encoding'}, 'gzip', 'gzip - encoding');\nis($frame->{headers}->{'vary'}, 'Accept-Encoding', 'gzip - vary');\n\n($frame) = grep { $_->{type} eq \"DATA\" } @$frames;\ngunzip_like($frame->{data}, qr/^SEE-THIS\\Z/, 'gzip - DATA');\n\n# charset\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/charset' });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{'content-type'}, 'text/plain; charset=utf-8', 'charset');\n\n# partial request header frame received (field split),\n# the rest of frame is received after client header timeout\n\n$s = Test::Nginx::HTTP2->new(port(8087));\n$sid = $s->new_stream({ path => '/t2.html', split => [35],\n\tsplit_delay => 2.1 });\n$frames = $s->read(all => [{ type => 'RST_STREAM' }]);\n\n($frame) = grep { $_->{type} eq \"RST_STREAM\" } @$frames;\nok($frame, 'client header timeout');\nis($frame->{code}, 1, 'client header timeout - protocol error');\n\n$s->h2_ping('SEE-THIS');\n$frames = $s->read(all => [{ type => 'PING' }]);\n\n($frame) = grep { $_->{type} eq \"PING\" && $_->{flags} & 0x1 } @$frames;\nok($frame, 'client header timeout - PING');\n\n# partial request header frame received (no field split),\n# the rest of frame is received after client header timeout\n\n$s = Test::Nginx::HTTP2->new(port(8087));\n$sid = $s->new_stream({ path => '/t2.html', split => [20], split_delay => 2.1 });\n$frames = $s->read(all => [{ type => 'RST_STREAM' }]);\n\n($frame) = grep { $_->{type} eq \"RST_STREAM\" } @$frames;\nok($frame, 'client header timeout 2');\nis($frame->{code}, 1, 'client header timeout 2 - protocol error');\n\n$s->h2_ping('SEE-THIS');\n$frames = $s->read(all => [{ type => 'PING' }]);\n\n($frame) = grep { $_->{type} eq \"PING\" && $_->{flags} & 0x1 } @$frames;\nok($frame, 'client header timeout 2 - PING');\n\n# partial request body data frame received, the rest is after body timeout\n\n$s = Test::Nginx::HTTP2->new(port(8087));\n$sid = $s->new_stream({ path => '/proxy/t2.html', body_more => 1 });\n$s->h2_body('TEST', { split => [10], split_delay => 2.1 });\n$frames = $s->read(all => [{ type => 'RST_STREAM' }]);\n\n($frame) = grep { $_->{type} eq \"RST_STREAM\" } @$frames;\nok($frame, 'client body timeout');\nis($frame->{code}, 1, 'client body timeout - protocol error');\n\n$s->h2_ping('SEE-THIS');\n$frames = $s->read(all => [{ type => 'PING' }]);\n\n($frame) = grep { $_->{type} eq \"PING\" && $_->{flags} & 0x1 } @$frames;\nok($frame, 'client body timeout - PING');\n\n# partial request body data frame with connection close after body timeout\n\n$s = Test::Nginx::HTTP2->new(port(8087));\n$sid = $s->new_stream({ path => '/proxy/t2.html', body_more => 1 });\n$s->h2_body('TEST', { split => [ 12 ], abort => 1 });\n\nselect undef, undef, undef, 1.1;\nundef $s;\n\n# proxied request with logging pristine request header field (e.g., referer)\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET' },\n\t{ name => ':scheme', value => 'http' },\n\t{ name => ':path', value => '/proxy2/' },\n\t{ name => ':authority', value => 'localhost' },\n\t{ name => 'referer', value => 'foo' }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 200, 'proxy with logging request headers');\n\n$sid = $s->new_stream();\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nok($frame->{headers}, 'proxy with logging request headers - next');\n\n# initial window size, client side\n\n# 6.9.2.  Initial Flow-Control Window Size\n#   When an HTTP/2 connection is first established, new streams are\n#   created with an initial flow-control window size of 65,535 octets.\n#   The connection flow-control window is also 65,535 octets.\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/t1.html' });\n$frames = $s->read(all => [{ sid => $sid, length => 2**16 - 1 }]);\n\n# with the default http2_chunk_size, data is divided into 8 data frames\n\n@data = grep { $_->{type} eq \"DATA\" } @$frames;\nmy $lengths = join ' ', map { $_->{length} } @data;\nis($lengths, '8192 8192 8192 8192 8192 8192 8192 8191',\n\t'iws - stream blocked on initial window size');\n\n$s->h2_ping('SEE-THIS');\n$frames = $s->read(all => [{ type => 'PING' }]);\n\n($frame) = grep { $_->{type} eq \"PING\" && $_->{flags} & 0x1 } @$frames;\nok($frame, 'iws - PING not blocked');\n\n$s->h2_window(2**16, $sid);\n$frames = $s->read(wait => 0.2);\nis(@$frames, 0, 'iws - updated stream window');\n\n$s->h2_window(2**16);\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n@data = grep { $_->{type} eq \"DATA\" } @$frames;\nmy $sum = eval join '+', map { $_->{length} } @data;\nis($sum, 81, 'iws - updated connection window');\n\n# SETTINGS (initial window size, client side)\n\n# 6.9.2.  Initial Flow-Control Window Size\n#   Both endpoints can adjust the initial window size for new streams by\n#   including a value for SETTINGS_INITIAL_WINDOW_SIZE in the SETTINGS\n#   frame that forms part of the connection preface.  The connection\n#   flow-control window can only be changed using WINDOW_UPDATE frames.\n\n$s = Test::Nginx::HTTP2->new();\n$s->h2_settings(0, 0x4 => 2**17);\n$s->h2_window(2**17);\n\n$sid = $s->new_stream({ path => '/t1.html' });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n@data = grep { $_->{type} eq \"DATA\" } @$frames;\n$sum = eval join '+', map { $_->{length} } @data;\nis($sum, 2**16 + 80, 'iws - increased');\n\n# INITIAL_WINDOW_SIZE duplicate settings\n\n# 6.5.  SETTINGS\n#   Each parameter in a SETTINGS frame replaces any existing value for\n#   that parameter.  Parameters are processed in the order in which they\n#   appear, and a receiver of a SETTINGS frame does not need to maintain\n#   any state other than the current value of its parameters.  Therefore,\n#   the value of a SETTINGS parameter is the last value that is seen by a\n#   receiver.\n\n$s = Test::Nginx::HTTP2->new();\n$s->h2_window(2**17);\n\n$sid = $s->new_stream({ path => '/t1.html' });\n\n$frames = $s->read(all => [{ sid => $sid, length => 2**16 - 1 }]);\n@data = grep { $_->{type} eq \"DATA\" } @$frames;\n$sum = eval join '+', map { $_->{length} } @data;\nis($sum, 2**16 - 1, 'iws duplicate - default stream window');\n\n# this should effect in extra stream window octect\n# $s->h2_settings(0, 0x4 => 42, 0x4 => 2**16);\n{\n\tlocal $SIG{PIPE} = 'IGNORE';\n\tsyswrite($s->{socket}, pack(\"x2C2x5nNnN\", 12, 0x4, 4, 42, 4, 2**16));\n}\n\n$frames = $s->read(all => [{ sid => $sid, length => 1 }]);\n@data = grep { $_->{type} eq \"DATA\" } @$frames;\n$sum = eval join '+', map { $_->{length} } @data;\nis($sum, 1, 'iws duplicate - updated stream window');\n\n# yet more octets to finish receiving the response\n\n$s->h2_settings(0, 0x4 => 2**16 + 80);\n\n$frames = $s->read(all => [{ sid => $sid, length => 80 }]);\n@data = grep { $_->{type} eq \"DATA\" } @$frames;\n$sum = eval join '+', map { $_->{length} } @data;\nis($sum, 80, 'iws duplicate - updated stream window 2');\n\n# probe for negative available space in a flow control window\n\n# 6.9.2.  Initial Flow-Control Window Size\n#   A change to SETTINGS_INITIAL_WINDOW_SIZE can cause the available\n#   space in a flow-control window to become negative.  A sender MUST\n#   track the negative flow-control window and MUST NOT send new flow-\n#   controlled frames until it receives WINDOW_UPDATE frames that cause\n#   the flow-control window to become positive.\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/t1.html' });\n$s->read(all => [{ sid => $sid, length => 2**16 - 1 }]);\n\n$s->h2_window(1);\n$s->h2_settings(0, 0x4 => 42);\n$s->h2_window(1024, $sid);\n\n$frames = $s->read(all => [{ type => 'SETTINGS' }]);\n\n($frame) = grep { $_->{type} eq 'SETTINGS' } @$frames;\nok($frame, 'negative window - SETTINGS frame ack');\nis($frame->{flags}, 1, 'negative window - SETTINGS flags ack');\n\n($frame) = grep { $_->{type} ne 'SETTINGS' } @$frames;\nis($frame, undef, 'negative window - no data');\n\n# predefined window size, minus new iws settings, minus window update\n\n$s->h2_window(2**16 - 1 - 42 - 1024, $sid);\n\n$frames = $s->read(wait => 0.2);\nis(@$frames, 0, 'zero window - no data');\n\n$s->h2_window(1, $sid);\n\n$frames = $s->read(all => [{ sid => $sid, length => 1 }]);\nis(@$frames, 1, 'positive window');\n\nSKIP: {\nskip 'failed connection', 2 unless @$frames;\n\nis(@$frames[0]->{type}, 'DATA', 'positive window - data');\nis(@$frames[0]->{length}, 1, 'positive window - data length');\n\n}\n\n$s = Test::Nginx::HTTP2->new();\n$s->h2_window(2**30);\n$s->h2_settings(0, 0x4 => 2**30);\n\n$sid = $s->new_stream({ path => '/frame_size/tbig.html' });\n\nsleep 1;\n$s->h2_settings(0, 0x5 => 2**15);\n\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n$lengths = join ' ', map { $_->{length} } @$frames;\nunlike($lengths, qr/16384 0 16384/, 'SETTINGS ack after queued DATA');\n\n# ask write handler in sending large response\n\nSKIP: {\nskip 'unsafe socket tests', 4 unless $ENV{TEST_NGINX_UNSAFE};\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/tbig.html' });\n\n$s->h2_window(2**30, $sid);\n$s->h2_window(2**30);\n\nsleep 1;\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 200, 'large response - HEADERS');\n\n@data = grep { $_->{type} eq \"DATA\" } @$frames;\n$sum = eval join '+', map { $_->{length} } @data;\nis($sum, 5000000, 'large response - DATA');\n\n# Make sure http2 write handler doesn't break a connection.\n\n$sid = $s->new_stream();\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 200, 'new stream after large response');\n\n# write event send timeout\n\n$s = Test::Nginx::HTTP2->new(port(8086));\n$sid = $s->new_stream({ path => '/tbig.html' });\n$s->h2_window(2**30, $sid);\n$s->h2_window(2**30);\n\nselect undef, undef, undef, 2.1;\n\n$s->h2_ping('SEE-THIS');\n\n$frames = $s->read(all => [{ type => 'PING' }]);\nok(!grep ({ $_->{type} eq \"PING\" } @$frames), 'large response - send timeout');\n\n}\n\n# SETTINGS_MAX_FRAME_SIZE\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/frame_size/t1.html' });\n$s->h2_window(2**18, 1);\n$s->h2_window(2**18);\n\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n@data = grep { $_->{type} eq \"DATA\" } @$frames;\nis($data[0]->{length}, 2**14, 'max frame size - default');\n\n$s = Test::Nginx::HTTP2->new();\n$s->h2_settings(0, 0x5 => 2**15);\n$sid = $s->new_stream({ path => '/frame_size/t1.html' });\n$s->h2_window(2**18, 1);\n$s->h2_window(2**18);\n\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n@data = grep { $_->{type} eq \"DATA\" } @$frames;\nis($data[0]->{length}, 2**15, 'max frame size - custom');\n\n# SETTINGS_INITIAL_WINDOW_SIZE + SETTINGS_MAX_FRAME_SIZE\n# Expanding available stream window should not result in emitting\n# new frames before remaining SETTINGS parameters were applied.\n\n$s = Test::Nginx::HTTP2->new();\n$s->h2_window(2**17);\n$s->h2_settings(0, 0x4 => 42);\n\n$sid = $s->new_stream({ path => '/frame_size/t1.html' });\n$s->read(all => [{ sid => $sid, length => 42 }]);\n\n$s->h2_settings(0, 0x4 => 2**17, 0x5 => 2**15);\n\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n@data = grep { $_->{type} eq \"DATA\" } @$frames;\n$lengths = join ' ', map { $_->{length} } @data;\nis($lengths, '32768 32768 38', 'multiple SETTINGS');\n\n# stream multiplexing + WINDOW_UPDATE\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/t1.html' });\n$frames = $s->read(all => [{ sid => $sid, length => 2**16 - 1 }]);\n\n@data = grep { $_->{type} eq \"DATA\" } @$frames;\n$sum = eval join '+', map { $_->{length} } @data;\nis($sum, 2**16 - 1, 'multiple - stream1 data');\n\nmy $sid2 = $s->new_stream({ path => '/t1.html' });\n$frames = $s->read(all => [{ sid => $sid2, fin => 0x4 }]);\n\n@data = grep { $_->{type} eq \"DATA\" } @$frames;\nis(@data, 0, 'multiple - stream2 no data');\n\n$s->h2_window(2**17, $sid);\n$s->h2_window(2**17, $sid2);\n$s->h2_window(2**17);\n\n$frames = $s->read(all => [\n\t{ sid => $sid, fin => 1 },\n\t{ sid => $sid2, fin => 1 }\n]);\n\n@data = grep { $_->{type} eq \"DATA\" && $_->{sid} == $sid } @$frames;\n$sum = eval join '+', map { $_->{length} } @data;\nis($sum, 81, 'multiple - stream1 remain data');\n\n@data = grep { $_->{type} eq \"DATA\" && $_->{sid} == $sid2 } @$frames;\n$sum = eval join '+', map { $_->{length} } @data;\nis($sum, 2**16 + 80, 'multiple - stream2 full data');\n\n# http2_max_concurrent_streams\n\n$s = Test::Nginx::HTTP2->new(port(8083), pure => 1);\n$frames = $s->read(all => [{ type => 'SETTINGS' }]);\n\n($frame) = grep { $_->{type} eq 'SETTINGS' } @$frames;\nis($frame->{3}, 1, 'http2_max_concurrent_streams SETTINGS');\n\n$s->h2_window(2**18);\n\n$sid = $s->new_stream({ path => '/t1.html' });\n$frames = $s->read(all => [{ sid => $sid, length => 2 ** 16 - 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" && $_->{sid} == $sid } @$frames;\nis($frame->{headers}->{':status'}, 200, 'http2_max_concurrent_streams');\n\n$sid2 = $s->new_stream({ path => '/t1.html' });\n$frames = $s->read(all => [{ type => 'RST_STREAM' }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" && $_->{sid} == $sid2 } @$frames;\nisnt($frame->{headers}->{':status'}, 200, 'http2_max_concurrent_streams 2');\n\n($frame) = grep { $_->{type} eq \"RST_STREAM\" && $_->{sid} == $sid2 } @$frames;\nis($frame->{sid}, $sid2, 'http2_max_concurrent_streams RST_STREAM sid');\nis($frame->{length}, 4, 'http2_max_concurrent_streams RST_STREAM length');\nis($frame->{flags}, 0, 'http2_max_concurrent_streams RST_STREAM flags');\nis($frame->{code}, 7, 'http2_max_concurrent_streams RST_STREAM code');\n\n# properly skip header field that's not/never indexed from discarded streams\n\n$sid2 = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET' },\n\t{ name => ':scheme', value => 'http' },\n\t{ name => ':path', value => '/', mode => 6 },\n\t{ name => ':authority', value => 'localhost' },\n\t{ name => 'x-foo', value => 'Foo', mode => 2 }]});\n$frames = $s->read(all => [{ type => 'RST_STREAM' }]);\n\n# also if split across writes\n\n$sid2 = $s->new_stream({ split => [ 22 ], headers => [\n\t{ name => ':method', value => 'GET' },\n\t{ name => ':scheme', value => 'http' },\n\t{ name => ':path', value => '/', mode => 6 },\n\t{ name => ':authority', value => 'localhost' },\n\t{ name => 'x-bar', value => 'Bar', mode => 2 }]});\n$frames = $s->read(all => [{ type => 'RST_STREAM' }]);\n\n# also if split across frames\n\n$sid2 = $s->new_stream({ continuation => [ 17 ], headers => [\n\t{ name => ':method', value => 'GET' },\n\t{ name => ':scheme', value => 'http' },\n\t{ name => ':path', value => '/', mode => 6 },\n\t{ name => ':authority', value => 'localhost' },\n\t{ name => 'x-baz', value => 'Baz', mode => 2 }]});\n$frames = $s->read(all => [{ type => 'RST_STREAM' }]);\n\n$s->h2_window(2**16, $sid);\n$s->read(all => [{ sid => $sid, fin => 1 }]);\n\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET' },\n\t{ name => ':scheme', value => 'http' },\n\t{ name => ':path', value => '/t2.html' },\n\t{ name => ':authority', value => 'localhost' },\n# make sure that discarded streams updated dynamic table\n\t{ name => 'x-foo', value => 'Foo', mode => 0 },\n\t{ name => 'x-bar', value => 'Bar', mode => 0 },\n\t{ name => 'x-baz', value => 'Baz', mode => 0 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" && $_->{sid} == $sid } @$frames;\nis($frame->{headers}->{':status'}, 200, 'http2_max_concurrent_streams 3');\n\n\n# some invalid cases below\n\n# invalid connection preface\n\nTODO: {\nlocal $TODO = 'not yet' unless $t->has_version('1.25.1');\n\nlike(http('x' x 16), qr/400 Bad Request/, 'invalid preface');\nlike(http('PRI * HTTP/2.0' . CRLF . CRLF . 'x' x 8), qr/400 Bad Request/,\n\t'invalid preface 2');\n\n}\n\n# GOAWAY on SYN_STREAM with even StreamID\n\n$s = Test::Nginx::HTTP2->new();\n$s->new_stream({ path => '/' }, 2);\n$frames = $s->read(all => [{ type => 'GOAWAY' }]);\n\n($frame) = grep { $_->{type} eq \"GOAWAY\" } @$frames;\nok($frame, 'even stream - GOAWAY frame');\nis($frame->{code}, 1, 'even stream - error code');\nis($frame->{last_sid}, 0, 'even stream - last stream');\n\n# GOAWAY on SYN_STREAM with backward StreamID\n\n# 5.1.1.  Stream Identifiers\n#   The first use of a new stream identifier implicitly closes all\n#   streams in the \"idle\" state <..> with a lower-valued stream identifier.\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/' }, 3);\n$s->read(all => [{ sid => $sid, fin => 1 }]);\n\n$sid2 = $s->new_stream({ path => '/' }, 1);\n$frames = $s->read(all => [{ type => 'GOAWAY' }]);\n\n($frame) = grep { $_->{type} eq \"GOAWAY\" } @$frames;\nok($frame, 'backward stream - GOAWAY frame');\nis($frame->{code}, 1, 'backward stream - error code');\nis($frame->{last_sid}, $sid, 'backward stream - last stream');\n\n# GOAWAY on the second SYN_STREAM with same StreamID\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/' });\n$s->read(all => [{ sid => $sid, fin => 1 }]);\n\n$sid2 = $s->new_stream({ path => '/' }, $sid);\n$frames = $s->read(all => [{ type => 'GOAWAY' }]);\n\n($frame) = grep { $_->{type} eq \"GOAWAY\" } @$frames;\nok($frame, 'dup stream - GOAWAY frame');\nis($frame->{code}, 1, 'dup stream - error code');\nis($frame->{last_sid}, $sid, 'dup stream - last stream');\n\n# aborted stream with zero HEADERS payload followed by client connection close\n\nTest::Nginx::HTTP2->new()->new_stream({ split => [ 9 ], abort => 1 });\n\n# unknown frame type\n\n$s = Test::Nginx::HTTP2->new();\n$s->h2_unknown('payload');\n$s->h2_ping('SEE-THIS');\n$frames = $s->read(all => [{ type => 'PING' }]);\n\n($frame) = grep { $_->{type} eq \"PING\" } @$frames;\nis($frame->{value}, 'SEE-THIS', 'unknown frame type');\n\n# graceful shutdown with stream waiting on HEADERS payload\n\nmy $grace = Test::Nginx::HTTP2->new(port(8087));\n$grace->new_stream({ split => [ 9 ], abort => 1 });\n\n# graceful shutdown waiting on incomplete request body DATA frames\n\nmy $grace3 = Test::Nginx::HTTP2->new(port(8087));\n$sid = $grace3->new_stream({ path => '/proxy/t2.html', body_more => 1 });\n$grace3->h2_body('TEST', { body_more => 1 });\n\n# GOAWAY without awaiting active streams, further streams ignored\n\n$s = Test::Nginx::HTTP2->new(port(8080));\n$sid = $s->new_stream({ path => '/t1.html' });\n$s->read(all => [{ sid => $sid, length => 2**16 - 1 }]);\n\n$t->reload();\n\n$frames = $s->read(all => [{ type => 'GOAWAY' }]);\n\n($frame) = grep { $_->{type} eq \"GOAWAY\" } @$frames;\nis($frame->{last_sid}, $sid, 'GOAWAY with active stream - last sid');\n\n$sid2 = $s->new_stream();\n$frames = $s->read(all => [{ sid => $sid2, fin => 0x4 }], wait => 0.5);\n\n($frame) = grep { $_->{type} eq 'HEADERS' } @$frames;\nis($frame, undef, 'GOAWAY with active stream - no new stream');\n\n$s->h2_window(100, $sid);\n$s->h2_window(100);\n$frames = $s->read(all => [{ sid => $sid, fin => 0x1 }]);\n\n@data = grep { $_->{type} eq \"DATA\" && $_->{sid} == $sid } @$frames;\n$sum = eval join '+', map { $_->{length} } @data;\nis($sum, 81, 'GOAWAY with active stream - active stream DATA after GOAWAY');\n\n# GOAWAY - force closing a connection by server with idle or active streams\n\n$s = Test::Nginx::HTTP2->new(port(8086));\n$sid = $s->new_stream();\n$s->read(all => [{ sid => $sid, fin => 1 }]);\n\nmy $active = Test::Nginx::HTTP2->new(port(8086));\n$sid = $active->new_stream({ path => '/t1.html' });\n$active->read(all => [{ sid => $sid, length => 2**16 - 1 }]);\n\n$t->stop();\n\n$frames = $s->read(all => [{ type => 'GOAWAY' }]);\n($frame) = grep { $_->{type} eq \"GOAWAY\" } @$frames;\nok($frame, 'GOAWAY on connection close - idle stream');\n\n$frames = $active->read(all => [{ type => 'GOAWAY' }]);\n($frame) = grep { $_->{type} eq \"GOAWAY\" } @$frames;\nok($frame, 'GOAWAY on connection close - active stream');\n\n###############################################################################\n\nsub gunzip_like {\n\tmy ($in, $re, $name) = @_;\n\n\tSKIP: {\n\t\teval { require IO::Uncompress::Gunzip; };\n\t\tTest::More::skip(\n\t\t\t\"IO::Uncompress::Gunzip not installed\", 1) if $@;\n\n\t\tmy $out;\n\n\t\tIO::Uncompress::Gunzip::gunzip(\\$in => \\$out);\n\n\t\tlike($out, $re, $name);\n\t}\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/h2_absolute_redirect.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for absolute_redirect directive and Location escaping.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse Test::Nginx::HTTP2;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http http_v2 proxy rewrite/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    absolute_redirect off;\n\n    server {\n        listen       127.0.0.1:8080 http2;\n        server_name  on;\n\n        absolute_redirect on;\n        error_page 400 /return301;\n\n        location / { }\n\n        location /auto/ {\n            proxy_pass http://127.0.0.1:8080;\n        }\n\n        location \"/auto sp/\" {\n            proxy_pass http://127.0.0.1:8080;\n        }\n\n        location /return301 {\n            return 301 /redirect;\n        }\n\n        location /return301/name {\n            return 301 /redirect;\n            server_name_in_redirect on;\n        }\n\n        location /return301/port {\n            return 301 /redirect;\n            port_in_redirect off;\n        }\n\n        location /i/ {\n            alias %%TESTDIR%%/;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8080 http2;\n        server_name  off;\n\n        location / { }\n\n        location /auto/ {\n            proxy_pass http://127.0.0.1:8080;\n        }\n\n        location \"/auto sp/\" {\n            proxy_pass http://127.0.0.1:8080;\n        }\n\n        location '/auto \"#%<>?\\^`{|}/' {\n            proxy_pass http://127.0.0.1:8080;\n        }\n\n        location /return301 {\n            return 301 /redirect;\n        }\n\n        location /i/ {\n            alias %%TESTDIR%%/;\n        }\n    }\n}\n\nEOF\n\nmkdir($t->testdir() . '/dir');\nmkdir($t->testdir() . '/dir sp');\n\n# suppress deprecation warning\nopen OLDERR, \">&\", \\*STDERR; close STDERR;\n$t->run()->plan(23);\nopen STDERR, \">&\", \\*OLDERR;\n\n###############################################################################\n\nmy $p = port(8080);\n\nlike(get('on', '/dir'), qr!Location: http://on:$p/dir/\\x0d?$!m, 'directory');\nlike(get('on', '/i/dir'), qr!Location: http://on:$p/i/dir/\\x0d?$!m,\n\t'directory alias');\n\nTODO: {\nlocal $TODO = 'not yet' unless $t->has_version('1.21.0');\n\nlike(get('on', '/dir%20sp'), qr!Location: http://on:$p/dir%20sp/\\x0d?$!m,\n\t'directory escaped');\nlike(get('on', '/dir%20sp?a=b'),\n\tqr!Location: http://on:$p/dir%20sp/\\?a=b\\x0d?$!m,\n\t'directory escaped args');\n\n}\n\nlike(get('on', '/auto'), qr!Location: http://on:$p/auto/\\x0d?$!m, 'auto');\nlike(get('on', '/auto?a=b'), qr!Location: http://on:$p/auto/\\?a=b\\x0d?$!m,\n\t'auto args');\n\nTODO: {\nlocal $TODO = 'not yet' unless $t->has_version('1.21.0');\n\nlike(get('on', '/auto%20sp'), qr!Location: http://on:$p/auto%20sp/\\x0d?$!m,\n\t'auto escaped');\nlike(get('on', '/auto%20sp?a=b'),\n\tqr!Location: http://on:$p/auto%20sp/\\?a=b\\x0d?$!m,\n\t'auto escaped args');\n\n}\n\nlike(get('on', '/return301'), qr!Location: http://on:$p/redirect\\x0d?$!m,\n\t'return');\n\nlike(get('host', '/return301/name'), qr!Location: http://on:$p/redirect\\x0d?!m,\n\t'server_name_in_redirect on');\nlike(get('host', '/return301'), qr!Location: http://host:$p/redirect\\x0d?$!m,\n\t'server_name_in_redirect off - using host');\nmy $ph = IO::Socket::INET->new(\"127.0.0.1:$p\")->peerhost();\nlike(get('.', '/return301'), qr!Location: http://$ph:$p/redirect\\x0d?$!m,\n\t'invalid host - using local sockaddr');\nlike(get('host', '/return301/port'), qr!Location: http://host/redirect\\x0d?$!m,\n\t'port_in_redirect off');\n\nlike(get('off', '/dir'), qr!Location: /dir/\\x0d?$!m, 'off directory');\nlike(get('off', '/i/dir'), qr!Location: /i/dir/\\x0d?$!m, 'off directory alias');\n\nTODO: {\nlocal $TODO = 'not yet' unless $t->has_version('1.21.0');\n\nlike(get('off', '/dir%20sp'), qr!Location: /dir%20sp/\\x0d?$!m,\n\t'off directory escaped');\nlike(get('off', '/dir%20sp?a=b'), qr!Location: /dir%20sp/\\?a=b\\x0d?$!m,\n\t'off directory escaped args');\n\n}\n\nlike(get('off', '/auto'), qr!Location: /auto/\\x0d?$!m, 'off auto');\nlike(get('off', '/auto?a=b'), qr!Location: /auto/\\?a=b\\x0d?$!m,\n\t'off auto args');\n\nTODO: {\nlocal $TODO = 'not yet' unless $t->has_version('1.21.0');\n\nlike(get('off', '/auto%20sp'), qr!Location: /auto%20sp/\\x0d?$!m,\n\t'auto escaped');\nlike(get('off', '/auto%20sp?a=b'), qr!Location: /auto%20sp/\\?a=b\\x0d?$!m,\n\t'auto escaped args');\n\n}\n\nlike(get('off', '/return301'), qr!Location: /redirect\\x0d?$!m, 'off return');\n\n# per RFC 3986, these characters are not allowed unescaped:\n# %00-%1F, %7F-%FF, \" \", \"\"\", \"<\", \">\", \"\\\", \"^\", \"`\", \"{\", \"|\", \"}\"\n# additionally, all characters in ESCAPE_URI: \"?\", \"%\", \"#\"\n\nSKIP: {\nskip 'win32', 1 if $^O eq 'MSWin32';\n\nTODO: {\nlocal $TODO = 'not yet' unless $t->has_version('1.21.1');\n\nlike(get('off', '/auto%20%22%23%25%3C%3E%3F%5C%5E%60%7B%7C%7D'),\n\tqr!Location: /auto%20%22%23%25%3C%3E%3F%5C%5E%60%7B%7C%7D/\\x0d?$!m,\n\t'auto escaped strict');\n\n}\n\n}\n\n###############################################################################\n\nsub get {\n\tmy ($host, $uri) = @_;\n\n\tmy $s = Test::Nginx::HTTP2->new();\n\tmy $sid = $s->new_stream({ host => $host, path => $uri });\n\tmy $frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n\tmy ($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\n\treturn 'Location: ' . $frame->{headers}->{'location'};\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/h2_auth_request.t",
    "content": "#!/usr/bin/perl\n\n# (C) Andrey Zelenkov\n# (C) Nginx, Inc.\n\n# Tests for HTTP/2 protocol with auth_request.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse Test::Nginx::HTTP2;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http http_v2 rewrite proxy auth_request/)\n\t->plan(2);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080 http2;\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        location / {\n            return 200;\n        }\n        location /auth {\n            add_header X-Body-File $request_body_file;\n            client_body_buffer_size 512;\n            auth_request /auth_request;\n            proxy_pass http://127.0.0.1:8081/auth_proxy;\n        }\n        location /auth_request {\n            proxy_pass http://127.0.0.1:8081/;\n            proxy_pass_request_body off;\n            proxy_set_header Content-Length \"\";\n        }\n        location /auth_proxy {\n            add_header X-Body $request_body;\n            proxy_pass http://127.0.0.1:8081/;\n        }\n    }\n}\n\nEOF\n\n# suppress deprecation warning\nopen OLDERR, \">&\", \\*STDERR; close STDERR;\n$t->run();\nopen STDERR, \">&\", \\*OLDERR;\n\n###############################################################################\n\nmy ($s, $sid, $frames, $frame);\n\n# second stream is used to induce body corruption issue\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/auth', method => 'POST', body => 'A' x 600 });\n$s->new_stream({ path => '/auth', method => 'POST', body => 'B' x 600 });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" && $_->{sid} == $sid } @$frames;\nis($frame->{headers}->{'x-body'}, 'A' x 600, 'auth request body');\nisnt($frame->{headers}->{'x-body-file'}, undef, 'auth request body file');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/h2_error_page.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for HTTP/2 protocol with error_page directive.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse Test::Nginx::HTTP2;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http http_v2 rewrite/)->plan(2)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080 http2;\n        server_name  localhost;\n\n        lingering_close off;\n\n        error_page 400 = /close;\n\n        location / { }\n\n        location /close {\n            return 444;\n        }\n    }\n}\n\nEOF\n\n# suppress deprecation warning\n\nopen OLDERR, \">&\", \\*STDERR; close STDERR;\n$t->run();\nopen STDERR, \">&\", \\*OLDERR;\n\n###############################################################################\n\n# tests for socket leaks with \"return 444\" in error_page\n\nmy ($sid, $frames, $frame);\n\n# make sure there is no socket leak when the request is rejected\n# due to missing mandatory \":scheme\" pseudo-header and \"return 444;\"\n# is used in error_page 400 (ticket #274)\n\nmy $s1 = Test::Nginx::HTTP2->new();\n$sid = $s1->new_stream({ headers => [\n        { name => ':method', value => 'GET' },\n        { name => ':path', value => '/' },\n        { name => ':authority', value => 'localhost' }]});\n$frames = $s1->read(all => [{ type => 'RST_STREAM' }]);\n\n($frame) = grep { $_->{type} eq \"RST_STREAM\" } @$frames;\nis($frame->{sid}, $sid, 'error 400 return 444 - missing header');\n\n# make sure there is no socket leak when the request is rejected\n# due to invalid method with lower-case letters and \"return 444;\"\n# is used in error_page 400 (ticket #2455)\n\nmy $s2 = Test::Nginx::HTTP2->new();\n$sid = $s2->new_stream({ method => 'foo' });\n$frames = $s2->read(all => [{ type => 'RST_STREAM' }]);\n\n($frame) = grep { $_->{type} eq \"RST_STREAM\" } @$frames;\nis($frame->{sid}, $sid, 'error 400 return 444 - invalid header');\n\n# while keeping $s1 and $s2, stop nginx; this should result in\n# \"open socket ... left in connection ...\" alerts if any of these\n# sockets are still open\n\n$t->stop();\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/h2_fastcgi_request_buffering.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for HTTP/2 protocol with unbuffered request body and fastcgi backend.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse Test::Nginx::HTTP2;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http http_v2 fastcgi/)->plan(48);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080 http2;\n        server_name  localhost;\n\n        location / {\n            fastcgi_request_buffering off;\n            fastcgi_pass 127.0.0.1:8081;\n            fastcgi_param REQUEST_URI $request_uri;\n            client_body_buffer_size 1k;\n        }\n    }\n}\n\nEOF\n\n# suppress deprecation warning\nopen OLDERR, \">&\", \\*STDERR; close STDERR;\n$t->run();\nopen STDERR, \">&\", \\*OLDERR;\n\n###############################################################################\n\n# unbuffered request body to fastcgi\n\nmy $f = get_body('/');\nok($f->{headers}, 'request');\nis($f->{upload}('01234', body_more => 1), '01234', 'part');\nis($f->{upload}('56789'), '56789_eos', 'part 2');\nis($f->{http_end}(), 200, 'response');\n\n$f = get_body('/');\nok($f->{headers}, 'buffer');\nis($f->{upload}('0123' x 128, body_more => 1), '0123' x 128, 'buffer - below');\nis($f->{upload}('4567' x 128, body_more => 1), '4567' x 128, 'buffer - equal');\nis($f->{upload}('89AB' x 128), '89AB' x 128 . '_eos', 'buffer - above');\nis($f->{http_end}(), 200, 'buffer - response');\n\n$f = get_body('/');\nok($f->{headers}, 'many');\nis($f->{upload}('01234many', body_split => [ 5 ], body_more => 1),\n\t'01234many', 'many - part');\nis($f->{upload}('56789many', body_split => [ 5 ]),\n\t'56789many_eos', 'many - part 2');\nis($f->{http_end}(), 200, 'many - response');\n\n$f = get_body('/');\nok($f->{headers}, 'empty');\nis($f->{upload}('', body_more => 1, wait => 0.2), '', 'empty - part');\nis($f->{upload}(''), '_eos', 'empty - part 2');\nis($f->{http_end}(), 200, 'empty - response');\n\n$f = get_body('/');\nok($f->{headers}, 'split');\nis($f->{upload}('0123456789', split => [ 14 ]), '0123456789_eos',\n\t'split - part');\nis($f->{http_end}(), 200, 'split - response');\n\n# unbuffered request body to fastcgi, content-length\n\n$f = get_body('/', 'content-length' => 10);\nok($f->{headers}, 'cl');\n\nis($f->{upload}('01234', body_more => 1), '01234', 'cl - part');\nis($f->{upload}('56789'), '56789_eos', 'cl - part 2');\nis($f->{http_end}(), 200, 'cl - response');\n\n$f = get_body('/', 'content-length' => 1536);\nok($f->{headers}, 'cl buffer');\nis($f->{upload}('0123' x 128, body_more => 1), '0123' x 128,\n\t'cl buffer - below');\nis($f->{upload}('4567' x 128, body_more => 1), '4567' x 128,\n\t'cl buffer - equal');\nis($f->{upload}('89AB' x 128), '89AB' x 128 . '_eos', 'cl buffer - above');\nis($f->{http_end}(), 200, 'cl buffer - response');\n\n$f = get_body('/', 'content-length' => 10);\nok($f->{headers}, 'cl much');\nis($f->{upload}('0123456789', body_more => 1), '0123456789', 'cl much - part');\nis($f->{upload}('many'), '', 'cl much - part 2');\nis($f->{http_end}(), 400, 'cl much - response');\n\n$f = get_body('/', 'content-length' => 10);\nok($f->{headers}, 'cl less');\nis($f->{upload}('0123', body_more => 1), '0123', 'cl less - part');\nis($f->{upload}('56789'), '', 'cl less - part 2');\nis($f->{http_end}(), 400, 'cl less - response');\n\n$f = get_body('/', 'content-length' => 18);\nok($f->{headers}, 'cl many');\nis($f->{upload}('01234many', body_split => [ 5 ], body_more => 1),\n\t'01234many', 'cl many - part');\nis($f->{upload}('56789many', body_split => [ 5 ]), '56789many_eos',\n\t'cl many - part 2');\nis($f->{http_end}(), 200, 'cl many - response');\n\n$f = get_body('/', 'content-length' => 0);\nok($f->{headers}, 'cl empty');\nis($f->{upload}('', body_more => 1, wait => 0.2), '', 'cl empty - part');\nis($f->{upload}(''), '_eos', 'cl empty - part 2');\nis($f->{http_end}(), 200, 'cl empty - response');\n\n$f = get_body('/', 'content-length' => 10);\nok($f->{headers}, 'cl split');\nis($f->{upload}('0123456789', split => [ 14 ]), '0123456789_eos', 'cl split');\nis($f->{http_end}(), 200, 'cl split - response');\n\n###############################################################################\n\n# Simple FastCGI responder implementation.\n\n# http://www.fastcgi.com/devkit/doc/fcgi-spec.html\n\nsub fastcgi_read_record($) {\n\tmy ($buf) = @_;\n\tmy $h;\n\n\treturn undef unless length $$buf;\n\n\t@{$h}{qw/ version type id clen plen /} = unpack(\"CCnnC\", $$buf);\n\n\t$h->{content} = substr $$buf, 8, $h->{clen};\n\t$h->{padding} = substr $$buf, 8 + $h->{clen}, $h->{plen};\n\n\t$$buf = substr $$buf, 8 + $h->{clen} + $h->{plen};\n\n\treturn $h;\n}\n\nsub fastcgi_respond($$$$) {\n\tmy ($socket, $version, $id, $body) = @_;\n\n\t# stdout\n\t$socket->write(pack(\"CCnnCx\", $version, 6, $id, length($body), 0));\n\t$socket->write($body);\n\n\t# close stdout\n\t$socket->write(pack(\"CCnnCx\", $version, 6, $id, 0, 0));\n\n\t# end request\n\t$socket->write(pack(\"CCnnCx\", $version, 3, $id, 8, 0));\n\t$socket->write(pack(\"NCxxx\", 0, 0));\n}\n\nsub get_body {\n\tmy ($url, %extra) = @_;\n\tmy ($server, $client, $f);\n\n\t$server = IO::Socket::INET->new(\n\t\tProto => 'tcp',\n\t\tLocalHost => '127.0.0.1',\n\t\tLocalPort => port(8081),\n\t\tListen => 5,\n\t\tTimeout => 3,\n\t\tReuse => 1\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\tmy $s = Test::Nginx::HTTP2->new();\n\tmy $sid = exists $extra{'content-length'}\n\t\t? $s->new_stream({ headers => [\n\t\t\t{ name => ':method', value => 'GET' },\n\t\t\t{ name => ':scheme', value => 'http' },\n\t\t\t{ name => ':path', value => $url, },\n\t\t\t{ name => ':authority', value => 'localhost' },\n\t\t\t{ name => 'content-length',\n\t\t\t\tvalue => $extra{'content-length'} }],\n\t\t\tbody_more => 1 })\n\t\t: $s->new_stream({ path => $url, body_more => 1 });\n\n\t$client = $server->accept() or return;\n\n\tlog2c(\"(new connection $client)\");\n\n\t$f->{headers} = backend_read($client);\n\n\tmy $h = fastcgi_read_record(\\$f->{headers});\n\tmy $version = $h->{version};\n\tmy $id = $h->{id};\n\n\t$f->{upload} = sub {\n\t\tmy ($body, %extra) = @_;\n\t\tmy $len = length($body);\n\t\tmy $wait = $extra{wait};\n\n\t\t$s->h2_body($body, { %extra });\n\n\t\t$body = '';\n\n\t\tfor (1 .. 10) {\n\t\t\tmy $buf = backend_read($client, $wait) or return '';\n\n\t\t\twhile (my $h = fastcgi_read_record(\\$buf)) {\n\n\t\t\t\t# skip everything unless stdin\n\t\t\t\tnext if $h->{type} != 5;\n\n\t\t\t\t$body .= $h->{content};\n\n\t\t\t\t# mark the end-of-stream indication\n\t\t\t\t$body .= \"_eos\" if $h->{clen} == 0;\n\t\t\t}\n\n\t\t\tlast if length($body) >= $len;\n\t\t}\n\n\t\treturn $body;\n\t};\n\t$f->{http_end} = sub {\n\t\tlocal $SIG{PIPE} = 'IGNORE';\n\n\t\tfastcgi_respond($client, $version, $id, <<EOF);\nStatus: 200 OK\nConnection: close\n\nOK\nEOF\n\n\t\t$client->close;\n\n\t\tmy $frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\t\tmy ($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\n\t\treturn $frame->{headers}->{':status'};\n\t};\n\treturn $f;\n}\n\nsub backend_read {\n\tmy ($s, $timo) = @_;\n\tmy $buf = '';\n\n\tif (IO::Select->new($s)->can_read($timo || 8)) {\n\t\t$s->sysread($buf, 16384) or return;\n\t\tlog2i($buf);\n\t}\n\treturn $buf;\n}\n\nsub log2i { Test::Nginx::log_core('|| <<', @_); }\nsub log2o { Test::Nginx::log_core('|| >>', @_); }\nsub log2c { Test::Nginx::log_core('||', @_); }\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/h2_headers.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for HTTP/2 headers.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse Test::Nginx::HTTP2;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http http_v2 proxy rewrite/)->plan(103)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080 http2;\n        listen       127.0.0.1:8081;\n        listen       127.0.0.1:8082 http2 sndbuf=128;\n        server_name  localhost;\n\n        large_client_header_buffers 2 64k;\n\n        location / {\n            add_header X-Sent-Foo $http_x_foo;\n            add_header X-Referer $http_referer;\n            return 200;\n        }\n        location /frame_size {\n            add_header X-LongHeader $arg_h;\n            add_header X-LongHeader $arg_h;\n            add_header X-LongHeader $arg_h;\n            alias %%TESTDIR%%/t2.html;\n        }\n        location /continuation {\n            add_header X-LongHeader $arg_h;\n            add_header X-LongHeader $arg_h;\n            add_header X-LongHeader $arg_h;\n            return 200 body;\n\n            location /continuation/204 {\n                return 204;\n            }\n        }\n        location /proxy/ {\n            add_header X-UC-a $upstream_cookie_a;\n            add_header X-UC-c $upstream_cookie_c;\n            proxy_pass http://127.0.0.1:8083/;\n            proxy_set_header X-Cookie-a $cookie_a;\n            proxy_set_header X-Cookie-c $cookie_c;\n        }\n        location /proxy2/ {\n            proxy_pass http://127.0.0.1:8081/;\n        }\n        location /set-cookie {\n            add_header Set-Cookie a=b;\n            add_header Set-Cookie c=d;\n            return 200;\n        }\n        location /cookie {\n            add_header X-Cookie $http_cookie;\n            add_header X-Cookie-a $cookie_a;\n            add_header X-Cookie-c $cookie_c;\n            return 200;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8084 http2;\n        server_name  localhost;\n\n        large_client_header_buffers 4 512;\n    }\n\n    server {\n        listen       127.0.0.1:8085 http2;\n        server_name  localhost;\n\n        large_client_header_buffers 1 512;\n    }\n\n    server {\n        listen       127.0.0.1:8086 http2;\n        server_name  localhost;\n\n        underscores_in_headers on;\n        add_header X-Sent-Foo $http_x_foo always;\n    }\n\n    server {\n        listen       127.0.0.1:8087 http2;\n        server_name  localhost;\n\n        ignore_invalid_headers off;\n        add_header X-Sent-Foo $http_x_foo always;\n    }\n}\n\nEOF\n\n$t->run_daemon(\\&http_daemon);\n# suppress deprecation warning\nopen OLDERR, \">&\", \\*STDERR; close STDERR;\n$t->run();\nopen STDERR, \">&\", \\*OLDERR;\n$t->waitforsocket('127.0.0.1:' . port(8083));\n\n# file size is slightly beyond initial window size: 2**16 + 80 bytes\n\n$t->write_file('t1.html',\n\tjoin('', map { sprintf \"X%04dXXX\", $_ } (1 .. 8202)));\n\n$t->write_file('t2.html', 'SEE-THIS');\n\n###############################################################################\n\n# 6.1. Indexed Header Field Representation\n\nmy $s = Test::Nginx::HTTP2->new();\nmy $sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/', mode => 0 },\n\t{ name => ':authority', value => 'localhost', mode => 1 }]});\nmy $frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\nmy ($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 200, 'indexed header field');\n\n# 6.2.1. Literal Header Field with Incremental Indexing\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 1, huff => 0 },\n\t{ name => ':scheme', value => 'http', mode => 1, huff => 0 },\n\t{ name => ':path', value => '/', mode => 1, huff => 0 },\n\t{ name => ':authority', value => 'localhost', mode => 1, huff => 0 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 200, 'literal with indexing');\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 1, huff => 1 },\n\t{ name => ':scheme', value => 'http', mode => 1, huff => 1 },\n\t{ name => ':path', value => '/', mode => 1, huff => 1 },\n\t{ name => ':authority', value => 'localhost', mode => 1, huff => 1 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 200, 'literal with indexing - huffman');\n\n# 6.2.1. Literal Header Field with Incremental Indexing -- New Name\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 2, huff => 0 },\n\t{ name => ':scheme', value => 'http', mode => 2, huff => 0 },\n\t{ name => ':path', value => '/', mode => 2, huff => 0 },\n\t{ name => ':authority', value => 'localhost', mode => 2, huff => 0 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 200, 'literal with indexing - new');\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 2, huff => 1 },\n\t{ name => ':scheme', value => 'http', mode => 2, huff => 1 },\n\t{ name => ':path', value => '/', mode => 2, huff => 1 },\n\t{ name => ':authority', value => 'localhost', mode => 2, huff => 1 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 200, 'literal with indexing - new huffman');\n\n# 6.2.2. Literal Header Field without Indexing\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 3, huff => 0 },\n\t{ name => ':scheme', value => 'http', mode => 3, huff => 0 },\n\t{ name => ':path', value => '/', mode => 3, huff => 0 },\n\t{ name => ':authority', value => 'localhost', mode => 3, huff => 0 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 200, 'literal without indexing');\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 3, huff => 1 },\n\t{ name => ':scheme', value => 'http', mode => 3, huff => 1 },\n\t{ name => ':path', value => '/', mode => 3, huff => 1 },\n\t{ name => ':authority', value => 'localhost', mode => 3, huff => 1 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 200, 'literal without indexing - huffman');\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 3, huff => 0 },\n\t{ name => ':scheme', value => 'http', mode => 3, huff => 0 },\n\t{ name => ':path', value => '/', mode => 3, huff => 0 },\n\t{ name => ':authority', value => 'localhost', mode => 3, huff => 0 },\n\t{ name => 'referer', value => 'foo', mode => 3, huff => 0 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 200,\n\t'literal without indexing - multibyte index');\nis($frame->{headers}->{'x-referer'}, 'foo',\n\t'literal without indexing - multibyte index value');\n\n# 6.2.2. Literal Header Field without Indexing -- New Name\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 4, huff => 0 },\n\t{ name => ':scheme', value => 'http', mode => 4, huff => 0 },\n\t{ name => ':path', value => '/', mode => 4, huff => 0 },\n\t{ name => ':authority', value => 'localhost', mode => 4, huff => 0 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 200, 'literal without indexing - new');\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 4, huff => 1 },\n\t{ name => ':scheme', value => 'http', mode => 4, huff => 1 },\n\t{ name => ':path', value => '/', mode => 4, huff => 1 },\n\t{ name => ':authority', value => 'localhost', mode => 4, huff => 1 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 200,\n\t'literal without indexing - new huffman');\n\n# 6.2.3. Literal Header Field Never Indexed\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 5, huff => 0 },\n\t{ name => ':scheme', value => 'http', mode => 5, huff => 0 },\n\t{ name => ':path', value => '/', mode => 5, huff => 0 },\n\t{ name => ':authority', value => 'localhost', mode => 5, huff => 0 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 200, 'literal never indexed');\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 5, huff => 1 },\n\t{ name => ':scheme', value => 'http', mode => 5, huff => 1 },\n\t{ name => ':path', value => '/', mode => 5, huff => 1 },\n\t{ name => ':authority', value => 'localhost', mode => 5, huff => 1 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 200, 'literal never indexed - huffman');\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 5, huff => 0 },\n\t{ name => ':scheme', value => 'http', mode => 5, huff => 0 },\n\t{ name => ':path', value => '/', mode => 5, huff => 0 },\n\t{ name => ':authority', value => 'localhost', mode => 5, huff => 0 },\n\t{ name => 'referer', value => 'foo', mode => 5, huff => 0 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 200,\n\t'literal never indexed - multibyte index');\nis($frame->{headers}->{'x-referer'}, 'foo',\n\t'literal never indexed - multibyte index value');\n\n# 6.2.3. Literal Header Field Never Indexed -- New Name\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 6, huff => 0 },\n\t{ name => ':scheme', value => 'http', mode => 6, huff => 0 },\n\t{ name => ':path', value => '/', mode => 6, huff => 0 },\n\t{ name => ':authority', value => 'localhost', mode => 6, huff => 0 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 200, 'literal never indexed - new');\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 6, huff => 1 },\n\t{ name => ':scheme', value => 'http', mode => 6, huff => 1 },\n\t{ name => ':path', value => '/', mode => 6, huff => 1 },\n\t{ name => ':authority', value => 'localhost', mode => 6, huff => 1 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 200, 'literal never indexed - new huffman');\n\n# reuse literal with multibyte indexing\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/', mode => 0 },\n\t{ name => ':authority', value => 'localhost', mode => 1 },\n\t{ name => 'referer', value => 'foo', mode => 1 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{'x-referer'}, 'foo', 'value with indexing - new');\n\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/', mode => 0 },\n\t{ name => ':authority', value => 'localhost', mode => 0 },\n\t{ name => 'referer', value => 'foo', mode => 0 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{'x-referer'}, 'foo', 'value with indexing - indexed');\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/', mode => 0 },\n\t{ name => ':authority', value => 'localhost', mode => 1 },\n\t{ name => 'x-foo', value => 'X-Bar', mode => 2 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{'x-sent-foo'}, 'X-Bar', 'name with indexing - new');\n\n# reuse literal with multibyte indexing - reused name\n\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/', mode => 0 },\n\t{ name => ':authority', value => 'localhost', mode => 0 },\n\t{ name => 'x-foo', value => 'X-Bar', mode => 0 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{'x-sent-foo'}, 'X-Bar', 'name with indexing - indexed');\n\n# reuse literal with multibyte indexing - reused name only\n\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/', mode => 0 },\n\t{ name => ':authority', value => 'localhost', mode => 0 },\n\t{ name => 'x-foo', value => 'X-Baz', mode => 1 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{'x-sent-foo'}, 'X-Baz',\n\t'name with indexing - indexed name');\n\n# response header field with characters not suitable for huffman encoding\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/', mode => 0 },\n\t{ name => ':authority', value => 'localhost', mode => 1 },\n\t{ name => 'x-foo', value => '{{{{{', mode => 2 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{'x-sent-foo'}, '{{{{{', 'rare chars');\nlike($s->{headers}, qr/\\Q{{{{{/, 'rare chars - no huffman encoding');\n\n# response header field with huffman encoding\n# NB: implementation detail, not obligated\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/', mode => 0 },\n\t{ name => ':authority', value => 'localhost', mode => 1 },\n\t{ name => 'x-foo', value => 'aaaaa', mode => 2 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{'x-sent-foo'}, 'aaaaa', 'well known chars');\nunlike($s->{headers}, qr/aaaaa/, 'well known chars - huffman encoding');\n\n# response header field with huffman encoding - complete table mod \\0, CR, LF\n# first saturate with short-encoded characters (NB: implementation detail)\n\nmy $field = pack \"C*\", ((map { 97 } (1 .. 862)), 1 .. 9, 11, 12, 14 .. 255);\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/', mode => 0 },\n\t{ name => ':authority', value => 'localhost', mode => 1 },\n\t{ name => 'x-foo', value => $field, mode => 2 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{'x-sent-foo'}, $field, 'all chars');\nunlike($s->{headers}, qr/abcde/, 'all chars - huffman encoding');\n\n# 6.3.  Dynamic Table Size Update\n\n# remove some indexed headers from the dynamic table\n# by maintaining dynamic table space only for index 0\n# 'x-foo' has index 0, and 'referer' has index 1\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/', mode => 0 },\n\t{ name => ':authority', value => 'localhost', mode => 1 },\n\t{ name => 'referer', value => 'foo', mode => 1 },\n\t{ name => 'x-foo', value => 'X-Bar', mode => 2 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n$sid = $s->new_stream({ table_size => 61, headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/', mode => 0 },\n\t{ name => 'x-foo', value => 'X-Bar', mode => 0 },\n\t{ name => ':authority', value => 'localhost', mode => 1 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nisnt($frame, undef, 'updated table size - remaining index');\n\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/', mode => 0 },\n\t{ name => ':authority', value => 'localhost', mode => 1 },\n\t{ name => 'referer', value => 'foo', mode => 0 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"GOAWAY\" } @$frames;\nis($frame->{code}, 0x9, 'invalid index');\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/', mode => 0 },\n\t{ name => ':authority', value => 'localhost', mode => 1 },\n\t{ name => 'unknown', value => 'foo', mode => 3 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"GOAWAY\" } @$frames;\nis($frame->{code}, 0x9, 'invalid index in literal header field');\n\n# 5.4.1.  Connection Error Handling\n#   An endpoint that encounters a connection error SHOULD first send a\n#   GOAWAY frame <..>\n\n($frame) = grep { $_->{type} eq \"GOAWAY\" } @$frames;\nok($frame, 'invalid index - GOAWAY');\n\n# RFC 7541, 2.3.3.  Index Address Space\n#   Indices strictly greater than the sum of the lengths of both tables\n#   MUST be treated as a decoding error.\n\n# 4.3.  Header Compression and Decompression\n#   A decoding error in a header block MUST be treated\n#   as a connection error of type COMPRESSION_ERROR.\n\nis($frame->{last_sid}, $sid, 'invalid index - GOAWAY last stream');\nis($frame->{code}, 9, 'invalid index - GOAWAY COMPRESSION_ERROR');\n\n# HPACK zero index\n\n# RFC 7541, 6.1  Indexed Header Field Representation\n#   The index value of 0 is not used.  It MUST be treated as a decoding\n#   error if found in an indexed header field representation.\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/', mode => 0 },\n\t{ name => ':authority', value => 'localhost', mode => 1 },\n\t{ name => '', value => '', mode => 0 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\nok($frame, 'zero index - GOAWAY');\nis($frame->{code}, 9, 'zero index - GOAWAY COMPRESSION_ERROR');\n\n# invalid table size update\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ table_size => 4097, headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/', mode => 0 },\n\t{ name => 'x-foo', value => 'X-Bar', mode => 0 },\n\t{ name => ':authority', value => 'localhost', mode => 1 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"GOAWAY\" } @$frames;\nok($frame, 'invalid table size - GOAWAY');\nis($frame->{last_sid}, $sid, 'invalid table size - GOAWAY last stream');\nis($frame->{code}, 9, 'invalid table size - GOAWAY COMPRESSION_ERROR');\n\n# request header field with multiple values\n\n# 8.1.2.5.  Compressing the Cookie Header Field\n#   To allow for better compression efficiency, the Cookie header field\n#   MAY be split into separate header fields <..>.\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/cookie', mode => 2 },\n\t{ name => ':authority', value => 'localhost', mode => 1 },\n\t{ name => 'cookie', value => 'a=b', mode => 2},\n\t{ name => 'cookie', value => 'c=d', mode => 2}]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{'x-cookie-a'}, 'b',\n\t'multiple request header fields - cookie');\nis($frame->{headers}->{'x-cookie-c'}, 'd',\n\t'multiple request header fields - cookie 2');\nis($frame->{headers}->{'x-cookie'}, 'a=b; c=d',\n\t'multiple request header fields - semi-colon');\n\n# request header field with multiple values to HTTP backend\n\n# 8.1.2.5.  Compressing the Cookie Header Field\n#   these MUST be concatenated into a single octet string\n#   using the two-octet delimiter of 0x3B, 0x20 (the ASCII string \"; \")\n#   before being passed into a non-HTTP/2 context, such as an HTTP/1.1\n#   connection <..>\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/proxy/cookie', mode => 2 },\n\t{ name => ':authority', value => 'localhost', mode => 1 },\n\t{ name => 'cookie', value => 'a=b', mode => 2 },\n\t{ name => 'cookie', value => 'c=d', mode => 2 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{'x-sent-cookie'}, 'a=b; c=d',\n\t'multiple request header fields proxied - semi-colon');\nis($frame->{headers}->{'x-sent-cookie2'}, '',\n\t'multiple request header fields proxied - dublicate cookie');\nis($frame->{headers}->{'x-sent-cookie-a'}, 'b',\n\t'multiple request header fields proxied - cookie 1');\nis($frame->{headers}->{'x-sent-cookie-c'}, 'd',\n\t'multiple request header fields proxied - cookie 2');\n\n# response header field with multiple values\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/set-cookie' });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{'set-cookie'}[0], 'a=b',\n\t'multiple response header fields - cookie');\nis($frame->{headers}->{'set-cookie'}[1], 'c=d',\n\t'multiple response header fields - cookie 2');\n\n# response header field with multiple values from HTTP backend\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/proxy/set-cookie' });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{'set-cookie'}[0], 'a=b',\n\t'multiple response header proxied - cookie');\nis($frame->{headers}->{'set-cookie'}[1], 'c=d',\n\t'multiple response header proxied - cookie 2');\nis($frame->{headers}->{'x-uc-a'}, 'b',\n\t'multiple response header proxied - upstream cookie');\nis($frame->{headers}->{'x-uc-c'}, 'd',\n\t'multiple response header proxied - upstream cookie 2');\n\n# CONTINUATION in response\n# put three long header fields (not less than SETTINGS_MAX_FRAME_SIZE/2)\n# to break header block into separate frames, one such field per frame\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/continuation?h=' . 'x' x 2**13 });\n\n$frames = $s->read(all => [{ sid => $sid, fin => 0x4 }]);\nmy @data = grep { $_->{type} =~ \"HEADERS|CONTINUATION\" } @$frames;\nmy $data = $data[-1];\nis(@{$data->{headers}{'x-longheader'}}, 3,\n\t'response CONTINUATION - headers');\nis($data->{headers}{'x-longheader'}[0], 'x' x 2**13,\n\t'response CONTINUATION - header 1');\nis($data->{headers}{'x-longheader'}[1], 'x' x 2**13,\n\t'response CONTINUATION - header 2');\nis($data->{headers}{'x-longheader'}[2], 'x' x 2**13,\n\t'response CONTINUATION - header 3');\n@data = sort { $a <=> $b } map { $_->{length} } @data;\ncmp_ok($data[-1], '<=', 2**14, 'response CONTINUATION - max frame size');\n\n# same but without response DATA frames\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/continuation/204?h=' . 'x' x 2**13 });\n\n$frames = $s->read(all => [{ sid => $sid, fin => 0x4 }]);\n@data = grep { $_->{type} =~ \"HEADERS|CONTINUATION\" } @$frames;\n$data = $data[-1];\nis(@{$data->{headers}{'x-longheader'}}, 3,\n\t'no body CONTINUATION - headers');\nis($data->{headers}{'x-longheader'}[0], 'x' x 2**13,\n\t'no body CONTINUATION - header 1');\nis($data->{headers}{'x-longheader'}[1], 'x' x 2**13,\n\t'no body CONTINUATION - header 2');\nis($data->{headers}{'x-longheader'}[2], 'x' x 2**13,\n\t'no body CONTINUATION - header 3');\n@data = sort { $a <=> $b } map { $_->{length} } @data;\ncmp_ok($data[-1], '<=', 2**14, 'no body CONTINUATION - max frame size');\n\n# response header block is always split by SETTINGS_MAX_FRAME_SIZE\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/continuation?h=' . 'x' x 2**15 });\n\n$frames = $s->read(all => [{ sid => $sid, fin => 0x4 }]);\n@data = grep { $_->{type} =~ \"HEADERS|CONTINUATION\" } @$frames;\n@data = sort { $a <=> $b } map { $_->{length} } @data;\ncmp_ok($data[-1], '<=', 2**14, 'response header frames limited');\n\n# response header frame sent in parts\n\n$s = Test::Nginx::HTTP2->new(port(8082));\n$s->h2_settings(0, 0x5 => 2**17);\n\n$sid = $s->new_stream({ path => '/frame_size?h=' . 'x' x 2**15 });\n$frames = $s->read(all => [{ sid => $sid, fin => 0x4 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nok($frame, 'response header - parts');\n\nSKIP: {\nskip 'response header failed', 1 unless $frame;\n\nis(length join('', @{$frame->{headers}->{'x-longheader'}}), 98304,\n\t'response header - headers');\n\n}\n\n# response header block split and sent in parts\n\n$s = Test::Nginx::HTTP2->new(port(8082));\n$sid = $s->new_stream({ path => '/continuation?h=' . 'x' x 2**15 });\n$frames = $s->read(all => [{ sid => $sid, fin => 0x4 }]);\n\n@data = grep { $_->{type} =~ \"HEADERS|CONTINUATION\" } @$frames;\nok(@data, 'response header split');\n\nSKIP: {\nskip 'response header split failed', 2 unless @data;\n\nmy ($lengths) = sort { $b <=> $a } map { $_->{length} } @data;\ncmp_ok($lengths, '<=', 16384, 'response header split - max size');\n\nis(length join('', @{$data[-1]->{headers}->{'x-longheader'}}), 98304,\n\t'response header split - headers');\n\n}\n\n# max_field_size - header field name\n\n$s = Test::Nginx::HTTP2->new(port(8084));\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/t2.html', mode => 1 },\n\t{ name => ':authority', value => 'localhost', mode => 1 },\n\t{ name => 'x' x 511, value => 'value', mode => 2 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq 'DATA' } @$frames;\nok($frame, 'field name size less');\n\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/t2.html', mode => 1 },\n\t{ name => ':authority', value => 'localhost', mode => 1 },\n\t{ name => 'x' x 511, value => 'value', mode => 2 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq 'DATA' } @$frames;\nok($frame, 'field name size second');\n\n$s = Test::Nginx::HTTP2->new(port(8084));\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/t2.html', mode => 1 },\n\t{ name => ':authority', value => 'localhost', mode => 1 },\n\t{ name => 'x' x 512, value => 'value', mode => 2 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq 'DATA' } @$frames;\nok($frame, 'field name size equal');\n\n$s = Test::Nginx::HTTP2->new(port(8084));\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/t2.html', mode => 1 },\n\t{ name => ':authority', value => 'localhost', mode => 1 },\n\t{ name =>  'x' x 513, value => 'value', mode => 2 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq 'DATA' } @$frames;\nis($frame, undef, 'field name size greater');\n\n# max_field_size - header field value\n\n$s = Test::Nginx::HTTP2->new(port(8084));\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/t2.html', mode => 1 },\n\t{ name => ':authority', value => 'localhost', mode => 1 },\n\t{ name => 'name', value => 'x' x 511, mode => 2 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq 'DATA' } @$frames;\nok($frame, 'field value size less');\n\n$s = Test::Nginx::HTTP2->new(port(8084));\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/t2.html', mode => 1 },\n\t{ name => ':authority', value => 'localhost', mode => 1 },\n\t{ name => 'name', value => 'x' x 511, mode => 2 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq 'DATA' } @$frames;\nok($frame, 'field value size equal');\n\n$s = Test::Nginx::HTTP2->new(port(8084));\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/t2.html', mode => 1 },\n\t{ name => ':authority', value => 'localhost', mode => 1 },\n\t{ name => 'name', value => 'x' x 513, mode => 2 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq 'DATA' } @$frames;\nis($frame, undef, 'field value size greater');\n\n# max_header_size\n\n$s = Test::Nginx::HTTP2->new(port(8085));\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/t2.html', mode => 1 },\n\t{ name => ':authority', value => 'localhost', mode => 1 },\n\t{ name => 'longname', value => 'x' x 450, mode => 2 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq 'DATA' } @$frames;\nok($frame, 'header size less');\n\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/t2.html', mode => 1 },\n\t{ name => ':authority', value => 'localhost', mode => 1 },\n\t{ name => 'longname', value => 'x' x 450, mode => 2 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq 'DATA' } @$frames;\nok($frame, 'header size second');\n\n$s = Test::Nginx::HTTP2->new(port(8085));\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/t2.html', mode => 1 },\n\t{ name => ':authority', value => 'localhost', mode => 1 },\n\t{ name => 'longname', value => 'x' x 451, mode => 2 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq 'DATA' } @$frames;\nok($frame, 'header size equal');\n\n$s = Test::Nginx::HTTP2->new(port(8085));\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/t2.html', mode => 1 },\n\t{ name => ':authority', value => 'localhost', mode => 1 },\n\t{ name => 'longname', value => 'x' x 452, mode => 2 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq 'DATA' } @$frames;\nis($frame, undef, 'header size greater');\n\n# header size is based on (decompressed) header list\n# two extra 1-byte indices would otherwise fit in max_header_size\n\n$s = Test::Nginx::HTTP2->new(port(8085));\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/t2.html', mode => 1 },\n\t{ name => ':authority', value => 'localhost', mode => 1 },\n\t{ name => 'longname', value => 'x' x 400, mode => 2 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq 'DATA' } @$frames;\nok($frame, 'header size new index');\n\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/t2.html', mode => 1 },\n\t{ name => ':authority', value => 'localhost', mode => 1 },\n\t{ name => 'longname', value => 'x' x 400, mode => 0 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq 'DATA' } @$frames;\nok($frame, 'header size indexed');\n\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/t2.html', mode => 1 },\n\t{ name => ':authority', value => 'localhost', mode => 1 },\n\t{ name => 'longname', value => 'x' x 400, mode => 0 },\n\t{ name => 'longname', value => 'x' x 400, mode => 0 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq 'GOAWAY' } @$frames;\nis($frame->{code}, 0xb, 'header size indexed greater');\n\n# HPACK table boundary\n\n$s = Test::Nginx::HTTP2->new();\n$s->read(all => [{ sid => $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/', mode => 0 },\n\t{ name => ':authority', value => '', mode => 0 },\n\t{ name => 'x' x 2016, value => 'x' x 2048, mode => 2 }]}), fin => 1 }]);\n$frames = $s->read(all => [{ sid => $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/', mode => 0 },\n\t{ name => ':authority', value => '', mode => 0 },\n\t{ name => 'x' x 2016, value => 'x' x 2048, mode => 0 }]}), fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nok($frame, 'HPACK table boundary');\n\n$s->read(all => [{ sid => $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/', mode => 0 },\n\t{ name => ':authority', value => '', mode => 0 },\n\t{ name => 'x' x 33, value => 'x' x 4031, mode => 2 }]}), fin => 1 }]);\n$frames = $s->read(all => [{ sid => $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/', mode => 0 },\n\t{ name => ':authority', value => '', mode => 0 },\n\t{ name => 'x' x 33, value => 'x' x 4031, mode => 0 }]}), fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nok($frame, 'HPACK table boundary - header field name');\n\n$s->read(all => [{ sid => $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/', mode => 0 },\n\t{ name => ':authority', value => '', mode => 0 },\n\t{ name => 'x', value => 'x' x 64, mode => 2 }]}), fin => 1 }]);\n$frames = $s->read(all => [{ sid => $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/', mode => 0 },\n\t{ name => ':authority', value => '', mode => 0 },\n\t{ name => 'x', value => 'x' x 64, mode => 0 }]}), fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nok($frame, 'HPACK table boundary - header field value');\n\n# ensure that request header field value with newline doesn't get split\n#\n# 10.3.  Intermediary Encapsulation Attacks\n#   Any request or response that contains a character not permitted\n#   in a header field value MUST be treated as malformed.\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/proxy2/', mode => 1 },\n\t{ name => ':authority', value => 'localhost', mode => 1 },\n\t{ name => 'x-foo', value => \"x-bar\\r\\nreferer:see-this\", mode => 2 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n# 10.3.  Intermediary Encapsulation Attacks\n#   An intermediary therefore cannot translate an HTTP/2 request or response\n#   containing an invalid field name into an HTTP/1.1 message.\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nisnt($frame->{headers}->{'x-referer'}, 'see-this', 'newline in request header');\n\nTODO: {\nlocal $TODO = 'not yet' unless $t->has_version('1.23.4');\n\nis($frame->{headers}->{':status'}, 400, 'newline in request header - bad request');\n\n}\n\n# invalid header name as seen with underscore should not lead to ignoring rest\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/', mode => 0 },\n\t{ name => ':authority', value => 'localhost', mode => 1 },\n\t{ name => 'x_foo', value => \"x-bar\", mode => 2 },\n\t{ name => 'referer', value => \"see-this\", mode => 1 }]});\n$frames = $s->read(all => [{ type => 'HEADERS' }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{'x-referer'}, 'see-this', 'after invalid header name');\n\n# other invalid header name characters as seen with ':'\nTODO: {\nlocal $TODO = 'not yet' unless $t->has_version('1.23.4');\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/', mode => 0 },\n\t{ name => ':authority', value => 'localhost', mode => 1 },\n\t{ name => 'x:foo', value => \"x-bar\", mode => 2 },\n\t{ name => 'referer', value => \"see-this\", mode => 1 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 400, 'colon in header name');\n\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/', mode => 0 },\n\t{ name => ':authority', value => 'localhost', mode => 1 },\n\t{ name => 'x foo', value => \"bar\", mode => 2 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 400, 'space in header name');\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/', mode => 0 },\n\t{ name => ':authority', value => 'localhost', mode => 1 },\n\t{ name => \"foo\\x02\", value => \"bar\", mode => 2 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 400, 'control in header name');\n\n}\n\n# header name with underscore - underscores_in_headers on\n\n$s = Test::Nginx::HTTP2->new(port(8086));\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/', mode => 0 },\n\t{ name => ':authority', value => 'localhost', mode => 1 },\n\t{ name => 'x_foo', value => \"x-bar\", mode => 2 },\n\t{ name => 'referer', value => \"see-this\", mode => 1 }]});\n$frames = $s->read(all => [{ type => 'HEADERS' }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{'x-sent-foo'}, 'x-bar',\n\t'underscore in header name - underscores_in_headers');\n\n# header name with underscore - ignore_invalid_headers off\n\n$s = Test::Nginx::HTTP2->new(port(8087));\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/', mode => 0 },\n\t{ name => ':authority', value => 'localhost', mode => 1 },\n\t{ name => 'x_foo', value => \"x-bar\", mode => 2 },\n\t{ name => 'referer', value => \"see-this\", mode => 1 }]});\n$frames = $s->read(all => [{ type => 'HEADERS' }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{'x-sent-foo'}, 'x-bar',\n\t'underscore in header name - ignore_invalid_headers');\n\n# missing mandatory request header ':scheme'\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':path', value => '/', mode => 0 },\n\t{ name => ':authority', value => 'localhost', mode => 1 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 400, 'incomplete headers');\n\n# empty request header ':authority'\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/', mode => 0 },\n\t{ name => ':authority', value => '', mode => 0 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 400, 'empty authority');\n\n# client sent invalid :path header\n\n$sid = $s->new_stream({ path => 't1.html' });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 400, 'invalid path');\n\nTODO: {\nlocal $TODO = 'not yet' unless $t->has_version('1.21.1');\n\n$sid = $s->new_stream({ path => \"/t1.html\\x02\" });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 400, 'invalid path control');\n\n}\n\n# ngx_http_v2_parse_int() error handling\n\n# NGX_ERROR\n\n$s = Test::Nginx::HTTP2->new();\n{\n\tlocal $SIG{PIPE} = 'IGNORE';\n\tsyswrite($s->{socket}, pack(\"x2C3NC\", 1, 0x1, 5, 1, 0xff));\n}\n$frames = $s->read(all => [{ type => \"GOAWAY\" }]);\n\n($frame) = grep { $_->{type} eq 'GOAWAY' } @$frames;\nis($frame->{code}, 0x6, 'invalid index length');\n\n$s = Test::Nginx::HTTP2->new();\n{\n\tlocal $SIG{PIPE} = 'IGNORE';\n\tsyswrite($s->{socket}, pack(\"x2C3NC2\", 2, 0x1, 5, 1, 0x42, 0xff));\n}\n$frames = $s->read(all => [{ type => \"GOAWAY\" }]);\n\n($frame) = grep { $_->{type} eq 'GOAWAY' } @$frames;\nis($frame->{code}, 0x6, 'invalid literal length');\n\n# NGX_DECLINED\n\n$s = Test::Nginx::HTTP2->new();\n{\n\tlocal $SIG{PIPE} = 'IGNORE';\n\tsyswrite($s->{socket}, pack(\"x2C3NN\", 5, 0x1, 5, 1, 0xffffffff));\n}\n$frames = $s->read(all => [{ type => \"GOAWAY\" }]);\n\n($frame) = grep { $_->{type} eq 'GOAWAY' } @$frames;\nis($frame->{code}, 0x9, 'too long index');\n\n$s = Test::Nginx::HTTP2->new();\n{\n\tlocal $SIG{PIPE} = 'IGNORE';\n\tsyswrite($s->{socket}, pack(\"x2C3NCN\", 6, 0x1, 5, 1, 0x42, 0xffffffff));\n}\n$frames = $s->read(all => [{ type => \"GOAWAY\" }]);\n\n($frame) = grep { $_->{type} eq 'GOAWAY' } @$frames;\nis($frame->{code}, 0x9, 'too long literal');\n\n# NGX_AGAIN\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ split => [35], split_delay => 1.1, headers => [\n\t{ name => ':method', value => 'GET', mode => 3, huff => 0 },\n\t{ name => ':scheme', value => 'http', mode => 3, huff => 0 },\n\t{ name => ':path', value => '/', mode => 3, huff => 0 },\n\t{ name => ':authority', value => 'localhost', mode => 3, huff => 0 },\n\t{ name => 'referer', value => 'foo', mode => 3, huff => 0 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{'x-referer'}, 'foo', 'header split index');\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ split => [37], split_delay => 1.1, headers => [\n\t{ name => ':method', value => 'GET', mode => 3, huff => 0 },\n\t{ name => ':scheme', value => 'http', mode => 3, huff => 0 },\n\t{ name => ':path', value => '/', mode => 3, huff => 0 },\n\t{ name => ':authority', value => 'localhost', mode => 3, huff => 0 },\n\t{ name => 'referer', value => '1234' x 32, mode => 3, huff => 0 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{'x-referer'}, '1234' x 32, 'header split field length');\n\n###############################################################################\n\nsub http_daemon {\n\tmy $server = IO::Socket::INET->new(\n\t\tProto => 'tcp',\n\t\tLocalHost => '127.0.0.1',\n\t\tLocalPort => port(8083),\n\t\tListen => 5,\n\t\tReuse => 1\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\tlocal $SIG{PIPE} = 'IGNORE';\n\n\twhile (my $client = $server->accept()) {\n\t\t$client->autoflush(1);\n\n\t\tmy $headers = '';\n\t\tmy $uri = '';\n\n\t\twhile (<$client>) {\n\t\t\t$headers .= $_;\n\t\t\tlast if (/^\\x0d?\\x0a?$/);\n\t\t}\n\n\t\tnext if $headers eq '';\n\t\t$uri = $1 if $headers =~ /^\\S+\\s+([^ ]+)\\s+HTTP/i;\n\n\t\tif ($uri eq '/cookie') {\n\n\t\t\tmy ($cookie, $cookie2) = $headers =~ /Cookie: (.+)/ig;\n\t\t\t$cookie2 = '' unless defined $cookie2;\n\n\t\t\tmy ($cookie_a, $cookie_c) = ('', '');\n\t\t\t$cookie_a = $1 if $headers =~ /X-Cookie-a: (.+)/i;\n\t\t\t$cookie_c = $1 if $headers =~ /X-Cookie-c: (.+)/i;\n\n\t\t\tprint $client <<EOF;\nHTTP/1.1 200 OK\nConnection: close\nX-Sent-Cookie: $cookie\nX-Sent-Cookie2: $cookie2\nX-Sent-Cookie-a: $cookie_a\nX-Sent-Cookie-c: $cookie_c\n\nEOF\n\n\t\t} elsif ($uri eq '/set-cookie') {\n\n\t\t\tprint $client <<EOF;\nHTTP/1.1 200 OK\nConnection: close\nSet-Cookie: a=b\nSet-Cookie: c=d\n\nEOF\n\n\t\t}\n\n\t} continue {\n\t\tclose $client;\n\t}\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/h2_keepalive.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for HTTP/2 protocol, keepalive directives.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse Socket qw(SOL_SOCKET SO_RCVBUF);\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse Test::Nginx::HTTP2;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http http_v2/)->plan(19)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080 http2 sndbuf=1m;\n        server_name  localhost;\n\n        keepalive_requests 2;\n\n        location / { }\n    }\n\n    server {\n        listen       127.0.0.1:8081 http2;\n        server_name  localhost;\n\n        keepalive_timeout 0;\n\n        location / { }\n    }\n\n    server {\n        listen       127.0.0.1:8082 http2;\n        server_name  localhost;\n\n        keepalive_time 1s;\n\n        add_header X-Conn $connection_requests:$connection_time;\n\n        location / { }\n    }\n}\n\nEOF\n\n$t->write_file('index.html', 'SEE-THAT' x 50000);\n$t->write_file('t.html', 'SEE-THAT');\n\n# suppress deprecation warning\nopen OLDERR, \">&\", \\*STDERR; close STDERR;\n$t->run();\nopen STDERR, \">&\", \\*OLDERR;\n\n###############################################################################\n\nmy $s = Test::Nginx::HTTP2->new();\n\n# to test lingering close, let full response settle down in send buffer space\n# so that client additional data past server-side close would trigger TCP RST\n\n$s->{socket}->setsockopt(SOL_SOCKET, SO_RCVBUF, 64*1024) or die $!;\n$s->h2_settings(0, 0x4 => 2**20);\n$s->h2_window(2**21);\n\nmy $frames = $s->read(all => [{ sid => $s->new_stream(), fin => 1 }]);\n\nmy ($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 200, 'max requests');\n\n$frames = $s->read(all => [{ type => 'GOAWAY' }], wait => 0.5)\n\tunless grep { $_->{type} eq \"GOAWAY\" } @$frames;\n\n($frame) = grep { $_->{type} eq \"GOAWAY\" } @$frames;\nis($frame, undef, 'max requests - GOAWAY');\n\n# max requests limited\n\nmy $sid = $s->new_stream();\n\n# wait server to finish and close socket if lingering close were disabled\n\nselect undef, undef, undef, 0.1;\n$s->h2_ping(\"SEE-THIS\");\n\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }, { type => 'GOAWAY' }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 200, 'max requests limited');\n\nmy @data = grep { $_->{type} eq \"DATA\" } @$frames;\nmy $sum = eval join '+', map { $_->{length} } @data;\nis($sum, 400000, 'max requests limited - all data received');\n\n($frame) = grep { $_->{type} eq \"GOAWAY\" } @$frames;\nok($frame, 'max requests limited - GOAWAY');\nis($frame->{last_sid}, $sid, 'max requests limited - GOAWAY last stream');\n\n# keepalive_timeout 0\n\n$s = Test::Nginx::HTTP2->new(port(8081));\n$sid = $s->new_stream({ path => '/t.html' });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }, { type => 'GOAWAY' }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 200, 'keepalive_timeout 0');\n\n($frame) = grep { $_->{type} eq \"GOAWAY\" } @$frames;\nok($frame, 'keepalive_timeout 0 - GOAWAY');\n\n# keepalive_time\n\n$s = Test::Nginx::HTTP2->new(port(8082));\n$sid = $s->new_stream({ path => '/t.html' });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 200, 'keepalive time request');\nlike($frame->{headers}->{'x-conn'}, qr/^1:0/, 'keepalive time variables');\n\n$frames = $s->read(all => [{ type => 'GOAWAY' }], wait => 0.5);\n\n($frame) = grep { $_->{type} eq \"GOAWAY\" } @$frames;\nis($frame, undef, 'keepalive time - no GOAWAY yet');\n\nselect undef, undef, undef, 1.1;\n\n$sid = $s->new_stream({ path => '/t.html' });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }, { type => 'GOAWAY' }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 200, 'keepalive time request 2');\nlike($frame->{headers}->{'x-conn'}, qr/^2:[^0]/, 'keepalive time variables 2');\n\n($frame) = grep { $_->{type} eq \"GOAWAY\" } @$frames;\nok($frame, 'keepalive time limit - GOAWAY');\nis($frame->{last_sid}, $sid, 'keepalive time limit - GOAWAY last stream');\n\n# graceful shutdown in idle state\n\n$s = Test::Nginx::HTTP2->new();\n$s->{socket}->setsockopt(SOL_SOCKET, SO_RCVBUF, 64*1024) or die $!;\n$s->h2_settings(0, 0x4 => 2**20);\n$s->h2_window(2**21);\n\n$sid = $s->new_stream();\n\n# wait server to finish and close socket if lingering close were disabled\n\nselect undef, undef, undef, 0.1;\n\n$t->reload();\n\nselect undef, undef, undef, 0.3;\n\n$s->h2_ping(\"SEE-THIS\");\n\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }, { type => 'GOAWAY' }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 200, 'graceful shutdown in idle');\n\n@data = grep { $_->{type} eq \"DATA\" } @$frames;\n$sum = eval join '+', map { $_->{length} } @data;\nis($sum, 400000, 'graceful shutdown in idle - all data received');\n\n($frame) = grep { $_->{type} eq \"GOAWAY\" } @$frames;\nok($frame, 'graceful shutdown in idle - GOAWAY');\nis($frame->{last_sid}, $sid, 'graceful shutdown in idle - GOAWAY last stream');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/h2_limit_conn.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for HTTP/2 protocol with limit_conn.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse Test::Nginx::HTTP2;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http http_v2 limit_conn/)->plan(4)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    limit_conn_zone  $binary_remote_addr  zone=conn:1m;\n\n    server {\n        listen       127.0.0.1:8080 http2;\n        server_name  localhost;\n\n        location /t.html {\n            limit_conn conn 1;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('t.html', 'SEE-THIS');\n# suppress deprecation warning\nopen OLDERR, \">&\", \\*STDERR; close STDERR;\n$t->run();\nopen STDERR, \">&\", \\*OLDERR;\n\n###############################################################################\n\nmy $s = Test::Nginx::HTTP2->new();\n$s->h2_settings(0, 0x4 => 1);\n\nmy $sid = $s->new_stream({ path => '/t.html' });\nmy $frames = $s->read(all => [{ sid => $sid, length => 1 }]);\n\nmy ($frame) = grep { $_->{type} eq \"HEADERS\" && $_->{sid} == $sid } @$frames;\nis($frame->{headers}->{':status'}, 200, 'limit_conn first stream');\n\nmy $sid2 = $s->new_stream({ path => '/t.html' });\n$frames = $s->read(all => [{ sid => $sid2, length => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" && $_->{sid} == $sid2 } @$frames;\nis($frame->{headers}->{':status'}, 503, 'limit_conn rejected');\n\n$s->h2_settings(0, 0x4 => 2**16);\n\n$s->read(all => [\n\t{ sid => $sid, fin => 1 },\n\t{ sid => $sid2, fin => 1 }\n]);\n\n# limit_conn + client's RST_STREAM\n\n$s = Test::Nginx::HTTP2->new();\n$s->h2_settings(0, 0x4 => 1);\n\n$sid = $s->new_stream({ path => '/t.html' });\n$frames = $s->read(all => [{ sid => $sid, length => 1 }]);\n$s->h2_rst($sid, 5);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" && $_->{sid} == $sid } @$frames;\nis($frame->{headers}->{':status'}, 200, 'RST_STREAM 1');\n\n$sid2 = $s->new_stream({ path => '/t.html' });\n$frames = $s->read(all => [{ sid => $sid2, length => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" && $_->{sid} == $sid2 } @$frames;\nis($frame->{headers}->{':status'}, 200, 'RST_STREAM 2');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/h2_limit_req.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for HTTP/2 protocol with limit_req.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse Test::Nginx::HTTP2;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http http_v2 proxy rewrite limit_req/)\n\t->plan(7);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    limit_req_zone   $binary_remote_addr  zone=req:1m rate=1r/s;\n\n    server {\n        listen       127.0.0.1:8080 http2;\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        location / { }\n        location /limit_req {\n            limit_req  zone=req burst=2;\n            alias %%TESTDIR%%/t.html;\n        }\n        location /proxy_limit_req/ {\n            add_header X-Body $request_body;\n            add_header X-Body-File $request_body_file;\n            client_body_in_file_only on;\n            proxy_pass http://127.0.0.1:8081/;\n            limit_req  zone=req burst=2;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('index.html', '');\n$t->write_file('t.html', 'SEE-THIS');\n# suppress deprecation warning\nopen OLDERR, \">&\", \\*STDERR; close STDERR;\n$t->run();\nopen STDERR, \">&\", \\*OLDERR;\n\n###############################################################################\n\n# request body delayed in limit_req\n\nmy $s = Test::Nginx::HTTP2->new();\nmy $sid = $s->new_stream({ path => '/proxy_limit_req/', body_more => 1 });\n$s->h2_body('TEST');\nmy $frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\nmy ($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis(read_body_file($frame->{headers}->{'x-body-file'}), 'TEST',\n\t'request body - limit req');\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/proxy_limit_req/', body_more => 1 });\nselect undef, undef, undef, 1.1;\n$s->h2_body('TEST');\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis(read_body_file($frame->{headers}->{'x-body-file'}), 'TEST',\n\t'request body - limit req - limited');\n\n# request body delayed in limit_req - with an empty DATA frame\n# \"zero size buf in output\" alerts seen\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/proxy_limit_req/', body_more => 1 });\n$s->h2_body('');\nselect undef, undef, undef, 1.1;\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 200, 'request body - limit req - empty');\n\n# request body sent along with request\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/proxy_limit_req/', body => 'TEST2' });\nselect undef, undef, undef, 1.1;\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis(read_body_file($frame->{headers}->{'x-body-file'}), 'TEST2',\n\t'request body - limit req 2');\n\n# partial request body data frame received (to be discarded) within request\n# delayed in limit_req, the rest of data frame is received after response\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/limit_req', body => 'TEST', split => [61],\n\tsplit_delay => 1.1 });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, '200', 'discard body - limit req - limited');\n\n$sid = $s->new_stream({ path => '/' });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, '200', 'discard body - limit req - next');\n\n# ditto, but instead of receiving the rest of data frame, connection is closed\n# 'http request already closed while closing request' alert can be produced\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/limit_req', body => 'TEST', split => [61],\n\tabort => 1 });\n\nselect undef, undef, undef, 1.1;\nclose $s->{socket};\n\npass('discard body - limit req - eof');\n\n###############################################################################\n\nsub read_body_file {\n\tmy ($path) = @_;\n\treturn unless $path;\n\topen FILE, $path or return \"$!\";\n\tlocal $/;\n\tmy $content = <FILE>;\n\tclose FILE;\n\treturn $content;\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/h2_priority.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for HTTP/2 protocol with priority.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse Test::Nginx::HTTP2;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http http_v2/)->plan(20)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080 http2;\n        server_name  localhost;\n    }\n}\n\nEOF\n\n# suppress deprecation warning\nopen OLDERR, \">&\", \\*STDERR; close STDERR;\n$t->run();\nopen STDERR, \">&\", \\*OLDERR;\n\n# file size is slightly beyond initial window size: 2**16 + 80 bytes\n\n$t->write_file('t1.html',\n\tjoin('', map { sprintf \"X%04dXXX\", $_ } (1 .. 8202)));\n\n$t->write_file('t2.html', 'SEE-THIS');\n\n###############################################################################\n\n# stream muliplexing + PRIORITY frames\n\nmy $s = Test::Nginx::HTTP2->new();\nmy $sid = $s->new_stream({ path => '/t1.html' });\n$s->read(all => [{ sid => $sid, length => 2**16 - 1 }]);\n\nmy $sid2 = $s->new_stream({ path => '/t2.html' });\n$s->read(all => [{ sid => $sid2, fin => 0x4 }]);\n\n$s->h2_priority(0, $sid);\n$s->h2_priority(255, $sid2);\n\n$s->h2_window(2**17, $sid);\n$s->h2_window(2**17, $sid2);\n$s->h2_window(2**17);\n\nmy $frames = $s->read(all => [\n\t{ sid => $sid, fin => 1 },\n\t{ sid => $sid2, fin => 1 }\n]);\n\nmy @data = grep { $_->{type} eq \"DATA\" } @$frames;\nis(join(' ', map { $_->{sid} } @data), \"$sid2 $sid\", 'weight - PRIORITY 1');\n\n# and vice versa\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/t1.html' });\n$s->read(all => [{ sid => $sid, length => 2**16 - 1 }]);\n\n$sid2 = $s->new_stream({ path => '/t2.html' });\n$s->read(all => [{ sid => $sid2, fin => 0x4 }]);\n\n$s->h2_priority(255, $sid);\n$s->h2_priority(0, $sid2);\n\n$s->h2_window(2**17, $sid);\n$s->h2_window(2**17, $sid2);\n$s->h2_window(2**17);\n\n$frames = $s->read(all => [\n\t{ sid => $sid, fin => 1 },\n\t{ sid => $sid2, fin => 1 }\n]);\n\n@data = grep { $_->{type} eq \"DATA\" } @$frames;\nis(join(' ', map { $_->{sid} } @data), \"$sid $sid2\", 'weight - PRIORITY 2');\n\n# stream muliplexing + HEADERS PRIORITY flag\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/t1.html', prio => 0 });\n$s->read(all => [{ sid => $sid, length => 2**16 - 1 }]);\n\n$sid2 = $s->new_stream({ path => '/t2.html', prio => 255 });\n$s->read(all => [{ sid => $sid2, fin => 0x4 }]);\n\n$s->h2_window(2**17, $sid);\n$s->h2_window(2**17, $sid2);\n$s->h2_window(2**17);\n\n$frames = $s->read(all => [\n\t{ sid => $sid, fin => 1 },\n\t{ sid => $sid2, fin => 1 }\n]);\n\n@data = grep { $_->{type} eq \"DATA\" } @$frames;\nmy $sids = join ' ', map { $_->{sid} } @data;\nis($sids, \"$sid2 $sid\", 'weight - HEADERS PRIORITY 1');\n\n# and vice versa\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/t1.html', prio => 255 });\n$s->read(all => [{ sid => $sid, length => 2**16 - 1 }]);\n\n$sid2 = $s->new_stream({ path => '/t2.html', prio => 0 });\n$s->read(all => [{ sid => $sid2, fin => 0x4 }]);\n\n$s->h2_window(2**17, $sid);\n$s->h2_window(2**17, $sid2);\n$s->h2_window(2**17);\n\n$frames = $s->read(all => [\n\t{ sid => $sid, fin => 1 },\n\t{ sid => $sid2, fin => 1 }\n]);\n\n@data = grep { $_->{type} eq \"DATA\" } @$frames;\n$sids = join ' ', map { $_->{sid} } @data;\nis($sids, \"$sid $sid2\", 'weight - HEADERS PRIORITY 2');\n\n# 5.3.1.  Stream Dependencies\n\n# PRIORITY frame\n\n$s = Test::Nginx::HTTP2->new();\n\n$s->h2_priority(16, 3, 0);\n$s->h2_priority(16, 1, 3);\n\n$sid = $s->new_stream({ path => '/t1.html' });\n$s->read(all => [{ sid => $sid, length => 2**16 - 1 }]);\n\n$sid2 = $s->new_stream({ path => '/t2.html' });\n$s->read(all => [{ sid => $sid2, fin => 0x4 }]);\n\n$s->h2_window(2**17, $sid);\n$s->h2_window(2**17, $sid2);\n$s->h2_window(2**17);\n\n$frames = $s->read(all => [\n\t{ sid => $sid, fin => 1 },\n\t{ sid => $sid2, fin => 1 },\n]);\n\n@data = grep { $_->{type} eq \"DATA\" } @$frames;\n$sids = join ' ', map { $_->{sid} } @data;\nis($sids, \"$sid2 $sid\", 'dependency - PRIORITY 1');\n\n# and vice versa\n\n$s = Test::Nginx::HTTP2->new();\n\n$s->h2_priority(16, 1, 0);\n$s->h2_priority(16, 3, 1);\n\n$sid = $s->new_stream({ path => '/t1.html' });\n$s->read(all => [{ sid => $sid, length => 2**16 - 1 }]);\n\n$sid2 = $s->new_stream({ path => '/t2.html' });\n$s->read(all => [{ sid => $sid2, fin => 0x4 }]);\n\n$s->h2_window(2**17, $sid);\n$s->h2_window(2**17, $sid2);\n$s->h2_window(2**17);\n\n$frames = $s->read(all => [\n\t{ sid => $sid, fin => 1 },\n\t{ sid => $sid2, fin => 1 },\n]);\n\n@data = grep { $_->{type} eq \"DATA\" } @$frames;\n$sids = join ' ', map { $_->{sid} } @data;\nis($sids, \"$sid $sid2\", 'dependency - PRIORITY 2');\n\n# PRIORITY - self dependency\n\n# 5.3.1.  Stream Dependencies\n#   A stream cannot depend on itself.  An endpoint MUST treat this as a\n#   stream error of type PROTOCOL_ERROR.\n# Instead, we respond with a connection error of type PROTOCOL_ERROR.\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream();\n$s->read(all => [{ sid => $sid, fin => 1 }]);\n\n$s->h2_priority(0, $sid, $sid);\n$frames = $s->read(all => [{ type => 'GOAWAY' }]);\n\nmy ($frame) = grep { $_->{type} eq \"GOAWAY\" } @$frames;\nis($frame->{last_sid}, $sid, 'dependency - PRIORITY self - GOAWAY');\nis($frame->{code}, 1, 'dependency - PRIORITY self - PROTOCOL_ERROR');\n\n# HEADERS PRIORITY flag, reprioritize prior PRIORITY frame records\n\n$s = Test::Nginx::HTTP2->new();\n\n$s->h2_priority(16, 1, 0);\n$s->h2_priority(16, 3, 0);\n\n$sid = $s->new_stream({ path => '/t1.html', dep => 3 });\n$s->read(all => [{ sid => $sid, length => 2**16 - 1 }]);\n\n$sid2 = $s->new_stream({ path => '/t2.html' });\n$s->read(all => [{ sid => $sid2, fin => 0x4 }]);\n\n$s->h2_window(2**17, $sid);\n$s->h2_window(2**17, $sid2);\n$s->h2_window(2**17);\n\n$frames = $s->read(all => [\n\t{ sid => $sid, fin => 1 },\n\t{ sid => $sid2, fin => 1 },\n]);\n\n@data = grep { $_->{type} eq \"DATA\" } @$frames;\n$sids = join ' ', map { $_->{sid} } @data;\nis($sids, \"$sid2 $sid\", 'dependency - HEADERS PRIORITY 1');\n\n# and vice versa\n\n$s = Test::Nginx::HTTP2->new();\n\n$s->h2_priority(16, 1, 0);\n$s->h2_priority(16, 3, 0);\n\n$sid = $s->new_stream({ path => '/t1.html' });\n$s->read(all => [{ sid => $sid, length => 2**16 - 1 }]);\n\n$sid2 = $s->new_stream({ path => '/t2.html', dep => 1 });\n$s->read(all => [{ sid => $sid2, fin => 0x4 }]);\n\n$s->h2_window(2**17, $sid);\n$s->h2_window(2**17, $sid2);\n$s->h2_window(2**17);\n\n$frames = $s->read(all => [\n\t{ sid => $sid, fin => 1 },\n\t{ sid => $sid2, fin => 1 },\n]);\n\n@data = grep { $_->{type} eq \"DATA\" } @$frames;\n$sids = join ' ', map { $_->{sid} } @data;\nis($sids, \"$sid $sid2\", 'dependency - HEADERS PRIORITY 2');\n\n# HEADERS - self dependency\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ dep => 1 });\n$frames = $s->read(all => [{ type => 'GOAWAY' }]);\n\n($frame) = grep { $_->{type} eq \"GOAWAY\" } @$frames;\nis($frame->{last_sid}, 0, 'dependency - HEADERS self - GOAWAY');\nis($frame->{code}, 1, 'dependency - HEADERS self - PROTOCOL_ERROR');\n\n# PRIORITY frame, weighted dependencies\n\n$s = Test::Nginx::HTTP2->new();\n\n$s->h2_priority(16, 5, 0);\n$s->h2_priority(255, 1, 5);\n$s->h2_priority(0, 3, 5);\n\n$sid = $s->new_stream({ path => '/t1.html' });\n$s->read(all => [{ sid => $sid, length => 2**16 - 1 }]);\n\n$sid2 = $s->new_stream({ path => '/t2.html' });\n$s->read(all => [{ sid => $sid2, fin => 0x4 }]);\n\nmy $sid3 = $s->new_stream({ path => '/t2.html' });\n$s->read(all => [{ sid => $sid3, fin => 0x4 }]);\n\n$s->h2_window(2**16, 1);\n$s->h2_window(2**16, 3);\n$s->h2_window(2**16, 5);\n$s->h2_window(2**16);\n\n$frames = $s->read(all => [\n\t{ sid => $sid, fin => 1 },\n\t{ sid => $sid2, fin => 1 },\n\t{ sid => $sid3, fin => 1 },\n]);\n\n@data = grep { $_->{type} eq \"DATA\" } @$frames;\n$sids = join ' ', map { $_->{sid} } @data;\nis($sids, \"$sid3 $sid $sid2\", 'weighted dependency - PRIORITY 1');\n\n# and vice versa\n\n$s = Test::Nginx::HTTP2->new();\n\n$s->h2_priority(16, 5, 0);\n$s->h2_priority(0, 1, 5);\n$s->h2_priority(255, 3, 5);\n\n$sid = $s->new_stream({ path => '/t1.html' });\n$s->read(all => [{ sid => $sid, length => 2**16 - 1 }]);\n\n$sid2 = $s->new_stream({ path => '/t2.html' });\n$s->read(all => [{ sid => $sid2, fin => 0x4 }]);\n\n$sid3 = $s->new_stream({ path => '/t2.html' });\n$s->read(all => [{ sid => $sid3, fin => 0x4 }]);\n\n$s->h2_window(2**16, 1);\n$s->h2_window(2**16, 3);\n$s->h2_window(2**16, 5);\n$s->h2_window(2**16);\n\n$frames = $s->read(all => [\n\t{ sid => $sid, fin => 1 },\n\t{ sid => $sid2, fin => 1 },\n\t{ sid => $sid3, fin => 1 },\n]);\n\n@data = grep { $_->{type} eq \"DATA\" } @$frames;\n$sids = join ' ', map { $_->{sid} } @data;\nis($sids, \"$sid3 $sid2 $sid\", 'weighted dependency - PRIORITY 2');\n\n# PRIORITY - reprioritization with circular dependency - after [3] removed\n# initial dependency tree:\n# 1 <- [3] <- 5\n\n$s = Test::Nginx::HTTP2->new();\n\n$s->h2_window(2**18);\n\n$s->h2_priority(16, 1, 0);\n$s->h2_priority(16, 3, 1);\n$s->h2_priority(16, 5, 3);\n\n$sid = $s->new_stream({ path => '/t1.html' });\n$s->read(all => [{ sid => $sid, length => 2**16 - 1 }]);\n\n$sid2 = $s->new_stream({ path => '/t1.html' });\n$s->read(all => [{ sid => $sid2, length => 2**16 - 1 }]);\n\n$sid3 = $s->new_stream({ path => '/t1.html' });\n$s->read(all => [{ sid => $sid3, length => 2**16 - 1 }]);\n\n$s->h2_window(2**16, $sid2);\n\n$frames = $s->read(all => [{ sid => $sid2, fin => 1 }]);\n$sids = join ' ', map { $_->{sid} } grep { $_->{type} eq \"DATA\" } @$frames;\nis($sids, $sid2, 'removed dependency');\n\nfor (1 .. 40) {\n\t$s->read(all => [{ sid => $s->new_stream(), fin => 1 }]);\n}\n\n# make circular dependency\n# 1 <- 5 -- current dependency tree before reprioritization\n# 5 <- 1\n# 1 <- 5\n\n$s->h2_priority(16, 1, 5);\n$s->h2_priority(16, 5, 1);\n\n$s->h2_window(2**16, $sid);\n$s->h2_window(2**16, $sid3);\n\n$frames = $s->read(all => [\n\t{ sid => $sid, fin => 1 },\n\t{ sid => $sid3, fin => 1 },\n]);\n\n($frame) = grep { $_->{type} eq \"DATA\" && $_->{sid} == $sid } @$frames;\nis($frame->{length}, 81, 'removed dependency - first stream');\n\n($frame) = grep { $_->{type} eq \"DATA\" && $_->{sid} == $sid3 } @$frames;\nis($frame->{length}, 81, 'removed dependency - last stream');\n\n# PRIORITY - reprioritization with circular dependency - exclusive [5]\n# 1 <- [5] <- 3\n\n$s = Test::Nginx::HTTP2->new();\n\n$s->h2_window(2**18);\n\n$s->h2_priority(16, 1, 0);\n$s->h2_priority(16, 3, 1);\n$s->h2_priority(16, 5, 1, excl => 1);\n\n$sid = $s->new_stream({ path => '/t1.html' });\n$s->read(all => [{ sid => $sid, length => 2**16 - 1 }]);\n\n$sid2 = $s->new_stream({ path => '/t1.html' });\n$s->read(all => [{ sid => $sid2, length => 2**16 - 1 }]);\n\n$sid3 = $s->new_stream({ path => '/t1.html' });\n$s->read(all => [{ sid => $sid3, length => 2**16 - 1 }]);\n\n$s->h2_window(2**16, $sid);\n\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n$sids = join ' ', map { $_->{sid} } grep { $_->{type} eq \"DATA\" } @$frames;\nis($sids, $sid, 'exclusive dependency - parent removed');\n\n# make circular dependency\n# 5 <- 3 -- current dependency tree before reprioritization\n# 3 <- 5\n\n$s->h2_priority(16, 5, 3);\n\n$s->h2_window(2**16, $sid2);\n$s->h2_window(2**16, $sid3);\n\n$frames = $s->read(all => [\n\t{ sid => $sid2, fin => 1 },\n\t{ sid => $sid3, fin => 1 },\n]);\n\n($frame) = grep { $_->{type} eq \"DATA\" && $_->{sid} == $sid2 } @$frames;\nis($frame->{length}, 81, 'exclusive dependency - first stream');\n\n($frame) = grep { $_->{type} eq \"DATA\" && $_->{sid} == $sid3 } @$frames;\nis($frame->{length}, 81, 'exclusive dependency - last stream');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/h2_proxy_cache.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for HTTP/2 protocol with cache.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse Test::Nginx::HTTP2;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http http_v2 proxy cache/)->plan(9)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    proxy_cache_path %%TESTDIR%%/cache    keys_zone=NAME:1m;\n\n    server {\n        listen       127.0.0.1:8080 http2;\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        location /cache {\n            proxy_pass http://127.0.0.1:8081/;\n            proxy_cache NAME;\n            proxy_cache_valid 1m;\n        }\n\n        location /proxy_buffering_off {\n            proxy_pass http://127.0.0.1:8081/;\n            proxy_cache NAME;\n            proxy_cache_valid 1m;\n            proxy_buffering off;\n        }\n\n        location / { }\n\n        location /slow {\n            limit_rate 200;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('t.html', 'SEE-THIS');\n$t->write_file('slow.html', 'SEE-THIS');\n# suppress deprecation warning\nopen OLDERR, \">&\", \\*STDERR; close STDERR;\n$t->run();\nopen STDERR, \">&\", \\*OLDERR;\n\n###############################################################################\n\n# simple proxy cache test\n\nmy $s = Test::Nginx::HTTP2->new();\nmy $sid = $s->new_stream({ path => '/cache/t.html' });\nmy $frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\nmy ($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, '200', 'proxy cache');\n\nmy $etag = $frame->{headers}->{'etag'};\n\n($frame) = grep { $_->{type} eq \"DATA\" } @$frames;\nis($frame->{length}, length 'SEE-THIS', 'proxy cache - DATA');\nis($frame->{data}, 'SEE-THIS', 'proxy cache - DATA payload');\n\n$t->write_file('t.html', 'NOOP');\n\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/cache/t.html' },\n\t{ name => ':authority', value => 'localhost', mode => 1 },\n\t{ name => 'if-none-match', value => $etag }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 304, 'proxy cache conditional');\n\n$t->write_file('t.html', 'SEE-THIS');\n\n# request body with cached response\n\n$sid = $s->new_stream({ path => '/cache/t.html', body_more => 1 });\n$s->h2_body('TEST');\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 200, 'proxy cache - request body');\n\n$s->h2_ping('SEE-THIS');\n$frames = $s->read(all => [{ type => 'PING' }]);\n\n($frame) = grep { $_->{type} eq \"PING\" && $_->{flags} & 0x1 } @$frames;\nok($frame, 'proxy cache - request body - next');\n\n# HEADERS could be received with fin, followed by DATA\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/cache/t.html?1', method => 'HEAD' });\n\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }], wait => 0.2);\npush @$frames, $_ for @{$s->read(all => [{ sid => $sid }], wait => 0.2)};\nok(!grep ({ $_->{type} eq \"DATA\" } @$frames), 'proxy cache HEAD - no body');\n\n# HEAD on empty cache with proxy_buffering off\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream(\n\t{ path => '/proxy_buffering_off/t.html?1', method => 'HEAD' });\n\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\npush @$frames, $_ for @{$s->read(all => [{ sid => $sid }], wait => 0.2)};\nok(!grep ({ $_->{type} eq \"DATA\" } @$frames),\n\t'proxy cache HEAD buffering off - no body');\n\nSKIP: {\nskip 'win32', 1 if $^O eq 'MSWin32';\n\n# client cancels stream with a cacheable request that was sent to upstream\n# HEADERS should not be produced for the canceled stream\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/cache/slow.html' });\n\n$s->h2_rst($sid, 8);\n\n$frames = $s->read(all => [{ sid => $sid, fin => 0x4 }], wait => 1.2);\nok(!(grep { $_->{type} eq \"HEADERS\" } @$frames), 'no headers');\n\n}\n\n# client closes connection after sending a cacheable request producing alert\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/cache/t.html?4' });\n\nundef $s;\nselect undef, undef, undef, 0.2;\n\n$t->stop();\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/h2_proxy_max_temp_file_size.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for http proxy module, proxy_max_temp_file_size directive.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse Test::Nginx::HTTP2;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http http_v2 proxy/)->plan(4);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080 http2;\n        server_name  localhost;\n\n        proxy_buffer_size 4k;\n        proxy_buffers 8 4k;\n\n        location / {\n            proxy_max_temp_file_size 4k;\n            proxy_pass http://127.0.0.1:8081/;\n        }\n\n        location /off/ {\n            proxy_max_temp_file_size 0;\n            proxy_pass http://127.0.0.1:8081/;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        location / { }\n    }\n}\n\nEOF\n\n$t->write_file('1', 'X' x (1024 * 1024));\n# suppress deprecation warning\nopen OLDERR, \">&\", \\*STDERR; close STDERR;\n$t->run();\nopen STDERR, \">&\", \\*OLDERR;\n\n###############################################################################\n\n# test that the response is wholly proxied when all event pipe buffers are full\n\nmy $s = Test::Nginx::HTTP2->new();\nmy $sid = $s->new_stream({ path => '/1' });\n\nselect undef, undef, undef, 0.4;\n$s->h2_window(1024 * 1024, $sid);\n$s->h2_window(1024 * 1024);\n\nmy $frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\nmy $body = join '', map { $_->{data} } grep { $_->{type} eq \"DATA\" } @$frames;\nlike($body, qr/^X+$/m, 'no pipe bufs - body');\nis(length($body), 1024 * 1024, 'no pipe bufs - body length');\n\n# also with disabled proxy temp file\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/off/1' });\n\nselect undef, undef, undef, 0.4;\n$s->h2_window(1024 * 1024, $sid);\n$s->h2_window(1024 * 1024);\n\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n$body = join '', map { $_->{data} } grep { $_->{type} eq \"DATA\" } @$frames;\nlike($body, qr/^X+$/m, 'no temp file - body');\nis(length($body), 1024 * 1024, 'no temp file - body length');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/h2_proxy_protocol.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for HTTP/2 protocol with proxy_protocol.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse Socket qw/ CRLF /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse Test::Nginx::HTTP2;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http http_v2 realip/)->plan(3)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080 proxy_protocol http2;\n        server_name  localhost;\n\n        location /pp {\n            set_real_ip_from 127.0.0.1/32;\n            real_ip_header proxy_protocol;\n            alias %%TESTDIR%%/t.html;\n            add_header X-PP $remote_addr;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('t.html', 'SEE-THIS');\n# suppress deprecation warning\nopen OLDERR, \">&\", \\*STDERR; close STDERR;\n$t->run();\nopen STDERR, \">&\", \\*OLDERR;\n\n###############################################################################\n\nmy $proxy = 'PROXY TCP4 192.0.2.1 192.0.2.2 1234 5678' . CRLF;\nmy $s = Test::Nginx::HTTP2->new(port(8080), proxy => $proxy);\nmy $sid = $s->new_stream({ path => '/pp' });\nmy $frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\nmy ($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nok($frame, 'PROXY HEADERS frame');\nis($frame->{headers}->{'x-pp'}, '192.0.2.1', 'PROXY remote addr');\n\n# invalid PROXY protocol string\n\nTODO: {\nlocal $TODO = 'not yet' unless $t->has_version('1.25.1');\n$proxy = 'BOGUS TCP4 192.0.2.1 192.0.2.2 1234 5678' . CRLF;\nok(!http($proxy), 'PROXY invalid protocol');\n\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/h2_proxy_request_buffering.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for HTTP/2 protocol with unbuffered request body.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse Socket qw/ CRLF /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse Test::Nginx::HTTP2;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http http_v2 proxy/)->plan(49);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080 http2;\n        listen       127.0.0.1:8082;\n        server_name  localhost;\n\n        location / {\n            proxy_request_buffering off;\n            proxy_pass http://127.0.0.1:8081/;\n            client_body_buffer_size 1k;\n        }\n        location /chunked {\n            proxy_request_buffering off;\n            proxy_http_version 1.1;\n            proxy_pass http://127.0.0.1:8081/;\n            client_body_buffer_size 1k;\n        }\n        location /abort {\n            proxy_request_buffering off;\n            proxy_http_version 1.1;\n            proxy_pass http://127.0.0.1:8082/;\n        }\n    }\n}\n\nEOF\n\n# suppress deprecation warning\nopen OLDERR, \">&\", \\*STDERR; close STDERR;\n$t->run();\nopen STDERR, \">&\", \\*OLDERR;\n\n###############################################################################\n\n# unbuffered request body\n\nmy $f = get_body('/', 'content-length' => 10);\nok($f->{headers}, 'request');\nis($f->{upload}('01234', body_more => 1), '01234', 'part');\nis($f->{upload}('56789'), '56789', 'part 2');\nis($f->{http_end}(), 200, 'response');\n\n$f = get_body('/', 'content-length' => 10);\nok($f->{headers}, 'much');\nis($f->{upload}('0123456789', body_more => 1), '0123456789', 'much - part');\nis($f->{upload}('many'), '', 'much - part 2');\nis($f->{http_end}(), 400, 'much - response');\n\n$f = get_body('/', 'content-length' => 10);\nok($f->{headers}, 'less');\nis($f->{upload}('0123', body_more => 1), '0123', 'less - part');\nis($f->{upload}('56789'), '', 'less - part 2');\nis($f->{http_end}(), 400, 'less - response');\n\n$f = get_body('/', 'content-length' => 18);\nok($f->{headers}, 'many');\nis($f->{upload}('01234many', body_split => [ 5 ], body_more => 1),\n\t'01234many', 'many - part');\nis($f->{upload}('56789many', body_split => [ 5 ]),\n\t'56789many', 'many - part 2');\nis($f->{http_end}(), 200, 'many - response');\n\n$f = get_body('/', 'content-length' => 0);\nok($f->{headers}, 'empty');\nis($f->{upload}('', body_more => 1, wait => 0.2), '', 'empty - part');\nis($f->{upload}('', wait => 0.2), '', 'empty - part 2');\nis($f->{http_end}(), 200, 'empty - response');\n\n$f = get_body('/', 'content-length' => 1536);\nok($f->{headers}, 'buffer');\nis($f->{upload}('0123' x 128, body_more => 1), '0123' x 128,\n\t'buffer - below');\nis($f->{upload}('4567' x 128, body_more => 1), '4567' x 128,\n\t'buffer - equal');\nis($f->{upload}('89AB' x 128), '89AB' x 128, 'buffer - above');\nis($f->{http_end}(), 200, 'buffer - response');\n\n$f = get_body('/', 'content-length' => 10);\nok($f->{headers}, 'split');\nis($f->{upload}('0123456789', split => [ 14 ]), '0123456789', 'split');\nis($f->{http_end}(), 200, 'split - response');\n\n# unbuffered request body, chunked transfer-encoding\n\n$f = get_body('/chunked');\nok($f->{headers}, 'chunked');\nis($f->{upload}('01234', body_more => 1), '5' . CRLF . '01234' . CRLF,\n\t'chunked - part');\nis($f->{upload}('56789'), '5' . CRLF . '56789' . CRLF . '0' . CRLF . CRLF,\n\t'chunked - part 2');\nis($f->{http_end}(), 200, 'chunked - response');\n\n$f = get_body('/chunked');\nok($f->{headers}, 'chunked buffer');\nis($f->{upload}('0123' x 128, body_more => 1),\n\t'200' . CRLF . '0123' x 128 . CRLF, 'chunked buffer - below');\nis($f->{upload}('4567' x 128, body_more => 1),\n\t'200' . CRLF . '4567' x 128 . CRLF, 'chunked buffer - equal');\nis($f->{upload}('89AB' x 128),\n\t'200' . CRLF . '89AB' x 128 . CRLF . '0' . CRLF . CRLF,\n\t'chunked buffer - above');\nis($f->{http_end}(), 200, 'chunked buffer - response');\n\n$f = get_body('/chunked');\nok($f->{headers}, 'chunked many');\nis($f->{upload}('01234many', body_split => [ 5 ], body_more => 1),\n\t'9' . CRLF . '01234many' . CRLF, 'chunked many - part');\nis($f->{upload}('56789many', body_split => [ 5 ]),\n\t'9' . CRLF . '56789many' . CRLF . '0' . CRLF . CRLF,\n\t'chunked many - part 2');\nis($f->{http_end}(), 200, 'chunked many - response');\n\n$f = get_body('/chunked');\nok($f->{headers}, 'chunked empty');\nis($f->{upload}('', body_more => 1, wait => 0.2), '', 'chunked empty - part');\nis($f->{upload}(''), '0' . CRLF . CRLF, 'chunked empty - part 2');\nis($f->{http_end}(), 200, 'chunked empty - response');\n\n$f = get_body('/chunked');\nok($f->{headers}, 'chunked split');\nis(http_content($f->{upload}('0123456789', split => [ 14 ])),\n\t'0123456789', 'chunked split');\nis($f->{http_end}(), 200, 'chunked split - response');\n\n# unbuffered request body, chunked transfer-encoding\n# client sends partial DATA frame and closes connection\n\nmy $s = Test::Nginx::HTTP2->new();\nmy $s2 = Test::Nginx::HTTP2->new();\n\n$s->new_stream({ path => '/abort', body_more => 1 });\n$s->h2_body('TEST', { split => [ 9 ], abort => 1 });\n\nclose $s->{socket};\n\n$s2->h2_ping('PING');\nisnt(@{$s2->read()}, 0, 'chunked abort');\n\n###############################################################################\n\nsub http_content {\n\tmy ($body) = @_;\n\tmy $content = '';\n\n\twhile ($body =~ /\\G\\x0d?\\x0a?([0-9a-f]+)\\x0d\\x0a?/gcmsi) {\n\t\tmy $len = hex($1);\n\t\t$content .= substr($body, pos($body), $len);\n\t\tpos($body) += $len;\n\t}\n\n\treturn $content;\n}\n\nsub get_body {\n\tmy ($url, %extra) = @_;\n\tmy ($server, $client, $f);\n\n\t$server = IO::Socket::INET->new(\n\t\tProto => 'tcp',\n\t\tLocalHost => '127.0.0.1',\n\t\tLocalPort => port(8081),\n\t\tListen => 5,\n\t\tTimeout => 3,\n\t\tReuse => 1\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\tmy $s = Test::Nginx::HTTP2->new();\n\tmy $sid = exists $extra{'content-length'}\n\t\t? $s->new_stream({ headers => [\n\t\t\t{ name => ':method', value => 'GET' },\n\t\t\t{ name => ':scheme', value => 'http' },\n\t\t\t{ name => ':path', value => $url, },\n\t\t\t{ name => ':authority', value => 'localhost' },\n\t\t\t{ name => 'content-length',\n\t\t\t\tvalue => $extra{'content-length'} }],\n\t\t\tbody_more => 1 })\n\t\t: $s->new_stream({ path => $url, body_more => 1 });\n\n\t$client = $server->accept() or return;\n\n\tlog2c(\"(new connection $client)\");\n\n\t$f->{headers} = backend_read($client);\n\n\tmy $chunked = $f->{headers} =~ /chunked/;\n\n\t$f->{upload} = sub {\n\t\tmy ($body, %extra) = @_;\n\t\tmy $len = length($body);\n\t\tmy $wait = $extra{wait};\n\n\t\t$s->h2_body($body, { %extra });\n\n\t\t$body = '';\n\n\t\tfor (1 .. 10) {\n\t\t\tmy $buf = backend_read($client, $wait) or return '';\n\t\t\t$body .= $buf;\n\n\t\t\tmy $got = 0;\n\t\t\t$got += $chunked ? hex $_ : $_ for $chunked\n\t\t\t\t? $body =~ /(\\w+)\\x0d\\x0a?\\w+\\x0d\\x0a?/g\n\t\t\t\t: length($body);\n\t\t\tnext if $chunked && !$extra{body_more}\n\t\t\t\t&& $buf !~ /^0\\x0d\\x0a?/m;\n\t\t\tlast if $got >= $len;\n\t\t}\n\n\t\treturn $body;\n\t};\n\t$f->{http_end} = sub {\n\t\t$client->write(<<EOF);\nHTTP/1.1 200 OK\nConnection: close\n\nEOF\n\n\t\t$client->close;\n\n\t\tmy $frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\t\tmy ($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\n\t\treturn $frame->{headers}->{':status'};\n\t};\n\treturn $f;\n}\n\nsub backend_read {\n\tmy ($s, $timo) = @_;\n\tmy $buf = '';\n\n\tif (IO::Select->new($s)->can_read($timo || 8)) {\n\t\t$s->sysread($buf, 16384) or return;\n\t\tlog2i($buf);\n\t}\n\treturn $buf;\n}\n\nsub log2i { Test::Nginx::log_core('|| <<', @_); }\nsub log2o { Test::Nginx::log_core('|| >>', @_); }\nsub log2c { Test::Nginx::log_core('||', @_); }\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/h2_proxy_request_buffering_redirect.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for HTTP/2 protocol with unbuffered request body.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse Test::Nginx::HTTP2;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http http_v2 proxy rewrite/)->plan(1);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080 http2;\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        proxy_http_version 1.1;\n\n        location / {\n            proxy_request_buffering off;\n            proxy_pass http://127.0.0.1:8081/bad;\n            proxy_intercept_errors on;\n            error_page 502 = /pass;\n        }\n\n        location /bad {\n            return 502;\n        }\n\n        location /pass {\n            proxy_pass http://127.0.0.1:8081/good;\n        }\n\n        location /good {\n            limit_rate 100;\n            return 200;\n        }\n    }\n}\n\nEOF\n\n# suppress deprecation warning\nopen OLDERR, \">&\", \\*STDERR; close STDERR;\n$t->run();\nopen STDERR, \">&\", \\*OLDERR;\n###############################################################################\n\n# unbuffered request body\n\nmy $s = Test::Nginx::HTTP2->new();\nmy $sid = $s->new_stream({ body_more => 1 });\n\n$s->h2_body('SEE-', { body_more => 1 });\nsleep 1;\n$s->h2_body('THIS');\n\nmy $frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\nmy ($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 200, 'discard body rest on redirect');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/h2_proxy_request_buffering_ssl.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for HTTP/2 protocol with unbuffered request body to ssl backend.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse Socket qw/ CRLF /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse Test::Nginx::HTTP2;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http http_ssl http_v2 proxy/)\n\t->has_daemon('openssl')->plan(40);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080 http2;\n        server_name  localhost;\n\n        location / {\n            proxy_request_buffering off;\n            proxy_pass https://127.0.0.1:8082;\n            client_body_buffer_size 512;\n        }\n        location /chunked {\n            proxy_request_buffering off;\n            proxy_http_version 1.1;\n            proxy_pass https://127.0.0.1:8082;\n            client_body_buffer_size 512;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8082 ssl;\n        server_name  localhost;\n\n        ssl_certificate_key localhost.key;\n        ssl_certificate localhost.crt;\n\n        location / {\n            proxy_request_buffering off;\n            proxy_pass http://127.0.0.1:8081/;\n            client_body_buffer_size 1k;\n        }\n        location /chunked {\n            proxy_request_buffering off;\n            proxy_http_version 1.1;\n            proxy_pass http://127.0.0.1:8081/;\n            client_body_buffer_size 1k;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('openssl.conf', <<EOF);\n[ req ]\ndefault_bits = 2048\nencrypt_key = no\ndistinguished_name = req_distinguished_name\n[ req_distinguished_name ]\nEOF\n\nmy $d = $t->testdir();\n\nforeach my $name ('localhost') {\n\tsystem('openssl req -x509 -new '\n\t\t. \"-config $d/openssl.conf -subj /CN=$name/ \"\n\t\t. \"-out $d/$name.crt -keyout $d/$name.key \"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create certificate for $name: $!\\n\";\n}\n\n# suppress deprecation warning\nopen OLDERR, \">&\", \\*STDERR; close STDERR;\n$t->run();\nopen STDERR, \">&\", \\*OLDERR;\n\n###############################################################################\n\n# unbuffered request body\n\nmy $f = get_body('/', 'content-length' => 10);\nok($f->{headers}, 'request');\nis($f->{upload}('01234', body_more => 1), '01234', 'part');\nis($f->{upload}('56789'), '56789', 'part 2');\nis($f->{http_end}(), 200, 'response');\n\n$f = get_body('/', 'content-length' => 1536);\nok($f->{headers}, 'buffer');\nis($f->{upload}('0123' x 128, body_more => 1), '0123' x 128, 'buffer - below');\nis($f->{upload}('4567' x 128, body_more => 1), '4567' x 128, 'buffer - equal');\nis($f->{upload}('89AB' x 128), '89AB' x 128, 'buffer - above');\nis($f->{http_end}(), 200, 'buffer - response');\n\n$f = get_body('/', 'content-length' => 18);\nok($f->{headers}, 'many');\nis($f->{upload}('01234many', body_split => [ 5 ], body_more => 1),\n\t'01234many', 'many - part');\nis($f->{upload}('56789many', body_split => [ 5 ]),\n\t'56789many', 'many - part 2');\nis($f->{http_end}(), 200, 'many - response');\n\n$f = get_body('/', 'content-length' => 0);\nok($f->{headers}, 'empty');\nis($f->{upload}('', body_more => 1, wait => 0.2), '', 'empty - part');\nis($f->{upload}('', wait => 0.2), '', 'empty - part 2');\nis($f->{http_end}(), 200, 'empty - response');\n\n$f = get_body('/', 'content-length' => 10);\nok($f->{headers}, 'split');\nis($f->{upload}('0123456789', split => [ 14 ]), '0123456789', 'split');\nis($f->{http_end}(), 200, 'split - response');\n\n# unbuffered request body, chunked transfer-encoding\n\n$f = get_body('/chunked');\nok($f->{headers}, 'chunk');\nis($f->{upload}('01234', body_more => 1), '5' . CRLF . '01234' . CRLF,\n\t'chunked - part');\nis($f->{upload}('56789'), '5' . CRLF . '56789' . CRLF . '0' . CRLF . CRLF,\n\t'chunked - part 2');\nis($f->{http_end}(), 200, 'chunked - response');\n\n$f = get_body('/chunked');\nok($f->{headers}, 'chunked buffer');\nis($f->{upload}('0123' x 64, body_more => 1),\n\t'100' . CRLF . '0123' x 64 . CRLF, 'chunked buffer - below');\nis($f->{upload}('4567' x 64, body_more => 1),\n\t'100' . CRLF . '4567' x 64 . CRLF, 'chunked buffer - equal');\nis($f->{upload}('89AB' x 64),\n\t'100' . CRLF . '89AB' x 64 . CRLF . '0' . CRLF . CRLF,\n\t'chunked buffer - above');\nis($f->{http_end}(), 200, 'chunked buffer - response');\n\n$f = get_body('/chunked');\nok($f->{headers}, 'chunked many');\nis($f->{upload}('01234many', body_split => [ 5 ], body_more => 1),\n\t'9' . CRLF . '01234many' . CRLF, 'chunked many - part');\nis($f->{upload}('56789many', body_split => [ 5 ]),\n\t'9' . CRLF . '56789many' . CRLF . '0' . CRLF . CRLF,\n\t'chunked many - part 2');\nis($f->{http_end}(), 200, 'chunked many - response');\n\n$f = get_body('/chunked');\nok($f->{headers}, 'chunked empty');\nis($f->{upload}('', body_more => 1, wait => 0.2), '', 'chunked empty - part');\nis($f->{upload}(''), '0' . CRLF . CRLF, 'chunked empty - part 2');\nis($f->{http_end}(), 200, 'chunked empty - response');\n\n$f = get_body('/chunked');\nok($f->{headers}, 'chunked split');\nis(http_content($f->{upload}('0123456789', split => [ 14 ])),\n\t'0123456789', 'chunked split');\nis($f->{http_end}(), 200, 'chunked split - response');\n\n###############################################################################\n\nsub http_content {\n\tmy ($body) = @_;\n\tmy $content = '';\n\n\twhile ($body =~ /\\G\\x0d?\\x0a?([0-9a-f]+)\\x0d\\x0a?/gcmsi) {\n\t\tmy $len = hex($1);\n\t\t$content .= substr($body, pos($body), $len);\n\t\tpos($body) += $len;\n\t}\n\n\treturn $content;\n}\n\nsub get_body {\n\tmy ($url, %extra) = @_;\n\tmy ($server, $client, $f);\n\n\t$server = IO::Socket::INET->new(\n\t\tProto => 'tcp',\n\t\tLocalHost => '127.0.0.1',\n\t\tLocalPort => port(8081),\n\t\tListen => 5,\n\t\tTimeout => 3,\n\t\tReuse => 1\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\tmy $s = Test::Nginx::HTTP2->new();\n\tmy $sid = exists $extra{'content-length'}\n\t\t? $s->new_stream({ headers => [\n\t\t\t{ name => ':method', value => 'GET' },\n\t\t\t{ name => ':scheme', value => 'http' },\n\t\t\t{ name => ':path', value => $url, },\n\t\t\t{ name => ':authority', value => 'localhost' },\n\t\t\t{ name => 'content-length',\n\t\t\t\tvalue => $extra{'content-length'} }],\n\t\t\tbody_more => 1 })\n\t\t: $s->new_stream({ path => $url, body_more => 1 });\n\n\t$client = $server->accept() or return;\n\n\tlog2c(\"(new connection $client)\");\n\n\t$f->{headers} = backend_read($client);\n\n\tmy $chunked = $f->{headers} =~ /chunked/;\n\n\t$f->{upload} = sub {\n\t\tmy ($body, %extra) = @_;\n\t\tmy $len = length($body);\n\t\tmy $wait = $extra{wait};\n\n\t\t$s->h2_body($body, { %extra });\n\n\t\t$body = '';\n\n\t\tfor (1 .. 10) {\n\t\t\tmy $buf = backend_read($client, $wait) or return '';\n\t\t\t$body .= $buf;\n\n\t\t\tmy $got = 0;\n\t\t\t$got += $chunked ? hex $_ : $_ for $chunked\n\t\t\t\t? $body =~ /(\\w+)\\x0d\\x0a?\\w+\\x0d\\x0a?/g\n\t\t\t\t: length($body);\n\t\t\tnext if $chunked && !$extra{body_more}\n\t\t\t\t&& $buf !~ /^0\\x0d\\x0a?/m;\n\t\t\tlast if $got >= $len;\n\t\t}\n\n\t\treturn $body;\n\t};\n\t$f->{http_end} = sub {\n\t\t$client->write(<<EOF);\nHTTP/1.1 200 OK\nConnection: close\n\nEOF\n\n\t\t$client->close;\n\n\t\tmy $frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\t\tmy ($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\n\t\treturn $frame->{headers}->{':status'};\n\t};\n\treturn $f;\n}\n\nsub backend_read {\n\tmy ($s, $timo) = @_;\n\tmy $buf = '';\n\n\tif (IO::Select->new($s)->can_read($timo || 8)) {\n\t\t$s->sysread($buf, 16384) or return;\n\t\tlog2i($buf);\n\t}\n\treturn $buf;\n}\n\nsub log2i { Test::Nginx::log_core('|| <<', @_); }\nsub log2o { Test::Nginx::log_core('|| >>', @_); }\nsub log2c { Test::Nginx::log_core('||', @_); }\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/h2_proxy_ssl.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for HTTP/2 protocol with proxy to ssl backend.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse Test::Nginx::HTTP2;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http http_ssl http_v2 proxy/)\n\t->has_daemon('openssl')->plan(1);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080 http2;\n        listen       127.0.0.1:8081 ssl;\n        server_name  localhost;\n\n        ssl_certificate_key localhost.key;\n        ssl_certificate localhost.crt;\n\n        location / { }\n        location /proxy_ssl/ {\n            proxy_pass https://127.0.0.1:8081/;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('openssl.conf', <<EOF);\n[ req ]\ndefault_bits = 2048\nencrypt_key = no\ndistinguished_name = req_distinguished_name\n[ req_distinguished_name ]\nEOF\n\nmy $d = $t->testdir();\n\nforeach my $name ('localhost') {\n\tsystem('openssl req -x509 -new '\n\t\t. \"-config $d/openssl.conf -subj /CN=$name/ \"\n\t\t. \"-out $d/$name.crt -keyout $d/$name.key \"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create certificate for $name: $!\\n\";\n}\n\n$t->write_file('index.html', '');\n# suppress deprecation warning\nopen OLDERR, \">&\", \\*STDERR; close STDERR;\n$t->run();\nopen STDERR, \">&\", \\*OLDERR;\n\n###############################################################################\n\n# request body with an empty DATA frame proxied to ssl backend\n# \"zero size buf in output\" alerts seen\n\nmy $s = Test::Nginx::HTTP2->new();\nmy $sid = $s->new_stream({ path => '/proxy_ssl/', body_more => 1 });\n$s->h2_body('');\nmy $frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\nmy ($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 200, 'empty request body');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/h2_request_body.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for HTTP/2 protocol with request body.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse Test::Nginx::HTTP2;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http http_v2 proxy/)->plan(49);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080 http2;\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        error_page 400 /proxy2/t.html;\n\n        location / {\n            add_header X-Length $http_content_length;\n        }\n        location /slow {\n            limit_rate 100;\n        }\n        location /off/ {\n            proxy_pass http://127.0.0.1:8081/;\n            add_header X-Body $request_body;\n            add_header X-Body-File $request_body_file;\n        }\n        location /proxy2/ {\n            add_header X-Body $request_body;\n            add_header X-Body-File $request_body_file;\n            client_body_in_file_only on;\n            proxy_pass http://127.0.0.1:8081/;\n        }\n        location /client_max_body_size {\n            add_header X-Body $request_body;\n            add_header X-Body-File $request_body_file;\n            client_body_in_single_buffer on;\n            client_body_in_file_only on;\n            proxy_pass http://127.0.0.1:8081/;\n            client_max_body_size 10;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('index.html', '');\n$t->write_file('t.html', 'SEE-THIS');\n$t->write_file('slow.html', 'SEE-THIS');\n# suppress deprecation warning\nopen OLDERR, \">&\", \\*STDERR; close STDERR;\n$t->run();\nopen STDERR, \">&\", \\*OLDERR;\n\n###############################################################################\n\n# request body (uses proxied response)\n\nmy $s = Test::Nginx::HTTP2->new();\nmy $sid = $s->new_stream({ path => '/proxy2/t.html', body => 'TEST' });\nmy $frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\nmy ($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis(read_body_file($frame->{headers}->{'x-body-file'}), 'TEST', 'request body');\nis($frame->{headers}->{'x-length'}, 4, 'request body - content length');\n\n# request body with padding (uses proxied response)\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream(\n\t{ path => '/proxy2/t.html', body => 'TEST', body_padding => 42 });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis(read_body_file($frame->{headers}->{'x-body-file'}), 'TEST',\n\t'request body with padding');\nis($frame->{headers}->{'x-length'}, 4,\n\t'request body with padding - content length');\n\n$sid = $s->new_stream();\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, '200', 'request body with padding - next');\n\n# request body sent in multiple DATA frames in a single packet\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream(\n\t{ path => '/proxy2/t.html', body => 'TEST', body_split => [2] });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis(read_body_file($frame->{headers}->{'x-body-file'}), 'TEST',\n\t'request body in multiple frames');\nis($frame->{headers}->{'x-length'}, 4,\n\t'request body in multiple frames - content length');\n\n# request body sent in multiple DATA frames, each in its own packet\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/proxy2/t.html', body_more => 1 });\n$s->h2_body('TEST', { body_more => 1 });\nselect undef, undef, undef, 0.1;\n$s->h2_body('MOREDATA');\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis(read_body_file($frame->{headers}->{'x-body-file'}), 'TESTMOREDATA',\n\t'request body in multiple frames separately');\nis($frame->{headers}->{'x-length'}, 12,\n\t'request body in multiple frames separately - content length');\n\n# if run with body buffering in filters, it's expected to update window\n# after request body populates initial stream window size set for preread\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/proxy2/t.html', body_more => 1 });\n$s->h2_body('01234567' x 2048, { body_more => 1 });\nselect undef, undef, undef, 0.1;\n$s->h2_body('01234567' x 2048, { body_more => 1 });\nselect undef, undef, undef, 0.1;\n$s->h2_body('01234567' x 2048, { body_more => 1 });\nselect undef, undef, undef, 0.1;\n$s->h2_body('01234567' x 2048, { body_more => 1 });\n\n$frames = $s->read(all => [{ type => 'WINDOW_UPDATE' }]);\n($frame) = grep { $_->{type} eq 'WINDOW_UPDATE' } @$frames;\nis($frame->{sid}, $sid, 'big request body - WINDOW_UPDATE sid');\ncmp_ok($frame->{wdelta}, '>=', 65536, 'big request body - WINDOW_UPDATE delta');\n\n$s->h2_body('01234567' x 2048);\n\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis(read_body_file($frame->{headers}->{'x-body-file'}), '01234567' x 10240,\n\t'big request body - content');\nis($frame->{headers}->{'x-length'}, 81920,\n\t'big request body - content length');\n\n# request body with an empty DATA frame\n# \"zero size buf in output\" alerts seen\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/proxy2/', body => '' });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 200, 'request body - empty');\nis($frame->{headers}->{'x-length'}, 0, 'request body - empty size');\nok($frame->{headers}{'x-body-file'}, 'request body - empty body file');\nis(read_body_file($frame->{headers}{'x-body-file'}), '',\n\t'request body - empty content');\n\n# it is expected to avoid adding Content-Length for requests without body\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/proxy2/' });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 200, 'request without body');\nis($frame->{headers}->{'x-length'}, undef,\n\t'request without body - content length');\n\n# request body discarded\n# RST_STREAM with zero code received\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ body_more => 1 });\n$frames = $s->read(all => [{ type => 'RST_STREAM' }], wait => 0.5);\n\n($frame) = grep { $_->{type} eq \"RST_STREAM\" } @$frames;\nis($frame->{code}, 0, 'request body discarded - zero RST_STREAM');\n\n# malformed request body length not equal to content-length\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ body => 'TEST', headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/client_max_body_size', mode => 1 },\n\t{ name => ':authority', value => 'localhost', mode => 1 },\n\t{ name => 'content-length', value => '5', mode => 1 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 400, 'request body less than content-length');\n\n$sid = $s->new_stream({ body => 'TEST', headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/client_max_body_size', mode => 1 },\n\t{ name => ':authority', value => 'localhost', mode => 1 },\n\t{ name => 'content-length', value => '3', mode => 1 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 400, 'request body more than content-length');\n\n# client_max_body_size\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/client_max_body_size/t.html',\n\tbody => 'TESTTEST12' });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 200, 'client_max_body_size - status');\nis(read_body_file($frame->{headers}->{'x-body-file'}), 'TESTTEST12',\n\t'client_max_body_size - body');\n\n# client_max_body_size - limited\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/client_max_body_size/t.html',\n\tbody => 'TESTTEST123' });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 413, 'client_max_body_size - limited');\n\n# client_max_body_size - many DATA frames\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/client_max_body_size/t.html',\n\tbody => 'TESTTEST12', body_split => [2] });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 200, 'client_max_body_size many - status');\nis(read_body_file($frame->{headers}->{'x-body-file'}), 'TESTTEST12',\n\t'client_max_body_size many - body');\n\n# client_max_body_size - many DATA frames - limited\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/client_max_body_size/t.html',\n\tbody => 'TESTTEST123', body_split => [2] });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 413, 'client_max_body_size many - limited');\n\n# client_max_body_size - padded DATA\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/client_max_body_size/t.html',\n\tbody => 'TESTTEST12', body_padding => 42 });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 200, 'client_max_body_size pad - status');\nis(read_body_file($frame->{headers}->{'x-body-file'}), 'TESTTEST12',\n\t'client_max_body_size pad - body');\n\n# client_max_body_size - padded DATA - limited\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/client_max_body_size/t.html',\n\tbody => 'TESTTEST123', body_padding => 42 });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 413, 'client_max_body_size pad - limited');\n\n# client_max_body_size - many padded DATA frames\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/client_max_body_size/t.html',\n\tbody => 'TESTTEST12', body_padding => 42, body_split => [2] });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 200,\n\t'client_max_body_size many pad - status');\nis(read_body_file($frame->{headers}->{'x-body-file'}), 'TESTTEST12',\n\t'client_max_body_size many pad - body');\n\n# client_max_body_size - many padded DATA frames - limited\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/client_max_body_size/t.html',\n\tbody => 'TESTTEST123', body_padding => 42, body_split => [2] });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 413,\n\t'client_max_body_size many pad - limited');\n\n# request body without content-length\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ body_more => 1, headers => [\n\t{ name => ':method', value => 'GET', mode => 2 },\n\t{ name => ':scheme', value => 'http', mode => 2 },\n\t{ name => ':path', value => '/client_max_body_size', mode => 2 },\n\t{ name => ':authority', value => 'localhost', mode => 2 }]});\n$s->h2_body('TESTTEST12');\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 200,\n\t'request body without content-length - status');\nis(read_body_file($frame->{headers}->{'x-body-file'}), 'TESTTEST12',\n\t'request body without content-length - body');\n\n# request body without content-length - limited\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ body_more => 1, headers => [\n\t{ name => ':method', value => 'GET', mode => 2 },\n\t{ name => ':scheme', value => 'http', mode => 2 },\n\t{ name => ':path', value => '/client_max_body_size', mode => 2 },\n\t{ name => ':authority', value => 'localhost', mode => 2 }]});\n$s->h2_body('TESTTEST123');\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 413,\n\t'request body without content-length - limited');\n\n# request body without content-length - many DATA frames\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ body_more => 1, headers => [\n\t{ name => ':method', value => 'GET', mode => 2 },\n\t{ name => ':scheme', value => 'http', mode => 2 },\n\t{ name => ':path', value => '/client_max_body_size', mode => 2 },\n\t{ name => ':authority', value => 'localhost', mode => 2 }]});\n$s->h2_body('TESTTEST12', { body_split => [2] });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 200,\n\t'request body without content-length many - status');\nis(read_body_file($frame->{headers}->{'x-body-file'}), 'TESTTEST12',\n\t'request body without content-length many - body');\n\n# request body without content-length - many DATA frames - limited\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ body_more => 1, headers => [\n\t{ name => ':method', value => 'GET', mode => 2 },\n\t{ name => ':scheme', value => 'http', mode => 2 },\n\t{ name => ':path', value => '/client_max_body_size', mode => 2 },\n\t{ name => ':authority', value => 'localhost', mode => 2 }]});\n$s->h2_body('TESTTEST123', { body_split => [2] });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 413,\n\t'request body without content-length many - limited');\n\n# request body without content-length - padding\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ body_more => 1, headers => [\n\t{ name => ':method', value => 'GET', mode => 2 },\n\t{ name => ':scheme', value => 'http', mode => 2 },\n\t{ name => ':path', value => '/client_max_body_size', mode => 2 },\n\t{ name => ':authority', value => 'localhost', mode => 2 }]});\n$s->h2_body('TESTTEST12', { body_padding => 42 });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 200,\n\t'request body without content-length pad - status');\nis(read_body_file($frame->{headers}->{'x-body-file'}), 'TESTTEST12',\n\t'request body without content-length pad - body');\n\n# request body without content-length - padding - limited\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ body_more => 1, headers => [\n\t{ name => ':method', value => 'GET', mode => 2 },\n\t{ name => ':scheme', value => 'http', mode => 2 },\n\t{ name => ':path', value => '/client_max_body_size', mode => 2 },\n\t{ name => ':authority', value => 'localhost', mode => 2 }]});\n$s->h2_body('TESTTEST123', { body_padding => 42 });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 413,\n\t'request body without content-length pad - limited');\n\n# request body without content-length - padding with many DATA frames\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ body_more => 1, headers => [\n\t{ name => ':method', value => 'GET', mode => 2 },\n\t{ name => ':scheme', value => 'http', mode => 2 },\n\t{ name => ':path', value => '/client_max_body_size', mode => 2 },\n\t{ name => ':authority', value => 'localhost', mode => 2 }]});\n$s->h2_body('TESTTEST', { body_padding => 42, body_split => [2] });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 200,\n\t'request body without content-length many pad - status');\nis(read_body_file($frame->{headers}->{'x-body-file'}), 'TESTTEST',\n\t'request body without content-length many pad - body');\n\n# request body without content-length - padding with many DATA frames - limited\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ body_more => 1, headers => [\n\t{ name => ':method', value => 'GET', mode => 2 },\n\t{ name => ':scheme', value => 'http', mode => 2 },\n\t{ name => ':path', value => '/client_max_body_size', mode => 2 },\n\t{ name => ':authority', value => 'localhost', mode => 2 }]});\n$s->h2_body('TESTTEST123', { body_padding => 42, body_split => [2] });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 413,\n\t'request body without content-length many pad - limited');\n\n# absent request body is not buffered with client_body_in_file_only off\n# see e02f1977846b for details\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/off/t.html' });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{'x-body-file'}, undef, 'no request body in file');\n\n# ticket #1384, request body corruption in recv_buffer\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/off/slow.html', body_more => 1 });\nselect undef, undef, undef, 0.1;\n\n# for simplicity, DATA frame is received on its own for a known offset\n\n$s->h2_body('TEST');\nselect undef, undef, undef, 0.1;\n\n# overwrite recv_buffer; since upstream response arrival is delayed,\n# this would make $request_body point to the overridden buffer space\n\n$s->h2_ping('xxxx');\n\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nisnt($frame->{headers}->{'x-body'}, 'xxxx', 'sync buffer');\n\n# request body after 400 errors redirected to a proxied location\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ body => \"\", headers => [\n\t{ name => ':method', value => \"\" }]});\n\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n($frame) = grep { $_->{type} eq 'DATA' } @$frames;\nis($frame->{data}, 'SEE-THIS', 'request body after 400 redirect');\n\n###############################################################################\n\nsub read_body_file {\n\tmy ($path) = @_;\n\topen FILE, $path or return \"$!\";\n\tlocal $/;\n\tmy $content = <FILE>;\n\tclose FILE;\n\treturn $content;\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/h2_request_body_extra.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n# (C) Nginx, Inc.\n\n# Tests for HTTP/2 protocol with request body, additional tests.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse Test::Nginx::HTTP2;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http http_v2 proxy rewrite/);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080 http2;\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        client_header_buffer_size 1k;\n        client_body_buffer_size 2k;\n\n        location / {\n            add_header X-Body $request_body;\n            add_header X-Body-File $request_body_file;\n            proxy_pass http://127.0.0.1:8082;\n        }\n\n        location /file {\n            client_body_in_file_only on;\n            add_header X-Body \"$request_body\";\n            add_header X-Body-File \"$request_body_file\";\n            proxy_pass http://127.0.0.1:8082;\n        }\n\n        location /single {\n            client_body_in_single_buffer on;\n            add_header X-Body \"$request_body\";\n            add_header X-Body-File \"$request_body_file\";\n            proxy_pass http://127.0.0.1:8082;\n        }\n\n        location /large {\n            client_max_body_size 1k;\n            proxy_pass http://127.0.0.1:8082;\n        }\n\n        location /unbuf/ {\n            add_header X-Unbuf-File \"$request_body_file\";\n            proxy_pass http://127.0.0.1:8081/;\n            proxy_request_buffering off;\n            proxy_http_version 1.1;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8082;\n        server_name  localhost;\n        return 204;\n    }\n}\n\nEOF\n\nplan(skip_all => 'not yet') unless $t->has_version('1.21.2');\n$t->plan(50);\n\n# suppress deprecation warning\nopen OLDERR, \">&\", \\*STDERR; close STDERR;\n$t->run();\nopen STDERR, \">&\", \\*OLDERR;\n\n###############################################################################\n\n# below are basic body tests from body.t, slightly\n# adapted to HTTP/2, repeated multiple times with variations:\n#\n# buffered vs. non-buffered, length vs. chunked,\n# single frame vs. multiple frames\n#\n# some does not make sense in HTTP/2 (such as \"body in two buffers\"), but\n# preserved for consistency and due to the fact that proxying via HTTP/1.1\n# is used in unbuffered tests\n\nunlike(http2_get('/'), qr/x-body:/ms, 'no body');\n\nlike(http2_get_body('/', '0123456789'),\n\tqr/x-body: 0123456789$/ms, 'body');\nlike(http2_get_body('/', '0123456789' x 128),\n\tqr/x-body: (0123456789){128}$/ms, 'body in two buffers');\nlike(http2_get_body('/', '0123456789' x 512),\n\tqr/x-body-file/ms, 'body in file');\nlike(read_body_file(http2_get_body('/file', '0123456789' x 512)),\n\tqr/^(0123456789){512}$/s, 'body in file only');\nlike(http2_get_body('/single', '0123456789' x 128),\n\tqr/x-body: (0123456789){128}$/ms, 'body in single buffer');\nlike(http2_get_body('/large', '0123456789' x 128),\n\tqr/:status: 413/, 'body too large');\n\n# without Content-Length header\n\nlike(http2_get_body_nolen('/', '0123456789'),\n\tqr/x-body: 0123456789$/ms, 'body nolen');\nlike(http2_get_body_nolen('/', '0123456789' x 128),\n\tqr/x-body: (0123456789){128}$/ms, 'body nolen in two buffers');\nlike(http2_get_body_nolen('/', '0123456789' x 512),\n\tqr/x-body-file/ms, 'body nolen in file');\nlike(read_body_file(http2_get_body_nolen('/file', '0123456789' x 512)),\n\tqr/^(0123456789){512}$/s, 'body nolen in file only');\nlike(http2_get_body_nolen('/single', '0123456789' x 128),\n\tqr/x-body: (0123456789){128}$/ms, 'body nolen in single buffer');\nlike(http2_get_body_nolen('/large', '0123456789' x 128),\n\tqr/:status: 413/, 'body nolen too large');\n\n# with multiple frames\n\nlike(http2_get_body_multi('/', '0123456789'),\n\tqr/x-body: 0123456789$/ms, 'body multi');\nlike(http2_get_body_multi('/', '0123456789' x 128),\n\tqr/x-body: (0123456789){128}$/ms, 'body multi in two buffers');\nlike(http2_get_body_multi('/', '0123456789' x 512),\n\tqr/x-body-file/ms, 'body multi in file');\nlike(read_body_file(http2_get_body_multi('/file', '0123456789' x 512)),\n\tqr/^(0123456789){512}$/s, 'body multi in file only');\nlike(http2_get_body_multi('/single', '0123456789' x 128),\n\tqr/x-body: (0123456789){128}$/ms, 'body multi in single buffer');\nlike(http2_get_body_multi('/large', '0123456789' x 128),\n\tqr/:status: 413/, 'body multi too large');\n\n# with multiple frames and without Content-Length header\n\nlike(http2_get_body_multi_nolen('/', '0123456789'),\n\tqr/x-body: 0123456789$/ms, 'body multi nolen');\nlike(http2_get_body_multi_nolen('/', '0123456789' x 128),\n\tqr/x-body: (0123456789){128}/ms, 'body multi nolen in two buffers');\nlike(http2_get_body_multi_nolen('/', '0123456789' x 512),\n\tqr/x-body-file/ms, 'body multi nolen in file');\nlike(read_body_file(http2_get_body_multi_nolen('/file', '0123456789' x 512)),\n\tqr/^(0123456789){512}$/s, 'body multi nolen in file only');\nlike(http2_get_body_multi_nolen('/single', '0123456789' x 128),\n\tqr/x-body: (0123456789){128}$/ms, 'body multi nolen in single buffer');\nlike(http2_get_body_multi_nolen('/large', '0123456789' x 128),\n\tqr/:status: 413/, 'body multi nolen too large');\n\n# unbuffered\n\nunlike(http2_get('/unbuf/'), qr/x-body:/ms, 'no body unbuf');\n\nlike(http2_get_body('/unbuf/', '0123456789'),\n\tqr/x-body: 0123456789$/ms, 'body unbuf');\nlike(http2_get_body('/unbuf/', '0123456789' x 128),\n\tqr/x-body: (0123456789){128}$/ms, 'body unbuf in two buffers');\nlike(http2_get_body('/unbuf/', '0123456789' x 512),\n\tqr/(?!.*x-unbuf-file.*)x-body-file/ms, 'body unbuf in file');\nlike(read_body_file(http2_get_body('/unbuf/file', '0123456789' x 512)),\n\tqr/^(0123456789){512}$/s, 'body unbuf in file only');\nlike(http2_get_body('/unbuf/single', '0123456789' x 128),\n\tqr/x-body: (0123456789){128}$/ms, 'body unbuf in single buffer');\nlike(http2_get_body('/unbuf/large', '0123456789' x 128),\n\tqr/:status: 413/, 'body unbuf too large');\n\n# unbuffered without Content-Length\n\nlike(http2_get_body_nolen('/unbuf/', '0123456789'),\n\tqr/x-body: 0123456789$/ms, 'body unbuf nolen');\nlike(http2_get_body_nolen('/unbuf/', '0123456789' x 128),\n\tqr/x-body: (0123456789){128}$/ms, 'body unbuf nolen in two buffers');\nlike(http2_get_body_nolen('/unbuf/', '0123456789' x 512),\n\tqr/(?!.*x-unbuf-file.*)x-body-file/ms, 'body unbuf nolen in file');\nlike(read_body_file(http2_get_body_nolen('/unbuf/file', '0123456789' x 512)),\n\tqr/^(0123456789){512}$/s, 'body unbuf nolen in file only');\nlike(http2_get_body_nolen('/unbuf/single', '0123456789' x 128),\n\tqr/x-body: (0123456789){128}$/ms, 'body unbuf nolen in single buffer');\nlike(http2_get_body_nolen('/unbuf/large', '0123456789' x 128),\n\tqr/:status: 413/, 'body unbuf nolen too large');\n\n# unbuffered with multiple frames\n\nlike(http2_get_body_multi('/unbuf/', '0123456789'),\n\tqr/x-body: 0123456789$/ms, 'body unbuf multi');\nlike(http2_get_body_multi('/unbuf/', '0123456789' x 128),\n\tqr/x-body: (0123456789){128}$/ms, 'body unbuf multi in two buffers');\nlike(http2_get_body_multi('/unbuf/', '0123456789' x 512),\n\tqr/(?!.*x-unbuf-file.*)x-body-file/ms, 'body unbuf multi in file');\nlike(read_body_file(http2_get_body_multi('/unbuf/file', '0123456789' x 512)),\n\tqr/^(0123456789){512}$/s, 'body unbuf multi in file only');\nlike(http2_get_body_multi('/unbuf/single', '0123456789' x 128),\n\tqr/x-body: (0123456789){128}$/ms, 'body unbuf multi in single buffer');\nlike(http2_get_body_multi('/unbuf/large', '0123456789' x 128),\n\tqr/:status: 413/, 'body unbuf multi too large');\n\n# unbuffered with multiple frames and without Content-Length\n\nlike(http2_get_body_multi_nolen('/unbuf/', '0123456789'),\n\tqr/x-body: 0123456789$/ms, 'body unbuf multi nolen');\nlike(http2_get_body_multi_nolen('/unbuf/', '0123456789' x 128),\n\tqr/x-body: (0123456789){128}$/ms,\n\t'body unbuf multi nolen in two buffers');\nlike(http2_get_body_multi_nolen('/unbuf/', '0123456789' x 512),\n\tqr/(?!.*x-unbuf-file.*)x-body-file/ms,\n        'body unbuf multi nolen in file');\nlike(read_body_file(http2_get_body_multi_nolen('/unbuf/file',\n\t'0123456789' x 512)), qr/^(0123456789){512}$/s,\n\t'body unbuf multi nolen in file only');\nlike(http2_get_body_multi_nolen('/unbuf/single', '0123456789' x 128),\n\tqr/x-body: (0123456789){128}$/ms,\n\t'body unbuf multi nolen in single buffer');\nlike(http2_get_body_multi_nolen('/unbuf/large', '0123456789' x 128),\n\tqr/:status: 413/, 'body unbuf multi nolen too large');\n\n###############################################################################\n\nsub http2_get {\n\tmy ($uri) = @_;\n\n\tmy $s = Test::Nginx::HTTP2->new();\n\tmy $sid = $s->new_stream({ path => $uri });\n\tmy $frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n\tmy ($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\n\n\treturn join(\"\\n\", map { \"$_: \" . $frame->{headers}->{$_}; }\n\t\tkeys %{$frame->{headers}});\n}\n\nsub http2_get_body {\n\tmy ($uri, $body) = @_;\n\n\tmy $s = Test::Nginx::HTTP2->new();\n\tmy $sid = $s->new_stream({ path => $uri, body => $body });\n\tmy $frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n\tmy ($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\n\n\treturn join(\"\\n\", map { \"$_: \" . $frame->{headers}->{$_}; }\n\t\t\tkeys %{$frame->{headers}});\n}\n\nsub http2_get_body_nolen {\n\tmy ($uri, $body) = @_;\n\n\tmy $s = Test::Nginx::HTTP2->new();\n\tmy $sid = $s->new_stream({ path => $uri, body_more => 1 });\n\t$s->h2_body($body);\n\tmy $frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n\tmy ($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\n\n\treturn join(\"\\n\", map { \"$_: \" . $frame->{headers}->{$_}; }\n\t\t\tkeys %{$frame->{headers}});\n}\n\nsub http2_get_body_multi {\n\tmy ($uri, $body) = @_;\n\n\tmy $s = Test::Nginx::HTTP2->new();\n\tmy $sid = $s->new_stream({\n\t\theaders => [\n\t\t\t{ name => ':method', value => 'GET' },\n\t\t\t{ name => ':scheme', value => 'http' },\n\t\t\t{ name => ':path', value => $uri },\n\t\t\t{ name => ':authority', value => 'localhost' },\n\t\t\t{ name => 'content-length', value => length $body },\n\t\t],\n\t\tbody_more => 1\n\t});\n\tfor my $b (split //, $body, 10) {\n\t\t$s->h2_body($b, { body_more => 1 });\n\t}\n\tselect undef, undef, undef, 0.1;\n\t$s->h2_body('');\n\tmy $frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n\tmy ($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\n\n\treturn join(\"\\n\", map { \"$_: \" . $frame->{headers}->{$_}; }\n\t\t\tkeys %{$frame->{headers}});\n}\n\nsub http2_get_body_multi_nolen {\n\tmy ($uri, $body) = @_;\n\n\tmy $s = Test::Nginx::HTTP2->new();\n\tmy $sid = $s->new_stream({ path => $uri, body_more => 1 });\n\tfor my $b (split //, $body, 10) {\n\t\t$s->h2_body($b, { body_more => 1 });\n\t}\n\tselect undef, undef, undef, 0.1;\n\t$s->h2_body('');\n\tmy $frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n\tmy ($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\n\n\treturn join(\"\\n\", map { \"$_: \" . $frame->{headers}->{$_}; }\n\t\t\tkeys %{$frame->{headers}});\n}\n\nsub read_body_file {\n\tmy ($r) = @_;\n\treturn '' unless $r =~ m/x-body-file: (.*)/;\n\topen FILE, $1\n\t\tor return \"$!\";\n\tlocal $/;\n\tmy $content = <FILE>;\n\tclose FILE;\n\treturn $content;\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/h2_request_body_js.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for HTTP/2 request body with njs subrequest in the body handler.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse Test::Nginx::HTTP2;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http http_v2/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    js_import test.js;\n\n    server {\n        listen       127.0.0.1:8080 http2;\n        server_name  localhost;\n\n        lingering_close off;\n\n        location / {\n            js_content test.sr_body;\n            add_header X-Body $request_body;\n        }\n\n        location /sr { }\n    }\n}\n\nEOF\n\n$t->write_file('test.js', <<EOF);\nfunction body_fwd_cb(r) {\n    r.parent.return(r.status, r.responseText);\n}\n\nfunction sr_body(r) {\n    r.subrequest('/sr', body_fwd_cb);\n}\n\nexport default {sr_body};\n\nEOF\n\n$t->write_file('sr', 'SEE-THIS');\n$t->try_run('no njs available')->plan(3);\n\n###############################################################################\n\nmy $s = Test::Nginx::HTTP2->new();\nmy $sid = $s->new_stream({ body => 'TEST' });\nmy $frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\nmy ($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 200, 'status');\nis($frame->{headers}->{'x-body'}, 'TEST', 'request body');\n\n($frame) = grep { $_->{type} eq \"DATA\" } @$frames;\nis($frame->{data}, 'SEE-THIS', 'response body');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/h2_request_body_preread.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for HTTP/2 protocol with preread request body.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse Test::Nginx::HTTP2;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http http_v2 proxy limit_req/)->plan(9);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    limit_req_zone   $binary_remote_addr  zone=req:1m rate=20r/m;\n\n    server {\n        listen       127.0.0.1:8080 http2;\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        http2_body_preread_size 10;\n\n        location /t { }\n        location / {\n            add_header X-Body $request_body;\n            proxy_pass http://127.0.0.1:8081/t;\n\n            location /req {\n                limit_req  zone=req burst=2;\n                proxy_pass http://127.0.0.1:8081/t;\n            }\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8082 http2;\n        server_name  localhost;\n\n        http2_body_preread_size 0;\n\n        location / {\n            add_header X-Body $request_body;\n            proxy_pass http://127.0.0.1:8081/t;\n\n            location /req {\n                limit_req  zone=req burst=2;\n                proxy_pass http://127.0.0.1:8081/t;\n            }\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8083 http2;\n        server_name  localhost;\n\n        location / {\n            add_header X-Body $request_body;\n            proxy_pass http://127.0.0.1:8081/t;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('t', '');\n# suppress deprecation warning\nopen OLDERR, \">&\", \\*STDERR; close STDERR;\n$t->run();\nopen STDERR, \">&\", \\*OLDERR;\n\n###############################################################################\n\n# request body within preread size (that is, stream window)\n\nmy $s = Test::Nginx::HTTP2->new();\nmy $sid = $s->new_stream({ body => 'TEST' });\nmy $frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\nmy ($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{'x-body'}, 'TEST', 'within preread');\n\n# request body beyond preread size\n# RST_STREAM expected due stream window violation\n\nTODO: {\nlocal $TODO = 'not yet';\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ body => 'TEST' x 10 });\n$frames = $s->read(all => [{ type => 'RST_STREAM' }], wait => 0.5);\n\n($frame) = grep { $_->{type} eq \"RST_STREAM\" } @$frames;\nis($frame->{code}, 3, 'beyond preread - FLOW_CONTROL_ERROR');\n\n}\n\n# within preread size - limited\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/req' });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n$sid = $s->new_stream({ path => '/req', body => 'TEST' });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{'x-body'}, 'TEST', 'within preread limited');\n\n# processing request body without END_STREAM in preread\n\n$sid = $s->new_stream({ path => '/req', body_more => 1, continuation => 1 });\n$s->h2_continue($sid,\n\t{ headers => [{ name => 'content-length', value => '8' }]});\n\n$s->h2_body('SEE', { body_more => 1 });\n$s->read(all => [{ type => 'WINDOW_UPDATE' }]);\n\n$s->h2_body('-THIS');\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{'x-body'}, 'SEE-THIS', 'within preread limited - more');\n\n# beyond preread size - limited\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/req', body => 'TEST' x 10 });\n$frames = $s->read(all => [{ type => 'RST_STREAM' }]);\n\n($frame) = grep { $_->{type} eq \"RST_STREAM\" } @$frames;\nis($frame->{code}, 3, 'beyond preread limited - FLOW_CONTROL_ERROR');\n\n\n# zero preread size\n\nTODO: {\nlocal $TODO = 'not yet';\n\n$s = Test::Nginx::HTTP2->new(port(8082));\n$sid = $s->new_stream({ body => 'TEST' });\n$frames = $s->read(all => [{ type => 'RST_STREAM' }], wait => 0.5);\n\n($frame) = grep { $_->{type} eq \"RST_STREAM\" } @$frames;\nis($frame->{code}, 3, 'zero preread - FLOW_CONTROL_ERROR');\n\n}\n\n# zero preread size - limited\n\n$s = Test::Nginx::HTTP2->new(port(8082));\n$sid = $s->new_stream({ path => '/req', body => 'TEST' });\n$frames = $s->read(all => [{ type => 'RST_STREAM' }]);\n\n($frame) = grep { $_->{type} eq \"RST_STREAM\" } @$frames;\nis($frame->{code}, 3, 'zero preread limited - FLOW_CONTROL_ERROR');\n\n\n# REFUSED_STREAM on request body prior SETTINGS acknowledgement\n\n$s = Test::Nginx::HTTP2->new(port(8080), pure => 1);\n$sid = $s->new_stream({ body => 'TEST' });\n$frames = $s->read(all => [{ type => 'RST_STREAM' }]);\n\n($frame) = grep { $_->{type} eq \"RST_STREAM\" } @$frames;\nis($frame->{code}, 7, 'no SETTINGS ack - REFUSED_STREAM');\n\n# default preread size - no REFUSED_STREAM expected\n\n$s = Test::Nginx::HTTP2->new(port(8083), pure => 1);\n$sid = $s->new_stream({ body => 'TEST' });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{'x-body'}, 'TEST', 'no SETTINGS ack - default preread');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/h2_server_push.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for HTTP/2 server push.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse Test::Nginx::HTTP2;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http http_v2 proxy rewrite gzip/)->plan(54)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080 http2;\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        location /prio {\n            http2_push /t1;\n            http2_push /t2;\n            return 204;\n        }\n\n        location /expl {\n            http2_push /push;\n            http2_push /push2;\n\n            location /expl/off {\n                http2_push off;\n            }\n        }\n\n        location /preload {\n            http2_push_preload on;\n            add_header Link \"</push>; rel=preload\";\n            add_header X-Link $sent_http_link;\n            return 200 SEE-THIS;\n        }\n\n        location /preload2 {\n            http2_push_preload on;\n            add_header Link \"</push>; rel=preload\";           # valid\n            add_header Link \"</push2 >; rel=preload\";         # valid\n            add_header Link \"</push3>; rel=preloadX\";         # not\n            add_header Link '</push4>; rel=\"preload\"';        # valid\n            add_header Link '</push5>; rel=\"preloadX\"';       # not\n            add_header Link \"</push6>; rel=preload; nopush\";  # not\n            add_header Link '</push7>; rel=\"foo\"';            # not\n            add_header Link '</push7>; rel=\"foo preload\"';    # valid\n            return 200 SEE-THIS;\n        }\n\n        location /preload/many {\n            http2_push_preload on;\n            add_header Link \"</push>; rel=preload, </push2>; rel=preload\";\n            add_header Link \"</push3>, </push4>; rel=preload\";\n            return 200 SEE-THIS;\n        }\n\n        location /preload/proxy {\n            http2_push_preload on;\n            proxy_pass http://127.0.0.1:8081/proxied;\n        }\n\n        location /proxied {\n            add_header Link \"</push>; rel=preload\";\n            add_header Link \"</push2>; rel=preload\";\n            return 200 SEE-THIS;\n        }\n\n        location /both {\n            http2_push /push;\n            http2_push_preload on;\n            add_header Link \"</push>; rel=preload\";\n            return 200 SEE-THIS;\n        }\n\n        location /arg {\n            http2_push $arg_push;\n            return 204;\n        }\n\n        location /continuation {\n            http2_push /push?arg=\"$args$args$args$args\";\n            return 204;\n        }\n\n        location /push {\n            return 200 PROMISED;\n        }\n\n        location /gzip.html {\n            gzip on;\n            gzip_min_length 0;\n            return 200 PROMISED;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8082 http2;\n        server_name  max_pushes;\n\n        http2_max_concurrent_pushes 2;\n        http2_push /push;\n        http2_push /push;\n        http2_push /push;\n    }\n}\n\nEOF\n\n$t->write_file('t1', join('', map { sprintf \"X%04dXXX\", $_ } (1 .. 8202)));\n$t->write_file('t2', 'SEE-THIS');\n$t->write_file('explf', join('', map { sprintf \"X%06dXXX\", $_ } (1 .. 6553)));\n\n$t->run();\n\n###############################################################################\n\n# 6.6.  PUSH_PROMISE\n#   PUSH_PROMISE frames MUST only be sent on a peer-initiated stream that\n#   is in either the \"open\" or \"half-closed (remote)\" state.\n\n# preload & format\n\nmy $s = Test::Nginx::HTTP2->new();\nmy $sid = $s->new_stream({ path => '/preload' });\nmy $frames = $s->read(all => [{ sid => 1, fin => 1 }, { sid => 2, fin => 1 }]);\n\nmy ($frame) = grep { $_->{type} eq \"PUSH_PROMISE\" } @$frames;\nok($frame, 'push promise');\nis($frame->{headers}->{':authority'}, 'localhost', 'authority');\nis($frame->{headers}->{':scheme'}, 'http', 'scheme');\nis($frame->{headers}->{':method'}, 'GET', 'method');\nis($frame->{headers}->{':path'}, '/push', 'path');\nis($frame->{flags}, 4, 'flags');\nis($frame->{promised}, 2, 'promised stream');\n\n($frame) = grep { $_->{type} eq \"DATA\" && $_->{sid} eq 2 } @$frames;\nis($frame->{data}, 'PROMISED', 'promised stream payload');\n\n($frame) = grep { $_->{type} eq \"HEADERS\" && $_->{sid} eq $sid } @$frames;\nis($frame->{headers}->{'x-link'}, '</push>; rel=preload', 'sent_http_link');\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/preload2' });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\nis(grep({ $_->{type} eq \"PUSH_PROMISE\" } @$frames), 4, 'preload 2');\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/preload/many' });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\nis(grep({ $_->{type} eq \"PUSH_PROMISE\" } @$frames), 3, 'preload many');\n\n# preload proxy\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/preload/proxy' });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\nis(grep({ $_->{type} eq \"PUSH_PROMISE\" } @$frames), 2, 'preload proxy');\n\n# both h2_push & preload\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/both' });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\nis(grep({ $_->{type} eq \"PUSH_PROMISE\" } @$frames), 2, 'h2_push and preload');\n\n# h2_push\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/expl' });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"PUSH_PROMISE\" } @$frames;\nok($frame, 'h2_push only');\n\n# h2_push off\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/expl/off' });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"PUSH_PROMISE\" } @$frames;\nok(!$frame, 'h2_push off');\n\n# h2_push $var\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/arg?push=/push' });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n($frame) = grep { $_->{type} eq \"PUSH_PROMISE\" } @$frames;\nok($frame, 'h2_push variable');\n\n$sid = $s->new_stream({ path => '/arg?push=' });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n($frame) = grep { $_->{type} eq \"PUSH_PROMISE\" } @$frames;\nok(!$frame, 'h2_push variable empty');\n\n$sid = $s->new_stream({ path => '/arg?push=off' });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n($frame) = grep { $_->{type} eq \"PUSH_PROMISE\" } @$frames;\nok(!$frame, 'h2_push variable off');\n\n$sid = $s->new_stream({ path => '/arg?push=foo' });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n($frame) = grep { $_->{type} eq \"PUSH_PROMISE\" } @$frames;\nok(!$frame, 'h2_push variable relative path');\n\n# SETTINGS_ENABLE_PUSH\n\n$s = Test::Nginx::HTTP2->new();\n$s->h2_settings(0, 0x2 => 0);\n$sid = $s->new_stream({ path => '/expl' });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"PUSH_PROMISE\" } @$frames;\nok(!$frame, 'push setting disabled');\n\n$s->h2_settings(0, 0x2 => 1);\n$sid = $s->new_stream({ path => '/expl' });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"PUSH_PROMISE\" } @$frames;\nok($frame, 'push setting enabled');\n\n$s->h2_settings(0, 0x2 => 42);\n$frames = $s->read(all => [{ type => 'GOAWAY' }]);\n\n($frame) = grep { $_->{type} =~ \"GOAWAY\" } @$frames;\nis($frame->{'code'}, 1, 'push setting invalid - GOAWAY protocol error');\ncmp_ok($frame->{'last_sid'}, '<', 5, 'push setting invalid - last sid');\n\n# SETTINGS_MAX_CONCURRENT_STREAMS\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/expl' });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\nis(grep({ $_->{type} eq \"PUSH_PROMISE\" } @$frames), 2, 'max pushes default');\n\n$s = Test::Nginx::HTTP2->new();\n$s->h2_settings(0, 0x3 => 1);\n$sid = $s->new_stream({ path => '/expl' });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\nis(grep({ $_->{type} eq \"PUSH_PROMISE\" } @$frames), 1, 'max pushes limited');\n\n$s = Test::Nginx::HTTP2->new();\n$s->h2_settings(0, 0x3 => 0);\n$sid = $s->new_stream({ path => '/expl' });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\nis(grep({ $_->{type} eq \"PUSH_PROMISE\" } @$frames), 0, 'max pushes disabled');\n\nTODO: {\ntodo_skip 'long tests with aio', 6 unless $ENV{TEST_NGINX_UNSAFE}\n\tor $t->read_file('nginx.conf') !~ /aio (on|threads)/;\nlocal $TODO = 'not yet' if $t->read_file('nginx.conf') =~ /aio (on|threads)/;\n\n# server push flow control & rst\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/explf' });\n$frames = $s->read(all => [\n\t{ sid => 1, fin => 1 },\n\t{ sid => 2, length => 5 },\n\t{ sid => 4, fin => 4 }]);\n\n($frame) = grep { $_->{type} eq \"DATA\" && $_->{sid} == 2 } @$frames;\nis($frame->{length}, 5, 'flow control - pushed stream limited');\nis($frame->{flags}, 0, 'flow control - pushed stream flags');\n\n($frame) = grep { $_->{type} eq \"DATA\" && $_->{sid} == 4 } @$frames;\nok(!$frame, 'flow control - no window for next stream');\n\n# window update\n\n$s->h2_window(2);\n\n$frames = $s->read(all => [{ length => 2 }]);\n($frame) = grep { $_->{type} eq \"DATA\" && $_->{sid} == 2 } @$frames;\nis($frame->{length}, 2, 'window update');\n\n# client refused stream\n\n$s->h2_rst(4, 7);\n$s->h2_window(2**16);\n\n$frames = $s->read(all => [{ sid => 2, length => 1 }]);\npush @$frames, @{ $s->read(all => [{ sid => 4, fin => 1 }], wait => 0.2) };\n\n($frame) = grep { $_->{type} eq \"DATA\" && $_->{sid} == 2 } @$frames;\nis($frame->{length}, 1, 'pushed response flow control');\nis($frame->{flags}, 1, 'pushed response END_STREAM');\n\n}\n\n($frame) = grep { $_->{type} eq \"DATA\" && $_->{sid} == 4 } @$frames;\nok(!$frame, 'rst pushed stream');\n\nTODO: {\ntodo_skip 'long tests with aio', 2 unless $ENV{TEST_NGINX_UNSAFE}\n\tor $t->read_file('nginx.conf') !~ /aio (on|threads)/;\nlocal $TODO = 'not yet' if $t->read_file('nginx.conf') =~ /aio (on|threads)/;\n\n# priority\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/prio' });\n$frames = $s->read(all => [{ length => 2**16 - 1 }, { sid => 4, fin => 4 }]);\n\n$s->h2_priority(16, 2, 4);\n\n$s->h2_window(2**17, 2);\n$s->h2_window(2**17, 4);\n$s->h2_window(2**17);\n\n$frames = $s->read(all => [{ sid => 2, fin => 1 }, { sid => 4, fin => 1 }]);\nmy @data = grep { $_->{type} eq \"DATA\" } @$frames;\nis(join(' ', map { $_->{sid} } @data), \"4 2\", 'priority 1');\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/prio' });\n$frames = $s->read(all => [{ length => 2**16 - 1 }, { sid => 4, fin => 4 }]);\n\n$s->h2_priority(16, 4, 2);\n\n$s->h2_window(2**17, 2);\n$s->h2_window(2**17, 4);\n$s->h2_window(2**17);\n\n$frames = $s->read(all => [{ sid => 2, fin => 1 }, { sid => 4, fin => 1 }]);\n@data = grep { $_->{type} eq \"DATA\" } @$frames;\nis(join(' ', map { $_->{sid} } @data), \"2 4\", 'priority 2');\n\n}\n\n# http2_max_concurrent_pushes\n\n$s = Test::Nginx::HTTP2->new(port(8082));\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/', mode => 0 },\n\t{ name => ':authority', value => 'max_pushes', mode => 1 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\nis(grep({ $_->{type} eq \"PUSH_PROMISE\" } @$frames), 2, 'http2 max pushes lim');\n\n$s = Test::Nginx::HTTP2->new(port(8082));\n$s->h2_settings(0, 0x3 => 1);\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/', mode => 0 },\n\t{ name => ':authority', value => 'max_pushes', mode => 1 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\nis(grep({ $_->{type} eq \"PUSH_PROMISE\" } @$frames), 1, 'http2 max pushes 2');\n\n# missing request header ':authority'\n\n$s = Test::Nginx::HTTP2->new(port(8082));\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/', mode => 0 }]});\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 400, 'incomplete headers');\n\n# gzip tests\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'http', mode => 0 },\n\t{ name => ':path', value => '/arg?push=/gzip.html' },\n\t{ name => ':authority', value => 'localhost', mode => 1 },\n\t{ name => 'accept-encoding', value => 'gzip' }]});\n$frames = $s->read(all => [{ sid => 2, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"PUSH_PROMISE\" && $_->{sid} == $sid } @$frames;\nis($frame->{headers}->{'accept-encoding'}, 'gzip', 'gzip - push promise');\n\n($frame) = grep { $_->{type} eq \"HEADERS\" && $_->{sid} == 2 } @$frames;\nis($frame->{headers}->{'content-encoding'}, 'gzip', 'gzip - headers');\n\n($frame) = grep { $_->{type} eq \"DATA\" && $_->{sid} == 2 } @$frames;\ngunzip_like($frame->{data}, qr/^PROMISED\\Z/, 'gzip - response');\n\n# scheme https\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\n\t{ name => ':scheme', value => 'https', mode => 0 },\n\t{ name => ':path', value => '/preload' },\n\t{ name => ':authority', value => 'localhost', mode => 1 }]});\n$frames = $s->read(all => [{ sid => 2, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"PUSH_PROMISE\" && $_->{sid} == $sid } @$frames;\nis($frame->{headers}->{':scheme'}, 'https', 'scheme https');\n\n# CONTINUATION\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/continuation?x=' . ('X' x 4096) });\n$frames = $s->read(all => [{ sid => 1, fin => 1 }, { sid => 2, fin => 1 }]);\n\n@$frames = grep { $_->{promised} } @$frames;\nis(@$frames, 2, 'continuation - frames');\n\n$frame = shift @$frames;\nis($frame->{type}, 'PUSH_PROMISE', 'continuation - PUSH_PROMISE');\nis($frame->{length}, 16384, 'continuation - PUSH_PROMISE length');\nis($frame->{flags}, 0, 'continuation - PUSH_PROMISE flags');\nis($frame->{sid}, $sid, 'continuation - PUSH_PROMISE sid');\nis($frame->{promised}, 2, 'continuation - promised stream');\n\n$frame = shift @$frames;\nis($frame->{type}, 'CONTINUATION', 'continuation - CONTINUATION');\nis($frame->{flags}, 4, 'continuation - CONTINUATION flags');\nis($frame->{headers}->{':authority'}, 'localhost', 'continuation - authority');\nis($frame->{headers}->{':scheme'}, 'http', 'continuation - scheme');\nis($frame->{headers}->{':method'}, 'GET', 'continuation - method');\nlike($frame->{headers}->{':path'}, qr!^/push!, 'continuation - path');\n\n###############################################################################\n\nsub gunzip_like {\n\tmy ($in, $re, $name) = @_;\n\n\tSKIP: {\n\t\teval { require IO::Uncompress::Gunzip; };\n\t\tTest::More::skip(\n\t\t\t\"IO::Uncompress::Gunzip not installed\", 1) if $@;\n\n\t\tmy $out;\n\n\t\tIO::Uncompress::Gunzip::gunzip(\\$in => \\$out);\n\n\t\tif ($in =~ $re) {\n\t\t\tfail($name);\n\t\t\treturn;\n\t\t}\n\n\t\tlike($out, $re, $name);\n\t}\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/h2_server_tokens.t",
    "content": "#!/usr/bin/perl\n\n# (C) Andrey Zelenkov\n# (C) Nginx, Inc.\n\n# Tests for HTTP/2 protocol with server_tokens directive.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse Test::Nginx::HTTP2;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http http_v2 rewrite/)->plan(12)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080 http2;\n        server_name  localhost;\n\n        location /200 {\n            return 200;\n        }\n\n        location /404 {\n            return 404;\n        }\n\n        location /off {\n            server_tokens off;\n\n            location /off/200 {\n                return 200;\n            }\n\n            location /off/404 {\n                return 404;\n            }\n        }\n\n        location /on {\n            server_tokens on;\n\n            location /on/200 {\n                return 200;\n            }\n\n            location /on/404 {\n                return 404;\n            }\n        }\n\n        location /b {\n            server_tokens build;\n\n            location /b/200 {\n                return 200;\n            }\n\n            location /b/404 {\n                return 404;\n            }\n        }\n    }\n}\n\nEOF\n\n# suppress deprecation warning\nopen OLDERR, \">&\", \\*STDERR; close STDERR;\n$t->run();\nopen STDERR, \">&\", \\*OLDERR;\n\n###############################################################################\n\nmy $re = qr/nginx\\/\\d+\\.\\d+\\.\\d+/;\n\nlike(header_server('/200'), qr/^$re$/, 'http2 tokens default 200');\nlike(header_server('/404'), qr/^$re$/, 'http2 tokens default 404');\nlike(body('/404'), qr/$re/, 'http2 tokens default 404 body');\n\nis(header_server('/off/200'), 'nginx', 'http2 tokens off 200');\nis(header_server('/off/404'), 'nginx', 'http2 tokens off 404');\nlike(body('/off/404'), qr/nginx(?!\\/)/, 'http2 tokens off 404 body');\n\nlike(header_server('/on/200'), qr/^$re$/, 'http2 tokens on 200');\nlike(header_server('/on/404'), qr/^$re$/, 'http2 tokens on 404');\nlike(body('/on/404'), $re, 'http2 tokens on 404 body');\n\n$re = qr/$re \\(.*\\)/ if $t->has_module('--build=');\n\nlike(header_server('/b/200'), qr/^$re$/, 'http2 tokens build 200');\nlike(header_server('/b/404'), qr/^$re$/, 'http2 tokens build 404');\nlike(body('/b/404'), qr/$re/, 'http2 tokens build 404 body');\n\n###############################################################################\n\nsub header_server {\n\tmy ($path) = shift;\n\n\tmy $s = Test::Nginx::HTTP2->new();\n\tmy $sid = $s->new_stream({ path => $path });\n\tmy $frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n\tmy ($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\n\treturn $frame->{headers}->{'server'};\n}\n\nsub body {\n\tmy ($path) = shift;\n\n\tmy $s = Test::Nginx::HTTP2->new();\n\tmy $sid = $s->new_stream({ path => $path });\n\tmy $frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n\tmy ($frame) = grep { $_->{type} eq \"DATA\" } @$frames;\n\treturn $frame->{'data'};\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/h2_ssl.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for HTTP/2 protocol with ssl.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse Socket qw/ SOL_SOCKET SO_RCVBUF /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse Test::Nginx::HTTP2;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http http_ssl http_v2 socket_ssl_alpn/)\n\t->has_daemon('openssl');\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080 http2 ssl;\n        server_name  localhost;\n\n        ssl_certificate_key localhost.key;\n        ssl_certificate localhost.crt;\n\n        lingering_close off;\n\n        location / { }\n    }\n}\n\nEOF\n\n\n\n\n$t->write_file('openssl.conf', <<EOF);\n[ req ]\ndefault_bits = 2048\nencrypt_key = no\ndistinguished_name = req_distinguished_name\n[ req_distinguished_name ]\nEOF\n\nmy $d = $t->testdir();\n\nforeach my $name ('localhost') {\n\tsystem('openssl req -x509 -new '\n\t\t. \"-config $d/openssl.conf -subj /CN=$name/ \"\n\t\t. \"-out $d/$name.crt -keyout $d/$name.key \"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create certificate for $name: $!\\n\";\n}\n\n$t->write_file('index.html', '');\n$t->write_file('tbig.html',\n\tjoin('', map { sprintf \"XX%06dXX\", $_ } (1 .. 500000)));\n\nopen OLDERR, \">&\", \\*STDERR; close STDERR;\n$t->run();\nopen STDERR, \">&\", \\*OLDERR;\n\nplan(skip_all => 'no ALPN negotiation') unless defined getconn();\n$t->plan(4);\n\n###############################################################################\n\nSKIP: {\nskip 'LibreSSL too old', 1\n\tif $t->has_module('LibreSSL')\n\tand not $t->has_feature('libressl:3.4.0');\nskip 'OpenSSL too old', 1\n\tif $t->has_module('OpenSSL')\n\tand not $t->has_feature('openssl:1.1.0');\n\nTODO: {\nlocal $TODO = 'not yet' unless $t->has_version('1.21.4');\n\nok(!get_ssl_socket(['unknown']), 'alpn rejected');\n\n}\n\n}\n\nlike(http_get('/', socket => get_ssl_socket(['http/1.1'])),\n\tqr/200 OK/, 'alpn to HTTP/1.1 fallback');\n\nmy $s = getconn(['http/1.1', 'h2']);\nmy $sid = $s->new_stream();\nmy $frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\nmy ($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nis($frame->{headers}->{':status'}, 200, 'alpn to HTTP/2');\n# h2c preface on ssl-enabled socket is rejected as invalid HTTP/1.x request,\n# ensure that HTTP/2 auto-detection doesn't kick in\nlike(http(\"PRI * HTTP/2.0\\r\\n\\r\\nSM\\r\\n\\r\\n\"), qr/Bad Request/,\n\t'no h2c on ssl socket');\n\n# client cancels last stream after HEADERS has been created,\n# while some unsent data was left in the SSL buffer\n# HEADERS frame may stuck in SSL buffer and won't be sent producing alert\n\n$s = getconn(['http/1.1', 'h2']);\n$s->{socket}->setsockopt(SOL_SOCKET, SO_RCVBUF, 1024*1024) or die $!;\n$sid = $s->new_stream({ path => '/tbig.html' });\n\nselect undef, undef, undef, 0.2;\n$s->h2_rst($sid, 8);\n\n$sid = $s->new_stream({ path => '/tbig.html' });\n\nselect undef, undef, undef, 0.2;\n$s->h2_rst($sid, 8);\n\n$t->stop();\n\n###############################################################################\n\nsub getconn {\n\tmy ($alpn) = @_;\n\t$alpn = ['h2'] if !defined $alpn;\n\n\tmy $sock = get_ssl_socket($alpn);\n\tmy $s = Test::Nginx::HTTP2->new(undef, socket => $sock)\n\t\tif $sock->alpn_selected();\n}\n\nsub get_ssl_socket {\n\tmy ($alpn) = @_;\n\tmy $s;\n\n\teval {\n\t\tlocal $SIG{ALRM} = sub { die \"timeout\\n\" };\n\t\tlocal $SIG{PIPE} = sub { die \"sigpipe\\n\" };\n\t\talarm(8);\n\t\t$s = IO::Socket::SSL->new(\n\t\t\tProto => 'tcp',\n\t\t\tPeerAddr => '127.0.0.1',\n\t\t\tPeerPort => port(8080),\n\t\t\tSSL_verify_mode => IO::Socket::SSL::SSL_VERIFY_NONE(),\n\t\t\tSSL_alpn_protocols => $alpn,\n\t\t\tSSL_error_trap => sub { die $_[1] }\n\t\t);\n\t\talarm(0);\n\t};\n\talarm(0);\n\n\tif ($@) {\n\t\tlog_in(\"died: $@\");\n\t\treturn undef;\n\t}\n\n\treturn $s;\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/h2_ssl_proxy_cache.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for HTTP/2 protocol with ssl and http proxy cache.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse Test::Nginx::HTTP2;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()\n\n\t->has(qw/http http_ssl http_v2 proxy cache socket_ssl/)\n\t->has_daemon('openssl');\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    proxy_cache_path   %%TESTDIR%%/cache  keys_zone=NAME:1m;\n\n    server {\n        listen       127.0.0.1:8080 http2 ssl sndbuf=32k;\n        server_name  localhost;\n\n        ssl_certificate_key localhost.key;\n        ssl_certificate localhost.crt;\n\n        send_timeout 1s;\n        lingering_close off;\n\n        location / {\n            proxy_pass   http://127.0.0.1:8081;\n            proxy_cache  NAME;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081 sndbuf=64k;\n        server_name  localhost;\n\n        location / { }\n    }\n}\n\nEOF\n\n$t->write_file('openssl.conf', <<EOF);\n[ req ]\ndefault_bits = 2048\nencrypt_key = no\ndistinguished_name = req_distinguished_name\n[ req_distinguished_name ]\nEOF\n\nmy $d = $t->testdir();\n\nforeach my $name ('localhost') {\n\tsystem('openssl req -x509 -new '\n\t\t. \"-config $d/openssl.conf -subj /CN=$name/ \"\n\t\t. \"-out $d/$name.crt -keyout $d/$name.key \"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create certificate for $name: $!\\n\";\n}\n\n$t->write_file('tbig.html',\n\tjoin('', map { sprintf \"XX%06dXX\", $_ } (1 .. 500000)));\n\nopen OLDERR, \">&\", \\*STDERR; close STDERR;\n$t->run();\nopen STDERR, \">&\", \\*OLDERR;\n\nplan(skip_all => 'no ALPN/NPN negotiation') unless defined getconn(port(8080));\n$t->plan(1);\n\n###############################################################################\n\n# client cancels stream with a cacheable request sent to upstream causing alert\n\nmy $s = getconn(port(8080));\nok($s, 'ssl connection');\n\nmy $sid = $s->new_stream();\n$s->h2_rst($sid, 8);\n\n# large response may stuck in SSL buffer and won't be sent producing alert\n\nmy $s2 = getconn(port(8080));\n$sid = $s2->new_stream({ path => '/tbig.html' });\n$s2->h2_window(2**30, $sid);\n$s2->h2_window(2**30);\n\nselect undef, undef, undef, 0.2;\n\n$t->stop();\n\n###############################################################################\n\nsub getconn {\n\tmy ($port) = @_;\n\tmy $s;\n\n\teval {\n\t\tmy $sock = Test::Nginx::HTTP2::new_socket($port, SSL => 1,\n\t\t\talpn => 'h2');\n\t\t$s = Test::Nginx::HTTP2->new($port, socket => $sock)\n\t\t\tif $sock->alpn_selected();\n\t};\n\n\treturn $s if defined $s;\n\n\teval {\n\t\tmy $sock = Test::Nginx::HTTP2::new_socket($port, SSL => 1,\n\t\t\tnpn => 'h2');\n\t\t$s = Test::Nginx::HTTP2->new($port, socket => $sock)\n\t\t\tif $sock->next_proto_negotiated();\n\t};\n\n\treturn $s;\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/h2_ssl_proxy_protocol.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for HTTP/2 protocol with proxy_protocol.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse Socket qw/ CRLF /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse Test::Nginx::HTTP2;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()\n\t->has(qw/http http_ssl http_v2 realip socket_ssl_alpn/)\n\t->has_daemon('openssl')->plan(3);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080 proxy_protocol http2 ssl;\n        server_name  localhost;\n\n        ssl_certificate_key localhost.key;\n        ssl_certificate localhost.crt;\n\n        location /pp {\n            set_real_ip_from 127.0.0.1/32;\n            real_ip_header proxy_protocol;\n            alias %%TESTDIR%%/t.html;\n            add_header X-PP $remote_addr;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('openssl.conf', <<EOF);\n[ req ]\ndefault_bits = 2048\nencrypt_key = no\ndistinguished_name = req_distinguished_name\n[ req_distinguished_name ]\nEOF\n\nmy $d = $t->testdir();\n\nforeach my $name ('localhost') {\n\tsystem('openssl req -x509 -new '\n\t\t. \"-config $d/openssl.conf -subj /CN=$name/ \"\n\t\t. \"-out $d/$name.crt -keyout $d/$name.key \"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create certificate for $name: $!\\n\";\n}\n\n$t->write_file('t.html', 'SEE-THIS');\n\nopen OLDERR, \">&\", \\*STDERR; close STDERR;\n$t->run();\nopen STDERR, \">&\", \\*OLDERR;\n\n###############################################################################\n\nmy $proxy = 'PROXY TCP4 192.0.2.1 192.0.2.2 1234 5678' . CRLF;\nmy $sock = http($proxy, start => 1);\nhttp('', start => 1, socket => $sock, SSL => 1, SSL_alpn_protocols => ['h2']);\n\nSKIP: {\nskip 'no ALPN negotiation', 2 unless $sock->alpn_selected();\n\nmy $s = Test::Nginx::HTTP2->new(undef, socket => $sock);\nmy $sid = $s->new_stream({ path => '/pp' });\nmy $frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\nmy ($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\nok($frame, 'PROXY HEADERS frame');\nis($frame->{headers}->{'x-pp'}, '192.0.2.1', 'PROXY remote addr');\n\n}\n\n$sock->close();\n\n# invalid PROXY protocol string\n\n$proxy = 'BOGUS TCP4 192.0.2.1 192.0.2.2 1234 5678' . CRLF;\nok(!http($proxy), 'PROXY invalid protocol');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/h2_ssl_variables.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for HTTP/2 protocol with ssl.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse Test::Nginx::HTTP2;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\n\nmy $t = Test::Nginx->new()->has(qw/http http_ssl http_v2 rewrite socket_ssl/)\n\t->has_daemon('openssl')->plan(8);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080 http2 ssl;\n        server_name  localhost;\n\n        ssl_certificate_key localhost.key;\n        ssl_certificate localhost.crt;\n\n        location /h2 {\n            return 200 $http2;\n        }\n        location /sp {\n            return 200 $server_protocol;\n        }\n        location /scheme {\n            return 200 $scheme;\n        }\n        location /https {\n            return 200 $https;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('openssl.conf', <<EOF);\n[ req ]\ndefault_bits = 2048\nencrypt_key = no\ndistinguished_name = req_distinguished_name\n[ req_distinguished_name ]\nEOF\n\nmy $d = $t->testdir();\n\nforeach my $name ('localhost') {\n\tsystem('openssl req -x509 -new '\n\t\t. \"-config $d/openssl.conf -subj /CN=$name/ \"\n\t\t. \"-out $d/$name.crt -keyout $d/$name.key \"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create certificate for $name: $!\\n\";\n}\n\nopen OLDERR, \">&\", \\*STDERR; close STDERR;\n$t->run();\nopen STDERR, \">&\", \\*OLDERR;\n\n###############################################################################\n\nmy ($s, $sid, $frames, $frame);\n\nmy $has_npn = eval { Test::Nginx::HTTP2::new_socket(port(8080), SSL => 1,\n\tnpn => 'h2')->next_proto_negotiated() };\nmy $has_alpn = eval { Test::Nginx::HTTP2::new_socket(port(8080), SSL => 1,\n\talpn => 'h2')->alpn_selected() };\n\n# SSL/TLS connection, NPN\n\nSKIP: {\nskip 'OpenSSL NPN support required', 1 unless $has_npn;\n\n$s = Test::Nginx::HTTP2->new(port(8080), SSL => 1, npn => 'h2');\n$sid = $s->new_stream({ path => '/h2' });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"DATA\" } @$frames;\nis($frame->{data}, 'h2', 'http variable - npn');\n\n}\n\n# SSL/TLS connection, ALPN\n\nSKIP: {\nskip 'OpenSSL ALPN support required', 1 unless $has_alpn;\n\n$s = Test::Nginx::HTTP2->new(port(8080), SSL => 1, alpn => 'h2');\n$sid = $s->new_stream({ path => '/h2' });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"DATA\" } @$frames;\nis($frame->{data}, 'h2', 'http variable - alpn');\n\n}\n\n# $server_protocol - SSL/TLS connection, NPN\n\nSKIP: {\nskip 'OpenSSL NPN support required', 1 unless $has_npn;\n\n$s = Test::Nginx::HTTP2->new(port(8080), SSL => 1, npn => 'h2');\n$sid = $s->new_stream({ path => '/sp' });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"DATA\" } @$frames;\nis($frame->{data}, 'HTTP/2.0', 'server_protocol variable - npn');\n\n}\n\n# $server_protocol - SSL/TLS connection, ALPN\n\nSKIP: {\nskip 'OpenSSL ALPN support required', 1 unless $has_alpn;\n\n$s = Test::Nginx::HTTP2->new(port(8080), SSL => 1, alpn => 'h2');\n$sid = $s->new_stream({ path => '/sp' });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"DATA\" } @$frames;\nis($frame->{data}, 'HTTP/2.0', 'server_protocol variable - alpn');\n\n}\n\n# $scheme - SSL/TLS connection, NPN\n\nSKIP: {\nskip 'OpenSSL NPN support required', 1 unless $has_npn;\n\n$s = Test::Nginx::HTTP2->new(port(8080), SSL => 1, npn => 'h2');\n$sid = $s->new_stream({ path => '/scheme' });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"DATA\" } @$frames;\nis($frame->{data}, 'https', 'scheme variable - npn');\n\n}\n\n# $scheme - SSL/TLS connection, ALPN\n\nSKIP: {\nskip 'OpenSSL ALPN support required', 1 unless $has_alpn;\n\n$s = Test::Nginx::HTTP2->new(port(8080), SSL => 1, alpn => 'h2');\n$sid = $s->new_stream({ path => '/scheme' });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"DATA\" } @$frames;\nis($frame->{data}, 'https', 'scheme variable - alpn');\n\n}\n\n# $https - SSL/TLS connection, NPN\n\nSKIP: {\nskip 'OpenSSL NPN support required', 1 unless $has_npn;\n\n$s = Test::Nginx::HTTP2->new(port(8080), SSL => 1, npn => 'h2');\n$sid = $s->new_stream({ path => '/https' });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"DATA\" } @$frames;\nis($frame->{data}, 'on', 'https variable - npn');\n\n}\n\n# $https - SSL/TLS connection, ALPN\n\nSKIP: {\nskip 'OpenSSL ALPN support required', 1 unless $has_alpn;\n\n$s = Test::Nginx::HTTP2->new(port(8080), SSL => 1, alpn => 'h2');\n$sid = $s->new_stream({ path => '/https' });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"DATA\" } @$frames;\nis($frame->{data}, 'on', 'https variable - alpn');\n\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/h2_ssl_verify_client.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for HTTP/2 protocol with ssl, ssl_verify_client.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse Test::Nginx::HTTP2;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\n\nmy $t = Test::Nginx->new()->has(qw/http http_ssl sni http_v2 socket_ssl_alpn/)\n\t->has_daemon('openssl');\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    ssl_certificate_key localhost.key;\n    ssl_certificate localhost.crt;\n\n    ssl_verify_client optional_no_ca;\n\n    add_header X-Verify $ssl_client_verify;\n\n    server {\n        listen       127.0.0.1:8080 ssl http2;\n        server_name  localhost;\n\n        ssl_client_certificate client.crt;\n\n        location / { }\n    }\n\n    server {\n        listen       127.0.0.1:8080 ssl http2;\n        server_name  example.com;\n\n        location / { }\n    }\n}\n\nEOF\n\n$t->write_file('openssl.conf', <<EOF);\n[ req ]\ndefault_bits = 2048\nencrypt_key = no\ndistinguished_name = req_distinguished_name\n[ req_distinguished_name ]\nEOF\n\nmy $d = $t->testdir();\n\nforeach my $name ('localhost', 'client') {\n\tsystem('openssl req -x509 -new '\n\t\t. \"-config $d/openssl.conf -subj /CN=$name/ \"\n\t\t. \"-out $d/$name.crt -keyout $d/$name.key \"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create certificate for $name: $!\\n\";\n}\n\n$t->write_file('t', 'SEE-THIS');\n\nopen OLDERR, \">&\", \\*STDERR; close STDERR;\n$t->run();\nopen STDERR, \">&\", \\*OLDERR;\n\nmy $s = get_ssl_socket();\nplan(skip_all => 'no alpn') unless $s->alpn_selected();\n$t->plan(3);\n\n###############################################################################\n\nis(get('localhost')->{'x-verify'}, 'SUCCESS', 'success');\nlike(get('example.com')->{'x-verify'}, qr/FAILED/, 'failed');\nis(get('localhost', 'example.com')->{':status'}, '421', 'misdirected');\n\n###############################################################################\n\nsub get_ssl_socket {\n\tmy ($sni) = @_;\n\tmy $s;\n\n\teval {\n\t\tlocal $SIG{ALRM} = sub { die \"timeout\\n\" };\n\t\tlocal $SIG{PIPE} = sub { die \"sigpipe\\n\" };\n\t\talarm(8);\n\t\t$s = IO::Socket::SSL->new(\n\t\t\tProto => 'tcp',\n\t\t\tPeerAddr => '127.0.0.1',\n\t\t\tPeerPort => port(8080),\n\t\t\tSSL_verify_mode => IO::Socket::SSL::SSL_VERIFY_NONE(),\n\t\t\tSSL_alpn_protocols => [ 'h2' ],\n\t\t\tSSL_hostname => $sni,\n\t\t\tSSL_cert_file => \"$d/client.crt\",\n\t\t\tSSL_key_file => \"$d/client.key\",\n\t\t\tSSL_error_trap => sub { die $_[1] }\n\t\t);\n\t\talarm(0);\n\t};\n\talarm(0);\n\n\tif ($@) {\n\t\tlog_in(\"died: $@\");\n\t\treturn undef;\n\t}\n\n\treturn $s;\n}\n\nsub get {\n\tmy ($sni, $host) = @_;\n\n\t$host = $sni if !defined $host;\n\n\tmy $s = get_ssl_socket($sni);\n\tmy $sess = Test::Nginx::HTTP2->new(port(8080), socket => $s);\n\tmy $sid = $sess->new_stream({ headers => [\n\t\t{ name => ':method', value => 'GET', mode => 0 },\n\t\t{ name => ':scheme', value => 'http', mode => 0 },\n\t\t{ name => ':path', value => '/t', mode => 1 },\n\t\t{ name => ':authority', value => $host, mode => 1 }]});\n\tmy $frames = $sess->read(all => [{ sid => $sid, fin => 1 }]);\n\n\tmy ($frame) = grep { $_->{type} eq \"HEADERS\" } @$frames;\n\treturn $frame->{'headers'};\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/h2_trailers.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for HTTP/2 trailers.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse Test::Nginx::HTTP2;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http http_v2/)->plan(22)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080 http2;\n        server_name  localhost;\n\n        location / {\n            add_trailer X-Var $host;\n        }\n\n        location /continuation {\n            # many trailers to send in parts\n            add_trailer X-LongHeader $arg_h;\n            add_trailer X-LongHeader $arg_h;\n            add_trailer X-LongHeader $arg_h;\n            add_trailer X-LongHeader $arg_h;\n            add_trailer X-LongHeader $arg_h;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('index.html', 'SEE-THIS');\n$t->write_file('empty', '');\n$t->write_file('continuation', 'SEE-THIS');\n# suppress deprecation warning\nopen OLDERR, \">&\", \\*STDERR; close STDERR;\n$t->run();\nopen STDERR, \">&\", \\*OLDERR;\n\n###############################################################################\n\nmy ($s, $sid, $frames, $frame);\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/' });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n@$frames = grep { $_->{type} =~ \"HEADERS|DATA\" } @$frames;\n\nis(@$frames, 3, 'frames');\n\n$frame = shift @$frames;\nis($frame->{headers}->{':status'}, 200, 'header');\nis($frame->{headers}->{'x-var'}, undef, 'header not trailer');\nis($frame->{flags}, 4, 'header flags');\n\n$frame = shift @$frames;\nis($frame->{data}, 'SEE-THIS', 'data');\nis($frame->{flags}, 0, 'data flags');\n\n$frame = shift @$frames;\nis($frame->{headers}->{'x-var'}, 'localhost', 'trailer');\nis($frame->{flags}, 5, 'trailer flags');\n\n# with zero content-length\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/empty' });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n@$frames = grep { $_->{type} =~ \"HEADERS|DATA\" } @$frames;\n\nis(@$frames, 2, 'no data - frames');\n\n$frame = shift @$frames;\nis($frame->{headers}->{':status'}, 200, 'no data - header');\nis($frame->{flags}, 4, 'no data - header flags');\n\n$frame = shift @$frames;\nis($frame->{headers}->{'x-var'}, 'localhost', 'no data - trailer');\nis($frame->{flags}, 5, 'no data - trailer flags');\n\n# CONTINUATION in response trailers\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/continuation?h=' . 'x' x 4000 });\n$frames = $s->read(all => [{ sid => $sid, type => 'CONTINUATION' }]);\n@$frames = grep { $_->{type} =~ \"HEADERS|CONTINUATION|DATA\" } @$frames;\n\nis(@$frames, 4, 'continuation - frames');\n\n$frame = shift @$frames;\nis($frame->{headers}->{':status'}, 200, 'continuation - header');\nis($frame->{flags}, 4, 'continuation - header flags');\n\n$frame = shift @$frames;\nis($frame->{data}, 'SEE-THIS', 'continuation - data');\nis($frame->{flags}, 0, 'continuation - data flags');\n\n$frame = shift @$frames;\nis($frame->{type}, 'HEADERS', 'continuation - trailer');\nis($frame->{flags}, 1, 'continuation - trailer flags');\n\n$frame = shift @$frames;\nis($frame->{type}, 'CONTINUATION', 'continuation - trailer continuation');\nis($frame->{flags}, 4, 'continuation - trailer continuation flags');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/h2_variables.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for HTTP/2 protocol with variables.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse Test::Nginx::HTTP2;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http http_v2 rewrite/)->plan(6)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080 http2;\n        server_name  localhost;\n\n        location /h2 {\n            return 200 $http2;\n        }\n        location /sp {\n            return 200 $server_protocol;\n        }\n        location /scheme {\n            return 200 $scheme;\n        }\n        location /https {\n            return 200 \"body $https\";\n        }\n        location /rl {\n            return 200 $request_length;\n        }\n    }\n}\n\nEOF\n\n# suppress deprecation warning\nopen OLDERR, \">&\", \\*STDERR; close STDERR;\n$t->run();\nopen STDERR, \">&\", \\*OLDERR;\n\n###############################################################################\n\n# $http2\n\nmy $s = Test::Nginx::HTTP2->new();\nmy $sid = $s->new_stream({ path => '/h2' });\nmy $frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\nmy ($frame) = grep { $_->{type} eq \"DATA\" } @$frames;\nis($frame->{data}, 'h2c', 'http variable - h2c');\n\n# $server_protocol\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/sp' });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"DATA\" } @$frames;\nis($frame->{data}, 'HTTP/2.0', 'server_protocol variable');\n\n# $scheme\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/scheme' });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"DATA\" } @$frames;\nis($frame->{data}, 'http', 'scheme variable');\n\n# $https\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ path => '/https' });\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"DATA\" } @$frames;\nis($frame->{data}, 'body ', 'https variable');\n\n# $request_length, HEADERS payload length\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\t\t# 1\n\t{ name => ':scheme', value => 'http', mode => 0 },\t\t# 1\n\t{ name => ':authority', value => 'localhost', mode => 1 },\t# 1+1+9\n\t{ name => ':path', value => '/rl', mode => 1 }]});\t\t# 1+1+3\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"DATA\" } @$frames;\nis($frame->{data}, '18', 'request length');\n\n# $request_length, HEADERS+CONTINUATION payload length\n\n$s = Test::Nginx::HTTP2->new();\n$sid = $s->new_stream({ continuation => 1, headers => [\n\t{ name => ':method', value => 'GET', mode => 0 },\t\t# 1\n\t{ name => ':authority', value => 'localhost', mode => 1 },\t# 1+1+9\n\t{ name => ':path', value => '/rl', mode => 1 }]});\t\t# 1+1+3\n$s->h2_continue($sid, { headers => [\n\t{ name => ':scheme', value => 'http', mode => 0 }]});\t\t# 1\n$frames = $s->read(all => [{ sid => $sid, fin => 1 }]);\n\n($frame) = grep { $_->{type} eq \"DATA\" } @$frames;\nis($frame->{data}, '18', 'request length');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/headers.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Maxim Dounin\n# (C) Nginx, Inc.\n\n# Tests for headers module.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy/)->plan(28)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        add_header   X-URI $uri;\n        add_header   X-Always $uri always;\n        add_header   ETag foo always;\n        add_header   ETag '' always;\n        expires      epoch;\n\n        location /t1 {\n        }\n\n        location /nx {\n        }\n\n        location /epoch {\n            expires epoch;\n        }\n\n        location /max {\n            expires max;\n        }\n\n        location /off {\n            expires off;\n        }\n\n        location /access {\n            expires 2048;\n\n            location /access_inner {\n                # inherited from outer\n            }\n        }\n\n        location /negative {\n            expires -2048;\n        }\n\n        location /daily {\n            expires @15h30m33s;\n        }\n\n        location /modified {\n            expires modified 2048;\n\n            location /modified/proxy {\n                proxy_pass http://127.0.0.1:8081/modified;\n            }\n        }\n\n        location /var {\n            expires $arg_e;\n\n            location /var_inner {\n                # inherited from outer\n            }\n\n            location /var_modified {\n                expires modified $arg_e;\n            }\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        add_header   Last-Modified \"Mon, 28 Sep 1970 06:00:00 GMT\";\n    }\n}\n\nEOF\n\n$t->write_file('t1', '');\n$t->write_file('epoch', '');\n$t->write_file('max', '');\n$t->write_file('off', '');\n$t->write_file('access', '');\n$t->write_file('access_inner', '');\n$t->write_file('negative', '');\n$t->write_file('daily', '');\n$t->write_file('modified', '');\n$t->write_file('var', '');\n$t->write_file('var_inner', '');\n$t->write_file('var_modified', '');\n\n$t->run();\n\n###############################################################################\n\nmy $r;\n\n# test for header field presence\n\n$r = http_get('/t1');\nlike($r, qr/Cache-Control/, 'good expires');\nlike($r, qr/X-URI/, 'good add_header');\nlike($r, qr/X-Always/, 'good add_header always');\nunlike($r, qr/ETag/, 'good add_header always empty');\n\n$r = http_get('/nx');\nunlike($r, qr/Cache-Control/, 'bad expires');\nunlike($r, qr/X-URI/, 'bad add_header');\nlike($r, qr/X-Always/, 'bad add_header always');\nunlike($r, qr/ETag/, 'bad add_header always empty');\n\n# various expires variants\n\nlike(http_get('/epoch'), qr/Expires:.*1970/, 'expires epoch');\nlike(http_get('/max'), qr/Expires:.*2037/, 'expires max');\nunlike(http_get('/off'), qr/Expires:/, 'expires off');\nlike(http_get('/access'), qr/max-age=2048/, 'expires access');\nlike(http_get('/access_inner'), qr/max-age=2048/, 'expires inner');\nlike(http_get('/negative'), qr/no-cache/, 'expires negative');\nlike(http_get('/daily'), qr/Expires:.*:33 GMT/, 'expires daily');\nlike(http_get('/modified'), qr/max-age=204./, 'expires modified');\n\n# \"expires modified\" with proxy\n\nlike(http_get('/modified/proxy'), qr/Expires: Mon, 28 Sep 1970 06:34:08 GMT/,\n\t'expires modified proxy');\n\n# expires with variables\n\nlike(http_get('/var?e=epoch'), qr/Expires:.*1970/, 'expires var epoch');\nlike(http_get('/var?e=max'), qr/Expires:.*2037/, 'expires var max');\nunlike(http_get('/var?e=off'), qr/Expires:/, 'expires var off');\nlike(http_get('/var?e=2048'), qr/max-age=2048/, 'expires var access');\nlike(http_get('/var_inner?e=2048'), qr/max-age=2048/, 'expires var inner');\nlike(http_get('/var?e=-2048'), qr/no-cache/, 'expires var negative');\nlike(http_get('/var?e=@33s'), qr/Expires:.*:33 GMT/, 'expires var daily');\nlike(http_get('/var_modified?e=2048'), qr/max-age=204./,\n\t'expires var modified');\n\n# some invalid cases\n\nunlike(http_get('/var'), qr/Expires/, 'expires var empty');\nunlike(http_get('/var?e=bad'), qr/Expires/, 'expires var bad');\nunlike(http_get('/var_modified?e=epoch'), qr/Expires/,\n\t'expires var modified epoch');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/http_absolute_redirect.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for absolute_redirect directive and Location escaping.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy rewrite/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    absolute_redirect off;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  on;\n\n        absolute_redirect on;\n        error_page 400 /return301;\n\n        location / { }\n\n        location /auto/ {\n            proxy_pass http://127.0.0.1:8080;\n        }\n\n        location \"/auto sp/\" {\n            proxy_pass http://127.0.0.1:8080;\n        }\n\n        location /return301 {\n            return 301 /redirect;\n        }\n\n        location /return301/name {\n            return 301 /redirect;\n            server_name_in_redirect on;\n        }\n\n        location /return301/port {\n            return 301 /redirect;\n            port_in_redirect off;\n        }\n\n        location /i/ {\n            alias %%TESTDIR%%/;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  off;\n\n        location / { }\n\n        location /auto/ {\n            proxy_pass http://127.0.0.1:8080;\n        }\n\n        location \"/auto sp/\" {\n            proxy_pass http://127.0.0.1:8080;\n        }\n\n        location '/auto \"#%<>?\\^`{|}/' {\n            proxy_pass http://127.0.0.1:8080;\n        }\n\n        location /return301 {\n            return 301 /redirect;\n        }\n\n        location /i/ {\n            alias %%TESTDIR%%/;\n        }\n    }\n}\n\nEOF\n\nmkdir($t->testdir() . '/dir');\nmkdir($t->testdir() . '/dir sp');\n\n$t->run()->plan(23);\n\n###############################################################################\n\nmy $p = port(8080);\n\nlike(get('on', '/dir'), qr!Location: http://on:$p/dir/\\x0d?$!m, 'directory');\nlike(get('on', '/i/dir'), qr!Location: http://on:$p/i/dir/\\x0d?$!m,\n\t'directory alias');\n\nTODO: {\nlocal $TODO = 'not yet' unless $t->has_version('1.21.0');\n\nlike(get('on', '/dir%20sp'), qr!Location: http://on:$p/dir%20sp/\\x0d?$!m,\n\t'directory escaped');\nlike(get('on', '/dir%20sp?a=b'),\n\tqr!Location: http://on:$p/dir%20sp/\\?a=b\\x0d?$!m,\n\t'directory escaped args');\n\n}\n\nlike(get('on', '/auto'), qr!Location: http://on:$p/auto/\\x0d?$!m, 'auto');\nlike(get('on', '/auto?a=b'), qr!Location: http://on:$p/auto/\\?a=b\\x0d?$!m,\n\t'auto args');\n\nTODO: {\nlocal $TODO = 'not yet' unless $t->has_version('1.21.0');\n\nlike(get('on', '/auto%20sp'), qr!Location: http://on:$p/auto%20sp/\\x0d?$!m,\n\t'auto escaped');\nlike(get('on', '/auto%20sp?a=b'),\n\tqr!Location: http://on:$p/auto%20sp/\\?a=b\\x0d?$!m,\n\t'auto escaped args');\n\n}\n\nlike(get('on', '/return301'), qr!Location: http://on:$p/redirect\\x0d?$!m,\n\t'return');\n\nlike(get('host', '/return301/name'), qr!Location: http://on:$p/redirect\\x0d?!m,\n\t'server_name_in_redirect on');\nlike(get('host', '/return301'), qr!Location: http://host:$p/redirect\\x0d?$!m,\n\t'server_name_in_redirect off - using host');\nmy $ph = IO::Socket::INET->new(\"127.0.0.1:$p\")->peerhost();\nlike(get('.', '/return301'), qr!Location: http://$ph:$p/redirect\\x0d?$!m,\n\t'invalid host - using local sockaddr');\nlike(get('host', '/return301/port'), qr!Location: http://host/redirect\\x0d?$!m,\n\t'port_in_redirect off');\n\nlike(get('off', '/dir'), qr!Location: /dir/\\x0d?$!m, 'off directory');\nlike(get('off', '/i/dir'), qr!Location: /i/dir/\\x0d?$!m, 'off directory alias');\n\nTODO: {\nlocal $TODO = 'not yet' unless $t->has_version('1.21.0');\n\nlike(get('off', '/dir%20sp'), qr!Location: /dir%20sp/\\x0d?$!m,\n\t'off directory escaped');\nlike(get('off', '/dir%20sp?a=b'), qr!Location: /dir%20sp/\\?a=b\\x0d?$!m,\n\t'off directory escaped args');\n\n}\n\nlike(get('off', '/auto'), qr!Location: /auto/\\x0d?$!m, 'off auto');\nlike(get('off', '/auto?a=b'), qr!Location: /auto/\\?a=b\\x0d?$!m,\n\t'off auto args');\n\nTODO: {\nlocal $TODO = 'not yet' unless $t->has_version('1.21.0');\n\nlike(get('off', '/auto%20sp'), qr!Location: /auto%20sp/\\x0d?$!m,\n\t'auto escaped');\nlike(get('off', '/auto%20sp?a=b'), qr!Location: /auto%20sp/\\?a=b\\x0d?$!m,\n\t'auto escaped args');\n\n}\n\nlike(get('off', '/return301'), qr!Location: /redirect\\x0d?$!m, 'off return');\n\n# per RFC 3986, these characters are not allowed unescaped:\n# %00-%1F, %7F-%FF, \" \", \"\"\", \"<\", \">\", \"\\\", \"^\", \"`\", \"{\", \"|\", \"}\"\n# additionally, all characters in ESCAPE_URI: \"?\", \"%\", \"#\"\n\nSKIP: {\nskip 'win32', 1 if $^O eq 'MSWin32';\n\nTODO: {\nlocal $TODO = 'not yet' unless $t->has_version('1.21.1');\n\nlike(get('off', '/auto%20%22%23%25%3C%3E%3F%5C%5E%60%7B%7C%7D'),\n\tqr!Location: /auto%20%22%23%25%3C%3E%3F%5C%5E%60%7B%7C%7D/\\x0d?$!m,\n\t'auto escaped strict');\n\n}\n\n}\n\n###############################################################################\n\nsub get {\n\tmy ($host, $uri) = @_;\n\thttp(<<EOF);\nGET $uri HTTP/1.0\nHost: $host\n\nEOF\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/http_disable_symlinks.t",
    "content": "#!/usr/bin/perl\n\n# (C) Andrey Belov\n\n# Tests for disable_symlinks directive.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\nuse POSIX;\nuse Cwd qw/ realpath /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http rewrite symlink/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  s1;\n\n        location /on/ {\n            disable_symlinks on;\n        }\n\n        location /not_owner/ {\n            disable_symlinks if_not_owner;\n        }\n\n        location /try_on/ {\n            disable_symlinks on;\n            try_files $uri $uri.html =404;\n        }\n\n        location /try_not_owner/ {\n            disable_symlinks if_not_owner;\n            try_files $uri $uri.txt =404;\n        }\n\n        location /if_on/ {\n            disable_symlinks on;\n            if (-f $request_filename) {\n                return 204;\n            }\n        }\n\n        location /if_not_owner/ {\n            disable_symlinks if_not_owner;\n            if (-f $request_filename) {\n                return 204;\n            }\n        }\n\n        location /complex/1/ {\n            disable_symlinks on;\n            alias %%TESTDIR%%/./cached/../;\n        }\n\n        location /complex/2/ {\n            disable_symlinks on;\n            alias %%TESTDIR%%//./cached/..//;\n        }\n\n        location /complex/3/ {\n            disable_symlinks on;\n            alias ///%%TESTDIR%%//./cached/..//;\n        }\n\n        location ~ (.+/)tail$ {\n            disable_symlinks on;\n            alias %%TESTDIR%%/$1;\n        }\n\n        location ~ (.+/)tailowner$ {\n            disable_symlinks if_not_owner;\n            alias %%TESTDIR%%/$1;\n        }\n\n        location ~ (.+/)tailoff$ {\n            disable_symlinks off;\n            alias %%TESTDIR%%/$1;\n        }\n\n        location /dir {\n            disable_symlinks on;\n            try_files $uri/ =404;\n        }\n\n        location /from {\n            disable_symlinks on from=$document_root;\n\n            location /from/wo_slash {\n                alias %%TESTDIR%%/dirlink;\n            }\n            location /from/with_slash/ {\n                alias %%TESTDIR%%/dirlink/;\n            }\n            location ~ ^/from/exact/(.+)$ {\n                alias %%TESTDIR%%/$1;\n            }\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  s2;\n\n        open_file_cache max=16 inactive=60s;\n        open_file_cache_valid 30s;\n        open_file_cache_min_uses 1;\n        open_file_cache_errors on;\n\n        location /cached-off/ {\n            disable_symlinks off;\n            alias %%TESTDIR%%/cached/;\n        }\n\n        location /cached-on/ {\n            disable_symlinks on;\n            alias %%TESTDIR%%/cached/;\n        }\n\n        location /cached-if-not-owner/ {\n            disable_symlinks if_not_owner;\n            alias %%TESTDIR%%/cached/;\n        }\n\n        location / {\n            disable_symlinks off;\n        }\n    }\n}\n\nEOF\n\nmy $uid = getuid();\nmy ($extfile) = grep { -f && !-l && $uid != (stat())[4] }\n\t('/etc/resolv.conf', '/etc/protocols', '/etc/host.conf');\n\nplan(skip_all => 'no external file found')\n\tif !defined $extfile;\n\n$t->try_run('no disable_symlinks')->plan(28);\n\nmy $d = $t->testdir();\n\nmkdir(\"$d/on\");\nmkdir(\"$d/not_owner\");\nmkdir(\"$d/try_on\");\nmkdir(\"$d/try_not_owner\");\nmkdir(\"$d/if_on\");\nmkdir(\"$d/if_not_owner\");\nmkdir(\"$d/cached\");\n\n$t->write_file(\"empty.html\", \"\");\nsymlink(\"empty.html\", \"$d/link\");\nsymlink($extfile, \"$d/link2\");\n\n$t->write_file(\"on/empty.html\", \"\");\nsymlink(\"empty.html\", \"$d/on/link\");\nsymlink($extfile, \"$d/on/link2\");\n\n$t->write_file(\"not_owner/empty.html\", \"\");\nsymlink(\"empty.html\", \"$d/not_owner/link\");\nsymlink($extfile, \"$d/not_owner/link2\");\n\n$t->write_file(\"try_on/try.html\", \"LOCAL TRY\");\nsymlink($extfile, \"$d/try_on/try\");\n\n$t->write_file(\"try_not_owner/try.html\", \"LOCAL TRY\");\nsymlink($extfile, \"$d/try_not_owner/try\");\nsymlink(\"try.html\", \"$d/try_not_owner/try.txt\");\n\n$t->write_file(\"if_on/empty.html\", \"\");\nsymlink(\"empty.html\", \"$d/if_on/link\");\nsymlink($extfile, \"$d/if_on/link2\");\n\n$t->write_file(\"if_not_owner/empty.html\", \"\");\nsymlink(\"empty.html\", \"$d/if_not_owner/link\");\nsymlink($extfile, \"$d/if_not_owner/link2\");\n\nmkdir(\"$d/dir\");\n$t->write_file(\"dir/empty.html\", \"\");\nsymlink(\"dir\", \"$d/dirlink\");\n\nsymlink($extfile, \"$d/cached/link\");\n\n###############################################################################\n\nSKIP: {\nskip 'cannot test under symlink', 25 if $d ne realpath($d) or $^O eq 'netbsd';\n\nlike(http_get_host('s1', '/link'), qr!200 OK!, 'static (off, same uid)');\nlike(http_get_host('s1', '/link2'), qr!200 OK!, 'static (off, other uid)');\n\nlike(http_get_host('s1', '/on/link'), qr!403 Forbidden!,\n\t'static (on, same uid)');\nlike(http_get_host('s1', '/on/link2'), qr!403 Forbidden!,\n\t'static (on, other uid)');\n\nlike(http_get_host('s1', '/not_owner/link'), qr!200 OK!,\n\t'static (if_not_owner, same uid)');\nlike(http_get_host('s1', '/not_owner/link2'), qr!403 Forbidden!,\n\t'static (if_not_owner, other uid)');\n\nlike(http_get_host('s1', '/try_on/try'), qr/LOCAL TRY/,\n\t'try_files (on)');\nlike(http_get_host('s1', '/try_not_owner/try'), qr/LOCAL TRY/,\n\t'try_files (if_not_owner)');\n\nlike(http_get_host('s1', '/if_on/link'), qr!403 Forbidden!,\n\t'if (on, same uid)');\nlike(http_get_host('s1', '/if_on/link2'), qr!403 Forbidden!,\n\t'if (on, other uid)');\n\nlike(http_get_host('s1', '/if_not_owner/link'), qr!204 No Content!,\n\t'if (if_not_owner, same uid)');\nlike(http_get_host('s1', '/if_not_owner/link2'), qr!403 Forbidden!,\n\t'if (if_not_owner, other uid)');\n\nlike(http_get_host('s2', '/cached-off/link'), qr!200 OK!,\n\t'open_file_cache (pass 1)');\nlike(http_get_host('s2', '/cached-on/link'), qr!403 Forbidden!,\n\t'open_file_cache (pass 2)');\nlike(http_get_host('s2', '/cached-off/link'), qr!200 OK!,\n\t'open_file_cache (pass 3)');\nlike(http_get_host('s2', '/cached-if-not-owner/link'), qr!403 Forbidden!,\n\t'open_file_cache (pass 4)');\nlike(http_get_host('s2', '/cached-off/link'), qr!200 OK!,\n\t'open_file_cache (pass 5)');\n\nlike(http_get('/complex/1/empty.html'), qr!200 OK!, 'complex root 1');\nlike(http_get('/complex/2/empty.html'), qr!200 OK!, 'complex root 2');\nlike(http_get('/complex/3/empty.html'), qr!200 OK!, 'complex root 3');\n\n# workaround for freebsd 8: we use O_EXEC instead of O_SEARCH (since there\n# is no O_SEARCH), and O_DIRECTORY does nothing.  setting the 'x' bit\n# tests to pass as openat() will correctly fail with ENOTDIR\n\nchmod(0700, \"$d/link\");\nmy $rc = $^O eq 'darwin' ? 200 : 404;\n\nlike(http_get('/link/tail'), qr!40[34] !, 'file with trailing /, on');\nlike(http_get('/link/tailowner'), qr!404 !, 'file with trailing /, owner');\nlike(http_get('/link/tailoff'), qr!$rc !, 'file with trailing /, off');\n\nlike(http_get('/dirlink'), qr!404 !, 'directory without /');\nlike(http_get('/dirlink/'), qr!404 !, 'directory with trailing /');\n\n} # SKIP: cannot test under symlink\n\nlike(http_get('/from/wo_slash/empty.html'), qr!200 OK!, '\"from=\" without /');\nlike(http_get('/from/with_slash/empty.html'), qr!200 OK!, '\"from=\" with /');\nlike(http_get('/from/exact/link'), qr!200 OK!, '\"from=\" exact match');\n\n###############################################################################\n\nsub http_get_host {\n\tmy ($host, $url) = @_;\n\treturn http(<<EOF);\nGET $url HTTP/1.0\nHost: $host\n\nEOF\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/http_error_page.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for error_page directive.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy rewrite/)->plan(9)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location /redirect200 {\n            error_page 404 =200 http://example.com/;\n            return 404;\n        }\n\n        location /redirect497 {\n            # 497 implies implicit status code change\n            error_page 497 https://example.com/;\n            return 497;\n        }\n\n        location /error302redirect {\n            error_page 302 http://example.com/;\n            return 302 \"first\";\n        }\n\n        location /error302return302text {\n            error_page 302 /return302text;\n            return 302 \"first\";\n        }\n\n        location /return302text {\n            return 302 \"http://example.com/\";\n        }\n\n        location /error302return302args {\n            error_page 302 /return302args?1;\n            return 302 \"first\";\n        }\n\n        location /error302return302varargs {\n            error_page 302 /return302args?$arg_a;\n            return 302 \"first\";\n        }\n\n        location /return302args {\n            return 302 \"http://example.com/$args\";\n        }\n\n        location /error302rewrite {\n            error_page 302 /rewrite;\n            return 302 \"first\";\n        }\n\n        location /rewrite {\n            rewrite ^ http://example.com/;\n        }\n\n        location /error302directory {\n            error_page 302 /directory;\n            return 302 \"first\";\n        }\n\n        location /directory {\n        }\n\n        location /error302auto {\n            error_page 302 /auto;\n            return 302 \"first\";\n        }\n\n        location /auto/ {\n            proxy_pass http://127.0.0.1:8081;\n        }\n    }\n}\n\nEOF\n\nmkdir($t->testdir() . '/directory');\n\n$t->run();\n\n###############################################################################\n\n# tests for error_page status code change for redirects. problems\n# introduced in 0.8.53 and fixed in 0.9.5.\n\nlike(http_get('/redirect200'), qr!HTTP!, 'redirect 200');\nlike(http_get('/redirect497'), qr!HTTP/1.1 302!, 'redirect 497');\n\n# various tests to see if old location cleared if we happen to redirect\n# again in error_page 302\n\nlike(http_get('/error302redirect'),\n\tqr{HTTP/1.1 302(?!.*Location: first).*Location: http://example.com/}ms,\n\t'error 302 redirect - old location cleared');\n\nlike(http_get('/error302return302text'),\n\tqr{HTTP/1.1 302(?!.*Location: first).*Location: http://example.com/}ms,\n\t'error 302 return 302 text - old location cleared');\n\nlike(http_get('/error302return302args'),\n\tqr{HTTP/1.1 302(?!.*Location: first).*Location: http://example.com/1}ms,\n\t'error 302 return 302 args - old location cleared');\n\nlike(http_get('/error302return302varargs?a=2'),\n\tqr{HTTP/1.1 302(?!.*Location: first).*Location: http://example.com/2}ms,\n\t'error 302 return 302 var args - old location cleared');\n\nlike(http_get('/error302rewrite'),\n\tqr{HTTP/1.1 302(?!.*Location: first).*Location: http://example.com/}ms,\n\t'error 302 rewrite - old location cleared');\n\nlike(http_get('/error302directory'),\n\tqr{HTTP/1.1 301(?!.*Location: first).*Location: http://}ms,\n\t'error 302 directory redirect - old location cleared');\n\nlike(http_get('/error302auto'),\n\tqr{HTTP/1.1 301(?!.*Location: first).*Location: http://}ms,\n\t'error 302 auto redirect - old location cleared');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/http_expect_100_continue.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for Expect: 100-continue support.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy/)->plan(5);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n        location / {\n            proxy_pass http://127.0.0.1:8080/local;\n        }\n        location /local {\n        }\n    }\n}\n\nEOF\n\n$t->run();\n\n###############################################################################\n\nlike(http_100_request('/', '1.1'), qr/ 100 /, 'expect 100 continue');\n\n# Comparison of expectation values is case-insensitive for unquoted tokens.\n\nlike(http_100_request('/', '1.1', '100-Continue'), qr/ 100 /,\n\t'expect 100 continue case-insensitive');\n\n# From RFC 2616, 8.2.3 Use of the 100 (Continue) Status:\n#\n#      - An origin server SHOULD NOT send a 100 (Continue) response if\n#        the request message does not include an Expect request-header\n#        field with the \"100-continue\" expectation, and MUST NOT send a\n#        100 (Continue) response if such a request comes from an HTTP/1.0\n#        (or earlier) client.\n\nunlike(http_100_request('/', '1.0'), qr/ 100 /, 'no 100 continue via http 1.0');\n\n# From RFC 2616, 14.20 Expect:\n#\n#    A server that does not understand or is unable to comply with any of\n#    the expectation values in the Expect field of a request MUST respond\n#    with appropriate error status. The server MUST respond with a 417\n#    (Expectation Failed) status if any of the expectations cannot be met.\n#\n#    <..> If a server receives a request containing an\n#    Expect field that includes an expectation-extension that it does not\n#    support, it MUST respond with a 417 (Expectation Failed) status.\n\nTODO: {\nlocal $TODO = 'not yet';\n\nlike(http_100_request('/', '1.1', 'unknown'), qr/ 417 /, 'unknown expectation');\nlike(http_100_request('/', '1.1', 'token=param'), qr/ 417 /,\n\t'unsupported expectation extension');\n\n}\n\n###############################################################################\n\nsub http_100_request {\n\tmy ($url, $version, $value) = @_;\n\t$value = '100-continue' unless defined $value;\n\thttp(<<EOF);\nPOST $url HTTP/$version\nHost: localhost\nExpect: $value\nContent-Length: 0\nConnection: close\n\nEOF\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/http_header_buffers.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n# (C) Nginx, Inc.\n\n# Tests for large_client_header_buffers directive.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse Socket qw/ CRLF /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http rewrite/)->plan(10)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    connection_pool_size 128;\n    client_header_buffer_size 128;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  five;\n\n        large_client_header_buffers 5 256;\n\n        return 204;\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  ten;\n\n        large_client_header_buffers 10 256;\n\n        return 204;\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  one;\n\n        large_client_header_buffers 1 256;\n\n        return 204;\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  foo;\n\n        large_client_header_buffers 5 256;\n\n        add_header X-URI $uri;\n        add_header X-Foo $http_x_foo;\n        return 204;\n    }\n}\n\nEOF\n\n$t->run();\n\n###############################################################################\n\nTODO: {\ntodo_skip 'overflow', 2 unless $ENV{TEST_NGINX_UNSAFE};\n\n# if hc->busy is allocated before the virtual server is selected,\n# and then additional buffers are allocated in a virtual server with larger\n# number of buffers configured, hc->busy will be overflowed\n\nlike(http(\n\t\"GET / HTTP/1.0\" . CRLF .\n\t\"X-Foo: \" . (\"1234567890\" x 20) . CRLF .\n\t\"Host: ten\" . CRLF .\n\t\"X-Foo: \" . (\"1234567890\" x 20) . CRLF .\n\t\"X-Foo: \" . (\"1234567890\" x 20) . CRLF .\n\t\"X-Foo: \" . (\"1234567890\" x 20) . CRLF .\n\t\"X-Foo: \" . (\"1234567890\" x 20) . CRLF .\n\t\"X-Foo: \" . (\"1234567890\" x 20) . CRLF .\n\tCRLF\n), qr/204|400/, 'additional buffers in virtual server');\n\n# for pipelined requests large header buffers are saved to hc->free;\n# it sized for number of buffers in the current virtual server, but\n# saves previously allocated buffers, and there may be more buffers if\n# allocatad before the virtual server was selected\n\nlike(http(\n\t\"GET / HTTP/1.1\" . CRLF .\n\t\"X-Foo: \" . (\"1234567890\" x 20) . CRLF .\n\t\"X-Foo: \" . (\"1234567890\" x 20) . CRLF .\n\t\"X-Foo: \" . (\"1234567890\" x 20) . CRLF .\n\t\"X-Foo: \" . (\"1234567890\" x 20) . CRLF .\n\t\"X-Foo: \" . (\"1234567890\" x 20) . CRLF .\n\t\"Host: one\" . CRLF .\n\tCRLF .\n\t\"GET / HTTP/1.1\" . CRLF .\n\t\"Host: one\" . CRLF .\n\t\"Connection: close\" . CRLF .\n\tCRLF\n), qr/204/, 'pipelined with too many buffers');\n\n}\n\n# check if long header and long request lines are correctly returned\n# when nginx allocates a long header buffer\n\nlike(http(\n\t\"GET / HTTP/1.0\" . CRLF .\n\t\"Host: foo\" . CRLF .\n\t\"X-Foo: foo\" . (\"1234567890\" x 20) . \"bar\" . CRLF .\n\tCRLF\n), qr/X-Foo: foo(1234567890){20}bar/, 'long header');\n\nlike(http(\n\t\"GET /foo\" . (\"1234567890\" x 20) . \"bar HTTP/1.0\" . CRLF .\n\t\"Host: foo\" . CRLF .\n\tCRLF\n), qr!X-URI: /foo(1234567890){20}bar!, 'long request line');\n\n# the same as the above, but with pipelining, so there is a buffer\n# allocated in the previous request\n\nlike(http(\n\t\"GET / HTTP/1.1\" . CRLF .\n\t\"Host: foo\" . CRLF .\n\t\"X-Foo: \" . (\"1234567890\" x 20) . CRLF .\n\t\"X-Foo: \" . (\"1234567890\" x 20) . CRLF .\n\t\"X-Foo: \" . (\"1234567890\" x 20) . CRLF .\n\tCRLF .\n\t\"GET / HTTP/1.1\" . CRLF .\n\t\"Host: foo\" . CRLF .\n\t\"Connection: close\" . CRLF .\n\t\"X-Foo: foo\" . (\"1234567890\" x 20) . \"bar\" . CRLF .\n\tCRLF\n), qr/X-Foo: foo(1234567890){20}bar/, 'long header after pipelining');\n\nlike(http(\n\t\"GET / HTTP/1.1\" . CRLF .\n\t\"Host: foo\" . CRLF .\n\t\"X-Foo: \" . (\"1234567890\" x 20) . CRLF .\n\t\"X-Foo: \" . (\"1234567890\" x 20) . CRLF .\n\t\"X-Foo: \" . (\"1234567890\" x 20) . CRLF .\n\tCRLF .\n\t\"GET /foo\" . (\"1234567890\" x 20) . \"bar HTTP/1.1\" . CRLF .\n\t\"Host: foo\" . CRLF .\n\t\"Connection: close\" . CRLF .\n\tCRLF\n), qr!X-URI: /foo(1234567890){20}bar!, 'long request line after pipelining');\n\n# the same as the above, but with keepalive; this ensures that previously\n# allocated buffers are properly cleaned up when we set keepalive handler\n\nlike(http(\n\t\"GET / HTTP/1.1\" . CRLF .\n\t\"Host: foo\" . CRLF .\n\t\"X-Foo: \" . (\"1234567890\" x 20) . CRLF .\n\t\"X-Foo: \" . (\"1234567890\" x 20) . CRLF .\n\t\"X-Foo: \" . (\"1234567890\" x 20) . CRLF .\n\tCRLF,\nsleep => 0.1, body =>\n\t\"GET / HTTP/1.1\" . CRLF .\n\t\"Host: foo\" . CRLF .\n\t\"Connection: close\" . CRLF .\n\t\"X-Foo: foo\" . (\"1234567890\" x 20) . \"bar\" . CRLF .\n\tCRLF\n), qr/X-Foo: foo(1234567890){20}bar/, 'long header after keepalive');\n\nlike(http(\n\t\"GET / HTTP/1.1\" . CRLF .\n\t\"Host: foo\" . CRLF .\n\t\"X-Foo: \" . (\"1234567890\" x 20) . CRLF .\n\t\"X-Foo: \" . (\"1234567890\" x 20) . CRLF .\n\t\"X-Foo: \" . (\"1234567890\" x 20) . CRLF .\n\tCRLF,\nsleep => 0.1, body =>\n\t\"GET /foo\" . (\"1234567890\" x 20) . \"bar HTTP/1.1\" . CRLF .\n\t\"Host: foo\" . CRLF .\n\t\"Connection: close\" . CRLF .\n\tCRLF\n), qr!X-URI: /foo(1234567890){20}bar!, 'long request line after keepalive');\n\n# the same as the above, but with pipelining and then keepalive;\n# this ensures that previously allocated buffers are properly cleaned\n# up when we set keepalive handler, including hc->free\n\nlike(http(\n\t\"GET / HTTP/1.1\" . CRLF .\n\t\"Host: foo\" . CRLF .\n\t\"X-Foo: \" . (\"1234567890\" x 20) . CRLF .\n\t\"X-Foo: \" . (\"1234567890\" x 20) . CRLF .\n\t\"X-Foo: \" . (\"1234567890\" x 20) . CRLF .\n\tCRLF .\n\t\"GET / HTTP/1.1\" . CRLF .\n\t\"Host: foo\" . CRLF .\n\t\"X-Foo: \" . (\"1234567890\" x 20) . CRLF .\n\tCRLF,\nsleep => 0.1, body =>\n\t\"GET / HTTP/1.1\" . CRLF .\n\t\"Host: foo\" . CRLF .\n\t\"Connection: close\" . CRLF .\n\t\"X-Foo: foo\" . (\"1234567890\" x 20) . \"bar\" . CRLF .\n\tCRLF\n), qr/X-Foo: foo(1234567890){20}bar/, 'long header after both');\n\nlike(http(\n\t\"GET / HTTP/1.1\" . CRLF .\n\t\"Host: foo\" . CRLF .\n\t\"X-Foo: \" . (\"1234567890\" x 20) . CRLF .\n\t\"X-Foo: \" . (\"1234567890\" x 20) . CRLF .\n\t\"X-Foo: \" . (\"1234567890\" x 20) . CRLF .\n\tCRLF .\n\t\"GET / HTTP/1.1\" . CRLF .\n\t\"Host: foo\" . CRLF .\n\t\"X-Foo: \" . (\"1234567890\" x 20) . CRLF .\n\tCRLF,\nsleep => 0.1, body =>\n\t\"GET /foo\" . (\"1234567890\" x 20) . \"bar HTTP/1.1\" . CRLF .\n\t\"Host: foo\" . CRLF .\n\t\"Connection: close\" . CRLF .\n\tCRLF\n), qr!X-URI: /foo(1234567890){20}bar!, 'long request line after both');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/http_headers_multi.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n# (C) Nginx, Inc.\n\n# Tests for handling of multiple http headers and access via variables.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\nuse Socket qw/ CRLF /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http rewrite proxy/)->plan(42);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            add_header X-Forwarded-For $http_x_forwarded_for;\n            add_header X-Cookie $http_cookie;\n            add_header X-Foo $http_foo;\n\n            add_header X-Cookie-Foo $cookie_foo;\n            add_header X-Cookie-Bar $cookie_bar;\n            add_header X-Cookie-Bazz $cookie_bazz;\n\n            return 204;\n        }\n\n        location /s {\n            add_header Cache-Control foo;\n            add_header Cache-Control bar;\n            add_header Cache-Control bazz;\n\n            add_header Link foo;\n            add_header Link bar;\n            add_header Link bazz;\n\n            add_header Foo foo;\n            add_header Foo bar;\n            add_header Foo bazz;\n\n            add_header X-Sent-CC $sent_http_cache_control;\n            add_header X-Sent-Link $sent_http_link;\n            add_header X-Sent-Foo $sent_http_foo;\n\n            return 204;\n        }\n\n        location /t {\n            add_trailer Foo foo;\n            add_trailer Foo bar;\n            add_trailer Foo bazz;\n            add_trailer X-Sent-Trailer-Foo $sent_trailer_foo;\n\n            return 200 \"\";\n        }\n\n        location /v {\n            add_header X-Forwarded-For $http_x_forwarded_for;\n            add_header X-Cookie $http_cookie;\n\n            add_header X-HTTP-Host $http_host;\n            add_header X-User-Agent $http_user_agent;\n            add_header X-Referer $http_referer;\n            add_header X-Via $http_via;\n\n            add_header X-Content-Length $content_length;\n            add_header X-Content-Type $content_type;\n            add_header X-Host $host;\n            add_header X-Remote-User $remote_user;\n\n            return 204;\n        }\n\n        location /d {\n            return 204;\n        }\n\n        location /u {\n            add_header X-Upstream-Set-Cookie $upstream_http_set_cookie;\n            add_header X-Upstream-Bar $upstream_http_bar;\n\n            add_header X-Upstream-Cookie-Foo $upstream_cookie_foo;\n            add_header X-Upstream-Cookie-Bar $upstream_cookie_bar;\n            add_header X-Upstream-Cookie-Bazz $upstream_cookie_bazz;\n\n            proxy_pass http://127.0.0.1:8080/backend;\n        }\n\n        location /backend {\n            add_header Set-Cookie foo=1;\n            add_header Set-Cookie bar=2;\n            add_header Set-Cookie bazz=3;\n            add_header Bar foo;\n            add_header Bar bar;\n            add_header Bar bazz;\n            return 204;\n        }\n    }\n}\n\nEOF\n\n$t->run();\n\n###############################################################################\n\n# combining multiple headers:\n#\n# $http_cookie, $http_x_forwarded_for, $sent_http_cache_control,\n# and $sent_http_link with special handling, other headers with\n# general handling\n\n# request headers, $http_*\n\nlike(get('/', map { \"X-Forwarded-For: $_\" } qw/ foo bar bazz /),\n\tqr/X-Forwarded-For: foo, bar, bazz/, 'multi $http_x_forwarded_for');\nlike(get('/', 'Cookie: foo=1', 'Cookie: bar=2', 'Cookie: bazz=3'),\n\tqr/X-Cookie: foo=1; bar=2; bazz=3/, 'multi $http_cookie');\n\nTODO: {\nlocal $TODO = 'not yet' unless $t->has_version('1.23.0');\n\nlike(get('/', 'Foo: foo', 'Foo: bar', 'Foo: bazz'),\n\tqr/X-Foo: foo, bar, bazz/, 'multi $http_foo');\n\n}\n\n# request cookies, $cookie_*\n\nmy $r = get('/', 'Cookie: foo=1', 'Cookie: bar=2', 'Cookie: bazz=3');\n\nlike($r, qr/X-Cookie-Foo: 1/, '$cookie_foo');\nlike($r, qr/X-Cookie-Bar: 2/, '$cookie_bar');\nlike($r, qr/X-Cookie-Bazz: 3/, '$cookie_bazz');\n\n# response headers, $http_*\n\n$r = get('/s');\n\nlike($r, qr/X-Sent-CC: foo, bar, bazz/, 'multi $sent_http_cache_control');\nlike($r, qr/X-Sent-Link: foo, bar, bazz/, 'multi $sent_http_link');\n\nTODO: {\nlocal $TODO = 'not yet' unless $t->has_version('1.23.0');\n\nlike($r, qr/X-Sent-Foo: foo, bar, bazz/, 'multi $sent_http_foo');\n\n}\n\n# upstream response headers, $upstream_http_*\n\n$r = get('/u');\n\nTODO: {\nlocal $TODO = 'not yet' unless $t->has_version('1.23.0');\n\nlike($r, qr/X-Upstream-Set-Cookie: foo=1, bar=2, bazz=3/,\n\t'multi $upstream_http_set_cookie');\nlike($r, qr/X-Upstream-Bar: foo, bar, bazz/, 'multi $upstream_http_bar');\n\n}\n\n# upstream response cookies, $upstream_cookie_*\n\nlike($r, qr/X-Upstream-Cookie-Foo: 1/, '$upstream_cookie_foo');\nlike($r, qr/X-Upstream-Cookie-Bar: 2/, '$upstream_cookie_bar');\nlike($r, qr/X-Upstream-Cookie-Bazz: 3/, '$upstream_cookie_bazz');\n\n# response trailers, $sent_trailer_*\n\nTODO: {\nlocal $TODO = 'not yet' unless $t->has_version('1.23.0');\n\nlike(get('/t'), qr/X-Sent-Trailer-Foo: foo, bar, bazz/,\n\t'multi $sent_trailer_foo');\n\n}\n\n# various variables for request headers:\n#\n# $http_host, $http_user_agent, $http_referer\n# multiple Host, User-Agent, Referer headers are invalid, but we currently\n# reject only requests with multiple Host headers\n#\n# $http_via, $http_x_forwarded_for, $http_cookie\n# multiple headers are valid\n\nlike(get('/v'), qr/X-HTTP-Host: localhost/, '$http_host');\nlike(get('/v', 'Host: foo', 'Host: bar'),\n\tqr/400 Bad/, 'duplicate host rejected');\n\nTODO: {\nlocal $TODO = 'not yet' unless $t->has_version('1.23.0');\n\nlike(get('/v', 'User-Agent: foo', 'User-Agent: bar'),\n\tqr/X-User-Agent: foo, bar/, 'multi $http_user_agent (invalid)');\nlike(get('/v', 'Referer: foo', 'Referer: bar'),\n\tqr/X-Referer: foo, bar/, 'multi $http_referer (invalid)');\nlike(get('/v', 'Via: foo', 'Via: bar', 'Via: bazz'),\n\tqr/X-Via: foo, bar, bazz/, 'multi $http_via');\n\n}\n\nlike(get('/v', 'Cookie: foo', 'Cookie: bar', 'Cookie: bazz'),\n\tqr/X-Cookie: foo; bar; bazz/, 'multi $http_cookie');\nlike(get('/v', 'X-Forwarded-For: foo', 'X-Forwarded-For: bar',\n\t'X-Forwarded-For: bazz'),\n\tqr/X-Forwarded-For: foo, bar, bazz/, 'multi $http_x_forwarded_for');\n\n# other variables related to request headers:\n#\n# $content_length, $content_type, $host, $remote_user\n\nlike(get('/v', 'Content-Length: 0'),\n\tqr/X-Content-Length: 0/, '$content_length');\nlike(get('/v', 'Content-Length: 0', 'Content-Length: 0'),\n\tqr/400 Bad/, 'duplicate Content-Length rejected');\n\nlike(get('/v', 'Content-Type: foo'),\n\tqr/X-Content-Type: foo/, '$content_type');\n\nTODO: {\nlocal $TODO = 'not yet' unless $t->has_version('1.23.0');\n\nlike(get('/v', 'Content-Type: foo', 'Content-Type: bar'),\n\tqr/X-Content-Type: foo, bar/, 'multi $content_type (invalid)');\n\n}\n\nlike(http(\"GET /v HTTP/1.0\" . CRLF . CRLF),\n\tqr/X-Host: localhost/, '$host from server_name');\nlike(http(\"GET /v HTTP/1.0\" . CRLF . \"Host: foo\" . CRLF . CRLF),\n\tqr/X-Host: foo/, '$host');\nlike(http(\"GET /v HTTP/1.0\" . CRLF . \"Host: foo\" . CRLF .\n\t\"Host: bar\" . CRLF . CRLF),\n\tqr/400 Bad/, 'duplicate host rejected');\n\nlike(get('/v', 'Authorization: Basic dXNlcjpzZWNyZXQ='),\n\tqr/X-Remote-User: user/, '$remote_user');\nlike(get('/v', 'Authorization: Basic dXNlcjpzZWNyZXQ=',\n\t'Authorization: Basic dXNlcjpzZWNyZXQ='),\n\tqr/400 Bad/, 'duplicate authorization rejected');\n\n# request headers required to be unique:\n#\n# Host, If-Modified-Since, If-Unmodified-Since, If-Match, If-None-Match,\n# Content-Length, Content-Range, If-Range, Transfer-Encoding, Expect,\n# Authorization\n\nlike(get('/d', 'Host: foo', 'Host: bar'),\n\tqr/400 Bad/, 'duplicate Host rejected');\nlike(get('/d', 'If-Modified-Since: foo', 'If-Modified-Since: bar'),\n\tqr/400 Bad/, 'duplicate If-Modified-Since rejected');\nlike(get('/d', 'If-Unmodified-Since: foo', 'If-Unmodified-Since: bar'),\n\tqr/400 Bad/, 'duplicate If-Unmodified-Since rejected');\nlike(get('/d', 'If-Match: foo', 'If-Match: bar'),\n\tqr/400 Bad/, 'duplicate If-Match rejected');\nlike(get('/d', 'If-None-Match: foo', 'If-None-Match: bar'),\n\tqr/400 Bad/, 'duplicate If-None-Match rejected');\nlike(get('/d', 'Content-Length: 0', 'Content-Length: 0'),\n\tqr/400 Bad/, 'duplicate Content-Length rejected');\nlike(get('/d', 'Content-Range: foo', 'Content-Range: bar'),\n\tqr/400 Bad/, 'duplicate Content-Range rejected');\nlike(get('/d', 'If-Range: foo', 'If-Range: bar'),\n\tqr/400 Bad/, 'duplicate If-Range rejected');\nlike(get('/d', 'Transfer-Encoding: foo', 'Transfer-Encoding: bar'),\n\tqr/400 Bad/, 'duplicate Transfer-Encoding rejected');\nlike(get('/d', 'Expect: foo', 'Expect: bar'),\n\tqr/400 Bad/, 'duplicate Expect rejected');\nlike(get('/d', 'Authorization: foo', 'Authorization: bar'),\n\tqr/400 Bad/, 'duplicate Authorization rejected');\n\n###############################################################################\n\nsub get {\n\tmy ($url, @headers) = @_;\n\treturn http(\n\t\t\"GET $url HTTP/1.1\" . CRLF .\n\t\t'Host: localhost' . CRLF .\n\t\t'Connection: close' . CRLF .\n\t\tjoin(CRLF, @headers) . CRLF . CRLF\n\t);\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/http_host.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n# (C) Valentin Bartenev\n\n# Tests for host parsing in requests.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx qw/ :DEFAULT http_content /;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http rewrite/)->plan(37);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen  127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            return  200  $host;\n        }\n    }\n}\n\nEOF\n\n$t->run();\n\n###############################################################################\n\nis(http_host_header('www.abcd-ef.g02.xyz'), 'www.abcd-ef.g02.xyz',\n\t'domain w/o port (host header)');\nis(http_host_header('abcd-ef.g02.xyz:' . port(8080)), 'abcd-ef.g02.xyz',\n\t'domain w/port (host header)');\n\nis(http_absolute_path('abcd-ef.g02.xyz'), 'abcd-ef.g02.xyz',\n\t'domain w/o port (absolute request)');\nis(http_absolute_path('www.abcd-ef.g02.xyz:10'), 'www.abcd-ef.g02.xyz',\n\t'domain w/port (absolute request)');\n\n\nis(http_host_header('www.abcd-ef.g02.xyz.'), 'www.abcd-ef.g02.xyz',\n\t'domain w/ ending dot w/o port (host header)');\n\nis(http_host_header('abcd-ef.g02.xyz.:88'), 'abcd-ef.g02.xyz',\n\t'domain w/ ending dot w/port (host header)');\n\nis(http_absolute_path('www.abcd-ef.g02.xyz.'), 'www.abcd-ef.g02.xyz',\n\t'domain w/ ending dot w/o port (absolute request)');\nis(http_absolute_path('abcd-ef.g02.xyz.:2'), 'abcd-ef.g02.xyz',\n\t'domain w/ ending dot w/port (absolute request)');\n\n\nis(http_absolute_path('AbC-d93.0.34ZhGt-s.nk.Ru'), 'abc-d93.0.34zhgt-s.nk.ru',\n\t'mixed case domain w/o port (absolute request)');\nis(http_host_header('AbC-d93.0.34ZhGt-s.nk.Ru:88'), 'abc-d93.0.34zhgt-s.nk.ru',\n\t'mixed case domain w/port (host header)');\n\n\nis(http_host_header('123.40.56.78'), '123.40.56.78',\n\t'ipv4 w/o port (host header)');\nis(http_host_header('123.49.0.78:987'), '123.49.0.78',\n\t'ipv4 w/port (host header)');\n\nis(http_absolute_path('123.49.0.78'), '123.49.0.78',\n\t'ipv4 w/o port (absolute request)');\nis(http_absolute_path('123.40.56.78:123'), '123.40.56.78',\n\t'ipv4 w/port (absolute request)');\n\nis(http_host_header('[abcd::ef98:0:7654:321]'), '[abcd::ef98:0:7654:321]',\n\t'ipv6 literal w/o port (host header)');\nis(http_host_header('[abcd::ef98:0:7654:321]:80'), '[abcd::ef98:0:7654:321]',\n\t'ipv6 literal w/port (host header)');\n\nis(http_absolute_path('[abcd::ef98:0:7654:321]'), '[abcd::ef98:0:7654:321]',\n\t'ipv6 literal w/o port (absolute request)');\nis(http_absolute_path('[abcd::ef98:0:7654:321]:5'), '[abcd::ef98:0:7654:321]',\n\t'ipv6 literal w/port (absolute request)');\n\nis(http_host_header('[::ffff:12.30.67.89]'), '[::ffff:12.30.67.89]',\n\t'ipv4-mapped ipv6 w/o port (host header)');\nis(http_host_header('[::123.45.67.89]:4321'), '[::123.45.67.89]',\n\t'ipv4-mapped ipv6 w/port (host header)');\n\nis(http_absolute_path('[::123.45.67.89]'), '[::123.45.67.89]',\n\t'ipv4-mapped ipv6 w/o port (absolute request)');\nis(http_absolute_path('[::ffff:12.30.67.89]:4321'), '[::ffff:12.30.67.89]',\n\t'ipv4-mapped ipv6 w/port (absolute request)');\n\nlike(http_host_header('example.com/\\:552', 1), qr/ 400 /,\n\t'domain w/ path separators (host header)');\nlike(http_absolute_path('\\e/xample.com', 1), qr/ 400 /,\n\t'domain w/ path separators (absolute request)');\n\nlike(http_host_header('..examp-LE.com', 1), qr/ 400 /,\n\t'domain w/ double dot (host header)');\nlike(http_absolute_path('com.exa-m.45..:', 1), qr/ 400 /,\n\t'domain w/ double dot (absolute request)');\n\n\nlike(http_host_header('[abcd::e\\f98:0/:7654:321]', 1), qr/ 400 /,\n\t'ipv6 literal w/ path separators (host header)');\nlike(http_absolute_path('[abcd\\::ef98:0:7654:321/]:12', 1), qr/ 400 /,\n\t'ipv6 literal w/ path separators (absolute request)');\n\nlike(http_host_header('[abcd::ef98:0:7654:321]..:98', 1), qr/ 400 /,\n\t'ipv6 literal w/ double dot (host header)');\nlike(http_absolute_path('[ab..cd::ef98:0:7654:321]', 1), qr/ 400 /,\n\t'ipv6 literal w/ double dot (absolute request)');\n\n\nlike(http_host_header('[abcd::ef98:0:7654:321]..:98', 1), qr/ 400 /,\n\t'ipv6 literal w/ double dot (host header)');\nlike(http_absolute_path('[ab..cd::ef98:0:7654:321]', 1), qr/ 400 /,\n\t'ipv6 literal w/ double dot (absolute request)');\n\n\n# As per RFC 3986,\n# http://tools.ietf.org/html/rfc3986#section-3.2.2\n#\n# IP-literal    = \"[\" ( IPv6address / IPvFuture  ) \"]\"\n#\n# IPvFuture     = \"v\" 1*HEXDIG \".\" 1*( unreserved / sub-delims / \":\" )\n#\n# sub-delims    = \"!\" / \"$\" / \"&\" / \"'\" / \"(\" / \")\"\n#               / \"*\" / \"+\" / \",\" / \";\" / \"=\"\n#\n# unreserved    = ALPHA / DIGIT / \"-\" / \".\" / \"_\" / \"~\"\n#\n\nis(http_host_header(\n\t'[v0123456789aBcDeF.!$&\\'()*+,;=-._~AbCdEfGhIjKlMnOpQrStUvWxYz'\n\t. '0123456789:]'),\n\t'[v0123456789abcdef.!$&\\'()*+,;=-._~abcdefghijklmnopqrstuvwxyz'\n\t. '0123456789:]',\n\t'IPvFuture all symbols (host header)');\n\nis(http_absolute_path(\n\t'[v0123456789aBcDeF.!$&\\'()*+,;=-._~AbCdEfGhIjKlMnOpQrStUvWxYz'\n\t. '0123456789:]'),\n\t'[v0123456789abcdef.!$&\\'()*+,;=-._~abcdefghijklmnopqrstuvwxyz'\n\t. '0123456789:]',\n\t'IPvFuture all symbols (absolute request)');\n\nis(http_host_header('123.40.56.78:9000:80'), '123.40.56.78',\n\t'double port hack');\n\nlike(http_host_header(\"localhost\\nHost: again\", 1), qr/ 400 /, 'host repeat');\n\nTODO: {\nlocal $TODO = 'not yet' unless $t->has_version('1.21.1');\n\nlike(http_host_header(\"localhost\\x02\", 1), qr/ 400 /, 'control');\n\n}\n\n###############################################################################\n\nsub http_host_header {\n\tmy ($host, $all) = @_;\n\tmy ($r) = http(<<EOF);\nGET / HTTP/1.0\nHost: $host\n\nEOF\n\treturn ($all ? $r : http_content($r));\n}\n\nsub http_absolute_path {\n\tmy ($host, $all) = @_;\n\tmy ($r) = http(<<EOF);\nGET http://$host/ HTTP/1.0\nHost: localhost\n\nEOF\n\treturn ($all ? $r : http_content($r));\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/http_include.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for include directive.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http rewrite proxy access/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    upstream u {\n        include ups.conf;\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        if ($arg_s) {\n            include sif.conf;\n        }\n\n        location / {\n            if ($arg_l) {\n                include lif.conf;\n            }\n        }\n\n        location /lmt {\n            limit_except GET {\n                include lmt.conf;\n            }\n        }\n\n        location /proxy {\n            add_header X-IP $upstream_addr always;\n            proxy_pass http://u/backend;\n        }\n\n        location /backend { }\n    }\n}\n\nEOF\n\nmy $p = port(8080);\n\n$t->write_file('sif.conf', 'return 200 SIF;');\n$t->write_file('lif.conf', 'return 200 LIF;');\n$t->write_file('lmt.conf', 'deny all;');\n$t->write_file('ups.conf', \"server 127.0.0.1:$p;\");\n\n$t->run()->plan(5);\n\n###############################################################################\n\nlike(http_get('/?s=1'), qr/SIF/, 'include in server if');\nlike(http_get('/?l=1'), qr/LIF/, 'include in location if');\nlike(http_post('/lmt'), qr/ 403 /, 'include in limit_except');\nlike(http_get('/proxy'), qr/X-IP: 127.0.0.1:$p/, 'include in upstream');\n\nunlike(http_get('/'), qr/ 200 /, 'no include');\n\n###############################################################################\n\nsub http_post {\n\tmy ($uri) = @_;\n\thttp(<<EOF);\nPOST $uri HTTP/1.0\nHost: localhost\n\nEOF\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/http_keepalive.t",
    "content": "#!/usr/bin/perl\n\n# (C) Andrey Zelenkov\n# (C) Nginx, Inc.\n\n# Tests for http keepalive directives.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse IO::Select;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    log_format test1 $sent_http_connection;\n    log_format test2 $sent_http_keep_alive;\n    access_log %%TESTDIR%%/test1.log test1 if=$arg_l;\n    access_log %%TESTDIR%%/test2.log test2 if=$arg_l;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        keepalive_requests  2;\n        keepalive_timeout   1 9;\n\n        add_header X-Conn $connection_requests:$connection_time;\n\n        location / { }\n        location /r {\n            keepalive_requests  4;\n            keepalive_timeout   30s;\n        }\n\n        location /time {\n            keepalive_requests  100;\n            keepalive_timeout   75s;\n            keepalive_time      1s;\n        }\n\n        location /safari {\n            keepalive_disable  safari;\n        }\n\n        location /none {\n            keepalive_disable  none;\n        }\n\n        location /zero {\n            keepalive_timeout  0;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('index.html', '');\n$t->write_file('r', '');\n$t->write_file('time', '');\n$t->write_file('safari', '');\n$t->write_file('none', '');\n$t->write_file('zero', '');\n$t->run()->plan(21);\n\n###############################################################################\n\n# keepalive_requests\n\nlike(http_keepalive('/'), qr/Connection: keep-alive/, 'keepalive request');\nis(count_keepalive(http_keepalive('/?l=ok', req => 2)), 1, 'keepalive limit');\nis(count_keepalive(http_keepalive('/r', req => 3)), 3, 'keepalive merge');\nis(count_keepalive(http_keepalive('/r', req => 5)), 3, 'keepalive merge limit');\n\n# keepalive_disable\n\nlike(http_keepalive('/', method => 'POST', ua => \"MSIE 5.0\"),\n\tqr/Connection: close/, 'keepalive disable msie6');\nlike(http_keepalive('/', ua => \"MSIE 5.0\"), qr/Connection: keep-alive/,\n\t'keepalive disable msie6 GET');\nlike(http_keepalive('/', method => 'POST', ua => \"MSIE 7.0\"),\n\tqr/Connection: keep-alive/, 'keepalive disable msie6 modern');\nlike(http_keepalive('/', ua => \"Mac OS X Safari/7534.48.3\"),\n\tqr/Connection: keep-alive/, 'keepalive disable msie6 safari');\nlike(http_keepalive('/safari', ua => \"Mac OS X Safari/7534.48.3\"),\n\tqr/Connection: close/, 'keepalive disable safari');\nlike(http_keepalive('/none', method => 'POST', ua => \"MSIE 5.0\"),\n\tqr/Connection: keep-alive/, 'keepalive disable none');\n\n# keepalive_timeout\n\nmy $r = http_keepalive('/', req => 2, sleep => 2.1);\nis(count_keepalive($r), 1, 'keepalive timeout request');\nlike($r, qr/Keep-Alive: timeout=9/, 'keepalive timeout header');\n\nlike(http_keepalive('/zero'), qr/Connection: close/, 'keepalive timeout 0');\n\n# keepalive_time\n\n$r = http_keepalive('/time', req => 3);\nis(() = $r =~ /(200 OK)/g, 3, 'keepalive time requests');\nunlike($r, qr/Connection: close/, 'keepalive time connection');\n\n$r = http_keepalive('/time', req => 3, sleep => 1.2);\nis(() = $r =~ /(200 OK)/g, 2, 'keepalive time limit requests');\nlike($r, qr/Connection: close/, 'keepalive time limit connection');\n\nlike($r, qr/X-Conn: 1:0.*X-Conn: 2:[^0]/s, 'keepalive time limit variables');\n\n# cancel keepalive on EOF while discarding body\n\nmy $s = http(<<EOF, start => 1);\nPOST /r HTTP/1.1\nHost: localhost\nContent-Length: 10\n\nEOF\n\nread_keepalive($s);\nshutdown($s, 1);\n\nok(IO::Select->new($s)->can_read(3), 'EOF in discard body');\n\n$t->stop();\n\nTODO: {\nlocal $TODO = 'not yet';\n\nis($t->read_file('test1.log'), \"keep-alive\\nclose\\n\", 'sent_http_connection');\nis($t->read_file('test2.log'), \"timeout=9\\n-\\n\", 'sent_http_keep_alive');\n\n}\n\n###############################################################################\n\nsub http_keepalive {\n\tmy ($url, %opts) = @_;\n\tmy $total = '';\n\n\t$opts{ua} = $opts{ua} || '';\n\t$opts{req} = $opts{req} || 1;\n\t$opts{sleep} = $opts{sleep} || 0;\n\t$opts{method} = $opts{method} || 'GET';\n\n\tlocal $SIG{PIPE} = 'IGNORE';\n\n\tmy $s = http('', start => 1);\n\n\tfor my $i (1 .. $opts{req}) {\n\n\t\tmy $sleep = ($i == 1 ? $opts{sleep} : 0);\n\n\t\thttp(<<EOF, socket => $s, start => 1, sleep => $sleep);\n$opts{method} $url HTTP/1.1\nHost: localhost\nUser-Agent: $opts{ua}\n\nEOF\n\n\t\t$total .= read_keepalive($s);\n\t}\n\n\treturn $total;\n}\n\nsub read_keepalive {\n\tmy ($s) = @_;\n\tmy $data = '';\n\n\twhile (IO::Select->new($s)->can_read(3)) {\n\t\tsysread($s, my $buffer, 4096) or last;\n\t\t$data .= $buffer;\n\t\tlast if $data =~ /^\\x0d\\x0a/ms;\n\t}\n\n\tlog_in($data);\n\treturn $data;\n}\n\nsub count_keepalive {\n\tmy ($str) = @_;\n\treturn $str =~ s/Connection: keep-alive//g;\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/http_keepalive_shutdown.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for http keepalive connections on worker shutdown.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx qw/ :DEFAULT http_end /;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http limit_req/)->plan(1);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    limit_req_zone  $binary_remote_addr  zone=one:1m  rate=1r/s;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            limit_req  zone=one  burst=5;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('test.html', 'XtestX');\n$t->run();\n\n###############################################################################\n\nlocal $TODO = 'not yet' unless $t->has_version('1.21.6');\n\n# signaling on graceful shutdown to client that keepalive connection is closing\n\nmy $s = http(<<EOF, start => 1);\nHEAD /test.html HTTP/1.1\nHost: localhost\n\nHEAD /test.html HTTP/1.1\nHost: localhost\n\nEOF\n\nselect undef, undef, undef, 0.1;\n\n$t->stop();\n\nlike(http_end($s), qr/Connection: close/, 'connection close on exit');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/http_listen.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for listen port ranges.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy rewrite/);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        listen       127.0.0.1:%%PORT_8182%%-%%PORT_8183%%;\n        listen       [::1]:%%PORT_8182%%-%%PORT_8183%%;\n        server_name  localhost;\n\n        location / {\n            proxy_pass  http://$arg_b/t;\n        }\n\n        location /t {\n            return  200  $server_addr:$server_port;\n        }\n    }\n\n    # catch out of range\n\n    server {\n        listen       127.0.0.1:8181;\n        listen       127.0.0.1:8184;\n        listen       [::1]:%%PORT_8181%%;\n        listen       [::1]:%%PORT_8184%%;\n        server_name  localhost;\n    }\n}\n\nEOF\n\nmy $p0 = port(8080); my $p3 = port(8183);\nmy $p1 = port(8181); my $p4 = port(8184);\nmy $p2 = port(8182);\n\n\nplan(skip_all => 'no requested ranges')\n\tif \"$p2$p3\" ne \"81828183\";\n\n$t->run()->plan(9);\n\n###############################################################################\n\nlike(http_get(\"/?b=127.0.0.1:$p0\"), qr/127.0.0.1:$p0/, 'single');\nunlike(http_get(\"/?b=127.0.0.1:$p1\"), qr/127.0.0.1:$p1/, 'out of range 1');\nlike(http_get(\"/?b=127.0.0.1:$p2\"), qr/127.0.0.1:$p2/, 'range 1');\nlike(http_get(\"/?b=127.0.0.1:$p3\"), qr/127.0.0.1:$p3/, 'range 2');\nunlike(http_get(\"/?b=127.0.0.1:$p4\"), qr/127.0.0.1:$p4/, 'out of range 2');\n\nunlike(http_get(\"/?b=[::1]:$p1\"), qr/::1:$p1/, 'inet6 out of range 1');\nlike(http_get(\"/?b=[::1]:$p2\"), qr/::1:$p2/, 'inet6 range 1');\nlike(http_get(\"/?b=[::1]:$p3\"), qr/::1:$p3/, 'inet6 range 2');\nunlike(http_get(\"/?b=[::1]:$p4\"), qr/::1:$p4/, 'inet6 out of range 2');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/http_listen_wildcard.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for listen port ranges with a wildcard address.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy rewrite/);\n\nplan(skip_all => 'listen on wildcard address')\n\tunless $ENV{TEST_NGINX_UNSAFE};\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        listen       %%PORT_8186%%-%%PORT_8187%%;\n        server_name  localhost;\n\n        location / {\n            proxy_pass  http://$arg_b/t;\n        }\n\n        location /t {\n            return  200  $server_addr:$server_port;\n        }\n    }\n\n    # catch out of range\n\n    server {\n        listen       127.0.0.1:8185;\n        listen       127.0.0.1:8188;\n        server_name  localhost;\n    }\n}\n\nEOF\n\nmy $p5 = port(8185); my $p7 = port(8187);\nmy $p6 = port(8186); my $p8 = port(8188);\n\nplan(skip_all => 'no requested ranges')\n\tif \"$p6$p7\" ne \"81868187\";\n\n$t->run()->plan(4);\n\n###############################################################################\n\nunlike(http_get(\"/?b=127.0.0.1:$p5\"), qr/127.0.0.1:$p5/, 'out of range 1');\nlike(http_get(\"/?b=127.0.0.1:$p6\"), qr/127.0.0.1:$p6/, 'wildcard range 1');\nlike(http_get(\"/?b=127.0.0.1:$p7\"), qr/127.0.0.1:$p7/, 'wildcard range 2');\nunlike(http_get(\"/?b=127.0.0.1:$p8\"), qr/127.0.0.1:$p8/, 'out of range 2');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/http_location.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for location selection.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http rewrite/)->plan(14)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location = / {\n            add_header X-Location exactlyroot;\n            return 204;\n        }\n\n        location / {\n            add_header X-Location root;\n            return 204;\n        }\n\n        location ^~ /images/ {\n            add_header X-Location images;\n            return 204;\n        }\n\n        location ~* \\.(gif|jpg|jpeg)$ {\n            add_header X-Location regex;\n            return 204;\n        }\n\n        location ~ casefull {\n            add_header X-Location casefull;\n            return 204;\n        }\n\n        location = /foo {\n            add_header X-Location \"/foo exact\";\n            return 204;\n        }\n\n        location /foo {\n            add_header X-Location \"/foo prefix\";\n            return 204;\n        }\n\n        location = /foo/ {\n            add_header X-Location \"/foo/ exact\";\n            return 204;\n        }\n\n        location /foo/ {\n            add_header X-Location \"/foo/ prefix\";\n            return 204;\n        }\n\n        location /lowercase {\n            add_header X-Location lowercase;\n            return 204;\n        }\n\n        location /UPPERCASE {\n            add_header X-Location uppercase;\n            return 204;\n        }\n    }\n}\n\nEOF\n\n$t->run();\n\n###############################################################################\n\nlike(http_get('/'), qr/X-Location: exactlyroot/, 'exactlyroot');\nlike(http_get('/x'), qr/X-Location: root/, 'root');\nlike(http_get('/images/t.gif'), qr/X-Location: images/, 'images');\nlike(http_get('/t.gif'), qr/X-Location: regex/, 'regex');\nlike(http_get('/t.GIF'), qr/X-Location: regex/, 'regex with mungled case');\nlike(http_get('/casefull/t.gif'), qr/X-Location: regex/, 'first regex wins');\nlike(http_get('/casefull/'), qr/X-Location: casefull/, 'casefull regex');\n\nlike(http_get('/foo'), qr!X-Location: /foo exact!, '/foo exact');\nlike(http_get('/foobar'), qr!X-Location: /foo prefix!, '/foo prefix');\nlike(http_get('/foo/'), qr!X-Location: /foo/ exact!, '/foo/ exact');\nlike(http_get('/foo/bar'), qr!X-Location: /foo/ prefix!, '/foo/ prefix');\n\nSKIP: {\n\tskip 'caseless os', 1\n\t\tif $^O eq 'MSWin32' or $^O eq 'darwin';\n\n\tlike(http_get('/CASEFULL/'), qr/X-Location: root/,\n\t\t'casefull regex do not match wrong case');\n}\n\n# on case-insensitive systems a request to \"/UPPERCASE\" might fail,\n# if location search tree is incorrectly sorted and uppercase\n# characters are used in location directives (ticket #90)\n\nlike(http_get('/lowercase'), qr/X-Location: lowercase/, 'lowercase');\nlike(http_get('/UPPERCASE'), qr/X-Location: uppercase/, 'uppercase');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/http_location_auto.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for location selection, an auto_redirect edge case.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy rewrite/)->plan(4)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        proxy_hide_header X-Location;\n        add_header X-Location unset;\n\n        # As of nginx 1.5.4, this results in the following\n        # location tree:\n        #\n        #         \"/a-b\"\n        # \"/a-a\"          \"/a/\"\n        #\n        # A request to \"/a\" is expected to match \"/a/\" with auto_redirect,\n        # but with such a tree it tests locations \"/a-b\", \"/a-a\" and then\n        # falls back to null location.\n        #\n        # Key factor is that \"-\" is less than \"/\".\n\n        location /a/  { proxy_pass http://127.0.0.1:8080/a-a; }\n        location /a-a { add_header X-Location a-a; return 204; }\n        location /a-b { add_header X-Location a-b; return 204; }\n    }\n}\n\nEOF\n\n$t->run();\n\n###############################################################################\n\nlike(http_get('/a'), qr/301 Moved/, 'auto redirect');\nlike(http_get('/a/'), qr/X-Location: unset/, 'match a');\nlike(http_get('/a-a'), qr/X-Location: a-a/, 'match a-a');\nlike(http_get('/a-b'), qr/X-Location: a-b/, 'match a-b');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/http_location_win32.t",
    "content": "#!/usr/bin/env perl\n\n# (C) Maxim Dounin\n\n# Tests for location selection on win32.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nplan(skip_all => 'not win32')\n\tif $^O ne 'MSWin32' && $^O ne 'msys';\n\nmy $t = Test::Nginx->new()->has(qw/http rewrite/)->plan(19)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon         off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            add_header X-Location root;\n            return 204;\n        }\n\n        location /directory/ {\n            add_header X-Location directory;\n            return 204;\n        }\n\n        location /direct~1 {\n        }\n\n        location = /file {\n            add_header X-Location file;\n            return 204;\n        }\n    }\n}\n\nEOF\n\n$t->run();\n\nmy $d = $t->testdir();\nmkdir(\"$d/directory\");\n\n$t->write_file('directory/file', 'SEE-THIS');\n\n###############################################################################\n\nlike(http_get('/x'), qr/X-Location: root/, 'root');\n\n# these all are mapped to \"/directory/\"\n\nlike(http_get('/directory/'), qr/X-Location: directory/, 'directory');\nlike(http_get('/Directory/'), qr/X-Location: directory/, 'directory caseless');\nlike(http_get('/directory./'), qr/X-Location: directory/, 'directory dot');\nlike(http_get('/directory.%2ffile'), qr/X-Location: directory/,\n\t'directory dot encoded slash');\nlike(http_get('/directory::$index_allocation/'),\n\tqr/X-Location: directory|400 Bad/,\n\t'directory stream');\nlike(http_get('/directory::$index_allocation./'),\n\tqr/X-Location: directory|400 Bad/,\n\t'directory stream dot');\nlike(http_get('/directory:$i30:$index_allocation./'),\n\tqr/X-Location: directory|400 Bad/,\n\t'directory i30 stream dot');\n\n# these looks similar, but shouldn't be mapped to \"/directory/\"\n\nlike(http_get('/directory../'), qr/X-Location: root/, 'directory dot dot');\nlike(http_get('/directory.::$index_allocation/'), qr/X-Location: root|400 Bad/,\n\t'directory dot stream');\n\n# short name, should be rejected\n\nunlike(http_get('/direct~1/file'), qr/SEE-THIS/, 'short name');\nunlike(http_get('/direct~1./file'), qr/SEE-THIS/, 'short name dot');\nunlike(http_get('/direct~1::$index_allocation./file'), qr/SEE-THIS/,\n\t'short name stream dot');\nunlike(http_get('/direct~1.::$index_allocation/file'), qr/SEE-THIS/,\n\t'short name dot stream');\n\n# these should be mapped to /file\n\nlike(http_get('/file'), qr/X-Location: file/, 'file');\nlike(http_get('/file.'), qr/X-Location: file/, 'file dot');\nlike(http_get('/file..'), qr/X-Location: file/, 'file dot dot');\nlike(http_get('/file%20.%20.'), qr/X-Location: file/, 'file dots and spaces');\nlike(http_get('/file::$data..'), qr/X-Location: file|400 Bad/,\n\t'file stream dot dot');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/http_method.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for HTTP methods.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http rewrite/)->plan(2)\n\t->write_file_expand('nginx.conf', <<'EOF')->run();\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            return 200;\n        }\n    }\n}\n\nEOF\n\n###############################################################################\n\nlike(http(<<EOF), qr/405 Not Allowed(?!.*200 OK)/s, 'trace');\nTRACE / HTTP/1.1\nHost: localhost\n\nGET / HTTP/1.1\nHost: localhost\nConnection: close\n\nEOF\n\nTODO: {\nlocal $TODO = 'not yet' unless $t->has_version('1.21.1');\n\nlike(http(<<EOF), qr/405 Not Allowed(?!.*200 OK)/s, 'connect');\nCONNECT / HTTP/1.1\nHost: localhost\n\nGET / HTTP/1.1\nHost: localhost\nConnection: close\n\nEOF\n\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/http_resolver.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for http resolver.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse IO::Select;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx qw/ :DEFAULT http_end /;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy rewrite/);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\nworker_processes 1;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            resolver    127.0.0.1:%%PORT_8981_UDP%%;\n            resolver_timeout 1s;\n            proxy_pass  http://$host:%%PORT_8080%%/backend;\n\n            proxy_next_upstream http_504 timeout error;\n            proxy_intercept_errors on;\n            proxy_connect_timeout 1s;\n            error_page 504 502 /50x;\n        }\n        location /two {\n            resolver    127.0.0.1:%%PORT_8981_UDP%% 127.0.0.1:%%PORT_8982_UDP%%;\n            proxy_pass  http://$host:%%PORT_8080%%/backend;\n        }\n        location /valid {\n            resolver    127.0.0.1:%%PORT_8981_UDP%% valid=5s;\n            proxy_pass  http://$host:%%PORT_8080%%/backend;\n        }\n        location /case {\n            resolver    127.0.0.1:%%PORT_8981_UDP%%;\n            proxy_pass  http://$http_x_name:%%PORT_8080%%/backend;\n        }\n        location /invalid {\n            proxy_pass  http://$host:%%PORT_8080%%/backend;\n        }\n        location /long {\n            resolver    127.0.0.1:%%PORT_8981_UDP%%;\n            resolver_timeout 4s;\n            proxy_pass  http://$host:%%PORT_8080%%/backend;\n        }\n        location /resend {\n            resolver    127.0.0.1:%%PORT_8981_UDP%%;\n            resolver_timeout 8s;\n            proxy_pass  http://$host:%%PORT_8080%%/backend;\n        }\n        location /bad {\n            resolver    127.0.0.1:%%PORT_8984_UDP%%;\n            resolver_timeout 1s;\n            proxy_pass  http://$host:%%PORT_8080%%/backend;\n        }\n        location /tcp {\n            resolver    127.0.0.1:%%PORT_8983_UDP%% 127.0.0.1:%%PORT_8982_UDP%%;\n            resolver_timeout 1s;\n            proxy_pass  http://$host:%%PORT_8080%%/backend;\n            proxy_connect_timeout 1s;\n            add_header X-IP $upstream_addr;\n            error_page 504 502 /50x;\n\n            location /tcp2 {\n                resolver_timeout 8s;\n                proxy_pass  http://$host:%%PORT_8080%%/backend;\n            }\n        }\n\n        location /backend {\n            return 200;\n        }\n        location /50x {\n            return 200 $upstream_addr;\n        }\n    }\n}\n\nEOF\n\n$t->run_daemon(\\&dns_daemon, port(8981), $t);\n$t->run_daemon(\\&dns_daemon, port(8982), $t);\n\n$t->run_daemon(\\&dns_daemon, port(8983), $t, tcp => 1);\n$t->waitforfile($t->testdir . '/' . port(8983));\nport(8983, socket => 1)->close();\n\n$t->run_daemon(\\&dns_daemon, port(8984), $t);\n\n$t->run()->plan(38);\n\n$t->waitforfile($t->testdir . '/' . port(8981));\n$t->waitforfile($t->testdir . '/' . port(8982));\n$t->waitforfile($t->testdir . '/' . port(8984));\n\n###############################################################################\n\nmy $p0 = port(8080);\n\n# schedule resend test, which takes about 5 seconds to complete\n\nmy $s = http_host_header('id.example.net', '/resend', start => 1);\nmy $fe = http_host_header('fe.example.net', '/resend', start => 1);\n\nlike(http_host_header('a.example.net', '/'), qr/200 OK/, 'A');\n\n# ensure that resolver serves queries from cache in a case-insensitive manner\n# we check this by marking 2nd and subsequent queries on backend with SERVFAIL\n\nhttp_x_name_header('case.example.net', '/case');\nlike(http_x_name_header('CASE.example.net', '/case'), qr/200 OK/,\n\t'A case-insensitive');\n\nlike(http_host_header('awide.example.net', '/'), qr/200 OK/, 'A uncompressed');\nlike(http_host_header('short.example.net', '/'), qr/502 Bad/,\n\t'A short dns response');\n\nlike(http_host_header('nx.example.net', '/'), qr/502 Bad/, 'NXDOMAIN');\nlike(http_host_header('cname.example.net', '/'), qr/200 OK/, 'CNAME');\nlike(http_host_header('cname.example.net', '/'), qr/200 OK/,\n\t'CNAME cached');\n\n# CNAME + A combined answer\n# demonstrates the name in answer section different from what is asked\n\nlike(http_host_header('cname_a.example.net', '/'), qr/200 OK/, 'CNAME + A');\n\n# CNAME refers to non-existing A\n\nlike(http_host_header('cname2.example.net', '/'), qr/502 Bad/, 'CNAME bad');\nlike(http_host_header('long.example.net', '/'), qr/200 OK/, 'long label');\nlike(http_host_header('long2.example.net', '/'), qr/200 OK/, 'long name');\n\n# take into account DNAME\n\nlike(http_host_header('alias.example.com', '/'), qr/200 OK/, 'DNAME');\n\n# many A records in round robin\n# nonexisting IPs enumerated with proxy_next_upstream\n\nlike(http_host_header('many.example.net', '/'),\n\tqr/^127.0.0.20(1:$p0, 127.0.0.202:$p0|2:$p0, 127.0.0.201:$p0)$/m,\n\t'A many');\n\nlike(http_host_header('many.example.net', '/'),\n\tqr/^127.0.0.20(1:$p0, 127.0.0.202:$p0|2:$p0, 127.0.0.201:$p0)$/m,\n\t'A many cached');\n\n# tests for several resolvers specified in directive\n# query bad ns, make sure that error responses are not cached\n\nlike(http_host_header('2.example.net', '/two'), qr/502 Bad/, 'two ns bad');\n\n# now get correct response\n\nlike(http_host_header('2.example.net', '/two'), qr/200 OK/, 'two ns good');\n\n# response is cached, actual request would get error\n\nlike(http_host_header('2.example.net', '/two'), qr/200 OK/, 'two ns cached');\n\n# ttl tested with 1st req good and 2nd req bad\n# send 1st request and cache its good response\n\nlike(http_host_header('ttl.example.net', '/'), qr/200 OK/, 'ttl');\n\n# response is cached, actual request would get error\n\nlike(http_host_header('ttl.example.net', '/'), qr/200 OK/, 'ttl cached 1');\nlike(http_host_header('ttl.example.net', '/'), qr/200 OK/, 'ttl cached 2');\n\nsleep 2;\n\n# expired ttl causes nginx to make actual query\n\nlike(http_host_header('ttl.example.net', '/'), qr/502 Bad/, 'ttl expired');\n\n# zero ttl prohibits response caching\n\nlike(http_host_header('ttl0.example.net', '/'), qr/200 OK/, 'zero ttl');\n\nTODO: {\nlocal $TODO = 'support for zero ttl';\n\nlike(http_host_header('ttl0.example.net', '/'), qr/502 Bad/,\n\t'zero ttl not cached');\n\n}\n\n# \"valid\" parameter tested with 1st req good and 2nd req bad\n# send 1st request and cache its good response\n\nlike(http_host_header('ttl.example.net', '/valid'), qr/200 OK/, 'valid');\n\n# response is cached, actual request would get error\n\nlike(http_host_header('ttl.example.net', '/valid'), qr/200 OK/,\n\t'valid cached 1');\nlike(http_host_header('ttl.example.net', '/valid'), qr/200 OK/,\n\t'valid cached 2');\n\nsleep 2;\n\n# expired ttl is overridden with \"valid\" parameter\n# response is taken from cache\n\nlike(http_host_header('ttl.example.net', '/valid'), qr/200 OK/,\n\t'valid overrides ttl');\n\nsleep 4;\n\n# expired \"valid\" value causes nginx to make actual query\n\nlike(http_host_header('ttl.example.net', '/valid'), qr/502 Bad/,\n\t'valid expired');\n\n# Ensure that resolver respects expired CNAME in CNAME + A combined response.\n# When ttl in CNAME is expired, the answer should not be served from cache.\n# Catch this by returning SERVFAIL on the 2nd and subsequent queries.\n\nhttp_host_header('cname_a_ttl2.example.net', '/');\n\nsleep 2;\n\nlike(http_host_header('cname_a_ttl2.example.net', '/'), qr/502 Bad/,\n\t'CNAME + A with expired CNAME ttl');\n\nSKIP: {\nskip 'no resolver';\nlike(http_host_header('example.net', '/invalid'), qr/502 Bad/, 'no resolver');\n}\nlike(http_end($s), qr/200 OK/, 'resend after malformed response');\nlike(http_end($fe), qr/200 OK/, 'resend after format error');\n\n$s = http_get('/bad', start => 1);\nmy $s2 = http_get('/bad', start => 1);\n\nhttp_end($s);\nok(http_end($s2), 'timeout handler on 2nd request');\n\nlike(http_host_header('fe_id.example.net', '/'), qr/502 Bad/, 'format error');\n\n# several requests waiting on same name query\n# 1st request aborts before name is resolved\n# 2nd request completes on resolver timeout\n\n$s = http_host_header('timeout.example.net', '/long', start => 1);\n$s2 = http_host_header('timeout.example.net', '/long', start => 1);\n\nselect undef, undef, undef, 1.1;\n\nclose $s;\n\nlike(http_end($s2), qr/502 Bad/, 'timeout after aborted request');\n\n# resend DNS query over TCP once UDP response came truncated\n\nunlike(http_host_header('tcp.example.net', '/tcp'), qr/127.0.0.201/, 'tc');\nlike(http_host_header('tcp.example.net', '/tcp'), qr/X-IP: 127.0.0.1/, 'tcp');\nlike(http_host_header('tcp2.example.net', '/tcp2'), qr/X-IP: 127.0.0.1/,\n\t'tcp with resend');\n\n###############################################################################\n\nsub http_host_header {\n\tmy ($host, $uri, %extra) = @_;\n\treturn http(<<EOF, %extra);\nGET $uri HTTP/1.0\nHost: $host\n\nEOF\n}\n\nsub http_x_name_header {\n\tmy ($host, $uri) = @_;\n\treturn http(<<EOF);\nGET $uri HTTP/1.0\nX-Name: $host\n\nEOF\n}\n\n###############################################################################\n\nsub reply_handler {\n\tmy ($recv_data, $port, $state, %extra) = @_;\n\n\tmy (@name, @rdata);\n\n\tuse constant NOERROR\t=> 0;\n\tuse constant FORMERR\t=> 1;\n\tuse constant SERVFAIL\t=> 2;\n\tuse constant NXDOMAIN\t=> 3;\n\n\tuse constant A\t\t=> 1;\n\tuse constant CNAME\t=> 5;\n\tuse constant DNAME\t=> 39;\n\n\tuse constant IN\t\t=> 1;\n\n\t# default values\n\n\tmy ($hdr, $rcode, $ttl) = (0x8180, NOERROR, 3600);\n\n\t# decode name\n\n\tmy ($len, $offset) = (undef, 12);\n\twhile (1) {\n\t\t$len = unpack(\"\\@$offset C\", $recv_data);\n\t\tlast if $len == 0;\n\t\t$offset++;\n\t\tpush @name, unpack(\"\\@$offset A$len\", $recv_data);\n\t\t$offset += $len;\n\t}\n\n\t$offset -= 1;\n\tmy ($id, $type, $class) = unpack(\"n x$offset n2\", $recv_data);\n\n\tmy $name = join('.', @name);\n\tif (($name eq 'a.example.net') || ($name eq 'alias.example.net')) {\n\t\tif ($type == A || $type == CNAME) {\n\t\t\tpush @rdata, rd_addr($ttl, '127.0.0.1');\n\t\t}\n\n\t} elsif ($name eq 'fe.example.net' && $type == A) {\n\t\tif (++$state->{fecnt} > 1) {\n\t\t\t$rcode = FORMERR;\n\t\t}\n\n\t\tpush @rdata, rd_addr($ttl, '127.0.0.1');\n\n\t} elsif ($name eq 'fe_id.example.net' && $type == A) {\n\t\t$id = 42;\n\t\t$rcode = FORMERR;\n\n\t} elsif ($name eq 'case.example.net' && $type == A) {\n\t\tif (++$state->{casecnt} > 1) {\n\t\t\t$rcode = SERVFAIL;\n\t\t}\n\n\t\tpush @rdata, rd_addr($ttl, '127.0.0.1');\n\n\t} elsif ($name eq 'id.example.net' && $type == A) {\n\t\tif (++$state->{idcnt} == 1) {\n\t\t\t$id++;\n\t\t}\n\n\t\tpush @rdata, rd_addr($ttl, '127.0.0.1');\n\n\t} elsif ($name eq 'awide.example.net' && $type == A) {\n\t\tpush @rdata, pack '(C/a*)3x n2N nC4',\n\t\t\t('awide', 'example', 'net'), A, IN, $ttl,\n\t\t\t4, (127, 0, 0, 1);\n\n\t} elsif (($name eq 'many.example.net') && $type == A) {\n\t\t$state->{manycnt}++;\n\t\tif ($state->{manycnt} > 1) {\n\t\t\t$rcode = SERVFAIL;\n\t\t}\n\n\t\tpush @rdata, rd_addr($ttl, '127.0.0.201');\n\t\tpush @rdata, rd_addr($ttl, '127.0.0.202');\n\n\n\t} elsif (($name eq 'short.example.net')) {\n\t\t# zero length RDATA in DNS response\n\n\t\tif ($type == A) {\n\t\t\tpush @rdata, rd_addr($ttl, '');\n\t\t}\n\n\t} elsif (($name eq 'alias.example.com')) {\n\t\t# example.com.       3600 IN DNAME example.net.\n\n\t\tmy @dname = ('example', 'net');\n\t\tmy $rdlen = length(join '', @dname) + @dname + 1;\n\t\tpush @rdata, pack(\"n3N n(C/a*)* x\", 0xc012, DNAME, IN, $ttl,\n\t\t\t$rdlen, @dname);\n\n\t\t# alias.example.com. 3600 IN CNAME alias.example.net.\n\n\t\tpush @rdata, pack(\"n3N nCa5n\", 0xc00c, CNAME, IN, $ttl,\n\t\t\t8, 5, 'alias', 0xc02f);\n\n\t} elsif ($name eq 'cname.example.net') {\n\t\t$state->{cnamecnt}++;\n\t\tif ($state->{cnamecnt} > 2) {\n\t\t\t$rcode = SERVFAIL;\n\t\t}\n\t\tpush @rdata, pack(\"n3N nCa5n\", 0xc00c, CNAME, IN, $ttl,\n\t\t\t8, 5, 'alias', 0xc012);\n\n\t} elsif ($name eq 'timeout.example.net') {\n\t\tselect undef, undef, undef, 2.1;\n\t\treturn;\n\n\t} elsif ($name eq 'cname_a.example.net') {\n\t\tpush @rdata, pack(\"n3N nCa5n\", 0xc00c, CNAME, IN, $ttl,\n\t\t\t8, 5, 'alias', 0xc014);\n\n\t\t# points to \"alias\" set in previous rdata\n\n\t\tif ($type == A) {\n\t\t\tpush @rdata, pack('n3N nC4', 0xc031, A, IN, $ttl,\n\t\t\t\t4, split(/\\./, '127.0.0.1'));\n\t\t}\n\n\t} elsif ($name eq 'cname_a_ttl2.example.net' && $type == A) {\n\t\tpush @rdata, pack(\"n3N nCa18n\", 0xc00c, CNAME, IN, 1,\n\t\t\t21, 18, 'cname_a_ttl2_alias', 0xc019);\n\t\tif (++$state->{cttl2cnt} >= 2) {\n\t\t\t$rcode = SERVFAIL;\n\t\t}\n\t\tpush @rdata, pack('n3N nC4', 0xc036, A, IN, $ttl,\n\t\t\t4, split(/\\./, '127.0.0.1'));\n\n\t} elsif ($name eq 'cname_a_ttl_alias.example.net' && $type == A) {\n\t\tpush @rdata, rd_addr($ttl, '127.0.0.1');\n\n\t} elsif ($name eq 'cname2.example.net') {\n\t\t# points to non-existing A\n\n\t\tpush @rdata, pack(\"n3N nCa2n\", 0xc00c, CNAME, IN, $ttl,\n\t\t\t5, 2, 'nx', 0xc02f);\n\n\t} elsif ($name eq 'long.example.net') {\n\t\tpush @rdata, pack(\"n3N nCA63x\", 0xc00c, CNAME, IN, $ttl,\n\t\t\t65, 63, 'a' x 63);\n\n\t} elsif (($name eq 'a' x 63) && $type == A) {\n\t\tpush @rdata, rd_addr($ttl, '127.0.0.1');\n\n\t} elsif ($name eq 'long2.example.net') {\n\t\tpush @rdata, pack(\"n3N n(CA63)4x\", 0xc00c, CNAME, IN, $ttl, 257,\n\t\t\t63, 'a' x 63, 63, 'a' x 63, 63, 'a' x 63, 63, 'a' x 63);\n\n\t} elsif (($name eq 'a' x 63 . '.' . 'a' x 63 . '.' . 'a' x 63 . '.'\n\t\t\t. 'a' x 63) && $type == A)\n\t{\n\t\tpush @rdata, rd_addr($ttl, '127.0.0.1');\n\n\t} elsif ($name eq 'ttl.example.net' && $type == A) {\n\t\t$state->{ttlcnt}++;\n\t\tif ($state->{ttlcnt} == 2 || $state->{ttlcnt} == 4) {\n\t\t\t$rcode = SERVFAIL;\n\t\t}\n\n\t\tpush @rdata, rd_addr(1, '127.0.0.1');\n\n\t} elsif ($name eq 'ttl0.example.net' && $type == A) {\n\t\t$state->{ttl0cnt}++;\n\t\tif ($state->{ttl0cnt} == 2) {\n\t\t\t$rcode = SERVFAIL;\n\t\t}\n\n\t\tpush @rdata, rd_addr(0, '127.0.0.1');\n\n\t} elsif ($name eq '2.example.net') {\n\t\tif ($port == port(8981)) {\n\t\t\t$state->{twocnt}++;\n\t\t}\n\t\tif ($state->{twocnt} & 1) {\n\t\t\t$rcode = SERVFAIL;\n\t\t}\n\n\t\tif ($type == A) {\n\t\t\tpush @rdata, rd_addr($ttl, '127.0.0.1');\n\t\t}\n\n\t} elsif ($name =~ /tcp2?.example.net/) {\n\t\t$rcode = FORMERR if $port == port(8982);\n\t\t$hdr |= 0x0300 unless $extra{tcp};\n\t\tpush @rdata, rd_addr($ttl, $extra{tcp}\n\t\t\t? '127.0.0.1' : '127.0.0.201') if $type == A;\n\t}\n\n\t$len = @name;\n\tpack(\"n6 (C/a*)$len x n2\", $id, $hdr | $rcode, 1, scalar @rdata,\n\t\t0, 0, @name, $type, $class) . join('', @rdata);\n}\n\nsub rd_addr {\n\tmy ($ttl, $addr) = @_;\n\n\tmy $code = 'split(/\\./, $addr)';\n\n\treturn pack 'n3N', 0xc00c, A, IN, $ttl if $addr eq '';\n\n\tpack 'n3N nC4', 0xc00c, A, IN, $ttl, eval \"scalar $code\", eval($code);\n}\n\nsub dns_daemon {\n\tmy ($port, $t, %extra) = @_;\n\n\tmy ($data, $recv_data);\n\tmy $socket = IO::Socket::INET->new(\n\t\tLocalAddr => '127.0.0.1',\n\t\tLocalPort => $port,\n\t\tProto => 'udp',\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\tmy $sel = IO::Select->new($socket);\n\tmy $tcp = 0;\n\n\tif ($extra{tcp}) {\n\t\t$tcp = port(8983, socket => 1);\n\t\t$sel->add($tcp);\n\t}\n\n\tlocal $SIG{PIPE} = 'IGNORE';\n\n\t# track number of relevant queries\n\n\tmy %state = (\n\t\tcnamecnt\t=> 0,\n\t\ttwocnt\t\t=> 0,\n\t\tttlcnt\t\t=> 0,\n\t\tttl0cnt\t\t=> 0,\n\t\tcttlcnt\t\t=> 0,\n\t\tcttl2cnt\t=> 0,\n\t\tmanycnt\t\t=> 0,\n\t\tcasecnt\t\t=> 0,\n\t\tidcnt\t\t=> 0,\n\t\tfecnt\t\t=> 0,\n\t);\n\n\t# signal we are ready\n\n\topen my $fh, '>', $t->testdir() . '/' . $port;\n\tclose $fh;\n\n\twhile (my @ready = $sel->can_read) {\n\t\tforeach my $fh (@ready) {\n\t\t\tif ($tcp == $fh) {\n\t\t\t\tmy $new = $fh->accept;\n\t\t\t\t$new->autoflush(1);\n\t\t\t\t$sel->add($new);\n\n\t\t\t} elsif ($socket == $fh) {\n\t\t\t\t$fh->recv($recv_data, 65536);\n\t\t\t\t$data = reply_handler($recv_data, $port,\n\t\t\t\t\t\\%state);\n\t\t\t\t$fh->send($data);\n\n\t\t\t} else {\n\t\t\t\t$fh->recv($recv_data, 65536);\n\t\t\t\tunless (length $recv_data) {\n\t\t\t\t\t$sel->remove($fh);\n\t\t\t\t\t$fh->close;\n\t\t\t\t\tnext;\n\t\t\t\t}\n\nagain:\n\t\t\t\tmy $len = unpack(\"n\", $recv_data);\n\t\t\t\t$data = substr $recv_data, 2, $len;\n\t\t\t\t$data = reply_handler($data, $port, \\%state,\n\t\t\t\t\ttcp => 1);\n\t\t\t\t$data = pack(\"n\", length $data) . $data;\n\t\t\t\t$fh->send($data);\n\t\t\t\t$recv_data = substr $recv_data, 2 + $len;\n\t\t\t\tgoto again if length $recv_data;\n\t\t\t}\n\t\t}\n\t}\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/http_resolver_aaaa.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for AAAA capable http resolver.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy rewrite/);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        listen       [::1]:%%PORT_8080%%;\n        server_name  localhost;\n\n        location / {\n            resolver    127.0.0.1:%%PORT_8981_UDP%%;\n            proxy_pass  http://$host:%%PORT_8080%%/backend;\n\n            proxy_next_upstream http_504 timeout error;\n            proxy_intercept_errors on;\n            proxy_connect_timeout 50ms;\n            error_page 504 502 /50x;\n            add_header X-Host $upstream_addr;\n        }\n        location /two {\n            resolver    127.0.0.1:%%PORT_8981_UDP%% 127.0.0.1:%%PORT_8982_UDP%%;\n            proxy_pass  http://$host:%%PORT_8080%%/backend;\n        }\n\n        location /backend {\n            return 200;\n        }\n        location /50x {\n            return 200 $upstream_addr;\n        }\n    }\n}\n\nEOF\n\n$t->try_run('no inet6 support')->plan(72);\n\n$t->run_daemon(\\&dns_daemon, port(8981), $t);\n$t->run_daemon(\\&dns_daemon, port(8982), $t);\n\n$t->waitforfile($t->testdir . '/' . port(8981));\n$t->waitforfile($t->testdir . '/' . port(8982));\n\n###############################################################################\n\nmy (@n, $response);\n\nmy $p0 = port(8080);\n\nlike(http_host_header('aaaa.example.net', '/'), qr/\\[fe80::1\\]/, 'AAAA');\nlike(http_host_header('cname.example.net', '/'), qr/\\[fe80::1\\]/, 'CNAME');\nlike(http_host_header('cname.example.net', '/'), qr/\\[fe80::1\\]/,\n\t'CNAME cached');\n\n# CNAME + AAAA combined answer\n# demonstrates the name in answer section different from what is asked\n\nlike(http_host_header('cname_a.example.net', '/'), qr/\\[::1\\]/, 'CNAME + AAAA');\n\n# many AAAA records in round robin\n# nonexisting IPs enumerated with proxy_next_upstream\n\nlike(http_host_header('many.example.net', '/'),\n\tqr/^\\[fe80::(1\\]:$p0, \\[fe80::2\\]:$p0|2\\]:$p0, \\[fe80::1\\]:$p0)$/m,\n\t'AAAA many');\n\nlike(http_host_header('many.example.net', '/'),\n\tqr/^\\[fe80::(1\\]:$p0, \\[fe80::2\\]:$p0|2\\]:$p0, \\[fe80::1\\]:$p0)$/m,\n\t'AAAA many cached');\n\n# tests for several resolvers specified in directive\n# query bad ns, make sure that error responses are not cached\n\nlike(http_host_header('2.example.net', '/two'), qr/502 Bad/, 'two ns bad');\n\n# now get correct response\n\nlike(http_host_header('2.example.net', '/two'), qr/200 OK/, 'two ns good');\n\n# response is cached, actual request would get error\n\nlike(http_host_header('2.example.net', '/two'), qr/200 OK/, 'two ns cached');\n\n# various ipv4/ipv6 combinations\n\n$response = http_host_header('z_z.example.net', '/');\nis(@n = $response =~ /$p0/g, 0, 'zero zero responses');\nlike($response, qr/502 Bad/, 'zero zero');\n\nlike(http_host_header('z_n.example.net', '/'), qr/^\\[fe80::1\\]:$p0$/ms,\n\t'zero AAAA');\n\n$response = http_host_header('z_c.example.net', '/');\nis(@n = $response =~ /$p0/g, 2, 'zero CNAME responses');\nlike($response, qr/127.0.0.201:$p0/, 'zero CNAME 1');\nlike($response, qr/\\[fe80::1\\]:$p0/, 'zero CNAME 2');\n\n$response = http_host_header('z_cn.example.net', '/');\nis(@n = $response =~ /$p0/g, 2, 'zero CNAME+AAAA responses');\nlike($response, qr/\\[fe80::1\\]:$p0/, 'zero CNAME+AAAA 1');\nlike($response, qr/\\[fe80::2\\]:$p0/, 'zero CNAME+AAAA 2');\n\n$response = http_host_header('z_e.example.net', '/');\nis(@n = $response =~ /$p0/g, 0, 'zero error responses');\nlike($response, qr/502 Bad/, 'zero error');\n\nlike(http_host_header('n_z.example.net', '/'), qr/^127.0.0.201:$p0$/ms,\n\t'A zero');\n\n$response = http_host_header('n_n.example.net', '/');\nis(@n = $response =~ /$p0/g, 2, 'A AAAA responses');\nlike($response, qr/127.0.0.201:$p0/, 'A AAAA 1');\nlike($response, qr/\\[fe80::1\\]:$p0/, 'A AAAA 2');\n\nlike(http_host_header('n_c.example.net', '/'), qr/^127.0.0.201:$p0$/ms,\n\t'A CNAME');\n\n$response = http_host_header('n_cn.example.net', '/');\nis(@n = $response =~ /$p0/g, 4, 'A CNAME+AAAA responses');\nlike($response, qr/127.0.0.201:$p0/, 'A CNAME+AAAA 1');\nlike($response, qr/127.0.0.202:$p0/, 'A CNAME+AAAA 2');\nlike($response, qr/\\[fe80::1\\]:$p0/, 'A CNAME+AAAA 3');\nlike($response, qr/\\[fe80::2\\]:$p0/, 'A CNAME+AAAA 4');\n\n$response = http_host_header('n_e.example.net', '/');\nis(@n = $response =~ /$p0/g, 0, 'A error responses');\nlike($response, qr/502 Bad/, 'A error');\n\n$response = http_host_header('c_z.example.net', '/');\nis(@n = $response =~ /$p0/g, 0, 'CNAME zero responses');\nlike($response, qr/502 Bad/, 'CNAME zero');\n\nlike(http_host_header('c_n.example.net', '/'), qr/^\\[fe80::1\\]:$p0$/ms,\n\t'CNAME AAAA');\n\n$response = http_host_header('c_c.example.net', '/');\nis(@n = $response =~ /$p0/g, 2, 'CNAME CNAME responses');\nlike($response, qr/127.0.0.201:$p0/, 'CNAME CNAME 1');\nlike($response, qr/\\[fe80::1\\]:$p0/, 'CNAME CNAME 2');\n\nlike(http_host_header('c1_c2.example.net', '/'), qr/^\\[fe80::1\\]:$p0$/ms,\n\t'CNAME1 CNAME2');\n\n$response = http_host_header('c_cn.example.net', '/');\nis(@n = $response =~ /$p0/g, 2, 'CNAME CNAME+AAAA responses');\nlike($response, qr/\\[fe80::1\\]:$p0/, 'CNAME CNAME+AAAA 1');\nlike($response, qr/\\[fe80::2\\]:$p0/, 'CNAME CNAME+AAAA 1');\n\n$response = http_host_header('c_e.example.net', '/');\nis(@n = $response =~ /$p0/g, 0, 'CNAME error responses');\nlike($response, qr/502 Bad/, 'CNAME error');\n\n$response = http_host_header('cn_z.example.net', '/');\nis(@n = $response =~ /$p0/g, 2, 'CNAME+A zero responses');\nlike($response, qr/127.0.0.201:$p0/, 'CNAME+A zero 1');\nlike($response, qr/127.0.0.202:$p0/, 'CNAME+A zero 2');\n\n$response = http_host_header('cn_n.example.net', '/');\nis(@n = $response =~ /$p0/g, 4, 'CNAME+A AAAA responses');\nlike($response, qr/127.0.0.201:$p0/, 'CNAME+A AAAA 1');\nlike($response, qr/127.0.0.202:$p0/, 'CNAME+A AAAA 2');\nlike($response, qr/\\[fe80::1\\]:$p0/, 'CNAME+A AAAA 3');\nlike($response, qr/\\[fe80::2\\]:$p0/, 'CNAME+A AAAA 4');\n\n$response = http_host_header('cn_c.example.net', '/');\nis(@n = $response =~ /$p0/g, 2, 'CNAME+A CNAME responses');\nlike($response, qr/127.0.0.201:$p0/, 'CNAME+A CNAME 1');\nlike($response, qr/127.0.0.202:$p0/, 'CNAME+A CNAME 2');\n\n$response = http_host_header('cn_cn.example.net', '/');\nis(@n = $response =~ /$p0/g, 4, 'CNAME+A CNAME+AAAA responses');\nlike($response, qr/127.0.0.201:$p0/, 'CNAME+A CNAME+AAAA 1');\nlike($response, qr/127.0.0.202:$p0/, 'CNAME+A CNAME+AAAA 2');\nlike($response, qr/\\[fe80::1\\]:$p0/, 'CNAME+A CNAME+AAAA 3');\nlike($response, qr/\\[fe80::2\\]:$p0/, 'CNAME+A CNAME+AAAA 4');\n\n$response = http_host_header('cn_e.example.net', '/');\nis(@n = $response =~ /$p0/g, 0, 'CNAME+A error responses');\nlike($response, qr/502 Bad/, 'CNAME+A error');\n\n$response = http_host_header('e_z.example.net', '/');\nis(@n = $response =~ /$p0/g, 0, 'error zero responses');\nlike($response, qr/502 Bad/, 'error zero');\n\n$response = http_host_header('e_n.example.net', '/');\nis(@n = $response =~ /$p0/g, 0, 'error AAAA responses');\nlike($response, qr/502 Bad/, 'error AAAA');\n\n$response = http_host_header('e_c.example.net', '/');\nis(@n = $response =~ /$p0/g, 0, 'error CNAME responses');\nlike($response, qr/502 Bad/, 'error CNAME');\n\n$response = http_host_header('e_cn.example.net', '/');\nis(@n = $response =~ /$p0/g, 0, 'error CNAME+AAAA responses');\nlike($response, qr/502 Bad/, 'error CNAME+AAAA');\n\n$response = http_host_header('e_e.example.net', '/');\nis(@n = $response =~ /$p0/g, 0, 'error error responses');\nlike($response, qr/502 Bad/, 'error error');\n\n###############################################################################\n\nsub http_host_header {\n\tmy ($host, $uri) = @_;\n\treturn http(<<EOF);\nGET $uri HTTP/1.0\nHost: $host\n\nEOF\n}\n\n###############################################################################\n\nsub reply_handler {\n\tmy ($recv_data, $port, $state) = @_;\n\n\tmy (@name, @rdata);\n\n\tuse constant NOERROR\t=> 0;\n\tuse constant SERVFAIL\t=> 2;\n\tuse constant NXDOMAIN\t=> 3;\n\n\tuse constant A\t\t=> 1;\n\tuse constant CNAME\t=> 5;\n\tuse constant AAAA\t=> 28;\n\tuse constant DNAME\t=> 39;\n\n\tuse constant IN\t\t=> 1;\n\n\t# default values\n\n\tmy ($hdr, $rcode, $ttl) = (0x8180, NOERROR, 3600);\n\n\t# decode name\n\n\tmy ($len, $offset) = (undef, 12);\n\twhile (1) {\n\t\t$len = unpack(\"\\@$offset C\", $recv_data);\n\t\tlast if $len == 0;\n\t\t$offset++;\n\t\tpush @name, unpack(\"\\@$offset A$len\", $recv_data);\n\t\t$offset += $len;\n\t}\n\n\t$offset -= 1;\n\tmy ($id, $type, $class) = unpack(\"n x$offset n2\", $recv_data);\n\n\tmy $name = join('.', @name);\n\tif (($name eq 'aaaa.example.net') || ($name eq 'alias.example.net')) {\n\t\tif ($type == AAAA) {\n\t\t\tpush @rdata, rd_addr6($ttl, \"fe80::1\");\n\t\t}\n\n\t} elsif ($name eq 'alias2.example.net') {\n\t\tif ($type == A) {\n\t\t\tpush @rdata, rd_addr($ttl, '127.0.0.201');\n\t\t}\n\t\tif ($type == AAAA) {\n\t\t\tpush @rdata, rd_addr6($ttl, \"fe80::1\");\n\t\t}\n\n\t} elsif ($name eq 'alias4.example.net') {\n\t\tif ($type == A) {\n\t\t\tpush @rdata, rd_addr($ttl, '127.0.0.201');\n\t\t}\n\n\t} elsif ($name eq 'alias6.example.net') {\n\t\tif ($type == AAAA) {\n\t\t\tpush @rdata, rd_addr6($ttl, \"fe80::1\");\n\t\t}\n\n\t} elsif (($name eq 'many.example.net') && $type == AAAA) {\n\t\t$state->{manycnt}++;\n\t\tif ($state->{manycnt} > 1) {\n\t\t\t$rcode = SERVFAIL;\n\t\t}\n\n\t\tpush @rdata, rd_addr6($ttl, 'fe80::1');\n\t\tpush @rdata, rd_addr6($ttl, 'fe80::2');\n\n\t} elsif ($name eq 'cname.example.net') {\n\t\t$state->{cnamecnt}++;\n\t\tif ($state->{cnamecnt} > 2) {\n\t\t\t$rcode = SERVFAIL;\n\t\t}\n\t\tpush @rdata, pack(\"n3N nCa5n\", 0xc00c, CNAME, IN, $ttl,\n\t\t\t8, 5, 'alias', 0xc012);\n\n\t} elsif ($name eq 'cname_a.example.net') {\n\t\tpush @rdata, pack(\"n3N nCa5n\", 0xc00c, CNAME, IN, $ttl,\n\t\t\t8, 5, 'alias', 0xc014);\n\n\t\t# points to \"alias\" set in previous rdata\n\n\t\tif ($type == AAAA) {\n\t\t\tpush @rdata, pack('n3N nn8', 0xc031, AAAA, IN, $ttl,\n\t\t\t\t16, expand_ip6(\"::1\"));\n\t\t}\n\n\t} elsif ($name eq '2.example.net') {\n\t\tif ($port == port(8981)) {\n\t\t\t$state->{twocnt}++;\n\t\t}\n\t\tif ($state->{twocnt} & 1) {\n\t\t\t$rcode = SERVFAIL;\n\t\t}\n\n\t\tif ($type == AAAA) {\n\t\t\tpush @rdata, rd_addr6($ttl, '::1');\n\t\t}\n\n\t} elsif ($name eq 'z_z.example.net') {\n\t\t# assume no answers given\n\n\t} elsif ($name eq 'z_n.example.net') {\n\t\tif ($type == AAAA) {\n\t\t\tpush @rdata, rd_addr6($ttl, 'fe80::1');\n\t\t}\n\n\t} elsif ($name eq 'z_c.example.net') {\n\t\tif ($type == AAAA) {\n\t\t\tpush @rdata, pack(\"n3N nCa6n\", 0xc00c, CNAME, IN, $ttl,\n\t\t\t\t9, 6, 'alias2', 0xc010);\n\t\t}\n\n\t} elsif ($name eq 'z_cn.example.net') {\n\t\tif ($type == AAAA) {\n\t\t\tpush @rdata, pack(\"n3N nCa5n\", 0xc00c, CNAME, IN, $ttl,\n\t\t\t\t8, 5, 'alias', 0xc011);\n\t\t\tpush @rdata, pack('n3N nn8', 0xc02e, AAAA, IN, $ttl,\n\t\t\t\t16, expand_ip6(\"fe80::1\"));\n\t\t\tpush @rdata, pack('n3N nn8', 0xc02e, AAAA, IN, $ttl,\n\t\t\t\t16, expand_ip6(\"fe80::2\"));\n\t\t}\n\n\t} elsif ($name eq 'z_e.example.net') {\n\t\tif ($type == AAAA) {\n\t\t\t$rcode = SERVFAIL;\n\t\t}\n\n\t} elsif ($name eq 'n_z.example.net') {\n\t\tif ($type == A) {\n\t\t\tpush @rdata, rd_addr($ttl, '127.0.0.201');\n\t\t}\n\n\t} elsif ($name eq 'n_n.example.net') {\n\t\tif ($type == A) {\n\t\t\tpush @rdata, rd_addr($ttl, '127.0.0.201');\n\t\t}\n\t\tif ($type == AAAA) {\n\t\t\tpush @rdata, rd_addr6($ttl, 'fe80::1');\n\t\t}\n\n\t} elsif ($name eq 'n_c.example.net') {\n\t\tif ($type == A) {\n\t\t\tpush @rdata, rd_addr($ttl, '127.0.0.201');\n\t\t}\n\t\tif ($type == AAAA) {\n\t\t\tpush @rdata, pack(\"n3N nCa6n\", 0xc00c, CNAME, IN, $ttl,\n\t\t\t\t9, 6, 'alias2', 0xc010);\n\t\t}\n\n\t} elsif ($name eq 'n_cn.example.net') {\n\t\tif ($type == A) {\n\t\t\tpush @rdata, rd_addr($ttl, '127.0.0.201');\n\t\t\tpush @rdata, rd_addr($ttl, '127.0.0.202');\n\t\t}\n\t\tif ($type == AAAA) {\n\t\t\tpush @rdata, pack(\"n3N nCa5n\", 0xc00c, CNAME, IN, $ttl,\n\t\t\t\t8, 5, 'alias', 0xc011);\n\t\t\tpush @rdata, pack('n3N nn8', 0xc02e, AAAA, IN, $ttl,\n\t\t\t\t16, expand_ip6(\"fe80::1\"));\n\t\t\tpush @rdata, pack('n3N nn8', 0xc02e, AAAA, IN, $ttl,\n\t\t\t\t16, expand_ip6(\"fe80::2\"));\n\t\t}\n\n\t} elsif ($name eq 'n_e.example.net') {\n\t\tif ($type == A) {\n\t\t\tpush @rdata, rd_addr($ttl, '127.0.0.201');\n\t\t}\n\t\tif ($type == AAAA) {\n\t\t\t$rcode = SERVFAIL;\n\t\t}\n\n\t} elsif ($name eq 'c_z.example.net') {\n\t\tif ($type == A) {\n\t\t\tpush @rdata, pack(\"n3N nCa5n\", 0xc00c, CNAME, IN, $ttl,\n\t\t\t\t8, 5, 'alias', 0xc010);\n\t\t}\n\n\t} elsif ($name eq 'c_n.example.net') {\n\t\tif ($type == A) {\n\t\t\tpush @rdata, pack(\"n3N nCa5n\", 0xc00c, CNAME, IN, $ttl,\n\t\t\t\t8, 5, 'alias', 0xc010);\n\t\t}\n\t\tif ($type == AAAA) {\n\t\t\tpush @rdata, rd_addr6($ttl, \"fe80::1\");\n\t\t}\n\n\t} elsif ($name eq 'c_c.example.net') {\n\t\tpush @rdata, pack(\"n3N nCa6n\", 0xc00c, CNAME, IN, $ttl,\n\t\t\t9, 6, 'alias2', 0xc010);\n\n\t} elsif ($name eq 'c1_c2.example.net') {\n\t\tif ($type == A) {\n\t\t\tpush @rdata, pack(\"n3N nCa6n\", 0xc00c, CNAME, IN, $ttl,\n\t\t\t\t9, 6, 'alias4', 0xc012);\n\t\t}\n\t\tif ($type == AAAA) {\n\t\t\tpush @rdata, pack(\"n3N nCa6n\", 0xc00c, CNAME, IN, $ttl,\n\t\t\t\t9, 6, 'alias6', 0xc012);\n\t\t}\n\n\t} elsif ($name eq 'c_cn.example.net') {\n\t\tpush @rdata, pack(\"n3N nCa6n\", 0xc00c, CNAME, IN, $ttl,\n\t\t\t9, 6, 'alias2', 0xc011);\n\n\t\tif ($type == AAAA) {\n\t\t\tpush @rdata, pack('n3N nn8', 0xc02e, AAAA, IN, $ttl,\n\t\t\t\t16, expand_ip6(\"fe80::1\"));\n\t\t\tpush @rdata, pack('n3N nn8', 0xc02e, AAAA, IN, $ttl,\n\t\t\t\t16, expand_ip6(\"fe80::2\"));\n\t\t}\n\n\t} elsif ($name eq 'cn_z.example.net') {\n\t\tif ($type == A) {\n\t\t\tpush @rdata, pack(\"n3N nCa6n\", 0xc00c, CNAME, IN, $ttl,\n\t\t\t\t9, 6, 'alias2', 0xc011);\n\t\t\tpush @rdata, pack(\"n3N nC4\", 0xc02e, A, IN, $ttl,\n\t\t\t\t4, split('\\.', '127.0.0.201'));\n\t\t\tpush @rdata, pack(\"n3N nC4\", 0xc02e, A, IN, $ttl,\n\t\t\t\t4, split('\\.', '127.0.0.202'));\n\t\t}\n\n\t} elsif ($name eq 'cn_n.example.net') {\n\t\tif ($type == A) {\n\t\t\tpush @rdata, pack(\"n3N nCa6n\", 0xc00c, CNAME, IN, $ttl,\n\t\t\t\t9, 6, 'alias2', 0xc011);\n\t\t\tpush @rdata, pack(\"n3N nC4\", 0xc02e, A, IN, $ttl,\n\t\t\t\t4, split('\\.', '127.0.0.201'));\n\t\t\tpush @rdata, pack(\"n3N nC4\", 0xc02e, A, IN, $ttl,\n\t\t\t\t4, split('\\.', '127.0.0.202'));\n\t\t}\n\t\tif ($type == AAAA) {\n\t\t\tpush @rdata, pack('n3N nn8', 0xc00c, AAAA, IN, $ttl,\n\t\t\t\t16, expand_ip6(\"fe80::1\"));\n\t\t\tpush @rdata, pack('n3N nn8', 0xc00c, AAAA, IN, $ttl,\n\t\t\t\t16, expand_ip6(\"fe80::2\"));\n\t\t}\n\n\t} elsif ($name eq 'cn_c.example.net') {\n\t\tpush @rdata, pack(\"n3N nCa6n\", 0xc00c, CNAME, IN, $ttl,\n\t\t\t9, 6, 'alias2', 0xc011);\n\t\tif ($type == A) {\n\t\t\tpush @rdata, pack(\"n3N nC4\", 0xc02e, A, IN, $ttl,\n\t\t\t\t4, split('\\.', '127.0.0.201'));\n\t\t\tpush @rdata, pack(\"n3N nC4\", 0xc02e, A, IN, $ttl,\n\t\t\t\t4, split('\\.', '127.0.0.202'));\n\t\t}\n\n\t} elsif ($name eq 'cn_cn.example.net') {\n\t\tpush @rdata, pack(\"n3N nCa6n\", 0xc00c, CNAME, IN, $ttl,\n\t\t\t9, 6, 'alias2', 0xc012);\n\n\t\tif ($type == A) {\n\t\t\tpush @rdata, pack(\"n3N nC4\", 0xc02f, A, IN, $ttl,\n\t\t\t\t4, split('\\.', '127.0.0.201'));\n\t\t\tpush @rdata, pack(\"n3N nC4\", 0xc02f, A, IN, $ttl,\n\t\t\t\t4, split('\\.', '127.0.0.202'));\n\t\t}\n\t\tif ($type == AAAA) {\n\t\t\tpush @rdata, pack('n3N nn8', 0xc02f, AAAA, IN, $ttl,\n\t\t\t\t16, expand_ip6(\"fe80::1\"));\n\t\t\tpush @rdata, pack('n3N nn8', 0xc02f, AAAA, IN, $ttl,\n\t\t\t\t16, expand_ip6(\"fe80::2\"));\n\t\t}\n\n\t} elsif ($name eq 'cn_e.example.net') {\n\t\tif ($type == A) {\n\t\t\tpush @rdata, pack(\"n3N nCa6n\", 0xc00c, CNAME, IN, $ttl,\n\t\t\t\t9, 6, 'alias2', 0xc011);\n\t\t\tpush @rdata, pack(\"n3N nC4\", 0xc02e, A, IN, $ttl,\n\t\t\t\t4, split('\\.', '127.0.0.201'));\n\t\t\tpush @rdata, pack(\"n3N nC4\", 0xc02e, A, IN, $ttl,\n\t\t\t\t4, split('\\.', '127.0.0.202'));\n\t\t}\n\t\tif ($type == AAAA) {\n\t\t\t$rcode = SERVFAIL;\n\t\t}\n\n\n\t} elsif ($name eq 'e_z.example.net') {\n\t\tif ($type == A) {\n\t\t\t$rcode = SERVFAIL;\n\t\t}\n\n\t} elsif ($name eq 'e_n.example.net') {\n\t\tif ($type == A) {\n\t\t\t$rcode = SERVFAIL;\n\t\t}\n\t\tif ($type == AAAA) {\n\t\t\tpush @rdata, rd_addr6($ttl, 'fe80::1');\n\t\t}\n\n\t} elsif ($name eq 'e_c.example.net') {\n\t\tif ($type == A) {\n\t\t\t$rcode = SERVFAIL;\n\t\t}\n\t\tif ($type == AAAA) {\n\t\t\tpush @rdata, pack(\"n3N nCa6n\", 0xc00c, CNAME, IN, $ttl,\n\t\t\t\t9, 6, 'alias2', 0xc010);\n\t\t}\n\n\t} elsif ($name eq 'e_cn.example.net') {\n\t\tif ($type == A) {\n\t\t\t$rcode = SERVFAIL;\n\t\t}\n\t\tif ($type == AAAA) {\n\t\t\tpush @rdata, pack(\"n3N nCa5n\", 0xc00c, CNAME, IN, $ttl,\n\t\t\t\t8, 5, 'alias', 0xc011);\n\t\t\tpush @rdata, pack('n3N nn8', 0xc02e, AAAA, IN, $ttl,\n\t\t\t\t16, expand_ip6(\"fe80::1\"));\n\t\t\tpush @rdata, pack('n3N nn8', 0xc02e, AAAA, IN, $ttl,\n\t\t\t\t16, expand_ip6(\"fe80::2\"));\n\t\t}\n\n\t} elsif ($name eq 'e_e.example.net') {\n\t\tif ($type == A) {\n\t\t\t$rcode = SERVFAIL;\n\t\t}\n\t\tif ($type == AAAA) {\n\t\t\t$rcode = NXDOMAIN;\n\t\t}\n\t}\n\n\t$len = @name;\n\tpack(\"n6 (C/a*)$len x n2\", $id, $hdr | $rcode, 1, scalar @rdata,\n\t\t0, 0, @name, $type, $class) . join('', @rdata);\n}\n\nsub rd_addr {\n\tmy ($ttl, $addr) = @_;\n\n\tmy $code = 'split(/\\./, $addr)';\n\n\tpack 'n3N nC4', 0xc00c, A, IN, $ttl, eval \"scalar $code\", eval($code);\n}\n\nsub expand_ip6 {\n\tmy ($addr) = @_;\n\n\tsubstr ($addr, index($addr, \"::\"), 2) =\n\t\tjoin \"0\", map { \":\" } (0 .. 8 - (split /:/, $addr) + 1);\n\tmap { hex \"0\" x (4 - length $_) . \"$_\" } split /:/, $addr;\n}\n\nsub rd_addr6 {\n\tmy ($ttl, $addr) = @_;\n\n\tpack 'n3N nn8', 0xc00c, AAAA, IN, $ttl, 16, expand_ip6($addr);\n}\n\nsub dns_daemon {\n\tmy ($port, $t) = @_;\n\n\tmy ($data, $recv_data);\n\tmy $socket = IO::Socket::INET->new(\n\t\tLocalAddr => '127.0.0.1',\n\t\tLocalPort => $port,\n\t\tProto => 'udp',\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\t# track number of relevant queries\n\n\tmy %state = (\n\t\tcnamecnt\t=> 0,\n\t\ttwocnt\t\t=> 0,\n\t\tmanycnt\t\t=> 0,\n\t);\n\n\t# signal we are ready\n\n\topen my $fh, '>', $t->testdir() . '/' . $port;\n\tclose $fh;\n\n\twhile (1) {\n\t\t$socket->recv($recv_data, 65536);\n\t\t$data = reply_handler($recv_data, $port, \\%state);\n\t\t$socket->send($data);\n\t}\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/http_resolver_cleanup.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for http resolver, worker process termination.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy/);\n\nplan(skip_all => 'win32') if $^O eq 'MSWin32';\n\n$t->write_file_expand('nginx.conf', <<'EOF')->plan(1);\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            resolver 127.0.0.1:%%PORT_8981_UDP%%;\n            proxy_pass http://example.net/$args;\n        }\n\n        location /pid {\n            add_header X-Pid $pid always;\n        }\n    }\n}\n\nEOF\n\n$t->run_daemon(\\&dns_daemon, $t);\n$t->run()->waitforfile($t->testdir . '/' . port(8981));\n\n###############################################################################\n\n# truncated UDP response, no response over TCP\n\nmy $s = http_get('/', start => 1);\n\npass('request');\n\nsleep 1;\n\n# retrasmission timer wasn't removed during resolver cleanup,\n# while the event memory was freed, resulting in use-after-free\n# when later removing timer in TCP connection\n\nhttp_get('/pid') =~ qr/X-Pid: (\\d+)/;\nkill 'TERM', $1;\n\n###############################################################################\n\nsub dns_daemon {\n\tmy ($t) = @_;\n\tmy ($data);\n\n\tmy $socket = IO::Socket::INET->new(\n\t\tLocalAddr => '127.0.0.1',\n\t\tLocalPort => port(8981),\n\t\tProto => 'udp',\n\t)\n\t\tor die \"Can't create UDP socket: $!\\n\";\n\n\t# signal we are ready\n\n\topen my $fh, '>', $t->testdir() . '/' . port(8981);\n\tclose $fh;\n\n\twhile (1) {\n\t\t$socket->recv($data, 65536);\n\t\t# truncation bit set\n\t\t$data |= pack(\"n2\", 0, 0x8380);\n\t\t$socket->send($data);\n\t}\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/http_resolver_cname.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for http resolver with CNAME.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx qw/ :DEFAULT http_end /;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy/)->plan(11);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location /short {\n            resolver    127.0.0.1:%%PORT_8981_UDP%%;\n            resolver_timeout 2s;\n\n            proxy_pass  http://$host:%%PORT_8080%%/t;\n        }\n\n        location /long {\n            resolver    127.0.0.1:%%PORT_8981_UDP%%;\n            resolver_timeout 5s;\n\n            proxy_pass  http://$host:%%PORT_8080%%/t;\n        }\n\n        location / { }\n    }\n}\n\nEOF\n\n$t->run_daemon(\\&dns_daemon, port(8981), $t);\n\n$t->write_file('t', '');\n$t->run();\n\n$t->waitforfile($t->testdir . '/' . port(8981));\n\n###############################################################################\n\n# CNAME pointing to name which times out\n\nlike(http_host('cn01.example.net', '/short'), qr/502 Bad/, 'CNAME timeout');\n\n# several requests on CNAME pointing to invalid name\n\nmy @s;\n\npush @s, http_host('cn03.example.net', '/long', start => 1);\npush @s, http_host('cn03.example.net', '/long', start => 1);\n\nlike(http_end(pop @s), qr/502 Bad/, 'invalid CNAME - first');\nlike(http_end(pop @s), qr/502 Bad/, 'invalid CNAME - last');\n\n# several requests on CNAME pointing to cached name\n\n@s = ();\n\nhttp_host('a.example.net', '/long');\n\npush @s, http_host('cn04.example.net', '/long', start => 1);\npush @s, http_host('cn04.example.net', '/long', start => 1);\n\nlike(http_end(pop @s), qr/200 OK/, 'cached CNAME - first');\nlike(http_end(pop @s), qr/200 OK/, 'cached CNAME - last');\n\n# several requests on CNAME pointing to name being resolved\n\n@s = ();\n\nmy $s = http_host('cn06.example.net', '/long', start => 1);\n\nsleep 1;\n\npush @s, http_host('cn05.example.net', '/long', start => 1);\npush @s, http_host('cn05.example.net', '/long', start => 1);\n\nlike(http_end(pop @s), qr/502 Bad/, 'CNAME in progress - first');\nlike(http_end(pop @s), qr/502 Bad/, 'CNAME in progress - last');\n\n# several requests on CNAME pointing to name which times out\n# 1st request receives CNAME with short ttl\n# 2nd request replaces expired CNAME\n\n@s = ();\n\npush @s, http_host('cn07.example.net', '/long', start => 1);\n\nsleep 2;\n\npush @s, http_host('cn07.example.net', '/long', start => 1);\n\nlike(http_end(pop @s), qr/502 Bad/, 'CNAME ttl - first');\nlike(http_end(pop @s), qr/502 Bad/, 'CNAME ttl - last');\n\n# several requests on CNAME pointing to name\n# 1st request aborts before name is resolved\n# 2nd request finishes with name resolved\n\n@s = ();\n\npush @s, http_host('cn09.example.net', '/long', start => 1);\npush @s, http_host('cn09.example.net', '/long', start => 1);\n\nselect undef, undef, undef, 0.4;\t# let resolver hang on CNAME\n\nclose(shift @s);\n\nlike(http_end(pop @s), qr/200 OK/, 'abort on CNAME');\n\nlike(http_host('cn001.example.net', '/short'), qr/502 Bad/, 'recurse uncached');\n\n###############################################################################\n\nsub http_host {\n\tmy ($host, $uri, %extra) = @_;\n\treturn http(<<EOF, %extra);\nGET $uri HTTP/1.0\nHost: $host\n\nEOF\n}\n\n###############################################################################\n\nsub reply_handler {\n\tmy ($recv_data) = @_;\n\n\tmy (@name, @rdata);\n\n\tuse constant NOERROR\t=> 0;\n\n\tuse constant A\t\t=> 1;\n\tuse constant CNAME\t=> 5;\n\n\tuse constant IN\t\t=> 1;\n\n\t# default values\n\n\tmy ($hdr, $rcode, $ttl) = (0x8180, NOERROR, 3600);\n\n\t# decode name\n\n\tmy ($len, $offset) = (undef, 12);\n\twhile (1) {\n\t\t$len = unpack(\"\\@$offset C\", $recv_data);\n\t\tlast if $len == 0;\n\t\t$offset++;\n\t\tpush @name, unpack(\"\\@$offset A$len\", $recv_data);\n\t\t$offset += $len;\n\t}\n\n\t$offset -= 1;\n\tmy ($id, $type, $class) = unpack(\"n x$offset n2\", $recv_data);\n\n\tmy $name = join('.', @name);\n\tif ($name eq 'a.example.net' && $type == A) {\n\t\tpush @rdata, rd_addr($ttl, '127.0.0.1');\n\n\t} elsif ($name eq 'b.example.net' && $type == A) {\n\t\tsleep 2;\n\t\tpush @rdata, rd_addr($ttl, '127.0.0.1');\n\n\t} elsif ($name eq 'cn01.example.net') {\n\t\t$ttl = 1;\n\t\tpush @rdata, pack(\"n3N nCa4n\", 0xc00c, CNAME, IN, $ttl,\n\t\t\t7, 4, \"cn02\", 0xc011);\n\n\t} elsif ($name =~ /cn0[268].example.net/) {\n\t\t# resolver timeout\n\n\t\treturn;\n\n\t} elsif ($name eq 'cn03.example.net') {\n\t\tselect undef, undef, undef, 1.1;\n\t\tpush @rdata, pack(\"n3N nC\", 0xc00c, CNAME, IN, $ttl, 0);\n\n\t} elsif ($name eq 'cn04.example.net') {\n\t\tselect undef, undef, undef, 1.1;\n\t\tpush @rdata, pack(\"n3N nCa1n\", 0xc00c, CNAME, IN, $ttl,\n\t\t\t4, 1, \"a\", 0xc011);\n\n\t} elsif ($name eq 'cn05.example.net') {\n\t\tselect undef, undef, undef, 1.1;\n\t\tpush @rdata, pack(\"n3N nCa4n\", 0xc00c, CNAME, IN, $ttl,\n\t\t\t7, 4, \"cn06\", 0xc011);\n\n\t} elsif ($name eq 'cn07.example.net') {\n\t\t$ttl = 1;\n\t\tpush @rdata, pack(\"n3N nCa4n\", 0xc00c, CNAME, IN, $ttl,\n\t\t\t7, 4, \"cn08\", 0xc011);\n\n\t} elsif ($name eq 'cn09.example.net') {\n\t\tif ($type == A) {\n\t\t\t# await both HTTP requests\n\t\t\tselect undef, undef, undef, 0.2;\n\t\t}\n\t\tpush @rdata, pack(\"n3N nCa1n\", 0xc00c, CNAME, IN, $ttl,\n\t\t\t4, 1, \"b\", 0xc011);\n\n\t} elsif ($name eq 'cn052.example.net') {\n\t\tif ($type == A) {\n\t\t\tpush @rdata, rd_addr($ttl, '127.0.0.1');\n\t\t}\n\n\t} elsif ($name =~ /cn0\\d+.example.net/) {\n\t\tmy ($id) = $name =~ /cn(\\d+)/;\n\t\t$id++;\n\t\tpush @rdata, pack(\"n3N nCa5n\", 0xc00c, CNAME, IN, $ttl,\n\t\t\t8, 5, \"cn$id\", 0xc012);\n\t}\n\n\t$len = @name;\n\tpack(\"n6 (C/a*)$len x n2\", $id, $hdr | $rcode, 1, scalar @rdata,\n\t\t0, 0, @name, $type, $class) . join('', @rdata);\n}\n\nsub rd_addr {\n\tmy ($ttl, $addr) = @_;\n\n\tmy $code = 'split(/\\./, $addr)';\n\n\treturn pack 'n3N', 0xc00c, A, IN, $ttl if $addr eq '';\n\n\tpack 'n3N nC4', 0xc00c, A, IN, $ttl, eval \"scalar $code\", eval($code);\n}\n\nsub dns_daemon {\n\tmy ($port, $t) = @_;\n\n\tmy ($data, $recv_data);\n\tmy $socket = IO::Socket::INET->new(\n\t\tLocalAddr => '127.0.0.1',\n\t\tLocalPort => $port,\n\t\tProto => 'udp',\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\t# signal we are ready\n\n\topen my $fh, '>', $t->testdir() . '/' . $port;\n\tclose $fh;\n\n\twhile (1) {\n\t\t$socket->recv($recv_data, 65536);\n\t\t$data = reply_handler($recv_data);\n\t\t$socket->send($data);\n\t}\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/http_resolver_ipv4.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Eugene Grebenschikov\n# (C) Nginx, Inc.\n\n# Tests for http resolver with ipv4/ipv6 parameters.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy rewrite/);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            proxy_pass  http://$arg_h:%%PORT_8081%%/;\n            resolver  127.0.0.1:%%PORT_8980_UDP%% ipv4=on ipv6=on;\n        }\n\n        location /ipv4 {\n            proxy_pass  http://$arg_h:%%PORT_8081%%/;\n            resolver  127.0.0.1:%%PORT_8980_UDP%% ipv4=on ipv6=off;\n        }\n\n        location /ipv6 {\n            proxy_pass  http://$arg_h:%%PORT_8081%%/;\n            resolver  127.0.0.1:%%PORT_8980_UDP%% ipv4=off ipv6=on;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        location / {\n            return  200 \"ipv4\";\n        }\n    }\n\n    server {\n        listen       [::1]:%%PORT_8081%%;\n        server_name  localhost;\n\n        location / {\n            return  200 \"ipv6\";\n        }\n    }\n}\n\nEOF\n\n$t->try_run('no resolver ipv4')->plan(3);\n\n$t->run_daemon(\\&dns_daemon, port(8980), $t);\n$t->waitforfile($t->testdir . '/' . port(8980));\n\n###############################################################################\n\nlike(many('/', 10), qr/ipv4: \\d+, ipv6: \\d+/, 'ipv4 ipv6');\nis(many('/ipv4', 10), 'ipv4: 10', 'ipv4 only');\nis(many('/ipv6', 10), 'ipv6: 10', 'ipv6 only');\n\n###############################################################################\n\nsub many {\n\tmy ($uri, $count) = @_;\n\tmy %hits;\n\n\tfor (1 .. $count) {\n\t\tif (http_get(\"$uri?h=example.com\") =~ /(ipv(4|6))/) {;\n\t\t\t$hits{$1} = 0 unless defined $hits{$1};\n\t\t\t$hits{$1}++;\n\t\t}\n\t}\n\n\treturn join ', ', map { $_ . \": \" . $hits{$_} } sort keys %hits;\n}\n\n###############################################################################\n\nsub reply_handler {\n\tmy ($recv_data, $port) = @_;\n\n\tmy (@name, @rdata);\n\n\tuse constant NOERROR\t=> 0;\n\n\tuse constant A\t\t=> 1;\n\tuse constant AAAA\t=> 28;\n\n\tuse constant IN\t\t=> 1;\n\n\t# default values\n\n\tmy ($hdr, $rcode, $ttl) = (0x8180, NOERROR, 3600);\n\n\t# decode name\n\n\tmy ($len, $offset) = (undef, 12);\n\twhile (1) {\n\t\t$len = unpack(\"\\@$offset C\", $recv_data);\n\t\tlast if $len == 0;\n\t\t$offset++;\n\t\tpush @name, unpack(\"\\@$offset A$len\", $recv_data);\n\t\t$offset += $len;\n\t}\n\n\t$offset -= 1;\n\tmy ($id, $type, $class) = unpack(\"n x$offset n2\", $recv_data);\n\n\tmy $name = join('.', @name);\n\tif ($name eq 'example.com') {\n\t\tif ($type == A) {\n\t\t\tpush @rdata, rd_addr($ttl, '127.0.0.1');\n\t\t}\n\t\tif ($type == AAAA) {\n\t\t\tpush @rdata, rd_addr6($ttl, \"::1\");\n\t\t}\n\t}\n\n\t$len = @name;\n\tpack(\"n6 (C/a*)$len x n2\", $id, $hdr | $rcode, 1, scalar @rdata,\n\t\t0, 0, @name, $type, $class) . join('', @rdata);\n}\n\nsub rd_addr {\n\tmy ($ttl, $addr) = @_;\n\n\tmy $code = 'split(/\\./, $addr)';\n\n\tpack 'n3N nC4', 0xc00c, A, IN, $ttl, eval \"scalar $code\", eval($code);\n}\n\nsub expand_ip6 {\n\tmy ($addr) = @_;\n\n\tsubstr ($addr, index($addr, \"::\"), 2) =\n\t\tjoin \"0\", map { \":\" } (0 .. 8 - (split /:/, $addr) + 1);\n\tmap { hex \"0\" x (4 - length $_) . \"$_\" } split /:/, $addr;\n}\n\nsub rd_addr6 {\n\tmy ($ttl, $addr) = @_;\n\n\tpack 'n3N nn8', 0xc00c, AAAA, IN, $ttl, 16, expand_ip6($addr);\n}\n\nsub dns_daemon {\n\tmy ($port, $t) = @_;\n\n\tmy ($data, $recv_data);\n\tmy $socket = IO::Socket::INET->new(\n\t\tLocalAddr => '127.0.0.1',\n\t\tLocalPort => $port,\n\t\tProto => 'udp',\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\t# signal we are ready\n\n\topen my $fh, '>', $t->testdir() . '/' . $port;\n\tclose $fh;\n\n\twhile (1) {\n\t\t$socket->recv($recv_data, 65536);\n\t\t$data = reply_handler($recv_data, $port);\n\t\t$socket->send($data);\n\t}\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/http_server_name.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n# (C) Andrey Zelenkov\n# (C) Nginx, Inc.\n\n# Tests for server_name selection.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse Socket qw/ CRLF /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nplan(skip_all => 'win32') if $^O eq 'MSWin32';\n\nmy $t = Test::Nginx->new()->has(qw/http rewrite/)->plan(20)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server_names_hash_bucket_size 64;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            add_header X-Server $server_name;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  \"\";\n\n        location / {\n            add_header X-Server $server_name;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  www.example.com;\n\n        location / {\n            add_header X-Server $server_name;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  ~^EXAMPLE\\.COM$;\n\n        location / {\n            add_header X-Server $server_name;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  ~^(?P<name>.+)\\Q.example.com\\E$;\n\n        location / {\n            add_header X-Server $server_name;\n            add_header X-Match  $name;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  \"~^(?P<name>www\\p{N}+)\\.example\\.com$\";\n\n        location / {\n            add_header X-Server $server_name;\n            add_header X-Match  $name;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  many.example.com many2.example.com;\n\n        location / {\n            add_header X-Server $server_name;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  many3.example.com;\n        server_name  many4.example.com;\n\n        location / {\n            add_header X-Server $server_name;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  *.wc.example.com;\n\n        location / {\n            add_header X-Server $server_name;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  *.pref.wc.example.com;\n\n        location / {\n            add_header X-Server $server_name;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  wc2.example.*;\n\n        location / {\n            add_header X-Server $server_name;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  wc2.example.com.*;\n\n        location / {\n            add_header X-Server $server_name;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  .dot.example.com;\n\n        location / {\n            add_header X-Server $server_name;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('index.html', '');\n$t->run();\n\n###############################################################################\n\nlike(http_server('xxx'), qr/X-Server: localhost/, 'default');\nunlike(http_server(), qr/X-Server/, 'empty');\n\nlike(http_server('www.example.com'), qr/\\QX-Server: www.example.com/,\n\t'www.example.com');\nlike(http_server('WWW.EXAMPLE.COM'), qr/\\QX-Server: www.example.com/,\n\t'www.example.com uppercase');\n\nlike(http_server('example.com'), qr/\\QX-Server: ~^EXAMPLE\\.COM$/,\n\t'example.com regex');\nlike(http_server('EXAMPLE.COM'), qr/\\QX-Server: ~^EXAMPLE\\.COM$/,\n\t'example.com regex uppercase');\n\nlike(http_server('blah.example.com'), qr/X-Match: blah/,\n\t'(P<name>.*).example.com named capture');\nlike(http_server('BLAH.EXAMPLE.COM'), qr/X-Match: blah/,\n\t'(P<name>.*).example.com named capture uppercase');\n\nlike(http_server('www01.example.com'), qr/X-Match: www01/,\n\t'\\p{N} in named capture');\nlike(http_server('WWW01.EXAMPLE.COM'), qr/X-Match: www01/,\n\t'\\p{N} in named capture uppercase');\n\nlike(http_server('many.example.com'), qr/\\QX-Server: many.example.com/,\n\t'name row - first');\nlike(http_server('many2.example.com'), qr/\\QX-Server: many.example.com/,\n\t'name row - second');\n\nlike(http_server('many3.example.com'), qr/\\QX-Server: many3.example.com/,\n\t'name list - first');\nlike(http_server('many4.example.com'), qr/\\QX-Server: many3.example.com/,\n\t'name list - second');\n\nlike(http_server('www.wc.example.com'),\n\tqr/\\QX-Server: *.wc.example.com/, 'wildcard first');\nlike(http_server('www.pref.wc.example.com'),\n\tqr/\\QX-Server: *.pref.wc.example.com/, 'wildcard first most specific');\nlike(http_server('wc2.example.net'),\n\tqr/\\QX-Server: wc2.example.*/, 'wildcard last');\nlike(http_server('wc2.example.com.pref'),\n\tqr/\\QX-Server: wc2.example.com.*/, 'wildcard last most specific');\n\nlike(http_server('www.dot.example.com'), qr/\\QX-Server: dot.example.com/,\n\t'wildcard dot');\nlike(http_server('dot.example.com'), qr/\\QX-Server: dot.example.com/,\n\t'wildcard dot empty');\n\n###############################################################################\n\nsub http_server {\n\tmy ($host) = @_;\n\n\tmy $str = 'GET / HTTP/1.0' . CRLF .\n\t\t(defined $host ? \"Host: $host\" . CRLF : '') .\n\t\tCRLF;\n\n\treturn http($str);\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/http_time_http_variable.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n# (C) flygoast\n\n# Tests for 'time_http' built-in variable.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http rewrite/)->plan(1)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon         off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            add_header X-TH \"$time_http\";\n            return 200;\n        }\n    }\n}\n\nEOF\n\n$t->run();\n\n###############################################################################\n\nlike(http_get('/'), \n    qr/X-TH: [A-Z][a-z]{2}, \\d{2} [A-Z][a-z]{2} \\d{4} \\d{2}:\\d{2}:\\d{2} GMT/,\n    '$time_http test');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/http_try_files.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for try_files directive.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy rewrite/)->plan(10)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            try_files $uri /fallback;\n        }\n\n        location /nouri/ {\n            try_files $uri /fallback-nouri;\n        }\n\n        location /short/ {\n            try_files /short $uri =404;\n        }\n\n        location /file-file/ {\n            try_files /found.html =404;\n        }\n\n        location /file-dir/ {\n            try_files /found.html/ =404;\n        }\n\n        location /dir-dir/ {\n            try_files /directory/ =404;\n        }\n\n        location /dir-file/ {\n            try_files /directory =404;\n        }\n\n        location ~ /alias-re.html {\n            alias %%TESTDIR%%/directory;\n            try_files $uri =404;\n        }\n\n        location /alias-nested/ {\n            alias %%TESTDIR%%/;\n            location ~ html {\n                try_files $uri =404;\n            }\n        }\n\n        location /fallback {\n            proxy_pass http://127.0.0.1:8081/fallback;\n        }\n        location /fallback-nouri {\n            proxy_pass http://127.0.0.1:8081;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        location / {\n            add_header X-URI $request_uri;\n            return 204;\n        }\n    }\n}\n\nEOF\n\nmkdir($t->testdir() . '/directory');\n$t->write_file('directory/alias-re.html', 'SEE THIS');\n$t->write_file('found.html', 'SEE THIS');\n$t->run();\n\n###############################################################################\n\nlike(http_get('/found.html'), qr!SEE THIS!, 'found');\nlike(http_get('/uri/notfound'), qr!X-URI: /fallback!, 'not found uri');\nlike(http_get('/nouri/notfound'), qr!X-URI: /fallback!, 'not found nouri');\nlike(http_get('/short/long'), qr!404 Not!, 'short uri in try_files');\n\nlike(http_get('/file-file/'), qr!SEE THIS!, 'file matches file');\nlike(http_get('/file-dir/'), qr!404 Not!, 'file does not match dir');\nlike(http_get('/dir-dir/'), qr!301 Moved Permanently!, 'dir matches dir');\nlike(http_get('/dir-file/'), qr!404 Not!, 'dir does not match file');\n\nlike(http_get('/alias-re.html'), qr!SEE THIS!, 'alias in regex location');\nlike(http_get('/alias-nested/found.html'), qr!SEE THIS!,\n\t'alias with nested location');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/http_uri.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for URI normalization.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http rewrite/)->plan(19)\n\t->write_file_expand('nginx.conf', <<'EOF')->run();\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            add_header  X-URI          \"x $uri x\";\n            add_header  X-Args         \"y $args y\";\n            add_header  X-Request-URI  \"z $request_uri z\";\n            return      204;\n        }\n    }\n}\n\nEOF\n\n###############################################################################\n\nlike(http_get('/foo/bar%'), qr/400 Bad/, 'percent');\nlike(http_get('/foo/bar%1'), qr/400 Bad/, 'percent digit');\n\nlike(http_get('/foo/bar/.?args'), qr!x /foo/bar/ x!, 'dot args');\nlike(http_get('/foo/bar/.#frag'), qr!x /foo/bar/ x!, 'dot frag');\nlike(http_get('/foo/bar/..?args'), qr!x /foo/ x!, 'dot dot args');\nlike(http_get('/foo/bar/..#frag'), qr!x /foo/ x!, 'dot dot frag');\nlike(http_get('/foo/bar/.'), qr!x /foo/bar/ x!, 'trailing dot');\nlike(http_get('/foo/bar/..'), qr!x /foo/ x!, 'trailing dot dot');\n\nlike(http_get('http://localhost'), qr!x / x!, 'absolute');\nlike(http_get('http://localhost/'), qr!x / x!, 'absolute slash');\nlike(http_get('http://localhost?args'), qr!x / x.*y args y!ms,\n\t'absolute args');\nlike(http_get('http://localhost?args#frag'), qr!x / x.*y args y!ms,\n\t'absolute args and frag');\n\nlike(http_get('http://localhost:8080'), qr!x / x!, 'port');\nlike(http_get('http://localhost:8080/'), qr!x / x!, 'port slash');\nlike(http_get('http://localhost:8080?args'), qr!x / x.*y args y!ms,\n\t'port args');\nlike(http_get('http://localhost:8080?args#frag'), qr!x / x.*y args y!ms,\n\t'port args and frag');\n\nTODO: {\nlocal $TODO = 'not yet' unless $t->has_version('1.21.1');\n\nlike(http_get('/ /'), qr/400 Bad/, 'space');\n\n}\n\nTODO: {\nlocal $TODO = 'not yet' unless $t->has_version('1.21.1');\n\nlike(http_get(\"/\\x02\"), qr/400 Bad/, 'control');\n\n}\n\nlike(http_get('/%02'), qr!x /\\x02 x!, 'control escaped');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/http_variables.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n# (C) Valentin Bartenev\n\n# Tests for http variables.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http rewrite proxy/)->plan(7);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    log_format cc \"$uri: $sent_http_cache_control\";\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        access_log %%TESTDIR%%/cc.log cc;\n\n        location / {\n            return 200 OK;\n        }\n\n        location /arg {\n            return 200 $arg_l:$arg_;\n        }\n\n        location /set {\n            add_header Cache-Control max-age=3600;\n            add_header Cache-Control private;\n            add_header Cache-Control must-revalidate;\n            return 200 OK;\n        }\n\n        location /redefine {\n            expires epoch;\n            proxy_pass http://127.0.0.1:8080/set;\n        }\n\n        location /limit_rate {\n            set $limit_rate $arg_l;\n            add_header X-Rate $limit_rate;\n            return 200 OK;\n        }\n    }\n}\n\nEOF\n\n$t->run();\n\n###############################################################################\n\nhttp_get('/');\nhttp_get('/../bad_uri');\nhttp_get('/redefine');\n\nlike(http_get('/arg?l=42'), qr/42:$/, 'arg');\n\n# $limit_rate is a special variable that has its own set_handler / get_handler\n\nlike(http_get('/limit_rate?l=40k'), qr/X-Rate: 40960/, 'limit_rate handlers');\nlike(http_get('/limit_rate'), qr/X-Rate: 0/, 'limit_rate invalid');\n\n$t->stop();\n\nmy $log = $t->read_file('cc.log');\nlike($log, qr!^: -$!m, 'no uri');\nlike($log, qr!^/: -$!m, 'no header');\nlike($log, qr!^/set: max-age=3600, private, must-revalidate$!m,\n\t'multi headers');\n\nlike($log, qr!^/redefine: no-cache$!m, 'ignoring headers with (hash == 0)');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/ignore_invalid_headers.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for ignore_invalid_headers, underscores_in_headers directives.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse Socket qw/ CRLF /;\nuse MIME::Base64 qw/ encode_base64 decode_base64 /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy/)->plan(12)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        ignore_invalid_headers off;\n\n        location / {\n            proxy_pass http://127.0.0.1:8085;\n        }\n\n        location /v {\n            add_header X-Cookie $http_cookie;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        location / {\n            proxy_pass http://127.0.0.1:8085;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8082;\n        server_name  localhost;\n\n        underscores_in_headers on;\n\n        location / {\n            proxy_pass http://127.0.0.1:8085;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('index.html', '');\n$t->write_file('v', '');\n$t->run_daemon(\\&http_daemon);\n$t->run()->waitforsocket('127.0.0.1:' . port(8085));\n\n###############################################################################\n\nmy $us = 'GET / HTTP/1.0' . CRLF\n\t. 'x_foo: x-bar' . CRLF . CRLF;\nmy $us2 = 'GET / HTTP/1.0' . CRLF\n\t. '_foo: x-bar' . CRLF . CRLF;\nmy $bad = 'GET / HTTP/1.0' . CRLF\n\t. 'x.foo: x-bar' . CRLF . CRLF;\nmy $bad2 = 'GET / HTTP/1.0' . CRLF\n\t. '.foo: x-bar' . CRLF . CRLF;\n\n# ignore_invalid_headers off;\n\nlike(get($us, 8080), qr/x-bar/, 'off - underscore');\nlike(get($us2, 8080), qr/x-bar/, 'off - underscore first');\nlike(get($bad, 8080), qr/x-bar/, 'off - bad');\nlike(get($bad2, 8080), qr/x-bar/, 'off - bad first');\n\n# ignore_invalid_headers off; headers parsing post 8f55cb5c7e79\n\nunlike(http('GET /v HTTP/1.0' . CRLF\n\t. 'Host: localhost' . CRLF\n\t. 'coo: foo' . CRLF\n\t. '</kie>: x-bar' . CRLF . CRLF), qr/x-bar/, 'off - several');\n\n# ignore_invalid_headers on;\n\nunlike(get($us, 8081), qr/x-bar/, 'on - underscore');\nunlike(get($us2, 8081), qr/x-bar/, 'on - underscore first');\n\n# ignore_invalid_headers on; underscores_in_headers on;\n\nlike(get($us, 8082), qr/x-bar/, 'underscores_in_headers');\nlike(get($us2, 8082), qr/x-bar/, 'underscores_in_headers - first');\n\n# always invalid header characters\n\nmy $bad3 = 'GET / HTTP/1.0' . CRLF\n\t. ':foo: x-bar' . CRLF . CRLF;\nmy $bad4 = 'GET / HTTP/1.0' . CRLF\n\t. ' foo: x-bar' . CRLF . CRLF;\nmy $bad5 = 'GET / HTTP/1.0' . CRLF\n\t. \"foo\\x02: x-bar\" . CRLF . CRLF;\n\nTODO: {\nlocal $TODO = 'not yet' unless $t->has_version('1.21.1');\n\nlike(http($bad3), qr/400 Bad/, 'colon first');\nlike(http($bad4), qr/400 Bad/, 'space');\nlike(http($bad5), qr/400 Bad/, 'control');\n\n}\n\n###############################################################################\n\nsub get {\n\tmy ($msg, $port) = @_;\n\n\tmy $s = IO::Socket::INET->new('127.0.0.1:' . port($port)) or die;\n\tmy ($headers) = http($msg, socket => $s) =~ /X-Headers: (\\w+)/;\n\tdecode_base64($headers);\n}\n\n###############################################################################\n\nsub http_daemon {\n\tmy $once = 1;\n\tmy $server = IO::Socket::INET->new(\n\t\tProto => 'tcp',\n\t\tLocalHost => '127.0.0.1:' . port(8085),\n\t\tListen => 5,\n\t\tReuse => 1\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\tlocal $SIG{PIPE} = 'IGNORE';\n\n\twhile (my $client = $server->accept()) {\n\t\t$client->autoflush(1);\n\n\t\tmy $headers = '';\n\t\tmy $uri = '';\n\n\t\twhile (<$client>) {\n\t\t\t$headers .= $_;\n\t\t\tlast if (/^\\x0d?\\x0a?$/);\n\t\t}\n\n\t\t$headers = encode_base64($headers, \"\");\n\n\t\tprint $client <<EOF;\nHTTP/1.1 200 OK\nConnection: close\nX-Headers: $headers\n\nEOF\n\n\t}\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/image_filter.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for image filter module.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse Socket qw/CRLF/;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\neval { require GD; };\nplan(skip_all => 'GD not installed') if $@;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy map image_filter/)->plan(39)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    map $arg_w $w {\n        \"\" '-';\n        default $arg_w;\n    }\n    map $arg_h $h {\n        \"\" '-';\n        default $arg_h;\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location /size {\n            image_filter size;\n            alias %%TESTDIR%%/;\n        }\n\n        location /test {\n            image_filter test;\n            alias %%TESTDIR%%/;\n\n            location /test/off {\n                image_filter off;\n                alias %%TESTDIR%%/;\n            }\n        }\n\n        location /resize {\n            image_filter resize 10 12;\n            alias %%TESTDIR%%/;\n        }\n        location /resize1 {\n            image_filter resize 10 -;\n            alias %%TESTDIR%%/;\n        }\n        location /resize2 {\n            image_filter resize - 12;\n            alias %%TESTDIR%%/;\n        }\n        location /resize_var {\n            image_filter resize $w $h;\n            alias %%TESTDIR%%/;\n        }\n\n        location /rotate {\n            image_filter rotate 90;\n            alias %%TESTDIR%%/;\n        }\n        location /rotate_var {\n            image_filter rotate $arg_r;\n            alias %%TESTDIR%%/;\n        }\n\n        location /crop {\n            image_filter crop 60 80;\n            alias %%TESTDIR%%/;\n        }\n        location /crop_var {\n            image_filter crop $arg_w $arg_h;\n            alias %%TESTDIR%%/;\n        }\n        location /crop_rotate {\n            image_filter crop $arg_w $arg_h;\n            image_filter rotate $arg_r;\n            alias %%TESTDIR%%/;\n        }\n        location /resize_rotate {\n            image_filter resize $w $h;\n            image_filter rotate $arg_r;\n            alias %%TESTDIR%%/;\n\n            location /resize_rotate/resize {\n                image_filter resize 10 12;\n                alias %%TESTDIR%%/;\n            }\n        }\n\n        location /interlaced {\n            image_filter resize 10 12;\n            image_filter_interlace on;\n            alias %%TESTDIR%%/;\n        }\n\n        location /nontransparent {\n            image_filter resize 10 12;\n            image_filter_transparency off;\n            alias %%TESTDIR%%/;\n        }\n\n        location /quality {\n            image_filter resize 10 12;\n            image_filter_jpeg_quality 50;\n            alias %%TESTDIR%%/;\n        }\n        location /quality_var {\n            image_filter resize 10 12;\n            image_filter_jpeg_quality $arg_q;\n            alias %%TESTDIR%%/;\n\n            location /quality_var/quality {\n                image_filter_jpeg_quality 60;\n                alias %%TESTDIR%%/;\n            }\n        }\n\n        location /buffer {\n            image_filter test;\n            image_filter_buffer 1k;\n            alias %%TESTDIR%%/;\n        }\n        location /proxy_buffer {\n            image_filter rotate 90;\n            image_filter_buffer 20;\n            proxy_pass http://127.0.0.1:8081/;\n            proxy_buffering off;\n            proxy_buffer_size 512;\n        }\n    }\n}\n\nEOF\n\n\nmy $im = new GD::Image(100, 120);\nmy $white = $im->colorAllocate(255, 255, 255);\nmy $black = $im->colorAllocate(0, 0, 0);\n\n$im->transparent($white);\n$im->rectangle(0, 0, 99, 99, $black);\n\n$t->write_file('jpeg', $im->jpeg);\n$t->write_file('gif', $im->gif);\n$t->write_file('png', $im->png);\n$t->write_file('txt', 'SEE-THIS');\n\n$t->run_daemon(\\&http_daemon, $t);\n$t->run()->waitforsocket('127.0.0.1:' . port(8081));\n\n###############################################################################\n\nlike(http_head('/test/gif'), qr/200 OK/, 'test');\nlike(http_head('/test/gif'), qr!Content-Type: image/gif!, 'test content-type');\nlike(http_get('/test/txt'), qr/415 Unsupported/, 'test fail');\nlike(http_get('/test/off/txt'), qr/SEE-THIS/, 'off');\n\nis(http_get_body('/size/txt'), '{}' . CRLF, 'size wrong type');\nlike(http_head('/size/txt'), qr!Content-Type: application/json!,\n\t'size content-type');\nlike(http_get('/size/jpeg'), qr/\"width\": 100/, 'size width');\nlike(http_get('/size/jpeg'), qr/\"height\": 120/, 'size height');\nlike(http_get('/size/jpeg'), qr/\"type\": \"jpeg\"/, 'size jpeg');\nlike(http_get('/size/gif'), qr/\"type\": \"gif\"/, 'size gif');\nlike(http_get('/size/png'), qr/\"type\": \"png\"/, 'size png');\n\nis(gif_size('/resize/gif'), '10 12', 'resize');\nis(gif_size('/resize1/gif'), '10 12', 'resize 1');\nis(gif_size('/resize2/gif'), '10 12', 'resize 2');\n\nis(gif_size('/resize_var/gif?w=10&h=12'), '10 12', 'resize var');\nis(gif_size('/resize_var/gif?w=10'), '10 12', 'resize var 1');\nis(gif_size('/resize_var/gif?h=12'), '10 12', 'resize var 2');\n\nis(gif_size('/rotate/gif?r=90'), '120 100', 'rotate');\nis(gif_size('/rotate_var/gif?r=180'), '100 120', 'rotate var 1');\nis(gif_size('/rotate_var/gif?r=270'), '120 100', 'rotate var 2');\n\n$im = GD::Image->newFromGifData(http_get_body('/gif'));\nis($im->interlaced, 0, 'gif interlaced off');\nis($im->transparent, 0, 'gif transparent white');\n\nSKIP: {\nskip 'broken/unknown libgd', 1\n\tunless has_gdversion('2.1.0') or $ENV{TEST_NGINX_UNSAFE};\n\n$im = GD::Image->newFromGifData(http_get_body('/interlaced/gif'));\nis($im->interlaced, 1, 'gif interlaced on');\n\n}\n\n$im = GD::Image->newFromGifData(http_get_body('/nontransparent/gif'));\nis($im->transparent, -1, 'gif transparent loss');\n\n$im = GD::Image->newFromPngData(http_get_body('/png'));\nis($im->interlaced, 0, 'png interlaced off');\nis($im->transparent, 0, 'png transparent white');\n\n# this test produces libpng warning on STDERR:\n# \"Interlace handling should be turned on when using png_read_image\"\n\nSKIP: {\nskip 'can wedge nginx with SIGPIPE', 1 unless $ENV{TEST_NGINX_UNSAFE};\n\n$im = GD::Image->newFromPngData(http_get_body('/interlaced/png'));\nis($im->interlaced, 1, 'png interlaced on');\n\n}\n\n$im = GD::Image->newFromPngData(http_get_body('/nontransparent/png'));\nis($im->transparent, -1, 'png transparent loss');\n\nlike(http_get('/resize/jpeg'), qr/quality = 75/, 'quality default');\nlike(http_get('/quality/jpeg'), qr/quality = 50/, 'quality');\nlike(http_get('/quality_var/jpeg?q=40'), qr/quality = 40/, 'quality var');\nlike(http_get('/quality_var/quality/jpeg?q=40'), qr/quality = 60/,\n\t'quality nested');\n\nis(gif_size('/crop/gif'), '60 80', 'crop');\nis(gif_size('/crop_var/gif?w=10&h=20'), '10 20', 'crop var');\nis(gif_size('/crop_rotate/gif?w=5&h=6&r=90'), '5 5', 'rotate before crop');\nis(gif_size('/resize_rotate/gif?w=5&h=6&r=90'), '6 5', 'rotate after resize');\nis(gif_size('/resize_rotate/resize/gif??w=5&h=6&r=90'), '10 12',\n\t'resize rotate nested');\n\nlike(http_get('/buffer/jpeg'), qr/415 Unsupported/, 'small buffer');\nisnt(http_get('/proxy_buffer/jpeg'), undef, 'small buffer proxy');\n\n###############################################################################\n\nsub gif_size {\n\tjoin ' ', unpack(\"x6v2\", http_get_body(@_));\n}\n\nsub http_get_body {\n\tmy ($uri) = @_;\n\n\treturn undef if !defined $uri;\n\n\tmy $text = http_get($uri);\n\n\tif ($text !~ /(.*?)\\x0d\\x0a?\\x0d\\x0a?(.*)/ms) {\n\t\treturn undef;\n\t}\n\n\treturn $2;\n}\n\nsub has_gdversion {\n\tmy ($need) = @_;\n\n\tmy $v_str = `gdlib-config --version 2>&1`\n\t\t|| eval { GD::VERSION_STRING() } or return 0;\n\t($v_str) = $v_str =~ m!^([0-9.]+)!m or return 0;\n\tmy @v = split(/\\./, $v_str);\n\tmy ($n, $v);\n\n\tfor $n (split(/\\./, $need)) {\n\t\t$v = shift @v || 0;\n\t\treturn 0 if $n > $v;\n\t\treturn 1 if $v > $n;\n\t}\n\n\treturn 1;\n}\n\n###############################################################################\n\n# serve static files without Content-Length\n\nsub http_daemon {\n\tmy ($t) = @_;\n\n\tmy $server = IO::Socket::INET->new(\n\t\tProto => 'tcp',\n\t\tLocalHost => '127.0.0.1',\n\t\tLocalPort => port(8081),\n\t\tListen => 5,\n\t\tReuse => 1\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\tlocal $SIG{PIPE} = 'IGNORE';\n\n\twhile (my $client = $server->accept()) {\n\t\t$client->autoflush(1);\n\n\t\tmy $headers = '';\n\t\tmy $uri = '';\n\n\t\twhile (<$client>) {\n\t\t\t$headers .= $_;\n\t\t\tlast if (/^\\x0d?\\x0a?$/);\n\t\t}\n\n\t\tnext if $headers eq '';\n\t\t$uri = $1 if $headers =~ /^\\S+\\s+([^ ]+)\\s+HTTP/i;\n\t\tmy $data = $t->read_file($uri);\n\n\t\tprint $client <<EOF;\nHTTP/1.1 200 OK\nConnection: close\n\n$data\nEOF\n\n\t} continue {\n\t\tclose $client;\n\t}\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/image_filter_finalize.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for http filter finalize code.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy cache image_filter limit_req/)\n\t->has(qw/rewrite/)->plan(3)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    proxy_cache_path %%TESTDIR%%/cache keys_zone=cache:1m;\n\n    limit_req_zone $binary_remote_addr zone=limit:1m rate=25r/m;\n\n    log_format time \"$request_uri:$status:$upstream_response_time\";\n    access_log time.log time;\n\n    upstream u {\n        server 127.0.0.1:8081;\n        server 127.0.0.1:8081;\n        server 127.0.0.1:8081;\n        server 127.0.0.1:8081;\n        server 127.0.0.1:8080;\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        # this used to cause a segmentation fault before 07f028df3879 (1.3.1)\n        # http://nginx.org/pipermail/nginx/2011-January/024703.html\n\n        location /t1 {\n            proxy_pass http://127.0.0.1:8080/bad;\n            proxy_cache cache;\n            proxy_cache_valid any 1h;\n\n            image_filter   resize  150 100;\n            error_page     415   = /empty;\n        }\n\n        location /empty {\n            return 204;\n        }\n\n        location /bad {\n            return 404;\n        }\n\n        # another segfault, introduced in 204b780a89de (1.3.0),\n        # fixed in 07f028df3879 (1.3.1)\n\n        location /t2 {\n            proxy_pass http://127.0.0.1:8080/big;\n            proxy_store on;\n\n            image_filter_buffer 10m;\n            image_filter   resize  150 100;\n            error_page     415   = /empty;\n        }\n\n        location /big {\n            # big enough static file\n        }\n\n        # filter finalization may cause duplicate upstream finalization,\n        # resulting in wrong $upstream_response_time,\n        # http://nginx.org/pipermail/nginx-devel/2015-February/006539.html\n\n        # note that we'll need upstream response time to be at least 1 second,\n        # and at least 4 failed requests to make sure r->upstream_states will\n        # not be reallocated\n\n        location /t3 {\n            proxy_pass http://u/slow;\n            proxy_buffering off;\n\n            image_filter   resize  150 100;\n            error_page     415   = /upstream;\n        }\n\n        location /slow {\n            limit_req zone=limit burst=5;\n        }\n\n        location /upstream {\n            proxy_pass http://127.0.0.1:8080/empty;\n        }\n\n        location /time.log {\n            # access to log\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n        return 444;\n    }\n}\n\nEOF\n\n$t->write_file('big', \"x\" x 10240000);\n$t->write_file('slow', \"x\");\n\n$t->run();\n\n###############################################################################\n\nlike(http_get('/t1'), qr/HTTP/, 'image filter and cache');\nlike(http_get('/t2'), qr/HTTP/, 'image filter and store');\n\nhttp_get('/slow');\nhttp_get('/t3');\nlike(http_get('/time.log'), qr!/t3:.*, [1-9]\\.!, 'upstream response time');\n\n# \"aio_write\" is used to produce the following alert on some platforms:\n# \"readv() failed (9: Bad file descriptor) while reading upstream\"\n\n$t->todo_alerts() if $t->read_file('nginx.conf') =~ /aio_write on/\n\tand $t->read_file('nginx.conf') =~ /aio threads/;\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/image_filter_webp.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for image filter module, WebP support.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http image_filter/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location /size {\n            image_filter size;\n            alias %%TESTDIR%%/;\n        }\n\n        location /test {\n            image_filter test;\n            alias %%TESTDIR%%/;\n        }\n\n        location /resize {\n            image_filter resize 1 1;\n            alias %%TESTDIR%%/;\n        }\n\n        location /quality {\n            image_filter rotate 90;\n            image_filter_webp_quality 50;\n            alias %%TESTDIR%%/;\n        }\n        location /quality_var {\n            image_filter rotate 90;\n            image_filter_webp_quality $arg_q;\n            alias %%TESTDIR%%/;\n        }\n    }\n}\n\nEOF\n\n$t->run()->plan(18);\n\n$t->write_file('webp', pack(\"A4LA8\", \"RIFF\", 0x22, \"WEBPVP8 \") .\n\tpack(\"N4\", 0x16000000, 0x3001009d, 0x012a0100, 0x01000ec0) .\n\tpack(\"N2n\", 0xfe25a400, 0x03700000, 0x0000));\n$t->write_file('webpl', pack(\"A4LA8\", \"RIFF\", 0x1a, \"WEBPVP8L\") .\n\tpack(\"N4n\", 0x0d000000, 0x2f000000, 0x10071011, 0x118888fe, 0x0700));\n$t->write_file('webpx', pack(\"A4LA8\", \"RIFF\", 0x4a, \"WEBPVP8X\") .\n\tpack(\"N4\", 0x0a000000, 0x10000000, 0x00000000, 0x0000414c) .\n\tpack(\"N4\", 0x50480c00, 0x00001107, 0x1011fd0f, 0x4444ff03) .\n\tpack(\"N4\", 0x00005650, 0x38201800, 0x00001401, 0x009d012a) .\n\tpack(\"N4n\", 0x01000100, 0x0000fe00, 0x000dc000, 0xfee6b500, 0x0000));\n\n$t->write_file('webperr', pack(\"A4LA8\", \"RIFF\", 0x22, \"WEBPERR \") .\n\tpack(\"N4\", 0x16000000, 0x3001009d, 0x012a0100, 0x01000ec0) .\n\tpack(\"N2n\", 0xfe25a400, 0x03700000, 0x0000));\n$t->write_file('webptrunc', substr $t->read_file('webp'), 0, 29);\n\n###############################################################################\n\nmy $r = http_get('/test/webp');\nlike($r, qr!Content-Type: image/webp!, 'content-type');\nlike($r, qr/RIFF/, 'content');\n\n$r = http_get('/size/webp');\nlike($r, qr/\"type\": \"webp\"/, 'size type');\nlike($r, qr/\"width\": 1/, 'size width');\nlike($r, qr/\"height\": 1/, 'size height');\n\n# lossless\n\n$r = http_get('/size/webpl');\nlike($r, qr/\"type\": \"webp\"/, 'lossless type');\nlike($r, qr/\"width\": 1/, 'lossless width');\nlike($r, qr/\"height\": 1/, 'lossless height');\n\n# extended\n\n$r = http_get('/size/webpx');\nlike($r, qr/\"type\": \"webp\"/, 'extended type');\nlike($r, qr/\"width\": 1/, 'extended width');\nlike($r, qr/\"height\": 1/, 'extended height');\n\n# transforms, libgd may have no WebP support\n\nlike(http_get('/quality/webp'), qr/RIFF|415/, 'quality');\nlike(http_get('/quality_var/webp?q=40'), qr/RIFF|415/, 'quality var');\nlike(http_get('/resize/webp'), qr/RIFF/, 'resize as is');\n\n# generic error handling\n\nlike(http_get('/quality/webperr'), qr/415 Unsupported/, 'bad header');\nlike(http_get('/quality/webptrunc'), qr/415 Unsupported/, 'truncated');\n\nlike(http_get('/size/webperr'), qr/{}/, 'size - bad header');\nlike(http_get('/size/webptrunc'), qr/{}/, 'size - truncated');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/index.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for index module.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http/)->plan(14)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n        add_header   X-URI $uri;\n\n        location / {\n            # index index.html by default\n        }\n\n        location /redirect/ {\n            index /re.html;\n        }\n\n        location /loop/ {\n            index /loop/;\n        }\n\n        location /no_index/ {\n            alias %%TESTDIR%%/;\n            index nonexisting.html;\n        }\n\n        location /many/ {\n            alias %%TESTDIR%%/;\n            index nonexisting.html many.html;\n        }\n\n        location /var/ {\n            alias %%TESTDIR%%/;\n            index $server_name.html;\n        }\n\n        location /va2/ {\n            alias %%TESTDIR%%/;\n            # before 1.13.8, the token produced emerg:\n            # directive \"index\" is not terminated by \";\"\n            index ${server_name}.html;\n        }\n\n        location /var_redirect/ {\n            index /$server_name.html;\n        }\n\n        location /not_found/ {\n            error_log %%TESTDIR%%/log_not_found.log;\n\n            location /not_found/off/ {\n                error_log %%TESTDIR%%/off.log;\n                log_not_found off;\n            }\n        }\n    }\n}\n\nEOF\n\n$t->write_file('index.html', 'body');\n$t->write_file('many.html', 'manybody');\n$t->write_file('re.html', 'rebody');\n$t->write_file('localhost.html', 'varbody');\n\nmy $d = $t->testdir();\nmkdir(\"$d/forbidden\");\nchmod(0000, \"$d/forbidden\");\n\n$t->run();\n\n###############################################################################\n\nlike(http_get('/'), qr/X-URI: \\/index.html.*body/ms, 'default index');\nlike(http_get('/no_index/'), qr/403 Forbidden/, 'no index');\nlike(http_get('/redirect/'), qr/X-URI: \\/re.html.*rebody/ms, 'redirect');\nlike(http_get('/loop/'), qr/500 Internal/, 'redirect loop');\nlike(http_get('/many/'), qr/X-URI: \\/many\\/many.html.*manybody/ms, 'many');\nlike(http_get('/var/'), qr/X-URI: \\/var\\/localhost.html.*varbody/ms, 'var');\nlike(http_get('/va2/'), qr/X-URI: \\/va2\\/localhost.html.*varbody/ms, 'var 2');\nlike(http_get('/var_redirect/'), qr/X-URI: \\/localhost.html.*varbody/ms,\n\t'var with redirect');\n\nlike(http_get('/not_found/'), qr/404 Not Found/, 'not found');\nlike(http_get('/not_found/off/'), qr/404 Not Found/, 'not found log off');\nlike(http_get('/forbidden/'), qr/403 Forbidden/, 'directory access denied');\nlike(http_get('/index.html/'), qr/404 Not Found/, 'not a directory');\n\n$t->stop();\n\nlike($t->read_file('log_not_found.log'), qr/error/, 'log_not_found');\nunlike($t->read_file('off.log'), qr/error/, 'log_not_found off');\n\nchmod(0700, \"$d/forbidden\");\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/js.t",
    "content": "#!/usr/bin/perl\n\n# (C) Roman Arutyunyan\n# (C) Dmitry Volyntsev\n# (C) Nginx, Inc.\n\n# Tests for http njs module.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\nuse Socket qw/ CRLF /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http rewrite/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    js_set $test_method   test.method;\n    js_set $test_version  test.version;\n    js_set $test_addr     test.addr;\n    js_set $test_uri      test.uri;\n    js_set $test_var      test.variable;\n    js_set $test_type     test.type;\n    js_set $test_global   test.global_obj;\n    js_set $test_log      test.log;\n    js_set $test_internal test.sub_internal;\n    js_set $test_except   test.except;\n\n    js_import test.js;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location /njs {\n            js_content test.njs;\n        }\n\n        location /method {\n            return 200 $test_method;\n        }\n\n        location /version {\n            return 200 $test_version;\n        }\n\n        location /addr {\n            return 200 $test_addr;\n        }\n\n        location /uri {\n            return 200 $test_uri;\n        }\n\n        location /var {\n            return 200 $test_var;\n        }\n\n        location /global {\n            return 200 $test_global;\n        }\n\n        location /body {\n            js_content test.request_body;\n        }\n\n        location /in_file {\n            client_body_in_file_only on;\n            js_content test.request_body;\n        }\n\n        location /status {\n            js_content test.status;\n        }\n\n        location /request_body {\n            js_content test.request_body;\n        }\n\n        location /request_body_cache {\n            js_content test.request_body_cache;\n        }\n\n        location /send {\n            js_content test.send;\n        }\n\n        location /return_method {\n            js_content test.return_method;\n        }\n\n        location /type {\n            js_content test.type;\n        }\n\n        location /log {\n            return 200 $test_log;\n        }\n\n        location /internal {\n            js_content test.internal;\n        }\n\n        location /sub_internal {\n            internal;\n            return 200 $test_internal;\n        }\n\n        location /except {\n            return 200 $test_except;\n        }\n\n        location /content_except {\n            js_content test.content_except;\n        }\n\n        location /content_empty {\n            js_content test.content_empty;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('test.js', <<EOF);\n    var global = ['n', 'j', 's'].join(\"\");\n\n    function test_njs(r) {\n        r.return(200, njs.version);\n    }\n\n    function method(r) {\n        return 'method=' + r.method;\n    }\n\n    function version(r) {\n        return 'version=' + r.httpVersion;\n    }\n\n    function addr(r) {\n        return 'addr=' + r.remoteAddress;\n    }\n\n    function uri(r) {\n        return 'uri=' + r.uri;\n    }\n\n    function variable(r) {\n        return 'variable=' + r.variables.remote_addr;\n    }\n\n    function global_obj(r) {\n        return 'global=' + global;\n    }\n\n    function status(r) {\n        r.status = 204;\n        r.sendHeader();\n        r.finish();\n    }\n\n    function request_body(r) {\n        try {\n            var body = r.requestText;\n            r.return(200, body);\n\n        } catch (e) {\n            r.return(500, e.message);\n        }\n    }\n\n    function request_body_cache(r) {\n        function t(v) {return Buffer.isBuffer(v) ? 'buffer' : (typeof v);}\n        r.return(200,\n      `requestText:\\${t(r.requestText)} requestBuffer:\\${t(r.requestBuffer)}`);\n    }\n\n    function send(r) {\n        var a, s;\n        r.status = 200;\n        r.sendHeader();\n        for (a in r.args) {\n            if (a.substr(0, 3) == 'foo') {\n                s = r.args[a];\n                r.send('n=' + a + ', v=' + s.substr(0, 2) + ' ');\n            }\n        }\n        r.finish();\n    }\n\n    function return_method(r) {\n        r.return(Number(r.args.c), r.args.t);\n    }\n\n    function type(r) {\n        var p = r.args.path.split('.').reduce((a, v) => a[v], r);\n\n        var typ = Buffer.isBuffer(p) ? 'buffer' : (typeof p);\n        r.return(200, `type: \\${typ}`);\n    }\n\n    function log(r) {\n        r.log('SEE-LOG');\n    }\n\n    async function internal(r) {\n        let reply = await r.subrequest('/sub_internal');\n\n        r.return(200, `parent: \\${r.internal} sub: \\${reply.responseText}`);\n    }\n\n    function sub_internal(r) {\n        return r.internal;\n    }\n\n    function except(r) {\n        var fs = require('fs');\n        fs.readFileSync();\n    }\n\n\n    function content_except(r) {\n        JSON.parse({}.a.a);\n    }\n\n    function content_empty(r) {\n    }\n\n    export default {njs:test_njs, method, version, addr, uri,\n                    variable, global_obj, status, request_body, internal,\n                    request_body_cache, send, return_method, sub_internal,\n                    type, log, except, content_except, content_empty};\n\nEOF\n\n$t->try_run('no njs available')->plan(27);\n\n###############################################################################\n\nlike(http_get('/method'), qr/method=GET/, 'r.method');\nlike(http_get('/version'), qr/version=1.0/, 'r.httpVersion');\nlike(http_get('/addr'), qr/addr=127.0.0.1/, 'r.remoteAddress');\nlike(http_get('/uri'), qr/uri=\\/uri/, 'r.uri');\n\nlike(http_get('/status'), qr/204 No Content/, 'r.status');\n\nlike(http_post('/body'), qr/REQ-BODY/, 'request body');\nlike(http_post('/in_file'), qr/request body is in a file/,\n\t'request body in file');\nlike(http_post_big('/body'), qr/200.*^(1234567890){1024}$/ms,\n\t'request body big');\n\nlike(http_get('/send?foo=12345&n=11&foo-2=bar&ndd=&foo-3=z'),\n\tqr/n=foo, v=12 n=foo-2, v=ba n=foo-3, v=z/, 'r.send');\n\nlike(http_get('/return_method?c=200'), qr/200 OK.*\\x0d\\x0a?\\x0d\\x0a?$/s,\n\t'return code');\nlike(http_get('/return_method?c=200&t=SEE-THIS'), qr/200 OK.*^SEE-THIS$/ms,\n\t'return text');\nlike(http_get('/return_method?c=301&t=path'), qr/ 301 .*Location: path/s,\n\t'return redirect');\nlike(http_get('/return_method?c=404'), qr/404 Not.*html/s, 'return error page');\nlike(http_get('/return_method?c=inv'), qr/ 500 /, 'return invalid');\n\nTODO: {\nlocal $TODO = 'not yet'\n\tunless http_get('/njs') =~ /^([.0-9]+)$/m && $1 ge '0.5.0';\n\nlike(http_get('/type?path=variables.host'), qr/200 OK.*type: string$/s,\n\t'variables type');\nlike(http_get('/type?path=rawVariables.host'), qr/200 OK.*type: buffer$/s,\n\t'rawVariables type');\n\nlike(http_post('/type?path=requestText'), qr/200 OK.*type: string$/s,\n\t'requestText type');\nlike(http_post('/type?path=requestBuffer'), qr/200 OK.*type: buffer$/s,\n\t'requestBuffer type');\nlike(http_post('/request_body_cache'),\n\tqr/requestText:string requestBuffer:buffer$/s, 'request body cache');\n\n}\n\nlike(http_get('/var'), qr/variable=127.0.0.1/, 'r.variables');\nlike(http_get('/global'), qr/global=njs/, 'global code');\nlike(http_get('/log'), qr/200 OK/, 'r.log');\n\nTODO: {\nlocal $TODO = 'not yet'\n\tunless http_get('/njs') =~ /^([.0-9]+)$/m && $1 ge '0.7.7';\n\nlike(http_get('/internal'), qr/parent: false sub: true/, 'r.internal');\n\n}\n\nhttp_get('/except');\nhttp_get('/content_except');\n\nlike(http_get('/content_empty'), qr/500 Internal Server Error/,\n\t'empty handler');\n\n$t->stop();\n\nok(index($t->read_file('error.log'), 'SEE-LOG') > 0, 'log js');\nok(index($t->read_file('error.log'), 'at fs.readFileSync') > 0,\n\t'js_set backtrace');\nok(index($t->read_file('error.log'), 'at JSON.parse') > 0,\n\t'js_content backtrace');\n\n###############################################################################\n\nsub http_get_hdr {\n\tmy ($url, %extra) = @_;\n\treturn http(<<EOF, %extra);\nGET $url HTTP/1.0\nFoO: 12345\n\nEOF\n}\n\nsub http_get_ihdr {\n\tmy ($url, %extra) = @_;\n\treturn http(<<EOF, %extra);\nGET $url HTTP/1.0\nfoo: 12345\nHost: localhost\nfoo2: bar\nX-xxx: more\nfoo-3: z\n\nEOF\n}\n\nsub http_post {\n\tmy ($url, %extra) = @_;\n\n\tmy $p = \"POST $url HTTP/1.0\" . CRLF .\n\t\t\"Host: localhost\" . CRLF .\n\t\t\"Content-Length: 8\" . CRLF .\n\t\tCRLF .\n\t\t\"REQ-BODY\";\n\n\treturn http($p, %extra);\n}\n\nsub http_post_big {\n\tmy ($url, %extra) = @_;\n\n\tmy $p = \"POST $url HTTP/1.0\" . CRLF .\n\t\t\"Host: localhost\" . CRLF .\n\t\t\"Content-Length: 10240\" . CRLF .\n\t\tCRLF .\n\t\t(\"1234567890\" x 1024);\n\n\treturn http($p, %extra);\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/js_args.t",
    "content": "#!/usr/bin/perl\n\n# (C) Dmitry Volyntsev\n# (C) Nginx, Inc.\n\n# Tests for http njs module, arguments tests.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\neval { require JSON::PP; };\nplan(skip_all => \"JSON::PP not installed\") if $@;\n\nmy $t = Test::Nginx->new()->has(qw/http/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    js_import test.js;\n\n    js_set $test_iter     test.iter;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location /njs {\n            js_content test.njs;\n        }\n\n        location /iter {\n            return 200 $test_iter;\n        }\n\n        location /keys {\n            js_content test.keys;\n        }\n\n        location /object {\n            js_content test.object;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('test.js', <<EOF);\n    function test_njs(r) {\n        r.return(200, njs.version);\n    }\n\n    function iter(r) {\n        var s = '', a;\n        for (a in r.args) {\n            if (a.substr(0, 3) == 'foo') {\n                s += r.args[a];\n            }\n        }\n\n        return s;\n    }\n\n    function keys(r) {\n        r.return(200, Object.keys(r.args).sort());\n    }\n\n    function object(r) {\n        r.return(200, JSON.stringify(r.args));\n    }\n\n    export default {njs: test_njs, iter, keys, object};\n\nEOF\n\n$t->try_run('no njs')->plan(15);\n\n###############################################################################\n\nsub recode {\n\tmy $json;\n\teval { $json = JSON::PP::decode_json(shift) };\n\n\tif ($@) {\n\t\treturn \"<failed to parse JSON>\";\n\t}\n\n\tJSON::PP->new()->canonical()->encode($json);\n}\n\nsub get_json {\n\thttp_get(shift) =~ /\\x0d\\x0a?\\x0d\\x0a?(.*)/ms;\n\trecode($1);\n}\n\n###############################################################################\n\nTODO: {\nlocal $TODO = 'not yet'\n    unless http_get('/njs') =~ /^([.0-9]+)$/m && $1 ge '0.7.6';\n\nlike(http_get('/iter?foo=12345&foo2=bar&nn=22&foo-3=z'), qr/12345barz/,\n\t'r.args iteration');\nlike(http_get('/iter?foo=123&foo2=&foo3&foo4=456'), qr/123456/,\n\t'r.args iteration 2');\nlike(http_get('/iter?foo=123&foo2=&foo3'), qr/123/, 'r.args iteration 3');\nlike(http_get('/iter?foo=123&foo2='), qr/123/, 'r.args iteration 4');\nlike(http_get('/iter?foo=1&foo=2'), qr/1,2/m, 'r.args iteration 5');\n\nlike(http_get('/keys?b=1&c=2&a=5'), qr/a,b,c/m, 'r.args sorted keys');\nlike(http_get('/keys?b=1&b=2'), qr/b/m, 'r.args duplicate keys');\nlike(http_get('/keys?b=1&a&c='), qr/a,b,c/m, 'r.args empty value');\n\nis(get_json('/object'), '{}', 'empty object');\nis(get_json('/object?a=1&b=2&c=3'), '{\"a\":\"1\",\"b\":\"2\",\"c\":\"3\"}',\n\t'ordinary object');\nis(get_json('/object?a=1&A=2'), '{\"A\":\"2\",\"a\":\"1\"}',\n\t'case sensitive object');\nis(get_json('/object?a=1&A=2&a=3'), '{\"A\":\"2\",\"a\":[\"1\",\"3\"]}',\n\t'duplicate keys object');\nis(get_json('/object?%61=1&a=2'), '{\"a\":[\"1\",\"2\"]}',\n\t'keys percent-encoded object');\nis(get_json('/object?a=%62%63&b=%63%64'), '{\"a\":\"bc\",\"b\":\"cd\"}',\n\t'values percent-encoded object');\nis(get_json('/object?a=%6&b=%&c=%%&d=%zz'),\n\t'{\"a\":\"%6\",\"b\":\"%\",\"c\":\"%%\",\"d\":\"%zz\"}',\n\t'values percent-encoded broken object');\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/js_async.t",
    "content": "#!/usr/bin/perl\n\n# (C) Dmitry Volyntsev\n# (C) Nginx, Inc.\n\n# Async tests for http njs module.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http rewrite/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    js_set $test_async      test.set_timeout;\n    js_set $context_var     test.context_var;\n    js_set $test_set_rv_var test.set_rv_var;\n\n    js_import test.js;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location /njs {\n            js_content test.njs;\n        }\n\n        location /async_var {\n            return 200 $test_async;\n        }\n\n        location /shared_ctx {\n            add_header H $context_var;\n            js_content test.shared_ctx;\n        }\n\n        location /set_timeout {\n            js_content test.set_timeout;\n        }\n\n        location /set_timeout_many {\n            js_content test.set_timeout_many;\n        }\n\n        location /set_timeout_data {\n            postpone_output 0;\n            js_content test.set_timeout_data;\n        }\n\n        location /limit_rate {\n            postpone_output 0;\n            sendfile_max_chunk 5;\n            js_content test.limit_rate;\n        }\n\n        location /async_content {\n            js_content test.async_content;\n        }\n\n        location /set_rv_var {\n            return 200 $test_set_rv_var;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('test.js', <<EOF);\n    function test_njs(r) {\n        r.return(200, njs.version);\n    }\n\n    function set_timeout(r) {\n        var timerId = setTimeout(timeout_cb_r, 5, r, 0);\n        clearTimeout(timerId);\n        setTimeout(timeout_cb_r, 5, r, 0)\n    }\n\n    function set_timeout_data(r) {\n        setTimeout(timeout_cb_data, 5, r, 0);\n    }\n\n    function set_timeout_many(r) {\n        for (var i = 0; i < 5; i++) {\n            setTimeout(timeout_cb_empty, 5, r, i);\n        }\n\n        setTimeout(timeout_cb_reply, 10, r);\n    }\n\n    function timeout_cb_r(r, cnt) {\n        if (cnt == 10) {\n            r.status = 200;\n            r.headersOut['Content-Type'] = 'foo';\n            r.sendHeader();\n            r.finish();\n\n        } else {\n            setTimeout(timeout_cb_r, 5, r, ++cnt);\n        }\n    }\n\n    function timeout_cb_empty(r, arg) {\n        r.log(\"timeout_cb_empty\" + arg);\n    }\n\n    function timeout_cb_reply(r) {\n        r.status = 200;\n        r.headersOut['Content-Type'] = 'reply';\n        r.sendHeader();\n        r.finish();\n    }\n\n    function timeout_cb_data(r, counter) {\n        if (counter == 0) {\n            r.log(\"timeout_cb_data: init\");\n            r.status = 200;\n            r.sendHeader();\n            setTimeout(timeout_cb_data, 5, r, ++counter);\n\n        } else if (counter == 10) {\n            r.log(\"timeout_cb_data: finish\");\n            r.finish();\n\n        } else {\n            r.send(\"\" + counter);\n            setTimeout(timeout_cb_data, 5, r, ++counter);\n        }\n    }\n\n    var js_;\n    function context_var() {\n        return js_;\n    }\n\n    function shared_ctx(r) {\n        js_ = r.variables.arg_a;\n\n        r.status = 200;\n        r.sendHeader();\n        r.finish();\n    }\n\n    function limit_rate_cb(r) {\n        r.finish();\n    }\n\n    function limit_rate(r) {\n        r.status = 200;\n        r.sendHeader();\n        r.send(\"AAAAA\".repeat(10))\n        setTimeout(limit_rate_cb, 1000, r);\n    }\n\n    function pr(x) {\n        return new Promise(resolve => {resolve(x)}).then(v => v).then(v => v);\n    }\n\n    async function async_content(r) {\n        const a1 = await pr('A');\n        const a2 = await pr('B');\n\n        r.return(200, `retval: \\${a1 + a2}`);\n    }\n\n    async function set_rv_var(r) {\n        const a1 = await pr(10);\n        const a2 = await pr(20);\n\n        r.setReturnValue(`retval: \\${a1 + a2}`);\n    }\n\n    export default {njs:test_njs, set_timeout, set_timeout_data,\n                    set_timeout_many, context_var, shared_ctx, limit_rate,\n                    async_content, set_rv_var};\n\nEOF\n\n$t->try_run('no njs available')->plan(9);\n\n###############################################################################\n\nlike(http_get('/set_timeout'), qr/Content-Type: foo/, 'setTimeout');\nlike(http_get('/set_timeout_many'), qr/Content-Type: reply/, 'setTimeout many');\nlike(http_get('/set_timeout_data'), qr/123456789/, 'setTimeout data');\nlike(http_get('/shared_ctx?a=xxx'), qr/H: xxx/, 'shared context');\nlike(http_get('/limit_rate'), qr/A{50}/, 'limit_rate');\n\nTODO: {\nlocal $TODO = 'not yet'\n\tunless http_get('/njs') =~ /^([.0-9]+)$/m && $1 ge '0.7.0';\n\nlike(http_get('/async_content'), qr/retval: AB/, 'async content');\nlike(http_get('/set_rv_var'), qr/retval: 30/, 'set return value variable');\n\n}\n\nhttp_get('/async_var');\n\n$t->stop();\n\nok(index($t->read_file('error.log'), 'pending events') > 0,\n   'pending js events');\nok(index($t->read_file('error.log'), 'async operation inside') > 0,\n   'async op in var handler');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/js_body_filter.t",
    "content": "#!/usr/bin/perl\n\n# (C) Dmitry Volyntsev\n# (C) Nginx, Inc.\n\n# Tests for http njs module, body filter.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    js_import test.js;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location /njs {\n            js_content test.njs;\n        }\n\n        location /append {\n            js_body_filter test.append;\n            proxy_pass http://127.0.0.1:8081/source;\n        }\n\n        location /buffer_type {\n            js_body_filter test.buffer_type buffer_type=buffer;\n            proxy_pass http://127.0.0.1:8081/source;\n        }\n\n        location /forward {\n            js_body_filter test.forward buffer_type=string;\n            proxy_pass http://127.0.0.1:8081/source;\n        }\n\n        location /filter {\n            proxy_buffering off;\n            js_body_filter test.filter;\n            proxy_pass http://127.0.0.1:8081/source;\n        }\n\n        location /prepend {\n            js_body_filter test.prepend;\n            proxy_pass http://127.0.0.1:8081/source;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        location /source {\n            postpone_output 1;\n            js_content test.source;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('test.js', <<EOF);\n    function test_njs(r) {\n        r.return(200, njs.version);\n    }\n\n    function append(r, data, flags) {\n        r.sendBuffer(data, {last:false});\n\n        if (flags.last) {\n            r.sendBuffer(\"XXX\", flags);\n        }\n    }\n\n    var collect = Buffer.from([]);\n    function buffer_type(r, data, flags) {\n        collect = Buffer.concat([collect, data]);\n\n        if (flags.last) {\n            r.sendBuffer(collect, flags);\n        }\n    }\n\n    function chain(chunks, i) {\n        if (i < chunks.length) {\n            chunks.r.send(chunks[i++]);\n            setTimeout(chunks.chain, chunks.delay, chunks, i);\n\n        } else {\n            chunks.r.finish();\n        }\n    }\n\n    function source(r) {\n        var chunks = ['AAA', 'BB', 'C', 'DDDD'];\n        chunks.delay = 5;\n        chunks.r = r;\n        chunks.chain = chain;\n\n        r.status = 200;\n        r.sendHeader();\n        chain(chunks, 0);\n    }\n\n    function filter(r, data, flags) {\n        if (flags.last || data.length >= Number(r.args.len)) {\n            r.sendBuffer(`\\${data}|`, flags);\n\n            if (r.args.dup && !flags.last) {\n                r.sendBuffer(data, flags);\n            }\n        }\n    }\n\n    function forward(r, data, flags) {\n        r.sendBuffer(data, flags);\n    }\n\n    function prepend(r, data, flags) {\n        r.sendBuffer(\"XXX\");\n        r.sendBuffer(data, flags);\n        r.done();\n    }\n\n    export default {njs: test_njs, append, buffer_type, filter, forward,\n                    prepend, source};\n\nEOF\n\n$t->try_run('no njs body filter')->plan(6);\n\n###############################################################################\n\nlike(http_get('/append'), qr/AAABBCDDDDXXX/, 'append');\nlike(http_get('/buffer_type'), qr/AAABBCDDDD/, 'buffer type');\nlike(http_get('/forward'), qr/AAABBCDDDD/, 'forward');\nlike(http_get('/filter?len=3'), qr/AAA|DDDD|/, 'filter 3');\nlike(http_get('/filter?len=2&dup=1'), qr/AAA|AAABB|BBDDDD|DDDD/,\n\t'filter 2 dup');\nlike(http_get('/prepend'), qr/XXXAAABBCDDDD/, 'prepend');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/js_body_filter_if.t",
    "content": "#!/usr/bin/perl\n\n# (C) Dmitry Volyntsev\n# (C) Nginx, Inc.\n\n# Tests for http njs module, body filter, if context.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy rewrite/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    js_import test.js;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location /njs {\n            js_content test.njs;\n        }\n\n        location /filter {\n            if ($arg_name ~ \"prepend\") {\n                js_body_filter test.prepend;\n            }\n\n            if ($arg_name ~ \"append\") {\n                js_body_filter test.append;\n            }\n\n            js_body_filter test.should_not_be_called;\n\n            proxy_pass http://127.0.0.1:8081/source;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        location /source {\n            postpone_output 1;\n            js_content test.source;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('test.js', <<EOF);\n    function test_njs(r) {\n        r.return(200, njs.version);\n    }\n\n    function append(r, data, flags) {\n        r.sendBuffer(data, {last:false});\n\n        if (flags.last) {\n            r.sendBuffer(\"XXX\", flags);\n        }\n    }\n\n    function chain(chunks, i) {\n        if (i < chunks.length) {\n            chunks.r.send(chunks[i++]);\n            setTimeout(chunks.chain, chunks.delay, chunks, i);\n\n        } else {\n            chunks.r.finish();\n        }\n    }\n\n    function source(r) {\n        var chunks = ['AAA', 'BB', 'C', 'DDDD'];\n        chunks.delay = 5;\n        chunks.r = r;\n        chunks.chain = chain;\n\n        r.status = 200;\n        r.sendHeader();\n        chain(chunks, 0);\n    }\n\n    function prepend(r, data, flags) {\n        r.sendBuffer(\"XXX\");\n        r.sendBuffer(data, flags);\n        r.done();\n    }\n\n    export default {njs: test_njs, append, prepend, source};\n\nEOF\n\n$t->try_run('no njs body filter')->plan(2);\n\n###############################################################################\n\nlike(http_get('/filter?name=append'), qr/AAABBCDDDDXXX/, 'append');\nlike(http_get('/filter?name=prepend'), qr/XXXAAABBCDDDD/, 'prepend');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/js_buffer.t",
    "content": "#!/usr/bin/perl\n\n# (C) Dmitry Volyntsev\n# (C) Nginx, Inc.\n\n# Tests for http njs module, buffer properties.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse Socket qw/ CRLF /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\neval { require JSON::PP; };\nplan(skip_all => \"JSON::PP not installed\") if $@;\n\nmy $t = Test::Nginx->new()->has(qw/http rewrite proxy/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    js_import test.js;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location /njs {\n            js_content test.njs;\n        }\n\n        location /return {\n            js_content test.return;\n        }\n\n        location /req_body {\n            js_content test.req_body;\n        }\n\n        location /res_body {\n            js_content test.res_body;\n        }\n\n        location /res_text {\n            js_content test.res_text;\n        }\n\n        location /binary_var {\n            js_content test.binary_var;\n        }\n\n        location /p/ {\n            proxy_pass http://127.0.0.1:8081/;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        location /sub1 {\n            return 200 '{\"a\": {\"b\": 1}}';\n        }\n    }\n}\n\nEOF\n\n$t->write_file('test.js', <<EOF);\n    function test_njs(r) {\n        r.return(200, njs.version);\n    }\n\n    function test_return(r) {\n        var body = Buffer.from(\"body: \");\n        body = Buffer.concat([body, Buffer.from(r.args.text)]);\n        r.return(200, body);\n    }\n\n    function req_body(r) {\n        var body = r.requestBuffer;\n        var view = new DataView(body.buffer);\n        view.setInt8(2, 'c'.charCodeAt(0));\n        r.return(200, JSON.parse(body).c.b);\n    }\n\n    function type(v) {return Buffer.isBuffer(v) ? 'buffer' : (typeof v);}\n\n    function res_body(r) {\n        r.subrequest('/p/sub1')\n        .then(reply => {\n            var body = reply.responseBuffer;\n            var view = new DataView(body.buffer);\n            view.setInt8(2, 'c'.charCodeAt(0));\n            body = JSON.parse(body);\n            body.type = type(reply.responseBuffer);\n            r.return(200, JSON.stringify(body));\n        })\n    }\n\n    function res_text(r) {\n        r.subrequest('/p/sub1')\n        .then(reply => {\n            var body = JSON.parse(reply.responseText);\n            body.type = type(reply.responseText);\n            r.return(200, JSON.stringify(body));\n        })\n    }\n\n    function binary_var(r) {\n        var test = r.rawVariables.binary_remote_addr\n                   .equals(Buffer.from([127,0,0,1]));\n        r.return(200, test);\n    }\n\n    export default {njs: test_njs, return: test_return, req_body, res_body,\n                    res_text, binary_var};\n\nEOF\n\n$t->try_run('no njs buffer')->plan(5);\n\n###############################################################################\n\nlike(http_get('/return?text=FOO'), qr/200 OK.*body: FOO$/s,\n\t'return buffer');\nlike(http_post('/req_body'), qr/200 OK.*BAR$/s, 'request buffer');\nis(get_json('/res_body'), '{\"c\":{\"b\":1},\"type\":\"buffer\"}', 'response buffer');\nis(get_json('/res_text'), '{\"a\":{\"b\":1},\"type\":\"string\"}', 'response text');\nlike(http_get('/binary_var'), qr/200 OK.*true$/s,\n\t'binary var');\n\n###############################################################################\n\nsub recode {\n\tmy $json;\n\teval { $json = JSON::PP::decode_json(shift) };\n\n\tif ($@) {\n\t\treturn \"<failed to parse JSON>\";\n\t}\n\n\tJSON::PP->new()->canonical()->encode($json);\n}\n\nsub get_json {\n\thttp_get(shift) =~ /\\x0d\\x0a?\\x0d\\x0a?(.*)/ms;\n\trecode($1);\n}\n\nsub http_post {\n\tmy ($url, %extra) = @_;\n\n\tmy $p = \"POST $url HTTP/1.0\" . CRLF .\n\t\t\"Host: localhost\" . CRLF .\n\t\t\"Content-Length: 17\" . CRLF .\n\t\tCRLF .\n\t\t\"{\\\"a\\\":{\\\"b\\\":\\\"BAR\\\"}}\";\n\n\treturn http($p, %extra);\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/js_dump.t",
    "content": "#!/usr/bin/perl\n\n# (C) Dmitry Volyntsev\n# (C) Nginx, Inc.\n\n# Tests for http njs module, request object dump.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\nuse Socket qw/ CRLF /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http rewrite/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    js_import test.js;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location /dump {\n            js_content test.dump;\n        }\n\n        location /stringify {\n            js_content test.stringify;\n        }\n\n        location /stringify_subrequest {\n            js_content test.stringify_subrequest;\n        }\n\n        location /js_sub {\n            return 201 '{$request_method}';\n        }\n    }\n}\n\nEOF\n\n$t->write_file('test.js', <<EOF);\n    function dump(r) {\n        r.headersOut.baz = 'bar';\n        r.return(200, njs.dump(r));\n    }\n\n    function stringify(r) {\n        r.headersOut.baz = 'bar';\n        var obj = JSON.parse(JSON.stringify(r));\n        r.return(200, JSON.stringify(obj));\n    }\n\n    function stringify_subrequest(r) {\n        r.subrequest('/js_sub', reply => {\n            r.return(200, JSON.stringify(reply))\n        });\n    }\n\n    export default {dump, stringify, stringify_subrequest};\n\nEOF\n\n$t->try_run('no njs dump')->plan(3);\n\n###############################################################################\n\nlike(http(\n\t'GET /dump?v=1&t=x HTTP/1.0' . CRLF\n\t. 'Foo: bar' . CRLF\n\t. 'Foo2: bar2' . CRLF\n\t. 'Host: localhost' . CRLF . CRLF\n), qr/method:'GET'/, 'njs.dump(r)');\n\nlike(http(\n\t'GET /stringify?v=1&t=x HTTP/1.0' . CRLF\n\t. 'Foo: bar' . CRLF\n\t. 'Foo2: bar2' . CRLF\n\t. 'Host: localhost' . CRLF . CRLF\n), qr/headersOut\":\\{\"baz\":\"bar\"}/, 'JSON.stringify(r)');\n\nlike(http(\n\t'GET /stringify_subrequest HTTP/1.0' . CRLF\n\t. 'Host: localhost' . CRLF . CRLF\n), qr/\"status\":201/, 'JSON.stringify(reply)');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/js_fetch.t",
    "content": "#!/usr/bin/perl\n\n# (C) Dmitry Volyntsev\n# (C) Nginx, Inc.\n\n# Tests for http njs module, fetch method.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse Socket qw/ CRLF /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\neval { require JSON::PP; };\nplan(skip_all => \"JSON::PP not installed\") if $@;\n\nmy $t = Test::Nginx->new()->has(qw/http/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    js_import test.js;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location /njs {\n            js_content test.njs;\n        }\n\n        location /broken {\n            js_content test.broken;\n        }\n\n        location /broken_response {\n            js_content test.broken_response;\n        }\n\n        location /body {\n            js_content test.body;\n        }\n\n        location /body_special {\n            js_content test.body_special;\n        }\n\n        location /chain {\n            js_content test.chain;\n        }\n\n        location /chunked {\n            js_content test.chunked;\n        }\n\n        location /header {\n            js_content test.header;\n        }\n\n        location /header_iter {\n            js_content test.header_iter;\n        }\n\n        location /multi {\n            js_content test.multi;\n        }\n\n        location /property {\n            js_content test.property;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  aaa;\n\n        location /loc {\n            js_content test.loc;\n        }\n\n        location /json { }\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  bbb;\n\n        location /loc {\n            js_content test.loc;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081;\n        server_name  ccc;\n\n        location /loc {\n            js_content test.loc;\n        }\n    }\n}\n\nEOF\n\nmy $p0 = port(8080);\nmy $p1 = port(8081);\nmy $p2 = port(8082);\n\n$t->write_file('json', '{\"a\":[1,2], \"b\":{\"c\":\"FIELD\"}}');\n\n$t->write_file('test.js', <<EOF);\n    function test_njs(r) {\n        r.return(200, njs.version);\n    }\n\n    function body(r) {\n        var loc = r.args.loc;\n        var getter = r.args.getter;\n\n        function query(obj) {\n            var path = r.args.path;\n            var retval = (getter == 'arrayBuffer') ? Buffer.from(obj).toString()\n                                                   : obj;\n\n            if (path) {\n                retval = path.split('.').reduce((a, v) => a[v], obj);\n            }\n\n            return JSON.stringify(retval);\n        }\n\n        ngx.fetch(`http://127.0.0.1:$p0/\\${loc}`, {headers: {Host: 'aaa'}})\n        .then(reply => reply[getter]())\n        .then(data => r.return(200, query(data)))\n        .catch(e => r.return(501, e.message))\n    }\n\n    function property(r) {\n        var opts = {headers:{Host: 'aaa'}};\n\n        if (r.args.code) {\n            opts.headers.code = r.args.code;\n        }\n\n        var p = ngx.fetch('http://127.0.0.1:$p0/loc', opts)\n\n        if (r.args.readBody) {\n            p = p.then(rep =>\n                 rep.text().then(body => {rep.text = body; return rep;}))\n        }\n\n        p.then(reply => r.return(200, reply[r.args.pr]))\n        .catch(e => r.return(501, e.message))\n    }\n\n    function process_errors(r, tests) {\n        var results = [];\n\n        tests.forEach(args => {\n            ngx.fetch.apply(r, args)\n            .then(reply => {\n                r.return(400, '[\"unexpected then\"]');\n            })\n            .catch(e => {\n                results.push(e.message);\n\n                if (results.length == tests.length) {\n                    results.sort();\n                    r.return(200, JSON.stringify(results));\n                }\n            })\n        })\n    }\n\n    function broken(r) {\n        var tests = [\n            ['http://127.0.0.1:1/loc'],\n            ['http://127.0.0.1:80800/loc'],\n            [Symbol.toStringTag],\n        ];\n\n        return process_errors(r, tests);\n    }\n\n    function broken_response(r) {\n        var tests = [\n            ['http://127.0.0.1:$p2/status_line'],\n            ['http://127.0.0.1:$p2/length'],\n            ['http://127.0.0.1:$p2/header'],\n            ['http://127.0.0.1:$p2/headers'],\n            ['http://127.0.0.1:$p2/content_length'],\n        ];\n\n        return process_errors(r, tests);\n    }\n\n    function chain(r) {\n        var results = [];\n        var reqs = [\n             ['http://127.0.0.1:$p0/loc', {headers: {Host:'aaa'}}],\n             ['http://127.0.0.1:$p0/loc', {headers: {Host:'bbb'}}],\n           ];\n\n           function next(reply) {\n              if (reqs.length == 0) {\n                 r.return(200, \"SUCCESS\");\n                 return;\n              }\n\n              ngx.fetch.apply(r, reqs.pop())\n              .then(next)\n              .catch(e => r.return(400, e.message))\n           }\n\n           next();\n    }\n\n    function chunked(r) {\n        var results = [];\n        var tests = [\n            ['http://127.0.0.1:$p2/big', {max_response_body_size:128000}],\n            ['http://127.0.0.1:$p2/big/ok', {max_response_body_size:128000}],\n            ['http://127.0.0.1:$p2/chunked'],\n            ['http://127.0.0.1:$p2/chunked/ok'],\n            ['http://127.0.0.1:$p2/chunked/big', {max_response_body_size:128}],\n            ['http://127.0.0.1:$p2/chunked/big'],\n        ];\n\n        function collect(v) {\n            results.push(v);\n\n            if (results.length == tests.length) {\n                results.sort();\n                r.return(200, JSON.stringify(results));\n            }\n        }\n\n        tests.forEach(args => {\n            ngx.fetch.apply(r, args)\n            .then(reply => reply.text())\n            .then(body => collect(body.length))\n            .catch(e => collect(e.message))\n        })\n    }\n\n    function header(r) {\n        var url = `http://127.0.0.1:$p2/\\${r.args.loc}`;\n        var method = r.args.method ? r.args.method : 'get';\n\n        var p = ngx.fetch(url)\n\n        if (r.args.readBody) {\n            p = p.then(rep =>\n                 rep.text().then(body => {rep.text = body; return rep;}))\n        }\n\n        p.then(reply => {\n            var h = reply.headers[method](r.args.h);\n            r.return(200, njs.dump(h));\n        })\n        .catch(e => r.return(501, e.message))\n    }\n\n    async function body_special(r) {\n        let reply = await ngx.fetch(`http://127.0.0.1:$p2/\\${r.args.loc}`);\n        let body = await reply.text();\n\n        r.return(200, body);\n    }\n\n    async function header_iter(r) {\n        let url = `http://127.0.0.1:$p2/\\${r.args.loc}`;\n\n        let response = await ngx.fetch(url);\n\n        let headers = response.headers;\n        let out = [];\n        for (let key in response.headers) {\n            if (key != 'Connection') {\n                out.push(`\\${key}:\\${headers.get(key)}`);\n            }\n        }\n\n        r.return(200, njs.dump(out));\n    }\n\n    function multi(r) {\n        var results = [];\n        var tests = [\n             [\n              'http://127.0.0.1:$p0/loc',\n               { headers: {Code: 201, Host: 'aaa'}},\n             ],\n             [\n              'http://127.0.0.1:$p0/loc',\n               { method:'POST', headers: {Code: 401, Host: 'bbb'}, body: 'OK'},\n             ],\n             [\n              'http://127.0.0.1:$p1/loc',\n               { method:'PATCH',\n                 headers: {foo:undefined, bar:'xxx', Host: 'ccc'}},\n             ],\n           ];\n\n        function cmp(a,b) {\n            if (a.b > b.b) {return 1;}\n            if (a.b < b.b) {return -1;}\n            return 0\n        }\n\n        tests.forEach(args => {\n            ngx.fetch.apply(r, args)\n            .then(rep =>\n                 rep.text().then(body => {rep.text = body; return rep;}))\n            .then(rep => {\n                results.push({b:rep.text,\n                              c:rep.status,\n                              u:rep.url});\n\n                if (results.length == tests.length) {\n                    results.sort(cmp);\n                    r.return(200, JSON.stringify(results));\n                }\n            })\n            .catch(e => {\n                r.return(400, `[\"\\${e.message}\"]`);\n                throw e;\n            })\n        })\n\n        if (r.args.throw) {\n            throw 'Oops';\n        }\n    }\n\n    function str(v) { return v ? v : ''};\n\n    function loc(r) {\n        var v = r.variables;\n        var body = str(r.requestText);\n        var foo = str(r.headersIn.foo);\n        var bar = str(r.headersIn.bar);\n        var c = r.headersIn.code ? Number(r.headersIn.code) : 200;\n        r.return(c, `\\${v.host}:\\${v.request_method}:\\${foo}:\\${bar}:\\${body}`);\n    }\n\n     export default {njs: test_njs, body, broken, broken_response, body_special,\n                     chain, chunked, header, header_iter, multi, loc, property};\nEOF\n\n$t->try_run('no njs.fetch')->plan(31);\n\n$t->run_daemon(\\&http_daemon, port(8082));\n$t->waitforsocket('127.0.0.1:' . port(8082));\n\n###############################################################################\n\nlike(http_get('/body?getter=arrayBuffer&loc=loc'), qr/200 OK.*\"aaa:GET:::\"$/s,\n\t'fetch body arrayBuffer');\nlike(http_get('/body?getter=text&loc=loc'), qr/200 OK.*\"aaa:GET:::\"$/s,\n\t'fetch body text');\nlike(http_get('/body?getter=json&loc=json&path=b.c'),\n\tqr/200 OK.*\"FIELD\"$/s, 'fetch body json');\nlike(http_get('/body?getter=json&loc=loc'), qr/501/s,\n\t'fetch body json invalid');\nlike(http_get('/body_special?loc=parted'), qr/200 OK.*X{32000}$/s,\n\t'fetch body parted');\nlike(http_get('/property?pr=bodyUsed'), qr/false$/s,\n\t'fetch bodyUsed false');\nlike(http_get('/property?pr=bodyUsed&readBody=1'), qr/true$/s,\n\t'fetch bodyUsed true');\nlike(http_get('/property?pr=ok'), qr/200 OK.*true$/s,\n\t'fetch ok true');\nlike(http_get('/property?pr=ok&code=401'), qr/200 OK.*false$/s,\n\t'fetch ok false');\nlike(http_get('/property?pr=redirected'), qr/200 OK.*false$/s,\n\t'fetch redirected false');\nlike(http_get('/property?pr=statusText'), qr/200 OK.*OK$/s,\n\t'fetch statusText OK');\nlike(http_get('/property?pr=statusText&code=403'), qr/200 OK.*Forbidden$/s,\n\t'fetch statusText Forbidden');\nlike(http_get('/property?pr=type'), qr/200 OK.*basic$/s,\n\t'fetch type');\nlike(http_get('/header?loc=duplicate_header&h=BAR'), qr/200 OK.*c$/s,\n\t'fetch header');\nlike(http_get('/header?loc=duplicate_header&h=BARR'), qr/200 OK.*null$/s,\n\t'fetch no header');\nlike(http_get('/header?loc=duplicate_header&h=foo'), qr/200 OK.*a,b$/s,\n\t'fetch header duplicate');\nlike(http_get('/header?loc=duplicate_header&h=BAR&method=getAll'),\n\tqr/200 OK.*\\['c']$/s, 'fetch getAll header');\nlike(http_get('/header?loc=duplicate_header&h=BARR&method=getAll'),\n\tqr/200 OK.*\\[]$/s, 'fetch getAll no header');\nlike(http_get('/header?loc=duplicate_header&h=FOO&method=getAll'),\n\tqr/200 OK.*\\['a','b']$/s, 'fetch getAll duplicate');\nlike(http_get('/header?loc=duplicate_header&h=bar&method=has'),\n\tqr/200 OK.*true$/s, 'fetch header has');\nlike(http_get('/header?loc=duplicate_header&h=buz&method=has'),\n\tqr/200 OK.*false$/s, 'fetch header does not have');\nlike(http_get('/header?loc=chunked/big&h=BAR&readBody=1'), qr/200 OK.*xxx$/s,\n\t'fetch chunked header');\nis(get_json('/multi'),\n\t'[{\"b\":\"aaa:GET:::\",\"c\":201,\"u\":\"http://127.0.0.1:'.$p0.'/loc\"},' .\n\t'{\"b\":\"bbb:POST:::OK\",\"c\":401,\"u\":\"http://127.0.0.1:'.$p0.'/loc\"},' .\n\t'{\"b\":\"ccc:PATCH::xxx:\",\"c\":200,\"u\":\"http://127.0.0.1:'.$p1.'/loc\"}]',\n\t'fetch multi');\nlike(http_get('/multi?throw=1'), qr/500/s, 'fetch destructor');\nis(get_json('/broken'),\n\t'[' .\n\t'\"connect failed\",' .\n\t'\"failed to convert url arg\",' .\n\t'\"invalid url\"]', 'fetch broken');\nis(get_json('/broken_response'),\n\t'[\"invalid fetch content length\",' .\n\t'\"invalid fetch header\",' .\n\t'\"invalid fetch status line\",' .\n\t'\"prematurely closed connection\",' .\n\t'\"prematurely closed connection\"]', 'fetch broken response');\nis(get_json('/chunked'),\n\t'[10,100010,25500,' .\n\t'\"invalid fetch chunked response\",' .\n\t'\"prematurely closed connection\",' .\n\t'\"very large fetch chunked response\"]', 'fetch chunked');\nlike(http_get('/chain'), qr/200 OK.*SUCCESS$/s, 'fetch chain');\n\nTODO: {\ntodo_skip 'leaves coredump', 1 unless $ENV{TEST_NGINX_UNSAFE}\n\tor http_get('/njs') =~ /^([.0-9]+)$/m && $1 ge '0.7.4';\n\nlike(http_get('/header_iter?loc=duplicate_header_large'),\n\tqr/\\['A:a','B:a','C:a','D:a','E:a','F:a','G:a','H:a','Foo:a,b']$/s,\n\t'fetch header duplicate large');\n\n}\n\nTODO: {\nlocal $TODO = 'not yet'\n\tunless http_get('/njs') =~ /^([.0-9]+)$/m && $1 ge '0.7.7';\n\nlike(http_get('/body_special?loc=no_content_length'),\n\tqr/200 OK.*CONTENT-BODY$/s, 'fetch body without content-length');\nlike(http_get('/body_special?loc=no_content_length/parted'),\n\tqr/200 OK.*X{32000}$/s, 'fetch body without content-length parted');\n\n}\n\n###############################################################################\n\nsub recode {\n\tmy $json;\n\teval { $json = JSON::PP::decode_json(shift) };\n\n\tif ($@) {\n\t\treturn \"<failed to parse JSON>\";\n\t}\n\n\tJSON::PP->new()->canonical()->encode($json);\n}\n\nsub get_json {\n\thttp_get(shift) =~ /\\x0d\\x0a?\\x0d\\x0a?(.*)/ms;\n\trecode($1);\n}\n\n###############################################################################\n\nsub http_daemon {\n\tmy $port = shift;\n\n\tmy $server = IO::Socket::INET->new(\n\t\tProto => 'tcp',\n\t\tLocalAddr => '127.0.0.1:' . $port,\n\t\tListen => 5,\n\t\tReuse => 1\n\t) or die \"Can't create listening socket: $!\\n\";\n\n\tlocal $SIG{PIPE} = 'IGNORE';\n\n\twhile (my $client = $server->accept()) {\n\t\t$client->autoflush(1);\n\n\t\tmy $headers = '';\n\t\tmy $uri = '';\n\n\t\twhile (<$client>) {\n\t\t\t$headers .= $_;\n\t\t\tlast if (/^\\x0d?\\x0a?$/);\n\t\t}\n\n\t\t$uri = $1 if $headers =~ /^\\S+\\s+([^ ]+)\\s+HTTP/i;\n\n\t\tif ($uri eq '/status_line') {\n\t\t\tprint $client\n\t\t\t\t\"HTTP/1.1 2A\";\n\n\t\t} elsif ($uri eq '/content_length') {\n\t\t\tprint $client\n\t\t\t\t\"HTTP/1.1 200 OK\" . CRLF .\n\t\t\t\t\"Content-Length: \" . CRLF .\n\t\t\t\t\"Connection: close\" . CRLF .\n\t\t\t\tCRLF;\n\n\t\t} elsif ($uri eq '/header') {\n\t\t\tprint $client\n\t\t\t\t\"HTTP/1.1 200 OK\" . CRLF .\n\t\t\t\t\"@#\" . CRLF .\n\t\t\t\t\"Connection: close\" . CRLF .\n\t\t\t\tCRLF;\n\n\t\t} elsif ($uri eq '/duplicate_header') {\n\t\t\tprint $client\n\t\t\t\t\"HTTP/1.1 200 OK\" . CRLF .\n\t\t\t\t\"Foo: a\" . CRLF .\n\t\t\t\t\"bar: c\" . CRLF .\n\t\t\t\t\"Foo: b\" . CRLF .\n\t\t\t\t\"Connection: close\" . CRLF .\n\t\t\t\tCRLF;\n\n\t\t} elsif ($uri eq '/duplicate_header_large') {\n\t\t\tprint $client\n\t\t\t\t\"HTTP/1.1 200 OK\" . CRLF .\n\t\t\t\t\"A: a\" . CRLF .\n\t\t\t\t\"B: a\" . CRLF .\n\t\t\t\t\"C: a\" . CRLF .\n\t\t\t\t\"D: a\" . CRLF .\n\t\t\t\t\"E: a\" . CRLF .\n\t\t\t\t\"F: a\" . CRLF .\n\t\t\t\t\"G: a\" . CRLF .\n\t\t\t\t\"H: a\" . CRLF .\n\t\t\t\t\"Foo: a\" . CRLF .\n\t\t\t\t\"Foo: b\" . CRLF .\n\t\t\t\t\"Connection: close\" . CRLF .\n\t\t\t\tCRLF;\n\n\t\t} elsif ($uri eq '/headers') {\n\t\t\tprint $client\n\t\t\t\t\"HTTP/1.1 200 OK\" . CRLF .\n\t\t\t\t\"Connection: close\" . CRLF;\n\n\t\t} elsif ($uri eq '/length') {\n\t\t\tprint $client\n\t\t\t\t\"HTTP/1.1 200 OK\" . CRLF .\n\t\t\t\t\"Content-Length: 100\" . CRLF .\n\t\t\t\t\"Connection: close\" . CRLF .\n\t\t\t\tCRLF .\n\t\t\t\t\"unfinished\" . CRLF;\n\n\t\t} elsif ($uri eq '/parted') {\n\t\t\tprint $client\n\t\t\t\t\"HTTP/1.1 200 OK\" . CRLF .\n\t\t\t\t\"Content-Length: 32000\" . CRLF .\n\t\t\t\t\"Connection: close\" . CRLF .\n\t\t\t\tCRLF;\n\n\t\t\tfor (1 .. 4) {\n\t\t\t\tselect undef, undef, undef, 0.01;\n\t\t\t\tprint $client \"X\" x 8000;\n\t\t\t}\n\n\t\t} elsif ($uri eq '/no_content_length') {\n\t\t\tprint $client\n\t\t\t\t\"HTTP/1.1 200 OK\" . CRLF .\n\t\t\t\t\"Connection: close\" . CRLF .\n\t\t\t\tCRLF .\n\t\t\t\t\"CONTENT-BODY\";\n\n\t\t} elsif ($uri eq '/no_content_length/parted') {\n\t\t\tprint $client\n\t\t\t\t\"HTTP/1.1 200 OK\" . CRLF .\n\t\t\t\t\"Connection: close\" . CRLF .\n\t\t\t\tCRLF;\n\n\t\t\tfor (1 .. 4) {\n\t\t\t\tselect undef, undef, undef, 0.01;\n\t\t\t\tprint $client \"X\" x 8000;\n\t\t\t}\n\n\t\t} elsif ($uri eq '/big') {\n\t\t\tprint $client\n\t\t\t\t\"HTTP/1.1 200 OK\" . CRLF .\n\t\t\t\t\"Content-Length: 100100\" . CRLF .\n\t\t\t\t\"Connection: close\" . CRLF .\n\t\t\t\tCRLF;\n\t\t\tfor (1 .. 1000) {\n\t\t\t\tprint $client (\"X\" x 98) . CRLF;\n\t\t\t}\n\t\t\tprint $client \"unfinished\" . CRLF;\n\n\t\t} elsif ($uri eq '/big/ok') {\n\t\t\tprint $client\n\t\t\t\t\"HTTP/1.1 200 OK\" . CRLF .\n\t\t\t\t\"Content-Length: 100010\" . CRLF .\n\t\t\t\t\"Connection: close\" . CRLF .\n\t\t\t\tCRLF;\n\t\t\tfor (1 .. 1000) {\n\t\t\t\tprint $client (\"X\" x 98) . CRLF;\n\t\t\t}\n\t\t\tprint $client \"finished\" . CRLF;\n\n\t\t} elsif ($uri eq '/chunked') {\n\t\t\tprint $client\n\t\t\t\t\"HTTP/1.1 200 OK\" . CRLF .\n\t\t\t\t\"Transfer-Encoding: chunked\" . CRLF .\n\t\t\t\t\"Connection: close\" . CRLF .\n\t\t\t\tCRLF .\n\t\t\t\t\"ff\" . CRLF .\n\t\t\t\t\"unfinished\" . CRLF;\n\n\t\t} elsif ($uri eq '/chunked/ok') {\n\t\t\tprint $client\n\t\t\t\t\"HTTP/1.1 200 OK\" . CRLF .\n\t\t\t\t\"Transfer-Encoding: chunked\" . CRLF .\n\t\t\t\t\"Connection: close\" . CRLF .\n\t\t\t\tCRLF .\n\t\t\t\t\"a\" . CRLF .\n\t\t\t\t\"finished\" . CRLF .\n\t\t\t\tCRLF . \"0\" . CRLF . CRLF;\n\t\t} elsif ($uri eq '/chunked/big') {\n\t\t\tprint $client\n\t\t\t\t\"HTTP/1.1 200 OK\" . CRLF .\n\t\t\t\t\"Transfer-Encoding: chunked\" . CRLF .\n\t\t\t\t\"Bar: xxx\" . CRLF .\n\t\t\t\t\"Connection: close\" . CRLF .\n\t\t\t\tCRLF;\n\n\t\t\tfor (1 .. 100) {\n\t\t\t\tprint $client \"ff\" . CRLF . (\"X\" x 255) . CRLF;\n\t\t\t}\n\n\t\t    print $client  \"0\" . CRLF . CRLF;\n\t\t}\n\t}\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/js_fetch_https.t",
    "content": "#!/usr/bin/perl\n\n# (C) Antoine Bonavita\n# (C) Nginx, Inc.\n\n# Tests for http njs module, fetch method, https support.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http http_ssl rewrite/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    js_import test.js;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        resolver   127.0.0.1:%%PORT_8981_UDP%%;\n        resolver_timeout 1s;\n\n        location /njs {\n            js_content test.njs;\n        }\n\n        location /https {\n            js_content test.https;\n        }\n\n        location /https.myca {\n            js_content test.https;\n\n            js_fetch_ciphers HIGH:!aNull:!MD5;\n            js_fetch_protocols TLSv1.1 TLSv1.2;\n            js_fetch_trusted_certificate myca.crt;\n        }\n\n        location /https.myca.short {\n            js_content test.https;\n\n            js_fetch_verify_depth 0;\n            js_fetch_trusted_certificate myca.crt;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081 ssl default;\n        server_name  default.example.com;\n\n        ssl_certificate default.example.com.chained.crt;\n        ssl_certificate_key default.example.com.key;\n\n        location /loc {\n            return 200 \"You are at default.example.com.\";\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081 ssl;\n        server_name  1.example.com;\n\n        ssl_certificate 1.example.com.chained.crt;\n        ssl_certificate_key 1.example.com.key;\n\n        location /loc {\n            return 200 \"You are at 1.example.com.\";\n        }\n    }\n}\n\nEOF\n\nmy $p1 = port(8081);\n\n$t->write_file('test.js', <<EOF);\n    function test_njs(r) {\n        r.return(200, njs.version);\n    }\n\n    function https(r) {\n        var url = `https://\\${r.args.domain}:$p1/loc`;\n        var opt = {};\n\n        if (r.args.verify != null && r.args.verify == \"false\") {\n            opt.verify = false;\n        }\n\n        ngx.fetch(url, opt)\n        .then(reply => reply.text())\n        .then(body => r.return(200, body))\n        .catch(e => r.return(501, e.message))\n    }\n\n    export default {njs: test_njs, https};\nEOF\n\nmy $d = $t->testdir();\n\n$t->write_file('openssl.conf', <<EOF);\n[ req ]\ndefault_bits = 2048\nencrypt_key = no\ndistinguished_name = req_distinguished_name\n[ req_distinguished_name ]\nEOF\n\n$t->write_file('myca.conf', <<EOF);\n[ ca ]\ndefault_ca = myca\n\n[ myca ]\nnew_certs_dir = $d\ndatabase = $d/certindex\ndefault_md = sha256\npolicy = myca_policy\nserial = $d/certserial\ndefault_days = 1\nx509_extensions = myca_extensions\n\n[ myca_policy ]\ncommonName = supplied\n\n[ myca_extensions ]\nbasicConstraints = critical,CA:TRUE\nEOF\n\nsystem('openssl req -x509 -new '\n\t. \"-config $d/openssl.conf -subj /CN=myca/ \"\n\t. \"-out $d/myca.crt -keyout $d/myca.key \"\n\t. \">>$d/openssl.out 2>&1\") == 0\n\tor die \"Can't create self-signed certificate for CA: $!\\n\";\n\nforeach my $name ('intermediate', 'default.example.com', '1.example.com') {\n\tsystem(\"openssl req -new \"\n\t\t. \"-config $d/openssl.conf -subj /CN=$name/ \"\n\t\t. \"-out $d/$name.csr -keyout $d/$name.key \"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create certificate signing req for $name: $!\\n\";\n}\n\n$t->write_file('certserial', '1000');\n$t->write_file('certindex', '');\n\nsystem(\"openssl ca -batch -config $d/myca.conf \"\n\t. \"-keyfile $d/myca.key -cert $d/myca.crt \"\n\t. \"-subj /CN=intermediate/ -in $d/intermediate.csr \"\n\t. \"-out $d/intermediate.crt \"\n\t. \">>$d/openssl.out 2>&1\") == 0\n\tor die \"Can't sign certificate for intermediate: $!\\n\";\n\nforeach my $name ('default.example.com', '1.example.com') {\n\tsystem(\"openssl ca -batch -config $d/myca.conf \"\n\t\t. \"-keyfile $d/intermediate.key -cert $d/intermediate.crt \"\n\t\t. \"-subj /CN=$name/ -in $d/$name.csr -out $d/$name.crt \"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't sign certificate for $name $!\\n\";\n\t$t->write_file(\"$name.chained.crt\", $t->read_file(\"$name.crt\")\n\t\t. $t->read_file('intermediate.crt'));\n}\n\n$t->try_run('no njs.fetch')->plan(7);\n\n$t->run_daemon(\\&dns_daemon, port(8981), $t);\n$t->waitforfile($t->testdir . '/' . port(8981));\n\n###############################################################################\n\nlocal $TODO = 'not yet'\n\tunless http_get('/njs') =~ /^([.0-9]+)$/m && $1 ge '0.7.0';\n\nlike(http_get('/https?domain=default.example.com&verify=false'),\n\tqr/You are at default.example.com.$/s, 'fetch https');\nlike(http_get('/https?domain=127.0.0.1&verify=false'),\n\tqr/You are at default.example.com.$/s, 'fetch https by IP');\nlike(http_get('/https?domain=1.example.com&verify=false'),\n\tqr/You are at 1.example.com.$/s, 'fetch tls extension');\nlike(http_get('/https.myca?domain=default.example.com'),\n\tqr/You are at default.example.com.$/s, 'fetch https trusted certificate');\nlike(http_get('/https.myca?domain=localhost'),\n\tqr/connect failed/s, 'fetch https wrong CN certificate');\nlike(http_get('/https?domain=default.example.com'),\n\tqr/connect failed/s, 'fetch https non trusted CA');\nlike(http_get('/https.myca.short?domain=default.example.com'),\n\tqr/connect failed/s, 'fetch https CA too far');\n\n###############################################################################\n\nsub reply_handler {\n\tmy ($recv_data, $port, %extra) = @_;\n\n\tmy (@name, @rdata);\n\n\tuse constant NOERROR\t=> 0;\n\tuse constant A\t\t=> 1;\n\tuse constant IN\t\t=> 1;\n\n\t# default values\n\n\tmy ($hdr, $rcode, $ttl) = (0x8180, NOERROR, 3600);\n\n\t# decode name\n\n\tmy ($len, $offset) = (undef, 12);\n\twhile (1) {\n\t\t$len = unpack(\"\\@$offset C\", $recv_data);\n\t\tlast if $len == 0;\n\t\t$offset++;\n\t\tpush @name, unpack(\"\\@$offset A$len\", $recv_data);\n\t\t$offset += $len;\n\t}\n\n\t$offset -= 1;\n\tmy ($id, $type, $class) = unpack(\"n x$offset n2\", $recv_data);\n\n\tmy $name = join('.', @name);\n\n\tif ($type == A) {\n\t\tpush @rdata, rd_addr($ttl, '127.0.0.1');\n\t}\n\n\t$len = @name;\n\tpack(\"n6 (C/a*)$len x n2\", $id, $hdr | $rcode, 1, scalar @rdata,\n\t\t0, 0, @name, $type, $class) . join('', @rdata);\n}\n\nsub rd_addr {\n\tmy ($ttl, $addr) = @_;\n\n\tmy $code = 'split(/\\./, $addr)';\n\n\treturn pack 'n3N', 0xc00c, A, IN, $ttl if $addr eq '';\n\n\tpack 'n3N nC4', 0xc00c, A, IN, $ttl, eval \"scalar $code\", eval($code);\n}\n\nsub dns_daemon {\n\tmy ($port, $t) = @_;\n\n\tmy ($data, $recv_data);\n\tmy $socket = IO::Socket::INET->new(\n\t\tLocalAddr    => '127.0.0.1',\n\t\tLocalPort    => $port,\n\t\tProto        => 'udp',\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\tlocal $SIG{PIPE} = 'IGNORE';\n\n\t# signal we are ready\n\n\topen my $fh, '>', $t->testdir() . '/' . $port;\n\tclose $fh;\n\n\twhile (1) {\n\t\t$socket->recv($recv_data, 65536);\n\t\t$data = reply_handler($recv_data, $port);\n\t\t$socket->send($data);\n\t}\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/js_fetch_resolver.t",
    "content": "#!/usr/bin/perl\n\n# (C) Dmitry Volyntsev\n# (C) Nginx, Inc.\n\n# Tests for http njs module, fetch method, dns support.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse IO::Select;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nplan(skip_all => '127.0.0.2 local address required')\n\tunless defined IO::Socket::INET->new( LocalAddr => '127.0.0.2' );\n\nmy $t = Test::Nginx->new()->has(qw/http/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    js_import test.js;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location /njs {\n            js_content test.njs;\n        }\n\n        location /dns {\n            js_content test.dns;\n\n            resolver   127.0.0.1:%%PORT_8981_UDP%%;\n            resolver_timeout 1s;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  aaa;\n\n        location /loc {\n            js_content test.loc;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  many;\n\n        location /loc {\n            js_content test.loc;\n        }\n    }\n}\n\nEOF\n\nmy $p0 = port(8080);\n\n$t->write_file('test.js', <<EOF);\n    function test_njs(r) {\n        r.return(200, njs.version);\n    }\n\n    function dns(r) {\n        var url = `http://\\${r.args.domain}:$p0/loc`;\n\n        ngx.fetch(url)\n        .then(reply => reply.text())\n        .then(body => r.return(200, body))\n        .catch(e => r.return(501, e.message))\n    }\n\n    function str(v) { return v ? v : ''};\n\n    function loc(r) {\n        var v = r.variables;\n        var body = str(r.requestText);\n        var foo = str(r.headersIn.foo);\n        var bar = str(r.headersIn.bar);\n        var c = r.headersIn.code ? Number(r.headersIn.code) : 200;\n        r.return(c, `\\${v.host}:\\${v.request_method}:\\${foo}:\\${bar}:\\${body}`);\n    }\n\n     export default {njs: test_njs, dns, loc};\nEOF\n\n$t->try_run('no njs.fetch')->plan(3);\n\n$t->run_daemon(\\&dns_daemon, port(8981), $t);\n$t->waitforfile($t->testdir . '/' . port(8981));\n\n###############################################################################\n\nlike(http_get('/dns?domain=aaa'), qr/aaa:GET:::$/s, 'fetch dns aaa');\nlike(http_get('/dns?domain=many'), qr/many:GET:::$/s, 'fetch dns many');\nlike(http_get('/dns?domain=unknown'), qr/\"unknown\" could not be resolved/s,\n\t'fetch dns unknown');\n\n###############################################################################\n\nsub reply_handler {\n\tmy ($recv_data, $port, %extra) = @_;\n\n\tmy (@name, @rdata);\n\n\tuse constant NOERROR\t=> 0;\n\tuse constant FORMERR\t=> 1;\n\tuse constant SERVFAIL\t=> 2;\n\tuse constant NXDOMAIN\t=> 3;\n\n\tuse constant A\t\t=> 1;\n\n\tuse constant IN\t\t=> 1;\n\n\t# default values\n\n\tmy ($hdr, $rcode, $ttl) = (0x8180, NOERROR, 3600);\n\n\t# decode name\n\n\tmy ($len, $offset) = (undef, 12);\n\twhile (1) {\n\t\t$len = unpack(\"\\@$offset C\", $recv_data);\n\t\tlast if $len == 0;\n\t\t$offset++;\n\t\tpush @name, unpack(\"\\@$offset A$len\", $recv_data);\n\t\t$offset += $len;\n\t}\n\n\t$offset -= 1;\n\tmy ($id, $type, $class) = unpack(\"n x$offset n2\", $recv_data);\n\n\tmy $name = join('.', @name);\n\n\tif ($name eq 'aaa' && $type == A) {\n\t\tpush @rdata, rd_addr($ttl, '127.0.0.1');\n\n\t} elsif ($name eq 'many' && $type == A) {\n\t\tpush @rdata, rd_addr($ttl, '127.0.0.2');\n\t\tpush @rdata, rd_addr($ttl, '127.0.0.1');\n\t}\n\n\t$len = @name;\n\tpack(\"n6 (C/a*)$len x n2\", $id, $hdr | $rcode, 1, scalar @rdata,\n\t\t0, 0, @name, $type, $class) . join('', @rdata);\n}\n\nsub rd_addr {\n\tmy ($ttl, $addr) = @_;\n\n\tmy $code = 'split(/\\./, $addr)';\n\n\treturn pack 'n3N', 0xc00c, A, IN, $ttl if $addr eq '';\n\n\tpack 'n3N nC4', 0xc00c, A, IN, $ttl, eval \"scalar $code\", eval($code);\n}\n\nsub dns_daemon {\n\tmy ($port, $t, %extra) = @_;\n\n\tmy ($data, $recv_data);\n\tmy $socket = IO::Socket::INET->new(\n\t\tLocalAddr => '127.0.0.1',\n\t\tLocalPort => $port,\n\t\tProto => 'udp',\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\tmy $sel = IO::Select->new($socket);\n\n\tlocal $SIG{PIPE} = 'IGNORE';\n\n\t# signal we are ready\n\n\topen my $fh, '>', $t->testdir() . '/' . $port;\n\tclose $fh;\n\n\twhile (my @ready = $sel->can_read) {\n\t\tforeach my $fh (@ready) {\n\t\t\tif ($socket == $fh) {\n\t\t\t\t$fh->recv($recv_data, 65536);\n\t\t\t\t$data = reply_handler($recv_data, $port);\n\t\t\t\t$fh->send($data);\n\n\t\t\t} else {\n\t\t\t\t$fh->recv($recv_data, 65536);\n\t\t\t\tunless (length $recv_data) {\n\t\t\t\t\t$sel->remove($fh);\n\t\t\t\t\t$fh->close;\n\t\t\t\t\tnext;\n\t\t\t\t}\n\nagain:\n\t\t\t\tmy $len = unpack(\"n\", $recv_data);\n\t\t\t\t$data = substr $recv_data, 2, $len;\n\t\t\t\t$data = reply_handler($data, $port, tcp => 1);\n\t\t\t\t$data = pack(\"n\", length $data) . $data;\n\t\t\t\t$fh->send($data);\n\t\t\t\t$recv_data = substr $recv_data, 2 + $len;\n\t\t\t\tgoto again if length $recv_data;\n\t\t\t}\n\t\t}\n\t}\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/js_fetch_timeout.t",
    "content": "#!/usr/bin/perl\n\n# (C) Dmitry Volyntsev\n# (C) Nginx, Inc.\n\n# Tests for http njs module, fetch method timeout.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse Socket qw/ CRLF /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    js_import test.js;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location /njs {\n            js_content test.njs;\n        }\n\n        location /normal_timeout {\n            js_content test.timeout_test;\n        }\n\n        location /short_timeout {\n            js_fetch_timeout 200ms;\n            js_content test.timeout_test;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        location /normal_reply {\n            js_content test.normal_reply;\n        }\n\n        location /delayed_reply {\n            js_content test.delayed_reply;\n        }\n    }\n}\n\nEOF\n\nmy $p1 = port(8081);\n\n$t->write_file('test.js', <<EOF);\n    function test_njs(r) {\n        r.return(200, njs.version);\n    }\n\n    async function timeout_test(r) {\n        let rs = await Promise.allSettled([\n            'http://127.0.0.1:$p1/normal_reply',\n            'http://127.0.0.1:$p1/delayed_reply',\n        ].map(v => ngx.fetch(v)));\n\n        let bs = rs.map(v => ({s: v.status, v: v.value ? v.value.headers.X\n                                                       : v.reason}));\n\n        r.return(200, njs.dump(bs));\n    }\n\n    function normal_reply(r) {\n        r.headersOut.X = 'N';\n        r.return(200);\n    }\n\n    function delayed_reply(r) {\n        r.headersOut.X = 'D';\n        setTimeout((r) => { r.return(200); }, 250, r, 0);\n    }\n\n     export default {njs: test_njs, timeout_test, normal_reply, delayed_reply};\nEOF\n\n$t->try_run('no js_fetch_timeout')->plan(2);\n\n###############################################################################\n\nlike(http_get('/normal_timeout'),\n\tqr/\\[\\{s:'fulfilled',v:'N'},\\{s:'fulfilled',v:'D'}]$/s,\n\t'normal timeout');\nlike(http_get('/short_timeout'),\n\tqr/\\[\\{s:'fulfilled',v:'N'},\\{s:'rejected',v:Error: read timed out}]$/s,\n\t'short timeout');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/js_fetch_verify.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for http njs module, fetch method, backend certificate verification.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http http_ssl/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    js_import test.js;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        resolver   127.0.0.1:%%PORT_8981_UDP%%;\n        resolver_timeout 1s;\n\n        location /njs {\n            js_content test.njs;\n        }\n\n        location /https {\n            js_content test.https;\n        }\n\n        location /https.verify_off {\n            js_content test.https;\n            js_fetch_verify off;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081 ssl;\n        server_name  localhost;\n\n        ssl_certificate localhost.crt;\n        ssl_certificate_key localhost.key;\n    }\n}\n\nEOF\n\nmy $p1 = port(8081);\n\n$t->write_file('test.js', <<EOF);\n    function test_njs(r) {\n        r.return(200, njs.version);\n    }\n\n    function https(r) {\n        ngx.fetch(`https://example.com:$p1/loc`)\n        .then(reply => reply.text())\n        .then(body => r.return(200, body))\n        .catch(e => r.return(501, e.message));\n    }\n\n    export default {njs: test_njs, https};\nEOF\n\n$t->write_file('openssl.conf', <<EOF);\n[ req ]\ndefault_bits = 2048\nencrypt_key = no\ndistinguished_name = req_distinguished_name\n[ req_distinguished_name ]\nEOF\n\nmy $d = $t->testdir();\n\nforeach my $name ('localhost') {\n\tsystem('openssl req -x509 -new '\n\t\t. \"-config $d/openssl.conf -subj /CN=$name/ \"\n\t\t. \"-out $d/$name.crt -keyout $d/$name.key \"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create certificate for $name: $!\\n\";\n}\n\n$t->try_run('no js_fetch_verify')->plan(2);\n\n$t->run_daemon(\\&dns_daemon, port(8981), $t);\n$t->waitforfile($t->testdir . '/' . port(8981));\n\n###############################################################################\n\nlike(http_get('/https'), qr/connect failed/, 'fetch verify error');\nlike(http_get('/https.verify_off'), qr/200 OK/, 'fetch verify off');\n\n###############################################################################\n\nsub reply_handler {\n\tmy ($recv_data, $port, %extra) = @_;\n\n\tmy (@name, @rdata);\n\n\tuse constant NOERROR\t=> 0;\n\tuse constant A\t\t=> 1;\n\tuse constant IN\t\t=> 1;\n\n\t# default values\n\n\tmy ($hdr, $rcode, $ttl) = (0x8180, NOERROR, 3600);\n\n\t# decode name\n\n\tmy ($len, $offset) = (undef, 12);\n\twhile (1) {\n\t\t$len = unpack(\"\\@$offset C\", $recv_data);\n\t\tlast if $len == 0;\n\t\t$offset++;\n\t\tpush @name, unpack(\"\\@$offset A$len\", $recv_data);\n\t\t$offset += $len;\n\t}\n\n\t$offset -= 1;\n\tmy ($id, $type, $class) = unpack(\"n x$offset n2\", $recv_data);\n\n\tmy $name = join('.', @name);\n\n\tif ($type == A) {\n\t\tpush @rdata, rd_addr($ttl, '127.0.0.1');\n\t}\n\n\t$len = @name;\n\tpack(\"n6 (C/a*)$len x n2\", $id, $hdr | $rcode, 1, scalar @rdata,\n\t\t0, 0, @name, $type, $class) . join('', @rdata);\n}\n\nsub rd_addr {\n\tmy ($ttl, $addr) = @_;\n\n\tmy $code = 'split(/\\./, $addr)';\n\n\treturn pack 'n3N', 0xc00c, A, IN, $ttl if $addr eq '';\n\n\tpack 'n3N nC4', 0xc00c, A, IN, $ttl, eval \"scalar $code\", eval($code);\n}\n\nsub dns_daemon {\n\tmy ($port, $t) = @_;\n\n\tmy ($data, $recv_data);\n\tmy $socket = IO::Socket::INET->new(\n\t\tLocalAddr    => '127.0.0.1',\n\t\tLocalPort    => $port,\n\t\tProto        => 'udp',\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\tlocal $SIG{PIPE} = 'IGNORE';\n\n\t# signal we are ready\n\n\topen my $fh, '>', $t->testdir() . '/' . $port;\n\tclose $fh;\n\n\twhile (1) {\n\t\t$socket->recv($recv_data, 65536);\n\t\t$data = reply_handler($recv_data, $port);\n\t\t$socket->send($data);\n\t}\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/js_header_filter.t",
    "content": "#!/usr/bin/perl\n\n# (C) Dmitry Volyntsev\n# (C) Nginx, Inc.\n\n# Tests for http njs module, header filter.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy rewrite/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    js_import test.js;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location /njs {\n            js_content test.njs;\n        }\n\n        location /filter/ {\n            js_header_filter test.filter;\n            proxy_pass http://127.0.0.1:8081/;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        location / {\n            add_header Set-Cookie \"BB\";\n            add_header Set-Cookie \"CCCC\";\n\n            return 200;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('test.js', <<EOF);\n    function test_njs(r) {\n        r.return(200, njs.version);\n    }\n\n    function filter(r) {\n        var cookies = r.headersOut['Set-Cookie'];\n        var len = r.args.len ? Number(r.args.len) : 0;\n        r.headersOut['Set-Cookie'] = cookies.filter(v=>v.length > len);\n    }\n\n    export default {njs: test_njs, filter};\n\nEOF\n\n$t->try_run('no njs header filter')->plan(2);\n\n###############################################################################\n\nlike(http_get('/filter/?len=1'), qr/Set-Cookie: BB.*Set-Cookie: CCCC.*/ms,\n\t'all');;\nunlike(http_get('/filter/?len=3'), qr/Set-Cookie: BB/,\n\t'filter');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/js_header_filter_if.t",
    "content": "#!/usr/bin/perl\n\n# (C) Dmitry Volyntsev\n# (C) Nginx, Inc.\n\n# Tests for http njs module, header filter, if context.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy rewrite/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    js_import test.js;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location /njs {\n            js_content test.njs;\n        }\n\n        location / {\n            if ($arg_name ~ \"add\") {\n                js_header_filter test.add;\n            }\n\n            js_header_filter test.add2;\n\n            proxy_pass http://127.0.0.1:8081/;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        location / {\n            return 200;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('test.js', <<EOF);\n    function test_njs(r) {\n        r.return(200, njs.version);\n    }\n\n    function add(r) {\n        r.headersOut['Foo'] = 'bar';\n    }\n\n    function add2(r) {\n        r.headersOut['Bar'] = 'xxx';\n    }\n\n    export default {njs: test_njs, add, add2};\n\nEOF\n\n$t->try_run('no njs header filter')->plan(2);\n\n###############################################################################\n\nlike(http_get('/?name=add'), qr/Foo: bar/, 'header filter if');\nlike(http_get('/'), qr/Bar: xxx/, 'header filter');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/js_headers.t",
    "content": "#!/usr/bin/perl\n\n# (C) Dmitry Volyntsev\n# (C) Nginx, Inc.\n\n# Tests for http njs module, working with headers.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse Socket qw/ CRLF /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http charset/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    js_set $test_foo_in   test.foo_in;\n    js_set $test_ifoo_in  test.ifoo_in;\n\n    js_import test.js;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location /njs {\n            js_content test.njs;\n        }\n\n        location /content_length {\n            js_content test.content_length;\n        }\n\n        location /content_length_arr {\n            js_content test.content_length_arr;\n        }\n\n        location /content_length_keys {\n            js_content test.content_length_keys;\n        }\n\n        location /content_type {\n            charset windows-1251;\n\n            default_type text/plain;\n            js_content test.content_type;\n        }\n\n        location /content_type_arr {\n            charset windows-1251;\n\n            default_type text/plain;\n            js_content test.content_type_arr;\n        }\n\n        location /content_encoding {\n            js_content test.content_encoding;\n        }\n\n        location /content_encoding_arr {\n            js_content test.content_encoding_arr;\n        }\n\n        location /headers_list {\n            js_content test.headers_list;\n        }\n\n        location /foo_in {\n            return 200 $test_foo_in;\n        }\n\n        location /ifoo_in {\n            return 200 $test_ifoo_in;\n        }\n\n        location /hdr_in {\n            js_content test.hdr_in;\n        }\n\n        location /raw_hdr_in {\n            js_content test.raw_hdr_in;\n        }\n\n        location /hdr_out {\n            js_content test.hdr_out;\n        }\n\n        location /raw_hdr_out {\n            js_content test.raw_hdr_out;\n        }\n\n        location /hdr_out_array {\n            js_content test.hdr_out_array;\n        }\n\n        location /hdr_out_set_cookie {\n            js_content test.hdr_out_set_cookie;\n        }\n\n        location /hdr_out_single {\n            js_content test.hdr_out_single;\n        }\n\n        location /ihdr_out {\n            js_content test.ihdr_out;\n        }\n\n        location /hdr_sorted_keys {\n            js_content test.hdr_sorted_keys;\n        }\n\n        location /hdr_out_special_set {\n            js_content test.hdr_out_special_set;\n        }\n\n        location /copy_subrequest_hdrs {\n            js_content test.copy_subrequest_hdrs;\n        }\n\n        location = /subrequest {\n            internal;\n\n            js_content test.subrequest;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('test.js', <<EOF);\n    function test_njs(r) {\n        r.return(200, njs.version);\n    }\n\n    function content_length(r) {\n        if (njs.version_number >= 0x000705) {\n            var clength = r.headersOut['Content-Length'];\n            if (clength !== undefined) {\n                r.return(500, `Content-Length \"\\${clength}\" is not empty`);\n                return;\n            }\n        }\n\n        delete r.headersOut['Content-Length'];\n        r.headersOut['Content-Length'] = '';\n        r.headersOut['Content-Length'] = 3;\n        delete r.headersOut['Content-Length'];\n        r.headersOut['Content-Length'] = 3;\n        r.sendHeader();\n        r.send('XXX');\n        r.finish();\n    }\n\n    function content_length_arr(r) {\n        r.headersOut['Content-Length'] = [5];\n        r.headersOut['Content-Length'] = [];\n        r.headersOut['Content-Length'] = [4,3];\n        r.sendHeader();\n        r.send('XXX');\n        r.finish();\n    }\n\n    function content_length_keys(r) {\n        r.headersOut['Content-Length'] = 3;\n        var in_keys = Object.keys(r.headersOut).some(v=>v=='Content-Length');\n        r.return(200, `B:\\${in_keys}`);\n    }\n\n    function content_type(r) {\n        if (njs.version_number >= 0x000705) {\n            var ctype = r.headersOut['Content-Type'];\n            if (ctype !== undefined) {\n                r.return(500, `Content-Type \"\\${ctype}\" is not empty`);\n                return;\n            }\n        }\n\n        delete r.headersOut['Content-Type'];\n        r.headersOut['Content-Type'] = 'text/xml';\n        r.headersOut['Content-Type'] = '';\n        r.headersOut['Content-Type'] = 'text/xml; charset=';\n        delete r.headersOut['Content-Type'];\n        r.headersOut['Content-Type'] = 'text/xml; charset=utf-8';\n        r.headersOut['Content-Type'] = 'text/xml; charset=\"utf-8\"';\n        var in_keys = Object.keys(r.headersOut).some(v=>v=='Content-Type');\n        r.return(200, `B:\\${in_keys}`);\n    }\n\n    function content_type_arr(r) {\n        r.headersOut['Content-Type'] = ['text/html'];\n        r.headersOut['Content-Type'] = [];\n        r.headersOut['Content-Type'] = [ 'text/xml', 'text/html'];\n        r.return(200);\n    }\n\n    function content_encoding(r) {\n        if (njs.version_number >= 0x000705) {\n            var ce = r.headersOut['Content-Encoding'];\n            if (ce !== undefined) {\n                r.return(500, `Content-Encoding \"\\${ce}\" is not empty`);\n                return;\n            }\n        }\n\n        delete r.headersOut['Content-Encoding'];\n        r.headersOut['Content-Encoding'] = '';\n        r.headersOut['Content-Encoding'] = 'test';\n        delete r.headersOut['Content-Encoding'];\n        r.headersOut['Content-Encoding'] = 'gzip';\n        r.return(200);\n    }\n\n    function content_encoding_arr(r) {\n        r.headersOut['Content-Encoding'] = 'test';\n        r.headersOut['Content-Encoding'] = [];\n        r.headersOut['Content-Encoding'] = ['test', 'gzip'];\n        r.return(200);\n    }\n\n    function headers_list(r) {\n        for (var h in {a:1, b:2, c:3}) {\n            r.headersOut[h] = h;\n        }\n\n        delete r.headersOut.b;\n        r.headersOut.d = 'd';\n\n        var out = \"\";\n        for (var h in r.headersOut) {\n            out += h + \":\";\n        }\n\n        r.return(200, out);\n    }\n\n    function hdr_in(r) {\n        var s = '', h;\n        for (h in r.headersIn) {\n            s += `\\${h.toLowerCase()}: \\${r.headersIn[h]}\\n`;\n        }\n\n        r.return(200, s);\n    }\n\n    function raw_hdr_in(r) {\n        var filtered = r.rawHeadersIn\n                       .filter(v=>v[0].toLowerCase() == r.args.filter);\n        r.return(200, 'raw:' + filtered.map(v=>v[1]).join('|'));\n    }\n\n    function hdr_sorted_keys(r) {\n        var s = '';\n        var hdr = r.args.in ? r.headersIn : r.headersOut;\n\n        if (!r.args.in) {\n            r.headersOut.b = 'b';\n            r.headersOut.c = 'c';\n            r.headersOut.a = 'a';\n        }\n\n        r.return(200, Object.keys(hdr).sort());\n    }\n\n    function foo_in(r) {\n        return 'hdr=' + r.headersIn.foo;\n    }\n\n    function ifoo_in(r) {\n        var s = '', h;\n        for (h in r.headersIn) {\n            if (h.substr(0, 3) == 'foo') {\n                s += r.headersIn[h];\n            }\n        }\n        return s;\n    }\n\n    function hdr_out(r) {\n        r.status = 200;\n        r.headersOut['Foo'] = r.args.foo;\n\n        if (r.args.bar) {\n            r.headersOut['Bar'] =\n                r.headersOut[(r.args.bar == 'empty' ? 'Baz' :'Foo')]\n        }\n\n        r.sendHeader();\n        r.finish();\n    }\n\n    function raw_hdr_out(r) {\n        r.headersOut.a = ['foo', 'bar'];\n        r.headersOut.b = 'b';\n\n        var filtered = r.rawHeadersOut\n                       .filter(v=>v[0].toLowerCase() == r.args.filter);\n        r.return(200, 'raw:' + filtered.map(v=>v[1]).join('|'));\n    }\n\n    function hdr_out_array(r) {\n        if (!r.args.hidden) {\n            r.headersOut['Foo'] = [r.args.foo];\n            r.headersOut['Foo'] = [];\n            r.headersOut['Foo'] = ['bar', r.args.foo];\n        }\n\n        if (r.args.scalar_set) {\n            r.headersOut['Foo'] = 'xxx';\n        }\n\n        r.return(200, `B:\\${njs.dump(r.headersOut.foo)}`);\n    }\n\n    function hdr_out_single(r) {\n        r.headersOut.ETag = ['a', 'b'];\n        r.return(200, `B:\\${njs.dump(r.headersOut.etag)}`);\n    }\n\n    function hdr_out_set_cookie(r) {\n        r.headersOut['Set-Cookie'] = [];\n        r.headersOut['Set-Cookie'] = ['a', 'b'];\n        delete r.headersOut['Set-Cookie'];\n        r.headersOut['Set-Cookie'] = 'e';\n        r.headersOut['Set-Cookie'] = ['c', '', null, 'd', 'f'];\n\n        r.return(200, `B:\\${njs.dump(r.headersOut['Set-Cookie'])}`);\n    }\n\n    function ihdr_out(r) {\n        r.status = 200;\n        r.headersOut['a'] = r.args.a;\n        r.headersOut['b'] = r.args.b;\n\n        var s = '', h;\n        for (h in r.headersOut) {\n            s += r.headersOut[h];\n        }\n\n        r.sendHeader();\n        r.send(s);\n        r.finish();\n    }\n\n    function hdr_out_special_set(r) {\n        r.headersOut['Foo'] = \"xxx\";\n        r.headersOut['Content-Encoding'] = 'abc';\n\n        let ce = r.headersOut['Content-Encoding'];\n        r.return(200, `CE: \\${ce}`);\n    }\n\n    async function copy_subrequest_hdrs(r) {\n        let resp = await r.subrequest(\"/subrequest\");\n\n        for (const h in resp.headersOut) {\n            r.headersOut[h] = resp.headersOut[h];\n        }\n\n        r.return(200, resp.responseText);\n    }\n\n    function subrequest(r) {\n        r.headersOut['A'] = 'a';\n        r.headersOut['Content-Encoding'] = 'ce';\n        r.headersOut['B'] = 'b';\n        r.headersOut['C'] = 'c';\n        r.headersOut['D'] = 'd';\n        r.headersOut['Set-Cookie'] = ['A', 'BB'];\n        r.headersOut['Content-Length'] = 3;\n        r.headersOut['Content-Type'] = 'ct';\n        r.sendHeader();\n        r.send('XXX');\n        r.finish();\n    }\n\n    export default {njs:test_njs, content_length, content_length_arr,\n                    content_length_keys, content_type, content_type_arr,\n                    content_encoding, content_encoding_arr, headers_list,\n                    hdr_in, raw_hdr_in, hdr_sorted_keys, foo_in, ifoo_in,\n                    hdr_out, raw_hdr_out, hdr_out_array, hdr_out_single,\n                    hdr_out_set_cookie, ihdr_out, hdr_out_special_set,\n                    copy_subrequest_hdrs, subrequest};\n\n\nEOF\n\n$t->try_run('no njs')->plan(42);\n\n###############################################################################\n\nlike(http_get('/content_length'), qr/Content-Length: 3/,\n\t'set Content-Length');\nlike(http_get('/content_type'), qr/Content-Type: text\\/xml; charset=\"utf-8\"\\r/,\n\t'set Content-Type');\nunlike(http_get('/content_type'), qr/Content-Type: text\\/plain/,\n\t'set Content-Type 2');\nlike(http_get('/content_encoding'), qr/Content-Encoding: gzip/,\n\t'set Content-Encoding');\nlike(http_get('/headers_list'), qr/a:c:d/, 'headers list');\n\nlike(http_get('/ihdr_out?a=12&b=34'), qr/^1234$/m, 'r.headersOut iteration');\nlike(http_get('/ihdr_out'), qr/\\x0d\\x0a?\\x0d\\x0a?$/m, 'r.send zero');\nlike(http_get('/hdr_out?foo=12345'), qr/Foo: 12345/, 'r.headersOut');\nlike(http_get('/hdr_out?foo=123&bar=copy'), qr/Bar: 123/, 'r.headersOut get');\nunlike(http_get('/hdr_out?bar=empty'), qr/Bar:/, 'r.headersOut empty');\nunlike(http_get('/hdr_out?foo='), qr/Foo:/, 'r.headersOut no value');\nunlike(http_get('/hdr_out?foo'), qr/Foo:/, 'r.headersOut no value 2');\n\nlike(http_get('/content_length_keys'), qr/B:true/, 'Content-Length in keys');\nlike(http_get('/content_length_arr'), qr/Content-Length: 3/,\n\t'set Content-Length arr');\n\nlike(http_get('/content_type'), qr/B:true/, 'Content-Type in keys');\nlike(http_get('/content_type_arr'), qr/Content-Type: text\\/html/,\n\t'set Content-Type arr');\nlike(http_get('/content_encoding_arr'), qr/Content-Encoding: gzip/,\n\t'set Content-Encoding arr');\n\nlike(http_get('/hdr_out_array?foo=12345'), qr/Foo: bar\\r\\nFoo: 12345/,\n\t'r.headersOut arr');\nlike(http_get('/hdr_out_array'), qr/Foo: bar/,\n\t'r.headersOut arr last is empty');\nlike(http_get('/hdr_out_array?foo=abc'), qr/B:bar,\\s?abc/,\n\t'r.headersOut get');\nlike(http_get('/hdr_out_array'), qr/B:bar/, 'r.headersOut get2');\nlike(http_get('/hdr_out_array?hidden=1'), qr/B:undefined/,\n\t'r.headersOut get3');\nlike(http_get('/hdr_out_array?scalar_set=1'), qr/B:xxx/,\n\t'r.headersOut scalar set');\nlike(http_get('/hdr_out_single'), qr/ETag: a\\r\\nETag: b/,\n\t'r.headersOut single');\nlike(http_get('/hdr_out_single'), qr/B:a/,\n\t'r.headersOut single get');\nlike(http_get('/hdr_out_set_cookie'), qr/Set-Cookie: c\\r\\nSet-Cookie: d/,\n\t'set_cookie');\nlike(http_get('/hdr_out_set_cookie'), qr/B:\\['c','d','f']/,\n\t'set_cookie2');\nunlike(http_get('/hdr_out_set_cookie'), qr/Set-Cookie: [abe]/,\n\t'set_cookie3');\n\nlike(http(\n\t'GET /hdr_in HTTP/1.0' . CRLF\n\t. 'Cookie: foo' . CRLF\n\t. 'Host: localhost' . CRLF . CRLF\n), qr/cookie: foo/, 'r.headersIn cookie');\n\nlike(http(\n\t'GET /hdr_in HTTP/1.0' . CRLF\n\t. 'X-Forwarded-For: foo' . CRLF\n\t. 'Host: localhost' . CRLF . CRLF\n), qr/x-forwarded-for: foo/, 'r.headersIn xff');\n\nlike(http(\n\t'GET /hdr_in HTTP/1.0' . CRLF\n\t. 'Cookie: foo1' . CRLF\n\t. 'Cookie: foo2' . CRLF\n\t. 'Host: localhost' . CRLF . CRLF\n), qr/cookie: foo1;\\s?foo2/, 'r.headersIn cookie2');\n\nlike(http(\n\t'GET /hdr_in HTTP/1.0' . CRLF\n\t. 'X-Forwarded-For: foo1' . CRLF\n\t. 'X-Forwarded-For: foo2' . CRLF\n\t. 'Host: localhost' . CRLF . CRLF\n), qr/x-forwarded-for: foo1,\\s?foo2/, 'r.headersIn xff2');\n\nlike(http(\n\t'GET /hdr_in HTTP/1.0' . CRLF\n\t. 'ETag: bar1' . CRLF\n\t. 'ETag: bar2' . CRLF\n\t. 'Host: localhost' . CRLF . CRLF\n), qr/etag: bar1(?!,\\s?bar2)/, 'r.headersIn duplicate single');\n\nlike(http(\n\t'GET /hdr_in HTTP/1.0' . CRLF\n\t. 'Content-Type: bar1' . CRLF\n\t. 'Content-Type: bar2' . CRLF\n\t. 'Host: localhost' . CRLF . CRLF\n), qr/content-type: bar1(?!,\\s?bar2)/, 'r.headersIn duplicate single 2');\n\nlike(http(\n\t'GET /hdr_in HTTP/1.0' . CRLF\n\t. 'Foo: bar1' . CRLF\n\t. 'Foo: bar2' . CRLF\n\t. 'Host: localhost' . CRLF . CRLF\n), qr/foo: bar1,\\s?bar2/, 'r.headersIn duplicate generic');\n\nlike(http(\n\t'GET /raw_hdr_in?filter=foo HTTP/1.0' . CRLF\n\t. 'foo: bar1' . CRLF\n\t. 'Foo: bar2' . CRLF\n\t. 'Host: localhost' . CRLF . CRLF\n), qr/raw: bar1|bar2/, 'r.rawHeadersIn');\n\nlike(http_get('/raw_hdr_out?filter=a'), qr/raw: foo|bar/, 'r.rawHeadersOut');\n\nlike(http(\n\t'GET /hdr_sorted_keys?in=1 HTTP/1.0' . CRLF\n\t. 'Cookie: foo1' . CRLF\n\t. 'Accept: */*' . CRLF\n\t. 'Cookie: foo2' . CRLF\n\t. 'Host: localhost' . CRLF . CRLF\n), qr/Accept,Cookie,Host/, 'r.headersIn sorted keys');\n\nlike(http(\n\t'GET /hdr_sorted_keys HTTP/1.0' . CRLF\n\t. 'Host: localhost' . CRLF . CRLF\n), qr/a,b,c/, 'r.headersOut sorted keys');\n\nTODO: {\nlocal $TODO = 'not yet'\n\tunless http_get('/njs') =~ /^([.0-9]+)$/m && $1 ge '0.7.6';\n\nlike(http_get('/hdr_out_special_set'), qr/CE: abc/,\n\t'r.headerOut special set');\n\nlike(http_get('/copy_subrequest_hdrs'),\n\tqr/A: a.*B: b.*C: c.*D: d.*Set-Cookie: A.*Set-Cookie: BB/s,\n\t'subrequest copy');\n\nlike(http_get('/copy_subrequest_hdrs'),\n\tqr/Content-Type: ct.*Content-Encoding: ce.*Content-Length: 3/s,\n\t'subrequest copy special');\n\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/js_import.t",
    "content": "#!/usr/bin/perl\n\n# (C) Dmitry Volyntsev\n# (c) Nginx, Inc.\n\n# Tests for http njs module, js_import directive.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    js_set $test foo.bar.p;\n\n    js_import lib.js;\n    js_import fun.js;\n    js_import foo from ./main.js;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location /njs {\n            js_content foo.version;\n        }\n\n        location /test_foo {\n            js_content foo.test;\n        }\n\n        location /test_lib {\n            js_content lib.test;\n        }\n\n        location /test_fun {\n            js_content fun;\n        }\n\n        location /test_var {\n            return 200 $test;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('lib.js', <<EOF);\n    function test(r) {\n        r.return(200, \"LIB-TEST\");\n    }\n\n    export default {test};\n\nEOF\n\n$t->write_file('fun.js', <<EOF);\n    export default function (r) {r.return(200, \"FUN-TEST\")};\n\nEOF\n\n$t->write_file('main.js', <<EOF);\n    function version(r) {\n        r.return(200, njs.version);\n    }\n\n    function test(r) {\n        r.return(200, \"MAIN-TEST\");\n    }\n\n    export default {version, test, bar: {p(r) {return \"P-TEST\"}}};\n\nEOF\n\n$t->try_run('no njs available')->plan(4);\n\n###############################################################################\n\nlike(http_get('/test_foo'), qr/MAIN-TEST/s, 'foo.test');\nlike(http_get('/test_lib'), qr/LIB-TEST/s, 'lib.test');\nlike(http_get('/test_fun'), qr/FUN-TEST/s, 'fun');\nlike(http_get('/test_var'), qr/P-TEST/s, 'foo.bar.p');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/js_import2.t",
    "content": "#!/usr/bin/perl\n\n# (C) Dmitry Volyntsev\n# (c) Nginx, Inc.\n\n# Tests for http njs module, js_import directive in server | location contexts.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy rewrite/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        js_set $test foo.bar.p;\n\n        js_import foo from main.js;\n\n        location /njs {\n            js_content foo.version;\n        }\n\n        location /test_foo {\n            js_content foo.test;\n        }\n\n        location /test_lib {\n            js_import lib.js;\n            js_content lib.test;\n        }\n\n        location /test_fun {\n            js_import fun.js;\n            js_content fun;\n        }\n\n        location /test_var {\n            return 200 $test;\n        }\n\n        location /proxy {\n            proxy_pass http://127.0.0.1:8081/;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        location /test_fun {\n            js_import fun.js;\n            js_content fun;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('lib.js', <<EOF);\n    function test(r) {\n        r.return(200, \"LIB-TEST\");\n    }\n\n    function p(r) {\n        return \"LIB-P\";\n    }\n\n    export default {test, p};\n\nEOF\n\n$t->write_file('fun.js', <<EOF);\n    export default function (r) {r.return(200, \"FUN-TEST\")};\n\nEOF\n\n$t->write_file('main.js', <<EOF);\n    function version(r) {\n        r.return(200, njs.version);\n    }\n\n    function test(r) {\n        r.return(200, \"MAIN-TEST\");\n    }\n\n    export default {version, test, bar: {p(r) {return \"P-TEST\"}}};\n\nEOF\n\n$t->try_run('no njs available')->plan(5);\n\n###############################################################################\n\nlike(http_get('/test_foo'), qr/MAIN-TEST/s, 'foo.test');\nlike(http_get('/test_lib'), qr/LIB-TEST/s, 'lib.test');\nlike(http_get('/test_fun'), qr/FUN-TEST/s, 'fun');\nlike(http_get('/proxy/test_fun'), qr/FUN-TEST/s, 'proxy fun');\nlike(http_get('/test_var'), qr/P-TEST/s, 'foo.bar.p');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/js_internal_redirect.t",
    "content": "#!/usr/bin/perl\n\n# (C) Dmitry Volyntsev\n# (C) Nginx, Inc.\n\n# Tests for http njs module, internalRedirect method.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http rewrite/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    js_import test.js;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location /njs {\n            js_content test.njs;\n        }\n\n        location /test {\n            js_content test.redirect;\n        }\n\n        location /redirect {\n            internal;\n            return 200 redirect$arg_b;\n        }\n\n        location @named {\n            return 200 named;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('test.js', <<EOF);\n    function test_njs(r) {\n        r.return(200, njs.version);\n    }\n\n    function redirect(r) {\n        if (r.variables.arg_dest == 'named') {\n            r.internalRedirect('\\@named');\n\n        } else if (r.variables.arg_unsafe) {\n            r.internalRedirect('/red\\0rect');\n\n        } else if (r.variables.arg_quoted) {\n            r.internalRedirect('/red%69rect');\n\n        } else {\n            if (r.variables.arg_a) {\n                r.internalRedirect('/redirect?b=' + r.variables.arg_a);\n\n            } else {\n                r.internalRedirect('/redirect');\n            }\n        }\n    }\n\n    export default {njs:test_njs, redirect};\n\nEOF\n\n$t->try_run('no njs available')->plan(5);\n\n###############################################################################\n\nlike(http_get('/test'), qr/redirect/s, 'redirect');\nlike(http_get('/test?a=A'), qr/redirectA/s, 'redirect with args');\nlike(http_get('/test?dest=named'), qr/named/s, 'redirect to named location');\n\nTODO: {\nlocal $TODO = 'not yet'\n\tunless http_get('/njs') =~ /^([.0-9]+)$/m && $1 ge '0.7.4';\n\nlike(http_get('/test?unsafe=1'), qr/500 Internal Server/s,\n\t'unsafe redirect');\nlike(http_get('/test?quoted=1'), qr/200 .*redirect/s,\n\t'quoted redirect');\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/js_modules.t",
    "content": "#!/usr/bin/perl\n\n# (C) Dmitry Volyntsev\n# (C) Nginx, Inc.\n\n# Tests for http njs module, ES6 import, export.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    js_import test.js;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location /test {\n            js_content test.test;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('test.js', <<EOF);\n    import m from 'module.js';\n\n    function test(r) {\n        r.return(200, m[r.args.fun](r.args.a, r.args.b));\n    }\n\n    export default {test};\n\nEOF\n\n$t->write_file('module.js', <<EOF);\n    function sum(a, b) {\n        return Number(a) + Number(b);\n    }\n\n    function prod(a, b) {\n        return Number(a) * Number(b);\n    }\n\n    export default {sum, prod};\n\nEOF\n\n\n$t->try_run('no njs modules')->plan(2);\n\n###############################################################################\n\nlike(http_get('/test?fun=sum&a=3&b=4'), qr/7/s, 'test sum');\nlike(http_get('/test?fun=prod&a=3&b=4'), qr/12/s, 'test prod');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/js_ngx.t",
    "content": "#!/usr/bin/perl\n\n# (C) Dmitry Volyntsev\n# (C) Nginx, Inc.\n\n# Tests for http njs module, ngx object.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    js_import test.js;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location /njs {\n            js_content test.njs;\n        }\n\n        location /log {\n            js_content test.log;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('test.js', <<EOF);\n    function test_njs(r) {\n        r.return(200, njs.version);\n    }\n\n    function level(r) {\n        switch (r.args.level) {\n        case 'INFO': return ngx.INFO;\n        case 'WARN': return ngx.WARN;\n        case 'ERR': return ngx.ERR;\n        default:\n            throw Error(`Unknown log level:\"\\${r.args.level}\"`);\n        }\n    }\n\n    function log(r) {\n        ngx.log(level(r), `ngx.log:\\${r.args.text}`);\n        r.return(200);\n    }\n\n    export default {njs: test_njs, log};\n\nEOF\n\n$t->try_run('no njs ngx')->plan(3);\n\n###############################################################################\n\nhttp_get('/log?level=INFO&text=FOO');\nhttp_get('/log?level=WARN&text=BAR');\nhttp_get('/log?level=ERR&text=BAZ');\n\n$t->stop();\n\nlike($t->read_file('error.log'), qr/\\[info\\].*ngx.log:FOO/, 'ngx.log info');\nlike($t->read_file('error.log'), qr/\\[warn\\].*ngx.log:BAR/, 'ngx.log warn');\nlike($t->read_file('error.log'), qr/\\[error\\].*ngx.log:BAZ/, 'ngx.log err');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/js_object.t",
    "content": "#!/usr/bin/perl\n\n# (C) Dmitry Volyntsev\n# (C) Nginx, Inc.\n\n# Tests for http njs module, request object.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\nuse Socket qw/ CRLF /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    js_import test.js;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location /to_string {\n            js_content test.to_string;\n        }\n\n        location /define_prop {\n            js_content test.define_prop;\n        }\n\n        location /in_operator {\n            js_content test.in_operator;\n        }\n\n        location /redefine_bind {\n            js_content test.redefine_bind;\n        }\n\n        location /redefine_proxy {\n            js_content test.redefine_proxy;\n        }\n\n        location /redefine_proto {\n            js_content test.redefine_proto;\n        }\n\n        location /get_own_prop_descs {\n            js_content test.get_own_prop_descs;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('test.js', <<EOF);\n    function to_string(r) {\n        r.return(200, r.toString());\n    }\n\n    function define_prop(r) {\n        Object.defineProperty(r.headersOut, 'Foo', {value:'bar'});\n        r.return(200);\n    }\n\n    function in_operator(r) {\n        r.return(200, ['Foo', 'Bar'].map(v=>v in r.headersIn)\n                      .toString() === 'true,false');\n    }\n\n    function redefine_bind(r) {\n        r.return = r.return.bind(r, 200);\n        r.return('redefine_bind');\n    }\n\n    function redefine_proxy(r) {\n        r.return_orig = r.return;\n        r.return = function (body) { this.return_orig(200, body);}\n        r.return('redefine_proxy');\n    }\n\n    function redefine_proto(r) {\n        r[0] = 'a';\n        r[1] = 'b';\n        r.length = 2;\n        Object.setPrototypeOf(r, Array.prototype);\n        r.return(200, r.join('|'));\n    }\n\n    function get_own_prop_descs(r) {\n        r.return(200,\n                 Object.getOwnPropertyDescriptors(r)['log'].value === r.log);\n    }\n\n    export default {to_string, define_prop, in_operator, redefine_bind,\n                    redefine_proxy, redefine_proto, get_own_prop_descs};\n\nEOF\n\n$t->try_run('no njs request object')->plan(7);\n\n###############################################################################\n\nlike(http_get('/to_string'), qr/\\[object Request\\]/, 'toString');\nlike(http_get('/define_prop'), qr/Foo: bar/, 'define_prop');\nlike(http(\n\t'GET /in_operator HTTP/1.0' . CRLF\n\t. 'Foo: foo' . CRLF\n\t. 'Host: localhost' . CRLF . CRLF\n), qr/true/, 'in_operator');\nlike(http_get('/redefine_bind'), qr/redefine_bind/, 'redefine_bind');\nlike(http_get('/redefine_proxy'), qr/redefine_proxy/, 'redefine_proxy');\nlike(http_get('/redefine_proto'), qr/a|b/, 'redefine_proto');\nlike(http_get('/get_own_prop_descs'), qr/true/, 'get_own_prop_descs');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/js_paths.t",
    "content": "#!/usr/bin/perl\n\n# (C) Dmitry Volyntsev\n# (C) Nginx, Inc.\n\n# Tests for http njs module, js_path directive.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    js_path \"%%TESTDIR%%/lib1\";\n    js_path \"lib2\";\n\n    js_import test.js;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location /test {\n            js_content test.test;\n        }\n\n        location /test2 {\n            js_content test.test2;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('test.js', <<EOF);\n    import m1 from 'module1.js';\n    import m2 from 'module2.js';\n    import m3 from 'lib1/module1.js';\n\n    function test(r) {\n        r.return(200, m1[r.args.fun](r.args.a, r.args.b));\n    }\n\n    function test2(r) {\n        r.return(200, m2.sum(r.args.a, r.args.b));\n    }\n\n    function test3(r) {\n        r.return(200, m3.sum(r.args.a, r.args.b));\n    }\n\n    export default {test, test2};\n\nEOF\n\nmy $d = $t->testdir();\n\nmkdir(\"$d/lib1\");\nmkdir(\"$d/lib2\");\n\n$t->write_file('lib1/module1.js', <<EOF);\n    function sum(a, b) { return Number(a) + Number(b); }\n    function prod(a, b) { return Number(a) * Number(b); }\n\n    export default {sum, prod};\n\nEOF\n\n$t->write_file('lib2/module2.js', <<EOF);\n    function sum(a, b) { return a + b; }\n\n    export default {sum};\n\nEOF\n\n\n$t->try_run('no njs available')->plan(4);\n\n###############################################################################\n\nlike(http_get('/test?fun=sum&a=3&b=4'), qr/7/s, 'test sum');\nlike(http_get('/test?fun=prod&a=3&b=4'), qr/12/s, 'test prod');\nlike(http_get('/test2?a=3&b=4'), qr/34/s, 'test2');\nlike(http_get('/test2?a=A&b=B'), qr/AB/s, 'test2 relative');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/js_preload_object.t",
    "content": "#!/usr/bin/perl\n\n# (C) Vadim Zhestikov\n# (C) Nginx, Inc.\n\n# Tests for http njs module, js_preload_object directive.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http rewrite/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    js_preload_object g1 from g.json;\n    js_preload_object ga from ga.json;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        js_import lib.js;\n        js_preload_object lx from l.json;\n\n        location /test {\n            js_content lib.test;\n        }\n\n        location /test_query {\n            js_import lib1.js;\n            js_content lib1.query;\n        }\n\n        location /test_query_preloaded {\n            js_import lib1.js;\n            js_preload_object l.json;\n            js_content lib1.query;\n        }\n\n        location /test_var {\n            js_set $test_var lib.test_var;\n            return 200 $test_var;\n        }\n\n        location /test_mutate {\n            js_content lib.mutate;\n        }\n\n        location /test_no_suffix {\n            js_preload_object gg from no_suffix;\n            js_content lib.suffix;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('lib.js', <<EOF);\n    function test(r) {\n        r.return(200, ga + ' ' + g1.c.prop[0].a + ' ' + lx);\n    }\n\n    function test_var(r) {\n        return g1.b[2];\n    }\n\n    function mutate(r) {\n        var res = \"OK\";\n\n        try {\n            switch (r.args.method) {\n            case 'set_obj':\n                g1.c.prop[0].a = 5;\n                break;\n            case 'set_arr':\n                g1.c.prop[0] = 5;\n                break;\n            case 'add_obj':\n                g1.c.prop[0].xxx = 5;\n                break;\n            case 'add_arr':\n                g1.c.prop[10] = 5;\n                break;\n            case 'del_obj':\n                delete g1.c.prop[0].a;\n                break;\n            case 'del_arr':\n                delete g1.c.prop[0];\n                break;\n            }\n\n        } catch (e) {\n            res = e.message;\n        }\n\n        r.return(200, res);\n    }\n\n    function suffix(r) {\n        r.return(200, gg);\n    }\n\n    export default {test, test_var, mutate, suffix};\n\nEOF\n\n$t->write_file('lib1.js', <<EOF);\n    function query(r) {\n        var res = 'ok';\n\n        try {\n            res = r.args.path.split('.').reduce((a, v) => a[v], globalThis);\n\n        } catch (e) {\n            res = e.message;\n        }\n\n        r.return(200, njs.dump(res));\n    }\n\n    export default {query};\n\nEOF\n\n$t->write_file('g.json',\n\t'{\"a\":1, \"b\":[1,2,\"element\",4,5], \"c\":{\"prop\":[{\"a\":2}]}}');\n$t->write_file('ga.json', '\"ga loaded\"');\n$t->write_file('l.json', '\"l loaded\"');\n$t->write_file('no_suffix', '\"no_suffix loaded\"');\n\n$t->try_run('no js_preload_object available')->plan(12);\n\n###############################################################################\n\nlike(http_get('/test'), qr/ga loaded 2 l loaded/s, 'direct query');\nlike(http_get('/test_query?path=l'), qr/undefined/s, 'unreferenced');\nlike(http_get('/test_query_preloaded?path=l'), qr/l loaded/s,\n\t'reference preload');\nlike(http_get('/test_query?path=g1.b.1'), qr/2/s, 'complex query');\nlike(http_get('/test_var'), qr/element/s, 'var reference');\n\nlike(http_get('/test_mutate?method=set_obj'), qr/Cannot assign to read-only/s,\n\t'preload_object props are const (object)');\nlike(http_get('/test_mutate?method=set_arr'), qr/Cannot assign to read-only/s,\n\t'preload_object props are const (array)');\nlike(http_get('/test_mutate?method=add_obj'), qr/Cannot add property \"xxx\"/s,\n\t'preload_object props are not extensible (object)');\nlike(http_get('/test_mutate?method=add_arr'), qr/Cannot add property \"10\"/s,\n\t'preload_object props are not extensible (array)');\nlike(http_get('/test_mutate?method=del_obj'), qr/Cannot delete property \"a\"/s,\n\t'preload_object props are not deletable (object)');\nlike(http_get('/test_mutate?method=del_arr'), qr/Cannot delete property \"0\"/s,\n\t'preload_object props are not deletable (array)');\n\nlike(http_get('/test_no_suffix'), qr/no_suffix loaded/s,\n\t'load without suffix');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/js_promise.t",
    "content": "#!/usr/bin/perl\n\n# (C) Nginx, Inc.\n\n# Promise tests for http njs module.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    js_import test.js;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location /njs {\n            js_content test.njs;\n        }\n\n        location /promise {\n            js_content test.promise;\n        }\n\n        location /promise_throw {\n            js_content test.promise_throw;\n        }\n\n        location /promise_pure {\n            js_content test.promise_pure;\n        }\n\n        location /timeout {\n            js_content test.timeout;\n        }\n\n        location /sub_token {\n            js_content test.sub_token;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('test.js', <<EOF);\n    var global_token = '';\n\n    function test_njs(r) {\n        r.return(200, njs.version);\n    }\n\n    function promise(r) {\n        promisified_subrequest(r, '/sub_token', 'code=200&token=a')\n        .then(reply => {\n            var data = JSON.parse(reply.responseText);\n\n            if (data['token'] !== \"a\") {\n                throw new Error('token is not \"a\"');\n            }\n\n            return data['token'];\n        })\n        .then(token => {\n            promisified_subrequest(r, '/sub_token', 'code=200&token=b')\n            .then(reply => {\n                var data = JSON.parse(reply.responseText);\n\n                r.return(200, '{\"token\": \"' + data['token'] + '\"}');\n            })\n            .catch(() => {\n                throw new Error(\"failed promise() test\");\n            });\n        })\n        .catch(() => {\n            r.return(500);\n        });\n    }\n\n    function promise_throw(r) {\n        promisified_subrequest(r, '/sub_token', 'code=200&token=x')\n        .then(reply => {\n            var data = JSON.parse(reply.responseText);\n\n            if (data['token'] !== \"a\") {\n                throw data['token'];\n            }\n\n            return data['token'];\n        })\n        .then(() => {\n            r.return(500);\n        })\n        .catch(token => {\n            r.return(200, '{\"token\": \"' + token + '\"}');\n        });\n    }\n\n    function promise_pure(r) {\n        var count = 0;\n\n        Promise.resolve(true)\n        .then(() => count++)\n        .then(() => not_exist_ref)\n        .finally(() => count++)\n        .catch(() => {\n            r.return((count != 2) ? 500 : 200);\n        });\n    }\n\n    function timeout(r) {\n        promisified_subrequest(r, '/sub_token', 'code=200&token=R')\n        .then(reply => JSON.parse(reply.responseText))\n        .then(data => {\n            setTimeout(timeout_cb, 50, r, '/sub_token', 'code=200&token=T');\n            return data;\n        })\n        .then(data => {\n            setTimeout(timeout_cb, 1, r, '/sub_token', 'code=200&token='\n                                                        + data['token']);\n        })\n        .catch(() => {\n            r.return(500);\n        });\n    }\n\n    function timeout_cb(r, url, args) {\n        promisified_subrequest(r, url, args)\n        .then(reply => {\n            if (global_token == '') {\n                var data = JSON.parse(reply.responseText);\n\n                global_token = data['token'];\n\n                r.return(200, '{\"token\": \"' + data['token'] + '\"}');\n            }\n        })\n        .catch(() => {\n            r.return(500);\n        });\n    }\n\n    function promisified_subrequest(r, uri, args) {\n        return new Promise((resolve, reject) => {\n            r.subrequest(uri, args, (reply) => {\n                if (reply.status < 400) {\n                    resolve(reply);\n                } else {\n                    reject(reply);\n                }\n            });\n        })\n    }\n\n    function sub_token(r) {\n        var code = r.variables.arg_code;\n        var token = r.variables.arg_token;\n\n        r.return(parseInt(code), '{\"token\": \"'+ token +'\"}');\n    }\n\n    export default {njs:test_njs, promise, promise_throw, promise_pure,\n                    timeout, sub_token};\n\nEOF\n\n$t->try_run('no njs available')->plan(4);\n\n###############################################################################\n\nlike(http_get('/promise'), qr/{\"token\": \"b\"}/, \"Promise\");\nlike(http_get('/promise_throw'), qr/{\"token\": \"x\"}/, \"Promise throw and catch\");\nlike(http_get('/timeout'), qr/{\"token\": \"R\"}/, \"Promise with timeout\");\nlike(http_get('/promise_pure'), qr/200 OK/, \"events handling\");\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/js_request_body.t",
    "content": "#!/usr/bin/perl\n\n# (C) Dmitry Volyntsev\n# (C) Nginx, Inc.\n\n# Tests for http njs module, r.requestText method.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse Socket qw/ CRLF /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    js_import test.js;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location /body {\n            js_content test.body;\n        }\n\n        location /in_file {\n            client_body_in_file_only on;\n            js_content test.body;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('test.js', <<EOF);\n    function body(r) {\n        try {\n            var body = r.requestText;\n            r.return(200, body);\n\n        } catch (e) {\n            r.return(500, e.message);\n        }\n    }\n\n    export default {body};\n\nEOF\n\n$t->try_run('no njs request body')->plan(3);\n\n###############################################################################\n\nlike(http_post('/body'), qr/REQ-BODY/, 'request body');\nlike(http_post('/in_file'), qr/request body is in a file/,\n\t'request body in file');\nlike(http_post_big('/body'), qr/200.*^(1234567890){1024}$/ms,\n\t\t'request body big');\n\n###############################################################################\n\nsub http_post {\n\tmy ($url, %extra) = @_;\n\n\tmy $p = \"POST $url HTTP/1.0\" . CRLF .\n\t\t\"Host: localhost\" . CRLF .\n\t\t\"Content-Length: 8\" . CRLF .\n\t\tCRLF .\n\t\t\"REQ-BODY\";\n\n\treturn http($p, %extra);\n}\n\nsub http_post_big {\n\tmy ($url, %extra) = @_;\n\n\tmy $p = \"POST $url HTTP/1.0\" . CRLF .\n\t\t\"Host: localhost\" . CRLF .\n\t\t\"Content-Length: 10240\" . CRLF .\n\t\tCRLF .\n\t\t(\"1234567890\" x 1024);\n\n\treturn http($p, %extra);\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/js_return.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for http njs module, return method.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse Config;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    js_import test.js;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            js_content test.returnf;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('test.js', <<EOF);\n    function returnf(r) {\n        r.return(Number(r.args.c), r.args.t);\n    }\n\n    export default {returnf};\n\nEOF\n\n$t->try_run('no njs return')->plan(5);\n\n###############################################################################\n\nlike(http_get('/?c=200'), qr/200 OK.*\\x0d\\x0a?\\x0d\\x0a?$/s, 'return code');\nlike(http_get('/?c=200&t=SEE-THIS'), qr/200 OK.*^SEE-THIS$/ms, 'return text');\nlike(http_get('/?c=301&t=path'), qr/ 301 .*Location: path/s, 'return redirect');\nlike(http_get('/?c=404'), qr/404 Not.*html/s, 'return error page');\nlike(http_get('/?c=inv'), qr/ 500 /, 'return invalid');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/js_subrequests.t",
    "content": "#!/usr/bin/perl\n#\n# (C) Dmitry Volyntsev.\n# (C) Nginx, Inc.\n\n# Tests for subrequests in http njs module.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse Socket qw/ CRLF /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\neval { require JSON::PP; };\nplan(skip_all => \"JSON::PP not installed\") if $@;\n\nmy $t = Test::Nginx->new()->has(qw/http rewrite proxy cache/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    proxy_cache_path   %%TESTDIR%%/cache1\n                       keys_zone=ON:1m      use_temp_path=on;\n\n    js_import test.js;\n\n    js_set $async_var       test.async_var;\n    js_set $subrequest_var  test.subrequest_var;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location /njs {\n            js_content test.njs;\n        }\n\n        location /sr {\n            js_content test.sr;\n        }\n\n        location /sr_pr {\n            js_content test.sr_pr;\n        }\n\n        location /sr_args {\n            js_content test.sr_args;\n        }\n\n        location /sr_options_args {\n            js_content test.sr_options_args;\n        }\n\n        location /sr_options_args_pr {\n            js_content test.sr_options_args_pr;\n        }\n\n        location /sr_options_method {\n            js_content test.sr_options_method;\n        }\n\n        location /sr_options_method_pr {\n            js_content test.sr_options_method_pr;\n        }\n\n        location /sr_options_body {\n            js_content test.sr_options_body;\n        }\n\n        location /sr_options_method_head {\n            js_content test.sr_options_method_head;\n        }\n\n        location /sr_body {\n            js_content test.sr_body;\n        }\n\n        location /sr_body_pr {\n            js_content test.sr_body_pr;\n        }\n\n        location /sr_body_special {\n            js_content test.sr_body_special;\n        }\n\n        location /sr_in_variable_handler {\n            set $_ $async_var;\n            js_content test.sr_in_variable_handler;\n        }\n\n        location /sr_detached_in_variable_handler {\n            return 200 $subrequest_var;\n        }\n\n        location /sr_async_var {\n            set $_ $async_var;\n            error_page 404 /return;\n            return 404;\n        }\n\n        location /sr_error_page {\n            js_content test.sr_error_page;\n        }\n\n        location /sr_js_in_subrequest {\n            js_content test.sr_js_in_subrequest;\n        }\n\n        location /sr_js_in_subrequest_pr {\n            js_content test.sr_js_in_subrequest_pr;\n        }\n\n        location /sr_file {\n            js_content test.sr_file;\n        }\n\n        location /sr_cache {\n            js_content test.sr_cache;\n        }\n\n\n        location /sr_unavail {\n            js_content test.sr_unavail;\n        }\n\n        location /sr_unavail_pr {\n            js_content test.sr_unavail_pr;\n        }\n\n        location /sr_broken {\n            js_content test.sr_broken;\n        }\n\n        location /sr_too_large {\n            js_content test.sr_too_large;\n        }\n\n        location /sr_out_of_order {\n            js_content test.sr_out_of_order;\n        }\n\n        location /sr_except_not_a_func {\n            js_content test.sr_except_not_a_func;\n        }\n\n        location /sr_except_failed_to_convert_options_arg {\n            js_content test.sr_except_failed_to_convert_options_arg;\n        }\n\n        location /sr_except_invalid_options_header_only {\n            js_content test.sr_except_invalid_options_header_only;\n        }\n\n        location /sr_in_sr_callback {\n            js_content test.sr_in_sr_callback;\n        }\n\n        location /sr_uri_except {\n            js_content test.sr_uri_except;\n        }\n\n\n        location /file/ {\n            alias %%TESTDIR%%/;\n        }\n\n        location /p/ {\n            proxy_cache $arg_c;\n            proxy_pass http://127.0.0.1:8081/;\n        }\n\n        location /daemon/ {\n            proxy_pass http://127.0.0.1:8082/;\n        }\n\n        location /too_large/ {\n            subrequest_output_buffer_size 3;\n            proxy_pass http://127.0.0.1:8081/;\n        }\n\n        location /sr_in_sr {\n            js_content test.sr_in_sr;\n        }\n\n        location /unavail {\n            proxy_pass http://127.0.0.1:8084/;\n        }\n\n        location /sr_parent {\n             js_content test.sr_parent;\n        }\n\n        location /js_sub {\n            js_content test.js_sub;\n        }\n\n        location /return {\n            return 200 '[\"$request_method\"]';\n        }\n\n        location /error_page_404 {\n            return 404;\n\n            error_page 404 /404.html;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        location /sub1 {\n            add_header H $arg_h;\n            return 206 '{\"a\": {\"b\": 1}}';\n        }\n\n        location /sub2 {\n            return 404 '{\"e\": \"msg\"}';\n        }\n\n        location /method {\n            return 200 '[\"$request_method\"]';\n        }\n\n        location /body {\n            js_content test.body;\n        }\n\n        location /detached {\n            js_content test.detached;\n        }\n\n        location /delayed {\n            js_content test.delayed;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8084;\n        server_name  localhost;\n\n        return 444;\n    }\n}\n\nEOF\n\n$t->write_file('test.js', <<EOF);\n    function test_njs(r) {\n        r.return(200, njs.version);\n    }\n\n    function sr(r) {\n        subrequest_fn(r, ['/p/sub2'], ['uri', 'status'])\n    }\n\n    function sr_pr(r) {\n        r.subrequest('/p/sub1', 'h=xxx')\n        .then(reply => r.return(200, JSON.stringify({h:reply.headersOut.h})))\n    }\n\n    function sr_args(r) {\n        r.subrequest('/p/sub1', 'h=xxx', reply => {\n            r.return(200, JSON.stringify({h:reply.headersOut.h}));\n        });\n    }\n\n    function sr_options_args(r) {\n        r.subrequest('/p/sub1', {args:'h=xxx'}, reply => {\n            r.return(200, JSON.stringify({h:reply.headersOut.h}));\n        });\n    }\n\n    function sr_options_args_pr(r) {\n        r.subrequest('/p/sub1', {args:'h=xxx'})\n        .then(reply => r.return(200, JSON.stringify({h:reply.headersOut.h})))\n    }\n\n    function sr_options_method(r) {\n        r.subrequest('/p/method', {method:r.args.m}, body_fwd_cb);\n    }\n\n    function sr_options_method_pr(r) {\n        r.subrequest('/p/method', {method:r.args.m})\n        .then(body_fwd_cb);\n    }\n\n    function sr_options_body(r) {\n        r.subrequest('/p/body', {method:'POST', body:'[\"REQ-BODY\"]'},\n                     body_fwd_cb);\n    }\n\n    function sr_options_method_head(r) {\n        r.subrequest('/p/method', {method:'HEAD'}, reply => {\n            r.return(200, JSON.stringify({c:reply.status}));\n        });\n    }\n\n    function sr_body(r) {\n        r.subrequest('/p/sub1', body_fwd_cb);\n    }\n\n    function sr_body_pr(r) {\n        r.subrequest('/p/sub1')\n        .then(body_fwd_cb);\n    }\n\n    function sr_body_special(r) {\n        r.subrequest('/p/sub2', body_fwd_cb);\n    }\n\n    function body(r) {\n        r.return(200, r.variables.request_body);\n    }\n\n    function delayed(r) {\n        setTimeout(r => r.return(200), 100, r);\n    }\n\n    function detached(r) {\n        var method = r.variables.request_method;\n        r.log(`DETACHED: \\${method} args: \\${r.variables.args}`);\n\n        r.return(200);\n    }\n\n    function sr_in_variable_handler(r) {\n    }\n\n    function async_var(r) {\n        r.subrequest('/p/delayed', reply => {\n            r.return(200, JSON.stringify([\"CB-VAR\"]));\n        });\n\n        return \"\";\n    }\n\n    function sr_error_page(r) {\n         r.subrequest('/error_page_404')\n         .then(reply => {r.return(200, `reply.status:\\${reply.status}`)});\n    }\n\n    function subrequest_var(r) {\n        r.subrequest('/p/detached',  {detached:true});\n        r.subrequest('/p/detached',  {detached:true, args:'a=yyy',\n                                      method:'POST'});\n\n        return \"subrequest_var\";\n    }\n\n    function sr_file(r) {\n        r.subrequest('/file/t', body_fwd_cb);\n    }\n\n    function sr_cache(r) {\n        r.subrequest('/p/t', body_fwd_cb);\n    }\n\n    function sr_unavail(req) {\n        subrequest_fn(req, ['/unavail'], ['uri', 'status']);\n    }\n\n    function sr_unavail_pr(req) {\n        subrequest_fn_pr(req, ['/unavail'], ['uri', 'status']);\n    }\n\n    function sr_broken(r) {\n        r.subrequest('/daemon/unfinished', reply => {\n            r.return(200, JSON.stringify({code:reply.status}));\n        });\n    }\n\n    function sr_too_large(r) {\n        r.subrequest('/too_large/t', body_fwd_cb);\n    }\n\n    function sr_in_sr(r) {\n        r.subrequest('/sr', body_fwd_cb);\n    }\n\n    function sr_js_in_subrequest(r) {\n        r.subrequest('/js_sub', body_fwd_cb);\n    }\n\n    function sr_js_in_subrequest_pr(r) {\n        r.subrequest('/js_sub')\n        .then(body_fwd_cb);\n    }\n\n    function sr_in_sr_callback(r) {\n        r.subrequest('/return', function (reply) {\n                try {\n                    reply.subrequest('/return');\n\n                } catch (err) {\n                    r.return(200, JSON.stringify({e:err.message}));\n                    return;\n                }\n\n                r.return(200);\n            });\n    }\n\n    function sr_parent(r) {\n        try {\n            var parent = r.parent;\n\n        } catch (err) {\n            r.return(200, JSON.stringify({e:err.message}));\n            return;\n        }\n\n        r.return(200);\n    }\n\n    function sr_out_of_order(r) {\n        subrequest_fn(r, ['/p/delayed', '/p/sub1', '/unknown'],\n                      ['uri', 'status']);\n    }\n\n    function collect(replies, props, total, reply) {\n        reply.log(`subrequest handler: \\${reply.uri} status: \\${reply.status}`)\n\n        var rep = {};\n        props.forEach(p => {rep[p] = reply[p]});\n\n        replies.push(rep);\n\n        if (replies.length == total) {\n            reply.parent.return(200, JSON.stringify(replies));\n        }\n    }\n\n    function subrequest_fn(r, subs, props) {\n        var replies = [];\n\n        subs.forEach(sr =>\n                     r.subrequest(sr, collect.bind(null, replies,\n                                                   props, subs.length)));\n    }\n\n    function subrequest_fn_pr(r, subs, props) {\n        var replies = [];\n\n        subs.forEach(sr => r.subrequest(sr)\n            .then(collect.bind(null, replies, props, subs.length)));\n    }\n\n    function sr_except_not_a_func(r) {\n        r.subrequest('/sub1', 'a=1', 'b');\n    }\n\n    let Failed = {get toConvert() { return {toString(){return {};}}}};\n\n    function sr_except_failed_to_convert_options_arg(r) {\n        r.subrequest('/sub1', {args:Failed.toConvert}, ()=>{});\n    }\n\n    function sr_uri_except(r) {\n        r.subrequest(Failed.toConvert, 'a=1', 'b');\n    }\n\n    function body_fwd_cb(r) {\n        r.parent.return(200, JSON.stringify(JSON.parse(r.responseText)));\n    }\n\n    function js_sub(r) {\n        r.return(200, '[\"JS-SUB\"]');\n    }\n\n    export default {njs:test_njs, sr, sr_pr, sr_args, sr_options_args,\n                    sr_options_args_pr, sr_options_method, sr_options_method_pr,\n                    sr_options_method_head, sr_options_body, sr_body,\n                    sr_body_pr, sr_body_special, body, delayed, detached,\n                    sr_in_variable_handler, async_var, sr_error_page,\n                    subrequest_var, sr_file, sr_cache, sr_unavail, sr_parent,\n                    sr_unavail_pr, sr_broken, sr_too_large, sr_in_sr,\n                    sr_js_in_subrequest, sr_js_in_subrequest_pr, js_sub,\n                    sr_in_sr_callback, sr_out_of_order, sr_except_not_a_func,\n                    sr_uri_except, sr_except_failed_to_convert_options_arg};\n\nEOF\n\n$t->write_file('t', '[\"SEE-THIS\"]');\n\n$t->try_run('no njs available')->plan(32);\n$t->run_daemon(\\&http_daemon);\n\n###############################################################################\n\nis(get_json('/sr'), '[{\"status\":404,\"uri\":\"/p/sub2\"}]', 'sr');\nis(get_json('/sr_args'), '{\"h\":\"xxx\"}', 'sr_args');\nis(get_json('/sr_options_args'), '{\"h\":\"xxx\"}', 'sr_options_args');\nis(get_json('/sr_options_method?m=POST'), '[\"POST\"]', 'sr method POST');\nis(get_json('/sr_options_method?m=PURGE'), '[\"PURGE\"]', 'sr method PURGE');\nis(get_json('/sr_options_body'), '[\"REQ-BODY\"]', 'sr_options_body');\nis(get_json('/sr_options_method_head'), '{\"c\":200}', 'sr_options_method_head');\nis(get_json('/sr_body'), '{\"a\":{\"b\":1}}', 'sr_body');\nis(get_json('/sr_body_special'), '{\"e\":\"msg\"}', 'sr_body_special');\nis(get_json('/sr_in_variable_handler'), '[\"CB-VAR\"]', 'sr_in_variable_handler');\nis(get_json('/sr_file'), '[\"SEE-THIS\"]', 'sr_file');\nis(get_json('/sr_cache?c=1'), '[\"SEE-THIS\"]', 'sr_cache');\nis(get_json('/sr_cache?c=1'), '[\"SEE-THIS\"]', 'sr_cached');\nis(get_json('/sr_js_in_subrequest'), '[\"JS-SUB\"]', 'sr_js_in_subrequest');\nis(get_json('/sr_unavail'), '[{\"status\":502,\"uri\":\"/unavail\"}]',\n\t'sr_unavail');\nis(get_json('/sr_out_of_order'),\n\t'[{\"status\":404,\"uri\":\"/unknown\"},' .\n\t'{\"status\":206,\"uri\":\"/p/sub1\"},' .\n\t'{\"status\":200,\"uri\":\"/p/delayed\"}]',\n\t'sr_multi');\n\nis(get_json('/sr_pr'), '{\"h\":\"xxx\"}', 'sr_promise');\nis(get_json('/sr_options_args_pr'), '{\"h\":\"xxx\"}', 'sr_options_args_pr');\nis(get_json('/sr_options_method_pr?m=PUT'), '[\"PUT\"]', 'sr method PUT');\nis(get_json('/sr_body_pr'), '{\"a\":{\"b\":1}}', 'sr_body_pr');\nis(get_json('/sr_js_in_subrequest_pr'), '[\"JS-SUB\"]', 'sr_js_in_subrequest_pr');\nis(get_json('/sr_unavail_pr'), '[{\"status\":502,\"uri\":\"/unavail\"}]',\n\t'sr_unavail_pr');\n\nlike(http_get('/sr_detached_in_variable_handler'), qr/subrequest_var/,\n     'sr_detached_in_variable_handler');\n\nlike(http_get('/sr_error_page'), qr/reply\\.status:404/,\n     'sr_error_page');\n\nhttp_get('/sr_broken');\nhttp_get('/sr_in_sr');\nhttp_get('/sr_in_variable_handler');\nhttp_get('/sr_async_var');\nhttp_get('/sr_too_large');\nhttp_get('/sr_except_not_a_func');\nhttp_get('/sr_except_failed_to_convert_options_arg');\nhttp_get('/sr_uri_except');\n\nis(get_json('/sr_in_sr_callback'),\n\t'{\"e\":\"subrequest can only be created for the primary request\"}',\n\t'subrequest for non-primary request');\n\n$t->stop();\n\nok(index($t->read_file('error.log'), 'callback is not a function') > 0,\n\t'subrequest cb exception');\nok(index($t->read_file('error.log'), 'failed to convert uri arg') > 0,\n\t'subrequest uri exception');\nok(index($t->read_file('error.log'), 'failed to convert options.args') > 0,\n\t'subrequest invalid args exception');\nok(index($t->read_file('error.log'), 'too big subrequest response') > 0,\n\t'subrequest too large body');\nok(index($t->read_file('error.log'), 'subrequest creation failed') > 0,\n\t'subrequest creation failed');\nok(index($t->read_file('error.log'),\n\t\t'js subrequest: failed to get the parent context') > 0,\n\t'zero parent ctx');\n\nok(index($t->read_file('error.log'), 'DETACHED') > 0,\n\t'detached subrequest');\n\n###############################################################################\n\nsub recode {\n\tmy $json;\n\teval { $json = JSON::PP::decode_json(shift) };\n\n\tif ($@) {\n\t\treturn \"<failed to parse JSON>\";\n\t}\n\n\tJSON::PP->new()->canonical()->encode($json);\n}\n\nsub get_json {\n\thttp_get(shift) =~ /\\x0d\\x0a?\\x0d\\x0a?(.*)/ms;\n\trecode($1);\n}\n\n###############################################################################\n\nsub http_daemon {\n\tmy $server = IO::Socket::INET->new(\n\t\tProto => 'tcp',\n\t\tLocalAddr => '127.0.0.1:' . port(8082),\n\t\tListen => 5,\n\t\tReuse => 1\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\tlocal $SIG{PIPE} = 'IGNORE';\n\n\twhile (my $client = $server->accept()) {\n\t\t$client->autoflush(1);\n\n\t\tmy $headers = '';\n\t\tmy $uri = '';\n\n\t\twhile (<$client>) {\n\t\t\t$headers .= $_;\n\t\t\tlast if (/^\\x0d?\\x0a?$/);\n\t\t}\n\n\t\t$uri = $1 if $headers =~ /^\\S+\\s+([^ ]+)\\s+HTTP/i;\n\n\t\tif ($uri eq '/unfinished') {\n\t\t\tprint $client\n\t\t\t\t\"HTTP/1.1 200 OK\" . CRLF .\n\t\t\t\t\"Transfer-Encoding: chunked\" . CRLF .\n\t\t\t\t\"Content-Length: 100\" . CRLF .\n\t\t\t\tCRLF .\n\t\t\t\t\"unfinished\" . CRLF;\n\t\t\tclose($client);\n\t\t}\n\t}\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/js_var.t",
    "content": "#!/usr/bin/perl\n\n# (C) Dmitry Volyntsev\n# (C) Nginx, Inc.\n\n# Tests for http njs module, js_var directive.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http rewrite/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    js_import test.js;\n\n    js_var $foo;\n    js_var $bar a:$arg_a;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location /test {\n            js_content test.test;\n        }\n\n        location /sub {\n            return 200 DONE;\n        }\n\n        location /dest {\n            return 200 $bar;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('test.js', <<EOF);\n    function test(r) {\n        if (r.args.sub) {\n            r.subrequest('/sub')\n            .then(reply => {\n                r.variables.bar = reply.responseText;\n                r.internalRedirect('/dest');\n            });\n\n            return;\n        }\n\n        r.return(200, `V:\\${r.variables[r.args.var]}`);\n    }\n\n    export default {test};\n\nEOF\n\n$t->try_run('no njs js_var')->plan(3);\n\n###############################################################################\n\nlike(http_get('/test?var=bar&a=qq'), qr/200 OK.*V:a:qq$/s, 'default value');\nlike(http_get('/test?var=foo'), qr/200 OK.*V:$/s, 'default empty value');\nlike(http_get('/test?sub=1&var=bar&a=qq'), qr/200 OK.*DONE$/s, 'value set');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/js_var2.t",
    "content": "#!/usr/bin/perl\n\n# (C) Dmitry Volyntsev\n# (C) Nginx, Inc.\n\n# Tests for http njs module, js_var directive in server | location contexts.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http rewrite/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    js_import test.js;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        js_var $foo;\n\n        location /test {\n            js_content test.test;\n        }\n\n        location /sub {\n            return 200 DONE;\n        }\n\n        location /dest {\n            js_var $bar a:$arg_a;\n            return 200 $bar;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('test.js', <<EOF);\n    function test(r) {\n        if (r.args.sub) {\n            r.subrequest('/sub')\n            .then(reply => {\n                r.variables.bar = reply.responseText;\n                r.internalRedirect('/dest');\n            });\n\n            return;\n        }\n\n        r.return(200, `V:\\${r.variables[r.args.var]}`);\n    }\n\n    export default {test};\n\nEOF\n\n$t->try_run('no njs js_var')->plan(3);\n\n###############################################################################\n\nlike(http_get('/test?var=bar&a=qq'), qr/200 OK.*V:a:qq$/s, 'default value');\nlike(http_get('/test?var=foo'), qr/200 OK.*V:$/s, 'default empty value');\nlike(http_get('/test?sub=1&var=bar&a=qq'), qr/200 OK.*DONE$/s, 'value set');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/js_variables.t",
    "content": "#!/usr/bin/perl\n\n# (C) Dmitry Volyntsev\n# (C) Nginx, Inc.\n\n# Tests for http njs module, setting nginx variables.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http rewrite/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    js_set $test_var   test.variable;\n\n    js_import test.js;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        set $foo       test.foo_orig;\n\n        location /var_set {\n            return 200 $test_var$foo;\n        }\n\n        location /content_set {\n            js_content test.content_set;\n        }\n\n        location /not_found_set {\n            js_content test.not_found_set;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('test.js', <<EOF);\n    function variable(r) {\n        r.variables.foo = r.variables.arg_a;\n        return 'test_var';\n    }\n\n    function content_set(r) {\n        r.variables.foo = r.variables.arg_a;\n        r.return(200, r.variables.foo);\n    }\n\n    function not_found_set(r) {\n        try {\n            r.variables.unknown = 1;\n        } catch (e) {\n            r.return(500, e);\n        }\n    }\n\n    export default {variable, content_set, not_found_set};\n\nEOF\n\n$t->try_run('no njs')->plan(3);\n\n###############################################################################\n\nlike(http_get('/var_set?a=bar'), qr/test_varbar/, 'var set');\nlike(http_get('/content_set?a=bar'), qr/bar/, 'content set');\nlike(http_get('/not_found_set'), qr/variable not found/, 'not found exception');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/lib/Test/Nginx/HTTP2.pm",
    "content": "package Test::Nginx::HTTP2;\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Module for nginx HTTP/2 tests.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More qw//;\nuse IO::Select;\nuse IO::Socket;\nuse Socket qw/ CRLF /;\nuse Data::Dumper;\n\nuse Test::Nginx;\n\nmy %cframe = (\n\t0 => { name => 'DATA', value => \\&data },\n\t1 => { name => 'HEADERS', value => \\&headers },\n#\t2 => { name => 'PRIORITY', value => \\&priority },\n\t3 => { name => 'RST_STREAM', value => \\&rst_stream },\n\t4 => { name => 'SETTINGS', value => \\&settings },\n\t5 => { name => 'PUSH_PROMISE', value => \\&push_promise },\n\t6 => { name => 'PING', value => \\&ping },\n\t7 => { name => 'GOAWAY', value => \\&goaway },\n\t8 => { name => 'WINDOW_UPDATE', value => \\&window_update },\n\t9 => { name => 'CONTINUATION', value => \\&continuation },\n);\n\nsub new {\n\tmy $class = shift;\n\tmy ($port, %extra) = @_;\n\n\tmy $s = $extra{socket} || new_socket($port, %extra);\n\tmy $preface = defined $extra{preface} ? $extra{preface}\n\t\t: 'PRI * HTTP/2.0' . CRLF . CRLF . 'SM' . CRLF . CRLF;\n\n\tmy $self = bless {\n\t\tsocket => $s, last_stream => -1,\n\t\tdynamic_encode => [ static_table() ],\n\t\tdynamic_decode => [ static_table() ],\n\t\tstatic_table_size => scalar @{[static_table()]},\n\t\tiws => 65535, conn_window => 65535, streams => {}\n\t}, $class;\n\n\tif ($extra{proxy}) {\n\t\t$self->raw_write($extra{proxy});\n\t}\n\n\t# preface\n\n\t$self->raw_write($preface);\n\n\treturn $self if $extra{pure};\n\n\t# update windows, if any\n\n\tmy $frames = $self->read(all => [\n\t\t{ type => 'WINDOW_UPDATE' },\n\t\t{ type => 'SETTINGS'}\n\t]);\n\n\t# 6.5.3.  Settings Synchronization\n\n\tif (grep { $_->{type} eq \"SETTINGS\" && $_->{flags} == 0 } @$frames) {\n\t\t$self->h2_settings(1);\n\t}\n\n\treturn $self;\n}\n\nsub h2_ping {\n\tmy ($self, $payload) = @_;\n\n\t$self->raw_write(pack(\"x2C2x5a8\", 8, 0x6, $payload));\n}\n\nsub h2_rst {\n\tmy ($self, $stream, $error) = @_;\n\n\t$self->raw_write(pack(\"x2C2xNN\", 4, 0x3, $stream, $error));\n}\n\nsub h2_goaway {\n\tmy ($self, $stream, $lstream, $err, $debug, %extra) = @_;\n\t$debug = '' unless defined $debug;\n\tmy $len = defined $extra{len} ? $extra{len} : 8 + length($debug);\n\tmy $buf = pack(\"x2C2xN3A*\", $len, 0x7, $stream, $lstream, $err, $debug);\n\n\tmy @bufs = map {\n\t\t$self->raw_write(substr $buf, 0, $_, \"\");\n\t\tselect undef, undef, undef, 0.2;\n\t} @{$extra{split}};\n\n\t$self->raw_write($buf);\n}\n\nsub h2_priority {\n\tmy ($self, $w, $stream, $dep, %extra) = @_;\n\n\t$stream = 0 unless defined $stream;\n\t$dep = 0 unless defined $dep;\n\t$dep |= $extra{excl} << 31 if exists $extra{excl};\n\t$self->raw_write(pack(\"x2C2xNNC\", 5, 0x2, $stream, $dep, $w));\n}\n\nsub h2_window {\n\tmy ($self, $win, $stream) = @_;\n\n\t$stream = 0 unless defined $stream;\n\t$self->raw_write(pack(\"x2C2xNN\", 4, 0x8, $stream, $win));\n}\n\nsub h2_settings {\n\tmy ($self, $ack, @pairs) = @_;\n\n\tmy $len = 6 * @pairs / 2;\n\tmy $buf = pack_length($len) . pack \"CCx4\", 0x4, $ack ? 0x1 : 0x0;\n\t$buf .= pack \"nN\", splice @pairs, 0, 2 while @pairs;\n\t$self->raw_write($buf);\n}\n\nsub h2_unknown {\n\tmy ($self, $payload) = @_;\n\n\tmy $buf = pack_length(length($payload)) . pack(\"Cx5a*\", 0xa, $payload);\n\t$self->raw_write($buf);\n}\n\nsub h2_continue {\n\tmy ($ctx, $stream, $uri) = @_;\n\n\t$uri->{h2_continue} = 1;\n\treturn new_stream($ctx, $uri, $stream);\n}\n\nsub h2_body {\n\tmy ($self, $body, $extra) = @_;\n\t$extra = {} unless defined $extra;\n\n\tmy $len = length $body;\n\tmy $sid = $self->{last_stream};\n\n\tif ($len > $self->{conn_window} || $len > $self->{streams}{$sid}) {\n\t\t$self->read(all => [{ type => 'WINDOW_UPDATE' }]);\n\t}\n\n\tif ($len > $self->{conn_window} || $len > $self->{streams}{$sid}) {\n\t\treturn;\n\t}\n\n\t$self->{conn_window} -= $len;\n\t$self->{streams}{$sid} -= $len;\n\n\tmy $buf;\n\n\tmy $split = ref $extra->{body_split} && $extra->{body_split} || [];\n\tfor (@$split) {\n\t\t$buf .= pack_body($self, substr($body, 0, $_, \"\"), 0x0, $extra);\n\t}\n\n\t$buf .= pack_body($self, $body, 0x1, $extra) if defined $body;\n\n\t$split = ref $extra->{split} && $extra->{split} || [];\n\tfor (@$split) {\n\t\t$self->raw_write(substr($buf, 0, $_, \"\"));\n\t\treturn if $extra->{abort};\n\t\tselect undef, undef, undef, ($extra->{split_delay} || 0.2);\n\t}\n\n\t$self->raw_write($buf);\n}\n\nsub new_stream {\n\tmy ($self, $uri, $stream) = @_;\n\tmy ($input, $buf);\n\tmy ($d, $status);\n\n\t$self->{headers} = '';\n\n\tmy $host = $uri->{host} || 'localhost';\n\tmy $method = $uri->{method} || 'GET';\n\tmy $scheme = $uri->{scheme} || 'http';\n\tmy $path = $uri->{path} || '/';\n\tmy $headers = $uri->{headers};\n\tmy $body = $uri->{body};\n\tmy $prio = $uri->{prio};\n\tmy $dep = $uri->{dep};\n\n\tmy $pad = defined $uri->{padding} ? $uri->{padding} : 0;\n\tmy $padlen = defined $uri->{padding} ? 1 : 0;\n\n\tmy $type = defined $uri->{h2_continue} ? 0x9 : 0x1;\n\tmy $flags = defined $uri->{continuation} ? 0x0 : 0x4;\n\t$flags |= 0x1 unless defined $body || defined $uri->{body_more}\n\t\t|| defined $uri->{h2_continue};\n\t$flags |= 0x8 if $padlen;\n\t$flags |= 0x20 if defined $dep || defined $prio;\n\n\tif ($stream) {\n\t\t$self->{last_stream} = $stream;\n\t} else {\n\t\t$self->{last_stream} += 2;\n\t}\n\t$self->{streams}{$self->{last_stream}} = $self->{iws};\n\n\t$buf = pack(\"xxx\");\t\t\t\t# Length stub\n\t$buf .= pack(\"CC\", $type, $flags);\t\t# END_HEADERS\n\t$buf .= pack(\"N\", $self->{last_stream});\t# Stream-ID\n\n\t$dep = 0 if defined $prio and not defined $dep;\n\t$prio = 16 if defined $dep and not defined $prio;\n\n\tunless ($headers) {\n\t\t$input = hpack($self, \":method\", $method);\n\t\t$input .= hpack($self, \":scheme\", $scheme);\n\t\t$input .= hpack($self, \":path\", $path);\n\t\t$input .= hpack($self, \":authority\", $host);\n\t\t$input .= hpack($self, \"content-length\", length($body))\n\t\t\tif $body;\n\n\t} else {\n\t\t$input = join '', map {\n\t\t\thpack($self, $_->{name}, $_->{value},\n\t\t\tmode => $_->{mode}, huff => $_->{huff})\n\t\t} @$headers if $headers;\n\t}\n\n\t$input = pack(\"B*\", '001' . ipack(5, $uri->{table_size})) . $input\n\t\tif defined $uri->{table_size};\n\n\tmy $split = ref $uri->{continuation} && $uri->{continuation} || [];\n\tmy @input = map { substr $input, 0, $_, \"\" } @$split;\n\tpush @input, $input;\n\n\t# set length, attach headers, padding, priority\n\n\tmy $hlen = length($input[0]) + $pad + $padlen;\n\t$hlen += 5 if $flags & 0x20;\n\t$buf |= pack_length($hlen);\n\n\t$buf .= pack 'C', $pad if $padlen;\t\t# Pad Length?\n\t$buf .= pack 'NC', $dep, $prio if $flags & 0x20;\n\t$buf .= $input[0];\n\t$buf .= (pack 'C', 0) x $pad if $padlen;\t# Padding\n\n\tshift @input;\n\n\twhile (@input) {\n\t\t$input = shift @input;\n\t\t$flags = @input ? 0x0 : 0x4;\n\t\t$buf .= pack_length(length($input));\n\t\t$buf .= pack(\"CC\", 0x9, $flags);\n\t\t$buf .= pack(\"N\", $self->{last_stream});\n\t\t$buf .= $input;\n\t}\n\n\t$split = ref $uri->{body_split} && $uri->{body_split} || [];\n\tfor (@$split) {\n\t\t$buf .= pack_body($self, substr($body, 0, $_, \"\"), 0x0, $uri);\n\t}\n\n\t$buf .= pack_body($self, $body, 0x1, $uri) if defined $body;\n\n\t$split = ref $uri->{split} && $uri->{split} || [];\n\tfor (@$split) {\n\t\t$self->raw_write(substr($buf, 0, $_, \"\"));\n\t\tgoto done if $uri->{abort};\n\t\tselect undef, undef, undef, ($uri->{split_delay} || 0.2);\n\t}\n\n\t$self->raw_write($buf);\ndone:\n\treturn $self->{last_stream};\n}\n\nsub read {\n\tmy ($self, %extra) = @_;\n\tmy (@got);\n\tmy $s = $self->{socket};\n\tmy $buf = '';\n\tmy $wait = $extra{wait};\n\n\tlocal $Data::Dumper::Terse = 1;\n\n\twhile (1) {\n\t\t$buf = $self->raw_read($buf, 9, $wait);\n\t\tlast if length $buf < 9;\n\n\t\tmy $length = unpack_length($buf);\n\t\tmy $type = unpack('x3C', $buf);\n\t\tmy $flags = unpack('x4C', $buf);\n\n\t\tmy $stream = unpack \"x5 B32\", $buf;\n\t\tsubstr($stream, 0, 1) = 0;\n\t\t$stream = unpack(\"N\", pack(\"B32\", $stream));\n\n\t\t$buf = $self->raw_read($buf, $length + 9, $wait);\n\t\tlast if length($buf) < $length + 9;\n\n\t\t$buf = substr($buf, 9);\n\n\t\tmy $frame = $cframe{$type}{value}($self, $buf, $length, $flags,\n\t\t\t$stream);\n\t\t$frame->{length} = $length;\n\t\t$frame->{type} = $cframe{$type}{name};\n\t\t$frame->{flags} = $flags;\n\t\t$frame->{sid} = $stream;\n\t\tpush @got, $frame;\n\n\t\tTest::Nginx::log_core('||', $_) for split \"\\n\", Dumper $frame;\n\n\t\t$buf = substr($buf, $length);\n\n\t\tlast unless $extra{all} && test_fin($got[-1], $extra{all});\n\t};\n\treturn \\@got;\n}\n\nsub raw_read {\n\tmy ($self, $buf, $len, $timo) = @_;\n\t$timo = 8 unless $timo;\n\tmy $got = '';\n\tmy $s = $self->{socket};\n\n\twhile (length($buf) < $len && IO::Select->new($s)->can_read($timo)) {\n\t\t$s->sysread($got, 16384) or last;\n\t\tlog_in($got);\n\t\t$buf .= $got;\n\t}\n\treturn $buf;\n}\n\nsub raw_write {\n\tmy ($self, $message) = @_;\n\n\tif ($self->{chaining}) {\n\t\treturn add_chain($self, $message);\n\t}\n\n\tmy $s = $self->{socket};\n\n\tlocal $SIG{PIPE} = 'IGNORE';\n\n\twhile (IO::Select->new($s)->can_write(0.4)) {\n\t\tlog_out($message);\n\t\tmy $n = $s->syswrite($message);\n\t\tlast unless $n;\n\t\t$message = substr($message, $n);\n\t\tlast unless length $message;\n\t}\n}\n\nsub start_chain {\n\tmy ($self) = @_;\n\n\t$self->{chaining} = 1;\n}\n\nsub add_chain {\n\tmy ($self, $buf) = @_;\n\n\tif ($self->{chained_buf}) {\n\t\t$self->{chained_buf} .= $buf;\n\t} else {\n\t\t$self->{chained_buf} = $buf;\n\t}\n}\n\nsub send_chain {\n\tmy ($self) = @_;\n\n\tundef $self->{chaining};\n\t$self->raw_write($self->{chained_buf}) if $self->{chained_buf};\n\tundef $self->{chained_buf};\n}\n\n###############################################################################\n\nsub pack_body {\n\tmy ($ctx, $body, $flags, $extra) = @_;\n\n\tmy $pad = defined $extra->{body_padding} ? $extra->{body_padding} : 0;\n\tmy $padlen = defined $extra->{body_padding} ? 1 : 0;\n\n\tmy $buf = pack_length(length($body) + $pad + $padlen);\n\t$flags |= 0x8 if $padlen;\n\tvec($flags, 0, 1) = 0 if $extra->{body_more};\n\t$buf .= pack 'CC', 0x0, $flags;\t\t# DATA, END_STREAM\n\t$buf .= pack 'N', $ctx->{last_stream};\n\t$buf .= pack 'C', $pad if $padlen;\t# DATA Pad Length?\n\t$buf .= $body;\n\t$buf .= pack \"x$pad\" if $padlen;\t# DATA Padding\n\treturn $buf;\n}\n\nsub test_fin {\n\tmy ($frame, $all) = @_;\n\tmy @test = @{$all};\n\n\t# wait for the specified DATA length\n\n\tfor (@test) {\n\t\tif ($_->{length} && $frame->{type} eq 'DATA') {\n\t\t\t# check also for StreamID if needed\n\n\t\t\tif (!$_->{sid} || $_->{sid} == $frame->{sid}) {\n\t\t\t\t$_->{length} -= $frame->{length};\n\t\t\t}\n\t\t}\n\t}\n\t@test = grep { !(defined $_->{length} && $_->{length} == 0) } @test;\n\n\t# wait for the fin flag\n\n\t@test = grep { !(defined $_->{fin}\n\t\t&& (!defined $_->{sid} || $_->{sid} == $frame->{sid})\n\t\t&& $_->{fin} & $frame->{flags})\n\t} @test if defined $frame->{flags};\n\n\t# wait for the specified frame\n\n\t@test = grep { !($_->{type} && $_->{type} eq $frame->{type}) } @test;\n\n\t@{$all} = @test;\n}\n\nsub headers {\n\tmy ($ctx, $buf, $len, $flags) = @_;\n\t$ctx->{headers} = substr($buf, 0, $len);\n\treturn unless $flags & 0x4;\n\t{ headers => hunpack($ctx, $buf, $len) };\n}\n\nsub continuation {\n\tmy ($ctx, $buf, $len, $flags) = @_;\n\tmy %payload;\n\n\t$ctx->{headers} .= substr($buf, 0, $len);\n\t$payload{promised} = $ctx->{promised} if $ctx->{promised};\n\treturn \\%payload unless $flags & 0x4;\n\n\t$ctx->{promised} = undef;\n\treturn { %payload, headers => hunpack($ctx, $ctx->{headers},\n\t\tlength($ctx->{headers})) };\n}\n\nsub data {\n\tmy ($ctx, $buf, $len) = @_;\n\treturn { data => substr($buf, 0, $len) };\n}\n\nsub settings {\n\tmy ($ctx, $buf, $len) = @_;\n\tmy %payload;\n\tmy $skip = 0;\n\n\tfor (1 .. $len / 6) {\n\t\tmy $id = hex unpack \"\\@$skip n\", $buf; $skip += 2;\n\t\t$payload{$id} = unpack \"\\@$skip N\", $buf; $skip += 4;\n\n\t\t$ctx->{iws} = $payload{$id} if $id == 4;\n\t}\n\treturn \\%payload;\n}\n\nsub push_promise {\n\tmy ($ctx, $buf, $len, $flags) = @_;\n\tmy %payload;\n\t$len -= 4;\n\n\t$ctx->{promised} = $payload{promised} = unpack(\"N\", $buf);\n\t$ctx->{headers} = substr($buf, 4, $len);\n\treturn \\%payload unless $flags & 0x4;\n\treturn { %payload, headers => hunpack($ctx, $ctx->{headers}, $len) };\n}\n\nsub ping {\n\tmy ($ctx, $buf, $len) = @_;\n\treturn { value => unpack \"A$len\", $buf };\n}\n\nsub rst_stream {\n\tmy ($ctx, $buf, $len) = @_;\n\treturn { code => unpack \"N\", $buf };\n}\n\nsub goaway {\n\tmy ($ctx, $buf, $len) = @_;\n\tmy %payload;\n\n\tmy $stream = unpack \"B32\", $buf;\n\tsubstr($stream, 0, 1) = 0;\n\t$stream = unpack(\"N\", pack(\"B32\", $stream));\n\t$payload{last_sid} = $stream;\n\n\t$len -= 4;\n\t$payload{code} = unpack \"x4 N\", $buf;\n\t$payload{debug} = unpack \"x8 A$len\", $buf;\n\treturn \\%payload;\n}\n\nsub window_update {\n\tmy ($ctx, $buf, $len, $flags, $sid) = @_;\n\tmy $value = unpack \"B32\", $buf;\n\tsubstr($value, 0, 1) = 0;\n\t$value = unpack(\"N\", pack(\"B32\", $value));\n\n\tunless ($sid) {\n\t\t$ctx->{conn_window} += $value;\n\n\t} else {\n\t\t$ctx->{streams}{$sid} = $ctx->{iws}\n\t\t\tunless defined $ctx->{streams}{$sid};\n\t\t$ctx->{streams}{$sid} += $value;\n\t}\n\n\treturn { wdelta => $value };\n}\n\nsub pack_length {\n\tpack 'c3', unpack 'xc3', pack 'N', $_[0];\n}\n\nsub unpack_length {\n\tunpack 'N', pack 'xc3', unpack 'c3', $_[0];\n}\n\nsub new_socket {\n\tmy ($port, %extra) = @_;\n\tmy $npn = $extra{'npn'};\n\tmy $alpn = $extra{'alpn'};\n\tmy $s;\n\n\t$port ||= port(8080);\n\n\teval {\n\t\tlocal $SIG{ALRM} = sub { die \"timeout\\n\" };\n\t\tlocal $SIG{PIPE} = sub { die \"sigpipe\\n\" };\n\t\talarm(8);\n\t\t$s = IO::Socket::INET->new(\n\t\t\tProto => 'tcp',\n\t\t\tPeerAddr => \"127.0.0.1:$port\",\n\t\t);\n\t\trequire IO::Socket::SSL if $extra{'SSL'};\n\t\tIO::Socket::SSL->start_SSL($s,\n\t\t\tSSL_verify_mode => IO::Socket::SSL::SSL_VERIFY_NONE(),\n\t\t\tSSL_npn_protocols => $npn ? [ $npn ] : undef,\n\t\t\tSSL_alpn_protocols => $alpn ? [ $alpn ] : undef,\n\t\t\tSSL_error_trap => sub { die $_[1] }\n\t\t) if $extra{'SSL'};\n\t\talarm(0);\n\t};\n\talarm(0);\n\n\tif ($@) {\n\t\tlog_in(\"died: $@\");\n\t\treturn undef;\n\t}\n\n\treturn $s;\n}\n\nsub static_table {\n\t[ '',\t\t\t''\t\t], # unused\n\t[ ':authority',\t\t''\t\t],\n\t[ ':method',\t\t'GET'\t\t],\n\t[ ':method',\t\t'POST'\t\t],\n\t[ ':path',\t\t'/'\t\t],\n\t[ ':path',\t\t'/index.html'\t],\n\t[ ':scheme',\t\t'http'\t\t],\n\t[ ':scheme',\t\t'https'\t\t],\n\t[ ':status',\t\t'200'\t\t],\n\t[ ':status',\t\t'204'\t\t],\n\t[ ':status',\t\t'206'\t\t],\n\t[ ':status',\t\t'304'\t\t],\n\t[ ':status',\t\t'400'\t\t],\n\t[ ':status',\t\t'404'\t\t],\n\t[ ':status',\t\t'500'\t\t],\n\t[ 'accept-charset',\t''\t\t],\n\t[ 'accept-encoding',\t'gzip, deflate'\t],\n\t[ 'accept-language',\t''\t\t],\n\t[ 'accept-ranges',\t''\t\t],\n\t[ 'accept',\t\t''\t\t],\n\t[ 'access-control-allow-origin',\n\t\t\t\t''\t\t],\n\t[ 'age',\t\t''\t\t],\n\t[ 'allow',\t\t''\t\t],\n\t[ 'authorization',\t''\t\t],\n\t[ 'cache-control',\t''\t\t],\n\t[ 'content-disposition',\n\t\t\t\t''\t\t],\n\t[ 'content-encoding',\t''\t\t],\n\t[ 'content-language',\t''\t\t],\n\t[ 'content-length',\t''\t\t],\n\t[ 'content-location',\t''\t\t],\n\t[ 'content-range',\t''\t\t],\n\t[ 'content-type',\t''\t\t],\n\t[ 'cookie',\t\t''\t\t],\n\t[ 'date',\t\t''\t\t],\n\t[ 'etag',\t\t''\t\t],\n\t[ 'expect',\t\t''\t\t],\n\t[ 'expires',\t\t''\t\t],\n\t[ 'from',\t\t''\t\t],\n\t[ 'host',\t\t''\t\t],\n\t[ 'if-match',\t\t''\t\t],\n\t[ 'if-modified-since',\t''\t\t],\n\t[ 'if-none-match',\t''\t\t],\n\t[ 'if-range',\t\t''\t\t],\n\t[ 'if-unmodified-since',\n\t\t\t\t''\t\t],\n\t[ 'last-modified',\t''\t\t],\n\t[ 'link',\t\t''\t\t],\n\t[ 'location',\t\t''\t\t],\n\t[ 'max-forwards',\t''\t\t],\n\t[ 'proxy-authenticate',\t''\t\t],\n\t[ 'proxy-authorization',\n\t\t\t\t''\t\t],\n\t[ 'range',\t\t''\t\t],\n\t[ 'referer',\t\t''\t\t],\n\t[ 'refresh',\t\t''\t\t],\n\t[ 'retry-after',\t''\t\t],\n\t[ 'server',\t\t''\t\t],\n\t[ 'set-cookie',\t\t''\t\t],\n\t[ 'strict-transport-security',\n\t\t\t\t''\t\t],\n\t[ 'transfer-encoding',\t''\t\t],\n\t[ 'user-agent',\t\t''\t\t],\n\t[ 'vary',\t\t''\t\t],\n\t[ 'via',\t\t''\t\t],\n\t[ 'www-authenticate',\t''\t\t],\n}\n\n# RFC 7541, 5.1.  Integer Representation\n\nsub ipack {\n\tmy ($base, $d) = @_;\n\treturn sprintf(\"%.*b\", $base, $d) if $d < 2**$base - 1;\n\n\tmy $o = sprintf(\"%${base}b\", 2**$base - 1);\n\t$d -= 2**$base - 1;\n\twhile ($d >= 128) {\n\t\t$o .= sprintf(\"%8b\", $d % 128 + 128);\n\t\t$d /= 128;\n\t}\n\t$o .= sprintf(\"%08b\", $d);\n\treturn $o;\n}\n\nsub iunpack {\n\tmy ($base, $b, $s) = @_;\n\n\tmy $len = unpack(\"\\@$s B8\", $b); $s++;\n\tmy $prefix = substr($len, 0, 8 - $base);\n\t$len = '0' x (8 - $base) . substr($len, 8 - $base);\n\t$len = unpack(\"C\", pack(\"B8\", $len));\n\n\treturn ($len, $s, $prefix) if $len < 2**$base - 1;\n\n\tmy $m = 0;\n\tmy $d;\n\n\tdo {\n\t\t$d = unpack(\"\\@$s C\", $b); $s++;\n\t\t$len += ($d & 127) * 2**$m;\n\t\t$m += $base;\n\t} while (($d & 128) == 128);\n\n\treturn ($len, $s, $prefix);\n}\n\nsub hpack {\n\tmy ($ctx, $name, $value, %extra) = @_;\n\tmy $table = $ctx->{dynamic_encode};\n\tmy $mode = defined $extra{mode} ? $extra{mode} : 1;\n\tmy $huff = $extra{huff};\n\n\tmy ($index, $buf) = 0;\n\n\t# 6.1.  Indexed Header Field Representation\n\n\tif ($mode == 0) {\n\t\t++$index until $index > $#$table\n\t\t\tor $table->[$index][0] eq $name\n\t\t\tand $table->[$index][1] eq $value;\n\t\t$buf = pack('B*', '1' . ipack(7, $index));\n\t}\n\n\t# 6.2.1.  Literal Header Field with Incremental Indexing\n\n\tif ($mode == 1) {\n\t\tsplice @$table, $ctx->{static_table_size}, 0, [ $name, $value ];\n\n\t\t++$index until $index > $#$table\n\t\t\tor $table->[$index][0] eq $name;\n\t\tmy $value = $huff ? huff($value) : $value;\n\n\t\t$buf = pack('B*', '01' . ipack(6, $index)\n\t\t\t. ($huff ? '1' : '0') . ipack(7, length($value)));\n\t\t$buf .= $value;\n\t}\n\n\t# 6.2.1.  Literal Header Field with Incremental Indexing -- New Name\n\n\tif ($mode == 2) {\n\t\tsplice @$table, $ctx->{static_table_size}, 0, [ $name, $value ];\n\n\t\tmy $name = $huff ? huff($name) : $name;\n\t\tmy $value = $huff ? huff($value) : $value;\n\t\tmy $hbit = ($huff ? '1' : '0');\n\n\t\t$buf = pack('B*', '01000000');\n\t\t$buf .= pack('B*', $hbit . ipack(7, length($name)));\n\t\t$buf .= $name;\n\t\t$buf .= pack('B*', $hbit . ipack(7, length($value)));\n\t\t$buf .= $value;\n\t}\n\n\t# 6.2.2.  Literal Header Field without Indexing\n\n\tif ($mode == 3) {\n\t\t++$index until $index > $#$table\n\t\t\tor $table->[$index][0] eq $name;\n\t\tmy $value = $huff ? huff($value) : $value;\n\n\t\t$buf = pack('B*', '0000' . ipack(4, $index)\n\t\t\t. ($huff ? '1' : '0') . ipack(7, length($value)));\n\t\t$buf .= $value;\n\t}\n\n\t# 6.2.2.  Literal Header Field without Indexing -- New Name\n\n\tif ($mode == 4) {\n\t\tmy $name = $huff ? huff($name) : $name;\n\t\tmy $value = $huff ? huff($value) : $value;\n\t\tmy $hbit = ($huff ? '1' : '0');\n\n\t\t$buf = pack('B*', '00000000');\n\t\t$buf .= pack('B*', $hbit . ipack(7, length($name)));\n\t\t$buf .= $name;\n\t\t$buf .= pack('B*', $hbit . ipack(7, length($value)));\n\t\t$buf .= $value;\n\t}\n\n\t# 6.2.3.  Literal Header Field Never Indexed\n\n\tif ($mode == 5) {\n\t\t++$index until $index > $#$table\n\t\t\tor $table->[$index][0] eq $name;\n\t\tmy $value = $huff ? huff($value) : $value;\n\n\t\t$buf = pack('B*', '0001' . ipack(4, $index)\n\t\t\t. ($huff ? '1' : '0') . ipack(7, length($value)));\n\t\t$buf .= $value;\n\t}\n\n\t# 6.2.3.  Literal Header Field Never Indexed -- New Name\n\n\tif ($mode == 6) {\n\t\tmy $name = $huff ? huff($name) : $name;\n\t\tmy $value = $huff ? huff($value) : $value;\n\t\tmy $hbit = ($huff ? '1' : '0');\n\n\t\t$buf = pack('B*', '00010000');\n\t\t$buf .= pack('B*', $hbit . ipack(7, length($name)));\n\t\t$buf .= $name;\n\t\t$buf .= pack('B*', $hbit . ipack(7, length($value)));\n\t\t$buf .= $value;\n\t}\n\n\treturn $buf;\n}\n\nsub hunpack {\n\tmy ($ctx, $data, $length) = @_;\n\tmy $table = $ctx->{dynamic_decode};\n\tmy %headers;\n\tmy $skip = 0;\n\tmy ($index, $name, $value, $size);\n\n\tmy $field = sub {\n\t\tmy ($b) = @_;\n\t\tmy ($len, $s, $huff) = iunpack(7, @_);\n\n\t\tmy $field = substr($b, $s, $len);\n\t\t$field = $huff ? dehuff($field) : $field;\n\t\t$s += $len;\n\t\treturn ($field, $s);\n\t};\n\n\tmy $add = sub {\n\t\tmy ($h, $n, $v) = @_;\n\t\treturn $h->{$n} = $v unless exists $h->{$n};\n\t\t$h->{$n} = [ $h->{$n} ] unless ref $h->{$n};\n\t\tpush @{$h->{$n}}, $v;\n\t};\n\n\twhile ($skip < $length) {\n\t\tmy $ib = unpack(\"\\@$skip B8\", $data);\n\n\t\tif (substr($ib, 0, 1) eq '1') {\n\t\t\t($index, $skip) = iunpack(7, $data, $skip);\n\t\t\t$add->(\\%headers,\n\t\t\t\t$table->[$index][0], $table->[$index][1]);\n\t\t\tnext;\n\t\t}\n\n\t\tif (substr($ib, 0, 2) eq '01') {\n\t\t\t($index, $skip) = iunpack(6, $data, $skip);\n\t\t\t$name = $table->[$index][0];\n\n\t\t\t($name, $skip) = $field->($data, $skip) unless $name;\n\t\t\t($value, $skip) = $field->($data, $skip);\n\n\t\t\tsplice @$table,\n\t\t\t\t$ctx->{static_table_size}, 0, [ $name, $value ];\n\t\t\t$add->(\\%headers, $name, $value);\n\t\t\tnext;\n\t\t}\n\n\t\tif (substr($ib, 0, 4) eq '0000') {\n\t\t\t($index, $skip) = iunpack(4, $data, $skip);\n\t\t\t$name = $table->[$index][0];\n\n\t\t\t($name, $skip) = $field->($data, $skip) unless $name;\n\t\t\t($value, $skip) = $field->($data, $skip);\n\n\t\t\t$add->(\\%headers, $name, $value);\n\t\t\tnext;\n\t\t}\n\n\t\tif (substr($ib, 0, 3) eq '001') {\n\t\t\t($size, $skip) = iunpack(5, $data, $skip);\n\n\t\t\t# TODO: handle dynamic table size update\n\n\t\t\tnext;\n\t\t}\n\n\t\tlast;\n\t}\n\n\treturn \\%headers;\n}\n\nsub huff_code { scalar {\n\tpack('C', 0)\t=> '1111111111000',\n\tpack('C', 1)\t=> '11111111111111111011000',\n\tpack('C', 2)\t=> '1111111111111111111111100010',\n\tpack('C', 3)\t=> '1111111111111111111111100011',\n\tpack('C', 4)\t=> '1111111111111111111111100100',\n\tpack('C', 5)\t=> '1111111111111111111111100101',\n\tpack('C', 6)\t=> '1111111111111111111111100110',\n\tpack('C', 7)\t=> '1111111111111111111111100111',\n\tpack('C', 8)\t=> '1111111111111111111111101000',\n\tpack('C', 9)\t=> '111111111111111111101010',\n\tpack('C', 10)\t=> '111111111111111111111111111100',\n\tpack('C', 11)\t=> '1111111111111111111111101001',\n\tpack('C', 12)\t=> '1111111111111111111111101010',\n\tpack('C', 13)\t=> '111111111111111111111111111101',\n\tpack('C', 14)\t=> '1111111111111111111111101011',\n\tpack('C', 15)\t=> '1111111111111111111111101100',\n\tpack('C', 16)\t=> '1111111111111111111111101101',\n\tpack('C', 17)\t=> '1111111111111111111111101110',\n\tpack('C', 18)\t=> '1111111111111111111111101111',\n\tpack('C', 19)\t=> '1111111111111111111111110000',\n\tpack('C', 20)\t=> '1111111111111111111111110001',\n\tpack('C', 21)\t=> '1111111111111111111111110010',\n\tpack('C', 22)\t=> '111111111111111111111111111110',\n\tpack('C', 23)\t=> '1111111111111111111111110011',\n\tpack('C', 24)\t=> '1111111111111111111111110100',\n\tpack('C', 25)\t=> '1111111111111111111111110101',\n\tpack('C', 26)\t=> '1111111111111111111111110110',\n\tpack('C', 27)\t=> '1111111111111111111111110111',\n\tpack('C', 28)\t=> '1111111111111111111111111000',\n\tpack('C', 29)\t=> '1111111111111111111111111001',\n\tpack('C', 30)\t=> '1111111111111111111111111010',\n\tpack('C', 31)\t=> '1111111111111111111111111011',\n\tpack('C', 32)\t=> '010100',\n\tpack('C', 33)\t=> '1111111000',\n\tpack('C', 34)\t=> '1111111001',\n\tpack('C', 35)\t=> '111111111010',\n\tpack('C', 36)\t=> '1111111111001',\n\tpack('C', 37)\t=> '010101',\n\tpack('C', 38)\t=> '11111000',\n\tpack('C', 39)\t=> '11111111010',\n\tpack('C', 40)\t=> '1111111010',\n\tpack('C', 41)\t=> '1111111011',\n\tpack('C', 42)\t=> '11111001',\n\tpack('C', 43)\t=> '11111111011',\n\tpack('C', 44)\t=> '11111010',\n\tpack('C', 45)\t=> '010110',\n\tpack('C', 46)\t=> '010111',\n\tpack('C', 47)\t=> '011000',\n\tpack('C', 48)\t=> '00000',\n\tpack('C', 49)\t=> '00001',\n\tpack('C', 50)\t=> '00010',\n\tpack('C', 51)\t=> '011001',\n\tpack('C', 52)\t=> '011010',\n\tpack('C', 53)\t=> '011011',\n\tpack('C', 54)\t=> '011100',\n\tpack('C', 55)\t=> '011101',\n\tpack('C', 56)\t=> '011110',\n\tpack('C', 57)\t=> '011111',\n\tpack('C', 58)\t=> '1011100',\n\tpack('C', 59)\t=> '11111011',\n\tpack('C', 60)\t=> '111111111111100',\n\tpack('C', 61)\t=> '100000',\n\tpack('C', 62)\t=> '111111111011',\n\tpack('C', 63)\t=> '1111111100',\n\tpack('C', 64)\t=> '1111111111010',\n\tpack('C', 65)\t=> '100001',\n\tpack('C', 66)\t=> '1011101',\n\tpack('C', 67)\t=> '1011110',\n\tpack('C', 68)\t=> '1011111',\n\tpack('C', 69)\t=> '1100000',\n\tpack('C', 70)\t=> '1100001',\n\tpack('C', 71)\t=> '1100010',\n\tpack('C', 72)\t=> '1100011',\n\tpack('C', 73)\t=> '1100100',\n\tpack('C', 74)\t=> '1100101',\n\tpack('C', 75)\t=> '1100110',\n\tpack('C', 76)\t=> '1100111',\n\tpack('C', 77)\t=> '1101000',\n\tpack('C', 78)\t=> '1101001',\n\tpack('C', 79)\t=> '1101010',\n\tpack('C', 80)\t=> '1101011',\n\tpack('C', 81)\t=> '1101100',\n\tpack('C', 82)\t=> '1101101',\n\tpack('C', 83)\t=> '1101110',\n\tpack('C', 84)\t=> '1101111',\n\tpack('C', 85)\t=> '1110000',\n\tpack('C', 86)\t=> '1110001',\n\tpack('C', 87)\t=> '1110010',\n\tpack('C', 88)\t=> '11111100',\n\tpack('C', 89)\t=> '1110011',\n\tpack('C', 90)\t=> '11111101',\n\tpack('C', 91)\t=> '1111111111011',\n\tpack('C', 92)\t=> '1111111111111110000',\n\tpack('C', 93)\t=> '1111111111100',\n\tpack('C', 94)\t=> '11111111111100',\n\tpack('C', 95)\t=> '100010',\n\tpack('C', 96)\t=> '111111111111101',\n\tpack('C', 97)\t=> '00011',\n\tpack('C', 98)\t=> '100011',\n\tpack('C', 99)\t=> '00100',\n\tpack('C', 100)\t=> '100100',\n\tpack('C', 101)\t=> '00101',\n\tpack('C', 102)\t=> '100101',\n\tpack('C', 103)\t=> '100110',\n\tpack('C', 104)\t=> '100111',\n\tpack('C', 105)\t=> '00110',\n\tpack('C', 106)\t=> '1110100',\n\tpack('C', 107)\t=> '1110101',\n\tpack('C', 108)\t=> '101000',\n\tpack('C', 109)\t=> '101001',\n\tpack('C', 110)\t=> '101010',\n\tpack('C', 111)\t=> '00111',\n\tpack('C', 112)\t=> '101011',\n\tpack('C', 113)\t=> '1110110',\n\tpack('C', 114)\t=> '101100',\n\tpack('C', 115)\t=> '01000',\n\tpack('C', 116)\t=> '01001',\n\tpack('C', 117)\t=> '101101',\n\tpack('C', 118)\t=> '1110111',\n\tpack('C', 119)\t=> '1111000',\n\tpack('C', 120)\t=> '1111001',\n\tpack('C', 121)\t=> '1111010',\n\tpack('C', 122)\t=> '1111011',\n\tpack('C', 123)\t=> '111111111111110',\n\tpack('C', 124)\t=> '11111111100',\n\tpack('C', 125)\t=> '11111111111101',\n\tpack('C', 126)\t=> '1111111111101',\n\tpack('C', 127)\t=> '1111111111111111111111111100',\n\tpack('C', 128)\t=> '11111111111111100110',\n\tpack('C', 129)\t=> '1111111111111111010010',\n\tpack('C', 130)\t=> '11111111111111100111',\n\tpack('C', 131)\t=> '11111111111111101000',\n\tpack('C', 132)\t=> '1111111111111111010011',\n\tpack('C', 133)\t=> '1111111111111111010100',\n\tpack('C', 134)\t=> '1111111111111111010101',\n\tpack('C', 135)\t=> '11111111111111111011001',\n\tpack('C', 136)\t=> '1111111111111111010110',\n\tpack('C', 137)\t=> '11111111111111111011010',\n\tpack('C', 138)\t=> '11111111111111111011011',\n\tpack('C', 139)\t=> '11111111111111111011100',\n\tpack('C', 140)\t=> '11111111111111111011101',\n\tpack('C', 141)\t=> '11111111111111111011110',\n\tpack('C', 142)\t=> '111111111111111111101011',\n\tpack('C', 143)\t=> '11111111111111111011111',\n\tpack('C', 144)\t=> '111111111111111111101100',\n\tpack('C', 145)\t=> '111111111111111111101101',\n\tpack('C', 146)\t=> '1111111111111111010111',\n\tpack('C', 147)\t=> '11111111111111111100000',\n\tpack('C', 148)\t=> '111111111111111111101110',\n\tpack('C', 149)\t=> '11111111111111111100001',\n\tpack('C', 150)\t=> '11111111111111111100010',\n\tpack('C', 151)\t=> '11111111111111111100011',\n\tpack('C', 152)\t=> '11111111111111111100100',\n\tpack('C', 153)\t=> '111111111111111011100',\n\tpack('C', 154)\t=> '1111111111111111011000',\n\tpack('C', 155)\t=> '11111111111111111100101',\n\tpack('C', 156)\t=> '1111111111111111011001',\n\tpack('C', 157)\t=> '11111111111111111100110',\n\tpack('C', 158)\t=> '11111111111111111100111',\n\tpack('C', 159)\t=> '111111111111111111101111',\n\tpack('C', 160)\t=> '1111111111111111011010',\n\tpack('C', 161)\t=> '111111111111111011101',\n\tpack('C', 162)\t=> '11111111111111101001',\n\tpack('C', 163)\t=> '1111111111111111011011',\n\tpack('C', 164)\t=> '1111111111111111011100',\n\tpack('C', 165)\t=> '11111111111111111101000',\n\tpack('C', 166)\t=> '11111111111111111101001',\n\tpack('C', 167)\t=> '111111111111111011110',\n\tpack('C', 168)\t=> '11111111111111111101010',\n\tpack('C', 169)\t=> '1111111111111111011101',\n\tpack('C', 170)\t=> '1111111111111111011110',\n\tpack('C', 171)\t=> '111111111111111111110000',\n\tpack('C', 172)\t=> '111111111111111011111',\n\tpack('C', 173)\t=> '1111111111111111011111',\n\tpack('C', 174)\t=> '11111111111111111101011',\n\tpack('C', 175)\t=> '11111111111111111101100',\n\tpack('C', 176)\t=> '111111111111111100000',\n\tpack('C', 177)\t=> '111111111111111100001',\n\tpack('C', 178)\t=> '1111111111111111100000',\n\tpack('C', 179)\t=> '111111111111111100010',\n\tpack('C', 180)\t=> '11111111111111111101101',\n\tpack('C', 181)\t=> '1111111111111111100001',\n\tpack('C', 182)\t=> '11111111111111111101110',\n\tpack('C', 183)\t=> '11111111111111111101111',\n\tpack('C', 184)\t=> '11111111111111101010',\n\tpack('C', 185)\t=> '1111111111111111100010',\n\tpack('C', 186)\t=> '1111111111111111100011',\n\tpack('C', 187)\t=> '1111111111111111100100',\n\tpack('C', 188)\t=> '11111111111111111110000',\n\tpack('C', 189)\t=> '1111111111111111100101',\n\tpack('C', 190)\t=> '1111111111111111100110',\n\tpack('C', 191)\t=> '11111111111111111110001',\n\tpack('C', 192)\t=> '11111111111111111111100000',\n\tpack('C', 193)\t=> '11111111111111111111100001',\n\tpack('C', 194)\t=> '11111111111111101011',\n\tpack('C', 195)\t=> '1111111111111110001',\n\tpack('C', 196)\t=> '1111111111111111100111',\n\tpack('C', 197)\t=> '11111111111111111110010',\n\tpack('C', 198)\t=> '1111111111111111101000',\n\tpack('C', 199)\t=> '1111111111111111111101100',\n\tpack('C', 200)\t=> '11111111111111111111100010',\n\tpack('C', 201)\t=> '11111111111111111111100011',\n\tpack('C', 202)\t=> '11111111111111111111100100',\n\tpack('C', 203)\t=> '111111111111111111111011110',\n\tpack('C', 204)\t=> '111111111111111111111011111',\n\tpack('C', 205)\t=> '11111111111111111111100101',\n\tpack('C', 206)\t=> '111111111111111111110001',\n\tpack('C', 207)\t=> '1111111111111111111101101',\n\tpack('C', 208)\t=> '1111111111111110010',\n\tpack('C', 209)\t=> '111111111111111100011',\n\tpack('C', 210)\t=> '11111111111111111111100110',\n\tpack('C', 211)\t=> '111111111111111111111100000',\n\tpack('C', 212)\t=> '111111111111111111111100001',\n\tpack('C', 213)\t=> '11111111111111111111100111',\n\tpack('C', 214)\t=> '111111111111111111111100010',\n\tpack('C', 215)\t=> '111111111111111111110010',\n\tpack('C', 216)\t=> '111111111111111100100',\n\tpack('C', 217)\t=> '111111111111111100101',\n\tpack('C', 218)\t=> '11111111111111111111101000',\n\tpack('C', 219)\t=> '11111111111111111111101001',\n\tpack('C', 220)\t=> '1111111111111111111111111101',\n\tpack('C', 221)\t=> '111111111111111111111100011',\n\tpack('C', 222)\t=> '111111111111111111111100100',\n\tpack('C', 223)\t=> '111111111111111111111100101',\n\tpack('C', 224)\t=> '11111111111111101100',\n\tpack('C', 225)\t=> '111111111111111111110011',\n\tpack('C', 226)\t=> '11111111111111101101',\n\tpack('C', 227)\t=> '111111111111111100110',\n\tpack('C', 228)\t=> '1111111111111111101001',\n\tpack('C', 229)\t=> '111111111111111100111',\n\tpack('C', 230)\t=> '111111111111111101000',\n\tpack('C', 231)\t=> '11111111111111111110011',\n\tpack('C', 232)\t=> '1111111111111111101010',\n\tpack('C', 233)\t=> '1111111111111111101011',\n\tpack('C', 234)\t=> '1111111111111111111101110',\n\tpack('C', 235)\t=> '1111111111111111111101111',\n\tpack('C', 236)\t=> '111111111111111111110100',\n\tpack('C', 237)\t=> '111111111111111111110101',\n\tpack('C', 238)\t=> '11111111111111111111101010',\n\tpack('C', 239)\t=> '11111111111111111110100',\n\tpack('C', 240)\t=> '11111111111111111111101011',\n\tpack('C', 241)\t=> '111111111111111111111100110',\n\tpack('C', 242)\t=> '11111111111111111111101100',\n\tpack('C', 243)\t=> '11111111111111111111101101',\n\tpack('C', 244)\t=> '111111111111111111111100111',\n\tpack('C', 245)\t=> '111111111111111111111101000',\n\tpack('C', 246)\t=> '111111111111111111111101001',\n\tpack('C', 247)\t=> '111111111111111111111101010',\n\tpack('C', 248)\t=> '111111111111111111111101011',\n\tpack('C', 249)\t=> '1111111111111111111111111110',\n\tpack('C', 250)\t=> '111111111111111111111101100',\n\tpack('C', 251)\t=> '111111111111111111111101101',\n\tpack('C', 252)\t=> '111111111111111111111101110',\n\tpack('C', 253)\t=> '111111111111111111111101111',\n\tpack('C', 254)\t=> '111111111111111111111110000',\n\tpack('C', 255)\t=> '11111111111111111111101110',\n\t'_eos'\t\t=> '111111111111111111111111111111',\n}};\n\nsub huff {\n\tmy ($string) = @_;\n\tmy $code = &huff_code;\n\n\tmy $ret = join '', map { $code->{$_} } (split //, $string);\n\tmy $len = length($ret) + (8 - length($ret) % 8);\n\t$ret .= $code->{_eos};\n\n\treturn pack(\"B$len\", $ret);\n}\n\nsub dehuff {\n\tmy ($string) = @_;\n\tmy $code = &huff_code;\n\tmy %decode = reverse %$code;\n\n\tmy $ret = ''; my $c = '';\n\tfor (split //, unpack('B*', $string)) {\n\t\t$c .= $_;\n\t\tnext unless exists $decode{$c};\n\t\tlast if $decode{$c} eq '_eos';\n\n\t\t$ret .= $decode{$c};\n\t\t$c = '';\n\t}\n\n\treturn $ret;\n}\n\n###############################################################################\n\n1;\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/lib/Test/Nginx/IMAP.pm",
    "content": "package Test::Nginx::IMAP;\n\n# (C) Maxim Dounin\n\n# Module for nginx imap tests.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More qw//;\nuse IO::Select;\nuse IO::Socket;\nuse Socket qw/ CRLF /;\n\nuse Test::Nginx;\n\nsub new {\n\tmy $self = {};\n\tbless $self, shift @_;\n\n\tmy $port = {@_}->{'SSL'} ? 8993 : 8143;\n\teval {\n\t\tlocal $SIG{ALRM} = sub { die \"timeout\\n\" };\n\t\tlocal $SIG{PIPE} = sub { die \"sigpipe\\n\" };\n\t\talarm(8);\n\t$self->{_socket} = IO::Socket::INET->new(\n\t\tProto => \"tcp\",\n\t\t\tPeerAddr => \"127.0.0.1:\" . port($port),\n\t\t@_\n\t)\n\t\tor die \"Can't connect to nginx: $!\\n\";\n\n\tif ({@_}->{'SSL'}) {\n\t\trequire IO::Socket::SSL;\n\t\t\tIO::Socket::SSL->start_SSL(\n\t\t\t\t$self->{_socket},\n\t\t\t\tSSL_verify_mode =>\n\t\t\t\t\tIO::Socket::SSL::SSL_VERIFY_NONE(),\n\t\t\t\t@_\n\t\t\t)\n\t\t\tor die $IO::Socket::SSL::SSL_ERROR . \"\\n\";\n\t\t\tmy $s = $self->{_socket};\n\t\t\tlog_in(\"ssl cipher: \" . $s->get_cipher());\n\t\t\tlog_in(\"ssl cert: \" . $s->peer_certificate('issuer'));\n\t\t}\n\t\talarm(0);\n\t};\n\talarm(0);\n\tif ($@) {\n\t\tlog_in(\"died: $@\");\n\t}\n\n\t$self->{_socket}->autoflush(1);\n\t$self->{_read_buffer} = '';\n\n\treturn $self;\n}\nsub DESTROY {\n\tmy $self = shift;\n\t$self->{_socket}->close();\n}\n\nsub eof {\n\tmy $self = shift;\n\treturn $self->{_socket}->eof();\n}\n\nsub print {\n\tmy ($self, $cmd) = @_;\n\tlog_out($cmd);\n\t$self->{_socket}->print($cmd);\n}\n\nsub send {\n\tmy ($self, $cmd) = @_;\n\tlog_out($cmd);\n\t$self->{_socket}->print($cmd . CRLF);\n}\n\nsub getline {\n\tmy ($self) = @_;\n\tmy $socket = $self->{_socket};\n\n\tif ($self->{_read_buffer} =~ /^(.*?\\x0a)(.*)/ms) {\n\t\t$self->{_read_buffer} = $2;\n\t\treturn $1;\n\t}\n\n\twhile (IO::Select->new($socket)->can_read(8)) {\n\t        $socket->blocking(0);\n\t\tmy $n = $socket->sysread(my $buf, 1024);\n\t\tmy $again = !defined $n && $!{EWOULDBLOCK};\n\t        $socket->blocking(1);\n\t\tnext if $again;\n\t\tlast unless $n;\n\n\t\t$self->{_read_buffer} .= $buf;\n\n\t\tif ($self->{_read_buffer} =~ /^(.*?\\x0a)(.*)/ms) {\n\t\t\t$self->{_read_buffer} = $2;\n\t\t\treturn $1;\n\t\t}\n\t};\n}\n\nsub read {\n\tmy ($self) = @_;\n\tmy $socket = $self->{_socket};\n\n\twhile (defined($_ = $self->getline())) {\n\t\tlog_in($_);\n\t\tlast;\n\t}\n\n\treturn $_;\n}\n\nsub check {\n\tmy ($self, $regex, $name) = @_;\n\tTest::More->builder->like($self->read(), $regex, $name);\n}\n\nsub ok {\n\tmy $self = shift;\n\tTest::More->builder->like($self->read(), qr/^\\S+ OK/, @_);\n}\n\nsub can_read {\n\tmy ($self, $timo) = @_;\n\tIO::Select->new($self->{_socket})->can_read($timo || 3);\n}\nsub socket {\n\tmy ($self) = @_;\n\t$self->{_socket};\n}\n\n###############################################################################\n\nsub imap_test_daemon {\n\tmy ($port) = @_;\n\n\tmy $server = IO::Socket::INET->new(\n\t\tProto => 'tcp',\n\t\tLocalAddr => '127.0.0.1:' . ($port || port(8144)),\n\t\tListen => 5,\n\t\tReuse => 1\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\twhile (my $client = $server->accept()) {\n\t\t$client->autoflush(1);\n\t\tprint $client \"* OK fake imap server ready\" . CRLF;\n\n\t\twhile (<$client>) {\n\t\t\tTest::Nginx::log_core('||', $_);\n\n\t\t\twhile (m/{(\\d+)}\\x0d?$/) {\n\t\t\t\tprint $client '+ ' . CRLF;\n\t\t\t\t$client->sysread(my $buf, $1);\n\t\t\t\tTest::Nginx::log_core('||', $buf);\n\t\t\t\t$buf = <$client>;\n\t\t\t\tTest::Nginx::log_core('||', $buf);\n\t\t\t\t$_ .= $buf;\n\t\t\t}\n\n\t\t\tmy $tag = '';\n\n\t\t\t$tag = $1 if m/^(\\S+)/;\n\t\t\ts/^(\\S+)\\s+//;\n\n\t\t\tif (/^logout/i) {\n\t\t\t\tprint $client $tag . ' OK logout ok' . CRLF;\n\t\t\t} elsif (/^login /i) {\n\t\t\t\tprint $client $tag . ' OK login ok' . CRLF;\n\t\t\t} else {\n\t\t\t\tprint $client $tag . ' ERR unknown command' . CRLF;\n\t\t\t}\n\t\t}\n\n\t\tclose $client;\n\t}\n}\n\n###############################################################################\n\n1;\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/lib/Test/Nginx/POP3.pm",
    "content": "package Test::Nginx::POP3;\n\n# (C) Maxim Dounin\n\n# Module for nginx pop3 tests.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More qw//;\nuse IO::Select;\nuse IO::Socket;\nuse Socket qw/ CRLF /;\n\nuse Test::Nginx;\n\nsub new {\n\tmy $self = {};\n\tbless $self, shift @_;\n\n\tmy $port = {@_}->{'SSL'} ? 8995 : 8110;\n\teval {\n\t\tlocal $SIG{ALRM} = sub { die \"timeout\\n\" };\n\t\tlocal $SIG{PIPE} = sub { die \"sigpipe\\n\" };\n\t\talarm(8);\n\t$self->{_socket} = IO::Socket::INET->new(\n\t\tProto => \"tcp\",\n\t\t\tPeerAddr => \"127.0.0.1:\" . port($port),\n\t\t@_\n\t)\n\t\tor die \"Can't connect to nginx: $!\\n\";\n\n\tif ({@_}->{'SSL'}) {\n\t\trequire IO::Socket::SSL;\n\t\t\tIO::Socket::SSL->start_SSL(\n\t\t\t\t$self->{_socket},\n\t\t\t\tSSL_verify_mode =>\n\t\t\t\t\tIO::Socket::SSL::SSL_VERIFY_NONE(),\n\t\t\t\t@_\n\t\t\t)\n\t\t\tor die $IO::Socket::SSL::SSL_ERROR . \"\\n\";\n\t\t\tmy $s = $self->{_socket};\n\t\t\tlog_in(\"ssl cipher: \" . $s->get_cipher());\n\t\t\tlog_in(\"ssl cert: \" . $s->peer_certificate('issuer'));\n\t\t}\n\t\talarm(0);\n\t};\n\talarm(0);\n\tif ($@) {\n\t\tlog_in(\"died: $@\");\n\t}\n\n\t$self->{_socket}->autoflush(1);\n\t$self->{_read_buffer} = '';\n\n\treturn $self;\n}\nsub DESTROY {\n\tmy $self = shift;\n\t$self->{_socket}->close();\n}\n\nsub eof {\n\tmy $self = shift;\n\treturn $self->{_socket}->eof();\n}\n\nsub print {\n\tmy ($self, $cmd) = @_;\n\tlog_out($cmd);\n\t$self->{_socket}->print($cmd);\n}\n\nsub send {\n\tmy ($self, $cmd) = @_;\n\tlog_out($cmd);\n\t$self->{_socket}->print($cmd . CRLF);\n}\n\nsub getline {\n\tmy ($self) = @_;\n\tmy $socket = $self->{_socket};\n\n\tif ($self->{_read_buffer} =~ /^(.*?\\x0a)(.*)/ms) {\n\t\t$self->{_read_buffer} = $2;\n\t\treturn $1;\n\t}\n\n\twhile (IO::Select->new($socket)->can_read(8)) {\n\t\t$socket->blocking(0);\n\t\tmy $n = $socket->sysread(my $buf, 1024);\n\t\tmy $again = !defined $n && $!{EWOULDBLOCK};\n\t\t$socket->blocking(1);\n\t\tnext if $again;\n\t\tlast unless $n;\n\n\t\t$self->{_read_buffer} .= $buf;\n\n\t\tif ($self->{_read_buffer} =~ /^(.*?\\x0a)(.*)/ms) {\n\t\t\t$self->{_read_buffer} = $2;\n\t\t\treturn $1;\n\t\t}\n\t};\n}\n\nsub read {\n\tmy ($self) = @_;\n\tmy $socket = $self->{_socket};\n\n\twhile (defined($_ = $self->getline())) {\n\t\tlog_in($_);\n\t\tlast;\n\t}\n\n\treturn $_;\n}\n\nsub check {\n\tmy ($self, $regex, $name) = @_;\n\tTest::More->builder->like($self->read(), $regex, $name);\n}\n\nsub ok {\n\tmy $self = shift;\n\tTest::More->builder->like($self->read(), qr/^\\+OK/, @_);\n}\n\nsub can_read {\n\tmy ($self, $timo) = @_;\n\tIO::Select->new($self->{_socket})->can_read($timo || 3);\n}\nsub socket {\n\tmy ($self) = @_;\n\t$self->{_socket};\n}\n\n###############################################################################\n\nsub pop3_test_daemon {\n\tmy ($port) = @_;\n\n\tmy $server = IO::Socket::INET->new(\n\t\tProto => 'tcp',\n\t\tLocalAddr => '127.0.0.1:' . ($port || port(8111)),\n\t\tListen => 5,\n\t\tReuse => 1\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\twhile (my $client = $server->accept()) {\n\t\t$client->autoflush(1);\n\t\tprint $client \"+OK fake pop3 server ready\" . CRLF;\n\n\t\twhile (<$client>) {\n\t\t\tif (/^quit/i) {\n\t\t\t\tprint $client '+OK quit ok' . CRLF;\n\t\t\t} elsif (/^user test\\@example.com/i) {\n\t\t\t\tprint $client '+OK user ok' . CRLF;\n\t\t\t} elsif (/^pass secret/i) {\n\t\t\t\tprint $client '+OK pass ok' . CRLF;\n\t\t\t} else {\n\t\t\t\tprint $client \"-ERR unknown command\" . CRLF;\n\t\t\t}\n\t\t}\n\n\t\tclose $client;\n\t}\n}\n\n###############################################################################\n\n1;\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/lib/Test/Nginx/SMTP.pm",
    "content": "package Test::Nginx::SMTP;\n\n# (C) Maxim Dounin\n\n# Module for nginx smtp tests.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More qw//;\nuse IO::Select;\nuse IO::Socket;\nuse Socket qw/ CRLF /;\n\nuse Test::Nginx;\n\nsub new {\n\tmy $self = {};\n\tbless $self, shift @_;\n\n\tmy $port = {@_}->{'SSL'} ? 8465 : 8025;\n\teval {\n\t\tlocal $SIG{ALRM} = sub { die \"timeout\\n\" };\n\t\tlocal $SIG{PIPE} = sub { die \"sigpipe\\n\" };\n\t\talarm(8);\n\t$self->{_socket} = IO::Socket::INET->new(\n\t\tProto => \"tcp\",\n\t\t\tPeerAddr => \"127.0.0.1:\" . port($port),\n\t\t@_\n\t)\n\t\tor die \"Can't connect to nginx: $!\\n\";\n\n\tif ({@_}->{'SSL'}) {\n\t\trequire IO::Socket::SSL;\n\t\t\tIO::Socket::SSL->start_SSL(\n\t\t\t\t$self->{_socket},\n\t\t\t\tSSL_verify_mode =>\n\t\t\t\t\tIO::Socket::SSL::SSL_VERIFY_NONE(),\n\t\t\t\t@_\n\t\t\t)\n\t\t\tor die $IO::Socket::SSL::SSL_ERROR . \"\\n\";\n\t\t\tmy $s = $self->{_socket};\n\t\t\tlog_in(\"ssl cipher: \" . $s->get_cipher());\n\t\t\tlog_in(\"ssl cert: \" . $s->peer_certificate('issuer'));\n\t\t}\n\t\talarm(0);\n\t};\n\talarm(0);\n\tif ($@) {\n\t\tlog_in(\"died: $@\");\n\t}\n\n\t$self->{_socket}->autoflush(1);\n\t$self->{_read_buffer} = '';\n\n\treturn $self;\n}\nsub DESTROY {\n\tmy $self = shift;\n\t$self->{_socket}->close();\n}\n\nsub eof {\n\tmy $self = shift;\n\treturn $self->{_socket}->eof();\n}\n\nsub print {\n\tmy ($self, $cmd) = @_;\n\tlog_out($cmd);\n\t$self->{_socket}->print($cmd);\n}\n\nsub send {\n\tmy ($self, $cmd) = @_;\n\tlog_out($cmd);\n\t$self->{_socket}->print($cmd . CRLF);\n}\n\nsub getline {\n\tmy ($self) = @_;\n\tmy $socket = $self->{_socket};\n\n\tif ($self->{_read_buffer} =~ /^(.*?\\x0a)(.*)/ms) {\n\t\t$self->{_read_buffer} = $2;\n\t\treturn $1;\n\t}\n\n\twhile (IO::Select->new($socket)->can_read(8)) {\n\t\t$socket->blocking(0);\n\t\tmy $n = $socket->sysread(my $buf, 1024);\n\t\tmy $again = !defined $n && $!{EWOULDBLOCK};\n\t\t$socket->blocking(1);\n\t\tnext if $again;\n\t\tlast unless $n;\n\n\t\t$self->{_read_buffer} .= $buf;\n\n\t\tif ($self->{_read_buffer} =~ /^(.*?\\x0a)(.*)/ms) {\n\t\t\t$self->{_read_buffer} = $2;\n\t\t\treturn $1;\n\t\t}\n\t};\n}\n\nsub read {\n\tmy ($self) = @_;\n\tmy $socket = $self->{_socket};\n\n\twhile (defined($_ = $self->getline())) {\n\t\tlog_in($_);\n\t\tnext if m/^\\d\\d\\d-/;\n\t\tlast;\n\t}\n\n\treturn $_;\n}\n\nsub check {\n\tmy ($self, $regex, $name) = @_;\n\tTest::More->builder->like($self->read(), $regex, $name);\n}\n\nsub ok {\n\tmy $self = shift;\n\tTest::More->builder->like($self->read(), qr/^2\\d\\d /, @_);\n}\n\nsub authok {\n\tmy $self = shift;\n\tTest::More->builder->like($self->read(), qr/^235 /, @_);\n}\n\nsub can_read {\n\tmy ($self, $timo) = @_;\n\tIO::Select->new($self->{_socket})->can_read($timo || 3);\n}\nsub socket {\n\tmy ($self) = @_;\n\t$self->{_socket};\n}\n\n###############################################################################\n\nsub smtp_test_daemon {\n\tmy ($port) = @_;\n\tmy $proxy_protocol;\n\n\tmy $server = IO::Socket::INET->new(\n\t\tProto => 'tcp',\n\t\tLocalAddr => '127.0.0.1:' . ($port || port(8026)),\n\t\tListen => 5,\n\t\tReuse => 1\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\twhile (my $client = $server->accept()) {\n\t\t$client->autoflush(1);\n\t\tprint $client \"220 fake esmtp server ready\" . CRLF;\n\n\t\t$proxy_protocol = '';\n\n\t\twhile (<$client>) {\n\t\t\tTest::Nginx::log_core('||', $_);\n\n\t\t\tif (/^quit/i) {\n\t\t\t\tprint $client '221 quit ok' . CRLF;\n\t\t\t} elsif (/^(ehlo|helo)/i) {\n\t\t\t\tprint $client '250 hello ok' . CRLF;\n\t\t\t} elsif (/^rset/i) {\n\t\t\t\tprint $client '250 rset ok' . CRLF;\n\t\t\t} elsif (/^auth plain/i) {\n\t\t\t\tprint $client '235 auth ok' . CRLF;\n\t\t\t} elsif (/^mail from:[^@]+$/i) {\n\t\t\t\tprint $client '500 mail from error' . CRLF;\n\t\t\t} elsif (/^mail from:/i) {\n\t\t\t\tprint $client '250 mail from ok' . CRLF;\n\t\t\t} elsif (/^rcpt to:[^@]+$/i) {\n\t\t\t\tprint $client '500 rcpt to error' . CRLF;\n\t\t\t} elsif (/^rcpt to:/i) {\n\t\t\t\tprint $client '250 rcpt to ok' . CRLF;\n\t\t\t} elsif (/^xclient/i) {\n\t\t\t\tprint $client '220 xclient ok' . CRLF;\n\t\t\t} elsif (/^proxy/i) {\n\t\t\t\t$proxy_protocol = $_;\n\t\t\t} elsif (/^xproxy/i) {\n\t\t\t\tprint $client '211 ' . $proxy_protocol . CRLF;\n\t\t\t} else {\n\t\t\t\tprint $client \"500 unknown command\" . CRLF;\n\t\t\t}\n\t\t}\n\n\t\tclose $client;\n\t}\n}\n\n###############################################################################\n\n1;\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/lib/Test/Nginx/Stream.pm",
    "content": "package Test::Nginx::Stream;\n\n# (C) Andrey Zelenkov\n# (C) Nginx, Inc.\n\n# Module for nginx stream tests.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse base qw/ Exporter /;\nour @EXPORT_OK = qw/ stream dgram /;\n\nuse Test::More qw//;\nuse IO::Select;\nuse IO::Socket;\n\nuse Test::Nginx;\n\nsub stream {\n\treturn Test::Nginx::Stream->new(@_);\n}\n\nsub dgram {\n\tunshift(@_, \"PeerAddr\") if @_ == 1;\n\n\treturn Test::Nginx::Stream->new(\n\t\tProto => \"udp\",\n\t\t@_\n\t);\n}\n\nsub new {\n\tmy $self = {};\n\tbless $self, shift @_;\n\n\tunshift(@_, \"PeerAddr\") if @_ == 1;\n\n\teval {\n\t\tlocal $SIG{ALRM} = sub { die \"timeout\\n\" };\n\t\tlocal $SIG{PIPE} = sub { die \"sigpipe\\n\" };\n\t\talarm(8);\n\n\t$self->{_socket} = IO::Socket::INET->new(\n\t\tProto => \"tcp\",\n\t\tPeerAddr => '127.0.0.1',\n\t\t@_\n\t)\n\t\tor die \"Can't connect to nginx: $!\\n\";\n\n\tif ({@_}->{'SSL'}) {\n\t\trequire IO::Socket::SSL;\n\t\t\tIO::Socket::SSL->start_SSL(\n\t\t\t\t$self->{_socket},\n\t\t\t\tSSL_verify_mode =>\n\t\t\t\t\tIO::Socket::SSL::SSL_VERIFY_NONE(),\n\t\t\t\t@_\n\t\t\t)\n\t\t\tor die $IO::Socket::SSL::SSL_ERROR . \"\\n\";\n\t\t\tmy $s = $self->{_socket};\n\t\t\tlog_in(\"ssl cipher: \" . $s->get_cipher());\n\t\t\tlog_in(\"ssl cert: \" . $s->peer_certificate('issuer'));\n\t\t}\n\t\talarm(0);\n\t};\n\talarm(0);\n\tif ($@) {\n\t\tlog_in(\"died: $@\");\n\t}\n\n\t$self->{_socket}->autoflush(1);\n\n\treturn $self;\n}\nsub DESTROY {\n\tmy $self = shift;\n\t$self->{_socket}->close();\n}\n\nsub write {\n\tmy ($self, $message, %extra) = @_;\n\tmy $s = $self->{_socket};\n\n\tlocal $SIG{PIPE} = 'IGNORE';\n\n\t$s->blocking(0);\n\twhile (IO::Select->new($s)->can_write($extra{write_timeout} || 1.5)) {\n\t\tmy $n = $s->syswrite($message);\n\t\tlast unless $n;\n\t\tlog_out(substr($message, 0, $n));\n\n\t\t$message = substr($message, $n);\n\t\tlast unless length $message;\n\t}\n\n\tif (length $message) {\n\t\t$s->close();\n\t}\n}\n\nsub read {\n\tmy ($self, %extra) = @_;\n\tmy ($s, $buf);\n\n\t$s = $self->{_socket};\n\n\t$s->blocking(0);\n\twhile (IO::Select->new($s)->can_read($extra{read_timeout} || 8)) {\n\t\tmy $n = $s->sysread($buf, 1024);\n\t\tnext if !defined $n && $!{EWOULDBLOCK};\n\t\tlast;\n\t}\n\n\tlog_in($buf);\n\treturn $buf;\n}\n\nsub io {\n\tmy $self = shift;\n\n\tmy ($data, %extra) = @_;\n\tmy $length = $extra{length};\n\tmy $read = $extra{read};\n\n\t$read = 1 if !defined $read\n\t\t&& $self->{_socket}->socktype() == &SOCK_DGRAM;\n\n\t$self->write($data, %extra);\n\n\t$data = '';\n\twhile (1) {\n\t\tlast if defined $read && --$read < 0;\n\n\t\tmy $buf = $self->read(%extra);\n\t\tlast unless defined $buf and length($buf);\n\n\t\t$data .= $buf;\n\t\tlast if defined $length && length($data) >= $length;\n\t}\n\n\treturn $data;\n}\n\nsub sockaddr {\n\tmy $self = shift;\n\treturn $self->{_socket}->sockaddr();\n}\n\nsub sockhost {\n\tmy $self = shift;\n\treturn $self->{_socket}->sockhost();\n}\n\nsub sockport {\n\tmy $self = shift;\n\treturn $self->{_socket}->sockport();\n}\n\nsub socket {\n\tmy ($self) = @_;\n\t$self->{_socket};\n}\n###############################################################################\n\n1;\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/lib/Test/Nginx.pm",
    "content": "package Test::Nginx;\n\n# (C) Maxim Dounin\n\n# Generic module for nginx tests.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse base qw/ Exporter /;\n\nour @EXPORT = qw/ log_in log_out http http_get http_head port /;\nour @EXPORT_OK = qw/\n\thttp_gzip_request http_gzip_like http_start http_end http_content\n/;\nour %EXPORT_TAGS = (\n\tgzip => [ qw/ http_gzip_request http_gzip_like / ]\n);\n\n###############################################################################\n\nuse File::Path qw/ rmtree /;\nuse File::Spec qw//;\nuse File::Temp qw/ tempdir /;\nuse IO::Socket;\nuse POSIX qw/ waitpid WNOHANG /;\nuse Socket qw/ CRLF /;\nuse Test::More qw//;\n\n###############################################################################\n\nour $NGINX = defined $ENV{TEST_NGINX_BINARY} ? $ENV{TEST_NGINX_BINARY}\n\t: '../nginx/objs/nginx';\nour %ports = ();\n\nsub new {\n\tmy $self = {};\n\tbless $self;\n\n\t$self->{_pid} = $$;\n\t$self->{_alerts} = 1;\n\n\t$self->{_testdir} = tempdir(\n\t\t'nginx-test-XXXXXXXXXX',\n\t\tTMPDIR => 1\n\t)\n\t\tor die \"Can't create temp directory: $!\\n\";\n\t$self->{_testdir} =~ s!\\\\!/!g if $^O eq 'MSWin32';\n\tmkdir \"$self->{_testdir}/logs\"\n\t\tor die \"Can't create logs directory: $!\\n\";\n\n\tTest::More::BAIL_OUT(\"no $NGINX binary found\")\n\t\tunless -x $NGINX;\n\n\treturn $self;\n}\n\nsub DESTROY {\n\tmy ($self) = @_;\n\tlocal $?;\n\n\treturn if $self->{_pid} != $$;\n\n\t$self->stop();\n\t$self->stop_daemons();\n\n\tif (Test::More->builder->expected_tests) {\n\t\tlocal $Test::Nginx::TODO = 'alerts' unless $self->{_alerts};\n\n\t\tmy @alerts = $self->read_file('error.log') =~ /.+\\[alert\\].+/gm;\n\n\t\tif ($^O eq 'solaris') {\n\t\t\t$Test::Nginx::TODO = 'alerts' if @alerts\n\t\t\t\t&& ! grep { $_ !~ /phantom event/ } @alerts;\n\t\t}\n\t\tif ($^O eq 'MSWin32') {\n\t\t\tmy $re = qr/CloseHandle|TerminateProcess/;\n\t\t\t$Test::Nginx::TODO = 'alerts' if @alerts\n\t\t\t\t&& ! grep { $_ !~ $re } @alerts;\n\t\t}\n\n\t\tTest::More::is(join(\"\\n\", @alerts), '', 'no alerts');\n\t}\n\n\tif (Test::More->builder->expected_tests) {\n\t\tlocal $Test::Nginx::TODO;\n\t\tmy $errors = $self->read_file('error.log');\n\t\t$errors = join \"\\n\", $errors =~ /.+Sanitizer.+/gm;\n\t\tTest::More::is($errors, '', 'no sanitizer errors');\n\t}\n\n\tif ($ENV{TEST_NGINX_CATLOG}) {\n\t\tsystem(\"cat $self->{_testdir}/error.log\");\n\t}\n\tif (not $ENV{TEST_NGINX_LEAVE}) {\n\t\teval { rmtree($self->{_testdir}); };\n\t}\n}\n\nsub has($;) {\n\tmy ($self, @features) = @_;\n\n\tforeach my $feature (@features) {\n\t\tTest::More::plan(skip_all => \"no $feature available\")\n\t\t\tunless $self->has_module($feature)\n\t\t\tor $self->has_feature($feature);\n\t}\n\n\treturn $self;\n}\n\nsub has_module($) {\n\tmy ($self, $feature) = @_;\n\n\tmy %regex = (\n\t\tsni\t=> 'TLS SNI support enabled',\n\t\tmail\t=> '--with-mail((?!\\S)|=dynamic)',\n\t\tflv\t=> '--with-http_flv_module',\n\t\tperl\t=> '--with-http_perl_module',\n\t\tauth_request\n\t\t\t=> '--with-http_auth_request_module',\n\t\trealip\t=> '--with-http_realip_module',\n\t\tsub\t=> '--with-http_sub_module',\n\t\tcharset\t=> '(?s)^(?!.*--without-http_charset_module)',\n\t\tgzip\t=> '(?s)^(?!.*--without-http_gzip_module)',\n\t\tssi\t=> '(?s)^(?!.*--without-http_ssi_module)',\n\t\tmirror\t=> '(?s)^(?!.*--without-http_mirror_module)',\n\t\tuserid\t=> '(?s)^(?!.*--without-http_userid_module)',\n\t\taccess\t=> '(?s)^(?!.*--without-http_access_module)',\n\t\tauth_basic\n\t\t\t=> '(?s)^(?!.*--without-http_auth_basic_module)',\n\t\tautoindex\n\t\t\t=> '(?s)^(?!.*--without-http_autoindex_module)',\n\t\tgeo\t=> '(?s)^(?!.*--without-http_geo_module)',\n\t\tmap\t=> '(?s)^(?!.*--without-http_map_module)',\n\t\treferer\t=> '(?s)^(?!.*--without-http_referer_module)',\n\t\trewrite\t=> '(?s)^(?!.*--without-http_rewrite_module)',\n\t\tproxy\t=> '(?s)^(?!.*--without-http_proxy_module)',\n\t\tfastcgi\t=> '(?s)^(?!.*--without-http_fastcgi_module)',\n\t\tuwsgi\t=> '(?s)^(?!.*--without-http_uwsgi_module)',\n\t\tscgi\t=> '(?s)^(?!.*--without-http_scgi_module)',\n\t\tgrpc\t=> '(?s)^(?!.*--without-http_grpc_module)',\n\t\tmemcached\n\t\t\t=> '(?s)^(?!.*--without-http_memcached_module)',\n\t\tlimit_conn\n\t\t\t=> '(?s)^(?!.*--without-http_limit_conn_module)',\n\t\tlimit_req\n\t\t\t=> '(?s)^(?!.*--without-http_limit_req_module)',\n\t\tempty_gif\n\t\t\t=> '(?s)^(?!.*--without-http_empty_gif_module)',\n\t\tbrowser\t=> '(?s)^(?!.*--without-http_browser_module)',\n\t\tupstream_hash\n\t\t\t=> '(?s)^(?!.*--without-http_upstream_hash_module)',\n\t\tupstream_ip_hash\n\t\t\t=> '(?s)^(?!.*--without-http_upstream_ip_hash_module)',\n\t\tupstream_least_conn\n\t\t\t=> '(?s)^(?!.*--without-http_upstream_least_conn_mod)',\n\t\tupstream_random\n\t\t\t=> '(?s)^(?!.*--without-http_upstream_random_module)',\n\t\tupstream_keepalive\n\t\t\t=> '(?s)^(?!.*--without-http_upstream_keepalive_modu)',\n\t\tupstream_zone\n\t\t\t=> '(?s)^(?!.*--without-http_upstream_zone_module)',\n\t\thttp\t=> '(?s)^(?!.*--without-http(?!\\S))',\n\t\tcache\t=> '(?s)^(?!.*--without-http-cache)',\n\t\tpop3\t=> '(?s)^(?!.*--without-mail_pop3_module)',\n\t\timap\t=> '(?s)^(?!.*--without-mail_imap_module)',\n\t\tsmtp\t=> '(?s)^(?!.*--without-mail_smtp_module)',\n\t\tpcre\t=> '(?s)^(?!.*--without-pcre)',\n\t\tsplit_clients\n\t\t\t=> '(?s)^(?!.*--without-http_split_clients_module)',\n\t\tstream\t=> '--with-stream((?!\\S)|=dynamic)',\n\t\tstream_access\n\t\t\t=> '(?s)^(?!.*--without-stream_access_module)',\n\t\tstream_geo\n\t\t\t=> '(?s)^(?!.*--without-stream_geo_module)',\n\t\tstream_limit_conn\n\t\t\t=> '(?s)^(?!.*--without-stream_limit_conn_module)',\n\t\tstream_map\n\t\t\t=> '(?s)^(?!.*--without-stream_map_module)',\n\t\tstream_return\n\t\t\t=> '(?s)^(?!.*--without-stream_return_module)',\n\t\tstream_set\n\t\t\t=> '(?s)^(?!.*--without-stream_set_module)',\n\t\tstream_split_clients\n\t\t\t=> '(?s)^(?!.*--without-stream_split_clients_module)',\n\t\tstream_ssl\n\t\t\t=> '--with-stream_ssl_module',\n\t\tstream_upstream_hash\n\t\t\t=> '(?s)^(?!.*--without-stream_upstream_hash_module)',\n\t\tstream_upstream_least_conn\n\t\t\t=> '(?s)^(?!.*--without-stream_upstream_least_conn_m)',\n\t\tstream_upstream_random\n\t\t\t=> '(?s)^(?!.*--without-stream_upstream_random_modul)',\n\t\tstream_upstream_zone\n\t\t\t=> '(?s)^(?!.*--without-stream_upstream_zone_module)',\n\t);\n\n\tmy $re = $regex{$feature};\n\t$re = $feature if !defined $re;\n\n\t$self->{_configure_args} = `$NGINX -V 2>&1`\n\t\tif !defined $self->{_configure_args};\n\n\treturn 1 if $self->{_configure_args} =~ $re;\n\n\tmy %modules = (\n\t\thttp_geoip\n\t\t\t=> 'ngx_http_geoip_module',\n\t\timage_filter\n\t\t\t=> 'ngx_http_image_filter_module',\n\t\tperl\t=> 'ngx_http_perl_module',\n\t\txslt\t=> 'ngx_http_xslt_filter_module',\n\t\tmail\t=> 'ngx_mail_module',\n\t\tstream\t=> 'ngx_stream_module',\n\t\tstream_geoip\n\t\t\t=> 'ngx_stream_geoip_module',\n\t);\n\n\tmy $module = $modules{$feature};\n\tif (defined $module && defined $ENV{TEST_NGINX_GLOBALS}) {\n\t\t$re = qr/load_module\\s+[^;]*\\Q$module\\E[-\\w]*\\.so\\s*;/;\n\t\treturn 1 if $ENV{TEST_NGINX_GLOBALS} =~ $re;\n\t}\n\n\treturn 0;\n}\n\nsub has_feature($) {\n\tmy ($self, $feature) = @_;\n\n\tif ($feature eq 'symlink') {\n\t\treturn $^O ne 'MSWin32';\n\t}\n\n\tif ($feature eq 'unix') {\n\t\treturn $^O ne 'MSWin32';\n\t}\n\n\tif ($feature eq 'udp') {\n\t\treturn $^O ne 'MSWin32';\n\t}\n\n\tif ($feature =~ /^socket_ssl/) {\n\t\teval { require IO::Socket::SSL; };\n\t\treturn 0 if $@;\n\t\teval { IO::Socket::SSL::SSL_VERIFY_NONE(); };\n\t\treturn 0 if $@;\n\t\tif ($feature eq 'socket_ssl') {\n\t\t\treturn 1;\n\t\t}\n\t\tif ($feature eq 'socket_ssl_sni') {\n\t\t\teval { IO::Socket::SSL->can_client_sni() or die; };\n\t\t\treturn !$@;\n\t\t}\n\t\tif ($feature eq 'socket_ssl_alpn') {\n\t\t\teval { IO::Socket::SSL->can_alpn() or die; };\n\t\t\treturn !$@;\n\t\t}\n\t\tif ($feature eq 'socket_ssl_sslversion') {\n\t\t\treturn IO::Socket::SSL->can('get_sslversion');\n\t\t}\n\t\tif ($feature eq 'socket_ssl_reused') {\n\t\t\treturn IO::Socket::SSL->can('get_session_reused');\n\t\t}\n\t\treturn 0;\n\t}\n\n\tif ($feature =~ /^(openssl|libressl):([0-9.]+)/) {\n\t\tmy $library = $1;\n\t\tmy $need = $2;\n\n\t\t$self->{_configure_args} = `$NGINX -V 2>&1`\n\t\t\tif !defined $self->{_configure_args};\n\n\t\treturn 0 unless\n\t\t\t$self->{_configure_args} =~ /with $library ([0-9.]+)/i;\n\n\t\tmy @v = split(/\\./, $1);\n\t\tmy ($n, $v);\n\n\t\tfor $n (split(/\\./, $need)) {\n\t\t\t$v = shift @v || 0;\n\t\t\treturn 0 if $n > $v;\n\t\t\treturn 1 if $v > $n;\n\t\t}\n\n\t\treturn 1;\n\t}\n\n\tif ($feature eq 'cryptx') {\n\t\teval { require Crypt::Misc; };\n\t\treturn 0 if $@;\n\t\teval { die if $Crypt::Misc::VERSION < 0.067; };\n\t\treturn !$@;\n\t}\n\n\treturn 0;\n}\n\nsub has_version($) {\n\tmy ($self, $need) = @_;\n\n\t$self->{_configure_args} = `$NGINX -V 2>&1`\n\t\tif !defined $self->{_configure_args};\n\n\t$self->{_configure_args} =~ m!nginx version: nginx/([0-9.]+)!;\n\n\tmy @v = split(/\\./, $1);\n\tmy ($n, $v);\n\n\tfor $n (split(/\\./, $need)) {\n\t\t$v = shift @v || 0;\n\t\treturn 0 if $n > $v;\n\t\treturn 1 if $v > $n;\n\t}\n\n\treturn 1;\n}\n\nsub has_daemon($) {\n\tmy ($self, $daemon) = @_;\n\n\tif ($^O eq 'MSWin32') {\n\t\t`for %i in ($daemon.exe) do \\@echo | set /p x=%~\\$PATH:i`\n\t\t\tor Test::More::plan(skip_all => \"$daemon not found\");\n\t\treturn $self;\n\t}\n\n\tif ($^O eq 'solaris') {\n\t\tTest::More::plan(skip_all => \"$daemon not found\")\n\t\t\tunless `command -v $daemon`;\n\t\treturn $self;\n\t}\n\n\tTest::More::plan(skip_all => \"$daemon not found\")\n\t\tunless `which $daemon`;\n\n\treturn $self;\n}\n\nsub try_run($$) {\n\tmy ($self, $message) = @_;\n\n\teval {\n\t\topen OLDERR, \">&\", \\*STDERR; close STDERR;\n\t\t$self->run();\n\t\topen STDERR, \">&\", \\*OLDERR;\n\t};\n\n\treturn $self unless $@;\n\n\tif ($ENV{TEST_NGINX_VERBOSE}) {\n\t\topen F, '<', $self->{_testdir} . '/error.log'\n\t\t\tor die \"Can't open error.log: $!\";\n\t\tlog_core($_) while (<F>);\n\t\tclose F;\n\t}\n\n\tTest::More::plan(skip_all => $message);\n\treturn $self;\n}\n\nsub plan($) {\n\tmy ($self, $plan) = @_;\n\n\tTest::More::plan(tests => $plan + 2);\n\n\treturn $self;\n}\n\nsub todo_alerts() {\n\tmy ($self) = @_;\n\n\t$self->{_alerts} = 0;\n\n\treturn $self;\n}\n\nsub run(;$) {\n\tmy ($self, $conf) = @_;\n\n\tmy $testdir = $self->{_testdir};\n\n\tif (defined $conf) {\n\t\tmy $c = `cat $conf`;\n\t\t$self->write_file_expand('nginx.conf', $c);\n\t}\n\n\tmy $pid = fork();\n\tdie \"Unable to fork(): $!\\n\" unless defined $pid;\n\n\tif ($pid == 0) {\n\t\tmy @globals = $self->{_test_globals} ?\n\t\t\t() : ('-g', \"pid $testdir/nginx.pid; \"\n\t\t\t. \"error_log $testdir/error.log debug;\");\n\t\texec($NGINX, '-p', \"$testdir/\", '-c', 'nginx.conf',\n\t\t\t'-e', 'error.log', @globals)\n\t\t\tor die \"Unable to exec(): $!\\n\";\n\t}\n\n\t# wait for nginx to start\n\n\t$self->waitforfile(\"$testdir/nginx.pid\", $pid)\n\t\tor die \"Can't start nginx\";\n\n\tfor (1 .. 50) {\n\t\tlast if $^O ne 'MSWin32';\n\t\tlast if $self->read_file('error.log') =~ /create thread/;\n\t\tselect undef, undef, undef, 0.1;\n\t}\n\n\t$self->{_started} = 1;\n\treturn $self;\n}\n\nsub port {\n\tmy ($num, %opts) = @_;\n\tmy ($sock, $lock, $port);\n\n\tgoto done if defined $ports{$num};\n\n\tmy $socket = sub {\n\t\tIO::Socket::INET->new(\n\t\t\tProto => 'tcp',\n\t\t\tLocalAddr => '127.0.0.1:' . shift,\n\t\t\tListen => 1,\n\t\t\tReuse => ($^O ne 'MSWin32'),\n\t\t);\n\t};\n\n\tmy $socketl = sub {\n\t\tIO::Socket::INET->new(\n\t\t\tProto => 'udp',\n\t\t\tLocalAddr => '127.0.0.1:' . shift,\n\t\t);\n\t};\n\n\t($socket, $socketl) = ($socketl, $socket) if $opts{udp};\n\n\t$port = $num;\n\n\tfor (1 .. 10) {\n\t\t$port = int($port / 500) * 500 + int(rand(500)) unless $_ == 1;\n\n\t\t$lock = $socketl->($port) or next;\n\t\t$sock = $socket->($port) and last;\n\t}\n\n\tdie \"Port limit exceeded\" unless defined $lock and defined $sock;\n\n\t$ports{$num} = {\n\t\tport => $port,\n\t\tsocket => $lock\n\t};\n\ndone:\n\treturn $ports{$num}{socket} if $opts{socket};\n\treturn $ports{$num}{port};\n}\n\nsub dump_config() {\n\tmy ($self) = @_;\n\n\tmy $testdir = $self->{_testdir};\n\n\tmy @globals = $self->{_test_globals} ?\n\t\t() : ('-g', \"pid $testdir/nginx.pid; \"\n\t\t. \"error_log $testdir/error.log debug;\");\n\tmy $command = \"$NGINX -T -p $testdir/ -c nginx.conf \"\n\t\t. \"-e error.log \" . join(' ', @globals);\n\n\treturn qx/$command 2>&1/;\n}\n\nsub waitforfile($;$) {\n\tmy ($self, $file, $pid) = @_;\n\tmy $exited;\n\n\t# wait for file to appear\n\t# or specified process to exit\n\n\tfor (1 .. 50) {\n\t\treturn 1 if -e $file;\n\t\treturn 0 if $exited;\n\t\t$exited = waitpid($pid, WNOHANG) != 0 if $pid;\n\t\tselect undef, undef, undef, 0.1;\n\t}\n\n\treturn undef;\n}\n\nsub waitforsocket($) {\n\tmy ($self, $peer) = @_;\n\n\t# wait for socket to accept connections\n\n\tfor (1 .. 50) {\n\t\tmy $s = IO::Socket::INET->new(\n\t\t\tProto => 'tcp',\n\t\t\tPeerAddr => $peer\n\t\t);\n\n\t\treturn 1 if defined $s;\n\n\t\tselect undef, undef, undef, 0.1;\n\t}\n\n\treturn undef;\n}\n\nsub reload() {\n\tmy ($self) = @_;\n\n\treturn $self unless $self->{_started};\n\n\tmy $pid = $self->read_file('nginx.pid');\n\n\tif ($^O eq 'MSWin32') {\n\t\tmy $testdir = $self->{_testdir};\n\t\tmy @globals = $self->{_test_globals} ?\n\t\t\t() : ('-g', \"pid $testdir/nginx.pid; \"\n\t\t\t. \"error_log $testdir/error.log debug;\");\n\t\tsystem($NGINX, '-p', $testdir, '-c', \"nginx.conf\",\n\t\t\t'-s', 'reload', '-e', 'error.log', @globals) == 0\n\t\t\tor die \"system() failed: $?\\n\";\n\n\t} else {\n\t\tkill 'HUP', $pid;\n\t}\n\n\treturn $self;\n}\n\nsub stop() {\n\tmy ($self) = @_;\n\n\treturn $self unless $self->{_started};\n\n\tmy $pid = $self->read_file('nginx.pid');\n\n\tif ($^O eq 'MSWin32') {\n\t\tmy $testdir = $self->{_testdir};\n\t\tmy @globals = $self->{_test_globals} ?\n\t\t\t() : ('-g', \"pid $testdir/nginx.pid; \"\n\t\t\t. \"error_log $testdir/error.log debug;\");\n\t\tsystem($NGINX, '-p', $testdir, '-c', \"nginx.conf\",\n\t\t\t'-s', 'quit', '-e', 'error.log', @globals) == 0\n\t\t\tor die \"system() failed: $?\\n\";\n\n\t} else {\n\t\tkill 'QUIT', $pid;\n\t}\n\n\tmy $exited;\n\n\tfor (1 .. 900) {\n\t\t$exited = waitpid($pid, WNOHANG) != 0;\n\t\tlast if $exited;\n\t\tselect undef, undef, undef, 0.1;\n\t}\n\n\tif (!$exited) {\n\t\tif ($^O eq 'MSWin32') {\n\t\t\tmy $testdir = $self->{_testdir};\n\t\t\tmy @globals = $self->{_test_globals} ?\n\t\t\t\t() : ('-g', \"pid $testdir/nginx.pid; \"\n\t\t\t\t. \"error_log $testdir/error.log debug;\");\n\t\t\tsystem($NGINX, '-p', $testdir, '-c', \"nginx.conf\",\n\t\t\t\t'-s', 'stop', '-e', 'error.log', @globals) == 0\n\t\t\t\tor die \"system() failed: $?\\n\";\n\n\t\t} else {\n\t\t\tkill 'TERM', $pid;\n\t\t}\n\n\t\twaitpid($pid, 0);\n\t}\n\n\t$self->{_started} = 0;\n\n\treturn $self;\n}\n\nsub stop_daemons() {\n\tmy ($self) = @_;\n\n\twhile ($self->{_daemons} && scalar @{$self->{_daemons}}) {\n\t\tmy $p = shift @{$self->{_daemons}};\n\t\tkill $^O eq 'MSWin32' ? 9 : 'TERM', $p;\n\n\t\tmy $exited;\n\n\t\tfor (1 .. 50) {\n\t\t\t$exited = waitpid($p, WNOHANG) != 0;\n\t\t\tlast if $exited;\n\t\t\tselect undef, undef, undef, 0.1;\n\t\t}\n\n\t\tif (!$exited) {\n\t\t\tkill $^O eq 'MSWin32' ? 9 : 'TERM', $p;\n\t\t\twaitpid($p, 0);\n\t\t}\n\t}\n\n\treturn $self;\n}\n\nsub read_file($) {\n\tmy ($self, $name) = @_;\n\tlocal $/;\n\n\topen F, '<', $self->{_testdir} . '/' . $name\n\t\tor die \"Can't open $name: $!\";\n\tmy $content = <F>;\n\tclose F;\n\n\treturn $content;\n}\n\nsub write_file($$) {\n\tmy ($self, $name, $content) = @_;\n\n\topen F, '>' . $self->{_testdir} . '/' . $name\n\t\tor die \"Can't create $name: $!\";\n\tbinmode F;\n\tprint F $content;\n\tclose F;\n\n\treturn $self;\n}\n\nsub write_file_expand($$) {\n\tmy ($self, $name, $content) = @_;\n\n\t$content =~ s/%%TEST_GLOBALS%%/$self->test_globals()/gmse;\n\t$content =~ s/%%TEST_GLOBALS_HTTP%%/$self->test_globals_http()/gmse;\n\t$content =~ s/%%TEST_GLOBALS_STREAM%%/$self->test_globals_stream()/gmse;\n\t$content =~ s/%%TESTDIR%%/$self->{_testdir}/gms;\n\n\t$content =~ s/127\\.0\\.0\\.1:(8\\d\\d\\d)/'127.0.0.1:' . port($1)/gmse;\n\n\t$content =~ s/%%PORT_(\\d+)%%/port($1)/gmse;\n\t$content =~ s/%%PORT_(\\d+)_UDP%%/port($1, udp => 1)/gmse;\n\n\treturn $self->write_file($name, $content);\n}\n\nsub run_daemon($;@) {\n\tmy ($self, $code, @args) = @_;\n\n\tmy $pid = fork();\n\tdie \"Can't fork daemon: $!\\n\" unless defined $pid;\n\n\tif ($pid == 0) {\n\t\tif (ref($code) eq 'CODE') {\n\t\t\t$code->(@args);\n\t\t\texit 0;\n\t\t} else {\n\t\t\texec($code, @args);\n\t\t\texit 0;\n\t\t}\n\t}\n\n\t$self->{_daemons} = [] unless defined $self->{_daemons};\n\tpush @{$self->{_daemons}}, $pid;\n\n\treturn $self;\n}\n\nsub testdir() {\n\tmy ($self) = @_;\n\treturn $self->{_testdir};\n}\n\nsub test_globals() {\n\tmy ($self) = @_;\n\n\treturn $self->{_test_globals}\n\t\tif defined $self->{_test_globals};\n\n\tmy $s = '';\n\n\t$s .= \"pid $self->{_testdir}/nginx.pid;\\n\";\n\t$s .= \"error_log $self->{_testdir}/error.log debug;\\n\";\n\n\t$s .= $ENV{TEST_NGINX_GLOBALS}\n\t\tif $ENV{TEST_NGINX_GLOBALS};\n\n\t$s .= $self->test_globals_modules();\n\t$s .= $self->test_globals_perl5lib() if $s !~ /env PERL5LIB/;\n\n\t$self->{_test_globals} = $s;\n}\n\nsub test_globals_modules() {\n\tmy ($self) = @_;\n\n\tmy $modules = $ENV{TEST_NGINX_MODULES};\n\n\tif (!defined $modules) {\n\t\tmy ($volume, $dir) = File::Spec->splitpath($NGINX);\n\t\t$modules = File::Spec->catpath($volume, $dir, '');\n\t}\n\n\t$modules = File::Spec->rel2abs($modules);\n\t$modules =~ s!\\\\!/!g if $^O eq 'MSWin32';\n\n\tmy $s = '';\n\n\t$s .= \"load_module $modules/ngx_http_geoip_module.so;\\n\"\n\t\tif $self->has_module('http_geoip\\S+=dynamic');\n\n\t$s .= \"load_module $modules/ngx_http_image_filter_module.so;\\n\"\n\t\tif $self->has_module('image_filter\\S+=dynamic');\n\n\t$s .= \"load_module $modules/ngx_http_perl_module.so;\\n\"\n\t\tif $self->has_module('perl\\S+=dynamic');\n\n\t$s .= \"load_module $modules/ngx_http_xslt_filter_module.so;\\n\"\n\t\tif $self->has_module('xslt\\S+=dynamic');\n\n\t$s .= \"load_module $modules/ngx_mail_module.so;\\n\"\n\t\tif $self->has_module('mail=dynamic');\n\n\t$s .= \"load_module $modules/ngx_stream_module.so;\\n\"\n\t\tif $self->has_module('stream=dynamic');\n\n\t$s .= \"load_module $modules/ngx_stream_geoip_module.so;\\n\"\n\t\tif $self->has_module('stream_geoip\\S+=dynamic');\n\n\treturn $s;\n}\n\nsub test_globals_perl5lib() {\n\tmy ($self) = @_;\n\n\treturn '' unless $self->has_module('perl');\n\n\tmy ($volume, $dir) = File::Spec->splitpath($NGINX);\n\tmy $objs = File::Spec->catpath($volume, $dir, '');\n\n\t$objs = File::Spec->rel2abs($objs);\n\t$objs =~ s!\\\\!/!g if $^O eq 'MSWin32';\n\n\treturn \"env PERL5LIB=$objs/src/http/modules/perl:\"\n\t\t. \"$objs/src/http/modules/perl/blib/arch;\\n\";\n}\n\nsub test_globals_http() {\n\tmy ($self) = @_;\n\n\treturn $self->{_test_globals_http}\n\t\tif defined $self->{_test_globals_http};\n\n\tmy $s = '';\n\n\t$s .= \"root $self->{_testdir};\\n\";\n\t$s .= \"access_log $self->{_testdir}/access.log;\\n\";\n\t$s .= \"client_body_temp_path $self->{_testdir}/client_body_temp;\\n\";\n\t$s .= \"lua_package_path \\\"/usr/local/lib/lua/?.lua;;\\\";\\n\";\n\n\t$s .= \"fastcgi_temp_path $self->{_testdir}/fastcgi_temp;\\n\"\n\t\tif $self->has_module('fastcgi');\n\n\t$s .= \"proxy_temp_path $self->{_testdir}/proxy_temp;\\n\"\n\t\tif $self->has_module('proxy');\n\n\t$s .= \"uwsgi_temp_path $self->{_testdir}/uwsgi_temp;\\n\"\n\t\tif $self->has_module('uwsgi');\n\n\t$s .= \"scgi_temp_path $self->{_testdir}/scgi_temp;\\n\"\n\t\tif $self->has_module('scgi');\n\n\t$s .= $ENV{TEST_NGINX_GLOBALS_HTTP}\n\t\tif $ENV{TEST_NGINX_GLOBALS_HTTP};\n\n\t$self->{_test_globals_http} = $s;\n}\n\nsub test_globals_stream() {\n\tmy ($self) = @_;\n\n\treturn $self->{_test_globals_stream}\n\t\tif defined $self->{_test_globals_stream};\n\n\tmy $s = '';\n\n\t$s .= $ENV{TEST_NGINX_GLOBALS_STREAM}\n\t\tif $ENV{TEST_NGINX_GLOBALS_STREAM};\n\n\t$self->{_test_globals_stream} = $s;\n}\n\n###############################################################################\n\nsub log_core {\n\treturn unless $ENV{TEST_NGINX_VERBOSE};\n\tmy ($prefix, $msg) = @_;\n\t($prefix, $msg) = ('', $prefix) unless defined $msg;\n\t$prefix .= ' ' if length($prefix) > 0;\n\n\tif (length($msg) > 2048) {\n\t\t$msg = substr($msg, 0, 2048)\n\t\t\t. \"(...logged only 2048 of \" . length($msg)\n\t\t\t. \" bytes)\";\n\t}\n\n\t$msg =~ s/^/# $prefix/gm;\n\t$msg =~ s/([^\\x20-\\x7e])/sprintf('\\\\x%02x', ord($1)) . (($1 eq \"\\n\") ? \"\\n\" : '')/gmxe;\n\t$msg .= \"\\n\" unless $msg =~ /\\n\\Z/;\n\tprint $msg;\n}\n\nsub log_out {\n\tlog_core('>>', @_);\n}\n\nsub log_in {\n\tlog_core('<<', @_);\n}\n\n###############################################################################\n\nsub http_get($;%) {\n\tmy ($url, %extra) = @_;\n\treturn http(<<EOF, %extra);\nGET $url HTTP/1.0\nHost: localhost\n\nEOF\n}\n\nsub http_head($;%) {\n\tmy ($url, %extra) = @_;\n\treturn http(<<EOF, %extra);\nHEAD $url HTTP/1.0\nHost: localhost\n\nEOF\n}\n\nsub http($;%) {\n\tmy ($request, %extra) = @_;\n\n\tmy $s = http_start($request, %extra);\n\n\treturn $s if $extra{start} or !defined $s;\n\treturn http_end($s, %extra);\n}\n\nsub http_start($;%) {\n\tmy ($request, %extra) = @_;\n\tmy $s;\n\n\tmy $port = $extra{SSL} ? 8443 : 8080;\n\teval {\n\t\tlocal $SIG{ALRM} = sub { die \"timeout\\n\" };\n\t\tlocal $SIG{PIPE} = sub { die \"sigpipe\\n\" };\n\t\talarm(8);\n\n\t\t$s = $extra{socket} || IO::Socket::INET->new(\n\t\t\tProto => 'tcp',\n\t\t\tPeerAddr => '127.0.0.1:' . port($port),\n\t\t\t%extra\n\t\t)\n\t\t\tor die \"Can't connect to nginx: $!\\n\";\n\t\tif ($extra{SSL}) {\n\t\t\trequire IO::Socket::SSL;\n\t\t\tIO::Socket::SSL->start_SSL(\n\t\t\t\t$s,\n\t\t\t\tSSL_verify_mode =>\n\t\t\t\t\tIO::Socket::SSL::SSL_VERIFY_NONE(),\n\t\t\t\t%extra\n\t\t\t)\n\t\t\t\tor die $IO::Socket::SSL::SSL_ERROR . \"\\n\";\n\t\t\tlog_in(\"ssl cipher: \" . $s->get_cipher());\n\t\t\tlog_in(\"ssl cert: \" . $s->peer_certificate('issuer'));\n\t\t}\n\n\t\tlog_out($request);\n\t\t$s->print($request);\n\n\t\tselect undef, undef, undef, $extra{sleep} if $extra{sleep};\n\t\treturn '' if $extra{aborted};\n\n\t\tif ($extra{body}) {\n\t\t\tlog_out($extra{body});\n\t\t\t$s->print($extra{body});\n\t\t}\n\n\t\talarm(0);\n\t};\n\talarm(0);\n\tif ($@) {\n\t\tlog_in(\"died: $@\");\n\t\treturn undef;\n\t}\n\n\treturn $s;\n}\n\nsub http_end($;%) {\n\tmy ($s) = @_;\n\tmy $reply;\n\n\teval {\n\t\tlocal $SIG{ALRM} = sub { die \"timeout\\n\" };\n\t\tlocal $SIG{PIPE} = sub { die \"sigpipe\\n\" };\n\t\talarm(8);\n\n\t\tlocal $/;\n\t\t$reply = $s->getline();\n\t\t$s->close();\n\n\t\talarm(0);\n\t};\n\talarm(0);\n\tif ($@) {\n\t\tlog_in(\"died: $@\");\n\t\treturn undef;\n\t}\n\n\tlog_in($reply);\n\treturn $reply;\n}\n\n###############################################################################\n\nsub http_gzip_request {\n\tmy ($url) = @_;\n\tmy $r = http(<<EOF);\nGET $url HTTP/1.1\nHost: localhost\nConnection: close\nAccept-Encoding: gzip\n\nEOF\n}\n\nsub http_content {\n\tmy ($text) = @_;\n\n\treturn undef if !defined $text;\n\n\tif ($text !~ /(.*?)\\x0d\\x0a?\\x0d\\x0a?(.*)/ms) {\n\t\treturn undef;\n\t}\n\n\tmy ($headers, $body) = ($1, $2);\n\n\tif ($headers !~ /Transfer-Encoding: chunked/i) {\n\t\treturn $body;\n\t}\n\n\tmy $content = '';\n\tmy $len = -1;\n\n\twhile ($body =~ /\\G\\x0d?\\x0a?([0-9a-f]+)\\x0d\\x0a?/gcmsi) {\n\t\t$len = hex($1);\n\t\t$content .= substr($body, pos($body), $len);\n\t\tpos($body) += $len;\n\t}\n\n\tif ($len != 0) {\n\t\t$content .= '[no-last-chunk]';\n\t}\n\n\treturn $content;\n}\n\nsub http_gzip_like {\n\tmy ($text, $re, $name) = @_;\n\n\tSKIP: {\n\t\teval { require IO::Uncompress::Gunzip; };\n\t\tTest::More::skip(\n\t\t\t\"IO::Uncompress::Gunzip not installed\", 1) if $@;\n\n\t\tmy $in = http_content($text);\n\t\tmy $out;\n\n\t\tIO::Uncompress::Gunzip::gunzip(\\$in => \\$out);\n\n\t\tTest::More->builder->like($out, $re, $name);\n\t}\n}\n\n###############################################################################\n\n1;\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/lib/Time/Parse.pm",
    "content": "# Date::Parse\n#\n# Copyright (c) 1995 Graham Barr. All rights reserved. This program is free\n# software; you can redistribute it and/or modify it under the same terms\n# as Perl itself.\n\npackage Time::Parse;\n\n=head1 NAME\n\nDate::Parse - Parse date strings into time values\n\n=head1 SYNOPSIS\n\n\tuse Date::Parse;\n\t\n\t$time = str2time($date);\n\t\n\t($ss,$mm,$hh,$day,$month,$year,$zone) = strptime($date);\n\n=head1 DESCRIPTION\n\nC<Date::Parse> provides two routines for parsing date strings into time values.\n\n=over 4\n\n=item str2time(DATE [, ZONE])\n\nC<str2time> parses C<DATE> and returns a unix time value, or undef upon failure.\nC<ZONE>, if given, specifies the timezone to assume when parsing if the\ndate string does not specify a timezome.\n\n=item strptime(DATE [, ZONE])\n\nC<strptime> takes the same arguments as str2time but returns an array of\nvalues C<($ss,$mm,$hh,$day,$month,$year,$zone)>. Elements are only defined\nif they could be extracted from the date string. The C<$zone> element is\nthe timezone offset in seconds from GMT. An empty array is returned upon\nfailure.\n\n=head1 MULTI-LANGUAGE SUPPORT\n\nDate::Parse is capable of parsing dates in several languages, these are\nEnglish, French, German and Italian. Changing the language is done via\na static method call, for example\n\n\tDate::Parse->language('German');\n\nwill cause Date::Parse to attempt to parse any subsequent dates in German.\n\nThis is only a first pass, I am considering changing this to be\n\n\t$lang = Date::Language->new('German');\n\t$lang->str2time(\"25 Jun 1996 21:09:55 +0100\");\n\nI am open to suggestions on this.\n\n=head1 AUTHOR\n\nGraham Barr <Graham.Barr@tiuk.ti.com>\n\n=head1 REVISION\n\n$Revision: 2.6 $\n\n=head1 COPYRIGHT\n\nCopyright (c) 1995 Graham Barr. All rights reserved. This program is free\nsoftware; you can redistribute it and/or modify it under the same terms\nas Perl itself.\n\n=cut\n\nrequire 5.000;\nuse strict;\nuse vars qw($VERSION @ISA @EXPORT);\nuse Time::Local;\nuse Carp;\nuse Time::Zone;\nuse Exporter;\n\n@ISA = qw(Exporter);\n@EXPORT = qw(&strtotime &str2time &strptime);\n\n$VERSION = sprintf(\"%d.%02d\", q$Revision: 2.6 $ =~ m#(\\d+)\\.(\\d+)#);\n\nmy %month = (\n\tjanuary\t\t=> 0,\n\tfebruary\t=> 1,\n\tmarch\t\t=> 2,\n\tapril\t\t=> 3,\n\tmay\t\t=> 4,\n\tjune\t\t=> 5,\n\tjuly\t\t=> 6,\n\taugust\t\t=> 7,\n\tseptember\t=> 8,\n\tsept\t\t=> 8,\n\toctober\t\t=> 9,\n\tnovember\t=> 10,\n\tdecember\t=> 11,\n\t);\n\nmy %day = (\n\tsunday\t\t=> 0,\n\tmonday\t\t=> 1,\n\ttuesday\t\t=> 2,\n\ttues\t\t=> 2,\n\twednesday\t=> 3,\n\twednes\t\t=> 3,\n\tthursday\t=> 4,\n\tthur\t\t=> 4,\n\tthurs\t\t=> 4,\n\tfriday\t\t=> 5,\n\tsaturday\t=> 6,\n\t);\n\nmy @suf = (qw(th st nd rd th th th th th th)) x 3;\n@suf[11,12,13] = qw(th th th);\n\n#Abbreviations\n\nmap { $month{substr($_,0,3)} = $month{$_} } keys %month;\nmap { $day{substr($_,0,3)}   = $day{$_} }   keys %day;\n\nmy $strptime = <<'ESQ';\n my %month = map { lc $_ } %$mon_ref;\n my $daypat = join(\"|\", map { lc $_ } keys %$day_ref);\n my $monpat = join(\"|\", keys %month);\n my $sufpat = join(\"|\", map { lc $_ } @$suf_ref);\n\n my %ampm = (\n\tam => 0,\n\tpm => 12\n\t);\n\n # allow map am +. a.m.\n map { my($z) = $_; $z =~ s#(\\w)#$1\\.#g; $ampm{$z} = $ampm{$_} } keys %ampm;\n\n my($AM, $PM) = (0,12);\n\nsub\n{\n\n my $dtstr = lc shift;\n my $merid = 24;\n\n my($year,$month,$day,$hh,$mm,$ss,$zone) = (undef) x 7;\n\n $zone = tz_offset(shift)\n    if(@_);\n\n while(1) { last unless($dtstr =~ s#\\([^\\(\\)]*\\)# #o) }\n\n $dtstr =~ s#(\\A|\\n|\\Z)# #sog;\n\n # ignore day names\n $dtstr =~ s#([\\d\\w\\s])[\\.\\,]\\s#$1 #sog;\n $dtstr =~ s#($daypat)\\s*(den\\s)?# #o;\n # Time: 12:00 or 12:00:00 with optional am/pm\n  \n if($dtstr =~ s#[:\\s](\\d\\d?):(\\d\\d)(:(\\d\\d)(?:\\.\\d+)?)?\\s*([ap]\\.?m\\.?)?\\s# #o)\n  {\n   ($hh,$mm,$ss) = ($1,$2,$4 || 0);\n   $merid = $ampm{$5} if($5);\n  }\n\n # Time: 12 am\n  \n elsif($dtstr =~ s#\\s(\\d\\d?)\\s*([ap]\\.?m\\.?)\\s# #o)\n  {\n   ($hh,$mm,$ss) = ($1,0,0);\n   $merid = $ampm{$2};\n  }\n  \n # Date: 12-June-96 (using - . or /)\n  \n if($dtstr =~ s#\\s(\\d\\d?)([\\-\\./])($monpat)(\\2(\\d\\d+))?\\s# #o)\n  {\n   ($month,$day) = ($month{$3},$1);\n   $year = $5\n        if($5);\n  }\n  \n # Date: 12-12-96 (using '-', '.' or '/' )\n  \n elsif($dtstr =~ s#\\s(\\d\\d*)([\\-\\./])(\\d\\d?)(\\2(\\d\\d+))?\\s# #o)\n  {\n   ($month,$day) = ($1 - 1,$3);\n   if($5)\n    {\n     $year = $5;\n     # Possible match for 1995-01-24 (short mainframe date format);\n     ($year,$month,$day) = ($1, $3 - 1, $5)\n    \t    if($month > 12);\n    }\n  }\n elsif($dtstr =~ s#\\s(\\d+)\\s*($sufpat)?\\s*($monpat)# #o)\n  {\n   ($month,$day) = ($month{$3},$1);\n  }\n elsif($dtstr =~ s#($monpat)\\s*(\\d+)\\s*($sufpat)?\\s# #o)\n  {\n   ($month,$day) = ($month{$1},$2);\n  }\n\n # Date: 961212\n\n elsif($dtstr =~ s#\\s(\\d\\d)(\\d\\d)(\\d\\d)\\s# #o)\n  {\n   ($year,$month,$day) = ($1,$2-1,$3);\n  }\n\n $year = $1\n    if(!defined($year) && $dtstr =~ s#\\s(\\d{2}(\\d{2})?)[\\s\\.,]# #o);\n\n # Zone\n\n if($dtstr =~ s#\\s\"?(\\w{3,})\\s# #o) \n  {\n   $zone = tz_offset($1);\n   return ()\n    unless(defined $zone);\n  }\n elsif($dtstr =~ s#\\s(([\\-\\+])\\d\\d?)(\\d\\d)\\s# #o)\n  {\n   my $m = $2 . $3;\n   $zone = 60 * ($m + (60 * $1));\n  }\n\n return ()\n    if($dtstr =~ /\\S/o);\n\n $hh += 12\n\tif(defined $hh && $merid == $PM);\n\n $year -= 1900\n\tif(defined $year && $year > 1900);\n\n return ($ss,$mm,$hh,$day,$month,$year,$zone);\n}\nESQ\n\nuse vars qw($day_ref $mon_ref $suf_ref $obj);\n\nsub gen_parser\n{\n local($day_ref,$mon_ref,$suf_ref,$obj) = @_;\n\n if($obj)\n  {\n   my $obj_strptime = $strptime;\n   substr($obj_strptime,index($strptime,\"sub\")+6,0) = <<'ESQ';\n shift; # package\nESQ\n   return eval \"$obj_strptime\";\n  }\n\n eval \"$strptime\";\n\n}\n\n*strptime = gen_parser(\\%day,\\%month,\\@suf);\n\nsub str2time\n{\n my @t = strptime(@_);\n\n return undef\n\tunless @t;\n\n my($ss,$mm,$hh,$day,$month,$year,$zone) = @t;\n my @lt  = localtime(time);\n\n $hh    ||= 0;\n $mm    ||= 0;\n $ss    ||= 0;\n\n $month = $lt[4]\n\tunless(defined $month);\n\n $day  = $lt[3]\n\tunless(defined $day);\n\n $year = ($month > $lt[4]) ? ($lt[5] - 1) : $lt[5]\n\tunless(defined $year);\n\n return defined $zone ? timegm($ss,$mm,$hh,$day,$month,$year) - $zone\n    \t    \t      : timelocal($ss,$mm,$hh,$day,$month,$year);\n}\n\n1;\n\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/lib/Time/Zone.pm",
    "content": "\npackage Time::Zone;\n\n=head1 NAME\n\nTime::Zone -- miscellaneous timezone manipulations routines\n\n=head1 SYNOPSIS\n\n\tuse Time::Zone;\n\tprint tz2zone();\n\tprint tz2zone($ENV{'TZ'});\n\tprint tz2zone($ENV{'TZ'}, time());\n\tprint tz2zone($ENV{'TZ'}, undef, $isdst);\n\t$offset = tz_local_offset();\n\t$offset = tz_offset($TZ);\n\n=head1 DESCRIPTION\n\nThis is a collection of miscellaneous timezone manipulation routines.\n\nC<tz2zone()> parses the TZ environment variable and returns a timezone\nstring suitable for inclusion in L<date>-like output.  It opionally takes\na timezone string, a time, and a is-dst flag.\n\nC<tz_local_offset()> determins the offset from GMT time in seconds.  It\nonly does the calculation once.\n\nC<tz_offset()> determines the offset from GMT in seconds of a specified\ntimezone.  \n\nC<tz_name()> determines the name of the timezone based on its offset\n\n=head1 AUTHORS\n\nGraham Barr <bodg@tiuk.ti.com>\nDavid Muir Sharnoff <muir@idiom.com>\nPaul Foley <paul@ascent.com>\n\n=cut\n\nrequire 5.002;\n\nrequire Exporter;\nuse Carp;\nuse strict;\nuse vars qw(@ISA @EXPORT $VERSION @tz_local);\n\n@ISA = qw(Exporter);\n@EXPORT = qw(tz2zone tz_local_offset tz_offset tz_name);\n$VERSION = \"2.04\";\n\n# Parts stolen from code by Paul Foley <paul@ascent.com>\n\nsub tz2zone (;$$$)\n{\n\tmy($TZ, $time, $isdst) = @_;\n\n\tuse vars qw(%tzn_cache);\n\n\t$TZ = defined($ENV{'TZ'}) ? ( $ENV{'TZ'} ? $ENV{'TZ'} : 'GMT' ) : ''\n\t    unless $TZ;\n\n\t# Hack to deal with 'PST8PDT' format of TZ\n\t# Note that this can't deal with all the esoteric forms, but it\n\t# does recognize the most common: [:]STDoff[DST[off][,rule]]\n\n\tif (! defined $isdst) {\n\t\tmy $j;\n\t\t$time = time() unless $time;\n\t\t($j, $j, $j, $j, $j, $j, $j, $j, $isdst) = localtime($time);\n\t}\n\n\tif (defined $tzn_cache{$TZ}->[$isdst]) {\n\t\treturn $tzn_cache{$TZ}->[$isdst];\n\t}\n      \n\tif ($TZ =~ /^\n\t\t    ( [^:\\d+\\-,] {3,} )\n\t\t    ( [+-] ?\n\t\t      \\d {1,2}\n\t\t      ( : \\d {1,2} ) {0,2} \n\t\t    )\n\t\t    ( [^\\d+\\-,] {3,} )?\n\t\t    /x\n\t    ) {\n\t\t$TZ = $isdst ? $4 : $1;\n\t\t$tzn_cache{$TZ} = [ $1, $4 ];\n\t} else {\n\t\t$tzn_cache{$TZ} = [ $TZ, $TZ ];\n\t}\n\treturn $TZ;\n}\n\nsub tz_local_offset (;$)\n{\n\tmy ($time) = @_;\n\n\t$time = time() unless $time;\n\tmy (@l) = localtime($time);\n\tmy $isdst = $l[8];\n\n\tif (defined($tz_local[$isdst])) {\n\t\treturn $tz_local[$isdst];\n\t}\n\n\t$tz_local[$isdst] = &calc_off($time);\n\n\treturn $tz_local[$isdst];\n}\n\nsub calc_off\n{\n\tmy ($time) = @_;\n\n\tmy (@l) = localtime($time);\n\tmy (@g) = gmtime($time);\n\n\tmy $off;\n\n\t$off =     $l[0] - $g[0]\n\t\t+ ($l[1] - $g[1]) * 60\n\t\t+ ($l[2] - $g[2]) * 3600;\n\n\t# subscript 7 is yday.\n\n\tif ($l[7] == $g[7]) {\n\t\t# done\n\t} elsif ($l[7] == $g[7] + 1) {\n\t\t$off += 86400;\n\t} elsif ($l[7] == $g[7] - 1) {\n\t\t$off -= 86400;\n\t} elsif ($l[7] < $g[7]) {\n\t\t# crossed over a year boundry!\n\t\t# localtime is beginning of year, gmt is end\n\t\t# therefore local is ahead\n\t\t$off += 86400;\n\t} else {\n\t\t$off -= 86400;\n\t}\n\n\treturn $off;\n}\n\n# constants\n\nCONFIG: {\n\tuse vars qw(%dstZone %zoneOff %dstZoneOff %Zone);\n\n\t%dstZone = (\n\t#   \"ndt\"  =>   -2*3600-1800,\t # Newfoundland Daylight   \n\t    \"adt\"  =>   -3*3600,  \t # Atlantic Daylight   \n\t    \"edt\"  =>   -4*3600,  \t # Eastern Daylight\n\t    \"cdt\"  =>   -5*3600,  \t # Central Daylight\n\t    \"mdt\"  =>   -6*3600,  \t # Mountain Daylight\n\t    \"pdt\"  =>   -7*3600,  \t # Pacific Daylight\n\t    \"ydt\"  =>   -8*3600,  \t # Yukon Daylight\n\t    \"hdt\"  =>   -9*3600,  \t # Hawaii Daylight\n\t    \"bst\"  =>   +1*3600,  \t # British Summer   \n\t    \"mest\" =>   +2*3600,  \t # Middle European Summer   \n\t    \"sst\"  =>   +2*3600,  \t # Swedish Summer\n\t    \"fst\"  =>   +2*3600,  \t # French Summer\n\t    \"wadt\" =>   +8*3600,  \t # West Australian Daylight\n\t#   \"cadt\" =>  +10*3600+1800,\t # Central Australian Daylight\n\t    \"eadt\" =>  +11*3600,  \t # Eastern Australian Daylight\n\t    \"nzdt\" =>  +13*3600,  \t # New Zealand Daylight   \n\t);\n\n\t%Zone = (\n\t    \"gmt\"\t=>   0,  \t # Greenwich Mean\n\t    \"ut\"        =>   0,  \t # Universal (Coordinated)\n\t    \"utc\"       =>   0,\n\t    \"wet\"       =>   0,  \t # Western European\n\t    \"wat\"       =>  -1*3600,\t # West Africa\n\t    \"at\"        =>  -2*3600,\t # Azores\n\t# For completeness.  BST is also British Summer, and GST is also Guam Standard.\n\t#   \"bst\"       =>  -3*3600,\t # Brazil Standard\n\t#   \"gst\"       =>  -3*3600,\t # Greenland Standard\n\t#   \"nft\"       =>  -3*3600-1800,# Newfoundland\n\t#   \"nst\"       =>  -3*3600-1800,# Newfoundland Standard\n\t    \"ast\"       =>  -4*3600,\t # Atlantic Standard\n\t    \"est\"       =>  -5*3600,\t # Eastern Standard\n\t    \"cst\"       =>  -6*3600,\t # Central Standard\n\t    \"mst\"       =>  -7*3600,\t # Mountain Standard\n\t    \"pst\"       =>  -8*3600,\t # Pacific Standard\n\t    \"yst\"\t=>  -9*3600,\t # Yukon Standard\n\t    \"hst\"\t=> -10*3600,\t # Hawaii Standard\n\t    \"cat\"\t=> -10*3600,\t # Central Alaska\n\t    \"ahst\"\t=> -10*3600,\t # Alaska-Hawaii Standard\n\t    \"nt\"\t=> -11*3600,\t # Nome\n\t    \"idlw\"\t=> -12*3600,\t # International Date Line West\n\t    \"cet\"\t=>  +1*3600, \t # Central European\n\t    \"met\"\t=>  +1*3600, \t # Middle European\n\t    \"mewt\"\t=>  +1*3600, \t # Middle European Winter\n\t    \"swt\"\t=>  +1*3600, \t # Swedish Winter\n\t    \"fwt\"\t=>  +1*3600, \t # French Winter\n\t    \"eet\"\t=>  +2*3600, \t # Eastern Europe, USSR Zone 1\n\t    \"bt\"\t=>  +3*3600, \t # Baghdad, USSR Zone 2\n\t#   \"it\"\t=>  +3*3600+1800,# Iran\n\t    \"zp4\"\t=>  +4*3600, \t # USSR Zone 3\n\t    \"zp5\"\t=>  +5*3600, \t # USSR Zone 4\n\t#   \"ist\"\t=>  +5*3600+1800,# Indian Standard\n\t    \"zp6\"\t=>  +6*3600, \t # USSR Zone 5\n\t# For completeness.  NST is also Newfoundland Stanard, and SST is also Swedish Summer.\n\t#   \"nst\"\t=>  +6*3600+1800,# North Sumatra\n\t#   \"sst\"\t=>  +7*3600, \t # South Sumatra, USSR Zone 6\n\t    \"wast\"\t=>  +7*3600, \t # West Australian Standard\n\t#   \"jt\"\t=>  +7*3600+1800,# Java (3pm in Cronusland!)\n\t    \"cct\"\t=>  +8*3600, \t # China Coast, USSR Zone 7\n\t    \"jst\"\t=>  +9*3600,\t # Japan Standard, USSR Zone 8\n\t#   \"cast\"\t=>  +9*3600+1800,# Central Australian Standard\n\t    \"east\"\t=> +10*3600,\t # Eastern Australian Standard\n\t    \"gst\"\t=> +10*3600,\t # Guam Standard, USSR Zone 9\n\t    \"nzt\"\t=> +12*3600,\t # New Zealand\n\t    \"nzst\"\t=> +12*3600,\t # New Zealand Standard\n\t    \"idle\"\t=> +12*3600,\t # International Date Line East\n\t);\n\n\t%zoneOff = reverse(%Zone);\n\t%dstZoneOff = reverse(%dstZone);\n\n\t# Preferences\n\n\t$zoneOff{0}       = 'gmt';\n\t$dstZoneOff{3600} = 'bst';\n\n}\n\nsub tz_offset (;$$)\n{\n\tmy ($zone, $time) = @_;\n\n\treturn &tz_local_offset() unless($zone);\n\n\t$time = time() unless $time;\n\tmy(@l) = localtime($time);\n\tmy $dst = $l[8];\n\n\t$zone = lc $zone;\n\n\tif($zone =~ /^(([\\-\\+])\\d\\d?)(\\d\\d)$/) {\n\t\tmy $v = $2 . $3;\n\t\treturn $1 * 3600 + $v * 60;\n\t} elsif (exists $dstZone{$zone} && ($dst || !exists $Zone{$zone})) {\n\t\treturn $dstZone{$zone};\n\t} elsif(exists $Zone{$zone}) {\n\t\treturn $Zone{$zone};\n\t}\n\tundef;\n}\n\nsub tz_name (;$$)\n{\n\tmy ($off, $dst) = @_;\n\n\t$off = tz_offset()\n\t\tunless(defined $off);\n\n\t$dst = (localtime(time))[8]\n\t\tunless(defined $dst);\n\n\tif (exists $dstZoneOff{$off} && ($dst || !exists $zoneOff{$off})) {\n\t\treturn $dstZoneOff{$off};\n\t} elsif (exists $zoneOff{$off}) {\n\t\treturn $zoneOff{$off};\n\t}\n\tsprintf(\"%+05d\", int($off / 60) * 100 + $off % 60);\n}\n\n1;\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/limit_conn.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n\n# limit_req based tests for nginx limit_conn module.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy limit_conn limit_req/);\n\n$t->write_file_expand('nginx.conf', <<'EOF')->plan(8);\n\n%%TEST_GLOBALS%%\n\ndaemon off;\nworker_processes 1;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    limit_req_zone   $binary_remote_addr  zone=req:1m rate=30r/m;\n\n    limit_conn_zone  $binary_remote_addr  zone=zone:1m;\n    limit_conn_zone  $binary_remote_addr  zone=zone2:1m;\n    limit_conn_zone  $binary_remote_addr  zone=custom:1m;\n\n    server {\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        location /w {\n            limit_req  zone=req burst=10;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            proxy_pass http://127.0.0.1:8081;\n            limit_conn zone 1;\n        }\n\n        location /1 {\n            limit_conn zone 1;\n        }\n\n        location /zone {\n            limit_conn zone2 1;\n        }\n\n        location /unlim {\n            limit_conn zone 5;\n        }\n\n        location /custom {\n            proxy_pass http://127.0.0.1:8081/;\n            limit_conn_log_level info;\n            limit_conn_status 501;\n            limit_conn custom 1;\n        }\n    }\n}\n\nEOF\n\n$t->run();\n\n###############################################################################\n\n# charge limit_req\n\nhttp_get('/w');\n\n# same and other zones in different locations\n\nmy $s = http_get('/w', start => 1);\nlike(http_get('/'), qr/^HTTP\\/1.. 503 /, 'rejected');\nlike(http_get('/1'), qr/^HTTP\\/1.. 503 /, 'rejected different location');\nunlike(http_get('/zone'), qr/^HTTP\\/1.. 503 /, 'passed different zone');\n\nclose $s;\nunlike(http_get('/1'), qr/^HTTP\\/1.. 503 /, 'passed');\n\n# custom error code and log level\n\n$s = http_get('/custom/w', start => 1);\nlike(http_get('/custom'), qr/^HTTP\\/1.. 501 /, 'limit_conn_status');\n\nlike($t->read_file('error.log'),\n\tqr/\\[info\\].*limiting connections by zone \"custom\"/,\n\t'limit_conn_log_level');\n\n# limited after unlimited\n\n$s = http_get('/w', start => 1);\nlike(http_get('/unlim'), qr/404 Not Found/, 'unlimited passed');\nlike(http_get('/'), qr/503 Service/, 'limited rejected');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/limit_conn_complex.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# limit_req based tests for limit_conn module with complex keys.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse IO::Select;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy limit_conn limit_req/)\n\t->plan(4);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    limit_req_zone   $binary_remote_addr$arg_r  zone=req:1m rate=1r/m;\n    limit_req_zone   $binary_remote_addr        zone=re2:1m rate=1r/m;\n    limit_conn_zone  $binary_remote_addr$arg_c  zone=conn:1m;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            limit_conn conn 1;\n        }\n\n        location /w {\n            limit_conn conn 1;\n            proxy_pass http://127.0.0.1:8080/req2;\n        }\n\n        location /req {\n            limit_req  zone=req burst=2;\n        }\n\n        location /req2 {\n            limit_req  zone=re2 burst=2;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('req', '');\n$t->run();\n\n###############################################################################\n\nmy $s;\n\n# charge limit_req\n\nhttp_get('/req');\n\n# limit_req tests\n\n$s = http_get('/req', start => 1);\nok(!IO::Select->new($s)->can_read(1), 'limit_req same key');\n\nlike(http_get('/req?r=2'), qr/200 OK/, 'limit_req different key');\n\n# limit_conn tests\n\nhttp_get('/req2');\n\n$s = http_get('/w', start => 1);\nselect undef, undef, undef, 0.2;\n\nlike(http_get('/'), qr/^HTTP\\/1.. 503 /, 'limit_conn same key');\nunlike(http_get('/?c=2'), qr/^HTTP\\/1.. 503 /, 'limit_conn different key');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/limit_conn_dry_run.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for limit_conn_dry_run directive, limit_conn_status variable.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy limit_conn limit_req/);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    limit_req_zone   $binary_remote_addr  zone=req:1m rate=30r/m;\n    limit_conn_zone  $binary_remote_addr  zone=zone:1m;\n\n    log_format test $uri:$limit_conn_status;\n\n    server {\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        location /w {\n            limit_req  zone=req burst=10;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        add_header X-Status $limit_conn_status always;\n        access_log %%TESTDIR%%/test.log test;\n\n        location /reject {\n            proxy_pass http://127.0.0.1:8081/w;\n            limit_conn zone 1;\n        }\n\n        location /dry {\n            limit_conn zone 1;\n            limit_conn_dry_run on;\n        }\n\n        location / { }\n    }\n}\n\nEOF\n\n$t->write_file('w', '');\n$t->run()->plan(6);\n\n###############################################################################\n\nlike(http_get('/reject'), qr/ 200 .*PASSED/s, 'passed');\n\nmy $s = http_get('/reject', start => 1);\nlike(http_get('/reject'), qr/ 503 .*REJECTED(?!_)/s, 'rejected');\nlike(http_get('/dry'), qr/ 404 .*REJECTED_DRY_RUN/s, 'rejected dry run');\nunlike(http_get('/'), qr/X-Status/, 'no limit');\n\nclose $s;\n\n$t->stop();\n\nlike($t->read_file('error.log'), qr/limiting connections, dry/, 'log dry run');\nlike($t->read_file('test.log'), qr|^/:-|m, 'log not found');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/limit_rate.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for limit_rate and limit_rate_after directives.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nplan(skip_all => 'win32') if $^O eq 'MSWin32';\n\nmy $t = Test::Nginx->new()->has(qw/http proxy/);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    log_format test escape=none $uri:$arg_a$arg_xal:$upstream_response_time;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            proxy_pass http://127.0.0.1:8081;\n            access_log %%TESTDIR%%/test.log test;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        limit_rate 12k;\n        limit_rate_after 256;\n\n        location /data {\n            add_header X-Accel-Redirect $arg_xar;\n            add_header X-Accel-Limit-Rate $arg_xal;\n        }\n\n        location /redirect {\n            limit_rate 0;\n            alias %%TESTDIR%%/data;\n        }\n\n        location /var {\n            alias %%TESTDIR%%/data;\n            limit_rate $arg_l;\n            limit_rate_after $arg_a;\n        }\n\n        location /proxy/ {\n            proxy_pass http://127.0.0.1:8081/;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('data', 'X' x 30000);\n$t->run()->plan(7);\n\n###############################################################################\n\n# NB: response time may be 1s less, if timer is scheduled on upper half second\n\nlike(http_get('/data'), qr/^(XXXXXXXXXX){3000}\\x0d?\\x0a?$/m, 'response body');\nlike($t->read_file('test.log'), qr/data::[12]/, 'limit_rate');\n\n# /proxy -> /redirect\n# before 1.17.0, limit was set once in ngx_http_update_location_config()\n\nhttp_get('/proxy/data?xar=/redirect');\nlike($t->read_file('test.log'), qr!proxy/data::0!, 'X-Accel-Redirect');\n\n# X-Accel-Limit-Rate has higher precedence\n\nhttp_get('/proxy/data?xar=/redirect&xal=13000');\nlike($t->read_file('test.log'), qr!roxy/data:13000:[12]!, 'X-Accel-Limit-Rate');\n\nhttp_get('/var?l=12k&a=256');\nlike($t->read_file('test.log'), qr/var:256:[12]/, 'variable');\n\nhttp_get('/var?l=12k&a=40k');\nlike($t->read_file('test.log'), qr/var:40k:0/, 'variable after');\n\nhttp_get('/var');\nlike($t->read_file('test.log'), qr/var::0/, 'variables unset');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/limit_req.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for nginx limit_req module.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http limit_req/)->plan(6);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    limit_req_zone  $binary_remote_addr  zone=one:1m   rate=2r/s;\n    limit_req_zone  $binary_remote_addr  zone=long:1m  rate=2r/s;\n    limit_req_zone  $binary_remote_addr  zone=fast:1m  rate=1000r/s;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n        location / {\n            limit_req    zone=one  burst=1  nodelay;\n        }\n        location /status {\n            limit_req    zone=one  burst=1  nodelay;\n\n            limit_req_status  501;\n        }\n        location /long {\n            limit_req    zone=long  burst=5;\n        }\n        location /fast {\n            limit_req    zone=fast  burst=1;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('test1.html', 'XtestX');\n$t->write_file('long.html', \"1234567890\\n\" x (1 << 16));\n$t->write_file('fast.html', 'XtestX');\n$t->run();\n\n###############################################################################\n\nlike(http_get('/test1.html'), qr/^HTTP\\/1.. 200 /m, 'request');\nhttp_get('/test1.html');\nlike(http_get('/test1.html'), qr/^HTTP\\/1.. 503 /m, 'request rejected');\nlike(http_get('/status.html'), qr/^HTTP\\/1.. 501 /m, 'request rejected status');\nhttp_get('/test1.html');\nhttp_get('/test1.html');\n\n# Second request will be delayed by limit_req, make sure it isn't truncated.\n# The bug only manifests itself if buffer will be filled, so sleep for a while\n# before reading response.\n\nmy $l1 = length(http_get('/long.html'));\nmy $l2 = length(http_get('/long.html', sleep => 0.6));\nis($l2, $l1, 'delayed big request not truncated');\n\n# make sure rejected requests are not counted, and access is again allowed\n# after 1/rate seconds\n\nlike(http_get('/test1.html'), qr/^HTTP\\/1.. 200 /m, 'rejects not counted');\n\n# make sure negative excess values are handled properly\n\nhttp_get('/fast.html');\nselect undef, undef, undef, 0.1;\nlike(http_get('/fast.html'), qr/^HTTP\\/1.. 200 /m, 'negative excess');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/limit_req2.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for nginx limit_req module, multiple limits.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http limit_req/)->plan(14);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    limit_req_zone  $arg_a  zone=slow:1m   rate=1r/m;\n    limit_req_zone  $arg_b  zone=fast:1m   rate=1000r/s;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            limit_req    zone=fast;\n            limit_req    zone=slow;\n        }\n\n        location /t2.html {\n            limit_req    zone=fast  nodelay;\n            limit_req    zone=slow  nodelay;\n\n            alias %%TESTDIR%%/t1.html;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('t1.html', 'XtestX');\n$t->run();\n\n###############################################################################\n\nlike(http_get('/t1.html?b=1'), qr/^HTTP\\/1.. 200 /m, 'fast');\nselect undef, undef, undef, 0.1;\nlike(http_get('/t1.html?b=1'), qr/^HTTP\\/1.. 200 /m, 'fast - passed');\n\nlike(http_get('/t1.html?a=1'), qr/^HTTP\\/1.. 200 /m, 'slow');\nselect undef, undef, undef, 0.1;\nlike(http_get('/t1.html?a=1'), qr/^HTTP\\/1.. 503 /m, 'slow - rejected');\n\nlike(http_get('/t1.html?a=2&b=2'), qr/^HTTP\\/1.. 200 /m, 'both');\nselect undef, undef, undef, 0.1;\nlike(http_get('/t1.html?a=2&b=2'), qr/^HTTP\\/1.. 503 /m, 'both - rejected');\n\nlike(http_get('/t1.html'), qr/^HTTP\\/1.. 200 /m, 'no key');\nlike(http_get('/t1.html'), qr/^HTTP\\/1.. 200 /m, 'no key - passed');\n\n# nodelay\n\nlike(http_get('/t2.html?b=3'), qr/^HTTP\\/1.. 200 /m, 'nodelay fast');\nselect undef, undef, undef, 0.1;\nlike(http_get('/t2.html?b=3'), qr/^HTTP\\/1.. 200 /m, 'nodelay fast - passed');\n\nlike(http_get('/t2.html?a=3'), qr/^HTTP\\/1.. 200 /m, 'nodelay slow');\nselect undef, undef, undef, 0.1;\nlike(http_get('/t2.html?a=3'), qr/^HTTP\\/1.. 503 /m, 'nodelay slow - rejected');\n\nlike(http_get('/t2.html?a=4&b=4'), qr/^HTTP\\/1.. 200 /m, 'nodelay both');\nselect undef, undef, undef, 0.1;\nlike(http_get('/t2.html?a=4&b=4'), qr/^HTTP\\/1.. 503 /m,\n\t'nodelay both - rejected');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/limit_req_delay.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for nginx limit_req module, delay parameter.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx qw/ :DEFAULT http_end /;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http limit_req/)->plan(4);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    limit_req_zone $binary_remote_addr zone=one:1m rate=30r/m;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            limit_req zone=one delay=1 burst=2;\n            add_header X-Time $request_time;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('delay.html', 'XtestX');\n$t->run();\n\n###############################################################################\n\nlike(http_get('/delay.html'), qr/^HTTP\\/1.. 200 /m, 'request');\nlike(http_get('/delay.html'), qr/X-Time: 0.000/, 'not yet delayed');\nmy $s = http_get('/delay.html', start => 1, sleep => 0.2);\nlike(http_get('/delay.html'), qr/^HTTP\\/1.. 503 /m, 'rejected');\nlike(http_end($s), qr/^HTTP\\/1.. 200 .*X-Time: (?!0.000)/ms, 'delayed');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/limit_req_dry_run.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for nginx limit_req module, limit_req_dry_run directive.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http limit_req/);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    limit_req_zone  $binary_remote_addr  zone=one:1m   rate=1r/m;\n\n    log_format test $uri:$limit_req_status;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        limit_req_dry_run  on;\n        add_header X-Status $limit_req_status always;\n        access_log %%TESTDIR%%/test.log test;\n\n        location /delay {\n            limit_req    zone=one  burst=2;\n        }\n\n        location /reject {\n            limit_req    zone=one;\n        }\n\n        location /reject/off {\n            limit_req    zone=one;\n\n            limit_req_dry_run off;\n        }\n\n        location / { }\n    }\n}\n\nEOF\n\n$t->write_file('delay', 'SEE-THIS');\n$t->write_file('reject', 'SEE-THIS');\n$t->run()->plan(8);\n\n###############################################################################\n\nlike(http_get('/delay'), qr/ 200 .*PASSED/ms, 'dry run - passed');\nlike(http_get('/delay'), qr/ 200 .*DELAYED_DRY_RUN/ms, 'dry run - delayed');\nlike(http_get('/reject'), qr/ 200 .*REJECTED_DRY_RUN/ms, 'dry run - rejected');\n\nlike(http_get('/reject/off'), qr/ 503 .*REJECTED/ms, 'dry run off - rejected');\n\nunlike(http_get('/'), qr/X-Status/, 'no limit');\n\n$t->stop();\n\nlike($t->read_file('error.log'), qr/delaying request, dry/, 'log - delay');\nlike($t->read_file('error.log'), qr/limiting requests, dry/, 'log - reject');\n\nlike($t->read_file('test.log'), qr|^/:-|m, 'log - not found');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/mail_capability.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for imap/pop3/smtp capabilities.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse Test::Nginx::IMAP;\nuse Test::Nginx::POP3;\nuse Test::Nginx::SMTP;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nlocal $SIG{PIPE} = 'IGNORE';\nmy $t = Test::Nginx->new()->has(qw/mail mail_ssl imap pop3 smtp/)\n\t->has_daemon('openssl')->plan(17);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nmail {\n    ssl_certificate_key localhost.key;\n    ssl_certificate localhost.crt;\n\n    auth_http  http://127.0.0.1:8080; # unused\n\n    pop3_auth  plain apop cram-md5;\n\n    server {\n        listen     127.0.0.1:8143;\n        protocol   imap;\n        imap_capabilities SEE-THIS;\n    }\n\n    server {\n        listen     127.0.0.1:8144;\n        protocol   imap;\n        starttls   on;\n    }\n\n    server {\n        listen     127.0.0.1:8145;\n        protocol   imap;\n        starttls   only;\n    }\n\n    server {\n        listen     127.0.0.1:8110;\n        protocol   pop3;\n    }\n\n    server {\n        listen     127.0.0.1:8111;\n        protocol   pop3;\n        starttls   on;\n    }\n\n    server {\n        listen     127.0.0.1:8112;\n        protocol   pop3;\n        starttls   only;\n    }\n\n    server {\n        listen     127.0.0.1:8025;\n        protocol   smtp;\n        starttls   off;\n    }\n\n    server {\n        listen     127.0.0.1:8026;\n        protocol   smtp;\n        starttls   on;\n    }\n\n    server {\n        listen     127.0.0.1:8027;\n        protocol   smtp;\n        starttls   only;\n    }\n}\n\nEOF\n\n$t->write_file('openssl.conf', <<EOF);\n[ req ]\ndefault_bits = 2048\nencrypt_key = no\ndistinguished_name = req_distinguished_name\n[ req_distinguished_name ]\nEOF\n\nmy $d = $t->testdir();\n\nforeach my $name ('localhost') {\n\tsystem('openssl req -x509 -new '\n\t\t. \"-config $d/openssl.conf -subj /CN=$name/ \"\n\t\t. \"-out $d/$name.crt -keyout $d/$name.key \"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create certificate for $name: $!\\n\";\n}\n\n$t->run();\n\n###############################################################################\n\n# imap, custom capabilities\n\nmy $s = Test::Nginx::IMAP->new();\n$s->read();\n\n$s->send('1 CAPABILITY');\n$s->check(qr/^\\* CAPABILITY SEE-THIS AUTH=PLAIN/, 'imap capability');\n$s->ok('imap capability completed');\n\n# imap starttls\n\n$s = Test::Nginx::IMAP->new(PeerAddr => '127.0.0.1:' . port(8144));\n$s->read();\n\n$s->send('1 CAPABILITY');\n$s->check(qr/^\\* CAPABILITY IMAP4 IMAP4rev1 UIDPLUS AUTH=PLAIN STARTTLS/,\n\t'imap capability starttls');\n\n# imap starttls only\n\n$s = Test::Nginx::IMAP->new(PeerAddr => '127.0.0.1:' . port(8145));\n$s->read();\n\n$s->send('1 CAPABILITY');\n$s->check(qr/^\\* CAPABILITY IMAP4 IMAP4rev1 UIDPLUS STARTTLS LOGINDISABLED/,\n\t'imap capability starttls only');\n\n# pop3\n\n$s = Test::Nginx::POP3->new(PeerAddr => '127.0.0.1:' . port(8110));\n$s->read();\n\n$s->send('CAPA');\n$s->ok('pop3 capa');\n\nmy $caps = get_auth_caps($s);\nlike($caps, qr/USER/, 'pop3 - user');\nlike($caps, qr/SASL (PLAIN LOGIN|LOGIN PLAIN) CRAM-MD5/, 'pop3 - methods');\nunlike($caps, qr/STLS/, 'pop3 - no stls');\n\n# pop3 starttls\n\n$s = Test::Nginx::POP3->new(PeerAddr => '127.0.0.1:' . port(8111));\n$s->read();\n\n$s->send('CAPA');\n\n$caps = get_auth_caps($s);\nlike($caps, qr/USER/, 'pop3 starttls - user');\nlike($caps, qr/SASL (PLAIN LOGIN|LOGIN PLAIN) CRAM-MD5/,\n\t'pop3 starttls - methods');\nlike($caps, qr/STLS/, 'pop3 startls - stls');\n\n# pop3 starttls only\n\n$s = Test::Nginx::POP3->new(PeerAddr => '127.0.0.1:' . port(8112));\n$s->read();\n\n$s->send('CAPA');\n\n$caps = get_auth_caps($s);\nunlike($caps, qr/USER/, 'pop3 starttls only - no user');\nunlike($caps, qr/SASL/, 'pop3 starttls only - no methods');\nlike($caps, qr/STLS/, 'pop3 startls only - stls');\n\n# smtp\n\n$s = Test::Nginx::SMTP->new(PeerAddr => '127.0.0.1:' . port(8025));\n$s->read();\n\n$s->send('EHLO example.com');\n$s->check(qr/^250 AUTH PLAIN LOGIN\\x0d\\x0a?/, 'smtp ehlo');\n\n# smtp starttls\n\n$s = Test::Nginx::SMTP->new(PeerAddr => '127.0.0.1:' . port(8026));\n$s->read();\n\n$s->send('EHLO example.com');\n$s->check(qr/^250 STARTTLS/, 'smtp ehlo - starttls');\n\n# smtp starttls only\n\n$s = Test::Nginx::SMTP->new(PeerAddr => '127.0.0.1:' . port(8027));\n$s->read();\n\n$s->send('EHLO example.com');\n$s->check(qr/^250 STARTTLS/, 'smtp ehlo - starttls only');\n\n###############################################################################\n\nsub get_auth_caps {\n\tmy ($s) = @_;\n\tmy @meth;\n\n\twhile ($s->read()) {\n\t\tlast if /^\\./;\n\t\tpush @meth, $1 if /(.*?)\\x0d\\x0a?/ms;\n\t}\n\tjoin ':', @meth;\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/mail_error_log.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for nginx mail imap module, error_log directive.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse IO::Select;\nuse Sys::Hostname;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse Test::Nginx::IMAP;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nlocal $SIG{PIPE} = 'IGNORE';\nplan(skip_all => 'win32') if $^O eq 'MSWin32';\n\nmy $t = Test::Nginx->new()->has(qw/mail imap http rewrite/);\n\n$t->plan(30)->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\nerror_log %%TESTDIR%%/e_glob.log info;\nerror_log %%TESTDIR%%/e_glob2.log info;\nerror_log syslog:server=127.0.0.1:%%PORT_8981_UDP%% info;\n\ndaemon off;\n\nevents {\n}\n\nmail {\n    proxy_timeout  15s;\n    auth_http  http://127.0.0.1:8080/mail/auth;\n\n    server {\n        listen     127.0.0.1:8143;\n        protocol   imap;\n\n        error_log %%TESTDIR%%/e_debug.log debug;\n        error_log %%TESTDIR%%/e_info.log info;\n        error_log syslog:server=127.0.0.1:%%PORT_8982_UDP%% info;\n        error_log stderr info;\n    }\n\n    server {\n        listen     127.0.0.1:8145;\n        protocol   imap;\n\n        error_log syslog:server=127.0.0.1:%%PORT_8983_UDP%% info;\n    }\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location = /mail/auth {\n            add_header Auth-Status OK;\n            add_header Auth-Server 127.0.0.1;\n            add_header Auth-Port %%PORT_8144%%;\n            add_header Auth-Wait 1;\n            return 204;\n        }\n    }\n}\n\nEOF\n\nopen OLDERR, \">&\", \\*STDERR;\nopen STDERR, '>', $t->testdir() . '/stderr' or die \"Can't reopen STDERR: $!\";\nopen my $stderr, '<', $t->testdir() . '/stderr'\n\tor die \"Can't open stderr file: $!\";\n\n$t->run_daemon(\\&Test::Nginx::IMAP::imap_test_daemon);\n$t->run_daemon(\\&syslog_daemon, port(8981), $t, 's_glob.log');\n$t->run_daemon(\\&syslog_daemon, port(8982), $t, 's_info.log');\n\n$t->waitforsocket('127.0.0.1:' . port(8144));\n$t->waitforfile($t->testdir . '/s_glob.log');\n$t->waitforfile($t->testdir . '/s_info.log');\n\n$t->run();\n\nopen STDERR, \">&\", \\*OLDERR;\n\n###############################################################################\n\nmy $s = Test::Nginx::IMAP->new();\n$s->ok('greeting');\n\n# error_log levels\n\nSKIP: {\nskip \"no --with-debug\", 1 unless $t->has_module('--with-debug');\n\nisnt(lines($t, 'e_debug.log', '[debug]'), 0, 'file debug in debug');\n\n}\n\nisnt(lines($t, 'e_info.log', '[info]'), 0, 'file info in info');\nis(lines($t, 'e_info.log', '[debug]'), 0, 'file debug in info');\nisnt(lines($t, 'stderr', '[info]'), 0, 'stderr info in info');\nis(lines($t, 'stderr', '[debug]'), 0, 'stderr debug in info');\n\n# multiple error_log\n\nlike($t->read_file('e_glob.log'), qr!nginx/[.0-9]+!, 'error global');\nlike($t->read_file('e_glob2.log'), qr!nginx/[.0-9]+!, 'error global 2');\nis_deeply(levels($t, 'e_glob.log'), levels($t, 'e_glob2.log'),\n\t'multiple error global');\n\n# syslog\n\nparse_syslog_message('syslog', get_syslog());\n\nis_deeply(levels($t, 's_glob.log'), levels($t, 'e_glob.log'),\n\t'global syslog messages');\nis_deeply(levels($t, 's_info.log'), levels($t, 'e_info.log'),\n\t'mail syslog messages');\n\n###############################################################################\n\nsub lines {\n\tmy ($t, $file, $pattern) = @_;\n\n\tif ($file eq 'stderr') {\n\t\tmy $value = map { $_ =~ /\\Q$pattern\\E/ } (<$stderr>);\n\t\t$stderr->clearerr();\n\t\treturn $value;\n\t}\n\n\tmy $path = $t->testdir() . '/' . $file;\n\topen my $fh, '<', $path or return \"$!\";\n\tmy $value = map { $_ =~ /\\Q$pattern\\E/ } (<$fh>);\n\tclose $fh;\n\treturn $value;\n}\n\nsub levels {\n\tmy ($t, $file) = @_;\n\tmy %levels_hash;\n\n\tmap { $levels_hash{$_}++; } ($t->read_file($file) =~ /(\\[\\w+\\])/g);\n\n\treturn \\%levels_hash;\n}\n\nsub get_syslog {\n\tmy $data = '';\n\tmy ($s);\n\n\teval {\n\t\tlocal $SIG{ALRM} = sub { die \"timeout\\n\" };\n\t\tlocal $SIG{PIPE} = sub { die \"sigpipe\\n\" };\n\t\talarm(1);\n\t\t$s = IO::Socket::INET->new(\n\t\t\tProto => 'udp',\n\t\t\tLocalAddr => '127.0.0.1:' . port(8983)\n\t\t);\n\t\talarm(0);\n\t};\n\talarm(0);\n\tif ($@) {\n\t\tlog_in(\"died: $@\");\n\t\treturn undef;\n\t}\n\n\tTest::Nginx::IMAP->new(PeerAddr => '127.0.0.1:' . port(8145))->read();\n\n\tIO::Select->new($s)->can_read(1.5);\n\twhile (IO::Select->new($s)->can_read(0.1)) {\n\t\tmy $buffer;\n\t\tsysread($s, $buffer, 4096);\n\t\t$data .= $buffer;\n\t}\n\t$s->close();\n\treturn $data;\n}\n\nsub parse_syslog_message {\n\tmy ($desc, $line) = @_;\n\n\tok($line, $desc);\n\nSKIP: {\n\tskip \"$desc timeout\", 18 unless $line;\n\n\tmy @months = ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug',\n\t\t'Sep', 'Oct', 'Nov', 'Dec');\n\n\tmy ($pri, $mon, $mday, $hour, $minute, $sec, $host, $tag, $msg) =\n\t\t$line =~ /^<(\\d{1,3})>\t\t\t\t# PRI\n\t\t\t([A-Z][a-z]{2})\\s\t\t\t# mon\n\t\t\t([ \\d]\\d)\\s(\\d{2}):(\\d{2}):(\\d{2})\\s\t# date\n\t\t\t([\\S]*)\\s\t\t\t\t# host\n\t\t\t(\\w{1,32}):\\s\t\t\t\t# tag\n\t\t\t(.*)/x;\t\t\t\t\t# MSG\n\n\tmy $sev = $pri & 0x07;\n\tmy $fac = ($pri & 0x03f8) >> 3;\n\n\tok(defined($pri), \"$desc has PRI\");\n\tok($sev >= 0 && $sev <= 7, \"$desc valid severity\");\n\tok($fac >= 0 && $fac < 24, \"$desc valid facility\");\n\n\tok(defined($mon), \"$desc has month\");\n\tok((grep $mon, @months), \"$desc valid month\");\n\n\tok(defined($mday), \"$desc has day\");\n\tok($mday <= 31, \"$desc valid day\");\n\n\tok(defined($hour), \"$desc has hour\");\n\tok($hour < 24, \"$desc valid hour\");\n\n\tok(defined($minute), \"$desc has minutes\");\n\tok($minute < 60, \"$desc valid minutes\");\n\n\tok(defined($sec), \"$desc has seconds\");\n\tok($sec < 60, \"$desc valid seconds\");\n\n\tok(defined($host), \"$desc has host\");\n\tis($host, lc(hostname()), \"$desc valid host\");\n\n\tok(defined($tag), \"$desc has tag\");\n\tlike($tag, qr'\\w+', \"$desc valid tag\");\n\n\tok(length($msg) > 0, \"$desc valid CONTENT\");\n}\n\n}\n\n###############################################################################\n\nsub syslog_daemon {\n\tmy ($port, $t, $file) = @_;\n\n\tmy $s = IO::Socket::INET->new(\n\t\tProto => 'udp',\n\t\tLocalAddr => \"127.0.0.1:$port\"\n\t);\n\n\topen my $fh, '>', $t->testdir() . '/' . $file;\n\tselect $fh; $| = 1;\n\n\twhile (1) {\n\t\tmy $buffer;\n\t\t$s->recv($buffer, 4096);\n\t\tprint $fh $buffer . \"\\n\";\n\t}\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/mail_imap.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for nginx mail imap module.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse MIME::Base64;\nuse Socket qw/ CRLF /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse Test::Nginx::IMAP;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nlocal $SIG{PIPE} = 'IGNORE';\n\nmy $t = Test::Nginx->new()->has(qw/mail imap http rewrite/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nmail {\n    proxy_pass_error_message  on;\n    proxy_timeout  15s;\n    auth_http  http://127.0.0.1:8080/mail/auth;\n\n    server {\n        listen     127.0.0.1:8143;\n        protocol   imap;\n        imap_auth  plain cram-md5 external;\n    }\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location = /mail/auth {\n            set $reply ERROR;\n            set $passw \"\";\n\n            set $userpass \"$http_auth_user:$http_auth_pass\";\n            if ($userpass = 'test@example.com:secret') {\n                set $reply OK;\n            }\n            if ($userpass = 'te\\\\\"st@example.com:se\\\\\"cret') {\n                set $reply OK;\n            }\n\n            set $userpass \"$http_auth_user:$http_auth_salt:$http_auth_pass\";\n            if ($userpass ~ '^test@example.com:<.*@.*>:0{32}$') {\n                set $reply OK;\n                set $passw secret;\n            }\n\n            set $userpass \"$http_auth_method:$http_auth_user:$http_auth_pass\";\n            if ($userpass = 'external:test@example.com:') {\n                set $reply OK;\n                set $passw secret;\n            }\n\n            add_header Auth-Status $reply;\n            add_header Auth-Server 127.0.0.1;\n            add_header Auth-Port %%PORT_8144%%;\n            add_header Auth-Pass $passw;\n            add_header Auth-Wait 1;\n            return 204;\n        }\n    }\n}\n\nEOF\n\n$t->run_daemon(\\&Test::Nginx::IMAP::imap_test_daemon);\n$t->run()->plan(29);\n\n$t->waitforsocket('127.0.0.1:' . port(8144));\n\n###############################################################################\n\n# login\n\nmy $s = Test::Nginx::IMAP->new();\n$s->ok('greeting');\n\n$s->send('a01 LOGIN');\n$s->check(qr/^a01 BAD/, 'login without arguments');\n\n$s->send('a02 LOGIN test@example.com bad');\n$s->check(qr/^a02 NO/, 'login with bad password');\n\n$s->send('a03 LOGIN test@example.com secret');\n$s->ok('login');\n\n# auth\n\n$s = Test::Nginx::IMAP->new();\n$s->read();\n\n$s->send('1 AUTHENTICATE');\n$s->check(qr/^\\S+ BAD/, 'auth without arguments');\n\n# auth plain\n\n$s->send('1 AUTHENTICATE PLAIN ' . encode_base64(\"\\0test\\@example.com\\0bad\", ''));\n$s->check(qr/^\\S+ NO/, 'auth plain with bad password');\n\n$s->send('1 AUTHENTICATE PLAIN ' . encode_base64(\"\\0test\\@example.com\\0secret\", ''));\n$s->ok('auth plain');\n\n# auth login simple\n\n$s = Test::Nginx::IMAP->new();\n$s->read();\n\n$s->send('1 AUTHENTICATE LOGIN');\n$s->check(qr/\\+ VXNlcm5hbWU6/, 'auth login username challenge');\n\n$s->send(encode_base64('test@example.com', ''));\n$s->check(qr/\\+ UGFzc3dvcmQ6/, 'auth login password challenge');\n\n$s->send(encode_base64('secret', ''));\n$s->ok('auth login simple');\n\n# auth login with username\n\n$s = Test::Nginx::IMAP->new();\n$s->read();\n\n$s->send('1 AUTHENTICATE LOGIN ' . encode_base64('test@example.com', ''));\n$s->check(qr/\\+ UGFzc3dvcmQ6/, 'auth login with username password challenge');\n\n$s->send(encode_base64('secret', ''));\n$s->ok('auth login with username');\n\n# auth cram-md5\n\n$s = Test::Nginx::IMAP->new();\n$s->read();\n\n$s->send('1 AUTHENTICATE CRAM-MD5');\n$s->check(qr/\\+ /, 'auth cram-md5 challenge');\n\n$s->send(encode_base64('test@example.com ' . ('0' x 32), ''));\n$s->ok('auth cram-md5');\n\n# auth external\n\n$s = Test::Nginx::IMAP->new();\n$s->read();\n\n$s->send('1 AUTHENTICATE EXTERNAL');\n$s->check(qr/\\+ VXNlcm5hbWU6/, 'auth external challenge');\n\n$s->send(encode_base64('test@example.com', ''));\n$s->ok('auth external');\n\n# auth external with username\n\n$s = Test::Nginx::IMAP->new();\n$s->read();\n\n$s->send('1 AUTHENTICATE EXTERNAL ' . encode_base64('test@example.com', ''));\n$s->ok('auth external with username');\n\n# quoted strings\n\n$s = Test::Nginx::IMAP->new();\n$s->read();\n\n$s->send('a01 LOGIN \"te\\\\\\\\\\\"st@example.com\" \"se\\\\\\\\\\\"cret\"');\n$s->ok('quoted strings');\n\n# literals\n\n$s = Test::Nginx::IMAP->new();\n$s->read();\n\n$s->send('a01 LOGIN {18}');\n$s->check(qr/\\+ /, 'login username literal continue');\n\n$s->send('te\\\"st@example.com' . ' {8}');\n$s->check(qr/\\+ /, 'login password literal continue');\n\n$s->send('se\\\"cret');\n$s->ok('login literals');\n\n# non-synchronizing literals\n\n$s = Test::Nginx::IMAP->new();\n$s->read();\n\n$s->send('a01 LOGIN {18+}' . CRLF\n\t. 'te\\\"st@example.com' . ' {8+}' . CRLF\n\t. 'se\\\"cret');\n$s->ok('login non-sync literals');\n\n# backslash in quotes and literals\n\n$s = Test::Nginx::IMAP->new();\n$s->read();\n\n$s->send('a01 LOGIN {18+}' . CRLF\n\t. 'te\\\"st@example.com' . ' \"se\\\\\\\\\\\"cret\"');\n\nTODO: {\nlocal $TODO = 'not yet' unless $t->has_version('1.21.0');\n\n$s->ok('backslash in literal');\n\n}\n\n# pipelining\n\n$s = Test::Nginx::IMAP->new();\n$s->read();\n\n$s->send('a01 INVALID COMMAND WITH ARGUMENTS' . CRLF\n\t. 'a02 NOOP');\n$s->check(qr/^a01 BAD/, 'pipelined invalid command');\n\nTODO: {\nlocal $TODO = 'not yet' unless $t->has_version('1.21.0');\n\n$s->ok('pipelined noop after invalid command');\n\n}\n\n$s->send('a03 FOOBAR {10+}' . CRLF\n\t. 'test test ' . CRLF\n\t. 'a04 NOOP');\n$s->check(qr/^a03 BAD/, 'invalid with non-sync literal');\n$s->check(qr/^(a04 |$)/, 'literal not command');\n\nTODO: {\ntodo_skip('not yet', 2) unless $t->has_version('1.21.0');\n\n# skipped without a fix, since with level-triggered event methods\n# this hogs cpu till the connection is closed by the backend server,\n# and generates a lot of debug logs\n\n$s = Test::Nginx::IMAP->new();\n$s->read();\n\n$s->send('a01 LOGIN test@example.com secret' . CRLF\n\t. 'a02 LOGOUT');\n$s->ok('pipelined login');\n$s->ok('pipelined logout');\n\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/mail_imap_ssl.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for nginx mail imap module with ssl.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse MIME::Base64;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse Test::Nginx::IMAP;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\n\nlocal $SIG{PIPE} = 'IGNORE';\n\nmy $t = Test::Nginx->new()\n\t->has(qw/mail mail_ssl imap http rewrite socket_ssl_sslversion/)\n\t->has_daemon('openssl')->plan(13)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nmail {\n    proxy_pass_error_message  on;\n    proxy_timeout  15s;\n    auth_http  http://127.0.0.1:8080/mail/auth;\n    auth_http_pass_client_cert on;\n\n    ssl_certificate_key 1.example.com.key;\n    ssl_certificate 1.example.com.crt;\n\n    server {\n        listen     127.0.0.1:8143;\n        protocol   imap;\n    }\n\n    server {\n        listen     127.0.0.1:8993 ssl;\n        protocol   imap;\n\n        ssl_verify_client on;\n        ssl_client_certificate 2.example.com.crt;\n    }\n\n    server {\n        listen     127.0.0.1:8994 ssl;\n        protocol   imap;\n\n        ssl_verify_client optional;\n        ssl_client_certificate 2.example.com.crt;\n    }\n\n    server {\n        listen     127.0.0.1:8995 ssl;\n        protocol   imap;\n\n        ssl_verify_client optional;\n        ssl_client_certificate 2.example.com.crt;\n        ssl_trusted_certificate 3.example.com.crt;\n    }\n\n    server {\n        listen     127.0.0.1:8996 ssl;\n        protocol   imap;\n\n        ssl_verify_client optional_no_ca;\n        ssl_client_certificate 2.example.com.crt;\n    }\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    log_format  test  '$http_auth_ssl:$http_auth_ssl_verify:'\n                      '$http_auth_ssl_subject:$http_auth_ssl_issuer:'\n                      '$http_auth_ssl_serial:$http_auth_ssl_fingerprint:'\n                      '$http_auth_ssl_cert:$http_auth_pass';\n    log_format  test2 '$http_auth_ssl_cipher:$http_auth_ssl_protocol';\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location = /mail/auth {\n            access_log auth.log test;\n            access_log auth2.log test2;\n\n            add_header Auth-Status OK;\n            add_header Auth-Server 127.0.0.1;\n            add_header Auth-Port %%PORT_8144%%;\n            add_header Auth-Wait 1;\n            return 204;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('openssl.conf', <<EOF);\n[ req ]\ndefault_bits = 2048\nencrypt_key = no\ndistinguished_name = req_distinguished_name\n[ req_distinguished_name ]\nEOF\n\nmy $d = $t->testdir();\n\nforeach my $name ('1.example.com', '2.example.com', '3.example.com') {\n\tsystem('openssl req -x509 -new '\n\t\t. \"-config $d/openssl.conf -subj /CN=$name/ \"\n\t\t. \"-out $d/$name.crt -keyout $d/$name.key \"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create certificate for $name: $!\\n\";\n}\n\n$t->run_daemon(\\&Test::Nginx::IMAP::imap_test_daemon);\n$t->run()->waitforsocket('127.0.0.1:' . port(8144));\n\n###############################################################################\n\nmy $cred = sub { encode_base64(\"\\0test\\@example.com\\0$_[0]\", '') };\n\n# no ssl connection\n\nmy $s = Test::Nginx::IMAP->new();\n$s->ok('plain connection');\n$s->send('1 AUTHENTICATE PLAIN ' . $cred->(\"s1\"));\n\n# no cert\n\n$s = Test::Nginx::IMAP->new(SSL => 1);\n$s->check(qr/BYE No required SSL certificate/, 'no cert');\n\n# no cert with ssl_verify_client optional\n\n$s = Test::Nginx::IMAP->new(PeerAddr => '127.0.0.1:' . port(8994), SSL => 1);\n$s->ok('no optional cert');\n$s->send('1 AUTHENTICATE PLAIN ' . $cred->(\"s2\"));\n\n# wrong cert with ssl_verify_client optional\n\n$s = Test::Nginx::IMAP->new(\n\tPeerAddr => '127.0.0.1:' . port(8995),\n\tSSL => 1,\n\tSSL_cert_file => \"$d/1.example.com.crt\",\n\tSSL_key_file => \"$d/1.example.com.key\"\n);\n$s->check(qr/BYE SSL certificate error/, 'bad optional cert');\n\n# wrong cert with ssl_verify_client optional_no_ca\n\n$s = Test::Nginx::IMAP->new(\n\tPeerAddr => '127.0.0.1:' . port(8996),\n\tSSL => 1,\n\tSSL_cert_file => \"$d/1.example.com.crt\",\n\tSSL_key_file => \"$d/1.example.com.key\"\n);\n$s->ok('bad optional_no_ca cert');\n$s->send('1 AUTHENTICATE PLAIN ' . $cred->(\"s3\"));\n\n# matching cert with ssl_verify_client optional\n\n$s = Test::Nginx::IMAP->new(\n\tPeerAddr => '127.0.0.1:' . port(8995),\n\tSSL => 1,\n\tSSL_cert_file => \"$d/2.example.com.crt\",\n\tSSL_key_file => \"$d/2.example.com.key\"\n);\n$s->ok('good cert');\n$s->send('1 AUTHENTICATE PLAIN ' . $cred->(\"s4\"));\n\n# trusted cert with ssl_verify_client optional\n\n$s = Test::Nginx::IMAP->new(\n\tPeerAddr => '127.0.0.1:' . port(8995),\n\tSSL => 1,\n\tSSL_cert_file => \"$d/3.example.com.crt\",\n\tSSL_key_file => \"$d/3.example.com.key\"\n);\n$s->ok('trusted cert');\n$s->send('1 AUTHENTICATE PLAIN ' . $cred->(\"s5\"));\n$s->read();\n\n# Auth-SSL-Protocol and Auth-SSL-Cipher headers\n\nmy ($cipher, $sslversion);\n\n$s = Test::Nginx::IMAP->new(SSL => 1);\n$cipher = $s->socket()->get_cipher();\n$sslversion = $s->socket()->get_sslversion();\n\t$sslversion =~ s/_/./;\n\nundef $s;\n\n# test auth_http request header fields with access_log\n\n$t->stop();\n\nmy $f = $t->read_file('auth.log');\n\nlike($f, qr/^-:-:-:-:-:-:-\\x0d?\\x0a?:s1$/m, 'log - plain connection');\nlike($f, qr/^on:NONE:-:-:-:-:-\\x0d?\\x0a?:s2$/m, 'log - no cert');\nlike($f, qr!^on:FAILED(?:.*):(/?CN=1.example.com):\\1:\\w+:\\w+:[^:]+:s3$!m,\n\t'log - bad cert');\nlike($f, qr!^on:SUCCESS:(/?CN=2.example.com):\\1:\\w+:\\w+:[^:]+:s4$!m,\n\t'log - good cert');\nlike($f, qr!^on:SUCCESS:(/?CN=3.example.com):\\1:\\w+:\\w+:[^:]+:s5$!m,\n\t'log - trusted cert');\n\n\nTODO: {\nlocal $TODO = 'not yet' unless $t->has_version('1.21.2');\n\n$f = $t->read_file('auth2.log');\nlike($f, qr|^$cipher:$sslversion$|m, 'log - cipher sslversion');\n\n}\n\n\n###############################################################################\n\n\n\n\n\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/mail_max_errors.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n# (C) Nginx, Inc.\n\n# Tests for mail max_errors.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\nuse Socket qw/ CRLF /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse Test::Nginx::IMAP;\nuse Test::Nginx::POP3;\nuse Test::Nginx::SMTP;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nlocal $SIG{PIPE} = 'IGNORE';\n\nmy $t = Test::Nginx->new()->has(qw/mail imap pop3 smtp/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nmail {\n    auth_http  http://127.0.0.1:8080; # unused\n\n    max_errors 2;\n\n    server {\n        listen     127.0.0.1:8143;\n        protocol   imap;\n    }\n\n    server {\n        listen     127.0.0.1:8110;\n        protocol   pop3;\n    }\n\n    server {\n        listen     127.0.0.1:8025;\n        protocol   smtp;\n    }\n}\n\nEOF\n\n$t->try_run('no max_errors')->plan(18);\n\n###############################################################################\n\n# imap\n\nmy $s = Test::Nginx::IMAP->new();\n$s->read();\n\n$s->send('a01 FOO');\n$s->check(qr/^a01 BAD/, 'imap first error');\n$s->send('a02 BAR');\n$s->check(qr/^a02 BAD/, 'imap second error');\n$s->send('a03 BAZZ');\n$s->check(qr/^$/, 'imap max errors');\n\n$s = Test::Nginx::IMAP->new();\n$s->read();\n\n$s->send('a01 FOO' . CRLF . 'a02 BAR' . CRLF . 'a03 BAZZ');\n$s->check(qr/^a01 BAD/, 'imap pipelined first error');\n$s->check(qr/^a02 BAD/, 'imap pipelined second error');\n$s->check(qr/^$/, 'imap pipelined max errors');\n\n# pop3\n\n$s = Test::Nginx::POP3->new();\n$s->read();\n\n$s->send('FOO');\n$s->check(qr/^-ERR/, 'pop3 first error');\n$s->send('BAR');\n$s->check(qr/^-ERR/, 'pop3 second error');\n$s->send('BAZZ');\n$s->check(qr/^$/, 'pop3 max errors');\n\n$s = Test::Nginx::POP3->new();\n$s->read();\n\n$s->send('FOO' . CRLF . 'BAR' . CRLF . 'BAZZ');\n$s->check(qr/^-ERR/, 'pop3 pipelined first error');\n$s->check(qr/^-ERR/, 'pop3 pipelined second error');\n$s->check(qr/^$/, 'pop3 pipelined max errors');\n\n# smtp\n\n$s = Test::Nginx::SMTP->new();\n$s->read();\n\n$s->send('FOO');\n$s->check(qr/^5.. /, 'smtp first error');\n$s->send('BAR');\n$s->check(qr/^5.. /, 'smtp second error');\n$s->send('BAZZ');\n$s->check(qr/^$/, 'smtp max errors');\n\n$s = Test::Nginx::SMTP->new();\n$s->read();\n\n$s->send('FOO' . CRLF . 'BAR' . CRLF . 'BAZZ');\n$s->check(qr/^5.. /, 'smtp pipelined first error');\n$s->check(qr/^5.. /, 'smtp pipelined second error');\n$s->check(qr/^$/, 'smtp pipelined max errors');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/mail_pop3.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for nginx mail pop3 module.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse MIME::Base64;\nuse Socket qw/ CRLF /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse Test::Nginx::POP3;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nlocal $SIG{PIPE} = 'IGNORE';\n\nmy $t = Test::Nginx->new()->has(qw/mail pop3 http rewrite/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nmail {\n    proxy_pass_error_message  on;\n    proxy_timeout  15s;\n    auth_http  http://127.0.0.1:8080/mail/auth;\n\n    server {\n        listen     127.0.0.1:8110;\n        protocol   pop3;\n        pop3_auth  plain apop cram-md5 external;\n    }\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location = /mail/auth {\n            set $reply ERROR;\n            set $passw \"\";\n\n            set $userpass \"$http_auth_user:$http_auth_pass\";\n            if ($userpass ~ '^test@example.com:secret$') {\n                set $reply OK;\n            }\n\n            set $userpass \"$http_auth_user:$http_auth_salt:$http_auth_pass\";\n            if ($userpass ~ '^test@example.com:<.*@.*>:0{32}$') {\n                set $reply OK;\n                set $passw secret;\n            }\n\n            set $userpass \"$http_auth_method:$http_auth_user:$http_auth_pass\";\n            if ($userpass ~ '^external:test@example.com:$') {\n                set $reply OK;\n                set $passw secret;\n            }\n\n            add_header Auth-Status $reply;\n            add_header Auth-Server 127.0.0.1;\n            add_header Auth-Port %%PORT_8111%%;\n            add_header Auth-Pass $passw;\n            add_header Auth-Wait 1;\n            return 204;\n        }\n    }\n}\n\nEOF\n\n$t->run_daemon(\\&Test::Nginx::POP3::pop3_test_daemon);\n$t->run()->plan(28);\n\n$t->waitforsocket('127.0.0.1:' . port(8111));\n\n###############################################################################\n\nmy $s = Test::Nginx::POP3->new();\n$s->ok('greeting');\n\n# user / pass\n\n$s->send('USER test@example.com');\n$s->ok('user');\n\n$s->send('PASS secret');\n$s->ok('pass');\n\n# apop\n\n$s = Test::Nginx::POP3->new();\n$s->check(qr/<.*\\@.*>/, 'apop salt');\n\n$s->send('APOP test@example.com ' . ('1' x 32));\n$s->check(qr/^-ERR/, 'apop error');\n\n$s->send('APOP test@example.com ' . ('0' x 32));\n$s->ok('apop');\n\n# auth capabilities\n\n$s = Test::Nginx::POP3->new();\n$s->read();\n\n$s->send('AUTH');\n$s->ok('auth');\n\nis(get_auth_caps($s), 'PLAIN:LOGIN:CRAM-MD5:EXTERNAL', 'auth capabilities');\n\n# auth plain\n\n$s = Test::Nginx::POP3->new();\n$s->read();\n\n$s->send('AUTH PLAIN ' . encode_base64(\"\\0test\\@example.com\\0bad\", ''));\n$s->check(qr/^-ERR/, 'auth plain with bad password');\n\n$s->send('AUTH PLAIN ' . encode_base64(\"\\0test\\@example.com\\0secret\", ''));\n$s->ok('auth plain');\n\n# auth login simple\n\n$s = Test::Nginx::POP3->new();\n$s->read();\n\n$s->send('AUTH LOGIN');\n$s->check(qr/\\+ VXNlcm5hbWU6/, 'auth login username challenge');\n\n$s->send(encode_base64('test@example.com', ''));\n$s->check(qr/\\+ UGFzc3dvcmQ6/, 'auth login password challenge');\n\n$s->send(encode_base64('secret', ''));\n$s->ok('auth login simple');\n\n# auth login with username\n\n$s = Test::Nginx::POP3->new();\n$s->read();\n\n$s->send('AUTH LOGIN ' . encode_base64('test@example.com', ''));\n$s->check(qr/\\+ UGFzc3dvcmQ6/, 'auth login with username password challenge');\n\n$s->send(encode_base64('secret', ''));\n$s->ok('auth login with username');\n\n# auth cram-md5\n\n$s = Test::Nginx::POP3->new();\n$s->read();\n\n$s->send('AUTH CRAM-MD5');\n$s->check(qr/\\+ /, 'auth cram-md5 challenge');\n\n$s->send(encode_base64('test@example.com ' . ('0' x 32), ''));\n$s->ok('auth cram-md5');\n\n# auth external\n\n$s = Test::Nginx::POP3->new();\n$s->read();\n\n$s->send('AUTH EXTERNAL');\n$s->check(qr/\\+ VXNlcm5hbWU6/, 'auth external challenge');\n\n$s->send(encode_base64('test@example.com', ''));\n$s->ok('auth external');\n\n# auth external with username\n\n$s = Test::Nginx::POP3->new();\n$s->read();\n\n$s->send('AUTH EXTERNAL ' . encode_base64('test@example.com', ''));\n$s->ok('auth external with username');\n\n# pipelining\n\n$s = Test::Nginx::POP3->new();\n$s->read();\n\n$s->send('INVALID COMMAND WITH ARGUMENTS' . CRLF\n\t. 'NOOP');\n$s->check(qr/^-ERR/, 'pipelined invalid command');\n\nTODO: {\nlocal $TODO = 'not yet' unless $t->has_version('1.21.0');\n\n$s->ok('pipelined noop after invalid command');\n\n}\n\n$s->send('USER test@example.com' . CRLF\n\t. 'PASS secret' . CRLF\n\t. 'QUIT');\n$s->ok('pipelined user');\n\nTODO: {\nlocal $TODO = 'not yet' unless $t->has_version('1.21.0');\n\n$s->ok('pipelined pass');\n$s->ok('pipelined quit');\n\n}\n\n$s = Test::Nginx::POP3->new();\n$s->read();\n\n$s->send('AUTH LOGIN' . CRLF\n\t. encode_base64('test@example.com', '') . CRLF\n\t. encode_base64('secret', ''));\n$s->check(qr/\\+ VXNlcm5hbWU6/, 'pipelined auth username challenge');\n\nTODO: {\nlocal $TODO = 'not yet' unless $t->has_version('1.21.0');\n\n$s->check(qr/\\+ UGFzc3dvcmQ6/, 'pipelined auth password challenge');\n$s->ok('pipelined auth');\n\n}\n\n###############################################################################\n\nsub get_auth_caps {\n\tmy ($s) = @_;\n\tmy @meth;\n\n\twhile ($s->read()) {\n\t\tlast if /^\\./;\n\t\tpush @meth, $1 if /(.*?)\\x0d\\x0a?/ms;\n\t}\n\tjoin ':', @meth;\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/mail_proxy_protocol.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for mail proxy module, PROXY protocol with realip.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse MIME::Base64;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse Test::Nginx::SMTP;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nlocal $SIG{PIPE} = 'IGNORE';\n\nmy $t = Test::Nginx->new()->has(qw/mail smtp http rewrite/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nmail {\n    proxy_pass_error_message  on;\n    proxy_timeout             15s;\n    proxy_smtp_auth           on;\n    proxy_protocol            on;\n    auth_http  http://127.0.0.1:8080/mail/auth;\n    smtp_auth  login plain;\n\n    server {\n        listen    127.0.0.1:8025 proxy_protocol;\n        protocol  smtp;\n\n        auth_http_header  X-Type proxy;\n    }\n\n    server {\n        listen    127.0.0.1:8027 proxy_protocol;\n        protocol  smtp;\n\n        set_real_ip_from  127.0.0.1/32;\n        auth_http_header  X-Type realip;\n    }\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location = /mail/auth {\n            set $reply ERROR;\n            set $test $http_x_type:$http_client_ip:$http_proxy_protocol_addr;\n\n            if ($test = proxy:127.0.0.1:192.0.2.1) {\n                set $reply OK;\n            }\n\n            if ($test = realip:192.0.2.1:192.0.2.1) {\n                set $reply OK;\n            }\n\n            add_header Auth-Status $reply;\n            add_header Auth-Server 127.0.0.1;\n            add_header Auth-Port %%PORT_8026%%;\n            add_header Auth-Wait 1;\n            return 204;\n        }\n    }\n}\n\nEOF\n\n$t->run_daemon(\\&Test::Nginx::SMTP::smtp_test_daemon);\n$t->run()->plan(8);\n\n$t->waitforsocket('127.0.0.1:' . port(8026));\n\n###############################################################################\n\n# connection with PROXY protocol\n\nmy $s = Test::Nginx::SMTP->new(PeerAddr => '127.0.0.1:' . port(8025));\n$s->send('PROXY TCP4 192.0.2.1 192.0.2.2 123 5678');\n$s->check(qr/^220 /, \"greeting with proxy_protocol\");\n\n$s->send('EHLO example.com');\n$s->check(qr/^250 /, \"ehlo with proxy_protocol\");\n\n$s->send('AUTH PLAIN ' . encode_base64(\"\\0test\\@example.com\\0secret\", ''));\n$s->authok('auth with proxy_protocol');\n\n$s->send('XPROXY');\n$s->check(qr/^211 PROXY TCP4 127.0.0.1 127.0.0.1 \\d+ \\d+/,\n\t'proxy protocol to backend');\n\n# connection with PROXY protocol and set_realip_from\n\n$s = Test::Nginx::SMTP->new(PeerAddr => '127.0.0.1:' . port(8027));\n\n$s->send('PROXY TCP4 192.0.2.1 192.0.2.2 123 5678');\n$s->check(qr/^220 /, \"greeting with proxy_protocol and realip\");\n\n$s->send('EHLO example.com');\n$s->check(qr/^250 /, \"ehlo with proxy_protocol and realip\");\n\n$s->send('AUTH PLAIN ' . encode_base64(\"\\0test\\@example.com\\0secret\", ''));\n$s->authok('auth with proxy_protocol and realip');\n\n$s->send('XPROXY');\n$s->check(qr/^211 PROXY TCP4 192.0.2.1 127.0.0.1 \\d+ \\d+/,\n\t'proxy_protocol to backend and realip');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/mail_proxy_smtp_auth.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for nginx mail proxy module, the proxy_smtp_auth directive.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse MIME::Base64;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse Test::Nginx::SMTP;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nlocal $SIG{PIPE} = 'IGNORE';\n\nmy $t = Test::Nginx->new()->has(qw/mail smtp http rewrite/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nmail {\n    proxy_pass_error_message  on;\n    proxy_timeout             15s;\n    proxy_smtp_auth           on;\n    auth_http  http://127.0.0.1:8080/mail/auth;\n    smtp_auth  login plain external;\n\n    server {\n        listen     127.0.0.1:8025;\n        protocol   smtp;\n    }\n\n    server {\n        listen     127.0.0.1:8027;\n        protocol   smtp;\n        xclient    off;\n    }\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location = /mail/auth {\n            add_header Auth-Status OK;\n            add_header Auth-Server 127.0.0.1;\n            add_header Auth-Port   %%PORT_8026%%;\n            add_header Auth-Wait   1;\n            return 204;\n        }\n    }\n}\n\nEOF\n\n$t->run_daemon(\\&Test::Nginx::SMTP::smtp_test_daemon);\n$t->run()->plan(7);\n\n$t->waitforsocket('127.0.0.1:' . port(8026));\n\n###############################################################################\n\n# The following combinations may be sent to backend with proxy_smtp_auth on:\n#\n# ehlo, xclient, auth\n# ehlo, xclient, helo, auth\n# ehlo, xclient, ehlo, auth\n# helo, auth\n# ehlo, auth\n#\n# Test them in order.\n\n# ehlo, xclient, auth\n\nmy $s = Test::Nginx::SMTP->new();\n$s->read();\n$s->send('AUTH PLAIN ' . encode_base64(\"\\0test\\@example.com\\0secret\", ''));\n$s->authok('ehlo, xclient, auth');\n\n# ehlo, xclient, helo, auth\n\n$s = Test::Nginx::SMTP->new();\n$s->read();\n$s->send('HELO example.com');\n$s->read();\n$s->send('AUTH PLAIN ' . encode_base64(\"\\0test\\@example.com\\0secret\", ''));\n$s->authok('ehlo, xclient, helo, auth');\n\n# ehlo, xclient, ehlo, auth\n\n$s = Test::Nginx::SMTP->new();\n$s->read();\n$s->send('EHLO example.com');\n$s->read();\n$s->send('AUTH PLAIN ' . encode_base64(\"\\0test\\@example.com\\0secret\", ''));\n$s->authok('ehlo, xclient, ehlo, auth');\n\n# helo, auth\n\n$s = Test::Nginx::SMTP->new(PeerAddr => '127.0.0.1:' . port(8027));\n$s->read();\n$s->send('AUTH PLAIN ' . encode_base64(\"\\0test\\@example.com\\0secret\", ''));\n$s->authok('helo, auth');\n\n# ehlo, auth\n\n$s = Test::Nginx::SMTP->new(PeerAddr => '127.0.0.1:' . port(8027));\n$s->read();\n$s->send('EHLO example.com');\n$s->read();\n$s->send('AUTH PLAIN ' . encode_base64(\"\\0test\\@example.com\\0secret\", ''));\n$s->authok('ehlo, auth');\n\n# Try auth external\n\n$s = Test::Nginx::SMTP->new();\n$s->read();\n$s->send('EHLO example.com');\n$s->read();\n\n$s->send('AUTH EXTERNAL');\n$s->check(qr/^334 VXNlcm5hbWU6/, 'auth external challenge');\n$s->send(encode_base64('test@example.com', ''));\n$s->check(qr/^4.. /, 'auth external no password');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/mail_resolver.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for mail resolver.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse Test::Nginx::SMTP;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\n\nlocal $SIG{PIPE} = 'IGNORE';\n\nmy $t = Test::Nginx->new()->has(qw/mail mail_ssl smtp http rewrite socket_ssl/)\n\t->has_daemon('openssl')->plan(11)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nmail {\n    auth_http    http://127.0.0.1:8080/mail/auth;\n    smtp_auth    none;\n    server_name  locahost;\n\n    proxy_timeout 15s;\n\n    # prevent useless resend\n    resolver_timeout 2s;\n\n    server {\n        listen    127.0.0.1:8025;\n        protocol  smtp;\n        resolver  127.0.0.1:%%PORT_8981_UDP%%\n                  127.0.0.1:%%PORT_8982_UDP%%\n                  127.0.0.1:%%PORT_8983_UDP%%;\n    }\n\n    server {\n        listen    127.0.0.1:8027;\n        protocol  smtp;\n        resolver  127.0.0.1:%%PORT_8982_UDP%%;\n    }\n\n    server {\n        listen    127.0.0.1:8028;\n        protocol  smtp;\n        resolver  127.0.0.1:%%PORT_8983_UDP%%;\n        resolver_timeout 1s;\n    }\n\n    server {\n        listen    127.0.0.1:8029;\n        protocol  smtp;\n        resolver  127.0.0.1:%%PORT_8984_UDP%%;\n    }\n\n    server {\n        listen    127.0.0.1:8030;\n        protocol  smtp;\n        resolver  127.0.0.1:%%PORT_8985_UDP%%;\n    }\n\n    server {\n        listen    127.0.0.1:8031;\n        protocol  smtp;\n        resolver  127.0.0.1:%%PORT_8986_UDP%%;\n        resolver_timeout 1s;\n    }\n\n    server {\n        listen    127.0.0.1:8032;\n        protocol  smtp;\n        resolver  127.0.0.1:%%PORT_8987_UDP%%;\n    }\n\n    server {\n        ssl_certificate_key localhost.key;\n        ssl_certificate localhost.crt;\n\n        listen    127.0.0.1:8033 ssl;\n        protocol  smtp;\n        resolver  127.0.0.1:%%PORT_8983_UDP%%;\n    }\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location = /mail/auth {\n            set $reply $http_client_host;\n\n            if ($http_client_host !~ UNAVAIL) {\n                set $reply OK;\n            }\n\n            add_header Auth-Status $reply;\n            add_header Auth-Server 127.0.0.1;\n            add_header Auth-Port %%PORT_8026%%;\n            return 204;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('openssl.conf', <<EOF);\n[ req ]\ndefault_bits = 2048\nencrypt_key = no\ndistinguished_name = req_distinguished_name\n[ req_distinguished_name ]\nEOF\n\nmy $d = $t->testdir();\n\nforeach my $name ('localhost') {\n\tsystem('openssl req -x509 -new '\n\t\t. \"-config $d/openssl.conf -subj /CN=$name/ \"\n\t\t. \"-out $d/$name.crt -keyout $d/$name.key \"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create certificate for $name: $!\\n\";\n}\n\n$t->run_daemon(\\&Test::Nginx::SMTP::smtp_test_daemon);\n$t->run_daemon(\\&dns_daemon, port($_), $t) foreach (8981 .. 8987);\n\n$t->run();\n\n$t->waitforsocket('127.0.0.1:' . port(8026));\n$t->waitforfile($t->testdir . '/' . port($_)) foreach (8981 .. 8987);\n\n###############################################################################\n\n# PTR\n\nmy $s = Test::Nginx::SMTP->new();\nmy $s2 = Test::Nginx::SMTP->new();\n$s->read();\n$s->send('EHLO example.com');\n$s->read();\n$s->send('MAIL FROM:<test@example.com> SIZE=100');\n$s->read();\n\n$s->send('RCPT TO:<test@example.com>');\n$s->ok('PTR');\n\n$s->send('QUIT');\n$s->read();\n\n$s2->read();\n$s2->send('EHLO example.com');\n$s2->ok('PTR waiting');\n\n# Cached PTR prevents from querying bad ns on port 8983\n\n$s = Test::Nginx::SMTP->new();\n$s->read();\n$s->send('EHLO example.com');\n$s->read();\n$s->send('MAIL FROM:<test@example.com> SIZE=100');\n$s->read();\n\n$s->send('RCPT TO:<test@example.com>');\n$s->ok('PTR cached');\n\n$s->send('QUIT');\n$s->read();\n\n# SERVFAIL\n\n$s = Test::Nginx::SMTP->new(PeerAddr => '127.0.0.1:' . port(8027));\n$s->read();\n$s->send('EHLO example.com');\n$s->read();\n$s->send('MAIL FROM:<test@example.com> SIZE=100');\n$s->read();\n\n$s->send('RCPT TO:<test@example.com>');\n$s->check(qr/TEMPUNAVAIL/, 'PTR SERVFAIL');\n\n$s->send('QUIT');\n$s->read();\n\n# PTR with zero length RDATA\n\n$s = Test::Nginx::SMTP->new(PeerAddr => '127.0.0.1:' . port(8028));\n$s2 = Test::Nginx::SMTP->new(PeerAddr => '127.0.0.1:' . port(8028));\n$s->read();\n$s->send('EHLO example.com');\n$s->read();\n$s->send('MAIL FROM:<test@example.com> SIZE=100');\n$s->read();\n\n$s->send('RCPT TO:<test@example.com>');\n$s->check(qr/TEMPUNAVAIL/, 'PTR empty');\n\n$s->send('QUIT');\n$s->read();\n\n# resolver timeout is set\n\n$s2->read();\n$s2->send('EHLO example.com');\n$s2->ok('PTR empty waiting');\n\n# CNAME\n\nTODO: {\nlocal $TODO = 'support for CNAME RR';\n\n$s = Test::Nginx::SMTP->new(PeerAddr => '127.0.0.1:' . port(8029));\n$s->read();\n$s->send('EHLO example.com');\n$s->read();\n$s->send('MAIL FROM:<test@example.com> SIZE=100');\n$s->read();\n\n$s->send('RCPT TO:<test@example.com>');\n$s->ok('CNAME');\n\n$s->send('QUIT');\n$s->read();\n\n}\n\n# uncompressed answer\n\n$s = Test::Nginx::SMTP->new(PeerAddr => '127.0.0.1:' . port(8030));\n$s->read();\n$s->send('EHLO example.com');\n$s->read();\n$s->send('MAIL FROM:<test@example.com> SIZE=100');\n$s->read();\n\n$s->send('RCPT TO:<test@example.com>');\n$s->ok('uncompressed PTR');\n\n$s->send('QUIT');\n$s->read();\n\n$s = Test::Nginx::SMTP->new(PeerAddr => '127.0.0.1:' . port(8031));\n$s->read();\n$s->send('EHLO example.com');\n$s->read();\n$s->send('MAIL FROM:<test@example.com> SIZE=100');\n$s->read();\n\n$s->send('RCPT TO:<test@example.com>');\n$s->check(qr/TEMPUNAVAIL/, 'PTR type');\n\n$s->send('QUIT');\n$s->read();\n\n# CNAME and PTR in one answer section\n\n$s = Test::Nginx::SMTP->new(PeerAddr => '127.0.0.1:' . port(8032));\n$s->read();\n$s->send('EHLO example.com');\n$s->read();\n$s->send('MAIL FROM:<test@example.com> SIZE=100');\n$s->read();\n\n$s->send('RCPT TO:<test@example.com>');\n$s->ok('CNAME with PTR');\n\n$s->send('QUIT');\n$s->read();\n\n# before 1.17.3, read event while in resolving resulted in duplicate resolving\n\nmy %ssl = (\n\tSSL => 1,\n\tSSL_verify_mode => IO::Socket::SSL::SSL_VERIFY_NONE(),\n\tSSL_error_trap => sub { die $_[1] },\n);\n\n$s = Test::Nginx::SMTP->new(PeerAddr => '127.0.0.1:' . port(8033), %ssl);\n$s->send('EHLO example.com');\n$s->read();\n$s->send('MAIL FROM:<test@example.com> SIZE=100');\n$s->read();\n$s->read();\n\n$s->send('RCPT TO:<test@example.com>');\n$s->check(qr/TEMPUNAVAIL/, 'PTR SSL empty');\n\n$s->send('QUIT');\n$s->read();\n\n###############################################################################\n\nsub reply_handler {\n\tmy ($recv_data, $port) = @_;\n\n\tmy (@name, @rdata);\n\n\tuse constant NOERROR\t=> 0;\n\tuse constant SERVFAIL\t=> 2;\n\tuse constant NXDOMAIN\t=> 3;\n\n\tuse constant A\t\t=> 1;\n\tuse constant CNAME\t=> 5;\n\tuse constant PTR\t=> 12;\n\tuse constant DNAME\t=> 39;\n\n\tuse constant IN\t\t=> 1;\n\n\t# default values\n\n\tmy ($hdr, $rcode, $ttl) = (0x8180, NOERROR, 3600);\n\n\t# decode name\n\n\tmy ($len, $offset) = (undef, 12);\n\twhile (1) {\n\t\t$len = unpack(\"\\@$offset C\", $recv_data);\n\t\tlast if $len == 0;\n\t\t$offset++;\n\t\tpush @name, unpack(\"\\@$offset A$len\", $recv_data);\n\t\t$offset += $len;\n\t}\n\n\t$offset -= 1;\n\tmy ($id, $type, $class) = unpack(\"n x$offset n2\", $recv_data);\n\n\tmy $name = join('.', @name);\n\tif ($name eq 'a.example.net' && $type == A) {\n\t\tpush @rdata, rd_addr($ttl, '127.0.0.1');\n\n\t} elsif ($name eq '1.0.0.127.in-addr.arpa' && $type == PTR) {\n\t\tif ($port == port(8981)) {\n\t\t\tpush @rdata, rd_name(PTR, $ttl, 'a.example.net');\n\n\t\t} elsif ($port == port(8982)) {\n\t\t\t$rcode = SERVFAIL;\n\n\t\t} elsif ($port == port(8983)) {\n\t\t\t# zero length RDATA\n\n\t\t\tpush @rdata, pack(\"n3N n\", 0xc00c, PTR, IN, $ttl, 0);\n\n\t\t} elsif ($port == port(8984)) {\n\t\t\t# PTR answered with CNAME\n\n\t\t\tpush @rdata, rd_name(CNAME, $ttl,\n\t\t\t\t'1.1.0.0.127.in-addr.arpa');\n\n\t\t} elsif ($port == port(8985)) {\n\t\t\t# uncompressed answer\n\n\t\t\tpush @rdata, pack(\"(C/a*)6x n2N n(C/a*)3x\",\n\t\t\t\t('1', '0', '0', '127', 'in-addr', 'arpa'),\n\t\t\t\tPTR, IN, $ttl, 15, ('a', 'example', 'net'));\n\n\t\t} elsif ($port == port(8986)) {\n\t\t\tpush @rdata, rd_name(DNAME, $ttl, 'a.example.net');\n\n\t\t} elsif ($port == port(8987)) {\n\t\t\t# PTR answered with CNAME+PTR\n\n\t\t\tpush @rdata, rd_name(CNAME, $ttl,\n\t\t\t\t'1.1.0.0.127.in-addr.arpa');\n\t\t\tpush @rdata, pack(\"n3N n(C/a*)3 x\", 0xc034,\n\t\t\t\tPTR, IN, $ttl, 15, ('a', 'example', 'net'));\n\t\t}\n\n\t} elsif ($name eq '1.1.0.0.127.in-addr.arpa' && $type == PTR) {\n\t\tpush @rdata, rd_name(PTR, $ttl, 'a.example.net');\n\t}\n\n\t$len = @name;\n\tpack(\"n6 (C/a*)$len x n2\", $id, $hdr | $rcode, 1, scalar @rdata,\n\t\t0, 0, @name, $type, $class) . join('', @rdata);\n}\n\nsub rd_name {\n\tmy ($type, $ttl, $name) = @_;\n\tmy ($rdlen, @rdname);\n\n\t@rdname = split /\\./, $name;\n\t$rdlen = length(join '', @rdname) + @rdname + 1;\n\tpack(\"n3N n(C/a*)* x\", 0xc00c, $type, IN, $ttl, $rdlen, @rdname);\n}\n\nsub rd_addr {\n\tmy ($ttl, $addr) = @_;\n\n\tmy $code = 'split(/\\./, $addr)';\n\n\t# use a special pack string to not zero pad\n\n\treturn pack 'n3N', 0xc00c, A, IN, $ttl if $addr eq '';\n\n\tpack 'n3N nC4', 0xc00c, A, IN, $ttl, eval \"scalar $code\", eval($code);\n}\n\nsub dns_daemon {\n\tmy ($port, $t) = @_;\n\n\tmy ($data, $recv_data);\n\tmy $socket = IO::Socket::INET->new(\n\t\tLocalAddr => '127.0.0.1',\n\t\tLocalPort => $port,\n\t\tProto => 'udp',\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\t# signal we are ready\n\n\topen my $fh, '>', $t->testdir() . '/' . $port;\n\tclose $fh;\n\n\twhile (1) {\n\t\t$socket->recv($recv_data, 65536);\n\t\t$data = reply_handler($recv_data, $port);\n\t\t$socket->send($data);\n\t}\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/mail_smtp.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for nginx mail smtp module.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse MIME::Base64;\nuse Socket qw/ CRLF /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse Test::Nginx::SMTP;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nlocal $SIG{PIPE} = 'IGNORE';\n\nmy $t = Test::Nginx->new()->has(qw/mail smtp http rewrite/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nmail {\n    proxy_pass_error_message  on;\n    proxy_timeout  15s;\n    auth_http  http://127.0.0.1:8080/mail/auth;\n    xclient    off;\n\n    server {\n        listen     127.0.0.1:8025;\n        protocol   smtp;\n        smtp_auth  login plain none cram-md5 external;\n    }\n\n    server {\n        listen     127.0.0.1:8027;\n        protocol   smtp;\n        smtp_auth  none;\n        smtp_client_buffer 128;\n    }\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location = /mail/auth {\n            set $reply ERROR;\n\n            if ($http_auth_smtp_to ~ example.com) {\n                set $reply OK;\n            }\n\n            set $userpass \"$http_auth_user:$http_auth_pass\";\n            if ($userpass ~ '^test@example.com:secret$') {\n                set $reply OK;\n            }\n\n            set $userpass \"$http_auth_user:$http_auth_salt:$http_auth_pass\";\n            if ($userpass ~ '^test@example.com:<.*@.*>:0{32}$') {\n                set $reply OK;\n            }\n\n            set $userpass \"$http_auth_method:$http_auth_user:$http_auth_pass\";\n            if ($userpass ~ '^external:test@example.com:$') {\n                set $reply OK;\n            }\n\n            add_header Auth-Status $reply;\n            add_header Auth-Server 127.0.0.1;\n            add_header Auth-Port %%PORT_8026%%;\n            add_header Auth-Wait 1;\n            return 204;\n        }\n    }\n}\n\nEOF\n\n$t->run_daemon(\\&Test::Nginx::SMTP::smtp_test_daemon);\n$t->run()->plan(41);\n\n$t->waitforsocket('127.0.0.1:' . port(8026));\n\n###############################################################################\n\nmy $s = Test::Nginx::SMTP->new();\n$s->check(qr/^220 /, \"greeting\");\n\n$s->send('EHLO example.com');\n$s->check(qr/^250 /, \"ehlo\");\n\n$s->send('AUTH PLAIN ' . encode_base64(\"\\0test\\@example.com\\0bad\", ''));\n$s->check(qr/^5.. /, 'auth plain with bad password');\n\n$s->send('AUTH PLAIN ' . encode_base64(\"\\0test\\@example.com\\0secret\", ''));\n$s->authok('auth plain');\n\n# We are talking to backend from this point\n\n$s->send('MAIL FROM:<test@example.com> SIZE=100');\n$s->ok('mail from after auth');\n\n$s->send('RSET');\n$s->ok('rset');\n\n$s->send('MAIL FROM:<test@xn--e1afmkfd.xn--80akhbyknj4f> SIZE=100');\n$s->ok(\"idn mail from (example.test in russian)\");\n\n$s->send('QUIT');\n$s->ok(\"quit\");\n\n# Try auth login in simple form\n\n$s = Test::Nginx::SMTP->new();\n$s->read();\n$s->send('EHLO example.com');\n$s->read();\n\n$s->send('AUTH LOGIN');\n$s->check(qr/^334 VXNlcm5hbWU6/, 'auth login simple username challenge');\n$s->send(encode_base64('test@example.com', ''));\n$s->check(qr/^334 UGFzc3dvcmQ6/, 'auth login simple password challenge');\n$s->send(encode_base64('secret', ''));\n$s->authok('auth login simple');\n\n# Try auth plain with username.  Details:\n#\n# [MS-XLOGIN]: SMTP Protocol AUTH LOGIN Extension Specification\n# http://download.microsoft.com/download/5/D/D/5DD33FDF-91F5-496D-9884-0A0B0EE698BB/%5BMS-XLOGIN%5D.pdf\n\n$s = Test::Nginx::SMTP->new();\n$s->read();\n$s->send('EHLO example.com');\n$s->read();\n\n$s->send('AUTH LOGIN ' . encode_base64('test@example.com', ''));\n$s->check(qr/^334 UGFzc3dvcmQ6/, 'auth login with username password challenge');\n$s->send(encode_base64('secret', ''));\n$s->authok('auth login with username');\n\n# Try auth cram-md5\n\n$s = Test::Nginx::SMTP->new();\n$s->read();\n$s->send('EHLO example.com');\n$s->read();\n\n$s->send('AUTH CRAM-MD5');\n$s->check(qr/^334 /, 'auth cram-md5 challenge');\n$s->send(encode_base64('test@example.com ' . ('0' x 32), ''));\n$s->authok('auth cram-md5');\n\n# Try auth external\n\n$s = Test::Nginx::SMTP->new();\n$s->read();\n$s->send('EHLO example.com');\n$s->read();\n\n$s->send('AUTH EXTERNAL');\n$s->check(qr/^334 VXNlcm5hbWU6/, 'auth external challenge');\n$s->send(encode_base64('test@example.com', ''));\n$s->ok('auth external');\n\n# Try auth external with username\n\n$s = Test::Nginx::SMTP->new();\n$s->read();\n$s->send('EHLO example.com');\n$s->read();\n\n$s->send('AUTH EXTERNAL ' . encode_base64('test@example.com', ''));\n$s->ok('auth external with username');\n\n# Try auth plain with pipelining\n\n$s = Test::Nginx::SMTP->new();\n$s->read();\n$s->send('EHLO example.com');\n$s->read();\n\n$s->send('INVALID COMMAND WITH ARGUMENTS' . CRLF\n\t. 'RSET');\n$s->read();\n$s->ok('pipelined rset after invalid command');\n\n$s->send('AUTH PLAIN '\n\t. encode_base64(\"\\0test\\@example.com\\0bad\", '') . CRLF\n\t. 'MAIL FROM:<test@example.com> SIZE=100');\n$s->read();\n$s->ok('mail from after failed pipelined auth');\n\n$s->send('AUTH PLAIN '\n\t. encode_base64(\"\\0test\\@example.com\\0secret\", '') . CRLF\n\t. 'MAIL FROM:<test@example.com> SIZE=100');\n$s->read();\n$s->ok('mail from after pipelined auth');\n\n# Try auth none\n\n$s = Test::Nginx::SMTP->new();\n$s->read();\n$s->send('EHLO example.com');\n$s->read();\n\n$s->send('MAIL FROM:<test@example.com> SIZE=100');\n$s->ok('auth none - mail from');\n\n$s->send('RCPT TO:<test@example.com>');\n$s->ok('auth none - rcpt to');\n\n$s->send('RSET');\n$s->ok('auth none - rset, should go to backend');\n\n# Auth none with pipelining\n\n$s = Test::Nginx::SMTP->new();\n$s->read();\n$s->send('EHLO example.com');\n$s->read();\n\n$s->send('MAIL FROM:<test@example.com> SIZE=100' . CRLF\n\t. 'RCPT TO:<test@example.com>' . CRLF\n\t. 'RSET');\n\n$s->ok('pipelined mail from');\n$s->ok('pipelined rcpt to');\n$s->ok('pipelined rset');\n\n# Pipelining with split command\n\n$s = Test::Nginx::SMTP->new();\n$s->read();\n$s->send('EHLO example.com');\n$s->read();\n\n$s->print('MAIL FROM:<test@example.com> SIZE=100' . CRLF\n\t. 'RCPT TO:<test@example.com>' . CRLF\n\t. 'RS');\n\n$s->ok('split pipelined mail from');\n\nTODO: {\nlocal $TODO = 'not yet' unless $t->has_version('1.21.0');\n\n$s->ok('split pipelined rcpt to');\n\n}\n\n$s->send('ET');\n$s->ok('split pipelined rset');\n\n# Pipelining longer than smtp_client_buffer\n\n$s = Test::Nginx::SMTP->new(PeerAddr => '127.0.0.1:' . port(8027));\n$s->read();\n$s->send('EHLO example.com');\n$s->read();\n\n$s->send('MAIL FROM:<test@example.com> SIZE=100' . CRLF\n\t. 'RCPT TO:<foo@example.com>' . CRLF\n\t. 'RCPT TO:<bar@example.com>' . CRLF\n\t. 'RCPT TO:<baz@example.com>' . CRLF\n\t. 'RCPT TO:<foobar@example.com>' . CRLF\n\t. 'RSET');\n\nTODO: {\ntodo_skip 'long pipelined - not yet', 6 unless $t->has_version('1.21.0');\n\n$s->ok('long pipelined mail from');\n$s->ok('long pipelined rcpt to');\n$s->ok('long pipelined rcpt to 2');\n$s->ok('long pipelined rcpt to 3');\n$s->ok('long pipelined rcpt to 4');\n$s->ok('long pipelined rset');\n\n}\n\n# Connection must stay even if error returned to rcpt to command\n\n$s = Test::Nginx::SMTP->new();\n$s->read();\n$s->send('EHLO example.com');\n$s->read();\n\n$s->send('MAIL FROM:<test@example.com> SIZE=100');\n$s->read(); # skip mail from reply\n\n$s->send('RCPT TO:<example.com>');\n$s->check(qr/^5.. /, \"bad rcpt to\");\n\n$s->send('RCPT TO:<test@example.com>');\n$s->ok('good rcpt to');\n\n# Make sure command split into many packets processed correctly\n\n$s = Test::Nginx::SMTP->new();\n$s->read();\n\n$s->print('HEL');\nselect undef, undef, undef, 0.1;\n$s->send('O example.com');\n$s->ok('split command');\n\n# Invalid command split into many packets\n\n$s = Test::Nginx::SMTP->new();\n$s->read();\n\n$s->print('FOO B');\nselect undef, undef, undef, 0.1;\n$s->send('AR');\n$s->check(qr/^5.. /, 'invalid split command');\n\nTODO: {\nlocal $TODO = 'not yet' unless $t->has_version('1.21.0');\n\n$s->send('HELO example.com');\n$s->ok('good after invalid split command');\n\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/mail_smtp_greeting_delay.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse Test::Nginx::SMTP;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nlocal $SIG{PIPE} = 'IGNORE';\n\nmy $t = Test::Nginx->new()->has(qw/mail smtp/)->plan(2)\n\t->write_file_expand('nginx.conf', <<'EOF')->run();\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nmail {\n    proxy_pass_error_message  on;\n    proxy_timeout  15s;\n    auth_http  http://127.0.0.1:8080/mail/auth;\n    xclient    off;\n\n    server {\n        listen     127.0.0.1:8025;\n        protocol   smtp;\n        smtp_greeting_delay  1s;\n    }\n}\n\nEOF\n\n###############################################################################\n\n# With smtp_greeting_delay session expected to be closed after first error\n# message if client sent something before greeting.\n\nmy $s = Test::Nginx::SMTP->new();\n$s->send('HELO example.com');\n$s->check(qr/^5.. /, \"command before greeting - session must be rejected\");\nok($s->eof(), \"session have to be closed\");\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/mail_smtp_xclient.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse MIME::Base64;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse Test::Nginx::SMTP;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nlocal $SIG{PIPE} = 'IGNORE';\n\nmy $t = Test::Nginx->new()->has(qw/mail smtp http rewrite/)->plan(6)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nmail {\n    proxy_pass_error_message  on;\n    proxy_timeout  15s;\n    auth_http  http://127.0.0.1:8080/mail/auth;\n    xclient    on;\n\n    server {\n        listen     127.0.0.1:8025;\n        protocol   smtp;\n        smtp_auth  login plain none;\n    }\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location = /mail/auth {\n            add_header Auth-Status OK;\n            add_header Auth-Server 127.0.0.1;\n            add_header Auth-Port   %%PORT_8026%%;\n            add_header Auth-Wait   1;\n            return 204;\n        }\n    }\n}\n\nEOF\n\n$t->run_daemon(\\&Test::Nginx::SMTP::smtp_test_daemon);\n$t->run()->waitforsocket('127.0.0.1:' . port(8026));\n\n###############################################################################\n\n# When XCLIENT's HELO= argument isn't used, the  following combinations may be\n# send to backend with xclient on:\n#\n# xclient\n# xclient, helo\n# xclient, ehlo\n# xclient, from, rcpt\n# xclient, helo, from, rcpt\n# xclient, ehlo, from, rcpt\n#\n# Test them in order.\n\n# xclient\n\nmy $s = Test::Nginx::SMTP->new();\n$s->read();\n$s->send('AUTH PLAIN ' . encode_base64(\"\\0test\\@example.com\\0secret\", ''));\n$s->authok('xclient');\n\n# xclient, helo\n\n$s = Test::Nginx::SMTP->new();\n$s->read();\n$s->send('HELO example.com');\n$s->read();\n$s->send('AUTH PLAIN ' . encode_base64(\"\\0test\\@example.com\\0secret\", ''));\n$s->authok('xclient, helo');\n\n# xclient, ehlo\n\n$s = Test::Nginx::SMTP->new();\n$s->read();\n$s->send('EHLO example.com');\n$s->read();\n$s->send('AUTH PLAIN ' . encode_base64(\"\\0test\\@example.com\\0secret\", ''));\n$s->authok('xclient, ehlo');\n\n# xclient, from, rcpt\n\n$s = Test::Nginx::SMTP->new();\n$s->read();\n$s->send('MAIL FROM:<test@example.com>');\n$s->read();\n$s->send('RCPT TO:<test@example.com>');\n$s->ok('xclient, from');\n\n# xclient, helo, from, rcpt\n\n$s = Test::Nginx::SMTP->new();\n$s->read();\n$s->send('HELO example.com');\n$s->read();\n$s->send('MAIL FROM:<test@example.com>');\n$s->read();\n$s->send('RCPT TO:<test@example.com>');\n$s->ok('xclient, helo, from');\n\n# xclient, ehlo, from, rcpt\n\n$s = Test::Nginx::SMTP->new();\n$s->read();\n$s->send('EHLO example.com');\n$s->read();\n$s->send('MAIL FROM:<test@example.com>');\n$s->read();\n$s->send('RCPT TO:<test@example.com>');\n$s->ok('xclient, ehlo, from');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/mail_ssl.t",
    "content": "#!/usr/bin/perl\n\n# (C) Andrey Zelenkov\n# (C) Nginx, Inc.\n\n# Tests for mail ssl module.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse Test::Nginx::IMAP;\nuse Test::Nginx::POP3;\nuse Test::Nginx::SMTP;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nlocal $SIG{PIPE} = 'IGNORE';\n\n\nmy $t = Test::Nginx->new()->has(qw/mail mail_ssl imap pop3 smtp socket_ssl/)\n\t->has_daemon('openssl')->plan(19);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nmail {\n    ssl_certificate_key localhost.key;\n    ssl_certificate localhost.crt;\n\n    ssl_password_file password;\n\n    auth_http  http://127.0.0.1:8080;\t# unused\n\n\n    server {\n        listen             127.0.0.1:8143;\n        listen             127.0.0.1:8145 ssl;\n        protocol           imap;\n\n    }\n\n    server {\n\n\n\n\n\n        listen             127.0.0.1:8148 ssl;\n        protocol           imap;\n\n        ssl_certificate_key inherits.key;\n        ssl_certificate inherits.crt;\n    }\n\n    server {\n        listen             127.0.0.1:8149;\n        protocol           imap;\n\n        starttls           on;\n    }\n\n    server {\n        listen             127.0.0.1:8150;\n        protocol           imap;\n\n        starttls           only;\n    }\n\n    server {\n        listen             127.0.0.1:8151;\n        protocol           pop3;\n\n        starttls           on;\n    }\n\n    server {\n        listen             127.0.0.1:8152;\n        protocol           pop3;\n\n        starttls           only;\n    }\n\n    server {\n        listen             127.0.0.1:8153;\n        protocol           smtp;\n\n        starttls           on;\n    }\n\n    server {\n        listen             127.0.0.1:8154;\n        protocol           smtp;\n\n        starttls           only;\n    }\n}\n\nEOF\n\n$t->write_file('openssl.conf', <<EOF);\n[ req ]\ndefault_bits = 2048\nencrypt_key = no\ndistinguished_name = req_distinguished_name\n[ req_distinguished_name ]\nEOF\n\nmy $d = $t->testdir();\n\nforeach my $name ('localhost', 'inherits') {\n\tsystem(\"openssl genrsa -out $d/$name.key -passout pass:localhost \"\n\t\t. \"-aes128 2048 >>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create private key: $!\\n\";\n\tsystem('openssl req -x509 -new '\n\t\t. \"-config $d/openssl.conf -subj /CN=$name/ \"\n\t\t. \"-out $d/$name.crt \"\n\t\t. \"-key $d/$name.key -passin pass:localhost\"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create certificate for $name: $!\\n\";\n}\n\n$t->write_file('password', 'localhost');\n\n$t->run();\n\n###############################################################################\n\nmy ($s, $ssl);\n# simple tests to ensure that nothing broke with ssl_password_file directive\n\n$s = Test::Nginx::IMAP->new();\n$s->ok('greeting');\n\n$s->send('1 AUTHENTICATE LOGIN');\n$s->check(qr/\\+ VXNlcm5hbWU6/, 'login');\n\n\n\n\n\n\n\n\n\n\n\n# ssl_certificate inheritance\n\n$s = Test::Nginx::IMAP->new(PeerAddr => '127.0.0.1:' . port(8145), SSL => 1);\n$s->ok('greeting ssl');\nlike($s->socket()->dump_peer_certificate(), qr/CN=localhost/, 'CN');\n\n$s = Test::Nginx::IMAP->new(PeerAddr => '127.0.0.1:' . port(8148), SSL => 1);\n$s->read();\nlike($s->socket()->dump_peer_certificate(), qr/CN=inherits/, 'CN inner');\n\n# alpn\n\n\nSKIP: {\nskip 'LibreSSL too old', 2\n\tif $t->has_module('LibreSSL')\n\tand not $t->has_feature('libressl:3.4.0');\nskip 'OpenSSL too old', 2\n\tif $t->has_module('OpenSSL')\n\tand not $t->has_feature('openssl:1.1.0');\nskip 'no ALPN support in IO::Socket::SSL', 2\n\tunless $t->has_feature('socket_ssl_alpn');\n\n$s = Test::Nginx::IMAP->new(\n\tPeerAddr => '127.0.0.1:' . port(8148),\n\tSSL => 1,\n\tSSL_alpn_protocols => [ 'imap' ]\n);\n$s->ok('alpn');\nTODO: {\nlocal $TODO = 'not yet' unless $t->has_version('1.21.4');\n\n$s = Test::Nginx::IMAP->new(\n\tPeerAddr => '127.0.0.1:' . port(8148),\n\tSSL => 1,\n\tSSL_alpn_protocols => [ 'unknown' ]\n);\nok(!$s->read(), 'alpn rejected');\n\n}\n\n}\n\n# starttls imap\n\n$s = Test::Nginx::IMAP->new(PeerAddr => '127.0.0.1:' . port(8149));\n$s->read();\n\n$s->send('1 AUTHENTICATE LOGIN');\n$s->check(qr/\\+ VXNlcm5hbWU6/, 'imap auth before startls on');\n\n$s = Test::Nginx::IMAP->new(PeerAddr => '127.0.0.1:' . port(8149));\n$s->read();\n\n$s->send('1 STARTTLS');\n$s->ok('imap starttls on');\n\n$s = Test::Nginx::IMAP->new(PeerAddr => '127.0.0.1:' . port(8150));\n$s->read();\n\n$s->send('1 AUTHENTICATE LOGIN');\n$s->check(qr/^\\S+ BAD/, 'imap auth before startls only');\n\n$s = Test::Nginx::IMAP->new(PeerAddr => '127.0.0.1:' . port(8150));\n$s->read();\n\n$s->send('1 STARTTLS');\n$s->ok('imap starttls only');\n\n# starttls pop3\n\n$s = Test::Nginx::POP3->new(PeerAddr => '127.0.0.1:' . port(8151));\n$s->read();\n\n$s->send('AUTH LOGIN');\n$s->check(qr/\\+ VXNlcm5hbWU6/, 'pop3 auth before startls on');\n\n$s = Test::Nginx::POP3->new(PeerAddr => '127.0.0.1:' . port(8151));\n$s->read();\n\n$s->send('STLS');\n$s->ok('pop3 starttls on');\n\n$s = Test::Nginx::POP3->new(PeerAddr => '127.0.0.1:' . port(8152));\n$s->read();\n\n$s->send('AUTH LOGIN');\n$s->check(qr/^-ERR/, 'pop3 auth before startls only');\n\n$s = Test::Nginx::POP3->new(PeerAddr => '127.0.0.1:' . port(8152));\n$s->read();\n\n$s->send('STLS');\n$s->ok('pop3 starttls only');\n\n# starttls smtp\n\n$s = Test::Nginx::SMTP->new(PeerAddr => '127.0.0.1:' . port(8153));\n$s->read();\n\n$s->send('AUTH LOGIN');\n$s->check(qr/^334 VXNlcm5hbWU6/, 'smtp auth before startls on');\n\n$s = Test::Nginx::SMTP->new(PeerAddr => '127.0.0.1:' . port(8153));\n$s->read();\n\n$s->send('STARTTLS');\n$s->ok('smtp starttls on');\n\n$s = Test::Nginx::SMTP->new(PeerAddr => '127.0.0.1:' . port(8154));\n$s->read();\n\n$s->send('AUTH LOGIN');\n$s->check(qr/^5.. /, 'smtp auth before startls only');\n\n$s = Test::Nginx::SMTP->new(PeerAddr => '127.0.0.1:' . port(8154));\n$s->read();\n\n$s->send('STARTTLS');\n$s->ok('smtp starttls only');\n\n###############################################################################\n\n\n\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/mail_ssl_conf_command.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for mail ssl module, ssl_conf_command.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse Test::Nginx::IMAP;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nlocal $SIG{PIPE} = 'IGNORE';\n\nmy $t = Test::Nginx->new()\n\t->has(qw/mail mail_ssl imap openssl:1.0.2 socket_ssl_reused/)\n\t->has_daemon('openssl');\n\nplan(skip_all => 'no ssl_conf_command') if $t->has_module('BoringSSL');\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nmail {\n    auth_http  http://127.0.0.1:8080;   # unused\n\n    server {\n        listen       127.0.0.1:8993 ssl;\n        protocol     imap;\n\n        ssl_protocols TLSv1.2;\n\n        ssl_session_tickets off;\n        ssl_conf_command Options SessionTicket;\n\n        ssl_prefer_server_ciphers on;\n        ssl_conf_command Options -ServerPreference;\n        ssl_ciphers ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256;\n\n        ssl_certificate localhost.crt;\n        ssl_certificate_key localhost.key;\n        ssl_conf_command Certificate override.crt;\n        ssl_conf_command PrivateKey override.key;\n    }\n}\n\nEOF\n\n$t->write_file('openssl.conf', <<EOF);\n[ req ]\ndefault_bits = 2048\nencrypt_key = no\ndistinguished_name = req_distinguished_name\n[ req_distinguished_name ]\nEOF\n\nmy $d = $t->testdir();\n\nforeach my $name ('localhost', 'override') {\n\tsystem('openssl req -x509 -new '\n\t\t. \"-config $d/openssl.conf -subj /CN=$name/ \"\n\t\t. \"-out $d/$name.crt -keyout $d/$name.key \"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create certificate for $name: $!\\n\";\n}\n\n$t->run()->plan(3);\n\n###############################################################################\n\nmy $s;\n\n$s = Test::Nginx::IMAP->new(\n\tSSL => 1,\n\tSSL_session_cache_size => 100\n);\n$s->read();\nlike($s->socket()->dump_peer_certificate(), qr/CN=override/, 'Certificate');\n\n$s = Test::Nginx::IMAP->new(\n\tSSL => 1,\n\tSSL_reuse_ctx => $s->socket()\n);\nok($s->socket()->get_session_reused(), 'SessionTicket');\n\n$s = Test::Nginx::IMAP->new(\n\tSSL => 1,\n\tSSL_cipher_list =>\n\t\t'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384'\n);\nis($s->socket()->get_cipher(),\n\t'ECDHE-RSA-AES128-GCM-SHA256', 'ServerPreference');\n\n###############################################################################\n\n\n\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/mail_ssl_session_reuse.t",
    "content": "#!/usr/bin/perl\n\n# (C) Andrey Zelenkov\n# (C) Maxim Dounin\n# (C) Nginx, Inc.\n\n# Tests for mail ssl module, session reuse.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse Test::Nginx::IMAP;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nlocal $SIG{PIPE} = 'IGNORE';\n\nmy $t = Test::Nginx->new()\n\t->has(qw/mail mail_ssl imap socket_ssl_sslversion socket_ssl_reused/)\n\t->has_daemon('openssl')->plan(7);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nmail {\n    auth_http  http://127.0.0.1:8080;\n\n    ssl_certificate localhost.crt;\n    ssl_certificate_key localhost.key;\n\n    server {\n        listen    127.0.0.1:8993 ssl;\n        protocol  imap;\n    }\n\n    server {\n        listen    127.0.0.1:8994 ssl;\n        protocol  imap;\n\n        ssl_session_cache shared:SSL:1m;\n        ssl_session_tickets on;\n    }\n\n    server {\n        listen    127.0.0.1:8995 ssl;\n        protocol  imap;\n\n        ssl_session_cache shared:SSL:1m;\n        ssl_session_tickets off;\n    }\n\n    server {\n        listen    127.0.0.1:8996 ssl;\n        protocol  imap;\n\n        ssl_session_cache builtin;\n        ssl_session_tickets off;\n    }\n\n    server {\n        listen    127.0.0.1:8997 ssl;\n        protocol  imap;\n\n        ssl_session_cache builtin:1000;\n        ssl_session_tickets off;\n    }\n\n    server {\n        listen    127.0.0.1:8998 ssl;\n        protocol  imap;\n\n        ssl_session_cache none;\n        ssl_session_tickets off;\n    }\n\n    server {\n        listen    127.0.0.1:8999 ssl;\n        protocol  imap;\n\n        ssl_session_cache off;\n        ssl_session_tickets off;\n    }\n}\n\nEOF\n\n$t->write_file('openssl.conf', <<EOF);\n[ req ]\ndefault_bits = 2048\nencrypt_key = no\ndistinguished_name = req_distinguished_name\n[ req_distinguished_name ]\nEOF\n\nmy $d = $t->testdir();\n\nforeach my $name ('localhost') {\n\tsystem('openssl req -x509 -new '\n\t\t. \"-config $d/openssl.conf -subj /CN=$name/ \"\n\t\t. \"-out $d/$name.crt -keyout $d/$name.key \"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create certificate for $name: $!\\n\";\n}\n\n$t->run();\n\n###############################################################################\n\n# session reuse:\n#\n# - only tickets, the default\n# - tickets and shared cache, should work always\n# - only shared cache\n# - only builtin cache\n# - only builtin cache with explicitly configured size\n# - only cache none\n# - only cache off\n\nTODO: {\nlocal $TODO = 'no TLSv1.3 sessions, old Net::SSLeay'\n\tif $Net::SSLeay::VERSION < 1.88 && test_tls13();\nlocal $TODO = 'no TLSv1.3 sessions, old IO::Socket::SSL'\n\tif $IO::Socket::SSL::VERSION < 2.061 && test_tls13();\nlocal $TODO = 'no TLSv1.3 sessions in LibreSSL'\n\tif $t->has_module('LibreSSL') && test_tls13();\n\nis(test_reuse(8993), 1, 'tickets reused');\nis(test_reuse(8994), 1, 'tickets and cache reused');\n\nTODO: {\nlocal $TODO = 'no TLSv1.3 session cache in BoringSSL'\n\tif $t->has_module('BoringSSL') && test_tls13();\n\nis(test_reuse(8995), 1, 'cache shared reused');\nis(test_reuse(8996), 1, 'cache builtin reused');\nis(test_reuse(8997), 1, 'cache builtin size reused');\n\n}\n}\n\nis(test_reuse(8998), 0, 'cache none not reused');\nis(test_reuse(8999), 0, 'cache off not reused');\n\n###############################################################################\n\nsub test_tls13 {\n\tmy $s = Test::Nginx::IMAP->new(SSL => 1);\n\treturn ($s->socket()->get_sslversion_int() > 0x303);\n}\n\nsub test_reuse {\n\tmy ($port) = @_;\n\n\tmy $s = Test::Nginx::IMAP->new(\n\t\tPeerAddr => '127.0.0.1:' . port($port),\n\t\tSSL => 1,\n\t\tSSL_session_cache_size => 100\n\t);\n\t$s->read();\n\n\t$s = Test::Nginx::IMAP->new(\n\t\tPeerAddr => '127.0.0.1:' . port($port),\n\t\tSSL => 1,\n\t\tSSL_reuse_ctx => $s->socket()\n\t);\n\n\treturn $s->socket()->get_session_reused();\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/map.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n# (C) Andrey Zelenkov\n# (C) Nginx, Inc.\n\n# Tests for map module.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http map rewrite/)->plan(19);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    map $args $x {\n        default                     0;\n        foo                         bar;\n        foo2                        bar;\n    }\n\n    map $args $y {\n        hostnames;\n        default                     0;\n        example.com                 foo;\n        example.*                   right-wildcard;\n        *.example.com               left-wildcard;\n        .dot.example.com            special-wildcard;\n        ~^REGEX.EXAMPLE\\.ORG$       regex-sensitive;\n        ~*^www.regex.example\\.org$  regex-insensitive;\n        \\include                    include;\n        server                      $server_name;\n        var                         $z;\n    }\n\n    map $args $z {\n        default                     0;\n        var                         baz;\n        include                     map.conf;\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            add_header X-Foo \"x:$x y:$y\\n\";\n            return 204;\n        }\n        location /z {\n            add_header X-Foo \"z:$z\\n\";\n            return 204;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('map.conf', \"foo bar;\");\n$t->run();\n\n###############################################################################\n\nlike(http_get('/?1'), qr/x:0 y:0/, 'map default');\nlike(http_get('/?foo'), qr/x:bar y:0/, 'map foo bar');\nlike(http_get('/?foo2'), qr/x:bar y:0/, 'map foo bar key');\nlike(http_get('/?example.com'), qr/x:0 y:foo/, 'map example.com foo');\nlike(http_get('/?EXAMPLE.COM'), qr/x:0 y:foo/, 'map EXAMPLE.COM foo');\nlike(http_get('/?example.com.'), qr/x:0 y:foo/, 'map example.com. foo');\nlike(http_get('/?example.org'), qr/x:0 y:right-wildcard/,\n\t'map example.org wildcard');\nlike(http_get('/?foo.example.com'), qr/x:0 y:left-wildcard/,\n\t'map foo.example.com wildcard');\nlike(http_get('/?foo.example.com.'), qr/x:0 y:left-wildcard/,\n\t'map foo.example.com. wildcard');\nlike(http_get('/?dot.example.com'), qr/x:0 y:special-wildcard/,\n\t'map dot.example.com special wildcard');\nlike(http_get('/?www.dot.example.com'), qr/x:0 y:special-wildcard/,\n\t'map www.dot.example.com special wildcard');\nlike(http_get('/?REGEX.EXAMPLE.ORG'), qr/x:0 y:regex-sensitive/,\n\t'map REGEX.EXAMPLE.ORG');\nlike(http_get('/?regex.example.org'), qr/x:0 y:0/,\n\t'map regex.example.org');\nlike(http_get('/?www.regex.example.org'), qr/x:0 y:regex-insensitive/,\n\t'map www.regex.example.org insensitive');\nlike(http_get('/?WWW.REGEX.EXAMPLE.ORG'), qr/x:0 y:regex-insensitive/,\n\t'map WWW.REGEX.EXAMPLE.ORG insensitive');\nlike(http_get('/?include'), qr/x:0 y:include/, 'map special parameter');\nlike(http_get('/?server'), qr/x:0 y:localhost/, 'map server_name variable');\nlike(http_get('/?var'), qr/x:0 y:baz/, 'map z variable');\nlike(http_get('/z?foo'), qr/z:bar/, 'include foo bar');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/map_complex.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for map module with complex value.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http map rewrite/);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    map $args $x {\n        var      foo:$y;\n        var2     $y:foo;\n        default  foo:$y;\n    }\n\n    map $args $y {\n        default  bar;\n        same     baz;\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            add_header X-Foo $x;\n            return 204;\n        }\n    }\n}\n\nEOF\n\n$t->run()->plan(3);\n\n###############################################################################\n\nlike(http_get('/?var'), qr/foo:bar/, 'map cv');\nlike(http_get('/?var2'), qr/bar:foo/, 'map cv 2');\nlike(http_get('/?same'), qr/foo:baz/, 'map cv key');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/map_volatile.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for map module with volatile.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http map/);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    map $uri $uri_cached {\n        /1/          /1/redirect;\n        /1/redirect  uncached;\n    }\n\n    map $uri $uri_uncached {\n        volatile;\n\n        /2/          /2/redirect;\n        /2/redirect  uncached;\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location /1 {\n            index $uri_cached;\n        }\n        location /1/redirect {\n            add_header X-URI $uri_cached always;\n        }\n\n        location /2 {\n            index $uri_uncached;\n        }\n        location /2/redirect {\n            add_header X-URI $uri_uncached always;\n        }\n    }\n}\n\nEOF\n\nmkdir($t->testdir() . '/1');\nmkdir($t->testdir() . '/2');\n\n$t->run()->plan(2);\n\n###############################################################################\n\nlike(http_get('/1/'), qr!X-URI: /1/redirect!, 'map');\nlike(http_get('/2/'), qr/X-URI: uncached/, 'map volatile');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/memcached.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Test for memcached backend.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\neval { require Cache::Memcached; };\nplan(skip_all => 'Cache::Memcached not installed') if $@;\n\nmy $t = Test::Nginx->new()->has(qw/http rewrite memcached/)\n\t->has_daemon('memcached')->plan(4)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            set $memcached_key $uri;\n            memcached_pass 127.0.0.1:8081;\n        }\n\n        location /next {\n            set $memcached_key $uri;\n            memcached_next_upstream  not_found;\n            memcached_pass 127.0.0.1:8081;\n        }\n    }\n}\n\nEOF\n\nmy $memhelp = `memcached -h`;\nmy @memopts = ();\n\nif ($memhelp =~ /repcached/) {\n\t# repcached patch adds additional listen socket\n\tpush @memopts, '-X', port(8082);\n}\nif ($memhelp =~ /-U/) {\n\t# UDP port is on by default in memcached 1.2.7+\n\tpush @memopts, '-U', '0';\n}\n\n$t->run_daemon('memcached', '-l', '127.0.0.1', '-p', port(8081), @memopts);\n$t->run();\n\n$t->waitforsocket('127.0.0.1:' . port(8081))\n\tor die \"Can't start memcached\";\n\n###############################################################################\n\nmy $memd = Cache::Memcached->new(servers => [ '127.0.0.1:' . port(8081) ],\n\tconnect_timeout => 1.0);\n$memd->set('/', 'SEE-THIS')\n\tor die \"can't put value into memcached: $!\";\n\nlike(http_get('/'), qr/SEE-THIS/, 'memcached request');\n\nlike(http_get('/notfound'), qr/ 404 /, 'memcached not found');\n\nlike(http_get('/next'), qr/ 404 /, 'not found with memcached_next_upstream');\n\nunlike(http_head('/'), qr/SEE-THIS/, 'memcached no data in HEAD');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/memcached_fake.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Test for memcached backend with fake daemon.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\nuse Socket qw/ CRLF /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http rewrite memcached ssi/)->plan(3)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            set $memcached_key $uri;\n            memcached_pass 127.0.0.1:8081;\n        }\n\n        location /ssi {\n            default_type text/html;\n            ssi on;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('ssi.html',\n\t'<!--#include virtual=\"/\" set=\"blah\" -->' .\n\t'blah: <!--#echo var=\"blah\" -->');\n\n$t->run_daemon(\\&memcached_fake_daemon);\n$t->run();\n\n$t->waitforsocket('127.0.0.1:' . port(8081))\n\tor die \"Can't start fake memcached\";\n\n###############################################################################\n\nlike(http_get('/'), qr/SEE-THIS/, 'memcached split trailer');\n\nlike(http_get('/ssi.html'), qr/SEE-THIS/, 'memcached ssi var');\n\nlike(`grep -F '[error]' ${\\($t->testdir())}/error.log`, qr/^$/s, 'no errors');\n\n###############################################################################\n\nsub memcached_fake_daemon {\n\tmy $server = IO::Socket::INET->new(\n\t\tProto => 'tcp',\n\t\tLocalAddr => '127.0.0.1:' . port(8081),\n\t\tListen => 5,\n\t\tReuse => 1\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\tlocal $SIG{PIPE} = 'IGNORE';\n\n\twhile (my $client = $server->accept()) {\n\t\t$client->autoflush(1);\n\n\t\twhile (<$client>) {\n\t\t\tlast if (/\\x0d\\x0a$/);\n\t\t}\n\n\t\tprint $client 'VALUE / 0 8' . CRLF;\n\t\tprint $client 'SEE-TH';\n\t\tselect(undef, undef, undef, 0.1);\n\t\tprint $client 'IS';\n\t\tselect(undef, undef, undef, 0.1);\n\t\tprint $client CRLF . 'EN';\n\t\tselect(undef, undef, undef, 0.1);\n\t\tprint $client 'D' . CRLF;\n\t\tclose $client;\n\t}\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/memcached_fake_extra.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n# (C) Nginx, Inc.\n\n# Test for memcached backend returning extra data after trailer.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\nuse Socket qw/ CRLF /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http rewrite memcached/)->plan(1)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            set $memcached_key $uri;\n            memcached_pass 127.0.0.1:8081;\n        }\n    }\n}\n\nEOF\n\n$t->run_daemon(\\&memcached_fake_daemon);\n$t->run();\n\n$t->waitforsocket('127.0.0.1:' . port(8081))\n\tor die \"Can't start fake memcached\";\n\n###############################################################################\n\nlike(http_get('/'), qr/SEE-THIS/, 'memcached data after trailer');\n\n###############################################################################\n\nsub memcached_fake_daemon {\n\tmy $server = IO::Socket::INET->new(\n\t\tProto => 'tcp',\n\t\tLocalAddr => '127.0.0.1:' . port(8081),\n\t\tListen => 5,\n\t\tReuse => 1\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\tlocal $SIG{PIPE} = 'IGNORE';\n\n\twhile (my $client = $server->accept()) {\n\t\t$client->autoflush(1);\n\n\t\twhile (<$client>) {\n\t\t\tlast if (/\\x0d\\x0a$/);\n\t\t}\n\n\t\tprint $client 'VALUE / 0 8' . CRLF;\n\t\tprint $client 'SEE-THIS' . CRLF . 'END' . CRLF\n\t\t\t. \"\\0\" . (\"1\" x 1024);\n\n\t\tselect(undef, undef, undef, 0.2);\n\n\t\tprint $client 'EXTRA' . CRLF;\n\t\tclose $client;\n\t}\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/memcached_keepalive.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Test for memcached with keepalive.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\neval { require Cache::Memcached; };\nplan(skip_all => 'Cache::Memcached not installed') if $@;\n\nmy $t = Test::Nginx->new()->has(qw/http memcached upstream_keepalive rewrite/)\n\t->has_daemon('memcached')->plan(15)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\nworker_processes 1;  # NOTE: The default value of Tengine worker_processes directive is `worker_processes auto;`.\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    upstream memd {\n        server 127.0.0.1:8081;\n        keepalive 1;\n    }\n\n    upstream memd3 {\n        server 127.0.0.1:8081;\n        server 127.0.0.1:8082;\n        keepalive 1;\n    }\n\n    upstream memd4 {\n        server 127.0.0.1:8081;\n        server 127.0.0.1:8082;\n        keepalive 10;\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            set $memcached_key $uri;\n            memcached_pass memd;\n        }\n\n        location /next {\n            set $memcached_key $uri;\n            memcached_next_upstream  not_found;\n            memcached_pass memd;\n        }\n\n        location /memd3 {\n            set $memcached_key \"/\";\n            memcached_pass memd3;\n        }\n\n        location /memd4 {\n            set $memcached_key \"/\";\n            memcached_pass memd4;\n        }\n    }\n}\n\nEOF\n\nmy $memhelp = `memcached -h`;\nmy @memopts1 = ();\nmy @memopts2 = ();\n\nif ($memhelp =~ /repcached/) {\n\t# repcached patches adds additional listen socket memcached\n\t# that should be different too\n\n\tpush @memopts1, '-X', port(8083);\n\tpush @memopts2, '-X', port(8084);\n}\nif ($memhelp =~ /-U/) {\n\t# UDP ports no longer off by default in memcached 1.2.7+\n\n\tpush @memopts1, '-U', '0';\n\tpush @memopts2, '-U', '0';\n}\nif ($memhelp =~ /-t/) {\n\t# for connection stats consistency in threaded memcached 1.3+\n\n\tpush @memopts1, '-t', '1';\n\tpush @memopts2, '-t', '1';\n}\n\n$t->run_daemon('memcached', '-l', '127.0.0.1', '-p', port(8081), @memopts1);\n$t->run_daemon('memcached', '-l', '127.0.0.1', '-p', port(8082), @memopts2);\n\n$t->run();\n\n$t->waitforsocket('127.0.0.1:' . port(8081))\n\tor die \"Unable to start memcached\";\n$t->waitforsocket('127.0.0.1:' . port(8082))\n\tor die \"Unable to start second memcached\";\n\n###############################################################################\n\nmy $memd1 = Cache::Memcached->new(servers => [ '127.0.0.1:' . port(8081) ],\n\tconnect_timeout => 1.0);\nmy $memd2 = Cache::Memcached->new(servers => [ '127.0.0.1:' . port(8082) ],\n\tconnect_timeout => 1.0);\n\n$memd1->set('/', 'SEE-THIS');\n$memd2->set('/', 'SEE-THIS');\n$memd1->set('/big', 'X' x 1000000);\n\nmy $total = $memd1->stats()->{total}->{total_connections};\n\nlike(http_get('/'), qr/SEE-THIS/, 'keepalive memcached request');\nlike(http_get('/notfound'), qr/ 404 /, 'keepalive memcached not found');\nlike(http_get('/next'), qr/ 404 /,\n\t'keepalive not found with memcached_next_upstream');\nlike(http_get('/'), qr/SEE-THIS/, 'keepalive memcached request again');\nlike(http_get('/'), qr/SEE-THIS/, 'keepalive memcached request again');\nlike(http_get('/'), qr/SEE-THIS/, 'keepalive memcached request again');\n\nis($memd1->stats()->{total}->{total_connections}, $total + 1,\n\t'only one connection used');\n\n# Since nginx doesn't read all data from connection in some situations (head\n# requests, post_action, errors writing to client) we have to close such\n# connections.  Check if we really do close them.\n\n$total = $memd1->stats()->{total}->{total_connections};\n\nunlike(http_head('/'), qr/SEE-THIS/, 'head request');\nlike(http_get('/'), qr/SEE-THIS/, 'get after head');\n\nis($memd1->stats()->{total}->{total_connections}, $total + 1,\n\t'head request closes connection');\n\n$total = $memd1->stats()->{total}->{total_connections};\n\nunlike(http_head('/big'), qr/XXX/, 'big head');\nlike(http_get('/'), qr/SEE-THIS/, 'get after big head');\n\nis($memd1->stats()->{total}->{total_connections}, $total + 1,\n\t'big head request closes connection');\n\n# two backends with maximum number of cached connections set to 1,\n# should establish new connection on each request\n\n$total = $memd1->stats()->{total}->{total_connections} +\n\t$memd2->stats()->{total}->{total_connections};\n\nhttp_get('/memd3');\nhttp_get('/memd3');\nhttp_get('/memd3');\n\nis($memd1->stats()->{total}->{total_connections} +\n\t$memd2->stats()->{total}->{total_connections}, $total + 3,\n\t'3 connections should be established');\n\n# two backends with maximum number of cached connections set to 10,\n# should establish only two connections (1 per backend)\n\n$total = $memd1->stats()->{total}->{total_connections} +\n\t$memd2->stats()->{total}->{total_connections};\n\nhttp_get('/memd4');\nhttp_get('/memd4');\nhttp_get('/memd4');\n\nis($memd1->stats()->{total}->{total_connections} +\n\t$memd2->stats()->{total}->{total_connections}, $total + 2,\n\t'connection per backend');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/memcached_keepalive_stale.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Test for stale events handling in upstream keepalive.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\neval { require Cache::Memcached; };\nplan(skip_all => 'Cache::Memcached not installed') if $@;\n\nmy $t = Test::Nginx->new()->has(qw/http memcached upstream_keepalive rewrite/)\n\t->has_daemon('memcached')->plan(1)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nworker_processes 2;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    upstream memd {\n        server 127.0.0.1:8081;\n        keepalive 1;\n    }\n\n    server {\n        listen       127.0.0.1:8080 sndbuf=32k;\n        server_name  localhost;\n\n        location / {\n            set $memcached_key $uri;\n            memcached_pass memd;\n        }\n    }\n}\n\nEOF\n\nmy $memhelp = `memcached -h`;\nmy @memopts1 = ();\n\nif ($memhelp =~ /repcached/) {\n\t# repcached patches adds additional listen socket memcached\n\t# that should be different too\n\n\tpush @memopts1, '-X', port(8082);\n}\nif ($memhelp =~ /-U/) {\n\t# UDP ports no longer off by default in memcached 1.2.7+\n\n\tpush @memopts1, '-U', '0';\n}\nif ($memhelp =~ /-t/) {\n\t# for connection stats consistency in threaded memcached 1.3+\n\n\tpush @memopts1, '-t', '1';\n}\n\n$t->run_daemon('memcached', '-l', '127.0.0.1', '-p', port(8081), @memopts1);\n\n$t->run();\n\n$t->waitforsocket('127.0.0.1:' . port(8081))\n\tor die \"Unable to start memcached\";\n\n###############################################################################\n\nmy $memd1 = Cache::Memcached->new(servers => [ '127.0.0.1:' . port(8081) ],\n\tconnect_timeout => 1.0);\n\n# It's possible that stale events occur, i.e. read event handler called\n# for just saved upstream connection without any data available for\n# read.  We shouldn't close upstream connection in such situation.\n#\n# This happens due to reading from upstream connection on downstream write\n# events.  More likely to happen with multiple workers due to use of posted\n# events.\n#\n# Stale event may only happen if reading response from upstream requires\n# entering event loop, i.e. response should be big enough.  On the other\n# hand, it is less likely to occur with full client's connection output\n# buffer.\n#\n# We use here 2 workers, 20k response and set output buffer on clients\n# connection to 32k.  This allows more or less reliably reproduce stale\n# events at least on FreeBSD testbed here.\n\n$memd1->set('/big', 'X' x 20480);\n\nmy $total = $memd1->stats()->{total}->{total_connections};\n\nfor (1 .. 100) {\n\thttp_get('/big');\n}\n\ncmp_ok($memd1->stats()->{total}->{total_connections}, '<=', $total + 2,\n\t'only one connection per worker used');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/merge_slashes.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for URI normalization, merge_slashes off.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http rewrite/)->plan(2)\n\t->write_file_expand('nginx.conf', <<'EOF')->run();\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        merge_slashes off;\n\n        location / {\n            add_header  X-URI  \"x $uri x\";\n            return      204;\n        }\n    }\n}\n\nEOF\n\n###############################################################################\n\nlike(http_get('/foo//../bar'), qr!x /foo/bar x!, 'merge slashes');\nlike(http_get('/foo///../bar'), qr!x /foo//bar x!, 'merge slashes 2');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/mirror.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for http mirror module.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http mirror/)->plan(8);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    log_format test $uri;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            mirror /mirror;\n\n            location /off {\n                mirror off;\n            }\n        }\n\n        location /many {\n            mirror /mirror/1;\n            mirror /mirror/2;\n        }\n\n        location /mirror {\n            log_subrequest on;\n            access_log test$args.log test;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('index.html', '');\n$t->write_file('many', '');\n$t->write_file('off', '');\n$t->run();\n\n###############################################################################\n\nlike(http_get('/index.html?1'), qr/200 OK/, 'request');\nlike(http_get('/?2'), qr/200 OK/, 'internal redirect');\nlike(http_get('/off?3'), qr/200 OK/, 'mirror off');\nlike(http_get('/many?4'), qr/200 OK/, 'mirror many');\n\n$t->stop();\n\nis($t->read_file('test1.log'), \"/mirror\\n\", 'log - request');\nis($t->read_file('test2.log'), \"/mirror\\n/mirror\\n\", 'log - redirect');\nok(!-e $t->testdir() . '/test3.log', 'log - mirror off');\nis($t->read_file('test4.log'), \"/mirror/1\\n/mirror/2\\n\", 'log - mirror many');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/mirror_proxy.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for http mirror module and it's interaction with proxy.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse IO::Select;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy mirror rewrite limit_req/);\n\n$t->write_file_expand('nginx.conf', <<'EOF')->plan(7);\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    limit_req_zone  $uri  zone=slow:1m  rate=30r/m;\n    log_format  test  $request_uri:$request_body;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            mirror /mirror;\n            proxy_pass http://127.0.0.1:8081;\n        }\n\n        location /off {\n            mirror /mirror/off;\n            mirror_request_body off;\n            proxy_pass http://127.0.0.1:8081;\n        }\n\n        location /mirror {\n            internal;\n            proxy_pass http://127.0.0.1:8082;\n            limit_req  zone=slow burst=1;\n        }\n\n        location /mirror/off {\n            internal;\n            proxy_pass http://127.0.0.1:8082;\n            proxy_set_header Content-Length \"\";\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081;\n        listen       127.0.0.1:8082;\n        server_name  localhost;\n\n        location / {\n            proxy_pass http://127.0.0.1:$server_port/return204;\n            access_log %%TESTDIR%%/test.log test;\n            add_header X-Body $request_body;\n        }\n\n        location /return204 {\n            return 204;\n        }\n    }\n}\n\nEOF\n\n$t->run();\n\n###############################################################################\n\nlike(http_post('/'), qr/X-Body: 1234567890\\x0d?$/m, 'mirror proxy');\nlike(http_post('/off'), qr/X-Body: 1234567890\\x0d?$/m, 'mirror_request_body');\n\n# delayed subrequest should not affect main request processing nor stuck itself\n\nmy $s = http_post('/delay?1', start => 1);\nlike(read_keepalive($s), qr/X-Body: 1234567890\\x0d?$/m, 'mirror delay');\n\n$t->todo_alerts();\n$t->stop();\n\nmy $log = $t->read_file('test.log');\nlike($log, qr!^/:1234567890$!m, 'log - request body');\nlike($log, qr!^/mirror:1234567890$!m, 'log - request body in mirror');\nlike($log, qr!^/off:1234567890$!m, 'log - mirror_request_body off');\nlike($log, qr!^/mirror/off:-$!m,, 'log - mirror_request_body off in mirror');\n\n###############################################################################\n\nsub http_post {\n\tmy ($url, %extra) = @_;\n\n\thttp(<<EOF, %extra);\nPOST $url HTTP/1.0\nHost: localhost\nContent-Length: 10\n\n1234567890\nEOF\n}\n\nsub read_keepalive {\n\tmy ($s) = @_;\n\tmy $data = '';\n\n\twhile (IO::Select->new($s)->can_read(3)) {\n\t\tsysread($s, my $buffer, 4096) or last;\n\t\t$data .= $buffer;\n\t\tlast if $data =~ /^\\x0d\\x0a/ms;\n\t}\n\n\tlog_in($data);\n\treturn $data;\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/mp4.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for mp4 module.\n# Ensures that requested stream duration is given with sane accuracy.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx qw/ :DEFAULT http_content /;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http mp4/)->has_daemon('ffprobe')\n\t->has_daemon('ffmpeg')\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            mp4;\n        }\n    }\n}\n\nEOF\n\nplan(skip_all => 'no lavfi')\n\tunless grep /lavfi/, `ffmpeg -loglevel quiet -formats`;\nsystem('ffmpeg -nostdin -loglevel quiet -y '\n\t. '-f lavfi -i testsrc=duration=10:size=320x200:rate=15 '\n\t. '-f lavfi -i testsrc=duration=20:size=320x200:rate=15 '\n\t. '-map 0:0 -map 1:0 -pix_fmt yuv420p -g 15 -c:v libx264 '\n\t. \"${\\($t->testdir())}/test.mp4\") == 0\n\tor die \"Can't create mp4 file: $!\";\nsystem('ffmpeg -nostdin -loglevel quiet -y '\n\t. '-f lavfi -i testsrc=duration=10:size=320x200:rate=15 '\n\t. '-f lavfi -i testsrc=duration=20:size=320x200:rate=15 '\n\t. '-map 0:0 -map 1:0 -pix_fmt yuv420p -g 15 -c:v libx264 '\n\t. '-movflags +faststart '\n\t. \"${\\($t->testdir())}/no_mdat.mp4\") == 0\n\tor die \"Can't create mp4 file: $!\";\n\nmy $sbad = <<'EOF';\n00000000:  00 00 00 1c 66 74 79 70  69 73 6f 6d 00 00 02 00  |....ftypisom....|\n00000010:  69 73 6f 6d 69 73 6f 32  6d 70 34 31 00 00 00 09  |isomiso2mp41....|\n00000020:  6d 64 61 74 00 00 00 00  94 6d 6f 6f 76 00 00 00  |mdat.....moov...|\n00000030:  8c 74 72 61 6b 00 00 00  84 6d 64 69 61 00 00 00  |.trak....mdia...|\n00000040:  7c 6d 69 6e 66 00 00 00  74 73 74 62 6c 00 00 00  ||minf...tstbl...|\n00000050:  18 73 74 74 73 00 00 00  00 00 00 00 01 00 00 03  |.stts...........|\n00000060:  3a 00 00 04 00 00 00 00  28 73 74 73 63 00 00 00  |:.......(stsc...|\n00000070:  00 00 00 00 02 00 00 00  01 00 00 03 0f 00 00 00  |................|\n00000080:  01 00 00 00 02 00 00 00  2b 00 00 00 01 00 00 00  |........+.......|\n00000090:  14 73 74 73 7a 00 00 00  00 00 00 05 a9 00 00 03  |.stsz...........|\n000000a0:  3b 00 00 00 18 63 6f 36  34 00 00 00 00 00 00 00  |;....co64.......|\n000000b0:  01 ff ff ff ff f0 0f fb  e7                       |.........|\nEOF\n\n$t->write_file('bad.mp4', unhex($sbad));\n$t->run()->plan(27);\n\n###############################################################################\n\nmy $test_uri = '/test.mp4';\n\nagain:\n\nis(durations($t, 0.0), '10.0 20.0', 'start zero');\nis(durations($t, 2), '8.0 18.0', 'start integer');\nis(durations($t, 7.1), '2.9 12.9', 'start float');\n\nis(durations($t, 6, 9), '3.0 3.0', 'start end integer');\nis(durations($t, 2.7, 5.6), '2.9 2.9', 'start end float');\n\nis(durations($t, undef, 9), '9.0 9.0', 'end integer');\nis(durations($t, undef, 5.6), '5.6 5.6', 'end float');\n\n# invalid range results in ignoring end argument\n\nlike(http_head(\"$test_uri?start=1&end=1\"), qr/200 OK/, 'zero range');\nlike(http_head(\"$test_uri?start=1&end=0\"), qr/200 OK/, 'negative range');\n\n# start/end values exceeding track/file duration\n\nunlike(http_head(\"$test_uri?end=11\"), qr!HTTP/1.1 500!,\n\t'end beyond short track');\nunlike(http_head(\"$test_uri?end=21\"), qr!HTTP/1.1 500!, 'end beyond EOF');\nunlike(http_head(\"$test_uri?start=11\"), qr!HTTP/1.1 500!,\n\t'start beyond short track');\nlike(http_head(\"$test_uri?start=21\"), qr!HTTP/1.1 500!, 'start beyond EOF');\n\n$test_uri = '/no_mdat.mp4', goto again unless $test_uri eq '/no_mdat.mp4';\n\n# corrupted formats\n\nlike(http_get(\"/bad.mp4?start=0.5\"), qr/500 Internal/, 'co64 chunk beyond EOF');\n\n###############################################################################\n\nsub durations {\n\tmy ($t, $start, $end) = @_;\n\tmy $path = $t->{_testdir} . '/frag.mp4';\n\n\tmy $uri = $test_uri;\n\tif (defined $start) {\n\t\t$uri .= \"?start=$start\";\n\t\tif (defined $end) {\n\t\t\t$uri .= \"&end=$end\";\n\t\t}\n\n\t} elsif (defined $end) {\n\t\t$uri .= \"?end=$end\";\n\t}\n\n\t$t->write_file('frag.mp4', http_content(http_get($uri)));\n\n\tmy $r = `ffprobe -show_streams $path 2>/dev/null`;\n\tTest::Nginx::log_core('||', $r);\n\tsprintf \"%.1f %.1f\", $r =~ /duration=(\\d+\\.\\d+)/g;\n}\n\nsub unhex {\n\tmy ($input) = @_;\n\tmy $buffer = '';\n\n\tfor my $l ($input =~ m/:  +((?:[0-9a-f]{2,4} +)+) /gms) {\n\t\tfor my $v ($l =~ m/[0-9a-f]{2}/g) {\n\t\t\t$buffer .= chr(hex($v));\n\t\t}\n\t}\n\n\treturn $buffer;\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/mp4_ssi.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Test for mp4 module in subrequests.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http mp4 ssi/)->has_daemon('ffprobe')\n\t->has_daemon('ffmpeg')->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            ssi on;\n        }\n        location /ssi {\n            mp4;\n        }\n    }\n}\n\nEOF\n\nplan(skip_all => 'no lavfi')\n\tunless grep /lavfi/, `ffmpeg -loglevel quiet -formats`;\nsystem('ffmpeg -nostdin -loglevel quiet -y '\n\t. '-f lavfi -i testsrc=duration=10:size=320x200:rate=15 '\n\t. '-f lavfi -i testsrc=duration=20:size=320x200:rate=15 '\n\t. '-map 0:0 -map 1:0 -pix_fmt yuv420p -g 15 -c:v libx264 '\n\t. \"${\\($t->testdir())}/ssi.mp4\") == 0\n\tor die \"Can't create mp4 file: $!\";\n\n$t->write_file('index.html', 'X<!--#include virtual=\"/ssi.mp4?end=1\" -->X');\n\n$t->run()->plan(1);\n\n###############################################################################\n\n(my $r = get('/')) =~ s/([^\\x20-\\x7e])/sprintf('\\\\x%02x', ord($1))/gmxe;\nunlike($r, qr/\\\\x0d(\\\\x0a)?0\\\\x0d(\\\\x0a)?\\\\x0d(\\\\x0a)?\\w/, 'only final chunk');\n\n###############################################################################\n\nsub get {\n\tmy ($url, $extra) = @_;\n\treturn http(<<EOF);\nGET $url HTTP/1.1\nHost: localhost\nConnection: close\n\nEOF\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/mp4_start_key_frame.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for the mp4_start_key_frame directive.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx qw/ :DEFAULT http_content /;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http mp4/)->has_daemon('ffprobe')\n\t->has_daemon('ffmpeg')\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            mp4;\n        }\n\n        location /force/ {\n            mp4;\n            mp4_start_key_frame on;\n            alias %%TESTDIR%%/;\n        }\n    }\n}\n\nEOF\n\nplan(skip_all => 'no lavfi')\n\tunless grep /lavfi/, `ffmpeg -loglevel quiet -formats`;\nsystem('ffmpeg -nostdin -loglevel quiet -y '\n\t. '-f lavfi -i testsrc=duration=10:size=320x200:rate=15 '\n\t. '-pix_fmt yuv420p -g 15 -c:v libx264 '\n\t. \"${\\($t->testdir())}/test.mp4\") == 0\n\tor die \"Can't create mp4 file: $!\";\n$t->try_run('no mp4_start_key_frame')->plan(4);\n\n###############################################################################\n\n# baseline durations\n\nmy $test_uri = '/test.mp4';\nis(durations($t, 2.0, 4.0), '2.00', 'start at key frame');\nisnt(durations($t, 2.1, 4.0), '1.90', 'start off key frame');\n\n# with forced start at key frame\n\n$test_uri = '/force/test.mp4';\nis(durations($t, 2.0, 4.0), '2.00', 'start at key frame force');\nis(durations($t, 2.1, 4.0), '1.90', 'start off key frame force');\n\n###############################################################################\n\nsub durations {\n\tmy ($t, $start, $end) = @_;\n\tmy $path = $t->{_testdir} . '/frag.mp4';\n\n\tmy $uri = $test_uri;\n\tif (defined $start) {\n\t\t$uri .= \"?start=$start\";\n\t\tif (defined $end) {\n\t\t\t$uri .= \"&end=$end\";\n\t\t}\n\n\t} elsif (defined $end) {\n\t\t$uri .= \"?end=$end\";\n\t}\n\n\t$t->write_file('frag.mp4', http_content(http_get($uri)));\n\n\tmy $r = `ffprobe -show_streams $path 2>/dev/null`;\n\tTest::Nginx::log_core('||', $r);\n\tsprintf \"%.2f\", $r =~ /duration=(\\d+\\.\\d+)/g;\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/msie_refresh.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Test for msie_refresh.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http rewrite ssi/)->plan(5)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        msie_refresh on;\n\n        location / {\n            return 301 text;\n        }\n\n        location /space {\n            return 301 \"space \";\n        }\n\n        location /error_page {\n            return 301;\n            error_page 301 text;\n        }\n\n        location /off {\n            msie_refresh off;\n            return 301 text;\n        }\n\n        location /ssi {\n            ssi on;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('ssi.html', 'X<!--#include virtual=\"/\" -->X');\n$t->run();\n\n###############################################################################\n\nlike(get('/'), qr/Refresh.*URL=text\"/, 'msie refresh');\nlike(get('/space'), qr/URL=space%20\"/, 'msie refresh escaped url');\nlike(get('/error_page'), qr/URL=text\"/, 'msie refresh error page');\n\nunlike(get('/off'), qr/Refresh/, 'msie refresh disabled');\n\nunlike(get('/ssi.html'), qr/^0\\x0d\\x0a?\\x0d\\x0a?\\w/m, 'only final chunk');\n\n###############################################################################\n\nsub get {\n\tmy ($url, $extra) = @_;\n\treturn http(<<EOF);\nGET $url HTTP/1.1\nHost: localhost\nConnection: close\nUser-Agent: MSIE foo\n\nEOF\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/not_modified.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for not modified filter module.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has('http')->plan(15)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            if_modified_since before;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('t', '');\n\n$t->run();\n\n###############################################################################\n\nlike(http_get_ims('/t', 'Wed, 08 Jul 2037 22:53:52 GMT'), qr/ 304 /,\n\t'0x7F000000');\nlike(http_get_ims('/t', 'Tue, 19 Jan 2038 03:14:07 GMT'), qr/ 304 /,\n\t'0x7FFFFFFF');\n\nSKIP: {\n\tskip \"only for 32-bit time_t\", 2 if (gmtime(0xFFFFFFFF))[5] == 206;\n\n\tlike(http_get_ims('/t', 'Tue, 19 Jan 2038 03:14:08 GMT'), qr/ 200 /,\n\t\t'0x7FFFFFFF + 1');\n\tlike(http_get_ims('/t', 'Fri, 25 Feb 2174 09:42:23 GMT'), qr/ 200 /,\n\t\t'0x17FFFFFFF');\n}\n\n# If-Match, If-None-Match tests\n\nmy ($t1, $etag);\n\n$t1 = http_get('/t');\n$t1 =~ /ETag: (\".*\")/;\n$etag = $1;\n\nlike(http_get_inm('/t', $etag), qr/ 304 /, 'if-none-match');\nlike(http_get_inm('/t', '\"foo\"'), qr/ 200 /, 'if-none-match fail');\nlike(http_get_inm('/t', '\"foo\", \"bar\", ' . $etag . ' , \"baz\"'), qr/ 304 /,\n\t'if-none-match with complex list');\nlike(http_get_inm('/t', '*'), qr/ 304 /, 'if-none-match all');\nlike(http_get_inm('/t', 'W/' . $etag), qr/ 304 /, 'if-none-match weak');\nlike(http_get_im('/t', $etag), qr/ 200 /, 'if-match');\nlike(http_get_im('/t', '\"foo\"'), qr/ 412 /, 'if-match fail');\nlike(http_get_im('/t', '\"foo\", \"bar\", ' . \"\\t\" . $etag . ' , \"baz\"'),\n\tqr/ 200 /, 'if-match with complex list');\nlike(http_get_im('/t', '*'), qr/ 200 /, 'if-match all');\nlike(http_get_im('/t', 'W/' . $etag), qr/ 412 /, 'if-match weak fail');\n\n# server MUST ignore precondition if its response wouldn't be 2xx or 412\n\nlike(http_get_im('/nx', '\"foo\"'), qr/ 404 /, 'if-match ignored with 404');\n\n###############################################################################\n\nsub http_get_ims {\n\tmy ($url, $ims) = @_;\n\treturn http(<<EOF);\nGET $url HTTP/1.0\nHost: localhost\nIf-Modified-Since: $ims\n\nEOF\n}\n\nsub http_get_inm {\n\tmy ($url, $inm) = @_;\n\treturn http(<<EOF);\nGET $url HTTP/1.0\nHost: localhost\nIf-None-Match: $inm\n\nEOF\n}\n\nsub http_get_im {\n\tmy ($url, $inm) = @_;\n\treturn http(<<EOF);\nGET $url HTTP/1.0\nHost: localhost\nIf-Match: $inm\n\nEOF\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/not_modified_finalize.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for not modified filter and filter finalization.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy cache/)->plan(2)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    proxy_cache_path %%TESTDIR%%/cache keys_zone=cache:1m;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        error_page 412 /error412.html;\n\n        location / {\n            proxy_pass        http://127.0.0.1:8081;\n            proxy_cache       cache;\n            proxy_cache_lock  on;\n            proxy_cache_valid 1h;\n        }\n\n        location /error412 {\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n    }\n}\n\nEOF\n\n$t->write_file('t.html', 'test file');\n$t->write_file('error412.html', 'error412');\n\n$t->run();\n\n###############################################################################\n\n# we trigger filter finalization in not modified filter by using\n# the If-Unmodified-Since/If-Match header;\n# with cache enabled and updating bit set, this currently results in\n# \"stalled cache updating\" alerts\n\nlike(http_match_get('/t.html'), qr//, 'request 412');\n\n$t->todo_alerts();\n\n# in addition, in 1.11.10 .. 1.17.1, if the response was previously\n# cached, such a request resulted in r->cache null pointer dereference\n# in ngx_http_upstream_cache_background_update(), after it was reset\n# during internal redirect\n\nhttp_get('/t.html');\n\nlike(http_match_get('/t.html'), qr//, 'request 412 cached');\n\n###############################################################################\n\nsub http_match_get {\n\tmy ($url, %extra) = @_;\n\treturn http(<<EOF, %extra);\nGET $url HTTP/1.0\nHost: localhost\nIf-Match: tt\n\nEOF\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/not_modified_proxy.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for not modified filter module and it's interaction with proxy.\n#\n# Notably, requests which are proxied should be skipped (that is, if\n# a backend returned 200, we should pass 200 to a client without any\n# attempts to handle conditional headers in the request), but responses\n# from cache should be handled.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy cache/)->plan(12);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    proxy_cache_path %%TESTDIR%%/cache keys_zone=one:1m;\n\n    proxy_set_header If-Modified-Since \"\";\n    proxy_set_header If-Unmodified-Since \"\";\n    proxy_set_header If-None-Match \"\";\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n        }\n\n        location /etag {\n            add_header Last-Modified \"\";\n        }\n\n        location /proxy/ {\n            proxy_pass http://127.0.0.1:8080/;\n        }\n\n        location /cache/ {\n            proxy_pass http://127.0.0.1:8080/;\n            proxy_cache one;\n            proxy_cache_valid 200 1y;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('t', '');\n$t->write_file('etag', '');\n\n$t->run();\n\n###############################################################################\n\nmy ($t1, $lm, $etag);\n\n$t1 = http_get('/cache/t');\n$t1 =~ /Last-Modified: (.*)/; $lm = $1;\n$t1 =~ /ETag: (.*)/; $etag = $1;\n\nlike(http_get_ims('/t', $lm), qr/ 304 /, 'if-modified-since');\nlike(http_get_ims('/proxy/t', $lm), qr/ 200 /, 'ims proxy ignored');\nlike(http_get_ims('/cache/t', $lm), qr/ 304 /, 'ims from cache');\n\n$t1 = 'Fri, 05 Jul 1985 14:30:52 GMT';\n\nlike(http_get_iums('/t', $t1), qr/ 412 /, 'if-unmodified-since');\nlike(http_get_iums('/proxy/t', $t1), qr/ 200 /, 'iums proxy ignored');\nlike(http_get_iums('/cache/t', $t1), qr/ 412 /, 'iums from cache');\n\nlike(http_get_inm('/t', $etag), qr/ 304 /, 'if-none-match');\nlike(http_get_inm('/proxy/t', $etag), qr/ 200 /, 'inm proxy ignored');\nlike(http_get_inm('/cache/t', $etag), qr/ 304 /, 'inm from cache');\n\n# backend response with ETag only, no Last-Modified\n\n$t1 = http_get('/cache/etag');\n$t1 =~ /ETag: (.*)/; $etag = $1;\n\nlike(http_get_inm('/etag', $etag), qr/ 304 /, 'if-none-match etag only');\nlike(http_get_inm('/proxy/etag', $etag), qr/ 200 /, 'inm etag proxy ignored');\nlike(http_get_inm('/cache/etag', $etag), qr/ 304 /, 'inm etag from cache');\n\n###############################################################################\n\nsub http_get_ims {\n\tmy ($url, $ims) = @_;\n\treturn http(<<EOF);\nGET $url HTTP/1.0\nHost: localhost\nIf-Modified-Since: $ims\n\nEOF\n}\n\nsub http_get_iums {\n\tmy ($url, $ims) = @_;\n\treturn http(<<EOF);\nGET $url HTTP/1.0\nHost: localhost\nIf-Unmodified-Since: $ims\n\nEOF\n}\n\nsub http_get_inm {\n\tmy ($url, $inm) = @_;\n\treturn http(<<EOF);\nGET $url HTTP/1.0\nHost: localhost\nIf-None-Match: $inm\n\nEOF\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/perl.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for embedded perl module.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse Socket qw/ CRLF /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http perl rewrite/)->plan(27)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            set $testvar \"TEST\";\n            perl 'sub {\n                use warnings;\n                use strict;\n\n                my $r = shift;\n\n                $r->status(204) if $r->args =~ /204/;\n\n                $r->send_http_header(\"text/plain\");\n\n                return OK if $r->header_only;\n\n                my $v = $r->variable(\"testvar\");\n\n                $r->print(\"testvar: $v\\n\");\n\n                $r->print(\"host: \", $r->header_in(\"Host\"), \"\\n\");\n                $r->print(\"xfoo: \", $r->header_in(\"X-Foo\"), \"\\n\");\n                $r->print(\"cookie: \", $r->header_in(\"Cookie\"), \"\\n\");\n                $r->print(\"xff: \", $r->header_in(\"X-Forwarded-For\"), \"\\n\");\n                $r->print(\"connection: \", $r->header_in(\"Connection\"), \"\\n\");\n\n                return OK;\n            }';\n        }\n\n        location /range {\n            perl 'sub {\n                use warnings;\n                use strict;\n\n                my $r = shift;\n\n                $r->header_out(\"Content-Length\", \"42\");\n                $r->allow_ranges();\n                $r->send_http_header(\"text/plain\");\n\n                return OK if $r->header_only;\n\n                $r->print(\"x\" x 42);\n\n                return OK;\n            }';\n        }\n\n        location /body {\n            perl 'sub {\n                use warnings;\n                use strict;\n\n                my $r = shift;\n\n                if ($r->has_request_body(\\&post)) {\n                    return OK;\n                }\n\n                return HTTP_BAD_REQUEST;\n\n                sub post {\n                    my $r = shift;\n                    $r->send_http_header;\n                    $r->print(\"body: \", $r->request_body, \"\\n\");\n                    $r->print(\"file: \", $r->request_body_file, \"\\n\");\n                }\n            }';\n        }\n\n        location /discard {\n            perl 'sub {\n                use warnings;\n                use strict;\n\n                my $r = shift;\n\n                $r->discard_request_body;\n\n                $r->send_http_header(\"text/plain\");\n\n                return OK if $r->header_only;\n\n                $r->print(\"host: \", $r->header_in(\"Host\"), \"\\n\");\n\n                return OK;\n            }';\n        }\n    }\n}\n\nEOF\n\n$t->run();\n\n###############################################################################\n\nlike(http_get('/'), qr/ 200 .*TEST/s, 'perl response');\nlike(http_head('/'), qr/ 200 (?!.*TEST)/s, 'perl header_only');\nlike(http_get('/?204'), qr/ 204 (?!.*TEST)/s, 'perl status, args');\n\n# various $r->header_in() cases\n\nlike(http(\n\t'GET / HTTP/1.0' . CRLF\n\t. 'Host: localhost' . CRLF . CRLF\n), qr/host: localhost/, 'perl header_in known');\n\nlike(http(\n\t'GET / HTTP/1.0' . CRLF\n\t. 'X-Foo: foo' . CRLF\n\t. 'Host: localhost' . CRLF . CRLF\n), qr/xfoo: foo/, 'perl header_in unknown');\n\nTODO: {\nlocal $TODO = 'not yet' unless $t->has_version('1.23.0');\n\nlike(http(\n\t'GET / HTTP/1.0' . CRLF\n\t. 'X-Foo: foo' . CRLF\n\t. 'X-Foo: bar' . CRLF\n\t. 'Host: localhost' . CRLF . CRLF\n), qr/xfoo: foo, bar/, 'perl header_in unknown2');\n\n}\n\nlike(http(\n\t'GET / HTTP/1.0' . CRLF\n\t. 'Cookie: foo' . CRLF\n\t. 'Host: localhost' . CRLF . CRLF\n), qr/cookie: foo/, 'perl header_in cookie');\n\nlike(http(\n\t'GET / HTTP/1.0' . CRLF\n\t. 'Cookie: foo1' . CRLF\n\t. 'Cookie: foo2' . CRLF\n\t. 'Host: localhost' . CRLF . CRLF\n), qr/cookie: foo1; foo2/, 'perl header_in cookie2');\n\nlike(http(\n\t'GET / HTTP/1.0' . CRLF\n\t. 'X-Forwarded-For: foo' . CRLF\n\t. 'Host: localhost' . CRLF . CRLF\n), qr/xff: foo/, 'perl header_in xff');\n\nlike(http(\n\t'GET / HTTP/1.0' . CRLF\n\t. 'X-Forwarded-For: foo1' . CRLF\n\t. 'X-Forwarded-For: foo2' . CRLF\n\t. 'Host: localhost' . CRLF . CRLF\n), qr/xff: foo1, foo2/, 'perl header_in xff2');\n\nTODO: {\nlocal $TODO = 'not yet' unless $t->has_version('1.23.0');\n\nlike(http(\n\t'GET / HTTP/1.0' . CRLF\n\t. 'Connection: close' . CRLF\n\t. 'Host: localhost' . CRLF . CRLF\n), qr/connection: close/, 'perl header_in connection');\n\nlike(http(\n\t'GET / HTTP/1.0' . CRLF\n\t. 'Connection: close' . CRLF\n\t. 'Connection: foo' . CRLF\n\t. 'Host: localhost' . CRLF . CRLF\n), qr/connection: close, foo/, 'perl header_in connection2');\n\n}\n\n# headers_out content-length tests with range filter\n\nlike(http_get('/range'), qr/Content-Length: 42.*^x{42}$/ms,\n\t'perl header_out content-length');\n\nlike(http(\n\t'GET /range HTTP/1.0' . CRLF\n\t. 'Host: localhost' . CRLF\n\t. 'Range: bytes=0-1' . CRLF . CRLF\n), qr/Content-Length: 2.*^xx$/ms, 'perl header_out content-length range');\n\nlike(http(\n\t'GET /range HTTP/1.0' . CRLF\n\t. 'Host: localhost' . CRLF\n\t. 'Range: bytes=0-1,3-5' . CRLF . CRLF\n), qr/Content-Length: (?!42).*^xx\\x0d.*^xxx\\x0d/ms,\n\t'perl header_out content-length multipart');\n\nlike(http(\n\t'GET /range HTTP/1.0' . CRLF\n\t. 'Host: localhost' . CRLF\n\t. 'Range: bytes=100000-' . CRLF . CRLF\n), qr|^\\QHTTP/1.1 416\\E.*(?!xxx)|ms, 'perl range not satisfiable');\n\nlike(http(\n\t'GET / HTTP/1.0' . CRLF\n\t. 'Host: localhost' . CRLF\n\t. 'If-Match: tt' . CRLF . CRLF\n), qr|200 OK|ms, 'perl precondition failed');\n\n# various request body tests\n\nlike(http_get('/body'), qr/400 Bad Request/, 'perl no body');\n\nlike(http(\n\t'GET /body HTTP/1.0' . CRLF\n\t. 'Host: localhost' . CRLF\n\t. 'Content-Length: 10' . CRLF . CRLF\n\t. '1234567890'\n), qr/body: 1234567890/, 'perl body preread');\n\nlike(http(\n\t'GET /body HTTP/1.0' . CRLF\n\t. 'Host: localhost' . CRLF\n\t. 'Content-Length: 10' . CRLF . CRLF,\n\tsleep => 0.1,\n\tbody => '1234567890'\n), qr/body: 1234567890/, 'perl body late');\n\nlike(http(\n\t'GET /body HTTP/1.0' . CRLF\n\t. 'Host: localhost' . CRLF\n\t. 'Content-Length: 10' . CRLF . CRLF\n\t. '12345',\n\tsleep => 0.1,\n\tbody => '67890'\n), qr/body: 1234567890/, 'perl body split');\n\nlike(http(\n\t'GET /body HTTP/1.1' . CRLF\n\t. 'Host: localhost' . CRLF\n\t. 'Connection: close' . CRLF\n\t. 'Transfer-Encoding: chunked' . CRLF . CRLF\n\t. 'a' . CRLF\n\t. '1234567890' . CRLF\n\t. '0' . CRLF . CRLF\n), qr/body: 1234567890/, 'perl body chunked');\n\nlike(http(\n\t'GET /body HTTP/1.1' . CRLF\n\t. 'Host: localhost' . CRLF\n\t. 'Connection: close' . CRLF\n\t. 'Transfer-Encoding: chunked' . CRLF . CRLF,\n\tsleep => 0.1,\n\tbody => 'a' . CRLF . '1234567890' . CRLF . '0' . CRLF . CRLF\n), qr/body: 1234567890/, 'perl body chunked late');\n\nlike(http(\n\t'GET /body HTTP/1.1' . CRLF\n\t. 'Host: localhost' . CRLF\n\t. 'Connection: close' . CRLF\n\t. 'Transfer-Encoding: chunked' . CRLF . CRLF\n\t. 'a' . CRLF\n\t. '12345',\n\tsleep => 0.1,\n\tbody => '67890' . CRLF . '0' . CRLF . CRLF\n), qr/body: 1234567890/, 'perl body chunked split');\n\nlike(http(\n\t'GET /discard HTTP/1.1' . CRLF\n\t. 'Host: localhost' . CRLF\n\t. 'Connection: close' . CRLF\n\t. 'Transfer-Encoding: chunked' . CRLF . CRLF\n\t. 'a' . CRLF\n\t. '1234567890' . CRLF\n\t. '0' . CRLF . CRLF\n), qr/host: localhost/, 'perl body discard');\n\nlike(http(\n\t'GET /discard HTTP/1.1' . CRLF\n\t. 'Host: localhost' . CRLF\n\t. 'Connection: close' . CRLF\n\t. 'Transfer-Encoding: chunked' . CRLF . CRLF\n\t. 'ak' . CRLF\n\t. '1234567890' . CRLF\n\t. '0' . CRLF . CRLF\n), qr/400 Bad Request/, 'perl body discard bad chunk');\n\nlike(http(\n\t'GET /body HTTP/1.1' . CRLF\n\t. 'Host: localhost' . CRLF\n\t. 'Connection: close' . CRLF\n\t. 'Transfer-Encoding: chunked' . CRLF . CRLF\n\t. 'ak' . CRLF\n\t. '1234567890' . CRLF\n\t. '0' . CRLF . CRLF\n), qr/400 Bad Request/, 'perl body bad chunk');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/perl_gzip.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for embedded perl module.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx qw/ :DEFAULT :gzip /;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\neval { require IO::Compress::Gzip; };\nplan(skip_all => \"IO::Compress::Gzip not found\") if $@;\n\nmy $t = Test::Nginx->new()->has(qw/http perl gzip/)->plan(2)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        gzip on;\n        gzip_types text/plain;\n\n        location / {\n            perl 'sub {\n                my $r = shift;\n                $r->send_http_header(\"text/plain\");\n                return OK if $r->header_only;\n                $r->print(\"TEST\");\n                return OK;\n            }';\n        }\n\n        location /gz {\n            perl 'sub {\n                my $r = shift;\n                $r->header_out(\"Content-Encoding\", \"gzip\");\n                $r->send_http_header(\"text/plain\");\n                return OK if $r->header_only;\n                use IO::Compress::Gzip;\n                my $in = \"TEST\";\n                my $out;\n                IO::Compress::Gzip::gzip(\\\\$in => \\\\$out);\n                $r->print($out);\n                return OK;\n            }';\n        }\n    }\n}\n\nEOF\n\n$t->run();\n\n###############################################################################\n\nhttp_gzip_like(http_gzip_request('/'), qr/TEST/, 'perl response gzipped');\nhttp_gzip_like(http_gzip_request('/gz'), qr/TEST/, 'not doublegzipped');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/perl_sleep.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for embedded perl module, $r->sleep().\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http perl ssi/)->plan(2)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            ssi on;\n            sendfile_max_chunk 100;\n            postpone_output 0;\n        }\n\n        location /sleep {\n            perl 'sub {\n                my $r = shift;\n\n                $r->sleep(100, sub {\n                    my $r = shift;\n                    $r->send_http_header;\n                    $r->print(\"it works\");\n                    return OK;\n                });\n\n                return OK;\n            }';\n        }\n    }\n}\n\nEOF\n\n$t->write_file('subrequest.html', ('x' x 200) .\n\t'X<!--#include virtual=\"/sleep\" -->X');\n\n$t->run();\n\n###############################################################################\n\nlike(http_get('/sleep'), qr/works/, 'perl sleep');\nlike(http_get('/subrequest.html'), qr/works/, 'perl sleep in subrequest');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/perl_ssi.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for embedded perl module.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http perl ssi/)->plan(3)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            ssi on;\n        }\n\n        location /dummy {\n            perl 'sub foo { my $r = shift; $r->print(join \",\", @_); }';\n        }\n    }\n}\n\nEOF\n\n$t->write_file('t1.html', 'X<!--#perl sub=\"foo\" arg=\"arg1\" -->X');\n$t->write_file('t2.html', 'X<!--#perl sub=\"foo\" arg=\"arg1\" arg=\"arg2\" -->X');\n$t->write_file('noargs.html', 'X<!--#perl sub=\"foo\" -->X');\n\n$t->run();\n\n###############################################################################\n\nlike(http_get('/t1.html'), qr/Xarg1X/, 'perl ssi response');\nlike(http_get('/t2.html'), qr/Xarg1,arg2X/, 'perl ssi two args');\nlike(http_get('/noargs.html'), qr/XX/, 'perl ssi noargs');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/post_action.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n# (C) Nginx, Inc.\n\n# Tests for nginx post_action directive.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy/)->plan(5);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            post_action /post.html;\n        }\n\n        location /post.html {\n            # static\n        }\n\n        location /remote {\n            post_action /post.remote;\n        }\n\n        location /post.remote {\n            proxy_pass http://127.0.0.1:8080/post.html;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('index.html', 'SEE-THIS');\n$t->write_file('post.html', 'HIDDEN');\n$t->write_file('remote', 'SEE-THIS');\n\n$t->run();\n\n###############################################################################\n\nlike(http_get('/'), qr/SEE-THIS/m, 'post action');\nunlike(http_get('/'), qr/HIDDEN/m, 'no additional body');\n\nlike(http_get('/remote'), qr/SEE-THIS/m, 'post action proxy');\nunlike(http_get('/remote'), qr/HIDDEN/m, 'no additional body proxy');\n\n$t->stop();\n\nlike(`cat ${\\($t->testdir())}/access.log`, qr/post/, 'post action in logs');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for http proxy module.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy/)->plan(28);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    log_format time '$upstream_connect_time:$upstream_header_time:'\n                    '$upstream_response_time';\n\n    upstream u {\n        server 127.0.0.1:8081;\n    }\n\n    upstream u2 {\n        server 127.0.0.1:8081;\n        server 127.0.0.1:8081;\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        add_header X-Connect $upstream_connect_time;\n        add_header X-Header $upstream_header_time;\n        add_header X-Response $upstream_response_time;\n\n        location / {\n            proxy_pass http://127.0.0.1:8081;\n            proxy_read_timeout 2s;\n            proxy_connect_timeout 2s;\n        }\n\n        location /var {\n            proxy_pass http://$arg_b;\n            proxy_read_timeout 2s;\n            proxy_connect_timeout 2s;\n        }\n\n        location /timeout {\n            proxy_pass http://127.0.0.1:8081;\n            proxy_connect_timeout 2s;\n        }\n\n        location /time/ {\n            proxy_pass http://127.0.0.1:8081/;\n            access_log %%TESTDIR%%/time.log time;\n        }\n\n        location /pnu {\n            proxy_pass http://u2/bad;\n        }\n\n        location /vars {\n            proxy_pass http://127.0.0.1:8080/stub;\n\n            add_header X-Proxy-Host $proxy_host;\n            add_header X-Proxy-Port $proxy_port;\n            add_header X-Proxy-Forwarded $proxy_add_x_forwarded_for;\n        }\n\n        location /stub { }\n    }\n}\n\nEOF\n\n$t->write_file('stub', '');\n$t->run_daemon(\\&http_daemon);\n$t->run()->waitforsocket('127.0.0.1:' . port(8081));\n\n###############################################################################\n\nlike(http_get('/'), qr/SEE-THIS/, 'proxy request');\nlike(http_get('/multi'), qr/AND-THIS/, 'proxy request with multiple packets');\n\nunlike(http_head('/'), qr/SEE-THIS/, 'proxy head request');\n\nlike(http_get('/var?b=127.0.0.1:' . port(8081) . '/'), qr/SEE-THIS/,\n\t'proxy with variables');\nlike(http_get('/var?b=u/'), qr/SEE-THIS/, 'proxy with variables to upstream');\n\nlike(http_get('/timeout'), qr/200 OK/, 'proxy connect timeout');\n\nmy $re = qr/(\\d\\.\\d{3})/;\nmy $p0 = port(8080);\nmy ($ct, $ht, $rt, $ct2, $ht2, $rt2, $ct3, $ht3, $rt3);\n\nlike(http_get('/vars'), qr/X-Proxy-Host:\\s127\\.0\\.0\\.1:$p0/, 'proxy_host');\nlike(http_get('/vars'), qr/X-Proxy-Port:\\s$p0/, 'proxy_port');\nlike(http_xff('/vars', '192.0.2.1'), qr/X-Proxy-Forwarded:.*192\\.0\\.2\\.1/,\n\t'proxy_add_x_forwarded_for');\n\n($ct, $ht) = get('/time/header');\ncmp_ok($ct, '<', 1, 'connect time - slow response header');\ncmp_ok($ht, '>=', 1, 'header time - slow response header');\n\n($ct, $ht) = get('/time/body');\ncmp_ok($ct, '<', 1, 'connect time - slow response body');\ncmp_ok($ht, '<', 1, 'header time - slow response body');\n\nmy $s = http_get('/time/header', start => 1);\nselect undef, undef, undef, 0.4;\nclose ($s);\n\n# expect no header time in 1st (bad) upstream, no (yet) response time in 2nd\n\n$re = qr/(\\d\\.\\d{3}|-)/;\n($ct, $ct2, $ht, $ht2, $rt, $rt2) = get('/pnu', many => 1);\n\ncmp_ok($ct, '<', 1, 'connect time - next');\ncmp_ok($ct2, '<', 1, 'connect time - next 2');\n\nis($ht, '-', 'header time - next');\ncmp_ok($ht2, '<', 1, 'header time - next 2');\n\ncmp_ok($rt, '>=', 1, 'response time - next');\nis($rt2, '-', 'response time - next 2');\n\n$t->stop();\n\n($ct, $ht, $rt, $ct2, $ht2, $rt2, $ct3, $ht3, $rt3)\n\t= $t->read_file('time.log') =~ /^$re:$re:$re\\n$re:$re:$re\\n$re:$re:$re$/;\n\ncmp_ok($ct, '<', 1, 'connect time log - slow response header');\ncmp_ok($ct2, '<', 1, 'connect time log - slow response body');\ncmp_ok($ct3, '<', 1, 'connect time log - client close');\n\ncmp_ok($ht, '>=', 1, 'header time log - slow response header');\ncmp_ok($ht2, '<', 1, 'header time log - slow response body');\nis($ht3, '-', 'header time log - client close');\n\ncmp_ok($rt, '>=', 1, 'response time log - slow response header');\ncmp_ok($rt2, '>=', 1, 'response time log - slow response body');\ncmp_ok($rt3, '>', $ct3, 'response time log - client close');\n\n###############################################################################\n\nsub get {\n\tmy ($uri, %extra) = @_;\n\tmy $re = $extra{many} ? qr/$re, $re?/ : $re;\n\tmy $r = http_get($uri);\n\t$r =~ /X-Connect: $re/, $r =~ /X-Header: $re/, $r =~ /X-Response: $re/;\n}\n\nsub http_xff {\n\tmy ($uri, $xff) = @_;\n\treturn http(<<EOF);\nGET $uri HTTP/1.0\nHost: localhost\nX-Forwarded-For: $xff\n\nEOF\n}\n\nsub http_daemon {\n\tmy $once = 1;\n\tmy $server = IO::Socket::INET->new(\n\t\tProto => 'tcp',\n\t\tLocalHost => '127.0.0.1:' . port(8081),\n\t\tListen => 5,\n\t\tReuse => 1\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\tlocal $SIG{PIPE} = 'IGNORE';\n\n\twhile (my $client = $server->accept()) {\n\t\t$client->autoflush(1);\n\n\t\tmy $headers = '';\n\t\tmy $uri = '';\n\n\t\twhile (<$client>) {\n\t\t\t$headers .= $_;\n\t\t\tlast if (/^\\x0d?\\x0a?$/);\n\t\t}\n\n\t\t$uri = $1 if $headers =~ /^\\S+\\s+([^ ]+)\\s+HTTP/i;\n\n\t\tif ($uri eq '/') {\n\t\t\tprint $client <<'EOF';\nHTTP/1.1 200 OK\nConnection: close\n\nEOF\n\t\t\tprint $client \"TEST-OK-IF-YOU-SEE-THIS\"\n\t\t\t\tunless $headers =~ /^HEAD/i;\n\n\t\t} elsif ($uri eq '/multi') {\n\n\t\t\tprint $client <<\"EOF\";\nHTTP/1.1 200 OK\nConnection: close\n\nTEST-OK-IF-YOU-SEE-THIS\nEOF\n\n\t\t\tselect undef, undef, undef, 0.1;\n\t\t\tprint $client 'AND-THIS';\n\n\t\t} elsif ($uri eq '/timeout') {\n\t\t\tsleep 3;\n\n\t\t\tprint $client <<\"EOF\";\nHTTP/1.1 200 OK\nConnection: close\n\nEOF\n\n\t\t} elsif ($uri eq '/bad') {\n\n\t\t\tif ($once) {\n\t\t\t\t$once = 0;\n\t\t\t\tselect undef, undef, undef, 1.1;\n\t\t\t\tnext;\n\t\t\t}\n\n\t\t\tprint $client <<EOF;\nHTTP/1.1 200 OK\nConnection: close\n\nSEE-THIS-AND-THIS\nEOF\n\n\t\t} elsif ($uri eq '/header') {\n\t\t\tselect undef, undef, undef, 1.1;\n\n\t\t\tprint $client <<EOF;\nHTTP/1.1 200 OK\nConnection: close\n\nSEE-THIS-AND-THIS;\nEOF\n\n\t\t} elsif ($uri eq '/body') {\n\n\t\t\tprint $client <<EOF;\nHTTP/1.1 200 OK\nConnection: close\n\nSEE-THIS-\nEOF\n\n\t\t\tselect undef, undef, undef, 1.1;\n\t\t\tprint $client 'AND-THIS';\n\n\t\t} else {\n\n\t\t\tprint $client <<\"EOF\";\nHTTP/1.1 404 Not Found\nConnection: close\n\nOops, '$uri' not found\nEOF\n\t\t}\n\n\t\tclose $client;\n\t}\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_available.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for http proxy module with available bytes counting.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse IO::Select;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx qw/ :DEFAULT http_end /;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy/)->plan(2);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location /buffered {\n            proxy_pass http://127.0.0.1:8081;\n            proxy_buffer_size 512;\n        }\n\n        location /unbuffered {\n            proxy_pass http://127.0.0.1:8082;\n            proxy_buffer_size 512;\n            proxy_buffering off;\n        }\n    }\n}\n\nEOF\n\n$t->run_daemon(\\&http_daemon, port(8081));\n$t->run_daemon(\\&http_daemon, port(8082));\n$t->run();\n\n$t->waitforsocket('127.0.0.1:' . port(8081));\n$t->waitforsocket('127.0.0.1:' . port(8082));\n\n###############################################################################\n\n# ticket #2367: socket leaks with EPOLLRDHUP\n# due to missing rev->ready reset on rev->available == 0\n#\n# to reproduce leaks, the first part of the response should fit proxy buffer\n\nmy $s = http_get('/buffered', start => 1);\nIO::Select->new($s)->can_read(3);\n\n$t->reload();\n\nTODO: {\nlocal $TODO = 'not yet' if $^O eq 'linux' and !$t->has_version('1.23.1');\n\nlike(http_end($s), qr/AND-THIS/, 'zero available - buffered');\n\n}\n\n$s = http_get('/unbuffered', start => 1);\nIO::Select->new($s)->can_read(3);\n\n$t->stop();\n\nlike(http_end($s), qr/AND-THIS/, 'zero available - unbuffered');\n\n$t->todo_alerts() if $^O eq 'linux' and !$t->has_version('1.23.1');\n\n###############################################################################\n\nsub http_daemon {\n\tmy ($port) = @_;\n\n\tmy $server = IO::Socket::INET->new(\n\t\tProto => 'tcp',\n\t\tLocalHost => \"127.0.0.1:$port\",\n\t\tListen => 5,\n\t\tReuse => 1\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\tlocal $SIG{PIPE} = 'IGNORE';\n\n\twhile (my $client = $server->accept()) {\n\t\t$client->autoflush(1);\n\n\t\tmy $headers = '';\n\t\tmy $uri = '';\n\n\t\twhile (<$client>) {\n\t\t\t$headers .= $_;\n\t\t\tlast if (/^\\x0d?\\x0a?$/);\n\t\t}\n\n\t\tnext if $headers eq '';\n\n\t\tmy $r = <<EOF;\nHTTP/1.1 200 OK\nConnection: close\n\nEOF\n\n\t\t$r = $r . 'x' x (512 - length($r));\n\t\tprint $client $r;\n\n\t\tselect undef, undef, undef, 1.1;\n\t\tprint $client 'AND-THIS';\n\t}\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_bind.t",
    "content": "#!/usr/bin/perl\n\n# (C) Andrey Zelenkov\n# (C) Nginx, Inc.\n\n# Tests for http proxy_bind directive.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nplan(skip_all => 'win32') if $^O eq 'MSWin32';\nplan(skip_all => '127.0.0.2 local address required')\n\tunless defined IO::Socket::INET->new( LocalAddr => '127.0.0.2' );\n\nmy $t = Test::Nginx->new()->has(qw/http proxy/)->plan(5)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen          127.0.0.1:8080;\n        server_name     localhost;\n\n        proxy_bind      127.0.0.2;\n\n        location / {\n            proxy_bind  127.0.0.1;\n            proxy_pass  http://127.0.0.1:8081/;\n        }\n\n        location /inherit {\n            proxy_pass  http://127.0.0.1:8081/;\n        }\n\n        location /off {\n            proxy_bind  off;\n            proxy_pass  http://127.0.0.1:8081/;\n        }\n\n        location /var {\n            proxy_bind  $arg_b;\n            proxy_pass  http://127.0.0.1:8081/;\n        }\n\n        location /port {\n            proxy_bind  127.0.0.2:$remote_port;\n            proxy_pass  http://127.0.0.1:8081/;\n            add_header  X-Client-Port $remote_port;\n        }\n    }\n\n    server {\n        listen          127.0.0.1:8081;\n        server_name     localhost;\n\n        location / {\n            add_header   X-IP $remote_addr;\n            add_header   X-Port $remote_port;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('index.html', '');\n$t->run();\n\n###############################################################################\n\nlike(http_get('/'), qr/X-IP: 127.0.0.1/, 'bind');\nlike(http_get('/inherit'), qr/X-IP: 127.0.0.2/, 'bind inherit');\nlike(http_get('/off'), qr/X-IP: 127.0.0.1/, 'bind off');\nlike(http_get('/var?b=127.0.0.2'), qr/X-IP: 127.0.0.2/, 'bind var');\nlike(http_get('/port'), qr/Port: (\\d+)(?!\\d).*Port: \\1/s, 'bind port');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_bind_transparent.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for http proxy_bind transparent.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nplan(skip_all => 'win32') if $^O eq 'MSWin32';\nplan(skip_all => 'must be root') if $> != 0;\nplan(skip_all => '127.0.0.2 local address required')\n\tunless defined IO::Socket::INET->new( LocalAddr => '127.0.0.2' );\n\nmy $t = Test::Nginx->new()->has(qw/http proxy/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\nuser root wheel;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen          127.0.0.1:8080;\n        server_name     localhost;\n\n        location / {\n            proxy_bind  127.0.0.2 transparent;\n            proxy_pass  http://127.0.0.1:8081/;\n        }\n    }\n\n    server {\n        listen          127.0.0.1:8081;\n        server_name     localhost;\n\n        location / {\n            add_header   X-IP $remote_addr always;\n        }\n    }\n}\n\nEOF\n\n$t->run()->plan(1);\n\n###############################################################################\n\nlike(http_get('/'), qr/X-IP: 127.0.0.2/, 'transparent');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_bind_transparent_capability.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for http proxy_bind transparent with Linux CAP_NET_RAW capability.\n# Ensure that such configuration isn't broken under a non-priveleged user.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nplan(skip_all => 'no linux capability') if $^O ne 'linux';\nplan(skip_all => 'must be root') if $> != 0;\nplan(skip_all => '127.0.0.2 local address required')\n\tunless defined IO::Socket::INET->new( LocalAddr => '127.0.0.2' );\n\nmy $t = Test::Nginx->new()->has(qw/http proxy/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen          127.0.0.1:8080;\n        server_name     localhost;\n\n        location / {\n            proxy_bind  127.0.0.2 transparent;\n            proxy_pass  http://127.0.0.1:8081/;\n        }\n    }\n\n    server {\n        listen          127.0.0.1:8081;\n        server_name     localhost;\n\n        location / {\n            add_header   X-IP $remote_addr always;\n        }\n    }\n}\n\nEOF\n\n$t->run()->plan(1);\n\n###############################################################################\n\nlike(http_get('/'), qr/X-IP: 127.0.0.2/, 'transparent');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_cache.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for http proxy cache.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx qw/ :DEFAULT :gzip /;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy cache gzip/)->plan(15)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    proxy_cache_path   %%TESTDIR%%/cache  levels=1:2\n                       keys_zone=NAME:1m;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        gzip on;\n        gzip_min_length 0;\n\n        location / {\n            proxy_pass    http://127.0.0.1:8081;\n\n            proxy_cache   NAME;\n\n            proxy_cache_valid   200 302  2s;\n            proxy_cache_valid   301      1d;\n            proxy_cache_valid   any      1m;\n\n            proxy_cache_min_uses  1;\n\n            proxy_cache_use_stale  error timeout invalid_header http_500\n                                   http_404;\n\n            proxy_no_cache  $arg_e;\n\n            add_header X-Cache-Status $upstream_cache_status;\n        }\n    }\n    server {\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        location / {\n            limit_rate 512;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('t.html', 'SEE-THIS');\n$t->write_file('t2.html', 'SEE-THIS');\n$t->write_file('empty.html', '');\n$t->write_file('big.html', 'x' x 1024);\n\n$t->run();\n\n###############################################################################\n\nlike(http_get('/t.html'), qr/SEE-THIS/, 'proxy request');\n\n$t->write_file('t.html', 'NOOP');\nlike(http_get('/t.html'), qr/SEE-THIS/, 'proxy request cached');\n\nunlike(http_head('/t2.html'), qr/SEE-THIS/, 'head request');\nlike(http_get('/t2.html'), qr/SEE-THIS/, 'get after head');\nunlike(http_head('/t2.html'), qr/SEE-THIS/, 'head after get');\n\nlike(http_head('/empty.html?head'), qr/MISS/, 'empty head first');\nlike(http_head('/empty.html?head'), qr/HIT/, 'empty head second');\n\nlike(http_get_range('/t.html', 'Range: bytes=4-'), qr/^THIS/m, 'cached range');\nlike(http_get_range('/t.html', 'Range: bytes=0-2,4-'), qr/^SEE.*^THIS/ms,\n\t'cached multipart range');\n\nlike(http_get('/empty.html'), qr/MISS/, 'empty get first');\nlike(http_get('/empty.html'), qr/HIT/, 'empty get second');\n\nselect(undef, undef, undef, 3.1);\nunlink $t->testdir() . '/t.html';\nlike(http_gzip_request('/t.html'),\n\tqr/HTTP.*STALE.*1c\\x0d\\x0a.{28}\\x0d\\x0a0\\x0d\\x0a\\x0d\\x0a\\z/s,\n\t'non-empty get stale');\n\nunlink $t->testdir() . '/empty.html';\nlike(http_gzip_request('/empty.html'),\n\tqr/HTTP.*STALE.*14\\x0d\\x0a.{20}\\x0d\\x0a0\\x0d\\x0a\\x0d\\x0a\\z/s,\n\t'empty get stale');\n\n# no client connection close with response on non-cacheable HEAD requests\n# see 545b5e4d83b2 in nginx for detailed explanation\n\nmy $s = http(<<EOF, start => 1);\nHEAD /big.html?e=1 HTTP/1.1\nHost: localhost\n\nEOF\n\nmy $r = http_get('/t.html', socket => $s);\n\nlike($r, qr/Connection: keep-alive/, 'non-cacheable head - keepalive');\nlike($r, qr/SEE-THIS/, 'non-cacheable head - second');\n\n###############################################################################\n\nsub http_get_range {\n\tmy ($url, $extra) = @_;\n\treturn http(<<EOF);\nGET $url HTTP/1.1\nHost: localhost\nConnection: close\n$extra\n\nEOF\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_cache_bypass.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for http proxy cache, proxy_cache_bypass.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy cache rewrite/)->plan(8)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    proxy_cache_path %%TESTDIR%%/cache keys_zone=one:1m;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            proxy_pass http://127.0.0.1:8081;\n\n            proxy_cache one;\n            proxy_cache_key $uri;\n            proxy_cache_bypass $arg_bypass;\n            proxy_cache_valid any 1y;\n\n            proxy_intercept_errors on;\n            error_page 404 = @fallback;\n        }\n\n        location @fallback {\n            return 403;\n        }\n\n        add_header X-Cache-Status $upstream_cache_status;\n    }\n\n    server {\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        location / {\n        }\n    }\n}\n\nEOF\n\n$t->write_file('t', 'SEE-THIS');\n\n$t->run();\n\n###############################################################################\n\nlike(http_get('/t'), qr/SEE-THIS/, 'request');\n\n$t->write_file('t', 'NOOP');\n\nlike(http_get('/t'), qr/SEE-THIS/, 'request cached');\nlike(http_get('/t?bypass=1'), qr/NOOP/, 'cache bypassed');\nlike(http_get('/t'), qr/NOOP/, 'cached after bypass');\n\n# ticket #827, cache item \"error\" field was not cleared\n# on cache bypass\n\nlike(http_get('/t2'), qr/403 Forbidden/, 'intercepted error');\n\n$t->write_file('t2', 'NOOP');\n\nlike(http_get('/t2'), qr/403 Forbidden/, 'error cached');\nlike(http_get('/t2?bypass=1'), qr/NOOP/, 'error cache bypassed');\nlike(http_get('/t2'), qr/NOOP/, 'error cached after bypass');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_cache_chunked.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Test for proxy cache with Transfer-Encoding: chunked.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse Socket qw/ CRLF /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy cache/)->plan(2);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    proxy_cache_path %%TESTDIR%%/cache keys_zone=NAME:1m;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            proxy_pass http://127.0.0.1:8081;\n            proxy_http_version 1.1;\n            proxy_cache NAME;\n            proxy_cache_valid any 1m;\n            add_header X-Status $upstream_cache_status;\n        }\n    }\n}\n\nEOF\n\n$t->run_daemon(\\&http_chunked_daemon);\n$t->run()->waitforsocket('127.0.0.1:' . port(8081));\n\n###############################################################################\n\nlike(http_get(\"/\"), qr/SEE-THIS/s, \"chunked\");\nlike(http_get(\"/\"), qr/SEE-THIS.*HIT/s, \"chunked cached\");\n\n###############################################################################\n\nsub http_chunked_daemon {\n\tmy $server = IO::Socket::INET->new(\n\t\tProto => 'tcp',\n\t\tLocalAddr => '127.0.0.1:' . port(8081),\n\t\tListen => 5,\n\t\tReuse => 1\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\tlocal $SIG{PIPE} = 'IGNORE';\n\n\twhile (my $client = $server->accept()) {\n\t\t$client->autoflush(1);\n\n\t\twhile (<$client>) {\n\t\t\tlast if (/^\\x0d?\\x0a?$/);\n\t\t}\n\n\t\tprint $client <<'EOF';\nHTTP/1.1 200 OK\nX-Test: SEE-THIS\nConnection: close\nTransfer-Encoding: chunked\n\nEOF\n\t\tprint $client \"85\" . CRLF;\n\t\tselect undef, undef, undef, 0.1;\n\t\tprint $client \"FOO\" . (\"0123456789abcdef\" x 8) . CRLF . CRLF;\n\n\t\tprint $client \"0\" . CRLF . CRLF;\n\t\tclose $client;\n\t}\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_cache_control.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for proxy headers Expires / Cache-Control / X-Accel-Expires.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy cache rewrite/)->plan(19);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    proxy_cache_path   %%TESTDIR%%/cache  keys_zone=NAME:1m;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        add_header  X-Cache-Status  $upstream_cache_status;\n\n        location / {\n            proxy_pass  http://127.0.0.1:8081;\n            proxy_cache NAME;\n\n            proxy_cache_background_update on;\n        }\n\n        location /ignore {\n            proxy_pass  http://127.0.0.1:8081;\n            proxy_cache NAME;\n\n            proxy_ignore_headers Cache-Control Expires;\n            proxy_ignore_headers X-Accel-Expires;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        location /expires {\n            add_header Expires \"Thu, 31 Dec 2037 23:55:55 GMT\";\n            return 204;\n        }\n\n        location /cache-control {\n            add_header Cache-Control max-age=60;\n            return 204;\n        }\n\n        location /x-accel-expires {\n            add_header X-Accel-Expires 60;\n            return 204;\n        }\n\n        location /x-accel-expires-at {\n            add_header X-Accel-Expires @60;\n            return 204;\n        }\n\n        location /x-accel-expires-duplicate {\n            add_header X-Accel-Expires 60;\n            add_header X-Accel-Expires 0;\n            return 204;\n        }\n\n        location /ignore {\n            add_header Expires \"Thu, 31 Dec 2037 23:55:55 GMT\";\n            add_header Cache-Control max-age=60;\n            add_header X-Accel-Expires 60;\n            return 204;\n        }\n\n        location /cache-control-before-expires {\n            add_header Cache-Control max-age=60;\n            add_header Expires \"Thu, 01 Jan 1970 00:00:01 GMT\";\n            return 204;\n        }\n\n        location /cache-control-after-expires {\n            add_header Expires \"Thu, 01 Jan 1970 00:00:01 GMT\";\n            add_header Cache-Control max-age=60;\n            return 204;\n        }\n\n        location /cache-control-no-cache-before-expires {\n            add_header Cache-Control no-cache;\n            add_header Expires \"Thu, 31 Dec 2037 23:55:55 GMT\";\n            return 204;\n        }\n\n        location /cache-control-no-cache-after-expires {\n            add_header Expires \"Thu, 31 Dec 2037 23:55:55 GMT\";\n            add_header Cache-Control no-cache;\n            return 204;\n        }\n\n        location /x-accel-expires-before {\n            add_header X-Accel-Expires 60;\n            add_header Expires \"Thu, 01 Jan 1970 00:00:01 GMT\";\n            add_header Cache-Control no-cache;\n            return 204;\n        }\n\n        location /x-accel-expires-after {\n            add_header Expires \"Thu, 01 Jan 1970 00:00:01 GMT\";\n            add_header Cache-Control no-cache;\n            add_header X-Accel-Expires 60;\n            return 204;\n        }\n\n        location /x-accel-expires-0-before {\n            add_header X-Accel-Expires 0;\n            add_header Cache-Control max-age=60;\n            add_header Expires \"Thu, 31 Dec 2037 23:55:55 GMT\";\n            return 204;\n        }\n\n        location /x-accel-expires-0-after {\n            add_header Cache-Control max-age=60;\n            add_header Expires \"Thu, 31 Dec 2037 23:55:55 GMT\";\n            add_header X-Accel-Expires 0;\n            return 204;\n        }\n\n        location /cache-control-no-cache-one {\n            add_header Cache-Control \"no-cache, max-age=60\";\n            return 204;\n        }\n\n        location /cache-control-no-cache-multi {\n            add_header Cache-Control no-cache;\n            add_header Cache-Control max-age=60;\n            return 204;\n        }\n\n        location /extension-before-x-accel-expires {\n            add_header Cache-Control stale-while-revalidate=2145902155;\n            add_header X-Accel-Expires  @1;\n            return 204;\n        }\n\n        location /extension-after-x-accel-expires {\n            add_header X-Accel-Expires @1;\n            add_header Cache-Control stale-while-revalidate=2145902155;\n            return 204;\n        }\n\n        location /set-cookie {\n            add_header Set-Cookie foo;\n            add_header Expires \"Thu, 01 Jan 1970 00:00:01 GMT\";\n            add_header Cache-control max-age=60;\n            return 204;\n        }\n    }\n}\n\nEOF\n\n$t->run();\n\n###############################################################################\n\n# cache headers work\n\nlike(get('/expires'), qr/HIT/, 'expires');\nlike(get('/cache-control'), qr/HIT/, 'cache-control');\nlike(get('/x-accel-expires'), qr/HIT/, 'x-accel-expires');\nlike(get('/x-accel-expires-at'), qr/EXPIRED/, 'x-accel-expires at');\n\nTODO: {\nlocal $TODO = 'not yet' unless $t->has_version('1.23.0');\n\n# the second header to disable cache is duplicate and ignored\n\nlike(get('/x-accel-expires-duplicate'), qr/HIT/, 'x-accel-expires duplicate');\n\n}\n\n# with cache headers ignored, the response will be fresh\n\nlike(get('/ignore'), qr/MISS/, 'cache headers ignored');\n\n# Cache-Control is preferred over Expires\n\nlike(get('/cache-control-before-expires'), qr/HIT/,\n\t'cache-control before expires');\n\nTODO: {\nlocal $TODO = 'not yet' unless $t->has_version('1.23.0');\n\nlike(get('/cache-control-after-expires'), qr/HIT/,\n\t'cache-control after expires');\n\n}\n\nlike(get('/cache-control-no-cache-before-expires'), qr/MISS/,\n\t'cache-control no-cache before expires');\nlike(get('/cache-control-no-cache-after-expires'), qr/MISS/,\n\t'cache-control no-cache after expires');\n\n# X-Accel-Expires is preferred over both Cache-Control and Expires\n\nlike(get('/x-accel-expires-before'), qr/HIT/, 'x-accel-expires before');\n\nTODO: {\nlocal $TODO = 'not yet' unless $t->has_version('1.23.0');\n\nlike(get('/x-accel-expires-after'), qr/HIT/, 'x-accel-expires after');\n\n}\n\nlike(get('/x-accel-expires-0-before'), qr/MISS/, 'x-accel-expires 0 before');\nlike(get('/x-accel-expires-0-after'), qr/MISS/, 'x-accel-expires 0 after');\n\n# \"Cache-Control: no-cache\" disables caching, no matter of \"max-age\"\n\nlike(get('/cache-control-no-cache-one'), qr/MISS/,\n\t'cache-control no-cache');\nlike(get('/cache-control-no-cache-multi'), qr/MISS/,\n\t'cache-control no-cache multi line');\n\n# Cache-Control extensions are preserved with X-Accel-Expires\n\nlike(get('/extension-before-x-accel-expires'),\n\tqr/STALE/, 'cache-control extensions before x-accel-expires');\n\nTODO: {\nlocal $TODO = 'not yet' unless $t->has_version('1.23.0');\n\nlike(get('/extension-after-x-accel-expires'),\n\tqr/STALE/, 'cache-control extensions after x-accel-expires');\n\n}\n\n# Set-Cookie is considered when caching with Cache-Control\n\nlike(get('/set-cookie'), qr/MISS/, 'set-cookie not cached');\n\n###############################################################################\n\nsub get {\n\tmy ($uri) = @_;\n\thttp_get($uri);\n\thttp_get($uri);\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_cache_convert_head.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for http proxy cache with proxy_cache_convert_head directive.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy cache/)->plan(8)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    proxy_cache_path   %%TESTDIR%%/cache  levels=1:2\n                       keys_zone=NAME:1m;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        proxy_cache   NAME;\n\n        proxy_cache_key $request_uri;\n\n        proxy_cache_valid   200 302  2s;\n\n        add_header X-Cache-Status $upstream_cache_status;\n\n        location / {\n            proxy_pass http://127.0.0.1:8081/t.html;\n            proxy_cache_convert_head   off;\n\n            location /inner {\n                proxy_pass http://127.0.0.1:8081/t.html;\n                proxy_cache_convert_head on;\n            }\n        }\n\n        location /on {\n            proxy_pass http://127.0.0.1:8081/t.html;\n            proxy_cache_convert_head on;\n        }\n    }\n    server {\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        location / {\n            add_header X-Method $request_method;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('t.html', 'SEE-THIS');\n$t->run();\n\n###############################################################################\n\nlike(http_get('/'), qr/X-Method: GET/, 'get');\nlike(http_head('/?2'), qr/X-Method: HEAD/, 'head');\nlike(http_head('/?2'), qr/HIT/, 'head cached');\nunlike(http_get('/?2'), qr/SEE-THIS/, 'get after head');\n\nlike(http_get('/on'), qr/X-Method: GET/, 'on - get');\nlike(http_head('/on?2'), qr/X-Method: GET/, 'on - head');\n\nlike(http_get('/inner'), qr/X-Method: GET/, 'inner - get');\nlike(http_head('/inner?2'), qr/X-Method: GET/, 'inner - head');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_cache_error.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for http proxy cache, \"header already sent\" alerts on backend errors,\n# http://mailman.nginx.org/pipermail/nginx-devel/2018-January/010737.html.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy cache/)->plan(1)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    proxy_cache_path   %%TESTDIR%%/cache  levels=1:2\n                       keys_zone=NAME:1m;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            proxy_pass    http://127.0.0.1:8081;\n            proxy_cache   NAME;\n\n            proxy_read_timeout 500ms;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        location / {\n            postpone_output 0;\n            limit_rate 512;\n            expires 1m;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('big.html', 'x' x 1024);\n\n$t->run();\n\n###############################################################################\n\n# make a HEAD request; since cache is enabled, nginx converts HEAD to GET\n# and will set u->pipe->downstream_error to suppress sending the response\n# body to the client\n\nlike(http_head('/big.html'), qr/200 OK/, 'head request');\n\n# once proxy_read_timeout expires, nginx will call\n# ngx_http_finalize_upstream_request() with u->pipe->downstream_error set\n# and rc = NGX_HTTP_BAD_GATEWAY; after revision ad3f342f14ba046c this\n# will result in ngx_http_finalize_request(NGX_HTTP_BAD_GATEWAY),\n# leading to an attempt to return additional error response and\n# the \"header already sent\" alert; fixed in 93abb5a855d6\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_cache_lock.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for http proxy cache lock.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx qw/ :DEFAULT http_end /;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy cache/)->plan(17)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    proxy_cache_path   %%TESTDIR%%/cache  levels=1:2\n                       keys_zone=NAME:1m;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            proxy_pass    http://127.0.0.1:8081;\n            proxy_cache   NAME;\n\n            proxy_cache_lock on;\n        }\n\n        location /timeout {\n            proxy_pass    http://127.0.0.1:8081;\n            proxy_cache   NAME;\n\n            proxy_cache_lock on;\n            proxy_cache_lock_timeout 200ms;\n        }\n\n        location /nolock {\n            proxy_pass    http://127.0.0.1:8081;\n            proxy_cache   NAME;\n        }\n    }\n}\n\nEOF\n\n$t->run_daemon(\\&http_fake_daemon);\n\n$t->run();\n\n$t->waitforsocket('127.0.0.1:' . port(8081));\n\n###############################################################################\n\n# sequential requests\n\nfor my $i (1 .. 5) {\n\tlike(http_get('/seq'), qr/request 1/, 'sequential request ' . $i);\n}\n\n# parallel requests\n\nmy @sockets;\n\nfor my $i (1 .. 5) {\n\t$sockets[$i] = http_get('/par1', start => 1);\n}\n\nfor my $i (1 .. 5) {\n\tlike(http_end($sockets[$i]), qr/request 1/, 'parallel request ' . $i);\n}\n\nlike(http_get('/par1'), qr/request 1/, 'first request cached');\n\n# since 1.7.8, parallel requests with cache lock timeout expired are not cached\n\nfor my $i (1 .. 3) {\n\t$sockets[$i] = http_get('/timeout', start => 1);\n}\n\nlike(http_end($sockets[1]), qr/request 1/, 'lock timeout - first');\n\nmy $rest = http_end($sockets[2]);\n$rest .= http_end($sockets[3]);\n\nlike($rest, qr/request (2.*request 3|3.*request 2)/s, 'lock timeout - rest');\nlike(http_get('/timeout'), qr/request 1/, 'lock timeout - first only cached');\n\n# no lock\n\nfor my $i (1 .. 3) {\n\t$sockets[$i] = http_get('/nolock', start => 1);\n}\n\n$rest = join '', map { http_end($sockets[$_]) } (1 .. 3);\n\nlike($rest, qr/request 1/, 'nolock - first');\nlike($rest, qr/request 3/, 'nolock - last');\nlike(http_get('/nolock'), qr/request 3/, 'nolock - last cached');\n\n###############################################################################\n\nsub http_fake_daemon {\n\tmy $server = IO::Socket::INET->new(\n\t\tProto => 'tcp',\n\t\tLocalAddr => '127.0.0.1:' . port(8081),\n\t\tListen => 5,\n\t\tReuse => 1\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\tmy $num = 0;\n\tmy $uri = '';\n\n\twhile (my $client = $server->accept()) {\n\t\t$client->autoflush(1);\n\n\t\twhile (<$client>) {\n\t\t\tif (/GET (.*) HTTP/ && $1 ne $uri) {\n\t\t\t\t$uri = $1;\n\t\t\t\t$num = 0;\n\t\t\t}\n\n\t\t\t$uri = $1 if /GET (.*) HTTP/;\n\t\t\tlast if /^\\x0d?\\x0a?$/;\n\t\t}\n\n\t\tnext unless $uri;\n\n\t\tselect(undef, undef, undef, 1.1);\n\n\t\t$num++;\n\t\tprint $client <<\"EOF\";\nHTTP/1.1 200 OK\nCache-Control: max-age=300\nConnection: close\n\nrequest $num\nEOF\n\t}\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_cache_lock_age.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for http proxy cache lock aged.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse IO::Select;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx qw/ :DEFAULT http_end /;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy cache/)->plan(4)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\nworker_processes 1;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    proxy_cache_path   %%TESTDIR%%/cache  levels=1:2\n                       keys_zone=NAME:1m;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            proxy_pass    http://127.0.0.1:8081;\n            proxy_cache   NAME;\n\n            proxy_cache_lock on;\n            proxy_cache_lock_age 100ms;\n        }\n    }\n}\n\nEOF\n\n$t->run_daemon(\\&http_daemon, port(8081));\n$t->run()->waitforsocket('127.0.0.1:' . port(8081));\n\n###############################################################################\n\nmy $s = http_get('/', start => 1);\n\nlike(http_get('/'), qr/request 2/, 'request');\nlike(http_get('/'), qr/request 2/, 'request cached');\n\nhttp_get('/close');\n\nlike(http_end($s), qr/request 1/, 'request aged');\nlike(http_get('/'), qr/request 1/, 'request aged cached');\n\n###############################################################################\n\nsub http_daemon {\n\tmy (@ports) = @_;\n\tmy @socks;\n\n\tfor my $port (@ports) {\n\t\tmy $server = IO::Socket::INET->new(\n\t\t\tProto => 'tcp',\n\t\t\tLocalHost => \"127.0.0.1:$port\",\n\t\t\tListen => 5,\n\t\t\tReuse => 1\n\t\t)\n\t\t\tor die \"Can't create listening socket: $!\\n\";\n\t\tpush @socks, $server;\n\t}\n\n\tmy $sel = IO::Select->new(@socks);\n\tmy $num = 0;\n\tmy $s;\n\n\tlocal $SIG{PIPE} = 'IGNORE';\n\n\twhile (my @ready = $sel->can_read) {\n\t\tforeach my $fh (@ready) {\n\t\t\tif (grep $_ == $fh, @socks) {\n\t\t\t\tmy $new = $fh->accept;\n\t\t\t\t$new->autoflush(1);\n\t\t\t\t$sel->add($new);\n\n\t\t\t} elsif (process_socket($fh, \\$num, \\$s)) {\n\t\t\t\t$sel->remove($fh);\n\t\t\t\t$fh->close;\n\t\t\t}\n\t\t}\n\t}\n}\n\n# Returns true to close connection\n\nsub process_socket {\n\tmy ($client, $num, $s) = @_;\n\n\tmy $headers = '';\n\tmy $uri = '';\n\n\twhile (<$client>) {\n\t\t$headers .= $_;\n\t\tlast if (/^\\x0d?\\x0a?$/);\n\t}\n\treturn 1 if $headers eq '';\n\n\t$uri = $1 if $headers =~ /^\\S+\\s+([^ ]+)\\s+HTTP/i;\n\treturn 1 if $uri eq '';\n\n\t# finish a previously saved socket\n\tclose $$s if $uri eq '/close';\n\n\t$$num++;\n\n\tprint $client <<EOF;\nHTTP/1.1 200 OK\nCache-Control: max-age=300\nConnection: close\n\nrequest $$num\nEOF\n\n\t# save socket and wait\n\tif ($$num == 1) {\n\t\t$$s = $client;\n\t\treturn 0;\n\t}\n\n\treturn 1;\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_cache_lock_ssi.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for http proxy cache lock with subrequests.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx qw/ :DEFAULT http_end /;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy cache ssi/)\n\t->write_file_expand('nginx.conf', <<'EOF')->plan(2);\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    proxy_cache_path   %%TESTDIR%%/cache  levels=1:2\n                       keys_zone=NAME:1m;\n\n    limit_req_zone $binary_remote_addr zone=one:1m rate=1r/m;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            proxy_pass    http://127.0.0.1:8081;\n            proxy_cache   NAME;\n\n            proxy_cache_lock on;\n            proxy_cache_lock_timeout 100ms;\n\n            proxy_read_timeout 3s;\n        }\n\n        location = /ssi.html {\n            ssi on;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n        limit_req zone=one burst=5;\n    }\n\n}\n\nEOF\n\n$t->write_file('ssi.html',\n\t'<!--#include virtual=\"/active\" -->' .\n\t'<!--#include virtual=\"/locked\" -->' .\n\t'end'\n);\n\n$t->write_file('active', 'active');\n$t->write_file('locked', 'locked');\n\n$t->run();\n\n###############################################################################\n\n# problem: if proxy cache lock wakeup happens in an inactive\n# subrequest, just a connection write event may not trigger any\n# further work\n\n# main request -> subrequest /active (waiting for a backend),\n#              -> subrequest /locked (locked by another request)\n\n# this doesn't result in an infinite timeout as second subrequest\n# is woken up by the postpone filter once first subrequest completes,\n# but this is suboptimal behaviour\n\nhttp_get('/charge');\nmy $start = time();\n\nmy $s = http_get('/locked', start => 1);\nselect undef, undef, undef, 0.2;\n\nlike(http_get('/ssi.html'), qr/end/, 'cache lock ssi');\nhttp_end($s);\ncmp_ok(time() - $start, '<=', 5, 'parallel execution after lock timeout');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_cache_manager.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for http proxy cache, manager parameters.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nplan(skip_all => 'long test') unless $ENV{TEST_NGINX_UNSAFE};\n\nplan(skip_all => 'page size is not appropriate') unless\n        POSIX::sysconf(&POSIX::_SC_PAGESIZE) == 4096;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy cache/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    proxy_cache_path   %%TESTDIR%%/cache  max_size=0  keys_zone=NAME:1m\n                       manager_sleep=5  manager_files=2  manager_threshold=10;\n\n    proxy_cache_path   %%TESTDIR%%/water  keys_zone=NAM2:16k\n                       manager_sleep=5;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            proxy_pass    http://127.0.0.1:8081;\n            proxy_cache   NAME;\n\n            proxy_cache_valid   any   1m;\n        }\n\n        location /water/ {\n            proxy_pass    http://127.0.0.1:8081/t.html;\n            proxy_cache   NAM2;\n\n            proxy_cache_valid   any   1m;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        location / { }\n    }\n}\n\nEOF\n\n$t->write_file('t.html', 'SEE-THIS');\n$t->run()->plan(3);\n\n###############################################################################\n\nmy $d = $t->testdir();\n\n# wait for cache manager start\n\nsleep 1;\n\nhttp_get(\"/t.html?$_\") for (1 .. 5);\n\n# pretend we could not fit into zone\n\nhttp_get(\"/water/?$_\") for (1 .. 100);\n\nmy $n = files(\"$d/water\");\n\n# wait for cache manager process\n\nsleep 10;\n\ncmp_ok(files(\"$d/water\"), '<', $n, 'manager watermark');\n\nis(files(\"$d/cache\"), 3, 'manager files');\n\nsleep 5;\n\nis(files(\"$d/cache\"), 1, 'manager sleep');\n\n###############################################################################\n\nsub files {\n\tmy ($path) = @_;\n\tmy $dh;\n\n\topendir($dh, $path);\n\treturn scalar grep { ! /^\\./ } readdir($dh);\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_cache_max_range_offset.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for http proxy cache, proxy_cache_max_range_offset directive.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy cache/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    proxy_cache_path   %%TESTDIR%%/cache  levels=1:2\n                       keys_zone=NAME:1m;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            proxy_pass    http://127.0.0.1:8081/;\n            proxy_cache   NAME;\n            proxy_cache_valid 200 1m;\n            proxy_cache_max_range_offset 2;\n        }\n\n        location /zero/ {\n            proxy_pass    http://127.0.0.1:8081/;\n            proxy_cache   NAME;\n            proxy_cache_valid 200 1m;\n            proxy_cache_max_range_offset 0;\n        }\n\n        location /min_uses/ {\n            proxy_pass    http://127.0.0.1:8081/;\n            proxy_cache   NAME;\n            proxy_cache_valid 200 1m;\n            proxy_cache_max_range_offset 2;\n            proxy_cache_min_uses 2;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        location / {\n            add_header X-Range $http_range;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('t.html', 'SEE-THIS');\n$t->run()->plan(8);\n\n###############################################################################\n\nunlike(get('/t.html?1', 'bytes=1-'), qr/X-Range/, 'range - below');\nlike(get('/t.html?2', 'bytes=3-'), qr/X-Range/, 'range - above');\nlike(get('/t.html?3', 'bytes=-1'), qr/X-Range/, 'range - last');\n\nTODO: {\nlocal $TODO = 'not yet';\n\nlike(get('/t.html?4', 'bytes=1-1,3-'), qr/X-Range/, 'range - multipart above');\n\n}\n\nlike(get('/zero/t.html?5', 'bytes=0-0'), qr/X-Range/, 'always non-cacheable');\nlike(get('/min_uses/t.html?6', 'bytes=1-'), qr/X-Range/, 'below min_uses');\n\n# no range in client request\n\nlike(http_get('/t.html'), qr/SEE-THIS/, 'no range');\n\n$t->write_file('t.html', 'NOOP');\nlike(http_get('/t.html'), qr/SEE-THIS/, 'no range - cached');\n\n###############################################################################\n\nsub get {\n\tmy ($url, $extra) = @_;\n\treturn http(<<EOF);\nGET $url HTTP/1.1\nHost: localhost\nConnection: close\nRange: $extra\n\nEOF\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_cache_min_free.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for http proxy cache, min_free parameter.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy cache/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    proxy_cache_path   %%TESTDIR%%/cache  levels=1:2 min_free=4k\n                       keys_zone=NAME:1m;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            proxy_pass    http://127.0.0.1:8081;\n\n            proxy_cache   NAME;\n\n            proxy_cache_valid   any      1m;\n\n            add_header X-Cache-Status $upstream_cache_status;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        location / { }\n    }\n}\n\nEOF\n\n$t->write_file('t.html', 'SEE-THIS');\n$t->run()->plan(2);\n\n###############################################################################\n\nlike(http_get('/t.html'), qr/SEE-THIS/, 'proxy request');\n\n$t->write_file('t.html', 'NOOP');\nlike(http_get('/t.html'), qr/SEE-THIS/, 'proxy request cached');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_cache_path.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for http proxy cache with use_temp_path parameter.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy cache/)->plan(6)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    proxy_cache_path   %%TESTDIR%%/cache1\n                       keys_zone=ON:1m      use_temp_path=on;\n    proxy_cache_path   %%TESTDIR%%/cache2\n                       keys_zone=OFF:1m     use_temp_path=off;\n    proxy_cache_path   %%TESTDIR%%/cache4   levels=1:2\n                       keys_zone=LEVELS:1m  use_temp_path=off;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            proxy_pass    http://127.0.0.1:8081;\n\n            proxy_cache   $arg_c;\n\n            proxy_cache_valid   any      1m;\n\n            add_header X-Cache-Status $upstream_cache_status;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        location / {\n        }\n    }\n}\n\nEOF\n\n$t->write_file('t', 'SEE-THIS');\n\n$t->run();\n\n###############################################################################\n\nlike(http_get('/t?c=ON'), qr/MISS.*SEE-THIS/ms, 'temp path');\nlike(http_get('/t?c=OFF'), qr/MISS.*SEE-THIS/ms, 'temp path off');\nlike(http_get('/t?c=LEVELS'), qr/MISS.*SEE-THIS/ms, 'temp path levels');\n\n$t->write_file('t', 'SEE-THAT');\n\nlike(http_get('/t?c=ON'), qr/HIT.*SEE-THIS/ms, 'temp path cached');\nlike(http_get('/t?c=OFF'), qr/HIT.*SEE-THIS/ms, 'temp path cached off');\nlike(http_get('/t?c=LEVELS'), qr/HIT.*SEE-THIS/ms, 'temp path cached levels');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_cache_range.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for http proxy cache and range filter.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy cache/)->plan(7)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    proxy_cache_path   %%TESTDIR%%/cache  levels=1:2\n                       keys_zone=NAME:1m;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            proxy_pass    http://127.0.0.1:8081;\n            proxy_cache   NAME;\n            proxy_cache_valid 200 1m;\n        }\n\n        location /min_uses {\n            proxy_pass    http://127.0.0.1:8081/;\n            proxy_cache   NAME;\n            proxy_cache_valid 200 1m;\n            proxy_cache_min_uses 2;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        location / {\n        }\n\n        location /tbig.html {\n            limit_rate 50k;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('t.html', 'SEE-THIS');\n\n# should not fit in a single proxy buffer\n\n$t->write_file('tbig.html',\n\tjoin('', map { sprintf \"XX%06dXX\", $_ } (1 .. 7000)));\n\n$t->run();\n\n###############################################################################\n\nlike(http_get_range('/t.html?1', 'Range: bytes=4-'), qr/^THIS/m,\n\t'range on first request');\n\n{\nlocal $TODO = 'not yet';\n\nlike(http_get_range('/t.html?2', 'Range: bytes=0-2,4-'), qr/^SEE.*^THIS/ms,\n\t'multipart range on first request');\n}\n\nlike(http_get_range('/t.html?1', 'Range: bytes=4-'), qr/^THIS/m,\n\t'cached range');\nlike(http_get_range('/t.html?1', 'Range: bytes=0-2,4-'), qr/^SEE.*^THIS/ms,\n\t'cached multipart range');\n\nlike(http_get_range('/min_uses/t.html?3', 'Range: bytes=4-'),\n\tqr/^THIS/m, 'range below min_uses');\n\nlike(http_get_range('/min_uses/t.html?4', 'Range: bytes=0-2,4-'),\n\tqr/^SEE.*^THIS/ms, 'multipart range below min_uses');\n\nlike(http_get_range('/tbig.html', 'Range: bytes=0-19'),\n\tqr/^XX000001XXXX000002XX$/ms, 'range of response received in parts');\n\n###############################################################################\n\nsub http_get_range {\n\tmy ($url, $extra) = @_;\n\treturn http(<<EOF);\nGET $url HTTP/1.1\nHost: localhost\nConnection: close\n$extra\n\nEOF\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_cache_revalidate.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for http proxy cache revalidation with conditional requests.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy cache rewrite/)->plan(23)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    proxy_cache_path   %%TESTDIR%%/cache  levels=1:2\n                       keys_zone=one:1m;\n\n    proxy_cache_revalidate on;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            proxy_pass    http://127.0.0.1:8081;\n            proxy_cache   one;\n\n            proxy_cache_valid  200 404  2s;\n\n            add_header X-Cache-Status $upstream_cache_status;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        location / { }\n        location /etag/ {\n            proxy_pass http://127.0.0.1:8081/;\n            proxy_hide_header Last-Modified;\n        }\n        location /201 {\n            add_header Last-Modified \"Mon, 02 Mar 2015 17:20:58 GMT\";\n            add_header Cache-Control \"max-age=1\";\n            add_header X-If-Modified-Since $http_if_modified_since;\n            return 201;\n        }\n    }\n}\n\nEOF\n\nmy $d = $t->testdir();\n\n$t->write_file('t', 'SEE-THIS');\n$t->write_file('t2', 'SEE-THIS');\n$t->write_file('t3', 'SEE-THIS');\n\n$t->run();\n\n###############################################################################\n\n# request documents and make sure they are cached\n\nlike(http_get('/t'), qr/X-Cache-Status: MISS.*SEE/ms, 'request');\nlike(http_get('/t'), qr/X-Cache-Status: HIT.*SEE/ms, 'request cached');\n\nlike(http_get('/t2'), qr/X-Cache-Status: MISS.*SEE/ms, '2nd request');\nlike(http_get('/t2'), qr/X-Cache-Status: HIT.*SEE/ms, '2nd request cached');\n\nlike(http_get('/etag/t'), qr/X-Cache-Status: MISS.*SEE/ms, 'etag');\nlike(http_get('/etag/t'), qr/X-Cache-Status: HIT.*SEE/ms, 'etag cached');\n\nlike(http_get('/etag/t2'), qr/X-Cache-Status: MISS.*SEE/ms, 'etag2');\nlike(http_get('/etag/t2'), qr/X-Cache-Status: HIT.*SEE/ms, 'etag2 cached');\n\nlike(http_get('/201'), qr/X-Cache-Status: MISS/, 'other status');\nlike(http_get('/201'), qr/X-Cache-Status: HIT/, 'other status cached');\n\nlike(http_get('/t3'), qr/SEE/, 'cache before 404');\n\n# wait for a while for cached responses to expire\n\nselect undef, undef, undef, 3.5;\n\n# 1st document isn't modified, and should be revalidated on first request\n# (a 304 status code will appear in backend's logs), then cached again\n\nlike(http_get('/t'), qr/X-Cache-Status: REVALIDATED.*SEE/ms, 'revalidated');\nlike(http_get('/t'), qr/X-Cache-Status: HIT.*SEE/ms, 'cached again');\n\nrename(\"$d/t3\", \"$d/t3_moved\");\n\nlike(http_get('/t3'), qr/ 404 /, 'cache 404 response');\n\nselect undef, undef, undef, 0.1;\nlike($t->read_file('access.log'), qr/ 304 /, 'not modified');\n\n# 2nd document is recreated with a new content\n\n$t->write_file('t2', 'NEW');\nlike(http_get('/t2'), qr/X-Cache-Status: EXPIRED.*NEW/ms, 'revalidate failed');\nlike(http_get('/t2'), qr/X-Cache-Status: HIT.*NEW/ms, 'new response cached');\n\n# the same for etag:\n# 1st document isn't modified\n# 2nd document is recreated\n\nlike(http_get('/etag/t'), qr/X-Cache-Status: REVALIDATED.*SEE/ms,\n\t'etag revalidated');\nlike(http_get('/etag/t'), qr/X-Cache-Status: HIT.*SEE/ms,\n\t'etag cached again');\nlike(http_get('/etag/t2'), qr/X-Cache-Status: EXPIRED.*NEW/ms,\n\t'etag2 revalidate failed');\nlike(http_get('/etag/t2'), qr/X-Cache-Status: HIT.*NEW/ms,\n\t'etag2 new response cached');\n\n# check that conditional requests are only used for 200/206 responses\n\n# d0ce06cb9be1 in 1.7.3 changed to ignore header filter's work to strip\n# the Last-Modified header when storing non-200/206 in cache;\n# 1573fc7875fa in 1.7.9 effectively turned it back.\n\nunlike(http_get('/201'), qr/X-If-Modified/, 'other status no revalidation');\n\n# wait for a while for a cached 404 response to expire\n\nselect undef, undef, undef, 3.5;\n\n# check that conditional requests are not used to revalidate 404 response\n\n# before fd283aa92e04 introduced in 1.7.7, this test passed by chance because\n# of the If-Modified-Since header that was sent with Epoch in revalidation\n# of responses cached without the Last-Modified header;\n# fd283aa92e04 leaved (an legitimate) successful revalidation of 404 by ETag\n# (introduced by 44b9ab7752e3 in 1.7.3), which caused the test to fail;\n# 1573fc7875fa in 1.7.9 changed to not revalidate non-200/206 responses but\n# leaked Last-Modified and ETag into 404 inherited from stale 200/206 response;\n# 174512857ccf in 1.7.11 fixed the leak and allowed the test to pass.\n\nrename(\"$d/t3_moved\", \"$d/t3\");\n\nlike(http_get('/t3'), qr/SEE/, 'no 404 revalidation after stale 200');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_cache_use_stale.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for http proxy cache, proxy_cache_use_stale.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse IO::Select;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx qw/ :DEFAULT http_end /;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy cache rewrite limit_req ssi/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    proxy_cache_path   %%TESTDIR%%/cache  levels=1:2  keys_zone=NAME:1m;\n\n    limit_req_zone  $binary_remote_addr  zone=one:1m  rate=10r/m;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location /ssi.html {\n            ssi on;\n            sendfile_max_chunk  4k;\n        }\n\n        location /escape {\n            proxy_pass    http://127.0.0.1:8081;\n            proxy_cache   NAME;\n            proxy_cache_background_update  on;\n            add_header X-Cache-Status $upstream_cache_status;\n        }\n\n        location / {\n            proxy_pass    http://127.0.0.1:8081;\n\n            proxy_cache   NAME;\n\n            proxy_cache_key  $uri;\n\n            proxy_cache_revalidate  on;\n\n            proxy_cache_background_update  on;\n\n            add_header X-Cache-Status $upstream_cache_status;\n\n            location /t4.html {\n                proxy_pass    http://127.0.0.1:8081/t.html;\n\n                proxy_cache_revalidate  off;\n            }\n\n            location /t5.html {\n                proxy_pass    http://127.0.0.1:8081/t.html;\n\n                proxy_cache_background_update  off;\n            }\n\n            location ~ /(reg)(?P<name>exp).html {\n                proxy_pass    http://127.0.0.1:8081/$1$name.html;\n\n                proxy_cache_background_update  on;\n            }\n\n            location /updating/ {\n                proxy_pass    http://127.0.0.1:8081/;\n\n                proxy_cache_use_stale  updating;\n            }\n\n            location /t7.html {\n                proxy_pass    http://127.0.0.1:8081;\n\n                sendfile_max_chunk  4k;\n            }\n\n            location /t8.html {\n                proxy_pass    http://127.0.0.1:8081/t.html;\n\n                proxy_cache_valid  1s;\n            }\n\n            if ($arg_if) {\n                # nothing\n            }\n        }\n    }\n    server {\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        add_header Cache-Control $http_x_cache_control;\n\n        if ($arg_lim) {\n            set $limit_rate 1k;\n        }\n\n        if ($arg_e) {\n            return 500;\n        }\n\n        location / { }\n\n        location /t6.html {\n            limit_req zone=one burst=2;\n        }\n\n        location /t9.html {\n            add_header Cache-Control \"max-age=1, stale-while-revalidate=10\";\n        }\n    }\n}\n\nEOF\n\n$t->write_file('t.html', 'SEE-THIS');\n$t->write_file('tt.html', 'SEE-THIS');\n$t->write_file('t2.html', 'SEE-THIS');\n$t->write_file('t3.html', 'SEE-THIS');\n$t->write_file('t6.html', 'SEE-THIS');\n$t->write_file('t7.html', 'SEE-THIS' x 1024);\n$t->write_file('t9.html', 'SEE-THIS' x 1024);\n$t->write_file('ssi.html', 'xxx <!--#include virtual=\"/t9.html\" --> xxx');\n$t->write_file('escape.html', 'SEE-THIS');\n$t->write_file('regexp.html', 'SEE-THIS');\n\n$t->run()->plan(34);\n\n###############################################################################\n\nlike(get('/t.html', 'max-age=1, stale-if-error=5'), qr/MISS/, 'stale-if-error');\nlike(http_get('/t.html?e=1'), qr/HIT/, 's-i-e - cached');\n\nlike(get('/t2.html', 'max-age=1, stale-while-revalidate=10'), qr/MISS/,\n\t'stale-while-revalidate');\nlike(http_get('/t2.html'), qr/HIT/, 's-w-r - cached');\n\nget('/tt.html', 'max-age=1, stale-if-error=3');\nget('/t3.html', 'max-age=1, stale-while-revalidate=2');\nget('/t4.html', 'max-age=1, stale-while-revalidate=3');\nget('/t5.html', 'max-age=1, stale-while-revalidate=3');\nget('/t6.html', 'max-age=1, stale-while-revalidate=4');\nget('/t7.html', 'max-age=1, stale-while-revalidate=10');\nhttp_get('/ssi.html');\nget('/updating/t.html', 'max-age=1');\nget('/updating/t2.html', 'max-age=1, stale-while-revalidate=2');\nget('/updating/tt.html', 'max-age=1, stale-if-error=5');\nget('/t8.html', 'stale-while-revalidate=10');\nget('/escape.htm%6C', 'max-age=1, stale-while-revalidate=10');\nget('/regexp.html', 'max-age=1, stale-while-revalidate=10');\n\nsleep 2;\n\n# stale 5xx response is ignored since 1.19.3,\n# \"proxy_cache_use_stale updating;\" allows to get it still\n\nlike(http_get('/t.html?e=1'), qr/ 500 /, 's-i-e - stale 5xx ignore');\nlike(http_get('/tt.html?e=1'), qr/ 500 /, 's-i-e - stale 5xx ignore 2');\nlike(http_get('/updating/tt.html'), qr/STALE/, 's-i-e - stale 5xx updating');\nlike(http_get('/t.html'), qr/REVALIDATED/, 's-i-e - revalidated');\n\nlike(http_get('/t2.html?e=1'), qr/STALE/, 's-w-r - revalidate error');\nlike(http_get('/t2.html'), qr/STALE/, 's-w-r - stale while revalidate');\nlike(http_get('/t2.html'), qr/HIT/, 's-w-r - revalidated');\n\nlike(get('/t4.html', 'max-age=1, stale-while-revalidate=2'), qr/STALE/,\n\t's-w-r - unconditional revalidate');\nlike(http_get('/t4.html'), qr/HIT/, 's-w-r - unconditional revalidated');\n\nlike(http_get('/t5.html?e=1'), qr/ 500 /,\n\t's-w-r - foreground revalidate error');\nlike(http_get('/t5.html'), qr/REVALIDATED/, 's-w-r - foreground revalidated');\n\n# proxy_pass to regular expression with named and positional captures\n\nlike(http_get('/regexp.html'), qr/STALE/, 's-w-r - regexp background update');\nlike(http_get('/regexp.html'), qr/HIT/, 's-w-r - regexp revalidated');\n\n# UPDATING while s-w-r\n\n$t->write_file('t6.html', 'SEE-THAT');\n\nmy $s = get('/t6.html', 'max-age=1, stale-while-revalidate=2', start => 1);\nselect undef, undef, undef, 0.2;\nlike(http_get('/t6.html'), qr/UPDATING.*SEE-THIS/s, 's-w-r - updating');\nlike(http_end($s), qr/STALE.*SEE-THIS/s, 's-w-r - updating stale');\nlike(http_get('/t6.html'), qr/HIT.*SEE-THAT/s, 's-w-r - updating revalidated');\n\n# stale-while-revalidate with proxy_cache_use_stale updating\n\nlike(http_get('/updating/t.html'), qr/STALE/,\n\t's-w-r - use_stale updating stale');\nlike(http_get('/updating/t.html'), qr/HIT/,\n\t's-w-r - use_stale updating revalidated');\n\n# stale-while-revalidate with proxy_cache_valid\n\nlike(http_get('/t8.html'), qr/STALE/, 's-w-r - proxy_cache_valid revalidate');\nlike(http_get('/t8.html'), qr/HIT/, 's-w-r - proxy_cache_valid revalidated');\n\nsleep 2;\n\nlike(http_get('/t2.html?e=1'), qr/STALE/, 's-w-r - stale after revalidate');\nlike(http_get('/t3.html?e=1'), qr/ 500 /, 's-w-r - ceased');\nlike(http_get('/tt.html?e=1'), qr/ 500 /, 's-i-e - ceased');\nlike(http_get('/updating/t2.html'), qr/STALE/,\n\t's-w-r - overriden with use_stale updating');\n\n# stale response not blocked by background update.\n# before 1.13.1, if stale response was not sent in one pass, its remaining\n# part was blocked and not sent until background update has been finished\n\n$t->write_file('t7.html', 'SEE-THAT' x 256);\n\nmy $r = read_all(get('/t7.html?lim=1', 'max-age=1', start => 1));\nlike($r, qr/STALE.*^(SEE-THIS){1024}$/ms, 's-w-r - stale response not blocked');\n\n$t->write_file('t9.html', 'SEE-THAT' x 256);\n$t->write_file('ssi.html', 'xxx <!--#include virtual=\"/t9.html?lim=1\" --> xxx');\n\n$r = read_all(http_get('/ssi.html', start => 1));\nlike($r, qr/^xxx (SEE-THIS){1024} xxx$/ms, 's-w-r - not blocked in subrequest');\n\n# \"aio_write\" is used to produce \"open socket ... left in connection\" alerts.\n\n$t->todo_alerts() if $t->read_file('nginx.conf') =~ /aio_write on/\n        and $t->read_file('nginx.conf') =~ /aio threads/ and $^O eq 'freebsd';\n\n# due to the missing content_handler inheritance in a cloned subrequest,\n# this used to access a static file in the update request\n\nlike(http_get('/t2.html?if=1'), qr/STALE/, 'background update in if');\nlike(http_get('/t2.html?if=1'), qr/HIT/, 'background update in if - updated');\n\n# ticket #1430, uri escaping in cloned subrequests\n\n$t->write_file('escape.html', 'SEE-THAT');\n\nget('/escape.htm%6C', 'max-age=1');\n\nlike(http_get('/escape.htm%6C'), qr/HIT/, 'escaped after escaped');\nlike(http_get('/escape.html'), qr/MISS/, 'unescaped after escaped');\n\n###############################################################################\n\nsub get {\n\tmy ($url, $extra, %extra) = @_;\n\treturn http(<<EOF, %extra);\nGET $url HTTP/1.1\nHost: localhost\nConnection: close\nX-Cache-Control: $extra\n\nEOF\n}\n\n# background update is known to postpone closing connection with client\n\nsub read_all {\n\tmy ($s) = @_;\n\tmy $r = '';\n\twhile (IO::Select->new($s)->can_read(1)) {\n\t\t$s->sysread(my $buf, 8192) or last;\n\t\tlog_in($buf);\n\t\t$r .= $buf;\n\t}\n\treturn $r;\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_cache_valid.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for http proxy cache, the proxy_cache_valid directive\n# used with the caching parameters set in the response header.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy cache rewrite/)->plan(12)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    proxy_cache_path   %%TESTDIR%%/cache  levels=1:2\n                       keys_zone=NAME:1m;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            proxy_pass    http://127.0.0.1:8081;\n            proxy_cache   NAME;\n\n            proxy_cache_valid  200 401  1m;\n\n            proxy_intercept_errors on;\n            error_page 404 401 = @fallback;\n\n            add_header X-Cache-Status $upstream_cache_status;\n        }\n\n        location @fallback {\n            return 403;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        location / {\n            add_header Cache-Control $http_x_cc always;\n            error_page 403 = /index-no-cache;\n        }\n\n        location /index-no-cache {\n            add_header Cache-Control no-cache always;\n            return 401;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('t.html', 'SEE-THIS');\n$t->run();\n\n###############################################################################\n\nlike(get('/t.html?1', 'X-CC: max-age=1'), qr/MISS/, 'max-age');\nlike(get('/t.html?2', 'X-CC: max-age=1, s-maxage=10'), qr/MISS/, 's-maxage');\nlike(http_get('/t.html?3'), qr/MISS/, 'proxy_cache_valid');\n\n$t->write_file('t.html', 'NOOP');\n\nlike(http_get('/t.html?1'), qr/HIT/, 'max-age cached');\nlike(http_get('/t.html?2'), qr/HIT/, 's-maxage cached');\nlike(http_get('/t.html?3'), qr/HIT/, 'proxy_cache_valid cached');\n\nselect undef, undef, undef, 2.1;\n\n# Cache-Control in the response header overrides proxy_cache_valid\n\nlike(http_get('/t.html?1'), qr/EXPIRED/, 'max-age ceased');\nlike(http_get('/t.html?2'), qr/HIT/, 's-maxage overrides max-age');\n\n# ticket #1382, cache item \"error\" field was not set from Cache-Control: max-age\n\nlike(get('/t2.html', 'X-CC: max-age=1'), qr/403 Forbidden/, 'intercept error');\n\n$t->write_file('t2.html', 'NOOP');\n\nlike(http_get('/t2.html'), qr/403 Forbidden/, 'error cached from max-age');\n\n# ticket #1382, cache item \"error\" field was set regardless of u->cacheable.\n\nlike(http_get('/'), qr/403 Forbidden/, 'error no-cache');\n\n$t->write_file('index.html', '');\n\nlike(http_get('/'), qr/200 OK/, 'error no-cache - not cacheable');\n\n###############################################################################\n\nsub get {\n\tmy ($url, $extra) = @_;\n\treturn http(<<EOF);\nGET $url HTTP/1.1\nHost: localhost\nConnection: close\n$extra\n\nEOF\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_cache_variables.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for http proxy cache, proxy_cache directive with variables.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy cache/)->plan(8)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    proxy_cache_path   %%TESTDIR%%/cache1  levels=1:2\n                       keys_zone=NAME1:1m;\n    proxy_cache_path   %%TESTDIR%%/cache2  levels=1:2\n                       keys_zone=NAME2:1m;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            proxy_pass    http://127.0.0.1:8081;\n\n            proxy_cache   $arg_c;\n\n            proxy_cache_valid   any      1m;\n\n            add_header X-Cache-Status $upstream_cache_status;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        location / {\n        }\n    }\n}\n\nEOF\n\n$t->write_file('index.html', 'SEE-THIS');\n\n$t->run();\n\n###############################################################################\n\nlike(http_get('/?c=NAME1'), qr/MISS.*SEE-THIS/ms, 'proxy request');\nlike(http_get('/?c=NAME1'), qr/HIT.*SEE-THIS/ms, 'proxy request cached');\n\nunlike(http_head('/?c=NAME1'), qr/SEE-THIS/, 'head request');\n\n$t->write_file('index.html', 'SEE-THAT');\n\nlike(http_get('/?c=NAME2'), qr/MISS.*SEE-THAT/ms, 'proxy request 2');\nlike(http_get('/?c=NAME2'), qr/HIT.*SEE-THAT/ms, 'proxy request 2 cached');\n\n# some invalid cases\n\nlike(http_get('/?c=NAME'), qr/ 500 /, 'proxy_cache unknown');\nlike(http_get('/'), qr/(?<!X-Cache).*SEE-THAT/ms, 'proxy_cache empty');\n\n$t->write_file('index.html', 'SEE-THOSE');\n\nlike(http_get('/'), qr/SEE-THOSE/, 'proxy_cache empty - not cached');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_cache_vary.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for http proxy cache, the Vary header.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy cache gzip rewrite/)\n\t->plan(52)->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    proxy_cache_path   %%TESTDIR%%/cache keys_zone=one:1m inactive=5s;\n    proxy_cache_key    $uri;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        add_header X-Cache-Status $upstream_cache_status;\n\n        location / {\n            proxy_pass    http://127.0.0.1:8081/;\n            proxy_cache   one;\n        }\n\n        location /replace/ {\n            proxy_pass    http://127.0.0.1:8081/;\n            proxy_cache   one;\n        }\n\n        location /revalidate/ {\n            proxy_pass    http://127.0.0.1:8081/;\n            proxy_cache   one;\n            proxy_cache_revalidate on;\n        }\n\n        location /ignore/ {\n            proxy_pass    http://127.0.0.1:8081/;\n            proxy_cache   one;\n            proxy_ignore_headers Vary;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        gzip on;\n        gzip_min_length 0;\n        gzip_http_version 1.0;\n        gzip_vary on;\n\n        expires 2s;\n\n        location / {\n            if ($args = \"novary\") {\n                return 200 \"the only variant\\n\";\n            }\n        }\n\n        location /asterisk {\n            gzip off;\n            add_header Vary \"*\";\n        }\n\n        location /complex {\n            gzip off;\n            add_header Vary \",, Accept-encoding , ,\";\n        }\n\n        location /multi {\n            gzip off;\n            add_header Vary Accept-Encoding;\n            add_header Vary Foo;\n        }\n\n        location /cold {\n            expires max;\n            add_header Vary $arg_vary;\n            add_header Xtra $arg_xtra;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('index.html', 'SEE-THIS');\n$t->write_file('asterisk', 'SEE-THIS');\n$t->write_file('complex', 'SEE-THIS');\n$t->write_file('multi', 'SEE-THIS');\n$t->write_file('cold', 'SEE-THIS');\n\n$t->run();\n\n###############################################################################\n\nlike(get('/', 'gzip'), qr/MISS/ms, 'first request');\nlike(get('/', 'gzip'), qr/HIT/ms, 'vary match cached');\nlike(get('/', 'deflate'), qr/MISS/ms, 'vary mismatch');\nlike(get('/', 'deflate'), qr/HIT/ms, 'vary mismatch cached');\nlike(get('/', 'foo'), qr/MISS/ms, 'vary mismatch 2');\nlike(get('/', 'foo'), qr/HIT/ms, 'vary mismatch 2 cached');\nlike(get('/', 'gzip'), qr/HIT/ms, 'multiple representations cached');\n\nSKIP: {\nskip 'long tests', 6 unless $ENV{TEST_NGINX_UNSAFE};\n\n# make sure all variants are properly expire\n# and removed after inactive timeout\n\nsleep(3);\n\nlike(get('/', 'gzip'), qr/EXPIRED/ms, 'first expired');\nlike(get('/', 'deflate'), qr/EXPIRED/ms, 'second variant expired');\n\nlike(get('/', 'gzip'), qr/HIT/ms, 'first cached after expire');\nlike(get('/', 'deflate'), qr/HIT/ms, 'second cached after expire');\n\nsleep(12);\n\nlike(get('/', 'gzip'), qr/MISS/ms, 'first inactive removed');\nlike(get('/', 'deflate'), qr/MISS/ms, 'second variant removed');\n\n}\n\nSKIP: {\nskip 'long tests', 6 unless $ENV{TEST_NGINX_UNSAFE};\n\n# check if the variant which was loaded first will be properly\n# removed if it's not requested (but another variant is requested\n# at the same time)\n\nsleep(3);\nlike(get('/', 'deflate'), qr/EXPIRED/ms, 'bump1');\nsleep(3);\nlike(get('/', 'deflate'), qr/EXPIRED/ms, 'bump2');\nsleep(3);\nlike(get('/', 'deflate'), qr/EXPIRED/ms, 'bump3');\nsleep(3);\nlike(get('/', 'deflate'), qr/EXPIRED/ms, 'bump4');\n\nTODO: {\nlocal $TODO = 'not yet';\n\nlike(get('/', 'gzip'), qr/MISS/ms, 'first not bumped by second requests');\n\n}\n\nlike(get('/', 'deflate'), qr/HIT/ms, 'second variant cached');\n\n}\n\n# if a response without Vary is returned to replace previously returned\n# responses with Vary, make sure it is then used in all cases\n\nlike(get('/replace/', 'gzip'), qr/MISS/, 'replace first');\nlike(get('/replace/', 'deflate'), qr/MISS/, 'replace second');\n\nsleep(3);\n\nlike(get('/replace/?novary', 'deflate'), qr/EXPIRED/, 'replace novary');\nlike(get('/replace/?zztest', 'gzip'), qr/HIT/, 'all replaced');\n\n# make sure revalidation of variants works fine\n\nlike(get('/revalidate/', 'gzip'), qr/MISS/, 'revalidate first');\nlike(get('/revalidate/', 'deflate'), qr/MISS/, 'revalidate second');\n\nsleep(3);\n\nlike(get('/revalidate/', 'gzip'), qr/REVALIDATED/, 'revalidated first');\nlike(get('/revalidate/', 'deflate'), qr/REVALIDATED/, 'revalidated second');\nlike(get('/revalidate/', 'gzip'), qr/HIT/, 'revalidate first after');\nlike(get('/revalidate/', 'deflate'), qr/HIT/, 'revalidate second after');\n\n# if the Vary header is ignored, cached version can be returned\n# regardless of request headers\n\nlike(get('/ignore/', 'gzip'), qr/MISS/ms, 'another request');\nlike(get('/ignore/', 'deflate'), qr/HIT/ms, 'vary ignored');\n\n# check parsing of Vary with multiple headers listed\n\nlike(get('/complex', 'gzip'), qr/MISS/ms, 'vary complex first');\nlike(get('/complex', 'deflate'), qr/MISS/ms, 'vary complex second');\nlike(get('/complex', 'gzip'), qr/HIT/ms, 'vary complex first cached');\nlike(get('/complex', 'deflate'), qr/HIT/ms, 'vary complex second cached');\n\n# From RFC 7231, \"7.1.4. Vary\",\n# http://tools.ietf.org/html/rfc7231#section-7.1.4:\n#\n#    A Vary field value of \"*\" signals that anything about the request\n#    might play a role in selecting the response representation, possibly\n#    including elements outside the message syntax (e.g., the client's\n#    network address).  A recipient will not be able to determine whether\n#    this response is appropriate for a later request without forwarding\n#    the request to the origin server.\n#\n# In theory, If-None-Match can be used to check if the representation\n# present in the cache is appropriate.  This seems to be only possible\n# with strong entity tags though, as representation with different\n# content condings may share the same weak entity tag.\n\nlike(get('/asterisk', 'gzip'), qr/MISS/ms, 'vary asterisk first');\nlike(get('/asterisk', 'gzip'), qr/MISS/ms, 'vary asterisk second');\n\n# From RFC 7234, \"4.1. Calculating Secondary Keys with Vary\",\n# http://tools.ietf.org/html/rfc7234#section-4.1:\n#\n#    The selecting header fields from two requests are defined to match if\n#    and only if those in the first request can be transformed to those in\n#    the second request by applying any of the following:\n#\n#    o  adding or removing whitespace, where allowed in the header field's\n#       syntax\n#\n#    o  combining multiple header fields with the same field name (see\n#       Section 3.2 of [RFC7230])\n#\n#    o  normalizing both header field values in a way that is known to\n#       have identical semantics, according to the header field's\n#       specification (e.g., reordering field values when order is not\n#       significant; case-normalization, where values are defined to be\n#       case-insensitive)\n#\n# Only whitespace normalization is currently implemented.\n\nlike(get('/', 'foo, bar'), qr/MISS/ms, 'normalize first');\nlike(get('/', 'foo,bar'), qr/HIT/ms, 'normalize whitespace');\nlike(get('/', 'foo,,  ,bar , '), qr/HIT/ms, 'normalize empty');\nlike(get('/', 'foobar'), qr/MISS/ms, 'normalize no whitespace mismatch');\n\nTODO: {\nlocal $TODO = 'not yet';\n\nlike(get('/', 'bar,foo'), qr/HIT/ms, 'normalize order');\n\n}\n\n# Multiple Vary headers (ticket #1423).\n\nlike(get('/multi', 'foo'), qr/MISS/ms, 'multi first');\nlike(get('/multi', 'foo'), qr/HIT/ms, 'multi second');\n\nTODO: {\nlocal $TODO = 'not yet' unless $t->has_version('1.23.0');\n\nlike(get('/multi', 'bar'), qr/MISS/ms, 'multi other');\n\n}\n\n# keep c->body_start when Vary changes (ticket #2029)\n\n# before 1.19.3, this prevented updating c->body_start of a main key\n# triggering \"cache file .. has too long header\" critical errors\n\nget1('/cold?vary=z', 'z:1');\nlike(get1('/cold?vary=x,y', 'x:1'), qr/MISS/, 'change first');\nlike(get1('/cold?vary=x,y', 'x:1'), qr/HIT/, 'change first cached');\n\nlike(get1('/cold?vary=x,y&xtra=1', 'x:2'), qr/MISS/, 'change second');\nlike(get1('/cold?vary=x,y&xtra=1', 'x:2'), qr/HIT/, 'change second cached');\n\n$t->stop();\n$t->run();\n\n# reset c->body_start when loading a secondary key variant\n\n# before 1.19.3, it was loaded using a variant stored with a main key\n# triggering \"cache file .. has too long header\" critical errors\n\nlike(get1('/cold?vary=x,y', 'x:1'), qr/HIT/, 'cold first');\nlike(get1('/cold?vary=x,y&xtra=1', 'x:2'), qr/HIT/, 'cold second');\n\n$t->stop();\n\nlike(`grep -F '[crit]' ${\\($t->testdir())}/error.log`, qr/^$/s, 'no crit');\n\n###############################################################################\n\nsub get {\n\tmy ($url, $extra) = @_;\n\treturn http(<<EOF);\nGET $url HTTP/1.1\nHost: localhost\nConnection: close\nAccept-Encoding: $extra\n\nEOF\n}\n\nsub get1 {\n\tmy ($url, $extra) = @_;\n\treturn http(<<EOF);\nGET $url HTTP/1.1\nHost: localhost\nConnection: close\n$extra\n\nEOF\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_chunked.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Test for http backend returning response with Transfer-Encoding: chunked.\n\n# Since nginx uses HTTP/1.0 in requests to backend it's backend bug, but we\n# want to handle this gracefully.  And anyway chunked support will be required\n# for HTTP/1.1 backend connections.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy ssi/)->plan(3);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            proxy_pass http://127.0.0.1:8081;\n            proxy_read_timeout 1s;\n        }\n        location /nobuffering {\n            proxy_pass http://127.0.0.1:8081;\n            proxy_read_timeout 1s;\n            proxy_buffering off;\n        }\n        location /inmemory.html {\n            ssi on;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('inmemory.html',\n\t'<!--#include virtual=\"/\" set=\"one\" --><!--#echo var=\"one\" -->');\n\n$t->run_daemon(\\&http_chunked_daemon);\n$t->run()->waitforsocket('127.0.0.1:' . port(8081));\n\n###############################################################################\n\nlike(http_get('/'), qr/\\x0d\\x0aSEE-THIS$/s, 'chunked');\nlike(http_get('/nobuffering'), qr/\\x0d\\x0aSEE-THIS$/s, 'chunked nobuffering');\nlike(http_get('/inmemory.html'), qr/\\x0d\\x0aSEE-THIS$/s, 'chunked inmemory');\n\n###############################################################################\n\nsub http_chunked_daemon {\n\tmy $server = IO::Socket::INET->new(\n\t\tProto => 'tcp',\n\t\tLocalAddr => '127.0.0.1:' . port(8081),\n\t\tListen => 5,\n\t\tReuse => 1\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\tlocal $SIG{PIPE} = 'IGNORE';\n\n\twhile (my $client = $server->accept()) {\n\t\t$client->autoflush(1);\n\n\t\twhile (<$client>) {\n\t\t\tlast if (/^\\x0d?\\x0a?$/);\n\t\t}\n\n\t\tprint $client <<'EOF';\nHTTP/1.1 200 OK\nConnection: close\nTransfer-Encoding: chunked\n\n9\nSEE-THIS\n\n0\n\nEOF\n\n\t\tclose $client;\n\t}\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_chunked_extra.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n# (C) Nginx, Inc.\n\n# Test for http backend returning response with Transfer-Encoding: chunked,\n# followed by some extra data.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\nuse Socket qw/ CRLF /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy/)->plan(1);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        proxy_buffer_size 128;\n        proxy_buffers 4 128;\n\n        location / {\n            proxy_pass http://127.0.0.1:8081;\n            proxy_read_timeout 1s;\n        }\n    }\n}\n\nEOF\n\n$t->run_daemon(\\&http_chunked_daemon);\n$t->run()->waitforsocket('127.0.0.1:' . port(8081));\n\n###############################################################################\n\nlike(http_get('/'), qr/200 OK(?!.*zzz)/s, 'chunked with extra data');\n\n###############################################################################\n\nsub http_chunked_daemon {\n\tmy $server = IO::Socket::INET->new(\n\t\tProto => 'tcp',\n\t\tLocalAddr => '127.0.0.1:' . port(8081),\n\t\tListen => 5,\n\t\tReuse => 1\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\tlocal $SIG{PIPE} = 'IGNORE';\n\n\twhile (my $client = $server->accept()) {\n\t\t$client->autoflush(1);\n\n\t\twhile (<$client>) {\n\t\t\tlast if (/^\\x0d?\\x0a?$/);\n\t\t}\n\n\t\t# return a large response start to allocate\n\t\t# multiple buffers; stop at the buffer end\n\n\t\tprint $client \"\"\n\t\t\t. \"HTTP/1.1 200 OK\" . CRLF\n\t\t\t. \"Connection: close\" . CRLF\n\t\t\t. \"Transfer-Encoding: chunked\" . CRLF . CRLF\n\t\t\t. \"80\" . CRLF . (\"x\" x 126) . CRLF . CRLF\n\t\t\t. \"80\" . CRLF . (\"x\" x 126) . CRLF . CRLF\n\t\t\t. \"80\" . CRLF . (\"x\" x 126) . CRLF . CRLF\n\t\t\t. \"80\" . CRLF . (\"x\" x 126) . CRLF . CRLF\n\t\t\t. \"20\" . CRLF . (\"x\" x 30) . CRLF . CRLF;\n\n\t\tselect(undef, undef, undef, 0.3);\n\n\t\t# fill three full buffers here, so they are\n\t\t# processed in order, regardless of the\n\t\t# p->upstream_done flag set\n\n\t\tprint $client \"\"\n\t\t\t. \"75\" . CRLF . (\"y\" x 115) . CRLF . CRLF\n\t\t\t. \"0\" . CRLF . CRLF\n\t\t\t. \"75\" . CRLF . (\"z\" x 115) . CRLF . CRLF\n\t\t\t. \"0\" . CRLF . CRLF\n\t\t\t. \"75\" . CRLF . (\"z\" x 115) . CRLF . CRLF\n\t\t\t. \"0\" . CRLF . CRLF;\n\n\t\tclose $client;\n\t}\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_cookie.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n# (C) Valentin Bartenev\n\n# Tests for the proxy_cookie_domain and proxy_cookie_path directives.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy rewrite/);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            proxy_pass http://127.0.0.1:8081;\n\n            proxy_cookie_domain www.example.org .example.com;\n            proxy_cookie_domain .$server_name.com en.$server_name.org;\n            proxy_cookie_domain ~^(.+)\\.com$ $1.org;\n\n            proxy_cookie_path /path/ /new/;\n            proxy_cookie_path /$server_name/ /new/$server_name/;\n            proxy_cookie_path ~^/regex/(.+)$ /$1;\n            proxy_cookie_path ~*^/caseless/(.+)$ /$1;\n\n            location /off/ {\n                proxy_pass http://127.0.0.1:8081;\n\n                proxy_cookie_domain off;\n                proxy_cookie_path off;\n            }\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        location / {\n            if ($arg_domain) {\n                set $sc_domain \"; Domain=$arg_domain\";\n            }\n            if ($arg_path) {\n                set $sc_path \"; Path=$arg_path\";\n            }\n            add_header Set-Cookie v=path=domain=$sc_domain$sc_path;\n            return 200 OK;\n        }\n    }\n}\n\nEOF\n\n$t->run()->plan(9);\n\n###############################################################################\n\nmy $port = port(8080);\n\nis(http_get_set_cookie('/?domain=www.Example.org'),\n\t'v=path=domain=; Domain=example.com', 'domain rewrite');\nis(http_get_set_cookie('/?domain=.LocalHost.com'),\n\t'v=path=domain=; Domain=.en.localhost.org',\n\t'domain rewrite with vars');\nis(http_get_set_cookie('/?domain=www.example.COM'),\n\t'v=path=domain=; Domain=www.example.org', 'domain regex rewrite');\n\nis(http_get_set_cookie('/?path=/path/test.html'),\n\t'v=path=domain=; Path=/new/test.html', 'path rewrite');\nis(http_get_set_cookie('/?path=/localhost/test.html'),\n\t'v=path=domain=; Path=/new/localhost/test.html',\n\t'path rewrite with vars');\nis(http_get_set_cookie('/?path=/regex/test.html'),\n\t'v=path=domain=; Path=/test.html', 'path regex rewrite');\nis(http_get_set_cookie('/?path=/CASEless/test.html'),\n\t'v=path=domain=; Path=/test.html', 'path caseless regex rewrite');\n\nis(http_get_set_cookie('/?domain=www.example.org&path=/path/test.html'),\n\t'v=path=domain=; Domain=example.com; Path=/new/test.html',\n\t'domain and path rewrite');\nis(http_get_set_cookie('/off/?domain=www.example.org&path=/path/test.html'),\n\t'v=path=domain=; Domain=www.example.org; Path=/path/test.html',\n\t'domain and path rewrite off');\n\n###############################################################################\n\nsub http_get_set_cookie {\n\tmy ($uri) = @_;\n\thttp_get(\"http://127.0.0.1:$port$uri\") =~ /^Set-Cookie:\\s(.+?)\\x0d?$/mi;\n\treturn $1;\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_cookie_flags.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for the proxy_cookie_flags directive.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy rewrite/);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            proxy_pass http://127.0.0.1:8081;\n\n            proxy_cookie_flags a secure httponly samesite=none;\n            proxy_cookie_flags b secure httponly samesite=lax;\n            proxy_cookie_flags c secure httponly samesite=strict;\n            proxy_cookie_flags d nosecure nohttponly nosamesite;\n\n            proxy_cookie_flags $arg_complex secure;\n            proxy_cookie_flags ~BAR httponly;\n\n            location /off/ {\n                proxy_pass http://127.0.0.1:8081;\n                proxy_cookie_flags off;\n            }\n        }\n\n        location /var/ {\n            proxy_pass http://127.0.0.1:8081;\n            proxy_cookie_flags $arg_v $arg_f1 $arg_f2 $arg_f3;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        location / {\n            set $c \"$arg_v$arg_complex=path=domain=; Domain=example.org$arg_f\";\n            add_header Set-Cookie $c;\n            return 200 OK;\n        }\n    }\n}\n\nEOF\n\n$t->run()->plan(14);\n\n###############################################################################\n\nis(http_get_set_cookie('/?v=a'),\n\t'a=path=domain=; Domain=example.org; Secure; HttpOnly; SameSite=None',\n\t'flags set all');\nis(http_get_set_cookie('/?v=b'),\n\t'b=path=domain=; Domain=example.org; Secure; HttpOnly; SameSite=Lax',\n\t'flags set lax');\nis(http_get_set_cookie('/?v=c'),\n\t'c=path=domain=; Domain=example.org; Secure; HttpOnly; SameSite=Strict',\n\t'flags set strict');\n\n# edit already set flags\n\nis(http_get_set_cookie('/?v=a&f=;Secure;HttpOnly;SameSite=Lax'),\n\t'a=path=domain=; Domain=example.org; Secure; HttpOnly; SameSite=None',\n\t'flags reset all');\nis(http_get_set_cookie('/?v=b&f=;Secure;HttpOnly;SameSite=None'),\n\t'b=path=domain=; Domain=example.org; Secure; HttpOnly; SameSite=Lax',\n\t'flags reset lax');\nis(http_get_set_cookie('/?v=c&f=;Secure;HttpOnly;SameSite=None'),\n\t'c=path=domain=; Domain=example.org; Secure; HttpOnly; SameSite=Strict',\n\t'flags reset strict');\n\nis(http_get_set_cookie('/?v=d&f=;secure;httponly;samesite=lax'),\n\t'd=path=domain=; Domain=example.org',\n\t'flags remove');\n\nis(http_get_set_cookie('/?v=nx&f=;samesite=none'),\n\t'nx=path=domain=; Domain=example.org;samesite=none', 'flags no match');\n\nis(http_get_set_cookie('/?complex=v'),\n\t'v=path=domain=; Domain=example.org; Secure', 'flags variable');\nis(http_get_set_cookie('/?v=foobarbaz'),\n\t'foobarbaz=path=domain=; Domain=example.org; HttpOnly', 'flags regex');\n\nis(http_get_set_cookie('/off/?v=a'), 'a=path=domain=; Domain=example.org',\n\t'flags off');\n\n# variables in flags\n\nis(http_get_set_cookie('/var/?v=v&f1=secure&f2=httponly&f3=samesite=none'),\n\t'v=path=domain=; Domain=example.org; Secure; HttpOnly; SameSite=None',\n\t'flags set');\nis(http_get_set_cookie('/var/?v=v&f=;Secure;HttpOnly;SameSite=Lax' .\n\t'&f1=secure&f2=httponly&f3=samesite=none'),\n\t'v=path=domain=; Domain=example.org; Secure; HttpOnly; SameSite=None',\n\t'flags reset');\nis(http_get_set_cookie('/var/?v=v&f=;secure;httponly;samesite=lax' .\n\t'&f1=nosecure&f2=nohttponly&f3=nosamesite'),\n\t'v=path=domain=; Domain=example.org',\n\t'flags remove');\n\n###############################################################################\n\nsub http_get_set_cookie {\n\tmy ($uri) = @_;\n\thttp_get($uri) =~ /^Set-Cookie:\\s(.+?)\\x0d?$/mi;\n\treturn $1;\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_duplicate_headers.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Test for http backend returning response with invalid and duplicate\n# headers.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\nuse Socket qw/ CRLF /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy/)->plan(8);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            proxy_pass http://127.0.0.1:8081;\n            proxy_read_timeout 1s;\n        }\n    }\n}\n\nEOF\n\n$t->run_daemon(\\&http_daemon);\n$t->run()->waitforsocket('127.0.0.1:' . port(8081));\n\n###############################################################################\n\nlike(http_get('/'), qr/200 OK/, 'normal');\n\nTODO: {\nlocal $TODO = 'not yet' unless $t->has_version('1.23.0');\n\nlike(http_get('/invalid-length'), qr/502 Bad/, 'invalid length');\nlike(http_get('/duplicate-length'), qr/502 Bad/, 'duplicate length');\nlike(http_get('/unknown-transfer-encoding'), qr/502 Bad/,\n\t'unknown transfer encoding');\nlike(http_get('/duplicate-transfer-encoding'), qr/502 Bad/,\n\t'duplicate transfer encoding');\nlike(http_get('/length-and-transfer-encoding'), qr/502 Bad/,\n\t'length and transfer encoding');\nlike(http_get('/transfer-encoding-and-length'), qr/502 Bad/,\n\t'transfer encoding and length');\n\nlike(http_get('/duplicate-expires'), qr/Expires: foo(?!.*bar)/s,\n\t'duplicate expires ignored');\n\n}\n\n###############################################################################\n\nsub http_daemon {\n\tmy $server = IO::Socket::INET->new(\n\t\tProto => 'tcp',\n\t\tLocalAddr => '127.0.0.1:' . port(8081),\n\t\tListen => 5,\n\t\tReuse => 1\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\tlocal $SIG{PIPE} = 'IGNORE';\n\n\twhile (my $client = $server->accept()) {\n\t\t$client->autoflush(1);\n\n\t\tmy $headers = '';\n\t\tmy $uri = '';\n\n\t\twhile (<$client>) {\n\t\t\t$headers .= $_;\n\t\t\tlast if (/^\\x0d?\\x0a?$/);\n\t\t}\n\n\t\t$uri = $1 if $headers =~ /^\\S+\\s+([^ ]+)\\s+HTTP/i;\n\n\t\tif ($uri eq '/') {\n\n\t\t\tprint $client\n\t\t\t\t'HTTP/1.1 200 OK' . CRLF .\n\t\t\t\t'Connection: close' . CRLF .\n\t\t\t\t'Content-Length: 0' . CRLF . CRLF;\n\n\t\t} elsif ($uri eq '/invalid-length') {\n\n\t\t\tprint $client\n\t\t\t\t'HTTP/1.1 200 OK' . CRLF .\n\t\t\t\t'Connection: close' . CRLF .\n\t\t\t\t'Content-Length: foo' . CRLF . CRLF;\n\n\t\t} elsif ($uri eq '/duplicate-length') {\n\n\t\t\tprint $client\n\t\t\t\t'HTTP/1.1 200 OK' . CRLF .\n\t\t\t\t'Connection: close' . CRLF .\n\t\t\t\t'Content-Length: 0' . CRLF .\n\t\t\t\t'Content-Length: 0' . CRLF . CRLF;\n\n\t\t} elsif ($uri eq '/unknown-transfer-encoding') {\n\n\t\t\tprint $client\n\t\t\t\t'HTTP/1.1 200 OK' . CRLF .\n\t\t\t\t'Connection: close' . CRLF .\n\t\t\t\t'Transfer-Encoding: foo' . CRLF . CRLF;\n\n\t\t} elsif ($uri eq '/duplicate-transfer-encoding') {\n\n\t\t\tprint $client\n\t\t\t\t'HTTP/1.1 200 OK' . CRLF .\n\t\t\t\t'Connection: close' . CRLF .\n\t\t\t\t'Transfer-Encoding: chunked' . CRLF .\n\t\t\t\t'Transfer-Encoding: chunked' . CRLF . CRLF .\n\t\t\t\t'0' . CRLF . CRLF;\n\n\t\t} elsif ($uri eq '/length-and-transfer-encoding') {\n\n\t\t\tprint $client\n\t\t\t\t'HTTP/1.1 200 OK' . CRLF .\n\t\t\t\t'Connection: close' . CRLF .\n\t\t\t\t'Content-Length: 0' . CRLF .\n\t\t\t\t'Transfer-Encoding: chunked' . CRLF . CRLF .\n\t\t\t\t'0' . CRLF . CRLF;\n\n\t\t} elsif ($uri eq '/transfer-encoding-and-length') {\n\n\t\t\tprint $client\n\t\t\t\t'HTTP/1.1 200 OK' . CRLF .\n\t\t\t\t'Connection: close' . CRLF .\n\t\t\t\t'Transfer-Encoding: chunked' . CRLF .\n\t\t\t\t'Content-Length: 0' . CRLF . CRLF .\n\t\t\t\t'0' . CRLF . CRLF;\n\n\t\t} elsif ($uri eq '/duplicate-expires') {\n\n\t\t\tprint $client\n\t\t\t\t'HTTP/1.1 200 OK' . CRLF .\n\t\t\t\t'Connection: close' . CRLF .\n\t\t\t\t'Expires: foo' . CRLF .\n\t\t\t\t'Expires: bar' . CRLF . CRLF;\n\n\t\t}\n\n\t\tclose $client;\n\t}\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_extra_data.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n# (C) Nginx, Inc.\n\n# Tests for http backend with extra data.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()\n\t->has(qw/http proxy cache rewrite addition/)->plan(22)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    proxy_cache_path cache keys_zone=one:1m;\n    proxy_cache_key $request_uri;\n    proxy_cache_valid any 1m;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            proxy_pass http://127.0.0.1:8081;\n            add_after_body /after;\n        }\n\n        location /unbuf/ {\n            proxy_pass http://127.0.0.1:8081;\n            proxy_buffering off;\n            add_after_body /after;\n        }\n\n        location /head/ {\n            proxy_pass http://127.0.0.1:8081;\n            proxy_cache one;\n            add_after_body /after;\n        }\n\n        location /after {\n            return 200 \":after\\n\";\n        }\n    }\n}\n\nEOF\n\n$t->run_daemon(\\&http_daemon);\n$t->run()->waitforsocket('127.0.0.1:' . port(8081));\n\n###############################################################################\n\nlike(http_get('/'), qr/SEE-THIS(?!-BUT-NOT-THIS)/, 'response with extra data');\nlike(http_get('/short'), qr/SEE-THIS(?!.*:after)/s, 'too short response');\nlike(http_get('/empty'), qr/200 OK(?!.*:after)/s, 'empty too short response');\n\nlike(http_head('/'), qr/200 OK(?!.*SEE-THIS)/s, 'no data in HEAD');\nlike(http_head('/short'), qr/200 OK(?!.*SEE-THIS)/s, 'too short to HEAD');\nlike(http_head('/empty'), qr/200 OK/, 'empty response to HEAD');\n\n# unbuffered responses\n\nlike(http_get('/unbuf/'), qr/SEE-THIS(?!-BUT-NOT-THIS)/,\n\t'unbuffered with extra data');\nlike(http_get('/unbuf/short'), qr/SEE-THIS(?!.*:after)/s,\n\t'unbuffered too short response');\nlike(http_get('/unbuf/empty'), qr/200 OK(?!.*:after)/s,\n\t'unbuffered empty too short response');\n\nlike(http_head('/unbuf/'), qr/200 OK(?!.*SEE-THIS)/s,\n\t'unbuffered no data in HEAD');\nlike(http_head('/unbuf/short'), qr/200 OK(?!.*SEE-THIS)/s,\n\t'unbuffered too short response to HEAD');\nlike(http_head('/unbuf/empty'), qr/200 OK/,\n\t'unbuffered empty response to HEAD');\n\n# caching of responsses to HEAD requests\n\nlike(http_head('/head/empty'), qr/200 OK(?!.*SEE-THIS)/s, 'head no body');\nlike(http_head('/head/matching'), qr/200 OK(?!.*SEE-THIS)/s, 'head matching');\nlike(http_head('/head/extra'), qr/200 OK(?!.*SEE-THIS)/s, 'head extra');\nlike(http_head('/head/short'), qr/200 OK(?!.*SEE-THIS)/s, 'head too short');\n\nlike(http_get('/head/empty'), qr/SEE-THIS/, 'head no body cached');\nlike(http_get('/head/matching'), qr/SEE-THIS/, 'head matching cached');\nlike(http_get('/head/extra'), qr/SEE-THIS(?!-BUT-NOT-THIS)/s,\n\t'head extra cached');\nlike(http_get('/head/short'), qr/SEE-THIS(?!.*:after)/s,\n\t'head too short cached');\n\n# \"zero size buf\" alerts (ticket #2117)\n\nlike(http_get('/zero'), qr/200 OK(?!.*NOT-THIS)/s, 'zero size');\nlike(http_get('/unbuf/zero'), qr/200 OK(?!.*NOT-THIS)/s,\n\t'unbuffered zero size');\n\n###############################################################################\n\nsub http_daemon {\n\tmy $server = IO::Socket::INET->new(\n\t\tProto => 'tcp',\n\t\tLocalHost => '127.0.0.1:' . port(8081),\n\t\tListen => 5,\n\t\tReuse => 1\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\tlocal $SIG{PIPE} = 'IGNORE';\n\n\tmy ($uri, $head);\n\n\twhile (my $c = $server->accept()) {\n\t\t$c->autoflush(1);\n\n\t\tmy $headers = '';\n\t\tmy $uri = '';\n\n\t\twhile (<$c>) {\n\t\t\t$headers .= $_;\n\t\t\tlast if (/^\\x0d?\\x0a?$/);\n\t\t}\n\n\t\t$uri = $1 if $headers =~ /^\\S+\\s+([^ ]+)\\s+HTTP/i;\n\t\t$uri =~ s!^/unbuf!!;\n\n\t\t$head = ($headers =~ /^HEAD/);\n\n\t\tif ($uri eq '/') {\n\t\t\t$c->print(\"HTTP/1.1 200 OK\\n\");\n\t\t\t$c->print(\"Content-Type: text/html\\n\");\n\t\t\t$c->print(\"Content-Length: 8\\n\\n\");\n\t\t\t$c->print(\"SEE-THIS-BUT-NOT-THIS\\n\");\n\n\t\t} elsif ($uri eq '/zero') {\n\t\t\t$c->print(\"HTTP/1.1 200 OK\\n\");\n\t\t\t$c->print(\"Content-Type: text/html\\n\");\n\t\t\t$c->print(\"Content-Length: 0\\n\\n\");\n\t\t\t$c->print(\"NOT-THIS\\n\");\n\n\t\t} elsif ($uri eq '/short') {\n\t\t\t$c->print(\"HTTP/1.1 200 OK\\n\");\n\t\t\t$c->print(\"Content-Type: text/html\\n\");\n\t\t\t$c->print(\"Content-Length: 100\\n\\n\");\n\t\t\t$c->print(\"SEE-THIS-TOO-SHORT-RESPONSE\\n\");\n\n\t\t} elsif ($uri eq '/empty') {\n\t\t\t$c->print(\"HTTP/1.1 200 OK\\n\");\n\t\t\t$c->print(\"Content-Type: text/html\\n\");\n\t\t\t$c->print(\"Content-Length: 100\\n\\n\");\n\n\t\t} elsif ($uri eq '/head/empty') {\n\t\t\t$c->print(\"HTTP/1.1 200 OK\\n\");\n\t\t\t$c->print(\"Content-Type: text/html\\n\");\n\t\t\t$c->print(\"Content-Length: 8\\n\\n\");\n\t\t\t$c->print(\"SEE-THIS\") unless $head;\n\n\t\t} elsif ($uri eq '/head/matching') {\n\t\t\t$c->print(\"HTTP/1.1 200 OK\\n\");\n\t\t\t$c->print(\"Content-Type: text/html\\n\");\n\t\t\t$c->print(\"Content-Length: 8\\n\\n\");\n\t\t\t$c->print(\"SEE-THIS\");\n\n\t\t} elsif ($uri eq '/head/extra') {\n\t\t\t$c->print(\"HTTP/1.1 200 OK\\n\");\n\t\t\t$c->print(\"Content-Type: text/html\\n\");\n\t\t\t$c->print(\"Content-Length: 8\\n\\n\");\n\t\t\t$c->print(\"SEE-THIS-BUT-NOT-THIS\\n\");\n\n\t\t} elsif ($uri eq '/head/short') {\n\t\t\t$c->print(\"HTTP/1.1 200 OK\\n\");\n\t\t\t$c->print(\"Content-Type: text/html\\n\");\n\t\t\t$c->print(\"Content-Length: 100\\n\\n\");\n\t\t\t$c->print(\"SEE-THIS\\n\");\n\t\t}\n\n\t\tclose $c;\n\t}\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_force_ranges.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for http proxy cache and range filter.\n# proxy_force_ranges enables partial response regardless Accept-Ranges.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse Socket qw/ $CRLF /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy cache/)->plan(7)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    proxy_cache_path   %%TESTDIR%%/cache  levels=1:2\n                       keys_zone=NAME:1m;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            proxy_pass    http://127.0.0.1:8081;\n        }\n\n        location /proxy/ {\n            proxy_pass    http://127.0.0.1:8081/;\n            proxy_force_ranges on;\n            add_trailer X-Trailer \"\";\n        }\n\n        location /cache/ {\n            proxy_pass    http://127.0.0.1:8081/;\n            proxy_cache   NAME;\n            proxy_cache_valid 200 1m;\n\n            proxy_force_ranges on;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        location / {\n            max_ranges 0;\n            add_header Last-Modified \"Mon, 28 Sep 1970 06:00:00 GMT\";\n            add_header ETag '\"59a5401c-8\"';\n        }\n    }\n}\n\nEOF\n\n$t->write_file('t.html', 'SEE-THIS');\n$t->run();\n\n###############################################################################\n\n# serving range requests requires Accept-Ranges by default\n\nunlike(http_get_range('/t.html', 'Range: bytes=4-'), qr/^THIS/m,\n\t'range without Accept-Ranges');\n\nlike(http_get_range('/cache/t.html', 'Range: bytes=4-'), qr/^THIS/m,\n\t'uncached range');\nlike(http_get_range('/cache/t.html', 'Range: bytes=4-'), qr/^THIS/m,\n\t'cached range');\nlike(http_get_range('/cache/t.html', 'Range: bytes=0-2,4-'), qr/^SEE.*^THIS/ms,\n\t'cached multipart range');\n\n# If-Range HTTP-date request\n\nlike(http_get_range('/proxy/t.html',\n\t\"Range: bytes=4-\\nIf-Range: Mon, 28 Sep 1970 06:00:00 GMT\"),\n\tqr/^THIS/m, 'if-range last-modified proxy');\n\n# If-Range entity-tag request\n\nlike(http_get_range('/proxy/t.html',\n\t\"Range: bytes=4-\\nIf-Range: \\\"59a5401c-8\\\"\"),\n\tqr/^THIS/m, 'if-range etag proxy');\n\n# range sent using chunked transfer encoding\n\nlike(http_get_range('/proxy/t.html', 'Range: bytes=-2'),\n\tqr/2${CRLF}IS${CRLF}0$CRLF$CRLF$/, 'no dublicate final chunk');\n\n###############################################################################\n\nsub http_get_range {\n\tmy ($url, $extra) = @_;\n\treturn http(<<EOF);\nGET $url HTTP/1.1\nHost: localhost\nConnection: close\n$extra\n\nEOF\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_if.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for http proxy module related to use with the \"if\" directive.\n# See http://wiki.nginx.org/IfIsEvil for more details.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy rewrite http_ssl/)\n\t->has_daemon('openssl')->plan(15);\n\n$t->write_file_expand('nginx.conf', <<'EOF')->todo_alerts();\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            proxy_pass http://127.0.0.1:8081/;\n        }\n\n        # request was sent to backend without uri changed\n        # to '/' due to if\n\n        location /proxy-pass-uri {\n            proxy_pass http://127.0.0.1:8081/replacement;\n\n            if ($arg_if) {\n                # nothing\n            }\n\n            location /proxy-pass-uri/inner {\n                # no proxy_pass here, static\n\n                if ($arg_if) {\n                    # nothing\n                }\n            }\n        }\n\n        # same as the above, but there is a special handling\n        # in configuration merge; it used to do wrong things with\n        # nested locations though\n\n        location /proxy-pass-uri-lmt {\n            proxy_pass http://127.0.0.1:8081/replacement;\n\n            limit_except POST {\n                # nothing\n            }\n\n            location /proxy-pass-uri-lmt/inner {\n                # no proxy_pass here, static\n\n                limit_except POST {\n                    # nothing\n                }\n            }\n        }\n\n        location /proxy-pass-uri-lmt-different {\n            proxy_pass http://127.0.0.1:8081/replacement;\n\n            limit_except POST {\n                proxy_pass http://127.0.0.1:8081;\n            }\n        }\n\n        # segmentation fault in old versions,\n        # fixed to return 500 Internal Error in nginx 1.3.10\n\n        location /proxy-inside-if-crash {\n\n            set $true 1;\n\n            if ($true) {\n                # proxy_pass inside if\n                proxy_pass http://127.0.0.1:8081;\n            }\n\n            if ($true) {\n                # no handler here\n            }\n        }\n\n        # normal proxy_pass and proxy_pass with variables\n        # use distinct field, and inheritance should be mutually\n        # exclusive\n\n        location /variables {\n            proxy_pass http://127.0.0.1:8081/outer/$host;\n\n            if ($arg_if) {\n                proxy_pass http://127.0.0.1:8081;\n            }\n\n            location /variables/inner {\n                proxy_pass http://127.0.0.1:8081;\n            }\n        }\n\n        # ssl context shouldn't be inherited into nested\n        # locations with different proxy_pass, but should\n        # be correctly inherited into if's\n\n        location /ssl {\n            proxy_pass https://127.0.0.1:8082/outer;\n\n            if ($arg_if) {\n                # inherited from outer\n            }\n\n            location /ssl/inner {\n                proxy_pass http://127.0.0.1:8081;\n            }\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081;\n        listen       127.0.0.1:8082 ssl;\n        server_name  localhost;\n\n        ssl_certificate localhost.crt;\n        ssl_certificate_key localhost.key;\n\n        return 200 \"uri:$uri\\n\";\n    }\n}\n\nEOF\n\n$t->write_file('openssl.conf', <<EOF);\n[ req ]\ndefault_bits = 2048\nencrypt_key = no\ndistinguished_name = req_distinguished_name\n[ req_distinguished_name ]\nEOF\n\nmy $d = $t->testdir();\n\nforeach my $name ('localhost') {\n\tsystem('openssl req -x509 -new '\n\t\t. \"-config $d/openssl.conf -subj /CN=$name/ \"\n\t\t. \"-out $d/$name.crt -keyout $d/$name.key \"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create certificate for $name: $!\\n\";\n}\n\n$t->run();\n\n###############################################################################\n\nlike(http_get('/'), qr!uri:/$!, 'proxy request');\n\nlike(http_get('/proxy-pass-uri'), qr!uri:/replacement$!,\n\t'proxy_pass uri changed');\n\n# due to missing information about an original location where\n# proxy_pass was specified, this used to pass request with\n# original unmodified uri\n\nlike(http_get('/proxy-pass-uri?if=1'), qr!uri:/replacement$!,\n\t'proxy_pass uri changed in if');\n\nlike(http_get('/proxy-pass-uri/inner'), qr!404 Not Found!,\n\t'proxy_pass uri changed inner');\nlike(http_get('/proxy-pass-uri/inner?if=1'), qr!404 Not Found!,\n\t'proxy_pass uri changed inner in if');\n\n# limit_except\n\nlike(http_get('/proxy-pass-uri-lmt'), qr!uri:/replacement$!,\n\t'proxy_pass uri and limit_except');\n\n# special handling of limit_except resulted in wrong handling\n# of requests in nested locations\n\nlike(http_get('/proxy-pass-uri-lmt/inner'), qr!404 Not Found!,\n\t'proxy_pass uri and limit_except, inner');\n\nlike(http_get('/proxy-pass-uri-lmt-different'),\n\tqr!uri:/proxy-pass-uri-lmt-different!,\n\t'proxy_pass and limit_except with different proxy_pass');\n\n# segmentation fault in old versions,\n# fixed to return 500 Internal Error in nginx 1.3.10\n\nlike(http_get('/proxy-inside-if-crash'), qr!500 Internal Server Error!,\n\t'proxy_pass inside if');\n\n# normal proxy_pass and proxy_pass with variables\n# use distinct field, and inheritance should be mutually\n# exclusive, see ticket #645\n\nlike(http_get('/variables'), qr!uri:/outer!,\n\t'proxy_pass variables');\nlike(http_get('/variables?if=1'), qr!uri:/variables!,\n\t'proxy_pass variables if');\nlike(http_get('/variables/inner'), qr!uri:/variables/inner!,\n\t'proxy_pass variables nested');\n\n# ssl context shouldn't be inherited into nested\n# locations with different proxy_pass, but should\n# be correctly inherited into if's\n\nlike(http_get('/ssl'), qr!uri:/outer!,\n\t'proxy_pass ssl');\nlike(http_get('/ssl?if=1'), qr!uri:/outer!,\n\t'proxy_pass ssl inside if');\nlike(http_get('/ssl/inner'), qr!uri:/ssl/inner!,\n\t'proxy_pass nossl inside ssl');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_implicit.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for an upstream implicitly defined by proxy_pass.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse Socket qw/ SOCK_STREAM IPPROTO_TCP AF_INET6 /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\neval { die if $Socket::VERSION < 1.96; };\nplan(skip_all => 'Socket too old for getaddrinfo') if $@;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy/);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        listen       [::1]:%%PORT_8080%%;\n        server_name  localhost;\n\n        location / {\n            proxy_pass http://localhost:%%PORT_8080%%/stub;\n            proxy_next_upstream http_404;\n            add_header X-Addr $upstream_addr always;\n        }\n\n        location /var {\n            proxy_pass http://$arg_b/stub;\n            proxy_next_upstream http_404;\n            add_header X-Addr $upstream_addr always;\n        }\n\n        location /stub { }\n    }\n}\n\nEOF\n\n$t->try_run('no inet6 support')->plan(3);\n\n###############################################################################\n\nmy $p = port(8080);\nmy @addrs = resolve('localhost');\nmy $exp = qr/$addrs[0]:$p/ if @addrs == 1;\nmy $v1 = \"$addrs[0]:$p\", my $v2 = \"$addrs[1]:$p\" if @addrs == 2;\n$exp = qr/\\Q$v1, $v2\\E|\\Q$v2, $v1\\E/ if @addrs == 2;\ndie \"too many addresses in localhost\" if @addrs > 2;\n\nlike(http_get('/'), qr/Not Found/, 'implicit upstream');\nlike(http_get('/'), $exp, 'implicit upstream all tried');\nlike(http_get(\"/var?b=localhost:$p\"), qr/Not Found/,\n\t'implicit upstream by variable');\n\n###############################################################################\n\nsub resolve {\n\tmy ($name) = @_;\n\n\tmy $ai_addrconfig = eval { Socket::AI_ADDRCONFIG() };\n\tmy ($err, @res) = Socket::getaddrinfo($name, \"\",\n\t\t{ socktype => SOCK_STREAM, protocol => IPPROTO_TCP,\n\t\t\tflags => $ai_addrconfig });\n\tdie \"Cannot getaddrinfo - $err\" if $err;\n\n\tmy @addrs;\n\tforeach my $ai (@res) {\n\t\tmy ($err, $addr) = Socket::getnameinfo($ai->{addr},\n\t\t\tSocket::NI_NUMERICHOST(), Socket::NIx_NOSERV());\n\t\tdie \"Cannot getnameinfo - $err\" if $err;\n\t\t$addr = '[' . $addr . ']' if $ai->{family} == AF_INET6;\n\t\tpush @addrs, $addr;\n\t}\n\treturn @addrs;\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_intercept_errors.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for http proxy module, proxy_intercept_errors directive.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy rewrite/)->plan(4);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            proxy_pass http://127.0.0.1:8081;\n            proxy_intercept_errors on;\n            error_page 401 500 /intercepted;\n        }\n\n        location = /intercepted {\n            return 200 \"intercepted\\n\";\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        location / {\n            return 404 \"SEE-THIS\";\n        }\n\n        location /500 {\n            return 500;\n        }\n\n        location /auth {\n            add_header WWW-Authenticate foo always;\n            return 401;\n        }\n\n        location /auth-multi {\n            add_header WWW-Authenticate foo always;\n            add_header WWW-Authenticate bar always;\n            return 401;\n        }\n    }\n}\n\nEOF\n\n$t->run();\n\n###############################################################################\n\n# make sure errors without error_page set are not intercepted\n\nlike(http_get('/'), qr/SEE-THIS/, 'not intercepted');\n\n# make sure errors with error_page are intercepted\n\nlike(http_get('/500'), qr/500.*intercepted/s, 'intercepted 500');\nlike(http_get('/auth'), qr/401.*WWW-Authenticate.*intercepted/s,\n\t'intercepted 401');\n\n# make sure multiple WWW-Authenticate headers are returned\n# along with intercepted response (ticket #485)\n\nTODO: {\nlocal $TODO = 'not yet' unless $t->has_version('1.23.0');\n\nlike(http_get('/auth-multi'), qr/401.*WWW-Authenticate: foo.*bar.*intercept/s,\n\t'intercepted 401 multi');\n\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_keepalive.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for proxy with keepalive.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse IO::Socket::INET;\nuse Socket qw/ CRLF /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy upstream_keepalive ssi rewrite/)\n\t->plan(49)->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\nworker_processes 1;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    upstream backend {\n        server 127.0.0.1:8081;\n        keepalive 1;\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        proxy_http_version 1.1;\n        proxy_set_header Connection \"\";\n\n        location / {\n            proxy_pass http://backend;\n        }\n\n        location /unbuffered/ {\n            proxy_pass http://backend;\n            proxy_buffering off;\n        }\n\n        location /inmemory/ {\n            ssi on;\n            rewrite ^ /ssi.html break;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('ssi.html',\n\t'<!--#include virtual=\"/include$request_uri\" set=\"x\" -->' .\n\t'set: <!--#echo var=\"x\" -->');\n\n$t->run_daemon(\\&http_daemon);\n$t->run();\n\n$t->waitforsocket('127.0.0.1:' . port(8081))\n\tor die \"Can't start test backend\";\n\n###############################################################################\n\n# There are 3 mostly independent modes of upstream operation:\n#\n# 1. Buffered, i.e. normal mode with \"proxy_buffering on;\"\n# 2. Unbuffered, i.e. \"proxy_buffering off;\".\n# 3. In memory, i.e. ssi <!--#include ... set -->\n#\n# These all should be tested.\n\nmy ($r, $n);\n\n# buffered\n\nlike($r = http_get('/buffered/length1'), qr/SEE-THIS/, 'buffered');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/buffered/length2'), qr/X-Connection: $n.*SEE/ms, 'buffered 2');\n\nlike($r = http_get('/buffered/chunked1'), qr/SEE-THIS/, 'buffered chunked');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/buffered/chunked2'), qr/X-Connection: $n/,\n\t'buffered chunked 2');\n\nlike($r = http_get('/buffered/complex1'), qr/(0123456789){100}/,\n\t'buffered complex chunked');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/buffered/complex2'), qr/X-Connection: $n/,\n\t'buffered complex chunked 2');\n\nlike($r = http_get('/buffered/chunk01'), qr/200 OK/, 'buffered 0 chunk');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/buffered/chunk02'), qr/X-Connection: $n/, 'buffered 0 chunk 2');\n\nlike($r = http_head('/buffered/length/head1'), qr/(?!SEE-THIS)/,\n\t'buffered head');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_head('/buffered/length/head2'), qr/X-Connection: $n/,\n\t'buffered head 2');\n\nlike($r = http_get('/buffered/empty1'), qr/200 OK/, 'buffered empty');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/buffered/empty2'), qr/X-Connection: $n/, 'buffered empty 2');\n\nlike($r = http_get('/buffered/304nolen1'), qr/304 Not/, 'buffered 304');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/buffered/304nolen2'), qr/X-Connection: $n/, 'buffered 304 2');\n\nlike($r = http_get('/buffered/304len1'), qr/304 Not/,\n\t'buffered 304 with length');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/buffered/304len2'), qr/X-Connection: $n/,\n\t'buffered 304 with length 2');\n\n# unbuffered\n\nlike($r = http_get('/unbuffered/length1'), qr/SEE-THIS/, 'unbuffered');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/unbuffered/length2'), qr/X-Connection: $n/, 'unbuffered 2');\n\nlike($r = http_get('/unbuffered/chunked1'), qr/SEE-THIS/, 'unbuffered chunked');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/unbuffered/chunked2'), qr/X-Connection: $n/,\n\t'unbuffered chunked 2');\n\nlike($r = http_get('/unbuffered/complex1'), qr/(0123456789){100}/,\n\t'unbuffered complex chunked');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/unbuffered/complex2'), qr/X-Connection: $n/,\n\t'unbuffered complex chunked 2');\n\nlike($r = http_get('/unbuffered/chunk01'), qr/200 OK/, 'unbuffered 0 chunk');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/unbuffered/chunk02'), qr/X-Connection: $n/,\n\t'unbuffered 0 chunk 2');\n\nlike($r = http_get('/unbuffered/empty1'), qr/200 OK/, 'unbuffered empty');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/unbuffered/empty2'), qr/X-Connection: $n/,\n\t'unbuffered empty 2');\n\nlike($r = http_head('/unbuffered/length/head1'), qr/(?!SEE-THIS)/,\n\t'unbuffered head');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_head('/unbuffered/length/head2'), qr/X-Connection: $n/,\n\t'unbuffered head 2');\n\nlike($r = http_get('/unbuffered/304nolen1'), qr/304 Not/, 'unbuffered 304');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/unbuffered/304nolen2'), qr/X-Connection: $n/,\n\t'unbuffered 304 2');\n\nlike($r = http_get('/unbuffered/304len1'), qr/304 Not/,\n\t'unbuffered 304 with length');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/unbuffered/304len2'), qr/X-Connection: $n/,\n\t'unbuffered 304 with length 2');\n\n# in memory\n\nlike($r = http_get('/inmemory/length1'), qr/SEE-THIS/, 'inmemory');\n$r =~ m/SEE-THIS(\\d+)/; $n = $1;\nlike(http_get('/inmemory/length2'), qr/SEE-THIS$n/, 'inmemory 2');\n\nlike($r = http_get('/inmemory/empty1'), qr/200 OK/, 'inmemory empty');\n$r =~ m/SEE-THIS(\\d+)/; $n = $1;\nlike(http_get('/inmemory/empty2'), qr/200 OK/, 'inmemory empty 2');\n\nlike($r = http_get('/inmemory/chunked1'), qr/SEE-THIS/, 'inmemory chunked');\n$r =~ m/SEE-THIS(\\d+)/; $n = $1;\nlike(http_get('/inmemory/chunked2'), qr/SEE-THIS$n/, 'inmemory chunked 2');\n\nlike($r = http_get('/inmemory/complex1'), qr/(0123456789){100}/,\n\t'inmemory complex chunked');\n$r =~ m/SEE-THIS(\\d+)/; $n = $1;\nlike(http_get('/inmemory/complex2'), qr/SEE-THIS$n/,\n\t'inmemory complex chunked 2');\n\nlike(http_get('/inmemory/chunk01'), qr/set: $/, 'inmemory 0 chunk');\nlike(http_get('/inmemory/chunk02'), qr/set: $/, 'inmemory 0 chunk 2');\n\n# closed connection tests\n\nlike(http_get('/buffered/closed1'), qr/200 OK/, 'buffered closed 1');\nlike(http_get('/buffered/closed2'), qr/200 OK/, 'buffered closed 2');\nlike(http_get('/unbuffered/closed1'), qr/200 OK/, 'unbuffered closed 1');\nlike(http_get('/unbuffered/closed2'), qr/200 OK/, 'unbuffered closed 2');\nlike(http_get('/inmemory/closed1'), qr/200 OK/, 'inmemory closed 1');\nlike(http_get('/inmemory/closed2'), qr/200 OK/, 'inmemory closed 2');\n\n# check for errors, shouldn't be any\n\nlike(`grep -F '[error]' ${\\($t->testdir())}/error.log`, qr/^$/s, 'no errors');\n\n###############################################################################\n\nsub http_daemon {\n\tmy $server = IO::Socket::INET->new(\n\t\tProto => 'tcp',\n\t\tLocalHost => '127.0.0.1:' . port(8081),\n\t\tListen => 5,\n\t\tReuse => 1\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\tmy $ccount = 0;\n\tmy $rcount = 0;\n\n\t# dumb server which is able to keep connections alive\n\n\twhile (my $client = $server->accept()) {\n\t\tTest::Nginx::log_core('||',\n\t\t\t\"connection from \" . $client->peerhost());\n\t\t$client->autoflush(1);\n\t\t$ccount++;\n\n\t\twhile (1) {\n\t\t\tmy $headers = '';\n\t\t\tmy $uri = '';\n\n\t\t\twhile (<$client>) {\n\t\t\t\tTest::Nginx::log_core('||', $_);\n\t\t\t\t$headers .= $_;\n\t\t\t\tlast if (/^\\x0d?\\x0a?$/);\n\t\t\t}\n\n\t\t\tlast if $headers eq '';\n\t\t\t$rcount++;\n\n\t\t\t$uri = $1 if $headers =~ /^\\S+\\s+([^ ]+)\\s+HTTP/i;\n\n\t\t\tif ($uri =~ m/length/) {\n\t\t\t\tprint $client\n\t\t\t\t\t\"HTTP/1.1 200 OK\" . CRLF .\n\t\t\t\t\t\"X-Request: $rcount\" . CRLF .\n\t\t\t\t\t\"X-Connection: $ccount\" . CRLF .\n\t\t\t\t\t\"Content-Length: 26\" . CRLF . CRLF;\n\t\t\t\tprint $client \"TEST-OK-IF-YOU-SEE-THIS\" .\n\t\t\t\t\tsprintf(\"%03d\", $ccount)\n\t\t\t\t\tunless $headers =~ /^HEAD/i;\n\n\t\t\t} elsif ($uri =~ m/empty/) {\n\t\t\t\tprint $client\n\t\t\t\t\t\"HTTP/1.1 200 OK\" . CRLF .\n\t\t\t\t\t\"X-Request: $rcount\" . CRLF .\n\t\t\t\t\t\"X-Connection: $ccount\" . CRLF .\n\t\t\t\t\t\"Content-Length: 0\" . CRLF . CRLF;\n\n\t\t\t} elsif ($uri =~ m/304nolen/) {\n\t\t\t\tprint $client\n\t\t\t\t\t\"HTTP/1.1 304 Not Modified\" . CRLF .\n\t\t\t\t\t\"X-Request: $rcount\" . CRLF .\n\t\t\t\t\t\"X-Connection: $ccount\" . CRLF . CRLF;\n\n\t\t\t} elsif ($uri =~ m/304len/) {\n\t\t\t\tprint $client\n\t\t\t\t\t\"HTTP/1.1 304 Not Modified\" . CRLF .\n\t\t\t\t\t\"X-Request: $rcount\" . CRLF .\n\t\t\t\t\t\"X-Connection: $ccount\" . CRLF .\n\t\t\t\t\t\"Content-Length: 100\" . CRLF . CRLF;\n\n\t\t\t} elsif ($uri =~ m/chunked/) {\n\t\t\t\tprint $client\n\t\t\t\t\t\"HTTP/1.1 200 OK\" . CRLF .\n\t\t\t\t\t\"X-Request: $rcount\" . CRLF .\n\t\t\t\t\t\"X-Connection: $ccount\" . CRLF .\n\t\t\t\t\t\"Transfer-Encoding: chunked\" . CRLF .\n\t\t\t\t\tCRLF;\n\t\t\t\tprint $client\n\t\t\t\t\t\"1a\" . CRLF .\n\t\t\t\t\t\"TEST-OK-IF-YOU-SEE-THIS\" .\n\t\t\t\t\tsprintf(\"%03d\", $ccount) . CRLF .\n\t\t\t\t\t\"0\" . CRLF . CRLF\n\t\t\t\t\tunless $headers =~ /^HEAD/i;\n\n\t\t\t} elsif ($uri =~ m/complex/) {\n\t\t\t\tprint $client\n\t\t\t\t\t\"HTTP/1.1 200 OK\" . CRLF .\n\t\t\t\t\t\"X-Request: $rcount\" . CRLF .\n\t\t\t\t\t\"X-Connection: $ccount\" . CRLF .\n\t\t\t\t\t\"Transfer-Encoding: chunked\" . CRLF .\n\t\t\t\t\tCRLF;\n\n\t\t\t\tif ($headers !~ /^HEAD/i) {\n\t\t\t\t\tfor my $n (1..100) {\n\t\t\t\t\t\tprint $client\n\t\t\t\t\t\t\t\"a\" . CRLF .\n\t\t\t\t\t\t\t\"0123456789\" . CRLF;\n\t\t\t\t\t\tselect undef, undef, undef, 0.01\n\t\t\t\t\t\t\tif $n % 50 == 0;\n\t\t\t\t\t}\n\t\t\t\t\tprint $client\n\t\t\t\t\t\t\"1a\" . CRLF .\n\t\t\t\t\t\t\"TEST-OK-IF-YOU-SEE-THIS\" .\n\t\t\t\t\t\tsprintf(\"%03d\", $ccount) .\n\t\t\t\t\t\tCRLF .\n\t\t\t\t\t\t\"0\" . CRLF;\n\t\t\t\t\tselect undef, undef, undef, 0.05;\n\t\t\t\t\tprint $client CRLF;\n\t\t\t\t}\n\n\t\t\t} elsif ($uri =~ m/chunk0/) {\n\t\t\t\tprint $client\n\t\t\t\t\t\"HTTP/1.1 200 OK\" . CRLF .\n\t\t\t\t\t\"X-Request: $rcount\" . CRLF .\n\t\t\t\t\t\"X-Connection: $ccount\" . CRLF .\n\t\t\t\t\t\"Transfer-Encoding: chunked\" . CRLF .\n\t\t\t\t\tCRLF;\n\t\t\t\tprint $client\n\t\t\t\t\t\"0\" . CRLF . CRLF\n\t\t\t\t\tunless $headers =~ /^HEAD/i;\n\n\t\t\t} elsif ($uri =~ m/closed/) {\n\t\t\t\tprint $client\n\t\t\t\t\t\"HTTP/1.1 200 OK\" . CRLF .\n\t\t\t\t\t\"X-Request: $rcount\" . CRLF .\n\t\t\t\t\t\"X-Connection: $ccount\" . CRLF .\n\t\t\t\t\t\"Connection: close\" . CRLF .\n\t\t\t\t\t\"Content-Length: 12\" . CRLF . CRLF .\n\t\t\t\t\t\"0123456789\" . CRLF;\n\t\t\t\tlast;\n\n\t\t\t} else {\n\t\t\t\tprint $client\n\t\t\t\t\t\"HTTP/1.1 404 Not Found\" . CRLF .\n\t\t\t\t\t\"X-Request: $rcount\" . CRLF .\n\t\t\t\t\t\"X-Connection: $ccount\" . CRLF .\n\t\t\t\t\t\"Connection: close\" . CRLF . CRLF .\n\t\t\t\t\t\"Oops, '$uri' not found\" . CRLF;\n\t\t\t\tlast;\n\t\t\t}\n\t\t}\n\n\t\tclose $client;\n\t}\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_limit_rate.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for the proxy_limit_rate directive.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy upstream_keepalive/)->plan(4);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    upstream u {\n        server 127.0.0.1:8080;\n        keepalive 1;\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            proxy_pass http://127.0.0.1:8080/data;\n            proxy_buffer_size 4k;\n            proxy_limit_rate 20000;\n            add_header X-Msec $msec;\n        }\n\n        location /keepalive {\n            proxy_http_version 1.1;\n            proxy_set_header Connection \"\";\n            proxy_pass http://u/data;\n            proxy_buffer_size 4k;\n            proxy_limit_rate 20000;\n            add_header X-Msec $msec;\n        }\n\n        location /data {\n        }\n    }\n}\n\nEOF\n\n$t->write_file('data', 'X' x 40000);\n$t->run();\n\n###############################################################################\n\nmy $r = http_get('/');\n\nmy ($t1) = $r =~ /X-Msec: (\\d+)/;\nmy $diff = time() - $t1;\n\n# four chunks are split with three 1s delays\n\ncmp_ok($diff, '>=', 1, 'proxy_limit_rate');\nlike($r, qr/^(XXXXXXXXXX){4000}\\x0d?\\x0a?$/m, 'response body');\n\n# in case keepalive connection was saved with the delayed flag,\n# the read timer used to be a delay timer in the next request\n\nlike(http_get('/keepalive'), qr/200 OK/, 'keepalive');\nlike(http_get('/keepalive'), qr/200 OK/, 'keepalive 2');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_max_temp_file_size.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for http proxy module, proxy_max_temp_file_size directive.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx qw/ :DEFAULT http_content /;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy/)->plan(4);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080 sndbuf=32k;\n        server_name  localhost;\n\n        proxy_buffer_size 4k;\n        proxy_buffers 8 4k;\n\n        location / {\n            proxy_max_temp_file_size 4k;\n            proxy_pass http://127.0.0.1:8081/;\n        }\n\n        location /off/ {\n            proxy_max_temp_file_size 0;\n            proxy_pass http://127.0.0.1:8081/;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        location / { }\n    }\n}\n\nEOF\n\n$t->write_file('1', 'X' x (1024 * 1024));\n$t->run();\n\n###############################################################################\n\n# test that the response is wholly proxied when all event pipe buffers are full\n\nmy $body = http_content(http_get('/1', sleep => 0.4));\nlike($body, qr/^X+$/m, 'no pipe bufs - body');\nis(length($body), 1024 * 1024, 'no pipe bufs - body length');\n\n# also with disabled proxy temp file\n\n$body = http_content(http_get('/off/1', sleep => 0.4));\nlike($body, qr/^X+$/m, 'no temp file - body');\nis(length($body), 1024 * 1024, 'no temp file - body length');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_merge_headers.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for proxy_set_header inheritance.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\nuse Socket qw/ CRLF /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy cache rewrite/)->plan(11)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    proxy_cache_path   %%TESTDIR%%/cache  levels=1:2\n                       keys_zone=NAME:1m;\n\n    proxy_set_header X-Blah \"blah\";\n    proxy_hide_header X-Hidden;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        proxy_cache  NAME;\n\n        location / {\n            proxy_pass    http://127.0.0.1:8081;\n\n            location /nested/ {\n                proxy_pass   http://127.0.0.1:8081;\n                proxy_pass_header X-Pad;\n            }\n        }\n\n        location /no/ {\n            proxy_pass    http://127.0.0.1:8081;\n            proxy_cache   off;\n        }\n\n        location /setbody/ {\n            proxy_pass    http://127.0.0.1:8081;\n            proxy_set_body \"body\";\n        }\n\n        location /passdate/ {\n            proxy_pass    http://127.0.0.1:8082;\n            proxy_pass_header Date;\n            proxy_pass_header Server;\n\n            location /passdate/no/ {\n                proxy_pass   http://127.0.0.1:8082;\n            }\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        location / {\n            add_header X-Hidden \"hidden\";\n            add_header X-Pad \"passed\";\n            return 200 \"ims=$http_if_modified_since;blah=$http_x_blah;\";\n        }\n    }\n}\n\nEOF\n\n$t->run_daemon(\\&http_daemon);\n$t->run();\n\n$t->waitforsocket('127.0.0.1:' . port(8082));\n\n###############################################################################\n\nlike(http_get_ims('/'), qr/ims=;blah=blah;/,\n\t'if-modified-since cleared with cache');\n\nlike(http_get_ims('/no/'), qr/ims=blah;blah=blah;/,\n\t'if-modified-since preserved without cache');\n\nlike(http_get_ims('/setbody/'), qr/blah=blah;/,\n\t'proxy_set_header inherited with proxy_set_body');\n\nunlike(http_get('/'), qr/X-Pad/, 'proxy_pass_header default');\nlike(http_get('/nested/'), qr/X-Pad/, 'proxy_pass_header nested');\nunlike(http_get('/'), qr/X-Hidden/, 'proxy_hide_header inherited');\nunlike(http_get('/nested/'), qr/X-Hidden/, 'proxy_hide_header nested');\n\nlike(http_get('/passdate/'), qr/Date: passed/, 'proxy_pass_header date');\nlike(http_get('/passdate/'), qr/Server: passed/, 'proxy_pass_header server');\nunlike(http_get('/passdate/no/'), qr/Date/, 'proxy_pass_header no date');\nunlike(http_get('/passdate/no/'), qr/Server/, 'proxy_pass_header no server');\n\n###############################################################################\n\nsub http_get_ims {\n\tmy ($url) = @_;\n\treturn http(<<EOF);\nGET $url HTTP/1.0\nHost: localhost\nConnection: close\nIf-Modified-Since: blah\n\nEOF\n}\n\n###############################################################################\n\nsub http_daemon {\n\tmy $server = IO::Socket::INET->new(\n\t\tProto => 'tcp',\n\t\tLocalHost => '127.0.0.1',\n\t\tLocalPort => port(8082),\n\t\tListen => 5,\n\t\tReuse => 1\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\tlocal $SIG{PIPE} = 'IGNORE';\n\n\twhile (my $client = $server->accept()) {\n\t\t$client->autoflush(1);\n\n\t\tmy $headers = '';\n\t\tmy $uri = '';\n\n\t\twhile (<$client>) {\n\t\t\t$headers .= $_;\n\t\t\tlast if (/^\\x0d?\\x0a?$/);\n\t\t}\n\n\t\t$uri = $1 if $headers =~ /^\\S+\\s+([^ ]+)\\s+HTTP/i;\n\n\t\tif ($uri =~ 'no') {\n\t\t\tprint $client\n\t\t\t\t'HTTP/1.0 200 OK' . CRLF . CRLF;\n\n\t\t} else {\n\t\t\tprint $client\n\t\t\t\t'HTTP/1.0 200 OK' . CRLF .\n\t\t\t\t'Date: passed' . CRLF .\n\t\t\t\t'Server: passed' . CRLF . CRLF;\n\t\t}\n\n\t\tclose $client;\n\t}\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_method.t",
    "content": "#!/usr/bin/perl\n\n# (C) Dmitry Lazurkin\n\n# Tests for proxy_method.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy rewrite/)->plan(4)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location /preserve {\n            proxy_pass http://127.0.0.1:8080/get-method;\n        }\n\n        location /const {\n            proxy_pass http://127.0.0.1:8080/get-method;\n            proxy_method POST;\n        }\n\n        location /var {\n            proxy_pass http://127.0.0.1:8080/get-method;\n            proxy_method $arg_method;\n        }\n\n        location /parent {\n            proxy_method POST;\n            location /parent/child {\n                proxy_pass http://127.0.0.1:8080/get-method;\n            }\n        }\n\n        location /get-method {\n            return 200 \"request_method=$request_method\";\n        }\n    }\n}\n\nEOF\n\n$t->run();\n\n###############################################################################\n\nlike(http_get('/preserve'), qr/request_method=GET/,\n\t'proxy_method from request');\n\nlike(http_get('/const'), qr/request_method=POST/,\n\t'proxy_method from constant');\n\nlike(http_get('/var?method=POST'), qr/request_method=POST/,\n\t'proxy_method from variable');\n\nlike(http_get('/parent/child'), qr/request_method=POST/,\n\t'proxy_method from parent');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_next_upstream.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for http proxy module, proxy_next_upstream directive.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy rewrite/)->plan(8);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\nworker_processes 1;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    upstream u {\n        server 127.0.0.1:8081;\n        server 127.0.0.1:8082;\n    }\n\n    upstream u2 {\n        server 127.0.0.1:8081;\n        server 127.0.0.1:8082;\n    }\n\n    upstream u3 {\n        server 127.0.0.1:8081;\n        server 127.0.0.1:8082 down;\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            proxy_pass http://u;\n            proxy_next_upstream http_500 http_404;\n        }\n\n        location /all/ {\n            proxy_pass http://u2;\n            proxy_next_upstream http_500 http_404;\n            error_page 404 /all/404;\n            proxy_intercept_errors on;\n        }\n\n        location /all/404 {\n            return 200 \"$upstream_addr\\n\";\n        }\n\n        location /down {\n            proxy_pass http://u3;\n            proxy_next_upstream http_404;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        location / {\n            return 404;\n        }\n        location /ok {\n            return 200 \"AND-THIS\\n\";\n        }\n        location /500 {\n            return 500;\n        }\n\n        location /all/ {\n            return 404;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8082;\n        server_name  localhost;\n\n        location / {\n            return 200 \"TEST-OK-IF-YOU-SEE-THIS\\n\";\n        }\n\n        location /all/ {\n            return 404;\n        }\n    }\n}\n\nEOF\n\n$t->run();\n\n###############################################################################\n\nmy ($p1, $p2) = (port(8081), port(8082));\n\n# check if both request fallback to a backend\n# which returns valid response\n\nlike(http_get('/'), qr/SEE-THIS/, 'proxy request');\nlike(http_get('/'), qr/SEE-THIS/, 'second request');\n\n# make sure backend isn't switched off after\n# proxy_next_upstream http_404\n\nlike(http_get('/ok') . http_get('/ok'), qr/AND-THIS/, 'not down');\n\n# next upstream on http_500\n\nlike(http_get('/500'), qr/SEE-THIS/, 'request 500');\nlike(http_get('/500'), qr/SEE-THIS/, 'request 500 second');\n\n# make sure backend switched off with http_500\n\nunlike(http_get('/ok') . http_get('/ok'), qr/AND-THIS/, 'down after 500');\n\n# make sure all backends are tried once\n\nlike(http_get('/all/rr'),\n\tqr/^127.0.0.1:($p1, 127.0.0.1:$p2|$p2, 127.0.0.1:$p1)$/mi,\n\t'all tried once');\n\n# make sure backend marked as down doesn't count towards \"no live upstreams\"\n# after all backends are tried with http_404\n\nlike(http_get('/down/'), qr/Not Found/, 'all tried with down');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_next_upstream_tries.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for http proxy module, proxy_next_upstream_tries\n# and proxy_next_upstream_timeout directives.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy rewrite/)->plan(8);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    upstream u {\n        server 127.0.0.1:8081;\n        server 127.0.0.1:8081;\n        server 127.0.0.1:8081;\n    }\n\n    upstream u2 {\n        server 127.0.0.1:8081;\n        server 127.0.0.1:8081 backup;\n        server 127.0.0.1:8081 backup;\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        proxy_next_upstream http_404;\n        proxy_intercept_errors on;\n        error_page 404 /404;\n\n        location /tries {\n            proxy_pass http://u;\n            proxy_next_upstream_tries 2;\n        }\n\n        location /tries/backup {\n            proxy_pass http://u2;\n            proxy_next_upstream_tries 2;\n        }\n\n        location /tries/resolver {\n            resolver 127.0.0.1:%%PORT_8982_UDP%%;\n\n            proxy_pass http://$host:%%PORT_8081%%;\n            proxy_next_upstream_tries 2;\n        }\n\n        location /tries/zero {\n            proxy_pass http://u;\n            proxy_next_upstream_tries 0;\n        }\n\n        location /timeout {\n            proxy_pass http://u/w2;\n            proxy_next_upstream_timeout 3800ms;\n        }\n\n        location /timeout/backup {\n            proxy_pass http://u2/w2;\n            proxy_next_upstream_timeout 3800ms;\n        }\n\n        location /timeout/resolver {\n            resolver 127.0.0.1:%%PORT_8982_UDP%%;\n\n            proxy_pass http://$host:%%PORT_8081%%/w2;\n            proxy_next_upstream_timeout 3800ms;\n        }\n\n        location /timeout/zero {\n            proxy_pass http://u/w;\n            proxy_next_upstream_timeout 0;\n        }\n\n        location /404 {\n            return 200 x${upstream_status}x;\n        }\n    }\n}\n\nEOF\n\n$t->run_daemon(\\&http_daemon, port(8081));\n$t->run_daemon(\\&dns_daemon, port(8982), $t);\n$t->run();\n\n$t->waitforsocket('127.0.0.1:' . port(8081));\n$t->waitforfile($t->testdir . '/' . port(8982));\n\n###############################################################################\n\nlike(http_get('/tries'), qr/x404, 404x/, 'tries');\nlike(http_get('/tries/backup'), qr/x404, 404x/, 'tries backup');\nlike(http_get('/tries/resolver'), qr/x404, 404x/, 'tries resolved');\nlike(http_get('/tries/zero'), qr/x404, 404, 404x/, 'tries zero');\n\n# two tries fit into 1.9s\n\nSKIP: {\nskip 'long tests', 4 unless $ENV{TEST_NGINX_UNSAFE};\n\nlike(http_get('/timeout'), qr/x404, 404x/, 'timeout');\nlike(http_get('/timeout/backup'), qr/x404, 404x/, 'timeout backup');\nlike(http_get('/timeout/resolver'), qr/x404, 404x/, 'timeout resolved');\nlike(http_get('/timeout/zero'), qr/x404, 404, 404x/, 'timeout zero');\n\n}\n\n###############################################################################\n\nsub http_daemon {\n\tmy ($port) = @_;\n\n\tmy $server = IO::Socket::INET->new(\n\t\tProto => 'tcp',\n\t\tLocalHost => '127.0.0.1',\n\t\tLocalPort => $port,\n\t\tListen => 5,\n\t\tReuse => 1\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\tlocal $SIG{PIPE} = 'IGNORE';\n\n\twhile (my $client = $server->accept()) {\n\t\t$client->autoflush(1);\n\n\t\tmy $headers = '';\n\t\tmy $uri = '';\n\n\t\twhile (<$client>) {\n\t\t\t$headers .= $_;\n\t\t\tlast if (/^\\x0d?\\x0a?$/);\n\t\t}\n\n\t\tnext if $headers eq '';\n\n\t\t$uri = $1 if $headers =~ /^\\S+\\s+([^ ]+)\\s+HTTP/i;\n\n\t\tif ($uri eq '/w') {\n\t\t\tTest::Nginx::log_core('||', \"$port: sleep(1)\");\n\t\t\tselect undef, undef, undef, 1;\n\t\t}\n\n\t\tif ($uri eq '/w2') {\n\t\t\tTest::Nginx::log_core('||', \"$port: sleep(2)\");\n\t\t\tselect undef, undef, undef, 2;\n\t\t}\n\n\t\tTest::Nginx::log_core('||', \"$port: response, 404\");\n\t\tprint $client <<EOF;\nHTTP/1.1 404 Not Found\nConnection: close\n\nEOF\n\n\t} continue {\n\t\tclose $client;\n\t}\n}\n\nsub reply_handler {\n\tmy ($recv_data) = @_;\n\n\tmy (@name, @rdata);\n\n\tuse constant NOERROR\t=> 0;\n\tuse constant A\t\t=> 1;\n\tuse constant IN\t\t=> 1;\n\n\t# default values\n\n\tmy ($hdr, $rcode, $ttl) = (0x8180, NOERROR, 3600);\n\n\t# decode name\n\n\tmy ($len, $offset) = (undef, 12);\n\twhile (1) {\n\t\t$len = unpack(\"\\@$offset C\", $recv_data);\n\t\tlast if $len == 0;\n\t\t$offset++;\n\t\tpush @name, unpack(\"\\@$offset A$len\", $recv_data);\n\t\t$offset += $len;\n\t}\n\n\t$offset -= 1;\n\tmy ($id, $type, $class) = unpack(\"n x$offset n2\", $recv_data);\n\n\t@rdata = map { rd_addr($ttl, '127.0.0.1') } (1 .. 3) if $type == A;\n\n\t$len = @name;\n\tpack(\"n6 (C/a*)$len x n2\", $id, $hdr | $rcode, 1, scalar @rdata,\n\t\t0, 0, @name, $type, $class) . join('', @rdata);\n}\n\nsub rd_addr {\n\tmy ($ttl, $addr) = @_;\n\n\tmy $code = 'split(/\\./, $addr)';\n\n\treturn pack 'n3N', 0xc00c, A, IN, $ttl if $addr eq '';\n\n\tpack 'n3N nC4', 0xc00c, A, IN, $ttl, eval \"scalar $code\", eval($code);\n}\n\nsub dns_daemon {\n\tmy ($port, $t) = @_;\n\n\tmy ($data, $recv_data);\n\tmy $socket = IO::Socket::INET->new(\n\t\tLocalAddr => '127.0.0.1',\n\t\tLocalPort => $port,\n\t\tProto => 'udp',\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\t# signal we are ready\n\n\topen my $fh, '>', $t->testdir() . '/' . $port;\n\tclose $fh;\n\n\twhile (1) {\n\t\t$socket->recv($recv_data, 65536);\n\t\t$data = reply_handler($recv_data);\n\t\t$socket->send($data);\n\t}\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_noclose.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Test for http backend not closing connection properly after sending full\n# reply.  This is in fact backend bug, but it seems common, and anyway\n# correct handling is required to support persistent connections.\n\n# There are actually 2 nginx problems here:\n#\n# 1. It doesn't send reply in-time even if got Content-Length and all the data.\n#\n# 2. If upstream times out some data may be left in input buffer and won't be\n#    sent to downstream.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse IO::Select;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy/)->plan(4);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            proxy_pass http://127.0.0.1:8081;\n            proxy_read_timeout 2s;\n        }\n\n        location /uselen {\n            proxy_pass http://127.0.0.1:8081;\n\n            # test will wait only 2s for reply, we it will fail if\n            # Content-Length not used as a hint\n\n            proxy_read_timeout 10s;\n        }\n    }\n}\n\nEOF\n\n$t->run_daemon(\\&http_noclose_daemon);\n$t->run()->waitforsocket('127.0.0.1:' . port(8081));\n\n###############################################################################\n\nlike(http_get('/'), qr/SEE-THIS/, 'request to bad backend');\nlike(http_get('/multi'), qr/AND-THIS/, 'bad backend - multiple packets');\nlike(http_get('/uselen'), qr/SEE-THIS/, 'content-length actually used');\n\nTODO: {\nlocal $TODO = 'not yet';\nlocal $SIG{__WARN__} = sub {};\n\nlike(http_get('/nolen'), qr/SEE-THIS/, 'bad backend - no content length');\n\n}\n\n###############################################################################\n\nsub http_noclose_daemon {\n\tmy $server = IO::Socket::INET->new(\n\t\tProto => 'tcp',\n\t\tLocalAddr => '127.0.0.1:' . port(8081),\n\t\tListen => 5,\n\t\tReuse => 1\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\tlocal $SIG{PIPE} = 'IGNORE';\n\n\twhile (my $client = $server->accept()) {\n\t\t$client->autoflush(1);\n\n\t\tmy $multi = 0;\n\t\tmy $nolen = 0;\n\n\t\twhile (<$client>) {\n\t\t\t$multi = 1 if /multi/;\n\t\t\t$nolen = 1 if /nolen/;\n\t\t\tlast if (/^\\x0d?\\x0a?$/);\n\t\t}\n\n\t\tif ($nolen) {\n\n\t\t\tprint $client <<'EOF';\nHTTP/1.1 200 OK\nConnection: close\n\nTEST-OK-IF-YOU-SEE-THIS\nEOF\n\t\t} elsif ($multi) {\n\n\t\t\tprint $client <<\"EOF\";\nHTTP/1.1 200 OK\nContent-Length: 32\nConnection: close\n\nTEST-OK-IF-YOU-SEE-THIS\nEOF\n\n\t\t\tselect undef, undef, undef, 0.1;\n\t\t\tprint $client 'AND-THIS';\n\n\t\t} else {\n\n\t\t\tprint $client <<\"EOF\";\nHTTP/1.1 200 OK\nContent-Length: 24\nConnection: close\n\nTEST-OK-IF-YOU-SEE-THIS\nEOF\n\t\t}\n\n\t\tmy $select = IO::Select->new($client);\n\t\t$select->can_read(10);\n\t\tclose $client;\n\t}\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_non_idempotent.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n# (C) Nginx, Inc.\n\n# Tests for proxy_next_upstream non_idempotent.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy rewrite upstream_keepalive/)\n\t->plan(8);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    upstream u {\n        server 127.0.0.1:8081 max_fails=0;\n        server 127.0.0.1:8081 max_fails=0;\n    }\n\n    upstream uk {\n        server 127.0.0.1:8081 max_fails=0;\n        server 127.0.0.1:8081 max_fails=0;\n        keepalive 10;\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        add_header X-IP $upstream_addr always;\n\n        location / {\n            proxy_pass http://u;\n            proxy_next_upstream error timeout http_404;\n        }\n\n        location /non {\n            proxy_pass http://u;\n            proxy_next_upstream error timeout non_idempotent;\n        }\n\n        location /keepalive {\n            proxy_pass http://uk;\n            proxy_next_upstream error timeout;\n            proxy_http_version 1.1;\n            proxy_set_header Connection \"\";\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        location / {\n            return 444;\n        }\n\n        location /404 {\n            return 404 SEE-THIS;\n        }\n\n        location /keepalive/establish {\n            return 204;\n        }\n    }\n}\n\nEOF\n\n$t->run();\n\n###############################################################################\n\n# non-idempotent requests should not be retried by default\n# if a request has been sent to a backend\n\nlike(http_get('/'), qr/X-IP: (\\S+), \\1\\x0d?$/m, 'get');\nlike(http_post('/'), qr/X-IP: (\\S+)\\x0d?$/m, 'post');\n\n# non-idempotent requests should not be retried by default,\n# in particular, not emit builtin error page due to next upstream\n\nlike(http_get('/404'), qr/X-IP: (\\S+), \\1.*SEE-THIS/s, 'get 404');\nlike(http_post('/404'), qr/X-IP: (\\S++)(?! ).*SEE-THIS/s, 'post 404');\n\n# with \"proxy_next_upstream non_idempotent\" there is no\n# difference between idempotent and non-idempotent requests,\n# non-idempotent requests are retried as usual\n\nlike(http_get('/non'), qr/X-IP: (\\S+), \\1\\x0d?$/m, 'get non_idempotent');\nlike(http_post('/non'), qr/X-IP: (\\S+), \\1\\x0d?$/m, 'post non_idempotent');\n\n# cached connections follow the same rules\n\nlike(http_get('/keepalive/establish'), qr/204 No Content/m, 'keepalive');\nlike(http_post('/keepalive/drop'), qr/X-IP: (\\S+)\\x0d?$/m, 'keepalive post');\n\n###############################################################################\n\nsub http_post {\n\tmy ($uri, %extra) = @_;\n\tmy $cl = $extra{cl} || 0;\n\n\thttp(<<\"EOF\");\nPOST $uri HTTP/1.0\nContent-Length: $cl\n\nEOF\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_pass_request.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for proxy_pass_request_headers, proxy_pass_request_body directives.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy/)->plan(3);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        proxy_pass_request_headers off;\n\n        location / {\n            proxy_pass http://127.0.0.1:8081;\n        }\n\n        location /body {\n            proxy_pass http://127.0.0.1:8081;\n            proxy_pass_request_headers on;\n            proxy_pass_request_body off;\n        }\n\n        location /both {\n            proxy_pass http://127.0.0.1:8081;\n            proxy_pass_request_headers off;\n            proxy_pass_request_body off;\n        }\n    }\n}\n\nEOF\n\n$t->run_daemon(\\&http_daemon);\n$t->run()->waitforsocket('127.0.0.1:' . port(8081));\n\n###############################################################################\n\nlike(get('/', 'foo', 'bar'), qr/Header: none.*Body: bar/s, 'no headers');\nlike(get('/body', 'foo', 'bar'), qr/Header: foo.*Body: none/s, 'no body');\nlike(get('/both', 'foo', 'bar'), qr/Header: none.*Body: none/s, 'both');\n\n###############################################################################\n\nsub get {\n\tmy ($uri, $header, $body) = @_;\n\tmy $cl = length(\"$body\\n\");\n\n\thttp(<<EOF);\nGET $uri HTTP/1.0\nHost: localhost\nX-Header: $header\nContent-Length: $cl\n\n$body\nEOF\n}\n\nsub http_daemon {\n\tmy $server = IO::Socket::INET->new(\n\t\tProto => 'tcp',\n\t\tLocalHost => '127.0.0.1:' . port(8081),\n\t\tListen => 5,\n\t\tReuse => 1\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\tlocal $SIG{PIPE} = 'IGNORE';\n\n\twhile (my $client = $server->accept()) {\n\t\t$client->autoflush(1);\n\n\t\tmy $r = '';\n\n\t\teval {\n\t\t\tlocal $SIG{ALRM} = sub { die \"timeout\\n\" };\n\t\t\tlocal $SIG{PIPE} = sub { die \"sigpipe\\n\" };\n\t\t\talarm(2);\n\t\t\t$client->sysread($r, 4096);\n\t\t\talarm(0);\n\t\t};\n\t\talarm(0);\n\t\tif ($@) {\n\t\t\tlog_in(\"died: $@\");\n\t\t\tnext;\n\t\t}\n\n\t\tnext if $r eq '';\n\n\t\tTest::Nginx::log_core('|| <<', $r);\n\n\t\tmy $header = $r =~ /x-header: (\\S+)/i && $1 || 'none';\n\t\tmy $body = $r =~ /\\x0d\\x0a?\\x0d\\x0a?(.+)/ && $1 || 'none';\n\n\t\tprint $client <<\"EOF\";\nHTTP/1.1 200 OK\nConnection: close\nX-Header: $header\nX-Body: $body\n\nEOF\n\n\t\tclose $client;\n\t}\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_protocol.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for haproxy protocol.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse Socket qw/ CRLF /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http access realip/);\n\n$t->write_file_expand('nginx.conf', <<'EOF')->plan(24);\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    log_format pp $remote_addr:$remote_port;\n\n    add_header X-IP $remote_addr!$remote_port;\n    add_header X-PP $proxy_protocol_addr!$proxy_protocol_port;\n    add_header X-PPS $proxy_protocol_server_addr!$proxy_protocol_server_port;\n    server {\n        listen       127.0.0.1:8080 proxy_protocol;\n        server_name  localhost;\n\n        set_real_ip_from  127.0.0.1/32;\n\n        location /pp {\n            real_ip_header proxy_protocol;\n            error_page 404 =200 /t1;\n\n            location /pp_4 {\n                deny 192.0.2.1/32;\n                access_log %%TESTDIR%%/pp4.log pp;\n            }\n\n            location /pp_6 {\n                deny 2001:DB8::1/128;\n                access_log %%TESTDIR%%/pp6.log pp;\n            }\n        }\n\n        location / { }\n    }\n}\n\nEOF\n\n$t->write_file('t1', 'SEE-THIS');\n$t->run();\n\n###############################################################################\n\nmy $tcp4 = 'PROXY TCP4 192.0.2.1 192.0.2.2 123 5678' . CRLF;\nmy $tcp6 = 'PROXY TCP6 2001:Db8::1 2001:Db8::2 123 5678' . CRLF;\nmy $unk1 = 'PROXY UNKNOWN' . CRLF;\nmy $unk2 = 'PROXY UNKNOWN 1 2 3 4 5 6' . CRLF;\nmy $r;\n\n# no realip, just PROXY header parsing\n\n$r = pp_get('/t1', $tcp4);\nlike($r, qr/SEE-THIS/, 'tcp4 request');\nlike($r, qr/X-PP: 192.0.2.1!123\\x0d/, 'tcp4 proxy');\nlike($r, qr/X-PPS: 192.0.2.2!5678\\x0d/, 'tcp4 proxy server');\nunlike($r, qr/X-IP: (192.0.2.1|[^!]+!123\\x0d)/, 'tcp4 client');\n\n$r = pp_get('/t1', $tcp6);\nlike($r, qr/SEE-THIS/, 'tcp6 request');\nlike($r, qr/X-PP: 2001:DB8::1!123\\x0d/i, 'tcp6 proxy');\nlike($r, qr/X-PPS: 2001:DB8::2!5678\\x0d/i, 'tcp6 proxy server');\nunlike($r, qr/X-IP: (2001:DB8::1|[^!]+!123\\x0d)/i, 'tcp6 client');\n\n$r = pp_get('/t1', $unk1);\nlike($r, qr/SEE-THIS/, 'unknown request 1');\nlike($r, qr/X-PP: !\\x0d/, 'unknown proxy 1');\nlike($r, qr/X-PPS: !\\x0d/, 'unknown proxy server 1');\n\n$r = pp_get('/t1', $unk2);\nlike($r, qr/SEE-THIS/, 'unknown request 2');\nlike($r, qr/X-PP: !\\x0d/, 'unknown proxy 2');\nlike($r, qr/X-PPS: !\\x0d/, 'unknown proxy server 2');\n\n# realip\n\n$r = pp_get('/pp', $tcp4);\nlike($r, qr/SEE-THIS/, 'tcp4 request realip');\nlike($r, qr/X-PP: 192.0.2.1!123\\x0d/, 'tcp4 proxy realip');\nlike($r, qr/X-IP: 192.0.2.1!123\\x0d/, 'tcp4 client realip');\n\n$r = pp_get('/pp', $tcp6);\nlike($r, qr/SEE-THIS/, 'tcp6 request realip');\nlike($r, qr/X-PP: 2001:DB8::1!123\\x0d/i, 'tcp6 proxy realip');\nlike($r, qr/X-IP: 2001:DB8::1!123\\x0d/i, 'tcp6 client realip');\n\n# access\n\n$r = pp_get('/pp_4', $tcp4);\nlike($r, qr/403 Forbidden/, 'tcp4 access');\n\n$r = pp_get('/pp_6', $tcp6);\nlike($r, qr/403 Forbidden/, 'tcp6 access');\n\n# client address in access.log\n\n$t->stop();\n\nis($t->read_file('pp4.log'), \"192.0.2.1:123\\n\", 'tcp4 log');\nis($t->read_file('pp6.log'), \"2001:db8::1:123\\n\", 'tcp6 log');\n\n###############################################################################\n\nsub pp_get {\n\tmy ($url, $proxy) = @_;\n\treturn http($proxy . <<EOF);\nGET $url HTTP/1.0\nHost: localhost\n\nEOF\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_protocol2.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for haproxy protocol.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http access realip/);\n\n$t->write_file_expand('nginx.conf', <<'EOF')->plan(28);\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    log_format pp $remote_addr:$remote_port;\n\n    add_header X-IP $remote_addr!$remote_port;\n    add_header X-PP $proxy_protocol_addr!$proxy_protocol_port;\n    add_header X-PPS $proxy_protocol_server_addr!$proxy_protocol_server_port;\n    server {\n        listen       127.0.0.1:8080 proxy_protocol;\n        server_name  localhost;\n\n        set_real_ip_from  127.0.0.1/32;\n\n        location /pp {\n            real_ip_header proxy_protocol;\n            error_page 404 =200 /t1;\n\n            location /pp_4 {\n                deny 192.0.2.1/32;\n                access_log %%TESTDIR%%/pp4.log pp;\n            }\n\n            location /pp_6 {\n                deny 2001:DB8::1/128;\n                access_log %%TESTDIR%%/pp6.log pp;\n            }\n        }\n\n        location / { }\n    }\n}\n\nEOF\n\n$t->write_file('t1', 'SEE-THIS');\n$t->run();\n\n###############################################################################\n\nmy $p = pack(\"N3C\", 0x0D0A0D0A, 0x000D0A51, 0x5549540A, 0x21);\nmy $tcp4 = $p . pack(\"CnN2n2\", 0x11, 12, 0xc0000201, 0xc0000202, 123, 5678);\nmy $tcp6 = $p . pack(\"CnNx8NNx8Nn2\", 0x21, 36,\n\t0x20010db8, 0x00000001, 0x20010db8, 0x00000002, 123, 5678);\nmy $tlv = $p . pack(\"CnN2n2x9\", 0x11, 21, 0xc0000201, 0xc0000202, 123, 5678);\nmy $unk1 = $p . pack(\"Cxx\", 0x01);\nmy $unk2 = $p . pack(\"CnC4\", 0x41, 4, 1, 2, 3, 4);\nmy $r;\n\n# no realip, just PROXY header parsing\n\n$r = pp_get('/t1', $tcp4);\nlike($r, qr/SEE-THIS/, 'tcp4 request');\nlike($r, qr/X-PP: 192.0.2.1!123\\x0d/, 'tcp4 proxy');\nlike($r, qr/X-PPS: 192.0.2.2!5678\\x0d/, 'tcp4 proxy server');\nunlike($r, qr/X-IP: (192.0.2.1|[^!]+!123\\x0d)/, 'tcp4 client');\n\n$r = pp_get('/t1', $tcp6);\nlike($r, qr/SEE-THIS/, 'tcp6 request');\nlike($r, qr/X-PP: 2001:DB8::1!123\\x0d/i, 'tcp6 proxy');\nlike($r, qr/X-PPS: 2001:DB8::2!5678\\x0d/i, 'tcp6 proxy server');\nunlike($r, qr/X-IP: (2001:DB8::1|[^!]+!123\\x0d)/i, 'tcp6 client');\n\n$r = pp_get('/t1', $tlv);\nlike($r, qr/SEE-THIS/, 'tlv request');\nlike($r, qr/X-PP: 192.0.2.1!123\\x0d/, 'tlv proxy');\nlike($r, qr/X-PPS: 192.0.2.2!5678\\x0d/, 'tlv proxy server');\nunlike($r, qr/X-IP: (192.0.2.1|[^!]+!123\\x0d)/, 'tlv client');\n\n$r = pp_get('/t1', $unk1);\nlike($r, qr/SEE-THIS/, 'unknown request 1');\nlike($r, qr/X-PP: !\\x0d/, 'unknown proxy 1');\nlike($r, qr/X-PPS: !\\x0d/, 'unknown proxy server 1');\n\n$r = pp_get('/t1', $unk2);\nlike($r, qr/SEE-THIS/, 'unknown request 2');\nlike($r, qr/X-PP: !\\x0d/, 'unknown proxy 2');\nlike($r, qr/X-PPS: !\\x0d/, 'unknown proxy server 2');\n\n# realip\n\n$r = pp_get('/pp', $tcp4);\nlike($r, qr/SEE-THIS/, 'tcp4 request realip');\nlike($r, qr/X-PP: 192.0.2.1!123\\x0d/, 'tcp4 proxy realip');\nlike($r, qr/X-IP: 192.0.2.1!123\\x0d/, 'tcp4 client realip');\n\n$r = pp_get('/pp', $tcp6);\nlike($r, qr/SEE-THIS/, 'tcp6 request realip');\nlike($r, qr/X-PP: 2001:DB8::1!123\\x0d/i, 'tcp6 proxy realip');\nlike($r, qr/X-IP: 2001:DB8::1!123\\x0d/i, 'tcp6 client realip');\n\n# access\n\n$r = pp_get('/pp_4', $tcp4);\nlike($r, qr/403 Forbidden/, 'tcp4 access');\n\n$r = pp_get('/pp_6', $tcp6);\nlike($r, qr/403 Forbidden/, 'tcp6 access');\n\n# client address in access.log\n\n$t->stop();\n\nis($t->read_file('pp4.log'), \"192.0.2.1:123\\n\", 'tcp4 log');\nis($t->read_file('pp6.log'), \"2001:db8::1:123\\n\", 'tcp6 log');\n\n###############################################################################\n\nsub pp_get {\n\tmy ($url, $proxy) = @_;\n\treturn http($proxy . <<EOF);\nGET $url HTTP/1.0\nHost: localhost\n\nEOF\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_protocol2_port.t",
    "content": "#!/usr/bin/perl\n\n# (C) Andrey Zelenkov\n# (C) Nginx, Inc.\n\n# Tests for proxy_protocol_port variable.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http realip/);\n\n$t->write_file_expand('nginx.conf', <<'EOF')->plan(8);\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    log_format port $proxy_protocol_port;\n\n    server {\n        listen       127.0.0.1:8080 proxy_protocol;\n        server_name  localhost;\n\n        add_header X-PP-Port $proxy_protocol_port;\n        add_header X-Remote-Port $remote_port;\n\n        location /pp {\n            real_ip_header proxy_protocol;\n            error_page 404 =200 /t;\n\n            location /pp/real {\n                set_real_ip_from  127.0.0.1/32;\n            }\n        }\n\n        location /log {\n            access_log %%TESTDIR%%/port.log port;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('t', 'SEE-THIS');\n$t->run();\n\n###############################################################################\n\nmy $p = pack(\"N3C\", 0x0D0A0D0A, 0x000D0A51, 0x5549540A, 0x21);\nmy $tcp4 = $p . pack(\"CnN2n2\", 0x11, 12, 0xc0000201, 0xc0000202, 123, 5678);\nmy $tcp6 = $p . pack(\"CnNx8NNx8Nn2\", 0x21, 36,\n\t0x20010db8, 0x00000001, 0x20010db8, 0x00000002, 123, 5678);\nmy $unk = $p . pack(\"CnC4\", 0x44, 4, 1, 2, 3, 4);\n\n# realip\n\nlike(pp_get('/pp', $tcp4), qr/X-PP-Port: 123\\x0d/, 'pp port tcp4');\nlike(pp_get('/pp', $tcp6), qr/X-PP-Port: 123\\x0d/, 'pp port tcp6');\nunlike(pp_get('/pp', $unk), qr/X-PP-Port/, 'pp port unknown');\n\n# remote_port\n\nlike(pp_get('/pp/real', $tcp4), qr/X-Remote-Port: 123\\x0d/, 'remote port tcp4');\nunlike(pp_get('/pp', $tcp4), qr/X-Remote-Port: 123\\x0d/, 'no remote port tcp4');\nlike(pp_get('/pp/real', $tcp6), qr/X-Remote-Port: 123\\x0d/, 'remote port tcp6');\nunlike(pp_get('/pp', $tcp6), qr/X-Remote-Port: 123\\x0d/, 'no remote port tcp6');\n\n# log\n\npp_get('/log', $tcp4);\n\n$t->stop();\n\nmy $log = $t->read_file('/port.log');\nchomp $log;\n\nis($log, 123, 'pp port log');\n\n###############################################################################\n\nsub pp_get {\n\tmy ($url, $proxy) = @_;\n\treturn http($proxy . <<EOF);\nGET $url HTTP/1.0\nHost: localhost\n\nEOF\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_protocol2_server.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for haproxy protocol.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http access realip/);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    log_format pp $remote_addr:$remote_port;\n\n    add_header X-IP $remote_addr!$remote_port;\n    add_header X-PP $proxy_protocol_addr!$proxy_protocol_port;\n    add_header X-PPS $proxy_protocol_server_addr!$proxy_protocol_server_port;\n\n    server {\n        listen       127.0.0.1:8080 proxy_protocol;\n        server_name  localhost;\n\n        set_real_ip_from  127.0.0.1/32;\n\n        location /pp {\n            real_ip_header proxy_protocol;\n            error_page 404 =200 /t1;\n\n            location /pp_4 {\n                deny 192.0.2.1/32;\n                access_log %%TESTDIR%%/pp4.log pp;\n            }\n\n            location /pp_6 {\n                deny 2001:DB8::1/128;\n                access_log %%TESTDIR%%/pp6.log pp;\n            }\n        }\n\n        location / { }\n    }\n}\n\nEOF\n\n$t->write_file('t1', 'SEE-THIS');\n$t->run()->plan(28);\n\n###############################################################################\n\nmy $p = pack(\"N3C\", 0x0D0A0D0A, 0x000D0A51, 0x5549540A, 0x21);\nmy $tcp4 = $p . pack(\"CnN2n2\", 0x11, 12, 0xc0000201, 0xc0000202, 123, 567);\nmy $tcp6 = $p . pack(\"CnNx8NNx8Nn2\", 0x21, 36,\n\t0x20010db8, 0x00000001, 0x20010db8, 0x00000002, 123, 567);\nmy $tlv = $p . pack(\"CnN2n2x9\", 0x11, 21, 0xc0000201, 0xc0000202, 123, 567);\nmy $unk1 = $p . pack(\"Cxx\", 0x01);\nmy $unk2 = $p . pack(\"CnC4\", 0x41, 4, 1, 2, 3, 4);\nmy $r;\n\n# no realip, just PROXY header parsing\n\n$r = pp_get('/t1', $tcp4);\nlike($r, qr/SEE-THIS/, 'tcp4 request');\nlike($r, qr/X-PP: 192.0.2.1!123\\x0d/, 'tcp4 proxy');\nlike($r, qr/X-PPS: 192.0.2.2!567\\x0d/, 'tcp4 proxy server');\nunlike($r, qr/X-IP: (192.0.2.1|[^!]+!123\\x0d)/, 'tcp4 client');\n\n$r = pp_get('/t1', $tcp6);\nlike($r, qr/SEE-THIS/, 'tcp6 request');\nlike($r, qr/X-PP: 2001:DB8::1!123\\x0d/i, 'tcp6 proxy');\nlike($r, qr/X-PPS: 2001:DB8::2!567\\x0d/i, 'tcp6 proxy server');\nunlike($r, qr/X-IP: (2001:DB8::1|[^!]+!123\\x0d)/i, 'tcp6 client');\n\n$r = pp_get('/t1', $tlv);\nlike($r, qr/SEE-THIS/, 'tlv request');\nlike($r, qr/X-PP: 192.0.2.1!123\\x0d/, 'tlv proxy');\nlike($r, qr/X-PPS: 192.0.2.2!567\\x0d/, 'tlv proxy server');\nunlike($r, qr/X-IP: (192.0.2.1|[^!]+!123\\x0d)/, 'tlv client');\n\n$r = pp_get('/t1', $unk1);\nlike($r, qr/SEE-THIS/, 'unknown request 1');\nlike($r, qr/X-PP: !\\x0d/, 'unknown proxy 1');\nlike($r, qr/X-PPS: !\\x0d/, 'unknown proxy server 1');\n\n$r = pp_get('/t1', $unk2);\nlike($r, qr/SEE-THIS/, 'unknown request 2');\nlike($r, qr/X-PP: !\\x0d/, 'unknown proxy 2');\nlike($r, qr/X-PPS: !\\x0d/, 'unknown proxy server 2');\n\n# realip\n\n$r = pp_get('/pp', $tcp4);\nlike($r, qr/SEE-THIS/, 'tcp4 request realip');\nlike($r, qr/X-PP: 192.0.2.1!123\\x0d/, 'tcp4 proxy realip');\nlike($r, qr/X-IP: 192.0.2.1!123\\x0d/, 'tcp4 client realip');\n\n$r = pp_get('/pp', $tcp6);\nlike($r, qr/SEE-THIS/, 'tcp6 request realip');\nlike($r, qr/X-PP: 2001:DB8::1!123\\x0d/i, 'tcp6 proxy realip');\nlike($r, qr/X-IP: 2001:DB8::1!123\\x0d/i, 'tcp6 client realip');\n\n# access\n\n$r = pp_get('/pp_4', $tcp4);\nlike($r, qr/403 Forbidden/, 'tcp4 access');\n\n$r = pp_get('/pp_6', $tcp6);\nlike($r, qr/403 Forbidden/, 'tcp6 access');\n\n# client address in access.log\n\n$t->stop();\n\nis($t->read_file('pp4.log'), \"192.0.2.1:123\\n\", 'tcp4 log');\nis($t->read_file('pp6.log'), \"2001:db8::1:123\\n\", 'tcp6 log');\n\n###############################################################################\n\nsub pp_get {\n\tmy ($url, $proxy) = @_;\n\treturn http($proxy . <<EOF);\nGET $url HTTP/1.0\nHost: localhost\n\nEOF\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_protocol2_tlv.t",
    "content": "#!/usr/bin/perl\n\n# (C) Roman Arutyunyan\n# (C) Eugene Grebenschikov\n# (C) Nginx, Inc.\n\n# Tests for variables for proxy protocol v2 TLVs.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http map/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    map $proxy_protocol_tlv_ssl $binary_present {\n        \"~\\\\x00\" \"true\";\n    }\n\n    add_header X-ALPN\n        $proxy_protocol_tlv_alpn-$proxy_protocol_tlv_0x01;\n    add_header X-AUTHORITY\n        $proxy_protocol_tlv_authority-$proxy_protocol_tlv_0x02;\n    add_header X-UNIQUE-ID\n        $proxy_protocol_tlv_unique_id-$proxy_protocol_tlv_0x05;\n    add_header X-NETNS\n        $proxy_protocol_tlv_netns-$proxy_protocol_tlv_0x30;\n    add_header X-SSL-VERIFY\n        $proxy_protocol_tlv_ssl_verify;\n    add_header X-SSL-VERSION\n        $proxy_protocol_tlv_ssl_version-$proxy_protocol_tlv_ssl_0x21;\n    add_header X-SSL-CN\n        $proxy_protocol_tlv_ssl_cn-$proxy_protocol_tlv_ssl_0x22;\n    add_header X-SSL-CIPHER\n        $proxy_protocol_tlv_ssl_cipher-$proxy_protocol_tlv_ssl_0x23;\n    add_header X-SSL-SIG-ALG\n        $proxy_protocol_tlv_ssl_sig_alg-$proxy_protocol_tlv_ssl_0x24;\n    add_header X-SSL-KEY-ALG\n        $proxy_protocol_tlv_ssl_key_alg-$proxy_protocol_tlv_ssl_0x25;\n    add_header X-TLV-CRC32C\n        $proxy_protocol_tlv_0x3;\n    add_header X-TLV-CUSTOM\n        $proxy_protocol_tlv_0x000ae;\n    add_header X-TLV-X\n        $proxy_protocol_tlv_0x000e-$proxy_protocol_tlv_0x0f;\n    add_header X-SSL-BINARY\n        $binary_present;\n\n    server {\n        listen       127.0.0.1:8080 proxy_protocol;\n        server_name  localhost;\n\n        location / { }\n    }\n}\n\nEOF\n\n$t->write_file('t1', 'SEE-THIS');\n$t->try_run('no proxy_protocol tlv')->plan(14);\n\n###############################################################################\n\nmy $tlv = pp2_create_tlv(0x1, \"ALPN1\");\n$tlv .= pp2_create_tlv(0x2, \"localhost\");\n$tlv .= pp2_create_tlv(0x3, \"4321\");\n$tlv .= pp2_create_tlv(0x5, \"UNIQQ\");\n\nmy $sub = pp2_create_tlv(0x21, \"TLSv1.2\");\n$sub .= pp2_create_tlv(0x22, \"example.com\");\n$sub .= pp2_create_tlv(0x23, \"AES256-SHA\");\n$sub .= pp2_create_tlv(0x24, \"SHA1\");\n$sub .= pp2_create_tlv(0x25, \"RSA512\");\nmy $ssl = pp2_create_ssl(0x01, 255, $sub);\n$tlv .= pp2_create_tlv(0x20, $ssl);\n\n$tlv .= pp2_create_tlv(0x30, \"NETNS\");\n$tlv .= pp2_create_tlv(0xae, \"12345\");\nmy $p = pp2_create($tlv);\n\nmy $r = pp_get('/t1', $p);\nlike($r, qr/X-ALPN: ALPN1-ALPN1\\x0d?$/m, 'ALPN');\nlike($r, qr/X-AUTHORITY: localhost-localhost\\x0d?$/m, 'AUTHORITY');\nlike($r, qr/X-TLV-CRC32C: 4321\\x0d?$/m, 'CRC32C');\nlike($r, qr/X-UNIQUE-ID: UNIQQ-UNIQQ\\x0d?$/m, 'UNIQUE_ID');\nlike($r, qr/X-SSL-BINARY: true/, 'SSL_BINARY');\nlike($r, qr/X-SSL-VERIFY: 255\\x0d?$/m, 'SSL_VERIFY');\nlike($r, qr/X-SSL-VERSION: TLSv1.2-TLSv1.2\\x0d?$/m, 'SSL_VERSION');\nlike($r, qr/X-SSL-CN: example.com-example.com\\x0d?$/m, 'SSL_CN');\nlike($r, qr/X-SSL-CIPHER: AES256-SHA-AES256-SHA\\x0d?$/m, 'SSL_CIPHER');\nlike($r, qr/X-SSL-SIG-ALG: SHA1-SHA1\\x0d?$/m, 'SSL_SIG_ALG');\nlike($r, qr/X-SSL-KEY-ALG: RSA512-RSA512\\x0d?$/m, 'SSL_KEY_ALG');\nlike($r, qr/X-NETNS: NETNS-NETNS\\x0d?$/m, 'NETNS');\nlike($r, qr/X-TLV-CUSTOM: 12345\\x0d?$/m, 'custom');\nlike($r, qr/X-TLV-X: -\\x0d?$/m, 'non-existent');\n\n###############################################################################\n\nsub pp_get {\n\tmy ($url, $proxy) = @_;\n\treturn http($proxy . <<EOF);\nGET $url HTTP/1.0\nHost: localhost\n\nEOF\n}\n\nsub pp2_create {\n\tmy ($tlv) = @_;\n\n\tmy $pp2_sig = pack(\"N3\", 0x0D0A0D0A, 0x000D0A51, 0x5549540A);\n\tmy $ver_cmd = pack('C', 0x21);\n\tmy $family = pack('C', 0x11);\n\tmy $packet = $pp2_sig . $ver_cmd . $family;\n\n\tmy $ip1 = pack('N', 0xc0000201); # 192.0.2.1\n\tmy $ip2 = pack('N', 0xc0000202); # 192.0.2.2\n\tmy $port1 = pack('n', 123);\n\tmy $port2 = pack('n', 5678);\n\tmy $addrs = $ip1 . $ip2 . $port1 . $port2;\n\n\tmy $len = length($addrs) + length($tlv);\n\n\t$packet .= pack('n', $len) . $addrs . $tlv;\n\n\treturn $packet;\n}\n\nsub pp2_create_tlv {\n\tmy ($type, $content) = @_;\n\n\tmy $len = length($content);\n\n\treturn pack(\"CnA*\", $type, $len, $content);\n}\n\nsub pp2_create_ssl {\n\tmy ($client, $verify, $content) = @_;\n\n\treturn pack(\"CNA*\", $client, $verify, $content);\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_protocol_ipv6.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for haproxy protocol on IPv6 listening socket.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http realip stream/);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       [::1]:%%PORT_8080%% proxy_protocol;\n        server_name  localhost;\n\n        add_header X-IP $remote_addr;\n        add_header X-PP $proxy_protocol_addr;\n        real_ip_header proxy_protocol;\n\n        location / { }\n        location /pp {\n            set_real_ip_from ::1/128;\n            error_page 404 =200 /t;\n        }\n    }\n}\n\nstream {\n    %%TEST_GLOBALS_STREAM%%\n\n    server {\n        listen      127.0.0.1:8080;\n        proxy_pass  [::1]:%%PORT_8080%%;\n\n        proxy_protocol on;\n    }\n}\n\nEOF\n\n$t->write_file('t', 'SEE-THIS');\n$t->try_run('no inet6 support')->plan(3);\n\n###############################################################################\n\nmy $r = http_get('/t');\nlike($r, qr/X-IP: ::1/, 'realip');\nlike($r, qr/X-PP: 127.0.0.1/, 'proxy protocol');\n\n$r = http_get('/pp');\nlike($r, qr/X-IP: 127.0.0.1/, 'proxy protocol realip');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_protocol_port.t",
    "content": "#!/usr/bin/perl\n\n# (C) Andrey Zelenkov\n# (C) Nginx, Inc.\n\n# Tests for proxy_protocol_port variable.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse Socket qw/ CRLF /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http realip/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    log_format port $proxy_protocol_port;\n\n    server {\n        listen       127.0.0.1:8080 proxy_protocol;\n        server_name  localhost;\n\n        add_header X-PP-Port $proxy_protocol_port;\n        add_header X-Remote-Port $remote_port;\n\n        location /pp {\n            real_ip_header proxy_protocol;\n            error_page 404 =200 /t;\n\n            location /pp/real {\n                set_real_ip_from  127.0.0.1/32;\n            }\n        }\n\n        location /log {\n            access_log %%TESTDIR%%/port.log port;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('t', 'SEE-THIS');\n$t->run()->plan(8);\n\n###############################################################################\n\nmy $tcp4 = 'PROXY TCP4 192.0.2.1 192.0.2.2 123 5678' . CRLF;\nmy $tcp6 = 'PROXY TCP6 2001:Db8::1 2001:Db8::2 123 5678' . CRLF;\nmy $unk = 'PROXY UNKNOWN 1 2 3 4 5 6' . CRLF;\n\n# realip\n\nlike(pp_get('/pp', $tcp4), qr/X-PP-Port: 123\\x0d/, 'pp port tcp4');\nlike(pp_get('/pp', $tcp6), qr/X-PP-Port: 123\\x0d/, 'pp port tcp6');\nunlike(pp_get('/pp', $unk), qr/X-PP-Port/, 'pp port unknown');\n\n# remote_port\n\nlike(pp_get('/pp/real', $tcp4), qr/X-Remote-Port: 123\\x0d/, 'remote port tcp4');\nunlike(pp_get('/pp', $tcp4), qr/X-Remote-Port: 123\\x0d/, 'no remote port tcp4');\nlike(pp_get('/pp/real', $tcp6), qr/X-Remote-Port: 123\\x0d/, 'remote port tcp6');\nunlike(pp_get('/pp', $tcp6), qr/X-Remote-Port: 123\\x0d/, 'no remote port tcp6');\n\n# log\n\npp_get('/log', $tcp4);\n\n$t->stop();\n\nmy $log = $t->read_file('/port.log');\nchomp $log;\n\nis($log, 123, 'pp port log');\n\n###############################################################################\n\nsub pp_get {\n\tmy ($url, $proxy) = @_;\n\treturn http($proxy . <<EOF);\nGET $url HTTP/1.0\nHost: localhost\n\nEOF\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_protocol_server.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for haproxy protocol.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse Socket qw/ CRLF /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http access realip/);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    log_format pp $remote_addr:$remote_port;\n\n    add_header X-IP $remote_addr!$remote_port;\n    add_header X-PP $proxy_protocol_addr!$proxy_protocol_port;\n    add_header X-PPS $proxy_protocol_server_addr!$proxy_protocol_server_port;\n\n    server {\n        listen       127.0.0.1:8080 proxy_protocol;\n        server_name  localhost;\n\n        set_real_ip_from  127.0.0.1/32;\n\n        location /pp {\n            real_ip_header proxy_protocol;\n            error_page 404 =200 /t1;\n\n            location /pp_4 {\n                deny 192.0.2.1/32;\n                access_log %%TESTDIR%%/pp4.log pp;\n            }\n\n            location /pp_6 {\n                deny 2001:DB8::1/128;\n                access_log %%TESTDIR%%/pp6.log pp;\n            }\n        }\n\n        location / { }\n    }\n}\n\nEOF\n\n$t->write_file('t1', 'SEE-THIS');\n$t->run()->plan(24);\n\n###############################################################################\n\nmy $tcp4 = 'PROXY TCP4 192.0.2.1 192.0.2.2 123 567' . CRLF;\nmy $tcp6 = 'PROXY TCP6 2001:Db8::1 2001:Db8::2 123 567' . CRLF;\nmy $unk1 = 'PROXY UNKNOWN' . CRLF;\nmy $unk2 = 'PROXY UNKNOWN 1 2 3 4 5 6' . CRLF;\nmy $r;\n\n# no realip, just PROXY header parsing\n\n$r = pp_get('/t1', $tcp4);\nlike($r, qr/SEE-THIS/, 'tcp4 request');\nlike($r, qr/X-PP: 192.0.2.1!123\\x0d/, 'tcp4 proxy');\nlike($r, qr/X-PPS: 192.0.2.2!567\\x0d/, 'tcp4 proxy server');\nunlike($r, qr/X-IP: (192.0.2.1|[^!]+!123\\x0d)/, 'tcp4 client');\n\n$r = pp_get('/t1', $tcp6);\nlike($r, qr/SEE-THIS/, 'tcp6 request');\nlike($r, qr/X-PP: 2001:DB8::1!123\\x0d/i, 'tcp6 proxy');\nlike($r, qr/X-PPS: 2001:DB8::2!567\\x0d/i, 'tcp6 proxy server');\nunlike($r, qr/X-IP: (2001:DB8::1|[^!]+!123\\x0d)/i, 'tcp6 client');\n\n$r = pp_get('/t1', $unk1);\nlike($r, qr/SEE-THIS/, 'unknown request 1');\nlike($r, qr/X-PP: !\\x0d/, 'unknown proxy 1');\nlike($r, qr/X-PPS: !\\x0d/, 'unknown proxy server 1');\n\n$r = pp_get('/t1', $unk2);\nlike($r, qr/SEE-THIS/, 'unknown request 2');\nlike($r, qr/X-PP: !\\x0d/, 'unknown proxy 2');\nlike($r, qr/X-PPS: !\\x0d/, 'unknown proxy server 2');\n\n# realip\n\n$r = pp_get('/pp', $tcp4);\nlike($r, qr/SEE-THIS/, 'tcp4 request realip');\nlike($r, qr/X-PP: 192.0.2.1!123\\x0d/, 'tcp4 proxy realip');\nlike($r, qr/X-IP: 192.0.2.1!123\\x0d/, 'tcp4 client realip');\n\n$r = pp_get('/pp', $tcp6);\nlike($r, qr/SEE-THIS/, 'tcp6 request realip');\nlike($r, qr/X-PP: 2001:DB8::1!123\\x0d/i, 'tcp6 proxy realip');\nlike($r, qr/X-IP: 2001:DB8::1!123\\x0d/i, 'tcp6 client realip');\n\n# access\n\n$r = pp_get('/pp_4', $tcp4);\nlike($r, qr/403 Forbidden/, 'tcp4 access');\n\n$r = pp_get('/pp_6', $tcp6);\nlike($r, qr/403 Forbidden/, 'tcp6 access');\n\n# client address in access.log\n\n$t->stop();\n\nis($t->read_file('pp4.log'), \"192.0.2.1:123\\n\", 'tcp4 log');\nis($t->read_file('pp6.log'), \"2001:db8::1:123\\n\", 'tcp6 log');\n\n###############################################################################\n\nsub pp_get {\n\tmy ($url, $proxy) = @_;\n\treturn http($proxy . <<EOF);\nGET $url HTTP/1.0\nHost: localhost\n\nEOF\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_protocol_unix.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for haproxy protocol with unix socket.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse Test::Nginx::Stream;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()\n\t->has(qw/http realip stream stream_realip stream_return unix/)\n\t->plan(5);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       unix:%%TESTDIR%%/unix.sock proxy_protocol;\n        server_name  localhost;\n\n        add_header X-IP $remote_addr;\n        add_header X-PP $proxy_protocol_addr;\n        real_ip_header proxy_protocol;\n\n        location / { }\n        location /pp {\n            set_real_ip_from unix:;\n            error_page 404 =200 /t;\n        }\n    }\n}\n\nstream {\n    %%TEST_GLOBALS_STREAM%%\n\n    server {\n        listen      unix:%%TESTDIR%%/unix1.sock proxy_protocol;\n        return      $remote_addr:$proxy_protocol_addr;\n    }\n\n    server {\n        listen      unix:%%TESTDIR%%/unix2.sock proxy_protocol;\n        return      $remote_addr:$proxy_protocol_addr;\n\n        set_real_ip_from unix:;\n    }\n\n    server {\n        listen      127.0.0.1:8080;\n        proxy_pass  unix:%%TESTDIR%%/unix.sock;\n\n        proxy_protocol on;\n    }\n\n    server {\n        listen      127.0.0.1:8081;\n        proxy_pass  unix:%%TESTDIR%%/unix1.sock;\n\n        proxy_protocol on;\n    }\n\n    server {\n        listen      127.0.0.1:8082;\n        proxy_pass  unix:%%TESTDIR%%/unix2.sock;\n\n        proxy_protocol on;\n    }\n}\n\nEOF\n\n$t->write_file('t', 'SEE-THIS');\n$t->run();\n\n###############################################################################\n\nmy $r = http_get('/t');\nlike($r, qr/X-IP: unix/, 'remote_addr');\nlike($r, qr/X-PP: 127.0.0.1/, 'proxy_protocol_addr');\n\n$r = http_get('/pp');\nlike($r, qr/X-IP: 127.0.0.1/, 'remote_addr realip');\n\n# listen proxy_protocol in stream\n\nis(get(8081), 'unix::127.0.0.1', 'stream proxy_protocol');\nis(get(8082), '127.0.0.1:127.0.0.1', 'stream proxy_protocol realip');\n\n###############################################################################\n\nsub get {\n\tTest::Nginx::Stream->new(PeerPort => port(shift))->read();\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_redirect.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n# (C) Valentin Bartenev\n\n# Tests for the proxy_redirect directive.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy rewrite/)->plan(15);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            set $some_var var_here;\n\n            proxy_pass http://127.0.0.1:8081;\n\n            proxy_redirect http://127.0.0.1:8081/var_in_second/\n                           /$some_var/;\n            proxy_redirect http://127.0.0.1:8081/$some_var/ /replaced/;\n\n            proxy_redirect ~^(.+)/regex_w_([^/]+) $1/$2/test.html;\n            proxy_redirect ~*re+gexp? /replaced/test.html;\n        }\n\n        location /expl_default/ {\n            proxy_pass http://127.0.0.1:8081/replace_this/;\n            proxy_redirect wrong wrong;\n            proxy_redirect default;\n        }\n\n        location /impl_default/ {\n            proxy_pass http://127.0.0.1:8081/replace_this/;\n        }\n\n        location /off/ {\n            proxy_pass http://127.0.0.1:8081/;\n            proxy_redirect off;\n\n            location /off/on/ {\n                proxy_pass http://127.0.0.1:8081;\n                proxy_redirect http://127.0.0.1:8081/off/ /;\n\n                location /off/on/on/ {\n                    proxy_pass http://127.0.0.1:8081;\n                }\n            }\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        location / {\n            add_header Refresh \"7; url=http://127.0.0.1:8081$uri\";\n            return http://127.0.0.1:8081$uri;\n        }\n    }\n}\n\nEOF\n\n$t->run();\n\n###############################################################################\n\nmy ($p0, $p1) = (port(8080), port(8081));\n\nis(http_get_location(\"http://127.0.0.1:$p0/impl_default/test.html\"),\n\t\"http://127.0.0.1:$p0/impl_default/test.html\", 'implicit default');\nis(http_get_location(\"http://127.0.0.1:$p0/expl_default/test.html\"),\n\t\"http://127.0.0.1:$p0/expl_default/test.html\", 'explicit default');\n\nis(http_get_refresh(\"http://127.0.0.1:$p0/impl_default/test.html\"),\n\t'7; url=/impl_default/test.html', 'implicit default (refresh)');\nis(http_get_refresh(\"http://127.0.0.1:$p0/expl_default/test.html\"),\n\t'7; url=/expl_default/test.html', 'explicit default (refresh)');\n\nis(http_get_location(\"http://127.0.0.1:$p0/var_in_second/test.html\"),\n\t\"http://127.0.0.1:$p0/var_here/test.html\", 'variable in second arg');\nis(http_get_refresh(\"http://127.0.0.1:$p0/var_in_second/test.html\"),\n\t'7; url=/var_here/test.html', 'variable in second arg (refresh)');\n\nis(http_get_location(\"http://127.0.0.1:$p0/off/test.html\"),\n\t\"http://127.0.0.1:$p1/test.html\", 'rewrite off');\nis(http_get_location(\"http://127.0.0.1:$p0/off/on/test.html\"),\n\t\"http://127.0.0.1:$p0/on/test.html\", 'rewrite off overwrite');\n\nis(http_get_location(\"http://127.0.0.1:$p0/off/on/on/test.html\"),\n\t\"http://127.0.0.1:$p0/on/on/test.html\", 'rewrite inheritance');\n\nis(http_get_location(\"http://127.0.0.1:$p0/var_here/test.html\"),\n\t\"http://127.0.0.1:$p0/replaced/test.html\", 'variable in first arg');\nis(http_get_refresh(\"http://127.0.0.1:$p0/var_here/test.html\"),\n\t'7; url=/replaced/test.html', 'variable in first arg (refresh)');\n\nis(http_get_location(\"http://127.0.0.1:$p0/ReeegEX/test.html\"),\n\t\"http://127.0.0.1:$p0/replaced/test.html\", 'caseless regexp');\nis(http_get_location(\"http://127.0.0.1:$p0/regex_w_captures/test.html\"),\n\t\"http://127.0.0.1:$p1/captures/test.html\", 'regexp w/captures');\n\nis(http_get_refresh(\"http://127.0.0.1:$p0/ReeegEX/test.html\"),\n\t'7; url=/replaced/test.html', 'caseless regexp (refresh)');\nis(http_get_refresh(\"http://127.0.0.1:$p0/regex_w_captures/test.html\"),\n\t\"7; url=http://127.0.0.1:$p1/captures/test.html\",\n\t'regexp w/captures (refresh)');\n\n###############################################################################\n\nsub http_get_location {\n\tmy ($url) = @_;\n\thttp_get($url) =~ /^Location:\\s(.+?)\\x0d?$/mi;\n\treturn $1;\n}\n\nsub http_get_refresh {\n\tmy ($url) = @_;\n\thttp_get($url) =~ /^Refresh:\\s(.+?)\\x0d?$/mi;\n\treturn $1;\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_request_buffering.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for unbuffered request body.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\nuse Socket qw/ CRLF /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy rewrite/)->plan(18);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        client_header_buffer_size 1k;\n        proxy_request_buffering off;\n\n        location / {\n            client_body_buffer_size 2k;\n            add_header X-Body \"$request_body\";\n            proxy_pass http://127.0.0.1:8081;\n        }\n        location /small {\n            client_body_in_file_only on;\n            proxy_pass http://127.0.0.1:8080/;\n        }\n        location /single {\n            client_body_in_single_buffer on;\n            add_header X-Body \"$request_body\";\n            proxy_pass http://127.0.0.1:8081;\n        }\n        location /discard {\n            return 200 \"TEST\\n\";\n        }\n        location /preread {\n            proxy_pass http://127.0.0.1:8082/;\n        }\n        location /error_page {\n            proxy_pass http://127.0.0.1:8081/404;\n            error_page 404 /404;\n            proxy_intercept_errors on;\n        }\n        location /404 {\n            return 200 \"$request_body\\n\";\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        location / {\n            proxy_pass http://127.0.0.1:8080/discard;\n        }\n        location /404 { }\n    }\n}\n\nEOF\n\n$t->run();\n\n###############################################################################\n\nunlike(http_get('/'), qr/X-Body:/ms, 'no body');\n\nlike(http_get_body('/', '0123456789'),\n\tqr/X-Body: 0123456789\\x0d?$/ms, 'body');\n\nlike(http_get_body('/', '0123456789' x 128),\n\tqr/X-Body: (0123456789){128}\\x0d?$/ms, 'body in two buffers');\n\nlike(http_get_body('/single', '0123456789' x 128),\n\tqr/X-Body: (0123456789){128}\\x0d?$/ms, 'body in single buffer');\n\nlike(http_get_body('/error_page', '0123456789'),\n\tqr/^0123456789$/m, 'body in error page');\n\n# pipelined requests\n\nlike(http_get_body('/', '0123456789', '0123456789' x 128, '0123456789' x 512,\n\t'foobar'), qr/X-Body: foobar\\x0d?$/ms, 'body pipelined');\nlike(http_get_body('/', '0123456789' x 128, '0123456789' x 512, '0123456789',\n\t'foobar'), qr/X-Body: foobar\\x0d?$/ms, 'body pipelined 2');\n\nlike(http_get_body('/discard', '0123456789', '0123456789' x 128,\n\t'0123456789' x 512, 'foobar'), qr/(TEST.*){4}/ms,\n\t'body discard');\nlike(http_get_body('/discard', '0123456789' x 128, '0123456789' x 512,\n\t'0123456789', 'foobar'), qr/(TEST.*){4}/ms,\n\t'body discard 2');\n\n# proxy with file only is disabled in unbuffered mode\n\nlike(http_get_body('/small', '0123456789'),\n\tqr/X-Body: 0123456789\\x0d?$/ms, 'small body in file only');\n\n# interactive tests\n\nmy $s = get_body('/preread', port(8082), 10);\nok($s, 'no preread');\n\nSKIP: {\nskip 'no preread failed', 3 unless $s;\n\nis($s->{upload}('01234'), '01234', 'no preread - body part');\nis($s->{upload}('56789'), '56789', 'no preread - body part 2');\n\nlike($s->{http_end}(), qr/200 OK/, 'no preread - response');\n\n}\n\n$s = get_body('/preread', port(8082), 10, '01234');\nok($s, 'preread');\n\nSKIP: {\nskip 'preread failed', 3 unless $s;\n\nis($s->{preread}, '01234', 'preread - preread');\nis($s->{upload}('56789'), '56789', 'preread - body');\n\nlike($s->{http_end}(), qr/200 OK/, 'preread - response');\n\n}\n\n###############################################################################\n\nsub http_get_body {\n\tmy $uri = shift;\n\tmy $last = pop;\n\treturn http( join '', (map {\n\t\tmy $body = $_;\n\t\t\"GET $uri HTTP/1.1\" . CRLF\n\t\t. \"Host: localhost\" . CRLF\n\t\t. \"Content-Length: \" . (length $body) . CRLF . CRLF\n\t\t. $body\n\t} @_),\n\t\t\"GET $uri HTTP/1.1\" . CRLF\n\t\t. \"Host: localhost\" . CRLF\n\t\t. \"Connection: close\" . CRLF\n\t\t. \"Content-Length: \" . (length $last) . CRLF . CRLF\n\t\t. $last\n\t);\n}\n\nsub get_body {\n\tmy ($url, $port, $length, $body) = @_;\n\tmy ($server, $client, $s);\n\n\t$server = IO::Socket::INET->new(\n\t\tProto => 'tcp',\n\t\tLocalHost => '127.0.0.1',\n\t\tLocalPort => $port,\n\t\tListen => 5,\n\t\tReuse => 1\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\tmy $r = <<EOF;\nGET $url HTTP/1.1\nHost: localhost\nConnection: close\nContent-Length: $length\n\nEOF\n\n\tif (defined $body) {\n\t\t$r .= $body;\n\t}\n\n\t$s = http($r, start => 1);\n\n\teval {\n\t\tlocal $SIG{ALRM} = sub { die \"timeout\\n\" };\n\t\tlocal $SIG{PIPE} = sub { die \"sigpipe\\n\" };\n\t\talarm(5);\n\n\t\t$client = $server->accept();\n\n\t\tlog2c(\"(new connection $client)\");\n\n\t\talarm(0);\n\t};\n\talarm(0);\n\tif ($@) {\n\t\tlog_in(\"died: $@\");\n\t\treturn undef;\n\t}\n\n\t$client->sysread(my $buf, 1024);\n\tlog2i($buf);\n\n\t$buf =~ s/.*?\\x0d\\x0a?\\x0d\\x0a?(.*)/$1/ms;\n\n\tmy $f = { preread => $buf };\n\t$f->{upload} = sub {\n\t\tmy $buf = shift;\n\n\t\teval {\n\t\t\tlocal $SIG{ALRM} = sub { die \"timeout\\n\" };\n\t\t\tlocal $SIG{PIPE} = sub { die \"sigpipe\\n\" };\n\t\t\talarm(5);\n\n\t\t\tlog_out($buf);\n\t\t\t$s->write($buf);\n\n\t\t\t$client->sysread($buf, 1024);\n\t\t\tlog2i($buf);\n\n\t\t\talarm(0);\n\t\t};\n\t\talarm(0);\n\t\tif ($@) {\n\t\t\tlog_in(\"died: $@\");\n\t\t\treturn undef;\n\t\t}\n\n\t\treturn $buf;\n\t};\n\t$f->{http_end} = sub {\n\t\tmy $buf = '';\n\n\t\t$client->write(<<EOF);\nHTTP/1.1 200 OK\nConnection: close\nX-Port: $port\n\nOK\nEOF\n\n\t\t$client->close;\n\n\t\teval {\n\t\t\tlocal $SIG{ALRM} = sub { die \"timeout\\n\" };\n\t\t\tlocal $SIG{PIPE} = sub { die \"sigpipe\\n\" };\n\t\t\talarm(5);\n\n\t\t\t$s->sysread($buf, 1024);\n\t\t\tlog_in($buf);\n\n\t\t\talarm(0);\n\t\t};\n\t\talarm(0);\n\t\tif ($@) {\n\t\t\tlog_in(\"died: $@\");\n\t\t\treturn undef;\n\t\t}\n\n\t\treturn $buf;\n\t};\n\treturn $f;\n}\n\nsub log2i { Test::Nginx::log_core('|| <<', @_); }\nsub log2o { Test::Nginx::log_core('|| >>', @_); }\nsub log2c { Test::Nginx::log_core('||', @_); }\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_request_buffering_chunked.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for unbuffered request body, chunked transfer-encoding.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\nuse Socket qw/ CRLF /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy rewrite/)->plan(22);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        client_header_buffer_size 1k;\n        proxy_request_buffering off;\n        proxy_http_version 1.1;\n\n        location / {\n            client_body_buffer_size 2k;\n            add_header X-Body \"$request_body\";\n            proxy_pass http://127.0.0.1:8081;\n        }\n        location /small {\n            client_body_in_file_only on;\n            proxy_pass http://127.0.0.1:8080/;\n        }\n        location /single {\n            client_body_in_single_buffer on;\n            add_header X-Body \"$request_body\";\n            proxy_pass http://127.0.0.1:8081;\n        }\n        location /discard {\n            return 200 \"TEST\\n\";\n        }\n        location /preread {\n            proxy_pass http://127.0.0.1:8082/;\n        }\n        location /error_page {\n            proxy_pass http://127.0.0.1:8081/404;\n            error_page 404 /404;\n            proxy_intercept_errors on;\n        }\n        location /404 {\n            return 200 \"$request_body\\n\";\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        location / {\n            proxy_pass http://127.0.0.1:8080/discard;\n        }\n        location /404 { }\n    }\n}\n\nEOF\n\n$t->run();\n\n###############################################################################\n\nunlike(http_get('/'), qr/X-Body:/ms, 'no body');\n\nlike(http_get_body('/', '0123456789'),\n\tqr/X-Body: 0123456789\\x0d?$/ms, 'body');\n\nlike(http_get_body('/', '0123456789' x 128),\n\tqr/X-Body: (0123456789){128}\\x0d?$/ms, 'body in two buffers');\n\nlike(http_get_body('/single', '0123456789' x 128),\n\tqr/X-Body: (0123456789){128}\\x0d?$/ms, 'body in single buffer');\n\nlike(http_get_body('/error_page', '0123456789'),\n\tqr/^0123456789$/m, 'body in error page');\n\n# pipelined requests\n\nlike(http_get_body('/', '0123456789', '0123456789' x 128, '0123456789' x 512,\n\t'foobar'), qr/X-Body: foobar\\x0d?$/ms, 'body pipelined');\nlike(http_get_body('/', '0123456789' x 128, '0123456789' x 512, '0123456789',\n\t'foobar'), qr/X-Body: foobar\\x0d?$/ms, 'body pipelined 2');\n\nlike(http_get_body('/discard', '0123456789', '0123456789' x 128,\n\t'0123456789' x 512, 'foobar'), qr/(TEST.*){4}/ms,\n\t'body discard');\nlike(http_get_body('/discard', '0123456789' x 128, '0123456789' x 512,\n\t'0123456789', 'foobar'), qr/(TEST.*){4}/ms,\n\t'body discard 2');\n\n# proxy with file only is disabled in unbuffered mode\n\nlike(http_get_body('/small', '0123456789'),\n\tqr/X-Body: 0123456789\\x0d?$/ms, 'small body in file only');\n\n# interactive tests\n\nmy $s = get_body('/preread', port(8082));\nok($s, 'no preread');\n\nSKIP: {\nskip 'no preread failed', 3 unless $s;\n\nis($s->{upload}('01234'), '5' . CRLF . '01234' . CRLF,\n\t'no preread - body part');\nis($s->{upload}('56789', last => 1),\n\t'5' . CRLF . '56789' . CRLF . '0' . CRLF . CRLF,\n\t'no preread - body part 2');\n\nlike($s->{http_end}(), qr/200 OK/, 'no preread - response');\n\n}\n\n$s = get_body('/preread', port(8082), '01234');\nok($s, 'preread');\n\nSKIP: {\nskip 'preread failed', 3 unless $s;\n\nis($s->{preread}, '5' . CRLF . '01234' . CRLF, 'preread - preread');\nis($s->{upload}('56789', last => 1),\n\t'5' . CRLF . '56789' . CRLF . '0' . CRLF . CRLF, 'preread - body');\n\nlike($s->{http_end}(), qr/200 OK/, 'preread - response');\n\n}\n\n$s = get_body('/preread', port(8082), '01234', many => 1);\nok($s, 'chunks');\n\nSKIP: {\nskip 'chunks failed', 3 unless $s;\n\nis($s->{preread}, '9' . CRLF . '01234many' . CRLF, 'chunks - preread');\nis($s->{upload}('56789', many => 1, last => 1),\n\t'9' . CRLF . '56789many' . CRLF . '0' . CRLF . CRLF, 'chunks - body');\n\nlike($s->{http_end}(), qr/200 OK/, 'chunks - response');\n\n}\n\n###############################################################################\n\nsub http_get_body {\n\tmy $uri = shift;\n\tmy $last = pop;\n\treturn http( join '', (map {\n\t\tmy $body = $_;\n\t\t\"GET $uri HTTP/1.1\" . CRLF\n\t\t. \"Host: localhost\" . CRLF\n\t\t. \"Transfer-Encoding: chunked\" . CRLF . CRLF\n\t\t. sprintf(\"%x\", length $body) . CRLF\n\t\t. $body . CRLF\n\t\t. \"0\" . CRLF . CRLF\n\t} @_),\n\t\t\"GET $uri HTTP/1.1\" . CRLF\n\t\t. \"Host: localhost\" . CRLF\n\t\t. \"Connection: close\" . CRLF\n\t\t. \"Transfer-Encoding: chunked\" . CRLF . CRLF\n\t\t. sprintf(\"%x\", length $last) . CRLF\n\t\t. $last . CRLF\n\t\t. \"0\" . CRLF . CRLF\n\t);\n}\n\nsub get_body {\n\tmy ($url, $port, $body, %extra) = @_;\n\tmy ($server, $client, $s);\n\tmy ($last, $many) = (0, 0);\n\n\t$last = $extra{last} if defined $extra{last};\n\t$many = $extra{many} if defined $extra{many};\n\n\t$server = IO::Socket::INET->new(\n\t\tProto => 'tcp',\n\t\tLocalHost => '127.0.0.1',\n\t\tLocalPort => $port,\n\t\tListen => 5,\n\t\tReuse => 1\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\tmy $r = <<EOF;\nGET $url HTTP/1.1\nHost: localhost\nConnection: close\nTransfer-Encoding: chunked\n\nEOF\n\n\tif (defined $body) {\n\t\t$r .= sprintf(\"%x\", length $body) . CRLF;\n\t\t$r .= $body . CRLF;\n\t}\n\tif (defined $body && $many) {\n\t\t$r .= sprintf(\"%x\", length 'many') . CRLF;\n\t\t$r .= 'many' . CRLF;\n\t}\n\tif ($last) {\n\t\t$r .= \"0\" . CRLF . CRLF;\n\t}\n\n\t$s = http($r, start => 1);\n\n\teval {\n\t\tlocal $SIG{ALRM} = sub { die \"timeout\\n\" };\n\t\tlocal $SIG{PIPE} = sub { die \"sigpipe\\n\" };\n\t\talarm(5);\n\n\t\t$client = $server->accept();\n\n\t\tlog2c(\"(new connection $client)\");\n\n\t\talarm(0);\n\t};\n\talarm(0);\n\tif ($@) {\n\t\tlog_in(\"died: $@\");\n\t\treturn undef;\n\t}\n\n\t$client->sysread(my $buf, 1024);\n\tlog2i($buf);\n\n\t$buf =~ s/.*?\\x0d\\x0a?\\x0d\\x0a?(.*)/$1/ms;\n\n\tmy $f = { preread => $buf };\n\t$f->{upload} = sub {\n\t\tmy ($body, %extra) = @_;\n\t\tmy ($last, $many) = (0, 0);\n\n\t\t$last = $extra{last} if defined $extra{last};\n\t\t$many = $extra{many} if defined $extra{many};\n\n\t\tmy $buf = sprintf(\"%x\", length $body) . CRLF;\n\t\t$buf .= $body . CRLF;\n\t\tif ($many) {\n\t\t\t$buf .= sprintf(\"%x\", length 'many') . CRLF;\n\t\t\t$buf .= 'many' . CRLF;\n\t\t}\n\t\tif ($last) {\n\t\t\t$buf .= \"0\" . CRLF . CRLF;\n\t\t}\n\n\t\teval {\n\t\t\tlocal $SIG{ALRM} = sub { die \"timeout\\n\" };\n\t\t\tlocal $SIG{PIPE} = sub { die \"sigpipe\\n\" };\n\t\t\talarm(5);\n\n\t\t\tlog_out($buf);\n\t\t\t$s->write($buf);\n\n\t\t\t$client->sysread($buf, 1024);\n\t\t\tlog2i($buf);\n\n\t\t\talarm(0);\n\t\t};\n\t\talarm(0);\n\t\tif ($@) {\n\t\t\tlog_in(\"died: $@\");\n\t\t\treturn undef;\n\t\t}\n\n\t\treturn $buf;\n\t};\n\t$f->{http_end} = sub {\n\t\tmy $buf = '';\n\n\t\t$client->write(<<EOF);\nHTTP/1.1 200 OK\nConnection: close\nX-Port: $port\n\nOK\nEOF\n\n\t\t$client->close;\n\n\t\teval {\n\t\t\tlocal $SIG{ALRM} = sub { die \"timeout\\n\" };\n\t\t\tlocal $SIG{PIPE} = sub { die \"sigpipe\\n\" };\n\t\t\talarm(5);\n\n\t\t\t$s->sysread($buf, 1024);\n\t\t\tlog_in($buf);\n\n\t\t\t$s->close();\n\n\t\t\talarm(0);\n\t\t};\n\t\talarm(0);\n\t\tif ($@) {\n\t\t\tlog_in(\"died: $@\");\n\t\t\treturn undef;\n\t\t}\n\n\t\treturn $buf;\n\t};\n\treturn $f;\n}\n\nsub log2i { Test::Nginx::log_core('|| <<', @_); }\nsub log2o { Test::Nginx::log_core('|| >>', @_); }\nsub log2c { Test::Nginx::log_core('||', @_); }\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_request_buffering_keepalive.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for unbuffered request body and proxy with keepalive.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy upstream_keepalive/)->plan(1);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    upstream backend {\n        server 127.0.0.1:8081;\n        keepalive 1;\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        proxy_http_version 1.1;\n        proxy_set_header Connection \"\";\n\n        location / {\n            proxy_pass http://backend;\n            add_header X-Body $request_body;\n            proxy_request_buffering off;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        location / { }\n    }\n}\n\nEOF\n\n$t->write_file('t1', 'SEE-THIS');\n$t->run();\n\n###############################################################################\n\n# We emulate an early upstream server response while proxy is still\n# transmitting the request body.  In this case, the request body is\n# discarded by proxy, and 2nd request will be processed by upstream\n# as remain request body.\n\nhttp(<<EOF);\nGET /t1 HTTP/1.0\nHost: localhost\nContent-Length: 10\n\nEOF\n\nlike(http_get('/t1'), qr/200 OK.*SEE/ms, 'keepalive after discarded');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_request_buffering_ssl.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for unbuffered request body to ssl backend.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\nuse Socket qw/ CRLF /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http http_ssl proxy rewrite/)\n\t->has_daemon('openssl')->plan(18);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        client_header_buffer_size 1k;\n        proxy_request_buffering off;\n\n        location / {\n            client_body_buffer_size 2k;\n            add_header X-Body \"$request_body\";\n            proxy_pass https://127.0.0.1:8081;\n        }\n        location /single {\n            client_body_in_single_buffer on;\n            add_header X-Body \"$request_body\";\n            proxy_pass https://127.0.0.1:8081;\n        }\n        location /discard {\n            return 200 \"TEST\\n\";\n        }\n        location /preread {\n            proxy_pass https://127.0.0.1:8081;\n        }\n        location /error_page {\n            proxy_pass https://127.0.0.1:8081/404;\n            error_page 404 /404;\n            proxy_intercept_errors on;\n        }\n        location /404 {\n            return 200 \"$request_body\\n\";\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081 ssl;\n        server_name  localhost;\n\n        ssl_certificate_key localhost.key;\n        ssl_certificate localhost.crt;\n\n        location /preread {\n            client_body_buffer_size 2k;\n            add_header X-Body \"$request_body\";\n            proxy_pass http://127.0.0.1:8082/;\n            proxy_request_buffering off;\n        }\n\n        location / {\n            proxy_pass http://127.0.0.1:8080/discard;\n        }\n        location /404 { }\n    }\n}\n\nEOF\n\n$t->write_file('openssl.conf', <<EOF);\n[ req ]\ndefault_bits = 2048\nencrypt_key = no\ndistinguished_name = req_distinguished_name\n[ req_distinguished_name ]\nEOF\n\nmy $d = $t->testdir();\n\nforeach my $name ('localhost') {\n\tsystem('openssl req -x509 -new '\n\t\t. \"-config $d/openssl.conf -subj /CN=$name/ \"\n\t\t. \"-out $d/$name.crt -keyout $d/$name.key \"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create certificate for $name: $!\\n\";\n}\n\n$t->run();\n\n###############################################################################\n\nunlike(http_get('/'), qr/X-Body:/ms, 'no body');\n\nlike(http_get_body('/', '0123456789'),\n\tqr/X-Body: 0123456789\\x0d?$/ms, 'body');\n\nlike(http_get_body('/', '0123456789' x 128),\n\tqr/X-Body: (0123456789){128}\\x0d?$/ms, 'body in two buffers');\n\nlike(http_get_body('/single', '0123456789' x 128),\n\tqr/X-Body: (0123456789){128}\\x0d?$/ms, 'body in single buffer');\n\nlike(http_get_body('/error_page', '0123456789'),\n\tqr/^0123456789$/m, 'body in error page');\n\n# pipelined requests\n\nlike(http_get_body('/', '0123456789', '0123456789' x 128, '0123456789' x 512,\n\t'foobar'), qr/X-Body: foobar\\x0d?$/ms, 'body pipelined');\nlike(http_get_body('/', '0123456789' x 128, '0123456789' x 512, '0123456789',\n\t'foobar'), qr/X-Body: foobar\\x0d?$/ms, 'body pipelined 2');\n\nlike(http_get_body('/discard', '0123456789', '0123456789' x 128,\n\t'0123456789' x 512, 'foobar'), qr/(TEST.*){4}/ms,\n\t'body discard');\nlike(http_get_body('/discard', '0123456789' x 128, '0123456789' x 512,\n\t'0123456789', 'foobar'), qr/(TEST.*){4}/ms,\n\t'body discard 2');\n\n# interactive tests\n\nmy $s = get_body('/preread', port(8082), 10);\nok($s, 'no preread');\n\nSKIP: {\nskip 'no preread failed', 3 unless $s;\n\nis($s->{upload}('01234'), '01234', 'no preread - body part');\nis($s->{upload}('56789'), '56789', 'no preread - body part 2');\n\nlike($s->{http_end}(), qr/200 OK/, 'no preread - response');\n\n}\n\n$s = get_body('/preread', port(8082), 15, '01234');\nok($s, 'preread');\n\nSKIP: {\nskip 'preread failed', 3 unless $s;\n\nis($s->{preread}, '01234', 'preread - preread');\nis($s->{upload}('56789'), '56789', 'preread - body part');\nis($s->{upload}('abcde'), 'abcde', 'preread - body part 2');\n\nlike($s->{http_end}(), qr/200 OK/, 'preread - response');\n\n}\n\n###############################################################################\n\nsub http_get_body {\n\tmy $uri = shift;\n\tmy $last = pop;\n\treturn http( join '', (map {\n\t\tmy $body = $_;\n\t\t\"GET $uri HTTP/1.1\" . CRLF\n\t\t. \"Host: localhost\" . CRLF\n\t\t. \"Content-Length: \" . (length $body) . CRLF . CRLF\n\t\t. $body\n\t} @_),\n\t\t\"GET $uri HTTP/1.1\" . CRLF\n\t\t. \"Host: localhost\" . CRLF\n\t\t. \"Connection: close\" . CRLF\n\t\t. \"Content-Length: \" . (length $last) . CRLF . CRLF\n\t\t. $last\n\t);\n}\n\nsub get_body {\n\tmy ($url, $port, $length, $body) = @_;\n\tmy ($server, $client, $s);\n\n\t$server = IO::Socket::INET->new(\n\t\tProto => 'tcp',\n\t\tLocalHost => '127.0.0.1',\n\t\tLocalPort => $port,\n\t\tListen => 5,\n\t\tReuse => 1\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\tmy $r = <<EOF;\nGET $url HTTP/1.1\nHost: localhost\nConnection: close\nContent-Length: $length\n\nEOF\n\n\tif (defined $body) {\n\t\t$r .= $body;\n\t}\n\n\t$s = http($r, start => 1);\n\n\teval {\n\t\tlocal $SIG{ALRM} = sub { die \"timeout\\n\" };\n\t\tlocal $SIG{PIPE} = sub { die \"sigpipe\\n\" };\n\t\talarm(5);\n\n\t\t$client = $server->accept();\n\n\t\tlog2c(\"(new connection $client)\");\n\n\t\talarm(0);\n\t};\n\talarm(0);\n\tif ($@) {\n\t\tlog_in(\"died: $@\");\n\t\treturn undef;\n\t}\n\n\t$client->sysread(my $buf, 1024);\n\tlog2i($buf);\n\n\t$buf =~ s/.*?\\x0d\\x0a?\\x0d\\x0a?(.*)/$1/ms;\n\n\tmy $f = { preread => $buf };\n\t$f->{upload} = sub {\n\t\tmy $buf = shift;\n\n\t\teval {\n\t\t\tlocal $SIG{ALRM} = sub { die \"timeout\\n\" };\n\t\t\tlocal $SIG{PIPE} = sub { die \"sigpipe\\n\" };\n\t\t\talarm(5);\n\n\t\t\tlog_out($buf);\n\t\t\t$s->write($buf);\n\n\t\t\t$client->sysread($buf, 1024);\n\t\t\tlog2i($buf);\n\n\t\t\talarm(0);\n\t\t};\n\t\talarm(0);\n\t\tif ($@) {\n\t\t\tlog_in(\"died: $@\");\n\t\t\treturn undef;\n\t\t}\n\n\t\treturn $buf;\n\t};\n\t$f->{http_end} = sub {\n\t\tmy $buf = '';\n\n\t\t$client->write(<<EOF);\nHTTP/1.1 200 OK\nConnection: close\nX-Port: $port\n\nOK\nEOF\n\n\t\t$client->close;\n\n\t\teval {\n\t\t\tlocal $SIG{ALRM} = sub { die \"timeout\\n\" };\n\t\t\tlocal $SIG{PIPE} = sub { die \"sigpipe\\n\" };\n\t\t\talarm(5);\n\n\t\t\t$s->sysread($buf, 1024);\n\t\t\tlog_in($buf);\n\n\t\t\talarm(0);\n\t\t};\n\t\talarm(0);\n\t\tif ($@) {\n\t\t\tlog_in(\"died: $@\");\n\t\t\treturn undef;\n\t\t}\n\n\t\treturn $buf;\n\t};\n\treturn $f;\n}\n\nsub log2i { Test::Nginx::log_core('|| <<', @_); }\nsub log2o { Test::Nginx::log_core('|| >>', @_); }\nsub log2c { Test::Nginx::log_core('||', @_); }\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_set_body.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for proxy_set_body.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy rewrite/)->plan(2)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            proxy_pass http://127.0.0.1:8080/body;\n            proxy_set_body \"body\";\n        }\n\n        location /p1 {\n            proxy_pass http://127.0.0.1:8080/x1;\n            proxy_set_body \"body\";\n        }\n\n        location /p2 {\n            proxy_pass http://127.0.0.1:8080/body;\n            proxy_set_body \"body two\";\n        }\n\n        location /x1 {\n            add_header X-Accel-Redirect /p2;\n            return 204;\n        }\n\n        location /body {\n            add_header X-Body $request_body;\n            proxy_pass http://127.0.0.1:8080/empty;\n        }\n\n        location /empty {\n            return 204;\n        }\n    }\n}\n\nEOF\n\n$t->run();\n\n###############################################################################\n\nlike(http_get('/'), qr/X-Body: body/, 'proxy_set_body');\nlike(http_get('/p1'), qr/X-Body: body two/, 'proxy_set_body twice');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_ssi_body.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Test for proxied subrequest with request body in file.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse Socket qw/ CRLF /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy ssi/)->plan(1);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n        }\n\n        location /proxy {\n            proxy_pass http://127.0.0.1:8080/;\n            client_body_in_file_only on;\n            ssi on;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('ssi.html', 'X<!--# include virtual=\"test.html\" -->X');\n$t->write_file('test.html', 'YY');\n\n$t->run();\n\n###############################################################################\n\n# Request body cache file is released once a response is got.\n# If later a subrequest tries to use body, it fails.\n\nlike(http_get_body('/proxy/ssi.html', \"1234567890\"), qr/^XYYX$/m,\n\t'body in file in proxied subrequest');\n\n###############################################################################\n\nsub http_get_body {\n\tmy ($url, $body, %extra) = @_;\n\n\tmy $p = \"GET $url HTTP/1.0\" . CRLF\n\t\t. \"Host: localhost\" . CRLF\n\t\t. \"Content-Length: \" . (length $body) . CRLF . CRLF\n\t\t. $body;\n\n\treturn http($p, %extra);\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_ssl.t",
    "content": "#!/usr/bin/perl\n\n# (C) Nginx, Inc.\n\n# Tests for proxy to ssl backend.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\n\nmy $t = Test::Nginx->new()->has(qw/http proxy http_ssl socket_ssl/)\n\t->has_daemon('openssl')->plan(8)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen 127.0.0.1:8081 ssl;\n\n        ssl_certificate_key localhost.key;\n        ssl_certificate localhost.crt;\n        ssl_session_cache builtin;\n\n        location / {\n            add_header X-Session $ssl_session_reused;\n            add_header X-Protocol $ssl_protocol;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location /ssl_reuse {\n            proxy_pass https://127.0.0.1:8081/;\n            proxy_ssl_session_reuse on;\n        }\n\n        location /ssl {\n            proxy_pass https://127.0.0.1:8081/;\n            proxy_ssl_session_reuse off;\n        }\n\n        location /timeout {\n            proxy_pass https://127.0.0.1:8082;\n            proxy_connect_timeout 3s;\n        }\n\n        location /timeout_h {\n            proxy_pass https://127.0.0.1:8083;\n            proxy_connect_timeout 1s;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('openssl.conf', <<EOF);\n[ req ]\ndefault_bits = 2048\nencrypt_key = no\ndistinguished_name = req_distinguished_name\n[ req_distinguished_name ]\nEOF\n\n$t->write_file('big.html', 'xxxxxxxxxx' x 72000);\n$t->write_file('index.html', '');\n\nmy $d = $t->testdir();\n\nforeach my $name ('localhost') {\n\tsystem('openssl req -x509 -new '\n\t\t. \"-config $d/openssl.conf -subj /CN=$name/ \"\n\t\t. \"-out $d/$name.crt -keyout $d/$name.key \"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create certificate for $name: $!\\n\";\n}\n\n$t->run_daemon(\\&http_daemon, port(8082));\n$t->run_daemon(\\&http_daemon, port(8083));\n$t->run();\n$t->waitforsocket('127.0.0.1:' . port(8082));\n$t->waitforsocket('127.0.0.1:' . port(8083));\n\n###############################################################################\n\nlike(http_get('/ssl'), qr/200 OK.*X-Session: \\./s, 'ssl');\nlike(http_get('/ssl'), qr/200 OK.*X-Session: \\./s, 'ssl 2');\nlike(http_get('/ssl_reuse'), qr/200 OK.*X-Session: \\./s, 'ssl session new');\nTODO: {\nlocal $TODO = 'no TLS 1.3 sessions in LibreSSL'\n\tif $t->has_module('LibreSSL') && http_get('/ssl') =~ /TLSv1.3/;\nlike(http_get('/ssl_reuse'), qr/200 OK.*X-Session: r/s, 'ssl session reused');\nlike(http_get('/ssl_reuse'), qr/200 OK.*X-Session: r/s, 'ssl session reused 2');\n}\n\nSKIP: {\nskip 'long test', 1 unless $ENV{TEST_NGINX_UNSAFE};\n\nlike(http_get('/timeout'), qr/200 OK/, 'proxy connect timeout');\n\n}\n\nlike(http_get('/timeout_h'), qr/504 Gateway/, 'proxy handshake timeout');\n\nis(length(Test::Nginx::http_content(http_get('/ssl/big.html'))), 720000,\n\t'big length');\n\n###############################################################################\n\nsub http_daemon {\n\tmy ($port) = @_;\n\tmy $server = IO::Socket::INET->new(\n\t\tProto => 'tcp',\n\t\tLocalHost => '127.0.0.1:' . $port,\n\t\tListen => 5,\n\t\tReuse => 1\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\tlocal $SIG{PIPE} = 'IGNORE';\n\n\twhile (my $client = $server->accept()) {\n\t\t$client->autoflush(1);\n\n\t\tif ($port == port(8083)) {\n\t\t\tsleep 3;\n\n\t\t\tclose $client;\n\t\t\tnext;\n\t\t}\n\n\t\tmy $headers = '';\n\t\tmy $uri = '';\n\n\t\t# would fail on waitforsocket\n\n\t\teval {\n\t\t\tIO::Socket::SSL->start_SSL($client,\n\t\t\t\tSSL_server => 1,\n\t\t\t\tSSL_cert_file => \"$d/localhost.crt\",\n\t\t\t\tSSL_key_file => \"$d/localhost.key\",\n\t\t\t\tSSL_error_trap => sub { die $_[1] }\n\t\t\t);\n\t\t};\n\t\tnext if $@;\n\n\t\twhile (<$client>) {\n\t\t\t$headers .= $_;\n\t\t\tlast if (/^\\x0d?\\x0a?$/);\n\t\t}\n\n\t\t$uri = $1 if $headers =~ /^\\S+\\s+([^ ]+)\\s+HTTP/i;\n\t\tnext if $uri eq '';\n\n\t\tif ($uri eq '/timeout') {\n\t\t\tsleep 4;\n\n\t\t\tprint $client <<EOF;\nHTTP/1.1 200 OK\nConnection: close\n\nEOF\n\t\t}\n\n\t\tclose $client;\n\t}\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_ssl_certificate.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for http proxy module with proxy certificate to ssl backend.\n# The proxy_ssl_certificate and proxy_ssl_password_file directives.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http http_ssl proxy/)\n\t->has_daemon('openssl')->plan(5);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        proxy_ssl_session_reuse off;\n\n        location /verify {\n            proxy_pass https://127.0.0.1:8081/;\n            proxy_ssl_certificate 1.example.com.crt;\n            proxy_ssl_certificate_key 1.example.com.key;\n        }\n\n        location /fail {\n            proxy_pass https://127.0.0.1:8081/;\n            proxy_ssl_certificate 2.example.com.crt;\n            proxy_ssl_certificate_key 2.example.com.key;\n        }\n\n        location /encrypted {\n            proxy_pass https://127.0.0.1:8082/;\n            proxy_ssl_certificate 3.example.com.crt;\n            proxy_ssl_certificate_key 3.example.com.key;\n            proxy_ssl_password_file password;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081 ssl;\n        server_name  localhost;\n\n        ssl_certificate 2.example.com.crt;\n        ssl_certificate_key 2.example.com.key;\n\n        ssl_verify_client optional_no_ca;\n        ssl_trusted_certificate 1.example.com.crt;\n\n        location / {\n            add_header X-Verify $ssl_client_verify;\n            add_header X-Name   $ssl_client_s_dn;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8082 ssl;\n        server_name  localhost;\n\n        ssl_certificate 1.example.com.crt;\n        ssl_certificate_key 1.example.com.key;\n\n        ssl_verify_client optional_no_ca;\n        ssl_trusted_certificate 3.example.com.crt;\n\n        location / {\n            add_header X-Verify $ssl_client_verify;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('openssl.conf', <<EOF);\n[ req ]\ndefault_bits = 2048\nencrypt_key = no\ndistinguished_name = req_distinguished_name\n[ req_distinguished_name ]\nEOF\n\nmy $d = $t->testdir();\n\nforeach my $name ('1.example.com', '2.example.com') {\n\tsystem('openssl req -x509 -new '\n\t\t. \"-config $d/openssl.conf -subj /CN=$name/ \"\n\t\t. \"-out $d/$name.crt -keyout $d/$name.key \"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create certificate for $name: $!\\n\";\n}\n\nforeach my $name ('3.example.com') {\n\tsystem(\"openssl genrsa -out $d/$name.key -passout pass:$name \"\n\t\t. \"-aes128 2048 >>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create private key: $!\\n\";\n\tsystem('openssl req -x509 -new '\n\t\t. \"-config $d/openssl.conf -subj /CN=$name/ \"\n\t\t. \"-out $d/$name.crt \"\n\t\t. \"-key $d/$name.key -passin pass:$name\"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create certificate for $name: $!\\n\";\n}\n\nsleep 1 if $^O eq 'MSWin32';\n\n$t->write_file('password', '3.example.com');\n$t->write_file('index.html', '');\n\n$t->run();\n\n###############################################################################\n\nlike(http_get('/verify'), qr/X-Verify: SUCCESS/ms, 'verify certificate');\nlike(http_get('/fail'), qr/X-Verify: FAILED/ms, 'fail certificate');\nlike(http_get('/encrypted'), qr/X-Verify: SUCCESS/ms, 'with encrypted key');\n\nlike(http_get('/verify'), qr!X-Name: /?CN=1.example!, 'valid certificate');\nunlike(http_get('/fail'), qr!X-Name: /?CN=1.example!, 'invalid certificate');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_ssl_certificate_empty.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for http proxy module with proxy certificate to ssl backend.\n# The proxy_ssl_certificate directive empty value cancels inheritance.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http http_ssl proxy/)\n\t->has_daemon('openssl');\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        proxy_ssl_session_reuse off;\n\n        proxy_ssl_certificate 1.example.com.crt;\n        proxy_ssl_certificate_key 1.example.com.key;\n\n        location /verify {\n            proxy_pass https://127.0.0.1:8081/;\n        }\n\n        location /cancel {\n            proxy_pass https://127.0.0.1:8081/;\n            proxy_ssl_certificate \"\";\n            proxy_ssl_certificate_key \"\";\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081 ssl;\n        server_name  localhost;\n\n        ssl_certificate 2.example.com.crt;\n        ssl_certificate_key 2.example.com.key;\n\n        ssl_verify_client optional;\n        ssl_client_certificate 1.example.com.crt;\n\n        location / {\n            add_header X-Verify $ssl_client_verify;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('openssl.conf', <<EOF);\n[ req ]\ndefault_bits = 2048\nencrypt_key = no\ndistinguished_name = req_distinguished_name\n[ req_distinguished_name ]\nEOF\n\nmy $d = $t->testdir();\n\nforeach my $name ('1.example.com', '2.example.com') {\n\tsystem('openssl req -x509 -new '\n\t\t. \"-config $d/openssl.conf -subj /CN=$name/ \"\n\t\t. \"-out $d/$name.crt -keyout $d/$name.key \"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create certificate for $name: $!\\n\";\n}\n\nsleep 1 if $^O eq 'MSWin32';\n\n$t->write_file('index.html', '');\n\n$t->try_run('no empty value support')->plan(2);\n\n###############################################################################\n\nlike(http_get('/verify'), qr/X-Verify: SUCCESS/ms, 'verify certificate');\nlike(http_get('/cancel'), qr/X-Verify: NONE/ms, 'cancel certificate');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_ssl_certificate_vars.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for http proxy module with variables in ssl certificates.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http http_ssl proxy/)\n\t->has_daemon('openssl');\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        proxy_ssl_session_reuse off;\n\n        location / {\n            proxy_pass https://127.0.0.1:8081/;\n            proxy_ssl_certificate $arg_cert.example.com.crt;\n            proxy_ssl_certificate_key $arg_cert.example.com.key;\n        }\n\n        location /encrypted {\n            proxy_pass https://127.0.0.1:8082/;\n            proxy_ssl_certificate $arg_cert.example.com.crt;\n            proxy_ssl_certificate_key $arg_cert.example.com.key;\n            proxy_ssl_password_file password;\n        }\n\n        location /none {\n            proxy_pass https://127.0.0.1:8082/;\n            proxy_ssl_certificate $arg_cert;\n            proxy_ssl_certificate_key $arg_cert;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081 ssl;\n        server_name  localhost;\n\n        ssl_certificate 2.example.com.crt;\n        ssl_certificate_key 2.example.com.key;\n\n        ssl_verify_client optional_no_ca;\n        ssl_trusted_certificate 1.example.com.crt;\n\n        location / {\n            add_header X-Verify $ssl_client_verify;\n            add_header X-Name   $ssl_client_s_dn;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8082 ssl;\n        server_name  localhost;\n\n        ssl_certificate 1.example.com.crt;\n        ssl_certificate_key 1.example.com.key;\n\n        ssl_verify_client optional_no_ca;\n        ssl_trusted_certificate 3.example.com.crt;\n\n        location / {\n            add_header X-Verify $ssl_client_verify;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('openssl.conf', <<EOF);\n[ req ]\ndefault_bits = 2048\nencrypt_key = no\ndistinguished_name = req_distinguished_name\n[ req_distinguished_name ]\nEOF\n\nmy $d = $t->testdir();\n\nforeach my $name ('1.example.com', '2.example.com') {\n\tsystem('openssl req -x509 -new '\n\t\t. \"-config $d/openssl.conf -subj /CN=$name/ \"\n\t\t. \"-out $d/$name.crt -keyout $d/$name.key \"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create certificate for $name: $!\\n\";\n}\n\nforeach my $name ('3.example.com') {\n\tsystem(\"openssl genrsa -out $d/$name.key -passout pass:$name \"\n\t\t. \"-aes128 2048 >>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create private key: $!\\n\";\n\tsystem('openssl req -x509 -new '\n\t\t. \"-config $d/openssl.conf -subj /CN=$name/ \"\n\t\t. \"-out $d/$name.crt \"\n\t\t. \"-key $d/$name.key -passin pass:$name\"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create certificate for $name: $!\\n\";\n}\n\nsleep 1 if $^O eq 'MSWin32';\n\n$t->write_file('password', '3.example.com');\n$t->write_file('index.html', '');\n\n$t->try_run('no upstream ssl_certificate variables')->plan(4);\n\n###############################################################################\n\nlike(http_get('/?cert=1'),\n\tqr/X-Verify: SUCCESS/ms, 'variable - verify certificate');\nlike(http_get('/?cert=2'),\n\tqr/X-Verify: FAILED/ms, 'variable - fail certificate');\nlike(http_get('/encrypted?cert=3'),\n\tqr/X-Verify: SUCCESS/ms, 'variable - with encrypted key');\nlike(http_get('/none'),\n\tqr/X-Verify: NONE/ms, 'variable - no certificate');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_ssl_conf_command.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for proxy_ssl_conf_command and friends.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()\n\t->has(qw/http http_ssl proxy uwsgi http_v2 grpc openssl:1.0.2/)\n\t->has_daemon('openssl');\n\nplan(skip_all => 'no ssl_conf_command') if $t->has_module('BoringSSL');\n\n$t->write_file_expand('nginx.conf', <<'EOF')->plan(3);\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            proxy_ssl_certificate localhost.crt;\n            proxy_ssl_certificate_key localhost.key;\n            proxy_ssl_conf_command Certificate override.crt;\n            proxy_ssl_conf_command PrivateKey override.key;\n            proxy_pass https://127.0.0.1:8081;\n        }\n\n        location /uwsgi {\n            uwsgi_ssl_certificate localhost.crt;\n            uwsgi_ssl_certificate_key localhost.key;\n            uwsgi_ssl_conf_command Certificate override.crt;\n            uwsgi_ssl_conf_command PrivateKey override.key;\n            uwsgi_ssl_session_reuse off;\n            uwsgi_pass suwsgi://127.0.0.1:8081;\n        }\n\n        location /grpc {\n            grpc_ssl_certificate localhost.crt;\n            grpc_ssl_certificate_key localhost.key;\n            grpc_ssl_conf_command Certificate override.crt;\n            grpc_ssl_conf_command PrivateKey override.key;\n            grpc_pass grpcs://127.0.0.1:8082;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081 ssl;\n        listen       127.0.0.1:8082 ssl http2;\n        server_name  localhost;\n\n        ssl_certificate localhost.crt;\n        ssl_certificate_key localhost.key;\n        ssl_verify_client optional_no_ca;\n\n        # stub to implement SSL logic for tests\n\n        add_header X-Cert $ssl_client_s_dn always;\n    }\n}\n\nEOF\n\n$t->write_file('openssl.conf', <<EOF);\n[ req ]\ndefault_bits = 2048\nencrypt_key = no\ndistinguished_name = req_distinguished_name\n[ req_distinguished_name ]\nEOF\n\nmy $d = $t->testdir();\n\nforeach my $name ('localhost', 'override') {\n\tsystem('openssl req -x509 -new '\n\t\t. \"-config $d/openssl.conf -subj /CN=$name/ \"\n\t\t. \"-out $d/$name.crt -keyout $d/$name.key \"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create certificate for $name: $!\\n\";\n}\n\n$t->write_file('index.html', '');\n# suppress deprecation warning\nopen OLDERR, \">&\", \\*STDERR; close STDERR;\n$t->run();\nopen STDERR, \">&\", \\*OLDERR;\n\n###############################################################################\n\nlike(http_get('/'), qr/CN=override/, 'proxy_ssl_conf_command');\nlike(http_get('/uwsgi'), qr/CN=override/, 'uwsgi_ssl_conf_command');\nlike(http_get('/grpc'), qr/CN=override/, 'grpc_ssl_conf_command');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_ssl_keepalive.t",
    "content": "#!/usr/bin/perl\n\n# (C) Andrey Zelenkov\n# (C) Nginx, Inc.\n\n# Tests for proxy with keepalive to ssl backend.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\n\nmy $t = Test::Nginx->new()->has(qw/http http_ssl proxy upstream_keepalive/)\n\t->has_daemon('openssl')->plan(3)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\nworker_processes 1;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    upstream u {\n        server 127.0.0.1:8081;\n        keepalive 1;\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        proxy_http_version 1.1;\n\n        location / {\n            proxy_pass https://u/;\n            proxy_set_header Connection $args;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081 ssl;\n        server_name  localhost;\n\n        ssl_certificate_key localhost.key;\n        ssl_certificate localhost.crt;\n\n        location / {\n            add_header X-Connection $connection;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('openssl.conf', <<EOF);\n[ req ]\ndefault_bits = 2048\nencrypt_key = no\ndistinguished_name = req_distinguished_name\n[ req_distinguished_name ]\nEOF\n\nmy $d = $t->testdir();\n\nforeach my $name ('localhost') {\n\tsystem('openssl req -x509 -new '\n\t\t. \"-config $d/openssl.conf -subj /CN=$name/ \"\n\t\t. \"-out $d/$name.crt -keyout $d/$name.key \"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create certificate for $name: $!\\n\";\n}\n\n$t->write_file('index.html', 'SEE-THIS');\n$t->run();\n\n###############################################################################\n\nmy ($r, $n);\n\nlike($r = http_get('/'), qr/200 OK.*SEE-THIS/ms, 'first');\n$r =~ m/X-Connection: (\\d+)/; $n = $1;\nlike(http_get('/'), qr/X-Connection: $n[^\\d].*SEE-THIS/ms, 'second');\n\nhttp_get('/?close');\nunlike(http_get('/'), qr/X-Connection: $n[^\\d]/, 'close');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_ssl_name.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n# (C) Nginx, Inc.\n\n# Tests for proxy to ssl backend, use of Server Name Indication\n# (proxy_ssl_name, proxy_ssl_server_name directives).\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http http_ssl sni proxy/)\n\t->has_daemon('openssl')\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    upstream backend {\n        server 127.0.0.1:8081;\n    }\n\n    upstream backend2 {\n        server 127.0.0.1:8081;\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        # session reuse is off, as sessions are cached\n        # for a particular upstream, and resumed session\n        # will use server name previously negotiated\n\n        proxy_ssl_session_reuse off;\n\n        location /1 {\n            proxy_pass https://127.0.0.1:8081/;\n            proxy_ssl_name 1.example.com;\n            proxy_ssl_server_name on;\n        }\n\n        location /2 {\n            proxy_pass https://127.0.0.1:8081/;\n            proxy_ssl_name 2.example.com;\n            proxy_ssl_server_name on;\n\n        }\n\n        location /off {\n            proxy_pass https://backend/;\n            proxy_ssl_server_name off;\n        }\n\n        location /default {\n            proxy_pass https://backend/;\n            proxy_ssl_server_name on;\n        }\n\n        location /default2 {\n            proxy_pass https://backend2/;\n            proxy_ssl_server_name on;\n        }\n\n        location /port {\n            proxy_pass https://backend/;\n            proxy_ssl_server_name on;\n            proxy_ssl_name backend:123;\n        }\n\n        location /ip {\n            proxy_pass https://127.0.0.1:8081/;\n            proxy_ssl_server_name on;\n        }\n\n        location /ip6 {\n            proxy_pass https://[::1]:%%PORT_8081%%/;\n            proxy_ssl_server_name on;\n        }\n    }\n\n    server {\n        listen 127.0.0.1:8081 ssl;\n        listen [::1]:%%PORT_8081%% ssl;\n        server_name 1.example.com;\n\n        ssl_certificate localhost.crt;\n        ssl_certificate_key localhost.key;\n\n        add_header X-Name $ssl_server_name,;\n    }\n}\n\nEOF\n\n$t->write_file('openssl.conf', <<EOF);\n[ req ]\ndefault_bits = 2048\nencrypt_key = no\ndistinguished_name = req_distinguished_name\n[ req_distinguished_name ]\nEOF\n\nmy $d = $t->testdir();\n\nforeach my $name ('localhost') {\n\tsystem('openssl req -x509 -new '\n\t\t. \"-config $d/openssl.conf -subj /commonName=$name/ \"\n\t\t. \"-out $d/$name.crt -keyout $d/$name.key \"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create certificate for $name: $!\\n\";\n}\n\n$t->write_file('index.html', '');\n\n$t->try_run('no inet6 support')->plan(9);\n\n###############################################################################\n\nlike(http_get('/1'), qr/200 OK.*X-Name: 1.example.com,/ms, 'name 1');\nlike(http_get('/2'), qr/200 OK.*X-Name: 2.example.com,/ms, 'name 2');\nlike(http_get('/off'), qr/200 OK.*X-Name: ,/ms, 'no name');\n\nlike(http_get('/default'), qr/200 OK.*X-Name: backend,/ms, 'default');\nlike(http_get('/default2'), qr/200 OK.*X-Name: backend2,/ms, 'default2');\nlike(http_get('/default'), qr/200 OK.*X-Name: backend,/ms, 'default again');\n\nlike(http_get('/port'), qr/200 OK.*X-Name: backend,/ms, 'no port in name');\nlike(http_get('/ip'), qr/200 OK.*X-Name: ,/ms, 'no ip');\nlike(http_get('/ip6'), qr/200 OK.*X-Name: ,/ms, 'no ipv6');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_ssl_verify.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n# (C) Nginx, Inc.\n\n# Tests for proxy to ssl backend, backend certificate verification.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http http_ssl proxy/)\n\t->has_daemon('openssl')->plan(6)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location /verify {\n            proxy_pass https://127.0.0.1:8081/;\n            proxy_ssl_name example.com;\n            proxy_ssl_verify on;\n            proxy_ssl_trusted_certificate 1.example.com.crt;\n        }\n\n        location /wildcard {\n            proxy_pass https://127.0.0.1:8081/;\n            proxy_ssl_name foo.example.com;\n            proxy_ssl_verify on;\n            proxy_ssl_trusted_certificate 1.example.com.crt;\n        }\n\n        location /fail {\n            proxy_pass https://127.0.0.1:8081/;\n            proxy_ssl_name no.match.example.com;\n            proxy_ssl_verify on;\n            proxy_ssl_trusted_certificate 1.example.com.crt;\n        }\n\n        location /cn {\n            proxy_pass https://127.0.0.1:8082/;\n            proxy_ssl_name 2.example.com;\n            proxy_ssl_verify on;\n            proxy_ssl_trusted_certificate 2.example.com.crt;\n        }\n\n        location /cn/fail {\n            proxy_pass https://127.0.0.1:8082/;\n            proxy_ssl_name bad.example.com;\n            proxy_ssl_verify on;\n            proxy_ssl_trusted_certificate 2.example.com.crt;\n        }\n\n        location /untrusted {\n            proxy_pass https://127.0.0.1:8082/;\n            proxy_ssl_verify on;\n            proxy_ssl_trusted_certificate 1.example.com.crt;\n            proxy_ssl_session_reuse off;\n        }\n    }\n\n    server {\n        listen 127.0.0.1:8081 ssl;\n        server_name 1.example.com;\n\n        ssl_certificate 1.example.com.crt;\n        ssl_certificate_key 1.example.com.key;\n\n        add_header X-Name $ssl_server_name;\n    }\n\n    server {\n        listen 127.0.0.1:8082 ssl;\n        server_name 2.example.com;\n\n        ssl_certificate 2.example.com.crt;\n        ssl_certificate_key 2.example.com.key;\n\n        add_header X-Name $ssl_server_name;\n    }\n}\n\nEOF\n\n$t->write_file('openssl.1.example.com.conf', <<EOF);\n[ req ]\nprompt = no\ndefault_bits = 2048\nencrypt_key = no\ndistinguished_name = req_distinguished_name\nx509_extensions = v3_req\n\n[ req_distinguished_name ]\ncommonName=no.match.example.com\n\n[ v3_req ]\nsubjectAltName = DNS:example.com,DNS:*.example.com\nEOF\n\n$t->write_file('openssl.2.example.com.conf', <<EOF);\n[ req ]\nprompt = no\ndefault_bits = 2048\nencrypt_key = no\ndistinguished_name = req_distinguished_name\n\n[ req_distinguished_name ]\ncommonName=2.example.com\nEOF\n\nmy $d = $t->testdir();\n\nforeach my $name ('1.example.com', '2.example.com') {\n\tsystem('openssl req -x509 -new '\n\t\t. \"-config $d/openssl.$name.conf \"\n\t\t. \"-out $d/$name.crt -keyout $d/$name.key \"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create certificate for $name: $!\\n\";\n}\n\nsleep 1 if $^O eq 'MSWin32';\n\n$t->write_file('index.html', '');\n\n$t->run();\n\n###############################################################################\n\n# subjectAltName\n\nlike(http_get('/verify'), qr/200 OK/ms, 'verify');\nlike(http_get('/wildcard'), qr/200 OK/ms, 'verify wildcard');\nlike(http_get('/fail'), qr/502 Bad/ms, 'verify fail');\n\n# commonName\n\nlike(http_get('/cn'), qr/200 OK/ms, 'verify cn');\nlike(http_get('/cn/fail'), qr/502 Bad/ms, 'verify cn fail');\n\n# untrusted\n\nlike(http_get('/untrusted'), qr/502 Bad/ms, 'untrusted');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_store.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for proxy_store functionality.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new();\n\n$t->write_file_expand('nginx.conf', <<'EOF')->has(qw/http proxy ssi/)->plan(9);\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location /store- {\n            proxy_pass http://127.0.0.1:8080/;\n            proxy_store on;\n        }\n        location /store-string- {\n            proxy_pass http://127.0.0.1:8080/;\n            proxy_store %%TESTDIR%%$uri;\n        }\n        location /ssi.html {\n            ssi on;\n        }\n        location /index-big.html {\n            limit_rate  200k;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('index.html', 'SEE-THIS');\n$t->write_file('index-nostore.html', 'SEE-THIS');\n$t->write_file('index-big.html', 'x' x (100 << 10));\n$t->write_file('ssi.html',\n\t'<!--#include virtual=\"/store-index-big.html?1\" -->' .\n\t'<!--#include virtual=\"/store-index-big.html?2\" -->'\n);\n$t->run();\n\n###############################################################################\n\nlike(http_get('/store-index.html'), qr/SEE-THIS/, 'proxy request');\nok(-e $t->testdir() . '/store-index.html', 'result stored');\n\nlike(http_get('/store-string-index.html'), qr/SEE-THIS/,\n\t'proxy string path request');\nok(-e $t->testdir() . '/store-string-index.html', 'string path result stored');\n\nlike(http_head('/store-index-nostore.html'), qr/200 OK/, 'head request');\nok(!-e $t->testdir() . '/store-index-nostore.html', 'result not stored');\n\nok(scalar @{[ glob $t->testdir() . '/proxy_temp/*' ]} == 0, 'no temp files');\n\nhttp_get('/store-index-big.html', aborted => 1, sleep => 0.1);\n\nselect(undef, undef, undef, 0.5);\nselect(undef, undef, undef, 2.5)\n\tif scalar @{[ glob $t->testdir() . '/proxy_temp/*' ]};\n\nok(scalar @{[ glob $t->testdir() . '/proxy_temp/*' ]} == 0,\n\t'no temp files after aborted request');\n\nhttp_get('/ssi.html', aborted => 1, sleep => 0.1);\n\nselect(undef, undef, undef, 0.5);\nselect(undef, undef, undef, 2.5)\n\tif scalar @{[ glob $t->testdir() . '/proxy_temp/*' ]};\n\nok(scalar @{[ glob $t->testdir() . '/proxy_temp/*' ]} == 0,\n\t'no temp files after aborted ssi');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_unfinished.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for http proxy and prematurely closed connections.  Incomplete\n# responses shouldn't loose information about their incompleteness.\n\n# In particular, incomplete responses:\n#\n# - shouldn't be cached\n#\n# - if a response is sent using chunked transfer encoding,\n#   final chunk shouldn't be sent\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse Socket qw/ CRLF /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx qw/ :DEFAULT http_content /;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy cache sub/)->plan(15);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    proxy_cache_path   %%TESTDIR%%/cache  levels=1:2\n                       keys_zone=one:1m;\n\n    server {\n        listen       127.0.0.1:8080 sndbuf=32k;\n        server_name  localhost;\n\n        location / {\n            sub_filter foo bar;\n            sub_filter_types *;\n            proxy_pass http://127.0.0.1:8081;\n        }\n\n        location /un/ {\n            sub_filter foo bar;\n            sub_filter_types *;\n            proxy_pass http://127.0.0.1:8081/;\n            proxy_buffering off;\n        }\n\n        location /cache/ {\n            proxy_pass http://127.0.0.1:8081/;\n            proxy_cache one;\n            add_header X-Cache-Status $upstream_cache_status;\n        }\n\n        location /proxy/ {\n            sub_filter foo bar;\n            sub_filter_types *;\n            proxy_pass http://127.0.0.1:8080/local/;\n            proxy_buffer_size 1k;\n            proxy_buffers 4 1k;\n        }\n\n        location /local/ {\n            alias %%TESTDIR%%/;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('big.html', 'X' x (1024 * 1024) . 'finished');\n\n$t->run_daemon(\\&http_daemon);\n$t->run()->waitforsocket('127.0.0.1:' . port(8081));\n\n###############################################################################\n\nhttp_get('/cache/length');\nlike(http_get('/cache/length'), qr/MISS/, 'unfinished not cached');\n\n# chunked encoding has enough information to don't cache a response,\n# much like with Content-Length available\n\nhttp_get('/cache/chunked');\nlike(http_get('/cache/chunked'), qr/MISS/, 'unfinished chunked');\n\n# make sure there is no final chunk in unfinished responses\n\nlike(http_get_11('/length'), qr/unfinished.*no-last-chunk/s,\n\t'length no final chunk');\nlike(http_get_11('/chunked'), qr/unfinished.*no-last-chunk/s,\n\t'chunked no final chunk');\n\n# but there is final chunk in complete responses\n\nlike(http_get_11('/length/ok'), qr/finished\\x0d\\x0a$/s,\n\t'length final chunk');\nlike(http_get_11('/chunked/ok'), qr/finished\\x0d\\x0a$/s,\n\t'chunked final chunk');\n\n# the same with proxy_buffering set to off\n\nlike(http_get_11('/un/length'), qr/unfinished.*no-last-chunk/s,\n\t'unbuffered length no final chunk');\nlike(http_get_11('/un/chunked'), qr/unfinished.*no-last-chunk/s,\n\t'unbuffered chunked no final chunk');\n\nlike(http_get_11('/un/length/ok'), qr/finished\\x0d\\x0a$/s,\n\t'unbuffered length final chunk');\nlike(http_get_11('/un/chunked/ok'), qr/finished\\x0d\\x0a$/s,\n\t'unbuffered chunked final chunk');\n\n# big responses\n\nlike(http_get('/big', sleep => 0.1), qr/unfinished/s, 'big unfinished');\nlike(http_get('/big/ok', sleep => 0.1), qr/finished/s, 'big finished');\nlike(http_get('/un/big', sleep => 0.1), qr/unfinished/s, 'big unfinished un');\nlike(http_get('/un/big/ok', sleep => 0.1), qr/finished/s, 'big finished un');\n\n# if disk buffering fails for some reason, there should be\n# no final chunk\n\nchmod(0000, $t->testdir() . '/proxy_temp');\n\nmy $r = http_get_11('/proxy/big.html', sleep => 0.5);\n\nSKIP: {\nskip 'finished', 1 if length($r) == 1024 * 1024 + 8;\n\nlike($r, qr/X(?!.*\\x0d\\x0a?0\\x0d\\x0a?)/s, 'no proxy temp');\n\n}\n\nchmod(0700, $t->testdir() . '/proxy_temp');\n\n###############################################################################\n\nsub http_get_11 {\n\tmy ($uri, %extra) = @_;\n\n\treturn http_content(http(\n\t\t\"GET $uri HTTP/1.1\" . CRLF .\n\t\t\"Connection: close\" . CRLF .\n\t\t\"Host: localhost\" . CRLF . CRLF,\n\t\t%extra\n\t));\n}\n\n###############################################################################\n\nsub http_daemon {\n\tmy $server = IO::Socket::INET->new(\n\t\tProto => 'tcp',\n\t\tLocalAddr => '127.0.0.1:' . port(8081),\n\t\tListen => 5,\n\t\tReuse => 1\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\tlocal $SIG{PIPE} = 'IGNORE';\n\n\twhile (my $client = $server->accept()) {\n\t\t$client->autoflush(1);\n\n\t\tmy $headers = '';\n\t\tmy $uri = '';\n\n\t\twhile (<$client>) {\n\t\t\t$headers .= $_;\n\t\t\tlast if (/^\\x0d?\\x0a?$/);\n\t\t}\n\n\t\t$uri = $1 if $headers =~ /^\\S+\\s+([^ ]+)\\s+HTTP/i;\n\n\t\tif ($uri eq '/length') {\n\t\t\tprint $client\n\t\t\t\t\"HTTP/1.1 200 OK\" . CRLF .\n\t\t\t\t\"Content-Length: 100\" . CRLF .\n\t\t\t\t\"Cache-Control: max-age=300\" . CRLF .\n\t\t\t\t\"Connection: close\" . CRLF .\n\t\t\t\tCRLF .\n\t\t\t\t\"unfinished\" . CRLF;\n\n\t\t} elsif ($uri eq '/length/ok') {\n\t\t\tprint $client\n\t\t\t\t\"HTTP/1.1 200 OK\" . CRLF .\n\t\t\t\t\"Content-Length: 10\" . CRLF .\n\t\t\t\t\"Cache-Control: max-age=300\" . CRLF .\n\t\t\t\t\"Connection: close\" . CRLF .\n\t\t\t\tCRLF .\n\t\t\t\t\"finished\" . CRLF;\n\n\t\t} elsif ($uri eq '/big') {\n\t\t\tprint $client\n\t\t\t\t\"HTTP/1.1 200 OK\" . CRLF .\n\t\t\t\t\"Content-Length: 1000100\" . CRLF .\n\t\t\t\t\"Cache-Control: max-age=300\" . CRLF .\n\t\t\t\t\"Connection: close\" . CRLF .\n\t\t\t\tCRLF;\n\t\t\tfor (1 .. 10000) {\n\t\t\t\tprint $client (\"X\" x 98) . CRLF;\n\t\t\t}\n\t\t\tprint $client \"unfinished\" . CRLF;\n\n\t\t} elsif ($uri eq '/big/ok') {\n\t\t\tprint $client\n\t\t\t\t\"HTTP/1.1 200 OK\" . CRLF .\n\t\t\t\t\"Content-Length: 1000010\" . CRLF .\n\t\t\t\t\"Cache-Control: max-age=300\" . CRLF .\n\t\t\t\t\"Connection: close\" . CRLF .\n\t\t\t\tCRLF;\n\t\t\tfor (1 .. 10000) {\n\t\t\t\tprint $client (\"X\" x 98) . CRLF;\n\t\t\t}\n\t\t\tprint $client \"finished\" . CRLF;\n\n\t\t} elsif ($uri eq '/chunked') {\n\t\t\tprint $client\n\t\t\t\t\"HTTP/1.1 200 OK\" . CRLF .\n\t\t\t\t\"Transfer-Encoding: chunked\" . CRLF .\n\t\t\t\t\"Cache-Control: max-age=300\" . CRLF .\n\t\t\t\t\"Connection: close\" . CRLF .\n\t\t\t\tCRLF .\n\t\t\t\t\"ff\" . CRLF .\n\t\t\t\t\"unfinished\" . CRLF;\n\n\t\t} elsif ($uri eq '/chunked/ok') {\n\t\t\tprint $client\n\t\t\t\t\"HTTP/1.1 200 OK\" . CRLF .\n\t\t\t\t\"Transfer-Encoding: chunked\" . CRLF .\n\t\t\t\t\"Cache-Control: max-age=300\" . CRLF .\n\t\t\t\t\"Connection: close\" . CRLF .\n\t\t\t\tCRLF .\n\t\t\t\t\"a\" . CRLF .\n\t\t\t\t\"finished\" . CRLF .\n\t\t\t\tCRLF . \"0\" . CRLF . CRLF;\n\t\t}\n\t}\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_unix.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for http proxy module with unix socket.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\neval { require IO::Socket::UNIX; };\nplan(skip_all => 'IO::Socket::UNIX not installed') if $@;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy unix/)->plan(5);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    upstream u {\n        server unix:%%TESTDIR%%/unix.sock;\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            proxy_pass http://unix:%%TESTDIR%%/unix.sock;\n            proxy_read_timeout 1s;\n            proxy_connect_timeout 2s;\n        }\n\n        location /var {\n            proxy_pass http://$arg_b;\n            proxy_read_timeout 1s;\n        }\n\n        location /u {\n            proxy_pass http://u;\n            proxy_read_timeout 1s;\n        }\n    }\n}\n\nEOF\n\nmy $path = $t->testdir() . '/unix.sock';\n\n$t->run_daemon(\\&http_daemon, $path);\n$t->run();\n\n# wait for unix socket to appear\n\nfor (1 .. 50) {\n\tlast if -S $path;\n\tselect undef, undef, undef, 0.1;\n}\n\n###############################################################################\n\nlike(http_get('/'), qr/SEE-THIS/, 'proxy request');\nlike(http_get('/multi'), qr/AND-THIS/, 'proxy request with multiple packets');\n\nunlike(http_head('/'), qr/SEE-THIS/, 'proxy head request');\n\nlike(http_get(\"/var?b=unix:$path:/\"), qr/SEE-THIS/, 'proxy with variables');\n\nlike(http_get('/u'), qr/SEE-THIS/, 'proxy implicit upstream');\n\n###############################################################################\n\nsub http_daemon {\n\tmy $server = IO::Socket::UNIX->new(\n\t\tProto => 'tcp',\n\t\tLocal => shift,\n\t\tListen => 5,\n\t\tReuse => 1\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\tlocal $SIG{PIPE} = 'IGNORE';\n\n\twhile (my $client = $server->accept()) {\n\t\t$client->autoflush(1);\n\n\t\tmy $headers = '';\n\t\tmy $uri = '';\n\n\t\twhile (<$client>) {\n\t\t\t$headers .= $_;\n\t\t\tlast if (/^\\x0d?\\x0a?$/);\n\t\t}\n\n\t\t$uri = $1 if $headers =~ /^\\S+\\s+([^ ]+)\\s+HTTP/i;\n\n\t\tif (grep { $uri eq $_ } ('/', '/u')) {\n\t\t\tprint $client <<'EOF';\nHTTP/1.1 200 OK\nConnection: close\n\nEOF\n\t\t\tprint $client \"TEST-OK-IF-YOU-SEE-THIS\"\n\t\t\t\tunless $headers =~ /^HEAD/i;\n\n\t\t} elsif ($uri eq '/multi') {\n\n\t\t\tprint $client <<\"EOF\";\nHTTP/1.1 200 OK\nConnection: close\n\nTEST-OK-IF-YOU-SEE-THIS\nEOF\n\n\t\t\tselect undef, undef, undef, 0.1;\n\t\t\tprint $client 'AND-THIS';\n\n\t\t} else {\n\n\t\t\tprint $client <<\"EOF\";\nHTTP/1.1 404 Not Found\nConnection: close\n\nOops, '$uri' not found\nEOF\n\t\t}\n\n\t\tclose $client;\n\t}\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_upgrade.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for http proxy upgrade support.\n# In contrast to proxy_websocket.t, this test doesn't try to use binary\n# WebSocket protocol, but uses simple plain text protocol instead.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse IO::Poll;\nuse IO::Select;\nuse Socket qw/ CRLF /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy ssi/)\n\t->write_file_expand('nginx.conf', <<'EOF')->plan(31);\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    log_format test \"$bytes_sent $body_bytes_sent $sent_http_connection\";\n    access_log %%TESTDIR%%/cc.log test;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            proxy_pass    http://127.0.0.1:8081;\n            proxy_http_version 1.1;\n            proxy_set_header Upgrade $http_upgrade;\n            proxy_set_header Connection \"Upgrade\";\n            proxy_read_timeout 2s;\n            send_timeout 2s;\n        }\n\n        location /ssi.html {\n            ssi on;\n        }\n    }\n}\n\nEOF\n\nmy $d = $t->testdir();\n\n$t->write_file('ssi.html', '<!--#include virtual=\"/upgrade\" --> SEE-THIS');\n\n$t->run_daemon(\\&upgrade_fake_daemon);\n$t->run();\n\n$t->waitforsocket('127.0.0.1:' . port(8081))\n\tor die \"Can't start test backend\";\n\n###############################################################################\n\n# establish connection\n\nmy @r;\nmy $s = upgrade_connect();\nok($s, \"handshake\");\n\nSKIP: {\n\tskip \"handshake failed\", 22 unless $s;\n\n\t# send a frame\n\n\tupgrade_write($s, 'foo');\n\tis(upgrade_read($s), 'bar', \"upgrade response\");\n\n\t# send some big frame\n\n\tupgrade_write($s, 'foo' x 16384);\n\tlike(upgrade_read($s), qr/^(bar){16384}$/, \"upgrade big response\");\n\n\t# send multiple frames\n\n\tfor my $i (1 .. 10) {\n\t\tupgrade_write($s, ('foo' x 16384) . $i, continue => 1);\n\t\tupgrade_write($s, 'bazz' . $i, continue => $i != 10);\n\t}\n\n\tfor my $i (1 .. 10) {\n\t\tlike(upgrade_read($s), qr/^(bar){16384}\\d+$/, \"upgrade $i\");\n\t\tis(upgrade_read($s), 'bazz' . $i, \"upgrade small $i\");\n\t}\n}\n\npush @r, $s ? ${*$s}->{_upgrade_private}->{r} : 'failed';\nundef $s;\n\n# establish connection with some pipelined data\n# and make sure they are correctly passed upstream\n\n$s = upgrade_connect(message => \"foo\");\nok($s, \"handshake pipelined\");\n\nSKIP: {\n\tskip \"handshake failed\", 2 unless $s;\n\n\tis(upgrade_read($s), \"bar\", \"response pipelined\");\n\n\tupgrade_write($s, \"foo\");\n\tis(upgrade_read($s), \"bar\", \"next to pipelined\");\n}\n\npush @r, $s ? ${*$s}->{_upgrade_private}->{r} : 'failed';\nundef $s;\n\n# connection should not be upgraded unless upgrade was actually\n# requested and allowed by configuration\n\n$s = upgrade_connect(noheader => 1);\nok(!$s, \"handshake noupgrade\");\n\n# connection upgrade in subrequests shouldn't cause a segfault\n\n$s = upgrade_connect(uri => '/ssi.html');\nok(!$s, \"handshake in subrequests\");\n\n# bytes sent on upgraded connection\n# verify with 1) data actually read by client, 2) expected data from backend\n\n$t->stop();\n\nopen my $f, '<', \"$d/cc.log\" or die \"Can't open cc.log: $!\";\n\nis($f->getline(), shift (@r) . \" 540793 upgrade\\n\", 'log - bytes');\nis($f->getline(), shift (@r) . \" 22 upgrade\\n\", 'log - bytes pipelined');\nlike($f->getline(), qr/\\d+ 0 /, 'log - bytes noupgrade');\n\n###############################################################################\n\nsub upgrade_connect {\n\tmy (%opts) = @_;\n\n\tmy $s = IO::Socket::INET->new(\n\t\tProto => 'tcp',\n\t\tPeerAddr => '127.0.0.1:' . port(8080),\n\t)\n\t\tor die \"Can't connect to nginx: $!\\n\";\n\n\t# send request, $h->to_string\n\n\tmy $uri = $opts{uri} || '/';\n\n\tmy $buf = \"GET $uri HTTP/1.1\" . CRLF\n\t\t. \"Host: localhost\" . CRLF\n\t\t. ($opts{noheader} ? '' : \"Upgrade: foo\" . CRLF)\n\t\t. \"Connection: Upgrade\" . CRLF . CRLF;\n\n\t$buf .= $opts{message} . CRLF . 'FIN' if defined $opts{message};\n\n\tlocal $SIG{PIPE} = 'IGNORE';\n\n\tlog_out($buf);\n\t$s->syswrite($buf);\n\n\t# read response\n\n\tmy $got = '';\n\t$buf = '';\n\n\twhile (1) {\n\t\t$buf = upgrade_getline($s);\n\t\tlast unless defined $buf and length $buf;\n\t\tlog_in($buf);\n\t\t$got .= $buf;\n\t\tlast if $got =~ /\\x0d?\\x0a\\x0d?\\x0a$/;\n\t}\n\n\t# parse server response\n\n\treturn if $got !~ m!HTTP/1.1 101!;\n\n\t# make sure next line is \"handshaked\"\n\n\t$buf = upgrade_read($s);\n\n\treturn if !defined $buf or $buf ne 'handshaked';\n\treturn $s;\n}\n\nsub upgrade_getline {\n\tmy ($s) = @_;\n\tmy ($h, $buf);\n\n\t${*$s}->{_upgrade_private} ||= { b => '', r => 0 };\n\t$h = ${*$s}->{_upgrade_private};\n\n\tif ($h->{b} =~ /^(.*?\\x0a)(.*)/ms) {\n\t\t$h->{b} = $2;\n\t\treturn $1;\n\t}\n\n\t$s->blocking(0);\n\twhile (IO::Select->new($s)->can_read(3)) {\n\t\tmy $n = $s->sysread($buf, 1024);\n\t\tlast unless $n;\n\n\t\t$h->{b} .= $buf;\n\t\t$h->{r} += $n;\n\n\t\tif ($h->{b} =~ /^(.*?\\x0a)(.*)/ms) {\n\t\t\t$h->{b} = $2;\n\t\t\treturn $1;\n\t\t}\n\t};\n}\n\nsub upgrade_write {\n\tmy ($s, $message, %extra) = @_;\n\n\t$message = $message . CRLF;\n\t$message = $message . 'FIN' unless $extra{continue};\n\n\tlocal $SIG{PIPE} = 'IGNORE';\n\n\t$s->blocking(0);\n\twhile (IO::Select->new($s)->can_write(1.5)) {\n\t\tmy $n = $s->syswrite($message);\n\t\tlast unless $n;\n\t\t$message = substr($message, $n);\n\t\tlast unless length $message;\n\t}\n\n\tif (length $message) {\n\t\t$s->close();\n\t}\n}\n\nsub upgrade_read {\n\tmy ($s) = @_;\n\tmy $m = upgrade_getline($s);\n\t$m =~ s/\\x0d?\\x0a// if defined $m;\n\tlog_in($m);\n\treturn $m;\n}\n\n###############################################################################\n\nsub upgrade_fake_daemon {\n\tmy $server = IO::Socket::INET->new(\n\t\tProto => 'tcp',\n\t\tLocalAddr => '127.0.0.1:' . port(8081),\n\t\tListen => 5,\n\t\tReuse => 1\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\twhile (my $client = $server->accept()) {\n\t\tupgrade_handle_client($client);\n\t}\n}\n\nsub upgrade_handle_client {\n\tmy ($client) = @_;\n\n\t$client->autoflush(1);\n\t$client->blocking(0);\n\n\tmy $poll = IO::Poll->new;\n\n\tmy $handshake = 1;\n\tmy $unfinished = '';\n\tmy $buffer = '';\n\tmy $n;\n\n\tlog2c(\"(new connection $client)\");\n\n\twhile (1) {\n\t\t$poll->mask($client => ($buffer ? POLLIN|POLLOUT : POLLIN));\n\t\tmy $p = $poll->poll(0.5);\n\t\tlog2c(\"(poll $p)\");\n\n\t\tforeach my $reader ($poll->handles(POLLIN)) {\n\t\t\t$n = $client->sysread(my $chunk, 65536);\n\t\t\treturn unless $n;\n\n\t\t\tlog2i($chunk);\n\n\t\t\tif ($handshake) {\n\t\t\t\t$buffer .= $chunk;\n\t\t\t\tnext unless $buffer =~ /\\x0d?\\x0a\\x0d?\\x0a$/;\n\n\t\t\t\tlog2c(\"(handshake done)\");\n\n\t\t\t\t$handshake = 0;\n\t\t\t\t$buffer = 'HTTP/1.1 101 Switching' . CRLF\n\t\t\t\t\t. 'Upgrade: foo' . CRLF\n\t\t\t\t\t. 'Connection: Upgrade' . CRLF . CRLF\n\t\t\t\t\t. 'handshaked' . CRLF;\n\n\t\t\t\tlog2o($buffer);\n\n\t\t\t\tnext;\n\t\t\t}\n\n\t\t\t$unfinished .= $chunk;\n\n\t\t\tif ($unfinished =~ m/\\x0d?\\x0aFIN\\z/) {\n\t\t\t\t$unfinished =~ s/FIN\\z//;\n\t\t\t\t$unfinished =~ s/foo/bar/g;\n\t\t\t\tlog2o($unfinished);\n\t\t\t\t$buffer .= $unfinished;\n\t\t\t\t$unfinished = '';\n\t\t\t}\n\t\t}\n\n\t\tforeach my $writer ($poll->handles(POLLOUT)) {\n\t\t\tnext unless length $buffer;\n\t\t\t$n = $writer->syswrite($buffer);\n\t\t\tsubstr $buffer, 0, $n, '';\n\t\t}\n\t}\n}\n\nsub log2i { Test::Nginx::log_core('|| <<', @_); }\nsub log2o { Test::Nginx::log_core('|| >>', @_); }\nsub log2c { Test::Nginx::log_core('||', @_); }\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_upstream_cookie.t",
    "content": "#!/usr/bin/perl\n\n# (C) Nginx, Inc.\n\n# Tests for the $upstream_cookie_<name> variables.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy rewrite/)->plan(19);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            add_header X-Upstream-Cookie $upstream_cookie_tc;\n            proxy_pass http://127.0.0.1:8081;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        location / {\n            add_header Set-Cookie $http_x_test_cookie;\n            return 204;\n        }\n\n        # embed multiline cookie with add_header\n        location /mcomma {\n            add_header Set-Cookie \"tc=one,two,three\";\n            add_header Set-Cookie \"tc=four,five,six\";\n            return 204;\n        }\n        location /msemicolon {\n            add_header Set-Cookie \"tc=one;two;three\";\n            add_header Set-Cookie \"tc=four;five;six\";\n            return 204;\n        }\n    }\n}\n\nEOF\n\n$t->run();\n\n###############################################################################\n\nis(http_get_uc('tc='), undef, 'value_none');\nis(http_get_uc('tc=;'), undef, 'semicolon');\nis(http_get_uc('tc= ;'), undef, 'space_semicolon');\nis(http_get_uc('tc =   ; Domain=example.com;'), undef, 'space_semicolon_more');\n\nis(http_get_uc('tc=x'), 'x', 'onechar');\nis(http_get_uc('tc=,'), ',', 'comma');\nis(http_get_uc('tc\t=\tcontent     ;'), undef, 'tabbed');\nis(http_get_uc('tc=\"content\"'), '\"content\"', 'dquoted');\nis(http_get_uc('tc=content'), 'content', 'normal');\nis(http_get_uc('tc=con  tent; Domain=example.com'), 'con  tent',\n\t'internal_space');\nis(http_get_uc('tc = content'), 'content', 'separated');\n\nis(http_get_uc('tc=1.2.3'), '1.2.3', 'dots');\nis(http_get_uc('tc==abc'), '=abc', 'deq');\nis(http_get_uc('tc==;abc'), '=', 'deqsemi');\nis(http_get_uc('=tc=content'), undef, 'eqfirst');\nis(http_get_uc('tc=first,tc=second'), 'first,tc=second', 'two_comma');\nis(http_get_uc('tc=first;tc=second'), 'first', 'two_semicolon');\n\nlike(http_get('/mcomma'), qr/^X-Upstream-Cookie: one,two,three\\x0d?$/mi,\n\t'multiline comma');\nlike(http_get('/msemicolon'), qr/^X-Upstream-Cookie: one\\x0d?$/mi,\n\t'multiline semicolon');\n\n###############################################################################\n\nsub http_get_uc {\n\tmy ($cookie) = @_;\n\n\thttp(<<EOF) =~ qr/^X-Upstream-Cookie:\\s(.+?)\\x0d?$/mi;\nGET / HTTP/1.1\nHost: localhost\nConnection: close\nX-Test-Cookie: $cookie\n\nEOF\n\n\treturn $1;\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_variables.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for http proxy module with upstream variables.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy/)->plan(4)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    log_format u $uri:$upstream_response_length:$upstream_bytes_received:\n                 $upstream_bytes_sent:$upstream_http_x_len;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            proxy_pass http://127.0.0.1:8081;\n            access_log %%TESTDIR%%/test.log u;\n        }\n    }\n}\n\nEOF\n\n$t->run_daemon(\\&http_daemon, port(8081));\n$t->run();\n\n$t->waitforsocket('127.0.0.1:' . port(8081));\n\n###############################################################################\n\nmy $r;\n\nmy ($l1) = ($r = http_get('/')) =~ /X-Len: (\\d+)/;\nlike($r, qr/SEE-THIS/, 'proxy request');\n\nmy ($l2) = ($r = http_get('/multi')) =~ /X-Len: (\\d+)/;\nlike($r, qr/AND-THIS/, 'proxy request with multiple packets');\n\n$t->stop();\n\nmy $f = $t->read_file('test.log');\nTest::Nginx::log_core('||', $f);\n\nlike($f, qr!^/:23:68:$l1:$l1!m, 'log - response length');\nlike($f, qr!^/multi:32:77:$l2:$l2!m, 'log - response length - multi packets');\n\n###############################################################################\n\nsub http_daemon {\n\tmy ($port) = @_;\n\tmy $server = IO::Socket::INET->new(\n\t\tProto => 'tcp',\n\t\tLocalHost => '127.0.0.1',\n\t\tLocalPort => $port,\n\t\tListen => 5,\n\t\tReuse => 1\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\tlocal $SIG{PIPE} = 'IGNORE';\n\n\twhile (my $client = $server->accept()) {\n\t\t$client->autoflush(1);\n\n\t\tmy $headers = '';\n\t\tmy $uri = '';\n\n\t\twhile (<$client>) {\n\t\t\t$headers .= $_;\n\t\t\tlast if (/^\\x0d?\\x0a?$/);\n\t\t}\n\n\t\t$uri = $1 if $headers =~ /^\\S+\\s+([^ ]+)\\s+HTTP/i;\n\t\tmy $len = length($headers);\n\n\t\tif ($uri eq '/') {\n\t\t\tprint $client <<\"EOF\";\nHTTP/1.1 200 OK\nConnection: close\nX-Len: $len\n\nEOF\n\t\t\tprint $client \"TEST-OK-IF-YOU-SEE-THIS\"\n\t\t\t\tunless $headers =~ /^HEAD/i;\n\n\t\t} elsif ($uri eq '/multi') {\n\n\t\t\tprint $client <<\"EOF\";\nHTTP/1.1 200 OK\nConnection: close\nX-Len: $len\n\nTEST-OK-IF-YOU-SEE-THIS\nEOF\n\n\t\t\tselect undef, undef, undef, 0.1;\n\t\t\tprint $client 'AND-THIS';\n\t\t}\n\n\t\tclose $client;\n\t}\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_websocket.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for http proxy websockets support.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse IO::Poll;\nuse IO::Select;\nuse IO::Socket::INET;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\neval {\n\trequire Protocol::WebSocket::Handshake::Client;\n\trequire Protocol::WebSocket::Handshake::Server;\n\trequire Protocol::WebSocket::Frame;\n};\n\nplan(skip_all => 'Protocol::WebSocket not installed') if $@;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy/)\n\t->write_file_expand('nginx.conf', <<'EOF')->plan(26);\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            proxy_pass    http://127.0.0.1:8081;\n            proxy_http_version 1.1;\n            proxy_set_header Upgrade $http_upgrade;\n            proxy_set_header Connection \"Upgrade\";\n            proxy_read_timeout 2s;\n            send_timeout 2s;\n        }\n    }\n}\n\nEOF\n\n$t->run_daemon(\\&websocket_fake_daemon);\n$t->run();\n\n$t->waitforsocket('127.0.0.1:' . port(8081))\n\tor die \"Can't start test backend\";\n\n###############################################################################\n\n# establish websocket connection\n\nmy $s = websocket_connect();\nok($s, \"websocket handshake\");\n\nSKIP: {\n\tskip \"handshake failed\", 22 unless $s;\n\n\t# send a frame\n\n\twebsocket_write($s, 'foo');\n\tis(websocket_read($s), 'bar', \"websocket response\");\n\n\t# send some big frame\n\n\twebsocket_write($s, 'foo' x 16384);\n\tlike(websocket_read($s), qr/^(bar){16384}$/, \"websocket big response\");\n\n\t# send multiple frames\n\n\tfor my $i (1 .. 10) {\n\t\twebsocket_write($s, ('foo' x 16384) . $i);\n\t\twebsocket_write($s, 'bazz' . $i);\n\t}\n\n\tfor my $i (1 .. 10) {\n\t\tlike(websocket_read($s), qr/^(bar){16384}\\d+$/, \"websocket $i\");\n\t\tis(websocket_read($s), 'bazz' . $i, \"websocket small $i\");\n\t}\n}\n\n# establish websocket connection with some pipelined data\n# and make sure they are correctly passed upstream\n\nundef $s;\n$s = websocket_connect(\"foo\");\nok($s, \"handshake pipelined\");\n\nSKIP: {\n\tskip \"handshake failed\", 2 unless $s;\n\n\tis(websocket_read($s), \"bar\", \"response pipelined\");\n\n\twebsocket_write($s, \"foo\");\n\tis(websocket_read($s), \"bar\", \"next to pipelined\");\n}\n\n###############################################################################\n\nsub websocket_connect {\n\tmy ($message) = @_;\n\n\tmy $s = IO::Socket::INET->new(\n\t\tProto => 'tcp',\n\t\tPeerAddr => '127.0.0.1:' . port(8080)\n\t)\n\t\tor die \"Can't connect to nginx: $!\\n\";\n\n\tmy $h = Protocol::WebSocket::Handshake::Client->new(\n\t\turl => 'ws://localhost');\n\n\t# send request, $h->to_string\n\n\tmy $buf = $h->to_string;\n\t$buf .= Protocol::WebSocket::Frame->new($message)->to_bytes\n\t\tif $message;\n\n\tlocal $SIG{PIPE} = 'IGNORE';\n\n\tlog_out($buf);\n\t$s->syswrite($buf);\n\n\t# read response\n\n\tmy $got = '';\n\t$buf = '';\n\n\t$s->blocking(0);\n\twhile (IO::Select->new($s)->can_read(1.5)) {\n\t\tmy $n = $s->sysread($buf, 1024);\n\t\tlast unless $n;\n\t\tlog_in($buf);\n\t\t$got .= $buf;\n\t\tlast if $got =~ /\\x0d?\\x0a\\x0d?\\x0a$/;\n\t}\n\n\t# parse server response\n\n\t$h->parse($got);\n\n\t# store the rest for later websocket_read()\n\t# see websocket_read() for details\n\n\t${*$s}->{_websocket_frame} ||= Protocol::WebSocket::Frame->new();\n\t${*$s}->{_websocket_frame}->append($got);\n\n\treturn $s if $h->is_done;\n}\n\nsub websocket_write {\n\tmy ($s, $message) = @_;\n\tmy $frame = Protocol::WebSocket::Frame->new($message);\n\n\tlocal $SIG{PIPE} = 'IGNORE';\n\t$s->blocking(1);\n\n\tlog_out($frame->to_bytes);\n\t$s->syswrite($frame->to_bytes);\n}\n\nsub websocket_read {\n\tmy ($s) = @_;\n\tmy ($buf, $got);\n\n\t# store frame object in socket itself to simplify things\n\t# this works as $s is IO::Handle, see man IO::Handle\n\n\t${*$s}->{_websocket_frame} ||= Protocol::WebSocket::Frame->new();\n\tmy $frame = ${*$s}->{_websocket_frame};\n\n\t$s->blocking(0);\n\t$got = $frame->next();\n\treturn $got if defined $got;\n\n\twhile (IO::Select->new($s)->can_read(1.5)) {\n\t\tmy $n = $s->sysread($buf, 65536);\n\t\treturn $got unless $n;\n\t\tlog_in($buf);\n\t\t$frame->append($buf);\n\t\t$got = $frame->next();\n\t\treturn $got if defined $got;\n\t}\n}\n\n###############################################################################\n\nsub websocket_fake_daemon {\n\tmy $server = IO::Socket::INET->new(\n\t\tProto => 'tcp',\n\t\tLocalAddr => '127.0.0.1:' . port(8081),\n\t\tListen => 5,\n\t\tReuse => 1\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\twhile (my $client = $server->accept()) {\n\t\twebsocket_handle_client($client);\n\t}\n}\n\nsub websocket_handle_client {\n\tmy ($client) = @_;\n\n\t$client->autoflush(1);\n\t$client->blocking(0);\n\n\tmy $poll = IO::Poll->new;\n\n\tmy $hs = Protocol::WebSocket::Handshake::Server->new;\n\tmy $frame = Protocol::WebSocket::Frame->new;\n\tmy $buffer = '';\n\tmy $closed;\n\tmy $n;\n\n\tlog2c(\"(new connection $client)\");\n\n\twhile (1) {\n\t\t$poll->mask($client => ($buffer ? POLLIN|POLLOUT : POLLIN));\n\t\tmy $p = $poll->poll(0.5);\n\t\tlog2c(\"(poll $p)\");\n\n\t\tforeach ($poll->handles(POLLIN)) {\n\t\t\t$n = $client->sysread(my $chunk, 65536);\n\t\t\treturn unless $n;\n\n\t\t\tlog2i($chunk);\n\n\t\t\tif (!$hs->is_done) {\n\t\t\t\tunless (defined $hs->parse($chunk)) {\n\t\t\t\t\tlog2c(\"(error: \" . $hs->error . \")\");\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif ($hs->is_done) {\n\t\t\t\t\t$buffer = $hs->to_string;\n\t\t\t\t\tlog2o($buffer);\n\t\t\t\t}\n\n\t\t\t\tlog2c(\"(parse: $chunk)\");\n\t\t\t}\n\n\t\t\t$frame->append($chunk);\n\n\t\t\twhile (defined(my $message = $frame->next)) {\n\t\t\t\tmy $f;\n\n\t\t\t\tif ($frame->is_close) {\n\t\t\t\t\tlog2c(\"(close frame)\");\n\t\t\t\t\t$closed = 1;\n\t\t\t\t\t$f = $frame->new(type => 'close')\n\t\t\t\t\t\t->to_bytes;\n\t\t\t\t} else {\n\t\t\t\t\t$message =~ s/foo/bar/g;\n\t\t\t\t\t$f = $frame->new($message)->to_bytes;\n\t\t\t\t}\n\n\t\t\t\tlog2o($f);\n\t\t\t\t$buffer .= $f;\n\t\t\t}\n\t\t}\n\n\t\tforeach my $writer ($poll->handles(POLLOUT)) {\n\t\t\tnext unless length $buffer;\n\t\t\t$n = $writer->syswrite($buffer);\n\t\t\tsubstr $buffer, 0, $n, '';\n\t\t}\n\n\t\tif ($closed && length $buffer == 0) {\n\t\t\tlog2c(\"(closed)\");\n\t\t\treturn;\n\t\t}\n\t}\n}\n\nsub log2i { Test::Nginx::log_core('|| <<', @_); }\nsub log2o { Test::Nginx::log_core('|| >>', @_); }\nsub log2c { Test::Nginx::log_core('||', @_); }\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/proxy_xar.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for proxy X-Accel-Redirect functionality.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy rewrite/)->plan(16);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        # catch safe and unhandled unsafe URIs,\n        # bypassed with redirect to named location\n        if ($upstream_http_x_accel_redirect) {\n            return 200 \"xar: $upstream_http_x_accel_redirect uri: $uri\n                        method: $request_method\";\n        }\n\n        location /proxy {\n            proxy_pass http://127.0.0.1:8080/return-xar;\n        }\n        location /return-xar {\n            add_header  X-Accel-Redirect     $arg_xar;\n\n            # this headers will be preserved on\n            # X-Accel-Redirect\n\n            add_header  Content-Type         text/blah;\n            add_header  Set-Cookie           blah=blah;\n            add_header  Content-Disposition  attachment;\n            add_header  Cache-Control        no-cache;\n            add_header  Expires              fake;\n            add_header  Accept-Ranges        parrots;\n\n            # others won't be\n            add_header  Something            other;\n\n            return 204;\n        }\n        location @named {\n            return 200 \"named xar: $upstream_http_x_accel_redirect uri: $uri\";\n        }\n    }\n}\n\nEOF\n\n$t->run();\n\n###############################################################################\n\nmy $r = http_get('/proxy?xar=/index.html');\nlike($r, qr/xar: \\/index.html uri: \\/index.html/, 'X-Accel-Redirect works');\nlike($r, qr/^Content-Type: text\\/blah/m, 'Content-Type preserved');\nlike($r, qr/^Set-Cookie: blah=blah/m, 'Set-Cookie preserved');\nlike($r, qr/^Content-Disposition: attachment/m, 'Content-Disposition preserved');\nlike($r, qr/^Cache-Control: no-cache/m, 'Cache-Control preserved');\nlike($r, qr/^Expires: fake/m, 'Expires preserved');\nlike($r, qr/^Accept-Ranges: parrots/m, 'Accept-Ranges preserved');\nunlike($r, qr/^Something/m, 'other headers stripped');\n\nlike(http_post('/proxy?xar=/index.html'), qr/method: GET/,\n\t'X-Accel-Redirect method name');\n\n# escaped characters\n\nlike(http_get('/proxy?xar=/foo?bar'), qr/200 OK.*xar: \\/foo\\?bar/s,\n\t'X-Accel-Redirect value unchanged');\nunlike(http_get('/proxy?xar=..'), qr/200 OK/,\n\t'X-Accel-Redirect unsafe dotdot');\nunlike(http_get('/proxy?xar=../foo'), qr/200 OK/,\n\t'X-Accel-Redirect unsafe dotdotsep');\nunlike(http_get('/proxy?xar=/foo/..'), qr/200 OK/,\n\t'X-Accel-Redirect unsafe sepdotdot');\nunlike(http_get('/proxy?xar=/foo/.%2e'), qr/200 OK/,\n\t'X-Accel-Redirect unsafe unescaped');\nlike(http_get('/proxy?xar=/foo%20bar'), qr/uri: \\/foo bar/,\n\t'X-Accel-Redirect unescaped');\n\nlike(http_get('/proxy?xar=@named'),\n\tqr!200 OK.*named xar: \\@named uri: /proxy!s, 'in named location');\n\n###############################################################################\n\nsub http_post {\n\tmy ($url) = @_;\n\thttp(<<EOF);\nPOST $url HTTP/1.0\nHost: localhost\n\nEOF\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/random_index.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for random index module.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http random_index symlink/)->plan(1)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            random_index on;\n        }\n    }\n}\n\nEOF\n\nmy $d = $t->testdir();\n\nmkdir(\"$d/x\");\nmkdir(\"$d/x/test-dir\");\nsymlink(\"$d/x/test-dir\", \"$d/x/test-dir-link\");\n\n$t->write_file('test-file', 'RIGHT');\nsymlink(\"$d/test-file\", \"$d/x/test-file-link\");\n\n$t->run();\n\n###############################################################################\n\nlike(http_get('/x/'), qr/RIGHT/s, 'file');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/range.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for range filter module.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http charset/)->plan(41);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    charset_map B A {\n        58 59; # X -> Y\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location /t2.html {\n            charset A;\n            source_charset B;\n        }\n\n        location /t3.html {\n            max_ranges 2;\n        }\n\n        location /t4.html {\n            max_ranges 0;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('t1.html',\n\tjoin('', map { sprintf \"X%03dXXXXXX\", $_ } (0 .. 99)));\n$t->write_file('t2.html',\n\tjoin('', map { sprintf \"X%03dXXXXXX\", $_ } (0 .. 99)));\n$t->write_file('t3.html',\n\tjoin('', map { sprintf \"X%03dXXXXXX\", $_ } (0 .. 99)));\n$t->write_file('t4.html',\n\tjoin('', map { sprintf \"X%03dXXXXXX\", $_ } (0 .. 99)));\n$t->run();\n\n###############################################################################\n\nmy $t1;\n\n$t1 = http_get_range('/t1.html', 'Range: bytes=0-8');\nlike($t1, qr/ 206 /, 'range request - 206 partial reply');\nlike($t1, qr/Content-Length: 9/, 'range request - correct length');\nlike($t1, qr/Content-Range: bytes 0-8\\/1000/, 'range request - content range');\nlike($t1, qr/^X000XXXXX$/m, 'range request - correct content');\n\n$t1 = http_get_range('/t1.html', 'Range: bytes=-10');\nlike($t1, qr/ 206 /, 'final bytes - 206 partial reply');\nlike($t1, qr/Content-Length: 10/, 'final bytes - content length');\nlike($t1, qr/Content-Range: bytes 990-999\\/1000/,\n\t'final bytes - content range');\nlike($t1, qr/^X099XXXXXX$/m, 'final bytes - correct content');\n\n$t1 = http_get_range('/t1.html', 'Range: bytes=990-');\nlike($t1, qr/ 206 /, 'final bytes explicit - 206 partial reply');\nlike($t1, qr/Content-Length: 10/, 'final bytes explicit - content length');\nlike($t1, qr/Content-Range: bytes 990-999\\/1000/,\n\t'final bytes explicit - content range');\nlike($t1, qr/^X099XXXXXX$/m, 'final bytes explicit - correct content');\n\n$t1 = http_get_range('/t1.html', 'Range: bytes=990-1990');\nlike($t1, qr/ 206 /, 'more than length - 206 partial reply');\nlike($t1, qr/Content-Length: 10/, 'more than length - content length');\nlike($t1, qr/Content-Range: bytes 990-999\\/1000/,\n\t'more than length - content range');\nlike($t1, qr/^X099XXXXXX$/m, 'more than length - correct content');\n\n$t1 = http_get_range('/t2.html', 'Range: bytes=990-1990');\nlike($t1, qr/ 206 /, 'recoded - 206 partial reply');\nlike($t1, qr/Content-Length: 10/, 'recoded - content length');\nlike($t1, qr/Content-Range: bytes 990-999\\/1000/, 'recoded - content range');\nlike($t1, qr/^Y099YYYYYY$/m, 'recoded - correct content');\n\n$t1 = http_get_range('/t1.html', 'Range: bytes=0-9, -10, 10-19');\nlike($t1, qr/ 206 /, 'multipart - 206 partial reply');\nlike($t1, qr/Content-Type: multipart\\/byteranges; boundary=/,\n\t'multipart - content type');\nlike($t1, qr/X000XXXXXX/m, 'multipart - content 0-9');\nlike($t1, qr/^X099XXXXXX\\x0d?$/m, 'multipart - content -10 aka 990-999');\nlike($t1, qr/X001XXXXXX\\x0d?$/m, 'multipart - content 10-19');\n\n$t1 = http_get_range('/t1.html', 'Range: bytes=0-9, -10, 100000-, 10-19');\nlike($t1, qr/ 206 /, 'multipart big - 206 partial reply');\nlike($t1, qr/Content-Type: multipart\\/byteranges; boundary=/,\n\t'multipart big - content type');\nlike($t1, qr/X000XXXXXX/m, 'multipart big - content 0-9');\nlike($t1, qr/^X099XXXXXX\\x0d?$/m, 'multipart big - content -10 aka 990-999');\nlike($t1, qr/X001XXXXXX\\x0d?$/m, 'multipart big - content 10-19');\n\nlike(http_get_range('/t1.html', 'Range: bytes=100000-'), qr/ 416 /,\n\t'not satisfiable - too big first byte pos');\nlike(http_get_range('/t1.html', 'Range: bytes=alpha'), qr/ 416 /,\n\t'not satisfiable - alpha in first byte pos');\nlike(http_get_range('/t1.html', 'Range: bytes=10-alpha'), qr/ 416 /,\n\t'not satisfiable - alpha in last byte pos');\nlike(http_get_range('/t1.html', 'Range: bytes=10'), qr/ 416 /,\n\t'not satisfiable - no hyphen');\nlike(http_get_range('/t1.html', 'Range: bytes=10-11 12-'), qr/ 416 /,\n\t'not satisfiable - no comma');\n\n# last-byte-pos is taken to be equal to one less than the current length\n# of the entity-body in bytes -- rfc2616 sec 14.35.\n\nlike(http_get_range('/t1.html', 'Range: bytes=0-10001'), qr/ 206 /,\n\t'satisfiable - last byte pos adjusted');\n\n# total size of all ranges is greater than source response size\n\nlike(http_get_range('/t1.html', 'Range: bytes=0-10001, 0-0'), qr/ 200 /,\n\t'not satisfiable - malicious byte ranges');\n\nlike(http_get_range('/t3.html', 'Range: bytes=0-9, -10'), qr/ 206 /,\n\t'max_ranges not reached');\nlike(http_get_range('/t3.html', 'Range: bytes=0-9, -10, 10000-'), qr/ 206 /,\n\t'max_ranges not reached bad range');\nunlike(http_get_range('/t3.html', 'Range: bytes=0-9, -10, 10-19'),\n\tqr/ 206 /, 'max_ranges reached');\nunlike(http_get_range('/t4.html', 'Range: bytes=0-9'), qr/ 206 /,\n\t'max_ranges zero');\n\n###############################################################################\n\nsub http_get_range {\n\tmy ($url, $extra) = @_;\n\treturn http(<<EOF);\nGET $url HTTP/1.1\nHost: localhost\nConnection: close\n$extra\n\nEOF\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/range_charset.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for range filter on proxied response with charset.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy cache charset/)->plan(10)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    charset_map B A {\n        58 59; # X -> Y\n    }\n\n    proxy_cache_path   %%TESTDIR%%/cache  levels=1:2\n                       keys_zone=NAME:1m;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            proxy_pass    http://127.0.0.1:8081;\n            proxy_cache   NAME;\n            proxy_cache_valid 200 1m;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        charset B;\n\n        location /t2.html {\n            add_header X-Accel-Charset A;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('t1.html',\n\tjoin('', map { sprintf \"X%03dXXXXXX\", $_ } (0 .. 99)));\n$t->write_file('t2.html',\n\tjoin('', map { sprintf \"X%03dXXXXXX\", $_ } (0 .. 99)));\n$t->run();\n\n###############################################################################\n\nmy $t1;\n\n# range request on proxied response with charset attribute in content-type\n# NB: to get partial content, requests need to be served from cache\n\nhttp_get('/t1.html');\n$t1 = http_get_range('/t1.html', 'Range: bytes=0-9, 10-19');\nlike($t1, qr/ 206 /, 'charset - 206 partial reply');\nlike($t1, qr/Content-Type: multipart\\/byteranges; boundary=\\w+\\x0d\\x0a/,\n\t'charset - content type');\nlike($t1, qr/Content-Type: text\\/html; charset=B(?!; charset)/,\n\t'charset - charset attribute');\nlike($t1, qr/X000XXXXXX/m, 'charset - content 0-9');\nlike($t1, qr/X001XXXXXX\\x0d?$/m, 'charset - content 10-19');\n\nhttp_get('/t2.html');\n$t1 = http_get_range('/t2.html', 'Range: bytes=0-9, 10-19');\nlike($t1, qr/ 206 /, 'x-accel-charset - 206 partial reply');\nlike($t1, qr/Content-Type: multipart\\/byteranges; boundary=\\w+\\x0d\\x0a/,\n\t'x-accel-charset - content type');\nlike($t1, qr/Content-Type: text\\/html; charset=A(?!; charset)/,\n\t'x-accel-charset - charset attribute');\nlike($t1, qr/Y000YYYYYY/m, 'x-accel-charset - content 0-9');\nlike($t1, qr/Y001YYYYYY\\x0d?$/m, 'x-accel-charset - content 10-19');\n\n###############################################################################\n\nsub http_get_range {\n\tmy ($url, $extra) = @_;\n\treturn http(<<EOF);\nGET $url HTTP/1.1\nHost: localhost\nConnection: close\n$extra\n\nEOF\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/range_clearing.t",
    "content": "#!/usr/bin/perl\n\n# (C) Eugene Grebenschikov\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for clearing of pre-existing Content-Range headers.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http rewrite proxy cache/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    proxy_cache_path %%TESTDIR%%/cache levels=1:2 keys_zone=NAME:1m;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            proxy_pass http://127.0.0.1:8080/stub;\n            proxy_cache NAME;\n            proxy_cache_valid 200 1m;\n        }\n\n        location /stub {\n            add_header Content-Range stub;\n            add_header Accept-Ranges bytes;\n            return 200 \"SEE-THIS\";\n        }\n    }\n}\n\nEOF\n\n$t->run()->plan(3);\n\n###############################################################################\n\nlocal $TODO = 'not yet' unless $t->has_version('1.23.1');\n\nlike(http_get_range('/', 'Range: bytes=0-4'),\n\tqr/ 206 (?!.*stub)/s, 'content range cleared - range request');\nlike(http_get_range('/', 'Range: bytes=0-2,4-'),\n\tqr/ 206 (?!.*stub)/s, 'content range cleared - multipart');\nlike(http_get_range('/', 'Range: bytes=1000-'),\n\tqr/ 416 (?!.*stub)/s, 'content range cleared - not satisfable');\n\n###############################################################################\n\nsub http_get_range {\n\tmy ($url, $extra) = @_;\n\treturn http(<<EOF);\nGET $url HTTP/1.1\nHost: localhost\nConnection: close\n$extra\n\nEOF\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/range_flv.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for range filter module.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http flv/)->plan(12);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n        location / {\n            flv;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('t1.flv',\n\tjoin('', map { sprintf \"X%03dXXXXXX\", $_ } (0 .. 99)));\n$t->run();\n\n###############################################################################\n\nmy $t1;\n\n# FLV has 13 byte header at start.\n\n$t1 = http_get_range('/t1.flv?start=100', 'Range: bytes=0-9');\nlike($t1, qr/ 206 /, 'first bytes - 206 partial reply');\nlike($t1, qr/Content-Length: 10/, 'first bytes - correct length');\nlike($t1, qr/Content-Range: bytes 0-9\\/913/, 'first bytes - content range');\nlike($t1, qr/^FLV.{7}$/m, 'first bytes - correct content');\n\n$t1 = http_get_range('/t1.flv?start=100', 'Range: bytes=-10');\nlike($t1, qr/ 206 /, 'final bytes - 206 partial reply');\nlike($t1, qr/Content-Length: 10/, 'final bytes - content length');\nlike($t1, qr/Content-Range: bytes 903-912\\/913/,\n\t'final bytes - content range');\nlike($t1, qr/^X099XXXXXX$/m, 'final bytes - correct content');\n\n$t1 = http_get_range('/t1.flv?start=100', 'Range: bytes=0-99');\nlike($t1, qr/ 206 /, 'multi buffers - 206 partial reply');\nlike($t1, qr/Content-Length: 100/, 'multi buffers - content length');\nlike($t1, qr/Content-Range: bytes 0-99\\/913/, 'multi buffers - content range');\nlike($t1, qr/^FLV.{10}X010XXXXXX(X01[1-7]XXXXXX){7}X018XXX$/m,\n\t'multi buffers - correct content');\n\n###############################################################################\n\nsub http_get_range {\n\tmy ($url, $extra) = @_;\n\treturn http(<<EOF);\nGET $url HTTP/1.1\nHost: localhost\nConnection: close\n$extra\n\nEOF\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/range_if_range.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for range filter module with If-Range header.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http/)->plan(8);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location /t2.html {\n            add_header Last-Modified \"\";\n        }\n\n        location /t3.html {\n            add_header Last-Modified \"Mon, 28 Sep 1970 06:00:00 GMT\";\n        }\n    }\n}\n\nEOF\n\n$t->write_file('t1.html',\n\tjoin('', map { sprintf \"X%03dXXXXXX\", $_ } (0 .. 99)));\n$t->write_file('t2.html',\n\tjoin('', map { sprintf \"X%03dXXXXXX\", $_ } (0 .. 99)));\n$t->write_file('t3.html',\n\tjoin('', map { sprintf \"X%03dXXXXXX\", $_ } (0 .. 99)));\n$t->run();\n\n###############################################################################\n\nmy $t1;\n\n# If-Range\n\n$t1 = http_get_range('/t1.html', \"Range: bytes=0-9\\nIf-Range: wrong\");\nlike($t1, qr/200 OK/, 'if-range wrong');\nlike($t1, qr/Last-Modified: /, 'if-range wrong - last modified');\n\n$t1 =~ m/Last-Modified: (.*)/m;\nmy $last = $1;\n\n$t1 = http_get_range('/t1.html', \"Range: bytes=0-9\\nIf-Range: $last\");\nlike($t1, qr/ 206 /, 'if-range');\n\n# If-Range + add_header Last-Modified \"\"\n\n$t1 = http_get_range('/t2.html', \"Range: bytes=0-9\\nIf-Range: wrong\");\nlike($t1, qr/200 OK/, 'if-range notime');\nunlike($t1, qr/Last-Modified: /, 'if-range notime - no last modified');\n\n# If-Range + add_header Last-Modified \"Mon, 28 Sep 1970 06:00:00 GMT\"\n\n$t1 = http_get_range('/t3.html', \"Range: bytes=0-9\\nIf-Range: wrong\");\nlike($t1, qr/200 OK/, 'if-range time wrong');\nlike($t1, qr/Last-Modified: Mon, 28 Sep 1970 06:00:00 GMT/,\n\t'if-range time wrong - last modified');\n\n$t1 = http_get_range('/t3.html',\n\t\"Range: bytes=0-9\\nIf-Range: Mon, 28 Sep 1970 06:00:00 GMT\");\nlike($t1, qr/ 206 /, 'if-range time');\n\n###############################################################################\n\nsub http_get_range {\n\tmy ($url, $extra) = @_;\n\treturn http(<<EOF);\nGET $url HTTP/1.1\nHost: localhost\nConnection: close\n$extra\n\nEOF\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/range_mp4.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for mp4 module with range filter module.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http mp4/)->has_daemon('ffmpeg');\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n        location / {\n            mp4;\n        }\n    }\n}\n\nEOF\n\nplan(skip_all => 'no lavfi')\n\tunless grep /lavfi/, `ffmpeg -nostdin -loglevel quiet -formats`;\nsystem('ffmpeg -loglevel quiet -y '\n\t. '-f lavfi -i testsrc=duration=10:size=320x200:rate=15 '\n\t. \"-pix_fmt yuv420p -c:v libx264 ${\\($t->testdir())}/test.mp4\") == 0\n\tor die \"Can't create mp4 file: $!\";\n\n$t->run()->plan(13);\n\n###############################################################################\n\n# simply ensure that mp4 start argument works, we rely on this in range tests\n\nmy $fsz0 = http_head('/test.mp4') =~ /Content-Length: (\\d+)/ && $1;\nmy $fsz = http_head('/test.mp4?start=1') =~ /Content-Length: (\\d+)/ && $1;\nisnt($fsz0, $fsz, 'mp4 start argument works');\n\nmy $t1;\n\n# MP4 has minimally 16 byte ftyp object at start\n\nmy $start = $fsz - 10;\nmy $last = $fsz - 1;\n\n$t1 = http_get_range('/test.mp4?start=1', 'Range: bytes=0-9');\nlike($t1, qr/ 206 /, 'first bytes - 206 partial reply');\nlike($t1, qr/Content-Length: 10/, 'first bytes - content length');\nlike($t1, qr/Content-Range: bytes 0-9\\/$fsz/, 'first bytes - content range');\n\n$t1 = http_get_range('/test.mp4?start=1', 'Range: bytes=-10');\nlike($t1, qr/ 206 /, 'final bytes - 206 partial reply');\nlike($t1, qr/Content-Length: 10/, 'final bytes - content length');\nlike($t1, qr/Content-Range: bytes $start-$last\\/$fsz/,\n\t'final bytes - content range');\n\n$t1 = http_get_range('/test.mp4?start=1', 'Range: bytes=0-99');\nlike($t1, qr/ 206 /, 'multi buffers - 206 partial reply');\nlike($t1, qr/Content-Length: 100/, 'multi buffers - content length');\nlike($t1, qr/Content-Range: bytes 0-99\\/$fsz/,\n\t'multi buffers - content range');\n\nTODO: {\nlocal $TODO = 'multipart range on mp4';\n\n$t1 = http_get_range('/test.mp4?start=1', 'Range: bytes=0-10,11-99');\nlike($t1, qr/ 206 /, 'multipart range - 206 partial reply');\nlike($t1, qr/Content-Length: 100/, 'multipart range - content length');\nlike($t1, qr/Content-Range: bytes 0-10,11-99\\/$fsz/,\n\t'multipart range - content range');\n\n}\n\n###############################################################################\n\nsub http_get_range {\n\tmy ($url, $extra) = @_;\n\treturn http(<<EOF);\nHEAD $url HTTP/1.1\nHost: localhost\nConnection: close\n$extra\n\nEOF\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/realip.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for nginx realip module.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http realip rewrite/);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    add_header X-IP $remote_addr;\n    set_real_ip_from  127.0.0.1/32;\n    set_real_ip_from  10.0.1.0/24;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / { }\n        location /custom {\n            real_ip_header    X-Real-IP-Custom;\n        }\n\n        location /1 {\n            real_ip_header    X-Forwarded-For;\n            real_ip_recursive off;\n        }\n\n        location /2 {\n            real_ip_header    X-Forwarded-For;\n            real_ip_recursive on;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        return 204;\n    }\n}\n\nEOF\n\n$t->write_file('index.html', '');\n$t->write_file('custom', '');\n$t->write_file('1', '');\n$t->write_file('2', '');\n$t->run();\n\nplan(skip_all => 'no 127.0.0.1 on host')\n\tif http_get('/') !~ /X-IP: 127.0.0.1/m;\n\n$t->plan(8);\n\n###############################################################################\n\nlike(http(<<EOF), qr/^X-IP: 192.0.2.1/m, 'realip');\nGET / HTTP/1.0\nHost: localhost\nX-Real-IP: 192.0.2.1\n\nEOF\n\nlike(http(<<EOF), qr/^X-IP: 192.0.2.1/m, 'realip custom');\nGET /custom HTTP/1.0\nHost: localhost\nX-Real-IP-Custom: 192.0.2.1\n\nEOF\n\nlike(http_xff('/1', '10.0.0.1, 192.0.2.1'), qr/^X-IP: 192.0.2.1/m,\n\t'realip multi');\nlike(http_xff('/1', '192.0.2.1, 10.0.1.1, 127.0.0.1'),\n\tqr/^X-IP: 127.0.0.1/m, 'realip recursive off');\nlike(http_xff('/2', '10.0.1.1, 192.0.2.1, 127.0.0.1'),\n\tqr/^X-IP: 192.0.2.1/m, 'realip recursive on');\n\nlike(http(<<EOF), qr/^X-IP: 10.0.1.1/m, 'realip multi xff recursive off');\nGET /1 HTTP/1.0\nHost: localhost\nX-Forwarded-For: 192.0.2.1\nX-Forwarded-For: 127.0.0.1, 10.0.1.1\n\nEOF\n\nlike(http(<<EOF), qr/^X-IP: 192.0.2.1/m, 'realip multi xff recursive on');\nGET /2 HTTP/1.0\nHost: localhost\nX-Forwarded-For: 10.0.1.1\nX-Forwarded-For: 192.0.2.1\nX-Forwarded-For: 127.0.0.1\n\nEOF\n\nmy $s = IO::Socket::INET->new('127.0.0.1:' . port(8081));\nlike(http(<<EOF, socket => $s), qr/ 204 .*192.0.2.1/s, 'realip post read');\nGET / HTTP/1.0\nHost: localhost\nX-Real-IP: 192.0.2.1\n\nEOF\n\n###############################################################################\n\nsub http_xff {\n\tmy ($uri, $xff) = @_;\n\treturn http(<<EOF);\nGET $uri HTTP/1.0\nHost: localhost\nX-Forwarded-For: $xff\n\nEOF\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/realip_hostname.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for nginx realip module, 'unix:' and hostname in set_real_ip_from.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http realip proxy unix/);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        listen       unix:%%TESTDIR%%/unix.sock;\n        server_name  localhost;\n\n        location /1 {\n            set_real_ip_from  localhost;\n            add_header X-IP $remote_addr;\n        }\n\n        location /2 {\n            set_real_ip_from  unix:;\n            add_header X-IP $remote_addr;\n        }\n\n        location /unix {\n            proxy_pass http://unix:%%TESTDIR%%/unix.sock:/;\n            proxy_set_header X-Real-IP 192.0.2.1;\n        }\n\n        location /ip {\n            proxy_pass http://127.0.0.1:8080/;\n            proxy_set_header X-Real-IP 192.0.2.1;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('1', '');\n$t->write_file('2', '');\n$t->run();\n\nplan(skip_all => 'no 127.0.0.1 on host')\n\tif http_get('/1') !~ /X-IP: 127.0.0.1/m;\n\n$t->plan(4);\n\n###############################################################################\n\nlike(http_get('/unix/2'), qr/X-IP: 192.0.2.1/, 'realip unix');\nunlike(http_get('/unix/1'), qr/X-IP: 192.0.2.1/, 'realip unix - no match');\n\nlike(http_get('/ip/1'), qr/X-IP: 192.0.2.1/, 'realip hostname');\nunlike(http_get('/ip/2'), qr/X-IP: 192.0.2.1/, 'realip hostname - no match');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/realip_remote_addr.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for nginx realip module, realip_remote_addr variable.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http realip/);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    set_real_ip_from  127.0.0.1/32;\n    set_real_ip_from  192.0.2.1/32;\n    real_ip_header    X-Forwarded-For;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            add_header X-IP $remote_addr;\n            add_header X-Real-IP $realip_remote_addr;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('index.html', '');\n$t->write_file('1', '');\n$t->run();\n\nplan(skip_all => 'no 127.0.0.1 on host')\n\tif http_get('/') !~ /X-IP: 127.0.0.1/m;\n\n$t->plan(4);\n\n###############################################################################\n\nlike(http_get('/1'), qr/X-Real-IP: 127.0.0.1/m, 'request');\nlike(http_get('/'), qr/X-Real-IP: 127.0.0.1/m, 'request redirect');\n\nlike(http_xff('/1', '192.0.2.1'), qr/X-Real-IP: 127.0.0.1/m, 'realip');\nlike(http_xff('/', '192.0.2.1'), qr/X-Real-IP: 127.0.0.1/m, 'realip redirect');\n\n###############################################################################\n\nsub http_xff {\n\tmy ($uri, $xff) = @_;\n\treturn http(<<EOF);\nGET $uri HTTP/1.0\nHost: localhost\nX-Forwarded-For: $xff\n\nEOF\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/realip_remote_port.t",
    "content": "#!/usr/bin/perl\n\n# (C) Andrey Zelenkov\n# (C) Nginx, Inc.\n\n# Tests for nginx realip module, $realip_remote_port variable.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx qw/ :DEFAULT http_end /;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http realip/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    set_real_ip_from  127.0.0.1/32;\n    real_ip_header    X-Forwarded-For;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            add_header X-IP $remote_addr;\n            add_header X-Real-Port $realip_remote_port;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('index.html', '');\n$t->write_file('1', '');\n$t->run();\n\nplan(skip_all => 'no 127.0.0.1 on host')\n\tif http_get('/') !~ /X-IP: 127.0.0.1/m;\n\n$t->plan(4);\n\n###############################################################################\n\nmy ($sp, $data) = http_sp_get('/1');\nlike($data, qr/X-Real-Port: $sp/, 'request');\n\n($sp, $data) = http_sp_get('/');\nlike($data, qr/X-Real-Port: $sp/, 'request redirect');\n\n($sp, $data) = http_sp_xff('/1', '127.0.0.1:123');\nlike($data, qr/X-Real-Port: $sp/, 'realip');\n\n($sp, $data) = http_sp_xff('/', '127.0.0.1:123');\nlike($data, qr/X-Real-Port: $sp/, 'realip redirect');\n\n###############################################################################\n\nsub http_sp_get {\n\tmy $s = http_get(shift, start => 1);\n\treturn ($s->sockport(), http_end($s));\n}\n\nsub http_sp_xff {\n\tmy ($url, $xff) = @_;\n\n\tmy $s = http(<<EOF, start => 1);\nGET $url HTTP/1.0\nHost: localhost\nX-Forwarded-For: $xff\n\nEOF\n\n\treturn ($s->sockport(), http_end($s));\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/referer.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n\n# Tests for referer module.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http referer rewrite/)->plan(54);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  another;\n\n        valid_referers server_names;\n        return 200 \"$host value $invalid_referer\";\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  _;\n\n        location / {\n            valid_referers server_names;\n            return 200 \"$host value $invalid_referer\";\n        }\n        server_name  below;\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost ~bar ~^anchoredre$;\n\n        location /blocked {\n            valid_referers blocked www.example.org;\n            return 200 \"value $invalid_referer\";\n        }\n        location /none {\n            valid_referers none www.example.org;\n            return 200 \"value $invalid_referer\";\n        }\n        location /simple {\n            valid_referers www.example.org;\n            return 200 \"value $invalid_referer\";\n        }\n        location /regex {\n            valid_referers ~example.org ~(?-i)example.net;\n            return 200 \"value $invalid_referer\";\n        }\n        location /regex2 {\n            valid_referers ~example.org/uri;\n            return 200 \"value $invalid_referer\";\n        }\n        location /regex3 {\n            valid_referers ~example.org$;\n            return 200 \"value $invalid_referer\";\n        }\n        location /uri {\n            valid_referers www.example.org/uri;\n            return 200 \"value $invalid_referer\";\n        }\n        location /sn {\n            valid_referers server_names;\n            return 200 \"value $invalid_referer\";\n        }\n        location /sn_blocked {\n            valid_referers blocked server_names;\n            return 200 \"value $invalid_referer\";\n        }\n        location /wc {\n            valid_referers *.example.com *.example.org www.example.* example.*;\n            return 200 \"value $invalid_referer\";\n        }\n        location /long {\n            valid_referers ~.*;\n            return 200 \"value $invalid_referer\";\n        }\n        location /wc2 {\n            valid_referers www.example.*/uri;\n            return 200 \"value $invalid_referer\";\n        }\n    }\n}\n\nEOF\n\n$t->run();\n\n###############################################################################\n\nok(valid('/simple', 'http://www.example.org'), 'simple');\nok(valid('/simple', 'http://www.example.org/uri'), 'simple uri');\nok(valid('/simple', 'http://www.example.org:' . port(8080) . '/uri'),\n\t'simple port uri');\nok(!valid('/simple', 'localhost'), 'simple invalid');\nok(valid('/simple', 'https://www.example.org'), 'https');\nok(!valid('/simple', 'example.com'), 'no scheme');\nok(!valid('/simple'), 'no none');\nok(valid('/none'), 'none');\nok(!valid('/none', ''), 'none empty');\n\nok(valid('/blocked', 'www.example.org'), 'blocked');\nok(valid('/blocked', 'www.example.com'), 'blocked 2');\nok(valid('/blocked', 'http://su'), 'blocked short');\nok(valid('/blocked', 'foobar'), 'blocked short no scheme');\nok(valid('/blocked', ''), 'blocked empty');\n\nok(!valid('/simple', 'foobar'), 'small');\nok(valid('/simple', 'http://www.example.org/' . 'a' x 256), 'long uri');\nok(!valid('/simple', 'http://www.example.' . 'a' x 256), 'long hostname');\nok(!valid('/wc', 'http://example.' . 'a' x 256), 'long hostname wildcard');\n\nok(valid('/long', 'http://' . 'a' x 255), 'long hostname 255');\nok(valid('/long', 'http://' . 'a' x 256), 'long hostname 256');\nok(!valid('/long', 'http://' . 'a' x 257), 'long hostname 257');\n\nok(valid('/uri', 'http://www.example.org/uri'), 'uri');\nok(valid('/uri', 'http://www.example.org/urii'), 'uri prefix');\nok(!valid('/uri', 'http://www.example.org/uRi'), 'uri case');\nok(valid('/uri', 'http://www.example.org:' . port(8080) . '/urii'), 'uri port');\nok(!valid('/uri', 'http://www.example.org/ur'), 'uri invalid len');\nok(!valid('/uri', 'http://www.example.org/urd'), 'uri invalid cmp');\n\nok(valid('/regex', 'http://www.example.org'), 'regex');\nok(valid('/regex', 'http://www.eXample.org'), 'regex caseless');\nok(valid('/regex', 'http://www.example.org/uri'), 'regex uri');\nok(!valid('/regex', 'http://www.example.com'), 'regex mismatch');\nok(!valid('/regex', 'http://www.eXample.net'), 'regex case mismatch');\n\nok(valid('/regex2', 'http://www.example.org/uri'), 'regex 2 uri');\nok(!valid('/regex2', 'http://www.example.org'), 'regex 2 no uri');\nok(valid('/regex2', 'http://www.example.org/uRI'), 'regex 2 uri caseless');\nok(valid('/regex3', 'https://www.eXample.org'), 'regex https');\n\nok(valid('/sn', 'http://localhost'), 'server_names');\nok(valid('/sn', 'http://localHost'), 'server_names caseless');\nok(valid('/sn', 'http://localhost/uri'), 'server_names uri');\nok(valid('/sn', 'http://foobar'), 'server_names regex');\nok(valid('/sn', 'http://foobAr'), 'server_names regex caseless');\nok(valid('/sn', 'http://foobAr/uri'), 'server_names regex caseless uri');\nok(valid('/sn', 'http://anchoredre/uri'), 'server_names regex anchored');\nok(valid('/sn', 'http://foobar/uri'), 'server_names regex uri');\nok(!valid('/sn', 'localhost'), 'server_names no scheme');\nok(!valid('/sn', 'foobar'), 'server_names regex no scheme');\nok(valid('/sn_blocked', 'localhost'), 'server_names no scheme blocked');\n\nok(valid('/wc', 'http://www.example.org'), 'wildcard head');\nok(valid('/wc', 'http://www.example.net'), 'wildcard tail');\nok(valid('/wc2', 'http://www.example.net/uri'), 'wildcard uri');\nok(valid('/wc2', 'http://www.example.net/urii'), 'wildcard uri prefix');\nok(!valid('/wc2', 'http://www.example.net/uRI'), 'wildcard uri case');\n\nok(valid('/', 'http://another', 'another'), 'server context');\n\n# server_name below valid_referers\n\nok(valid('/', 'http://below', 'below'), 'server below');\n\n###############################################################################\n\nsub valid {\n\tmy ($uri, $referer, $host) = @_;\n\tmy $text;\n\n\t$host = 'localhost' unless defined $host;\n\n\tunless (defined $referer) {\n\t\t$text = http_get($uri);\n\t} else {\n\t\t$text = http(<<EOF);\nGET $uri HTTP/1.0\nHost: $host\nReferer: $referer\n\nEOF\n\t}\n\n\t$text =~ /value 1/ && return 0;\n\t$text =~ /value/ && return 1;\n\tfail(\"no valid_referers in $uri\");\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/request_id.t",
    "content": "#!/usr/bin/perl\n\n# (C) Andrey Zelenkov\n# (C) Nginx, Inc.\n\n# Tests for request_id variable.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http rewrite ssi/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    log_format id $request_id;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        add_header X-Request-Id $request_id;\n        add_header X-blah blah;\n\n        location / {\n            ssi on;\n        }\n        location /body {\n            return 200 $request_id;\n        }\n        location /log {\n            access_log %%TESTDIR%%/id.log id;\n            return 200;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('index.html', '');\n$t->write_file('add.html', '<!--#include virtual=\"/body\" -->');\n$t->run()->plan(12);\n\n###############################################################################\n\nmy ($id1) = http_get('/') =~ qr/^X-Request-Id: (.*)\\x0d/m;\nmy ($id2) = http_get('/') =~ qr/^X-Request-Id: (.*)\\x0d/m;\n\nlike($id1, qr/^[a-z0-9]{32}$/, 'format id 1');\nlike($id2, qr/^[a-z0-9]{32}$/, 'format id 2');\n\nisnt($id1, $id2, 'different id');\n\n# same request\n\n($id1, $id2) = http_get('/body')\n\t=~ qr/^X-Request-Id: (.*?)\\x0d.*\\x0d\\x0a(.*)/ms;\n\nlike($id1, qr/^[a-z0-9]{32}$/, 'format id 1 - same');\nlike($id2, qr/^[a-z0-9]{32}$/, 'format id 2 - same');\n\nis($id1, $id2, 'equal id - same');\n\n# subrequest\n\n($id1, $id2) = http_get('/add.html')\n\t=~ qr/^X-Request-Id: (.*?)\\x0d.*\\x0d\\x0a(.*)/ms;\n\nlike($id1, qr/^[a-z0-9]{32}$/, 'format id 1 - sub');\nlike($id2, qr/^[a-z0-9]{32}$/, 'format id 2 - sub');\n\nis($id1, $id2, 'equal id - sub');\n\n# log\n\n($id1) = http_get('/log') =~ qr/^X-Request-Id: (.*)\\x0d/m;\n\n$t->stop();\n\n$id2 = $t->read_file('/id.log');\nchomp $id2;\n\nlike($id1, qr/^[a-z0-9]{32}$/, 'format id 1 - log');\nlike($id2, qr/^[a-z0-9]{32}$/, 'format id 2 - log');\n\nis($id1, $id2, 'equal id - log');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/rewrite.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for rewrite module.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http rewrite proxy/)->plan(23)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            rewrite ^ http://example.com/ redirect;\n        }\n\n        location /add {\n            rewrite ^ http://example.com/?c=d redirect;\n        }\n\n        location /no {\n            rewrite ^ http://example.com/?c=d? redirect;\n        }\n\n        location /return204 {\n            return 204;\n        }\n\n        location /return200 {\n            return 200;\n        }\n\n        location /return306 {\n            return 306;\n        }\n\n        location /return405 {\n            return 405;\n        }\n\n        location /error404return405 {\n            error_page 404 /return405;\n            return 404;\n        }\n\n        location /error405return204 {\n            error_page 405 /return204;\n            return 405;\n        }\n\n        location /error405return200 {\n            error_page 405 /return200;\n            return 405;\n        }\n\n        location /return200text {\n            return 200 \"text\";\n        }\n\n        location /return404text {\n            return 404 \"text\";\n        }\n\n        location /return302text {\n            return 302 \"text\";\n        }\n\n        location /error405return200text {\n            error_page 405 /return200text;\n            return 405;\n        }\n\n        location /error302return200text {\n            error_page 302 /return200text;\n            return 302 \"text\";\n        }\n\n        location /error405return302text {\n            error_page 405 /return302text;\n            return 405;\n        }\n\n        location /error405rewrite {\n            error_page 405 /;\n            return 405;\n        }\n\n        location /error405directory {\n            error_page 405 /directory;\n            return 405;\n        }\n\n        location /directory {\n        }\n\n        location /capture {\n            rewrite ^(.*) $1?c=d;\n            return 200 \"uri:$uri args:$args\";\n        }\n\n        location /capturedup {\n            rewrite ^(.*) $1?c=$1;\n            return 200 \"uri:$uri args:$args\";\n        }\n\n        location /break {\n            rewrite ^ /return200;\n            break;\n            proxy_pass http://127.0.0.1:8080/return204;\n        }\n    }\n}\n\nEOF\n\nmkdir($t->testdir() . '/directory');\n\n$t->run();\n\n###############################################################################\n\nlike(http_get('/'), qr!^Location: http://example.com/\\x0d?$!ms, 'simple');\nlike(http_get('/?a=b'), qr!^Location: http://example.com/\\?a=b\\x0d?$!ms,\n\t'simple with args');\nlike(http_get('/add'), qr!^Location: http://example.com/\\?c=d\\x0d?$!ms,\n\t'add args');\n\nlike(http_get('/add?a=b'), qr!^Location: http://example.com/\\?c=d&a=b\\x0d?$!ms,\n\t'add args with args');\n\nlike(http_get('/no?a=b'), qr!^Location: http://example.com/\\?c=d\\x0d?$!ms,\n\t'no args with args');\n\nlike(http_get('/return204'), qr!204 No Content!, 'return 204');\nlike(http_get('/return200'), qr!200 OK!, 'return 200');\nlike(http_get('/return306'), qr!HTTP/1.1 306 !, 'return 306');\nlike(http_get('/return405'), qr!HTTP/1.1 405.*body!ms, 'return 405');\n\n# this used to result in 404, but was changed in 1.15.4\n# to respond with 405 instead, much like a real error would do\n\nlike(http_get('/error404return405'), qr!HTTP/1.1 405!, 'error 404 return 405');\n\n# status code should be 405, and entity body is expected (vs. normal 204\n# replies which doesn't expect to have body); use HTTP/1.1 for test\n# to make problem clear\n\nmy $r = http(<<EOF);\nGET /error405return204 HTTP/1.1\nHost: localhost\nConnection: close\n\nEOF\n\nlike($r, qr/HTTP\\/1.1 405.*(Content-Length|\\x0d\\0a0\\x0d\\x0a)/ms,\n\t'error 405 return 204');\n\n# the same test, but with return 200.  this doesn't have special\n# handling and returns builtin error page body (the same problem as\n# in /error405return200text below)\n\nlike(http_get('/error405return200'), qr/HTTP\\/1.1 405(?!.*body)/ms,\n\t'error 405 return 200');\n\n# tests involving return with two arguments, as introduced in\n# 0.8.42\n\nlike(http_get('/return200text'), qr!text\\z!, 'return 200 text');\nlike(http_get('/return404text'), qr!text\\z!, 'return 404 text');\n\nlike(http_get('/error405return200text'), qr!HTTP/1.1 405.*text\\z!ms,\n\t'error 405 to return 200 text');\n\n# return 302 is somewhat special: it adds Location header instead of\n# body text.  additionally it doesn't sent reply directly (as it's done for\n# other returns since 0.8.42) but instead returns NGX_HTTP_* code\n\nlike(http_get('/return302text'), qr!HTTP/1.1 302.*Location: text!ms,\n\t'return 302 text');\n\nlike(http_get('/error302return200text'),\n\tqr!HTTP/1.1 302.*Location: text.*text\\z!ms,\n\t'error 302 return 200 text');\n\n# in contrast to other return's this shouldn't preserve original status code\n# from error, and the same applies to \"rewrite ... redirect\" as an error\n# handler; both should in line with e.g. directory redirect as well\n\nlike(http_get('/error405return302text'),\n\tqr!HTTP/1.1 302.*Location: text!ms,\n\t'error 405 return 302 text');\n\nlike(http_get('/error405rewrite'),\n\tqr!HTTP/1.1 302.*Location: http://example.com/!ms,\n\t'error 405 rewrite redirect');\n\nlike(http_get('/error405directory'),\n\tqr!HTTP/1.1 301.*Location: http://!ms,\n\t'error 405 directory redirect');\n\n# escaping of uri if there are args added in rewrite, and length\n# is actually calculated (ticket #162)\n\nlike(http_get('/capture/%25?a=b'),\n\tqr!^uri:/capture/% args:c=d&a=b$!ms,\n\t'escape with added args');\n\nlike(http_get('/capturedup/%25?a=b'),\n\tqr!^uri:/capturedup/% args:c=/capturedup/%25&a=b$!ms,\n\t'escape with added args');\n\n# break\n\nlike(http_get('/break'), qr/200/, 'valid_location reset');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/rewrite_if.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for rewrite \"if\" condition.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http rewrite/)->plan(33)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            if ($arg_c) {\n                return 204;\n            }\n        }\n\n        location /sp {\n            if ( $arg_c ) {\n                return 204;\n            }\n        }\n\n        location /eq {\n            if ($arg_c = 1) {\n                return 204;\n            }\n        }\n\n        location /not {\n            if ($arg_c != 2) {\n                return 204;\n            }\n        }\n\n        location /pos {\n            if ($arg_c ~ foo) {\n                return 204;\n            }\n        }\n\n        location /cpos {\n            if ($arg_c ~* foo) {\n                return 204;\n            }\n        }\n\n        location /neg {\n            if ($arg_c !~ foo) {\n                return 204;\n            }\n        }\n\n        location /cneg {\n            if ($arg_c !~* foo) {\n                return 204;\n            }\n        }\n\n        location /plain {\n            if (-f %%TESTDIR%%/$arg_c) {\n                return 204;\n            }\n        }\n\n        location /dir {\n            if (-d %%TESTDIR%%/$arg_c) {\n                return 204;\n            }\n        }\n\n        location /exist {\n            if (-e %%TESTDIR%%/$arg_c) {\n                return 204;\n            }\n        }\n\n        location /exec {\n            if (-x %%TESTDIR%%/$arg_c) {\n                return 204;\n            }\n        }\n\n        location /not_plain {\n            if (!-f %%TESTDIR%%/$arg_c) {\n                return 204;\n            }\n        }\n\n        location /not_dir {\n            if (!-d %%TESTDIR%%/$arg_c) {\n                return 204;\n            }\n        }\n\n        location /not_exist {\n            if (!-e %%TESTDIR%%/$arg_c) {\n                return 204;\n            }\n        }\n\n        location /not_exec {\n            if (!-x %%TESTDIR%%/$arg_c) {\n                return 204;\n            }\n        }\n    }\n}\n\nEOF\n\n$t->write_file('file', '');\nmkdir($t->testdir() . '/dir');\n\n$t->run();\n\n###############################################################################\n\nlike(http_get('/?c=1'), qr/ 204 /, 'var');\nunlike(http_get('/?c=0'), qr/ 204 /, 'false');\nlike(http_get('/sp?c=1'), qr/ 204 /, 'spaces');\n\nlike(http_get('/eq?c=1'), qr/ 204 /, 'equal');\nunlike(http_get('/eq?c=2'), qr/ 204 /, 'equal false');\nlike(http_get('/not?c=1'), qr/ 204 /, 'not equal');\nunlike(http_get('/not?c=2'), qr/ 204 /, 'not equal false');\n\nlike(http_get('/pos?c=food'), qr/ 204 /, 'match');\nlike(http_get('/cpos?c=FooD'), qr/ 204 /, 'match case');\nlike(http_get('/neg?c=FooD'), qr/ 204 /, 'match negative');\nlike(http_get('/cneg?c=bar'), qr/ 204 /, 'match negative case');\n\nunlike(http_get('/pos?c=FooD'), qr/ 204 /, 'mismatch');\nunlike(http_get('/cpos?c=bar'), qr/ 204 /, 'mismatch case');\nunlike(http_get('/neg?c=food'), qr/ 204 /, 'mismatch negative');\nunlike(http_get('/cneg?c=FooD'), qr/ 204 /, 'mismatch negative case');\n\nlike(http_get('/plain?c=file'), qr/ 204 /, 'plain file');\nunlike(http_get('/plain?c=dir'), qr/ 204 /, 'plain dir');\nunlike(http_get('/not_plain?c=file'), qr/ 204 /, 'not plain file');\nlike(http_get('/not_plain?c=dir'), qr/ 204 /, 'not plain dir');\n\nunlike(http_get('/dir/?c=file'), qr/ 204 /, 'directory file');\nlike(http_get('/dir?c=dir'), qr/ 204 /, 'directory dir');\nlike(http_get('/not_dir?c=file'), qr/ 204 /, 'not directory file');\nunlike(http_get('/not_dir?c=dir'), qr/ 204 /, 'not directory dir');\n\nlike(http_get('/exist?c=file'), qr/ 204 /, 'exist file');\nlike(http_get('/exist?c=dir'), qr/ 204 /, 'exist dir');\nunlike(http_get('/exist?c=nx'), qr/ 204 /, 'exist non-existent');\nunlike(http_get('/not_exist?c=file'), qr/ 204 /, 'not exist file');\nunlike(http_get('/not_exist?c=dir'), qr/ 204 /, 'not exist dir');\nlike(http_get('/not_exist?c=nx'), qr/ 204 /, 'not exist non-existent');\n\nSKIP: {\nskip 'no exec on win32', 4 if $^O eq 'MSWin32';\n\nunlike(http_get('/exec?c=file'), qr/ 204 /, 'executable file');\nlike(http_get('/exec?c=dir'), qr/ 204 /, 'executable dir');\nlike(http_get('/not_exec?c=file'), qr/ 204 /, 'not executable file');\nunlike(http_get('/not_exec?c=dir'), qr/ 204 /, 'not executable dir');\n\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/rewrite_set.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for rewrite set.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http rewrite ssi/)->plan(4);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        ssi on;\n\n        location /t1 {\n            set $http_foo \"set_foo\";\n            return 200 'X<!--#echo var=\"http_foo\" -->X';\n        }\n\n        location /t2 {\n            return 200 'X<!--#echo var=\"http_bar\" -->X';\n        }\n\n        location /t3 {\n            return 200 'X<!--#echo var=\"http_baz\" -->X';\n        }\n\n        location /t4 {\n            set $http_connection \"bar\";\n            return 200 \"X${http_connection}X\\n\";\n        }\n\n        # set in other context\n        location /other {\n            set $http_bar \"set_bar\";\n        }\n    }\n}\n\nEOF\n\n$t->run();\n\n###############################################################################\n\n# prefixed variables\n\nlike(http_get_extra('/t1.html', 'Foo: http_foo'), qr/Xset_fooX/,\n\t'set in this context');\nlike(http_get_extra('/t2.html', 'Bar: http_bar'), qr/Xhttp_barX/,\n\t'set in other context');\n\nlike(http_get_extra('/t3.html', 'Baz: http_baz'), qr/Xhttp_bazX/, 'not set');\n\nlike(http_get('/t4.html'), qr/XbarX/, 'set get in return');\n\n###############################################################################\n\nsub http_get_extra {\n\tmy ($uri, $extra) = @_;\n\treturn http(<<EOF);\nGET $uri HTTP/1.0\n$extra\n\nEOF\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/rewrite_unescape.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for escaping/unescaping in rewrite module.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http rewrite/)->plan(9)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location /t1 {\n            rewrite ^ $arg_r? redirect;\n        }\n\n        location /t2 {\n            rewrite ^ http://example.com$request_uri? redirect;\n        }\n\n        location /t3 {\n            rewrite ^ http://example.com$uri redirect;\n        }\n\n        location /t4 {\n            rewrite ^(.*) http://example.com$1 redirect;\n        }\n\n        location /t5 {\n            rewrite ^ http://example.com/blah%20%3Fblah redirect;\n        }\n\n        location /t6 {\n            rewrite ^ http://example.com/blah%20%2Fblah redirect;\n        }\n    }\n}\n\nEOF\n\nmkdir($t->testdir() . '/directory');\n\n$t->run();\n\n###############################################################################\n\n# Some rewrites and expected (?) behaviour\n#\n# /t1?r=http%3A%2F%2Fexample.com%2F%3Ffrom\n# rewrite ^ $arg_r? redirect;\n# expected: http://example.com/?from\n# got:      http://example.com/?from\n#\n# /t1?r=http%3A%2F%2Fexample.com%0D%0Asplit\n# rewrite ^ $arg_r? redirect;\n# expected: http://example.com%0D%0Asplit\n# got:      http://example.com%0D%0Asplit\n#\n# /t1?r=http%3A%2F%2Fexample.com%2F%3Ffrom%3Dblah\n# rewrite ^ $arg_r? redirect;\n# expected: http://example.com/?from=blah\n# got:      http://example.com/?from%3Dblah\n#\n# /blah%3Fblah\n# rewrite ^ http://example.com$request_uri? redirect;\n# expected: http://example.com/blah%3Fblah\n# got:      http://example.com/blah?blah\n#\n# /blah%3Fblah\n# rewrite ^ http://example.com$uri redirect;\n# expected: http://example.com/blah%3Fblah\n# got:      http://example.com/blah?blah\n#\n# /blah%3Fblah\n# rewrite ^(.*) http://example.com$1 redirect;\n# expected: http://example.com/blah%3Fblah\n# got:      http://example.com/blah?blah\n#\n# /\n# rewrite ^ http://example.com/blah%3Fblah redirect;\n# expected: http://example.com/blah%3Fblah\n# got:      http://example.com/blah?blah\n#\n\nlocation('/t1?r=http%3A%2F%2Fexample.com%2F%3Ffrom',\n\t'http://example.com/?from', 'escaped argument');\n\nlocation('/t1?r=http%3A%2F%2Fexample.com%0D%0Asplit',\n\t'http://example.com%0D%0Asplit', 'escaped argument header splitting');\n\nTODO: {\nlocal $TODO = 'not yet';\n\n# Fixing this cases will require major changes to the whole approach and\n# likely to break some currently working cases.  On the other hand, current\n# behaviour is far from acceptable.  Should be carefully thought.\n\nlocation('/t1?r=http%3A%2F%2Fexample.com%2F%3Ffrom%3Dblah',\n\t'http://example.com/?from=blah', 'escaped argument with complex query');\n\nlocation('/t2/blah%20%3Fblah',\n\t'http://example.com/t2/blah%20%3Fblah', 'escaped $request_uri');\n\nlocation('/t3/blah%20%3Fblah',\n\t'http://example.com/t3/blah%20%3Fblah', 'escaped $uri');\n\nlocation('/t4/blah%20%3Fblah',\n\t'http://example.com/t4/blah%20%3Fblah', 'escaped $1');\n\nlocation('/t5',\n\t'http://example.com/blah%20%3Fblah', 'escaped static');\n\nlocation('/t5?arg=blah',\n\t'http://example.com/blah%20%3Fblah?arg=blah',\n\t'escaped static with argument');\n\nlocation('/t6',\n\t'http://example.com/blah%20%2Fblah', 'escaped static slash');\n\n}\n\n###############################################################################\n\nsub location {\n\tmy ($url, $value, $name) = @_;\n\tmy $data = http_get($url);\n\tif ($data !~ qr!^Location: (.*?)\\x0d?$!ms) {\n\t\tfail($name);\n\t\treturn;\n\t}\n\tmy $location = $1;\n\tis($location, $value, $name);\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/scgi.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Test for scgi backend.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\neval { require SCGI; };\nplan(skip_all => 'SCGI not installed') if $@;\n\nmy $t = Test::Nginx->new()->has(qw/http scgi/)->plan(10)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    upstream u {\n        server 127.0.0.1:8081;\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            scgi_pass 127.0.0.1:8081;\n            scgi_param SCGI 1;\n            scgi_param REQUEST_URI $request_uri;\n            scgi_param HTTP_X_BLAH \"blah\";\n        }\n\n        location /var {\n            scgi_pass $arg_b;\n            scgi_param SCGI 1;\n            scgi_param REQUEST_URI $request_uri;\n        }\n\n    }\n}\n\nEOF\n\n$t->run_daemon(\\&scgi_daemon);\n$t->run()->waitforsocket('127.0.0.1:' . port(8081));\n\n###############################################################################\n\nlike(http_get('/'), qr/SEE-THIS/, 'scgi request');\nlike(http_get('/redir'), qr/ 302 /, 'scgi redirect');\nlike(http_get('/'), qr/^3$/m, 'scgi third request');\n\nunlike(http_head('/'), qr/SEE-THIS/, 'no data in HEAD');\n\nlike(http_get_headers('/headers'), qr/SEE-THIS/,\n\t'scgi request with many ignored headers');\n\nlike(http_get('/var?b=127.0.0.1:' . port(8081)), qr/SEE-THIS/,\n\t'scgi with variables');\nlike(http_get('/var?b=u'), qr/SEE-THIS/, 'scgi with variables to upstream');\n\nTODO: {\nlocal $TODO = 'not yet' unless $t->has_version('1.23.0');\n\nmy $r = http(<<EOF);\nGET / HTTP/1.0\nHost: localhost\nX-Forwarded-For: foo\nX-Forwarded-For: bar\nX-Forwarded-For: bazz\nCookie: foo\nCookie: bar\nCookie: bazz\nFoo: foo\nFoo: bar\nFoo: bazz\n\nEOF\n\nlike($r, qr/X-Forwarded-For: foo, bar, bazz/,\n\t'scgi with multiple X-Forwarded-For headers');\nlike($r, qr/X-Cookie: foo; bar; bazz/,\n\t'scgi with multiple Cookie headers');\nlike($r, qr/X-Foo: foo, bar, bazz/,\n\t'scgi with multiple unknown headers');\n\n}\n\n###############################################################################\n\nsub http_get_headers {\n\tmy ($url, %extra) = @_;\n\treturn http(<<EOF, %extra);\nGET $url HTTP/1.0\nHost: localhost\nX-Blah: ignored header\nX-Blah: ignored header\nX-Blah: ignored header\nX-Blah: ignored header\nX-Blah: ignored header\nX-Blah: ignored header\nX-Blah: ignored header\nX-Blah: ignored header\nX-Blah: ignored header\nX-Blah: ignored header\nX-Blah: ignored header\nX-Blah: ignored header\nX-Blah: ignored header\nX-Blah: ignored header\nX-Blah: ignored header\nX-Blah: ignored header\nX-Blah: ignored header\nX-Blah: ignored header\nX-Blah: ignored header\n\nEOF\n}\n\n###############################################################################\n\nsub scgi_daemon {\n\tmy $server = IO::Socket::INET->new(\n\t\tProto => 'tcp',\n\t\tLocalHost => '127.0.0.1:' . port(8081),\n\t\tListen => 5,\n\t\tReuse => 1\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\tmy $scgi = SCGI->new($server, blocking => 1);\n\tmy $count = 0;\n\n\twhile (my $request = $scgi->accept()) {\n\t\teval { $request->read_env(); };\n\t\tnext if $@;\n\n\t\t$count++;\n\n\t\tmy $xfwd = $request->env->{HTTP_X_FORWARDED_FOR} || '';\n\t\tmy $cookie = $request->env->{HTTP_COOKIE} || '';\n\t\tmy $foo = $request->env->{HTTP_FOO} || '';\n\n\t\t$request->connection()->print(<<EOF);\nLocation: http://localhost/redirect\nContent-Type: text/html\nX-Forwarded-For: $xfwd\nX-Cookie: $cookie\nX-Foo: $foo\n\nSEE-THIS\n$count\nEOF\n\t}\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/scgi_body.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Test for scgi backend with chunked request body.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\neval { require SCGI; };\nplan(skip_all => 'SCGI not installed') if $@;\n\nmy $t = Test::Nginx->new()->has(qw/http scgi/)->plan(5)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            scgi_pass 127.0.0.1:8081;\n            scgi_param SCGI 1;\n            scgi_param REQUEST_URI $request_uri;\n        }\n    }\n}\n\nEOF\n\n$t->run_daemon(\\&scgi_daemon);\n$t->run()->waitforsocket('127.0.0.1:' . port(8081));\n\n###############################################################################\n\n\nlike(http_get('/'), qr/X-Body: /, 'scgi no body');\n\nlike(http_get_length('/', ''), qr/X-Body: /, 'scgi empty body');\nlike(http_get_length('/', 'foobar'), qr/X-Body: foobar/, 'scgi body');\n\nlike(http(<<EOF), qr/X-Body: foobar/, 'scgi chunked');\nGET / HTTP/1.1\nHost: localhost\nConnection: close\nTransfer-Encoding: chunked\n\n6\nfoobar\n0\n\nEOF\n\nlike(http(<<EOF), qr/X-Body: /, 'scgi empty chunked');\nGET / HTTP/1.1\nHost: localhost\nConnection: close\nTransfer-Encoding: chunked\n\n0\n\nEOF\n\n###############################################################################\n\nsub http_get_length {\n\tmy ($url, $body) = @_;\n\tmy $length = length $body;\n\treturn http(<<EOF);\nGET $url HTTP/1.1\nHost: localhost\nConnection: close\nContent-Length: $length\n\n$body\nEOF\n}\n\n###############################################################################\n\nsub scgi_daemon {\n\tmy $server = IO::Socket::INET->new(\n\t\tProto => 'tcp',\n\t\tLocalHost => '127.0.0.1:' . port(8081),\n\t\tListen => 5,\n\t\tReuse => 1\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\tmy $scgi = SCGI->new($server, blocking => 1);\n\tmy $body;\n\n\twhile (my $request = $scgi->accept()) {\n\t\teval { $request->read_env(); };\n\t\tnext if $@;\n\n\t\tread($request->connection, $body,\n\t\t\t$request->env->{CONTENT_LENGTH});\n\n\t\t$request->connection()->print(<<EOF);\nLocation: http://localhost/redirect\nContent-Type: text/html\nX-Body: $body\n\nSEE-THIS\nEOF\n\t}\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/scgi_cache.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for scgi_cache.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\nuse Socket qw/ CRLF /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\neval { require SCGI; };\nplan(skip_all => 'SCGI not installed') if $@;\n\nmy $t = Test::Nginx->new()->has(qw/http scgi cache/)->plan(10)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    scgi_cache_path  %%TESTDIR%%/cache  keys_zone=one:1m;\n    scgi_cache_key   $request_uri;\n\n    add_header       X-Cache-Status  $upstream_cache_status;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            scgi_pass 127.0.0.1:8081;\n            scgi_param SCGI 1;\n            scgi_param REQUEST_URI $uri;\n            scgi_cache one;\n        }\n    }\n}\n\nEOF\n\n$t->run_daemon(\\&scgi_daemon);\n$t->run()->waitforsocket('127.0.0.1:' . port(8081));\n\n###############################################################################\n\nlike(http_get('/len'), qr/MISS/, 'length');\nlike(http_get('/len'), qr/HIT/, 'length cached');\n\nlike(http_get('/nolen'), qr/MISS/, 'no length');\nlike(http_get('/nolen'), qr/HIT/, 'no length cached');\n\nlike(http_get('/len/empty'), qr/MISS/, 'empty length');\nlike(http_get('/len/empty'), qr/HIT/, 'empty length cached');\n\nlike(http_get('/nolen/empty'), qr/MISS/, 'empty no length');\nlike(http_get('/nolen/empty'), qr/HIT/, 'empty no length cached');\n\nlike(http_get('/unfinished'), qr/MISS/, 'unfinished');\nlike(http_get('/unfinished'), qr/MISS/, 'unfinished not cached');\n\n###############################################################################\n\nsub scgi_daemon {\n\tmy $server = IO::Socket::INET->new(\n\t\tProto => 'tcp',\n\t\tLocalHost => '127.0.0.1:' . port(8081),\n\t\tListen => 5,\n\t\tReuse => 1\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\tmy $scgi = SCGI->new($server, blocking => 1);\n\tmy %count;\n\n\twhile (my $request = $scgi->accept()) {\n\t\teval { $request->read_env(); };\n\t\tnext if $@;\n\n\t\tmy $uri = $request->env->{REQUEST_URI} || '';\n\t\tmy $c = $request->connection();\n\n\t\t$count{$uri} ||= 0;\n\t\t$count{$uri}++;\n\n\t\tif ($uri eq '/len') {\n\t\t\t$c->print(\n\t\t\t\t\"Content-Length: 9\" . CRLF .\n\t\t\t\t\"Content-Type: text/html\" . CRLF .\n\t\t\t\t\"Cache-Control: max-age=300\" . CRLF . CRLF .\n\t\t\t\t\"test body\"\n\t\t\t);\n\n\t\t} elsif ($uri eq '/nolen') {\n\t\t\t$c->print(\n\t\t\t\t\"Content-Type: text/html\" . CRLF .\n\t\t\t\t\"Cache-Control: max-age=300\" . CRLF . CRLF .\n\t\t\t\t\"test body\"\n\t\t\t);\n\n\t\t} elsif ($uri eq '/len/empty') {\n\t\t\t$c->print(\n\t\t\t\t\"Content-Length: 0\" . CRLF .\n\t\t\t\t\"Content-Type: text/html\" . CRLF .\n\t\t\t\t\"Cache-Control: max-age=300\" . CRLF . CRLF\n\t\t\t);\n\n\t\t} elsif ($uri eq '/nolen/empty') {\n\t\t\t$c->print(\n\t\t\t\t\"Content-Type: text/html\" . CRLF .\n\t\t\t\t\"Cache-Control: max-age=300\" . CRLF . CRLF\n\t\t\t);\n\n\t\t} elsif ($uri eq '/unfinished') {\n\t\t\t$c->print(\n\t\t\t\t\"Content-Length: 10\" . CRLF .\n\t\t\t\t\"Content-Type: text/html\" . CRLF .\n\t\t\t\t\"Cache-Control: max-age=300\" . CRLF . CRLF\n\t\t\t);\n\t\t}\n\t}\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/scgi_extra_data.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n# (C) Nginx, Inc.\n\n# Test for scgi backend with extra data.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\neval { require SCGI; };\nplan(skip_all => 'SCGI not installed') if $@;\n\nmy $t = Test::Nginx->new()\n\t->has(qw/http scgi cache rewrite addition/)->plan(22)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    scgi_param SCGI 1;\n    scgi_param REQUEST_URI $request_uri;\n    scgi_param REQUEST_METHOD $request_method;\n\n    scgi_cache_path cache keys_zone=one:1m;\n    scgi_cache_key $request_uri;\n    scgi_cache_valid any 1m;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            scgi_pass 127.0.0.1:8081;\n            add_after_body /after;\n        }\n\n        location /unbuf/ {\n            scgi_pass 127.0.0.1:8081;\n            scgi_buffering off;\n            add_after_body /after;\n        }\n\n        location /head/ {\n            scgi_pass 127.0.0.1:8081;\n            scgi_cache one;\n            add_after_body /after;\n        }\n\n        location /after {\n            return 200 \":after\\n\";\n        }\n    }\n}\n\nEOF\n\n$t->run_daemon(\\&scgi_daemon);\n$t->run()->waitforsocket('127.0.0.1:' . port(8081));\n\n###############################################################################\n\nlike(http_get('/'), qr/SEE-THIS(?!-BUT-NOT-THIS)/, 'response with extra data');\nlike(http_get('/short'), qr/SEE-THIS(?!.*:after)/s, 'too short response');\nlike(http_get('/empty'), qr/200 OK(?!.*:after)/s, 'empty too short response');\n\nlike(http_head('/'), qr/200 OK(?!.*SEE-THIS)/s, 'no data in HEAD');\nlike(http_head('/short'), qr/200 OK(?!.*SEE-THIS)/s, 'too short to HEAD');\nlike(http_head('/empty'), qr/200 OK/, 'empty response to HEAD');\n\n# unbuffered responses\n\nlike(http_get('/unbuf/'), qr/SEE-THIS(?!-BUT-NOT-THIS)/,\n\t'unbuffered with extra data');\nlike(http_get('/unbuf/short'), qr/SEE-THIS(?!.*:after)/s,\n\t'unbuffered too short response');\nlike(http_get('/unbuf/empty'), qr/200 OK(?!.*:after)/s,\n\t'unbuffered empty too short response');\n\nlike(http_head('/unbuf/'), qr/200 OK(?!.*SEE-THIS)/s,\n\t'unbuffered no data in HEAD');\nlike(http_head('/unbuf/short'), qr/200 OK(?!.*SEE-THIS)/s,\n\t'unbuffered too short response to HEAD');\nlike(http_head('/unbuf/empty'), qr/200 OK/,\n\t'unbuffered empty response to HEAD');\n\n# caching of responsses to HEAD requests\n\nlike(http_head('/head/empty'), qr/200 OK(?!.*SEE-THIS)/s, 'head no body');\nlike(http_head('/head/matching'), qr/200 OK(?!.*SEE-THIS)/s, 'head matching');\nlike(http_head('/head/extra'), qr/200 OK(?!.*SEE-THIS)/s, 'head extra');\nlike(http_head('/head/short'), qr/200 OK(?!.*SEE-THIS)/s, 'head too short');\n\nlike(http_get('/head/empty'), qr/SEE-THIS/, 'head no body cached');\nlike(http_get('/head/matching'), qr/SEE-THIS/, 'head matching cached');\nlike(http_get('/head/extra'), qr/SEE-THIS(?!-BUT-NOT-THIS)/s,\n\t'head extra cached');\nlike(http_get('/head/short'), qr/SEE-THIS(?!.*:after)/s,\n\t'head too short cached');\n\n# \"zero size buf\" alerts (ticket #2117)\n\nlike(http_get('/zero'), qr/200 OK(?!.*NOT-THIS)/s, 'zero size');\nlike(http_get('/unbuf/zero'), qr/200 OK(?!.*NOT-THIS)/s,\n\t'unbuffered zero size');\n\n###############################################################################\n\nsub scgi_daemon {\n\tmy $server = IO::Socket::INET->new(\n\t\tProto => 'tcp',\n\t\tLocalHost => '127.0.0.1:' . port(8081),\n\t\tListen => 5,\n\t\tReuse => 1\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\tmy $scgi = SCGI->new($server, blocking => 1);\n\tmy ($c, $uri, $head);\n\n\twhile (my $request = $scgi->accept()) {\n\t\teval { $request->read_env(); };\n\t\tnext if $@;\n\t\t\n\t\t$uri = $request->env->{REQUEST_URI};\n\t\t$uri =~ s!^/unbuf!!;\n\n\t\t$head = $request->env->{REQUEST_METHOD} eq 'HEAD';\n\n\t\t$c = $request->connection();\n\n\t\tif ($uri eq '/') {\n\t\t\t$c->print(\"Content-Type: text/html\\n\");\n\t\t\t$c->print(\"Content-Length: 8\\n\\n\");\n\t\t\t$c->print(\"SEE-THIS-BUT-NOT-THIS\\n\");\n\n\t\t} elsif ($uri eq '/zero') {\n\t\t\t$c->print(\"Content-Type: text/html\\n\");\n\t\t\t$c->print(\"Content-Length: 0\\n\\n\");\n\t\t\t$c->print(\"NOT-THIS\\n\");\n\n\t\t} elsif ($uri eq '/short') {\n\t\t\t$c->print(\"Content-Type: text/html\\n\");\n\t\t\t$c->print(\"Content-Length: 100\\n\\n\");\n\t\t\t$c->print(\"SEE-THIS-TOO-SHORT-RESPONSE\\n\");\n\n\t\t} elsif ($uri eq '/empty') {\n\t\t\t$c->print(\"Content-Type: text/html\\n\");\n\t\t\t$c->print(\"Content-Length: 100\\n\\n\");\n\n\t\t} elsif ($uri eq '/head/empty') {\n\t\t\t$c->print(\"Content-Type: text/html\\n\");\n\t\t\t$c->print(\"Content-Length: 8\\n\\n\");\n\t\t\t$c->print(\"SEE-THIS\") unless $head;\n\n\t\t} elsif ($uri eq '/head/matching') {\n\t\t\t$c->print(\"Content-Type: text/html\\n\");\n\t\t\t$c->print(\"Content-Length: 8\\n\\n\");\n\t\t\t$c->print(\"SEE-THIS\");\n\n\t\t} elsif ($uri eq '/head/extra') {\n\t\t\t$c->print(\"Content-Type: text/html\\n\");\n\t\t\t$c->print(\"Content-Length: 8\\n\\n\");\n\t\t\t$c->print(\"SEE-THIS-BUT-NOT-THIS\\n\");\n\n\t\t} elsif ($uri eq '/head/short') {\n\t\t\t$c->print(\"Content-Type: text/html\\n\");\n\t\t\t$c->print(\"Content-Length: 100\\n\\n\");\n\t\t\t$c->print(\"SEE-THIS\\n\");\n\t\t}\n\t}\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/scgi_gzip.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Test for scgi backend and gzip.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx qw/ :DEFAULT :gzip /;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\neval { require SCGI; };\nplan(skip_all => 'SCGI not installed') if $@;\n\nmy $t = Test::Nginx->new()->has(qw/http scgi gzip/)->plan(1)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            gzip on;\n            scgi_pass 127.0.0.1:8081;\n            scgi_param SCGI 1;\n            scgi_param REQUEST_URI $request_uri;\n            scgi_param HTTP_X_BLAH \"blah\";\n        }\n    }\n}\n\nEOF\n\n$t->run_daemon(\\&scgi_daemon);\n$t->run()->waitforsocket('127.0.0.1:' . port(8081));\n\n###############################################################################\n\nlike(http_gzip_request('/'), qr/Content-Encoding: gzip/, 'scgi request');\n\n###############################################################################\n\nsub scgi_daemon {\n\tmy $server = IO::Socket::INET->new(\n\t\tProto => 'tcp',\n\t\tLocalHost => '127.0.0.1:' . port(8081),\n\t\tListen => 5,\n\t\tReuse => 1\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\tmy $scgi = SCGI->new($server, blocking => 1);\n\n\twhile (my $request = $scgi->accept()) {\n\t\teval { $request->read_env(); };\n\t\tnext if $@;\n\n\t\t$request->connection()->print(<<EOF);\nContent-Type: text/html\n\nSEE-THIS-1234567890-1234567890\nEOF\n\t}\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/scgi_merge_params.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for scgi_param inheritance.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\neval { require SCGI; };\nplan(skip_all => 'SCGI not installed') if $@;\n\nmy $t = Test::Nginx->new()->has(qw/http scgi cache/)->plan(9)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    scgi_cache_path  %%TESTDIR%%/cache  levels=1:2\n                     keys_zone=NAME:1m;\n\n    scgi_cache_key   stub;\n\n    scgi_param SCGI 1;\n    scgi_param HTTP_X_BLAH \"blah\";\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        scgi_cache  NAME;\n\n        location / {\n            scgi_pass    127.0.0.1:8081;\n        }\n\n        location /no/ {\n            scgi_pass    127.0.0.1:8081;\n            scgi_cache   off;\n        }\n\n        location /custom/ {\n            scgi_pass    127.0.0.1:8081;\n            scgi_param   SCGI 1;\n            scgi_param   HTTP_X_BLAH  \"custom\";\n        }\n    }\n}\n\nEOF\n\n$t->run_daemon(\\&scgi_daemon);\n$t->run()->waitforsocket('127.0.0.1:' . port(8081));\n\n###############################################################################\n\nlike(http_get_ims('/'), qr/ims=;/,\n\t'if-modified-since cleared with cache');\nlike(http_get_ims('/'), qr/iums=;/,\n\t'if-unmodified-since cleared with cache');\nlike(http_get_ims('/'), qr/blah=blah;/,\n\t'custom params with cache');\n\nlike(http_get_ims('/no/'), qr/ims=blah;/,\n\t'if-modified-since preserved without cache');\nlike(http_get_ims('/no/'), qr/iums=blah;/,\n\t'if-unmodified-since preserved without cache');\nlike(http_get_ims('/'), qr/blah=blah;/,\n\t'custom params without cache');\n\nlike(http_get_ims('/custom/'), qr/ims=;/,\n\t'if-modified-since cleared with cache custom');\nlike(http_get_ims('/custom/'), qr/iums=;/,\n\t'if-unmodified-since cleared with cache custom');\nlike(http_get_ims('/custom/'), qr/blah=custom;/,\n\t'custom params with cache custom');\n\n###############################################################################\n\nsub http_get_ims {\n\tmy ($url) = @_;\n\treturn http(<<EOF);\nGET $url HTTP/1.0\nHost: localhost\nConnection: close\nIf-Modified-Since: blah\nIf-Unmodified-Since: blah\n\nEOF\n}\n\n###############################################################################\n\nsub scgi_daemon {\n\tmy $server = IO::Socket::INET->new(\n\t\tProto => 'tcp',\n\t\tLocalHost => '127.0.0.1:' . port(8081),\n\t\tListen => 5,\n\t\tReuse => 1\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\tmy $scgi = SCGI->new($server, blocking => 1);\n\n\twhile (my $request = $scgi->accept()) {\n\t\teval { $request->read_env(); };\n\t\tnext if $@;\n\n\t\tmy $ims = $request->env->{HTTP_IF_MODIFIED_SINCE} || '';\n\t\tmy $iums = $request->env->{HTTP_IF_UNMODIFIED_SINCE} || '';\n\t\tmy $blah = $request->env->{HTTP_X_BLAH} || '';\n\n\t\t$request->connection()->print(<<EOF);\nLocation: http://localhost/redirect\nContent-Type: text/html\n\nims=$ims;iums=$iums;blah=$blah;\nEOF\n\t}\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/secure_link.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for nginx secure_link module.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse Digest::MD5 qw/ md5 md5_hex /;\nuse MIME::Base64 qw/ encode_base64 /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http secure_link rewrite/)->plan(19);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            # new style\n            # /test.html?hash=BASE64URL\n\n            secure_link      $arg_hash;\n            secure_link_md5  secret$uri;\n\n            # invalid hash\n            if ($secure_link = \"\") {\n                return 403;\n            }\n\n            # expired\n            if ($secure_link = \"0\") {\n                return 403;\n            }\n\n            # $secure_link = \"1\"\n        }\n\n        location = /expires.html {\n            # new style with expires\n            # /test.html?hash=BASE64URL&expires=12345678\n\n            add_header X-Expires $secure_link_expires;\n\n            secure_link      $arg_hash,$arg_expires;\n            secure_link_md5  secret$uri$arg_expires;\n\n            # invalid hash\n            if ($secure_link = \"\") {\n                return 403;\n            }\n\n            # expired\n            if ($secure_link = \"0\") {\n                return 403;\n            }\n\n            # $secure_link = \"1\"\n        }\n\n        location /p/ {\n            # old style\n            # /p/d8e8fca2dc0f896fd7cb4cb0031ba249/test.html\n\n            secure_link_secret secret;\n\n            if ($secure_link = \"\") {\n                return 403;\n            }\n\n            rewrite ^ /$secure_link break;\n        }\n\n        location /inheritance/ {\n            secure_link_secret secret;\n\n            location = /inheritance/test {\n                secure_link      Xr4ilOzQ4PCOq3aQ0qbuaQ==;\n                secure_link_md5  secret;\n\n                if ($secure_link = \"1\") {\n                    rewrite ^ /test.html break;\n                }\n\n                return 403;\n            }\n        }\n\n        location /stub {\n            return 200 x$secure_link${secure_link_expires}x;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('test.html', 'PASSED');\n$t->write_file('expires.html', 'PASSED');\n$t->run();\n\n###############################################################################\n\n# new style\n\nlike(http_get('/test.html?hash=q-5vpkjBkRXXtkUMXiJVHA=='),\n\tqr/PASSED/, 'request md5');\nlike(http_get('/test.html?hash=q-5vpkjBkRXXtkUMXiJVHA'),\n\tqr/PASSED/, 'request md5 no padding');\nlike(http_get('/test.html?hash=q-5vpkjBkRXXtkUMXiJVHAQQ'),\n\tqr/^HTTP.*403/, 'request md5 too long');\nlike(http_get('/test.html?hash=q-5vpkjBkRXXtkUMXiJVHA-TOOLONG'),\n\tqr/^HTTP.*403/, 'request md5 too long encoding');\nlike(http_get('/test.html?hash=BADHASHLENGTH'),\n\tqr/^HTTP.*403/, 'request md5 decode error');\nlike(http_get('/test.html?hash=q-5vpkjBkRXXtkUMXiJVHX=='),\n\tqr/^HTTP.*403/, 'request md5 mismatch');\nlike(http_get('/test.html'), qr/^HTTP.*403/, 'request no hash');\n\n# new style with expires\n\nmy ($expires, $hash);\n\n$expires = time() + 86400;\n$hash = encode_base64url(md5(\"secret/expires.html$expires\"));\nlike(http_get('/expires.html?hash=' . $hash . '&expires=' . $expires),\n\tqr/PASSED/, 'request md5 not expired');\nlike(http_get('/expires.html?hash=' . $hash . '&expires=' . $expires),\n\tqr/X-Expires: $expires/, 'secure_link_expires variable');\n\n$expires = time() - 86400;\n$hash = encode_base64url(md5(\"secret/expires.html$expires\"));\nlike(http_get('/expires.html?hash=' . $hash . '&expires=' . $expires),\n\tqr/^HTTP.*403/, 'request md5 expired');\n\n$expires = 0;\n$hash = encode_base64url(md5(\"secret/expires.html$expires\"));\nlike(http_get('/expires.html?hash=' . $hash . '&expires=' . $expires),\n\tqr/^HTTP.*403/, 'request md5 invalid expiration');\n\n# old style\n\nlike(http_get('/p/' . md5_hex('test.html' . 'secret') . '/test.html'),\n\tqr/PASSED/, 'request old style');\nlike(http_get('/p/' . md5_hex('fake') . '/test.html'), qr/^HTTP.*403/,\n\t'request old style fake hash');\nlike(http_get('/p/' . 'foo' . '/test.html'), qr/^HTTP.*403/,\n\t'request old style short hash');\nlike(http_get('/p/' . 'x' x 32 . '/test.html'), qr/^HTTP.*403/,\n\t'request old style corrupt hash');\nlike(http_get('/p%2f'), qr/^HTTP.*403/, 'request old style bad uri');\nlike(http_get('/p/test.html'), qr/^HTTP.*403/, 'request old style no hash');\nlike(http_get('/inheritance/test'), qr/PASSED/, 'inheritance');\n\nlike(http_get('/stub'), qr/xx/, 'secure_link not found');\n\n###############################################################################\n\nsub encode_base64url {\n\tmy $e = encode_base64(shift, \"\");\n\t$e =~ s/=+\\z//;\n\t$e =~ tr[+/][-_];\n\treturn $e;\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/server_tokens.t",
    "content": "#!/usr/bin/perl\n\n# (C) Andrey Zelenkov\n# (C) Nginx, Inc.\n\n# Tests for server_tokens directive.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http rewrite/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location /200 {\n            return 200;\n        }\n\n        location /404 {\n            return 404;\n        }\n\n        location /off {\n            server_tokens off;\n\n            location /off/200 {\n                return 200;\n            }\n\n            location /off/404 {\n                return 404;\n            }\n        }\n\n        location /on {\n            server_tokens on;\n\n            location /on/200 {\n                return 200;\n            }\n\n            location /on/404 {\n                return 404;\n            }\n        }\n\n        location /b {\n            server_tokens build;\n\n            location /b/200 {\n                return 200;\n            }\n\n            location /b/404 {\n                return 404;\n            }\n        }\n    }\n}\n\nEOF\n\n$t->run()->plan(12);\n\n###############################################################################\n\nmy $re = qr/nginx\\/\\d+\\.\\d+\\.\\d+/;\n\nlike(http_get_server('/200'), $re, 'tokens default 200');\nlike(http_get_server('/404'), $re, 'tokens default 404');\nlike(http_body('/404'), $re, 'tokens default 404 body');\n\nis(http_get_server('/off/200'), 'nginx', 'tokens off 200');\nis(http_get_server('/off/404'), 'nginx', 'tokens off 404');\nlike(http_body('/off/404'), qr/nginx(?!\\/)/, 'tokens off 404 body');\n\nlike(http_get_server('/on/200'), $re, 'tokens on 200');\nlike(http_get_server('/on/404'), $re, 'tokens on 404');\nlike(http_body('/on/404'), $re, 'tokens on 404 body');\n\n$re = qr/$re \\(.*\\)/ if $t->has_module('--build=');\n\nlike(http_get_server('/b/200'), $re, 'tokens build 200');\nlike(http_get_server('/b/404'), $re, 'tokens build 404');\nlike(http_body('/b/404'), $re, 'tokens build 404 body');\n\n###############################################################################\n\nsub http_body {\n\tmy ($uri) = shift;\n\treturn http_get($uri) =~ /.*?\\x0d\\x0a?\\x0d\\x0a?(.*)/ms && $1;\n}\n\nsub http_get_server {\n\tmy ($url) = @_;\n\treturn http_get($url) =~ /^Server:\\s(.+?)\\x0d?$/mi && $1 || undef;\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/slice.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for slice filter.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy cache fastcgi slice rewrite/)\n\t->plan(79);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    proxy_cache_path   %%TESTDIR%%/cache  keys_zone=NAME:1m;\n    proxy_cache_path   %%TESTDIR%%/cach3  keys_zone=NAME3:1m;\n    proxy_cache_key    $uri$is_args$args$slice_range;\n\n    fastcgi_cache_path   %%TESTDIR%%/cache2  keys_zone=NAME2:1m;\n    fastcgi_cache_key    $uri$is_args$args$slice_range;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / { }\n\n        location /proxy/ {\n            slice 2;\n\n            proxy_pass    http://127.0.0.1:8081/;\n\n            proxy_set_header   Range  $slice_range;\n        }\n\n        location /cache/ {\n            slice 2;\n\n            proxy_pass    http://127.0.0.1:8081/;\n\n            proxy_cache   NAME;\n\n            proxy_set_header   Range  $slice_range;\n\n            proxy_cache_valid   200 206  1h;\n\n            add_header X-Cache-Status $upstream_cache_status;\n        }\n\n        location /fastcgi {\n            slice 2;\n\n            fastcgi_pass    127.0.0.1:8082;\n\n            fastcgi_cache   NAME2;\n\n            fastcgi_param   Range $slice_range;\n\n            fastcgi_cache_valid   200 206  1h;\n\n            fastcgi_force_ranges  on;\n\n            add_header X-Cache-Status $upstream_cache_status;\n        }\n\n        location /cache-redirect {\n            error_page 404 = @fallback;\n        }\n\n        location @fallback {\n            slice 2;\n\n            proxy_pass    http://127.0.0.1:8081/t$is_args$args;\n\n            proxy_cache   NAME3;\n\n            proxy_set_header   Range  $slice_range;\n\n            proxy_cache_valid   200 206  1h;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8081;\n        server_name  localhost;\n\n        add_header Accept-Ranges bytes;\n\n        location / {\n            if ($http_range = \"\") {\n                set $limit_rate 100;\n\t    }\n        }\n    }\n}\n\nEOF\n\n$t->write_file('t', '0123456789abcdef');\n$t->run();\n\n###############################################################################\n\nmy $r;\n\nlike(http_get('/cache/nx'), qr/ 404 /, 'not found');\nlike(http_get('/cache/t'), qr/ 200 .*0123456789abcdef$/ms, 'no range');\n\n$r = get('/proxy/t', 'Range: bytes=3-4');\nlike($r, qr/ 206 /, 'proxy - 206 partial reply');\nlike($r, qr/^34$/m, 'proxy - correct content');\nunlike($r, qr/Accept-Ranges/, 'proxy - no original accept-ranges');\n\n$r = get('/cache/t?single', \"Range: bytes=0-0\");\nlike($r, qr/ 206 /, 'single - 206 partial reply');\nlike($r, qr/^0$/m, 'single - correct content');\nlike($r, qr/Status: MISS/m, 'single - cache status');\n\n$r = get('/cache/t?single', \"Range: bytes=0-0\");\nlike($r, qr/ 206 /, 'single cached - 206 partial reply');\nlike($r, qr/^0$/m, 'single cached - correct content');\nlike($r, qr/Status: HIT/m, 'single cached - cache status');\n\n$r = get('/cache/t?single', \"Range: bytes=1-1\");\nlike($r, qr/ 206 /, 'single next - 206 partial reply');\nlike($r, qr/^1$/m, 'single next - correct content');\nlike($r, qr/Status: HIT/m, 'single next - cache status');\n\n$r = get('/cache/t?single', \"Range: bytes=2-2\");\nlike($r, qr/ 206 /, 'slice next - 206 partial reply');\nlike($r, qr/^2$/m, 'slice next - correct content');\nlike($r, qr/Status: MISS/m, 'slice next - cache status');\n\n$r = get('/cache/t?single', \"Range: bytes=2-2\");\nlike($r, qr/ 206 /, 'slice next cached - 206 partial reply');\nlike($r, qr/^2$/m, 'slice next cached - correct content');\nlike($r, qr/Status: HIT/m, 'slice next cached - cache status');\n\n$r = get('/cache/t?median', \"Range: bytes=2-2\");\nlike($r, qr/ 206 /, 'slice median - 206 partial reply');\nlike($r, qr/^2$/m, 'slice median - correct content');\nlike($r, qr/Status: MISS/m, 'slice median - cache status');\n\n$r = get('/cache/t?median', \"Range: bytes=0-0\");\nlike($r, qr/ 206 /, 'before median - 206 partial reply');\nlike($r, qr/^0$/m, 'before median - correct content');\nlike($r, qr/Status: MISS/m, 'before median - cache status');\n\n# range span to multiple slices\n\n$r = get('/cache/t?range', \"Range: bytes=1-2\");\nlike($r, qr/ 206 /, 'slice range - 206 partial reply');\nlike($r, qr/^12$/m, 'slice range - correct content');\nlike($r, qr/Status: MISS/m, 'slice range - cache status');\n\n$r = get('/cache/t?range', \"Range: bytes=0-0\");\nlike($r, qr/ 206 /, 'slice 1st - 206 partial reply');\nlike($r, qr/^0$/m, 'slice 1st - correct content');\nlike($r, qr/Status: HIT/m, 'slice 1st - cache status');\n\n$r = get('/cache/t?range', \"Range: bytes=2-2\");\nlike($r, qr/ 206 /, 'slice 2nd - 206 partial reply');\nlike($r, qr/^2$/m, 'slice 2nd - correct content');\nlike($r, qr/Status: HIT/m, 'slice 2nd - cache status');\n\n$r = get('/cache/t?mrange', \"Range: bytes=3-4\");\nlike($r, qr/ 206 /, 'range median - 206 partial reply');\nlike($r, qr/^34$/m, 'range median - correct content');\nlike($r, qr/Status: MISS/m, 'range median - cache status');\n\n$r = get('/cache/t?mrange', \"Range: bytes=2-3\");\nlike($r, qr/ 206 /, 'range prev - 206 partial reply');\nlike($r, qr/^23$/m, 'range prev - correct content');\nlike($r, qr/Status: HIT/m, 'range prev - cache status');\n\n$r = get('/cache/t?mrange', \"Range: bytes=4-5\");\nlike($r, qr/ 206 /, 'range next - 206 partial reply');\nlike($r, qr/^45$/m, 'range next - correct content');\nlike($r, qr/Status: HIT/m, 'range next - cache status');\n\n$r = get('/cache/t?first', \"Range: bytes=2-\");\nlike($r, qr/ 206 /, 'first bytes - 206 partial reply');\nlike($r, qr/^23456789abcdef$/m, 'first bytes - correct content');\nlike($r, qr/Status: MISS/m, 'first bytes - cache status');\n\n$r = get('/cache/t?first', \"Range: bytes=4-\");\nlike($r, qr/ 206 /, 'first bytes cached - 206 partial reply');\nlike($r, qr/^456789abcdef$/m, 'first bytes cached - correct content');\nlike($r, qr/Status: HIT/m, 'first bytes cached - cache status');\n\n# multiple ranges\n# we want 206, but 200 is also fine\n\n$r = get('/cache/t?many', \"Range: bytes=3-3,4-4\");\nlike($r, qr/200 OK/, 'many - 206 partial reply');\nlike($r, qr/^0123456789abcdef$/m, 'many - correct content');\n\n$r = get('/cache/t?last', \"Range: bytes=-10\");\nlike($r, qr/206 /, 'last bytes - 206 partial reply');\nlike($r, qr/^6789abcdef$/m, 'last bytes - correct content');\n\n# respect not modified and range filters\n\nmy ($etag) = http_get('/t') =~ /ETag: (.*)/;\n\nlike(get('/cache/t?inm', \"If-None-Match: $etag\"), qr/ 304 /, 'inm');\nlike(get('/cache/t?inm', \"If-None-Match: bad\"), qr/ 200 /, 'inm bad');\n\nlike(get('/cache/t?im', \"If-Match: $etag\"), qr/ 200 /, 'im');\nlike(get('/cache/t?im', \"If-Match: bad\"), qr/ 412 /, 'im bad');\n\n$r = get('/cache/t?if', \"Range: bytes=3-4\\nIf-Range: $etag\");\nlike($r, qr/ 206 /, 'if-range - 206 partial reply');\nlike($r, qr/^34$/m, 'if-range - correct content');\n\n# respect Last-Modified from non-cacheable response with If-Range\n\nmy ($lm) = http_get('/t') =~ /Last-Modified: (.*)/;\n$r = get('/proxy/t', \"Range: bytes=3-4\\nIf-Range: $lm\");\nlike($r, qr/ 206 /, 'if-range last-modified proxy - 206 partial reply');\nlike($r, qr/^34$/m, 'if-range last-modified proxy - correct content');\n\n$r = get('/cache/t?ifb', \"Range: bytes=3-4\\nIf-Range: bad\");\nlike($r, qr/ 200 /, 'if-range bad - 200 ok');\nlike($r, qr/^0123456789abcdef$/m, 'if-range bad - correct content');\n\n# first slice isn't known\n\n$r = get('/cache/t?skip', \"Range: bytes=6-7\\nIf-Range: $etag\");\nlike($r, qr/ 206 /, 'if-range skip slice - 206 partial reply');\nlike($r, qr/^67$/m, 'if-range skip slice - correct content');\n\n$r = get('/cache/t?skip', \"Range: bytes=6-7\\nIf-Range: $etag\");\nlike($r, qr/ 206 /, 'if-range skip slice - cached - 206 partial reply');\nlike($r, qr/^67$/m, 'if-range skip slice - cached - correct content');\nlike($r, qr/HIT/, 'if-range skip bytes - cached - cache status');\n\n$r = get('/cache/t?skip', \"Range: bytes=2-3\");\nlike($r, qr/ 206 /, 'if-range skip slice - skipped - 206 partial reply');\nlike($r, qr/^23$/m, 'if-range skip slice - skipped - correct content');\nlike($r, qr/MISS/, 'if-range skip bytes - skipped - cache status');\n\nSKIP: {\n\teval { require FCGI; };\n\tskip 'FCGI not installed', 5 if $@;\n\tskip 'win32', 5 if $^O eq 'MSWin32';\n\n\t$t->run_daemon(\\&fastcgi_daemon);\n\t$t->waitforsocket('127.0.0.1:' . port(8082));\n\n\tlike(http_get('/fastcgi'), qr/200 OK.*MISS.*^012345678$/ms, 'fastcgi');\n\tlike(http_get('/fastcgi'), qr/200 OK.*HIT.*^012345678$/ms,\n\t\t'fastcgi cached');\n\n\tlike(get(\"/fastcgi?1\", \"Range: bytes=0-0\"), qr/ 206 .*MISS.*^0$/ms,\n\t\t'fastcgi slice');\n\tlike(get(\"/fastcgi?1\", \"Range: bytes=1-1\"), qr/ 206 .*HIT.*^1$/ms,\n\t\t'fastcgi slice cached');\n\tlike(get(\"/fastcgi?1\", \"Range: bytes=2-2\"), qr/ 206 .*MISS.*^2$/ms,\n\t\t'fastcgi slice next');\n}\n\n# slicing in named location\n\n$r = http_get('/cache-redirect');\n\nlike($r, qr/ 200 .*^0123456789abcdef$/ms, 'in named location');\nis(scalar @{[ glob $t->testdir() . '/cach3/*' ]}, 8,\n\t'in named location - cache entries');\n\n###############################################################################\n\nsub get {\n\tmy ($url, $extra) = @_;\n\treturn http(<<EOF);\nGET $url HTTP/1.1\nHost: localhost\nConnection: close\n$extra\n\nEOF\n}\n\n###############################################################################\n\nsub fastcgi_daemon {\n\tmy $socket = FCGI::OpenSocket('127.0.0.1:' . port(8082), 5);\n\tmy $request = FCGI::Request(\\*STDIN, \\*STDOUT, \\*STDERR, \\%ENV,\n\t\t$socket);\n\n\tmy $body = '012345678';\n\tmy $len = length($body);\n\n\twhile ($request->Accept() >= 0) {\n\t\tmy ($start, $stop) = $ENV{Range} =~ /bytes=(\\d+)-(\\d+)/;\n\t\tmy $body = substr($body, $start, ($stop - $start) + 1);\n\t\t$stop = $len - 1 if $stop > $len - 1;\n\n\t\tprint <<EOF;\nStatus: 206\nContent-Type: text/html\nContent-Range: bytes $start-$stop/$len\n\nEOF\n\n\t\tprint $body;\n\t}\n\n\tFCGI::CloseSocket($socket);\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/split_clients.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for split_client module.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http split_clients/)->plan(1);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    split_clients $connection $variant {\n        51.2%  \".one\";\n        10%    \".two\";\n        *      \".three\";\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / {\n            index index${variant}.html;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('index.one.html', 'first');\n$t->write_file('index.two.html', 'second');\n$t->write_file('index.three.html', 'third');\n\n$t->run();\n\n###############################################################################\n\n# NB: split_clients distribution is a subject to implementation details\n\nlike(many('/', 20), qr/first: 12, second: 2, third: 6/, 'split');\n\n###############################################################################\n\nsub many {\n\tmy ($uri, $count) = @_;\n\tmy %dist;\n\n\tfor (1 .. $count) {\n\t\tif (http_get($uri) =~ /(first|second|third)/) {\n\t\t\t$dist{$1} = 0 unless defined $dist{$1};\n\t\t\t$dist{$1}++;\n\t\t}\n\t}\n\n\treturn join ', ', map { $_ . \": \" . $dist{$_} } sort keys %dist;\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/ssi.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for nginx ssi module.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http ssi cache proxy rewrite/)\n\t->plan(30);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    proxy_cache_path       %%TESTDIR%%/cache levels=1:2\n                           keys_zone=NAME:1m;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        if ($args = \"found\") {\n            return 204;\n        }\n\n        location / {\n            ssi on;\n        }\n        location /proxy/ {\n            ssi on;\n            proxy_pass http://127.0.0.1:8080/local/;\n        }\n        location /cache/ {\n            proxy_pass http://127.0.0.1:8080/local/;\n            proxy_cache NAME;\n            proxy_cache_valid 200 1h;\n        }\n        location /local/ {\n            ssi off;\n            alias %%TESTDIR%%/;\n        }\n        location = /test-empty-postpone.html {\n            ssi on;\n            postpone_output 0;\n        }\n        location /var {\n            ssi on;\n            add_header X-Var x${date_gmt}x;\n        }\n        location /var_noformat {\n            ssi on;\n            add_header X-Var x${date_gmt}x;\n            return 200;\n        }\n        location /var_nossi {\n            add_header X-Var x${date_gmt}x;\n            return 200;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('test1.html', 'X<!--#echo var=\"arg_test\" -->X');\n$t->write_file('test2.html',\n\t'X<!--#include virtual=\"/test1.html?test=test\" -->X');\n$t->write_file('test3.html',\n\t'X<!--#set var=\"blah\" value=\"test\" --><!--#echo var=\"blah\" -->X');\n$t->write_file('test4-echo-none.html',\n\t'X<!--#set var=\"blah\" value=\"<test>\" -->'\n\t. '<!--#echo var=\"blah\" encoding=\"none\" -->X');\n$t->write_file('test5-echo-url.html',\n\t'X<!--#set var=\"blah\" value=\"<test>\" -->'\n\t. '<!--#echo var=\"blah\" encoding=\"url\" -->X');\n$t->write_file('test6-echo-entity.html',\n\t'X<!--#set var=\"blah\" value=\"<test>\" -->'\n\t. '<!--#echo var=\"blah\" encoding=\"entity\" -->X');\n$t->write_file('test-args-rewrite.html',\n\t'X<!--#include virtual=\"/check?found\" -->X');\n$t->write_file('test-empty1.html', 'X<!--#include virtual=\"/empty.html\" -->X');\n$t->write_file('test-empty2.html',\n\t'X<!--#include virtual=\"/local/empty.html\" -->X');\n$t->write_file('test-empty3.html',\n\t'X<!--#include virtual=\"/cache/empty.html\" -->X');\n$t->write_file('test-empty-postpone.html',\n\t'X<!--#include virtual=\"/proxy/empty.html\" -->X');\n$t->write_file('empty.html', '');\n\n$t->write_file('unescape.html?', 'SEE-THIS') unless $^O eq 'MSWin32';\n$t->write_file('unescape1.html',\n\t'X<!--#include virtual=\"/tes%741.html?test=test\" -->X');\n$t->write_file('unescape2.html',\n\t'X<!--#include virtual=\"/unescape.html%3f\" -->X');\n$t->write_file('unescape3.html',\n\t'X<!--#include virtual=\"/test1.html%3ftest=test\" -->X');\n\n$t->write_file('var_format.html',\n\t'x<!--#if expr=\"$arg_custom\" -->'\n\t\t. '<!--#config timefmt=\"%A, %H:%M:%S\" -->'\n\t\t. '<!--#set var=\"v\" value=\"$date_gmt\" -->'\n\t\t. '<!--#echo var=\"v\" -->'\n\t. '<!--#else -->'\n\t\t. '<!--#set var=\"v\" value=\"$date_gmt\" -->'\n\t\t. '<!--#echo var=\"v\" -->'\n\t. '<!--#endif -->x');\n\n$t->run();\n\n###############################################################################\n\nlike(http_get('/test1.html'), qr/^X\\(none\\)X$/m, 'echo no argument');\nlike(http_get('/test1.html?test='), qr/^XX$/m, 'empty argument');\nlike(http_get('/test1.html?test=test'), qr/^XtestX$/m, 'argument');\nlike(http_get('/test1.html?test=test&a=b'), qr/^XtestX$/m, 'argument 2');\nlike(http_get('/test1.html?a=b&test=test'), qr/^XtestX$/m, 'argument 3');\nlike(http_get('/test1.html?a=b&test=test&d=c'), qr/^XtestX$/m, 'argument 4');\nlike(http_get('/test1.html?atest=a&testb=b&ctestc=c&test=test'), qr/^XtestX$/m,\n\t'argument 5');\n\nlike(http_get('/test2.html'), qr/^XXtestXX$/m, 'argument via include');\n\nlike(http_get('/test3.html'), qr/^XtestX$/m, 'set');\n\nlike(http_get('/test4-echo-none.html'), qr/^X<test>X$/m,\n\t'echo encoding none');\n\nTODO: {\nlocal $TODO = 'no strict URI escaping yet' unless $t->has_version('1.21.1');\n\nlike(http_get('/test5-echo-url.html'), qr/^X%3Ctest%3EX$/m,\n\t'echo encoding url');\n\n}\n\nlike(http_get('/test6-echo-entity.html'), qr/^X&lt;test&gt;X$/m,\n\t'echo encoding entity');\n\n# args should be in subrequest even if original request has no args and that\n# was queried somehow (e.g. by server rewrites)\n\nlike(http_get('/test-args-rewrite.html'), qr/^XX$/m, 'args only subrequest');\n\nlike(http_get('/test-args-rewrite.html?wasargs'), qr/^XX$/m,\n\t'args was in main request');\n\n# Last-Modified and Accept-Ranges headers should be cleared\n\nunlike(http_get('/test1.html'), qr/Last-Modified|Accept-Ranges/im,\n\t'cleared headers');\nunlike(http_get('/proxy/test1.html'), qr/Last-Modified|Accept-Ranges/im,\n\t'cleared headers from proxy');\n\n# empty subrequests\n\nlike(http_get('/test-empty1.html'), qr/HTTP/, 'empty with ssi');\nlike(http_get('/test-empty2.html'), qr/HTTP/, 'empty without ssi');\nlike(http_get('/test-empty3.html'), qr/HTTP/, 'empty with proxy');\nlike(http_get('/test-empty3.html'), qr/HTTP/, 'empty with proxy cached');\n\nlike(http_get('/test-empty-postpone.html'), qr/HTTP.*XX/ms,\n\t'empty with postpone_output 0');\n\n# handling of escaped URIs\n\nlike(http_get('/unescape1.html'), qr/^XXtestXX$/m, 'escaped in path');\n\nSKIP: {\nskip 'incorrect filename on win32', 2 if $^O eq 'MSWin32';\n\nlike(http_get('/unescape2.html'), qr/^XSEE-THISX$/m,\n\t'escaped question in path');\nlike(http_get('/unescape3.html'), qr/404 Not Found/,\n\t'escaped query separator');\n\n}\n\n# handling of embedded date variables\n\nmy $re_date_gmt = qr/X-Var: x.+, \\d\\d-.+-\\d{4} \\d\\d:\\d\\d:\\d\\d .+x/;\n\nlike(http_get('/var_nossi.html'), $re_date_gmt, 'no ssi');\nlike(http_get('/var_noformat.html'), $re_date_gmt, 'no format');\n\nlike(http_get('/var_format.html?custom=1'), $re_date_gmt, 'custom header');\nlike(http_get('/var_format.html'), $re_date_gmt, 'default header');\n\nlike(http_get('/var_format.html?custom=1'),\n\tqr/x.+, \\d\\d:\\d\\d:\\d\\dx/, 'custom ssi');\nlike(http_get('/var_format.html'),\n\tqr/x.+, \\d\\d-.+-\\d{4} \\d\\d:\\d\\d:\\d\\d .+x/, 'default ssi');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/ssi_delayed.t",
    "content": "#!/usr/bin/perl\n\n# (C) Andrey Zelenkov\n# (C) Roman Arutyunyan\n# (C) Nginx, Inc.\n\n# Test for subrequest bug with delay (see 903fb1ddc07f for details).\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http proxy ssi/)->plan(1);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location / { }\n        location /delayed.html {\n            ssi on;\n            sendfile_max_chunk 100;\n            postpone_output 0;\n        }\n\n        location /1 {\n            proxy_buffers 3 256;\n            proxy_buffer_size 256;\n            proxy_max_temp_file_size 0;\n            proxy_pass http://127.0.0.1:8081;\n        }\n    }\n}\n\nEOF\n\n\n$t->write_file('delayed.html', ('x' x 100) . '<!--#include virtual=\"/1\"-->');\n\n$t->run_daemon(\\&http_daemon);\n$t->run()->waitforsocket('127.0.0.1:' . port(8081));\n\n###############################################################################\n\n# If a response sending is delayed by sendfile_max_chunk, and\n# then we've switched to a different subrequest, which is not yet\n# ready to handle corresponding write event, wev->delayed won't be\n# cleared.  This results in the subrequest response not being\n# sent to the client, and the whole request will hang if all proxy\n# buffers will be exhausted.  Fixed in 1.11.13 (903fb1ddc07f).\n\nlike(http_get('/delayed.html'), qr/x{100}y{1024}SEE-THIS/, 'delayed');\n\n###############################################################################\n\nsub http_daemon {\n\tmy ($t) = @_;\n\n\tmy $server = IO::Socket::INET->new(\n\t\tProto => 'tcp',\n\t\tLocalHost => '127.0.0.1',\n\t\tLocalPort => port(8081),\n\t\tListen => 5,\n\t\tReuse => 1\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\tlocal $SIG{PIPE} = 'IGNORE';\n\n\tmy $data = ('y' x 1024) . 'SEE-THIS';\n\n\twhile (my $client = $server->accept()) {\n\t\t$client->autoflush(1);\n\n\t\tmy $headers = '';\n\n\t\twhile (<$client>) {\n\t\t\t$headers .= $_;\n\t\t\tlast if (/^\\x0d?\\x0a?$/);\n\t\t}\n\n\t\tselect undef, undef, undef, 0.5;\n\n\t\tprint $client <<EOF;\nHTTP/1.1 200 OK\nConnection: close\n\n$data\nEOF\n\t}\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/ssi_if.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n# (C) Valentin Bartenev\n\n# Tests for nginx ssi module, \"if\" statement.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http ssi/)->plan(43);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n        location / {\n            ssi on;\n        }\n    }\n}\n\nEOF\n\n\nmy $if_elif_else =\n\t'<!--#if expr=\"$arg_if\" -->IF'\n\t. '<!--#elif expr=\"$arg_elif\" -->ELIF'\n\t. '<!--#else -->ELSE'\n\t. '<!--#endif -->';\n\nmy $zig = 'GOOD';\nmy $zag = 'GOOD';\n\nforeach my $i (reverse 1 .. 15) {\n\tif ($i % 2) {\n\t\t$zig =\n\t\t\"<!--#if expr='\\$arg_$i' -->$i<!--#else -->$zig<!--#endif -->\";\n\t\t$zag =\n\t\t\"<!--#if expr='\\$arg_$i' -->$zag<!--#else -->$i<!--#endif -->\";\n\t} else {\n\t\t$zig =\n\t\t\"<!--#if expr='\\$arg_$i' -->$zig<!--#else -->$i<!--#endif -->\";\n\t\t$zag =\n\t\t\"<!--#if expr='\\$arg_$i' -->$i<!--#else -->$zag<!--#endif -->\";\n\t}\n}\n\n$t->run();\n\n###############################################################################\n\n$t->write_file('if_var.html', 'x<!--#if expr=\"$arg_v\" -->OK<!--#endif -->x');\n\nlike(http_get('/if_var.html?v=1'), qr/^xOKx$/m, 'if variable exists');\nlike(http_get('/if_var.html'), qr/^xx$/m, 'if variable not exists');\n\n\n$t->write_file('if_eq.html',\n\t'x<!--#if expr=\"$arg_v = equal\" -->OK<!--#endif -->x');\n\nlike(http_get('/if_eq.html?v=equal'), qr/^xOKx$/m, 'if var = text');\nlike(http_get('/if_eq.html?v=notequal'), qr/^xx$/m, 'if var = text (false)');\n\n\n$t->write_file('if_neq.html',\n\t'x<!--#if expr=\"equal != $arg_v\" -->OK<!--#endif -->x');\n\nlike(http_get('/if_neq.html?v=notequal'), qr/^xOKx$/m, 'if text != var');\nlike(http_get('/if_neq.html?v=equal'), qr/^xx$/m, 'if text != var (false)');\n\n\nSKIP: {\n\t# PCRE may not be available unless we have rewrite module\n\n\tskip 'no PCRE', 4 unless $t->has_module('rewrite');\n\n\t$t->write_file('if_eq_re.html',\n\t\t'x<!--#if expr=\"$arg_v = /re+gexp?/\" -->OK<!--#endif -->x');\n\n\tlike(http_get('/if_eq_re.html?v=XreeeegexX'), qr/^xOKx$/m,\n\t\t'if var = /regex/');\n\tlike(http_get('/if_eq_re.html?v=XrgxX'), qr/^xx$/m,\n\t\t'if var = /regex/ (false)');\n\n\n\t$t->write_file('if_neq_re.html',\n\t\t'x<!--#if expr=\"$arg_v != /re+gexp?/\" -->OK<!--#endif -->x');\n\n\tlike(http_get('/if_neq_re.html?v=XrgxX'), qr/^xOKx$/m,\n\t\t'if var != /regex/');\n\tlike(http_get('/if_neq_re.html?v=XreeeegexX'), qr/^xx$/m,\n\t\t'if var != /regex/ (false)');\n}\n\n\n$t->write_file('if_varvar.html',\n\t'x<!--#if expr=\"$arg_v = var$arg_v2\" -->OK<!--#endif -->x');\n\nlike(http_get('/if_varvar.html?v=varHERE&v2=HERE'), qr/^xOKx$/m,\n\t'if var = complex');\n\n\nSKIP: {\n\t# PCRE may not be available unless we have rewrite module\n\n\tskip 'no PCRE', 2 unless $t->has_module('rewrite');\n\n\t$t->write_file('if_cap_re.html',\n\t\t'x<!--#if expr=\"$arg_v = /(CAP\\d).*(CAP\\d)/\" -->'\n\t\t\t. '<!--#echo var=\"1\" -->x<!--#echo var=\"2\" -->'\n\t\t. '<!--#endif -->x');\n\n\tlike(http_get('/if_cap_re.html?v=hereCAP1andCAP2'), qr/^xCAP1xCAP2x$/m,\n\t\t'if regex with captures');\n\n\n\t$t->write_file('if_ncap_re.html',\n\t\t'x<!--#if expr=\"$arg_v = /(?P<ncap>HERE)/\" -->'\n\t\t\t. '<!--#echo var=\"ncap\" -->'\n\t\t. '<!--#endif -->x');\n\n\tlike(http_get('/if_ncap_re.html?v=captureHEREeee'), qr/^xHEREx$/m,\n\t\t'if regex with named capture');\n}\n\n\n$t->write_file('if.html', 'x' . $if_elif_else . 'x');\n\nlike(http_get('/if.html?if=1'), qr/^xIFx$/m, 'if');\nlike(http_get('/if.html?if=1&elif=1'), qr/^xIFx$/m, 'if suppresses elif');\nlike(http_get('/if.html?elif=1'), qr/^xELIFx$/m, 'elif');\nlike(http_get('/if.html'), qr/^xELSEx$/m, 'else');\n\n\n$t->write_file('if_multi.html',\n\t'x<!--#if expr=\"$arg_1\" -->IF1<!--#else -->ELSE1<!--#endif -->'\n\t. 'x<!--#if expr=\"$arg_2\" -->IF2<!--#else -->ELSE2<!--#endif -->'\n\t. 'x<!--#if expr=\"$arg_3\" -->IF3<!--#else -->ELSE3<!--#endif -->'\n\t. 'x<!--#if expr=\"$arg_4\" -->IF4<!--#else -->ELSE4<!--#endif -->'\n\t. 'x<!--#if expr=\"$arg_5\" -->IF5<!--#else -->ELSE5<!--#endif -->x');\n\nlike(http_get('/if_multi.html?1=t&2=t&3=t&4=t&5=t'),\n\tqr/^xIF1xIF2xIF3xIF4xIF5x$/m, 'multiple if (sequentially)');\nlike(http_get('/if_multi.html?1=t&3=t&5=t'), qr/^xIF1xELSE2xIF3xELSE4xIF5x$/m,\n\t'multiple if (interlaced)');\nlike(http_get('/if_multi.html?2=t&4=t'), qr/^xELSE1xIF2xELSE3xIF4xELSE5x$/m,\n\t'multiple if (interlaced reversed)');\n\n\n$t->write_file('if_in_block.html',\n\t'<!--#block name=\"one\" -->' . $if_elif_else . '<!--#endblock -->'\n\t. 'x<!--#include virtual=\"/404?$args\" stub=\"one\" -->x');\n\nlike(http_get('/if_in_block.html?if=1'), qr/^xIFx$/m, 'if (in block)');\nlike(http_get('/if_in_block.html?if=1&elif=1'), qr/^xIFx$/m,\n\t'if suppresses elif (in block)');\nlike(http_get('/if_in_block.html?elif=1'), qr/^xELIFx$/m, 'elif (in block)');\nlike(http_get('/if_in_block.html'), qr/^xELSEx$/m, 'else (in block)');\n\n\n$t->write_file('if_config_set_echo.html',\n\t'x<!--#if expr=\"$arg_if\" -->'\n\t\t. '<!--#config timefmt=\"IF\" -->'\n\t\t. '<!--#set var=\"v\" value=\"$date_gmt\" -->'\n\t\t. '<!--#echo var=\"v\" -->'\n\t. '<!--#else -->'\n\t\t. '<!--#config timefmt=\"ELSE\" -->'\n\t\t. '<!--#set var=\"v\" value=\"$date_gmt\" -->'\n\t\t. '<!--#echo var=\"v\" -->'\n\t. '<!--#endif -->x');\n\nlike(http_get('/if_config_set_echo.html?if=1'), qr/^xIFx$/m,\n\t'if config-set-echo');\nlike(http_get('/if_config_set_echo.html'), qr/^xELSEx$/m,\n\t'else config-set-echo');\n\n\n$t->write_file('if_include.html',\n\t'x<!--#if expr=\"$arg_if\" -->'\n\t\t. '<!--#include virtual=\"/if.html?if=1\" -->'\n\t. '<!--#else -->'\n\t\t. '<!--#include virtual=\"/if.html\" -->'\n\t. '<!--#endif -->x');\n\nlike(http_get('/if_include.html?if=1'), qr/^xxIFxx$/m,\n\t'if include');\nlike(http_get('/if_include.html'), qr/^xxELSExx$/m,\n\t'else include');\n\n\n$t->write_file('if_block.html',\n\t'<!--#if expr=\"$arg_if\" -->'\n\t\t. '<!--#block name=\"one\" -->IF<!--#endblock -->'\n\t. '<!--#else -->'\n\t\t. '<!--#block name=\"one\" -->ELSE<!--#endblock -->'\n\t. '<!--#endif -->'\n\t. 'x<!--#include virtual=\"/404\" stub=\"one\" -->x');\n\nlike(http_get('/if_block.html?if=1'), qr/^xIFx$/m, 'if block');\nlike(http_get('/if_block.html'), qr/^xELSEx$/m, 'else block');\n\n\nTODO: {\nlocal $TODO = 'support for nested ifs';\n\n$t->write_file('ifif.html',\n\t'x<!--#if expr=\"$arg__if\" -->IFx' . $if_elif_else\n\t. '<!--#elif expr=\"$arg__elif\" -->ELIFx' . $if_elif_else\n\t. '<!--#else -->ELSEx' . $if_elif_else\n\t. '<!--#endif -->x');\n\nlike(http_get('/ifif.html?_if=1&if=1'), qr/^xIFxIFx$/m, 'if if');\nlike(http_get('/ifif.html?_if=1&elif=1'), qr/^xIFxELIFx$/m, 'if elif');\nlike(http_get('/ifif.html?_if=1'), qr/^xIFxELSEx$/m, 'if else');\n\nlike(http_get('/ifif.html?_elif=1&if=1'), qr/^xELIFxIFx$/m, 'elif if');\nlike(http_get('/ifif.html?_elif=1&elif=1'), qr/^xELIFxELIFx$/m, 'elif elif');\nlike(http_get('/ifif.html?_elif=1'), qr/^xELIFxELSEx$/m, 'elif else');\n\nlike(http_get('/ifif.html?if=1'), qr/^xELSExIFx$/m, 'else if');\nlike(http_get('/ifif.html?elif=1'), qr/^xELSExELIFx$/m, 'else elif');\nlike(http_get('/ifif.html'), qr/^xELSExELSEx$/m, 'else else');\n\n\n$t->write_file('zigzag.html',\n\t\"x<!--#if expr='\\$arg_0' -->$zig<!--#else -->$zag<!--#endif -->x\");\n\nlike(http_get('/zigzag.html?0=t&2=t&4=t&6=t&8=t&10=t&12=t&14=t'),\n\tqr/^xGOODx$/m, 'zigzag');\nlike(http_get('/zigzag.html?1=t&3=t&5=t&7=t&9=t&11=t&13=t&15=t'),\n\tqr/^xGOODx$/m, 'zagzig');\n\n\n$t->write_file('zigzag_block.html',\n\t'<!--#block name=\"one\" -->'\n\t. \"x<!--#if expr='\\$arg_0' -->$zig<!--#else -->$zag<!--#endif -->x\"\n\t. '<!--#endblock -->'\n\t. 'x<!--#include virtual=\"/404?$args\" stub=\"one\" -->x');\n\nlike(http_get('/zigzag_block.html?0=t&2=t&4=t&6=t&8=t&10=t&12=t&14=t'),\n\tqr/^xGOODx$/m, 'zigzag block');\nlike(http_get('/zigzag_block.html?1=t&3=t&5=t&7=t&9=t&11=t&13=t&15=t'),\n\tqr/^xGOODx$/m, 'zagzig block');\n\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/ssi_include_big.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for nginx ssi bug with big includes.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx qw/ :DEFAULT :gzip /;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http ssi rewrite gzip proxy/)->plan(8);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    output_buffers  2 512;\n    ssi on;\n    gzip on;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        location /proxy/ {\n            proxy_pass http://127.0.0.1:8080/local/;\n        }\n        location = /local/blah {\n            return 204;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('c1.html', 'X' x 1023);\n$t->write_file('c2.html', 'X' x 1024);\n$t->write_file('c3.html', 'X' x 1025);\n$t->write_file('test1.html', '<!--#include virtual=\"/proxy/blah\" -->'\n\t. '<!--#include virtual=\"/c1.html\" -->');\n$t->write_file('test2.html', '<!--#include virtual=\"/proxy/blah\" -->'\n\t. '<!--#include virtual=\"/c2.html\" -->');\n$t->write_file('test3.html', '<!--#include virtual=\"/proxy/blah\" -->'\n\t. '<!--#include virtual=\"/c3.html\" -->');\n$t->write_file('test4.html', '<!--#include virtual=\"/proxy/blah\" -->'\n\t. ('X' x 1025));\n\n$t->run();\n\n###############################################################################\n\nmy $t1 = http_gzip_request('/test1.html');\nok(defined $t1, 'small included file (less than output_buffers)');\nhttp_gzip_like($t1, qr/^X{1023}\\Z/, 'small included file content');\n\nmy $t2 = http_gzip_request('/test2.html');\nok(defined $t2, 'small included file (equal to output_buffers)');\nhttp_gzip_like($t2, qr/^X{1024}\\Z/, 'small included file content');\n\nmy $t3 = http_gzip_request('/test3.html');\nok(defined $t3, 'big included file (more than output_buffers)');\nhttp_gzip_like($t3, qr/^X{1025}\\Z/, 'big included file content');\n\nmy $t4 = http_gzip_request('/test4.html');\nok(defined $t4, 'big ssi main file');\nhttp_gzip_like($t4, qr/^X{1025}\\Z/, 'big ssi main file content');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/ssi_waited.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for nginx ssi module, waited subrequests.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http ssi/)->plan(1);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n        location / {\n            ssi on;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('index.html', 'x<!--#include virtual=\"/first.html\" -->' .\n\t'x<!--#include virtual=\"/second.html\" -->x');\n$t->write_file('first.html', 'FIRST');\n$t->write_file('second.html',\n\t'<!--#include virtual=\"/waited.html\" wait=\"yes\"-->xSECOND');\n$t->write_file('waited.html', 'WAITED');\n\n$t->run();\n\n###############################################################################\n\nlike(http_get('/'), qr/^xFIRSTxWAITEDxSECONDx$/m, 'waited non-active');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/ssl.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Andrey Zelenkov\n# (C) Nginx, Inc.\n\n# Tests for http ssl module.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse Socket qw/ CRLF /;\nuse IO::Select;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\n\nmy $t = Test::Nginx->new()->has(qw/http http_ssl rewrite proxy socket_ssl/)\n\t->has_daemon('openssl')->plan(21);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    ssl_certificate_key localhost.key;\n    ssl_certificate localhost.crt;\n\n    log_format ssl $ssl_protocol;\n\n    server {\n        listen       127.0.0.1:8085 ssl;\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        ssl_certificate_key inner.key;\n        ssl_certificate inner.crt;\n        ssl_session_cache shared:SSL:1m;\n        ssl_session_tickets on;\n        ssl_verify_client optional_no_ca;\n\n        keepalive_requests 1000;\n\n        location / {\n            return 200 \"body $ssl_session_reused\";\n        }\n        location /id {\n            return 200 \"body $ssl_session_id\";\n        }\n        location /cipher {\n            return 200 \"body $ssl_cipher\";\n        }\n        location /ciphers {\n            return 200 \"body $ssl_ciphers\";\n        }\n        location /client_verify {\n            return 200 \"body $ssl_client_verify\";\n        }\n        location /protocol {\n            return 200 \"body $ssl_protocol\";\n        }\n        location /issuer {\n            return 200 \"body $ssl_client_i_dn:$ssl_client_i_dn_legacy\";\n        }\n        location /subject {\n            return 200 \"body $ssl_client_s_dn:$ssl_client_s_dn_legacy\";\n        }\n        location /time {\n            return 200 \"body $ssl_client_v_start!$ssl_client_v_end!$ssl_client_v_remain\";\n        }\n\n        location /body {\n            add_header X-Body $request_body always;\n            proxy_pass http://127.0.0.1:8080/;\n\n            access_log %%TESTDIR%%/ssl.log ssl;\n        }\n    }\n\n    server {\n\n\n\n\n\n\n\n\n\n\n\n\n\n        listen       127.0.0.1:8086 ssl;\n        server_name  localhost;\n\n        ssl_session_cache shared:SSL:1m;\n        ssl_session_tickets on;\n        ssl_session_timeout 1;\n\n        location / {\n            return 200 \"body $ssl_session_reused\";\n        }\n    }\n}\n\nEOF\n\n$t->write_file('openssl.conf', <<EOF);\n[ req ]\ndefault_bits = 2048\nencrypt_key = no\ndistinguished_name = req_distinguished_name\n[ req_distinguished_name ]\nEOF\n\nmy $d = $t->testdir();\n\n$t->write_file('ca.conf', <<EOF);\n[ ca ]\ndefault_ca = myca\n\n[ myca ]\nnew_certs_dir = $d\ndatabase = $d/certindex\ndefault_md = sha256\npolicy = myca_policy\nserial = $d/certserial\ndefault_days = 3\n\n[ myca_policy ]\ncommonName = supplied\nEOF\n\n$t->write_file('certserial', '1000');\n$t->write_file('certindex', '');\n\nsystem('openssl req -x509 -new '\n\t. \"-config $d/openssl.conf -subj /CN=issuer/ \"\n\t. \"-out $d/issuer.crt -keyout $d/issuer.key \"\n\t. \">>$d/openssl.out 2>&1\") == 0\n\tor die \"Can't create certificate for issuer: $!\\n\";\n\nsystem(\"openssl req -new \"\n\t. \"-config $d/openssl.conf -subj /CN=subject/ \"\n\t. \"-out $d/subject.csr -keyout $d/subject.key \"\n\t. \">>$d/openssl.out 2>&1\") == 0\n\tor die \"Can't create certificate for subject: $!\\n\";\n\nsystem(\"openssl ca -batch -config $d/ca.conf \"\n\t. \"-keyfile $d/issuer.key -cert $d/issuer.crt \"\n\t. \"-subj /CN=subject/ -in $d/subject.csr -out $d/subject.crt \"\n\t. \">>$d/openssl.out 2>&1\") == 0\n\tor die \"Can't sign certificate for subject: $!\\n\";\n\nforeach my $name ('localhost', 'inner') {\n\tsystem('openssl req -x509 -new '\n\t\t. \"-config $d/openssl.conf -subj /CN=$name/ \"\n\t\t. \"-out $d/$name.crt -keyout $d/$name.key \"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create certificate for $name: $!\\n\";\n}\n\n\n$t->run();\n\n###############################################################################\n\n# ssl session reuse\n\n\nmy $ctx = get_ssl_context();\n\nlike(get('/', 8085, $ctx), qr/^body \\.$/m, 'session');\n\nTODO: {\nlocal $TODO = 'no TLSv1.3 sessions, old Net::SSLeay'\n\tif $Net::SSLeay::VERSION < 1.88 && test_tls13();\nlocal $TODO = 'no TLSv1.3 sessions, old IO::Socket::SSL'\n\tif $IO::Socket::SSL::VERSION < 2.061 && test_tls13();\nlocal $TODO = 'no TLSv1.3 sessions in LibreSSL'\n\tif $t->has_module('LibreSSL') && test_tls13();\n\nlike(get('/', 8085, $ctx), qr/^body r$/m, 'session reused');\n\n}\n\n\n\n\n\n# ssl certificate inheritance\n\nmy $s = get_ssl_socket(8086);\nlike($s->dump_peer_certificate(), qr/CN=localhost/, 'CN');\n\n\n$s = get_ssl_socket(8085);\nlike($s->dump_peer_certificate(), qr/CN=inner/, 'CN inner');\n\n\n# session timeout\n\n$ctx = get_ssl_context();\n\nget('/', 8086, $ctx);\nselect undef, undef, undef, 2.1;\n\nlike(get('/', 8086, $ctx), qr/^body \\.$/m, 'session timeout');\n\n# embedded variables\n\n$ctx = get_ssl_context();\nlike(get('/id', 8085, $ctx), qr/^body (\\w{64})?$/m, 'session id');\nTODO: {\nlocal $TODO = 'no TLSv1.3 sessions in LibreSSL'\n\tif $t->has_module('LibreSSL') && test_tls13();\nlocal $TODO = 'no TLSv1.3 sessions ids in BoringSSL'\n\tif $t->has_module('BoringSSL') && test_tls13();\nlike(get('/id', 8085, $ctx), qr/^body \\w{64}$/m, 'session id reused');\n}\nunlike(http_get('/id'), qr/body \\w/, 'session id no ssl');\nlike(get('/cipher', 8085), qr/^body [\\w-]+$/m, 'cipher');\n\nSKIP: {\nskip 'BoringSSL', 1 if $t->has_module('BoringSSL');\n\nlike(get('/ciphers', 8085), qr/^body [:\\w-]+$/m, 'ciphers');\n\n}\n\nlike(get('/client_verify', 8085), qr/^body NONE$/m, 'client verify');\nlike(get('/protocol', 8085), qr/^body (TLS|SSL)v(\\d|\\.)+$/m, 'protocol');\nlike(cert('/issuer', 8085), qr!^body CN=issuer:/CN=issuer$!m, 'issuer');\nlike(cert('/subject', 8085), qr!^body CN=subject:/CN=subject$!m, 'subject');\nlike(cert('/time', 8085), qr/^body [:\\s\\w]+![:\\s\\w]+![23]$/m, 'time');\n\n# c->read->ready handling bug in ngx_ssl_recv(), triggered with chunked body\n\nlike(get_body('/body', '0123456789', 20, 5), qr/X-Body: (0123456789){100}/,\n\t'request body chunked');\n\n# pipelined requests\n\n$s = get_ssl_socket(8085);\nmy $req = <<EOF;\nGET / HTTP/1.1\nHost: localhost\n\nEOF\n\n$req x= 1000;\n\nmy $r = http($req, socket => $s) || \"\";\n$s = undef;\nis(() = $r =~ /(200 OK)/g, 1000, 'pipelined requests');\n\n# OpenSSL 3.0 error \"unexpected eof while reading\" seen as a critical error\n\nok(get_ssl_socket(8085), 'ssl unexpected eof');\n\n# close_notify is sent before lingering close\n\nis(get_ssl_shutdown(8085), 1, 'ssl shutdown on lingering close');\n\n$t->stop();\n\nlike($t->read_file('ssl.log'), qr/^(TLS|SSL)v(\\d|\\.)+$/m,\n\t'log ssl variable on lingering close');\n\nlike(`grep -F '[crit]' ${\\($t->testdir())}/error.log`, qr/^$/s, 'no crit');\n\n###############################################################################\n\nsub test_tls13 {\n\treturn get('/protocol', 8085) =~ /TLSv1.3/;\n}\nsub get {\n\tmy ($uri, $port, $ctx, %extra) = @_;\n\tmy $s = get_ssl_socket($port, $ctx, %extra) or return;\n\treturn http_get($uri, socket => $s);\n}\n\nsub get_body {\n\tmy ($uri, $body, $len, $n) = @_;\n\tmy $s = get_ssl_socket(8085) or return;\n\thttp(\"GET /body HTTP/1.1\" . CRLF\n\t\t. \"Host: localhost\" . CRLF\n\t\t. \"Connection: close\" . CRLF\n\t\t. \"Transfer-Encoding: chunked\" . CRLF . CRLF,\n\t\tsocket => $s, start => 1);\n\tmy $chs = unpack(\"H*\", pack(\"C\", length($body) * $len));\n\thttp($chs . CRLF . $body x $len . CRLF, socket => $s, start => 1)\n\t\tfor 1 .. $n;\n\tmy $r = http(\"0\" . CRLF . CRLF, socket => $s);\n\treturn $r;\n}\n\nsub cert {\n\tmy ($uri, $port) = @_;\n\treturn get(\n\t\t$uri, $port, undef,\n\t\tSSL_cert_file => \"$d/subject.crt\",\n\t\tSSL_key_file => \"$d/subject.key\"\n\t);\n}\n\nsub get_ssl_context {\n\treturn IO::Socket::SSL::SSL_Context->new(\n\t\tSSL_verify_mode => IO::Socket::SSL::SSL_VERIFY_NONE(),\n\t\tSSL_session_cache_size => 100\n\t);\n}\n\nsub get_ssl_socket {\n\tmy ($port, $ctx, %extra) = @_;\n\treturn http(\n\n\t\t'', PeerAddr => '127.0.0.1:' . port($port), start => 1,\n\t\tSSL => 1,\n\t\t\tSSL_reuse_ctx => $ctx,\n\t\t\t%extra\n\t\t);\n\n\n}\n\nsub get_ssl_shutdown {\n\tmy ($port) = @_;\n\n\tmy $s = http(\n\t\t'GET /' . CRLF . 'extra',\n\t\tPeerAddr => '127.0.0.1:' . port($port), start => 1,\n\t\tSSL => 1\n\t);\n\t$s->blocking(0);\n\twhile (IO::Select->new($s)->can_read(8)) {\n                my $n = $s->sysread(my $buf, 16384);\n                next if !defined $n && $!{EWOULDBLOCK};\n                last;\n\t}\n\t$s->blocking(1);\n\treturn $s->stop_SSL();\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/ssl_certificate.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for http ssl module with dynamic certificates.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse Socket qw/ CRLF /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx qw/ :DEFAULT http_end /;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\n\nmy $t = Test::Nginx->new()\n\n\t->has(qw/http http_ssl geo openssl:1.0.2 socket_ssl_sni/)\n\t->has_daemon('openssl');\n\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    geo $one {\n        default one;\n    }\n\n    geo $two {\n        default two;\n    }\n\n    geo $pass {\n        default pass;\n    }\n\n    add_header X-SSL $ssl_server_name:$ssl_session_reused;\n    add_header X-SSL-Protocol $ssl_protocol;\n    ssl_session_cache shared:SSL:1m;\n    ssl_session_tickets on;\n\n    server {\n        listen       127.0.0.1:8080 ssl;\n        server_name  default;\n\n        ssl_certificate $one.crt;\n        ssl_certificate_key $one.key;\n    }\n\n    server {\n        listen       127.0.0.1:8080 ssl;\n        server_name  virtual;\n\n        # found in key\n        ssl_certificate $two.crt;\n        ssl_certificate_key $two.key;\n    }\n\n    server {\n        listen       127.0.0.1:8080 ssl;\n        server_name  no_ctx;\n    }\n\n    server {\n        listen       127.0.0.1:8083 ssl;\n        server_name  password;\n\n        # found in key\n        ssl_certificate pass.crt;\n        ssl_certificate_key $pass.key;\n        ssl_password_file password_file;\n    }\n\n    server {\n        listen       127.0.0.1:8081 ssl;\n        server_name  default;\n\n        ssl_certificate $one.crt;\n        ssl_certificate_key $one.key;\n    }\n\n    server {\n        listen       127.0.0.1:8082 ssl;\n        server_name  default;\n\n        ssl_certificate $two.crt;\n        ssl_certificate_key $two.key;\n    }\n\n    server {\n        listen       127.0.0.1:8084 ssl;\n        server_name  localhost;\n\n        ssl_certificate $ssl_server_name.crt;\n        ssl_certificate_key $ssl_server_name.key;\n    }\n}\n\nEOF\n\n$t->write_file('openssl.conf', <<EOF);\n[ req ]\ndefault_bits = 2048\nencrypt_key = no\ndistinguished_name = req_distinguished_name\n[ req_distinguished_name ]\nEOF\n\nmy $d = $t->testdir();\n\nforeach my $name ('one', 'two') {\n\tsystem('openssl req -x509 -new '\n\t\t. \"-config $d/openssl.conf -subj /CN=$name/ \"\n\t\t. \"-out $d/$name.crt -keyout $d/$name.key \"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create certificate for $name: $!\\n\";\n}\n\nforeach my $name ('pass') {\n\tsystem(\"openssl genrsa -out $d/$name.key -passout pass:pass \"\n\t\t. \"-aes128 2048 >>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create $name key: $!\\n\";\n\tsystem(\"openssl req -x509 -new -config $d/openssl.conf \"\n\t\t. \"-subj /CN=$name/ -out $d/$name.crt -key $d/$name.key \"\n\t\t. \"-passin pass:pass >>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create $name certificate: $!\\n\";\n}\n\n$t->write_file('password_file', 'pass');\n$t->write_file('index.html', '');\n\n$t->run()->plan(11);\n\n###############################################################################\n\nlike(cert('default', 8080), qr/CN=one/, 'default certificate');\nlike(get('default', 8080), qr/default/, 'default context');\n\nlike(cert('virtual', 8080), qr/CN=two/, 'virtual server certificate');\nlike(get('virtual', 8080), qr/virtual/, 'virtual server context');\n\nlike(cert('no_ctx', 8080), qr/CN=one/, 'certificate - no context');\nlike(get('no_ctx', 8080), qr/no_ctx/, 'virtual server - no context');\n\nlike(get('password', 8083), qr/password/, 'ssl_password_file');\n\n# session reuse\n\nmy $s = session('default', 8080);\nTODO: {\nlocal $TODO = 'no TLSv1.3 sessions, old Net::SSLeay'\n\tif $Net::SSLeay::VERSION < 1.88 && test_tls13();\nlocal $TODO = 'no TLSv1.3 sessions, old IO::Socket::SSL'\n\tif $IO::Socket::SSL::VERSION < 2.061 && test_tls13();\n\nlike(get('default', 8080, $s), qr/default:r/, 'session reused');\nTODO: {\n# ticket key name mismatch prevents session resumption\nlocal $TODO = 'not yet' unless $t->has_version('1.23.2');\nlocal $TODO = 'no SSL_session_key, old IO::Socket::SSL'\n\tif $IO::Socket::SSL::VERSION < 1.965;\nlike(get('default', 8081, $s), qr/default:r/, 'session id context match');\n}\n}\nlike(get('default', 8082, $s), qr/default:\\./, 'session id context distinct');\n\n# errors\n\nok(!get('nx', 8084), 'no certificate');\n\n###############################################################################\n\nsub get {\n\tmy $s = get_socket(@_) || return;\n\n\n\treturn http_end($s);\n}\n\nsub cert {\n\tmy $s = get_socket(@_) || return;\n\treturn $s->dump_peer_certificate();\n}\nsub session {\n\tmy $s = get_socket(@_) || return;\n\thttp_end($s);\n\treturn $s;\n}\nsub get_socket {\n\tmy ($host, $port, $ctx) = @_;\n\treturn http_get(\n\t\t'/', start => 1, PeerAddr => '127.0.0.1:' . port($port),\n\t\tSSL => 1,\n\t\tSSL_hostname => $host,\n\t\tSSL_session_cache_size => 100,\n\t\tSSL_session_key => 1,\n\t\tSSL_reuse_ctx => $ctx\n\t);\n}\n\nsub test_tls13 {\n\treturn get('default', 8080) =~ /TLSv1.3/;\n\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/ssl_certificate_chain.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for http ssl module with certificate chain.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\n\nmy $t = Test::Nginx->new()->has(qw/http http_ssl socket_ssl/)\n\t->has_daemon('openssl')->plan(3);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8080 ssl;\n        server_name  localhost;\n\n        ssl_certificate_key end.key;\n        ssl_certificate end.crt;\n    }\n\n    server {\n        listen       127.0.0.1:8081 ssl;\n        server_name  localhost;\n\n        ssl_certificate_key int.key;\n        ssl_certificate int.crt;\n    }\n\n    server {\n        listen       127.0.0.1:8082 ssl;\n        server_name  localhost;\n\n        ssl_certificate_key end.key;\n        ssl_certificate end-int.crt;\n    }\n}\n\nEOF\n\nmy $d = $t->testdir();\n\n$t->write_file('openssl.conf', <<EOF);\n[ req ]\ndefault_bits = 2048\nencrypt_key = no\ndistinguished_name = req_distinguished_name\n[ req_distinguished_name ]\nEOF\n\n$t->write_file('ca.conf', <<EOF);\n[ ca ]\ndefault_ca = myca\n\n[ myca ]\nnew_certs_dir = $d\ndatabase = $d/certindex\ndefault_md = sha256\npolicy = myca_policy\nserial = $d/certserial\ndefault_days = 1\nx509_extensions = myca_extensions\n\n[ myca_policy ]\ncommonName = supplied\n\n[ myca_extensions ]\nbasicConstraints = critical,CA:TRUE\nsubjectAltName = IP:127.0.0.1\nEOF\n\nforeach my $name ('root') {\n\tsystem('openssl req -x509 -new '\n\t\t. \"-config $d/openssl.conf -subj /CN=$name/ \"\n\t\t. \"-out $d/$name.crt -keyout $d/$name.key \"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create certificate for $name: $!\\n\";\n}\n\nforeach my $name ('int', 'end') {\n\tsystem(\"openssl req -new \"\n\t\t. \"-config $d/openssl.conf -subj /CN=$name/ \"\n\t\t. \"-out $d/$name.csr -keyout $d/$name.key \"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create certificate for $name: $!\\n\";\n}\n\n$t->write_file('certserial', '1000');\n$t->write_file('certindex', '');\n\nsystem(\"openssl ca -batch -config $d/ca.conf \"\n\t. \"-keyfile $d/root.key -cert $d/root.crt \"\n\t. \"-subj /CN=int/ -in $d/int.csr -out $d/int.crt \"\n\t. \">>$d/openssl.out 2>&1\") == 0\n\tor die \"Can't sign certificate for int: $!\\n\";\n\nsystem(\"openssl ca -batch -config $d/ca.conf \"\n\t. \"-keyfile $d/int.key -cert $d/int.crt \"\n\t. \"-subj /CN=end/ -in $d/end.csr -out $d/end.crt \"\n\t. \">>$d/openssl.out 2>&1\") == 0\n\tor die \"Can't sign certificate for end: $!\\n\";\n\n$t->write_file('end-int.crt',\n\t$t->read_file('end.crt') . $t->read_file('int.crt'));\n\n$t->run();\n\n###############################################################################\n\nok(!get_ssl_socket(8080), 'incomplete chain');\nok(get_ssl_socket(8081), 'intermediate');\nok(get_ssl_socket(8082), 'intermediate server');\n\n###############################################################################\n\nsub get_ssl_socket {\n\tmy ($port) = @_;\n\tmy ($verify);\n\n\thttp(\n\t\t'', PeerAddr => '127.0.0.1:' . port($port), start => 1,\n\t\tSSL => 1,\n\t\t\tSSL_verify_mode => IO::Socket::SSL::SSL_VERIFY_PEER(),\n\t\t\tSSL_ca_file => \"$d/root.crt\",\n\t\t\tSSL_verify_callback => sub {\n\t\t\t\tmy ($ok) = @_;\n\t\t\t\t$verify = $ok;\n\t\t\t\treturn $ok;\n\t\t}\n\t\t);\n\n\n\treturn $verify;\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/ssl_certificate_perl.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for http ssl module, loading certificates from memory with perl module.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\n\nmy $t = Test::Nginx->new()\n\n\t->has(qw/http http_ssl perl openssl:1.0.2 socket_ssl_sni/)\n\t->has_daemon('openssl');\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    perl_set $pem '\n        sub {\n            my $r = shift;\n            local $/;\n            my $sni = $r->variable(\"ssl_server_name\");\n            open my $fh, \"<\", \"%%TESTDIR%%/$sni.crt\";\n            my $content = <$fh>;\n            close $fh;\n            return $content;\n        }\n    ';\n\n    server {\n        listen       127.0.0.1:8443 ssl;\n        server_name  localhost;\n\n        ssl_certificate data:$pem;\n        ssl_certificate_key data:$pem;\n    }\n}\n\nEOF\n\n$t->write_file('openssl.conf', <<EOF);\n[ req ]\ndefault_bits = 2048\nencrypt_key = no\ndistinguished_name = req_distinguished_name\n[ req_distinguished_name ]\nEOF\n\nmy $d = $t->testdir();\n\nforeach my $name ('one', 'two') {\n\tsystem('openssl req -x509 -new '\n\t\t. \"-config $d/openssl.conf -subj /CN=$name/ \"\n\t\t. \"-out $d/$name.crt -keyout $d/$name.crt \"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create certificate for $name: $!\\n\";\n}\n\n$t->run()->plan(2);\n\n###############################################################################\n\nlike(cert('one'), qr/CN=one/, 'certificate');\nlike(cert('two'), qr/CN=two/, 'certificate 2');\n\n###############################################################################\n\nsub cert {\n\tmy $s = get_socket(@_) || return;\n\treturn $s->dump_peer_certificate();\n}\n\nsub get_socket {\n\tmy $host = shift;\n\treturn http_get('/', start => 1, SSL => 1, SSL_hostname => $host);\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/ssl_certificates.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for http ssl module with multiple certificates.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\n\nmy $t = Test::Nginx->new()->has(qw/http http_ssl socket_ssl/)\n\t->has_daemon('openssl');\n\nplan(skip_all => 'no multiple certificates') if $t->has_module('BoringSSL');\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    ssl_certificate_key rsa.key;\n    ssl_certificate rsa.crt;\n    ssl_ciphers DEFAULT:ECCdraft;\n\n    add_header X-SSL-Protocol $ssl_protocol;\n    server {\n        listen       127.0.0.1:8443 ssl;\n        server_name  localhost;\n\n        ssl_certificate_key ec.key;\n        ssl_certificate ec.crt;\n\n        ssl_certificate_key rsa.key;\n        ssl_certificate rsa.crt;\n\n        ssl_certificate_key rsa.key;\n        ssl_certificate rsa.crt;\n    }\n}\n\nEOF\n\n$t->write_file('openssl.conf', <<EOF);\n[ req ]\ndefault_bits = 2048\nencrypt_key = no\ndistinguished_name = req_distinguished_name\n[ req_distinguished_name ]\nEOF\n\nmy $d = $t->testdir();\n\nsystem(\"openssl ecparam -genkey -out $d/ec.key -name prime256v1 \"\n\t. \">>$d/openssl.out 2>&1\") == 0 or die \"Can't create EC pem: $!\\n\";\nsystem(\"openssl genrsa -out $d/rsa.key 2048 >>$d/openssl.out 2>&1\") == 0\n        or die \"Can't create RSA pem: $!\\n\";\n\nforeach my $name ('ec', 'rsa') {\n\tsystem(\"openssl req -x509 -new -key $d/$name.key \"\n\t\t. \"-config $d/openssl.conf -subj /CN=$name/ \"\n\t\t. \"-out $d/$name.crt -keyout $d/$name.key \"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create certificate for $name: $!\\n\";\n}\n\n$t->write_file('index.html', '');\n$t->run()->plan(2);\n\n###############################################################################\n\nTODO: {\nlocal $TODO = 'broken TLSv1.3 sigalgs in LibreSSL'\n\tif $t->has_module('LibreSSL') && test_tls13();\nlike(cert('RSA'), qr/CN=rsa/, 'ssl cert RSA');\n}\nlike(cert('ECDSA'), qr/CN=ec/, 'ssl cert ECDSA');\n\n###############################################################################\n\nsub test_tls13 {\n\treturn http_get('/', SSL => 1) =~ /TLSv1.3/;\n}\n\nsub cert {\n\tmy $s = get_socket(@_) || return;\n\treturn $s->dump_peer_certificate();\n}\n\nsub get_socket {\n\tmy ($type) = @_;\n\n\tmy $ctx_cb = sub {\n\t\tmy $ctx = shift;\n\n\t\treturn unless defined $type;\n\n\n\t\tmy $ssleay = Net::SSLeay::SSLeay();\n\t\treturn if ($ssleay < 0x1000200f || $ssleay == 0x20000000);\n\t\tmy @sigalgs = ('RSA+SHA256:PSS+SHA256', 'RSA+SHA256');\n\t\t@sigalgs = ($type . '+SHA256') unless $type eq 'RSA';\n\t\t\t# SSL_CTRL_SET_SIGALGS_LIST\n\t\tNet::SSLeay::CTX_ctrl($ctx, 98, 0, $sigalgs[0])\n\t\t\tor Net::SSLeay::CTX_ctrl($ctx, 98, 0, $sigalgs[1])\n\t\t\t\tor die(\"Failed to set sigalgs\");\n\t};\n\n\treturn http_get(\n\t\t'/', start => 1,\n\t\tSSL => 1,\n\t\tSSL_cipher_list => $type,\n\t\tSSL_create_ctx_callback => $ctx_cb\n\t);\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/ssl_client_escaped_cert.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for http ssl module, $ssl_client_escaped_cert variable.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\n\nmy $t = Test::Nginx->new()->has(qw/http http_ssl rewrite socket_ssl/)\n\t->has_daemon('openssl')->plan(3);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    ssl_certificate_key localhost.key;\n    ssl_certificate localhost.crt;\n    ssl_verify_client optional_no_ca;\n\n    server {\n        listen       127.0.0.1:8443 ssl;\n        server_name  localhost;\n\n        location /cert {\n            return 200 $ssl_client_raw_cert;\n        }\n        location /escaped {\n            return 200 $ssl_client_escaped_cert;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('openssl.conf', <<EOF);\n[ req ]\ndefault_bits = 2048\nencrypt_key = no\ndistinguished_name = req_distinguished_name\n[ req_distinguished_name ]\nEOF\n\nmy $d = $t->testdir();\n\nforeach my $name ('localhost') {\n\tsystem('openssl req -x509 -new '\n\t\t. \"-config $d/openssl.conf -subj /CN=$name/ \"\n\t\t. \"-out $d/$name.crt -keyout $d/$name.key \"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create certificate for $name: $!\\n\";\n}\n\n$t->run();\n\n###############################################################################\n\nmy ($cert) = cert('/cert') =~ /\\x0d\\x0a?\\x0d\\x0a?(.*)/ms;\nmy ($escaped) = cert('/escaped') =~ /\\x0d\\x0a?\\x0d\\x0a?(.*)/ms;\n\nok($cert, 'ssl_client_raw_cert');\nok($escaped, 'ssl_client_escaped_cert');\n\n$escaped =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg;\nis($escaped, $cert, 'ssl_client_escaped_cert unescape match');\n\n###############################################################################\n\nsub cert {\n\tmy ($uri) = @_;\n\treturn http_get(\n\t\t$uri,\n\t\tSSL => 1,\n\t\t\tSSL_cert_file => \"$d/localhost.crt\",\n\t\tSSL_key_file => \"$d/localhost.key\"\n\t\t);\n\n\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/ssl_conf_command.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for http ssl module, ssl_conf_command.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx qw/ :DEFAULT http_end /;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\n\nmy $t = Test::Nginx->new()\n\t->has(qw/http http_ssl openssl:1.0.2 socket_ssl_reused/)\n\t->has_daemon('openssl');\n\nplan(skip_all => 'no ssl_conf_command') if $t->has_module('BoringSSL');\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8443 ssl;\n        server_name  localhost;\n\n        ssl_protocols TLSv1.2;\n\n        ssl_session_tickets off;\n        ssl_conf_command Options SessionTicket;\n\n        ssl_prefer_server_ciphers on;\n        ssl_conf_command Options -ServerPreference;\n        ssl_ciphers ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256;\n\n        ssl_certificate localhost.crt;\n        ssl_certificate_key localhost.key;\n        ssl_conf_command Certificate override.crt;\n        ssl_conf_command PrivateKey override.key;\n    }\n}\n\nEOF\n\n$t->write_file('openssl.conf', <<EOF);\n[ req ]\ndefault_bits = 2048\nencrypt_key = no\ndistinguished_name = req_distinguished_name\n[ req_distinguished_name ]\nEOF\n\nmy $d = $t->testdir();\n\nforeach my $name ('localhost', 'override') {\n\tsystem('openssl req -x509 -new '\n\t\t. \"-config $d/openssl.conf -subj /CN=$name/ \"\n\t\t. \"-out $d/$name.crt -keyout $d/$name.key \"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create certificate for $name: $!\\n\";\n}\n\n$t->run()->plan(3);\n\n###############################################################################\n\nmy $s;\n\n$s = http_get(\n\t'/', start => 1,\n\tSSL => 1,\n\tSSL_session_cache_size => 100\n);\nlike($s->dump_peer_certificate(), qr/CN=override/, 'Certificate');\nhttp_end($s);\n\n$s = http_get(\n\t'/', start => 1,\n\tSSL => 1,\n\tSSL_reuse_ctx => $s\n);\nok($s->get_session_reused(), 'SessionTicket');\n\n$s = http_get(\n\t'/', start => 1,\n\tSSL => 1,\n\tSSL_cipher_list =>\n\t\t'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384'\n);\n\nis($s->get_cipher(), 'ECDHE-RSA-AES128-GCM-SHA256', 'ServerPreference');\n\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/ssl_crl.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for http ssl module, ssl_crl directive.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\n\nmy $t = Test::Nginx->new()->has(qw/http http_ssl socket_ssl/)\n\t->has_daemon('openssl')->plan(3);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    ssl_certificate_key  localhost.key;\n    ssl_certificate localhost.crt;\n\n    ssl_verify_client on;\n    ssl_client_certificate int-root.crt;\n\n    add_header X-Verify $ssl_client_verify always;\n\n    server {\n        listen       127.0.0.1:8080 ssl;\n        server_name  localhost;\n\n        ssl_client_certificate root.crt;\n        ssl_crl empty.crl;\n    }\n\n    server {\n        listen       127.0.0.1:8081 ssl;\n        server_name  localhost;\n\n        ssl_client_certificate root.crt;\n        ssl_crl root.crl;\n    }\n\n    server {\n        listen       127.0.0.1:8082 ssl;\n        server_name  localhost;\n\n        ssl_verify_depth 2;\n        ssl_crl root.crl;\n    }\n}\n\nEOF\n\nmy $d = $t->testdir();\n\n$t->write_file('openssl.conf', <<EOF);\n[ req ]\ndefault_bits = 2048\nencrypt_key = no\ndistinguished_name = req_distinguished_name\n[ req_distinguished_name ]\nEOF\n\n$t->write_file('ca.conf', <<EOF);\n[ ca ]\ndefault_ca = myca\n\n[ myca ]\nnew_certs_dir = $d\ndatabase = $d/certindex\ndefault_md = sha256\npolicy = myca_policy\nserial = $d/certserial\ndefault_days = 1\n\n[ myca_policy ]\ncommonName = supplied\nEOF\n\nforeach my $name ('root', 'localhost') {\n\tsystem('openssl req -x509 -new '\n\t\t. \"-config $d/openssl.conf -subj /CN=$name/ \"\n\t\t. \"-out $d/$name.crt -keyout $d/$name.key \"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create certificate for $name: $!\\n\";\n}\n\nforeach my $name ('int', 'end') {\n\tsystem(\"openssl req -new \"\n\t\t. \"-config $d/openssl.conf -subj /CN=$name/ \"\n\t\t. \"-out $d/$name.csr -keyout $d/$name.key \"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create certificate for $name: $!\\n\";\n}\n\n$t->write_file('certserial', '1000');\n$t->write_file('certindex', '');\n\nsystem(\"openssl ca -batch -config $d/ca.conf \"\n\t. \"-keyfile $d/root.key -cert $d/root.crt \"\n\t. \"-subj /CN=int/ -in $d/int.csr -out $d/int.crt \"\n\t. \">>$d/openssl.out 2>&1\") == 0\n\tor die \"Can't sign certificate for int: $!\\n\";\n\nsystem(\"openssl ca -batch -config $d/ca.conf \"\n\t. \"-keyfile $d/int.key -cert $d/int.crt \"\n\t. \"-subj /CN=end/ -in $d/end.csr -out $d/end.crt \"\n\t. \">>$d/openssl.out 2>&1\") == 0\n\tor die \"Can't sign certificate for end: $!\\n\";\n\nsystem(\"openssl ca -gencrl -config $d/ca.conf \"\n\t. \"-keyfile $d/root.key -cert $d/root.crt \"\n\t. \"-out $d/empty.crl -crldays 1 \"\n\t. \">>$d/openssl.out 2>&1\") == 0\n\tor die \"Can't create empty crl: $!\\n\";\n\nsystem(\"openssl ca -config $d/ca.conf -revoke $d/int.crt \"\n\t. \"-keyfile $d/root.key -cert $d/root.crt \"\n\t. \">>$d/openssl.out 2>&1\") == 0\n\tor die \"Can't revoke int.crt: $!\\n\";\n\nsystem(\"openssl ca -gencrl -config $d/ca.conf \"\n\t. \"-keyfile $d/root.key -cert $d/root.crt \"\n\t. \"-out $d/root.crl -crldays 1 \"\n\t. \">>$d/openssl.out 2>&1\") == 0\n\tor die \"Can't update crl: $!\\n\";\n\n$t->write_file('int-root.crt',\n\t$t->read_file('int.crt') . $t->read_file('root.crt'));\n\n$t->write_file('t', '');\n$t->run();\n\n###############################################################################\n\nlike(get(8080, 'int'), qr/SUCCESS/, 'crl - no revoked certs');\nlike(get(8081, 'int'), qr/FAILED/, 'crl - client cert revoked');\nlike(get(8082, 'end'), qr/FAILED/, 'crl - intermediate cert revoked');\n\n###############################################################################\n\nsub get {\n\tmy ($port, $cert) = @_;\n\thttp_get(\n\n\n\t\t'/t', PeerAddr => '127.0.0.1:' . port($port),\n\t\tSSL => 1,\n\t\t\tSSL_cert_file => \"$d/$cert.crt\",\n\t\tSSL_key_file => \"$d/$cert.key\"\n\t\t);\n\n\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/ssl_curve.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for http ssl module, $ssl_curve variable.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()\n\n\t->has(qw/http http_ssl rewrite socket_ssl openssl:3.0.0/)\n\t->has_daemon('openssl');\n\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    ssl_certificate_key localhost.key;\n    ssl_certificate localhost.crt;\n\n    ssl_ecdh_curve prime256v1;\n\n    server {\n        listen       127.0.0.1:8443 ssl;\n        server_name  localhost;\n\n        return 200 \"$ssl_curve $ssl_curves\";\n    }\n}\n\nEOF\n\n$t->write_file('openssl.conf', <<EOF);\n[ req ]\ndefault_bits = 2048\nencrypt_key = no\ndistinguished_name = req_distinguished_name\n[ req_distinguished_name ]\nEOF\n\nmy $d = $t->testdir();\n\nforeach my $name ('localhost') {\n\tsystem('openssl req -x509 -new '\n\t\t. \"-config $d/openssl.conf -subj /CN=$name/ \"\n\t\t. \"-out $d/$name.crt -keyout $d/$name.key \"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create certificate for $name: $!\\n\";\n}\n\n$t->try_run('no $ssl_curve')->plan(1);\n\n###############################################################################\n\nlike(http_get('/curve', SSL => 1), qr/^prime256v1 /m, 'ssl curve');\n\n###############################################################################\n\n\n\n\n\n\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/ssl_engine_keys.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for http ssl module, loading \"engine:...\" keys.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nplan(skip_all => 'win32') if $^O eq 'MSWin32';\n\nplan(skip_all => 'may not work, leaves coredump')\n\tunless $ENV{TEST_NGINX_UNSAFE};\n\nmy $t = Test::Nginx->new()->has(qw/http proxy http_ssl/)->has_daemon('openssl')\n\t->has_daemon('softhsm2-util')->has_daemon('pkcs11-tool')->plan(2);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8081 ssl;\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        ssl_certificate localhost.crt;\n        ssl_certificate_key engine:pkcs11:id_00;\n\n        location / {\n            # index index.html by default\n        }\n\n        location /proxy {\n            proxy_pass https://127.0.0.1:8081/;\n        }\n\n        location /var {\n            proxy_pass https://127.0.0.1:8082/;\n            proxy_ssl_name localhost;\n            proxy_ssl_server_name on;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8082 ssl;\n        server_name  localhost;\n\n        ssl_certificate $ssl_server_name.crt;\n        ssl_certificate_key engine:pkcs11:id_00;\n\n        location / {\n            # index index.html by default\n        }\n    }\n}\n\nEOF\n\n# Create a SoftHSM token with a secret key, and configure OpenSSL\n# to access it using the pkcs11 engine, see detailed example\n# posted by Dmitrii Pichulin here:\n#\n# http://mailman.nginx.org/pipermail/nginx-devel/2014-October/006151.html\n#\n# Note that library paths may differ on different systems,\n# and may need to be adjusted.\n\n$t->write_file('openssl.conf', <<EOF);\nopenssl_conf = openssl_def\n\n[openssl_def]\nengines = engine_section\n\n[engine_section]\npkcs11 = pkcs11_section\n\n[pkcs11_section]\nengine_id = pkcs11\ndynamic_path = /usr/local/lib/engines/pkcs11.so\nMODULE_PATH = /usr/local/lib/softhsm/libsofthsm2.so\ninit = 1\nPIN = 1234\n\n[ req ]\ndefault_bits = 2048\nencrypt_key = no\ndistinguished_name = req_distinguished_name\n[ req_distinguished_name ]\nEOF\n\nmy $d = $t->testdir();\n\n$t->write_file('softhsm2.conf', <<EOF);\ndirectories.tokendir = $d/tokens/\nobjectstore.backend = file\nEOF\n\nmkdir($d . '/tokens');\n\n$ENV{SOFTHSM2_CONF} = \"$d/softhsm2.conf\";\n$ENV{OPENSSL_CONF} = \"$d/openssl.conf\";\n\nforeach my $name ('localhost') {\n\tsystem('softhsm2-util --init-token --slot 0 --label NginxZero '\n\t\t. '--pin 1234 --so-pin 1234 '\n\t\t. \">>$d/openssl.out 2>&1\");\n\n\tsystem('pkcs11-tool --module=/usr/local/lib/softhsm/libsofthsm2.so '\n\t\t. '-p 1234 -l -k -d 0 -a nx_key_0 --key-type rsa:2048 '\n\t\t. \">>$d/openssl.out 2>&1\");\n\n\tsystem('openssl req -x509 -new '\n\t\t. \"-subj /CN=$name/ -out $d/$name.crt -text \"\n\t\t. \"-engine pkcs11 -keyform engine -key id_00 \"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create certificate for $name: $!\\n\";\n}\n\n$t->run();\n\n$t->write_file('index.html', '');\n\n###############################################################################\n\nlike(http_get('/proxy'), qr/200 OK/, 'ssl engine keys');\nlike(http_get('/var'), qr/200 OK/, 'ssl_certificate with variable');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/ssl_password_file.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for ssl_password_file directive.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse POSIX qw/ mkfifo /;\nuse Socket qw/ $CRLF /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\n\nplan(skip_all => 'win32') if $^O eq 'MSWin32';\n\nmy $t = Test::Nginx->new()->has(qw/http http_ssl rewrite socket_ssl/)\n\t->has_daemon('openssl');\n\n$t->plan(3)->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    ssl_certificate_key localhost.key;\n    ssl_certificate localhost.crt;\n\n    # inherited by server \"inherits\"\n    ssl_password_file password_http;\n\n    server {\n        listen       127.0.0.1:8443 ssl;\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        ssl_password_file password;\n\n        location / {\n            return 200 \"$scheme\";\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  two_entries;\n\n        ssl_password_file password_many;\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  file_is_fifo;\n\n        ssl_password_file password_fifo;\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  inherits;\n\n        ssl_certificate_key inherits.key;\n        ssl_certificate inherits.crt;\n    }\n}\n\nEOF\n\n$t->write_file('openssl.conf', <<EOF);\n[ req ]\ndefault_bits = 2048\nencrypt_key = no\ndistinguished_name = req_distinguished_name\n[ req_distinguished_name ]\nEOF\n\nmy $d = $t->testdir();\nmkfifo(\"$d/password_fifo\", 0700);\n\nforeach my $name ('localhost', 'inherits') {\n\tsystem(\"openssl genrsa -out $d/$name.key -passout pass:$name \"\n\t\t. \"-aes128 2048 >>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create private key: $!\\n\";\n\tsystem('openssl req -x509 -new '\n\t\t. \"-config $d/openssl.conf -subj /CN=$name/ \"\n\t\t. \"-out $d/$name.crt \"\n\t\t. \"-key $d/$name.key -passin pass:$name\"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create certificate for $name: $!\\n\";\n}\n\n$t->write_file('password', 'localhost');\n$t->write_file('password_many', \"wrong$CRLF\" . \"localhost$CRLF\");\n$t->write_file('password_http', 'inherits');\n\nmy $p = fork();\nexec(\"echo localhost > $d/password_fifo\") if $p == 0;\n\n# do not mangle with try_run()\n# we need to distinguish ssl_password_file support vs its brokenness\n\neval {\n\topen OLDERR, \">&\", \\*STDERR; close STDERR;\n\t$t->run();\n\topen STDERR, \">&\", \\*OLDERR;\n};\nkill 'INT', $p if $@;\n\n###############################################################################\n\nis($@, '', 'ssl_password_file works');\n\n# simple tests to ensure that nothing broke with ssl_password_file directive\n\nlike(http_get('/'), qr/200 OK.*http/ms, 'http');\nlike(http_get('/', SSL => 1), qr/200 OK.*https/ms, 'https');\n\n###############################################################################\n\n\n\n\n\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/ssl_proxy_protocol.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for http ssl module with haproxy protocol.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse Socket qw/ CRLF /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\n\nmy $t = Test::Nginx->new()->has(qw/http http_ssl access realip socket_ssl/)\n\t->has_daemon('openssl');\n\n$t->write_file_expand('nginx.conf', <<'EOF')->plan(18);\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    log_format pp '$remote_addr $request';\n\n    server {\n        listen       127.0.0.1:8080 proxy_protocol ssl;\n        server_name  localhost;\n\n        ssl_certificate_key localhost.key;\n        ssl_certificate localhost.crt;\n\n        set_real_ip_from  127.0.0.1/32;\n        add_header X-IP $remote_addr;\n        add_header X-PP $proxy_protocol_addr;\n\n        location /pp {\n            real_ip_header proxy_protocol;\n            error_page 404 =200 /t1;\n            access_log %%TESTDIR%%/pp.log pp;\n\n            location /pp_4 {\n                deny 192.0.2.1/32;\n            }\n            location /pp_6 {\n                deny 2001:DB8::1/128;\n            }\n        }\n    }\n}\n\nEOF\n\n$t->write_file('openssl.conf', <<EOF);\n[ req ]\ndefault_bits = 2048\nencrypt_key = no\ndistinguished_name = req_distinguished_name\n[ req_distinguished_name ]\nEOF\n\nmy $d = $t->testdir();\n\nforeach my $name ('localhost') {\n\tsystem('openssl req -x509 -new '\n\t\t. \"-config $d/openssl.conf -subj /CN=$name/ \"\n\t\t. \"-out $d/$name.crt -keyout $d/$name.key \"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\tor die \"Can't create certificate for $name: $!\\n\";\n}\n\n$t->write_file('t1', 'SEE-THIS');\n$t->run();\n\n###############################################################################\n\nmy $tcp4 = 'PROXY TCP4 192.0.2.1 192.0.2.2 1234 5678' . CRLF;\nmy $tcp6 = 'PROXY TCP6 2001:Db8::1 2001:Db8::2 1234 5678' . CRLF;\nmy $unk1 = 'PROXY UNKNOWN' . CRLF;\nmy $unk2 = 'PROXY UNKNOWN 1 2 3 4 5 6' . CRLF;\nmy $r;\n\n# no realip, just PROXY header parsing\n\n$r = pp_get('/t1', $tcp4);\nlike($r, qr/SEE-THIS/, 'tcp4 request');\nlike($r, qr/X-PP: 192.0.2.1/, 'tcp4 proxy');\nunlike($r, qr/X-IP: 192.0.2.1/, 'tcp4 client');\n\n$r = pp_get('/t1', $tcp6);\nlike($r, qr/SEE-THIS/, 'tcp6 request');\nlike($r, qr/X-PP: 2001:DB8::1/i, 'tcp6 proxy');\nunlike($r, qr/X-IP: 2001:DB8::1/i, 'tcp6 client');\n\nlike(pp_get('/t1', $unk1), qr/SEE-THIS/, 'unknown request 1');\nlike(pp_get('/t1', $unk2), qr/SEE-THIS/, 'unknown request 2');\n\n# realip\n\n$r = pp_get('/pp', $tcp4);\nlike($r, qr/SEE-THIS/, 'tcp4 request realip');\nlike($r, qr/X-PP: 192.0.2.1/, 'tcp4 proxy realip');\nlike($r, qr/X-IP: 192.0.2.1/, 'tcp4 client realip');\n\n$r = pp_get('/pp', $tcp6);\nlike($r, qr/SEE-THIS/, 'tcp6 request realip');\nlike($r, qr/X-PP: 2001:DB8::1/i, 'tcp6 proxy realip');\nlike($r, qr/X-IP: 2001:DB8::1/i, 'tcp6 client realip');\n\n# access\n\n$r = pp_get('/pp_4', $tcp4);\nlike($r, qr/403 Forbidden/, 'tcp4 access');\n\n$r = pp_get('/pp_6', $tcp6);\nlike($r, qr/403 Forbidden/, 'tcp6 access');\n\n# client address in access.log\n\n$t->stop();\n\nmy $log = $t->read_file('pp.log');\nlike($log, qr!^192\\.0\\.2\\.1 GET /pp_4!m, 'tcp4 access log');\nlike($log, qr!^2001:DB8::1 GET /pp_6!mi, 'tcp6 access log');\n\n###############################################################################\n\nsub pp_get {\n\tmy ($url, $proxy) = @_;\n\n\tmy $s = http($proxy, start => 1);\n\n\n\n\treturn http(<<EOF, socket => $s, SSL => 1);\nGET $url HTTP/1.0\nHost: localhost\n\nEOF\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/ssl_proxy_upgrade.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for http proxy upgrade support with http ssl module.\n# In contrast to proxy_websocket.t, this test doesn't try to use binary\n# WebSocket protocol, but uses simple plain text protocol instead.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse IO::Poll;\nuse IO::Select;\nuse Socket qw/ CRLF /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\n\nmy $t = Test::Nginx->new()->has(qw/http proxy http_ssl socket_ssl/)\n\t->has_daemon('openssl')\n\t->write_file_expand('nginx.conf', <<'EOF')->plan(30);\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    log_format test \"$bytes_sent $body_bytes_sent\";\n    access_log %%TESTDIR%%/cc.log test;\n\n    server {\n        listen       127.0.0.1:8080 ssl;\n        server_name  localhost;\n\n        ssl_certificate_key localhost.key;\n        ssl_certificate localhost.crt;\n\n        location / {\n            proxy_pass    http://127.0.0.1:8081;\n            proxy_http_version 1.1;\n            proxy_set_header Upgrade $http_upgrade;\n            proxy_set_header Connection \"Upgrade\";\n            proxy_read_timeout 2s;\n            send_timeout 2s;\n        }\n    }\n}\n\nEOF\n\n$t->write_file('openssl.conf', <<EOF);\n[ req ]\ndefault_bits = 2048\nencrypt_key = no\ndistinguished_name = req_distinguished_name\n[ req_distinguished_name ]\nEOF\n\nmy $d = $t->testdir();\n\nforeach my $name ('localhost') {\n\tsystem('openssl req -x509 -new '\n\t\t. \"-config $d/openssl.conf -subj /CN=$name/ \"\n\t\t. \"-out $d/$name.crt -keyout $d/$name.key \"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create certificate for $name: $!\\n\";\n}\n\n$t->run_daemon(\\&upgrade_fake_daemon);\n$t->run();\n\n$t->waitforsocket('127.0.0.1:' . port(8081))\n\tor die \"Can't start test backend\";\n\n###############################################################################\n\n# establish connection\n\nmy @r;\nmy $s = upgrade_connect();\nok($s, \"handshake\");\n\nSKIP: {\n\tskip \"handshake failed\", 22 unless $s;\n\n\t# send a frame\n\n\tupgrade_write($s, 'foo');\n\tis(upgrade_read($s), 'bar', \"upgrade response\");\n\n\t# send some big frame\n\n\tupgrade_write($s, 'foo' x 16384);\n\tlike(upgrade_read($s), qr/^(bar){16384}$/, \"upgrade big response\");\n\n\t# send multiple frames\n\n\tfor my $i (1 .. 10) {\n\t\tupgrade_write($s, ('foo' x 16384) . $i, continue => 1);\n\t\tupgrade_write($s, 'bazz' . $i, continue => $i != 10);\n\t}\n\n\tfor my $i (1 .. 10) {\n\t\tlike(upgrade_read($s), qr/^(bar){16384}\\d+$/, \"upgrade $i\");\n\t\tis(upgrade_read($s), 'bazz' . $i, \"upgrade small $i\");\n\t}\n}\n\npush @r, $s ? ${*$s}->{_upgrade_private}->{r} : 'failed';\nundef $s;\n\n# establish connection with some pipelined data\n# and make sure they are correctly passed upstream\n\n$s = upgrade_connect(message => \"foo\");\nok($s, \"handshake pipelined\");\n\nSKIP: {\n\tskip \"handshake failed\", 2 unless $s;\n\n\tis(upgrade_read($s), \"bar\", \"response pipelined\");\n\n\tupgrade_write($s, \"foo\");\n\tis(upgrade_read($s), \"bar\", \"next to pipelined\");\n}\n\npush @r, $s ? ${*$s}->{_upgrade_private}->{r} : 'failed';\nundef $s;\n\n# connection should not be upgraded unless upgrade was actually\n# requested and allowed by configuration\n\n$s = upgrade_connect(noheader => 1);\nok(!$s, \"handshake noupgrade\");\n\n# bytes sent on upgraded connection, fixed in c2f309fb7ad2 (1.7.11)\n# verify with 1) data actually read by client, 2) expected data from backend\n\n$t->stop();\n\nopen my $f, '<', \"$d/cc.log\" or die \"Can't open cc.log: $!\";\n\nis($f->getline(), shift (@r) . \" 540793\\n\", 'log - bytes');\nis($f->getline(), shift (@r) . \" 22\\n\", 'log - bytes pipelined');\nlike($f->getline(), qr/\\d+ 0\\n/, 'log - bytes noupgrade');\n\n###############################################################################\n\nsub upgrade_connect {\n\tmy (%opts) = @_;\n\n\tmy $s = IO::Socket::SSL->new(\n\t\tProto => 'tcp',\n\t\tPeerAddr => '127.0.0.1:' . port(8080),\n\t\tSSL_verify_mode => IO::Socket::SSL::SSL_VERIFY_NONE(),\n\t)\n\t\tor die \"Can't connect to nginx: $!\\n\";\n\n\t# send request, $h->to_string\n\n\tmy $buf = \"GET / HTTP/1.1\" . CRLF\n\t\t. \"Host: localhost\" . CRLF\n\t\t. ($opts{noheader} ? '' : \"Upgrade: foo\" . CRLF)\n\t\t. \"Connection: Upgrade\" . CRLF . CRLF;\n\n\t$buf .= $opts{message} . CRLF . 'FIN' if defined $opts{message};\n\n\tlocal $SIG{PIPE} = 'IGNORE';\n\n\tlog_out($buf);\n\t$s->syswrite($buf);\n\n\t# read response\n\n\tmy $got = '';\n\t$buf = '';\n\n\twhile (1) {\n\t\t$buf = upgrade_getline($s);\n\t\tlast unless defined $buf and length $buf;\n\t\tlog_in($buf);\n\t\t$got .= $buf;\n\t\tlast if $got =~ /\\x0d?\\x0a\\x0d?\\x0a$/;\n\t}\n\n\t# parse server response\n\n\treturn if $got !~ m!HTTP/1.1 101!;\n\n\t# make sure next line is \"handshaked\"\n\n\t$buf = upgrade_read($s);\n\n\treturn if !defined $buf or $buf ne 'handshaked';\n\treturn $s;\n}\n\nsub upgrade_getline {\n\tmy ($s) = @_;\n\tmy ($h, $buf);\n\n\t${*$s}->{_upgrade_private} ||= { b => '', r => 0 };\n\t$h = ${*$s}->{_upgrade_private};\n\n\tif ($h->{b} =~ /^(.*?\\x0a)(.*)/ms) {\n\t\t$h->{b} = $2;\n\t\treturn $1;\n\t}\n\n\t$s->blocking(0);\n\twhile (IO::Select->new($s)->can_read(3)) {\n\t\tmy $n = $s->sysread($buf, 16384);\n\t\tif (!defined $n) {\n\t\t\tnext if $s->errstr() == IO::Socket::SSL->SSL_WANT_READ;\n\t\t\tlast;\n\n\t\t} elsif (!$n) {\n\t\t\tlast;\n\t\t}\n\n\t\t$h->{b} .= $buf;\n\t\t$h->{r} += $n;\n\n\t\tif ($h->{b} =~ /^(.*?\\x0a)(.*)/ms) {\n\t\t\t$h->{b} = $2;\n\t\t\treturn $1;\n\t\t}\n\t};\n}\n\nsub upgrade_write {\n\tmy ($s, $message, %extra) = @_;\n\n\t$message = $message . CRLF;\n\t$message = $message . 'FIN' unless $extra{continue};\n\n\tlocal $SIG{PIPE} = 'IGNORE';\n\n\t$s->blocking(0);\n\twhile (IO::Select->new($s)->can_write(1.5)) {\n\t\tmy $n = $s->syswrite($message);\n\t\tunless ($n) {\n\t\t\tnext if $s->errstr() == IO::Socket::SSL->SSL_WANT_WRITE;\n\t\t\tlast;\n\t\t}\n\t\t$message = substr($message, $n);\n\t\tlast unless length $message;\n\t}\n\n\tif (length $message) {\n\t\t$s->close();\n\t}\n}\n\nsub upgrade_read {\n\tmy ($s) = @_;\n\tmy $m = upgrade_getline($s);\n\t$m =~ s/\\x0d?\\x0a// if defined $m;\n\tlog_in($m);\n\treturn $m;\n}\n\n###############################################################################\n\nsub upgrade_fake_daemon {\n\tmy $server = IO::Socket::INET->new(\n\t\tProto => 'tcp',\n\t\tLocalAddr => '127.0.0.1:' . port(8081),\n\t\tListen => 5,\n\t\tReuse => 1\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\twhile (my $client = $server->accept()) {\n\t\tupgrade_handle_client($client);\n\t}\n}\n\nsub upgrade_handle_client {\n\tmy ($client) = @_;\n\n\t$client->autoflush(1);\n\t$client->blocking(0);\n\n\tmy $poll = IO::Poll->new;\n\n\tmy $handshake = 1;\n\tmy $unfinished = '';\n\tmy $buffer = '';\n\tmy $n;\n\n\tlog2c(\"(new connection $client)\");\n\n\twhile (1) {\n\t\t$poll->mask($client => ($buffer ? POLLIN|POLLOUT : POLLIN));\n\t\tmy $p = $poll->poll(0.5);\n\t\tlog2c(\"(poll $p)\");\n\n\t\tforeach ($poll->handles(POLLIN)) {\n\t\t\t$n = $client->sysread(my $chunk, 65536);\n\t\t\treturn unless $n;\n\n\t\t\tlog2i($chunk);\n\n\t\t\tif ($handshake) {\n\t\t\t\t$buffer .= $chunk;\n\t\t\t\tnext unless $buffer =~ /\\x0d?\\x0a\\x0d?\\x0a$/;\n\n\t\t\t\tlog2c(\"(handshake done)\");\n\n\t\t\t\t$handshake = 0;\n\t\t\t\t$buffer = 'HTTP/1.1 101 Switching' . CRLF\n\t\t\t\t\t. 'Upgrade: foo' . CRLF\n\t\t\t\t\t. 'Connection: Upgrade' . CRLF . CRLF\n\t\t\t\t\t. 'handshaked' . CRLF;\n\n\t\t\t\tlog2o($buffer);\n\n\t\t\t\tnext;\n\t\t\t}\n\n\t\t\t$unfinished .= $chunk;\n\n\t\t\tif ($unfinished =~ m/\\x0d?\\x0aFIN\\z/) {\n\t\t\t\t$unfinished =~ s/FIN\\z//;\n\t\t\t\t$unfinished =~ s/foo/bar/g;\n\t\t\t\tlog2o($unfinished);\n\t\t\t\t$buffer .= $unfinished;\n\t\t\t\t$unfinished = '';\n\t\t\t}\n\t\t}\n\n\t\tforeach my $writer ($poll->handles(POLLOUT)) {\n\t\t\tnext unless length $buffer;\n\t\t\t$n = $writer->syswrite($buffer);\n\t\t\tsubstr $buffer, 0, $n, '';\n\t\t}\n\t}\n}\n\nsub log2i { Test::Nginx::log_core('|| <<', @_); }\nsub log2o { Test::Nginx::log_core('|| >>', @_); }\nsub log2c { Test::Nginx::log_core('||', @_); }\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/ssl_reject_handshake.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for http ssl module, ssl_reject_handshake.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\n\nmy $t = Test::Nginx->new()->has(qw/http http_ssl sni socket_ssl/)\n\t->has_daemon('openssl')->plan(7);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    add_header X-Name $ssl_server_name;\n\n    server {\n        listen       127.0.0.1:8080 ssl;\n        server_name  localhost;\n\n        ssl_reject_handshake on;\n    }\n\n    server {\n        listen       127.0.0.1:8080;\n\n\n        server_name  virtual;\n\n        ssl_certificate localhost.crt;\n        ssl_certificate_key localhost.key;\n    }\n\n    server {\n        listen       127.0.0.1:8082 ssl;\n        server_name  localhost;\n\n        ssl_certificate localhost.crt;\n        ssl_certificate_key localhost.key;\n    }\n\n    server {\n        listen       127.0.0.1:8082;\n        server_name  virtual1;\n    }\n\n    server {\n        listen       127.0.0.1:8082;\n        server_name  virtual2;\n\n        ssl_reject_handshake on;\n    }\n}\n\nEOF\n\n$t->write_file('openssl.conf', <<EOF);\n[ req ]\ndefault_bits = 2048\nencrypt_key = no\ndistinguished_name = req_distinguished_name\n[ req_distinguished_name ]\nEOF\n\nmy $d = $t->testdir();\n\nforeach my $name ('localhost') {\n\tsystem('openssl req -x509 -new '\n\t\t. \"-config $d/openssl.conf -subj /CN=$name/ \"\n\t\t. \"-out $d/$name.crt -keyout $d/$name.key \"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create certificate for $name: $!\\n\";\n}\n\n$t->write_file('index.html', '');\n\n\n$t->run();\n\n###############################################################################\n\n# default virtual server rejected\n\nlike(get('default', 8080), qr/unrecognized name/, 'default rejected');\nlike(get(undef, 8080), qr/unrecognized name/, 'absent sni rejected');\nlike(get('virtual', 8080), qr/virtual/, 'virtual accepted');\n\n\n\n# non-default server \"virtual2\" rejected\n\nlike(get('default', 8082), qr/default/, 'default accepted');\nlike(get(undef, 8082), qr/200 OK(?!.*X-Name)/is, 'absent sni accepted');\nlike(get('virtual1', 8082), qr/virtual1/, 'virtual 1 accepted');\nlike(get('virtual2', 8082), qr/unrecognized name/, 'virtual 2 rejected');\n\n###############################################################################\n\nsub get {\n\tmy ($host, $port) = @_;\n\tmy $r = http(\n\t\t\"GET / HTTP/1.0\\nHost: \" . ($host || 'localhost') . \"\\n\\n\",\n\t\tPeerAddr => '127.0.0.1:' . port($port),\n\t\tSSL => 1,\n\t\tSSL_hostname => $host\n\t)\n\t\tor return \"$@\";\n\n\treturn $r;\n}\n\n\n\n\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/ssl_session_reuse.t",
    "content": "#!/usr/bin/perl\n\n# (C) Andrey Zelenkov\n# (C) Maxim Dounin\n# (C) Nginx, Inc.\n\n# Tests for http ssl module, session reuse.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx qw/ :DEFAULT http_end /;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http http_ssl rewrite socket_ssl/)\n\t->has_daemon('openssl')->plan(8);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    ssl_certificate_key localhost.key;\n    ssl_certificate localhost.crt;\n\n    server {\n        listen       127.0.0.1:8443 ssl;\n        server_name  localhost;\n\n        location / {\n            return 200 \"body $ssl_session_reused\";\n        }\n        location /protocol {\n            return 200 \"body $ssl_protocol\";\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8444 ssl;\n        server_name  localhost;\n\n        ssl_session_cache shared:SSL:1m;\n        ssl_session_tickets on;\n\n        location / {\n            return 200 \"body $ssl_session_reused\";\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8445 ssl;\n        server_name  localhost;\n\n        ssl_session_cache shared:SSL:1m;\n        ssl_session_tickets off;\n\n        location / {\n            return 200 \"body $ssl_session_reused\";\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8446 ssl;\n        server_name  localhost;\n\n        ssl_session_cache builtin;\n        ssl_session_tickets off;\n\n        location / {\n            return 200 \"body $ssl_session_reused\";\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8447 ssl;\n        server_name  localhost;\n\n        ssl_session_cache builtin:1000;\n        ssl_session_tickets off;\n\n        location / {\n            return 200 \"body $ssl_session_reused\";\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8448 ssl;\n        server_name  localhost;\n\n        ssl_session_cache none;\n        ssl_session_tickets off;\n\n        location / {\n            return 200 \"body $ssl_session_reused\";\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8449 ssl;\n        server_name  localhost;\n\n        ssl_session_cache off;\n        ssl_session_tickets off;\n\n        location / {\n            return 200 \"body $ssl_session_reused\";\n        }\n    }\n}\n\nEOF\n\n$t->write_file('openssl.conf', <<EOF);\n[ req ]\ndefault_bits = 2048\nencrypt_key = no\ndistinguished_name = req_distinguished_name\n[ req_distinguished_name ]\nEOF\n\nmy $d = $t->testdir();\n\nforeach my $name ('localhost') {\n\tsystem('openssl req -x509 -new '\n\t\t. \"-config $d/openssl.conf -subj /CN=$name/ \"\n\t\t. \"-out $d/$name.crt -keyout $d/$name.key \"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create certificate for $name: $!\\n\";\n}\n\n$t->run();\n\n###############################################################################\n\n# session reuse:\n#\n# - only tickets, the default\n# - tickets and shared cache, should work always\n# - only shared cache\n# - only builtin cache\n# - only builtin cache with explicitly configured size\n# - only cache none\n# - only cache off\n\nTODO: {\nlocal $TODO = 'no TLSv1.3 sessions, old Net::SSLeay'\n\tif $Net::SSLeay::VERSION < 1.88 && test_tls13();\nlocal $TODO = 'no TLSv1.3 sessions, old IO::Socket::SSL'\n\tif $IO::Socket::SSL::VERSION < 2.061 && test_tls13();\nlocal $TODO = 'no TLSv1.3 sessions in LibreSSL'\n\tif $t->has_module('LibreSSL') && test_tls13();\n\nis(test_reuse(8443), 1, 'tickets reused');\nis(test_reuse(8444), 1, 'tickets and cache reused');\n\nTODO: {\nlocal $TODO = 'no TLSv1.3 session cache in BoringSSL'\n\tif $t->has_module('BoringSSL') && test_tls13();\n\nis(test_reuse(8445), 1, 'cache shared reused');\nis(test_reuse(8446), 1, 'cache builtin reused');\nis(test_reuse(8447), 1, 'cache builtin size reused');\n\n}\n}\n\nis(test_reuse(8448), 0, 'cache none not reused');\nis(test_reuse(8449), 0, 'cache off not reused');\n\n$t->stop();\n\nlike(`grep -F '[crit]' ${\\($t->testdir())}/error.log`, qr/^$/s, 'no crit');\n\n###############################################################################\n\nsub test_tls13 {\n\treturn http_get('/protocol', SSL => 1) =~ /TLSv1.3/;\n}\n\nsub test_reuse {\n\tmy ($port) = @_;\n\n\tmy $s = http_get(\n\t\t'/', PeerAddr => '127.0.0.1:' . port($port), start => 1,\n\t\tSSL => 1,\n\t\tSSL_session_cache_size => 100\n\t);\n\thttp_end($s);\n\n\tmy $r = http_get(\n\t\t'/', PeerAddr => '127.0.0.1:' . port($port),\n\t\tSSL => 1,\n\t\tSSL_reuse_ctx => $s\n\t);\n\n\treturn ($r =~ qr/^body r$/m) ? 1 : 0;\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/ssl_session_ticket_key.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for rotation of SSL session ticket keys.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx qw/ :DEFAULT http_end /;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\neval { require Net::SSLeay; die if $Net::SSLeay::VERSION < 1.86; };\nplan(skip_all => 'Net::SSLeay version => 1.86 required') if $@;\neval { require IO::Socket::SSL; die if $IO::Socket::SSL::VERSION < 2.030; };\nplan(skip_all => 'IO::Socket::SSL version => 2.030 required') if $@;\n\nmy $t = Test::Nginx->new()->has(qw/http http_ssl socket_ssl/)\n\t->has_daemon('openssl')->plan(2)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\nworker_processes 2;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    ssl_certificate_key localhost.key;\n    ssl_certificate localhost.crt;\n\n    add_header X-SSL-Protocol $ssl_protocol;\n\n    server {\n        listen       127.0.0.1:8443 ssl;\n        server_name  localhost;\n\n        ssl_session_cache shared:SSL:1m;\n        ssl_session_timeout 2;\n    }\n}\n\nEOF\n\n$t->write_file('openssl.conf', <<EOF);\n[ req ]\ndefault_bits = 2048\nencrypt_key = no\ndistinguished_name = req_distinguished_name\n[ req_distinguished_name ]\nEOF\n\nmy $d = $t->testdir();\n\nforeach my $name ('localhost') {\n\tsystem('openssl req -x509 -new '\n\t\t. \"-config $d/openssl.conf -subj /CN=$name/ \"\n\t\t. \"-out $d/$name.crt -keyout $d/$name.key \"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create certificate for $name: $!\\n\";\n}\n\n$t->write_file('index.html', '');\n\n$t->run();\n\n###############################################################################\n\n# the test uses multiple worker processes to check shared tickey key rotation\n#\n# before 1.23.2, any test can fail depending on which worker served connection:\n# the 1st test fails if served by another worker, because keys aren't shared\n# the 2nd test fails if served by the same worker due to the lack of rotation\n#\n# with a single worker process it is only the 2nd test that fails\n\nlocal $TODO = 'not yet' unless $t->has_version('1.23.2');\n\nmy $key = get_ticket_key_name();\n\nselect undef, undef, undef, 0.5;\nis(get_ticket_key_name(), $key, 'ticket key match');\n\nselect undef, undef, undef, 2.5;\n\nlocal $TODO = 'no TLSv1.3 sessions, old Net::SSLeay'\n\tif $Net::SSLeay::VERSION < 1.88 && test_tls13();\nlocal $TODO = 'no TLSv1.3 sessions, old IO::Socket::SSL'\n\tif $IO::Socket::SSL::VERSION < 2.061 && test_tls13();\nlocal $TODO = 'no TLSv1.3 sessions in LibreSSL'\n\tif $t->has_module('LibreSSL') && test_tls13();\n\ncmp_ok(get_ticket_key_name(), 'ne', $key, 'ticket key next');\n\n###############################################################################\n\nsub get_ticket_key_name {\n\tmy $asn = get_ssl_session();\n\tmy $any = qr/[\\x00-\\xff]/;\nnext:\n\t# tag(10) | len{2} | OCTETSTRING(4) | len{2} | ticket(key_name|..)\n\t$asn =~ /\\xaa\\x81($any)\\x04\\x81($any)($any{16})/g;\n\treturn '' if !defined $3;\n\tgoto next if unpack(\"C\", $1) - unpack(\"C\", $2) != 3;\n\tmy $key = unpack \"H*\", $3;\n\tTest::Nginx::log_core('||', \"ticket key: $key\");\n\treturn $key;\n}\n\nsub get_ssl_session {\n\tmy $cache = IO::Socket::SSL::Session_Cache->new(100);\n\n\tmy $s = http_get(\n\t\t'/', start => 1,\n\t\tSSL => 1,\n\t\tSSL_session_cache => $cache,\n\t\tSSL_session_key => 1\n\t);\n\n\treturn unless $s;\n\thttp_end($s);\n\n\tmy $sess = $cache->get_session(1);\n\treturn '' unless defined $sess;\n\treturn Net::SSLeay::i2d_SSL_SESSION($sess);\n}\n\nsub test_tls13 {\n\treturn http_get('/', SSL => 1) =~ /TLSv1.3/;\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/ssl_sni.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n# (C) Valentin Bartenev\n\n# Tests for Server Name Indication (SNI) TLS extension\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http http_ssl sni rewrite socket_ssl_sni/)\n\t->has_daemon('openssl')->plan(8)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    server {\n        listen       127.0.0.1:8443 ssl;\n        server_name  localhost;\n\n        ssl_certificate_key localhost.key;\n        ssl_certificate localhost.crt;\n\n        location / {\n            return 200 $server_name:$ssl_server_name;\n        }\n\n        location /protocol {\n            return 200 $ssl_protocol;\n        }\n        location /name {\n            return 200 $ssl_session_reused:$ssl_server_name;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8443;\n        server_name  example.com;\n\n        ssl_certificate_key example.com.key;\n        ssl_certificate example.com.crt;\n\n        location / {\n\n\n\n            return 200 $server_name:$ssl_server_name;\n        }\n    }\n}\n\nEOF\n\n\n\n\n\n$t->write_file('openssl.conf', <<EOF);\n[ req ]\ndefault_bits = 2048\nencrypt_key = no\ndistinguished_name = req_distinguished_name\n[ req_distinguished_name ]\nEOF\n\nmy $d = $t->testdir();\n\nforeach my $name ('localhost', 'example.com') {\n\tsystem('openssl req -x509 -new '\n\t\t. \"-config $d/openssl.conf -subj /CN=$name/ \"\n\t\t. \"-out $d/$name.crt -keyout $d/$name.key \"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create certificate for $name: $!\\n\";\n}\n\n$t->run();\n\n###############################################################################\n\nlike(get_cert_cn(), qr!/CN=localhost!, 'default cert');\nlike(get_cert_cn('example.com'), qr!/CN=example.com!, 'sni cert');\n\nlike(get_host('example.com'), qr!example.com:example.com!,\n\t'host exists, sni exists, and host is equal sni');\n\nlike(get_host('example.com', 'example.org'), qr!example.com:example.org!,\n\t'host exists, sni not found');\n\nTODO: {\nlocal $TODO = 'sni restrictions';\n\nlike(get_host('example.com', 'localhost'), qr!400 Bad Request!,\n\t'host exists, sni exists, and host is not equal sni');\n\nlike(get_host('example.org', 'example.com'), qr!400 Bad Request!,\n\t'host not found, sni exists');\n\n}\n\n# $ssl_server_name in sessions\n\nmy $ctx = new IO::Socket::SSL::SSL_Context(\n\tSSL_verify_mode => IO::Socket::SSL::SSL_VERIFY_NONE(),\n\tSSL_session_cache_size => 100);\n\nlike(get('/name', 'localhost', $ctx), qr/^\\.:localhost$/m, 'ssl server name');\n\nTODO: {\nlocal $TODO = 'no TLSv1.3 sessions, old Net::SSLeay'\n\tif $Net::SSLeay::VERSION < 1.88 && test_tls13();\nlocal $TODO = 'no TLSv1.3 sessions, old IO::Socket::SSL'\n\tif $IO::Socket::SSL::VERSION < 2.061 && test_tls13();\nlocal $TODO = 'no TLSv1.3 sessions in LibreSSL'\n\tif $t->has_module('LibreSSL') && test_tls13();\n\nlike(get('/name', 'localhost', $ctx), qr/^r:localhost$/m,\n\t'ssl server name - reused');\n\n}\n\n###############################################################################\n\nsub test_tls13 {\n\tget('/protocol', 'localhost') =~ /TLSv1.3/;\n\n\n\n}\n\nsub get_cert_cn {\n\tmy ($host) = @_;\n\tmy $s = http('', start => 1, SSL => 1, SSL_hostname => $host);\n\n\treturn $s->dump_peer_certificate();\n}\n\nsub get_host {\n\tmy ($host, $sni) = @_;\n\n\treturn http(\n\t\t\"GET / HTTP/1.0\\nHost: $host\\n\\n\",\n\t\tSSL => 1,\n\t\tSSL_hostname => $sni || $host\n\t);\n}\n\nsub get {\n\tmy ($uri, $host, $ctx) = @_;\n\treturn http_get(\n\t\t$uri,\n\t\tSSL => 1,\n\t\tSSL_hostname => $host,\n\t\tSSL_reuse_ctx => $ctx\n\t);\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/ssl_sni_reneg.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for http ssl module with SNI and renegotiation.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse Socket qw/ CRLF /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx qw/ :DEFAULT http_end /;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\n\n\nmy $t = Test::Nginx->new()->has(qw/http http_ssl socket_ssl_sni/)\n\t->has_daemon('openssl')->plan(8);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    ssl_certificate_key localhost.key;\n    ssl_certificate localhost.crt;\n    ssl_protocols TLSv1.2;\n\n    server {\n        listen       127.0.0.1:8443 ssl;\n        listen       127.0.0.1:8444 ssl;\n        server_name  localhost;\n\n        location / { }\n    }\n\n    server {\n        listen       127.0.0.1:8444 ssl;\n        server_name  localhost2;\n\n        location / { }\n    }\n}\n\nEOF\n\n$t->write_file('openssl.conf', <<EOF);\n[ req ]\ndefault_bits = 2048\nencrypt_key = no\ndistinguished_name = req_distinguished_name\n[ req_distinguished_name ]\nEOF\n\nmy $d = $t->testdir();\n\nforeach my $name ('localhost') {\n\tsystem('openssl req -x509 -new '\n\t\t. \"-config $d/openssl.conf -subj /CN=$name/ \"\n\t\t. \"-out $d/$name.crt -keyout $d/$name.key \"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create certificate for $name: $!\\n\";\n}\n\n$t->run();\n\n\n\n###############################################################################\n\nmy ($s, $ssl);\n$s = http('', start => 1, SSL => 1);\nok($s, 'connection');\n\nSKIP: {\nskip 'connection failed', 3 unless $s;\n\nlocal $SIG{PIPE} = 'IGNORE';\n\n$s->print('GET / HTTP/1.0' . CRLF);\n\n# Note: this uses IO::Socket::SSL::_get_ssl_object() internal method.\n# While not exactly correct, it looks like there is no other way to\n# trigger renegotiation with IO::Socket::SSL, and this seems to be\n# good enough for tests.\n$ssl = $s->_get_ssl_object();\nok(Net::SSLeay::renegotiate($ssl), 'renegotiation');\nok(Net::SSLeay::set_tlsext_host_name($ssl, 'localhost'), 'SNI');\n\n$s->print('Host: localhost' . CRLF . CRLF);\n\nok(!http_end($s), 'response');\n\n}\n\n# virtual servers\n\n$s = http('', start => 1, PeerAddr => '127.0.0.1:' . port(8444), SSL => 1);\nok($s, 'connection 2');\n\nSKIP: {\nskip 'connection failed', 3 unless $s;\n\nlocal $SIG{PIPE} = 'IGNORE';\n\n$s->print('GET / HTTP/1.0' . CRLF);\n\n# Note: this uses IO::Socket::SSL::_get_ssl_object() internal method.\n# While not exactly correct, it looks like there is no other way to\n# trigger renegotiation with IO::Socket::SSL, and this seems to be\n# good enough for tests.\n$ssl = $s->_get_ssl_object();\nok(Net::SSLeay::renegotiate($ssl), 'renegotiation');\nok(Net::SSLeay::set_tlsext_host_name($ssl, 'localhost'), 'SNI');\n\n$s->print('Host: localhost' . CRLF . CRLF);\n\nok(!http_end($s), 'virtual servers');\n\n}\n\n###############################################################################\n\n\n\n\n\n\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/ssl_sni_sessions.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n\n# Tests for SSL session resumption with SNI.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http http_ssl sni rewrite socket_ssl_sni/)\n\t->has_daemon('openssl')\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    ssl_certificate_key localhost.key;\n    ssl_certificate localhost.crt;\n\n    server {\n        listen       127.0.0.1:8443 ssl;\n        server_name  default;\n\n        ssl_session_tickets off;\n        ssl_session_cache shared:cache1:1m;\n\n        location / {\n            return 200 $ssl_server_name:$ssl_session_reused:$ssl_protocol;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8443;\n        server_name  nocache;\n\n        ssl_session_tickets off;\n        ssl_session_cache shared:cache2:1m;\n\n        location / {\n            return 200 $ssl_server_name:$ssl_session_reused;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8444 ssl;\n        server_name  default;\n\n        ssl_session_ticket_key ticket1.key;\n\n        location / {\n            return 200 $ssl_server_name:$ssl_session_reused;\n        }\n    }\n\n    server {\n        listen       127.0.0.1:8444;\n        server_name  tickets;\n\n        ssl_session_ticket_key ticket2.key;\n\n        location / {\n            return 200 $ssl_server_name:$ssl_session_reused;\n        }\n    }\n}\n\nEOF\n\n\n\n\n$t->write_file('openssl.conf', <<EOF);\n[ req ]\ndefault_bits = 2048\nencrypt_key = no\ndistinguished_name = req_distinguished_name\n[ req_distinguished_name ]\nEOF\n\nmy $d = $t->testdir();\n\nforeach my $name ('localhost') {\n\tsystem('openssl req -x509 -new '\n\t\t. \"-config $d/openssl.conf -subj /CN=$name/ \"\n\t\t. \"-out $d/$name.crt -keyout $d/$name.key \"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create certificate for $name: $!\\n\";\n}\n\n$t->write_file('ticket1.key', '1' x 48);\n$t->write_file('ticket2.key', '2' x 48);\n\n$t->run();\n\nplan(skip_all => 'no TLSv1.3 sessions, old Net::SSLeay')\n\tif $Net::SSLeay::VERSION < 1.88 && test_tls13();\nplan(skip_all => 'no TLSv1.3 sessions, old IO::Socket::SSL')\n\tif $IO::Socket::SSL::VERSION < 2.061 && test_tls13();\nplan(skip_all => 'no TLSv1.3 sessions in LibreSSL')\n        if $t->has_module('LibreSSL') && test_tls13();\nplan(skip_all => 'no TLS 1.3 session cache in BoringSSL')\n\tif $t->has_module('BoringSSL') && test_tls13();\n\n$t->plan(6);\n\n###############################################################################\n\n# check that everything works fine with default server\n\nmy $ctx = get_ssl_context();\n\nlike(get('default', 8443, $ctx), qr!default:\\.!, 'default server');\nlike(get('default', 8443, $ctx), qr!default:r!, 'default server reused');\n\n# check that sessions are still properly saved and restored\n# when using an SNI-based virtual server with different session cache;\n# as session resumption happens before SNI, only default server\n# settings are expected to matter\n\n# this didn't work before nginx 1.9.6 (and caused segfaults if no session\n# cache was configured the SNI-based virtual server), because OpenSSL, when\n# creating new sessions, uses callbacks from the default server context, but\n# provides access to the SNI-selected server context only (ticket #235)\n\n$ctx = get_ssl_context();\n\nlike(get('nocache', 8443, $ctx), qr!nocache:\\.!, 'without cache');\nlike(get('nocache', 8443, $ctx), qr!nocache:r!, 'without cache reused');\n\n# make sure tickets can be used if an SNI-based virtual server\n# uses a different set of session ticket keys explicitly set\n\n$ctx = get_ssl_context();\n\nlike(get('tickets', 8444, $ctx), qr!tickets:\\.!, 'tickets');\nlike(get('tickets', 8444, $ctx), qr!tickets:r!, 'tickets reused');\n\n###############################################################################\n\nsub get_ssl_context {\n\treturn IO::Socket::SSL::SSL_Context->new(\n\t\tSSL_verify_mode => IO::Socket::SSL::SSL_VERIFY_NONE(),\n\t\tSSL_session_cache_size => 100\n\t);\n}\n\nsub get {\n\tmy ($host, $port, $ctx) = @_;\n\treturn http(\n\t\t\"GET / HTTP/1.0\\nHost: $host\\n\\n\",\n\t\tPeerAddr => '127.0.0.1:' . port($port),\n\t\tSSL => 1,\n\t\t\tSSL_hostname => $host,\n\t\tSSL_reuse_ctx => $ctx\n\t\t);\n\n\n}\n\nsub test_tls13 {\n\treturn get('default', 8443) =~ /TLSv1.3/;\n\n\n\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/ssl_stapling.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for OCSP stapling.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse MIME::Base64 qw/ decode_base64 /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/http http_ssl socket_ssl/)\n\t->has_daemon('openssl');\n\neval { defined &Net::SSLeay::set_tlsext_status_type or die; };\nplan(skip_all => 'Net::SSLeay too old') if $@;\neval { defined &IO::Socket::SSL::SSL_OCSP_TRY_STAPLE or die; };\nplan(skip_all => 'IO::Socket::SSL too old') if $@;\n\nplan(skip_all => 'no OCSP stapling') if $t->has_module('BoringSSL');\n\n$t->plan(10)->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    ssl_stapling on;\n    ssl_trusted_certificate trusted.crt;\n\n    ssl_certificate ec-end-int.crt;\n    ssl_certificate_key ec-end.key;\n\n    ssl_certificate end-int.crt;\n    ssl_certificate_key end.key;\n\n    ssl_ciphers DEFAULT:ECCdraft;\n    add_header X-SSL-Protocol $ssl_protocol always;\n\n    server {\n        listen       127.0.0.1:8443 ssl;\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n    }\n\n    server {\n        listen       127.0.0.1:8444 ssl;\n        server_name  localhost;\n\n        ssl_stapling_responder http://127.0.0.1:8081/;\n    }\n\n    server {\n        listen       127.0.0.1:8445 ssl;\n        server_name  localhost;\n\n        ssl_stapling_verify on;\n    }\n\n    server {\n        listen       127.0.0.1:8446 ssl;\n        server_name  localhost;\n\n        ssl_certificate ec-end.crt;\n        ssl_certificate_key ec-end.key;\n    }\n\n    server {\n        listen       127.0.0.1:8447 ssl;\n        server_name  localhost;\n\n        ssl_certificate end-int.crt;\n        ssl_certificate_key end.key;\n\n        ssl_stapling_file %%TESTDIR%%/resp.der;\n    }\n\n    server {\n        listen       127.0.0.1:8448 ssl;\n        server_name  localhost;\n\n        ssl_certificate ec-end-int.crt;\n        ssl_certificate_key ec-end.key;\n\n        ssl_stapling_file %%TESTDIR%%/ec-resp.der;\n    }\n\n    server {\n        listen       127.0.0.1:8449 ssl;\n        server_name  localhost;\n\n        ssl_stapling_responder http://127.0.0.1:8080/;\n    }\n}\n\nEOF\n\nmy $d = $t->testdir();\nmy $p = port(8081);\n\n$t->write_file('openssl.conf', <<EOF);\n[ req ]\ndefault_bits = 2048\nencrypt_key = no\ndistinguished_name = req_distinguished_name\n[ req_distinguished_name ]\nEOF\n\n$t->write_file('ca.conf', <<EOF);\n[ ca ]\ndefault_ca = myca\n\n[ myca ]\nnew_certs_dir = $d\ndatabase = $d/certindex\ndefault_md = sha256\npolicy = myca_policy\nserial = $d/certserial\ndefault_days = 1\nx509_extensions = myca_extensions\n\n[ myca_policy ]\ncommonName = supplied\n\n[ myca_extensions ]\nbasicConstraints = critical,CA:TRUE\nauthorityInfoAccess = OCSP;URI:http://127.0.0.1:$p\nEOF\n\nforeach my $name ('root') {\n\tsystem('openssl req -x509 -new '\n\t\t. \"-config $d/openssl.conf -subj /CN=$name/ \"\n\t\t. \"-out $d/$name.crt -keyout $d/$name.key \"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create certificate for $name: $!\\n\";\n}\n\nforeach my $name ('int', 'end') {\n\tsystem(\"openssl req -new \"\n\t\t. \"-config $d/openssl.conf -subj /CN=$name/ \"\n\t\t. \"-out $d/$name.csr -keyout $d/$name.key \"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create certificate for $name: $!\\n\";\n}\n\nforeach my $name ('ec-end') {\n\tsystem(\"openssl ecparam -genkey -out $d/$name.key -name prime256v1 \"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create EC param: $!\\n\";\n\tsystem(\"openssl req -new -key $d/$name.key \"\n\t\t. \"-config $d/openssl.conf -subj /CN=$name/ \"\n\t\t. \"-out $d/$name.csr \"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create certificate for $name: $!\\n\";\n}\n\n$t->write_file('certserial', '1000');\n$t->write_file('certindex', '');\n\nsystem(\"openssl ca -batch -config $d/ca.conf \"\n\t. \"-keyfile $d/root.key -cert $d/root.crt \"\n\t. \"-subj /CN=int/ -in $d/int.csr -out $d/int.crt \"\n\t. \">>$d/openssl.out 2>&1\") == 0\n\tor die \"Can't sign certificate for int: $!\\n\";\n\nsystem(\"openssl ca -batch -config $d/ca.conf \"\n\t. \"-keyfile $d/int.key -cert $d/int.crt \"\n\t. \"-subj /CN=ec-end/ -in $d/ec-end.csr -out $d/ec-end.crt \"\n\t. \">>$d/openssl.out 2>&1\") == 0\n\tor die \"Can't sign certificate for ec-end: $!\\n\";\n\nsystem(\"openssl ca -batch -config $d/ca.conf \"\n\t. \"-keyfile $d/int.key -cert $d/int.crt \"\n\t. \"-subj /CN=end/ -in $d/end.csr -out $d/end.crt \"\n\t. \">>$d/openssl.out 2>&1\") == 0\n\tor die \"Can't sign certificate for end: $!\\n\";\n\n# RFC 6960, serialNumber\n\nsystem(\"openssl x509 -in $d/end.crt -serial -noout \"\n\t. \">>$d/serial 2>>$d/openssl.out\") == 0\n\tor die \"Can't obtain serial for end: $!\\n\";\n\nmy $serial = pack(\"n2\", 0x0202, hex $1) if $t->read_file('serial') =~ /(\\d+)/;\n\nsystem(\"openssl ca -config $d/ca.conf -revoke $d/end.crt \"\n\t. \"-keyfile $d/root.key -cert $d/root.crt \"\n\t. \">>$d/openssl.out 2>&1\") == 0\n\tor die \"Can't revoke end.crt: $!\\n\";\n\nsystem(\"openssl ocsp -issuer $d/int.crt -cert $d/end.crt \"\n\t. \"-reqout $d/req.der >>$d/openssl.out 2>&1\") == 0\n\tor die \"Can't create OCSP request: $!\\n\";\n\nsystem(\"openssl ocsp -index $d/certindex -CA $d/int.crt \"\n\t. \"-rsigner $d/root.crt -rkey $d/root.key \"\n\t. \"-reqin $d/req.der -respout $d/resp.der -ndays 1 \"\n\t. \">>$d/openssl.out 2>&1\") == 0\n\tor die \"Can't create OCSP response: $!\\n\";\n\nsystem(\"openssl ocsp -issuer $d/int.crt -cert $d/ec-end.crt \"\n\t. \"-reqout $d/ec-req.der >>$d/openssl.out 2>&1\") == 0\n\tor die \"Can't create EC OCSP request: $!\\n\";\n\nsystem(\"openssl ocsp -index $d/certindex -CA $d/int.crt \"\n\t. \"-rsigner $d/root.crt -rkey $d/root.key \"\n\t. \"-reqin $d/ec-req.der -respout $d/ec-resp.der -ndays 1 \"\n\t. \">>$d/openssl.out 2>&1\") == 0\n\tor die \"Can't create EC OCSP response: $!\\n\";\n\n$t->write_file('trusted.crt',\n\t$t->read_file('int.crt') . $t->read_file('root.crt'));\n$t->write_file('end-int.crt',\n\t$t->read_file('end.crt') . $t->read_file('int.crt'));\n$t->write_file('ec-end-int.crt',\n\t$t->read_file('ec-end.crt') . $t->read_file('int.crt'));\n\n$t->run_daemon(\\&http_daemon, $t);\n$t->run();\n\n$t->waitforsocket(\"127.0.0.1:\" . port(8081));\n\n###############################################################################\n\n\nstaple(8443, 'RSA');\nstaple(8443, 'ECDSA');\nstaple(8444, 'RSA');\nstaple(8444, 'ECDSA');\nstaple(8445, 'ECDSA');\nstaple(8446, 'ECDSA');\nstaple(8449, 'ECDSA');\n\nsleep 1;\n\nok(!staple(8443, 'RSA'), 'staple revoked');\nTODO: {\nlocal $TODO = 'broken TLSv1.3 sigalgs in LibreSSL'\n\tif $t->has_module('LibreSSL') && test_tls13();\nok(staple(8443, 'ECDSA'), 'staple success');\n\n}\nok(!staple(8444, 'RSA'), 'responder revoked');\nTODO: {\nlocal $TODO = 'broken TLSv1.3 sigalgs in LibreSSL'\n\tif $t->has_module('LibreSSL') && test_tls13();\nok(staple(8444, 'ECDSA'), 'responder success');\n\n}\nok(!staple(8445, 'ECDSA'), 'verify - root not trusted');\n\nok(staple(8446, 'ECDSA', \"$d/int.crt\"), 'cert store');\n\nis(staple(8447, 'RSA'), '1 1', 'file revoked');\nis(staple(8448, 'ECDSA'), '1 0', 'file success');\n\nok(!staple(8449, 'ECDSA'), 'ocsp error');\n\nTODO: {\nlocal $TODO = 'broken TLSv1.3 sigalgs in LibreSSL'\n\tif $t->has_module('LibreSSL') && test_tls13();\nlike(`grep -F '[crit]' ${\\($t->testdir())}/error.log`, qr/^$/s, 'no crit');\n}\n###############################################################################\n\nsub staple {\n\tmy ($port, $ciphers, $ca) = @_;\n\tmy (@resp);\n\n\tmy $staple_cb = sub {\n\t\tmy ($s, $resp) = @_;\n\t\tpush @resp, !!$resp;\n\t\treturn 1 unless $resp;\n\t\t# Contrary to the documentation, IO::Socket::SSL calls the\n\t\t# SSL_ocsp_staple_callback with the socket, and not the\n\t\t# Net::SSLeay object.\n\t\tmy $ssl = $s->_get_ssl_object();\n\t\tmy $cert = Net::SSLeay::get_peer_certificate($ssl);\n\t\tmy $certid = eval { Net::SSLeay::OCSP_cert2ids($ssl, $cert) }\n\t\t\tor do { die \"no OCSP_CERTID for certificate: $@\"; };\n\n\t\tmy @res = Net::SSLeay::OCSP_response_results($resp, $certid);\n\t\tpush @resp, $res[0][2]->{'statusType'};\n\t};\n\n\tmy $ctx_cb = sub {\n\t\tmy $ctx = shift;\n\n\n\t\treturn unless defined $ciphers;\n\n\n\tmy $ssleay = Net::SSLeay::SSLeay();\n\t\treturn if ($ssleay < 0x1000200f || $ssleay == 0x20000000);\n\t\tmy @sigalgs = ('RSA+SHA256:PSS+SHA256', 'RSA+SHA256');\n\t\t@sigalgs = ($ciphers . '+SHA256') unless $ciphers eq 'RSA';\n\t\t# SSL_CTRL_SET_SIGALGS_LIST\n\t\tNet::SSLeay::CTX_ctrl($ctx, 98, 0, $sigalgs[0])\n\t\t\tor Net::SSLeay::CTX_ctrl($ctx, 98, 0, $sigalgs[1])\n\t\t\tor die(\"Failed to set sigalgs\");\n\t};\n\n\tmy $s = http_get(\n\t\t'/', start => 1, PeerAddr => '127.0.0.1:' . port($port),\n\t\tSSL => 1,\n\t\tSSL_cipher_list => $ciphers,\n\t\tSSL_create_ctx_callback => $ctx_cb,\n\t\tSSL_ocsp_staple_callback => $staple_cb,\n\t\tSSL_ocsp_mode => IO::Socket::SSL::SSL_OCSP_TRY_STAPLE(),\n\t\tSSL_ca_file => $ca\n\t);\n\n\treturn $s unless $s;\n\treturn join ' ', @resp;\n}\n\nsub test_tls13 {\n\treturn http_get('/', SSL => 1) =~ /TLSv1.3/;\n\n\n\n\n}\n\n###############################################################################\n\nsub http_daemon {\n\tmy ($t) = shift;\n\tmy $server = IO::Socket::INET->new(\n\t\tProto => 'tcp',\n\t\tLocalHost => \"127.0.0.1:\" . port(8081),\n\t\tListen => 5,\n\t\tReuse => 1\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\tlocal $SIG{PIPE} = 'IGNORE';\n\n\twhile (my $client = $server->accept()) {\n\t\t$client->autoflush(1);\n\n\t\tmy $headers = '';\n\t\tmy $uri = '';\n\n\t\twhile (<$client>) {\n\t\t\t$headers .= $_;\n\t\t\tlast if (/^\\x0d?\\x0a?$/);\n\t\t}\n\n\t\t$uri = $1 if $headers =~ /^\\S+\\s+\\/([^ ]+)\\s+HTTP/i;\n\t\tnext unless $uri;\n\n\t\t$uri =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg;\n\t\tmy $req = decode_base64($uri);\n\t\tmy $resp = index($req, $serial) > 0 ? 'resp' : 'ec-resp';\n\n\t\t# ocsp dummy handler\n\n\t\tselect undef, undef, undef, 0.02;\n\n\t\t$headers = <<\"EOF\";\nHTTP/1.1 200 OK\nConnection: close\nContent-Type: application/ocsp-response\n\nEOF\n\n\t\tlocal $/;\n\t\topen my $fh, '<', \"$d/$resp.der\"\n\t\t\tor die \"Can't open $resp.der: $!\";\n\t\tbinmode $fh;\n\t\tmy $content = <$fh>;\n\t\tclose $fh;\n\n\t\tprint $client $headers . $content;\n\t}\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/ssl_verify_client.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for http ssl module, ssl_verify_client.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nuse Socket qw/ CRLF /;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx qw/ :DEFAULT http_end /;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\n\n\nmy $t = Test::Nginx->new()->has(qw/http http_ssl sni socket_ssl_sni/)\n\t->has_daemon('openssl')->plan(13);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    add_header X-Verify x$ssl_client_verify:${ssl_client_cert}x;\n    add_header X-Protocol $ssl_protocol;\n\n    ssl_session_cache shared:SSL:1m;\n    ssl_session_tickets off;\n\n    server {\n        listen       127.0.0.1:8080;\n        server_name  localhost;\n\n        ssl_certificate_key 1.example.com.key;\n        ssl_certificate 1.example.com.crt;\n\n        ssl_verify_client on;\n        ssl_client_certificate 2.example.com.crt;\n    }\n\n    server {\n        listen       127.0.0.1:8443 ssl;\n        server_name  on;\n\n        ssl_certificate_key 1.example.com.key;\n        ssl_certificate 1.example.com.crt;\n\n        ssl_verify_client on;\n        ssl_client_certificate 2.example.com.crt;\n    }\n\n    server {\n        listen       127.0.0.1:8443 ssl;\n        server_name  optional;\n\n        ssl_certificate_key 1.example.com.key;\n        ssl_certificate 1.example.com.crt;\n\n        ssl_verify_client optional;\n        ssl_client_certificate 2.example.com.crt;\n        ssl_trusted_certificate 3.example.com.crt;\n    }\n\n    server {\n        listen       127.0.0.1:8443 ssl;\n        server_name  off;\n\n        ssl_certificate_key 1.example.com.key;\n        ssl_certificate 1.example.com.crt;\n\n        ssl_verify_client off;\n        ssl_client_certificate 2.example.com.crt;\n        ssl_trusted_certificate 3.example.com.crt;\n    }\n\n    server {\n        listen       127.0.0.1:8443 ssl;\n        server_name  optional.no.ca;\n\n        ssl_certificate_key 1.example.com.key;\n        ssl_certificate 1.example.com.crt;\n\n        ssl_verify_client optional_no_ca;\n        ssl_client_certificate 2.example.com.crt;\n    }\n\n    server {\n        listen       127.0.0.1:8443 ssl;\n        server_name  no.context;\n\n        ssl_verify_client on;\n    }\n}\n\nEOF\n\n$t->write_file('openssl.conf', <<EOF);\n[ req ]\ndefault_bits = 2048\nencrypt_key = no\ndistinguished_name = req_distinguished_name\n[ req_distinguished_name ]\nEOF\n\nmy $d = $t->testdir();\n\nforeach my $name ('1.example.com', '2.example.com', '3.example.com') {\n\tsystem('openssl req -x509 -new '\n\t\t. \"-config $d/openssl.conf -subj /CN=$name/ \"\n\t\t. \"-out $d/$name.crt -keyout $d/$name.key \"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create certificate for $name: $!\\n\";\n}\n\nsleep 1 if $^O eq 'MSWin32';\n\n$t->write_file('t', 'SEE-THIS');\n\n$t->run();\n\n###############################################################################\n\nlike(http_get('/t'), qr/x:x/, 'plain connection');\nlike(get('on'), qr/400 Bad Request/, 'no cert');\nlike(get('no.context'), qr/400 Bad Request/, 'no server cert');\nlike(get('optional'), qr/NONE:x/, 'no optional cert');\nlike(get('optional', '1.example.com'), qr/400 Bad/, 'bad optional cert');\nlike(get('optional.no.ca', '1.example.com'), qr/FAILED.*BEGIN/,\n\t'bad optional_no_ca cert');\nlike(get('off', '2.example.com'), qr/NONE/, 'off cert');\nlike(get('off', '3.example.com'), qr/NONE/, 'off cert trusted');\n\nlike(get('localhost', '2.example.com'), qr/SUCCESS.*BEGIN/, 'good cert');\nlike(get('optional', '2.example.com'), qr/SUCCESS.*BEGI/, 'good cert optional');\nlike(get('optional', '3.example.com'), qr/SUCCESS.*BEGIN/, 'good cert trusted');\n\nSKIP: {\nskip 'Net::SSLeay version >= 1.36 required', 1 if $Net::SSLeay::VERSION < 1.36;\n\nTODO: {\nlocal $TODO = 'broken TLSv1.3 CA list in LibreSSL'\n\tif $t->has_module('LibreSSL') && test_tls13();\nmy $ca = join ' ', get('optional', '3.example.com');\nis($ca, '/CN=2.example.com', 'no trusted sent');\n\n}\n}\n\nlike(get('optional', undef, 'localhost'), qr/421 Misdirected/, 'misdirected');\n\n###############################################################################\n\nsub test_tls13 {\n\tget('optional') =~ /TLSv1.3/;\n}\nsub get {\n\tmy ($sni, $cert, $host) = @_;\n\n\n\t$host = $sni if !defined $host;\n\n\tmy $s = http(\n\t\t\"GET /t HTTP/1.0\" . CRLF .\n\t\t\"Host: $host\" . CRLF . CRLF,\n\t\tstart => 1,\n\t\tSSL => 1,\n\t\tSSL_hostname => $sni,\n\t\t$cert ? (\n\t\tSSL_cert_file => \"$d/$cert.crt\",\n\t\tSSL_key_file => \"$d/$cert.key\"\n\t\t) : ()\n\t);\n\n\treturn http_end($s) unless wantarray();\n\n\t# Note: this uses IO::Socket::SSL::_get_ssl_object() internal method.\n\t# While not exactly correct, it looks like there is no other way to\n\t# obtain CA list with IO::Socket::SSL, and this seems to be good\n\t# enough for tests.\n\tmy $ssl = $s->_get_ssl_object();\n\tmy $list = Net::SSLeay::get_client_CA_list($ssl);\n\tmy @names;\n\tfor my $i (0 .. Net::SSLeay::sk_X509_NAME_num($list) - 1) {\n\t\tmy $name = Net::SSLeay::sk_X509_NAME_value($list, $i);\n\t\tpush @names, Net::SSLeay::X509_NAME_oneline($name);\n\t}\n\treturn @names;\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/ssl_verify_depth.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Tests for http ssl module, ssl_verify_depth.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\n\nmy $t = Test::Nginx->new()->has(qw/http http_ssl socket_ssl/)\n\t->has_daemon('openssl');\n\nplan(skip_all => 'LibreSSL') if $t->has_module('LibreSSL');\n\n$t->plan(9)->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nhttp {\n    %%TEST_GLOBALS_HTTP%%\n\n    ssl_certificate localhost.crt;\n    ssl_certificate_key localhost.key;\n\n    ssl_verify_client on;\n    ssl_client_certificate root-int.crt;\n\n    add_header X-Client $ssl_client_s_dn always;\n    add_header X-Verify $ssl_client_verify always;\n\n    server {\n        listen       127.0.0.1:8080 ssl;\n        server_name  localhost;\n        ssl_verify_depth 0;\n    }\n\n    server {\n        listen       127.0.0.1:8081 ssl;\n        server_name  localhost;\n        ssl_verify_depth 1;\n    }\n\n    server {\n        listen       127.0.0.1:8082 ssl;\n        server_name  localhost;\n        ssl_verify_depth 2;\n    }\n}\n\nEOF\n\nmy $d = $t->testdir();\n\n$t->write_file('openssl.conf', <<EOF);\n[ req ]\ndefault_bits = 2048\nencrypt_key = no\ndistinguished_name = req_distinguished_name\n[ req_distinguished_name ]\nEOF\n\n$t->write_file('ca.conf', <<EOF);\n[ ca ]\ndefault_ca = myca\n\n[ myca ]\nnew_certs_dir = $d\ndatabase = $d/certindex\ndefault_md = sha256\npolicy = myca_policy\nserial = $d/certserial\ndefault_days = 1\nx509_extensions = myca_extensions\n\n[ myca_policy ]\ncommonName = supplied\n\n[ myca_extensions ]\nbasicConstraints = critical,CA:TRUE\nEOF\n\nforeach my $name ('root', 'localhost') {\n\tsystem('openssl req -x509 -new '\n\t\t. \"-config $d/openssl.conf -subj /CN=$name/ \"\n\t\t. \"-out $d/$name.crt -keyout $d/$name.key \"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create certificate for $name: $!\\n\";\n}\n\nforeach my $name ('int', 'end') {\n\tsystem(\"openssl req -new \"\n\t\t. \"-config $d/openssl.conf -subj /CN=$name/ \"\n\t\t. \"-out $d/$name.csr -keyout $d/$name.key \"\n\t\t. \">>$d/openssl.out 2>&1\") == 0\n\t\tor die \"Can't create certificate for $name: $!\\n\";\n}\n\n$t->write_file('certserial', '1000');\n$t->write_file('certindex', '');\n\nsystem(\"openssl ca -batch -config $d/ca.conf \"\n\t. \"-keyfile $d/root.key -cert $d/root.crt \"\n\t. \"-subj /CN=int/ -in $d/int.csr -out $d/int.crt \"\n\t. \">>$d/openssl.out 2>&1\") == 0\n\tor die \"Can't sign certificate for int: $!\\n\";\n\nsystem(\"openssl ca -batch -config $d/ca.conf \"\n\t. \"-keyfile $d/int.key -cert $d/int.crt \"\n\t. \"-subj /CN=end/ -in $d/end.csr -out $d/end.crt \"\n\t. \">>$d/openssl.out 2>&1\") == 0\n\tor die \"Can't sign certificate for end: $!\\n\";\n\n$t->write_file('root-int.crt', $t->read_file('root.crt')\n\t. $t->read_file('int.crt'));\n\n$t->write_file('t', '');\n$t->run();\n\n###############################################################################\n\n# with verify depth 0, only self-signed certificates should\n# be allowed\n\n# OpenSSL 1.1.0+ instead limits the number of intermediate certs allowed;\n# as a result, it is not possible to limit certificate checking\n# to self-signed certificates only when using OpenSSL 1.1.0+\n\nlike(get(8080, 'root'), qr/SUCCESS/, 'verify depth 0 - root');\nlike(get(8080, 'int'),  qr/FAI|SUC/, 'verify depth 0 - no int');\nlike(get(8080, 'end'),  qr/FAILED/,  'verify depth 0 - no end');\n\n# with verify depth 1 (the default), one signature is\n# expected to be checked, so certificates directly signed\n# by the root cert are allowed, but nothing more\n\n# OpenSSL 1.1.0+ instead limits the number of intermediate certs allowed;\n# so with depth 1 it is possible to validate not only directly signed\n# certificates, but also chains with one intermediate certificate\n\nlike(get(8081, 'root'), qr/SUCCESS/, 'verify depth 1 - root');\nlike(get(8081, 'int'),  qr/SUCCESS/, 'verify depth 1 - int');\nlike(get(8081, 'end'),  qr/FAI|SUC/, 'verify depth 1 - no end');\n\n# with verify depth 2 it is also possible to validate up to two signatures,\n# so chains with one intermediate certificate are allowed\n\nlike(get(8082, 'root'), qr/SUCCESS/, 'verify depth 2 - root');\nlike(get(8082, 'int'),  qr/SUCCESS/, 'verify depth 2 - int');\nlike(get(8082, 'end'),  qr/SUCCESS/, 'verify depth 2 - end');\n\n###############################################################################\n\nsub get {\n\tmy ($port, $cert) = @_;\n\thttp_get(\n\t\t\"/t?$cert\",\n\n\t\tPeerAddr => '127.0.0.1:' . port($port),\n\t\tSSL => 1,\n\t\t\tSSL_cert_file => \"$d/$cert.crt\",\n\t\tSSL_key_file => \"$d/$cert.key\"\n\t\t);\n\n\n}\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/stream_access.t",
    "content": "#!/usr/bin/perl\n\n# (C) Andrey Zelenkov\n# (C) Nginx, Inc.\n\n# Tests for stream access module.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse Test::Nginx::Stream qw/ stream /;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/stream stream_access unix/);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nstream {\n    %%TEST_GLOBALS_STREAM%%\n\n    server {\n        listen       127.0.0.1:8082;\n        proxy_pass   [::1]:%%PORT_8080%%;\n    }\n\n    server {\n        listen       127.0.0.1:8083;\n        proxy_pass   unix:%%TESTDIR%%/unix.sock.0;\n    }\n\n    server {\n        listen       127.0.0.1:8085;\n        proxy_pass   [::1]:%%PORT_8081%%;\n    }\n\n    server {\n        listen       127.0.0.1:8086;\n        proxy_pass   unix:%%TESTDIR%%/unix.sock.1;\n    }\n\n    server {\n        listen       127.0.0.1:8088;\n        proxy_pass   [::1]:%%PORT_8082%%;\n    }\n\n    server {\n        listen       127.0.0.1:8089;\n        proxy_pass   unix:%%TESTDIR%%/unix.sock.2;\n    }\n\n    server {\n        listen       127.0.0.1:8091;\n        proxy_pass   [::1]:%%PORT_8083%%;\n    }\n\n    server {\n        listen       127.0.0.1:8092;\n        proxy_pass   unix:%%TESTDIR%%/unix.sock.3;\n    }\n\n    server {\n        listen       127.0.0.1:8094;\n        proxy_pass   [::1]:%%PORT_8084%%;\n    }\n\n    server {\n        listen       127.0.0.1:8095;\n        proxy_pass   unix:%%TESTDIR%%/unix.sock.4;\n    }\n\n    server {\n        listen       127.0.0.1:8097;\n        proxy_pass   [::1]:%%PORT_8085%%;\n    }\n\n    server {\n        listen       127.0.0.1:8098;\n        proxy_pass   unix:%%TESTDIR%%/unix.sock.5;\n    }\n\n    server {\n        listen       127.0.0.1:8081;\n        listen       [::1]:%%PORT_8080%%;\n        listen       unix:%%TESTDIR%%/unix.sock.0;\n        proxy_pass   127.0.0.1:8080;\n        allow        all;\n    }\n\n    server {\n        listen       127.0.0.1:8084;\n        listen       [::1]:%%PORT_8081%%;\n        listen       unix:%%TESTDIR%%/unix.sock.1;\n        proxy_pass   127.0.0.1:8080;\n        deny         all;\n    }\n\n    server {\n        listen       127.0.0.1:8087;\n        listen       [::1]:%%PORT_8082%%;\n        listen       unix:%%TESTDIR%%/unix.sock.2;\n        proxy_pass   127.0.0.1:8080;\n        allow        unix:;\n    }\n\n    server {\n        listen       127.0.0.1:8090;\n        listen       [::1]:%%PORT_8083%%;\n        listen       unix:%%TESTDIR%%/unix.sock.3;\n        proxy_pass   127.0.0.1:8080;\n        deny         127.0.0.1;\n    }\n\n    server {\n        listen       127.0.0.1:8093;\n        listen       [::1]:%%PORT_8084%%;\n        listen       unix:%%TESTDIR%%/unix.sock.4;\n        proxy_pass   127.0.0.1:8080;\n        deny         ::1;\n    }\n\n    server {\n        listen       127.0.0.1:8096;\n        listen       [::1]:%%PORT_8085%%;\n        listen       unix:%%TESTDIR%%/unix.sock.5;\n        proxy_pass   127.0.0.1:8080;\n        deny         unix:;\n    }\n}\n\nEOF\n\n$t->try_run('no inet6 support')->plan(18);\n$t->run_daemon(\\&stream_daemon);\n$t->waitforsocket('127.0.0.1:' . port(8080));\n\n###############################################################################\n\nmy $str = 'SEE-THIS';\n\n# allow all\n\nis(stream('127.0.0.1:' . port(8081))->io($str), $str, 'inet allow all');\nis(stream('127.0.0.1:' . port(8082))->io($str), $str, 'inet6 allow all');\nis(stream('127.0.0.1:' . port(8083))->io($str), $str, 'unix allow all');\n\n# deny all\n\nis(stream('127.0.0.1:' . port(8084))->io($str), '', 'inet deny all');\nis(stream('127.0.0.1:' . port(8085))->io($str), '', 'inet6 deny all');\nis(stream('127.0.0.1:' . port(8086))->io($str), '', 'unix deny all');\n\n# allow unix\n\nis(stream('127.0.0.1:' . port(8087))->io($str), $str, 'inet allow unix');\nis(stream('127.0.0.1:' . port(8088))->io($str), $str, 'inet6 allow unix');\nis(stream('127.0.0.1:' . port(8089))->io($str), $str, 'unix allow unix');\n\n# deny inet\n\nis(stream('127.0.0.1:' . port(8090))->io($str), '', 'inet deny inet');\nis(stream('127.0.0.1:' . port(8091))->io($str), $str, 'inet6 deny inet');\nis(stream('127.0.0.1:' . port(8092))->io($str), $str, 'unix deny inet');\n\n# deny inet6\n\nis(stream('127.0.0.1:' . port(8093))->io($str), $str, 'inet deny inet6');\nis(stream('127.0.0.1:' . port(8094))->io($str), '', 'inet6 deny inet6');\nis(stream('127.0.0.1:' . port(8095))->io($str), $str, 'unix deny inet6');\n\n# deny unix\n\nis(stream('127.0.0.1:' . port(8096))->io($str), $str, 'inet deny unix');\nis(stream('127.0.0.1:' . port(8097))->io($str), $str, 'inet6 deny unix');\nis(stream('127.0.0.1:' . port(8098))->io($str), '', 'unix deny unix');\n\n###############################################################################\n\nsub stream_daemon {\n\tmy $server = IO::Socket::INET->new(\n\t\tProto => 'tcp',\n\t\tLocalAddr => '127.0.0.1:' . port(8080),\n\t\tListen => 5,\n\t\tReuse => 1\n\t)\n\t\tor die \"Can't create listening socket: $!\\n\";\n\n\tlocal $SIG{PIPE} = 'IGNORE';\n\n\twhile (my $client = $server->accept()) {\n\t\t$client->autoflush(1);\n\n\t\tlog2c(\"(new connection $client)\");\n\n\t\t$client->sysread(my $buffer, 65536) or next;\n\n\t\tlog2i(\"$client $buffer\");\n\n\t\tlog2o(\"$client $buffer\");\n\n\t\t$client->syswrite($buffer);\n\n\t\tclose $client;\n\t}\n}\n\nsub log2i { Test::Nginx::log_core('|| <<', @_); }\nsub log2o { Test::Nginx::log_core('|| >>', @_); }\nsub log2c { Test::Nginx::log_core('||', @_); }\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/stream_access_log_escape.t",
    "content": "#!/usr/bin/perl\n\n# (C) Sergey Kandaurov\n# (C) Nginx, Inc.\n\n# Stream tests for access_log with escape parameter.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/stream stream_map stream_return/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nstream {\n    %%TEST_GLOBALS_STREAM%%\n\n    map $pid $a {\n        default '\" \\ \"';\n    }\n    map $pid $b {\n        default \"foo\";\n    }\n\n    log_format json     escape=json     $a$b$upstream_addr;\n    log_format default  escape=default  $a$b$upstream_addr;\n\n    server {\n        listen       127.0.0.1:8080;\n        return       ok;\n\n        access_log %%TESTDIR%%/json.log json;\n        access_log %%TESTDIR%%/test.log default;\n    }\n}\n\nEOF\n\n$t->run()->plan(2);\n\n###############################################################################\n\nhttp_get('/');\n\n$t->stop();\n\nis($t->read_file('json.log'), '\\\" \\\\\\\\ \\\"foo' . \"\\n\", 'json');\nis($t->read_file('test.log'), '\\x22 \\x5C \\x22foo-' . \"\\n\", 'default');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/stream_geo.t",
    "content": "#!/usr/bin/perl\n\n# (C) Maxim Dounin\n# (C) Sergey Kandaurov\n# (C) Andrey Zelenkov\n# (C) Nginx, Inc.\n\n# Tests for stream geo module.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse Test::Nginx::Stream qw/ stream /;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/stream stream_return stream_map stream_geo/);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nstream {\n    %%TEST_GLOBALS_STREAM%%\n\n    geo $geo {\n        127.0.0.0/8   loopback;\n        192.0.2.0/24  test;\n        0.0.0.0/0     world;\n    }\n\n    geo $geo_include {\n        include       geo.conf;\n        192.0.2.0/24  test;\n        0.0.0.0/0     world;\n    }\n\n    geo $geo_delete {\n        127.0.0.0/8   loopback;\n        192.0.2.0/24  test;\n        0.0.0.0/0     world;\n        delete        127.0.0.0/8;\n    }\n\n    geo $remote_addr $geo_from_addr {\n        127.0.0.0/8   loopback;\n        192.0.2.0/24  test;\n    }\n\n    map $server_port $var {\n        %%PORT_8080%%  \"192.0.2.1\";\n        %%PORT_8081%%  \"10.0.0.1\";\n        %%PORT_8085%%  \"10.11.2.1\";\n        %%PORT_8086%%  \"loopback\";\n        %%PORT_8087%%  \"10.13.2.1\";\n    }\n\n    geo $var $geo_from_var {\n        default       default;\n        127.0.0.0/8   loopback;\n        192.0.2.0/24  test;\n    }\n\n    geo $var $geo_var_ranges {\n        ranges;\n        default                default;\n        127.0.0.0-127.0.0.1    loopback;\n\n        # ranges with two /16 networks\n        # the latter network has greater two least octets\n        # (see 1301a58b5dac for details)\n        10.10.3.0-10.11.2.255  foo;\n        10.12.3.0-10.13.2.255  foo2;\n        delete                 10.10.3.0-10.11.2.255;\n    }\n\n    geo $var $geo_world {\n        127.0.0.0/8   loopback;\n        192.0.2.0/24  test;\n        0.0.0.0/0     world;\n    }\n\n    geo $geo_ranges {\n        ranges;\n        default                    default;\n        127.0.0.0-127.255.255.255  loopback;\n        192.0.2.0-192.0.2.255      test;\n    }\n\n    geo $geo_ranges_include {\n        ranges;\n        default                default;\n        include                geo-ranges.conf;\n        192.0.2.0-192.0.2.255  test;\n    }\n\n    geo $geo_ranges_delete {\n        ranges;\n        default                default;\n        127.0.0.0-127.0.0.255  test;\n        127.0.0.1-127.0.0.1    loopback;\n        delete                 127.0.0.0-127.0.0.0;\n        delete                 127.0.0.2-127.0.0.255;\n        delete                 127.0.0.1-127.0.0.1;\n    }\n\n    # delete range with two /16\n    geo $geo_ranges_delete_2 {\n        ranges;\n        default              default;\n        127.0.0.0-127.1.0.0  loopback;\n        delete               127.0.0.0-127.1.0.0;\n    }\n\n    geo $geo_before {\n        ranges;\n        default                default;\n        127.0.0.1-127.0.0.255  loopback;\n        127.0.0.0-127.0.0.0    test;\n    }\n\n    geo $geo_after {\n        ranges;\n        default                default;\n        127.0.0.0-127.0.0.1    loopback;\n        127.0.0.2-127.0.0.255  test;\n    }\n\n    geo $geo_insert {\n        ranges;\n        default                default;\n        127.0.0.0-127.0.0.255  test;\n        127.0.0.1-127.0.0.2    test2;\n        127.0.0.1-127.0.0.1    loopback;\n    }\n\n    geo $geo_insert_before {\n        ranges;\n        default                default;\n        127.0.0.0-127.0.0.255  test;\n        127.0.0.0-127.0.0.1    loopback;\n    }\n\n    geo $geo_insert_after {\n        ranges;\n        default                default;\n        127.0.0.0-127.0.0.255  test;\n        127.0.0.1-127.0.0.255  loopback;\n     }\n\n    server {\n        listen  127.0.0.1:8080;\n        return  \"geo:$geo\n                 geo_include:$geo_include\n                 geo_delete:$geo_delete\n                 geo_ranges:$geo_ranges\n                 geo_ranges_include:$geo_ranges_include\n                 geo_before:$geo_before\n                 geo_after:$geo_after\n                 geo_insert:$geo_insert\n                 geo_insert_before:$geo_insert_before\n                 geo_insert_after:$geo_insert_after\n                 geo_from_addr:$geo_from_addr\n                 geo_from_var:$geo_from_var\";\n    }\n\n    server {\n        listen  127.0.0.1:8081;\n        return  $geo_from_var;\n    }\n\n    server {\n        listen  127.0.0.1:8082;\n        return  $geo_world;\n    }\n\n    server {\n        listen  127.0.0.1:8083;\n        return  $geo_ranges_delete;\n    }\n\n    server {\n        listen  127.0.0.1:8084;\n        return  $geo_ranges_delete_2;\n    }\n\n    server {\n        listen  127.0.0.1:8085;\n        return  $geo_var_ranges;\n    }\n\n    server {\n        listen  127.0.0.1:8086;\n        return  $geo_var_ranges;\n    }\n\n    server {\n        listen  127.0.0.1:8087;\n        return  $geo_var_ranges;\n    }\n}\n\nEOF\n\n$t->write_file('geo.conf', '127.0.0.0/8  loopback;');\n$t->write_file('geo-ranges.conf', '127.0.0.0-127.255.255.255  loopback;');\n\n$t->run()->plan(19);\n\n###############################################################################\n\nmy %data = stream('127.0.0.1:' . port(8080))->read() =~ /(\\w+):(\\w+)/g;\nis($data{geo}, 'loopback', 'geo');\nis($data{geo_include}, 'loopback', 'geo include');\nis($data{geo_delete}, 'world', 'geo delete');\nis($data{geo_ranges}, 'loopback', 'geo ranges');\nis($data{geo_ranges_include}, 'loopback', 'geo ranges include');\n\nis(stream('127.0.0.1:' . port(8083))->read(), 'default', 'geo ranges delete');\nis(stream('127.0.0.1:' . port(8084))->read(), 'default', 'geo ranges delete 2');\n\nis($data{geo_before}, 'loopback', 'geo ranges add before');\nis($data{geo_after}, 'loopback', 'geo ranges add after');\nis($data{geo_insert}, 'loopback', 'geo ranges insert');\nis($data{geo_insert_before}, 'loopback', 'geo ranges insert before');\nis($data{geo_insert_after}, 'loopback', 'geo ranges insert after');\n\nis($data{geo_from_addr}, 'loopback', 'geo from addr');\nis($data{geo_from_var}, 'test', 'geo from var');\n\nis(stream('127.0.0.1:' . port(8085))->read(), 'default',\n\t'geo delete range from variable');\n\nis(stream('127.0.0.1:' . port(8081))->read(), 'default', 'geo default');\nis(stream('127.0.0.1:' . port(8082))->read(), 'world', 'geo world');\nis(stream('127.0.0.1:' . port(8086))->read(), 'default', 'geo ranges default');\nis(stream('127.0.0.1:' . port(8087))->read(), 'foo2', 'geo ranges add');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/stream_geo_binary.t",
    "content": "#!/usr/bin/perl\n\n# (C) Andrey Zelenkov\n# (C) Nginx, Inc.\n\n# Tests for stream geo module with binary base.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse Test::Nginx::Stream qw/ stream /;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nplan(skip_all => 'long configuration parsing') unless $ENV{TEST_NGINX_UNSAFE};\n\nmy $t = Test::Nginx->new()->has(qw/stream stream_return stream_geo/);\n\n$t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nstream {\n    %%TEST_GLOBALS_STREAM%%\n\n    geo $geo_base_create {\n        ranges;\n        include  base.conf;\n    }\n\n    geo $geo_base_include {\n        ranges;\n        include  base.conf;\n    }\n\n    server {\n        listen  127.0.0.1:8080;\n        return  \"geo_base_create:$geo_base_create\n                 geo_base_include:$geo_base_include\";\n    }\n}\n\nEOF\n\n$t->write_file('base.conf', join('', map {\n\t\"127.\" . $_/256/256 % 256 . \".\" . $_/256 % 256 . \".\" . $_ % 256 .\n\t\"-127.\" . $_/256/256 % 256 . \".\" . $_/256 % 256 . \".\" .$_ % 256 . \" \" .\n\t($_ == 1 ? \"loopback\" : \"range$_\") . \";\" } (0 .. 100000)));\n\n$t->run()->plan(2);\n\n###############################################################################\n\nmy %data = stream('127.0.0.1:' . port(8080))->read() =~ /(\\w+):(\\w+)/g;\nis($data{geo_base_create}, 'loopback', 'geo binary base create');\nis($data{geo_base_include}, 'loopback', 'geo binary base include');\n\n###############################################################################\n"
  },
  {
    "path": "tests/nginx-tests/nginx-tests/stream_geo_ipv6.t",
    "content": "#!/usr/bin/perl\n\n# (C) Andrey Zelenkov\n# (C) Nginx, Inc.\n\n# Stream tests for geo module with IPv6.\n\n###############################################################################\n\nuse warnings;\nuse strict;\n\nuse Test::More;\n\nBEGIN { use FindBin; chdir($FindBin::Bin); }\n\nuse lib 'lib';\nuse Test::Nginx;\nuse Test::Nginx::Stream qw/ stream /;\n\n###############################################################################\n\nselect STDERR; $| = 1;\nselect STDOUT; $| = 1;\n\nmy $t = Test::Nginx->new()->has(qw/stream stream_return stream_map stream_geo/)\n\t->write_file_expand('nginx.conf', <<'EOF');\n\n%%TEST_GLOBALS%%\n\ndaemon off;\n\nevents {\n}\n\nstream {\n    %%TEST_GLOBALS_STREAM%%\n\n    geo $geo {\n        ::1/128         loopback;\n        2001:0db8::/32  test;\n        ::/0            world;\n    }\n\n    geo $geo_delete {\n        ::1/128         loopback;\n        2001:0db8::/32  test;\n        ::/0            world;\n        delete          ::1/128;\n    }\n\n    map $server_port $var {\n        %%PORT_8080%%  \"::1\";\n        %%PORT_8081%%  \"::ffff:192.0.2.1\";\n    }\n\n    geo $var $geo_var {\n        default    default;\n        192.0.2.1  test;\n    }\n\n    geo $var $geo_var_ranges {\n        ranges;\n        default              default;\n        127.0.0.1-127.0.0.2  loopback;\n        192.0.2.0-192.0.2.1  test;\n    }\n\n    server {\n        listen      127.0.0.1:8080;\n        proxy_pass  [::1]:%%PORT_8080%%;\n    }\n\n    server {\n        listen  [::1]:%%PORT_8080%%;\n        return  \"geo:$geo\n                 geo_delete:$geo_delete\n                 geo_var:$geo_var\n                 geo_var_ranges:$geo_var_ranges\";\n    }\n\n    server {\n        listen  127.0.0.1:8081;\n        return  \"geo_var:$geo_var\n                 geo_var_ranges:$geo_var_ranges\";\n    }\n}\n\nEOF\n\n$t->try_run('no inet6 support')->plan(6);\n\n###############################################################################\n\nmy %data = stream('127.0.0.1:' . port(8080))->read() =~ /(\\w+):(\\w+)/g;\nis($data{geo}, 'loopback', 'geo ipv6');\nis($data{geo_delete}, 'world', 'geo ipv6 delete');\nis($data{geo_var}, 'default', 'geo ipv6 from variable');\nis($data{geo_var_ranges}, 'default', 'geo ipv6 from variable range');\n\n%data = stream('127.0.0.1:' . port(8081))->read() =~ /(\\w+):(\\w+)/g;\nis($data{geo_var}, 'test', 'geo ipv6 ipv4-mapped from variable');\nis($data{geo_var_ranges}, 'test', 'geo ipv6 ipv4-mapped from variable range');\n\n###############################################################################\n"
  }
]